瀏覽代碼

Merge branch 'master' of http://43.138.82.93:10601/bxf/ducha-qianduan

gao 1 天之前
父節點
當前提交
fb79485d91

+ 4 - 0
package-lock.json

@@ -4914,7 +4914,11 @@
     },
     "dayjs": {
       "version": "1.11.18",
+<<<<<<< HEAD
       "resolved": "https://mirrors.huaweicloud.com/repository/npm/dayjs/-/dayjs-1.11.18.tgz",
+=======
+      "resolved": "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.18.tgz",
+>>>>>>> be382cee57eb0bde73b1469e1fef0452a2b6c322
       "integrity": "sha512-zFBQ7WFRvVRhKcWoUh+ZA1g2HVgUbsZm9sbddh8EC5iv93sui8DVVz1Npvz+r6meo9VKfa8NyLWBsQK1VvIKPA=="
     },
     "de-indent": {

+ 2 - 1
package.json

@@ -43,7 +43,7 @@
     "core-js": "3.8.1",
     "dayjs": "^1.11.18",
     "docx-preview": "^0.1.4",
-    "echarts": "^4.9.0",
+    "echarts": "^5.6.0",
     "element-ui": "2.15.6",
     "file-saver": "^2.0.5",
     "flv.js": "^1.6.2",
@@ -66,6 +66,7 @@
     "vue-count-to": "1.0.13",
     "vue-cropper": "0.5.5",
     "vue-doc-preview": "^0.3.2",
+    "vue-echarts": "^8.0.1",
     "vue-horizontal-calendar": "^1.0.0",
     "vue-meta": "^2.4.0",
     "vue-pdf": "^4.3.0",

+ 41 - 33
src/api/grassrootsregistration/bdglcookbook.js

@@ -2,60 +2,68 @@ import request from '@/utils/request'
 
 // 查询一周食谱列表
 export function listBdglcookbook(query) {
-  return request({
-    url: '/grassrootsregistration/bdglcookbook/list',
-    method: 'get',
-    params: query
-  })
+    return request({
+        url: '/grassrootsregistration/bdglcookbook/list',
+        method: 'get',
+        params: query
+    })
 }
 
 // 查询一周食谱详细
 export function getBdglcookbook(id) {
-  return request({
-    url: '/grassrootsregistration/bdglcookbook/' + id,
-    method: 'get'
-  })
+    return request({
+        url: '/grassrootsregistration/bdglcookbook/' + id,
+        method: 'get'
+    })
 }
 
 // 新增一周食谱
 export function addBdglcookbook(data) {
-  return request({
-    url: '/grassrootsregistration/bdglcookbook',
-    method: 'post',
-    data: data
-  })
+    return request({
+        url: '/grassrootsregistration/bdglcookbook',
+        method: 'post',
+        data: data
+    })
 }
 
 // 修改一周食谱
 export function updateBdglcookbook(data) {
-  return request({
-    url: '/grassrootsregistration/bdglcookbook',
-    method: 'put',
-    data: data
-  })
+    return request({
+        url: '/grassrootsregistration/bdglcookbook',
+        method: 'put',
+        data: data
+    })
 }
 
 // 删除一周食谱
 export function delBdglcookbook(id) {
-  return request({
-    url: '/grassrootsregistration/bdglcookbook/' + id,
-    method: 'delete'
-  })
+    return request({
+        url: '/grassrootsregistration/bdglcookbook/' + id,
+        method: 'delete'
+    })
 }
 
 // 导出一周食谱
 export function exportBdglcookbook(query) {
-  return request({
-    url: '/grassrootsregistration/bdglcookbook/export',
-    method: 'get',
-    params: query
-  })
+    return request({
+        url: '/grassrootsregistration/bdglcookbook/export',
+        method: 'get',
+        params: query
+    })
+}
+//导出一周食谱Excel
+export function exportBdglcookbookExcel(id) {
+    return request({
+        url: '/grassrootsregistration/bdglcookbook/exportExcel/' + id,
+        method: 'get',
+        responseType: 'blob'
+    })
 }
 //  获取所有食品
 export function getThings(query) {
-  return request({
-    url: '/grassrootsregistration/bdglcookbook/getfood',
-    method: 'get',
-    params: query
-  })
+    return request({
+        url: '/grassrootsregistration/bdglcookbook/getfood',
+        method: 'get',
+        params: query
+    })
 }

+ 21 - 11
src/layout/components/Navbar.vue

@@ -57,6 +57,7 @@ import Search from "@/components/HeaderSearch";
 import RuoYiGit from "@/components/supervision/Git";
 import RuoYiDoc from "@/components/supervision/Doc";
 import { getUserProfile } from "@/api/system/user";
+import WebSocketService from "@/utils/WebSocketService";
 
 export default {
   components: {
@@ -110,17 +111,26 @@ export default {
       this.$store.dispatch("app/toggleSideBar");
     },
     async logout() {
-      this.$confirm("确定注销并退出系统吗?", "提示", {
-        confirmButtonText: "确定",
-        cancelButtonText: "取消",
-        type: "warning",
-      })
-        .then(() => {
-          this.$store.dispatch("LogOut").then(() => {
-            location.href = "/index";
-          });
-        })
-        .catch(() => { });
+      try {
+        await this.$confirm("确定注销并退出系统吗?", "提示", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning",
+        });
+
+        // 调用 Vuex 退出登录
+        await this.$store.dispatch("LogOut");
+
+        // 断开 WebSocket(如果存在)
+        if (this.$store.getters.webSocketState === 'open') {
+          WebSocketService.close();
+        }
+
+        // 跳转登录页
+        location.href = "/index";
+      } catch (err) {
+        // 用户取消操作,不处理
+      }
     },
     getUser() {
       getUserProfile().then((response) => {

+ 6 - 5
src/store/getters.js

@@ -11,8 +11,9 @@ const getters = {
   roles: state => state.user.roles,
   permissions: state => state.user.permissions,
   permission_routes: state => state.permission.routes,
-  topbarRouters:state => state.permission.topbarRouters,
-  defaultRoutes:state => state.permission.defaultRoutes,
-  sidebarRouters:state => state.permission.sidebarRouters,
-}
-export default getters
+  topbarRouters: state => state.permission.topbarRouters,
+  defaultRoutes: state => state.permission.defaultRoutes,
+  sidebarRouters: state => state.permission.sidebarRouters,
+  webSocketState: state => state.ws.webSocketState
+};
+export default getters;

+ 14 - 12
src/store/index.js

@@ -1,13 +1,14 @@
-import Vue from 'vue'
-import Vuex from 'vuex'
-import app from './modules/app'
-import user from './modules/user'
-import tagsView from './modules/tagsView'
-import permission from './modules/permission'
-import settings from './modules/settings'
-import getters from './getters'
+import Vue from "vue";
+import Vuex from "vuex";
+import app from "./modules/app";
+import user from "./modules/user";
+import tagsView from "./modules/tagsView";
+import permission from "./modules/permission";
+import settings from "./modules/settings";
+import ws from "./modules/ws";
+import getters from "./getters";
 
-Vue.use(Vuex)
+Vue.use(Vuex);
 
 const store = new Vuex.Store({
   state:{
@@ -18,9 +19,10 @@ const store = new Vuex.Store({
     user,
     tagsView,
     permission,
-    settings
+    settings,
+    ws,
   },
   getters
-})
+});
 
-export default store
+export default store;

+ 35 - 0
src/store/modules/ws.js

@@ -0,0 +1,35 @@
+import WebSocketService from '@/utils/WebSocketService';
+
+export default {
+  namespaced: true,
+  state: {
+    webSocketState: 'closed', // 'open' | 'connecting' | 'error' | 'closed'
+  },
+  mutations: {
+    setWebSocketStatus(state, status) {
+      console.log('WebSocket状态已更新:', status);
+      state.webSocketState = status;
+    },
+  },
+  actions: {
+    connectWebSocket({ commit }, token) {
+      commit('setWebSocketStatus', 'connecting');
+      WebSocketService.initWebSocket(token);
+
+      const handler = (e) => commit('setWebSocketStatus', e.detail);
+
+      // 避免重复绑定
+      window.removeEventListener('ws-status', handler);
+      window.addEventListener('ws-status', handler);
+    },
+    reconnectWebSocket({ state, commit }) {
+      if (state.status === 'open' || state.status === 'connecting') {
+        console.log("WebSocket 已连接或正在连接中,跳过重连");
+        return;
+      }
+
+      commit('setWebSocketStatus', 'connecting');
+      WebSocketService.reconnect();
+    },
+  }
+};

+ 42 - 3
src/utils/WebSocketService.js

@@ -1,3 +1,5 @@
+// WebSocketService.js
+
 class WebSocketService {
   constructor() {
     this.websocket = null;
@@ -9,12 +11,35 @@ class WebSocketService {
   initWebSocket(token) {
     if (!token) {
       console.warn('❌ WebSocket token 为空');
+
+      // 全局提示用户登录过期
+      if (typeof window !== 'undefined' && window.Vue && Vue.prototype.$message) {
+        Vue.prototype.$message.error('登录状态已过期,请重新登录!');
+      } else {
+        alert('登录状态已过期,请重新登录!');
+      }
+
+      // 退出并跳转登录页
+      if (this.store) {
+        this.store.dispatch("LogOut").then(() => {
+          location.href = "/index";
+        });
+      } else {
+        // 如果没有 store 引用,直接跳转
+        location.href = "/index";
+      }
+      return;
+    }
+
+    // 如果已连接或连接中,跳过
+    if (this.websocket &&
+      (this.websocket.readyState === WebSocket.OPEN || this.websocket.readyState === WebSocket.CONNECTING)) {
+      console.log('ℹ️ WebSocket 已连接或连接中,跳过重复连接');
       return;
     }
 
     this.token = token;
     const wsUrl = `${process.env.VUE_APP_WS_URL}/${token}`;
-    console.log("🔌 WebSocket连接中:", process.env.VUE_APP_WS_URL);
     console.log("🔌 WebSocket连接中:", wsUrl);
 
     this.websocket = new WebSocket(wsUrl);
@@ -23,26 +48,39 @@ class WebSocketService {
     this.websocket.onerror = this.websocketonerror.bind(this);
     this.websocket.onmessage = this.setOnmessageMessage.bind(this);
     this.websocket.onclose = this.websocketclose.bind(this);
+
+    // 初始化状态为 connecting
+    window.dispatchEvent(new CustomEvent('ws-status', { detail: 'connecting' }));
   }
 
   websocketonopen() {
     console.log('✅ WebSocket连接成功');
     this.startHeartbeat();
+    window.dispatchEvent(new CustomEvent('ws-status', { detail: 'open' }));
   }
 
   websocketonerror(e) {
-    console.error("❌ WebSocket连接错误:", e);
+    console.error('❌ WebSocket连接错误:', e);
+    window.dispatchEvent(new CustomEvent('ws-status', { detail: 'error' }));
     this.reconnect();
   }
 
   websocketclose(e) {
-    console.warn("⚠️ WebSocket连接关闭:", e);
+    console.warn('⚠️ WebSocket连接关闭:', e);
     this.stopHeartbeat();
+    window.dispatchEvent(new CustomEvent('ws-status', { detail: 'closed' }));
     this.reconnect();
   }
 
   reconnect() {
     if (this.reconnectTimer) return;
+
+    if (this.websocket &&
+      (this.websocket.readyState === WebSocket.OPEN || this.websocket.readyState === WebSocket.CONNECTING)) {
+      console.log('ℹ️ WebSocket 已连接,无需重连');
+      return;
+    }
+
     this.reconnectTimer = setTimeout(() => {
       console.warn('🔁 尝试重新连接 WebSocket...');
       this.initWebSocket(this.token);
@@ -85,6 +123,7 @@ class WebSocketService {
       this.websocket = null;
     }
     console.log("🧹 WebSocket 已销毁");
+    window.dispatchEvent(new CustomEvent('ws-status', { detail: 'closed' }));
   }
 }
 

+ 294 - 0
src/views/doorcarManage/infoManage/eventTypeMap.js

@@ -0,0 +1,294 @@
+// eventTypeMap.js
+
+export const minorTypeMap = {
+  0x1: {
+    0x400: "防区短路报警",
+    0x401: "防区断路报警",
+    0x402: "防区异常报警",
+    0x403: "防区报警恢复",
+    0x404: "设备防拆报警",
+    0x405: "设备防拆恢复",
+    0x406: "读卡器防拆报警",
+    0x407: "读卡器防拆恢复",
+    0x408: "事件输入报警",
+    0x409: "事件输入恢复",
+    0x40a: "胁迫报警",
+    0x40b: "离线事件满90%报警",
+    0x40c: "卡号认证失败超次报警",
+    0x40d: "SD卡存储满报警",
+    0x40e: "联动抓拍事件报警",
+    0x40f: "门控安全模块防拆报警",
+    0x410: "门控安全模块防拆恢复",
+    0x411: "POS开启",
+    0x412: "POS结束",
+    0x413: "人脸图像画质低",
+    0x414: "指纹图像画质低",
+    0x415: "消防输入短路报警",
+    0x416: "消防输入断路报警",
+    0x417: "消防输入恢复",
+    0x418: "消防按钮触发",
+    0x419: "消防按钮恢复",
+    0x41a: "维护按钮触发",
+    0x41b: "维护按钮恢复",
+    0x41c: "紧急按钮触发",
+    0x41d: "紧急按钮恢复",
+    0x41e: "分控器防拆报警",
+    0x41f: "分控器防拆报警恢复",
+    0x422: "通道控制器防拆报警",
+    0x423: "通道控制器防拆报警恢复",
+    0x424: "通道控制器消防输入报警",
+    0x425: "通道控制器消防输入报警恢复",
+    0x442: "合法事件满90%报警",
+    0x95d: "智能锁防劫持报警",
+  },
+  0x2: {
+    // MAJOR_EXCEPTION - 异常
+    0x27: "网络断开",
+    0x28: "网络恢复",
+    0x3a: "RS485连接状态异常",
+    0x3b: "RS485连接状态异常恢复",
+    0x400: "设备上电启动",
+    0x401: "设备掉电关闭",
+    0x402: "看门狗复位",
+    0x403: "蓄电池电压低",
+    0x404: "蓄电池电压恢复正常",
+    0x405: "交流电断电",
+    0x406: "交流电恢复",
+    0x407: "网络恢复",
+    0x408: "FLASH读写异常",
+    0x409: "读卡器掉线",
+    0x40a: "读卡器掉线恢复",
+    0x40b: "指示灯关闭",
+    0x40c: "指示灯恢复",
+    0x40d: "通道控制器掉线",
+    0x40e: "通道控制器恢复",
+    0x40f: "门控安全模块掉线",
+    0x410: "门控安全模块掉线恢复",
+    0x411: "电池电压低(仅人脸设备使用)",
+    0x412: "电池电压恢复正常(仅人脸设备使用)",
+    0x413: "就地控制器网络断开",
+    0x414: "就地控制器网络恢复",
+    0x415: "主控RS485环路节点断开",
+    0x416: "主控RS485环路节点恢复",
+    0x417: "就地控制器掉线",
+    0x418: "就地控制器掉线恢复",
+    0x419: "就地下行RS485环路断开",
+    0x41a: "就地下行RS485环路恢复",
+    0x41b: "分控器在线",
+    0x41c: "分控器离线",
+    0x41d: "身份证阅读器未连接(智能专用)",
+    0x41e: "身份证阅读器连接恢复(智能专用)",
+    0x41f: "指纹模组未连接(智能专用)",
+    0x420: "指纹模组连接恢复(智能专用)",
+    0x421: "摄像头未连接",
+    0x422: "摄像头连接恢复",
+    0x423: "COM口未连接",
+    0x424: "COM口连接恢复",
+    0x425: "设备未授权",
+    0x426: "人证设备在线",
+    0x427: "人证设备离线",
+    0x428: "本地登录锁定",
+    0x429: "本地登录解锁",
+    0x42a: "与反潜回服务器通信断开",
+    0x42b: "与反潜回服务器通信恢复",
+    0x42c: "电机或传感器异常",
+    0x42d: "CAN总线异常",
+    0x42e: "CAN总线恢复",
+    0x42f: "闸机腔体温度超限",
+    0x430: "红外对射异常",
+    0x431: "红外对射恢复",
+    0x432: "灯板通信异常",
+    0x433: "灯板通信恢复",
+    0x434: "红外转接板通信异常",
+    0x435: "红外转接板通信恢复",
+  },
+  0x3: {
+    // MAJOR_OPERATION - 操作
+    0x50: "本地登陆",
+    0x51: "本地注销登陆",
+    0x52: "修改密码",
+    0x53: "本地参数修改",
+    0x5a: "本地升级",
+    0x60: "远程参数修改",
+    0x61: "远程开门",
+    0x62: "远程关闭门",
+    0x70: "远程登录",
+    0x71: "远程注销登陆",
+    0x72: "远程升级",
+    0x73: "远程布防/撤防",
+    0x79: "远程布防",
+    0x7a: "远程撤防",
+    0x7b: "远程重启",
+    0x7e: "远程升级",
+    0x86: "远程导出配置文件",
+    0x87: "远程导入配置文件",
+    0xd6: "远程手动开启报警输出",
+    0xd7: "远程手动关闭报警输出",
+    0x400: "远程开门",
+    0x401: "远程关门(梯控受控)",
+    0x402: "远程常开(梯控自由)",
+    0x403: "远程常关(梯控禁用)",
+    0x404: "远程手动校时",
+    0x405: "NTP自动校时",
+    0x406: "远程清空卡号",
+    0x407: "远程恢复默认参数",
+    0x408: "防区布防",
+    0x409: "防区撤防",
+    0x40a: "本地恢复默认参数",
+    0x40b: "远程抓拍",
+    0x40c: "修改网络中心参数配置",
+    0x40d: "修改GPRS中心参数配置",
+    0x40e: "修改中心组参数配置",
+    0x40f: "解除码输入",
+    0x410: "自动重新编号",
+    0x411: "自动补充编号",
+    0x412: "导入普通配置文件",
+    0x413: "导出普通配置文件",
+    0x414: "导入卡权限参数",
+    0x415: "导出卡权限参数",
+    0x416: "本地U盘升级",
+    0x417: "访客呼梯",
+    0x418: "住户呼梯",
+    0x419: "远程实时布防",
+    0x41a: "远程实时撤防",
+    0x41b: "遥控器未对码操作失败",
+    0x41c: "遥控器关门",
+    0x41d: "遥控器开门",
+    0x41e: "遥控器常开门",
+  },
+  0x5: {
+    // MAJOR_EVENT - 事件
+    0x01: "合法卡认证通过",
+    0x02: "刷卡加密码认证通过",
+    0x03: "刷卡加密码认证失败",
+    0x04: "数卡加密码认证超时",
+    0x05: "刷卡加密码超次",
+    0x06: "未分配权限",
+    0x07: "无效时段",
+    0x08: "卡号过期",
+    0x09: "无此卡号",
+    0x0a: "反潜回认证失败",
+    0x0b: "互锁门未关闭",
+    0x0c: "卡不属于多重认证群组",
+    0x0d: "卡不在多重认证时间段内",
+    0x0e: "多重认证模式超级权限认证失败",
+    0x0f: "多重认证模式远程认证失败",
+    0x10: "多重认证成功",
+    0x11: "首卡开门开始",
+    0x12: "首卡开门结束",
+    0x13: "常开状态开始",
+    0x14: "常开状态结束",
+    0x15: "门锁打开",
+    0x16: "门锁关闭",
+    0x17: "开门按钮打开",
+    0x18: "开门按钮放开",
+    0x19: "正常开门(门磁)",
+    0x1a: "正常关门(门磁)",
+    0x1b: "门异常打开(门磁)",
+    0x1c: "门打开超时(门磁)",
+    0x1d: "报警输出打开",
+    0x1e: "报警输出关闭",
+    0x1f: "常关状态开始",
+    0x20: "常关状态结束",
+    0x21: "多重认证需要远程开门",
+    0x22: "多重认证超级密码认证成功事件",
+    0x23: "多重认证重复认证事件",
+    0x24: "多重认证超时",
+    0x25: "门铃响",
+    0x26: "指纹比对通过",
+    0x27: "指纹比对失败",
+    0x28: "刷卡加指纹认证通过",
+    0x29: "刷卡加指纹认证失败",
+    0x2a: "刷卡加指纹认证超时",
+    0x2b: "刷卡加指纹加密码认证通过",
+    0x2c: "刷卡加指纹加密码认证失败",
+    0x2d: "刷卡加指纹加密码认证超时",
+    0x2e: "指纹加密码认证通过",
+    0x2f: "指纹加密码认证失败",
+    0x30: "指纹加密码认证超时",
+    0x31: "指纹不存在",
+    0x32: "刷卡平台认证",
+    0x33: "呼叫中心事件",
+    0x34: "消防继电器导通触发门常开",
+    0x35: "消防继电器恢复门恢复正常",
+    0x36: "人脸加指纹认证通过",
+    0x37: "人脸加指纹认证失败",
+    0x38: "人脸加指纹认证超时",
+    0x39: "人脸加密码认证通过",
+    0x3a: "人脸加密码认证失败",
+    0x3b: "人脸加密码认证超时",
+    0x3c: "人脸加刷卡认证通过",
+    0x3d: "人脸加刷卡认证失败",
+    0x3e: "人脸加刷卡认证超时",
+    0x3f: "人脸加密码加指纹认证通过",
+    0x40: "人脸加密码加指纹认证失败",
+    0x41: "人脸加密码加指纹认证超时",
+    0x42: "人脸加刷卡加指纹认证通过",
+    0x43: "人脸加刷卡加指纹认证失败",
+    0x44: "人脸加刷卡加指纹认证超时",
+    0x45: "工号加指纹认证通过",
+    0x46: "工号加指纹认证失败",
+    0x47: "工号加指纹认证超时",
+    0x48: "工号加指纹加密码认证通过",
+    0x49: "工号加指纹加密码认证失败",
+    0x4a: "工号加指纹加密码认证超时",
+    0x4b: "人脸认证通过",
+    0x4c: "人脸认证失败",
+    0x4d: "工号加人脸认证通过",
+    0x4e: "工号加人脸认证失败",
+    0x4f: "工号加人脸认证超时",
+    0x50: "人脸抓拍失败",
+    0x51: "首卡授权开始",
+    0x52: "首卡授权结束",
+    0x53: "门锁输入短路报警",
+    0x54: "门锁输入断路报警",
+    0x55: "门锁输入异常报警",
+    0x56: "门磁输入短路报警",
+    0x57: "门磁输入断路报警",
+    0x58: "门磁输入异常报警",
+    0x59: "开门按钮输入短路报警",
+    0x5a: "开门按钮输入断路报警",
+    0x5b: "开门按钮输入异常报警",
+    0x5c: "门锁异常打开",
+    0x5d: "门锁打开超时",
+    0x5e: "首卡未授权开门失败",
+    0x5f: "呼梯继电器断开",
+    0x60: "呼梯继电器闭合",
+    0x61: "自动按键继电器断开",
+    0x62: "自动按键继电器闭合",
+    0x63: "按键梯控继电器断开",
+    0x64: "按键梯控继电器闭合",
+    0x65: "工号加密码认证通过",
+    0x66: "工号加密码认证失败",
+    0x67: "工号加密码认证超时",
+    0x68: "真人检测失败",
+    0x69: "人证比对通过",
+    0x70: "人证比对失败",
+    0x71: "非授权名单事件",
+    0x72: "合法短信",
+    0x73: "非法短信",
+    0x74: "MAC侦测",
+    0x75: "门状态常闭或休眠状态认证失败",
+    0x76: "认证计划休眠模式认证失败",
+    0x77: "卡加密校验失败",
+    0x78: "反潜回服务器应答失败",
+    0x85: "尾随通行",
+    0x86: "反向闯入",
+    0x87: "外力冲撞",
+    0x88: "翻越",
+    0x89: "通行超时",
+    0x8a: "误闯报警",
+    0x8b: "闸机自由通行时未认证通过",
+    0x8c: "摆臂被阻挡",
+    0x8d: "摆臂阻挡消除",
+    0x8e: "设备升级本地人脸建模失败",
+    0x8f: "逗留事件",
+    0x97: "密码不匹配",
+    0x98: "工号不存在",
+    0x99: "组合认证通过",
+    0x9a: "组合认证超时",
+    0x9b: "认证方式不匹配",
+  },
+};
+
+export default minorTypeMap;

文件差異過大導致無法顯示
+ 772 - 285
src/views/doorcarManage/infoManage/index.vue


+ 294 - 0
src/views/doormanManage/infoManage/eventTypeMap.js

@@ -0,0 +1,294 @@
+// eventTypeMap.js
+
+export const minorTypeMap = {
+  0x1: {
+    0x400: "防区短路报警",
+    0x401: "防区断路报警",
+    0x402: "防区异常报警",
+    0x403: "防区报警恢复",
+    0x404: "设备防拆报警",
+    0x405: "设备防拆恢复",
+    0x406: "读卡器防拆报警",
+    0x407: "读卡器防拆恢复",
+    0x408: "事件输入报警",
+    0x409: "事件输入恢复",
+    0x40a: "胁迫报警",
+    0x40b: "离线事件满90%报警",
+    0x40c: "卡号认证失败超次报警",
+    0x40d: "SD卡存储满报警",
+    0x40e: "联动抓拍事件报警",
+    0x40f: "门控安全模块防拆报警",
+    0x410: "门控安全模块防拆恢复",
+    0x411: "POS开启",
+    0x412: "POS结束",
+    0x413: "人脸图像画质低",
+    0x414: "指纹图像画质低",
+    0x415: "消防输入短路报警",
+    0x416: "消防输入断路报警",
+    0x417: "消防输入恢复",
+    0x418: "消防按钮触发",
+    0x419: "消防按钮恢复",
+    0x41a: "维护按钮触发",
+    0x41b: "维护按钮恢复",
+    0x41c: "紧急按钮触发",
+    0x41d: "紧急按钮恢复",
+    0x41e: "分控器防拆报警",
+    0x41f: "分控器防拆报警恢复",
+    0x422: "通道控制器防拆报警",
+    0x423: "通道控制器防拆报警恢复",
+    0x424: "通道控制器消防输入报警",
+    0x425: "通道控制器消防输入报警恢复",
+    0x442: "合法事件满90%报警",
+    0x95d: "智能锁防劫持报警",
+  },
+  0x2: {
+    // MAJOR_EXCEPTION - 异常
+    0x27: "网络断开",
+    0x28: "网络恢复",
+    0x3a: "RS485连接状态异常",
+    0x3b: "RS485连接状态异常恢复",
+    0x400: "设备上电启动",
+    0x401: "设备掉电关闭",
+    0x402: "看门狗复位",
+    0x403: "蓄电池电压低",
+    0x404: "蓄电池电压恢复正常",
+    0x405: "交流电断电",
+    0x406: "交流电恢复",
+    0x407: "网络恢复",
+    0x408: "FLASH读写异常",
+    0x409: "读卡器掉线",
+    0x40a: "读卡器掉线恢复",
+    0x40b: "指示灯关闭",
+    0x40c: "指示灯恢复",
+    0x40d: "通道控制器掉线",
+    0x40e: "通道控制器恢复",
+    0x40f: "门控安全模块掉线",
+    0x410: "门控安全模块掉线恢复",
+    0x411: "电池电压低(仅人脸设备使用)",
+    0x412: "电池电压恢复正常(仅人脸设备使用)",
+    0x413: "就地控制器网络断开",
+    0x414: "就地控制器网络恢复",
+    0x415: "主控RS485环路节点断开",
+    0x416: "主控RS485环路节点恢复",
+    0x417: "就地控制器掉线",
+    0x418: "就地控制器掉线恢复",
+    0x419: "就地下行RS485环路断开",
+    0x41a: "就地下行RS485环路恢复",
+    0x41b: "分控器在线",
+    0x41c: "分控器离线",
+    0x41d: "身份证阅读器未连接(智能专用)",
+    0x41e: "身份证阅读器连接恢复(智能专用)",
+    0x41f: "指纹模组未连接(智能专用)",
+    0x420: "指纹模组连接恢复(智能专用)",
+    0x421: "摄像头未连接",
+    0x422: "摄像头连接恢复",
+    0x423: "COM口未连接",
+    0x424: "COM口连接恢复",
+    0x425: "设备未授权",
+    0x426: "人证设备在线",
+    0x427: "人证设备离线",
+    0x428: "本地登录锁定",
+    0x429: "本地登录解锁",
+    0x42a: "与反潜回服务器通信断开",
+    0x42b: "与反潜回服务器通信恢复",
+    0x42c: "电机或传感器异常",
+    0x42d: "CAN总线异常",
+    0x42e: "CAN总线恢复",
+    0x42f: "闸机腔体温度超限",
+    0x430: "红外对射异常",
+    0x431: "红外对射恢复",
+    0x432: "灯板通信异常",
+    0x433: "灯板通信恢复",
+    0x434: "红外转接板通信异常",
+    0x435: "红外转接板通信恢复",
+  },
+  0x3: {
+    // MAJOR_OPERATION - 操作
+    0x50: "本地登陆",
+    0x51: "本地注销登陆",
+    0x52: "修改密码",
+    0x53: "本地参数修改",
+    0x5a: "本地升级",
+    0x60: "远程参数修改",
+    0x61: "远程开门",
+    0x62: "远程关闭门",
+    0x70: "远程登录",
+    0x71: "远程注销登陆",
+    0x72: "远程升级",
+    0x73: "远程布防/撤防",
+    0x79: "远程布防",
+    0x7a: "远程撤防",
+    0x7b: "远程重启",
+    0x7e: "远程升级",
+    0x86: "远程导出配置文件",
+    0x87: "远程导入配置文件",
+    0xd6: "远程手动开启报警输出",
+    0xd7: "远程手动关闭报警输出",
+    0x400: "远程开门",
+    0x401: "远程关门(梯控受控)",
+    0x402: "远程常开(梯控自由)",
+    0x403: "远程常关(梯控禁用)",
+    0x404: "远程手动校时",
+    0x405: "NTP自动校时",
+    0x406: "远程清空卡号",
+    0x407: "远程恢复默认参数",
+    0x408: "防区布防",
+    0x409: "防区撤防",
+    0x40a: "本地恢复默认参数",
+    0x40b: "远程抓拍",
+    0x40c: "修改网络中心参数配置",
+    0x40d: "修改GPRS中心参数配置",
+    0x40e: "修改中心组参数配置",
+    0x40f: "解除码输入",
+    0x410: "自动重新编号",
+    0x411: "自动补充编号",
+    0x412: "导入普通配置文件",
+    0x413: "导出普通配置文件",
+    0x414: "导入卡权限参数",
+    0x415: "导出卡权限参数",
+    0x416: "本地U盘升级",
+    0x417: "访客呼梯",
+    0x418: "住户呼梯",
+    0x419: "远程实时布防",
+    0x41a: "远程实时撤防",
+    0x41b: "遥控器未对码操作失败",
+    0x41c: "遥控器关门",
+    0x41d: "遥控器开门",
+    0x41e: "遥控器常开门",
+  },
+  0x5: {
+    // MAJOR_EVENT - 事件
+    0x01: "合法卡认证通过",
+    0x02: "刷卡加密码认证通过",
+    0x03: "刷卡加密码认证失败",
+    0x04: "数卡加密码认证超时",
+    0x05: "刷卡加密码超次",
+    0x06: "未分配权限",
+    0x07: "无效时段",
+    0x08: "卡号过期",
+    0x09: "无此卡号",
+    0x0a: "反潜回认证失败",
+    0x0b: "互锁门未关闭",
+    0x0c: "卡不属于多重认证群组",
+    0x0d: "卡不在多重认证时间段内",
+    0x0e: "多重认证模式超级权限认证失败",
+    0x0f: "多重认证模式远程认证失败",
+    0x10: "多重认证成功",
+    0x11: "首卡开门开始",
+    0x12: "首卡开门结束",
+    0x13: "常开状态开始",
+    0x14: "常开状态结束",
+    0x15: "门锁打开",
+    0x16: "门锁关闭",
+    0x17: "开门按钮打开",
+    0x18: "开门按钮放开",
+    0x19: "正常开门(门磁)",
+    0x1a: "正常关门(门磁)",
+    0x1b: "门异常打开(门磁)",
+    0x1c: "门打开超时(门磁)",
+    0x1d: "报警输出打开",
+    0x1e: "报警输出关闭",
+    0x1f: "常关状态开始",
+    0x20: "常关状态结束",
+    0x21: "多重认证需要远程开门",
+    0x22: "多重认证超级密码认证成功事件",
+    0x23: "多重认证重复认证事件",
+    0x24: "多重认证超时",
+    0x25: "门铃响",
+    0x26: "指纹比对通过",
+    0x27: "指纹比对失败",
+    0x28: "刷卡加指纹认证通过",
+    0x29: "刷卡加指纹认证失败",
+    0x2a: "刷卡加指纹认证超时",
+    0x2b: "刷卡加指纹加密码认证通过",
+    0x2c: "刷卡加指纹加密码认证失败",
+    0x2d: "刷卡加指纹加密码认证超时",
+    0x2e: "指纹加密码认证通过",
+    0x2f: "指纹加密码认证失败",
+    0x30: "指纹加密码认证超时",
+    0x31: "指纹不存在",
+    0x32: "刷卡平台认证",
+    0x33: "呼叫中心事件",
+    0x34: "消防继电器导通触发门常开",
+    0x35: "消防继电器恢复门恢复正常",
+    0x36: "人脸加指纹认证通过",
+    0x37: "人脸加指纹认证失败",
+    0x38: "人脸加指纹认证超时",
+    0x39: "人脸加密码认证通过",
+    0x3a: "人脸加密码认证失败",
+    0x3b: "人脸加密码认证超时",
+    0x3c: "人脸加刷卡认证通过",
+    0x3d: "人脸加刷卡认证失败",
+    0x3e: "人脸加刷卡认证超时",
+    0x3f: "人脸加密码加指纹认证通过",
+    0x40: "人脸加密码加指纹认证失败",
+    0x41: "人脸加密码加指纹认证超时",
+    0x42: "人脸加刷卡加指纹认证通过",
+    0x43: "人脸加刷卡加指纹认证失败",
+    0x44: "人脸加刷卡加指纹认证超时",
+    0x45: "工号加指纹认证通过",
+    0x46: "工号加指纹认证失败",
+    0x47: "工号加指纹认证超时",
+    0x48: "工号加指纹加密码认证通过",
+    0x49: "工号加指纹加密码认证失败",
+    0x4a: "工号加指纹加密码认证超时",
+    0x4b: "人脸认证通过",
+    0x4c: "人脸认证失败",
+    0x4d: "工号加人脸认证通过",
+    0x4e: "工号加人脸认证失败",
+    0x4f: "工号加人脸认证超时",
+    0x50: "人脸抓拍失败",
+    0x51: "首卡授权开始",
+    0x52: "首卡授权结束",
+    0x53: "门锁输入短路报警",
+    0x54: "门锁输入断路报警",
+    0x55: "门锁输入异常报警",
+    0x56: "门磁输入短路报警",
+    0x57: "门磁输入断路报警",
+    0x58: "门磁输入异常报警",
+    0x59: "开门按钮输入短路报警",
+    0x5a: "开门按钮输入断路报警",
+    0x5b: "开门按钮输入异常报警",
+    0x5c: "门锁异常打开",
+    0x5d: "门锁打开超时",
+    0x5e: "首卡未授权开门失败",
+    0x5f: "呼梯继电器断开",
+    0x60: "呼梯继电器闭合",
+    0x61: "自动按键继电器断开",
+    0x62: "自动按键继电器闭合",
+    0x63: "按键梯控继电器断开",
+    0x64: "按键梯控继电器闭合",
+    0x65: "工号加密码认证通过",
+    0x66: "工号加密码认证失败",
+    0x67: "工号加密码认证超时",
+    0x68: "真人检测失败",
+    0x69: "人证比对通过",
+    0x70: "人证比对失败",
+    0x71: "非授权名单事件",
+    0x72: "合法短信",
+    0x73: "非法短信",
+    0x74: "MAC侦测",
+    0x75: "门状态常闭或休眠状态认证失败",
+    0x76: "认证计划休眠模式认证失败",
+    0x77: "卡加密校验失败",
+    0x78: "反潜回服务器应答失败",
+    0x85: "尾随通行",
+    0x86: "反向闯入",
+    0x87: "外力冲撞",
+    0x88: "翻越",
+    0x89: "通行超时",
+    0x8a: "误闯报警",
+    0x8b: "闸机自由通行时未认证通过",
+    0x8c: "摆臂被阻挡",
+    0x8d: "摆臂阻挡消除",
+    0x8e: "设备升级本地人脸建模失败",
+    0x8f: "逗留事件",
+    0x97: "密码不匹配",
+    0x98: "工号不存在",
+    0x99: "组合认证通过",
+    0x9a: "组合认证超时",
+    0x9b: "认证方式不匹配",
+  },
+};
+
+export default minorTypeMap;

文件差異過大導致無法顯示
+ 676 - 264
src/views/doormanManage/infoManage/index.vue


+ 25 - 0
src/views/grassrootsregistration/bdglcookbook/index.vue

@@ -155,6 +155,12 @@
             v-hasPermi="['grassrootsregistration:bdglcookbook:edit']"
             ><span class="edit">修改</span></el-button
           >
+          <el-button
+            size="mini"
+            type="warning"
+            @click="handleExportExcel(scope.row)"
+            ><span class="edit">Excel导出</span></el-button
+          >
           <el-button
             size="btd"
             type="text"
@@ -1786,6 +1792,7 @@ import bdglcookbook from "@/components/look_word/bdglcookbook.vue";
 import {
   listBdglcookbook,
   getBdglcookbook,
+  exportBdglcookbookExcel,
   delBdglcookbook,
   addBdglcookbook,
   updateBdglcookbook,
@@ -2349,6 +2356,24 @@ export default {
         this.menuRoleVisible = true;
       });
     },
+    //导出按钮操作
+    handleExportExcel(row) {
+      const id = row.id || this.ids;
+      exportBdglcookbookExcel(id).then(res => {
+        // 处理文件下载
+        const blob = new Blob([res], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
+        const link = document.createElement('a');
+        link.href = URL.createObjectURL(blob);
+        link.download = "(" + row.title + ")" + '一周食谱表.xlsx';
+        document.body.appendChild(link);
+        link.click();
+        document.body.removeChild(link);
+        URL.revokeObjectURL(link.href);
+      }).catch(error => {
+        console.error('导出Excel失败:', error);
+        this.$message.error('导出失败,请重试');
+      });
+    },
     /** 修改按钮操作 */
     handleUpdate(row) {
       this.getTreeselect();

+ 1 - 1
src/views/grassrootsregistration/bdgldrills/index.vue

@@ -20,7 +20,7 @@
         </el-date-picker>
       </el-form-item>
       <el-form-item>
-        <el-button size="btr" @click="resetQuery">重置</el-button>
+        <el-button size="btr" @click="resetQuery">重1置</el-button>
       </el-form-item>
     </el-form>
    <div v-if="status==1">

+ 65 - 0
src/views/grassrootsregistration/bdglweekwork/index.vue

@@ -157,6 +157,13 @@
             v-hasPermi="['grassrootsregistration:bdglweekwork:edit']"
             ><span class="edit">修改</span></el-button
           >
+          <el-button
+            size="small"
+            type="warning"
+            @click="handleCopy(scope.row)"
+            v-hasPermi="['grassrootsregistration:bdglweekwork:edit']"
+            ><span class="edit">复制</span></el-button
+          >
           <el-button
             size="btd"
             type="text"
@@ -968,6 +975,64 @@ export default {
         this.tomList = response.data.bdglWeekworkRegisterList;
         this.gongZuoTiem = response.data.bdglWeekworkRegisterList[0].time;
       });
+    },
+     /** 复制按钮操作 */
+     handleCopy(row) { 
+      this.reset();
+      this.getTreeselect();
+      this.form.unitId = row.unitId;
+      this.ZhuChiRen(this.form.unitId);
+      const id = row.id || this.ids;
+      getBdglweekwork(id).then((response) => {
+        console.log("复制按钮0",this.form)
+        this.form = response.data;
+        console.log("复制按钮1",this.form)
+        this.tomList = response.data.bdglWeekworkRegisterList;
+        // 添加安全检查防止数组为空
+        if (response.data.bdglWeekworkRegisterList && response.data.bdglWeekworkRegisterList.length > 0) {
+          this.gongZuoTiem = response.data.bdglWeekworkRegisterList[0].time;
+        }
+        // 在获取数据完成后再执行添加操作,确保数据一致性
+        this.form.id = null;
+        for (let i = 0; i < this.form.bdglWeekworkRegisterList.length; i++) {
+          this.form.bdglWeekworkRegisterList[i].id = null;
+        }
+        //name前面加"副本"+1,2,3 + "-",如果name已经有"副本"+1,2,3 + "-",则进行+1操作
+        if (this.form.name.includes("副本")) {
+          // 使用正则表达式匹配"副本"+数字的模式
+          const reg = /副本(\d+)$/;
+          const match = this.form.name.match(reg);
+          
+          if (match) {
+            const num = parseInt(match[1]);
+            const baseName = this.form.name.substring(0, this.form.name.length - match[1].length);
+            this.form.name = baseName + (num + 1);
+          } else {
+            // 如果有"副本"但不匹配模式,则直接添加"-副本1"
+            this.form.name = this.form.name + "-副本1";
+          }
+        } else {
+          this.form.name = this.form.name + "-副本1";
+        }
+
+        return addBdglweekwork(this.form);
+      }).then((response) => {
+        this.$modal.msgSuccess("新增副本成功");
+        this.getList();
+      }).catch((error) => {
+        // 添加错误处理
+        this.$modal.msgError("操作失败,请重试");
+        console.error("复制操作失败:", error);
+      });
+      // copyBdglweekwork(id).then((response) => {
+      //   if (response.code == 200) {
+      //     this.$modal.msgSuccess(response.msg);
+      //     this.getList();
+      //   } else {
+      //     this.$modal.msgError(response.msg);
+      //   }
+      // });
+
     },
     //查看按钮操作
     handleChakan(row) {

+ 360 - 281
src/views/grassrootsregistration/personnelRoster/index.vue

@@ -1,140 +1,160 @@
 <!--人员记录本领导 -->
 <template>
-  <div class="arr_conten">
-    <div v-if="loading" class="loading">加载中...</div>
-    <!-- 空状态 -->
-    <div v-else-if="!lieList || lieList.length === 0" class="empty">
-      暂无数据
-    </div>
-<!--    <div v-if="status == 1">-->
-      <div v-else-if="lieList && lieList.length > 0">
-      <ul v-for="(item, i) in lieList" :key="i + ' '">
-        <li v-for="(item1, i1) in item" :key="i1">
-          <div class="names">{{ item1.deptName }}</div>
-          <div class="rens">{{item1.peopleNum}}人</div>
-          <div class="btn">
-            <el-button size="btJS" @click="chaKan(item1.deptId)"
-              >查看</el-button
-            >
-          </div>
-        </li>
-      </ul>
+  <div class="personnel-roster-container">
+    <!-- 左侧部门列表 -->
+    <div class="department-list">
+      <div class="department-title">部门列表</div>
+      <div 
+        v-for="(item, i) in lieList" 
+        :key="i"
+        class="department-item"
+        :class="{ active: selectedDeptId === item.deptId }"
+        @click="selectDepartment(item.deptId)"
+      >
+        <div class="dept-name">{{ item.deptName }}</div>
+        <div class="people-count">{{item.peopleNum}}人</div>
+      </div>
+      <div v-if="lieList.length === 0" class="empty-tip">暂无部门数据</div>
     </div>
 
-    <!-- 查看第一次 -->
-    <!-- <el-dialog
-      :visible.sync="tableStatus"
-      :title="title"
-      append-to-body
-      id="list"
-      :close-on-click-modal="false"
-      custom-class="lieBiao"
-    >
-      <el-table
-        v-loading="loading"
-        :data="bdglevenList"
-        :header-cell-style="{ background: '#003C69', color: 'white' }"
-      >
-        <el-table-column label="序号" type="index" width="50" align="center">
-          <template scope="scope">
-            <span>{{
-              (queryParams.pageNum - 1) * queryParams.pageSize +
-              scope.$index +
-              1
-            }}</span>
-          </template>
-        </el-table-column>
-        <el-table-column
-          label="单位"
-          align="center"
-          prop="deptName"
-          show-overflow-tooltip
-          width="150"
-        />
-        <el-table-column label="姓名" align="center" prop="name" />
-        <el-table-column label="性别" align="center" prop="sex">
-          <template slot-scope="scope">
-            <dict-tag
-              :options="dict.type.sys_user_sex"
-              :value="scope.row.sex"
-            />
-          </template>
-        </el-table-column>
-        <el-table-column label="年龄" align="center" prop="age" />
-        <el-table-column
-          label="身份证号"
-          align="center"
-          prop="idcard"
-          width="170"
-          show-overflow-tooltip
-        />
-        <el-table-column
-          label="部职别"
-          align="center"
-          width="200"
-          prop="duty"
-          show-overflow-tooltip
-        />
-        <el-table-column label="职务" align="center" width="120" prop="postId">
-          <template slot-scope="scope">
-            <dict-tag
-              :options="dict.type.post_Level"
-              :value="scope.row.postId"
-            />
-          </template>
-        </el-table-column>
-        <el-table-column
-          label="职务等级时间"
-          align="center"
-          prop="postDate"
-          width="180"
+    <!-- 右侧人员详情 -->
+    <div class="personnel-details">
+      <div v-if="selectedDeptId" class="details-content">
+        <div class="details-header">
+          <h3>{{ currentDeptName }}</h3>
+          <div class="total-count">共 {{ total }} 人</div>
+        </div>
+        
+        <el-table
+          v-loading="loading"
+          :data="bdglevenList"
+          :header-cell-style="{ background: '#003C69', color: 'white' }"
+          style="width: 100%"
         >
-          <template slot-scope="scope">
-            <span>{{ parseTime(scope.row.postDate, "{y}-{m}-{d}") }}</span>
-          </template>
-        </el-table-column>
-        <el-table-column
-          label="籍贯"
-          align="center"
-          prop="origin"
-          width="150"
+          <el-table-column label="序号" type="index" width="50" align="center">
+            <template slot-scope="scope">
+              <span>{{
+                (queryParams.pageNum - 1) * queryParams.pageSize +
+                scope.$index +
+                1
+              }}</span>
+            </template>
+          </el-table-column>
+          <el-table-column
+            label="单位"
+            align="center"
+            prop="deptName"
+            show-overflow-tooltip
+            width="150"
+          />
+          <el-table-column label="姓名" align="center" prop="name" />
+          <el-table-column label="性别" align="center" prop="sex">
+            <template slot-scope="scope">
+              <dict-tag
+                :options="dict.type.sys_user_sex"
+                :value="scope.row.sex"
+              />
+            </template>
+          </el-table-column>
+          <el-table-column label="年龄" align="center" prop="age" />
+          <el-table-column
+            label="身份证号"
+            align="center"
+            prop="idcard"
+            width="170"
+            show-overflow-tooltip
+          />
+          <el-table-column
+            label="部职别"
+            align="center"
+            width="200"
+            prop="duty"
+            show-overflow-tooltip
+          />
+          <el-table-column label="职务" align="center" width="120" prop="postId">
+            <template slot-scope="scope">
+              <dict-tag
+                :options="dict.type.post_Level"
+                :value="scope.row.postId"
+              />
+            </template>
+          </el-table-column>
+          <el-table-column
+            label="职务等级时间"
+            align="center"
+            prop="postDate"
+            width="180"
+          >
+            <template slot-scope="scope">
+              <span>{{ parseTime(scope.row.postDate, "{y}-{m}-{d}") }}</span>
+            </template>
+          </el-table-column>
+          <el-table-column
+            label="籍贯"
+            align="center"
+            prop="origin"
+            width="150"
+          />
+          <el-table-column
+            label="在位情况"
+            align="center"
+            prop="reign"
+            width="90"
+          >
+            <template slot-scope="scope">
+              <dict-tag
+                :options="dict.type.people_state"
+                :value="scope.row.reign"
+              />
+            </template>
+          </el-table-column>
+          <el-table-column
+            label="操作"
+            align="center"
+            class-name="small-padding fixed-width"
+            width="100"
+            fixed="right"
+          >
+            <template slot-scope="scope">
+              <el-button size="btk" type="text" @click="handleChakan(scope.row)"
+                ><span class="chakan">查看</span>
+              </el-button>
+            </template>
+          </el-table-column>
+        </el-table>
+
+        <pagination
+          v-show="total > 0"
+          :total="total"
+          :page.sync="queryParams.pageNum"
+          :limit.sync="queryParams.pageSize"
+          @pagination="handlePagination"
         />
-        <el-table-column
-          label="在位情况"
-          align="center"
-          prop="reign"
-          width="90"
-        >
-          <template slot-scope="scope">
-            <dict-tag
-              :options="dict.type.people_state"
-              :value="scope.row.reign"
-            />
-          </template>
-        </el-table-column>
-        <el-table-column
-          label="操作"
-          align="center"
-          class-name="small-padding fixed-width"
-          width="200"
-          fixed="right"
-        >
-          <template slot-scope="scope">
-            <el-button size="btk" type="text" @click="handleChakan(scope.row)"
-              ><span class="chakan">查看</span>
-            </el-button>
-          </template>
-        </el-table-column>
-      </el-table>
-
-      <pagination
-        v-show="total > 0"
-        :total="total"
-        :page.sync="queryParams.pageNum"
-        :limit.sync="queryParams.pageSize"
-        @pagination="chaKan1"
-      />
-    </el-dialog> -->
+      </div>
+      
+      <div v-else class="no-selection">
+        <div class="empty-icon">👥</div>
+        <div class="empty-text">请选择一个部门查看人员详情</div>
+      </div>
+    </div>
+
+    <!-- 人员详情弹窗 -->
+    <div class="fff">
+      <el-dialog
+        :visible.sync="menuRoleVisible"
+        :title="title"
+        append-to-body
+        id="chakan"
+        :close-on-click-modal="false"
+      >
+        <personnelRoster
+          v-if="menuRoleVisible"
+          ref="menuRole"
+          :message="wordInfo"
+          :renshu="renshu"
+        ></personnelRoster>
+      </el-dialog>
+    </div>
 
     <div class="fff">
       <el-dialog
@@ -157,24 +177,15 @@
 
 <script>
 import personnelRoster from "@/components/look_word/personnelRoster.vue";
-import { listTypestatistics } from "@/api/peopleManage/typestatistics";
 import {
-  listPeople,
-  getPeople,
-  delPeople,
-  addPeople,
-  updatePeople,
-  exportPeople,
   getDept,
   treeselect,
 } from "@/api/peopleManage/people";
-import Treeselect from "@riophae/vue-treeselect";
 import {
   getPersonnelRosterList,
   getPersonnelRosterOnUnitList,
 } from "@/api/grassrootsregistration/personnelRoster";
 import {
-  getXiangQis,
   getRenYuanXiangQing,
 } from "@/api/grassrootsregistration/bdglmeeting";
 
@@ -239,8 +250,6 @@ export default {
       status: null,
       arr: [],
       lieList: [],
-      //查看页面
-      menuRoleVisible: false,
       // 表单校验
       rules: {},
       //性别数组
@@ -288,6 +297,10 @@ export default {
       value: null,
       tableStatus: false,
       bdglevenList: [],
+      // 新增:选中的部门ID
+      selectedDeptId: null,
+      // 新增:当前选中部门名称
+      currentDeptName: '',
     };
   },
   // updated() {
@@ -308,47 +321,44 @@ export default {
     });
   },
   methods: {
-    chaKan(row) {
-      this.opens = false;
-      const deptId = row;
-      getRenYuanXiangQing(deptId).then((response) => {
-        this.renshu = response.data[0];
-        this.wordInfo = response.data[1];
-        this.title = "查看人员名册";
-        this.menuRoleVisible = true;
-        console.log("请求参数是:", this.queryParams);
-        console.log("接口返回的数据:", res);
-      })
-      console.log("请求参数是:", this.queryParams);
-      console.log("接口返回的数据:", res);
-      // this.queryParams.unitId = row;
-      // this.unitId = row;
-      // getPersonnelRosterOnUnitList(this.queryParams).then((res) => {
-      //   this.bdglevenList = res.rows;
-      //   this.total = res.total;
-      //   this.title = "查看人员名册";
-      //   this.tableStatus = true;
-      //   this.loading = false;
-      // });
+    // 选择部门
+    selectDepartment(deptId) {
+      this.selectedDeptId = deptId;
+      // 获取部门名称
+      const selectedDept = this.lieList.find(item => item.deptId === deptId);
+      if (selectedDept) {
+        this.currentDeptName = selectedDept.deptName;
+      }
+      
+      // 查询该部门的人员数据
+      this.queryParams.unitId = deptId;
+      this.queryParams.pageNum = 1; // 重置页码
+      this.loading = true;
+      
+      getPersonnelRosterOnUnitList(this.queryParams).then((res) => {
+        this.bdglevenList = res.rows || [];
+        this.total = res.total || 0;
+        this.loading = false;
+      }).catch(error => {
+        console.error("获取部门人员数据失败:", error);
+        this.bdglevenList = [];
+        this.total = 0;
+        this.loading = false;
+      });
     },
-    chaKan1(row) {
-      getMeetRecordOnUnitList(this.queryParams).then((res) => {
-        this.bdglevenList = res.rows;
-        this.total = res.total;
-        this.title = "查看人员名册";
-        this.tableStatus = true;
+    
+    // 处理分页
+    handlePagination() {
+      getPersonnelRosterOnUnitList(this.queryParams).then((res) => {
+        this.bdglevenList = res.rows || [];
+        this.total = res.total || 0;
+      }).catch(error => {
+        console.error("分页查询失败:", error);
+        this.bdglevenList = [];
+        this.total = 0;
       });
     },
     /** 查询人员管理列表 */
-    // getList() {
-    //     getPersonnelRosterList().then((res) => {
-    //       res.rows.forEach((item) => {
-    //         this.status = 1;
-    //       });
-    //       this.lieList = res.rows;
-    //       console.log(res.rows);
-    //     });
-    // },
     getList() {
       this.loading = true;
       getPersonnelRosterList().then((res) => {
@@ -388,9 +398,7 @@ export default {
     },
     //查看按钮操作
     handleChakan(row) {
-      console.log(row.deptId);
       // 是否隐藏按钮
-      this.opens = false;
       const deptId = row.deptId;
       getRenYuanXiangQing(deptId).then((response) => {
         this.renshu = response.data[0];
@@ -418,135 +426,205 @@ export default {
 };
 </script>
 <style  scoped>
-::v-deep .el-dialog {
-  width: 1070px !important;
+/* 主容器样式 */
+.personnel-roster-container {
+  display: flex;
+  height: calc(100vh - 60px);
+  padding: 20px;
+  gap: 20px;
 }
 
-::v-deep .el-dialog__body {
-  margin: 10px 30px 20px 44px;
-  padding-top: 20px !important;
-  box-sizing: border-box;
+/* 左侧部门列表样式 */
+.department-list {
+  width: 300px;
+  background: rgba(0, 60, 105, 0.3);
+  border: 1px solid #718a9d;
+  border-radius: 8px;
+  padding: 20px;
+  overflow-y: auto;
+  display: flex;
+  flex-direction: column;
 }
 
-::v-deep .el-dialog__header {
+.department-title {
+  font-size: 18px;
+  font-weight: bold;
+  color: #fff;
+  margin-bottom: 20px;
+  text-align: center;
+  padding-bottom: 10px;
   border-bottom: 1px solid #718a9d;
 }
 
-.arr_conten {
-  padding-top: 20px;
+.department-item {
+  background: rgba(0, 60, 105, 0.5);
+  border: 1px solid #3d83b8;
+  border-radius: 6px;
+  padding: 15px;
+  margin-bottom: 12px;
+  cursor: pointer;
+  transition: all 0.3s ease;
+}
+
+.department-item:hover {
+  background: rgba(0, 60, 105, 0.8);
+  border-color: #90cef1;
+  transform: translateX(5px);
+}
+
+.department-item.active {
+  background: rgba(0, 120, 215, 0.7);
+  border-color: #90cef1;
+  box-shadow: 0 0 10px rgba(144, 206, 252, 0.3);
+}
+
+.dept-name {
+  color: #fff;
+  font-size: 16px;
+  font-weight: 500;
+  margin-bottom: 8px;
+}
+
+.people-count {
+  color: #90cef1;
+  font-size: 14px;
+}
+
+.empty-tip {
+  color: #90cef1;
+  text-align: center;
+  padding: 40px 0;
+  font-size: 14px;
 }
 
-ul {
-  /* justify-content: space-between; */
-  padding: 0 22px;
+/* 右侧人员详情样式 */
+.personnel-details {
+  flex: 1;
+  background: rgba(0, 60, 105, 0.3);
+  border: 1px solid #718a9d;
+  border-radius: 8px;
   display: flex;
-  flex-wrap: wrap;
+  flex-direction: column;
+  min-width: 0;
 }
-.span {
+
+.details-content {
+  height: 100%;
   display: flex;
-  flex-wrap: wrap;
+  flex-direction: column;
+  padding: 20px;
 }
 
-li {
-  list-style: none;
-  background: url("../../../assets/images/book.png") no-repeat;
-  height: 140px;
-  width: 222px;
-  /* margin: 20px 0; */
+.details-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
   margin-bottom: 20px;
-  background-size: 100% 100%;
-  flex: 0 0 16%;
-  margin-right: 10px;
+  padding-bottom: 10px;
+  border-bottom: 1px solid #718a9d;
 }
 
-.names {
+.details-header h3 {
   color: #fff;
-  font-size: 14px;
-  text-align: center;
-  padding: 26px 26px 38px 26px;
-  letter-spacing: 1px;
+  font-size: 20px;
+  margin: 0;
 }
-.rens{
-  color: #fff;
-  font-size: 14px;
-  text-align: center;
-  position: relative;
-  left: 0%;
-  top: -12%;
-  letter-spacing: 1px;
+
+.total-count {
+  color: #90cef1;
+  font-size: 16px;
+  font-weight: 500;
 }
 
-.btn {
-  text-align: center;
+.no-selection {
+  flex: 1;
+  display: flex;
+  flex-direction: column;
+  justify-content: center;
+  align-items: center;
+  color: #90cef1;
 }
 
-::v-deep .lieBiao {
-  background: #004d86 !important;
-  min-height: 42vh !important;
+.empty-icon {
+  font-size: 64px;
+  margin-bottom: 20px;
 }
 
-::v-deep .el-dialog__title {
-  color: #fff;
-  font: 18px;
+.empty-text {
+  font-size: 18px;
+}
+
+/* 表格样式 */
+::v-deep .el-table {
+  flex: 1;
+  background: transparent;
+}
+
+::v-deep .el-table__header-wrapper th {
+  background-color: #003C69 !important;
+  color: white !important;
+  border-bottom: 1px solid #718a9d;
+}
+
+::v-deep .el-table__body-wrapper {
+  overflow-y: auto;
+  flex: 1;
 }
 
-::v-deep .el-dialog__headerbtn .el-dialog__close {
+::v-deep .el-table__row {
+  background-color: rgba(0, 54, 95, 0.8);
   color: #fff;
 }
 
+::v-deep .el-table__row:nth-child(even) {
+  background-color: rgba(0, 54, 95, 0.6);
+}
+
+::v-deep .el-table__row:hover {
+  background-color: #3c749d !important;
+}
+
+::v-deep .el-table__body tr:hover > td {
+  background-color: transparent !important;
+}
+
+/* 分页样式 */
 ::v-deep .pagination-container {
-  /* display: none;  */
-  position: relative;
-  height: 49px;
-  margin-bottom: 10px;
-  margin-top: 6vh;
-  padding: 10px 20px !important;
+  margin-top: 20px;
+  padding: 10px 0;
   background: transparent !important;
 }
 
 ::v-deep .el-pagination .el-select .el-input .el-input__inner {
   background: #00365f !important;
+  border-color: #718a9d;
+  color: #fff;
 }
 
 ::v-deep .el-pagination.is-background .btn-prev,
-.el-pagination.is-background .btn-next {
+::v-deep .el-pagination.is-background .btn-next {
   background: #00365f !important;
+  border-color: #718a9d;
+  color: #fff;
 }
 
 ::v-deep .el-pagination__editor.el-input .el-input__inner {
   background: #00365f !important;
-}
-
-::v-deep .el-pagination.is-background .btn-next {
-  background: #00365f !important;
+  border-color: #718a9d;
+  color: #fff;
 }
 
 ::v-deep .el-pagination.is-background .el-pager li {
   background: #00365f;
+  color: #fff;
 }
 
-::v-deep .el-table__body tr:hover > td {
-  background-color: #3c749d !important;
-}
-::v-deep .el-table tr:nth-child(even) {
-  background: #00365f;
-}
-
-::v-deep .el-input__inner {
-  cursor: pointer !important;
-}
-
-::v-deep .el-input__inner {
-  background: transparent;
+::v-deep .el-pagination.is-background .el-pager li.active {
+  background: #3c749d;
   color: #fff;
-  border: 1px solid white !important;
-  border-color: #fff !important;
 }
 
-::v-deep .el-input--medium .el-input__inner {
-  height: 36px;
-  line-height: 37px;
-}
+/* 弹窗样式保持不变 */
 #chakan ::v-deep .el-dialog {
   min-height: 700px;
   background: #fff !important;
@@ -555,36 +633,37 @@ li {
 #chakan /deep/ .el-dialog__body {
   padding: 30px 30px 30px 30px;
 }
-.divbox {
-  width: 1016px !important;
-  min-height: 540px;
-}
-.divbox img {
-  width: 700px;
-  height: 500px;
-  border-radius: 10px;
-}
+
 img {
   object-fit: cover;
 }
-/* 添加内部滚动条 */
-::v-deep .el-dialog__body {
-  overflow: auto;
-  overflow-x: hidden;
+
+/* 滚动条样式 */
+::-webkit-scrollbar {
+  width: 8px;
+  height: 8px;
 }
 
-/*定义滚动条宽高及背景,宽高分别对应横竖滚动条的尺寸*/
-::v-deep .el-dialog__body ::-webkit-scrollbar {
-  width: 14px;
-  /* height: 2px !important; */
+::-webkit-scrollbar-track {
+  background: rgba(0, 60, 105, 0.3);
+}
+
+::-webkit-scrollbar-thumb {
   background: #3d83b8;
+  border-radius: 4px;
+}
+
+::-webkit-scrollbar-thumb:hover {
+  background: #90cef1;
 }
 
-/*定义滑块,内阴影及圆角*/
-::v-deep .el-dialog__body ::-webkit-scrollbar-thumb {
-  /* border-radius: 20px; */
-  /* height: 1px;
-  width: 3px; */
-  background: rgba(144, 206, 252, 1);
+/* 查看按钮样式 */
+.chakan {
+  color: #90cef1;
+}
+
+.chakan:hover {
+  color: #fff;
+  text-decoration: underline;
 }
-</style>
+</style>

+ 3 - 3
src/views/login.vue

@@ -54,7 +54,6 @@
 import { getCodeImg } from "@/api/login";
 import Cookies from "js-cookie";
 import { encrypt, decrypt } from "@/utils/jsencrypt";
-import WebSocketService from "@/utils/WebSocketService";
 
 export default {
   name: "Login",
@@ -153,8 +152,9 @@ export default {
           }
           this.$store.dispatch("Login", this.loginForm).then(() => {
             const token = this.$store.getters.token;
-            // console.log("token", token)
-            WebSocketService.initWebSocket(token);
+            if (token) {
+              this.$store.dispatch('ws/connectWebSocket', token);
+            }
             // this.requestFullScreen()
             // this.$router.push({ path: this.redirect || "/" }).catch(() => {});
             this.$router.push({ path: "/pt" }).catch(() => { });

+ 617 - 41
src/views/peopleChuRu/peopleAccess/index.vue

@@ -1,45 +1,113 @@
 <template>
   <div class="app-container">
-    <h2>人员出入管理(模拟人脸识别)</h2>
-
-    <!-- 人员表 -->
-    <el-table :data="peopleList" border style="width: 100%; margin-bottom: 30px;">
-      <el-table-column prop="id" label="人员ID" width="80" />
-      <el-table-column prop="name" label="姓名" width="120" />
-      <el-table-column prop="dept" label="部门" width="180" />
-      <el-table-column label="当前状态" width="120">
-        <template slot-scope="scope">
-          <el-tag :type="scope.row.status === '在营' ? 'success' : 'info'">
-            {{ scope.row.status }}
-          </el-tag>
-        </template>
-      </el-table-column>
-      <el-table-column label="操作" width="160">
-        <template slot-scope="scope">
-          <el-button size="mini" type="primary" @click="mockRecognition(scope.row, '入')">模拟入营</el-button>
-          <el-button size="mini" type="danger" @click="mockRecognition(scope.row, '出')">模拟出营</el-button>
-        </template>
-      </el-table-column>
-    </el-table>
-
-    <!-- 日志记录 -->
-    <el-card shadow="hover">
-      <div slot="header">
-        <span>人脸识别出入记录</span>
+
+    <!-- 主内容区域 -->
+    <div class="content-wrapper">
+      <!-- 左侧人员信息卡片 -->
+      <div class="people-section">
+        <div class="section-header">
+          <div class="section-title">人员列表</div>
+          <div class="section-stats">共 {{ peopleList.length }} 人</div>
+        </div>
+        
+        <!-- 人员卡片列表 -->
+        <div class="people-cards">
+          <div 
+            v-for="person in peopleList" 
+            :key="person.id" 
+            class="person-card"
+            :class="{
+              'person-in': person.status === '在营',
+              'person-out': person.status === '外出',
+              'person-unknown': person.status === '未识别'
+            }"
+          >
+            <div class="person-avatar">{{ person.name.charAt(0) }}</div>
+            <div class="person-info">
+              <div class="person-name">{{ person.name }}</div>
+              <div class="person-id">ID: {{ person.id }}</div>
+              <div class="person-dept">{{ person.dept }}</div>
+              <div class="person-status">
+                <el-tag 
+                  :type="getTagType(person.status)" 
+                  size="small"
+                  effect="dark"
+                >
+                  {{ person.status }}
+                </el-tag>
+              </div>
+            </div>
+            <div class="person-actions">
+              <el-button 
+                size="small" 
+                type="success" 
+                plain
+                icon="el-icon-check" 
+                @click="mockRecognition(person, '入')"
+                :disabled="person.status === '在营'"
+              >
+                入营
+              </el-button>
+              <el-button 
+                size="small" 
+                type="danger" 
+                plain
+                icon="el-icon-close" 
+                @click="mockRecognition(person, '出')"
+                :disabled="person.status === '外出'"
+              >
+                出营
+              </el-button>
+            </div>
+          </div>
+        </div>
+      </div>
+
+      <!-- 右侧出入记录 -->
+      <div class="log-section">
+        <el-card class="log-card" shadow="hover">
+          <div slot="header" class="log-header">
+            <div class="log-title">
+              <i class="el-icon-timer"></i> 人脸识别出入记录
+            </div>
+            <div class="log-stats" v-if="logList.length > 0">
+              今日记录: {{ logList.length }}
+            </div>
+          </div>
+          
+          <!-- 日志表格 -->
+          <div class="log-table-wrapper">
+            <el-table 
+              v-loading="false" 
+              :data="logList" 
+              style="width: 100%"
+              class="log-table"
+              :row-class-name="tableRowClassName"
+            >
+              <el-table-column prop="time" label="时间"  />
+              <el-table-column prop="name" label="姓名">
+                <template slot-scope="scope">
+                  <div class="log-name">{{ scope.row.name }}</div>
+                </template>
+              </el-table-column>
+              <el-table-column prop="dept" label="部门" />
+              <el-table-column prop="action" label="出入类型" >
+                <template slot-scope="scope">
+                  <el-tag 
+                    :type="scope.row.action === '入营' ? 'success' : 'danger'" 
+                    class="action-tag"
+                    effect="dark"
+                  >
+                    {{ scope.row.action }}
+                  </el-tag>
+                </template>
+              </el-table-column>
+            </el-table>
+            
+          </div>
+        </el-card>
       </div>
-      <el-table :data="logList" border style="width: 100%">
-        <el-table-column prop="time" label="时间" width="180" />
-        <el-table-column prop="name" label="姓名" width="120" />
-        <el-table-column prop="dept" label="部门" width="180" />
-        <el-table-column prop="action" label="出入类型" width="120">
-          <template slot-scope="scope">
-            <el-tag :type="scope.row.action === '入营' ? 'success' : 'danger'">
-              {{ scope.row.action }}
-            </el-tag>
-          </template>
-        </el-table-column>
-      </el-table>
-    </el-card>
+    </div>
   </div>
 </template>
 
@@ -59,7 +127,29 @@ export default {
     };
   },
   methods: {
+    // 根据状态获取标签类型
+    getTagType(status) {
+      switch(status) {
+        case '在营': return 'success';
+        case '外出': return 'danger';
+        default: return 'info';
+      }
+    },
+    
+    // 表格行样式
+    tableRowClassName({ row, rowIndex }) {
+      return 'log-row';
+    },
+    
+    // 模拟人脸识别
     mockRecognition(person, type) {
+      // 显示操作成功提示
+      this.$message({
+        message: `${person.name} ${type === '入' ? '入营' : '出营'}成功`,
+        type: 'success',
+        duration: 2000
+      });
+      
       // 更新时间
       const now = new Date();
       const time = now.toLocaleString();
@@ -67,7 +157,7 @@ export default {
       // 更新状态
       person.status = type === "入" ? "在营" : "外出";
 
-      // 写入日志
+      // 写入日志并添加动画效果
       this.logList.unshift({
         time,
         name: person.name,
@@ -80,7 +170,493 @@ export default {
 </script>
 
 <style scoped>
-h2 {
+/* 全局样式 */
+.app-container {
+  min-height: 100vh;
+  background: linear-gradient(135deg, #0c1624 0%, #1a2a40 100%);
+  padding: 20px;
+  color: #e3f2fd;
+  margin: 0;
+  overflow-x: hidden;
+}
+
+/* 页面标题已移除 */
+
+/* 主内容区域 */
+.content-wrapper {
+  display: flex;
+  gap: 20px;
+  height: calc(100vh - 40px);
+  width: 100%;
+  max-width: 1400px;
+  margin: 0 auto;
+  box-sizing: border-box;
+  overflow-x: hidden;
+  position: relative;
+}
+
+/* 人员区域 */
+.people-section {
+  flex: 1;
+  background: rgba(26, 42, 64, 0.8);
+  border-radius: 12px;
+  padding: 20px;
+  border: 1px solid rgba(56, 142, 255, 0.3);
+  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.3);
+  display: flex;
+  flex-direction: column;
+}
+
+.section-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
   margin-bottom: 20px;
+  padding-bottom: 10px;
+  border-bottom: 1px solid rgba(56, 142, 255, 0.3);
+}
+
+.section-title {
+  font-size: 18px;
+  font-weight: 600;
+  color: #ffffff;
+}
+
+.section-stats {
+  font-size: 14px;
+  color: #64b5f6;
+}
+
+/* 人员卡片列表 */
+.people-cards {
+  display: flex;
+  flex-direction: column;
+  gap: 12px;
+  overflow-y: auto;
+  flex: 1;
+}
+
+.people-cards::-webkit-scrollbar {
+  width: 6px;
+}
+
+.people-cards::-webkit-scrollbar-track {
+  background: rgba(26, 42, 64, 0.5);
+  border-radius: 3px;
+}
+
+.people-cards::-webkit-scrollbar-thumb {
+  background: rgba(56, 142, 255, 0.6);
+  border-radius: 3px;
+}
+
+/* 人员卡片 */
+.person-card {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  gap: 15px;
+  padding: 15px;
+  background: rgba(26, 42, 64, 0.7);
+  border-radius: 8px;
+  transition: all 0.3s ease;
+  border: 1px solid rgba(56, 142, 255, 0.2);
+}
+
+.person-card:hover {
+  transform: translateY(-2px);
+  box-shadow: 0 4px 12px rgba(56, 142, 255, 0.2);
+  background: rgba(26, 42, 64, 0.9);
+  border-color: rgba(56, 142, 255, 0.4);
+}
+
+.person-card.person-in {
+  border-color: rgba(76, 175, 80, 0.4);
+  background: rgba(76, 175, 80, 0.05);
+}
+
+.person-card.person-out {
+  border-color: rgba(244, 67, 54, 0.4);
+  background: rgba(244, 67, 54, 0.05);
+}
+
+.person-card.person-unknown {
+  border-color: rgba(56, 142, 255, 0.4);
+  background: rgba(56, 142, 255, 0.05);
+}
+
+/* 人员头像 */
+.person-avatar {
+  width: 50px;
+  height: 50px;
+  border-radius: 50%;
+  background: linear-gradient(45deg, #1976d2, #42a5f5);
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  font-size: 20px;
+  font-weight: 600;
+  color: white;
+  box-shadow: 0 2px 10px rgba(25, 118, 210, 0.4);
+  flex-shrink: 0;
+}
+
+/* 人员信息 */
+.person-info {
+  flex: 1;
+}
+
+.person-name {
+  font-size: 16px;
+  font-weight: 600;
+  color: #ffffff;
+  margin-bottom: 3px;
+}
+
+.person-id,
+.person-dept {
+  font-size: 13px;
+  color: #90caf9;
+  margin-bottom: 2px;
+}
+
+.person-status {
+  margin-top: 5px;
+}
+
+/* 人员操作按钮 */
+.person-actions {
+  display: flex;
+  flex-direction: column;
+  gap: 6px;
+  align-items: flex-end;
+  flex-shrink: 0;
+}
+
+.el-button {
+  transition: all 0.3s ease;
+  min-width: 80px;
+}
+
+.el-button:hover:not(:disabled) {
+  transform: translateY(-1px);
+  box-shadow: 0 2px 8px rgba(56, 142, 255, 0.3);
+}
+
+.el-button:disabled {
+  opacity: 0.5;
+  cursor: not-allowed;
+}
+
+/* 日志区域 */
+.log-section {
+  flex: 2;
+  width: 100%;
+  box-sizing: border-box;
+  min-width: 0; /* 防止flex子元素溢出 */
+}
+
+.log-card {
+  height: 100%;
+  background: rgba(26, 42, 64, 0.8);
+  border: 1px solid rgba(56, 142, 255, 0.3);
+  border-radius: 12px;
+  overflow: hidden;
+  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.3);
+  margin: 0 !important;
+  padding: 0 !important;
+}
+
+.log-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  background: rgba(26, 42, 64, 0.95);
+  padding: 15px 20px;
+  border-bottom: 1px solid rgba(56, 142, 255, 0.3);
+}
+
+.log-title {
+  font-size: 16px;
+  font-weight: 600;
+  color: #ffffff;
+  display: flex;
+  align-items: center;
+  gap: 8px;
+}
+
+.log-stats {
+  font-size: 14px;
+  color: #64b5f6;
+}
+
+/* 日志表格 */
+.log-table-wrapper {
+  height: calc(100% - 60px);
+  overflow: hidden;
+  display: flex;
+  flex-direction: column;
+  width: 100%;
+  box-sizing: border-box;
+}
+
+.log-table {
+  background: transparent;
+  flex: 1;
+  overflow: hidden;
+  width: 100% !important;
+  box-sizing: border-box;
+}
+
+.log-table .el-table__header {
+  background: rgba(26, 42, 64, 0.95);
+}
+
+.log-table .el-table__header th {
+  background: transparent !important;
+  color: #e3f2fd !important;
+  border-bottom: 1px solid rgba(56, 142, 255, 0.3) !important;
+  font-weight: 600 !important;
+  padding: 12px 10px !important;
+}
+
+.log-table .el-table__body-wrapper {
+  overflow-y: auto;
+  height: calc(100% - 40px);
+  width: 100% !important;
+}
+
+.log-table .el-table__body-wrapper::-webkit-scrollbar {
+  width: 6px;
+  height: 6px;
+}
+
+.log-table .el-table__body-wrapper::-webkit-scrollbar-track {
+  background: rgba(26, 42, 64, 0.5);
+  border-radius: 3px;
+}
+
+.log-table .el-table__body-wrapper::-webkit-scrollbar-thumb {
+  background: rgba(56, 142, 255, 0.6);
+  border-radius: 3px;
+}
+
+.log-row {
+  transition: all 0.2s ease;
+  background: rgba(26, 42, 64, 0.7) !important;
+}
+
+.log-row:hover {
+  background: rgba(26, 42, 64, 0.9) !important;
+}
+
+.log-table .el-table__row td {
+  border-bottom: 1px solid rgba(56, 142, 255, 0.2) !important;
+  color: #e3f2fd !important;
+  padding: 12px 10px !important;
+}
+
+.log-name {
+  font-weight: 600;
+  color: #ffffff;
+}
+
+.action-tag {
+  font-weight: 600;
+}
+
+/* 空状态 */
+.empty-log {
+  height: 200px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+
+.el-empty {
+  color: #64b5f6;
+}
+
+/* 确保Element UI组件正确适配 */
+.el-table,
+.log-table {
+  --el-table-bg-color: transparent !important;
+  --el-table-header-bg-color: transparent !important;
+  --el-table-row-hover-bg-color: rgba(26, 42, 64, 0.9) !important;
+  --el-table-border-color: rgba(56, 142, 255, 0.2) !important;
+  background-color: transparent !important;
+  width: 100% !important;
+  overflow-x: hidden !important; /* 防止表格溢出 */
+}
+
+/* 表格容器宽度确保 - 加强版 */
+.log-section,
+.log-card,
+.log-table-wrapper,
+.el-table,
+.log-table,
+.el-table__inner-wrapper,
+.el-table__body-wrapper,
+.el-table__header-wrapper,
+.el-table__container {
+  width: 100% !important;
+  min-width: 0 !important;
+  max-width: 100% !important;
+  overflow-x: hidden !important;
+  box-sizing: border-box !important;
+  position: relative;
+}
+
+/* 强制表格列自适应 */
+.el-table th.gutter,
+.el-table td.gutter {
+  width: 0 !important;
+  padding: 0 !important;
+}
+
+/* 防止表格内容溢出 */
+.el-table th,
+.el-table td {
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  box-sizing: border-box !important;
+  padding: 12px 8px !important; /* 减少内边距 */
+  max-width: 150px; /* 限制单元格最大宽度 */
+}
+
+/* 确保没有额外的内边距影响宽度 */
+.log-card .el-card__body {
+  padding: 0 !important;
+  margin: 0 !important;
+}
+
+/* 确保表格行宽度 */
+.el-table__row {
+  width: 100% !important;
+  box-sizing: border-box !important;
+}
+
+/* 表头样式强制覆盖 */
+.el-table th,
+.el-table thead th,
+.log-table th,
+.log-table thead th {
+  background-color: rgba(26, 42, 64, 0.95) !important;
+  color: #e3f2fd !important;
+  border-bottom: 1px solid rgba(56, 142, 255, 0.3) !important;
+  font-weight: 600 !important;
+  padding: 12px 10px !important;
+}
+
+/* 表格行样式强制覆盖 */
+.el-table td,
+.el-table tr,
+.log-table td,
+.log-table tr {
+  background-color: transparent !important;
+  border-bottom: 1px solid rgba(56, 142, 255, 0.2) !important;
+  color: #e3f2fd !important;
+}
+
+/* 确保行背景色 */
+.el-table__row {
+  background-color: rgba(26, 42, 64, 0.7) !important;
+}
+
+.el-table__row:hover {
+  background-color: rgba(26, 42, 64, 0.9) !important;
+}
+
+/* 确保卡片无边距 */
+.el-card__body {
+  padding: 0 !important;
+  margin: 0 !important;
+  height: 100%;
+}
+
+/* 移除表格默认边框 */
+.el-table--border,
+.el-table--group {
+  border: none !important;
+}
+
+.el-table--border th,
+.el-table--group th,
+.el-table--border td,
+.el-table--group td {
+  border-right: none !important;
+  border-left: none !important;
+}
+
+/* 确保表格内容区域样式 */
+.el-table__inner-wrapper {
+  overflow-x: hidden !important;
+}
+
+/* 确保标签样式 */
+.el-tag {
+  border-radius: 4px;
+  padding: 0 8px;
+  height: 24px;
+  line-height: 22px;
+}
+
+/* 响应式设计 */
+@media (max-width: 768px) {
+  .content-wrapper {
+    flex-direction: column;
+    height: auto;
+  }
+  
+  .people-section,
+  .log-section {
+    height: auto;
+    min-height: 400px;
+  }
+}
+
+/* 针对不同屏幕宽度的优化 */
+@media (max-width: 1200px) {
+  .content-wrapper {
+    padding: 0;
+    gap: 15px;
+  }
+  
+  .el-table th,
+  .el-table td {
+    padding: 10px 6px !important;
+    font-size: 13px;
+  }
+  
+  .people-section {
+    flex: 0.8;
+  }
+}
+
+/* 动画效果 */
+@keyframes fadeIn {
+  from {
+    opacity: 0;
+    transform: translateY(-20px);
+  }
+  to {
+    opacity: 1;
+    transform: translateY(0);
+  }
+}
+
+/* 响应式设计 */
+@media (max-width: 768px) {
+  .content-wrapper {
+    flex-direction: column;
+    height: auto;
+  }
+  
+  .people-section,
+  .log-section {
+    height: auto;
+    min-height: 400px;
+  }
 }
 </style>

+ 72 - 61
src/views/pt.vue

@@ -811,6 +811,7 @@
     </el-dialog>
   </div>
 </template>
+
 <script>
 import {
   printTrigger,
@@ -834,6 +835,8 @@ import {
 import { getUserProfile } from "@/api/system/user";
 import Cookies from "js-cookie";
 import axios from "axios";
+import WebSocketService from "@/utils/WebSocketService";
+
 export default {
   dicts: ["sys_notice_status", "sys_notice_type"],
   data() {
@@ -847,13 +850,12 @@ export default {
       timer2: null,
       open5: false,
       ggflg: false,
-      form: "",
-      rules: "",
       title: "",
       title2: "",
       title3: "",
       title4: "",
       url: process.env.VUE_APP_BASE_API,
+      form: "",
       rules: {
         noticeTitle: [
           { required: true, message: "公告标题不能为空", trigger: "blur" },
@@ -862,8 +864,6 @@ export default {
           { required: true, message: "公告类型不能为空", trigger: "change" },
         ],
       },
-      // 表单参数
-      form: {},
       noticeList: [],
       worklist: [],
       // 用户信息
@@ -935,18 +935,29 @@ export default {
     },
     // 退出
     async logout() {
-      this.$confirm("确定注销并退出系统吗?", "提示", {
-        confirmButtonText: "确定",
-        cancelButtonText: "取消",
-        type: "warning",
-      })
-        .then(() => {
-          this.$store.dispatch("LogOut").then(() => {
-            location.href = "/index";
-            Cookies.remove("tixing");
-          });
-        })
-        .catch(() => {});
+      try {
+        await this.$confirm("确定注销并退出系统吗?", "提示", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning",
+        });
+
+        // 调用 Vuex 退出登录
+        await this.$store.dispatch("LogOut");
+
+        // 清理本地数据
+        Cookies.remove("tixing");
+
+        // 断开 WebSocket(如果存在)
+        if (this.$store.getters.webSocketState === 'open') {
+          WebSocketService.close();
+        }
+
+        // 跳转登录页
+        location.href = "/index";
+      } catch (err) {
+        // 用户取消操作,不处理
+      }
     },
     // 手机柜线S地址console
     xianShang() {
@@ -1368,7 +1379,7 @@ li {
   height: 100%;
   width: 100%;
   overflow: auto;
-  background: url("../images/背景1.png") no-repeat center;
+  background: url("~@/images/背景1.png") no-repeat center;
   background-size: 100% 100%;
   position: relative;
 }
@@ -1376,7 +1387,7 @@ li {
   color: #fff;
   position: relative;
   height: 120px;
-  background: url("../assets/images/首页顶部.gif") no-repeat center;
+  background: url("~@/assets/images/首页顶部.gif") no-repeat center;
   background-size: cover;
 }
 .tou .yong_hu {
@@ -1410,7 +1421,7 @@ li {
   color: #fff;
   width: 107px;
   height: 95%;
-  background: url("../images/tx.png") no-repeat;
+  background: url("~@/images/tx.png") no-repeat;
   text-align: center;
   margin-top: 10%;
   position: relative;
@@ -1483,7 +1494,7 @@ hr {
   color: #fff;
   width: 529px;
   height: 41vh;
-  background: url("../images/底3.png") no-repeat;
+  background: url("~@/images/底3.png") no-repeat;
   margin-bottom: 10px;
   padding: 16px 20px 0px 20px;
   background-size: 100% 100%;
@@ -1780,7 +1791,7 @@ hr {
 .jiben {
   width: 760px;
   height: 32px;
-  background-image: url("../images/小标题底.png");
+  background-image: url("~@/images/小标题底.png");
   margin-bottom: 25px;
   color: #fff;
   padding-left: 16px;
@@ -1852,7 +1863,7 @@ hr {
 .tkbox {
   width: 500px;
   height: 220px;
-  background-image: url("../assets/images/框.png");
+  background-image: url("~@/assets/images/框.png");
   background-size: 100% 100%;
   background-repeat: no-repeat;
   position: absolute;
@@ -1863,7 +1874,7 @@ hr {
 .tkbox2 {
   width: 500px;
   height: 220px;
-  background-image: url("../assets/images/框.png");
+  background-image: url("~@/assets/images/框.png");
   background-size: 100% 100%;
   background-repeat: no-repeat;
   position: absolute;
@@ -1874,7 +1885,7 @@ hr {
 .tkbox3 {
   width: 500px;
   height: 220px;
-  background-image: url("../assets/images/框.png");
+  background-image: url("~@/assets/images/框.png");
   background-size: 100% 100%;
   background-repeat: no-repeat;
   position: absolute;
@@ -1885,7 +1896,7 @@ hr {
 .tkbox4 {
   width: 500px;
   height: 220px;
-  background-image: url("../assets/images/框.png");
+  background-image: url("~@/assets/images/框.png");
   background-size: 100% 100%;
   background-repeat: no-repeat;
   position: absolute;
@@ -2006,114 +2017,114 @@ hr {
   width: 30px;
   height: 30px;
   top: 6.5%;
-  background: url(../images/无光_00000.png) no-repeat;
+  background: url(~@/images/无光_00000.png) no-repeat;
   animation: mymove 2s 0.5s infinite linear alternate forwards;
 }
 @keyframes mymove {
   0% {
-    background: url(../images/无光_00000.png) no-repeat;
+    background: url(~@/images/无光_00000.png) no-repeat;
   }
   3% {
-    background: url(../images/无光_00001.png) no-repeat;
+    background: url(~@/images/无光_00001.png) no-repeat;
   }
   6% {
-    background: url(../images/无光_00002.png) no-repeat;
+    background: url(~@/images/无光_00002.png) no-repeat;
   }
   9% {
-    background: url(../images/无光_00003.png) no-repeat;
+    background: url(~@/images/无光_00003.png) no-repeat;
   }
   12% {
-    background: url(../images/无光_00004.png) no-repeat;
+    background: url(~@/images/无光_00004.png) no-repeat;
   }
   15% {
-    background: url(../images/无光_00005.png) no-repeat;
+    background: url(~@/images/无光_00005.png) no-repeat;
   }
   18% {
-    background: url(../images/无光_00006.png) no-repeat;
+    background: url(~@/images/无光_00006.png) no-repeat;
   }
   21% {
-    background: url(../images/无光_00007.png) no-repeat;
+    background: url(~@/images/无光_00007.png) no-repeat;
   }
   24% {
-    background: url(../images/无光_00008.png) no-repeat;
+    background: url(~@/images/无光_00008.png) no-repeat;
   }
   27% {
-    background: url(../images/无光_00009.png) no-repeat;
+    background: url(~@/images/无光_00009.png) no-repeat;
   }
   30% {
-    background: url(../images/无光_00010.png) no-repeat;
+    background: url(~@/images/无光_00010.png) no-repeat;
   }
   33% {
-    background: url(../images/无光_00011.png) no-repeat;
+    background: url(~@/images/无光_00011.png) no-repeat;
   }
   36% {
-    background: url(../images/无光_00012.png) no-repeat;
+    background: url(~@/images/无光_00012.png) no-repeat;
   }
   39% {
-    background: url(../images/无光_00013.png) no-repeat;
+    background: url(~@/images/无光_00013.png) no-repeat;
   }
   42% {
-    background: url(../images/无光_00014.png) no-repeat;
+    background: url(~@/images/无光_00014.png) no-repeat;
   }
   45% {
-    background: url(../images/无光_00015.png) no-repeat;
+    background: url(~@/images/无光_00015.png) no-repeat;
   }
   48% {
-    background: url(../images/无光_00016.png) no-repeat;
+    background: url(~@/images/无光_00016.png) no-repeat;
   }
   51% {
-    background: url(../images/无光_00017.png) no-repeat;
+    background: url(~@/images/无光_00017.png) no-repeat;
   }
   54% {
-    background: url(../images/无光_00018.png) no-repeat;
+    background: url(~@/images/无光_00018.png) no-repeat;
   }
   57% {
-    background: url(../images/无光_00019.png) no-repeat;
+    background: url(~@/images/无光_00019.png) no-repeat;
   }
   60% {
-    background: url(../images/无光_00020.png) no-repeat;
+    background: url(~@/images/无光_00020.png) no-repeat;
   }
   63% {
-    background: url(../images/无光_00021.png) no-repeat;
+    background: url(~@/images/无光_00021.png) no-repeat;
   }
   66% {
-    background: url(../images/无光_00022.png) no-repeat;
+    background: url(~@/images/无光_00022.png) no-repeat;
   }
   69% {
-    background: url(../images/无光_00023.png) no-repeat;
+    background: url(~@/images/无光_00023.png) no-repeat;
   }
   72% {
-    background: url(../images/无光_00024.png) no-repeat;
+    background: url(~@/images/无光_00024.png) no-repeat;
   }
   75% {
-    background: url(../images/无光_00025.png) no-repeat;
+    background: url(~@/images/无光_00025.png) no-repeat;
   }
   78% {
-    background: url(../images/无光_00026.png) no-repeat;
+    background: url(~@/images/无光_00026.png) no-repeat;
   }
   81% {
-    background: url(../images/无光_00027.png) no-repeat;
+    background: url(~@/images/无光_00027.png) no-repeat;
   }
   84% {
-    background: url(../images/无光_00028.png) no-repeat;
+    background: url(~@/images/无光_00028.png) no-repeat;
   }
   87% {
-    background: url(../images/无光_00029.png) no-repeat;
+    background: url(~@/images/无光_00029.png) no-repeat;
   }
   90% {
-    background: url(../images/无光_00030.png) no-repeat;
+    background: url(~@/images/无光_00030.png) no-repeat;
   }
   92% {
-    background: url(../images/无光_00031.png) no-repeat;
+    background: url(~@/images/无光_00031.png) no-repeat;
   }
   95% {
-    background: url(../images/无光_00032.png) no-repeat;
+    background: url(~@/images/无光_00032.png) no-repeat;
   }
   98% {
-    background: url(../images/无光_00033.png) no-repeat;
+    background: url(~@/images/无光_00033.png) no-repeat;
   }
   100% {
-    background: url(../images/无光_00034.png) no-repeat;
+    background: url(~@/images/无光_00034.png) no-repeat;
   }
 }
 </style>

部分文件因文件數量過多而無法顯示