3 Коммиты 51965cbf74 ... 6892ce8981

Автор SHA1 Сообщение Дата
  画安 6892ce8981 重构步进锁定时间弹窗结构,支持多屏模式下各面板独立显示 месяцев назад: 2
  画安 dc6b159cd2 修改干线协调和勤务管理的左侧菜单栏 месяцев назад: 2
  画安 163f20ad5c 修改crossingDetailPanel中的步进控制方式下默认显示方案圆饼图 месяцев назад: 2

+ 75 - 68
src/components/ui/CrossingDetailPanel.vue

@@ -108,8 +108,8 @@
                                 </div>
                             </div>
 
-                            <!-- 方案圆饼图:步进模式下不显示 -->
-                            <div class="donut-row" v-if="!showLockTime && currentMethod !== 'step'">
+                            <!-- 方案圆饼图 -->
+                            <div class="donut-row">
                                 <div class="donut-item">
                                     <div class="donut-title">实时方案(执行方案3)</div>
                                     <PlanDonutChart :chartData="realtimeDonutData"
@@ -136,35 +136,38 @@
                 </div>
             </form>
         </div>
-        <!-- 步进锁定时间居中弹窗 -->
+        <!-- 步进锁定时间弹窗 -->
         <transition name="fade">
             <div class="lock-time-overlay" v-if="showLockTime" @click.self="showLockTime = false">
-                <div class="lock-time-modal">
-                    <div class="lock-time-label-wrap glow-header">
-                        <div class="lock-time-label">锁定时间</div>
-                        <div class="lock-time-close" @click="showLockTime = false">✕</div>
+                <div class="lock-time-dialog">
+                    <div class="lock-time-header">
+                        <span class="lock-time-title">锁定时间</span>
+                        <span class="lock-time-close" @click="showLockTime = false">✕</span>
                     </div>
-                    <div class="lock-time-options">
-                        <div class="lock-time-option">
-                            <label>
-                                <input type="radio" v-model="lockTimeType" value="continuous" /> 持续放行
-                            </label>
+                    <div class="lock-time-divider"></div>
+                    <div class="lock-time-body">
+                        <div class="lock-time-options">
+                            <div class="lock-time-option">
+                                <label>
+                                    <input type="radio" v-model="lockTimeType" value="continuous" /> 持续放行
+                                </label>
+                            </div>
+                            <div class="lock-time-option">
+                                <label>
+                                    <input type="radio" v-model="lockTimeType" value="timer" /> 放行
+                                    <DropdownSelect placeholder="锁定时间" v-model="currentLocktime"
+                                        :options="locktimeOptions" size="auto" @click.native.prevent />
+                                    秒解锁
+                                </label>
+                            </div>
                         </div>
-                        <div class="lock-time-option">
-                            <label>
-                                <input type="radio" v-model="lockTimeType" value="timer" /> 放行
-                                <DropdownSelect placeholder="锁定时间" v-model="currentLocktime"
-                                    :options="locktimeOptions" size="auto" @click.native.prevent />
-                                秒解锁
-                            </label>
+                        <div class="lock-time-actions">
+                            <button type="button" class="btn btn-cancel"
+                                @click="onLockTimeCancel">取消</button>
+                            <button type="button" class="btn btn-confirm"
+                                @click="onLockTimeConfirm">确认</button>
                         </div>
                     </div>
-                    <div class="lock-time-actions">
-                        <button type="button" class="btn btn-cancel"
-                            @click="onLockTimeCancel">取消</button>
-                        <button type="button" class="btn btn-confirm"
-                            @click="onLockTimeConfirm">确认</button>
-                    </div>
                 </div>
             </div>
         </transition>
@@ -548,6 +551,7 @@ export default {
 <style scoped>
 .crossing-detail-panel {
     --s: 1;
+    position: relative;
     display: flex;
     flex-direction: row;
     gap: clamp(4px, calc(var(--s) * 12px), 12px);
@@ -747,30 +751,7 @@ export default {
         inset 20px 0px 30px -10px rgba(88, 146, 255, 0.15);
 }
 
-.lock-time-label-wrap {
-    display: flex;
-    align-items: center;
-    justify-content: space-between;
-    padding: clamp(4px, calc(var(--s) * 8px), 10px);
-    border-radius: 8px 8px 0 0;
-    color: #ffffff;
-}
-
-.lock-time-label {
-    font-size: clamp(10px, calc(var(--s) * 16px), 16px);
-    color: #ffffff;
-}
-
-.lock-time-options {
-    display: flex;
-    flex-direction: column;
-    row-gap: clamp(4px, calc(var(--s) * 10px), 10px);
-    font-size: clamp(9px, calc(var(--s) * 14px), 14px);
-    padding: clamp(4px, calc(var(--s) * 10px), 10px);
-    color: #ffffff;
-}
-
-/* 步进锁定时间居中弹窗遮罩 */
+/* 步进锁定时间弹窗 - 遮罩 */
 .lock-time-overlay {
     position: absolute;
     inset: 0;
@@ -782,7 +763,8 @@ export default {
     border-radius: inherit;
 }
 
-.lock-time-modal {
+/* 步进锁定时间弹窗 */
+.lock-time-dialog {
     background: linear-gradient(135deg, rgba(10, 25, 60, 0.97) 0%, rgba(20, 40, 90, 0.97) 100%);
     border: 1px solid rgba(161, 190, 255, 0.3);
     border-radius: 6px;
@@ -792,6 +774,42 @@ export default {
     box-shadow: 0 8px 32px rgba(0, 0, 0, 0.6);
 }
 
+.lock-time-header {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    padding: clamp(4px, calc(var(--s) * 8px), 10px);
+    border-radius: 6px 6px 0 0;
+    color: #ffffff;
+    background: linear-gradient(180deg,
+            rgba(65, 115, 205, 0.6) 0%,
+            rgba(40, 70, 130, 0.1) 100%);
+    backdrop-filter: blur(10px);
+}
+
+.lock-time-title {
+    font-size: clamp(10px, calc(var(--s) * 16px), 16px);
+    color: #ffffff;
+}
+
+.lock-time-close {
+    cursor: pointer;
+    color: #ffffff;
+}
+
+.lock-time-body {
+    padding: 0;
+}
+
+.lock-time-options {
+    display: flex;
+    flex-direction: column;
+    row-gap: clamp(4px, calc(var(--s) * 10px), 10px);
+    font-size: clamp(9px, calc(var(--s) * 14px), 14px);
+    padding: clamp(4px, calc(var(--s) * 10px), 10px);
+    color: #ffffff;
+}
+
 .lock-time-actions {
     display: flex;
     justify-content: flex-end;
@@ -804,15 +822,14 @@ export default {
     font-size: clamp(9px, calc(var(--s) * 14px), 14px);
 }
 
-.lock-time-close {
-    cursor: pointer;
+/* 过渡动画 */
+.fade-enter-active,
+.fade-leave-active {
+    transition: opacity 0.25s;
 }
-
-.glow-header {
-    background: linear-gradient(180deg,
-            rgba(65, 115, 205, 0.6) 0%,
-            rgba(40, 70, 130, 0.1) 100%);
-    backdrop-filter: blur(10px);
+.fade-enter,
+.fade-leave-to {
+    opacity: 0;
 }
 
 .current-stage {
@@ -1045,16 +1062,6 @@ export default {
 }
 
 /* 弹窗过渡动画 */
-.fade-enter-active,
-.fade-leave-active {
-    transition: opacity 0.3s, transform 0.3s;
-}
-
-.fade-enter,
-.fade-leave-to {
-    opacity: 0;
-    transform: translateY(-10px);
-}
 
 /* lock-time 弹窗补充样式 */
 .lock-time {

+ 29 - 43
src/views/SpecialSituationMonitoring.vue

@@ -27,43 +27,16 @@
 
 
         <template #left>
-            <!-- 左侧Tab菜单栏 -->
-            <div class="left-sidebar-wrap" v-if="currentView !== 'list-mode'">
-                <TechTabs v-model="activeLeftTab" type="underline" @tab-click="handleTabClick">
-                    <TechTabPane label="总览" name="overview" class="menu-scroll-view">
-                        <MenuItem theme="tech" v-for="item in menuData" :key="item.id" :node="item" :level="0"
-                            @node-click="handleMenuClick" @folder-click="handleFolderClick"/>
-                    </TechTabPane>
-                    <TechTabPane label="路口" name="crossing" class="menu-scroll-view">
-                        <MenuItem theme="tech" v-for="item in menuData" :key="item.id" :node="item" :level="0"
-                            @node-click="handleMenuClick" />
-                    </TechTabPane>
-                    <TechTabPane label="干线" name="trunkLine" class="menu-scroll-view">
-                        <MenuItem v-for="item in trunkLineMenuData" :key="item.id" :node="item" :level="0"
-                            @node-click="handleTrunkLineClick">
-                        <template #label="{ node }">
-                            <span v-if="node.children && node.children.length > 0">{{ node.label }}</span>
-                            <span v-else>{{ node.label }} 绿波带</span>
-                        </template>
-                        </MenuItem>
-                    </TechTabPane>
-                    <TechTabPane label="特勤" name="specialDuty" class="menu-scroll-view">
-                        <TaskCardList :listData="tableData" class="special-duty-pane"
-                            @view="({ item }) => handleSpecialTaskView(item)"
-                            @start="({ item }) => handleSpecialTaskStart(item)"
-                            @end="({ item }) => handleSpecialTaskEnd(item)"
-                            @restart="({ item }) => handleSpecialTaskRestart(item)"
-                        />
-                    </TechTabPane>
-                </TechTabs>
-            </div>
-            <div class="list-mode-tabs" v-else>
-                <TechTabs v-model="activeLeftTab" type="underline" @tab-click="onListTabSelect">
-                    <TechTabPane label="总览" name="overview" />
-                    <TechTabPane label="路口" name="crossing" />
-                    <TechTabPane label="干线" name="trunkLine" />
-                    <TechTabPane label="特勤" name="specialDuty" />
-                </TechTabs>
+            <div class="left-sidebar-wrap">
+                <div class="left-sidebar-title">勤务管理</div>
+                <div class="left-sidebar-body">
+                    <TaskCardList :listData="tableData" class="special-duty-pane"
+                        @view="({ item }) => handleSpecialTaskView(item)"
+                        @start="({ item }) => handleSpecialTaskStart(item)"
+                        @end="({ item }) => handleSpecialTaskEnd(item)"
+                        @restart="({ item }) => handleSpecialTaskRestart(item)"
+                    />
+                </div>
             </div>
         </template>
 
@@ -104,10 +77,7 @@
 <script>
 import DashboardLayout from '@/layouts/DashboardLayout.vue';
 import DateTimeWidget from '@/components/ui/DateTimeWidget.vue';
-import TechTabs from '@/components/ui/TechTabs.vue';
-import TechTabPane from '@/components/ui/TechTabPane.vue';
 import TongzhouTrafficMap from '@/components/TongzhouTrafficMap.vue';
-import MenuItem from '@/components/ui/MenuItem.vue';
 import ButtonGroup from '@/components/ui/ButtonGroup.vue';
 import TaskCardList from '@/components/ui/TaskCardList.vue';
 import CrossingListPanel from '@/components/ui/CrossingListPanel.vue';
@@ -121,10 +91,7 @@ export default {
     components: {
         DashboardLayout,
         DateTimeWidget,
-        TechTabs,
-        TechTabPane,
         TongzhouTrafficMap,
-        MenuItem,
         ButtonGroup,
         TaskCardList,
         CrossingListPanel,
@@ -770,6 +737,25 @@ export default {
     flex-shrink: 0;
     max-width: 400px;
 }
+.left-sidebar-wrap {
+    display: flex;
+    flex-direction: column;
+    max-width: 400px;
+}
+.left-sidebar-title {
+    font-size: clamp(14px, 1.04vw, 20px);
+    font-weight: bold;
+    color: #e0e8f0;
+    padding: 12px 0;
+    letter-spacing: 2px;
+}
+.left-sidebar-body {
+    background: rgba(17, 36, 70, 0.9);
+    outline: 2px solid #3760A9;
+    outline-offset: -2px;
+    height: 700px;
+    overflow: auto;
+}
 .duty-name {
     display: inline-block;
     max-width: 8em;

+ 34 - 36
src/views/TrunkCoordination.vue

@@ -27,18 +27,11 @@
 
 
         <template #left>
-            <!-- 左侧Tab菜单栏 -->
-            <div class="left-sidebar-wrap" v-if="currentView !== 'list-mode'">
-                <TechTabs v-model="activeLeftTab" type="underline" @tab-click="handleTabClick">
-                    <TechTabPane label="总览" name="overview" class="menu-scroll-view" :loading="menuData.length === 0">
-                        <MenuItem theme="tech" v-for="item in menuData" :key="item.id" :node="item" :level="0"
-                            @node-click="handleMenuClick" @folder-click="handleFolderClick"/>
-                    </TechTabPane>
-                    <TechTabPane label="路口" name="crossing" class="menu-scroll-view" :loading="menuData.length === 0">
-                        <MenuItem theme="tech" v-for="item in menuData" :key="item.id" :node="item" :level="0"
-                            @node-click="handleMenuClick" />
-                    </TechTabPane>
-                    <TechTabPane label="干线" name="trunkLine" class="menu-scroll-view" :loading="trunkLineMenuData.length === 0">
+            <div class="left-sidebar-wrap">
+                <div class="left-sidebar-title">干线协调</div>
+                <div class="left-sidebar-body">
+                    <div v-if="trunkLineMenuData.length === 0" class="sidebar-loading">加载中...</div>
+                    <template v-else>
                         <MenuItem v-for="item in trunkLineMenuData" :key="item.id" :node="item" :level="0"
                             @node-click="handleTrunkLineClick">
                         <template #label="{ node }">
@@ -46,24 +39,8 @@
                             <span v-else>{{ node.label }} 绿波带</span>
                         </template>
                         </MenuItem>
-                    </TechTabPane>
-                    <TechTabPane label="特勤" name="specialDuty" class="menu-scroll-view">
-                        <TaskCardList :listData="tableData" class="special-duty-pane"
-                            @view="({ item }) => handleSpecialTaskView(item)"
-                            @start="({ item }) => handleSpecialTaskStart(item)"
-                            @end="({ item }) => handleSpecialTaskEnd(item)"
-                            @restart="({ item }) => handleSpecialTaskRestart(item)"
-                        />
-                    </TechTabPane>
-                </TechTabs>
-            </div>
-            <div class="list-mode-tabs" v-else>
-                <TechTabs v-model="activeLeftTab" type="underline" @tab-click="onListTabSelect">
-                    <TechTabPane label="总览" name="overview" />
-                    <TechTabPane label="路口" name="crossing" />
-                    <TechTabPane label="干线" name="trunkLine" />
-                    <TechTabPane label="特勤" name="specialDuty" />
-                </TechTabs>
+                    </template>
+                </div>
             </div>
         </template>
 
@@ -104,12 +81,9 @@
 <script>
 import DashboardLayout from '@/layouts/DashboardLayout.vue';
 import DateTimeWidget from '@/components/ui/DateTimeWidget.vue';
-import TechTabs from '@/components/ui/TechTabs.vue';
-import TechTabPane from '@/components/ui/TechTabPane.vue';
 import TongzhouTrafficMap from '@/components/TongzhouTrafficMap.vue';
 import MenuItem from '@/components/ui/MenuItem.vue';
 import ButtonGroup from '@/components/ui/ButtonGroup.vue';
-import TaskCardList from '@/components/ui/TaskCardList.vue';
 import CrossingListPanel from '@/components/ui/CrossingListPanel.vue';
 import OnlineStatusTabs from '@/components/ui/OnlineStatusTabs.vue';
 import DeviceStatusTabs from '@/components/ui/DeviceStatusTabs.vue';
@@ -121,12 +95,9 @@ export default {
     components: {
         DashboardLayout,
         DateTimeWidget,
-        TechTabs,
-        TechTabPane,
         TongzhouTrafficMap,
         MenuItem,
         ButtonGroup,
-        TaskCardList,
         CrossingListPanel,
         OnlineStatusTabs,
         DeviceStatusTabs,
@@ -717,6 +688,33 @@ export default {
 .mode-switch>div {
     width: 200px;
 }
+.left-sidebar-wrap {
+    display: flex;
+    flex-direction: column;
+    max-width: 400px;
+}
+.left-sidebar-title {
+    font-size: clamp(14px, 1.04vw, 20px);
+    font-weight: bold;
+    color: #e0e8f0;
+    padding: 12px 0;
+    letter-spacing: 2px;
+}
+.left-sidebar-body {
+    background: rgba(17, 36, 70, 0.9);
+    outline: 2px solid #3760A9;
+    outline-offset: -2px;
+    height: 700px;
+    overflow: auto;
+}
+.sidebar-loading {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    height: 120px;
+    color: #758599;
+    font-size: 14px;
+}
 .duty-table {
     margin-top: 10px;
 }