tm-dragList.vue 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. <template>
  2. <view class="tm-dragList ">
  3. <view :style="{height:h*list.length+'px',width:w+'px'}" class="relative"
  4. :class="[disabled?'opacity-7':'']">
  5. <view class="fulled-height overflow" :class="[bgColor,black_tmeme?'grey-darken-4 bk':'',
  6. 'absolute',
  7. 'tm-dragList-item','shadow-'+(nowMove_index==index?16:0),'flex-between']" v-for="(item,index) in listData"
  8. :key="index" :style="{
  9. transition: nowMove_index==index||endDrage?'all 0s':'all 0.25s',
  10. top: item.top+'px',
  11. height:h+'px',width:w+'px',zIndex:nowMove_index==index?5:0}">
  12. <view class=" flex-start fulled" :class="[black_tmeme?'border-grey-darken-5-b-1':'border-b-1']"
  13. :style="{height:(h-1)+'px'}"
  14. >
  15. <view v-if="item['icon']" class="flex-shrink pl-32 fulled-height flex-center">
  16. <tm-icons :black="black_tmeme" :color="item['color']||'black'" :fllowTheme="fllowTheme" dense
  17. :name="item['icon']" :size="40"></tm-icons>
  18. </view>
  19. <view class="pl-32 text-size-n" :class="[black_tmeme?'bk':'']">{{item.text}}</view>
  20. </view>
  21. <view
  22. :style="{height:(h-1)+'px',width: '100rpx'}"
  23. @touchstart="m_start($event,index)"
  24. @mousedown="m_start($event,index)"
  25. @touchmove.stop.prevent="m_move($event,index)"
  26. @mousemove.stop.prevent="m_move($event,index)"
  27. @touchend="m_end($event,index)"
  28. @mouseup="m_end($event,index)"
  29. class="flex-shrink flex-end " :class="[black_tmeme?'border-grey-darken-5-b-1':'border-b-1']">
  30. <text class="iconfont icon-menu pr-32 text-size-n"
  31. :class="[black_tmeme?' bk text-grey-darken-2':'text-grey']"></text>
  32. </view>
  33. </view>
  34. </view>
  35. </view>
  36. </template>
  37. <script>
  38. /**
  39. * 拖放排序
  40. * @property {String | Boolean} black = [true|false] 默认:null,是否开启暗黑模式
  41. * @property {String | Boolean} disabled = [true|false] 默认:false,是否禁用,禁用后无法操作。
  42. * @property {Number} width = [] 默认:0,组件的宽度,rpx,可不提供,默认为父组件的宽度。
  43. * @property {Number} height = [] 默认:120,列表项目的高度,rpx,
  44. * @property {String} bgColor = [] 默认:white,项目的背景色
  45. * @property {String} right-icon = [] 默认:'',项目右边可拖动的图标
  46. * @property {String} rang-key = [] 默认:'text',列表项目读取文本的key
  47. * @property {String} list = [] 默认:[],列表数据[{text: "菜单选项",icon: 'icon-menu',color:'red'}]
  48. * @param {Function} change 拖动排序后,触发,返回新的排序后list数据。
  49. */
  50. import tmIcons from '@/tm-vuetify/components/tm-icons/tm-icons.vue';
  51. export default {
  52. name: "tm-dragList",
  53. components: {
  54. tmIcons
  55. },
  56. props: {
  57. disabled: {
  58. type: String | Boolean,
  59. default: false
  60. },
  61. // 跟随主题色的改变而改变。
  62. fllowTheme: {
  63. type: Boolean | String,
  64. default: true
  65. },
  66. // 是否开启暗黑模式
  67. black: {
  68. type: String | Boolean,
  69. default: null
  70. },
  71. width: {
  72. type: Number,
  73. default: 0
  74. },
  75. height: {
  76. type: Number,
  77. default: 120
  78. },
  79. list: {
  80. type: Array,
  81. default: () => {
  82. return []
  83. }
  84. },
  85. rangKey: {
  86. type: String,
  87. default: 'text'
  88. },
  89. rightIcon: {
  90. type: String,
  91. default: "icon-menu"
  92. },
  93. bgColor: {
  94. type: String,
  95. default: "white"
  96. },
  97. },
  98. destroyed() {
  99. clearTimeout(999)
  100. },
  101. watch: {
  102. list: {
  103. deep: true,
  104. handler() {
  105. this.jishunTopData();
  106. }
  107. }
  108. },
  109. data() {
  110. return {
  111. w: 0,
  112. h: 0,
  113. totalH: 0,
  114. y: 0,
  115. new_index: null, //即将被替换的索引(实质性被替换)
  116. nowMove_index: null, //现在正在移动的索引
  117. listData: [], //被处理过的数据。
  118. new_item: [], //虚拟列表,内部排列好,但未在页面中渲染。
  119. endDrage: false,
  120. Drage__id: '', //正在被拖动的id;
  121. h_top: 0,
  122. };
  123. },
  124. computed: {
  125. black_tmeme: function() {
  126. if (this.black !== null) return this.black;
  127. return this.$tm.vx.state().tmVuetify.black;
  128. },
  129. },
  130. async mounted() {
  131. let t = this;
  132. this.jishunTopData();
  133. },
  134. methods: {
  135. jishunTopData() {
  136. this.$nextTick(async function() {
  137. this.listData = [];
  138. let p = await this.$Querey(".tm-dragList", this).catch(e => {})
  139. this.w = uni.upx2px(this.width) || p[0].width;
  140. this.h = uni.upx2px(this.height)
  141. this.totalH = this.h * this.list.length
  142. let list = [];
  143. for (let i = 0; i < this.list.length; i++) {
  144. let p = this.list[i];
  145. p['top'] = i * this.h;
  146. p['i'] = i;
  147. p['__id'] = uni.$tm.guid();
  148. this.listData.push(p)
  149. }
  150. this.new_item = [...this.listData];
  151. })
  152. },
  153. m_start(event, index) {
  154. event.preventDefault()
  155. event.stopPropagation()
  156. if (this.disabled) return;
  157. this.nowMove_index = index;
  158. this.endDrage = false;
  159. this.new_item = [...this.listData];
  160. if (event.type.indexOf('mouse') == -1 && event.changedTouches.length == 1) {
  161. var touch = event.changedTouches[0];
  162. this.y = touch.pageY - event.currentTarget.offsetTop - this.listData[index].top
  163. } else {
  164. this.y = event.pageY - event.currentTarget.offsetTop - this.listData[index].top
  165. }
  166. // #ifdef MP
  167. uni.vibrateShort({})
  168. // #endif
  169. },
  170. m_move(event, index) {
  171. if (this.disabled) return;
  172. let t = this
  173. event.preventDefault()
  174. event.stopPropagation()
  175. if (t.nowMove_index == null) return;
  176. //当前元素的top位置。
  177. let ch = 0;
  178. if (event.type.indexOf('mouse') == -1 && event.changedTouches.length == 1) {
  179. var touch = event.changedTouches[0];
  180. ch = touch.pageY - t.y
  181. } else {
  182. ch = event.pageY - t.y
  183. }
  184. t.listData.splice(index, 1, {
  185. ...t.listData[index],
  186. top: ch
  187. })
  188. const currenit_index = index;
  189. const currentSort = t.listData[currenit_index].i;
  190. const currenit_id = t.listData[currenit_index].__id;
  191. // 计算当前移动的index.
  192. let moveIndex = Math.round(ch / t.h);
  193. moveIndex = moveIndex < 0 ? 0 : moveIndex;
  194. moveIndex = moveIndex > t.listData.length - 1 ? t.listData.length - 1 : moveIndex;
  195. moveIndex = Math.abs(moveIndex)
  196. index = moveIndex;
  197. let elList = [...t.listData]
  198. for (let i = 0; i < elList.length; i++) {
  199. if (currentSort < moveIndex) {
  200. if (elList[i].i > currentSort && elList[i].i <= moveIndex) {
  201. elList[i].i -= 1;
  202. };
  203. } else if (currentSort > moveIndex) {
  204. if (elList[i].i < currentSort && elList[i].i >= moveIndex) {
  205. elList[i].i += 1;
  206. };
  207. }
  208. };
  209. elList[currenit_index].i = moveIndex;
  210. elList = elList.map(im => {
  211. if (im.__id != currenit_id) {
  212. im.top = im.i * t.h;
  213. }
  214. return im;
  215. })
  216. t.listData = elList;
  217. t.new_index = moveIndex;
  218. // #ifdef MP
  219. uni.vibrateShort({})
  220. // #endif
  221. },
  222. m_end(event, index) {
  223. if (this.disabled) return;
  224. let t = this;
  225. event.preventDefault()
  226. event.stopPropagation()
  227. this.nowMove_index = null;
  228. this.endDrage = true;
  229. if (this.new_index == null) return;
  230. let elList = t.listData
  231. elList = elList.map(im => {
  232. im.top = im.i * t.h;
  233. return im;
  234. })
  235. elList.sort((a,b)=>a.i-b.i)
  236. t.listData = [...elList]
  237. this.moveChange();
  238. },
  239. moveChange(e, index) {
  240. if (this.disabled) return;
  241. //change后修改的数据 。
  242. this.$emit('change', this.listData);
  243. }
  244. },
  245. }
  246. </script>
  247. <style lang="scss">
  248. </style>