From 0000e935d6c7f74cb6682aea1bbf24d8deade390 Mon Sep 17 00:00:00 2001 From: wwl <xchao828@163.com> Date: 星期四, 03 七月 2025 17:49:18 +0800 Subject: [PATCH] 1 --- src/views/index.vue | 198 +++++++++++++-------- src/assets/styles/index.scss | 38 +++ src/main.js | 55 ++++-- src/main copy.js | 142 +++++++++++++++ src/utils/websocket.js | 104 +++++----- 5 files changed, 384 insertions(+), 153 deletions(-) diff --git a/src/assets/styles/index.scss b/src/assets/styles/index.scss index 37d9dfd..deb1889 100644 --- a/src/assets/styles/index.scss +++ b/src/assets/styles/index.scss @@ -1,3 +1,4 @@ +/* src/assets/styles/index.scss */ @import './variables.scss'; @import './mixin.scss'; @import './transition.scss'; @@ -119,7 +120,7 @@ } } -//main-container鍏ㄥ眬鏍峰紡 +// main-container鍏ㄥ眬鏍峰紡 .app-container { padding: 20px; width: 100%; @@ -135,7 +136,7 @@ } .text-center { - text-align: center + text-align: center; } .sub-navbar { @@ -182,7 +183,7 @@ } } -//refine vue-multiselect plugin +// refine vue-multiselect plugin .multiselect { line-height: 16px; } @@ -190,3 +191,34 @@ .multiselect--active { z-index: 1000 !important; } + +/* 鑷畾涔� Element UI 閫氱煡鏍峰紡 */ +.el-notification { + min-width: 300px !important; + background-color: #f0f9eb !important; + border-color: #e1f3d8 !important; + color: #67c23a !important; + font-size: 16px !important; + padding: 15px 20px !important; + z-index: 10000 !important; /* 鎻愰珮 z-index锛岃秴杩� Layout 鐨� drawer-bg */ + position: fixed !important; + top: 50px !important; + right: 20px !important; +} + +.el-notification.global-notification { + z-index: 10000 !important; /* 璋冭瘯鐢紝纭繚涓嶈閬尅 */ +} + +.el-notification__title { + font-weight: bold !important; +} + +.el-notification__content { + font-size: 14px !important; + color: #333 !important; +} + +.el-notification__closeBtn { + color: #999 !important; +} \ No newline at end of file diff --git a/src/main copy.js b/src/main copy.js new file mode 100644 index 0000000..30d4cab --- /dev/null +++ b/src/main copy.js @@ -0,0 +1,142 @@ +// 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"; +import "@/assets/styles/ruoyi.scss"; +import App from "./App"; +import store from "./store"; +import router from "./router"; +import directive from "./directive"; +import plugins from "./plugins"; +import { download } from "@/utils/request"; +import Print from "vue-print-nb"; +import JsonExcel from "vue-json-excel"; +import "./assets/icons"; +import "./permission"; +import { getDicts } from "@/api/system/dict/data"; +import { getConfigKey } from "@/api/system/config"; +import { + parseTime, + resetForm, + addDateRange, + selectDictLabel, + selectDictLabels, + handleTree, +} from "@/utils/ruoyi"; +import Pagination from "@/components/Pagination"; +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"; + +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.prototype.getDicts = getDicts; +Vue.prototype.getConfigKey = getConfigKey; +Vue.prototype.parseTime = parseTime; +Vue.prototype.resetForm = resetForm; +Vue.prototype.addDateRange = addDateRange; +Vue.prototype.selectDictLabel = selectDictLabel; +Vue.prototype.selectDictLabels = selectDictLabels; +Vue.prototype.download = download; +Vue.prototype.handleTree = handleTree; +Vue.prototype.$echarts = echarts; + +Vue.prototype.$showNotification = function (type, title, message, onClick) { + console.log('瑙﹀彂閫氱煡:', { type, title, message }); + Vue.prototype.$notify({ + title, + message, + type, + duration: 5000, + position: 'top-right', + offset: 50, + onClick, + customClass: 'global-notification', + appendTo: document.body + }); +}; + +// 鐩戝惉璺敱鍙樺寲 +router.afterEach(() => { + console.log('璺敱鍒囨崲瀹屾垚锛屽綋鍓嶈矾寰�:', router.currentRoute.path); +}); + +const app = new Vue({ + el: "#app", + router, + store, + render: (h) => h(App), + mounted() { + const token = store.state.user.token || Cookies.get('token') || ''; + if (token) { + console.log('Token:', token); + initWebSocket(token, (type, data) => { + if (type === 'error') { + Vue.prototype.$showNotification('error', '閿欒', data); + return; + } + try { + const message = JSON.parse(data); + console.log('WebSocket 瑙f瀽鍚庢秷鎭�:', message); + if (message.noticeId && message.noticeTitle) { + const noticeTypeLabel = message.noticeType === '1' ? '閫氱煡' : '鍏憡'; + const contentPreview = message.noticeContent + ? message.noticeContent.replace(/<[^>]+>/g, '').substring(0, 20) + '...' + : '鏃犲唴瀹�'; + Vue.prototype.$showNotification( + 'success', + `鏂�${noticeTypeLabel}`, + `${message.noticeTitle} - ${contentPreview}`, + () => { + + router.push({ + path: '/redirect/notice', + query: { noticeId: message.noticeId } + }); + } + ); + } else { + console.log('鏈煡娑堟伅绫诲瀷:', message); + Vue.prototype.$showNotification('info', '娑堟伅', '鏀跺埌鏈煡鏍煎紡鐨勬秷鎭�'); + } + } catch (error) { + console.error('娑堟伅瑙f瀽澶辫触:', error, '鍘熷鏁版嵁:', data); + Vue.prototype.$showNotification('info', '娑堟伅', `鏈嶅姟鍣ㄥ洖搴斿瓧绗︿覆: ${data}`); + } + }); + } else { + console.error('鏈壘鍒� token锛屾棤娉曞垵濮嬪寲 WebSocket'); + } + }, + beforeDestroy() { + closeWebSocket(); // 娓呯悊 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; \ No newline at end of file diff --git a/src/main.js b/src/main.js index 7ee26a4..9ebec6f 100644 --- a/src/main.js +++ b/src/main.js @@ -1,20 +1,21 @@ +// 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 JsonExcel from "vue-json-excel"; -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 { @@ -26,7 +27,6 @@ handleTree, } from "@/utils/ruoyi"; import Pagination from "@/components/Pagination"; -import RightToolbar from "@/components/RightToolbar"; import Editor from "@/components/Editor"; import FileUpload from "@/components/FileUpload"; import ImageUpload from "@/components/ImageUpload"; @@ -41,13 +41,11 @@ 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); -// 鍏ㄥ眬鏂规硶鎸傝浇 Vue.prototype.getDicts = getDicts; Vue.prototype.getConfigKey = getConfigKey; Vue.prototype.parseTime = parseTime; @@ -59,20 +57,41 @@ 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, + type, + duration: 5000, + position: 'top-right', + offset: 50, + onClick, + customClass: 'global-notification', + appendTo: document.body + }); +}; + +// 鐩戝惉璺敱鍙樺寲 +router.afterEach(() => { + console.log('璺敱鍒囨崲瀹屾垚锛屽綋鍓嶈矾寰�:', router.currentRoute.path); +}); + +const app = new Vue({ + el: "#app", + router, + store, + render: (h) => h(App) +}); + Vue.use(directive); Vue.use(plugins); Vue.use(VueMeta); Vue.use(Print); Vue.use(Element, { - size: Cookies.get("size") || "medium", // set element-ui default size + size: Cookies.get("size") || "medium", }); DictData.install(); -Vue.config.productionTip = false; - -new Vue({ - el: "#app", - router, - store, - render: (h) => h(App), -}); \ No newline at end of file +Vue.config.productionTip = false; \ No newline at end of file diff --git a/src/utils/websocket.js b/src/utils/websocket.js index a525f68..40529b1 100644 --- a/src/utils/websocket.js +++ b/src/utils/websocket.js @@ -1,80 +1,78 @@ // src/utils/websocket.js -let socket = null; +let ws = null; let reconnectAttempts = 0; const maxReconnectAttempts = 5; -const reconnectInterval = 5000; -let messageCallback = null; +const reconnectInterval = 5000; // 5绉掗噸杩為棿闅� +const pingInterval = 5000; // 5绉掑彂閫乸ing -const initWebSocket = (token, callback) => { - if (socket && socket.readyState === WebSocket.OPEN) { - console.log('WebSocket 宸茶繛鎺ワ紝鏃犻渶閲嶅鍒濆鍖�'); - return; - } - +export function initWebSocket(token, onMessage) { if (!token) { - console.error('鏈彁渚� token锛屾棤娉曞缓绔� WebSocket 杩炴帴'); - callback('error', '鏈壘鍒� token锛屾棤娉曞缓绔� WebSocket 杩炴帴'); + console.error('WebSocket 鍒濆鍖栧け璐ワ細缂哄皯 token'); return; } - const wsUrl = `ws://192.168.1.2:5011/ws?token=${encodeURIComponent(token)}`; - console.log('灏濊瘯杩炴帴 WebSocket:', wsUrl); + const wsUrl = `ws://192.168.1.2:5011/ws?token=${token}`; + ws = new WebSocket(wsUrl); - try { - socket = new WebSocket(wsUrl); - } catch (error) { - console.error('WebSocket 鍒濆鍖栧け璐�:', error); - callback('error', '鏃犳硶鍒濆鍖� WebSocket 杩炴帴'); - return; - } - - messageCallback = callback; - - socket.onopen = () => { + ws.onopen = () => { console.log('WebSocket 杩炴帴鎴愬姛'); reconnectAttempts = 0; + + // 鍚姩蹇冭烦鏈哄埗 + const pingTimer = setInterval(() => { + if (ws.readyState === WebSocket.OPEN) { + console.log('鍙戦�� ping 娑堟伅'); + ws.send('ping'); + } else { + console.warn('WebSocket 鏈繛鎺ワ紝鍋滄 ping'); + clearInterval(pingTimer); + } + }, pingInterval); }; - socket.onmessage = (event) => { - console.log('WebSocket 鏀跺埌鍘熷娑堟伅:', event.data); - if (messageCallback) { - messageCallback('message', event.data); + ws.onmessage = (event) => { + const data = event.data; + console.log('WebSocket 鏀跺埌鍘熷娑堟伅:', data); + if (data === 'pong') { + console.log('鏀跺埌 pong 鍝嶅簲锛岃繛鎺ユ椿璺�'); + return; } + onMessage('message', data); }; - socket.onerror = (error) => { + ws.onerror = (error) => { console.error('WebSocket 閿欒:', error); - if (messageCallback) { - messageCallback('error', 'WebSocket 杩炴帴閿欒'); - } + onMessage('error', 'WebSocket 杩炴帴閿欒'); }; - socket.onclose = (event) => { - console.log('WebSocket 杩炴帴鍏抽棴锛屼唬鐮�:', event.code, '鍘熷洜:', event.reason); + ws.onclose = () => { + console.warn('WebSocket 杩炴帴鍏抽棴'); if (reconnectAttempts < maxReconnectAttempts) { + reconnectAttempts++; + console.log(`灏濊瘯閲嶈繛 (${reconnectAttempts}/${maxReconnectAttempts})...`); setTimeout(() => { - console.log(`灏濊瘯閲嶈繛 (${reconnectAttempts + 1}/${maxReconnectAttempts})`); - reconnectAttempts++; - initWebSocket(token, callback); + initWebSocket(token, onMessage); }, reconnectInterval); } else { - if (messageCallback) { - messageCallback('error', 'WebSocket 閲嶈繛澶辫触锛岃妫�鏌ョ綉缁滄垨鏈嶅姟鍣�'); - } + console.error('杈惧埌鏈�澶ч噸杩炴鏁帮紝鍋滄閲嶈繛'); + onMessage('error', 'WebSocket 杩炴帴澶辫触锛屽凡杈炬渶澶ч噸杩炴鏁�'); } }; -}; -const closeWebSocket = () => { - if (socket) { - socket.close(1000, '娴忚鍣ㄥ叧闂紝姝e父鏂紑'); - socket = null; - console.log('WebSocket 杩炴帴宸插叧闂�'); + // 娓呯悊 WebSocket + window.addEventListener('beforeunload', () => { + if (ws) { + ws.close(); + ws = null; + console.log('WebSocket 宸叉竻鐞�'); + } + }); +} + +export function closeWebSocket() { + if (ws) { + ws.close(); + ws = null; + console.log('WebSocket 宸插叧闂�'); } -}; - -window.addEventListener('beforeunload', () => { - closeWebSocket(); -}); - -export { initWebSocket, closeWebSocket }; \ No newline at end of file +} \ No newline at end of file diff --git a/src/views/index.vue b/src/views/index.vue index 268ec38..1ea2590 100644 --- a/src/views/index.vue +++ b/src/views/index.vue @@ -1,5 +1,28 @@ <template> <div class="centre"> + <!-- 浠婃棩閫氱煡 --> + <div style="display: flex; justify-content: center; align-items: center;"> + <h4>浠婃棩閫氱煡</h4> + </div> + <div class="notice-area"> + <el-carousel :interval="2000" direction="vertical" :autoplay="true" :loop="true" height="120px" + v-if="groupedNoticeList.length > 0" class="carousel"> + <el-carousel-item v-for="(group, index) in groupedNoticeList" :key="index"> + <div class="notice-group"> + <div class="notice-item" v-for="notice in group" :key="notice.noticeId" + @click="goToNotice(notice.noticeId)"> + <el-tag size="small" :type="notice.noticeType === '1' ? 'info' : 'warning'"> + {{ notice.noticeType === '1' ? '閫氱煡' : '鍏憡' }} + </el-tag> + <span class="notice-title">{{ notice.noticeTitle || '鏃犳爣棰�' }}</span> + <span class="notice-time">{{ parseTime(notice.createTime, '{y}-{m}-{d}') || '鏃犳椂闂�' }}</span> + </div> + </div> + </el-carousel-item> + </el-carousel> + <div v-else class="no-notice">鏆傛棤閫氱煡</div> + </div> + <!-- 浠婃棩缁熻 --> <div style="display: flex; justify-content: center; align-items: center;"> <h4>浠婃棩缁熻</h4> @@ -56,7 +79,6 @@ <script> import { getCustomer, getOrder, getReportToday, getTobeToday, getPieChart, getChart } from "@/api/home"; import { noticeToday } from "@/api/system/notice"; -import { initWebSocket } from "@/utils/websocket"; const echarts = require('echarts/lib/echarts'); require('echarts/lib/component/title'); require('echarts/lib/component/tooltip'); @@ -79,18 +101,27 @@ teamYYNum: [], PieChart: [], PieChart2: [], + noticeList: [], + groupedNoticeList: [], // 鍒嗙粍鍚庣殑閫氱煡鍒楄〃 loading: false }; }, created() { this.getList(); - this.initWebSocket(); }, watch: { $route(to, from) { window.location.reload(); + }, + noticeList: { + handler(newList) { + // 灏嗛�氱煡鎸夋瘡缁勪笁鏉″垎缁� + this.groupedNoticeList = this.chunkArray(newList, 3); + console.log('groupedNoticeList:', this.groupedNoticeList); // 璋冭瘯 + }, + deep: true } }, @@ -101,91 +132,49 @@ query: { noticeId } }); }, - - // 鍒濆鍖� WebSocket 杩炴帴 - initWebSocket() { - const token = this.$store.state.user.token || ''; - initWebSocket(token, (type, data) => { - if (type === 'error') { - this.$notify.error({ - title: '閿欒', - message: data, - duration: 5000, - position: 'top-right' - }); - return; - } - - try { - const message = JSON.parse(data); - console.log('WebSocket 瑙f瀽鍚庢秷鎭�:', message); - if (message.noticeId && message.noticeTitle) { - const noticeTypeLabel = message.noticeType === '1' ? '閫氱煡' : '鍏憡'; - this.$notify({ - title: `鏂�${noticeTypeLabel}`, - message: message.noticeTitle || '鏃犳爣棰�', - type: 'success', - duration: 5000, // 鎮仠 5 绉� - position: 'top-right', - offset: 50, - onClick: () => { - this.goToNotice(message.noticeId); - } - }); - } else { - console.log('鏈煡娑堟伅绫诲瀷:', message); - } - } catch (error) { - console.error('娑堟伅瑙f瀽澶辫触:', error, '鍘熷鏁版嵁:', data); - this.$notify.info({ - title: '娑堟伅', - message: `鏈嶅姟鍣ㄥ洖搴斿瓧绗︿覆: ${data}`, - duration: 5000, - position: 'top-right', - offset: 50 - }); - } - }); - }, - getList() { this.loading = true; - // 鏌ヨ鎵�鏈夊叕鍛婏紙浠呯敤浜庡垵濮嬪寲鏁版嵁锛屽彲鏍规嵁闇�瑕佷繚鐣欐垨绉婚櫎锛� + // 鏌ヨ鎵�鏈夊叕鍛� noticeToday().then(response => { console.log('Notice API response:', response); + this.noticeList = response.rows || response.data || []; + console.log('noticeList:', this.noticeList); this.loading = false; + this.$nextTick(() => { + console.log('Carousel updated'); + }); }).catch(error => { console.error('Notice API error:', error); - this.$notify.error({ - title: '閿欒', - message: `鑾峰彇閫氱煡澶辫触锛�${error.message}`, - duration: 5000, - position: 'top-right' - }); + this.$message.error("鑾峰彇閫氱煡澶辫触锛�" + error.message); this.loading = false; }); + // 鏌ヨ浠婃棩鐧昏 getCustomer().then(response => { this.Customer = response.data || response; this.loading = false; }); + // 鏌ヨ浠婃棩宸叉 getOrder().then(response => { this.Order = response.data || response; this.loading = false; }); + // 鏌ヨ浠婃棩鎶ュ憡 getReportToday().then(response => { this.ReportToday = response.data || response; this.loading = false; }); + // 鏌ヨ浠婃棩寰呮 getTobeToday().then(response => { this.TobeToday = response.data || response; this.loading = false; }); + // 鎶樼嚎鍥� getChart().then(response => { response.data.forEach(item => { this.LineChart.push(item.date); @@ -222,6 +211,7 @@ this.loading = false; }); + // 楗肩姸鍥� getPieChart().then(response => { if (response.data) { if (response.data.tjdj == 0 || !response.data.tjdj?.length) { @@ -291,6 +281,23 @@ }); }, + // 鏁扮粍鍒嗙粍鏂规硶 + chunkArray(array, size) { + if (!array || array.length === 0) return []; + const result = []; + for (let i = 0; i < array.length; i += size) { + result.push(array.slice(i, i + size)); + } + // 纭繚寰幆婊氬姩骞虫粦锛岃嫢涓嶈冻 size 鏉★紝琛ラ綈 + if (array.length % size !== 0 && array.length > size) { + const lastGroup = result[result.length - 1]; + while (lastGroup.length < size) { + lastGroup.push(array[lastGroup.length % array.length]); + } + } + return result; + }, + parseTime(time, cFormat) { if (!time) return ''; try { @@ -320,6 +327,62 @@ margin: 15px; background-color: #f3f3f3; padding: 10px; +} + +.notice-area { + width: 100%; + min-height: 120px; + /* 璋冩暣涓轰笁鏉¢�氱煡楂樺害 */ + background-color: #fff; + margin: 10px 0; + padding: 0 20px; +} + +.carousel { + width: 100% !important; +} + +.notice-group { + display: flex; + flex-direction: column; + height: 120px; + /* 纭繚鍖呭惈涓夋潯閫氱煡 */ +} + +.notice-item { + display: flex; + align-items: center; + width: 100%; + height: 40px; + line-height: 40px; + font-size: 14px; + cursor: pointer; +} + +.notice-title { + margin-left: 10px; + font-size: 14px; + color: #333; + flex-grow: 1; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.notice-time { + font-size: 12px; + color: #999; + margin-left: 20px; +} + +.no-notice { + width: 100%; + height: 120px; + /* 涓庤疆鎾珮搴︿竴鑷� */ + line-height: 120px; + text-align: center; + color: #999; + font-size: 14px; } .top { @@ -384,28 +447,5 @@ height: 350px; background-color: #fff; margin-right: 20px; -} - -/* 鑷畾涔� Element UI 閫氱煡鏍峰紡 */ -.el-notification { - min-width: 300px; - background-color: #f0f9eb; - border-color: #e1f3d8; - color: #67c23a; - font-size: 16px; - padding: 15px 20px; -} - -.el-notification__title { - font-weight: bold; -} - -.el-notification__content { - font-size: 14px; - color: #333; -} - -.el-notification__closeBtn { - color: #999; } </style> \ No newline at end of file -- Gitblit v1.8.0