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

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<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>