Переглянути джерело

特勤任务表格移入左侧Tab;弹窗/表格操作按钮联动;TechTable 字体动态适配

  1. 特勤表格从右上角移入左侧特勤 Tab content
  - StatusMonitoring.vue — 将 TechTable 从 #right 插槽移到 specialDuty TechTabPane 内,新增 .special-duty-pane 样式清除
  Tab 默认背景边框

  2. 特勤任务操作按钮增强
  - StatusMonitoring.vue — 表格操作列增加"立即执行"(未开始)和"立即结束"(进行中)按钮,新增
  handleSpecialTaskStart/handleSpecialTaskEnd 方法;列宽重新分配
  - TaskMonitorHeader.vue — 弹窗 header
  按钮根据状态动态切换:未开始显示"立即执行"(蓝色实心),进行中显示"立即结束"(半透明蓝底+蓝边框,同
  IntersectionControlCard 立即解锁风格);新增 onStartTask prop
  - StatusMonitoring.vue — openDutyDetailDialog 中 headerProps 新增 onStartTask 回调,弹窗操作同步更新 tableData
  对应行状态

  3. Mock 数据状态统一
  - mock/api.js — apiGetSpecialTaskMonitorData 的 taskInfo.status 改为直接取表格行
  task.status,状态值统一为"未开始/进行中/已完成",移除原来随机的 statusList

  4. CrossingDetailHeader 关闭按钮调整
  - CrossingDetailHeader.vue — 移除自带的 close 按钮及 handleClose 方法
  - StatusMonitoring.vue — CrossingDetail 弹窗改为 showClose: true,使用 SmartDialog 默认关闭按钮
  - CrossingMultiView.vue — 改用独立的 span.cell-close 作为关闭按钮

  5. TechTable 字体动态适配
  - TechTable.vue — 添加 container-type: inline-size,表头和表体 font-size 改为 clamp(14px, 2.5cqw, 16px),padding 改为
  clamp 动态缩放
画安 1 місяць тому
батько
коміт
add1156fb3

+ 0 - 22
src/components/ui/CrossingDetailHeader.vue

@@ -11,7 +11,6 @@
     <span>运行时段:{{ currentRoute.runTime || '07:00-09:00' }}</span>
     <span class="sep">/</span>
     <span>设备:<span :class="isOnline ? 'online' : 'offline'">{{ intersectionData.status || '离线' }}</span></span>
-    <div class="close-btn" @click="handleClose">✕</div>
   </div>
 </template>
 
@@ -31,15 +30,6 @@ export default {
     schemeName() {
       return this.currentRoute.schemeName || '早高峰方案';
     }
-  },
-  methods: {
-    handleClose() {
-      if (this.onClose) {
-        this.onClose();
-      } else {
-        this.$emit('close');
-      }
-    }
   }
 };
 </script>
@@ -76,16 +66,4 @@ export default {
   color: #f56c6c;
 }
 
-.close-btn {
-  margin-left: auto;
-  cursor: pointer;
-  color: #e0e6f1;
-  opacity: 0.6;
-  font-size: 14px;
-  padding: 0 4px;
-}
-
-.close-btn:hover {
-  opacity: 1;
-}
 </style>

+ 1 - 1
src/components/ui/CrossingMultiView.vue

@@ -33,8 +33,8 @@
                         :currentRoute="slot.headerData ? slot.headerData.currentRoute : { name: slot.data.label || slot.data.name }"
                         :intersectionData="slot.headerData ? slot.headerData.intersectionData : {}"
                         :cycleLength="slot.headerData ? slot.headerData.cycleLength : 0"
-                        :onClose="() => handleRemove(slot.data.id)"
                     />
+                    <span class="cell-close" @click.stop="handleRemove(slot.data.id)">✕</span>
                 </div>
                 <div class="cell-body">
                     <CrossingDetailPanel

+ 34 - 1
src/components/ui/TaskMonitorHeader.vue

@@ -7,7 +7,8 @@
             <span class="info-text">{{ taskData.manager }}</span>
             <span class="level-tag">{{ taskData.level }}</span>
             <span class="info-text">{{ taskData.status }}</span>
-            <button class="btn btn-view" @click="handleEnd">立即结束</button>
+            <button v-if="taskData.status === '未开始'" class="btn btn-start" @click="handleStart">立即执行</button>
+            <button v-if="taskData.status === '进行中'" class="btn btn-end" @click="handleEnd">立即结束</button>
         </div>
     </div>
 </template>
@@ -17,9 +18,13 @@ export default {
     name: 'TaskMonitorHeader',
     props: {
         taskData: { type: Object, required: true },
+        onStartTask: { type: Function },
         onEndTask: { type: Function }
     },
     methods: {
+        handleStart() {
+            if (this.onStartTask) this.onStartTask();
+        },
         handleEnd() {
             if (this.onEndTask) this.onEndTask();
         }
@@ -34,6 +39,7 @@ export default {
     align-items: center;
     width: 100%;
     padding-right: 20px;
+    padding: 10px 0;
 }
 
 .left-info {
@@ -69,4 +75,31 @@ export default {
     border-radius: 4px;
     font-size: 12px;
 }
+
+.btn {
+    padding: 5px 8px;
+    border: none;
+    border-radius: 4px;
+    color: #fff;
+    cursor: pointer;
+    font-size: 14px;
+    font-weight: bold;
+    transition: opacity 0.3s;
+    background: #1E6AFF;
+}
+
+.btn:hover {
+    opacity: 0.8;
+}
+
+.btn-end {
+    background: rgba(40, 90, 180, 0.6);
+    border: 1px solid #448aff;
+    font-weight: normal;
+}
+
+.btn-end:hover {
+    background: rgba(40, 90, 180, 0.9);
+    box-shadow: 0 0 8px rgba(68, 138, 255, 0.5);
+}
 </style>

+ 5 - 4
src/components/ui/TechTable.vue

@@ -74,6 +74,7 @@ export default {
     overflow-y: auto;
     scrollbar-width: none; /* 兼容 Firefox 隐藏滚动条 */
     -ms-overflow-style: none; /* 兼容 IE/Edge 隐藏滚动条 */
+    container-type: inline-size;
 }
 
 .tech-table-wrapper::-webkit-scrollbar {
@@ -98,9 +99,9 @@ export default {
     top: 0;
     z-index: 10;
     color: #6CFFD2;
-    font-size: 14px;
+    font-size: clamp(14px, 2.5cqw, 16px);
     font-weight: 600;
-    padding: 10px 10px;
+    padding: clamp(4px, 1.5cqw, 10px) clamp(4px, 1.5cqw, 10px);
     box-shadow: inset 0 -1px 0 rgba(255, 255, 255, 0.05);
     background-color: #112446;
 }
@@ -108,8 +109,8 @@ export default {
 /* ================= 表体样式 ================= */
 .tech-table tbody td {
     color: #ffffff;
-    font-size: 14px;
-    padding: 10px 10px;
+    font-size: clamp(14px, 2.5cqw, 16px);
+    padding: clamp(10px, 1.5cqw, 15px) clamp(10px, 1.5cqw, 15px);
     /* 文字超出省略号 */
     white-space: nowrap;
     overflow: hidden;

+ 8 - 9
src/mock/api.js

@@ -683,21 +683,20 @@ export async function apiGetSpecialTaskMonitorData(id) {
   const taskIdx = Math.abs(numId - 1) % (DB.securityTasks.length || 1)
   const task = DB.securityTasks.find(t => t.id === id || t.id === numId) || DB.securityTasks[taskIdx] || {}
   const timeSlots = ['07:30-09:30', '09:00-11:00', '12:00-14:00', '14:00-16:00', '17:00-19:00', '19:00-21:00']
-  const statusList = [
-    { status: '进行中', color: '#ff4d4f' },
-    { status: '待执行', color: '#ffaa00' },
-    { status: '进行中', color: '#ff4d4f' },
-    { status: '进行中', color: '#00e5ff' },
-  ]
-  const statusItem = statusList[seed % statusList.length]
+  const statusColorMap = {
+    '未开始': '#ffaa00',
+    '进行中': '#ff4d4f',
+    '已完成': '#8dc453',
+  }
+  const taskStatus = task.status || '未开始'
 
   const taskInfo = {
     name: task.name || '特勤路线',
     time: timeSlots[seed % timeSlots.length],
     manager: task.executor || DB.securityTasks[seed % DB.securityTasks.length]?.executor || '王建国',
     level: task.level || (seed % 3 === 0 ? '二级' : '一级'),
-    status: statusItem.status,
-    statusColor: statusItem.color,
+    status: taskStatus,
+    statusColor: statusColorMap[taskStatus] || '#ffaa00',
   }
 
   // 根据任务 id 选取不同的关键路口(每个任务关联不同的4个路口)

+ 89 - 38
src/views/StatusMonitoring.vue

@@ -44,7 +44,26 @@
                         </template>
                         </MenuItem>
                     </TechTabPane>
-                    <TechTabPane label="特勤" name="specialDuty">
+                    <TechTabPane label="特勤" name="specialDuty" class="menu-scroll-view special-duty-pane">
+                        <TechTable ref="dutyTable" :columns="tableColumns" :data="tableData" class="duty-table">
+                            <template #level="{ row }">
+                                <span :title="row.level" :style="{ color: row.level === '二级' ? '#FFDF0C' : '#F00' }">
+                                    {{ row.level }}
+                                </span>
+                            </template>
+                            <template #status="{ row }">
+                                <span :title="row.status" :style="{ color: row.status === '进行中' ? '#FFDF0C' : row.status === '未开始' ? '#fff' : '#8dc453' }">
+                                    {{ row.status }}
+                                </span>
+                            </template>
+                            <template #action="{ row }">
+                                <div class="btn-group">
+                                    <span class="action-btn" @click="handleSpecialTaskView(row)">查看</span>
+                                    <span v-if="row.status === '未开始'" class="action-btn action-start" @click="handleSpecialTaskStart(row)">立即执行</span>
+                                    <span v-if="row.status === '进行中'" class="action-btn action-end" @click="handleSpecialTaskEnd(row)">立即结束</span>
+                                </div>
+                            </template>
+                        </TechTable>
                     </TechTabPane>
                 </TechTabs>
             </div>
@@ -55,28 +74,6 @@
             <div class="mode-switch" v-if="activeLeftTab === 'crossing'">
                 <ButtonGroup v-model="currentView" :options="viewOptions" @select="onViewSelect" />
             </div>
-            <!-- 特勤右上角表格 -->
-            <TechTable ref="dutyTable" :columns="tableColumns" :data="tableData" class="duty-table" v-if="activeLeftTab === 'specialDuty'">
-
-                <template #level="{ row }">
-                    <span :title="row.level" :style="{ color: row.level === '二级' ? '#FFDF0C' : '#F00' }">
-                        {{ row.level }}
-                    </span>
-                </template>
-
-                <template #status="{ row }">
-                    <span :title="row.status" :style="{ color: row.status === '进行中' ? '#FFDF0C' : '#F00' }">
-                        {{ row.status }}
-                    </span>
-                </template>
-
-                <template #action="{ row }">
-                    <span class="action-btn" @click="handleSpecialTaskView(row)">
-                        查看
-                    </span>
-                </template>
-
-            </TechTable>
         </template>
 
         <template #center>
@@ -149,14 +146,14 @@ export default {
                 { label: '列表模式', value: 'list-mode' },
                 { label: '地图模式', value: 'map-mode' },
             ],
-            // 1. 表头
+            // 特勤表头
             tableColumns: [
-                { label: '序号', key: 'id', width: '14%' },
-                { label: '名称', key: 'name', width: '20%' },
-                { label: '执行人', key: 'executor', width: '18%' },
-                { label: '等级', key: 'level', width: '14%' },
-                { label: '状态', key: 'status', width: '20%' },
-                { label: '操作', key: 'action', width: '14%' }
+                { label: '序号', key: 'id', width: '10%' },
+                { label: '名称', key: 'name', width: '30%' },
+                { label: '执行人', key: 'executor', width: '15%' },
+                { label: '等级', key: 'level', width: '12%' },
+                { label: '状态', key: 'status', width: '13%' },
+                { label: '操作', key: 'action', width: '20%' }
             ],
 
             tableData: [],
@@ -385,7 +382,7 @@ export default {
                 width: 1315,
                 height: 682,
                 center: false,
-                showClose: false,
+                showClose: true,
                 position: { x: 500, y: 170 },
                 noPadding: false,
                 enableDblclickExpand: false,
@@ -395,9 +392,6 @@ export default {
                     currentRoute: detailData?.currentRoute || {},
                     intersectionData: detailData?.intersectionData || {},
                     cycleLength: detailData?.cycleLength || 0,
-                    onClose: () => {
-                        this.$refs.layout.handleDialogClose(dialogId);
-                    }
                 }
             });
         },
@@ -487,10 +481,17 @@ export default {
                 headerComponent: 'TaskMonitorHeader',
                 headerProps: {
                     taskData: panelData.taskInfo,
+                    onStartTask: () => {
+                        console.log('点击了立即执行');
+                        panelData.taskInfo.status = '进行中';
+                        const tableRow = this.tableData.find(r => r.id === nodeData.id);
+                        if (tableRow) tableRow.status = '进行中';
+                    },
                     onEndTask: () => {
-                        console.log('点击了结束任务');
-                        // this.$refs.layout.handleDialogClose(id);
-                        panelData.taskInfo.status = '已结束';
+                        console.log('点击了立即结束');
+                        panelData.taskInfo.status = '已完成';
+                        const tableRow = this.tableData.find(r => r.id === nodeData.id);
+                        if (tableRow) tableRow.status = '已完成';
                     }
                 }
             });
@@ -498,7 +499,14 @@ export default {
         handleSpecialTaskView(row) {
             console.log('查看特勤线路,当前数据:', row);
             this.openDutyDetailDialog(row);
-        
+        },
+        handleSpecialTaskStart(row) {
+            console.log('立即执行特勤任务:', row);
+            row.status = '进行中';
+        },
+        handleSpecialTaskEnd(row) {
+            console.log('立即结束特勤任务:', row);
+            row.status = '已完成';
         },
 
 
@@ -518,6 +526,20 @@ export default {
 .duty-table {
     margin-top: 10px;
 }
+.action-btn {
+    cursor: pointer;
+    color: #4da8ff;
+    margin-right: 10px;
+}
+.action-btn:hover {
+    text-decoration: underline;
+}
+.action-start {
+    color: #67c23a;
+}
+.action-end {
+    color: #f56c6c;
+}
 .top-charts-bar {
     display: flex;
     justify-content: center;
@@ -542,4 +564,33 @@ export default {
     flex-direction: column;
     overflow: hidden;
 }
+/* 针对特勤 Tab 单独剥离背景和边框 */
+::v-deep .special-duty-pane {
+    background: transparent !important;
+    background-color: transparent !important;
+    background-image: none !important;
+    border: none !important;
+    box-shadow: none !important;
+    outline: none !important;
+}
+
+/* 如果你的科技UI边框是利用伪元素(::before / ::after)绘制的,再加上这两句抹除 */
+::v-deep .special-duty-pane::before,
+::v-deep .special-duty-pane::after {
+    display: none !important;
+}
+
+::v-deep .special-duty-pane .tech-table tbody td {
+    color: #ffffff;
+    font-size: clamp(12px, 2.5cqw, 14px);
+    padding: clamp(8px, 1.5cqw, 10px) clamp(8px, 1.5cqw, 10px);
+    white-space: normal !important; 
+    overflow: hidden;
+    text-overflow: ellipsis;
+}
+::v-deep .special-duty-pane .tech-table .btn-group {
+    display: flex;
+    gap: 10px;
+    flex-direction: column;
+}
 </style>