@dolphinweex/weex-harmony 0.1.28 → 0.1.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
@@ -8,7 +8,7 @@
|
|
8
8
|
<div class="grid-items-wrapper">
|
9
9
|
<transition-group name="grid-fade" tag="div" class="grid-inner-wrapper">
|
10
10
|
<div
|
11
|
-
v-for="(item, index) in
|
11
|
+
v-for="(item, index) in data.list"
|
12
12
|
:key="item.itemView.id"
|
13
13
|
class="grid-item"
|
14
14
|
:class="{
|
@@ -117,34 +117,34 @@
|
|
117
117
|
<div v-if="isDragClone" class="drag-clone" :style="dragCloneStyle">
|
118
118
|
<div
|
119
119
|
class="grid-box"
|
120
|
-
:style="applyNormalAttribute(
|
120
|
+
:style="applyNormalAttribute(data.list[draggingIndex].itemView)"
|
121
121
|
>
|
122
122
|
<div class="flexBox">
|
123
123
|
<img
|
124
|
-
:src="
|
124
|
+
:src="data.list[draggingIndex].iconImage.url"
|
125
125
|
:style="
|
126
|
-
applyNormalAttribute(
|
126
|
+
applyNormalAttribute(data.list[draggingIndex].iconImage)
|
127
127
|
"
|
128
128
|
/>
|
129
129
|
<div @click.stop.prevent>
|
130
130
|
<img
|
131
131
|
class="isSelected"
|
132
132
|
:src="
|
133
|
-
|
134
|
-
?
|
135
|
-
:
|
133
|
+
data.list[draggingIndex].itemView.isSelected
|
134
|
+
? data.list[draggingIndex].selectIcon.selectedUrl
|
135
|
+
: data.list[draggingIndex].selectIcon.url
|
136
136
|
"
|
137
137
|
:style="
|
138
|
-
applyNormalAttribute(
|
138
|
+
applyNormalAttribute(data.list[draggingIndex].selectIcon)
|
139
139
|
"
|
140
140
|
/>
|
141
141
|
</div>
|
142
142
|
</div>
|
143
143
|
<div
|
144
144
|
class="itemText"
|
145
|
-
:style="[applyNormalAttribute(
|
145
|
+
:style="[applyNormalAttribute(data.list[draggingIndex].text)]"
|
146
146
|
>
|
147
|
-
{{
|
147
|
+
{{ data.list[draggingIndex].text.text }}
|
148
148
|
</div>
|
149
149
|
</div>
|
150
150
|
</div>
|
@@ -152,6 +152,7 @@
|
|
152
152
|
</template>
|
153
153
|
|
154
154
|
<script>
|
155
|
+
import { weex_harmony_rebindGestureDelayed } from '@dolphinweex/harmony-webpack-adapter/registerGesture.js'; // 请替换为实际的loader文件路径
|
155
156
|
import lottie from 'lottie-web';
|
156
157
|
const scla = 2;
|
157
158
|
export default {
|
@@ -173,7 +174,6 @@ export default {
|
|
173
174
|
return {
|
174
175
|
targetTouchX: 0,
|
175
176
|
targetTouchY: 0,
|
176
|
-
processedList: [],
|
177
177
|
selected: null,
|
178
178
|
longPressTimer: null,
|
179
179
|
longPressDuration: 350,
|
@@ -198,9 +198,11 @@ export default {
|
|
198
198
|
fixedItemWidth: 0,
|
199
199
|
fixedItemHeight: 0,
|
200
200
|
animationFrameId: null,
|
201
|
-
|
202
|
-
|
203
|
-
|
201
|
+
isProcessingData: false,
|
202
|
+
// 手势事件配置 - 根据实际使用的手势事件来配置
|
203
|
+
gestureEventMap: [],
|
204
|
+
// 元素ID - 用于标识需要绑定手势的元素
|
205
|
+
elementId: 'grid-item'
|
204
206
|
};
|
205
207
|
},
|
206
208
|
computed: {
|
@@ -208,6 +210,7 @@ export default {
|
|
208
210
|
return {
|
209
211
|
'--grid-cols': this.data.spanCount || this.cols,
|
210
212
|
'--grid-gap': `${this.data.spaceSize || 8}px`,
|
213
|
+
'--grid-margin': `${(this.data.layoutConfig.marginStart ) + (this.data.layoutConfig.marginEnd )}px`,
|
211
214
|
marginTop: this.data.layoutConfig.marginTop * scla,
|
212
215
|
marginBottom: this.data.layoutConfig.marginBottom * scla,
|
213
216
|
marginLeft: this.data.layoutConfig.marginStart * scla,
|
@@ -220,7 +223,7 @@ export default {
|
|
220
223
|
return { display: 'none' };
|
221
224
|
}
|
222
225
|
|
223
|
-
const item = this.
|
226
|
+
const item = this.data.list[this.draggingIndex];
|
224
227
|
let itemView = {};
|
225
228
|
if (item) {
|
226
229
|
itemView = this.applyNormalAttribute(item.itemView);
|
@@ -245,10 +248,7 @@ export default {
|
|
245
248
|
updateListItem(data) {
|
246
249
|
console.log('updateListItem收到数据:', JSON.stringify(data));
|
247
250
|
const id = data.itemView.id;
|
248
|
-
const index = this.
|
249
|
-
(item) => item.itemView.id == id
|
250
|
-
);
|
251
|
-
const dataIndex = this.data.list.findIndex(
|
251
|
+
const index = this.data.list.findIndex(
|
252
252
|
(item) => item.itemView.id == id
|
253
253
|
);
|
254
254
|
|
@@ -257,89 +257,148 @@ export default {
|
|
257
257
|
}
|
258
258
|
|
259
259
|
// 检查是否有动画URL的变化
|
260
|
-
const oldBgAnimUrl = this.
|
261
|
-
const oldLoadingAnimUrl =
|
262
|
-
|
263
|
-
const oldFrontAnimUrl =
|
264
|
-
this.processedList[index].frontAnimView.animUrl || '';
|
265
|
-
|
260
|
+
const oldBgAnimUrl = this.data.list[index].bgAnimView.animUrl || '';
|
261
|
+
const oldLoadingAnimUrl = this.data.list[index].loadingAnimView.animUrl || '';
|
262
|
+
const oldFrontAnimUrl = this.data.list[index].frontAnimView.animUrl || '';
|
266
263
|
const newBgAnimUrl = data.bgAnimView.animUrl || '';
|
267
264
|
const newLoadingAnimUrl = data.loadingAnimView.animUrl || '';
|
268
265
|
const newFrontAnimUrl = data.frontAnimView.animUrl || '';
|
269
|
-
// 同时更新processedList和原始data.list数据
|
270
|
-
this.processedList[index] = data;
|
271
|
-
this.processedList = [...this.processedList];
|
272
|
-
|
273
|
-
// 更新原始数据以确保watch能触发
|
274
|
-
if (dataIndex !== -1) {
|
275
|
-
// 使用Vue的$set确保响应式更新
|
276
|
-
this.$set(this.data.list, dataIndex, JSON.parse(JSON.stringify(data)));
|
277
|
-
}
|
278
266
|
|
279
|
-
//
|
280
|
-
|
267
|
+
// 合并全局数据到新数据
|
268
|
+
const mergedData = this.mergeItemData(data);
|
269
|
+
|
270
|
+
// 更新数据
|
271
|
+
this.$set(this.data.list, index, mergedData);
|
281
272
|
|
282
|
-
// 等待下一帧DOM更新完成后再操作
|
283
273
|
this.$nextTick(() => {
|
284
|
-
// 清理旧的动画实例
|
285
274
|
const itemId = String(data.itemView.id);
|
286
275
|
|
287
|
-
//
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
`front-${itemId}`,
|
292
|
-
];
|
293
|
-
animKeys.forEach((key) => {
|
294
|
-
if (this.lottieAnimations[key]) {
|
295
|
-
console.log(`销毁动画实例: ${key}`);
|
296
|
-
const anim = this.lottieAnimations[key];
|
297
|
-
try {
|
298
|
-
anim.removeEventListener('complete');
|
299
|
-
} catch (e) {
|
300
|
-
console.warn(`移除事件监听器失败: ${e.message}`);
|
301
|
-
}
|
302
|
-
anim.destroy();
|
303
|
-
delete this.lottieAnimations[key];
|
304
|
-
}
|
305
|
-
});
|
276
|
+
// 处理背景动画
|
277
|
+
if (oldBgAnimUrl !== newBgAnimUrl && newBgAnimUrl) {
|
278
|
+
this.createLottieAnimation('bg', itemId, mergedData.bgAnimView, mergedData);
|
279
|
+
}
|
306
280
|
|
307
|
-
|
281
|
+
// 处理加载动画
|
282
|
+
if (oldLoadingAnimUrl !== newLoadingAnimUrl && newLoadingAnimUrl) {
|
283
|
+
this.createLottieAnimation('loading', itemId, mergedData.loadingAnimView, mergedData);
|
284
|
+
}
|
308
285
|
|
309
|
-
//
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
286
|
+
// 处理前景动画
|
287
|
+
if (oldFrontAnimUrl !== newFrontAnimUrl && newFrontAnimUrl) {
|
288
|
+
this.createLottieAnimation('front', itemId, mergedData.frontAnimView, mergedData);
|
289
|
+
}
|
290
|
+
// 重新绑定手势事件 - 解决DOM更新后手势失效问题
|
291
|
+
this.rebindGestureEvents();
|
292
|
+
});
|
293
|
+
},
|
294
|
+
// 重新绑定手势事件的方法
|
295
|
+
rebindGestureEvents() {
|
296
|
+
try {
|
297
|
+
// 延迟执行确保DOM完全更新
|
298
|
+
weex_harmony_rebindGestureDelayed(
|
299
|
+
this.$el, // 组件根元素
|
300
|
+
this.gestureEventMap, // 手势事件配置数组
|
301
|
+
this.elementId, // 元素类名ID
|
302
|
+
100 // 延迟100ms
|
303
|
+
);
|
304
|
+
console.log('手势事件重新绑定完成');
|
305
|
+
} catch (error) {
|
306
|
+
console.error('重新绑定手势事件失败:', error);
|
307
|
+
}
|
308
|
+
},
|
309
|
+
// 初始化手势事件配置
|
310
|
+
initGestureConfig() {
|
311
|
+
// 根据你实际使用的手势事件来配置
|
312
|
+
this.gestureEventMap = [
|
313
|
+
{
|
314
|
+
name: "@touchstart",
|
315
|
+
handler: this.startLongPress,
|
316
|
+
params: ['event', 'index'],
|
317
|
+
isEvent: true,
|
318
|
+
modifier: []
|
319
|
+
},
|
320
|
+
{
|
321
|
+
name: "@touchmove",
|
322
|
+
handler: this.checkLongPressMove,
|
323
|
+
params: [],
|
324
|
+
isEvent: true,
|
325
|
+
modifier: []
|
326
|
+
},
|
327
|
+
{
|
328
|
+
name: "@touchend",
|
329
|
+
handler: this.endTouch,
|
330
|
+
params: [],
|
331
|
+
isEvent: true,
|
332
|
+
modifier: []
|
333
|
+
},
|
334
|
+
{
|
335
|
+
name: "@touchcancel",
|
336
|
+
handler: this.endTouch,
|
337
|
+
params: [],
|
338
|
+
isEvent: true,
|
339
|
+
modifier: []
|
340
|
+
}
|
341
|
+
];
|
342
|
+
},
|
343
|
+
// 简化的动画创建方法
|
344
|
+
createLottieAnimation(type, itemId, animView, itemData) {
|
345
|
+
const animKey = `${type}-${itemId}`;
|
346
|
+
const refName = `${type}Anim-${itemId}`;
|
347
|
+
|
348
|
+
// 销毁旧动画
|
349
|
+
if (this.lottieAnimations[animKey]) {
|
350
|
+
this.lottieAnimations[animKey].destroy();
|
351
|
+
delete this.lottieAnimations[animKey];
|
352
|
+
}
|
320
353
|
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
data
|
327
|
-
);
|
328
|
-
}
|
354
|
+
// 检查DOM元素是否存在
|
355
|
+
const animEl = this.$refs[refName];
|
356
|
+
if (!animEl || !animEl[0] || !animView.animUrl) {
|
357
|
+
return;
|
358
|
+
}
|
329
359
|
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
360
|
+
// 检查可见性
|
361
|
+
if (typeof animView.visibility === 'number' && animView.visibility === 0) {
|
362
|
+
return;
|
363
|
+
}
|
364
|
+
|
365
|
+
try {
|
366
|
+
const anim = lottie.loadAnimation({
|
367
|
+
container: animEl[0],
|
368
|
+
renderer: 'svg',
|
369
|
+
loop: Boolean(itemData.animRepeatCount) || false,
|
370
|
+
autoplay: true,
|
371
|
+
path: animView.animUrl,
|
372
|
+
rendererSettings: {
|
373
|
+
preserveAspectRatio: 'xMidYMid slice',
|
374
|
+
progressiveLoad: true,
|
375
|
+
hideOnTransparent: true,
|
376
|
+
},
|
377
|
+
});
|
378
|
+
|
379
|
+
this.lottieAnimations[animKey] = anim;
|
380
|
+
|
381
|
+
// 非循环动画完成后清理
|
382
|
+
if (!itemData.animRepeatCount) {
|
383
|
+
anim.addEventListener('complete', () => {
|
384
|
+
if (this.lottieAnimations[animKey] === anim) {
|
385
|
+
anim.destroy();
|
386
|
+
delete this.lottieAnimations[animKey];
|
387
|
+
|
388
|
+
// 清空对应的animUrl数据
|
389
|
+
const index = this.data.list.findIndex(i => i.itemView.id == itemId);
|
390
|
+
if (index !== -1) {
|
391
|
+
this.$set(this.data.list[index][`${type}AnimView`], 'animUrl', '');
|
392
|
+
}
|
393
|
+
}
|
394
|
+
});
|
395
|
+
}
|
396
|
+
} catch (error) {
|
397
|
+
console.error(`创建${type}动画失败:`, error);
|
398
|
+
}
|
340
399
|
},
|
341
400
|
getListData(callback) {
|
342
|
-
let data = Array.from(this.
|
401
|
+
let data = Array.from(this.data.list);
|
343
402
|
data = data.map((item) => ({
|
344
403
|
...item,
|
345
404
|
itemView: {
|
@@ -364,7 +423,7 @@ export default {
|
|
364
423
|
},
|
365
424
|
selectIconClick(e, item, index) {
|
366
425
|
e.preventDefault();
|
367
|
-
e.stopPropagation();
|
426
|
+
e.stopPropagation();
|
368
427
|
if (item.selectIcon.visibility) {
|
369
428
|
item.itemView.isSelected = !item.itemView.isSelected;
|
370
429
|
}
|
@@ -380,16 +439,10 @@ export default {
|
|
380
439
|
},
|
381
440
|
|
382
441
|
itemViewClick(e, item, index) {
|
383
|
-
// 如果事件已经被处理或被阻止默认行为,则不继续执行
|
384
442
|
if (e.defaultPrevented) {
|
385
443
|
return;
|
386
444
|
}
|
387
|
-
|
388
|
-
...item.itemView,
|
389
|
-
id: String(item.itemView.id),
|
390
|
-
index,
|
391
|
-
...e,
|
392
|
-
};
|
445
|
+
|
393
446
|
console.log('itemViewClick');
|
394
447
|
// 确保取消任何长按计时器
|
395
448
|
this.cancelLongPress();
|
@@ -401,10 +454,13 @@ export default {
|
|
401
454
|
}
|
402
455
|
e.preventDefault();
|
403
456
|
e.stopPropagation();
|
404
|
-
|
457
|
+
const target = {
|
458
|
+
...item.itemView,
|
459
|
+
id: String(item.itemView.id),
|
460
|
+
index,
|
461
|
+
...e,
|
462
|
+
};
|
405
463
|
this.$emit('onClickItem', target);
|
406
|
-
this.data.isEditing&&item.selectIcon.visibility&&this.$emit('onSelectItems', target);
|
407
|
-
|
408
464
|
},
|
409
465
|
|
410
466
|
actionClick(e, item, index) {
|
@@ -425,14 +481,12 @@ export default {
|
|
425
481
|
return false;
|
426
482
|
},
|
427
483
|
|
428
|
-
// 新增方法:清理除指定项目外的所有动画实例
|
429
484
|
cleanupOtherAnimations(currentItemId) {
|
430
485
|
console.log('清理其他项目的动画实例,当前项目ID:', currentItemId);
|
431
486
|
|
432
487
|
Object.keys(this.lottieAnimations).forEach((key) => {
|
433
488
|
if (!key.includes(`-${currentItemId}`)) {
|
434
489
|
if (this.lottieAnimations[key]) {
|
435
|
-
console.log(`销毁其他项目的动画实例: ${key}`);
|
436
490
|
const anim = this.lottieAnimations[key];
|
437
491
|
|
438
492
|
// 不要立即销毁,而是先停止动画
|
@@ -477,37 +531,55 @@ export default {
|
|
477
531
|
});
|
478
532
|
},
|
479
533
|
|
480
|
-
// 生成处理后的数据列表
|
481
534
|
generateDataList() {
|
482
535
|
if (!this.data || !this.data.list || !this.data.globalData) {
|
483
|
-
return
|
536
|
+
return;
|
484
537
|
}
|
485
538
|
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
'text',
|
493
|
-
'actionButton',
|
494
|
-
'rightIcon',
|
495
|
-
'leftTopIcon',
|
496
|
-
'iconImage',
|
497
|
-
'selectIcon',
|
498
|
-
'loadingAnimView',
|
499
|
-
'bgAnimView',
|
500
|
-
'frontAnimView',
|
501
|
-
];
|
539
|
+
// 防止死循环
|
540
|
+
if (this.isProcessingData) {
|
541
|
+
return;
|
542
|
+
}
|
543
|
+
|
544
|
+
this.isProcessingData = true;
|
502
545
|
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
processedItem
|
546
|
+
try {
|
547
|
+
// 直接处理 this.data.list,为每个项目合并全局数据
|
548
|
+
this.data.list.forEach((item, index) => {
|
549
|
+
const processedItem = this.mergeItemData(item);
|
550
|
+
this.$set(this.data.list, index, processedItem);
|
551
|
+
});
|
552
|
+
} finally {
|
553
|
+
// 确保标志位被重置
|
554
|
+
this.$nextTick(() => {
|
555
|
+
this.isProcessingData = false;
|
507
556
|
});
|
508
|
-
|
557
|
+
}
|
558
|
+
},
|
559
|
+
|
560
|
+
mergeItemData(item) {
|
561
|
+
const processedItem = {};
|
562
|
+
const types = [
|
563
|
+
'itemView',
|
564
|
+
'text',
|
565
|
+
'actionButton',
|
566
|
+
'rightIcon',
|
567
|
+
'leftTopIcon',
|
568
|
+
'iconImage',
|
569
|
+
'selectIcon',
|
570
|
+
'loadingAnimView',
|
571
|
+
'bgAnimView',
|
572
|
+
'frontAnimView',
|
573
|
+
];
|
574
|
+
|
575
|
+
types.forEach((type) => {
|
576
|
+
// 使用mergeDate合并全局数据和项目数据
|
577
|
+
processedItem[type] = this.mergeDate(item[type] || {}, type);
|
509
578
|
});
|
579
|
+
|
580
|
+
return processedItem;
|
510
581
|
},
|
582
|
+
|
511
583
|
getItemStyle(item, type) {
|
512
584
|
const result = this.mergeDate(item[type] || {}, type);
|
513
585
|
console.log(type, this.applyNormalAttribute(result), 'ggg');
|
@@ -538,18 +610,14 @@ export default {
|
|
538
610
|
borderRadius: `${(data.cornerRadius || 0) * scla}px`,
|
539
611
|
opacity: data.alpha || 1,
|
540
612
|
};
|
541
|
-
// 特殊处理visibility和display
|
542
613
|
if (typeof data.visibility === 'number') {
|
543
614
|
if (data.visibility === 0) {
|
544
|
-
// 如果visibility为0,则完全隐藏元素
|
545
615
|
style.display = 'none';
|
546
616
|
} else {
|
547
|
-
// 否则保持原有display属性,并设置visibility
|
548
617
|
style.display = data.display || '';
|
549
618
|
style.visibility = data.visibility === 1 ? 'visible' : 'hidden';
|
550
619
|
}
|
551
620
|
} else {
|
552
|
-
// 如果未设置visibility,保持原有display
|
553
621
|
style.display = data.display || '';
|
554
622
|
}
|
555
623
|
|
@@ -582,18 +650,6 @@ export default {
|
|
582
650
|
this.$emit('onDragEventStart', e);
|
583
651
|
|
584
652
|
// 只在编辑模式下阻止事件冒泡和默认行为
|
585
|
-
if (this.data.isEditing) {
|
586
|
-
if (e.oriEvent) {
|
587
|
-
// e.oriEvent.preventDefault();
|
588
|
-
// e.oriEvent.stopPropagation();
|
589
|
-
// e.oriEvent.stopImmediatePropagation();
|
590
|
-
}
|
591
|
-
// e.preventDefault && e.preventDefault();
|
592
|
-
// e.stopPropagation && e.stopPropagation();
|
593
|
-
// e.stopImmediatePropagation && e.stopImmediatePropagation();
|
594
|
-
}
|
595
|
-
|
596
|
-
// 清除任何现有计时器
|
597
653
|
this.cancelLongPress();
|
598
654
|
// 记录起始触摸位置和索引
|
599
655
|
const touch = e.changedTouches[0];
|
@@ -603,11 +659,6 @@ export default {
|
|
603
659
|
this.longPressStartIndex = index;
|
604
660
|
this.isScrolling = false; // 重置滚动状态
|
605
661
|
|
606
|
-
// 添加临时事件监听器,在长按期间阻止滚动
|
607
|
-
document.addEventListener('touchmove', this.onTouchMove, {
|
608
|
-
passive: false // 设置为false以允许阻止默认行为
|
609
|
-
});
|
610
|
-
|
611
662
|
// 设置新的长按计时器
|
612
663
|
this.longPressTimer = setTimeout(() => {
|
613
664
|
// 只有在没有滚动的情况下才激活拖拽模式
|
@@ -671,13 +722,6 @@ export default {
|
|
671
722
|
this.draggingIndex = index;
|
672
723
|
this.isTouchDragging = true;
|
673
724
|
|
674
|
-
// 此时添加阻止默认行为的事件监听,但只影响拖拽状态下的行为
|
675
|
-
document.addEventListener('touchmove', this.onTouchMove, {
|
676
|
-
passive: false, // 只有在拖拽模式下才阻止默认行为
|
677
|
-
});
|
678
|
-
document.addEventListener('touchend', this.onTouchEnd, {
|
679
|
-
passive: true,
|
680
|
-
});
|
681
725
|
|
682
726
|
// 使用存储的固定宽高
|
683
727
|
this.draggedItemRect = {
|
@@ -696,11 +740,8 @@ export default {
|
|
696
740
|
// 设置克隆元素的初始位置
|
697
741
|
this.dragCloneX = rect.left;
|
698
742
|
this.dragCloneY = rect.top;
|
699
|
-
|
700
|
-
// 显示拖拽克隆
|
701
743
|
this.isDragClone = true;
|
702
744
|
|
703
|
-
// 立即执行一次定位以确保初始位置准确
|
704
745
|
requestAnimationFrame(() => {
|
705
746
|
this.dragCloneX = this.touchStartX - this.touchOffsetX;
|
706
747
|
this.dragCloneY = this.touchStartY - this.touchOffsetY;
|
@@ -732,7 +773,7 @@ export default {
|
|
732
773
|
if (e.oriEvent) {
|
733
774
|
e.oriEvent.preventDefault();
|
734
775
|
e.oriEvent.stopPropagation();
|
735
|
-
e.oriEvent.stopImmediatePropagation();
|
776
|
+
e.oriEvent.stopImmediatePropagation();
|
736
777
|
}
|
737
778
|
e.preventDefault && e.preventDefault();
|
738
779
|
e.stopPropagation && e.stopPropagation();
|
@@ -750,122 +791,72 @@ export default {
|
|
750
791
|
this.animationFrameId = requestAnimationFrame(this.updateDragPosition);
|
751
792
|
}
|
752
793
|
|
753
|
-
|
754
|
-
|
755
|
-
|
794
|
+
// 简化的交换逻辑
|
795
|
+
this.handleSwapLogic(touch.pageX, touch.pageY);
|
796
|
+
},
|
797
|
+
|
798
|
+
// 简化的交换处理逻辑
|
799
|
+
handleSwapLogic(touchX, touchY) {
|
800
|
+
// 控制交换频率,避免过于频繁的交换
|
801
|
+
const now = Date.now();
|
802
|
+
if (now - this.lastSwapTime < 300) return;
|
756
803
|
|
757
804
|
// 获取所有网格元素
|
758
805
|
const gridItems = Array.from(document.querySelectorAll('.grid-item'));
|
806
|
+
let targetIndex = null;
|
759
807
|
|
760
|
-
|
761
|
-
let maxCenterScore = 0; // 用于记录最佳的中心区域得分
|
762
|
-
|
763
|
-
// ✅ 优化:精确坐标检测,偏向中心区域的元素
|
808
|
+
// 寻找触摸点所在的目标元素
|
764
809
|
for (const item of gridItems) {
|
765
810
|
const itemIndex = parseInt(item.getAttribute('data-index'), 10);
|
766
811
|
if (itemIndex === this.draggingIndex) continue;
|
767
812
|
|
768
813
|
const rect = item.getBoundingClientRect();
|
769
|
-
|
770
|
-
//
|
771
|
-
const
|
772
|
-
const
|
773
|
-
|
774
|
-
|
775
|
-
|
776
|
-
|
777
|
-
|
778
|
-
|
779
|
-
|
780
|
-
|
781
|
-
|
782
|
-
|
783
|
-
|
784
|
-
|
785
|
-
const centerY = (rect.top + rect.bottom) / 2;
|
786
|
-
const distanceX = Math.abs(touchX - centerX) / (rect.width / 2);
|
787
|
-
const distanceY = Math.abs(touchY - centerY) / (rect.height / 2);
|
788
|
-
|
789
|
-
// 0表示在中心,1表示在边缘
|
790
|
-
const distanceFromCenter = Math.max(
|
791
|
-
0,
|
792
|
-
Math.min(1, (distanceX + distanceY) / 2)
|
793
|
-
);
|
794
|
-
const centerScore = 1 - distanceFromCenter;
|
795
|
-
|
796
|
-
// 只有当分数更高且高于阈值时才更新bestTarget
|
797
|
-
// 提高阈值到0.35,确保更稳定的交换判断
|
798
|
-
if (centerScore > maxCenterScore && centerScore > 0.35) {
|
799
|
-
maxCenterScore = centerScore;
|
800
|
-
bestTarget = itemIndex;
|
801
|
-
}
|
802
|
-
}
|
803
|
-
}
|
804
|
-
|
805
|
-
// 设置悬停高亮,仅当有合格的目标时才高亮
|
806
|
-
this.hoverIndex = bestTarget;
|
807
|
-
|
808
|
-
// 增加交换防抖动处理
|
809
|
-
// 如果bestTarget变化但不等于上次的目标,先记录一下
|
810
|
-
if (this.lastBestTarget !== bestTarget && bestTarget !== null) {
|
811
|
-
if (!this.targetChangeTime) {
|
812
|
-
this.targetChangeTime = Date.now();
|
813
|
-
this.pendingTarget = bestTarget;
|
814
|
-
} else if (Date.now() - this.targetChangeTime > 100) {
|
815
|
-
// 只有当目标持续100ms稳定不变时,才认为是有效目标
|
816
|
-
this.lastBestTarget = this.pendingTarget;
|
817
|
-
this.targetChangeTime = null;
|
818
|
-
this.pendingTarget = null;
|
814
|
+
|
815
|
+
// 触摸点是否在元素的中心区域内
|
816
|
+
const centerX = (rect.left + rect.right) / 2;
|
817
|
+
const centerY = (rect.top + rect.bottom) / 2;
|
818
|
+
const halfWidth = rect.width * 0.4; // 缩小判断区域到40%,避免边缘误触
|
819
|
+
const halfHeight = rect.height * 0.4;
|
820
|
+
|
821
|
+
// 检查触摸点是否在元素的中心区域内
|
822
|
+
if (
|
823
|
+
touchX >= centerX - halfWidth &&
|
824
|
+
touchX <= centerX + halfWidth &&
|
825
|
+
touchY >= centerY - halfHeight &&
|
826
|
+
touchY <= centerY + halfHeight
|
827
|
+
) {
|
828
|
+
targetIndex = itemIndex;
|
829
|
+
break; // 找到第一个符合条件的就退出
|
819
830
|
}
|
820
|
-
} else if (bestTarget === null) {
|
821
|
-
// 如果没有目标,清除防抖状态
|
822
|
-
this.targetChangeTime = null;
|
823
|
-
this.pendingTarget = null;
|
824
831
|
}
|
825
832
|
|
826
|
-
//
|
827
|
-
|
828
|
-
|
829
|
-
// 控制交换频率,改为250ms,大幅增加稳定性
|
830
|
-
const now = Date.now();
|
831
|
-
if (now - this.lastSwapTime < 250 || bestTarget === null) return;
|
832
|
-
|
833
|
-
// 执行交换逻辑
|
834
|
-
if (bestTarget !== this.draggingIndex) {
|
835
|
-
console.log(`交换元素: ${this.draggingIndex} 和 ${bestTarget}`);
|
833
|
+
// 设置悬停高亮
|
834
|
+
this.hoverIndex = targetIndex;
|
836
835
|
|
837
|
-
|
836
|
+
// 执行交换
|
837
|
+
if (targetIndex !== null && targetIndex !== this.draggingIndex) {
|
838
838
|
[
|
839
|
-
this.
|
840
|
-
this.
|
839
|
+
this.data.list[this.draggingIndex],
|
840
|
+
this.data.list[targetIndex],
|
841
841
|
] = [
|
842
|
-
this.
|
843
|
-
this.processedList[this.draggingIndex],
|
844
|
-
];
|
845
|
-
|
846
|
-
// 同时更新原始数据,保持同步
|
847
|
-
[this.data.list[this.draggingIndex], this.data.list[bestTarget]] = [
|
848
|
-
this.data.list[bestTarget],
|
842
|
+
this.data.list[targetIndex],
|
849
843
|
this.data.list[this.draggingIndex],
|
850
844
|
];
|
845
|
+
console.log(`交换: ${this.draggingIndex} ↔ ${targetIndex}`);
|
851
846
|
|
852
847
|
// 更新拖拽索引
|
853
|
-
this.draggingIndex =
|
848
|
+
this.draggingIndex = targetIndex;
|
854
849
|
this.lastSwapTime = now;
|
855
850
|
}
|
856
851
|
},
|
857
852
|
|
858
|
-
//
|
853
|
+
// 平滑更新拖拽位置的方法
|
859
854
|
updateDragPosition() {
|
860
|
-
// 计算当前位置到目标位置的平滑过渡
|
861
855
|
const easing = 0.6; // 增加缓动系数,让拖拽更跟手
|
862
|
-
|
863
856
|
this.dragCloneX +=
|
864
857
|
(this.targetTouchX - this.touchOffsetX - this.dragCloneX) * easing;
|
865
858
|
this.dragCloneY +=
|
866
859
|
(this.targetTouchY - this.touchOffsetY - this.dragCloneY) * easing;
|
867
|
-
|
868
|
-
// 如果距离目标很近,直接设为目标位置以避免微小抖动
|
869
860
|
if (
|
870
861
|
Math.abs(this.dragCloneX - (this.targetTouchX - this.touchOffsetX)) <
|
871
862
|
0.5
|
@@ -876,8 +867,6 @@ export default {
|
|
876
867
|
0.5
|
877
868
|
)
|
878
869
|
this.dragCloneY = this.targetTouchY - this.touchOffsetY;
|
879
|
-
|
880
|
-
// 继续下一帧动画
|
881
870
|
this.animationFrameId = requestAnimationFrame(this.updateDragPosition);
|
882
871
|
},
|
883
872
|
|
@@ -887,17 +876,8 @@ export default {
|
|
887
876
|
this.$emit('onDragEventEnd', e);
|
888
877
|
// 如果只是短按(还在计时),就取消长按
|
889
878
|
this.cancelLongPress();
|
890
|
-
|
891
879
|
// 如果正在拖拽中,确保清理状态
|
892
880
|
if (this.isTouchDragging) {
|
893
|
-
// 移除全局事件监听
|
894
|
-
document.removeEventListener('touchmove', this.onTouchMove, {
|
895
|
-
passive: false,
|
896
|
-
});
|
897
|
-
document.removeEventListener('touchend', this.onTouchEnd, {
|
898
|
-
passive: false,
|
899
|
-
});
|
900
|
-
|
901
881
|
// 取消动画帧
|
902
882
|
if (this.animationFrameId) {
|
903
883
|
cancelAnimationFrame(this.animationFrameId);
|
@@ -908,28 +888,12 @@ export default {
|
|
908
888
|
this.isDragClone = false;
|
909
889
|
this.hoverIndex = null;
|
910
890
|
this.draggingIndex = null;
|
911
|
-
this.lastBestTarget = null;
|
912
|
-
this.targetChangeTime = null;
|
913
|
-
this.pendingTarget = null;
|
914
|
-
this.touchStartX = 0;
|
915
|
-
this.touchStartY = 0;
|
916
|
-
this.targetTouchX = 0;
|
917
|
-
this.targetTouchY = 0;
|
918
891
|
}
|
919
892
|
},
|
920
893
|
|
921
894
|
// 触摸结束处理 - 全局事件
|
922
895
|
onTouchEnd(e) {
|
923
896
|
console.log('全局触摸结束');
|
924
|
-
|
925
|
-
// 移除全局事件监听
|
926
|
-
document.removeEventListener('touchmove', this.onTouchMove, {
|
927
|
-
passive: false,
|
928
|
-
});
|
929
|
-
document.removeEventListener('touchend', this.onTouchEnd, {
|
930
|
-
passive: false,
|
931
|
-
});
|
932
|
-
|
933
897
|
// 取消动画帧
|
934
898
|
if (this.animationFrameId) {
|
935
899
|
cancelAnimationFrame(this.animationFrameId);
|
@@ -985,168 +949,27 @@ export default {
|
|
985
949
|
|
986
950
|
console.log(
|
987
951
|
'开始初始化Lottie动画,列表项数量:',
|
988
|
-
this.
|
952
|
+
this.data.list.length
|
989
953
|
);
|
990
954
|
|
991
955
|
// 延迟一点时间确保DOM完全更新并且之前的动画已被销毁
|
992
956
|
setTimeout(() => {
|
993
957
|
// 为每个列表项初始化动画
|
994
|
-
this.
|
958
|
+
this.data.list.forEach((item) => {
|
995
959
|
const itemId = String(item.itemView.id);
|
996
|
-
console.log('处理项目ID:', itemId);
|
997
|
-
console.log('loadingAnimView:', item.loadingAnimView);
|
998
|
-
console.log('bgAnimView:', item.bgAnimView);
|
999
|
-
console.log('frontAnimView:', item.frontAnimView);
|
1000
|
-
|
1001
960
|
// 加载背景动画
|
1002
|
-
this.
|
1003
|
-
item.bgAnimView,
|
1004
|
-
`bgAnim-${itemId}`,
|
1005
|
-
`bg-${itemId}`,
|
1006
|
-
item
|
1007
|
-
);
|
961
|
+
this.createLottieAnimation('bg', itemId, item.bgAnimView, item);
|
1008
962
|
|
1009
963
|
// 加载加载中动画
|
1010
|
-
this.
|
1011
|
-
item.loadingAnimView,
|
1012
|
-
`loadingAnim-${itemId}`,
|
1013
|
-
`loading-${itemId}`,
|
1014
|
-
item
|
1015
|
-
);
|
964
|
+
this.createLottieAnimation('loading', itemId, item.loadingAnimView, item);
|
1016
965
|
|
1017
966
|
// 加载前景动画
|
1018
|
-
this.
|
1019
|
-
item.frontAnimView,
|
1020
|
-
`frontAnim-${itemId}`,
|
1021
|
-
`front-${itemId}`,
|
1022
|
-
item
|
1023
|
-
);
|
967
|
+
this.createLottieAnimation('front', itemId, item.frontAnimView, item);
|
1024
968
|
});
|
1025
969
|
}, 50); // 小延迟确保DOM已更新和旧动画已销毁
|
1026
970
|
});
|
1027
971
|
},
|
1028
972
|
|
1029
|
-
// 抽取公共的loadLottieAnimation方法
|
1030
|
-
loadLottieAnimation(animView, refName, animKey, item) {
|
1031
|
-
if (!animView || !animView.animUrl) {
|
1032
|
-
console.log(`[${animKey}] 跳过加载:animUrl不存在`);
|
1033
|
-
return;
|
1034
|
-
}
|
1035
|
-
|
1036
|
-
console.log(
|
1037
|
-
`[${animKey}] 尝试初始化${refName}, animUrl:`,
|
1038
|
-
animView.animUrl
|
1039
|
-
);
|
1040
|
-
|
1041
|
-
const animEl = this.$refs[refName];
|
1042
|
-
if (!animEl || !animEl[0]) {
|
1043
|
-
console.warn(`[${animKey}] 未找到${refName}的DOM引用`);
|
1044
|
-
return;
|
1045
|
-
}
|
1046
|
-
|
1047
|
-
try {
|
1048
|
-
// 如果已经有实例,先暂停它
|
1049
|
-
if (this.lottieAnimations[animKey]) {
|
1050
|
-
this.lottieAnimations[animKey].pause();
|
1051
|
-
}
|
1052
|
-
|
1053
|
-
// 确保容器可见且透明度重置
|
1054
|
-
animEl[0].style.display = '';
|
1055
|
-
animEl[0].style.opacity = '1';
|
1056
|
-
animEl[0].style.transition = 'opacity 0.2s';
|
1057
|
-
|
1058
|
-
if (
|
1059
|
-
typeof animView.visibility !== 'number' ||
|
1060
|
-
animView.visibility !== 0
|
1061
|
-
) {
|
1062
|
-
// 延迟一帧再销毁旧实例
|
1063
|
-
requestAnimationFrame(() => {
|
1064
|
-
if (this.lottieAnimations[animKey]) {
|
1065
|
-
this.lottieAnimations[animKey].destroy();
|
1066
|
-
delete this.lottieAnimations[animKey];
|
1067
|
-
}
|
1068
|
-
|
1069
|
-
// 创建新的动画实例
|
1070
|
-
console.log(`[${animKey}] 创建新的动画实例`);
|
1071
|
-
const anim = lottie.loadAnimation({
|
1072
|
-
container: animEl[0],
|
1073
|
-
renderer: 'svg',
|
1074
|
-
loop: Boolean(item.animRepeatCount) || false,
|
1075
|
-
autoplay: true,
|
1076
|
-
path: animView.animUrl,
|
1077
|
-
rendererSettings: {
|
1078
|
-
preserveAspectRatio: 'xMidYMid slice',
|
1079
|
-
progressiveLoad: true, // 启用渐进式加载
|
1080
|
-
hideOnTransparent: true, // 优化透明度处理
|
1081
|
-
},
|
1082
|
-
});
|
1083
|
-
|
1084
|
-
this.lottieAnimations[animKey] = anim;
|
1085
|
-
anim.addEventListener('complete', () => {
|
1086
|
-
// 检查是否需要销毁动画(非循环动画完成后销毁)
|
1087
|
-
if (!item.animRepeatCount) {
|
1088
|
-
// 销毁前再次检查实例是否存在
|
1089
|
-
if (this.lottieAnimations[animKey] === anim) {
|
1090
|
-
anim.removeEventListener('complete');
|
1091
|
-
anim.destroy();
|
1092
|
-
delete this.lottieAnimations[animKey];
|
1093
|
-
// 将对应数据中的animUrl清空
|
1094
|
-
const itemId = String(item.itemView.id);
|
1095
|
-
const index = this.processedList.findIndex(
|
1096
|
-
(i) => i.itemView.id == itemId
|
1097
|
-
);
|
1098
|
-
if (index !== -1) {
|
1099
|
-
// 根据动画类型设置对应的animUrl为空
|
1100
|
-
if (animKey.startsWith('bg-')) {
|
1101
|
-
this.$set(
|
1102
|
-
this.processedList[index].bgAnimView,
|
1103
|
-
'animUrl',
|
1104
|
-
''
|
1105
|
-
);
|
1106
|
-
this.$set(
|
1107
|
-
this.data.list[index].bgAnimView,
|
1108
|
-
'animUrl',
|
1109
|
-
''
|
1110
|
-
);
|
1111
|
-
} else if (animKey.startsWith('loading-')) {
|
1112
|
-
this.$set(
|
1113
|
-
this.processedList[index].loadingAnimView,
|
1114
|
-
'animUrl',
|
1115
|
-
''
|
1116
|
-
);
|
1117
|
-
this.$set(
|
1118
|
-
this.data.list[index].loadingAnimView,
|
1119
|
-
'animUrl',
|
1120
|
-
''
|
1121
|
-
);
|
1122
|
-
} else if (animKey.startsWith('front-')) {
|
1123
|
-
this.$set(
|
1124
|
-
this.processedList[index].frontAnimView,
|
1125
|
-
'animUrl',
|
1126
|
-
''
|
1127
|
-
);
|
1128
|
-
this.$set(
|
1129
|
-
this.data.list[index].frontAnimView,
|
1130
|
-
'animUrl',
|
1131
|
-
''
|
1132
|
-
);
|
1133
|
-
}
|
1134
|
-
console.log(`[${animKey}] 已清空动画URL数据`);
|
1135
|
-
item.addEventListener('touchstart', (e) => this.startLongPress(e, index), { passive: false });
|
1136
|
-
item.addEventListener('touchmove', this.checkLongPressMove, { passive: false });
|
1137
|
-
item.addEventListener('touchend', this.endTouch, { passive: false });
|
1138
|
-
item.addEventListener('touchcancel', this.endTouch, { passive: false });
|
1139
|
-
}
|
1140
|
-
}
|
1141
|
-
}
|
1142
|
-
});
|
1143
|
-
});
|
1144
|
-
}
|
1145
|
-
} catch (error) {
|
1146
|
-
console.error(`[${animKey}] 初始化失败:`, error);
|
1147
|
-
}
|
1148
|
-
},
|
1149
|
-
|
1150
973
|
// 添加新方法用于获取和存储固定宽高
|
1151
974
|
updateFixedItemSize() {
|
1152
975
|
this.$nextTick(() => {
|
@@ -1168,110 +991,55 @@ export default {
|
|
1168
991
|
},
|
1169
992
|
},
|
1170
993
|
watch: {
|
1171
|
-
|
1172
|
-
|
1173
|
-
|
1174
|
-
|
1175
|
-
|
1176
|
-
}
|
1177
|
-
|
1178
|
-
deep: true,
|
994
|
+
'data.isEditing':{
|
995
|
+
handler(newVal){
|
996
|
+
if(!newVal){
|
997
|
+
this.$set(this.data, 'list', this.data.list);
|
998
|
+
}
|
999
|
+
},
|
1000
|
+
deep:true
|
1179
1001
|
},
|
1180
1002
|
// 监听列表数据变化,自动更新处理后的数据
|
1181
1003
|
'data.list': {
|
1182
1004
|
handler(newList, oldList) {
|
1183
|
-
|
1005
|
+
// 防止死循环
|
1006
|
+
if (this.isProcessingData) {
|
1007
|
+
return;
|
1008
|
+
}
|
1009
|
+
|
1010
|
+
console.log('检测到data.list变化');
|
1011
|
+
this.generateDataList();
|
1184
1012
|
this.$nextTick(() => {
|
1185
|
-
|
1186
|
-
this
|
1187
|
-
const gridItems = document.querySelectorAll('.grid-item');
|
1188
|
-
gridItems.forEach((item, index) => {
|
1189
|
-
const hasEvents = item._vei;
|
1190
|
-
if (!hasEvents) {
|
1191
|
-
item.addEventListener('touchstart', (e) => this.startLongPress(e, index), { passive: false });
|
1192
|
-
item.addEventListener('touchmove', this.checkLongPressMove, { passive: false });
|
1193
|
-
item.addEventListener('touchend', this.endTouch, { passive: false });
|
1194
|
-
item.addEventListener('touchcancel', this.endTouch, { passive: false });
|
1195
|
-
}
|
1196
|
-
});
|
1197
|
-
this.initLottieAnimations();
|
1198
|
-
});
|
1013
|
+
this.initLottieAnimations();
|
1014
|
+
this.rebindGestureEvents();
|
1199
1015
|
});
|
1200
1016
|
},
|
1201
1017
|
deep: true,
|
1202
1018
|
},
|
1203
|
-
// 监听所有动画URL变化,确保能及时更新动画
|
1204
|
-
processedList: {
|
1205
|
-
handler(newList, oldList) {
|
1206
|
-
if (!newList || !newList.length || this.isTouchDragging) return;
|
1207
|
-
// 避免在拖拽过程中重新初始化动画
|
1208
|
-
if (this.draggingIndex !== null) return;
|
1209
|
-
// 检查每个项目的动画URL是否有变化
|
1210
|
-
},
|
1211
|
-
deep: true,
|
1212
|
-
},
|
1213
1019
|
},
|
1020
|
+
|
1214
1021
|
mounted() {
|
1022
|
+
this.initGestureConfig()
|
1215
1023
|
console.log(this.data, 'djdjdjdjdjdjdj');
|
1216
1024
|
// 初始获取grid-box宽度
|
1217
1025
|
this.updateGridBoxWidth();
|
1218
1026
|
// 获取并存储固定宽高
|
1219
1027
|
this.updateFixedItemSize();
|
1220
|
-
|
1221
|
-
// 监听窗口大小变化,更新grid-box宽度
|
1222
|
-
window.addEventListener('resize', () => {
|
1223
|
-
this.updateGridBoxWidth();
|
1224
|
-
this.updateFixedItemSize();
|
1225
|
-
});
|
1226
|
-
|
1227
1028
|
// 初始化Lottie动画
|
1228
1029
|
this.initLottieAnimations();
|
1229
1030
|
},
|
1230
1031
|
beforeDestroy() {
|
1231
1032
|
// 确保清理所有事件监听器
|
1232
1033
|
this.cancelLongPress();
|
1233
|
-
document.removeEventListener('touchmove', this.onTouchMove, {
|
1234
|
-
passive: false,
|
1235
|
-
});
|
1236
|
-
document.removeEventListener('touchend', this.onTouchEnd, {
|
1237
|
-
passive: false,
|
1238
|
-
});
|
1239
|
-
window.removeEventListener('resize', () => {
|
1240
|
-
this.updateGridBoxWidth();
|
1241
|
-
this.updateFixedItemSize();
|
1242
|
-
});
|
1243
|
-
|
1244
1034
|
// 清理动画帧
|
1245
1035
|
if (this.animationFrameId) {
|
1246
1036
|
cancelAnimationFrame(this.animationFrameId);
|
1247
1037
|
this.animationFrameId = null;
|
1248
1038
|
}
|
1249
|
-
|
1250
|
-
// 清理所有Lottie动画实例
|
1251
|
-
Object.keys(this.lottieAnimations).forEach((key) => {
|
1252
|
-
const anim = this.lottieAnimations[key];
|
1253
|
-
if (anim && typeof anim.destroy === 'function') {
|
1254
|
-
console.log(`组件销毁时清理动画: ${key}`);
|
1255
|
-
try {
|
1256
|
-
anim.removeEventListener('complete');
|
1257
|
-
anim.removeEventListener('DOMLoaded');
|
1258
|
-
anim.removeEventListener('data_ready');
|
1259
|
-
anim.removeEventListener('loaded_images');
|
1260
|
-
anim.removeEventListener('loopComplete');
|
1261
|
-
} catch (e) {
|
1262
|
-
console.warn(`移除事件监听器失败: ${e.message}`);
|
1263
|
-
}
|
1264
|
-
anim.destroy();
|
1265
|
-
}
|
1266
|
-
delete this.lottieAnimations[key];
|
1267
|
-
});
|
1268
|
-
|
1269
|
-
// 确保动画实例对象被清空
|
1270
|
-
this.lottieAnimations = {};
|
1271
1039
|
},
|
1272
1040
|
created() {
|
1273
1041
|
// 初始化处理后的数据列表
|
1274
|
-
this.
|
1042
|
+
this.generateDataList();
|
1275
1043
|
},
|
1276
1044
|
};
|
1277
1045
|
</script>
|
@@ -1302,7 +1070,7 @@ button {
|
|
1302
1070
|
display: grid;
|
1303
1071
|
grid-template-columns: repeat(var(--grid-cols), 1fr);
|
1304
1072
|
grid-gap: var(--grid-gap);
|
1305
|
-
width: calc(100% -
|
1073
|
+
width: calc(100% - var(--grid-margin));
|
1306
1074
|
}
|
1307
1075
|
|
1308
1076
|
/* 优化排序过渡效果 */
|