66 Commits

Author SHA1 Message Date
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 52840 additions and 58768 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

156
README.md
View File

@@ -1,78 +1,78 @@
#### 介绍 #### 介绍
``` ```
Vue 3 + TypeScript + Vite这个模板可以帮助您开始使用Vue 3和TypeScript在Vite中进行开发。该模板使用了Vue 3的<script setup>单文件组件请查看script setup文档了解更多信息。 Vue 3 + TypeScript + Vite这个模板可以帮助您开始使用Vue 3和TypeScript在Vite中进行开发。该模板使用了Vue 3的<script setup>单文件组件请查看script setup文档了解更多信息。
``` ```
#### 安装依赖&运行项目 #### 安装依赖&运行项目
> node version:^18.17.0 || >=20.5.0" > node version:^18.17.0 || >=20.5.0"
```shell ```shell
#项目使用pnpm包管理器 #项目使用pnpm包管理器
npm i pnpm -g npm i pnpm -g
#安装依赖 #安装依赖
pnpm i pnpm i
#运行项目 #运行项目
npm run dev npm run dev
#冀北现场启动命令 区分是否加载现场电网一张图内网api #jb现场启动命令 区分是否加载现场电网一张图内网api
npm run dev:jibei npm run dev:jibei
#去除后台所有模块名 如除/xxxx-boot #去除后台所有模块名 如除/xxxx-boot
npm run dev:removeMode 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
``` ```
#### 页面编写-示例 #### 页面编写-示例
``` ```
基础页面写法请查看`src/template`下的readme.md 基础页面写法请查看`src/template`下的readme.md
``` ```
#### 开发规范 #### 开发规范
> 初衷:养成合理的习惯,精力留在技术调研、业务开发中。 > 初衷:养成合理的习惯,精力留在技术调研、业务开发中。
> >
> 常见点如下描述,更多的规范待前端开发人员有时间后,慢慢丰富。 > 常见点如下描述,更多的规范待前端开发人员有时间后,慢慢丰富。
- 命名风格:所有的包(文件夹)、文件名以小驼峰的风格命名,比如 xxxAaa禁止 XxxAaa 或者 xxx-aaa。 - 命名风格:所有的包(文件夹)、文件名以小驼峰的风格命名,比如 xxxAaa禁止 XxxAaa 或者 xxx-aaa。
- 命名语义:禁止中文命名或拼音,英文命名借用下工具,稍微准确一点,不要与实际业务相差太远。 - 命名语义:禁止中文命名或拼音,英文命名借用下工具,稍微准确一点,不要与实际业务相差太远。
- 功能组件创建风格:以**功能名称**命名文件夹,每个功能下以**index.vue**作为该功能的组件入口。正确示例参考:/src/views/auth/menu/index.vue。 - 功能组件创建风格:以**功能名称**命名文件夹,每个功能下以**index.vue**作为该功能的组件入口。正确示例参考:/src/views/auth/menu/index.vue。
![组件层级图](./src/assets/readme/moduleLevel.png) ![组件层级图](./src/assets/readme/moduleLevel.png)
- todo...:待后续补充。 - todo...:待后续补充。
#### 开发助手 #### 开发助手
##### 1、表格系列 ##### 1、表格系列
- 页面表格以及表格页面按钮的弹出等功能参考:/views/pqs/voltageSags/sagGovern/index.vue - 页面表格以及表格页面按钮的弹出等功能参考:/views/pqs/voltageSags/sagGovern/index.vue
- 表格中需要替换数据: - 表格中需要替换数据:
```js ```js
// 通过formatter函数返回实际需要返回的值 // 通过formatter函数返回实际需要返回的值
{ {
title: '数据来源', field: 'dataSource', minWidth: '130', title: '数据来源', field: 'dataSource', minWidth: '130',
formatter: (row: any) => { formatter: (row: any) => {
return row.dataSource == 0 ? '离线导入' : '监测点同步' return row.dataSource == 0 ? '离线导入' : '监测点同步'
} }
}**** }****
``` ```
##### 2、样式系列 ##### 2、样式系列
- 获取当前主体的高度import { mainHeight } from '@/utils/layout' - 获取当前主体的高度import { mainHeight } from '@/utils/layout'
- 弹框内输入框长度设置 1 行 1 个 class="form-one" 1 行 2 个 class="form-two" - 弹框内输入框长度设置 1 行 1 个 class="form-one" 1 行 2 个 class="form-two"
#### 依赖变更记录 #### 依赖变更记录
- 2024-04-25 新增 vform3by 洪圣文,用于页面表单设计保存为 json命令pnpm i vform3-builds - 2024-04-25 新增 vform3by 洪圣文,用于页面表单设计保存为 json命令pnpm i vform3-builds

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"
}
} }

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

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,47 +1,41 @@
import request from '@/config/axios' import { requestData } from '@/utils/request'
export type UserGroupVO = { export type UserGroupVO = {
id: number id: number
name: string name: string
description: string description: string
userIds: number[] userIds: number[]
status: number status: number
remark: string remark: string
createTime: string createTime: string
} }
// 创建用户组 // 创建用户组
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) => {
return await requestData({ url: '/bpm/user-group/update', method: 'PUT', data })
// 更新用户组 }
export const updateUserGroup = async (data: UserGroupVO) => {
return await request.put({ // 删除用户组
url: '/bpm/user-group/update', export const deleteUserGroup = async (id: number) => {
data: data return await requestData({ url: '/bpm/user-group/delete?id=' + id, method: 'DELETE' })
}) }
}
// 获得用户组
// 删除用户组 export const getUserGroup = async (id: number) => {
export const deleteUserGroup = async (id: number) => { return await requestData({ url: '/bpm/user-group/get?id=' + id, method: 'GET' })
return await request.delete({ url: '/bpm/user-group/delete?id=' + id }) }
}
// 获得用户组分页
// 获得用户组 export const getUserGroupPage = async (params) => {
export const getUserGroup = async (id: number) => { return await requestData({ url: '/bpm/user-group/page', method: 'GET', params })
return await request.get({ url: '/bpm/user-group/get?id=' + id }) }
}
// 获取用户组精简信息列表
// 获得用户组分页 export const getUserGroupSimpleList = async (): Promise<UserGroupVO[]> => {
export const getUserGroupPage = async (params) => { return await requestData({ url: '/bpm/user-group/simple-list', method: 'GET' })
return await request.get({ url: '/bpm/user-group/page', params }) }
}
// 获取用户组精简信息列表
export const getUserGroupSimpleList = async (): Promise<UserGroupVO[]> => {
return await request.get({ url: '/bpm/user-group/simple-list' })
}

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

@@ -31,4 +31,13 @@ export function getDevTypeList() {
method: 'post', method: 'post',
}) })
}
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

@@ -1,106 +1,114 @@
import request from '@/utils/request' import request from '@/utils/request'
export function getTypeIdData(data: any) { export function getTypeIdData(data: any) {
return request({ return request({
url: '/system-boot/dictData/getTypeIdData', url: '/system-boot/dictData/getTypeIdData',
method: 'post', method: 'post',
data: data data: data
}) })
} }
export function getOnlineRateData2(data:any) { export function getOnlineRateData2(data:any) {
return request({ return request({
url: '/harmonic-boot/onlineRateData/getOnlineRateData', url: '/harmonic-boot/onlineRateData/getOnlineRateData',
method: 'post', method: 'post',
data: data data: data
}) })
} }
export function getAreaDept() { export function getAreaDept() {
return request({ return request({
url: '/user-boot/dept/loginDeptTree', url: '/user-boot/dept/loginDeptTree',
method: 'post' method: 'post'
}) })
} }
export function getOnlineRateDataCensus(data:any) { // 承载能力评估去除前缀
return request({ export function getAreaDeptRemoveMode(params) {
url: '/device-boot/terminalOnlineRateData/getOnlineRateDataCensus', return request({
method: 'post', url: '/user-boot/dept/loginDeptTree',
data: data method: 'GET',
}) params: params
} })
export function IntegrityIcon(data:any) { }
return request({ export function getOnlineRateDataCensus(data:any) {
url: '/harmonic-boot/integrity/getIntegrityIcon', return request({
method: 'post', url: '/device-boot/terminalOnlineRateData/getOnlineRateDataCensus',
data: data method: 'post',
}) data: data
} })
export function getDeptIdAreaTree() { }
return request({ export function IntegrityIcon(data:any) {
url: '/system-boot/area/getDeptIdAreaTree', return request({
method: 'post' url: '/harmonic-boot/integrity/getIntegrityIcon',
}) method: 'post',
} data: data
export function getOnlineRateData(data:any) { })
return request({ }
url: '/device-boot/terminalOnlineRateData/getOnlineRateData', export function getDeptIdAreaTree() {
method: 'post', return request({
data: data url: '/system-boot/area/getDeptIdAreaTree',
}) method: 'post'
} })
export function getSubstationInfoById(data:any) { }
return request({ export function getOnlineRateData(data:any) {
url: '/harmonic-boot/PollutionSubstation/getSubstationInfoById', return request({
method: 'post', url: '/device-boot/terminalOnlineRateData/getOnlineRateData',
data: data method: 'post',
}) data: data
} })
export function getLineInfoById(data:any) { }
return request({ export function getSubstationInfoById(data:any) {
url: '/harmonic-boot/PollutionSubstation/getLineInfoById', return request({
method: 'post', url: '/harmonic-boot/PollutionSubstation/getSubstationInfoById',
data: data method: 'post',
}) data: data
} })
export function getLineRank(data:any) { }
return request({ export function getLineInfoById(data:any) {
url: '/harmonic-boot/PollutionSubstation/getLineRank', return request({
method: 'post', url: '/harmonic-boot/PollutionSubstation/getLineInfoById',
data: data method: 'post',
}) data: data
} })
export function deptInfo(data:any) { }
return request({ export function getLineRank(data:any) {
url: '/harmonic-boot/detailAnalysis/deptInfo', return request({
method: 'post', url: '/harmonic-boot/PollutionSubstation/getLineRank',
data: data method: 'post',
}) data: data
} })
export function getXbLineInfoById(data:any) { }
return request({ export function deptInfo(data:any) {
url: '/harmonic-boot/detailAnalysis/getXbLineInfoById', return request({
method: 'post', url: '/harmonic-boot/detailAnalysis/deptInfo',
data: data method: 'post',
}) data: data
} })
export function getSubInfoById(data:any) { }
return request({ export function getXbLineInfoById(data:any) {
url: '/harmonic-boot/detailAnalysis/getSubInfoById', return request({
method: 'post', url: '/harmonic-boot/detailAnalysis/getXbLineInfoById',
data: data method: 'post',
}) data: data
} })
export function getXbLineRank(data:any) { }
return request({ export function getSubInfoById(data:any) {
url: '/harmonic-boot/detailAnalysis/getXbLineRank', return request({
method: 'post', url: '/harmonic-boot/detailAnalysis/getSubInfoById',
data: data method: 'post',
}) data: data
} })
// 数据补招 }
export function FullRecall(data:any) { export function getXbLineRank(data:any) {
return request({ return request({
url: '/data-processing-boot/data/FullRecall', url: '/harmonic-boot/detailAnalysis/getXbLineRank',
method: 'post', method: 'post',
data: data data: data
}) })
} }
// 数据补招
export function FullRecall(data:any) {
return request({
url: '/data-processing-boot/data/FullRecall',
method: 'post',
data: data
})
}

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,49 +1,49 @@
import request from '@/config/axios' import { requestData, requestDownload } from '@/utils/request'
export type DictDataVO = { export type DictDataVO = {
id: number | undefined id: number | undefined
sort: number | undefined sort: number | undefined
label: string label: string
value: string value: string
dictType: string dictType: string
status: number status: number
colorType: string colorType: string
cssClass: string cssClass: string
remark: string remark: string
createTime: Date createTime: Date
} }
// 查询字典数据(精简)列表 // 查询字典数据(精简)列表
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,44 +1,45 @@
import request from '@/config/axios' import { requestData, requestDownload } from '@/utils/request'
export type DictTypeVO = { export type DictTypeVO = {
id: number | undefined id: number | undefined
name: string name: string
type: string type: string
status: number status: number
remark: string remark: string
createTime: Date createTime: Date
} }
// 查询字典(精简)列表 // 查询字典(精简)列表
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) => { // 导出字典类型
return request.download({ url: '/system/dict-type/export', params }) export const exportDictType = (params) => {
} return requestDownload({ url: '/system/dict-type/export', method: 'GET', params, responseType: 'blob' })
}

View File

@@ -1,49 +1,50 @@
import request from '@/config/axios' import { requestData } from '@/utils/request'
import qs from 'qs' import qs from 'qs'
export interface NotifyMessageVO { export interface NotifyMessageVO {
id: number id: number
userId: number userId: number
userType: number userType: number
templateId: number templateId: number
templateCode: string templateCode: string
templateNickname: string templateNickname: string
templateContent: string templateContent: string
templateType: number templateType: number
templateParams: string templateParams: string
readStatus: boolean readStatus: boolean
readTime: Date readTime: Date
createTime: Date createTime: Date
} }
// 查询站内信消息列表 // 查询站内信消息列表
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 () => { // 标记所有站内信为已读
return await request.put({ url: '/system/notify-message/update-all-read' }) export const updateAllNotifyMessageRead = async () => {
} return await requestData({ url: '/system/notify-message/update-all-read', method: 'PUT' })
}
// 获取当前用户的最新站内信列表
export const getUnreadNotifyMessageList = async () => { // 获取当前用户的最新站内信列表
return await request.get({ url: '/system/notify-message/get-unread-list' }) export const getUnreadNotifyMessageList = async () => {
} return await requestData({ url: '/system/notify-message/get-unread-list', method: 'GET' })
}
// 获得当前用户的未读站内信数量
export const getUnreadNotifyMessageCount = async () => { // 获得当前用户的未读站内信数量
return await request.get({ url: '/system/notify-message/get-unread-count' }) export const getUnreadNotifyMessageCount = async () => {
} return await requestData({ url: '/system/notify-message/get-unread-count', method: 'GET' })
}

View File

@@ -1,49 +1,49 @@
import request from '@/config/axios' import { requestData } from '@/utils/request'
export interface NotifyTemplateVO { export interface NotifyTemplateVO {
id?: number id?: number
name: string name: string
nickname: string nickname: string
code: string code: string
content: string content: string
type?: number type?: number
params: string params: string
status: number status: number
remark: string remark: string
} }
export interface NotifySendReqVO { export interface NotifySendReqVO {
userId: number | null userId: number | null
templateCode: string templateCode: string
templateParams: Map<String, Object> templateParams: Map<String, Object>
} }
// 查询站内信模板列表 // 查询站内信模板列表
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

@@ -1,215 +1,250 @@
import request from '@/utils/request' import request from '@/utils/request'
import { LoginData } from '@/api/types' import { LoginData } from '@/api/types'
import { useAdminInfo } from '@/stores/adminInfo' import { useAdminInfo } from '@/stores/adminInfo'
import { sm3Digest } from '@/assets/commjs/sm3.js' import { sm3Digest } from '@/assets/commjs/sm3.js'
import { sm2, encrypt } from '@/assets/commjs/sm2.js' import { sm2, encrypt } from '@/assets/commjs/sm2.js'
// 获取公钥 // 获取公钥
export function gongkey(params?: any) { export function gongkey(params?: any) {
if (!params) { if (!params) {
const adminInfo = useAdminInfo() const adminInfo = useAdminInfo()
params = { params = {
loginName: encrypt(adminInfo.$state.loginName) loginName: encrypt(adminInfo.$state.loginName)
} }
} }
return request({ return request({
url: '/user-boot/user/generateSm2Key', url: '/user-boot/user/generateSm2Key',
method: 'get', method: 'get',
params params
}) })
} }
export async function pwdSm3(pwd: any, loginName?: string) { export async function pwdSm3(pwd: any, loginName?: string) {
let publicKey = await gongkey( let publicKey = await gongkey(
loginName loginName
? { ? {
loginName: encrypt(loginName) loginName: encrypt(loginName)
} }
: false : false
) )
let sm3Pwd = sm3Digest(pwd) //SM3加密 let sm3Pwd = sm3Digest(pwd) //SM3加密
return sm2(sm3Pwd + '|' + pwd, publicKey.data, 0) return sm2(sm3Pwd + '|' + pwd, publicKey.data, 0)
} }
//登录获取token //登录获取token
export async function login(params: any) { export async function login(params: any) {
if (params.refresh_token == undefined) { if (params.refresh_token == undefined) {
params.password = await pwdSm3(params.password, params.username) params.password = await pwdSm3(params.password, params.username)
} }
params.username = encrypt(params.username) params.username = encrypt(params.username)
return request({ return request({
url: '/pqs-auth/oauth/token', url: '/pqs-auth/oauth/token',
method: 'post', method: 'post',
params params
}) })
} }
//辽宁嵌入登录获取token
//获取用户信息 export async function loginLNqr(data: any) {
export function getUserById() { return request({
const adminInfo = useAdminInfo() url: '/pqs-auth/oauth/lnLogin',
return request({ method: 'get',
url: '/user-boot/user/getUserById?id=' + adminInfo.userIndex, params: data
method: 'get' })
}) }
}
export async function lnRefreshToken(data: any) {
// 刷新token return request({
// 导出一个名为refreshToken的函数该函数返回一个Promise对象 url: '/pqs-auth/oauth/lnRefreshToken',
export function refreshToken(): Promise<any> { method: 'get',
// 调用useAdminInfo函数获取管理员信息并将其赋值给adminInfo变量 params: data
const adminInfo = useAdminInfo() })
// 调用login函数传入一个对象作为参数该对象包含grant_type、refresh_token和username三个属性 }
// grant_type设置为'refresh_token',表示使用刷新令牌的方式获取新的访问令牌
// refresh_token使用adminInfo中的refresh_token属性 export async function logout() {
// username使用adminInfo中的loginName属性 return request({
// 返回login函数的调用结果即一个Promise对象 url: '/pqs-auth/oauth/logout',
return login({ method: 'DELETE',
grant_type: 'refresh_token', })
refresh_token: adminInfo.refresh_token, }
username: adminInfo.loginName
})
} //获取用户信息
export function getUserById() {
/** const adminInfo = useAdminInfo()
* 获取营销用户列表 return request({
* @returns {AxiosPromise} url: '/user-boot/user/getUserById?id=' + adminInfo.userIndex,
*/ method: 'get'
export const getMarketList = () => { })
return request({ }
url: '/user-boot/user/getMarketList',
method: 'post' // 刷新token
}) // 导出一个名为refreshToken的函数该函数返回一个Promise对象
} export function refreshToken(): Promise<any> {
// 调用useAdminInfo函数获取管理员信息并将其赋值给adminInfo变量
export function add(data: any) { const adminInfo = useAdminInfo()
return request({ // 调用login函数传入一个对象作为参数该对象包含grant_type、refresh_token和username三个属性
url: '/user-boot/user/add', // grant_type设置为'refresh_token',表示使用刷新令牌的方式获取新的访问令牌
method: 'post', // refresh_token使用adminInfo中的refresh_token属性
data: data // username使用adminInfo中的loginName属性
}) // 返回login函数的调用结果即一个Promise对象
} return login({
grant_type: 'refresh_token',
export function edit(data: any) { refresh_token: adminInfo.refresh_token,
return request({ username: adminInfo.loginName
url: '/user-boot/user/update', })
method: 'put', }
data: data
})
} export function lnRefreshTokenTo(): Promise<any> {
// 调用useAdminInfo函数获取管理员信息并将其赋值给adminInfo变量
export async function passwordConfirm(pwd: string) { const adminInfo = useAdminInfo()
return request({ return lnRefreshToken({
url: '/user-boot/user/passwordConfirm?password=' + (await pwdSm3(pwd)), refreshToken: adminInfo.refresh_token,
method: 'get' clientId: "njcn",
}) clientSecret:"njcnpqs"
} })
}
export function deluser(data: any) {
return request({ /**
url: '/user-boot/user/delete', * 获取营销用户列表
method: 'delete', * @returns {AxiosPromise}
params: data */
}) export const getMarketList = () => {
} return request({
url: '/user-boot/user/getMarketList',
export function activateUser(data: any) { method: 'post'
return request({ })
url: '/user-boot/user/activateUser', }
method: 'put',
params: data export function add(data: any) {
}) return request({
} url: '/user-boot/user/add',
method: 'post',
export async function updatePassword(params: any) { data: data
return request({ })
url: '/user-boot/user/updatePassword', }
method: 'put',
params: { export function edit(data: any) {
id: params.id, return request({
newPassword: await pwdSm3(params.newPassword) url: '/user-boot/user/update',
} method: 'put',
}) data: data
} })
}
export async function updateFirstPassword(params: any) {
return request({ export async function passwordConfirm(pwd: string) {
url: '/user-boot/user/updateFirstPassword', return request({
method: 'put', url: '/user-boot/user/passwordConfirm?password=' + (await pwdSm3(pwd)),
data: { method: 'get'
name: encrypt(params.name), })
password: await pwdSm3(params.password, params.name) }
}
}) export function deluser(data: any) {
} return request({
url: '/user-boot/user/delete',
export function checkUser(data: any) { method: 'delete',
return request({ params: data
url: '/user-boot/user/check', })
method: 'put', }
data: data
}) export function activateUser(data: any) {
} return request({
export function existMonitorDeptTree() { url: '/user-boot/user/activateUser',
return request({ method: 'put',
url: '/user-boot/dept/existMonitorDeptTree', params: data
method: 'post' })
}) }
}
// 获取用户 export async function updatePassword(params: any) {
export function getUserByRoleType(data: any) { return request({
return request({ url: '/user-boot/user/updatePassword',
url: '/user-boot/user/getUserByRoleType?roleType=' + data, method: 'put',
method: 'GET' params: {
}) id: params.id,
} newPassword: await pwdSm3(params.newPassword)
}
// 获取部门下所有用户 })
export function listAllUserByDeptId(data: any) { }
return request({
url: '/user-boot/user/listAllUserByDeptId?deptId=' + data, export async function updateFirstPassword(params: any) {
method: 'GET' return request({
}) url: '/user-boot/user/updateFirstPassword',
} method: 'put',
data: {
// 根据id获取所有用户信息 name: encrypt(params.name),
export function getUserListByIds(data: any) { password: await pwdSm3(params.password, params.name)
return request({ }
url: '/user-boot/user/getUserListByIds', })
method: 'POST', }
data
}) export function checkUser(data: any) {
} return request({
url: '/user-boot/user/check',
/** method: 'put',
* 查询所有用户不包括管理员 data: data
*/ })
export const getUserSimpleList = () => { }
return request({ export function existMonitorDeptTree() {
url: '/user-boot/user/simpleList', return request({
method: 'GET' url: '/user-boot/dept/existMonitorDeptTree',
}) method: 'post'
} })
}
/** // 获取用户
* 查询所有用户包括管理员 export function getUserByRoleType(data: any) {
*/ return request({
export const getAllUserSimpleList = () => { url: '/user-boot/user/getUserByRoleType?roleType=' + data,
return request({ method: 'GET'
url: '/user-boot/user/getAllUserSimpleList', })
method: 'GET' }
})
} // 获取部门下所有用户
export function listAllUserByDeptId(data: any) {
export const getSysConfig = () => { return request({
return request({ url: '/user-boot/user/listAllUserByDeptId?deptId=' + data,
url: '/system-boot/config/getSysConfig', method: 'GET'
method: 'get' })
}) }
}
// 驾驶舱组件配置 // 根据id获取所有用户信息
export const componentTree = () => { export function getUserListByIds(data: any) {
return request({ return request({
url: '/user-boot/component/componentTree', url: '/user-boot/user/getUserListByIds',
method: 'GET' method: 'POST',
}) data
} })
}
/**
* 查询所有用户不包括管理员
*/
export const getUserSimpleList = () => {
return request({
url: '/user-boot/user/simpleList',
method: 'GET'
})
}
/**
* 查询所有用户包括管理员
*/
export const getAllUserSimpleList = () => {
return request({
url: '/user-boot/user/getAllUserSimpleList',
method: 'GET'
})
}
export const getSysConfig = () => {
return request({
url: '/system-boot/config/getSysConfig',
method: 'get'
})
}
// 驾驶舱组件配置
export const componentTree = () => {
return request({
url: '/user-boot/component/componentTree',
method: 'GET'
})
}

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,46 +1,46 @@
<!-- 数据字典 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)"
:key="index" :key="index"
:label="dict.label" :label="dict.label"
:value="dict.value" :value="dict.value"
/> />
</template> </template>
<template v-if="valueType === 'str'"> <template v-if="valueType === 'str'">
<el-option <el-option
v-for="(dict, index) in getStrDictOptions(dictType)" v-for="(dict, index) in getStrDictOptions(dictType)"
:key="index" :key="index"
:label="dict.label" :label="dict.label"
:value="dict.value" :value="dict.value"
/> />
</template> </template>
<template v-if="valueType === 'bool'"> <template v-if="valueType === 'bool'">
<el-option <el-option
v-for="(dict, index) in getBoolDictOptions(dictType)" v-for="(dict, index) in getBoolDictOptions(dictType)"
:key="index" :key="index"
:label="dict.label" :label="dict.label"
:value="dict.value" :value="dict.value"
/> />
</template> </template>
</el-select> </el-select>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { getBoolDictOptions, getIntDictOptions, getStrDictOptions } from '@/utils/dict' import { getBoolDictOptions, getIntDictOptions, getStrDictOptions } from '@/utils/dict'
// 接受父组件参数 // 接受父组件参数
interface Props { interface Props {
dictType: string // 字典类型 dictType: string // 字典类型
valueType: string // 字典值类型 valueType: string // 字典值类型
} }
withDefaults(defineProps<Props>(), { withDefaults(defineProps<Props>(), {
dictType: '', dictType: '',
valueType: 'str' valueType: 'str'
}) })
const attrs = useAttrs() const attrs = useAttrs()
defineOptions({ name: 'DictSelect' }) defineOptions({ name: 'DictSelect' })
</script> </script>

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

@@ -1,481 +1,481 @@
<template> <template>
<div class="panel-tab__content"> <div class="panel-tab__content">
<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>
<!-- <el-form-item label="业务标识">--> <!-- <el-form-item label="业务标识">-->
<!-- <el-select v-model="businessKey" @change="updateElementBusinessKey">--> <!-- <el-select v-model="businessKey" @change="updateElementBusinessKey">-->
<!-- <el-option v-for="i in fieldList" :key="i.id" :value="i.id" :label="i.label" />--> <!-- <el-option v-for="i in fieldList" :key="i.id" :value="i.id" :label="i.label" />-->
<!-- <el-option label="无" value="" />--> <!-- <el-option label="无" value="" />-->
<!-- </el-select>--> <!-- </el-select>-->
<!-- </el-form-item>--> <!-- </el-form-item>-->
</el-form> </el-form>
<!--字段列表--> <!--字段列表-->
<!-- <div class="element-property list-property">--> <!-- <div class="element-property list-property">-->
<!-- <el-divider><Icon icon="ep:coin" /> 表单字段</el-divider>--> <!-- <el-divider><Icon icon="ep:coin" /> 表单字段</el-divider>-->
<!-- <el-table :data="fieldList" max-height="240" fit border>--> <!-- <el-table :data="fieldList" max-height="240" fit border>-->
<!-- <el-table-column label="序号" type="index" width="50px" />--> <!-- <el-table-column label="序号" type="index" width="50px" />-->
<!-- <el-table-column label="字段名称" prop="label" min-width="80px" show-overflow-tooltip />--> <!-- <el-table-column label="字段名称" prop="label" min-width="80px" show-overflow-tooltip />-->
<!-- <el-table-column--> <!-- <el-table-column-->
<!-- label="字段类型"--> <!-- label="字段类型"-->
<!-- prop="type"--> <!-- prop="type"-->
<!-- min-width="80px"--> <!-- min-width="80px"-->
<!-- :formatter="(row) => fieldType[row.type] || row.type"--> <!-- :formatter="(row) => fieldType[row.type] || row.type"-->
<!-- show-overflow-tooltip--> <!-- show-overflow-tooltip-->
<!-- />--> <!-- />-->
<!-- <el-table-column--> <!-- <el-table-column-->
<!-- label="默认值"--> <!-- label="默认值"-->
<!-- prop="defaultValue"--> <!-- prop="defaultValue"-->
<!-- min-width="80px"--> <!-- min-width="80px"-->
<!-- show-overflow-tooltip--> <!-- show-overflow-tooltip-->
<!-- />--> <!-- />-->
<!-- <el-table-column label="操作" width="90px">--> <!-- <el-table-column label="操作" width="90px">-->
<!-- <template #default="scope">--> <!-- <template #default="scope">-->
<!-- <el-button type="primary" link @click="openFieldForm(scope, scope.$index)"--> <!-- <el-button type="primary" link @click="openFieldForm(scope, scope.$index)"-->
<!-- >编辑</el-button--> <!-- >编辑</el-button-->
<!-- >--> <!-- >-->
<!-- <el-divider direction="vertical" />--> <!-- <el-divider direction="vertical" />-->
<!-- <el-button--> <!-- <el-button-->
<!-- type="primary"--> <!-- type="primary"-->
<!-- link--> <!-- link-->
<!-- style="color: #ff4d4f"--> <!-- style="color: #ff4d4f"-->
<!-- @click="removeField(scope, scope.$index)"--> <!-- @click="removeField(scope, scope.$index)"-->
<!-- >移除</el-button--> <!-- >移除</el-button-->
<!-- >--> <!-- >-->
<!-- </template>--> <!-- </template>-->
<!-- </el-table-column>--> <!-- </el-table-column>-->
<!-- </el-table>--> <!-- </el-table>-->
<!-- </div>--> <!-- </div>-->
<!-- <div class="element-drawer__button">--> <!-- <div class="element-drawer__button">-->
<!-- <XButton type="primary" proIcon="ep:plus" title="添加字段" @click="openFieldForm(null, -1)" />--> <!-- <XButton type="primary" proIcon="ep:plus" title="添加字段" @click="openFieldForm(null, -1)" />-->
<!-- </div>--> <!-- </div>-->
<!--字段配置侧边栏--> <!--字段配置侧边栏-->
<!-- <el-drawer--> <!-- <el-drawer-->
<!-- v-model="fieldModelVisible"--> <!-- v-model="fieldModelVisible"-->
<!-- title="字段配置"--> <!-- title="字段配置"-->
<!-- :size="`${width}px`"--> <!-- :size="`${width}px`"-->
<!-- append-to-body--> <!-- append-to-body-->
<!-- destroy-on-close--> <!-- destroy-on-close-->
<!-- >--> <!-- >-->
<!-- <el-form :model="formFieldForm" label-width="90px">--> <!-- <el-form :model="formFieldForm" label-width="90px">-->
<!-- <el-form-item label="字段ID">--> <!-- <el-form-item label="字段ID">-->
<!-- <el-input v-model="formFieldForm.id" clearable />--> <!-- <el-input v-model="formFieldForm.id" clearable />-->
<!-- </el-form-item>--> <!-- </el-form-item>-->
<!-- <el-form-item label="类型">--> <!-- <el-form-item label="类型">-->
<!-- <el-select--> <!-- <el-select-->
<!-- v-model="formFieldForm.typeType"--> <!-- v-model="formFieldForm.typeType"-->
<!-- placeholder="请选择字段类型"--> <!-- placeholder="请选择字段类型"-->
<!-- clearable--> <!-- clearable-->
<!-- @change="changeFieldTypeType"--> <!-- @change="changeFieldTypeType"-->
<!-- >--> <!-- >-->
<!-- <el-option v-for="(value, key) of fieldType" :label="value" :value="key" :key="key" />--> <!-- <el-option v-for="(value, key) of fieldType" :label="value" :value="key" :key="key" />-->
<!-- </el-select>--> <!-- </el-select>-->
<!-- </el-form-item>--> <!-- </el-form-item>-->
<!-- <el-form-item label="类型名称" v-if="formFieldForm.typeType === 'custom'">--> <!-- <el-form-item label="类型名称" v-if="formFieldForm.typeType === 'custom'">-->
<!-- <el-input v-model="formFieldForm.type" clearable />--> <!-- <el-input v-model="formFieldForm.type" clearable />-->
<!-- </el-form-item>--> <!-- </el-form-item>-->
<!-- <el-form-item label="名称">--> <!-- <el-form-item label="名称">-->
<!-- <el-input v-model="formFieldForm.label" clearable />--> <!-- <el-input v-model="formFieldForm.label" clearable />-->
<!-- </el-form-item>--> <!-- </el-form-item>-->
<!-- <el-form-item label="时间格式" v-if="formFieldForm.typeType === 'date'">--> <!-- <el-form-item label="时间格式" v-if="formFieldForm.typeType === 'date'">-->
<!-- <el-input v-model="formFieldForm.datePattern" clearable />--> <!-- <el-input v-model="formFieldForm.datePattern" clearable />-->
<!-- </el-form-item>--> <!-- </el-form-item>-->
<!-- <el-form-item label="默认值">--> <!-- <el-form-item label="默认值">-->
<!-- <el-input v-model="formFieldForm.defaultValue" clearable />--> <!-- <el-input v-model="formFieldForm.defaultValue" clearable />-->
<!-- </el-form-item>--> <!-- </el-form-item>-->
<!-- </el-form>--> <!-- </el-form>-->
<!-- &lt;!&ndash; 枚举值设置 &ndash;&gt;--> <!-- &lt;!&ndash; 枚举值设置 &ndash;&gt;-->
<!-- <template v-if="formFieldForm.type === 'enum'">--> <!-- <template v-if="formFieldForm.type === 'enum'">-->
<!-- <el-divider key="enum-divider" />--> <!-- <el-divider key="enum-divider" />-->
<!-- <p class="listener-filed__title" key="enum-title">--> <!-- <p class="listener-filed__title" key="enum-title">-->
<!-- <span><Icon icon="ep:menu" />枚举值列表</span>--> <!-- <span><Icon icon="ep:menu" />枚举值列表</span>-->
<!-- <el-button type="primary" @click="openFieldOptionForm(null, -1, 'enum')"--> <!-- <el-button type="primary" @click="openFieldOptionForm(null, -1, 'enum')"-->
<!-- >添加枚举值</el-button--> <!-- >添加枚举值</el-button-->
<!-- >--> <!-- >-->
<!-- </p>--> <!-- </p>-->
<!-- <el-table :data="fieldEnumList" key="enum-table" max-height="240" fit border>--> <!-- <el-table :data="fieldEnumList" key="enum-table" max-height="240" fit border>-->
<!-- <el-table-column label="序号" width="50px" type="index" />--> <!-- <el-table-column label="序号" width="50px" type="index" />-->
<!-- <el-table-column label="枚举值编号" prop="id" min-width="100px" show-overflow-tooltip />--> <!-- <el-table-column label="枚举值编号" prop="id" min-width="100px" show-overflow-tooltip />-->
<!-- <el-table-column label="枚举值名称" prop="name" min-width="100px" show-overflow-tooltip />--> <!-- <el-table-column label="枚举值名称" prop="name" min-width="100px" show-overflow-tooltip />-->
<!-- <el-table-column label="操作" width="90px">--> <!-- <el-table-column label="操作" width="90px">-->
<!-- <template #default="scope">--> <!-- <template #default="scope">-->
<!-- <el-button--> <!-- <el-button-->
<!-- type="primary"--> <!-- type="primary"-->
<!-- link--> <!-- link-->
<!-- @click="openFieldOptionForm(scope, scope.$index, 'enum')"--> <!-- @click="openFieldOptionForm(scope, scope.$index, 'enum')"-->
<!-- >编辑</el-button--> <!-- >编辑</el-button-->
<!-- >--> <!-- >-->
<!-- <el-divider direction="vertical" />--> <!-- <el-divider direction="vertical" />-->
<!-- <el-button--> <!-- <el-button-->
<!-- type="primary"--> <!-- type="primary"-->
<!-- link--> <!-- link-->
<!-- style="color: #ff4d4f"--> <!-- style="color: #ff4d4f"-->
<!-- @click="removeFieldOptionItem(scope, scope.$index, 'enum')"--> <!-- @click="removeFieldOptionItem(scope, scope.$index, 'enum')"-->
<!-- >移除</el-button--> <!-- >移除</el-button-->
<!-- >--> <!-- >-->
<!-- </template>--> <!-- </template>-->
<!-- </el-table-column>--> <!-- </el-table-column>-->
<!-- </el-table>--> <!-- </el-table>-->
<!-- </template>--> <!-- </template>-->
<!-- &lt;!&ndash; 校验规则 &ndash;&gt;--> <!-- &lt;!&ndash; 校验规则 &ndash;&gt;-->
<!-- <el-divider key="validation-divider" />--> <!-- <el-divider key="validation-divider" />-->
<!-- <p class="listener-filed__title" key="validation-title">--> <!-- <p class="listener-filed__title" key="validation-title">-->
<!-- <span><Icon icon="ep:menu" />约束条件列表</span>--> <!-- <span><Icon icon="ep:menu" />约束条件列表</span>-->
<!-- <el-button type="primary" @click="openFieldOptionForm(null, -1, 'constraint')"--> <!-- <el-button type="primary" @click="openFieldOptionForm(null, -1, 'constraint')"-->
<!-- >添加约束</el-button--> <!-- >添加约束</el-button-->
<!-- >--> <!-- >-->
<!-- </p>--> <!-- </p>-->
<!-- <el-table :data="fieldConstraintsList" key="validation-table" max-height="240" fit border>--> <!-- <el-table :data="fieldConstraintsList" key="validation-table" max-height="240" fit border>-->
<!-- <el-table-column label="序号" width="50px" type="index" />--> <!-- <el-table-column label="序号" width="50px" type="index" />-->
<!-- <el-table-column label="约束名称" prop="name" min-width="100px" show-overflow-tooltip />--> <!-- <el-table-column label="约束名称" prop="name" min-width="100px" show-overflow-tooltip />-->
<!-- <el-table-column label="约束配置" prop="config" min-width="100px" show-overflow-tooltip />--> <!-- <el-table-column label="约束配置" prop="config" min-width="100px" show-overflow-tooltip />-->
<!-- <el-table-column label="操作" width="90px">--> <!-- <el-table-column label="操作" width="90px">-->
<!-- <template #default="scope">--> <!-- <template #default="scope">-->
<!-- <el-button--> <!-- <el-button-->
<!-- type="primary"--> <!-- type="primary"-->
<!-- link--> <!-- link-->
<!-- @click="openFieldOptionForm(scope, scope.$index, 'constraint')"--> <!-- @click="openFieldOptionForm(scope, scope.$index, 'constraint')"-->
<!-- >编辑</el-button--> <!-- >编辑</el-button-->
<!-- >--> <!-- >-->
<!-- <el-divider direction="vertical" />--> <!-- <el-divider direction="vertical" />-->
<!-- <el-button--> <!-- <el-button-->
<!-- type="primary"--> <!-- type="primary"-->
<!-- link--> <!-- link-->
<!-- style="color: #ff4d4f"--> <!-- style="color: #ff4d4f"-->
<!-- @click="removeFieldOptionItem(scope, scope.$index, 'constraint')"--> <!-- @click="removeFieldOptionItem(scope, scope.$index, 'constraint')"-->
<!-- >移除</el-button--> <!-- >移除</el-button-->
<!-- >--> <!-- >-->
<!-- </template>--> <!-- </template>-->
<!-- </el-table-column>--> <!-- </el-table-column>-->
<!-- </el-table>--> <!-- </el-table>-->
<!-- &lt;!&ndash; 表单属性 &ndash;&gt;--> <!-- &lt;!&ndash; 表单属性 &ndash;&gt;-->
<!-- <el-divider key="property-divider" />--> <!-- <el-divider key="property-divider" />-->
<!-- <p class="listener-filed__title" key="property-title">--> <!-- <p class="listener-filed__title" key="property-title">-->
<!-- <span><Icon icon="ep:menu" />字段属性列表</span>--> <!-- <span><Icon icon="ep:menu" />字段属性列表</span>-->
<!-- <el-button type="primary" @click="openFieldOptionForm(null, -1, 'property')"--> <!-- <el-button type="primary" @click="openFieldOptionForm(null, -1, 'property')"-->
<!-- >添加属性</el-button--> <!-- >添加属性</el-button-->
<!-- >--> <!-- >-->
<!-- </p>--> <!-- </p>-->
<!-- <el-table :data="fieldPropertiesList" key="property-table" max-height="240" fit border>--> <!-- <el-table :data="fieldPropertiesList" key="property-table" max-height="240" fit border>-->
<!-- <el-table-column label="序号" width="50px" type="index" />--> <!-- <el-table-column label="序号" width="50px" type="index" />-->
<!-- <el-table-column label="属性编号" prop="id" min-width="100px" show-overflow-tooltip />--> <!-- <el-table-column label="属性编号" prop="id" min-width="100px" show-overflow-tooltip />-->
<!-- <el-table-column label="属性值" prop="value" min-width="100px" show-overflow-tooltip />--> <!-- <el-table-column label="属性值" prop="value" min-width="100px" show-overflow-tooltip />-->
<!-- <el-table-column label="操作" width="90px">--> <!-- <el-table-column label="操作" width="90px">-->
<!-- <template #default="scope">--> <!-- <template #default="scope">-->
<!-- <el-button--> <!-- <el-button-->
<!-- type="primary"--> <!-- type="primary"-->
<!-- link--> <!-- link-->
<!-- @click="openFieldOptionForm(scope, scope.$index, 'property')"--> <!-- @click="openFieldOptionForm(scope, scope.$index, 'property')"-->
<!-- >编辑</el-button--> <!-- >编辑</el-button-->
<!-- >--> <!-- >-->
<!-- <el-divider direction="vertical" />--> <!-- <el-divider direction="vertical" />-->
<!-- <el-button--> <!-- <el-button-->
<!-- type="primary"--> <!-- type="primary"-->
<!-- link--> <!-- link-->
<!-- style="color: #ff4d4f"--> <!-- style="color: #ff4d4f"-->
<!-- @click="removeFieldOptionItem(scope, scope.$index, 'property')"--> <!-- @click="removeFieldOptionItem(scope, scope.$index, 'property')"-->
<!-- >移除</el-button--> <!-- >移除</el-button-->
<!-- >--> <!-- >-->
<!-- </template>--> <!-- </template>-->
<!-- </el-table-column>--> <!-- </el-table-column>-->
<!-- </el-table>--> <!-- </el-table>-->
<!-- &lt;!&ndash; 底部按钮 &ndash;&gt;--> <!-- &lt;!&ndash; 底部按钮 &ndash;&gt;-->
<!-- <div class="element-drawer__button">--> <!-- <div class="element-drawer__button">-->
<!-- <el-button> </el-button>--> <!-- <el-button> </el-button>-->
<!-- <el-button type="primary" @click="saveField"> </el-button>--> <!-- <el-button type="primary" @click="saveField"> </el-button>-->
<!-- </div>--> <!-- </div>-->
<!-- </el-drawer>--> <!-- </el-drawer>-->
<!-- <el-dialog--> <!-- <el-dialog-->
<!-- v-model="fieldOptionModelVisible"--> <!-- v-model="fieldOptionModelVisible"-->
<!-- :title="optionModelTitle"--> <!-- :title="optionModelTitle"-->
<!-- width="600px"--> <!-- width="600px"-->
<!-- append-to-body--> <!-- append-to-body-->
<!-- destroy-on-close--> <!-- destroy-on-close-->
<!-- >--> <!-- >-->
<!-- <el-form :model="fieldOptionForm" label-width="96px">--> <!-- <el-form :model="fieldOptionForm" label-width="96px">-->
<!-- <el-form-item label="编号/ID" v-if="fieldOptionType !== 'constraint'" key="option-id">--> <!-- <el-form-item label="编号/ID" v-if="fieldOptionType !== 'constraint'" key="option-id">-->
<!-- <el-input v-model="fieldOptionForm.id" clearable />--> <!-- <el-input v-model="fieldOptionForm.id" clearable />-->
<!-- </el-form-item>--> <!-- </el-form-item>-->
<!-- <el-form-item label="名称" v-if="fieldOptionType !== 'property'" key="option-name">--> <!-- <el-form-item label="名称" v-if="fieldOptionType !== 'property'" key="option-name">-->
<!-- <el-input v-model="fieldOptionForm.name" clearable />--> <!-- <el-input v-model="fieldOptionForm.name" clearable />-->
<!-- </el-form-item>--> <!-- </el-form-item>-->
<!-- <el-form-item label="配置" v-if="fieldOptionType === 'constraint'" key="option-config">--> <!-- <el-form-item label="配置" v-if="fieldOptionType === 'constraint'" key="option-config">-->
<!-- <el-input v-model="fieldOptionForm.config" clearable />--> <!-- <el-input v-model="fieldOptionForm.config" clearable />-->
<!-- </el-form-item>--> <!-- </el-form-item>-->
<!-- <el-form-item label="值" v-if="fieldOptionType === 'property'" key="option-value">--> <!-- <el-form-item label="值" v-if="fieldOptionType === 'property'" key="option-value">-->
<!-- <el-input v-model="fieldOptionForm.value" clearable />--> <!-- <el-input v-model="fieldOptionForm.value" clearable />-->
<!-- </el-form-item>--> <!-- </el-form-item>-->
<!-- </el-form>--> <!-- </el-form>-->
<!-- <template #footer>--> <!-- <template #footer>-->
<!-- <el-button @click="fieldOptionModelVisible = false"> </el-button>--> <!-- <el-button @click="fieldOptionModelVisible = false"> </el-button>-->
<!-- <el-button type="primary" @click="saveFieldOption"> </el-button>--> <!-- <el-button type="primary" @click="saveFieldOption"> </el-button>-->
<!-- </template>--> <!-- </template>-->
<!-- </el-dialog>--> <!-- </el-dialog>-->
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { getFormSimpleList } from '@/api/bpm-boot/form' import { getFormSimpleList } from '@/api/bpm-boot/form'
defineOptions({ name: 'ElementForm' }) defineOptions({ name: 'ElementForm' })
const props = defineProps({ const props = defineProps({
id: String, id: String,
type: String type: String
}) })
const prefix = inject('prefix') const prefix = inject('prefix')
const width = inject('width') const width = inject('width')
const formKey = ref('') const formKey = ref('')
const businessKey = ref('') const businessKey = ref('')
const optionModelTitle = ref('') const optionModelTitle = ref('')
const fieldList = ref<any[]>([]) const fieldList = ref<any[]>([])
const formFieldForm = ref<any>({}) const formFieldForm = ref<any>({})
const fieldType = ref({ const fieldType = ref({
long: '长整型', long: '长整型',
string: '字符串', string: '字符串',
boolean: '布尔类', boolean: '布尔类',
date: '日期类', date: '日期类',
enum: '枚举类', enum: '枚举类',
custom: '自定义类型' custom: '自定义类型'
}) })
const formFieldIndex = ref(-1) // 编辑中的字段, -1 为新增 const formFieldIndex = ref(-1) // 编辑中的字段, -1 为新增
const formFieldOptionIndex = ref(-1) // 编辑中的字段配置项, -1 为新增 const formFieldOptionIndex = ref(-1) // 编辑中的字段配置项, -1 为新增
const fieldModelVisible = ref(false) const fieldModelVisible = ref(false)
const fieldOptionModelVisible = ref(false) const fieldOptionModelVisible = ref(false)
const fieldOptionForm = ref<any>({}) // 当前激活的字段配置项数据 const fieldOptionForm = ref<any>({}) // 当前激活的字段配置项数据
const fieldOptionType = ref('') // 当前激活的字段配置项弹窗 类型 const fieldOptionType = ref('') // 当前激活的字段配置项弹窗 类型
const fieldEnumList = ref<any[]>([]) // 枚举值列表 const fieldEnumList = ref<any[]>([]) // 枚举值列表
const fieldConstraintsList = ref<any[]>([]) // 约束条件列表 const fieldConstraintsList = ref<any[]>([]) // 约束条件列表
const fieldPropertiesList = ref<any[]>([]) // 绑定属性列表 const fieldPropertiesList = ref<any[]>([]) // 绑定属性列表
const bpmnELement = ref() const bpmnELement = ref()
const elExtensionElements = ref() const elExtensionElements = ref()
const formData = ref() const formData = ref()
const otherExtensions = ref() const otherExtensions = ref()
const bpmnInstances = () => (window as any)?.bpmnInstances const bpmnInstances = () => (window as any)?.bpmnInstances
const resetFormList = () => { const resetFormList = () => {
bpmnELement.value = bpmnInstances().bpmnElement bpmnELement.value = bpmnInstances().bpmnElement
formKey.value = bpmnELement.value.businessObject.formKey formKey.value = bpmnELement.value.businessObject.formKey
// if (formKey.value?.length > 0) { // if (formKey.value?.length > 0) {
// formKey.value = parseInt(formKey.value) // formKey.value = parseInt(formKey.value)
// } // }
// 获取元素扩展属性 或者 创建扩展属性 // 获取元素扩展属性 或者 创建扩展属性
elExtensionElements.value = elExtensionElements.value =
bpmnELement.value.businessObject.get('extensionElements') || bpmnELement.value.businessObject.get('extensionElements') ||
bpmnInstances().moddle.create('bpmn:ExtensionElements', { values: [] }) bpmnInstances().moddle.create('bpmn:ExtensionElements', { values: [] })
// 获取元素表单配置 或者 创建新的表单配置 // 获取元素表单配置 或者 创建新的表单配置
formData.value = formData.value =
elExtensionElements.value.values.filter((ex) => ex.$type === `${prefix}:FormData`)?.[0] || elExtensionElements.value.values.filter((ex) => ex.$type === `${prefix}:FormData`)?.[0] ||
bpmnInstances().moddle.create(`${prefix}:FormData`, { fields: [] }) bpmnInstances().moddle.create(`${prefix}:FormData`, { fields: [] })
// 业务标识 businessKey 绑定在 formData 中 // 业务标识 businessKey 绑定在 formData 中
businessKey.value = formData.value.businessKey businessKey.value = formData.value.businessKey
// 保留剩余扩展元素,便于后面更新该元素对应属性 // 保留剩余扩展元素,便于后面更新该元素对应属性
otherExtensions.value = elExtensionElements.value.values.filter( otherExtensions.value = elExtensionElements.value.values.filter(
(ex) => ex.$type !== `${prefix}:FormData` (ex) => ex.$type !== `${prefix}:FormData`
) )
// 复制原始值,填充表格 // 复制原始值,填充表格
fieldList.value = JSON.parse(JSON.stringify(formData.value.fields || [])) fieldList.value = JSON.parse(JSON.stringify(formData.value.fields || []))
// 更新元素扩展属性,避免后续报错 // 更新元素扩展属性,避免后续报错
updateElementExtensions() updateElementExtensions()
} }
const updateElementFormKey = () => { const updateElementFormKey = () => {
bpmnInstances().modeling.updateProperties(toRaw(bpmnELement.value), { bpmnInstances().modeling.updateProperties(toRaw(bpmnELement.value), {
formKey: formKey.value formKey: formKey.value
}) })
} }
const updateElementBusinessKey = () => { const updateElementBusinessKey = () => {
bpmnInstances().modeling.updateModdleProperties(toRaw(bpmnELement.value), formData.value, { bpmnInstances().modeling.updateModdleProperties(toRaw(bpmnELement.value), formData.value, {
businessKey: businessKey.value businessKey: businessKey.value
}) })
} }
// 根据类型调整字段type // 根据类型调整字段type
const changeFieldTypeType = (type) => { const changeFieldTypeType = (type) => {
// this.$set(this.formFieldForm, "type", type === "custom" ? "" : type); // this.$set(this.formFieldForm, "type", type === "custom" ? "" : type);
formFieldForm.value['type'] = type === 'custom' ? '' : type formFieldForm.value['type'] = type === 'custom' ? '' : type
} }
// 打开字段详情侧边栏 // 打开字段详情侧边栏
const openFieldForm = (field, index) => { const openFieldForm = (field, index) => {
formFieldIndex.value = index formFieldIndex.value = index
if (index !== -1) { if (index !== -1) {
const FieldObject = formData.value.fields[index] const FieldObject = formData.value.fields[index]
formFieldForm.value = JSON.parse(JSON.stringify(field)) formFieldForm.value = JSON.parse(JSON.stringify(field))
// 设置自定义类型 // 设置自定义类型
// this.$set(this.formFieldForm, "typeType", !this.fieldType[field.type] ? "custom" : field.type); // this.$set(this.formFieldForm, "typeType", !this.fieldType[field.type] ? "custom" : field.type);
formFieldForm.value['typeType'] = !fieldType.value[field.type] ? 'custom' : field.type formFieldForm.value['typeType'] = !fieldType.value[field.type] ? 'custom' : field.type
// 初始化枚举值列表 // 初始化枚举值列表
field.type === 'enum' && field.type === 'enum' &&
(fieldEnumList.value = JSON.parse(JSON.stringify(FieldObject?.values || []))) (fieldEnumList.value = JSON.parse(JSON.stringify(FieldObject?.values || [])))
// 初始化约束条件列表 // 初始化约束条件列表
fieldConstraintsList.value = JSON.parse( fieldConstraintsList.value = JSON.parse(
JSON.stringify(FieldObject?.validation?.constraints || []) JSON.stringify(FieldObject?.validation?.constraints || [])
) )
// 初始化自定义属性列表 // 初始化自定义属性列表
fieldPropertiesList.value = JSON.parse(JSON.stringify(FieldObject?.properties?.values || [])) fieldPropertiesList.value = JSON.parse(JSON.stringify(FieldObject?.properties?.values || []))
} else { } else {
formFieldForm.value = {} formFieldForm.value = {}
// 初始化枚举值列表 // 初始化枚举值列表
fieldEnumList.value = [] fieldEnumList.value = []
// 初始化约束条件列表 // 初始化约束条件列表
fieldConstraintsList.value = [] fieldConstraintsList.value = []
// 初始化自定义属性列表 // 初始化自定义属性列表
fieldPropertiesList.value = [] fieldPropertiesList.value = []
} }
fieldModelVisible.value = true fieldModelVisible.value = true
} }
// 打开字段 某个 配置项 弹窗 // 打开字段 某个 配置项 弹窗
const openFieldOptionForm = (option, index, type) => { const openFieldOptionForm = (option, index, type) => {
fieldOptionModelVisible.value = true fieldOptionModelVisible.value = true
fieldOptionType.value = type fieldOptionType.value = type
formFieldOptionIndex.value = index formFieldOptionIndex.value = index
if (type === 'property') { if (type === 'property') {
fieldOptionForm.value = option ? JSON.parse(JSON.stringify(option)) : {} fieldOptionForm.value = option ? JSON.parse(JSON.stringify(option)) : {}
return (optionModelTitle.value = '属性配置') return (optionModelTitle.value = '属性配置')
} }
if (type === 'enum') { if (type === 'enum') {
fieldOptionForm.value = option ? JSON.parse(JSON.stringify(option)) : {} fieldOptionForm.value = option ? JSON.parse(JSON.stringify(option)) : {}
return (optionModelTitle.value = '枚举值配置') return (optionModelTitle.value = '枚举值配置')
} }
fieldOptionForm.value = option ? JSON.parse(JSON.stringify(option)) : {} fieldOptionForm.value = option ? JSON.parse(JSON.stringify(option)) : {}
return (optionModelTitle.value = '约束条件配置') return (optionModelTitle.value = '约束条件配置')
} }
// 保存字段 某个 配置项 // 保存字段 某个 配置项
const saveFieldOption = () => { const saveFieldOption = () => {
if (formFieldOptionIndex.value === -1) { if (formFieldOptionIndex.value === -1) {
if (fieldOptionType.value === 'property') { if (fieldOptionType.value === 'property') {
fieldPropertiesList.value.push(fieldOptionForm.value) fieldPropertiesList.value.push(fieldOptionForm.value)
} }
if (fieldOptionType.value === 'constraint') { if (fieldOptionType.value === 'constraint') {
fieldConstraintsList.value.push(fieldOptionForm.value) fieldConstraintsList.value.push(fieldOptionForm.value)
} }
if (fieldOptionType.value === 'enum') { if (fieldOptionType.value === 'enum') {
fieldEnumList.value.push(fieldOptionForm.value) fieldEnumList.value.push(fieldOptionForm.value)
} }
} else { } else {
fieldOptionType.value === 'property' && fieldOptionType.value === 'property' &&
fieldPropertiesList.value.splice(formFieldOptionIndex.value, 1, fieldOptionForm.value) fieldPropertiesList.value.splice(formFieldOptionIndex.value, 1, fieldOptionForm.value)
fieldOptionType.value === 'constraint' && fieldOptionType.value === 'constraint' &&
fieldConstraintsList.value.splice(formFieldOptionIndex.value, 1, fieldOptionForm.value) fieldConstraintsList.value.splice(formFieldOptionIndex.value, 1, fieldOptionForm.value)
fieldOptionType.value === 'enum' && fieldOptionType.value === 'enum' &&
fieldEnumList.value.splice(formFieldOptionIndex.value, 1, fieldOptionForm.value) fieldEnumList.value.splice(formFieldOptionIndex.value, 1, fieldOptionForm.value)
} }
fieldOptionModelVisible.value = false fieldOptionModelVisible.value = false
fieldOptionForm.value = {} fieldOptionForm.value = {}
} }
// 保存字段配置 // 保存字段配置
const saveField = () => { const saveField = () => {
const { id, type, label, defaultValue, datePattern } = formFieldForm.value const { id, type, label, defaultValue, datePattern } = formFieldForm.value
const Field = bpmnInstances().moddle.create(`${prefix}:FormField`, { id, type, label }) const Field = bpmnInstances().moddle.create(`${prefix}:FormField`, { id, type, label })
defaultValue && (Field.defaultValue = defaultValue) defaultValue && (Field.defaultValue = defaultValue)
datePattern && (Field.datePattern = datePattern) datePattern && (Field.datePattern = datePattern)
// 构建属性 // 构建属性
if (fieldPropertiesList.value && fieldPropertiesList.value.length) { if (fieldPropertiesList.value && fieldPropertiesList.value.length) {
const fieldPropertyList = fieldPropertiesList.value.map((fp) => { const fieldPropertyList = fieldPropertiesList.value.map((fp) => {
return bpmnInstances().moddle.create(`${prefix}:Property`, { return bpmnInstances().moddle.create(`${prefix}:Property`, {
id: fp.id, id: fp.id,
value: fp.value value: fp.value
}) })
}) })
Field.properties = bpmnInstances().moddle.create(`${prefix}:Properties`, { Field.properties = bpmnInstances().moddle.create(`${prefix}:Properties`, {
values: fieldPropertyList values: fieldPropertyList
}) })
} }
// 构建校验规则 // 构建校验规则
if (fieldConstraintsList.value && fieldConstraintsList.value.length) { if (fieldConstraintsList.value && fieldConstraintsList.value.length) {
const fieldConstraintList = fieldConstraintsList.value.map((fc) => { const fieldConstraintList = fieldConstraintsList.value.map((fc) => {
return bpmnInstances().moddle.create(`${prefix}:Constraint`, { return bpmnInstances().moddle.create(`${prefix}:Constraint`, {
name: fc.name, name: fc.name,
config: fc.config config: fc.config
}) })
}) })
Field.validation = bpmnInstances().moddle.create(`${prefix}:Validation`, { Field.validation = bpmnInstances().moddle.create(`${prefix}:Validation`, {
constraints: fieldConstraintList constraints: fieldConstraintList
}) })
} }
// 构建枚举值 // 构建枚举值
if (fieldEnumList.value && fieldEnumList.value.length) { if (fieldEnumList.value && fieldEnumList.value.length) {
Field.values = fieldEnumList.value.map((fe) => { Field.values = fieldEnumList.value.map((fe) => {
return bpmnInstances().moddle.create(`${prefix}:Value`, { name: fe.name, id: fe.id }) return bpmnInstances().moddle.create(`${prefix}:Value`, { name: fe.name, id: fe.id })
}) })
} }
// 更新数组 与 表单配置实例 // 更新数组 与 表单配置实例
if (formFieldIndex.value === -1) { if (formFieldIndex.value === -1) {
fieldList.value.push(formFieldForm.value) fieldList.value.push(formFieldForm.value)
formData.value.fields.push(Field) formData.value.fields.push(Field)
} else { } else {
fieldList.value.splice(formFieldIndex.value, 1, formFieldForm.value) fieldList.value.splice(formFieldIndex.value, 1, formFieldForm.value)
formData.value.fields.splice(formFieldIndex.value, 1, Field) formData.value.fields.splice(formFieldIndex.value, 1, Field)
} }
updateElementExtensions() updateElementExtensions()
fieldModelVisible.value = false fieldModelVisible.value = false
} }
// 移除某个 字段的 配置项 // 移除某个 字段的 配置项
const removeFieldOptionItem = (option, index, type) => { const removeFieldOptionItem = (option, index, type) => {
// console.log(option, 'option') // console.log(option, 'option')
if (type === 'property') { if (type === 'property') {
fieldPropertiesList.value.splice(index, 1) fieldPropertiesList.value.splice(index, 1)
return return
} }
if (type === 'enum') { if (type === 'enum') {
fieldEnumList.value.splice(index, 1) fieldEnumList.value.splice(index, 1)
return return
} }
fieldConstraintsList.value.splice(index, 1) fieldConstraintsList.value.splice(index, 1)
} }
// 移除 字段 // 移除 字段
const removeField = (field, index) => { const removeField = (field, index) => {
console.log(field, 'field') console.log(field, 'field')
fieldList.value.splice(index, 1) fieldList.value.splice(index, 1)
formData.value.fields.splice(index, 1) formData.value.fields.splice(index, 1)
updateElementExtensions() updateElementExtensions()
} }
const updateElementExtensions = () => { const updateElementExtensions = () => {
// 更新回扩展元素 // 更新回扩展元素
const newElExtensionElements = bpmnInstances().moddle.create(`bpmn:ExtensionElements`, { const newElExtensionElements = bpmnInstances().moddle.create(`bpmn:ExtensionElements`, {
values: otherExtensions.value.concat(formData.value) values: otherExtensions.value.concat(formData.value)
}) })
// 更新到元素上 // 更新到元素上
bpmnInstances().modeling.updateProperties(toRaw(bpmnELement.value), { bpmnInstances().modeling.updateProperties(toRaw(bpmnELement.value), {
extensionElements: newElExtensionElements extensionElements: newElExtensionElements
}) })
} }
const formList = ref([]) // 流程表单的下拉框的数据 const formList = ref([]) // 流程表单的下拉框的数据
onMounted(async () => { onMounted(async () => {
await getFormSimpleList().then(res => { await getFormSimpleList().then(res => {
formList.value = res.data formList.value = res.data
}) })
}) })
watch( watch(
() => props.id, () => props.id,
(val) => { (val) => {
val && val &&
val.length && val.length &&
nextTick(() => { nextTick(() => {
resetFormList() resetFormList()
}) })
}, },
{ immediate: true } { immediate: true }
) )
</script> </script>

View File

@@ -1,463 +1,459 @@
<template> <template>
<div class='panel-tab__content'> <div class='panel-tab__content'>
<el-table :data='elementListenersList' size='small' border> <el-table :data='elementListenersList' size='small' border>
<el-table-column label='序号' width='80px' type='index' /> <el-table-column label='序号' width='80px' type='index' />
<el-table-column label='事件类型' min-width='100px' prop='event' /> <el-table-column label='事件类型' min-width='100px' prop='event' />
<el-table-column <el-table-column
label='监听器类型' label='监听器类型'
min-width='100px' min-width='100px'
show-overflow-tooltip show-overflow-tooltip
:formatter='(row) => listenerTypeObject[row.listenerType]' :formatter='(row) => listenerTypeObject[row.listenerType]'
/> />
<el-table-column label='操作' width='100px'> <el-table-column label='操作' width='100px'>
<template #default='scope'> <template #default='scope'>
<el-button size='small' link @click='openListenerForm(scope.row, scope.$index)' <el-button size='small' link @click='openListenerForm(scope.row, scope.$index)'
>编辑 >编辑
</el-button </el-button
> >
<el-divider direction='vertical' /> <el-divider direction='vertical' />
<el-button size='small' link style='color: #ff4d4f' @click='removeListener(scope.$index)' <el-button size='small' link style='color: #ff4d4f' @click='removeListener(scope.$index)'
>移除 >移除
</el-button </el-button
> >
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
<div class='element-drawer__button'> <div class='element-drawer__button'>
<XButton <XButton
type="primary" type="primary"
preIcon="ep:plus" preIcon="ep:plus"
title="添加监听器" title="添加监听器"
size="small" size="small"
@click="openListenerForm(null)" @click="openListenerForm(null)"
/> />
<!-- <Plus type='primary'--> <!-- <Plus type='primary'-->
<!-- title='添加监听器'--> <!-- title='添加监听器'-->
<!-- size='small'--> <!-- size='small'-->
<!-- @click='openListenerForm(null)' />--> <!-- @click='openListenerForm(null)' />-->
<XButton <XButton
type="success" type="success"
preIcon="ep:select" preIcon="ep:select"
title="选择监听器" title="选择监听器"
size="small" size="small"
@click="openProcessListenerDialog" @click="openProcessListenerDialog"
/> />
<!-- <Select--> <!-- <Select-->
<!-- type='success'--> <!-- type='success'-->
<!-- title='选择监听器'--> <!-- title='选择监听器'-->
<!-- size='small'--> <!-- size='small'-->
<!-- @click='openProcessListenerDialog'--> <!-- @click='openProcessListenerDialog'-->
<!-- />--> <!-- />-->
</div> </div>
<!-- 监听器 编辑/创建 部分 --> <!-- 监听器 编辑/创建 部分 -->
<el-drawer <el-drawer
v-model='listenerFormModelVisible' v-model='listenerFormModelVisible'
title='执行监听器' title='执行监听器'
:size='`${width}px`' :size='`${width}px`'
append-to-body append-to-body
destroy-on-close destroy-on-close
> >
<el-form :model='listenerForm' label-width='96px' ref='listenerFormRef'> <el-form :model='listenerForm' label-width='96px' ref='listenerFormRef'>
<el-form-item <el-form-item
label='事件类型' label='事件类型'
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>
</el-form-item> </el-form-item>
<el-form-item <el-form-item
label='监听器类型' label='监听器类型'
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'
:label='listenerTypeObject[i]' :label='listenerTypeObject[i]'
:value='i' :value='i'
/> />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item <el-form-item
v-if="listenerForm.listenerType === 'classListener'" v-if="listenerForm.listenerType === 'classListener'"
label='Java类' label='Java类'
prop='class' prop='class'
key='listener-class' key='listener-class'
:rules="{ required: true, trigger: ['blur', 'change'] }" :rules="{ required: true, trigger: ['blur', 'change'] }"
> >
<el-input v-model='listenerForm.class' clearable /> <el-input v-model='listenerForm.class' clearable />
</el-form-item> </el-form-item>
<el-form-item <el-form-item
v-if="listenerForm.listenerType === 'expressionListener'" v-if="listenerForm.listenerType === 'expressionListener'"
label='表达式' label='表达式'
prop='expression' prop='expression'
key='listener-expression' key='listener-expression'
:rules="{ required: true, trigger: ['blur', 'change'] }" :rules="{ required: true, trigger: ['blur', 'change'] }"
> >
<el-input v-model='listenerForm.expression' clearable /> <el-input v-model='listenerForm.expression' clearable />
</el-form-item> </el-form-item>
<el-form-item <el-form-item
v-if="listenerForm.listenerType === 'delegateExpressionListener'" v-if="listenerForm.listenerType === 'delegateExpressionListener'"
label='代理表达式' label='代理表达式'
prop='delegateExpression' prop='delegateExpression'
key='listener-delegate' key='listener-delegate'
:rules="{ required: true, trigger: ['blur', 'change'] }" :rules="{ required: true, trigger: ['blur', 'change'] }"
> >
<el-input v-model='listenerForm.delegateExpression' clearable /> <el-input v-model='listenerForm.delegateExpression' clearable />
</el-form-item> </el-form-item>
<template v-if="listenerForm.listenerType === 'scriptListener'"> <template v-if="listenerForm.listenerType === 'scriptListener'">
<el-form-item <el-form-item
label='脚本格式' label='脚本格式'
prop='scriptFormat' prop='scriptFormat'
key='listener-script-format' key='listener-script-format'
:rules="{ required: true, trigger: ['blur', 'change'], message: '请填写脚本格式' }" :rules="{ required: true, trigger: ['blur', 'change'], message: '请填写脚本格式' }"
> >
<el-input v-model='listenerForm.scriptFormat' clearable /> <el-input v-model='listenerForm.scriptFormat' clearable />
</el-form-item> </el-form-item>
<el-form-item <el-form-item
label='脚本类型' label='脚本类型'
prop='scriptType' prop='scriptType'
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>
</el-form-item> </el-form-item>
<el-form-item <el-form-item
v-if="listenerForm.scriptType === 'inlineScript'" v-if="listenerForm.scriptType === 'inlineScript'"
label='脚本内容' label='脚本内容'
prop='value' prop='value'
key='listener-script' key='listener-script'
:rules="{ required: true, trigger: ['blur', 'change'], message: '请填写脚本内容' }" :rules="{ required: true, trigger: ['blur', 'change'], message: '请填写脚本内容' }"
> >
<el-input v-model='listenerForm.value' clearable /> <el-input v-model='listenerForm.value' clearable />
</el-form-item> </el-form-item>
<el-form-item <el-form-item
v-if="listenerForm.scriptType === 'externalScript'" v-if="listenerForm.scriptType === 'externalScript'"
label='资源地址' label='资源地址'
prop='resource' prop='resource'
key='listener-resource' key='listener-resource'
:rules="{ required: true, trigger: ['blur', 'change'], message: '请填写资源地址' }" :rules="{ required: true, trigger: ['blur', 'change'], message: '请填写资源地址' }"
> >
<el-input v-model='listenerForm.resource' clearable /> <el-input v-model='listenerForm.resource' clearable />
</el-form-item> </el-form-item>
</template> </template>
</el-form> </el-form>
<el-divider /> <el-divider />
<p class='listener-filed__title'> <p class='listener-filed__title'>
<span><Menu />注入字段</span> <span><Menu />注入字段</span>
<XButton type='primary' @click='openListenerFieldForm(null)' title='添加字段' /> <XButton type='primary' @click='openListenerFieldForm(null)' title='添加字段' />
</p> </p>
<el-table <el-table
:data='fieldsListOfListener' :data='fieldsListOfListener'
size='small' size='small'
max-height='240' max-height='240'
fit fit
border border
style='flex: none' style='flex: none'
> >
<el-table-column label='序号' width='80px' type='index' /> <el-table-column label='序号' width='80px' type='index' />
<el-table-column label='字段名称' min-width='100px' prop='name' /> <el-table-column label='字段名称' min-width='100px' prop='name' />
<el-table-column <el-table-column
label='字段类型' label='字段类型'
min-width='80px' min-width='80px'
show-overflow-tooltip show-overflow-tooltip
:formatter='(row) => fieldTypeObject[row.fieldType]' :formatter='(row) => fieldTypeObject[row.fieldType]'
/> />
<el-table-column <el-table-column
label='字段值/表达式' label='字段值/表达式'
min-width='100px' min-width='100px'
show-overflow-tooltip show-overflow-tooltip
:formatter='(row) => row.string || row.expression' :formatter='(row) => row.string || row.expression'
/> />
<el-table-column label='操作' width='130px'> <el-table-column label='操作' width='130px'>
<template #default='scope'> <template #default='scope'>
<el-button size='small' link @click='openListenerFieldForm(scope.row, scope.$index)' <el-button size='small' link @click='openListenerFieldForm(scope.row, scope.$index)'
>编辑 >编辑
</el-button </el-button
> >
<el-divider direction='vertical' /> <el-divider direction='vertical' />
<el-button <el-button
size='small' size='small'
link link
style='color: #ff4d4f' style='color: #ff4d4f'
@click='removeListenerField(scope.$index)' @click='removeListenerField(scope.$index)'
>移除 >移除
</el-button </el-button
> >
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
<div class='element-drawer__button'> <div class='element-drawer__button'>
<el-button @click='listenerFormModelVisible = false'> </el-button> <el-button @click='listenerFormModelVisible = false'> </el-button>
<el-button type='primary' @click='saveListenerConfig'> </el-button> <el-button type='primary' @click='saveListenerConfig'> </el-button>
</div> </div>
</el-drawer> </el-drawer>
<!-- 注入西段 编辑/创建 部分 --> <!-- 注入西段 编辑/创建 部分 -->
<el-dialog <el-dialog
title='字段配置' title='字段配置'
v-model='listenerFieldFormModelVisible' v-model='listenerFieldFormModelVisible'
width='600px' width='600px'
append-to-body append-to-body
destroy-on-close destroy-on-close
> >
<el-form <el-form
:model='listenerFieldForm' :model='listenerFieldForm'
label-width='96spx' label-width='96spx'
ref='listenerFieldFormRef' ref='listenerFieldFormRef'
style='height: 136px' style='height: 136px'
> >
<el-form-item <el-form-item
label='字段名称:' label='字段名称:'
prop='name' prop='name'
:rules="{ required: true, trigger: ['blur', 'change'] }" :rules="{ required: true, trigger: ['blur', 'change'] }"
> >
<el-input v-model='listenerFieldForm.name' clearable /> <el-input v-model='listenerFieldForm.name' clearable />
</el-form-item> </el-form-item>
<el-form-item <el-form-item
label='字段类型:' label='字段类型:'
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'
:label='fieldTypeObject[i]' :label='fieldTypeObject[i]'
:value='i' :value='i'
/> />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item <el-form-item
v-if="listenerFieldForm.fieldType === 'string'" v-if="listenerFieldForm.fieldType === 'string'"
label='字段值:' label='字段值:'
prop='string' prop='string'
key='field-string' key='field-string'
:rules="{ required: true, trigger: ['blur', 'change'] }" :rules="{ required: true, trigger: ['blur', 'change'] }"
> >
<el-input v-model='listenerFieldForm.string' clearable /> <el-input v-model='listenerFieldForm.string' clearable />
</el-form-item> </el-form-item>
<el-form-item <el-form-item
v-if="listenerFieldForm.fieldType === 'expression'" v-if="listenerFieldForm.fieldType === 'expression'"
label='表达式:' label='表达式:'
prop='expression' prop='expression'
key='field-expression' key='field-expression'
:rules="{ required: true, trigger: ['blur', 'change'] }" :rules="{ required: true, trigger: ['blur', 'change'] }"
> >
<el-input v-model='listenerFieldForm.expression' clearable /> <el-input v-model='listenerFieldForm.expression' clearable />
</el-form-item> </el-form-item>
</el-form> </el-form>
<template #footer> <template #footer>
<el-button size='small' @click='listenerFieldFormModelVisible = false'> </el-button> <el-button size='small' @click='listenerFieldFormModelVisible = false'> </el-button>
<el-button size='small' type='primary' @click='saveListenerFiled'> </el-button> <el-button size='small' type='primary' @click='saveListenerFiled'> </el-button>
</template> </template>
</el-dialog> </el-dialog>
</div> </div>
<!-- 选择弹窗 --> <!-- 选择弹窗 -->
<ProcessListenerDialog ref='processListenerDialogRef' @select='selectProcessListener' /> <ProcessListenerDialog ref='processListenerDialogRef' @select='selectProcessListener' />
</template> </template>
<script lang='ts' setup> <script lang='ts' setup>
import { ElMessageBox } from 'element-plus' import { ElMessageBox } from 'element-plus'
import XButton from '@/components/XButton/src/XButton.vue' import XButton from '@/components/XButton/src/XButton.vue'
import { Plus, Select, Menu } from '@element-plus/icons-vue' import { Plus, Select, Menu } from '@element-plus/icons-vue'
import { createListenerObject, updateElementExtensions } from '../../utils' import { createListenerObject, updateElementExtensions } from '../../utils'
import { import {
initListenerType, initListenerType,
initListenerForm, initListenerForm,
listenerType, listenerType,
fieldType, fieldType,
initListenerForm2 initListenerForm2
} from './utilSelf' } from './utilSelf'
import ProcessListenerDialog from './ProcessListenerDialog.vue' import ProcessListenerDialog from './ProcessListenerDialog.vue'
defineOptions({ name: 'ElementListeners' }) defineOptions({ name: 'ElementListeners' })
const props = defineProps({ const props = defineProps({
id: String, id: String,
type: String type: String
}) })
const prefix = inject('prefix') const prefix = inject('prefix')
const width = inject('width') const width = inject('width')
const elementListenersList = ref<any[]>([]) // 监听器列表 const elementListenersList = ref<any[]>([]) // 监听器列表
const listenerForm = ref<any>({}) // 监听器详情表单 const listenerForm = ref<any>({}) // 监听器详情表单
const listenerFormModelVisible = ref(false) // 监听器 编辑 侧边栏显示状态 const listenerFormModelVisible = ref(false) // 监听器 编辑 侧边栏显示状态
const fieldsListOfListener = ref<any[]>([]) const fieldsListOfListener = ref<any[]>([])
const listenerFieldForm = ref<any>({}) // 监听器 注入字段 详情表单 const listenerFieldForm = ref<any>({}) // 监听器 注入字段 详情表单
const listenerFieldFormModelVisible = ref(false) // 监听器 注入字段表单弹窗 显示状态 const listenerFieldFormModelVisible = ref(false) // 监听器 注入字段表单弹窗 显示状态
const editingListenerIndex = ref(-1) // 监听器所在下标,-1 为新增 const editingListenerIndex = ref(-1) // 监听器所在下标,-1 为新增
const editingListenerFieldIndex = ref(-1) // 字段所在下标,-1 为新增 const editingListenerFieldIndex = ref(-1) // 字段所在下标,-1 为新增
const listenerTypeObject = ref(listenerType) const listenerTypeObject = ref(listenerType)
const fieldTypeObject = ref(fieldType) const fieldTypeObject = ref(fieldType)
const bpmnElement = ref() const bpmnElement = ref()
const otherExtensionList = ref() const otherExtensionList = ref()
const bpmnElementListeners = ref() const bpmnElementListeners = ref()
const listenerFormRef = ref() const listenerFormRef = ref()
const listenerFieldFormRef = ref() const listenerFieldFormRef = ref()
const bpmnInstances = () => (window as any)?.bpmnInstances const bpmnInstances = () => (window as any)?.bpmnInstances
const resetListenersList = () => { const resetListenersList = () => {
bpmnElement.value = bpmnInstances().bpmnElement bpmnElement.value = bpmnInstances().bpmnElement
otherExtensionList.value = [] otherExtensionList.value = []
bpmnElementListeners.value = bpmnElementListeners.value =
bpmnElement.value.businessObject?.extensionElements?.values?.filter( bpmnElement.value.businessObject?.extensionElements?.values?.filter(
(ex) => ex.$type === `${prefix}:ExecutionListener` (ex) => ex.$type === `${prefix}:ExecutionListener`
) ?? [] ) ?? []
elementListenersList.value = bpmnElementListeners.value.map((listener) => elementListenersList.value = bpmnElementListeners.value.map((listener) =>
initListenerType(listener) initListenerType(listener)
) )
} }
// 打开 监听器详情 侧边栏 // 打开 监听器详情 侧边栏
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 } else {
} else { listenerForm.value = {}
listenerForm.value = {} editingListenerIndex.value = -1 // 标记为新增
editingListenerIndex.value = -1 // 标记为新增 }
} if (listener && listener.fields) {
if (listener && listener.fields) { fieldsListOfListener.value = listener.fields.map((field) => ({
fieldsListOfListener.value = listener.fields.map((field) => ({ ...field,
...field, fieldType: field.string ? 'string' : 'expression'
fieldType: field.string ? 'string' : 'expression' }))
})) } else {
} else { fieldsListOfListener.value = []
fieldsListOfListener.value = [] listenerForm.value['fields'] = []
listenerForm.value['fields'] = [] }
} // 打开侧边栏并清楚验证状态
// 打开侧边栏并清楚验证状态 listenerFormModelVisible.value = true
listenerFormModelVisible.value = true nextTick(() => {
nextTick(() => { if (listenerFormRef.value) {
if (listenerFormRef.value) { listenerFormRef.value.clearValidate()
listenerFormRef.value.clearValidate() }
} })
}) }
} // 打开监听器字段编辑弹窗
// 打开监听器字段编辑弹窗 const openListenerFieldForm = (field, index?) => {
const openListenerFieldForm = (field, index?) => { listenerFieldForm.value = field ? JSON.parse(JSON.stringify(field)) : {}
listenerFieldForm.value = field ? JSON.parse(JSON.stringify(field)) : {} editingListenerFieldIndex.value = field ? index : -1
editingListenerFieldIndex.value = field ? index : -1 listenerFieldFormModelVisible.value = true
listenerFieldFormModelVisible.value = true nextTick(() => {
nextTick(() => { if (listenerFieldFormRef.value) {
if (listenerFieldFormRef.value) { listenerFieldFormRef.value.clearValidate()
listenerFieldFormRef.value.clearValidate() }
} })
}) }
} // 保存监听器注入字段
// 保存监听器注入字段 const saveListenerFiled = async () => {
const saveListenerFiled = async () => { let validateStatus = await listenerFieldFormRef.value.validate()
// debugger if (!validateStatus) return // 验证不通过直接返回
let validateStatus = await listenerFieldFormRef.value.validate() if (editingListenerFieldIndex.value === -1) {
if (!validateStatus) return // 验证不通过直接返回 fieldsListOfListener.value.push(listenerFieldForm.value)
if (editingListenerFieldIndex.value === -1) { listenerForm.value.fields.push(listenerFieldForm.value)
fieldsListOfListener.value.push(listenerFieldForm.value) } else {
listenerForm.value.fields.push(listenerFieldForm.value) fieldsListOfListener.value.splice(editingListenerFieldIndex.value, 1, listenerFieldForm.value)
} else { listenerForm.value.fields.splice(editingListenerFieldIndex.value, 1, listenerFieldForm.value)
fieldsListOfListener.value.splice(editingListenerFieldIndex.value, 1, listenerFieldForm.value) }
listenerForm.value.fields.splice(editingListenerFieldIndex.value, 1, listenerFieldForm.value) listenerFieldFormModelVisible.value = false
} nextTick(() => {
listenerFieldFormModelVisible.value = false listenerFieldForm.value = {}
nextTick(() => { })
listenerFieldForm.value = {} }
}) // 移除监听器字段
} const removeListenerField = (index) => {
// 移除监听器字段 ElMessageBox.confirm('确认移除该字段吗?', '提示', {
const removeListenerField = (index) => { confirmButtonText: '确 认',
// debugger cancelButtonText: '取 消'
ElMessageBox.confirm('确认移除该字段吗?', '提示', { })
confirmButtonText: '确 认', .then(() => {
cancelButtonText: '取 消' fieldsListOfListener.value.splice(index, 1)
}) listenerForm.value.fields.splice(index, 1)
.then(() => { })
fieldsListOfListener.value.splice(index, 1) .catch(() => console.info('操作取消'))
listenerForm.value.fields.splice(index, 1) }
}) // 移除监听器
.catch(() => console.info('操作取消')) const removeListener = (index) => {
} ElMessageBox.confirm('确认移除该监听器吗?', '提示', {
// 移除监听器 confirmButtonText: '确 认',
const removeListener = (index) => { cancelButtonText: '取 消'
ElMessageBox.confirm('确认移除该监听器吗?', '提示', { })
confirmButtonText: '确 认', .then(() => {
cancelButtonText: '取 消' bpmnElementListeners.value.splice(index, 1)
}) elementListenersList.value.splice(index, 1)
.then(() => { updateElementExtensions(
bpmnElementListeners.value.splice(index, 1) bpmnElement.value,
elementListenersList.value.splice(index, 1) otherExtensionList.value.concat(bpmnElementListeners.value)
updateElementExtensions( )
bpmnElement.value, })
otherExtensionList.value.concat(bpmnElementListeners.value) .catch(() => console.info('操作取消'))
) }
}) // 保存监听器配置
.catch(() => console.info('操作取消')) const saveListenerConfig = async () => {
} let validateStatus = await listenerFormRef.value.validate()
// 保存监听器配置 if (!validateStatus) return // 验证不通过直接返回
const saveListenerConfig = async () => { const listenerObject = createListenerObject(listenerForm.value, false, prefix)
// debugger if (editingListenerIndex.value === -1) {
let validateStatus = await listenerFormRef.value.validate() bpmnElementListeners.value.push(listenerObject)
if (!validateStatus) return // 验证不通过直接返回 elementListenersList.value.push(listenerForm.value)
const listenerObject = createListenerObject(listenerForm.value, false, prefix) } else {
if (editingListenerIndex.value === -1) { bpmnElementListeners.value.splice(editingListenerIndex.value, 1, listenerObject)
bpmnElementListeners.value.push(listenerObject) elementListenersList.value.splice(editingListenerIndex.value, 1, listenerForm.value)
elementListenersList.value.push(listenerForm.value) }
} else { // 保存其他配置
bpmnElementListeners.value.splice(editingListenerIndex.value, 1, listenerObject) otherExtensionList.value =
elementListenersList.value.splice(editingListenerIndex.value, 1, listenerForm.value) bpmnElement.value.businessObject?.extensionElements?.values?.filter(
} (ex) => ex.$type !== `${prefix}:ExecutionListener`
// 保存其他配置 ) ?? []
otherExtensionList.value = updateElementExtensions(
bpmnElement.value.businessObject?.extensionElements?.values?.filter( bpmnElement.value,
(ex) => ex.$type !== `${prefix}:ExecutionListener` otherExtensionList.value.concat(bpmnElementListeners.value)
) ?? [] )
updateElementExtensions( // 4. 隐藏侧边栏
bpmnElement.value, listenerFormModelVisible.value = false
otherExtensionList.value.concat(bpmnElementListeners.value) listenerForm.value = {}
) }
// 4. 隐藏侧边栏
listenerFormModelVisible.value = false // 打开监听器弹窗
listenerForm.value = {} const processListenerDialogRef = ref()
} const openProcessListenerDialog = async () => {
processListenerDialogRef.value.open('execution')
// 打开监听器弹窗 }
const processListenerDialogRef = ref() const selectProcessListener = (listener) => {
const openProcessListenerDialog = async () => { const listenerForm = initListenerForm2(listener)
processListenerDialogRef.value.open('execution') const listenerObject = createListenerObject(listenerForm, false, prefix)
} bpmnElementListeners.value.push(listenerObject)
const selectProcessListener = (listener) => { elementListenersList.value.push(listenerForm)
const listenerForm = initListenerForm2(listener)
const listenerObject = createListenerObject(listenerForm, false, prefix) // 保存其他配置
bpmnElementListeners.value.push(listenerObject) otherExtensionList.value =
elementListenersList.value.push(listenerForm) bpmnElement.value.businessObject?.extensionElements?.values?.filter(
(ex) => ex.$type !== `${prefix}:ExecutionListener`
// 保存其他配置 ) ?? []
otherExtensionList.value = updateElementExtensions(
bpmnElement.value.businessObject?.extensionElements?.values?.filter( bpmnElement.value,
(ex) => ex.$type !== `${prefix}:ExecutionListener` otherExtensionList.value.concat(bpmnElementListeners.value)
) ?? [] )
updateElementExtensions( }
bpmnElement.value,
otherExtensionList.value.concat(bpmnElementListeners.value) watch(
) () => props.id,
} (val) => {
val &&
watch( val.length &&
() => props.id, nextTick(() => {
(val) => { resetListenersList()
val && })
val.length && },
nextTick(() => { { immediate: true }
resetListenersList() )
}) </script>
},
{ immediate: true }
)
</script>

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

@@ -1,280 +1,280 @@
<template> <template>
<div class="panel-tab__content"> <div class="panel-tab__content">
<el-form label-width="90px"> <el-form label-width="90px">
<el-form-item label="快捷配置"> <el-form-item label="快捷配置">
<el-button size="small" @click="changeConfig('依次审批')">依次审批</el-button> <el-button size="small" @click="changeConfig('依次审批')">依次审批</el-button>
<el-button size="small" @click="changeConfig('会签')">会签</el-button> <el-button size="small" @click="changeConfig('会签')">会签</el-button>
<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" />
</el-select> </el-select>
</el-form-item> </el-form-item>
<template <template
v-if=" v-if="
loopCharacteristics === 'ParallelMultiInstance' || loopCharacteristics === 'ParallelMultiInstance' ||
loopCharacteristics === 'SequentialMultiInstance' loopCharacteristics === 'SequentialMultiInstance'
" "
> >
<el-form-item label="循环数量" key="loopCardinality"> <el-form-item label="循环数量" key="loopCardinality">
<el-input <el-input
v-model="loopInstanceForm.loopCardinality" v-model="loopInstanceForm.loopCardinality"
clearable clearable
@change="updateLoopCardinality" @change="updateLoopCardinality"
/> />
</el-form-item> </el-form-item>
<el-form-item label="集合" key="collection" v-show="false"> <el-form-item label="集合" key="collection" v-show="false">
<el-input v-model="loopInstanceForm.collection" clearable @change="updateLoopBase" /> <el-input v-model="loopInstanceForm.collection" clearable @change="updateLoopBase" />
</el-form-item> </el-form-item>
<!-- add by 芋艿:由于「元素变量」暂时用不到,所以这里 display 为 none --> <!-- add by 芋艿:由于「元素变量」暂时用不到,所以这里 display 为 none -->
<el-form-item label="元素变量" key="elementVariable" style="display: none"> <el-form-item label="元素变量" key="elementVariable" style="display: none">
<el-input v-model="loopInstanceForm.elementVariable" clearable @change="updateLoopBase" /> <el-input v-model="loopInstanceForm.elementVariable" clearable @change="updateLoopBase" />
</el-form-item> </el-form-item>
<el-form-item label="完成条件" key="completionCondition"> <el-form-item label="完成条件" key="completionCondition">
<el-input <el-input
v-model="loopInstanceForm.completionCondition" v-model="loopInstanceForm.completionCondition"
clearable clearable
@change="updateLoopCondition" @change="updateLoopCondition"
/> />
</el-form-item> </el-form-item>
<!-- add by 芋艿:由于「异步状态」暂时用不到,所以这里 display 为 none --> <!-- add by 芋艿:由于「异步状态」暂时用不到,所以这里 display 为 none -->
<el-form-item label="异步状态" key="async" style="display: none"> <el-form-item label="异步状态" key="async" style="display: none">
<el-checkbox <el-checkbox
v-model="loopInstanceForm.asyncBefore" v-model="loopInstanceForm.asyncBefore"
label="异步前" label="异步前"
@change="updateLoopAsync('asyncBefore')" @change="updateLoopAsync('asyncBefore')"
/> />
<el-checkbox <el-checkbox
v-model="loopInstanceForm.asyncAfter" v-model="loopInstanceForm.asyncAfter"
label="异步后" label="异步后"
@change="updateLoopAsync('asyncAfter')" @change="updateLoopAsync('asyncAfter')"
/> />
<el-checkbox <el-checkbox
v-model="loopInstanceForm.exclusive" v-model="loopInstanceForm.exclusive"
v-if="loopInstanceForm.asyncAfter || loopInstanceForm.asyncBefore" v-if="loopInstanceForm.asyncAfter || loopInstanceForm.asyncBefore"
label="排除" label="排除"
@change="updateLoopAsync('exclusive')" @change="updateLoopAsync('exclusive')"
/> />
</el-form-item> </el-form-item>
<el-form-item <el-form-item
label="重试周期" label="重试周期"
prop="timeCycle" prop="timeCycle"
v-if="loopInstanceForm.asyncAfter || loopInstanceForm.asyncBefore" v-if="loopInstanceForm.asyncAfter || loopInstanceForm.asyncBefore"
key="timeCycle" key="timeCycle"
> >
<el-input v-model="loopInstanceForm.timeCycle" clearable @change="updateLoopTimeCycle" /> <el-input v-model="loopInstanceForm.timeCycle" clearable @change="updateLoopTimeCycle" />
</el-form-item> </el-form-item>
</template> </template>
</el-form> </el-form>
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
defineOptions({ name: 'ElementMultiInstance' }) defineOptions({ name: 'ElementMultiInstance' })
const props = defineProps({ const props = defineProps({
businessObject: Object, businessObject: Object,
type: String type: String
}) })
const prefix = inject('prefix') const prefix = inject('prefix')
const loopCharacteristics = ref('') const loopCharacteristics = ref('')
//默认配置,用来覆盖原始不存在的选项,避免报错 //默认配置,用来覆盖原始不存在的选项,避免报错
const defaultLoopInstanceForm = ref({ const defaultLoopInstanceForm = ref({
completionCondition: '', completionCondition: '',
loopCardinality: '', loopCardinality: '',
extensionElements: [], extensionElements: [],
asyncAfter: false, asyncAfter: false,
asyncBefore: false, asyncBefore: false,
exclusive: false exclusive: false
}) })
const loopInstanceForm = ref<any>({}) const loopInstanceForm = ref<any>({})
const bpmnElement = ref(null) const bpmnElement = ref(null)
const multiLoopInstance = ref(null) const multiLoopInstance = ref(null)
const bpmnInstances = () => (window as any)?.bpmnInstances const bpmnInstances = () => (window as any)?.bpmnInstances
const getElementLoop = (businessObject) => { const getElementLoop = (businessObject) => {
if (!businessObject.loopCharacteristics) { if (!businessObject.loopCharacteristics) {
loopCharacteristics.value = 'Null' loopCharacteristics.value = 'Null'
loopInstanceForm.value = {} loopInstanceForm.value = {}
return return
} }
if (businessObject.loopCharacteristics.$type === 'bpmn:StandardLoopCharacteristics') { if (businessObject.loopCharacteristics.$type === 'bpmn:StandardLoopCharacteristics') {
loopCharacteristics.value = 'StandardLoop' loopCharacteristics.value = 'StandardLoop'
loopInstanceForm.value = {} loopInstanceForm.value = {}
return return
} }
if (businessObject.loopCharacteristics.isSequential) { if (businessObject.loopCharacteristics.isSequential) {
loopCharacteristics.value = 'SequentialMultiInstance' loopCharacteristics.value = 'SequentialMultiInstance'
} else { } else {
loopCharacteristics.value = 'ParallelMultiInstance' loopCharacteristics.value = 'ParallelMultiInstance'
} }
// 合并配置 // 合并配置
loopInstanceForm.value = { loopInstanceForm.value = {
...defaultLoopInstanceForm.value, ...defaultLoopInstanceForm.value,
...businessObject.loopCharacteristics, ...businessObject.loopCharacteristics,
completionCondition: businessObject.loopCharacteristics?.completionCondition?.body ?? '', completionCondition: businessObject.loopCharacteristics?.completionCondition?.body ?? '',
loopCardinality: businessObject.loopCharacteristics?.loopCardinality?.body ?? '' loopCardinality: businessObject.loopCharacteristics?.loopCardinality?.body ?? ''
} }
// 保留当前元素 businessObject 上的 loopCharacteristics 实例 // 保留当前元素 businessObject 上的 loopCharacteristics 实例
multiLoopInstance.value = bpmnInstances().bpmnElement.businessObject.loopCharacteristics multiLoopInstance.value = bpmnInstances().bpmnElement.businessObject.loopCharacteristics
// 更新表单 // 更新表单
if ( if (
businessObject.loopCharacteristics.extensionElements && businessObject.loopCharacteristics.extensionElements &&
businessObject.loopCharacteristics.extensionElements.values && businessObject.loopCharacteristics.extensionElements.values &&
businessObject.loopCharacteristics.extensionElements.values.length businessObject.loopCharacteristics.extensionElements.values.length
) { ) {
loopInstanceForm.value['timeCycle'] = loopInstanceForm.value['timeCycle'] =
businessObject.loopCharacteristics.extensionElements.values[0].body businessObject.loopCharacteristics.extensionElements.values[0].body
} }
} }
const changeLoopCharacteristicsType = (type) => { const changeLoopCharacteristicsType = (type) => {
// this.loopInstanceForm = { ...this.defaultLoopInstanceForm }; // 切换类型取消原表单配置 // this.loopInstanceForm = { ...this.defaultLoopInstanceForm }; // 切换类型取消原表单配置
// 取消多实例配置 // 取消多实例配置
if (type === 'Null') { if (type === 'Null') {
bpmnInstances().modeling.updateProperties(toRaw(bpmnElement.value), { bpmnInstances().modeling.updateProperties(toRaw(bpmnElement.value), {
loopCharacteristics: null loopCharacteristics: null
}) })
return return
} }
// 配置循环 // 配置循环
if (type === 'StandardLoop') { if (type === 'StandardLoop') {
const loopCharacteristicsObject = bpmnInstances().moddle.create( const loopCharacteristicsObject = bpmnInstances().moddle.create(
'bpmn:StandardLoopCharacteristics' 'bpmn:StandardLoopCharacteristics'
) )
bpmnInstances().modeling.updateProperties(toRaw(bpmnElement.value), { bpmnInstances().modeling.updateProperties(toRaw(bpmnElement.value), {
loopCharacteristics: loopCharacteristicsObject loopCharacteristics: loopCharacteristicsObject
}) })
multiLoopInstance.value = null multiLoopInstance.value = null
return return
} }
// 时序 // 时序
if (type === 'SequentialMultiInstance') { if (type === 'SequentialMultiInstance') {
multiLoopInstance.value = bpmnInstances().moddle.create( multiLoopInstance.value = bpmnInstances().moddle.create(
'bpmn:MultiInstanceLoopCharacteristics', 'bpmn:MultiInstanceLoopCharacteristics',
{ isSequential: true } { isSequential: true }
) )
} else { } else {
multiLoopInstance.value = bpmnInstances().moddle.create( multiLoopInstance.value = bpmnInstances().moddle.create(
'bpmn:MultiInstanceLoopCharacteristics', 'bpmn:MultiInstanceLoopCharacteristics',
{ collection: '${coll_userList}' } { collection: '${coll_userList}' }
) )
} }
bpmnInstances().modeling.updateProperties(toRaw(bpmnElement.value), { bpmnInstances().modeling.updateProperties(toRaw(bpmnElement.value), {
loopCharacteristics: toRaw(multiLoopInstance.value) loopCharacteristics: toRaw(multiLoopInstance.value)
}) })
} }
// 循环基数 // 循环基数
const updateLoopCardinality = (cardinality) => { const updateLoopCardinality = (cardinality) => {
let loopCardinality = null let loopCardinality = null
if (cardinality && cardinality.length) { if (cardinality && cardinality.length) {
loopCardinality = bpmnInstances().moddle.create('bpmn:FormalExpression', { loopCardinality = bpmnInstances().moddle.create('bpmn:FormalExpression', {
body: cardinality body: cardinality
}) })
} }
bpmnInstances().modeling.updateModdleProperties( bpmnInstances().modeling.updateModdleProperties(
toRaw(bpmnElement.value), toRaw(bpmnElement.value),
multiLoopInstance.value, multiLoopInstance.value,
{ {
loopCardinality loopCardinality
} }
) )
} }
// 完成条件 // 完成条件
const updateLoopCondition = (condition) => { const updateLoopCondition = (condition) => {
let completionCondition = null let completionCondition = null
if (condition && condition.length) { if (condition && condition.length) {
completionCondition = bpmnInstances().moddle.create('bpmn:FormalExpression', { completionCondition = bpmnInstances().moddle.create('bpmn:FormalExpression', {
body: condition body: condition
}) })
} }
bpmnInstances().modeling.updateModdleProperties( bpmnInstances().modeling.updateModdleProperties(
toRaw(bpmnElement.value), toRaw(bpmnElement.value),
multiLoopInstance.value, multiLoopInstance.value,
{ {
completionCondition completionCondition
} }
) )
} }
// 重试周期 // 重试周期
const updateLoopTimeCycle = (timeCycle) => { const updateLoopTimeCycle = (timeCycle) => {
const extensionElements = bpmnInstances().moddle.create('bpmn:ExtensionElements', { const extensionElements = bpmnInstances().moddle.create('bpmn:ExtensionElements', {
values: [ values: [
bpmnInstances().moddle.create(`${prefix}:FailedJobRetryTimeCycle`, { bpmnInstances().moddle.create(`${prefix}:FailedJobRetryTimeCycle`, {
body: timeCycle body: timeCycle
}) })
] ]
}) })
bpmnInstances().modeling.updateModdleProperties( bpmnInstances().modeling.updateModdleProperties(
toRaw(bpmnElement.value), toRaw(bpmnElement.value),
multiLoopInstance.value, multiLoopInstance.value,
{ {
extensionElements extensionElements
} }
) )
} }
// 直接更新的基础信息 // 直接更新的基础信息
const updateLoopBase = () => { const updateLoopBase = () => {
bpmnInstances().modeling.updateModdleProperties( bpmnInstances().modeling.updateModdleProperties(
toRaw(bpmnElement.value), toRaw(bpmnElement.value),
multiLoopInstance.value, multiLoopInstance.value,
{ {
collection: loopInstanceForm.value.collection || null, collection: loopInstanceForm.value.collection || null,
elementVariable: loopInstanceForm.value.elementVariable || null elementVariable: loopInstanceForm.value.elementVariable || null
} }
) )
} }
// 各异步状态 // 各异步状态
const updateLoopAsync = (key) => { const updateLoopAsync = (key) => {
const { asyncBefore, asyncAfter } = loopInstanceForm.value const { asyncBefore, asyncAfter } = loopInstanceForm.value
let asyncAttr = Object.create(null) let asyncAttr = Object.create(null)
if (!asyncBefore && !asyncAfter) { if (!asyncBefore && !asyncAfter) {
// this.$set(this.loopInstanceForm, "exclusive", false); // this.$set(this.loopInstanceForm, "exclusive", false);
loopInstanceForm.value['exclusive'] = false loopInstanceForm.value['exclusive'] = false
asyncAttr = { asyncBefore: false, asyncAfter: false, exclusive: false, extensionElements: null } asyncAttr = { asyncBefore: false, asyncAfter: false, exclusive: false, extensionElements: null }
} else { } else {
asyncAttr[key] = loopInstanceForm.value[key] asyncAttr[key] = loopInstanceForm.value[key]
} }
bpmnInstances().modeling.updateModdleProperties( bpmnInstances().modeling.updateModdleProperties(
toRaw(bpmnElement.value), toRaw(bpmnElement.value),
multiLoopInstance.value, multiLoopInstance.value,
asyncAttr asyncAttr
) )
} }
const changeConfig = (config) => { const changeConfig = (config) => {
if (config === '依次审批') { if (config === '依次审批') {
changeLoopCharacteristicsType('SequentialMultiInstance') changeLoopCharacteristicsType('SequentialMultiInstance')
updateLoopCardinality('1') updateLoopCardinality('1')
updateLoopCondition('${ nrOfCompletedInstances >= nrOfInstances }') updateLoopCondition('${ nrOfCompletedInstances >= nrOfInstances }')
} else if (config === '会签') { } else if (config === '会签') {
changeLoopCharacteristicsType('ParallelMultiInstance') changeLoopCharacteristicsType('ParallelMultiInstance')
updateLoopCondition('${ nrOfCompletedInstances >= nrOfInstances }') updateLoopCondition('${ nrOfCompletedInstances >= nrOfInstances }')
} else if (config === '或签') { } else if (config === '或签') {
changeLoopCharacteristicsType('ParallelMultiInstance') changeLoopCharacteristicsType('ParallelMultiInstance')
updateLoopCondition('${ nrOfCompletedInstances > 0 }') updateLoopCondition('${ nrOfCompletedInstances > 0 }')
} }
} }
onBeforeUnmount(() => { onBeforeUnmount(() => {
multiLoopInstance.value = null multiLoopInstance.value = null
bpmnElement.value = null bpmnElement.value = null
}) })
watch( watch(
() => props.businessObject, () => props.businessObject,
(val) => { (val) => {
bpmnElement.value = bpmnInstances().bpmnElement bpmnElement.value = bpmnInstances().bpmnElement
getElementLoop(val) getElementLoop(val)
}, },
{ immediate: true } { immediate: true }
) )
</script> </script>

View File

@@ -1,125 +1,125 @@
<template> <template>
<div style="margin-top: 16px"> <div style="margin-top: 16px">
<el-form-item label="消息实例"> <el-form-item label="消息实例">
<div <div
style=" style="
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
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"
:label="messageMap[key]" :label="messageMap[key]"
:key="key" :key="key"
/> />
</el-select> </el-select>
<XButton <XButton
type="primary" type="primary"
preIcon="ep:plus" preIcon="ep:plus"
style="margin-left: 8px" style="margin-left: 8px"
@click="openMessageModel" @click="openMessageModel"
/> />
</div> </div>
</el-form-item> </el-form-item>
<el-dialog <el-dialog
v-model="messageModelVisible" v-model="messageModelVisible"
:close-on-click-modal="false" :close-on-click-modal="false"
title="创建新消息" title="创建新消息"
width="400px" width="400px"
append-to-body append-to-body
destroy-on-close destroy-on-close
> >
<el-form :model="newMessageForm" size="small" label-width="90px"> <el-form :model="newMessageForm" size="small" label-width="90px">
<el-form-item label="消息ID"> <el-form-item label="消息ID">
<el-input v-model="newMessageForm.id" clearable /> <el-input v-model="newMessageForm.id" clearable />
</el-form-item> </el-form-item>
<el-form-item label="消息名称"> <el-form-item label="消息名称">
<el-input v-model="newMessageForm.name" clearable /> <el-input v-model="newMessageForm.name" clearable />
</el-form-item> </el-form-item>
</el-form> </el-form>
<template #footer> <template #footer>
<el-button size="small" type="primary" @click="createNewMessage"> </el-button> <el-button size="small" type="primary" @click="createNewMessage"> </el-button>
</template> </template>
</el-dialog> </el-dialog>
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
defineOptions({ name: 'ReceiveTask' }) defineOptions({ name: 'ReceiveTask' })
const props = defineProps({ const props = defineProps({
id: String, id: String,
type: String type: String
}) })
const message = useMessage() const message = useMessage()
const bindMessageId = ref('') const bindMessageId = ref('')
const newMessageForm = ref<any>({}) const newMessageForm = ref<any>({})
const messageMap = ref<any>({}) const messageMap = ref<any>({})
const messageModelVisible = ref(false) const messageModelVisible = ref(false)
const bpmnElement = ref<any>() const bpmnElement = ref<any>()
const bpmnMessageRefsMap = ref<any>() const bpmnMessageRefsMap = ref<any>()
const bpmnRootElements = ref<any>() const bpmnRootElements = ref<any>()
const bpmnInstances = () => (window as any).bpmnInstances const bpmnInstances = () => (window as any).bpmnInstances
const getBindMessage = () => { const getBindMessage = () => {
bpmnElement.value = bpmnInstances().bpmnElement bpmnElement.value = bpmnInstances().bpmnElement
bindMessageId.value = bpmnElement.value.businessObject?.messageRef?.id || '-1' bindMessageId.value = bpmnElement.value.businessObject?.messageRef?.id || '-1'
} }
const openMessageModel = () => { const openMessageModel = () => {
messageModelVisible.value = true messageModelVisible.value = true
newMessageForm.value = {} newMessageForm.value = {}
} }
const createNewMessage = () => { const createNewMessage = () => {
if (messageMap.value[newMessageForm.value.id]) { if (messageMap.value[newMessageForm.value.id]) {
message.error('该消息已存在请修改id后重新保存') message.error('该消息已存在请修改id后重新保存')
return return
} }
const newMessage = bpmnInstances().moddle.create('bpmn:Message', newMessageForm.value) const newMessage = bpmnInstances().moddle.create('bpmn:Message', newMessageForm.value)
bpmnRootElements.value.push(newMessage) bpmnRootElements.value.push(newMessage)
messageMap.value[newMessageForm.value.id] = newMessageForm.value.name messageMap.value[newMessageForm.value.id] = newMessageForm.value.name
bpmnMessageRefsMap.value[newMessageForm.value.id] = newMessage bpmnMessageRefsMap.value[newMessageForm.value.id] = newMessage
messageModelVisible.value = false messageModelVisible.value = false
} }
const updateTaskMessage = (messageId) => { const updateTaskMessage = (messageId) => {
if (messageId === '-1') { if (messageId === '-1') {
bpmnInstances().modeling.updateProperties(toRaw(bpmnElement.value), { bpmnInstances().modeling.updateProperties(toRaw(bpmnElement.value), {
messageRef: null messageRef: null
}) })
} else { } else {
bpmnInstances().modeling.updateProperties(toRaw(bpmnElement.value), { bpmnInstances().modeling.updateProperties(toRaw(bpmnElement.value), {
messageRef: bpmnMessageRefsMap.value[messageId] messageRef: bpmnMessageRefsMap.value[messageId]
}) })
} }
} }
onMounted(() => { onMounted(() => {
bpmnMessageRefsMap.value = Object.create(null) bpmnMessageRefsMap.value = Object.create(null)
bpmnRootElements.value = bpmnInstances().modeler.getDefinitions().rootElements bpmnRootElements.value = bpmnInstances().modeler.getDefinitions().rootElements
bpmnRootElements.value bpmnRootElements.value
.filter((el) => el.$type === 'bpmn:Message') .filter((el) => el.$type === 'bpmn:Message')
.forEach((m) => { .forEach((m) => {
bpmnMessageRefsMap.value[m.id] = m bpmnMessageRefsMap.value[m.id] = m
messageMap.value[m.id] = m.name messageMap.value[m.id] = m.name
}) })
messageMap.value['-1'] = '无' messageMap.value['-1'] = '无'
}) })
onBeforeUnmount(() => { onBeforeUnmount(() => {
bpmnElement.value = null bpmnElement.value = null
}) })
watch( watch(
() => props.id, () => props.id,
() => { () => {
// bpmnElement.value = bpmnInstances().bpmnElement // bpmnElement.value = bpmnInstances().bpmnElement
nextTick(() => { nextTick(() => {
getBindMessage() getBindMessage()
}) })
}, },
{ immediate: true } { immediate: true }
) )
</script> </script>

View File

@@ -1,99 +1,99 @@
<template> <template>
<div style="margin-top: 16px"> <div style="margin-top: 16px">
<el-form-item label="脚本格式"> <el-form-item label="脚本格式">
<el-input <el-input
v-model="scriptTaskForm.scriptFormat" v-model="scriptTaskForm.scriptFormat"
clearable clearable
@input="updateElementTask()" @input="updateElementTask()"
@change="updateElementTask()" @change="updateElementTask()"
/> />
</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>
</el-form-item> </el-form-item>
<el-form-item label="脚本" v-show="scriptTaskForm.scriptType === 'inline'"> <el-form-item label="脚本" v-show="scriptTaskForm.scriptType === 'inline'">
<el-input <el-input
v-model="scriptTaskForm.script" v-model="scriptTaskForm.script"
type="textarea" type="textarea"
resize="vertical" resize="vertical"
:autosize="{ minRows: 2, maxRows: 4 }" :autosize="{ minRows: 2, maxRows: 4 }"
clearable clearable
@input="updateElementTask()" @input="updateElementTask()"
@change="updateElementTask()" @change="updateElementTask()"
/> />
</el-form-item> </el-form-item>
<el-form-item label="资源地址" v-show="scriptTaskForm.scriptType === 'external'"> <el-form-item label="资源地址" v-show="scriptTaskForm.scriptType === 'external'">
<el-input <el-input
v-model="scriptTaskForm.resource" v-model="scriptTaskForm.resource"
clearable clearable
@input="updateElementTask()" @input="updateElementTask()"
@change="updateElementTask()" @change="updateElementTask()"
/> />
</el-form-item> </el-form-item>
<el-form-item label="结果变量"> <el-form-item label="结果变量">
<el-input <el-input
v-model="scriptTaskForm.resultVariable" v-model="scriptTaskForm.resultVariable"
clearable clearable
@input="updateElementTask()" @input="updateElementTask()"
@change="updateElementTask()" @change="updateElementTask()"
/> />
</el-form-item> </el-form-item>
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
defineOptions({ name: 'ScriptTask' }) defineOptions({ name: 'ScriptTask' })
const props = defineProps({ const props = defineProps({
id: String, id: String,
type: String type: String
}) })
const defaultTaskForm = ref({ const defaultTaskForm = ref({
scriptFormat: '', scriptFormat: '',
script: '', script: '',
resource: '', resource: '',
resultVariable: '' resultVariable: ''
}) })
const scriptTaskForm = ref<any>({}) const scriptTaskForm = ref<any>({})
const bpmnElement = ref() const bpmnElement = ref()
const bpmnInstances = () => (window as any)?.bpmnInstances const bpmnInstances = () => (window as any)?.bpmnInstances
const resetTaskForm = () => { const resetTaskForm = () => {
for (let key in defaultTaskForm.value) { for (let key in defaultTaskForm.value) {
let value = bpmnElement.value?.businessObject[key] || defaultTaskForm.value[key] let value = bpmnElement.value?.businessObject[key] || defaultTaskForm.value[key]
scriptTaskForm.value[key] = value scriptTaskForm.value[key] = value
} }
scriptTaskForm.value.scriptType = scriptTaskForm.value.script ? 'inline' : 'external' scriptTaskForm.value.scriptType = scriptTaskForm.value.script ? 'inline' : 'external'
} }
const updateElementTask = () => { const updateElementTask = () => {
let taskAttr = Object.create(null) let taskAttr = Object.create(null)
taskAttr.scriptFormat = scriptTaskForm.value.scriptFormat || null taskAttr.scriptFormat = scriptTaskForm.value.scriptFormat || null
taskAttr.resultVariable = scriptTaskForm.value.resultVariable || null taskAttr.resultVariable = scriptTaskForm.value.resultVariable || null
if (scriptTaskForm.value.scriptType === 'inline') { if (scriptTaskForm.value.scriptType === 'inline') {
taskAttr.script = scriptTaskForm.value.script || null taskAttr.script = scriptTaskForm.value.script || null
taskAttr.resource = null taskAttr.resource = null
} else { } else {
taskAttr.resource = scriptTaskForm.value.resource || null taskAttr.resource = scriptTaskForm.value.resource || null
taskAttr.script = null taskAttr.script = null
} }
bpmnInstances().modeling.updateProperties(toRaw(bpmnElement.value), taskAttr) bpmnInstances().modeling.updateProperties(toRaw(bpmnElement.value), taskAttr)
} }
onBeforeUnmount(() => { onBeforeUnmount(() => {
bpmnElement.value = null bpmnElement.value = null
}) })
watch( watch(
() => props.id, () => props.id,
() => { () => {
bpmnElement.value = bpmnInstances().bpmnElement bpmnElement.value = bpmnInstances().bpmnElement
nextTick(() => { nextTick(() => {
resetTaskForm() resetTaskForm()
}) })
}, },
{ immediate: true } { immediate: true }
) )
</script> </script>

View File

@@ -1,185 +1,188 @@
<template> <template>
<el-form label-width='100px'> <el-form label-width='100px'>
<el-form-item label='规则类型' prop='candidateStrategy'> <el-form-item label='规则类型' prop='candidateStrategy'>
<el-select <el-select
v-model='userTaskForm.candidateStrategy' v-model='userTaskForm.candidateStrategy'
clearable clearable
style='width: 100%' style='width: 100%'
@change='changeCandidateStrategy' @change='changeCandidateStrategy'
> filterable
<el-option >
v-for='dict in RuleTypeList' <el-option
:key='dict.code' v-for='dict in RuleTypeList'
:label='dict.name' :key='dict.code'
:value='dict.code' :label='dict.name'
/> :value='dict.code'
</el-select> />
</el-form-item> </el-select>
<el-form-item </el-form-item>
v-if='userTaskForm.candidateStrategy == 10' <el-form-item
label='指定角色' v-if='userTaskForm.candidateStrategy == 10'
prop='candidateParam' label='指定角色'
> prop='candidateParam'
<el-select >
v-model='userTaskForm.candidateParam' <el-select
clearable v-model='userTaskForm.candidateParam'
multiple clearable
style='width: 100%' multiple
@change='updateElementTask' style='width: 100%'
> @change='updateElementTask'
<el-option v-for='item in roleOptions' :key='item.id' :label='item.name' :value='item.id' /> filterable
</el-select> >
</el-form-item> <el-option v-for='item in roleOptions' :key='item.id' :label='item.name' :value='item.id' />
<el-form-item </el-select>
v-if='userTaskForm.candidateStrategy == 20' </el-form-item>
label='指定部门' <el-form-item
prop='candidateParam' v-if='userTaskForm.candidateStrategy == 20'
span='24' label='指定部门'
> prop='candidateParam'
<el-tree-select span='24'
ref='treeRef' >
v-model='userTaskForm.candidateParam' <el-tree-select
:data='deptTreeOptions' ref='treeRef'
:props='defaultProps' v-model='userTaskForm.candidateParam'
empty-text='加载中请稍后' :data='deptTreeOptions'
multiple :props='defaultProps'
node-key='id' empty-text='加载中请稍后'
show-checkbox multiple
@change='updateElementTask' node-key='id'
/> show-checkbox
</el-form-item> @change='updateElementTask'
/>
<el-form-item </el-form-item>
v-if='userTaskForm.candidateStrategy == 30'
label='指定用户' <el-form-item
prop='candidateParam' v-if='userTaskForm.candidateStrategy == 30'
span='24' label='指定用户'
> prop='candidateParam'
<el-select span='24'
v-model='userTaskForm.candidateParam' >
clearable <el-select
multiple v-model='userTaskForm.candidateParam'
style='width: 100%' clearable
@change='updateElementTask' multiple
> style='width: 100%'
<el-option @change='updateElementTask'
v-for='item in userOptions' filterable
:key='item.id' >
:label='item.name' <el-option
:value='item.id' v-for='item in userOptions'
/> :key='item.id'
</el-select> :label='item.name'
</el-form-item> :value='item.id'
/>
</el-form> </el-select>
</template> </el-form-item>
<script lang='ts' setup> </el-form>
import { defaultProps, handleTree } from '@/utils/tree' </template>
import { useDictData } from '@/stores/dictData'
const dictData = useDictData() <script lang='ts' setup>
const RuleTypeList = ref() import { defaultProps, handleTree } from '@/utils/tree'
import { getRoleSimpleList } from '@/api/user-boot/role' import { useDictData } from '@/stores/dictData'
import { getUserSimpleList } from '@/api/user-boot/user' const dictData = useDictData()
const RuleTypeList = ref()
defineOptions({ name: 'UserTask' }) import { getRoleSimpleList } from '@/api/user-boot/role'
const props = defineProps({ import { getUserSimpleList } from '@/api/user-boot/user'
id: String,
type: String defineOptions({ name: 'UserTask' })
}) const props = defineProps({
const userTaskForm = ref({ id: String,
candidateStrategy: '', // 分配规则 type: String
candidateParam: [] // 分配选项 })
}) const userTaskForm = ref({
const bpmnElement = ref() candidateStrategy: '', // 分配规则
const bpmnInstances = () => (window as any)?.bpmnInstances candidateParam: [] // 分配选项
})
export interface optionVO { const bpmnElement = ref()
id: string const bpmnInstances = () => (window as any)?.bpmnInstances
name: string
} export interface optionVO {
id: string
const roleOptions = ref<optionVO[]>([]) // 角色列表 name: string
const userOptions = ref<optionVO[]>([]) // 用户列表 }
const deptTreeOptions = ref() // 部门树
const roleOptions = ref<optionVO[]>([]) // 角色列表
const resetTaskForm = () => { const userOptions = ref<optionVO[]>([]) // 用户列表
const businessObject = bpmnElement.value.businessObject const deptTreeOptions = ref() // 部门树
if (!businessObject) {
return const resetTaskForm = () => {
} const businessObject = bpmnElement.value.businessObject
if (businessObject.candidateStrategy != undefined) { if (!businessObject) {
userTaskForm.value.candidateStrategy = businessObject.candidateStrategy return
} else { }
userTaskForm.value.candidateStrategy = undefined if (businessObject.candidateStrategy != undefined) {
} userTaskForm.value.candidateStrategy = businessObject.candidateStrategy
if (businessObject.candidateParam && businessObject.candidateParam.length > 0) { } else {
if (userTaskForm.value.candidateStrategy === 60) { userTaskForm.value.candidateStrategy = undefined
// 特殊:流程表达式,只有一个 input 输入框 }
userTaskForm.value.candidateParam = [businessObject.candidateParam] if (businessObject.candidateParam && businessObject.candidateParam.length > 0) {
} else { if (userTaskForm.value.candidateStrategy === 60) {
userTaskForm.value.candidateParam = businessObject.candidateParam.split(',') // 特殊:流程表达式,只有一个 input 输入框
} userTaskForm.value.candidateParam = [businessObject.candidateParam]
} else { } else {
userTaskForm.value.candidateParam = [] userTaskForm.value.candidateParam = businessObject.candidateParam.split(',')
} }
} } else {
userTaskForm.value.candidateParam = []
/** 更新 candidateStrategy 字段时,需要清空 candidateParam并触发 bpmn 图更新 */ }
const changeCandidateStrategy = () => { }
userTaskForm.value.candidateParam = []
updateElementTask() /** 更新 candidateStrategy 字段时,需要清空 candidateParam并触发 bpmn 图更新 */
} const changeCandidateStrategy = () => {
userTaskForm.value.candidateParam = []
/** 选中某个 options 时候,更新 bpmn 图 */ updateElementTask()
const updateElementTask = () => { }
bpmnInstances().modeling.updateProperties(toRaw(bpmnElement.value), {
candidateStrategy: userTaskForm.value.candidateStrategy, /** 选中某个 options 时候,更新 bpmn 图 */
candidateParam: userTaskForm.value.candidateParam.join(',') const updateElementTask = () => {
}) bpmnInstances().modeling.updateProperties(toRaw(bpmnElement.value), {
} candidateStrategy: userTaskForm.value.candidateStrategy,
candidateParam: userTaskForm.value.candidateParam.join(',')
// 打开监听器弹窗 })
const processExpressionDialogRef = ref() }
const openProcessExpressionDialog = async () => {
processExpressionDialogRef.value.open() // 打开监听器弹窗
} const processExpressionDialogRef = ref()
const selectProcessExpression = (expression) => { const openProcessExpressionDialog = async () => {
userTaskForm.value.candidateParam = [expression.expression] processExpressionDialogRef.value.open()
} }
const selectProcessExpression = (expression) => {
watch( userTaskForm.value.candidateParam = [expression.expression]
() => props.id, }
() => {
bpmnElement.value = bpmnInstances().bpmnElement watch(
nextTick(() => { () => props.id,
resetTaskForm() () => {
}) bpmnElement.value = bpmnInstances().bpmnElement
}, nextTick(() => {
{ immediate: true } resetTaskForm()
) })
},
{ immediate: true }
onMounted(async () => { )
// 获得角色列表
await getRoleSimpleList().then(res => {
roleOptions.value = res.data onMounted(async () => {
}) // 获得角色列表
await getRoleSimpleList().then(res => {
// 获得用户列表 roleOptions.value = res.data
await getUserSimpleList().then(res => { })
userOptions.value = res.data
}) // 获得用户列表
//传入type的名称 await getUserSimpleList().then(res => {
RuleTypeList.value = dictData.getBasicData('rule_type') userOptions.value = res.data
})
// //传入type的名称
// // 获得部门列表 RuleTypeList.value = dictData.getBasicData('rule_type')
// const deptOptions = await DeptApi.getSimpleDeptList()
// deptTreeOptions.value = handleTree(deptOptions, 'id') //
}) // // 获得部门列表
// const deptOptions = await DeptApi.getSimpleDeptList()
onBeforeUnmount(() => { // deptTreeOptions.value = handleTree(deptOptions, 'id')
bpmnElement.value = null })
})
</script> onBeforeUnmount(() => {
bpmnElement.value = null
})
</script>

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' : '610px' }">
<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

@@ -1,49 +1,49 @@
<template> <template>
<div v-if="isUrl" :style="urlIconStyle" class="url-svg svg-icon icon" /> <div v-if="isUrl" :style="urlIconStyle" class="url-svg svg-icon icon" />
<svg v-else class="svg-icon icon" :style="iconStyle"> <svg v-else class="svg-icon icon" :style="iconStyle">
<use :href="iconName" /> <use :href="iconName" />
</svg> </svg>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { computed, type CSSProperties } from 'vue' 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
} }
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
name: '', name: '',
size: '18px', size: '18px',
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 {
color: props.color, color: props.color,
fontSize: s, fontSize: s,
} }
}) })
const isUrl = computed(() => isExternal(props.name)) const isUrl = computed(() => isExternal(props.name))
const urlIconStyle = computed(() => { const urlIconStyle = computed(() => {
return { return {
width: s, width: s,
height: s, height: s,
mask: `url(${props.name}) no-repeat 50% 50%`, mask: `url(${props.name}) no-repeat 50% 50%`,
'-webkit-mask': `url(${props.name}) no-repeat 50% 50%`, '-webkit-mask': `url(${props.name}) no-repeat 50% 50%`,
} }
}) })
</script> </script>
<style scoped> <style scoped>
.svg-icon { .svg-icon {
width: 1em; width: 1em;
height: 1em; height: 1em;
fill: currentColor; fill: currentColor;
overflow: hidden; overflow: hidden;
} }
</style> </style>

View File

@@ -1,23 +1,41 @@
<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'
export default defineComponent({ /** storage 开启时列必须有稳定 field序号/操作等无 field 列按 title 生成 */
name: 'Column', function resolveColumnField(attr: Record<string, any>): string | undefined {
props: { if (attr.field) return attr.field
attr: { if (attr.prop) return attr.prop
type: Object, // checkbox / radio / expand / seq 等类型列由 vxe 内部处理,可不强制 field
required: true if (attr.type && ['checkbox', 'radio', 'expand', 'html'].includes(attr.type)) {
} return undefined
}, }
setup(props, { slots }) { if (attr.title != null && attr.title !== '') {
const attr = reactive(props.attr) return `__col_${attr.title}`
attr['align'] = attr['align'] ? attr['align'] : 'center' }
attr['column-key'] = attr['column-key'] ? attr['column-key'] : attr.prop || uuid() return `col_${uuid()}`
return () => { }
return createVNode(Column, attr, slots.default)
} export default defineComponent({
} name: 'Column',
}) props: {
</script> attr: {
type: Object,
required: true
}
},
setup(props, { slots }) {
const attr = reactive({ ...props.attr })
attr['align'] = attr['align'] ? attr['align'] : 'center'
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 createVNode(Column, attr, slots.default)
}
}
})
</script>

View File

@@ -6,14 +6,12 @@ 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: { // 注意:全局不要默认开启 treeConfig会与 stripe 冲突;树表在页面自行配置
reserve: true customConfig: { enabled: true, allowFixed: true, storage: false, showFooter: false, immediate: true ,mode:'default'},
}, showOverflow: 'tooltip',
showOverflow: true, showHeaderOverflow: false
showFooter:true
} }

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' : '610px' }"
>
<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,35 +1,36 @@
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 localeStore = useLocaleStoreWithOut() const setI18nLanguage = (locale: LocaleType) => {
const localeStore = useLocaleStoreWithOut()
if (i18n.mode === 'legacy') {
i18n.global.locale = locale if (i18n.mode === 'legacy') {
} else { i18n.global.locale = locale
;(i18n.global.locale as any).value = locale } else {
} ;(i18n.global.locale as any).value = locale
localeStore.setCurrentLocale({ }
lang: locale localeStore.setCurrentLocale({
}) lang: locale
setHtmlPageLang(locale) })
} setHtmlPageLang(locale)
}
export const useLocale = () => {
// Switching the language will change the locale of useI18n export const useLocale = () => {
// And submit to configuration modification // Switching the language will change the locale of useI18n
const changeLocale = async (locale: LocaleType) => { // And submit to configuration modification
const globalI18n = i18n.global const changeLocale = async (locale: LocaleType) => {
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)
}
return {
changeLocale return {
} changeLocale
} }
}

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

@@ -1,452 +1,453 @@
<template> <template>
<div class="layout-config-drawer"> <div class="layout-config-drawer">
<el-drawer :model-value="configStore.layout.showDrawer" title="布局配置" size="310px" @close="onCloseDrawer"> <el-drawer :model-value="configStore.layout.showDrawer" title="布局配置" size="310px" @close="onCloseDrawer">
<el-scrollbar class="layout-mode-style-scrollbar"> <el-scrollbar class="layout-mode-style-scrollbar">
<el-form ref="formRef" :model="configStore.layout"> <el-form ref="formRef" :model="configStore.layout">
<div class="layout-mode-styles-box"> <div class="layout-mode-styles-box">
<el-divider border-style="dashed">布局方式</el-divider> <el-divider border-style="dashed">布局方式</el-divider>
<div class="layout-mode-box-style"> <div class="layout-mode-box-style">
<el-row class="layout-mode-box-style-row" :gutter="10"> <el-row class="layout-mode-box-style-row" :gutter="10">
<el-col :span="12"> <el-col :span="12">
<div <div
@click="setLayoutMode('Default')" @click="setLayoutMode('Default')"
class="layout-mode-style default" class="layout-mode-style default"
:class="configStore.layout.layoutMode == 'Default' ? 'active' : ''" :class="configStore.layout.layoutMode == 'Default' ? 'active' : ''"
> >
<div class="layout-mode-style-box"> <div class="layout-mode-style-box">
<div class="layout-mode-style-aside"></div> <div class="layout-mode-style-aside"></div>
<div class="layout-mode-style-container-box"> <div class="layout-mode-style-container-box">
<div class="layout-mode-style-header"></div> <div class="layout-mode-style-header"></div>
<div class="layout-mode-style-container"></div> <div class="layout-mode-style-container"></div>
</div> </div>
</div> </div>
<div class="layout-mode-style-name">默认</div> <div class="layout-mode-style-name">默认</div>
</div> </div>
</el-col> </el-col>
<el-col :span="12"> <el-col :span="12">
<div <div
@click="setLayoutMode('Classic')" @click="setLayoutMode('Classic')"
class="layout-mode-style classic" class="layout-mode-style classic"
:class="configStore.layout.layoutMode == 'Classic' ? 'active' : ''" :class="configStore.layout.layoutMode == 'Classic' ? 'active' : ''"
> >
<div class="layout-mode-style-box"> <div class="layout-mode-style-box">
<div class="layout-mode-style-aside"></div> <div class="layout-mode-style-aside"></div>
<div class="layout-mode-style-container-box"> <div class="layout-mode-style-container-box">
<div class="layout-mode-style-header"></div> <div class="layout-mode-style-header"></div>
<div class="layout-mode-style-container"></div> <div class="layout-mode-style-container"></div>
</div> </div>
</div> </div>
<div class="layout-mode-style-name">经典</div> <div class="layout-mode-style-name">经典</div>
</div> </div>
</el-col> </el-col>
</el-row> </el-row>
<el-row :gutter="10"> <el-row :gutter="10">
<el-col :span="12"> <el-col :span="12">
<div <div
@click="setLayoutMode('Streamline')" @click="setLayoutMode('Streamline')"
class="layout-mode-style streamline" class="layout-mode-style streamline"
:class="configStore.layout.layoutMode == 'Streamline' ? 'active' : ''" :class="configStore.layout.layoutMode == 'Streamline' ? 'active' : ''"
> >
<div class="layout-mode-style-box"> <div class="layout-mode-style-box">
<div class="layout-mode-style-container-box"> <div class="layout-mode-style-container-box">
<div class="layout-mode-style-header"></div> <div class="layout-mode-style-header"></div>
<div class="layout-mode-style-container"></div> <div class="layout-mode-style-container"></div>
</div> </div>
</div> </div>
<div class="layout-mode-style-name">单栏</div> <div class="layout-mode-style-name">单栏</div>
</div> </div>
</el-col> </el-col>
</el-row> </el-row>
</div> </div>
<el-divider border-style="dashed">全局</el-divider> <el-divider border-style="dashed">全局</el-divider>
<div class="layout-config-global"> <div class="layout-config-global">
<el-form-item label="后台页面切换动画"> <el-form-item label="后台页面切换动画">
<el-select <el-select
@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-left" value="slide-left"></el-option> <el-option label="slide-right" value="slide-right"></el-option>
<el-option label="el-fade-in-linear" value="el-fade-in-linear"></el-option> <el-option label="slide-left" value="slide-left"></el-option>
<el-option label="el-fade-in" value="el-fade-in"></el-option> <el-option label="el-fade-in-linear" value="el-fade-in-linear"></el-option>
<el-option label="el-zoom-in-center" value="el-zoom-in-center"></el-option> <el-option label="el-fade-in" value="el-fade-in"></el-option>
<el-option label="el-zoom-in-top" value="el-zoom-in-top"></el-option> <el-option label="el-zoom-in-center" value="el-zoom-in-center"></el-option>
<el-option label="el-zoom-in-bottom" value="el-zoom-in-bottom"></el-option> <el-option label="el-zoom-in-top" value="el-zoom-in-top"></el-option>
</el-select> <el-option label="el-zoom-in-bottom" value="el-zoom-in-bottom"></el-option>
</el-form-item> </el-select>
<el-form-item label="组件主题色"> </el-form-item>
<el-color-picker <el-form-item label="组件主题色">
@change="onCommitColorState($event, 'elementUiPrimary')" <el-color-picker
:model-value="configStore.getColorVal('elementUiPrimary')" @change="onCommitColorState($event, 'elementUiPrimary')"
/> :model-value="configStore.getColorVal('elementUiPrimary')"
</el-form-item> />
<el-form-item label="表格标题栏背景颜色"> </el-form-item>
<el-color-picker <el-form-item label="表格标题栏背景颜色">
@change="onCommitColorState($event, 'tableHeaderBackground')" <el-color-picker
:model-value="configStore.getColorVal('tableHeaderBackground')" @change="onCommitColorState($event, 'tableHeaderBackground')"
/> :model-value="configStore.getColorVal('tableHeaderBackground')"
</el-form-item> />
<el-form-item label="表格标题栏文字颜色"> </el-form-item>
<el-color-picker <el-form-item label="表格标题栏文字颜色">
@change="onCommitColorState($event, 'tableHeaderColor')" <el-color-picker
:model-value="configStore.getColorVal('tableHeaderColor')" @change="onCommitColorState($event, 'tableHeaderColor')"
/> :model-value="configStore.getColorVal('tableHeaderColor')"
</el-form-item> />
<el-form-item label="表格激活栏颜色"> </el-form-item>
<el-color-picker <el-form-item label="表格激活栏颜色">
@change="onCommitColorState($event, 'tableCurrent')" <el-color-picker
:model-value="configStore.getColorVal('tableCurrent')" @change="onCommitColorState($event, 'tableCurrent')"
/> :model-value="configStore.getColorVal('tableCurrent')"
</el-form-item> />
</div> </el-form-item>
<el-divider border-style="dashed">侧边栏</el-divider> </div>
<div class="layout-config-aside"> <el-divider border-style="dashed">侧边栏</el-divider>
<el-form-item label="侧边菜单栏背景色"> <div class="layout-config-aside">
<el-color-picker <el-form-item label="侧边菜单栏背景色">
@change="onCommitColorState($event, 'menuBackground')" <el-color-picker
:model-value="configStore.getColorVal('menuBackground')" @change="onCommitColorState($event, 'menuBackground')"
/> :model-value="configStore.getColorVal('menuBackground')"
</el-form-item> />
<el-form-item label="侧边菜单文字颜色"> </el-form-item>
<el-color-picker <el-form-item label="侧边菜单文字颜色">
@change="onCommitColorState($event, 'menuColor')" <el-color-picker
:model-value="configStore.getColorVal('menuColor')" @change="onCommitColorState($event, 'menuColor')"
/> :model-value="configStore.getColorVal('menuColor')"
</el-form-item> />
<el-form-item label="侧边菜单激活项背景色"> </el-form-item>
<el-color-picker <el-form-item label="侧边菜单激活项背景色">
@change="onCommitColorState($event, 'menuActiveBackground')" <el-color-picker
:model-value="configStore.getColorVal('menuActiveBackground')" @change="onCommitColorState($event, 'menuActiveBackground')"
/> :model-value="configStore.getColorVal('menuActiveBackground')"
</el-form-item> />
<el-form-item label="侧边菜单激活项文字色"> </el-form-item>
<el-color-picker <el-form-item label="侧边菜单激活项文字色">
@change="onCommitColorState($event, 'menuActiveColor')" <el-color-picker
:model-value="configStore.getColorVal('menuActiveColor')" @change="onCommitColorState($event, 'menuActiveColor')"
/> :model-value="configStore.getColorVal('menuActiveColor')"
</el-form-item> />
<el-form-item label="显示侧边菜单顶栏(LOGO栏)"> </el-form-item>
<el-switch <el-form-item label="显示侧边菜单顶栏(LOGO栏)">
@change="onCommitState($event, 'menuShowTopBar')" <el-switch
:model-value="configStore.layout.menuShowTopBar" @change="onCommitState($event, 'menuShowTopBar')"
></el-switch> :model-value="configStore.layout.menuShowTopBar"
</el-form-item> ></el-switch>
<el-form-item label="侧边菜单顶栏背景色"> </el-form-item>
<el-color-picker <el-form-item label="侧边菜单顶栏背景色">
@change="onCommitColorState($event, 'menuTopBarBackground')" <el-color-picker
:model-value="configStore.getColorVal('menuTopBarBackground')" @change="onCommitColorState($event, 'menuTopBarBackground')"
/> :model-value="configStore.getColorVal('menuTopBarBackground')"
</el-form-item> />
<el-form-item label="侧边菜单宽度(展开时)"> </el-form-item>
<el-input <el-form-item label="侧边菜单宽度(展开时)">
@input="onCommitState($event, 'menuWidth')" <el-input
type="number" @input="onCommitState($event, 'menuWidth')"
:step="10" type="number"
:model-value="configStore.layout.menuWidth" :step="10"
> :model-value="configStore.layout.menuWidth"
<template #append>px</template> >
</el-input> <template #append>px</template>
</el-form-item> </el-input>
<!-- <el-form-item label="侧边菜单默认图标">--> </el-form-item>
<!-- <IconSelector--> <!-- <el-form-item label="侧边菜单默认图标">-->
<!-- @change="onCommitMenuDefaultIcon($event, 'menuDefaultIcon')"--> <!-- <IconSelector-->
<!-- :model-value="configStore.layout.menuDefaultIcon"--> <!-- @change="onCommitMenuDefaultIcon($event, 'menuDefaultIcon')"-->
<!-- />--> <!-- :model-value="configStore.layout.menuDefaultIcon"-->
<!-- </el-form-item>--> <!-- />-->
<!-- <el-form-item label="侧边菜单水平折叠">--> <!-- </el-form-item>-->
<!-- <el-switch--> <!-- <el-form-item label="侧边菜单水平折叠">-->
<!-- @change="onCommitState($event, 'menuCollapse')"--> <!-- <el-switch-->
<!-- :model-value="configStore.layout.menuCollapse"--> <!-- @change="onCommitState($event, 'menuCollapse')"-->
<!-- ></el-switch>--> <!-- :model-value="configStore.layout.menuCollapse"-->
<!-- </el-form-item>--> <!-- ></el-switch>-->
<!-- <el-form-item label="侧边菜单手风琴">--> <!-- </el-form-item>-->
<!-- <el-switch--> <!-- <el-form-item label="侧边菜单手风琴">-->
<!-- @change="onCommitState($event, 'menuUniqueOpened')"--> <!-- <el-switch-->
<!-- :model-value="configStore.layout.menuUniqueOpened"--> <!-- @change="onCommitState($event, 'menuUniqueOpened')"-->
<!-- ></el-switch>--> <!-- :model-value="configStore.layout.menuUniqueOpened"-->
<!-- </el-form-item>--> <!-- ></el-switch>-->
</div> <!-- </el-form-item>-->
</div>
<el-divider border-style="dashed">顶栏</el-divider>
<div class="layout-config-aside"> <el-divider border-style="dashed">顶栏</el-divider>
<el-form-item label="顶栏背景色"> <div class="layout-config-aside">
<el-color-picker <el-form-item label="顶栏背景色">
@change="onCommitColorState($event, 'headerBarBackground')" <el-color-picker
:model-value="configStore.getColorVal('headerBarBackground')" @change="onCommitColorState($event, 'headerBarBackground')"
/> :model-value="configStore.getColorVal('headerBarBackground')"
</el-form-item> />
<el-form-item label="顶栏文字色"> </el-form-item>
<el-color-picker <el-form-item label="顶栏文字色">
@change="onCommitColorState($event, 'headerBarTabColor')" <el-color-picker
:model-value="configStore.getColorVal('headerBarTabColor')" @change="onCommitColorState($event, 'headerBarTabColor')"
/> :model-value="configStore.getColorVal('headerBarTabColor')"
</el-form-item> />
<!-- <el-form-item label="顶栏悬停时背景色">--> </el-form-item>
<!-- <el-color-picker--> <!-- <el-form-item label="顶栏悬停时背景色">-->
<!-- @change="onCommitColorState($event, 'headerBarHoverBackground')"--> <!-- <el-color-picker-->
<!-- :model-value="configStore.getColorVal('headerBarHoverBackground')"--> <!-- @change="onCommitColorState($event, 'headerBarHoverBackground')"-->
<!-- />--> <!-- :model-value="configStore.getColorVal('headerBarHoverBackground')"-->
<!-- </el-form-item>--> <!-- />-->
<!-- <el-form-item label="顶栏菜单激活项背景色">--> <!-- </el-form-item>-->
<!-- <el-color-picker--> <!-- <el-form-item label="顶栏菜单激活项背景色">-->
<!-- @change="onCommitColorState($event, 'headerBarTabActiveBackground')"--> <!-- <el-color-picker-->
<!-- :model-value="configStore.getColorVal('headerBarTabActiveBackground')"--> <!-- @change="onCommitColorState($event, 'headerBarTabActiveBackground')"-->
<!-- />--> <!-- :model-value="configStore.getColorVal('headerBarTabActiveBackground')"-->
<!-- </el-form-item>--> <!-- />-->
<!-- <el-form-item label="顶栏菜单激活项文字色">--> <!-- </el-form-item>-->
<!-- <el-color-picker--> <!-- <el-form-item label="顶栏菜单激活项文字色">-->
<!-- @change="onCommitColorState($event, 'headerBarTabActiveColor')"--> <!-- <el-color-picker-->
<!-- :model-value="configStore.getColorVal('headerBarTabActiveColor')"--> <!-- @change="onCommitColorState($event, 'headerBarTabActiveColor')"-->
<!-- />--> <!-- :model-value="configStore.getColorVal('headerBarTabActiveColor')"-->
<!-- </el-form-item>--> <!-- />-->
</div> <!-- </el-form-item>-->
<el-popconfirm @confirm="restoreDefault" title="确定要恢复全部配置到默认值吗?"> </div>
<template #reference> <el-popconfirm @confirm="restoreDefault" title="确定要恢复全部配置到默认值吗?">
<div class="ba-center"> <template #reference>
<el-button class="w80" type="info">恢复默认</el-button> <div class="ba-center">
</div> <el-button class="w80" type="info">恢复默认</el-button>
</template> </div>
</el-popconfirm> </template>
</div> </el-popconfirm>
</el-form> </div>
</el-scrollbar> </el-form>
</el-drawer> </el-scrollbar>
</div> </el-drawer>
</template> </div>
</template>
<script setup lang="ts">
import { useConfig } from '@/stores/config' <script setup lang="ts">
import { useNavTabs } from '@/stores/navTabs' import { useConfig } from '@/stores/config'
import { useRouter } from 'vue-router' import { useNavTabs } from '@/stores/navTabs'
import IconSelector from '@/components/baInput/components/iconSelector.vue' import { useRouter } from 'vue-router'
import { STORE_CONFIG } from '@/stores/constant/cacheKey' import IconSelector from '@/components/baInput/components/iconSelector.vue'
import { Local, Session } from '@/utils/storage' import { STORE_CONFIG } from '@/stores/constant/cacheKey'
import type { Layout } from '@/stores/interface' import { Local, Session } from '@/utils/storage'
import type { Layout } from '@/stores/interface'
const configStore = useConfig()
const navTabs = useNavTabs() const configStore = useConfig()
const router = useRouter() const navTabs = useNavTabs()
const router = useRouter()
const onCommitState = (value: any, name: any) => {
configStore.setLayout(name, value) const onCommitState = (value: any, name: any) => {
} configStore.setLayout(name, value)
}
const onCommitColorState = (value: string | null, name: keyof Layout) => {
if (value === null) return const onCommitColorState = (value: string | null, name: keyof Layout) => {
const colors = configStore.layout[name] as string[] if (value === null) return
if (configStore.layout.isDark) { const colors = configStore.layout[name] as string[]
colors[1] = value if (configStore.layout.isDark) {
} else { colors[1] = value
colors[0] = value } else {
} colors[0] = value
configStore.setLayout(name, colors) }
} configStore.setLayout(name, colors)
}
const setLayoutMode = (mode: string) => {
configStore.setLayoutMode(mode) const setLayoutMode = (mode: string) => {
} configStore.setLayoutMode(mode)
}
// 修改默认菜单图标
const onCommitMenuDefaultIcon = (value: any, name: any) => { // 修改默认菜单图标
configStore.setLayout(name, value) const onCommitMenuDefaultIcon = (value: any, name: any) => {
configStore.setLayout(name, value)
const menus = navTabs.state.tabsViewRoutes
navTabs.setTabsViewRoutes([]) const menus = navTabs.state.tabsViewRoutes
setTimeout(() => { navTabs.setTabsViewRoutes([])
navTabs.setTabsViewRoutes(menus) setTimeout(() => {
}, 200) navTabs.setTabsViewRoutes(menus)
} }, 200)
}
const onCloseDrawer = () => {
configStore.setLayout('showDrawer', false) const onCloseDrawer = () => {
} configStore.setLayout('showDrawer', false)
}
const restoreDefault = () => {
Local.remove(STORE_CONFIG) const restoreDefault = () => {
router.go(0) Local.remove(STORE_CONFIG)
} router.go(0)
</script> }
</script>
<style scoped lang="scss">
<style scoped lang="scss">
.layout-config-drawer :deep(.el-input__inner) {
padding: 0 0 0 6px; .layout-config-drawer :deep(.el-input__inner) {
} padding: 0 0 0 6px;
}
.layout-config-drawer :deep(.el-input-group__append) {
padding: 0 10px; .layout-config-drawer :deep(.el-input-group__append) {
} padding: 0 10px;
}
.layout-config-drawer :deep(.el-drawer__header) {
margin-bottom: 0 !important; .layout-config-drawer :deep(.el-drawer__header) {
} margin-bottom: 0 !important;
}
.layout-config-drawer :deep(.el-drawer__body) {
padding: 0; .layout-config-drawer :deep(.el-drawer__body) {
} padding: 0;
}
.layout-mode-styles-box {
padding: 20px; .layout-mode-styles-box {
} padding: 20px;
}
.layout-mode-box-style-row {
margin-bottom: 15px; .layout-mode-box-style-row {
} margin-bottom: 15px;
}
.layout-mode-style {
position: relative; .layout-mode-style {
height: 100px; position: relative;
border: 1px solid var(--el-border-color-light); height: 100px;
border-radius: var(--el-border-radius-small); border: 1px solid var(--el-border-color-light);
border-radius: var(--el-border-radius-small);
&:hover,
&.active { &:hover,
border: 1px solid var(--el-color-primary); &.active {
} border: 1px solid var(--el-color-primary);
}
.layout-mode-style-name {
position: absolute; .layout-mode-style-name {
display: flex; position: absolute;
align-items: center; display: flex;
justify-content: center; align-items: center;
color: var(--el-color-primary-light-5); justify-content: center;
border-radius: 50%; color: var(--el-color-primary-light-5);
height: 50px; border-radius: 50%;
width: 50px; height: 50px;
border: 1px solid var(--el-color-primary-light-3); width: 50px;
} border: 1px solid var(--el-color-primary-light-3);
}
.layout-mode-style-box {
display: flex; .layout-mode-style-box {
align-items: center; display: flex;
justify-content: center; align-items: center;
width: 100%; justify-content: center;
height: 100%; width: 100%;
} height: 100%;
}
&.default {
display: flex; &.default {
align-items: center; display: flex;
justify-content: center; align-items: center;
justify-content: center;
.layout-mode-style-aside {
width: 18%; .layout-mode-style-aside {
height: 90%; width: 18%;
background-color: var(--el-border-color-lighter); height: 90%;
} background-color: var(--el-border-color-lighter);
}
.layout-mode-style-container-box {
width: 68%; .layout-mode-style-container-box {
height: 90%; width: 68%;
margin-left: 4%; height: 90%;
margin-left: 4%;
.layout-mode-style-header {
width: 100%; .layout-mode-style-header {
height: 10%; width: 100%;
background-color: var(--el-border-color-lighter); height: 10%;
} background-color: var(--el-border-color-lighter);
}
.layout-mode-style-container {
width: 100%; .layout-mode-style-container {
height: 85%; width: 100%;
background-color: var(--el-border-color-extra-light); height: 85%;
margin-top: 5%; background-color: var(--el-border-color-extra-light);
} margin-top: 5%;
} }
} }
}
&.classic {
display: flex; &.classic {
align-items: center; display: flex;
justify-content: center; align-items: center;
justify-content: center;
.layout-mode-style-aside {
width: 18%; .layout-mode-style-aside {
height: 100%; width: 18%;
background-color: var(--el-border-color-lighter); height: 100%;
} background-color: var(--el-border-color-lighter);
}
.layout-mode-style-container-box {
width: 82%; .layout-mode-style-container-box {
height: 100%; width: 82%;
height: 100%;
.layout-mode-style-header {
width: 100%; .layout-mode-style-header {
height: 10%; width: 100%;
background-color: var(--el-border-color); height: 10%;
} background-color: var(--el-border-color);
}
.layout-mode-style-container {
width: 100%; .layout-mode-style-container {
height: 90%; width: 100%;
background-color: var(--el-border-color-extra-light); height: 90%;
} background-color: var(--el-border-color-extra-light);
} }
} }
}
&.streamline {
display: flex; &.streamline {
align-items: center; display: flex;
justify-content: center; align-items: center;
justify-content: center;
.layout-mode-style-container-box {
width: 100%; .layout-mode-style-container-box {
height: 100%; width: 100%;
height: 100%;
.layout-mode-style-header {
width: 100%; .layout-mode-style-header {
height: 10%; width: 100%;
background-color: var(--el-border-color); height: 10%;
} background-color: var(--el-border-color);
}
.layout-mode-style-container {
width: 100%; .layout-mode-style-container {
height: 90%; width: 100%;
background-color: var(--el-border-color-extra-light); height: 90%;
} background-color: var(--el-border-color-extra-light);
} }
} }
}
&.double {
display: flex; &.double {
align-items: center; display: flex;
justify-content: center; align-items: center;
justify-content: center;
.layout-mode-style-aside {
width: 18%; .layout-mode-style-aside {
height: 100%; width: 18%;
background-color: var(--el-border-color); height: 100%;
} background-color: var(--el-border-color);
}
.layout-mode-style-container-box {
width: 82%; .layout-mode-style-container-box {
height: 100%; width: 82%;
height: 100%;
.layout-mode-style-header {
width: 100%; .layout-mode-style-header {
height: 10%; width: 100%;
background-color: var(--el-border-color); height: 10%;
} background-color: var(--el-border-color);
}
.layout-mode-style-container {
width: 100%; .layout-mode-style-container {
height: 90%; width: 100%;
background-color: var(--el-border-color-extra-light); height: 90%;
} background-color: var(--el-border-color-extra-light);
} }
} }
} }
}
.w80 {
width: 90%; .w80 {
} width: 90%;
</style> }
</style>

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

@@ -1,128 +1,130 @@
<template> <template>
<div> <div>
<component :is="config.layout.layoutMode"></component> <component :is="config.layout.layoutMode"></component>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { reactive } from 'vue' import { reactive } from 'vue'
import { useConfig } from '@/stores/config' import { useConfig } from '@/stores/config'
import { useNavTabs } from '@/stores/navTabs' import { useNavTabs } from '@/stores/navTabs'
import { useAdminInfo } from '@/stores/adminInfo' import { useAdminInfo } from '@/stores/adminInfo'
import { useDictData } from '@/stores/dictData' import { useDictData } from '@/stores/dictData'
import { useRoute } from 'vue-router' import { useRoute } from 'vue-router'
import Default from '@/layouts/admin/container/default.vue' import Default from '@/layouts/admin/container/default.vue'
import Classic from '@/layouts/admin/container/classic.vue' import Classic from '@/layouts/admin/container/classic.vue'
import Streamline from '@/layouts/admin/container/streamline.vue' import Streamline from '@/layouts/admin/container/streamline.vue'
import Double from '@/layouts/admin/container/double.vue' import Double from '@/layouts/admin/container/double.vue'
import { onMounted, onBeforeMount } from 'vue' import { onMounted, onBeforeMount } from 'vue'
import { handleAdminRoute, getFirstRoute, routePush } from '@/utils/router' import { handleAdminRoute, getFirstRoute, routePush } from '@/utils/router'
import router from '@/router/index' import router from '@/router/index'
import { useEventListener } from '@vueuse/core' import { useEventListener } from '@vueuse/core'
import { isEmpty } from 'lodash-es' import { isEmpty } from 'lodash-es'
import { setNavTabsWidth } from '@/utils/layout' import { setNavTabsWidth } from '@/utils/layout'
import { adminBaseRoutePath } from '@/router/static' import { adminBaseRoutePath } from '@/router/static'
import { getRouteMenu, dictDataCache } from '@/api/auth' import { getRouteMenu, dictDataCache } from '@/api/auth'
import { getAreaList, areaSelect } from '@/api/common' import { getAreaList, areaSelect } from '@/api/common'
import { BasicDictData } from '@/stores/interface' import { BasicDictData } from '@/stores/interface'
import { getAllUserSimpleList, getUserById } from '@/api/user-boot/user' import { getAllUserSimpleList, getUserById } from '@/api/user-boot/user'
defineOptions({ defineOptions({
components: { Default, Classic, Streamline, Double } components: { Default, Classic, Streamline, Double }
}) })
const navTabs = useNavTabs() const navTabs = useNavTabs()
const config = useConfig() const config = useConfig()
const route = useRoute() const route = useRoute()
const adminInfo = useAdminInfo() const adminInfo = useAdminInfo()
const dictData = useDictData() const dictData = useDictData()
const state = reactive({ const state = reactive({
autoMenuCollapseLock: false autoMenuCollapseLock: false
}) })
onMounted(() => { onMounted(() => {
// if (!adminInfo.token) return router.push({ name: 'login' }) // if (!adminInfo.token) return router.push({ name: 'login' })
init() init()
setNavTabsWidth() setNavTabsWidth()
useEventListener(window, 'resize', setNavTabsWidth) useEventListener(window, 'resize', setNavTabsWidth)
}) })
onBeforeMount(() => { onBeforeMount(() => {
onAdaptiveLayout() onAdaptiveLayout()
useEventListener(window, 'resize', onAdaptiveLayout) useEventListener(window, 'resize', onAdaptiveLayout)
}) })
const init = async () => { const init = async () => {
await Promise.all([getAreaList(), dictDataCache(), getUserById(), areaSelect(), getAllUserSimpleList()]).then(res => { await Promise.all([getAreaList(), dictDataCache(), getUserById(), areaSelect(), getAllUserSimpleList()]).then(res => {
dictData.state.area = res[0].data dictData.state.area = res[0].data
dictData.state.basic = res[1].data dictData.state.basic = res[1].data
dictData.state.userList = res[4].data dictData.state.userList = res[4].data
adminInfo.dataFill(res[2].data) adminInfo.dataFill(res[2].data)
dictData.state.areaTree = res[3].data dictData.state.areaTree = res[3].data
}) })
/** /**
* 后台初始化请求,获取站点配置,动态路由等信息 * 后台初始化请求,获取站点配置,动态路由等信息
*/ */
getRouteMenu().then((res: any) => { getRouteMenu().then((res: any) => {
const handlerMenu = (data: any) => { const handlerMenu = (data: any) => {
data.forEach((item: any) => { data.forEach((item: any) => {
item.routePath = item.routePath =
item.routePath[0] == '/' ? item.routePath.substring(1, item.routePath.length) : item.routePath item.routePath[0] == '/' ? item.routePath.substring(1, item.routePath.length) : item.routePath
item.path = item.routePath item.path = item.routePath
item.name = item.routePath item.name = item.routePath
item.keepalive = item.routePath item.keepalive = item.routePath
item.component = item.routeName item.component = item.routeName
? item.routeName.indexOf('/src/views/') > -1 ? item.routeName.indexOf('/src/views/') > -1
? item.routeName ? item.routeName
: `/src/views/${item.routeName}/index.vue` : `/src/views/${item.routeName}/index.vue`
: '' : ''
item.type = item.children && item.children.length > 0 ? 'menu_dir' : 'menu' item.type = item.children && item.children.length > 0 ? 'menu_dir' : 'menu'
item.menu_type = item.children && item.children.length > 0 ? null : 'tab' item.menu_type = item.children && item.children.length > 0 ? null : 'tab'
if (item.children) { if (item.children) {
handlerMenu(item.children) handlerMenu(item.children)
} }
}) })
} }
handlerMenu(res.data) handlerMenu(res.data)
handleAdminRoute(res.data) handleAdminRoute(res.data)
if (route.params.to) { if (route.params.to) {
const lastRoute = JSON.parse(route.params.to as string) const lastRoute = JSON.parse(route.params.to as string)
if (lastRoute.path != adminBaseRoutePath) { if (lastRoute.path != adminBaseRoutePath) {
let query = !isEmpty(lastRoute.query) ? lastRoute.query : {} let query = !isEmpty(lastRoute.query) ? lastRoute.query : {}
routePush({ path: lastRoute.path, query: query }) routePush({ path: lastRoute.path, query: query })
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)
} }
})
const onAdaptiveLayout = () => { }
let defaultBeforeResizeLayout = {
layoutMode: config.layout.layoutMode, const onAdaptiveLayout = () => {
menuCollapse: config.layout.menuCollapse let defaultBeforeResizeLayout = {
} layoutMode: config.layout.layoutMode,
menuCollapse: config.layout.menuCollapse
const clientWidth = document.body.clientWidth }
if (clientWidth < 1024) {
/** const clientWidth = document.body.clientWidth
* 锁定窗口改变自动调整 menuCollapse if (clientWidth < 1024) {
* 避免已是小窗且打开了菜单栏时,意外的自动关闭菜单栏 /**
*/ * 锁定窗口改变自动调整 menuCollapse
if (!state.autoMenuCollapseLock) { * 避免已是小窗且打开了菜单栏时,意外的自动关闭菜单栏
state.autoMenuCollapseLock = true */
config.setLayout('menuCollapse', true) if (!state.autoMenuCollapseLock) {
} state.autoMenuCollapseLock = true
config.setLayout('shrink', true) config.setLayout('menuCollapse', true)
config.setLayoutMode('Classic') }
} else { config.setLayout('shrink', true)
state.autoMenuCollapseLock = false config.setLayoutMode('Classic')
config.setLayout('menuCollapse', defaultBeforeResizeLayout.menuCollapse) } else {
config.setLayout('shrink', false) state.autoMenuCollapseLock = false
config.setLayoutMode(defaultBeforeResizeLayout.layoutMode) config.setLayout('menuCollapse', defaultBeforeResizeLayout.menuCollapse)
} config.setLayout('shrink', false)
} config.setLayoutMode(defaultBeforeResizeLayout.layoutMode)
</script> }
}
</script>

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

@@ -1,59 +1,98 @@
<template> <template>
<div> <div>
<div <div
v-loading="true" v-loading="true"
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>
</div> </div>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { onUnmounted, reactive } from 'vue' import { onMounted, onUnmounted, reactive, watch } from 'vue'
import router from '@/router/index' import { useRoute } from 'vue-router'
import { useNavTabs } from '@/stores/navTabs' import router from '@/router/index'
import { isAdminApp } from '@/utils/common' import { useNavTabs } from '@/stores/navTabs'
import { getFirstRoute, routePush } from '@/utils/router' import { isAdminApp } from '@/utils/common'
let timer: number import { getFirstRoute, routePush } from '@/utils/router'
import { isEmpty } from 'lodash-es'
const navTabs = useNavTabs() import { adminBaseRoutePath } from '@/router/static'
const state = reactive({
maximumWait: 1000 * 6, let timer: number
showReload: false,
}) const route = useRoute()
const navTabs = useNavTabs()
const refresh = () => { const state = reactive({
router.go(0) maximumWait: 1000 * 6,
} showReload: false
// if (isAdminApp() && navTabs.state.tabsViewRoutes.length > 0) { })
// let firstRoute = getFirstRoute(navTabs.state.tabsViewRoutes)
// if (firstRoute) routePush(firstRoute.path) const tryRedirect = () => {
// } if (route.params.to) {
try {
timer = window.setTimeout(() => { const lastRoute = JSON.parse(route.params.to as string)
state.showReload = true if (lastRoute.path && lastRoute.path !== adminBaseRoutePath) {
}, state.maximumWait) const query = !isEmpty(lastRoute.query) ? lastRoute.query : {}
routePush({ path: lastRoute.path, query })
onUnmounted(() => { return true
clearTimeout(timer) }
}) } catch (error) {
</script> console.error(error)
}
<style scoped lang="scss"> }
.ba-main-loading { if (isAdminApp() && navTabs.state.tabsViewRoutes.length > 0) {
height: 300px; const firstRoute = getFirstRoute(navTabs.state.tabsViewRoutes)
display: flex; if (firstRoute) {
align-items: center; routePush(firstRoute.path)
justify-content: center; return true
} }
.loading-footer { }
display: flex; return false
align-items: center; }
justify-content: center;
} const refresh = () => {
</style> 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(() => {
state.showReload = true
}, state.maximumWait)
onUnmounted(() => {
clearTimeout(timer)
})
</script>
<style scoped lang="scss">
.ba-main-loading {
height: 300px;
display: flex;
align-items: center;
justify-content: center;
}
.loading-footer {
display: flex;
align-items: center;
justify-content: center;
}
</style>

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