mirror of
https://gitee.com/dromara/mayfly-go
synced 2026-04-19 10:45:19 +08:00
Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d6280ea280 | ||
|
|
666b191b6c | ||
|
|
778cb7f4de | ||
|
|
142bbd265d | ||
|
|
f676ec9e7b | ||
|
|
44d379a016 | ||
|
|
2170509d92 | ||
|
|
798ab7d18b | ||
|
|
abd2b4bac0 |
@@ -15,11 +15,14 @@
|
||||
<img src="https://img.shields.io/github/stars/dromara/mayfly-go.svg?style=social" alt="github star"/>
|
||||
<img src="https://img.shields.io/github/forks/dromara/mayfly-go.svg?style=social" alt="github fork"/>
|
||||
</a>
|
||||
<a href="https://github.com/dromara/mayfly-go" target="_blank">
|
||||
<img src="https://gitcode.com/dromara/mayfly-go/star/badge.svg" alt="github star"/>
|
||||
</a>
|
||||
<a href="https://hub.docker.com/r/mayflygo/mayfly-go/tags" target="_blank">
|
||||
<img src="https://img.shields.io/docker/pulls/mayflygo/mayfly-go.svg?label=docker%20pulls&color=fac858" alt="docker pulls"/>
|
||||
</a>
|
||||
<a href="https://github.com/golang/go" target="_blank">
|
||||
<img src="https://img.shields.io/badge/Golang-1.22%2B-yellow.svg" alt="golang"/>
|
||||
<img src="https://img.shields.io/badge/Golang-1.24%2B-yellow.svg" alt="golang"/>
|
||||
</a>
|
||||
<a href="https://cn.vuejs.org" target="_blank">
|
||||
<img src="https://img.shields.io/badge/Vue-3.x-green.svg" alt="vue">
|
||||
@@ -28,7 +31,7 @@
|
||||
|
||||
## 前言
|
||||
|
||||
Web版 **统一管理操作平台**,集成了对Linux系统的全面操作支持(包括终端管理[终端回放、命令过滤]、文件管理、脚本执行、进程监控及计划任务设置),同时提供了多种数据库(如 MySQL、PostgreSQL、Oracle、SQL Server、达梦、高斯、SQLite 等)的数据操作、数据同步与数据迁移功能。此外,还支持 Redis(单机、哨兵、集群模式)以及 MongoDB 的操作管理,并结合工单流程审批功能,为企业提供一站式的运维与管理解决方案。
|
||||
Web 版 **统一管理操作平台**,集成了对 Linux 系统的全面操作支持(包括终端管理[终端回放、命令过滤]、文件管理、脚本执行、进程监控及计划任务设置),同时提供了多种数据库(如 MySQL、PostgreSQL、Oracle、SQL Server、达梦、高斯、SQLite 等)的数据操作、数据同步与数据迁移功能。此外,还支持 Redis(单机、哨兵、集群模式)、 MongoDB 、Es 的操作管理,并结合工单流程审批功能,为企业提供一站式的运维与管理解决方案。
|
||||
|
||||
## 开发语言与主要框架
|
||||
|
||||
@@ -106,7 +109,7 @@ http://go.mayfly.run
|
||||
|
||||
## 💌 支持作者
|
||||
|
||||
如果觉得项目不错,或者已经在使用了,希望你可以去 <a target="_blank" href="https://github.com/dromara/mayfly-go">Github</a> 或者 <a target="_blank" href="https://gitee.com/dromara/mayfly-go">Gitee</a> 帮我点个 ⭐ Star,这将是对我极大的鼓励与支持。
|
||||
如果觉得项目不错,或者已经在使用了,希望你可以去 <a target="_blank" href="https://github.com/dromara/mayfly-go">Github</a> 或 <a target="_blank" href="https://gitee.com/dromara/mayfly-go">Gitee</a> 或 <a target="_blank" href="https://gitcode.com/dromara/mayfly-go">Gitcode</a> 帮我点个 ⭐ Star,这将是对我极大的鼓励与支持。
|
||||
|
||||
> 喝杯咖啡 ☕️ 或者来杯奶茶 🧋,让作者更有精神,写出更棒的代码!
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
|
||||
## Preface
|
||||
|
||||
Web-based **Unified Management and Operation Platform**, integrating comprehensive operation support for Linux systems (including terminal management [terminal playback, command filtering], file management, script execution, process monitoring, and cronjob settings). It also provides data operation, data synchronization, and data migration for multiple databases (such as MySQL, PostgreSQL, Oracle, SQL Server, Dameng, Gauss, SQLite, etc.). Additionally, it supports Redis operations (standalone, sentinel, and cluster modes) and MongoDB management, combined with work order process approval functionality to offer enterprises an all-in-one solution for operations and management.
|
||||
Web-based **Unified Management and Operation Platform**, integrating comprehensive operation support for Linux systems (including terminal management [terminal playback, command filtering], file management, script execution, process monitoring, and cronjob settings). It also provides data operation, data synchronization, and data migration for multiple databases (such as MySQL, PostgreSQL, Oracle, SQL Server, Dameng, Gauss, SQLite, etc.). Additionally, it supports Redis operations (standalone, sentinel, and cluster modes) and MongoDB、Es management, combined with work order process approval functionality to offer enterprises an all-in-one solution for operations and management.
|
||||
|
||||
## Development languages and major frameworks
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ module.exports = {
|
||||
parser: '@typescript-eslint/parser',
|
||||
sourceType: 'module',
|
||||
},
|
||||
extends: ['plugin:vue/vue3-essential', 'plugin:vue/essential', 'eslint:recommended'],
|
||||
extends: ['plugin:vue/essential', 'eslint:recommended'],
|
||||
plugins: ['vue', '@typescript-eslint'],
|
||||
overrides: [
|
||||
{
|
||||
@@ -35,9 +35,8 @@ module.exports = {
|
||||
'@typescript-eslint/ban-types': 'off',
|
||||
'@typescript-eslint/no-non-null-assertion': 'off',
|
||||
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
||||
'@typescript-eslint/no-redeclare': 'error',
|
||||
'@typescript-eslint/no-non-null-asserted-optional-chain': 'off',
|
||||
'@typescript-eslint/no-unused-vars': [2],
|
||||
'@typescript-eslint/no-unused-vars': 'off',
|
||||
'vue/custom-event-name-casing': 'off',
|
||||
'vue/attributes-order': 'off',
|
||||
'vue/one-component-per-file': 'off',
|
||||
@@ -53,6 +52,7 @@ module.exports = {
|
||||
'vue/no-arrow-functions-in-watch': 'off',
|
||||
'vue/no-template-key': 'off',
|
||||
'vue/no-v-html': 'off',
|
||||
'vue/no-unused-vars': 'off',
|
||||
'vue/comment-directive': 'off',
|
||||
'vue/no-parsing-error': 'off',
|
||||
'vue/no-deprecated-v-on-native-modifier': 'off',
|
||||
@@ -67,7 +67,7 @@ module.exports = {
|
||||
'generator-star-spacing': 'off',
|
||||
'no-unreachable': 'off',
|
||||
'no-multiple-template-root': 'off',
|
||||
'no-unused-vars': 'error',
|
||||
'no-unused-vars': 'off',
|
||||
'no-v-model-argument': 'off',
|
||||
'no-case-declarations': 'off',
|
||||
// 'no-console': 'error',
|
||||
|
||||
@@ -11,7 +11,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@element-plus/icons-vue": "^2.3.1",
|
||||
"@vueuse/core": "^13.1.0",
|
||||
"@logicflow/core": "^2.0.13",
|
||||
"@logicflow/extension": "^2.0.18",
|
||||
"@vueuse/core": "^13.2.0",
|
||||
"@xterm/addon-fit": "^0.10.0",
|
||||
"@xterm/addon-search": "^0.15.0",
|
||||
"@xterm/addon-web-links": "^0.11.0",
|
||||
@@ -22,44 +24,48 @@
|
||||
"crypto-js": "^4.2.0",
|
||||
"dayjs": "^1.11.13",
|
||||
"echarts": "^5.6.0",
|
||||
"element-plus": "^2.9.7",
|
||||
"element-plus": "^2.9.11",
|
||||
"js-base64": "^3.7.7",
|
||||
"jsencrypt": "^3.3.2",
|
||||
"mitt": "^3.0.1",
|
||||
"monaco-editor": "^0.52.2",
|
||||
"monaco-sql-languages": "^0.12.2",
|
||||
"monaco-themes": "^0.4.4",
|
||||
"monaco-sql-languages": "^0.14.0",
|
||||
"monaco-themes": "^0.4.5",
|
||||
"nprogress": "^0.2.0",
|
||||
"pinia": "^3.0.2",
|
||||
"qrcode.vue": "^3.6.0",
|
||||
"screenfull": "^6.0.2",
|
||||
"sortablejs": "^1.15.6",
|
||||
"splitpanes": "^4.0.3",
|
||||
"sql-formatter": "^15.4.10",
|
||||
"sql-formatter": "^15.6.1",
|
||||
"trzsz": "^1.1.5",
|
||||
"uuid": "^9.0.1",
|
||||
"vue": "^3.5.13",
|
||||
"vue": "^3.5.14",
|
||||
"vue-i18n": "^11.1.3",
|
||||
"vue-router": "^4.5.0",
|
||||
"vue-router": "^4.5.1",
|
||||
"vuedraggable": "^4.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/vite": "^4.1.6",
|
||||
"@types/crypto-js": "^4.2.2",
|
||||
"@types/node": "^18.14.0",
|
||||
"@types/nprogress": "^0.2.0",
|
||||
"@types/sortablejs": "^1.15.8",
|
||||
"@typescript-eslint/eslint-plugin": "^6.7.4",
|
||||
"@typescript-eslint/parser": "^6.7.4",
|
||||
"@vitejs/plugin-vue": "^5.2.3",
|
||||
"@vue/compiler-sfc": "^3.5.13",
|
||||
"code-inspector-plugin": "^0.4.5",
|
||||
"@vitejs/plugin-vue": "^5.2.4",
|
||||
"@vue/compiler-sfc": "^3.5.14",
|
||||
"autoprefixer": "^10.4.21",
|
||||
"code-inspector-plugin": "^0.20.9",
|
||||
"dotenv": "^16.3.1",
|
||||
"eslint": "^8.35.0",
|
||||
"eslint": "^9.25.1",
|
||||
"eslint-plugin-vue": "^10.0.0",
|
||||
"prettier": "^3.2.5",
|
||||
"sass": "^1.86.3",
|
||||
"postcss": "^8.5.3",
|
||||
"prettier": "^3.5.3",
|
||||
"sass": "^1.89.0",
|
||||
"tailwindcss": "^4.1.7",
|
||||
"typescript": "^5.8.2",
|
||||
"vite": "^6.2.6",
|
||||
"vite": "^6.3.5",
|
||||
"vite-plugin-progress": "0.0.7",
|
||||
"vue-eslint-parser": "^10.1.3"
|
||||
},
|
||||
|
||||
6
frontend/postcss.config.js
Normal file
6
frontend/postcss.config.js
Normal file
@@ -0,0 +1,6 @@
|
||||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
};
|
||||
@@ -1,13 +1,13 @@
|
||||
<template>
|
||||
<el-config-provider :size="getGlobalComponentSize" :locale="getGlobalI18n">
|
||||
<div class="h100">
|
||||
<div class="h-full">
|
||||
<el-watermark
|
||||
:zIndex="10000000"
|
||||
:width="210"
|
||||
v-if="themeConfig.isWatermark"
|
||||
:font="{ color: 'rgba(180, 180, 180, 0.3)' }"
|
||||
:content="themeConfig.watermarkText"
|
||||
class="h100"
|
||||
class="!h-full"
|
||||
>
|
||||
<router-view v-show="themeConfig.lockScreenTime !== 0" />
|
||||
</el-watermark>
|
||||
|
||||
1
frontend/src/assets/icon/es/es-color.svg
Normal file
1
frontend/src/assets/icon/es/es-color.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024"><path d="M96.426667 649.173333H712.96a137.173333 137.173333 0 0 0 0-274.346666H96.426667c-12.8 43.52-19.626667 89.514667-19.626667 137.173333s6.826667 93.696 19.626667 137.173333z" fill="#07A5DE" p-id="6101"></path><path d="M563.2 25.6A486.4 486.4 0 0 0 125.354667 299.946667H837.546667c52.096 0 97.450667-29.013333 120.661333-71.808A485.76 485.76 0 0 0 563.2 25.6z" fill="#EFBF19" p-id="6102"></path><path d="M942.421333 816.64a137.258667 137.258667 0 0 0-129.749333-92.586667H125.312A486.4 486.4 0 0 0 563.2 998.4c153.344 0 290.090667-70.954667 379.221333-181.76z" fill="#3EBEB1" p-id="6103"></path><path d="M506.197333 649.173333c12.8-43.52 19.626667-89.514667 19.626667-137.173333s-6.826667-93.696-19.626667-137.173333H96.469333c-12.8 43.52-19.626667 89.514667-19.626666 137.173333s6.826667 93.696 19.626666 137.173333h409.728z" fill="#231F20" p-id="6104"></path><path d="M477.269333 724.053333H125.354667a488.533333 488.533333 0 0 0 175.957333 197.888 488.533333 488.533333 0 0 0 175.957333-197.930666z" fill="#019B8F" p-id="6105"></path><path d="M301.312 102.058667a488.533333 488.533333 0 0 1 175.957333 197.930666H125.354667a488.533333 488.533333 0 0 1 175.957333-197.930666z" fill="#D8A22A" p-id="6106"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
1
frontend/src/assets/icon/es/es.svg
Normal file
1
frontend/src/assets/icon/es/es.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024"><path d="M465.664 679.168c105.301333 0.597333 172.970667 1.066667 202.922667 1.450667 20.48 0.256 36.181333 0.426667 47.274666 0.469333h3.84c45.824 0 84.096 8.533333 114.901334 25.258667 31.189333 16.938667 54.826667 42.368 70.826666 76.245333l1.152 2.517333a24.106667 24.106667 0 0 1-1.621333 3.413334c-46.336 67.84-101.034667 116.565333-164.096 146.346666-63.146667 29.824-134.613333 40.704-214.485333 32.469334-159.232-16.384-283.477333-106.24-372.352-269.994667a5.973333 5.973333 0 0 1 3.584-8.618667c13.653333-3.968 27.733333-6.528 41.941333-7.594666 91.306667-1.365333 170.538667-1.877333 238.165333-1.962667h27.946667z m44.885333 63.829333l-0.64 1.152c-3.754667 6.485333-9.386667 15.36-16.128 25.6l-2.645333 3.925334-1.578667 2.346666c-21.205333 31.445333-51.072 72.234667-70.784 94.464 64.853333 34.304 133.162667 45.44 227.157334 27.52 95.146667-18.090667 145.450667-52.565333 175.829333-114.090666-5.034667-10.581333-14.592-19.285333-31.488-27.733334-12.8-6.4-32.426667-11.050667-58.752-14.250666l-221.013333 1.066666z m-257.578666-5.546666l1.237333 1.536c21.504 26.112 67.712 72.277333 96.896 95.786666 15.146667-14.08 29.098667-29.397333 41.642667-45.824 13.952-18.261333 24.149333-32.64 35.370666-52.821333l-175.146666 1.322667z m471.296-360.874667c38.229333 5.077333 67.626667 18.944 88.448 41.301333 20.736 22.229333 33.024 52.992 36.565333 92.373334 3.626667 39.722667-5.76 71.808-27.733333 96.426666-20.906667 23.381333-53.461333 40.106667-97.877334 49.706667l-2.645333 0.597333-2.816 0.554667H144.725333a8.021333 8.021333 0 0 1-7.893333-6.485333 1545.173333 1545.173333 0 0 1-0.298667-1.578667c-12.373333-62.378667-18.517333-106.666667-18.517333-132.906667 0-38.570667 5.888-81.962667 17.706667-130.261333l1.066666-4.394667a7.082667 7.082667 0 0 1 6.826667-5.333333h580.650667zM197.546667 442.88l-0.853334 2.688c-7.509333 24.064-12.544 44.330667-12.117333 70.954667 0 30.293333 5.418667 54.272 13.653333 81.664h283.050667l0.341333-2.218667 0.469334-3.2c3.541333-24.448 4.010667-47.701333 4.010666-76.544 0-30.805333-1.066667-51.541333-6.4-75.264l-282.154666 1.92z m493.397333-3.029333l-131.797333 1.024 0.512 2.474666c4.48 22.357333 6.741333 43.861333 6.741333 73.216 0 30.421333-2.432 53.76-7.552 79.189334l134.826667-0.170667 1.962666-0.213333c28.16-2.901333 49.194667-7.210667 62.421334-23.04 11.52-13.866667 17.152-32.469333 17.152-55.765334 0-24.746667-6.272-42.624-19.456-54.826666-13.653333-12.714667-34.474667-19.2-61.994667-21.674667l-2.816-0.213333z m49.877333-342.784c63.104 29.824 117.845333 78.592 164.181334 146.346666l1.536 2.304a23.466667 23.466667 0 0 1-1.066667 3.669334c-16 33.92-39.594667 59.306667-70.784 76.245333-30.805333 16.768-69.12 25.258667-114.986667 25.258667-11.178667 0-28.16 0.213333-50.944 0.469333-45.098667 0.597333-112.597333 1.408-202.965333 1.493333h-14.122667c-70.613333 0-154.453333-0.512-251.733333-1.962666a207.061333 207.061333 0 0 1-42.24-7.594667 5.973333 5.973333 0 0 1-3.626667-8.618667C242.986667 170.922667 367.146667 81.066667 526.378667 64.682667c79.829333-8.277333 151.296 2.56 214.4 32.426666z m-102.101333 28.501333c-85.205333-15.36-143.957333-4.010667-213.717333 27.221333 11.648 13.312 26.410667 33.621333 40.874666 55.04l1.578667 2.389334 2.56 3.754666 3.498667 5.376 2.346666 3.584 1.237334 1.877334c18.688 28.757333 35.157333 56.746667 41.728 70.613333h213.674666l2.474667-0.298667 2.56-0.341333c21.290667-2.986667 38.144-10.794667 55.978667-19.754667 17.408-8.576 30.122667-18.304 40.106666-29.866666-49.493333-63.018667-108.586667-104.106667-194.901333-119.594667zM367.744 186.453333c-12.458667 10.069333-34.304 29.44-56.192 50.048l-1.706667 1.621334-3.328 3.157333-3.498666 3.328-1.877334 1.792-2.048 2.005333c-17.322667 16.64-33.578667 33.109333-44.501333 45.909334l179.797333-1.536-1.109333-1.877334a3067.264 3067.264 0 0 1-12.672-21.418666l-11.776-20.053334-2.474667-4.053333-2.56-4.266667-1.152-2.005333-1.237333-2.005333c-12.458667-20.693333-24.917333-40.405333-33.706667-50.645334z" fill="#2c2c2c" p-id="5739"></path></svg>
|
||||
|
After Width: | Height: | Size: 4.0 KiB |
@@ -1,5 +1,5 @@
|
||||
import request from './request';
|
||||
import { useApiFetch } from '@/hooks/useRequest';
|
||||
import { RequestOptions, useApiFetch } from '@/hooks/useRequest';
|
||||
|
||||
/**
|
||||
* 可用于各模块定义各自api请求
|
||||
@@ -49,7 +49,7 @@ class Api {
|
||||
* @param reqOptions 其他可选值
|
||||
* @returns
|
||||
*/
|
||||
useApi<T>(params: any = null, reqOptions: RequestInit = {}) {
|
||||
useApi<T>(params: any = null, reqOptions?: RequestOptions) {
|
||||
return useApiFetch<T>(this, params, reqOptions);
|
||||
}
|
||||
|
||||
@@ -59,8 +59,8 @@ class Api {
|
||||
*/
|
||||
async request(param: any = null, options: any = {}): Promise<any> {
|
||||
const { execute, data } = this.useApi(param, options);
|
||||
await execute();
|
||||
return data.value;
|
||||
const res = await execute();
|
||||
return data.value || res;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import { i18n } from '@/i18n';
|
||||
import { ElMessage } from 'element-plus';
|
||||
|
||||
/**
|
||||
* 不符合业务断言错误
|
||||
*/
|
||||
class AssertError extends Error {
|
||||
constructor(message: string) {
|
||||
ElMessage.error(message);
|
||||
super(message);
|
||||
// 错误类名
|
||||
this.name = 'AssertError';
|
||||
@@ -15,11 +17,11 @@ class AssertError extends Error {
|
||||
* 断言表达式为true
|
||||
*
|
||||
* @param condition 条件表达式
|
||||
* @param msg 错误消息
|
||||
* @param msgOrI18nKey 错误消息 或者 i18n key
|
||||
*/
|
||||
export function isTrue(condition: boolean, msg: string) {
|
||||
export function isTrue(condition: boolean, msgOrI18nKey: string) {
|
||||
if (!condition) {
|
||||
throw new AssertError(msg);
|
||||
throw new AssertError(i18n.global.t(msgOrI18nKey));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,8 @@ export const ResourceTypeEnum = {
|
||||
Db: EnumValue.of(2, '数据库实例').setExtra({ icon: 'Coin', iconColor: 'var(--el-color-warning)' }).tagTypeWarning(),
|
||||
Redis: EnumValue.of(3, 'redis').setExtra({ icon: 'icon redis/redis', iconColor: 'var(--el-color-danger)' }).tagTypeInfo(),
|
||||
Mongo: EnumValue.of(4, 'mongo').setExtra({ icon: 'icon mongo/mongo', iconColor: 'var(--el-color-success)' }).tagTypeDanger(),
|
||||
AuthCert: EnumValue.of(5, '授权凭证').setExtra({ icon: 'Ticket', iconColor: 'var(--el-color-success)' }),
|
||||
Es: EnumValue.of(6, 'ES实例').setExtra({ icon: 'icon es/es-color', iconColor: 'var(--el-color-warning)' }).tagTypeWarning(),
|
||||
};
|
||||
|
||||
// 标签关联的资源类型
|
||||
@@ -24,9 +26,10 @@ export const TagResourceTypeEnum = {
|
||||
|
||||
Machine: ResourceTypeEnum.Machine,
|
||||
DbInstance: ResourceTypeEnum.Db,
|
||||
EsInstance: ResourceTypeEnum.Es,
|
||||
Redis: ResourceTypeEnum.Redis,
|
||||
Mongo: ResourceTypeEnum.Mongo,
|
||||
AuthCert: EnumValue.of(5, '授权凭证').setExtra({ icon: 'Ticket', iconColor: 'var(--el-color-success)' }),
|
||||
AuthCert: ResourceTypeEnum.AuthCert,
|
||||
|
||||
Db: EnumValue.of(22, '数据库').setExtra({ icon: 'Coin' }),
|
||||
};
|
||||
@@ -37,4 +40,5 @@ export const TagResourceTypePath = {
|
||||
|
||||
DbInstanceAuthCert: `${TagResourceTypeEnum.DbInstance.value}/${TagResourceTypeEnum.AuthCert.value}`,
|
||||
Db: `${TagResourceTypeEnum.DbInstance.value}/${TagResourceTypeEnum.AuthCert.value}/${TagResourceTypeEnum.Db.value}`,
|
||||
Es: `${TagResourceTypeEnum.EsInstance.value}/${TagResourceTypeEnum.AuthCert.value}`,
|
||||
};
|
||||
|
||||
@@ -15,7 +15,7 @@ const config = {
|
||||
baseWsUrl: `${(window as any).globalConfig.BaseWsUrl || `${location.protocol == 'https:' ? 'wss:' : 'ws:'}//${getBaseApiUrl()}`}/api`,
|
||||
|
||||
// 系统版本
|
||||
version: 'v1.9.4',
|
||||
version: 'v1.10.0',
|
||||
};
|
||||
|
||||
export default config;
|
||||
|
||||
@@ -30,6 +30,18 @@ export function formatByteSize(size: number, fixed = 2) {
|
||||
return parseFloat((size / Math.pow(base, exponent)).toFixed(fixed)) + units[exponent];
|
||||
}
|
||||
|
||||
export function formatDocSize(size: number, fixed = 2) {
|
||||
if (size === 0) {
|
||||
return '0';
|
||||
}
|
||||
|
||||
const units = ['', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'];
|
||||
const base = 1000;
|
||||
const exponent = Math.floor(Math.log(size) / Math.log(base));
|
||||
|
||||
return parseFloat((size / Math.pow(base, exponent)).toFixed(fixed)) + units[exponent];
|
||||
}
|
||||
|
||||
/**
|
||||
* 容量转为对应的字节大小,如 1KB转为 1024
|
||||
* @param sizeString 1kb 1gb等
|
||||
@@ -86,8 +98,8 @@ export function formatTime(time: number, unit: string = 's') {
|
||||
let result = '';
|
||||
|
||||
const timeUnits = Object.entries(units).map(([unit, duration]) => {
|
||||
const value = Math.floor(seconds / duration);
|
||||
seconds %= duration;
|
||||
const value = Math.floor(seconds / (duration as any));
|
||||
seconds %= duration as any;
|
||||
return { value, unit };
|
||||
});
|
||||
|
||||
|
||||
@@ -107,7 +107,6 @@ const handleItemKeyupEnter = (item: SearchItem) => {
|
||||
margin-bottom: 10px;
|
||||
|
||||
box-sizing: border-box;
|
||||
overflow-x: hidden;
|
||||
background-color: var(--el-bg-color);
|
||||
border: 1px solid var(--el-border-color-light);
|
||||
border-radius: 6px;
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
:style="`top: ${state.dropdown.y + 5}px;left: ${state.dropdown.x}px;`"
|
||||
:key="Math.random()"
|
||||
v-show="state.isShow && !allHide"
|
||||
@contextmenu="headerContextmenuClick"
|
||||
>
|
||||
<ul class="el-dropdown-menu">
|
||||
<template v-for="(v, k) in state.dropdownList">
|
||||
@@ -125,6 +126,10 @@ const onCurrentContextmenuClick = (ci: ContextmenuItem) => {
|
||||
emit('currentContextmenuClick', { id: ci.clickId, item: state.item });
|
||||
};
|
||||
|
||||
const headerContextmenuClick = (event: any, data: any) => {
|
||||
event.preventDefault(); // 阻止默认的右击菜单行为
|
||||
};
|
||||
|
||||
// 打开右键菜单:判断是否固定,固定则不显示关闭按钮
|
||||
const openContextmenu = (item: any) => {
|
||||
state.item = item;
|
||||
|
||||
@@ -37,9 +37,9 @@
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<div class="flex-align-center w100">
|
||||
<el-radio v-model="radioValue" :label="7" class="mr5"> {{ $t('components.crontab.appoint') }} </el-radio>
|
||||
<el-select @click="radioValue = 7" class="w100" clearable v-model="checkboxList" multiple>
|
||||
<div class="flex items-center w-full">
|
||||
<el-radio v-model="radioValue" :label="7" class="mr-1"> {{ $t('components.crontab.appoint') }} </el-radio>
|
||||
<el-select @click="radioValue = 7" class="!w-full" clearable v-model="checkboxList" multiple>
|
||||
<el-option v-for="item in 31" :key="item" :value="`${item}`">{{ item }}</el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
|
||||
@@ -22,9 +22,9 @@
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<div class="flex-align-center w100">
|
||||
<el-radio v-model="radioValue" :label="4" class="mr5"> {{ $t('components.crontab.appoint') }} </el-radio>
|
||||
<el-select @click="radioValue = 4" class="w100" clearable v-model="checkboxList" multiple>
|
||||
<div class="flex items-center w-full">
|
||||
<el-radio v-model="radioValue" :label="4" class="mr-1"> {{ $t('components.crontab.appoint') }} </el-radio>
|
||||
<el-select @click="radioValue = 4" class="!w-full" clearable v-model="checkboxList" multiple>
|
||||
<el-option v-for="item in 60" :key="item" :value="`${item - 1}`">{{ item - 1 }}</el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
|
||||
@@ -22,9 +22,9 @@
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<div class="flex-align-center w100">
|
||||
<el-radio v-model="radioValue" :label="4" class="mr5"> {{ $t('components.crontab.appoint') }} </el-radio>
|
||||
<el-select @click="radioValue = 4" class="w100" clearable v-model="checkboxList" multiple>
|
||||
<div class="flex items-center w-full">
|
||||
<el-radio v-model="radioValue" :label="4" class="mr-1"> {{ $t('components.crontab.appoint') }} </el-radio>
|
||||
<el-select @click="radioValue = 4" class="!w-full" clearable v-model="checkboxList" multiple>
|
||||
<el-option v-for="item in 60" :key="item" :value="`${item - 1}`">{{ item - 1 }}</el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
|
||||
@@ -22,9 +22,9 @@
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<div class="flex-align-center w100">
|
||||
<el-radio v-model="radioValue" :label="4" class="mr5"> {{ $t('components.crontab.appoint') }} </el-radio>
|
||||
<el-select @click="radioValue = 4" class="w100" clearable v-model="checkboxList" multiple>
|
||||
<div class="flex items-center w-full">
|
||||
<el-radio v-model="radioValue" :label="4" class="mr-1"> {{ $t('components.crontab.appoint') }} </el-radio>
|
||||
<el-select @click="radioValue = 4" class="!w-full" clearable v-model="checkboxList" multiple>
|
||||
<el-option v-for="item in 12" :key="item" :value="`${item}`">{{ item }}</el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
|
||||
@@ -22,9 +22,9 @@
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<div class="flex-align-center w100">
|
||||
<el-radio v-model="radioValue" :label="4" class="mr5"> {{ $t('components.crontab.appoint') }} </el-radio>
|
||||
<el-select @click="radioValue = 4" class="w100" clearable v-model="checkboxList" multiple>
|
||||
<div class="flex items-center w-full">
|
||||
<el-radio v-model="radioValue" :label="4" class="mr-1"> {{ $t('components.crontab.appoint') }} </el-radio>
|
||||
<el-select @click="radioValue = 4" class="!w-full" clearable v-model="checkboxList" multiple>
|
||||
<el-option v-for="item in 60" :key="item" :value="`${item - 1}`">{{ item - 1 }}</el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
|
||||
@@ -32,9 +32,9 @@
|
||||
</el-form-item> -->
|
||||
|
||||
<el-form-item>
|
||||
<div class="flex-align-center w100">
|
||||
<el-radio v-model="radioValue" :label="6" class="mr5"> {{ $t('components.crontab.appoint') }} </el-radio>
|
||||
<el-select @click="radioValue = 6" class="w100" clearable v-model="checkboxList" multiple>
|
||||
<div class="flex items-center w-full">
|
||||
<el-radio v-model="radioValue" :label="6" class="mr-1"> {{ $t('components.crontab.appoint') }} </el-radio>
|
||||
<el-select @click="radioValue = 6" class="!w-full" clearable v-model="checkboxList" multiple>
|
||||
<el-option v-for="(item, index) of weekList" :label="item" :key="index" :value="`${index + 1}`">{{ $t(item) }}</el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
|
||||
@@ -26,9 +26,9 @@
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<div class="flex-align-center w100">
|
||||
<el-radio v-model="radioValue" :label="5" class="mr5"> {{ $t('components.crontab.appoint') }} </el-radio>
|
||||
<el-select @click="radioValue = 5" class="w100" clearable v-model="checkboxList" multiple>
|
||||
<div class="flex items-center w-full">
|
||||
<el-radio v-model="radioValue" :label="5" class="mr-1"> {{ $t('components.crontab.appoint') }} </el-radio>
|
||||
<el-select @click="radioValue = 5" class="!w-full" clearable v-model="checkboxList" multiple>
|
||||
<el-option v-for="item in 9" :key="item" :value="`${item - 1 + fullYear}`" :label="item - 1 + fullYear" />
|
||||
</el-select>
|
||||
</div>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<template>
|
||||
<div class="dynamic-form-edit w100">
|
||||
<el-table :data="formItems" stripe class="w100">
|
||||
<div class="dynamic-form-edit !w-full">
|
||||
<el-table :data="formItems" stripe class="!w-full">
|
||||
<el-table-column prop="name" label="model" min-width="100px">
|
||||
<template #header>
|
||||
<el-button class="ml0" type="primary" circle size="small" icon="Plus" @click="addItem()"> </el-button>
|
||||
<span class="ml10">model field</span>
|
||||
<span class="ml-2">model field</span>
|
||||
</template>
|
||||
<template #default="scope">
|
||||
<el-input v-model="scope.row['model']" :placeholder="$t('components.df.fieldModelPlaceholder')" clearable> </el-input>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="icon-selector w100 h100">
|
||||
<div class="icon-selector !w-full !h-full">
|
||||
<el-input
|
||||
v-model="state.fontIconSearch"
|
||||
:placeholder="state.fontIconPlaceholder"
|
||||
@@ -12,7 +12,7 @@
|
||||
@blur="onIconBlur"
|
||||
>
|
||||
<template #prepend>
|
||||
<SvgIcon :name="state.fontIconPrefix === '' ? prepend : state.fontIconPrefix" class="font14" />
|
||||
<SvgIcon :name="state.fontIconPrefix === '' ? prepend : state.fontIconPrefix" class="!text-[14px]" />
|
||||
</template>
|
||||
</el-input>
|
||||
<el-popover
|
||||
@@ -25,7 +25,7 @@
|
||||
virtual-triggering
|
||||
>
|
||||
<template #default>
|
||||
<div class="ml5 mt5">{{ $t(title) }}</div>
|
||||
<div class="ml-1 mt-1">{{ $t(title) }}</div>
|
||||
<div class="icon-selector-warp">
|
||||
<el-tabs v-model="state.fontIconTabActive" @tab-click="onIconClick">
|
||||
<el-tab-pane lazy label="ele" name="ele">
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
<template>
|
||||
<div class="monaco-editor" style="border: 1px solid var(--el-border-color-light, #ebeef5); height: 100%">
|
||||
<div class="monaco-editor-custom relative h-full">
|
||||
<div class="monaco-editor-content" ref="monacoTextareaRef" :style="{ height: height }"></div>
|
||||
<el-select v-if="canChangeMode" class="code-mode-select" v-model="languageMode" @change="changeLanguage" filterable>
|
||||
<el-option v-for="mode in languageArr" :key="mode.value" :label="mode.label" :value="mode.value"> </el-option>
|
||||
<el-option v-for="mode in languageArr" :key="mode.value" :label="mode.label" :value="mode.value" />
|
||||
</el-select>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { watch, toRefs, reactive, onMounted, onBeforeUnmount, useTemplateRef, Ref } from 'vue';
|
||||
import * as monaco from 'monaco-editor/esm/vs/editor/editor.api';
|
||||
import * as monaco from 'monaco-editor';
|
||||
// 相关语言
|
||||
import 'monaco-editor/esm/vs/basic-languages/shell/shell.contribution.js';
|
||||
import 'monaco-editor/esm/vs/basic-languages/yaml/yaml.contribution.js';
|
||||
@@ -31,7 +31,6 @@ import 'monaco-editor/esm/vs/editor/contrib/format//browser/formatActions.js';
|
||||
// 提示
|
||||
import 'monaco-editor/esm/vs/editor/contrib/suggest/browser/suggestController.js';
|
||||
import 'monaco-editor/esm/vs/editor/contrib/suggest/browser/suggestInlineCompletions.js';
|
||||
|
||||
import { editor, languages } from 'monaco-editor';
|
||||
import EditorWorker from 'monaco-editor/esm/vs/editor/editor.worker.js?worker';
|
||||
import JsonWorker from 'monaco-editor/esm/vs/language/json/json.worker?worker';
|
||||
@@ -134,6 +133,7 @@ const defaultOptions = {
|
||||
theme: 'SolarizedLight',
|
||||
automaticLayout: true, //自适应宽高布局
|
||||
foldingStrategy: 'indentation', //代码可分小段折叠
|
||||
folding: true,
|
||||
roundedSelection: false, // 禁用选择文本背景的圆角
|
||||
matchBrackets: 'near',
|
||||
linkedEditing: true,
|
||||
@@ -149,7 +149,13 @@ const defaultOptions = {
|
||||
minimap: {
|
||||
enabled: false, // 不要小地图
|
||||
},
|
||||
};
|
||||
renderLineHighlight: 'all',
|
||||
selectOnLineNumbers: false,
|
||||
readOnly: false,
|
||||
scrollBeyondLastLine: false,
|
||||
lineNumbers: 'on',
|
||||
lineNumbersMinChars: 3,
|
||||
} as editor.IStandaloneEditorConstructionOptions;
|
||||
|
||||
const monacoTextareaRef: Ref<any> = useTemplateRef('monacoTextareaRef');
|
||||
|
||||
@@ -221,7 +227,8 @@ const initMonacoEditorIns = () => {
|
||||
monaco.editor.defineTheme('SolarizedLight', SolarizedLight);
|
||||
defaultOptions.language = state.languageMode;
|
||||
defaultOptions.theme = themeConfig.value.editorTheme;
|
||||
monacoEditorIns = monaco.editor.create(monacoTextareaRef.value, Object.assign(defaultOptions, props.options as any));
|
||||
let options = Object.assign(defaultOptions, props.options as any);
|
||||
monacoEditorIns = monaco.editor.create(monacoTextareaRef.value, options);
|
||||
|
||||
// 监听内容改变,双向绑定
|
||||
monacoEditorIns.onDidChangeModelContent(() => {
|
||||
@@ -309,7 +316,7 @@ defineExpose({ getEditor, format, focus });
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.monaco-editor {
|
||||
.monaco-editor-custom {
|
||||
.code-mode-select {
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
@@ -317,5 +324,8 @@ defineExpose({ getEditor, format, focus });
|
||||
top: 10px;
|
||||
max-width: 130px;
|
||||
}
|
||||
|
||||
border: 1px solid var(--el-border-color-light, #ebeef5);
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="h-full flex flex-col flex-1 overflow-hidden">
|
||||
<transition name="el-zoom-in-top">
|
||||
<!-- 查询表单 -->
|
||||
<SearchForm v-if="isShowSearch" :items="tableSearchItems" v-model="queryForm" :search="search"
|
||||
:reset="reset" :search-col="searchCol">
|
||||
<SearchForm v-if="isShowSearch" :items="tableSearchItems" v-model="queryForm" :search="search" :reset="reset" :search-col="searchCol">
|
||||
<!-- 遍历父组件传入的 solts 透传给子组件 -->
|
||||
<template v-for="(_, key) in useSlots()" v-slot:[key]>
|
||||
<slot :name="key"></slot>
|
||||
@@ -11,83 +10,104 @@
|
||||
</SearchForm>
|
||||
</transition>
|
||||
|
||||
<div class="card">
|
||||
<div class="table-main">
|
||||
<!-- 表格头部 操作按钮 -->
|
||||
<div class="table-header">
|
||||
<div class="header-button-lf">
|
||||
<slot name="tableHeader" />
|
||||
</div>
|
||||
|
||||
<div v-if="toolButton" class="header-button-ri">
|
||||
<slot name="toolButton">
|
||||
<div class="tool-button">
|
||||
<!-- 简易单个搜索项 -->
|
||||
<div v-if="nowSearchItem" class="simple-search-form">
|
||||
<el-dropdown v-if="searchItems?.length > 1">
|
||||
<SvgIcon :size="16" name="CaretBottom" class="mr4 mt6 simple-search-form-btn" />
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item v-for="searchItem in searchItems"
|
||||
:key="searchItem.prop" @click="changeSimpleFormItem(searchItem)">
|
||||
{{ $t(searchItem.label) }}
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
|
||||
<div class="simple-search-form-label mt5">
|
||||
<el-text truncated tag="b">{{ `${$t(nowSearchItem?.label)} : ` }}</el-text>
|
||||
</div>
|
||||
|
||||
<el-form-item style="width: 200px" :key="nowSearchItem.prop">
|
||||
<SearchFormItem @keyup.enter.native="searchFormItemKeyUpEnter"
|
||||
v-if="!nowSearchItem.slot" :item="nowSearchItem"
|
||||
v-model="queryForm[nowSearchItem.prop]" />
|
||||
|
||||
<slot @keyup.enter.native="searchFormItemKeyUpEnter" v-else
|
||||
:name="nowSearchItem.slot">
|
||||
</slot>
|
||||
</el-form-item>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<el-button v-if="showToolButton('search') && searchItems?.length" icon="Search"
|
||||
circle @click="search" />
|
||||
|
||||
<!-- <el-button v-if="showToolButton('refresh')" icon="Refresh" circle @click="execQuery()" /> -->
|
||||
|
||||
<el-button v-if="showToolButton('search') && searchItems?.length > 1"
|
||||
:icon="isShowSearch ? 'ArrowDown' : 'ArrowUp'" circle
|
||||
@click="isShowSearch = !isShowSearch" />
|
||||
|
||||
<el-popover placement="bottom" title="表格配置"
|
||||
popper-style="max-height: 550px; overflow: auto; max-width: 450px" width="auto"
|
||||
trigger="click">
|
||||
<div v-for="(item, index) in tableColumns" :key="index">
|
||||
<el-checkbox v-model="item.show" :label="item.label" :true-value="true"
|
||||
:false-value="false" />
|
||||
</div>
|
||||
<template #reference>
|
||||
<el-button icon="Operation" circle :size="props.size"></el-button>
|
||||
</template>
|
||||
</el-popover>
|
||||
</div>
|
||||
</div>
|
||||
</slot>
|
||||
</div>
|
||||
<el-card class="h-full" body-class="h-full flex flex-col">
|
||||
<!-- 表格头部 操作按钮 -->
|
||||
<div class="flex justify-between">
|
||||
<div>
|
||||
<slot name="tableHeader" />
|
||||
</div>
|
||||
|
||||
<el-table ref="tableRef" v-bind="$attrs" :max-height="tableMaxHeight"
|
||||
@selection-change="handleSelectionChange" :data="tableData" highlight-current-row
|
||||
v-loading="loading" :size="props.size as any" :border="border">
|
||||
<slot v-if="toolButton" name="toolButton">
|
||||
<div class="flex">
|
||||
<!-- 简易单个搜索项 -->
|
||||
<div v-if="nowSearchItem" class="flex">
|
||||
<el-dropdown v-if="searchItems?.length > 1">
|
||||
<SvgIcon :size="16" name="CaretBottom" class="!mr-1 !mt-1.5 simple-search-form-btn" />
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item v-for="searchItem in searchItems" :key="searchItem.prop" @click="changeSimpleFormItem(searchItem)">
|
||||
{{ $t(searchItem.label) }}
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
|
||||
<div class="text-right mr-1.5 mt-1">
|
||||
<el-text truncated tag="b">{{ `${$t(nowSearchItem?.label)} : ` }}</el-text>
|
||||
</div>
|
||||
|
||||
<el-form-item class="w-[200px]" :key="nowSearchItem.prop">
|
||||
<SearchFormItem
|
||||
@keyup.enter.native="searchFormItemKeyUpEnter"
|
||||
v-if="!nowSearchItem.slot"
|
||||
:item="nowSearchItem"
|
||||
v-model="queryForm[nowSearchItem.prop]"
|
||||
/>
|
||||
|
||||
<slot @keyup.enter.native="searchFormItemKeyUpEnter" v-else :name="nowSearchItem.slot"> </slot>
|
||||
</el-form-item>
|
||||
</div>
|
||||
|
||||
<div class="ml-2">
|
||||
<el-button v-if="showToolButton('search') && searchItems?.length" icon="Search" circle @click="search" />
|
||||
|
||||
<!-- <el-button v-if="showToolButton('refresh')" icon="Refresh" circle @click="execQuery()" /> -->
|
||||
|
||||
<el-button
|
||||
v-if="showToolButton('search') && searchItems?.length > 1"
|
||||
:icon="isShowSearch ? 'ArrowDown' : 'ArrowUp'"
|
||||
circle
|
||||
@click="isShowSearch = !isShowSearch"
|
||||
/>
|
||||
|
||||
<el-popover
|
||||
placement="bottom"
|
||||
title="表格配置"
|
||||
popper-style="max-height: 550px; overflow: auto; max-width: 450px"
|
||||
width="auto"
|
||||
trigger="click"
|
||||
>
|
||||
<div v-for="(item, index) in tableColumns" :key="index">
|
||||
<el-checkbox v-model="item.show" :label="$t(item.label)" :true-value="true" :false-value="false" />
|
||||
</div>
|
||||
<template #reference>
|
||||
<el-button icon="Operation" circle :size="props.size"></el-button>
|
||||
</template>
|
||||
</el-popover>
|
||||
</div>
|
||||
</div>
|
||||
</slot>
|
||||
</div>
|
||||
|
||||
<div class="flex-1 overflow-auto">
|
||||
<el-table
|
||||
v-show="showTable"
|
||||
ref="tableRef"
|
||||
v-bind="$attrs"
|
||||
height="100%"
|
||||
@selection-change="handleSelectionChange"
|
||||
:data="tableData"
|
||||
highlight-current-row
|
||||
v-loading="loading"
|
||||
:size="props.size as any"
|
||||
:border="border"
|
||||
>
|
||||
<el-table-column v-if="props.showSelection" :selectable="selectable" type="selection" width="40" />
|
||||
|
||||
<template v-for="(item, index) in tableColumns">
|
||||
<el-table-column :key="index" v-if="item.show" :prop="item.prop" :label="$t(item.label)"
|
||||
:fixed="item.fixed" :align="item.align" :show-overflow-tooltip="item.showOverflowTooltip"
|
||||
:min-width="item.minWidth" :sortable="item.sortable || false" :type="item.type"
|
||||
:width="item.width">
|
||||
<el-table-column
|
||||
:key="index"
|
||||
v-if="item.show"
|
||||
:prop="item.prop"
|
||||
:label="$t(item.label)"
|
||||
:fixed="item.fixed"
|
||||
:align="item.align"
|
||||
:show-overflow-tooltip="item.showOverflowTooltip"
|
||||
:min-width="item.minWidth"
|
||||
:sortable="item.sortable || false"
|
||||
:type="item.type"
|
||||
:width="item.width"
|
||||
>
|
||||
<!-- 插槽:预留功能 -->
|
||||
<template #default="scope" v-if="item.slot">
|
||||
<slot :name="item.slotName ? item.slotName : item.prop" :data="scope.row"></slot>
|
||||
@@ -95,21 +115,29 @@
|
||||
|
||||
<!-- 枚举类型使用tab展示 -->
|
||||
<template #default="scope" v-else-if="item.type == 'tag'">
|
||||
<enum-tag :size="props.size" :enums="item.typeParam"
|
||||
:value="item.getValueByData(scope.row)"></enum-tag>
|
||||
<enum-tag :size="props.size" :enums="item.typeParam" :value="item.getValueByData(scope.row)"></enum-tag>
|
||||
</template>
|
||||
|
||||
<template #default="scope" v-else>
|
||||
<!-- 配置了美化文本按钮以及文本内容大于指定长度,则显示美化按钮 -->
|
||||
<el-popover v-if="item.isBeautify && item.getValueByData(scope.row)?.length > 35"
|
||||
effect="light" trigger="click" placement="top" width="600px">
|
||||
<el-popover
|
||||
v-if="item.isBeautify && item.getValueByData(scope.row)?.length > 35"
|
||||
effect="light"
|
||||
trigger="click"
|
||||
placement="top"
|
||||
width="600px"
|
||||
>
|
||||
<template #default>
|
||||
<el-input :autosize="{ minRows: 3, maxRows: 15 }" disabled v-model="formatVal"
|
||||
type="textarea" />
|
||||
<el-input :autosize="{ minRows: 3, maxRows: 15 }" disabled v-model="formatVal" type="textarea" />
|
||||
</template>
|
||||
<template #reference>
|
||||
<el-link @click="formatText(item.getValueByData(scope.row))" :underline="false"
|
||||
type="success" icon="MagicStick" class="mr5"></el-link>
|
||||
<el-link
|
||||
@click="formatText(item.getValueByData(scope.row))"
|
||||
underline="never"
|
||||
type="success"
|
||||
icon="MagicStick"
|
||||
class="mr-1"
|
||||
></el-link>
|
||||
</template>
|
||||
</el-popover>
|
||||
|
||||
@@ -120,38 +148,42 @@
|
||||
</el-table>
|
||||
</div>
|
||||
|
||||
<el-row v-if="props.pageable" class="mt20" type="flex" justify="end">
|
||||
<el-pagination :small="props.size == 'small'" @current-change="pageNumChange"
|
||||
@size-change="pageSizeChange" style="text-align: right" layout="prev, pager, next, total, sizes"
|
||||
:total="total" v-model:current-page="queryForm.pageNum" v-model:page-size="queryForm.pageSize"
|
||||
:page-sizes="pageSizes" />
|
||||
<el-row v-if="props.pageable" class="mt-4" type="flex" justify="end">
|
||||
<el-pagination
|
||||
:small="props.size == 'small'"
|
||||
@current-change="pageNumChange"
|
||||
@size-change="pageSizeChange"
|
||||
layout="prev, pager, next, total, sizes"
|
||||
:total="total"
|
||||
v-model:current-page="queryForm.pageNum"
|
||||
v-model:page-size="queryForm.pageSize"
|
||||
:page-sizes="pageSizes"
|
||||
/>
|
||||
</el-row>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { toRefs, watch, reactive, onMounted, Ref, ref, useSlots, toValue } from 'vue';
|
||||
import { toRefs, watch, reactive, onMounted, Ref, ref, useSlots, toValue, h } from 'vue';
|
||||
import { TableColumn } from './index';
|
||||
import EnumTag from '@/components/enumtag/EnumTag.vue';
|
||||
import { useThemeConfig } from '@/store/themeConfig';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useEventListener } from '@vueuse/core';
|
||||
import Api from '@/common/Api';
|
||||
import SearchForm from '@/components/SearchForm/index.vue';
|
||||
import { SearchItem } from '../SearchForm/index';
|
||||
import SearchFormItem from '../SearchForm/components/SearchFormItem.vue';
|
||||
import SvgIcon from '@/components/svgIcon/index.vue';
|
||||
import { usePageTable } from '@/hooks/usePageTable';
|
||||
import { ElTable } from 'element-plus';
|
||||
|
||||
import { ElInput, ElTable } from 'element-plus';
|
||||
|
||||
const emit = defineEmits(['update:selectionData', 'pageSizeChange', 'pageNumChange']);
|
||||
|
||||
export interface PageTableProps {
|
||||
size?: string;
|
||||
pageApi?: Api; // 请求表格数据的 api
|
||||
columns: TableColumn[]; // 列配置项 ==> 必传
|
||||
columns: TableColumn[] | any[]; // 列配置项 ==> 必传
|
||||
showSelection?: boolean;
|
||||
selectable?: (row: any) => boolean; // 是否可选
|
||||
pageable?: boolean;
|
||||
@@ -208,6 +240,10 @@ const showToolButton = (key: 'setting' | 'search') => {
|
||||
|
||||
const nowSearchItem: Ref<SearchItem> = ref(null) as any;
|
||||
|
||||
// 是否已经计算列宽度
|
||||
const isCalculatedWidth: Ref<boolean> = ref(false);
|
||||
const showTable: Ref<boolean> = ref(false);
|
||||
|
||||
/**
|
||||
* 改变当前的搜索项
|
||||
* @param searchItem 当前点击的搜索项
|
||||
@@ -239,24 +275,35 @@ const state = reactive({
|
||||
pageSizes: [] as any, // 可选每页显示的数据量
|
||||
// 输入框宽度
|
||||
formatVal: '', // 格式化后的值
|
||||
tableMaxHeight: '500px',
|
||||
});
|
||||
|
||||
const { pageSizes, formatVal, tableMaxHeight } = toRefs(state);
|
||||
const { pageSizes, formatVal } = toRefs(state);
|
||||
|
||||
watch(tableData, (newValue: any) => {
|
||||
if (newValue && newValue.length > 0) {
|
||||
props.columns.forEach((item) => {
|
||||
if (item.autoWidth && item.show) {
|
||||
item.autoCalculateMinWidth(tableData.value);
|
||||
}
|
||||
});
|
||||
calculateTableColumnMinWidth();
|
||||
// 需要计算完才能显示表格,否则会有表格闪烁的问题
|
||||
if (!showTable.value) {
|
||||
showTable.value = true;
|
||||
}
|
||||
});
|
||||
|
||||
watch(isShowSearch, () => {
|
||||
calcuTableHeight();
|
||||
});
|
||||
/**
|
||||
* 计算表格列宽
|
||||
*/
|
||||
const calculateTableColumnMinWidth = () => {
|
||||
if (isCalculatedWidth.value || !tableData.value || tableData.value.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 计算表格列宽
|
||||
props.columns.forEach((item) => {
|
||||
if (item.autoWidth && item.show) {
|
||||
item.autoCalculateMinWidth(tableData.value);
|
||||
}
|
||||
});
|
||||
|
||||
isCalculatedWidth.value = true;
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.data,
|
||||
@@ -266,9 +313,6 @@ watch(
|
||||
);
|
||||
|
||||
onMounted(async () => {
|
||||
calcuTableHeight();
|
||||
useEventListener(window, 'resize', calcuTableHeight);
|
||||
|
||||
if (props.searchItems.length > 0) {
|
||||
nowSearchItem.value = props.searchItems[0];
|
||||
}
|
||||
@@ -292,11 +336,6 @@ onMounted(async () => {
|
||||
}
|
||||
});
|
||||
|
||||
const calcuTableHeight = () => {
|
||||
const headerHeight = isShowSearch.value ? 330 : 250;
|
||||
state.tableMaxHeight = window.innerHeight - headerHeight + 'px';
|
||||
};
|
||||
|
||||
const searchFormItemKeyUpEnter = (event: any) => {
|
||||
event.preventDefault();
|
||||
search();
|
||||
@@ -326,114 +365,4 @@ defineExpose({
|
||||
total,
|
||||
});
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
.table-box,
|
||||
.table-main {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
// 表格 header 样式
|
||||
.table-header {
|
||||
width: 100%;
|
||||
|
||||
.header-button-lf {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.header-button-ri {
|
||||
float: right;
|
||||
|
||||
.tool-button {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.simple-search-form {
|
||||
margin-right: 10px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
::v-deep(.el-form-item__content > *) {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
.simple-search-form-label {
|
||||
text-align: right;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.simple-search-form-btn:hover {
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.el-button {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
// el-table 表格样式
|
||||
.el-table {
|
||||
flex: 1;
|
||||
|
||||
// 修复 safari 浏览器表格错位 https://github.com/HalseySpicy/Geeker-Admin/issues/83
|
||||
table {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
// .el-table__header th {
|
||||
// height: 45px;
|
||||
// font-size: 15px;
|
||||
// font-weight: bold;
|
||||
// color: var(--el-text-color-primary);
|
||||
// background: var(--el-fill-color-light);
|
||||
// }
|
||||
|
||||
// .el-table__row {
|
||||
// height: 45px;
|
||||
// font-size: 14px;
|
||||
|
||||
// .move {
|
||||
// cursor: move;
|
||||
|
||||
// .el-icon {
|
||||
// cursor: move;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// 设置 el-table 中 header 文字不换行,并省略
|
||||
.el-table__header .el-table__cell>.cell {
|
||||
// white-space: nowrap;
|
||||
white-space: wrap;
|
||||
}
|
||||
|
||||
// 解决表格数据为空时样式不居中问题(仅在element-plus中)
|
||||
// .el-table__empty-block {
|
||||
// position: absolute;
|
||||
// top: 50%;
|
||||
// left: 50%;
|
||||
// transform: translate(-50%, -50%);
|
||||
|
||||
// .table-empty {
|
||||
// line-height: 30px;
|
||||
// }
|
||||
// }
|
||||
|
||||
// table 中 image 图片样式
|
||||
.table-image {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
::v-deep(.el-form-item__label) {
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<style scoped lang="scss"></style>
|
||||
|
||||
@@ -38,7 +38,7 @@ export class TableColumn {
|
||||
/**
|
||||
* 插槽名,
|
||||
*/
|
||||
private slotName: string = '';
|
||||
slotName: string = '';
|
||||
|
||||
showOverflowTooltip: boolean = true;
|
||||
|
||||
@@ -73,12 +73,12 @@ export class TableColumn {
|
||||
/**
|
||||
* 是否显示该列
|
||||
*/
|
||||
private show: boolean = true;
|
||||
show: boolean = true;
|
||||
|
||||
/**
|
||||
* 是否展示美化按钮(主要用于美化json文本等)
|
||||
*/
|
||||
private isBeautify: boolean = false;
|
||||
isBeautify: boolean = false;
|
||||
|
||||
constructor(prop: string, label: string) {
|
||||
this.prop = prop;
|
||||
|
||||
@@ -3,12 +3,12 @@
|
||||
<div ref="viewportRef" class="viewport" :style="{ width: state.size.width + 'px', height: state.size.height + 'px' }">
|
||||
<div ref="displayRef" class="display" tabindex="0" />
|
||||
<div class="btn-box">
|
||||
<SvgIcon name="DocumentCopy" @click="openPaste" :size="20" class="pointer-icon mr10" title="剪贴板" />
|
||||
<SvgIcon name="FolderOpened" @click="openFilesystem" :size="20" class="pointer-icon mr10" title="文件管理" />
|
||||
<SvgIcon name="FullScreen" @click="state.fullscreen ? closeFullScreen() : openFullScreen()" :size="20" class="pointer-icon mr10" title="全屏" />
|
||||
<SvgIcon name="DocumentCopy" @click="openPaste" :size="20" class="pointer-icon mr-2" title="剪贴板" />
|
||||
<SvgIcon name="FolderOpened" @click="openFilesystem" :size="20" class="pointer-icon mr-2" title="文件管理" />
|
||||
<SvgIcon name="FullScreen" @click="state.fullscreen ? closeFullScreen() : openFullScreen()" :size="20" class="pointer-icon mr-2" title="全屏" />
|
||||
|
||||
<el-dropdown>
|
||||
<SvgIcon name="Monitor" :size="20" class="pointer-icon mr10" title="发送快捷键" style="color: #fff" />
|
||||
<SvgIcon name="Monitor" :size="20" class="pointer-icon mr-2" title="发送快捷键" style="color: #fff" />
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item @click="openSendKeyboard(['65507', '65513', '65535'])"> Ctrl + Alt + Delete </el-dropdown-item>
|
||||
@@ -21,7 +21,7 @@
|
||||
</template>
|
||||
</el-dropdown>
|
||||
|
||||
<SvgIcon name="Refresh" @click="connect(0, 0)" :size="20" class="pointer-icon mr10" title="重新连接" />
|
||||
<SvgIcon name="Refresh" @click="connect(0, 0)" :size="20" class="pointer-icon mr-2" title="重新连接" />
|
||||
</div>
|
||||
<clipboard-dialog ref="clipboardRef" v-model:visible="state.clipboardDialog.visible" @close="closePaste" @submit="onsubmitClipboard" />
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<div class="title-right-fixed">
|
||||
<el-popconfirm @confirm="connect(true)" title="确认重新连接?">
|
||||
<template #reference>
|
||||
<div class="mr10 pointer">
|
||||
<div class="mr-2 cursor-pointer">
|
||||
<el-tag v-if="state.status == TerminalStatus.Connected" type="success" effect="light" round> 已连接 </el-tag>
|
||||
<el-tag v-else type="danger" effect="light" round> 未连接,点击重连 </el-tag>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div id="terminal-body" :style="{ height }">
|
||||
<div ref="terminalRef" class="terminal" />
|
||||
<div class="h-full w-full flex">
|
||||
<div ref="terminalRef" class="h-full w-full" :style="{ background: getTerminalTheme().background }" />
|
||||
|
||||
<TerminalSearch ref="terminalSearchRef" :search-addon="state.addon.search" @close="focus" />
|
||||
</div>
|
||||
@@ -18,7 +18,7 @@ import { useThemeConfig } from '@/store/themeConfig';
|
||||
import { ref, nextTick, reactive, onMounted, onBeforeUnmount, watch } from 'vue';
|
||||
import TerminalSearch from './TerminalSearch.vue';
|
||||
import { TerminalStatus } from './common';
|
||||
import { useDebounceFn, useEventListener } from '@vueuse/core';
|
||||
import { useDebounceFn, useEventListener, useIntervalFn } from '@vueuse/core';
|
||||
import themes from './themes';
|
||||
import { TrzszFilter } from 'trzsz';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
@@ -41,13 +41,6 @@ const props = defineProps({
|
||||
socketUrl: {
|
||||
type: String,
|
||||
},
|
||||
/**
|
||||
* 高度
|
||||
*/
|
||||
height: {
|
||||
type: [String, Number],
|
||||
default: '100%',
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits(['statusChange']);
|
||||
@@ -60,7 +53,6 @@ const { themeConfig } = storeToRefs(useThemeConfig());
|
||||
// 终端实例
|
||||
let term: Terminal;
|
||||
let socket: WebSocket;
|
||||
let pingInterval: any;
|
||||
|
||||
const state = reactive({
|
||||
// 插件
|
||||
@@ -89,7 +81,9 @@ watch(
|
||||
watch(
|
||||
() => themeConfig.value.terminalTheme,
|
||||
() => {
|
||||
term.options.theme = getTerminalTheme();
|
||||
if (term) {
|
||||
term.options.theme = getTerminalTheme();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
@@ -97,7 +91,7 @@ onBeforeUnmount(() => {
|
||||
close();
|
||||
});
|
||||
|
||||
function init() {
|
||||
const init = () => {
|
||||
state.status = TerminalStatus.NoConnected;
|
||||
if (term) {
|
||||
console.log('重新连接...');
|
||||
@@ -106,9 +100,9 @@ function init() {
|
||||
nextTick(() => {
|
||||
initTerm();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
async function initTerm() {
|
||||
const initTerm = async () => {
|
||||
term = new Terminal({
|
||||
fontSize: themeConfig.value.terminalFontSize || 15,
|
||||
fontWeight: themeConfig.value.terminalFontWeight || 'normal',
|
||||
@@ -144,9 +138,9 @@ async function initTerm() {
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
function initSocket() {
|
||||
const initSocket = () => {
|
||||
if (!props.socketUrl) {
|
||||
return;
|
||||
}
|
||||
@@ -154,7 +148,8 @@ function initSocket() {
|
||||
// 监听socket连接
|
||||
socket.onopen = () => {
|
||||
// 注册心跳
|
||||
pingInterval = setInterval(sendPing, 15000);
|
||||
useIntervalFn(sendPing, 15000);
|
||||
|
||||
state.status = TerminalStatus.Connected;
|
||||
|
||||
focus();
|
||||
@@ -162,7 +157,7 @@ function initSocket() {
|
||||
|
||||
// 如果有初始要执行的命令,则发送执行命令
|
||||
if (props.cmd) {
|
||||
sendCmd(props.cmd + ' \r');
|
||||
sendData(props.cmd + ' \r');
|
||||
}
|
||||
};
|
||||
|
||||
@@ -177,9 +172,9 @@ function initSocket() {
|
||||
console.log('terminal socket close...', e.reason);
|
||||
state.status = TerminalStatus.Disconnected;
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
function loadAddon() {
|
||||
const loadAddon = () => {
|
||||
// 注册搜索组件
|
||||
const searchAddon = new SearchAddon();
|
||||
state.addon.search = searchAddon;
|
||||
@@ -196,7 +191,7 @@ function loadAddon() {
|
||||
// write the server output to the terminal
|
||||
writeToTerminal: (data: any) => term.write(typeof data === 'string' ? data : new Uint8Array(data)),
|
||||
// send the user input to the server
|
||||
sendToServer: sendCmd,
|
||||
sendToServer: sendData,
|
||||
// the terminal columns
|
||||
terminalColumns: term.cols,
|
||||
// there is a windows shell
|
||||
@@ -222,7 +217,7 @@ function loadAddon() {
|
||||
.then(() => console.log('upload success'))
|
||||
.catch((err: any) => console.log(err));
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// 写入内容至终端
|
||||
const write2Term = (data: any) => {
|
||||
@@ -270,30 +265,28 @@ enum MsgType {
|
||||
Ping = 3,
|
||||
}
|
||||
|
||||
const send = (msg: any) => {
|
||||
state.status == TerminalStatus.Connected && socket?.send(msg);
|
||||
const send2Socket = (data: any) => {
|
||||
state.status == TerminalStatus.Connected && socket?.send(data);
|
||||
};
|
||||
|
||||
const sendResize = (cols: number, rows: number) => {
|
||||
send(`${MsgType.Resize}|${rows}|${cols}`);
|
||||
send2Socket(`${MsgType.Resize}|${rows}|${cols}`);
|
||||
};
|
||||
|
||||
const sendPing = () => {
|
||||
send(`${MsgType.Ping}|ping`);
|
||||
send2Socket(`${MsgType.Ping}|ping`);
|
||||
};
|
||||
|
||||
function sendCmd(key: any) {
|
||||
send(`${MsgType.Data}|${key}`);
|
||||
}
|
||||
const sendData = (key: any) => {
|
||||
send2Socket(`${MsgType.Data}|${key}`);
|
||||
};
|
||||
|
||||
function closeSocket() {
|
||||
const closeSocket = () => {
|
||||
// 关闭 websocket
|
||||
socket && socket.readyState === 1 && socket.close();
|
||||
// 清除 ping
|
||||
pingInterval && clearInterval(pingInterval);
|
||||
}
|
||||
};
|
||||
|
||||
function close() {
|
||||
const close = () => {
|
||||
console.log('in terminal body close');
|
||||
closeSocket();
|
||||
if (term) {
|
||||
@@ -302,7 +295,7 @@ function close() {
|
||||
state.addon.weblinks.dispose();
|
||||
term.dispose();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const getStatus = (): TerminalStatus => {
|
||||
return state.status;
|
||||
@@ -310,17 +303,4 @@ const getStatus = (): TerminalStatus => {
|
||||
|
||||
defineExpose({ init, fitTerminal, focus, clear, close, getStatus, sendResize, write2Term, writeln2Term });
|
||||
</script>
|
||||
<style lang="scss">
|
||||
#terminal-body {
|
||||
width: 100%;
|
||||
|
||||
.terminal {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
// .xterm .xterm-viewport {
|
||||
// overflow-y: hidden;
|
||||
// }
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<style lang="scss"></style>
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
<div class="title-right-fixed">
|
||||
<el-popconfirm @confirm="reConnect(openTerminal.terminalId)" :title="$t('components.terminal.connConfirm')">
|
||||
<template #reference>
|
||||
<div class="mr15 pointer">
|
||||
<div class="mr-4 cursor-pointer">
|
||||
<el-tag v-if="openTerminal.status == TerminalStatus.Connected" type="success" effect="light" round>
|
||||
{{ $t('components.terminal.connected') }}
|
||||
</el-tag>
|
||||
@@ -39,10 +39,10 @@
|
||||
|
||||
<el-popover placement="bottom" :width="200" trigger="hover">
|
||||
<template #reference>
|
||||
<SvgIcon name="QuestionFilled" :size="20" class="pointer-icon mr10" />
|
||||
<SvgIcon name="QuestionFilled" :size="20" class="pointer-icon !mr-2" />
|
||||
</template>
|
||||
<div>ctrl | command + f ({{ $t('components.terminal.search') }})</div>
|
||||
<div class="mt5">{{ $t('components.terminal.reConnTips') }}</div>
|
||||
<div class="mt-1">{{ $t('components.terminal.reConnTips') }}</div>
|
||||
</el-popover>
|
||||
|
||||
<SvgIcon
|
||||
@@ -50,7 +50,7 @@
|
||||
v-if="props.visibleMinimize"
|
||||
@click="minimize(openTerminal.terminalId)"
|
||||
:size="20"
|
||||
class="pointer-icon mr10"
|
||||
class="pointer-icon mr-2"
|
||||
:title="$t('components.terminal.minimize')"
|
||||
/>
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
name="FullScreen"
|
||||
@click="handlerFullScreen(openTerminal)"
|
||||
:size="20"
|
||||
class="pointer-icon mr10"
|
||||
class="pointer-icon mr-2"
|
||||
:title="$t('components.terminal.fullScreenTitle')"
|
||||
/>
|
||||
|
||||
@@ -88,7 +88,7 @@
|
||||
<el-card
|
||||
v-for="minimizeTerminal of minimizeTerminals"
|
||||
:key="minimizeTerminal.terminalId"
|
||||
:class="`terminal-minimize-item pointer ${minimizeTerminal.styleClass}`"
|
||||
:class="`terminal-minimize-item cursor-pointer ${minimizeTerminal.styleClass}`"
|
||||
size="small"
|
||||
@click="maximize(minimizeTerminal.terminalId)"
|
||||
>
|
||||
@@ -99,7 +99,7 @@
|
||||
</el-tooltip>
|
||||
|
||||
<!-- 关闭按钮 -->
|
||||
<SvgIcon name="CloseBold" @click.stop="closeMinimizeTerminal(minimizeTerminal.terminalId)" class="ml10 pointer-icon fr" :size="20" />
|
||||
<SvgIcon name="CloseBold" @click.stop="closeMinimizeTerminal(minimizeTerminal.terminalId)" class="ml-2 pointer-icon float-right" :size="20" />
|
||||
</el-card>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-drawer v-model="visible" :before-close="cancel" size="50%">
|
||||
<el-drawer v-model="visible" :before-close="cancel" size="50%" body-class="flex flex-col">
|
||||
<template #header>
|
||||
<DrawerHeader :header="props.title" :back="cancel">
|
||||
<template #extra>
|
||||
<EnumTag :enums="LogTypeEnum" :value="log?.type" class="mr20" />
|
||||
<EnumTag :enums="LogTypeEnum" :value="log?.type" class="mr-4.5" />
|
||||
</template>
|
||||
</DrawerHeader>
|
||||
</template>
|
||||
|
||||
<el-descriptions class="mb10" :column="1" border v-if="extra">
|
||||
<el-descriptions class="mb-2" :column="1" border v-if="extra">
|
||||
<el-descriptions-item v-for="(value, key) in extra" :key="key" :span="1" :label="key">{{ value }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
|
||||
<TerminalBody class="mb10" ref="terminalRef" height="calc(100vh - 220px)" />
|
||||
<TerminalBody class="mb-2 flex-1" ref="terminalRef" />
|
||||
</el-drawer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -22,7 +22,7 @@ export const usePageTable = (
|
||||
) => {
|
||||
const state = reactive({
|
||||
// 表格数据
|
||||
tableData: [],
|
||||
tableData: [{}],
|
||||
// 总数量
|
||||
total: 0,
|
||||
// 查询参数,包含分页参数
|
||||
|
||||
@@ -2,11 +2,11 @@ import router from '@/router';
|
||||
import { clearUser, getClientId, getRefreshToken, getToken, saveRefreshToken, saveToken } from '@/common/utils/storage';
|
||||
import { templateResolve } from '@/common/utils/string';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import { createFetch } from '@vueuse/core';
|
||||
import { createFetch, UseFetchReturn } from '@vueuse/core';
|
||||
import Api from '@/common/Api';
|
||||
import { Result, ResultEnum } from '@/common/request';
|
||||
import config from '@/common/config';
|
||||
import { unref } from 'vue';
|
||||
import { ref, unref } from 'vue';
|
||||
import { URL_401 } from '@/router/staticRouter';
|
||||
import openApi from '@/common/openApi';
|
||||
import { useThemeConfig } from '@/store/themeConfig';
|
||||
@@ -38,14 +38,19 @@ const useCustomFetch = createFetch({
|
||||
return { options };
|
||||
},
|
||||
async afterFetch(ctx) {
|
||||
const result: Result = await ctx.response.json();
|
||||
ctx.data = result;
|
||||
ctx.data = await ctx.response.json();
|
||||
return ctx;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export function useApiFetch<T>(api: Api, params: any = null, reqOptions: RequestInit = {}) {
|
||||
interface EsReq {
|
||||
esProxyReq: boolean;
|
||||
}
|
||||
|
||||
export interface RequestOptions extends RequestInit, EsReq {}
|
||||
|
||||
export function useApiFetch<T>(api: Api, params: any = null, reqOptions?: RequestOptions) {
|
||||
const uaf = useCustomFetch<T>(api.url, {
|
||||
async beforeFetch({ url, options }) {
|
||||
options.method = api.method;
|
||||
@@ -90,14 +95,24 @@ export function useApiFetch<T>(api: Api, params: any = null, reqOptions: Request
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
||||
onFetchError: (ctx) => {
|
||||
if (reqOptions?.esProxyReq) {
|
||||
uaf.data = { value: JSON.parse(ctx.data) };
|
||||
return Promise.resolve(uaf.data);
|
||||
}
|
||||
return ctx;
|
||||
},
|
||||
}) as any;
|
||||
|
||||
// 统一处理后的返回结果,如果直接使用uaf.data,则数据会出现由{code: x, data: {}} -> data 的变化导致某些结果绑定报错
|
||||
const data = ref<T | null>(null);
|
||||
return {
|
||||
execute: async function () {
|
||||
return execCustomFetch(uaf);
|
||||
await execCustomFetch(uaf, reqOptions);
|
||||
data.value = uaf.data.value;
|
||||
},
|
||||
isFetching: uaf.isFetching,
|
||||
data: uaf.data,
|
||||
data: data,
|
||||
abort: uaf.abort,
|
||||
};
|
||||
}
|
||||
@@ -105,37 +120,44 @@ export function useApiFetch<T>(api: Api, params: any = null, reqOptions: Request
|
||||
let refreshingToken = false;
|
||||
let queue: any[] = [];
|
||||
|
||||
async function execCustomFetch(uaf: any) {
|
||||
async function execCustomFetch(uaf: UseFetchReturn<any>, reqOptions?: RequestOptions) {
|
||||
try {
|
||||
await uaf.execute(true);
|
||||
} catch (e: any) {
|
||||
const rejectPromise = Promise.reject(e);
|
||||
if (!reqOptions?.esProxyReq) {
|
||||
const rejectPromise = Promise.reject(e);
|
||||
|
||||
if (e?.name == 'AbortError') {
|
||||
console.log('请求已取消');
|
||||
if (e?.name == 'AbortError') {
|
||||
console.log('请求已取消');
|
||||
return rejectPromise;
|
||||
}
|
||||
|
||||
const respStatus = uaf.response.value?.status;
|
||||
if (respStatus == 404) {
|
||||
ElMessage.error('url not found');
|
||||
return rejectPromise;
|
||||
}
|
||||
if (respStatus == 500) {
|
||||
ElMessage.error('server error');
|
||||
return rejectPromise;
|
||||
}
|
||||
|
||||
console.error(e);
|
||||
ElMessage.error('network error');
|
||||
return rejectPromise;
|
||||
}
|
||||
|
||||
const respStatus = uaf.response.value?.status;
|
||||
if (respStatus == 404) {
|
||||
ElMessage.error('请求接口不存在');
|
||||
return rejectPromise;
|
||||
}
|
||||
if (respStatus == 500) {
|
||||
ElMessage.error('服务器响应异常');
|
||||
return rejectPromise;
|
||||
}
|
||||
|
||||
console.error(e);
|
||||
ElMessage.error('网络请求错误');
|
||||
return rejectPromise;
|
||||
}
|
||||
|
||||
const result: Result = uaf.data.value as any;
|
||||
const result: Result & { error: any; status: number } = uaf.data.value as any;
|
||||
if (!result) {
|
||||
ElMessage.error('网络请求失败');
|
||||
ElMessage.error('network request failed');
|
||||
return Promise.reject(result);
|
||||
}
|
||||
// es代理请求
|
||||
if (reqOptions?.esProxyReq) {
|
||||
uaf.data.value = result;
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
|
||||
const resultCode = result.code;
|
||||
|
||||
@@ -151,7 +173,7 @@ async function execCustomFetch(uaf: any) {
|
||||
// 请求加入队列等待, 防止并发多次请求refreshToken
|
||||
return new Promise((resolve) => {
|
||||
queue.push(() => {
|
||||
resolve(execCustomFetch(uaf));
|
||||
resolve(execCustomFetch(uaf, reqOptions));
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -175,13 +197,13 @@ async function execCustomFetch(uaf: any) {
|
||||
queue = [];
|
||||
}
|
||||
|
||||
await execCustomFetch(uaf);
|
||||
await execCustomFetch(uaf, reqOptions);
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果提示没有权限,则跳转至无权限页面
|
||||
if (resultCode === ResultEnum.NO_PERMISSION) {
|
||||
router.push({
|
||||
await router.push({
|
||||
path: URL_401,
|
||||
});
|
||||
return Promise.reject(result);
|
||||
|
||||
@@ -7,12 +7,16 @@ export default {
|
||||
detail: 'Details',
|
||||
add: 'Add',
|
||||
save: 'Save',
|
||||
close: 'Close',
|
||||
download: 'Download',
|
||||
upload: 'Upload',
|
||||
remove: 'Remove',
|
||||
confirm: 'Confirm',
|
||||
cancel: 'Cancel',
|
||||
submit: 'Submit',
|
||||
operation: 'Operations',
|
||||
name: 'Name',
|
||||
version: 'Version',
|
||||
code: 'Code',
|
||||
remark: 'Remark',
|
||||
status: 'Status',
|
||||
@@ -48,9 +52,11 @@ export default {
|
||||
previousStep: 'Previous Step',
|
||||
nextStep: 'Next Step',
|
||||
copy: 'Copy',
|
||||
copyCell: 'Copy Cell',
|
||||
search: 'Search',
|
||||
pleaseInput: 'Please enter {label}',
|
||||
pleaseSelect: 'Please select {label}',
|
||||
pleaseSelectOne: 'Please select Only One Data',
|
||||
formValidationError: 'Please check the form',
|
||||
createTitle: 'Create {name}',
|
||||
editTitle: 'Edit {name}',
|
||||
@@ -61,6 +67,8 @@ export default {
|
||||
deleteSuccess: 'delete successfully',
|
||||
operateSuccess: 'operate successfully',
|
||||
fieldNotEmpty: '{field} cannot be empty',
|
||||
selectAll: 'Select all',
|
||||
MultiPlaceholder: 'Multiple are separated by commas',
|
||||
},
|
||||
layout: {
|
||||
user: {
|
||||
|
||||
@@ -219,4 +219,11 @@ export default {
|
||||
running: 'Running',
|
||||
waitRun: 'Wait Run',
|
||||
},
|
||||
es: {
|
||||
keywordPlaceholder: 'host / name / code',
|
||||
port: 'Port',
|
||||
acName: 'Credential',
|
||||
dbInst: 'Es Instance',
|
||||
connSuccess: 'be connected successfully',
|
||||
},
|
||||
};
|
||||
|
||||
122
frontend/src/i18n/en/es.ts
Normal file
122
frontend/src/i18n/en/es.ts
Normal file
@@ -0,0 +1,122 @@
|
||||
export default {
|
||||
es: {
|
||||
keywordPlaceholder: 'host / name / code',
|
||||
port: 'Port',
|
||||
size: 'size',
|
||||
docs: 'docs',
|
||||
health: 'health',
|
||||
aliases: 'Aliases',
|
||||
addAlias: 'Add Alias',
|
||||
specifyIdAdd: 'Specify the ID added, if id exists, then update',
|
||||
addIndex: 'Add Index',
|
||||
editIndex: 'Edit Index',
|
||||
status: 'status',
|
||||
acName: 'Credential',
|
||||
emptyTable: 'data not fund',
|
||||
connSuccess: 'be connected successfully',
|
||||
shouldTestConn: 'please test connection first',
|
||||
instance: 'ES Instance',
|
||||
instanceSave: 'Save Instance',
|
||||
instanceDel: 'Delete Instance',
|
||||
operation: 'Data Operation',
|
||||
dataSave: 'Data Save',
|
||||
dataDel: 'Data Del',
|
||||
indexName: 'Index Name',
|
||||
requireIndexName: 'Index Name Is Required',
|
||||
indexDetail: 'Index Detail',
|
||||
indexMapping: 'Mappings',
|
||||
indexStats: 'Stats',
|
||||
opViewColumns: 'Option View Columns',
|
||||
opIndex: 'Index Management',
|
||||
opSearch: 'Search',
|
||||
searchParamsPreview: 'Search Params Preview',
|
||||
opBasicSearch: 'Basic Search',
|
||||
opSeniorSearch: 'Senior Search',
|
||||
sampleMappings: 'Sample Mappings',
|
||||
copyMappings: 'Copy Mappings',
|
||||
readonlyMsg: 'The content is readOnly',
|
||||
opDashboard: 'Dashboard',
|
||||
opSettings: 'Settings',
|
||||
templates: 'Templates',
|
||||
availableSettingFields: 'Available Setting Fields',
|
||||
Reindex: 'Reindex',
|
||||
ReindexTargetIdx: 'Target Index',
|
||||
ReindexIsSync: 'Sync Able',
|
||||
ReindexDescription:
|
||||
"If a field in Mapping has been defined, you can't modify the type of the field, and you can't change the number of shards, you can use the Reindex API to solve this problem.",
|
||||
ReindexSyncDescription: 'If the amount of index data is large, we recommend that you enable asynchronous data to avoid request timeouts.',
|
||||
ReindexToOtherInst: 'To other Instance',
|
||||
ReindexSyncTask: 'Sync Task',
|
||||
makeSearchParam: 'Make Search Params',
|
||||
filterColumn: 'Filter Columns',
|
||||
searchParams: 'Search',
|
||||
searchParamsDesc: 'If no field is selected or no condition value is set, it will not take effect',
|
||||
standardSearch: 'Standard Search',
|
||||
AggregationSearch: 'Aggregation Search',
|
||||
SqlSearch: 'Sql Search',
|
||||
searchError: 'Search Error',
|
||||
execError: 'Exec Error',
|
||||
docJsonError: 'Document JSON Format Error',
|
||||
sortParams: 'Sort',
|
||||
otherParams: 'Other',
|
||||
previewParams: 'Preview',
|
||||
closeIndexConfirm: 'This operation will close index [{name}]. Do you want to continue?',
|
||||
openIndexConfirm: 'This operation will open index [{name}]. Do you want to continue?',
|
||||
clearCacheConfirm: 'This operation will clear index [{name}] cache. Do you want to continue?',
|
||||
page: {
|
||||
home: 'First Page',
|
||||
prev: 'Prev Page',
|
||||
next: 'Next Page',
|
||||
total: 'Total Count',
|
||||
changeSize: 'Change Page Size',
|
||||
},
|
||||
temp: {
|
||||
addTemp: 'Add template',
|
||||
view: 'Template Detail',
|
||||
name: 'name',
|
||||
priority: 'priority',
|
||||
index_patterns: 'patterns',
|
||||
content: 'content',
|
||||
showHide: 'show system templates',
|
||||
description: 'description',
|
||||
filter: 'filter name / description',
|
||||
versionAlert: 'Versions prior to 7.8 are not supported',
|
||||
note: `1、When creating a new index, if the index name matches the wildcard of the index template, the index template's settings (_setting, _mapping, etc.) are used。
|
||||
2、Templates take effect only when an index is created, and modifying a template does not affect existing indexes。
|
||||
3、You can specify the value of "priority", which was "order" before version 7.8, and if the new index name matches multiple templates, the one with the lowest priority will be used first.`,
|
||||
},
|
||||
dashboard: {
|
||||
instInfo: 'Instance Info',
|
||||
clusterHealth: 'Cluster Health',
|
||||
nodes: 'Nodes Info',
|
||||
sysMem: 'System Mem',
|
||||
jvmMem: 'JVM Mem',
|
||||
fileSystem: 'File System',
|
||||
analyze: 'Analyze',
|
||||
idxName: 'Index Name',
|
||||
field: 'Field',
|
||||
text: 'Text',
|
||||
startAnalyze: 'Start Analyze',
|
||||
},
|
||||
contextmenu: {
|
||||
index: {
|
||||
addIndex: 'Add Index',
|
||||
showSys: 'Show System Index',
|
||||
copyName: 'Copy Name',
|
||||
refresh: 'Refresh Index',
|
||||
flush: 'Flush Index',
|
||||
clearCache: 'Clear Index Cache',
|
||||
addAlias: 'Add Alias',
|
||||
Close: 'Close',
|
||||
Open: 'Open',
|
||||
Delete: 'Delete Index',
|
||||
edit: 'Edit Index',
|
||||
DeleteSelectLine: 'Copy Selected Line Json',
|
||||
BaseSearch: 'Base Search',
|
||||
SeniorSearch: 'Senior Search',
|
||||
copyLineJson: 'Copy Line Json',
|
||||
copySelectLineJson: 'Copy Selected Line Json',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -6,23 +6,23 @@ export default {
|
||||
triggeringCondition: 'Condition',
|
||||
triggeringConditionTips: 'go template syntax. If the output is 1, the approval process is triggered',
|
||||
conditionPlaceholder: 'Trigger condition, return value =1, means to trigger the approval process',
|
||||
conditionDefault: `{{/* DBMS- Run Sql rules The param parameter is described as follows */}}
|
||||
{{/* stmtType: select / read / insert / update / delete / ddl ; */}}
|
||||
{{ if eq .bizType "db_sql_exec_flow"}}
|
||||
{{/* Enable process approval when select and read statements are not available */}}
|
||||
{{ if and (ne .param.stmtType "select") (ne .param.stmtType "read") }}
|
||||
1
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
conditionDefault: `{'{{'}/* DBMS- Run Sql rules The param parameter is described as follows */{'}}'}
|
||||
{'{{'}/* stmtType: select / read / insert / update / delete / ddl ; */{'}}'}
|
||||
{'{{'} if eq .bizType "db_sql_exec_flow"{'}}'}
|
||||
{'{{'}/* Enable process approval when select and read statements are not available */{'}}'}
|
||||
{'{{'} if and (ne .param.stmtType "select") (ne .param.stmtType "read") {'}}'}
|
||||
1
|
||||
{'{{'} end {'}}'}
|
||||
{'{{'} end {'}}'}
|
||||
|
||||
{{/* Redis-Run Cmd rules; param: parameter is described as follows */}}
|
||||
{{/* cmdType: read(Read cmd) / write(Write cmd); */}}
|
||||
{{/* cmd: get/set/hset... */}}
|
||||
{{ if eq .bizType "redis_run_cmd_flow"}}
|
||||
{{ if eq .param.cmdType "write" }}
|
||||
1
|
||||
{{ end }}
|
||||
{{ end }}`,
|
||||
{'{{'}/* Redis-Run Cmd rules; param: parameter is described as follows */{'}}'}
|
||||
{'{{'}/* cmdType: read(Read cmd) / write(Write cmd); */{'}}'}
|
||||
{'{{'}/* cmd: get/set/hset... */{'}}'}
|
||||
{'{{'} if eq .bizType "redis_run_cmd_flow"{'}}'}
|
||||
{'{{'} if eq .param.cmdType "write" {'}}'}
|
||||
1
|
||||
{'{{'} end {'}}'}
|
||||
{'{{'} end {'}}'}`,
|
||||
nodeName: 'Node Name',
|
||||
nodeNameTips: 'Click the specified node to drag and drop sort',
|
||||
auditor: 'Auditor',
|
||||
@@ -32,6 +32,27 @@ export default {
|
||||
enable: 'Enable',
|
||||
disable: 'Disable',
|
||||
|
||||
todoTask: 'Pending Tasks',
|
||||
doneTask: 'Completed Tasks',
|
||||
flowDesign: 'Flow Design',
|
||||
clear: 'Clear',
|
||||
approvalMode: 'Approval Mode',
|
||||
andSign: 'All Approve (AND)',
|
||||
orSign: 'Any Approve (OR)',
|
||||
voteSign: 'Vote Approval',
|
||||
taskCandidate: 'Task Assignees',
|
||||
mustOneStartNode: 'There must be one start node in the flow',
|
||||
mustOneEndNode: 'There must be one end node in the flow',
|
||||
mustOneOutEdgeForStartNode: 'The start node must have at least one outgoing edge',
|
||||
mustOneInEdgeForEndNode: 'The end node must have at least one incoming edge',
|
||||
approvalRecord: 'Approval Records',
|
||||
start: 'Start',
|
||||
end: 'End',
|
||||
usertask: 'User Task', // 建议拼写修正为 userTask
|
||||
serial: 'Exclusive Gateway',
|
||||
parallel: 'Parallel Gateway',
|
||||
flowEdge: 'Sequence Flow',
|
||||
|
||||
// procinst
|
||||
startProcess: 'Start Process',
|
||||
cancelProcessConfirm: 'Confirm canceling the process?',
|
||||
@@ -80,15 +101,17 @@ export default {
|
||||
redisRunCmd: 'Redis-Run Cmd',
|
||||
|
||||
// task
|
||||
approveNode: 'Approve Node',
|
||||
approveForm: 'Approve Form',
|
||||
approveResult: 'Result',
|
||||
approveNode: 'Approval Node',
|
||||
approveForm: 'Approval Form',
|
||||
approveResult: 'Approval Result',
|
||||
approvalRemark: 'Approval Comments',
|
||||
approver: 'Approver',
|
||||
audit: 'Audit',
|
||||
procinstStatus: 'Process status',
|
||||
taskStatus: 'Task status',
|
||||
procinstStatus: 'Process Status',
|
||||
taskStatus: 'Task Status',
|
||||
taskName: 'Task Name',
|
||||
taskBeginTime: 'Begin Time',
|
||||
flowAudit: 'Approval Process',
|
||||
notify: 'Notification',
|
||||
taskBeginTime: 'Start Time',
|
||||
flowAudit: 'Process Audit',
|
||||
notify: 'Notify',
|
||||
},
|
||||
};
|
||||
|
||||
@@ -7,12 +7,16 @@ export default {
|
||||
detail: '详情',
|
||||
add: '添加',
|
||||
save: '保存',
|
||||
close: '关闭',
|
||||
download: '下载',
|
||||
upload: '上传',
|
||||
remove: '移除',
|
||||
confirm: '确定',
|
||||
cancel: '取消',
|
||||
submit: '提交',
|
||||
operation: '操作',
|
||||
name: '名称',
|
||||
version: '版本',
|
||||
code: '编号',
|
||||
remark: '备注',
|
||||
status: '状态',
|
||||
@@ -48,9 +52,11 @@ export default {
|
||||
previousStep: '上一步',
|
||||
nextStep: '下一步',
|
||||
copy: '复制',
|
||||
copyCell: '复制单元格',
|
||||
search: '搜索',
|
||||
pleaseInput: '请输入{label}',
|
||||
pleaseSelect: '请选择{label}',
|
||||
pleaseSelectOne: '请选择一条数据',
|
||||
formValidationError: '信息填写有误,请检查',
|
||||
createTitle: '创建{name}',
|
||||
editTitle: '编辑{name}',
|
||||
@@ -61,6 +67,8 @@ export default {
|
||||
deleteSuccess: '删除成功',
|
||||
operateSuccess: '操作成功',
|
||||
fieldNotEmpty: '{field}不能为空',
|
||||
selectAll: '全选',
|
||||
MultiPlaceholder: '多个用逗号隔开',
|
||||
},
|
||||
layout: {
|
||||
user: {
|
||||
|
||||
121
frontend/src/i18n/zh-cn/es.ts
Normal file
121
frontend/src/i18n/zh-cn/es.ts
Normal file
@@ -0,0 +1,121 @@
|
||||
export default {
|
||||
es: {
|
||||
keywordPlaceholder: 'host / 名称 / 编号',
|
||||
port: '端口',
|
||||
size: '存储大小',
|
||||
docs: '文档数',
|
||||
health: '健康',
|
||||
aliases: '别名',
|
||||
addAlias: '添加别名',
|
||||
specifyIdAdd: '可指定_id添加,如果_id已存在,则修改',
|
||||
addIndex: '添加索引',
|
||||
editIndex: '编辑索引',
|
||||
status: '状态',
|
||||
acName: '授权凭证',
|
||||
emptyTable: '无数据',
|
||||
connSuccess: '连接成功',
|
||||
shouldTestConn: '请先测试连接可用性',
|
||||
instance: 'ES实例',
|
||||
instanceSave: '实例保存',
|
||||
instanceDel: '实例删除',
|
||||
operation: '数据操作',
|
||||
dataSave: '数据保存',
|
||||
dataDel: '数据删除',
|
||||
indexName: '索引名',
|
||||
requireIndexName: '请填写索引名',
|
||||
indexDetail: '索引详情',
|
||||
indexMapping: '映射',
|
||||
indexStats: '统计信息',
|
||||
opViewColumns: '设置显示字段',
|
||||
opIndex: '索引管理',
|
||||
opSearch: '搜索',
|
||||
searchParamsPreview: '搜索条件预览',
|
||||
opBasicSearch: '基础搜索',
|
||||
opSeniorSearch: '高级搜索',
|
||||
sampleMappings: 'Mapping示例',
|
||||
copyMappings: '拷贝Mapping',
|
||||
readonlyMsg: '该内容不可修改',
|
||||
opDashboard: '仪表盘',
|
||||
opSettings: '设置',
|
||||
templates: '模板管理',
|
||||
availableSettingFields: '支持修改的字段',
|
||||
Reindex: '索引迁移',
|
||||
ReindexTargetIdx: '目标索引',
|
||||
ReindexIsSync: '是否异步',
|
||||
ReindexDescription: '如果 Mapping 中字段已经定义就不能修改其字段的类型等属性了,同时也不能改变分片的数量, 可以使用 Reindex API 来解决这个问题。',
|
||||
ReindexSyncDescription: '如果索引数据量较大,建议开启异步,以免造成请求超时。',
|
||||
ReindexToOtherInst: '迁移到其他实例',
|
||||
ReindexSyncTask: '异步任务',
|
||||
makeSearchParam: '组装搜索条件',
|
||||
filterColumn: '过滤列名',
|
||||
searchParams: '查询',
|
||||
searchParamsDesc: '未选择字段,或未设置条件值,则不生效',
|
||||
standardSearch: '标准查询',
|
||||
AggregationSearch: '聚合查询',
|
||||
SqlSearch: 'Sql查询',
|
||||
searchError: '查询错误',
|
||||
execError: '执行错误',
|
||||
docJsonError: '文档JSON格式错误',
|
||||
sortParams: '排序',
|
||||
otherParams: '其他',
|
||||
previewParams: '预览',
|
||||
closeIndexConfirm: '将会关闭索引:[{name}]。 确认继续吗?',
|
||||
openIndexConfirm: '将会打开索引:[{name}]。 确认继续吗?',
|
||||
clearCacheConfirm: '将会清除索引:[{name}]缓存。 确认继续吗?',
|
||||
page: {
|
||||
home: '首页',
|
||||
prev: '上一页',
|
||||
next: '下一页',
|
||||
total: '点击切换总条数',
|
||||
changeSize: '修改每页条数',
|
||||
},
|
||||
temp: {
|
||||
addTemp: '添加模板',
|
||||
view: '模板详情',
|
||||
name: '模板名',
|
||||
priority: '优先级',
|
||||
index_patterns: '匹配模式',
|
||||
content: '模板内容',
|
||||
showHide: '显示隐藏模板',
|
||||
description: '描述信息',
|
||||
filter: '模糊过滤名字和描述',
|
||||
versionAlert: '暂不支持 7.8 以前的版本',
|
||||
note: `1、在新建索引时,如果索引名与索引模板的通配符匹配,那么就使用索引模板的设置(_setting、_mapping等)。
|
||||
2、模板仅在索引创建时才会生效,而且修改模板不会影响现有的索引。
|
||||
3、可以指定"priority"的数值,7.8版本前是"order",如果新建的索引名匹配到了多个模板,则优先使用priority最小的那个。`,
|
||||
},
|
||||
dashboard: {
|
||||
instInfo: '实例信息',
|
||||
clusterHealth: '集群健康',
|
||||
nodes: '节点信息',
|
||||
sysMem: '系统内存',
|
||||
jvmMem: 'JVM内存',
|
||||
fileSystem: '文件系统',
|
||||
analyze: '字段分析',
|
||||
idxName: '索引名',
|
||||
field: '字段名',
|
||||
text: '文本',
|
||||
startAnalyze: '开始分析',
|
||||
},
|
||||
contextmenu: {
|
||||
index: {
|
||||
addIndex: '添加索引',
|
||||
showSys: '显示系统索引',
|
||||
copyName: '复制名字',
|
||||
refresh: '刷新索引',
|
||||
flush: 'flush索引',
|
||||
clearCache: '清除索引缓存',
|
||||
addAlias: '添加别名',
|
||||
Close: '关闭索引',
|
||||
Open: '打开索引',
|
||||
Delete: '删除索引',
|
||||
edit: '编辑索引',
|
||||
DeleteSelectLine: '删除选中行',
|
||||
BaseSearch: '基本搜索',
|
||||
SeniorSearch: '高级搜索',
|
||||
copyLineJson: '复制整行JSON',
|
||||
copySelectLineJson: '复制选中行JSON',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -6,23 +6,23 @@ export default {
|
||||
triggeringCondition: '触发条件',
|
||||
triggeringConditionTips: 'go template语法。若输出结果为1,则表示触发该审批流程',
|
||||
conditionPlaceholder: '触发条件, 返回值=1, 则表示触发该审批流程',
|
||||
conditionDefault: `{{/* DBMS-执行sql规则; param参数描述如下 */}}
|
||||
{{/* stmtType: select / read / insert / update / delete / ddl ; */}}
|
||||
{{ if eq .bizType "db_sql_exec_flow"}}
|
||||
{{/* 不是select和read语句时,开启流程审批 */}}
|
||||
{{ if and (ne .param.stmtType "select") (ne .param.stmtType "read") }}
|
||||
conditionDefault: `{'{{'}/* DBMS-执行sql规则; param参数描述如下 */{'}}'}
|
||||
{'{{'}/* stmtType: select / read / insert / update / delete / ddl ; */{'}}'}
|
||||
{'{{'} if eq .bizType "db_sql_exec_flow"{'}}'}
|
||||
{'{{'}/* 不是select和read语句时,开启流程审批 */{'}}'}
|
||||
{'{{'} if and (ne .param.stmtType "select") (ne .param.stmtType "read"){'}}'}
|
||||
1
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{'{{'} end {'}}'}
|
||||
{'{{'} end {'}}'}
|
||||
|
||||
{{/* Redis-执行命令规则; param参数描述如下 */}}
|
||||
{{/* cmdType: read(读命令) / write(写命令); */}}
|
||||
{{/* cmd: get/set/hset...等 */}}
|
||||
{{ if eq .bizType "redis_run_cmd_flow"}}
|
||||
{{ if eq .param.cmdType "write" }}
|
||||
{'{{'}/* Redis-执行命令规则; param参数描述如下 */{'}}'}
|
||||
{'{{'}/* cmdType: read(读命令) / write(写命令); */{'}}'}
|
||||
{'{{'}/* cmd: get/set/hset...等 */{'}}'}
|
||||
{'{{'} if eq .bizType "redis_run_cmd_flow"{'}}'}
|
||||
{'{{'} if eq .param.cmdType "write" {'}}'}
|
||||
1
|
||||
{{ end }}
|
||||
{{ end }}`,
|
||||
{'{{'} end {'}}'}
|
||||
{'{{'} end {'}}'}`,
|
||||
nodeName: '节点名称',
|
||||
nodeNameTips: '点击指定节点可进行拖拽排序',
|
||||
auditor: '审核人员',
|
||||
@@ -32,6 +32,27 @@ export default {
|
||||
enable: '启用',
|
||||
disable: '禁用',
|
||||
|
||||
todoTask: '待办任务',
|
||||
doneTask: '已办任务',
|
||||
flowDesign: '流程设计',
|
||||
clear: '清空',
|
||||
approvalMode: '审批模式',
|
||||
andSign: '会签',
|
||||
orSign: '或签',
|
||||
voteSign: '票签',
|
||||
taskCandidate: '处理候选人',
|
||||
mustOneStartNode: '流程必须要有一个开始节点',
|
||||
mustOneEndNode: '流程必须要有一个结束节点',
|
||||
mustOneOutEdgeForStartNode: '开始节点必须有出线',
|
||||
mustOneInEdgeForEndNode: '结束节点必须有入线',
|
||||
approvalRecord: '审批记录',
|
||||
start: '开始',
|
||||
end: '结束',
|
||||
usertask: '用户任务',
|
||||
serial: '互斥网关',
|
||||
parallel: '并行网关',
|
||||
flowEdge: '流程线',
|
||||
|
||||
// procinst
|
||||
startProcess: '发起流程',
|
||||
cancelProcessConfirm: '确认取消该流程?',
|
||||
@@ -57,7 +78,7 @@ export default {
|
||||
selectRedisPlaceholder: '请选择Redis实例与库',
|
||||
cmdPlaceholder: `如: SET 'key' 'value'; 多条命令;分割`,
|
||||
// ProcinstStatusEnum
|
||||
active: '执行中',
|
||||
active: '审批中',
|
||||
completed: '完成',
|
||||
suspended: '挂起',
|
||||
terminated: '终止',
|
||||
@@ -83,10 +104,12 @@ export default {
|
||||
approveNode: '审批节点',
|
||||
approveForm: '审批表单',
|
||||
approveResult: '审批结果',
|
||||
approvalRemark: '审批意见',
|
||||
approver: '审批人',
|
||||
audit: '审核',
|
||||
procinstStatus: '流程状态',
|
||||
taskStatus: '任务状态',
|
||||
taskName: '当前节点',
|
||||
taskName: '任务名',
|
||||
taskBeginTime: '开始时间',
|
||||
flowAudit: '流程审批',
|
||||
notify: '通知',
|
||||
|
||||
@@ -9,6 +9,7 @@ export default {
|
||||
tagTips3: '3. 拥有父标签的团队成员可访问操作其自身或子标签关联的资源',
|
||||
machine: '机器',
|
||||
db: '数据库',
|
||||
es: 'ES',
|
||||
code: '编号',
|
||||
createSubTag: '创建子标签',
|
||||
createSubTagTitle: '创建【{codePath}】的子标签',
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
</el-scrollbar>
|
||||
</el-aside>
|
||||
<el-drawer v-model="themeConfig.isCollapse" :with-header="false" direction="ltr" size="220px" v-else>
|
||||
<el-aside class="layout-aside w100 h100">
|
||||
<el-aside class="layout-aside !w-full !h-full">
|
||||
<Logo v-if="setShowLogo" />
|
||||
<el-scrollbar class="flex-auto" ref="layoutAsideScrollbarRef">
|
||||
<Vertical :menuList="state.menuList" />
|
||||
|
||||
@@ -2,25 +2,29 @@
|
||||
<div class="layout-columns-aside">
|
||||
<el-scrollbar>
|
||||
<ul>
|
||||
<li v-for="(v, k) in state.columnsAsideList" :key="k" @click="onColumnsAsideMenuClick(v, k)" :ref="(el) => {
|
||||
if (el) columnsAsideOffsetTopRefs[k] = el;
|
||||
}
|
||||
" :class="{ 'layout-columns-active': state.liIndex === k }" :title="$t(v.meta.title)">
|
||||
<div class="layout-columns-aside-li-box"
|
||||
v-if="!v.meta.link || (v.meta.link && v.meta.linkType == 1)">
|
||||
<li
|
||||
v-for="(v, k) in state.columnsAsideList"
|
||||
:key="k"
|
||||
@click="onColumnsAsideMenuClick(v, k)"
|
||||
:ref="
|
||||
(el) => {
|
||||
if (el) columnsAsideOffsetTopRefs[k] = el;
|
||||
}
|
||||
"
|
||||
:class="{ 'layout-columns-active': state.liIndex === k }"
|
||||
:title="$t(v.meta.title)"
|
||||
>
|
||||
<div class="layout-columns-aside-li-box" v-if="!v.meta.link || (v.meta.link && v.meta.linkType == 1)">
|
||||
<i :class="v.meta.icon"></i>
|
||||
<div class="layout-columns-aside-li-box-title font12">
|
||||
{{ $t(v.meta.title) && $t(v.meta.title).length >= 4 ? $t(v.meta.title).substr(0, 4) :
|
||||
$t(v.meta.title) }}
|
||||
<div class="layout-columns-aside-li-box-title !text-[12px]">
|
||||
{{ $t(v.meta.title) && $t(v.meta.title).length >= 4 ? $t(v.meta.title).substr(0, 4) : $t(v.meta.title) }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="layout-columns-aside-li-box" v-else>
|
||||
<a :href="v.meta.link" target="_blank">
|
||||
<i :class="v.meta.icon"></i>
|
||||
<div class="layout-columns-aside-li-box-title font12">
|
||||
{{ $t(v.meta.title) && $t(v.meta.title).length >= 4 ? $t(v.meta.title).substr(0, 4) :
|
||||
$t(v.meta.title)
|
||||
}}
|
||||
<div class="layout-columns-aside-li-box-title !text-[12px]">
|
||||
{{ $t(v.meta.title) && $t(v.meta.title).length >= 4 ? $t(v.meta.title).substr(0, 4) : $t(v.meta.title) }}
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
<template>
|
||||
<el-main class="layout-main">
|
||||
<el-scrollbar ref="layoutScrollbarRef" v-show="!state.currentRouteMeta.link && state.currentRouteMeta.linkType != 1">
|
||||
<el-main class="layout-main !h-full">
|
||||
<el-scrollbar ref="layoutScrollbarRef" view-class="!h-full" v-show="!state.currentRouteMeta.link && state.currentRouteMeta.linkType != 1">
|
||||
<LayoutParentView />
|
||||
</el-scrollbar>
|
||||
|
||||
<Link class="h100" :meta="state.currentRouteMeta" v-if="state.currentRouteMeta.link && state.currentRouteMeta.linkType == 2" />
|
||||
<Link class="!h-full" :meta="state.currentRouteMeta" v-if="state.currentRouteMeta.link && state.currentRouteMeta.linkType == 2" />
|
||||
|
||||
<Iframes
|
||||
class="h100"
|
||||
class="!h-full"
|
||||
:meta="state.currentRouteMeta"
|
||||
v-if="state.currentRouteMeta.link && state.currentRouteMeta.linkType == 1 && state.isShowLink"
|
||||
@getCurrentRouteMeta="onGetCurrentRouteMeta"
|
||||
@@ -33,7 +33,6 @@ const { proxy } = getCurrentInstance() as any;
|
||||
const { themeConfig } = storeToRefs(useThemeConfig());
|
||||
const route = useRoute();
|
||||
const state = reactive({
|
||||
headerHeight: '',
|
||||
currentRouteMeta: {} as any,
|
||||
isShowLink: false,
|
||||
});
|
||||
@@ -50,20 +49,12 @@ const initCurrentRouteMeta = (meta: object) => {
|
||||
state.isShowLink = true;
|
||||
}, 100);
|
||||
};
|
||||
// 设置 main 的高度
|
||||
const initHeaderHeight = () => {
|
||||
let { isTagsview } = themeConfig.value;
|
||||
if (isTagsview) return (state.headerHeight = `77px`);
|
||||
else return (state.headerHeight = `50px`);
|
||||
};
|
||||
// 页面加载前
|
||||
onBeforeMount(() => {
|
||||
initCurrentRouteMeta(route.meta);
|
||||
initHeaderHeight();
|
||||
});
|
||||
// 监听 themeConfig 配置文件的变化,更新菜单 el-scrollbar 的高度
|
||||
watch(themeConfig.value, (val) => {
|
||||
state.headerHeight = val.isTagsview ? '77px' : '50px';
|
||||
if (val.isFixedHeaderChange !== val.isFixedHeader) {
|
||||
if (!proxy.$refs.layoutScrollbarRef) return false;
|
||||
proxy.$refs.layoutScrollbarRef.update();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<el-container class="layout-container flex-center">
|
||||
<Header />
|
||||
<el-container class="layout-mian-height-50">
|
||||
<el-container class="flex-1 overflow-auto">
|
||||
<Aside />
|
||||
<div class="flex-center layout-backtop">
|
||||
<TagsView v-if="themeConfig.isTagsview" />
|
||||
|
||||
@@ -5,10 +5,8 @@
|
||||
<Aside />
|
||||
<el-container class="flex-center layout-backtop">
|
||||
<Header v-if="isFixedHeader" />
|
||||
<el-scrollbar>
|
||||
<Header v-if="!isFixedHeader" />
|
||||
<Main />
|
||||
</el-scrollbar>
|
||||
<Header v-if="!isFixedHeader" />
|
||||
<Main />
|
||||
</el-container>
|
||||
</div>
|
||||
<el-backtop target=".layout-backtop .el-scrollbar__wrap"></el-backtop>
|
||||
|
||||
@@ -3,10 +3,8 @@
|
||||
<Aside />
|
||||
<el-container class="flex-center layout-backtop">
|
||||
<Header v-if="isFixedHeader" />
|
||||
<el-scrollbar ref="layoutDefaultsScrollbarRef">
|
||||
<Header v-if="!isFixedHeader" />
|
||||
<Main />
|
||||
</el-scrollbar>
|
||||
<Header v-if="!isFixedHeader" />
|
||||
<Main />
|
||||
</el-container>
|
||||
<el-backtop target=".layout-backtop .el-scrollbar__wrap"></el-backtop>
|
||||
</el-container>
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
</el-icon>
|
||||
</template>
|
||||
<template #default="{ item }">
|
||||
<div><SvgIcon :name="item.meta.icon" class="mr5" />{{ $t(item.meta.title) }}</div>
|
||||
<div><SvgIcon :name="item.meta.icon" class="mr-1" />{{ $t(item.meta.title) }}</div>
|
||||
</template>
|
||||
</el-autocomplete>
|
||||
</el-dialog>
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<template v-if="themeConfig.terminalTheme == 'custom'">
|
||||
<div class="layout-breadcrumb-seting-bar-flex mt10">
|
||||
<div class="layout-breadcrumb-seting-bar-flex !mt-2">
|
||||
<div class="layout-breadcrumb-seting-bar-flex-label">{{ $t('layout.config.fontColor') }}</div>
|
||||
<div class="layout-breadcrumb-seting-bar-flex-value">
|
||||
<el-color-picker v-model="themeConfig.terminalForeground" size="small" @change="onColorPickerChange('terminalForeground')">
|
||||
@@ -37,14 +37,14 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div class="layout-breadcrumb-seting-bar-flex mt10">
|
||||
<div class="layout-breadcrumb-seting-bar-flex !mt-2">
|
||||
<div class="layout-breadcrumb-seting-bar-flex-label">{{ $t('layout.config.fontSize') }}</div>
|
||||
<div class="layout-breadcrumb-seting-bar-flex-value">
|
||||
<el-input-number v-model="themeConfig.terminalFontSize" controls-position="right" :min="12" :max="24" size="small" style="width: 90px">
|
||||
</el-input-number>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layout-breadcrumb-seting-bar-flex mt10">
|
||||
<div class="layout-breadcrumb-seting-bar-flex !mt-2">
|
||||
<div class="layout-breadcrumb-seting-bar-flex-label">{{ $t('layout.config.fontWeight') }}</div>
|
||||
<div class="layout-breadcrumb-seting-bar-flex-value">
|
||||
<el-select v-model="themeConfig.terminalFontWeight" size="small" style="width: 90px">
|
||||
@@ -68,7 +68,7 @@
|
||||
|
||||
<!-- 全局设置 -->
|
||||
<el-divider content-position="left">{{ $t('layout.config.globalSetting') }}</el-divider>
|
||||
<div class="layout-breadcrumb-seting-bar-flex mt15">
|
||||
<div class="layout-breadcrumb-seting-bar-flex !mt-3.5">
|
||||
<div class="layout-breadcrumb-seting-bar-flex-label">{{ $t('layout.config.pagesize') }}</div>
|
||||
<div class="layout-breadcrumb-seting-bar-flex-value">
|
||||
<el-input-number
|
||||
@@ -185,7 +185,7 @@
|
||||
</el-color-picker>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layout-breadcrumb-seting-bar-flex mt10">
|
||||
<div class="layout-breadcrumb-seting-bar-flex !mt-2">
|
||||
<div class="layout-breadcrumb-seting-bar-flex-label">顶栏背景渐变</div>
|
||||
<div class="layout-breadcrumb-seting-bar-flex-value">
|
||||
<el-switch v-model="themeConfig.isTopBarColorGradual"
|
||||
@@ -211,7 +211,7 @@
|
||||
<el-switch v-model="themeConfig.isCollapse" @change="onThemeConfigChange"></el-switch>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layout-breadcrumb-seting-bar-flex mt15">
|
||||
<div class="layout-breadcrumb-seting-bar-flex !mt-3.5">
|
||||
<div class="layout-breadcrumb-seting-bar-flex-label">
|
||||
{{ $t('layout.config.isUniqueOpened') }}
|
||||
</div>
|
||||
@@ -219,7 +219,7 @@
|
||||
<el-switch v-model="themeConfig.isUniqueOpened"></el-switch>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layout-breadcrumb-seting-bar-flex mt15">
|
||||
<div class="layout-breadcrumb-seting-bar-flex !mt-3.5">
|
||||
<div class="layout-breadcrumb-seting-bar-flex-label">
|
||||
{{ $t('layout.config.isFixedHeader') }}
|
||||
</div>
|
||||
@@ -227,7 +227,7 @@
|
||||
<el-switch v-model="themeConfig.isFixedHeader" @change="onIsFixedHeaderChange"></el-switch>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layout-breadcrumb-seting-bar-flex mt15" :style="{ opacity: themeConfig.layout !== 'classic' ? 0.5 : 1 }">
|
||||
<div class="layout-breadcrumb-seting-bar-flex !mt-3.5" :style="{ opacity: themeConfig.layout !== 'classic' ? 0.5 : 1 }">
|
||||
<div class="layout-breadcrumb-seting-bar-flex-label">
|
||||
{{ $t('layout.config.isClassicSplitMenu') }}
|
||||
</div>
|
||||
@@ -236,7 +236,7 @@
|
||||
</el-switch>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layout-breadcrumb-seting-bar-flex mt15">
|
||||
<div class="layout-breadcrumb-seting-bar-flex !mt-3.5">
|
||||
<div class="layout-breadcrumb-seting-bar-flex-label">
|
||||
{{ $t('layout.config.isLockScreen') }}
|
||||
</div>
|
||||
@@ -256,13 +256,13 @@
|
||||
|
||||
<!-- 界面显示 -->
|
||||
<el-divider content-position="left">{{ $t('layout.config.interfaceDisplay') }}</el-divider>
|
||||
<div class="layout-breadcrumb-seting-bar-flex mt15">
|
||||
<div class="layout-breadcrumb-seting-bar-flex !mt-3.5">
|
||||
<div class="layout-breadcrumb-seting-bar-flex-label">{{ $t('layout.config.isShowLogo') }}</div>
|
||||
<div class="layout-breadcrumb-seting-bar-flex-value">
|
||||
<el-switch v-model="themeConfig.isShowLogo" @change="onIsShowLogoChange"></el-switch>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layout-breadcrumb-seting-bar-flex mt15" :style="{ opacity: themeConfig.layout === 'transverse' ? 0.5 : 1 }">
|
||||
<div class="layout-breadcrumb-seting-bar-flex !mt-3.5" :style="{ opacity: themeConfig.layout === 'transverse' ? 0.5 : 1 }">
|
||||
<div class="layout-breadcrumb-seting-bar-flex-label">
|
||||
{{ $t('layout.config.isBreadcrumb') }}
|
||||
</div>
|
||||
@@ -274,7 +274,7 @@
|
||||
></el-switch>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layout-breadcrumb-seting-bar-flex mt15">
|
||||
<div class="layout-breadcrumb-seting-bar-flex !mt-3.5">
|
||||
<div class="layout-breadcrumb-seting-bar-flex-label">
|
||||
{{ $t('layout.config.isBreadcrumbIcon') }}
|
||||
</div>
|
||||
@@ -282,13 +282,13 @@
|
||||
<el-switch v-model="themeConfig.isBreadcrumbIcon"></el-switch>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layout-breadcrumb-seting-bar-flex mt15">
|
||||
<div class="layout-breadcrumb-seting-bar-flex !mt-3.5">
|
||||
<div class="layout-breadcrumb-seting-bar-flex-label">{{ $t('layout.config.isTagsview') }}</div>
|
||||
<div class="layout-breadcrumb-seting-bar-flex-value">
|
||||
<el-switch v-model="themeConfig.isTagsview"></el-switch>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layout-breadcrumb-seting-bar-flex mt15">
|
||||
<div class="layout-breadcrumb-seting-bar-flex !mt-3.5">
|
||||
<div class="layout-breadcrumb-seting-bar-flex-label">
|
||||
{{ $t('layout.config.isTagsviewIcon') }}
|
||||
</div>
|
||||
@@ -296,7 +296,7 @@
|
||||
<el-switch v-model="themeConfig.isTagsviewIcon"></el-switch>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layout-breadcrumb-seting-bar-flex mt15">
|
||||
<div class="layout-breadcrumb-seting-bar-flex !mt-3.5">
|
||||
<div class="layout-breadcrumb-seting-bar-flex-label">
|
||||
{{ $t('layout.config.isCacheTagsView') }}
|
||||
</div>
|
||||
@@ -304,7 +304,7 @@
|
||||
<el-switch v-model="themeConfig.isCacheTagsView"></el-switch>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layout-breadcrumb-seting-bar-flex mt15">
|
||||
<div class="layout-breadcrumb-seting-bar-flex !mt-3.5">
|
||||
<div class="layout-breadcrumb-seting-bar-flex-label">
|
||||
{{ $t('layout.config.isSortableTagsView') }}
|
||||
</div>
|
||||
@@ -312,19 +312,19 @@
|
||||
<el-switch v-model="themeConfig.isSortableTagsView" @change="onSortableTagsViewChange"></el-switch>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layout-breadcrumb-seting-bar-flex mt15">
|
||||
<div class="layout-breadcrumb-seting-bar-flex !mt-3.5">
|
||||
<div class="layout-breadcrumb-seting-bar-flex-label">{{ $t('layout.config.isFooter') }}</div>
|
||||
<div class="layout-breadcrumb-seting-bar-flex-value">
|
||||
<el-switch v-model="themeConfig.isFooter"></el-switch>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layout-breadcrumb-seting-bar-flex mt15">
|
||||
<div class="layout-breadcrumb-seting-bar-flex !mt-3.5">
|
||||
<div class="layout-breadcrumb-seting-bar-flex-label">{{ $t('layout.config.isGrayscale') }}</div>
|
||||
<div class="layout-breadcrumb-seting-bar-flex-value">
|
||||
<el-switch v-model="themeConfig.isGrayscale" @change="onAddFilterChange('grayscale')"></el-switch>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layout-breadcrumb-seting-bar-flex mt15">
|
||||
<div class="layout-breadcrumb-seting-bar-flex !mt-3.5">
|
||||
<div class="layout-breadcrumb-seting-bar-flex-label">{{ $t('layout.config.isInvert') }}</div>
|
||||
<div class="layout-breadcrumb-seting-bar-flex-value">
|
||||
<el-switch v-model="themeConfig.isInvert" @change="onAddFilterChange('invert')"></el-switch>
|
||||
@@ -333,7 +333,7 @@
|
||||
|
||||
<!-- 其它设置 -->
|
||||
<el-divider content-position="left">{{ $t('layout.config.otherSetting') }}</el-divider>
|
||||
<div class="layout-breadcrumb-seting-bar-flex mt15">
|
||||
<div class="layout-breadcrumb-seting-bar-flex !mt-3.5">
|
||||
<div class="layout-breadcrumb-seting-bar-flex-label">{{ $t('layout.config.tagsStyle') }}</div>
|
||||
<div class="layout-breadcrumb-seting-bar-flex-value">
|
||||
<el-select v-model="themeConfig.tagsStyle" placeholder="请选择" size="small" style="width: 90px">
|
||||
@@ -343,7 +343,7 @@
|
||||
</el-select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layout-breadcrumb-seting-bar-flex mt15">
|
||||
<div class="layout-breadcrumb-seting-bar-flex !mt-3.5">
|
||||
<div class="layout-breadcrumb-seting-bar-flex-label">{{ $t('layout.config.animation') }}</div>
|
||||
<div class="layout-breadcrumb-seting-bar-flex-value">
|
||||
<el-select v-model="themeConfig.animation" size="small" style="width: 90px">
|
||||
@@ -353,7 +353,7 @@
|
||||
</el-select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layout-breadcrumb-seting-bar-flex mt15 mb28">
|
||||
<div class="layout-breadcrumb-seting-bar-flex !mt-3.5 !mb-5.5">
|
||||
<div class="layout-breadcrumb-seting-bar-flex-label">
|
||||
{{ $t('layout.config.columnsAsideStyle') }}
|
||||
</div>
|
||||
|
||||
@@ -56,13 +56,13 @@
|
||||
</transition>
|
||||
</el-popover>
|
||||
</div>
|
||||
<div class="layout-navbars-breadcrumb-user-icon mr10" @click="onScreenfullClick">
|
||||
<div class="layout-navbars-breadcrumb-user-icon mr-2" @click="onScreenfullClick">
|
||||
<SvgIcon v-if="!state.isScreenfull" name="full-screen" :title="$t('layout.user.fullScreenOff')" />
|
||||
<SvgIcon v-else name="crop" />
|
||||
</div>
|
||||
<el-dropdown trigger="click" :show-timeout="70" :hide-timeout="50" @command="onHandleCommandClick">
|
||||
<span class="layout-navbars-breadcrumb-user-link" style="cursor: pointer">
|
||||
<img :src="userInfo.photo" class="layout-navbars-breadcrumb-user-link-photo mr5" />
|
||||
<span class="layout-navbars-breadcrumb-user-link cursor-pointer">
|
||||
<img :src="userInfo.photo" class="layout-navbars-breadcrumb-user-link-photo mr-1" />
|
||||
{{ userInfo.name || userInfo.username }}
|
||||
<i class="el-icon-arrow-down el-icon--right"></i>
|
||||
</span>
|
||||
|
||||
@@ -16,18 +16,17 @@
|
||||
}
|
||||
"
|
||||
>
|
||||
<SvgIcon name="icon layout/tag-view-active" class="layout-navbars-tagsview-ul-li-iconfont font14" v-if="isActive(v)" />
|
||||
<SvgIcon :name="v.icon" class="layout-navbars-tagsview-ul-li-iconfont" v-if="!isActive(v) && themeConfig.isTagsviewIcon" />
|
||||
<SvgIcon :name="v.icon" class="layout-navbars-tagsview-ul-li-iconfont" v-if="themeConfig.isTagsviewIcon" />
|
||||
<span>{{ $t(v.title) }}</span>
|
||||
<template v-if="isActive(v)">
|
||||
<SvgIcon
|
||||
name="RefreshRight"
|
||||
class="font14 ml5 layout-navbars-tagsview-ul-li-refresh"
|
||||
class="!text-[14px] ml-1 layout-navbars-tagsview-ul-li-refresh"
|
||||
@click.stop="refreshCurrentTagsView($route.fullPath)"
|
||||
/>
|
||||
<SvgIcon
|
||||
name="Close"
|
||||
class="font14 layout-navbars-tagsview-ul-li-icon layout-icon-active"
|
||||
class="!text-[14px] layout-navbars-tagsview-ul-li-icon layout-icon-active"
|
||||
v-if="!v.isAffix"
|
||||
@click.stop="closeCurrentTagsView(themeConfig.isShareTagsView ? v.path : v.path)"
|
||||
/>
|
||||
@@ -35,7 +34,7 @@
|
||||
|
||||
<SvgIcon
|
||||
name="Close"
|
||||
class="font14 layout-navbars-tagsview-ul-li-icon layout-icon-three"
|
||||
class="!text-[14px] layout-navbars-tagsview-ul-li-icon layout-icon-three"
|
||||
v-if="!v.isAffix"
|
||||
@click.stop="closeCurrentTagsView(themeConfig.isShareTagsView ? v.path : v.path)"
|
||||
/>
|
||||
@@ -521,7 +520,8 @@ onBeforeRouteUpdate((to) => {
|
||||
align-items: flex-end;
|
||||
|
||||
.tgs-style-three-svg {
|
||||
-webkit-mask-image: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNzAiIGhlaWdodD0iNzAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgZmlsbD0ibm9uZSI+CgogPGc+CiAgPHRpdGxlPkxheWVyIDE8L3RpdGxlPgogIDxwYXRoIHRyYW5zZm9ybT0icm90YXRlKC0wLjEzMzUwNiA1MC4xMTkyIDUwKSIgaWQ9InN2Z18xIiBkPSJtMTAwLjExOTE5LDEwMGMtNTUuMjI4LDAgLTEwMCwtNDQuNzcyIC0xMDAsLTEwMGwwLDEwMGwxMDAsMHoiIG9wYWNpdHk9InVuZGVmaW5lZCIgc3Ryb2tlPSJudWxsIiBmaWxsPSIjRjhFQUU3Ii8+CiAgPHBhdGggZD0ibS0wLjYzNzY2LDcuMzEyMjhjMC4xMTkxOSwwIDAuMjE3MzcsMC4wNTc5NiAwLjQ3Njc2LDAuMTE5MTljMC4yMzIsMC4wNTQ3NyAwLjI3MzI5LDAuMDM0OTEgMC4zNTc1NywwLjExOTE5YzAuMDg0MjgsMC4wODQyOCAwLjM1NzU3LDAgMC40NzY3NiwwbDAuMTE5MTksMGwwLjIzODM4LDAiIGlkPSJzdmdfMiIgc3Ryb2tlPSJudWxsIiBmaWxsPSJub25lIi8+CiAgPHBhdGggZD0ibTI4LjkyMTM0LDY5LjA1MjQ0YzAsMC4xMTkxOSAwLDAuMjM4MzggMCwwLjM1NzU3bDAsMC4xMTkxOWwwLDAuMTE5MTkiIGlkPSJzdmdfMyIgc3Ryb2tlPSJudWxsIiBmaWxsPSJub25lIi8+CiAgPHJlY3QgaWQ9InN2Z180IiBoZWlnaHQ9IjAiIHdpZHRoPSIxLjMxMTA4IiB5PSI2LjgzNTUyIiB4PSItMC4wNDE3MSIgc3Ryb2tlPSJudWxsIiBmaWxsPSJub25lIi8+CiAgPHJlY3QgaWQ9InN2Z181IiBoZWlnaHQ9IjEuNzg3ODQiIHdpZHRoPSIwLjExOTE5IiB5PSI2OC40NTY1IiB4PSIyOC45MjEzNCIgc3Ryb2tlPSJudWxsIiBmaWxsPSJub25lIi8+CiAgPHJlY3QgaWQ9InN2Z182IiBoZWlnaHQ9IjQuODg2NzciIHdpZHRoPSIxOS4wNzAzMiIgeT0iNTEuMjkzMjEiIHg9IjM2LjY2ODY2IiBzdHJva2U9Im51bGwiIGZpbGw9Im5vbmUiLz4KIDwvZz4KPC9zdmc+'),
|
||||
-webkit-mask-image:
|
||||
url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNzAiIGhlaWdodD0iNzAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgZmlsbD0ibm9uZSI+CgogPGc+CiAgPHRpdGxlPkxheWVyIDE8L3RpdGxlPgogIDxwYXRoIHRyYW5zZm9ybT0icm90YXRlKC0wLjEzMzUwNiA1MC4xMTkyIDUwKSIgaWQ9InN2Z18xIiBkPSJtMTAwLjExOTE5LDEwMGMtNTUuMjI4LDAgLTEwMCwtNDQuNzcyIC0xMDAsLTEwMGwwLDEwMGwxMDAsMHoiIG9wYWNpdHk9InVuZGVmaW5lZCIgc3Ryb2tlPSJudWxsIiBmaWxsPSIjRjhFQUU3Ii8+CiAgPHBhdGggZD0ibS0wLjYzNzY2LDcuMzEyMjhjMC4xMTkxOSwwIDAuMjE3MzcsMC4wNTc5NiAwLjQ3Njc2LDAuMTE5MTljMC4yMzIsMC4wNTQ3NyAwLjI3MzI5LDAuMDM0OTEgMC4zNTc1NywwLjExOTE5YzAuMDg0MjgsMC4wODQyOCAwLjM1NzU3LDAgMC40NzY3NiwwbDAuMTE5MTksMGwwLjIzODM4LDAiIGlkPSJzdmdfMiIgc3Ryb2tlPSJudWxsIiBmaWxsPSJub25lIi8+CiAgPHBhdGggZD0ibTI4LjkyMTM0LDY5LjA1MjQ0YzAsMC4xMTkxOSAwLDAuMjM4MzggMCwwLjM1NzU3bDAsMC4xMTkxOWwwLDAuMTE5MTkiIGlkPSJzdmdfMyIgc3Ryb2tlPSJudWxsIiBmaWxsPSJub25lIi8+CiAgPHJlY3QgaWQ9InN2Z180IiBoZWlnaHQ9IjAiIHdpZHRoPSIxLjMxMTA4IiB5PSI2LjgzNTUyIiB4PSItMC4wNDE3MSIgc3Ryb2tlPSJudWxsIiBmaWxsPSJub25lIi8+CiAgPHJlY3QgaWQ9InN2Z181IiBoZWlnaHQ9IjEuNzg3ODQiIHdpZHRoPSIwLjExOTE5IiB5PSI2OC40NTY1IiB4PSIyOC45MjEzNCIgc3Ryb2tlPSJudWxsIiBmaWxsPSJub25lIi8+CiAgPHJlY3QgaWQ9InN2Z182IiBoZWlnaHQ9IjQuODg2NzciIHdpZHRoPSIxOS4wNzAzMiIgeT0iNTEuMjkzMjEiIHg9IjM2LjY2ODY2IiBzdHJva2U9Im51bGwiIGZpbGw9Im5vbmUiLz4KIDwvZz4KPC9zdmc+'),
|
||||
url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNzAiIGhlaWdodD0iNzAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgZmlsbD0ibm9uZSI+CiA8Zz4KICA8dGl0bGU+TGF5ZXIgMTwvdGl0bGU+CiAgPHBhdGggdHJhbnNmb3JtPSJyb3RhdGUoLTg5Ljc2MjQgNy4zMzAxNCA1NS4xMjUyKSIgc3Ryb2tlPSJudWxsIiBpZD0ic3ZnXzEiIGZpbGw9IiNGOEVBRTciIGQ9Im02Mi41NzQ0OSwxMTcuNTIwODZjLTU1LjIyOCwwIC0xMDAsLTQ0Ljc3MiAtMTAwLC0xMDBsMCwxMDBsMTAwLDB6IiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPgogIDxwYXRoIGQ9Im0tMC42Mzc2Niw3LjMxMjI4YzAuMTE5MTksMCAwLjIxNzM3LDAuMDU3OTYgMC40NzY3NiwwLjExOTE5YzAuMjMyLDAuMDU0NzcgMC4yNzMyOSwwLjAzNDkxIDAuMzU3NTcsMC4xMTkxOWMwLjA4NDI4LDAuMDg0MjggMC4zNTc1NywwIDAuNDc2NzYsMGwwLjExOTE5LDBsMC4yMzgzOCwwIiBpZD0ic3ZnXzIiIHN0cm9rZT0ibnVsbCIgZmlsbD0ibm9uZSIvPgogIDxwYXRoIGQ9Im0yOC45MjEzNCw2OS4wNTI0NGMwLDAuMTE5MTkgMCwwLjIzODM4IDAsMC4zNTc1N2wwLDAuMTE5MTlsMCwwLjExOTE5IiBpZD0ic3ZnXzMiIHN0cm9rZT0ibnVsbCIgZmlsbD0ibm9uZSIvPgogIDxyZWN0IGlkPSJzdmdfNCIgaGVpZ2h0PSIwIiB3aWR0aD0iMS4zMTEwOCIgeT0iNi44MzU1MiIgeD0iLTAuMDQxNzEiIHN0cm9rZT0ibnVsbCIgZmlsbD0ibm9uZSIvPgogIDxyZWN0IGlkPSJzdmdfNSIgaGVpZ2h0PSIxLjc4Nzg0IiB3aWR0aD0iMC4xMTkxOSIgeT0iNjguNDU2NSIgeD0iMjguOTIxMzQiIHN0cm9rZT0ibnVsbCIgZmlsbD0ibm9uZSIvPgogIDxyZWN0IGlkPSJzdmdfNiIgaGVpZ2h0PSI0Ljg4Njc3IiB3aWR0aD0iMTkuMDcwMzIiIHk9IjUxLjI5MzIxIiB4PSIzNi42Njg2NiIgc3Ryb2tlPSJudWxsIiBmaWxsPSJub25lIi8+CiA8L2c+Cjwvc3ZnPg=='),
|
||||
url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg'><rect rx='8' width='100%' height='100%' fill='%23F8EAE7'/></svg>");
|
||||
-webkit-mask-size:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="layout-view-bg-white flex h100" v-loading="iframeLoading">
|
||||
<div class="layout-view-bg-white flex !h-full" v-loading="iframeLoading">
|
||||
<iframe :src="iframeUrl" frameborder="0" height="100%" width="100%" id="iframe" v-show="!iframeLoading"></iframe>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -9,12 +9,12 @@ import { registElSvgIcon } from '@/common/utils/svgIcons';
|
||||
import ElementPlus from 'element-plus';
|
||||
import 'element-plus/dist/index.css';
|
||||
import 'element-plus/theme-chalk/dark/css-vars.css';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import { i18n } from '@/i18n/index';
|
||||
|
||||
import 'splitpanes/dist/splitpanes.css';
|
||||
|
||||
import '@/theme/index.scss';
|
||||
import '@/theme/tailwind.css';
|
||||
import '@/assets/font/font.css';
|
||||
import '@/assets/icon/icon.js';
|
||||
import { getThemeConfig } from './common/utils/storage';
|
||||
@@ -30,12 +30,3 @@ app.use(pinia).use(router).use(i18n).use(ElementPlus, { size: getThemeConfig()?.
|
||||
|
||||
// 屏蔽警告信息
|
||||
app.config.warnHandler = () => null;
|
||||
// 全局error处理
|
||||
app.config.errorHandler = function (err: any, vm, info) {
|
||||
// 如果是断言错误,则进行提示即可
|
||||
if (err.name == 'AssertError') {
|
||||
ElMessage.error(err.message);
|
||||
} else {
|
||||
console.error(err, info);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -112,7 +112,7 @@ export const useThemeConfig = defineStore('themeConfig', {
|
||||
/* 布局切换
|
||||
------------------------------- */
|
||||
// 默认布局,可选 1、默认 defaults 2、经典 classic 3、横向 transverse 4、分栏 columns
|
||||
layout: 'classic',
|
||||
layout: 'transverse',
|
||||
|
||||
terminalTheme: 'light',
|
||||
// ssh终端字体颜色
|
||||
@@ -154,14 +154,14 @@ export const useThemeConfig = defineStore('themeConfig', {
|
||||
initThemeConfig() {
|
||||
// 获取缓存中的布局配置
|
||||
const tc = getThemeConfig();
|
||||
|
||||
|
||||
if (tc) {
|
||||
this.themeConfig = tc;
|
||||
document.documentElement.style.cssText = getLocal('themeConfigStyle');
|
||||
} else {
|
||||
getServerConf().then((res) => {
|
||||
this.themeConfig.globalI18n = res.i18n;
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
// 根据后台系统配置初始化
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
/* 初始化样式
|
||||
------------------------------- */
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
outline: none !important;
|
||||
}
|
||||
|
||||
:root {
|
||||
--color-white: #ffffff;
|
||||
@@ -40,13 +34,12 @@ body,
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
background-color: var(--bg-main-color);
|
||||
font-size: 14px;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* 主布局样式
|
||||
------------------------------- */
|
||||
.layout-container {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
@@ -108,10 +101,6 @@ body,
|
||||
transition: width 0.3s ease;
|
||||
}
|
||||
|
||||
.layout-mian-height-50 {
|
||||
height: calc(100vh - 50px);
|
||||
}
|
||||
|
||||
.layout-columns-warp {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
@@ -191,45 +180,12 @@ body,
|
||||
}
|
||||
}
|
||||
|
||||
.flex-justify-between {
|
||||
display: flex;
|
||||
align-items: center; // 垂直方向水平居中
|
||||
justify-content: space-between; // 使第一个子元素靠近父容器的起始位置,最后一个子元素靠近终止位置,而其他子元素均匀分布在它们之间
|
||||
}
|
||||
|
||||
.flex-all-center {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.flex-align-center {
|
||||
display: flex;
|
||||
align-items: center; // 垂直方向水平居中
|
||||
}
|
||||
|
||||
/* 宽高 100%
|
||||
------------------------------- */
|
||||
.w100 {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
.h100 {
|
||||
height: 100% !important;
|
||||
}
|
||||
|
||||
.vh100 {
|
||||
height: 100vh !important;
|
||||
}
|
||||
|
||||
.max100vh {
|
||||
max-height: 100vh !important;
|
||||
}
|
||||
|
||||
.min100vh {
|
||||
min-height: 100vh !important;
|
||||
}
|
||||
|
||||
/* 颜色值
|
||||
------------------------------- */
|
||||
.color-primary {
|
||||
@@ -252,54 +208,6 @@ body,
|
||||
color: var(--el-color-info);
|
||||
}
|
||||
|
||||
/* 字体大小全局样式
|
||||
------------------------------- */
|
||||
@for $i from 8 through 32 {
|
||||
.font#{$i} {
|
||||
font-size: #{$i}px !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* 外边距、内边距全局样式
|
||||
------------------------------- */
|
||||
@for $i from 1 through 35 {
|
||||
.mt#{$i} {
|
||||
margin-top: #{$i}px !important;
|
||||
}
|
||||
|
||||
.mr#{$i} {
|
||||
margin-right: #{$i}px !important;
|
||||
}
|
||||
|
||||
.mb#{$i} {
|
||||
margin-bottom: #{$i}px !important;
|
||||
}
|
||||
|
||||
.ml#{$i} {
|
||||
margin-left: #{$i}px !important;
|
||||
}
|
||||
|
||||
.pt#{$i} {
|
||||
padding-top: #{$i}px !important;
|
||||
}
|
||||
|
||||
.pr#{$i} {
|
||||
padding-right: #{$i}px !important;
|
||||
}
|
||||
|
||||
.pb#{$i} {
|
||||
padding-bottom: #{$i}px !important;
|
||||
}
|
||||
|
||||
.pl#{$i} {
|
||||
padding-left: #{$i}px !important;
|
||||
}
|
||||
|
||||
.pd#{$i} {
|
||||
padding: #{$i}px !important;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 4px;
|
||||
@@ -346,16 +254,6 @@ body,
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
/* 元素无法被选择 */
|
||||
.none-select {
|
||||
moz-user-select: -moz-none;
|
||||
-moz-user-select: none;
|
||||
-o-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
-webkit-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
/* custom card */
|
||||
.card {
|
||||
@@ -376,14 +274,6 @@ body,
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.fl {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.fr {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.search-form {
|
||||
.el-form-item {
|
||||
margin-bottom: 3px;
|
||||
@@ -394,10 +284,6 @@ body,
|
||||
z-index: inherit !important;
|
||||
}
|
||||
|
||||
.pointer {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.pointer-icon {
|
||||
cursor: pointer;
|
||||
transition: color 0.3s;
|
||||
@@ -406,42 +292,4 @@ body,
|
||||
.pointer-icon:hover {
|
||||
color: var(--el-color-primary);
|
||||
/* 鼠标移动到图标时的颜色 */
|
||||
}
|
||||
|
||||
|
||||
/** splitpanes **/
|
||||
.splitpanes.default-theme .splitpanes {
|
||||
background: transparent !important;
|
||||
}
|
||||
|
||||
.splitpanes.default-theme .splitpanes__pane {
|
||||
background: transparent !important;
|
||||
}
|
||||
|
||||
.default-theme.splitpanes--vertical>.splitpanes__splitter {
|
||||
border-left: 1px solid var(--bg-main-color);
|
||||
}
|
||||
|
||||
.default-theme.splitpanes--horizontal>.splitpanes__splitter {
|
||||
border-top: 1px solid var(--bg-main-color);
|
||||
}
|
||||
|
||||
// 竖线样式
|
||||
.splitpanes.default-theme .splitpanes__splitter::before,
|
||||
.splitpanes.default-theme .splitpanes__splitter::after {
|
||||
background-color: var(--el-color-info-light-5);
|
||||
}
|
||||
|
||||
.splitpanes.default-theme .splitpanes__splitter:hover::before,
|
||||
.splitpanes.default-theme .splitpanes__splitter:hover::after {
|
||||
background-color: var(--el-color-success);
|
||||
}
|
||||
|
||||
.splitpanes.default-theme .splitpanes__splitter {
|
||||
min-width: 6px;
|
||||
background: var(--el-color-info-light-8) !important;
|
||||
}
|
||||
|
||||
.splitpanes.default-theme .splitpanes__splitter:hover {
|
||||
background: var(--el-color-success-light-8) !important;
|
||||
}
|
||||
@@ -1,29 +1,29 @@
|
||||
html.dark {
|
||||
// 变量(自定义时,只需修改这里的值)
|
||||
--next-bg-main: #1f1f1f;
|
||||
--next-color-white: #ffffff;
|
||||
--next-color-disabled: #191919;
|
||||
--next-color-bar: #dadada;
|
||||
--next-color-primary: #303030;
|
||||
--next-border-color: #424242;
|
||||
--next-border-black: #333333;
|
||||
--next-border-columns: #2a2a2a;
|
||||
--next-color-seting: #505050;
|
||||
--next-text-color-regular: #9b9da1;
|
||||
--next-text-color-placeholder: #7a7a7a;
|
||||
--next-color-hover: #3c3c3c;
|
||||
--next-color-hover-rgba: rgba(0, 0, 0, 0.3);
|
||||
--next-bg-main: #1f1f1f;
|
||||
--next-color-white: #ffffff;
|
||||
--next-color-disabled: #191919;
|
||||
--next-color-bar: #dadada;
|
||||
--next-color-primary: #303030;
|
||||
--next-border-color: #424242;
|
||||
--next-border-black: #333333;
|
||||
--next-border-columns: #2a2a2a;
|
||||
--next-color-seting: #505050;
|
||||
--next-text-color-regular: #9b9da1;
|
||||
--next-text-color-placeholder: #7a7a7a;
|
||||
--next-color-hover: #3c3c3c;
|
||||
--next-color-hover-rgba: rgba(0, 0, 0, 0.3);
|
||||
|
||||
/* 自定义深色背景颜色 */
|
||||
// root
|
||||
--bg-main-color: var(--next-bg-main) !important;
|
||||
--bg-topBar: var(--next-color-disabled) !important;
|
||||
--bg-topBarColor: var(--next-color-bar) !important;
|
||||
--bg-menuBar: var(--next-color-disabled) !important;
|
||||
--bg-menuBarColor: var(--next-color-bar) !important;
|
||||
--bg-menuBarActiveColor: var(--next-color-hover-rgba) !important;
|
||||
--bg-columnsMenuBar: var(--next-color-disabled) !important;
|
||||
--bg-columnsMenuBarColor: var(--next-color-bar) !important;
|
||||
--bg-main-color: var(--next-bg-main) !important;
|
||||
--bg-topBar: var(--next-color-disabled) !important;
|
||||
--bg-topBarColor: var(--next-color-bar) !important;
|
||||
--bg-menuBar: var(--next-color-disabled) !important;
|
||||
--bg-menuBarColor: var(--next-color-bar) !important;
|
||||
--bg-menuBarActiveColor: var(--next-color-hover-rgba) !important;
|
||||
--bg-columnsMenuBar: var(--next-color-disabled) !important;
|
||||
--bg-columnsMenuBarColor: var(--next-color-bar) !important;
|
||||
|
||||
--tagsview3-active-background-color: var(--next-color-hover);
|
||||
}
|
||||
}
|
||||
@@ -10,27 +10,6 @@
|
||||
max-height: 280px !important;
|
||||
}
|
||||
|
||||
/* Form 表单
|
||||
------------------------------- */
|
||||
// .el-form {
|
||||
|
||||
// // 修复行内表单最后一个 el-form-item 位置下移问题
|
||||
// &.el-form--inline {
|
||||
// .el-form-item--large.el-form-item:last-of-type {
|
||||
// margin-bottom: 22px !important;
|
||||
// }
|
||||
|
||||
// .el-form-item--default.el-form-item:last-of-type,
|
||||
// .el-form-item--small.el-form-item:last-of-type {
|
||||
// margin-bottom: 18px !important;
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
// .el-form-item .el-form-item__label .el-icon {
|
||||
// margin-right: 0px;
|
||||
// }
|
||||
// }
|
||||
|
||||
/* Alert 警告
|
||||
------------------------------- */
|
||||
@@ -52,6 +31,9 @@
|
||||
|
||||
/* NavMenu 导航菜单
|
||||
------------------------------- */
|
||||
$radius: 6px;
|
||||
$menuHeight: 46px !important;
|
||||
|
||||
// 鼠标 hover 时颜色
|
||||
.el-menu-hover-bg-color {
|
||||
background-color: var(--bg-menuBarActiveColor) !important;
|
||||
@@ -64,13 +46,15 @@
|
||||
}
|
||||
|
||||
.el-menu-item {
|
||||
height: 56px !important;
|
||||
line-height: 56px !important;
|
||||
height: $menuHeight;
|
||||
line-height: $menuHeight;
|
||||
border-radius: $radius;
|
||||
}
|
||||
|
||||
.el-menu-item,
|
||||
.el-sub-menu__title {
|
||||
color: var(--bg-menuBarColor);
|
||||
height: $menuHeight;
|
||||
}
|
||||
|
||||
// 修复点击左侧菜单折叠再展开时,宽度不跟随问题
|
||||
@@ -100,20 +84,18 @@
|
||||
.el-sub-menu.is-active .el-sub-menu__title,
|
||||
.el-sub-menu:not(.is-opened):hover .el-sub-menu__title {
|
||||
@extend .el-menu-hover-bg-color;
|
||||
border-radius: $radius;
|
||||
}
|
||||
|
||||
.el-menu-item:hover {
|
||||
@extend .el-menu-hover-bg-color;
|
||||
border-radius: $radius;
|
||||
}
|
||||
|
||||
.el-sub-menu.is-active.is-opened .el-sub-menu__title {
|
||||
background-color: unset !important;
|
||||
}
|
||||
|
||||
// 子级菜单背景颜色
|
||||
// .el-menu--inline {
|
||||
// background: var(--next-bg-menuBar-light-1);
|
||||
// }
|
||||
// 水平菜单、横向菜单折叠 a 标签
|
||||
.el-popper.is-dark a {
|
||||
color: var(--el-color-white) !important;
|
||||
@@ -238,33 +220,6 @@
|
||||
|
||||
/* Dialog 对话框
|
||||
------------------------------- */
|
||||
.el-overlay {
|
||||
overflow: hidden;
|
||||
|
||||
.el-overlay-dialog {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: unset !important;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
.el-dialog {
|
||||
margin: 0 auto !important;
|
||||
position: absolute;
|
||||
|
||||
.el-dialog__body {
|
||||
padding: 20px !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.el-dialog__body {
|
||||
max-height: calc(90vh - 111px) !important;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
/* Card 卡片
|
||||
------------------------------- */
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
@use './app.scss';
|
||||
@use './base.scss';
|
||||
@use './other.scss';
|
||||
@use './element.scss';
|
||||
@use './media/media.scss';
|
||||
@use './waves.scss';
|
||||
@use './dark.scss';
|
||||
@use './iconSelector.scss';
|
||||
@use './iconSelector.scss';
|
||||
@use './splitpanes.scss';
|
||||
@@ -1,31 +0,0 @@
|
||||
/* wangeditor富文本编辑器
|
||||
------------------------------- */
|
||||
.w-e-toolbar {
|
||||
border: 1px solid #ebeef5 !important;
|
||||
border-bottom: 1px solid #ebeef5 !important;
|
||||
border-top-left-radius: 3px;
|
||||
border-top-right-radius: 3px;
|
||||
z-index: 2 !important;
|
||||
}
|
||||
|
||||
.w-e-text-container {
|
||||
border: 1px solid #ebeef5 !important;
|
||||
border-top: none !important;
|
||||
border-bottom-left-radius: 3px;
|
||||
border-bottom-right-radius: 3px;
|
||||
z-index: 1 !important;
|
||||
}
|
||||
|
||||
/* web端自定义截屏
|
||||
------------------------------- */
|
||||
#screenShotContainer {
|
||||
z-index: 9998 !important;
|
||||
}
|
||||
|
||||
#toolPanel {
|
||||
height: 42px !important;
|
||||
}
|
||||
|
||||
#optionPanel {
|
||||
height: 37px !important;
|
||||
}
|
||||
36
frontend/src/theme/splitpanes.scss
Normal file
36
frontend/src/theme/splitpanes.scss
Normal file
@@ -0,0 +1,36 @@
|
||||
/** splitpanes **/
|
||||
.splitpanes.default-theme .splitpanes {
|
||||
background: transparent !important;
|
||||
}
|
||||
|
||||
.splitpanes.default-theme .splitpanes__pane {
|
||||
background: transparent !important;
|
||||
}
|
||||
|
||||
.default-theme.splitpanes--vertical>.splitpanes__splitter {
|
||||
border-left: 1px solid var(--bg-main-color);
|
||||
}
|
||||
|
||||
.default-theme.splitpanes--horizontal>.splitpanes__splitter {
|
||||
border-top: 1px solid var(--bg-main-color);
|
||||
}
|
||||
|
||||
// 竖线样式
|
||||
.splitpanes.default-theme .splitpanes__splitter::before,
|
||||
.splitpanes.default-theme .splitpanes__splitter::after {
|
||||
background-color: var(--el-color-info-light-5);
|
||||
}
|
||||
|
||||
.splitpanes.default-theme .splitpanes__splitter:hover::before,
|
||||
.splitpanes.default-theme .splitpanes__splitter:hover::after {
|
||||
background-color: var(--el-color-success);
|
||||
}
|
||||
|
||||
.splitpanes.default-theme .splitpanes__splitter {
|
||||
min-width: 6px;
|
||||
background: var(--el-color-info-light-8) !important;
|
||||
}
|
||||
|
||||
.splitpanes.default-theme .splitpanes__splitter:hover {
|
||||
background: var(--el-color-success-light-8) !important;
|
||||
}
|
||||
2
frontend/src/theme/tailwind.css
Normal file
2
frontend/src/theme/tailwind.css
Normal file
@@ -0,0 +1,2 @@
|
||||
|
||||
@import "tailwindcss";
|
||||
@@ -28,7 +28,7 @@
|
||||
<span v-if="flowProcdef || !state.form.procdefId">
|
||||
<el-divider content-position="left">{{ $t('flow.approvalNode') }}</el-divider>
|
||||
|
||||
<ProcdefTasks v-if="flowProcdef" :procdef="flowProcdef" />
|
||||
<FlowDesign height="300px" v-if="flowProcdef" :data="flowProcdef.flowDef" disabled center />
|
||||
|
||||
<el-result v-if="!state.form.procdefId" icon="error" :title="$t('flow.approvalNodeNotExist')" :sub-title="$t('flow.resourceNotExistFlow')">
|
||||
</el-result>
|
||||
@@ -51,10 +51,10 @@ import { ElMessage } from 'element-plus';
|
||||
import DrawerHeader from '@/components/drawer-header/DrawerHeader.vue';
|
||||
import { FlowBizType } from './enums';
|
||||
import EnumSelect from '@/components/enumselect/EnumSelect.vue';
|
||||
import ProcdefTasks from './components/ProcdefTasks.vue';
|
||||
import RedisRunCmdFlowBizForm from './flowbiz/redis/RedisRunCmdFlowBizForm.vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { Rules } from '@/common/rule';
|
||||
import FlowDesign from './components/flowdesign/FlowDesign.vue';
|
||||
|
||||
const DbSqlExecFlowBizForm = defineAsyncComponent(() => import('./flowbiz/dbms/DbSqlExecFlowBizForm.vue'));
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-drawer @open="initSort" :title="title" v-model="visible" :before-close="cancel" :destroy-on-close="true" :close-on-click-modal="false" size="40%">
|
||||
<el-drawer :title="title" v-model="visible" :before-close="onCancel" :destroy-on-close="true" :close-on-click-modal="false" size="40%">
|
||||
<template #header>
|
||||
<DrawerHeader :header="title" :back="cancel" />
|
||||
<DrawerHeader :header="title" :back="onCancel" />
|
||||
</template>
|
||||
|
||||
<el-form :model="form" ref="formRef" :rules="rules" label-width="auto">
|
||||
@@ -38,45 +38,12 @@
|
||||
<el-form-item ref="tagSelectRef" prop="codePaths" :label="$t('tag.relateTag')">
|
||||
<tag-tree-check height="300px" v-model="form.codePaths" :tag-type="[TagResourceTypePath.Db, TagResourceTypeEnum.Redis.value]" />
|
||||
</el-form-item>
|
||||
|
||||
<el-divider content-position="left">{{ $t('flow.approvalNode') }}</el-divider>
|
||||
|
||||
<el-table ref="taskTableRef" :data="tasks" row-key="taskKey" stripe style="width: 100%">
|
||||
<el-table-column prop="name" min-width="100px">
|
||||
<template #header>
|
||||
<el-button class="ml0" type="primary" circle size="small" icon="Plus" @click="addTask()"> </el-button>
|
||||
<span class="ml10">{{ $t('flow.nodeName') }}<span class="ml5" style="color: red">*</span></span>
|
||||
<el-tooltip :content="$t('flow.nodeNameTips')" placement="top">
|
||||
<SvgIcon class="ml5" name="question-filled" />
|
||||
</el-tooltip>
|
||||
</template>
|
||||
<template #default="scope">
|
||||
<el-input v-model="scope.row.name"> </el-input>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column prop="userId" min-width="150px" show-overflow-tooltip>
|
||||
<template #header>
|
||||
<span class="ml10">{{ $t('flow.auditor') }}<span class="ml5" style="color: red">*</span></span>
|
||||
</template>
|
||||
|
||||
<template #default="scope">
|
||||
<AccountSelectFormItem style="margin-bottom: 0px" v-model="scope.row.userId" label="" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column :label="$t('common.operation')" width="110px">
|
||||
<template #default="scope">
|
||||
<el-link @click="deleteTask(scope.$index)" class="ml5" type="danger" icon="delete" plain></el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-form>
|
||||
|
||||
<template #footer>
|
||||
<div>
|
||||
<el-button @click="cancel()">{{ $t('common.cancel') }}</el-button>
|
||||
<el-button type="primary" :loading="saveBtnLoading" @click="btnOk">{{ $t('common.confirm') }}</el-button>
|
||||
<el-button @click="onCancel()">{{ $t('common.cancel') }}</el-button>
|
||||
<el-button type="primary" :loading="saveBtnLoading" @click="onSave">{{ $t('common.confirm') }}</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-drawer>
|
||||
@@ -84,13 +51,9 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { toRefs, reactive, watch, ref, nextTick } from 'vue';
|
||||
import { toRefs, reactive, watch, ref } from 'vue';
|
||||
import { procdefApi } from './api';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import DrawerHeader from '@/components/drawer-header/DrawerHeader.vue';
|
||||
import AccountSelectFormItem from '@/views/system/account/components/AccountSelectFormItem.vue';
|
||||
import Sortable from 'sortablejs';
|
||||
import { randomUuid } from '../../common/utils/string';
|
||||
import { ProcdefStatus } from './enums';
|
||||
import TagTreeCheck from '../ops/component/TagTreeCheck.vue';
|
||||
import { TagResourceTypeEnum, TagResourceTypePath } from '@/common/commonEnum';
|
||||
@@ -118,7 +81,6 @@ const visible = defineModel<boolean>('visible', { default: false });
|
||||
const emit = defineEmits(['cancel', 'val-change']);
|
||||
|
||||
const formRef: any = ref(null);
|
||||
const taskTableRef: any = ref(null);
|
||||
|
||||
const rules = {
|
||||
name: [Rules.requiredInput('common.name')],
|
||||
@@ -135,14 +97,11 @@ const state = reactive({
|
||||
condition: '',
|
||||
remark: null,
|
||||
msgTmplId: null,
|
||||
// 流程的审批节点任务
|
||||
tasks: '',
|
||||
codePaths: [],
|
||||
},
|
||||
sortable: '' as any,
|
||||
});
|
||||
|
||||
const { form, tasks } = toRefs(state);
|
||||
const { form } = toRefs(state);
|
||||
|
||||
const { isFetching: saveBtnLoading, execute: saveFlowDefExec } = procdefApi.save.useApi(form);
|
||||
|
||||
@@ -150,11 +109,6 @@ watch(props, async (newValue: any) => {
|
||||
if (newValue.data) {
|
||||
state.form = await procdefApi.detail.request({ id: newValue.data.id });
|
||||
state.form.codePaths = newValue.data.tags?.map((tag: any) => tag.codePath);
|
||||
const tasks = JSON.parse(state.form.tasks);
|
||||
tasks.forEach((t: any) => {
|
||||
t.userId = Number.parseInt(t.userId);
|
||||
});
|
||||
state.tasks = tasks;
|
||||
} else {
|
||||
state.form = { status: ProcdefStatus.Enable.value } as any;
|
||||
state.form.condition = t('flow.conditionDefault');
|
||||
@@ -162,37 +116,8 @@ watch(props, async (newValue: any) => {
|
||||
}
|
||||
});
|
||||
|
||||
const initSort = () => {
|
||||
nextTick(() => {
|
||||
const table = taskTableRef.value.$el.querySelector('table > tbody') as any;
|
||||
state.sortable = Sortable.create(table, {
|
||||
animation: 200,
|
||||
//拖拽结束事件
|
||||
onEnd: (evt) => {
|
||||
const curRow = state.tasks.splice(evt.oldIndex, 1)[0];
|
||||
state.tasks.splice(evt.newIndex, 0, curRow);
|
||||
},
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const addTask = () => {
|
||||
state.tasks.push({ taskKey: randomUuid() });
|
||||
};
|
||||
|
||||
const deleteTask = (idx: any) => {
|
||||
state.tasks.splice(idx, 1);
|
||||
};
|
||||
|
||||
const btnOk = async () => {
|
||||
const onSave = async () => {
|
||||
await useI18nFormValidate(formRef);
|
||||
const checkRes = checkTasks();
|
||||
if (checkRes.err) {
|
||||
ElMessage.error(checkRes.err);
|
||||
return false;
|
||||
}
|
||||
|
||||
state.form.tasks = JSON.stringify(checkRes.tasks);
|
||||
await saveFlowDefExec();
|
||||
useI18nSaveSuccessMsg();
|
||||
emit('val-change', state.form);
|
||||
@@ -201,29 +126,7 @@ const btnOk = async () => {
|
||||
state.form = {} as any;
|
||||
};
|
||||
|
||||
const checkTasks = () => {
|
||||
if (state.tasks?.length == 0) {
|
||||
return { err: t('flow.tasksNotEmpty') };
|
||||
}
|
||||
|
||||
const tasks = [];
|
||||
for (let i = 0; i < state.tasks.length; i++) {
|
||||
const task = { ...state.tasks[i] };
|
||||
if (!task.name || !task.userId) {
|
||||
return { err: t('flow.tasksNoComplete', { index: i + 1 }) };
|
||||
}
|
||||
// 转为字符串(方便后续万一需要调整啥的)
|
||||
task.userId = `${task.userId}`;
|
||||
if (!task.taskKey) {
|
||||
task.taskKey = randomUuid();
|
||||
}
|
||||
tasks.push(task);
|
||||
}
|
||||
|
||||
return { tasks: tasks };
|
||||
};
|
||||
|
||||
const cancel = () => {
|
||||
const onCancel = () => {
|
||||
visible.value = false;
|
||||
emit('cancel');
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="h-full">
|
||||
<page-table
|
||||
ref="pageTableRef"
|
||||
:page-api="procdefApi.list"
|
||||
@@ -10,46 +10,46 @@
|
||||
:columns="columns"
|
||||
>
|
||||
<template #tableHeader>
|
||||
<el-button v-auth="perms.save" type="primary" icon="plus" @click="editFlowDef(false)">{{ $t('common.create') }}</el-button>
|
||||
<el-button v-auth="perms.del" :disabled="state.selectionData.length < 1" @click="deleteProcdef()" type="danger" icon="delete">
|
||||
<el-button v-auth="perms.save" type="primary" icon="plus" @click="onEditFlowDef(false)">{{ $t('common.create') }}</el-button>
|
||||
<el-button v-auth="perms.del" :disabled="state.selectionData.length < 1" @click="onDeleteProcdef()" type="danger" icon="delete">
|
||||
{{ $t('common.delete') }}
|
||||
</el-button>
|
||||
</template>
|
||||
|
||||
<template #tasks="{ data }">
|
||||
<el-link @click="showProcdefTasks(data)" icon="view" type="primary" :underline="false"> </el-link>
|
||||
</template>
|
||||
|
||||
<template #codePaths="{ data }">
|
||||
<TagCodePath :path="data.tags?.map((tag: any) => tag.codePath)" />
|
||||
<TagCodePath :path="data.tags" />
|
||||
</template>
|
||||
|
||||
<template #action="{ data }">
|
||||
<el-button link v-if="actionBtns[perms.save]" @click="editFlowDef(data)" type="primary">{{ $t('common.edit') }}</el-button>
|
||||
<el-button link v-if="actionBtns[perms.save]" @click="onEditFlowDef(data)" type="primary">{{ $t('common.edit') }}</el-button>
|
||||
|
||||
<el-button link v-if="actionBtns[perms.save]" @click="onShowFlowDesign(data)" type="primary">{{ $t('flow.flowDesign') }}</el-button>
|
||||
</template>
|
||||
</page-table>
|
||||
|
||||
<el-dialog v-model="flowTasksDialog.visible" :title="flowTasksDialog.title">
|
||||
<procdef-tasks :tasks="flowTasksDialog.tasks" />
|
||||
</el-dialog>
|
||||
|
||||
<procdef-edit v-model:visible="flowDefEditor.visible" :title="flowDefEditor.title" v-model:data="flowDefEditor.data" @val-change="valChange()" />
|
||||
<procdef-edit v-model:visible="flowDefEditor.visible" :title="flowDefEditor.title" v-model:data="flowDefEditor.data" @val-change="handleValChange()" />
|
||||
<FlowDesignDrawer
|
||||
:disabled="flowDesignEditor.disabled"
|
||||
v-model:visible="flowDesignEditor.visible"
|
||||
:data="flowDesignEditor.data"
|
||||
@save="onSaveFlowDesign"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, toRefs, reactive, onMounted, Ref } from 'vue';
|
||||
import { procdefApi } from './api';
|
||||
import { procdefApi, procinstApi } from './api';
|
||||
import PageTable from '@/components/pagetable/PageTable.vue';
|
||||
import { TableColumn } from '@/components/pagetable';
|
||||
import { hasPerms } from '@/components/auth/auth';
|
||||
import { SearchItem } from '@/components/SearchForm';
|
||||
import ProcdefEdit from './ProcdefEdit.vue';
|
||||
import ProcdefTasks from './components/ProcdefTasks.vue';
|
||||
import { ProcdefStatus } from './enums';
|
||||
import TagCodePath from '../ops/component/TagCodePath.vue';
|
||||
import { useI18nCreateTitle, useI18nDeleteConfirm, useI18nDeleteSuccessMsg, useI18nEditTitle } from '@/hooks/useI18n';
|
||||
import { useI18nCreateTitle, useI18nDeleteConfirm, useI18nDeleteSuccessMsg, useI18nEditTitle, useI18nSaveSuccessMsg } from '@/hooks/useI18n';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import FlowDesignDrawer from './components/flowdesign/FlowDesignDrawer.vue';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
@@ -64,7 +64,6 @@ const columns = [
|
||||
TableColumn.new('defKey', 'Key'),
|
||||
TableColumn.new('status', 'common.status').typeTag(ProcdefStatus),
|
||||
TableColumn.new('remark', 'common.remark'),
|
||||
TableColumn.new('tasks', 'flow.approvalNode').isSlot().alignCenter().setMinWidth(60),
|
||||
TableColumn.new('codePaths', 'tag.relateTag').isSlot().setMinWidth('250px'),
|
||||
TableColumn.new('creator', 'common.creator'),
|
||||
TableColumn.new('createTime', 'common.createTime').isTime(),
|
||||
@@ -93,14 +92,16 @@ const state = reactive({
|
||||
visible: false,
|
||||
data: null as any,
|
||||
},
|
||||
flowTasksDialog: {
|
||||
flowDesignEditor: {
|
||||
title: '',
|
||||
disabled: false,
|
||||
visible: false,
|
||||
tasks: '',
|
||||
procdefId: 0,
|
||||
data: null as any,
|
||||
},
|
||||
});
|
||||
|
||||
const { selectionData, query, flowDefEditor, flowTasksDialog } = toRefs(state);
|
||||
const { selectionData, query, flowDefEditor, flowDesignEditor } = toRefs(state);
|
||||
|
||||
onMounted(() => {
|
||||
if (Object.keys(actionBtns).length > 0) {
|
||||
@@ -112,13 +113,7 @@ const search = async () => {
|
||||
pageTableRef.value.search();
|
||||
};
|
||||
|
||||
const showProcdefTasks = (procdef: any) => {
|
||||
state.flowTasksDialog.tasks = procdef.tasks;
|
||||
state.flowTasksDialog.title = procdef.name + ' - ' + t('flow.approvalNode');
|
||||
state.flowTasksDialog.visible = true;
|
||||
};
|
||||
|
||||
const editFlowDef = (data: any) => {
|
||||
const onEditFlowDef = (data: any) => {
|
||||
if (!data) {
|
||||
state.flowDefEditor.data = null;
|
||||
state.flowDefEditor.title = useI18nCreateTitle('flow.procdef');
|
||||
@@ -129,12 +124,12 @@ const editFlowDef = (data: any) => {
|
||||
state.flowDefEditor.visible = true;
|
||||
};
|
||||
|
||||
const valChange = () => {
|
||||
const handleValChange = () => {
|
||||
state.flowDefEditor.visible = false;
|
||||
search();
|
||||
};
|
||||
|
||||
const deleteProcdef = async () => {
|
||||
const onDeleteProcdef = async () => {
|
||||
try {
|
||||
await useI18nDeleteConfirm(state.selectionData.map((x: any) => x.name).join(', '));
|
||||
await procdefApi.del.request({ id: state.selectionData.map((x: any) => x.id).join(',') });
|
||||
@@ -144,5 +139,18 @@ const deleteProcdef = async () => {
|
||||
//
|
||||
}
|
||||
};
|
||||
|
||||
const onShowFlowDesign = async (data: any) => {
|
||||
state.flowDesignEditor.procdefId = data.id;
|
||||
state.flowDesignEditor.data = await procdefApi.flowDef.request({ id: data.id });
|
||||
state.flowDesignEditor.title = t('flow.procDesign');
|
||||
state.flowDesignEditor.visible = true;
|
||||
};
|
||||
|
||||
const onSaveFlowDesign = async (data: any) => {
|
||||
await procdefApi.saveFlowDef.request({ id: state.flowDesignEditor.procdefId, flow: data });
|
||||
useI18nSaveSuccessMsg();
|
||||
state.flowDesignEditor.visible = false;
|
||||
};
|
||||
</script>
|
||||
<style lang="scss"></style>
|
||||
|
||||
@@ -1,6 +1,14 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-drawer :title="props.title" v-model="visible" :before-close="cancel" size="50%" :close-on-click-modal="!props.instTaskId">
|
||||
<el-drawer
|
||||
:title="props.title"
|
||||
v-model="visible"
|
||||
:before-close="cancel"
|
||||
size="50%"
|
||||
body-class="!p-2"
|
||||
header-class="!mb-2"
|
||||
:close-on-click-modal="!props.instTaskId"
|
||||
>
|
||||
<template #header>
|
||||
<DrawerHeader :header="title" :back="cancel" />
|
||||
</template>
|
||||
@@ -13,7 +21,7 @@
|
||||
<enum-tag :enums="FlowBizType" :value="procinst.bizType"></enum-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item :span="1" :label="$t('flow.initiator')">
|
||||
<AccountInfo :account-id="procinst.creatorId" :username="procinst.creator" />
|
||||
<AccountInfo :username="procinst.creator" />
|
||||
</el-descriptions-item>
|
||||
|
||||
<el-descriptions-item :span="1" :label="$t('flow.procinstStatus')">
|
||||
@@ -35,11 +43,6 @@
|
||||
</el-descriptions>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<el-divider content-position="left">{{ $t('flow.approveNode') }}</el-divider>
|
||||
<procdef-tasks :tasks="procinst?.procdef?.tasks" :procinst-tasks="procinst.procinstTasks" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<el-divider content-position="left">{{ $t('flow.bizInfo') }}</el-divider>
|
||||
<component v-if="procinst.bizType" ref="keyValueRef" :is="bizComponents[procinst.bizType]" :procinst="procinst"> </component>
|
||||
@@ -61,11 +64,14 @@
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<div v-if="flowDef">
|
||||
<el-divider content-position="left">{{ $t('flow.approveNode') }}</el-divider>
|
||||
<FlowDesign height="300px" disabled center :data="flowDef" />
|
||||
</div>
|
||||
|
||||
<template #footer v-if="props.instTaskId">
|
||||
<div>
|
||||
<el-button @click="cancel()">{{ $t('common.cancel') }}</el-button>
|
||||
<el-button type="primary" :loading="saveBtnLoading" @click="btnOk">{{ $t('common.confirm') }}</el-button>
|
||||
</div>
|
||||
<el-button @click="cancel()">{{ $t('common.cancel') }}</el-button>
|
||||
<el-button type="primary" :loading="saveBtnLoading" @click="btnOk">{{ $t('common.confirm') }}</el-button>
|
||||
</template>
|
||||
</el-drawer>
|
||||
</div>
|
||||
@@ -73,15 +79,15 @@
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { toRefs, reactive, watch, defineAsyncComponent, shallowReactive } from 'vue';
|
||||
import { procinstApi } from './api';
|
||||
import { procinstApi, procinstTaskApi } from './api';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import DrawerHeader from '@/components/drawer-header/DrawerHeader.vue';
|
||||
import { FlowBizType, ProcinstBizStatus, ProcinstTaskStatus, ProcinstStatus } from './enums';
|
||||
import ProcdefTasks from './components/ProcdefTasks.vue';
|
||||
import { formatTime } from '@/common/utils/format';
|
||||
import EnumTag from '@/components/enumtag/EnumTag.vue';
|
||||
import AccountInfo from '@/views/system/account/components/AccountInfo.vue';
|
||||
import { formatDate } from '@/common/utils/format';
|
||||
import FlowDesign from './components/flowdesign/FlowDesign.vue';
|
||||
|
||||
const DbSqlExecBiz = defineAsyncComponent(() => import('./flowbiz/dbms/DbSqlExecBiz.vue'));
|
||||
const RedisRunCmdBiz = defineAsyncComponent(() => import('./flowbiz/redis/RedisRunCmdBiz.vue'));
|
||||
@@ -112,6 +118,7 @@ const bizComponents: any = shallowReactive({
|
||||
|
||||
const state = reactive({
|
||||
procinst: {} as any,
|
||||
flowDef: null as any,
|
||||
tasks: [] as any,
|
||||
form: {
|
||||
status: ProcinstTaskStatus.Pass.value,
|
||||
@@ -121,26 +128,66 @@ const state = reactive({
|
||||
sortable: '' as any,
|
||||
});
|
||||
|
||||
const { procinst, form, saveBtnLoading } = toRefs(state);
|
||||
const { procinst, flowDef, form, saveBtnLoading } = toRefs(state);
|
||||
|
||||
watch(
|
||||
() => props.procinstId,
|
||||
async (newValue: any) => {
|
||||
if (newValue) {
|
||||
state.procinst = await procinstApi.detail.request({ id: newValue });
|
||||
} else {
|
||||
if (!newValue) {
|
||||
state.procinst = {};
|
||||
state.flowDef = null;
|
||||
return;
|
||||
}
|
||||
|
||||
state.procinst = await procinstApi.detail.request({ id: newValue });
|
||||
|
||||
const flowdef = JSON.parse(state.procinst.flowDef);
|
||||
procinstApi.hisOp.request({ id: newValue }).then((res: any) => {
|
||||
const nodeKey2Ops = res.reduce(
|
||||
(acc: { [x: string]: any[] }, item: { nodeKey: any }) => {
|
||||
const key = item.nodeKey;
|
||||
if (!acc[key]) {
|
||||
acc[key] = [];
|
||||
}
|
||||
acc[key].push(item);
|
||||
return acc;
|
||||
},
|
||||
{} as Record<string, typeof res>
|
||||
);
|
||||
|
||||
const nodeKey2Tasks = state.procinst.procinstTasks.reduce(
|
||||
(acc: { [x: string]: any[] }, item: { nodeKey: any }) => {
|
||||
const key = item.nodeKey;
|
||||
if (!acc[key]) {
|
||||
acc[key] = [];
|
||||
}
|
||||
acc[key].push(item);
|
||||
return acc;
|
||||
},
|
||||
{} as Record<string, typeof res>
|
||||
);
|
||||
|
||||
flowdef.nodes.forEach((node: any) => {
|
||||
const key = node.key;
|
||||
if (nodeKey2Ops[key]) {
|
||||
// 将操作记录挂载到 node 下,例如命名为 historyList
|
||||
node.extra.opLog = nodeKey2Ops[key][0];
|
||||
node.extra.tasks = nodeKey2Tasks[key];
|
||||
}
|
||||
});
|
||||
|
||||
state.flowDef = flowdef;
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
const btnOk = async () => {
|
||||
const status = state.form.status;
|
||||
let api = procinstApi.completeTask;
|
||||
let api = procinstTaskApi.passTask;
|
||||
if (status === ProcinstTaskStatus.Back.value) {
|
||||
api = procinstApi.backTask;
|
||||
api = procinstTaskApi.backTask;
|
||||
} else if (status === ProcinstTaskStatus.Reject.value) {
|
||||
api = procinstApi.rejectTask;
|
||||
api = procinstTaskApi.rejectTask;
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="h-full">
|
||||
<page-table
|
||||
ref="pageTableRef"
|
||||
:page-api="procinstApi.list"
|
||||
|
||||
@@ -1,39 +1,66 @@
|
||||
<template>
|
||||
<div>
|
||||
<page-table
|
||||
ref="pageTableRef"
|
||||
:page-api="procinstApi.tasks"
|
||||
:search-items="searchItems"
|
||||
v-model:query-form="query"
|
||||
v-model:selection-data="selectionData"
|
||||
:columns="columns"
|
||||
>
|
||||
<template #tableHeader>
|
||||
<!-- <el-button v-auth="perms.addAccount" type="primary" icon="plus" @click="editFlowDef(false)">添加</el-button> -->
|
||||
</template>
|
||||
<div class="h-full card !p-2">
|
||||
<el-tabs v-model="activeTabName" @tab-change="onTaskTabChange" class="h-full">
|
||||
<el-tab-pane :label="$t('flow.todoTask')" :name="todoTabName" class="h-full">
|
||||
<div class="h-full">
|
||||
<page-table
|
||||
ref="todoPageTableRef"
|
||||
:page-api="procinstTaskApi.tasks"
|
||||
:search-items="todoSearchItems"
|
||||
v-model:query-form="todoQuery"
|
||||
v-model:selection-data="selectionData"
|
||||
:columns="todoColumns"
|
||||
>
|
||||
<template #tableHeader>
|
||||
<!-- <el-button v-auth="perms.addAccount" type="primary" icon="plus" @click="editFlowDef(false)">添加</el-button> -->
|
||||
</template>
|
||||
|
||||
<template #action="{ data }">
|
||||
<el-button link @click="showProcinst(data, false)" type="primary">{{ $t('common.detail') }}</el-button>
|
||||
<el-button v-if="data.status == ProcinstTaskStatus.Process.value" link @click="showProcinst(data, true)" type="primary">
|
||||
{{ $t('flow.audit') }}
|
||||
</el-button>
|
||||
</template>
|
||||
</page-table>
|
||||
<template #action="{ data }">
|
||||
<el-button link @click="onShowProcinst(data, false)" type="primary">{{ $t('common.detail') }}</el-button>
|
||||
<el-button v-if="data.status == ProcinstTaskStatus.Process.value" link @click="onShowProcinst(data, true)" type="primary">
|
||||
{{ $t('flow.audit') }}
|
||||
</el-button>
|
||||
</template>
|
||||
</page-table>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane :label="$t('flow.doneTask')" :name="doneTabName" class="h-full">
|
||||
<div class="h-full">
|
||||
<page-table
|
||||
ref="donePageTableRef"
|
||||
:page-api="procinstTaskApi.tasks"
|
||||
:search-items="searchItems"
|
||||
v-model:query-form="query"
|
||||
v-model:selection-data="selectionData"
|
||||
:columns="columns"
|
||||
>
|
||||
<template #tableHeader>
|
||||
<!-- <el-button v-auth="perms.addAccount" type="primary" icon="plus" @click="editFlowDef(false)">添加</el-button> -->
|
||||
</template>
|
||||
|
||||
<template #action="{ data }">
|
||||
<el-button link @click="onShowProcinst(data, false)" type="primary">{{ $t('common.detail') }}</el-button>
|
||||
</template>
|
||||
</page-table>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
|
||||
<ProcinstDetail
|
||||
v-model:visible="procinstDetail.visible"
|
||||
:title="procinstDetail.title"
|
||||
:procinst-id="procinstDetail.procinstId"
|
||||
:inst-task-id="procinstDetail.instTaskId"
|
||||
@val-change="valChange()"
|
||||
@val-change="onValChange()"
|
||||
@cancel="procinstDetail.procinstId = 0"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, toRefs, reactive, Ref } from 'vue';
|
||||
import { procinstApi } from './api';
|
||||
import { ref, toRefs, reactive, Ref, useTemplateRef } from 'vue';
|
||||
import { procinstTaskApi } from './api';
|
||||
import PageTable from '@/components/pagetable/PageTable.vue';
|
||||
import { TableColumn } from '@/components/pagetable';
|
||||
import { SearchItem } from '@/components/SearchForm';
|
||||
@@ -45,11 +72,28 @@ import { useI18n } from 'vue-i18n';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const todoSearchItems = [SearchItem.input('bizKey', 'flow.bizKey'), SearchItem.select('bizType', 'flow.bizType').withEnum(FlowBizType)];
|
||||
|
||||
const todoColumns = [
|
||||
TableColumn.new('procinst.bizType', 'flow.bizType').typeTag(FlowBizType),
|
||||
TableColumn.new('procinst.remark', 'common.remark'),
|
||||
TableColumn.new('procinst.creator', 'flow.initiator'),
|
||||
TableColumn.new('procinst.status', 'flow.procinstStatus').typeTag(ProcinstStatus),
|
||||
TableColumn.new('status', 'flow.taskStatus').typeTag(ProcinstTaskStatus),
|
||||
TableColumn.new('procinst.bizKey', 'flow.bizKey'),
|
||||
TableColumn.new('procinst.procdefName', 'flow.procdefName'),
|
||||
TableColumn.new('procinst.createTime', 'flow.startingTime').isTime(),
|
||||
TableColumn.new('nodeName', 'flow.taskName'),
|
||||
TableColumn.new('createTime', 'flow.taskBeginTime').isTime(),
|
||||
TableColumn.new('action', 'common.operation').isSlot().fixedRight().setMinWidth(160).noShowOverflowTooltip().alignCenter(),
|
||||
];
|
||||
|
||||
const searchItems = [
|
||||
SearchItem.select('status', 'common.status').withEnum(ProcinstTaskStatus),
|
||||
SearchItem.input('bizKey', 'flow.bizKey'),
|
||||
SearchItem.select('bizType', 'flow.bizType').withEnum(FlowBizType),
|
||||
];
|
||||
|
||||
const columns = [
|
||||
TableColumn.new('procinst.bizType', 'flow.bizType').typeTag(FlowBizType),
|
||||
TableColumn.new('procinst.remark', 'common.remark'),
|
||||
@@ -58,8 +102,8 @@ const columns = [
|
||||
TableColumn.new('status', 'flow.taskStatus').typeTag(ProcinstTaskStatus),
|
||||
TableColumn.new('procinst.bizKey', 'flow.bizKey'),
|
||||
TableColumn.new('procinst.procdefName', 'flow.procdefName'),
|
||||
TableColumn.new('taskName', 'flow.taskName'),
|
||||
TableColumn.new('procinst.createTime', 'flow.startingTime').isTime(),
|
||||
TableColumn.new('nodeName', 'flow.taskName'),
|
||||
TableColumn.new('createTime', 'flow.taskBeginTime').isTime(),
|
||||
TableColumn.new('endTime', 'flow.endTime').isTime(),
|
||||
TableColumn.new('duration', 'flow.duration').setFormatFunc((data: any, prop: string) => {
|
||||
@@ -69,10 +113,18 @@ const columns = [
|
||||
}
|
||||
return formatTime(duration);
|
||||
}),
|
||||
TableColumn.new('action', 'common.operation').isSlot().fixedRight().setMinWidth(160).noShowOverflowTooltip().alignCenter(),
|
||||
TableColumn.new('remark', 'flow.approvalRemark'),
|
||||
TableColumn.new('action', 'common.operation').isSlot().fixedRight().setMinWidth(80).noShowOverflowTooltip().alignCenter(),
|
||||
];
|
||||
|
||||
const pageTableRef: Ref<any> = ref(null);
|
||||
const todoTabName = 'todo';
|
||||
const doneTabName = 'done';
|
||||
|
||||
const activeTabName = ref(todoTabName);
|
||||
|
||||
const todoPageTableRef: Ref<any> = useTemplateRef('todoPageTableRef');
|
||||
const donePageTableRef: Ref<any> = useTemplateRef('donePageTableRef');
|
||||
|
||||
const state = reactive({
|
||||
/**
|
||||
* 选中的数据
|
||||
@@ -82,6 +134,12 @@ const state = reactive({
|
||||
* 查询条件
|
||||
*/
|
||||
query: {
|
||||
status: null,
|
||||
bizType: '',
|
||||
pageNum: 1,
|
||||
pageSize: 0,
|
||||
},
|
||||
todoQuery: {
|
||||
status: ProcinstTaskStatus.Process.value,
|
||||
bizType: '',
|
||||
pageNum: 1,
|
||||
@@ -95,13 +153,21 @@ const state = reactive({
|
||||
},
|
||||
});
|
||||
|
||||
const { selectionData, query, procinstDetail } = toRefs(state);
|
||||
const { selectionData, query, todoQuery, procinstDetail } = toRefs(state);
|
||||
|
||||
const search = async () => {
|
||||
pageTableRef.value.search();
|
||||
const todoSearch = async () => {
|
||||
todoPageTableRef.value.search();
|
||||
};
|
||||
|
||||
const showProcinst = (data: any, audit: boolean) => {
|
||||
const onTaskTabChange = (activeName: string) => {
|
||||
if (activeName === todoTabName) {
|
||||
todoPageTableRef.value.search();
|
||||
} else {
|
||||
donePageTableRef.value.search();
|
||||
}
|
||||
};
|
||||
|
||||
const onShowProcinst = (data: any, audit: boolean) => {
|
||||
state.procinstDetail.procinstId = data.procinstId;
|
||||
if (!audit) {
|
||||
state.procinstDetail.instTaskId = 0;
|
||||
@@ -113,9 +179,9 @@ const showProcinst = (data: any, audit: boolean) => {
|
||||
state.procinstDetail.visible = true;
|
||||
};
|
||||
|
||||
const valChange = () => {
|
||||
const onValChange = () => {
|
||||
state.procinstDetail.visible = false;
|
||||
search();
|
||||
todoSearch();
|
||||
};
|
||||
</script>
|
||||
<style lang="scss"></style>
|
||||
|
||||
@@ -3,8 +3,10 @@ import Api from '@/common/Api';
|
||||
export const procdefApi = {
|
||||
list: Api.newGet('/flow/procdefs'),
|
||||
detail: Api.newGet('/flow/procdefs/detail/{id}'),
|
||||
flowDef: Api.newGet('/flow/procdefs/flowdef/{id}'),
|
||||
getByResource: Api.newGet('/flow/procdefs/{resourceType}/{resourceCode}'),
|
||||
save: Api.newPost('/flow/procdefs'),
|
||||
saveFlowDef: Api.newPost('/flow/procdefs/flowdef'),
|
||||
del: Api.newDelete('/flow/procdefs/{id}'),
|
||||
};
|
||||
|
||||
@@ -13,8 +15,12 @@ export const procinstApi = {
|
||||
start: Api.newPost('/flow/procinsts/start'),
|
||||
detail: Api.newGet('/flow/procinsts/{id}'),
|
||||
cancel: Api.newPost('/flow/procinsts/{id}/cancel'),
|
||||
hisOp: Api.newGet('/flow/his-procinsts-op/{id}'),
|
||||
};
|
||||
|
||||
export const procinstTaskApi = {
|
||||
tasks: Api.newGet('/flow/procinsts/tasks'),
|
||||
completeTask: Api.newPost('/flow/procinsts/tasks/complete'),
|
||||
passTask: Api.newPost('/flow/procinsts/tasks/pass'),
|
||||
backTask: Api.newPost('/flow/procinsts/tasks/back'),
|
||||
rejectTask: Api.newPost('/flow/procinsts/tasks/reject'),
|
||||
save: Api.newPost('/flow/procdefs'),
|
||||
|
||||
@@ -1,134 +0,0 @@
|
||||
<template>
|
||||
<el-steps align-center :active="stepActive">
|
||||
<el-step v-for="task in tasksArr" :status="getStepStatus(task)" :title="task.name" :key="task.taskKey">
|
||||
<template #description>
|
||||
<div>{{ `${task.accountUsername}(${task.accountName})` }}</div>
|
||||
<div v-if="task.completeTime">{{ `${formatDate(task.completeTime)}` }}</div>
|
||||
<div v-if="task.remark">{{ task.remark }}</div>
|
||||
</template>
|
||||
</el-step>
|
||||
</el-steps>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { toRefs, reactive, watch, onMounted } from 'vue';
|
||||
import { accountApi } from '../../system/api';
|
||||
import { ProcinstTaskStatus } from '../enums';
|
||||
import { formatDate } from '@/common/utils/format';
|
||||
import { ElSteps, ElStep } from 'element-plus';
|
||||
|
||||
const props = defineProps({
|
||||
// 流程定义任务
|
||||
tasks: {
|
||||
type: [String, Object],
|
||||
},
|
||||
procdef: {
|
||||
type: [Object],
|
||||
},
|
||||
// 流程实例任务列表
|
||||
procinstTasks: {
|
||||
type: [Array],
|
||||
},
|
||||
});
|
||||
|
||||
const state = reactive({
|
||||
tasksArr: [] as any,
|
||||
stepActive: 0,
|
||||
});
|
||||
|
||||
const { tasksArr, stepActive } = toRefs(state);
|
||||
|
||||
watch(
|
||||
() => props.tasks,
|
||||
(newValue: any) => {
|
||||
parseTasks(newValue);
|
||||
}
|
||||
);
|
||||
|
||||
watch(
|
||||
() => props.procinstTasks,
|
||||
() => {
|
||||
parseTasks(props.tasks);
|
||||
}
|
||||
);
|
||||
|
||||
watch(
|
||||
() => props.procdef,
|
||||
async (newValue: any) => {
|
||||
if (newValue) {
|
||||
parseTasksByKey(newValue);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
onMounted(() => {
|
||||
if (props.procdef) {
|
||||
parseTasksByKey(props.procdef);
|
||||
return;
|
||||
}
|
||||
parseTasks(props.tasks);
|
||||
});
|
||||
|
||||
const parseTasksByKey = async (procdef: any) => {
|
||||
parseTasks(procdef.tasks);
|
||||
};
|
||||
|
||||
const parseTasks = async (tasksStr: any) => {
|
||||
if (!tasksStr) return;
|
||||
const tasks = JSON.parse(tasksStr);
|
||||
const userIds = tasks.map((x: any) => x.userId);
|
||||
const usersRes = await accountApi.querySimple.request({ ids: [...new Set(userIds)].join(','), pageSize: 50 });
|
||||
const users = usersRes.list;
|
||||
// 将数组转换为 Map 结构,以 id 为 key
|
||||
const userMap = users.reduce((acc: any, obj: any) => {
|
||||
acc.set(obj.id, obj);
|
||||
return acc;
|
||||
}, new Map());
|
||||
|
||||
// 流程实例任务(用于显示完成时间,完成到哪一步等)
|
||||
let instTasksMap: any;
|
||||
if (props.procinstTasks) {
|
||||
state.stepActive = props.procinstTasks.length - 1;
|
||||
instTasksMap = props.procinstTasks.reduce((acc: any, obj: any) => {
|
||||
acc.set(obj.taskKey, obj);
|
||||
return acc;
|
||||
}, new Map());
|
||||
}
|
||||
|
||||
for (let task of tasks) {
|
||||
const user = userMap.get(Number.parseInt(task.userId));
|
||||
task.accountUsername = user.username;
|
||||
task.accountName = user.name;
|
||||
|
||||
// 存在实例任务,则赋值实例任务对应的完成时间和备注
|
||||
const instTask = instTasksMap?.get(task.taskKey);
|
||||
if (instTask) {
|
||||
task.status = instTask.status;
|
||||
task.completeTime = instTask.endTime;
|
||||
task.remark = instTask.remark;
|
||||
}
|
||||
}
|
||||
|
||||
state.tasksArr = tasks;
|
||||
};
|
||||
|
||||
const getStepStatus = (task: any): any => {
|
||||
const taskStatus = task.status;
|
||||
if (!taskStatus) {
|
||||
return 'wait';
|
||||
}
|
||||
|
||||
if (taskStatus == ProcinstTaskStatus.Pass.value) {
|
||||
return 'success';
|
||||
}
|
||||
if (taskStatus == ProcinstTaskStatus.Process.value) {
|
||||
return 'proccess';
|
||||
}
|
||||
if (taskStatus == ProcinstTaskStatus.Back.value || taskStatus == ProcinstTaskStatus.Reject.value) {
|
||||
return 'error';
|
||||
}
|
||||
|
||||
return 'wait';
|
||||
};
|
||||
</script>
|
||||
<style lang="scss"></style>
|
||||
281
frontend/src/views/flow/components/flowdesign/FlowDesign.vue
Normal file
281
frontend/src/views/flow/components/flowdesign/FlowDesign.vue
Normal file
@@ -0,0 +1,281 @@
|
||||
<template>
|
||||
<div :style="{ height: props.height }" class="flex flex-col" v-loading="saveing">
|
||||
<div class="h-[100vh]" ref="flowContainerRef"></div>
|
||||
</div>
|
||||
|
||||
<PropSettingDrawer
|
||||
v-model:visible="propSettingEditor.visible"
|
||||
:disabled="props.disabled"
|
||||
:lf="lf"
|
||||
:node="propSettingEditor.node"
|
||||
:nodes="propSettingEditor.nodes"
|
||||
></PropSettingDrawer>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, ref, useTemplateRef, watch } from 'vue';
|
||||
import LogicFlow from '@logicflow/core';
|
||||
import '@logicflow/core/lib/style/index.css';
|
||||
import '@logicflow/extension/lib/style/index.css';
|
||||
import { Control, DndPanel, Menu, SelectionSelect } from '@logicflow/extension';
|
||||
import { initCustomNodes } from './node';
|
||||
import PropSettingDrawer from './node/PropSettingDrawer.vue';
|
||||
import { NodeTypeEnum } from './node/enums';
|
||||
import { isTrue } from '@/common/assert';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useThemeConfig } from '@/store/themeConfig';
|
||||
|
||||
const props = defineProps({
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
// 流程数据
|
||||
data: {
|
||||
type: [Object, String],
|
||||
},
|
||||
// 居中显示
|
||||
center: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
height: {
|
||||
type: [Number, String],
|
||||
default: '100%',
|
||||
},
|
||||
});
|
||||
|
||||
const { themeConfig } = storeToRefs(useThemeConfig());
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const flowContainerRef = useTemplateRef('flowContainerRef');
|
||||
|
||||
const emit = defineEmits(['save']);
|
||||
|
||||
const propSettingEditor = ref({
|
||||
visible: false,
|
||||
node: {},
|
||||
nodes: [],
|
||||
});
|
||||
|
||||
const saveing = ref(false);
|
||||
|
||||
let lf: LogicFlow;
|
||||
|
||||
onMounted(() => {
|
||||
LogicFlow.use(DndPanel);
|
||||
LogicFlow.use(SelectionSelect);
|
||||
LogicFlow.use(Control);
|
||||
LogicFlow.use(Menu);
|
||||
|
||||
const isDark = themeConfig.value.isDark;
|
||||
|
||||
lf = new LogicFlow({
|
||||
container: flowContainerRef.value as HTMLElement,
|
||||
grid: true,
|
||||
nodeTextEdit: false, // 节点文本不可编辑
|
||||
edgeTextEdit: false, // 连线文本不可编辑
|
||||
edgeType: NodeTypeEnum.Edge.value,
|
||||
isSilentMode: props.disabled,
|
||||
background: {
|
||||
backgroundColor: isDark ? '#000000' : false,
|
||||
},
|
||||
});
|
||||
|
||||
if (isDark) {
|
||||
lf.setTheme({
|
||||
baseEdge: {
|
||||
stroke: '#FFFFFF', // 连线颜色
|
||||
strokeWidth: 2,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
initCustomNodes(lf, props.disabled);
|
||||
initControl();
|
||||
initEvent();
|
||||
|
||||
// custom node -> logicflow node,userData由 lf.render(userData)传入
|
||||
lf.adapterIn = function (userData: any) {
|
||||
const nodes = userData.nodes?.map((node: any) => {
|
||||
const extra = node.extra;
|
||||
const lfNode = extra.lfNode;
|
||||
extra.lfNode = null; // 置空节点信息
|
||||
lfNode.properties = extra;
|
||||
return lfNode;
|
||||
});
|
||||
|
||||
const edges = userData.edges?.map((edge: any) => {
|
||||
const extra = edge.extra;
|
||||
const lfEdge = extra.lfEdge;
|
||||
extra.lfEdge = null; // 置空连线信息
|
||||
lfEdge.properties = extra;
|
||||
return lfEdge;
|
||||
});
|
||||
|
||||
// 这里把userData转换为LogicFlow支持的格式
|
||||
return { nodes, edges };
|
||||
};
|
||||
|
||||
// logicflow node -> custom node
|
||||
lf.adapterOut = function (logicFlowData) {
|
||||
const flowNodes = logicFlowData.nodes.map((node) => {
|
||||
const nodeProps = node.properties;
|
||||
node.properties = {};
|
||||
const text = node.text;
|
||||
return {
|
||||
name: text instanceof Object ? text.value : text,
|
||||
key: node.id,
|
||||
type: node.type,
|
||||
extra: { ...nodeProps, lfNode: node },
|
||||
};
|
||||
});
|
||||
|
||||
const flowEdges = logicFlowData.edges.map((edge) => {
|
||||
const edgeProps = edge.properties;
|
||||
edge.properties = {};
|
||||
const text = edge.text || '';
|
||||
return {
|
||||
name: text instanceof Object ? text.value : text,
|
||||
key: edge.id,
|
||||
sourceNodeKey: edge.sourceNodeId,
|
||||
targetNodeKey: edge.targetNodeId,
|
||||
extra: { ...edgeProps, lfEdge: edge },
|
||||
};
|
||||
});
|
||||
|
||||
// 这里把LogicFlow生成的数据转换为后端需要的格式。
|
||||
return { edges: flowEdges, nodes: flowNodes };
|
||||
};
|
||||
|
||||
renderData(props.data);
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props.data,
|
||||
(data) => {
|
||||
renderData(data);
|
||||
}
|
||||
);
|
||||
|
||||
const renderData = (data: any) => {
|
||||
if (typeof data == 'string') {
|
||||
data = JSON.parse(data);
|
||||
}
|
||||
|
||||
lf.render(data || {});
|
||||
if (props.center) {
|
||||
lf.translateCenter();
|
||||
}
|
||||
};
|
||||
|
||||
const getLfExtension = (): any => {
|
||||
return lf.extension;
|
||||
};
|
||||
|
||||
/**
|
||||
* 初始化控制面板
|
||||
*/
|
||||
function initControl() {
|
||||
if (props.disabled) {
|
||||
return;
|
||||
}
|
||||
const control = getLfExtension().control;
|
||||
// 控制面板-清空画布
|
||||
control.addItem({
|
||||
iconClass: 'lf-control-clear',
|
||||
title: 'clear',
|
||||
text: t('flow.clear'),
|
||||
onClick: (lf: LogicFlow, ev: any) => {
|
||||
lf.clearData();
|
||||
},
|
||||
});
|
||||
// 控制面板-保存
|
||||
control.addItem({
|
||||
iconClass: 'lf-control-save',
|
||||
title: '',
|
||||
text: t('common.save'),
|
||||
onClick: async (lf: LogicFlow, ev: any) => {
|
||||
validateFlow(lf.getGraphRawData());
|
||||
try {
|
||||
saveing.value = true;
|
||||
let graphData = lf.getGraphData();
|
||||
emit('save', graphData);
|
||||
} finally {
|
||||
saveing.value = false;
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function validateFlow(rawData: LogicFlow.GraphData) {
|
||||
// 提取节点和边
|
||||
const nodes = rawData.nodes || [];
|
||||
const edges = rawData.edges || [];
|
||||
|
||||
// 查找开始节点和结束节点
|
||||
const startNodes = nodes.filter((node) => node.type === 'start');
|
||||
const endNodes = nodes.filter((node) => node.type === 'end');
|
||||
|
||||
// 检查是否只有一个开始节点和结束节点
|
||||
isTrue(startNodes.length == 1, 'flow.mustOneStartNode');
|
||||
isTrue(endNodes.length == 1, 'flow.mustOneEndNode');
|
||||
|
||||
const startNode = startNodes[0];
|
||||
const endNode = endNodes[0];
|
||||
|
||||
// 检查开始节点是否有出线
|
||||
|
||||
isTrue(
|
||||
edges.some((edge) => edge.sourceNodeId === startNode.id),
|
||||
'flow.mustOneOutEdgeForStartNode'
|
||||
);
|
||||
|
||||
// 检查结束节点是否有入线
|
||||
isTrue(
|
||||
edges.some((edge) => edge.targetNodeId === endNode.id),
|
||||
'flow.mustOneInEdgeForEndNode'
|
||||
);
|
||||
}
|
||||
|
||||
const initEvent = () => {
|
||||
const { eventCenter } = lf.graphModel;
|
||||
eventCenter.on('node:dbclick', (args: any) => {
|
||||
propSettingEditor.value.node = args.data;
|
||||
let graphData: any = lf.getGraphData();
|
||||
propSettingEditor.value.nodes = graphData['nodes'];
|
||||
propSettingEditor.value.visible = true;
|
||||
});
|
||||
|
||||
eventCenter.on('edge:dbclick ', (args: any) => {
|
||||
propSettingEditor.value.node = args.data;
|
||||
let graphData: any = lf.getGraphData();
|
||||
propSettingEditor.value.nodes = graphData['edges'];
|
||||
propSettingEditor.value.visible = true;
|
||||
});
|
||||
|
||||
eventCenter.on('edge:add', (args: any) => {
|
||||
// 调整边类型
|
||||
// lf.changeEdgeType(args.data.id, NodeTypeEnum.Edge.value);
|
||||
// lf.setProperties(args.data.id, {
|
||||
// condition: 'PASS',
|
||||
// });
|
||||
});
|
||||
|
||||
// eventCenter.on('blank:click', () => {
|
||||
// propSettingEditor.value.visible = false;
|
||||
// });
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.lf-control-save {
|
||||
background-image: url('data:image/svg+xml;charset=utf-8;base64,PHN2ZyB0PSIxNzQ1ODg5NTU4MjQ3IiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHAtaWQ9IjE1ODQ4IiB3aWR0aD0iNDgiIGhlaWdodD0iNDgiPjxwYXRoIGQ9Ik01NjMuOTM1NTQgMTIyLjYxMTM2OGE0OC42MDk2MTkgNDguNjA5NjE5IDAgMCAwLTQ3Ljk3MDAxOCA0OS4zMTMxNzl2MzAuNjM2ODUyYTQ4LjYwOTYxOSA0OC42MDk2MTkgMCAwIDAgNDcuOTcwMDE4IDQ5LjMxMzE3OWMyNi40Nzk0NSAwIDQ3Ljk3MDAxOS0yMi4xMzAxNjkgNDcuOTcwMDE5LTQ5LjMxMzE3OXYtMzAuNjM2ODUyYTQ4LjYwOTYxOSA0OC42MDk2MTkgMCAwIDAtNDcuOTcwMDE5LTQ5LjMxMzE3OXoiIGZpbGw9IiMwMzAwMDAiIHAtaWQ9IjE1ODQ5Ij48L3BhdGg+PHBhdGggZD0iTTk5MS43MDAxODcgMjc3LjI2NjcwOGMwLTIuMDQ2NzIxLTAuODk1NDQtMy44Mzc2MDEtMS4xNTEyOC01LjgyMDM2MmE0OC45Mjk0MTkgNDguOTI5NDE5IDAgMCAwLTEzLjYyMzQ4NS00MC45OTgzNzZsLTIxNS43MzcxNjUtMjE1LjY3MzIwNGE0OS4zNzcxMzkgNDkuMzc3MTM5IDAgMCAwLTM2LjA3MzQ1NC0xNC40NTQ5NjZjLTAuNTExNjggMC0wLjg5NTQ0LTAuMjU1ODQtMS4zNDMxNi0wLjI1NTg0aC0zOC4yNDgwOTVMNjg1LjMzMTY2OCAwbC0wLjQ0NzcyIDAuMDYzOTZIMzM5LjExNjA1MkwzMzguOTI0MTcyIDBIODEuNjEyOTkybC0wLjcwMzU2IDAuMTI3OTJMODAuMjY5ODMxIDBhNDYuNDk4OTM4IDQ2LjQ5ODkzOCAwIDAgMC0zMC40NDQ5NzIgMTIuMDI0NDg1Yy0wLjk1OTQgMC44OTU0NC0yLjMwMjU2MSAxLjM0MzE2MS0zLjE5ODAwMSAyLjMwMjU2MS0xLjA4NzMyIDEuMDIzMzYtMS41OTkwMDEgMi40OTQ0NDEtMi40OTQ0NDEgMy42NDU3MjFhNDUuODU5MzM4IDQ1Ljg1OTMzOCAwIDAgMC0xMS44MzI2MDQgMjkuOTk3MjUybDAuMTI3OTIgMC42Mzk2LTAuMTI3OTIgMC43MDM1NnY5MjQuNzM0MDQxbDAuMTI3OTIgMC44MzE0ODEtMC4xMjc5MiAwLjU3NTY0YzAgMTIuMDI0NDg1IDQuOTI0OTIyIDIyLjY0MTg0OSAxMi4zNDQyODQgMzAuOTU2NjUyIDAuNzAzNTYgMC44MzE0OCAxLjE1MTI4IDEuODU0ODQxIDEuODU0ODQxIDIuNjIyMzYxIDEuMjc5MiAxLjM0MzE2MSAyLjk0MjE2MSAxLjk4Mjc2MSA0LjM0OTI4MiAzLjEzNDA0MWE0Ny4wNzQ1NzggNDcuMDc0NTc4IDAgMCAwIDI5LjQyMTYxMSAxMS4xOTMwMDVsMC41NzU2NDEtMC4xMjc5MiAwLjU3NTY0IDAuMTI3OTJoODYxLjE1Nzc3NmwwLjYzOTYtMC4xMjc5MiAwLjUxMTY4MSAwLjEyNzkyYTQ2LjQzNDk3OCA0Ni40MzQ5NzggMCAwIDAgMjkuMzU3NjUxLTExLjI1Njk2NWMxLjM0MzE2MS0xLjE1MTI4IDMuMTM0MDQxLTEuNzkwODgxIDQuMzQ5MjgyLTMuMTM0MDQxIDAuNzY3NTItMC43Njc1MiAxLjIxNTI0LTEuNzkwODgxIDEuODU0ODQxLTIuNjIyMzYxYTQ2LjgxODczOCA0Ni44MTg3MzggMCAwIDAgMTIuMjgwMzI0LTMwLjk1NjY1MmwtMC4xMjc5Mi0wLjYzOTYgMC4xMjc5Mi0wLjc2NzUyMSAwLjEyNzkyLTY5Ni43MTY1NTJ6TTM4Ni43NjYyNzEgOTUuOTQwMDM3aDI1MC40Njc0NTh2MTkzLjIyMzIzNkgzODYuNzY2MjcxVjk1Ljk0MDAzN3ogbTM1Mi40ODM2OTggODMxLjQ4MDMyNUgyODQuODEzOTkxdi0yNTAuODUxMjE4bDYyLjIzMzEwNS02Mi4yMzMxMDRoMzI5LjkwNTgwOGw2Mi4yOTcwNjUgNjIuMzYxMDI0djI1MC43MjMyOTh6IG0xNTYuNTEwMTgxIDBoLTYwLjU3MDE0NHYtMjY3LjA5NzA2NGMwLTAuNjM5Ni0wLjMxOTgtMS4yNzkyLTAuMzgzNzYtMS43OTA4ODFhNDguODY1NDU5IDQ4Ljg2NTQ1OSAwIDAgMC0xNC4zOTEwMDYtMzYuMzkzMjU0bC04OS4wOTYzMTQtODguOTA0NDM1YTQ5LjMxMzE3OSA0OS4zMTMxNzkgMCAwIDAtMzYuMDA5NDk0LTE0LjQ1NDk2NWMtMC41NzU2NCAwLTEuMDg3MzItMC4zMTk4LTEuNTM1MDQxLTAuMzE5OEgzMzAuODY1MjA5Yy0wLjYzOTYgMC0xLjIxNTI0IDAuMzE5OC0xLjg1NDg0IDAuMzgzNzZhNDkuMjQ5MjE5IDQ5LjI0OTIxOSAwIDAgMC0zNi40NTcyMTUgMTQuMzI3MDQ1bC04OS4wMzIzNTQgODguOTY4Mzk1YTQ5Ljg4ODgxOSA0OS44ODg4MTkgMCAwIDAtMTQuMzI3MDQ2IDM2LjU4NTEzNGMwIDAuNTExNjgtMC4zMTk4IDEuMDIzMzYtMC4zMTk4IDEuNTk5MDAxVjkyNy40MjAzNjJIMTI4LjIzOTg1di04MzEuNDgwMzI1aDE2Mi41ODYzODR2MjM5Ljg1MDA5NGwwLjEyNzkyIDAuNzAzNTYtMC4xMjc5MiAwLjYzOTYwMWMwIDExLjY0MDcyNSA0Ljc5NzAwMiAyMS45MzgyODkgMTEuOTYwNTI0IDMwLjI1MzA5MiAwLjgzMTQ4IDEuMDg3MzIgMS4zNDMxNjEgMi40MzA0ODEgMi4zNjY1MjEgMy4zODk4ODEgMS4wMjMzNiAxLjAyMzM2IDIuMzAyNTYxIDEuNDcxMDgxIDMuMzg5ODgyIDIuMzY2NTIxYTQ2LjY5MDgxOCA0Ni42OTA4MTggMCAwIDAgMzAuMzE3MDUxIDExLjk2MDUyNGwwLjcwMzU2MS0wLjEyNzkyIDAuNTc1NjQgMC4xMjc5MmgzNDMuNjU3MjE0bDAuNzY3NTItMC4xMjc5MiAwLjcwMzU2MSAwLjEyNzkyYTQ2LjM3MTAxOCA0Ni4zNzEwMTggMCAwIDAgMzAuMzE3MDUyLTExLjk2MDUyNGMxLjE1MTI4LTAuODk1NDQgMi4zNjY1MjEtMS4zNDMxNjEgMy4zODk4ODEtMi4zNjY1MjEgMS4wODczMi0wLjk1OTQgMS40NzEwODEtMi4zNjY1MjEgMi4zNjY1MjEtMy4zODk4ODFhNDYuNjI2ODU4IDQ2LjYyNjg1OCAwIDAgMCAxMi4wMjQ0ODQtMzAuMjUzMDkybC0wLjEyNzkyLTAuNjM5NjAxIDAuMTI3OTItMC43MDM1NlYxMjIuNDE5NDg4TDg5NS43NjAxNSAyODQuOTQxOTExVjkyNy40MjAzNjJ6IiBmaWxsPSIjMDMwMDAwIiBwLWlkPSIxNTg1MCI+PC9wYXRoPjwvc3ZnPg==');
|
||||
}
|
||||
.lf-control-clear {
|
||||
background-image: url('data:image/svg+xml;charset=utf-8;base64,PHN2ZyB0PSIxNzQ1ODg5NjY5MjU1IiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHAtaWQ9IjE5MzQ1IiB3aWR0aD0iNDgiIGhlaWdodD0iNDgiPjxwYXRoIGQ9Ik05NzcuMTg4NTcxIDIxOC42OTcxNDNINjU2LjgyMjg1N1Y0Ni44MTE0MjljMC0yNi4zMzE0MjktMjEuMjExNDI5LTQ2LjgxMTQyOS00Ni44MTE0MjgtNDYuODExNDI5SDQxMy45ODg1NzFjLTI1LjYgMC00Ni44MTE0MjkgMjAuNDgtNDYuODExNDI4IDQ2LjgxMTQyOXYxNzEuODg1NzE0SDQ2LjgxMTQyOWE0Ni4wOCA0Ni4wOCAwIDAgMC00Ni44MTE0MjkgNDYuMDh2MjE4LjY5NzE0M2MwIDI1LjYgMjAuNDggNDYuODExNDI5IDQ2LjgxMTQyOSA0Ni44MTE0MjhINzMuMTQyODU3djQ0Ny42MzQyODZjMCAyNS42IDIxLjIxMTQyOSA0Ni4wOCA0Ni44MTE0MjkgNDYuMDhIOTA0LjA0NTcxNGMyNS42IDAgNDYuODExNDI5LTIwLjQ4IDQ2LjgxMTQyOS00Ni44MTE0MjlWNTMwLjI4NTcxNGgyNy4wNjI4NTdjMjUuNiAwIDQ2LjgxMTQyOS0yMC40OCA0Ni44MTE0MjktNDYuODExNDI4VjI2NC43NzcxNDNhNDcuMTc3MTQzIDQ3LjE3NzE0MyAwIDAgMC00Ny41NDI4NTgtNDYuMDh6IG0tMTE5Ljk1NDI4NSA3MTIuNDExNDI4aC0xMDIuNHYtMTE5Ljk1NDI4NWMwLTI1LjYtMjAuNDgtNDYuODExNDI5LTQ2LjgxMTQyOS00Ni44MTE0MjktMjUuNiAwLTQ2LjgxMTQyOSAyMC40OC00Ni44MTE0MjggNDYuODExNDI5djExOS45NTQyODVoLTEwMi40di0xMTkuOTU0Mjg1YzAtMjUuNi0yMC40OC00Ni44MTE0MjktNDYuODExNDI5LTQ2LjgxMTQyOXMtNDYuODExNDI5IDIwLjQ4LTQ2LjgxMTQyOSA0Ni44MTE0Mjl2MTE5Ljk1NDI4NWgtMTAyLjR2LTExOS45NTQyODVjMC0yNS42LTIwLjQ4LTQ2LjgxMTQyOS00Ni44MTE0MjgtNDYuODExNDI5LTI1LjYgMC00Ni44MTE0MjkgMjAuNDgtNDYuODExNDI5IDQ2LjgxMTQyOXYxMTkuOTU0Mjg1aC0xMDIuNFY1MzIuNDhoNjkxLjJ2Mzk4LjYyODU3MXpNOTIuODkxNDI5IDMxMS41ODg1NzFoMzIxLjA5NzE0MmMyNS42IDAgNDYuODExNDI5LTIwLjQ4IDQ2LjgxMTQyOS00Ni44MTE0MjhWOTIuODkxNDI5aDEwMi40djE3MS44ODU3MTRjMCAyNS42IDIwLjQ4IDQ2LjgxMTQyOSA0Ni44MTE0MjkgNDYuODExNDI4aDMyMS4wOTcxNDJ2MTI1LjA3NDI4Nkg5Mi44OTE0MjlWMzExLjU4ODU3MXoiIGZpbGw9IiMzMzMzMzMiIHAtaWQ9IjE5MzQ2Ij48L3BhdGg+PC9zdmc+');
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,46 @@
|
||||
<template>
|
||||
<el-drawer
|
||||
:title="title"
|
||||
v-model="visible"
|
||||
:before-close="cancel"
|
||||
:destroy-on-close="true"
|
||||
:close-on-click-modal="false"
|
||||
size="80%"
|
||||
body-class="!p-2"
|
||||
header-class="!mb-2"
|
||||
>
|
||||
<template #header>
|
||||
<DrawerHeader :header="title" :back="cancel" />
|
||||
</template>
|
||||
|
||||
<FlowDesign :disabled="props.disabled" :data="props.data" @save="(data) => emit('save', data)" />
|
||||
</el-drawer>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import DrawerHeader from '@/components/drawer-header/DrawerHeader.vue';
|
||||
import FlowDesign from './FlowDesign.vue';
|
||||
|
||||
const props = defineProps({
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
data: {
|
||||
type: [Object],
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
},
|
||||
});
|
||||
|
||||
const visible = defineModel<boolean>('visible', { default: false });
|
||||
|
||||
const emit = defineEmits(['cancel', 'save']);
|
||||
|
||||
const cancel = () => {
|
||||
visible.value = false;
|
||||
emit('cancel');
|
||||
};
|
||||
</script>
|
||||
<style lang="scss"></style>
|
||||
@@ -0,0 +1,108 @@
|
||||
<template>
|
||||
<el-drawer
|
||||
body-class="!pt-2"
|
||||
header-class="!mb-2"
|
||||
:title="title"
|
||||
v-model="visible"
|
||||
:before-close="onCancel"
|
||||
:destroy-on-close="true"
|
||||
:close-on-click-modal="false"
|
||||
size="40%"
|
||||
>
|
||||
<template #header>
|
||||
<DrawerHeader :header="title" :back="onCancel" />
|
||||
</template>
|
||||
|
||||
<el-form ref="propSettingFormRef" :model="form" label-position="top" :disabled="props.disabled">
|
||||
<el-form-item ref="nameRef" :label="$t('common.name')" :rules="[Rules.requiredInput('common.name')]">
|
||||
<el-input v-model="name" clearable></el-input>
|
||||
</el-form-item>
|
||||
|
||||
<component ref="formItemsRef" :is="getCustomNode(props.node.type)?.propSettingComp" v-model="form" :disabled="disabled" :nodes="nodes" :node="node">
|
||||
<template v-slot:[key]="data" v-for="(item, key) in $slots">
|
||||
<slot :name="key" v-bind="data || {}"></slot>
|
||||
</template>
|
||||
</component>
|
||||
</el-form>
|
||||
|
||||
<template #footer>
|
||||
<el-button @click="onCancel()">{{ $t('common.cancel') }}</el-button>
|
||||
<el-button v-if="!props.disabled" type="primary" @click="onConfirm">{{ $t('common.confirm') }}</el-button>
|
||||
</template>
|
||||
</el-drawer>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { watch, ref, useTemplateRef } from 'vue';
|
||||
import DrawerHeader from '@/components/drawer-header/DrawerHeader.vue';
|
||||
import { useI18nFormValidate, useI18nPleaseInput } from '@/hooks/useI18n';
|
||||
import { Rules } from '@/common/rule';
|
||||
import LogicFlow from '@logicflow/core';
|
||||
import { getCustomNode } from '.';
|
||||
import { notEmpty } from '@/common/assert';
|
||||
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: [Boolean, Object],
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
node: {
|
||||
type: Object,
|
||||
default: {},
|
||||
},
|
||||
nodes: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
lf: {
|
||||
type: LogicFlow,
|
||||
default: null,
|
||||
},
|
||||
});
|
||||
|
||||
const propSettingFormRef = useTemplateRef('propSettingFormRef');
|
||||
const formItemsRef: any = useTemplateRef('formItemsRef');
|
||||
|
||||
const visible = defineModel<boolean>('visible', { default: false });
|
||||
|
||||
// 节点名
|
||||
const name = ref('');
|
||||
|
||||
// 节点props表单信息
|
||||
const form = ref({});
|
||||
|
||||
watch(
|
||||
() => props.node,
|
||||
(n) => {
|
||||
if (!n) {
|
||||
return;
|
||||
}
|
||||
name.value = n.text instanceof Object ? n.text.value : n.text;
|
||||
form.value = { ...n.properties };
|
||||
}
|
||||
);
|
||||
|
||||
const onConfirm = async () => {
|
||||
notEmpty(name.value, useI18nPleaseInput('common.name'));
|
||||
if (formItemsRef.value?.confirm) {
|
||||
formItemsRef.value?.confirm();
|
||||
}
|
||||
await useI18nFormValidate(propSettingFormRef);
|
||||
const nodeId = props.node.id;
|
||||
// 更新流程节点上的文本内容
|
||||
props.lf.updateText(nodeId, name.value);
|
||||
props.lf.setProperties(nodeId, form.value);
|
||||
onCancel();
|
||||
};
|
||||
|
||||
const onCancel = () => {
|
||||
visible.value = false;
|
||||
};
|
||||
</script>
|
||||
<style lang="scss"></style>
|
||||
@@ -0,0 +1,14 @@
|
||||
<template>
|
||||
<!-- <el-tabs v-model="activeTabName">
|
||||
<el-tab-pane :label="$t('common.basic')" :name="basic"> </el-tab-pane>
|
||||
</el-tabs> -->
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
|
||||
const basicTabName = 'basic';
|
||||
|
||||
const activeTabName = ref(basicTabName);
|
||||
|
||||
const form = defineModel<any>('modelValue', { required: true });
|
||||
</script>
|
||||
@@ -0,0 +1,49 @@
|
||||
import { BezierEdge, BezierEdgeModel, CircleNode, CircleNodeModel } from '@logicflow/core';
|
||||
import PropSetting from './PropSetting.vue';
|
||||
import { NodeTypeEnum } from '../enums';
|
||||
|
||||
class EdgeModel extends BezierEdgeModel {
|
||||
setAttributes() {
|
||||
this.offset = 20;
|
||||
|
||||
const {
|
||||
properties: { isExecuted },
|
||||
} = this;
|
||||
|
||||
if (isExecuted) {
|
||||
this.stroke = 'green';
|
||||
}
|
||||
}
|
||||
|
||||
getEdgeStyle() {
|
||||
const style = super.getEdgeStyle();
|
||||
const { properties } = this;
|
||||
if (properties.isActived) {
|
||||
style.strokeDasharray = '4 4';
|
||||
}
|
||||
return style;
|
||||
}
|
||||
|
||||
/**
|
||||
* 重写此方法,使保存数据是能带上锚点数据。
|
||||
*/
|
||||
getData() {
|
||||
const data = super.getData();
|
||||
data.sourceAnchorId = this.sourceAnchorId;
|
||||
data.targetAnchorId = this.targetAnchorId;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
const nodeType = NodeTypeEnum.Edge;
|
||||
|
||||
export default {
|
||||
type: nodeType.value,
|
||||
// 注册配置信息
|
||||
registerConf: {
|
||||
type: nodeType.value,
|
||||
model: EdgeModel,
|
||||
view: BezierEdge,
|
||||
},
|
||||
propSettingComp: PropSetting,
|
||||
};
|
||||
@@ -0,0 +1,12 @@
|
||||
<template>
|
||||
<el-tabs v-model="activeTabName"> </el-tabs>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
|
||||
const basicTabName = 'basic';
|
||||
|
||||
const activeTabName = ref(basicTabName);
|
||||
|
||||
const form = defineModel<any>('modelValue', { required: true });
|
||||
</script>
|
||||
@@ -0,0 +1,55 @@
|
||||
import { CircleNode, CircleNodeModel } from '@logicflow/core';
|
||||
import PropSetting from './PropSetting.vue';
|
||||
import { NodeTypeEnum } from '../enums';
|
||||
import { HisProcinstOpState } from '@/views/flow/enums';
|
||||
|
||||
class endModel extends CircleNodeModel {
|
||||
initNodeData(data: any) {
|
||||
super.initNodeData(data);
|
||||
this.r = 20;
|
||||
}
|
||||
|
||||
getNodeStyle() {
|
||||
const style = super.getNodeStyle();
|
||||
const properties = this.properties;
|
||||
|
||||
const opLog: any = properties.opLog;
|
||||
if (!opLog) {
|
||||
return style;
|
||||
}
|
||||
|
||||
if (opLog.state == HisProcinstOpState.Completed.value) {
|
||||
style.stroke = 'green';
|
||||
} else if (opLog.state == HisProcinstOpState.Failed.value) {
|
||||
style.stroke = 'red';
|
||||
} else {
|
||||
style.stroke = 'rgb(24, 125, 255)';
|
||||
}
|
||||
|
||||
return style;
|
||||
}
|
||||
}
|
||||
|
||||
class endView extends CircleNode {}
|
||||
|
||||
const nodeType = NodeTypeEnum.End;
|
||||
const nodeTypeExtra = nodeType.extra;
|
||||
|
||||
export default {
|
||||
order: 10,
|
||||
type: nodeType.value,
|
||||
// 注册配置信息
|
||||
registerConf: {
|
||||
type: nodeType.value,
|
||||
model: endModel,
|
||||
view: endView,
|
||||
},
|
||||
dndPanelConf: {
|
||||
type: nodeType.value,
|
||||
text: nodeTypeExtra.text,
|
||||
label: nodeType.label,
|
||||
icon: 'data:image/svg+xml;charset=utf-8;base64,PHN2ZyB0PSIxNzQ1ODg4Njg1MDk0IiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwNzkgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHAtaWQ9IjI0NzEiIHdpZHRoPSIzMiIgaGVpZ2h0PSIzMiI+PHBhdGggZD0iTTQ4Mi4zODEzMjIgNDYuOTg2MTQ4QzI4NS4wMjc4MjUgNDUuNTE2NzkzIDk5Ljg4MTQ2MiAxOTUuMTI5MDU2IDU4LjM1NzkzNiAzODcuODYzNzY2Yy00MC4wNDk0MjQgMTY5LjI5ODExMyAzMS42NTc1ODMgMzU4LjE0MzAxOCAxNzUuMjc0NzI1IDQ1Ni44NDA4ODYgMTM5LjM5OTM5IDk5Ljg0MTE3NSAzMzguMDcwMzcxIDEwNy42NjU4NzEgNDgzLjA3NjY2MiAxNC45Mzc0OTYgMTQ1LjgwNzg4Ni04OS4yODg5NjMgMjMxLjY3MDI1Mi0yNjcuNTkxNjI0IDIwNC40NjM0NDktNDM3LjI1NjYtMjQuOTEzNTQ2LTE3NS44Nzc5MzktMTY1LjQwODMwOC0zMjguODQyMjg4LTMzOS44NzM4NDMtMzY0Ljk5NDMxNC0zMi40MzYzOTgtNy4yMzY2Ny02NS42OTE0NzgtMTAuNjI1Mjk5LTk4LjkxNzYwNy0xMC40MDUwODZ6IG0xMC40NzkxMjMgMTM3LjU3NzQwOWMxNDUuMDE4MTU1LTAuNDU5NDExIDI3OC4yMjgzMTMgMTE5LjU1ODM0NyAyOTMuMDA5MTkyIDI2My45MzE1MjQgMTguODY3MTY4IDEzNy45OTUwNTUtNzAuMDk5NTQ0IDI4Mi4zNDQ5NzYtMjAzLjg1Nzg2MiAzMjMuODMyNDMyLTEzMC41NTA1MTEgNDQuNDI2NjQxLTI4Ny45NzI3NTktMTIuMjY5MzA3LTM1NS45ODQwNzItMTMzLjM2NjMwMS03NS4yNTQxNTItMTI1LjYzNzk0Ny00My4yMzU4NzUtMzA0LjY3NTI4NSA3Ni4wMTM5ODQtMzkxLjkwMjU5NSA1NC4wNzkwMTUtNDEuNzI2NjUzIDEyMi41NDIxNDUtNjQuMDY1OTggMTkwLjgxODc1OC02Mi40OTUwNnoiIHAtaWQ9IjI0NzIiPjwvcGF0aD48L3N2Zz4=',
|
||||
properties: nodeTypeExtra.defaultProp,
|
||||
},
|
||||
propSettingComp: PropSetting,
|
||||
};
|
||||
38
frontend/src/views/flow/components/flowdesign/node/enums.ts
Normal file
38
frontend/src/views/flow/components/flowdesign/node/enums.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import EnumValue from '@/common/Enum';
|
||||
import { i18n } from '@/i18n';
|
||||
|
||||
export const NodeTypeEnum = {
|
||||
Start: EnumValue.of('start', i18n.global.t('flow.start')).setExtra({
|
||||
order: 1,
|
||||
text: i18n.global.t('flow.start'),
|
||||
defaultProp: {},
|
||||
}),
|
||||
|
||||
End: EnumValue.of('end', i18n.global.t('flow.end')).setExtra({
|
||||
order: 100,
|
||||
text: i18n.global.t('flow.end'),
|
||||
defaultProp: {},
|
||||
}),
|
||||
|
||||
Edge: EnumValue.of('flow-edge', i18n.global.t('flow.flowEdge')).setExtra({
|
||||
text: i18n.global.t('flow.flowEdge'),
|
||||
}),
|
||||
|
||||
UserTask: EnumValue.of('usertask', i18n.global.t('flow.usertask')).setExtra({
|
||||
order: 2,
|
||||
type: 'usertask',
|
||||
text: i18n.global.t('flow.usertask'),
|
||||
}),
|
||||
|
||||
Serial: EnumValue.of('serial', i18n.global.t('flow.serial')).setExtra({
|
||||
order: 3,
|
||||
text: i18n.global.t('flow.serial'),
|
||||
defaultProp: { condition: `{{ procinstTaskStatus == 1 }}` },
|
||||
}),
|
||||
|
||||
Parallel: EnumValue.of('parallel', i18n.global.t('flow.parallel')).setExtra({
|
||||
order: 4,
|
||||
text: i18n.global.t('flow.parallel'),
|
||||
defaultProp: {},
|
||||
}),
|
||||
};
|
||||
81
frontend/src/views/flow/components/flowdesign/node/index.ts
Normal file
81
frontend/src/views/flow/components/flowdesign/node/index.ts
Normal file
@@ -0,0 +1,81 @@
|
||||
import EnumValue from '@/common/Enum';
|
||||
import LogicFlow from '@logicflow/core';
|
||||
|
||||
const allNodes: Record<string, any> = import.meta.glob('./**/index.ts', { eager: true });
|
||||
|
||||
const nodeMap = new Map<string, CustomNode>();
|
||||
|
||||
export interface CustomNode {
|
||||
order?: number; // 节点排序(影响拖拽面板显示顺序)
|
||||
type: string; // 节点类型
|
||||
registerConf: any; // 节点注册信息
|
||||
dndPanelConf: any; // 节点拖拽面板配置信息
|
||||
propSettingComp?: any; // 属性设置组件
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有自定义节点
|
||||
*
|
||||
* @returns 自定义节点配置
|
||||
*/
|
||||
export const getCustomNodes = () => {
|
||||
const nodes = [];
|
||||
for (const path in allNodes) {
|
||||
// path => ./start/index.ts
|
||||
// 获取默认导出的部件
|
||||
const node = allNodes[path].default;
|
||||
nodes.push(node);
|
||||
nodeMap.set(node.type, node);
|
||||
}
|
||||
|
||||
return nodes.sort((a, b) => {
|
||||
if (a.order !== undefined && b.order !== undefined) {
|
||||
return a.order - b.order; // 按order字段排序
|
||||
} else if (a.order !== undefined) {
|
||||
return -1; // a有order字段,排在前面
|
||||
} else if (b.order !== undefined) {
|
||||
return 1; // b有order字段,排在前面
|
||||
} else {
|
||||
return 0; // 两个都没有order字段,保持原顺序
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 根据节点类型获取自定义节点
|
||||
*
|
||||
* @param type 节点类型
|
||||
* @returns 节点信息
|
||||
*/
|
||||
export const getCustomNode = (type: string): CustomNode | undefined => {
|
||||
return nodeMap.get(type);
|
||||
};
|
||||
|
||||
/**
|
||||
* 注册自定义节点
|
||||
*
|
||||
* @param lf LogicFlow 实例
|
||||
*/
|
||||
export const initCustomNodes = (lf: LogicFlow, disable: boolean = false) => {
|
||||
const customNodes = getCustomNodes();
|
||||
const dndPanelItmes = [];
|
||||
|
||||
// 注册自定义节点
|
||||
for (const node of customNodes) {
|
||||
if (!node.registerConf) {
|
||||
continue;
|
||||
}
|
||||
lf.register(node.registerConf);
|
||||
if (node.dndPanelConf) {
|
||||
dndPanelItmes.push(node.dndPanelConf);
|
||||
}
|
||||
}
|
||||
|
||||
if (disable) {
|
||||
return;
|
||||
}
|
||||
|
||||
const extension: any = lf.extension;
|
||||
// 注册自定义节点面板
|
||||
extension?.dndPanel?.setPatternItems(dndPanelItmes);
|
||||
};
|
||||
@@ -0,0 +1,20 @@
|
||||
<template>
|
||||
<el-tabs v-model="activeTabName">
|
||||
<el-tab-pane :label="$t('common.basic')" :name="basic">
|
||||
<el-form-item :label="$t('common.name')">
|
||||
<el-input v-model="form.name" clearable></el-input>
|
||||
</el-form-item>
|
||||
</el-tab-pane>
|
||||
|
||||
<!-- <el-tab-pane :label="$t('')"> </el-tab-pane> -->
|
||||
</el-tabs>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
|
||||
const basic = 'basic';
|
||||
|
||||
const activeTabName = ref(basic);
|
||||
|
||||
const form = defineModel<any>('modelValue', { required: true });
|
||||
</script>
|
||||
@@ -0,0 +1,76 @@
|
||||
import { RectNode, RectNodeModel, h } from '@logicflow/core';
|
||||
import PropSetting from './PropSetting.vue';
|
||||
import { NodeTypeEnum } from '../enums';
|
||||
|
||||
class UserTaskModel extends RectNodeModel {
|
||||
initNodeData(data: any) {
|
||||
super.initNodeData(data);
|
||||
this.width = 100;
|
||||
this.height = 60;
|
||||
this.radius = 5;
|
||||
}
|
||||
getNodeStyle() {
|
||||
return super.getNodeStyle();
|
||||
}
|
||||
}
|
||||
|
||||
class UserTaskView extends RectNode {
|
||||
// 获取标签形状的方法,用于在节点中添加一个自定义的 SVG 元素
|
||||
getShape() {
|
||||
// 获取XxxNodeModel中定义的形状属性
|
||||
const { model } = this.props;
|
||||
const { x, y, width, height, radius } = model;
|
||||
// 获取XxxNodeModel中定义的样式属性
|
||||
const style = model.getNodeStyle();
|
||||
|
||||
return h('g', {}, [
|
||||
h('rect', {
|
||||
...style,
|
||||
x: x - width / 2,
|
||||
y: y - height / 2,
|
||||
width,
|
||||
height,
|
||||
rx: radius,
|
||||
ry: radius,
|
||||
}),
|
||||
h(
|
||||
'svg',
|
||||
{
|
||||
x: x - width / 2,
|
||||
y: y - height / 2,
|
||||
width: 20,
|
||||
height: 20,
|
||||
viewBox: '0 0 1024 1024',
|
||||
},
|
||||
[
|
||||
h('path', {
|
||||
fill: style.stroke,
|
||||
d: 'M507.776 186.88c-90.765824 0-155.697664 69.77536-155.879936 149.255168v0.045056c0.005632 24.035328 6.509568 49.40288 16.67072 72.282624 7.33696 16.520704 16.459264 31.709184 27.575296 43.876352-66.06336 22.601216-143.458816 59.79904-182.578176 133.147648L211.456 589.44V826.88h592.64v-237.44l-2.107904-3.95264c-38.556672-72.2944-114.277888-109.44-179.70176-132.135424 31.938048-32.477696 41.35936-74.396672 41.3696-117.171712v-0.045056C663.473664 256.65536 598.541824 186.88 507.776 186.88zM445.803008 271.513088c4.195328 0.01024 8.801792 0.150528 13.88032 0.450048 40.459264 2.384384 54.076416 9.667584 64.543744 16.574976 10.466816 6.90688 17.84576 13.482496 45.508096 14.288896h0.017408c21.555712-0.8064 31.922688-4.649472 39.356928-9.003008 3.012608-1.76384 5.541888-3.597824 8.134144-5.348864 6.85056 14.685184 10.530304 30.919168 10.571776 47.719936-0.014336 47.84128-8.239104 81.344512-52.105216 108.761088l4.291072 32.34304c9.130496 2.77248 18.568192 5.814784 28.1472 9.150976 1.337856 5.5808 2.883584 12.900352 3.922944 20.681728 1.089024 8.152576 1.517568 16.634368 0.845824 23.003136-0.671232 6.368768-2.648576 9.806848-2.995712 10.153984-22.296064 22.296064-61.873664 35.298816-102.017024 35.298816-40.143872 0-79.72096-13.002752-102.017024-35.298816-0.347136-0.347136-2.32448-3.785216-2.996224-10.153984-0.671232-6.368768-0.2432-14.85056 0.846336-23.003136 1.044992-7.824384 2.603008-15.186944 3.945984-20.779008 9.483264-3.29728 18.82624-6.30784 27.86816-9.053696l2.55744-34.656256c-2.082816-2.671104-4.205056-4.440576-6.73792-6.34112-9.789952-7.34464-21.66272-23.502336-30.04928-42.38592-8.382976-18.876416-13.576704-40.45312-13.584384-57.724928 0.051712-20.707328 5.62432-40.556032 15.859712-57.700864 1.831424-0.681984 3.762688-1.402368 5.933056-2.116096 7.632896-2.510336 18.092544-4.906496 36.273152-4.860928zM368.312832 501.68576c-0.032256 0.23552-0.068096 0.464384-0.09984 0.700416-1.324544 9.913856-2.102784 20.70528-0.964096 31.506944 1.138688 10.801152 3.98848 22.43072 13.296128 31.737856 31.768576 31.768576 79.8464 45.796864 127.358976 45.796864 47.512064 0 95.5904-14.0288 127.358976-45.796864 9.307136-9.307136 12.15744-20.936704 13.296128-31.737856 1.138688-10.801664 0.360448-21.593088-0.964096-31.506944-0.026112-0.195584-0.05632-0.385536-0.082944-0.580096 48.299008 21.180928 94.98368 51.51488 120.743936 96.805888V791.04H682.496v-135.68h-35.84v135.68H368.128v-135.68h-35.84v135.68H247.296v-192.428032c25.808896-45.376512 72.621056-75.74016 121.016832-96.926208z',
|
||||
}),
|
||||
]
|
||||
),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
const nodeType = NodeTypeEnum.Parallel;
|
||||
const nodeTypeExtra = nodeType.extra;
|
||||
|
||||
export default {
|
||||
order: nodeTypeExtra.order,
|
||||
type: nodeType.value,
|
||||
// 注册配置信息
|
||||
// registerConf: {
|
||||
// type: nodeType.value,
|
||||
// model: UserTaskModel,
|
||||
// view: UserTaskView,
|
||||
// },
|
||||
dndPanelConf: {
|
||||
type: nodeType.value,
|
||||
text: nodeTypeExtra.text,
|
||||
label: nodeType.label,
|
||||
icon: 'data:image/svg+xml;charset=utf-8;base64,PHN2ZyB0PSIxNzQ1OTk5OTIwMTE3IiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwNzkgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHAtaWQ9IjM4MjciIHdpZHRoPSIzMiIgaGVpZ2h0PSIzMiI+PHBhdGggZD0iTTQ4NS44NzE0OTIgNDcuMTc0MzM4Yy05LjE2ODcyOS0wLjAwODA2Ni0xOC4zMzY1MSAzLjM5MjQ4Ny0yNS4xMzc2MTYgMTAuMjI0OTA4TDU3LjM4MTQyNCA0NjAuNzU1NDk0Yy0xMy42MDMxNjEgMTMuNjAyMjEyLTEzLjU0MDA1NiAzNi42NzQ0NDMgMC4wNjI2MzEgNTAuMjc2NjU1bDQwMy4yODg4NzIgNDAzLjI4NjAyNWMxMy42MDMxNjEgMTMuNjA2OTU2IDM2LjY3Mzk2OSAxMy42NjY3NCA1MC4yNzY2NTUgMGw0MDMuMzUyOTI2LTQwMy4zNTAwNzljMTMuNjAyNjg2LTEzLjYwMTczNyAxMy41Mzk1ODEtMzYuNjc0OTE4LTAuMDY0MDU0LTUwLjI3NzEzTDUxMS4wMDkxMDcgNTcuMzk5MjQ2Yy02LjgwMTEwNi02LjgwMTEwNi0xNS45NjkzNjEtMTAuMjE3MzE2LTI1LjEzNzYxNS0xMC4yMjQ5MDh6IG0tMC4wMzA4NDEgNTkuODA1MDM2bDM3OC45NDMxNTMgMzc4Ljk0Ni0zNzguOTQzMTUzIDM3OC45NDE3My0zNzguOTQzMTUzLTM3OC45NDE3MyAzNzguOTQzMTUzLTM3OC45NDZ6IG0tMTAuMzQ0OTUgMTU2LjQ5MDkxMWMwIDAuMDA0NzQ1LTQuNTgxOTkyIDAuODcyMDgzLTQuNTg0MzY1IDAuODcyMDgyLTAuMDA0NzQ1IDAtMy43NTIxMzggMi41MjU2MjMtMy43NTQ5ODQgMi41MjU2MjQtMC4wMDQ3NDUgMC4wMDQ3NDUtMi42MDgxODIgMy44NTYwNDgtMi42MTA1NTUgMy44NTYwNDcgMCAwLjAwNDc0NS0wLjg4MjUyMSA0LjU5MDUzMy0wLjg4Mzk0NCA0LjU5MDUzM1Y0NjMuNjEwNDAySDI3NS4yODM5MzhsLTAuMDMxNzktMC4wMzcwMDljMCAwLjAwNDc0NS00LjU2MzQ4OCAxLjAxMDE1NC00LjU2NTg2IDEuMDEwMTU1LTAuMDA0NzQ1IDAuMDA0NzQ1LTMuNzUxNjYzIDIuNTI1MTQ5LTMuNzU0MDM2IDIuNTI1MTQ5bDAuMDAzNzk2LTAuMDQ2NDk5Yy0wLjAwNDc0NSAwLjAwNDc0NS0yLjYwODE4MiAzLjg1NjA0OC0yLjYxMDU1NCAzLjg1NjA0OC0wLjAwNDc0NSAwLTAuODgyOTk2IDQuNTkxMDA3LTAuODg0ODkzIDQuNTkxMDA3djIwLjYyNTM3MXMwLjg3NjgyNyA0LjY0MTc3NiAwLjkwODE0MiA0LjY3MzU2NmMwIDAuMDA0NzQ1IDIuNTk5MTY3IDMuNzY0NDc0IDIuNTk5MTY3IDMuNzY0NDc0IDAuMDA0NzQ1IDAuMDA0NzQ1IDMuNzc1Mzg3IDIuNTI1MTQ5IDMuNzc2ODExIDIuNTI1MTQ5IDAuMDA0NzQ1IDAuMDA0NzQ1IDQuNTgxNTE4IDEuMDA5MjA2IDQuNTgzNDE1IDEuMDA5MjA2aDE4OC4zNTU2MTV2MTg4LjI2NDA0MWwtMC4wMjk4OTItMC4wMjc1MmMwIDAuMDA0NzQ1IDAuOTA5MDkyIDQuNjczNTY2IDAuOTA4NjE3IDQuNjczNTY2IDAgMC4wMDQ3NDUgMi41OTgyMTggMy43NjQgMi41OTgyMTggMy43NjQgMC4wMDQ3NDUgMC4wMDQ3NDUgMy43NzY4MTEgMi41MjQyIDMuNzc3Mjg1IDIuNTI0MiAwIDAgNC41NTAyMDMgMC45NjQxMzEgNC41ODQzNjUgMS4wMTA2MjlsMjAuNjIxMS0wLjAwNDc0NWMwLjAwNTIxOSAwIDQuNjcxNjY4LTAuOTY0MTMxIDQuNjc0NTE1LTAuOTY0MTMxIDAgMCAzLjc1MjEzOC0yLjUyNDIgMy43NTQ5ODUtMi41MjQyIDAuMDA0NzQ1LTAuMDA0NzQ1IDIuNTk1MzcxLTMuNzY1NDIzIDIuNTk4MjE4LTMuNzY1NDIzIDAgMCAwLjg5NTMzMi00LjY1OTMzMiAwLjg5Njc1NS00LjY1OTMzMVY1MDguMTI1NTIzaDE4OC4zMDkxMTZjMC4wMDUyMTkgMC4wMDQ3NDUgNC42NzIxNDItMC45NjM2NTYgNC42NzQwNC0wLjk2MzY1NiAwLjAwNDc0NSAwIDMuNzUzMDg3LTIuNTI1MTQ5IDMuNzU0OTg1LTIuNTI1MTQ5IDAuMDA0NzQ1LTAuMDA0NzQ1IDIuNTk1ODQ2LTMuNzY0NDc0IDIuNTk4NjkyLTMuNzY0NDc0IDAgMCAwLjg5NDg1Ny00LjY1OTMzMiAwLjg5Njc1Ni00LjY1OTMzMnYtMjAuNjIxMWMwLTAuMDA0NzQ1LTAuODkwNTg3LTQuNTQ1NDU4LTAuODg5MTY0LTQuNTkxMDA4LTAuMDA0NzQ1LTAuMDA0NzQ1LTIuNTg1ODgyLTMuODU2MDQ4LTIuNjE4MTQ2LTMuODU2MDQ3LTAuMDA0NzQ1LTAuMDA0NzQ1LTMuNzc1ODYyLTIuNTI0Mi0zLjc3NjgxLTIuNTI0MiAwIDAuMDA0NzQ1LTQuNjU5MzMyLTEuMDEwNjI5LTQuNjYxNzA0LTAuOTY0NjA1aC0xODguMjg5MTg4VjI3NS4zNjQ4NjZjMC0wLjAwNDc0NS0wLjg5MTA2Mi00LjU0NDAzNC0wLjg4OTYzOC00LjU5MDA1OS0wLjAwNDc0NS0wLjAwNDc0NS0yLjU4NTg4Mi0zLjg1Njk5Ny0yLjU4NTg4Mi0zLjg1Njk5Ni0wLjAwNDc0NS0wLjAwNDc0NS0zLjc3NDkxMy0yLjUyNDItMy43NzU4NjItMi41MjQyLTAuMDA0NzQ1IDAtNC42NzI2MTctMC45MTg1ODEtNC42NzQ1MTQtMC45MTg1ODJsLTIwLjYyNDg5Ny0wLjAwNDc0NHoiIHAtaWQ9IjM4MjgiPjwvcGF0aD48L3N2Zz4=',
|
||||
properties: nodeTypeExtra.defaultProp,
|
||||
},
|
||||
propSettingComp: PropSetting,
|
||||
};
|
||||
@@ -0,0 +1,16 @@
|
||||
<template>
|
||||
<el-tabs v-model="activeTabName">
|
||||
<!-- <el-tab-pane :label="$t('common.basic')" :name="basic"> </el-tab-pane> -->
|
||||
|
||||
<!-- <el-tab-pane :label="$t('')"> </el-tab-pane> -->
|
||||
</el-tabs>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
|
||||
const basic = 'basic';
|
||||
|
||||
const activeTabName = ref(basic);
|
||||
|
||||
const form = defineModel<any>('modelValue', { required: true });
|
||||
</script>
|
||||
@@ -0,0 +1,76 @@
|
||||
import { RectNode, RectNodeModel, h } from '@logicflow/core';
|
||||
import PropSetting from './PropSetting.vue';
|
||||
import { NodeTypeEnum } from '../enums';
|
||||
|
||||
class UserTaskModel extends RectNodeModel {
|
||||
initNodeData(data: any) {
|
||||
super.initNodeData(data);
|
||||
this.width = 100;
|
||||
this.height = 60;
|
||||
this.radius = 5;
|
||||
}
|
||||
getNodeStyle() {
|
||||
return super.getNodeStyle();
|
||||
}
|
||||
}
|
||||
|
||||
class UserTaskView extends RectNode {
|
||||
// 获取标签形状的方法,用于在节点中添加一个自定义的 SVG 元素
|
||||
getShape() {
|
||||
// 获取XxxNodeModel中定义的形状属性
|
||||
const { model } = this.props;
|
||||
const { x, y, width, height, radius } = model;
|
||||
// 获取XxxNodeModel中定义的样式属性
|
||||
const style = model.getNodeStyle();
|
||||
|
||||
return h('g', {}, [
|
||||
h('rect', {
|
||||
...style,
|
||||
x: x - width / 2,
|
||||
y: y - height / 2,
|
||||
width,
|
||||
height,
|
||||
rx: radius,
|
||||
ry: radius,
|
||||
}),
|
||||
h(
|
||||
'svg',
|
||||
{
|
||||
x: x - width / 2,
|
||||
y: y - height / 2,
|
||||
width: 20,
|
||||
height: 20,
|
||||
viewBox: '0 0 1024 1024',
|
||||
},
|
||||
[
|
||||
h('path', {
|
||||
fill: style.stroke,
|
||||
d: 'M507.776 186.88c-90.765824 0-155.697664 69.77536-155.879936 149.255168v0.045056c0.005632 24.035328 6.509568 49.40288 16.67072 72.282624 7.33696 16.520704 16.459264 31.709184 27.575296 43.876352-66.06336 22.601216-143.458816 59.79904-182.578176 133.147648L211.456 589.44V826.88h592.64v-237.44l-2.107904-3.95264c-38.556672-72.2944-114.277888-109.44-179.70176-132.135424 31.938048-32.477696 41.35936-74.396672 41.3696-117.171712v-0.045056C663.473664 256.65536 598.541824 186.88 507.776 186.88zM445.803008 271.513088c4.195328 0.01024 8.801792 0.150528 13.88032 0.450048 40.459264 2.384384 54.076416 9.667584 64.543744 16.574976 10.466816 6.90688 17.84576 13.482496 45.508096 14.288896h0.017408c21.555712-0.8064 31.922688-4.649472 39.356928-9.003008 3.012608-1.76384 5.541888-3.597824 8.134144-5.348864 6.85056 14.685184 10.530304 30.919168 10.571776 47.719936-0.014336 47.84128-8.239104 81.344512-52.105216 108.761088l4.291072 32.34304c9.130496 2.77248 18.568192 5.814784 28.1472 9.150976 1.337856 5.5808 2.883584 12.900352 3.922944 20.681728 1.089024 8.152576 1.517568 16.634368 0.845824 23.003136-0.671232 6.368768-2.648576 9.806848-2.995712 10.153984-22.296064 22.296064-61.873664 35.298816-102.017024 35.298816-40.143872 0-79.72096-13.002752-102.017024-35.298816-0.347136-0.347136-2.32448-3.785216-2.996224-10.153984-0.671232-6.368768-0.2432-14.85056 0.846336-23.003136 1.044992-7.824384 2.603008-15.186944 3.945984-20.779008 9.483264-3.29728 18.82624-6.30784 27.86816-9.053696l2.55744-34.656256c-2.082816-2.671104-4.205056-4.440576-6.73792-6.34112-9.789952-7.34464-21.66272-23.502336-30.04928-42.38592-8.382976-18.876416-13.576704-40.45312-13.584384-57.724928 0.051712-20.707328 5.62432-40.556032 15.859712-57.700864 1.831424-0.681984 3.762688-1.402368 5.933056-2.116096 7.632896-2.510336 18.092544-4.906496 36.273152-4.860928zM368.312832 501.68576c-0.032256 0.23552-0.068096 0.464384-0.09984 0.700416-1.324544 9.913856-2.102784 20.70528-0.964096 31.506944 1.138688 10.801152 3.98848 22.43072 13.296128 31.737856 31.768576 31.768576 79.8464 45.796864 127.358976 45.796864 47.512064 0 95.5904-14.0288 127.358976-45.796864 9.307136-9.307136 12.15744-20.936704 13.296128-31.737856 1.138688-10.801664 0.360448-21.593088-0.964096-31.506944-0.026112-0.195584-0.05632-0.385536-0.082944-0.580096 48.299008 21.180928 94.98368 51.51488 120.743936 96.805888V791.04H682.496v-135.68h-35.84v135.68H368.128v-135.68h-35.84v135.68H247.296v-192.428032c25.808896-45.376512 72.621056-75.74016 121.016832-96.926208z',
|
||||
}),
|
||||
]
|
||||
),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
const nodeType = NodeTypeEnum.Serial;
|
||||
const nodeTypeExtra = nodeType.extra;
|
||||
|
||||
export default {
|
||||
order: nodeTypeExtra.order,
|
||||
type: nodeType.value,
|
||||
// 注册配置信息
|
||||
// registerConf: {
|
||||
// type: nodeType.value,
|
||||
// model: UserTaskModel,
|
||||
// view: UserTaskView,
|
||||
// },
|
||||
dndPanelConf: {
|
||||
type: nodeType.value,
|
||||
text: nodeTypeExtra.text,
|
||||
label: nodeType.label,
|
||||
icon: 'data:image/svg+xml;charset=utf-8;base64,PHN2ZyB0PSIxNzQ1OTk5Njg2MzM0IiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwNzkgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHAtaWQ9IjM0MDciIHdpZHRoPSIzMiIgaGVpZ2h0PSIzMiI+PHBhdGggZD0iTTQ4NS44NzE0OTIgNDcuMTc0MzM4Yy05LjE2ODcyOS0wLjAwODA2Ni0xOC4zMzY1MSAzLjM5MjQ4Ny0yNS4xMzc2MTYgMTAuMjI0OTA4TDU3LjM4MTQyNCA0NjAuNzU1NDk0Yy0xMy42MDMxNjEgMTMuNjAyMjEyLTEzLjU0MDA1NiAzNi42NzQ0NDMgMC4wNjI2MzEgNTAuMjc2NjU1bDQwMy4yODg4NzIgNDAzLjI4NjAyNWMxMy42MDMxNjEgMTMuNjA2OTU2IDM2LjY3Mzk2OSAxMy42NjY3NCA1MC4yNzY2NTUgMGw0MDMuMzUyOTI2LTQwMy4zNTAwNzljMTMuNjAyNjg2LTEzLjYwMTczNyAxMy41Mzk1ODEtMzYuNjc0OTE4LTAuMDY0MDU0LTUwLjI3NzEzTDUxMS4wMDkxMDcgNTcuMzk5MjQ2Yy02LjgwMTEwNi02LjgwMTEwNi0xNS45NjkzNjEtMTAuMjE3MzE2LTI1LjEzNzYxNS0xMC4yMjQ5MDh6IG0tMC4wMzA4NDEgNTkuODA1MDM2bDM3OC45NDMxNTMgMzc4Ljk0Ni0zNzguOTQzMTUzIDM3OC45NDE3My0zNzguOTQzMTUzLTM3OC45NDE3MyAzNzguOTQzMTUzLTM3OC45NDZ6TTM0NC4zMTg5MSAzMTcuODI5MzExYy0wLjAwNjY0MyAwLTQuNTYwNjQxIDAuODcyMDgzLTQuNTY0NDM2IDAuODcyMDgzLTAuMDA0NzQ1IDAtMy44NjQxMTQgMi42MTU3NzMtMy44NjY5NjEgMi42MTU3NzNsLTE0LjU4MTUyNSAxNC41ODQ4NDdjLTAuMDA0NzQ1IDAtMi42NjE3OTcgMy45MDI1NDYtMi42NjM2OTYgMy45NDg1NyAwIDAuMDA0NzQ1LTAuODI0MTYxIDQuNDk4MDExLTAuODIzNjg2IDQuNDk4MDExIDAgMC4wMDQ3NDUgMC44ODYzMTcgNC40NTI5MzYgMC44ODc3NCA0LjQ1MjkzNSAwIDAuMDA0NzQ1IDIuNTMyNzQxIDMuOTQ4NTcgMi41MzU1ODggMy45NDg1N2wxMzMuMTg4MDg0IDEzMy4xODQ3NjMtMTMzLjEyNDAzIDEzMy4xMjQ5OHYtMC4wNDE3NTRjMCAwLjAwNDc0NS0yLjY2MTc5NyAzLjk0NzYyMS0yLjY2MzY5NiAzLjk0NzYyMSAwIDAuMDA0NzQ1LTAuODIzNjg2IDQuNDk5NDM0LTAuODIzNjg2IDQuNDk5NDM0IDAgMC4wMDk0ODkgMC44ODYzMTcgNC40NTI5MzYgMC44ODc3NCA0LjQ1MjkzNiAwIDAgMi41MzMyMTUgMy45MDE1OTcgMi41MzU1ODggMy45NDc2MjFsMTQuNTgyNDc0IDE0LjU3OTYyN2MwLjAwNDc0NSAwLjAwNDc0NSAzLjk5MDc5OCAyLjYxNzE5NyAzLjk5NDExOSAyLjYxNzE5NyAwLjAwNDc0NSAwIDQuNDM0NDMxIDAuODcyMDgzIDQuNDM4MjI3IDAuODcyMDgzIDAuMDA0NzQ1IDAgNC40OTc1MzYtMC44MjU1ODQgNC41MDA4NTgtMC44MjU1ODQgMC4wMDQ3NDUgMCAzLjkyODY0Mi0yLjY2MzY5NSAzLjkzMTAxNC0yLjY2MzY5NmwxMzMuMTI1OTI5LTEzMy4xMjg3NzUgMTMzLjE1NDg3MSAxMzMuMTU2NzY5YzAuMDA0NzQ1IDAuMDA0NzQ1IDMuOTkxMjczIDIuNjE3MTk3IDMuOTk0MTIgMi42MTcxOTcgMC4wMDQ3NDUgMCA0LjQzNDQzMSAwLjg3MjA4MyA0LjQzODIyNiAwLjg3MjA4MyAwLjAwNDc0NSAwIDQuNDk4MDExLTAuODI3MDA4IDQuNTAxODA3LTAuODI3MDA4IDAuMDA0NzQ1IDAgMy45MjY3NDQtMi42NjE3OTcgMy45MjkxMTYtMi42NjE3OTdsMTQuNTgyOTQ5LTE0LjU4MDU3N2MwLjAwNDc0NS0wLjAwNDc0NSAyLjU5Nzc0My0zLjg1NTU3MyAyLjYwMDExNi0zLjg1NTU3MyAwLTAuMDA0NzQ1IDAuODg3NzQtNC41NDU0NTggMC44ODc3NC00LjU5MTAwNyAwLTAuMDA0NzQ1LTAuODg2NzkxLTQuNDUyOTM2LTAuODg4Njg5LTQuNDUyOTM2IDAgMC0yLjU5NjMyLTMuOTk0MTE5LTIuNTk5MTY3LTMuOTk0MTE5bC0xMzMuMTQwMTYzLTEzMy4xNDI1MzUgMTMzLjE0MTExMi0xMzMuMTM5MjE0YzAuMDA0NzQ1IDAgMi41OTY3OTQtMy44NTYwNDggMi41OTkxNjctMy44NTYwNDggMC0wLjAwNDc0NSAwLjg4Nzc0LTQuNTQ0NTA5IDAuODg3NzQtNC41NDQ1MDkgMC0wLjAwOTQ4OS0wLjg4NjMxNy00LjQ1MjkzNi0wLjg4NzI2Ni00LjQ1MjkzNSAwLTAuMDA0NzQ1LTIuNjU5ODk5LTMuOTQ4NTctMi42NjI3NDYtMy45NDg1N2wtMTQuNTgyOTQ5LTE0LjU4NDM3MmMtMC4wMDQ3NDUgMC0zLjg2MzYzOS0yLjYxNjI0OC0zLjg2Njk2LTIuNjE2MjQ4LTAuMDA0NzQ1IDAtNC40MzM5NTctMC44NzMwMzItNC40Mzc3NTMtMC44NzMwMzItMC4wMDQ3NDUgMC00LjU2MTExNiAwLjg3MzAzMi00LjU2NDQzNiAwLjg3MzAzMi0wLjAwNDc0NSAwLTMuODY0NTg4IDIuNjE2MjQ4LTMuODY2OTYxIDIuNjE2MjQ4bC0xMzMuMTQzNDg0IDEzMy4xNDM0ODQtMTMzLjIwMzI2OC0xMzMuMjA3NTM4di0wLjA0MTc1NGMtMC4wMDQ3NDUgMC0zLjkyNzY5My0yLjUyNDItMy45MzEwMTQtMi41MjQyLTAuMDA0NzQ1IDAtNC40MzE1ODQtMC44NzE2MDgtNC40MzY4MDQtMC44NzIwODNoLTAuMDAwOTQ5eiIgcC1pZD0iMzQwOCI+PC9wYXRoPjwvc3ZnPg==',
|
||||
properties: nodeTypeExtra.defaultProp,
|
||||
},
|
||||
propSettingComp: PropSetting,
|
||||
};
|
||||
@@ -0,0 +1,12 @@
|
||||
<template>
|
||||
<el-tabs v-model="activeTabName"> </el-tabs>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
|
||||
const basicTabName = 'basic';
|
||||
|
||||
const activeTabName = ref(basicTabName);
|
||||
|
||||
const form = defineModel<any>('modelValue', { required: true });
|
||||
</script>
|
||||
@@ -0,0 +1,56 @@
|
||||
import { CircleNode, CircleNodeModel } from '@logicflow/core';
|
||||
import PropSetting from './PropSetting.vue';
|
||||
import { NodeTypeEnum } from '../enums';
|
||||
import { HisProcinstOpState } from '@/views/flow/enums';
|
||||
|
||||
class StartModel extends CircleNodeModel {
|
||||
initNodeData(data: any) {
|
||||
super.initNodeData(data);
|
||||
this.r = 20;
|
||||
}
|
||||
|
||||
getNodeStyle() {
|
||||
const style = super.getNodeStyle();
|
||||
const properties = this.properties;
|
||||
|
||||
const opLog: any = properties.opLog;
|
||||
if (!opLog) {
|
||||
return style;
|
||||
}
|
||||
|
||||
if (opLog.state == HisProcinstOpState.Completed.value) {
|
||||
style.stroke = 'green';
|
||||
} else if (opLog.state == HisProcinstOpState.Failed.value) {
|
||||
style.stroke = 'red';
|
||||
} else {
|
||||
style.stroke = 'rgb(24, 125, 255)';
|
||||
}
|
||||
|
||||
return style;
|
||||
}
|
||||
}
|
||||
|
||||
class StartView extends CircleNode {}
|
||||
|
||||
const nodeType = NodeTypeEnum.Start;
|
||||
const nodeTypeExtra = nodeType.extra;
|
||||
|
||||
export default {
|
||||
order: nodeTypeExtra.order,
|
||||
type: nodeType.value,
|
||||
// 注册配置信息
|
||||
registerConf: {
|
||||
type: nodeType.value,
|
||||
model: StartModel,
|
||||
view: StartView,
|
||||
},
|
||||
// 拖拽面板配置
|
||||
dndPanelConf: {
|
||||
type: nodeType.value,
|
||||
text: nodeTypeExtra.text,
|
||||
label: nodeType.label,
|
||||
icon: 'data:image/svg+xml;charset=utf-8;base64,PHN2ZyB0PSIxNzQ1ODg4MTUzNDkzIiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwNzggMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHAtaWQ9IjE5MjUiIHdpZHRoPSIzMiIgaGVpZ2h0PSIzMiI+PHBhdGggZD0iTTQ4Mi4wMjQ5NDcgNDYuOTUzNjM5QzMxMC44Mjg4ODkgNDYuMzg1MDM5IDE0Ni41NTkwMjMgMTU3LjAwMjg5MiA4MS41ODM0MjMgMzE1LjI0MDc1NmMtNjguNDAxOTE0IDE1Ni42OTg2NTctMzIuODYxNTY3IDM1MS4zNTY4MjIgODYuNzkwNzYgNDczLjU2NjkgMTE1LjA3ODg0NyAxMjMuODczMTYyIDMwNC42ODQ2MzUgMTY5LjMzOTMyOCA0NjMuNDgwMTgzIDExMS4zODEwNDggMTY0LjU3MDc3Ny01Ni4wNDA3OTcgMjg2LjA2NzAyLTIxNy42NTk4ODUgMjkyLjY3MTQxOC0zOTEuNjA4NzY1IDExLjA3NzczMy0xNzAuOTI2NDcyLTg5LjQ4MzMwNC0zNDEuNDU0NzM0LTI0My40NTk5OTEtNDE1LjkxMDAwN0M2MjAuNzcxODg3IDYyLjU4MjA3IDU1My40NDI2MjQgNDYuODkxNDYzIDQ4Ni4wNzAxNzEgNDYuOTg3ODEyYTM5NS4wMjk4NTcgMzk1LjAyOTg1NyAwIDAgMC00LjA0NTIyNC0wLjAzNDE3M3ogbTEyLjAzMjIwMiA0Ny40NDkxNDdjMTY3LjU3NzA0OCAwLjkyMDI5NyAzMjUuNDA0ODM2IDEyMi4xNDkzMjYgMzY4Ljc0NDIxMSAyODQuMzE0MjMyIDQ2LjA1MjgwMiAxNTUuMTU4MDI3LTE2LjIzODc5OCAzMzQuODAyMzk5LTE0OS45MzY2ODQgNDI2LjY5OTE2OEM1NzMuOTgzNDE3IDkwNy40OTYwMjEgMzY4LjAzMTA5MSA5MDAuMTEwODY2IDIzNi44ODE0NjQgNzg4LjI1NjE0MmMtMTMyLjE0MjA2Ny0xMDUuMzU3MTE2LTE3OS40NDIxODItMzAwLjIzNTk4MS0xMTEuNjQ0NDY1LTQ1NC43NzY1MjFDMTgzLjk5ODgxNyAxOTAuNjExNTE3IDMzMS41MDMwNTEgOTIuNjM3MTgzIDQ4Ni4wNzAxNzEgOTQuNDUwMjQ4YzIuNjY0NTQxLTAuMDQ2NTEzIDUuMzI3MTg0LTAuMDYxNzAxIDcuOTg2OTc4LTAuMDQ3NDYyeiIgcC1pZD0iMTkyNiI+PC9wYXRoPjwvc3ZnPg==',
|
||||
properties: nodeTypeExtra.defaultProp,
|
||||
},
|
||||
propSettingComp: PropSetting,
|
||||
};
|
||||
@@ -0,0 +1,147 @@
|
||||
<template>
|
||||
<el-tabs v-model="activeTabName">
|
||||
<el-tab-pane :name="approvalRecordTabName" v-if="activeTabName == approvalRecordTabName" :label="$t('flow.approvalRecord')">
|
||||
<el-table :data="props.node?.properties?.tasks" stripe width="100%">
|
||||
<el-table-column :label="$t('common.time')" min-width="135">
|
||||
<template #default="scope">
|
||||
{{ formatDate(scope.row.endTime) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column :label="$t('flow.approver')" min-width="100">
|
||||
<template #default="scope">
|
||||
<AccountInfo :username="scope.row.handler" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column :label="$t('flow.approveResult')" width="80">
|
||||
<template #default="scope">
|
||||
<EnumTag :enums="ProcinstTaskStatus" :value="scope.row.status" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column :label="$t('flow.approvalRemark')" min-width="150">
|
||||
<template #default="scope">
|
||||
{{ scope.row.remark }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane :label="$t('common.basic')" :name="basicTabName">
|
||||
<el-form-item prop="completionCondition" :label="$t('flow.approvalMode')" :rules="[Rules.requiredSelect('flow.approvalMode')]">
|
||||
<el-radio-group v-model="form.completionCondition">
|
||||
<el-radio value="{{ eq .nrOfCompleted 1.0 }}">{{ $t('flow.orSign') }}</el-radio>
|
||||
<el-radio value="{{ eq .nrOfAll .nrOfCompleted }}">{{ $t('flow.andSign') }}</el-radio>
|
||||
<!-- <el-radio value="3">{{ $t('flow.voteSign') }}</el-radio> -->
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label-position="top" :label="$t('flow.taskCandidate')">
|
||||
<el-table :data="taskCandidates" stripe>
|
||||
<el-table-column :label="$t('common.type')" width="150">
|
||||
<template #header>
|
||||
<el-button class="ml-0" type="primary" circle size="small" icon="Plus" @click="onAddCandidate"> </el-button>
|
||||
<span class="ml-2">{{ $t('common.type') }}</span>
|
||||
</template>
|
||||
<template #default="scope">
|
||||
<EnumSelect :enums="UserTaskCandidateType" v-model="scope.row.type" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column :label="$t('common.name')" min-width="150">
|
||||
<template #default="scope">
|
||||
<AccountSelectFormItem label="" v-if="scope.row.type == UserTaskCandidateType.Account.value" v-model="scope.row.id" />
|
||||
<RoleSelectFormItem label="" v-else-if="scope.row.type == UserTaskCandidateType.Role.value" v-model="scope.row.id" />
|
||||
<el-input v-else v-model="scope.row.name" clearable> </el-input>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column :label="$t('common.operation')" min-width="50">
|
||||
<template #default="scope">
|
||||
<el-button type="danger" @click="onDeleteCandidate(scope.$index, scope.row)" icon="delete" plain></el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-form-item>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { notEmpty } from '@/common/assert';
|
||||
import { Rules } from '@/common/rule';
|
||||
import { formatDate } from '@/common/utils/format';
|
||||
import EnumSelect from '@/components/enumselect/EnumSelect.vue';
|
||||
import EnumTag from '@/components/enumtag/EnumTag.vue';
|
||||
import { useI18nPleaseSelect } from '@/hooks/useI18n';
|
||||
import { ProcinstTaskStatus, UserTaskCandidateType } from '@/views/flow/enums';
|
||||
import AccountInfo from '@/views/system/account/components/AccountInfo.vue';
|
||||
import AccountSelectFormItem from '@/views/system/account/components/AccountSelectFormItem.vue';
|
||||
import RoleSelectFormItem from '@/views/system/role/components/RoleSelectFormItem.vue';
|
||||
import { computed, onMounted, Ref, ref, watch } from 'vue';
|
||||
|
||||
const props = defineProps({
|
||||
// 节点信息
|
||||
node: {
|
||||
type: Object,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
|
||||
const basicTabName = 'basic';
|
||||
const approvalRecordTabName = 'approvalRecord';
|
||||
|
||||
const activeTabName = computed(() => {
|
||||
// 如果存在审批记录 tasks 且长度大于0,则激活审批记录 tab
|
||||
if (props.node?.properties?.tasks && props.node.properties.tasks.length > 0) {
|
||||
return approvalRecordTabName;
|
||||
}
|
||||
return basicTabName;
|
||||
});
|
||||
|
||||
const form: any = defineModel<any>('modelValue', { required: true });
|
||||
|
||||
const taskCandidates: Ref<any> = ref([]);
|
||||
|
||||
onMounted(() => {
|
||||
const rawCandidates = form.value?.candidates || [];
|
||||
taskCandidates.value = rawCandidates.map((item: any) => {
|
||||
if (item && typeof item === 'object') {
|
||||
return item;
|
||||
}
|
||||
|
||||
if (item.indexOf(':') == -1) {
|
||||
return { type: UserTaskCandidateType.Account.value, id: Number.parseInt(item) };
|
||||
}
|
||||
|
||||
let [type, id] = item.split(':');
|
||||
if (type == '') {
|
||||
type = UserTaskCandidateType.Account.value;
|
||||
}
|
||||
return { type: type, id: Number.parseInt(id) };
|
||||
});
|
||||
});
|
||||
|
||||
const onAddCandidate = () => {
|
||||
// 往数组头部添加元素
|
||||
taskCandidates.value = [...(taskCandidates.value || []), {}];
|
||||
};
|
||||
|
||||
const onDeleteCandidate = async (idx: any, row: any) => {
|
||||
taskCandidates.value.splice(idx, 1);
|
||||
};
|
||||
|
||||
const confirm = () => {
|
||||
notEmpty(taskCandidates.value, useI18nPleaseSelect('flow.taskCandidate'));
|
||||
form.value.candidates = taskCandidates.value.map((x: any) => {
|
||||
if (x.type == UserTaskCandidateType.Account.value) {
|
||||
return `${x.id}`;
|
||||
}
|
||||
return `${x.type}:${x.id}`;
|
||||
});
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
confirm,
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,99 @@
|
||||
import { RectNode, RectNodeModel, h } from '@logicflow/core';
|
||||
import PropSetting from './PropSetting.vue';
|
||||
import { NodeTypeEnum } from '../enums';
|
||||
import { HisProcinstOpState, ProcinstTaskStatus } from '@/views/flow/enums';
|
||||
|
||||
class UserTaskModel extends RectNodeModel {
|
||||
initNodeData(data: any) {
|
||||
super.initNodeData(data);
|
||||
this.width = 100;
|
||||
this.height = 60;
|
||||
this.radius = 5;
|
||||
}
|
||||
|
||||
getNodeStyle() {
|
||||
const style = super.getNodeStyle();
|
||||
const properties = this.properties;
|
||||
|
||||
const opLog: any = properties.opLog;
|
||||
if (!opLog) {
|
||||
return style;
|
||||
}
|
||||
|
||||
if (opLog.state == HisProcinstOpState.Completed.value && opLog.extra) {
|
||||
if (opLog.extra.approvalResult == ProcinstTaskStatus.Pass.value) {
|
||||
style.stroke = 'green';
|
||||
} else {
|
||||
style.stroke = 'red';
|
||||
}
|
||||
} else if (opLog.state == HisProcinstOpState.Failed.value) {
|
||||
style.stroke = 'red';
|
||||
} else {
|
||||
style.stroke = 'rgb(24, 125, 255)';
|
||||
}
|
||||
|
||||
return style;
|
||||
}
|
||||
}
|
||||
|
||||
class UserTaskView extends RectNode {
|
||||
// 获取标签形状的方法,用于在节点中添加一个自定义的 SVG 元素
|
||||
getShape() {
|
||||
// 获取XxxNodeModel中定义的形状属性
|
||||
const { model } = this.props;
|
||||
console.log(model.properties);
|
||||
const { x, y, width, height, radius } = model;
|
||||
// 获取XxxNodeModel中定义的样式属性
|
||||
const style = model.getNodeStyle();
|
||||
|
||||
return h('g', {}, [
|
||||
h('rect', {
|
||||
...style,
|
||||
x: x - width / 2,
|
||||
y: y - height / 2,
|
||||
width,
|
||||
height,
|
||||
rx: radius,
|
||||
ry: radius,
|
||||
}),
|
||||
h(
|
||||
'svg',
|
||||
{
|
||||
x: x - width / 2,
|
||||
y: y - height / 2,
|
||||
width: 20,
|
||||
height: 20,
|
||||
viewBox: '0 0 1024 1024',
|
||||
},
|
||||
[
|
||||
h('path', {
|
||||
fill: style.stroke,
|
||||
d: 'M507.776 186.88c-90.765824 0-155.697664 69.77536-155.879936 149.255168v0.045056c0.005632 24.035328 6.509568 49.40288 16.67072 72.282624 7.33696 16.520704 16.459264 31.709184 27.575296 43.876352-66.06336 22.601216-143.458816 59.79904-182.578176 133.147648L211.456 589.44V826.88h592.64v-237.44l-2.107904-3.95264c-38.556672-72.2944-114.277888-109.44-179.70176-132.135424 31.938048-32.477696 41.35936-74.396672 41.3696-117.171712v-0.045056C663.473664 256.65536 598.541824 186.88 507.776 186.88zM445.803008 271.513088c4.195328 0.01024 8.801792 0.150528 13.88032 0.450048 40.459264 2.384384 54.076416 9.667584 64.543744 16.574976 10.466816 6.90688 17.84576 13.482496 45.508096 14.288896h0.017408c21.555712-0.8064 31.922688-4.649472 39.356928-9.003008 3.012608-1.76384 5.541888-3.597824 8.134144-5.348864 6.85056 14.685184 10.530304 30.919168 10.571776 47.719936-0.014336 47.84128-8.239104 81.344512-52.105216 108.761088l4.291072 32.34304c9.130496 2.77248 18.568192 5.814784 28.1472 9.150976 1.337856 5.5808 2.883584 12.900352 3.922944 20.681728 1.089024 8.152576 1.517568 16.634368 0.845824 23.003136-0.671232 6.368768-2.648576 9.806848-2.995712 10.153984-22.296064 22.296064-61.873664 35.298816-102.017024 35.298816-40.143872 0-79.72096-13.002752-102.017024-35.298816-0.347136-0.347136-2.32448-3.785216-2.996224-10.153984-0.671232-6.368768-0.2432-14.85056 0.846336-23.003136 1.044992-7.824384 2.603008-15.186944 3.945984-20.779008 9.483264-3.29728 18.82624-6.30784 27.86816-9.053696l2.55744-34.656256c-2.082816-2.671104-4.205056-4.440576-6.73792-6.34112-9.789952-7.34464-21.66272-23.502336-30.04928-42.38592-8.382976-18.876416-13.576704-40.45312-13.584384-57.724928 0.051712-20.707328 5.62432-40.556032 15.859712-57.700864 1.831424-0.681984 3.762688-1.402368 5.933056-2.116096 7.632896-2.510336 18.092544-4.906496 36.273152-4.860928zM368.312832 501.68576c-0.032256 0.23552-0.068096 0.464384-0.09984 0.700416-1.324544 9.913856-2.102784 20.70528-0.964096 31.506944 1.138688 10.801152 3.98848 22.43072 13.296128 31.737856 31.768576 31.768576 79.8464 45.796864 127.358976 45.796864 47.512064 0 95.5904-14.0288 127.358976-45.796864 9.307136-9.307136 12.15744-20.936704 13.296128-31.737856 1.138688-10.801664 0.360448-21.593088-0.964096-31.506944-0.026112-0.195584-0.05632-0.385536-0.082944-0.580096 48.299008 21.180928 94.98368 51.51488 120.743936 96.805888V791.04H682.496v-135.68h-35.84v135.68H368.128v-135.68h-35.84v135.68H247.296v-192.428032c25.808896-45.376512 72.621056-75.74016 121.016832-96.926208z',
|
||||
}),
|
||||
]
|
||||
),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
const nodeType = NodeTypeEnum.UserTask;
|
||||
const nodeTypeExtra = nodeType.extra;
|
||||
|
||||
export default {
|
||||
order: nodeTypeExtra.order,
|
||||
type: nodeType.value,
|
||||
// 注册配置信息
|
||||
registerConf: {
|
||||
type: nodeType.value,
|
||||
model: UserTaskModel,
|
||||
view: UserTaskView,
|
||||
},
|
||||
dndPanelConf: {
|
||||
type: nodeType.value,
|
||||
text: nodeTypeExtra.text,
|
||||
label: nodeType.label,
|
||||
icon: 'data:image/svg+xml;charset=utf-8;base64,PHN2ZyB0PSIxNzQ1ODg4ODY2Mjc1IiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHAtaWQ9IjI4OTEiIHdpZHRoPSIzMiIgaGVpZ2h0PSIzMiI+PHBhdGggZD0iTTMzNS43NTE2OCAyNDQuNzgzMTA0Yy01Mi4xNDQ2NCAwLTg5LjQ0NzkzNiA0MC4wODYwMTYtODkuNTUyMzg0IDg1Ljc0NjY4OHYwLjAyNTZjMC4wMDMwNzIgMTMuODA4NjQgMy43Mzk2NDggMjguMzgxNjk2IDkuNTc3NDcyIDQxLjUyNjI3MiA0LjIxNTI5NiA5LjQ5MTQ1NiA5LjQ1NTYxNiAxOC4yMTY5NiAxNS44NDEyOCAyNS4yMDY3ODQtMzcuOTUyNTEyIDEyLjk4NDMyLTgyLjQxNjEyOCAzNC4zNTQ2ODgtMTA0Ljg4OTg1NiA3Ni40OTI4bC0xLjIxMTM5MiAyLjI3MTIzMnYxMzYuNDA4NTc2aDM0MC40Njk3NlY0NzYuMDUyNDhsLTEuMjEwODgtMi4yNzA3MmMtMjIuMTUwNjU2LTQxLjUzMjkyOC02NS42NTIyMjQtNjIuODczMDg4LTEwMy4yMzgxNDQtNzUuOTExMTY4IDE4LjM0ODAzMi0xOC42NTgzMDQgMjMuNzYwODk2LTQyLjc0MDczNiAyMy43NjcwNC02Ny4zMTUydi0wLjAyNTZjLTAuMTA0OTYtNDUuNjYwNjcyLTM3LjQwNzc0NC04NS43NDY2ODgtODkuNTUyODk2LTg1Ljc0NjY4OHogbS0zNS42MDI5NDQgNDguNjIxNTY4YzIuNDA5OTg0IDAuMDA2MTQ0IDUuMDU2NTEyIDAuMDg2NTI4IDcuOTczODg4IDAuMjU4NTYgMjMuMjQzNzc2IDEuMzY5NiAzMS4wNjcxMzYgNS41NTM2NjQgMzcuMDgwMDY0IDkuNTIyMTc2IDYuMDEzNDQgMy45NjggMTAuMjUyOCA3Ljc0NTUzNiAyNi4xNDQyNTYgOC4yMDg4OTZoMC4wMTAyNGMxMi4zODM3NDQtMC40NjMzNiAxOC4zMzk4NC0yLjY3MTEwNCAyMi42MDk5Mi01LjE3MjIyNCAxLjczMDU2LTEuMDEzNzYgMy4xODQ2NC0yLjA2Njk0NCA0LjY3MzUzNi0zLjA3MzAyNCAzLjkzNTc0NCA4LjQzNjczNiA2LjA0OTI4IDE3Ljc2MjgxNiA2LjA3MzM0NCAyNy40MTUwNC0wLjAwODE5MiAyNy40ODQ2NzItNC43MzM0NCA0Ni43MzIyODgtMjkuOTM0MDggNjIuNDgyOTQ0bDIuNDY1MjggMTguNTgwNDhhNDIyLjQwMjU2IDQyMi40MDI1NiAwIDAgMSAxNi4xNzA0OTYgNS4yNTc3MjhjMC43NjggMy4yMDYxNDQgMS42NTYzMiA3LjQxMTIgMi4yNTMzMTIgMTEuODgxNDcyIDAuNjI1NjY0IDQuNjgzNzc2IDAuODcxOTM2IDkuNTU2NDggMC40ODY0IDEzLjIxNTIzMi0wLjM4NjA0OCAzLjY1ODc1Mi0xLjUyMjE3NiA1LjYzNDA0OC0xLjcyMTM0NCA1LjgzMzcyOC0xMi44MDkyMTYgMTIuODA4NzA0LTM1LjU0NjExMiAyMC4yNzg3ODQtNTguNjA4NjQgMjAuMjc4Nzg0LTIzLjA2MjAxNiAwLTQ1Ljc5OTQyNC03LjQ3MDA4LTU4LjYwODY0LTIwLjI3ODc4NC0wLjE5OTE2OC0wLjE5OTY4LTEuMzM1Mjk2LTIuMTc0OTc2LTEuNzIwODMyLTUuODMzNzI4LTAuMzg1NTM2LTMuNjU4NzUyLTAuMTM5Nzc2LTguNTMxNDU2IDAuNDg2NC0xMy4yMTQ3MiAwLjYwMDA2NC00LjQ5NTM2IDEuNDk1MDQtOC43MjU1MDQgMi4yNjY2MjQtMTEuOTM3NzkyYTQyMi45ODIxNDQgNDIyLjk4MjE0NCAwIDAgMSAxNi4wMTAyNC01LjIwMTkybDEuNDY5NDQtMTkuOTA5NjMyYy0xLjE5NjU0NC0xLjUzNDQ2NC0yLjQxNTYxNi0yLjU1MDc4NC0zLjg3MDcyLTMuNjQyODgtNS42MjQ4MzItNC4yMTg4OC0xMi40NDUxODQtMTMuNTAxOTUyLTE3LjI2MzEwNC0yNC4zNTA3Mi00LjgxNjM4NC0xMC44NDQxNi03LjgwMDMyLTIzLjIzOTY4LTcuODA0OTI4LTMzLjE2MjI0IDAuMDMwMjA4LTExLjg5NjgzMiAzLjIzMTc0NC0yMy4yOTk1ODQgOS4xMTE1NTItMzMuMTQ5NDQgMS4wNTIxNi0wLjM5MTY4IDIuMTYxNjY0LTAuODA1Mzc2IDMuNDA4Mzg0LTEuMjE1NDg4IDQuMzg1MjgtMS40NDIzMDQgMTAuMzk0MTEyLTIuODE5MDcyIDIwLjgzODkxMi0yLjc5MjQ0OHogbS00NC41MTg0IDEzMi4yMzMyMTZjLTAuMDE3OTIgMC4xMzUxNjgtMC4wMzg5MTIgMC4yNjY3NTItMC4wNTY4MzIgMC40MDI0MzItMC43NjA4MzIgNS42OTU0ODgtMS4yMDgzMiAxMS44OTUyOTYtMC41NTM5ODQgMTguMTAwNzM2IDAuNjU0MzM2IDYuMjA1NDQgMi4yOTE3MTIgMTIuODg2NTI4IDcuNjM4NTI4IDE4LjIzMzM0NCAxOC4yNTA3NTIgMTguMjUwNzUyIDQ1Ljg3MTYxNiAyNi4zMTAxNDQgNzMuMTY3MzYgMjYuMzEwMTQ0IDI3LjI5NTIzMiAwIDU0LjkxNjYwOC04LjA1ODg4IDczLjE2NzM2LTI2LjMxMDE0NCA1LjM0NjgxNi01LjM0NjgxNiA2Ljk4NDE5Mi0xMi4wMjc5MDQgNy42Mzg1MjgtMTguMjMzMzQ0IDAuNjUzODI0LTYuMjA1NDQgMC4yMDY4NDgtMTIuNDA1MjQ4LTAuNTUzOTg0LTE4LjEwMDczNi0wLjAxNTM2LTAuMTEyNjQtMC4wMzIyNTYtMC4yMjExODQtMC4wNDc2MTYtMC4zMzI4IDI3Ljc0NzMyOCAxMi4xNjc2OCA1NC41Njc5MzYgMjkuNTk0NjI0IDY5LjM2Njc4NCA1NS42MTQ0NjR2MTEwLjU0ODk5MmgtNDkuMjY4NzM2di03Ny45NDczOTJoLTIwLjU5MDA4djc3Ljk0NzM5MkgyNTUuNTI0MzUydi03Ny45NDczOTJoLTIwLjU4OTU2OHY3Ny45NDczOTJIMTg2LjEwNjg4VjQ4MS4zMjE5ODRjMTQuODI3NTItMjYuMDY4NDggNDEuNzIwODMyLTQzLjUxMjMyIDY5LjUyMzk2OC01NS42ODQwOTZ6TTIxOS45ODA4IDEwNy41MkMxMTAuMDQ2MjA4IDEwNy41MiAyMC40OCAxOTYuNzA3ODQgMjAuNDggMzA2LjQzMnY0MTEuMTM2YzAgMTA5LjcyMzY0OCA4OS41NjYyMDggMTk4LjkxMiAxOTkuNTAwOCAxOTguOTEyaDU4NC4wMzg0YzEwOS45MzQ1OTIgMCAxOTkuNTAwOC04OS4xODgzNTIgMTk5LjUwMDgtMTk4LjkxMnYtNDExLjEzNmMwLTEwOS43MjM2NDgtODkuNTY2MjA4LTE5OC45MTItMTk5LjUwMDgtMTk4LjkxMkgyMTkuOTgwOHogbTAgNjEuNDRoNTg0LjAzODRDODgxLjA5NDE0NCAxNjguOTYgOTQyLjA4IDIyOS43OTg0IDk0Mi4wOCAzMDYuNDMydjQxMS4xMzZjMCA3Ni42MzMwODgtNjAuOTg1ODU2IDEzNy40NzItMTM4LjA2MDggMTM3LjQ3MkgyMTkuOTgwOEMxNDIuOTA1ODU2IDg1NS4wNCA4MS45MiA3OTQuMjAwNTc2IDgxLjkyIDcxNy41Njh2LTQxMS4xMzZDODEuOTIgMjI5Ljc5ODQgMTQyLjkwNTg1NiAxNjguOTYgMjE5Ljk4MDggMTY4Ljk2eiIgcC1pZD0iMjg5MiI+PC9wYXRoPjwvc3ZnPg==',
|
||||
properties: nodeTypeExtra.defaultProp,
|
||||
},
|
||||
propSettingComp: PropSetting,
|
||||
};
|
||||
@@ -5,6 +5,12 @@ export const ProcdefStatus = {
|
||||
Disable: EnumValue.of(-1, 'flow.disable').setTagType('warning'),
|
||||
};
|
||||
|
||||
export const UserTaskCandidateType = {
|
||||
Account: EnumValue.of('account', 'common.account'),
|
||||
Role: EnumValue.of('role', 'common.role'),
|
||||
Other: EnumValue.of('other', 'common.other'),
|
||||
};
|
||||
|
||||
export const ProcinstStatus = {
|
||||
Active: EnumValue.of(1, 'flow.active').setTagType('primary'),
|
||||
Completed: EnumValue.of(2, 'flow.completed').setTagType('success'),
|
||||
@@ -28,6 +34,12 @@ export const ProcinstTaskStatus = {
|
||||
Canceled: EnumValue.of(-3, 'flow.canceled').setTagType('warning'),
|
||||
};
|
||||
|
||||
export const HisProcinstOpState = {
|
||||
Pending: EnumValue.of(1, 'flow.waitProcess').setTagType('primary'),
|
||||
Completed: EnumValue.of(2, 'flow.pass').setTagType('success'),
|
||||
Failed: EnumValue.of(-1, 'flow.reject').setTagType('danger'),
|
||||
};
|
||||
|
||||
export const FlowBizType = {
|
||||
DbSqlExec: EnumValue.of('db_sql_exec_flow', 'flow.dbSqlExec').setTagType('warning'),
|
||||
RedisRunWriteCmd: EnumValue.of('redis_run_cmd_flow', 'flow.redisRunCmd').setTagType('danger'),
|
||||
|
||||
@@ -22,14 +22,21 @@
|
||||
|
||||
<el-table-column prop="res" :label="$t('flow.runResult')" :min-width="30" show-overflow-tooltip>
|
||||
<template #default="scope">
|
||||
<el-popover placement="top" :width="400" trigger="hover">
|
||||
<el-popover placement="top" width="50%" trigger="hover">
|
||||
<template #reference>
|
||||
<el-link icon="view" :type="scope.row.errorMsg ? 'danger' : 'success'" :underline="false"> </el-link>
|
||||
<el-link icon="view" :type="scope.row.errorMsg ? 'danger' : 'success'" underline="never"> </el-link>
|
||||
</template>
|
||||
|
||||
<el-text v-if="scope.row.errorMsg">{{ scope.row.errorMsg }}</el-text>
|
||||
<el-table v-else :data="scope.row.res" size="small">
|
||||
<el-table-column v-for="col in scope.row.columns" :key="col.name" :label="col.name" :prop="col.name" />
|
||||
<el-table max-height="600px" v-else :data="scope.row.res" size="small">
|
||||
<el-table-column
|
||||
:width="DbInst.flexColumnWidth(col.name, scope.row.res)"
|
||||
v-for="col in scope.row.columns"
|
||||
:key="col.name"
|
||||
:label="col.name"
|
||||
:prop="col.name"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
</el-table>
|
||||
</el-popover>
|
||||
</template>
|
||||
@@ -50,6 +57,7 @@ import { tagApi } from '@/views/ops/tag/api';
|
||||
import { TagResourceTypeEnum } from '@/common/commonEnum';
|
||||
import TagCodePath from '@/views/ops/component/TagCodePath.vue';
|
||||
import SvgIcon from '@/components/svgIcon/index.vue';
|
||||
import { DbInst } from '@/views/ops/db/db';
|
||||
|
||||
const props = defineProps({
|
||||
procinst: {
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item prop="sql" label="SQL" required>
|
||||
<div class="w100">
|
||||
<div class="!w-full">
|
||||
<monaco-editor height="300px" language="sql" v-model="bizForm.sql" />
|
||||
</div>
|
||||
</el-form-item>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<div class="personal-user">
|
||||
<div class="personal-user-left">
|
||||
<el-upload
|
||||
class="h100 personal-user-left-upload"
|
||||
class="!h-full personal-user-left-upload"
|
||||
:action="getUploadFileUrl(`avatar_${userInfo.username}`)"
|
||||
:limit="1"
|
||||
:show-file-list="false"
|
||||
@@ -20,16 +20,16 @@
|
||||
</div>
|
||||
<div class="personal-user-right">
|
||||
<el-row>
|
||||
<el-col :span="24" class="personal-title mb18">
|
||||
<el-col :span="24" class="personal-title mb-4">
|
||||
{{ $t('home.welcomeMsg', { name: userInfo.name }) }}
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-row>
|
||||
<el-col :xs="24" :sm="12" class="personal-item mb6">
|
||||
<el-col :xs="24" :sm="12" class="personal-item !mb-1.5">
|
||||
<div class="personal-item-label">{{ $t('common.username') }}:</div>
|
||||
<div class="personal-item-value">{{ userInfo.username }}</div>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" class="personal-item mb6">
|
||||
<el-col :xs="24" :sm="12" class="personal-item !mb-1.5">
|
||||
<div class="personal-item-label">{{ $t('common.role') }}:</div>
|
||||
<div class="personal-item-value">{{ roleInfo }}</div>
|
||||
</el-col>
|
||||
@@ -37,11 +37,11 @@
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-row>
|
||||
<el-col :xs="24" :sm="12" class="personal-item mb6">
|
||||
<el-col :xs="24" :sm="12" class="personal-item !mb-1.5">
|
||||
<div class="personal-item-label">{{ $t('home.lastLoginIp') }}:</div>
|
||||
<div class="personal-item-value">{{ userInfo.lastLoginIp }}</div>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" class="personal-item mb6">
|
||||
<el-col :xs="24" :sm="12" class="personal-item !mb-1.5">
|
||||
<div class="personal-item-label">{{ $t('home.lastLoginTime') }}:</div>
|
||||
<div class="personal-item-value">{{ formatDate(userInfo.lastLoginTime) }}</div>
|
||||
</el-col>
|
||||
@@ -71,14 +71,14 @@
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="20" class="mt20 resource-info">
|
||||
<el-row :gutter="20" class="!mt-4 resource-info">
|
||||
<el-col :sm="12">
|
||||
<el-card shadow="hover">
|
||||
<template #header>
|
||||
<el-row justify="center">
|
||||
<div class="resource-num pointer-icon" @click="toPage('machine')">
|
||||
<SvgIcon
|
||||
class="mb5 mr5"
|
||||
class="mb-1 mr-1"
|
||||
:size="28"
|
||||
:name="TagResourceTypeEnum.Machine.extra.icon"
|
||||
:color="TagResourceTypeEnum.Machine.extra.iconColor"
|
||||
@@ -123,7 +123,7 @@
|
||||
<el-row justify="center">
|
||||
<div class="resource-num pointer-icon" @click="toPage('db')">
|
||||
<SvgIcon
|
||||
class="mb5 mr5"
|
||||
class="mb-1 mr-1"
|
||||
:size="28"
|
||||
:name="TagResourceTypeEnum.DbInstance.extra.icon"
|
||||
:color="TagResourceTypeEnum.DbInstance.extra.iconColor"
|
||||
@@ -157,14 +157,14 @@
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="20" class="mt20 resource-info">
|
||||
<el-row :gutter="20" class="!mt-4 resource-info">
|
||||
<el-col :sm="12">
|
||||
<el-card shadow="hover">
|
||||
<template #header>
|
||||
<el-row justify="center">
|
||||
<div class="resource-num pointer-icon" @click="toPage('redis')">
|
||||
<SvgIcon
|
||||
class="mb5 mr5"
|
||||
class="mb-1 mr-1"
|
||||
:size="28"
|
||||
:name="TagResourceTypeEnum.Redis.extra.icon"
|
||||
:color="TagResourceTypeEnum.Redis.extra.iconColor"
|
||||
@@ -203,7 +203,7 @@
|
||||
<el-row justify="center">
|
||||
<div class="resource-num pointer-icon" @click="toPage('mongo')">
|
||||
<SvgIcon
|
||||
class="mb5 mr5"
|
||||
class="mb-1 mr-1"
|
||||
:size="28"
|
||||
:name="TagResourceTypeEnum.Mongo.extra.icon"
|
||||
:color="TagResourceTypeEnum.Mongo.extra.iconColor"
|
||||
@@ -251,7 +251,7 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-row type="flex" class="mt5" justify="center">
|
||||
<el-row type="flex" class="mt-1" justify="center">
|
||||
<el-pagination
|
||||
small
|
||||
@current-change="searchMsg"
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="login-content-code">
|
||||
<img class="login-content-code-img" @click="getCaptcha" width="130px" height="40px" :src="captchaImage" style="cursor: pointer" />
|
||||
<img class="login-content-code-img cursor-pointer" @click="getCaptcha" width="130px" height="40px" :src="captchaImage" />
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
@@ -63,7 +63,7 @@
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
<div class="mt20" v-if="state.oauth2LoginConfig.enable">
|
||||
<div class="!mt-4" v-if="state.oauth2LoginConfig.enable">
|
||||
<el-text size="small">{{ $t('login.thirdPartyLogin') }}: </el-text>
|
||||
<el-tooltip :content="state.oauth2LoginConfig.name" placement="bottom-start">
|
||||
<el-button link size="small" type="primary" @click="oauth2Login">
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="h-full">
|
||||
<page-table
|
||||
ref="pageTableRef"
|
||||
:page-api="channelApi.list"
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
|
||||
<FormItemTooltip prop="tmpl" :label="$t('msg.tmpl')" :tooltip="$t('msg.msgTmplTooltip')">
|
||||
<MonacoEditor
|
||||
class="w100"
|
||||
class="!w-full"
|
||||
height="200px"
|
||||
v-model="formData.tmpl"
|
||||
:language="EnumValue.getLabelByValue(TmplTypeEnum, formData.msgType)"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="h-full">
|
||||
<page-table
|
||||
ref="pageTableRef"
|
||||
:page-api="tmplApi.list"
|
||||
@@ -19,7 +19,7 @@
|
||||
<template #relateChannel="{ data }">
|
||||
<el-popover placement="top-start" trigger="click" width="auto">
|
||||
<template #reference>
|
||||
<el-link @click="getRelateChannels(data.id)" icon="view" type="primary" :underline="false"></el-link>
|
||||
<el-link @click="getRelateChannels(data.id)" icon="view" type="primary" underline="never"></el-link>
|
||||
</template>
|
||||
<el-row v-for="item in state.relateChannels" :key="item.id">
|
||||
{{ $t(EnumValue.getLabelByValue(ChannelTypeEnum, item.type)) }}
|
||||
|
||||
@@ -73,7 +73,7 @@
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item v-if="form.ciphertextType == AuthCertCiphertextTypeEnum.PrivateKey.value" prop="ciphertext" :label="$t('ac.privateKey')">
|
||||
<div class="w100" style="position: relative">
|
||||
<div class="!w-full" style="position: relative">
|
||||
<SvgIcon
|
||||
v-if="form.id"
|
||||
v-auth="'authcert:showciphertext'"
|
||||
|
||||
@@ -7,13 +7,13 @@
|
||||
</template>
|
||||
<template #default="scope">
|
||||
<el-button v-auth="'authcert:save'" @click="edit(scope.row, scope.$index)" type="primary" icon="edit" link></el-button>
|
||||
<el-button class="ml1" v-auth="'authcert:del'" type="danger" @click="deleteRow(scope.$index)" icon="delete" link></el-button>
|
||||
<el-button class="!ml-0.5" v-auth="'authcert:del'" type="danger" @click="deleteRow(scope.$index)" icon="delete" link></el-button>
|
||||
|
||||
<el-button
|
||||
:title="$t('ac.testConn')"
|
||||
:loading="props.testConnBtnLoading && scope.$index == state.idx"
|
||||
:disabled="props.testConnBtnLoading"
|
||||
class="ml1"
|
||||
class="!ml-0.5"
|
||||
type="success"
|
||||
@click="testConn(scope.row, scope.$index)"
|
||||
icon="Link"
|
||||
|
||||
@@ -19,8 +19,6 @@ const emit = defineEmits(['resize']);
|
||||
|
||||
const { width } = useWindowSize();
|
||||
|
||||
console.log(width);
|
||||
|
||||
const leftPaneSize = computed(() => (width.value >= 1600 ? 20 : 24));
|
||||
|
||||
// 处理 resize 事件
|
||||
|
||||
@@ -2,17 +2,17 @@
|
||||
<div v-if="props.tags">
|
||||
<el-row v-for="(tag, idx) in props.tags?.slice(0, 1)" :key="idx">
|
||||
<TagInfo :tag-path="tag.codePath" />
|
||||
<span class="ml3">{{ tag.codePath }}</span>
|
||||
<span class="ml-0.5">{{ tag.codePath }}</span>
|
||||
|
||||
<!-- 展示剩余的标签信息 -->
|
||||
<el-popover :show-after="300" v-if="props.tags.length > 1 && idx == 0" placement="top-start" width="230" trigger="hover">
|
||||
<template #reference>
|
||||
<SvgIcon class="mt5 ml5" color="var(--el-color-primary)" name="MoreFilled" />
|
||||
<SvgIcon class="mt-1 ml-1" color="var(--el-color-primary)" name="MoreFilled" />
|
||||
</template>
|
||||
|
||||
<el-row v-for="i in props.tags.slice(1)" :key="i">
|
||||
<TagInfo :tag-path="i.codePath" />
|
||||
<span class="ml3">{{ i.codePath }}</span>
|
||||
<span class="ml-0.5">{{ i.codePath }}</span>
|
||||
</el-row>
|
||||
</el-popover>
|
||||
</el-row>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user