| | |
| | | <template> |
| | | <div class="centre"> |
| | | <!-- 今日统计 --> |
| | | <div style="display: flex; justify-content: center; align-items: center;"> |
| | | <h4>今日统计</h4> |
| | | </div> |
| | | <div class="top"> |
| | | <div class="add"> |
| | | <div class="img"> |
| | |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- 近一月统计 --> |
| | | <div style="display: flex; justify-content: center; align-items: center;"> |
| | | <h4>近一月统计</h4> |
| | | </div> |
| | | <div class="data-view"> |
| | | <div id="main" style="width:95%;height:330px"></div> |
| | | <div id="main" style="width: 95%; height: 330px"></div> |
| | | </div> |
| | | <div class="view"> |
| | | <div id="main2"></div> |
| | |
| | | |
| | | <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'); |
| | | require('echarts/lib/component/legend'); |
| | | require('echarts/lib/chart/pie'); |
| | | require('echarts/lib/chart/line'); |
| | | |
| | | export default { |
| | | data() { |
| | |
| | | teamYYNum: [], |
| | | PieChart: [], |
| | | PieChart2: [], |
| | | } |
| | | loading: false |
| | | }; |
| | | }, |
| | | |
| | | created() { |
| | | this.getList(); |
| | | |
| | | this.initWebSocket(); |
| | | }, |
| | | |
| | | watch: { |
| | | $route(to, from) { |
| | | window.location.reload(); //监测到路由发生跳转时刷新一次页面 |
| | | // this.$router.go(0); |
| | | }, |
| | | }, |
| | | |
| | | $route(to, from) { |
| | | window.location.reload(); |
| | | } |
| | | }, |
| | | |
| | | methods: { |
| | | |
| | | goToNotice(noticeId) { |
| | | this.$router.push({ |
| | | path: '/notice', |
| | | 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; |
| | | // 查询今日登记 |
| | | getCustomer().then((response) => { |
| | | this.Customer = response |
| | | this.loading = false; |
| | | }); |
| | | // 查询今日已检 |
| | | getOrder().then((response) => { |
| | | this.Order = response |
| | | this.loading = false; |
| | | }); |
| | | // 查询今日报告 |
| | | getReportToday().then((response) => { |
| | | this.ReportToday = response |
| | | this.loading = false; |
| | | }); |
| | | // 查询今日待检 |
| | | getTobeToday().then((response) => { |
| | | this.TobeToday = response |
| | | this.loading = false; |
| | | }); |
| | | // 折线图 |
| | | getChart().then((response) => { |
| | | response.data.forEach(item => { |
| | | this.LineChart.push(item.date) |
| | | this.personYYNum.push(item.tdcoun); |
| | | this.reportNum.push(item.grcoun); |
| | | this.teamYYNum.push(item.bgcoun); |
| | | |
| | | // 查询所有公告(仅用于初始化数据,可根据需要保留或移除) |
| | | noticeToday().then(response => { |
| | | console.log('Notice API response:', response); |
| | | this.loading = false; |
| | | }).catch(error => { |
| | | console.error('Notice API error:', error); |
| | | this.$notify.error({ |
| | | title: '错误', |
| | | message: `获取通知失败:${error.message}`, |
| | | duration: 5000, |
| | | position: 'top-right' |
| | | }); |
| | | let myChart = this.$echarts.init(document.getElementById('main')); |
| | | 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.reportNum.push(item.tdcoun); |
| | | this.personYYNum.push(item.grcoun); |
| | | this.teamYYNum.push(item.bgcoun); |
| | | }); |
| | | |
| | | let myChart = this.$echarts.init(document.getElementById('main')); |
| | | myChart.setOption({ |
| | | tooltip: { |
| | | trigger: 'axis' |
| | | }, |
| | | legend: { |
| | | data: ['每日体检登记数', '每日团体登记数', '每日发布报告数'] |
| | | }, |
| | | grid: { |
| | | left: '3%', |
| | | right: '4%', |
| | | bottom: '3%', |
| | | containLabel: true |
| | | }, |
| | | toolbox: { |
| | | feature: { |
| | | saveAsImage: {} |
| | | } |
| | | }, |
| | | tooltip: { trigger: 'axis' }, |
| | | legend: { data: ['每日体检登记数', '每日团体登记数', '每日发布报告数'] }, |
| | | grid: { left: '3%', right: '4%', bottom: '3%', containLabel: true }, |
| | | toolbox: { feature: { saveAsImage: {} } }, |
| | | xAxis: { |
| | | type: 'category', |
| | | boundaryGap: false, |
| | | axisLine: { |
| | | show: true, |
| | | lineStyle: { |
| | | color: "blue", |
| | | size: 12, |
| | | width: 0, |
| | | tyle: "solid" |
| | | } |
| | | lineStyle: { width: 0, color: 'blue', type: 'solid' } |
| | | }, |
| | | data: this.LineChart |
| | | }, |
| | | yAxis: { |
| | | type: 'value', |
| | | min: 0, |
| | | max: 50, |
| | | interval: 5 |
| | | }, |
| | | yAxis: { type: 'value', min: 0, max: 400, interval: 20 }, |
| | | series: [ |
| | | { |
| | | name: '每日体检登记数', |
| | | type: 'line', |
| | | stack: 'Total', |
| | | data: this.personYYNum |
| | | }, |
| | | { |
| | | name: '每日团体登记数', |
| | | type: 'line', |
| | | stack: 'Total', |
| | | data: this.reportNum |
| | | }, |
| | | { |
| | | name: '每日发布报告数', |
| | | type: 'line', |
| | | stack: 'Total', |
| | | data: this.teamYYNum |
| | | }, |
| | | { name: '每日体检登记数', type: 'line', stack: 'Total', data: this.personYYNum }, |
| | | { name: '每日团体登记数', type: 'line', stack: 'Total', data: this.reportNum }, |
| | | { name: '每日发布报告数', type: 'line', stack: 'Total', data: this.teamYYNum } |
| | | ] |
| | | }); |
| | | |
| | | }) |
| | | let sizeFun = function () { |
| | | myChart.resize() |
| | | } |
| | | window.addEventListener("resize", sizeFun) |
| | | |
| | | const sizeFun = () => myChart.resize(); |
| | | window.addEventListener('resize', sizeFun); |
| | | this.loading = false; |
| | | }); |
| | | |
| | | // 饼状图 |
| | | getPieChart().then((response) => { |
| | | getPieChart().then(response => { |
| | | if (response.data) { |
| | | |
| | | if (response.data.tjdj.length === 0) { |
| | | |
| | | this.PieChart = [] |
| | | this.PieChart = [ |
| | | { |
| | | "name": "体检登记人数分布", |
| | | "count": 1, |
| | | "value": 10 |
| | | }, |
| | | |
| | | ] |
| | | if (response.data.tjdj == 0 || !response.data.tjdj?.length) { |
| | | this.PieChart = [{ name: '体检登记人数分布', count: 1, value: 10 }]; |
| | | } else { |
| | | this.PieChart = response.data.tjdj |
| | | this.PieChart.forEach(item => { |
| | | item.value = item.count |
| | | }) |
| | | this.PieChart.reverse() |
| | | this.PieChart.push(this.PieChart[0]) |
| | | this.PieChart.splice(0, 1) |
| | | |
| | | this.PieChart = response.data.tjdj; |
| | | this.PieChart.forEach(item => { item.value = item.count; }); |
| | | this.PieChart.reverse(); |
| | | this.PieChart.push(this.PieChart[0]); |
| | | this.PieChart.splice(0, 1); |
| | | } |
| | | |
| | | let myChart2 = this.$echarts.init(document.getElementById('main2')); |
| | | myChart2.setOption({ |
| | | title: { |
| | | text: '体检登记人数分布', |
| | | top: '5' |
| | | title: { text: '体检登记人数分布', top: '5' }, |
| | | tooltip: { trigger: 'item' }, |
| | | legend: { top: '80%', left: 'center' }, |
| | | series: [{ |
| | | type: 'pie', |
| | | radius: ['16%', '54%'], |
| | | center: ['50%', '43%'], |
| | | avoidLabelOverlap: false, |
| | | startAngle: 180, |
| | | minAngle: 10, |
| | | data: this.PieChart, |
| | | emphasis: { |
| | | itemStyle: { shadowBlur: 10, shadowOffsetX: 0, shadowColor: 'rgba(0, 0, 0, 0.5)' } |
| | | } |
| | | }] |
| | | }); |
| | | |
| | | }, |
| | | |
| | | tooltip: { |
| | | trigger: 'item' |
| | | }, |
| | | legend: { |
| | | top: '80%', |
| | | left: 'center' |
| | | }, |
| | | series: [ |
| | | { |
| | | // name: 'Access From', |
| | | type: 'pie', |
| | | radius: ['16%', '54%'], |
| | | center: ["50%", "43%"], |
| | | avoidLabelOveralap:false, |
| | | startAngle:180, |
| | | minAngle:10, |
| | | data: this.PieChart, |
| | | emphasis: { |
| | | itemStyle: { |
| | | shadowBlur: 10, |
| | | shadowOffsetX: 0, |
| | | shadowColor: 'rgba(0, 0, 0, 0.5)' |
| | | } |
| | | } |
| | | }] |
| | | }) |
| | | |
| | | if(response.data.tjyc == 0){ |
| | | this.PieChart2 = [] |
| | | this.PieChart2 = [ |
| | | { |
| | | "name": "体检结果异常人数分布", |
| | | "count": 1, |
| | | "value": 10 |
| | | }, |
| | | |
| | | ] |
| | | }else if (response.data.tjyc.length === 0) { |
| | | this.PieChart2 = [] |
| | | this.PieChart2 = [ |
| | | { |
| | | "name": "体检结果异常人数分布", |
| | | "count": 1, |
| | | "value": 10 |
| | | }, |
| | | |
| | | ] |
| | | if (response.data.tjyc == 0 || !response.data.tjyc?.length) { |
| | | this.PieChart2 = [{ name: '体检结果异常人数分布', count: 1, value: 0 }]; |
| | | } else { |
| | | this.PieChart2 = response.data.tjyc |
| | | this.PieChart2.reverse() |
| | | this.PieChart2.push(this.PieChart2[0]) |
| | | this.PieChart2.splice(0, 1) |
| | | this.PieChart2.forEach(item => { |
| | | item.value = item.count |
| | | }) |
| | | // this.TobeToday = response |
| | | this.PieChart2 = response.data.tjyc; |
| | | this.PieChart2.reverse(); |
| | | this.PieChart2.push(this.PieChart2[0]); |
| | | this.PieChart2.splice(0, 1); |
| | | this.PieChart2.forEach(item => { item.value = item.count; }); |
| | | } |
| | | |
| | | let myChart3 = this.$echarts.init(document.getElementById('main3')); |
| | | |
| | | myChart3.setOption({ |
| | | title: { |
| | | text: '体检结果异常人数分布', |
| | | top: '5' |
| | | title: { text: '体检结果异常人数分布', top: '5' }, |
| | | tooltip: { trigger: 'item' }, |
| | | legend: { top: '80%', left: 'center' }, |
| | | series: [{ |
| | | type: 'pie', |
| | | radius: ['16%', '54%'], |
| | | center: ['50%', '43%'], |
| | | avoidLabelOverlap: false, |
| | | startAngle: 180, |
| | | minAngle: 10, |
| | | data: this.PieChart2, |
| | | emphasis: { |
| | | itemStyle: { shadowBlur: 10, shadowOffsetX: 0, shadowColor: 'rgba(0, 0, 0, 0.5)' } |
| | | } |
| | | }] |
| | | }); |
| | | |
| | | }, |
| | | tooltip: { |
| | | trigger: 'item' |
| | | }, |
| | | legend: { |
| | | top: '80%', |
| | | left: 'center' |
| | | }, |
| | | series: [ |
| | | { |
| | | // name: 'Access From', |
| | | type: 'pie', |
| | | radius: ['16%', '54%'], |
| | | center: ["50%", "43%"], |
| | | avoidLabelOveralap:false, |
| | | startAngle:180, |
| | | minAngle:10, |
| | | data: this.PieChart2, |
| | | emphasis: { |
| | | itemStyle: { |
| | | shadowBlur: 10, |
| | | shadowOffsetX: 0, |
| | | shadowColor: 'rgba(0, 0, 0, 0.5)' |
| | | } |
| | | } |
| | | }] |
| | | }) |
| | | } |
| | | |
| | | |
| | | window.onresize = function () { |
| | | myChart2.resize() |
| | | myChart3.resize() |
| | | window.onresize = () => { |
| | | myChart2.resize(); |
| | | myChart3.resize(); |
| | | }; |
| | | } |
| | | this.loading = false; |
| | | }); |
| | | }, |
| | | |
| | | parseTime(time, cFormat) { |
| | | if (!time) return ''; |
| | | try { |
| | | const date = new Date(time); |
| | | if (isNaN(date.getTime())) return ''; |
| | | const formatObj = { |
| | | y: date.getFullYear(), |
| | | m: String(date.getMonth() + 1).padStart(2, '0'), |
| | | d: String(date.getDate()).padStart(2, '0'), |
| | | h: String(date.getHours()).padStart(2, '0'), |
| | | i: String(date.getMinutes()).padStart(2, '0'), |
| | | s: String(date.getSeconds()).padStart(2, '0') |
| | | }; |
| | | return cFormat.replace(/{([ymdhis]+)}/g, (result, key) => formatObj[key] || ''); |
| | | } catch (error) { |
| | | console.error('parseTime error:', error, 'time:', time); |
| | | return ''; |
| | | } |
| | | } |
| | | }, |
| | | |
| | | |
| | | mounted() { |
| | | |
| | | } |
| | | } |
| | | |
| | | |
| | | }; |
| | | </script> |
| | | |
| | | <style> |
| | | .centre { |
| | | height: 820px; |
| | | margin: 15px 15px; |
| | | min-height: 820px; |
| | | margin: 15px; |
| | | background-color: #f3f3f3; |
| | | padding: 10px; |
| | | } |
| | | |
| | | .top { |
| | | width: 100%; |
| | | display: flex; |
| | | height: 120px; |
| | | |
| | | } |
| | | |
| | | .add { |
| | | width: 380px; |
| | | width: 320px; |
| | | height: 75px; |
| | | margin-top: 30px; |
| | | margin-top: 20px; |
| | | margin-left: 20px; |
| | | margin-right: 20px; |
| | | background-color: #fff; |
| | | display: flex |
| | | display: flex; |
| | | } |
| | | |
| | | .img { |
| | |
| | | .image { |
| | | width: 60px; |
| | | height: 60px; |
| | | |
| | | } |
| | | |
| | | .txt { |
| | |
| | | |
| | | .data-view { |
| | | margin: 0 15px; |
| | | height: 323px !important |
| | | height: 323px !important; |
| | | } |
| | | |
| | | #main { |
| | |
| | | } |
| | | |
| | | .view { |
| | | margin: 0px 15px; |
| | | margin: 0 15px; |
| | | padding-top: 15px; |
| | | height: 350px; |
| | | min-height: 350px; |
| | | display: flex; |
| | | } |
| | | |
| | | #main2 { |
| | | width: 820px; |
| | | height: 350px; |
| | | background-color: #fff; |
| | | margin-right: 20px; |
| | | } |
| | | |
| | | #main2, |
| | | #main3 { |
| | | width: 820px; |
| | | 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> |