New file |
| | |
| | | import request from '@/utils/request' |
| | | |
| | | // 查询关键字列表 |
| | | export function listJcycpdgjz(query) { |
| | | return request({ |
| | | url: '/system/jcycpdgjz/list', |
| | | method: 'get', |
| | | params: query |
| | | }) |
| | | } |
| | | |
| | | // 查询关键字详细 |
| | | export function getJcycpdgjz(id) { |
| | | return request({ |
| | | url: '/system/jcycpdgjz/' + id, |
| | | method: 'get' |
| | | }) |
| | | } |
| | | |
| | | // 新增关键字 |
| | | export function addJcycpdgjz(data) { |
| | | return request({ |
| | | url: '/system/jcycpdgjz', |
| | | method: 'post', |
| | | data: data |
| | | }) |
| | | } |
| | | |
| | | // 修改关键字 |
| | | export function updateJcycpdgjz(data) { |
| | | return request({ |
| | | url: '/system/jcycpdgjz', |
| | | method: 'put', |
| | | data: data |
| | | }) |
| | | } |
| | | |
| | | // 删除关键字 |
| | | export function delJcycpdgjz(id) { |
| | | return request({ |
| | | url: '/system/jcycpdgjz/' + id, |
| | | method: 'delete' |
| | | }) |
| | | } |
| | |
| | | /* src/assets/styles/index.scss */ |
| | | @import './variables.scss'; |
| | | @import './mixin.scss'; |
| | | @import './transition.scss'; |
| | |
| | | } |
| | | } |
| | | |
| | | //main-container全局样式 |
| | | // main-container全局样式 |
| | | .app-container { |
| | | padding: 20px; |
| | | width: 100%; |
| | |
| | | } |
| | | |
| | | .text-center { |
| | | text-align: center |
| | | text-align: center; |
| | | } |
| | | |
| | | .sub-navbar { |
| | |
| | | } |
| | | } |
| | | |
| | | //refine vue-multiselect plugin |
| | | // refine vue-multiselect plugin |
| | | .multiselect { |
| | | line-height: 16px; |
| | | } |
| | |
| | | .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; |
| | | } |
New file |
| | |
| | | // 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 解析后消息:', 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('消息解析失败:', 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; |
| | |
| | | // 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 { |
| | |
| | | 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"; |
| | |
| | | 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; |
| | |
| | | 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), |
| | | }); |
| | | Vue.config.productionTip = false; |
| | |
| | | // 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秒发送ping |
| | | |
| | | 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, '浏览器关闭,正常断开'); |
| | | 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 }; |
| | | } |
| | |
| | | <div class="app-container"> |
| | | <el-row :gutter="24"> |
| | | <!-- 第一列:单选标本 --> |
| | | <el-col :span="8" :xs="24"> |
| | | <el-col :span="6" :xs="24"> |
| | | <el-form |
| | | :model="queryParams" |
| | | ref="queryForm" |
| | |
| | | > |
| | | <el-form-item label="项目名称" prop="tjh"> |
| | | <el-input |
| | | style="width: 140px" |
| | | v-model="queryParams.tjh" |
| | | placeholder="请输入项目名称" |
| | | clearable |
| | |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" size="mini" @click="handleManual">查询</el-button> |
| | | <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button> |
| | | <el-button type="primary" size="mini" @click="handleAdd">新增</el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | | |
| | |
| | | </el-table> |
| | | </el-col> |
| | | |
| | | |
| | | |
| | | |
| | | <!-- 第二列:中间选中项目列表 --> |
| | | <el-col :span="8" :xs="24"> |
| | | <el-form :model="form" size="small" :inline="true" label-width="68px" style="height: 45px" @submit.native.prevent /> |
| | | <el-col :span="9" :xs="24"> |
| | | <el-form :model="queryParams2" ref="queryForm" size="small" :inline="true" v-show="showSearch" |
| | | label-width="68px" style="height: 45px" @submit.native.prevent> |
| | | <el-form-item label="项目名称" prop="proName"> |
| | | <el-input ref="inputName" v-model="queryParams2.proName" placeholder="请输入项目名称" clearable |
| | | @keyup.enter.native="handleyixuan" style="width: 140px" /> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" icon="el-icon-search" size="mini" @click="handleyixuan">搜索</el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | | <el-table |
| | | border |
| | | v-loading="loading" |
| | | :data="OnenewpacName" |
| | | ref="tres" |
| | | height="478" |
| | | style="width: 100%" |
| | | > |
| | |
| | | OnenewpacName: [], |
| | | Treedata: [], |
| | | originalTreedata: [], |
| | | Treedatas: [], |
| | | queryParams: { |
| | | tjh: '', |
| | | tjCategory: null, |
| | | }, |
| | | queryParams1: { |
| | | proName: '', |
| | | }, |
| | | queryParams2: { |
| | | proName: '', |
| | | }, |
| | | form: { |
| | |
| | | originalSpecimenData: [], |
| | | }; |
| | | }, |
| | | dicts: ["sys_dict_specimen"], |
| | | dicts: ["sys_dict_specimen", "sys_normal_disable"], |
| | | created() { |
| | | this.fetchInitialBiaobenData(); |
| | | this.initializeSpecimenData(); |
| | |
| | | }, |
| | | deep: true, |
| | | immediate: true, |
| | | }, |
| | | }, |
| | | }, |
| | | methods: { |
| | | initializeSpecimenData() { |
| | |
| | | this.originalSpecimenData = []; |
| | | this.filteredSpecimenData = []; |
| | | } |
| | | }, |
| | | /** 新增按钮操作 */ |
| | | handleAdd() { |
| | | this.open = true; |
| | | this.title = "添加字典数据"; |
| | | this.form.dictType = "sys_dict_specimen"; |
| | | }, |
| | | handleyixuan() { |
| | | const searchText = this.queryParams2.proName?.trim().toLowerCase() || ''; |
| | | if (searchText) { |
| | | this.OnenewpacName = this.Treedatas.filter(item => |
| | | item.proName.toLowerCase().includes(searchText) |
| | | ); |
| | | this.OnenewpacName.length |
| | | ? this.$message.success('查询成功') |
| | | : this.$message.warning('未找到匹配的项目'); |
| | | } else { |
| | | this.OnenewpacName = [...this.Treedatas]; |
| | | this.$message.info('已显示所有项目'); |
| | | } |
| | | this.$refs.tres.clearSelection(); |
| | | }, |
| | | handleSearchFor() { |
| | | const searchText = this.queryParams1.proName?.trim().toLowerCase() || ''; |
| | |
| | | this.$refs.specimenTable.clearSelection(); |
| | | this.$message.success('已重置查询条件和标本数据'); |
| | | }, |
| | | handleManual() { |
| | | handleManual() { |
| | | const searchText = this.queryParams.tjh?.trim().toLowerCase() || ''; |
| | | this.filteredSpecimenData = searchText |
| | | ? this.originalSpecimenData.filter(item => item.label.toLowerCase().includes(searchText)) |
| | |
| | | cancell() { |
| | | this.openOne = false; |
| | | }, |
| | | submitrighr() { |
| | | submitrighr() { |
| | | if (!this.queryParams.tjCategory) { |
| | | this.$message.error('请先选择一个标本'); |
| | | return; |
| | |
| | | this.loading = true; |
| | | searchBiaoben({ bblx }) |
| | | .then(response => { |
| | | this.OnenewpacName = response.data || []; |
| | | this.Treedatas = response.data || []; |
| | | this.OnenewpacName = [...this.Treedatas]; |
| | | this.loading = false; |
| | | }) |
| | | .catch(error => { |
| | |
| | | <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> |
| | |
| | | <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'); |
| | |
| | | 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 |
| | | } |
| | | }, |
| | | |
| | |
| | | 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 解析后消息:', 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('消息解析失败:', 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); |
| | |
| | | this.loading = false; |
| | | }); |
| | | |
| | | // 饼状图 |
| | | getPieChart().then(response => { |
| | | if (response.data) { |
| | | if (response.data.tjdj == 0 || !response.data.tjdj?.length) { |
| | |
| | | }); |
| | | }, |
| | | |
| | | // 数组分组方法 |
| | | 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 { |
| | |
| | | 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 { |
| | |
| | | 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> |
| | |
| | | :value="dict.value" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | |
| | | <el-form-item label="是否替检" prop="tj"> |
| | | <el-select v-model="forms.tj" placeholder="请选择是否替检" style="width: 120px" clearable> |
| | | <el-option v-for="dict in dict.type.sys_yes_no" :key="dict.value" :label="dict.label" |
| | | :value="dict.value" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="年龄段"> |
| | | <el-col :span="7"> |
| | | <el-input v-model="forms.ltAge" style="width: 53px" /> |
| | |
| | | <el-input v-model="forms.gtAge" style="width: 54px" /> |
| | | </el-col> |
| | | </el-form-item> |
| | | |
| | | </el-form> |
| | | |
| | | <el-row :gutter="10" class="mb8"> |
| | |
| | | "dict_data_status", |
| | | "sys_user_sex", |
| | | "reservation_pay_type", |
| | | "sys_yes_no", |
| | | "sys_yes_no", "sys_normal_disable" |
| | | ], |
| | | data() { |
| | | let checkPhoneNum = (rule, value, callback) => { |
| | |
| | | if (val == 0) { |
| | | this.getList() |
| | | } else { |
| | | this.handleQuerydanwen() |
| | | this.handleQuerydanwen() |
| | | } |
| | | }, |
| | | |
| | |
| | | |
| | | // 获取选中的套餐项目列表 |
| | | const newProjects = this.selectedPackage.tjProjectList || []; |
| | | console.log(newProjects,22222) |
| | | console.log(newProjects, 22222) |
| | | // 将选中的套餐项目添加到左侧表格 |
| | | this.loading = true; |
| | | newProjects.forEach((project) => { |
| | |
| | | }, |
| | | |
| | | handleSelectionChange1(selection) { |
| | | console.log(selection,11111) |
| | | console.log(selection, 11111) |
| | | // 实现单选逻辑 |
| | | if (selection.length > 1) { |
| | | const lastSelected = selection[selection.length - 1]; |
New file |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <el-form |
| | | :model="queryParams" |
| | | ref="queryForm" |
| | | size="small" |
| | | :inline="true" |
| | | v-show="showSearch" |
| | | label-width="68px" |
| | | @submit.native.prevent |
| | | > |
| | | <el-form-item label="关键字" prop="gjz"> |
| | | <el-input |
| | | v-model="queryParams.gjz" |
| | | placeholder="请输入关键字" |
| | | clearable |
| | | @keyup.enter.native="handleQuery" |
| | | /> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button |
| | | type="primary" |
| | | icon="el-icon-search" |
| | | size="mini" |
| | | @click="handleQuery" |
| | | >搜索</el-button |
| | | > |
| | | <el-button icon="el-icon-refresh" size="mini" @click="resetQuery" |
| | | >重置</el-button |
| | | > |
| | | </el-form-item> |
| | | </el-form> |
| | | |
| | | <el-row :gutter="10" class="mb8"> |
| | | <el-col :span="1.5"> |
| | | <el-button |
| | | type="primary" |
| | | plain |
| | | icon="el-icon-plus" |
| | | size="mini" |
| | | @click="handleAdd" |
| | | v-hasPermi="['system:jcycpdgjz:add']" |
| | | >新增 |
| | | </el-button> |
| | | </el-col> |
| | | <el-col :span="1.5"> |
| | | <el-button |
| | | type="success" |
| | | plain |
| | | icon="el-icon-edit" |
| | | size="mini" |
| | | :disabled="single" |
| | | @click="handleUpdate" |
| | | v-hasPermi="['system:jcycpdgjz:edit']" |
| | | >修改 |
| | | </el-button> |
| | | </el-col> |
| | | <el-col :span="1.5"> |
| | | <el-button |
| | | type="danger" |
| | | plain |
| | | icon="el-icon-delete" |
| | | size="mini" |
| | | :disabled="multiple" |
| | | @click="handleDelete" |
| | | v-hasPermi="['system:jcycpdgjz:remove']" |
| | | >删除 |
| | | </el-button> |
| | | </el-col> |
| | | <el-col :span="1.5"> |
| | | <!-- <el-button |
| | | type="warning" |
| | | plain |
| | | icon="el-icon-download" |
| | | size="mini" |
| | | @click="handleExport" |
| | | v-hasPermi="['system:jcycpdgjz:export']" |
| | | >导出 |
| | | </el-button> --> |
| | | </el-col> |
| | | <right-toolbar |
| | | :showSearch.sync="showSearch" |
| | | @queryTable="getList" |
| | | ></right-toolbar> |
| | | </el-row> |
| | | |
| | | <el-table |
| | | v-loading="loading" |
| | | :data="jcycpdgjzList" |
| | | @selection-change="handleSelectionChange" |
| | | > |
| | | <el-table-column type="selection" width="55" align="center" /> |
| | | <el-table-column label="序号" align="center" prop="id" width="80" /> |
| | | <el-table-column label="关键字" align="center" prop="gjz" /> |
| | | <el-table-column |
| | | label="操作" |
| | | align="center" |
| | | class-name="small-padding fixed-width" |
| | | width="120" |
| | | > |
| | | <template slot-scope="scope"> |
| | | <el-button |
| | | size="mini" |
| | | type="text" |
| | | icon="el-icon-edit" |
| | | @click="handleUpdate(scope.row)" |
| | | v-hasPermi="['system:jcycpdgjz:edit']" |
| | | >修改 |
| | | </el-button> |
| | | <el-button |
| | | size="mini" |
| | | type="text" |
| | | icon="el-icon-delete" |
| | | @click="handleDelete(scope.row)" |
| | | v-hasPermi="['system:jcycpdgjz:remove']" |
| | | >删除 |
| | | </el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | |
| | | <pagination |
| | | v-show="total > 0" |
| | | :total="total" |
| | | :page.sync="queryParams.pageNum" |
| | | :limit.sync="queryParams.pageSize" |
| | | @pagination="getList" |
| | | /> |
| | | |
| | | <!-- 添加或修改关键字对话框 --> |
| | | <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body> |
| | | <el-form ref="form" :model="form" :rules="rules" label-width="80px"> |
| | | <el-form-item label="关键字" prop="gjz"> |
| | | <el-input v-model="form.gjz" placeholder="请输入关键字" /> |
| | | </el-form-item> |
| | | </el-form> |
| | | <div slot="footer" class="dialog-footer"> |
| | | <el-button type="primary" @click="submitForm">确 定</el-button> |
| | | <el-button @click="cancel">取 消</el-button> |
| | | </div> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | import { |
| | | listJcycpdgjz, |
| | | getJcycpdgjz, |
| | | delJcycpdgjz, |
| | | addJcycpdgjz, |
| | | updateJcycpdgjz, |
| | | } from "@/api/system/jcycpdgjz"; |
| | | |
| | | export default { |
| | | name: "Jcycpdgjz", |
| | | data() { |
| | | return { |
| | | // 遮罩层 |
| | | loading: true, |
| | | // 选中数组 |
| | | ids: [], |
| | | // 非单个禁用 |
| | | single: true, |
| | | // 非多个禁用 |
| | | multiple: true, |
| | | // 显示搜索条件 |
| | | showSearch: true, |
| | | // 总条数 |
| | | total: 0, |
| | | // 关键字表格数据 |
| | | jcycpdgjzList: [], |
| | | // 弹出层标题 |
| | | title: "", |
| | | // 是否显示弹出层 |
| | | open: false, |
| | | // 查询参数 |
| | | queryParams: { |
| | | pageNum: 1, |
| | | pageSize: 10, |
| | | gjz: null, |
| | | }, |
| | | // 表单参数 |
| | | form: {}, |
| | | // 表单校验 |
| | | rules: {}, |
| | | }; |
| | | }, |
| | | created() { |
| | | this.getList(); |
| | | }, |
| | | methods: { |
| | | /** 查询关键字列表 */ |
| | | getList() { |
| | | this.loading = true; |
| | | listJcycpdgjz(this.queryParams).then((response) => { |
| | | this.jcycpdgjzList = response.rows; |
| | | this.total = response.total; |
| | | this.loading = false; |
| | | }); |
| | | }, |
| | | // 取消按钮 |
| | | cancel() { |
| | | this.open = false; |
| | | this.reset(); |
| | | }, |
| | | // 表单重置 |
| | | reset() { |
| | | this.form = { |
| | | id: null, |
| | | gjz: null, |
| | | createTime: null, |
| | | createBy: null, |
| | | updateTime: null, |
| | | updateBy: null, |
| | | deleted: null, |
| | | }; |
| | | this.resetForm("form"); |
| | | }, |
| | | /** 搜索按钮操作 */ |
| | | handleQuery() { |
| | | this.queryParams.pageNum = 1; |
| | | this.getList(); |
| | | }, |
| | | /** 重置按钮操作 */ |
| | | resetQuery() { |
| | | this.resetForm("queryForm"); |
| | | this.handleQuery(); |
| | | }, |
| | | // 多选框选中数据 |
| | | handleSelectionChange(selection) { |
| | | this.ids = selection.map((item) => item.id); |
| | | this.single = selection.length !== 1; |
| | | this.multiple = !selection.length; |
| | | }, |
| | | /** 新增按钮操作 */ |
| | | handleAdd() { |
| | | this.reset(); |
| | | this.open = true; |
| | | this.title = "添加关键字"; |
| | | }, |
| | | /** 修改按钮操作 */ |
| | | handleUpdate(row) { |
| | | this.reset(); |
| | | const id = row.id || this.ids; |
| | | getJcycpdgjz(id).then((response) => { |
| | | this.form = response.data; |
| | | this.open = true; |
| | | this.title = "修改关键字"; |
| | | }); |
| | | }, |
| | | /** 新增修改操作 */ |
| | | submitForm() { |
| | | this.$refs["form"].validate((valid) => { |
| | | if (valid) { |
| | | // 如果有多选框字段(checkbox),这里处理为逗号拼接(如有需要可添加) |
| | | if (!this.form.id || this.form.id === "") { |
| | | addJcycpdgjz(this.form).then((response) => { |
| | | this.$modal.msgSuccess("新增成功"); |
| | | this.open = false; |
| | | this.getList(); |
| | | }); |
| | | } else { |
| | | updateJcycpdgjz(this.form).then((response) => { |
| | | this.$modal.msgSuccess("修改成功"); |
| | | this.open = false; |
| | | this.getList(); |
| | | }); |
| | | } |
| | | } |
| | | }); |
| | | }, |
| | | /** 删除按钮操作 */ |
| | | handleDelete(row) { |
| | | const id = row.id; |
| | | this.$modal |
| | | .confirm('是否确认删除"' + id + '"的数据项?') |
| | | .then(function () { |
| | | return delJcycpdgjz(id); |
| | | }) |
| | | .then(() => { |
| | | this.getList(); |
| | | this.$modal.msgSuccess("删除成功"); |
| | | }) |
| | | .catch(() => {}); |
| | | }, |
| | | /** 导出按钮操作 */ |
| | | /* handleExport() { |
| | | this.download( |
| | | "hosp/detail/export", |
| | | { |
| | | ...this.queryParams, |
| | | }, |
| | | `detail_${new Date().getTime()}.xlsx` |
| | | ); |
| | | }, */ |
| | | }, |
| | | }; |
| | | </script> |
| | |
| | | [process.env.VUE_APP_BASE_API]: { |
| | | // target: `https://ltpeis.xaltjdkj.cn:5801/`, |
| | | // target: `http://192.168.1.99:5012`, |
| | | <<<<<<< HEAD |
| | | target: `http://192.168.1.2:5011`, |
| | | ======= |
| | | target: `http://192.168.1.113:5011`, |
| | | >>>>>>> 2ee7a81c2eb0df1fc473da6dbbaa13df662967c5 |
| | | // target: `http://192.168.1.2:5011`, |
| | | // // target: `http://192.168.0.99:8080/ltkj-admin`, |
| | | // target: `https://ltpeis.xaltjdkj.cn:5011/ltkj-admin`, |