| | |
| | | import "./assets/icons"; |
| | | import "./permission"; |
| | | import { getDicts } from "@/api/system/dict/data"; |
| | | import { getConfigKey } from "@/api/system/config"; |
| | | import { getConfigKey, yidu } from "@/api/system/config"; |
| | | import { |
| | | parseTime, |
| | | resetForm, |
| | |
| | | import DictData from "@/components/DictData"; |
| | | import * as echarts from "echarts"; |
| | | import VueBarcode from "vue-barcode"; |
| | | import { initWebSocket, closeWebSocket } from "@/utils/websocket"; |
| | | import RightToolbar from "@/components/RightToolbar" |
| | | |
| | | // 注册全局组件 |
| | | Vue.component("downloadExcel", JsonExcel); |
| | | Vue.component("barcode", VueBarcode); |
| | | Vue.component("DictTag", DictTag); |
| | |
| | | Vue.component("FileUpload", FileUpload); |
| | | Vue.component("ImageUpload", ImageUpload); |
| | | Vue.component("ImagePreview", ImagePreview); |
| | | Vue.component("RightToolbar", RightToolbar); |
| | | |
| | | // 注册全局方法 |
| | | Vue.prototype.getDicts = getDicts; |
| | | Vue.prototype.getConfigKey = getConfigKey; |
| | | Vue.prototype.parseTime = parseTime; |
| | |
| | | Vue.prototype.handleTree = handleTree; |
| | | Vue.prototype.$echarts = echarts; |
| | | |
| | | // 保留 $showNotification,支持手动触发通知 |
| | | Vue.prototype.$showNotification = function (type, title, message, onClick) { |
| | | console.log('触发通知:', { type, title, message }, new Date().toLocaleString()); |
| | | Vue.prototype.$notify({ |
| | | title, |
| | | message, |
| | | // 通知管理:跟踪当前通知和偏移量 |
| | | const notificationManager = { |
| | | notifications: [], // 存储当前显示的通知实例 |
| | | baseOffset: 50, // 基础偏移量 |
| | | notificationHeight: 80, // 每个通知的估计高度(包括间距) |
| | | maxNotifications: 5, // 最大同时显示的通知数量 |
| | | addNotification(notification) { |
| | | if (this.notifications.length >= this.maxNotifications) { |
| | | // 关闭最早的通知 |
| | | const oldest = this.notifications.shift(); |
| | | oldest.close(); |
| | | } |
| | | this.notifications.push(notification); |
| | | // 设置动态 offset 和 z-index |
| | | notification.offset = this.baseOffset + this.notifications.length * this.notificationHeight; |
| | | notification.customClass += ` notification-${this.notifications.length}`; // 为 z-index 添加唯一类 |
| | | // 监听通知关闭 |
| | | notification.onClose = () => { |
| | | const index = this.notifications.indexOf(notification); |
| | | if (index > -1) { |
| | | this.notifications.splice(index, 1); |
| | | // 更新后续通知的 offset |
| | | this.updateOffsets(); |
| | | } |
| | | }; |
| | | }, |
| | | updateOffsets() { |
| | | this.notifications.forEach((notification, index) => { |
| | | notification.offset = this.baseOffset + (index + 1) * this.notificationHeight; |
| | | notification.customClass = notification.customClass.replace(/notification-\d+/, `notification-${index + 1}`); |
| | | }); |
| | | } |
| | | }; |
| | | |
| | | // 全局通知方法,添加“已读”按钮 |
| | | Vue.prototype.$showNotification = function (type, title, message, onClick, noticeId) { |
| | | console.log('触发通知:', { type, title, message, noticeId, noticeIdType: typeof noticeId }); // 调试:记录 noticeId 和类型 |
| | | const h = this.$createElement; |
| | | const notification = this.$notify({ |
| | | title: title, |
| | | message: h('div', { class: 'notification-content' }, [ |
| | | h('div', { class: 'notification-message' }, message), |
| | | noticeId ? h('el-button', { |
| | | class: 'read-button', |
| | | style: { marginLeft: '10px', float: 'right', cursor: 'pointer' }, |
| | | props: { type: 'primary', size: 'mini' }, |
| | | on: { |
| | | click: async () => { |
| | | console.log('点击“已读”按钮,noticeId:', noticeId, 'type:', typeof noticeId); // 调试:记录点击时的 noticeId |
| | | try { |
| | | await yidu({ noticeId: String(noticeId) }); |
| | | console.log(`通知 ${noticeId} 已标记为已读`); |
| | | this.$message.success('标记为已读成功'); |
| | | notification.close(); |
| | | } catch (error) { |
| | | console.error('标记已读失败:', error, 'noticeId:', noticeId); |
| | | this.$message.error('标记已读失败'); |
| | | } |
| | | } |
| | | } |
| | | }, '已读') : null |
| | | ]), |
| | | type, |
| | | duration: 5000, |
| | | position: 'top-right', |
| | | offset: 50, |
| | | onClick, |
| | | offset: notificationManager.baseOffset, // 初始 offset |
| | | onClick: null, // 不跳转 |
| | | customClass: 'global-notification', |
| | | dangerouslyUseHTMLString: false, |
| | | appendTo: document.body |
| | | }); |
| | | notificationManager.addNotification(notification); // 添加到通知管理 |
| | | }; |
| | | |
| | | // 监听路由变化 |
| | |
| | | console.log('路由切换完成,当前路径:', router.currentRoute.path); |
| | | }); |
| | | |
| | | // 定义 WebSocket 初始化标志,防止重复连接 |
| | | let isWebSocketInitialized = false; |
| | | |
| | | const app = new Vue({ |
| | | el: "#app", |
| | | router, |
| | | store, |
| | | render: (h) => h(App) |
| | | render: (h) => h(App), |
| | | mounted() { |
| | | const token = store.state.user.token || Cookies.get('token') || ''; |
| | | if (token && !isWebSocketInitialized) { |
| | | console.log('初始化 WebSocket,Token:', token); |
| | | isWebSocketInitialized = true; |
| | | initWebSocket(token, (type, data) => { |
| | | console.log('WebSocket 收到消息:', { type, data }); // 调试:记录原始数据 |
| | | if (type === 'error') { |
| | | Vue.prototype.$showNotification.call(this, 'error', '错误', data); |
| | | return; |
| | | } |
| | | try { |
| | | if (typeof data === 'string' && data.trim().startsWith('{')) { |
| | | // 替换大整数字段,防止精度丢失 |
| | | const normalizedData = data.replace(/"(noticeId|notice_id|id)":\s*(\d+)/g, '"$1":"$2"'); |
| | | const message = JSON.parse(normalizedData); |
| | | console.log('WebSocket 解析后消息:', message); // 调试:记录解析后的消息 |
| | | if (message.noticeId || message.notice_id || message.id) { |
| | | const noticeTypeLabel = message.noticeType === '1' ? '通知' : '公告'; |
| | | const noticeTitle = message.noticeTitle ? message.noticeTitle.replace(/<[^>]+>/g, '') : '无标题'; |
| | | const contentPreview = message.noticeContent |
| | | ? message.noticeContent.replace(/<[^>]+>/g, '').substring(0, 20) + '...' |
| | | : '无内容'; |
| | | const noticeId = String(message.noticeId || message.notice_id || message.id); |
| | | console.log('准备触发通知,noticeId:', noticeId, 'type:', typeof noticeId); // 调试:记录传递的 noticeId |
| | | Vue.prototype.$showNotification.call( |
| | | this, |
| | | 'success', |
| | | `新${noticeTypeLabel}`, |
| | | `${noticeTitle} - ${contentPreview}`, |
| | | null, |
| | | noticeId |
| | | ); |
| | | } else { |
| | | console.log('未知消息类型:', message); |
| | | Vue.prototype.$showNotification.call(this, 'info', '消息', '收到未知格式的消息'); |
| | | } |
| | | } else { |
| | | console.log('WebSocket 非 JSON 消息:', data); |
| | | |
| | | } |
| | | } catch (error) { |
| | | console.error('消息解析失败:', error, '原始数据:', data); |
| | | Vue.prototype.$showNotification.call(this, 'error', '消息解析失败', `服务器回应字符串: ${data}`); |
| | | } |
| | | }); |
| | | } else if (!token) { |
| | | console.error('未找到 token,无法初始化 WebSocket'); |
| | | } else { |
| | | console.log('WebSocket 已初始化,跳过重复连接'); |
| | | } |
| | | }, |
| | | beforeDestroy() { |
| | | if (isWebSocketInitialized) { |
| | | closeWebSocket(); |
| | | isWebSocketInitialized = false; |
| | | console.log('Vue 实例销毁,WebSocket 已清理'); |
| | | } |
| | | } |
| | | }); |
| | | |
| | | // 注册插件 |
| | | Vue.use(directive); |
| | | Vue.use(plugins); |
| | | Vue.use(VueMeta); |