qx
6 天以前 59dd5c96e8bca3341d1166a9a350f0731436817b
src/main.js
@@ -1,42 +1,23 @@
// src/main.js
import Vue from "vue";
import Cookies from "js-cookie";
import "babel-polyfill";
import Element from "element-ui";
import "./assets/styles/element-variables.scss";
import "@/assets/styles/index.scss"; // global css
import "@/assets/styles/ruoyi.scss"; // ruoyi css
import "@/assets/styles/index.scss";
import "@/assets/styles/ruoyi.scss";
import App from "./App";
import store from "./store";
import router from "./router";
import directive from "./directive"; // directive
import plugins from "./plugins"; // plugins
import directive from "./directive";
import plugins from "./plugins";
import { download } from "@/utils/request";
import Print from "vue-print-nb";
import Updater from "./utils/AutoUpdate.js";
//前端重新部署通知用户刷新网页
const AutoUpdate = new Updater()
AutoUpdate.on('update',()=>{
  setTimeout(async()=>{
      const result = confirm('当前版本已更新,请点击确定刷新页面体验');
      if(result){
        location.reload();
      }
  },500)
})
import JsonExcel from "vue-json-excel";
Vue.component("downloadExcel", JsonExcel);
import "./assets/icons"; // icon
import "./permission"; // permission control
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,
@@ -45,38 +26,31 @@
  selectDictLabels,
  handleTree,
} from "@/utils/ruoyi";
// 分页组件
import Pagination from "@/components/Pagination";
// 分页组件
// import { monitorZoom } from "@/utils/devicePixelRatio.js";
// const m = monitorZoom();
// if (window.screen.width * window.devicePixelRatio >= 3840) {
//   document.body.style.zoom = 100 / (Number(m) / 2); // 屏幕为 4k 时
// } else {
//   document.body.style.zoom = 100 / Number(m);
// }
// 自定义表格工具组件
import RightToolbar from "@/components/RightToolbar";
// 富文本组件
import Editor from "@/components/Editor";
// 文件上传组件
import FileUpload from "@/components/FileUpload";
// 图片上传组件
import ImageUpload from "@/components/ImageUpload";
// 图片预览组件
import ImagePreview from "@/components/ImagePreview";
// 字典标签组件
import DictTag from "@/components/DictTag";
// 头部标签组件
import VueMeta from "vue-meta";
// 字典数据组件
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("Pagination", Pagination);
Vue.component("Editor", Editor);
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;
@@ -87,42 +61,163 @@
Vue.prototype.download = download;
Vue.prototype.handleTree = handleTree;
Vue.prototype.$echarts = echarts;
import VueBarcode from "vue-barcode";
Vue.component("barcode", VueBarcode);
// 全局组件挂载
Vue.component("DictTag", DictTag);
Vue.component("Pagination", Pagination);
Vue.component("RightToolbar", RightToolbar);
Vue.component("Editor", Editor);
Vue.component("FileUpload", FileUpload);
Vue.component("ImageUpload", ImageUpload);
Vue.component("ImagePreview", ImagePreview);
// 通知管理:跟踪当前通知和偏移量
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.use(directive);
Vue.use(plugins);
Vue.use(VueMeta);
Vue.use(Print);
DictData.install();
// 全局通知方法,添加“已读”按钮
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: notificationManager.baseOffset, // 初始 offset
    onClick: null, // 不跳转
    customClass: 'global-notification',
    dangerouslyUseHTMLString: false,
    appendTo: document.body
  });
  notificationManager.addNotification(notification); // 添加到通知管理
};
/**
 * If you don't want to use mock-server
 * you want to use MockJs for mock api
 * you can execute: mockXHR()
 *
 * Currently MockJs will be used in the production environment,
 * please remove it before going online! ! !
 */
Vue.use(Element, {
  size: Cookies.get("size") || "medium", // set element-ui default size
// 监听路由变化
router.afterEach(() => {
  console.log('路由切换完成,当前路径:', router.currentRoute.path);
});
Vue.config.productionTip = false;
// 定义 WebSocket 初始化标志,防止重复连接
let isWebSocketInitialized = false;
new Vue({
const app = new Vue({
  el: "#app",
  router,
  store,
  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);
Vue.use(Print);
Vue.use(Element, {
  size: Cookies.get("size") || "medium",
});
DictData.install();
Vue.config.productionTip = false;