Просмотр исходного кода

CrossingMultiView 多视图布局优化:1个路口全屏显示,2个以上按 maxSlots 固定网格布局,空位显示占位格

画安 1 неделя назад
Родитель
Сommit
4f88cd2dff
1 измененных файлов с 53 добавлено и 24 удалено
  1. 53 24
      src/components/ui/CrossingMultiView.vue

+ 53 - 24
src/components/ui/CrossingMultiView.vue

@@ -21,28 +21,34 @@
         >
             <div
                 v-for="(slot, index) in visibleSlots"
-                :key="'panel-' + slot.data.id"
+                :key="slot.type === 'empty' ? slot.data.id : 'panel-' + slot.data.id"
                 class="grid-cell"
-                @dblclick="handleDblClick(slot)"
+                :class="{ 'empty-cell': slot.type === 'empty' }"
+                @dblclick="slot.type === 'panel' && handleDblClick(slot)"
             >
-                <div class="cell-header">
-                    <div class="drag-handle" title="拖拽换位" v-if="!expandedId">
-                        <span class="drag-icon">&#x2807;</span>
+                <template v-if="slot.type === 'panel'">
+                    <div class="cell-header">
+                        <div class="drag-handle" title="拖拽换位" v-if="!expandedId">
+                            <span class="drag-icon">&#x2807;</span>
+                        </div>
+                        <CrossingDetailHeader
+                            :currentRoute="slot.headerData ? { ...slot.headerData.currentRoute, name: slot.data.label || slot.headerData.currentRoute.name } : { name: slot.data.label || slot.data.name }"
+                            :intersectionData="slot.headerData ? slot.headerData.intersectionData : {}"
+                            :cycleLength="slot.headerData ? slot.headerData.cycleLength : 0"
+                        />
+                        <span class="cell-close" @click.stop="handleRemove(slot.data.id)">✕</span>
                     </div>
-                    <CrossingDetailHeader
-                        :currentRoute="slot.headerData ? { ...slot.headerData.currentRoute, name: slot.data.label || slot.headerData.currentRoute.name } : { name: slot.data.label || slot.data.name }"
-                        :intersectionData="slot.headerData ? slot.headerData.intersectionData : {}"
-                        :cycleLength="slot.headerData ? slot.headerData.cycleLength : 0"
-                    />
-                    <span class="cell-close" @click.stop="handleRemove(slot.data.id)">✕</span>
-                </div>
-                <div class="cell-body">
-                    <CrossingDetailPanel
-                        :id="slot.data.id"
-                        v-bind="slot.data"
-                        :preloadedData="slot.headerData"
-                    />
-                </div>
+                    <div class="cell-body">
+                        <CrossingDetailPanel
+                            :id="slot.data.id"
+                            v-bind="slot.data"
+                            :preloadedData="slot.headerData"
+                        />
+                    </div>
+                </template>
+                <template v-else>
+                    <div class="empty-placeholder">暂无路口</div>
+                </template>
             </div>
         </draggable>
     </div>
@@ -99,9 +105,15 @@ export default {
                 );
                 return found ? [found] : this.localSlots;
             }
-            return this.localSlots;
+            if (this.panelCount <= 1) return this.localSlots;
+            // 2个以上时,补空占位到 maxSlots
+            const slots = [...this.localSlots];
+            while (slots.length < this.maxSlots) {
+                slots.push({ type: 'empty', data: { id: '__empty_' + slots.length } });
+            }
+            return slots;
         },
-        // 根据实际选中数量决定网格:1→1x1, 2→2x1, 3~4→2x2
+        // 1个→全屏, 2个以上→按 maxSlots 布局
         gridCols() {
             if (this.expandedId) return 1;
             if (this.panelCount <= 1) return 1;
@@ -109,8 +121,8 @@ export default {
         },
         gridRows() {
             if (this.expandedId) return 1;
-            if (this.panelCount <= 2) return 1;
-            return 2;
+            if (this.panelCount <= 1) return 1;
+            return Math.ceil(this.maxSlots / 2);
         },
         gridStyle() {
             return {
@@ -120,7 +132,8 @@ export default {
         },
         gridClass() {
             if (this.expandedId) return 'slots-1';
-            return 'slots-' + this.panelCount;
+            if (this.panelCount <= 1) return 'slots-1';
+            return 'slots-' + this.maxSlots;
         }
     },
     watch: {
@@ -336,4 +349,20 @@ export default {
     border-radius: 4px;
 }
 
+/* ===== 空占位格 ===== */
+.empty-cell {
+    border: 1px dashed rgba(255, 255, 255, 0.15);
+    border-radius: 4px;
+}
+
+.empty-placeholder {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    height: 100%;
+    color: rgba(255, 255, 255, 0.2);
+    font-size: 14px;
+    user-select: none;
+}
+
 </style>