Browse Source

新增供暖办页面

develop
fuguobin 10 months ago
parent
commit
985081e616
  1. 4
      .env.production
  2. 1
      package.json
  3. BIN
      public/static/images/sanfasanqu.gif
  4. BIN
      public/static/images/sanfasanquyibeng.gif
  5. BIN
      public/static/images/yifaysanquyibeng.gif
  6. 15
      src/api/table/list.ts
  7. 167
      src/assets/iconfont/demo_index.html
  8. 34
      src/assets/iconfont/iconfont.css
  9. 2
      src/assets/iconfont/iconfont.js
  10. 49
      src/assets/iconfont/iconfont.json
  11. BIN
      src/assets/iconfont/iconfont.ttf
  12. BIN
      src/assets/iconfont/iconfont.woff
  13. BIN
      src/assets/iconfont/iconfont.woff2
  14. 14
      src/mock/routes.json
  15. 4
      src/permission.ts
  16. 7
      src/router/index.ts
  17. 6
      src/store/modules/user.ts
  18. 3
      src/styles/style.scss
  19. 10
      src/utils/index.ts
  20. 72
      src/utils/request.ts
  21. 228
      src/views/dashboard/components/BarChart.vue
  22. 77
      src/views/dashboard/components/LineChart.vue
  23. 154
      src/views/dashboard/components/PieChart.vue
  24. 15
      src/views/dashboard/index.scss
  25. 68
      src/views/dashboard/index.vue
  26. 2
      src/views/dataScreen/components/header.vue
  27. 16
      src/views/details/index.vue
  28. 2
      src/views/monitoring/components/header.vue
  29. 2
      src/views/monitoring/devicemanage/components/header.vue
  30. 96
      src/views/monitoring/devicemanage/components/main.vue
  31. 61
      src/views/monitoring/devicemanage/index.scss
  32. 2
      src/views/monitoring/screen/index.vue
  33. 149
      src/views/monitoring/screenData/components/infoPanel.vue
  34. 565
      src/views/monitoring/screenData/components/main.vue
  35. 68
      src/views/monitoring/screenData/components/menu.vue
  36. 299
      src/views/monitoring/screenData/components/showTree.vue
  37. 752
      src/views/monitoring/screenData/index.scss
  38. 200
      src/views/monitoring/screenData/index.vue

4
.env.production

@ -5,6 +5,6 @@ VITE_APP_PORT = 8089
#VITE_APP_WS_API = 'ws://board.heatiot.cn:8001/prod-api' ## websocket地址
#VITE_APP_BASE_API = 'http://board.heatiot.cn:8001/prod-api/' ## 线上接口
#VITE_APP_WS_API = 'ws://${window.location.host}/ws' ## websocket地址 ws://10.10.10.56:9000/websocket/
# VITE_APP_BASE_API = '/prod-api/' ## 正式环境
VITE_APP_BASE_API = '/biprod-api/' ## 测试环境
VITE_APP_BASE_API = '/prod-api/' ## 正式环境
# VITE_APP_BASE_API = '/biprod-api/' ## 测试环境

1
package.json

@ -33,6 +33,7 @@
"vue": "^3.2.45",
"vue-i18n": "^9.1.9",
"vue-router": "^4.1.6",
"vue3-seamless-scroll": "^2.0.1",
"vxe-table": "^4.5.6",
"xe-utils": "^3.5.12",
"xgplayer": "^3.0.2"

BIN
public/static/images/sanfasanqu.gif

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.3 MiB

After

Width:  |  Height:  |  Size: 8.2 MiB

BIN
public/static/images/sanfasanquyibeng.gif

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.4 MiB

After

Width:  |  Height:  |  Size: 7.9 MiB

BIN
public/static/images/yifaysanquyibeng.gif

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.6 MiB

After

Width:  |  Height:  |  Size: 6.5 MiB

15
src/api/table/list.ts

@ -45,7 +45,7 @@ export function getTableHeader(): AxiosPromise<[]> {
// }
/**
*
*
*
* @param data
*/
@ -57,6 +57,19 @@ export function getTableData(data: any) {
});
}
/**
*
*
* @param data
*/
export function getMockTableData(data: any) {
return request({
url: '/bi/opt/mock/getTableDataPage',
method: 'post',
data: data
});
}
/**
*
*

167
src/assets/iconfont/demo_index.html

@ -54,6 +54,48 @@
<div class="content unicode" style="display: block;">
<ul class="icon_lists dib-box">
<li class="dib">
<span class="icon iconfont">&#xe612;</span>
<div class="name">堆积面积图</div>
<div class="code-name">&amp;#xe612;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe7b1;</span>
<div class="name">air_conditioning_filter</div>
<div class="code-name">&amp;#xe7b1;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe60d;</span>
<div class="name">锅炉</div>
<div class="code-name">&amp;#xe60d;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe9fd;</span>
<div class="name">设备</div>
<div class="code-name">&amp;#xe9fd;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe84a;</span>
<div class="name">right-换热站-c</div>
<div class="code-name">&amp;#xe84a;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe690;</span>
<div class="name">30D上双箭头</div>
<div class="code-name">&amp;#xe690;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe691;</span>
<div class="name">30C下双箭头</div>
<div class="code-name">&amp;#xe691;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe601;</span>
<div class="name">多云</div>
@ -456,9 +498,9 @@
<pre><code class="language-css"
>@font-face {
font-family: 'iconfont';
src: url('iconfont.woff2?t=1703727917505') format('woff2'),
url('iconfont.woff?t=1703727917505') format('woff'),
url('iconfont.ttf?t=1703727917505') format('truetype');
src: url('iconfont.woff2?t=1706149491935') format('woff2'),
url('iconfont.woff?t=1706149491935') format('woff'),
url('iconfont.ttf?t=1706149491935') format('truetype');
}
</code></pre>
<h3 id="-iconfont-">第二步:定义使用 iconfont 的样式</h3>
@ -484,6 +526,69 @@
<div class="content font-class">
<ul class="icon_lists dib-box">
<li class="dib">
<span class="icon iconfont icon-area-chart"></span>
<div class="name">
堆积面积图
</div>
<div class="code-name">.icon-area-chart
</div>
</li>
<li class="dib">
<span class="icon iconfont icon-HP"></span>
<div class="name">
air_conditioning_filter
</div>
<div class="code-name">.icon-HP
</div>
</li>
<li class="dib">
<span class="icon iconfont icon-boiler"></span>
<div class="name">
锅炉
</div>
<div class="code-name">.icon-boiler
</div>
</li>
<li class="dib">
<span class="icon iconfont icon-device"></span>
<div class="name">
设备
</div>
<div class="code-name">.icon-device
</div>
</li>
<li class="dib">
<span class="icon iconfont icon-heat-exchange-station"></span>
<div class="name">
right-换热站-c
</div>
<div class="code-name">.icon-heat-exchange-station
</div>
</li>
<li class="dib">
<span class="icon iconfont icon-yincang"></span>
<div class="name">
30D上双箭头
</div>
<div class="code-name">.icon-yincang
</div>
</li>
<li class="dib">
<span class="icon iconfont icon-xianshi"></span>
<div class="name">
30C下双箭头
</div>
<div class="code-name">.icon-xianshi
</div>
</li>
<li class="dib">
<span class="icon iconfont icon-weather"></span>
<div class="name">
@ -1087,6 +1192,62 @@
<div class="content symbol">
<ul class="icon_lists dib-box">
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#icon-area-chart"></use>
</svg>
<div class="name">堆积面积图</div>
<div class="code-name">#icon-area-chart</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#icon-HP"></use>
</svg>
<div class="name">air_conditioning_filter</div>
<div class="code-name">#icon-HP</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#icon-boiler"></use>
</svg>
<div class="name">锅炉</div>
<div class="code-name">#icon-boiler</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#icon-device"></use>
</svg>
<div class="name">设备</div>
<div class="code-name">#icon-device</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#icon-heat-exchange-station"></use>
</svg>
<div class="name">right-换热站-c</div>
<div class="code-name">#icon-heat-exchange-station</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#icon-yincang"></use>
</svg>
<div class="name">30D上双箭头</div>
<div class="code-name">#icon-yincang</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#icon-xianshi"></use>
</svg>
<div class="name">30C下双箭头</div>
<div class="code-name">#icon-xianshi</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#icon-weather"></use>

34
src/assets/iconfont/iconfont.css

@ -1,8 +1,8 @@
@font-face {
font-family: "iconfont"; /* Project id 4223894 */
src: url('iconfont.woff2?t=1703727917505') format('woff2'),
url('iconfont.woff?t=1703727917505') format('woff'),
url('iconfont.ttf?t=1703727917505') format('truetype');
src: url('iconfont.woff2?t=1706149491935') format('woff2'),
url('iconfont.woff?t=1706149491935') format('woff'),
url('iconfont.ttf?t=1706149491935') format('truetype');
}
.iconfont {
@ -13,6 +13,34 @@
-moz-osx-font-smoothing: grayscale;
}
.icon-area-chart:before {
content: "\e612";
}
.icon-HP:before {
content: "\e7b1";
}
.icon-boiler:before {
content: "\e60d";
}
.icon-device:before {
content: "\e9fd";
}
.icon-heat-exchange-station:before {
content: "\e84a";
}
.icon-yincang:before {
content: "\e690";
}
.icon-xianshi:before {
content: "\e691";
}
.icon-weather:before {
content: "\e601";
}

2
src/assets/iconfont/iconfont.js

File diff suppressed because one or more lines are too long

49
src/assets/iconfont/iconfont.json

@ -5,6 +5,55 @@
"css_prefix_text": "icon-",
"description": "",
"glyphs": [
{
"icon_id": "4781603",
"name": "堆积面积图",
"font_class": "area-chart",
"unicode": "e612",
"unicode_decimal": 58898
},
{
"icon_id": "15731469",
"name": "air_conditioning_filter",
"font_class": "HP",
"unicode": "e7b1",
"unicode_decimal": 59313
},
{
"icon_id": "5037507",
"name": "锅炉",
"font_class": "boiler",
"unicode": "e60d",
"unicode_decimal": 58893
},
{
"icon_id": "23060488",
"name": "设备",
"font_class": "device",
"unicode": "e9fd",
"unicode_decimal": 59901
},
{
"icon_id": "25873889",
"name": "right-换热站-c",
"font_class": "heat-exchange-station",
"unicode": "e84a",
"unicode_decimal": 59466
},
{
"icon_id": "29522767",
"name": "30D上双箭头",
"font_class": "yincang",
"unicode": "e690",
"unicode_decimal": 59024
},
{
"icon_id": "29522768",
"name": "30C下双箭头",
"font_class": "xianshi",
"unicode": "e691",
"unicode_decimal": 59025
},
{
"icon_id": "1447",
"name": "多云",

BIN
src/assets/iconfont/iconfont.ttf

Binary file not shown.

BIN
src/assets/iconfont/iconfont.woff

Binary file not shown.

BIN
src/assets/iconfont/iconfont.woff2

Binary file not shown.

14
src/mock/routes.json

@ -17,7 +17,7 @@
"component": "monitoring/screen/index",
"name": "screen",
"meta": {
"title": "数据大屏",
"title": "数据监控",
"icon": "document",
"hidden": false,
"roles": ["biadmin"],
@ -47,6 +47,18 @@
"roles": ["biadmin"],
"keepAlive": true
}
},
{
"path": "/screenData",
"component": "screenData/index",
"name": "screenData",
"meta": {
"title": "供暖办",
"icon": "document",
"hidden": false,
"roles": ["biadmin"],
"keepAlive": true
}
}
]
}

4
src/permission.ts

@ -2,7 +2,7 @@
* Author: Fu Guobin
* Date: 2022/12/28
* Last Modified by: Fu Guobin
* Last Modified time: 2023/05/05
* Last Modified time: 2024/01/22
* Copyright:Daniel(Fu Guobin)
* Description:router配置
*/
@ -20,7 +20,7 @@ const whiteList = ['/login'];
router.beforeEach(async (to, from, next) => {
NProgress.start();
const hasToken = sessionStorage.getItem('userToken');
const hasToken = localStorage.getItem('userToken');
if (hasToken) {
if (to.path === '/login') {
// 如果已登录,跳转首页

7
src/router/index.ts

@ -2,7 +2,7 @@
* Author: Fu Guobin
* Date: 2022/12/28
* Last Modified by: Fu Guobin
* Last Modified time: 2023/05/12
* Last Modified time: 2024/01/22
* Copyright:Daniel(Fu Guobin)
* Description:router配置
*/
@ -39,6 +39,11 @@ export const constantRoutes: RouteRecordRaw[] = [
component: () => import('@/views/monitoring/screen/index.vue'),
meta: { hidden: true }
},
{
path: '/screenData',
component: () => import('@/views/monitoring/screenData/index.vue'),
meta: { hidden: true }
},
{
path: '/devicemanage',
component: () => import('@/views/monitoring/devicemanage/index.vue'),

6
src/store/modules/user.ts

@ -8,11 +8,11 @@ import { store } from '@/store';
import { LoginData } from '@/api/auth/types';
import { UserInfo } from '@/api/user/types';
import { useSessionStorage } from '@vueuse/core';
import { useStorage,useSessionStorage } from '@vueuse/core';
export const useUserStore = defineStore('user', () => {
// state
const token = useSessionStorage('userToken', '');
const token = useStorage('userToken', '');
const userInfo = useSessionStorage('userInfo', {});
const nickname = ref('');
const avatar = ref('');
@ -56,7 +56,7 @@ export const useUserStore = defineStore('user', () => {
roles.value = data.roles;
perms.value = data.permissions;
userInfo.value = user;
// useSessionStorage('userInfo', user);
// useStorage('userInfo', user);
resolve(data);
})
.catch(error => {

3
src/styles/style.scss

@ -10,6 +10,9 @@
.items-center {
align-items: center;
}
.items-end{
align-items: flex-end;
}
.column {
flex-direction: column;
}

10
src/utils/index.ts

@ -353,6 +353,16 @@ export function timePeriod(hours: number) {
}
}
/**
*
*/
export function getUrlRouter() {
var sHref = window.location.href;
var args = sHref.split('#');
var hrefarr = args[1].split('/')[1].split('?');
return hrefarr;
}
/**
* Check if an element has a class
* @param {HTMLElement} elm

72
src/utils/request.ts

@ -2,12 +2,13 @@
* Author: Fu Guobin
* Date: 2022/12/28
* Last Modified by: Fu Guobin
* Last Modified time: 2023/05/12
* Last Modified time: 2024/01/22
* Copyright:Daniel(Fu Guobin)
* Description:axios配置
*/
import axios, { InternalAxiosRequestConfig, AxiosResponse } from 'axios';
import { useUserStoreHook } from '@/store/modules/user';
import { getUrlRouter } from '@/utils/index';
// 创建 axios 实例
const service = axios.create({
@ -34,34 +35,61 @@ service.interceptors.request.use(
service.interceptors.response.use(
(response: AxiosResponse) => {
const { code, msg } = response.data;
if (code === 200) {
return response.data;
}
// if (code === 200) {
// return response.data;
// }
// 响应数据为二进制流处理(Excel导出)
if (response.data instanceof ArrayBuffer) {
return response;
}
ElMessage.error(msg || '系统出错');
return Promise.reject(new Error(msg || 'Error'));
if (code === 401) {
ElMessageBox.confirm('无效的会话,或者会话已过期,请重新登录', '提示', {
confirmButtonText: '确定',
type: 'warning'
}).then(() => {
localStorage.clear();
sessionStorage.clear();
if (getUrlRouter()[0] != 'login') {
window.location.href = '/';
}
});
} else if (code === 500) {
ElMessage({ message: msg || '未知错误', type: 'error' });
return Promise.reject(new Error(msg));
} else if (code === 601) {
ElMessage({ message: msg || '未知错误', type: 'warning' });
return Promise.reject(new Error(msg || 'Error'));
} else if (code !== 200) {
ElNotification.error({ title: msg });
return Promise.reject('error');
} else {
return response.data;
}
// if (code === 401) {
// ElMessageBox.confirm('当前页面已失效,请重新登录', '提示', {
// confirmButtonText: '确定',
// type: 'warning'
// }).then(() => {
// localStorage.clear();
// window.location.href = '/';
// });
// } else {
// ElMessage.error(msg || '未知错误');
// return Promise.reject(new Error(msg || 'Error'));
// }
},
(error: any) => {
if (error.response.data) {
const { code, msg } = error.response.data;
// token 过期,重新登录
if (code === '401') {
ElMessageBox.confirm('当前页面已失效,请重新登录', '提示', {
confirmButtonText: '确定',
type: 'warning'
}).then(() => {
localStorage.clear();
window.location.href = '/';
});
} else {
ElMessage.error(msg || '系统出错');
}
console.log('err' + error);
let { message } = error;
if (message == 'Network Error') {
message = '后端接口连接异常';
} else if (message.includes('timeout')) {
message = '系统接口请求超时';
} else if (message.includes('Request failed with status code')) {
message = '系统接口' + message.substr(message.length - 3) + '异常';
}
return Promise.reject(error.message);
ElMessage({ message: message, type: 'error', duration: 5 * 1000 });
return Promise.reject(error);
}
);

228
src/views/dashboard/components/BarChart.vue

@ -1,13 +1,14 @@
<!-- 线 + 柱混合图 -->
<template>
<el-card>
<template #header> 柱形图 </template>
<template #header> 进回均温排行 </template>
<div :id="id" :class="className" :style="{ height, width }" />
</el-card>
</template>
<script lang="ts" setup>
<script setup>
import * as echarts from 'echarts';
import { randomColor } from '@/utils/index';
const props = defineProps({
id: {
@ -30,52 +31,223 @@ const props = defineProps({
}
});
const defualData = [
{
cdate: '2016',
cname: '换热站1,换热站2,换热站3,换热站4,换热站5,换热站6,换热站7,换热站8,换热站9,换热站10',
cut: '20.9,13.82,3.6,0.56,1.49,9.48,32,21,65,19'
},
{
cdate: '2017',
cname: '换热站1,换热站2,换热站3,换热站4,换热站5,换热站6,换热站7,换热站8,换热站9,换热站10',
cut: '18.7,12.82,13.6,9.56,12.49,13.48,21,23,52,53'
},
{
cdate: '2018',
cname: '换热站1,换热站2,换热站3,换热站4,换热站5,换热站6,换热站7,换热站8,换热站9,换热站10',
cut: '14.9,18.82,7.6,19.56,13.49,17.48,80,33,63,42'
},
{
cdate: '2019',
cname: '换热站1,换热站2,换热站3,换热站4,换热站5,换热站6,换热站7,换热站8,换热站9,换热站10',
cut: '16.9,3.82,13.6,15.56,12.49,14.48,54,35,40,20'
},
{
cdate: '2020',
cname: '换热站1,换热站2,换热站3,换热站4,换热站5,换热站6,换热站7,换热站8,换热站9,换热站10',
cut: '3.8,8.82,13.6,21.56,15.49,19.2,16,19,24,52'
},
{
cdate: '2021',
cname: '换热站1,换热站2,换热站3,换热站4,换热站5,换热站6,换热站7,换热站8,换热站9,换热站10',
cut: '23.9,16.82,13.6,17.56,14.49,19.98,18,12,30,35'
},
{
cdate: '2022',
cname: '换热站1,换热站2,换热站3,换热站4,换热站5,换热站6,换热站7,换热站8,换热站9,换热站10',
cut: '12,23,46,52,39,32,25,28,31,37'
},
{
cdate: '2023',
cname: '换热站1,换热站2,换热站3,换热站4,换热站5,换热站6,换热站7,换热站8,换热站9,换热站10',
cut: '52,59,60,49,42,36,10,9,21,28'
}
];
const colors = ['#5470c6', '#91cc75', '#fac858', '#1a94bc', '#f26b1f', '#5bae23'];
const ccolors = {};
var updateFrequency = 1000; //
var years = [];
var startIndex = 0;
for (var i = 0; i < defualData.length; ++i) {
years.push(defualData[i]);
}
//
var startYear = years[startIndex].cdate;
var startName = years[startIndex].cname.split(',');
var startCut = years[startIndex].cut.split(',');
for (let i = 0; i < startName.length; i++) {
if (i < 6) {
ccolors[startName[i]] = colors[i];
} else {
ccolors[startName[i]] = randomColor();
}
}
console.log(ccolors);
const options = {
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
}
// tooltip: {
// trigger: 'axis',
// axisPointer: {
// type: 'shadow'
// }
// },
legend: {
right: 100,
itemWidth: 11,
itemHeight: 2,
color: '#fff',
fontSize: 14
},
grid: {
left: '3%',
right: '4%',
top: '3%',
bottom: '3%',
top: '0',
left: '0',
right: '0',
bottom: '0',
containLabel: true
},
xAxis: [
{
type: 'category',
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
axisTick: {
alignWithLabel: true
xAxis: {
max: 'dataMax',
type: 'value',
nameTextStyle: {
color: '#000',
fontSize: 12,
padding: [0, 0, 0, 0] //name
},
axisTick: {
//
show: false
},
axisLabel: {
show: true,
color: '#000',
formatter: function (n) {
return Math.round(n) + '';
}
},
splitLine: {
// 线
show: true,
lineStyle: {
//线
type: 'dashed',
color: '#306269',
width: 1,
opacity: 0.2
}
}
],
yAxis: [
{
type: 'value'
}
],
},
dataset: {
source: defualData
},
yAxis: {
type: 'category',
nameTextStyle: {
color: '#000',
fontSize: 12,
padding: [0, 0, 0, 0] //name
},
axisTick: {
//
show: false
},
axisLabel: {
show: true,
color: '#000',
rich: {
flag: {
fontSize: 25,
padding: 5
}
}
},
inverse: true,
animationDuration: 300, //
animationDurationUpdate: 300, //
data: startName
},
series: [
{
name: 'Direct',
// name: '2023',
type: 'bar',
barWidth: '60%',
data: [10, 52, 200, 334, 390, 330, 220]
barWidth: 20, //
showBackground: false,
realtimeSort: true, //
itemStyle: {
color: function (param) {
return ccolors[param.name];
}
},
encode: {
x: 0,
y: 3
},
label: {
show: true,
precision: 1,
position: 'right',
color: '#000',
valueAnimation: true //
},
emphasis: {
focus: 'series' //
},
data: startCut
}
]
],
animationDuration: 0,
animationDurationUpdate: 3000,
animationEasing: 'linear',
animationEasingUpdate: 'linear',
graphic: {
elements: [
{
type: 'text',
right: 120,
bottom: 100,
style: {
text: startYear,
font: 'bolder 80px monospace',
fill: 'rgba(100, 100, 100, 0.25)'
},
z: 100
}
]
}
};
onMounted(() => {
//
const chart = echarts.init(document.getElementById(props.id) as HTMLDivElement);
const chart = echarts.init(document.getElementById(props.id));
chart.setOption(options);
for (var i = startIndex; i < defualData.length - 1; ++i) {
(function (i) {
setTimeout(function () {
getDeviceInfos(years[i + 1]);
}, (i + 1) * updateFrequency);
})(i);
}
//
window.addEventListener('resize', () => {
chart.resize();
});
function getDeviceInfos(year) {
options.yAxis.data = year.cname.split(',');
options.series[0].data = year.cut.split(',');
options.graphic.elements[0].style.text = year.cdate;
const chart = echarts.init(document.getElementById(props.id));
chart.setOption(options);
}
});
</script>

77
src/views/dashboard/components/LineChart.vue

@ -1,7 +1,7 @@
<!-- 线 + 柱混合图 -->
<template>
<el-card>
<template #header> 折线图 </template>
<template #header> 一网进回水 </template>
<div :id="id" :class="className" :style="{ height, width }" />
</el-card>
</template>
@ -32,29 +32,78 @@ const props = defineProps({
const options = {
tooltip: {
trigger: 'axis'
trigger: 'axis',
axisPointer: {
type: 'cross'
}
},
legend: {
right: 0,
itemWidth: 11,
itemHeight: 2,
textStyle: {
color: '#000',
fontSize: 12
}
},
grid: {
left: '3%',
right: '4%',
top: '3%',
bottom: '3%',
top: '50',
left: '0',
right: '0',
bottom: '0',
containLabel: true
},
dataset: {
source: [
['product', '2024-01-01', '2024-01-02', '2024-01-03', '2024-01-04', '2024-01-05', '2024-01-06', '2024-01-07'],
['日均温', 56.5, 82.1, 88.7, 70.1, 53.4, 85.1, 52]
]
},
xAxis: {
type: 'category',
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
},
yAxis: {
type: 'value',
scale: true, //
minInterval: 1 //
axisLabel: {
show: true,
color: '#000'
}
},
yAxis: [
{
type: 'value',
name: '温度(℃)',
scale: true, //
nameTextStyle: {
color: '#000',
fontSize: 12,
padding: [0, 0, 0, 0] //name
},
axisTick: {
//
show: false
},
axisLabel: {
show: true,
color: '#000'
},
splitLine: {
// 线
show: true,
lineStyle: {
//线
color: '#306269',
width: 1,
opacity: 0.2
}
}
}
],
series: [
{
data: [820, 932, 901, 934, 1290, 1330, 1320],
type: 'line',
smooth: true
smooth: true,
seriesLayoutBy: 'row',
emphasis: {
focus: 'series'
}
}
]
};

154
src/views/dashboard/components/PieChart.vue

@ -1,13 +1,26 @@
<!-- 线 + 柱混合图 -->
<template>
<el-card>
<template #header> 饼形图 </template>
<div :id="id" :class="className" :style="{ height, width }" />
<template #header> 累计热量 </template>
<div class="table">
<div class="header">
<span class="cell">名称</span>
<span class="cell">累计热量</span>
<span class="cell">时间</span>
</div>
<vue3-seamless-scroll class="wrapper" :list="waterData" :step="1" hover wheel>
<div class="row" v-for="(item, index) in waterData" :key="index">
<span class="cell">{{ item.name }}</span>
<span class="cell">{{ item.field1 }}</span>
<span class="cell">{{ item.field2 }}</span>
</div>
</vue3-seamless-scroll>
</div>
</el-card>
</template>
<script lang="ts" setup>
import * as echarts from 'echarts';
import { Vue3SeamlessScroll } from 'vue3-seamless-scroll';
const props = defineProps({
id: {
@ -30,56 +43,97 @@ const props = defineProps({
}
});
const options = {
title: {
text: 'Referer of a Website',
left: 'center'
const waterData = ref([
{
name: '换热站1',
field1: '158889',
field2: '2024-01-25'
},
tooltip: {
trigger: 'item'
{
name: '换热站1',
field1: '158889',
field2: '2024-01-25'
},
legend: {
bottom: '0',
left: 'center'
{
name: '换热站1',
field1: '158889',
field2: '2024-01-25'
},
grid: {
left: '3%',
right: '4%',
top: '3%',
bottom: '3%',
containLabel: true
{
name: '换热站1',
field1: '158889',
field2: '2024-01-25'
},
series: [
{
name: 'Access From',
type: 'pie',
radius: '50%',
data: [
{ value: 1048, name: 'Search Engine' },
{ value: 735, name: 'Direct' },
{ value: 580, name: 'Email' },
{ value: 484, name: 'Union Ads' },
{ value: 300, name: 'Video Ads' }
],
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
}
}
]
};
onMounted(() => {
//
const chart = echarts.init(document.getElementById(props.id) as HTMLDivElement);
chart.setOption(options);
{
name: '换热站1',
field1: '158889',
field2: '2024-01-25'
},
{
name: '换热站1',
field1: '158889',
field2: '2024-01-25'
},
{
name: '换热站1',
field1: '158889',
field2: '2024-01-25'
},
{
name: '换热站1',
field1: '158889',
field2: '2024-01-25'
},
{
name: '换热站1',
field1: '158889',
field2: '2024-01-25'
}
]);
//
window.addEventListener('resize', () => {
chart.resize();
});
});
onMounted(() => {});
</script>
<style lang="scss" scoped>
.table {
width: 540px;
height: 300px;
padding: 10px;
.header {
display: flex;
justify-content: space-between;
text-align: center;
border-radius: 0px 0px 0px 0px;
border-bottom: 1px solid var(--el-card-border-color);
opacity: 1;
.cell {
width: 25%;
padding: 8px 0;
font-size: 14px;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}
}
.wrapper {
width: 100%;
height: 220px;
overflow: hidden;
.row {
display: flex;
justify-content: space-between;
text-align: center;
border-bottom: 1px solid var(--el-card-border-color);
opacity: 1;
.cell {
width: 25%;
padding: 8px 0;
font-size: 14px;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}
}
}
}
</style>

15
src/views/dashboard/index.scss

@ -11,18 +11,24 @@
margin-right: 10px;
}
.info {
.name{
font-size: 1.4rem;
span{
font-size: 1.2rem;
}
}
.time {
color: #9ca3af;
font-size: 12px;
font-size: 1.2rem;
}
}
}
.userNum {
.title {
color: #9ca3af;
font-size: 1.4rem;
}
span {
font-size: 22px;
font-size: 1.4rem;
}
}
// .currentTime{
@ -51,6 +57,9 @@
}
span {
font-size: 28px;
i{
font-style: normal;
}
}
}
}

68
src/views/dashboard/index.vue

@ -10,17 +10,23 @@
<div class="userName flex items-center">
<img :src="userStore.avatar === null || userStore.avatar === '' ? avatar : userStore.avatar" />
<div class="info flex column">
<div class="name">{{ userStore.nickname }}</div>
<div class="name">
{{ userStore.nickname }}
<span>{{ timePeriod }}</span>
</div>
<div class="time">{{ currentTime }}</div>
<div class="time">
{{ lunarDay.cnYear }}[{{ lunarDay.Animal }}] {{ lunarDay.IMonthCn }}{{ lunarDay.IDayCn }}
{{ lunarDay.Term }} {{ lunarDay.ncWeek }} {{ timePeriod }}
{{ lunarDay.Term }} {{ lunarDay.ncWeek }}
</div>
</div>
</div>
<div class="userNum flex column text-center">
<div class="title">用户数</div>
<span><countTo :start="1" :end="userNumber" :duration="5000"></countTo></span>
<div class="userNum flex column items-end">
<div class="title">{{ weatherData.city }}</div>
<span>
<svg-icon class="weatherSvg" :icon-class="weatherData.weatherImg" />
{{ weatherData.temperature }} {{ weatherData.weather }}
</span>
</div>
<!-- <div class="currentTime flex column text-center">
<span>{{ currentTime }}</span>
@ -43,7 +49,10 @@
<h3>{{ item.title }}</h3>
<div class="data">
<i class="iconfont" :class="item.icon"></i>
<span><countTo :start="1" :end="item.value" :duration="item.time"></countTo></span>
<span>
<countTo :start="1" :end="item.value" :duration="item.time"></countTo>
<span v-if="item.total!=0"><i>/</i>{{ item.total }}</span>
</span>
</div>
</div>
</el-col>
@ -69,6 +78,7 @@
<script lang="ts" setup>
import { useDateFormat, useNow, useTransition, TransitionPresets } from '@vueuse/core';
import { useUserStore } from '@/store/modules/user';
import { getWeather } from '@/api/user/index';
// import { transitionNum } from '@/utils/index';
import calendar from '@/utils/lunar';
import countTo from '@/utils/countTo';
@ -77,6 +87,8 @@ import BarChart from './components/BarChart.vue';
import PieChart from './components/PieChart.vue';
import avatar from '@/assets/images/avatar.png';
const timer = ref();
const isCurrentRoute = ref(true);
const userStore = useUserStore();
const duration = 5000;
const userNumber = ref(2800);
@ -88,6 +100,7 @@ const lunarDay: any = calendar.solarToLunar(
useNow().value.getUTCMonth() + 1,
useNow().value.getUTCDate()
);
const weatherData = ref({ city: '', weather: '', temperature: '', weatherImg: '' });
interface CardData {
id: string;
@ -96,40 +109,45 @@ interface CardData {
time: number;
colors: [string, string];
icon: string;
total: number;
}
const cardData: CardData[] = [
{
id: '1',
title: '访问量',
title: '供热面积',
value: 50000,
time: 5000,
colors: ['#ec4786', '#b955a4'],
icon: 'icon-user-visits'
icon: 'icon-area-chart',
total: 0
},
{
id: '2',
title: '成交额',
value: 8000000,
title: '产热量',
value: 200,
time: 5000,
colors: ['#865ec0', '#5144b4'],
icon: 'icon-turnover'
icon: 'icon-HP',
total: 8000
},
{
id: '3',
title: '订单数',
title: '换热站',
value: 2000,
time: 5000,
colors: ['#56cdf3', '#719de3'],
icon: 'icon-num-download'
icon: 'icon-heat-exchange-station',
total: 0
},
{
id: '4',
title: '成交数',
title: '设备',
value: 9000,
time: 5000,
colors: ['#fcbc25', '#f68057'],
icon: 'icon-num-transactions'
icon: 'icon-device',
total: 100000
}
];
@ -149,7 +167,25 @@ const timePeriod = computed(() => {
}
});
onMounted(() => {});
onMounted(() => {
getWeatherData();
});
function getWeatherData() {
//
getWeather().then(res => {
if (res.code === 200) {
if (isCurrentRoute.value) {
timer.value = setTimeout(async () => {
await (timer.value && clearTimeout(timer.value));
await getWeatherData();
}, 600000);
}
weatherData.value = res.data;
} else {
clearTimeout(timer.value);
}
});
}
</script>
<style lang="scss" scoped>

2
src/views/dataScreen/components/header.vue

@ -171,7 +171,7 @@ const lunarDay: any = calendar.solarToLunar(
const props = defineProps({
titleData: {
type: String,
default: '数据大屏'
default: '数据监控'
},
settingShow: {
type: Boolean,

16
src/views/details/index.vue

@ -180,7 +180,7 @@
</svg>
</div>
</el-card>
<el-card class="echart" :class="{'echartHide':!searchShow }" shadow="always">
<el-card class="echart" :class="{ echartHide: !searchShow }" shadow="always">
<div class="sidebar">
<h3>换热站</h3>
<el-menu :default-active="filterForm.deviceUuid" class="deviceMenu" @select="deviceSelect" @open="deviceOpen">
@ -216,7 +216,9 @@
</div>
</div>
</el-card>
<div class="copyright">Copyright © {{ $t('copyright.year') }} {{ $t('copyright.abbr') }} {{ $t('copyright.document') }}</div>
<div class="copyright">
Copyright © {{ $t('copyright.year') }} {{ $t('copyright.abbr') }} {{ $t('copyright.document') }}
</div>
</div>
</template>
<script lang="ts" setup>
@ -987,7 +989,7 @@ function init(data: any) {
window.addEventListener('resize', function () {
chart.resize();
});
mapEcharts.value=chart
mapEcharts.value = chart;
chart.resize();
console.log('options--', options);
chart.setOption(options);
@ -1113,7 +1115,13 @@ function verifyInfo() {
function searchClick(type: string) {
///
searchShow.value = type === 'hide' ? false : true;
verifyInfo();
if (filterForm.deviceUuid === '') {
} else if (filterForm.startTime === '') {
} else if (filterForm.step === null) {
} else if (filterForm.paramCode === '') {
} else {
getDeviceInfo();
}
}
// const submitForm = (formEl: FormInstance | undefined) => {
// //

2
src/views/monitoring/components/header.vue

@ -211,7 +211,7 @@ const lunarDay = calendar.solarToLunar(
const props = defineProps({
titleData: {
type: String,
default: '数据大屏'
default: '数据监控'
},
settingShow: {
type: Boolean,

2
src/views/monitoring/devicemanage/components/header.vue

@ -171,7 +171,7 @@ const lunarDay: any = calendar.solarToLunar(
const props = defineProps({
titleData: {
type: String,
default: '数据大屏'
default: '数据监控'
},
settingShow: {
type: Boolean,

96
src/views/monitoring/devicemanage/components/main.vue

@ -12,14 +12,10 @@
<div class="dataInfo" v-html="doubleCount('007', 0)"></div>
</div>
<div class="card card_c">
<div class="dataInfo dataFlex">
<div class="flexItem" v-html="doubleCount('047', 0)"></div>
<div class="flexItem" v-html="doubleCount('045', 0)"></div>
</div>
<div class="dataInfo dataFlex">
<div class="flexItem" v-html="doubleCount('048', 0)"></div>
<div class="flexItem" v-html="doubleCount('046', 0)"></div>
</div>
<div class="dataInfo" v-html="doubleCount('047', 0)"></div>
<div class="dataInfo" v-html="doubleCount('045', 0)"></div>
<div class="dataInfo" v-html="doubleCount('048', 0)"></div>
<div class="dataInfo" v-html="doubleCount('046', 0)"></div>
</div>
<div class="card card_f">
<div class="dataInfo" v-html="doubleCount('015', 0)"></div>
@ -39,26 +35,20 @@
<div class="card card_b">
<div class="dataInfo" v-html="doubleCount('002', 0)"></div>
<div class="dataInfo" v-html="doubleCount('007', 0)"></div>
<div class="dataInfo" v-html="doubleCount('002', 1)"></div>
<div class="dataInfo" v-html="doubleCount('007', 1)"></div>
</div>
<div class="card card_c">
<div class="dataInfo dataFlex">
<div class="flexItem" v-html="doubleCount('047', 0)"></div>
<div class="flexItem" v-html="doubleCount('045', 0)"></div>
</div>
<div class="dataInfo dataFlex">
<div class="flexItem" v-html="doubleCount('048', 0)"></div>
<div class="flexItem" v-html="doubleCount('046', 0)"></div>
</div>
<div class="dataInfo" v-html="doubleCount('047', 0)"></div>
<div class="dataInfo" v-html="doubleCount('045', 0)"></div>
<div class="dataInfo" v-html="doubleCount('048', 0)"></div>
<div class="dataInfo" v-html="doubleCount('046', 0)"></div>
</div>
<div class="card card_d">
<div class="dataInfo dataFlex">
<div class="flexItem" v-html="doubleCount('047', 1)"></div>
<div class="flexItem" v-html="doubleCount('045', 1)"></div>
</div>
<div class="dataInfo dataFlex">
<div class="flexItem" v-html="doubleCount('048', 1)"></div>
<div class="flexItem" v-html="doubleCount('046', 1)"></div>
</div>
<div class="dataInfo" v-html="doubleCount('047', 1)"></div>
<div class="dataInfo" v-html="doubleCount('045', 1)"></div>
<div class="dataInfo" v-html="doubleCount('048', 1)"></div>
<div class="dataInfo" v-html="doubleCount('046', 1)"></div>
</div>
<div class="card card_f">
<div class="dataInfo" v-html="doubleCount('015', 0)"></div>
@ -75,37 +65,37 @@
</div>
<div class="card card_b">
<div class="dataInfo" v-html="doubleCount('002', 0)"></div>
<div class="dataInfo" v-html="doubleCount('002', 1)"></div>
<div class="dataInfo" v-html="doubleCount('002', 2)"></div>
<div class="dataInfo" v-html="doubleCount('007', 0)"></div>
<div class="dataInfo" v-html="doubleCount('007', 1)"></div>
<div class="dataInfo" v-html="doubleCount('007', 2)"></div>
</div>
<!-- <div class="card card_g">
<div class="dataInfo" v-html="doubleCount('002', 1)"></div>
<div class="dataInfo" v-html="doubleCount('007', 1)"></div>
</div>
<div class="card card_h">
<div class="dataInfo" v-html="doubleCount('002', 2)"></div>
<div class="dataInfo" v-html="doubleCount('007', 2)"></div>
</div> -->
<div class="card card_c">
<div class="dataInfo dataFlex">
<div class="flexItem" v-html="doubleCount('047', 0)"></div>
<div class="flexItem" v-html="doubleCount('045', 0)"></div>
</div>
<div class="dataInfo dataFlex">
<div class="flexItem" v-html="doubleCount('048', 0)"></div>
<div class="flexItem" v-html="doubleCount('046', 0)"></div>
</div>
<div class="dataInfo" v-html="doubleCount('047', 0)"></div>
<div class="dataInfo" v-html="doubleCount('045', 0)"></div>
<div class="dataInfo" v-html="doubleCount('048', 0)"></div>
<div class="dataInfo" v-html="doubleCount('046', 0)"></div>
</div>
<div class="card card_d">
<div class="dataInfo dataFlex">
<div class="flexItem" v-html="doubleCount('047', 1)"></div>
<div class="flexItem" v-html="doubleCount('045', 1)"></div>
</div>
<div class="dataInfo dataFlex">
<div class="flexItem" v-html="doubleCount('048', 1)"></div>
<div class="flexItem" v-html="doubleCount('046', 1)"></div>
</div>
<div class="dataInfo" v-html="doubleCount('047', 1)"></div>
<div class="dataInfo" v-html="doubleCount('045', 1)"></div>
<div class="dataInfo" v-html="doubleCount('048', 1)"></div>
<div class="dataInfo" v-html="doubleCount('046', 1)"></div>
</div>
<div class="card card_e">
<div class="dataInfo dataFlex">
<div class="flexItem" v-html="doubleCount('047', 2)"></div>
<div class="flexItem" v-html="doubleCount('045', 2)"></div>
</div>
<div class="dataInfo dataFlex">
<div class="flexItem" v-html="doubleCount('048', 2)"></div>
<div class="flexItem" v-html="doubleCount('046', 2)"></div>
</div>
<div class="dataInfo" v-html="doubleCount('047', 2)"></div>
<div class="dataInfo" v-html="doubleCount('045', 2)"></div>
<div class="dataInfo" v-html="doubleCount('048', 2)"></div>
<div class="dataInfo" v-html="doubleCount('046', 2)"></div>
</div>
<div class="card card_f">
<div class="dataInfo" v-html="doubleCount('015', 0)"></div>
@ -384,12 +374,16 @@ onUnmounted(() => {
// };
// });
const doubleCount = (id: string, index: number) => {
const label = infoData.value[index].label;
const arr = infoData.value[index].header_main;
const obj = arr.find((item: any) => item.id === id);
const lable = obj != undefined ? `<span>${obj.label}</span><span>${obj.value} ${obj.paramUnit}</span>` : '';
const obj = arr?.find((item: any) => item.id === id);
const mainLable =
obj != undefined && obj?.value !== ''
? `<span>${obj.label}(${label}):</span><span>${obj.value === '' ? '--' : obj.value} ${obj.paramUnit}</span>`
: '';
// const lable = obj != undefined ? `${obj.label}` : ''
// const value = obj != undefined ? `${obj.value}` : '0.0'
return lable;
return mainLable;
};
console.log('doubleCount-', doubleCount);

61
src/views/monitoring/devicemanage/index.scss

@ -142,10 +142,9 @@
overflow: hidden;
.dataInfo {
padding: 5px 0;
span {
display: block;
display: inline-block;
padding: 5px 0;
}
}
@ -161,6 +160,13 @@
}
}
}
// .card.card_a{
// .dataInfo{
// span{
// display: inline-block;
// }
// }
// }
// .switch {
// position: absolute;
// z-index: 99;
@ -185,23 +191,23 @@
.deviceImgA {
.card_a {
top: 40%;
left: 5%;
top: 50%;
left: 1%;
}
.card_b {
top: 5%;
left: 5%;
top: 18%;
left: 1%;
}
.card_c {
top: 30%;
right: 3%;
top: 35%;
right: 1%;
}
.card_f {
top: 30%;
right: 20%;
top: 35%;
right: 17%;
}
// .switch_a {
// width: 35px;
@ -222,41 +228,51 @@
.deviceImgB {
.card_a {
top: 42%;
left: 5%;
left: 2%;
}
.card_b {
top: 8%;
left: 5%;
left: 2%;
}
.card_c {
top: 16%;
right: 3%;
right: 2%;
}
.card_d {
top: 52%;
right: 3%;
right: 2%;
}
.card_f {
top: 30%;
right: 21%;
right: 18%;
}
}
.deviceImgC {
.card_a {
top: 6%;
left: 6%;
left: 4%;
}
.card_b {
top: 62%;
left: 5%;
top: 40%;
left: 1%;
}
// .card_g {
// top: 60%;
// left: 5%;
// }
// .card_h {
// top: 80%;
// left: 5%;
// }
.card_c {
top: 2%;
right: 2%;
@ -268,13 +284,13 @@
}
.card_e {
top: 67%;
top: 68%;
right: 2%;
}
.card_f {
top: 30%;
right: 21%;
top: 36%;
right: 18%;
}
}
@ -718,6 +734,7 @@
font-family: Arial;
font-size: 12px;
letter-spacing: 1px;
margin-top: 5px;
}
}

2
src/views/monitoring/screen/index.vue

@ -78,7 +78,7 @@
<i class="iconfont icon-angle-double" :class="menuShow ? 'left' : 'right'"></i>
</div>
<div class="panelBtn" :class="menuShow ? 'left' : 'right'" @click="panelClick">
<i class="iconfont icon-arrow-up" :class="!panelShow ? 'up' : 'down'"></i>
<i class="iconfont icon-up" :class="!panelShow ? 'up' : 'down'"></i>
<div class="copyright">Copyright © {{ $t('copyright.year') }} {{ $t('copyright.abbr') }} {{ $t('copyright.document') }}</div>
<!-- <n-button type="info" size="tiny" ghost @click="panelClick">按钮</n-button> -->
</div>

149
src/views/monitoring/screenData/components/infoPanel.vue

@ -0,0 +1,149 @@
<template>
<div class="infoPanel">
<swiper
class="swiper"
:loop="false"
:autoplay="{ delay: 500000, pauseOnMouseEnter: true, disableOnInteraction: false }"
:modules="modules"
:slides-per-view="4"
:space-between="15"
navigation
:pagination="{ clickable: true }"
>
<swiper-slide class="item" v-for="(item, index) in panelData" :key="index">
<div class="content">
<div class="icon">
<img src="@/assets/images/panel_icon.png" />
</div>
<div class="numValue" v-if="item.type === 'A'">
<span>
<countTo :start="1" :end="item.value" :duration="3000"></countTo>
</span>
<i>{{ item.ext }}</i>
<p>{{ item.title }}</p>
</div>
<div class="numValueMore" v-if="item.type === 'B'">
<div class="numItem" v-for="(res, index) in item.extJsb.extData" :key="index">
<span>{{ res.name }}</span>
<span><countTo :start="1" :end="res.value" :duration="3000"></countTo></span>
<span>{{ res.unit }}</span>
</div>
</div>
</div>
</swiper-slide>
</swiper>
</div>
</template>
<script lang="ts" setup>
import { Swiper, SwiperSlide } from 'swiper/vue';
import { Navigation, Pagination, Autoplay } from 'swiper/modules';
import { getTableFooter } from '@/api/table/list';
import { PanelVo } from '@/api/table/types';
import countTo from '@/utils/countTo';
import mitt from '@/plugins/bus';
import 'swiper/css';
const modules = [Navigation, Pagination, Autoplay];
const panelData = ref<PanelVo[]>();
const deptId = ref(0);
// const panelData: PanelVo[] = [
// {
// title: '',
// value: 26449,
// ext: 'KJ',
// backImg: '/poll/hot',
// type: 'A',
// id: 2
// },
// {
// title: '',
// value: 9889,
// ext: 'm³',
// backImg: '/poll/onewater',
// type: 'A',
// id: 3
// },
// {
// title: '线',
// value: 40.425532,
// ext: '%',
// backImg: '/poll/dev',
// type: 'A',
// id: 1
// },
// {
// title: '',
// value: 27893,
// ext: '',
// backImg: '/poll/alert',
// type: 'A',
// id: 4
// },
// {
// title: '',
// value: 0,
// ext: [
// {
// name:'',
// value:'58996',
// ext:''
// },
// {
// name:'',
// value:'58996',
// ext:''
// },
// {
// name:'',
// value:'58996',
// ext:''
// },
// {
// name:'',
// value:'32369',
// ext:'t/h'
// },
// {
// name:'',
// value:'58996',
// ext:''
// },
// {
// name:'',
// value:'58996',
// ext:''
// },
// {
// name:'',
// value:'8956',
// ext:'T'
// }
// ],
// backImg: '/poll/alert',
// type: 'B',
// id: 5
// }
// ];
onMounted(() => {
deptId.value = sessionStorage.getItem('deptId') === null ? 0 : Number(sessionStorage.getItem('deptId'));
getPanel();
});
mitt.on('menuKey', (res: any) => {
//
deptId.value = res;
getPanel();
});
function getPanel() {
//
const params = deptId.value;
getTableFooter(params).then((res: any) => {
if (res.code === 200) {
panelData.value = res.data;
}
});
}
</script>

565
src/views/monitoring/screenData/components/main.vue

@ -0,0 +1,565 @@
<template>
<div class="headerInfo" v-show="tapsShow">
<div class="header">
<div class="headerItem" v-for="(item, index) in headerData" :key="index">
<div class="name">{{ item.name }}</div>
<div class="value">{{ item.value }}</div>
</div>
</div>
</div>
<!-- <div
class="mainTable"
:style="{ height: tapsShow ? 'calc(100vh - 5.7rem - 222px)' : 'calc(100vh - 5.7rem - 60px)' }"
> -->
<div class="mainTable" :style="{ height: `${sidebarHeight}px` }">
<!-- <n-spin :show="loadingShow"> -->
<vxe-grid
ref="tableRef"
class="tableGrid"
align="center"
auto-resize
keep-source
:height="sidebarHeight - 4"
header-row-class-name="headerRowClass"
header-cell-class-name="headerCellClass"
row-class-name="tableRowClass"
cell-class-name="tableCellClass"
:sort-config="{ multiple: true, trigger: 'cell' }"
:stripe="!tableBorder"
:border="tableBorder"
:column-config="{ resizable: true, useKey: true }"
:row-config="{ useKey: true }"
:span-method="mergeRowMethod"
:columns="tableColumn"
:data="tableData"
:loading="loadingShow"
show-overflow
@cell-dblclick="cellDBLClickEvent"
>
<template #deviceuuid_default="{ row }">
<div class="title">
<svg-icon
icon-class="warning_lights"
style="fill: currentColor; width: 15px; height: 15px; color: green"
v-if="row.deviceuuid.deviceStatus === 0"
/>
<svg-icon
icon-class="warning_lights"
style="fill: currentColor; width: 15px; height: 15px; color: red"
v-if="row.deviceuuid.deviceStatus === 2"
/>
<span class="name" @click.native="nameClick(row.deviceuuid)">{{ row.gTitle }}</span>
</div>
</template>
<template #pager>
<!--使用 pager 插槽-->
<vxe-pager
class="tablePage"
:layouts="['Sizes', 'PrevJump', 'PrevPage', 'Number', 'NextPage', 'NextJump', 'FullJump', 'Total']"
v-model:current-page="tablePage.currentPage"
v-model:page-size="tablePage.pageSize"
auto-hidden
:total="tablePage.total"
@page-change="handlePageChange"
>
</vxe-pager>
</template>
</vxe-grid>
<!-- <vxe-grid class="tableGrid" v-bind="gridOptions"> </vxe-grid> -->
<!-- </n-spin> -->
</div>
<vxe-modal :title="modalTitle" v-model="editModal">
<template #default>
<vxe-form
title-colon
ref="formRef"
title-align="right"
title-width="100"
:data="formData"
:loading="formLoading"
@submit="submitEvent"
@reset="resetEvent"
>
<vxe-form-item field="value" span="24" :item-render="{}" title-overflow>
<template #default="params">
<vxe-switch
v-model="params.data.value"
size="small"
open-label="启"
close-label="停"
v-if="params.data.valueType === 'bool'"
></vxe-switch>
<vxe-input
v-model="params.data.value"
placeholder="请输入数值"
type="number"
clearable
v-if="params.data.valueType != 'bool'"
></vxe-input>
<!-- <vxe-input
v-model="params.data.value"
placeholder="请输入数值"
clearable
v-if="params.data.valueType != 'bool'"
></vxe-input> -->
</template>
</vxe-form-item>
<vxe-form-item align="center" span="24">
<template #default>
<vxe-button type="submit" status="primary" content="确认"></vxe-button>
<vxe-button type="reset" content="重置"></vxe-button>
</template>
</vxe-form-item>
</vxe-form>
</template>
</vxe-modal>
</template>
<script lang="ts" setup>
import router from '@/router';
import {
VxeGridProps,
VxeGridInstance,
VxeTableEvents,
VxeColumnPropTypes,
VXETable,
VxeFormInstance,
VxeFormPropTypes,
VxeFormEvents,
VxeTablePropTypes,
VxePagerEvents
} from 'vxe-table';
import { getTableHeader, getStationInfo, getMockTableData, editConfig, sendCtrl } from '@/api/table/list';
import { TableVo } from '@/api/table/types';
import { tableStore } from '@/store/modules/table';
import mitt from '@/plugins/bus';
import socket from '@/utils/socket';
import useStorage from '@/utils/useStorage';
import { useUserStoreHook } from '@/store/modules/user';
const sessionStorageIns = useStorage('sessionStorage');
const { perms } = useUserStoreHook();
const requiredPerms = ['model:device:contrl']; //
const controlPerm = perms?.some(perm => {
return requiredPerms.includes(perm);
});
const tableStoreCounter = tableStore();
// import type { MenuOption } from 'naive-ui'
// import { useMessage } from 'naive-ui'
// const message = useMessage()
// const mainHeight = ref('calc(100vh - 161px)');
const loadingShow = ref(false);
const editModal = ref(false);
const modalTitle = ref('');
const menuKey = ref(0);
const tableColumn = ref([]);
const tableData = ref<TableVo[]>([]);
const tableRef = ref<VxeGridInstance<TableVo>>();
const cellRow = ref({});
const cellColumn = ref();
const cellField = ref();
const tableBorder = ref(true);
const timer = ref();
const isCurrentRoute = ref(true);
const tablePage = reactive({
total: 0,
currentPage: 1,
pageSize: 10
});
// const fields = ref(['deviceuuid']) //
// const waringArrow = ref([])
interface FormDataVO {
centeruuid: string;
paramcode: string;
url: string;
valueType: string;
value: string | boolean;
}
const formRef = ref<VxeFormInstance>();
const formLoading = ref(false);
const formData = ref<FormDataVO>({
centeruuid: '',
paramcode: '',
url: '',
valueType: '',
value: ''
});
// const formRules = ref<VxeFormPropTypes.Rules>({
// value: [
// { required: true, message: '' },
// { min: 1, max: 100, message: ' 1 100 ' },
// {
// validator({ itemValue }) {
// //
// const reg = /^-?\d+\.?\d{0,2}$/;
// if (!reg.test(itemValue)) {
// return new Error('');
// }
// }
// }
// ]
// });
const userStorageInfo = sessionStorage.getItem('userInfo');
const userInfo = JSON.parse(userStorageInfo === null ? '' : userStorageInfo);
//const apiUrl = import.meta.env.VITE_APP_WS_API
//const wsUrl = `${apiUrl}websocket/${userInfo.userName}`; //websocket
//const wsUrl = `${apiUrl}${userInfo.userName}`; //websocket
// const loginIp = userInfo.loginIp.split('.').join('');
// const baseApi = "http://172.1.2.106:9000"//websocket
// const baseApi = "http://board.heatiot.cn:8001/prod-api"//websocket
//const baseApi = import.meta.env.VITE_APP_BASE_API
//const apiUrl = baseApi.replace(/https?:/, '');
// const wsUrl = `ws://${window.location.host}/ws/websocket/${userInfo.userName}`; //websocket
// const wsUrl = `ws://10.10.10.56:9010/websocket/${userInfo.userName}`; //websocket
const emit = defineEmits(['tableHeaderData']);
// const listData = ref([
// {
// name: 'area_default',
// value: 1,
// },
// ]);
defineProps({
tapsShow: {
type: Boolean,
default: true
},
sidebarHeight: {
type: Number,
default: 0
}
});
// watch(
// () => tableStoreCounter.tableDataStore,
// (newValue, oldValue) => {
// console.log('', newValue, oldValue);
// const $table = tableRef.value;
// $table.loadData(newValue);
// }
// );
// watchEffect(() => {
// const titleRef = props.tapsShow;
// console.log(tableStoreCounter.tableDataStore);
// tableData.value = tableStoreCounter.tableDataStore;
// $table.loadData(tableStoreCounter.tableDataStore);
// });
interface HeaderVo {
code: string;
name: string;
value: string;
}
const headerData = ref<HeaderVo[]>();
onMounted(() => {
// stationInfo();
// tableHeader();
console.log('aaaa', sessionStorage.getItem('currentPage'));
tablePage.currentPage =
sessionStorage.getItem('currentPage') === null ? 1 : Number(sessionStorage.getItem('currentPage'));
console.log(Number(sessionStorage.getItem('currentPage')));
tablePage.pageSize = sessionStorage.getItem('pageSize') === null ? 10 : Number(sessionStorage.getItem('pageSize'));
// socket.initialize(wsUrl); //websocket http://
});
onUnmounted(() => {
//setTimeout
if (timer.value !== null || timer.value !== undefined) {
clearTimeout(timer.value);
}
});
mitt.on('currentPageEmit', (res: any) => {
//
tablePage.currentPage = res;
});
mitt.on('menuKey', (res: any) => {
//
menuKey.value = res;
tableHeader();
tableDatas();
});
mitt.on('treeData', (res: any) => {
//header
tableColumn.value = [];
nextTick(() => {
tableColumn.value = res;
});
const params = res;
editConfig(params).then((res: any) => {
if (res.code === 200) {
mitt.emit('treeClose', true);
}
});
// tableColumn.value=res
// gridOptions.columns?.push(...res);
// console.log('', gridOptions.columns);
});
// mitt.on('tableMessage', (res: any) => {
// //
// console.log('tableMessage--', res.data);
// const $table = tableRef.value;
// const tableArray = tableData.value;
// if ($table) {
// console.log('tableRef--', tableRef.value);
// res.data.map((item: any) => {
// const index = tableArray.findIndex(obj => obj.id === item.id);
// if (index !== -1) {
// tableArray.splice(index, 1, item);
// }
// });
// console.log('tableData--', tableArray);
// $table.loadData(tableArray);
// if (res.code === 'datareal') {
// const index = tableData.value.findIndex((obj) => obj.id === res.data.id);
// if (index !== -1) {
// tableData.value.splice(index, 1, res.data);
// }
// res.data.map((item: any) => {
// console.log(item)
// const index = tableData.value.findIndex((obj) => obj.id === item.id);
// if (index !== -1) {
// tableData.value.splice(index, 1, res.data);
// }
// })
// $table.loadData(mergedArray);
// }
// } else if (res.code === 'alertDev') {
// waringArrow.value.push(res.data)
// waringArrow.value = uniqueArrayObject(waringArrow.value, "devUuid")
// }
// console.log("waringArrow:", waringArrow.value)
// });
const handlePageChange: VxePagerEvents.PageChange = ({ currentPage, pageSize }) => {
tablePage.currentPage = currentPage;
tablePage.pageSize = pageSize;
sessionStorageIns.setUseStorage('currentPage', currentPage);
sessionStorageIns.setUseStorage('pageSize', pageSize);
tableDatas();
};
function stationInfo() {
//
getStationInfo().then((res: any) => {
if (res.code === 200) {
headerData.value = res.data;
}
});
}
function tableHeader() {
//header
getTableHeader().then((res: any) => {
console.log(res);
//
const tableCessText = [
{
id: 0,
title: 'ID',
field: 'id',
type: 'html',
formatter: formatRole,
visible: false
}
];
console.log(tableCessText);
res.data.map((item: any) => {
if (item.formatter != undefined || item.children != undefined) {
item.formatter = eval(item.formatter);
if (item.children && item.children.length) {
item.children.map((res: any) => {
res.formatter = eval(res.formatter);
});
}
}
});
nextTick(() => {
tableColumn.value = res.data;
});
// tableColumn.value = res.data;
// const arr = [
// {
// id: 1,
// title: '',
// field: 'deviceuuid',
// colSort: 2,
// colType: '2',
// width: 150,
// show: true,
// disabled: true,
// fixed: 'left',
// type: 'html',
// formatter: "formatRole",
// },
// ];
// arr.map((item)=>{
// item.formatter=eval(item.formatter);
// })
// console.log(arr)
// tableColumn.value = arr;
// gridOptions.columns = res.data;
// console.log('', gridOptions.value.columns);
emit('tableHeaderData', res.data);
// mitt.emit('tableHeaderData', res.data);
// tableDatas();
});
}
function tableDatas() {
//
// const params = menuKey.value;
const params = {
pageNum: tablePage.currentPage,
pageSize: tablePage.pageSize,
orgCode: menuKey.value
};
loadingShow.value = true;
getMockTableData(params).then((res: any) => {
if (res.code === 200) {
// tableData.value=oldData
tableData.value = res.rows;
tableStoreCounter.tableDataAction(res.rows);
tablePage.total = res.total;
// gridOptions.data = res.data;
loadingShow.value = false;
asyncTableDatas();
}
});
}
function asyncTableDatas() {
//
// const params = menuKey.value;
const params = {
pageNum: tablePage.currentPage,
pageSize: tablePage.pageSize,
orgCode: menuKey.value
};
getMockTableData(params).then((res: any) => {
if (res.code === 200) {
if (isCurrentRoute.value) {
timer.value = setTimeout(async () => {
await (timer.value && clearTimeout(timer.value));
await asyncTableDatas();
}, 5000);
}
tableData.value = res.rows;
tableStoreCounter.tableDataAction(res.rows);
tablePage.total = res.total;
}
});
}
const cellDBLClickEvent: VxeTableEvents.CellDblclick<TableVo> = ({ row, column }) => {
//
//edit ty zhousq 2023-10-12 formdata
// ctrlPro.centeruuid UUID cellField.ctrlPro.paramcode cellField.val
//ctrlPro
console.log('cellData--', row, column);
const cellField = row[column.field];
//const data = row.data;
cellRow.value = row;
cellColumn.value = column;
cellField.value = cellField;
if (cellField.canBeControl === '1' && controlPerm) {
modalTitle.value = column.title;
///formData.value.url = data.url;
//formData.value.deviceName = cellField.deviceName;
formData.value = cellField.ctrlPro;
formData.value.value = cellField.val;
// formData.value.paramCode = cellField.ctrlPro.paramcode;
editModal.value = true;
}
console.log(row[column.field]);
};
const formatRole: VxeColumnPropTypes.Formatter<HeaderVo> = ({ cellValue }) => {
//object
// console.log(cellValue);
// const iconFont=cellValue.changeProp===-1?'<i class="iconfont icon-decline" />':(cellValue.changeProp===1?'<i class="iconfont icon-rise" />':'<i/>')
// const cellData = `<span class="cellClass ${cellValue.alertProp===1?'warning':''}">${cellValue.val}</span>${iconFont}`;
// const cellData = `<span class="cellClass ${cellValue.alertProp === 1 ? 'warning' : cellValue.canBeControl === '1' ? 'cellEdit' : ''}">${cellValue.val}</span><i class="iconfont ${cellValue.changeProp === -1 ? 'icon-decline' : cellValue.changeProp === 1 ? 'icon-rise' : ''}" ></i>${cellValue.canBeControl === '1' ? '<i class="iconfont icon-edit-icon"></i>' : ''}`;
// const cellData = `
// <span class="cellClass ${cellValue.alertProp === 1 ? 'warning' : cellValue.canBeControl === '1' ? 'cellEdit' : ''}">
// ${cellValue.ctrlPro.valueType != 'bool' ? cellValue.val : cellValue.val === 'true' ? '' : ''}
// </span>
// <i class="iconfont ${
// cellValue.changeProp === -1 ? 'icon-decline' : cellValue.changeProp === 1 ? 'icon-rise' : ''
// }" ></i>
// ${cellValue.canBeControl === '1' && controlPerm ? '<i class="iconfont icon-edit-icon"></i>' : ''}`;
const cellData = `
<span class="cellClass ${cellValue.alertProp === 1 ? 'warning' : cellValue.canBeControl === '1' ? 'cellEdit' : ''}">
${cellValue.ctrlPro.valueType != 'bool' ? cellValue.val : cellValue.val === 'true' ? '启' : '停'}
</span>
${cellValue.canBeControl === '1' && controlPerm ? '<i class="iconfont icon-edit-icon"></i>' : ''}`;
return cellData;
};
//
const mergeRowMethod: VxeTablePropTypes.SpanMethod<TableVo> = ({ row, _rowIndex, column, visibleData }) => {
const fields = ['gTitle'];
const cellValue = row[column.field];
if (cellValue && fields.includes(column.field)) {
const prevRow = visibleData[_rowIndex - 1];
let nextRow = visibleData[_rowIndex + 1];
if (prevRow && prevRow[column.field] === cellValue) {
return { rowspan: 0, colspan: 0 };
} else {
let countRowspan = 1;
while (nextRow && nextRow[column.field] === cellValue) {
nextRow = visibleData[++countRowspan + _rowIndex];
}
if (countRowspan > 1) {
return { rowspan: countRowspan, colspan: 1 };
}
}
}
};
const submitEvent: VxeFormEvents.Submit = () => {
//websocket
formLoading.value = true;
const $table = tableRef.value;
const submitData = formData.value;
console.log(submitData);
//Add by zhousq 2023-10-12 post
sendCtrl(submitData).then((res: any) => {
if (res.code === 200) {
ElNotification({ message: res.data.msg });
}
});
//socket.onSend(submitData);
formLoading.value = false;
editModal.value = false;
if ($table) {
const row = cellRow.value;
const field = cellColumn.value.field;
row[field].val = formData.value.value;
$table.reloadRow(row, null, field);
}
// VXETable.modal.message({ content: '', status: 'success' });
};
function nameClick(row: any) {
//
console.log(row);
sessionStorageIns.setUseStorage('id', row.id);
router.push({ path: '/devicemanage', query: { id: row.id } });
}
const resetEvent: VxeFormEvents.Reset = () => {
console.log({ content: '重置', status: 'info' });
};
</script>
<style lang="scss" scoped>
@import '../index.scss';
</style>

68
src/views/monitoring/screenData/components/menu.vue

@ -0,0 +1,68 @@
<template>
<n-menu ref="menuInstRef" class="menu" :indent="0" :options="menuOptions" v-model:value="selectedKey" key-field="deptId"
label-field="deptName" :default-expand-all="false" :watch-props="['defaultExpandedKeys']"
:render-label="renderMenuLabel" @update:value="menuUpdateValue" />
</template>
<script lang="ts" setup>
import type { MenuOption } from 'naive-ui';
// import { NEllipsis } from 'naive-ui';
import { getMenu } from '@/api/table/list';
import { getFirstNodeLastLevel } from '@/utils/index';
import useStorage from '@/utils/useStorage'
import mitt from '@/plugins/bus';
const menuOptions = ref([]);
const selectedKey = ref();
const sessionStorageIns = useStorage('sessionStorage');
const emit = defineEmits(['tableMenuData']);
onMounted(() => {
menuApi();
});
function menuApi() {
//
getMenu().then((res: any) => {
if (res.code === 200) {
const key = getFirstNodeLastLevel(res.data).deptId
removeChildren(res.data);
menuOptions.value = res.data;
selectedKey.value = key;
sessionStorageIns.setUseStorage('deptId', key);
mitt.emit('menuKey', key);
emit('tableMenuData', res.data);
}
});
}
function removeChildren(menu: any) {
//children
if (!Array.isArray(menu)) {
return;
}
menu.forEach((item) => {
if (item.children && item.children.length === 0) {
delete item.children;
} else {
removeChildren(item.children);
}
});
}
function renderMenuLabel(option: MenuOption) {
//
return h(NEllipsis, null, option.deptName as string);
}
function menuUpdateValue(key: string, item: MenuOption) {
//
sessionStorageIns.setUseStorage('id', key);
mitt.emit('menuKey', key);
console.log(key, item);
}
</script>
<style lang="scss" scoped>
@import '../index.scss';
</style>

299
src/views/monitoring/screenData/components/showTree.vue

@ -0,0 +1,299 @@
<template>
<div class="treeCard">
<el-tree ref="tree" class="showTree" :data="treeData" node-key="id" show-checkbox draggable :props="defaultProps"
:default-checked-keys="defaultCheckedKeys" :allow-drop="allowDrop" @node-drop="nodeDrop"
@check-change="treeCheckChange">
<template #default="{ node, data }">
<span class="custom-tree-node">
<span>{{ data.field === 'deviceStatus' ? '状态' : data.title }}</span>
</span>
<div class="weight" v-if="!data.children">
<span>宽度(px):</span>
<el-input v-model="data.width" size="small" :disabled="data.disabled" placeholder="请输入宽度" />
</div>
</template>
</el-tree>
<div class="treeBtn">
<el-button @click="cancelClick">取消</el-button>
<el-button type="primary" :loading="treeLoading" @click="confirmClick">{{ treeLoading ? '确认中' : '确认' }}</el-button>
</div>
</div>
</template>
<script lang="ts" setup>
import { getCurrentInstance, ComponentInternalInstance } from 'vue';
import mitt from '@/plugins/bus';
const { proxy } = getCurrentInstance() as ComponentInternalInstance
const defaultCheckedKeys = ref([]); //
const checkList = ref<TreeVo[]>([]); //
interface TreeVo {
id?: number;
colSort: number;
colType: string;
field: string;
fixed?: string;
visible: boolean;
title: string;
width: number;
controlValue?: number;
children?: TreeVo[];
}
const treeLoading = ref(false)
const treeData = ref<TreeVo[]>();
const emit = defineEmits(['cancelClick', 'confirmClick']);
const props: any = defineProps({
headerData: {
type: Array,
default: [],
},
});
onMounted(() => {
treeData.value = props.headerData;
const checkData: any = [];
props.headerData.map((item: TreeVo) => {
if (item.visible) {
checkData.push(item.id);
checkList.value.push(item);
if (item.children && item.children.length) {
item.children.map((res: TreeVo) => {
checkData.push(res.id);
checkList.value.push(item);
});
}
}
});
defaultCheckedKeys.value = checkData;
});
// mitt.on('tableHeaderData', (res:any) => {
// //header
// treeData.value=res
// console.log(res)
// });
mitt.on('treeClose', (res: any) => {
//
emit('cancelClick', '');
treeLoading.value = false
});
const defaultProps = {
children: 'children',
label: 'title',
};
// const treeData: TreeVo[] = [
// {
// title: '',
// id: '1',
// field: 'name',
// width: 150,
// show: true,
// disabled: true,
// slots: { default: 'name_default' },
// },
// {
// title: '',
// id: '2',
// field: 'area',
// width: 150,
// show: true,
// },
// {
// title: '',
// id: '3',
// show: true,
// children: [
// {
// title: '',
// id: '4',
// field: 'oneSupplyPressure',
// width: 150,
// show: true,
// slots: { default: 'oneSupplyPressure_default' },
// },
// {
// title: '',
// id: '5',
// field: 'oneBackPressure',
// width: 150,
// show: true,
// },
// {
// title: '',
// id: '6',
// field: 'oneSupplyTemperature',
// width: 150,
// show: true,
// },
// {
// title: '',
// id: '7',
// field: 'oneBackTemperature',
// width: 150,
// show: true,
// },
// ],
// },
// {
// title: '',
// id: '8',
// show: true,
// children: [
// {
// title: '',
// id: '9',
// field: 'twoSupplyPressure',
// width: 150,
// show: true,
// },
// {
// title: '',
// id: '10',
// field: 'twoBackPressure',
// width: 150,
// show: true,
// },
// {
// title: '',
// id: '11',
// field: 'twoSupplyTemperature',
// width: 150,
// show: true,
// },
// {
// title: '',
// id: '12',
// field: 'twoBackTemperature',
// width: 150,
// show: true,
// },
// {
// title: '',
// id: '13',
// field: 'averageTemperature',
// width: 150,
// show: true,
// },
// ],
// },
// {
// title: '',
// id: '14',
// field: 'totalFlow',
// width: 150,
// show: true,
// },
// {
// title: '',
// id: '15',
// field: 'trafficFlow',
// width: 150,
// show: true,
// },
// {
// title: '',
// id: '16',
// field: 'instantHeat',
// width: 150,
// show: true,
// },
// {
// title: '',
// id: '17',
// field: 'AaccumulatHeat',
// width: 150,
// show: true,
// },
// {
// title: '',
// id: '18',
// field: 'valve',
// width: 150,
// show: true,
// },
// {
// title: '',
// id: '19',
// field: 'waterLevel',
// width: 150,
// show: true,
// },
// {
// title: '',
// id: '20',
// field: 'cycleFrequency',
// width: 150,
// show: true,
// },
// {
// title: '',
// id: '21',
// field: 'waterFrequency',
// width: 150,
// show: true,
// editRender: {},
// slots: { edit: 'waterFrequency_edit' },
// },
// {
// title: '',
// id: '22',
// field: 'waterPolice',
// width: 150,
// show: true,
// },
// {
// title: '',
// id: '23',
// field: 'oneSupplementTwo',
// width: 150,
// show: true,
// },
// {
// title: '',
// id: '24',
// field: 'soundTurnedOn',
// width: 150,
// show: true,
// slots: { default: 'soundTurnedOn_default' },
// },
// ];
function allowDrop(draggingNode: any, dropNode: any, type: any) {
//
if (type !== 'inner') {
let control = draggingNode.level === dropNode.level ? true : false;
return control;
}
}
const treeCheckChange = (data: TreeVo, checked: boolean, indeterminate: boolean) => {
//
console.log(data, checked, indeterminate);
data.visible = checked;
console.log(treeData.value);
};
const nodeDrop = () => {
//
nextTick(() => {
proxy?.$refs.tree.setCheckedKeys(defaultCheckedKeys.value);
});
};
function cancelClick() {
//
emit('cancelClick', '');
}
function confirmClick() {
//
treeLoading.value = true
mitt.emit('treeData', treeData.value);
}
</script>
<style lang="scss" scoped>
@import '../index.scss';
</style>

752
src/views/monitoring/screenData/index.scss

@ -0,0 +1,752 @@
@import url('@/assets/fonts/font.css');
.screen {
position: relative;
width: 100%;
height: 100vh;
background-color: #dedede;
// background: url(@/assets/images/screen.png) no-repeat;
// background-color: var(--tableBg);
// background-size: 100% 100%;
// background-color: rgba(29,30,31,0.8);
// background-blend-mode: multiply;
padding: 0 25px;
overflow: hidden;
.menuShow {
width: 26px;
height: 26px;
position: absolute;
top: 118px;
left: 0;
color: #1169c0;
text-align: center;
// border: 1px solid #0d55b0;
// background-color: #02072e;
border-right: none;
cursor: pointer;
.iconfont {
display: inline-block;
}
.left {
transform: rotate(0deg);
-webkit-transform: rotate(0deg);
transition: transform 0.5s;
}
.right {
transform: rotate(180deg);
-webkit-transform: rotate(180deg);
transition: transform 0.5s;
}
}
.panelBtn {
position: absolute;
bottom: 0;
left: 0;
right: 0;
width: 70%;
height: 45px;
text-align: center;
color: #1169c0;
// background: url(@/assets/images/panelBtn_bg.png) no-repeat center;
// background-size: 100% 100%;
transition: width 0.28s;
margin: 0 auto;
cursor: pointer;
.iconfont {
display: inline-block;
font-size: 18px;
}
.up {
transform: rotate(0deg);
-webkit-transform: rotate(0deg);
transition: transform 0.5s;
}
.down {
transform: rotate(180deg);
-webkit-transform: rotate(180deg);
transition: transform 0.5s;
}
.copyright {
width: 100%;
// height: 25px;
// line-height: 25px;
text-align: center;
color: #acaeb1;
font-family: Arial;
font-size: 12px;
letter-spacing: 1px;
}
}
.panelBtn.left {
left: 14%;
}
// .header {
// display: flex;
// justify-content: space-between;
// align-items: center;
// // height: 5.7rem;
// .title {
// width: 1500px;
// background: url(@/assets/images/title-bg.png);
// background-size: 100%;
// text-align: center;
// padding-bottom: 38px;
// margin: 0 auto;
// h3 {
// font-size: 3.8rem;
// font-family: 'YouSheBiaoTiHei';
// font-weight: 400;
// letter-spacing: 4px;
// background: linear-gradient(180deg, #FEFDFF 0%, #95DAFF 97%);
// background-clip: text;
// -webkit-background-clip: text;
// -webkit-text-fill-color: transparent;
// margin: 0;
// }
// }
// }
// .weather {
// display: flex;
// align-items: center;
// position: absolute;
// top: 2.5rem;
// left: 7rem;
// color: #B2D4FF;
// font-size: 1.6rem;
// line-height: 2.2rem;
// font-family: 'AlibabaPuHuiTiRegular';
// .line {
// width: 2px;
// height: 2rem;
// background: linear-gradient(to top, #000E38, #1EA8DD, #000E38);
// margin: 0 1rem;
// }
// .forecast {
// display: flex;
// align-items: center;
// img {
// width: 25px;
// margin-left: 10px;
// }
// }
// }
// .seeting {
// position: absolute;
// top: 2.5rem;
// right: 7rem;
// .tooltips {
// width: 36px;
// height: 36px;
// background: linear-gradient(180deg, #003269 1%, rgba(3, 79, 163, 0.2314) 56%, #003269 100%);
// border-radius: 0px 0px 0px 0px;
// opacity: 1;
// :deep(span) {
// color: #5beff9;
// }
// margin-left: 10px;
// }
// }
.layout {
display: flex;
// margin-top: 20px;
.sidebar {
width: 12%;
height: calc(100vh - 70px - 45px); //屏幕高度-头部header高度-底部高度
flex-shrink: 0;
border: 1px solid #b7babf;
padding: 10px 0;
// box-shadow: inset 0px 0px 10px 0px rgb(36, 90, 12);
margin-right: 1vw;
position: relative;
overflow: auto;
transition: width 0.28s;
// background-color: rgba(2, 8, 46, 0.8);
// .menu {
// text-align: center;
// :deep(.n-submenu) {
// --n-item-color-hover: auto;
// .n-menu-item {
// .n-menu-item-content {
// padding: 0 !important;
// .n-ellipsis {
// font-family: 'AlibabaPuHuiTiBold';
// padding: 0 15px;
// }
// }
// .n-menu-item-content-header {
// font-size: 2.4rem;
// color: #B1E3FF;
// }
// }
// .n-submenu-children {
// .n-menu-item-content-header {
// font-size: 1.8rem;
// }
// .n-menu-item-content--selected {
// .n-menu-item-content-header {
// color: #fff;
// .n-ellipsis {
// position: relative;
// span {
// padding: 0 10px;
// }
// span::before {
// content: '';
// position: absolute;
// left: 0;
// top: 0.7rem;
// width: 1.8rem;
// height: 1.8rem;
// background: url(../../../assets/images/taps.png) no-repeat;
// background-size: cover;
// }
// }
// }
// }
// .n-menu-item-content--selected::before {
// background: -webkit-linear-gradient(left, #1fc7ff29 0%, #1177e700 100%);
// left: 0;
// right: 0;
// }
// .n-menu-item-content--selected::after {
// content: '';
// position: absolute;
// bottom: 0;
// width: 100%;
// height: 2px;
// background: -webkit-linear-gradient(left, #1fc7ff29 0%, #1177e700 100%);
// }
// }
// .n-base-icon {
// color: #84e0f7;
// right: 10px;
// }
// }
// }
}
.sidebar.sidebarHide {
width: 0;
border: none;
margin-right: 0;
transition: width 0.28s;
}
.sidebar::after {
content: none;
position: absolute;
bottom: 0;
width: 100%;
height: 18rem;
background: url(@/assets/images/menu_bg.png) no-repeat;
background-size: 100% 100%;
}
.main {
position: relative;
width: 87%;
// background-color: rgba(2, 8, 46, 0.8);
.headerInfo {
border: 1px solid #0d55b0;
// box-shadow: inset 0px 0px 10px 0px rgb(36, 90, 124);
margin-bottom: 10px;
.header {
display: flex;
align-items: center;
flex-wrap: wrap;
.headerItem {
width: 11.1%;
color: #fff;
text-align: center;
.name {
font-size: 12px;
line-height: 36px;
padding: 0 3rem;
color: #ffffffb3;
background-color: #123f7580;
border-top: 1px solid #265a89;
border-bottom: 1px solid #265a89;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}
.value {
font-size: 18px;
color: #5beff9;
line-height: 36px;
}
}
}
}
.mainTable {
// height: calc(100vh - 8rem - 202px);
// height: calc(100% - 162px);
border: 1px solid #b7babf;
background-color: #dedede;
transition: height 0.28s;
overflow: hidden;
.tableGrid {
// --vxe-table-header-background-color: none;
// --vxe-table-body-background-color: none;
// --vxe-table-footer-background-color: none;
// --vxe-table-border-color: rgba(12, 80, 166, 0.2);
// --vxe-table-border-color: none;
--vxe-font-color: #000;
// --vxe-table-header-font-color: #ffffffb3;
// border: 1px solid #b7babf;
// box-shadow: inset 0px 0px 10px 0px rgb(36, 90, 124);
:deep(.vxe-table) {
// height: calc(100vh - 5.7rem - 60px) !important;
.vxe-table--body-wrapper {
background-color: #dedede;
// height: calc(100vh - 5.7rem - 182px) !important;
}
.headerRowClass {
.headerCellClass {
font-size: 18px;
font-weight: 800;
color: #eee;
background-color: #1169c0;
// border: 1px solid var(--vxe-font-color) !important;
border-left: none !important;
.vxe-resizable.is--line:before {
content: none;
}
}
// .headerCellClass:nth-child(1){
// border-left: none !important;
// }
.headerCellClass.col--fixed {
// background-color: #020e38;
// box-shadow: inset 0px 0px 10px 0px #245a7c;
}
}
.tableRowClass {
font-size: 18px;
font-weight: 500;
.tableCellClass {
background-color: #dedede;
border-bottom: 1px solid #b7babf !important;
border-right: 1px solid #b7babf !important;
border-right-color: #b7babf !important;
.vxe-cell {
font-family: 'AlibabaPuHuiTiRegular';
// color: #b1e3ff;
.title {
display: flex;
align-items: center;
text-align: left;
.svg-icon {
flex-shrink: 0;
}
}
}
.vxe-cell--html {
display: flex;
justify-content: center;
align-items: center;
.warning {
font-size: 18px;
font-weight: bold;
color: red;
}
.cellEdit {
cursor: pointer;
}
.iconfont {
font-size: 18px;
}
.iconfont.icon-rise {
color: green;
}
.iconfont.icon-decline {
color: red;
}
.iconfont.icon-edit-icon {
font-size: 16px;
margin-left: 5px;
cursor: pointer;
}
}
}
}
.tableRowClass.row--stripe {
background-color: rgba(0, 95, 199, 0.15);
}
.tips {
display: inline-block;
width: 10px;
height: 10px;
margin: 0 auto;
border-radius: 10px;
margin-right: 5px;
}
.tips.green {
background-color: green;
}
.tips.red {
background-color: red;
}
.name {
margin-left: 5px;
cursor: pointer;
}
.cellName {
display: flex;
justify-content: center;
align-items: center;
color: #fef961;
}
.cellInput {
input {
color: #222;
text-align: center;
}
}
.vxe-table--fixed-left-wrapper.scrolling--middle {
background: -webkit-linear-gradient(top, #02072b 0%, #02082b 100%);
box-shadow: inset 0px 0px 10px rgb(36, 90, 124) !important;
}
}
}
.tablePage {
background-color: #dedede;
// --vxe-pager-background-color: none;
padding: 0 2rem;
// color: #b1e3ff;
// :deep(.vxe-select) {
// .vxe-input--inner {
// color: #b1e3ff;
// border: 1px solid #b1e3ff;
// background-color: transparent;
// }
// .vxe-select--panel {
// color: #b1e3ff;
// .vxe-select-option--wrapper {
// border: 1px solid #b1e3ff;
// background: -webkit-linear-gradient(top, #02072b 0%, #02082b 100%);
// .vxe-select-option:not(.is--disabled).is--hover {
// background: none;
// }
// }
// }
// }
// :deep(.vxe-pager--jump) {
// .vxe-pager--goto {
// color: #b1e3ff;
// border: 1px solid #b1e3ff;
// background-color: transparent;
// }
// }
}
// /*滚动条整体部分*/
// .tableGrid ::-webkit-scrollbar {
// width: 8px;
// height: 8px;
// }
// /*滚动条的轨道*/
// .tableGrid ::-webkit-scrollbar-track {
// background-color: transparent;
// -webkit-border-radius: 8px;
// -moz-border-radius: 8px;
// border-radius: 8px;
// }
// /*滚动条里面的小方块能向上向下移动*/
// .tableGrid ::-webkit-scrollbar-thumb {
// background-color: rgb(147, 147, 153, 0.5);
// -webkit-border-radius: 8px;
// -moz-border-radius: 8px;
// border-radius: 8px;
// }
// .tableGrid ::-webkit-scrollbar-thumb:hover {
// background-color: #a8a8a8;
// }
// .tableGrid ::-webkit-scrollbar-thumb:active {
// background-color: #787878;
// }
// /*边角即两个滚动条的交汇处*/
// .tableGrid ::-webkit-scrollbar-corner {
// background-color: transparent;
// }
}
.infoPanel {
height: 280px;
margin-top: 20px;
transition: transform 0.5s;
:deep(.swiper) {
--swiper-theme-color: #ff6600;
/* 设置Swiper风格 */
--swiper-navigation-color: #00ff33;
/* 单独设置按钮颜色 */
--swiper-navigation-size: 30px;
/* 设置按钮大小 */
.item {
height: 280px;
.content {
display: flex;
align-items: center;
height: -webkit-fill-available;
border: 2px solid #b7babf;
border-radius: 5px;
color: #000;
// padding: 15px;
// background-color: rgba(2, 8, 46, 0.5);
.icon {
width: 35%;
img {
width: 100%;
}
}
.numValue {
flex: 1;
text-align: center;
color: #000;
span {
font-size: 5rem;
}
i {
font-size: 3rem;
font-style: normal;
}
p {
font-size: 2rem;
margin: 0;
color: #1782ff;
}
}
.numValueMore {
flex: 1;
margin-left: 1.5rem;
span {
font-size: 1.4rem;
line-height: 30px;
color: #000;
margin-right: 3px;
}
span:nth-child(1) {
font-size: 1.6rem;
color: #1782ff;
}
span:nth-child(2) {
font-size: 1.8rem;
margin-right: 5px;
}
}
}
}
}
}
}
/*滚动条整体部分*/
.tableGrid ::-webkit-scrollbar {
width: 8px;
height: 8px;
}
/*滚动条的轨道*/
.tableGrid ::-webkit-scrollbar-track {
background-color: transparent;
-webkit-border-radius: 8px;
-moz-border-radius: 8px;
border-radius: 8px;
}
/*滚动条里面的小方块,能向上向下移动*/
.tableGrid ::-webkit-scrollbar-thumb {
background-color: rgb(147, 147, 153, 0.5);
-webkit-border-radius: 8px;
-moz-border-radius: 8px;
border-radius: 8px;
}
.tableGrid ::-webkit-scrollbar-thumb:hover {
background-color: #a8a8a8;
}
.tableGrid ::-webkit-scrollbar-thumb:active {
background-color: #787878;
}
/*边角,即两个滚动条的交汇处*/
.tableGrid ::-webkit-scrollbar-corner {
background-color: transparent;
}
}
}
// .cardClass {
.treeCard {
.showTree {
height: 50vh;
overflow: auto;
.weight {
display: flex;
align-items: center;
margin-left: auto;
span {
margin-right: 2px;
}
}
}
.treeBtn {
margin-top: 20px;
text-align: right;
.n-button {
margin-left: 10px;
}
}
/*滚动条整体部分*/
.showTree::-webkit-scrollbar {
width: 8px;
height: 8px;
}
/*滚动条的轨道*/
.showTree::-webkit-scrollbar-track {
background-color: transparent;
-webkit-border-radius: 8px;
-moz-border-radius: 8px;
border-radius: 8px;
}
/*滚动条里面的小方块,能向上向下移动*/
.showTree::-webkit-scrollbar-thumb {
background-color: rgb(147, 147, 153, 0.5);
-webkit-border-radius: 8px;
-moz-border-radius: 8px;
border-radius: 8px;
}
.showTree::-webkit-scrollbar-thumb:hover {
background-color: #a8a8a8;
}
.showTree:-webkit-scrollbar-thumb:active {
background-color: #787878;
}
/*边角,即两个滚动条的交汇处*/
.showTree::-webkit-scrollbar-corner {
background-color: transparent;
}
}
// }
.dark {
.screen {
background-color: rgba(29, 30, 31, 0.8);
background-blend-mode: multiply;
.layout {
.sidebar {
background-color: rgba(29, 30, 31, 0.8);
}
.main {
background-color: rgba(29, 30, 31, 0.8);
.mainTable {
.tableGrid {
:deep(.vxe-table) {
.vxe-table--fixed-left-wrapper.scrolling--middle {
background: -webkit-linear-gradient(top, rgba(21, 21, 25, 1) 0%, rgba(21, 21, 25, 1) 100%);
box-shadow: inset 0px 0px 10px rgb(36, 90, 124) !important;
}
}
}
}
}
.infoPanel {
:deep(.swiper) {
.item {
.content {
background-color: rgba(29, 30, 31, 0.8);
}
}
}
}
}
}
}

200
src/views/monitoring/screenData/index.vue

@ -0,0 +1,200 @@
<template>
<div ref="screenRef" class="screen">
<section ref="titleRef" class="header">
<Header
:titleData="titleData"
:settingShow="true"
:warningShow="true"
@showModalClick="showModalClick"
@returnClick="returnClick"
/>
<!-- <div class="title">
<h3>{{ titleData }}</h3>
</div>
<div class="weather">
<div class="time">
{{ currentTime }} {{ lunarDay.ncWeek }}
</div>
<div class="line"></div>
<div class="forecast">
<span>天气多云</span>
<img src="../../../assets/images/weather/duoyun.png" />
</div>
</div>
<div class="seeting">
<n-tooltip trigger="hover">
<template #trigger>
<n-button class="tooltips" circle quaternary @click="showClick">
<template #icon>
<n-icon>
<Settings />
</n-icon>
</template>
</n-button>
</template>
显示项
</n-tooltip>
<n-tooltip trigger="hover">
<template #trigger>
<n-button class="tooltips" circle quaternary @click="returnBack">
<template #icon>
<n-icon>
<Power />
</n-icon>
</template>
</n-button>
</template>
返回首页
</n-tooltip>
</div> -->
</section>
<section class="layout">
<div ref="sidebar" class="sidebar animate__animated animate__fadeIn" :class="menuShow ? '' : 'sidebarHide'">
<Menu menuType="1" @tableMenuData="tableMenuData" />
</div>
<div class="main" :style="`width: ${mainWidth}%`">
<Main :tapsShow="tapsShow" :sidebarHeight="sidebarHeight" @tableHeaderData="tableHeaderData" />
<InfoPanel class="animate__animated animate__fadeInUp" v-if="panelShow" />
</div>
</section>
<section>
<!-- <n-modal v-model:show="showModal"> -->
<el-dialog v-model="showModal" title="显示项" width="600">
<!-- <n-card class="cardClass" style="width: 600px" title="显示项" :bordered="false" size="huge" role="dialog"
aria-modal="true"> -->
<ShowTree :headerData="headerData" @cancelClick="cancelClick" />
<!-- <template #header-extra>
*
</template> -->
<!-- <template #footer>
<n-button>取消</n-button>
<n-button type="info"> 确定 </n-button>
</template> -->
<!-- </n-card> -->
</el-dialog>
<!-- </n-modal> -->
</section>
<div class="menuShow" @click="menuIsShow">
<i class="iconfont icon-angle-double" :class="menuShow ? 'left' : 'right'"></i>
</div>
<div class="panelBtn" :class="menuShow ? 'left' : 'right'" @click="panelClick">
<i class="iconfont icon-arrow-up" :class="!panelShow ? 'up' : 'down'"></i>
<div class="copyright">Copyright © {{ $t('copyright.year') }} {{ $t('copyright.abbr') }} {{ $t('copyright.document') }}</div>
<!-- <n-button type="info" size="tiny" ghost @click="panelClick">按钮</n-button> -->
</div>
</div>
</template>
<script lang="ts" setup>
import { useRoute, useRouter, onBeforeRouteLeave } from 'vue-router';
// import { useDateFormat, useNow } from '@vueuse/core';
// import { Filter, Maximize, Settings, Power } from '@vicons/tabler';
import Header from '../components/header.vue';
import Menu from '../components/menu.vue';
import Main from './components/main.vue';
import InfoPanel from './components/infoPanel.vue';
import ShowTree from './components/showTree.vue';
import useStorage from '@/utils/useStorage';
import socket from '@/utils/socket';
const sessionStorageIns = useStorage('sessionStorage');
const route = useRoute();
const router = useRouter();
// import calendar from '@/utils/lunar';
// const currentTime = useDateFormat(useNow(), 'YYYY-MM-DD HH:mm:ss');
// const lunarDay: any = calendar.solarToLunar(
// useNow().value.getUTCFullYear(),
// useNow().value.getUTCMonth() + 1,
// useNow().value.getUTCDate()
// );
// import screenfull from 'screenfull'
const tapsShow = ref(false);
const showModal = ref(false);
const screenRef = ref<HTMLElement>();
const titleRef = ref<HTMLElement>();
const sidebar = ref<HTMLElement>();
const sidebarHeight = ref();
const headerData = ref([]);
const mainWidth = ref(87);
const titleData = ref('');
const menuShow = ref(true);
const panelShow = ref(false);
const panelHeight = ref(0);
onMounted(() => {
// titleRef.value?.clientHeight; //
const offsetHeight = sidebar.value?.offsetHeight; //
sidebarHeight.value = offsetHeight === undefined ? 0 : offsetHeight - panelHeight.value;
// menuShow.value=localStorage.getItem('menuShow')===undefined?true:JSON.parse(localStorage.getItem('menuShow'));
// console.log("sidebar:", sidebar.value?.offsetHeight)
});
window.addEventListener('resize', () => {
//
const offsetHeight = sidebar.value?.offsetHeight; //
sidebarHeight.value = offsetHeight === undefined ? 0 : offsetHeight - panelHeight.value;
console.log('监听sidebar:', sidebar.value?.offsetHeight);
});
onBeforeRouteLeave((to, from) => {
//
console.log('onBeforeRouteLeave--', to.path, from.path);
if (to.path != from.path) {
console.log('离开页面');
socket.close(false);
}
});
function tableMenuData(data: any) {
//
titleData.value = data[0].deptName;
}
function tableHeaderData(data: any) {
//header
headerData.value = data;
}
function filterClick() {
///
tapsShow.value = !tapsShow.value;
}
function showModalClick(val: boolean) {
///
showModal.value = val;
}
function cancelClick() {
//
showModal.value = false;
}
function returnClick(val: string) {
//
router.replace('/dashboard');
}
function menuIsShow() {
//
menuShow.value = !menuShow.value;
mainWidth.value = menuShow.value ? 87 : 100;
sessionStorageIns.setUseStorage('menuShow', menuShow.value);
}
function panelClick() {
//
panelShow.value = !panelShow.value;
panelHeight.value = panelShow.value ? 300 : 0;
const offsetHeight = sidebar.value?.offsetHeight; //
sidebarHeight.value = offsetHeight === undefined ? 0 : offsetHeight - panelHeight.value;
sessionStorageIns.setUseStorage('panelShow', panelShow.value);
}
// function screenClick(){
// if (screenfull.isEnabled) {
// //
// screenfull.toggle(screenRef.value)
// }
// }
</script>
<style lang="scss" scoped>
@import './index.scss';
</style>
Loading…
Cancel
Save