代码提交
3
.dev.production
Normal file
@@ -0,0 +1,3 @@
|
||||
NODE_ENV = 'production'
|
||||
VITE_APP_BASE_API = http://120.24.64.5:8088/mall-admin
|
||||
VITE_APP_BASE_URL = /index/
|
||||
2
.env.development
Normal file
@@ -0,0 +1,2 @@
|
||||
NODE_ENV = 'development'
|
||||
VITE_APP_BASE_API = http://120.24.64.5:8088/mall-admin
|
||||
28
.gitignore
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
|
||||
offline
|
||||
dist-ssr
|
||||
*.local
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.zip*
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
vite.config*.ts
|
||||
pnpm-lock.yaml
|
||||
BIN
@jiaminghi.rar
Normal file
36
README.en.md
Normal file
@@ -0,0 +1,36 @@
|
||||
# vue3-vite-ts-echarts5-axios 大屏可视化基础模板
|
||||
|
||||
#### Description
|
||||
基于vue3-vite-ts-echarts5-axios+element-plus-mockjs -atav做的大屏可视化基础模板,可用于大屏可视化,也可用于其他项目 已封装axios
|
||||
|
||||
#### Software Architecture
|
||||
Software architecture description
|
||||
|
||||
#### Installation
|
||||
|
||||
1. xxxx
|
||||
2. xxxx
|
||||
3. xxxx
|
||||
|
||||
#### Instructions
|
||||
|
||||
1. xxxx
|
||||
2. xxxx
|
||||
3. xxxx
|
||||
|
||||
#### Contribution
|
||||
|
||||
1. Fork the repository
|
||||
2. Create Feat_xxx branch
|
||||
3. Commit your code
|
||||
4. Create Pull Request
|
||||
|
||||
|
||||
#### Gitee Feature
|
||||
|
||||
1. You can use Readme\_XXX.md to support different languages, such as Readme\_en.md, Readme\_zh.md
|
||||
2. Gitee blog [blog.gitee.com](https://blog.gitee.com)
|
||||
3. Explore open source project [https://gitee.com/explore](https://gitee.com/explore)
|
||||
4. The most valuable open source project [GVP](https://gitee.com/gvp)
|
||||
5. The manual of Gitee [https://gitee.com/help](https://gitee.com/help)
|
||||
6. The most popular members [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)
|
||||
9
auto-imports.d.ts
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
/* eslint-disable */
|
||||
/* prettier-ignore */
|
||||
// @ts-nocheck
|
||||
// Generated by unplugin-auto-import
|
||||
export {}
|
||||
declare global {
|
||||
const ElMessage: typeof import('element-plus/es')['ElMessage']
|
||||
const ElMessageBox: typeof import('element-plus/es')['ElMessageBox']
|
||||
}
|
||||
69
components.d.ts
vendored
Normal file
@@ -0,0 +1,69 @@
|
||||
/* eslint-disable */
|
||||
/* prettier-ignore */
|
||||
// @ts-nocheck
|
||||
// Generated by unplugin-vue-components
|
||||
// Read more: https://github.com/vuejs/core/pull/3399
|
||||
import '@vue/runtime-core'
|
||||
|
||||
export {}
|
||||
|
||||
declare module '@vue/runtime-core' {
|
||||
export interface GlobalComponents {
|
||||
DatePicker: typeof import('./src/components/datePicker/index.vue')['default']
|
||||
EchartMap3D: typeof import('./src/components/echartMap3D.vue')['default']
|
||||
ElButton: typeof import('element-plus/es')['ElButton']
|
||||
ElCard: typeof import('element-plus/es')['ElCard']
|
||||
ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
|
||||
ElCheckboxButton: typeof import('element-plus/es')['ElCheckboxButton']
|
||||
ElCheckboxGroup: typeof import('element-plus/es')['ElCheckboxGroup']
|
||||
ElCol: typeof import('element-plus/es')['ElCol']
|
||||
ElDatePicker: typeof import('element-plus/es')['ElDatePicker']
|
||||
ElDescriptions: typeof import('element-plus/es')['ElDescriptions']
|
||||
ElDescriptionsItem: typeof import('element-plus/es')['ElDescriptionsItem']
|
||||
ElDialog: typeof import('element-plus/es')['ElDialog']
|
||||
ElDivider: typeof import('element-plus/es')['ElDivider']
|
||||
ElDrawer: typeof import('element-plus/es')['ElDrawer']
|
||||
ElDropdown: typeof import('element-plus/es')['ElDropdown']
|
||||
ElDropdownItem: typeof import('element-plus/es')['ElDropdownItem']
|
||||
ElDropdownMenu: typeof import('element-plus/es')['ElDropdownMenu']
|
||||
ElEmpty: typeof import('element-plus/es')['ElEmpty']
|
||||
ElForm: typeof import('element-plus/es')['ElForm']
|
||||
ElFormItem: typeof import('element-plus/es')['ElFormItem']
|
||||
ElIcon: typeof import('element-plus/es')['ElIcon']
|
||||
ElInput: typeof import('element-plus/es')['ElInput']
|
||||
ElInputNumber: typeof import('element-plus/es')['ElInputNumber']
|
||||
ElLink: typeof import('element-plus/es')['ElLink']
|
||||
ElOption: typeof import('element-plus/es')['ElOption']
|
||||
ElPagination: typeof import('element-plus/es')['ElPagination']
|
||||
ElPopover: typeof import('element-plus/es')['ElPopover']
|
||||
ElRadioButton: typeof import('element-plus/es')['ElRadioButton']
|
||||
ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup']
|
||||
ElRow: typeof import('element-plus/es')['ElRow']
|
||||
ElSelect: typeof import('element-plus/es')['ElSelect']
|
||||
ElTable: typeof import('element-plus/es')['ElTable']
|
||||
ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
|
||||
ElTabPane: typeof import('element-plus/es')['ElTabPane']
|
||||
ElTabs: typeof import('element-plus/es')['ElTabs']
|
||||
ElTag: typeof import('element-plus/es')['ElTag']
|
||||
ElTooltip: typeof import('element-plus/es')['ElTooltip']
|
||||
ElTreeSelect: typeof import('element-plus/es')['ElTreeSelect']
|
||||
ElUpload: typeof import('element-plus/es')['ElUpload']
|
||||
HelloWorld: typeof import('./src/components/HelloWorld.vue')['default']
|
||||
MyEchart: typeof import('./src/components/echarts/MyEchart.vue')['default']
|
||||
MyEchartMap: typeof import('./src/components/MyEchartMap.vue')['default']
|
||||
PointTree: typeof import('./src/components/tree/pointTree.vue')['default']
|
||||
Rmsboxi: typeof import('./src/components/BX/rmsboxi.vue')['default']
|
||||
Rmsboxi1: typeof import('./src/components/BX/rmsboxi1.vue')['default']
|
||||
Rmsboxi2: typeof import('./src/components/BX/rmsboxi2.vue')['default']
|
||||
RouterLink: typeof import('vue-router')['RouterLink']
|
||||
RouterView: typeof import('vue-router')['RouterView']
|
||||
Shushiboxi: typeof import('./src/components/BX/shushiboxi.vue')['default']
|
||||
Shushiboxi1: typeof import('./src/components/BX/shushiboxi1.vue')['default']
|
||||
Shushiboxi2: typeof import('./src/components/BX/shushiboxi2.vue')['default']
|
||||
SystemTree: typeof import('./src/components/tree/systemTree.vue')['default']
|
||||
WaveForm: typeof import('./src/components/BX/waveForm.vue')['default']
|
||||
}
|
||||
export interface ComponentCustomProperties {
|
||||
vLoading: typeof import('element-plus/es')['ElLoadingDirective']
|
||||
}
|
||||
}
|
||||
13
index.html
Normal file
@@ -0,0 +1,13 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>电压暂降监测平台</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
</html>
|
||||
2301
package-lock.json
generated
Normal file
46
package.json
Normal file
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"name": "vite-ts-demo",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite --host",
|
||||
"build": "vue-tsc && vite build",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@element-plus/icons-vue": "^2.3.1",
|
||||
"@jiaminghi/data-view": "^2.10.0",
|
||||
"@kjgl77/datav-vue3": "^1.4.2",
|
||||
"axios": "^1.3.4",
|
||||
"echarts": "^5.4.3",
|
||||
"echarts-gl": "^2.0.9",
|
||||
"echarts-liquidfill": "^3.1.0",
|
||||
"element-plus": "^2.9.11",
|
||||
"html2canvas": "^1.4.1",
|
||||
"jquery": "^3.7.1",
|
||||
"js-table2excel": "^1.1.2",
|
||||
"lodash": "^4.17.21",
|
||||
"mockjs": "^1.1.0",
|
||||
"splitpanes": "^4.0.4",
|
||||
"vue": "^3.2.47",
|
||||
"vue-baidu-map-3x": "^1.0.40",
|
||||
"vue-baidu-map-offline": "^1.0.7",
|
||||
"vue-router": "^4.1.6",
|
||||
"vuex": "^4.0.2",
|
||||
"xe-utils": "^3.7.5",
|
||||
"xlsx": "^0.18.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^18.15.3",
|
||||
"@vitejs/plugin-vue": "^4.1.0",
|
||||
"less": "^4.1.3",
|
||||
"sass": "^1.59.3",
|
||||
"typescript": "^4.9.3",
|
||||
"unplugin-auto-import": "^0.15.1",
|
||||
"unplugin-vue-components": "^0.24.1",
|
||||
"vite": "^4.2.0",
|
||||
"vite-plugin-mock": "^2.9.6",
|
||||
"vue-tsc": "^1.2.0"
|
||||
}
|
||||
}
|
||||
45
src/App.vue
Normal file
@@ -0,0 +1,45 @@
|
||||
<template>
|
||||
<div class="app">
|
||||
<router-view />
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import * as echarts from "echarts";
|
||||
import { provide, onBeforeMount } from "vue";
|
||||
provide("echarts", echarts);
|
||||
import { useStore } from "vuex";
|
||||
const store = useStore();
|
||||
|
||||
// 子页面(https://child.com)
|
||||
window.addEventListener("message", (event) => {
|
||||
console.log("🚀 ~ 传参了:");
|
||||
// 2. 获取数据
|
||||
const data = event.data;
|
||||
if (data.username) {
|
||||
// 调用token
|
||||
window.sessionStorage.setItem("userInfo", JSON.stringify(data));
|
||||
store.dispatch("loginAction", data);
|
||||
}
|
||||
});
|
||||
|
||||
// setTimeout(() => {
|
||||
store.dispatch("loginAction", {
|
||||
username: "cdf",
|
||||
password: "@#001njcnpqs",
|
||||
});
|
||||
// }, 100);
|
||||
onBeforeMount(() => {
|
||||
window.sessionStorage.setItem("token", "");
|
||||
console.log("销毁页面了");
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
@import "@/assets/scss/element.scss";
|
||||
.app {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
background-color: #000;
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
||||
13
src/api/login/login.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
// 导入axios实例
|
||||
import service from "@/utils/request";
|
||||
// 登录获取token
|
||||
export function login(data: object) {
|
||||
return service({
|
||||
url: "/cn_authenticate",
|
||||
method: "post",
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
339
src/api/manage_wx/index.ts
Normal file
@@ -0,0 +1,339 @@
|
||||
// 导入axios实例
|
||||
import http from "@/utils/request_wx";
|
||||
|
||||
// 项目管理弹框列表
|
||||
export function projectList(data: object) {
|
||||
return http.request({
|
||||
url: "/cs-harmonic-boot/csconfiguration/queryPage",
|
||||
method: "post",
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
// 新增
|
||||
export function add(data) {
|
||||
return http.request({
|
||||
url: "/cs-harmonic-boot/csconfiguration/add",
|
||||
method: "post",
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
// 修改 删除
|
||||
export function edit(data) {
|
||||
return http.request({
|
||||
url: "/cs-harmonic-boot/csconfiguration/audit",
|
||||
method: "post",
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
// 激活
|
||||
export function active(data) {
|
||||
return http.request({
|
||||
url: "/cs-harmonic-boot/csconfiguration/active",
|
||||
method: "get",
|
||||
params: data,
|
||||
});
|
||||
}
|
||||
|
||||
// 激活 改变画布状态
|
||||
export function getActive(data) {
|
||||
return http.request({
|
||||
url: "/cs-harmonic-boot/csconfiguration/getActive",
|
||||
method: "get",
|
||||
params: data,
|
||||
});
|
||||
}
|
||||
|
||||
// 右侧监测点规模统计
|
||||
export function ledgerScale(data) {
|
||||
return http.request({
|
||||
url: "/scale/ledgerScale",
|
||||
method: "post",
|
||||
data: data,
|
||||
});
|
||||
}
|
||||
|
||||
// 暂降溯源统计
|
||||
export function eventSource(data) {
|
||||
return http.request({
|
||||
url: "/scale/eventSource",
|
||||
method: "post",
|
||||
data: data,
|
||||
});
|
||||
}
|
||||
|
||||
// 暂降聚合个数
|
||||
export function eventAggregation(data) {
|
||||
return http.request({
|
||||
url: "/scale/eventAggregation",
|
||||
method: "post",
|
||||
data: data,
|
||||
});
|
||||
}
|
||||
|
||||
// 闪烁点统计
|
||||
export function hasEventList(data) {
|
||||
return http.request({
|
||||
url: "/scale/hasEventList",
|
||||
method: "post",
|
||||
data: data,
|
||||
});
|
||||
}
|
||||
|
||||
// 实时信息
|
||||
export function getEventList(data) {
|
||||
return http.request({
|
||||
url: "/scale/eventList",
|
||||
method: "post",
|
||||
data: data,
|
||||
});
|
||||
}
|
||||
|
||||
// 聚合
|
||||
export function clickImage(data) {
|
||||
return http.request({
|
||||
url: "/scale/clickImage",
|
||||
method: "get",
|
||||
params: data,
|
||||
});
|
||||
}
|
||||
|
||||
// 单个点击图元,查看详情
|
||||
export function processEvents(data) {
|
||||
return http.request({
|
||||
url: "/process/processEvents",
|
||||
method: "post",
|
||||
data: data,
|
||||
});
|
||||
}
|
||||
|
||||
// 监测点规模统计 站点弹框详情
|
||||
export function stationPage(data) {
|
||||
return http.request({
|
||||
url: "/scale/stationPage",
|
||||
method: "post",
|
||||
data: data,
|
||||
});
|
||||
}
|
||||
|
||||
// 监测点规模统计 终端弹框详情
|
||||
export function devPage(data) {
|
||||
return http.request({
|
||||
url: "/scale/devPage",
|
||||
method: "post",
|
||||
data: data,
|
||||
});
|
||||
}
|
||||
// 监测点规模统计 检测点弹框详情
|
||||
export function linePage(data) {
|
||||
return http.request({
|
||||
url: "/scale/linePage",
|
||||
method: "post",
|
||||
data: data,
|
||||
});
|
||||
}
|
||||
|
||||
// 获取区域中断终端
|
||||
export function getTerminalTreeForFive(data: any) {
|
||||
return http.request({
|
||||
url: "/terminalTree/getTerminalTreeForFive",
|
||||
method: "post",
|
||||
data: data,
|
||||
});
|
||||
}
|
||||
// 台账变更推送
|
||||
export function ledgerChangePush() {
|
||||
return http.request({
|
||||
url: "/device/ledgerChangePush",
|
||||
method: "post",
|
||||
});
|
||||
}
|
||||
// 台账变更推送
|
||||
export function getPushResult(data: any) {
|
||||
return http.request({
|
||||
url: "/device/getPushResult",
|
||||
method: "post",
|
||||
params: data,
|
||||
});
|
||||
}
|
||||
|
||||
//负荷数据 用采数据列表
|
||||
export function userDataList(data: any) {
|
||||
return http.request({
|
||||
url: "/responsibility/userDataList",
|
||||
method: "post",
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
// 完整性详情
|
||||
export function userDataIntegrityList(data: any) {
|
||||
return http.request({
|
||||
url: "/responsibility/userDataIntegrityList",
|
||||
method: "post",
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
//删除用采数据
|
||||
export function deleteUserDataByIds(data: any) {
|
||||
return http.request({
|
||||
url: "/responsibility/deleteUserDataByIds",
|
||||
method: "post",
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
// 上传文件
|
||||
export function uploadUserData(data: any) {
|
||||
return http.request({
|
||||
url: "/responsibility/uploadUserData",
|
||||
method: "post",
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
//执行
|
||||
export function getHistoryHarmData(data: any) {
|
||||
return http.request({
|
||||
url: "/harmonic/getHistoryHarmData",
|
||||
method: "post",
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
//生成谐波责任指标
|
||||
export function getResponsibilityData(data: any) {
|
||||
return http.request({
|
||||
url: "/responsibility/getResponsibilityData",
|
||||
method: "post",
|
||||
data,
|
||||
});
|
||||
}
|
||||
//生成动态谐波责任数据
|
||||
export function getDynamicData(data: any) {
|
||||
return http.request({
|
||||
url: "/responsibility/getDynamicData",
|
||||
method: "post",
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
// 谐波溯源表格
|
||||
export function responsibilityList(data: any) {
|
||||
return http.request({
|
||||
url: "/responsibility/responsibilityList",
|
||||
method: "post",
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
// 谐波溯源详情
|
||||
export function displayHistoryData(data: any) {
|
||||
return http.request({
|
||||
url: "/responsibility/displayHistoryData",
|
||||
method: "get",
|
||||
params: data,
|
||||
});
|
||||
}
|
||||
|
||||
// 谐波溯源删除
|
||||
export function deleteByIds(data: any) {
|
||||
return http.request({
|
||||
url: "/responsibility/deleteByIds",
|
||||
method: "post",
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
// 谐波放大表格
|
||||
export function getInfoList(data: any) {
|
||||
return http.request({
|
||||
url: "/harmonicUp/getInfoList",
|
||||
method: "post",
|
||||
data: data,
|
||||
});
|
||||
}
|
||||
|
||||
// 谐波放大测点
|
||||
export function tableInfo(data: any) {
|
||||
return http.request({
|
||||
url: "/harmonicUp/tableInfo",
|
||||
method: "post",
|
||||
data: data,
|
||||
});
|
||||
}
|
||||
|
||||
// 谐波放大测点详情
|
||||
export function getDetail(data: any) {
|
||||
return http.request({
|
||||
url: "/harmonicUp/getDetail",
|
||||
method: "post",
|
||||
data: data,
|
||||
});
|
||||
}
|
||||
|
||||
// 稳态指标
|
||||
export function realTimeData(data) {
|
||||
return http.request({
|
||||
url: "/data/realTimeData",
|
||||
method: "post",
|
||||
params: data,
|
||||
});
|
||||
}
|
||||
|
||||
// 趋势图
|
||||
export function getHistoryResult(data) {
|
||||
return http.request({
|
||||
url: "/harmonic/getHistoryResult",
|
||||
method: "post",
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
// 谐波溯源表格点击事件
|
||||
export function harmOneImage(data) {
|
||||
return http.request({
|
||||
url: "/scale/harmOneImage",
|
||||
method: "get",
|
||||
params: data,
|
||||
});
|
||||
}
|
||||
|
||||
// 谐波暂降详情点击
|
||||
export function eventListByLineId(data) {
|
||||
return http.request({
|
||||
url: "/scale/eventListByLineId",
|
||||
method: "post",
|
||||
data: data,
|
||||
});
|
||||
}
|
||||
|
||||
// 部门列表
|
||||
export function loginDeptTree(data) {
|
||||
return http.request({
|
||||
url: "/dept/loginDeptTree",
|
||||
method: "get",
|
||||
params: data,
|
||||
});
|
||||
}
|
||||
|
||||
// 事件类型
|
||||
export function getDicDataByTypeCode(data) {
|
||||
return http.request({
|
||||
url: "/dicData/getDicDataByTypeCode",
|
||||
method: "get",
|
||||
params: data,
|
||||
});
|
||||
}
|
||||
|
||||
// 谐波放大闪烁点
|
||||
export function hasUpEventList(data) {
|
||||
return http.request({
|
||||
url: "/scale/hasUpEventList",
|
||||
method: "post",
|
||||
data: data,
|
||||
});
|
||||
}
|
||||
272
src/api/statistics/index.ts
Normal file
@@ -0,0 +1,272 @@
|
||||
// 导入axios实例
|
||||
import service from "@/utils/request";
|
||||
|
||||
export function initLedger(data: object) {
|
||||
return service({
|
||||
url: "/largescreen/initLedger",
|
||||
method: "post",
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
// 台账规模统计
|
||||
export function ledgercount(data: object) {
|
||||
return service({
|
||||
url: "/largescreen/ledgercount",
|
||||
method: "post",
|
||||
data,
|
||||
});
|
||||
}
|
||||
// 告警统计分析
|
||||
export function alarmAnalysis(data: object) {
|
||||
return service({
|
||||
url: "/largescreen/alarmAnalysis",
|
||||
method: "post",
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
// 查询告警统计分析
|
||||
export function alarmAnalysisDetail(data: object) {
|
||||
return service({
|
||||
url: "/largescreen/alarmAnalysisDetail",
|
||||
method: "post",
|
||||
data,
|
||||
});
|
||||
}
|
||||
// 暂降事件趋势/远程通知趋势
|
||||
export function eventTrend(data: object) {
|
||||
return service({
|
||||
url: "/largescreen/eventTrend",
|
||||
method: "post",
|
||||
data,
|
||||
});
|
||||
}
|
||||
// 获取历史暂降事件趋势
|
||||
export function noDealEventList(data: object) {
|
||||
return service({
|
||||
url: "/largescreen/noDealEventList",
|
||||
method: "post",
|
||||
data,
|
||||
});
|
||||
}
|
||||
// 处理暂降事件
|
||||
export function lookEvent(data: object) {
|
||||
return service({
|
||||
url: "/largescreen/lookEvent",
|
||||
method: "post",
|
||||
data,
|
||||
});
|
||||
}
|
||||
// 暂降事件列表
|
||||
export function eventList(data: object) {
|
||||
return service({
|
||||
url: "/largescreen/eventList",
|
||||
method: "post",
|
||||
data,
|
||||
});
|
||||
}
|
||||
// 远程通知列表
|
||||
export function msgSendList(data: object) {
|
||||
return service({
|
||||
url: "/largescreen/msgSendList",
|
||||
method: "post",
|
||||
data,
|
||||
});
|
||||
}
|
||||
// 暂降事件列表详情按钮
|
||||
export function eventMsgDetail(params: object) {
|
||||
return service({
|
||||
url: "/largescreen/eventMsgDetail",
|
||||
method: "get",
|
||||
params,
|
||||
});
|
||||
}
|
||||
// 短信处理
|
||||
export function msgHandle(data: object) {
|
||||
return service({
|
||||
url: "/largescreen/msgHandle",
|
||||
method: "post",
|
||||
data,
|
||||
});
|
||||
}
|
||||
// 地图统计数量
|
||||
export function mapCount(data: object) {
|
||||
return service({
|
||||
url: "/largescreen/mapCount",
|
||||
method: "post",
|
||||
data,
|
||||
});
|
||||
}
|
||||
// 已发送短信列表
|
||||
export function hasSendMsgPage(data: object) {
|
||||
return service({
|
||||
url: "/largescreen/hasSendMsgPage",
|
||||
method: "post",
|
||||
data,
|
||||
});
|
||||
}
|
||||
// 暂降平台配置
|
||||
export function eventConfig(data: object) {
|
||||
return service({
|
||||
url: "/config/eventConfig",
|
||||
method: "post",
|
||||
data,
|
||||
});
|
||||
}
|
||||
// 查询配置
|
||||
export function queryConfig() {
|
||||
return service({
|
||||
url: "/config/queryConfig",
|
||||
method: "get",
|
||||
});
|
||||
}
|
||||
// 模拟短信发送
|
||||
export function simulationSend(data: any) {
|
||||
return service({
|
||||
url: "/accept/simulationSend",
|
||||
method: "post",
|
||||
data,
|
||||
});
|
||||
}
|
||||
// 趋势图
|
||||
export function getTransientAnalyseWave(data: object) {
|
||||
return service({
|
||||
url: "/accept/getTransientAnalyseWave",
|
||||
method: "post",
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
// 电能质量监测终端运行状态
|
||||
export function devFlagCount(data: object) {
|
||||
return service({
|
||||
url: "/largescreen/devFlagCount",
|
||||
method: "post",
|
||||
data,
|
||||
});
|
||||
}
|
||||
// 电能质量监测终端运行状态详情
|
||||
export function devicePage(data: object) {
|
||||
return service({
|
||||
url: "/largescreen/devicePage",
|
||||
method: "post",
|
||||
data,
|
||||
});
|
||||
}
|
||||
// 各区域终端运行状态
|
||||
export function regionDevCount(data: object) {
|
||||
return service({
|
||||
url: "/largescreen/regionDevCount",
|
||||
method: "post",
|
||||
data,
|
||||
});
|
||||
}
|
||||
export function rightEventOpenForDetail(data: object) {
|
||||
return service({
|
||||
url: "/right/rightEventOpenForDetail",
|
||||
method: "post",
|
||||
data,
|
||||
});
|
||||
}
|
||||
// 供电区域下拉
|
||||
export function gdSelect() {
|
||||
return service({
|
||||
url: "/right/gdSelect",
|
||||
method: "get",
|
||||
});
|
||||
}
|
||||
|
||||
// 供电区域下拉
|
||||
export function bdSelect() {
|
||||
return service({
|
||||
url: "/right/bdSelect",
|
||||
method: "get",
|
||||
});
|
||||
}
|
||||
|
||||
// 地图查询
|
||||
export function substationCount(data: object) {
|
||||
return service({
|
||||
url: "/largescreen/substationCount",
|
||||
method: "post",
|
||||
data,
|
||||
});
|
||||
}
|
||||
// 暂降事件列表
|
||||
export function eventPage(data: object) {
|
||||
return service({
|
||||
url: "/largescreen/eventPage",
|
||||
method: "post",
|
||||
data,
|
||||
});
|
||||
}
|
||||
// 电压暂降告警统计
|
||||
export function rightImportUser(data: object) {
|
||||
return service({
|
||||
url: "/right/rightImportUser",
|
||||
method: "post",
|
||||
data,
|
||||
});
|
||||
}
|
||||
// 电压暂降告警统计
|
||||
export function rightEvent(data: object) {
|
||||
return service({
|
||||
url: "/right/rightEvent",
|
||||
method: "post",
|
||||
data,
|
||||
});
|
||||
}
|
||||
// 电压暂降告警统计弹框
|
||||
export function rightEventOpen(data: object) {
|
||||
return service({
|
||||
url: "/right/rightEventOpen",
|
||||
method: "post",
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
export function rightEventOpenClone(data: object) {
|
||||
return service({
|
||||
url: "/right/rightEventOpenClone",
|
||||
method: "post",
|
||||
data,
|
||||
});
|
||||
}
|
||||
// 重要敏感用户暂降告警统计
|
||||
export function userEventList(data: object) {
|
||||
return service({
|
||||
url: "/largescreen/userEventList",
|
||||
method: "post",
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
export function rightImportOpenDetail(data: object) {
|
||||
return service({
|
||||
url: "/right/rightImportOpenDetail",
|
||||
method: "post",
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// 对象类型
|
||||
export function getDicTree(data: object) {
|
||||
return service({
|
||||
url: "/dicTree/getDicTree",
|
||||
method: "get",
|
||||
params: data,
|
||||
});
|
||||
}
|
||||
// 终端弹框
|
||||
|
||||
export function rightEventDevOpen(data: object) {
|
||||
return service({
|
||||
url: "/right/rightEventDevOpen",
|
||||
method: "post",
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
BIN
src/assets/download.png
Normal file
|
After Width: | Height: | Size: 6.1 KiB |
1
src/assets/gaoJ.svg
Normal file
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1752714800249" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2803" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M959.800889 318.762667a30.364444 30.364444 0 0 1-11.377778 41.543111l-98.133333 56.632889-30.222222-52.864 94.435555-56.632889a47.872 47.872 0 0 1 22.641778-3.783111 34.688 34.688 0 0 1 22.656 15.104z m-898.844445 0c4.48-6.968889 11.107556-12.273778 18.887112-15.104a24.974222 24.974222 0 0 1 22.656 3.783111l94.435555 56.632889-30.222222 52.864-94.421333-56.632889a30.364444 30.364444 0 0 1-11.377778-41.543111h0.042666zM249.799111 129.962667a47.900444 47.900444 0 0 1 22.656-3.783111 37.688889 37.688889 0 0 1 18.887111 15.104l56.661334 94.407111-52.878223 30.208-56.647111-94.407111a30.364444 30.364444 0 0 1 11.377778-41.528889h-0.056889zM513.848889 56.888889a37.105778 37.105778 0 0 1 36.636444 31.530667v109.511111h-70.570666v-109.511111C476.131556 73.315556 494.961778 56.888889 513.848889 56.888889z m253.383111 73.073778a30.364444 30.364444 0 0 1 11.377778 41.528889l-56.647111 94.407111-52.878223-30.208 56.647112-94.407111a66.304 66.304 0 0 1 18.887111-15.104c7.552 0 18.887111 0 22.656 3.783111h-0.042667zM533.020444 409.329778L378.168889 665.614222h124.629333l-30.222222 181.902222 162.247111-252.387555h-132.024889l30.222222-185.799111z m-271.928888 483.100444h-64.782223V594.360889c1.521778-172.103111 141.952-310.556444 314.055111-309.632A308.48 308.48 0 0 1 819.868444 594.346667v298.069333H261.091556z m-169.955556 0H933.404444a37.12 37.12 0 0 1 33.991112 37.603556 33.877333 33.877333 0 0 1-33.991112 33.991111H91.164444a33.848889 33.848889 0 0 1-33.991111-33.991111 37.12 37.12 0 0 1 33.991111-37.603556" fill="#323233" p-id="2804"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.9 KiB |
539
src/assets/icon/demo.css
Normal file
@@ -0,0 +1,539 @@
|
||||
/* Logo 字体 */
|
||||
@font-face {
|
||||
font-family: "iconfont logo";
|
||||
src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834');
|
||||
src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834#iefix') format('embedded-opentype'),
|
||||
url('https://at.alicdn.com/t/font_985780_km7mi63cihi.woff?t=1545807318834') format('woff'),
|
||||
url('https://at.alicdn.com/t/font_985780_km7mi63cihi.ttf?t=1545807318834') format('truetype'),
|
||||
url('https://at.alicdn.com/t/font_985780_km7mi63cihi.svg?t=1545807318834#iconfont') format('svg');
|
||||
}
|
||||
|
||||
.logo {
|
||||
font-family: "iconfont logo";
|
||||
font-size: 160px;
|
||||
font-style: normal;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
/* tabs */
|
||||
.nav-tabs {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.nav-tabs .nav-more {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
height: 42px;
|
||||
line-height: 42px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
#tabs {
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
#tabs li {
|
||||
cursor: pointer;
|
||||
width: 100px;
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
text-align: center;
|
||||
font-size: 16px;
|
||||
border-bottom: 2px solid transparent;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
margin-bottom: -1px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
|
||||
#tabs .active {
|
||||
border-bottom-color: #f00;
|
||||
color: #222;
|
||||
}
|
||||
|
||||
.tab-container .content {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* 页面布局 */
|
||||
.main {
|
||||
padding: 30px 100px;
|
||||
width: 960px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.main .logo {
|
||||
color: #333;
|
||||
text-align: left;
|
||||
margin-bottom: 30px;
|
||||
line-height: 1;
|
||||
height: 110px;
|
||||
margin-top: -50px;
|
||||
overflow: hidden;
|
||||
*zoom: 1;
|
||||
}
|
||||
|
||||
.main .logo a {
|
||||
font-size: 160px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.helps {
|
||||
margin-top: 40px;
|
||||
}
|
||||
|
||||
.helps pre {
|
||||
padding: 20px;
|
||||
margin: 10px 0;
|
||||
border: solid 1px #e7e1cd;
|
||||
background-color: #fffdef;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.icon_lists {
|
||||
width: 100% !important;
|
||||
overflow: hidden;
|
||||
*zoom: 1;
|
||||
}
|
||||
|
||||
.icon_lists li {
|
||||
width: 100px;
|
||||
margin-bottom: 10px;
|
||||
margin-right: 20px;
|
||||
text-align: center;
|
||||
list-style: none !important;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.icon_lists li .code-name {
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.icon_lists .icon {
|
||||
display: block;
|
||||
height: 100px;
|
||||
line-height: 100px;
|
||||
font-size: 42px;
|
||||
margin: 10px auto;
|
||||
color: #333;
|
||||
-webkit-transition: font-size 0.25s linear, width 0.25s linear;
|
||||
-moz-transition: font-size 0.25s linear, width 0.25s linear;
|
||||
transition: font-size 0.25s linear, width 0.25s linear;
|
||||
}
|
||||
|
||||
.icon_lists .icon:hover {
|
||||
font-size: 100px;
|
||||
}
|
||||
|
||||
.icon_lists .svg-icon {
|
||||
/* 通过设置 font-size 来改变图标大小 */
|
||||
width: 1em;
|
||||
/* 图标和文字相邻时,垂直对齐 */
|
||||
vertical-align: -0.15em;
|
||||
/* 通过设置 color 来改变 SVG 的颜色/fill */
|
||||
fill: currentColor;
|
||||
/* path 和 stroke 溢出 viewBox 部分在 IE 下会显示
|
||||
normalize.css 中也包含这行 */
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.icon_lists li .name,
|
||||
.icon_lists li .code-name {
|
||||
color: #666;
|
||||
}
|
||||
|
||||
/* markdown 样式 */
|
||||
.markdown {
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
line-height: 1.8;
|
||||
}
|
||||
|
||||
.highlight {
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.markdown img {
|
||||
vertical-align: middle;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.markdown h1 {
|
||||
color: #404040;
|
||||
font-weight: 500;
|
||||
line-height: 40px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.markdown h2,
|
||||
.markdown h3,
|
||||
.markdown h4,
|
||||
.markdown h5,
|
||||
.markdown h6 {
|
||||
color: #404040;
|
||||
margin: 1.6em 0 0.6em 0;
|
||||
font-weight: 500;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.markdown h1 {
|
||||
font-size: 28px;
|
||||
}
|
||||
|
||||
.markdown h2 {
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
.markdown h3 {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.markdown h4 {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.markdown h5 {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.markdown h6 {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.markdown hr {
|
||||
height: 1px;
|
||||
border: 0;
|
||||
background: #e9e9e9;
|
||||
margin: 16px 0;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.markdown p {
|
||||
margin: 1em 0;
|
||||
}
|
||||
|
||||
.markdown>p,
|
||||
.markdown>blockquote,
|
||||
.markdown>.highlight,
|
||||
.markdown>ol,
|
||||
.markdown>ul {
|
||||
width: 80%;
|
||||
}
|
||||
|
||||
.markdown ul>li {
|
||||
list-style: circle;
|
||||
}
|
||||
|
||||
.markdown>ul li,
|
||||
.markdown blockquote ul>li {
|
||||
margin-left: 20px;
|
||||
padding-left: 4px;
|
||||
}
|
||||
|
||||
.markdown>ul li p,
|
||||
.markdown>ol li p {
|
||||
margin: 0.6em 0;
|
||||
}
|
||||
|
||||
.markdown ol>li {
|
||||
list-style: decimal;
|
||||
}
|
||||
|
||||
.markdown>ol li,
|
||||
.markdown blockquote ol>li {
|
||||
margin-left: 20px;
|
||||
padding-left: 4px;
|
||||
}
|
||||
|
||||
.markdown code {
|
||||
margin: 0 3px;
|
||||
padding: 0 5px;
|
||||
background: #eee;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.markdown strong,
|
||||
.markdown b {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.markdown>table {
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0px;
|
||||
empty-cells: show;
|
||||
border: 1px solid #e9e9e9;
|
||||
width: 95%;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.markdown>table th {
|
||||
white-space: nowrap;
|
||||
color: #333;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.markdown>table th,
|
||||
.markdown>table td {
|
||||
border: 1px solid #e9e9e9;
|
||||
padding: 8px 16px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.markdown>table th {
|
||||
background: #F7F7F7;
|
||||
}
|
||||
|
||||
.markdown blockquote {
|
||||
font-size: 90%;
|
||||
color: #999;
|
||||
border-left: 4px solid #e9e9e9;
|
||||
padding-left: 0.8em;
|
||||
margin: 1em 0;
|
||||
}
|
||||
|
||||
.markdown blockquote p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.markdown .anchor {
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.markdown .waiting {
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
.markdown h1:hover .anchor,
|
||||
.markdown h2:hover .anchor,
|
||||
.markdown h3:hover .anchor,
|
||||
.markdown h4:hover .anchor,
|
||||
.markdown h5:hover .anchor,
|
||||
.markdown h6:hover .anchor {
|
||||
opacity: 1;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.markdown>br,
|
||||
.markdown>p>br {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
|
||||
.hljs {
|
||||
display: block;
|
||||
background: white;
|
||||
padding: 0.5em;
|
||||
color: #333333;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.hljs-comment,
|
||||
.hljs-meta {
|
||||
color: #969896;
|
||||
}
|
||||
|
||||
.hljs-string,
|
||||
.hljs-variable,
|
||||
.hljs-template-variable,
|
||||
.hljs-strong,
|
||||
.hljs-emphasis,
|
||||
.hljs-quote {
|
||||
color: #df5000;
|
||||
}
|
||||
|
||||
.hljs-keyword,
|
||||
.hljs-selector-tag,
|
||||
.hljs-type {
|
||||
color: #a71d5d;
|
||||
}
|
||||
|
||||
.hljs-literal,
|
||||
.hljs-symbol,
|
||||
.hljs-bullet,
|
||||
.hljs-attribute {
|
||||
color: #0086b3;
|
||||
}
|
||||
|
||||
.hljs-section,
|
||||
.hljs-name {
|
||||
color: #63a35c;
|
||||
}
|
||||
|
||||
.hljs-tag {
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.hljs-title,
|
||||
.hljs-attr,
|
||||
.hljs-selector-id,
|
||||
.hljs-selector-class,
|
||||
.hljs-selector-attr,
|
||||
.hljs-selector-pseudo {
|
||||
color: #795da3;
|
||||
}
|
||||
|
||||
.hljs-addition {
|
||||
color: #55a532;
|
||||
background-color: #eaffea;
|
||||
}
|
||||
|
||||
.hljs-deletion {
|
||||
color: #bd2c00;
|
||||
background-color: #ffecec;
|
||||
}
|
||||
|
||||
.hljs-link {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* 代码高亮 */
|
||||
/* PrismJS 1.15.0
|
||||
https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript */
|
||||
/**
|
||||
* prism.js default theme for JavaScript, CSS and HTML
|
||||
* Based on dabblet (http://dabblet.com)
|
||||
* @author Lea Verou
|
||||
*/
|
||||
code[class*="language-"],
|
||||
pre[class*="language-"] {
|
||||
color: black;
|
||||
background: none;
|
||||
text-shadow: 0 1px white;
|
||||
font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
|
||||
text-align: left;
|
||||
white-space: pre;
|
||||
word-spacing: normal;
|
||||
word-break: normal;
|
||||
word-wrap: normal;
|
||||
line-height: 1.5;
|
||||
|
||||
-moz-tab-size: 4;
|
||||
-o-tab-size: 4;
|
||||
tab-size: 4;
|
||||
|
||||
-webkit-hyphens: none;
|
||||
-moz-hyphens: none;
|
||||
-ms-hyphens: none;
|
||||
hyphens: none;
|
||||
}
|
||||
|
||||
pre[class*="language-"]::-moz-selection,
|
||||
pre[class*="language-"] ::-moz-selection,
|
||||
code[class*="language-"]::-moz-selection,
|
||||
code[class*="language-"] ::-moz-selection {
|
||||
text-shadow: none;
|
||||
background: #b3d4fc;
|
||||
}
|
||||
|
||||
pre[class*="language-"]::selection,
|
||||
pre[class*="language-"] ::selection,
|
||||
code[class*="language-"]::selection,
|
||||
code[class*="language-"] ::selection {
|
||||
text-shadow: none;
|
||||
background: #b3d4fc;
|
||||
}
|
||||
|
||||
@media print {
|
||||
|
||||
code[class*="language-"],
|
||||
pre[class*="language-"] {
|
||||
text-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* Code blocks */
|
||||
pre[class*="language-"] {
|
||||
padding: 1em;
|
||||
margin: .5em 0;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
:not(pre)>code[class*="language-"],
|
||||
pre[class*="language-"] {
|
||||
background: #f5f2f0;
|
||||
}
|
||||
|
||||
/* Inline code */
|
||||
:not(pre)>code[class*="language-"] {
|
||||
padding: .1em;
|
||||
border-radius: .3em;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
.token.comment,
|
||||
.token.prolog,
|
||||
.token.doctype,
|
||||
.token.cdata {
|
||||
color: slategray;
|
||||
}
|
||||
|
||||
.token.punctuation {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.namespace {
|
||||
opacity: .7;
|
||||
}
|
||||
|
||||
.token.property,
|
||||
.token.tag,
|
||||
.token.boolean,
|
||||
.token.number,
|
||||
.token.constant,
|
||||
.token.symbol,
|
||||
.token.deleted {
|
||||
color: #905;
|
||||
}
|
||||
|
||||
.token.selector,
|
||||
.token.attr-name,
|
||||
.token.string,
|
||||
.token.char,
|
||||
.token.builtin,
|
||||
.token.inserted {
|
||||
color: #690;
|
||||
}
|
||||
|
||||
.token.operator,
|
||||
.token.entity,
|
||||
.token.url,
|
||||
.language-css .token.string,
|
||||
.style .token.string {
|
||||
color: #9a6e3a;
|
||||
background: hsla(0, 0%, 100%, .5);
|
||||
}
|
||||
|
||||
.token.atrule,
|
||||
.token.attr-value,
|
||||
.token.keyword {
|
||||
color: #07a;
|
||||
}
|
||||
|
||||
.token.function,
|
||||
.token.class-name {
|
||||
color: #DD4A68;
|
||||
}
|
||||
|
||||
.token.regex,
|
||||
.token.important,
|
||||
.token.variable {
|
||||
color: #e90;
|
||||
}
|
||||
|
||||
.token.important,
|
||||
.token.bold {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.token.italic {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.token.entity {
|
||||
cursor: help;
|
||||
}
|
||||
211
src/assets/icon/demo_index.html
Normal file
@@ -0,0 +1,211 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>iconfont Demo</title>
|
||||
<link rel="shortcut icon" href="//img.alicdn.com/imgextra/i4/O1CN01Z5paLz1O0zuCC7osS_!!6000000001644-55-tps-83-82.svg" type="image/x-icon"/>
|
||||
<link rel="icon" type="image/svg+xml" href="//img.alicdn.com/imgextra/i4/O1CN01Z5paLz1O0zuCC7osS_!!6000000001644-55-tps-83-82.svg"/>
|
||||
<link rel="stylesheet" href="https://g.alicdn.com/thx/cube/1.3.2/cube.min.css">
|
||||
<link rel="stylesheet" href="demo.css">
|
||||
<link rel="stylesheet" href="iconfont.css">
|
||||
<script src="iconfont.js"></script>
|
||||
<!-- jQuery -->
|
||||
<script src="https://a1.alicdn.com/oss/uploads/2018/12/26/7bfddb60-08e8-11e9-9b04-53e73bb6408b.js"></script>
|
||||
<!-- 代码高亮 -->
|
||||
<script src="https://a1.alicdn.com/oss/uploads/2018/12/26/a3f714d0-08e6-11e9-8a15-ebf944d7534c.js"></script>
|
||||
<style>
|
||||
.main .logo {
|
||||
margin-top: 0;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.main .logo a {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.main .logo .sub-title {
|
||||
margin-left: 0.5em;
|
||||
font-size: 22px;
|
||||
color: #fff;
|
||||
background: linear-gradient(-45deg, #3967FF, #B500FE);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="main">
|
||||
<h1 class="logo"><a href="https://www.iconfont.cn/" title="iconfont 首页" target="_blank">
|
||||
<img width="200" src="https://img.alicdn.com/imgextra/i3/O1CN01Mn65HV1FfSEzR6DKv_!!6000000000514-55-tps-228-59.svg">
|
||||
|
||||
</a></h1>
|
||||
<div class="nav-tabs">
|
||||
<ul id="tabs" class="dib-box">
|
||||
<li class="dib active"><span>Unicode</span></li>
|
||||
<li class="dib"><span>Font class</span></li>
|
||||
<li class="dib"><span>Symbol</span></li>
|
||||
</ul>
|
||||
|
||||
<a href="https://www.iconfont.cn/manage/index?manage_type=myprojects&projectId=4977098" target="_blank" class="nav-more">查看项目</a>
|
||||
|
||||
</div>
|
||||
<div class="tab-container">
|
||||
<div class="content unicode" style="display: block;">
|
||||
<ul class="icon_lists dib-box">
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">告警</div>
|
||||
<div class="code-name">&#xe60c;</div>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
<div class="article markdown">
|
||||
<h2 id="unicode-">Unicode 引用</h2>
|
||||
<hr>
|
||||
|
||||
<p>Unicode 是字体在网页端最原始的应用方式,特点是:</p>
|
||||
<ul>
|
||||
<li>支持按字体的方式去动态调整图标大小,颜色等等。</li>
|
||||
<li>默认情况下不支持多色,直接添加多色图标会自动去色。</li>
|
||||
</ul>
|
||||
<blockquote>
|
||||
<p>注意:新版 iconfont 支持两种方式引用多色图标:SVG symbol 引用方式和彩色字体图标模式。(使用彩色字体图标需要在「编辑项目」中开启「彩色」选项后并重新生成。)</p>
|
||||
</blockquote>
|
||||
<p>Unicode 使用步骤如下:</p>
|
||||
<h3 id="-font-face">第一步:拷贝项目下面生成的 <code>@font-face</code></h3>
|
||||
<pre><code class="language-css"
|
||||
>@font-face {
|
||||
font-family: 'iconfont';
|
||||
src: url('iconfont.woff2?t=1752716651599') format('woff2'),
|
||||
url('iconfont.woff?t=1752716651599') format('woff'),
|
||||
url('iconfont.ttf?t=1752716651599') format('truetype');
|
||||
}
|
||||
</code></pre>
|
||||
<h3 id="-iconfont-">第二步:定义使用 iconfont 的样式</h3>
|
||||
<pre><code class="language-css"
|
||||
>.iconfont {
|
||||
font-family: "iconfont" !important;
|
||||
font-size: 16px;
|
||||
font-style: normal;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
</code></pre>
|
||||
<h3 id="-">第三步:挑选相应图标并获取字体编码,应用于页面</h3>
|
||||
<pre>
|
||||
<code class="language-html"
|
||||
><span class="iconfont">&#x33;</span>
|
||||
</code></pre>
|
||||
<blockquote>
|
||||
<p>"iconfont" 是你项目下的 font-family。可以通过编辑项目查看,默认是 "iconfont"。</p>
|
||||
</blockquote>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content font-class">
|
||||
<ul class="icon_lists dib-box">
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-gaojing"></span>
|
||||
<div class="name">
|
||||
告警
|
||||
</div>
|
||||
<div class="code-name">.icon-gaojing
|
||||
</div>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
<div class="article markdown">
|
||||
<h2 id="font-class-">font-class 引用</h2>
|
||||
<hr>
|
||||
|
||||
<p>font-class 是 Unicode 使用方式的一种变种,主要是解决 Unicode 书写不直观,语意不明确的问题。</p>
|
||||
<p>与 Unicode 使用方式相比,具有如下特点:</p>
|
||||
<ul>
|
||||
<li>相比于 Unicode 语意明确,书写更直观。可以很容易分辨这个 icon 是什么。</li>
|
||||
<li>因为使用 class 来定义图标,所以当要替换图标时,只需要修改 class 里面的 Unicode 引用。</li>
|
||||
</ul>
|
||||
<p>使用步骤如下:</p>
|
||||
<h3 id="-fontclass-">第一步:引入项目下面生成的 fontclass 代码:</h3>
|
||||
<pre><code class="language-html"><link rel="stylesheet" href="./iconfont.css">
|
||||
</code></pre>
|
||||
<h3 id="-">第二步:挑选相应图标并获取类名,应用于页面:</h3>
|
||||
<pre><code class="language-html"><span class="iconfont icon-xxx"></span>
|
||||
</code></pre>
|
||||
<blockquote>
|
||||
<p>"
|
||||
iconfont" 是你项目下的 font-family。可以通过编辑项目查看,默认是 "iconfont"。</p>
|
||||
</blockquote>
|
||||
</div>
|
||||
</div>
|
||||
<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-gaojing"></use>
|
||||
</svg>
|
||||
<div class="name">告警</div>
|
||||
<div class="code-name">#icon-gaojing</div>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
<div class="article markdown">
|
||||
<h2 id="symbol-">Symbol 引用</h2>
|
||||
<hr>
|
||||
|
||||
<p>这是一种全新的使用方式,应该说这才是未来的主流,也是平台目前推荐的用法。相关介绍可以参考这篇<a href="">文章</a>
|
||||
这种用法其实是做了一个 SVG 的集合,与另外两种相比具有如下特点:</p>
|
||||
<ul>
|
||||
<li>支持多色图标了,不再受单色限制。</li>
|
||||
<li>通过一些技巧,支持像字体那样,通过 <code>font-size</code>, <code>color</code> 来调整样式。</li>
|
||||
<li>兼容性较差,支持 IE9+,及现代浏览器。</li>
|
||||
<li>浏览器渲染 SVG 的性能一般,还不如 png。</li>
|
||||
</ul>
|
||||
<p>使用步骤如下:</p>
|
||||
<h3 id="-symbol-">第一步:引入项目下面生成的 symbol 代码:</h3>
|
||||
<pre><code class="language-html"><script src="./iconfont.js"></script>
|
||||
</code></pre>
|
||||
<h3 id="-css-">第二步:加入通用 CSS 代码(引入一次就行):</h3>
|
||||
<pre><code class="language-html"><style>
|
||||
.icon {
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
vertical-align: -0.15em;
|
||||
fill: currentColor;
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
||||
</code></pre>
|
||||
<h3 id="-">第三步:挑选相应图标并获取类名,应用于页面:</h3>
|
||||
<pre><code class="language-html"><svg class="icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-xxx"></use>
|
||||
</svg>
|
||||
</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
$(document).ready(function () {
|
||||
$('.tab-container .content:first').show()
|
||||
|
||||
$('#tabs li').click(function (e) {
|
||||
var tabContent = $('.tab-container .content')
|
||||
var index = $(this).index()
|
||||
|
||||
if ($(this).hasClass('active')) {
|
||||
return
|
||||
} else {
|
||||
$('#tabs li').removeClass('active')
|
||||
$(this).addClass('active')
|
||||
|
||||
tabContent.hide().eq(index).fadeIn()
|
||||
}
|
||||
})
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
19
src/assets/icon/iconfont.css
Normal file
@@ -0,0 +1,19 @@
|
||||
@font-face {
|
||||
font-family: "iconfont"; /* Project id 4977098 */
|
||||
src: url('iconfont.woff2?t=1752716651599') format('woff2'),
|
||||
url('iconfont.woff?t=1752716651599') format('woff'),
|
||||
url('iconfont.ttf?t=1752716651599') format('truetype');
|
||||
}
|
||||
|
||||
.iconfont {
|
||||
font-family: "iconfont" !important;
|
||||
font-size: 16px;
|
||||
font-style: normal;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.icon-gaojing:before {
|
||||
content: "\e60c";
|
||||
}
|
||||
|
||||
1
src/assets/icon/iconfont.js
Normal file
@@ -0,0 +1 @@
|
||||
window._iconfont_svg_string_4977098='<svg><symbol id="icon-gaojing" viewBox="0 0 1024 1024"><path d="M959.800889 318.762667a30.364444 30.364444 0 0 1-11.377778 41.543111l-98.133333 56.632889-30.222222-52.864 94.435555-56.632889a47.872 47.872 0 0 1 22.641778-3.783111 34.688 34.688 0 0 1 22.656 15.104z m-898.844445 0c4.48-6.968889 11.107556-12.273778 18.887112-15.104a24.974222 24.974222 0 0 1 22.656 3.783111l94.435555 56.632889-30.222222 52.864-94.421333-56.632889a30.364444 30.364444 0 0 1-11.377778-41.543111h0.042666zM249.799111 129.962667a47.900444 47.900444 0 0 1 22.656-3.783111 37.688889 37.688889 0 0 1 18.887111 15.104l56.661334 94.407111-52.878223 30.208-56.647111-94.407111a30.364444 30.364444 0 0 1 11.377778-41.528889h-0.056889zM513.848889 56.888889a37.105778 37.105778 0 0 1 36.636444 31.530667v109.511111h-70.570666v-109.511111C476.131556 73.315556 494.961778 56.888889 513.848889 56.888889z m253.383111 73.073778a30.364444 30.364444 0 0 1 11.377778 41.528889l-56.647111 94.407111-52.878223-30.208 56.647112-94.407111a66.304 66.304 0 0 1 18.887111-15.104c7.552 0 18.887111 0 22.656 3.783111h-0.042667zM533.020444 409.329778L378.168889 665.614222h124.629333l-30.222222 181.902222 162.247111-252.387555h-132.024889l30.222222-185.799111z m-271.928888 483.100444h-64.782223V594.360889c1.521778-172.103111 141.952-310.556444 314.055111-309.632A308.48 308.48 0 0 1 819.868444 594.346667v298.069333H261.091556z m-169.955556 0H933.404444a37.12 37.12 0 0 1 33.991112 37.603556 33.877333 33.877333 0 0 1-33.991112 33.991111H91.164444a33.848889 33.848889 0 0 1-33.991111-33.991111 37.12 37.12 0 0 1 33.991111-37.603556" fill="#323233" ></path></symbol></svg>',(n=>{var t=(e=(e=document.getElementsByTagName("script"))[e.length-1]).getAttribute("data-injectcss"),e=e.getAttribute("data-disable-injectsvg");if(!e){var o,i,a,c,d,l=function(t,e){e.parentNode.insertBefore(t,e)};if(t&&!n.__iconfont__svg__cssinject__){n.__iconfont__svg__cssinject__=!0;try{document.write("<style>.svgfont {display: inline-block;width: 1em;height: 1em;fill: currentColor;vertical-align: -0.1em;font-size:16px;}</style>")}catch(t){console&&console.log(t)}}o=function(){var t,e=document.createElement("div");e.innerHTML=n._iconfont_svg_string_4977098,(e=e.getElementsByTagName("svg")[0])&&(e.setAttribute("aria-hidden","true"),e.style.position="absolute",e.style.width=0,e.style.height=0,e.style.overflow="hidden",e=e,(t=document.body).firstChild?l(e,t.firstChild):t.appendChild(e))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(o,0):(i=function(){document.removeEventListener("DOMContentLoaded",i,!1),o()},document.addEventListener("DOMContentLoaded",i,!1)):document.attachEvent&&(a=o,c=n.document,d=!1,r(),c.onreadystatechange=function(){"complete"==c.readyState&&(c.onreadystatechange=null,s())})}function s(){d||(d=!0,a())}function r(){try{c.documentElement.doScroll("left")}catch(t){return void setTimeout(r,50)}s()}})(window);
|
||||
16
src/assets/icon/iconfont.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"id": "4977098",
|
||||
"name": "北京暂降平台",
|
||||
"font_family": "iconfont",
|
||||
"css_prefix_text": "icon-",
|
||||
"description": "",
|
||||
"glyphs": [
|
||||
{
|
||||
"icon_id": "36284864",
|
||||
"name": "告警",
|
||||
"font_class": "gaojing",
|
||||
"unicode": "e60c",
|
||||
"unicode_decimal": 58892
|
||||
}
|
||||
]
|
||||
}
|
||||
BIN
src/assets/icon/iconfont.ttf
Normal file
BIN
src/assets/icon/iconfont.woff
Normal file
BIN
src/assets/icon/iconfont.woff2
Normal file
BIN
src/assets/icon/传输设备 (1).png
Normal file
|
After Width: | Height: | Size: 4.9 KiB |
BIN
src/assets/icon/传输设备 (2).png
Normal file
|
After Width: | Height: | Size: 5.6 KiB |
BIN
src/assets/icon/传输设备 (3).png
Normal file
|
After Width: | Height: | Size: 5.7 KiB |
BIN
src/assets/icon/传输设备 (4).png
Normal file
|
After Width: | Height: | Size: 5.7 KiB |
BIN
src/assets/icon/传输设备.png
Normal file
|
After Width: | Height: | Size: 4.7 KiB |
BIN
src/assets/img/bdz.png
Normal file
|
After Width: | Height: | Size: 727 B |
BIN
src/assets/img/dw.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
src/assets/img/point.png
Normal file
|
After Width: | Height: | Size: 3.1 KiB |
BIN
src/assets/img/text.png
Normal file
|
After Width: | Height: | Size: 523 B |
BIN
src/assets/img/title.png
Normal file
|
After Width: | Height: | Size: 1.1 MiB |
BIN
src/assets/jcd.png
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
src/assets/mp3/9578.mp3
Normal file
BIN
src/assets/pageBg.png
Normal file
|
After Width: | Height: | Size: 289 KiB |
198
src/assets/scss/SagTraceResult_WX.scss
Normal file
@@ -0,0 +1,198 @@
|
||||
#index {
|
||||
color: #d3d6dd;
|
||||
width: 1920px;
|
||||
height: 1080px;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
transform-origin: left top;
|
||||
.bg {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 16px 16px 0 16px;
|
||||
background-image: url("../../assets/pageBg.png");
|
||||
background-size: cover;
|
||||
background-position: center center;
|
||||
}
|
||||
.headerBox {
|
||||
width: 100%;
|
||||
height: 124px;
|
||||
background-image: url("../../assets/download.png");
|
||||
background-size: cover;
|
||||
background-position: center center;
|
||||
}
|
||||
.secondLine {
|
||||
position: absolute;
|
||||
right: 20px;
|
||||
top: 30px;
|
||||
}
|
||||
.host-body {
|
||||
//头部样式
|
||||
.dv-dec-10,
|
||||
.dv-dec-10-s {
|
||||
width: 33.3%;
|
||||
height: 5px;
|
||||
}
|
||||
.dv-dec-10-s {
|
||||
transform: rotateY(180deg);
|
||||
}
|
||||
.dv-dec-8 {
|
||||
width: 200px;
|
||||
height: 50px;
|
||||
}
|
||||
.title {
|
||||
position: relative;
|
||||
width: 500px;
|
||||
text-align: center;
|
||||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
|
||||
.title-text {
|
||||
width: 100%;
|
||||
font-size: 35px;
|
||||
position: absolute;
|
||||
line-height: 35px;
|
||||
top: 10px;
|
||||
left: 50%;
|
||||
font-weight: 700;
|
||||
transform: translate(-50%);
|
||||
}
|
||||
|
||||
.dv-dec-6 {
|
||||
position: absolute;
|
||||
bottom: -30px;
|
||||
left: 50%;
|
||||
width: 250px;
|
||||
height: 8px;
|
||||
transform: translate(-50%);
|
||||
}
|
||||
}
|
||||
|
||||
// 第二行
|
||||
|
||||
.react-r-s,
|
||||
.react-l-s {
|
||||
background-color: #0f1325;
|
||||
}
|
||||
|
||||
// 平行四边形
|
||||
.react-right {
|
||||
&.react-l-s {
|
||||
text-align: right;
|
||||
width: 150px;
|
||||
}
|
||||
font-size: 18px;
|
||||
width: 200px;
|
||||
line-height: 30px;
|
||||
text-align: center;
|
||||
transform: skewX(-45deg);
|
||||
|
||||
.react-after {
|
||||
position: absolute;
|
||||
right: -25px;
|
||||
top: 0;
|
||||
height: 24px;
|
||||
width: 50px;
|
||||
background-color: #0f1325;
|
||||
transform: skewX(45deg);
|
||||
}
|
||||
|
||||
.text {
|
||||
display: inline-block;
|
||||
transform: skewX(45deg);
|
||||
}
|
||||
}
|
||||
|
||||
// .react-left {
|
||||
// &.react-l-s {
|
||||
// width: 500px;
|
||||
// text-align: left;
|
||||
// }
|
||||
// font-size: 18px;
|
||||
// width: 100px;
|
||||
// height: 50px;
|
||||
// line-height: 50px;
|
||||
// text-align: center;
|
||||
// transform: skewX(45deg);
|
||||
// background-color: #0f1325;
|
||||
|
||||
// .react-before {
|
||||
// position: absolute;
|
||||
// left: -25px;
|
||||
// top: 0;
|
||||
// height: 50px;
|
||||
// width: 50px;
|
||||
// background-color: #0f1325;
|
||||
// transform: skewX(-45deg);
|
||||
// }
|
||||
|
||||
// .text {
|
||||
// display: inline-block;
|
||||
// transform: skewX(-45deg);
|
||||
// }
|
||||
// }
|
||||
|
||||
.body-box {
|
||||
margin-top: 10px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
//下方区域的布局
|
||||
.content-box {
|
||||
display: grid;
|
||||
gap: 5px;
|
||||
grid-template-columns: 3fr 1fr;
|
||||
// grid-template-columns: 1fr 2fr 1fr;
|
||||
}
|
||||
//下方区域的布局
|
||||
.content-left {
|
||||
display: grid;
|
||||
gap: 5px;
|
||||
grid-template-rows: 1fr, 2fr;
|
||||
// grid-template-columns: 1fr 2fr 1fr;
|
||||
}
|
||||
// 底部数据
|
||||
.bototm-box {
|
||||
margin-top: 5px;
|
||||
display: grid;
|
||||
gap: 5px;
|
||||
grid-template-columns: 1fr 2fr 1fr;
|
||||
}
|
||||
}
|
||||
}
|
||||
.dv-border-box-13 {
|
||||
padding: 20px 10px 0 10px;
|
||||
}
|
||||
.writing {
|
||||
text-decoration: underline;
|
||||
color: #ffcc00;
|
||||
cursor: pointer;
|
||||
margin-right: 20px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.titleBox {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
line-height: 32px;
|
||||
font-size: 18px;
|
||||
padding-left: 20px;
|
||||
margin: 0 auto;
|
||||
// background-image: url("@/assets/img/title.png");
|
||||
// background-size: cover;
|
||||
// background-position: center center;
|
||||
background: linear-gradient(
|
||||
90deg,
|
||||
#cccccc80 0%,
|
||||
#1d83ce70 1%,
|
||||
#1d83ce50 35%,
|
||||
#1d83ce30 70%,
|
||||
#1d83ce10 100% /* 活力橙 */
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
97
src/assets/scss/_variables.scss
Normal file
@@ -0,0 +1,97 @@
|
||||
// 颜色
|
||||
$colors: (
|
||||
"primary": #db9e3f,
|
||||
"info-1": #4394e4,
|
||||
"info": #4b67af,
|
||||
"white": #ffffff,
|
||||
"light": #f9f9f9,
|
||||
"grey-1": #999999,
|
||||
"grey": #666666,
|
||||
"dark-1": #5f5f5f,
|
||||
"dark": #222222,
|
||||
"black-1": #171823,
|
||||
"black": #000000,
|
||||
);
|
||||
|
||||
// 字体大小
|
||||
$base-font-size: 16px;
|
||||
$font-sizes: (
|
||||
xxs: 0.1,
|
||||
//8px
|
||||
xs: 0.125,
|
||||
//10px
|
||||
sm: 0.2875,
|
||||
//12px
|
||||
md: 0.1625,
|
||||
//13px
|
||||
lg: 0.175,
|
||||
//14px
|
||||
xl: 0.2,
|
||||
//16px
|
||||
xxl: 0.225,
|
||||
//18px
|
||||
xxxl: 0.25 //20px,,,,
|
||||
);
|
||||
|
||||
// 宽高
|
||||
.w-100 {
|
||||
width: 100%;
|
||||
}
|
||||
.h-100 {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
//flex
|
||||
.d-flex {
|
||||
display: flex;
|
||||
}
|
||||
.flex-column {
|
||||
flex-direction: column;
|
||||
}
|
||||
.flex-wrap {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.flex-nowrap {
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
$flex-jc: (
|
||||
start: flex-start,
|
||||
end: flex-end,
|
||||
center: center,
|
||||
between: space-between,
|
||||
around: space-around,
|
||||
evenly: space-evenly,
|
||||
);
|
||||
|
||||
$flex-ai: (
|
||||
start: flex-start,
|
||||
end: flex-end,
|
||||
center: center,
|
||||
stretch: stretch,
|
||||
);
|
||||
|
||||
.flex-1 {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
//.mt-1 => margin top
|
||||
//spacing
|
||||
$spacing-types: (
|
||||
m: margin,
|
||||
p: padding,
|
||||
);
|
||||
$spacing-directions: (
|
||||
t: top,
|
||||
r: right,
|
||||
b: bottom,
|
||||
l: left,
|
||||
);
|
||||
$spacing-base-size: 16px;
|
||||
$spacing-sizes: (
|
||||
0: 0,
|
||||
1: 0.25,
|
||||
2: 0.5,
|
||||
3: 1,
|
||||
4: 1.5,
|
||||
5: 3,
|
||||
);
|
||||
101
src/assets/scss/element.scss
Normal file
@@ -0,0 +1,101 @@
|
||||
:deep(.el-dialog__body) {
|
||||
color: #fff !important;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
:deep(.el-form-item__label) {
|
||||
color: #fff !important;
|
||||
}
|
||||
:deep(.el-dialog__header) {
|
||||
padding: 10px;
|
||||
background-color: #21232b;
|
||||
border-bottom: 1px solid #fff;
|
||||
}
|
||||
:deep(.el-dialog__footer) {
|
||||
padding: 0 10px 10px !important;
|
||||
}
|
||||
|
||||
:deep(.el-descriptions__label.el-descriptions__cell) {
|
||||
background: #0a73ff40 !important;
|
||||
color: #fff !important;
|
||||
font-weight: bold;
|
||||
}
|
||||
:deep(.el-descriptions) {
|
||||
--el-descriptions-table-border: 1px solid #0a73ff;
|
||||
.el-descriptions__content.el-descriptions__cell.is-bordered-content {
|
||||
color: #fff;
|
||||
}
|
||||
.el-descriptions__body {
|
||||
background-color: #00000050;
|
||||
}
|
||||
}
|
||||
:deep(.tableBox) {
|
||||
padding: 10px;
|
||||
.el-table {
|
||||
--el-table-border-color: #0a73ff;
|
||||
--el-table-row-hover-bg-color: #0a73ff20;
|
||||
--el-table-header-bg-color: #0a73ff40;
|
||||
--el-table-bg-color: #ffffff00;
|
||||
text-align: center;
|
||||
// th {
|
||||
// background-color: #0a73ff;
|
||||
// color: #fff;
|
||||
// }
|
||||
tr {
|
||||
background-color: #00000050 !important;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
:deep(
|
||||
.el-table--striped
|
||||
.el-table__body
|
||||
tr.el-table__row--striped
|
||||
td.el-table__cell
|
||||
) {
|
||||
background: #5aa1ff29;
|
||||
}
|
||||
.titles {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
margin-left: 20px;
|
||||
|
||||
div {
|
||||
background-color: #0a73ff70;
|
||||
color: #ccc;
|
||||
cursor: pointer;
|
||||
&:nth-child(1) {
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
.titleClick {
|
||||
background-color: #0a73ff;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
:deep(.el-dialog) {
|
||||
--el-dialog-bg-color: #343849c7 !important;
|
||||
padding: 0px;
|
||||
--el-dialog-margin-top: 8vh;
|
||||
.el-dialog__title,
|
||||
.el-dialog__close {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
:deep(.el-drawer) {
|
||||
--el-drawer-bg-color: #343849c7;
|
||||
color: #fff;
|
||||
.el-drawer__header {
|
||||
// background-color: #21232b;
|
||||
background-color: #9f1700;
|
||||
}
|
||||
}
|
||||
:deep(.el-pagination) {
|
||||
.el-pagination__total,
|
||||
.el-pagination__goto,
|
||||
.el-pagination__classifier {
|
||||
color: #fff;
|
||||
}
|
||||
// --el-text-color-regular: #fff;
|
||||
}
|
||||
348
src/assets/scss/index.css
Normal file
@@ -0,0 +1,348 @@
|
||||
@charset "UTF-8";
|
||||
#index {
|
||||
color: #d3d6dd;
|
||||
width: 1920px;
|
||||
height: 1080px;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
transform-origin: left top;
|
||||
--el-text-color-primary: #9b9b9b;
|
||||
}
|
||||
|
||||
#index .bg {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 16px 16px 0 16px;
|
||||
background-image: url("../../assets/pageBg.png");
|
||||
background-size: cover;
|
||||
background-position: center center;
|
||||
}
|
||||
|
||||
#index .headerBox {
|
||||
width: 100%;
|
||||
height: 124px;
|
||||
background-image: url("../../assets/download.png");
|
||||
background-size: cover;
|
||||
background-position: center center;
|
||||
}
|
||||
|
||||
#index .secondLine {
|
||||
position: absolute;
|
||||
right: 20px;
|
||||
top: 30px;
|
||||
}
|
||||
|
||||
#index .host-body .dv-dec-10,
|
||||
#index .host-body .dv-dec-10-s {
|
||||
width: 33.3%;
|
||||
height: 5px;
|
||||
}
|
||||
|
||||
#index .host-body .dv-dec-10-s {
|
||||
transform: rotateY(180deg);
|
||||
}
|
||||
|
||||
#index .host-body .dv-dec-8 {
|
||||
width: 200px;
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
#index .host-body .title {
|
||||
position: relative;
|
||||
width: 500px;
|
||||
text-align: center;
|
||||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
#index .host-body .title .title-text {
|
||||
width: 100%;
|
||||
font-size: 35px;
|
||||
position: absolute;
|
||||
line-height: 35px;
|
||||
top: 10px;
|
||||
left: 50%;
|
||||
font-weight: 700;
|
||||
transform: translate(-50%);
|
||||
}
|
||||
|
||||
#index .host-body .title .dv-dec-6 {
|
||||
position: absolute;
|
||||
bottom: -30px;
|
||||
left: 50%;
|
||||
width: 250px;
|
||||
height: 8px;
|
||||
transform: translate(-50%);
|
||||
}
|
||||
|
||||
#index .host-body .react-r-s,
|
||||
#index .host-body .react-l-s {
|
||||
background-color: #0f1325;
|
||||
}
|
||||
|
||||
#index .host-body .react-right {
|
||||
font-size: 15px;
|
||||
width: 200px;
|
||||
line-height: 30px;
|
||||
text-align: center;
|
||||
transform: skewX(-45deg);
|
||||
}
|
||||
|
||||
#index .host-body .react-right.react-l-s {
|
||||
text-align: right;
|
||||
width: 150px;
|
||||
}
|
||||
|
||||
#index .host-body .react-right .react-after {
|
||||
position: absolute;
|
||||
right: -25px;
|
||||
top: 0;
|
||||
height: 24px;
|
||||
width: 50px;
|
||||
background-color: #0f1325;
|
||||
transform: skewX(45deg);
|
||||
}
|
||||
|
||||
#index .host-body .react-right .text {
|
||||
display: inline-block;
|
||||
transform: skewX(45deg);
|
||||
}
|
||||
|
||||
#index .host-body .body-box {
|
||||
margin-top: 10px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
#index .host-body .body-box .content-box {
|
||||
display: grid;
|
||||
gap: 5px;
|
||||
grid-template-columns: 1fr 2fr 1fr;
|
||||
}
|
||||
|
||||
#index .host-body .body-box .bototm-box {
|
||||
margin-top: 5px;
|
||||
display: grid;
|
||||
gap: 5px;
|
||||
grid-template-columns: 1fr 2fr 1fr;
|
||||
}
|
||||
|
||||
#index .dv-border-box-13 {
|
||||
padding: 20px 10px 0 10px;
|
||||
}
|
||||
|
||||
#index .writing {
|
||||
text-decoration: underline;
|
||||
color: #daa520;
|
||||
cursor: pointer;
|
||||
margin-right: 20px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
#index .titleBox {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
line-height: 32px;
|
||||
font-size: 18px;
|
||||
padding-left: 20px;
|
||||
margin: 0 auto;
|
||||
background-image: url("@/assets/img/title.png");
|
||||
background-size: cover;
|
||||
background-position: center center;
|
||||
}
|
||||
|
||||
.el-form-item {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
/* 呼吸闪烁的容器样式 红色*/
|
||||
.bg-red {
|
||||
/* 设置初始的边框样式 */
|
||||
/* 添加呼吸动画 */
|
||||
animation: breathing_red 2s ease-in-out infinite;
|
||||
}
|
||||
|
||||
/* 定义呼吸动画 */
|
||||
@keyframes breathing_red {
|
||||
0% {
|
||||
/* 开始时边框颜色较淡 */
|
||||
border-color: rgba(255, 0, 0, 0.3);
|
||||
box-shadow: inset 0 0 50px rgba(255, 0, 0, 0.2);
|
||||
}
|
||||
50% {
|
||||
/* 中间时边框颜色最深 */
|
||||
border-color: red;
|
||||
box-shadow: inset 0 0 100px rgba(255, 0, 0, 0.8);
|
||||
}
|
||||
100% {
|
||||
/* 结束时边框颜色回到较淡状态 */
|
||||
border-color: rgba(255, 0, 0, 0.3);
|
||||
box-shadow: inset 0 0 50px rgba(255, 0, 0, 0.2);
|
||||
}
|
||||
}
|
||||
|
||||
/* 呼吸闪烁的容器样式 */
|
||||
.bg-yellow {
|
||||
/* 设置初始的边框样式 */
|
||||
/* 添加呼吸动画 */
|
||||
animation: breathing_yellow 2s ease-in-out infinite;
|
||||
}
|
||||
|
||||
/* 定义呼吸动画 */
|
||||
@keyframes breathing_yellow {
|
||||
0% {
|
||||
/* 开始时边框颜色较淡 */
|
||||
border-color: rgba(255, 219, 0, 0.3);
|
||||
box-shadow: inset 0 0 50px rgba(255, 219, 0, 0.2);
|
||||
}
|
||||
50% {
|
||||
/* 中间时边框颜色最深 */
|
||||
border-color: #ffdb00;
|
||||
box-shadow: inset 0 0 100px rgba(255, 219, 0, 0.8);
|
||||
}
|
||||
100% {
|
||||
/* 结束时边框颜色回到较淡状态 */
|
||||
border-color: rgba(255, 219, 0, 0.3);
|
||||
box-shadow: inset 0 0 50px rgba(255, 219, 0, 0.2);
|
||||
}
|
||||
}
|
||||
|
||||
.scroll-box {
|
||||
width: 1820px;
|
||||
height: 40px;
|
||||
padding: 10px 15px;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
left: 50px;
|
||||
}
|
||||
|
||||
.scroll-content {
|
||||
position: absolute;
|
||||
white-space: nowrap;
|
||||
animation: scroll 0s linear infinite;
|
||||
/* 根据内容长度调整动画时间 */
|
||||
padding-left: 100%;
|
||||
/* 初始位置偏移 */
|
||||
}
|
||||
|
||||
@keyframes scroll {
|
||||
0% {
|
||||
transform: translateX(0);
|
||||
}
|
||||
100% {
|
||||
transform: translateX(-100%);
|
||||
}
|
||||
}
|
||||
|
||||
/* 闪烁动画定义 */
|
||||
@keyframes flash {
|
||||
0%,
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
50% {
|
||||
opacity: 0.3;
|
||||
/* 半透明效果实现闪烁 */
|
||||
}
|
||||
}
|
||||
|
||||
.animate-flash-red {
|
||||
animation: flash 1s infinite;
|
||||
/* 1秒周期,无限循环 */
|
||||
color: #ff2501;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.animate-flash-yellow {
|
||||
animation: flash 1s infinite;
|
||||
/* 1秒周期,无限循环 */
|
||||
color: #bb7b00;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* 悬停暂停效果 */
|
||||
.scroll-box:hover .scroll-content {
|
||||
animation-play-state: paused;
|
||||
}
|
||||
|
||||
:deep(.el-divider--horizontal) {
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
@keyframes step-scale {
|
||||
0% {
|
||||
transform: scale(1.2);
|
||||
}
|
||||
50% {
|
||||
transform: scale(1.2);
|
||||
}
|
||||
/* 突变时放大 */
|
||||
100% {
|
||||
transform: scale(1.2);
|
||||
}
|
||||
/* 稳定后保持放大 */
|
||||
}
|
||||
|
||||
.step-scale {
|
||||
animation: step-scale 1s infinite;
|
||||
/* forwards保持最终状态 */
|
||||
text-align: center;
|
||||
color: #ff0000;
|
||||
/* 警告色 */
|
||||
}
|
||||
|
||||
div {
|
||||
/* 滚动条轨道 */
|
||||
/* 滚动条滑块hover状态 */
|
||||
}
|
||||
|
||||
div::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
/* 滚动条宽度 */
|
||||
}
|
||||
|
||||
div::-webkit-scrollbar-thumb {
|
||||
border-radius: 10px;
|
||||
/* 滑块圆角 */
|
||||
background: rgba(204, 204, 204, 0.8);
|
||||
/* 设置滚动条轨道背景色 */
|
||||
cursor: pointer;
|
||||
transition: var(--el-transition-duration) background-color;
|
||||
}
|
||||
|
||||
div::-webkit-scrollbar-thumb:hover {
|
||||
background: rgba(204, 204, 204, 0.5);
|
||||
/* 设置滚动条轨道背景色 */
|
||||
}
|
||||
|
||||
div .el-divider--horizontal {
|
||||
border-top: 1px var(--el-color-primary) var(--el-border-style);
|
||||
}
|
||||
|
||||
:deep(.el-input-group__append, .el-input-group__prepend) {
|
||||
background-color: #ffffff00;
|
||||
}
|
||||
|
||||
:deep(.frontBox) {
|
||||
background-color: var(--el-color-primary) !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
::v-deep(.el-tabs__item) {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
::v-deep(.el-tabs__item.is-active) {
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
::v-deep(.el-tabs__nav-wrap::after) {
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
::v-deep(.el-tabs__active-bar) {
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
1
src/assets/scss/index.min.css
vendored
Normal file
@@ -0,0 +1 @@
|
||||
#index{color:#d3d6dd;width:1920px;height:1080px;position:absolute;top:50%;left:50%;transform:translate(-50%, -50%);transform-origin:left top;--el-text-color-primary:#9b9b9b}#index .bg{width:100%;height:100%;padding:16px 16px 0 16px;background-image:url("../../assets/pageBg.png");background-size:cover;background-position:center center}#index .headerBox{width:100%;height:124px;background-image:url("../../assets/download.png");background-size:cover;background-position:center center}#index .secondLine{position:absolute;right:20px;top:30px}#index .host-body .dv-dec-10,#index .host-body .dv-dec-10-s{width:33.3%;height:5px}#index .host-body .dv-dec-10-s{transform:rotateY(180deg)}#index .host-body .dv-dec-8{width:200px;height:50px}#index .host-body .title{position:relative;width:500px;text-align:center;background-size:cover;background-repeat:no-repeat}#index .host-body .title .title-text{width:100%;font-size:35px;position:absolute;line-height:35px;top:10px;left:50%;font-weight:700;transform:translate(-50%)}#index .host-body .title .dv-dec-6{position:absolute;bottom:-30px;left:50%;width:250px;height:8px;transform:translate(-50%)}#index .host-body .react-r-s,#index .host-body .react-l-s{background-color:#0f1325}#index .host-body .react-right{font-size:15px;width:200px;line-height:30px;text-align:center;transform:skewX(-45deg)}#index .host-body .react-right.react-l-s{text-align:right;width:150px}#index .host-body .react-right .react-after{position:absolute;right:-25px;top:0;height:24px;width:50px;background-color:#0f1325;transform:skewX(45deg)}#index .host-body .react-right .text{display:inline-block;transform:skewX(45deg)}#index .host-body .body-box{margin-top:10px;display:flex;flex-direction:column}#index .host-body .body-box .content-box{display:grid;gap:5px;grid-template-columns:1fr 2fr 1fr}#index .host-body .body-box .bototm-box{margin-top:5px;display:grid;gap:5px;grid-template-columns:1fr 2fr 1fr}#index .dv-border-box-13{padding:20px 10px 0 10px}#index .writing{text-decoration:underline;color:#daa520;cursor:pointer;margin-right:20px;white-space:nowrap}#index .titleBox{display:flex;justify-content:space-between;width:100%;line-height:32px;font-size:18px;padding-left:20px;margin:0 auto;background-image:url("@/assets/img/title.png");background-size:cover;background-position:center center}.el-form-item{margin-bottom:10px}.bg-red{animation:breathing_red 2s ease-in-out infinite}@keyframes breathing_red{0%{border-color:rgba(255,0,0,0.3);box-shadow:inset 0 0 50px rgba(255,0,0,0.2)}50%{border-color:red;box-shadow:inset 0 0 100px rgba(255,0,0,0.8)}100%{border-color:rgba(255,0,0,0.3);box-shadow:inset 0 0 50px rgba(255,0,0,0.2)}}.bg-yellow{animation:breathing_yellow 2s ease-in-out infinite}@keyframes breathing_yellow{0%{border-color:rgba(255,219,0,0.3);box-shadow:inset 0 0 50px rgba(255,219,0,0.2)}50%{border-color:#ffdb00;box-shadow:inset 0 0 100px rgba(255,219,0,0.8)}100%{border-color:rgba(255,219,0,0.3);box-shadow:inset 0 0 50px rgba(255,219,0,0.2)}}.scroll-box{width:1820px;height:40px;padding:10px 15px;overflow:hidden;position:relative;left:50px}.scroll-content{position:absolute;white-space:nowrap;animation:scroll 0s linear infinite;padding-left:100%}@keyframes scroll{0%{transform:translateX(0)}100%{transform:translateX(-100%)}}@keyframes flash{0%,100%{opacity:1}50%{opacity:0.3}}.animate-flash-red{animation:flash 1s infinite;color:#ff2501;cursor:pointer}.animate-flash-yellow{animation:flash 1s infinite;color:#bb7b00;cursor:pointer}.scroll-box:hover .scroll-content{animation-play-state:paused}:deep(.el-divider--horizontal){margin:10px 0}@keyframes step-scale{0%{transform:scale(1.2)}50%{transform:scale(1.2)}100%{transform:scale(1.2)}}.step-scale{animation:step-scale 1s infinite;text-align:center;color:#ff0000}div::-webkit-scrollbar{width:6px}div::-webkit-scrollbar-thumb{border-radius:10px;background:rgba(204,204,204,0.8);cursor:pointer;transition:var(--el-transition-duration) background-color}div::-webkit-scrollbar-thumb:hover{background:rgba(204,204,204,0.5)}div .el-divider--horizontal{border-top:1px var(--el-color-primary) var(--el-border-style)}:deep(.el-input-group__append,.el-input-group__prepend){background-color:#ffffff00}:deep(.frontBox){background-color:var(--el-color-primary) !important;color:#fff !important}::v-deep(.el-tabs__item){color:#fff}::v-deep(.el-tabs__item.is-active){color:var(--el-color-primary)}::v-deep(.el-tabs__nav-wrap::after){color:var(--el-color-primary)}::v-deep(.el-tabs__active-bar){color:var(--el-color-primary)}
|
||||
368
src/assets/scss/index.scss
Normal file
@@ -0,0 +1,368 @@
|
||||
#index {
|
||||
color: #d3d6dd;
|
||||
width: 1920px;
|
||||
height: 1080px;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
transform-origin: left top;
|
||||
--el-text-color-primary: #9b9b9b;
|
||||
.bg {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 16px 16px 0 16px;
|
||||
background-image: url("../../assets/pageBg.png");
|
||||
background-size: cover;
|
||||
background-position: center center;
|
||||
}
|
||||
.headerBox {
|
||||
width: 100%;
|
||||
height: 124px;
|
||||
background-image: url("../../assets/download.png");
|
||||
background-size: cover;
|
||||
background-position: center center;
|
||||
}
|
||||
.secondLine {
|
||||
position: absolute;
|
||||
right: 20px;
|
||||
top: 30px;
|
||||
}
|
||||
.host-body {
|
||||
//头部样式
|
||||
.dv-dec-10,
|
||||
.dv-dec-10-s {
|
||||
width: 33.3%;
|
||||
height: 5px;
|
||||
}
|
||||
.dv-dec-10-s {
|
||||
transform: rotateY(180deg);
|
||||
}
|
||||
.dv-dec-8 {
|
||||
width: 200px;
|
||||
height: 50px;
|
||||
}
|
||||
.title {
|
||||
position: relative;
|
||||
width: 500px;
|
||||
text-align: center;
|
||||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
|
||||
.title-text {
|
||||
width: 100%;
|
||||
font-size: 35px;
|
||||
position: absolute;
|
||||
line-height: 35px;
|
||||
top: 10px;
|
||||
left: 50%;
|
||||
font-weight: 700;
|
||||
transform: translate(-50%);
|
||||
}
|
||||
|
||||
.dv-dec-6 {
|
||||
position: absolute;
|
||||
bottom: -30px;
|
||||
left: 50%;
|
||||
width: 250px;
|
||||
height: 8px;
|
||||
transform: translate(-50%);
|
||||
}
|
||||
}
|
||||
|
||||
// 第二行
|
||||
|
||||
.react-r-s,
|
||||
.react-l-s {
|
||||
background-color: #0f1325;
|
||||
}
|
||||
|
||||
// 平行四边形
|
||||
.react-right {
|
||||
&.react-l-s {
|
||||
text-align: right;
|
||||
width: 150px;
|
||||
}
|
||||
font-size: 15px;
|
||||
width: 200px;
|
||||
line-height: 30px;
|
||||
text-align: center;
|
||||
transform: skewX(-45deg);
|
||||
|
||||
.react-after {
|
||||
position: absolute;
|
||||
right: -25px;
|
||||
top: 0;
|
||||
height: 24px;
|
||||
width: 50px;
|
||||
background-color: #0f1325;
|
||||
transform: skewX(45deg);
|
||||
}
|
||||
|
||||
.text {
|
||||
display: inline-block;
|
||||
transform: skewX(45deg);
|
||||
}
|
||||
}
|
||||
|
||||
// .react-left {
|
||||
// &.react-l-s {
|
||||
// width: 500px;
|
||||
// text-align: left;
|
||||
// }
|
||||
// font-size: 18px;
|
||||
// width: 100px;
|
||||
// height: 50px;
|
||||
// line-height: 50px;
|
||||
// text-align: center;
|
||||
// transform: skewX(45deg);
|
||||
// background-color: #0f1325;
|
||||
|
||||
// .react-before {
|
||||
// position: absolute;
|
||||
// left: -25px;
|
||||
// top: 0;
|
||||
// height: 50px;
|
||||
// width: 50px;
|
||||
// background-color: #0f1325;
|
||||
// transform: skewX(-45deg);
|
||||
// }
|
||||
|
||||
// .text {
|
||||
// display: inline-block;
|
||||
// transform: skewX(-45deg);
|
||||
// }
|
||||
// }
|
||||
|
||||
.body-box {
|
||||
margin-top: 10px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
//下方区域的布局
|
||||
.content-box {
|
||||
display: grid;
|
||||
gap: 5px;
|
||||
grid-template-columns: 1fr 2fr 1fr;
|
||||
}
|
||||
|
||||
// 底部数据
|
||||
.bototm-box {
|
||||
margin-top: 5px;
|
||||
display: grid;
|
||||
gap: 5px;
|
||||
grid-template-columns: 1fr 2fr 1fr;
|
||||
}
|
||||
}
|
||||
}
|
||||
.dv-border-box-13 {
|
||||
padding: 20px 10px 0 10px;
|
||||
}
|
||||
.writing {
|
||||
text-decoration: underline;
|
||||
color: #daa520;
|
||||
cursor: pointer;
|
||||
margin-right: 20px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.titleBox {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
// height: 30px;
|
||||
|
||||
line-height: 32px;
|
||||
font-size: 18px;
|
||||
padding-left: 20px;
|
||||
margin: 0 auto;
|
||||
background-image: url("@/assets/img/title.png");
|
||||
background-size: cover;
|
||||
background-position: center center;
|
||||
}
|
||||
}
|
||||
.el-form-item {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
/* 呼吸闪烁的容器样式 红色*/
|
||||
.bg-red {
|
||||
/* 设置初始的边框样式 */
|
||||
// border: 1px solid rgba(255, 0, 0, 0.3);
|
||||
/* 添加呼吸动画 */
|
||||
animation: breathing_red 2s ease-in-out infinite;
|
||||
}
|
||||
|
||||
/* 定义呼吸动画 */
|
||||
@keyframes breathing_red {
|
||||
0% {
|
||||
/* 开始时边框颜色较淡 */
|
||||
border-color: rgba(255, 0, 0, 0.3);
|
||||
box-shadow: inset 0 0 50px rgba(255, 0, 0, 0.2);
|
||||
}
|
||||
50% {
|
||||
/* 中间时边框颜色最深 */
|
||||
border-color: rgba(255, 0, 0, 1);
|
||||
box-shadow: inset 0 0 100px rgba(255, 0, 0, 0.8);
|
||||
}
|
||||
100% {
|
||||
/* 结束时边框颜色回到较淡状态 */
|
||||
border-color: rgba(255, 0, 0, 0.3);
|
||||
box-shadow: inset 0 0 50px rgba(255, 0, 0, 0.2);
|
||||
}
|
||||
}
|
||||
|
||||
/* 呼吸闪烁的容器样式 */
|
||||
.bg-yellow {
|
||||
/* 设置初始的边框样式 */
|
||||
// border: 1px solid rgba(255, 219, 0, 0.3);
|
||||
/* 添加呼吸动画 */
|
||||
animation: breathing_yellow 2s ease-in-out infinite;
|
||||
}
|
||||
|
||||
/* 定义呼吸动画 */
|
||||
@keyframes breathing_yellow {
|
||||
0% {
|
||||
/* 开始时边框颜色较淡 */
|
||||
border-color: rgba(255, 219, 0, 0.3);
|
||||
box-shadow: inset 0 0 50px rgba(255, 219, 0, 0.2);
|
||||
}
|
||||
50% {
|
||||
/* 中间时边框颜色最深 */
|
||||
border-color: rgba(255, 219, 0, 1);
|
||||
box-shadow: inset 0 0 100px rgba(255, 219, 0, 0.8);
|
||||
}
|
||||
100% {
|
||||
/* 结束时边框颜色回到较淡状态 */
|
||||
border-color: rgba(255, 219, 0, 0.3);
|
||||
box-shadow: inset 0 0 50px rgba(255, 219, 0, 0.2);
|
||||
}
|
||||
}
|
||||
|
||||
.scroll-box {
|
||||
width: 1820px;
|
||||
height: 40px;
|
||||
padding: 10px 15px;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
left: 50px;
|
||||
}
|
||||
|
||||
.scroll-content {
|
||||
position: absolute;
|
||||
white-space: nowrap;
|
||||
animation: scroll 0s linear infinite;
|
||||
/* 根据内容长度调整动画时间 */
|
||||
|
||||
padding-left: 100%;
|
||||
/* 初始位置偏移 */
|
||||
}
|
||||
|
||||
@keyframes scroll {
|
||||
0% {
|
||||
transform: translateX(0);
|
||||
}
|
||||
100% {
|
||||
transform: translateX(-100%);
|
||||
}
|
||||
}
|
||||
|
||||
/* 闪烁动画定义 */
|
||||
@keyframes flash {
|
||||
0%,
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
50% {
|
||||
opacity: 0.3; /* 半透明效果实现闪烁 */
|
||||
}
|
||||
}
|
||||
|
||||
.animate-flash-red {
|
||||
animation: flash 1s infinite; /* 1秒周期,无限循环 */
|
||||
color: #ff2501;
|
||||
cursor: pointer;
|
||||
}
|
||||
.animate-flash-yellow {
|
||||
animation: flash 1s infinite; /* 1秒周期,无限循环 */
|
||||
color: #bb7b00;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* 悬停暂停效果 */
|
||||
.scroll-box:hover .scroll-content {
|
||||
animation-play-state: paused;
|
||||
}
|
||||
|
||||
:deep(.el-divider--horizontal) {
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
@keyframes step-scale {
|
||||
0% {
|
||||
transform: scale(1.2);
|
||||
}
|
||||
50% {
|
||||
transform: scale(1.2);
|
||||
} /* 突变时放大 */
|
||||
100% {
|
||||
transform: scale(1.2);
|
||||
} /* 稳定后保持放大 */
|
||||
}
|
||||
.step-scale {
|
||||
animation: step-scale 1s infinite; /* forwards保持最终状态 */
|
||||
text-align: center;
|
||||
color: #ff0000; /* 警告色 */
|
||||
}
|
||||
|
||||
div {
|
||||
&::-webkit-scrollbar {
|
||||
width: 6px; /* 滚动条宽度 */
|
||||
}
|
||||
|
||||
/* 滚动条轨道 */
|
||||
// &::-webkit-scrollbar-track {
|
||||
// background: #ccc; /* 轨道背景色 */
|
||||
// border-radius: 10px; /* 轨道圆角 */
|
||||
// }
|
||||
|
||||
// /* 滚动条滑块 */
|
||||
&::-webkit-scrollbar-thumb {
|
||||
// background: #1d83ce; /* 滑块颜色 */
|
||||
border-radius: 10px; /* 滑块圆角 */
|
||||
background: rgba(204, 204, 204, 0.8); /* 设置滚动条轨道背景色 */
|
||||
cursor: pointer;
|
||||
transition: var(--el-transition-duration) background-color;
|
||||
}
|
||||
|
||||
/* 滚动条滑块hover状态 */
|
||||
&::-webkit-scrollbar-thumb:hover {
|
||||
// background: #1d83ce; /* hover时的滑块颜色 */
|
||||
background: rgba(204, 204, 204, 0.5); /* 设置滚动条轨道背景色 */
|
||||
}
|
||||
|
||||
.el-divider--horizontal {
|
||||
border-top: 1px var(--el-color-primary) var(--el-border-style);
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.el-input-group__append, .el-input-group__prepend) {
|
||||
background-color: #ffffff00;
|
||||
}
|
||||
:deep(.frontBox) {
|
||||
background-color: var(--el-color-primary) !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
::v-deep(.el-tabs__item) {
|
||||
color: #fff;
|
||||
}
|
||||
::v-deep(.el-tabs__item.is-active) {
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
::v-deep(.el-tabs__nav-wrap::after) {
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
::v-deep(.el-tabs__active-bar) {
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
253
src/assets/scss/style.scss
Normal file
@@ -0,0 +1,253 @@
|
||||
@import "./variables";
|
||||
|
||||
// 全局样式
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style-type: none;
|
||||
box-sizing: border-box;
|
||||
outline: none;
|
||||
--el-color-primary: #0a73ff;
|
||||
--el-color-success: #2e8b57;
|
||||
--el-color-warning: #daa520;
|
||||
--el-color-danger: #a52a2a;
|
||||
--el-scrollbar-bg-color: #ccc;
|
||||
--el-scrollbar-opacity: 0.7;
|
||||
// --el-fill-color-blank: #ffffff00;
|
||||
}
|
||||
|
||||
html {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
line-height: 1.2em;
|
||||
background-color: #f1f1f1;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
|
||||
/* 滚动条整体 */
|
||||
::-webkit-scrollbar {
|
||||
width: 8px; /* 垂直滚动条宽度 */
|
||||
height: 8px; /* 水平滚动条高度 */
|
||||
}
|
||||
|
||||
/* 滚动条滑块 */
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: var(--el-color-primary); /* 滑块颜色 */
|
||||
border-radius: 4px; /* 滑块圆角 */
|
||||
transition: background 0.3s; /* 滑块 hover 过渡效果 */
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
color: #343440;
|
||||
text-decoration: none;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.clearfix {
|
||||
&::after {
|
||||
content: "";
|
||||
display: table;
|
||||
height: 0;
|
||||
line-height: 0;
|
||||
visibility: hidden;
|
||||
clear: both;
|
||||
}
|
||||
}
|
||||
|
||||
// 图标
|
||||
.iconfont {
|
||||
font-size: 20px !important;
|
||||
color: #5cd9e8;
|
||||
}
|
||||
|
||||
//浮动
|
||||
.float-r {
|
||||
float: right;
|
||||
}
|
||||
|
||||
//浮动
|
||||
.float-l {
|
||||
float: left;
|
||||
}
|
||||
|
||||
// 字体加粗
|
||||
.fw-b {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
//文章一行显示,多余省略号显示
|
||||
.title-item {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.bg-color-black {
|
||||
background-color: rgba(19, 25, 47, 0.6);
|
||||
}
|
||||
|
||||
.bg-color-blue {
|
||||
background-color: #1a5cd7;
|
||||
}
|
||||
|
||||
.colorBlack {
|
||||
color: #272727 !important;
|
||||
|
||||
&:hover {
|
||||
color: #272727 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.colorGrass {
|
||||
color: #33cea0;
|
||||
|
||||
&:hover {
|
||||
color: #33cea0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.colorRed {
|
||||
color: #ff5722;
|
||||
|
||||
&:hover {
|
||||
color: #ff5722 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.colorText {
|
||||
color: #d3d6dd !important;
|
||||
|
||||
&:hover {
|
||||
color: #d3d6dd !important;
|
||||
}
|
||||
}
|
||||
|
||||
.colorBlue {
|
||||
color: #257dff !important;
|
||||
|
||||
&:hover {
|
||||
color: #257dff !important;
|
||||
}
|
||||
}
|
||||
|
||||
//颜色
|
||||
@each $colorkey, $color in $colors {
|
||||
.text-#{$colorkey} {
|
||||
color: $color;
|
||||
}
|
||||
|
||||
.bg-#{$colorkey} {
|
||||
background-color: $color;
|
||||
}
|
||||
}
|
||||
|
||||
//对齐
|
||||
@each $var in (left, center, right) {
|
||||
.text-#{$var} {
|
||||
text-align: $var !important;
|
||||
}
|
||||
}
|
||||
|
||||
//flex
|
||||
@each $key, $value in $flex-jc {
|
||||
.jc-#{$key} {
|
||||
justify-content: $value;
|
||||
}
|
||||
}
|
||||
|
||||
@each $key, $value in $flex-ai {
|
||||
.ai-#{$key} {
|
||||
align-items: $value;
|
||||
}
|
||||
}
|
||||
|
||||
//字体
|
||||
@each $fontkey, $fontvalue in $font-sizes {
|
||||
.fs-#{$fontkey} {
|
||||
font-size: $fontvalue * $base-font-size;
|
||||
}
|
||||
}
|
||||
|
||||
//.mt-1 => margin top
|
||||
//spacing
|
||||
|
||||
@each $typekey, $type in $spacing-types {
|
||||
//.m-1
|
||||
@each $sizekey, $size in $spacing-sizes {
|
||||
.#{$typekey}-#{$sizekey} {
|
||||
#{$type}: $size * $spacing-base-size;
|
||||
}
|
||||
}
|
||||
|
||||
//.mx-1
|
||||
@each $sizekey, $size in $spacing-sizes {
|
||||
.#{$typekey}x-#{$sizekey} {
|
||||
#{$type}-left: $size * $spacing-base-size;
|
||||
#{$type}-right: $size * $spacing-base-size;
|
||||
}
|
||||
|
||||
.#{$typekey}y-#{$sizekey} {
|
||||
#{$type}-top: $size * $spacing-base-size;
|
||||
#{$type}-bottom: $size * $spacing-base-size;
|
||||
}
|
||||
}
|
||||
|
||||
//.mt-1
|
||||
@each $directionkey, $direction in $spacing-directions {
|
||||
@each $sizekey, $size in $spacing-sizes {
|
||||
.#{$typekey}#{$directionkey}-#{$sizekey} {
|
||||
#{$type}-#{$direction}: $size * $spacing-base-size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.#{$typekey} {
|
||||
#{$type}: 0;
|
||||
}
|
||||
@for $i from 0 through 100 {
|
||||
.md#{$i} {
|
||||
margin: #{$i}px !important;
|
||||
}
|
||||
.mt#{$i} {
|
||||
margin-top: #{$i}px !important;
|
||||
}
|
||||
|
||||
.mr#{$i} {
|
||||
margin-right: #{$i}px !important;
|
||||
}
|
||||
|
||||
.mb#{$i} {
|
||||
margin-bottom: #{$i}px !important;
|
||||
}
|
||||
|
||||
.ml#{$i} {
|
||||
margin-left: #{$i}px !important;
|
||||
}
|
||||
|
||||
.pd#{$i} {
|
||||
padding: #{$i}px !important;
|
||||
}
|
||||
.pt#{$i} {
|
||||
padding-top: #{$i}px !important;
|
||||
}
|
||||
|
||||
.pr#{$i} {
|
||||
padding-right: #{$i}px !important;
|
||||
}
|
||||
|
||||
.pb#{$i} {
|
||||
padding-bottom: #{$i}px !important;
|
||||
}
|
||||
|
||||
.pl#{$i} {
|
||||
padding-left: #{$i}px !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BIN
src/assets/titleBG.png
Normal file
|
After Width: | Height: | Size: 1024 KiB |
BIN
src/assets/txycyzj.gif
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
1
src/assets/vue.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="37.07" height="36" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 198"><path fill="#41B883" d="M204.8 0H256L128 220.8L0 0h97.92L128 51.2L157.44 0h47.36Z"></path><path fill="#41B883" d="m0 0l128 220.8L256 0h-51.2L128 132.48L50.56 0H0Z"></path><path fill="#35495E" d="M50.56 0L128 133.12L204.8 0h-47.36L128 51.2L97.92 0H50.56Z"></path></svg>
|
||||
|
After Width: | Height: | Size: 496 B |
311
src/components/BX/rmsWorker.js
Normal file
@@ -0,0 +1,311 @@
|
||||
// 辅助函数
|
||||
const getMax = (temp, tempA, tempB, tempC) => {
|
||||
temp = temp > tempA ? temp : tempA;
|
||||
temp = temp > tempB ? temp : tempB;
|
||||
if (tempC !== undefined) {
|
||||
temp = temp > tempC ? temp : tempC;
|
||||
}
|
||||
return temp;
|
||||
};
|
||||
|
||||
const getMaxTwo = (temp, tempA, tempB) => {
|
||||
temp = temp > tempA ? temp : tempA;
|
||||
temp = temp > tempB ? temp : tempB;
|
||||
return temp;
|
||||
};
|
||||
|
||||
const getMin = (temp, tempA, tempB, tempC) => {
|
||||
temp = temp < tempA ? temp : tempA;
|
||||
temp = temp < tempB ? temp : tempB;
|
||||
if (tempC !== undefined) {
|
||||
temp = temp < tempC ? temp : tempC;
|
||||
}
|
||||
return temp;
|
||||
};
|
||||
|
||||
const getMinOpen = (temp, tempA, tempB) => {
|
||||
temp = temp < tempA ? temp : tempA;
|
||||
temp = temp < tempB ? temp : tempB;
|
||||
return temp;
|
||||
};
|
||||
|
||||
// 数据处理函数
|
||||
const fliteWaveData = (wp, step, iphasicValue, isOpen) => {
|
||||
const rmsData = wp.listRmsData;
|
||||
const pt = Number(wp.pt) / 1000;
|
||||
const ct = Number(wp.ct);
|
||||
const titleList = wp.waveTitle;
|
||||
let xishu = pt;
|
||||
let aTitle = "",
|
||||
bTitle = "",
|
||||
cTitle = "",
|
||||
unit = "电压";
|
||||
let rmsvFirstX = 0,
|
||||
rmsvFirstY = 0,
|
||||
rmsvSecondX = 0,
|
||||
rmsvSecondY = 0,
|
||||
firstZhou = "a",
|
||||
secondeZhou = "a";
|
||||
let ifmax = 0,
|
||||
ifmin = 0,
|
||||
ismax = 0,
|
||||
ismin = 0,
|
||||
rfmax = 0,
|
||||
rfmin = 0,
|
||||
rsmax = 0,
|
||||
rsmin = 0;
|
||||
|
||||
const shunshiFA = [];
|
||||
const shunshiFB = [];
|
||||
const shunshiFC = [];
|
||||
const shunshiSA = [];
|
||||
const shunshiSB = [];
|
||||
const shunshiSC = [];
|
||||
const rmsFA = [];
|
||||
const rmsFB = [];
|
||||
const rmsFC = [];
|
||||
const rmsSA = [];
|
||||
const rmsSB = [];
|
||||
const rmsSC = [];
|
||||
|
||||
|
||||
|
||||
if (titleList[iphasicValue * step + 1]?.substring(0, 1) !== "U") {
|
||||
xishu = ct;
|
||||
unit = "电流";
|
||||
}
|
||||
|
||||
for (let i = 1; i <= iphasicValue; i++) {
|
||||
switch (i) {
|
||||
case 1:
|
||||
aTitle = titleList[iphasicValue * step + i]?.substring(1) || "";
|
||||
break;
|
||||
case 2:
|
||||
bTitle = titleList[iphasicValue * step + i]?.substring(1) || "";
|
||||
break;
|
||||
case 3:
|
||||
cTitle = titleList[iphasicValue * step + i]?.substring(1) || "";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (rmsData[0] && rmsData[0][iphasicValue * step + 1] !== undefined) {
|
||||
rfmax = rmsData[0][iphasicValue * step + 1] * xishu;
|
||||
rfmin = rmsData[0][iphasicValue * step + 1] * xishu;
|
||||
rmsvFirstY = rmsData[0][iphasicValue * step + 1] * xishu;
|
||||
rmsvFirstX = rmsData[0][0];
|
||||
rsmax = rmsData[0][iphasicValue * step + 1];
|
||||
rsmin = rmsData[0][iphasicValue * step + 1];
|
||||
rmsvSecondY = rmsData[0][iphasicValue * step + 1];
|
||||
rmsvSecondX = rmsData[0][0];
|
||||
}
|
||||
|
||||
for (let rms = 0; rms < rmsData.length; rms++) {
|
||||
if (!rmsData[rms] || rmsData[rms][iphasicValue * step + 1] === undefined) {
|
||||
break;
|
||||
}
|
||||
|
||||
switch (iphasicValue) {
|
||||
case 1:
|
||||
const rmsFirstA = rmsData[rms][iphasicValue * step + 1] * xishu;
|
||||
rmsFA.push([rmsData[rms][0], rmsFirstA]);
|
||||
rfmax = rfmax > rmsFirstA ? rfmax : rmsFirstA;
|
||||
rfmin = rfmin < rmsFirstA ? rfmin : rmsFirstA;
|
||||
if (rfmin < rmsvFirstY) {
|
||||
rmsvFirstY = rfmin;
|
||||
firstZhou = "a";
|
||||
rmsvFirstX = rmsData[rms][0];
|
||||
}
|
||||
|
||||
const rmsSecondA = rmsData[rms][iphasicValue * step + 1];
|
||||
rmsSA.push([rmsData[rms][0], rmsSecondA]);
|
||||
rsmax = rsmax > rmsSecondA ? rsmax : rmsSecondA;
|
||||
rsmin = rsmin < rmsSecondA ? rsmin : rmsSecondA;
|
||||
if (rsmin < rmsvSecondY) {
|
||||
rmsvSecondY = rsmin;
|
||||
secondeZhou = "a";
|
||||
rmsvSecondX = rmsData[rms][0];
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
const rmsFirstA2 = rmsData[rms][iphasicValue * step + 1] * xishu;
|
||||
const rmsFirstB2 = rmsData[rms][iphasicValue * step + 2] * xishu;
|
||||
rmsFA.push([rmsData[rms][0], rmsFirstA2]);
|
||||
rmsFB.push([rmsData[rms][0], rmsFirstB2]);
|
||||
rfmax = getMaxTwo(rfmax, rmsFirstA2, rmsFirstB2);
|
||||
rfmin = getMinOpen(rfmin, rmsFirstA2, rmsFirstB2);
|
||||
if (rfmin < rmsvFirstY) {
|
||||
rmsvFirstY = rfmin;
|
||||
if (rfmin === rmsFirstA2) {
|
||||
firstZhou = "a";
|
||||
} else if (rfmin === rmsFirstB2) {
|
||||
firstZhou = "b";
|
||||
}
|
||||
rmsvFirstX = rmsData[rms][0];
|
||||
}
|
||||
|
||||
const rmsSecondA2 = rmsData[rms][iphasicValue * step + 1];
|
||||
const rmsSecondB2 = rmsData[rms][iphasicValue * step + 2];
|
||||
rmsSA.push([rmsData[rms][0], rmsSecondA2]);
|
||||
rmsSB.push([rmsData[rms][0], rmsSecondB2]);
|
||||
rsmax = getMaxTwo(rsmax, rmsSecondA2, rmsSecondB2);
|
||||
rsmin = getMinOpen(rsmin, rmsSecondA2, rmsSecondB2);
|
||||
if (rsmin < rmsvSecondY) {
|
||||
rmsvSecondY = rsmin;
|
||||
if (rsmin === rmsSecondA2) {
|
||||
secondeZhou = "a";
|
||||
} else if (rsmin === rmsSecondB2) {
|
||||
secondeZhou = "b";
|
||||
}
|
||||
rmsvSecondX = rmsData[rms][0];
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
const rmsFirstA3 = rmsData[rms][iphasicValue * step + 1] * xishu;
|
||||
const rmsFirstB3 = rmsData[rms][iphasicValue * step + 2] * xishu;
|
||||
const rmsFirstC3 = rmsData[rms][iphasicValue * step + 3] * xishu;
|
||||
rmsFA.push([rmsData[rms][0], rmsFirstA3]);
|
||||
rmsFB.push([rmsData[rms][0], rmsFirstB3]);
|
||||
rmsFC.push([rmsData[rms][0], rmsFirstC3]);
|
||||
rfmax = getMax(rfmax, rmsFirstA3, rmsFirstB3, rmsFirstC3);
|
||||
rfmin = isOpen
|
||||
? getMinOpen(rfmin, rmsFirstA3, rmsFirstC3)
|
||||
: getMin(rfmin, rmsFirstA3, rmsFirstB3, rmsFirstC3);
|
||||
if (rfmin < rmsvFirstY) {
|
||||
rmsvFirstY = rfmin;
|
||||
if (rfmin === rmsFirstA3) {
|
||||
firstZhou = "a";
|
||||
} else if (rfmin === rmsFirstB3) {
|
||||
firstZhou = "b";
|
||||
} else {
|
||||
firstZhou = "c";
|
||||
}
|
||||
rmsvFirstX = rmsData[rms][0];
|
||||
}
|
||||
|
||||
const rmsSecondA3 = rmsData[rms][iphasicValue * step + 1];
|
||||
const rmsSecondB3 = rmsData[rms][iphasicValue * step + 2];
|
||||
const rmsSecondC3 = rmsData[rms][iphasicValue * step + 3];
|
||||
rmsSA.push([rmsData[rms][0], rmsSecondA3]);
|
||||
rmsSB.push([rmsData[rms][0], rmsSecondB3]);
|
||||
rmsSC.push([rmsData[rms][0], rmsSecondC3]);
|
||||
rsmax = getMax(rsmax, rmsSecondA3, rmsSecondB3, rmsSecondC3);
|
||||
rsmin = isOpen
|
||||
? getMinOpen(rsmin, rmsSecondA3, rmsSecondC3)
|
||||
: getMin(rsmin, rmsSecondA3, rmsSecondB3, rmsSecondC3);
|
||||
if (rsmin < rmsvSecondY) {
|
||||
rmsvSecondY = rsmin;
|
||||
if (rsmin === rmsSecondA3) {
|
||||
secondeZhou = "a";
|
||||
} else if (rsmin === rmsSecondB3) {
|
||||
secondeZhou = "b";
|
||||
} else {
|
||||
secondeZhou = "c";
|
||||
}
|
||||
rmsvSecondX = rmsData[rms][0];
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const instantF = { max: ifmax, min: ifmin };
|
||||
const instantS = { max: ismax, min: ismin };
|
||||
const RMSF = { max: rfmax, min: rfmin };
|
||||
const RMSS = { max: rsmax, min: rsmin };
|
||||
const RMSFMinDetail = { rmsvFirstX, rmsvFirstY, firstZhou };
|
||||
const RMSSMinDetail = { rmsvSecondX, rmsvSecondY, secondeZhou };
|
||||
const shunshiF = { shunshiFA, shunshiFB, shunshiFC };
|
||||
const shunshiS = { shunshiSA, shunshiSB, shunshiSC };
|
||||
const RMSFWave = { rmsFA, rmsFB, rmsFC };
|
||||
const RMSSWave = { rmsSA, rmsSB, rmsSC };
|
||||
const title = { aTitle, bTitle, cTitle, unit };
|
||||
|
||||
|
||||
return {
|
||||
|
||||
instantF,
|
||||
instantS,
|
||||
RMSF,
|
||||
RMSS,
|
||||
RMSFMinDetail,
|
||||
RMSSMinDetail,
|
||||
shunshiF,
|
||||
shunshiS,
|
||||
RMSFWave,
|
||||
RMSSWave,
|
||||
title,
|
||||
unit,
|
||||
};
|
||||
};
|
||||
|
||||
// 监听消息
|
||||
self.onmessage = function (e) {
|
||||
const { wp, isOpen, value, boxoList } = JSON.parse(e.data);
|
||||
|
||||
try {
|
||||
const iphasicValue = wp.iphasic || 1;
|
||||
|
||||
const picCounts = (wp.waveTitle.length - 1) / iphasicValue;
|
||||
const waveDatas = [];
|
||||
|
||||
for (let i = 0; i < picCounts; i++) {
|
||||
const data = fliteWaveData(wp, i, iphasicValue, isOpen,boxoList);
|
||||
waveDatas.push(data);
|
||||
}
|
||||
// 处理标题
|
||||
let titles = "";
|
||||
if (boxoList.systemType == "pms") {
|
||||
titles =
|
||||
"变电站名称:" +
|
||||
boxoList.powerStationName +
|
||||
" 监测点名称:" +
|
||||
boxoList.measurementPointName +
|
||||
" 发生时刻:" +
|
||||
boxoList.startTime +
|
||||
" 残余电压:" +
|
||||
(boxoList.featureAmplitude * 100).toFixed(2) +
|
||||
"% 持续时间:" +
|
||||
boxoList.duration +
|
||||
"s";
|
||||
} else if (boxoList.systemType == "ZL") {
|
||||
titles =
|
||||
" 监测点名称:" +
|
||||
boxoList.equipmentName +
|
||||
" 发生时刻:" +
|
||||
boxoList.startTime +
|
||||
" 残余电压:" +
|
||||
boxoList.evtParamVVaDepth +
|
||||
" 持续时间:" +
|
||||
boxoList.evtParamTm +
|
||||
"s";
|
||||
} else {
|
||||
titles =
|
||||
"变电站名称:" +
|
||||
boxoList.subName +
|
||||
" 监测点名称:" +
|
||||
boxoList.lineName +
|
||||
" 发生时刻:" +
|
||||
boxoList.startTime +
|
||||
" 残余电压:" +
|
||||
(boxoList.featureAmplitude * 100).toFixed(2) +
|
||||
"% 持续时间:" +
|
||||
boxoList.duration +
|
||||
"s";
|
||||
}
|
||||
// 发送处理结果回主线程
|
||||
self.postMessage({
|
||||
titles: titles,
|
||||
success: true,
|
||||
waveDatas,
|
||||
time: wp.time,
|
||||
type: wp.waveType,
|
||||
severity: wp.yzd,
|
||||
iphasic: iphasicValue,
|
||||
});
|
||||
} catch (error) {
|
||||
self.postMessage({
|
||||
success: false,
|
||||
error: error.message,
|
||||
});
|
||||
}
|
||||
};
|
||||
1133
src/components/BX/rmsboxi.vue
Normal file
1608
src/components/BX/rmsboxi1.vue
Normal file
1136
src/components/BX/rmsboxi2.vue
Normal file
179
src/components/BX/shuWorker.js
Normal file
@@ -0,0 +1,179 @@
|
||||
// waveData.worker.js
|
||||
self.addEventListener('message', function(e) {
|
||||
const { wp, value, iphasic, isOpen, boxoList } = JSON.parse(e.data);
|
||||
|
||||
// 处理波形数据的函数
|
||||
const fliteWaveData = (wp, step) => {
|
||||
// 将原有的fliteWaveData函数实现复制到这里
|
||||
const shunData = wp.listWaveData;
|
||||
const pt = Number(wp.pt) / 1000;
|
||||
const ct = Number(wp.ct);
|
||||
const titleList = wp.waveTitle;
|
||||
let xishu = pt;
|
||||
let aTitle = '', bTitle = '', cTitle = '', unit = '电压';
|
||||
let ifmax = 0, ifmin = 0, ismax = 0, ismin = 0;
|
||||
|
||||
const shunshiFA = [];
|
||||
const shunshiFB = [];
|
||||
const shunshiFC = [];
|
||||
const shunshiSA = [];
|
||||
const shunshiSB = [];
|
||||
const shunshiSC = [];
|
||||
|
||||
if (shunData.length > 0) {
|
||||
if (titleList[iphasic * step + 1]?.substring(0, 1) !== 'U') {
|
||||
xishu = ct;
|
||||
unit = '电流';
|
||||
}
|
||||
|
||||
for (let i = 1; i <= iphasic; i++) {
|
||||
switch (i) {
|
||||
case 1:
|
||||
aTitle = titleList[iphasic * step + i]?.substring(1) || '';
|
||||
break;
|
||||
case 2:
|
||||
bTitle = titleList[iphasic * step + i]?.substring(1) || '';
|
||||
break;
|
||||
case 3:
|
||||
cTitle = titleList[iphasic * step + i]?.substring(1) || '';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (shunData[0][iphasic * step + 1] !== undefined) {
|
||||
ifmax = shunData[0][iphasic * step + 1] * xishu;
|
||||
ifmin = shunData[0][iphasic * step + 1] * xishu;
|
||||
ismax = shunData[0][iphasic * step + 1];
|
||||
ismin = shunData[0][iphasic * step + 1];
|
||||
}
|
||||
|
||||
for (let shun = 0; shun < shunData.length; shun++) {
|
||||
if (shunData[shun][iphasic * step + 1] === undefined) {
|
||||
break;
|
||||
}
|
||||
|
||||
switch (iphasic) {
|
||||
case 1:
|
||||
const shunFirstA = shunData[shun][iphasic * step + 1] * xishu;
|
||||
shunshiFA.push([shunData[shun][0], shunFirstA]);
|
||||
ifmax = Math.max(ifmax, shunFirstA);
|
||||
ifmin = Math.min(ifmin, shunFirstA);
|
||||
|
||||
const shunSecondA = shunData[shun][iphasic * step + 1];
|
||||
shunshiSA.push([shunData[shun][0], shunSecondA]);
|
||||
ismax = Math.max(ismax, shunSecondA);
|
||||
ismin = Math.min(ismin, shunSecondA);
|
||||
break;
|
||||
case 2:
|
||||
const shunFirstA2 = shunData[shun][iphasic * step + 1] * xishu;
|
||||
const shunFirstB2 = shunData[shun][iphasic * step + 2] * xishu;
|
||||
shunshiFA.push([shunData[shun][0], shunFirstA2]);
|
||||
shunshiFB.push([shunData[shun][0], shunFirstB2]);
|
||||
ifmax = Math.max(ifmax, shunFirstA2, shunFirstB2);
|
||||
ifmin = Math.min(ifmin, shunFirstA2, shunFirstB2);
|
||||
|
||||
const shunSecondA2 = shunData[shun][iphasic * step + 1];
|
||||
const shunSecondB2 = shunData[shun][iphasic * step + 2];
|
||||
shunshiSA.push([shunData[shun][0], shunSecondA2]);
|
||||
shunshiSB.push([shunData[shun][0], shunSecondB2]);
|
||||
ismax = Math.max(ismax, shunSecondA2, shunSecondB2);
|
||||
ismin = Math.min(ismin, shunSecondA2, shunSecondB2);
|
||||
break;
|
||||
case 3:
|
||||
const shunFirstA3 = shunData[shun][iphasic * step + 1] * xishu;
|
||||
const shunFirstB3 = shunData[shun][iphasic * step + 2] * xishu;
|
||||
const shunFirstC3 = shunData[shun][iphasic * step + 3] * xishu;
|
||||
shunshiFA.push([shunData[shun][0], shunFirstA3]);
|
||||
shunshiFB.push([shunData[shun][0], shunFirstB3]);
|
||||
shunshiFC.push([shunData[shun][0], shunFirstC3]);
|
||||
ifmax = Math.max(ifmax, shunFirstA3, shunFirstB3, shunFirstC3);
|
||||
ifmin = isOpen ? Math.min(ifmin, shunFirstA3, shunFirstC3) : Math.min(ifmin, shunFirstA3, shunFirstB3, shunFirstC3);
|
||||
|
||||
const shunSecondA3 = shunData[shun][iphasic * step + 1];
|
||||
const shunSecondB3 = shunData[shun][iphasic * step + 2];
|
||||
const shunSecondC3 = shunData[shun][iphasic * step + 3];
|
||||
shunshiSA.push([shunData[shun][0], shunSecondA3]);
|
||||
shunshiSB.push([shunData[shun][0], shunSecondB3]);
|
||||
shunshiSC.push([shunData[shun][0], shunSecondC3]);
|
||||
ismax = Math.max(ismax, shunSecondA3, shunSecondB3, shunSecondC3);
|
||||
ismin = isOpen ? Math.min(ismin, shunSecondA3, shunSecondC3) : Math.min(ismin, shunSecondA3, shunSecondB3, shunSecondC3);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const instantF = { max: ifmax, min: ifmin };
|
||||
const instantS = { max: ismax, min: ismin };
|
||||
const shunshiF = { shunshiFA, shunshiFB, shunshiFC };
|
||||
const shunshiS = { shunshiSA, shunshiSB, shunshiSC };
|
||||
const title = { aTitle, bTitle, cTitle, unit };
|
||||
|
||||
return { instantF, instantS, shunshiF, shunshiS, title, unit };
|
||||
};
|
||||
|
||||
// 处理标题
|
||||
let titles = '';
|
||||
if (boxoList.systemType == 'pms') {
|
||||
titles = '变电站名称:' +
|
||||
boxoList.powerStationName +
|
||||
' 监测点名称:' +
|
||||
boxoList.measurementPointName +
|
||||
' 发生时刻:' +
|
||||
boxoList.startTime +
|
||||
' 残余电压:' +
|
||||
(boxoList.featureAmplitude * 100).toFixed(2) +
|
||||
'% 持续时间:' +
|
||||
boxoList.duration +
|
||||
's';
|
||||
} else if (boxoList.systemType == 'ZL') {
|
||||
titles = ' 监测点名称:' +
|
||||
boxoList.equipmentName +
|
||||
' 发生时刻:' +
|
||||
boxoList.startTime +
|
||||
' 残余电压:' +
|
||||
boxoList.evtParamVVaDepth +
|
||||
' 持续时间:' +
|
||||
boxoList.evtParamTm +
|
||||
's';
|
||||
} else {
|
||||
titles = '变电站名称:' +
|
||||
boxoList.subName +
|
||||
' 监测点名称:' +
|
||||
boxoList.lineName +
|
||||
' 发生时刻:' +
|
||||
boxoList.startTime +
|
||||
' 残余电压:' +
|
||||
(boxoList.featureAmplitude * 100).toFixed(2) +
|
||||
'% 持续时间:' +
|
||||
boxoList.duration +
|
||||
's';
|
||||
}
|
||||
|
||||
const iphasicValue = wp.iphasic || 1;
|
||||
const picCounts = (wp.waveTitle.length - 1) / iphasicValue;
|
||||
const waveDatas = [];
|
||||
|
||||
for (let i = 0; i < picCounts; i++) {
|
||||
const data = fliteWaveData(wp, i);
|
||||
waveDatas.push(data);
|
||||
}
|
||||
|
||||
const time = wp.time;
|
||||
const type = wp.waveType;
|
||||
let severity = wp.yzd;
|
||||
|
||||
if (severity < 0) {
|
||||
severity = '/';
|
||||
type = '/';
|
||||
}
|
||||
|
||||
// 将处理结果发送回主线程
|
||||
self.postMessage({
|
||||
waveDatas,
|
||||
time,
|
||||
type,
|
||||
severity,
|
||||
titles,
|
||||
iphasic: iphasicValue
|
||||
});
|
||||
});
|
||||
863
src/components/BX/shushiboxi.vue
Normal file
@@ -0,0 +1,863 @@
|
||||
<template>
|
||||
<div v-loading="loading" :element-loading-background="'rgba(122, 122, 122, 0.8)'" class="boxbx"
|
||||
style="position: relative;height: 100%;">
|
||||
<div id="boxsj" style="background: #343849c7">
|
||||
<div id="shushi" :style="`height:${vh};overflow: hidden;`">
|
||||
<div class="bx" id="wave"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted, onBeforeUnmount, watch, nextTick } from 'vue'
|
||||
import html2canvas from 'html2canvas'
|
||||
import $ from 'jquery'
|
||||
import * as echarts from 'echarts'
|
||||
import url from '@/assets/img/point.png'
|
||||
// 创建Worker
|
||||
let waveDataWorker: Worker | null = null;
|
||||
interface WaveData {
|
||||
instantF: { max: number; min: number }
|
||||
instantS: { max: number; min: number }
|
||||
shunshiF: {
|
||||
shunshiFA: number[][]
|
||||
shunshiFB: number[][]
|
||||
shunshiFC: number[][]
|
||||
}
|
||||
shunshiS: {
|
||||
shunshiSA: number[][]
|
||||
shunshiSB: number[][]
|
||||
shunshiSC: number[][]
|
||||
}
|
||||
title: {
|
||||
aTitle: string
|
||||
bTitle: string
|
||||
cTitle: string
|
||||
unit: string
|
||||
}
|
||||
unit: string
|
||||
}
|
||||
|
||||
interface Props {
|
||||
value?: number
|
||||
flag?: string | number | boolean
|
||||
parentHeight?: string | number | boolean
|
||||
DColor?: boolean
|
||||
boxoList?: any
|
||||
wp?: any
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
value: 2,
|
||||
flag: 3,
|
||||
parentHeight: 0,
|
||||
DColor: false,
|
||||
boxoList: () => ({}),
|
||||
wp: () => ({})
|
||||
})
|
||||
|
||||
const loading = ref(true)
|
||||
const tabvalue = ref(props.value)
|
||||
const valA: any = ref(0)
|
||||
const valB: any = ref(0)
|
||||
const isOpen = ref(false)
|
||||
const time = ref('')
|
||||
const type = ref('')
|
||||
const severity: any = ref('')
|
||||
const iphasic: any = ref('')
|
||||
const eventValue = ref('')
|
||||
const persistTime = ref('')
|
||||
const lineName = ref('')
|
||||
const subName = ref('')
|
||||
const waveDatas = ref<WaveData[]>([])
|
||||
const ptpass = ref('')
|
||||
const waveHeight = ref<number>()
|
||||
const rmsHeight = ref<number>()
|
||||
const color = ref('#006565')
|
||||
const charts = ref({})
|
||||
const arrpoints = ref([])
|
||||
const titles = ref('')
|
||||
const zoom = ref(1)
|
||||
const myChartess = ref<echarts.ECharts | null>(null)
|
||||
const myChartess1 = ref<echarts.ECharts | null>(null)
|
||||
const myChartess2 = ref<echarts.ECharts | null>(null)
|
||||
const myChartess3 = ref<echarts.ECharts | null>(null)
|
||||
const myChartess4 = ref<echarts.ECharts | null>(null)
|
||||
const myChartess5 = ref<echarts.ECharts | null>(null)
|
||||
|
||||
const vh = computed(() => {
|
||||
if (props.flag == 1) {
|
||||
return '690px'
|
||||
} else if (props.parentHeight != 0) {
|
||||
return '310px'
|
||||
} else {
|
||||
return '350px'
|
||||
}
|
||||
})
|
||||
|
||||
const vw = computed(() => '100%')
|
||||
|
||||
watch(() => props.value, (newVal) => {
|
||||
if (newVal == 2) {
|
||||
initWaves()
|
||||
} else {
|
||||
$('#wave1').remove()
|
||||
initWaves()
|
||||
}
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
const zoomValue = document.body.style.getPropertyValue('zoom');
|
||||
zoom.value = 1 / (zoomValue ? parseFloat(zoomValue) : 1);
|
||||
|
||||
window.addEventListener('resize', handleResize)
|
||||
nextTick(() => {
|
||||
setTimeout(() => {
|
||||
query()
|
||||
}, 500)
|
||||
})
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
console.log('组件卸载');
|
||||
|
||||
if (waveDataWorker) {
|
||||
waveDataWorker.terminate();
|
||||
waveDataWorker = null;
|
||||
}
|
||||
|
||||
backbxlb();
|
||||
window.removeEventListener('resize', handleResize);
|
||||
})
|
||||
|
||||
const handleResize = () => {
|
||||
const zoomValue = document.body.style.getPropertyValue('zoom');
|
||||
zoom.value = 1 / (zoomValue ? parseFloat(zoomValue) : 1);
|
||||
}
|
||||
|
||||
const download = () => {
|
||||
const boxsj = document.getElementById('boxsj')
|
||||
if (boxsj) {
|
||||
html2canvas(boxsj, {
|
||||
scale: 2,
|
||||
}).then(function (canvas) {
|
||||
const creatIMg = document.createElement('a')
|
||||
creatIMg.download = '瞬间波形.png'
|
||||
creatIMg.href = canvas.toDataURL()
|
||||
creatIMg.click()
|
||||
creatIMg.remove()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const query = () => {
|
||||
loading.value = true
|
||||
initWaves()
|
||||
}
|
||||
|
||||
const waveData = (instantF: any, instantS: any, shunshiF: any, shunshiS: any, title: any, unit: any): WaveData => {
|
||||
return {
|
||||
instantF,
|
||||
instantS,
|
||||
shunshiF,
|
||||
shunshiS,
|
||||
title,
|
||||
unit
|
||||
}
|
||||
}
|
||||
|
||||
// 在组件中修改initWaves函数
|
||||
const initWaves = () => {
|
||||
if (props.wp) {
|
||||
loading.value = true;
|
||||
iphasic.value = props.wp.iphasic || 1
|
||||
// 使用Web Worker处理数据
|
||||
if (!waveDataWorker) {
|
||||
waveDataWorker = new Worker(new URL('./shuWorker.js', import.meta.url));
|
||||
|
||||
waveDataWorker.onmessage = function (e) {
|
||||
const data = e.data;
|
||||
|
||||
titles.value = data.titles;
|
||||
iphasic.value = data.iphasic;
|
||||
time.value = data.time;
|
||||
type.value = data.type;
|
||||
severity.value = data.severity;
|
||||
|
||||
initWave(data.waveDatas, data.time, data.type, data.severity, isOpen.value);
|
||||
|
||||
loading.value = false;
|
||||
};
|
||||
|
||||
waveDataWorker.onerror = function (error) {
|
||||
console.error('Worker error:', error);
|
||||
loading.value = false;
|
||||
// 备用方案:在主线程处理数据
|
||||
// processDataInMainThread();
|
||||
};
|
||||
}
|
||||
|
||||
// 发送数据到Worker
|
||||
waveDataWorker.postMessage(JSON.stringify({
|
||||
wp: props.wp,
|
||||
value: props.value,
|
||||
iphasic: iphasic.value,
|
||||
isOpen: isOpen.value,
|
||||
boxoList: props.boxoList
|
||||
}));
|
||||
} else {
|
||||
initWave(null, null, null, null, null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const initWave = (waveDatas: WaveData[] | null, time: string | null, type: string | null, severity: string | null, isOpen: boolean | null) => {
|
||||
$('div.bx1').remove()
|
||||
|
||||
let picHeight = vh.value
|
||||
const show = !isOpen
|
||||
let isvisible = false
|
||||
let cu: number[][] = []
|
||||
let titleText = ''
|
||||
let unit = ''
|
||||
let max: any = 0, min: any = 0
|
||||
let a: string | null = null, b: string | null = null, c: string | null = null
|
||||
let adata: number[][] = [], bdata: number[][] = [], cdata: number[][] = []
|
||||
const colors: string[] = []
|
||||
|
||||
if (!waveDatas) {
|
||||
titleText = '该事件暂无波形图'
|
||||
} else if (waveDatas.length > 0) {
|
||||
titleText = titles.value
|
||||
|
||||
if (Number(eventValue.value) <= 90) {
|
||||
isvisible = true
|
||||
}
|
||||
|
||||
switch (iphasic.value) {
|
||||
case 1:
|
||||
a = waveDatas[0].title.aTitle
|
||||
if (props.value === 1) {
|
||||
cu = [[0, waveDatas[0].instantF.min]]
|
||||
max = waveDatas[0].instantF.max
|
||||
min = waveDatas[0].instantF.min
|
||||
adata = waveDatas[0].shunshiF.shunshiFA
|
||||
} else {
|
||||
cu = [[0, waveDatas[0].instantS.min]]
|
||||
max = waveDatas[0].instantS.max
|
||||
min = waveDatas[0].instantS.min
|
||||
adata = waveDatas[0].shunshiS.shunshiSA
|
||||
}
|
||||
colors.push('#DAA520', '#fff', '#fff')
|
||||
break
|
||||
case 2:
|
||||
a = waveDatas[0].title.aTitle
|
||||
b = waveDatas[0].title.bTitle
|
||||
if (props.value === 1) {
|
||||
cu = [[0, waveDatas[0].instantF.min]]
|
||||
max = waveDatas[0].instantF.max
|
||||
min = waveDatas[0].instantF.min
|
||||
adata = waveDatas[0].shunshiF.shunshiFA
|
||||
bdata = waveDatas[0].shunshiF.shunshiFB
|
||||
} else {
|
||||
cu = [[0, waveDatas[0].instantS.min]]
|
||||
max = waveDatas[0].instantS.max
|
||||
min = waveDatas[0].instantS.min
|
||||
adata = waveDatas[0].shunshiS.shunshiSA
|
||||
bdata = waveDatas[0].shunshiS.shunshiSB
|
||||
}
|
||||
colors.push('#DAA520', '#2E8B57', '#fff')
|
||||
break
|
||||
case 3:
|
||||
a = waveDatas[0].title.aTitle
|
||||
b = waveDatas[0].title.bTitle
|
||||
c = waveDatas[0].title.cTitle
|
||||
if (props.value === 1) {
|
||||
cu = [[0, waveDatas[0].instantF.min]]
|
||||
max = waveDatas[0].instantF.max
|
||||
min = waveDatas[0].instantF.min
|
||||
adata = waveDatas[0].shunshiF.shunshiFA
|
||||
bdata = waveDatas[0].shunshiF.shunshiFB
|
||||
cdata = waveDatas[0].shunshiF.shunshiFC
|
||||
} else {
|
||||
cu = [[0, waveDatas[0].instantS.min]]
|
||||
max = waveDatas[0].instantS.max
|
||||
min = waveDatas[0].instantS.min
|
||||
adata = waveDatas[0].shunshiS.shunshiSA
|
||||
bdata = waveDatas[0].shunshiS.shunshiSB
|
||||
cdata = waveDatas[0].shunshiS.shunshiSC
|
||||
}
|
||||
colors.push('#DAA520', '#2E8B57', '#A52a2a')
|
||||
break
|
||||
}
|
||||
|
||||
if (waveDatas[0].title.unit === '电压') {
|
||||
unit = props.value === 1 ? 'kV' : 'V'
|
||||
} else {
|
||||
unit = 'A'
|
||||
}
|
||||
|
||||
for (let step = waveDatas.length - 1; step > 0 && step < waveDatas.length; step--) {
|
||||
const waveId = 'wave' + step
|
||||
const newDivShunshi = $(`<div style="height:${vh.value};overflow: hidden;">
|
||||
<div class='bx1' id='${waveId}'></div>
|
||||
</div>`)
|
||||
newDivShunshi.insertAfter($('#shushi'))
|
||||
$(`#${waveId}`).css('height', picHeight).css('width', vw.value)
|
||||
}
|
||||
} else {
|
||||
titleText = `变电站名称:${subName.value} 监测点名称:${lineName.value} 发生时刻:${time} 残余电压:${(Number(eventValue.value) * 1).toFixed(0)}% 持续时间:${persistTime.value}s`
|
||||
}
|
||||
|
||||
const wave = document.getElementById('wave')
|
||||
if (!wave) return
|
||||
|
||||
const myChartes = echarts.init(wave)
|
||||
const echartsColor = { WordColor: "#fff", thread: "#fff", FigureColor: ["#07CCCA ", "#00BFF5", "#FFBF00", "#77DA63", "#D5FF6B", "#Ff6600", "#FF9100", "#5B6E96", "#66FFCC", "#B3B3B3", "#FF00FF", "#CC00FF", "#FF9999"] }
|
||||
|
||||
setTimeout(() => {
|
||||
wave.style.width = '100%'
|
||||
wave.style.height = vh.value
|
||||
}, 0)
|
||||
|
||||
const option = {
|
||||
tooltip: {
|
||||
top: '10px',
|
||||
trigger: 'axis',
|
||||
borderColor: 'grey',
|
||||
style: {
|
||||
color: '#fff',
|
||||
fontSize: '15px',
|
||||
padding: 10
|
||||
},
|
||||
formatter: function (params: any) {
|
||||
let tips = '时刻:' + params[0].data[0] + '</br/>'
|
||||
for (let i = 0; i < params.length; i++) {
|
||||
if (params[i].seriesName != '暂降触发点') {
|
||||
tips += params[i].marker + params[i].seriesName + ':' + (params[i].value[1] - 0).toFixed(2) + '<br/>'
|
||||
}
|
||||
}
|
||||
return tips
|
||||
},
|
||||
textStyle: {
|
||||
color: '#fff',
|
||||
fontStyle: 'normal',
|
||||
opacity: 0.35,
|
||||
fontSize: 14
|
||||
},
|
||||
backgroundColor: 'rgba(0,0,0,0.55)',
|
||||
borderWidth: 0
|
||||
},
|
||||
title: {
|
||||
left: 'center',
|
||||
text: titleText,
|
||||
textStyle: {
|
||||
fontSize: '0.8rem',
|
||||
color: props.DColor ? '#fff' : echartsColor.WordColor
|
||||
}
|
||||
},
|
||||
legend: {
|
||||
right: 50,
|
||||
top: 25,
|
||||
verticalAlign: 'top',
|
||||
enabled: true,
|
||||
itemDistance: 5,
|
||||
textStyle: {
|
||||
fontSize: '0.6rem',
|
||||
color: props.DColor ? '#fff' : echartsColor.WordColor,
|
||||
rich: { a: { verticalAlign: 'middle' } },
|
||||
padding: [0, 0, 0, 0]
|
||||
}
|
||||
},
|
||||
toolbox: {
|
||||
right: 20,
|
||||
top: 15,
|
||||
feature: {
|
||||
myCustomDownload: {
|
||||
title: '',
|
||||
icon: 'path://M892.342857 463.238095l-73.142857-68.266666-258.438095 258.438095V29.257143h-97.52381v624.152381L204.8 394.971429 131.657143 463.238095l380.342857 380.342857zM107.27619 897.219048h804.571429v97.523809H107.27619z',
|
||||
onclick: () => download()
|
||||
}
|
||||
}
|
||||
},
|
||||
xAxis: {
|
||||
type: 'value',
|
||||
name: '时间\n(ms)',
|
||||
boundaryGap: false,
|
||||
min: props.wp?.listWaveData[0][0] || 0,
|
||||
max: props.wp?.listWaveData[props.wp?.listWaveData.length - 1][0] + 1 || 1,
|
||||
title: {
|
||||
text: 'ms',
|
||||
textStyle: {
|
||||
fontSize: '0.6rem',
|
||||
color: props.DColor ? '#fff' : echartsColor.WordColor
|
||||
},
|
||||
enabled: true,
|
||||
align: 'high'
|
||||
},
|
||||
splitLine: { show: false },
|
||||
nameTextStyle: { fontSize: '0.6rem' },
|
||||
axisTick: { alignWithLabel: true },
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: props.DColor ? '#fff' : echartsColor.thread
|
||||
},
|
||||
onZero: false
|
||||
},
|
||||
axisLabel: {
|
||||
fontSize: '0.6rem',
|
||||
color: props.DColor ? '#fff' : echartsColor.WordColor,
|
||||
formatter: function (value: number) {
|
||||
if (valA.value != (value - 0).toFixed(0)) {
|
||||
valA.value = Number((value - 0).toFixed(0))
|
||||
return (value - 0).toFixed(0)
|
||||
}
|
||||
return ''
|
||||
}
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
name: unit,
|
||||
title: {
|
||||
align: 'high',
|
||||
offset: 0,
|
||||
text: unit,
|
||||
rotation: 0,
|
||||
y: -10
|
||||
},
|
||||
boundaryGap: [0, '100%'],
|
||||
showLastLabel: true,
|
||||
max: max.toFixed(2) * 1.1,
|
||||
min: min.toFixed(2) > 0 ? min.toFixed(2) - min.toFixed(2) * 0.1 : min.toFixed(2) * 1.1,
|
||||
opposite: false,
|
||||
nameTextStyle: {
|
||||
fontSize: '0.6rem',
|
||||
color: props.DColor ? '#fff' : echartsColor.WordColor
|
||||
},
|
||||
axisLine: {
|
||||
show: true,
|
||||
lineStyle: {
|
||||
color: props.DColor ? '#fff' : echartsColor.thread
|
||||
},
|
||||
onZero: false
|
||||
},
|
||||
axisLabel: {
|
||||
fontSize: '0.6rem',
|
||||
color: props.DColor ? '#fff' : echartsColor.WordColor,
|
||||
formatter: function (value: number) {
|
||||
return (value - 0).toFixed(2)
|
||||
}
|
||||
},
|
||||
splitLine: {
|
||||
lineStyle: {
|
||||
color: [props.DColor ? '#fff' : echartsColor.thread],
|
||||
type: 'dashed',
|
||||
opacity: 0.5
|
||||
}
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
left: '1%',
|
||||
right: '2.8%',
|
||||
bottom: '40px',
|
||||
top: '70px',
|
||||
containLabel: true
|
||||
},
|
||||
dataZoom: [
|
||||
{
|
||||
type: 'inside',
|
||||
height: 13,
|
||||
start: 0,
|
||||
bottom: '20px',
|
||||
end: 100
|
||||
},
|
||||
{
|
||||
start: 0,
|
||||
height: 13,
|
||||
bottom: '20px',
|
||||
end: 100
|
||||
}
|
||||
],
|
||||
series: [
|
||||
{
|
||||
name: a,
|
||||
type: 'line',
|
||||
large: true,
|
||||
smooth: true,
|
||||
symbol: 'none',
|
||||
sampling: 'average',
|
||||
itemStyle: { color: '#DAA520' },
|
||||
data: adata
|
||||
},
|
||||
{
|
||||
name: b,
|
||||
type: 'line',
|
||||
large: true,
|
||||
smooth: true,
|
||||
symbol: 'none',
|
||||
sampling: 'average',
|
||||
itemStyle: { color: '#2E8B57' },
|
||||
data: bdata
|
||||
},
|
||||
{
|
||||
name: c,
|
||||
type: 'line',
|
||||
large: true,
|
||||
smooth: true,
|
||||
symbol: 'none',
|
||||
sampling: 'average',
|
||||
itemStyle: { color: '#A52a2a' },
|
||||
data: cdata
|
||||
},
|
||||
{
|
||||
name: '暂降触发点',
|
||||
type: 'scatter',
|
||||
symbol: 'image://' + url,
|
||||
itemStyle: { width: 18, height: 18 },
|
||||
data: cu
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
myChartes.setOption(option)
|
||||
myChartess.value = myChartes
|
||||
|
||||
setTimeout(() => {
|
||||
myChartes.resize()
|
||||
loading.value = false
|
||||
}, 400)
|
||||
|
||||
if (waveDatas && waveDatas.length > 1) {
|
||||
const waveDatasTemp = waveDatas.slice(1).reverse()
|
||||
for (let step = 0; step < waveDatasTemp.length; step++) {
|
||||
drawPics(waveDatasTemp[step], picHeight, step, show, myChartes, titleText)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const drawPics = (waveDataTemp: WaveData, picHeight: string, step: number, show: boolean, myChartes1: echarts.ECharts, title: string) => {
|
||||
step = step + 1
|
||||
const waveId = 'wave' + step
|
||||
let a: string | null = null, b: string | null = null, c: string | null = null
|
||||
let max: any = 0, min: any = 0, unit = ''
|
||||
let adata: number[][] = [], bdata: number[][] = [], cdata: number[][] = []
|
||||
const colors: string[] = []
|
||||
|
||||
switch (iphasic.value) {
|
||||
case 1:
|
||||
a = waveDataTemp.title.aTitle
|
||||
colors.push('#DAA520', '#fff', '#fff')
|
||||
break
|
||||
case 2:
|
||||
a = waveDataTemp.title.aTitle
|
||||
b = waveDataTemp.title.bTitle
|
||||
colors.push('#DAA520', '#2E8B57', '#fff')
|
||||
break
|
||||
case 3:
|
||||
a = waveDataTemp.title.aTitle
|
||||
b = waveDataTemp.title.bTitle
|
||||
c = waveDataTemp.title.cTitle
|
||||
colors.push('#DAA520', '#2E8B57', '#A52a2a')
|
||||
break
|
||||
}
|
||||
|
||||
if (props.value === 1) {
|
||||
max = waveDataTemp.instantF.max
|
||||
min = waveDataTemp.instantF.min
|
||||
adata = waveDataTemp.shunshiF.shunshiFA
|
||||
bdata = waveDataTemp.shunshiF.shunshiFB
|
||||
cdata = waveDataTemp.shunshiF.shunshiFC
|
||||
} else {
|
||||
max = waveDataTemp.instantS.max
|
||||
min = waveDataTemp.instantS.min
|
||||
adata = waveDataTemp.shunshiS.shunshiSA
|
||||
bdata = waveDataTemp.shunshiS.shunshiSB
|
||||
cdata = waveDataTemp.shunshiS.shunshiSC
|
||||
}
|
||||
|
||||
if (waveDataTemp.title.unit === '电压') {
|
||||
unit = props.value === 1 ? 'kV' : 'V'
|
||||
} else {
|
||||
unit = 'A'
|
||||
}
|
||||
|
||||
let titlename = ''
|
||||
if (props.boxoList.systemType == 'ZL') {
|
||||
const str = waveId.split('e')
|
||||
const str1 = Number(str[1])
|
||||
props.wp.channelNames.forEach((element: string, i: number) => {
|
||||
if (i == 4 || i == 7 || i == 10) {
|
||||
if (str1 == 1 && i == 4) {
|
||||
const s = element.split('A')
|
||||
const s1 = s[0] == 'LI' ? '电网侧-电流' : s[0] + '侧' + s[1]
|
||||
titlename = s1 + ' ' + title
|
||||
}
|
||||
if (str1 == 2 && i == 7) {
|
||||
const s = element.split('A')
|
||||
const s1 = s[0] == 'SU' ? '负载侧-电压' : s[0] + '侧' + s[1]
|
||||
titlename = s1 + ' ' + title
|
||||
}
|
||||
if (str1 == 3 && i == 10) {
|
||||
const s = element.split('A')
|
||||
const s1 = s[0] == 'SI' ? '负载侧-电流' : s[0] + '侧' + s[1]
|
||||
titlename = s1 + ' ' + title
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const waveIds = document.getElementById(waveId)
|
||||
if (!waveIds) return
|
||||
|
||||
const myChartes = echarts.init(waveIds)
|
||||
const echartsColor = { WordColor: "#fff", thread: "#fff", FigureColor: ["#07CCCA ", "#00BFF5", "#FFBF00", "#77DA63", "#D5FF6B", "#Ff6600", "#FF9100", "#5B6E96", "#66FFCC", "#B3B3B3", "#FF00FF", "#CC00FF", "#FF9999"] }
|
||||
|
||||
const option = {
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
borderColor: 'grey',
|
||||
formatter: function (params: any) {
|
||||
let tips = '时刻:' + params[0].data[0] + '</br/>'
|
||||
for (let i = 0; i < params.length; i++) {
|
||||
if (params[i].seriesName != '暂降触发点') {
|
||||
tips += params[i].seriesName + ':' + (params[i].value[1] - 0).toFixed(2) + '<br/>'
|
||||
}
|
||||
}
|
||||
return tips
|
||||
},
|
||||
textStyle: {
|
||||
color: '#fff',
|
||||
fontStyle: 'normal',
|
||||
opacity: 0.35,
|
||||
fontSize: 14
|
||||
},
|
||||
backgroundColor: 'rgba(0,0,0,0.55)',
|
||||
borderWidth: 0
|
||||
},
|
||||
title: {
|
||||
left: 'center',
|
||||
text: titlename || title,
|
||||
textStyle: {
|
||||
fontSize: '0.8rem',
|
||||
color: props.DColor ? '#fff' : echartsColor.WordColor
|
||||
}
|
||||
},
|
||||
legend: {
|
||||
right: 50,
|
||||
top: 25,
|
||||
verticalAlign: 'top',
|
||||
enabled: true,
|
||||
itemDistance: 5,
|
||||
textStyle: {
|
||||
fontSize: '0.6rem',
|
||||
color: props.DColor ? '#fff' : echartsColor.WordColor,
|
||||
rich: { a: { verticalAlign: 'middle' } },
|
||||
padding: [0, 0, 0, 0]
|
||||
}
|
||||
},
|
||||
xAxis: {
|
||||
type: 'value',
|
||||
name: '时间\n(ms)',
|
||||
boundaryGap: false,
|
||||
min: props.wp.listWaveData[0][0],
|
||||
max: props.wp.listWaveData[props.wp.listWaveData.length - 1][0] + 1,
|
||||
title: {
|
||||
text: 'ms',
|
||||
textStyle: {
|
||||
fontSize: '0.6rem',
|
||||
color: props.DColor ? '#fff' : echartsColor.WordColor
|
||||
},
|
||||
enabled: true,
|
||||
align: 'high'
|
||||
},
|
||||
splitLine: { show: false },
|
||||
axisTick: { alignWithLabel: true },
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: props.DColor ? '#fff' : echartsColor.thread
|
||||
},
|
||||
onZero: false
|
||||
},
|
||||
nameTextStyle: { fontSize: '0.6rem' },
|
||||
axisLabel: {
|
||||
fontSize: '0.6rem',
|
||||
color: props.DColor ? '#fff' : echartsColor.WordColor,
|
||||
formatter: function (value: number) {
|
||||
if (valB.value != (value - 0).toFixed(0)) {
|
||||
valB.value = Number((value - 0).toFixed(0))
|
||||
return (value - 0).toFixed(0)
|
||||
}
|
||||
return ''
|
||||
}
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
name: unit,
|
||||
title: {
|
||||
align: 'high',
|
||||
offset: 0,
|
||||
text: unit,
|
||||
rotation: 0,
|
||||
y: -10
|
||||
},
|
||||
boundaryGap: [0, '100%'],
|
||||
showLastLabel: true,
|
||||
max: max.toFixed(2) * 1.1,
|
||||
min: min.toFixed(2) > 0 ? min.toFixed(2) - min.toFixed(2) * 0.1 : min.toFixed(2) * 1.1,
|
||||
opposite: false,
|
||||
nameTextStyle: {
|
||||
fontSize: '0.6rem',
|
||||
color: props.DColor ? '#fff' : echartsColor.WordColor
|
||||
},
|
||||
axisLine: {
|
||||
show: true,
|
||||
lineStyle: {
|
||||
color: props.DColor ? '#fff' : echartsColor.thread
|
||||
},
|
||||
onZero: false
|
||||
},
|
||||
axisLabel: {
|
||||
fontSize: '0.6rem',
|
||||
color: props.DColor ? '#fff' : echartsColor.WordColor,
|
||||
formatter: function (value: number) {
|
||||
return (value - 0).toFixed(2)
|
||||
}
|
||||
},
|
||||
splitLine: {
|
||||
lineStyle: {
|
||||
color: [props.DColor ? '#fff' : echartsColor.thread],
|
||||
type: 'dashed',
|
||||
opacity: 0.5
|
||||
}
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
left: '1%',
|
||||
right: '2.8%',
|
||||
bottom: '40px',
|
||||
top: '70px',
|
||||
containLabel: true
|
||||
},
|
||||
dataZoom: [
|
||||
{
|
||||
type: 'inside',
|
||||
height: 13,
|
||||
start: 0,
|
||||
bottom: '20px',
|
||||
end: 100
|
||||
},
|
||||
{
|
||||
start: 0,
|
||||
height: 13,
|
||||
bottom: '20px',
|
||||
end: 100
|
||||
}
|
||||
],
|
||||
series: [
|
||||
{
|
||||
name: a,
|
||||
type: 'line',
|
||||
large: true,
|
||||
smooth: true,
|
||||
symbol: 'none',
|
||||
sampling: 'average',
|
||||
itemStyle: { color: '#DAA520' },
|
||||
data: adata
|
||||
},
|
||||
{
|
||||
name: b,
|
||||
type: 'line',
|
||||
large: true,
|
||||
smooth: true,
|
||||
symbol: 'none',
|
||||
sampling: 'average',
|
||||
itemStyle: { color: '#2E8B57' },
|
||||
data: bdata
|
||||
},
|
||||
{
|
||||
name: c,
|
||||
type: 'line',
|
||||
large: true,
|
||||
smooth: true,
|
||||
symbol: 'none',
|
||||
sampling: 'average',
|
||||
itemStyle: { color: '#A52a2a' },
|
||||
data: cdata
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
myChartes.setOption(option)
|
||||
|
||||
switch (step) {
|
||||
case 1: myChartess1.value = myChartes; break
|
||||
case 2: myChartess2.value = myChartes; break
|
||||
case 3: myChartess3.value = myChartes; break
|
||||
case 4: myChartess4.value = myChartes; break
|
||||
case 5: myChartess5.value = myChartes; break
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
myChartes.resize()
|
||||
loading.value = false
|
||||
}, 400)
|
||||
|
||||
echarts.connect([myChartes1, myChartes])
|
||||
}
|
||||
|
||||
const backbxlb = () => {
|
||||
waveDatas.value = []
|
||||
const charts = [myChartess.value, myChartess1.value, myChartess2.value, myChartess3.value, myChartess4.value, myChartess5.value]
|
||||
|
||||
charts.forEach(chart => {
|
||||
if (chart) {
|
||||
chart.dispose()
|
||||
}
|
||||
})
|
||||
|
||||
myChartess.value = null
|
||||
myChartess1.value = null
|
||||
myChartess2.value = null
|
||||
myChartess3.value = null
|
||||
myChartess4.value = null
|
||||
myChartess5.value = null
|
||||
|
||||
// echarts.disconnect(charts.filter(Boolean) as echarts.ECharts[])
|
||||
charts
|
||||
.filter(Boolean)
|
||||
.forEach(chart => {
|
||||
if (chart && typeof chart.dispose === 'function') {
|
||||
chart.dispose();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const getMax = (temp: number, tempA: number, tempB: number, tempC: number): number => {
|
||||
temp = temp > tempA ? temp : tempA
|
||||
temp = temp > tempB ? temp : tempB
|
||||
temp = temp > tempC ? temp : tempC
|
||||
return temp
|
||||
}
|
||||
|
||||
const getMaxTwo = (temp: number, tempA: number, tempB: number): number => {
|
||||
temp = temp > tempA ? temp : tempA
|
||||
temp = temp > tempB ? temp : tempB
|
||||
return temp
|
||||
}
|
||||
|
||||
const getMin = (temp: number, tempA: number, tempB: number, tempC: number): number => {
|
||||
temp = temp < tempA ? temp : tempA
|
||||
temp = temp < tempB ? temp : tempB
|
||||
temp = temp < tempC ? temp : tempC
|
||||
return temp
|
||||
}
|
||||
|
||||
const getMinOpen = (temp: number, tempA: number, tempB: number): number => {
|
||||
temp = temp < tempA ? temp : tempA
|
||||
temp = temp < tempB ? temp : tempB
|
||||
return temp
|
||||
}
|
||||
</script>
|
||||
1229
src/components/BX/shushiboxi1.vue
Normal file
984
src/components/BX/shushiboxi2.vue
Normal file
@@ -0,0 +1,984 @@
|
||||
<template>
|
||||
<div v-loading="loading" :element-loading-background="'rgba(122, 122, 122, 0.8)'" class="boxbx"
|
||||
style="position: relative;height: 100%;">
|
||||
<div id="boxsj" style="background: #343849c7">
|
||||
<div id="shushi" :style="`height:${vh};overflow: hidden;`">
|
||||
<div class="bx" id="wave"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted, onBeforeUnmount, watch, nextTick } from 'vue'
|
||||
import html2canvas from 'html2canvas'
|
||||
import $ from 'jquery'
|
||||
import * as echarts from 'echarts'
|
||||
import url from '@/assets/img/point.png'
|
||||
|
||||
interface WaveData {
|
||||
instantF: { max: number; min: number }
|
||||
instantS: { max: number; min: number }
|
||||
shunshiF: {
|
||||
shunshiFA: number[][]
|
||||
shunshiFB: number[][]
|
||||
shunshiFC: number[][]
|
||||
}
|
||||
shunshiS: {
|
||||
shunshiSA: number[][]
|
||||
shunshiSB: number[][]
|
||||
shunshiSC: number[][]
|
||||
}
|
||||
title: {
|
||||
aTitle: string
|
||||
bTitle: string
|
||||
cTitle: string
|
||||
unit: string
|
||||
}
|
||||
unit: string
|
||||
}
|
||||
|
||||
interface Props {
|
||||
value?: number
|
||||
flag?: string | number | boolean
|
||||
parentHeight?: string | number | boolean
|
||||
DColor?: boolean
|
||||
boxoList?: any
|
||||
wp?: any
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
value: 2,
|
||||
flag: 3,
|
||||
parentHeight: 0,
|
||||
DColor: false,
|
||||
boxoList: () => ({}),
|
||||
wp: () => ({})
|
||||
})
|
||||
|
||||
const loading = ref(true)
|
||||
const tabvalue = ref(props.value)
|
||||
const valA: any = ref(0)
|
||||
const valB: any = ref(0)
|
||||
const isOpen = ref(false)
|
||||
const time = ref('')
|
||||
const type = ref('')
|
||||
const severity: any = ref('')
|
||||
const iphasic: any = ref('')
|
||||
const eventValue = ref('')
|
||||
const persistTime = ref('')
|
||||
const lineName = ref('')
|
||||
const subName = ref('')
|
||||
const waveDatas = ref<WaveData[]>([])
|
||||
const ptpass = ref('')
|
||||
const waveHeight = ref<number>()
|
||||
const rmsHeight = ref<number>()
|
||||
const color = ref('#006565')
|
||||
const charts = ref({})
|
||||
const arrpoints = ref([])
|
||||
const titles = ref('')
|
||||
const zoom = ref(1)
|
||||
const myChartess = ref<echarts.ECharts | null>(null)
|
||||
const myChartess1 = ref<echarts.ECharts | null>(null)
|
||||
const myChartess2 = ref<echarts.ECharts | null>(null)
|
||||
const myChartess3 = ref<echarts.ECharts | null>(null)
|
||||
const myChartess4 = ref<echarts.ECharts | null>(null)
|
||||
const myChartess5 = ref<echarts.ECharts | null>(null)
|
||||
|
||||
const vh = computed(() => {
|
||||
if (props.flag == 1) {
|
||||
return '680px'
|
||||
} else if (props.parentHeight != 0) {
|
||||
return '300px'
|
||||
} else {
|
||||
return '340px'
|
||||
}
|
||||
})
|
||||
|
||||
const vw = computed(() => '100%')
|
||||
|
||||
watch(() => props.value, (newVal) => {
|
||||
if (newVal == 2) {
|
||||
initWaves()
|
||||
} else {
|
||||
$('#wave1').remove()
|
||||
initWaves()
|
||||
}
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
const zoomValue = document.body.style.getPropertyValue('zoom');
|
||||
zoom.value = 1 / (zoomValue ? parseFloat(zoomValue) : 1);
|
||||
|
||||
window.addEventListener('resize', handleResize)
|
||||
nextTick(() => {
|
||||
setTimeout(() => {
|
||||
query()
|
||||
}, 500)
|
||||
})
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
console.log('组件卸载');
|
||||
|
||||
backbxlb()
|
||||
window.removeEventListener('resize', handleResize)
|
||||
})
|
||||
|
||||
const handleResize = () => {
|
||||
const zoomValue = document.body.style.getPropertyValue('zoom');
|
||||
zoom.value = 1 / (zoomValue ? parseFloat(zoomValue) : 1);
|
||||
}
|
||||
|
||||
const download = () => {
|
||||
const boxsj = document.getElementById('boxsj')
|
||||
if (boxsj) {
|
||||
html2canvas(boxsj, {
|
||||
scale: 2,
|
||||
}).then(function (canvas) {
|
||||
const creatIMg = document.createElement('a')
|
||||
creatIMg.download = '瞬间波形.png'
|
||||
creatIMg.href = canvas.toDataURL()
|
||||
creatIMg.click()
|
||||
creatIMg.remove()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const query = () => {
|
||||
loading.value = true
|
||||
initWaves()
|
||||
}
|
||||
|
||||
const waveData = (instantF: any, instantS: any, shunshiF: any, shunshiS: any, title: any, unit: any): WaveData => {
|
||||
return {
|
||||
instantF,
|
||||
instantS,
|
||||
shunshiF,
|
||||
shunshiS,
|
||||
title,
|
||||
unit
|
||||
}
|
||||
}
|
||||
|
||||
const initWaves = () => {
|
||||
if (props.wp) {
|
||||
if (props.boxoList.systemType == 'pms') {
|
||||
titles.value =
|
||||
'变电站名称:' +
|
||||
props.boxoList.powerStationName +
|
||||
' 监测点名称:' +
|
||||
props.boxoList.measurementPointName +
|
||||
' 发生时刻:' +
|
||||
props.boxoList.startTime +
|
||||
' 残余电压:' +
|
||||
(props.boxoList.featureAmplitude * 100).toFixed(2) +
|
||||
'% 持续时间:' +
|
||||
props.boxoList.duration +
|
||||
's'
|
||||
} else if (props.boxoList.systemType == 'ZL') {
|
||||
titles.value =
|
||||
' 监测点名称:' +
|
||||
props.boxoList.equipmentName +
|
||||
' 发生时刻:' +
|
||||
props.boxoList.startTime +
|
||||
' 残余电压:' +
|
||||
props.boxoList.evtParamVVaDepth +
|
||||
' 持续时间:' +
|
||||
props.boxoList.evtParamTm +
|
||||
's'
|
||||
} else {
|
||||
titles.value =
|
||||
'变电站名称:' +
|
||||
props.boxoList.subName +
|
||||
' 监测点名称:' +
|
||||
props.boxoList.lineName +
|
||||
' 发生时刻:' +
|
||||
props.boxoList.startTime +
|
||||
' 残余电压:' +
|
||||
(props.boxoList.featureAmplitude * 100).toFixed(2) +
|
||||
'% 持续时间:' +
|
||||
props.boxoList.duration +
|
||||
's'
|
||||
}
|
||||
|
||||
iphasic.value = props.wp.iphasic || 1
|
||||
const picCounts = (props.wp.waveTitle.length - 1) / iphasic.value
|
||||
waveDatas.value = []
|
||||
|
||||
for (let i = 0; i < picCounts; i++) {
|
||||
const data = fliteWaveData(props.wp, i)
|
||||
waveDatas.value.push(data)
|
||||
}
|
||||
|
||||
time.value = props.wp.time
|
||||
type.value = props.wp.waveType
|
||||
severity.value = props.wp.yzd
|
||||
|
||||
if (severity.value < 0) {
|
||||
severity.value = '/'
|
||||
type.value = '/'
|
||||
}
|
||||
|
||||
initWave(waveDatas.value, time.value, type.value, severity.value, isOpen.value)
|
||||
} else {
|
||||
initWave(null, null, null, null, null)
|
||||
}
|
||||
}
|
||||
|
||||
const fliteWaveData = (wp: any, step: number): WaveData => {
|
||||
const shunData = wp.listWaveData
|
||||
const pt = Number(wp.pt) / 1000
|
||||
const ct = Number(wp.ct)
|
||||
const titleList = wp.waveTitle
|
||||
let xishu = pt
|
||||
let aTitle = '', bTitle = '', cTitle = '', unit = '电压'
|
||||
let ifmax = 0, ifmin = 0, ismax = 0, ismin = 0
|
||||
|
||||
const shunshiFA: number[][] = []
|
||||
const shunshiFB: number[][] = []
|
||||
const shunshiFC: number[][] = []
|
||||
const shunshiSA: number[][] = []
|
||||
const shunshiSB: number[][] = []
|
||||
const shunshiSC: number[][] = []
|
||||
|
||||
if (shunData.length > 0) {
|
||||
if (titleList[iphasic.value * step + 1]?.substring(0, 1) !== 'U') {
|
||||
xishu = ct
|
||||
unit = '电流'
|
||||
}
|
||||
|
||||
for (let i = 1; i <= iphasic.value; i++) {
|
||||
switch (i) {
|
||||
case 1:
|
||||
aTitle = titleList[iphasic.value * step + i]?.substring(1) || ''
|
||||
break
|
||||
case 2:
|
||||
bTitle = titleList[iphasic.value * step + i]?.substring(1) || ''
|
||||
break
|
||||
case 3:
|
||||
cTitle = titleList[iphasic.value * step + i]?.substring(1) || ''
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (shunData[0][iphasic.value * step + 1] !== undefined) {
|
||||
ifmax = shunData[0][iphasic.value * step + 1] * xishu
|
||||
ifmin = shunData[0][iphasic.value * step + 1] * xishu
|
||||
ismax = shunData[0][iphasic.value * step + 1]
|
||||
ismin = shunData[0][iphasic.value * step + 1]
|
||||
}
|
||||
|
||||
for (let shun = 0; shun < shunData.length; shun++) {
|
||||
if (shunData[shun][iphasic.value * step + 1] === undefined) {
|
||||
break
|
||||
}
|
||||
|
||||
switch (iphasic.value) {
|
||||
case 1:
|
||||
const shunFirstA = shunData[shun][iphasic.value * step + 1] * xishu
|
||||
shunshiFA.push([shunData[shun][0], shunFirstA])
|
||||
ifmax = ifmax > shunFirstA ? ifmax : shunFirstA
|
||||
ifmin = ifmin < shunFirstA ? ifmin : shunFirstA
|
||||
|
||||
const shunSecondA = shunData[shun][iphasic.value * step + 1]
|
||||
shunshiSA.push([shunData[shun][0], shunSecondA])
|
||||
ismax = ismax > shunSecondA ? ismax : shunSecondA
|
||||
ismin = ismin < shunSecondA ? ismin : shunSecondA
|
||||
break
|
||||
case 2:
|
||||
const shunFirstA2 = shunData[shun][iphasic.value * step + 1] * xishu
|
||||
const shunFirstB2 = shunData[shun][iphasic.value * step + 2] * xishu
|
||||
shunshiFA.push([shunData[shun][0], shunFirstA2])
|
||||
shunshiFB.push([shunData[shun][0], shunFirstB2])
|
||||
ifmax = getMaxTwo(ifmax, shunFirstA2, shunFirstB2)
|
||||
ifmin = getMinOpen(ifmin, shunFirstA2, shunFirstB2)
|
||||
|
||||
const shunSecondA2 = shunData[shun][iphasic.value * step + 1]
|
||||
const shunSecondB2 = shunData[shun][iphasic.value * step + 2]
|
||||
shunshiSA.push([shunData[shun][0], shunSecondA2])
|
||||
shunshiSB.push([shunData[shun][0], shunSecondB2])
|
||||
ismax = getMaxTwo(ismax, shunSecondA2, shunSecondB2)
|
||||
ismin = getMinOpen(ismin, shunSecondA2, shunSecondB2)
|
||||
break
|
||||
case 3:
|
||||
const shunFirstA3 = shunData[shun][iphasic.value * step + 1] * xishu
|
||||
const shunFirstB3 = shunData[shun][iphasic.value * step + 2] * xishu
|
||||
const shunFirstC3 = shunData[shun][iphasic.value * step + 3] * xishu
|
||||
shunshiFA.push([shunData[shun][0], shunFirstA3])
|
||||
shunshiFB.push([shunData[shun][0], shunFirstB3])
|
||||
shunshiFC.push([shunData[shun][0], shunFirstC3])
|
||||
ifmax = getMax(ifmax, shunFirstA3, shunFirstB3, shunFirstC3)
|
||||
ifmin = isOpen.value ? getMinOpen(ifmin, shunFirstA3, shunFirstC3) : getMin(ifmin, shunFirstA3, shunFirstB3, shunFirstC3)
|
||||
|
||||
const shunSecondA3 = shunData[shun][iphasic.value * step + 1]
|
||||
const shunSecondB3 = shunData[shun][iphasic.value * step + 2]
|
||||
const shunSecondC3 = shunData[shun][iphasic.value * step + 3]
|
||||
shunshiSA.push([shunData[shun][0], shunSecondA3])
|
||||
shunshiSB.push([shunData[shun][0], shunSecondB3])
|
||||
shunshiSC.push([shunData[shun][0], shunSecondC3])
|
||||
ismax = getMax(ismax, shunSecondA3, shunSecondB3, shunSecondC3)
|
||||
ismin = isOpen.value ? getMinOpen(ismin, shunSecondA3, shunSecondC3) : getMin(ismin, shunSecondA3, shunSecondB3, shunSecondC3)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const instantF = { max: ifmax, min: ifmin }
|
||||
const instantS = { max: ismax, min: ismin }
|
||||
const shunshiF = { shunshiFA, shunshiFB, shunshiFC }
|
||||
const shunshiS = { shunshiSA, shunshiSB, shunshiSC }
|
||||
const title = { aTitle, bTitle, cTitle, unit }
|
||||
|
||||
return waveData(instantF, instantS, shunshiF, shunshiS, title, unit)
|
||||
}
|
||||
|
||||
const initWave = (waveDatas: WaveData[] | null, time: string | null, type: string | null, severity: string | null, isOpen: boolean | null) => {
|
||||
$('div.bx1').remove()
|
||||
|
||||
let picHeight = vh.value
|
||||
const show = !isOpen
|
||||
let isvisible = false
|
||||
let cu: number[][] = []
|
||||
let titleText = ''
|
||||
let unit = ''
|
||||
let max: any = 0, min: any = 0
|
||||
let a: string | null = null, b: string | null = null, c: string | null = null
|
||||
let adata: number[][] = [], bdata: number[][] = [], cdata: number[][] = []
|
||||
const colors: string[] = []
|
||||
|
||||
if (!waveDatas) {
|
||||
titleText = '该事件暂无波形图'
|
||||
} else if (waveDatas.length > 0) {
|
||||
titleText = titles.value
|
||||
|
||||
if (Number(eventValue.value) <= 90) {
|
||||
isvisible = true
|
||||
}
|
||||
|
||||
switch (iphasic.value) {
|
||||
case 1:
|
||||
a = waveDatas[0].title.aTitle
|
||||
if (props.value === 1) {
|
||||
cu = [[0, waveDatas[0].instantF.min]]
|
||||
max = waveDatas[0].instantF.max
|
||||
min = waveDatas[0].instantF.min
|
||||
adata = waveDatas[0].shunshiF.shunshiFA
|
||||
} else {
|
||||
cu = [[0, waveDatas[0].instantS.min]]
|
||||
max = waveDatas[0].instantS.max
|
||||
min = waveDatas[0].instantS.min
|
||||
adata = waveDatas[0].shunshiS.shunshiSA
|
||||
}
|
||||
colors.push('#DAA520', '#fff', '#fff')
|
||||
break
|
||||
case 2:
|
||||
a = waveDatas[0].title.aTitle
|
||||
b = waveDatas[0].title.bTitle
|
||||
if (props.value === 1) {
|
||||
cu = [[0, waveDatas[0].instantF.min]]
|
||||
max = waveDatas[0].instantF.max
|
||||
min = waveDatas[0].instantF.min
|
||||
adata = waveDatas[0].shunshiF.shunshiFA
|
||||
bdata = waveDatas[0].shunshiF.shunshiFB
|
||||
} else {
|
||||
cu = [[0, waveDatas[0].instantS.min]]
|
||||
max = waveDatas[0].instantS.max
|
||||
min = waveDatas[0].instantS.min
|
||||
adata = waveDatas[0].shunshiS.shunshiSA
|
||||
bdata = waveDatas[0].shunshiS.shunshiSB
|
||||
}
|
||||
colors.push('#DAA520', '#2E8B57', '#fff')
|
||||
break
|
||||
case 3:
|
||||
a = waveDatas[0].title.aTitle
|
||||
b = waveDatas[0].title.bTitle
|
||||
c = waveDatas[0].title.cTitle
|
||||
if (props.value === 1) {
|
||||
cu = [[0, waveDatas[0].instantF.min]]
|
||||
max = waveDatas[0].instantF.max
|
||||
min = waveDatas[0].instantF.min
|
||||
adata = waveDatas[0].shunshiF.shunshiFA
|
||||
bdata = waveDatas[0].shunshiF.shunshiFB
|
||||
cdata = waveDatas[0].shunshiF.shunshiFC
|
||||
} else {
|
||||
cu = [[0, waveDatas[0].instantS.min]]
|
||||
max = waveDatas[0].instantS.max
|
||||
min = waveDatas[0].instantS.min
|
||||
adata = waveDatas[0].shunshiS.shunshiSA
|
||||
bdata = waveDatas[0].shunshiS.shunshiSB
|
||||
cdata = waveDatas[0].shunshiS.shunshiSC
|
||||
}
|
||||
colors.push('#DAA520', '#2E8B57', '#A52a2a')
|
||||
break
|
||||
}
|
||||
|
||||
if (waveDatas[0].title.unit === '电压') {
|
||||
unit = props.value === 1 ? 'kV' : 'V'
|
||||
} else {
|
||||
unit = 'A'
|
||||
}
|
||||
|
||||
for (let step = waveDatas.length - 1; step > 0 && step < waveDatas.length; step--) {
|
||||
const waveId = 'wave' + step
|
||||
const newDivShunshi = $(`<div style="height:${vh.value};overflow: hidden;">
|
||||
<div class='bx1' id='${waveId}'></div>
|
||||
</div>`)
|
||||
newDivShunshi.insertAfter($('#shushi'))
|
||||
$(`#${waveId}`).css('height', picHeight).css('width', vw.value)
|
||||
}
|
||||
} else {
|
||||
titleText = `变电站名称:${subName.value} 监测点名称:${lineName.value} 发生时刻:${time} 残余电压:${(Number(eventValue.value) * 1).toFixed(0)}% 持续时间:${persistTime.value}s`
|
||||
}
|
||||
|
||||
const wave = document.getElementById('wave')
|
||||
if (!wave) return
|
||||
|
||||
const myChartes = echarts.init(wave)
|
||||
const echartsColor = { WordColor: "#fff", thread: "#fff", FigureColor: ["#07CCCA ", "#00BFF5", "#FFBF00", "#77DA63", "#D5FF6B", "#Ff6600", "#FF9100", "#5B6E96", "#66FFCC", "#B3B3B3", "#FF00FF", "#CC00FF", "#FF9999"] }
|
||||
|
||||
setTimeout(() => {
|
||||
wave.style.width = '100%'
|
||||
wave.style.height = vh.value
|
||||
}, 0)
|
||||
|
||||
const option = {
|
||||
tooltip: {
|
||||
top: '10px',
|
||||
trigger: 'axis',
|
||||
borderColor: 'grey',
|
||||
backgroundColor: '#fff',
|
||||
style: {
|
||||
color: '#fff',
|
||||
fontSize: '15px',
|
||||
padding: 10
|
||||
},
|
||||
formatter: function (params: any) {
|
||||
let tips = '时刻:' + params[0].data[0] + '</br/>'
|
||||
for (let i = 0; i < params.length; i++) {
|
||||
if (params[i].seriesName != '暂降触发点') {
|
||||
tips += params[i].marker + params[i].seriesName + ':' + (params[i].value[1] - 0).toFixed(2) + '<br/>'
|
||||
}
|
||||
}
|
||||
return tips
|
||||
},
|
||||
textStyle: {
|
||||
color: '#fff',
|
||||
fontStyle: 'normal',
|
||||
opacity: 0.35,
|
||||
fontSize: 14
|
||||
},
|
||||
borderWidth: 0
|
||||
},
|
||||
title: {
|
||||
left: 'center',
|
||||
text: titleText,
|
||||
textStyle: {
|
||||
fontSize: '0.8rem',
|
||||
color: props.DColor ? '#fff' : echartsColor.WordColor
|
||||
}
|
||||
},
|
||||
legend: {
|
||||
right: 50,
|
||||
top: 25,
|
||||
verticalAlign: 'top',
|
||||
enabled: true,
|
||||
itemDistance: 5,
|
||||
textStyle: {
|
||||
fontSize: '0.6rem',
|
||||
color: props.DColor ? '#fff' : echartsColor.WordColor,
|
||||
rich: { a: { verticalAlign: 'middle' } },
|
||||
padding: [0, 0, 0, 0]
|
||||
}
|
||||
},
|
||||
toolbox: {
|
||||
right: 20,
|
||||
top: 15,
|
||||
feature: {
|
||||
myCustomDownload: {
|
||||
title: '',
|
||||
icon: 'path://M892.342857 463.238095l-73.142857-68.266666-258.438095 258.438095V29.257143h-97.52381v624.152381L204.8 394.971429 131.657143 463.238095l380.342857 380.342857zM107.27619 897.219048h804.571429v97.523809H107.27619z',
|
||||
onclick: () => download()
|
||||
}
|
||||
}
|
||||
},
|
||||
xAxis: {
|
||||
type: 'value',
|
||||
name: '时间\n(ms)',
|
||||
boundaryGap: false,
|
||||
min: props.wp?.listWaveData[0][0] || 0,
|
||||
max: props.wp?.listWaveData[props.wp?.listWaveData.length - 1][0] + 1 || 1,
|
||||
title: {
|
||||
text: 'ms',
|
||||
textStyle: {
|
||||
fontSize: '0.6rem',
|
||||
color: props.DColor ? '#fff' : echartsColor.WordColor
|
||||
},
|
||||
enabled: true,
|
||||
align: 'high'
|
||||
},
|
||||
splitLine: { show: false },
|
||||
nameTextStyle: { fontSize: '0.6rem' },
|
||||
axisTick: { alignWithLabel: true },
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: props.DColor ? '#fff' : echartsColor.thread
|
||||
},
|
||||
onZero: false
|
||||
},
|
||||
axisLabel: {
|
||||
fontSize: '0.6rem',
|
||||
color: props.DColor ? '#fff' : echartsColor.WordColor,
|
||||
formatter: function (value: number) {
|
||||
if (valA.value != (value - 0).toFixed(0)) {
|
||||
valA.value = Number((value - 0).toFixed(0))
|
||||
return (value - 0).toFixed(0)
|
||||
}
|
||||
return ''
|
||||
}
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
name: unit,
|
||||
title: {
|
||||
align: 'high',
|
||||
offset: 0,
|
||||
text: unit,
|
||||
rotation: 0,
|
||||
y: -10
|
||||
},
|
||||
boundaryGap: [0, '100%'],
|
||||
showLastLabel: true,
|
||||
max: max.toFixed(2) * 1.1,
|
||||
min: min.toFixed(2) > 0 ? min.toFixed(2) - min.toFixed(2) * 0.1 : min.toFixed(2) * 1.1,
|
||||
opposite: false,
|
||||
nameTextStyle: {
|
||||
fontSize: '0.6rem',
|
||||
color: props.DColor ? '#fff' : echartsColor.WordColor
|
||||
},
|
||||
axisLine: {
|
||||
show: true,
|
||||
lineStyle: {
|
||||
color: props.DColor ? '#fff' : echartsColor.thread
|
||||
},
|
||||
onZero: false
|
||||
},
|
||||
axisLabel: {
|
||||
fontSize: '0.6rem',
|
||||
color: props.DColor ? '#fff' : echartsColor.WordColor,
|
||||
formatter: function (value: number) {
|
||||
return (value - 0).toFixed(2)
|
||||
}
|
||||
},
|
||||
splitLine: {
|
||||
lineStyle: {
|
||||
color: [props.DColor ? '#fff' : echartsColor.thread],
|
||||
type: 'dashed',
|
||||
opacity: 0.5
|
||||
}
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
left: '1%',
|
||||
right: '2.8%',
|
||||
bottom: '40px',
|
||||
top: '70px',
|
||||
containLabel: true
|
||||
},
|
||||
dataZoom: [
|
||||
{
|
||||
type: 'inside',
|
||||
height: 13,
|
||||
start: 0,
|
||||
bottom: '20px',
|
||||
end: 100
|
||||
},
|
||||
{
|
||||
start: 0,
|
||||
height: 13,
|
||||
bottom: '20px',
|
||||
end: 100
|
||||
}
|
||||
],
|
||||
series: [
|
||||
{
|
||||
name: a,
|
||||
type: 'line',
|
||||
large: true,
|
||||
smooth: true,
|
||||
symbol: 'none',
|
||||
sampling: 'average',
|
||||
itemStyle: { color: '#DAA520' },
|
||||
data: adata
|
||||
},
|
||||
{
|
||||
name: b,
|
||||
type: 'line',
|
||||
large: true,
|
||||
smooth: true,
|
||||
symbol: 'none',
|
||||
sampling: 'average',
|
||||
itemStyle: { color: '#2E8B57' },
|
||||
data: bdata
|
||||
},
|
||||
{
|
||||
name: c,
|
||||
type: 'line',
|
||||
large: true,
|
||||
smooth: true,
|
||||
symbol: 'none',
|
||||
sampling: 'average',
|
||||
itemStyle: { color: '#A52a2a' },
|
||||
data: cdata
|
||||
},
|
||||
{
|
||||
name: '暂降触发点',
|
||||
type: 'scatter',
|
||||
symbol: 'image://' + url,
|
||||
itemStyle: { width: 18, height: 18 },
|
||||
data: cu
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
myChartes.setOption(option)
|
||||
myChartess.value = myChartes
|
||||
|
||||
setTimeout(() => {
|
||||
myChartes.resize()
|
||||
loading.value = false
|
||||
}, 400)
|
||||
|
||||
if (waveDatas && waveDatas.length > 1) {
|
||||
const waveDatasTemp = waveDatas.slice(1).reverse()
|
||||
for (let step = 0; step < waveDatasTemp.length; step++) {
|
||||
drawPics(waveDatasTemp[step], picHeight, step, show, myChartes, titleText)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const drawPics = (waveDataTemp: WaveData, picHeight: string, step: number, show: boolean, myChartes1: echarts.ECharts, title: string) => {
|
||||
step = step + 1
|
||||
const waveId = 'wave' + step
|
||||
let a: string | null = null, b: string | null = null, c: string | null = null
|
||||
let max: any = 0, min: any = 0, unit = ''
|
||||
let adata: number[][] = [], bdata: number[][] = [], cdata: number[][] = []
|
||||
const colors: string[] = []
|
||||
|
||||
switch (iphasic.value) {
|
||||
case 1:
|
||||
a = waveDataTemp.title.aTitle
|
||||
colors.push('#DAA520', '#fff', '#fff')
|
||||
break
|
||||
case 2:
|
||||
a = waveDataTemp.title.aTitle
|
||||
b = waveDataTemp.title.bTitle
|
||||
colors.push('#DAA520', '#2E8B57', '#fff')
|
||||
break
|
||||
case 3:
|
||||
a = waveDataTemp.title.aTitle
|
||||
b = waveDataTemp.title.bTitle
|
||||
c = waveDataTemp.title.cTitle
|
||||
colors.push('#DAA520', '#2E8B57', '#A52a2a')
|
||||
break
|
||||
}
|
||||
|
||||
if (props.value === 1) {
|
||||
max = waveDataTemp.instantF.max
|
||||
min = waveDataTemp.instantF.min
|
||||
adata = waveDataTemp.shunshiF.shunshiFA
|
||||
bdata = waveDataTemp.shunshiF.shunshiFB
|
||||
cdata = waveDataTemp.shunshiF.shunshiFC
|
||||
} else {
|
||||
max = waveDataTemp.instantS.max
|
||||
min = waveDataTemp.instantS.min
|
||||
adata = waveDataTemp.shunshiS.shunshiSA
|
||||
bdata = waveDataTemp.shunshiS.shunshiSB
|
||||
cdata = waveDataTemp.shunshiS.shunshiSC
|
||||
}
|
||||
|
||||
if (waveDataTemp.title.unit === '电压') {
|
||||
unit = props.value === 1 ? 'kV' : 'V'
|
||||
} else {
|
||||
unit = 'A'
|
||||
}
|
||||
|
||||
let titlename = ''
|
||||
if (props.boxoList.systemType == 'ZL') {
|
||||
const str = waveId.split('e')
|
||||
const str1 = Number(str[1])
|
||||
props.wp.channelNames.forEach((element: string, i: number) => {
|
||||
if (i == 4 || i == 7 || i == 10) {
|
||||
if (str1 == 1 && i == 4) {
|
||||
const s = element.split('A')
|
||||
const s1 = s[0] == 'LI' ? '电网侧-电流' : s[0] + '侧' + s[1]
|
||||
titlename = s1 + ' ' + title
|
||||
}
|
||||
if (str1 == 2 && i == 7) {
|
||||
const s = element.split('A')
|
||||
const s1 = s[0] == 'SU' ? '负载侧-电压' : s[0] + '侧' + s[1]
|
||||
titlename = s1 + ' ' + title
|
||||
}
|
||||
if (str1 == 3 && i == 10) {
|
||||
const s = element.split('A')
|
||||
const s1 = s[0] == 'SI' ? '负载侧-电流' : s[0] + '侧' + s[1]
|
||||
titlename = s1 + ' ' + title
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const waveIds = document.getElementById(waveId)
|
||||
if (!waveIds) return
|
||||
|
||||
const myChartes = echarts.init(waveIds)
|
||||
const echartsColor = { WordColor: "#fff", thread: "#fff", FigureColor: ["#07CCCA ", "#00BFF5", "#FFBF00", "#77DA63", "#D5FF6B", "#Ff6600", "#FF9100", "#5B6E96", "#66FFCC", "#B3B3B3", "#FF00FF", "#CC00FF", "#FF9999"] }
|
||||
|
||||
const option = {
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
borderColor: 'grey',
|
||||
formatter: function (params: any) {
|
||||
let tips = '时刻:' + params[0].data[0] + '</br/>'
|
||||
for (let i = 0; i < params.length; i++) {
|
||||
if (params[i].seriesName != '暂降触发点') {
|
||||
tips += params[i].seriesName + ':' + (params[i].value[1] - 0).toFixed(2) + '<br/>'
|
||||
}
|
||||
}
|
||||
return tips
|
||||
},
|
||||
textStyle: {
|
||||
color: '#fff',
|
||||
fontStyle: 'normal',
|
||||
opacity: 0.35,
|
||||
fontSize: 14
|
||||
},
|
||||
backgroundColor: 'rgba(0,0,0,0.55)',
|
||||
borderWidth: 0
|
||||
},
|
||||
title: {
|
||||
left: 'center',
|
||||
text: titlename || title,
|
||||
textStyle: {
|
||||
fontSize: '0.8rem',
|
||||
color: props.DColor ? '#fff' : echartsColor.WordColor
|
||||
}
|
||||
},
|
||||
legend: {
|
||||
right: 50,
|
||||
top: 25,
|
||||
verticalAlign: 'top',
|
||||
enabled: true,
|
||||
itemDistance: 5,
|
||||
textStyle: {
|
||||
fontSize: '0.6rem',
|
||||
color: props.DColor ? '#fff' : echartsColor.WordColor,
|
||||
rich: { a: { verticalAlign: 'middle' } },
|
||||
padding: [0, 0, 0, 0]
|
||||
}
|
||||
},
|
||||
xAxis: {
|
||||
type: 'value',
|
||||
name: '时间\n(ms)',
|
||||
boundaryGap: false,
|
||||
min: props.wp.listWaveData[0][0],
|
||||
max: props.wp.listWaveData[props.wp.listWaveData.length - 1][0] + 1,
|
||||
title: {
|
||||
text: 'ms',
|
||||
textStyle: {
|
||||
fontSize: '0.6rem',
|
||||
color: props.DColor ? '#fff' : echartsColor.WordColor
|
||||
},
|
||||
enabled: true,
|
||||
align: 'high'
|
||||
},
|
||||
splitLine: { show: false },
|
||||
axisTick: { alignWithLabel: true },
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: props.DColor ? '#fff' : echartsColor.thread
|
||||
},
|
||||
onZero: false
|
||||
},
|
||||
nameTextStyle: { fontSize: '0.6rem' },
|
||||
axisLabel: {
|
||||
fontSize: '0.6rem',
|
||||
color: props.DColor ? '#fff' : echartsColor.WordColor,
|
||||
formatter: function (value: number) {
|
||||
if (valB.value != (value - 0).toFixed(0)) {
|
||||
valB.value = Number((value - 0).toFixed(0))
|
||||
return (value - 0).toFixed(0)
|
||||
}
|
||||
return ''
|
||||
}
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
name: unit,
|
||||
title: {
|
||||
align: 'high',
|
||||
offset: 0,
|
||||
text: unit,
|
||||
rotation: 0,
|
||||
y: -10
|
||||
},
|
||||
boundaryGap: [0, '100%'],
|
||||
showLastLabel: true,
|
||||
max: max.toFixed(2) * 1.1,
|
||||
min: min.toFixed(2) > 0 ? min.toFixed(2) - min.toFixed(2) * 0.1 : min.toFixed(2) * 1.1,
|
||||
opposite: false,
|
||||
nameTextStyle: {
|
||||
fontSize: '0.6rem',
|
||||
color: props.DColor ? '#fff' : echartsColor.WordColor
|
||||
},
|
||||
axisLine: {
|
||||
show: true,
|
||||
lineStyle: {
|
||||
color: props.DColor ? '#fff' : echartsColor.thread
|
||||
},
|
||||
onZero: false
|
||||
},
|
||||
axisLabel: {
|
||||
fontSize: '0.6rem',
|
||||
color: props.DColor ? '#fff' : echartsColor.WordColor,
|
||||
formatter: function (value: number) {
|
||||
return (value - 0).toFixed(2)
|
||||
}
|
||||
},
|
||||
splitLine: {
|
||||
lineStyle: {
|
||||
color: [props.DColor ? '#fff' : echartsColor.thread],
|
||||
type: 'dashed',
|
||||
opacity: 0.5
|
||||
}
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
left: '1%',
|
||||
right: '2.8%',
|
||||
bottom: '40px',
|
||||
top: '70px',
|
||||
containLabel: true
|
||||
},
|
||||
dataZoom: [
|
||||
{
|
||||
type: 'inside',
|
||||
height: 13,
|
||||
start: 0,
|
||||
bottom: '20px',
|
||||
end: 100
|
||||
},
|
||||
{
|
||||
start: 0,
|
||||
height: 13,
|
||||
bottom: '20px',
|
||||
end: 100
|
||||
}
|
||||
],
|
||||
series: [
|
||||
{
|
||||
name: a,
|
||||
type: 'line',
|
||||
large: true,
|
||||
smooth: true,
|
||||
symbol: 'none',
|
||||
sampling: 'average',
|
||||
itemStyle: { color: '#DAA520' },
|
||||
data: adata
|
||||
},
|
||||
{
|
||||
name: b,
|
||||
type: 'line',
|
||||
large: true,
|
||||
smooth: true,
|
||||
symbol: 'none',
|
||||
sampling: 'average',
|
||||
itemStyle: { color: '#2E8B57' },
|
||||
data: bdata
|
||||
},
|
||||
{
|
||||
name: c,
|
||||
type: 'line',
|
||||
large: true,
|
||||
smooth: true,
|
||||
symbol: 'none',
|
||||
sampling: 'average',
|
||||
itemStyle: { color: '#A52a2a' },
|
||||
data: cdata
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
myChartes.setOption(option)
|
||||
|
||||
switch (step) {
|
||||
case 1: myChartess1.value = myChartes; break
|
||||
case 2: myChartess2.value = myChartes; break
|
||||
case 3: myChartess3.value = myChartes; break
|
||||
case 4: myChartess4.value = myChartes; break
|
||||
case 5: myChartess5.value = myChartes; break
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
myChartes.resize()
|
||||
loading.value = false
|
||||
}, 400)
|
||||
|
||||
echarts.connect([myChartes1, myChartes])
|
||||
}
|
||||
|
||||
const backbxlb = () => {
|
||||
waveDatas.value = []
|
||||
const charts = [myChartess.value, myChartess1.value, myChartess2.value, myChartess3.value, myChartess4.value, myChartess5.value]
|
||||
|
||||
charts.forEach(chart => {
|
||||
if (chart) {
|
||||
chart.dispose()
|
||||
}
|
||||
})
|
||||
|
||||
myChartess.value = null
|
||||
myChartess1.value = null
|
||||
myChartess2.value = null
|
||||
myChartess3.value = null
|
||||
myChartess4.value = null
|
||||
myChartess5.value = null
|
||||
|
||||
// echarts.disconnect(charts.filter(Boolean) as echarts.ECharts[])
|
||||
charts
|
||||
.filter(Boolean)
|
||||
.forEach(chart => {
|
||||
if (chart && typeof chart.dispose === 'function') {
|
||||
chart.dispose();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const getMax = (temp: number, tempA: number, tempB: number, tempC: number): number => {
|
||||
temp = temp > tempA ? temp : tempA
|
||||
temp = temp > tempB ? temp : tempB
|
||||
temp = temp > tempC ? temp : tempC
|
||||
return temp
|
||||
}
|
||||
|
||||
const getMaxTwo = (temp: number, tempA: number, tempB: number): number => {
|
||||
temp = temp > tempA ? temp : tempA
|
||||
temp = temp > tempB ? temp : tempB
|
||||
return temp
|
||||
}
|
||||
|
||||
const getMin = (temp: number, tempA: number, tempB: number, tempC: number): number => {
|
||||
temp = temp < tempA ? temp : tempA
|
||||
temp = temp < tempB ? temp : tempB
|
||||
temp = temp < tempC ? temp : tempC
|
||||
return temp
|
||||
}
|
||||
|
||||
const getMinOpen = (temp: number, tempA: number, tempB: number): number => {
|
||||
temp = temp < tempA ? temp : tempA
|
||||
temp = temp < tempB ? temp : tempB
|
||||
return temp
|
||||
}
|
||||
</script>
|
||||
189
src/components/BX/waveForm.vue
Normal file
@@ -0,0 +1,189 @@
|
||||
<template>
|
||||
<div v-if="view2" v-loading="loading" element-loading-background="#343849c7">
|
||||
<el-row v-if="view4">
|
||||
<el-col :span="12">
|
||||
<span style="font-size: 14px; line-height: 30px">值类型选择:</span>
|
||||
<el-select
|
||||
style="width: 150px"
|
||||
@change="changeView"
|
||||
size="small"
|
||||
v-model="value"
|
||||
placeholder="请选择值类型"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in options"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<div
|
||||
|
||||
v-if="view4"
|
||||
element-loading-background="rgba(122, 122, 122, 0.8)"
|
||||
style="height: 750px"
|
||||
>
|
||||
<el-tabs
|
||||
class="default-main"
|
||||
v-model="bxactiveName"
|
||||
@tab-click="bxhandleClick"
|
||||
>
|
||||
<el-tab-pane
|
||||
label="瞬时波形"
|
||||
name="ssbx"
|
||||
class="boxbx pb10"
|
||||
:style="'height:' + bxecharts + ';overflow-y: auto;'"
|
||||
>
|
||||
<shushiboxi
|
||||
ref="shushiboxiRef"
|
||||
v-if="bxactiveName == 'ssbx' && showBoxi"
|
||||
:value="value"
|
||||
:parentHeight="parentHeight"
|
||||
:boxoList="boxoList"
|
||||
:wp="wp"
|
||||
></shushiboxi>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane
|
||||
label="RMS波形"
|
||||
class="boxbx pb10"
|
||||
name="rmsbx"
|
||||
:style="'height:' + bxecharts + ';overflow-y: auto;'"
|
||||
>
|
||||
<rmsboxi
|
||||
ref="rmsboxiRef"
|
||||
v-if="bxactiveName == 'rmsbx' && showBoxi"
|
||||
:value="value"
|
||||
:parentHeight="parentHeight"
|
||||
:boxoList="boxoList"
|
||||
:wp="wp"
|
||||
></rmsboxi>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
<el-empty v-else description="暂无数据" style="height: 700px" />
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import shushiboxi from "./shushiboxi.vue";
|
||||
import rmsboxi from "./rmsboxi.vue";
|
||||
import { ref, reactive } from "vue";
|
||||
import { getTransientAnalyseWave } from "@/api/statistics/index";
|
||||
const emit = defineEmits(["backbxlb"]);
|
||||
interface Props {
|
||||
// boxoList: any
|
||||
// wp: any,
|
||||
senior?: boolean;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
senior: false,
|
||||
});
|
||||
const parentHeight = ref(0);
|
||||
const loading = ref(true);
|
||||
const bxactiveName = ref("ssbx");
|
||||
const rmsboxiRef = ref();
|
||||
const value = ref(1);
|
||||
const options = ref([
|
||||
{
|
||||
value: 1,
|
||||
label: "一次值",
|
||||
},
|
||||
{
|
||||
value: 2,
|
||||
label: "二次值",
|
||||
},
|
||||
]);
|
||||
const shushiboxiRef = ref();
|
||||
const bxecharts = ref("700px");
|
||||
const view2 = ref(true);
|
||||
const boxoList: any = ref({});
|
||||
const wp: any = ref(null);
|
||||
const showBoxi = ref(true);
|
||||
const view3 = ref(false);
|
||||
const view4 = ref(false);
|
||||
const GJList = ref([]);
|
||||
|
||||
const open = async (row: any) => {
|
||||
console.log("🚀 ~ open ~ row:", row);
|
||||
loading.value = true;
|
||||
await getTransientAnalyseWave({
|
||||
// id: "151984a0-4a2e-4c46-aece-493e1a2e24c1",
|
||||
id: row.eventdetail_index,
|
||||
systemType: 0,
|
||||
type: 0,
|
||||
})
|
||||
.then((res) => {
|
||||
row.loading = false;
|
||||
if (res != undefined) {
|
||||
boxoList.value = {
|
||||
subName: row.bdname,
|
||||
lineName: row.pointname,
|
||||
startTime: row.timeid,
|
||||
featureAmplitude: row.eventvalue,
|
||||
duration: row.persisttime,
|
||||
};
|
||||
wp.value = res.data;
|
||||
loading.value = false;
|
||||
view4.value = true;
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
};
|
||||
const bxhandleClick = (tab: any) => {
|
||||
// if (shushiboxiRef.value) shushiboxiRef.value.backbxlb();
|
||||
// if (rmsboxiRef.value) rmsboxiRef.value.backbxlb();
|
||||
|
||||
loading.value = true;
|
||||
if (tab.name == "ssbx") {
|
||||
bxactiveName.value = "ssbx";
|
||||
} else if (tab.name == "rmsbx") {
|
||||
bxactiveName.value = "rmsbx";
|
||||
}
|
||||
setTimeout(() => {
|
||||
loading.value = false;
|
||||
}, 0);
|
||||
// console.log(tab, event);
|
||||
};
|
||||
const backbxlb = () => {
|
||||
boxoList.value = null;
|
||||
wp.value = null;
|
||||
// if (shushiboxiRef.value) shushiboxiRef.value.backbxlb();
|
||||
// if (rmsboxiRef.value) rmsboxiRef.value.backbxlb();
|
||||
|
||||
emit("backbxlb");
|
||||
};
|
||||
const setHeight = (h: any, vh: any) => {
|
||||
if (h != false) {
|
||||
parentHeight.value = h;
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
bxecharts.value = "700px";
|
||||
}, 100);
|
||||
};
|
||||
// 高级分析
|
||||
|
||||
const changeView = () => {
|
||||
// if (shushiboxiRef.value) shushiboxiRef.value.backbxlb();
|
||||
// if (rmsboxiRef.value) rmsboxiRef.value.backbxlb();
|
||||
showBoxi.value = false;
|
||||
setTimeout(() => {
|
||||
showBoxi.value = true;
|
||||
}, 0);
|
||||
};
|
||||
const gaoBack = () => {
|
||||
view2.value = true;
|
||||
view3.value = false;
|
||||
};
|
||||
defineExpose({ open, setHeight });
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
:deep(.el-tabs__item) {
|
||||
// color: #c2c2c2;
|
||||
--el-text-color-primary: #d3d4d6;
|
||||
}
|
||||
</style>
|
||||
38
src/components/HelloWorld.vue
Normal file
@@ -0,0 +1,38 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
|
||||
defineProps<{ msg: string }>()
|
||||
|
||||
const count = ref(0)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<h1>{{ msg }}</h1>
|
||||
|
||||
<div class="card">
|
||||
<button type="button" @click="count++">count is {{ count }}</button>
|
||||
<p>
|
||||
Edit
|
||||
<code>components/HelloWorld.vue</code> to test HMR
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<p>
|
||||
Check out
|
||||
<a href="https://vuejs.org/guide/quick-start.html#local" target="_blank"
|
||||
>create-vue</a
|
||||
>, the official Vue + Vite starter
|
||||
</p>
|
||||
<p>
|
||||
Install
|
||||
<a href="https://github.com/vuejs/language-tools" target="_blank">Volar</a>
|
||||
in your IDE for a better DX
|
||||
</p>
|
||||
<p class="read-the-docs">Click on the Vite and Vue logos to learn more</p>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.read-the-docs {
|
||||
color: #888;
|
||||
}
|
||||
</style>
|
||||
300
src/components/MyEchartMap.vue
Normal file
@@ -0,0 +1,300 @@
|
||||
<!-- 地图组件 -->
|
||||
<template>
|
||||
<div style="position: relative">
|
||||
<div class="bars_w" ref="chartMap" id="chartMap"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onBeforeUnmount, ref, watch, onMounted, defineEmits } from "vue";
|
||||
import * as echarts from "echarts";
|
||||
import "echarts-gl";
|
||||
import "echarts-liquidfill";
|
||||
import "echarts/lib/component/dataZoom";
|
||||
|
||||
const props = defineProps(["options"]);
|
||||
const myCharts = ref();
|
||||
const showCircle = ref(false);
|
||||
|
||||
const fetchConfig = async (name: string) => {
|
||||
const res = await import(`./mapJson/${name}.json`);
|
||||
return res.default;
|
||||
// GetEchar(res.default)
|
||||
};
|
||||
// fetchConfig()
|
||||
|
||||
const emit = defineEmits(["getRegionByRegion", "eliminate", "clickMap"]);
|
||||
onMounted(() => {});
|
||||
|
||||
const GetEchar = async (name: string) => {
|
||||
let chartDom = document.getElementById("chartMap");
|
||||
myCharts.value?.dispose();
|
||||
myCharts.value = echarts.init(chartDom);
|
||||
|
||||
echarts.registerMap(name, await fetchConfig(name)); //注册可用的地图
|
||||
let option = {
|
||||
title: {
|
||||
left: "center",
|
||||
top: "3%",
|
||||
...props.options.title,
|
||||
},
|
||||
tooltip: {
|
||||
trigger: "item",
|
||||
axisPointer: {
|
||||
type: "shadow",
|
||||
label: {
|
||||
color: "#fff",
|
||||
fontSize: 16,
|
||||
},
|
||||
},
|
||||
textStyle: {
|
||||
color: "#fff",
|
||||
fontStyle: "normal",
|
||||
opacity: 0.35,
|
||||
fontSize: 14,
|
||||
},
|
||||
backgroundColor: "rgba(0,0,0,0.55)",
|
||||
...(props.options.tooltip || null),
|
||||
},
|
||||
|
||||
legend: {
|
||||
orient: "vertical",
|
||||
left: 25,
|
||||
bottom: 40,
|
||||
itemWidth: 16,
|
||||
itemHeight: 16,
|
||||
|
||||
...(props.options.legend || null),
|
||||
},
|
||||
|
||||
color: [
|
||||
...(props.options.color || ""),
|
||||
"#07CCCA ",
|
||||
"#00BFF5",
|
||||
"#FFBF00",
|
||||
"#77DA63",
|
||||
"#D5FF6B",
|
||||
"#Ff6600",
|
||||
"#FF9100",
|
||||
"#5B6E96",
|
||||
"#66FFCC",
|
||||
"#B3B3B3",
|
||||
],
|
||||
|
||||
geo: {
|
||||
map: name,
|
||||
zoom: 1.3,
|
||||
// top: 10,
|
||||
// bottom: 0,
|
||||
roam: true,
|
||||
rotation: 90,
|
||||
|
||||
label: {
|
||||
normal: {
|
||||
show: true,
|
||||
fontSize: "12",
|
||||
color: "#fff",
|
||||
},
|
||||
},
|
||||
|
||||
itemStyle: {
|
||||
normal: {
|
||||
color: "#0a73ff80", //地图背景色
|
||||
borderColor: "#fff",
|
||||
borderWidth: 1,
|
||||
areaColor: {
|
||||
type: "radial",
|
||||
x: 0.5,
|
||||
y: 0.5,
|
||||
r: 0.8,
|
||||
colorStops: [
|
||||
{
|
||||
offset: 0,
|
||||
color: "#0a73ff80", // 0% 处的颜色
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: "#0a73ff80", // 100% 处的颜色
|
||||
},
|
||||
],
|
||||
globalCoord: false, // 缺省为 false
|
||||
},
|
||||
shadowColor: "#0a73ff80",
|
||||
|
||||
shadowOffsetX: -2,
|
||||
shadowOffsetY: 2,
|
||||
shadowBlur: 10,
|
||||
},
|
||||
emphasis: {
|
||||
areaColor: "#0a73ff70",
|
||||
shadowOffsetX: 0,
|
||||
shadowOffsetY: 0,
|
||||
borderWidth: 0,
|
||||
color: "#fff",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
...props.options.options,
|
||||
};
|
||||
if (props.options.visualMap) {
|
||||
option.visualMap = props.options.visualMap;
|
||||
}
|
||||
if (props.options.geo3D) {
|
||||
option.geo = null;
|
||||
option.geo3D = {
|
||||
map: name,
|
||||
...(props.options.geo3D || null),
|
||||
};
|
||||
}
|
||||
myCharts.value.setOption(option);
|
||||
window.addEventListener("resize", resizeHandler);
|
||||
const flag1 = ref(true);
|
||||
// 点击事件
|
||||
myCharts.value.off("click");
|
||||
myCharts.value.on("click", (e: any) => {
|
||||
setIcon('')
|
||||
if (props.options.geo3D) {
|
||||
// emit('clickMap', e)
|
||||
if (flag1.value) {
|
||||
flag1.value = false;
|
||||
setTimeout(() => {
|
||||
emit("clickMap", e);
|
||||
flag1.value = true;
|
||||
}, 100);
|
||||
}
|
||||
} else {
|
||||
// if (name == dictData.state.area?.[0].name && e.componentIndex == 0) {
|
||||
if (name == "中国" && e.componentIndex == 0) {
|
||||
// console.log('🚀 ~ file: MyEchartMap.vue:156 ~ myCharts.value.on ~ MapReturn(e.name):', MapReturn(e.name))
|
||||
}
|
||||
}
|
||||
});
|
||||
myCharts.value.on("datarangeselected", function (params: any) {
|
||||
// 手动调用restore方法恢复地图的默认颜色映射
|
||||
myCharts.value.dispatchAction({
|
||||
type: "restore",
|
||||
});
|
||||
});
|
||||
};
|
||||
// 记录上次操作的点信息
|
||||
let lastModifiedPoint = null; // 格式: {name, oldColor, oldSize}
|
||||
|
||||
/**
|
||||
* 修改地图上指定名称点的样式并显示提示框
|
||||
* @param {string} targetName - 要修改的点的name属性值
|
||||
* @param {string} newColor - 新的颜色值(如"#ff0000")
|
||||
* @param {number} newSize - 新的图标大小(如20)
|
||||
*/
|
||||
function setIcon(targetName) {
|
||||
// 获取当前图表实例(替换为你的地图容器ID)
|
||||
const chartInstance = echarts.getInstanceByDom(
|
||||
document.getElementById("chartMap")
|
||||
);
|
||||
if (!chartInstance) return;
|
||||
|
||||
// 获取当前配置项
|
||||
const option = chartInstance.getOption();
|
||||
if (!option || !option.series ) return;
|
||||
|
||||
// 找到对应的series(你的场景中是第一个series)
|
||||
const targetSeries = option.series[0];
|
||||
let pointIndex = -1;
|
||||
let currentPoint = null;
|
||||
|
||||
// 1. 先恢复上次修改的点的样式
|
||||
if (lastModifiedPoint) {
|
||||
targetSeries.data.forEach((item) => {
|
||||
if (item.name === lastModifiedPoint.name) {
|
||||
// 恢复原始颜色和大小
|
||||
item.itemStyle.color = lastModifiedPoint.oldColor;
|
||||
item.symbolSize = lastModifiedPoint.oldSize;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 2. 查找当前目标点并记录原始样式
|
||||
targetSeries.data.forEach((item, index) => {
|
||||
if (item.name === targetName) {
|
||||
pointIndex = index;
|
||||
currentPoint = item;
|
||||
// 记录当前点的原始样式(用于下次恢复)
|
||||
lastModifiedPoint = {
|
||||
name: item.name,
|
||||
oldColor: "#00FF00", // 默认为原始绿色
|
||||
oldSize: 15, // 默认为原始大小15
|
||||
};
|
||||
// 修改为新样式
|
||||
item.itemStyle = { ...item.itemStyle, color: "#ff0000" };
|
||||
item.symbolSize = 25;
|
||||
}
|
||||
});
|
||||
|
||||
// 3. 更新图表并显示提示框
|
||||
if (pointIndex !== -1 && currentPoint) {
|
||||
chartInstance.setOption(option);
|
||||
|
||||
// 关闭之前的提示框(避免重叠)
|
||||
chartInstance.dispatchAction({ type: "hideTip" });
|
||||
|
||||
// 显示当前点的提示框
|
||||
setTimeout(() => {
|
||||
chartInstance.dispatchAction({
|
||||
type: "showTip",
|
||||
seriesIndex: 0,
|
||||
dataIndex: pointIndex,
|
||||
});
|
||||
}, 50);
|
||||
} else {
|
||||
// 如果未找到目标点,清空上次记录
|
||||
// lastModifiedPoint = null;
|
||||
// console.warn(`未找到名称为${targetName}的点`);
|
||||
// 如果未找到目标点,恢复所有点的样式并清空记录
|
||||
targetSeries.data.forEach((item) => {
|
||||
// 恢复默认样式
|
||||
item.itemStyle = { ...item.itemStyle, color: "#00FF00" };
|
||||
item.symbolSize = 15;
|
||||
});
|
||||
// 清空上次修改记录
|
||||
lastModifiedPoint = null;
|
||||
// 隐藏提示框
|
||||
chartInstance.dispatchAction({ type: "hideTip" });
|
||||
// 更新图表
|
||||
chartInstance.setOption(option);
|
||||
console.warn(`未找到名称为${targetName}的点,已恢复所有点原始样式`);
|
||||
}
|
||||
}
|
||||
|
||||
const resizeHandler = () => {
|
||||
myCharts.value?.resize();
|
||||
myCharts.value?.setOption(myCharts.value.getOption()); // 强制更新配置
|
||||
};
|
||||
onBeforeUnmount(() => {
|
||||
window.removeEventListener("resize", resizeHandler);
|
||||
myCharts.value?.dispose();
|
||||
});
|
||||
defineExpose({ GetEchar, setIcon });
|
||||
watch(
|
||||
() => props.options,
|
||||
(newVal, oldVal) => {
|
||||
// GetEchar('中国')
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.bars_w {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.iconfont {
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
left: 10px;
|
||||
z-index: 2;
|
||||
font-size: 20px;
|
||||
color: var(--el-color-primary) !important;
|
||||
}
|
||||
</style>
|
||||
671
src/components/datePicker/index.vue
Normal file
@@ -0,0 +1,671 @@
|
||||
<template>
|
||||
<div style="width: 540px">
|
||||
<el-select
|
||||
v-model="interval"
|
||||
style="min-width: 70px; width: 70px; margin-right: 10px"
|
||||
size="small"
|
||||
@change="timeChange"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in timeOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
|
||||
<el-date-picker
|
||||
v-model="timeValue"
|
||||
size="small"
|
||||
type="daterange"
|
||||
:disabled="disabledPicker"
|
||||
:disabled-date="isFutureDate"
|
||||
style="width: 190px; margin-right: 10px"
|
||||
unlink-panels
|
||||
:clearable="false"
|
||||
range-separator="至"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
value-format="YYYY-MM-DD"
|
||||
:shortcuts="shortcuts"
|
||||
/>
|
||||
<el-button
|
||||
:disabled="backDisabled"
|
||||
type="primary"
|
||||
size="small"
|
||||
:icon="DArrowLeft"
|
||||
@click="preClick"
|
||||
></el-button>
|
||||
<el-button type="primary" :icon="VideoPause" @click="nowTime" size="small"
|
||||
>当前</el-button
|
||||
>
|
||||
<el-button
|
||||
:disabled="preDisabled"
|
||||
type="primary"
|
||||
size="small"
|
||||
:icon="DArrowRight"
|
||||
@click="next"
|
||||
></el-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { DArrowLeft, VideoPause, DArrowRight } from "@element-plus/icons-vue";
|
||||
import { ref, onMounted, nextTick, watch } from "vue";
|
||||
import { useStore } from "vuex";
|
||||
const store = useStore();
|
||||
const emit = defineEmits(["timeChangeInfo"]);
|
||||
interface Props {
|
||||
nextFlag?: boolean;
|
||||
theCurrentTime?: boolean;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
nextFlag: false,
|
||||
theCurrentTime: true,
|
||||
});
|
||||
|
||||
const interval = ref(3);
|
||||
const timeFlag = ref(1);
|
||||
const count = ref(0);
|
||||
const disabledPicker = ref(true);
|
||||
const timeValue = ref([]);
|
||||
const backDisabled = ref(false);
|
||||
const preDisabled = ref(true);
|
||||
const timeOptions: any = ref([
|
||||
{ label: "年份", value: 1 },
|
||||
{ label: "季度", value: 2 },
|
||||
{ label: "月份", value: 3 },
|
||||
{ label: "周", value: 4 },
|
||||
{ label: "自定义", value: 5 },
|
||||
]);
|
||||
const shortcuts = [
|
||||
{
|
||||
text: "最近一周",
|
||||
value: () => {
|
||||
const end = new Date();
|
||||
const start = new Date();
|
||||
start.setTime(start.getTime() - 3600 * 1000 * 24 * 7);
|
||||
return [start, end];
|
||||
},
|
||||
},
|
||||
{
|
||||
text: "最近一个月",
|
||||
value: () => {
|
||||
const end = new Date();
|
||||
const start = new Date();
|
||||
start.setTime(start.getTime() - 3600 * 1000 * 24 * 30);
|
||||
return [start, end];
|
||||
},
|
||||
},
|
||||
{
|
||||
text: "最近3个月",
|
||||
value: () => {
|
||||
const end = new Date();
|
||||
const start = new Date();
|
||||
start.setTime(start.getTime() - 3600 * 1000 * 24 * 90);
|
||||
return [start, end];
|
||||
},
|
||||
},
|
||||
];
|
||||
const isFutureDate = (time: any) => {
|
||||
return time && time > Date.now();
|
||||
};
|
||||
onMounted(() => {
|
||||
timeChange(3);
|
||||
});
|
||||
// 选择时间范围
|
||||
const timeChange = (e: number) => {
|
||||
backDisabled.value = false;
|
||||
preDisabled.value = props.nextFlag ? false : true;
|
||||
count.value = 0;
|
||||
if (e == 1) {
|
||||
disabledPicker.value = true;
|
||||
|
||||
timeValue.value = [setTime(1), setTime()];
|
||||
} else if (e == 2) {
|
||||
disabledPicker.value = true;
|
||||
timeValue.value = [setTime(2), setTime()];
|
||||
} else if (e == 3) {
|
||||
disabledPicker.value = true;
|
||||
timeValue.value = [setTime(3), setTime()];
|
||||
} else if (e == 4) {
|
||||
let year = parseInt(setTime().substring(0, 4));
|
||||
let month = parseInt(setTime().substring(5, 7));
|
||||
let date = parseInt(setTime().substring(8, 10));
|
||||
|
||||
var start = new Date(year, month - 1, date);
|
||||
var dayOfWeek = start.getDay() == 0 ? 7 : start.getDay() - 1; // 如果为周日,则置为7天
|
||||
|
||||
disabledPicker.value = true;
|
||||
timeValue.value = [setTime(0, dayOfWeek), setTime(0, -6 + dayOfWeek)];
|
||||
} else if (e == 5) {
|
||||
disabledPicker.value = false;
|
||||
backDisabled.value = true;
|
||||
preDisabled.value = props.nextFlag ? false : true;
|
||||
timeValue.value = [setTime(), setTime()];
|
||||
}
|
||||
if (e == 1 || e == 2) {
|
||||
timeFlag.value = 0;
|
||||
} else {
|
||||
timeFlag.value = 1;
|
||||
}
|
||||
};
|
||||
|
||||
// 当前
|
||||
const nowTime = () => {
|
||||
console.log(interval.value, "000000000");
|
||||
timeChange(interval.value);
|
||||
};
|
||||
// 上一个
|
||||
const preClick = () => {
|
||||
preDisabled.value = false;
|
||||
let startTime = timeValue.value[0];
|
||||
let endTime = timeValue.value[1];
|
||||
let year = parseInt(startTime.substring(0, 4));
|
||||
let month = parseInt(startTime.substring(5, 7));
|
||||
let date = parseInt(startTime.substring(8, 10));
|
||||
//按月
|
||||
if (interval.value == 3) {
|
||||
// 换年份
|
||||
if (month == 1) {
|
||||
year = year - 1;
|
||||
startTime = year + "-12-01";
|
||||
endTime = year + "-12-31";
|
||||
} else if (month <= 10) {
|
||||
month = month - 1;
|
||||
startTime = year + "-0" + month + "-01";
|
||||
let day = getDays(year, month);
|
||||
endTime = year + "-0" + month + "-" + day;
|
||||
} else {
|
||||
month = month - 1;
|
||||
startTime = year + "-" + month + "-01";
|
||||
let day = getDays(year, month);
|
||||
endTime = year + "-" + month + "-" + day;
|
||||
}
|
||||
//按周
|
||||
} else if (interval.value == 4) {
|
||||
//根据开始时间推
|
||||
let start = new Date(year, month - 1, date);
|
||||
start.setDate(start.getDate() - 7);
|
||||
startTime = formatTime(start);
|
||||
var end = new Date(start);
|
||||
end.setDate(start.getDate() + 6);
|
||||
endTime = formatTime(end);
|
||||
//按季度
|
||||
} else if (interval.value == 2) {
|
||||
// 换年份
|
||||
if (month == 1) {
|
||||
year = year - 1;
|
||||
startTime = year + "-10-01";
|
||||
endTime = year + "-12-31";
|
||||
} else {
|
||||
// 还是本年
|
||||
month = month - 3;
|
||||
startTime = year + "-0" + month + "-01";
|
||||
month = month + 2;
|
||||
var day = getDays(year, month);
|
||||
endTime = year + "-0" + month + "-" + day;
|
||||
}
|
||||
//自定义
|
||||
} else if (interval.value == 1) {
|
||||
year = year - 1;
|
||||
startTime = year + "-01-01";
|
||||
endTime = year + "-12-31";
|
||||
}
|
||||
timeValue.value = [startTime, endTime];
|
||||
|
||||
// 判断向后键的状态
|
||||
// var temp = NowgetEndTime()
|
||||
// timeStatus(temp, endTime)
|
||||
};
|
||||
//下一个
|
||||
const next = () => {
|
||||
//向后
|
||||
let startTime = timeValue.value[0];
|
||||
let endTime = timeValue.value[1];
|
||||
let year = parseInt(startTime.substring(0, 4));
|
||||
let month = parseInt(startTime.substring(5, 7));
|
||||
let date = parseInt(startTime.substring(8, 10));
|
||||
var now = new Date();
|
||||
// 获取当前年份
|
||||
var presentY = now.getFullYear();
|
||||
// 获取当前月份
|
||||
var presentM = now.getMonth() + 1;
|
||||
// 获取当前日期
|
||||
var presentD = now.getDate();
|
||||
if (interval.value == 3) {
|
||||
if (month == 12) {
|
||||
year = year + 1;
|
||||
|
||||
// 年份进位后,大于当前的年份,是不科学的
|
||||
if (presentY < year && !props.nextFlag) {
|
||||
startTime = presentY + "-12-01";
|
||||
if (presentD < 10) {
|
||||
endTime = presentY + "-12" + "-0" + presentD;
|
||||
} else {
|
||||
endTime = presentY + "-12" + "-" + presentD;
|
||||
}
|
||||
// 年份进位后,等于当前的年份
|
||||
} else if (presentY == year) {
|
||||
startTime = year + "-01-01";
|
||||
if (presentM > 1) {
|
||||
endTime = year + "-01-31";
|
||||
} else {
|
||||
if (presentD < 10) {
|
||||
endTime = year + "-01" + "-0" + presentD;
|
||||
} else {
|
||||
endTime = year + "-01" + "-" + presentD;
|
||||
}
|
||||
}
|
||||
// 年份进位后,依旧小于当前的年份
|
||||
} else {
|
||||
startTime = year + "-01-01";
|
||||
endTime = year + "-01-31";
|
||||
}
|
||||
} else {
|
||||
month = month + 1;
|
||||
// 年份等于当前年份
|
||||
if (presentY == year) {
|
||||
// 月份超过当前月份,是不科学的
|
||||
if (month >= presentM && !props.nextFlag) {
|
||||
if (presentM < 10) {
|
||||
startTime = year + "-0" + presentM + "-01";
|
||||
if (presentD < 10) {
|
||||
endTime = year + "-0" + presentM + "-0" + presentD;
|
||||
} else {
|
||||
endTime = year + "-0" + presentM + "-" + presentD;
|
||||
}
|
||||
} else {
|
||||
startTime = year + "-" + presentM + "-01";
|
||||
if (presentD < 10) {
|
||||
endTime = year + "-" + presentM + "-0" + presentD;
|
||||
} else {
|
||||
endTime = year + "-" + presentM + "-" + presentD;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (month < 10) {
|
||||
startTime = year + "-0" + month + "-01";
|
||||
var day = getDays(year, month);
|
||||
endTime = year + "-0" + month + "-" + day;
|
||||
} else {
|
||||
startTime = year + "-" + month + "-01";
|
||||
var day = getDays(year, month);
|
||||
endTime = year + "-" + month + "-" + day;
|
||||
}
|
||||
}
|
||||
// 年份小于当前的年份
|
||||
} else {
|
||||
if (month < 10) {
|
||||
startTime = year + "-0" + month + "-01";
|
||||
var day = getDays(year, month);
|
||||
endTime = year + "-0" + month + "-" + day;
|
||||
} else {
|
||||
startTime = year + "-" + month + "-01";
|
||||
var day = getDays(year, month);
|
||||
endTime = year + "-" + month + "-" + day;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (interval.value == 2) {
|
||||
// 前进需要年份进位
|
||||
if (month == 10) {
|
||||
year = year + 1;
|
||||
// 年份进位后大于当前年份是不科学的
|
||||
if (year > presentY && !props.nextFlag) {
|
||||
startTime = presentY + "-10-01";
|
||||
if (presentD < 10) {
|
||||
endTime = year + "-" + presentM + "-0" + presentD;
|
||||
} else {
|
||||
endTime = year + "-" + presentM + "-" + presentD;
|
||||
}
|
||||
} else if (year == presentY) {
|
||||
startTime = year + "-01-01";
|
||||
// 当前月份大约3月份
|
||||
if (presentM > 3) {
|
||||
endTime = year + "-03-31";
|
||||
} else {
|
||||
// 当前月份也在第一季度里
|
||||
if (presentD < 10) {
|
||||
endTime = year + "-0" + presentM + "-0" + presentD;
|
||||
} else {
|
||||
endTime = year + "-0" + presentM + "-" + presentD;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
startTime = year + "-01-01";
|
||||
endTime = year + "-03-31";
|
||||
}
|
||||
} else {
|
||||
month = month + 3;
|
||||
console.log("🚀 ~ next ~ presentM:", presentM, month);
|
||||
|
||||
// 季度进位后,超过当前月份是不科学的
|
||||
if (year == presentY && !props.nextFlag) {
|
||||
if (month >= presentM) {
|
||||
// 当季度进位后大于当前月,以当前月的时间显示季度
|
||||
if (presentM > 0 && presentM < 4) {
|
||||
// 第一季度
|
||||
startTime = year + "-01-01";
|
||||
if (presentD < 10) {
|
||||
endTime = year + "-0" + presentM + "-0" + presentD;
|
||||
} else {
|
||||
endTime = year + "-0" + presentM + "-" + presentD;
|
||||
}
|
||||
} else if (presentM > 3 && presentM < 7) {
|
||||
console.log(123123);
|
||||
// 第二季度
|
||||
startTime = year + "-04-01";
|
||||
if (presentD < 10) {
|
||||
endTime = year + "-0" + presentM + "-0" + presentD;
|
||||
} else {
|
||||
endTime = year + "-0" + presentM + "-" + presentD;
|
||||
}
|
||||
} else if (presentM > 6 && presentM < 10) {
|
||||
// 第三季度
|
||||
startTime = year + "-07-01";
|
||||
if (presentD < 10) {
|
||||
endTime = year + "-0" + presentM + "-0" + presentD;
|
||||
} else {
|
||||
endTime = year + "-0" + presentM + "-" + presentD;
|
||||
}
|
||||
} else {
|
||||
// 第四季度
|
||||
startTime = year + "-10-01";
|
||||
if (presentD < 10) {
|
||||
endTime = year + "-" + presentM + "-0" + presentD;
|
||||
} else {
|
||||
endTime = year + "-" + presentM + "-" + presentD;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (month == 10) {
|
||||
startTime = year + "-" + month + "-01";
|
||||
} else {
|
||||
startTime = year + "-0" + month + "-01";
|
||||
}
|
||||
month = month + 2;
|
||||
if (month >= presentM) {
|
||||
endTime = NowgetEndTime();
|
||||
} else {
|
||||
var day = getDays(year, month);
|
||||
endTime = year + "-0" + month + "-" + day;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (month == 10) {
|
||||
startTime = year + "-" + month + "-01";
|
||||
month = month + 2;
|
||||
var day = getDays(year, month);
|
||||
endTime = year + "-" + month + "-" + day;
|
||||
} else {
|
||||
startTime = year + "-0" + month + "-01";
|
||||
month = month + 2;
|
||||
var day = getDays(year, month);
|
||||
endTime = year + "-0" + month + "-" + day;
|
||||
}
|
||||
}
|
||||
}
|
||||
console.log(startTime, endTime);
|
||||
} else if (interval.value == 5) {
|
||||
} else if (interval.value == 4) {
|
||||
//根据开始时间推
|
||||
var start = new Date(year, month - 1, date);
|
||||
start.setDate(start.getDate() + 7);
|
||||
startTime = formatTime(start);
|
||||
var end = new Date(start);
|
||||
end.setDate(start.getDate() + 6);
|
||||
endTime = formatTime(end);
|
||||
} else {
|
||||
year = year + 1;
|
||||
// 年份进位后大于当前年份,是不科学的
|
||||
if (year >= presentY && !props.nextFlag) {
|
||||
startTime = presentY + "-01-01";
|
||||
if (presentM < 10) {
|
||||
if (presentD < 10) {
|
||||
endTime = presentY + "-0" + presentM + "-0" + presentD;
|
||||
} else {
|
||||
endTime = presentY + "-0" + presentM + "-" + presentD;
|
||||
}
|
||||
} else {
|
||||
if (presentD < 10) {
|
||||
endTime = presentY + "-" + presentM + "-0" + presentD;
|
||||
} else {
|
||||
endTime = presentY + "-" + presentM + "-" + presentD;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
startTime = year + "-01-01";
|
||||
endTime = year + "-12-31";
|
||||
}
|
||||
}
|
||||
if (!props.nextFlag) {
|
||||
if (
|
||||
new Date(endTime + " 00:00:00").getTime() >=
|
||||
new Date(
|
||||
(window as any).XEUtils.toDateString(new Date(), "yyyy-MM-dd ") +
|
||||
" 00:00:00"
|
||||
).getTime()
|
||||
) {
|
||||
preDisabled.value = props.nextFlag ? false : true;
|
||||
}
|
||||
}
|
||||
|
||||
timeValue.value = [startTime, endTime];
|
||||
};
|
||||
|
||||
const setTime = (flag = 0, e = 0) => {
|
||||
let dd = (window as any).XEUtils.toDateString(
|
||||
new Date().getTime() - e * 3600 * 1000 * 24,
|
||||
"dd"
|
||||
);
|
||||
|
||||
let data = "";
|
||||
|
||||
if ((dd < 4 || dd == 0) && interval.value != 4 && !props.theCurrentTime) {
|
||||
data = (window as any).XEUtils.toDateString(
|
||||
new Date().getTime() - (e + dd) * 3600 * 1000 * 24,
|
||||
"yyyy-MM-dd"
|
||||
);
|
||||
} else {
|
||||
data = (window as any).XEUtils.toDateString(
|
||||
new Date().getTime() - e * 3600 * 1000 * 24,
|
||||
"yyyy-MM-dd"
|
||||
);
|
||||
}
|
||||
|
||||
if (flag == 1) {
|
||||
data = data.slice(0, 5) + "01-01";
|
||||
} else if (flag == 2) {
|
||||
let quarter = parseInt(data.slice(5, 7));
|
||||
if (0 < quarter && quarter <= 3) {
|
||||
data = data.slice(0, 5) + "01-01";
|
||||
} else if (3 < quarter && quarter <= 6) {
|
||||
data = data.slice(0, 5) + "04-01";
|
||||
} else if (6 < quarter && quarter <= 9) {
|
||||
data = data.slice(0, 5) + "07-01";
|
||||
} else {
|
||||
data = data.slice(0, 5) + "10-01";
|
||||
}
|
||||
}
|
||||
if (flag == 3) {
|
||||
data = data.slice(0, 8) + "01";
|
||||
}
|
||||
|
||||
return data;
|
||||
};
|
||||
|
||||
// 获取月份的天数
|
||||
const getDays = (year: any, month: any) => {
|
||||
let max = new Date(year, month, 0).getDate();
|
||||
return max;
|
||||
};
|
||||
// 时间格式化
|
||||
const formatTime = (time: any) => {
|
||||
return (
|
||||
time.getFullYear() +
|
||||
"-" +
|
||||
(time.getMonth() + 1 < 10 ? "0" : "") +
|
||||
(time.getMonth() + 1) +
|
||||
"-" +
|
||||
(time.getDate() < 10 ? "0" : "") +
|
||||
time.getDate()
|
||||
);
|
||||
};
|
||||
const NowgetEndTime = () => {
|
||||
let now = new Date();
|
||||
let sep = "-";
|
||||
let year = now.getFullYear();
|
||||
let month: any = now.getMonth() + 1;
|
||||
if (month < 10) {
|
||||
month = "0" + month;
|
||||
}
|
||||
let date: any = now.getDate();
|
||||
if (date < 10) {
|
||||
date = "0" + date;
|
||||
}
|
||||
|
||||
// 拼接当前的日期
|
||||
let endTime = year + sep + month + sep + date;
|
||||
return endTime;
|
||||
};
|
||||
const setTimeOptions = (list: any) => {
|
||||
timeOptions.value = list;
|
||||
};
|
||||
const setTheDate = (value: any) => {
|
||||
interval.value = value;
|
||||
timeChange(value);
|
||||
};
|
||||
|
||||
// 获取时间范围的同比
|
||||
function getYearOnYear(startDate: string, endDate: string): [string, string] {
|
||||
const startYearAgo = new Date(startDate);
|
||||
startYearAgo.setFullYear(startYearAgo.getFullYear() - 1);
|
||||
|
||||
const endYearAgo = new Date(endDate);
|
||||
endYearAgo.setFullYear(endYearAgo.getFullYear() - 1);
|
||||
|
||||
return [formatDate(startYearAgo), formatDate(endYearAgo)];
|
||||
}
|
||||
|
||||
// 获取时间范围的环比
|
||||
function getMonthOnMonth(startDate: string, endDate: string): [string, string] {
|
||||
const start = new Date(startDate);
|
||||
const end = new Date(endDate);
|
||||
|
||||
const diffTime = end.getTime() - start.getTime() + 60 * 60 * 24 * 1000; // 计算时间差
|
||||
const startMonthAgo = new Date(start);
|
||||
startMonthAgo.setTime(startMonthAgo.getTime() - diffTime); // 将开始时间向前推移相同的时间差
|
||||
|
||||
const endMonthAgo = new Date(start);
|
||||
endMonthAgo.setDate(start.getDate() - 1); // 结束时间是开始时间的前一天
|
||||
|
||||
return [formatDate(startMonthAgo), formatDate(endMonthAgo)];
|
||||
}
|
||||
|
||||
// 格式化日期为 YYYY-MM-DD
|
||||
function formatDate(date: Date): string {
|
||||
const year = date.getFullYear();
|
||||
const month = String(date.getMonth() + 1).padStart(2, "0");
|
||||
const day = String(date.getDate()).padStart(2, "0");
|
||||
return `${year}-${month}-${day}`;
|
||||
}
|
||||
|
||||
watch(timeValue, async (newVal) => {
|
||||
await store.dispatch("setTimeType", { type: interval, value: newVal });
|
||||
await emit("timeChangeInfo");
|
||||
});
|
||||
|
||||
defineExpose({
|
||||
timeValue,
|
||||
interval,
|
||||
timeFlag,
|
||||
setTimeOptions,
|
||||
setTheDate,
|
||||
getYearOnYear,
|
||||
getMonthOnMonth,
|
||||
timeChange,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.demo-date-picker {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.demo-date-picker .block {
|
||||
padding: 30px 0;
|
||||
text-align: center;
|
||||
border-right: solid 1px var(--el-border-color);
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.demo-date-picker .block:last-child {
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
.demo-date-picker .demonstration {
|
||||
display: block;
|
||||
color: var(--el-text-color-secondary);
|
||||
font-size: 14px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
:deep(.el-select__wrapper) {
|
||||
background-color: #ffffff00;
|
||||
box-shadow: 0 0 0 1px var(--el-color-primary) inset;
|
||||
}
|
||||
:deep(.el-select__placeholder) {
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
:deep(.el-range-editor.is-disabled) {
|
||||
background-color: #ffffff00;
|
||||
|
||||
input {
|
||||
background-color: #ffffff00;
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
}
|
||||
:deep(.el-range-editor.is-disabled input) {
|
||||
background-color: #ffffff00;
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
:deep(.el-.el-date-editor .el-range-input) {
|
||||
--el-input-border-color: var(--el-color-primary);
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
:deep(.el-input__wrapper) {
|
||||
background-color: #ffffff00;
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
:deep(.el-range-editor.is-disabled .el-range-separator) {
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
:deep(.el-date-editor .el-range-separator) {
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
:deep(.el-date-editor .el-range__icon) {
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
:deep(.el-date-editor .el-range-input) {
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
:deep(.el-date-editor.el-input__wrapper) {
|
||||
box-shadow: 0 0 0 1px var(--el-color-primary) inset;
|
||||
}
|
||||
:deep(.el-select__caret) {
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
:deep(.el-button--primary) {
|
||||
--el-button-bg-color: #0a73ff00;
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
:deep(.el-button.is-disabled) {
|
||||
--el-button-bg-color: #0a73ff00;
|
||||
background-color: rgb(160 206 255 / 17%);
|
||||
}
|
||||
</style>
|
||||
278
src/components/echartMap3D.vue
Normal file
@@ -0,0 +1,278 @@
|
||||
<!-- 地图组件 -->
|
||||
<template>
|
||||
<div style="position: relative">
|
||||
<div class="bars_w" ref="chartMap" id="chartMap"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onBeforeUnmount, ref, watch, onMounted, defineEmits } from "vue";
|
||||
import * as echarts from "echarts";
|
||||
import "echarts-gl";
|
||||
// import "echarts/lib/component/dataZoom";
|
||||
const props = defineProps(["options"]);
|
||||
const myCharts = ref();
|
||||
|
||||
const fetchConfig = async (name: string) => {
|
||||
const res = await import(`./mapJson/${name}.json`);
|
||||
return res.default;
|
||||
// GetEchar(res.default)
|
||||
};
|
||||
// fetchConfig()
|
||||
const areaName = ref("");
|
||||
const emit = defineEmits(["clickMap"]);
|
||||
onMounted(() => {});
|
||||
|
||||
const GetEchar = async (name: string) => {
|
||||
let chartDom = document.getElementById("chartMap");
|
||||
myCharts.value?.dispose();
|
||||
myCharts.value = echarts.init(chartDom);
|
||||
|
||||
echarts.registerMap(name, await fetchConfig(name)); //注册可用的地图
|
||||
let option = {
|
||||
title: {
|
||||
left: "center",
|
||||
top: "3%",
|
||||
...props.options.title,
|
||||
},
|
||||
tooltip: {
|
||||
trigger: "item",
|
||||
axisPointer: {
|
||||
type: "shadow",
|
||||
label: {
|
||||
color: "#fff",
|
||||
fontSize: 16,
|
||||
},
|
||||
},
|
||||
textStyle: {
|
||||
color: "#fff",
|
||||
fontStyle: "normal",
|
||||
opacity: 0.35,
|
||||
fontSize: 14,
|
||||
},
|
||||
backgroundColor: "rgba(0,0,0,0.55)",
|
||||
...(props.options.tooltip || null),
|
||||
},
|
||||
|
||||
color: [
|
||||
...(props.options.color || ""),
|
||||
"#07CCCA ",
|
||||
"#00BFF5",
|
||||
"#FFBF00",
|
||||
"#77DA63",
|
||||
"#D5FF6B",
|
||||
"#Ff6600",
|
||||
"#FF9100",
|
||||
"#5B6E96",
|
||||
"#66FFCC",
|
||||
"#B3B3B3",
|
||||
],
|
||||
|
||||
geo3D: {
|
||||
show: false,
|
||||
map: "北京",
|
||||
|
||||
viewControl: {
|
||||
autoRotate: false, // 关闭自动旋转
|
||||
center: [0, -10, 0], //位置
|
||||
beta: 45, //x轴旋转
|
||||
alpha: 50, //Y轴旋转
|
||||
},
|
||||
light: {
|
||||
main: {
|
||||
color: "#ffffff",
|
||||
intensity: 1,
|
||||
shadow: false,
|
||||
},
|
||||
},
|
||||
itemStyle: {
|
||||
color: "#4D96FA",
|
||||
borderWidth: 1,
|
||||
borderColor: "#fff",
|
||||
opcity: 1,
|
||||
},
|
||||
|
||||
shading: "realistic", //简化着色模式('color' 比 'realistic' 性能更好)
|
||||
label: {
|
||||
show: true,
|
||||
position: "bottom",
|
||||
distance: 0, // 紧贴地面
|
||||
color: "#fff",
|
||||
},
|
||||
emphasis: {
|
||||
label: {
|
||||
formatter: function (params: any) {
|
||||
areaName.value = params.name;
|
||||
return params.name;
|
||||
},
|
||||
show: true,
|
||||
textStyle: {
|
||||
color: "orange",
|
||||
fontSize: 18,
|
||||
backgroundColor: "rgba(0,23,11,0)",
|
||||
},
|
||||
},
|
||||
},
|
||||
postEffect: {
|
||||
enable: false, // 关闭后期特效(抗锯齿、 Bloom 等)
|
||||
},
|
||||
// 减少顶点计算
|
||||
polygonOffset: {
|
||||
factor: 1,
|
||||
units: 1,
|
||||
},
|
||||
// 启用 GPU 加速渲染
|
||||
renderOption: {
|
||||
useGPU: true, // 启用 GPU 渲染
|
||||
maxCache: 1000, // 缓存顶点数据
|
||||
vertexLimit: 50000, // 限制顶点数量
|
||||
},
|
||||
// 优化材质计算
|
||||
material: {
|
||||
color: "#4D96FA",
|
||||
opacity: 0.8,
|
||||
roughness: 0.2, // 粗糙度(数值越低性能越好)
|
||||
metalness: 0, // 金属度
|
||||
},
|
||||
|
||||
groundPlane: true,
|
||||
// ...(props.options.geo3Ds || null),
|
||||
// data: originalDatas.datas,
|
||||
// 将geo3d放在最底层
|
||||
},
|
||||
|
||||
...props.options.options,
|
||||
};
|
||||
if (props.options.visualMap) {
|
||||
option.visualMap = props.options.visualMap;
|
||||
}
|
||||
// if (props.options.geo3D) {
|
||||
// option.geo = null;
|
||||
// option.geo3D = {
|
||||
// map: name,
|
||||
// ...(props.options.geo3D || null),
|
||||
// };
|
||||
// }
|
||||
|
||||
myCharts.value.setOption(option);
|
||||
// window.addEventListener("resize", resizeHandler);
|
||||
const inquireTimer: any = ref(null);
|
||||
// 点击事件
|
||||
myCharts.value.off("click");
|
||||
let selectedPoint = null;
|
||||
myCharts.value.on("click", (params: any) => {
|
||||
console.log("系列索引:", params.seriesIndex); // 应为 1(监测点系列)
|
||||
console.log("数据索引:", params.dataIndex); // 应为 0、1、2...(对应点1、点2...)
|
||||
// console.log("🚀 ~ myCharts.value.getZr ~ e:", e);
|
||||
// // 清除上一次的定时器
|
||||
// if (inquireTimer.value) {
|
||||
// clearTimeout(inquireTimer.value);
|
||||
// }
|
||||
// inquireTimer.value = setTimeout(async () => {
|
||||
// emit("clickMap", e.name);
|
||||
// inquireTimer.value = null;
|
||||
// }, 300);
|
||||
});
|
||||
// myCharts.value.getZr().on("click", (e: any) => {
|
||||
// console.log("🚀 ~ myCharts.value.getZr ~ e:", e);
|
||||
// // 清除上一次的定时器
|
||||
// if (inquireTimer.value) {
|
||||
// clearTimeout(inquireTimer.value);
|
||||
// }
|
||||
// inquireTimer.value = setTimeout(async () => {
|
||||
// emit("clickMap", areaName.value);
|
||||
// inquireTimer.value = null;
|
||||
// }, 300);
|
||||
// });
|
||||
};
|
||||
|
||||
/**
|
||||
* 外部触发:通过 name 修改指定图标的颜色并显示 tooltip
|
||||
|
||||
* @param {string} targetName - 目标点的 name(如 "点1")
|
||||
* @param {string} newColor - 新颜色(默认红色 "#ff0000")
|
||||
*/
|
||||
function setIcon(targetName, newColor = "#ff0000") {
|
||||
console.log("🚀 ~ setIcon ~ targetName:", targetName);
|
||||
// 获取当前图表配置(基于您的 echartMapList.value.options)
|
||||
const currentOptions = props.options.options;
|
||||
// 找到 "监测点" 系列的索引(固定为 series 中的第1项,因您的配置中 map3D 是第0项)
|
||||
const monitorSeriesIndex = currentOptions.series.findIndex(
|
||||
(series) => series.name === "监测点"
|
||||
);
|
||||
|
||||
if (monitorSeriesIndex === -1) {
|
||||
console.warn("未找到 '监测点' 系列");
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取监测点数据列表
|
||||
const monitorData = currentOptions.series[monitorSeriesIndex].data;
|
||||
// 找到 name 匹配的点的索引
|
||||
const targetDataIndex = monitorData.findIndex(
|
||||
(item) => item.name === targetName
|
||||
);
|
||||
|
||||
if (targetDataIndex === -1) {
|
||||
console.warn(`未找到名称为 ${targetName} 的点`);
|
||||
return;
|
||||
}
|
||||
|
||||
// 1. 重置所有点的颜色(恢复默认蓝色,避免多个点同时变色)
|
||||
monitorData.forEach((item, index) => {
|
||||
item.itemStyle.color = "#0000ff";
|
||||
item.symbolSize = 17;
|
||||
});
|
||||
|
||||
// 2. 修改目标点的颜色
|
||||
monitorData[targetDataIndex].itemStyle.color = newColor;
|
||||
monitorData[targetDataIndex].symbolSize = 25;
|
||||
|
||||
// 3. 更新图表配置,使颜色生效
|
||||
myCharts.value.setOption(currentOptions);
|
||||
|
||||
myCharts.value.dispatchAction({
|
||||
type: "showTip",
|
||||
seriesIndex: monitorSeriesIndex,
|
||||
dataIndex: targetDataIndex,
|
||||
|
||||
// 可选:强制指定基于鼠标的位置(如果百分比位置无效)
|
||||
position: ["50%", "50%"],
|
||||
});
|
||||
|
||||
console.log(`已通过外部触发修改 ${targetName} 的颜色为 ${newColor}`);
|
||||
}
|
||||
|
||||
const resizeHandler = () => {
|
||||
myCharts.value?.resize();
|
||||
myCharts.value?.setOption(myCharts.value.getOption()); // 强制更新配置
|
||||
};
|
||||
onBeforeUnmount(() => {
|
||||
// window.removeEventListener("resize", resizeHandler);
|
||||
myCharts.value?.dispose();
|
||||
});
|
||||
defineExpose({ GetEchar, setIcon });
|
||||
watch(
|
||||
() => props.options,
|
||||
(newVal, oldVal) => {
|
||||
// GetEchar('中国')
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.bars_w {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.iconfont {
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
left: 10px;
|
||||
z-index: 2;
|
||||
font-size: 20px;
|
||||
color: var(--el-color-primary) !important;
|
||||
}
|
||||
</style>
|
||||
37
src/components/echarts.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
// 引入 echarts 核心模块。
|
||||
import * as echarts from "echarts/core";
|
||||
|
||||
/** 引入柱状图and折线图图表,图表后缀都为 Chart */
|
||||
import { BarChart, LineChart } from "echarts/charts";
|
||||
|
||||
// 引入提示框,标题,直角坐标系,数据集,内置数据转换器组件
|
||||
import {
|
||||
TitleComponent,
|
||||
TooltipComponent,
|
||||
GridComponent,
|
||||
DatasetComponent,
|
||||
TransformComponent,
|
||||
} from "echarts/components";
|
||||
|
||||
// 标签自动布局,全局过渡动画等
|
||||
import { LabelLayout, UniversalTransition } from "echarts/features";
|
||||
|
||||
// 引入 Canvas 渲染器,注意引入 CanvasRenderer 或者 SVGRenderer 是必须的
|
||||
import { CanvasRenderer } from "echarts/renderers";
|
||||
|
||||
// 注册组件
|
||||
echarts.use([
|
||||
TitleComponent,
|
||||
TooltipComponent,
|
||||
GridComponent,
|
||||
DatasetComponent,
|
||||
TransformComponent,
|
||||
BarChart,
|
||||
LabelLayout,
|
||||
UniversalTransition,
|
||||
CanvasRenderer,
|
||||
LineChart,
|
||||
]);
|
||||
|
||||
// 导出
|
||||
export default echarts;
|
||||
324
src/components/echarts/MyEchart.vue
Normal file
@@ -0,0 +1,324 @@
|
||||
<template>
|
||||
<div class="chart">
|
||||
<div ref="chartRef" class="my-chart" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onBeforeUnmount, onMounted, ref, defineExpose, watch, nextTick } from 'vue'
|
||||
// import echarts from './echarts'
|
||||
import * as echarts from 'echarts' // 全引入
|
||||
import 'echarts-gl'
|
||||
import 'echarts-liquidfill'
|
||||
import 'echarts/lib/component/dataZoom'
|
||||
import { color, gradeColor3 } from './color'
|
||||
// import { useConfig } from '@/stores/config'
|
||||
// const config = useConfig()
|
||||
const emit = defineEmits(['triggerPoint', 'group'])
|
||||
// color[0] = config.layout.elementUiPrimary[0]
|
||||
color[0] = '#0a73ff'
|
||||
const chartRef = ref<HTMLDivElement>()
|
||||
|
||||
const props = defineProps(['options', 'isInterVal', 'pieInterVal'])
|
||||
let chart: echarts.ECharts | any = null
|
||||
const resizeHandler = () => {
|
||||
// 不在视野中的时候不进行resize
|
||||
if (!chartRef.value) return
|
||||
if (chartRef.value.offsetHeight == 0) return
|
||||
chart.getZr().painter.getViewportRoot().style.display = 'none'
|
||||
requestAnimationFrame(() => {
|
||||
chart.resize()
|
||||
chart.getZr().painter.getViewportRoot().style.display = ''
|
||||
})
|
||||
}
|
||||
const initChart = () => {
|
||||
if (!props.isInterVal && !props.pieInterVal) {
|
||||
chart?.dispose()
|
||||
}
|
||||
// chart?.dispose()
|
||||
chart = echarts.getInstanceByDom(chartRef.value as HTMLDivElement) || echarts.init(chartRef.value as HTMLDivElement)
|
||||
const options = {
|
||||
title: {
|
||||
left: 'center',
|
||||
textStyle: {
|
||||
color: '#fff',
|
||||
fontSize: 18,
|
||||
},
|
||||
...(props.options?.title || null)
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
// axisPointer: {
|
||||
// type: 'shadow',
|
||||
// label: {
|
||||
// color: '#fff',
|
||||
// fontSize: 16
|
||||
// }
|
||||
// },
|
||||
textStyle: {
|
||||
color: '#fff',
|
||||
fontStyle: 'normal',
|
||||
opacity: 0.35,
|
||||
fontSize: 14
|
||||
},
|
||||
backgroundColor: 'rgba(0,0,0,0.55)',
|
||||
borderWidth: 0,
|
||||
confine: true,
|
||||
formatter: function (params: any) {
|
||||
let tips = `<strong>${params[0]?.name}</strong></br>` // 标题加粗
|
||||
params?.forEach((item: any) => {
|
||||
const value =
|
||||
item.value === 3.14159 || item.value === 0.14159
|
||||
? '暂无数据'
|
||||
: Math.round(item.value * 100) / 100 // 处理特殊值
|
||||
tips += `<div style=" display: flex;justify-content: space-between;">
|
||||
<span>${item.marker}
|
||||
${item.seriesName}:
|
||||
</span> ${value}
|
||||
</div>` // 统一格式
|
||||
})
|
||||
return tips
|
||||
},
|
||||
...(props.options?.tooltip || null)
|
||||
},
|
||||
toolbox: {
|
||||
right: 20,
|
||||
top: 15,
|
||||
feature: {
|
||||
saveAsImage: {
|
||||
title: '', // 按钮标题
|
||||
icon: 'path://M892.342857 463.238095l-73.142857-68.266666-258.438095 258.438095V29.257143h-97.52381v624.152381L204.8 394.971429 131.657143 463.238095l380.342857 380.342857zM107.27619 897.219048h804.571429v97.523809H107.27619z' // 自定义图标路径
|
||||
},
|
||||
|
||||
...(props.options?.toolbox?.featureProps || null)
|
||||
},
|
||||
emphasis: {
|
||||
iconStyle: {
|
||||
// borderColor: config.layout.elementUiPrimary[0], // 鼠标悬停时的边框颜色
|
||||
// color: config.layout.elementUiPrimary[0] // 鼠标悬停时的图标颜色
|
||||
borderColor: '#002B6A', // 鼠标悬停时的边框颜色
|
||||
color: '#002B6A' // 鼠标悬停时的图标颜色
|
||||
}
|
||||
},
|
||||
// },
|
||||
...(props.options?.toolbox || null)
|
||||
},
|
||||
legend: {
|
||||
right: 50,
|
||||
top: 25,
|
||||
itemGap: 10,
|
||||
itemStyle: {},
|
||||
// textStyle: {
|
||||
fontSize: 12,
|
||||
padding: [2, 0, 0, 0], //[上、右、下、左]
|
||||
// },
|
||||
itemWidth: 15,
|
||||
itemHeight: 10,
|
||||
...(props.options?.legend || null)
|
||||
},
|
||||
grid: {
|
||||
top: '50px',
|
||||
left: '30px',
|
||||
right: '70px',
|
||||
bottom: props.options?.options?.dataZoom === null ? '10px' : '40px',
|
||||
containLabel: true,
|
||||
...(props.options?.grid || null)
|
||||
},
|
||||
xAxis: props.options?.xAxis ? handlerXAxis() : null,
|
||||
yAxis: props.options?.yAxis ? handlerYAxis() : null,
|
||||
dataZoom: props.options?.dataZoom || [
|
||||
{
|
||||
type: 'inside',
|
||||
height: 13,
|
||||
start: 0,
|
||||
bottom: '20px',
|
||||
end: 100
|
||||
},
|
||||
{
|
||||
start: 0,
|
||||
height: 13,
|
||||
bottom: '20px',
|
||||
end: 100
|
||||
}
|
||||
],
|
||||
color: props.options?.color || color,
|
||||
series: props.options?.series,
|
||||
...props.options?.options
|
||||
}
|
||||
// console.log(options.series,"获取x轴");
|
||||
handlerBar(options)
|
||||
// 处理柱状图
|
||||
chart.setOption(options, true)
|
||||
// chart.group = 'group'
|
||||
emit('group', chart, chartRef.value)
|
||||
// 添加点击事件
|
||||
chart.on('click', function (params) {
|
||||
if (params.seriesName == '暂态触发点') {
|
||||
emit('triggerPoint', params.data)
|
||||
}
|
||||
})
|
||||
|
||||
setTimeout(() => {
|
||||
chart.resize()
|
||||
}, 0)
|
||||
}
|
||||
const handlerBar = (options: any) => {
|
||||
if (Array.isArray(options.series)) {
|
||||
options.series.forEach((item: any) => {
|
||||
if (item.type === 'bar') {
|
||||
item.barMinHeight = 10
|
||||
item.barMaxWidth = 20
|
||||
item.itemStyle = Object.assign(
|
||||
{
|
||||
color: (params: any) => {
|
||||
if (params.value == 0 || params.value == 3.14159 || params.value == 0.14159) {
|
||||
return '#ccc'
|
||||
} else {
|
||||
return props.options?.color
|
||||
? props.options?.color[params.seriesIndex]
|
||||
: color[params.seriesIndex]
|
||||
}
|
||||
}
|
||||
},
|
||||
item.itemStyle
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
const handlerYAxis = () => {
|
||||
let temp = {
|
||||
type: 'value',
|
||||
nameGap: 15,
|
||||
nameTextStyle: {
|
||||
color: '#fff'
|
||||
},
|
||||
splitNumber: 5,
|
||||
minInterval: 1,
|
||||
axisLine: {
|
||||
show: true,
|
||||
lineStyle: {
|
||||
color: '#fff'
|
||||
}
|
||||
},
|
||||
axisLabel: {
|
||||
color: '#fff',
|
||||
fontSize: 14,
|
||||
formatter: function (value) {
|
||||
return parseFloat(value.toFixed(1)) // 格式化显示为一位小数
|
||||
}
|
||||
},
|
||||
splitLine: {
|
||||
lineStyle: {
|
||||
// 使用深浅的间隔色
|
||||
color: ['#ccc'],
|
||||
type: 'dashed',
|
||||
opacity: 0.5
|
||||
}
|
||||
}
|
||||
}
|
||||
// props.options?.xAxis 是数组还是对象
|
||||
if (Array.isArray(props.options?.yAxis)) {
|
||||
return props.options?.yAxis.map((item: any) => {
|
||||
return {
|
||||
...temp,
|
||||
...item
|
||||
}
|
||||
})
|
||||
} else {
|
||||
return {
|
||||
...temp,
|
||||
...props.options?.yAxis
|
||||
}
|
||||
}
|
||||
}
|
||||
const handlerXAxis = () => {
|
||||
let temp = {
|
||||
type: 'category',
|
||||
axisTick: { show: false },
|
||||
nameTextStyle: {
|
||||
color: '#fff'
|
||||
},
|
||||
axisLine: {
|
||||
// lineStyle: {
|
||||
color: '#fff'
|
||||
// }
|
||||
},
|
||||
axisLabel: {
|
||||
// textStyle: {
|
||||
fontFamily: 'dinproRegular',
|
||||
color: '#fff',
|
||||
fontSize: '12'
|
||||
// }
|
||||
}
|
||||
// boundaryGap: false,
|
||||
}
|
||||
// props.options?.xAxis 是数组还是对象
|
||||
if (Array.isArray(props.options?.xAxis)) {
|
||||
return props.options?.xAxis.map((item: any) => {
|
||||
return {
|
||||
...temp,
|
||||
...item
|
||||
}
|
||||
})
|
||||
} else {
|
||||
return {
|
||||
...temp,
|
||||
...props.options?.xAxis
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let throttle: ReturnType<typeof setTimeout>
|
||||
// 动态计算table高度
|
||||
const resizeObserver = new ResizeObserver(entries => {
|
||||
for (const entry of entries) {
|
||||
if (throttle) {
|
||||
clearTimeout(throttle)
|
||||
}
|
||||
throttle = setTimeout(() => {
|
||||
resizeHandler()
|
||||
}, 100)
|
||||
}
|
||||
})
|
||||
const setOptions = (options: any) => {
|
||||
chart.setOption(options)
|
||||
}
|
||||
const getChart = () => {
|
||||
return chart
|
||||
}
|
||||
onMounted(() => {
|
||||
initChart()
|
||||
resizeObserver.observe(chartRef.value!)
|
||||
})
|
||||
defineExpose({ initChart, setOptions, getChart })
|
||||
onBeforeUnmount(() => {
|
||||
resizeObserver.unobserve(chartRef.value!)
|
||||
chart?.dispose()
|
||||
})
|
||||
watch(
|
||||
() => props.options,
|
||||
(newVal, oldVal) => {
|
||||
initChart()
|
||||
}
|
||||
)
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.chart {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
|
||||
.el-button {
|
||||
position: absolute;
|
||||
right: 0px;
|
||||
top: -60px;
|
||||
}
|
||||
|
||||
.my-chart {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
14
src/components/echarts/color.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
export let color = [
|
||||
'#07CCCA',
|
||||
'#00BFF5',
|
||||
'#FFBF00',
|
||||
'#77DA63',
|
||||
'#D5FF6B',
|
||||
'#Ff6600',
|
||||
'#FF9100',
|
||||
'#5B6E96',
|
||||
'#66FFCC',
|
||||
'#B3B'
|
||||
]
|
||||
export const gradeColor3 = ['#339966', '#FFCC33', '#A52a2a']
|
||||
export const gradeColor5 = ['#00CC00', '#99CC99', '#FF9900', '#996600', '#A52a2a']
|
||||
84
src/components/echarts/echarts.ts
Normal file
@@ -0,0 +1,84 @@
|
||||
import * as echarts from 'echarts/core'
|
||||
|
||||
//引入需要的图表,需要什么就加什么
|
||||
import {
|
||||
BarChart,
|
||||
LineChart,
|
||||
PieChart,
|
||||
ScatterChart,
|
||||
EffectScatterChart,
|
||||
RadarChart,
|
||||
GaugeChart
|
||||
} from 'echarts/charts'
|
||||
|
||||
// 引入提示框,标题,直角坐标系,数据集,内置数据转换器组件,组件后缀都为 Component
|
||||
import {
|
||||
TitleComponent,
|
||||
TooltipComponent,
|
||||
GridComponent,
|
||||
LegendComponent,
|
||||
ToolboxComponent,
|
||||
// 数据集组件
|
||||
DatasetComponent,
|
||||
// 内置数据转换器组件 (filter, sort)
|
||||
TransformComponent
|
||||
} from 'echarts/components'
|
||||
|
||||
// 标签自动布局,全局过渡动画等特性
|
||||
import { LabelLayout, UniversalTransition } from 'echarts/features'
|
||||
|
||||
// 引入 Canvas 渲染器,注意引入 CanvasRenderer 或者 SVGRenderer 是必须的一步
|
||||
import { CanvasRenderer } from 'echarts/renderers'
|
||||
|
||||
import type {
|
||||
// 系列类型的定义后缀都为 SeriesOption
|
||||
BarSeriesOption,
|
||||
LineSeriesOption
|
||||
} from 'echarts/charts'
|
||||
|
||||
import type {
|
||||
// 组件类型的定义后缀都为 ComponentOption
|
||||
TitleComponentOption,
|
||||
TooltipComponentOption,
|
||||
GridComponentOption,
|
||||
LegendComponentOption,
|
||||
ToolboxComponentOption,
|
||||
DatasetComponentOption
|
||||
} from 'echarts/components'
|
||||
import type { ComposeOption } from 'echarts/core'
|
||||
|
||||
// 通过 ComposeOption 来组合出一个只有必须组件和图表的 Option 类型
|
||||
type ECOption = ComposeOption<
|
||||
| BarSeriesOption
|
||||
| LineSeriesOption
|
||||
| TitleComponentOption
|
||||
| TooltipComponentOption
|
||||
| GridComponentOption
|
||||
| DatasetComponentOption
|
||||
| LegendComponentOption
|
||||
| ToolboxComponentOption
|
||||
>
|
||||
|
||||
// 注册必须的组件,上面引入的都需要在此注册
|
||||
echarts.use([
|
||||
TitleComponent,
|
||||
TooltipComponent,
|
||||
GridComponent,
|
||||
DatasetComponent,
|
||||
LegendComponent,
|
||||
ToolboxComponent,
|
||||
TransformComponent,
|
||||
BarChart,
|
||||
LineChart,
|
||||
PieChart,
|
||||
ScatterChart,
|
||||
LabelLayout,
|
||||
UniversalTransition,
|
||||
CanvasRenderer,
|
||||
EffectScatterChart,
|
||||
RadarChart,
|
||||
GaugeChart
|
||||
])
|
||||
|
||||
// 导出
|
||||
export default echarts
|
||||
1
src/components/mapJson/北京.json
Normal file
272
src/components/tree/pointTree.vue
Normal file
@@ -0,0 +1,272 @@
|
||||
<template>
|
||||
<div style="height: 755px">
|
||||
<div class="textInput">
|
||||
<el-input v-model="filterText" placeholder="请输入内容" clearable maxlength="10" show-word-limit @input="change"
|
||||
size="small">
|
||||
<template #prefix>
|
||||
<el-link :icon="Search"></el-link>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
<!-- :check-strictly="true"
|
||||
:check-on-click-node="true"
|
||||
:expand-on-click-node="false" 点击三角标的时候展开,点击文字不展开-->
|
||||
<el-tree class="treeSet" ref="treeRef" :props="defaultProps" highlight-current
|
||||
:filter-node-method="filterNode" node-key="id" :data="dataTree" v-bind="$attrs"
|
||||
:default-expanded-keys="expandedKeys">
|
||||
<template #default="{ node, data }">
|
||||
<span class="custom-tree-node">
|
||||
<el-link v-if="data.level == -1" style="color: #0a73ff" :icon="HomeFilled"></el-link>
|
||||
<el-link v-if="data.level == 0" style="color: #0a73ff" :icon="CollectionTag"></el-link>
|
||||
<el-link v-if="data.level == 2" style="color: #0a73ff" :icon="Flag"></el-link>
|
||||
<el-link v-if="data.level == 3" style="color: #0a73ff" :icon="Guide"></el-link>
|
||||
<el-link v-if="data.level == 7" style="color: #0a73ff" :icon="OfficeBuilding"></el-link>
|
||||
<el-link v-if="data.level == 6"
|
||||
:style="{ color: data.comFlag == 0 ? 'red' : data.comFlag == 1 ? '#0a73ff' : 'gray' }" :icon="Location">
|
||||
</el-link>
|
||||
<span style="margin-left: 4px">{{ node.label }}</span>
|
||||
</span>
|
||||
</template>
|
||||
</el-tree>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, watch, computed, nextTick } from "vue";
|
||||
import { ElTree } from "element-plus";
|
||||
import {
|
||||
Search,
|
||||
HomeFilled,
|
||||
CollectionTag,
|
||||
Flag,
|
||||
OfficeBuilding,
|
||||
Location,
|
||||
Guide,
|
||||
} from "@element-plus/icons-vue";
|
||||
import { getTerminalTreeForFive } from "@/api/manage_wx";
|
||||
import { useStore } from "vuex";
|
||||
|
||||
const store = useStore();
|
||||
|
||||
const filterText = ref("");
|
||||
const defaultProps = {
|
||||
label: "name",
|
||||
value: "id",
|
||||
};
|
||||
|
||||
const treeRef = ref();
|
||||
|
||||
const dataTree = ref([]);
|
||||
const expandedKeys = ref([]);
|
||||
|
||||
const emit = defineEmits(["init"]);
|
||||
|
||||
const currentNodeKey = ref<string | number>("");
|
||||
|
||||
const specialCharsPattern =
|
||||
/[`~!@$%^&*\-+=<>?:"{}|,.\/;'\\[\]·~!@¥%……&*()——\-+={}|《》?:“”【】、;‘’,。、~]/g;
|
||||
const change = (val) => {
|
||||
if (specialCharsPattern.test(val)) {
|
||||
ElMessage.warning("禁止输入特殊字符!");
|
||||
filterText.value = val.replace(
|
||||
/[`~!@$%^&*\-+=<>?:"{}|,.\/;'\\[\]·~!@¥%……&*()——\-+={}|《》?:“”【】、;‘’,。、~]/g,
|
||||
""
|
||||
);
|
||||
// console.log("🚀 ~ change ~ filterText.value:", filterText.value);
|
||||
|
||||
treeRef.value!.filter(filterText.value);
|
||||
} else {
|
||||
treeRef.value!.filter(filterText.value);
|
||||
}
|
||||
};
|
||||
|
||||
const filterNode = (value: string, data: any, node: any) => {
|
||||
if (!value) return true;
|
||||
// return data.name.includes(value)
|
||||
if (data.name) {
|
||||
return chooseNode(value, data, node);
|
||||
}
|
||||
};
|
||||
|
||||
// 过滤父节点 / 子节点 (如果输入的参数是父节点且能匹配,则返回该节点以及其下的所有子节点;如果参数是子节点,则返回该节点的父节点。name是中文字符,enName是英文字符.
|
||||
const chooseNode = (value: string, data: any, node: any) => {
|
||||
if (data.name.indexOf(value) !== -1) {
|
||||
return true;
|
||||
}
|
||||
const level = node.level;
|
||||
// 如果传入的节点本身就是一级节点就不用校验了
|
||||
if (level === 1) {
|
||||
return false;
|
||||
}
|
||||
// 先取当前节点的父节点
|
||||
let parentData = node.parent;
|
||||
// 遍历当前节点的父节点
|
||||
let index = 0;
|
||||
while (index < level - 1) {
|
||||
// 如果匹配到直接返回,此处name值是中文字符,enName是英文字符。判断匹配中英文过滤
|
||||
if (parentData.data.name.indexOf(value) !== -1) {
|
||||
return true;
|
||||
}
|
||||
// 否则的话再往上一层做匹配
|
||||
parentData = parentData.parent;
|
||||
index++;
|
||||
}
|
||||
// 没匹配到返回false
|
||||
return false;
|
||||
};
|
||||
|
||||
const classificationData = ref([]);
|
||||
|
||||
const formData = ref({
|
||||
deptIndex: store.state.deptId,
|
||||
});
|
||||
|
||||
const loadData = () => {
|
||||
let form = JSON.parse(JSON.stringify(formData.value));
|
||||
getTerminalTreeForFive(form).then((res) => {
|
||||
console.log(res);
|
||||
|
||||
res.data = [
|
||||
{
|
||||
name: "电网拓扑",
|
||||
level: -1,
|
||||
id: 0,
|
||||
children: res.data,
|
||||
},
|
||||
];
|
||||
|
||||
// expandedKeys.value = [res.data[0].id];
|
||||
|
||||
// 查找第一层级的最后一个子节点
|
||||
const firstLevelChildren = res.data[0].children;
|
||||
if (firstLevelChildren && firstLevelChildren.length > 0) {
|
||||
let flag = true;
|
||||
|
||||
// 设置节点别名
|
||||
res.data.forEach((item: any) => {
|
||||
item.children.forEach((item2: any) => {
|
||||
item2.children.forEach((item3: any) => {
|
||||
item3.children.forEach((item4: any) => {
|
||||
item4.children.forEach((item5: any) => {
|
||||
if (item5.level == 7) {
|
||||
item5.children.forEach((item6: any) => {
|
||||
item6.alias = `${item.name}>${item2.name}>${item3.name}>${item4.name}>${item5.name}>${item6.name}`;
|
||||
});
|
||||
} else {
|
||||
if (flag) {
|
||||
expandedKeys.value = [item5.id];
|
||||
treeRef.value.setCurrentKey(item5.id);
|
||||
emit("init", item5);
|
||||
flag = false
|
||||
}
|
||||
item5.alias = `${item.name}>${item2.name}>${item3.name}>${item4.name}>${item5.name}`;
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// const lastChild = firstLevelChildren[firstLevelChildren.length - 1];
|
||||
|
||||
// // 设置当前节点key
|
||||
// currentNodeKey.value = lastChild.children[0].children[0].children[0].children[0].id;
|
||||
|
||||
// // 发送初始化事件
|
||||
// emit("init", lastChild);
|
||||
|
||||
// // 设置默认选中节点
|
||||
// nextTick(() => {
|
||||
// if (treeRef.value) {
|
||||
// treeRef.value.setCurrentKey(currentNodeKey.value);
|
||||
// }
|
||||
// });
|
||||
}
|
||||
|
||||
dataTree.value = res.data;
|
||||
});
|
||||
};
|
||||
|
||||
loadData();
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.cn-tree {
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
box-sizing: border-box;
|
||||
padding: 10px;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
|
||||
:deep(.el-tree) {
|
||||
border: 1px solid var(--el-border-color);
|
||||
}
|
||||
|
||||
:deep(.el-tree--highlight-current .el-tree-node.is-current > .el-tree-node__content) {
|
||||
background-color: var(--el-color-primary-light-7);
|
||||
}
|
||||
|
||||
.menu-collapse {
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
}
|
||||
|
||||
.custom-tree-node {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.textInput {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
// height: 30px;
|
||||
padding: 5px;
|
||||
background-color: rgba(44, 46, 60);
|
||||
}
|
||||
|
||||
.treeSet {
|
||||
flex: 1;
|
||||
overflow: scroll;
|
||||
height: 97%;
|
||||
background-color: rgba(44, 46, 60);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
// 悬浮时的样式
|
||||
:deep(.el-tree-node__content:hover) {
|
||||
background-color: #0a73ff70 !important;
|
||||
color: #fff !important;
|
||||
|
||||
.custom-tree-node {
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
.el-link {
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
// 添加更具体的选择器来覆盖默认样式
|
||||
span {
|
||||
color: #fff !important;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.el-tree-node:focus > .el-tree-node__content) {
|
||||
background-color: #0a73ff70 !important;
|
||||
}
|
||||
|
||||
:deep(.el-tree--highlight-current .el-tree-node.is-current > .el-tree-node__content) {
|
||||
background-color: #0a73ff70 !important;
|
||||
color: #fff !important;
|
||||
|
||||
.custom-tree-node {
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
.el-link {
|
||||
color: #fff !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
372
src/components/tree/systemTree.vue
Normal file
@@ -0,0 +1,372 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="textInput">
|
||||
<el-input v-model="filterText" placeholder="请输入内容" clearable maxlength="10" show-word-limit @input="change"
|
||||
size="small">
|
||||
<template #prefix>
|
||||
<el-link :icon="Search"></el-link>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
<!-- :check-strictly="true"
|
||||
:check-on-click-node="true"
|
||||
:expand-on-click-node="false" 点击三角标的时候展开,点击文字不展开-->
|
||||
|
||||
<el-tree class="treeSet" ref="treeRef" show-checkbox :props="defaultProps" highlight-current
|
||||
:filter-node-method="filterNode" node-key="id" :data="dataTree" v-bind="$attrs"
|
||||
:default-expanded-keys="expandedKeys">
|
||||
<template #default="{ node, data }">
|
||||
<el-radio-group v-model="totalId" v-if="data.level == 6" @click.stop="changeRadio(data)">
|
||||
<el-radio :value="data.id"></el-radio>
|
||||
</el-radio-group>
|
||||
<div class="custom-tree-node">
|
||||
<div>
|
||||
<el-link v-if="data.level == -1" style="color: #0a73ff" :icon="HomeFilled"></el-link>
|
||||
<el-link v-if="data.level == 0" style="color: #0a73ff" :icon="CollectionTag"></el-link>
|
||||
<el-link v-if="data.level == 2" style="color: #0a73ff" :icon="Flag"></el-link>
|
||||
<el-link v-if="data.level == 3" style="color: #0a73ff" :icon="Guide"></el-link>
|
||||
<el-link v-if="data.level == 7" style="color: #0a73ff" :icon="OfficeBuilding"></el-link>
|
||||
<el-link v-if="data.level == 6"
|
||||
:style="{ color: data.comFlag == 0 ? 'red' : data.comFlag == 1 ? '#0a73ff' : 'gray' }"
|
||||
:icon="Location">
|
||||
</el-link>
|
||||
<span style="margin-left: 4px">{{ node.label }}</span>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</template>
|
||||
</el-tree>
|
||||
</div>
|
||||
|
||||
<div class="legend-group">
|
||||
<!-- 第一个图例:正方形 + 谐波 -->
|
||||
<div class="legend-item ml30">
|
||||
<div class="icon-square"></div>
|
||||
<span>用采用户</span>
|
||||
</div>
|
||||
<!-- 第二个图例:圆形 + 背景 -->
|
||||
<div class="legend-item">
|
||||
<div class="icon-circle"></div>
|
||||
<span>背景谐波用户</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, watch, computed, nextTick } from "vue";
|
||||
import { ElTree } from "element-plus";
|
||||
import {
|
||||
Search,
|
||||
HomeFilled,
|
||||
CollectionTag,
|
||||
Flag,
|
||||
OfficeBuilding,
|
||||
Location,
|
||||
Guide,
|
||||
} from "@element-plus/icons-vue";
|
||||
import { getTerminalTreeForFive } from "@/api/manage_wx";
|
||||
import { useStore } from "vuex";
|
||||
const totalId = ref('');
|
||||
const store = useStore();
|
||||
|
||||
const filterText = ref("");
|
||||
const defaultProps = {
|
||||
label: "name",
|
||||
value: "id",
|
||||
disabled: 'disabled',
|
||||
};
|
||||
|
||||
const treeRef = ref();
|
||||
|
||||
const dataTree = ref([]);
|
||||
const expandedKeys = ref([]);
|
||||
|
||||
const emit = defineEmits(["init"]);
|
||||
|
||||
const currentNodeKey = ref<string | number>("");
|
||||
|
||||
const specialCharsPattern =
|
||||
/[`~!@$%^&*\-+=<>?:"{}|,.\/;'\\[\]·~!@¥%……&*()——\-+={}|《》?:“”【】、;‘’,。、~]/g;
|
||||
const change = (val) => {
|
||||
if (specialCharsPattern.test(val)) {
|
||||
ElMessage.warning("禁止输入特殊字符!");
|
||||
filterText.value = val.replace(
|
||||
/[`~!@$%^&*\-+=<>?:"{}|,.\/;'\\[\]·~!@¥%……&*()——\-+={}|《》?:“”【】、;‘’,。、~]/g,
|
||||
""
|
||||
);
|
||||
|
||||
treeRef.value!.filter(filterText.value);
|
||||
} else {
|
||||
treeRef.value!.filter(filterText.value);
|
||||
}
|
||||
};
|
||||
|
||||
const filterNode = (value: string, data: any, node: any) => {
|
||||
if (!value) return true;
|
||||
// return data.name.includes(value)
|
||||
if (data.name) {
|
||||
return chooseNode(value, data, node);
|
||||
}
|
||||
};
|
||||
|
||||
// 过滤父节点 / 子节点 (如果输入的参数是父节点且能匹配,则返回该节点以及其下的所有子节点;如果参数是子节点,则返回该节点的父节点。name是中文字符,enName是英文字符.
|
||||
const chooseNode = (value: string, data: any, node: any) => {
|
||||
if (data.name.indexOf(value) !== -1) {
|
||||
return true;
|
||||
}
|
||||
const level = node.level;
|
||||
// 如果传入的节点本身就是一级节点就不用校验了
|
||||
if (level === 1) {
|
||||
return false;
|
||||
}
|
||||
// 先取当前节点的父节点
|
||||
let parentData = node.parent;
|
||||
// 遍历当前节点的父节点
|
||||
let index = 0;
|
||||
while (index < level - 1) {
|
||||
// 如果匹配到直接返回,此处name值是中文字符,enName是英文字符。判断匹配中英文过滤
|
||||
if (parentData.data.name.indexOf(value) !== -1) {
|
||||
return true;
|
||||
}
|
||||
// 否则的话再往上一层做匹配
|
||||
parentData = parentData.parent;
|
||||
index++;
|
||||
}
|
||||
// 没匹配到返回false
|
||||
return false;
|
||||
};
|
||||
|
||||
const classificationData = ref([]);
|
||||
|
||||
const formData = ref({
|
||||
deptIndex: store.state.deptId,
|
||||
});
|
||||
const nodeTreeCopy: any = ref({})
|
||||
// 点击单选
|
||||
const changeRadio = (data) => {
|
||||
|
||||
// 恢复原来禁用状态
|
||||
if (nodeTreeCopy.value.disabled) {
|
||||
nodeTreeCopy.value.disabled = false
|
||||
}
|
||||
setTimeout(() => {
|
||||
data.disabled = true;
|
||||
nodeTreeCopy.value = data
|
||||
}, 0)
|
||||
// 取消选中
|
||||
const currentChecked = treeRef.value.getCheckedKeys(true);
|
||||
const newChecked = currentChecked.filter(id => id !== data.id);
|
||||
console.log("🚀 ~ changeRadio ~ newChecked:", newChecked)
|
||||
treeRef.value.setCheckedKeys(newChecked);
|
||||
|
||||
};
|
||||
|
||||
const loadData = () => {
|
||||
let form = JSON.parse(JSON.stringify(formData.value));
|
||||
getTerminalTreeForFive(form).then((res) => {
|
||||
console.log(res);
|
||||
|
||||
res.data = [
|
||||
{
|
||||
name: "电网拓扑",
|
||||
level: -1,
|
||||
id: 0,
|
||||
children: res.data,
|
||||
},
|
||||
];
|
||||
|
||||
// expandedKeys.value = [res.data[0].id];
|
||||
|
||||
// 查找第一层级的最后一个子节点
|
||||
const firstLevelChildren = res.data[0].children;
|
||||
if (firstLevelChildren && firstLevelChildren.length > 0) {
|
||||
let flag = true;
|
||||
|
||||
// 设置节点别名
|
||||
res.data.forEach((item: any) => {
|
||||
item.children.forEach((item2: any) => {
|
||||
item2.children.forEach((item3: any) => {
|
||||
item3.children.forEach((item4: any) => {
|
||||
item4.children.forEach((item5: any) => {
|
||||
if (item5.level == 7) {
|
||||
item5.children.forEach((item6: any) => {
|
||||
item6.disabled = false
|
||||
item6.alias = `${item.name}>${item2.name}>${item3.name}>${item4.name}>${item5.name}>${item6.name}`;
|
||||
});
|
||||
} else {
|
||||
if (flag) {
|
||||
expandedKeys.value = [item5.id];
|
||||
treeRef.value.setCurrentKey(item5.id);
|
||||
emit("init", item5);
|
||||
flag = false
|
||||
}
|
||||
item5.disabled = false
|
||||
item5.alias = `${item.name}>${item2.name}>${item3.name}>${item4.name}>${item5.name}`;
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
dataTree.value = res.data;
|
||||
});
|
||||
};
|
||||
|
||||
loadData();
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.cn-tree {
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
box-sizing: border-box;
|
||||
padding: 10px;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
|
||||
:deep(.el-tree) {
|
||||
border: 1px solid var(--el-border-color);
|
||||
}
|
||||
|
||||
:deep(.el-tree--highlight-current .el-tree-node.is-current > .el-tree-node__content) {
|
||||
background-color: var(--el-color-primary-light-7);
|
||||
}
|
||||
|
||||
.menu-collapse {
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
}
|
||||
|
||||
.custom-tree-node {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.textInput {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
// height: 30px;
|
||||
padding: 5px;
|
||||
background-color: rgba(44, 46, 60);
|
||||
}
|
||||
|
||||
.treeSet {
|
||||
height: 690px;
|
||||
overflow-y: auto;
|
||||
background-color: rgba(44, 46, 60);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* 图例容器:横向排列、居中对齐 */
|
||||
.legend-group {
|
||||
background-color: rgba(44, 46, 60);
|
||||
padding: 5px;
|
||||
color: #fff;
|
||||
display: flex;
|
||||
|
||||
/* 两个图例之间的间距 */
|
||||
align-items: center;
|
||||
|
||||
/* 单个图例:固定宽度 150px、横向布局 */
|
||||
.legend-item {
|
||||
width: 120px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
// margin-left: 40px;
|
||||
/* 图标与文字间距 */
|
||||
font-size: 14px;
|
||||
|
||||
}
|
||||
|
||||
/* 正方形图标样式 */
|
||||
.icon-square {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
background-color: #fff;
|
||||
/* 蓝色示例色,可自行修改 */
|
||||
}
|
||||
|
||||
/* 圆形图标样式 */
|
||||
.icon-circle {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-radius: 50%;
|
||||
/* 圆形关键属性 */
|
||||
background-color: #fff;
|
||||
/* 绿色示例色,可自行修改 */
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// 悬浮时的样式
|
||||
:deep(.el-tree-node__content:hover) {
|
||||
background-color: #0a73ff70 !important;
|
||||
color: #fff !important;
|
||||
|
||||
.custom-tree-node {
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
.el-link {
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
// 添加更具体的选择器来覆盖默认样式
|
||||
span {
|
||||
color: #fff !important;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.el-tree-node:focus > .el-tree-node__content) {
|
||||
background-color: #0a73ff70 !important;
|
||||
}
|
||||
|
||||
:deep(.el-tree--highlight-current .el-tree-node.is-current > .el-tree-node__content) {
|
||||
background-color: #0a73ff70 !important;
|
||||
color: #fff !important;
|
||||
|
||||
.custom-tree-node {
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
.el-link {
|
||||
color: #fff !important;
|
||||
}
|
||||
}
|
||||
|
||||
.custom-tree-node {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
font-size: 14px;
|
||||
padding-right: 8px;
|
||||
}
|
||||
|
||||
:deep(.el-radio__input.is-checked .el-radio__inner) {
|
||||
background: #DAA520;
|
||||
border-color: #DAA520;
|
||||
}
|
||||
|
||||
:deep(.el-checkbox__input.is-disabled.is-checked .el-checkbox__inner) {
|
||||
background-color: #63636380;
|
||||
border-color: #63636380;
|
||||
}
|
||||
|
||||
:deep(.el-checkbox__input.is-disabled .el-checkbox__inner) {
|
||||
background-color: #63636380;
|
||||
border-color: #63636380;
|
||||
}
|
||||
</style>
|
||||
5
src/constant/index.d.ts
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
// 各类标题与图标
|
||||
export type ModuleInfo = {
|
||||
name: string
|
||||
icon: string
|
||||
}[]
|
||||
47
src/constant/index.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import { ModuleInfo } from "./index.d";
|
||||
// 运维单位id
|
||||
export const deptId = window.sessionStorage.getItem("deptId") || "10002";
|
||||
export const timeType = 3; //类型(1年 2季度 3月份 4周 5日)")
|
||||
|
||||
// 星期
|
||||
export const WEEK = ["周日", "周一", "周二", "周三", "周四", "周五", "周六"];
|
||||
|
||||
// 主题名称与副标题名称
|
||||
export const title = "电压暂降监测平台";
|
||||
export const subtitle = ["数据分析1", "数据分析2", "vue-big-screen"];
|
||||
// 组件颜色
|
||||
export const color = [
|
||||
["#0a73ff60", "#0a73ff"],
|
||||
["#0a73ff", "#003a87"],
|
||||
["#0a73ff", "#0a73ff"],
|
||||
["#0a73ff60", "#0a73ff60"],
|
||||
];
|
||||
|
||||
export const moduleInfo: ModuleInfo = [
|
||||
// 中间的几个模块
|
||||
{
|
||||
name: "任务通过率",
|
||||
icon: "icon-chart-bar",
|
||||
},
|
||||
{
|
||||
name: "地图数据",
|
||||
icon: "icon-tongji4",
|
||||
},
|
||||
{
|
||||
name: "产品销售渠道分析",
|
||||
icon: "icon-align-left",
|
||||
},
|
||||
{
|
||||
name: "任务完成排行榜",
|
||||
icon: "icon-zhibiao2",
|
||||
},
|
||||
// 底部两个模块
|
||||
{
|
||||
name: "数据统计图",
|
||||
icon: "icon-vector",
|
||||
},
|
||||
{
|
||||
name: "工单修复以及满意度统计图",
|
||||
icon: "icon-fenxi7",
|
||||
},
|
||||
];
|
||||
47
src/constant/index_wx.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import { ModuleInfo } from "./index.d";
|
||||
// 运维单位id
|
||||
export const deptId = window.sessionStorage.getItem("deptId") || "10002";
|
||||
export const timeType = 3; //类型(1年 2季度 3月份 4周 5日)")
|
||||
|
||||
// 星期
|
||||
export const WEEK = ["周日", "周一", "周二", "周三", "周四", "周五", "周六"];
|
||||
|
||||
// 主题名称与副标题名称
|
||||
export const title = "无锡电能质量演示平台";
|
||||
export const subtitle = ["数据分析1", "数据分析2", "vue-big-screen"];
|
||||
// 组件颜色
|
||||
export const color = [
|
||||
["#0a73ff60", "#0a73ff"],
|
||||
["#0a73ff", "#003a87"],
|
||||
["#0a73ff", "#0a73ff"],
|
||||
["#0a73ff60", "#0a73ff60"],
|
||||
];
|
||||
|
||||
export const moduleInfo: ModuleInfo = [
|
||||
// 中间的几个模块
|
||||
{
|
||||
name: "任务通过率",
|
||||
icon: "icon-chart-bar",
|
||||
},
|
||||
{
|
||||
name: "地图数据",
|
||||
icon: "icon-tongji4",
|
||||
},
|
||||
{
|
||||
name: "产品销售渠道分析",
|
||||
icon: "icon-align-left",
|
||||
},
|
||||
{
|
||||
name: "任务完成排行榜",
|
||||
icon: "icon-zhibiao2",
|
||||
},
|
||||
// 底部两个模块
|
||||
{
|
||||
name: "数据统计图",
|
||||
icon: "icon-vector",
|
||||
},
|
||||
{
|
||||
name: "工单修复以及满意度统计图",
|
||||
icon: "icon-fenxi7",
|
||||
},
|
||||
];
|
||||
45
src/main.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import { createApp } from "vue";
|
||||
import App from "./App.vue";
|
||||
import router from "./router/index";
|
||||
import store from "./store/index";
|
||||
//import dataV from '@jiaminghi/data-view';//引入dataV可能如果启动报错看这个https://blog.csdn.net/qq_54753561/article/details/125583526
|
||||
import echarts from "./components/echarts"; //echarts 根据官网封装的
|
||||
import DataVVue3 from "@kjgl77/datav-vue3"; //https://datav-vue3.netlify.app/Guide/index.html#%E4%BD%BF%E7%94%A8%E6%96%B9%E6%B3%95
|
||||
import * as XEUtils from "xe-utils";
|
||||
import ElementPlus from "element-plus";
|
||||
import "element-plus/dist/index.css";
|
||||
import zhCn from "element-plus/es/locale/lang/zh-cn";
|
||||
// 引入全局css
|
||||
import "./assets/scss/style.scss";
|
||||
// 引入图表(所有图标见 icon 目录下的 demo_index.html)
|
||||
import "./assets/icon/iconfont.css";
|
||||
import BaiduMap from "vue-baidu-map-3x";
|
||||
import BaiduMapOffline from "vue-baidu-map-offline";
|
||||
(window as any).XEUtils = XEUtils;
|
||||
const app = createApp(App);
|
||||
app.use(BaiduMapOffline, {
|
||||
offline: true,
|
||||
offlineConfig: {
|
||||
imgext: ".png",
|
||||
customstyle: "",
|
||||
tiles_dir: "",
|
||||
tiles_hybrid: "",
|
||||
tiles_self: "",
|
||||
tiles_v_dir: "",
|
||||
tiles_satellite_dir: "",
|
||||
tiles_road_dir: "",
|
||||
tiles_v_road_dir: "",
|
||||
home: "./plugin/offline/",
|
||||
},
|
||||
});
|
||||
app.use(BaiduMap, {
|
||||
// ak: 'Yp57V71dkOPiXjiN8VdcFRsVELzlVNKK',
|
||||
ak: "RpQi6WNFZ9tseKzhdwOQsXwFsoVntnsN",
|
||||
v: "3.0",
|
||||
});
|
||||
app.config.globalProperties.$echarts = echarts;
|
||||
app.use(DataVVue3);
|
||||
app.use(ElementPlus, {
|
||||
locale: zhCn,
|
||||
});
|
||||
app.use(store).use(router).mount("#app");
|
||||
19
src/mock/login.json
Normal file
@@ -0,0 +1,19 @@
|
||||
[{
|
||||
"id": "1",
|
||||
"imgUrl": "/images/banner1.jpg"
|
||||
},
|
||||
{
|
||||
"id": "2",
|
||||
"imgUrl": "/images/banner2.jpg"
|
||||
},
|
||||
{
|
||||
"id": "3",
|
||||
"imgUrl": "/images/banner3.jpg"
|
||||
},
|
||||
{
|
||||
"id": "4",
|
||||
"imgUrl": "/images/banner4.jpg"
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
17
src/mock/login.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { MockMethod } from "vite-plugin-mock";
|
||||
import login from "./login.json"
|
||||
//login是变量,可以随便命名
|
||||
|
||||
export default [
|
||||
{
|
||||
url: "/mock-login", // 注意,这里只能是string格式
|
||||
method: "post",
|
||||
response: () => {
|
||||
return {
|
||||
code: 0,
|
||||
message: 'ok',
|
||||
menusList:login,
|
||||
}
|
||||
},
|
||||
},
|
||||
] as MockMethod[]
|
||||
27
src/router/index.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { createRouter, createWebHashHistory, RouteRecordRaw } from "vue-router";
|
||||
const routes: Array<RouteRecordRaw> = [
|
||||
{
|
||||
path: "/",
|
||||
name: "Index",
|
||||
component: () => import("@/views/SagTraceResult_WX/index.vue"), // 懒加载组件
|
||||
},
|
||||
{
|
||||
path: "/VoltageSag", //电压暂降监测平台_北京
|
||||
name: "VoltageSag",
|
||||
component: () => import("@/views/VoltageSag_BJ/index.vue"), // 懒加载组件
|
||||
},
|
||||
{
|
||||
path: "/SagTraceResult", //暂降溯源演示平台_无锡
|
||||
name: "SagTraceResult",
|
||||
component: () => import("@/views/SagTraceResult_WX/index.vue"), // 懒加载组件
|
||||
},
|
||||
];
|
||||
|
||||
const base_url = import.meta.env.BASE_URL; //获取vite.config.js配置的base,默认是根目录/
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHashHistory(base_url),
|
||||
routes,
|
||||
});
|
||||
|
||||
export default router;
|
||||
15
src/shims-vue.d.ts
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
declare module '*.vue' {
|
||||
import { ComponentOptions } from 'vue'
|
||||
const componentOptions: ComponentOptions
|
||||
export default componentOptions
|
||||
}
|
||||
|
||||
|
||||
// /* eslint-disable */
|
||||
// declare module '*.vue' {
|
||||
// import type { DefineComponent } from 'vue'
|
||||
// const component: DefineComponent<{}, {}, any>
|
||||
// export default component
|
||||
// }
|
||||
|
||||
declare module '@jiaminghi/data-view'
|
||||
119
src/store/index.ts
Normal file
@@ -0,0 +1,119 @@
|
||||
import { createStore, Store, useStore } from "vuex";
|
||||
import { login } from "@/api/login/login";
|
||||
import { initLedger, queryConfig } from "@/api/statistics/index";
|
||||
// const store = useStore();
|
||||
|
||||
// 定义状态接口
|
||||
interface State {
|
||||
count: number;
|
||||
token: string;
|
||||
deptId: string;
|
||||
timeType: number;
|
||||
timeValue: string[];
|
||||
seriousNotice: number;
|
||||
normalNotic: number;
|
||||
voiceType: number;
|
||||
screenNotic: number;
|
||||
eventTypeList: string[];
|
||||
eventValue: number;
|
||||
eventDuration: number;
|
||||
realData: []; //实时数据
|
||||
}
|
||||
|
||||
// 初始状态
|
||||
const state: State = {
|
||||
count: 0,
|
||||
token: "",
|
||||
deptId: "10001",
|
||||
timeType: 3, //类型(1年 2季度 3月份 4周 5日)")
|
||||
timeValue: [], //类型(1年 2季度 3月份 4周 5日)")
|
||||
seriousNotice: 0, //严重通知(0关闭 1开启)
|
||||
normalNotic: 1, //普通通知(0关闭 1开启)
|
||||
voiceType: 1, //语音类型(1人声音 2音频)
|
||||
screenNotic: 1, //屏幕通知(0关闭 1开启)
|
||||
eventTypeList: [], //触发类型(1告警 2事件)
|
||||
eventValue: 0.7,
|
||||
eventDuration: 5,
|
||||
realData: [], //实时数据
|
||||
};
|
||||
|
||||
// 定义Mutation类型
|
||||
enum Mutations {
|
||||
INCREMENT = "INCREMENT",
|
||||
SET_TOKEN = "SET_TOKEN",
|
||||
SET_TIME = "SET-TIME",
|
||||
SET_CONFIG = "SET-CONFIG-TIME",
|
||||
}
|
||||
|
||||
export default createStore({
|
||||
state,
|
||||
mutations: {
|
||||
[Mutations.INCREMENT](state: State) {
|
||||
state.count++;
|
||||
},
|
||||
[Mutations.SET_TOKEN](state: State, data: any) {
|
||||
window.sessionStorage.setItem("token", data.token);
|
||||
window.sessionStorage.setItem("deptId", data.deptId);
|
||||
state.token = data.token;
|
||||
state.deptId = data.deptId;
|
||||
},
|
||||
[Mutations.SET_TIME](state: State, data: any) {
|
||||
state.timeType = data.type;
|
||||
state.timeValue = data.value;
|
||||
},
|
||||
[Mutations.SET_CONFIG](state: State, data: any) {
|
||||
state.seriousNotice = data.seriousNotice;
|
||||
state.normalNotic = data.normalNotic;
|
||||
state.voiceType = data.voiceType;
|
||||
state.screenNotic = data.screenNotic;
|
||||
state.eventTypeList = data.eventTypeList;
|
||||
state.eventValue = data.eventValue;
|
||||
state.eventDuration = data.eventDuration;
|
||||
},
|
||||
SET_REAL_DATA(state, data) {
|
||||
state.realData = data;
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
async loginAction({ commit }, data: any) {
|
||||
try {
|
||||
// const response = await login({
|
||||
// username: "screen",
|
||||
// password: "@#001njcnpqs",
|
||||
// });
|
||||
const response = await login(data);
|
||||
commit(Mutations.SET_TOKEN, response.data);
|
||||
|
||||
// await initLedger({ deptId: response.data.deptId, type: 0 });
|
||||
return;
|
||||
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error("登录失败:", error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
increment({ commit }) {
|
||||
commit(Mutations.INCREMENT);
|
||||
},
|
||||
setTimeType({ commit }, data: any) {
|
||||
commit(Mutations.SET_TIME, data);
|
||||
},
|
||||
setConfig({ commit }, data: any) {
|
||||
queryConfig().then((res) => {
|
||||
commit(Mutations.SET_CONFIG, res.data);
|
||||
});
|
||||
},
|
||||
updateRealData({ commit }, data) {
|
||||
commit("SET_REAL_DATA", data);
|
||||
},
|
||||
},
|
||||
getters: {
|
||||
double(state: State) {
|
||||
return 2 * state.count;
|
||||
},
|
||||
isAuthenticated(state: State) {
|
||||
return !!state.token;
|
||||
},
|
||||
},
|
||||
});
|
||||
54
src/utils/color.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* 根据数值排名自动分配颜色
|
||||
* @param {Array} data - 包含 name 和 value 的对象数组
|
||||
* @returns {Array} - 添加了 color 属性的新数组
|
||||
*/
|
||||
export const assignColorsByRank = (data: any) => {
|
||||
// 复制原数组避免修改
|
||||
const sortedData = [...data].sort((a, b) => b.value - a.value);
|
||||
const totalCount = sortedData.length;
|
||||
|
||||
// 计算各区间边界(向上取整确保覆盖所有数据)
|
||||
const top20Percent = Math.ceil(totalCount * 0.2);
|
||||
const middle60Percent = Math.ceil(totalCount * 0.8);
|
||||
|
||||
return sortedData.map((item, index) => {
|
||||
// 处理 value 为 0 的特殊情况
|
||||
if (item.value === 0) {
|
||||
return {
|
||||
...item,
|
||||
itemStyle: {
|
||||
color: "#4D96FA",
|
||||
opacity: 0.5,
|
||||
},
|
||||
}; // 蓝色
|
||||
}
|
||||
|
||||
// 根据排名分配颜色
|
||||
if (index < top20Percent) {
|
||||
return {
|
||||
...item,
|
||||
itemStyle: {
|
||||
color: "#FF0000",
|
||||
opacity: 0.5,
|
||||
},
|
||||
}; // 红色 - 前20%
|
||||
} else if (index < middle60Percent) {
|
||||
return {
|
||||
...item,
|
||||
itemStyle: {
|
||||
color: "#FF9800",
|
||||
opacity: 0.5,
|
||||
},
|
||||
}; // 橙色 - 中间60%
|
||||
} else {
|
||||
return {
|
||||
...item,
|
||||
itemStyle: {
|
||||
color: "#4CAF50",
|
||||
opacity: 0.5,
|
||||
},
|
||||
}; // 绿色 - 后20%
|
||||
}
|
||||
});
|
||||
};
|
||||
86
src/utils/common.ts
Normal file
@@ -0,0 +1,86 @@
|
||||
import type { App } from 'vue'
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 获取资源完整地址
|
||||
* @param relativeUrl 资源相对地址
|
||||
* @param domain 指定域名
|
||||
*/
|
||||
export const fullUrl = (relativeUrl: string, domain = '') => {
|
||||
return domain + '/api/system-boot/image/toStream?bgImage=' + relativeUrl
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否是外部链接
|
||||
* @param path
|
||||
*/
|
||||
export function isExternal(path: string): boolean {
|
||||
return /^(https?|ftp|mailto|tel):/.test(path)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 字符串补位
|
||||
*/
|
||||
const padStart = (str: string, maxLength: number, fillString = ' ') => {
|
||||
if (str.length >= maxLength) return str
|
||||
|
||||
const fillLength = maxLength - str.length
|
||||
let times = Math.ceil(fillLength / fillString.length)
|
||||
while ((times >>= 1)) {
|
||||
fillString += fillString
|
||||
if (times === 1) {
|
||||
fillString += fillString
|
||||
}
|
||||
}
|
||||
return fillString.slice(0, fillLength) + str
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化时间戳
|
||||
* @param dateTime 时间戳
|
||||
* @param fmt 格式化方式,默认:yyyy-mm-dd hh:MM:ss
|
||||
*/
|
||||
export const timeFormat = (dateTime: string | number | null = null, fmt = 'yyyy-mm-dd hh:MM:ss') => {
|
||||
if (dateTime == 'none') return '-'
|
||||
if (!dateTime) dateTime = Number(new Date())
|
||||
if (dateTime.toString().length === 10) {
|
||||
dateTime = +dateTime * 1000
|
||||
}
|
||||
|
||||
const date = new Date(dateTime)
|
||||
let ret
|
||||
const opt: any = {
|
||||
'y+': date.getFullYear().toString(), // 年
|
||||
'm+': (date.getMonth() + 1).toString(), // 月
|
||||
'd+': date.getDate().toString(), // 日
|
||||
'h+': date.getHours().toString(), // 时
|
||||
'M+': date.getMinutes().toString(), // 分
|
||||
's+': date.getSeconds().toString() // 秒
|
||||
}
|
||||
for (const k in opt) {
|
||||
ret = new RegExp('(' + k + ')').exec(fmt)
|
||||
if (ret) {
|
||||
fmt = fmt.replace(ret[1], ret[1].length == 1 ? opt[k] : padStart(opt[k], ret[1].length, '0'))
|
||||
}
|
||||
}
|
||||
return fmt
|
||||
}
|
||||
|
||||
/**
|
||||
* el-form 密码正则校验 密码需要包含特殊字符字母数字,长度为8-16
|
||||
*/
|
||||
export const validatePwd = (rule: any, value: string, callback: any) => {
|
||||
if (value === '') {
|
||||
callback(new Error('请输入密码'))
|
||||
} else {
|
||||
const reg = /^(?=.*[A-Za-z])(?=.*\d)(?=.*[@#$%^&+=!])(?=.*[a-zA-Z0-9])[A-Za-z\d@#$%^&+=!]{8,16}$/
|
||||
if (!reg.test(value)) {
|
||||
callback(new Error('密码需要包含特殊字符字母数字,长度为8-16'))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
159
src/utils/echartMethod.ts
Normal file
@@ -0,0 +1,159 @@
|
||||
const dataProcessing = (arr: any[]) => {
|
||||
return arr
|
||||
.filter(item => typeof item === 'number' || (typeof item === 'string' && !isNaN(parseFloat(item))))
|
||||
.map(item => (typeof item === 'number' ? item : parseFloat(item)))
|
||||
}
|
||||
const calculateValue = (o: number, value: number, num: number, isMin: boolean) => {
|
||||
if (value === 0) {
|
||||
return 0
|
||||
} else if (value > 0 && Math.abs(value) < 1 && isMin == true) {
|
||||
return 0
|
||||
} else if (value > -1 && value < 0 && isMin == false) {
|
||||
return 0
|
||||
}
|
||||
let base
|
||||
if (Math.abs(o) >= 100) {
|
||||
base = 100
|
||||
} else if (Math.abs(o) >= 10) {
|
||||
base = 10
|
||||
} else if (Math.abs(o) >= 1) {
|
||||
base = 1
|
||||
} else {
|
||||
base = 0.1
|
||||
}
|
||||
let calculatedValue
|
||||
if (isMin) {
|
||||
if (value < 0) {
|
||||
calculatedValue = value + num * value
|
||||
} else {
|
||||
calculatedValue = value - num * value
|
||||
}
|
||||
} else {
|
||||
if (value < 0) {
|
||||
calculatedValue = value - num * value
|
||||
} else {
|
||||
calculatedValue = value + num * value
|
||||
}
|
||||
}
|
||||
if (base === 0.1) {
|
||||
return parseFloat(calculatedValue.toFixed(1))
|
||||
} else if (isMin) {
|
||||
return Math.floor(calculatedValue / base) * base
|
||||
} else {
|
||||
return Math.ceil(calculatedValue / base) * base
|
||||
}
|
||||
}
|
||||
|
||||
// 处理y轴最大最小值
|
||||
export const yMethod = (arr: any) => {
|
||||
let num = 0.1
|
||||
let numList = dataProcessing(arr)
|
||||
let maxValue = 0
|
||||
let minValue = 0
|
||||
let max = 0
|
||||
let min = 0
|
||||
maxValue = Math.max(...numList)
|
||||
minValue = Math.min(...numList)
|
||||
const o = maxValue - minValue
|
||||
if (Math.abs(o) >= 300) {
|
||||
num = 0.02
|
||||
}
|
||||
|
||||
min = calculateValue(o, minValue, num, true)
|
||||
max = calculateValue(o, maxValue, num, false)
|
||||
// if (-100 >= minValue) {
|
||||
// min = Math.floor((minValue + num * minValue) / 100) * 100
|
||||
// } else if (-10 >= minValue && minValue > -100) {
|
||||
// min = Math.floor((minValue + num * minValue) / 10) * 10
|
||||
// } else if (-1 >= minValue && minValue > -10) {
|
||||
// min = Math.floor(minValue + num * minValue)
|
||||
// } else if (0 > minValue && minValue > -1) {
|
||||
// min = parseFloat((minValue + num * minValue).toFixed(1))
|
||||
// } else if (minValue == 0) {
|
||||
// min = 0
|
||||
// } else if (0 < minValue && minValue < 1) {
|
||||
// min = parseFloat((minValue - num * minValue).toFixed(1))
|
||||
// } else if (1 <= minValue && minValue < 10) {
|
||||
// min = Math.floor(minValue - num * minValue)
|
||||
// } else if (10 <= minValue && minValue < 100) {
|
||||
// min = Math.floor((minValue - num * minValue) / 10) * 10
|
||||
// } else if (100 <= minValue) {
|
||||
// min = Math.floor((minValue - num * minValue) / 100) * 100
|
||||
// }
|
||||
|
||||
// if (-100 >= maxValue) {
|
||||
// max = Math.ceil((maxValue - num * maxValue) / 100) * 100
|
||||
// } else if (-10 >= maxValue && maxValue > -100) {
|
||||
// max = Math.ceil((maxValue - num * maxValue) / 10) * 10
|
||||
// } else if (-1 >= maxValue && maxValue > -10) {
|
||||
// max = Math.ceil(maxValue - num * maxValue)
|
||||
// } else if (0 > maxValue && maxValue > -1) {
|
||||
// max = parseFloat((maxValue - num * maxValue).toFixed(1))
|
||||
// } else if (maxValue == 0) {
|
||||
// max = 0
|
||||
// } else if (0 < maxValue && maxValue < 1) {
|
||||
// max = parseFloat((maxValue + num * maxValue).toFixed(1))
|
||||
// } else if (1 <= maxValue && maxValue < 10) {
|
||||
// max = Math.ceil(maxValue + num * maxValue)
|
||||
// } else if (10 <= maxValue && maxValue < 100) {
|
||||
// max = Math.ceil((maxValue + num * maxValue) / 10) * 10
|
||||
// } else if (100 <= maxValue) {
|
||||
// max = Math.ceil((maxValue + num * maxValue) / 100) * 100
|
||||
// }
|
||||
|
||||
// if (maxValue > 1000 || minValue < -1000) {
|
||||
// max = Math.ceil(maxValue / 100) * 100
|
||||
// if (minValue == 0) {
|
||||
// min = 0
|
||||
// } else {
|
||||
// min = Math.floor(minValue / 100) * 100
|
||||
// }
|
||||
// } else if (maxValue < 60 && minValue > 40) {
|
||||
// max = 60
|
||||
// min = 40
|
||||
// } else if (maxValue == minValue && maxValue < 10 && minValue > 0) {
|
||||
// max = Math.ceil(maxValue / 10) * 10
|
||||
// min = Math.floor(minValue / 10) * 10
|
||||
// } else if (maxValue == minValue && maxValue != 0 && minValue != 0) {
|
||||
// max = Math.ceil(maxValue / 10 + 1) * 10
|
||||
// min = Math.floor(minValue / 10 - 1) * 10
|
||||
// } else {
|
||||
// max = Math.ceil(maxValue / 10) * 10
|
||||
// min = Math.floor(minValue / 10) * 10
|
||||
// }
|
||||
|
||||
// if (maxValue > 0 && maxValue < 1) {
|
||||
// max = 1
|
||||
// } else if (max == 0 && minValue > -1 && minValue < 0) {
|
||||
// min = -1
|
||||
// }
|
||||
|
||||
return [min, max]
|
||||
}
|
||||
|
||||
/**
|
||||
* title['A相','B相',]
|
||||
* data[[1,2],[3,4]]
|
||||
*/
|
||||
// 导出csv文件
|
||||
const convertToCSV = (title: any, data: any) => {
|
||||
console.log('🚀 ~ convertToCSV ~ data:', data)
|
||||
let csv = ''
|
||||
// 添加列头
|
||||
csv += ',' + title.join(',') + '\n'
|
||||
// 遍历数据并添加到CSV字符串中
|
||||
data?.map(item => {
|
||||
csv += item.join(',') + '\n'
|
||||
})
|
||||
return csv
|
||||
}
|
||||
export const exportCSV = (title: object, data: any, filename: string) => {
|
||||
const csv = convertToCSV(title, data)
|
||||
const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' })
|
||||
const link = document.createElement('a')
|
||||
link.href = URL.createObjectURL(blob)
|
||||
link.download = filename
|
||||
link.click()
|
||||
// 释放URL对象
|
||||
URL.revokeObjectURL(link.href)
|
||||
}
|
||||
21
src/utils/error-code-type.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
// 封装请求错误代码提示
|
||||
export const errorCodeType = function (code: string): string {
|
||||
let msg: string = ""
|
||||
switch (code) {
|
||||
case "401":
|
||||
msg = '认证失败,无法访问系统资源'
|
||||
break;
|
||||
case "403":
|
||||
msg = '当前操作没有权限'
|
||||
break;
|
||||
case "404":
|
||||
msg = '访问资源不存在'
|
||||
break;
|
||||
case "default":
|
||||
msg = '系统未知错误,请反馈给管理员'
|
||||
break;
|
||||
default:
|
||||
return '未知错误,请联系管理员'
|
||||
}
|
||||
return msg
|
||||
}
|
||||
115
src/utils/index.ts
Normal file
@@ -0,0 +1,115 @@
|
||||
import { ElSkeletonItem } from "element-plus";
|
||||
|
||||
/**
|
||||
* @param {date} time 需要转换的时间
|
||||
* @param {String} fmt 需要转换的格式 如 yyyy-MM-dd、yyyy-MM-dd HH:mm:ss
|
||||
* @returns {String}
|
||||
*/
|
||||
export const formatTime = (time: string | Date, fmt: string): string => {
|
||||
if (!time) return "";
|
||||
const date = new Date(time);
|
||||
const o = {
|
||||
"M+": date.getMonth() + 1,
|
||||
"d+": date.getDate(),
|
||||
"H+": date.getHours(),
|
||||
"m+": date.getMinutes(),
|
||||
"s+": date.getSeconds(),
|
||||
"q+": Math.floor((date.getMonth() + 3) / 3),
|
||||
S: date.getMilliseconds(),
|
||||
};
|
||||
if (/(y+)/.test(fmt))
|
||||
fmt = fmt.replace(
|
||||
RegExp.$1,
|
||||
(date.getFullYear() + "").substr(4 - RegExp.$1.length)
|
||||
);
|
||||
for (const k in o) {
|
||||
if (new RegExp("(" + k + ")").test(fmt)) {
|
||||
fmt = fmt.replace(
|
||||
RegExp.$1,
|
||||
// @ts-ignore: Unreachable code error
|
||||
RegExp.$1.length === 1 ? o[k] : ("00" + o[k]).substr(("" + o[k]).length)
|
||||
);
|
||||
}
|
||||
}
|
||||
return fmt;
|
||||
};
|
||||
|
||||
export const speak = (content: any, onComplete: Function = () => {}) => {
|
||||
// 检查浏览器是否支持语音合成
|
||||
if (!window.speechSynthesis) {
|
||||
console.warn("当前浏览器不支持语音合成功能");
|
||||
return;
|
||||
}
|
||||
|
||||
const utterance = new SpeechSynthesisUtterance(content);
|
||||
utterance.lang = "zh-CN";
|
||||
utterance.rate = 1;
|
||||
utterance.rate = 1.5;
|
||||
|
||||
// 设置结束回调(通过参数传递)
|
||||
utterance.onend = () => {
|
||||
onComplete();
|
||||
};
|
||||
|
||||
// 播放语音
|
||||
window.speechSynthesis.speak(utterance);
|
||||
};
|
||||
// 关闭语音播报
|
||||
export const stopSpeak = () => {
|
||||
(document.getElementById("audioId") as HTMLAudioElement)?.pause();
|
||||
window.speechSynthesis.cancel();
|
||||
};
|
||||
/**
|
||||
* 获取时间范围
|
||||
* @param {number} option - 1表示本周,3表示本月
|
||||
* @returns {{start: Date, end: Date}} - 包含开始时间和结束时间的对象
|
||||
*/
|
||||
export const getDateRange = (option: number) => {
|
||||
console.log("🚀 ~ getDateRange ~ option:", option);
|
||||
const now = new Date();
|
||||
if (option === 1) {
|
||||
//年
|
||||
const firstDay = new Date(now.getFullYear(), 0, 1);
|
||||
|
||||
const lastDay = new Date(now.getFullYear(), now.getMonth(), now.getDate());
|
||||
|
||||
return formatDate(firstDay) + " 至 " + formatDate(lastDay);
|
||||
}
|
||||
ElSkeletonItem;
|
||||
if (option === 3) {
|
||||
// 获取本月日期范围
|
||||
// 本月第一天
|
||||
const firstDay = new Date(now.getFullYear(), now.getMonth(), 1);
|
||||
|
||||
// 本月最后一天
|
||||
const lastDay = new Date(now.getFullYear(), now.getMonth() + 1, 0);
|
||||
|
||||
return formatDate(firstDay) + " 至 " + formatDate(lastDay);
|
||||
} else if (option === 4) {
|
||||
// 获取本周日期范围
|
||||
// 计算周一的日期
|
||||
const monday = new Date(now);
|
||||
monday.setDate(now.getDate() - now.getDay() + 1);
|
||||
|
||||
// 计算周日的日期
|
||||
const sunday = new Date(now);
|
||||
sunday.setDate(now.getDate() - now.getDay() + 7);
|
||||
|
||||
return formatDate(monday) + " 至 " + formatDate(sunday);
|
||||
} else if (option === 6) {
|
||||
// 获取本月日期范围
|
||||
// 本月第一天
|
||||
return formatDate(now);
|
||||
}
|
||||
};
|
||||
/**
|
||||
* 格式化日期为YYYY-MM-DD格式
|
||||
* @param {Date} date - 要格式化的日期对象
|
||||
* @returns {string} - 格式化后的日期字符串
|
||||
*/
|
||||
function formatDate(date: Date) {
|
||||
const year = date.getFullYear();
|
||||
const month = String(date.getMonth() + 1).padStart(2, "0");
|
||||
const day = String(date.getDate()).padStart(2, "0");
|
||||
return `${year}-${month}-${day}`;
|
||||
}
|
||||
11
src/utils/page-params.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
// 全局统一分页参数类型声明
|
||||
declare interface PageParams {
|
||||
pageNum: number,
|
||||
pageSize: number,
|
||||
type?: Model, // 可选参数
|
||||
readonly sort?: string // 只读可选参数
|
||||
}
|
||||
interface Model {
|
||||
type?: string
|
||||
}
|
||||
export default PageParams;
|
||||
172
src/utils/request.ts
Normal file
@@ -0,0 +1,172 @@
|
||||
import axios from "axios";
|
||||
import { errorCodeType } from "./error-code-type";
|
||||
import { ElMessage } from "element-plus";
|
||||
import store from "@/store/index";
|
||||
|
||||
const service = axios.create({
|
||||
baseURL: "/api",
|
||||
timeout: 60 * 1000,
|
||||
headers: { "Content-Type": "application/json;charset=utf-8" },
|
||||
});
|
||||
|
||||
// 认证状态管理
|
||||
let isAuthenticated = false;
|
||||
let isAuthenticating = false;
|
||||
const requestQueue: Array<() => void> = [];
|
||||
|
||||
// 执行队列中的请求
|
||||
const executeQueue = () => {
|
||||
while (requestQueue.length > 0) {
|
||||
const nextRequest = requestQueue.shift();
|
||||
if (nextRequest) nextRequest();
|
||||
}
|
||||
};
|
||||
|
||||
// 请求拦截器
|
||||
service.interceptors.request.use(
|
||||
(config) => {
|
||||
const isAuthRequest = config.url === "/cn_authenticate";
|
||||
|
||||
if (!isAuthRequest) {
|
||||
if (isAuthenticating) {
|
||||
return new Promise((resolve) => {
|
||||
requestQueue.push(() => {
|
||||
resolve(service(config));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const token = window.sessionStorage.getItem("token");
|
||||
if (token) {
|
||||
config.headers["Authorization"] = `Bearer ${token}`;
|
||||
}
|
||||
|
||||
if (!isAuthenticated) {
|
||||
return new Promise((resolve) => {
|
||||
requestQueue.push(() => {
|
||||
resolve(service(config));
|
||||
});
|
||||
});
|
||||
}
|
||||
} else {
|
||||
if (isAuthenticating) {
|
||||
return Promise.reject({ message: "认证中", isHandled: true });
|
||||
}
|
||||
isAuthenticating = true;
|
||||
config.headers["Authorization"] = "";
|
||||
}
|
||||
|
||||
// get请求参数处理(保持原逻辑)
|
||||
// if (config.method === "get" && config.params) {
|
||||
// // 这里用 toLowerCase() 确保兼容
|
||||
// let url = config.url + "?";
|
||||
// for (const propName of Object.keys(config.params)) {
|
||||
// const value = config.params[propName];
|
||||
// const part = encodeURIComponent(propName) + "=";
|
||||
// if (value !== null && typeof value !== "undefined") {
|
||||
// if (typeof value === "object") {
|
||||
// for (const key of Object.keys(value)) {
|
||||
// const params = propName + "[" + key + "]";
|
||||
// const subPart = encodeURIComponent(params) + "=";
|
||||
// url += subPart + encodeURIComponent(value[key]) + "&";
|
||||
// }
|
||||
// } else {
|
||||
// url += part + encodeURIComponent(value) + "&";
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// url = url.slice(0, -1);
|
||||
// config.params = {};
|
||||
// config.url = url;
|
||||
// }
|
||||
return config;
|
||||
},
|
||||
(error) => {
|
||||
console.log(error);
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
|
||||
// 响应拦截器(保持原逻辑,略作兼容处理)
|
||||
let refreshQueue: any = [];
|
||||
let isRefreshing = false;
|
||||
|
||||
service.interceptors.response.use(
|
||||
(res) => {
|
||||
if (res.config.url === "/cn_authenticate") {
|
||||
isAuthenticating = false;
|
||||
|
||||
if (res.data.code === "A0000") {
|
||||
isAuthenticated = true;
|
||||
setTimeout(executeQueue, 1000);
|
||||
} else {
|
||||
isAuthenticated = false;
|
||||
setTimeout(executeQueue, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
const code = res.data["code"] || "A0000";
|
||||
const msg =
|
||||
errorCodeType(code) || res.data["msg"] || errorCodeType("default");
|
||||
|
||||
if (code === "A0000") {
|
||||
return Promise.resolve(res.data);
|
||||
} else if (code === "A0025") {
|
||||
isAuthenticated = false;
|
||||
const originalRequest = res.config;
|
||||
|
||||
if (!isRefreshing) {
|
||||
isRefreshing = true;
|
||||
return store
|
||||
.dispatch(
|
||||
"loginAction",
|
||||
JSON.parse(window.localStorage.getItem("adminInfo") || "{}")
|
||||
)
|
||||
.then(() => {
|
||||
isAuthenticated = true;
|
||||
refreshQueue.forEach((callback: any) => callback());
|
||||
refreshQueue = [];
|
||||
return service(originalRequest);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error("刷新失败:", err);
|
||||
refreshQueue.forEach((callback: any) => callback());
|
||||
refreshQueue = [];
|
||||
return Promise.reject(err);
|
||||
})
|
||||
.finally(() => {
|
||||
isRefreshing = false;
|
||||
});
|
||||
} else {
|
||||
return new Promise((resolve) => {
|
||||
refreshQueue.push(() => resolve(service(originalRequest)));
|
||||
});
|
||||
}
|
||||
} else {
|
||||
return Promise.reject(res.data);
|
||||
}
|
||||
},
|
||||
(error) => {
|
||||
if (error.isHandled) return Promise.reject(error);
|
||||
|
||||
if (error.config?.url === "/cn_authenticate") {
|
||||
isAuthenticating = false;
|
||||
isAuthenticated = false;
|
||||
setTimeout(executeQueue, 1000);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
);
|
||||
|
||||
export default service;
|
||||
63
src/utils/request_wx.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
import axios from 'axios'
|
||||
import type { AxiosInstance, AxiosRequestConfig } from 'axios'
|
||||
|
||||
class HttpRequest {
|
||||
private readonly baseUrl: string
|
||||
constructor() {
|
||||
this.baseUrl = '/api'
|
||||
}
|
||||
getInsideConfig() {
|
||||
const config = {
|
||||
baseURL: this.baseUrl, // 所有的请求地址前缀部分(没有后端请求不用写)
|
||||
timeout: 80000 // 请求超时时间(毫秒)
|
||||
}
|
||||
return config
|
||||
}
|
||||
|
||||
// 请求拦截
|
||||
interceptors(instance: AxiosInstance, url: string | number | undefined) {
|
||||
instance.interceptors.request.use(
|
||||
config => {
|
||||
// 添加全局的loading..
|
||||
// config.headers['Authorization'] =
|
||||
// 'bearer ' + JSON.parse(window.localStorage.getItem('adminInfo') || '{}').access_token; // 请求头带上token token要在登录的时候保存在localStorage中
|
||||
// console.log(
|
||||
// "🚀 ~ requestHandler ~ config.headers['Authorization']:",
|
||||
// JSON.parse(window.localStorage.getItem('adminInfo') || '{}'),
|
||||
// config.headers
|
||||
// );
|
||||
|
||||
config.headers['Authorization'] =
|
||||
'bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySW5kZXgiOiJmYTM3YjkzY2M5MGQ0YzE3ODRjYThmNmRlYmRkZWUxYSIsInVzZXJfbmFtZSI6InJvb3QiLCJzY29wZSI6WyJhbGwiXSwibmlja25hbWUiOiLotoXnuqfnrqHnkIblkZgiLCJ1c2VyVHlwZSI6MCwiZGVwdEluZGV4IjoiNTY5OWU1OTE2YTE4YTYzODFlMWFjOTJkYTViZDI2MjgiLCJleHAiOjE4MjE4MTc2MTksImF1dGhvcml0aWVzIjpbInJvb3QiXSwianRpIjoiMmJiM2Q5ZTYtNmY3Yy00Yjg1LThiM2EtZDI2ODdmMTUzMDg5IiwiY2xpZW50X2lkIjoibmpjbnRlc3QiLCJoZWFkU2N1bHB0dXJlIjoicmVzb3VyY2VEYXRhLzMxNzRDRUFFOUQ0MjRGMjJCQjkxQTU4OURENjdCMDUxLmpwZyJ9.WjeYl1lvvJdDE1FUGIhS99rE5qKaBXOypWxmxK0svWweGqEbu1XCLjKm_YkiTwjZJ_oIcn5JOO9rvHFkkea76BUsYo5wlzuBBiy7sKqM1fFzOFQq6hdFevNTJAbYH9FiBxYxI-e9DZ5mvLGE6umOjUfn_FAsku2w6Uj5DtvpOKBWYzLEPTEifOqNI9he4zJAmVZniUUMf26SDoEdfu0TyrIS1j_qKaEb-cqR1XDhivdthEBK5m9vxJyXFZ5kofNxwQQkit_oiqJRkCZIt9TWAjCh-frzMHCvA30hkAr-VCD2JfCmmEr3hW_lmwfINaPtFVbHCdCKqdrl6VmF1HObaQ'
|
||||
// 请求头携带token
|
||||
return config
|
||||
},
|
||||
(error: any) => {
|
||||
return Promise.reject(error)
|
||||
}
|
||||
)
|
||||
//响应拦截
|
||||
instance.interceptors.response.use(
|
||||
res => {
|
||||
//返回数据
|
||||
const { data } = res
|
||||
// console.log('返回数据处理', res)
|
||||
return data
|
||||
},
|
||||
(error: any) => {
|
||||
console.log('error==>', error)
|
||||
return Promise.reject(error)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
request(options: AxiosRequestConfig) {
|
||||
const instance = axios.create()
|
||||
options = Object.assign(this.getInsideConfig(), options)
|
||||
this.interceptors(instance, options.url)
|
||||
return instance(options)
|
||||
}
|
||||
}
|
||||
|
||||
const http = new HttpRequest()
|
||||
export default http
|
||||
71
src/utils/useDraw.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
import { ref } from "vue";
|
||||
|
||||
export default function useDraw() {
|
||||
// 指向最外层容器
|
||||
const appRef = ref();
|
||||
// 定时函数
|
||||
const timer:any = ref(0);
|
||||
// 默认缩放值
|
||||
const scale = {
|
||||
width: "1",
|
||||
height: "1",
|
||||
};
|
||||
// 设计稿尺寸(px)
|
||||
const baseWidth = 1920;
|
||||
const baseHeight = 1080;
|
||||
|
||||
// 需保持的比例
|
||||
const baseProportion = parseFloat((baseWidth / baseHeight).toFixed(5));
|
||||
const calcRate = () => {
|
||||
// 当前宽高比
|
||||
const currentRate = parseFloat(
|
||||
(window.innerWidth / window.innerHeight).toFixed(5)
|
||||
);
|
||||
if (appRef.value) {
|
||||
if (currentRate > baseProportion) {
|
||||
// 表示更宽
|
||||
scale.width = (
|
||||
(window.innerHeight * baseProportion) /
|
||||
baseWidth
|
||||
).toFixed(5);
|
||||
scale.height = (window.innerHeight / baseHeight).toFixed(5);
|
||||
appRef.value.style.transform = `scale(${scale.width}, ${scale.height}) translate(-50%, -50%)`;
|
||||
} else {
|
||||
// 表示更高
|
||||
scale.height = (
|
||||
window.innerWidth /
|
||||
baseProportion /
|
||||
baseHeight
|
||||
).toFixed(5);
|
||||
scale.width = (window.innerWidth / baseWidth).toFixed(5);
|
||||
appRef.value.style.transform = `scale(${scale.width}, ${scale.height}) translate(-50%, -50%)`;
|
||||
}
|
||||
window.sessionStorage.setItem("scaleWidth", scale.width);
|
||||
window.sessionStorage.setItem("scaleheight", scale.height);
|
||||
}
|
||||
};
|
||||
|
||||
const resize = () => {
|
||||
clearTimeout(timer.value);
|
||||
timer.value = setTimeout(() => {
|
||||
calcRate();
|
||||
}, 200);
|
||||
};
|
||||
|
||||
// 改变窗口大小重新绘制
|
||||
const windowDraw = () => {
|
||||
window.addEventListener("resize", resize);
|
||||
};
|
||||
|
||||
// 改变窗口大小重新绘制
|
||||
const unWindowDraw = () => {
|
||||
window.removeEventListener("resize", resize);
|
||||
};
|
||||
|
||||
return {
|
||||
appRef,
|
||||
calcRate,
|
||||
windowDraw,
|
||||
unWindowDraw,
|
||||
};
|
||||
}
|
||||
210
src/utils/webSocketClient.ts
Normal file
@@ -0,0 +1,210 @@
|
||||
import { ElMessage, EVENT_CODE } from "element-plus";
|
||||
|
||||
// 定义消息类型,用于类型检查
|
||||
type MessageType = {
|
||||
[key: string]: any;
|
||||
};
|
||||
|
||||
export default class SocketService {
|
||||
// 单例模式实例
|
||||
private static instance: SocketService | null = null;
|
||||
// 和服务端连接的socket对象
|
||||
private ws: WebSocket | null = null;
|
||||
// 存储回调函数
|
||||
private callBackMapping: {
|
||||
[key: string]: ((message: MessageType) => void) | null;
|
||||
} = {};
|
||||
// 标识是否连接成功
|
||||
private connected: boolean = false;
|
||||
// 记录重试的次数
|
||||
private sendRetryCount: number = 0;
|
||||
// 重新连接尝试的次数
|
||||
private connectRetryCount: number = 0;
|
||||
// Web Worker 实例
|
||||
private work: Worker | null = null;
|
||||
// 临时的 Blob URL
|
||||
private workerBlobUrl: string | null = null;
|
||||
// 上次活动时间戳
|
||||
private lastActivityTime: number = 0;
|
||||
// 最后一次收到心跳回复时间
|
||||
private lastResponseHeartTime: number = Date.now();
|
||||
// 重新连接延迟,单位毫秒
|
||||
private reconnectDelay: number = 5000;
|
||||
|
||||
// 单例模式获取实例
|
||||
public static get Instance(): SocketService {
|
||||
if (!this.instance) {
|
||||
this.instance = new SocketService();
|
||||
}
|
||||
return this.instance;
|
||||
}
|
||||
|
||||
// 定义连接服务器的方法
|
||||
public async connect(id: any) {
|
||||
if (!window.WebSocket) {
|
||||
console.log("您的浏览器不支持WebSocket");
|
||||
return;
|
||||
}
|
||||
|
||||
// const response = await fetch("/vue/");
|
||||
const response = await fetch("");
|
||||
|
||||
const mqttUrl = response.headers.get("X-Mqtt-Url");
|
||||
console.log("🚀 ~ SocketService ~ connect ~ mqttUrl:", mqttUrl);
|
||||
setTimeout(() => {
|
||||
//"ws://10.156.193.182:18093/ws/screen" 北京
|
||||
//"ws://192.168.1.130:19001/ws/askRealTimeData/" 无锡
|
||||
const url = (mqttUrl || "ws://192.168.1.127:19001/ws/") + id;
|
||||
// const url = (mqttUrl || "ws://192.168.1.63:18093/ws/screen") + id;
|
||||
console.log("🚀 ~ SocketService ~ connect ~ url:", url);
|
||||
this.ws = new WebSocket(url);
|
||||
|
||||
this.ws.onopen = () => this.handleOpen();
|
||||
this.ws.onclose = () => this.handleClose();
|
||||
this.ws.onerror = () => this.handleError();
|
||||
this.ws.onmessage = (event) => this.handleMessage(event);
|
||||
}, 0);
|
||||
}
|
||||
|
||||
// 处理连接成功事件
|
||||
private handleOpen(): void {
|
||||
ElMessage.success("webSocket连接服务端成功了");
|
||||
console.log("连接服务端成功了");
|
||||
this.connected = true;
|
||||
this.connectRetryCount = 0;
|
||||
this.updateLastActivityTime();
|
||||
this.startHeartbeat();
|
||||
}
|
||||
|
||||
// 处理连接关闭事件
|
||||
private handleClose(): void {
|
||||
console.log("连接webSocket服务端关闭");
|
||||
this.connected = false;
|
||||
this.connectRetryCount++;
|
||||
this.clearHeartbeat();
|
||||
// 可根据需要添加重连逻辑
|
||||
// setTimeout(() => this.connect(), 500 * this.connectRetryCount);
|
||||
}
|
||||
|
||||
// 处理连接错误事件
|
||||
private handleError(): void {
|
||||
ElMessage.error("webSocket连接异常!");
|
||||
}
|
||||
|
||||
// 处理服务端发送过来的数据
|
||||
private handleMessage(event: MessageEvent): void {
|
||||
// console.log('🚀 ~ SocketService ~ handleMessage ~ event.data:', event.data)
|
||||
|
||||
if (event.data == "连接成功") {
|
||||
this.sendHeartbeat();
|
||||
} else if (event.data.length > 10) {
|
||||
let message: MessageType;
|
||||
try {
|
||||
// console.log(
|
||||
// 'Received message:', event.data)
|
||||
message = JSON.parse(event.data);
|
||||
this.callBackMapping["message"]!(message);
|
||||
} catch (e) {
|
||||
// console.error("消息解析失败", event.data, e);
|
||||
return;
|
||||
}
|
||||
// console.log("🚀 ~ SocketService ~ handleMessage ~ message:", message)
|
||||
|
||||
// this.callBackMapping["message"]!(message);
|
||||
} else {
|
||||
// ElMessage.error(event.data);
|
||||
}
|
||||
}
|
||||
|
||||
// 启动心跳检测
|
||||
private startHeartbeat(): void {
|
||||
this.lastResponseHeartTime = Date.now();
|
||||
const url = window.URL.createObjectURL(
|
||||
new Blob([
|
||||
"(function(e){setInterval(function(){this.postMessage(null)},30000)})()",
|
||||
])
|
||||
);
|
||||
this.workerBlobUrl = url;
|
||||
this.work = new Worker(url);
|
||||
this.work.onmessage = (e) => this.handleWorkerMessage(e);
|
||||
}
|
||||
|
||||
// 处理 Web Worker 消息
|
||||
private handleWorkerMessage(e: MessageEvent): void {
|
||||
// if (this.lastActivityTime - this.lastResponseHeartTime > 30000) {
|
||||
// // 说明已经三轮心跳没收到回复了,关闭检测,提示用户。
|
||||
// // ElMessage.error('业务主体模块发生未知异常,请尝试重新启动!')
|
||||
// this.clearHeartbeat()
|
||||
// return
|
||||
// }
|
||||
// console.log(123);
|
||||
|
||||
this.sendHeartbeat();
|
||||
}
|
||||
|
||||
// 发送心跳消息
|
||||
private sendHeartbeat(): void {
|
||||
// console.log(new Date() + '进入心跳消息发送。。。。。。。。。。。。。')
|
||||
if (this.ws) {
|
||||
this.ws.send("alive");
|
||||
this.updateLastActivityTime();
|
||||
}
|
||||
}
|
||||
|
||||
// 更新活动时间
|
||||
private updateLastActivityTime(): void {
|
||||
this.lastActivityTime = Date.now();
|
||||
}
|
||||
|
||||
// 清除心跳检测
|
||||
private clearHeartbeat(): void {
|
||||
if (this.work) {
|
||||
this.work.terminate();
|
||||
this.work = null;
|
||||
}
|
||||
if (this.workerBlobUrl) {
|
||||
window.URL.revokeObjectURL(this.workerBlobUrl);
|
||||
this.workerBlobUrl = null;
|
||||
}
|
||||
}
|
||||
|
||||
// 回调函数的注册
|
||||
public registerCallBack(
|
||||
socketType: string,
|
||||
callBack: (message: MessageType) => void
|
||||
): void {
|
||||
this.callBackMapping[socketType] = callBack;
|
||||
}
|
||||
|
||||
// 取消某一个回调函数
|
||||
public unRegisterCallBack(socketType: string): void {
|
||||
this.callBackMapping[socketType] = null;
|
||||
}
|
||||
|
||||
// 发送数据的方法
|
||||
public send(data: any): void {
|
||||
if (this.connected) {
|
||||
this.sendRetryCount = 0;
|
||||
try {
|
||||
if (this.ws) {
|
||||
this.ws.send(JSON.stringify(data));
|
||||
}
|
||||
} catch (e) {
|
||||
if (this.ws) {
|
||||
this.ws.send(data);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.sendRetryCount++;
|
||||
setTimeout(() => this.send(data), this.sendRetryCount * 500);
|
||||
}
|
||||
}
|
||||
|
||||
// 断开方法
|
||||
public closeWs(): void {
|
||||
if (this.connected && this.ws) {
|
||||
this.ws.close();
|
||||
}
|
||||
console.log("执行WS关闭命令..");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
<!-- 谐波放大表格详情 -->
|
||||
<template>
|
||||
<el-dialog
|
||||
v-model="visible"
|
||||
:close-on-click-modal="false"
|
||||
title="详情"
|
||||
draggable
|
||||
width="1000px"
|
||||
@close="handleCloseDialog"
|
||||
style="height: 600px"
|
||||
>
|
||||
<MyEChart :options="[]" />
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from "vue";
|
||||
import MyEChart from "@/components/echarts/MyEchart.vue";
|
||||
|
||||
const visible = ref(false);
|
||||
const options = ref<any>(null);
|
||||
|
||||
const open = () => {
|
||||
visible.value = true;
|
||||
}
|
||||
|
||||
const handleCloseDialog = () => {
|
||||
visible.value = false;
|
||||
options.value = null;
|
||||
};
|
||||
|
||||
// const showCharts = (row: any, valueType: number, name: string) => {
|
||||
// getHistoryLineData({
|
||||
// lineId: row.lineId,
|
||||
// number: row.number,
|
||||
// phaseType: row.phaseType,
|
||||
// searchTime: row.timeId,
|
||||
// targetCode: row.targetCode,
|
||||
// valueType,
|
||||
// }).then((res) => {
|
||||
// options.value = {
|
||||
// title: {
|
||||
// text:
|
||||
// row.subName +
|
||||
// " " +
|
||||
// row.lineName +
|
||||
// " " +
|
||||
// row.targetName +
|
||||
// " " +
|
||||
// row.phaseType +
|
||||
// "相" +
|
||||
// name,
|
||||
// },
|
||||
// legend: {
|
||||
// show: false,
|
||||
// },
|
||||
// xAxis: {
|
||||
// type: "category",
|
||||
// name: "时间",
|
||||
// data: res.data[0]?.value.map((item: any[]) => item[0]),
|
||||
// },
|
||||
// yAxis: {
|
||||
// name: "%",
|
||||
// type: "value",
|
||||
// },
|
||||
// series: [
|
||||
// {
|
||||
// name: name,
|
||||
// data: res.data[0]?.value.map((item: any[]) => item[1]),
|
||||
// type: "line",
|
||||
// },
|
||||
// ],
|
||||
// options: {
|
||||
// grid: {
|
||||
// top: "50px",
|
||||
// left: "40px",
|
||||
// right: "60px",
|
||||
// bottom: "10px",
|
||||
// containLabel: true,
|
||||
// },
|
||||
// dataZoom: null,
|
||||
// },
|
||||
// };
|
||||
// });
|
||||
// };
|
||||
defineExpose({
|
||||
open,
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,68 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
v-model="visible"
|
||||
title="责任度计算"
|
||||
draggable
|
||||
width="1700px"
|
||||
style="height: 875px"
|
||||
@close="handleClose"
|
||||
:close-on-click-modal="false"
|
||||
>
|
||||
<div class="currentPosition">
|
||||
当前位置:{{ alias || "" }}
|
||||
</div>
|
||||
<el-tabs v-model="activeName" class="demo-tabs">
|
||||
<el-tab-pane label="系统" name="1">
|
||||
<system @setTitle="setTitle" v-if="activeName == '1'" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="配网" name="2">
|
||||
<distributionNetwork @setTitle="setTitle" v-if="activeName == '2'" />
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted, reactive } from "vue";
|
||||
import distributionNetwork from "./distributionNetwork.vue";
|
||||
import system from "./system.vue";
|
||||
|
||||
const visible = ref(false);
|
||||
const alias = ref("");
|
||||
|
||||
const activeName = ref("1");
|
||||
|
||||
const openDialog = () => {
|
||||
visible.value = true;
|
||||
};
|
||||
|
||||
onMounted(() => {});
|
||||
const setTitle = (title: string) => {
|
||||
alias.value = title;
|
||||
};
|
||||
const emit = defineEmits(["showCalculation", "close-dialog"]); // 打开弹窗
|
||||
|
||||
const handleClose = () => {
|
||||
visible.value = false;
|
||||
// 通知父组件显示收集界面
|
||||
emit("showCalculation", false);
|
||||
emit("close-dialog"); // 关闭弹窗
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
openDialog,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@use "@/assets/scss/index.scss";
|
||||
|
||||
.currentPosition {
|
||||
position: absolute;
|
||||
top: 60px;
|
||||
right: 10px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
z-index: 10;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,337 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
v-model="visible"
|
||||
:close-on-click-modal="false"
|
||||
title="用采数据管理"
|
||||
draggable
|
||||
>
|
||||
<el-form label-width="auto" :model="form" inline class="formBox">
|
||||
<el-form-item label="关键字">
|
||||
<el-input
|
||||
v-model="form.searchValue"
|
||||
placeholder="关键字查询"
|
||||
clearable
|
||||
style="width: 180px"
|
||||
size="small"
|
||||
/>
|
||||
</el-form-item>
|
||||
<div class="mt5">
|
||||
<el-button
|
||||
type="primary"
|
||||
:icon="Search"
|
||||
size="small"
|
||||
@click="getTableData"
|
||||
>查询</el-button
|
||||
>
|
||||
<el-button type="primary" :icon="Plus" size="small" @click="uploadFile"
|
||||
>新增</el-button
|
||||
>
|
||||
<el-button
|
||||
type="primary"
|
||||
:icon="Download"
|
||||
@click="exportTable"
|
||||
size="small"
|
||||
>导出
|
||||
</el-button>
|
||||
</div>
|
||||
</el-form>
|
||||
|
||||
<div class="tableBox">
|
||||
<el-table
|
||||
:scrollbar-always-on="true"
|
||||
:data="tableData"
|
||||
height="400px"
|
||||
size="small"
|
||||
:header-cell-style="{ textAlign: 'center' }"
|
||||
v-loading="loading1"
|
||||
element-loading-background="#343849c7"
|
||||
border
|
||||
>
|
||||
<el-table-column prop="name" align="center" label="表名" />
|
||||
<el-table-column
|
||||
prop="startTime"
|
||||
align="center"
|
||||
label="起始时间"
|
||||
width="150"
|
||||
/>
|
||||
<el-table-column
|
||||
prop="endTime"
|
||||
align="center"
|
||||
label="截止时间"
|
||||
width="150"
|
||||
/>
|
||||
<el-table-column prop="updateTime" align="center" label="更新时间" />
|
||||
<el-table-column fixed="right" align="center" label="操作" width="140">
|
||||
<template #default="{ row }">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
size="small"
|
||||
v-if="row.integrity == 1"
|
||||
@click="handleClick(row)"
|
||||
>完整性详情</el-button
|
||||
>
|
||||
<el-button
|
||||
link
|
||||
type="danger"
|
||||
size="small"
|
||||
@click.stop="handleDelete(row)"
|
||||
>删除</el-button
|
||||
>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
<el-pagination
|
||||
size="small"
|
||||
style="margin-top: 10px"
|
||||
:currentPage="form.pageNum"
|
||||
:page-size="form.pageSize"
|
||||
:page-sizes="[10, 20, 50, 100, 200]"
|
||||
background
|
||||
:layout="'sizes,total, ->, prev, pager, next, jumper'"
|
||||
:total="total"
|
||||
@size-change="onTableSizeChange"
|
||||
@current-change="onTableCurrentChange"
|
||||
></el-pagination>
|
||||
</el-dialog>
|
||||
<!-- 详情 -->
|
||||
<completenessDetails ref="completenessDetailsRef" @close="close" />
|
||||
<el-dialog
|
||||
v-model="dialogVisible"
|
||||
draggable
|
||||
title="上传数据"
|
||||
width="500"
|
||||
:close-on-click-modal="false"
|
||||
:before-close="handleClose"
|
||||
>
|
||||
<el-upload
|
||||
ref="upload"
|
||||
action=""
|
||||
v-model:file-list="fileList"
|
||||
accept=".xlsx,.xls"
|
||||
:auto-upload="false"
|
||||
:on-change="choose"
|
||||
:limit="2"
|
||||
>
|
||||
<el-button type="primary" :icon="Upload" size="small">上传文件</el-button>
|
||||
</el-upload>
|
||||
<template #footer>
|
||||
<el-button @click="handleClose" size="small">取消</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
@click="submitupload"
|
||||
:loading="loading"
|
||||
size="small"
|
||||
>确认</el-button
|
||||
>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted, reactive } from "vue";
|
||||
import completenessDetails from "./completenessDetails.vue";
|
||||
import type { UploadInstance } from "element-plus";
|
||||
import { Search, Plus, Upload, Download } from "@element-plus/icons-vue";
|
||||
import { useStore } from "vuex";
|
||||
import * as XLSX from "xlsx";
|
||||
|
||||
import {
|
||||
userDataList,
|
||||
uploadUserData,
|
||||
deleteUserDataByIds,
|
||||
} from "@/api/manage_wx";
|
||||
const store = useStore();
|
||||
const visible = ref(false);
|
||||
const openDialog = () => {
|
||||
visible.value = true;
|
||||
};
|
||||
|
||||
const form = reactive({
|
||||
searchValue: "",
|
||||
pageNum: 1,
|
||||
pageSize: 20,
|
||||
});
|
||||
const total = ref(0); // 假设总条数为100
|
||||
const tableData = ref([]);
|
||||
|
||||
const completenessDetailsRef = ref();
|
||||
const dialogVisible = ref(false);
|
||||
const upload = ref<UploadInstance>();
|
||||
const fileList = ref([]);
|
||||
const loading = ref(false);
|
||||
const loading1 = ref(false);
|
||||
|
||||
// 关闭上传弹框
|
||||
const handleClose = () => {
|
||||
fileList.value = [];
|
||||
dialogVisible.value = false;
|
||||
visible.value = true;
|
||||
getTableData();
|
||||
};
|
||||
// 详情关闭弹框
|
||||
const close = () => {
|
||||
visible.value = true;
|
||||
};
|
||||
// 新增
|
||||
const uploadFile = () => {
|
||||
dialogVisible.value = true;
|
||||
visible.value = false;
|
||||
};
|
||||
|
||||
// 上传
|
||||
const choose = (e: any) => {
|
||||
upload.value!.clearFiles();
|
||||
setTimeout(() => {
|
||||
if (e.name.includes(".xls")) {
|
||||
fileList.value = [e];
|
||||
} else {
|
||||
ElMessage.warning("请上传Excel文件!");
|
||||
}
|
||||
}, 0);
|
||||
};
|
||||
|
||||
// 上传
|
||||
const submitupload = async () => {
|
||||
if (fileList.value.length == 0) {
|
||||
ElMessage.warning("请上传文件!");
|
||||
return;
|
||||
}
|
||||
ElMessage.info("上传中,请稍等...");
|
||||
const formData = new FormData();
|
||||
formData.append("file", fileList.value[0].raw);
|
||||
loading.value = true;
|
||||
uploadUserData(formData)
|
||||
.then((res) => {
|
||||
ElMessage.success("上传成功");
|
||||
loading.value = false;
|
||||
handleClose();
|
||||
// tableStore.index();
|
||||
})
|
||||
.catch((err) => {
|
||||
loading.value = false;
|
||||
});
|
||||
};
|
||||
// provide("tableStore", tableStore);
|
||||
// tableStore.table.params.searchValue = "";
|
||||
|
||||
const onTableSizeChange = (size: number) => {
|
||||
form.pageSize = size;
|
||||
// 重新加载数据
|
||||
getTableData();
|
||||
};
|
||||
const onTableCurrentChange = (page: number) => {
|
||||
form.pageNum = page;
|
||||
// 重新加载数据
|
||||
getTableData();
|
||||
};
|
||||
|
||||
const getTableData = async () => {
|
||||
loading1.value = true;
|
||||
// form.deptId = store.state.deptId;
|
||||
const res: any = await userDataList(form);
|
||||
|
||||
if (res.code == "A0000") {
|
||||
tableData.value = res.data.records;
|
||||
|
||||
total.value = res.data.total;
|
||||
}
|
||||
loading1.value = false;
|
||||
};
|
||||
|
||||
// 完整性详情
|
||||
const handleClick = (row) => {
|
||||
visible.value = false;
|
||||
completenessDetailsRef.value.open(row);
|
||||
};
|
||||
|
||||
// 删除
|
||||
const handleDelete = (row) => {
|
||||
ElMessageBox.confirm("确定删除?", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning",
|
||||
})
|
||||
.then(() => {
|
||||
deleteUserDataByIds([row.id]).then(() => {
|
||||
ElMessage.success("删除成功");
|
||||
getTableData();
|
||||
});
|
||||
})
|
||||
.catch(() => {
|
||||
ElMessage({
|
||||
type: "info",
|
||||
message: "删除取消",
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// 导出
|
||||
const exportTable = async () => {
|
||||
let columnExpor: any = [
|
||||
[
|
||||
"表名",
|
||||
"起始时间",
|
||||
"截止时间",
|
||||
"更新时间",
|
||||
],
|
||||
];
|
||||
|
||||
let list = [];
|
||||
|
||||
await userDataList({
|
||||
...form,
|
||||
pageNum: 1,
|
||||
pageSize: total.value,
|
||||
}).then((res) => {
|
||||
let data = res.data.records.map((item) => {
|
||||
return [
|
||||
item.name,
|
||||
item.startTime,
|
||||
item.endTime,
|
||||
item.updateTime,
|
||||
];
|
||||
});
|
||||
list = [...columnExpor, ...data];
|
||||
// 创建工作表
|
||||
const worksheet = XLSX.utils.aoa_to_sheet(list);
|
||||
|
||||
worksheet["!cols"] = list.map((col) => ({ wch: 20 }));
|
||||
|
||||
// 创建工作簿
|
||||
const workbook = XLSX.utils.book_new();
|
||||
XLSX.utils.book_append_sheet(workbook, worksheet, "用采数据管理");
|
||||
|
||||
// 写出文件
|
||||
XLSX.writeFile(workbook, "用采数据管理" + ".xlsx");
|
||||
});
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getTableData();
|
||||
});
|
||||
|
||||
defineExpose({
|
||||
openDialog,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@use "@/assets/scss/index.scss";
|
||||
.tableBox {
|
||||
padding: 0px !important;
|
||||
}
|
||||
.formBox {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
:deep(.el-upload-list__item-name) {
|
||||
color: #fff;
|
||||
}
|
||||
:deep(.el-upload-list__item:hover) {
|
||||
.el-upload-list__item-name {
|
||||
color: #909399;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,195 @@
|
||||
<template>
|
||||
<!--暂降 -->
|
||||
<el-dialog
|
||||
:close-on-click-modal="false"
|
||||
draggable
|
||||
v-model="machineVisible"
|
||||
title="完整性不足详情"
|
||||
:before-close="handleClose"
|
||||
width="1000px"
|
||||
>
|
||||
<div class="formBox">
|
||||
<el-form label-width="70px" :model="form" inline>
|
||||
<el-form-item label="关键字">
|
||||
<el-input
|
||||
style="width: 150px"
|
||||
v-model="form.searchValue"
|
||||
placeholder="关键字查询"
|
||||
clearable
|
||||
size="small"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div>
|
||||
<el-button
|
||||
type="primary"
|
||||
:icon="Search"
|
||||
size="small"
|
||||
@click="getTableData"
|
||||
>查询</el-button
|
||||
>
|
||||
<el-button
|
||||
type="primary"
|
||||
:icon="Download"
|
||||
@click="exportTable"
|
||||
size="small"
|
||||
>导出
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tableBox">
|
||||
<el-table
|
||||
:scrollbar-always-on="true"
|
||||
:data="tableData"
|
||||
height="400px"
|
||||
size="small"
|
||||
:header-cell-style="{ textAlign: 'center' }"
|
||||
v-loading="loading"
|
||||
element-loading-background="#343849c7"
|
||||
border
|
||||
>
|
||||
<el-table-column prop="userNo" align="center" label="户号" />
|
||||
<el-table-column prop="userName" align="center" label="用户名" />
|
||||
<el-table-column
|
||||
prop="lineNo"
|
||||
align="center"
|
||||
label="测量点局号"
|
||||
width="180"
|
||||
/>
|
||||
<el-table-column
|
||||
prop="updateTime"
|
||||
align="center"
|
||||
label="日期"
|
||||
width="180"
|
||||
/>
|
||||
<el-table-column
|
||||
prop="integrity"
|
||||
align="center"
|
||||
label="完整性(%)"
|
||||
width="130"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
{{ Math.floor(row.integrity * 10000) / 100 }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
<el-pagination
|
||||
size="small"
|
||||
style="margin-top: 10px"
|
||||
:currentPage="form.pageNum"
|
||||
:page-size="form.pageSize"
|
||||
:page-sizes="[10, 20, 50, 100, 200]"
|
||||
background
|
||||
:layout="'sizes,total, ->, prev, pager, next, jumper'"
|
||||
:total="total"
|
||||
@size-change="onTableSizeChange"
|
||||
@current-change="onTableCurrentChange"
|
||||
></el-pagination>
|
||||
</el-dialog>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive, inject, onMounted } from "vue";
|
||||
import { Search, Download } from "@element-plus/icons-vue";
|
||||
import { userDataIntegrityList } from "@/api/manage_wx";
|
||||
import { useStore } from "vuex";
|
||||
import * as XLSX from "xlsx";
|
||||
const emits = defineEmits(["close"]);
|
||||
const store = useStore();
|
||||
const machineVisible = ref(false);
|
||||
const form = reactive({
|
||||
searchValue: "",
|
||||
pageNum: 1,
|
||||
pageSize: 20,
|
||||
userDataId: "",
|
||||
});
|
||||
const loading = ref(false);
|
||||
const total = ref(0); // 假设总条数为100
|
||||
const tableData = ref([]);
|
||||
//form表单校验规则
|
||||
const open = (row) => {
|
||||
machineVisible.value = true;
|
||||
form.searchValue = "";
|
||||
form.pageNum = 1;
|
||||
form.pageSize = 20;
|
||||
form.userDataId = row.id;
|
||||
|
||||
getTableData();
|
||||
};
|
||||
const onTableSizeChange = (size: number) => {
|
||||
form.pageSize = size;
|
||||
getTableData();
|
||||
};
|
||||
const onTableCurrentChange = (page: number) => {
|
||||
form.pageNum = page;
|
||||
getTableData();
|
||||
};
|
||||
|
||||
const getTableData = async () => {
|
||||
loading.value = true;
|
||||
const res: any = await userDataIntegrityList(form);
|
||||
|
||||
if (res.code == "A0000") {
|
||||
tableData.value = res.data.records;
|
||||
total.value = res.data.total;
|
||||
}
|
||||
loading.value = false;
|
||||
};
|
||||
const handleClose = () => {
|
||||
machineVisible.value = false;
|
||||
emits("close");
|
||||
};
|
||||
|
||||
// 导出
|
||||
const exportTable = async () => {
|
||||
let columnExpor: any = [
|
||||
["户号", "用户名", "测量点局号", "日期", "完整性(%)"],
|
||||
];
|
||||
|
||||
let list = [];
|
||||
|
||||
await userDataIntegrityList({
|
||||
...form,
|
||||
pageNum: 1,
|
||||
pageSize: total.value,
|
||||
}).then((res) => {
|
||||
let data = res.data.records.map((item) => {
|
||||
return [
|
||||
item.userNo,
|
||||
item.userName,
|
||||
item.lineNo,
|
||||
item.updateTime,
|
||||
Math.floor(item.integrity * 10000) / 100,
|
||||
];
|
||||
});
|
||||
list = [...columnExpor, ...data];
|
||||
// 创建工作表
|
||||
const worksheet = XLSX.utils.aoa_to_sheet(list);
|
||||
|
||||
worksheet["!cols"] = list.map((col) => ({ wch: 20 }));
|
||||
|
||||
// 创建工作簿
|
||||
const workbook = XLSX.utils.book_new();
|
||||
XLSX.utils.book_append_sheet(workbook, worksheet, "完整性不足详情");
|
||||
|
||||
// 写出文件
|
||||
XLSX.writeFile(workbook, "完整性不足详情" + ".xlsx");
|
||||
});
|
||||
};
|
||||
|
||||
onMounted(() => {});
|
||||
|
||||
defineExpose({ open });
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@use "@/assets/scss/index.scss";
|
||||
|
||||
.tableBox {
|
||||
padding: 0px !important;
|
||||
}
|
||||
|
||||
.formBox {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,262 @@
|
||||
<template>
|
||||
<!--暂降 -->
|
||||
<el-dialog
|
||||
:close-on-click-modal="false"
|
||||
draggable
|
||||
v-model="machineVisible"
|
||||
title="暂降事件"
|
||||
width="1200px"
|
||||
>
|
||||
<div class="tableBox">
|
||||
<div style="display: flex;justify-content: flex-end;">
|
||||
<el-button
|
||||
type="primary"
|
||||
:icon="Download"
|
||||
@click="exportTable"
|
||||
size="small"
|
||||
>导出
|
||||
</el-button>
|
||||
</div>
|
||||
<el-table
|
||||
:scrollbar-always-on="true"
|
||||
:data="tableData"
|
||||
height="443px"
|
||||
size="small"
|
||||
v-loading="loading"
|
||||
element-loading-background="#343849c7"
|
||||
:header-cell-style="{ textAlign: 'center' }"
|
||||
border
|
||||
style="margin-top: 13px;"
|
||||
>
|
||||
<!-- <el-table-column type="index" align="center" label="序号" width="70" /> -->
|
||||
<el-table-column label="序号" align="center" type="index" width="70">
|
||||
<template #default="scope">
|
||||
<span>{{
|
||||
(form.pageNum - 1) * form.pageSize + scope.$index + 1
|
||||
}}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="startTime"
|
||||
align="center"
|
||||
label="发生时间"
|
||||
show-overflow-tooltip
|
||||
width="160"
|
||||
/>
|
||||
<el-table-column prop="stationName" align="center" label="变电站" />
|
||||
<el-table-column prop="lineName" align="center" label="监测点" />
|
||||
<el-table-column
|
||||
prop="objName"
|
||||
label="用户"
|
||||
align="center"
|
||||
show-overflow-tooltip
|
||||
>
|
||||
<template v-slot="scope">
|
||||
<span>{{ scope.row.objName ? scope.row.objName : "/" }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="eventType"
|
||||
align="center"
|
||||
label="触发类型"
|
||||
width="80"
|
||||
/>
|
||||
<el-table-column
|
||||
prop="featureAmplitude"
|
||||
align="center"
|
||||
label="残余电压(%)"
|
||||
width="100"
|
||||
>
|
||||
<template v-slot="scope">
|
||||
<span>{{ (scope.row.featureAmplitude * 100).toFixed(2) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="duration"
|
||||
align="center"
|
||||
label="持续时间(S)"
|
||||
width="100"
|
||||
/>
|
||||
<el-table-column fixed="right" label="操作" width="80" align="center">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
size="small"
|
||||
@click.stop="trendCharts(scope.row)"
|
||||
>波形</el-button
|
||||
>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
<el-pagination
|
||||
size="small"
|
||||
style="margin-top: 10px"
|
||||
:currentPage="form.pageNum"
|
||||
:page-size="form.pageSize"
|
||||
:page-sizes="[10, 20, 50, 100, 200]"
|
||||
background
|
||||
:layout="'sizes,total, ->, prev, pager, next, jumper'"
|
||||
:total="total"
|
||||
@size-change="onTableSizeChange"
|
||||
@current-change="onTableCurrentChange"
|
||||
></el-pagination>
|
||||
</el-dialog>
|
||||
<!-- 波形图 -->
|
||||
<el-dialog
|
||||
:close-on-click-modal="false"
|
||||
draggable
|
||||
v-model="trendVisible"
|
||||
v-if="trendVisible"
|
||||
title="波形"
|
||||
width="70%"
|
||||
@close="handleCloseTrend"
|
||||
>
|
||||
<waveForm ref="waveFormRef" />
|
||||
</el-dialog>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive, inject } from "vue";
|
||||
import { eventListByLineId } from "@/api/manage_wx";
|
||||
import waveForm from "@/components/BX/waveForm.vue";
|
||||
import * as XLSX from "xlsx";
|
||||
import { Download } from "@element-plus/icons-vue";
|
||||
|
||||
const machineVisible = ref(false);
|
||||
const form = reactive({
|
||||
lineId: "",
|
||||
pageNum: 1,
|
||||
pageSize: 20,
|
||||
searchBeginTime: "",
|
||||
searchEndTime: "",
|
||||
});
|
||||
const total = ref(0); // 假设总条数为100
|
||||
const loading = ref(false);
|
||||
const tableData = ref([]);
|
||||
import { useStore } from "vuex";
|
||||
|
||||
const store = useStore();
|
||||
|
||||
const trendVisible = ref(false);
|
||||
|
||||
const waveFormRef = ref();
|
||||
//点击波形图
|
||||
const trendCharts = (row: any) => {
|
||||
row.eventdetail_index = row.eventId;
|
||||
machineVisible.value = false;
|
||||
trendVisible.value = true;
|
||||
setTimeout(() => {
|
||||
waveFormRef.value?.open({
|
||||
...row,
|
||||
bdname: row.stationName,
|
||||
pointname: row.lineName,
|
||||
timeid: row.startTime,
|
||||
eventvalue: row.featureAmplitude,
|
||||
persisttime: row.duration,
|
||||
});
|
||||
}, 500);
|
||||
};
|
||||
|
||||
// 关闭波形图
|
||||
const handleCloseTrend = () => {
|
||||
trendVisible.value = false;
|
||||
machineVisible.value = true;
|
||||
};
|
||||
const open = async (id: any) => {
|
||||
form.pageNum = 1;
|
||||
form.pageSize = 20;
|
||||
form.lineId = id;
|
||||
machineVisible.value = true;
|
||||
await init();
|
||||
};
|
||||
|
||||
const init = async () => {
|
||||
loading.value = true;
|
||||
eventListByLineId({
|
||||
lineId: form.lineId,
|
||||
searchBeginTime: store.state.timeValue[0],
|
||||
searchEndTime: store.state.timeValue[1],
|
||||
}).then((res) => {
|
||||
tableData.value = res.data.records;
|
||||
total.value = res.data.total;
|
||||
loading.value = false;
|
||||
});
|
||||
};
|
||||
|
||||
const onTableSizeChange = (size: number) => {
|
||||
form.pageSize = size;
|
||||
init();
|
||||
};
|
||||
const onTableCurrentChange = (page: number) => {
|
||||
form.pageNum = page;
|
||||
init();
|
||||
};
|
||||
|
||||
// 暂降溯源导出
|
||||
const exportTable = async () => {
|
||||
let columnExpor: any = [
|
||||
[
|
||||
"发生时间",
|
||||
"变电站",
|
||||
"监测点",
|
||||
"用户",
|
||||
"触发类型",
|
||||
"残余电压(%)",
|
||||
"持续时间(S)",
|
||||
],
|
||||
];
|
||||
|
||||
let list = [];
|
||||
|
||||
await eventListByLineId({
|
||||
lineId: form.lineId,
|
||||
searchBeginTime: store.state.timeValue[0],
|
||||
searchEndTime: store.state.timeValue[1],
|
||||
pageNum: 1,
|
||||
pageSize: total.value,
|
||||
}).then((res) => {
|
||||
let data = res.data.records.map((item) => {
|
||||
return [
|
||||
item.startTime,
|
||||
item.stationName,
|
||||
item.lineName,
|
||||
item.objName,
|
||||
item.eventType,
|
||||
(item.featureAmplitude * 100).toFixed(2),
|
||||
item.duration,
|
||||
];
|
||||
});
|
||||
list = [...columnExpor, ...data];
|
||||
// 创建工作表
|
||||
const worksheet = XLSX.utils.aoa_to_sheet(list);
|
||||
|
||||
worksheet["!cols"] = list.map((col) => ({ wch: 20 }));
|
||||
|
||||
// 创建工作簿
|
||||
const workbook = XLSX.utils.book_new();
|
||||
XLSX.utils.book_append_sheet(workbook, worksheet, "暂降事件");
|
||||
|
||||
// 写出文件
|
||||
XLSX.writeFile(workbook, "暂降事件" + ".xlsx");
|
||||
});
|
||||
};
|
||||
|
||||
defineExpose({ open });
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@use "@/assets/scss/index.scss";
|
||||
.tableBox {
|
||||
padding: 0px !important;
|
||||
}
|
||||
.formBox {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.formLeft {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: #fff;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,558 @@
|
||||
<template>
|
||||
<!--暂降 -->
|
||||
<el-dialog
|
||||
:close-on-click-modal="false"
|
||||
draggable
|
||||
v-model="machineVisible"
|
||||
title="暂降事件"
|
||||
width="1000px"
|
||||
>
|
||||
<div class="tableBox">
|
||||
<el-form
|
||||
:inline="true"
|
||||
style="display: flex; justify-content: space-between"
|
||||
>
|
||||
<el-form-item label="关键字筛选">
|
||||
<el-input
|
||||
clearable
|
||||
style="width: 150px"
|
||||
v-model="searchValue"
|
||||
size="small"
|
||||
placeholder="电站、测点、用户信息"
|
||||
></el-input>
|
||||
<el-popover placement="bottom" :width="550" trigger="click">
|
||||
<template #reference>
|
||||
<el-button
|
||||
size="small"
|
||||
:icon="DArrowRight"
|
||||
type="primary"
|
||||
style="margin-left: 10px"
|
||||
>更多</el-button
|
||||
>
|
||||
</template>
|
||||
|
||||
<el-form label-width="auto">
|
||||
<!-- <el-form-item label="关键字筛选">
|
||||
<el-input
|
||||
clearable
|
||||
style="width: 150px"
|
||||
v-model="searchValue"
|
||||
size="small"
|
||||
placeholder="电站、测点、用户信息"
|
||||
></el-input>
|
||||
</el-form-item> -->
|
||||
<el-form-item label="运维单位">
|
||||
<el-tree-select
|
||||
v-model="deptsIndex"
|
||||
:data="deptsList"
|
||||
:render-after-expand="false"
|
||||
clearable
|
||||
size="small"
|
||||
style="width: 150px"
|
||||
:props="{
|
||||
value: 'id',
|
||||
label: 'name',
|
||||
children: 'children',
|
||||
}"
|
||||
/>
|
||||
<!-- <el-select
|
||||
v-model="deptsIndex"
|
||||
placeholder="请选择运维单位"
|
||||
clearable
|
||||
size="small"
|
||||
:teleported="false"
|
||||
style="width: 150px"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in deptsList"
|
||||
:label="item.name"
|
||||
:value="item.id"
|
||||
:key="item.id"
|
||||
></el-option>
|
||||
</el-select> -->
|
||||
</el-form-item>
|
||||
<el-form-item label="触发类型">
|
||||
<el-select
|
||||
clearable
|
||||
size="small"
|
||||
:teleported="false"
|
||||
v-model="eventForm.eventType"
|
||||
placeholder="请选择触发类型"
|
||||
style="width: 150px"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in eventTypeList"
|
||||
:label="item.name"
|
||||
:value="item.id"
|
||||
:key="item.id"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="残余电压百分比">
|
||||
<el-input-number
|
||||
v-model="eventForm.eventValueMin"
|
||||
:min="0"
|
||||
style="width: 150px"
|
||||
size="small"
|
||||
:max="100"
|
||||
:precision="1"
|
||||
:step="1"
|
||||
@change="
|
||||
(e) => (e == null ? (eventForm.eventValueMin = 1) : null)
|
||||
"
|
||||
><template #suffix>
|
||||
<span>%</span>
|
||||
</template></el-input-number
|
||||
>
|
||||
<span> < 残余电压 < </span>
|
||||
|
||||
<el-input-number
|
||||
v-model="eventForm.eventValueMax"
|
||||
:min="0"
|
||||
style="width: 150px"
|
||||
size="small"
|
||||
:max="100"
|
||||
:precision="1"
|
||||
:step="1"
|
||||
@change="
|
||||
(e) => (e == null ? (eventForm.eventValueMax = 1) : null)
|
||||
"
|
||||
><template #suffix>
|
||||
<span>%</span>
|
||||
</template></el-input-number
|
||||
>
|
||||
</el-form-item>
|
||||
<el-form-item label="暂降持续事时间">
|
||||
<el-input-number
|
||||
v-model="eventForm.eventDurationMin"
|
||||
:min="0"
|
||||
style="width: 150px"
|
||||
size="small"
|
||||
:max="1000000"
|
||||
:precision="1"
|
||||
:step="1"
|
||||
@change="
|
||||
(e) => (e == null ? (eventForm.eventDurationMin = 1) : null)
|
||||
"
|
||||
><template #suffix>
|
||||
<span>ms</span>
|
||||
</template></el-input-number
|
||||
>
|
||||
<span> < 持续时间 < </span>
|
||||
|
||||
<el-input-number
|
||||
v-model="eventForm.eventDurationMax"
|
||||
:min="0"
|
||||
style="width: 150px"
|
||||
size="small"
|
||||
:max="1000000"
|
||||
:precision="1"
|
||||
:step="1"
|
||||
@change="
|
||||
(e) => (e == null ? (eventForm.eventDurationMax = 1) : null)
|
||||
"
|
||||
><template #suffix>
|
||||
<span>ms</span>
|
||||
</template></el-input-number
|
||||
>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-popover>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item style="margin-right: -10px">
|
||||
<el-button size="small" :icon="Search" type="primary" @click="init()"
|
||||
>查询</el-button
|
||||
>
|
||||
<el-button size="small" :icon="RefreshLeft" @click="clearInit()"
|
||||
>重置</el-button
|
||||
>
|
||||
<el-button
|
||||
type="primary"
|
||||
:icon="Download"
|
||||
@click="exportTable"
|
||||
size="small"
|
||||
style="margin-right: 10px"
|
||||
>导出
|
||||
</el-button>
|
||||
<el-button
|
||||
style="margin-right: 10px"
|
||||
v-if="displayValue == 1"
|
||||
size="small"
|
||||
:icon="HelpFilled"
|
||||
type="primary"
|
||||
@click="handleAggregation"
|
||||
>溯源</el-button
|
||||
>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<!-- <div style="float: right; margin-bottom: 9px" v-if="displayValue == 1">
|
||||
<el-button
|
||||
size="small"
|
||||
:icon="HelpFilled"
|
||||
type="primary"
|
||||
@click="handleAggregation"
|
||||
>聚合</el-button
|
||||
>
|
||||
</div> -->
|
||||
<el-table
|
||||
:scrollbar-always-on="true"
|
||||
:data="tableData"
|
||||
height="443px"
|
||||
size="small"
|
||||
v-loading="loading"
|
||||
element-loading-background="#343849c7"
|
||||
:header-cell-style="{ textAlign: 'center' }"
|
||||
border
|
||||
>
|
||||
<!-- <el-table-column type="index" align="center" label="序号" width="70" /> -->
|
||||
<el-table-column label="序号" align="center" type="index" width="70">
|
||||
<template #default="scope">
|
||||
<span>{{
|
||||
(form.pageNum - 1) * form.pageSize + scope.$index + 1
|
||||
}}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="startTime"
|
||||
align="center"
|
||||
label="发生时间"
|
||||
show-overflow-tooltip
|
||||
width="160"
|
||||
/>
|
||||
<el-table-column
|
||||
prop="stationName"
|
||||
align="center"
|
||||
label="变电站"
|
||||
width="120"
|
||||
/>
|
||||
<el-table-column
|
||||
prop="lineName"
|
||||
align="center"
|
||||
label="监测点"
|
||||
width="120"
|
||||
/>
|
||||
<el-table-column
|
||||
prop="objName"
|
||||
label="用户"
|
||||
align="center"
|
||||
show-overflow-tooltip
|
||||
>
|
||||
<template v-slot="scope">
|
||||
<span>{{ scope.row.objName ? scope.row.objName : "/" }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="eventType"
|
||||
align="center"
|
||||
label="触发类型"
|
||||
width="90"
|
||||
/>
|
||||
<el-table-column
|
||||
prop="featureAmplitude"
|
||||
align="center"
|
||||
label="残余电压(%)"
|
||||
width="90"
|
||||
>
|
||||
<template v-slot="scope">
|
||||
<span>{{ (scope.row.featureAmplitude * 100).toFixed(2) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="duration"
|
||||
align="center"
|
||||
label="持续时间(S)"
|
||||
width="90"
|
||||
/>
|
||||
<el-table-column fixed="right" label="操作" width="80" align="center">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
size="small"
|
||||
@click.stop="trendCharts(scope.row)"
|
||||
>波形</el-button
|
||||
>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
<el-pagination
|
||||
size="small"
|
||||
style="margin-top: 10px"
|
||||
:currentPage="form.pageNum"
|
||||
:page-size="form.pageSize"
|
||||
:page-sizes="[10, 20, 50, 100, 200]"
|
||||
background
|
||||
:layout="'sizes,total, ->, prev, pager, next, jumper'"
|
||||
:total="total"
|
||||
@size-change="onTableSizeChange"
|
||||
@current-change="onTableCurrentChange"
|
||||
></el-pagination>
|
||||
</el-dialog>
|
||||
<!-- 波形图 -->
|
||||
<el-dialog
|
||||
:close-on-click-modal="false"
|
||||
draggable
|
||||
v-model="trendVisible"
|
||||
v-if="trendVisible"
|
||||
title="波形"
|
||||
width="70%"
|
||||
@close="handleCloseTrend"
|
||||
>
|
||||
<waveForm ref="waveFormRef" />
|
||||
</el-dialog>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive, inject, onMounted, watch } from "vue";
|
||||
import {
|
||||
getEventList,
|
||||
processEvents,
|
||||
loginDeptTree,
|
||||
getDicDataByTypeCode,
|
||||
} from "@/api/manage_wx";
|
||||
import { HelpFilled } from "@element-plus/icons-vue";
|
||||
import waveForm from "@/components/BX/waveForm.vue";
|
||||
import {
|
||||
DArrowRight,
|
||||
Search,
|
||||
RefreshLeft,
|
||||
Download,
|
||||
} from "@element-plus/icons-vue";
|
||||
import * as XLSX from "xlsx";
|
||||
|
||||
// 定义 emit
|
||||
const emit = defineEmits(["aggregation-success"]);
|
||||
|
||||
const machineVisible = ref(false);
|
||||
const form = reactive({
|
||||
pageNum: 1,
|
||||
pageSize: 20,
|
||||
});
|
||||
const total = ref(0); // 假设总条数为100
|
||||
const loading = ref(false);
|
||||
const tableData = ref([]);
|
||||
import { useStore } from "vuex";
|
||||
|
||||
const store = useStore();
|
||||
|
||||
const displayValue = ref();
|
||||
|
||||
const trendVisible = ref(false);
|
||||
|
||||
const waveFormRef = ref();
|
||||
|
||||
const open = async (val) => {
|
||||
displayValue.value = val;
|
||||
machineVisible.value = true;
|
||||
await init();
|
||||
};
|
||||
|
||||
// 查询
|
||||
const searchValue = ref("");
|
||||
const deptsIndex = ref("");
|
||||
const deptsList = ref([]); //部门列表
|
||||
const eventTypeList = ref([]); //触发类型
|
||||
const eventForm: any = reactive({
|
||||
eventValueMin: null,
|
||||
eventValueMax: null,
|
||||
eventDurationMin: null,
|
||||
eventDurationMax: null,
|
||||
eventType: null,
|
||||
});
|
||||
|
||||
const clearInit = async () => {
|
||||
searchValue.value = "";
|
||||
eventForm.eventValueMin = null;
|
||||
eventForm.eventValueMax = null;
|
||||
eventForm.eventDurationMin = null;
|
||||
eventForm.eventDurationMax = null;
|
||||
eventForm.eventType = null;
|
||||
deptsIndex.value = null;
|
||||
|
||||
await init();
|
||||
};
|
||||
|
||||
// 部门
|
||||
const initDept = () => {
|
||||
loginDeptTree({
|
||||
deptIndex: store.state.deptId,
|
||||
}).then((res: any) => {
|
||||
deptsList.value = res.data;
|
||||
});
|
||||
};
|
||||
|
||||
// 触发类型
|
||||
const dicDataByTypeCode = () => {
|
||||
getDicDataByTypeCode({
|
||||
dictTypeCode: "Event_Statis",
|
||||
}).then((res: any) => {
|
||||
eventTypeList.value = res.data;
|
||||
});
|
||||
};
|
||||
|
||||
const init = async () => {
|
||||
loading.value = true;
|
||||
getEventList({
|
||||
deptId: store.state.deptId,
|
||||
searchBeginTime: store.state.timeValue[0],
|
||||
searchEndTime: store.state.timeValue[1],
|
||||
pageNum: form.pageNum,
|
||||
pageSize: form.pageSize,
|
||||
searchValue: searchValue.value,
|
||||
...eventForm,
|
||||
eventValueMin:
|
||||
eventForm.eventValueMin == null ? null : eventForm.eventValueMin / 100,
|
||||
eventValueMax:
|
||||
eventForm.eventValueMax == null ? null : eventForm.eventValueMax / 100,
|
||||
}).then((res) => {
|
||||
tableData.value = res.data.records;
|
||||
total.value = res.data.total;
|
||||
loading.value = false;
|
||||
});
|
||||
};
|
||||
|
||||
const handleAggregation = (row: any) => {
|
||||
machineVisible.value = false;
|
||||
ElMessageBox.confirm(
|
||||
`是否确认对当前用户部门${store.state.timeValue[0]}至${store.state.timeValue[1]}之间的暂降事件进行溯源?`,
|
||||
"提示",
|
||||
{
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning",
|
||||
}
|
||||
)
|
||||
.then(() => {
|
||||
processEvents({
|
||||
deptId: store.state.deptId,
|
||||
searchBeginTime: store.state.timeValue[0],
|
||||
searchEndTime: store.state.timeValue[1],
|
||||
}).then((res: any) => {
|
||||
if (res.code == "A0000") {
|
||||
ElMessage({
|
||||
type: "success",
|
||||
message: res.message,
|
||||
});
|
||||
// 通知父组件执行 initialAggregation
|
||||
emit("aggregation-success");
|
||||
} else {
|
||||
ElMessage({
|
||||
type: "warning",
|
||||
message: res.message,
|
||||
});
|
||||
}
|
||||
});
|
||||
})
|
||||
.catch((error) => {});
|
||||
};
|
||||
|
||||
//点击波形图
|
||||
const trendCharts = (row: any) => {
|
||||
row.eventdetail_index = row.eventId;
|
||||
machineVisible.value = false;
|
||||
trendVisible.value = true;
|
||||
setTimeout(() => {
|
||||
waveFormRef.value?.open({
|
||||
...row,
|
||||
bdname: row.stationName,
|
||||
pointname: row.lineName,
|
||||
timeid: row.startTime,
|
||||
eventvalue: row.featureAmplitude,
|
||||
persisttime: row.duration,
|
||||
});
|
||||
}, 500);
|
||||
};
|
||||
|
||||
// 关闭波形图
|
||||
const handleCloseTrend = () => {
|
||||
trendVisible.value = false;
|
||||
machineVisible.value = true;
|
||||
};
|
||||
|
||||
const onTableSizeChange = (size: number) => {
|
||||
form.pageSize = size;
|
||||
init();
|
||||
};
|
||||
const onTableCurrentChange = (page: number) => {
|
||||
form.pageNum = page;
|
||||
init();
|
||||
};
|
||||
|
||||
// 暂降溯源导出
|
||||
const exportTable = async () => {
|
||||
let columnExpor: any = [
|
||||
[
|
||||
"发生时间",
|
||||
"变电站",
|
||||
"监测点",
|
||||
"用户",
|
||||
"触发类型",
|
||||
"残余电压(%)",
|
||||
"持续时间(S)",
|
||||
],
|
||||
];
|
||||
|
||||
let list = [];
|
||||
|
||||
await getEventList({
|
||||
deptId: store.state.deptId,
|
||||
searchBeginTime: store.state.timeValue[0],
|
||||
searchEndTime: store.state.timeValue[1],
|
||||
searchValue: searchValue.value,
|
||||
...eventForm,
|
||||
eventValueMin:
|
||||
eventForm.eventValueMin == null ? null : eventForm.eventValueMin / 100,
|
||||
eventValueMax:
|
||||
eventForm.eventValueMax == null ? null : eventForm.eventValueMax / 100,
|
||||
pageNum: 1,
|
||||
pageSize: total.value,
|
||||
}).then((res) => {
|
||||
let data = res.data.records.map((item) => {
|
||||
return [
|
||||
item.startTime,
|
||||
item.stationName,
|
||||
item.lineName,
|
||||
item.objName,
|
||||
item.eventType,
|
||||
(item.featureAmplitude * 100).toFixed(2),
|
||||
item.duration,
|
||||
];
|
||||
});
|
||||
list = [...columnExpor, ...data];
|
||||
// 创建工作表
|
||||
const worksheet = XLSX.utils.aoa_to_sheet(list);
|
||||
|
||||
worksheet["!cols"] = list.map((col) => ({ wch: 20 }));
|
||||
|
||||
// 创建工作簿
|
||||
const workbook = XLSX.utils.book_new();
|
||||
XLSX.utils.book_append_sheet(workbook, worksheet, "暂降事件");
|
||||
|
||||
// 写出文件
|
||||
XLSX.writeFile(workbook, "暂降事件" + ".xlsx");
|
||||
});
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
initDept();
|
||||
dicDataByTypeCode();
|
||||
});
|
||||
defineExpose({ open });
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@use "@/assets/scss/index.scss";
|
||||
.tableBox {
|
||||
padding: 0px !important;
|
||||
}
|
||||
.formBox {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.formLeft {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: #fff;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,701 @@
|
||||
<template>
|
||||
<!-- 配网计算 -->
|
||||
<splitpanes style="height: 100%" class="default-theme" id="navigation-splitpanes">
|
||||
<pane :size="20">
|
||||
<PointTree :showSelect="false" @node-click="handleNodeClick" @init="handleNodeClick"></PointTree>
|
||||
</pane>
|
||||
|
||||
<pane style="background-color: rgba(44, 46, 60, 0.1)" :size="100 - size">
|
||||
|
||||
<el-form :model="form" inline label-width="auto">
|
||||
<el-form-item label="谐波类型">
|
||||
<el-radio-group v-model="form.type">
|
||||
<el-radio-button label="谐波电压" size="small" value="1" />
|
||||
<el-radio-button label="谐波电流" size="small" value="0" />
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="谐波次数">
|
||||
<el-select v-model="form.index" filterable multiple :multiple-limit="5" collapse-tags collapse-tags-tooltip
|
||||
clearable placeholder="请选择次数" style="width: 200px" size="small">
|
||||
<el-option v-for="item in 49" :key="item" :label="item + 1 + '次'" :value="item + 1"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="负荷数据">
|
||||
<el-select v-model="form.loadDataId" clearable filterable placeholder="请选择负荷数据" style="width: 200px"
|
||||
size="small">
|
||||
<el-option v-for="item in loadDataOptions" :key="item.id" :label="item.name" :value="item.id"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<!-- <el-button type="primary" icon="el-icon-Plus" @click="push('/admin/division/aListOfLoadData')">
|
||||
新增
|
||||
</el-button> -->
|
||||
<el-button type="primary" :icon="Select" @click="submit" size="small">确定</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-tabs v-model="activeName" v-if="showTabs" style="height: 740px">
|
||||
<el-tab-pane v-for="(item, index) in tabList" :key="item" :label="item.label" :name="index"
|
||||
style="height: 100%">
|
||||
<div>
|
||||
<div>
|
||||
<span style="color: #fff">时间范围</span>
|
||||
<el-date-picker v-model="item.time" class="mr10 ml10" type="daterange" start-placeholder="起始时间"
|
||||
end-placeholder="结束时间" format="YYYY-MM-DD" date-format="YYYY-MM-DD" time-format="YYYY-MM-DD"
|
||||
value-format="YYYY-MM-DD" :disabled-date="handleDisabledDate" size="small" />
|
||||
<el-button type="primary" :icon="CaretRight" @click="execute(item, index)" size="small">
|
||||
执行
|
||||
</el-button>
|
||||
</div>
|
||||
<div v-if="item.showEcahr == 1" class="harmonicButton">
|
||||
<el-form :inline="true" v-model="item.form">
|
||||
<el-form-item label="限值" v-if="item.showDynamic">
|
||||
<el-input v-model="item.form.limit" placeholder="请选择限值" disabled style="width: 200px" size="small">
|
||||
<template #append>
|
||||
<el-button :icon="Edit" :class="[code == 0 ? 'frontBox' : '']" @click="setCode(0)" size="small" />
|
||||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="时间点一" v-if="item.showDynamic">
|
||||
<el-input v-model="item.form.time1" placeholder="请选择时间点一" disabled style="width: 200px" size="small">
|
||||
<template #append>
|
||||
<el-button :icon="Edit" :class="[code == 1 ? 'frontBox' : '']" @click="setCode(1)" size="small" />
|
||||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="时间点二" v-if="item.showDynamic">
|
||||
<el-input v-model="item.form.time2" placeholder="请选择时间点二" disabled style="width: 200px" size="small">
|
||||
<template #append>
|
||||
<el-button :icon="Edit" :class="[code == 2 ? 'frontBox' : '']" @click="setCode(2)" size="small" />
|
||||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
|
||||
<el-button type="primary" :icon="Document" @click="generateFn" v-if="!item.showDynamic" size="small">
|
||||
生成动态谐波责任数据
|
||||
</el-button>
|
||||
<el-button type="primary" :icon="Document" v-else @click="generateMetrics" size="small">
|
||||
生成谐波责任指标
|
||||
</el-button>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<div class="box" v-loading="loading" element-loading-background="#343849c7">
|
||||
<MyEChart :options="item.options" v-if="item.showEcahr == 1" @group="group" />
|
||||
<el-empty description="时间范围内无谐波数据" v-if="item.showEcahr == 2" />
|
||||
</div>
|
||||
<!-- 生成动态谐波责任数据 -->
|
||||
<div class="box boxTab" v-loading="loading1" element-loading-background="#343849c7">
|
||||
<MyEChart :options="item.dynamicOptions" style="flex: 1" v-if="item.showDynamic" />
|
||||
<div style="width: 500px" v-if="item.showDynamic" class="tableBox">
|
||||
<el-table :scrollbar-always-on="true" ref="tableRef" :data="item.dynamicData" height="280px"
|
||||
size="small" :header-cell-style="{ textAlign: 'center' }" border>
|
||||
<el-table-column prop="customerName" align="center" label="用户名(用户号)" />
|
||||
<el-table-column prop="responsibilityData" align="center" label="责任数据(%)" width="120">
|
||||
<template #default="scope">
|
||||
{{
|
||||
Math.floor(scope.row.responsibilityData * 10000) /
|
||||
10000
|
||||
}}
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</pane>
|
||||
</splitpanes>
|
||||
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted, reactive } from "vue";
|
||||
import "splitpanes/dist/splitpanes.css";
|
||||
import { Splitpanes, Pane } from "splitpanes";
|
||||
import PointTree from "@/components/tree/pointTree.vue";
|
||||
import { Edit, Right } from "@element-plus/icons-vue";
|
||||
import MyEChart from "@/components/echarts/MyEchart.vue";
|
||||
import { timeFormat } from "@/utils/common";
|
||||
import { yMethod } from "@/utils/echartMethod";
|
||||
import { Select, CaretRight, Document } from "@element-plus/icons-vue";
|
||||
import {
|
||||
userDataList,
|
||||
getHistoryHarmData,
|
||||
getDynamicData,
|
||||
getResponsibilityData,
|
||||
} from "@/api/manage_wx";
|
||||
const emit = defineEmits(["setTitle"]); // 打开弹窗
|
||||
const visible = ref(false);
|
||||
const size = ref(0);
|
||||
const dotList: any = ref({});
|
||||
const form: any = reactive({
|
||||
type: "1",
|
||||
index: [],
|
||||
loadDataId: "",
|
||||
});
|
||||
|
||||
const loadDataOptions: any = ref([]);
|
||||
const code = ref(3);
|
||||
const xAxisData = ref([]);
|
||||
const loading = ref(false);
|
||||
const loading1 = ref(false);
|
||||
const tabList: any = ref([]);
|
||||
const activeName = ref(0);
|
||||
const xValue = ref("");
|
||||
const showTabs = ref(false);
|
||||
|
||||
// 设置时间
|
||||
const timeFrame = ref(["", ""]);
|
||||
|
||||
const openDialog = () => {
|
||||
visible.value = true;
|
||||
};
|
||||
|
||||
const handleNodeClick = (data: any) => {
|
||||
if (data.level == 6) {
|
||||
dotList.value = data;
|
||||
setTimeout(() => {
|
||||
emit("setTitle", data.alias);
|
||||
}, 0)
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
// 处理日期禁用逻辑
|
||||
const handleDisabledDate = (date) => {
|
||||
// 定义时间边界
|
||||
const startLimit = new Date(timeFrame.value[0]).getTime() - 86400000; //向前推1天
|
||||
const endLimit = new Date(timeFrame.value[1]).setHours(23, 59, 59, 999);
|
||||
|
||||
// 如果日期不存在(选择今天时可能出现),不禁用
|
||||
if (!date) return false;
|
||||
|
||||
// 禁用 2025-08-01 之前和 2025-08-31 之后的日期
|
||||
return date.getTime() < startLimit || date.getTime() > endLimit;
|
||||
};
|
||||
// 这是按钮变色
|
||||
const setCode = (num: number) => {
|
||||
if (code.value == num) {
|
||||
return (code.value = 3);
|
||||
}
|
||||
code.value = num;
|
||||
};
|
||||
|
||||
// 确定
|
||||
const submit = () => {
|
||||
if (form.loadDataId == "") {
|
||||
return ElMessage.warning("请选择负荷数据");
|
||||
}
|
||||
if (form.index.length == 0) {
|
||||
return ElMessage.warning("请选择谐波次数");
|
||||
}
|
||||
|
||||
if (form.index.length == 0) {
|
||||
showTabs.value = false;
|
||||
} else {
|
||||
let timeList = loadDataOptions.value.filter(
|
||||
(item: any) => item.id == form.loadDataId
|
||||
)[0];
|
||||
showTabs.value = true;
|
||||
let list = JSON.parse(JSON.stringify(form.index)).sort((a, b) => a - b);
|
||||
tabList.value = [];
|
||||
list.forEach((item: any) => {
|
||||
tabList.value.push({
|
||||
label: item + "次谐波",
|
||||
key: item,
|
||||
time: [timeList.startTime, timeList.endTime],
|
||||
showExecute: false,
|
||||
form: {
|
||||
limit: "",
|
||||
time1: "",
|
||||
time2: "",
|
||||
},
|
||||
showEcahr: 3, //1显示echart 2显示无数据 3什么都没有
|
||||
options: {},
|
||||
dynamicOptions: {}, //动态echarts
|
||||
dynamicList: {}, //动态echarts
|
||||
showDynamic: false, //动态执行展示
|
||||
});
|
||||
timeFrame.value = [timeList.startTime, timeList.endTime];
|
||||
});
|
||||
code.value = 3;
|
||||
activeName.value = 0;
|
||||
}
|
||||
};
|
||||
|
||||
// 执行
|
||||
const execute = async (item: any, index: number) => {
|
||||
tabList.value[activeName.value].showDynamic = false;
|
||||
loading.value = true;
|
||||
await getHistoryHarmData({
|
||||
searchBeginTime: item.time[0],
|
||||
searchEndTime: item.time[1],
|
||||
type: form.type,
|
||||
time: item.key,
|
||||
lineId: dotList.value.id,
|
||||
})
|
||||
.then((res: any) => {
|
||||
if (res.code == "A0000") {
|
||||
let [min, max] = yMethod(
|
||||
res.data.historyData.map((item: any) => item.value + 0.1)
|
||||
);
|
||||
xAxisData.value = res.data.historyData.map((item: any) => item.time);
|
||||
tabList.value[index].options = {
|
||||
title: {
|
||||
text: "",
|
||||
},
|
||||
xAxis: {
|
||||
type: "time",
|
||||
name: "时间",
|
||||
axisLabel: {
|
||||
formatter: {
|
||||
day: "{MM}-{dd}",
|
||||
month: "{MM}",
|
||||
year: "{yyyy}",
|
||||
},
|
||||
},
|
||||
// 刻度线样式
|
||||
axisTick: {
|
||||
lineStyle: {
|
||||
color: "#fff", // 刻度线白色
|
||||
},
|
||||
},
|
||||
|
||||
// 轴线样式
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: "#fff", // 轴线白色
|
||||
},
|
||||
},
|
||||
},
|
||||
tooltip: {
|
||||
formatter(params: any) {
|
||||
xValue.value = params[0].value[0];
|
||||
let str = params[0].value[0] + "<br/>";
|
||||
for (let i = 0; i < params.length; i++) {
|
||||
str =
|
||||
str +
|
||||
params[i].marker +
|
||||
params[i].seriesName +
|
||||
":" +
|
||||
params[i].value[1] +
|
||||
"<br/>";
|
||||
}
|
||||
|
||||
return str;
|
||||
},
|
||||
},
|
||||
grid: {
|
||||
top: 30,
|
||||
right: 50,
|
||||
},
|
||||
legend: {
|
||||
show: false,
|
||||
},
|
||||
yAxis: {
|
||||
name: form.type == 1 ? "%" : "A",
|
||||
min: min,
|
||||
max: max,
|
||||
},
|
||||
toolbox: {
|
||||
show: false,
|
||||
},
|
||||
|
||||
series: [
|
||||
{
|
||||
name: item.key + (form.type == 1 ? "次谐波电压" : "次谐波电流"),
|
||||
data: res.data.historyData.map((item: any) => [
|
||||
item.time,
|
||||
Math.floor(item.value * 10000) / 10000,
|
||||
]),
|
||||
type: "line",
|
||||
symbol: "none",
|
||||
|
||||
markLine: {
|
||||
symbol: "none", // 去除箭头
|
||||
label: {
|
||||
show: false, // 隐藏标签
|
||||
},
|
||||
data: [
|
||||
{
|
||||
yAxis: "",
|
||||
},
|
||||
{
|
||||
xAxis: "",
|
||||
},
|
||||
{
|
||||
xAxis: "",
|
||||
},
|
||||
],
|
||||
// 样式配置
|
||||
lineStyle: {
|
||||
color: "red",
|
||||
type: "dashed", // 虚线
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
tabList.value[index].showEcahr = 1; //显示echart
|
||||
} else {
|
||||
ElMessage.warning(res.message);
|
||||
tabList.value[index].showEcahr = 2;
|
||||
}
|
||||
loading.value = false;
|
||||
})
|
||||
.catch(() => {
|
||||
tabList.value[index].showEcahr = 2;
|
||||
loading.value = false;
|
||||
});
|
||||
};
|
||||
const resDataId = ref("");
|
||||
// 生成动态谐波责任数据
|
||||
const generateFn = async () => {
|
||||
loading1.value = true;
|
||||
await getDynamicData({
|
||||
lineId: dotList.value.id,
|
||||
searchBeginTime: tabList.value[activeName.value].time[0],
|
||||
searchEndTime: tabList.value[activeName.value].time[1],
|
||||
time: tabList.value[activeName.value].key,
|
||||
type: form.type,
|
||||
userDataId: form.loadDataId,
|
||||
})
|
||||
.then((res: any) => {
|
||||
if (res.code == "A0000") {
|
||||
resDataId.value = res.data.responsibilityDataIndex;
|
||||
tabList.value[activeName.value].dynamicData = res.data.responsibilities;
|
||||
let [min, max] = yMethod(
|
||||
res.data.datas.map((item: any) => item.valueDatas).flat()
|
||||
);
|
||||
let series: any[] = [];
|
||||
let time: any[] = res.data.timeDatas.map((item: any) =>
|
||||
timeFormat(item)
|
||||
);
|
||||
res.data.datas.forEach((item: any) => {
|
||||
series.push({
|
||||
name: item.customerName,
|
||||
data: item.valueDatas.map((k: any, i: number) => [
|
||||
time[i],
|
||||
Math.floor(k * 10000) / 10000,
|
||||
]),
|
||||
type: "line",
|
||||
symbol: "none",
|
||||
});
|
||||
});
|
||||
|
||||
tabList.value[activeName.value].dynamicOptions = {
|
||||
title: {
|
||||
text: "",
|
||||
},
|
||||
xAxis: {
|
||||
type: "time",
|
||||
name: "时间",
|
||||
axisLabel: {
|
||||
formatter: {
|
||||
day: "{MM}-{dd}",
|
||||
month: "{MM}",
|
||||
year: "{yyyy}",
|
||||
},
|
||||
},
|
||||
// 刻度线样式
|
||||
axisTick: {
|
||||
lineStyle: {
|
||||
color: "#fff", // 刻度线白色
|
||||
},
|
||||
},
|
||||
|
||||
// 轴线样式
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: "#fff", // 轴线白色
|
||||
},
|
||||
},
|
||||
},
|
||||
tooltip: {
|
||||
formatter(params: any) {
|
||||
let str = params[0].value[0] + "<br/>";
|
||||
for (let i = 0; i < params.length; i++) {
|
||||
str =
|
||||
str +
|
||||
params[i].marker +
|
||||
params[i].seriesName +
|
||||
":" +
|
||||
params[i].value[1] +
|
||||
"<br/>";
|
||||
}
|
||||
|
||||
return str;
|
||||
},
|
||||
},
|
||||
grid: {
|
||||
top: 30,
|
||||
},
|
||||
legend: {
|
||||
show: false,
|
||||
},
|
||||
yAxis: {
|
||||
name: form.type == 1 ? "%" : "A",
|
||||
min: min,
|
||||
max: max,
|
||||
},
|
||||
toolbox: {
|
||||
show: false,
|
||||
},
|
||||
|
||||
options: {
|
||||
series: series,
|
||||
},
|
||||
};
|
||||
|
||||
tabList.value[activeName.value].showDynamic = true;
|
||||
} else {
|
||||
ElMessage.warning(res.message);
|
||||
}
|
||||
loading1.value = false;
|
||||
})
|
||||
.catch(() => {
|
||||
loading1.value = false;
|
||||
});
|
||||
loading1.value = false;
|
||||
};
|
||||
|
||||
// 生成指标
|
||||
const generateMetrics = async () => {
|
||||
if (tabList.value[activeName.value].form.limit == "")
|
||||
return ElMessage.warning("请选择限值!");
|
||||
if (tabList.value[activeName.value].form.time1 == "")
|
||||
return ElMessage.warning("请选择时间一!");
|
||||
if (tabList.value[activeName.value].form.time2 == "")
|
||||
return ElMessage.warning("请选择时间二!");
|
||||
loading1.value = true;
|
||||
await getResponsibilityData({
|
||||
limitEndTime: tabList.value[activeName.value].form.time2,
|
||||
limitStartTime: tabList.value[activeName.value].form.time1,
|
||||
limitValue: tabList.value[activeName.value].form.limit,
|
||||
resDataId: resDataId.value,
|
||||
time: tabList.value[activeName.value].key,
|
||||
type: form.type,
|
||||
})
|
||||
.then((res: any) => {
|
||||
tabList.value[activeName.value].dynamicData = res.data.responsibilities;
|
||||
let [min, max] = yMethod(
|
||||
res.data.datas.map((item: any) => item.valueDatas).flat()
|
||||
);
|
||||
let series: any[] = [];
|
||||
let time: any[] = res.data.timeDatas.map((item: any) => timeFormat(item));
|
||||
res.data.datas.forEach((item: any) => {
|
||||
series.push({
|
||||
name: item.customerName,
|
||||
data: item.valueDatas.map((k: any, i: number) => [
|
||||
time[i],
|
||||
Math.floor(k * 10000) / 10000,
|
||||
]),
|
||||
type: "line",
|
||||
smooth: true,
|
||||
symbol: "none",
|
||||
});
|
||||
});
|
||||
|
||||
tabList.value[activeName.value].dynamicOptions = {
|
||||
title: {
|
||||
text: "",
|
||||
},
|
||||
xAxis: {
|
||||
type: "time",
|
||||
name: "时间",
|
||||
axisLabel: {
|
||||
formatter: {
|
||||
day: "{MM}-{dd}",
|
||||
month: "{MM}",
|
||||
year: "{yyyy}",
|
||||
},
|
||||
},
|
||||
},
|
||||
tooltip: {
|
||||
formatter(params: any) {
|
||||
let str = params[0].value[0] + "<br/>";
|
||||
for (let i = 0; i < params.length; i++) {
|
||||
str =
|
||||
str +
|
||||
params[i].marker +
|
||||
params[i].seriesName +
|
||||
":" +
|
||||
params[i].value[1] +
|
||||
"<br/>";
|
||||
}
|
||||
|
||||
return str;
|
||||
},
|
||||
},
|
||||
grid: {
|
||||
top: 30,
|
||||
},
|
||||
legend: {
|
||||
show: false,
|
||||
},
|
||||
yAxis: {
|
||||
name: form.type == 1 ? "%" : "A",
|
||||
min: min,
|
||||
max: max,
|
||||
},
|
||||
toolbox: {
|
||||
show: false,
|
||||
},
|
||||
|
||||
options: {
|
||||
series: series,
|
||||
},
|
||||
};
|
||||
|
||||
tabList.value[activeName.value].showDynamic = true;
|
||||
})
|
||||
.catch(() => {
|
||||
loading1.value = false;
|
||||
});
|
||||
loading1.value = false;
|
||||
};
|
||||
// 监听echart点击
|
||||
const group = (chart: any, myChartDom: any) => {
|
||||
myChartDom.addEventListener("click", function (event: any) {
|
||||
// 获取点击位置相对于图表容器的坐标
|
||||
const rect = myChartDom.getBoundingClientRect();
|
||||
const x = event.clientX - rect.left;
|
||||
const y = event.clientY - rect.top;
|
||||
const pointInPixel = [x, y];
|
||||
// 转换为逻辑坐标(相对于图表坐标系)
|
||||
const pointInGrid = chart.convertFromPixel({ gridIndex: 0 }, pointInPixel);
|
||||
// 计算X轴和Y轴的对应数据
|
||||
|
||||
// 处理X轴数据(分类轴)
|
||||
|
||||
// 处理Y轴数据(数值轴)
|
||||
let yValue = pointInGrid[1].toFixed(4);
|
||||
// xValue = timeFormat(pointInGrid[0].toFixed(0) - 0)
|
||||
if (code.value == 0) {
|
||||
tabList.value[activeName.value].form.limit = yValue;
|
||||
tabList.value[activeName.value].options.series[0].markLine.data[0].yAxis =
|
||||
yValue;
|
||||
chart.setOption(tabList.value[activeName.value].options);
|
||||
} else if (code.value == 1) {
|
||||
tabList.value[activeName.value].form.time1 = xValue.value;
|
||||
tabList.value[activeName.value].options.series[0].markLine.data[1].xAxis =
|
||||
xValue.value;
|
||||
chart.setOption(tabList.value[activeName.value].options);
|
||||
} else if (code.value == 2) {
|
||||
tabList.value[activeName.value].form.time2 = xValue.value;
|
||||
tabList.value[activeName.value].options.series[0].markLine.data[2].xAxis =
|
||||
xValue.value;
|
||||
chart.setOption(tabList.value[activeName.value].options);
|
||||
}
|
||||
// 控制台输出详细信息
|
||||
// console.log('点击事件详情:', {
|
||||
// X轴数据: xValue,
|
||||
// Y轴数据: yValue
|
||||
// })
|
||||
});
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
const dom = document.getElementById("navigation-splitpanes");
|
||||
if (dom) {
|
||||
size.value = Math.round((180 / dom.offsetHeight) * 100);
|
||||
}
|
||||
userDataList({
|
||||
pageNum: 1,
|
||||
pageSize: 10000,
|
||||
searchValue: "",
|
||||
}).then((res: any) => {
|
||||
console.log(res.data);
|
||||
loadDataOptions.value = res.data.records;
|
||||
});
|
||||
|
||||
if (!dotList.value || !dotList.value.alias) {
|
||||
dotList.value = { alias: "请选择监测点位" };
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
defineExpose({
|
||||
openDialog,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@use "@/assets/scss/index.scss";
|
||||
|
||||
.box {
|
||||
// height: calc((100vh - 190px) / 2);
|
||||
height: 300px;
|
||||
}
|
||||
|
||||
.boxTab {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.harmonicButton {
|
||||
height: 42px;
|
||||
margin-top: 10px;
|
||||
display: flex;
|
||||
justify-content: end;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
:deep(.el-tabs__content) {
|
||||
height: calc(100vh - 265px);
|
||||
}
|
||||
|
||||
:deep(.el-input-group__append, .el-input-group__prepend) {
|
||||
background-color: #ffffff00;
|
||||
padding: 0 17px;
|
||||
}
|
||||
|
||||
:deep(.frontBox) {
|
||||
background-color: var(--el-color-primary) !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
::v-deep(.el-tabs__item) {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
::v-deep(.el-tabs__item.is-active) {
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
::v-deep(.el-tabs__nav-wrap::after) {
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
::v-deep(.el-tabs__active-bar) {
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
::v-deep(.splitpanes__splitter) {
|
||||
background-color: rgba(44, 46, 60, 0.1) !important;
|
||||
|
||||
&:before {
|
||||
background-color: #fff !important;
|
||||
}
|
||||
|
||||
&:after {
|
||||
background-color: #fffFFF80 !important;
|
||||
}
|
||||
}
|
||||
|
||||
::v-deep(.el-form-item) {
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
.currentPosition {
|
||||
position: absolute;
|
||||
top: -10px;
|
||||
right: 10px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,255 @@
|
||||
<template>
|
||||
<!--谐波放大事件 -->
|
||||
<el-dialog
|
||||
:close-on-click-modal="false"
|
||||
draggable
|
||||
v-model="machineVisible"
|
||||
:title="title"
|
||||
width="1200px"
|
||||
>
|
||||
<div class="tableBox">
|
||||
<div style="display: flex; justify-content: flex-end">
|
||||
<el-button
|
||||
type="primary"
|
||||
:icon="Download"
|
||||
@click="exportTable"
|
||||
size="small"
|
||||
>导出
|
||||
</el-button>
|
||||
</div>
|
||||
<el-table
|
||||
:scrollbar-always-on="true"
|
||||
:data="tableData"
|
||||
height="443px"
|
||||
size="small"
|
||||
v-loading="loading"
|
||||
element-loading-background="#343849c7"
|
||||
:header-cell-style="{ textAlign: 'center' }"
|
||||
border
|
||||
style="margin-top: 13px"
|
||||
>
|
||||
<el-table-column label="序号" align="center" type="index" width="70">
|
||||
<template #default="scope">
|
||||
<span>{{
|
||||
(form.pageNum - 1) * form.pageSize + scope.$index + 1
|
||||
}}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="monitorName"
|
||||
align="center"
|
||||
label="监测点名称"
|
||||
width="120"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column
|
||||
prop="objName"
|
||||
align="center"
|
||||
label="用户"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column
|
||||
prop="startTime"
|
||||
align="center"
|
||||
label="开始时间"
|
||||
width="140"
|
||||
/>
|
||||
<el-table-column
|
||||
prop="endTime"
|
||||
align="center"
|
||||
label="结束时间"
|
||||
width="140"
|
||||
/>
|
||||
<el-table-column
|
||||
prop="harmonicCount"
|
||||
align="center"
|
||||
label="次数"
|
||||
width="60"
|
||||
/>
|
||||
<el-table-column prop="phase" align="center" label="相别" width="60" />
|
||||
<el-table-column
|
||||
prop="duration"
|
||||
align="center"
|
||||
label="持续时间(min)"
|
||||
width="100"
|
||||
>
|
||||
<template #default="scope">
|
||||
{{ scope.row.duration / 60 }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="vavgValue"
|
||||
align="center"
|
||||
label="电压标准值"
|
||||
width="80"
|
||||
/>
|
||||
<el-table-column
|
||||
prop="iavgValue"
|
||||
align="center"
|
||||
label="电流标准值"
|
||||
width="80"
|
||||
>
|
||||
<template #default="scope">
|
||||
{{ Math.floor(scope.row.iavgValue * 100) / 100 }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="upScheme"
|
||||
align="center"
|
||||
label="处理措施"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column prop="address" align="center" label="操作" width="60">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
size="small"
|
||||
type="primary"
|
||||
link
|
||||
@click="arendChart(scope.row)"
|
||||
>趋势图</el-button
|
||||
>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
<el-pagination
|
||||
size="small"
|
||||
style="margin-top: 10px"
|
||||
:currentPage="form.pageNum"
|
||||
:page-size="form.pageSize"
|
||||
:page-sizes="[10, 20, 50, 100, 200]"
|
||||
background
|
||||
:layout="'sizes,total, ->, prev, pager, next, jumper'"
|
||||
:total="total"
|
||||
@size-change="onTableSizeChange"
|
||||
@current-change="onTableCurrentChange"
|
||||
></el-pagination>
|
||||
</el-dialog>
|
||||
<TrendChart ref="trendChartRef" @close="machineVisible = true"></TrendChart>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive, inject, nextTick } from "vue";
|
||||
import { getDetail } from "@/api/manage_wx";
|
||||
import TrendChart from "./trendChart.vue";
|
||||
import { Download } from "@element-plus/icons-vue";
|
||||
import * as XLSX from "xlsx";
|
||||
|
||||
const machineVisible = ref(false);
|
||||
const title = ref("谐波放大事件");
|
||||
const form = reactive({
|
||||
searchBeginTime: "",
|
||||
lineId: "",
|
||||
pageNum: 1,
|
||||
pageSize: 20,
|
||||
});
|
||||
const total = ref(0); // 假设总条数为100
|
||||
const tableData = ref([]);
|
||||
const loading = ref(false);
|
||||
const trendChartRef = ref();
|
||||
//form表单校验规则
|
||||
const open = (text: string, time?: any, lineId?: any) => {
|
||||
machineVisible.value = true;
|
||||
form.lineId = lineId;
|
||||
form.searchBeginTime = time;
|
||||
nextTick(() => {
|
||||
getTableData();
|
||||
});
|
||||
};
|
||||
const onTableSizeChange = (size: number) => {
|
||||
form.pageSize = size;
|
||||
getTableData();
|
||||
};
|
||||
const onTableCurrentChange = (page: number) => {
|
||||
form.pageNum = page;
|
||||
getTableData();
|
||||
};
|
||||
|
||||
const getTableData = async () => {
|
||||
loading.value = true;
|
||||
const res: any = await getDetail(form);
|
||||
|
||||
if (res.code == "A0000") {
|
||||
tableData.value = res.data.records;
|
||||
total.value = res.data.total;
|
||||
}
|
||||
loading.value = false;
|
||||
};
|
||||
|
||||
// 趋势图
|
||||
const arendChart = (row: any) => {
|
||||
trendChartRef.value.open(row);
|
||||
machineVisible.value = false;
|
||||
};
|
||||
|
||||
// 导出
|
||||
const exportTable = async () => {
|
||||
let columnExpor: any = [
|
||||
[
|
||||
"监测点名称",
|
||||
"用户",
|
||||
"开始时间",
|
||||
"结束时间",
|
||||
"次数",
|
||||
"相别",
|
||||
"持续时间(min)",
|
||||
"电压标准值",
|
||||
"电流标准值",
|
||||
],
|
||||
];
|
||||
|
||||
let list = [];
|
||||
|
||||
await getDetail({
|
||||
...form,
|
||||
pageNum: 1,
|
||||
pageSize: total.value,
|
||||
}).then((res) => {
|
||||
let data = res.data.records.map((item) => {
|
||||
return [
|
||||
item.monitorName,
|
||||
item.objName,
|
||||
item.startTime,
|
||||
item.endTime,
|
||||
item.harmonicCount,
|
||||
item.phase,
|
||||
item.duration / 60,
|
||||
item.vavgValue,
|
||||
Math.floor(item.iavgValue * 100) / 100,
|
||||
];
|
||||
});
|
||||
list = [...columnExpor, ...data];
|
||||
// 创建工作表
|
||||
const worksheet = XLSX.utils.aoa_to_sheet(list);
|
||||
|
||||
worksheet["!cols"] = list.map((col) => ({ wch: 20 }));
|
||||
|
||||
// 创建工作簿
|
||||
const workbook = XLSX.utils.book_new();
|
||||
XLSX.utils.book_append_sheet(workbook, worksheet, "谐波放大事件");
|
||||
|
||||
// 写出文件
|
||||
XLSX.writeFile(workbook, "谐波放大事件" + ".xlsx");
|
||||
});
|
||||
};
|
||||
|
||||
defineExpose({ open });
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@use "@/assets/scss/index.scss";
|
||||
|
||||
.tableBox {
|
||||
padding: 0px !important;
|
||||
}
|
||||
|
||||
.formBox {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.formLeft {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: #fff;
|
||||
}
|
||||
</style>
|
||||
1384
src/views/SagTraceResult_WX/components/eventStatistics/index.vue
Normal file
@@ -0,0 +1,667 @@
|
||||
<template>
|
||||
<!-- 配网计算 -->
|
||||
<splitpanes style="height: 100%" class="default-theme" id="navigation-splitpanes">
|
||||
<pane :size="20">
|
||||
<SystemTree :showSelect="false" @node-click="handleNodeClick" @init="handleNodeClick"></SystemTree>
|
||||
</pane>
|
||||
|
||||
<pane style="background-color: rgba(44, 46, 60, 0.1)" :size="100 - size">
|
||||
|
||||
<el-form :model="form" inline label-width="auto">
|
||||
<el-form-item label="谐波类型">
|
||||
<el-radio-group v-model="form.type">
|
||||
<el-radio-button label="谐波电压" size="small" value="1" />
|
||||
<el-radio-button label="谐波电流" size="small" value="0" />
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="谐波次数">
|
||||
<el-select v-model="form.index" filterable multiple :multiple-limit="5" collapse-tags
|
||||
collapse-tags-tooltip clearable placeholder="请选择次数" style="width: 200px" size="small">
|
||||
<el-option v-for="item in 49" :key="item" :label="item + 1 + '次'" :value="item + 1"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<!-- <el-button type="primary" icon="el-icon-Plus" @click="push('/admin/division/aListOfLoadData')">
|
||||
新增
|
||||
</el-button> -->
|
||||
<el-button type="primary" :icon="Select" @click="submit" size="small">确定</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-tabs v-model="activeName" v-if="showTabs" style="height: 740px">
|
||||
<el-tab-pane v-for="(item, index) in tabList" :key="item" :label="item.label" :name="index"
|
||||
style="height: 100%">
|
||||
<div>
|
||||
<div>
|
||||
<span style="color: #fff">时间范围</span>
|
||||
<el-date-picker v-model="item.time" class="mr10 ml10" type="daterange"
|
||||
start-placeholder="起始时间" end-placeholder="结束时间" format="YYYY-MM-DD"
|
||||
date-format="YYYY-MM-DD" time-format="YYYY-MM-DD" value-format="YYYY-MM-DD"
|
||||
:disabled-date="handleDisabledDate" size="small" />
|
||||
<el-button type="primary" :icon="CaretRight" @click="execute(item, index)" size="small">
|
||||
执行
|
||||
</el-button>
|
||||
</div>
|
||||
<div v-if="item.showEcahr == 1" class="harmonicButton">
|
||||
<el-form :inline="true" v-model="item.form">
|
||||
<el-form-item label="限值" v-if="item.showDynamic">
|
||||
<el-input v-model="item.form.limit" placeholder="请选择限值" disabled
|
||||
style="width: 200px" size="small">
|
||||
<template #append>
|
||||
<el-button :icon="Edit" :class="[code == 0 ? 'frontBox' : '']"
|
||||
@click="setCode(0)" size="small" />
|
||||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="时间点一" v-if="item.showDynamic">
|
||||
<el-input v-model="item.form.time1" placeholder="请选择时间点一" disabled
|
||||
style="width: 200px" size="small">
|
||||
<template #append>
|
||||
<el-button :icon="Edit" :class="[code == 1 ? 'frontBox' : '']"
|
||||
@click="setCode(1)" size="small" />
|
||||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="时间点二" v-if="item.showDynamic">
|
||||
<el-input v-model="item.form.time2" placeholder="请选择时间点二" disabled
|
||||
style="width: 200px" size="small">
|
||||
<template #append>
|
||||
<el-button :icon="Edit" :class="[code == 2 ? 'frontBox' : '']"
|
||||
@click="setCode(2)" size="small" />
|
||||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
|
||||
<el-button type="primary" :icon="Document" @click="generateFn" v-if="!item.showDynamic"
|
||||
size="small">
|
||||
生成动态谐波责任数据
|
||||
</el-button>
|
||||
<el-button type="primary" :icon="Document" v-else @click="generateMetrics" size="small">
|
||||
生成谐波责任指标
|
||||
</el-button>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<div class="box" v-loading="loading" element-loading-background="#343849c7">
|
||||
<MyEChart :options="item.options" v-if="item.showEcahr == 1" @group="group" />
|
||||
<el-empty description="时间范围内无谐波数据" v-if="item.showEcahr == 2" />
|
||||
</div>
|
||||
<!-- 生成动态谐波责任数据 -->
|
||||
<div class="box boxTab" v-loading="loading1" element-loading-background="#343849c7">
|
||||
<MyEChart :options="item.dynamicOptions" style="flex: 1" v-if="item.showDynamic" />
|
||||
<div style="width: 500px" v-if="item.showDynamic" class="tableBox">
|
||||
<el-table :scrollbar-always-on="true" ref="tableRef" :data="item.dynamicData"
|
||||
height="280px" size="small" :header-cell-style="{ textAlign: 'center' }" border>
|
||||
<el-table-column prop="customerName" align="center" label="用户名(用户号)" />
|
||||
<el-table-column prop="responsibilityData" align="center" label="责任数据(%)"
|
||||
width="120">
|
||||
<template #default="scope">
|
||||
{{
|
||||
Math.floor(scope.row.responsibilityData * 10000) /
|
||||
10000
|
||||
}}
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</pane>
|
||||
</splitpanes>
|
||||
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted, reactive } from "vue";
|
||||
import "splitpanes/dist/splitpanes.css";
|
||||
import { Splitpanes, Pane } from "splitpanes";
|
||||
import SystemTree from "@/components/tree/systemTree.vue";
|
||||
import { Edit, Right } from "@element-plus/icons-vue";
|
||||
import MyEChart from "@/components/echarts/MyEchart.vue";
|
||||
import { timeFormat } from "@/utils/common";
|
||||
import { yMethod } from "@/utils/echartMethod";
|
||||
import { Select, CaretRight, Document } from "@element-plus/icons-vue";
|
||||
import {
|
||||
getHistoryHarmData,
|
||||
getDynamicData,
|
||||
getResponsibilityData,
|
||||
} from "@/api/manage_wx";
|
||||
const visible = ref(false);
|
||||
const size = ref(0);
|
||||
const dotList: any = ref({});
|
||||
const form: any = reactive({
|
||||
type: "1",
|
||||
index: [],
|
||||
});
|
||||
|
||||
const code = ref(3);
|
||||
const xAxisData = ref([]);
|
||||
const loading = ref(false);
|
||||
const loading1 = ref(false);
|
||||
const tabList: any = ref([]);
|
||||
const activeName = ref(0);
|
||||
const xValue = ref("");
|
||||
const showTabs = ref(false);
|
||||
|
||||
// 设置时间
|
||||
const timeFrame = ref(["", ""]);
|
||||
|
||||
const openDialog = () => {
|
||||
visible.value = true;
|
||||
};
|
||||
|
||||
const handleNodeClick = (data: any) => {
|
||||
if (data.level == 6) {
|
||||
dotList.value = data;
|
||||
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
// 处理日期禁用逻辑
|
||||
const handleDisabledDate = (date) => {
|
||||
return date.getTime() > Date.now();
|
||||
};
|
||||
// 这是按钮变色
|
||||
const setCode = (num: number) => {
|
||||
if (code.value == num) {
|
||||
return (code.value = 3);
|
||||
}
|
||||
code.value = num;
|
||||
};
|
||||
|
||||
// 确定
|
||||
const submit = () => {
|
||||
|
||||
if (form.index.length == 0) {
|
||||
return ElMessage.warning("请选择谐波次数");
|
||||
}
|
||||
|
||||
if (form.index.length == 0) {
|
||||
showTabs.value = false;
|
||||
} else {
|
||||
|
||||
showTabs.value = true;
|
||||
let list = JSON.parse(JSON.stringify(form.index)).sort((a, b) => a - b);
|
||||
tabList.value = [];
|
||||
list.forEach((item: any) => {
|
||||
tabList.value.push({
|
||||
label: item + "次谐波",
|
||||
key: item,
|
||||
showExecute: false,
|
||||
form: {
|
||||
limit: "",
|
||||
time1: "",
|
||||
time2: "",
|
||||
},
|
||||
showEcahr: 3, //1显示echart 2显示无数据 3什么都没有
|
||||
options: {},
|
||||
dynamicOptions: {}, //动态echarts
|
||||
dynamicList: {}, //动态echarts
|
||||
showDynamic: false, //动态执行展示
|
||||
});
|
||||
});
|
||||
code.value = 3;
|
||||
activeName.value = 0;
|
||||
}
|
||||
};
|
||||
|
||||
// 执行
|
||||
const execute = async (item: any, index: number) => {
|
||||
tabList.value[activeName.value].showDynamic = false;
|
||||
loading.value = true;
|
||||
await getHistoryHarmData({
|
||||
searchBeginTime: item.time[0],
|
||||
searchEndTime: item.time[1],
|
||||
type: form.type,
|
||||
time: item.key,
|
||||
lineId: dotList.value.id,
|
||||
})
|
||||
.then((res: any) => {
|
||||
if (res.code == "A0000") {
|
||||
let [min, max] = yMethod(
|
||||
res.data.historyData.map((item: any) => item.value + 0.1)
|
||||
);
|
||||
xAxisData.value = res.data.historyData.map((item: any) => item.time);
|
||||
tabList.value[index].options = {
|
||||
title: {
|
||||
text: "",
|
||||
},
|
||||
xAxis: {
|
||||
type: "time",
|
||||
name: "时间",
|
||||
axisLabel: {
|
||||
formatter: {
|
||||
day: "{MM}-{dd}",
|
||||
month: "{MM}",
|
||||
year: "{yyyy}",
|
||||
},
|
||||
},
|
||||
// 刻度线样式
|
||||
axisTick: {
|
||||
lineStyle: {
|
||||
color: "#fff", // 刻度线白色
|
||||
},
|
||||
},
|
||||
|
||||
// 轴线样式
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: "#fff", // 轴线白色
|
||||
},
|
||||
},
|
||||
},
|
||||
tooltip: {
|
||||
formatter(params: any) {
|
||||
xValue.value = params[0].value[0];
|
||||
let str = params[0].value[0] + "<br/>";
|
||||
for (let i = 0; i < params.length; i++) {
|
||||
str =
|
||||
str +
|
||||
params[i].marker +
|
||||
params[i].seriesName +
|
||||
":" +
|
||||
params[i].value[1] +
|
||||
"<br/>";
|
||||
}
|
||||
|
||||
return str;
|
||||
},
|
||||
},
|
||||
grid: {
|
||||
top: 30,
|
||||
right: 50,
|
||||
},
|
||||
legend: {
|
||||
show: false,
|
||||
},
|
||||
yAxis: {
|
||||
name: form.type == 1 ? "%" : "A",
|
||||
min: min,
|
||||
max: max,
|
||||
},
|
||||
toolbox: {
|
||||
show: false,
|
||||
},
|
||||
|
||||
series: [
|
||||
{
|
||||
name: item.key + (form.type == 1 ? "次谐波电压" : "次谐波电流"),
|
||||
data: res.data.historyData.map((item: any) => [
|
||||
item.time,
|
||||
Math.floor(item.value * 10000) / 10000,
|
||||
]),
|
||||
type: "line",
|
||||
symbol: "none",
|
||||
markLine: {
|
||||
symbol: "none", // 去除箭头
|
||||
label: {
|
||||
show: false, // 隐藏标签
|
||||
},
|
||||
data: [
|
||||
{
|
||||
yAxis: "",
|
||||
},
|
||||
{
|
||||
xAxis: "",
|
||||
},
|
||||
{
|
||||
xAxis: "",
|
||||
},
|
||||
],
|
||||
// 样式配置
|
||||
lineStyle: {
|
||||
color: "red",
|
||||
type: "dashed", // 虚线
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
tabList.value[index].showEcahr = 1; //显示echart
|
||||
} else {
|
||||
ElMessage.warning(res.message);
|
||||
tabList.value[index].showEcahr = 2;
|
||||
}
|
||||
loading.value = false;
|
||||
})
|
||||
.catch(() => {
|
||||
tabList.value[index].showEcahr = 2;
|
||||
loading.value = false;
|
||||
});
|
||||
};
|
||||
const resDataId = ref("");
|
||||
// 生成动态谐波责任数据
|
||||
const generateFn = async () => {
|
||||
loading1.value = true;
|
||||
await getDynamicData({
|
||||
lineId: dotList.value.id,
|
||||
searchBeginTime: tabList.value[activeName.value].time[0],
|
||||
searchEndTime: tabList.value[activeName.value].time[1],
|
||||
time: tabList.value[activeName.value].key,
|
||||
type: form.type,
|
||||
})
|
||||
.then((res: any) => {
|
||||
if (res.code == "A0000") {
|
||||
resDataId.value = res.data.responsibilityDataIndex;
|
||||
tabList.value[activeName.value].dynamicData = res.data.responsibilities;
|
||||
let [min, max] = yMethod(
|
||||
res.data.datas.map((item: any) => item.valueDatas).flat()
|
||||
);
|
||||
let series: any[] = [];
|
||||
let time: any[] = res.data.timeDatas.map((item: any) =>
|
||||
timeFormat(item)
|
||||
);
|
||||
res.data.datas.forEach((item: any) => {
|
||||
series.push({
|
||||
name: item.customerName,
|
||||
data: item.valueDatas.map((k: any, i: number) => [
|
||||
time[i],
|
||||
Math.floor(k * 10000) / 10000,
|
||||
]),
|
||||
type: "line",
|
||||
smooth: true,
|
||||
symbol: "none",
|
||||
});
|
||||
});
|
||||
|
||||
tabList.value[activeName.value].dynamicOptions = {
|
||||
title: {
|
||||
text: "",
|
||||
},
|
||||
xAxis: {
|
||||
type: "time",
|
||||
name: "时间",
|
||||
axisLabel: {
|
||||
formatter: {
|
||||
day: "{MM}-{dd}",
|
||||
month: "{MM}",
|
||||
year: "{yyyy}",
|
||||
},
|
||||
},
|
||||
// 刻度线样式
|
||||
axisTick: {
|
||||
lineStyle: {
|
||||
color: "#fff", // 刻度线白色
|
||||
},
|
||||
},
|
||||
|
||||
// 轴线样式
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: "#fff", // 轴线白色
|
||||
},
|
||||
},
|
||||
},
|
||||
tooltip: {
|
||||
formatter(params: any) {
|
||||
let str = params[0].value[0] + "<br/>";
|
||||
for (let i = 0; i < params.length; i++) {
|
||||
str =
|
||||
str +
|
||||
params[i].marker +
|
||||
params[i].seriesName +
|
||||
":" +
|
||||
params[i].value[1] +
|
||||
"<br/>";
|
||||
}
|
||||
|
||||
return str;
|
||||
},
|
||||
},
|
||||
grid: {
|
||||
top: 30,
|
||||
},
|
||||
legend: {
|
||||
show: false,
|
||||
},
|
||||
yAxis: {
|
||||
name: form.type == 1 ? "%" : "A",
|
||||
min: min,
|
||||
max: max,
|
||||
},
|
||||
toolbox: {
|
||||
show: false,
|
||||
},
|
||||
|
||||
options: {
|
||||
series: series,
|
||||
},
|
||||
};
|
||||
|
||||
tabList.value[activeName.value].showDynamic = true;
|
||||
} else {
|
||||
ElMessage.warning(res.message);
|
||||
}
|
||||
loading1.value = false;
|
||||
})
|
||||
.catch(() => {
|
||||
loading1.value = false;
|
||||
});
|
||||
loading1.value = false;
|
||||
};
|
||||
|
||||
// 生成指标
|
||||
const generateMetrics = async () => {
|
||||
if (tabList.value[activeName.value].form.limit == "")
|
||||
return ElMessage.warning("请选择限值!");
|
||||
if (tabList.value[activeName.value].form.time1 == "")
|
||||
return ElMessage.warning("请选择时间一!");
|
||||
if (tabList.value[activeName.value].form.time2 == "")
|
||||
return ElMessage.warning("请选择时间二!");
|
||||
loading1.value = true;
|
||||
await getResponsibilityData({
|
||||
limitEndTime: tabList.value[activeName.value].form.time2,
|
||||
limitStartTime: tabList.value[activeName.value].form.time1,
|
||||
limitValue: tabList.value[activeName.value].form.limit,
|
||||
resDataId: resDataId.value,
|
||||
time: tabList.value[activeName.value].key,
|
||||
type: form.type,
|
||||
})
|
||||
.then((res: any) => {
|
||||
tabList.value[activeName.value].dynamicData = res.data.responsibilities;
|
||||
let [min, max] = yMethod(
|
||||
res.data.datas.map((item: any) => item.valueDatas).flat()
|
||||
);
|
||||
let series: any[] = [];
|
||||
let time: any[] = res.data.timeDatas.map((item: any) => timeFormat(item));
|
||||
res.data.datas.forEach((item: any) => {
|
||||
series.push({
|
||||
name: item.customerName,
|
||||
data: item.valueDatas.map((k: any, i: number) => [
|
||||
time[i],
|
||||
Math.floor(k * 10000) / 10000,
|
||||
]),
|
||||
type: "line",
|
||||
symbol: "none",
|
||||
});
|
||||
});
|
||||
|
||||
tabList.value[activeName.value].dynamicOptions = {
|
||||
title: {
|
||||
text: "",
|
||||
},
|
||||
xAxis: {
|
||||
type: "time",
|
||||
name: "时间",
|
||||
axisLabel: {
|
||||
formatter: {
|
||||
day: "{MM}-{dd}",
|
||||
month: "{MM}",
|
||||
year: "{yyyy}",
|
||||
},
|
||||
},
|
||||
},
|
||||
tooltip: {
|
||||
formatter(params: any) {
|
||||
let str = params[0].value[0] + "<br/>";
|
||||
for (let i = 0; i < params.length; i++) {
|
||||
str =
|
||||
str +
|
||||
params[i].marker +
|
||||
params[i].seriesName +
|
||||
":" +
|
||||
params[i].value[1] +
|
||||
"<br/>";
|
||||
}
|
||||
|
||||
return str;
|
||||
},
|
||||
},
|
||||
grid: {
|
||||
top: 30,
|
||||
},
|
||||
legend: {
|
||||
show: false,
|
||||
},
|
||||
yAxis: {
|
||||
name: form.type == 1 ? "%" : "A",
|
||||
min: min,
|
||||
max: max,
|
||||
},
|
||||
toolbox: {
|
||||
show: false,
|
||||
},
|
||||
|
||||
options: {
|
||||
series: series,
|
||||
},
|
||||
};
|
||||
|
||||
tabList.value[activeName.value].showDynamic = true;
|
||||
})
|
||||
.catch(() => {
|
||||
loading1.value = false;
|
||||
});
|
||||
loading1.value = false;
|
||||
};
|
||||
// 监听echart点击
|
||||
const group = (chart: any, myChartDom: any) => {
|
||||
myChartDom.addEventListener("click", function (event: any) {
|
||||
// 获取点击位置相对于图表容器的坐标
|
||||
const rect = myChartDom.getBoundingClientRect();
|
||||
const x = event.clientX - rect.left;
|
||||
const y = event.clientY - rect.top;
|
||||
const pointInPixel = [x, y];
|
||||
// 转换为逻辑坐标(相对于图表坐标系)
|
||||
const pointInGrid = chart.convertFromPixel({ gridIndex: 0 }, pointInPixel);
|
||||
// 计算X轴和Y轴的对应数据
|
||||
|
||||
// 处理X轴数据(分类轴)
|
||||
|
||||
// 处理Y轴数据(数值轴)
|
||||
let yValue = pointInGrid[1].toFixed(4);
|
||||
// xValue = timeFormat(pointInGrid[0].toFixed(0) - 0)
|
||||
if (code.value == 0) {
|
||||
tabList.value[activeName.value].form.limit = yValue;
|
||||
tabList.value[activeName.value].options.series[0].markLine.data[0].yAxis =
|
||||
yValue;
|
||||
chart.setOption(tabList.value[activeName.value].options);
|
||||
} else if (code.value == 1) {
|
||||
tabList.value[activeName.value].form.time1 = xValue.value;
|
||||
tabList.value[activeName.value].options.series[0].markLine.data[1].xAxis =
|
||||
xValue.value;
|
||||
chart.setOption(tabList.value[activeName.value].options);
|
||||
} else if (code.value == 2) {
|
||||
tabList.value[activeName.value].form.time2 = xValue.value;
|
||||
tabList.value[activeName.value].options.series[0].markLine.data[2].xAxis =
|
||||
xValue.value;
|
||||
chart.setOption(tabList.value[activeName.value].options);
|
||||
}
|
||||
// 控制台输出详细信息
|
||||
// console.log('点击事件详情:', {
|
||||
// X轴数据: xValue,
|
||||
// Y轴数据: yValue
|
||||
// })
|
||||
});
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
const dom = document.getElementById("navigation-splitpanes");
|
||||
if (dom) {
|
||||
size.value = Math.round((180 / dom.offsetHeight) * 100);
|
||||
}
|
||||
|
||||
|
||||
if (!dotList.value || !dotList.value.alias) {
|
||||
dotList.value = { alias: "请选择监测点位" };
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
defineExpose({
|
||||
openDialog,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@use "@/assets/scss/index.scss";
|
||||
|
||||
.box {
|
||||
// height: calc((100vh - 190px) / 2);
|
||||
height: 300px;
|
||||
}
|
||||
|
||||
.boxTab {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.harmonicButton {
|
||||
height: 42px;
|
||||
margin-top: 10px;
|
||||
display: flex;
|
||||
justify-content: end;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
:deep(.el-tabs__content) {
|
||||
height: calc(100vh - 265px);
|
||||
}
|
||||
|
||||
:deep(.el-input-group__append, .el-input-group__prepend) {
|
||||
background-color: #ffffff00;
|
||||
padding: 0 17px;
|
||||
}
|
||||
|
||||
:deep(.frontBox) {
|
||||
background-color: var(--el-color-primary) !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
::v-deep(.el-tabs__item) {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
::v-deep(.el-tabs__item.is-active) {
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
::v-deep(.el-tabs__nav-wrap::after) {
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
::v-deep(.el-tabs__active-bar) {
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
::v-deep(.splitpanes__splitter) {
|
||||
background-color: rgba(44, 46, 60, 0.1) !important;
|
||||
|
||||
&:before {
|
||||
background-color: #fff !important;
|
||||
}
|
||||
|
||||
&:after {
|
||||
background-color: #fffFFF80 !important;
|
||||
}
|
||||
}
|
||||
|
||||
::v-deep(.el-form-item) {
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,982 @@
|
||||
<template>
|
||||
<!-- 配网计算 -->
|
||||
<splitpanes
|
||||
style="height: 100%"
|
||||
class="default-theme"
|
||||
id="navigation-splitpanes"
|
||||
>
|
||||
<pane :size="20">
|
||||
<PointTree
|
||||
:showSelect="false"
|
||||
@node-click="handleNodeClick"
|
||||
@init="handleNodeClick"
|
||||
></PointTree>
|
||||
</pane>
|
||||
|
||||
<pane style="background-color: rgba(44, 46, 60, 0.1)" :size="100 - size">
|
||||
<el-form :model="form" inline label-width="auto">
|
||||
<el-form-item label="用采用户">
|
||||
<el-tree-select
|
||||
v-model="form.userList"
|
||||
:data="dataTree"
|
||||
multiple
|
||||
filterable
|
||||
show-checkbox
|
||||
ref="treeRef"
|
||||
:props="defaultProps"
|
||||
node-key="id"
|
||||
size="small"
|
||||
popper-class="tree-select-popper"
|
||||
:popper-append-to-body="false"
|
||||
:default-expanded-keys="expandedKeys"
|
||||
collapse-tags
|
||||
collapse-tags-tooltip
|
||||
clearable
|
||||
class="wide-tree-select"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="谐波类型">
|
||||
<el-radio-group v-model="form.type">
|
||||
<el-radio-button label="谐波电压" size="small" value="1" />
|
||||
<el-radio-button label="谐波电流" size="small" value="0" />
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="谐波次数">
|
||||
<el-select
|
||||
v-model="form.index"
|
||||
filterable
|
||||
multiple
|
||||
:multiple-limit="5"
|
||||
collapse-tags
|
||||
collapse-tags-tooltip
|
||||
clearable
|
||||
placeholder="请选择次数"
|
||||
style="width: 200px"
|
||||
size="small"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in 49"
|
||||
:key="item"
|
||||
:label="item + 1 + '次'"
|
||||
:value="item + 1"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<!-- <el-form-item label="负荷数据">
|
||||
<el-select v-model="form.loadDataId" clearable filterable placeholder="请选择负荷数据" style="width: 200px"
|
||||
size="small">
|
||||
<el-option v-for="item in loadDataOptions" :key="item.id" :label="item.name" :value="item.id"></el-option>
|
||||
</el-select>
|
||||
</el-form-item> -->
|
||||
<el-form-item>
|
||||
<!-- <el-button type="primary" icon="el-icon-Plus" @click="push('/admin/division/aListOfLoadData')">
|
||||
新增
|
||||
</el-button> -->
|
||||
<el-button type="primary" :icon="Select" @click="submit" size="small"
|
||||
>确定</el-button
|
||||
>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-tabs v-model="activeName" v-if="showTabs" style="height: 740px">
|
||||
<el-tab-pane
|
||||
v-for="(item, index) in tabList"
|
||||
:key="item"
|
||||
:label="item.label"
|
||||
:name="index"
|
||||
style="height: 100%"
|
||||
>
|
||||
<div>
|
||||
<div>
|
||||
<span style="color: #fff">时间范围</span>
|
||||
<el-date-picker
|
||||
v-model="item.time"
|
||||
class="mr10 ml10"
|
||||
type="daterange"
|
||||
start-placeholder="起始时间"
|
||||
end-placeholder="结束时间"
|
||||
format="YYYY-MM-DD"
|
||||
date-format="YYYY-MM-DD"
|
||||
time-format="YYYY-MM-DD"
|
||||
value-format="YYYY-MM-DD"
|
||||
:disabled-date="handleDisabledDate"
|
||||
size="small"
|
||||
/>
|
||||
<el-button
|
||||
type="primary"
|
||||
:icon="CaretRight"
|
||||
@click="execute(item, index)"
|
||||
size="small"
|
||||
>
|
||||
执行
|
||||
</el-button>
|
||||
</div>
|
||||
<div v-if="item.showEcahr == 1" class="harmonicButton">
|
||||
<el-form :inline="true" v-model="item.form">
|
||||
<el-form-item label="限值" v-if="item.showDynamic">
|
||||
<el-input
|
||||
v-model="item.form.limit"
|
||||
placeholder="请选择限值"
|
||||
disabled
|
||||
style="width: 200px"
|
||||
size="small"
|
||||
>
|
||||
<template #append>
|
||||
<el-button
|
||||
:icon="Edit"
|
||||
:class="[code == 0 ? 'frontBox' : '']"
|
||||
@click="setCode(0)"
|
||||
size="small"
|
||||
/>
|
||||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="时间点一" v-if="item.showDynamic">
|
||||
<el-input
|
||||
v-model="item.form.time1"
|
||||
placeholder="请选择时间点一"
|
||||
disabled
|
||||
style="width: 200px"
|
||||
size="small"
|
||||
>
|
||||
<template #append>
|
||||
<el-button
|
||||
:icon="Edit"
|
||||
:class="[code == 1 ? 'frontBox' : '']"
|
||||
@click="setCode(1)"
|
||||
size="small"
|
||||
/>
|
||||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="时间点二" v-if="item.showDynamic">
|
||||
<el-input
|
||||
v-model="item.form.time2"
|
||||
placeholder="请选择时间点二"
|
||||
disabled
|
||||
style="width: 200px"
|
||||
size="small"
|
||||
>
|
||||
<template #append>
|
||||
<el-button
|
||||
:icon="Edit"
|
||||
:class="[code == 2 ? 'frontBox' : '']"
|
||||
@click="setCode(2)"
|
||||
size="small"
|
||||
/>
|
||||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
|
||||
<el-button
|
||||
type="primary"
|
||||
:icon="Document"
|
||||
@click="generateFn"
|
||||
v-if="!item.showDynamic"
|
||||
size="small"
|
||||
>
|
||||
生成动态谐波责任数据
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
:icon="Document"
|
||||
v-else
|
||||
@click="generateMetrics"
|
||||
size="small"
|
||||
>
|
||||
生成谐波责任指标
|
||||
</el-button>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="box"
|
||||
v-loading="loading"
|
||||
element-loading-background="#343849c7"
|
||||
>
|
||||
<MyEChart
|
||||
:options="item.options"
|
||||
v-if="item.showEcahr == 1"
|
||||
@group="group"
|
||||
/>
|
||||
<el-empty
|
||||
description="时间范围内无谐波数据"
|
||||
v-if="item.showEcahr == 2"
|
||||
/>
|
||||
</div>
|
||||
<!-- 生成动态谐波责任数据 -->
|
||||
<div
|
||||
class="box boxTab"
|
||||
v-loading="loading1"
|
||||
element-loading-background="#343849c7"
|
||||
>
|
||||
<MyEChart
|
||||
:options="item.dynamicOptions"
|
||||
style="flex: 1"
|
||||
v-if="item.showDynamic"
|
||||
/>
|
||||
<div
|
||||
style="width: 500px"
|
||||
v-if="item.showDynamic"
|
||||
class="tableBox"
|
||||
>
|
||||
<el-table
|
||||
:scrollbar-always-on="true"
|
||||
ref="tableRef"
|
||||
:data="item.dynamicData"
|
||||
height="280px"
|
||||
size="small"
|
||||
:header-cell-style="{ textAlign: 'center' }"
|
||||
border
|
||||
>
|
||||
<el-table-column
|
||||
prop="customerName"
|
||||
align="center"
|
||||
label="用户名(用户号)"
|
||||
/>
|
||||
<el-table-column
|
||||
prop="responsibilityData"
|
||||
align="center"
|
||||
label="责任数据(%)"
|
||||
width="120"
|
||||
>
|
||||
<template #default="scope">
|
||||
{{
|
||||
Math.floor(scope.row.responsibilityData * 10000) / 10000
|
||||
}}
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</pane>
|
||||
</splitpanes>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted, reactive, watch } from "vue";
|
||||
import "splitpanes/dist/splitpanes.css";
|
||||
import { Splitpanes, Pane } from "splitpanes";
|
||||
import PointTree from "@/components/tree/pointTree.vue";
|
||||
import { Edit, Right } from "@element-plus/icons-vue";
|
||||
import MyEChart from "@/components/echarts/MyEchart.vue";
|
||||
import { timeFormat } from "@/utils/common";
|
||||
import { yMethod } from "@/utils/echartMethod";
|
||||
import { Select, CaretRight, Document } from "@element-plus/icons-vue";
|
||||
import {
|
||||
userDataList,
|
||||
getHistoryHarmData,
|
||||
getDynamicData,
|
||||
getResponsibilityData,
|
||||
getTerminalTreeForFive,
|
||||
} from "@/api/manage_wx";
|
||||
import { useStore } from "vuex";
|
||||
|
||||
const store = useStore();
|
||||
const emit = defineEmits(["setTitle", "init"]); // 打开弹窗
|
||||
const visible = ref(false);
|
||||
const size = ref(0);
|
||||
const dotList: any = ref({});
|
||||
const form: any = reactive({
|
||||
type: "1",
|
||||
index: [],
|
||||
userList: [],
|
||||
});
|
||||
|
||||
const loadDataOptions: any = ref([]);
|
||||
const code = ref(3);
|
||||
const xAxisData = ref([]);
|
||||
const loading = ref(false);
|
||||
const loading1 = ref(false);
|
||||
const tabList: any = ref([]);
|
||||
const activeName = ref(0);
|
||||
const xValue = ref("");
|
||||
const showTabs = ref(false);
|
||||
|
||||
// 设置时间
|
||||
const timeFrame = ref(["", ""]);
|
||||
|
||||
const openDialog = () => {
|
||||
visible.value = true;
|
||||
};
|
||||
|
||||
// 添加已选择的节点ID列表
|
||||
const selectedNodeIds = ref<string[]>([]);
|
||||
|
||||
const handleNodeClick = (data: any) => {
|
||||
if (data.level == 6) {
|
||||
dotList.value = data;
|
||||
setTimeout(() => {
|
||||
emit("setTitle", data.alias);
|
||||
}, 0);
|
||||
// 保存选择的节点ID
|
||||
if (data.id) {
|
||||
selectedNodeIds.value = [data.id];
|
||||
}
|
||||
// 如果右侧已经选择了这个节点,则从右侧选择中移除
|
||||
const selectedUserIdIndex = form.userList.indexOf(data.id);
|
||||
if (selectedUserIdIndex > -1) {
|
||||
// 创建新数组以触发视图更新
|
||||
form.userList = form.userList.filter((id: string) => id !== data.id);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 添加一个方法来更新树节点的禁用状态
|
||||
const updateTreeDataDisabledState = () => {
|
||||
if (!dataTree.value || dataTree.value.length === 0) return;
|
||||
|
||||
const disableNodes = (nodes: any[]) => {
|
||||
if (!nodes || nodes.length === 0) return;
|
||||
|
||||
nodes.forEach((node) => {
|
||||
// 如果节点ID在已选择列表中,则禁用
|
||||
if (selectedNodeIds.value.includes(node.id)) {
|
||||
node.disabled = true;
|
||||
} else {
|
||||
// 如果之前被禁用但现在不在选择列表中,则启用
|
||||
if (node.hasOwnProperty("disabled")) {
|
||||
node.disabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
// 递归处理子节点
|
||||
if (node.children && node.children.length > 0) {
|
||||
disableNodes(node.children);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
disableNodes(dataTree.value);
|
||||
};
|
||||
|
||||
// 监听 selectedNodeIds 的变化,更新树节点状态
|
||||
watch(
|
||||
selectedNodeIds,
|
||||
() => {
|
||||
updateTreeDataDisabledState();
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
// 处理日期禁用逻辑
|
||||
const handleDisabledDate = (date) => {
|
||||
// 定义时间边界
|
||||
const startLimit = new Date(timeFrame.value[0]).getTime() - 86400000; //向前推1天
|
||||
const endLimit = new Date(timeFrame.value[1]).setHours(23, 59, 59, 999);
|
||||
|
||||
// 如果日期不存在(选择今天时可能出现),不禁用
|
||||
if (!date) return false;
|
||||
|
||||
// 禁用 2025-08-01 之前和 2025-08-31 之后的日期
|
||||
return date.getTime() < startLimit || date.getTime() > endLimit;
|
||||
};
|
||||
// 这是按钮变色
|
||||
const setCode = (num: number) => {
|
||||
if (code.value == num) {
|
||||
return (code.value = 3);
|
||||
}
|
||||
code.value = num;
|
||||
};
|
||||
|
||||
// 确定
|
||||
const submit = () => {
|
||||
if (form.index.length == 0) {
|
||||
return ElMessage.warning("请选择谐波次数");
|
||||
}
|
||||
|
||||
if (form.index.length == 0) {
|
||||
showTabs.value = false;
|
||||
} else {
|
||||
showTabs.value = true;
|
||||
let list = JSON.parse(JSON.stringify(form.index)).sort((a, b) => a - b);
|
||||
tabList.value = [];
|
||||
list.forEach((item: any) => {
|
||||
tabList.value.push({
|
||||
label: item + "次谐波",
|
||||
key: item,
|
||||
showExecute: false,
|
||||
form: {
|
||||
limit: "",
|
||||
time1: "",
|
||||
time2: "",
|
||||
},
|
||||
showEcahr: 3, //1显示echart 2显示无数据 3什么都没有
|
||||
options: {},
|
||||
dynamicOptions: {}, //动态echarts
|
||||
dynamicList: {}, //动态echarts
|
||||
showDynamic: false, //动态执行展示
|
||||
});
|
||||
});
|
||||
code.value = 3;
|
||||
activeName.value = 0;
|
||||
}
|
||||
};
|
||||
|
||||
// 执行
|
||||
const execute = async (item: any, index: number) => {
|
||||
if (item.time == undefined) {
|
||||
return ElMessage.warning("请选择时间范围");
|
||||
}
|
||||
tabList.value[activeName.value].showDynamic = false;
|
||||
loading.value = true;
|
||||
await getHistoryHarmData({
|
||||
searchBeginTime: item.time[0],
|
||||
searchEndTime: item.time[1],
|
||||
type: form.type,
|
||||
time: item.key,
|
||||
lineId: dotList.value.id,
|
||||
})
|
||||
.then((res: any) => {
|
||||
if (res.code == "A0000") {
|
||||
let [min, max] = yMethod(
|
||||
res.data.historyData.map((item: any) => item.value + 0.1)
|
||||
);
|
||||
xAxisData.value = res.data.historyData.map((item: any) => item.time);
|
||||
tabList.value[index].options = {
|
||||
title: {
|
||||
text: "",
|
||||
},
|
||||
xAxis: {
|
||||
type: "time",
|
||||
name: "时间",
|
||||
axisLabel: {
|
||||
formatter: {
|
||||
day: "{MM}-{dd}",
|
||||
month: "{MM}",
|
||||
year: "{yyyy}",
|
||||
},
|
||||
},
|
||||
// 刻度线样式
|
||||
axisTick: {
|
||||
lineStyle: {
|
||||
color: "#fff", // 刻度线白色
|
||||
},
|
||||
},
|
||||
|
||||
// 轴线样式
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: "#fff", // 轴线白色
|
||||
},
|
||||
},
|
||||
},
|
||||
tooltip: {
|
||||
formatter(params: any) {
|
||||
xValue.value = params[0].value[0];
|
||||
let str = params[0].value[0] + "<br/>";
|
||||
for (let i = 0; i < params.length; i++) {
|
||||
str =
|
||||
str +
|
||||
params[i].marker +
|
||||
params[i].seriesName +
|
||||
":" +
|
||||
params[i].value[1] +
|
||||
"<br/>";
|
||||
}
|
||||
|
||||
return str;
|
||||
},
|
||||
},
|
||||
grid: {
|
||||
top: 30,
|
||||
right: 50,
|
||||
},
|
||||
legend: {
|
||||
show: false,
|
||||
},
|
||||
yAxis: {
|
||||
name: form.type == 1 ? "%" : "A",
|
||||
min: min,
|
||||
max: max,
|
||||
},
|
||||
toolbox: {
|
||||
show: false,
|
||||
},
|
||||
|
||||
series: [
|
||||
{
|
||||
name: item.key + (form.type == 1 ? "次谐波电压" : "次谐波电流"),
|
||||
data: res.data.historyData.map((item: any) => [
|
||||
item.time,
|
||||
Math.floor(item.value * 10000) / 10000,
|
||||
]),
|
||||
type: "line",
|
||||
symbol: "none",
|
||||
|
||||
markLine: {
|
||||
symbol: "none", // 去除箭头
|
||||
label: {
|
||||
show: false, // 隐藏标签
|
||||
},
|
||||
data: [
|
||||
{
|
||||
yAxis: "",
|
||||
},
|
||||
{
|
||||
xAxis: "",
|
||||
},
|
||||
{
|
||||
xAxis: "",
|
||||
},
|
||||
],
|
||||
// 样式配置
|
||||
lineStyle: {
|
||||
color: "red",
|
||||
type: "dashed", // 虚线
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
tabList.value[index].showEcahr = 1; //显示echart
|
||||
} else {
|
||||
ElMessage.warning(res.message);
|
||||
tabList.value[index].showEcahr = 2;
|
||||
}
|
||||
loading.value = false;
|
||||
})
|
||||
.catch(() => {
|
||||
tabList.value[index].showEcahr = 2;
|
||||
loading.value = false;
|
||||
});
|
||||
};
|
||||
const resDataId = ref("");
|
||||
// 生成动态谐波责任数据
|
||||
const generateFn = async () => {
|
||||
if (form.userList.length == 0) {
|
||||
return ElMessage.warning("请选择用采用户");
|
||||
}
|
||||
loading1.value = true;
|
||||
await getDynamicData({
|
||||
userDataId: "123",
|
||||
lineId: dotList.value.id,
|
||||
searchBeginTime: tabList.value[activeName.value].time[0],
|
||||
searchEndTime: tabList.value[activeName.value].time[1],
|
||||
time: tabList.value[activeName.value].key,
|
||||
type: form.type,
|
||||
userList: form.userList,
|
||||
systemType: 1,
|
||||
})
|
||||
.then((res: any) => {
|
||||
if (res.code == "A0000") {
|
||||
resDataId.value = res.data.responsibilityDataIndex;
|
||||
tabList.value[activeName.value].dynamicData = res.data.responsibilities;
|
||||
let [min, max] = yMethod(
|
||||
res.data.datas.map((item: any) => item.valueDatas).flat()
|
||||
);
|
||||
let series: any[] = [];
|
||||
let time: any[] = res.data.timeDatas.map((item: any) =>
|
||||
timeFormat(item)
|
||||
);
|
||||
res.data.datas.forEach((item: any) => {
|
||||
series.push({
|
||||
name: item.customerName,
|
||||
data: item.valueDatas.map((k: any, i: number) => [
|
||||
time[i],
|
||||
Math.floor(k * 10000) / 10000,
|
||||
]),
|
||||
type: "line",
|
||||
symbol: "none",
|
||||
});
|
||||
});
|
||||
|
||||
tabList.value[activeName.value].dynamicOptions = {
|
||||
title: {
|
||||
text: "",
|
||||
},
|
||||
xAxis: {
|
||||
type: "time",
|
||||
name: "时间",
|
||||
axisLabel: {
|
||||
formatter: {
|
||||
day: "{MM}-{dd}",
|
||||
month: "{MM}",
|
||||
year: "{yyyy}",
|
||||
},
|
||||
},
|
||||
// 刻度线样式
|
||||
axisTick: {
|
||||
lineStyle: {
|
||||
color: "#fff", // 刻度线白色
|
||||
},
|
||||
},
|
||||
|
||||
// 轴线样式
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: "#fff", // 轴线白色
|
||||
},
|
||||
},
|
||||
},
|
||||
tooltip: {
|
||||
formatter(params: any) {
|
||||
let str = params[0].value[0] + "<br/>";
|
||||
for (let i = 0; i < params.length; i++) {
|
||||
str =
|
||||
str +
|
||||
params[i].marker +
|
||||
params[i].seriesName +
|
||||
":" +
|
||||
params[i].value[1] +
|
||||
"<br/>";
|
||||
}
|
||||
|
||||
return str;
|
||||
},
|
||||
},
|
||||
grid: {
|
||||
top: 30,
|
||||
},
|
||||
legend: {
|
||||
show: false,
|
||||
},
|
||||
yAxis: {
|
||||
name: form.type == 1 ? "%" : "A",
|
||||
min: min,
|
||||
max: max,
|
||||
},
|
||||
toolbox: {
|
||||
show: false,
|
||||
},
|
||||
|
||||
options: {
|
||||
series: series,
|
||||
},
|
||||
};
|
||||
|
||||
tabList.value[activeName.value].showDynamic = true;
|
||||
} else {
|
||||
ElMessage.warning(res.message);
|
||||
}
|
||||
loading1.value = false;
|
||||
})
|
||||
.catch(() => {
|
||||
loading1.value = false;
|
||||
});
|
||||
loading1.value = false;
|
||||
};
|
||||
|
||||
// 生成指标
|
||||
const generateMetrics = async () => {
|
||||
if (tabList.value[activeName.value].form.limit == "")
|
||||
return ElMessage.warning("请选择限值!");
|
||||
if (tabList.value[activeName.value].form.time1 == "")
|
||||
return ElMessage.warning("请选择时间一!");
|
||||
if (tabList.value[activeName.value].form.time2 == "")
|
||||
return ElMessage.warning("请选择时间二!");
|
||||
loading1.value = true;
|
||||
await getResponsibilityData({
|
||||
limitEndTime: tabList.value[activeName.value].form.time2,
|
||||
limitStartTime: tabList.value[activeName.value].form.time1,
|
||||
limitValue: tabList.value[activeName.value].form.limit,
|
||||
resDataId: resDataId.value,
|
||||
time: tabList.value[activeName.value].key,
|
||||
type: form.type,
|
||||
})
|
||||
.then((res: any) => {
|
||||
tabList.value[activeName.value].dynamicData = res.data.responsibilities;
|
||||
let [min, max] = yMethod(
|
||||
res.data.datas.map((item: any) => item.valueDatas).flat()
|
||||
);
|
||||
let series: any[] = [];
|
||||
let time: any[] = res.data.timeDatas.map((item: any) => timeFormat(item));
|
||||
res.data.datas.forEach((item: any) => {
|
||||
series.push({
|
||||
name: item.customerName,
|
||||
data: item.valueDatas.map((k: any, i: number) => [
|
||||
time[i],
|
||||
Math.floor(k * 10000) / 10000,
|
||||
]),
|
||||
type: "line",
|
||||
smooth: true,
|
||||
symbol: "none",
|
||||
});
|
||||
});
|
||||
|
||||
tabList.value[activeName.value].dynamicOptions = {
|
||||
title: {
|
||||
text: "",
|
||||
},
|
||||
xAxis: {
|
||||
type: "time",
|
||||
name: "时间",
|
||||
axisLabel: {
|
||||
formatter: {
|
||||
day: "{MM}-{dd}",
|
||||
month: "{MM}",
|
||||
year: "{yyyy}",
|
||||
},
|
||||
},
|
||||
},
|
||||
tooltip: {
|
||||
formatter(params: any) {
|
||||
let str = params[0].value[0] + "<br/>";
|
||||
for (let i = 0; i < params.length; i++) {
|
||||
str =
|
||||
str +
|
||||
params[i].marker +
|
||||
params[i].seriesName +
|
||||
":" +
|
||||
params[i].value[1] +
|
||||
"<br/>";
|
||||
}
|
||||
|
||||
return str;
|
||||
},
|
||||
},
|
||||
grid: {
|
||||
top: 30,
|
||||
},
|
||||
legend: {
|
||||
show: false,
|
||||
},
|
||||
yAxis: {
|
||||
name: form.type == 1 ? "%" : "A",
|
||||
min: min,
|
||||
max: max,
|
||||
},
|
||||
toolbox: {
|
||||
show: false,
|
||||
},
|
||||
|
||||
options: {
|
||||
series: series,
|
||||
},
|
||||
};
|
||||
|
||||
tabList.value[activeName.value].showDynamic = true;
|
||||
})
|
||||
.catch(() => {
|
||||
loading1.value = false;
|
||||
});
|
||||
loading1.value = false;
|
||||
};
|
||||
// 监听echart点击
|
||||
const group = (chart: any, myChartDom: any) => {
|
||||
myChartDom.addEventListener("click", function (event: any) {
|
||||
// 获取点击位置相对于图表容器的坐标
|
||||
const rect = myChartDom.getBoundingClientRect();
|
||||
const x = event.clientX - rect.left;
|
||||
const y = event.clientY - rect.top;
|
||||
const pointInPixel = [x, y];
|
||||
// 转换为逻辑坐标(相对于图表坐标系)
|
||||
const pointInGrid = chart.convertFromPixel({ gridIndex: 0 }, pointInPixel);
|
||||
// 计算X轴和Y轴的对应数据
|
||||
|
||||
// 处理X轴数据(分类轴)
|
||||
|
||||
// 处理Y轴数据(数值轴)
|
||||
let yValue = pointInGrid[1].toFixed(4);
|
||||
// xValue = timeFormat(pointInGrid[0].toFixed(0) - 0)
|
||||
if (code.value == 0) {
|
||||
tabList.value[activeName.value].form.limit = yValue;
|
||||
tabList.value[activeName.value].options.series[0].markLine.data[0].yAxis =
|
||||
yValue;
|
||||
chart.setOption(tabList.value[activeName.value].options);
|
||||
} else if (code.value == 1) {
|
||||
tabList.value[activeName.value].form.time1 = xValue.value;
|
||||
tabList.value[activeName.value].options.series[0].markLine.data[1].xAxis =
|
||||
xValue.value;
|
||||
chart.setOption(tabList.value[activeName.value].options);
|
||||
} else if (code.value == 2) {
|
||||
tabList.value[activeName.value].form.time2 = xValue.value;
|
||||
tabList.value[activeName.value].options.series[0].markLine.data[2].xAxis =
|
||||
xValue.value;
|
||||
chart.setOption(tabList.value[activeName.value].options);
|
||||
}
|
||||
// 控制台输出详细信息
|
||||
// console.log('点击事件详情:', {
|
||||
// X轴数据: xValue,
|
||||
// Y轴数据: yValue
|
||||
// })
|
||||
});
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
const dom = document.getElementById("navigation-splitpanes");
|
||||
if (dom) {
|
||||
size.value = Math.round((180 / dom.offsetHeight) * 100);
|
||||
}
|
||||
userDataList({
|
||||
pageNum: 1,
|
||||
pageSize: 10000,
|
||||
searchValue: "",
|
||||
}).then((res: any) => {
|
||||
console.log(res.data);
|
||||
loadDataOptions.value = res.data.records;
|
||||
});
|
||||
|
||||
if (!dotList.value || !dotList.value.alias) {
|
||||
dotList.value = { alias: "请选择监测点位" };
|
||||
}
|
||||
});
|
||||
|
||||
const formData = ref({
|
||||
deptIndex: store.state.deptId,
|
||||
});
|
||||
|
||||
const dataTree = ref([]);
|
||||
const expandedKeys = ref([]);
|
||||
|
||||
const defaultProps = {
|
||||
label: "name",
|
||||
value: "id",
|
||||
disabled: "disabled",
|
||||
};
|
||||
|
||||
const treeRef = ref();
|
||||
|
||||
const loadData = () => {
|
||||
let form = JSON.parse(JSON.stringify(formData.value));
|
||||
getTerminalTreeForFive(form).then((res) => {
|
||||
console.log(res);
|
||||
|
||||
res.data = [
|
||||
{
|
||||
name: "电网拓扑",
|
||||
level: -1,
|
||||
id: 0,
|
||||
children: res.data,
|
||||
},
|
||||
];
|
||||
|
||||
// 查找第一层级的最后一个子节点
|
||||
const firstLevelChildren = res.data[0].children;
|
||||
if (firstLevelChildren && firstLevelChildren.length > 0) {
|
||||
let flag = true;
|
||||
|
||||
// 设置节点别名
|
||||
res.data.forEach((item: any) => {
|
||||
item.children.forEach((item2: any) => {
|
||||
item2.children.forEach((item3: any) => {
|
||||
item3.children.forEach((item4: any) => {
|
||||
item4.children.forEach((item5: any) => {
|
||||
if (item5.level == 7) {
|
||||
item5.children.forEach((item6: any) => {
|
||||
item6.disabled = false;
|
||||
item6.alias = `${item.name}>${item2.name}>${item3.name}>${item4.name}>${item5.name}>${item6.name}`;
|
||||
});
|
||||
} else {
|
||||
if (flag) {
|
||||
expandedKeys.value = [item5.id];
|
||||
treeRef.value.setCurrentKey(item5.id);
|
||||
emit("init", item5);
|
||||
flag = false;
|
||||
}
|
||||
item5.disabled = false;
|
||||
item5.alias = `${item.name}>${item2.name}>${item3.name}>${item4.name}>${item5.name}`;
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
dataTree.value = res.data;
|
||||
|
||||
// 更新禁用状态
|
||||
updateTreeDataDisabledState();
|
||||
});
|
||||
};
|
||||
|
||||
loadData();
|
||||
|
||||
defineExpose({
|
||||
openDialog,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@use "@/assets/scss/index.scss";
|
||||
|
||||
.box {
|
||||
// height: calc((100vh - 190px) / 2);
|
||||
height: 300px;
|
||||
}
|
||||
|
||||
.boxTab {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.harmonicButton {
|
||||
height: 42px;
|
||||
margin-top: 10px;
|
||||
display: flex;
|
||||
justify-content: end;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
:deep(.el-tabs__content) {
|
||||
height: calc(100vh - 265px);
|
||||
}
|
||||
|
||||
:deep(.el-input-group__append, .el-input-group__prepend) {
|
||||
background-color: #ffffff00;
|
||||
padding: 0 17px;
|
||||
}
|
||||
|
||||
:deep(.frontBox) {
|
||||
background-color: var(--el-color-primary) !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
::v-deep(.el-tabs__item) {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
::v-deep(.el-tabs__item.is-active) {
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
::v-deep(.el-tabs__nav-wrap::after) {
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
::v-deep(.el-tabs__active-bar) {
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
::v-deep(.splitpanes__splitter) {
|
||||
background-color: rgba(44, 46, 60, 0.1) !important;
|
||||
|
||||
&:before {
|
||||
background-color: #fff !important;
|
||||
}
|
||||
|
||||
&:after {
|
||||
background-color: #ffffff80 !important;
|
||||
}
|
||||
}
|
||||
|
||||
::v-deep(.el-form-item) {
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
.currentPosition {
|
||||
position: absolute;
|
||||
top: -10px;
|
||||
right: 10px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
// 树选择器下拉面板样式
|
||||
::v-deep(.tree-select-popper) {
|
||||
.el-select-dropdown__wrap {
|
||||
width: 400px; /* 设置你想要的宽度 */
|
||||
}
|
||||
}
|
||||
|
||||
::v-deep(.el-tag__content){
|
||||
max-width: 90px;
|
||||
}
|
||||
|
||||
/* 树选择器本身的宽度控制 */
|
||||
::v-deep(.wide-tree-select) {
|
||||
width: 200px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,230 @@
|
||||
<!-- 详情 -->
|
||||
<template>
|
||||
<el-dialog v-model="visible" :close-on-click-modal="false" title="详情" draggable width="1600px"
|
||||
@close="handleCloseDialog" style="height: 800px">
|
||||
<div class="default-main">
|
||||
<div class="title_1">
|
||||
<span class="monitoring-point">
|
||||
{{ props.detailsQuery.name || "" }}</span>
|
||||
</div>
|
||||
|
||||
<el-tabs v-model="activeName" @tab-change="generateFn">
|
||||
<el-tab-pane v-for="(item, index) in tabList" :key="index" :label="item.name + '次谐波'" style="height: 100%"
|
||||
:name="index" v-loading="loading">
|
||||
<div style="height: 680px; overflow-y: auto">
|
||||
<div class="box mb10" v-for="(value, i) in item.dynamicOptions" :key="i">
|
||||
<div class="boxTab">
|
||||
<MyEChart :options="item.dynamicOptions[i]" style="flex: 1" :style="{
|
||||
height: `calc((680px) / ${item.list.length == 0
|
||||
? 1
|
||||
: item.list.length > 1
|
||||
? 2
|
||||
: item.list.length
|
||||
} - 21px)`,
|
||||
}" />
|
||||
<div style="width: 500px" class="tableBox">
|
||||
<el-table ref="tableRef" :data="item.list[i]" size="small" :height="`calc((600px) / ${item.list.length == 0
|
||||
? 1
|
||||
: item.list.length > 1
|
||||
? 2
|
||||
: item.list.length
|
||||
} - 21px)`" :header-cell-style="{ textAlign: 'center' }" border>
|
||||
<el-table-column prop="customerName" align="center" label="用户名(用户号)" />
|
||||
<el-table-column prop="responsibilityData" align="center" label="责任数据(%)" width="120">
|
||||
<template #default="scope">
|
||||
{{
|
||||
Math.floor(scope.row.responsibilityData * 10000) / 10000
|
||||
}}
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-divider />
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, onMounted, nextTick } from "vue";
|
||||
import MyEChart from "@/components/echarts/MyEchart.vue";
|
||||
import { timeFormat } from "@/utils/common";
|
||||
import { yMethod } from "@/utils/echartMethod";
|
||||
import { displayHistoryData } from "@/api/manage_wx";
|
||||
|
||||
const props = defineProps<{
|
||||
detailsQuery?: any; // 根据实际类型调整
|
||||
}>();
|
||||
|
||||
// const height = mainHeight(155);
|
||||
const activeName = ref(0);
|
||||
const tabList: any = ref([]);
|
||||
|
||||
const visible: any = ref(false);
|
||||
|
||||
const loading: any = ref(false);
|
||||
const init = () => {
|
||||
let data =
|
||||
(Array.isArray(props.detailsQuery.time)
|
||||
? props.detailsQuery.time[0]
|
||||
: props.detailsQuery.time
|
||||
)?.split(",") ?? [];
|
||||
tabList.value = [];
|
||||
data.forEach((item: any) => {
|
||||
tabList.value.push({
|
||||
name: item,
|
||||
dynamicOptions: [],
|
||||
list: [],
|
||||
});
|
||||
});
|
||||
activeName.value = 0;
|
||||
generateFn(0);
|
||||
};
|
||||
|
||||
// 生成动态谐波责任数据
|
||||
const generateFn = async (e: any) => {
|
||||
if (tabList.value[e].dynamicOptions.length != 0) return;
|
||||
|
||||
loading.value = true;
|
||||
await displayHistoryData({
|
||||
id: props.detailsQuery.id,
|
||||
time: tabList.value[e].name,
|
||||
})
|
||||
.then((res: any) => {
|
||||
res.data.forEach((item: any) => {
|
||||
tabList.value[e].list.push(item.responsibilities);
|
||||
|
||||
let [min, max] = yMethod(
|
||||
item.datas.map((k: any) => k.valueDatas).flat()
|
||||
);
|
||||
let series: any[] = [];
|
||||
let time: any[] = item.timeDatas.map((k: any) => timeFormat(k));
|
||||
item.datas.forEach((k: any) => {
|
||||
series.push({
|
||||
name: k.customerName,
|
||||
data: k.valueDatas.map((k: any, i: number) => [
|
||||
time[i],
|
||||
Math.floor(k * 10000) / 10000,
|
||||
]),
|
||||
type: "line",
|
||||
symbol: "none",
|
||||
smooth: true,
|
||||
});
|
||||
});
|
||||
tabList.value[e].dynamicOptions.push({
|
||||
title: {
|
||||
text: `时间:${item.limitSTime} 至${item.limitETime} 限值:${item.limitValue}`,
|
||||
},
|
||||
xAxis: {
|
||||
type: "time",
|
||||
name: "时间",
|
||||
axisLabel: {
|
||||
formatter: {
|
||||
day: "{MM}-{dd}",
|
||||
month: "{MM}",
|
||||
year: "{yyyy}",
|
||||
},
|
||||
},
|
||||
// 刻度线样式
|
||||
axisTick: {
|
||||
lineStyle: {
|
||||
color: "#fff", // 刻度线白色
|
||||
},
|
||||
},
|
||||
|
||||
// 轴线样式
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: "#fff", // 轴线白色
|
||||
},
|
||||
},
|
||||
},
|
||||
tooltip: {
|
||||
formatter(params: any) {
|
||||
let str = params[0].value[0] + "<br/>";
|
||||
for (let i = 0; i < params.length; i++) {
|
||||
str =
|
||||
str +
|
||||
params[i].marker +
|
||||
params[i].seriesName +
|
||||
":" +
|
||||
params[i].value[1] +
|
||||
"<br/>";
|
||||
}
|
||||
|
||||
return str;
|
||||
},
|
||||
},
|
||||
grid: {
|
||||
top: 30,
|
||||
},
|
||||
legend: {
|
||||
show: false,
|
||||
},
|
||||
yAxis: {
|
||||
min: min,
|
||||
max: max,
|
||||
},
|
||||
toolbox: {
|
||||
show: false,
|
||||
},
|
||||
|
||||
options: {
|
||||
series: series,
|
||||
},
|
||||
});
|
||||
});
|
||||
})
|
||||
.catch(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
loading.value = false;
|
||||
};
|
||||
|
||||
const openDialog = () => {
|
||||
visible.value = true;
|
||||
if (props.detailsQuery) {
|
||||
nextTick(() => {
|
||||
init();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handleCloseDialog = () => {
|
||||
visible.value = false;
|
||||
};
|
||||
|
||||
// onMounted(() => {
|
||||
// init();
|
||||
// });
|
||||
|
||||
defineExpose({
|
||||
openDialog,
|
||||
});
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@use "@/assets/scss/index.scss";
|
||||
|
||||
.title_1 {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top: 65px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.monitoring-point {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.boxTab {
|
||||
display: flex;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,653 @@
|
||||
<!-- 谐波放大表格详情 -->
|
||||
<template>
|
||||
<el-dialog v-model="visible" :close-on-click-modal="false" title="趋势图" draggable width="70%"
|
||||
@close="handleCloseDialog">
|
||||
<el-radio-group v-model="condition" size="small" @change="init">
|
||||
<el-radio-button label="谐波电压" value="42" />
|
||||
<el-radio-button label="谐波电流" value="43" />
|
||||
|
||||
</el-radio-group>
|
||||
<MyEChart v-loading="loading" element-loading-background="rgba(122, 122, 122, 0.8)" :options="list[0]?.option"
|
||||
:style="`height:670px`" />
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from "vue";
|
||||
import MyEChart from "@/components/echarts/MyEchart.vue";
|
||||
import { getHistoryResult } from "@/api/manage_wx/index";
|
||||
import { yMethod } from "@/utils/echartMethod";
|
||||
|
||||
const emit = defineEmits(["close"]);
|
||||
const visible = ref(false);
|
||||
const loading = ref(false);
|
||||
const options = ref<any>(null);
|
||||
const list = ref<any>([]);
|
||||
const traceability = ref<any>([]);
|
||||
const rowData: any = ref({});
|
||||
const condition: any = ref('42');
|
||||
|
||||
const open = async (row: any) => {
|
||||
rowData.value = row;
|
||||
condition.value = '42';
|
||||
visible.value = true;
|
||||
init()
|
||||
};
|
||||
const init = async () => {
|
||||
list.value = [];
|
||||
|
||||
loading.value = true;
|
||||
await getHistoryResult({
|
||||
lineId: [rowData.value.monitorId],
|
||||
searchBeginTime: rowData.value.startTime,
|
||||
searchEndTime: rowData.value.endTime,
|
||||
valueType: 4,
|
||||
harmonic: rowData.value.harmonicCount,
|
||||
ptType: 0,
|
||||
condition: [condition.value],
|
||||
}).then((res) => {
|
||||
shujuchuli(res);
|
||||
});
|
||||
}
|
||||
const shujuchuli = (res: any) => {
|
||||
list.value = [];
|
||||
let shujuData = res.data;
|
||||
shujuData.forEach((item: any, i: number) => {
|
||||
//判断是否存在暂降点
|
||||
|
||||
if (item.eventDetail == null) {
|
||||
let [min, max] = yMethod([item.minValue, item.maxValue, condition.value == '42' ? rowData.value.vavgValue : rowData.value.vavgValue.iavgValue]
|
||||
|
||||
);
|
||||
//判断是否有限值(有上下限)
|
||||
if (item.topLimit !== 0 && item.lowerLimit !== 0) {
|
||||
item.phaiscType.push("上限");
|
||||
item.phaiscType.push("下限");
|
||||
if (item.minValue !== null && item.maxValue !== null) {
|
||||
//最小值等于下限值
|
||||
//图列为A,B,C,上限,下限
|
||||
if (item.phaiscType.length == 5) {
|
||||
let avalue = [];
|
||||
let bvalue = [];
|
||||
let cvalue = [];
|
||||
let topLimit = [];
|
||||
let lowerLimit = [];
|
||||
item.maxValue = item.topLimit;
|
||||
item.minValue = item.lowerLimit;
|
||||
//判断数据是否存在
|
||||
if (item.value !== null) {
|
||||
for (let j = 0; j < item.value.length; j++) {
|
||||
//判断存在缺失值a
|
||||
if (item.value[j][1] != undefined) {
|
||||
avalue.push([item.value[j][0], item.value[j][1].toFixed(3)]);
|
||||
} else {
|
||||
avalue.push([]);
|
||||
}
|
||||
//判断存在缺失值b
|
||||
if (item.value[j][2] != undefined) {
|
||||
bvalue.push([item.value[j][0], item.value[j][2].toFixed(3)]);
|
||||
} else {
|
||||
bvalue.push([]);
|
||||
}
|
||||
//判断存在缺失值c
|
||||
if (item.value[j][3] != undefined) {
|
||||
cvalue.push([item.value[j][0], item.value[j][3].toFixed(3)]);
|
||||
} else {
|
||||
cvalue.push([]);
|
||||
}
|
||||
//上下限值
|
||||
topLimit.push([item.value[j][0], item.topLimit.toFixed(3)]);
|
||||
lowerLimit.push([item.value[j][0], item.lowerLimit.toFixed(3)]);
|
||||
}
|
||||
//数据为空
|
||||
} else {
|
||||
avalue = [];
|
||||
bvalue = [];
|
||||
cvalue = [];
|
||||
}
|
||||
let shuju = {
|
||||
id: "qushifenx" + i,
|
||||
title: item.lineName + "--" + item.targetName,
|
||||
targetName: item.targetName,
|
||||
legend: item.phaiscType,
|
||||
valueName: item.unit[0],
|
||||
maxValue: max,
|
||||
minValue: min,
|
||||
avalue: avalue,
|
||||
bvalue: bvalue,
|
||||
cvalue: cvalue,
|
||||
topLimit: topLimit,
|
||||
lowerLimit: lowerLimit,
|
||||
};
|
||||
list.value.push(shuju);
|
||||
}
|
||||
//图列为频率等,上限,下限
|
||||
if (item.phaiscType.length == 3) {
|
||||
let gvalue = [];
|
||||
let topLimit = [];
|
||||
let lowerLimit = [];
|
||||
item.maxValue = item.topLimit;
|
||||
item.minValue = item.lowerLimit;
|
||||
//判断数据是否存在
|
||||
if (item.value !== null) {
|
||||
for (let j = 0; j < item.value.length; j++) {
|
||||
//判断存在缺失值a
|
||||
if (item.value[j][1] != undefined) {
|
||||
gvalue.push([item.value[j][0], item.value[j][1].toFixed(3)]);
|
||||
} else {
|
||||
gvalue.push([]);
|
||||
}
|
||||
//上下限值
|
||||
topLimit.push([item.value[j][0], item.topLimit.toFixed(3)]);
|
||||
lowerLimit.push([item.value[j][0], item.lowerLimit.toFixed(3)]);
|
||||
}
|
||||
//数据为空
|
||||
} else {
|
||||
gvalue = [];
|
||||
}
|
||||
let shuju = {
|
||||
id: "qushifenx" + i,
|
||||
title: item.lineName + "--" + item.targetName,
|
||||
targetName: item.targetName,
|
||||
legend: item.phaiscType,
|
||||
valueName: item.unit[0],
|
||||
maxValue: max,
|
||||
minValue: min,
|
||||
gvalue: gvalue,
|
||||
topLimit: topLimit,
|
||||
lowerLimit: lowerLimit,
|
||||
chufa: [],
|
||||
};
|
||||
list.value.push(shuju);
|
||||
}
|
||||
} else {
|
||||
let shuju = {
|
||||
id: "qushifenx" + i,
|
||||
title: item.lineName + "--" + item.targetName,
|
||||
targetName: item.targetName,
|
||||
legend: item.phaiscType,
|
||||
valueName: item.unit[0],
|
||||
minValue: null,
|
||||
avalue: [],
|
||||
bvalue: [],
|
||||
cvalue: [],
|
||||
topLimit: [],
|
||||
lowerLimit: [],
|
||||
chufa: [],
|
||||
};
|
||||
list.value.push(shuju);
|
||||
}
|
||||
}
|
||||
//有上限值
|
||||
if (item.topLimit !== 0 && item.lowerLimit == 0) {
|
||||
item.phaiscType.push("上限");
|
||||
if (item.minValue !== null) {
|
||||
//最小值等于下限值
|
||||
//图列为A,B,C,上限
|
||||
if (item.phaiscType.length == 4) {
|
||||
let avalue = [];
|
||||
let bvalue = [];
|
||||
let cvalue = [];
|
||||
let topLimit = [];
|
||||
item.maxValue = item.topLimit;
|
||||
// item.minValue=item.lowerLimit
|
||||
//判断数据是否存在
|
||||
if (item.value !== null) {
|
||||
for (let j = 0; j < item.value.length; j++) {
|
||||
//判断存在缺失值a
|
||||
if (item.value[j][1] != undefined) {
|
||||
avalue.push([item.value[j][0], item.value[j][1].toFixed(3)]);
|
||||
} else {
|
||||
avalue.push([]);
|
||||
}
|
||||
//判断存在缺失值b
|
||||
if (item.value[j][2] != undefined) {
|
||||
bvalue.push([item.value[j][0], item.value[j][2].toFixed(3)]);
|
||||
} else {
|
||||
bvalue.push([]);
|
||||
}
|
||||
//判断存在缺失值c
|
||||
if (item.value[j][3] != undefined) {
|
||||
cvalue.push([item.value[j][0], item.value[j][3].toFixed(3)]);
|
||||
} else {
|
||||
cvalue.push([]);
|
||||
}
|
||||
//上限值
|
||||
topLimit.push([item.value[j][0], item.topLimit.toFixed(3)]);
|
||||
}
|
||||
//数据为空
|
||||
} else {
|
||||
avalue = [];
|
||||
bvalue = [];
|
||||
cvalue = [];
|
||||
}
|
||||
let shuju = {
|
||||
id: "qushifenx" + i,
|
||||
title: item.lineName + "--" + item.targetName,
|
||||
targetName: item.targetName,
|
||||
legend: item.phaiscType,
|
||||
valueName: item.unit[0],
|
||||
maxValue: max,
|
||||
minValue: min,
|
||||
avalue: avalue,
|
||||
bvalue: bvalue,
|
||||
cvalue: cvalue,
|
||||
topLimit: topLimit,
|
||||
};
|
||||
list.value.push(shuju);
|
||||
}
|
||||
//图列为频率等,上限
|
||||
if (item.phaiscType.length == 2) {
|
||||
let gvalue = [];
|
||||
let topLimit = [];
|
||||
item.maxValue = item.topLimit;
|
||||
// item.minValue=item.lowerLimit
|
||||
//判断数据是否存在
|
||||
if (item.value !== null) {
|
||||
for (let j = 0; j < item.value.length; j++) {
|
||||
//判断存在缺失值a
|
||||
if (item.value[j][1] != undefined) {
|
||||
gvalue.push([item.value[j][0], item.value[j][1].toFixed(3)]);
|
||||
} else {
|
||||
gvalue.push([]);
|
||||
}
|
||||
//上限值
|
||||
topLimit.push([item.value[j][0], item.topLimit.toFixed(3)]);
|
||||
}
|
||||
//数据为空
|
||||
} else {
|
||||
gvalue = [];
|
||||
}
|
||||
let shuju = {
|
||||
id: "qushifenx" + i,
|
||||
title: item.lineName + "--" + item.targetName,
|
||||
targetName: item.targetName,
|
||||
legend: item.phaiscType,
|
||||
valueName: item.unit[0],
|
||||
minValue: min,
|
||||
maxValue: max,
|
||||
gvalue: gvalue,
|
||||
topLimit: topLimit,
|
||||
};
|
||||
list.value.push(shuju);
|
||||
}
|
||||
} else {
|
||||
let shuju = {
|
||||
id: "qushifenx" + i,
|
||||
title: item.lineName + "--" + item.targetName,
|
||||
targetName: item.targetName,
|
||||
legend: item.phaiscType,
|
||||
valueName: item.unit[0],
|
||||
minValue: null,
|
||||
avalue: [],
|
||||
bvalue: [],
|
||||
cvalue: [],
|
||||
topLimit: [],
|
||||
lowerLimit: [],
|
||||
chufa: [],
|
||||
};
|
||||
list.value.push(shuju);
|
||||
}
|
||||
}
|
||||
|
||||
//无限值
|
||||
if (item.topLimit == 0 && item.lowerLimit == 0) {
|
||||
if (item.minValue !== null) {
|
||||
//最小值等于下限值
|
||||
//图列为A,B,C
|
||||
if (item.phaiscType.length == 3) {
|
||||
let avalue = [];
|
||||
let bvalue = [];
|
||||
let cvalue = [];
|
||||
//判断数据是否存在
|
||||
if (item.value !== null) {
|
||||
for (let j = 0; j < item.value.length; j++) {
|
||||
//判断存在缺失值a
|
||||
if (item.value[j][1] != undefined) {
|
||||
avalue.push([item.value[j][0], item.value[j][1].toFixed(3)]);
|
||||
} else {
|
||||
avalue.push([]);
|
||||
}
|
||||
//判断存在缺失值b
|
||||
if (item.value[j][2] != undefined) {
|
||||
bvalue.push([item.value[j][0], item.value[j][2].toFixed(3)]);
|
||||
} else {
|
||||
bvalue.push([]);
|
||||
}
|
||||
//判断存在缺失值c
|
||||
if (item.value[j][3] != undefined) {
|
||||
cvalue.push([item.value[j][0], item.value[j][3].toFixed(3)]);
|
||||
} else {
|
||||
cvalue.push([]);
|
||||
}
|
||||
}
|
||||
//数据为空
|
||||
} else {
|
||||
avalue = [];
|
||||
bvalue = [];
|
||||
cvalue = [];
|
||||
}
|
||||
|
||||
let shuju = {
|
||||
id: "qushifenx" + i,
|
||||
title: item.lineName + "--" + item.targetName,
|
||||
targetName: item.targetName,
|
||||
legend: item.phaiscType,
|
||||
valueName: item.unit[0],
|
||||
minValue: min,
|
||||
maxValue: max,
|
||||
avalue: avalue,
|
||||
bvalue: bvalue,
|
||||
cvalue: cvalue,
|
||||
};
|
||||
list.value.push(shuju);
|
||||
}
|
||||
//图列为频率等
|
||||
if (item.phaiscType.length == 1) {
|
||||
let gvalue = [];
|
||||
//判断数据是否存在
|
||||
if (item.value !== null) {
|
||||
for (let j = 0; j < item.value.length; j++) {
|
||||
//判断存在缺失值a
|
||||
if (item.value[j][1] != undefined) {
|
||||
gvalue.push([item.value[j][0], item.value[j][1].toFixed(3)]);
|
||||
} else {
|
||||
gvalue.push([]);
|
||||
}
|
||||
}
|
||||
//数据为空
|
||||
} else {
|
||||
gvalue = [];
|
||||
}
|
||||
let shuju = {
|
||||
id: "qushifenx" + i,
|
||||
title: item.lineName + "--" + item.targetName,
|
||||
targetName: item.targetName,
|
||||
legend: item.phaiscType,
|
||||
valueName: item.unit[0],
|
||||
minValue: min,
|
||||
maxValue: max,
|
||||
gvalue: gvalue,
|
||||
};
|
||||
list.value.push(shuju);
|
||||
}
|
||||
} else {
|
||||
let shuju = {
|
||||
id: "qushifenx" + i,
|
||||
title: item.lineName + "--" + item.targetName,
|
||||
targetName: item.targetName,
|
||||
legend: item.phaiscType,
|
||||
valueName: item.unit[0],
|
||||
minValue: null,
|
||||
avalue: [],
|
||||
bvalue: [],
|
||||
cvalue: [],
|
||||
topLimit: [],
|
||||
lowerLimit: [],
|
||||
chufa: [],
|
||||
};
|
||||
list.value.push(shuju);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
rendering();
|
||||
};
|
||||
const rendering = () => {
|
||||
list.value.forEach((item: any) => {
|
||||
let opitonserise: any[] = [];
|
||||
item.legend.forEach((item2: any) => {
|
||||
if (
|
||||
item.avalue !== undefined &&
|
||||
(item2 == "A相" || item2 == "AB相" || item2 == "零序电压")
|
||||
) {
|
||||
let data = {
|
||||
name: item2,
|
||||
symbol: "none",
|
||||
symbolSize: 5,
|
||||
type: "line",
|
||||
smooth: true,
|
||||
itemStyle: {
|
||||
normal: {
|
||||
color: "#DAA520",
|
||||
},
|
||||
},
|
||||
|
||||
data: item.avalue,
|
||||
};
|
||||
opitonserise.push(data);
|
||||
} else if (
|
||||
item.bvalue !== undefined &&
|
||||
(item2 == "B相" || item2 == "BC相" || item2 == "正序电压")
|
||||
) {
|
||||
let data = {
|
||||
name: item2,
|
||||
symbol: "none",
|
||||
symbolSize: 5,
|
||||
type: "line",
|
||||
smooth: true,
|
||||
itemStyle: {
|
||||
normal: {
|
||||
color: "#2E8B57",
|
||||
},
|
||||
},
|
||||
|
||||
data: item.bvalue,
|
||||
};
|
||||
opitonserise.push(data);
|
||||
} else if (
|
||||
item.cvalue !== undefined &&
|
||||
(item2 == "C相" || item2 == "CA相" || item2 == "负序电压")
|
||||
) {
|
||||
let data = {
|
||||
name: item2,
|
||||
symbol: "none",
|
||||
symbolSize: 5,
|
||||
type: "line",
|
||||
smooth: true,
|
||||
barWidth: 22,
|
||||
itemStyle: {
|
||||
normal: {
|
||||
color: "#A52a2a",
|
||||
},
|
||||
},
|
||||
data: item.cvalue,
|
||||
};
|
||||
opitonserise.push(data);
|
||||
}
|
||||
});
|
||||
if (item.valueName == undefined) {
|
||||
item.valueName = "无";
|
||||
}
|
||||
opitonserise[0].markLine = {
|
||||
itemStyle: {
|
||||
normal: {
|
||||
lineStyle: {
|
||||
type: "dashed", //dotted、solid
|
||||
color: "#FF33FF",
|
||||
width: 2,
|
||||
},
|
||||
},
|
||||
},
|
||||
label: {
|
||||
normal: {
|
||||
color: "#fff",
|
||||
formatter: function (params) {
|
||||
return `标准值`;
|
||||
},
|
||||
},
|
||||
},
|
||||
data: [
|
||||
{
|
||||
name: "标准值",
|
||||
yAxis: condition.value == '42' ? rowData.value.vavgValue : rowData.value.iavgValue,
|
||||
},
|
||||
],
|
||||
};
|
||||
// console.log("🚀 ~ rendering ~ row.value:", rowData.value);
|
||||
|
||||
// console.log("🚀 ~ rendering ~ opitonserise:", opitonserise);
|
||||
item.serise = opitonserise;
|
||||
});
|
||||
|
||||
getEcharts();
|
||||
};
|
||||
const getEcharts = () => {
|
||||
list.value.forEach((item: any, i: number) => {
|
||||
console.log("🚀 ~ getEcharts ~ item:", item)
|
||||
item.option = {
|
||||
backgroundColor: "#fff",
|
||||
title: {
|
||||
left: "center",
|
||||
text: item.title,
|
||||
},
|
||||
tooltip: {
|
||||
top: "10px",
|
||||
trigger: "axis",
|
||||
borderColor: "grey",
|
||||
style: {
|
||||
color: "#fff",
|
||||
fontSize: "15px",
|
||||
padding: 10,
|
||||
},
|
||||
formatter: function (params) {
|
||||
// console.log(params)
|
||||
let tips = "";
|
||||
tips += "时刻:" + params[0].data[0] + "</br/>";
|
||||
|
||||
for (let i = 0; i < params.length; i++) {
|
||||
if (params[i].seriesName != "暂降触发点") {
|
||||
tips +=
|
||||
params[i].marker +
|
||||
params[i].seriesName +
|
||||
":" +
|
||||
(params[i].value[1] - 0).toFixed(2) +
|
||||
"<br/>";
|
||||
}
|
||||
}
|
||||
return tips;
|
||||
},
|
||||
// axisPointer: {
|
||||
// type: "cross",
|
||||
// label: {
|
||||
// color: "#fff",
|
||||
// fontSize: 16,
|
||||
// },
|
||||
// },
|
||||
textStyle: {
|
||||
color: "#fff",
|
||||
fontStyle: "normal",
|
||||
opacity: 0.35,
|
||||
fontSize: 14,
|
||||
},
|
||||
backgroundColor: "rgba(0,0,0,0.55)",
|
||||
borderWidth: 0,
|
||||
},
|
||||
|
||||
legend: {
|
||||
right: 50,
|
||||
top: 25,
|
||||
verticalAlign: "top",
|
||||
enabled: true,
|
||||
itemDistance: 5,
|
||||
textStyle: {
|
||||
fontSize: "0.6rem",
|
||||
color: "#fff",
|
||||
rich: {
|
||||
a: {
|
||||
verticalAlign: "middle",
|
||||
},
|
||||
},
|
||||
|
||||
padding: [0, 0, 0, 0], //[上、右、下、左]
|
||||
},
|
||||
},
|
||||
xAxis: [
|
||||
{
|
||||
type: "time",
|
||||
axisLine: {
|
||||
show: true,
|
||||
onZero: false,
|
||||
lineStyle: {
|
||||
color: "#fff",
|
||||
},
|
||||
},
|
||||
axisLabel: {
|
||||
textStyle: {
|
||||
fontFamily: "dinproRegular",
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
toolbox: {
|
||||
show: false,
|
||||
feature: {
|
||||
dataZoom: {
|
||||
// bottom: '10px',
|
||||
yAxisIndex: "none",
|
||||
},
|
||||
},
|
||||
},
|
||||
yAxis: [
|
||||
{
|
||||
type: "value",
|
||||
min: item.minValue,
|
||||
max: item.maxValue,
|
||||
name: item.valueName,
|
||||
|
||||
|
||||
axisLine: {
|
||||
show: true,
|
||||
onZero: false, //-----------重点
|
||||
lineStyle: {
|
||||
color: "#fff",
|
||||
},
|
||||
},
|
||||
splitLine: {
|
||||
lineStyle: {
|
||||
// 使用深浅的间隔色
|
||||
type: "dashed",
|
||||
opacity: 0.5,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
series: item.serise,
|
||||
};
|
||||
let aValues = [];
|
||||
let bValues = [];
|
||||
let CValues = [];
|
||||
let ZValues = [];
|
||||
|
||||
if (
|
||||
traceability.value.length > 0 &&
|
||||
traceability.value[0].value != null &&
|
||||
traceability.value[0].value.length > 0
|
||||
) {
|
||||
for (let i = 0; i < traceability.value[0].value.length; i++) {
|
||||
let T = traceability.value[0].value[i][0];
|
||||
let A = traceability.value[0].value[i][1];
|
||||
let B = traceability.value[0].value[i][2];
|
||||
let C = traceability.value[0].value[i][3];
|
||||
let Z = A + B + C;
|
||||
aValues.push([T, A > 0 ? 1 : A == 0 ? 0 : -1]);
|
||||
bValues.push([T, B > 0 ? 1 : B == 0 ? 0 : -1]);
|
||||
CValues.push([T, C > 0 ? 1 : C == 0 ? 0 : -1]);
|
||||
ZValues.push([T, Z > 0 ? 1 : Z == 0 ? 0 : -1]);
|
||||
}
|
||||
}
|
||||
});
|
||||
loading.value = false;
|
||||
};
|
||||
|
||||
const handleCloseDialog = () => {
|
||||
visible.value = false;
|
||||
options.value = null;
|
||||
emit("close");
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
open,
|
||||
});
|
||||
</script>
|
||||
583
src/views/SagTraceResult_WX/components/manage/iframeDia.vue
Normal file
@@ -0,0 +1,583 @@
|
||||
<template>
|
||||
<div class="container">
|
||||
<!-- 使用 v-for 遍历四个角落 -->
|
||||
<div
|
||||
v-for="corner in corners"
|
||||
:key="corner.id"
|
||||
v-show="corner.show"
|
||||
:class="['corner', corner.className]"
|
||||
>
|
||||
<div class="content">
|
||||
<div class="title">{{ corner.title }}</div>
|
||||
<el-descriptions :column="1" size="small" label-width="70px" border>
|
||||
<el-descriptions-item
|
||||
v-for="(item, index) in corner.data"
|
||||
:key="index"
|
||||
:label="item.label"
|
||||
>
|
||||
<!-- {{ item.value }} -->
|
||||
<div v-html="item.value" v-if="item.label !== '暂降次数'"></div>
|
||||
<!-- 跑马灯 -->
|
||||
<div v-else style="display: flex">
|
||||
<div style="width: 30px">{{ corner.raceLists.length }}次</div>
|
||||
<div class="simple-marquee">
|
||||
<div class="marquee-content">
|
||||
<span
|
||||
style="margin-right: 15px"
|
||||
v-for="(event, index) in corner.raceLists"
|
||||
:key="index"
|
||||
@click="goToRace(event)"
|
||||
>{{
|
||||
index +
|
||||
1 +
|
||||
"、" +
|
||||
event.startTime +
|
||||
"发生" +
|
||||
event.eventType +
|
||||
"," +
|
||||
"残余电压:" +
|
||||
(event.featureAmplitude * 100).toFixed(2) +
|
||||
"%" +
|
||||
"," +
|
||||
"持续时间:" +
|
||||
event.duration +
|
||||
"S"
|
||||
}}</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</div>
|
||||
<span class="close-btn" @click="closeCorner(corner.id)">
|
||||
<Close />
|
||||
</span>
|
||||
</div>
|
||||
<DipDetail ref="dipDetail"></DipDetail>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, onBeforeUnmount, nextTick, reactive } from "vue";
|
||||
import { clickImage, realTimeData } from "@/api/manage_wx";
|
||||
import { Close } from "@element-plus/icons-vue";
|
||||
import socketClient from "@/utils/webSocketClient";
|
||||
import DipDetail from "../eventStatistics/dipDetail.vue";
|
||||
|
||||
// 定义接收的 props
|
||||
const props = defineProps<{
|
||||
eventList?: [];
|
||||
}>();
|
||||
//开始创建webSocket客户端
|
||||
const dataSocket = reactive({
|
||||
socketServe: socketClient.Instance,
|
||||
});
|
||||
// 定义四个角落的数据
|
||||
const corners = ref([
|
||||
{
|
||||
id: "topLeft",
|
||||
title: "左上",
|
||||
className: "top-left",
|
||||
show: false,
|
||||
data: [] as { label: string; value: string }[],
|
||||
elementId: "", // 记录该角落对应的元素ID
|
||||
raceLists: [] as any[], // 为每个角落添加独立的跑马灯数据存储
|
||||
},
|
||||
{
|
||||
id: "topRight",
|
||||
title: "右上",
|
||||
className: "top-right",
|
||||
show: false,
|
||||
data: [] as { label: string; value: string }[],
|
||||
elementId: "",
|
||||
raceLists: [] as any[], // 为每个角落添加独立的跑马灯数据存储
|
||||
},
|
||||
// {
|
||||
// id: "bottomLeft",
|
||||
// title: "左下",
|
||||
// className: "bottom-left",
|
||||
// show: false,
|
||||
// data: [] as { label: string; value: string }[],
|
||||
// elementId: "",
|
||||
// },
|
||||
// {
|
||||
// id: "bottomRight",
|
||||
// title: "右下",
|
||||
// className: "bottom-right",
|
||||
// show: false,
|
||||
// data: [] as { label: string; value: string }[],
|
||||
// elementId: "",
|
||||
// },
|
||||
]);
|
||||
const steadyStateList = ref([]);
|
||||
|
||||
const selectedId = ref("");
|
||||
|
||||
// 内部响应式数据
|
||||
const eventList = ref([]);
|
||||
|
||||
// 点击跑马灯展示弹框
|
||||
const dipDetail = ref(null);
|
||||
|
||||
const handleClickImage = async (elementId: string) => {
|
||||
// 检查 elementId 是否有值,没有值则直接返回空数组
|
||||
if (!elementId) {
|
||||
eventList.value = [];
|
||||
return;
|
||||
}
|
||||
try {
|
||||
// 发送点击图片请求
|
||||
const res = await clickImage({ lineId: elementId });
|
||||
|
||||
// 确保返回的数据是数组格式,并且过滤掉 null/undefined 元素
|
||||
let dataToStore: any[] = [];
|
||||
|
||||
if (Array.isArray(res.data)) {
|
||||
dataToStore = res.data.filter(
|
||||
(item) => item !== null && item !== undefined
|
||||
);
|
||||
} else if (res.data && Array.isArray(res.data.records)) {
|
||||
dataToStore = res.data.records.filter(
|
||||
(item) => item !== null && item !== undefined
|
||||
);
|
||||
} else if (res.data) {
|
||||
// 如果是单个对象且不为 null
|
||||
if (res.data !== null && res.data !== undefined) {
|
||||
dataToStore = [res.data];
|
||||
}
|
||||
}
|
||||
|
||||
eventList.value = dataToStore;
|
||||
} catch (error) {
|
||||
console.error("调用 clickImage 接口出错:", error);
|
||||
// 出错时设置为空数组,避免后续处理出错
|
||||
eventList.value = [];
|
||||
}
|
||||
};
|
||||
|
||||
// 监听 props 变化
|
||||
// watch(
|
||||
// () => props.eventList,
|
||||
// (newVal) => {
|
||||
// if (newVal && Array.isArray(newVal)) {
|
||||
// eventList.value = [...newVal];
|
||||
// dataLoaded.value = true;
|
||||
// } else {
|
||||
// dataLoaded.value = false;
|
||||
// }
|
||||
// },
|
||||
// { immediate: true, deep: true }
|
||||
// );
|
||||
|
||||
// 记录显示顺序,用于循环替换
|
||||
const displayOrder = ref<number[]>([]);
|
||||
|
||||
// 计算跑马灯动画时长(毫秒)
|
||||
const calculateMarqueeDuration = (corner) => {
|
||||
if (!corner.raceLists || corner.raceLists.length === 0) {
|
||||
return 25000; // 默认25秒
|
||||
}
|
||||
|
||||
// 计算所有事件文本的总长度
|
||||
let totalTextLength = 0;
|
||||
corner.raceLists.forEach((event, index) => {
|
||||
const text = `${index + 1}、${event.startTime}发生${
|
||||
event.eventType
|
||||
},残余电压:${(event.featureAmplitude * 100).toFixed(2)}%,持续时间:${
|
||||
event.duration
|
||||
}S`;
|
||||
totalTextLength += text.length;
|
||||
});
|
||||
|
||||
// 添加分隔符长度和边距
|
||||
const separatorLength =
|
||||
corner.raceLists.length > 1 ? (corner.raceLists.length - 1) * 15 : 0; // margin-right: 15px
|
||||
totalTextLength += separatorLength;
|
||||
|
||||
// 根据文本长度计算时长(减慢速度)
|
||||
// 将每字符需要的时间从50ms增加到80ms,最少8秒,最多90秒
|
||||
const duration = Math.min(Math.max(totalTextLength * 80, 8000), 90000);
|
||||
return duration;
|
||||
};
|
||||
|
||||
// 更新指定角落数据的函数
|
||||
const updateCornerData = (
|
||||
cornerIndex: number,
|
||||
dataItem: any,
|
||||
elementId: string
|
||||
) => {
|
||||
// 更新标题为 objName
|
||||
if (dataItem.objName) {
|
||||
corners.value[cornerIndex].title = dataItem.objName;
|
||||
} else {
|
||||
corners.value[cornerIndex].title = dataItem.stationName;
|
||||
}
|
||||
|
||||
// 格式化数据
|
||||
corners.value[cornerIndex].data = [
|
||||
{ label: "监测点", value: dataItem.lineName },
|
||||
// {
|
||||
// label: "暂降次数",
|
||||
// value: dataItem.eventIds.length,
|
||||
// },
|
||||
{
|
||||
label: "暂降次数",
|
||||
value: ``,
|
||||
},
|
||||
// { label: "稳态指标", value: "Ua:65.5 Ub:65.02 Uc:65.27 Uac:112.85 Uab:112.67 Ubc:112.85" },
|
||||
{
|
||||
label: "稳态指标",
|
||||
value: ``,
|
||||
},
|
||||
];
|
||||
|
||||
// 记录该角落对应的元素ID
|
||||
corners.value[cornerIndex].elementId = elementId;
|
||||
corners.value[cornerIndex].show = true;
|
||||
|
||||
// 设置动画时长
|
||||
nextTick(() => {
|
||||
const duration = calculateMarqueeDuration(corners.value[cornerIndex]);
|
||||
const marqueeElement = document.querySelector(
|
||||
`.${corners.value[cornerIndex].className} .marquee-content`
|
||||
);
|
||||
if (marqueeElement) {
|
||||
(marqueeElement as HTMLElement).style.setProperty(
|
||||
"--marquee-duration",
|
||||
`${duration}ms`
|
||||
);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// 显示下一个角落的函数
|
||||
const showNextCorner = (elementId: string) => {
|
||||
// 检查该元素ID是否已经显示过
|
||||
const existingCornerIndex = corners.value.findIndex(
|
||||
(corner) => corner.elementId === elementId && corner.show
|
||||
);
|
||||
|
||||
if (existingCornerIndex !== -1) {
|
||||
// 如果该元素已经显示过,不更新数据
|
||||
return;
|
||||
}
|
||||
|
||||
// 确保 eventList.value 是数组并且过滤掉 null/undefined 元素
|
||||
if (!Array.isArray(eventList.value)) {
|
||||
console.warn("eventList.value 不是数组格式:", eventList.value);
|
||||
return;
|
||||
}
|
||||
|
||||
// 过滤掉 null 和 undefined 元素,然后查找匹配项
|
||||
const validItems = eventList.value.filter(
|
||||
(item) => item !== null && item !== undefined
|
||||
);
|
||||
const dataItem = validItems.find((item) => item.lineId === elementId);
|
||||
|
||||
// 如果没有找到匹配的数据项,则不更新数据
|
||||
if (!dataItem) {
|
||||
console.warn("未找到匹配的数据项:", elementId);
|
||||
return;
|
||||
}
|
||||
|
||||
// 查找一个未显示的角落
|
||||
const availableCornerIndex = corners.value.findIndex(
|
||||
(corner) => !corner.show
|
||||
);
|
||||
|
||||
if (availableCornerIndex !== -1) {
|
||||
// 有空闲角落,显示在该角落
|
||||
updateCornerData(availableCornerIndex, dataItem, elementId);
|
||||
// 将事件数据存储到该角落
|
||||
corners.value[availableCornerIndex].raceLists = dataItem.eventList || [];
|
||||
// 记录显示顺序
|
||||
displayOrder.value.push(availableCornerIndex);
|
||||
} else {
|
||||
// 没有空闲角落,按顺序替换角落
|
||||
// 获取需要替换的角落索引(循环替换)
|
||||
const replaceIndex = displayOrder.value.shift() || 0;
|
||||
updateCornerData(replaceIndex, dataItem, elementId);
|
||||
// 将事件数据存储到该角落
|
||||
corners.value[replaceIndex].raceLists = dataItem.eventList || [];
|
||||
// 将替换的索引重新加入队列末尾
|
||||
displayOrder.value.push(replaceIndex);
|
||||
}
|
||||
};
|
||||
|
||||
// 关闭指定角落的函数
|
||||
const closeCorner = (id: string) => {
|
||||
const cornerIndex = corners.value.findIndex((c) => c.id === id);
|
||||
if (cornerIndex !== -1) {
|
||||
corners.value[cornerIndex].show = false;
|
||||
corners.value[cornerIndex].elementId = ""; // 清空元素ID记录
|
||||
|
||||
// 从显示顺序中移除该角落索引
|
||||
const orderIndex = displayOrder.value.indexOf(cornerIndex);
|
||||
if (orderIndex !== -1) {
|
||||
displayOrder.value.splice(orderIndex, 1);
|
||||
}
|
||||
}
|
||||
send();
|
||||
};
|
||||
|
||||
// 关闭所有角落的函数
|
||||
const closeAllCorners = () => {
|
||||
corners.value.forEach((corner) => {
|
||||
corner.show = false;
|
||||
corner.elementId = "";
|
||||
});
|
||||
displayOrder.value = [];
|
||||
};
|
||||
|
||||
// 组件挂载后初始化监听器
|
||||
onMounted(() => {
|
||||
init();
|
||||
// 初始化时不显示任何内容
|
||||
});
|
||||
|
||||
// 连接webSocket客户端
|
||||
const init = () => {
|
||||
if (!dataSocket.socketServe) {
|
||||
console.error("WebSocket 客户端实例不存在");
|
||||
return;
|
||||
}
|
||||
dataSocket.socketServe.connect(new Date().getTime());
|
||||
dataSocket.socketServe.registerCallBack("message", (res: any) => {
|
||||
if (res.type == 1) {
|
||||
//稳态指标数据
|
||||
let steadyState = JSON.parse(res.message);
|
||||
|
||||
// console.log(steadyState, "8990hhhhh");
|
||||
if (steadyState == null || steadyState.length == 0) return;
|
||||
steadyStateList.value = steadyState;
|
||||
corners.value.forEach((corner, index) => {
|
||||
let str = ``;
|
||||
steadyState
|
||||
.filter((item) => item.lineId == corner.elementId)
|
||||
.forEach((item) => {
|
||||
if (item.value == 3.1415926) {
|
||||
str += `<div>${item.statisticalName}:/</div>`;
|
||||
} else {
|
||||
str += `<div>${item.statisticalName}:${item.value}${item.unit}</div>`;
|
||||
}
|
||||
});
|
||||
|
||||
corner.data.length > 0
|
||||
? (corner.data[2].value = `<div style="max-height: 100px;overflow-y: auto;">${str} </div>`)
|
||||
: "";
|
||||
|
||||
// 更新跑马灯动画时长
|
||||
nextTick(() => {
|
||||
const duration = calculateMarqueeDuration(corner);
|
||||
const marqueeElement = document.querySelector(
|
||||
`.${corner.className} .marquee-content`
|
||||
);
|
||||
if (marqueeElement) {
|
||||
(marqueeElement as HTMLElement).style.setProperty(
|
||||
"--marquee-duration",
|
||||
`${duration}ms`
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const time = ref(null);
|
||||
// 推送消息
|
||||
const send = () => {
|
||||
dataSocket.socketServe.send({
|
||||
pageId: selectedId.value,
|
||||
lineIdList: corners.value
|
||||
.filter((item) => item.show == true)
|
||||
.map((corner) => corner.elementId),
|
||||
});
|
||||
if (time.value) {
|
||||
clearTimeout(time.value);
|
||||
}
|
||||
if (
|
||||
corners.value
|
||||
.filter((item) => item.show == true)
|
||||
.map((corner) => corner.elementId).length == 0
|
||||
)
|
||||
return;
|
||||
time.value = setInterval(() => {
|
||||
dataSocket.socketServe.send({
|
||||
pageId: selectedId.value,
|
||||
lineIdList: corners.value
|
||||
.filter((item) => item.show == true)
|
||||
.map((corner) => corner.elementId),
|
||||
});
|
||||
}, 1000 * 60);
|
||||
};
|
||||
|
||||
const goToRace = (row) => {
|
||||
console.log(row.measurementPointId, "1222");
|
||||
dipDetail.value.open(row.measurementPointId);
|
||||
};
|
||||
|
||||
// 监听来自 iframe 的消息
|
||||
window.addEventListener("message", async function (event) {
|
||||
// 安全起见,可以验证消息来源(origin)
|
||||
// if (event.origin !== 'https://trusted-origin.com') return;
|
||||
|
||||
// 处理从 iframe 发送过来的消息
|
||||
if (event.data.action === "coreClick") {
|
||||
const clickedElementId = event.data.coreId;
|
||||
selectedId.value = event.data.selectedId;
|
||||
|
||||
// 调用接口获取最新数据
|
||||
await handleClickImage(clickedElementId);
|
||||
|
||||
// 根据接收到的元素LineId显示对应数据
|
||||
await showNextCorner(clickedElementId);
|
||||
|
||||
await send();
|
||||
}
|
||||
});
|
||||
|
||||
// 页面卸载时清除定时器
|
||||
onBeforeUnmount(() => {
|
||||
clearTimeout(time.value);
|
||||
dataSocket.socketServe?.closeWs();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
/* 走马灯样式 - 支持逐条显示 */
|
||||
.simple-marquee {
|
||||
width: 230px;
|
||||
height: 24px;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.marquee-content {
|
||||
display: inline-block;
|
||||
padding-left: 100%;
|
||||
/* animation: marquee-single 15s linear infinite; */
|
||||
animation: marquee-single var(--marquee-duration, 25s) linear infinite;
|
||||
line-height: 24px;
|
||||
text-decoration: underline;
|
||||
text-decoration-color: #4877f6;
|
||||
}
|
||||
|
||||
@keyframes marquee-single {
|
||||
0% {
|
||||
transform: translateX(0);
|
||||
}
|
||||
100% {
|
||||
transform: translateX(-100%);
|
||||
}
|
||||
}
|
||||
|
||||
/* 鼠标悬停时暂停动画 */
|
||||
.simple-marquee:hover .marquee-content {
|
||||
animation-play-state: paused;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.container {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.corner {
|
||||
width: 340px;
|
||||
/* height: 135px; */
|
||||
background-color: #2b2d3a90;
|
||||
position: absolute;
|
||||
color: white;
|
||||
/* font-weight: bold; */
|
||||
/* 添加弹出动画 */
|
||||
opacity: 0;
|
||||
transform: scale(0.3);
|
||||
transition: all 0.4s ease-out;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
/* 显示状态的样式 */
|
||||
.corner:not([style*="display: none"]):not([style*="display:none"]) {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
|
||||
.top-left {
|
||||
top: 10px;
|
||||
left: 10px;
|
||||
}
|
||||
|
||||
.top-right {
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
}
|
||||
|
||||
.bottom-left {
|
||||
top: 170px;
|
||||
left: 10px;
|
||||
}
|
||||
|
||||
.bottom-right {
|
||||
top: 170px;
|
||||
right: 10px;
|
||||
}
|
||||
|
||||
.content {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 16px;
|
||||
/* text-align: center; */
|
||||
/* margin-bottom: 8px; */
|
||||
padding: 8px;
|
||||
/* color: #409eff; */
|
||||
border-bottom: 1px solid #444;
|
||||
background-color: #21232b;
|
||||
border-radius: 8px 8px 0 0;
|
||||
}
|
||||
|
||||
.data-item {
|
||||
display: flex;
|
||||
margin-bottom: 4px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.label {
|
||||
/* font-weight: bold; */
|
||||
width: 55px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.value {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* 关闭按钮样式 */
|
||||
.close-btn {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
width: 14px;
|
||||
color: white;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.indicator {
|
||||
display: flex;
|
||||
}
|
||||
</style>
|
||||