mdm-client 1.0.3 → 1.0.4
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 +1 -1
- package/src/App.vue +72 -67
- package/src/assets/image/common/layout-active16.png +0 -0
- package/src/assets/image/common/layout-active25.png +0 -0
- package/src/assets/image/common/layout-active3.png +0 -0
- package/src/assets/image/common/layout-active9.png +0 -0
- package/src/assets/image/common/layout16.png +0 -0
- package/src/assets/image/common/layout25.png +0 -0
- package/src/assets/image/common/layout3.png +0 -0
- package/src/assets/image/common/layout9.png +0 -0
- package/src/assets/image/common/mirror.png +0 -0
- package/src/assets/image/common/rotate_icon1.png +0 -0
- package/src/assets/image/common/rotate_icon2.png +0 -0
- package/src/assets/image/common/rotate_icon3.png +0 -0
- package/src/assets/image/common/rotate_icon4.png +0 -0
- package/src/assets/style/base.scss +5 -0
- package/src/components/LiveMulti/LiveMulti.vue +27 -6
- package/src/components/LiveMultipleMeeting/LiveMultipleMeeting.vue +1063 -98
- package/src/components/LiveMultipleMeeting/style/index.scss +145 -14
- package/src/components/LivePoint/LivePoint.vue +49 -211
- package/src/components/LivePointMeeting/LivePointMeeting.vue +159 -10
- package/src/components/LivePointMeeting/style/index.scss +35 -0
- package/src/components/MeetingReadyDialog/MeetingReadyDialog.vue +96 -14
- package/src/components/other/addressBook.vue +137 -20
- package/src/components/other/appointDialog.vue +1 -1
- package/src/components/other/customLayout.vue +368 -202
- package/src/components/other/layoutSwitch.vue +253 -37
- package/src/components/other/leadershipFocus.vue +422 -0
- package/src/components/other/leaveOptionDialog.vue +1 -1
- package/src/components/other/moreOptionDialog.vue +17 -1
- package/src/components/other/selectDialog.vue +1 -1
- package/src/components/other/selectSpecialDialog.vue +1 -1
- package/src/utils/api.js +19 -0
- package/src/utils/livekit/live-client-esm.js +1 -1
|
@@ -14,18 +14,17 @@
|
|
|
14
14
|
<div class="custom-layout-simulate-header">
|
|
15
15
|
<div class="custom-layout-simulate-header-options">
|
|
16
16
|
<div class="layout-list">
|
|
17
|
-
<div
|
|
18
|
-
:class="['layout-list-item', item]"
|
|
19
|
-
v-for="item of layoutList"
|
|
17
|
+
<div
|
|
18
|
+
:class="['layout-list-item', item]"
|
|
19
|
+
v-for="item of layoutList"
|
|
20
20
|
:key="item"
|
|
21
21
|
@click="handleLayoutChange(item)"
|
|
22
22
|
>
|
|
23
23
|
<div
|
|
24
|
-
:class="
|
|
25
|
-
|
|
26
|
-
{ 'layout-list-item-icon-active': activeLayout === item }
|
|
27
|
-
]
|
|
28
|
-
"
|
|
24
|
+
:class="[
|
|
25
|
+
'layout-list-item-icon',
|
|
26
|
+
{ 'layout-list-item-icon-active': activeLayout === item },
|
|
27
|
+
]"
|
|
29
28
|
></div>
|
|
30
29
|
</div>
|
|
31
30
|
</div>
|
|
@@ -40,7 +39,7 @@
|
|
|
40
39
|
<div :class="['custom-layout-simulate-show', `layout-${activeLayout}`]">
|
|
41
40
|
<div class="layout-container">
|
|
42
41
|
<LayoutPlaceholder
|
|
43
|
-
v-for="index in placeholderCount"
|
|
42
|
+
v-for="index in placeholderCount"
|
|
44
43
|
:key="index"
|
|
45
44
|
:index="index"
|
|
46
45
|
:member="assignedMembers[index]"
|
|
@@ -72,11 +71,14 @@
|
|
|
72
71
|
<div class="custom-layout-member-list">
|
|
73
72
|
<div
|
|
74
73
|
class="custom-layout-member-list-item"
|
|
75
|
-
:class="{
|
|
74
|
+
:class="{
|
|
75
|
+
'is-dragging':
|
|
76
|
+
isDragging && currentDragMember && currentDragMember.identity === item.identity,
|
|
77
|
+
}"
|
|
76
78
|
v-for="item of memberList"
|
|
77
79
|
:key="item.identity"
|
|
78
80
|
>
|
|
79
|
-
<div
|
|
81
|
+
<div
|
|
80
82
|
class="custom-layout-member-list-item-drag-icon"
|
|
81
83
|
@mousedown="handleMouseDown(item, $event)"
|
|
82
84
|
@touchstart="handleTouchStart(item, $event)"
|
|
@@ -87,13 +89,9 @@
|
|
|
87
89
|
</div>
|
|
88
90
|
</div>
|
|
89
91
|
</div>
|
|
90
|
-
|
|
92
|
+
|
|
91
93
|
<!-- 拖拽时的浮动元素 -->
|
|
92
|
-
<div
|
|
93
|
-
v-if="isDragging && currentDragMember"
|
|
94
|
-
class="drag-ghost"
|
|
95
|
-
:style="dragGhostStyle"
|
|
96
|
-
>
|
|
94
|
+
<div v-if="isDragging && currentDragMember" class="drag-ghost" :style="dragGhostStyle">
|
|
97
95
|
<div class="drag-ghost-avatar"></div>
|
|
98
96
|
<div class="drag-ghost-name">{{ currentDragMember.name }}</div>
|
|
99
97
|
</div>
|
|
@@ -106,10 +104,12 @@
|
|
|
106
104
|
import LayoutPlaceholder from "./LayoutPlaceholder.vue";
|
|
107
105
|
import { ShowMessage } from "../../utils/index";
|
|
108
106
|
|
|
107
|
+
const STORAGE_PREFIX = "customLayoutState";
|
|
108
|
+
let reconcileTimer = null;
|
|
109
109
|
export default {
|
|
110
110
|
name: "CustomLayout",
|
|
111
111
|
components: {
|
|
112
|
-
LayoutPlaceholder
|
|
112
|
+
LayoutPlaceholder,
|
|
113
113
|
},
|
|
114
114
|
props: {
|
|
115
115
|
participantList: {
|
|
@@ -126,7 +126,7 @@ export default {
|
|
|
126
126
|
searchVal: "",
|
|
127
127
|
memberList: [],
|
|
128
128
|
autoFill: true,
|
|
129
|
-
layoutList: ["grid4", "grid9", "ring", "
|
|
129
|
+
layoutList: ["grid4", "grid9", "ring", "rightSide"],
|
|
130
130
|
activeLayout: "grid4",
|
|
131
131
|
// 拖拽相关状态
|
|
132
132
|
isDragging: false,
|
|
@@ -138,7 +138,9 @@ export default {
|
|
|
138
138
|
hoverPlaceholderIndex: null,
|
|
139
139
|
currentMousePosition: { x: 0, y: 0 },
|
|
140
140
|
liveClient: null,
|
|
141
|
-
showMessage: new ShowMessage()
|
|
141
|
+
showMessage: new ShowMessage(),
|
|
142
|
+
// 参会者变化对齐延迟标记,避免拖拽过程被打断
|
|
143
|
+
pendingReconcile: false,
|
|
142
144
|
};
|
|
143
145
|
},
|
|
144
146
|
computed: {
|
|
@@ -151,19 +153,44 @@ export default {
|
|
|
151
153
|
// 计算不同布局下的占位元素数量
|
|
152
154
|
placeholderCount() {
|
|
153
155
|
return this.getLayoutPlaceholderCount(this.activeLayout);
|
|
154
|
-
}
|
|
156
|
+
},
|
|
157
|
+
// 监听参会者身份集合变化(不仅仅是数量),防抖处理
|
|
158
|
+
participantIdentitySignature() {
|
|
159
|
+
return (this.participantList || [])
|
|
160
|
+
.map((m) => m && m.identity)
|
|
161
|
+
.filter(Boolean)
|
|
162
|
+
.sort()
|
|
163
|
+
.join(",");
|
|
164
|
+
},
|
|
155
165
|
},
|
|
156
166
|
watch: {
|
|
157
167
|
searchVal(newVal) {
|
|
158
|
-
this
|
|
168
|
+
this.$nextTick(() => {
|
|
169
|
+
this.getMemberList(newVal);
|
|
170
|
+
});
|
|
159
171
|
},
|
|
160
172
|
participantNum(newVal) {
|
|
161
173
|
this.getMemberList();
|
|
162
|
-
}
|
|
174
|
+
},
|
|
175
|
+
isDragging(newVal) {
|
|
176
|
+
if (!newVal && this.pendingReconcile) {
|
|
177
|
+
// 拖拽结束后再处理未完成的对齐
|
|
178
|
+
this.pendingReconcile = false;
|
|
179
|
+
Promise.resolve().then(() => this.reconcileAssignedMembers());
|
|
180
|
+
}
|
|
181
|
+
},
|
|
182
|
+
participantIdentitySignature(newVal) {
|
|
183
|
+
if (reconcileTimer) clearTimeout(reconcileTimer);
|
|
184
|
+
reconcileTimer = setTimeout(() => {
|
|
185
|
+
this.reconcileAssignedMembers();
|
|
186
|
+
}, 120);
|
|
187
|
+
},
|
|
163
188
|
},
|
|
164
189
|
mounted() {
|
|
165
190
|
this.getMemberList();
|
|
166
191
|
this.liveClient = window["liveClient"];
|
|
192
|
+
// 打开弹窗时尝试恢复上一次应用的布局
|
|
193
|
+
this.restoreLayoutState();
|
|
167
194
|
},
|
|
168
195
|
beforeDestroy() {
|
|
169
196
|
this.liveClient = null;
|
|
@@ -175,8 +202,7 @@ export default {
|
|
|
175
202
|
grid4: 4,
|
|
176
203
|
grid9: 9,
|
|
177
204
|
ring: 8,
|
|
178
|
-
|
|
179
|
-
rightSide: 4
|
|
205
|
+
rightSide: 4,
|
|
180
206
|
};
|
|
181
207
|
return layoutCountMap[layout] || 4;
|
|
182
208
|
},
|
|
@@ -184,95 +210,96 @@ export default {
|
|
|
184
210
|
// 长按开始拖拽 - 鼠标事件
|
|
185
211
|
handleMouseDown(member, event) {
|
|
186
212
|
event.preventDefault();
|
|
187
|
-
|
|
213
|
+
|
|
188
214
|
// 使用固定偏移量,让浮动元素在鼠标右下角
|
|
189
215
|
this.dragOffset = { x: 10, y: 10 };
|
|
190
|
-
|
|
216
|
+
|
|
191
217
|
// 设置初始鼠标位置
|
|
192
218
|
this.currentMousePosition = { x: event.clientX, y: event.clientY };
|
|
193
|
-
|
|
219
|
+
|
|
194
220
|
this.startDragTimer(member);
|
|
195
|
-
|
|
221
|
+
|
|
196
222
|
const startX = event.clientX;
|
|
197
223
|
const startY = event.clientY;
|
|
198
|
-
|
|
224
|
+
|
|
199
225
|
const handleMouseMove = (e) => {
|
|
200
226
|
// 保存鼠标实际位置
|
|
201
227
|
this.currentMousePosition = { x: e.clientX, y: e.clientY };
|
|
202
|
-
|
|
228
|
+
|
|
203
229
|
// 如果鼠标移动超过5px,清除长按定时器
|
|
204
230
|
const deltaX = Math.abs(e.clientX - startX);
|
|
205
231
|
const deltaY = Math.abs(e.clientY - startY);
|
|
206
232
|
if ((deltaX > 5 || deltaY > 5) && !this.isDragging) {
|
|
207
233
|
this.clearDragTimer();
|
|
208
234
|
}
|
|
209
|
-
|
|
235
|
+
|
|
210
236
|
if (this.isDragging) {
|
|
211
237
|
this.updateDragPosition(e.clientX, e.clientY);
|
|
212
238
|
}
|
|
213
239
|
};
|
|
214
|
-
|
|
240
|
+
|
|
215
241
|
const handleMouseUp = () => {
|
|
216
242
|
this.clearDragTimer();
|
|
217
243
|
this.endDrag();
|
|
218
|
-
document.removeEventListener(
|
|
219
|
-
document.removeEventListener(
|
|
244
|
+
document.removeEventListener("mousemove", handleMouseMove);
|
|
245
|
+
document.removeEventListener("mouseup", handleMouseUp);
|
|
220
246
|
};
|
|
221
|
-
|
|
222
|
-
document.addEventListener(
|
|
223
|
-
document.addEventListener(
|
|
247
|
+
|
|
248
|
+
document.addEventListener("mousemove", handleMouseMove);
|
|
249
|
+
document.addEventListener("mouseup", handleMouseUp);
|
|
224
250
|
},
|
|
225
251
|
|
|
226
252
|
// 长按开始拖拽 - 触摸事件
|
|
227
253
|
handleTouchStart(member, event) {
|
|
228
254
|
event.preventDefault();
|
|
229
255
|
const touch = event.touches[0];
|
|
230
|
-
|
|
256
|
+
|
|
231
257
|
// 使用固定偏移量,让浮动元素在触摸点右下角
|
|
232
258
|
this.dragOffset = { x: 10, y: 10 };
|
|
233
|
-
|
|
259
|
+
|
|
234
260
|
// 设置初始触摸位置
|
|
235
261
|
this.currentMousePosition = { x: touch.clientX, y: touch.clientY };
|
|
236
|
-
|
|
262
|
+
|
|
237
263
|
this.startDragTimer(member);
|
|
238
|
-
|
|
264
|
+
|
|
239
265
|
const startX = touch.clientX;
|
|
240
266
|
const startY = touch.clientY;
|
|
241
|
-
|
|
267
|
+
|
|
242
268
|
const handleTouchMove = (e) => {
|
|
243
269
|
const touch = e.touches[0];
|
|
244
270
|
// 保存触摸实际位置
|
|
245
271
|
this.currentMousePosition = { x: touch.clientX, y: touch.clientY };
|
|
246
|
-
|
|
272
|
+
|
|
247
273
|
// 如果触摸移动超过5px,清除长按定时器
|
|
248
274
|
const deltaX = Math.abs(touch.clientX - startX);
|
|
249
275
|
const deltaY = Math.abs(touch.clientY - startY);
|
|
250
276
|
if ((deltaX > 5 || deltaY > 5) && !this.isDragging) {
|
|
251
277
|
this.clearDragTimer();
|
|
252
278
|
}
|
|
253
|
-
|
|
279
|
+
|
|
254
280
|
if (this.isDragging) {
|
|
255
281
|
this.updateDragPosition(touch.clientX, touch.clientY);
|
|
256
282
|
}
|
|
257
283
|
};
|
|
258
|
-
|
|
284
|
+
|
|
259
285
|
const handleTouchEnd = () => {
|
|
260
286
|
this.clearDragTimer();
|
|
261
287
|
this.endDrag();
|
|
262
|
-
document.removeEventListener(
|
|
263
|
-
document.removeEventListener(
|
|
288
|
+
document.removeEventListener("touchmove", handleTouchMove);
|
|
289
|
+
document.removeEventListener("touchend", handleTouchEnd);
|
|
264
290
|
};
|
|
265
|
-
|
|
266
|
-
document.addEventListener(
|
|
267
|
-
document.addEventListener(
|
|
291
|
+
|
|
292
|
+
document.addEventListener("touchmove", handleTouchMove);
|
|
293
|
+
document.addEventListener("touchend", handleTouchEnd);
|
|
268
294
|
},
|
|
269
295
|
|
|
270
296
|
// 开始长按计时器
|
|
271
297
|
startDragTimer(member) {
|
|
272
298
|
this.clearDragTimer();
|
|
273
|
-
this.dragStartTimer = setTimeout(() => {
|
|
274
|
-
|
|
275
|
-
}, 500);
|
|
299
|
+
// this.dragStartTimer = setTimeout(() => {
|
|
300
|
+
// this.startDrag(member);
|
|
301
|
+
// }, 500);
|
|
302
|
+
this.startDrag(member);
|
|
276
303
|
},
|
|
277
304
|
|
|
278
305
|
// 清除长按计时器
|
|
@@ -287,24 +314,24 @@ export default {
|
|
|
287
314
|
startDrag(member) {
|
|
288
315
|
this.isDragging = true;
|
|
289
316
|
this.currentDragMember = member;
|
|
290
|
-
|
|
317
|
+
|
|
291
318
|
// 设置初始浮动元素位置
|
|
292
319
|
const { x, y } = this.currentMousePosition;
|
|
293
320
|
this.updateDragPosition(x, y);
|
|
294
|
-
|
|
295
|
-
console.log(
|
|
321
|
+
|
|
322
|
+
console.log("开始拖拽:", member.name);
|
|
296
323
|
},
|
|
297
324
|
|
|
298
325
|
// 更新拖拽位置
|
|
299
326
|
updateDragPosition(clientX, clientY) {
|
|
300
327
|
this.dragGhostStyle = {
|
|
301
|
-
position:
|
|
328
|
+
position: "fixed",
|
|
302
329
|
left: `${clientX + this.dragOffset.x}px`,
|
|
303
330
|
top: `${clientY + this.dragOffset.y}px`,
|
|
304
|
-
pointerEvents:
|
|
305
|
-
zIndex: 9999
|
|
331
|
+
pointerEvents: "none",
|
|
332
|
+
zIndex: 9999,
|
|
306
333
|
};
|
|
307
|
-
|
|
334
|
+
|
|
308
335
|
// 检测是否悬停在占位符上
|
|
309
336
|
this.checkHoverTarget(clientX, clientY);
|
|
310
337
|
},
|
|
@@ -313,8 +340,8 @@ export default {
|
|
|
313
340
|
checkHoverTarget(clientX, clientY) {
|
|
314
341
|
// 使用鼠标的实际位置来检测下方元素
|
|
315
342
|
const elementBelow = document.elementFromPoint(clientX, clientY);
|
|
316
|
-
const placeholder = elementBelow?.closest(
|
|
317
|
-
|
|
343
|
+
const placeholder = elementBelow?.closest(".layout-placeholder");
|
|
344
|
+
|
|
318
345
|
if (placeholder) {
|
|
319
346
|
const index = Array.from(placeholder.parentNode.children).indexOf(placeholder) + 1;
|
|
320
347
|
if (index > 0 && index <= this.placeholderCount) {
|
|
@@ -332,7 +359,7 @@ export default {
|
|
|
332
359
|
if (this.isDragging) {
|
|
333
360
|
// 检查是否拖拽到了占位符上
|
|
334
361
|
this.checkDropTarget();
|
|
335
|
-
|
|
362
|
+
|
|
336
363
|
this.isDragging = false;
|
|
337
364
|
this.currentDragMember = null;
|
|
338
365
|
this.dragGhostStyle = {};
|
|
@@ -345,10 +372,10 @@ export default {
|
|
|
345
372
|
checkDropTarget() {
|
|
346
373
|
// 使用保存的鼠标位置来检测拖拽目标
|
|
347
374
|
const { x, y } = this.currentMousePosition;
|
|
348
|
-
|
|
375
|
+
|
|
349
376
|
const elementBelow = document.elementFromPoint(x, y);
|
|
350
|
-
const placeholder = elementBelow?.closest(
|
|
351
|
-
|
|
377
|
+
const placeholder = elementBelow?.closest(".layout-placeholder");
|
|
378
|
+
|
|
352
379
|
if (placeholder) {
|
|
353
380
|
const index = Array.from(placeholder.parentNode.children).indexOf(placeholder) + 1;
|
|
354
381
|
if (index > 0 && index <= this.placeholderCount) {
|
|
@@ -362,17 +389,20 @@ export default {
|
|
|
362
389
|
if (draggedMember) {
|
|
363
390
|
// 1. 清除该成员在其他位置的显示
|
|
364
391
|
this.clearMemberFromOtherPositions(draggedMember.identity);
|
|
365
|
-
|
|
392
|
+
|
|
366
393
|
// 2. 将成员分配到新位置(直接覆盖原有成员)
|
|
367
394
|
const previousMember = this.assignedMembers[index];
|
|
368
395
|
this.assignedMembers[index] = draggedMember;
|
|
369
|
-
|
|
396
|
+
|
|
370
397
|
if (previousMember) {
|
|
371
|
-
console.log(
|
|
398
|
+
console.log(
|
|
399
|
+
"位置替换:",
|
|
400
|
+
`位置 ${index} 的 ${previousMember.name} 被 ${draggedMember.name} 替换`
|
|
401
|
+
);
|
|
372
402
|
} else {
|
|
373
|
-
console.log(
|
|
403
|
+
console.log("拖拽成功:", draggedMember.name, "已分配到位置", index);
|
|
374
404
|
}
|
|
375
|
-
|
|
405
|
+
|
|
376
406
|
// 可以在这里添加成功提示
|
|
377
407
|
// ElMessage.success(`${draggedMember.name} 已分配到位置 ${index}`);
|
|
378
408
|
}
|
|
@@ -388,7 +418,7 @@ export default {
|
|
|
388
418
|
}
|
|
389
419
|
}
|
|
390
420
|
if (clearedPositions.length > 0) {
|
|
391
|
-
console.log(
|
|
421
|
+
console.log("清除重复显示:", `成员在位置 [${clearedPositions.join(", ")}] 的显示已清除`);
|
|
392
422
|
}
|
|
393
423
|
},
|
|
394
424
|
|
|
@@ -397,32 +427,32 @@ export default {
|
|
|
397
427
|
const member = this.assignedMembers[index];
|
|
398
428
|
if (member) {
|
|
399
429
|
delete this.assignedMembers[index];
|
|
400
|
-
console.log(
|
|
430
|
+
console.log("手动清空:", `位置 ${index} 的 ${member.name} 已清空`);
|
|
401
431
|
}
|
|
402
432
|
},
|
|
403
433
|
|
|
404
434
|
handleLayoutChange(layout) {
|
|
405
435
|
const oldLayout = this.activeLayout;
|
|
406
436
|
this.activeLayout = layout;
|
|
407
|
-
|
|
437
|
+
|
|
408
438
|
// 获取新布局的占位符数量
|
|
409
439
|
const newPlaceholderCount = this.getLayoutPlaceholderCount(layout);
|
|
410
|
-
|
|
440
|
+
|
|
411
441
|
// 特殊处理:切换到topSide布局时的成员重排
|
|
412
|
-
if (layout ===
|
|
442
|
+
if (layout === "topSide" && oldLayout !== "topSide") {
|
|
413
443
|
this.handleTopSideLayoutTransition();
|
|
414
444
|
}
|
|
415
|
-
|
|
445
|
+
|
|
416
446
|
// 特殊处理:切换到rightSide布局时的成员重排
|
|
417
|
-
if (layout ===
|
|
447
|
+
if (layout === "rightSide" && oldLayout !== "rightSide") {
|
|
418
448
|
this.handleRightSideLayoutTransition();
|
|
419
449
|
}
|
|
420
|
-
|
|
450
|
+
|
|
421
451
|
// 特殊处理:切换到ring布局时的成员重排
|
|
422
|
-
if (layout ===
|
|
452
|
+
if (layout === "ring" && oldLayout !== "ring") {
|
|
423
453
|
this.handleRingLayoutTransition();
|
|
424
454
|
}
|
|
425
|
-
|
|
455
|
+
|
|
426
456
|
// 清空超出新布局占位符数量的成员
|
|
427
457
|
const clearedMembers = [];
|
|
428
458
|
for (const key in this.assignedMembers) {
|
|
@@ -433,68 +463,76 @@ export default {
|
|
|
433
463
|
delete this.assignedMembers[key];
|
|
434
464
|
}
|
|
435
465
|
}
|
|
436
|
-
|
|
466
|
+
|
|
437
467
|
if (clearedMembers.length > 0) {
|
|
438
|
-
console.log(`布局切换:清空超出范围的成员 [${clearedMembers.join(
|
|
468
|
+
console.log(`布局切换:清空超出范围的成员 [${clearedMembers.join(", ")}]`);
|
|
439
469
|
}
|
|
440
|
-
|
|
441
|
-
console.log(
|
|
470
|
+
|
|
471
|
+
console.log(
|
|
472
|
+
`布局切换:${oldLayout} -> ${layout},保留了 ${
|
|
473
|
+
Object.keys(this.assignedMembers).length
|
|
474
|
+
} 个已分配成员`
|
|
475
|
+
);
|
|
442
476
|
},
|
|
443
477
|
|
|
444
478
|
// 处理切换到topSide布局时的成员重排
|
|
445
479
|
handleTopSideLayoutTransition() {
|
|
446
480
|
// 在topSide布局中,位置1是焦点位置,位置2-4是上排小窗口
|
|
447
481
|
// 如果位置1已有成员,则无需调整;如果位置1没有成员但其他位置有,可以考虑智能重排
|
|
448
|
-
|
|
482
|
+
|
|
449
483
|
if (!this.assignedMembers[1]) {
|
|
450
484
|
// 如果焦点位置(位置1)为空,尝试将最重要的成员移到焦点位置
|
|
451
485
|
for (let i = 2; i <= 4; i++) {
|
|
452
486
|
if (this.assignedMembers[i]) {
|
|
453
|
-
console.log(
|
|
487
|
+
console.log(
|
|
488
|
+
`topSide布局优化:将位置${i}的${this.assignedMembers[i].name}移动到焦点位置`
|
|
489
|
+
);
|
|
454
490
|
this.assignedMembers[1] = this.assignedMembers[i];
|
|
455
491
|
delete this.assignedMembers[i];
|
|
456
492
|
break;
|
|
457
493
|
}
|
|
458
494
|
}
|
|
459
495
|
}
|
|
460
|
-
|
|
461
|
-
console.log(
|
|
496
|
+
|
|
497
|
+
console.log("topSide布局转换完成:位置1为焦点位置,位置2-4为上排小窗口");
|
|
462
498
|
},
|
|
463
499
|
|
|
464
500
|
// 处理切换到rightSide布局时的成员重排
|
|
465
501
|
handleRightSideLayoutTransition() {
|
|
466
502
|
// 在rightSide布局中,位置1是焦点位置,位置2-4是右侧纵向排列
|
|
467
503
|
// 如果位置1已有成员,则无需调整;如果位置1没有成员但其他位置有,可以考虑智能重排
|
|
468
|
-
|
|
504
|
+
|
|
469
505
|
if (!this.assignedMembers[1]) {
|
|
470
506
|
// 如果焦点位置(位置1)为空,尝试将最重要的成员移到焦点位置
|
|
471
507
|
for (let i = 2; i <= 4; i++) {
|
|
472
508
|
if (this.assignedMembers[i]) {
|
|
473
|
-
console.log(
|
|
509
|
+
console.log(
|
|
510
|
+
`rightSide布局优化:将位置${i}的${this.assignedMembers[i].name}移动到焦点位置`
|
|
511
|
+
);
|
|
474
512
|
this.assignedMembers[1] = this.assignedMembers[i];
|
|
475
513
|
delete this.assignedMembers[i];
|
|
476
514
|
break;
|
|
477
515
|
}
|
|
478
516
|
}
|
|
479
517
|
}
|
|
480
|
-
|
|
481
|
-
console.log(
|
|
518
|
+
|
|
519
|
+
console.log("rightSide布局转换完成:位置1为焦点位置,位置2-4为右侧纵向排列");
|
|
482
520
|
},
|
|
483
521
|
|
|
484
522
|
// 处理切换到ring布局时的成员重排
|
|
485
523
|
handleRingLayoutTransition() {
|
|
486
524
|
// 在ring布局中,位置1是焦点位置(左上角大窗口),位置2-8是环状排列
|
|
487
525
|
// 如果位置1已有成员,则无需调整;如果位置1没有成员但其他位置有,按优先级智能重排
|
|
488
|
-
|
|
526
|
+
|
|
489
527
|
if (!this.assignedMembers[1]) {
|
|
490
528
|
// 如果焦点位置(位置1)为空,按优先级顺序选择成员移到焦点位置
|
|
491
529
|
// 优先级:位置2 > 位置3 > 位置4 > 位置5 > 位置6 > 位置7 > 位置8
|
|
492
530
|
// 这样可以确保最重要或最早分配的成员优先显示在焦点位置
|
|
493
|
-
|
|
531
|
+
|
|
494
532
|
const priorityOrder = [2, 3, 4, 5, 6, 7, 8];
|
|
495
533
|
let movedMember = null;
|
|
496
534
|
let fromPosition = null;
|
|
497
|
-
|
|
535
|
+
|
|
498
536
|
for (const position of priorityOrder) {
|
|
499
537
|
if (this.assignedMembers[position]) {
|
|
500
538
|
movedMember = this.assignedMembers[position];
|
|
@@ -502,22 +540,22 @@ export default {
|
|
|
502
540
|
break;
|
|
503
541
|
}
|
|
504
542
|
}
|
|
505
|
-
|
|
543
|
+
|
|
506
544
|
if (movedMember && fromPosition) {
|
|
507
545
|
console.log(`ring布局优化:将位置${fromPosition}的${movedMember.name}移动到焦点位置`);
|
|
508
546
|
this.assignedMembers[1] = movedMember;
|
|
509
547
|
delete this.assignedMembers[fromPosition];
|
|
510
|
-
|
|
548
|
+
|
|
511
549
|
// 启用环状分布优化,让剩余成员排列更加平衡
|
|
512
550
|
this.optimizeRingMemberDistribution();
|
|
513
551
|
} else {
|
|
514
|
-
console.log(
|
|
552
|
+
console.log("ring布局:没有成员需要移动到焦点位置");
|
|
515
553
|
}
|
|
516
554
|
} else {
|
|
517
555
|
console.log(`ring布局:焦点位置已有成员 ${this.assignedMembers[1].name},无需重排`);
|
|
518
556
|
}
|
|
519
|
-
|
|
520
|
-
console.log(
|
|
557
|
+
|
|
558
|
+
console.log("ring布局转换完成:位置1为焦点位置,位置2-8为环状排列");
|
|
521
559
|
},
|
|
522
560
|
|
|
523
561
|
// 优化ring布局的成员分布
|
|
@@ -528,180 +566,193 @@ export default {
|
|
|
528
566
|
if (this.assignedMembers[i]) {
|
|
529
567
|
ringMembers.push({
|
|
530
568
|
member: this.assignedMembers[i],
|
|
531
|
-
originalPosition: i
|
|
569
|
+
originalPosition: i,
|
|
532
570
|
});
|
|
533
571
|
delete this.assignedMembers[i];
|
|
534
572
|
}
|
|
535
573
|
}
|
|
536
|
-
|
|
574
|
+
|
|
537
575
|
if (ringMembers.length === 0) {
|
|
538
|
-
console.log(
|
|
576
|
+
console.log("ring布局优化:没有环状位置的成员需要重新排列");
|
|
539
577
|
return;
|
|
540
578
|
}
|
|
541
|
-
|
|
579
|
+
|
|
542
580
|
// 重新分配到环状位置,优先使用前面的位置以保持视觉平衡
|
|
543
581
|
// 这样可以避免成员分布过于分散,让界面看起来更整洁
|
|
544
582
|
const ringPositions = [2, 3, 4, 5, 6, 7, 8];
|
|
545
583
|
let optimizedCount = 0;
|
|
546
|
-
|
|
584
|
+
|
|
547
585
|
for (let i = 0; i < ringMembers.length && i < ringPositions.length; i++) {
|
|
548
586
|
const newPosition = ringPositions[i];
|
|
549
587
|
const memberInfo = ringMembers[i];
|
|
550
|
-
|
|
588
|
+
|
|
551
589
|
this.assignedMembers[newPosition] = memberInfo.member;
|
|
552
|
-
|
|
590
|
+
|
|
553
591
|
if (memberInfo.originalPosition !== newPosition) {
|
|
554
592
|
optimizedCount++;
|
|
555
593
|
}
|
|
556
594
|
}
|
|
557
|
-
|
|
595
|
+
|
|
558
596
|
if (optimizedCount > 0) {
|
|
559
597
|
console.log(`ring布局优化:重新排列了${optimizedCount}个成员,使环状分布更加平衡`);
|
|
560
598
|
} else {
|
|
561
|
-
console.log(
|
|
599
|
+
console.log("ring布局优化:成员分布已经是最优状态,无需调整");
|
|
562
600
|
}
|
|
563
601
|
},
|
|
564
602
|
|
|
565
603
|
handleReset() {
|
|
566
|
-
this.
|
|
567
|
-
|
|
568
|
-
|
|
604
|
+
this.liveClient.resetVolteLayout(this.meetingNum).then((res) => {
|
|
605
|
+
if (res.code == 200) {
|
|
606
|
+
this.activeLayout = "grid4";
|
|
607
|
+
this.autoFill = true;
|
|
608
|
+
this.assignedMembers = {};
|
|
609
|
+
// 清空保存的状态
|
|
610
|
+
this.clearLayoutState();
|
|
611
|
+
} else {
|
|
612
|
+
this.showMessage.error(res?.msg || "volte融屏重置布局失败");
|
|
613
|
+
}
|
|
614
|
+
});
|
|
569
615
|
},
|
|
570
616
|
|
|
571
617
|
handleApply() {
|
|
572
618
|
// 获取当前布局的占位符数量
|
|
573
619
|
const currentPlaceholderCount = this.getLayoutPlaceholderCount(this.activeLayout);
|
|
574
|
-
|
|
620
|
+
|
|
575
621
|
// 生成布局映射数组
|
|
576
622
|
const layoutArray = this.generateLayoutArray(currentPlaceholderCount);
|
|
577
|
-
|
|
623
|
+
|
|
578
624
|
// 验证布局数组
|
|
579
625
|
const validation = this.validateLayoutArray(layoutArray);
|
|
580
|
-
|
|
626
|
+
|
|
581
627
|
if (!validation.isValid) {
|
|
582
|
-
console.error(
|
|
628
|
+
console.error("布局验证失败:", validation.errors);
|
|
583
629
|
// 可以在这里显示错误提示给用户
|
|
584
630
|
// ElMessage.error('布局配置有误,请检查后重试');
|
|
585
631
|
return null;
|
|
586
632
|
}
|
|
587
|
-
|
|
633
|
+
|
|
588
634
|
if (validation.warnings.length > 0) {
|
|
589
|
-
console.warn(
|
|
635
|
+
console.warn("布局验证警告:", validation.warnings);
|
|
590
636
|
// 可以在这里显示警告提示给用户
|
|
591
637
|
// ElMessage.warning('布局配置存在一些问题,建议检查');
|
|
592
638
|
}
|
|
593
|
-
|
|
639
|
+
|
|
594
640
|
// 这里可以发送应用布局的事件或API调用
|
|
595
|
-
console.log(
|
|
641
|
+
console.log("应用布局成功:", {
|
|
596
642
|
layout: this.activeLayout,
|
|
597
643
|
autoFill: this.autoFill,
|
|
598
644
|
placeholderCount: currentPlaceholderCount,
|
|
599
645
|
layoutArray: layoutArray,
|
|
600
646
|
assignedMembers: this.assignedMembers,
|
|
601
|
-
validation: validation
|
|
647
|
+
validation: validation,
|
|
602
648
|
});
|
|
603
|
-
|
|
649
|
+
|
|
604
650
|
const handleCurrentLayout = () => {
|
|
605
|
-
let layout = ""
|
|
606
|
-
switch(this.activeLayout) {
|
|
651
|
+
let layout = "";
|
|
652
|
+
switch (this.activeLayout) {
|
|
607
653
|
case "grid4":
|
|
608
|
-
layout = "four_grids"
|
|
654
|
+
layout = "four_grids";
|
|
609
655
|
break;
|
|
610
656
|
case "grid9":
|
|
611
|
-
layout = "nine_grids"
|
|
657
|
+
layout = "nine_grids";
|
|
612
658
|
break;
|
|
613
659
|
case "ring":
|
|
614
|
-
layout = "custom1"
|
|
660
|
+
layout = "custom1";
|
|
615
661
|
break;
|
|
616
662
|
case "topSide":
|
|
617
|
-
layout = "custom2"
|
|
663
|
+
layout = "custom2";
|
|
618
664
|
break;
|
|
619
665
|
case "rightSide":
|
|
620
|
-
layout = "custom3"
|
|
666
|
+
layout = "custom3";
|
|
621
667
|
break;
|
|
622
668
|
default:
|
|
623
|
-
layout = "four_grids"
|
|
669
|
+
layout = "four_grids";
|
|
624
670
|
break;
|
|
625
671
|
}
|
|
626
|
-
return layout
|
|
672
|
+
return layout;
|
|
627
673
|
};
|
|
628
|
-
|
|
629
|
-
this.liveClient
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
674
|
+
|
|
675
|
+
this.liveClient
|
|
676
|
+
.modifyVolteLayout({
|
|
677
|
+
roomNum: this.meetingNum,
|
|
678
|
+
layout: handleCurrentLayout(),
|
|
679
|
+
identitys: layoutArray.map((item) => (item && item?.identity ? item.identity : "")),
|
|
680
|
+
autoFill: this.autoFill,
|
|
681
|
+
})
|
|
682
|
+
.then((res) => {
|
|
683
|
+
if (res.code == 200) {
|
|
684
|
+
this.showMessage.success("volte融屏应用布局成功");
|
|
685
|
+
// 成功后保存当前布局状态,便于下次打开还原
|
|
686
|
+
this.saveLayoutState();
|
|
687
|
+
this.closeCustomLayout();
|
|
688
|
+
} else {
|
|
689
|
+
this.showMessage.error(res?.msg || "volte融屏应用布局失败");
|
|
690
|
+
}
|
|
691
|
+
})
|
|
692
|
+
.catch((err) => {
|
|
693
|
+
this.showMessage.error(err?.message || "volte融屏应用布局失败");
|
|
694
|
+
});
|
|
644
695
|
},
|
|
645
696
|
|
|
646
697
|
// 生成布局映射数组
|
|
647
698
|
generateLayoutArray(placeholderCount) {
|
|
648
699
|
const layoutArray = [];
|
|
649
|
-
|
|
700
|
+
|
|
650
701
|
// 遍历每个位置,构建数组
|
|
651
702
|
for (let i = 1; i <= placeholderCount; i++) {
|
|
652
703
|
const member = this.assignedMembers[i];
|
|
653
|
-
|
|
704
|
+
|
|
654
705
|
if (member) {
|
|
655
706
|
// 如果该位置有与会者,添加成员信息
|
|
656
707
|
layoutArray.push(member);
|
|
657
708
|
} else {
|
|
658
709
|
// 如果该位置是空占位符,添加空字符串
|
|
659
|
-
layoutArray.push(
|
|
710
|
+
layoutArray.push("");
|
|
660
711
|
}
|
|
661
712
|
}
|
|
662
|
-
|
|
713
|
+
|
|
663
714
|
// 输出详细信息
|
|
664
715
|
const statistics = this.getLayoutArrayStatistics(layoutArray);
|
|
665
|
-
console.log(
|
|
716
|
+
console.log("生成布局数组:", {
|
|
666
717
|
layout: this.activeLayout,
|
|
667
718
|
placeholderCount: placeholderCount,
|
|
668
719
|
...statistics,
|
|
669
|
-
array: layoutArray
|
|
720
|
+
array: layoutArray,
|
|
670
721
|
});
|
|
671
|
-
|
|
722
|
+
|
|
672
723
|
// 输出格式化的数组显示
|
|
673
|
-
console.log(
|
|
674
|
-
|
|
724
|
+
console.log("布局数组格式化显示:", this.formatLayoutArrayDisplay(layoutArray));
|
|
725
|
+
|
|
675
726
|
return layoutArray;
|
|
676
727
|
},
|
|
677
728
|
|
|
678
729
|
// 获取布局数组统计信息
|
|
679
730
|
getLayoutArrayStatistics(layoutArray) {
|
|
680
|
-
const memberCount = layoutArray.filter(item => item !==
|
|
681
|
-
const emptyCount = layoutArray.filter(item => item ===
|
|
682
|
-
const memberNames = layoutArray
|
|
683
|
-
|
|
684
|
-
.map(member => member.name);
|
|
685
|
-
|
|
731
|
+
const memberCount = layoutArray.filter((item) => item !== "").length;
|
|
732
|
+
const emptyCount = layoutArray.filter((item) => item === "").length;
|
|
733
|
+
const memberNames = layoutArray.filter((item) => item !== "").map((member) => member.name);
|
|
734
|
+
|
|
686
735
|
return {
|
|
687
736
|
arrayLength: layoutArray.length,
|
|
688
737
|
memberCount: memberCount,
|
|
689
738
|
emptyCount: emptyCount,
|
|
690
739
|
memberNames: memberNames,
|
|
691
|
-
utilizationRate: Math.round((memberCount / layoutArray.length) * 100) +
|
|
740
|
+
utilizationRate: Math.round((memberCount / layoutArray.length) * 100) + "%",
|
|
692
741
|
};
|
|
693
742
|
},
|
|
694
743
|
|
|
695
744
|
// 格式化布局数组显示
|
|
696
745
|
formatLayoutArrayDisplay(layoutArray) {
|
|
697
|
-
return layoutArray
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
746
|
+
return layoutArray
|
|
747
|
+
.map((item, index) => {
|
|
748
|
+
const position = index + 1;
|
|
749
|
+
if (item === "") {
|
|
750
|
+
return `位置${position}: [空]`;
|
|
751
|
+
} else {
|
|
752
|
+
return `位置${position}: ${item.name}`;
|
|
753
|
+
}
|
|
754
|
+
})
|
|
755
|
+
.join("\n");
|
|
705
756
|
},
|
|
706
757
|
|
|
707
758
|
// 验证布局数组
|
|
@@ -709,32 +760,32 @@ export default {
|
|
|
709
760
|
const validation = {
|
|
710
761
|
isValid: true,
|
|
711
762
|
errors: [],
|
|
712
|
-
warnings: []
|
|
763
|
+
warnings: [],
|
|
713
764
|
};
|
|
714
|
-
|
|
765
|
+
|
|
715
766
|
// 检查数组长度
|
|
716
767
|
const expectedLength = this.getLayoutPlaceholderCount(this.activeLayout);
|
|
717
768
|
if (layoutArray.length !== expectedLength) {
|
|
718
769
|
validation.isValid = false;
|
|
719
770
|
validation.errors.push(`数组长度不匹配:期望${expectedLength},实际${layoutArray.length}`);
|
|
720
771
|
}
|
|
721
|
-
|
|
772
|
+
|
|
722
773
|
// 检查重复成员
|
|
723
|
-
const members = layoutArray.filter(item => item !==
|
|
724
|
-
const memberIds = members.map(member => member.identity);
|
|
774
|
+
const members = layoutArray.filter((item) => item !== "");
|
|
775
|
+
const memberIds = members.map((member) => member.identity);
|
|
725
776
|
const uniqueIds = [...new Set(memberIds)];
|
|
726
|
-
|
|
777
|
+
|
|
727
778
|
if (memberIds.length !== uniqueIds.length) {
|
|
728
779
|
validation.isValid = false;
|
|
729
|
-
validation.errors.push(
|
|
780
|
+
validation.errors.push("检测到重复的成员分配");
|
|
730
781
|
}
|
|
731
|
-
|
|
782
|
+
|
|
732
783
|
// 检查成员利用率
|
|
733
784
|
const utilizationRate = (members.length / layoutArray.length) * 100;
|
|
734
785
|
if (utilizationRate < 50) {
|
|
735
786
|
validation.warnings.push(`成员利用率较低:${Math.round(utilizationRate)}%`);
|
|
736
787
|
}
|
|
737
|
-
|
|
788
|
+
|
|
738
789
|
return validation;
|
|
739
790
|
},
|
|
740
791
|
|
|
@@ -743,15 +794,129 @@ export default {
|
|
|
743
794
|
val ? item.name.includes(val) : item.name.includes(this.searchVal)
|
|
744
795
|
);
|
|
745
796
|
},
|
|
746
|
-
|
|
797
|
+
|
|
747
798
|
refreshMemberList() {
|
|
748
799
|
this.getMemberList();
|
|
749
800
|
},
|
|
750
|
-
|
|
801
|
+
|
|
751
802
|
closeCustomLayout() {
|
|
752
803
|
this.$emit("closeCustomLayout");
|
|
753
|
-
}
|
|
754
|
-
|
|
804
|
+
},
|
|
805
|
+
// =============================
|
|
806
|
+
// 布局记忆:存/取/清/还原
|
|
807
|
+
// =============================
|
|
808
|
+
getStorageKey() {
|
|
809
|
+
const meeting = this.meetingNum || "default";
|
|
810
|
+
return `${STORAGE_PREFIX}:${meeting}`;
|
|
811
|
+
},
|
|
812
|
+
saveLayoutState() {
|
|
813
|
+
try {
|
|
814
|
+
const layout = this.activeLayout;
|
|
815
|
+
const count = this.getLayoutPlaceholderCount(layout);
|
|
816
|
+
const slots = [];
|
|
817
|
+
for (let i = 1; i <= count; i++) {
|
|
818
|
+
const m = this.assignedMembers[i];
|
|
819
|
+
slots.push(m && m.identity ? m.identity : "");
|
|
820
|
+
}
|
|
821
|
+
const state = {
|
|
822
|
+
version: 1,
|
|
823
|
+
layout,
|
|
824
|
+
autoFill: !!this.autoFill,
|
|
825
|
+
slots,
|
|
826
|
+
updatedAt: Date.now(),
|
|
827
|
+
};
|
|
828
|
+
localStorage.setItem(this.getStorageKey(), JSON.stringify(state));
|
|
829
|
+
} catch (e) {
|
|
830
|
+
console.warn("保存布局状态失败:", e);
|
|
831
|
+
}
|
|
832
|
+
},
|
|
833
|
+
loadLayoutState() {
|
|
834
|
+
try {
|
|
835
|
+
const raw = localStorage.getItem(this.getStorageKey());
|
|
836
|
+
if (!raw) return null;
|
|
837
|
+
const state = JSON.parse(raw);
|
|
838
|
+
if (!state || !Array.isArray(state.slots) || typeof state.layout !== "string") return null;
|
|
839
|
+
return state;
|
|
840
|
+
} catch (e) {
|
|
841
|
+
console.warn("读取布局状态失败:", e);
|
|
842
|
+
return null;
|
|
843
|
+
}
|
|
844
|
+
},
|
|
845
|
+
clearLayoutState() {
|
|
846
|
+
try {
|
|
847
|
+
localStorage.removeItem(this.getStorageKey());
|
|
848
|
+
} catch (e) {
|
|
849
|
+
console.warn("清理布局状态失败:", e);
|
|
850
|
+
}
|
|
851
|
+
},
|
|
852
|
+
restoreLayoutState() {
|
|
853
|
+
const state = this.loadLayoutState();
|
|
854
|
+
if (!state) return;
|
|
855
|
+
|
|
856
|
+
const savedLayout = state.layout;
|
|
857
|
+
const slotCount = this.getLayoutPlaceholderCount(savedLayout);
|
|
858
|
+
this.activeLayout = savedLayout;
|
|
859
|
+
this.autoFill = !!state.autoFill;
|
|
860
|
+
|
|
861
|
+
// 根据当前参会者映射还原
|
|
862
|
+
const identityToMember = new Map();
|
|
863
|
+
(this.participantList || []).forEach((m) => {
|
|
864
|
+
if (m && m.identity) identityToMember.set(m.identity, m);
|
|
865
|
+
});
|
|
866
|
+
const nextAssigned = {};
|
|
867
|
+
for (let i = 0; i < Math.min(state.slots.length, slotCount); i++) {
|
|
868
|
+
const id = state.slots[i];
|
|
869
|
+
if (id && identityToMember.has(id)) {
|
|
870
|
+
nextAssigned[i + 1] = identityToMember.get(id);
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
this.assignedMembers = nextAssigned;
|
|
874
|
+
},
|
|
875
|
+
// 当参会者变化时,移除已离开的与会者,同时更新已保存的状态
|
|
876
|
+
reconcileAssignedMembers() {
|
|
877
|
+
const currentIds = new Set((this.participantList || []).map((m) => m.identity));
|
|
878
|
+
|
|
879
|
+
// 拖拽期间不打断,延迟处理
|
|
880
|
+
if (this.isDragging) {
|
|
881
|
+
this.pendingReconcile = true;
|
|
882
|
+
return;
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
// 1) 清理界面中的离会成员
|
|
886
|
+
let removed = false;
|
|
887
|
+
const nextAssigned = { ...this.assignedMembers };
|
|
888
|
+
Object.keys(nextAssigned).forEach((k) => {
|
|
889
|
+
const mem = nextAssigned[k];
|
|
890
|
+
if (mem && mem.identity && !currentIds.has(mem.identity)) {
|
|
891
|
+
delete nextAssigned[k];
|
|
892
|
+
removed = true;
|
|
893
|
+
}
|
|
894
|
+
});
|
|
895
|
+
if (removed) {
|
|
896
|
+
this.assignedMembers = nextAssigned;
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
// 2) 同步本地保存的 slots
|
|
900
|
+
const saved = this.loadLayoutState();
|
|
901
|
+
if (saved && Array.isArray(saved.slots)) {
|
|
902
|
+
let changed = false;
|
|
903
|
+
for (let i = 0; i < saved.slots.length; i++) {
|
|
904
|
+
const id = saved.slots[i];
|
|
905
|
+
if (id && !currentIds.has(id)) {
|
|
906
|
+
saved.slots[i] = "";
|
|
907
|
+
changed = true;
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
if (changed) {
|
|
911
|
+
try {
|
|
912
|
+
localStorage.setItem(this.getStorageKey(), JSON.stringify(saved));
|
|
913
|
+
} catch (e) {
|
|
914
|
+
console.warn("更新保存的布局状态失败:", e);
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
},
|
|
919
|
+
},
|
|
755
920
|
};
|
|
756
921
|
</script>
|
|
757
922
|
|
|
@@ -763,7 +928,7 @@ export default {
|
|
|
763
928
|
position: absolute;
|
|
764
929
|
right: 0;
|
|
765
930
|
top: 0;
|
|
766
|
-
z-index:
|
|
931
|
+
z-index: 3000;
|
|
767
932
|
overflow: hidden;
|
|
768
933
|
color: var(--theme-font-color);
|
|
769
934
|
background: var(--dialog-bg);
|
|
@@ -911,7 +1076,7 @@ export default {
|
|
|
911
1076
|
align-items: center;
|
|
912
1077
|
justify-content: center;
|
|
913
1078
|
padding: 16px;
|
|
914
|
-
|
|
1079
|
+
|
|
915
1080
|
.layout-container {
|
|
916
1081
|
width: 100%;
|
|
917
1082
|
max-width: 100%;
|
|
@@ -919,44 +1084,44 @@ export default {
|
|
|
919
1084
|
display: grid;
|
|
920
1085
|
gap: 8px;
|
|
921
1086
|
}
|
|
922
|
-
|
|
1087
|
+
|
|
923
1088
|
// 4宫格布局 - 2x2网格
|
|
924
1089
|
&.layout-grid4 .layout-container {
|
|
925
1090
|
grid-template-columns: 1fr 1fr;
|
|
926
1091
|
grid-template-rows: 1fr 1fr;
|
|
927
1092
|
}
|
|
928
|
-
|
|
1093
|
+
|
|
929
1094
|
// 9宫格布局 - 3x3网格
|
|
930
1095
|
&.layout-grid9 .layout-container {
|
|
931
1096
|
grid-template-columns: repeat(3, 1fr);
|
|
932
1097
|
grid-template-rows: repeat(3, 1fr);
|
|
933
1098
|
}
|
|
934
|
-
|
|
1099
|
+
|
|
935
1100
|
// 环状布局 - 左上角大窗口 + 右侧纵向 + 下侧横向 + 右下角
|
|
936
1101
|
&.layout-ring .layout-container {
|
|
937
1102
|
grid-template-columns: repeat(4, 1fr);
|
|
938
1103
|
grid-template-rows: repeat(4, 1fr);
|
|
939
|
-
grid-template-areas:
|
|
1104
|
+
grid-template-areas:
|
|
940
1105
|
"main main main slot1"
|
|
941
1106
|
"main main main slot2"
|
|
942
1107
|
"main main main slot3"
|
|
943
1108
|
"slot4 slot5 slot6 slot7";
|
|
944
1109
|
}
|
|
945
|
-
|
|
1110
|
+
|
|
946
1111
|
// 下焦点布局 - 上排3个小窗口,下方1个大窗口
|
|
947
1112
|
&.layout-topSide .layout-container {
|
|
948
1113
|
grid-template-columns: repeat(3, 1fr);
|
|
949
1114
|
grid-template-rows: 1fr 2fr;
|
|
950
|
-
grid-template-areas:
|
|
1115
|
+
grid-template-areas:
|
|
951
1116
|
"slot2 slot3 slot4"
|
|
952
1117
|
"main main main";
|
|
953
1118
|
}
|
|
954
|
-
|
|
1119
|
+
|
|
955
1120
|
// 左焦点布局 - 左侧1个大窗口,右侧3个纵向排列
|
|
956
1121
|
&.layout-rightSide .layout-container {
|
|
957
1122
|
grid-template-columns: 2fr 1fr;
|
|
958
1123
|
grid-template-rows: repeat(3, 1fr);
|
|
959
|
-
grid-template-areas:
|
|
1124
|
+
grid-template-areas:
|
|
960
1125
|
"main slot2"
|
|
961
1126
|
"main slot3"
|
|
962
1127
|
"main slot4";
|
|
@@ -1014,7 +1179,8 @@ export default {
|
|
|
1014
1179
|
.input-suffix-icon {
|
|
1015
1180
|
width: 20px;
|
|
1016
1181
|
height: 20px;
|
|
1017
|
-
background: url("../../assets/image/common/input_search_icon.png") no-repeat center / 100%
|
|
1182
|
+
background: url("../../assets/image/common/input_search_icon.png") no-repeat center / 100%
|
|
1183
|
+
100%;
|
|
1018
1184
|
}
|
|
1019
1185
|
}
|
|
1020
1186
|
&-list {
|
|
@@ -1027,14 +1193,14 @@ export default {
|
|
|
1027
1193
|
display: flex;
|
|
1028
1194
|
align-items: center;
|
|
1029
1195
|
transition: all 0.2s ease;
|
|
1030
|
-
|
|
1196
|
+
|
|
1031
1197
|
&.is-dragging {
|
|
1032
1198
|
opacity: 0.5;
|
|
1033
1199
|
background: rgba(255, 255, 255, 0.1);
|
|
1034
1200
|
border-radius: 4px;
|
|
1035
1201
|
transform: scale(0.95);
|
|
1036
1202
|
}
|
|
1037
|
-
|
|
1203
|
+
|
|
1038
1204
|
&-drag-icon {
|
|
1039
1205
|
width: 16px;
|
|
1040
1206
|
height: 16px;
|
|
@@ -1042,7 +1208,7 @@ export default {
|
|
|
1042
1208
|
cursor: pointer;
|
|
1043
1209
|
margin-right: 12px;
|
|
1044
1210
|
transition: opacity 0.2s ease;
|
|
1045
|
-
|
|
1211
|
+
|
|
1046
1212
|
&:hover {
|
|
1047
1213
|
opacity: 0.8;
|
|
1048
1214
|
}
|
|
@@ -1089,7 +1255,7 @@ export default {
|
|
|
1089
1255
|
pointer-events: none;
|
|
1090
1256
|
user-select: none;
|
|
1091
1257
|
max-width: 200px;
|
|
1092
|
-
|
|
1258
|
+
|
|
1093
1259
|
&-avatar {
|
|
1094
1260
|
width: 20px;
|
|
1095
1261
|
height: 20px;
|
|
@@ -1098,7 +1264,7 @@ export default {
|
|
|
1098
1264
|
margin-right: 6px;
|
|
1099
1265
|
flex-shrink: 0;
|
|
1100
1266
|
}
|
|
1101
|
-
|
|
1267
|
+
|
|
1102
1268
|
&-name {
|
|
1103
1269
|
color: var(--theme-font-color);
|
|
1104
1270
|
font-size: 11px;
|