<template> <view class="tm-dragList "> <view :style="{height:h*list.length+'px',width:w+'px'}" class="relative" :class="[disabled?'opacity-7':'']"> <view class="fulled-height overflow" :class="[bgColor,black_tmeme?'grey-darken-4 bk':'', 'absolute', 'tm-dragList-item','shadow-'+(nowMove_index==index?16:0),'flex-between']" v-for="(item,index) in listData" :key="index" :style="{ transition: nowMove_index==index||endDrage?'all 0s':'all 0.25s', top: item.top+'px', height:h+'px',width:w+'px',zIndex:nowMove_index==index?5:0}"> <view class=" flex-start fulled" :class="[black_tmeme?'border-grey-darken-5-b-1':'border-b-1']" :style="{height:(h-1)+'px'}" > <view v-if="item['icon']" class="flex-shrink pl-32 fulled-height flex-center"> <tm-icons :black="black_tmeme" :color="item['color']||'black'" :fllowTheme="fllowTheme" dense :name="item['icon']" :size="40"></tm-icons> </view> <view class="pl-32 text-size-n" :class="[black_tmeme?'bk':'']">{{item.text}}</view> </view> <view :style="{height:(h-1)+'px',width: '100rpx'}" @touchstart="m_start($event,index)" @mousedown="m_start($event,index)" @touchmove.stop.prevent="m_move($event,index)" @mousemove.stop.prevent="m_move($event,index)" @touchend="m_end($event,index)" @mouseup="m_end($event,index)" class="flex-shrink flex-end " :class="[black_tmeme?'border-grey-darken-5-b-1':'border-b-1']"> <text class="iconfont icon-menu pr-32 text-size-n" :class="[black_tmeme?' bk text-grey-darken-2':'text-grey']"></text> </view> </view> </view> </view> </template> <script> /** * 拖放排序 * @property {String | Boolean} black = [true|false] 默认:null,是否开启暗黑模式 * @property {String | Boolean} disabled = [true|false] 默认:false,是否禁用,禁用后无法操作。 * @property {Number} width = [] 默认:0,组件的宽度,rpx,可不提供,默认为父组件的宽度。 * @property {Number} height = [] 默认:120,列表项目的高度,rpx, * @property {String} bgColor = [] 默认:white,项目的背景色 * @property {String} right-icon = [] 默认:'',项目右边可拖动的图标 * @property {String} rang-key = [] 默认:'text',列表项目读取文本的key * @property {String} list = [] 默认:[],列表数据[{text: "菜单选项",icon: 'icon-menu',color:'red'}] * @param {Function} change 拖动排序后,触发,返回新的排序后list数据。 */ import tmIcons from '@/tm-vuetify/components/tm-icons/tm-icons.vue'; export default { name: "tm-dragList", components: { tmIcons }, props: { disabled: { type: String | Boolean, default: false }, // 跟随主题色的改变而改变。 fllowTheme: { type: Boolean | String, default: true }, // 是否开启暗黑模式 black: { type: String | Boolean, default: null }, width: { type: Number, default: 0 }, height: { type: Number, default: 120 }, list: { type: Array, default: () => { return [] } }, rangKey: { type: String, default: 'text' }, rightIcon: { type: String, default: "icon-menu" }, bgColor: { type: String, default: "white" }, }, destroyed() { clearTimeout(999) }, watch: { list: { deep: true, handler() { this.jishunTopData(); } } }, data() { return { w: 0, h: 0, totalH: 0, y: 0, new_index: null, //即将被替换的索引(实质性被替换) nowMove_index: null, //现在正在移动的索引 listData: [], //被处理过的数据。 new_item: [], //虚拟列表,内部排列好,但未在页面中渲染。 endDrage: false, Drage__id: '', //正在被拖动的id; h_top: 0, }; }, computed: { black_tmeme: function() { if (this.black !== null) return this.black; return this.$tm.vx.state().tmVuetify.black; }, }, async mounted() { let t = this; this.jishunTopData(); }, methods: { jishunTopData() { this.$nextTick(async function() { this.listData = []; let p = await this.$Querey(".tm-dragList", this).catch(e => {}) this.w = uni.upx2px(this.width) || p[0].width; this.h = uni.upx2px(this.height) this.totalH = this.h * this.list.length let list = []; for (let i = 0; i < this.list.length; i++) { let p = this.list[i]; p['top'] = i * this.h; p['i'] = i; p['__id'] = uni.$tm.guid(); this.listData.push(p) } this.new_item = [...this.listData]; }) }, m_start(event, index) { event.preventDefault() event.stopPropagation() if (this.disabled) return; this.nowMove_index = index; this.endDrage = false; this.new_item = [...this.listData]; if (event.type.indexOf('mouse') == -1 && event.changedTouches.length == 1) { var touch = event.changedTouches[0]; this.y = touch.pageY - event.currentTarget.offsetTop - this.listData[index].top } else { this.y = event.pageY - event.currentTarget.offsetTop - this.listData[index].top } // #ifdef MP uni.vibrateShort({}) // #endif }, m_move(event, index) { if (this.disabled) return; let t = this event.preventDefault() event.stopPropagation() if (t.nowMove_index == null) return; //当前元素的top位置。 let ch = 0; if (event.type.indexOf('mouse') == -1 && event.changedTouches.length == 1) { var touch = event.changedTouches[0]; ch = touch.pageY - t.y } else { ch = event.pageY - t.y } t.listData.splice(index, 1, { ...t.listData[index], top: ch }) const currenit_index = index; const currentSort = t.listData[currenit_index].i; const currenit_id = t.listData[currenit_index].__id; // 计算当前移动的index. let moveIndex = Math.round(ch / t.h); moveIndex = moveIndex < 0 ? 0 : moveIndex; moveIndex = moveIndex > t.listData.length - 1 ? t.listData.length - 1 : moveIndex; moveIndex = Math.abs(moveIndex) index = moveIndex; let elList = [...t.listData] for (let i = 0; i < elList.length; i++) { if (currentSort < moveIndex) { if (elList[i].i > currentSort && elList[i].i <= moveIndex) { elList[i].i -= 1; }; } else if (currentSort > moveIndex) { if (elList[i].i < currentSort && elList[i].i >= moveIndex) { elList[i].i += 1; }; } }; elList[currenit_index].i = moveIndex; elList = elList.map(im => { if (im.__id != currenit_id) { im.top = im.i * t.h; } return im; }) t.listData = elList; t.new_index = moveIndex; // #ifdef MP uni.vibrateShort({}) // #endif }, m_end(event, index) { if (this.disabled) return; let t = this; event.preventDefault() event.stopPropagation() this.nowMove_index = null; this.endDrage = true; if (this.new_index == null) return; let elList = t.listData elList = elList.map(im => { im.top = im.i * t.h; return im; }) elList.sort((a,b)=>a.i-b.i) t.listData = [...elList] this.moveChange(); }, moveChange(e, index) { if (this.disabled) return; //change后修改的数据 。 this.$emit('change', this.listData); } }, } </script> <style lang="scss"> </style>