You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

272 lines
7.3 KiB
Vue

1 year ago
<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>