tm-dragGrid.vue 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329
  1. <template>
  2. <view class="tm-dragGrid relative fulled" :style="{height:totalH+'px'}">
  3. <view class="absolute "
  4. :class="[ani&&!disabled?'aniOn':'',nowMove_index==index||endDrage?'':'tranAni']"
  5. :style="{
  6. width:itemWidth+'px',
  7. height:h+'px',
  8. left:item.left+'px',
  9. top:item.top+'px',
  10. zIndex:nowMove_index==index?5:0
  11. }"
  12. v-for="(item,index) in listData"
  13. :key="index"
  14. :id="'tm-dragGrid-' + index"
  15. @touchstart="m_start($event,index)"
  16. @mousedown="m_start($event,index)"
  17. @touchmove.stop.prevent="m_move($event,index)"
  18. @mousemove.stop.prevent="m_move($event,index)"
  19. @touchend="m_end($event,index)"
  20. @mouseup="m_end($event,index)"
  21. @longpress="startEdit"
  22. >
  23. <slot name="default" :item="item.data">
  24. <view v-if="model==0" class="flex-center flex-col" style="width:70%">
  25. <view style="width:100%"><tm-badges @click.stop="delitem(item)" v-if="!disabled" :offset="[0,-5]" icon="icon-times"></tm-badges></view>
  26. <tm-icons :size="45" :name="item.data.icon" :color="item.data.color"></tm-icons>
  27. <text class="text-size-s pt-10">{{item.data.text}}</text>
  28. </view>
  29. <view v-if="model==1" style="width:100%;height:100%">
  30. <tm-badges @click.stop="delitem(item)" v-if="!disabled" :offset="[-5,-5]" icon="icon-times"></tm-badges>
  31. <view :class="['bg-gradient-'+item.data.color+'-accent']" class=" flex-center round-4 text-size-s" style="width: 90%;height:100rpx">
  32. {{item.data.text}}
  33. </view>
  34. </view>
  35. </slot>
  36. </view>
  37. </view>
  38. </template>
  39. <script>
  40. /**
  41. * 宫格拖动排序
  42. * @property {Number} col = [] 4,一行排列几个
  43. * @property {Number} width = [] 0,组件宽度,可以不设置默认取父组件宽度
  44. * @property {Number} itemHeight = [] 120,项目的高度
  45. * @property {Boolean} ani = [] true,是否开启拖动显示内容抖动动画
  46. * @property {Boolean} disabled = [] false,是否开启拖动
  47. * @property {Boolean} longTap = [] true,是否允许长按启动编辑模式。
  48. * @property {Number} model = [0|1] 0,0图标模式,1方块样式。
  49. * @property {Array} list = [] [],排序的列表,只要是数组就可,至于内容是什么格式无所谓。
  50. * @property {Function} change 拖放排序时触发,返回更改后的列表数据。
  51. * @property {Function} remove 删除一个项目时触发。
  52. */
  53. import tmIcons from '@/tm-vuetify/components/tm-icons/tm-icons.vue';
  54. import tmBadges from '@/tm-vuetify/components/tm-badges/tm-badges.vue';
  55. export default {
  56. name:"tm-dragGrid",
  57. components:{tmBadges,tmIcons},
  58. props:{
  59. //几列,一行排几个。
  60. col:{
  61. type:Number,
  62. default:4
  63. },
  64. //组件宽度,可以不设置默认取父组件宽度
  65. width:{
  66. type:Number,
  67. default:0
  68. },
  69. //项目的高度
  70. itemHeight:{
  71. type:Number,
  72. default:120
  73. },
  74. //是否开启拖动显示内容抖动动画
  75. ani:{
  76. type:Boolean,
  77. default:true
  78. },
  79. disabled: {
  80. type: String | Boolean,
  81. default: false
  82. },
  83. list:{
  84. type:Array,
  85. default:()=>{}
  86. },
  87. //是否开户长按开始编辑。
  88. longTap:{
  89. type:Boolean,
  90. default:true
  91. },
  92. model:{
  93. type:Number,
  94. default:0,
  95. }
  96. },
  97. data() {
  98. return {
  99. w:0,
  100. h:0,
  101. row:0,
  102. totalH:0,
  103. listData:[],
  104. itemWidth:0,
  105. endDrage: false,
  106. Drage__id: '', //正在被拖动的id;
  107. nowMove_index: null, //现在正在移动的索引
  108. x:0,
  109. y:0,
  110. prarentTop:0,
  111. prarentLeft:0,
  112. grid:0,
  113. isDrage:false,
  114. };
  115. },
  116. computed:{
  117. },
  118. created() {
  119. this.grid = this.list.length;
  120. },
  121. mounted() {
  122. this.inits();
  123. },
  124. watch:{
  125. list:{
  126. deep:true,
  127. handler(){
  128. this.inits();
  129. }
  130. }
  131. },
  132. methods: {
  133. startEdit(){
  134. if(this.longTap==false) return;
  135. this.$emit("update:disabled",false)
  136. },
  137. delitem(item){
  138. this.list.splice(item.index,1)
  139. this.$emit('remove',item)
  140. this.$emit('change', this.listData);
  141. },
  142. m_start(event,index){
  143. event.preventDefault()
  144. event.stopPropagation()
  145. if (this.disabled) return;
  146. this.nowMove_index = index;
  147. this.endDrage = false;
  148. this.isDrage = true;
  149. if (event.type.indexOf('mouse') == -1 && event.changedTouches.length == 1) {
  150. var touch = event.changedTouches[0];
  151. this.y = touch.pageY - event.currentTarget.offsetTop - this.prarentTop
  152. this.x = touch.pageX - event.currentTarget.offsetLeft - this.prarentLeft
  153. } else {
  154. this.y = event.pageY - event.currentTarget.offsetTop - this.prarentTop
  155. this.x = event.pageX - event.currentTarget.offsetLeft - this.prarentLeft
  156. }
  157. // #ifdef MP
  158. uni.vibrateShort({})
  159. // #endif
  160. },
  161. m_move(event,index){
  162. let t = this
  163. if (this.disabled) return;
  164. event.preventDefault()
  165. event.stopPropagation()
  166. if(!this.isDrage) return;
  167. if (t.endDrage==true) return;
  168. //当前元素的top位置。
  169. let chy = 0;
  170. let chx = 0;
  171. if (event.type.indexOf('mouse') == -1 && event.changedTouches.length == 1) {
  172. var touch = event.changedTouches[0];
  173. chy = touch.pageY - t.y - this.prarentTop
  174. chx = touch.pageX - t.x - this.prarentLeft
  175. } else {
  176. chy = event.pageY - t.y - this.prarentTop
  177. chx = event.pageX - t.x - this.prarentLeft
  178. }
  179. t.listData.splice(index, 1, {
  180. ...t.listData[index],
  181. top: chy,
  182. left: chx,
  183. })
  184. t.nowMove_index = index;
  185. const currenit_index = index;
  186. const currentSort = t.listData[currenit_index].sort;
  187. const currenit_id = t.listData[currenit_index].__id;
  188. // 计算当前移动的index.
  189. let moveIndex = Math.round(chx / t.itemWidth) + Math.round(chy / t.h)*t.col;
  190. moveIndex = moveIndex < 0 ? 0 : moveIndex;
  191. moveIndex = moveIndex > t.listData.length - 1 ? t.listData.length - 1 : moveIndex;
  192. moveIndex = Math.abs(moveIndex)
  193. index = moveIndex;
  194. let elList = [...t.listData]
  195. for (let i = 0; i < elList.length; i++) {
  196. if (currentSort < moveIndex) {
  197. if (elList[i].sort > currentSort && elList[i].sort <= moveIndex) {
  198. elList[i].sort -= 1;
  199. };
  200. } else if (currentSort > moveIndex) {
  201. if (elList[i].sort < currentSort && elList[i].sort >= moveIndex) {
  202. elList[i].sort += 1;
  203. };
  204. }
  205. };
  206. elList[currenit_index].sort = moveIndex;
  207. elList = elList.map(el => {
  208. if (el.__id != currenit_id) {
  209. el.left = el.sort % t.col * t.itemWidth;
  210. el.top = parseInt(el.sort / t.col) * t.h;
  211. }
  212. return el;
  213. })
  214. t.listData = elList;
  215. },
  216. m_end(event,index){
  217. if (this.disabled) return;
  218. let t = this;
  219. event.preventDefault()
  220. event.stopPropagation()
  221. this.isDrage = false;
  222. this.endDrage = true;
  223. if (this.nowMove_index == null) return;
  224. let elList = [...t.listData]
  225. elList = this.setTL(elList);
  226. elList.sort((a,b)=>a.sort-b.sort)
  227. t.listData = [...elList]
  228. this.nowMove_index = null;
  229. this.moveChange();
  230. },
  231. moveChange(e, index) {
  232. if (this.disabled) return;
  233. //change后修改的数据 。
  234. const elList = [...this.listData]
  235. elList.sort((a,b)=>a.sort-b.sort-b)
  236. this.$emit('change',elList);
  237. },
  238. inits() {
  239. if(this.grid==0) return;
  240. this.$nextTick(async function() {
  241. let p = await this.$Querey(".tm-dragGrid", this).catch(e => {})
  242. this.grid = this.list.length;
  243. this.listData = [];
  244. //组件的宽度
  245. this.w = uni.upx2px(this.width) || p[0].width || 300;
  246. this.prarentTop = p[0].top;
  247. this.prarentLeft = p[0].left;
  248. //项目的高度。
  249. this.h = uni.upx2px(this.itemHeight)
  250. //项目的宽度
  251. this.itemWidth = this.w / this.col
  252. //行数。
  253. this.row = Math.ceil(this.grid / this.col);
  254. //总高度
  255. this.totalH = this.h * this.row
  256. //构造一个list宫格列表出来。
  257. for(let i=0;i<this.grid;i++){
  258. this.listData.push({
  259. "data":this.list[i],
  260. "__id":uni.$tm.guid(),
  261. "top":0,
  262. "left":0,
  263. "sort":i,
  264. "index":i
  265. })
  266. }
  267. let list = this.setTL([...this.listData]);
  268. this.listData = [...list];
  269. })
  270. },
  271. //计算位置。
  272. setTL(el){
  273. for(let i = 0;i < el.length;i++){
  274. el[i].left = el[i].sort % this.col * this.itemWidth;
  275. el[i].top = parseInt(el[i].sort / this.col) * this.h;
  276. }
  277. return el;
  278. }
  279. },
  280. }
  281. </script>
  282. <style lang="scss" scoped>
  283. .aniOn{
  284. animation:doudong 0.5s linear infinite;
  285. background:linear-gradient();
  286. background: -webkit-linear-gradient();
  287. }
  288. .tranAni{
  289. transition:all 0.15s
  290. }
  291. @keyframes doudong {
  292. 0%{
  293. transform: rotate(-2deg) translateX(2rpx) translateY(2rpx);
  294. }
  295. 25%{
  296. transform: rotate(0deg) translateX(-2rpx) translateY(-2rpx);
  297. }
  298. 50%{
  299. transform: rotate(0deg) translateX(0rpx) translateY(-2rpx);
  300. }
  301. 75%{
  302. transform: rotate(0deg) translateX(0rpx) translateY(2rpx);
  303. }
  304. 100%{
  305. transform: rotate(-2deg) translateX(2rpx) translateY(2rpx);
  306. }
  307. }
  308. </style>