68 Commits

Author SHA1 Message Date
guanj
1abd7dbc45 Merge branch 'master' of http://192.168.1.22:3000/Web/admin-sjzx 2026-07-21 15:10:58 +08:00
guanj
472babe0ce 修改全局悬浮框添加最大高度 2026-07-21 15:10:48 +08:00
8054b44e88 适配辽宁现场调整 2026-07-21 10:45:50 +08:00
guanj
5eb5a01b50 调整电网一张图污染水平评估 2026-07-20 10:04:02 +08:00
guanj
e77c57f8d0 修改辽宁测试问题 2026-07-20 09:30:48 +08:00
guanj
22011188a1 修改辽宁全景展示 2026-07-17 16:13:49 +08:00
guanj
c72ba3e5db 添加一类监测点字段 2026-07-17 11:27:40 +08:00
guanj
7f1c3e31c0 监测点评估添加字段 2026-07-17 11:17:38 +08:00
guanj
6c70a776a0 列设置添加缓存
调整监测点台账导出id重复问题
2026-07-17 08:50:25 +08:00
guanj
dc3b3cfc74 全局下拉框添加filterable属性 2026-07-15 09:21:35 +08:00
guanj
04c5eb7185 调整退出问题 2026-07-14 14:49:10 +08:00
guanj
d0d62ab0af 修改菜单树无法展开问题 2026-07-14 14:39:59 +08:00
guanj
b3d671a93e 调整前端两套互不连通的 HTTP 请求层并存、两套布局框架并存、两套 Pinia 实例并存问题 2026-07-10 14:52:29 +08:00
guanj
888b8d1bf4 修改点击列问题 2026-07-10 08:57:37 +08:00
guanj
ee9cd057b8 优化菜单切换导致浏览器刷新问题 2026-07-09 15:40:56 +08:00
guanj
8bca4191f2 调整代码 2026-07-09 15:26:01 +08:00
guanj
69e6a1f93f 调整 2026-07-09 14:41:56 +08:00
guanj
71df1ac4be 修改冲突 2026-07-09 13:59:02 +08:00
guanj
cd2be0f4aa Merge branch 'master' of http://192.168.1.22:3000/Web/admin-sjzx
# Conflicts:
#	src/views/pqs/business/terminal/terminaCkeck/index.vue
2026-07-09 13:57:58 +08:00
guanj
855c569535 全局表格添加列配置功能 2026-07-09 13:54:18 +08:00
6a84d28101 添加辽宁周期检测功能 2026-07-09 11:41:01 +08:00
guanj
1a69be62a0 修改监测点信息 2026-07-09 11:08:56 +08:00
guanj
02caca969e 修改用户性质 2026-07-09 10:33:22 +08:00
guanj
3f1af5e4cc 监测点信息添加终端型号 2026-07-08 16:08:02 +08:00
guanj
88e9812d5f 联调周期监测 2026-07-08 14:37:19 +08:00
guanj
83a827ba5b 修改文件上传问题 2026-07-08 11:14:32 +08:00
40b1092718 添加周期 2026-07-08 09:21:59 +08:00
guanj
83a1128884 用户管理添加监测报告,评估报告 2026-07-07 13:52:53 +08:00
1a44a2f9e0 添加周期 2026-07-07 10:18:22 +08:00
guanj
5044c88946 优化全景展示页面 2026-07-07 10:15:33 +08:00
guanj
51a862ed00 修改进程显示 2026-07-06 10:58:02 +08:00
guanj
1ca8e536dd 修改限值显示问题 2026-07-06 10:41:33 +08:00
guanj
2445cfbff3 修改辽宁全景展示问题 2026-07-06 10:12:51 +08:00
guanj
21ac0611cd 修改地图api 2026-06-29 14:41:12 +08:00
guanj
ff841cfa93 修改Socket路径问题 2026-06-26 10:17:43 +08:00
guanj
01cc29c8b2 修改电网一张图 2026-06-24 09:57:48 +08:00
guanj
a324f7afcf 修改算法库拖动问题 2026-06-23 21:36:43 +08:00
guanj
4272f7756e 修改配置 2026-06-23 21:28:35 +08:00
guanj
070e404bbb 修改辽宁反馈问题 2026-06-23 21:27:15 +08:00
ed60e2b7f3 台账新增修改添加objName 2026-06-23 16:09:41 +08:00
7785d4f708 台账新增修改添加objName 2026-06-23 15:12:58 +08:00
0c64711dd8 辽宁页面调整 2026-06-22 15:50:31 +08:00
guanj
d85dba8bf8 修改补召 2026-06-16 09:56:38 +08:00
guanj
56b8158f4a 优化波形 2026-06-16 08:36:22 +08:00
cdf
765507398e 辽宁单点登录 2026-06-15 17:57:14 +08:00
cdf
9224ea17b8 区域报告调整 2026-06-07 15:56:31 +08:00
cdf
dc08f7d1ac 辽宁统一认证登录 2026-06-03 17:41:37 +08:00
cdf
1652b5faf8 辽宁统一认证登录 2026-06-03 15:54:15 +08:00
guanj
fae2a7d735 修改辽宁嵌入 2026-06-02 16:33:08 +08:00
guanj
bf3a99cbf3 提交代码 2026-06-02 16:30:29 +08:00
guanj
2e7c0b7f44 修改辽宁免登录 2026-06-02 09:30:10 +08:00
guanj
98e0f9de02 修改关键字 2026-05-28 19:28:12 +08:00
guanj
21756e05d4 修改地图 2026-05-27 10:47:31 +08:00
guanj
abfdf29a38 修改页面样式 调整查询条件 2026-05-22 09:58:07 +08:00
guanj
120892808b 提交代码 2026-04-25 15:21:53 +08:00
guanj
0f5cb1d3c5 提交代码 2026-04-09 11:44:06 +08:00
cdf
dbaa42ff7e 通用台账查询页面调整 2026-04-09 11:42:54 +08:00
guanj
48472bdb85 微调 2026-04-03 14:48:10 +08:00
guanj
fafc5f82c4 修改itic点击波形图 2026-03-26 14:53:34 +08:00
cdf
93ee7e4034 通用台账查询页面调整 2026-03-23 09:19:43 +08:00
guanj
7d2ce51510 修改问题 2026-03-17 08:43:51 +08:00
guanj
9202da17f1 微调 2026-03-05 15:42:15 +08:00
guanj
30eddd0572 修改用户台账管理接口 2026-03-05 11:08:36 +08:00
sjl
15bd1ac6d2 事件报告导出 2026-03-02 15:35:29 +08:00
sjl
f5c76c1b7a 报告模版配置 2026-02-27 14:46:19 +08:00
sjl
b25515b5db 系统相关配置 2026-02-27 08:49:57 +08:00
guanj
bfa061fb03 冀北数据总览添加导出功能 2026-02-06 14:45:15 +08:00
sjl
15e3d4aec8 微调 2026-02-06 10:10:24 +08:00
428 changed files with 52915 additions and 58776 deletions

6
.env.LN Normal file
View File

@@ -0,0 +1,6 @@
# 辽宁嵌入
NODE_ENV = LN
VITE_NAME="LN"
# 电网一张图 地图图层
VITE_NARIMAP=null
VITE_NRGISCOMMON=null

6
.env.LNqr Normal file
View File

@@ -0,0 +1,6 @@
# 辽宁嵌入
NODE_ENV = LNqr
VITE_NAME="LNqr"
# 电网一张图 地图图层
VITE_NARIMAP=null
VITE_NRGISCOMMON=null

View File

@@ -18,7 +18,7 @@ pnpm i
#运行项目 #运行项目
npm run dev npm run dev
#冀北现场启动命令 区分是否加载现场电网一张图内网api #jb现场启动命令 区分是否加载现场电网一张图内网api
npm run dev:jibei npm run dev:jibei
#去除后台所有模块名 如除/xxxx-boot #去除后台所有模块名 如除/xxxx-boot
@@ -26,9 +26,9 @@ npm run dev:removeMode
#因海南和冀北技术监督不同 通过修改文件的方式来区分 #因海南和jb技术监督不同 通过修改文件的方式来区分
#海南打包 需要吧pqs目录下supervise_hn 文件夹改成supervise 原文件改成supervise_jb #海南打包 需要吧pqs目录下supervise_hn 文件夹改成supervise 原文件改成supervise_jb
#冀北打包 需要吧pqs目录下supervise_jb 文件夹改成supervise 原文件改成supervise_hn #jb打包 需要吧pqs目录下supervise_jb 文件夹改成supervise 原文件改成supervise_hn
``` ```
#### 页面编写-示例 #### 页面编写-示例

View File

@@ -15,7 +15,7 @@
<script src="/static/luckysheet/plugin.js"></script> <script src="/static/luckysheet/plugin.js"></script>
<script src="/static/luckysheet/luckysheet.umd.js"></script> <script src="/static/luckysheet/luckysheet.umd.js"></script>
<!-- 冀北地图 --> <!-- jb地图 -->
<!--引入样式文件--> <!--引入样式文件-->
<script src="%VITE_NARIMAP%"></script> <script src="%VITE_NARIMAP%"></script>
<!-- <script src="%VITE_NRGISCOMMON%"></script> --> <!-- <script src="%VITE_NRGISCOMMON%"></script> -->

8813
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,103 +1,108 @@
{ {
"name": "canneng-admin", "name": "canneng-admin",
"private": true, "private": true,
"version": "0.0.0", "version": "0.0.0",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite --mode dev", "dev": "vite --mode dev",
"dev:jibei": "vite --mode jibei", "dev:jibei": "vite --mode jibei",
"dev:hainan": "vite --mode hainan", "dev:hainan": "vite --mode hainan",
"dev:qujing": "vite --mode qujing", "dev:qujing": "vite --mode qujing",
"dev:removeMode": "vite --mode removeMode", "dev:LN": "vite --mode LN",
"build": "vite build --mode dev", "dev:LNqr": "vite --mode LNqr",
"build:jibei": "vite build --mode jibei", "dev:removeMode": "vite --mode removeMode",
"build:hainan": "vite build --mode hainan", "build": "vite build --mode dev",
"build:qujing": "vite build --mode qujing", "build:jibei": "vite build --mode jibei",
"build:removeMode": "vite build --mode removeMode", "build:hainan": "vite build --mode hainan",
"preview": "vite preview" "build:qujing": "vite build --mode qujing",
}, "build:LN": "vite build --mode LN",
"dependencies": { "build:LNqr": "vite build --mode LNqr",
"@ant-design/colors": "^7.0.2", "build:removeMode": "vite build --mode removeMode",
"@element-plus/icons-vue": "^2.3.1", "preview": "vite preview"
"@form-create/designer": "^3.1.3", },
"@form-create/element-ui": "^3.1.24", "dependencies": {
"@fortawesome/fontawesome-free": "^6.5.1", "@ant-design/colors": "^7.0.2",
"@kjgl77/datav-vue3": "^1.7.4", "@element-plus/icons-vue": "^2.3.1",
"@vue-office/docx": "^1.6.3", "@form-create/designer": "^3.1.3",
"@vue-office/excel": "^1.7.8", "@form-create/element-ui": "^3.1.24",
"@vue-office/pdf": "^2.0.2", "@fortawesome/fontawesome-free": "^6.5.1",
"@vueuse/core": "^10.7.0", "@kjgl77/datav-vue3": "^1.7.4",
"@wangeditor/editor": "^5.1.23", "@vue-office/docx": "^1.6.3",
"@wangeditor/editor-for-vue": "^5.1.12", "@vue-office/excel": "^1.7.8",
"@wangeditor/plugin-formula": "^1.0.11", "@vue-office/pdf": "^2.0.2",
"axios": "^1.6.2", "@vueuse/core": "^10.7.0",
"bpmn-js": "8.9.0", "@wangeditor/editor": "^5.1.23",
"bpmn-js-properties-panel": "0.46.0", "@wangeditor/editor-for-vue": "^5.1.12",
"bpmn-js-token-simulation": "^0.10.0", "@wangeditor/plugin-formula": "^1.0.11",
"bpmn-moddle": "^6.0.0", "axios": "^1.6.2",
"camunda-bpmn-moddle": "^7.0.1", "bpmn-js": "8.9.0",
"crypto-js": "^4.2.0", "bpmn-js-properties-panel": "0.46.0",
"dayjs": "^1.11.10", "bpmn-js-token-simulation": "^0.10.0",
"diagram-js": "^11.4.1", "bpmn-moddle": "^6.0.0",
"diagram-js-minimap": "^2.0.4", "camunda-bpmn-moddle": "^7.0.1",
"echarts": "^5.4.3", "crypto-js": "^4.2.0",
"echarts-gl": "^2.0.9", "dayjs": "^1.11.10",
"echarts-liquidfill": "^3.1.0", "diagram-js": "^11.4.1",
"echarts4": "npm:echarts@^4.9.0", "diagram-js-minimap": "^2.0.4",
"element-plus": "^2.9.11", "echarts": "^5.4.3",
"exceljs": "^4.4.0", "echarts-gl": "^2.0.9",
"file-saver": "^2.0.5", "echarts-liquidfill": "^3.1.0",
"grid-layout-plus": "^1.1.0", "echarts4": "npm:echarts@^4.9.0",
"html2canvas": "^1.4.1", "element-plus": "^2.9.11",
"jquery": "^3.7.1", "exceljs": "^4.4.0",
"js-pinyin": "^0.2.5", "file-saver": "^2.0.5",
"jsencrypt": "^3.3.2", "grid-layout-plus": "^1.1.0",
"katex": "^0.16.11", "html2canvas": "^1.4.1",
"less": "^4.2.0", "jquery": "^3.7.1",
"lodash-es": "^4.17.21", "js-pinyin": "^0.2.5",
"luckyexcel": "^1.0.1", "jsencrypt": "^3.3.2",
"luckysheet": "^2.1.13", "katex": "^0.16.11",
"mathjax": "^3.2.2", "less": "^4.2.0",
"min-dash": "^4.2.1", "lodash-es": "^4.17.21",
"mitt": "^3.0.1", "luckyexcel": "^1.0.1",
"mqtt": "^5.13.3", "luckysheet": "^2.1.13",
"nprogress": "^0.2.0", "mathjax": "^3.2.2",
"pinia": "^2.1.7", "min-dash": "^4.2.1",
"pinia-plugin-persistedstate": "^3.2.1", "mitt": "^3.0.1",
"qs": "^6.12.0", "mqtt": "^5.13.3",
"screenfull": "^6.0.2", "nprogress": "^0.2.0",
"splitpanes": "^3.1.5", "pinia": "^2.1.7",
"steady-xml": "0.1.0", "pinia-plugin-persistedstate": "^3.2.1",
"svg-pan-zoom": "^3.6.2", "qs": "^6.12.0",
"use-element-plus-theme": "^0.0.5", "screenfull": "^6.0.2",
"vexip-ui": "^2.3.28", "splitpanes": "^3.1.5",
"vue": "^3.3.11", "steady-xml": "0.1.0",
"vue-baidu-map-3x": "^1.0.35", "svg-pan-zoom": "^3.6.2",
"vue-baidu-map-offline": "^1.0.7", "use-element-plus-theme": "^0.0.5",
"vue-demi": "^0.14.8", "vexip-ui": "^2.3.28",
"vue-draggable-resizable": "3.0.0-beta.2", "vue": "^3.3.11",
"vue-i18n": "9.10.2", "vue-baidu-map-3x": "^1.0.35",
"vue-katex": "^0.5.0", "vue-baidu-map-offline": "^1.0.7",
"vue-router": "4", "vue-demi": "^0.14.8",
"vue-types": "^5.1.1", "vue-draggable-resizable": "3.0.0-beta.2",
"vxe-table": "^4.5.17", "vue-i18n": "9.10.2",
"vxe-table-plugin-export-xlsx": "^4.0.1", "vue-katex": "^0.5.0",
"web-storage-cache": "^1.1.1", "vue-router": "4",
"xe-utils": "^3.5.14", "vue-types": "^5.1.1",
"yarn": "^1.22.22" "vxe-pc-ui": "~4.15.19",
}, "vxe-table": "^4.19.22",
"devDependencies": { "vxe-table-plugin-export-xlsx": "^4.0.7",
"@purge-icons/generated": "^0.9.0", "web-storage-cache": "^1.1.1",
"@types/lodash-es": "^4.17.12", "xe-utils": "^3.5.14",
"@types/node": "^20.10.5", "yarn": "^1.22.22"
"@types/splitpanes": "^2.2.6", },
"@vitejs/plugin-vue": "^4.5.2", "devDependencies": {
"@vitejs/plugin-vue-jsx": "^3.1.0", "@purge-icons/generated": "^0.9.0",
"sass": "^1.69.5", "@types/lodash-es": "^4.17.12",
"typescript": "^5.2.2", "@types/node": "^20.10.5",
"unplugin-auto-import": "^0.16.7", "@types/splitpanes": "^2.2.6",
"vite": "^5.0.8", "@vitejs/plugin-vue": "^4.5.2",
"vite-plugin-mkcert": "^1.17.3", "@vitejs/plugin-vue-jsx": "^3.1.0",
"vue-tsc": "^1.8.25" "sass": "^1.69.5",
} "typescript": "^5.2.2",
"unplugin-auto-import": "^0.16.7",
"vite": "^5.0.8",
"vite-plugin-mkcert": "^1.17.3",
"vue-tsc": "^1.8.25"
}
} }

View File

@@ -1504,7 +1504,7 @@ window.BMAP_AUTHENTIC_KEY = "Yp57V71dkOPiXjiN8VdcFRsVELzlVNKK";
for (d in b) c = c + "&" + d + "=" + encodeURIComponent(b[d]); for (d in b) c = c + "&" + d + "=" + encodeURIComponent(b[d]);
var e = function (a) { var e = function (a) {
a && (rb = o, setTimeout(function () { a && (rb = o, setTimeout(function () {
sb.src = B.oc + "images/blank.gif?" + a.src // sb.src = B.oc + "images/blank.gif?" + a.src
}, 50)) }, 50))
}, },
f = function () { f = function () {

View File

@@ -1536,7 +1536,7 @@ window.BMAP_AUTHENTIC_KEY = ''
a && a &&
((ob = q), ((ob = q),
setTimeout(function () { setTimeout(function () {
pb.src = B.Wc + 'images/blank.gif?' + a.src // pb.src = B.Wc + 'images/blank.gif?' + a.src
}, 50)) }, 50))
}, },
g = function () { g = function () {
@@ -4747,10 +4747,11 @@ window.BMAP_AUTHENTIC_KEY = ''
c.Nm c.Nm
? c.Nm.sa(b) ? c.Nm.sa(b)
: ((c.Nm = new U(b, { : ((c.Nm = new U(b, {
icon: new qc(G.qa + 'blank.gif', { // icon: new qc(G.qa + 'blank.gif', {
width: 1, // width: 1,
height: 1 // height: 1
}), // }),
icon: '',
offset: new O(0, 0), offset: new O(0, 0),
clickable: t clickable: t
})), })),
@@ -5886,7 +5887,7 @@ window.BMAP_AUTHENTIC_KEY = ''
Qc.prototype.co = function () { Qc.prototype.co = function () {
this.Gb.src = this.Gb.src =
0 < z.ca.ia && 6 >= z.ca.ia && this.GJ 0 < z.ca.ia && 6 >= z.ca.ia && this.GJ
? G.qa + 'blank.gif' ? '' //G.qa + 'blank.gif'
: '' !== this.src && this.Gb.src == this.src : '' !== this.src && this.Gb.src == this.src
? this.src + '&t = ' + Date.now() ? this.src + '&t = ' + Date.now()
: this.src : this.src

File diff suppressed because one or more lines are too long

View File

@@ -9,14 +9,20 @@ import zhCn from 'element-plus/dist/locale/zh-cn.mjs'
import useSetTheme from '@/utils/setTheme' import useSetTheme from '@/utils/setTheme'
import { onMounted } from 'vue' import { onMounted } from 'vue'
useSetTheme() useSetTheme()
onMounted(async () => { const fetchMqttUrl = async () => {
const response = await fetch('/') const response = await fetch('/')
const WebSocketUrl:any = response.headers.get('X-WebSocket-Url') const WebSocketUrl: any = response.headers.get('X-WebSocket-Url')
const WebSocketUrl2:any = response.headers.get('X-WebSocket-Url2') const WebSocketUrl2: any = response.headers.get('X-WebSocket-Url2')
const MqttUrl:any = response.headers.get('X-MqttUrl-Url') const WebSocketUrl3: any = response.headers.get('X-WebSocket-Url3')
localStorage.setItem('WebSocketUrl2', WebSocketUrl2) const MqttUrl: any = response.headers.get('X-MqttUrl-Url')
localStorage.setItem('WebSocketUrl', WebSocketUrl) localStorage.setItem('WebSocketUrl', WebSocketUrl)
localStorage.setItem('WebSocketUrl2', WebSocketUrl2)
localStorage.setItem('WebSocketUrl3', WebSocketUrl3)
localStorage.setItem('MqttUrl', MqttUrl) localStorage.setItem('MqttUrl', MqttUrl)
}
onMounted(() => {
fetchMqttUrl()
}) })
</script> </script>

View File

@@ -1,5 +1,6 @@
import createAxios from '@/utils/request' import createAxios from '@/utils/request'
// 密码规则修改 // 密码规则修改
export function ruleUpdate(data) { export function ruleUpdate(data) {
return createAxios({ return createAxios({
@@ -24,3 +25,77 @@ export function unlockRoot(data) {
data data
}) })
} }
//根据客户端名查询信息
export function getClientInfoByPath() {
return createAxios({
url: '/user-boot/authClient/getAuthClientByName/njcn',
method: 'GET',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
})
}
//客户端会话配置更新
export function updateClientSessionConfig(data) {
return createAxios({
url: '/user-boot/authClient/sessionConfigUpdate',
method: 'post',
data
})
}
//获取用户配置
export function getUserConfig() {
return createAxios({
url: '/user-boot/password/getUserStrategyList',
method: 'post'
})
}
//查询系统列表
export function getSystemList() {
return createAxios({
url: '/system-boot/config/getSysConfigData',
method: 'get'
})
}
//激活系统配置
export function activeSystemConfig(data) {
return createAxios({
url: '/system-boot/config/updateSysConfig',
method: 'post',
data
})
}
//删除系统配置
export function deleteSystemConfig( data) {
return createAxios({
url: '/system-boot/config/removeSysConfigById',
method: 'get',
params: data
})
}
//新增系统配置
export function addSystemConfig(data) {
return createAxios({
url: '/system-boot/config/addSysConfig',
method: 'post',
data
})
}
//修改系统配置
export function updateSystemConfig(data) {
return createAxios({
url: '/system-boot/config/updateSysConfig',
method: 'post',
data
})
}

View File

@@ -1,4 +1,4 @@
import request from '@/config/axios' import { requestData, requestDownload } from '@/utils/request'
// BPM 流程表达式 VO // BPM 流程表达式 VO
export interface ProcessExpressionVO { export interface ProcessExpressionVO {
@@ -12,31 +12,36 @@ export interface ProcessExpressionVO {
export const ProcessExpressionApi = { export const ProcessExpressionApi = {
// 查询BPM 流程表达式分页 // 查询BPM 流程表达式分页
getProcessExpressionPage: async (params: any) => { getProcessExpressionPage: async (params: any) => {
return await request.get({ url: `/bpm/process-expression/page`, params }) return await requestData({ url: '/bpm/process-expression/page', method: 'GET', params })
}, },
// 查询BPM 流程表达式详情 // 查询BPM 流程表达式详情
getProcessExpression: async (id: number) => { getProcessExpression: async (id: number) => {
return await request.get({ url: `/bpm/process-expression/get?id=` + id }) return await requestData({ url: '/bpm/process-expression/get?id=' + id, method: 'GET' })
}, },
// 新增BPM 流程表达式 // 新增BPM 流程表达式
createProcessExpression: async (data: ProcessExpressionVO) => { createProcessExpression: async (data: ProcessExpressionVO) => {
return await request.post({ url: `/bpm/process-expression/create`, data }) return await requestData({ url: '/bpm/process-expression/create', method: 'POST', data })
}, },
// 修改BPM 流程表达式 // 修改BPM 流程表达式
updateProcessExpression: async (data: ProcessExpressionVO) => { updateProcessExpression: async (data: ProcessExpressionVO) => {
return await request.put({ url: `/bpm/process-expression/update`, data }) return await requestData({ url: '/bpm/process-expression/update', method: 'PUT', data })
}, },
// 删除BPM 流程表达式 // 删除BPM 流程表达式
deleteProcessExpression: async (id: number) => { deleteProcessExpression: async (id: number) => {
return await request.delete({ url: `/bpm/process-expression/delete?id=` + id }) return await requestData({ url: '/bpm/process-expression/delete?id=' + id, method: 'DELETE' })
}, },
// 导出BPM 流程表达式 Excel // 导出BPM 流程表达式 Excel
exportProcessExpression: async (params) => { exportProcessExpression: async (params) => {
return await request.download({ url: `/bpm/process-expression/export-excel`, params }) return await requestDownload({
url: '/bpm/process-expression/export-excel',
method: 'GET',
params,
responseType: 'blob'
})
} }
} }

View File

@@ -1,4 +1,4 @@
import request from '@/config/axios' import { requestData } from '@/utils/request'
// BPM 流程监听器 VO // BPM 流程监听器 VO
export interface ProcessListenerVO { export interface ProcessListenerVO {
@@ -15,26 +15,26 @@ export interface ProcessListenerVO {
export const ProcessListenerApi = { export const ProcessListenerApi = {
// 查询流程监听器分页 // 查询流程监听器分页
getProcessListenerPage: async (params: any) => { getProcessListenerPage: async (params: any) => {
return await request.get({ url: `/bpm/process-listener/page`, params }) return await requestData({ url: '/bpm/process-listener/page', method: 'GET', params })
}, },
// 查询流程监听器详情 // 查询流程监听器详情
getProcessListener: async (id: number) => { getProcessListener: async (id: number) => {
return await request.get({ url: `/bpm/process-listener/get?id=` + id }) return await requestData({ url: '/bpm/process-listener/get?id=' + id, method: 'GET' })
}, },
// 新增流程监听器 // 新增流程监听器
createProcessListener: async (data: ProcessListenerVO) => { createProcessListener: async (data: ProcessListenerVO) => {
return await request.post({ url: `/bpm/process-listener/create`, data }) return await requestData({ url: '/bpm/process-listener/create', method: 'POST', data })
}, },
// 修改流程监听器 // 修改流程监听器
updateProcessListener: async (data: ProcessListenerVO) => { updateProcessListener: async (data: ProcessListenerVO) => {
return await request.put({ url: `/bpm/process-listener/update`, data }) return await requestData({ url: '/bpm/process-listener/update', method: 'PUT', data })
}, },
// 删除流程监听器 // 删除流程监听器
deleteProcessListener: async (id: number) => { deleteProcessListener: async (id: number) => {
return await request.delete({ url: `/bpm/process-listener/delete?id=` + id }) return await requestData({ url: '/bpm/process-listener/delete?id=' + id, method: 'DELETE' })
} }
} }

View File

@@ -1,4 +1,4 @@
import request from '@/config/axios' import { requestData } from '@/utils/request'
export type UserGroupVO = { export type UserGroupVO = {
id: number id: number
@@ -12,36 +12,30 @@ export type UserGroupVO = {
// 创建用户组 // 创建用户组
export const createUserGroup = async (data: UserGroupVO) => { export const createUserGroup = async (data: UserGroupVO) => {
return await request.post({ return await requestData({ url: '/bpm/user-group/create', method: 'POST', data })
url: '/bpm/user-group/create',
data: data
})
} }
// 更新用户组 // 更新用户组
export const updateUserGroup = async (data: UserGroupVO) => { export const updateUserGroup = async (data: UserGroupVO) => {
return await request.put({ return await requestData({ url: '/bpm/user-group/update', method: 'PUT', data })
url: '/bpm/user-group/update',
data: data
})
} }
// 删除用户组 // 删除用户组
export const deleteUserGroup = async (id: number) => { export const deleteUserGroup = async (id: number) => {
return await request.delete({ url: '/bpm/user-group/delete?id=' + id }) return await requestData({ url: '/bpm/user-group/delete?id=' + id, method: 'DELETE' })
} }
// 获得用户组 // 获得用户组
export const getUserGroup = async (id: number) => { export const getUserGroup = async (id: number) => {
return await request.get({ url: '/bpm/user-group/get?id=' + id }) return await requestData({ url: '/bpm/user-group/get?id=' + id, method: 'GET' })
} }
// 获得用户组分页 // 获得用户组分页
export const getUserGroupPage = async (params) => { export const getUserGroupPage = async (params) => {
return await request.get({ url: '/bpm/user-group/page', params }) return await requestData({ url: '/bpm/user-group/page', method: 'GET', params })
} }
// 获取用户组精简信息列表 // 获取用户组精简信息列表
export const getUserGroupSimpleList = async (): Promise<UserGroupVO[]> => { export const getUserGroupSimpleList = async (): Promise<UserGroupVO[]> => {
return await request.get({ url: '/bpm/user-group/simple-list' }) return await requestData({ url: '/bpm/user-group/simple-list', method: 'GET' })
} }

View File

@@ -129,7 +129,7 @@ export function queryAllByType(params: any) {
//获取用户 //获取用户
export function selectUserList(data: any) { export function selectUserList(data: any) {
return createAxios({ return createAxios({
url: '/supervision-boot/userReport/selectUserList', url: '/device-boot/userReport/selectUserList',
method: 'post', method: 'post',
data data
}) })

View File

@@ -1,7 +1,7 @@
import request from '@/utils/request' import request from '@/utils/request'
// 获取区域中断终端 // 获取区域中断终端
export function getAreaOffDev(data) { export function getAreaOffDev(data:any) {
return request({ return request({
url: '/device-boot/device/getAreaOffDev', url: '/device-boot/device/getAreaOffDev',
method: 'post', method: 'post',
@@ -9,10 +9,28 @@ export function getAreaOffDev(data) {
}) })
} }
// 获取区域中断终端 // 获取区域中断终端
export function getYwZtSubstation(data) { export function getYwZtSubstation(data:any) {
return request({ return request({
url: '/device-boot/ywZt/getYwZtSubstation', url: '/device-boot/ywZt/getYwZtSubstation',
method: 'GET', method: 'GET',
params: data params: data
}) })
} }
//终端周期检测
export function terminalCheckPage(data:any) {
return request({
url: '/device-boot/alarm/terminalCheckPage',
method: 'post',
data
})
}
export function userCheckUpload(data:any) {
return request({
url: '/device-boot/userReport/userCheckUpload',
method: 'post',
data
})
}

View File

@@ -45,3 +45,13 @@ export const getLineOverLimitData = (id: string) => {
method: 'post' method: 'post'
}) })
} }
//导出数据总览
export function dataVerifyExcel(params: any) {
return request({
url: '/device-boot/dataVerify/dataVerifyExcel',
method: 'get',
params,
responseType: 'blob'
})
}

View File

@@ -32,3 +32,12 @@ export function getDevTypeList() {
}) })
} }
export const getDeviceTypeList = (params: any) => {
return createAxios({
url: '/device-boot/devType/pageDevTypeList',
method: 'POST',
data: params
})
}

View File

@@ -4,6 +4,10 @@ import request from '@/utils/request'
export function getSubLineGiveAnAlarm(data: any) { export function getSubLineGiveAnAlarm(data: any) {
return request({ url: '/harmonic-boot/gridDiagram/getSubLineGiveAnAlarm', method: 'post', data }) return request({ url: '/harmonic-boot/gridDiagram/getSubLineGiveAnAlarm', method: 'post', data })
} }
export function getAreaObjAlarm(data: any) {
return request({ url: '/harmonic-boot/gridDiagram/getAreaObjAlarm', method: 'post', data })
}
// 变电站详细列表 // 变电站详细列表
export function getPollutionAlarmPageData(data: any) { export function getPollutionAlarmPageData(data: any) {
return request({ url: '/harmonic-boot/gridDiagram/getPollutionAlarmPageData', method: 'post', data }) return request({ url: '/harmonic-boot/gridDiagram/getPollutionAlarmPageData', method: 'post', data })
@@ -182,3 +186,7 @@ export function limitTableDetail(data: any) {
export function downPollutionSensitiveUser(data: any) { export function downPollutionSensitiveUser(data: any) {
return request({ url: '/harmonic-boot/PollutionSubstation/downPollutionSensitiveUser', method: 'post', data }) return request({ url: '/harmonic-boot/PollutionSubstation/downPollutionSensitiveUser', method: 'post', data })
} }
//干扰源类型统计
export function loadTypeVOList(data: any) {
return request({ url: '/device-boot/gridDiagram/loadTypeVOList', method: 'post', data })
}

View File

@@ -0,0 +1,103 @@
import request from '@/utils/request'
// 新增敏感用户
export function saveUser(data: any) {
return request({
url: '/device-boot/pqSensitiveUser/save',
method: 'post',
data: data
})
}
// 修改敏感用户
export function updateUser(data: any) {
return request({
url: '/device-boot/pqSensitiveUser/update',
method: 'post',
data: data
})
}
// 删除敏感用户
export function deleteUser(data: any) {
return request({
url: '/device-boot/pqSensitiveUser/delete',
method: 'post',
data: data
})
}
/**
* 干扰源接入功能删除流程
*/
export const deleteUserReport = (data: any) => {
return request({
url: '/device-boot/userReport/deleteUserReport',
method: 'POST',
data
})
}
/**
* 提交表单数据
*/
export const submitFormData = (data: any) => {
return request({
url: '/device-boot/userReport/add',
method: 'POST',
data: data
})
}
// 根据id获取用户档案录入的详细数据
export const getById = (data: any) => {
return request({
url: '/device-boot/userReport/getById',
method: 'get',
params: data
})
}
/**
* 提交表单数据
*/
export const addEditor = (data: any) => {
return request({
url: '/device-boot/userReportRenewal/addEditor',
method: 'POST',
data: data
})
}
// 根据id获取用户档案录入的详细数据
export const getByDeptDevLine = (params: any) => {
return request({
url: '/device-boot/line/getByDeptDevLine',
method: 'get',
params
})
}
/**
* 根据id获取用户档案录入的详细数据
*/
export const getUserReportUpdateById = (id: any) => {
return request({
url: '/device-boot/userReportRenewal/getUserReportUpdateById?businessId='+id,
method: 'POST',
})
}
// 根据id获取用户档案录入的详细数据
export const getUserReportById = (id: any) => {
return request({
url: '/device-boot/userReport/getUserReportById?id='+id,
method: 'get'
})
}
// 根据id查询文件信息集合
export const getFileById = (params: any) => {
return request({
url: '/device-boot/fileUrl/getFileById',
method: 'get',
params
})
}

View File

@@ -2,13 +2,35 @@ import createAxios from '@/utils/request'
import { genFileId, ElMessage, ElNotification } from 'element-plus' import { genFileId, ElMessage, ElNotification } from 'element-plus'
//事件报告 //事件报告
// export function getEventReport(data) {
// return createAxios({
// url: '/event-boot/report/getEventReport',
// method: 'post',
// data
// })
// }
export function getEventReport(data) { export function getEventReport(data) {
return createAxios({ return createAxios({
url: '/event-boot/report/getEventReport', url: '/event-boot/report/createEventReport',
method: 'post', method: 'post',
data data,
responseType: 'blob'
}).then(async res => {
let load: any = await readJsonBlob(res)
if (load.code) {
if (load.data.code == 'A0011') {
ElMessage.warning('下载失败!')
} else {
ElMessage.warning(load.data.message)
}
} else {
return res
}
}) })
} }
// 生成报告 // 生成报告
export function getAreaReport(data) { export function getAreaReport(data) {
return createAxios({ return createAxios({

View File

@@ -20,6 +20,14 @@ export function getAreaDept() {
method: 'post' method: 'post'
}) })
} }
// 承载能力评估去除前缀
export function getAreaDeptRemoveMode(params) {
return request({
url: '/user-boot/dept/loginDeptTree',
method: 'GET',
params: params
})
}
export function getOnlineRateDataCensus(data:any) { export function getOnlineRateDataCensus(data:any) {
return request({ return request({
url: '/device-boot/terminalOnlineRateData/getOnlineRateDataCensus', url: '/device-boot/terminalOnlineRateData/getOnlineRateDataCensus',

View File

@@ -0,0 +1,42 @@
import request from "@/utils/request";
//查询所有模板
export function getList(data: any) {
return request({
url: "/system-boot/EventTemplate/getList",
method: "post",
data,
});
}
//字典树
export function getDictTree(data: any) {
return request({
url: "/system-boot/reportDict/DictTree",
method: "post",
params:data
});
}
//新增模板
export function addData(data: any) {
return request({
url: "/system-boot/EventTemplate/add",
method: "post",
data,
});
}
//修改模板
export function updateData(data: any) {
return request({
url: "/system-boot/EventTemplate/update",
method: "post",
data,
});
}
//删除模板
export function deleteData(data: any) {
return request({
url: "/system-boot/EventTemplate/delete",
method: "post",
data,
});
}

View File

@@ -0,0 +1,43 @@
import request from '@/utils/request'
//获取字典树数据
export function getDictTree(data) {
return request({
url: "/system-boot/reportDict/DictTree",
method: "post",
params:data
});
}
//查询字典列表
export function getReportDictList(data: any) {
return request({
url: "/system-boot/reportDict/getReportDictList",
method: "post",
data
});
}
//新增字典表
export function addDict(data: any) {
return request({
url: "/system-boot/reportDict/addDict",
method: "post",
data
});
}
//更新字典表
export function updateDict(data: any) {
return request({
url: "/system-boot/reportDict/updateDict",
method: "put",
data
});
}
//删除字典表
export function deleteDict(data: any) {
return request({
url: "/system-boot/reportDict/deleteDict",
method: "delete",
params:data
});
}

View File

@@ -0,0 +1,37 @@
import request from '@/utils/request'
//区域树形表格接口
export function areaTree(data: any) {
return request({
url: '/system-boot/area/areaTree',
method: 'post',
params: data,
})
}
export function areaAdd(data: any) {
return request({
url: '/system-boot/area/add',
method: 'post',
data: data,
})
}
export function areaDelete(data: any) {
return request({
url: '/system-boot/area/delete',
method: 'post',
data: data,
})
}
export function update(data: any) {
return request({
url: '/system-boot/area/update',
method: 'post',
data: data,
})
}
export function selectPid(data: any) {
return request({
url: '/system-boot/area/selectPid',
method: 'post',
data: data,
})
}

View File

@@ -1,4 +1,4 @@
import request from '@/config/axios' import { requestData, requestDownload } from '@/utils/request'
export type DictDataVO = { export type DictDataVO = {
id: number | undefined id: number | undefined
@@ -15,35 +15,35 @@ export type DictDataVO = {
// 查询字典数据(精简)列表 // 查询字典数据(精简)列表
export const getSimpleDictDataList = () => { export const getSimpleDictDataList = () => {
return request.get({ url: '/system/dict-data/simple-list' }) return requestData({ url: '/system/dict-data/simple-list', method: 'GET' })
} }
// 查询字典数据列表 // 查询字典数据列表
export const getDictDataPage = (params: PageParam) => { export const getDictDataPage = (params: PageParam) => {
return request.get({ url: '/system/dict-data/page', params }) return requestData({ url: '/system/dict-data/page', method: 'GET', params })
} }
// 查询字典数据详情 // 查询字典数据详情
export const getDictData = (id: number) => { export const getDictData = (id: number) => {
return request.get({ url: '/system/dict-data/get?id=' + id }) return requestData({ url: '/system/dict-data/get?id=' + id, method: 'GET' })
} }
// 新增字典数据 // 新增字典数据
export const createDictData = (data: DictDataVO) => { export const createDictData = (data: DictDataVO) => {
return request.post({ url: '/system/dict-data/create', data }) return requestData({ url: '/system/dict-data/create', method: 'POST', data })
} }
// 修改字典数据 // 修改字典数据
export const updateDictData = (data: DictDataVO) => { export const updateDictData = (data: DictDataVO) => {
return request.put({ url: '/system/dict-data/update', data }) return requestData({ url: '/system/dict-data/update', method: 'PUT', data })
} }
// 删除字典数据 // 删除字典数据
export const deleteDictData = (id: number) => { export const deleteDictData = (id: number) => {
return request.delete({ url: '/system/dict-data/delete?id=' + id }) return requestData({ url: '/system/dict-data/delete?id=' + id, method: 'DELETE' })
} }
// 导出字典类型数据 // 导出字典类型数据
export const exportDictData = (params) => { export const exportDictData = (params) => {
return request.download({ url: '/system/dict-data/export', params }) return requestDownload({ url: '/system/dict-data/export', method: 'GET', params, responseType: 'blob' })
} }

View File

@@ -1,4 +1,4 @@
import request from '@/config/axios' import { requestData, requestDownload } from '@/utils/request'
export type DictTypeVO = { export type DictTypeVO = {
id: number | undefined id: number | undefined
@@ -11,34 +11,35 @@ export type DictTypeVO = {
// 查询字典(精简)列表 // 查询字典(精简)列表
export const getSimpleDictTypeList = () => { export const getSimpleDictTypeList = () => {
return request.get({ url: '/system/dict-type/list-all-simple' }) return requestData({ url: '/system/dict-type/list-all-simple', method: 'GET' })
} }
// 查询字典列表 // 查询字典列表
export const getDictTypePage = (params: PageParam) => { export const getDictTypePage = (params: PageParam) => {
return request.get({ url: '/system/dict-type/page', params }) return requestData({ url: '/system/dict-type/page', method: 'GET', params })
} }
// 查询字典详情 // 查询字典详情
export const getDictType = (id: number) => { export const getDictType = (id: number) => {
return request.get({ url: '/system/dict-type/get?id=' + id }) return requestData({ url: '/system/dict-type/get?id=' + id, method: 'GET' })
} }
// 新增字典 // 新增字典
export const createDictType = (data: DictTypeVO) => { export const createDictType = (data: DictTypeVO) => {
return request.post({ url: '/system/dict-type/create', data }) return requestData({ url: '/system/dict-type/create', method: 'POST', data })
} }
// 修改字典 // 修改字典
export const updateDictType = (data: DictTypeVO) => { export const updateDictType = (data: DictTypeVO) => {
return request.put({ url: '/system/dict-type/update', data }) return requestData({ url: '/system/dict-type/update', method: 'PUT', data })
} }
// 删除字典 // 删除字典
export const deleteDictType = (id: number) => { export const deleteDictType = (id: number) => {
return request.delete({ url: '/system/dict-type/delete?id=' + id }) return requestData({ url: '/system/dict-type/delete?id=' + id, method: 'DELETE' })
} }
// 导出字典类型 // 导出字典类型
export const exportDictType = (params) => { export const exportDictType = (params) => {
return request.download({ url: '/system/dict-type/export', params }) return requestDownload({ url: '/system/dict-type/export', method: 'GET', params, responseType: 'blob' })
} }

View File

@@ -1,4 +1,4 @@
import request from '@/config/axios' import { requestData } from '@/utils/request'
import qs from 'qs' import qs from 'qs'
export interface NotifyMessageVO { export interface NotifyMessageVO {
@@ -17,33 +17,34 @@ export interface NotifyMessageVO {
} }
// 查询站内信消息列表 // 查询站内信消息列表
export const getNotifyMessagePage = async (params: PageParam) => { export const getNotifyMessagePage = async (params: any) => {
return await request.get({ url: '/system/notify-message/page', params }) return await requestData({ url: '/system/notify-message/page', method: 'GET', params })
} }
// 获得我的站内信分页 // 获得我的站内信分页
export const getMyNotifyMessagePage = async (params: PageParam) => { export const getMyNotifyMessagePage = async (params: any) => {
return await request.get({ url: '/system/notify-message/my-page', params }) return await requestData({ url: '/system/notify-message/my-page', method: 'GET', params })
} }
// 批量标记已读 // 批量标记已读
export const updateNotifyMessageRead = async (ids) => { export const updateNotifyMessageRead = async (ids: any) => {
return await request.put({ return await requestData({
url: '/system/notify-message/update-read?' + qs.stringify({ ids: ids }, { indices: false }) url: '/system/notify-message/update-read?' + qs.stringify({ ids: ids }, { indices: false }),
method: 'PUT'
}) })
} }
// 标记所有站内信为已读 // 标记所有站内信为已读
export const updateAllNotifyMessageRead = async () => { export const updateAllNotifyMessageRead = async () => {
return await request.put({ url: '/system/notify-message/update-all-read' }) return await requestData({ url: '/system/notify-message/update-all-read', method: 'PUT' })
} }
// 获取当前用户的最新站内信列表 // 获取当前用户的最新站内信列表
export const getUnreadNotifyMessageList = async () => { export const getUnreadNotifyMessageList = async () => {
return await request.get({ url: '/system/notify-message/get-unread-list' }) return await requestData({ url: '/system/notify-message/get-unread-list', method: 'GET' })
} }
// 获得当前用户的未读站内信数量 // 获得当前用户的未读站内信数量
export const getUnreadNotifyMessageCount = async () => { export const getUnreadNotifyMessageCount = async () => {
return await request.get({ url: '/system/notify-message/get-unread-count' }) return await requestData({ url: '/system/notify-message/get-unread-count', method: 'GET' })
} }

View File

@@ -1,4 +1,4 @@
import request from '@/config/axios' import { requestData } from '@/utils/request'
export interface NotifyTemplateVO { export interface NotifyTemplateVO {
id?: number id?: number
@@ -20,30 +20,30 @@ export interface NotifySendReqVO {
// 查询站内信模板列表 // 查询站内信模板列表
export const getNotifyTemplatePage = async (params: PageParam) => { export const getNotifyTemplatePage = async (params: PageParam) => {
return await request.get({ url: '/system/notify-template/page', params }) return await requestData({ url: '/system/notify-template/page', method: 'GET', params })
} }
// 查询站内信模板详情 // 查询站内信模板详情
export const getNotifyTemplate = async (id: number) => { export const getNotifyTemplate = async (id: number) => {
return await request.get({ url: '/system/notify-template/get?id=' + id }) return await requestData({ url: '/system/notify-template/get?id=' + id, method: 'GET' })
} }
// 新增站内信模板 // 新增站内信模板
export const createNotifyTemplate = async (data: NotifyTemplateVO) => { export const createNotifyTemplate = async (data: NotifyTemplateVO) => {
return await request.post({ url: '/system/notify-template/create', data }) return await requestData({ url: '/system/notify-template/create', method: 'POST', data })
} }
// 修改站内信模板 // 修改站内信模板
export const updateNotifyTemplate = async (data: NotifyTemplateVO) => { export const updateNotifyTemplate = async (data: NotifyTemplateVO) => {
return await request.put({ url: '/system/notify-template/update', data }) return await requestData({ url: '/system/notify-template/update', method: 'PUT', data })
} }
// 删除站内信模板 // 删除站内信模板
export const deleteNotifyTemplate = async (id: number) => { export const deleteNotifyTemplate = async (id: number) => {
return await request.delete({ url: '/system/notify-template/delete?id=' + id }) return await requestData({ url: '/system/notify-template/delete?id=' + id, method: 'DELETE' })
} }
// 发送站内信 // 发送站内信
export const sendNotify = (data: NotifySendReqVO) => { export const sendNotify = (data: NotifySendReqVO) => {
return request.post({ url: '/system/notify-template/send-notify', data }) return requestData({ url: '/system/notify-template/send-notify', method: 'POST', data })
} }

View File

@@ -43,6 +43,30 @@ export async function login(params: any) {
params params
}) })
} }
//辽宁嵌入登录获取token
export async function loginLNqr(data: any) {
return request({
url: '/pqs-auth/oauth/lnLogin',
method: 'get',
params: data
})
}
export async function lnRefreshToken(data: any) {
return request({
url: '/pqs-auth/oauth/lnRefreshToken',
method: 'get',
params: data
})
}
export async function logout() {
return request({
url: '/pqs-auth/oauth/logout',
method: 'DELETE',
})
}
//获取用户信息 //获取用户信息
export function getUserById() { export function getUserById() {
@@ -70,6 +94,17 @@ export function refreshToken(): Promise<any> {
}) })
} }
export function lnRefreshTokenTo(): Promise<any> {
// 调用useAdminInfo函数获取管理员信息并将其赋值给adminInfo变量
const adminInfo = useAdminInfo()
return lnRefreshToken({
refreshToken: adminInfo.refresh_token,
clientId: "njcn",
clientSecret:"njcnpqs"
})
}
/** /**
* 获取营销用户列表 * 获取营销用户列表
* @returns {AxiosPromise} * @returns {AxiosPromise}

BIN
src/assets/imgs/m0.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

BIN
src/assets/imgs/m1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

BIN
src/assets/imgs/m2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

BIN
src/assets/imgs/m3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

BIN
src/assets/imgs/m4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

File diff suppressed because one or more lines are too long

View File

@@ -1,6 +1,6 @@
<!-- 数据字典 Select 选择器 --> <!-- 数据字典 Select 选择器 -->
<template> <template>
<el-select class="w-1/1" v-bind="attrs"> <el-select class="w-1/1" v-bind="attrs" filterable>
<template v-if="valueType === 'int'"> <template v-if="valueType === 'int'">
<el-option <el-option
v-for="(dict, index) in getIntDictOptions(dictType)" v-for="(dict, index) in getIntDictOptions(dictType)"

View File

@@ -30,7 +30,7 @@ import { downloadFile } from '@/api/system-boot/file'
import { previewFile } from '@/utils/fileDownLoad' import { previewFile } from '@/utils/fileDownLoad'
const { push, options, currentRoute } = useRouter() const { push, options, currentRoute } = useRouter()
const VITE_FLAG = import.meta.env.VITE_NAME == 'jibei' const VITE_FLAG = false//import.meta.env.VITE_NAME == 'jibei'
// const url = 'http://192.168.1.22:9009/excelreport' + currentRoute.value.href?.split('?')[1] // const url = 'http://192.168.1.22:9009/excelreport' + currentRoute.value.href?.split('?')[1]
const url = ref('') const url = ref('')
const excelOptions = ref({}) const excelOptions = ref({})

View File

@@ -103,7 +103,7 @@ const highlightDiagram = async () => {
let findProcessTask = false //是否已经高亮了进行中的任务 let findProcessTask = false //是否已经高亮了进行中的任务
//进行中高亮之后的任务 key 集合,用于过滤掉 taskList 进行中后面的任务,避免进行中后面的数据 Hover 还有数据 //进行中高亮之后的任务 key 集合,用于过滤掉 taskList 进行中后面的任务,避免进行中后面的数据 Hover 还有数据
let removeTaskDefinitionKeyList = [] let removeTaskDefinitionKeyList = []
// debugger //
//芋道这里取值是rootElements[0].flowElements //芋道这里取值是rootElements[0].flowElements
bpmnModeler.getDefinitions().rootElements[1].flowElements?.forEach((n: any) => { bpmnModeler.getDefinitions().rootElements[1].flowElements?.forEach((n: any) => {
let activity: any = activityList.find((m: any) => m.key === n.id) // 找到对应的活动 let activity: any = activityList.find((m: any) => m.key === n.id) // 找到对应的活动
@@ -135,7 +135,6 @@ const highlightDiagram = async () => {
// 处理 outgoing 出线 // 处理 outgoing 出线
const outgoing = getActivityOutgoing(activity) const outgoing = getActivityOutgoing(activity)
outgoing?.forEach((nn: any) => { outgoing?.forEach((nn: any) => {
// debugger
let targetActivity: any = activityList.find((m: any) => m.key === nn.targetRef.id) let targetActivity: any = activityList.find((m: any) => m.key === nn.targetRef.id)
// 如果目标活动存在则根据该活动是否结束进行【bpmn:SequenceFlow】连线的高亮设置 // 如果目标活动存在则根据该活动是否结束进行【bpmn:SequenceFlow】连线的高亮设置
if (targetActivity) { if (targetActivity) {

View File

@@ -2,14 +2,14 @@
<div class="panel-tab__content"> <div class="panel-tab__content">
<el-form :model="flowConditionForm" label-width="90px" size="small"> <el-form :model="flowConditionForm" label-width="90px" size="small">
<el-form-item label="流转类型"> <el-form-item label="流转类型">
<el-select v-model="flowConditionForm.type" @change="updateFlowType"> <el-select v-model="flowConditionForm.type" @change="updateFlowType" filterable>
<el-option label="普通流转路径" value="normal" /> <el-option label="普通流转路径" value="normal" />
<el-option label="默认流转路径" value="default" /> <el-option label="默认流转路径" value="default" />
<el-option label="条件流转路径" value="condition" /> <el-option label="条件流转路径" value="condition" />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="条件格式" v-if="flowConditionForm.type === 'condition'" key="condition"> <el-form-item label="条件格式" v-if="flowConditionForm.type === 'condition'" key="condition">
<el-select v-model="flowConditionForm.conditionType"> <el-select v-model="flowConditionForm.conditionType" filterable>
<el-option label="表达式" value="expression" /> <el-option label="表达式" value="expression" />
<el-option label="脚本" value="script" /> <el-option label="脚本" value="script" />
</el-select> </el-select>
@@ -33,7 +33,7 @@
<el-input v-model="flowConditionForm.language" clearable @change="updateFlowCondition" /> <el-input v-model="flowConditionForm.language" clearable @change="updateFlowCondition" />
</el-form-item> </el-form-item>
<el-form-item label="脚本类型" key="scriptType"> <el-form-item label="脚本类型" key="scriptType">
<el-select v-model="flowConditionForm.scriptType"> <el-select v-model="flowConditionForm.scriptType" filterable>
<el-option label="内联脚本" value="inlineScript" /> <el-option label="内联脚本" value="inlineScript" />
<el-option label="外部脚本" value="externalScript" /> <el-option label="外部脚本" value="externalScript" />
</el-select> </el-select>

View File

@@ -3,7 +3,7 @@
<el-form label-width="80px"> <el-form label-width="80px">
<el-form-item label="流程表单"> <el-form-item label="流程表单">
<!-- <el-input v-model="formKey" clearable @change="updateElementFormKey" />--> <!-- <el-input v-model="formKey" clearable @change="updateElementFormKey" />-->
<el-select v-model="formKey" clearable @change="updateElementFormKey"> <el-select v-model="formKey" clearable @change="updateElementFormKey" filterable>
<el-option v-for="form in formList" :key="form.id" :label="form.name" :value="form.id" /> <el-option v-for="form in formList" :key="form.id" :label="form.name" :value="form.id" />
</el-select> </el-select>
</el-form-item> </el-form-item>

View File

@@ -64,7 +64,7 @@
prop='event' prop='event'
:rules="{ required: true, trigger: ['blur', 'change'] }" :rules="{ required: true, trigger: ['blur', 'change'] }"
> >
<el-select v-model='listenerForm.event'> <el-select v-model='listenerForm.event' filterable>
<el-option label='start' value='start' /> <el-option label='start' value='start' />
<el-option label='end' value='end' /> <el-option label='end' value='end' />
</el-select> </el-select>
@@ -74,7 +74,7 @@
prop='listenerType' prop='listenerType'
:rules="{ required: true, trigger: ['blur', 'change'] }" :rules="{ required: true, trigger: ['blur', 'change'] }"
> >
<el-select v-model='listenerForm.listenerType'> <el-select v-model='listenerForm.listenerType' filterable>
<el-option <el-option
v-for='i in Object.keys(listenerTypeObject)' v-for='i in Object.keys(listenerTypeObject)'
:key='i' :key='i'
@@ -125,7 +125,7 @@
key='listener-script-type' key='listener-script-type'
:rules="{ required: true, trigger: ['blur', 'change'], message: '请选择脚本类型' }" :rules="{ required: true, trigger: ['blur', 'change'], message: '请选择脚本类型' }"
> >
<el-select v-model='listenerForm.scriptType'> <el-select v-model='listenerForm.scriptType' filterable>
<el-option label='内联脚本' value='inlineScript' /> <el-option label='内联脚本' value='inlineScript' />
<el-option label='外部脚本' value='externalScript' /> <el-option label='外部脚本' value='externalScript' />
</el-select> </el-select>
@@ -228,7 +228,7 @@
prop='fieldType' prop='fieldType'
:rules="{ required: true, trigger: ['blur', 'change'] }" :rules="{ required: true, trigger: ['blur', 'change'] }"
> >
<el-select v-model='listenerFieldForm.fieldType'> <el-select v-model='listenerFieldForm.fieldType' filterable>
<el-option <el-option
v-for='i in Object.keys(fieldTypeObject)' v-for='i in Object.keys(fieldTypeObject)'
:key='i' :key='i'
@@ -318,7 +318,6 @@ const resetListenersList = () => {
} }
// 打开 监听器详情 侧边栏 // 打开 监听器详情 侧边栏
const openListenerForm = (listener, index?) => { const openListenerForm = (listener, index?) => {
// debugger
if (listener) { if (listener) {
listenerForm.value = initListenerForm(listener) listenerForm.value = initListenerForm(listener)
editingListenerIndex.value = index editingListenerIndex.value = index
@@ -356,7 +355,6 @@ const openListenerFieldForm = (field, index?) => {
} }
// 保存监听器注入字段 // 保存监听器注入字段
const saveListenerFiled = async () => { const saveListenerFiled = async () => {
// debugger
let validateStatus = await listenerFieldFormRef.value.validate() let validateStatus = await listenerFieldFormRef.value.validate()
if (!validateStatus) return // 验证不通过直接返回 if (!validateStatus) return // 验证不通过直接返回
if (editingListenerFieldIndex.value === -1) { if (editingListenerFieldIndex.value === -1) {
@@ -373,7 +371,6 @@ const saveListenerFiled = async () => {
} }
// 移除监听器字段 // 移除监听器字段
const removeListenerField = (index) => { const removeListenerField = (index) => {
// debugger
ElMessageBox.confirm('确认移除该字段吗?', '提示', { ElMessageBox.confirm('确认移除该字段吗?', '提示', {
confirmButtonText: '确 认', confirmButtonText: '确 认',
cancelButtonText: '取 消' cancelButtonText: '取 消'
@@ -402,7 +399,6 @@ const removeListener = (index) => {
} }
// 保存监听器配置 // 保存监听器配置
const saveListenerConfig = async () => { const saveListenerConfig = async () => {
// debugger
let validateStatus = await listenerFormRef.value.validate() let validateStatus = await listenerFormRef.value.validate()
if (!validateStatus) return // 验证不通过直接返回 if (!validateStatus) return // 验证不通过直接返回
const listenerObject = createListenerObject(listenerForm.value, false, prefix) const listenerObject = createListenerObject(listenerForm.value, false, prefix)

View File

@@ -62,7 +62,7 @@
prop="event" prop="event"
:rules="{ required: true, trigger: ['blur', 'change'] }" :rules="{ required: true, trigger: ['blur', 'change'] }"
> >
<el-select v-model="listenerForm.event"> <el-select v-model="listenerForm.event" filterable>
<el-option <el-option
v-for="i in Object.keys(listenerEventTypeObject)" v-for="i in Object.keys(listenerEventTypeObject)"
:key="i" :key="i"
@@ -83,7 +83,7 @@
prop="listenerType" prop="listenerType"
:rules="{ required: true, trigger: ['blur', 'change'] }" :rules="{ required: true, trigger: ['blur', 'change'] }"
> >
<el-select v-model="listenerForm.listenerType"> <el-select v-model="listenerForm.listenerType" filterable>
<el-option <el-option
v-for="i in Object.keys(listenerTypeObject)" v-for="i in Object.keys(listenerTypeObject)"
:key="i" :key="i"
@@ -134,7 +134,7 @@
key="listener-script-type" key="listener-script-type"
:rules="{ required: true, trigger: ['blur', 'change'], message: '请选择脚本类型' }" :rules="{ required: true, trigger: ['blur', 'change'], message: '请选择脚本类型' }"
> >
<el-select v-model="listenerForm.scriptType"> <el-select v-model="listenerForm.scriptType" filterable>
<el-option label="内联脚本" value="inlineScript" /> <el-option label="内联脚本" value="inlineScript" />
<el-option label="外部脚本" value="externalScript" /> <el-option label="外部脚本" value="externalScript" />
</el-select> </el-select>
@@ -161,7 +161,7 @@
<template v-if="listenerForm.event === 'timeout'"> <template v-if="listenerForm.event === 'timeout'">
<el-form-item label="定时器类型" prop="eventDefinitionType" key="eventDefinitionType"> <el-form-item label="定时器类型" prop="eventDefinitionType" key="eventDefinitionType">
<el-select v-model="listenerForm.eventDefinitionType"> <el-select v-model="listenerForm.eventDefinitionType" filterable>
<el-option label="统计时间" value="date" /> <el-option label="统计时间" value="date" />
<el-option label="持续时长" value="duration" /> <el-option label="持续时长" value="duration" />
<el-option label="循环" value="cycle" /> <el-option label="循环" value="cycle" />
@@ -259,7 +259,7 @@
prop="fieldType" prop="fieldType"
:rules="{ required: true, trigger: ['blur', 'change'] }" :rules="{ required: true, trigger: ['blur', 'change'] }"
> >
<el-select v-model="listenerFieldForm.fieldType"> <el-select v-model="listenerFieldForm.fieldType" filterable>
<el-option <el-option
v-for="i in Object.keys(fieldTypeObject)" v-for="i in Object.keys(fieldTypeObject)"
:key="i" :key="i"

View File

@@ -7,7 +7,7 @@
<el-button size="small" @click="changeConfig('或签')">或签</el-button> <el-button size="small" @click="changeConfig('或签')">或签</el-button>
</el-form-item> </el-form-item>
<el-form-item label="会签类型"> <el-form-item label="会签类型">
<el-select v-model="loopCharacteristics" @change="changeLoopCharacteristicsType"> <el-select v-model="loopCharacteristics" @change="changeLoopCharacteristicsType" filterable>
<el-option label="并行多重事件" value="ParallelMultiInstance" /> <el-option label="并行多重事件" value="ParallelMultiInstance" />
<el-option label="时序多重事件" value="SequentialMultiInstance" /> <el-option label="时序多重事件" value="SequentialMultiInstance" />
<el-option label="无" value="Null" /> <el-option label="无" value="Null" />

View File

@@ -9,7 +9,7 @@
flex-wrap: nowrap; flex-wrap: nowrap;
" "
> >
<el-select v-model="bindMessageId" @change="updateTaskMessage"> <el-select v-model="bindMessageId" @change="updateTaskMessage" filterable>
<el-option <el-option
v-for="key in Object.keys(messageMap)" v-for="key in Object.keys(messageMap)"
:value="key" :value="key"

View File

@@ -9,7 +9,7 @@
/> />
</el-form-item> </el-form-item>
<el-form-item label="脚本类型"> <el-form-item label="脚本类型">
<el-select v-model="scriptTaskForm.scriptType"> <el-select v-model="scriptTaskForm.scriptType" filterable>
<el-option label="内联脚本" value="inline" /> <el-option label="内联脚本" value="inline" />
<el-option label="外部资源" value="external" /> <el-option label="外部资源" value="external" />
</el-select> </el-select>

View File

@@ -6,6 +6,7 @@
clearable clearable
style='width: 100%' style='width: 100%'
@change='changeCandidateStrategy' @change='changeCandidateStrategy'
filterable
> >
<el-option <el-option
v-for='dict in RuleTypeList' v-for='dict in RuleTypeList'
@@ -26,6 +27,7 @@
multiple multiple
style='width: 100%' style='width: 100%'
@change='updateElementTask' @change='updateElementTask'
filterable
> >
<el-option v-for='item in roleOptions' :key='item.id' :label='item.name' :value='item.id' /> <el-option v-for='item in roleOptions' :key='item.id' :label='item.name' :value='item.id' />
</el-select> </el-select>
@@ -61,6 +63,7 @@
multiple multiple
style='width: 100%' style='width: 100%'
@change='updateElementTask' @change='updateElementTask'
filterable
> >
<el-option <el-option
v-for='item in userOptions' v-for='item in userOptions'

View File

@@ -126,7 +126,8 @@ const prop = defineProps({
width: { type: [String, Number] }, width: { type: [String, Number] },
height: { type: [String, Number] }, height: { type: [String, Number] },
timeKey: { type: [String, Number] }, timeKey: { type: [String, Number] },
timeValue: { type: Object } timeValue: { type: Object },
searchKey: { type: [String, Number] }
}) })
const headerHeight = ref(57) const headerHeight = ref(57)
@@ -347,22 +348,22 @@ onMounted(() => {
tableStore.index() tableStore.index()
}) })
watch( watch(
() => prop.timeKey, () => prop.searchKey,
val => { val => {
tableStore.index() tableStore.index()
} }
) )
watch( // watch(
() => prop.timeValue, // () => prop.timeValue,
val => { // val => {
tableStore.index() // tableStore.index()
}, // },
{ // {
deep: true // deep: true
} // }
) // )
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@import '@/assets/font/iconfont.css'; @import '@/assets/font/iconfont.css';

View File

@@ -122,7 +122,8 @@ const prop = defineProps({
width: { type: [String, Number] }, width: { type: [String, Number] },
height: { type: [String, Number] }, height: { type: [String, Number] },
timeKey: { type: [String, Number] }, timeKey: { type: [String, Number] },
timeValue: { type: Object } timeValue: { type: Object },
searchKey: { type: [String, Number] }
}) })
const headerHeight = ref(57) const headerHeight = ref(57)
@@ -183,7 +184,7 @@ const tableStore: any = new TableStore({
if (tableStore.table.params.statisticalType.name == '终端厂家') { if (tableStore.table.params.statisticalType.name == '终端厂家') {
return k.citTotalNum != 0 return k.citTotalNum != 0
} else { } else {
return k.citName != '上送国网' && k.citName != '上送国网' return !k.citName.includes('上送')
} }
}) })
} }
@@ -329,22 +330,22 @@ onMounted(() => {
tableStore.index() tableStore.index()
}) })
watch( watch(
() => prop.timeKey, () => prop.searchKey,
val => { val => {
tableStore.index() tableStore.index()
} }
) )
watch( // watch(
() => prop.timeValue, // () => prop.timeValue,
val => { // val => {
tableStore.index() // tableStore.index()
}, // },
{ // {
deep: true // deep: true
} // }
) // )
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@import '@/assets/font/iconfont.css'; @import '@/assets/font/iconfont.css';

View File

@@ -7,7 +7,8 @@
@selectChange="selectChange" @selectChange="selectChange"
datePicker datePicker
v-if="fullscreen" v-if="fullscreen"
></TableHeader> >
</TableHeader>
<div <div
class="monitoringPoints" class="monitoringPoints"
:style="{ :style="{
@@ -120,7 +121,8 @@ const prop = defineProps({
width: { type: [String, Number] }, width: { type: [String, Number] },
height: { type: [String, Number] }, height: { type: [String, Number] },
timeKey: { type: [String, Number] }, timeKey: { type: [String, Number] },
timeValue: { type: Object } timeValue: { type: Object },
searchKey: { type: [String, Number] }
}) })
const headerHeight = ref(57) const headerHeight = ref(57)
@@ -181,7 +183,7 @@ const tableStore: any = new TableStore({
if (tableStore.table.params.statisticalType.name == '终端厂家') { if (tableStore.table.params.statisticalType.name == '终端厂家') {
return k.citTotalNum != 0 return k.citTotalNum != 0
} else { } else {
return k.citName != '上送国网' && k.citName != '上送国网' return !k.citName.includes('上送')
} }
}) })
@@ -328,22 +330,22 @@ onMounted(() => {
tableStore.index() tableStore.index()
}) })
watch( watch(
() => prop.timeKey, () => prop.searchKey,
val => { val => {
tableStore.index() tableStore.index()
} }
) )
watch( // watch(
() => prop.timeValue, // () => prop.timeValue,
val => { // val => {
tableStore.index() // tableStore.index()
}, // },
{ // {
deep: true // deep: true
} // }
) // )
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@import '@/assets/font/iconfont.css'; @import '@/assets/font/iconfont.css';

View File

@@ -150,7 +150,8 @@ const prop = defineProps({
width: { type: [String, Number] }, width: { type: [String, Number] },
height: { type: [String, Number] }, height: { type: [String, Number] },
timeKey: { type: [String, Number] }, timeKey: { type: [String, Number] },
timeValue: { type: Object } timeValue: { type: Object },
searchKey: { type: [String, Number] }
}) })
const headerHeight = ref(57) const headerHeight = ref(57)
@@ -233,7 +234,7 @@ const tableStore: any = new TableStore({
if (tableStore.table.params.statisticalType.name == '终端厂家') { if (tableStore.table.params.statisticalType.name == '终端厂家') {
return k.count != 0 return k.count != 0
} else { } else {
return k.name != '上送国网' && k.name != '上送国网' return !k.name.includes('上送')
} }
}) })
} }
@@ -246,22 +247,28 @@ onMounted(() => {
tableStore.index() tableStore.index()
}) })
watch( watch(
() => prop.timeKey, () => prop.searchKey,
val => { val => {
tableStore.index() tableStore.index()
} }
) )
watch( // watch(
() => prop.timeValue, // () => prop.timeKey,
// val => {
// tableStore.index()
// }
// )
// watch(
// () => prop.timeValue,
val => { // val => {
tableStore.index() // tableStore.index()
}, // },
{ // {
deep: true // deep: true
} // }
) // )
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@import '@/assets/font/iconfont.css'; @import '@/assets/font/iconfont.css';

View File

@@ -169,7 +169,7 @@ const handlerBar = (options: any) => {
if (Array.isArray(options.series)) { if (Array.isArray(options.series)) {
options.series.forEach((item: any) => { options.series.forEach((item: any) => {
if (item.type === 'bar') { if (item.type === 'bar') {
item.barMinHeight = 10 item.barMinHeight = 0
item.barMaxWidth = 20 item.barMaxWidth = 20
item.itemStyle = Object.assign( item.itemStyle = Object.assign(
{ {

View File

@@ -6,29 +6,40 @@
</div> </div>
<el-tabs type="border-card"> <el-tabs type="border-card">
<el-tab-pane label="暂态波形上送" :style="'height:' + vhh"> <el-tab-pane label="暂态波形上送" :style="'height:' + vhh">
<el-table stripe :data="Data" :height="height" border style="width: 100%" <vxe-table
header-cell-class-name="table_header"> stripe
<el-table-column align="center" prop="number" label="事件段"></el-table-column> :data="Data"
<el-table-column align="center" prop="number" label="波形起始点相位(°)"> :height="height"
<el-table-column align="center" prop="number" label="A相"></el-table-column> border
<el-table-column align="center" prop="number" label="B相"></el-table-column> style="width: 100%"
<el-table-column align="center" prop="number" label="C相"></el-table-column> header-cell-class-name="table_header"
</el-table-column> >
<el-table-column align="center" prop="number" label="跳变段电压变化率(V/ms)"> <vxe-column align="center" field="number" title="事件段"></vxe-column>
<el-table-column align="center" prop="number" label="A相"></el-table-column> <vxe-colgroup align="center" field="number1" title="波形起始点相位(°)">
<el-table-column align="center" prop="number" label="B相"></el-table-column> <vxe-column align="center" field="number2" title="A相"></vxe-column>
<el-table-column align="center" prop="number" label="C相"></el-table-column> <vxe-column align="center" field="number3" title="B相"></vxe-column>
</el-table-column> <vxe-column align="center" field="number4" title="C相"></vxe-column>
<el-table-column align="center" prop="number" label="相位跳变(°)"> </vxe-colgroup>
<el-table-column align="center" prop="number" label="A相"></el-table-column> <vxe-colgroup align="center" field="number" title="跳变段电压变化率(V/ms)">
<el-table-column align="center" prop="number" label="B相"></el-table-column> <vxe-column align="center" field="number5" title="A相"></vxe-column>
<el-table-column align="center" prop="number" label="C相"></el-table-column> <vxe-column align="center" field="number6" title="B相"></vxe-column>
</el-table-column> <vxe-column align="center" field="number7" title="C相"></vxe-column>
<el-table-column align="center" prop="number" label="总分段数目"></el-table-column> </vxe-colgroup>
<el-table-column align="center" prop="number" label="三相电压不平衡度(%)" width="180"></el-table-column> <vxe-colgroup align="center" field="number" title="相位跳变(°)">
<el-table-column align="center" prop="number" label="触发类型"></el-table-column> <vxe-column align="center" field="number8" title="A相"></vxe-column>
<el-table-column align="center" prop="number" label="暂降原因"></el-table-column> <vxe-column align="center" field="number9" title="B相"></vxe-column>
</el-table> <vxe-column align="center" field="number10" title="C相"></vxe-column>
</vxe-colgroup>
<vxe-column align="center" field="number11" title="总分段数目"></vxe-column>
<vxe-column
align="center"
field="number12"
title="三相电压不平衡度(%)"
width="180"
></vxe-column>
<vxe-column align="center" field="number13" title="触发类型"></vxe-column>
<vxe-column align="center" field="number14" title="暂降原因"></vxe-column>
</vxe-table>
</el-tab-pane> </el-tab-pane>
</el-tabs> </el-tabs>
</div> </div>
@@ -73,7 +84,25 @@ export default {
subName: '', subName: '',
waveDatas: [], waveDatas: [],
Data: [], Data: [
{
number: 1,
number1: 0,
number2: 0,
number3: 0,
number4: 0,
number5: 0,
number6: 0,
number7: 0,
number8: 0,
number9: 0,
number10: 0,
number11: 1,
number12: 0,
number13: '其他',
number14: '其他'
}
],
height: null, height: null,
vhh: null, vhh: null,
ptpass: '', ptpass: '',
@@ -88,7 +117,7 @@ export default {
zoom: '' zoom: ''
} }
}, },
created() { }, created() {},
watch: { watch: {
value: function (a, b) { value: function (a, b) {
if (a == 2) { if (a == 2) {
@@ -116,10 +145,10 @@ export default {
this.zoom = 1 / document.body.style.zoom this.zoom = 1 / document.body.style.zoom
if (this.flag) { if (this.flag) {
// console.log(123); // console.log(123);
this.vh = mainHeight(250).height this.vh = mainHeight(280).height
} else { } else {
// console.log(3333); // console.log(3333);
this.vh = mainHeight(270).height this.vh = mainHeight(305).height
} }
}, },
query() { query() {
@@ -197,6 +226,8 @@ export default {
'#FF9999' '#FF9999'
] ]
} }
console.log('🚀 ~ this.boxoList:', this.boxoList)
var option = { var option = {
tooltip: { tooltip: {
top: '10px', top: '10px',
@@ -239,7 +270,7 @@ export default {
title: { title: {
left: 'center', left: 'center',
text: '发生时刻:' + this.boxoList.startTime + ' PT变化:' + this.boxoList.measurementPointName, text: '发生时刻:' + this.boxoList.startTime + ' PT变化:' + this.boxoList.pt,
textStyle: { textStyle: {
fontSize: 16, fontSize: 16,
color: _this.DColor ? '#fff' : echartsColor.WordColor color: _this.DColor ? '#fff' : echartsColor.WordColor
@@ -378,6 +409,33 @@ export default {
} }
], ],
series: [ series: [
{
name: '跳变期',
type: 'line',
data: [],
showSymbol: true, // 强制显示标记
symbol: 'rect', // 标记形状为方块
symbolSize: 10, // 方块大小
itemStyle: {
color: '#888888', // 方块颜色为灰色
borderWidth: 0
},
markArea: {
silent: true, // 不响应交互
itemStyle: { color: '#ccc' },
data: [
[
{ xAxis: '0' }, // 第一个跳变期起始
{ xAxis: '5' } // 第一个跳变期结束
],
[
{ xAxis: '210' }, // 第二个跳变期起始
{ xAxis: '220' } // 第二个跳变期结束
]
]
}
},
{ {
name: 'A相', name: 'A相',
type: 'line', type: 'line',

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,322 @@
// 辅助函数
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, requestId } = 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.engineeringName == undefined ? '' : ' 项目名称:' + boxoList.engineeringName) +
' 监测点名称:' +
boxoList.equipmentName +
' 发生时刻:' +
boxoList.startTime +
' 暂降(骤升)幅值:' +
boxoList.evtParamVVaDepth +
'% 持续时间:' +
boxoList.evtParamTm +
's'
} else if (boxoList.systemType == 'YPT') {
titles =
(boxoList.engineeringName == undefined ? '' : ' 项目名称:' + boxoList.engineeringName) +
' 监测点名称:' +
boxoList.lineName +
' 发生时刻:' +
boxoList.startTime +
' 暂降(骤升)幅值:' +
(boxoList.featureAmplitude * 100).toFixed(2) +
'% 持续时间:' +
boxoList.persistTime +
's'
} else {
titles =
' 变电站名称:' +
boxoList.subName +
' 监测点名称:' +
boxoList.lineName +
' 发生时刻:' +
boxoList.startTime +
' 暂降(骤升)幅值:' +
(boxoList.featureAmplitude * 100).toFixed(2) +
'% 持续时间:' +
boxoList.duration +
's'
}
// 发送处理结果回主线程
self.postMessage({
requestId,
titles: titles,
success: true,
waveDatas,
time: wp.time,
type: wp.waveType,
severity: wp.yzd,
iphasic: iphasicValue
})
} catch (error) {
self.postMessage({
requestId,
success: false,
error: error.message
})
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,207 @@
// waveData.worker.js
self.addEventListener('message', function (e) {
const { wp, value, iphasic, isOpen, boxoList, requestId } = 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.engineeringName == undefined ? '' : ' 项目名称:' + boxoList.engineeringName) +
' 监测点名称:' +
boxoList.equipmentName +
' 发生时刻:' +
boxoList.startTime +
' 暂降(骤升)幅值:' +
boxoList.evtParamVVaDepth +
'% 持续时间:' +
boxoList.evtParamTm +
's'
} else if (boxoList.systemType == 'YPT') {
titles =
(boxoList.engineeringName == undefined ? '' : ' 项目名称:' + boxoList.engineeringName) +
' 监测点名称:' +
boxoList.lineName +
' 发生时刻:' +
boxoList.startTime +
' 暂降(骤升)幅值:' +
(boxoList.featureAmplitude * 100).toFixed(2) +
'% 持续时间:' +
boxoList.persistTime +
'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({
requestId,
success: true,
waveDatas,
time,
type,
severity,
titles,
iphasic: iphasicValue
})
})

File diff suppressed because it is too large Load Diff

View File

@@ -2,7 +2,7 @@
<div v-if="view2"> <div v-if="view2">
<el-row> <el-row>
<el-col :span="12"> <el-col :span="12">
<span style="font-size: 14px; line-height: 30px">值类型选择:</span> <!-- <span style="font-size: 14px; line-height: 30px">值类型选择:</span>
<el-select <el-select
style="min-width: 200px; width: 200px" style="min-width: 200px; width: 200px"
@change="changeView" @change="changeView"
@@ -15,52 +15,31 @@
:label="item.label" :label="item.label"
:value="item.value" :value="item.value"
></el-option> ></el-option>
</el-select> </el-select> -->
<!-- <el-button v-if="view2 && senior" class="ml10" type="primary" <el-radio-group v-model.trim="value" @change="changeView">
@click="AdvancedAnalytics">高级分析</el-button> --> <el-radio-button label="一次值" :value="1" />
<el-radio-button label="二次值" :value="2" />
</el-radio-group>
<!-- <el-button v-if="view2 && senior" class="ml10" type="primary" @click="AdvancedAnalytics">
高级分析
</el-button> -->
</el-col> </el-col>
<el-col :span="12"> <el-col :span="12">
<el-button <el-button @click="backbxlb" icon="el-icon-Back" style="float: right">返回</el-button>
@click="backbxlb"
class="el-icon-refresh-right"
icon="el-icon-Back"
style="float: right"
>
返回
</el-button>
</el-col> </el-col>
</el-row> </el-row>
<div v-loading="loading" style="height: calc(100vh - 190px)"> <div v-loading="loading" style="height: calc(100vh - 190px)" class="mt10">
<el-tabs v-if="view4" class="default-main" v-model="bxactiveName" @tab-click="bxhandleClick"> <el-tabs v-if="view4" type="border-card" v-model="bxactiveName"
<el-tab-pane @tab-click="bxhandleClick">
label="瞬时波形" <el-tab-pane label="瞬时波形" name="ssbx" class="boxbx pt10 pb10"
name="ssbx" :style="'height:' + bxecharts + ';overflow-y: scroll;'">
class="boxbx pt10 pb10" <shushiboxi ref="shushiboxiRef" v-if="bxactiveName == 'ssbx' && showBoxi" :value="value"
:style="'height:' + bxecharts + ';overflow-y: scroll;'" :parentHeight="parentHeight" :boxoList="boxoList" :wp="wp"></shushiboxi>
>
<shushiboxi
ref="shushiboxiRef"
v-if="bxactiveName == 'ssbx' && showBoxi"
:value="value"
:parentHeight="parentHeight"
:boxoList="boxoList"
:wp="wp"
></shushiboxi>
</el-tab-pane> </el-tab-pane>
<el-tab-pane <el-tab-pane label="RMS波形" class="boxbx pt10 pb10" name="rmsbx"
label="RMS波形" :style="'height:' + bxecharts + ';overflow-y: scroll;'">
class="boxbx pt10 pb10" <rmsboxi ref="rmsboxiRef" v-if="bxactiveName == 'rmsbx' && showBoxi" :value="value"
name="rmsbx" :parentHeight="parentHeight" :boxoList="boxoList" :wp="wp"></rmsboxi>
:style="'height:' + bxecharts + ';overflow-y: scroll;'"
>
<rmsboxi
ref="rmsboxiRef"
v-if="bxactiveName == 'rmsbx' && showBoxi"
:value="value"
:parentHeight="parentHeight"
:boxoList="boxoList"
:wp="wp"
></rmsboxi>
</el-tab-pane> </el-tab-pane>
</el-tabs> </el-tabs>
<el-empty v-else description="暂无数据" style="height: calc(100vh - 190px)" /> <el-empty v-else description="暂无数据" style="height: calc(100vh - 190px)" />
@@ -76,10 +55,12 @@
import shushiboxi from '@/components/echarts/shushiboxi.vue' import shushiboxi from '@/components/echarts/shushiboxi.vue'
import rmsboxi from '@/components/echarts/rmsboxi.vue' import rmsboxi from '@/components/echarts/rmsboxi.vue'
import analytics from '@/components/echarts/analytics.vue' import analytics from '@/components/echarts/analytics.vue'
import { ref, reactive } from 'vue' import { ref, shallowRef } from 'vue'
import { analysis } from '@/api/advance-boot/analyse' import { analysis } from '@/api/advance-boot/analyse'
import { mainHeight } from '@/utils/layout' import { mainHeight } from '@/utils/layout'
import { getMonitorEventAnalyseWave, downloadWaveFile } from '@/api/event-boot/transient' import { getMonitorEventAnalyseWave, downloadWaveFile } from '@/api/event-boot/transient'
import { buildWaveStoreKey, registerWaveRaw, buildSlimWp, releaseWaveRaw } from '@/utils/waveRawStore'
import { clearWaveCache } from '@/utils/waveCache'
const emit = defineEmits(['backbxlb']) const emit = defineEmits(['backbxlb'])
interface Props { interface Props {
// boxoList: any // boxoList: any
@@ -106,14 +87,14 @@ const options = ref([
} }
]) ])
const shushiboxiRef = ref() const shushiboxiRef = ref()
const bxecharts = ref(mainHeight(95).height as any) const bxecharts = ref(mainHeight(145).height as any)
const view2 = ref(true) const view2 = ref(true)
const boxoList = ref(null) const boxoList: any = ref(null)
const wp = ref(null) const wp = shallowRef<any>(null)
const showBoxi = ref(true) const showBoxi = ref(true)
const view3 = ref(false) const view3 = ref(false)
const view4 = ref(false) const view4 = ref(false)
const GJList = ref([]) const GJList = ref({})
const open = async (row: any) => { const open = async (row: any) => {
loading.value = true loading.value = true
@@ -122,19 +103,20 @@ const open = async (row: any) => {
row.loading = false row.loading = false
if (res != undefined) { if (res != undefined) {
boxoList.value = row boxoList.value = row
wp.value = res.data boxoList.value.pt = res.data.pt
const storeKey = buildWaveStoreKey(row)
registerWaveRaw(storeKey, res.data)
wp.value = buildSlimWp(res.data, storeKey)
loading.value = false loading.value = false
view4.value = true view4.value = true
} }
}) })
.catch(() => { .catch(() => {
loading.value = false loading.value = false
backbxlb()
}) })
} }
const bxhandleClick = (tab: any) => { const bxhandleClick = (tab: any) => {
if (shushiboxiRef.value) shushiboxiRef.value.backbxlb()
if (rmsboxiRef.value) rmsboxiRef.value.backbxlb()
loading.value = true loading.value = true
if (tab.name == 'ssbx') { if (tab.name == 'ssbx') {
bxactiveName.value = 'ssbx' bxactiveName.value = 'ssbx'
@@ -147,10 +129,12 @@ const bxhandleClick = (tab: any) => {
// console.log(tab, event); // console.log(tab, event);
} }
const backbxlb = () => { const backbxlb = () => {
const storeKey = wp.value?.waveStoreKey
releaseWaveRaw(storeKey, 'all')
clearWaveCache()
boxoList.value = null boxoList.value = null
wp.value = null wp.value = null
if (shushiboxiRef.value) shushiboxiRef.value.backbxlb()
if (rmsboxiRef.value) rmsboxiRef.value.backbxlb()
emit('backbxlb') emit('backbxlb')
} }
@@ -168,14 +152,17 @@ const AdvancedAnalytics = () => {
analysis({ analysis({
eventIndex: boxoList.value.eventId eventIndex: boxoList.value.eventId
}).then(res => { }).then(res => {
GJList.value = res.data // GJList.value = res.data
view3.value = true // view3.value = true
view2.value = false // view2.value = false
}) })
GJList.value = {}
view3.value = true
view2.value = false
} }
const changeView = () => { const changeView = () => {
if (shushiboxiRef.value) shushiboxiRef.value.backbxlb() shushiboxiRef.value?.backbxlb?.()
if (rmsboxiRef.value) rmsboxiRef.value.backbxlb() rmsboxiRef.value?.backbxlb?.()
showBoxi.value = false showBoxi.value = false
setTimeout(() => { setTimeout(() => {
showBoxi.value = true showBoxi.value = true

View File

@@ -1,22 +1,22 @@
<template> <template>
<div style="width: 540px"> <div :style="{ width: isMinuteData ? '750px' : '620px' }">
<el-select v-model="interval" style="min-width: 90px; width: 90px; margin-right: 10px" @change="timeChange"> <el-select v-model="interval" style="min-width: 90px; width: 90px; margin-right: 10px" @change="timeChange" filterable>
<el-option v-for="item in timeOptions" :key="item.value" :label="item.label" :value="item.value" /> <el-option v-for="item in timeOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select> </el-select>
<el-date-picker <el-date-picker
v-model="timeValue" v-model="timeValue"
type="daterange" :type="pickerType"
:disabled="disabledPicker" :disabled="disabledPicker"
:disabled-date="isFutureDate" :disabled-date="isFutureDate"
style="width: 220px; margin-right: 10px" :style="{ width: isMinuteData ? '360px' : '220px', marginRight: '10px' }"
unlink-panels unlink-panels
:clearable="false" :clearable="false"
range-separator="" range-separator=""
start-placeholder="开始日期" :start-placeholder="isMinuteData ? '开始时间' : '开始日期'"
end-placeholder="结束日期" :end-placeholder="isMinuteData ? '结束时间' : '结束日期'"
value-format="YYYY-MM-DD" :value-format="pickerValueFormat"
:shortcuts="shortcuts" :shortcuts="isMinuteData ? undefined : shortcuts"
/> />
<el-button :disabled="backDisabled" type="primary" :icon="DArrowLeft" @click="preClick"></el-button> <el-button :disabled="backDisabled" type="primary" :icon="DArrowLeft" @click="preClick"></el-button>
<el-button type="primary" :icon="VideoPause" @click="nowTime">当前</el-button> <el-button type="primary" :icon="VideoPause" @click="nowTime">当前</el-button>
@@ -26,18 +26,41 @@
<script lang="ts" setup> <script lang="ts" setup>
import { DArrowLeft, VideoPause, DArrowRight } from '@element-plus/icons-vue' import { DArrowLeft, VideoPause, DArrowRight } from '@element-plus/icons-vue'
import { ref, onMounted, nextTick, watch } from 'vue' import { ref, onMounted, computed, watch } from 'vue'
interface Props { interface Props {
nextFlag?: boolean nextFlag?: boolean
theCurrentTime?: boolean theCurrentTime?: boolean
isStatisticData?: number | string
} }
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
nextFlag: false, nextFlag: false,
theCurrentTime: false theCurrentTime: false,
isStatisticData: 0
}) })
const isMinuteData = computed(() => Number(props.isStatisticData) === 1)
const pickerType = computed(() => (isMinuteData.value ? 'datetimerange' : 'daterange'))
const pickerValueFormat = computed(() => (isMinuteData.value ? 'YYYY-MM-DD HH:mm:ss' : 'YYYY-MM-DD'))
const toDatePart = (value: string) => (value?.includes(' ') ? value.split(' ')[0] : value)
const toMinuteStart = (date: string) => `${toDatePart(date)} 00:00:00`
const toMinuteEnd = (date: string) => `${toDatePart(date)} 23:59:59`
const normalizeRange = (start: string, end: string) => {
if (!isMinuteData.value) {
return [toDatePart(start), toDatePart(end)]
}
return [toMinuteStart(start), toMinuteEnd(end)]
}
const setRangeValue = (start: string, end: string) => {
timeValue.value = normalizeRange(start, end)
}
const interval = ref(3) const interval = ref(3)
const timeFlag = ref(1) const timeFlag = ref(1)
const count = ref(0) const count = ref(0)
@@ -95,13 +118,13 @@ const timeChange = (e: number) => {
if (e == 1) { if (e == 1) {
disabledPicker.value = true disabledPicker.value = true
timeValue.value = [setTime(1), setTime()] setRangeValue(setTime(1), setTime())
} else if (e == 2) { } else if (e == 2) {
disabledPicker.value = true disabledPicker.value = true
timeValue.value = [setTime(2), setTime()] setRangeValue(setTime(2), setTime())
} else if (e == 3) { } else if (e == 3) {
disabledPicker.value = true disabledPicker.value = true
timeValue.value = [setTime(3), setTime()] setRangeValue(setTime(3), setTime())
} else if (e == 4) { } else if (e == 4) {
let year = parseInt(setTime().substring(0, 4)) let year = parseInt(setTime().substring(0, 4))
let month = parseInt(setTime().substring(5, 7)) let month = parseInt(setTime().substring(5, 7))
@@ -111,12 +134,12 @@ const timeChange = (e: number) => {
var dayOfWeek = start.getDay() == 0 ? 7 : start.getDay() - 1 // 如果为周日则置为7天 var dayOfWeek = start.getDay() == 0 ? 7 : start.getDay() - 1 // 如果为周日则置为7天
disabledPicker.value = true disabledPicker.value = true
timeValue.value = [setTime(0, dayOfWeek), setTime(0, -6 + dayOfWeek)] setRangeValue(setTime(0, dayOfWeek), setTime(0, -6 + dayOfWeek))
} else if (e == 5) { } else if (e == 5) {
disabledPicker.value = false disabledPicker.value = false
backDisabled.value = true backDisabled.value = true
preDisabled.value = props.nextFlag ? false : true preDisabled.value = props.nextFlag ? false : true
timeValue.value = [setTime(), setTime()] setRangeValue(setTime(), setTime())
} }
if (e == 1 || e == 2) { if (e == 1 || e == 2) {
timeFlag.value = 0 timeFlag.value = 0
@@ -186,7 +209,7 @@ const preClick = () => {
startTime = year + '-01-01' startTime = year + '-01-01'
endTime = year + '-12-31' endTime = year + '-12-31'
} }
timeValue.value = [startTime, endTime] setRangeValue(startTime, endTime)
// 判断向后键的状态 // 判断向后键的状态
// var temp = NowgetEndTime() // var temp = NowgetEndTime()
@@ -415,16 +438,27 @@ const next = () => {
} }
if (!props.nextFlag) { if (!props.nextFlag) {
if ( if (
new Date(endTime + ' 00:00:00').getTime() >= new Date(toDatePart(endTime) + ' 00:00:00').getTime() >=
new Date(window.XEUtils.toDateString(new Date(), 'yyyy-MM-dd ') + ' 00:00:00').getTime() new Date(window.XEUtils.toDateString(new Date(), 'yyyy-MM-dd ') + ' 00:00:00').getTime()
) { ) {
preDisabled.value = props.nextFlag ? false : true preDisabled.value = props.nextFlag ? false : true
} }
} }
timeValue.value = [startTime, endTime] setRangeValue(startTime, endTime)
} }
watch(
() => props.isStatisticData,
() => {
if (timeValue.value?.length === 2) {
setRangeValue(timeValue.value[0], timeValue.value[1])
} else {
timeChange(interval.value)
}
}
)
const setTime = (flag = 0, e = 0) => { const setTime = (flag = 0, e = 0) => {
let dd = window.XEUtils.toDateString(new Date().getTime() - e * 3600 * 1000 * 24, 'dd') let dd = window.XEUtils.toDateString(new Date().getTime() - e * 3600 * 1000 * 24, 'dd')

View File

@@ -10,7 +10,7 @@ export default defineComponent({
required: true required: true
}, },
size: { size: {
type: String, type: [String, Number],
default: '18px' default: '18px'
}, },
color: { color: {
@@ -21,7 +21,7 @@ export default defineComponent({
setup(props) { setup(props) {
const iconStyle = computed((): CSSProperties => { const iconStyle = computed((): CSSProperties => {
const { size, color } = props const { size, color } = props
let s = `${size.replace('px', '')}px` const s = `${String(size).replace('px', '')}px`
return { return {
fontSize: s, fontSize: s,
color: color color: color

View File

@@ -10,7 +10,7 @@ import { computed, type CSSProperties } from 'vue'
import { isExternal } from '@/utils/common' import { isExternal } from '@/utils/common'
interface Props { interface Props {
name: string name: string
size: string size: string | number
color: string color: string
} }
@@ -20,7 +20,7 @@ const props = withDefaults(defineProps<Props>(), {
color: '#000000', color: '#000000',
}) })
const s = `${props.size.replace('px', '')}px` const s = `${String(props.size).replace('px', '')}px`
const iconName = computed(() => `#${props.name}`) const iconName = computed(() => `#${props.name}`)
const iconStyle = computed((): CSSProperties => { const iconStyle = computed((): CSSProperties => {
return { return {

View File

@@ -1,8 +1,22 @@
<script lang='ts'> <script lang="ts">
import { defineComponent, createVNode, reactive } from 'vue' import { defineComponent, createVNode, reactive } from 'vue'
import { Column } from 'vxe-table' import { Column } from 'vxe-table'
import { uuid } from '@/utils/random' import { uuid } from '@/utils/random'
/** storage 开启时列必须有稳定 field序号/操作等无 field 列按 title 生成 */
function resolveColumnField(attr: Record<string, any>): string | undefined {
if (attr.field) return attr.field
if (attr.prop) return attr.prop
// checkbox / radio / expand / seq 等类型列由 vxe 内部处理,可不强制 field
if (attr.type && ['checkbox', 'radio', 'expand', 'html'].includes(attr.type)) {
return undefined
}
if (attr.title != null && attr.title !== '') {
return `__col_${attr.title}`
}
return `col_${uuid()}`
}
export default defineComponent({ export default defineComponent({
name: 'Column', name: 'Column',
props: { props: {
@@ -12,9 +26,13 @@ export default defineComponent({
} }
}, },
setup(props, { slots }) { setup(props, { slots }) {
const attr = reactive(props.attr) const attr = reactive({ ...props.attr })
attr['align'] = attr['align'] ? attr['align'] : 'center' attr['align'] = attr['align'] ? attr['align'] : 'center'
attr['column-key'] = attr['column-key'] ? attr['column-key'] : attr.prop || uuid() const field = resolveColumnField(attr)
if (field) {
attr.field = field
}
attr['column-key'] = attr['column-key'] ? attr['column-key'] : attr.field || attr.prop || uuid()
return () => { return () => {
return createVNode(Column, attr, slots.default) return createVNode(Column, attr, slots.default)
} }

View File

@@ -6,14 +6,20 @@ export const defaultAttribute: VxeTableProps = {
border: true, border: true,
stripe: true, stripe: true,
size: 'small', size: 'small',
columnConfig: { resizable: true }, columnConfig: { resizable: true, useKey: true },
rowConfig: { isCurrent: true, isHover: true }, rowConfig: { isCurrent: true, isHover: true },
scrollX: { scrollToLeftOnChange: true }, scrollX: { scrollToLeftOnChange: true },
scrollY: { scrollToTopOnChange: true, enabled: true }, scrollY: { enabled: false },
treeConfig: { tooltipConfig: { maxHeight: 150 },
reserve: true // 注意:全局不要默认开启 treeConfig会与 stripe 冲突;树表在页面自行配置
customConfig: {
enabled: true,
allowFixed: true,
storage: false,
showFooter: false,
immediate: true,
mode: 'default'
}, },
showOverflow: true, showOverflow: 'tooltip',
showFooter:true showHeaderOverflow: false
} }

View File

@@ -9,7 +9,11 @@
label-position="left" label-position="left"
:inline="true" :inline="true"
> >
<el-form-item v-if="datePicker" style="grid-column: span 2; max-width: 630px"> <el-form-item
v-if="datePicker"
style="grid-column: span 2"
:style="{ maxWidth: isStatisticData == 1 ? '750px' : '620px' }"
>
<template #label> <template #label>
<el-checkbox v-if="showTimeAll" v-model="timeAll" label="统计时间" /> <el-checkbox v-if="showTimeAll" v-model="timeAll" label="统计时间" />
<span v-else>{{ dateLabel }}</span> <span v-else>{{ dateLabel }}</span>
@@ -19,11 +23,17 @@
v-if="timeAll" v-if="timeAll"
:nextFlag="nextFlag" :nextFlag="nextFlag"
:theCurrentTime="theCurrentTime" :theCurrentTime="theCurrentTime"
:isStatisticData="isStatisticData"
></DatePicker> ></DatePicker>
</el-form-item> </el-form-item>
<el-form-item label="区域" v-if="area"> <el-form-item label="区域" v-if="area">
<Area ref="areaRef" v-model="tableStore.table.params.deptIndex" @change-value="onAreaChange" /> <Area
ref="areaRef"
v-model="tableStore.table.params.deptIndex"
@change-value="onAreaChange"
style="width: 200px"
/>
</el-form-item> </el-form-item>
<slot name="select"></slot> <slot name="select"></slot>
</el-form> </el-form>
@@ -60,6 +70,15 @@
</el-button> </el-button>
</template> </template>
<slot name="operation"></slot> <slot name="operation"></slot>
<el-button
class="ml10"
v-if="showCustomColumn"
:icon="Setting"
@mousedown="onCustomColumnMousedown"
@click="openCustomColumn"
>
列设置
</el-button>
</div> </div>
<el-form <el-form
:style="showSelect && showUnfoldButton ? headerFormSecondStyleOpen : headerFormSecondStyleClose" :style="showSelect && showUnfoldButton ? headerFormSecondStyleOpen : headerFormSecondStyleClose"
@@ -79,9 +98,8 @@ import DatePicker from '@/components/form/datePicker/index.vue'
import Area from '@/components/form/area/index.vue' import Area from '@/components/form/area/index.vue'
import { mainHeight } from '@/utils/layout' import { mainHeight } from '@/utils/layout'
import { useDictData } from '@/stores/dictData' import { useDictData } from '@/stores/dictData'
import { Search, RefreshLeft } from '@element-plus/icons-vue' import { Search, RefreshLeft, Setting } from '@element-plus/icons-vue'
import { defineProps } from 'vue' const emit = defineEmits(['selectChange', 'areaChange'])
const emit = defineEmits(['selectChange','areaChange'])
const tableStore = inject('tableStore') as TableStore const tableStore = inject('tableStore') as TableStore
const tableHeader = ref() const tableHeader = ref()
const datePickerRef = ref() const datePickerRef = ref()
@@ -100,6 +118,8 @@ interface Props {
showExport?: boolean //导出控制 showExport?: boolean //导出控制
showTimeAll?: boolean //控制时间是否显示 showTimeAll?: boolean //控制时间是否显示
dateLabel?: string //设置时间名称 dateLabel?: string //设置时间名称
isStatisticData?: number | string // 0统计数据 1分钟数据
showCustomColumn?: boolean // 列设置
} }
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
@@ -111,7 +131,9 @@ const props = withDefaults(defineProps<Props>(), {
showReset: true, showReset: true,
showExport: false, showExport: false,
showTimeAll: false, showTimeAll: false,
dateLabel: '统计日期' dateLabel: '统计日期',
isStatisticData: 0,
showCustomColumn: false
}) })
// 动态计算table高度 // 动态计算table高度
const resizeObserver = new ResizeObserver(entries => { const resizeObserver = new ResizeObserver(entries => {
@@ -132,9 +154,8 @@ const headerFormSecondStyleClose = {
padding: '0' padding: '0'
} }
const onAreaChange = (data) => { const onAreaChange = data => {
emit('areaChange', { label: data.label })
emit('areaChange', {label: data.label})
} }
watch( watch(
@@ -142,7 +163,6 @@ watch(
newVal => { newVal => {
setTimeout(() => { setTimeout(() => {
areaRef.value && areaRef.value.change() areaRef.value && areaRef.value.change()
}, 0) }, 0)
} }
) )
@@ -155,7 +175,31 @@ watch(
}, 500) }, 500)
} }
) )
let customColumnVisibleOnPress = false
const onCustomColumnMousedown = () => {
const table = tableStore?.table?.ref as any
if (!table) return
customColumnVisibleOnPress = table.getCustomVisible?.() ?? !!table.reactData?.customStore?.visible
if (table.reactData?.customStore) {
table.reactData.customStore.activeBtn = true
}
}
const openCustomColumn = () => {
const table = tableStore?.table?.ref as any
if (!table) return
if (table.reactData?.customStore) {
table.reactData.customStore.activeBtn = false
}
if (customColumnVisibleOnPress) {
table?.closeCustom?.()
} else {
table?.openCustom?.()
}
}
onMounted(() => { onMounted(() => {
if (props.showCustomColumn && tableStore?.table) {
tableStore.table.customColumnInHeader = true
}
timeAll.value = props.showTimeAll ? false : true timeAll.value = props.showTimeAll ? false : true
if (props.datePicker && timeAll.value) { if (props.datePicker && timeAll.value) {
tableStore.table.params.searchBeginTime = datePickerRef.value.timeValue[0] tableStore.table.params.searchBeginTime = datePickerRef.value.timeValue[0]
@@ -238,14 +282,14 @@ const onResetForm = () => {
//时间重置成默认值 //时间重置成默认值
datePickerRef.value?.setTheDate(3) datePickerRef.value?.setTheDate(3)
if(props.showTimeAll){ if (props.showTimeAll) {
timeAll.value = false timeAll.value = false
delete tableStore.table.params.searchBeginTime delete tableStore.table.params.searchBeginTime
delete tableStore.table.params.searchEndTime delete tableStore.table.params.searchEndTime
delete tableStore.table.params.startTime delete tableStore.table.params.startTime
delete tableStore.table.params.endTime delete tableStore.table.params.endTime
delete tableStore.table.params.timeFlag delete tableStore.table.params.timeFlag
delete tableStore.table.params.interval delete tableStore.table.params.interval
} }
if (props.datePicker && timeAll.value) { if (props.datePicker && timeAll.value) {
@@ -264,7 +308,6 @@ const setTheDate = (val: any) => {
} }
// 导出 // 导出
const onExport = () => { const onExport = () => {
tableStore.onTableAction('export', { showAllFlag: true }) tableStore.onTableAction('export', { showAllFlag: true })
} }
@@ -300,6 +343,7 @@ defineExpose({
padding: 13px 15px; padding: 13px 15px;
font-size: 14px; font-size: 14px;
overflow: hidden; overflow: hidden;
.table-header-operate-text { .table-header-operate-text {
margin-left: 6px; margin-left: 6px;
} }

View File

@@ -1,24 +1,49 @@
<template> <template>
<div :style="{ height: typeof props.height === 'string' ? props.height : tableStore.table.height }"> <div :style="{ height: typeof props.height === 'string' ? props.height : tableStore.table.height }">
<vxe-table ref="tableRef" height="auto" :key="key" :data="tableStore.table.data" <div v-if="showCustomColumn && !tableStore?.table?.customColumnInHeader" class="table-custom-toolbar">
v-loading="tableStore.table.loading" v-bind="Object.assign({}, defaultAttribute, $attrs)" <el-button icon="el-icon-Setting" @mousedown="onCustomColumnMousedown" @click="openCustomColumn">
@checkbox-all="selectChangeEvent" @checkbox-change="selectChangeEvent" :showOverflow="showOverflow" 列设置
@sort-change="handleSortChange"> </el-button>
</div>
<vxe-table
ref="tableRef"
height="auto"
:key="key"
:data="tableStore.table.data"
v-loading="tableStore.table.loading"
v-bind="tableBindProps"
:id="getTableId()"
@checkbox-all="selectChangeEvent"
@checkbox-change="selectChangeEvent"
@cell-click="onCellClick"
>
<!-- @sort-change="handleSortChange" -->
<!-- Column 组件内部是 el-table-column --> <!-- Column 组件内部是 el-table-column -->
<template v-if="isGroup"> <template v-if="isGroup">
<GroupColumn :column="tableStore.table.column" /> <GroupColumn :column="tableStore.table.column" />
</template> </template>
<template v-else> <template v-else>
<Column :attr="item" :key="key + '-column'" v-for="(item, key) in tableStore.table.column" <Column
:tree-node="item.treeNode"> :attr="item"
:key="key + '-column'"
v-for="(item, key) in tableStore.table.column"
:tree-node="item.treeNode"
>
<!-- tableStore 预设的列 render 方案 --> <!-- tableStore 预设的列 render 方案 -->
<template v-if="item.render" #default="scope"> <template v-if="item.render" #default="scope">
<FieldRender :field="item" :row="scope.row" :column="scope.column" :index="scope.rowIndex" :key="key + <FieldRender
'-' + :field="item"
item.render + :row="scope.row"
'-' + :column="scope.column"
(item.field ? '-' + item.field + '-' + scope.row[item.field] : '') :index="scope.rowIndex"
" /> :key="
key +
'-' +
item.render +
'-' +
(item.field ? '-' + item.field + '-' + scope.row[item.field] : '')
"
/>
</template> </template>
</Column> </Column>
</template> </template>
@@ -27,17 +52,22 @@
</div> </div>
<div v-if="tableStore.showPage" class="table-pagination"> <div v-if="tableStore.showPage" class="table-pagination">
<el-pagination :currentPage="tableStore.table.params!.pageNum" :page-size="tableStore.table.params!.pageSize" <el-pagination
:page-sizes="pageSizes" background :currentPage="tableStore.table.params!.pageNum"
:page-size="tableStore.table.params!.pageSize"
:page-sizes="pageSizes"
background
:layout="config.layout.shrink ? 'prev, next, jumper' : 'sizes,total, ->, prev, pager, next, jumper'" :layout="config.layout.shrink ? 'prev, next, jumper' : 'sizes,total, ->, prev, pager, next, jumper'"
:total="tableStore.table.total" @size-change="onTableSizeChange" :total="tableStore.table.total"
@current-change="onTableCurrentChange"></el-pagination> @size-change="onTableSizeChange"
@current-change="onTableCurrentChange"
></el-pagination>
</div> </div>
<slot name="footer"></slot> <slot name="footer"></slot>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, nextTick, inject, computed, onMounted, watch } from 'vue' import { ref, nextTick, inject, computed, onMounted, watch, useAttrs } from 'vue'
import type { ElTable } from 'element-plus' import type { ElTable } from 'element-plus'
import { VxeTableEvents, VxeTableInstance } from 'vxe-table' import { VxeTableEvents, VxeTableInstance } from 'vxe-table'
import FieldRender from '@/components/table/fieldRender/index.vue' import FieldRender from '@/components/table/fieldRender/index.vue'
@@ -48,22 +78,49 @@ import type TableStoreClass from '@/utils/tableStore'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import { defaultAttribute } from '@/components/table/defaultAttribute' import { defaultAttribute } from '@/components/table/defaultAttribute'
defineOptions({
inheritAttrs: false
})
const config = useConfig() const config = useConfig()
const tableRef = ref<VxeTableInstance>() const tableRef = ref<VxeTableInstance>()
const tableStore = inject('tableStore') as TableStoreClass const tableStore = inject('tableStore') as TableStoreClass
const router = useRouter() const router = useRouter()
const key = ref(0) const key = ref(0)
const emit = defineEmits<{
(e: 'cell-click', params: any): void
}>()
interface Props extends /* @vue-ignore */ Partial<InstanceType<typeof ElTable>> { interface Props extends /* @vue-ignore */ Partial<InstanceType<typeof ElTable>> {
isGroup?: boolean isGroup?: boolean
showOverflow?: boolean showOverflow?: boolean | 'ellipsis' | 'title' | 'tooltip'
height?: string | number height?: string | number
showCustomColumn?: boolean
} }
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
isGroup: false, isGroup: false,
showOverflow: true, showOverflow: 'tooltip',
height: undefined height: undefined,
showCustomColumn: false
}) })
const attrs = useAttrs()
const tableBindProps = computed(() => {
const enableStorage = props.showCustomColumn || !!tableStore?.table?.customColumnInHeader
const baseCustomConfig = (defaultAttribute.customConfig || {}) as Record<string, any>
const attrsCustomConfig = ((attrs.customConfig as Record<string, any>) || {}) as Record<string, any>
return Object.assign({}, defaultAttribute, attrs, {
showOverflow: props.showOverflow,
showHeaderOverflow: false,
customConfig: {
...baseCustomConfig,
...attrsCustomConfig,
storage: enableStorage ? true : !!attrsCustomConfig.storage
}
})
})
const onCellClick: VxeTableEvents.CellClick<any> = params => {
emit('cell-click', params)
}
onMounted(() => { onMounted(() => {
tableStore.table.ref = tableRef.value as VxeTableInstance tableStore.table.ref = tableRef.value as VxeTableInstance
}) })
@@ -93,10 +150,33 @@ const selectChangeEvent: VxeTableEvents.CheckboxChange<any> = ({ checked }) => {
const records = (tableRef.value as VxeTableInstance).getCheckboxRecords() const records = (tableRef.value as VxeTableInstance).getCheckboxRecords()
tableStore.onTableAction('selection-change', records) tableStore.onTableAction('selection-change', records)
} }
let customColumnVisibleOnPress = false
const onCustomColumnMousedown = () => {
const table = tableRef.value as any
if (!table) return
customColumnVisibleOnPress = table.getCustomVisible?.() ?? !!table.reactData?.customStore?.visible
if (table.reactData?.customStore) {
table.reactData.customStore.activeBtn = true
}
}
const openCustomColumn = () => {
const table = tableRef.value as any
if (!table) return
if (table.reactData?.customStore) {
table.reactData.customStore.activeBtn = false
}
if (customColumnVisibleOnPress) {
table?.closeCustom?.()
} else {
table?.openCustom?.()
}
}
const getRef = () => { const getRef = () => {
return tableRef.value return tableRef.value
} }
const getTableId = () => {
return tableStore.table.filename || document.querySelectorAll('.ba-nav-tab.active')[0].textContent || ''
}
// 排序 // 排序
const handleSortChange = ({ column, order }: { column: TableColumn; order: 'asc' | 'desc' | null }) => { const handleSortChange = ({ column, order }: { column: TableColumn; order: 'asc' | 'desc' | null }) => {
// console.log('排序列:', column?.property); // console.log('排序列:', column?.property);

View File

@@ -3,167 +3,177 @@
<el-tabs type="border-card" v-model="activeName" @tab-click="handleClick"> <el-tabs type="border-card" v-model="activeName" @tab-click="handleClick">
<el-tab-pane label="监测点详情" name="tab1"> <el-tab-pane label="监测点详情" name="tab1">
<el-descriptions :column="3" border label-width="150px"> <el-descriptions :column="3" border label-width="150px">
<el-descriptions-item label="项目工程"> <el-descriptions-item label="项目工程">
{{ details.areaName }} {{ details.areaName }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="项目单位"> <el-descriptions-item label="项目单位">
{{ details.gdName }} {{ details.gdName }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="所属变电站"> <el-descriptions-item label="所属变电站">
{{ details.bdName }} {{ details.bdName }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="电压等级"> <el-descriptions-item label="监测点名称">
{{ details.scale }}
</el-descriptions-item>
<el-descriptions-item label="终端厂家">
{{ details.manufacturer }}
</el-descriptions-item>
<el-descriptions-item label="终端名称">
{{ details.devName }}
</el-descriptions-item>
<el-descriptions-item label="网络参数">
{{ details.ip }}
</el-descriptions-item>
<el-descriptions-item label="监测点名称">
{{ details.lineName }} {{ details.lineName }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="监测点序号"> <el-descriptions-item label="监测点对象名称">
{{ details.id }}
</el-descriptions-item>
<el-descriptions-item label="通讯状态">
{{ details.comFlag }}
</el-descriptions-item>
<el-descriptions-item label="干扰源类型">
{{ details.loadType }}
</el-descriptions-item>
<el-descriptions-item label="监测点对象名称">
{{ details.objName }} {{ details.objName }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="接线方式"> <el-descriptions-item label="监测点序号">
{{ details.id }}
</el-descriptions-item>
<el-descriptions-item label="终端厂家">
{{ details.manufacturer }}
</el-descriptions-item>
<el-descriptions-item label="终端名称">
{{ details.devName }}
</el-descriptions-item>
<el-descriptions-item label="终端型号">
{{ typeArr.find(item => item.id == details.devType)?.name }}
</el-descriptions-item>
<el-descriptions-item label="电压等级">
{{ details.scale }}
</el-descriptions-item>
<el-descriptions-item label="网络参数">
{{ details.ip }}
</el-descriptions-item>
<el-descriptions-item label="通讯状态">
{{ details.comFlag }}
</el-descriptions-item>
<el-descriptions-item label="干扰源类型">
{{ details.loadType }}
</el-descriptions-item>
<el-descriptions-item label="接线方式">
{{ details.ptType }} {{ details.ptType }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="PT变比"> <el-descriptions-item label="统计间隔(分钟)">
{{ details.timeInterval }}
</el-descriptions-item>
<el-descriptions-item label="PT变比">
{{ details.pt }} {{ details.pt }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="CT变比"> <el-descriptions-item label="CT变比">
{{ details.ct }} {{ details.ct }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="基准容量(MVA)"> <el-descriptions-item label="基准容量(MVA)">
{{ details.standardCapacity }} {{ details.standardCapacity }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="最小短路容量(MVA)"> <el-descriptions-item label="供电设备容量(MVA)">
{{ details.shortCapacity }}
</el-descriptions-item>
<el-descriptions-item label="供电设备容量(MVA)">
{{ details.devCapacity }} {{ details.devCapacity }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="用户协议容量(MVA)"> <el-descriptions-item label="最小短路容量(MW)">
{{ details.shortCapacity }}
</el-descriptions-item>
<el-descriptions-item label="用户协议容量(MW)">
{{ details.dealCapacity }} {{ details.dealCapacity }}
</el-descriptions-item> </el-descriptions-item>
</el-descriptions> </el-descriptions>
</el-tab-pane> </el-tab-pane>
<el-tab-pane label="监测点限值" name="tab2"> <el-tab-pane label="监测点限值" name="tab2">
<el-descriptions :column="3" border label-width="250px"> <el-descriptions :column="3" border label-width="250px">
<el-descriptions-item label="电压偏差上限值(%)"> <el-descriptions-item label="电压偏差上限值(%)">
{{ limitValue.voltageDev }} {{ limitValue.voltageDev }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="电压偏差下限值(%)"> <el-descriptions-item label="电压偏差下限值(%)">
{{ limitValue.uvoltageDev }} {{ limitValue.uvoltageDev }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="频率偏差限值(Hz)"> <el-descriptions-item label="频率偏差限值(Hz)">
{{ limitValue.freqDev }} {{ limitValue.freqDev }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="三相电压不平衡度限值(%)"> <el-descriptions-item label="三相电压不平衡度限值(%)">
{{ limitValue.ubalance }} {{ limitValue.ubalance }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="负序电流限值(A)"> <el-descriptions-item label="负序电流限值(A)">
{{ limitValue.ineg }} {{ limitValue.ineg }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="长时闪变限值(%)"> <el-descriptions-item label="长时闪变限值(%)">
{{ limitValue.flicker }} {{ limitValue.flicker }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="电压总畸变率限值(%)"> <el-descriptions-item label="电压总畸变率限值(%)">
{{ limitValue.uaberrance }} {{ limitValue.uaberrance }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="奇次谐波电压含有率限值(%)"> <el-descriptions-item label="奇次谐波电压含有率限值(%)">
{{ limitValue.oddHarm }} {{ limitValue.oddHarm }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="偶次谐波电压含有率限值(%)"> <el-descriptions-item label="偶次谐波电压含有率限值(%)">
{{ limitValue.evenHarm }} {{ limitValue.evenHarm }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="3次谐波电流幅值限值(A)"> <el-descriptions-item label="3次谐波电流幅值限值(A)">
{{ limitValue.iharm3 }} {{ limitValue.iharm3 }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="4次谐波电流幅值限值(A)"> <el-descriptions-item label="4次谐波电流幅值限值(A)">
{{ limitValue.iharm4 }} {{ limitValue.iharm4 }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="5次谐波电流幅值限值(A)"> <el-descriptions-item label="5次谐波电流幅值限值(A)">
{{ limitValue.iharm5 }} {{ limitValue.iharm5 }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="6次谐波电流幅值限值(A)"> <el-descriptions-item label="6次谐波电流幅值限值(A)">
{{ limitValue.iharm6 }} {{ limitValue.iharm6 }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="7次谐波电流幅值限值(A)"> <el-descriptions-item label="7次谐波电流幅值限值(A)">
{{ limitValue.iharm7 }} {{ limitValue.iharm7 }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="8次谐波电流幅值限值(A)"> <el-descriptions-item label="8次谐波电流幅值限值(A)">
{{ limitValue.iharm8 }} {{ limitValue.iharm8 }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="9次谐波电流幅值限值(A)"> <el-descriptions-item label="9次谐波电流幅值限值(A)">
{{ limitValue.iharm9 }} {{ limitValue.iharm9 }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="10次谐波电流幅值限值(A)"> <el-descriptions-item label="10次谐波电流幅值限值(A)">
{{ limitValue.iharm10 }} {{ limitValue.iharm10 }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="11次谐波电流幅值限值(A)"> <el-descriptions-item label="11次谐波电流幅值限值(A)">
{{ limitValue.iharm11 }} {{ limitValue.iharm11 }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="12次谐波电流幅值限值(A)"> <el-descriptions-item label="12次谐波电流幅值限值(A)">
{{ limitValue.iharm12 }} {{ limitValue.iharm12 }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="13次谐波电流幅值限值(A)"> <el-descriptions-item label="13次谐波电流幅值限值(A)">
{{ limitValue.iharm13 }} {{ limitValue.iharm13 }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="14次谐波电流幅值限值(A)"> <el-descriptions-item label="14次谐波电流幅值限值(A)">
{{ limitValue.iharm14 }} {{ limitValue.iharm14 }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="15次谐波电流幅值限值(A)"> <el-descriptions-item label="15次谐波电流幅值限值(A)">
{{ limitValue.iharm15 }} {{ limitValue.iharm15 }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="16次谐波电流幅值限值(A)"> <el-descriptions-item label="16次谐波电流幅值限值(A)">
{{ limitValue.iharm16 }} {{ limitValue.iharm16 }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="17次谐波电流幅值限值(A)"> <el-descriptions-item label="17次谐波电流幅值限值(A)">
{{ limitValue.iharm17 }} {{ limitValue.iharm17 }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="18次谐波电流幅值限值(A)"> <el-descriptions-item label="18次谐波电流幅值限值(A)">
{{ limitValue.iharm18 }} {{ limitValue.iharm18 }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="19次谐波电流幅值限值(A)"> <el-descriptions-item label="19次谐波电流幅值限值(A)">
{{ limitValue.iharm19 }} {{ limitValue.iharm19 }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="20次谐波电流幅值限值(A)"> <el-descriptions-item label="20次谐波电流幅值限值(A)">
{{ limitValue.iharm20 }} {{ limitValue.iharm20 }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="21次谐波电流幅值限值(A)"> <el-descriptions-item label="21次谐波电流幅值限值(A)">
{{ limitValue.iharm21 }} {{ limitValue.iharm21 }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="22次谐波电流幅值限值(A)"> <el-descriptions-item label="22次谐波电流幅值限值(A)">
{{ limitValue.iharm22 }} {{ limitValue.iharm22 }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="23次谐波电流幅值限值(A)"> <el-descriptions-item label="23次谐波电流幅值限值(A)">
{{ limitValue.iharm23 }} {{ limitValue.iharm23 }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="24次谐波电流幅值限值(A)"> <el-descriptions-item label="24次谐波电流幅值限值(A)">
{{ limitValue.iharm24 }} {{ limitValue.iharm24 }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="25次谐波电流幅值限值(A)"> <el-descriptions-item label="25次谐波电流幅值限值(A)">
{{ limitValue.iharm25 }} {{ limitValue.iharm25 }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="0.5-1.5次间谐波电压含有率限值(%)"> <el-descriptions-item label="0.5-1.5次间谐波电压含有率限值(%)">
{{ limitValue.inUharm }} {{ limitValue.inUharm }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="2.5-15.5次谐波电压含有率限值(%)"> <el-descriptions-item label="2.5-15.5次谐波电压含有率限值(%)">
{{ limitValue.inUharm16 }} {{ limitValue.inUharm16 }}
</el-descriptions-item> </el-descriptions-item>
</el-descriptions> </el-descriptions>
@@ -174,11 +184,14 @@
<script lang="ts" setup> <script lang="ts" setup>
import { ref, inject } from 'vue' import { ref, inject } from 'vue'
import { getLineOverLimitData, getLineDetailData } from '@/api/device-boot/line' import { getLineOverLimitData, getLineDetailData } from '@/api/device-boot/line'
import { useDictData } from '@/stores/dictData'
const dialogVisible = ref(false) const dialogVisible = ref(false)
const title = ref('') const title = ref('')
const activeName = ref('tab1') const activeName = ref('tab1')
const details: any = ref([]) const details: any = ref([])
const dictData = useDictData()
const limitValue: any = ref([]) const limitValue: any = ref([])
const typeArr = dictData.getBasicData('Dev_Type')
const open = (data: any) => { const open = (data: any) => {
details.value = [] details.value = []
limitValue.value = [] limitValue.value = []
@@ -187,7 +200,7 @@ const open = (data: any) => {
getLineDetailData(data.id).then(res => { getLineDetailData(data.id).then(res => {
details.value = res.data details.value = res.data
res.data.objName ? (title.value = res.data.objName + '_' + title.value) : ''
}) })
getLineOverLimitData(data.id).then(res => { getLineOverLimitData(data.id).then(res => {
limitValue.value = res.data limitValue.value = res.data

View File

@@ -1,8 +1,7 @@
<template> <template>
<Tree ref="treRef" :data="tree" style="height: 100%" :width="'100%'" :expanded="expanded" /> <Tree ref="treRef" :data="tree" style="height: 100%" :width="'100%'" :expanded="expanded" />
</template> </template>
<!-- <div class="mb10"> <!-- <div class="mb10">
<el-button type="primary" icon="el-icon-Download" size="small" @click="exportExcelTemplate" :loading="loading">模版下载</el-button> <el-button type="primary" icon="el-icon-Download" size="small" @click="exportExcelTemplate" :loading="loading">模版下载</el-button>
<el-button type="primary" icon="el-icon-Upload" size="small">导入</el-button> <el-button type="primary" icon="el-icon-Upload" size="small">导入</el-button>
<el-button type="primary" icon="el-icon-Download" size="small" @click="ledgerEverywhere" :loading="loading1">导出</el-button> <el-button type="primary" icon="el-icon-Download" size="small" @click="ledgerEverywhere" :loading="loading1">导出</el-button>
@@ -11,7 +10,7 @@
import { ref, nextTick } from 'vue' import { ref, nextTick } from 'vue'
import Tree from './index.vue' import Tree from './index.vue'
import { getTerminalTree,downTerminalTemplate,exportTerminalBase } from '@/api/device-boot/Business' import { getTerminalTree, downTerminalTemplate, exportTerminalBase } from '@/api/device-boot/Business'
import { useConfig } from '@/stores/config' import { useConfig } from '@/stores/config'
const VITE_FLAG = import.meta.env.VITE_NAME == 'jibei' const VITE_FLAG = import.meta.env.VITE_NAME == 'jibei'
defineOptions({ defineOptions({
@@ -28,58 +27,108 @@ const info = (id: any) => {
expanded.value = [id] expanded.value = [id]
getTerminalTree().then(res => { getTerminalTree().then(res => {
// let arr: any[] = [] // let arr: any[] = []
if (VITE_FLAG) { // if (VITE_FLAG) {
res.data.forEach((item: any) => { // res.data.forEach((item: any) => {
item.icon = 'el-icon-Menu' // item.icon = 'el-icon-Menu'
item.plevel = item.level // item.plevel = item.level
item.level = 0 // item.level = 0
item.children.forEach((item2: any) => { // item.children.forEach((item2: any) => {
item2.icon = 'el-icon-HomeFilled' // item2.icon = 'el-icon-HomeFilled'
item2.plevel = item2.level // item2.plevel = item2.level
item2.level = 100 // item2.level = 100
expanded.value.push(item2.id) // expanded.value.push(item2.id)
item2.children.forEach((item3: any) => { // item2.children.forEach((item3: any) => {
item3.icon = 'el-icon-CollectionTag' // item3.icon = 'el-icon-CollectionTag'
item3.plevel = item3.level // item3.plevel = item3.level
item3.level = 200 // item3.level = 200
item3.children.forEach((item4: any) => { // item3.children.forEach((item4: any) => {
item4.icon = 'el-icon-Flag' // item4.icon = 'el-icon-Flag'
item4.plevel = item4.level // item4.plevel = item4.level
item4.level = 300 // item4.level = 300
// arr.push(item4) // // arr.push(item4)
item4.children.forEach((item5: any) => { // item4.children.forEach((item5: any) => {
item5.icon = 'el-icon-OfficeBuilding' // item5.icon = 'el-icon-OfficeBuilding'
item5.plevel = item5.level // item5.plevel = item5.level
item5.level = 300 // item5.level = 300
// item5.id = item4.id // // item5.id = item4.id
item5.children.forEach((item6: any) => { // item5.children.forEach((item6: any) => {
item6.icon = 'el-icon-HelpFilled' // item6.icon = 'el-icon-HelpFilled'
item6.plevel = 4 // item6.plevel = 4
if (item6.name == '电网侧' && item6.children.length == 0) { // if (item6.name == '电网侧' && item6.children.length == 0) {
item6.level = 400 // item6.level = 400
} else { // } else {
item6.level = 400 // item6.level = 400
} // }
item6.children.forEach((item7: any) => { // item6.children.forEach((item7: any) => {
item7.icon = 'el-icon-Film' // item7.icon = 'el-icon-Film'
item7.plevel = item7.level // item7.plevel = item7.level
item7.level = 400 // item7.level = 400
item7.children.forEach((item8: any) => { // item7.children.forEach((item8: any) => {
item8.icon = 'el-icon-Collection' // item8.icon = 'el-icon-Collection'
item8.plevel = item8.level // item8.plevel = item8.level
item8.level = 500 // item8.level = 500
item8.children.forEach((item9: any) => { // item8.children.forEach((item9: any) => {
item9.icon = 'el-icon-Share' // item9.icon = 'el-icon-Share'
item9.plevel = item9.level // item9.plevel = item9.level
item9.level = 600 // item9.level = 600
item9.children.forEach((item10: any) => { // item9.children.forEach((item10: any) => {
item10.icon = 'el-icon-Location' // item10.icon = 'el-icon-Location'
item10.plevel = item10.level // item10.plevel = item10.level
item10.level = 700 // item10.level = 700
}) // })
}) // })
}) // })
// })
// })
// })
// })
// })
// })
// })
// } else {
res.data.forEach((item: any) => {
item.icon = 'el-icon-Menu'
item.color = 'var(--el-color-primary)'
item.plevel = item.level
item.level = (item.level + 1) * 100
item.children.forEach((item2: any) => {
item2.icon = 'el-icon-HomeFilled'
item2.color = 'var(--el-color-primary)'
item2.plevel = item2.level
item2.level = (item2.level + 1) * 100
expanded.value.push(item2.id)
item2.children.forEach((item3: any) => {
item3.icon = 'el-icon-CollectionTag'
item3.color = 'var(--el-color-primary)'
item3.plevel = item3.level
item3.level = (item3.level + 1) * 100
item3.children.forEach((item4: any) => {
item4.icon = 'el-icon-Flag'
item4.color = 'var(--el-color-primary)'
item4.plevel = item4.level
item4.level = (item4.level + 1) * 100
item4.children.forEach((item5: any) => {
item5.icon = 'el-icon-OfficeBuilding'
item5.color = 'var(--el-color-primary)'
item5.plevel = item5.level
item5.level = (item5.level == 7 ? 4 : item5.level + 1) * 100
item5.children.forEach((item6: any) => {
item6.icon =
item6.children?.[0]?.children?.length > 0 ? 'el-icon-Film' : 'el-icon-Share'
item6.color = 'var(--el-color-primary)'
item6.plevel = item6.level
item6.level = (item6.level + 1) * 100
item6.children.forEach((item7: any) => {
item7.icon = item7.children.length > 0 ? 'el-icon-Share' : 'el-icon-Location'
item7.color = 'var(--el-color-primary)'
item7.plevel = item7.level
item7.level = (item7.level + 1) * 100
item7.children.forEach((item8: any) => {
item8.icon = 'el-icon-Location'
item8.color = 'var(--el-color-primary)'
item8.plevel = item8.level
item8.level = (item8.level + 1) * 100
}) })
}) })
}) })
@@ -87,49 +136,8 @@ const info = (id: any) => {
}) })
}) })
}) })
} else { })
res.data.forEach((item: any) => { // }
item.icon = 'el-icon-Menu'
item.plevel = item.level
item.level = (item.level + 1) * 100
item.children.forEach((item2: any) => {
item2.icon = 'el-icon-HomeFilled'
item2.plevel = item2.level
item2.level = (item2.level + 1) * 100
expanded.value.push(item2.id)
item2.children.forEach((item3: any) => {
item3.icon = 'el-icon-CollectionTag'
item3.plevel = item3.level
item3.level = (item3.level + 1) * 100
item3.children.forEach((item4: any) => {
item4.icon = 'el-icon-Flag'
item4.plevel = item4.level
item4.level = (item4.level + 1) * 100
item4.children.forEach((item5: any) => {
item5.icon = 'el-icon-OfficeBuilding'
item5.plevel = item5.level
item5.level = (item5.level == 7 ? 4 : item5.level + 1) * 100
item5.children.forEach((item6: any) => {
item6.icon = 'el-icon-Film'
item6.plevel = item6.level
item6.level = (item6.level + 1) * 100
item6.children.forEach((item7: any) => {
item7.icon = 'el-icon-Share'
item7.plevel = item7.level
item7.level = (item7.level + 1) * 100
item7.children.forEach((item8: any) => {
item8.icon = 'el-icon-Location'
item8.plevel = item8.level
item8.level = (item8.level + 1) * 100
})
})
})
})
})
})
})
})
}
tree.value = res.data tree.value = res.data
@@ -149,7 +157,7 @@ const info = (id: any) => {
}) })
} }
// 下载模版 // 下载模版
const exportExcelTemplate = async() => { const exportExcelTemplate = async () => {
loading.value = true loading.value = true
downTerminalTemplate().then((res: any) => { downTerminalTemplate().then((res: any) => {
let blob = new Blob([res], { let blob = new Blob([res], {
@@ -168,7 +176,7 @@ const exportExcelTemplate = async() => {
}, 0) }, 0)
} }
// 导出台账 // 导出台账
const ledgerEverywhere = async() => { const ledgerEverywhere = async () => {
loading1.value = true loading1.value = true
exportTerminalBase().then((res: any) => { exportTerminalBase().then((res: any) => {
let blob = new Blob([res], { let blob = new Blob([res], {

View File

@@ -6,6 +6,7 @@
style="min-width: unset; padding: 10px 10px 0" style="min-width: unset; padding: 10px 10px 0"
@change="loadData" @change="loadData"
v-if="props.showSelect" v-if="props.showSelect"
filterable
> >
<el-option <el-option
v-for="item in classificationData" v-for="item in classificationData"

View File

@@ -1,28 +0,0 @@
const config: {
base_url: string
result_code: number | string
default_headers: AxiosHeaders
request_timeout: number
} = {
/**
* api请求基础路径
*/
base_url: import.meta.env.VITE_BASE_URL + import.meta.env.VITE_API_URL,
/**
* 接口成功返回状态码
*/
result_code: 200,
/**
* 接口请求超时时间
*/
request_timeout: 30000,
/**
* 默认接口请求类型
* 可选值application/x-www-form-urlencoded multipart/form-data
*/
default_headers: 'application/json'
}
export { config }

View File

@@ -1,6 +0,0 @@
export default {
'401': '认证失败,无法访问系统资源',
'403': '当前操作没有权限',
'404': '访问资源不存在',
default: '系统未知错误,请反馈给管理员'
}

View File

@@ -1,51 +0,0 @@
import { service } from './service'
import { config } from './config'
const { default_headers } = config
const request = (option: any) => {
const { url, method, params, data, headersType, responseType, ...config } = option
return service({
url: url,
method,
params,
data,
...config,
responseType: responseType,
headers: {
'Content-Type': headersType || default_headers
}
})
}
export default {
get: async <T = any>(option: any) => {
const res = await request({ method: 'GET', ...option })
return res.data as unknown as T
},
post: async <T = any>(option: any) => {
const res = await request({ method: 'POST', ...option })
return res.data as unknown as T
},
postOriginal: async (option: any) => {
const res = await request({ method: 'POST', ...option })
return res
},
delete: async <T = any>(option: any) => {
const res = await request({ method: 'DELETE', ...option })
return res.data as unknown as T
},
put: async <T = any>(option: any) => {
const res = await request({ method: 'PUT', ...option })
return res.data as unknown as T
},
download: async <T = any>(option: any) => {
const res = await request({ method: 'GET', responseType: 'blob', ...option })
return res as unknown as Promise<T>
},
upload: async <T = any>(option: any) => {
option.headersType = 'multipart/form-data'
const res = await request({ method: 'POST', ...option })
return res as unknown as Promise<T>
}
}

View File

@@ -1,228 +0,0 @@
import axios, {
AxiosError,
AxiosInstance,
AxiosRequestHeaders,
AxiosResponse,
InternalAxiosRequestConfig
} from 'axios'
import { ElMessage, ElMessageBox, ElNotification } from 'element-plus'
import qs from 'qs'
import { config } from '@/config/axios/config'
import { getAccessToken, getRefreshToken, getTenantId, removeToken, setToken } from '@/utils/auth'
import errorCode from './errorCode'
import { deleteUserCache } from '@/hooks/web/useCache'
const tenantEnable = import.meta.env.VITE_APP_TENANT_ENABLE
const { result_code, base_url, request_timeout } = config
// 需要忽略的提示。忽略后,自动 Promise.reject('error')
const ignoreMsgs = [
'无效的刷新令牌', // 刷新令牌被删除时,不用提示
'刷新令牌已过期' // 使用刷新令牌,刷新获取新的访问令牌时,结果因为过期失败,此时需要忽略。否则,会导致继续 401无法跳转到登出界面
]
// 是否显示重新登录
export const isRelogin = { show: false }
// Axios 无感知刷新令牌,参考 https://www.dashingdog.cn/article/11 与 https://segmentfault.com/a/1190000020210980 实现
// 请求队列
let requestList: any[] = []
// 是否正在刷新中
let isRefreshToken = false
// 请求白名单无须token的接口
const whiteList: string[] = ['/login', '/refresh-token']
// 创建axios实例
const service: AxiosInstance = axios.create({
baseURL: base_url, // api 的 base_url
timeout: request_timeout, // 请求超时时间
withCredentials: false // 禁用 Cookie 等信息
})
// request拦截器
service.interceptors.request.use(
(config: InternalAxiosRequestConfig) => {
// 是否需要设置 token
let isToken = (config!.headers || {}).isToken === false
whiteList.some((v) => {
if (config.url) {
config.url.indexOf(v) > -1
return (isToken = false)
}
})
if (getAccessToken() && !isToken) {
;(config as Recordable).headers.Authorization = 'Bearer ' + getAccessToken() // 让每个请求携带自定义token
}
// 设置租户
if (tenantEnable && tenantEnable === 'true') {
const tenantId = getTenantId()
if (tenantId) (config as Recordable).headers['tenant-id'] = tenantId
}
const params = config.params || {}
const data = config.data || false
if (
config.method?.toUpperCase() === 'POST' &&
(config.headers as AxiosRequestHeaders)['Content-Type'] ===
'application/x-www-form-urlencoded'
) {
config.data = qs.stringify(data)
}
// get参数编码
if (config.method?.toUpperCase() === 'GET' && params) {
config.params = {}
const paramsStr = qs.stringify(params, { allowDots: true })
if (paramsStr) {
config.url = config.url + '?' + paramsStr
}
}
return config
},
(error: AxiosError) => {
// Do something with request error
console.log(error) // for debug
Promise.reject(error)
}
)
// response 拦截器
service.interceptors.response.use(
async (response: AxiosResponse<any>) => {
let { data } = response
const config = response.config
if (!data) {
// 返回“[HTTP]请求没有返回值”;
throw new Error()
}
const { t } = useI18n()
// 未设置状态码则默认成功状态
// 二进制数据则直接返回,例如说 Excel 导出
if (
response.request.responseType === 'blob' ||
response.request.responseType === 'arraybuffer'
) {
// 注意:如果导出的响应为 json说明可能失败了不直接返回进行下载
if (response.data.type !== 'application/json') {
return response.data
}
data = await new Response(response.data).json()
}
const code = data.code || result_code
// 获取错误信息
const msg = data.msg || errorCode[code] || errorCode['default']
if (ignoreMsgs.indexOf(msg) !== -1) {
// 如果是忽略的错误码,直接返回 msg 异常
return Promise.reject(msg)
} else if (code === 401) {
// 如果未认证,并且未进行刷新令牌,说明可能是访问令牌过期了
if (!isRefreshToken) {
isRefreshToken = true
// 1. 如果获取不到刷新令牌,则只能执行登出操作
if (!getRefreshToken()) {
return handleAuthorized()
}
// 2. 进行刷新访问令牌
try {
const refreshTokenRes = await refreshToken()
// 2.1 刷新成功,则回放队列的请求 + 当前请求
setToken((await refreshTokenRes).data.data)
config.headers!.Authorization = 'Bearer ' + getAccessToken()
requestList.forEach((cb: any) => {
cb()
})
requestList = []
return service(config)
} catch (e) {
// 为什么需要 catch 异常呢?刷新失败时,请求因为 Promise.reject 触发异常。
// 2.2 刷新失败,只回放队列的请求
requestList.forEach((cb: any) => {
cb()
})
// 提示是否要登出。即不回放当前请求!不然会形成递归
return handleAuthorized()
} finally {
requestList = []
isRefreshToken = false
}
} else {
// 添加到队列,等待刷新获取到新的令牌
return new Promise((resolve) => {
requestList.push(() => {
config.headers!.Authorization = 'Bearer ' + getAccessToken() // 让每个请求携带自定义token 请根据实际情况自行修改
resolve(service(config))
})
})
}
} else if (code === 500) {
ElMessage.error(t('sys.api.errMsg500'))
return Promise.reject(new Error(msg))
} else if (code === 901) {
ElMessage.error({
offset: 300,
dangerouslyUseHTMLString: true,
message:
'<div>' +
t('sys.api.errMsg901') +
'</div>' +
'<div> &nbsp; </div>' +
'<div>参考 https://doc.iocoder.cn/ 教程</div>' +
'<div> &nbsp; </div>' +
'<div>5 分钟搭建本地环境</div>'
})
return Promise.reject(new Error(msg))
} else if (code !== 200) {
if (msg === '无效的刷新令牌') {
// hard coding忽略这个提示直接登出
console.log(msg)
} else {
ElNotification.error({ title: msg })
}
return Promise.reject('error')
} else {
return data
}
},
(error: AxiosError) => {
console.log('err' + error) // for debug
let { message } = error
const { t } = useI18n()
if (message === 'Network Error') {
message = t('sys.api.errorMessage')
} else if (message.includes('timeout')) {
message = t('sys.api.apiTimeoutMessage')
} else if (message.includes('Request failed with status code')) {
message = t('sys.api.apiRequestFailed') + message.substr(message.length - 3)
}
ElMessage.error(message)
return Promise.reject(error)
}
)
const refreshToken = async () => {
axios.defaults.headers.common['tenant-id'] = getTenantId()
return await axios.post(base_url + '/system/auth/refresh-token?refreshToken=' + getRefreshToken())
}
const handleAuthorized = () => {
const { t } = useI18n()
if (!isRelogin.show) {
// 如果已经到重新登录页面则不进行弹窗提示
if (window.location.href.includes('login?redirect=')) {
return
}
isRelogin.show = true
ElMessageBox.confirm(t('sys.api.timeoutMessage'), t('common.confirmTitle'), {
showCancelButton: false,
closeOnClickModal: false,
showClose: false,
confirmButtonText: t('login.relogin'),
type: 'warning'
}).then(() => {
deleteUserCache() // 删除用户缓存
removeToken()
isRelogin.show = false
// 干掉token后再走一次路由让它过router.beforeEach的校验
window.location.href = window.location.href
})
}
return Promise.reject(t('sys.api.timeoutMessage'))
}
export { service }

View File

@@ -1,6 +1,7 @@
import { i18n } from '@/plugins/vueI18n' import { i18n } from '@/plugins/vueI18n'
import { useLocaleStoreWithOut } from '@/stores/modules/locale' import { useLocaleStoreWithOut } from '@/stores/modules/locale'
import { setHtmlPageLang } from '@/plugins/vueI18n/helper' import { setHtmlPageLang } from '@/plugins/vueI18n/helper'
import { loadLocaleMessages } from '@/locales'
const setI18nLanguage = (locale: LocaleType) => { const setI18nLanguage = (locale: LocaleType) => {
const localeStore = useLocaleStoreWithOut() const localeStore = useLocaleStoreWithOut()
@@ -22,9 +23,9 @@ export const useLocale = () => {
const changeLocale = async (locale: LocaleType) => { const changeLocale = async (locale: LocaleType) => {
const globalI18n = i18n.global const globalI18n = i18n.global
const langModule = await import(`../../locales/${locale}.ts`) const message = await loadLocaleMessages(locale)
globalI18n.setLocaleMessage(locale, langModule.default) globalI18n.setLocaleMessage(locale, message)
setI18nLanguage(locale) setI18nLanguage(locale)
} }

View File

@@ -1,78 +0,0 @@
<script lang="tsx">
import { computed, defineComponent, unref } from 'vue'
import { useAppStore } from '@/stores/modules/app'
import { Backtop } from '@/components/Backtop'
import { Setting } from '@/layouts/components/Setting'
import { useRenderLayout } from './components/useRenderLayout'
import { useDesign } from '@/hooks/web/useDesign'
const { getPrefixCls } = useDesign()
const prefixCls = getPrefixCls('layout')
const appStore = useAppStore()
// 是否是移动端
const mobile = computed(() => appStore.getMobile)
// 菜单折叠
const collapse = computed(() => appStore.getCollapse)
const layout = computed(() => appStore.getLayout)
const handleClickOutside = () => {
appStore.setCollapse(true)
}
const renderLayout = () => {
switch (unref(layout)) {
case 'classic':
const { renderClassic } = useRenderLayout()
return renderClassic()
case 'topLeft':
const { renderTopLeft } = useRenderLayout()
return renderTopLeft()
case 'top':
const { renderTop } = useRenderLayout()
return renderTop()
case 'cutMenu':
const { renderCutMenu } = useRenderLayout()
return renderCutMenu()
default:
break
}
}
export default defineComponent({
name: 'Layout',
setup() {
return () => (
<section class={[prefixCls, `${prefixCls}__${layout.value}`, 'w-[100%] h-[100%] relative']}>
{mobile.value && !collapse.value ? (
<div
class="absolute left-0 top-0 z-99 h-full w-full bg-[var(--el-color-black)] opacity-30"
onClick={handleClickOutside}
></div>
) : undefined}
{renderLayout()}
<Backtop></Backtop>
<Setting></Setting>
</section>
)
}
})
</script>
<style lang="scss" scoped>
$prefix-cls: v-layout;
.#{$prefix-cls} {
background-color: var(--app-content-bg-color);
:deep(.el-scrollbar__view) {
height: 100% !important;
}
}
</style>

View File

@@ -72,6 +72,7 @@
@change="onCommitState($event, 'mainAnimation')" @change="onCommitState($event, 'mainAnimation')"
:model-value="configStore.layout.mainAnimation" :model-value="configStore.layout.mainAnimation"
:placeholder="'layouts.Please select an animation name'" :placeholder="'layouts.Please select an animation name'"
filterable
> >
<el-option label="slide-right" value="slide-right"></el-option> <el-option label="slide-right" value="slide-right"></el-option>
<el-option label="slide-left" value="slide-left"></el-option> <el-option label="slide-left" value="slide-left"></el-option>

View File

@@ -11,25 +11,25 @@
:data="eventList.slice((pageNum - 1) * pageSize, pageNum * pageSize)" :data="eventList.slice((pageNum - 1) * pageSize, pageNum * pageSize)"
> >
<!-- <vxe-column type="seq" width="70px" title="序号"></vxe-column> --> <!-- <vxe-column type="seq" width="70px" title="序号"></vxe-column> -->
<vxe-column field="time" width="180px" sortable title="发生时刻"></vxe-column> <vxe-column field="time" width="180px" sortable title="暂降发生时刻"></vxe-column>
<vxe-column field="lineName" title="监测点"></vxe-column>
<vxe-column field="powerCompany" title="变电站" width="100px"></vxe-column>
<vxe-column field="powerCompany" title="供电公司" width="100px"></vxe-column>
<vxe-column field="persistTime" width="120px" sortable title="持续时间(s)">
<template #default="{ row }">
{{ Math.floor(row.persistTime * 1000) / 1000 }}
</template>
</vxe-column>
<vxe-column field="eventValue" width="160px" sortable title="暂降(骤升)幅值(%)"> <vxe-column field="eventValue" width="160px" sortable title="暂降(骤升)幅值(%)">
<template #default="{ row }"> <template #default="{ row }">
{{ Math.floor(row.eventValue * 10000) / 100 }} {{ Math.floor(row.eventValue * 10000) / 100 }}
</template> </template>
</vxe-column> </vxe-column>
<vxe-column field="persistTime" width="120px" sortable title="持续时间(s)">
<template #default="{ row }">
{{ Math.floor(row.persistTime * 1000) / 1000 }}
</template>
</vxe-column>
<vxe-column field="eventType" width="100px" title="触发类型"> <vxe-column field="eventType" width="100px" title="触发类型">
<template #default="{ row }"> <template #default="{ row }">
{{ event.filter(item => item.id == row.eventType)[0]?.name || '/' }} {{ event.filter(item => item.id == row.eventType)[0]?.name || '/' }}
</template> </template>
</vxe-column> </vxe-column>
<vxe-column field="powerCompany" title="变电站" width="100px"></vxe-column>
<vxe-column field="powerCompany" title="供电公司" width="100px"></vxe-column>
<vxe-column field="lineName" title="监测点"></vxe-column>
</vxe-table> </vxe-table>
<div class="table-pagination mt10"> <div class="table-pagination mt10">
<el-pagination <el-pagination
@@ -50,7 +50,8 @@ import { ref, reactive, onMounted } from 'vue'
import { defaultAttribute } from '@/components/table/defaultAttribute' import { defaultAttribute } from '@/components/table/defaultAttribute'
import { mainHeight } from '@/utils/layout' import { mainHeight } from '@/utils/layout'
import { useDictData } from '@/stores/dictData' import { useDictData } from '@/stores/dictData'
import MQTT from '@/utils/mqtt' // import MQTT from '@/utils/mqtt'
import socketClient from '@/utils/webSocketClient'
const dictData = useDictData() const dictData = useDictData()
const event = dictData.getBasicData('Event_Statis') const event = dictData.getBasicData('Event_Statis')
import { useAdminInfo } from '@/stores/adminInfo' import { useAdminInfo } from '@/stores/adminInfo'
@@ -68,37 +69,71 @@ const handleClose = (done: any) => {
drawer.value = false drawer.value = false
done() done()
} }
const init = async () => { const dataSocket = reactive({
const mqttClient = new MQTT('/sendEvent') socketServe: socketClient.Instance
// 设置消息接收回调 })
try { // const init = async () => {
await mqttClient.init() // const mqttClient = new MQTT('/sendEvent')
// // 设置消息接收回调
// try {
// await mqttClient.init()
// 订阅主题 // // 订阅主题
await mqttClient.subscribe() // await mqttClient.subscribe()
// 设置消息接收回调 // // 设置消息接收回调
mqttClient.onMessage((topic, message) => { // mqttClient.onMessage((topic, message) => {
const msg = JSON.parse(message.toString()) // const msg = JSON.parse(message.toString())
// console.log('🚀 ~ init ~ msg:', msg) // // console.log('🚀 ~ init ~ msg:', msg)
if (msg.deptList.includes(adminInfo.$state.deptId)) { // if (msg.deptList.includes(adminInfo.$state.deptId)) {
drawer.value = true // drawer.value = true
isLoading.value = true // isLoading.value = true
eventList.value.unshift(msg) // eventList.value.unshift(msg)
setTimeout(() => { // setTimeout(() => {
isLoading.value = false // isLoading.value = false
}, 500) // }, 500)
} // }
}) // })
} catch (error) { // } catch (error) {
console.error('MQTT 初始化失败:', error) // // console.error('MQTT 初始化失败:', error)
} // }
// }
const socket = async () => {
const url = localStorage.getItem('WebSocketUrl3') || 'null' //'ws://192.168.2.130:10203/event/'
// const url = 'ws://192.168.1.68:10203/event/'
await dataSocket.socketServe.connect(`${url}${adminInfo.id}`)
await dataSocket.socketServe.registerCallBack('message', (res: any) => {
if (res.deptList.includes(adminInfo.$state.deptId)) {
drawer.value = true
isLoading.value = true
eventList.value.unshift(res)
setTimeout(() => {
isLoading.value = false
}, 500)
}
// logList.value.push({
// type: res.code == 500 ? 'error' : '',
// time: formatDate(new Date(), 'YYYY-MM-DD hh:mm:ss'),
// name: res.message
// })
})
} }
onUnmounted(() => {
dataSocket.socketServe?.closeWs()
})
onMounted(() => { onMounted(() => {
// startMqtt('/sendEvent', (topic, message) => { // startMqtt('/sendEvent', (topic, message) => {
// const msg = JSON.parse(message.toString()) // const msg = JSON.parse(message.toString())
// console.log(msg) // console.log(msg)
// }) // })
init()
setTimeout(() => {
socket()
}, 3000)
}) })
defineExpose({ defineExpose({
open, open,

View File

@@ -8,41 +8,14 @@
name="el-icon-BellFilled" name="el-icon-BellFilled"
size="18" size="18"
/> />
<span class="nav-menu-text" v-if="globalPopUpRef?.eventList.length != 0"> <span class="nav-menu-text" v-if="globalPopUpRef?.eventList?.length > 0">
{{ (globalPopUpRef?.eventList.length>99? '99+':globalPopUpRef?.eventList.length) || 0 }} {{ globalPopUpRef.eventList.length > 99 ? '99+' : globalPopUpRef.eventList.length }}
</span> </span>
</div> </div>
</el-tooltip> </el-tooltip>
<!-- <el-tooltip effect="dark" content="截图" placement="bottom">
<div @click="savePng" class="nav-menu-item">
<Icon
:color="configStore.getColorVal('headerBarTabColor')"
class="nav-menu-icon"
name="el-icon-Camera"
size="18"
/>
</div>
</el-tooltip>
<el-tooltip effect="dark" :content="state.isFullScreen ? '缩小' : '放大'" placement="bottom">
<div @click="onFullScreen" class="nav-menu-item" :class="state.isFullScreen ? 'hover' : ''">
<Icon
:color="configStore.getColorVal('headerBarTabColor')"
class="nav-menu-icon"
v-if="state.isFullScreen"
name="fa-solid fa-compress"
size="18"
/>
<Icon
:color="configStore.getColorVal('headerBarTabColor')"
class="nav-menu-icon"
v-else
name="fa-solid fa-expand"
size="18"
/>
</div>
</el-tooltip> -->
<el-dropdown style="height: 100%" @command="handleCommand"> <el-dropdown style="height: 100%" @command="handleCommand">
<div class="admin-info" :class="state.currentNavMenu == 'adminInfo' ? 'hover' : ''"> <div class="admin-info" :class="state.currentNavMenu === 'adminInfo' ? 'hover' : ''">
<el-avatar :size="25" fit="fill"> <el-avatar :size="25" fit="fill">
<img src="@/assets/avatar.png" alt="" /> <img src="@/assets/avatar.png" alt="" />
</el-avatar> </el-avatar>
@@ -51,98 +24,117 @@
<template #dropdown> <template #dropdown>
<el-dropdown-menu> <el-dropdown-menu>
<el-dropdown-item command="adminInfo">个人资料</el-dropdown-item> <el-dropdown-item command="adminInfo">个人资料</el-dropdown-item>
<el-dropdown-item command="changePwd">修改密码</el-dropdown-item> <el-dropdown-item command="changePwd" v-if="!IS_LNQR">修改密码</el-dropdown-item>
<el-dropdown-item command="layout">退出登录</el-dropdown-item> <el-dropdown-item command="logout">退出登录</el-dropdown-item>
</el-dropdown-menu> </el-dropdown-menu>
</template> </template>
</el-dropdown> </el-dropdown>
<!-- <div @click="configStore.setLayout('showDrawer', true)" class="nav-menu-item">
<Icon
:color="configStore.getColorVal('headerBarTabColor')"
class="nav-menu-icon"
name="fa fa-cogs"
size="18"
/>
</div> -->
<Config /> <Config />
<PopupPwd ref="popupPwd" /> <PopupPwd ref="popupPwd" />
<AdminInfo ref="popupAdminInfo" /> <AdminInfo ref="popupAdminInfo" />
<!-- <TerminalVue /> -->
<!-- 全局暂降事件 -->
<globalPopUp ref="globalPopUpRef" /> <globalPopUp ref="globalPopUpRef" />
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { reactive, ref } from 'vue' import { reactive, ref } from 'vue'
import screenfull from 'screenfull'
import { useConfig } from '@/stores/config' import { useConfig } from '@/stores/config'
import { ElMessage } from 'element-plus'
import Config from './config.vue'
import { useAdminInfo } from '@/stores/adminInfo' import { useAdminInfo } from '@/stores/adminInfo'
import router from '@/router' import { useNavTabs } from '@/stores/navTabs'
import globalPopUp from './globalPopUp.vue' import { logout } from '@/api/user-boot/user'
import { routePush } from '@/utils/router' import { Local } from '@/utils/storage'
import html2canvas from 'html2canvas' import Config from './config.vue'
import PopupPwd from './popup/password.vue' import PopupPwd from './popup/password.vue'
import AdminInfo from './popup/adminInfo.vue' import AdminInfo from './popup/adminInfo.vue'
import { useNavTabs } from '@/stores/navTabs' import globalPopUp from './globalPopUp.vue'
const { replace } = useRouter()
// 环境标识
const IS_LNQR = import.meta.env.VITE_NAME === 'LNqr'
const adminInfo = useAdminInfo() const adminInfo = useAdminInfo()
const navTabs = useNavTabs() const navTabs = useNavTabs()
const configStore = useConfig() const configStore = useConfig()
// 引用
const popupPwd = ref() const popupPwd = ref()
const popupAdminInfo = ref() const popupAdminInfo = ref()
const state = reactive({
isFullScreen: false,
currentNavMenu: '',
showLayoutDrawer: false,
showAdminInfoPopover: false
})
const globalPopUpRef = ref() const globalPopUpRef = ref()
const savePng = () => {
html2canvas(document.body, { // 状态
scale: 1, const state = reactive({
useCORS: true currentNavMenu: ''
}).then(function (canvas) { })
var link = document.createElement('a')
link.href = canvas.toDataURL('image/png') // 打开暂降事件
link.download = 'screenshot.png' const temporaryLandingEvent = () => {
link.click() if (globalPopUpRef.value) {
}) globalPopUpRef.value.open()
}
const onFullScreen = () => {
if (!screenfull.isEnabled) {
ElMessage.warning('layouts.Full screen is not supported')
return false
} }
screenfull.toggle()
screenfull.onchange(() => {
state.isFullScreen = screenfull.isFullscreen
})
} }
const handleCommand = (key: string) => { // 下拉菜单命令
console.log(key) const handleCommand = async (key: string) => {
switch (key) { switch (key) {
case 'adminInfo': case 'adminInfo':
popupAdminInfo.value.open() popupAdminInfo.value?.open()
break break
case 'changePwd': case 'changePwd':
popupPwd.value.open() popupPwd.value?.open()
break break
case 'layout': case 'logout':
navTabs.closeTabs() await handleLogout()
window.localStorage.clear()
adminInfo.reset()
router.push({ name: 'login' })
break
default:
break break
} }
} }
const temporaryLandingEvent = () => {
globalPopUpRef.value.open() // 统一登出逻辑
const handleLogout = async () => {
try {
// 1. 调用后端登出接口
await logout()
// console.log('✅ 后端登出成功')
} catch (err) {
// console.error('❌ 登出接口异常', err)
} finally {
// 2. 清理本地所有数据
clearLocalData()
// 3. LNqr 环境跳 CAS 登出
if (IS_LNQR) {
redirectToCasLogout()
} else {
// 普通环境直接跳登录页
window.location.reload()
setTimeout(() => {
replace('/login')
}, 1000)
}
}
}
// 清理本地数据
const clearLocalData = () => {
// 清空 localStorage保留 VXE 自定义列、DevTools 配置)
Local.clear()
// 清空用户状态
adminInfo.reset()
// 清空标签页
navTabs.closeTabs()
}
// CAS 登出跳转
const redirectToCasLogout = () => {
const casLogoutUrl =
'http://privilege-epri.dcloud.ln.dc.sgcc.com.cn/cas/logout?service=http://privilege-epri.dcloud.ln.dc.sgcc.com.cn/cas/login?service=http://PQMonitoring.dcloud.ln.dc.sgcc.com.cn'
window.location.href = casLogoutUrl
// 清 Cookie
document.cookie.split(';').forEach(cookie => {
const name = cookie.split('=')[0].trim()
document.cookie = `${name}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;`
})
} }
</script> </script>
@@ -156,7 +148,6 @@ const temporaryLandingEvent = () => {
height: 60px; height: 60px;
display: flex; display: flex;
align-items: center; align-items: center;
// height: 100%;
margin-left: auto; margin-left: auto;
background-color: v-bind('configStore.getColorVal("headerBarBackground")'); background-color: v-bind('configStore.getColorVal("headerBarBackground")');
@@ -212,37 +203,15 @@ const temporaryLandingEvent = () => {
} }
} }
.dropdown-menu-box :deep(.el-dropdown-menu__item) { .nav-menu-text {
justify-content: center; position: absolute;
} top: 13px;
left: 20px;
.admin-info-base { font-size: 12px;
display: flex; background: #ff0000;
justify-content: center; color: #fff;
flex-wrap: wrap; border-radius: 5px;
padding-top: 10px; padding: 0 3px;
.admin-info-other {
display: block;
width: 100%;
text-align: center;
padding: 10px 0;
.admin-info-name {
font-size: var(--el-font-size-large);
}
}
}
.admin-info-footer {
padding: 10px 0;
margin: 0 -12px -12px -12px;
display: flex;
justify-content: space-around;
}
.pt2 {
padding-top: 2px;
} }
@keyframes twinkle { @keyframes twinkle {
@@ -256,15 +225,4 @@ const temporaryLandingEvent = () => {
transform: scale(1); transform: scale(1);
} }
} }
.nav-menu-text {
position: absolute;
top: 13px;
left: 20px;
font-size: 12px;
display: inline-block;
background-color: #ff0000;
color: #fff;
border-radius: 5px;
padding: 0 3px;
}
</style> </style>

View File

@@ -30,6 +30,7 @@ import { validatePwd } from '@/utils/common'
import { useAdminInfo } from '@/stores/adminInfo' import { useAdminInfo } from '@/stores/adminInfo'
import router from '@/router' import router from '@/router'
import { useNavTabs } from '@/stores/navTabs' import { useNavTabs } from '@/stores/navTabs'
import { Local } from '@/utils/storage'
const navTabs = useNavTabs() const navTabs = useNavTabs()
const adminInfo = useAdminInfo() const adminInfo = useAdminInfo()
const dialogVisible = ref(false) const dialogVisible = ref(false)
@@ -103,7 +104,7 @@ const submit = () => {
dialogVisible.value = false dialogVisible.value = false
setTimeout(() => { setTimeout(() => {
navTabs.closeTabs() navTabs.closeTabs()
window.localStorage.clear() Local.clear()
adminInfo.reset() adminInfo.reset()
router.push({ name: 'login' }) router.push({ name: 'login' })
}, 0) }, 0)

View File

@@ -94,9 +94,11 @@ const init = async () => {
return return
} }
} }
// 跳转到第一个菜单 // 仅在 loading 页时跳转到第一个菜单,避免覆盖用户当前页面
let firstRoute = getFirstRoute(navTabs.state.tabsViewRoutes) if (route.name === 'adminMainLoading') {
if (firstRoute) routePush(firstRoute.path) const firstRoute = getFirstRoute(navTabs.state.tabsViewRoutes)
if (firstRoute) routePush(firstRoute.path)
}
}) })
} }

View File

@@ -1,9 +1,13 @@
<template> <template>
<el-main class="layout-main" :style="mainHeight()"> <el-main class="layout-main" :style="mainHeight()">
<router-view v-slot="{ Component }"> <router-view v-slot="{ Component, route: viewRoute }">
<transition :name="config.layout.mainAnimation" mode="out-in"> <transition :name="config.layout.mainAnimation" mode="out-in">
<keep-alive :include="state.keepAliveComponentNameList"> <keep-alive :include="state.keepAliveComponentNameList">
<component :is="Component" :key="state.componentKey" /> <component
:is="Component"
v-if="Component"
:key="state.componentKey || viewRoute.fullPath"
/>
</keep-alive> </keep-alive>
</transition> </transition>
</router-view> </router-view>
@@ -32,7 +36,7 @@ const state: {
componentKey: string componentKey: string
keepAliveComponentNameList: string[] keepAliveComponentNameList: string[]
} = reactive({ } = reactive({
componentKey: route.path, componentKey: '',
keepAliveComponentNameList: [] keepAliveComponentNameList: []
}) })
@@ -51,9 +55,10 @@ onBeforeMount(() => {
state.keepAliveComponentNameList = state.keepAliveComponentNameList.filter( state.keepAliveComponentNameList = state.keepAliveComponentNameList.filter(
(name: string) => menu.meta.keepalive !== name (name: string) => menu.meta.keepalive !== name
) )
state.componentKey = '' // 临时换 key 强制卸载,再在 nextTick 恢复为路由 fullPath避免 keep-alive 缓存错乱
state.componentKey = `${menu.fullPath || menu.path}__refresh__${Date.now()}`
nextTick(() => { nextTick(() => {
state.componentKey = menu.path state.componentKey = ''
addKeepAliveComponentName(menu.meta.keepalive as string) addKeepAliveComponentName(menu.meta.keepalive as string)
}) })
}) })
@@ -79,7 +84,6 @@ onMounted(() => {
watch( watch(
() => route.path, () => route.path,
() => { () => {
state.componentKey = route.path
if (typeof navTabs.state.activeRoute?.meta.keepalive == 'string') { if (typeof navTabs.state.activeRoute?.meta.keepalive == 'string') {
addKeepAliveComponentName(navTabs.state.activeRoute?.meta.keepalive) addKeepAliveComponentName(navTabs.state.activeRoute?.meta.keepalive)
} }

View File

@@ -5,7 +5,7 @@
element-loading-background="var(--ba-bg-color-overlay)" element-loading-background="var(--ba-bg-color-overlay)"
element-loading-text="加载中" element-loading-text="加载中"
class="default-main ba-main-loading" class="default-main ba-main-loading"
:style="{ height:'calc(100vh - 170px)'}" :style="{ height: 'calc(100vh - 170px)' }"
></div> ></div>
<div v-if="state.showReload" class="loading-footer mt10"> <div v-if="state.showReload" class="loading-footer mt10">
<el-button @click="refresh" type="warning">重新加载</el-button> <el-button @click="refresh" type="warning">重新加载</el-button>
@@ -14,26 +14,65 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { onUnmounted, reactive } from 'vue' import { onMounted, onUnmounted, reactive, watch } from 'vue'
import { useRoute } from 'vue-router'
import router from '@/router/index' import router from '@/router/index'
import { useNavTabs } from '@/stores/navTabs' import { useNavTabs } from '@/stores/navTabs'
import { isAdminApp } from '@/utils/common' import { isAdminApp } from '@/utils/common'
import { getFirstRoute, routePush } from '@/utils/router' import { getFirstRoute, routePush } from '@/utils/router'
import { isEmpty } from 'lodash-es'
import { adminBaseRoutePath } from '@/router/static'
let timer: number let timer: number
const route = useRoute()
const navTabs = useNavTabs() const navTabs = useNavTabs()
const state = reactive({ const state = reactive({
maximumWait: 1000 * 6, maximumWait: 1000 * 6,
showReload: false, showReload: false
}) })
const refresh = () => { const tryRedirect = () => {
router.go(0) if (route.params.to) {
try {
const lastRoute = JSON.parse(route.params.to as string)
if (lastRoute.path && lastRoute.path !== adminBaseRoutePath) {
const query = !isEmpty(lastRoute.query) ? lastRoute.query : {}
routePush({ path: lastRoute.path, query })
return true
}
} catch (error) {
console.error(error)
}
}
if (isAdminApp() && navTabs.state.tabsViewRoutes.length > 0) {
const firstRoute = getFirstRoute(navTabs.state.tabsViewRoutes)
if (firstRoute) {
routePush(firstRoute.path)
return true
}
}
return false
} }
// if (isAdminApp() && navTabs.state.tabsViewRoutes.length > 0) {
// let firstRoute = getFirstRoute(navTabs.state.tabsViewRoutes) const refresh = () => {
// if (firstRoute) routePush(firstRoute.path) if (!tryRedirect()) {
// } router.replace(route.fullPath)
}
}
onMounted(() => {
if (tryRedirect()) return
const stop = watch(
() => navTabs.state.tabsViewRoutes.length,
len => {
if (len > 0 && tryRedirect()) {
stop()
}
}
)
})
timer = window.setTimeout(() => { timer = window.setTimeout(() => {
state.showReload = true state.showReload = true

View File

@@ -1,72 +0,0 @@
<script lang="ts" setup>
import { useTagsViewStore } from '@/stores/modules/tagsView'
import { useAppStore } from '@/stores/modules/app'
import { Footer } from '@/layouts/components/Footer'
defineOptions({ name: 'AppView' })
const appStore = useAppStore()
const layout = computed(() => appStore.getLayout)
const fixedHeader = computed(() => appStore.getFixedHeader)
const footer = computed(() => appStore.getFooter)
const tagsViewStore = useTagsViewStore()
const getCaches = computed((): string[] => {
return tagsViewStore.getCachedViews
})
const tagsView = computed(() => appStore.getTagsView)
//region 无感刷新
const routerAlive = ref(true)
// 无感刷新,防止出现页面闪烁白屏
const reload = () => {
routerAlive.value = false
nextTick(() => (routerAlive.value = true))
}
// 为组件后代提供刷新方法
provide('reload', reload)
//endregion
</script>
<template>
<section
:class="[
'p-[var(--app-content-padding)] w-[calc(100%-var(--app-content-padding)-var(--app-content-padding))] bg-[var(--app-content-bg-color)] dark:bg-[var(--el-bg-color)]',
{
'!min-h-[calc(100%-var(--app-content-padding)-var(--app-content-padding)-var(--app-footer-height))]':
(fixedHeader &&
(layout === 'classic' || layout === 'topLeft' || layout === 'top') &&
footer) ||
(!tagsView && layout === 'top' && footer),
'!min-h-[calc(100%-var(--app-content-padding)-var(--app-content-padding)-var(--app-footer-height)-var(--tags-view-height))]':
tagsView && layout === 'top' && footer,
'!min-h-[calc(100%-var(--tags-view-height)-var(--app-content-padding)-var(--app-content-padding)-var(--top-tool-height)-var(--app-footer-height))]':
!fixedHeader && layout === 'classic' && footer,
'!min-h-[calc(100%-var(--tags-view-height)-var(--app-content-padding)-var(--app-content-padding)-var(--app-footer-height))]':
!fixedHeader && layout === 'topLeft' && footer,
'!min-h-[calc(100%-var(--top-tool-height)-var(--app-content-padding)-var(--app-content-padding))]':
fixedHeader && layout === 'cutMenu' && footer,
'!min-h-[calc(100%-var(--top-tool-height)-var(--app-content-padding)-var(--app-content-padding)-var(--tags-view-height))]':
!fixedHeader && layout === 'cutMenu' && footer
}
]"
>
<router-view v-if="routerAlive">
<template #default="{ Component, route }">
<keep-alive :include="getCaches">
<component :is="Component" :key="route.fullPath" />
</keep-alive>
</template>
</router-view>
</section>
<Footer v-if="footer" />
</template>

View File

@@ -1,3 +0,0 @@
import Breadcrumb from './src/Breadcrumb.vue'
export { Breadcrumb }

View File

@@ -1,130 +0,0 @@
<script lang="tsx">
import { ElBreadcrumb, ElBreadcrumbItem } from 'element-plus'
import { ref, watch, computed, unref, defineComponent, TransitionGroup } from 'vue'
import { useRouter } from 'vue-router'
import { usePermissionStore } from '@/stores/modules/permission'
import { filterBreadcrumb } from './helper'
import { filter, treeToList } from '@/utils/tree'
import type { RouteLocationNormalizedLoaded, RouteMeta } from 'vue-router'
import { Icon } from '@/components/Icon'
import { useAppStore } from '@/stores/modules/app'
import { useDesign } from '@/hooks/web/useDesign'
const { getPrefixCls } = useDesign()
const prefixCls = getPrefixCls('breadcrumb')
const appStore = useAppStore()
// 面包屑图标
const breadcrumbIcon = computed(() => appStore.getBreadcrumbIcon)
export default defineComponent({
name: 'Breadcrumb',
setup() {
const { currentRoute } = useRouter()
const { t } = useI18n()
const levelList = ref<AppRouteRecordRaw[]>([])
const permissionStore = usePermissionStore()
const menuRouters = computed(() => {
const routers = permissionStore.getRouters
return filterBreadcrumb(routers)
})
const getBreadcrumb = () => {
const currentPath = currentRoute.value.matched.slice(-1)[0].path
levelList.value = filter<AppRouteRecordRaw>(unref(menuRouters), (node: AppRouteRecordRaw) => {
return node.path === currentPath
})
}
const renderBreadcrumb = () => {
const breadcrumbList = treeToList<AppRouteRecordRaw[]>(unref(levelList))
return breadcrumbList.map((v) => {
const disabled = !v.redirect || v.redirect === 'noredirect'
const meta = v.meta as RouteMeta
return (
<ElBreadcrumbItem to={{ path: disabled ? '' : v.path }} key={v.name}>
{meta?.icon && breadcrumbIcon.value ? (
<div class="flex items-center">
<Icon icon={meta.icon} class="mr-[2px]" svgClass="inline-block"></Icon>
{t(v?.meta?.title)}
</div>
) : (
t(v?.meta?.title)
)}
</ElBreadcrumbItem>
)
})
}
watch(
() => currentRoute.value,
(route: RouteLocationNormalizedLoaded) => {
if (route.path.startsWith('/redirect/')) {
return
}
getBreadcrumb()
},
{
immediate: true
}
)
return () => (
<ElBreadcrumb separator="/" class={`${prefixCls} flex items-center h-full ml-[10px]`}>
<TransitionGroup appear enter-active-class="animate__animated animate__fadeInRight">
{renderBreadcrumb()}
</TransitionGroup>
</ElBreadcrumb>
)
}
})
</script>
<style lang="scss" scoped>
$prefix-cls: el-breadcrumb;
.#{$prefix-cls} {
:deep(&__item) {
display: flex;
.#{$prefix-cls}__inner {
display: flex;
align-items: center;
color: var(--top-header-text-color);
&:hover {
color: var(--el-color-primary);
}
}
}
:deep(&__item):not(:last-child) {
.#{$prefix-cls}__inner {
color: var(--top-header-text-color);
&:hover {
color: var(--el-color-primary);
}
}
}
:deep(&__item):last-child {
.#{$prefix-cls}__inner {
display: flex;
align-items: center;
color: var(--el-text-color-placeholder);
&:hover {
color: var(--el-text-color-placeholder);
}
}
}
}
</style>

View File

@@ -1,31 +0,0 @@
import { pathResolve } from '@/utils/routerHelper'
import type { RouteMeta } from 'vue-router'
export const filterBreadcrumb = (
routes: AppRouteRecordRaw[],
parentPath = ''
): AppRouteRecordRaw[] => {
const res: AppRouteRecordRaw[] = []
for (const route of routes) {
const meta = route?.meta as RouteMeta
if (meta.hidden && !meta.canTo) {
continue
}
const data: AppRouteRecordRaw =
!meta.alwaysShow && route.children?.length === 1
? { ...route.children[0], path: pathResolve(route.path, route.children[0].path) }
: { ...route }
data.path = pathResolve(parentPath, data.path)
if (data.children) {
data.children = filterBreadcrumb(data.children, data.path)
}
if (data) {
res.push(data)
}
}
return res
}

View File

@@ -1,3 +0,0 @@
import Collapse from './src/Collapse.vue'
export { Collapse }

View File

@@ -1,36 +0,0 @@
<script lang="ts" setup>
import { useAppStore } from '@/stores/modules/app'
import { propTypes } from '@/utils/propTypes'
import { useDesign } from '@/hooks/web/useDesign'
import { Expand,Fold,ZoomIn,Delete} from '@element-plus/icons-vue'
defineOptions({ name: 'Collapse' })
const { getPrefixCls } = useDesign()
const prefixCls = getPrefixCls('collapse')
defineProps({
color: propTypes.string.def('')
})
const appStore = useAppStore()
const collapse = computed(() => appStore.getCollapse)
const toggleCollapse = () => {
const collapsed = unref(collapse)
appStore.setCollapse(!collapsed)
}
</script>
<template>
<div :class="prefixCls" @click="toggleCollapse">
<Icon
:color="color"
:icon="collapse ? Expand: Fold"
:size="18"
class="cursor-pointer"
/>
</div>
</template>

View File

@@ -1,10 +0,0 @@
import ContextMenu from './src/ContextMenu.vue'
import { ElDropdown } from 'element-plus'
import type { RouteLocationNormalizedLoaded } from 'vue-router'
export interface ContextMenuExpose {
elDropdownMenuRef: ComponentRef<typeof ElDropdown>
tagItem: RouteLocationNormalizedLoaded
}
export { ContextMenu }

View File

@@ -1,76 +0,0 @@
<script lang="ts" setup>
import { PropType } from 'vue'
import { useDesign } from '@/hooks/web/useDesign'
import type { RouteLocationNormalizedLoaded } from 'vue-router'
import { contextMenuSchema } from '@/types/contextMenu'
import type { ElDropdown } from 'element-plus'
defineOptions({ name: 'ContextMenu' })
const { getPrefixCls } = useDesign()
const prefixCls = getPrefixCls('context-menu')
const { t } = useI18n()
const emit = defineEmits(['visibleChange'])
const props = defineProps({
schema: {
type: Array as PropType<contextMenuSchema[]>,
default: () => []
},
trigger: {
type: String as PropType<'click' | 'hover' | 'focus' | 'contextmenu'>,
default: 'contextmenu'
},
tagItem: {
type: Object as PropType<RouteLocationNormalizedLoaded>,
default: () => ({})
}
})
const command = (item: contextMenuSchema) => {
item.command && item.command(item)
}
const visibleChange = (visible: boolean) => {
emit('visibleChange', visible, props.tagItem)
}
const elDropdownMenuRef = ref<ComponentRef<typeof ElDropdown>>()
defineExpose({
elDropdownMenuRef,
tagItem: props.tagItem
})
</script>
<template>
<ElDropdown
ref="elDropdownMenuRef"
:class="prefixCls"
:trigger="trigger"
placement="bottom-start"
popper-class="v-context-menu-popper"
@command="command"
@visible-change="visibleChange"
>
<slot></slot>
<template #dropdown>
<ElDropdownMenu>
<ElDropdownItem
v-for="(item, index) in schema"
:key="`dropdown${index}`"
:command="item"
:disabled="item.disabled"
:divided="item.divided"
>
<Icon :icon="item.icon" />
{{ t(item.label) }}
</ElDropdownItem>
</ElDropdownMenu>
</template>
</ElDropdown>
</template>

View File

@@ -1,3 +0,0 @@
import Footer from './src/Footer.vue'
export { Footer }

View File

@@ -1,24 +0,0 @@
<script lang="ts" setup>
import { useAppStore } from '@/stores/modules/app'
import { useDesign } from '@/hooks/web/useDesign'
// eslint-disable-next-line vue/no-reserved-component-names
defineOptions({ name: 'Footer' })
const { getPrefixCls } = useDesign()
const prefixCls = getPrefixCls('footer')
const appStore = useAppStore()
const title = computed(() => appStore.getTitle)
</script>
<template>
<div
:class="prefixCls"
class="h-[var(--app-footer-height)] bg-[var(--app-content-bg-color)] text-center leading-[var(--app-footer-height)] text-[var(--el-text-color-placeholder)] dark:bg-[var(--el-bg-color)]"
>
<span class="text-14px">Copyright ©2022-{{ title }}</span>
</div>
</template>

View File

@@ -1,3 +0,0 @@
import LocaleDropdown from './src/LocaleDropdown.vue'
export { LocaleDropdown }

View File

@@ -1,52 +0,0 @@
<script lang="ts" setup>
import { useLocaleStore } from '@/stores/modules/locale'
import { useLocale } from '@/hooks/web/useLocale'
import { propTypes } from '@/utils/propTypes'
import { useDesign } from '@/hooks/web/useDesign'
defineOptions({ name: 'LocaleDropdown' })
const { getPrefixCls } = useDesign()
const prefixCls = getPrefixCls('locale-dropdown')
defineProps({
color: propTypes.string.def('')
})
const localeStore = useLocaleStore()
const langMap = computed(() => localeStore.getLocaleMap)
const currentLang = computed(() => localeStore.getCurrentLocale)
const setLang = (lang: LocaleType) => {
if (lang === unref(currentLang).lang) return
// 需要重新加载页面让整个语言多初始化
window.location.reload()
localeStore.setCurrentLocale({
lang
})
const { changeLocale } = useLocale()
changeLocale(lang)
}
</script>
<template>
<ElDropdown :class="prefixCls" trigger="click" @command="setLang">
<Icon
:class="$attrs.class"
:color="color"
:size="18"
class="cursor-pointer !p-0"
icon="ion:language-sharp"
/>
<template #dropdown>
<ElDropdownMenu>
<ElDropdownItem v-for="item in langMap" :key="item.lang" :command="item.lang">
{{ item.name }}
</ElDropdownItem>
</ElDropdownMenu>
</template>
</ElDropdown>
</template>

View File

@@ -1,3 +0,0 @@
import Logo from './src/Logo.vue'
export { Logo }

View File

@@ -1,88 +0,0 @@
<script lang="ts" setup>
import { computed, onMounted, ref, unref, watch } from 'vue'
import { useAppStore } from '@/stores/modules/app'
import { useDesign } from '@/hooks/web/useDesign'
defineOptions({ name: 'Logo' })
const { getPrefixCls } = useDesign()
const prefixCls = getPrefixCls('logo')
const appStore = useAppStore()
const show = ref(true)
const title = computed(() => appStore.getTitle)
const layout = computed(() => appStore.getLayout)
const collapse = computed(() => appStore.getCollapse)
onMounted(() => {
if (unref(collapse)) show.value = false
})
watch(
() => collapse.value,
(collapse: boolean) => {
if (unref(layout) === 'topLeft' || unref(layout) === 'cutMenu') {
show.value = true
return
}
if (!collapse) {
setTimeout(() => {
show.value = !collapse
}, 400)
} else {
show.value = !collapse
}
}
)
watch(
() => layout.value,
(layout) => {
if (layout === 'top' || layout === 'cutMenu') {
show.value = true
} else {
if (unref(collapse)) {
show.value = false
} else {
show.value = true
}
}
}
)
</script>
<template>
<div>
<router-link
:class="[
prefixCls,
layout !== 'classic' ? `${prefixCls}__Top` : '',
'flex !h-[var(--logo-height)] items-center cursor-pointer pl-8px relative decoration-none overflow-hidden'
]"
to="/"
>
<img
class="h-[calc(var(--logo-height)-10px)] w-[calc(var(--logo-height)-10px)]"
src="@/assets/imgs/logo.png"
/>
<div
v-if="show"
:class="[
'ml-10px text-16px font-700',
{
'text-[var(--logo-title-text-color)]': layout === 'classic',
'text-[var(--top-header-text-color)]':
layout === 'topLeft' || layout === 'top' || layout === 'cutMenu'
}
]"
>
{{ title }}
</div>
</router-link>
</div>
</template>

View File

@@ -1,3 +0,0 @@
import Menu from './src/Menu.vue'
export { Menu }

View File

@@ -1,257 +0,0 @@
<script lang="tsx">
import { PropType } from 'vue'
import { ElMenu, ElScrollbar } from 'element-plus'
import { useAppStore } from '@/stores/modules/app'
import { usePermissionStore } from '@/stores/modules/permission'
import { useRenderMenuItem } from './components/useRenderMenuItem'
import { isUrl } from '@/utils/is'
import { useDesign } from '@/hooks/web/useDesign'
import { LayoutType } from '@/types/layout'
const { getPrefixCls } = useDesign()
const prefixCls = getPrefixCls('menu')
export default defineComponent({
// eslint-disable-next-line vue/no-reserved-component-names
name: 'Menu',
props: {
menuSelect: {
type: Function as PropType<(index: string) => void>,
default: undefined
}
},
setup(props) {
const appStore = useAppStore()
const layout = computed(() => appStore.getLayout)
const { push, currentRoute } = useRouter()
const permissionStore = usePermissionStore()
const menuMode = computed((): 'vertical' | 'horizontal' => {
// 竖
const vertical: LayoutType[] = ['classic', 'topLeft', 'cutMenu']
if (vertical.includes(unref(layout))) {
return 'vertical'
} else {
return 'horizontal'
}
})
const routers = computed(() =>
unref(layout) === 'cutMenu' ? permissionStore.getMenuTabRouters : permissionStore.getRouters
)
const collapse = computed(() => appStore.getCollapse)
const uniqueOpened = computed(() => appStore.getUniqueOpened)
const activeMenu = computed(() => {
const { meta, path } = unref(currentRoute)
// if set path, the sidebar will highlight the path you set
if (meta.activeMenu) {
return meta.activeMenu as string
}
return path
})
const menuSelect = (index: string) => {
if (props.menuSelect) {
props.menuSelect(index)
}
// 自定义事件
if (isUrl(index)) {
window.open(index)
} else {
push(index)
}
}
const renderMenuWrap = () => {
if (unref(layout) === 'top') {
return renderMenu()
} else {
return <ElScrollbar>{renderMenu()}</ElScrollbar>
}
}
const renderMenu = () => {
return (
<ElMenu
defaultActive={unref(activeMenu)}
mode={unref(menuMode)}
collapse={
unref(layout) === 'top' || unref(layout) === 'cutMenu' ? false : unref(collapse)
}
uniqueOpened={unref(layout) === 'top' ? false : unref(uniqueOpened)}
backgroundColor="var(--left-menu-bg-color)"
textColor="var(--left-menu-text-color)"
activeTextColor="var(--left-menu-text-active-color)"
onSelect={menuSelect}
>
{{
default: () => {
const { renderMenuItem } = useRenderMenuItem(unref(menuMode))
return renderMenuItem(unref(routers))
}
}}
</ElMenu>
)
}
return () => (
<div
id={prefixCls}
class={[
`${prefixCls} ${prefixCls}__${unref(menuMode)}`,
'h-[100%] overflow-hidden flex-col bg-[var(--left-menu-bg-color)]',
{
'w-[var(--left-menu-min-width)]': unref(collapse) && unref(layout) !== 'cutMenu',
'w-[var(--left-menu-max-width)]': !unref(collapse) && unref(layout) !== 'cutMenu'
}
]}
>
{renderMenuWrap()}
</div>
)
}
})
</script>
<style lang="scss" scoped>
$prefix-cls: v-menu;
.#{$prefix-cls} {
position: relative;
transition: width var(--transition-time-02);
:deep(.el-menu) {
width: 100% !important;
border-right: none;
// 设置选中时子标题的颜色
.is-active {
& > .el-sub-menu__title {
color: var(--left-menu-text-active-color) !important;
}
}
// 设置子菜单悬停的高亮和背景色
.el-sub-menu__title,
.el-menu-item {
&:hover {
color: var(--left-menu-text-active-color) !important;
background-color: var(--left-menu-bg-color) !important;
}
}
// 设置选中时的高亮背景和高亮颜色
.el-menu-item.is-active {
color: var(--left-menu-text-active-color) !important;
background-color: var(--left-menu-bg-active-color) !important;
&:hover {
background-color: var(--left-menu-bg-active-color) !important;
}
}
.el-menu-item.is-active {
position: relative;
}
// 设置子菜单的背景颜色
.el-menu {
.el-sub-menu__title,
.el-menu-item:not(.is-active) {
background-color: var(--left-menu-bg-light-color) !important;
}
}
}
// 折叠时的最小宽度
:deep(.el-menu--collapse) {
width: var(--left-menu-min-width);
& > .is-active,
& > .is-active > .el-sub-menu__title {
position: relative;
background-color: var(--left-menu-collapse-bg-active-color) !important;
}
}
// 折叠动画的时候,就需要把文字给隐藏掉
:deep(.horizontal-collapse-transition) {
// transition: 0s width ease-in-out, 0s padding-left ease-in-out, 0s padding-right ease-in-out !important;
.#{$prefix-cls}__title {
display: none;
}
}
// 水平菜单
&__horizontal {
height: calc(var(--top-tool-height)) !important;
:deep(.el-menu--horizontal) {
height: calc(var(--top-tool-height));
border-bottom: none;
// 重新设置底部高亮颜色
& > .el-sub-menu.is-active {
.el-sub-menu__title {
border-bottom-color: var(--el-color-primary) !important;
}
}
.el-menu-item.is-active {
position: relative;
&::after {
display: none !important;
}
}
.#{$prefix-cls}__title {
/* stylelint-disable-next-line */
max-height: calc(var(--top-tool-height) - 2px) !important;
/* stylelint-disable-next-line */
line-height: calc(var(--top-tool-height) - 2px);
}
}
}
}
</style>
<style lang="scss">
$prefix-cls: v-menu-popper;
.#{$prefix-cls}--vertical,
.#{$prefix-cls}--horizontal {
// 设置选中时子标题的颜色
.is-active {
& > .el-sub-menu__title {
color: var(--left-menu-text-active-color) !important;
}
}
// 设置子菜单悬停的高亮和背景色
.el-sub-menu__title,
.el-menu-item {
&:hover {
color: var(--left-menu-text-active-color) !important;
background-color: var(--left-menu-bg-color) !important;
}
}
// 设置选中时的高亮背景
.el-menu-item.is-active {
position: relative;
background-color: var(--left-menu-bg-active-color) !important;
&:hover {
background-color: var(--left-menu-bg-active-color) !important;
}
}
}
</style>

View File

@@ -1,50 +0,0 @@
import { ElSubMenu, ElMenuItem } from 'element-plus'
import { hasOneShowingChild } from '../helper'
import { isUrl } from '@/utils/is'
import { useRenderMenuTitle } from './useRenderMenuTitle'
import { pathResolve } from '@/utils/routerHelper'
const { renderMenuTitle } = useRenderMenuTitle()
export const useRenderMenuItem = () =>
// allRouters: AppRouteRecordRaw[] = [],
{
const renderMenuItem = (routers: AppRouteRecordRaw[], parentPath = '/') => {
return routers
.filter((v) => !v.meta?.hidden)
.map((v) => {
const meta = v.meta ?? {}
const { oneShowingChild, onlyOneChild } = hasOneShowingChild(v.children, v)
const fullPath = isUrl(v.path) ? v.path : pathResolve(parentPath, v.path) // getAllParentPath<AppRouteRecordRaw>(allRouters, v.path).join('/')
if (
oneShowingChild &&
(!onlyOneChild?.children || onlyOneChild?.noShowingChildren) &&
!meta?.alwaysShow
) {
return (
<ElMenuItem
index={onlyOneChild ? pathResolve(fullPath, onlyOneChild.path) : fullPath}
>
{{
default: () => renderMenuTitle(onlyOneChild ? onlyOneChild?.meta : meta)
}}
</ElMenuItem>
)
} else {
return (
<ElSubMenu index={fullPath}>
{{
title: () => renderMenuTitle(meta),
default: () => renderMenuItem(v.children!, fullPath)
}}
</ElSubMenu>
)
}
})
}
return {
renderMenuItem
}
}

View File

@@ -1,27 +0,0 @@
import type { RouteMeta } from 'vue-router'
import { Icon } from '@/components/Icon'
import { useI18n } from '@/hooks/web/useI18n'
export const useRenderMenuTitle = () => {
const renderMenuTitle = (meta: RouteMeta) => {
const { t } = useI18n()
const { title = 'Please set title', icon } = meta
return icon ? (
<>
<Icon icon={meta.icon}></Icon>
<span class="v-menu__title overflow-hidden overflow-ellipsis whitespace-nowrap">
{t(title as string)}
</span>
</>
) : (
<span class="v-menu__title overflow-hidden overflow-ellipsis whitespace-nowrap">
{t(title as string)}
</span>
)
}
return {
renderMenuTitle
}
}

Some files were not shown because too many files have changed in this diff Show More