----ui 初始化

This commit is contained in:
liwq 2025-06-10 16:59:13 +08:00
parent 2fbf5c4794
commit f5fcae6ad3
62 changed files with 1212 additions and 7512 deletions

View File

@ -22,7 +22,6 @@ import com.base.common.sdk.sse.dto.SseMessageDto;
import com.base.common.sdk.sse.utils.SseMessageUtils;
import com.base.system.domain.vo.SysClientVo;
import com.base.system.service.ISysClientService;
import com.base.system.service.ISysConfigService;
import com.base.system.service.ISysSocialService;
import com.base.web.domain.vo.LoginVo;
import com.base.web.service.IAuthStrategy;
@ -59,7 +58,6 @@ public class AuthController {
private final SocialProperties socialProperties;
private final SysLoginService loginService;
private final SysRegisterService registerService;
private final ISysConfigService configService;
private final ISysSocialService socialUserService;
private final ISysClientService clientService;
private final ScheduledExecutorService scheduledExecutorService;
@ -93,7 +91,7 @@ public class AuthController {
Long userId = LoginHelper.getUserId();
scheduledExecutorService.schedule(() -> {
SseMessageDto dto = new SseMessageDto();
dto.setMessage("欢迎登录boot-Vue-Plus后台管理系统");
dto.setMessage("欢迎登录boot后台管理系统");
dto.setUserIds(List.of(userId));
SseMessageUtils.publishMessage(dto);
}, 5, TimeUnit.SECONDS);

View File

@ -1,5 +1,5 @@
# 页面标题
VITE_APP_TITLE = RuoYi-Vue-Plus多租户管理系统
VITE_APP_TITLE = 后台管理系统
# 开发环境配置
VITE_APP_ENV = 'development'
@ -10,16 +10,10 @@ VITE_APP_BASE_API = '/dev-api'
# 应用访问路径 例如使用前缀 /admin/
VITE_APP_CONTEXT_PATH = '/'
# 监控地址
VITE_APP_MONITOR_ADMIN = 'http://localhost:9090/admin/applications'
# SnailJob 控制台地址
VITE_APP_SNAILJOB_ADMIN = 'http://localhost:8800/snail-job'
VITE_APP_PORT = 80
# 接口加密功能开关(如需关闭 后端也必须对应关闭)
VITE_APP_ENCRYPT = true
VITE_APP_ENCRYPT = false
# 接口加密传输 RSA 公钥与后端解密私钥对应 如更换需前后端一同更换
VITE_APP_RSA_PUBLIC_KEY = 'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdHnzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ=='
# 接口响应解密 RSA 私钥与后端加密公钥对应 如更换需前后端一同更换

View File

@ -1,5 +1,5 @@
# 页面标题
VITE_APP_TITLE = RuoYi-Vue-Plus多租户管理系统
VITE_APP_TITLE = 后台管理系统
# 生产环境配置
VITE_APP_ENV = 'production'

View File

@ -1,76 +0,0 @@
## 平台简介
- 本仓库为前端技术栈 [Vue3](https://v3.cn.vuejs.org) + [TS](https://www.typescriptlang.org/) + [Element Plus](https://element-plus.org/zh-CN) + [Vite](https://cn.vitejs.dev) 版本。
- 成员项目: 基于 vben(ant-design-vue) 的前端项目 [ruoyi-plus-vben](https://gitee.com/dapppp/ruoyi-plus-vben)
- 配套后端代码仓库地址
- [RuoYi-Vue-Plus 5.X(注意版本号)](https://gitee.com/dromara/RuoYi-Vue-Plus)
- [RuoYi-Cloud-Plus 2.X(注意版本号)](https://gitee.com/dromara/RuoYi-Cloud-Plus)
## 前端运行
```bash
# 克隆项目
git clone https://gitee.com/JavaLionLi/plus-ui.git
# 安装依赖
npm install --registry=https://registry.npmmirror.com
# 启动服务
npm run dev
# 构建生产环境
npm run build:prod
# 前端访问地址 http://localhost:80
```
## 本框架与RuoYi的业务差异
| 业务 | 功能说明 | 本框架 | RuoYi |
| ------------ | ------------------------------------------------------------- | ------ | ----------------------------- |
| 租户管理 | 系统内租户的管理 如:租户套餐、过期时间、用户数量、企业信息等 | 支持 | 无 |
| 租户套餐管理 | 系统内租户所能使用的套餐管理 如:套餐内所包含的菜单等 | 支持 | 无 |
| 用户管理 | 用户的管理配置 如:新增用户、分配用户所属部门、角色、岗位等 | 支持 | 支持 |
| 部门管理 | 配置系统组织机构(公司、部门、小组) 树结构展现支持数据权限 | 支持 | 支持 |
| 岗位管理 | 配置系统用户所属担任职务 | 支持 | 支持 |
| 菜单管理 | 配置系统菜单、操作权限、按钮权限标识等 | 支持 | 支持 |
| 角色管理 | 角色菜单权限分配、设置角色按机构进行数据范围权限划分 | 支持 | 支持 |
| 字典管理 | 对系统中经常使用的一些较为固定的数据进行维护 | 支持 | 支持 |
| 参数管理 | 对系统动态配置常用参数 | 支持 | 支持 |
| 通知公告 | 系统通知公告信息发布维护 | 支持 | 支持 |
| 操作日志 | 系统正常操作日志记录和查询 系统异常信息日志记录和查询 | 支持 | 支持 |
| 登录日志 | 系统登录日志记录查询包含登录异常 | 支持 | 支持 |
| 文件管理 | 系统文件展示、上传、下载、删除等管理 | 支持 | 无 |
| 文件配置管理 | 系统文件上传、下载所需要的配置信息动态添加、修改、删除等管理 | 支持 | 无 |
| 在线用户管理 | 已登录系统的在线用户信息监控与强制踢出操作 | 支持 | 支持 |
| 定时任务 | 运行报表、任务管理(添加、修改、删除)、日志管理、执行器管理等 | 支持 | 仅支持任务与日志管理 |
| 代码生成 | 多数据源前后端代码的生成java、html、xml、sql支持CRUD下载 | 支持 | 仅支持单数据源 |
| 系统接口 | 根据业务代码自动生成相关的api接口文档 | 支持 | 支持 |
| 服务监控 | 监视集群系统CPU、内存、磁盘、堆栈、在线日志、Spring相关配置等 | 支持 | 仅支持单机CPU、内存、磁盘监控 |
| 缓存监控 | 对系统的缓存信息查询,命令统计等。 | 支持 | 支持 |
| 在线构建器 | 拖动表单元素生成相应的HTML代码。 | 支持 | 支持 |
| 使用案例 | 系统的一些功能案例 | 支持 | 不支持 |
## 演示图例
| | |
| ---------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
| ![输入图片说明](https://foruda.gitee.com/images/1680077524361362822/270bb429_1766278.png '屏幕截图') | ![输入图片说明](https://foruda.gitee.com/images/1680077619939771291/989bf9b6_1766278.png '屏幕截图') |
| ![输入图片说明](https://foruda.gitee.com/images/1680077681751513929/1c27c5bd_1766278.png '屏幕截图') | ![输入图片说明](https://foruda.gitee.com/images/1680077721559267315/74d63e23_1766278.png '屏幕截图') |
| ![输入图片说明](https://foruda.gitee.com/images/1680077765638904515/1b75d4a6_1766278.png '屏幕截图') | ![输入图片说明](https://foruda.gitee.com/images/1680078026375951297/eded7a4b_1766278.png '屏幕截图') |
| ![输入图片说明](https://foruda.gitee.com/images/1680078237104531207/0eb1b6a7_1766278.png '屏幕截图') | ![输入图片说明](https://foruda.gitee.com/images/1680078254306078709/5931e22f_1766278.png '屏幕截图') |
| ![输入图片说明](https://foruda.gitee.com/images/1680078287971528493/0b9af60a_1766278.png '屏幕截图') | ![输入图片说明](https://foruda.gitee.com/images/1680078308138770249/8d3b6696_1766278.png '屏幕截图') |
| ![输入图片说明](https://foruda.gitee.com/images/1680078352553634393/db5ef880_1766278.png '屏幕截图') | ![输入图片说明](https://foruda.gitee.com/images/1680078378238393374/601e4357_1766278.png '屏幕截图') |
| ![输入图片说明](https://foruda.gitee.com/images/1680078414983206024/2aae27c1_1766278.png '屏幕截图') | ![输入图片说明](https://foruda.gitee.com/images/1680078446738419874/ecce7d59_1766278.png '屏幕截图') |
| ![输入图片说明](https://foruda.gitee.com/images/1680078475971341775/149e8634_1766278.png '屏幕截图') | ![输入图片说明](https://foruda.gitee.com/images/1680078491666717143/3fadece7_1766278.png '屏幕截图') |
| ![输入图片说明](https://foruda.gitee.com/images/1680078558863188826/fb8ced2a_1766278.png '屏幕截图') | ![输入图片说明](https://foruda.gitee.com/images/1680078574561685461/ae68a0b2_1766278.png '屏幕截图') |
| ![输入图片说明](https://foruda.gitee.com/images/1680078594932772013/9d8bfec6_1766278.png '屏幕截图') | ![输入图片说明](https://foruda.gitee.com/images/1680078626493093532/fcfe4ff6_1766278.png '屏幕截图') |
| ![输入图片说明](https://foruda.gitee.com/images/1680078643608812515/0295bd4f_1766278.png '屏幕截图') | ![输入图片说明](https://foruda.gitee.com/images/1680078685196286463/d7612c81_1766278.png '屏幕截图') |
| ![输入图片说明](https://foruda.gitee.com/images/1680078703877318597/56fce0bc_1766278.png '屏幕截图') | ![输入图片说明](https://foruda.gitee.com/images/1680078716586545643/b6dbd68f_1766278.png '屏幕截图') |
| ![输入图片说明](https://foruda.gitee.com/images/1680078734103217688/eb1e6aa6_1766278.png '屏幕截图') | ![输入图片说明](https://foruda.gitee.com/images/1680078759131415480/73c525d8_1766278.png '屏幕截图') |
| ![输入图片说明](https://foruda.gitee.com/images/1680078779416197879/75e3ed02_1766278.png '屏幕截图') | ![输入图片说明](https://foruda.gitee.com/images/1680078802329118061/77e10915_1766278.png '屏幕截图') |
| ![输入图片说明](https://foruda.gitee.com/images/1680078893627848351/34a1c342_1766278.png '屏幕截图') | ![输入图片说明](https://foruda.gitee.com/images/1680078928175016986/f126ec4a_1766278.png '屏幕截图') |
| ![输入图片说明](https://foruda.gitee.com/images/1680078941718318363/b68a0f72_1766278.png '屏幕截图') | ![输入图片说明](https://foruda.gitee.com/images/1680078963175518631/3bb769a1_1766278.png '屏幕截图') |
| ![输入图片说明](https://foruda.gitee.com/images/1680078982294090567/b31c343d_1766278.png '屏幕截图') | ![输入图片说明](https://foruda.gitee.com/images/1680079000642440444/77ca82a9_1766278.png '屏幕截图') |
| ![输入图片说明](https://foruda.gitee.com/images/1680079020995074177/03b7d52e_1766278.png '屏幕截图') | ![输入图片说明](https://foruda.gitee.com/images/1680079039367822173/76811806_1766278.png '屏幕截图') |
| ![输入图片说明](https://foruda.gitee.com/images/1680079274333484664/4dfdc7c0_1766278.png '屏幕截图') | ![输入图片说明](https://foruda.gitee.com/images/1680079290467458224/d6715fcf_1766278.png '屏幕截图') |

View File

@ -6,7 +6,7 @@
<meta name="renderer" content="webkit" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" />
<link rel="icon" href="/favicon.ico" />
<title>RuoYi-Vue-Plus多租户管理系统</title>
<title>后台管理系统</title>
<!--[if lt IE 11
]><script>
window.location.href = '/html/ie.html';

View File

@ -1,7 +1,7 @@
{
"name": "ruoyi-vue-plus",
"name": "后台管理系统",
"version": "5.2.3",
"description": "RuoYi-Vue-Plus多租户管理系统",
"description": "后台管理系统",
"author": "LionLi",
"license": "MIT",
"type": "module",

View File

@ -1,62 +0,0 @@
import request from '@/utils/request';
import { AxiosPromise } from 'axios';
import { DemoVO, DemoForm, DemoQuery } from '@/api/demo/demo/types';
/**
*
* @param query
* @returns {*}
*/
export const listDemo = (query?: DemoQuery): AxiosPromise<DemoVO[]> => {
return request({
url: '/demo/demo/list',
method: 'get',
params: query
});
};
/**
*
* @param id
*/
export const getDemo = (id: string | number): AxiosPromise<DemoVO> => {
return request({
url: '/demo/demo/' + id,
method: 'get'
});
};
/**
*
* @param data
*/
export const addDemo = (data: DemoForm) => {
return request({
url: '/demo/demo',
method: 'post',
data: data
});
};
/**
*
* @param data
*/
export const updateDemo = (data: DemoForm) => {
return request({
url: '/demo/demo',
method: 'put',
data: data
});
};
/**
*
* @param id
*/
export const delDemo = (id: string | number | Array<string | number>) => {
return request({
url: '/demo/demo/' + id,
method: 'delete'
});
};

View File

@ -1,90 +0,0 @@
export interface DemoVO {
/**
*
*/
id: string | number;
/**
* id
*/
deptId: string | number;
/**
* id
*/
userId: string | number;
/**
*
*/
orderNum: number;
/**
* key键
*/
testKey: string;
/**
*
*/
value: string;
}
export interface DemoForm extends BaseEntity {
/**
*
*/
id?: string | number;
/**
* id
*/
deptId?: string | number;
/**
* id
*/
userId?: string | number;
/**
*
*/
orderNum?: number;
/**
* key键
*/
testKey?: string;
/**
*
*/
value?: string;
}
export interface DemoQuery extends PageQuery {
/**
* id
*/
deptId?: string | number;
/**
* id
*/
userId?: string | number;
/**
*
*/
orderNum?: number;
/**
* key键
*/
testKey?: string;
/**
*
*/
value?: string;
}

View File

@ -1,62 +0,0 @@
import request from '@/utils/request';
import { AxiosPromise } from 'axios';
import { TreeVO, TreeForm, TreeQuery } from '@/api/demo/tree/types';
/**
*
* @param query
* @returns {*}
*/
export const listTree = (query?: TreeQuery): AxiosPromise<TreeVO[]> => {
return request({
url: '/demo/tree/list',
method: 'get',
params: query
});
};
/**
*
* @param id
*/
export const getTree = (id: string | number): AxiosPromise<TreeVO> => {
return request({
url: '/demo/tree/' + id,
method: 'get'
});
};
/**
*
* @param data
*/
export const addTree = (data: TreeForm) => {
return request({
url: '/demo/tree',
method: 'post',
data: data
});
};
/**
*
* @param data
*/
export const updateTree = (data: TreeForm) => {
return request({
url: '/demo/tree',
method: 'put',
data: data
});
};
/**
*
* @param id
*/
export const delTree = (id: string | number | Array<string | number>) => {
return request({
url: '/demo/tree/' + id,
method: 'delete'
});
};

View File

@ -1,80 +0,0 @@
export interface TreeVO {
/**
*
*/
id: string | number;
/**
* id
*/
parentId: string | number;
/**
* id
*/
deptId: string | number;
/**
* id
*/
userId: string | number;
/**
*
*/
treeName: string;
/**
*
*/
children: TreeVO[];
}
export interface TreeForm extends BaseEntity {
/**
*
*/
id?: string | number;
/**
* id
*/
parentId?: string | number;
/**
* id
*/
deptId?: string | number;
/**
* id
*/
userId?: string | number;
/**
*
*/
treeName?: string;
}
export interface TreeQuery {
/**
* id
*/
parentId?: string | number;
/**
* id
*/
deptId?: string | number;
/**
* id
*/
userId?: string | number;
/**
*
*/
treeName?: string;
}

View File

@ -1,23 +0,0 @@
function generateRandomValue() {
// 生成一个随机数
const randomValue = Math.random().toString(36).slice(2, 12);
return `Process_${randomValue}`;
}
const cartage: string = 'default';
export default `<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:bioc="http://bpmn.io/schema/bpmn/biocolor/1.0" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:flowable="http://flowable.org/bpmn" targetNamespace="http://www.flowable.org/processdef">
<process id="process_${generateRandomValue()}" name="name_${generateRandomValue()}">
<startEvent id="startNode1" name="开始" />
</process>
<bpmndi:BPMNDiagram id="BPMNDiagram_flow">
<bpmndi:BPMNPlane id="BPMNPlane_flow" bpmnElement="T-2d89e7a3-ba79-4abd-9f64-ea59621c258c">
<bpmndi:BPMNShape id="BPMNShape_startNode1" bpmnElement="startNode1" bioc:stroke="">
<omgdc:Bounds x="240" y="200" width="30" height="30" />
<bpmndi:BPMNLabel>
<omgdc:Bounds x="242" y="237" width="23" height="14" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</definitions>`;

View File

@ -1,126 +0,0 @@
export const NodeName = {
'bpmn:Process': '流程',
'bpmn:StartEvent': '开始事件',
'bpmn:IntermediateThrowEvent': '中间事件',
'bpmn:Task': '任务',
'bpmn:SendTask': '发送任务',
'bpmn:ReceiveTask': '接收任务',
'bpmn:UserTask': '用户任务',
'bpmn:ManualTask': '手工任务',
'bpmn:BusinessRuleTask': '业务规则任务',
'bpmn:ServiceTask': '服务任务',
'bpmn:ScriptTask': '脚本任务',
'bpmn:EndEvent': '结束事件',
'bpmn:SequenceFlow': '流程线',
'bpmn:ExclusiveGateway': '互斥网关',
'bpmn:ParallelGateway': '并行网关',
'bpmn:InclusiveGateway': '相容网关',
'bpmn:ComplexGateway': '复杂网关',
'bpmn:EventBasedGateway': '事件网关',
'bpmn:Participant': '池/参与者',
'bpmn:SubProcess': '子流程',
'bpmn:DataObjectReference': '数据对象引用',
'bpmn:DataStoreReference': '数据存储引用',
'bpmn:Group': '组'
};
export default {
'Activate hand tool': '启动手动工具',
'Activate lasso tool': '启动 Lasso 工具',
'Activate create/remove space tool': '启动创建/删除空间工具',
'Activate global connect tool': '启动全局连接工具',
'Ad-hoc': 'Ad-hoc',
'Add lane above': '在上方添加泳道',
'Add lane below': '在下方添加泳道',
'Business rule task': '规则任务',
'Call activity': '引用流程',
'Compensation end event': '结束补偿事件',
'Compensation intermediate throw event': '中间补偿抛出事件',
'Complex gateway': '复杂网关',
'Conditional intermediate catch event': '中间条件捕获事件',
'Conditional start event (non-interrupting)': '条件启动事件 (非中断)',
'Conditional start event': '条件启动事件',
'Connect using association': '文本关联',
'Connect using sequence/message flow or association': '消息关联',
'Change element': '更改元素',
'Change type': '更改类型',
'Create data object reference': '创建数据对象引用',
'Create data store reference': '创建数据存储引用',
'Create expanded sub-process': '创建可折叠子流程',
'Create pool/participant': '创建池/参与者',
'Collection': '集合',
'Connect using data input association': '数据输入关联',
'Data store reference': '数据存储引用',
'Data object reference': '数据对象引用',
'Divide into two lanes': '分成两个泳道',
'Divide into three lanes': '分成三个泳道',
'End event': '结束事件',
'Error end event': '结束错误事件',
'Escalation end event': '结束升级事件',
'Escalation intermediate throw event': '中间升级抛出事件',
'Event sub-process': '事件子流程',
'Event-based gateway': '事件网关',
'Exclusive gateway': '互斥网关',
'Empty pool/participant (removes content)': '清空池/参与者 (删除内容)',
'Empty pool/participant': '清空池/参与者',
'Expanded pool/participant': '展开池/参与者',
'Inclusive gateway': '相容网关',
'Intermediate throw event': '中间抛出事件',
'Loop': '循环',
'Link intermediate catch event': '中间链接捕获事件',
'Link intermediate throw event': '中间链接抛出事件',
'Manual task': '手动任务',
'Message end event': '结束消息事件',
'Message intermediate catch event': '中间消息捕获事件',
'Message intermediate throw event': '中间消息抛出事件',
'Message start event': '消息启动事件',
'Parallel gateway': '并行网关',
'Parallel multi-instance': '并行多实例',
'Participant multiplicity': '参与者多重性',
'Receive task': '接受任务',
'Remove': '移除',
'Script task': '脚本任务',
'Send task': '发送任务',
'Sequential multi-instance': '串行多实例',
'Service task': '服务任务',
'Signal end event': '结束信号事件',
'Signal intermediate catch event': '中间信号捕获事件',
'Signal intermediate throw event': '中间信号抛出事件',
'Signal start event (non-interrupting)': '信号启动事件 (非中断)',
'Signal start event': '信号启动事件',
'Start event': '开始事件',
'Sub-process (collapsed)': '可折叠子流程',
'Sub-process (expanded)': '可展开子流程',
'Sub rocess': '子流程',
'Task': '任务',
'Transaction': '事务',
'Terminate end event': '终止边界事件',
'Timer intermediate catch event': '中间定时捕获事件',
'Timer start event (non-interrupting)': '定时启动事件 (非中断)',
'Timer start event': '定时启动事件',
'User task': '用户任务',
'Create start event': '创建开始事件',
'Create gateway': '创建网关',
'Create intermediate/boundary event': '创建中间/边界事件',
'Create end event': '创建结束事件',
'Create group': '创建组',
'Create startEvent': '开始节点',
'Create endEvent': '结束节点',
'Create exclusiveGateway': '互斥网关',
'Create parallelGateway': '并行网关',
'Create task': '任务节点',
'Create userTask': '用户任务节点',
'Condition type': '条件类型',
'Append end event': '追加结束事件节点',
'Append gateway': '追加网关节点',
'Append task': '追加任务',
'Append user task': '追加用户任务节点',
'Append text annotation': '追加文本注释',
'Append intermediate/boundary event': '追加中间或边界事件',
'Append receive task': '追加接收任务节点',
'Append message intermediate catch event': '追加中间消息捕获事件',
'Append timer intermediate catch event': '追加中间定时捕获事件',
'Append conditional intermediate catch event': '追加中间条件捕获事件',
'Append signal intermediate catch event': '追加中间信号捕获事件',
'flow elements must be children of pools/participants': '流程元素必须是池/参与者的子元素'
};

File diff suppressed because it is too large Load Diff

View File

@ -1,138 +0,0 @@
import ContextPadProvider from 'bpmn-js/lib/features/context-pad/ContextPadProvider';
import { Injector } from 'didi';
import EventBus from 'diagram-js/lib/core/EventBus';
import ContextPad from 'diagram-js/lib/features/context-pad/ContextPad';
import Modeling from 'bpmn-js/lib/features/modeling/Modeling.js';
import ElementFactory from 'bpmn-js/lib/features/modeling/ElementFactory';
import Connect from 'diagram-js/lib/features/connect/Connect';
import Create from 'diagram-js/lib/features/create/Create';
import PopupMenu from 'diagram-js/lib/features/popup-menu/PopupMenu';
import Canvas from 'diagram-js/lib/core/Canvas';
import Rules from 'diagram-js/lib/features/rules/Rules';
import { Element, Shape } from 'diagram-js/lib/model/Types';
import BpmnFactory from 'bpmn-js/lib/features/modeling/BpmnFactory';
import modeler from '@/store/modules/modeler';
// @Description: 增强元素连线事件
class CustomContextPadProvider extends ContextPadProvider {
private _contextPad: ContextPad;
private _modeling: Modeling;
private _elementFactory: ElementFactory;
private _autoPlace: any;
private _connect: Connect;
private _create: Create;
private _popupMenu: PopupMenu;
private _canvas: Canvas;
private _rules: Rules;
constructor(
config: any,
injector: Injector,
eventBus: EventBus,
contextPad: ContextPad,
modeling: Modeling,
elementFactory: ElementFactory,
connect: Connect,
create: Create,
popupMenu: PopupMenu,
canvas: Canvas,
rules: Rules,
translate
) {
// @ts-expect-error 忽略异常
super(config, injector, eventBus, contextPad, modeling, elementFactory, connect, create, popupMenu, canvas, rules, translate);
this._contextPad = contextPad;
this._modeling = modeling;
this._elementFactory = elementFactory;
this._connect = connect;
this._create = create;
this._popupMenu = popupMenu;
this._canvas = canvas;
this._rules = rules;
this._autoPlace = injector.get('autoPlace', false);
}
getContextPadEntries(element: Element) {
const actions: Record<string, any> = {};
const appendUserTask = (event: Event, element: Shape) => {
const shape = this._elementFactory.createShape({ type: 'bpmn:UserTask' });
this._create.start(event, shape, {
source: element
});
};
const appendMultiInstanceUserTask = (event: Event, element: Shape) => {
const store = modeler();
const bpmnFactory = store.getModeler().get('bpmnFactory') as BpmnFactory;
const businessObject = bpmnFactory.create('bpmn:UserTask', {
// name: '多实例用户任务',
isForCompensation: false
});
businessObject.loopCharacteristics = bpmnFactory.create('bpmn:MultiInstanceLoopCharacteristics');
// 创建 Shape
const shape = this._elementFactory.createShape({
type: 'bpmn:UserTask',
businessObject: businessObject
});
this._create.start(event, shape, { source: element });
};
const appendTask = this._autoPlace
? (event, element) => {
const bpmnFactory: BpmnFactory | undefined = modeler().getModeler().get('bpmnFactory');
const businessObject = bpmnFactory.create('bpmn:UserTask', {
// name: '多实例用户任务',// 右键创建显示
isForCompensation: false
});
// 创建多实例属性并分配给用户任务的 loopCharacteristics
businessObject.loopCharacteristics = bpmnFactory.create('bpmn:MultiInstanceLoopCharacteristics');
// 创建 Shape
const shape = this._elementFactory.createShape({
type: 'bpmn:UserTask',
businessObject: businessObject
});
this._autoPlace.append(element, shape);
}
: appendMultiInstanceUserTask;
const append = this._autoPlace
? (event: Event, element: Shape) => {
const shape = this._elementFactory.createShape({ type: 'bpmn:UserTask' });
this._autoPlace.append(element, shape);
}
: appendUserTask;
// // 添加创建用户任务按钮
actions['append.append-user-task'] = {
group: 'model',
className: 'bpmn-icon-user-task',
title: '用户任务',
action: {
dragstart: appendUserTask,
click: append
}
};
// 添加创建多实例用户任务按钮
actions['append.append-multi-instance-user-task'] = {
group: 'model',
className: 'bpmn-icon-user', // 你可以使用多实例用户任务的图标 bpmn-icon-user bpmn-icon-user-task
title: '多实例用户任务',
action: {
dragstart: appendMultiInstanceUserTask,
click: appendTask
}
};
return actions;
}
}
export default CustomContextPadProvider;

View File

@ -1,109 +0,0 @@
import { assign } from 'min-dash';
import PaletteProvider from 'bpmn-js/lib/features/palette/PaletteProvider';
import ElementFactory from 'bpmn-js/lib/features/modeling/ElementFactory';
import Create from 'diagram-js/lib/features/create/Create';
import SpaceTool from 'diagram-js/lib/features/space-tool/SpaceTool';
import LassoTool from 'diagram-js/lib/features/lasso-tool/LassoTool';
import HandTool from 'diagram-js/lib/features/hand-tool/HandTool';
import GlobalConnect from 'diagram-js/lib/features/global-connect/GlobalConnect';
import Palette from 'diagram-js/lib/features/palette/Palette';
import modeler from '@/store/modules/modeler';
import BpmnFactory from 'bpmn-js/lib/features/modeling/BpmnFactory';
// @Description: 增强左侧面板
class CustomPaletteProvider extends PaletteProvider {
private readonly _palette: Palette;
private readonly _create: Create;
private readonly _elementFactory: ElementFactory;
private readonly _spaceTool: SpaceTool;
private readonly _lassoTool: LassoTool;
private readonly _handTool: HandTool;
private readonly _globalConnect: GlobalConnect;
private readonly _translate: any;
constructor(palette, create, elementFactory, spaceTool, lassoTool, handTool, globalConnect, translate) {
super(palette, create, elementFactory, spaceTool, lassoTool, handTool, globalConnect, translate);
this._palette = palette;
this._create = create;
this._elementFactory = elementFactory;
this._spaceTool = spaceTool;
this._lassoTool = lassoTool;
this._handTool = handTool;
this._globalConnect = globalConnect;
this._translate = translate;
}
getPaletteEntries() {
const actions = {},
create = this._create,
elementFactory = this._elementFactory,
translate = this._translate;
function createAction(type: string, group: string, className: string, title: string, options?: object) {
function createListener(event) {
const shape = elementFactory.createShape(assign({ type: type }, options));
if (options) {
!shape.businessObject.di && (shape.businessObject.di = {});
shape.businessObject.di.isExpanded = (options as { [key: string]: any }).isExpanded;
}
create.start(event, shape, null);
}
const shortType = type.replace(/^bpmn:/, '');
return {
group: group,
className: className,
title: title || translate('Create {type}', { type: shortType }),
action: {
dragstart: createListener,
click: createListener
}
};
}
function createMultiInstanceUserTask(event) {
const bpmnFactory: BpmnFactory | undefined = modeler().getBpmnFactory();
// 创建一个 bpmn:UserTask
const userTask = bpmnFactory.create('bpmn:UserTask', {
// name: '多实例用户任务', // 在画板中显示字段
isForCompensation: false
});
// 将多实例属性分配给 bpmn:UserTask 的 loopCharacteristics
userTask.loopCharacteristics = bpmnFactory.create('bpmn:MultiInstanceLoopCharacteristics');
const customUserTask = elementFactory.createShape({
type: 'bpmn:UserTask',
businessObject: userTask // 分配创建的 userTask 到 businessObject
});
create.start(event, customUserTask, {});
}
assign(actions, {
'create.parallel-gateway': createAction('bpmn:ParallelGateway', 'gateway', 'bpmn-icon-gateway-parallel', '并行网关'),
'create.event-base-gateway': createAction('bpmn:EventBasedGateway', 'gateway', 'bpmn-icon-gateway-eventbased', '事件网关'),
// 分组线
'gateway-separator': {
group: 'gateway',
separator: true
},
'create.user-task': createAction('bpmn:UserTask', 'activity', 'bpmn-icon-user-task', '创建用户任务'),
'create.multi-instance-user-task': {
group: 'activity',
type: 'bpmn:UserTask',
className: 'bpmn-icon-user task-multi-instance',
title: '创建多实例用户任务',
action: {
click: createMultiInstanceUserTask,
dragstart: createMultiInstanceUserTask
}
},
'task-separator': {
group: 'activity',
separator: true
}
});
return actions;
}
}
CustomPaletteProvider['$inject'] = ['palette', 'create', 'elementFactory', 'spaceTool', 'lassoTool', 'handTool', 'globalConnect', 'translate'];
export default CustomPaletteProvider;

View File

@ -1,56 +0,0 @@
import BaseRenderer from 'diagram-js/lib/draw/BaseRenderer';
import {
append as svgAppend,
attr as svgAttr,
create as svgCreate,
select as svgSelect,
selectAll as svgSelectAll,
clone as svgClone,
clear as svgClear,
remove as svgRemove
} from 'tiny-svg';
const HIGH_PRIORITY = 1500;
export default class CustomRenderer extends BaseRenderer {
bpmnRenderer: BaseRenderer;
modeling: any;
constructor(eventBus, bpmnRenderer, modeling) {
super(eventBus, HIGH_PRIORITY);
this.bpmnRenderer = bpmnRenderer;
this.modeling = modeling;
}
canRender(element) {
// ignore labels
return !element.labelTarget;
}
/**
*
* @param {*} parentNode svgNode
* @param {*} element
* @returns
*/
drawShape(parentNode, element) {
const shape = this.bpmnRenderer.drawShape(parentNode, element);
const { type, width, height } = element;
// 开始 填充绿色
if (type === 'bpmn:StartEvent') {
svgAttr(shape, { fill: '#77DF6D' });
return shape;
}
if (type === 'bpmn:EndEvent') {
svgAttr(shape, { fill: '#EE7B77' });
return shape;
}
if (type === 'bpmn:UserTask') {
svgAttr(shape, { fill: '#A9C4F8' });
return shape;
}
return shape;
}
getShapePath(shape) {
return this.bpmnRenderer.getShapePath(shape);
}
}
CustomRenderer['$inject'] = ['eventBus', 'bpmnRenderer'];

View File

@ -1,15 +0,0 @@
import zh from '../../lang/zh';
const customTranslate = (template: any, replacements: any) => {
replacements = replacements || {};
template = zh[template] || template;
return template.replace(/{([^}]+)}/g, function (_: any, key: any) {
return replacements[key] || '{' + key + '}';
});
};
export const translateModule = {
translate: ['value', customTranslate]
};
export default translateModule;

View File

@ -1,17 +0,0 @@
// 翻译模块
import TranslationModule from './Translate';
import { ModuleDeclaration } from 'didi';
import CustomPaletteProvider from './Palette/CustomPaletteProvider';
import CustomRenderer from './Renderer/CustomRenderer';
import CustomContextPadProvider from './ContextPad/CustomContextPadProvider';
const Module: ModuleDeclaration[] = [
{
__init__: ['customPaletteProvider', 'customContextPadProvider', 'customRenderer'],
customPaletteProvider: ['type', CustomPaletteProvider],
customRenderer: ['type', CustomRenderer],
customContextPadProvider: ['type', CustomContextPadProvider]
},
TranslationModule
];
export default Module;

View File

@ -1,50 +0,0 @@
export default {
'bpmn:EndEvent': {},
'bpmn:StartEvent': {
initiator: true,
formKey: true
},
'bpmn:UserTask': {
allocationType: true,
specifyDesc: true,
multipleUserAuditType: true,
async: true,
priority: true,
skipExpression: true,
dueDate: true,
taskListener: true,
executionListener: true
},
'bpmn:ServiceTask': {
async: true,
skipExpression: true,
isForCompensation: true,
triggerable: true,
class: true
},
'bpmn:ScriptTask': {
async: true,
isForCompensation: true,
autoStoreVariables: true
},
'bpmn:ManualTask': {
async: true,
isForCompensation: true
},
'bpmn:ReceiveTask': {
async: true,
isForCompensation: true
},
'bpmn:SendTask': {
async: true,
isForCompensation: true
},
'bpmn:BusinessRuleTask': {
async: true,
isForCompensation: true,
ruleVariablesInput: true,
rules: true,
resultVariable: true,
exclude: true
}
};

View File

@ -1,284 +0,0 @@
.djs-palette {
width: 300px;
.bpmn-icon-hand-tool:hover {
&:after {
content: '启动手动工具';
position: absolute;
left: 45px;
width: 120px;
font-size: 15px;
font-weight: bold;
color: #3a84de;
border-radius: 2px;
border: 1px solid #cccccc;
background-color: #fafafa;
opacity: 0.8;
}
}
.bpmn-icon-lasso-tool:hover {
&:after {
content: '启动套索工具';
position: absolute;
left: 100px;
width: 120px;
font-size: 15px;
font-weight: bold;
color: #3a84de;
border-radius: 2px;
border: 1px solid #cccccc;
background-color: #fafafa;
opacity: 0.8;
}
}
.bpmn-icon-space-tool:hover {
&:after {
content: '启动创建/删除空间工具';
position: absolute;
left: 45px;
width: 170px;
font-size: 15px;
font-weight: bold;
color: #3a84de;
border-radius: 2px;
border: 1px solid #cccccc;
background-color: #fafafa;
opacity: 0.8;
}
}
.bpmn-icon-connection-multi:hover {
&:after {
content: '启动全局连接工具';
position: absolute;
left: 100px;
width: 140px;
font-size: 15px;
font-weight: bold;
color: #3a84de;
border-radius: 2px;
border: 1px solid #cccccc;
background-color: #fafafa;
opacity: 0.8;
}
}
.bpmn-icon-start-event-none:hover {
&:after {
content: '创建开始事件';
position: absolute;
left: 45px;
width: 120px;
font-size: 15px;
font-weight: bold;
color: #3a84de;
border-radius: 2px;
border: 1px solid #cccccc;
background-color: #fafafa;
opacity: 0.8;
}
}
.bpmn-icon-intermediate-event-none:hover {
&:after {
content: '创建中间/边界事件';
position: absolute;
left: 100px;
width: 140px;
font-size: 15px;
font-weight: bold;
color: #3a84de;
border-radius: 2px;
border: 1px solid #cccccc;
background-color: #fafafa;
opacity: 0.8;
}
}
.bpmn-icon-end-event-none:hover {
&:after {
content: '创建结束事件';
position: absolute;
left: 45px;
width: 120px;
font-size: 15px;
font-weight: bold;
color: #3a84de;
border-radius: 2px;
border: 1px solid #cccccc;
background-color: #fafafa;
opacity: 0.8;
}
}
.bpmn-icon-gateway-none:hover {
&:after {
content: '创建网关';
position: absolute;
left: 100px;
width: 90px;
font-size: 15px;
font-weight: bold;
color: #3a84de;
border-radius: 2px;
border: 1px solid #cccccc;
background-color: #fafafa;
opacity: 0.8;
}
}
.bpmn-icon-gateway-parallel:hover {
&:after {
content: '创建并行网关';
position: absolute;
left: 45px;
width: 120px;
font-size: 15px;
font-weight: bold;
color: #3a84de;
border-radius: 2px;
border: 1px solid #cccccc;
background-color: #fafafa;
opacity: 0.8;
}
}
.bpmn-icon-gateway-eventbased:hover {
&:after {
content: '创建事件网关';
position: absolute;
left: 100px;
width: 120px;
font-size: 15px;
font-weight: bold;
color: #3a84de;
border-radius: 2px;
border: 1px solid #cccccc;
background-color: #fafafa;
opacity: 0.8;
}
}
.bpmn-icon-task:hover {
&:after {
content: '创建任务';
position: absolute;
left: 45px;
width: 80px;
font-size: 15px;
font-weight: bold;
color: #3a84de;
border-radius: 2px;
border: 1px solid #cccccc;
background-color: #fafafa;
opacity: 0.8;
}
}
.bpmn-icon-subprocess-expanded:hover {
&:after {
content: '创建可折叠子流程';
position: absolute;
left: 100px;
width: 140px;
font-size: 15px;
font-weight: bold;
color: #3a84de;
border-radius: 2px;
border: 1px solid #cccccc;
background-color: #fafafa;
opacity: 0.8;
}
}
.bpmn-icon-user-task:hover {
&:after {
content: '创建用户任务';
position: absolute;
left: 45px;
width: 120px;
font-size: 15px;
font-weight: bold;
color: #3a84de;
border-radius: 2px;
border: 1px solid #cccccc;
background-color: #fafafa;
opacity: 0.8;
}
}
.task-multi-instance:hover {
&:after {
content: '创建多实例用户任务';
position: absolute;
left: 100px;
width: 160px;
font-size: 15px;
font-weight: bold;
color: #3a84de;
border-radius: 2px;
border: 1px solid #cccccc;
background-color: #fafafa;
opacity: 0.8;
}
}
.bpmn-icon-participant:hover {
&:after {
content: '创建泳池/泳道';
position: absolute;
left: 45px;
width: 120px;
font-size: 15px;
font-weight: bold;
color: #3a84de;
border-radius: 2px;
border: 1px solid #cccccc;
background-color: #fafafa;
opacity: 0.8;
}
}
.bpmn-icon-data-object {
display: none;
&:hover {
&:after {
content: '创建数据对象';
position: absolute;
left: 45px;
width: 120px;
font-size: 15px;
font-weight: bold;
color: #3a84de;
border-radius: 2px;
border: 1px solid #cccccc;
background-color: #fafafa;
opacity: 0.8;
}
}
}
.bpmn-icon-data-store {
display: none;
&:hover {
&:after {
content: '创建数据存储';
position: absolute;
left: 100px;
width: 120px;
font-size: 15px;
font-weight: bold;
color: #3a84de;
border-radius: 2px;
border: 1px solid #cccccc;
background-color: #fafafa;
opacity: 0.8;
}
}
}
.bpmn-icon-group {
display: none;
&:hover {
&:after {
content: '创建分组';
position: absolute;
left: 100px;
width: 100px;
font-size: 15px;
font-weight: bold;
color: #3a84de;
border-radius: 2px;
border: 1px solid #cccccc;
background-color: #fafafa;
opacity: 0.8;
}
}
}
}

View File

@ -1,145 +0,0 @@
import showConfig from '../assets/showConfig';
import type { ModdleElement } from 'bpmn';
import useModelerStore from '@/store/modules/modeler';
import { MultiInstanceTypeEnum } from '@/enums/bpmn/IndexEnums';
interface Options {
element: ModdleElement;
}
export default (ops: Options) => {
const { element } = ops;
const { getModeling, getModdle } = useModelerStore();
const modeling = getModeling();
const moddle = getModdle();
/**
*
*/
const elementType = computed(() => {
const bizObj = element.businessObject;
return bizObj.eventDefinitions ? bizObj.eventDefinitions[0].$type : bizObj.$type;
});
/**
*
*/
const config = computed(() => showConfig[elementType.value] || {});
/**
*
* @param elementType
* @param properties
* @param parent
*/
const createModdleElement = (elementType: string, properties: any, parent: ModdleElement) => {
const element = moddle.create(elementType, properties);
parent && (element.$parent = parent);
return element;
};
/**
*
*/
const getExtensionElements = (create = true) => {
let extensionElements = element.businessObject.get<ModdleElement>('extensionElements');
if (!extensionElements && create) {
extensionElements = createModdleElement('bpmn:ExtensionElements', { values: [] }, element.businessObject);
modeling.updateModdleProperties(element, element.businessObject, { extensionElements });
}
return extensionElements;
};
/**
* extensionElements下的properties
* @param extensionElements Element下的extensionElements下的Properties
*/
const getPropertiesElements = (extensionElements?: ModdleElement) => {
if (!extensionElements) {
extensionElements = getExtensionElements();
}
let propertiesElements = extensionElements.values.find((item) => item.$type === 'flowable:properties');
if (!propertiesElements) {
propertiesElements = createModdleElement('flowable:properties', { values: [] }, extensionElements);
modeling.updateModdleProperties(element, extensionElements, {
values: [...extensionElements.get<[]>('values'), propertiesElements]
});
}
return propertiesElements;
};
/**
*
* @param properties
*/
const updateProperties = (properties: any) => {
modeling.updateProperties(element, properties);
};
/**
*
* @param updateElement
* @param properties
*/
const updateModdleProperties = (updateElement, properties: any) => {
modeling.updateModdleProperties(element, updateElement, properties);
};
/**
* Property属性
* @param name key值
* @param value
*/
const updateProperty = (name: string, value: string) => {
const propertiesElements = getPropertiesElements();
let propertyElements = propertiesElements.values.find((item) => item.name === name);
if (!propertyElements) {
propertyElements = createModdleElement('flowable:property', { name: name, value: value }, propertiesElements);
modeling.updateModdleProperties(element, propertiesElements, {
values: [...propertiesElements.get('values'), propertyElements]
});
} else {
propertyElements.name = name;
propertyElements.value = value;
}
return propertyElements;
};
const idChange = (newVal: string) => {
if (newVal) {
updateProperties({ id: newVal });
}
};
const nameChange = (newVal: string) => {
if (newVal) {
updateProperties({ name: newVal });
}
};
const formKeyChange = (newVal: string) => {
updateProperties({ formKey: newVal });
};
const constant = {
MultiInstanceType: [
{ id: '373d4b81-a0d1-4eb8-8685-0d2fb1b468e2', label: '无', value: MultiInstanceTypeEnum.NONE },
{ id: 'b5acea7c-b7e5-46b0-8778-390db091bdab', label: '串行', value: MultiInstanceTypeEnum.SERIAL },
{ id: 'b4f0c683-1ccc-43c4-8380-e1b998986caf', label: '并行', value: MultiInstanceTypeEnum.PARALLEL }
]
};
return {
elementType,
constant,
showConfig: config,
updateProperties,
updateProperty,
updateModdleProperties,
createModdleElement,
idChange,
nameChange,
formKeyChange,
getExtensionElements,
getPropertiesElements
};
};

View File

@ -1,34 +0,0 @@
import type { ModdleElement } from 'bpmn';
interface Options {
element: ModdleElement;
}
interface Data {
id: string;
}
export default (ops: Options) => {
const { element } = ops;
const parseData = <T>(): T => {
const result = {
...element.businessObject,
...element.businessObject.$attrs
};
// 移除flowable前缀格式化数组
for (const key in result) {
if (key.indexOf('flowable:') === 0) {
const newKey = key.replace('flowable:', '');
result[newKey] = result[key];
delete result[key];
}
}
return { ...result } as T;
};
return {
parseData
};
};

View File

@ -1,496 +0,0 @@
<template>
<div class="containers-bpmn">
<!-- dark模式下 连接线的箭头样式 -->
<svg width="0" height="0" style="position: absolute">
<defs>
<marker id="markerArrow-dark-mode" viewBox="0 0 20 20" refX="11" refY="10" markerWidth="10" markerHeight="10" orient="auto">
<path d="M 1 5 L 11 10 L 1 15 Z" class="arrow-dark" />
</marker>
</defs>
</svg>
<div v-loading="loading" class="app-containers-bpmn">
<el-container class="h-full">
<el-container style="align-items: stretch">
<el-header>
<div class="process-toolbar">
<el-space wrap :size="10">
<el-tooltip effect="dark" content="自适应屏幕" placement="bottom">
<el-button size="small" icon="Rank" @click="fitViewport" />
</el-tooltip>
<el-tooltip effect="dark" content="放大" placement="bottom">
<el-button size="small" icon="ZoomIn" @click="zoomViewport(true)" />
</el-tooltip>
<el-tooltip effect="dark" content="缩小" placement="bottom">
<el-button size="small" icon="ZoomOut" @click="zoomViewport(false)" />
</el-tooltip>
<el-tooltip effect="dark" content="后退" placement="bottom">
<el-button size="small" icon="Back" @click="bpmnModeler.get('commandStack').undo()" />
</el-tooltip>
<el-tooltip effect="dark" content="前进" placement="bottom">
<el-button size="small" icon="Right" @click="bpmnModeler.get('commandStack').redo()" />
</el-tooltip>
</el-space>
<el-space wrap :size="10" style="float: right; padding-right: 10px">
<el-button size="small" type="primary" @click="saveXml"> </el-button>
<el-dropdown size="small">
<el-button size="small" type="primary"> </el-button>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item icon="Document" @click="previewXML">XML预览</el-dropdown-item>
<el-dropdown-item icon="View" @click="previewSVG"> SVG预览</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
<el-dropdown size="small">
<el-button size="small" type="primary"> </el-button>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item icon="Download" @click="downloadXML">下载XML</el-dropdown-item>
<el-dropdown-item icon="Download" @click="downloadSVG"> 下载SVG</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</el-space>
</div>
</el-header>
<div ref="canvas" class="canvas" />
</el-container>
<div :class="{ 'process-panel': true, 'hide': panelFlag }">
<div class="process-panel-bar" @click="panelBarClick">
<div class="open-bar">
<el-link type="default" :underline="false">
<svg-icon class-name="open-bar" :icon-class="panelFlag ? 'caret-back' : 'caret-forward'"></svg-icon>
</el-link>
</div>
</div>
<transition enter-active-class="animate__animated animate__fadeIn">
<div v-show="showPanel" v-if="bpmnModeler" class="panel-content">
<PropertyPanel :modeler="bpmnModeler" />
</div>
</transition>
</div>
</el-container>
</div>
</div>
<div>
<el-dialog v-model="perviewXMLShow" title="XML预览" width="80%" append-to-body>
<highlightjs :code="xmlStr" language="XML" />
</el-dialog>
</div>
<div>
<el-dialog v-model="perviewSVGShow" title="SVG预览" width="80%" append-to-body>
<div style="text-align: center" v-html="svgData" />
</el-dialog>
</div>
</template>
<script lang="ts" setup name="BpmnDesign">
import 'bpmn-js/dist/assets/diagram-js.css';
import 'bpmn-js/dist/assets/bpmn-font/css/bpmn.css';
import 'bpmn-js/dist/assets/bpmn-font/css/bpmn-codes.css';
import 'bpmn-js/dist/assets/bpmn-font/css/bpmn-embedded.css';
import './assets/style/index.scss';
import type { Canvas, Modeler } from 'bpmn';
import PropertyPanel from './panel/index.vue';
import BpmnModeler from 'bpmn-js/lib/Modeler.js';
import defaultXML from './assets/defaultXML';
import flowableModdle from './assets/moddle/flowable';
import Modules from './assets/module/index';
import useModelerStore from '@/store/modules/modeler';
import useDialog from '@/hooks/useDialog';
const emit = defineEmits(['closeCallBack', 'saveCallBack']);
const { visible, title, openDialog, closeDialog } = useDialog({
title: '编辑流程'
});
const modelerStore = useModelerStore();
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const panelFlag = ref(false);
const showPanel = ref(true);
const canvas = ref<HTMLDivElement>();
const panel = ref<HTMLDivElement>();
const bpmnModeler = ref<Modeler>();
const zoom = ref(1);
const perviewXMLShow = ref(false);
const perviewSVGShow = ref(false);
const xmlStr = ref('');
const svgData = ref('');
const loading = ref(false);
const panelBarClick = () => {
//
panelFlag.value = !panelFlag.value;
setTimeout(() => {
showPanel.value = !panelFlag.value;
}, 100);
};
/**
* 初始化Canvas
*/
const initCanvas = () => {
bpmnModeler.value = new BpmnModeler({
container: canvas.value,
//
keyboard: {
bindTo: window // window
},
propertiesPanel: {
parent: panel.value
},
additionalModules: Modules,
moddleExtensions: {
flowable: flowableModdle
}
});
};
/**
* 初始化Model
*/
const initModel = () => {
if (modelerStore.getModeler()) {
modelerStore.getModeler().destroy();
modelerStore.setModeler(undefined);
}
modelerStore.setModeler(bpmnModeler.value);
};
/**
* 新建
*/
const newDiagram = async () => {
await proxy?.$modal.confirm('是否确认新建');
initDiagram();
};
/**
* 初始化
*/
const initDiagram = (xml?: string) => {
if (!xml) xml = defaultXML;
bpmnModeler.value.importXML(xml);
};
/**
* 自适应屏幕
*/
const fitViewport = () => {
zoom.value = bpmnModeler.value.get<Canvas>('canvas').zoom('fit-viewport');
const bbox = document.querySelector<SVGGElement>('.app-containers-bpmn .viewport').getBBox();
const currentViewBox = bpmnModeler.value.get<Canvas>('canvas').viewbox();
const elementMid = {
x: bbox.x + bbox.width / 2 - 65,
y: bbox.y + bbox.height / 2
};
bpmnModeler.value.get<Canvas>('canvas').viewbox({
x: elementMid.x - currentViewBox.width / 2,
y: elementMid.y - currentViewBox.height / 2,
width: currentViewBox.width,
height: currentViewBox.height
});
zoom.value = (bbox.width / currentViewBox.width) * 1.8;
};
/**
* 放大或者缩小
* @param zoomIn true 放大 | false 缩小
*/
const zoomViewport = (zoomIn = true) => {
zoom.value = bpmnModeler.value.get<Canvas>('canvas').zoom();
zoom.value += zoomIn ? 0.1 : -0.1;
bpmnModeler.value.get<Canvas>('canvas').zoom(zoom.value);
};
/**
* 下载XML
*/
const downloadXML = async () => {
try {
const { xml } = await bpmnModeler.value.saveXML({ format: true });
downloadFile(`${getProcessElement().name}.bpmn20.xml`, xml, 'application/xml');
} catch (e) {
proxy?.$modal.msgError(e);
}
};
/**
* 下载SVG
*/
const downloadSVG = async () => {
try {
const { svg } = await bpmnModeler.value.saveSVG();
downloadFile(getProcessElement().name, svg, 'image/svg+xml');
} catch (e) {
proxy?.$modal.msgError(e);
}
};
/**
* XML预览
*/
const previewXML = async () => {
try {
const { xml } = await bpmnModeler.value.saveXML({ format: true });
xmlStr.value = xml;
perviewXMLShow.value = true;
} catch (e) {
proxy?.$modal.msgError(e);
}
};
/**
* SVG预览
*/
const previewSVG = async () => {
try {
const { svg } = await bpmnModeler.value.saveSVG();
svgData.value = svg;
perviewSVGShow.value = true;
} catch (e) {
proxy?.$modal.msgError(e);
}
};
const curNodeInfo = reactive({
curType: '', //
curNode: '',
expValue: '' //
});
const downloadFile = (fileName: string, data: any, type: string) => {
const a = document.createElement('a');
const url = window.URL.createObjectURL(new Blob([data], { type: type }));
a.href = url;
a.download = fileName;
a.click();
window.URL.revokeObjectURL(url);
};
const getProcessElement = () => {
const rootElements = bpmnModeler.value?.getDefinitions().rootElements;
for (let i = 0; i < rootElements.length; i++) {
if (rootElements[i].$type === 'bpmn:Process') return rootElements[i];
}
};
const getProcess = () => {
const element = getProcessElement();
return {
id: element.id,
name: element.name
};
};
const saveXml = async () => {
const { xml } = await bpmnModeler.value.saveXML({ format: true });
const { svg } = await bpmnModeler.value.saveSVG();
const process = getProcess();
let data = {
xml: xml,
svg: svg,
key: process.id,
name: process.name,
loading: loading
};
emit('saveCallBack', data);
};
const open = (xml?: string) => {
openDialog();
nextTick(() => {
initDiagram(xml);
});
};
const close = () => {
closeDialog();
};
onMounted(() => {
nextTick(() => {
initCanvas();
initModel();
});
});
/**
* 对外暴露子组件方法
*/
defineExpose({
initDiagram,
saveXml,
open,
close
});
</script>
<style lang="scss">
/** 夜间模式 线条的颜色 */
$stroke-color-dark: white;
$bpmn-font-size: 12px;
/** 日间模式 字体颜色 */
$bpmn-font-color-dark: white;
/** 夜间模式 字体颜色 */
$bpmn-font-color-light: #222;
/* 背景网格 */
@mixin djs-container {
background-image: linear-gradient(90deg, hsl(0deg 0% 78.4% / 15%) 10%, transparent 0), linear-gradient(hsl(0deg 0% 78.4% / 15%) 10%, transparent 0) !important;
background-size: 10px 10px !important;
}
html[class='light'] {
/** 从左侧拖动时的背景图 */
svg.new-parent {
@include djs-container;
}
/** 双击编辑元素时样式保持一致 */
div.djs-direct-editing-parent {
border-radius: 10px;
background-color: transparent !important;
color: $bpmn-font-color-light;
}
g.djs-visual {
.djs-label {
fill: $bpmn-font-color-light !important;
font-size: $bpmn-font-size !important;
}
}
}
html[class='dark'] {
/** dark模式下 连接线的箭头样式 */
.arrow-dark {
stroke-width: 1px;
stroke-linecap: round;
stroke: $stroke-color-dark;
fill: $stroke-color-dark;
stroke-linejoin: round;
}
/** 从左侧拖动时的背景图 */
svg.new-parent {
background-color: black !important;
@include djs-container;
}
/** 双击编辑元素时样式保持一致 */
div.djs-direct-editing-parent {
border-radius: 10px;
background-color: transparent !important;
color: $bpmn-font-color-dark;
}
/** 元素相关设置 */
g.djs-visual {
/** 元素边框 需要去除文字(.djs-label) */
& > *:first-child:not(.djs-label) {
stroke: $stroke-color-dark !important;
}
/** 字体颜色 */
.djs-label {
fill: $bpmn-font-color-dark !important;
font-size: $bpmn-font-size !important;
}
/* 连接线样式 */
path[data-corner-radius] {
stroke: $stroke-color-dark !important;
marker-end: url('#markerArrow-dark-mode') !important;
}
}
}
.containers-bpmn {
height: 100%;
.app-containers-bpmn {
width: 100%;
height: 100%;
.canvas {
width: 100%;
height: 100%;
@include djs-container;
}
.el-header {
height: 35px;
padding: 0;
}
.process-panel {
transition: width 0.25s ease-in;
.process-panel-bar {
width: 34px;
height: 40px;
.open-bar {
width: 34px;
line-height: 40px;
}
}
//
&.hide {
width: 34px;
overflow: hidden;
padding: 0;
.process-panel-bar {
width: 34px;
height: 100%;
box-sizing: border-box;
display: block;
text-align: left;
line-height: 34px;
}
.process-panel-bar:hover {
background-color: var(--bpmn-panel-bar-background-color);
}
}
}
}
}
pre {
margin: 0;
height: 100%;
max-height: calc(80vh - 32px);
overflow-x: hidden;
overflow-y: auto;
.hljs {
word-break: break-word;
white-space: pre-wrap;
padding: 0.5em;
}
}
.open-bar {
font-size: 20px;
cursor: pointer;
text-align: center;
}
.process-panel {
box-sizing: border-box;
padding: 0 8px 0 8px;
border-left: 1px solid var(--bpmn-panel-border);
box-shadow: var(--bpmn-panel-box-shadow) 0 0 8px;
max-height: 100%;
width: 25%;
height: calc(100vh - 100px);
.el-collapse {
height: calc(100vh - 182px);
overflow: auto;
}
}
//
//:deep(.djs-palette) {
// opacity: 0.3;
// transition: all 1s;
//}
//
//:deep(.djs-palette:hover) {
// opacity: 1;
// transition: all 1s;
//}
</style>

View File

@ -1,68 +0,0 @@
<template>
<div>
<el-collapse v-model="currentCollapseItem">
<el-collapse-item name="1">
<template #title>
<div class="collapse__title">
<el-icon>
<InfoFilled />
</el-icon>
常规
</div>
</template>
<div>
<el-form ref="formRef" :model="formData" :rules="formRules" label-width="80px">
<el-form-item prop="id" label="节点 ID">
<el-input v-model="formData.id" @change="idChange"> </el-input>
</el-form-item>
<el-form-item prop="name" label="节点名称">
<el-input v-model="formData.name" @change="nameChange"> </el-input>
</el-form-item>
</el-form>
</div>
</el-collapse-item>
<el-collapse-item name="2">
<template #title>
<div class="collapse__title">
<el-icon>
<BellFilled />
</el-icon>
执行监听器
</div>
</template>
<div>
<ExecutionListener :element="element"></ExecutionListener>
</div>
</el-collapse-item>
</el-collapse>
</div>
</template>
<script setup lang="ts">
import useParseElement from '../hooks/useParseElement';
import usePanel from '../hooks/usePanel';
import type { Modeler, ModdleElement } from 'bpmn';
import type { GatewayPanel } from 'bpmnDesign';
import ExecutionListener from './property/ExecutionListener.vue';
interface PropType {
element: ModdleElement;
}
const props = withDefaults(defineProps<PropType>(), {});
const { nameChange, idChange } = usePanel({
element: toRaw(props.element)
});
const { parseData } = useParseElement({
element: toRaw(props.element)
});
const currentCollapseItem = ref(['1', '2']);
const formData = ref(parseData<GatewayPanel>());
const formRules = ref<ElFormRules>({
processCategory: [{ required: true, message: '请选择', trigger: 'blur' }],
id: [{ required: true, message: '请输入', trigger: 'blur' }],
name: [{ required: true, message: '请输入', trigger: 'blur' }]
});
</script>
<style lang="scss" scoped></style>

View File

@ -1,68 +0,0 @@
<template>
<div>
<el-collapse v-model="currentCollapseItem">
<el-collapse-item name="1">
<template #title>
<div class="collapse__title">
<el-icon>
<InfoFilled />
</el-icon>
常规
</div>
</template>
<div>
<el-form ref="formRef" :model="formData" :rules="formRules" label-width="90px">
<el-form-item prop="id" label="节点 ID">
<el-input v-model="formData.id" @change="idChange"></el-input>
</el-form-item>
<el-form-item prop="name" label="节点名称">
<el-input v-model="formData.name" @change="nameChange"></el-input>
</el-form-item>
</el-form>
</div>
</el-collapse-item>
<el-collapse-item name="2">
<template #title>
<div class="collapse__title">
<el-icon>
<BellFilled />
</el-icon>
执行监听器
</div>
</template>
<div>
<ExecutionListener :element="element"></ExecutionListener>
</div>
</el-collapse-item>
</el-collapse>
</div>
</template>
<script setup lang="ts">
import useParseElement from '../hooks/useParseElement';
import usePanel from '../hooks/usePanel';
import ExecutionListener from './property/ExecutionListener.vue';
import type { ModdleElement } from 'bpmn';
import type { ParticipantPanel } from 'bpmnDesign';
interface PropType {
element: ModdleElement;
}
const props = withDefaults(defineProps<PropType>(), {});
const { nameChange, idChange } = usePanel({
element: toRaw(props.element)
});
const { parseData } = useParseElement({
element: toRaw(props.element)
});
const formData = ref(parseData<ParticipantPanel>());
const currentCollapseItem = ref(['1', '2']);
const formRules = ref<ElFormRules>({
id: [{ required: true, message: '请输入', trigger: 'blur' }],
name: [{ required: true, message: '请输入', trigger: 'blur' }]
});
</script>
<style lang="scss" scoped></style>

View File

@ -1,71 +0,0 @@
<template>
<div>
<el-collapse v-model="currentCollapseItem">
<el-collapse-item name="1">
<template #title>
<div class="collapse__title">
<el-icon>
<InfoFilled />
</el-icon>
常规
</div>
</template>
<div>
<el-form ref="formRef" :model="formData" :rules="formRules" label-width="80px">
<el-form-item label="流程标识" prop="id">
<el-input v-model="formData.id" @change="idChange"></el-input>
</el-form-item>
<el-form-item label="流程名称" prop="name">
<el-input v-model="formData.name" @change="nameChange"></el-input>
</el-form-item>
</el-form>
</div>
</el-collapse-item>
<el-collapse-item name="2">
<template #title>
<div class="collapse__title">
<el-icon>
<BellFilled />
</el-icon>
执行监听器
</div>
</template>
<div>
<ExecutionListener :element="element"></ExecutionListener>
</div>
</el-collapse-item>
</el-collapse>
</div>
</template>
<script setup lang="ts">
import ExecutionListener from './property/ExecutionListener.vue';
import useParseElement from '../hooks/useParseElement';
import usePanel from '../hooks/usePanel';
import type { Modeler, ModdleElement } from 'bpmn';
import type { ProcessPanel } from 'bpmnDesign';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
interface PropType {
element: ModdleElement;
}
const props = withDefaults(defineProps<PropType>(), {});
const { parseData } = useParseElement({
element: toRaw(props.element)
});
const { idChange, nameChange } = usePanel({
element: toRaw(props.element)
});
const currentCollapseItem = ref(['1', '2']);
const formData = ref<ProcessPanel>(parseData<ProcessPanel>());
const formRules = ref<ElFormRules>({
id: [{ required: true, message: '请输入', trigger: 'blur' }],
name: [{ required: true, message: '请输入', trigger: 'blur' }]
});
</script>
<style scoped lang="scss"></style>

View File

@ -1,95 +0,0 @@
<template>
<div>
<el-collapse v-model="currentCollapseItem">
<el-collapse-item name="1">
<template #title>
<div class="collapse__title">
<el-icon>
<InfoFilled />
</el-icon>
常规
</div>
</template>
<div>
<el-form ref="formRef" :model="formData" :rules="formRules" label-width="90px">
<el-form-item prop="id" label="节点 ID">
<el-input v-model="formData.id" @change="idChange"> </el-input>
</el-form-item>
<el-form-item prop="name" label="节点名称">
<el-input v-model="formData.name" @change="nameChange"> </el-input>
</el-form-item>
<el-form-item prop="conditionExpression" label="跳转条件">
<el-input v-model="formData.conditionExpressionValue" @change="conditionExpressionChange"> </el-input>
</el-form-item>
<el-form-item prop="skipExpression" label="跳过表达式">
<el-input v-model="formData.skipExpression" @change="skipExpressionChange"> </el-input>
</el-form-item>
</el-form>
</div>
</el-collapse-item>
<el-collapse-item name="2">
<template #title>
<div class="collapse__title">
<el-icon>
<BellFilled />
</el-icon>
执行监听器
</div>
</template>
<div>
<ExecutionListener :element="element"></ExecutionListener>
</div>
</el-collapse-item>
</el-collapse>
</div>
</template>
<script setup lang="ts">
import useParseElement from '../hooks/useParseElement';
import useModelerStore from '@/store/modules/modeler';
import usePanel from '../hooks/usePanel';
import ExecutionListener from './property/ExecutionListener.vue';
import type { Modeler, ModdleElement } from 'bpmn';
import type { SequenceFlowPanel } from 'bpmnDesign';
interface PropType {
element: ModdleElement;
}
const props = withDefaults(defineProps<PropType>(), {});
const { nameChange, idChange, updateProperties } = usePanel({
element: toRaw(props.element)
});
const { parseData } = useParseElement({
element: toRaw(props.element)
});
const moddle = useModelerStore().getModdle();
const currentCollapseItem = ref(['1', '2']);
const formData = ref(parseData<SequenceFlowPanel>());
const formRules = ref<ElFormRules>({
processCategory: [{ required: true, message: '请选择', trigger: 'blur' }],
id: [{ required: true, message: '请输入', trigger: 'blur' }],
name: [{ required: true, message: '请输入', trigger: 'blur' }]
});
const conditionExpressionChange = (val: string) => {
if (val) {
const newCondition = moddle.create('bpmn:FormalExpression', { body: val });
updateProperties({ conditionExpression: newCondition });
} else {
updateProperties({ conditionExpression: null });
}
};
const skipExpressionChange = (val: string) => {
updateProperties({ 'flowable:skipExpression': val });
};
onBeforeMount(() => {
if (formData.value.conditionExpression) {
formData.value.conditionExpressionValue = formData.value.conditionExpression.body;
}
});
</script>
<style lang="scss" scoped></style>

View File

@ -1,67 +0,0 @@
<template>
<div>
<el-collapse v-model="currentCollapseItem">
<el-collapse-item name="1">
<template #title>
<div class="collapse__title">
<el-icon>
<InfoFilled />
</el-icon>
常规
</div>
</template>
<div>
<el-form ref="formRef" :model="formData" :rules="formRules" label-width="90px">
<el-form-item prop="id" label="节点 ID">
<el-input v-model="formData.id" @change="idChange"> </el-input>
</el-form-item>
<el-form-item prop="name" label="节点名称">
<el-input v-model="formData.name" @change="nameChange"> </el-input>
</el-form-item>
</el-form>
</div>
</el-collapse-item>
<el-collapse-item name="2">
<template #title>
<div class="collapse__title">
<el-icon>
<BellFilled />
</el-icon>
执行监听器
</div>
</template>
<div>
<ExecutionListener :element="element"></ExecutionListener>
</div>
</el-collapse-item>
</el-collapse>
</div>
</template>
<script setup lang="ts">
import ExecutionListener from './property/ExecutionListener.vue';
import useParseElement from '../hooks/useParseElement';
import usePanel from '../hooks/usePanel';
import type { Modeler, ModdleElement } from 'bpmn';
import type { StartEndPanel } from 'bpmnDesign';
interface PropType {
element: ModdleElement;
}
const props = withDefaults(defineProps<PropType>(), {});
const { nameChange, idChange } = usePanel({
element: toRaw(props.element)
});
const { parseData } = useParseElement({
element: toRaw(props.element)
});
const formData = ref(parseData<StartEndPanel>());
const currentCollapseItem = ref(['1', '2']);
const formRules = ref<ElFormRules>({
id: [{ required: true, message: '请输入', trigger: 'blur' }],
name: [{ required: true, message: '请输入', trigger: 'blur' }]
});
</script>
<style lang="scss" scoped></style>

View File

@ -1,193 +0,0 @@
<template>
<div>
<el-form ref="formRef" :model="formData" :rules="formRules" label-width="90px">
<el-collapse v-model="currentCollapseItem">
<el-collapse-item name="1">
<template #title>
<div class="collapse__title">
<el-icon>
<InfoFilled />
</el-icon>
常规
</div>
</template>
<div>
<el-form-item prop="id" label="节点 ID">
<el-input v-model="formData.id" @change="idChange"> </el-input>
</el-form-item>
<el-form-item prop="name" label="节点名称">
<el-input v-model="formData.name" @change="nameChange"> </el-input>
</el-form-item>
</div>
</el-collapse-item>
<el-collapse-item name="2">
<template #title>
<div class="collapse__title">
<el-icon>
<BellFilled />
</el-icon>
执行监听器
</div>
</template>
<div>
<ExecutionListener :element="element"></ExecutionListener>
</div>
</el-collapse-item>
<el-collapse-item name="3">
<template #title>
<div class="collapse__title">
<el-icon>
<HelpFilled />
</el-icon>
多实例
</div>
</template>
<div>
<el-form-item label="多实例类型">
<el-select v-model="formData.multiInstanceType" @change="multiInstanceTypeChange">
<el-option v-for="item in constant.MultiInstanceType" :key="item.id" :value="item.value" :label="item.label"> </el-option>
</el-select>
</el-form-item>
<div v-if="formData.multiInstanceType !== MultiInstanceTypeEnum.NONE">
<el-form-item label="集合">
<template #label>
<span>
集合
<el-tooltip placement="top">
<el-icon><QuestionFilled /></el-icon>
<template #content>
属性会作为表达式进行解析如果表达式解析为字符串而不是一个集合<br />
不论是因为本身配置的就是静态字符串值还是表达式计算结果为字符串<br />
这个字符串都会被当做变量名并从流程变量中用于获取实际的集合
</template>
</el-tooltip>
</span>
</template>
<el-input v-model="formData.collection" @change="collectionChange"></el-input>
</el-form-item>
<el-form-item label="元素变量">
<template #label>
<span>
元素变量
<el-tooltip placement="top">
<el-icon><QuestionFilled /></el-icon>
<template #content>
每创建一个用户任务前先以该元素变量为label集合中的一项为value<br />
创建局部流程变量该局部流程变量被用于指派用户任务<br />
一般来说该字符串应与指定人员变量相同
</template>
</el-tooltip>
</span>
</template>
<el-input v-model="formData.elementVariable" @change="elementVariableChange"> </el-input>
</el-form-item>
<el-form-item label="完成条件">
<template #label>
<span>
完成条件
<el-tooltip placement="top">
<el-icon><QuestionFilled /></el-icon>
<template #content>
多实例活动在所有实例都完成时结束然而也可以指定一个表达式在每个实例<br />
结束时进行计算当表达式计算为true时将销毁所有剩余的实例并结束多实例<br />
活动继续执行流程例如 ${nrOfCompletedInstances/nrOfInstances >= 0.6 }<br />
表示当任务完成60%该节点就算完成
</template>
</el-tooltip>
</span>
</template>
<el-input v-model="formData.completionCondition" @change="completionConditionChange"> </el-input>
</el-form-item>
</div>
</div>
</el-collapse-item>
</el-collapse>
</el-form>
</div>
</template>
<script setup lang="ts">
import ExecutionListener from './property/ExecutionListener.vue';
import useParseElement from '../hooks/useParseElement';
import usePanel from '../hooks/usePanel';
import type { ModdleElement } from 'bpmn';
import type { SubProcessPanel } from 'bpmnDesign';
import { MultiInstanceTypeEnum } from '@/enums/bpmn/IndexEnums';
interface PropType {
element: ModdleElement;
}
const props = withDefaults(defineProps<PropType>(), {});
const { nameChange, idChange, updateProperties, createModdleElement, constant } = usePanel({
element: toRaw(props.element)
});
const { parseData } = useParseElement({
element: toRaw(props.element)
});
const formData = ref(parseData<SubProcessPanel>());
const currentCollapseItem = ref(['1', '2', '3']);
const multiInstanceTypeChange = (newVal) => {
if (newVal !== MultiInstanceTypeEnum.NONE) {
let loopCharacteristics = props.element.businessObject.get('loopCharacteristics');
if (!loopCharacteristics) {
loopCharacteristics = createModdleElement('bpmn:MultiInstanceLoopCharacteristics', {}, props.element.businessObject);
}
loopCharacteristics.isSequential = newVal === MultiInstanceTypeEnum.SERIAL;
updateProperties({ loopCharacteristics: loopCharacteristics });
} else {
updateProperties({ loopCharacteristics: undefined });
}
};
const collectionChange = (newVal) => {
let loopCharacteristics = props.element.businessObject.get('loopCharacteristics');
if (!loopCharacteristics) {
loopCharacteristics = createModdleElement('bpmn:MultiInstanceLoopCharacteristics', {}, props.element.businessObject);
}
loopCharacteristics.collection = newVal && newVal.length > 0 ? newVal : undefined;
updateProperties({ loopCharacteristics: loopCharacteristics });
};
const elementVariableChange = (newVal) => {
let loopCharacteristics = props.element.businessObject.get('loopCharacteristics');
if (!loopCharacteristics) {
loopCharacteristics = createModdleElement('bpmn:MultiInstanceLoopCharacteristics', {}, props.element.businessObject);
}
loopCharacteristics.elementVariable = newVal && newVal.length > 0 ? newVal : undefined;
updateProperties({ loopCharacteristics: loopCharacteristics });
};
const completionConditionChange = (newVal) => {
let loopCharacteristics = props.element.businessObject.get<ModdleElement>('loopCharacteristics');
if (!loopCharacteristics) {
loopCharacteristics = createModdleElement('bpmn:MultiInstanceLoopCharacteristics', {}, props.element.businessObject);
}
if (newVal && newVal.length > 0) {
if (!loopCharacteristics.completionCondition) {
loopCharacteristics.completionCondition = createModdleElement('bpmn:Expression', { body: newVal }, loopCharacteristics);
} else {
loopCharacteristics.completionCondition.body = newVal;
}
} else {
loopCharacteristics.completionCondition = undefined;
}
updateProperties({ loopCharacteristics: loopCharacteristics });
};
onBeforeMount(() => {
if (formData.value.loopCharacteristics) {
const loopCharacteristics = formData.value.loopCharacteristics;
formData.value.collection = loopCharacteristics.collection || '';
formData.value.elementVariable = loopCharacteristics.elementVariable || '';
formData.value.completionCondition = loopCharacteristics.completionCondition?.body || '';
formData.value.multiInstanceType = loopCharacteristics.isSequential ? MultiInstanceTypeEnum.SERIAL : MultiInstanceTypeEnum.PARALLEL;
}
});
const formRules = ref<ElFormRules>({
id: [{ required: true, message: '请输入', trigger: 'blur' }],
name: [{ required: true, message: '请输入', trigger: 'blur' }]
});
</script>
<style lang="scss" scoped></style>

View File

@ -1,491 +0,0 @@
<template>
<div>
<el-form ref="formRef" size="default" :model="formData" :rules="formRules" label-width="100px">
<el-collapse v-model="currentCollapseItem">
<el-collapse-item name="1">
<template #title>
<div class="collapse__title">
<el-icon>
<InfoFilled />
</el-icon>
常规
</div>
</template>
<div>
<el-form-item prop="id" label="节点 ID">
<el-input v-model="formData.id" @change="idChange"> </el-input>
</el-form-item>
<el-form-item prop="name" label="节点名称">
<el-input v-model="formData.name" @change="nameChange"> </el-input>
</el-form-item>
<el-form-item v-if="showConfig.skipExpression" prop="skipExpression" label="跳过表达式">
<el-input v-model="formData.skipExpression" @change="skipExpressionChange"> </el-input>
</el-form-item>
<el-form-item v-loading="formManageListLoading" prop="formKey" label="表单地址">
<el-select v-model="formData.formKey" clearable filterable placeholder="请选择表单" style="width: 260px" @change="formKeyChange">
<el-option
v-for="item in formManageList"
:key="item.id"
:label="item.formTypeName + ':' + item.formName"
:value="item.formType + ':' + item.id"
/>
</el-select>
</el-form-item>
</div>
</el-collapse-item>
<el-collapse-item name="2">
<template #title>
<div class="collapse__title">
<el-icon>
<Checked />
</el-icon>
任务
</div>
</template>
<div>
<el-form-item v-if="showConfig.async" prop="sync" label="是否异步">
<el-switch v-model="formData.async" inline-prompt active-text="是" inactive-text="否" @change="syncChange" />
</el-form-item>
<el-tabs tab-position="left" class="demo-tabs">
<el-tab-pane label="身份存储">
<el-form-item label="分配人员">
<el-input v-model="formData.assignee" @blur="blurAssignee(formData.assignee)">
<template #append>
<el-button icon="Search" type="primary" @click="openSingleUserSelect" />
</template>
</el-input>
</el-form-item>
<el-form-item label="候选人员">
<el-badge :value="selectUserLength" :max="99">
<el-button size="small" type="primary" @click="openUserSelect">选择人员</el-button>
</el-badge>
</el-form-item>
<el-form-item label="候选组">
<el-badge :value="selectRoleLength" :max="99">
<el-button size="small" type="primary" @click="openRoleSelect">选择组</el-button>
</el-badge>
</el-form-item>
</el-tab-pane>
<!-- <el-tab-pane label="固定值">
<el-form-item prop="auditUserType" label="分配类型">
<el-select v-model="formData.allocationType">
<el-option v-for="item in AllocationTypeSelect" :key="item.id" :value="item.value" :label="item.label"> </el-option>
</el-select>
</el-form-item>
<el-form-item v-if="formData.allocationType === AllocationTypeEnum.USER" label="分配人员">
<el-input v-model="formData.assignee">
<template #append>
<el-button icon="Search" type="primary" @click="openSingleUserSelect" />
</template>
</el-input>
</el-form-item>
<div v-if="formData.allocationType === AllocationTypeEnum.CANDIDATE">
<el-form-item label="候选人员">
<el-badge :value="selectUserLength" :max="99">
<el-button size="small" type="primary" @click="openUserSelect">选择人员</el-button>
</el-badge>
</el-form-item>
<el-form-item label="候选组">
<el-badge :value="selectRoleLength" :max="99">
<el-button size="small" type="primary" @click="openRoleSelect">选择组</el-button>
</el-badge>
</el-form-item>
</div>
<el-form-item v-if="formData.allocationType === AllocationTypeEnum.SPECIFY && showConfig.specifyDesc" style="">
<el-radio-group v-model="formData.specifyDesc" class="ml-4">
<el-radio v-for="item in SpecifyDesc" :key="item.id" :value="item.value" size="large">{{ item.label }}</el-radio>
</el-radio-group>
</el-form-item>
</el-tab-pane> -->
</el-tabs>
<el-form-item v-if="showConfig.dueDate" prop="dueDate" label="到期时间">
<el-input v-model="formData.dueDate" clearable @change="dueDateChange" @click="openDueDate">
<template #append>
<el-button icon="Search" type="primary" @click="openDueDate" />
</template>
</el-input>
</el-form-item>
<el-form-item v-if="showConfig.priority" prop="priority" label="优先级">
<el-input-number v-model="formData.priority" :min="0" @change="priorityChange"> </el-input-number>
</el-form-item>
</div>
</el-collapse-item>
<el-collapse-item name="3">
<template #title>
<div class="collapse__title">
<el-icon>
<HelpFilled />
</el-icon>
多实例
</div>
</template>
<div>
<el-form-item label="多实例类型">
<el-select v-model="formData.multiInstanceType" @change="multiInstanceTypeChange">
<el-option v-for="item in constant.MultiInstanceType" :key="item.id" :value="item.value" :label="item.label"> </el-option>
</el-select>
</el-form-item>
<div v-if="formData.multiInstanceType !== MultiInstanceTypeEnum.NONE">
<el-form-item label="集合">
<template #label>
<span>
集合
<el-tooltip placement="top">
<el-icon><QuestionFilled /></el-icon>
<template #content>
属性会作为表达式进行解析如果表达式解析为字符串而不是一个集合<br />
不论是因为本身配置的就是静态字符串值还是表达式计算结果为字符串<br />
这个字符串都会被当做变量名并从流程变量中用于获取实际的集合
</template>
</el-tooltip>
</span>
</template>
<el-input v-model="formData.collection" @change="collectionChange"></el-input>
</el-form-item>
<el-form-item label="元素变量">
<template #label>
<span>
元素变量
<el-tooltip placement="top">
<el-icon><QuestionFilled /></el-icon>
<template #content>
每创建一个用户任务前先以该元素变量为label集合中的一项为value<br />
创建局部流程变量该局部流程变量被用于指派用户任务<br />
一般来说该字符串应与指定人员变量相同
</template>
</el-tooltip>
</span>
</template>
<el-input v-model="formData.elementVariable" @change="elementVariableChange"> </el-input>
</el-form-item>
<el-form-item label="完成条件">
<template #label>
<span>
完成条件
<el-tooltip placement="top">
<el-icon><QuestionFilled /></el-icon>
<template #content>
多实例活动在所有实例都完成时结束然而也可以指定一个表达式在每个实例<br />
结束时进行计算当表达式计算为true时将销毁所有剩余的实例并结束多实例<br />
活动继续执行流程例如 ${nrOfCompletedInstances/nrOfInstances >= 0.6 }<br />
表示当任务完成60%该节点就算完成
</template>
</el-tooltip>
</span>
</template>
<el-input v-model="formData.completionCondition" @change="completionConditionChange"> </el-input>
</el-form-item>
</div>
</div>
</el-collapse-item>
<el-collapse-item v-if="showConfig.taskListener" name="4">
<template #title>
<div class="collapse__title">
<el-icon>
<BellFilled />
</el-icon>
任务监听器
</div>
</template>
<div>
<TaskListener v-if="showConfig.taskListener" :element="element"></TaskListener>
</div>
</el-collapse-item>
<el-collapse-item v-if="showConfig.executionListener" name="5">
<template #title>
<div class="collapse__title">
<el-icon>
<BellFilled />
</el-icon>
执行监听器
</div>
</template>
<div>
<ExecutionListener v-if="showConfig.executionListener" :element="element"></ExecutionListener>
</div>
</el-collapse-item>
<el-form-item v-if="showConfig.isForCompensation" prop="isForCompensation" label="是否为补偿">
<el-switch v-model="formData.isForCompensation" inline-prompt active-text="是" inactive-text="否" />
</el-form-item>
<el-form-item v-if="showConfig.triggerServiceTask" prop="triggerServiceTask" label="服务任务可触发">
<el-switch v-model="formData.triggerServiceTask" inline-prompt active-text="是" inactive-text="否" />
</el-form-item>
<el-form-item v-if="showConfig.autoStoreVariables" prop="autoStoreVariables" label="自动存储变量">
<el-switch v-model="formData.autoStoreVariables" inline-prompt active-text="是" inactive-text="否" />
</el-form-item>
<el-form-item v-if="showConfig.ruleVariablesInput" prop="skipExpression" label="输入变量">
<el-input v-model="formData.ruleVariablesInput"> </el-input>
</el-form-item>
<el-form-item v-if="showConfig.exclude" prop="exclude" label="排除">
<el-switch v-model="formData.exclude" inline-prompt active-text="是" inactive-text="否" />
</el-form-item>
<el-form-item v-if="showConfig.class" prop="class" label="类">
<el-input v-model="formData.class"> </el-input>
</el-form-item>
</el-collapse>
</el-form>
<UserSelect ref="userSelectRef" :data="formData.candidateUsers" @confirm-call-back="userSelectCallBack"></UserSelect>
<UserSelect ref="singleUserSelectRef" :data="formData.assignee" :multiple="false" @confirm-call-back="singleUserSelectCallBack"></UserSelect>
<RoleSelect ref="roleSelectRef" :data="formData.candidateGroups" @confirm-call-back="roleSelectCallBack"></RoleSelect>
<DueDate ref="dueDateRef" v-model="formData.dueDate" :data="formData.dueDate" @confirm-call-back="dueDateCallBack"></DueDate>
</div>
</template>
<script setup lang="ts">
import useParseElement from '../hooks/useParseElement';
import usePanel from '../hooks/usePanel';
import UserSelect from '@/components/UserSelect';
import RoleSelect from '@/components/RoleSelect';
import ExecutionListener from './property/ExecutionListener.vue';
import TaskListener from './property/TaskListener.vue';
import DueDate from './property/DueDate.vue';
import type { ModdleElement } from 'bpmn';
import type { TaskPanel } from 'bpmnDesign';
import { AllocationTypeEnum, MultiInstanceTypeEnum, SpecifyDescEnum } from '@/enums/bpmn/IndexEnums';
import { UserVO } from '@/api/system/user/types';
import { RoleVO } from '@/api/system/role/types';
import { selectListFormManage } from '@/api/workflow/formManage';
import { FormManageVO } from '@/api/workflow/formManage/types';
const formManageList = ref<FormManageVO[]>([]);
const formManageListLoading = ref(false);
interface PropType {
element: ModdleElement;
}
const props = withDefaults(defineProps<PropType>(), {});
const { showConfig, nameChange, formKeyChange, idChange, updateProperties, getExtensionElements, createModdleElement, constant } = usePanel({
element: toRaw(props.element)
});
const { parseData } = useParseElement({
element: toRaw(props.element)
});
const initFormData = {
id: '',
name: '',
dueDate: '',
multiInstanceType: MultiInstanceTypeEnum.NONE,
allocationType: AllocationTypeEnum.USER,
specifyDesc: SpecifyDescEnum.SPECIFY_SINGLE
};
const formData = ref({ ...initFormData, ...parseData<TaskPanel>() });
const assignee = ref<Partial<UserVO>>({
userName: ''
});
const currentCollapseItem = ref(['1', '2']);
const userSelectRef = ref<InstanceType<typeof UserSelect>>();
const singleUserSelectRef = ref<InstanceType<typeof UserSelect>>();
const roleSelectRef = ref<InstanceType<typeof RoleSelect>>();
const dueDateRef = ref<InstanceType<typeof DueDate>>();
const openUserSelect = () => {
userSelectRef.value.open();
};
const openSingleUserSelect = () => {
if (formData.value.assignee?.includes('$')) {
formData.value.assignee = '';
}
singleUserSelectRef.value.open();
};
const openRoleSelect = () => {
roleSelectRef.value.open();
};
const openDueDate = (e) => {
dueDateRef.value.openDialog();
};
const blurAssignee = (assignee) => {
updateProperties({ 'flowable:assignee': assignee ? assignee : undefined });
};
const singleUserSelectCallBack = (data: UserVO[]) => {
const user: UserVO = data.length !== 0 ? data[0] : undefined;
updateProperties({ 'flowable:assignee': user?.userId });
assignee.value = user ? user : { userName: '' };
formData.value.assignee = String(user?.userId);
let extensionElements = getExtensionElements();
extensionElements.values = extensionElements.get('values').filter((item) => item.$type !== 'flowable:extAssignee');
if (user) {
const extAssigneeElement = createModdleElement('flowable:extAssignee', { body: '' }, extensionElements);
extensionElements.get('values').push(extAssigneeElement);
extAssigneeElement.body = JSON.stringify({ userName: user.userName, userId: user.userId });
}
if (extensionElements.values.length === 0) {
extensionElements = undefined;
}
updateProperties({ extensionElements: extensionElements });
};
const userSelectCallBack = (data: UserVO[]) => {
let extensionElements = getExtensionElements();
extensionElements.values = extensionElements.values.filter((item) => item.$type !== 'flowable:extCandidateUsers');
if (data.length === 0) {
formData.value.candidateUsers = undefined;
updateProperties({ 'flowable:candidateUsers': undefined });
} else {
const userIds = data.map((item) => item.userId).join(',');
formData.value.candidateUsers = userIds;
updateProperties({ 'flowable:candidateUsers': userIds });
const extCandidateUsersElement = createModdleElement('flowable:extCandidateUsers', { body: '' }, extensionElements);
extensionElements.values.push(extCandidateUsersElement);
const users = data.map((item) => {
return {
userId: item.userId,
userName: item.userName
};
});
extCandidateUsersElement.body = JSON.stringify(users);
}
if (extensionElements.values.length === 0) {
extensionElements = undefined;
}
updateProperties({ extensionElements: extensionElements });
};
const roleSelectCallBack = (data: RoleVO[]) => {
if (data.length === 0) {
formData.value.candidateGroups = '';
updateProperties({ 'flowable:candidateGroups': undefined });
} else {
const roleIds = data.map((item) => item.roleId).join(',');
formData.value.candidateGroups = roleIds;
updateProperties({ 'flowable:candidateGroups': roleIds });
}
};
const dueDateCallBack = (data: string) => {
updateProperties({ 'flowable:dueDate': data });
};
const taskTabClick = (e) => {
formData.value.candidateGroups = '';
formData.value.candidateUsers = '';
formData.value.assignee = '';
// formData.value.fixedAssignee = '';
assignee.value = {};
};
const syncChange = (newVal) => {
updateProperties({ 'flowable:async': newVal });
};
const skipExpressionChange = (newVal) => {
updateProperties({ 'flowable:skipExpression': newVal && newVal.length > 0 ? newVal : undefined });
};
const priorityChange = (newVal) => {
updateProperties({ 'flowable:priority': newVal });
};
const fixedAssigneeChange = (newVal) => {
updateProperties({ 'flowable:assignee': newVal && newVal.length > 0 ? newVal : undefined });
};
const multiInstanceTypeChange = (newVal) => {
if (newVal !== MultiInstanceTypeEnum.NONE) {
let loopCharacteristics = props.element.businessObject.get('loopCharacteristics');
if (!loopCharacteristics) {
loopCharacteristics = createModdleElement('bpmn:MultiInstanceLoopCharacteristics', {}, props.element.businessObject);
}
loopCharacteristics.isSequential = newVal === MultiInstanceTypeEnum.SERIAL;
updateProperties({ loopCharacteristics: loopCharacteristics });
} else {
updateProperties({ loopCharacteristics: undefined });
}
};
const collectionChange = (newVal) => {
let loopCharacteristics = props.element.businessObject.get('loopCharacteristics');
if (!loopCharacteristics) {
loopCharacteristics = createModdleElement('bpmn:MultiInstanceLoopCharacteristics', {}, props.element.businessObject);
}
loopCharacteristics.collection = newVal && newVal.length > 0 ? newVal : undefined;
updateProperties({ loopCharacteristics: loopCharacteristics });
};
const elementVariableChange = (newVal) => {
let loopCharacteristics = props.element.businessObject.get('loopCharacteristics');
if (!loopCharacteristics) {
loopCharacteristics = createModdleElement('bpmn:MultiInstanceLoopCharacteristics', {}, props.element.businessObject);
}
loopCharacteristics.elementVariable = newVal && newVal.length > 0 ? newVal : undefined;
updateProperties({ loopCharacteristics: loopCharacteristics });
};
const completionConditionChange = (newVal) => {
let loopCharacteristics = props.element.businessObject.get<ModdleElement>('loopCharacteristics');
if (!loopCharacteristics) {
loopCharacteristics = createModdleElement('bpmn:MultiInstanceLoopCharacteristics', {}, props.element.businessObject);
}
if (newVal && newVal.length > 0) {
if (!loopCharacteristics.completionCondition) {
loopCharacteristics.completionCondition = createModdleElement('bpmn:Expression', { body: newVal }, loopCharacteristics);
} else {
loopCharacteristics.completionCondition.body = newVal;
}
} else {
loopCharacteristics.completionCondition = undefined;
}
updateProperties({ loopCharacteristics: loopCharacteristics });
};
const dueDateChange = (newVal) => {
updateProperties({ 'flowable:dueDate': newVal && newVal.length > 0 ? newVal : undefined });
};
const selectUserLength = computed(() => {
if (formData.value.candidateUsers) {
return formData.value.candidateUsers.split(',').length;
} else {
return 0;
}
});
const selectRoleLength = computed(() => {
if (formData.value.candidateGroups) {
return formData.value.candidateGroups.split(',').length;
} else {
return 0;
}
});
onBeforeMount(() => {
const extensionElements = getExtensionElements(false);
if (extensionElements && extensionElements.get('values')) {
let extAssigneeElement = extensionElements.get('values').find((item) => item.$type === 'flowable:extAssignee');
if (extAssigneeElement) {
assignee.value = JSON.parse(extAssigneeElement.body);
}
}
if (formData.value.loopCharacteristics) {
const loopCharacteristics = formData.value.loopCharacteristics;
formData.value.collection = loopCharacteristics.collection || '';
formData.value.elementVariable = loopCharacteristics.elementVariable || '';
formData.value.completionCondition = loopCharacteristics.completionCondition?.body || '';
formData.value.multiInstanceType = loopCharacteristics.isSequential ? MultiInstanceTypeEnum.SERIAL : MultiInstanceTypeEnum.PARALLEL;
}
if (formData.value.assignee) {
formData.value.fixedAssignee = formData.value.assignee;
}
});
const formRules = ref<ElFormRules>({
id: [{ required: true, message: '请输入', trigger: 'blur' }],
name: [{ required: true, message: '请输入', trigger: 'blur' }]
});
const AllocationTypeSelect = [
{ id: 'b9cdf970-dd91-47c0-819f-42a7010ca2a6', label: '指定人员', value: AllocationTypeEnum.USER },
{ id: '3f7ccbcd-c464-4602-bb9d-e96649d10585', label: '候选人员', value: AllocationTypeEnum.CANDIDATE },
{ id: 'c49065e0-7f2d-4c09-aedb-ab2d47d9a454', label: '发起人自己', value: AllocationTypeEnum.YOURSELF },
{ id: '6ef40a03-7e9a-4898-89b2-c88fe9064542', label: '发起人指定', value: AllocationTypeEnum.SPECIFY }
];
const SpecifyDesc = [
{ id: 'fa253b34-4335-458c-b1bc-b039e2a2b7a6', label: '指定一个人', value: 'specifySingle' },
{ id: '7365ff54-2e05-4312-9bfb-0b8edd779c5b', label: '指定多个人', value: 'specifyMultiple' }
];
const listFormManage = async () => {
formManageListLoading.value = true;
const res = await selectListFormManage();
formManageList.value = res.data;
formManageListLoading.value = false;
};
onMounted(() => {
nextTick(() => {
listFormManage();
});
});
</script>
<style lang="scss" scoped></style>

View File

@ -1,110 +0,0 @@
<template>
<div ref="propertyPanel">
<div v-if="nodeName" class="node-name">{{ nodeName }}</div>
<component :is="component" v-if="element" :element="element" />
</div>
</template>
<script setup lang="ts" name="PropertyPanel">
import { NodeName } from '../assets/lang/zh';
import TaskPanel from './TaskPanel.vue';
import ProcessPanel from './ProcessPanel.vue';
import StartEndPanel from './StartEndPanel.vue';
import GatewayPanel from './GatewayPanel.vue';
import SequenceFlowPanel from './SequenceFlowPanel.vue';
import ParticipantPanel from './ParticipantPanel.vue';
import SubProcessPanel from './SubProcessPanel.vue';
import type { Modeler, ModdleElement } from 'bpmn';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
interface propsType {
modeler: Modeler;
}
const props = withDefaults(defineProps<propsType>(), {});
const element = ref<ModdleElement>();
const processElement = ref<ModdleElement>();
const startEndType = ['bpmn:IntermediateThrowEvent', 'bpmn:StartEvent', 'bpmn:EndEvent'];
const taskType = [
'bpmn:UserTask',
'bpmn:Task',
'bpmn:SendTask',
'bpmn:ReceiveTask',
'bpmn:ManualTask',
'bpmn:BusinessRuleTask',
'bpmn:ServiceTask',
'bpmn:ScriptTask'
];
const sequenceType = ['bpmn:SequenceFlow'];
const gatewayType = ['bpmn:InclusiveGateway', 'bpmn:ExclusiveGateway', 'bpmn:ParallelGateway', 'bpmn:EventBasedGateway', 'bpmn:ComplexGateway'];
const processType = ['bpmn:Process'];
//
const component = computed(() => {
if (!element.value) return null;
const type = element.value.type;
if (startEndType.includes(type)) return StartEndPanel;
if (taskType.includes(type)) return TaskPanel;
if (sequenceType.includes(type)) return SequenceFlowPanel;
if (gatewayType.includes(type)) return GatewayPanel;
if (processType.includes(type)) return ProcessPanel;
if (type === 'bpmn:Participant') return ParticipantPanel;
if (type === 'bpmn:SubProcess') return SubProcessPanel;
//return proxy?.$modal.msgWarning('....');
return undefined;
});
const nodeName = computed(() => {
if (element.value) {
const bizObj = element.value.businessObject;
const type = bizObj?.eventDefinitions && bizObj?.eventDefinitions.length > 0 ? bizObj.eventDefinitions[0].$type : bizObj.$type;
return NodeName[type] || type;
}
return '';
});
const handleModeler = () => {
props.modeler.on('root.added', (e: any) => {
element.value = null;
if (e.element.type === 'bpmn:Process') {
nextTick(() => {
element.value = e.element;
processElement.value = e.element;
});
}
});
props.modeler.on('element.click', (e: any) => {
if (e.element.type === 'bpmn:Process') {
nextTick(() => {
element.value = e.element;
processElement.value = e.element;
});
}
});
props.modeler.on('selection.changed', (e: any) => {
// nullvue
element.value = null;
const newElement = e.newSelection[0];
if (newElement) {
nextTick(() => {
element.value = newElement;
});
} else {
nextTick(() => {
element.value = processElement.value;
});
}
});
};
onMounted(() => {
handleModeler();
});
</script>
<style scoped lang="scss">
.node-name {
font-size: 16px;
font-weight: bold;
padding: 10px;
}
</style>

View File

@ -1,252 +0,0 @@
<template>
<div>
<el-dialog v-model="visible" :title="title" width="600px" append-to-body>
<el-form label-width="100px">
<el-form-item label="小时">
<el-radio-group v-model="hourValue" @change="hourChange">
<el-radio-button label="4" value="4" />
<el-radio-button label="8" value="8" />
<el-radio-button label="12" value="12" />
<el-radio-button label="24" value="24" />
<el-radio-button label="自定义" value="自定义" />
<el-input-number v-show="hourValue === '自定义'" v-model="customHourValue" :min="1" @change="customHourValueChange"></el-input-number>
</el-radio-group>
</el-form-item>
<el-form-item label="天">
<el-radio-group v-model="dayValue" @change="dayChange">
<el-radio-button label="1" value="1" />
<el-radio-button label="2" value="2" />
<el-radio-button label="3" value="3" />
<el-radio-button label="4" value="4" />
<el-radio-button label="自定义" value="自定义" />
<el-input-number v-show="dayValue === '自定义'" v-model="customDayValue" :min="1" @change="customDayValueChange"></el-input-number>
</el-radio-group>
</el-form-item>
<el-form-item label="周">
<el-radio-group v-model="weekValue" @change="weekChange">
<el-radio-button label="1" value="1" />
<el-radio-button label="2" value="2" />
<el-radio-button label="3" value="3" />
<el-radio-button label="4" value="4" />
<el-radio-button label="自定义" value="自定义" />
<el-input-number v-show="weekValue === '自定义'" v-model="customWeekValue" :min="1" @change="customWeekValueChange"></el-input-number>
</el-radio-group>
</el-form-item>
<el-form-item label="月">
<el-radio-group v-model="monthValue" @change="monthChange">
<el-radio-button label="1" value="1" />
<el-radio-button label="2" value="2" />
<el-radio-button label="3" value="3" />
<el-radio-button label="4" value="4" />
<el-radio-button label="自定义" value="自定义" />
<el-input-number v-show="monthValue === '自定义'" v-model="customMonthValue" :min="1" @change="customMonthValueChange"></el-input-number>
</el-radio-group>
</el-form-item>
</el-form>
<template #footer>
<div>
<el-button @click="closeDialog">取消</el-button>
<el-button type="primary" @click="confirm">确定</el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup lang="ts">
import useDialog from '@/hooks/useDialog';
interface PropType {
modelValue?: string;
data?: string;
}
const prop = withDefaults(defineProps<PropType>(), {
modelValue: '',
data: ''
});
const emit = defineEmits(['update:modelValue', 'confirmCallBack']);
const { title, visible, openDialog, closeDialog } = useDialog({
title: '设置任务到期时间'
});
const formValue = ref();
const valueType = ref();
const hourValue = ref('');
const dayValue = ref('');
const weekValue = ref('');
const monthValue = ref('');
const customHourValue = ref(1);
const customDayValue = ref(1);
const customWeekValue = ref(1);
const customMonthValue = ref(1);
const hourValueConst = ['4', '8', '12', '24'];
const dayAndWeekAndMonthValueConst = ['1', '2', '3', '4'];
const initValue = () => {
formValue.value = prop.data;
if (prop.data) {
const lastStr = prop.data.substring(prop.data.length - 1);
if (lastStr === 'H') {
const hourValueValue = prop.data.substring(2, prop.data.length - 1);
if (hourValueConst.includes(hourValueValue)) {
hourValue.value = hourValueValue;
} else {
hourValue.value = '自定义';
customHourValue.value = Number(hourValueValue);
}
}
const dayAndWeekAndMonthValue = prop.data.substring(1, prop.data.length - 1);
if (lastStr === 'D') {
if (dayAndWeekAndMonthValueConst.includes(dayAndWeekAndMonthValue)) {
dayValue.value = dayAndWeekAndMonthValue;
} else {
dayValue.value = '自定义';
customDayValue.value = Number(dayAndWeekAndMonthValue);
}
}
if (lastStr === 'W') {
if (dayAndWeekAndMonthValueConst.includes(dayAndWeekAndMonthValue)) {
weekValue.value = dayAndWeekAndMonthValue;
} else {
weekValue.value = '自定义';
customWeekValue.value = Number(dayAndWeekAndMonthValue);
}
}
if (lastStr === 'M') {
if (dayAndWeekAndMonthValueConst.includes(dayAndWeekAndMonthValue)) {
monthValue.value = dayAndWeekAndMonthValue;
} else {
monthValue.value = '自定义';
customMonthValue.value = Number(dayAndWeekAndMonthValue);
}
}
}
};
const confirm = () => {
emit('update:modelValue', formValue.value);
emit('confirmCallBack', formValue.value);
closeDialog();
};
const customHourValueChange = (customHourValue) => {
formValue.value = `PT${customHourValue}H`;
dayValue.value = '';
weekValue.value = '';
monthValue.value = '';
customDayValue.value = 1;
customWeekValue.value = 1;
customMonthValue.value = 1;
};
const customDayValueChange = (customDayValue) => {
formValue.value = `P${customDayValue}D`;
hourValue.value = '';
weekValue.value = '';
monthValue.value = '';
customHourValue.value = 1;
customWeekValue.value = 1;
customMonthValue.value = 1;
};
const customWeekValueChange = (customWeekValue) => {
formValue.value = `P${customWeekValue}W`;
hourValue.value = '';
dayValue.value = '';
monthValue.value = '';
customHourValue.value = 1;
customDayValue.value = 1;
customMonthValue.value = 1;
};
const customMonthValueChange = (customMonthValue) => {
formValue.value = `P${customMonthValue}M`;
hourValue.value = '';
dayValue.value = '';
weekValue.value = '';
customHourValue.value = 1;
customDayValue.value = 1;
customWeekValue.value = 1;
};
const hourChange = (hourValue) => {
if (hourValue === '自定义') {
formValue.value = `PT${customHourValue.value}H`;
} else {
formValue.value = `PT${hourValue}H`;
}
dayValue.value = '';
weekValue.value = '';
monthValue.value = '';
customDayValue.value = 1;
customWeekValue.value = 1;
customMonthValue.value = 1;
};
const dayChange = (dayValue) => {
if (dayValue === '自定义') {
formValue.value = `P${customDayValue.value}D`;
} else {
formValue.value = `P${dayValue}D`;
}
hourValue.value = '';
weekValue.value = '';
monthValue.value = '';
customHourValue.value = 1;
customWeekValue.value = 1;
customMonthValue.value = 1;
};
const weekChange = (weekValue) => {
if (weekValue === '自定义') {
formValue.value = `P${customWeekValue.value}W`;
} else {
formValue.value = `P${weekValue}W`;
}
hourValue.value = '';
dayValue.value = '';
monthValue.value = '';
customHourValue.value = 1;
customDayValue.value = 1;
customMonthValue.value = 1;
};
const monthChange = (monthValue) => {
if (monthValue === '自定义') {
formValue.value = `P${customMonthValue.value}M`;
} else {
formValue.value = `P${monthValue}M`;
}
hourValue.value = '';
dayValue.value = '';
weekValue.value = '';
customHourValue.value = 1;
customDayValue.value = 1;
customWeekValue.value = 1;
};
watch(
() => visible.value,
() => {
if (visible.value) {
initValue();
}
}
);
defineExpose({
openDialog,
closeDialog
});
</script>

View File

@ -1,308 +0,0 @@
<template>
<div>
<vxe-toolbar>
<template #buttons>
<el-button type="primary" link size="small" @click="insertEvent">新增</el-button>
<el-button type="primary" link size="small" @click="removeSelectRowEvent">删除</el-button>
</template>
</vxe-toolbar>
<vxe-table
ref="tableRef"
size="mini"
height="100px"
border
show-overflow
keep-source
:data="tableData"
:menu-config="menuConfig"
@cell-dblclick="cellDBLClickEvent"
@menu-click="contextMenuClickEvent"
>
<vxe-column type="checkbox" width="40"></vxe-column>
<vxe-column type="seq" width="40"></vxe-column>
<vxe-column field="event" title="事件" min-width="100px">
<template #default="slotParams">
<span>{{ eventSelect.find((e) => e.value === slotParams.row.event)?.label }}</span>
</template>
</vxe-column>
<vxe-column field="type" title="类型" min-width="100px">
<template #default="slotParams">
<span>{{ typeSelect.find((e) => e.value === slotParams.row.type)?.label }}</span>
</template>
</vxe-column>
<vxe-column field="className" title="Java 类名" min-width="100px"> </vxe-column>
</vxe-table>
<el-dialog
v-model="formDialog.visible.value"
:title="formDialog.title.value"
width="600px"
:close-on-click-modal="false"
:close-on-press-escape="false"
:show-close="false"
append-to-body
>
<el-form ref="formRef" :model="formData" :rules="tableRules" label-width="100px">
<el-form-item label="事件" prop="event">
<el-select v-model="formData.event">
<el-option v-for="item in eventSelect" :key="item.id" :value="item.value" :label="item.label"></el-option>
</el-select>
</el-form-item>
<el-form-item label="类型" prop="type">
<template #label>
<span>
类型
<el-tooltip placement="top">
<el-icon><QuestionFilled /></el-icon>
<template #content>
示例 com.company.MyCustomListener自定义类必须实现 org.flowable.engine.delegate.TaskListener 接口<br />
表达式示例 ${myObject.callMethod(task, task.eventName)}<br />
委托表达式示例 ${myListenerSpringBean} springBean 需要实现 org.flowable.engine.delegate.TaskListener 接口
</template>
</el-tooltip>
</span>
</template>
<el-select v-model="formData.type">
<el-option v-for="item in typeSelect" :key="item.id" :value="item.value" :label="item.label"></el-option>
</el-select>
</el-form-item>
<el-form-item
:label="typeSelect.filter((e) => e.value === formData.type)[0] ? typeSelect.filter((e) => e.value === formData.type)[0]?.label : '表达式'"
prop="className"
>
<el-input v-model="formData.className" type="text"></el-input>
</el-form-item>
</el-form>
<el-tabs type="border-card">
<el-tab-pane label="参数">
<ListenerParam ref="listenerParamRef" :table-data="formData.params" />
</el-tab-pane>
</el-tabs>
<template #footer>
<div class="dialog-footer">
<el-button @click="formDialog.closeDialog"> </el-button>
<el-button type="primary" @click="submitEvent"> </el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup lang="ts">
import ListenerParam from './ListenerParam.vue';
import { VxeTableEvents, VxeTableInstance, VxeTablePropTypes } from 'vxe-table';
import type { ExecutionListenerVO } from 'bpmnDesign';
import type { Moddle, Modeler, ModdleElement } from 'bpmn';
import usePanel from '../../hooks/usePanel';
import useDialog from '@/hooks/useDialog';
import useModelerStore from '@/store/modules/modeler';
const emit = defineEmits(['close']);
interface PropType {
element: ModdleElement;
}
const props = withDefaults(defineProps<PropType>(), {});
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const selectRow = ref<ExecutionListenerVO | null>();
const formDialog = useDialog({
title: selectRow.value ? '编辑&保存' : '新增&保存'
});
const { showConfig, elementType, updateProperties } = usePanel({
element: toRaw(props.element)
});
const { getModdle } = useModelerStore();
const moddle = getModdle();
const listenerParamRef = ref<InstanceType<typeof ListenerParam>>();
const tableRef = ref<VxeTableInstance<ExecutionListenerVO>>();
const formRef = ref<ElFormInstance>();
const initData: ExecutionListenerVO = {
event: '',
type: '',
className: '',
params: []
};
const formData = ref<ExecutionListenerVO>({ ...initData });
const tableData = ref<ExecutionListenerVO[]>([]);
const tableRules = ref<ElFormRules>({
event: [{ required: true, message: '请选择', trigger: 'blur' }],
type: [{ required: true, message: '请选择', trigger: 'blur' }],
className: [{ required: true, message: '请输入', trigger: 'blur' }]
});
const submitEvent = async () => {
const error = await listenerParamRef.value.validate();
await formRef.value.validate((validate) => {
if (validate && !error) {
const $table = tableRef.value;
if ($table) {
formData.value.params = listenerParamRef.value.getTableData();
if (selectRow.value) {
Object.assign(selectRow.value, formData.value);
} else {
$table.insertAt({ ...formData.value }, -1);
}
updateElement();
formDialog.closeDialog();
}
}
});
};
const removeSelectRowEvent = async () => {
const $table = tableRef.value;
if ($table) {
const selectCount = $table.getCheckboxRecords().length;
if (selectCount === 0) {
proxy?.$modal.msgWarning('请选择行');
} else {
await $table.removeCheckboxRow();
updateElement();
}
}
};
const insertEvent = async () => {
Object.assign(formData.value, initData);
selectRow.value = null;
formDialog.openDialog();
};
const editEvent = (row: ExecutionListenerVO) => {
Object.assign(formData.value, row);
selectRow.value = row;
formDialog.openDialog();
};
const removeEvent = async (row: ExecutionListenerVO) => {
await proxy?.$modal.confirm('您确定要删除该数据?');
const $table = tableRef.value;
if ($table) {
await $table.remove(row);
updateElement();
}
};
const updateElement = () => {
const $table = tableRef.value;
const data = $table.getTableData().fullData;
if (data.length) {
let extensionElements = props.element.businessObject.get('extensionElements');
if (!extensionElements) {
extensionElements = moddle.create('bpmn:ExtensionElements');
}
//
extensionElements.values = extensionElements.values?.filter((item) => item.$type !== 'flowable:ExecutionListener') ?? [];
data.forEach((item) => {
const executionListener = moddle.create('flowable:ExecutionListener');
executionListener['event'] = item.event;
executionListener[item.type] = item.className;
if (item.params && item.params.length) {
item.params.forEach((field) => {
const fieldElement = moddle.create('flowable:Field');
fieldElement['name'] = field.name;
fieldElement[field.type] = field.value;
executionListener.get('fields').push(fieldElement);
});
}
extensionElements.get('values').push(executionListener);
});
updateProperties({ extensionElements: extensionElements });
} else {
const extensionElements = props.element.businessObject[`extensionElements`];
if (extensionElements) {
extensionElements.values = extensionElements.values?.filter((item) => item.$type !== 'flowable:ExecutionListener') ?? [];
}
}
};
const cellDBLClickEvent: VxeTableEvents.CellDblclick<ExecutionListenerVO> = ({ row }) => {
editEvent(row);
};
const menuConfig = reactive<VxeTablePropTypes.MenuConfig<ExecutionListenerVO>>({
body: {
options: [
[
{ code: 'edit', name: '编辑', prefixIcon: 'vxe-icon-edit', disabled: false },
{ code: 'remove', name: '删除', prefixIcon: 'vxe-icon-delete', disabled: false }
]
]
},
visibleMethod({ options, column }) {
const isDisabled = !column;
options.forEach((list) => {
list.forEach((item) => {
item.disabled = isDisabled;
});
});
return true;
}
});
const contextMenuClickEvent: VxeTableEvents.MenuClick<ExecutionListenerVO> = ({ menu, row, column }) => {
const $table = tableRef.value;
if ($table) {
switch (menu.code) {
case 'edit':
editEvent(row);
break;
case 'remove':
removeEvent(row);
break;
}
}
};
const initTableData = () => {
tableData.value =
props.element.businessObject.extensionElements?.values
.filter((item) => item.$type === 'flowable:ExecutionListener')
.map((item) => {
let type;
if ('class' in item) type = 'class';
if ('expression' in item) type = 'expression';
if ('delegateExpression' in item) type = 'delegateExpression';
return {
event: item.event,
type: type,
className: item[type],
params:
item.fields?.map((field) => {
let fieldType;
if ('stringValue' in field) fieldType = 'stringValue';
if ('expression' in field) fieldType = 'expression';
return {
name: field.name,
type: fieldType,
value: field[fieldType]
};
}) ?? []
};
}) ?? [];
};
onMounted(() => {
initTableData();
});
const typeSelect = [
{ id: '742fdeb7-23b4-416b-ac66-cd4ec8b901b7', label: '类', value: 'class' },
{ id: '660c9c46-8fae-4bae-91a0-0335420019dc', label: '表达式', value: 'expression' },
{ id: '4b8135ab-6bc3-4a0f-80be-22f58bc6c5fd', label: '委托表达式', value: 'delegateExpression' }
];
const eventSelect = [
{ id: 'e6e0a51a-2d5d-4dc4-b847-b5c14f43a6ab', label: '开始', value: 'start' },
{ id: '6da97c1e-15fc-4445-8943-75d09f49778e', label: '结束', value: 'end' },
{ id: '6a2cbcec-e026-4f11-bef7-fff0b5c871e2', label: '启用', value: 'take' }
];
</script>
<style scoped lang="scss">
.el-badge {
:deep(.el-badge__content) {
top: 10px;
}
}
</style>

View File

@ -1,121 +0,0 @@
<template>
<vxe-toolbar>
<template #buttons>
<el-button icon="Plus" @click="insertRow">新增</el-button>
</template>
</vxe-toolbar>
<vxe-table
ref="tableRef"
:height="height"
border
show-overflow
keep-source
:data="tableData"
:edit-rules="tableRules"
:edit-config="{ trigger: 'click', mode: 'row', showStatus: true }"
>
<vxe-column type="seq" width="40"></vxe-column>
<vxe-column field="type" title="类型" :edit-render="{}">
<template #default="slotParams">
<span>{{ typeSelect.find((e) => e.value === slotParams.row.type)?.label }}</span>
</template>
<template #edit="slotParams">
<vxe-select v-model="slotParams.row.type">
<vxe-option v-for="item in typeSelect" :key="item.id" :value="item.value" :label="item.label"></vxe-option>
</vxe-select>
</template>
</vxe-column>
<vxe-column field="name" title="名称" :edit-render="{}">
<template #edit="slotParams">
<vxe-input v-model="slotParams.row.name" type="text"></vxe-input>
</template>
</vxe-column>
<vxe-column field="value" title="值" :edit-render="{}">
<template #edit="slotParams">
<vxe-input v-model="slotParams.row.value" type="text"></vxe-input>
</template>
</vxe-column>
<vxe-column title="操作" width="100" show-overflow align="center">
<template #default="slotParams">
<el-tooltip content="删除" placement="top">
<el-button link type="danger" icon="Delete" @click="removeRow(slotParams.row)"></el-button>
</el-tooltip>
</template>
</vxe-column>
</vxe-table>
</template>
<script setup lang="ts">
import { VXETable, VxeTableInstance, VxeTablePropTypes } from 'vxe-table';
import type { ParamVO } from 'bpmnDesign';
import useDialog from '@/hooks/useDialog';
interface PropType {
height?: string;
tableData?: ParamVO[];
}
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const props = withDefaults(defineProps<PropType>(), {
height: '200px',
tableData: () => []
});
const tableRules = ref<VxeTablePropTypes.EditRules>({
type: [{ required: true, message: '请选择', trigger: 'blur' }],
name: [{ required: true, message: '请输入', trigger: 'blur' }],
value: [{ required: true, message: '请输入', trigger: 'blur' }]
});
const { title, visible, openDialog, closeDialog } = useDialog({
title: '监听器参数'
});
const typeSelect = [
{ id: '742fdeb7-23b4-416b-ac66-cd4ec8b901b7', label: '字符串', value: 'stringValue' },
{ id: '660c9c46-8fae-4bae-91a0-0335420019dc', label: '表达式', value: 'expression' }
];
const tableRef = ref<VxeTableInstance<ParamVO>>();
const getTableData = () => {
const $table = tableRef.value;
if ($table) {
return $table.getTableData().fullData;
}
return [];
};
const insertRow = async () => {
const $table = tableRef.value;
if ($table) {
const { row: newRow } = await $table.insertAt({}, -1);
//
await $table.validate(newRow);
}
};
const removeRow = async (row: ParamVO) => {
await proxy?.$modal.confirm('您确定要删除该数据?');
const $table = tableRef.value;
if ($table) {
await $table.remove(row);
}
};
const validate = async () => {
const $table = tableRef.value;
if ($table) {
return await $table.validate(true);
}
};
defineExpose({
closeDialog,
openDialog,
validate,
getTableData
});
</script>
<style scoped lang="scss"></style>

View File

@ -1,310 +0,0 @@
<template>
<div>
<vxe-toolbar>
<template #buttons>
<el-button type="primary" link size="small" @click="insertEvent">新增</el-button>
<el-button type="primary" link size="small" @click="removeSelectRowEvent">删除</el-button>
</template>
</vxe-toolbar>
<vxe-table
ref="tableRef"
size="mini"
height="100px"
border
show-overflow
keep-source
:data="tableData"
:menu-config="menuConfig"
@cell-dblclick="cellDBLClickEvent"
@menu-click="contextMenuClickEvent"
>
<vxe-column type="checkbox" width="40"></vxe-column>
<vxe-column type="seq" width="40"></vxe-column>
<vxe-column field="event" title="事件" min-width="100px">
<template #default="slotParams">
<span>{{ eventSelect.find((e) => e.value === slotParams.row.event)?.label }}</span>
</template>
</vxe-column>
<vxe-column field="type" title="类型" min-width="100px">
<template #default="slotParams">
<span>{{ typeSelect.find((e) => e.value === slotParams.row.type)?.label }}</span>
</template>
</vxe-column>
<vxe-column field="className" title="Java 类名" min-width="100px"> </vxe-column>
</vxe-table>
<el-dialog
v-model="formDialog.visible.value"
:title="formDialog.title.value"
width="600px"
:close-on-click-modal="false"
:close-on-press-escape="false"
:show-close="false"
append-to-body
>
<el-form ref="formRef" :model="formData" :rules="tableRules" label-width="100px">
<el-form-item label="事件" prop="event">
<template #label>
<span>
事件
<el-tooltip placement="top">
<el-icon><QuestionFilled /></el-icon>
<template #content>
create创建当任务已经创建并且所有任务参数都已经设置时触发<br />
assignment指派当任务已经指派给某人时触发请注意当流程执行到达用户任务时在触发create事件之前会首先触发assignment事件<br />
complete完成当任务已经完成从运行时数据中删除前触发<br />
delete删除在任务即将被删除前触发请注意任务由completeTask正常完成时也会触发
</template>
</el-tooltip>
</span>
</template>
<el-select v-model="formData.event">
<el-option v-for="item in eventSelect" :key="item.id" :value="item.value" :label="item.label"></el-option>
</el-select>
</el-form-item>
<el-form-item label="类型" prop="type">
<el-select v-model="formData.type">
<el-option v-for="item in typeSelect" :key="item.id" :value="item.value" :label="item.label"></el-option>
</el-select>
</el-form-item>
<el-form-item
:label="typeSelect.filter((e) => e.value === formData.type)[0] ? typeSelect.filter((e) => e.value === formData.type)[0]?.label : '表达式'"
prop="className"
>
<el-input v-model="formData.className" type="text"></el-input>
</el-form-item>
</el-form>
<el-tabs type="border-card">
<el-tab-pane label="参数">
<ListenerParam ref="listenerParamRef" :table-data="formData.params" />
</el-tab-pane>
</el-tabs>
<template #footer>
<div class="dialog-footer">
<el-button @click="formDialog.closeDialog"> </el-button>
<el-button type="primary" @click="submitEvent"> </el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup lang="ts">
import ListenerParam from './ListenerParam.vue';
import { VxeTableEvents, VxeTableInstance, VxeTablePropTypes } from 'vxe-table';
import type { TaskListenerVO } from 'bpmnDesign';
import type { ModdleElement } from 'bpmn';
import usePanel from '../../hooks/usePanel';
import useDialog from '@/hooks/useDialog';
import useModelerStore from '@/store/modules/modeler';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
interface PropType {
element: ModdleElement;
}
const props = withDefaults(defineProps<PropType>(), {});
const selectRow = ref<TaskListenerVO | null>();
const formDialog = useDialog({
title: selectRow.value ? '编辑&保存' : '新增&保存'
});
const { showConfig, elementType, updateProperties } = usePanel({
element: toRaw(props.element)
});
const { getModdle } = useModelerStore();
const moddle = getModdle();
const listenerParamRef = ref<InstanceType<typeof ListenerParam>>();
const tableRef = ref<VxeTableInstance<TaskListenerVO>>();
const formRef = ref<ElFormInstance>();
const initData: TaskListenerVO = {
event: '',
type: '',
className: '',
name: '',
params: []
};
const formData = ref<TaskListenerVO>({ ...initData });
const currentIndex = ref(0);
const tableData = ref<TaskListenerVO[]>([]);
const tableRules = ref<VxeTablePropTypes.EditRules>({
event: [{ required: true, message: '请选择', trigger: 'blur' }],
type: [{ required: true, message: '请选择', trigger: 'blur' }],
name: [{ required: true, message: '请输入', trigger: 'blur' }],
className: [{ required: true, message: '请输入', trigger: 'blur' }]
});
const submitEvent = async () => {
const error = await listenerParamRef.value.validate();
await formRef.value.validate((validate) => {
if (validate && !error) {
const $table = tableRef.value;
if ($table) {
formData.value.params = listenerParamRef.value.getTableData();
if (selectRow.value) {
Object.assign(selectRow.value, formData.value);
} else {
$table.insertAt({ ...formData.value }, -1);
}
updateElement();
formDialog.closeDialog();
}
}
});
};
const insertEvent = async () => {
Object.assign(formData.value, initData);
selectRow.value = null;
formDialog.openDialog();
};
const editEvent = (row: TaskListenerVO) => {
Object.assign(formData.value, row);
selectRow.value = row;
formDialog.openDialog();
};
const removeEvent = async (row: TaskListenerVO) => {
await proxy?.$modal.confirm('您确定要删除该数据?');
const $table = tableRef.value;
if ($table) {
await $table.remove(row);
updateElement();
}
};
const removeSelectRowEvent = async () => {
const $table = tableRef.value;
if ($table) {
const selectCount = $table.getCheckboxRecords().length;
if (selectCount === 0) {
proxy?.$modal.msgWarning('请选择行');
} else {
await $table.removeCheckboxRow();
updateElement();
}
}
};
const updateElement = () => {
const $table = tableRef.value;
const data = $table.getTableData().fullData;
if (data.length) {
let extensionElements = props.element.businessObject.get('extensionElements');
if (!extensionElements) {
extensionElements = moddle.create('bpmn:ExtensionElements');
}
//
extensionElements.values = extensionElements.values?.filter((item) => item.$type !== 'flowable:TaskListener') ?? [];
data.forEach((item) => {
const taskListener = moddle.create('flowable:TaskListener');
taskListener['event'] = item.event;
taskListener[item.type] = item.className;
if (item.params && item.params.length) {
item.params.forEach((field) => {
const fieldElement = moddle.create('flowable:Field');
fieldElement['name'] = field.name;
fieldElement[field.type] = field.value;
taskListener.get('fields').push(fieldElement);
});
}
extensionElements.get('values').push(taskListener);
});
updateProperties({ extensionElements: extensionElements });
} else {
const extensionElements = props.element.businessObject[`extensionElements`];
if (extensionElements) {
extensionElements.values = extensionElements.values?.filter((item) => item.$type !== 'flowable:TaskListener') ?? [];
}
}
};
const cellDBLClickEvent: VxeTableEvents.CellDblclick<TaskListenerVO> = ({ row }) => {
editEvent(row);
};
const menuConfig = reactive<VxeTablePropTypes.MenuConfig<TaskListenerVO>>({
body: {
options: [
[
{ code: 'edit', name: '编辑', prefixIcon: 'vxe-icon-edit', disabled: false },
{ code: 'remove', name: '删除', prefixIcon: 'vxe-icon-delete', disabled: false }
]
]
},
visibleMethod({ options, column }) {
const isDisabled = !column;
options.forEach((list) => {
list.forEach((item) => {
item.disabled = isDisabled;
});
});
return true;
}
});
const contextMenuClickEvent: VxeTableEvents.MenuClick<TaskListenerVO> = ({ menu, row, column }) => {
const $table = tableRef.value;
if ($table) {
switch (menu.code) {
case 'edit':
editEvent(row);
break;
case 'remove':
removeEvent(row);
break;
}
}
};
const initTableData = () => {
tableData.value =
props.element.businessObject.extensionElements?.values
.filter((item) => item.$type === 'flowable:TaskListener')
.map((item) => {
let type;
if ('class' in item) type = 'class';
if ('expression' in item) type = 'expression';
if ('delegateExpression' in item) type = 'delegateExpression';
return {
event: item.event,
type: type,
className: item[type],
params:
item.fields?.map((field) => {
let fieldType;
if ('stringValue' in field) fieldType = 'stringValue';
if ('expression' in field) fieldType = 'expression';
return {
name: field.name,
type: fieldType,
value: field[fieldType]
};
}) ?? []
};
}) ?? [];
};
onMounted(() => {
initTableData();
});
const typeSelect = [
{ id: '742fdeb7-23b4-416b-ac66-cd4ec8b901b7', label: '类', value: 'class' },
{ id: '660c9c46-8fae-4bae-91a0-0335420019dc', label: '表达式', value: 'expression' },
{ id: '4b8135ab-6bc3-4a0f-80be-22f58bc6c5fd', label: '委托表达式', value: 'delegateExpression' }
];
const eventSelect = [
{ id: 'e6e0a51a-2d5d-4dc4-b847-b5c14f43a6ab', label: '创建', value: 'create' },
{ id: '6da97c1e-15fc-4445-8943-75d09f49778e', label: '指派', value: 'assignment' },
{ id: '6a2cbcec-e026-4f11-bef7-fff0b5c871e2', label: '完成', value: 'complete' },
{ id: '68801972-85f1-482f-bd86-1fad015c26ed', label: '删除', value: 'delete' }
];
</script>
<style scoped lang="scss">
.el-badge {
:deep(.el-badge__content) {
top: 10px;
}
}
</style>

View File

@ -1,71 +0,0 @@
<template>
<div class="design">
<el-dialog v-model="visible" width="100%" fullscreen :title="title">
<div class="modeler">
<bpmn-design ref="bpmnDesignRef" @save-call-back="saveCallBack"></bpmn-design>
</div>
</el-dialog>
</div>
</template>
<script lang="ts" setup name="Design">
import { getInfo, editModelXml } from '@/api/workflow/model';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
import { ModelForm } from '@/api/workflow/model/types';
import BpmnDesign from '@/bpmn/index.vue';
import useDialog from '@/hooks/useDialog';
const bpmnDesignRef = ref<InstanceType<typeof BpmnDesign>>();
const modelForm = ref<ModelForm>();
const emit = defineEmits(['closeCallBack']);
const { visible, title } = useDialog({
title: '编辑流程'
});
const modelId = ref('');
const open = async (id) => {
visible.value = true;
modelId.value = id;
const { data } = await getInfo(id);
modelForm.value = data;
bpmnDesignRef.value.initDiagram(modelForm.value.xml);
};
//
const saveCallBack = async (data) => {
await proxy?.$modal.confirm('是否确认保存?');
data.loading.value = true;
modelForm.value.id = modelId.value;
modelForm.value.xml = data.xml;
modelForm.value.svg = data.svg;
modelForm.value.key = data.key;
modelForm.value.name = data.name;
editModelXml(modelForm.value).then((res) => {
if (res.code === 200) {
visible.value = false;
proxy?.$modal.msgSuccess('保存成功');
emit('closeCallBack', data);
}
});
data.loading.value = false;
};
/**
* 对外暴露子组件方法
*/
defineExpose({
open
});
</script>
<style lang="scss" scoped>
.design {
:deep(.el-dialog .el-dialog__body) {
max-height: 100% !important;
min-height: calc(100vh - 80px);
padding: 10px 0 10px 0 !important;
}
:deep(.el-dialog__header) {
padding: 0 0 5px 0 !important;
}
}
</style>

View File

@ -1,411 +0,0 @@
<template>
<div v-loading="loading" class="bpmnDialogContainers">
<el-header style="border-bottom: 1px solid rgb(218 218 218); height: auto">
<div class="header-div">
<div>
<el-tooltip effect="dark" content="自适应屏幕" placement="bottom">
<el-button size="small" icon="Rank" @click="fitViewport" />
</el-tooltip>
<el-tooltip effect="dark" content="放大" placement="bottom">
<el-button size="small" icon="ZoomIn" @click="zoomViewport(true)" />
</el-tooltip>
<el-tooltip effect="dark" content="缩小" placement="bottom">
<el-button size="small" icon="ZoomOut" @click="zoomViewport(false)" />
</el-tooltip>
</div>
<div>
<div class="tips-label">
<div class="un-complete">未完成</div>
<div class="in-progress">进行中</div>
<div class="complete">已完成</div>
</div>
</div>
</div>
</el-header>
<div class="flow-containers">
<el-container class="bpmn-el-container" style="align-items: stretch">
<el-main style="padding: 0">
<div ref="canvas" class="canvas" />
</el-main>
</el-container>
</div>
</div>
</template>
<script lang="ts" setup>
import BpmnViewer from 'bpmn-js/lib/Viewer';
import MoveCanvasModule from 'diagram-js/lib/navigation/movecanvas';
import ZoomScrollModule from 'diagram-js/lib/navigation/zoomscroll';
import { ModuleDeclaration } from 'didi';
import type { Canvas, ModdleElement } from 'bpmn';
import EventBus from 'diagram-js/lib/core/EventBus';
import Overlays from 'diagram-js/lib/features/overlays/Overlays';
import processApi from '@/api/workflow/processInstance/index';
const canvas = ref<HTMLElement>();
const modeler = ref<BpmnViewer>();
const taskList = ref([]);
const zoom = ref(1);
const xml = ref('');
const loading = ref(false);
const bpmnVisible = ref(true);
const historyList = ref([]);
const init = (businessKey) => {
loading.value = true;
bpmnVisible.value = true;
nextTick(async () => {
if (modeler.value) modeler.value.destroy();
modeler.value = new BpmnViewer({
container: canvas.value,
additionalModules: [
{
//
zoomScroll: ['value', '']
},
ZoomScrollModule,
MoveCanvasModule
] as ModuleDeclaration[]
});
const resp = await processApi.getHistoryList(businessKey);
xml.value = resp.data.xml;
taskList.value = resp.data.taskList;
historyList.value = resp.data.historyList;
await createDiagram(xml.value);
loading.value = false;
});
};
const initXml = (xmlStr: string) => {
loading.value = true;
bpmnVisible.value = true;
nextTick(async () => {
if (modeler.value) modeler.value.destroy();
modeler.value = new BpmnViewer({
container: canvas.value,
additionalModules: [
{
//
zoomScroll: ['value', '']
},
ZoomScrollModule,
MoveCanvasModule
] as ModuleDeclaration[]
});
xml.value = xmlStr;
await createDiagram(xml.value);
loading.value = false;
});
};
const createDiagram = async (data) => {
try {
await modeler.value.importXML(data);
fitViewport();
fillColor();
loading.value = false;
addEventBusListener();
} catch (err) {
console.log(err);
}
};
const addEventBusListener = () => {
const eventBus = modeler.value.get<EventBus>('eventBus');
const overlays = modeler.value.get<Overlays>('overlays');
eventBus.on<ModdleElement>('element.hover', (e) => {
let data = historyList.value.find((t) => t.taskDefinitionKey === e.element.id);
if (e.element.type === 'bpmn:UserTask' && data) {
setTimeout(() => {
genNodeDetailBox(e, overlays, data);
}, 10);
}
});
eventBus.on('element.out', (e) => {
overlays.clear();
});
};
const genNodeDetailBox = (e, overlays, data) => {
overlays.add(e.element.id, {
position: { top: e.element.height, left: 0 },
html: `<div class="verlays">
<p>审批人员: ${data.nickName || ''}<p/>
<p>节点状态${data.status || ''}</p>
<p>开始时间${data.startTime || ''}</p>
<p>结束时间${data.endTime || ''}</p>
<p>审批耗时${data.runDuration || ''}</p>
<p>流程版本v${data.version || ''}</p>
</div>`
});
};
//
const fitViewport = () => {
zoom.value = modeler.value.get<Canvas>('canvas').zoom('fit-viewport');
const bbox = document.querySelector<SVGGElement>('.flow-containers .viewport').getBBox();
const currentViewBox = modeler.value.get('canvas').viewbox();
const elementMid = {
x: bbox.x + bbox.width / 2 - 65,
y: bbox.y + bbox.height / 2
};
modeler.value.get<Canvas>('canvas').viewbox({
x: elementMid.x - currentViewBox.width / 2,
y: elementMid.y - currentViewBox.height / 2,
width: currentViewBox.width,
height: currentViewBox.height
});
zoom.value = (bbox.width / currentViewBox.width) * 1.8;
};
//
const zoomViewport = (zoomIn = true) => {
zoom.value = modeler.value.get<Canvas>('canvas').zoom();
zoom.value += zoomIn ? 0.1 : -0.1;
modeler.value.get<Canvas>('canvas').zoom(zoom.value);
};
//
const fillColor = () => {
const canvas = modeler.value.get<Canvas>('canvas');
bpmnNodeList(modeler.value._definitions.rootElements[0].flowElements, canvas);
};
//
const bpmnNodeList = (flowElements, canvas) => {
flowElements.forEach((n) => {
if (n.$type === 'bpmn:UserTask') {
const completeTask = taskList.value.find((m) => m.key === n.id);
if (completeTask) {
canvas.addMarker(n.id, completeTask.completed ? 'highlight' : 'highlight-todo');
n.outgoing?.forEach((nn) => {
const targetTask = taskList.value.find((m) => m.key === nn.targetRef.id);
if (targetTask) {
canvas.addMarker(nn.id, targetTask.completed ? 'highlight' : 'highlight-todo');
} else if (nn.targetRef.$type === 'bpmn:ExclusiveGateway') {
canvas.addMarker(nn.id, completeTask.completed ? 'highlight' : 'highlight-todo');
canvas.addMarker(nn.targetRef.id, completeTask.completed ? 'highlight' : 'highlight-todo');
nn.targetRef.outgoing.forEach((e) => {
gateway(e.id, e.targetRef.$type, e.targetRef.id, canvas, completeTask.completed);
});
} else if (nn.targetRef.$type === 'bpmn:ParallelGateway') {
canvas.addMarker(nn.id, completeTask.completed ? 'highlight' : 'highlight-todo');
canvas.addMarker(nn.targetRef.id, completeTask.completed ? 'highlight' : 'highlight-todo');
nn.targetRef.outgoing.forEach((e) => {
gateway(e.id, e.targetRef.$type, e.targetRef.id, canvas, completeTask.completed);
});
} else if (nn.targetRef.$type === 'bpmn:InclusiveGateway') {
canvas.addMarker(nn.id, completeTask.completed ? 'highlight' : 'highlight-todo');
canvas.addMarker(nn.targetRef.id, completeTask.completed ? 'highlight' : 'highlight-todo');
nn.targetRef.outgoing.forEach((e) => {
gateway(e.id, e.targetRef.$type, e.targetRef.id, canvas, completeTask.completed);
});
}
});
}
} else if (n.$type === 'bpmn:ExclusiveGateway') {
n.outgoing.forEach((nn) => {
const targetTask = taskList.value.find((m) => m.key === nn.targetRef.id);
if (targetTask) {
canvas.addMarker(nn.id, targetTask.completed ? 'highlight' : 'highlight-todo');
}
});
} else if (n.$type === 'bpmn:ParallelGateway') {
n.outgoing.forEach((nn) => {
const targetTask = taskList.value.find((m) => m.key === nn.targetRef.id);
if (targetTask) {
canvas.addMarker(nn.id, targetTask.completed ? 'highlight' : 'highlight-todo');
}
});
} else if (n.$type === 'bpmn:InclusiveGateway') {
n.outgoing.forEach((nn) => {
const targetTask = taskList.value.find((m) => m.key === nn.targetRef.id);
if (targetTask) {
canvas.addMarker(nn.id, targetTask.completed ? 'highlight' : 'highlight-todo');
}
});
} else if (n.$type === 'bpmn:SubProcess') {
const completeTask = taskList.value.find((m) => m.key === n.id);
if (completeTask) {
canvas.addMarker(n.id, completeTask.completed ? 'highlight' : 'highlight-todo');
}
bpmnNodeList(n.flowElements, canvas);
} else if (n.$type === 'bpmn:StartEvent') {
canvas.addMarker(n.id, 'startEvent');
if (n.outgoing) {
n.outgoing.forEach((nn) => {
const completeTask = taskList.value.find((m) => m.key === nn.targetRef.id);
if (completeTask) {
canvas.addMarker(nn.id, 'highlight');
canvas.addMarker(n.id, 'highlight');
}
});
}
} else if (n.$type === 'bpmn:EndEvent') {
canvas.addMarker(n.id, 'endEvent');
const completeTask = taskList.value.find((m) => m.key === n.id);
if (completeTask) {
canvas.addMarker(completeTask.key, 'highlight');
canvas.addMarker(n.id, 'highlight');
return;
}
}
});
};
const gateway = (id, targetRefType, targetRefId, canvas, completed) => {
if (targetRefType === 'bpmn:ExclusiveGateway') {
canvas.addMarker(id, completed ? 'highlight' : 'highlight-todo');
canvas.addMarker(targetRefId, completed ? 'highlight' : 'highlight-todo');
}
if (targetRefType === 'bpmn:ParallelGateway') {
canvas.addMarker(id, completed ? 'highlight' : 'highlight-todo');
canvas.addMarker(targetRefId, completed ? 'highlight' : 'highlight-todo');
}
if (targetRefType === 'bpmn:InclusiveGateway') {
canvas.addMarker(id, completed ? 'highlight' : 'highlight-todo');
canvas.addMarker(targetRefId, completed ? 'highlight' : 'highlight-todo');
}
};
defineExpose({
init,
initXml
});
</script>
<style lang="scss" scoped>
.canvas {
width: 100%;
height: 100%;
}
.header-div {
display: flex;
padding: 10px 0;
justify-content: space-between;
.tips-label {
display: flex;
div {
margin-right: 10px;
padding: 5px;
font-size: 12px;
}
.un-complete {
border: 1px solid #000;
}
.in-progress {
background-color: rgb(255, 237, 204);
border: 1px dashed orange;
}
.complete {
background-color: rgb(204, 230, 204);
border: 1px solid green;
}
}
}
.view-mode {
.el-header,
.el-aside,
.djs-palette,
.bjs-powered-by {
display: none;
}
.el-loading-mask {
background-color: initial;
}
.el-loading-spinner {
display: none;
}
}
.bpmn-el-container {
height: calc(100vh - 350px);
}
.flow-containers {
width: 100%;
height: 100%;
overflow-y: auto;
.canvas {
width: 100%;
height: 100%;
}
.load {
margin-right: 10px;
}
:deep(.el-form-item__label) {
font-size: 13px;
}
:deep(.djs-palette) {
left: 0 !important;
top: 0;
border-top: none;
}
:deep(.djs-container svg) {
min-height: 650px;
}
:deep(.startEvent.djs-shape .djs-visual > :nth-child(1)) {
fill: #77df6d !important;
}
:deep(.endEvent.djs-shape .djs-visual > :nth-child(1)) {
fill: #ee7b77 !important;
}
:deep(.highlight.djs-shape .djs-visual > :nth-child(1)) {
fill: green !important;
stroke: green !important;
fill-opacity: 0.2 !important;
}
:deep(.highlight.djs-shape .djs-visual > :nth-child(2)) {
fill: green !important;
}
:deep(.highlight.djs-shape .djs-visual > path) {
fill: green !important;
fill-opacity: 0.2 !important;
stroke: green !important;
}
:deep(.highlight.djs-connection > .djs-visual > path) {
stroke: green !important;
}
//
@keyframes path-animation {
from {
stroke-dashoffset: 100%;
}
to {
stroke-dashoffset: 0%;
}
}
:deep(.highlight-todo.djs-connection > .djs-visual > path) {
animation: path-animation 60s;
animation-timing-function: linear;
animation-iteration-count: infinite;
stroke-dasharray: 4px !important;
stroke: orange !important;
fill-opacity: 0.2 !important;
marker-end: url('#sequenceflow-end-_E7DFDF-_E7DFDF-803g1kf6zwzmcig1y2ulm5egr');
}
:deep(.highlight-todo.djs-shape .djs-visual > :nth-child(1)) {
animation: path-animation 60s;
animation-timing-function: linear;
animation-iteration-count: infinite;
stroke-dasharray: 4px !important;
stroke: orange !important;
fill: orange !important;
fill-opacity: 0.2 !important;
}
}
:deep(.verlays) {
width: 250px;
background: rgb(102, 102, 102);
border-radius: 4px;
border: 1px solid #ebeef5;
color: #fff;
padding: 15px 10px;
p {
line-height: 28px;
margin: 0;
padding: 0;
}
cursor: pointer;
}
</style>

View File

@ -1,116 +0,0 @@
<template>
<div class="container">
<el-dialog v-model="visible" draggable title="审批记录" :width="props.width" :height="props.height" :close-on-click-modal="false">
<el-tabs v-model="tabActiveName" class="demo-tabs">
<el-tab-pane label="流程图" name="bpmn">
<BpmnView ref="bpmnViewRef"></BpmnView>
</el-tab-pane>
<el-tab-pane v-loading="loading" label="审批信息" name="info">
<div>
<el-table :data="historyList" style="width: 100%" border fit>
<el-table-column type="index" label="序号" align="center" width="60"></el-table-column>
<el-table-column prop="name" label="任务名称" sortable align="center"></el-table-column>
<el-table-column prop="nickName" :show-overflow-tooltip="true" label="办理人" sortable align="center">
<template #default="scope">
<el-tag type="success">{{ scope.row.nickName || '无' }}</el-tag>
</template>
</el-table-column>
<el-table-column label="状态" sortable align="center">
<template #default="scope">
<el-tag type="success">{{ scope.row.statusName }}</el-tag>
</template>
</el-table-column>
<el-table-column prop="comment" label="审批意见" sortable align="center"></el-table-column>
<el-table-column prop="startTime" label="开始时间" sortable align="center"></el-table-column>
<el-table-column prop="endTime" label="结束时间" sortable align="center"></el-table-column>
<el-table-column prop="runDuration" label="运行时长" sortable align="center"></el-table-column>
<el-table-column prop="attachmentList" label="附件" sortable align="center">
<template #default="scope">
<el-popover v-if="scope.row.attachmentList && scope.row.attachmentList.length > 0" placement="right" :width="310" trigger="click">
<template #reference>
<el-button style="margin-right: 16px">附件</el-button>
</template>
<el-table border :data="scope.row.attachmentList">
<el-table-column prop="name" width="202" :show-overflow-tooltip="true" label="附件名称"></el-table-column>
<el-table-column prop="name" width="80" align="center" :show-overflow-tooltip="true" label="操作">
<template #default="tool">
<el-button type="text" @click="handleDownload(tool.row.contentId)">下载</el-button>
</template>
</el-table-column>
</el-table>
</el-popover>
</template>
</el-table-column>
</el-table>
</div>
</el-tab-pane>
</el-tabs>
</el-dialog>
</div>
</template>
<script lang="ts" setup>
import BpmnView from '@/components/BpmnView/index.vue';
import processApi from '@/api/workflow/processInstance';
import { propTypes } from '@/utils/propTypes';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const props = defineProps({
width: propTypes.string.def('70%'),
height: propTypes.string.def('100%')
});
const loading = ref(false);
const visible = ref(false);
const historyList = ref<Array<any>>([]);
const tabActiveName = ref('bpmn');
const bpmnViewRef = ref<BpmnView>();
//
const init = async (businessKey: string | number) => {
visible.value = true;
loading.value = true;
tabActiveName.value = 'bpmn';
historyList.value = [];
processApi.getHistoryRecord(businessKey).then((resp) => {
historyList.value = resp.data;
loading.value = false;
});
await nextTick(() => {
bpmnViewRef.value.init(businessKey);
});
};
/** 下载按钮操作 */
const handleDownload = (ossId: string) => {
proxy?.$download.oss(ossId);
};
/**
* 对外暴露子组件方法
*/
defineExpose({
init
});
</script>
<style lang="scss" scoped>
.triangle {
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
border-radius: 6px;
}
.triangle::after {
content: ' ';
position: absolute;
top: 8em;
right: 215px;
border: 15px solid;
border-color: transparent #fff transparent transparent;
}
.container {
:deep(.el-dialog .el-dialog__body) {
max-height: calc(100vh - 170px) !important;
min-height: calc(100vh - 170px) !important;
}
}
</style>

View File

@ -1,378 +0,0 @@
<template>
<el-dialog v-model="visible" draggable :title="title" :width="width" :height="height" append-to-body :close-on-click-modal="false">
<div v-if="multiInstance === 'add'" class="p-2">
<el-row :gutter="20">
<!-- 部门树 -->
<el-col :lg="4" :xs="24" style="">
<el-card shadow="hover">
<el-input v-model="deptName" placeholder="请输入部门名称" prefix-icon="Search" clearable />
<el-tree
ref="deptTreeRef"
class="mt-2"
node-key="id"
:data="deptOptions"
:props="{ label: 'label', children: 'children' }"
:expand-on-click-node="false"
:filter-node-method="filterNode"
highlight-current
default-expand-all
@node-click="handleNodeClick"
></el-tree>
</el-card>
</el-col>
<el-col :lg="20" :xs="24">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div v-show="showSearch" class="search">
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
<el-form-item label="用户名称" prop="userName">
<el-input v-model="queryParams.userName" placeholder="请输入用户名称" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="手机号码" prop="phonenumber">
<el-input v-model="queryParams.phonenumber" placeholder="请输入手机号码" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
</div>
</transition>
<el-card shadow="hover">
<template #header>
<el-row :gutter="10">
<right-toolbar v-model:showSearch="showSearch" :search="true" @query-table="handleQuery"></right-toolbar>
</el-row>
</template>
<el-table ref="multipleTableRef" v-loading="loading" :data="userList" row-key="userId" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="50" align="center" />
<el-table-column key="userId" label="用户编号" align="center" prop="userId" />
<el-table-column key="userName" label="用户名称" align="center" prop="userName" :show-overflow-tooltip="true" />
<el-table-column key="nickName" label="用户昵称" align="center" prop="nickName" :show-overflow-tooltip="true" />
<el-table-column key="phonenumber" label="手机号码" align="center" prop="phonenumber" width="120" />
<el-table-column label="创建时间" align="center" prop="createTime" width="160">
<template #default="scope">
<span>{{ scope.row.createTime }}</span>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total > 0"
v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize"
:total="total"
@pagination="handleQuery"
/>
</el-card>
<el-card shadow="hover">
<el-tag v-for="(user, index) in chooseUserList" :key="user.userId" style="margin: 2px" closable @close="handleCloseTag(user, index)"
>{{ user.userName }}
</el-tag>
</el-card>
</el-col>
</el-row>
</div>
<div v-if="multiInstance === 'delete'" class="p-2">
<el-table v-loading="loading" :data="taskList" @selection-change="handleTaskSelection">
<el-table-column type="selection" width="55" />
<el-table-column prop="name" label="任务名称" />
<el-table-column prop="assigneeName" label="办理人" />
</el-table>
</div>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="submitFileForm"> </el-button>
<el-button @click="visible = false"> </el-button>
</div>
</template>
</el-dialog>
</template>
<script setup name="User" lang="ts">
import { deptTreeSelect, listUser, optionSelect } from '@/api/system/user';
import {
addMultiInstanceExecution,
deleteMultiInstanceExecution,
getTaskUserIdsByAddMultiInstance,
getListByDeleteMultiInstance
} from '@/api/workflow/task';
import { UserVO } from '@/api/system/user/types';
import { DeptVO } from '@/api/system/dept/types';
import { ComponentInternalInstance } from 'vue';
import { ElTree, ElTable } from 'element-plus';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const props = defineProps({
//
width: {
type: String,
default: '70%'
},
//
height: {
type: String,
default: '100%'
},
//
title: {
type: String,
default: '加签人员'
},
//
multiple: {
type: Boolean,
default: true
},
//id
userIdList: {
type: Array,
default: () => []
}
});
const deptTreeRef = ref(ElTree);
const multipleTableRef = ref(ElTable);
const userList = ref<UserVO[]>();
const taskList = ref<Array<any>[]>();
const loading = ref(true);
const showSearch = ref(true);
const selectionTask = ref<Array<any>[]>();
const visible = ref(false);
const total = ref(0);
const deptName = ref('');
const deptOptions = ref<DeptVO[]>([]);
const chooseUserList = ref(ref<UserVO[]>());
const userIds = ref<Array<number | string>>([]);
//
const multiInstance = ref('');
const queryParams = ref<Record<string, any>>({
pageNum: 1,
pageSize: 10,
userName: '',
nickName: '',
taskId: ''
});
/** 查询用户列表 */
const getAddMultiInstanceList = async (taskId: string, userIdList: Array<number | string>) => {
deptOptions.value = [];
getTreeSelect();
multiInstance.value = 'add';
userIds.value = userIdList;
visible.value = true;
queryParams.value.taskId = taskId;
loading.value = true;
const res1 = await getTaskUserIdsByAddMultiInstance(taskId);
queryParams.value.excludeUserIds = res1.data;
const res = await listUser(queryParams.value);
loading.value = false;
userList.value = res.rows;
total.value = res.total;
if (userList.value && userIds.value.length > 0) {
const data = await optionSelect(userIds.value);
if (data.data && data.data.length > 0) {
chooseUserList.value = data.data;
data.data.forEach((user: UserVO) => {
multipleTableRef.value!.toggleRowSelection(
userList.value.find((item) => {
return item.userId == user.userId;
}),
true
);
});
}
}
};
const getList = async () => {
loading.value = true;
const res1 = await getTaskUserIdsByAddMultiInstance(queryParams.value.taskId);
queryParams.value.excludeUserIds = res1.data;
const res = await listUser(queryParams.value);
loading.value = false;
userList.value = res.rows;
total.value = res.total;
if (userList.value && userIds.value.length > 0) {
const data = await optionSelect(userIds.value);
if (data.data && data.data.length > 0) {
chooseUserList.value = data.data;
data.data.forEach((user: UserVO) => {
multipleTableRef.value!.toggleRowSelection(
userList.value.find((item) => {
return item.userId == user.userId;
}),
true
);
});
}
}
};
const getDeleteMultiInstanceList = async (taskId: string) => {
deptOptions.value = [];
loading.value = true;
queryParams.value.taskId = taskId;
multiInstance.value = 'delete';
visible.value = true;
const res = await getListByDeleteMultiInstance(taskId);
taskList.value = res.data;
loading.value = false;
};
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.value.pageNum = 1;
getAddMultiInstanceList(queryParams.value.taskId, userIds.value);
};
/** 重置按钮操作 */
const resetQuery = () => {
queryParams.value.pageNum = 1;
queryParams.value.deptId = undefined;
queryParams.value.userName = undefined;
queryParams.value.nickName = undefined;
deptTreeRef.value.setCurrentKey(null);
handleQuery();
};
/** 选择条数 */
const handleSelectionChange = (selection: UserVO[]) => {
if (props.multiple) {
chooseUserList.value = selection.filter((element, index, self) => {
return self.findIndex((x) => x.userId === element.userId) === index;
});
selection.forEach((u) => {
if (chooseUserList.value && !chooseUserList.value.includes(u)) {
multipleTableRef.value!.toggleRowSelection(u, undefined);
}
});
userIds.value = chooseUserList.value.map((item) => {
return item.userId;
});
} else {
chooseUserList.value = selection;
if (selection.length > 1) {
let delRow = selection.shift();
multipleTableRef.value!.toggleRowSelection(delRow, undefined);
}
if (selection.length === 0) {
chooseUserList.value = [];
}
}
};
/** 选择条数 */
const handleTaskSelection = (selection: any) => {
selectionTask.value = selection;
};
/** 查询部门下拉树结构 */
const getTreeSelect = async () => {
const res = await deptTreeSelect();
deptOptions.value = res.data;
};
/** 通过条件过滤节点 */
const filterNode = (value: string, data: any) => {
if (!value) return true;
return data.label.indexOf(value) !== -1;
};
/** 根据名称筛选部门树 */
watchEffect(
() => {
if (visible.value && deptOptions.value && deptOptions.value.length > 0) {
deptTreeRef.value.filter(deptName.value);
}
},
{
flush: 'post' // watchEffectDOMDOM
}
);
/** 节点单击事件 */
const handleNodeClick = (data: DeptVO) => {
queryParams.value.deptId = data.id;
getList();
};
//tag
const handleCloseTag = (user: UserVO, index: any) => {
if (multipleTableRef.value.selection && multipleTableRef.value.selection.length > 0) {
multipleTableRef.value.selection.forEach((u: UserVO, i: number) => {
if (user.userId === u.userId) {
multipleTableRef.value.selection.splice(i, 1);
}
});
}
if (chooseUserList.value && chooseUserList.value.length > 0) {
chooseUserList.value.splice(index, 1);
}
multipleTableRef.value.toggleRowSelection(user, undefined);
if (userIds.value && userIds.value.length > 0) {
userIds.value.forEach((userId, i) => {
if (userId === user.userId) {
userIds.value.splice(i, 1);
}
});
}
};
const submitFileForm = async () => {
if (multiInstance.value === 'add') {
if (chooseUserList.value && chooseUserList.value.length > 0) {
loading.value = true;
let userIds = chooseUserList.value.map((item) => {
return item.userId;
});
let nickNames = chooseUserList.value.map((item) => {
return item.nickName;
});
let params = {
taskId: queryParams.value.taskId,
assignees: userIds,
assigneeNames: nickNames
};
await addMultiInstanceExecution(params);
emits('submitCallback');
loading.value = false;
proxy?.$modal.msgSuccess('操作成功');
visible.value = false;
}
} else {
if (selectionTask.value && selectionTask.value.length > 0) {
loading.value = true;
let taskIds = selectionTask.value.map((item: any) => {
return item.id;
});
let executionIds = selectionTask.value.map((item: any) => {
return item.executionId;
});
let assigneeIds = selectionTask.value.map((item: any) => {
return item.assignee;
});
let assigneeNames = selectionTask.value.map((item: any) => {
return item.assigneeName;
});
let params = {
taskId: queryParams.value.taskId,
taskIds: taskIds,
executionIds: executionIds,
assigneeIds: assigneeIds,
assigneeNames: assigneeNames
};
await deleteMultiInstanceExecution(params);
emits('submitCallback');
loading.value = false;
proxy?.$modal.msgSuccess('操作成功');
visible.value = false;
}
}
};
//
const emits = defineEmits(['submitCallback']);
/**
* 对外暴露子组件方法
*/
defineExpose({
getAddMultiInstanceList,
getDeleteMultiInstanceList
});
</script>

View File

@ -1,365 +0,0 @@
<template>
<el-dialog v-model="dialog.visible" :title="dialog.title" width="50%" draggable :before-close="cancel" center :close-on-click-modal="false">
<el-form v-loading="loading" :model="form" label-width="120px">
<el-form-item label="消息提醒">
<el-checkbox-group v-model="form.messageType">
<el-checkbox label="1" name="type" disabled>站内信</el-checkbox>
<el-checkbox label="2" name="type">邮件</el-checkbox>
<el-checkbox label="3" name="type">短信</el-checkbox>
</el-checkbox-group>
</el-form-item>
<el-form-item v-if="task.businessStatus === 'waiting'" label="附件">
<fileUpload v-model="form.fileId" :file-type="['doc', 'xls', 'ppt', 'txt', 'pdf', 'xlsx', 'docx', 'zip']" :file-size="'20'" />
</el-form-item>
<el-form-item label="抄送">
<el-button type="primary" icon="Plus" circle @click="openUserSelectCopy" />
<el-tag v-for="user in selectCopyUserList" :key="user.userId" closable style="margin: 2px" @close="handleCopyCloseTag(user)">
{{ user.userName }}
</el-tag>
</el-form-item>
<el-form-item v-if="task.businessStatus === 'waiting'" label="审批意见">
<el-input v-model="form.message" type="textarea" resize="none" />
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button :disabled="buttonDisabled" type="primary" @click="handleCompleteTask"> 提交 </el-button>
<el-button v-if="task.businessStatus === 'waiting'" :disabled="buttonDisabled" type="primary" @click="openDelegateTask"> 委托 </el-button>
<el-button v-if="task.businessStatus === 'waiting'" :disabled="buttonDisabled" type="primary" @click="openTransferTask"> 转办 </el-button>
<el-button
v-if="task.businessStatus === 'waiting' && task.multiInstance"
:disabled="buttonDisabled"
type="primary"
@click="addMultiInstanceUser"
>
加签
</el-button>
<el-button
v-if="task.businessStatus === 'waiting' && task.multiInstance"
:disabled="buttonDisabled"
type="primary"
@click="deleteMultiInstanceUser"
>
减签
</el-button>
<el-button v-if="task.businessStatus === 'waiting'" :disabled="buttonDisabled" type="danger" @click="handleTerminationTask"> 终止 </el-button>
<el-button v-if="task.businessStatus === 'waiting'" :disabled="buttonDisabled" type="danger" @click="handleBackProcessOpen"> 退回 </el-button>
<el-button :disabled="buttonDisabled" @click="cancel">取消</el-button>
</span>
</template>
<!-- 抄送 -->
<UserSelect ref="userSelectCopyRef" :multiple="true" :data="selectCopyUserIds" @confirm-call-back="userSelectCopyCallBack"></UserSelect>
<!-- 转办 -->
<UserSelect ref="transferTaskRef" :multiple="false" @confirm-call-back="handleTransferTask"></UserSelect>
<!-- 委托 -->
<UserSelect ref="delegateTaskRef" :multiple="false" @confirm-call-back="handleDelegateTask"></UserSelect>
<!-- 加签组件 -->
<multiInstanceUser ref="multiInstanceUserRef" :title="title" @submit-callback="closeDialog" />
<!-- 驳回开始 -->
<el-dialog v-model="backVisible" draggable title="驳回" width="40%" :close-on-click-modal="false">
<el-form v-if="task.businessStatus === 'waiting'" v-loading="backLoading" :model="backForm" label-width="120px">
<el-form-item label="驳回节点">
<el-select v-model="backForm.targetActivityId" clearable placeholder="请选择" style="width: 300px">
<el-option v-for="item in taskNodeList" :key="item.nodeId" :label="item.nodeName" :value="item.nodeId" />
</el-select>
</el-form-item>
<el-form-item label="消息提醒">
<el-checkbox-group v-model="backForm.messageType">
<el-checkbox label="1" name="type" disabled>站内信</el-checkbox>
<el-checkbox label="2" name="type">邮件</el-checkbox>
<el-checkbox label="3" name="type">短信</el-checkbox>
</el-checkbox-group>
</el-form-item>
<el-form-item label="审批意见">
<el-input v-model="backForm.message" type="textarea" resize="none" />
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer" style="float: right; padding-bottom: 20px">
<el-button :disabled="backButtonDisabled" type="primary" @click="handleBackProcess">确认</el-button>
<el-button :disabled="backButtonDisabled" @click="backVisible = false">取消</el-button>
</div>
</template>
</el-dialog>
<!-- 驳回结束 -->
</el-dialog>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import { ComponentInternalInstance } from 'vue';
import { ElForm } from 'element-plus';
import { completeTask, backProcess, getTaskById, transferTask, terminationTask, getTaskNodeList, delegateTask } from '@/api/workflow/task';
import UserSelect from '@/components/UserSelect';
import MultiInstanceUser from '@/components/Process/multiInstanceUser.vue';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
import { UserVO } from '@/api/system/user/types';
import { TaskVO } from '@/api/workflow/task/types';
const userSelectCopyRef = ref<InstanceType<typeof UserSelect>>();
const transferTaskRef = ref<InstanceType<typeof UserSelect>>();
const delegateTaskRef = ref<InstanceType<typeof UserSelect>>();
//
const multiInstanceUserRef = ref<InstanceType<typeof MultiInstanceUser>>();
const props = defineProps({
taskVariables: {
type: Object as () => Record<string, any>,
default: () => {}
}
});
//
const loading = ref(true);
//
const buttonDisabled = ref(true);
//id
const taskId = ref<string>('');
//
const selectCopyUserList = ref<UserVO[]>([]);
//id
const selectCopyUserIds = ref<string>(undefined);
//
const backVisible = ref(false);
const backLoading = ref(true);
const backButtonDisabled = ref(true);
//
const taskNodeList = ref([]);
//
const task = ref<TaskVO>({
id: undefined,
name: undefined,
description: undefined,
priority: undefined,
owner: undefined,
assignee: undefined,
assigneeName: undefined,
processInstanceId: undefined,
executionId: undefined,
taskDefinitionId: undefined,
processDefinitionId: undefined,
endTime: undefined,
taskDefinitionKey: undefined,
dueDate: undefined,
category: undefined,
parentTaskId: undefined,
claimTime: undefined,
businessStatus: undefined,
businessStatusName: undefined,
processDefinitionName: undefined,
processDefinitionKey: undefined,
participantVo: undefined,
multiInstance: undefined,
businessKey: undefined,
wfNodeConfigVo: undefined
});
//
const title = ref('');
const dialog = reactive<DialogOption>({
visible: false,
title: '提示'
});
const form = ref<Record<string, any>>({
taskId: undefined,
message: undefined,
variables: {},
messageType: ['1'],
wfCopyList: []
});
const backForm = ref<Record<string, any>>({
taskId: undefined,
targetActivityId: undefined,
message: undefined,
variables: {},
messageType: ['1']
});
const closeDialog = () => {
dialog.visible = false;
};
//
const openDialog = (id?: string) => {
selectCopyUserIds.value = undefined;
selectCopyUserList.value = [];
form.value.fileId = undefined;
taskId.value = id;
form.value.message = undefined;
dialog.visible = true;
loading.value = true;
buttonDisabled.value = true;
nextTick(() => {
getTaskById(taskId.value).then((response) => {
task.value = response.data;
loading.value = false;
buttonDisabled.value = false;
});
});
};
onMounted(() => {});
const emits = defineEmits(['submitCallback', 'cancelCallback']);
/** 办理流程 */
const handleCompleteTask = async () => {
form.value.taskId = taskId.value;
form.value.taskVariables = props.taskVariables;
if (selectCopyUserList.value && selectCopyUserList.value.length > 0) {
let wfCopyList = [];
selectCopyUserList.value.forEach((e) => {
let copyUser = {
userId: e.userId,
userName: e.nickName
};
wfCopyList.push(copyUser);
});
form.value.wfCopyList = wfCopyList;
}
await proxy?.$modal.confirm('是否确认提交?');
loading.value = true;
buttonDisabled.value = true;
try {
await completeTask(form.value);
dialog.visible = false;
emits('submitCallback');
proxy?.$modal.msgSuccess('操作成功');
} finally {
loading.value = false;
buttonDisabled.value = false;
}
};
/** 驳回弹窗打开 */
const handleBackProcessOpen = async () => {
backForm.value = {};
backForm.value.messageType = ['1'];
backVisible.value = true;
backLoading.value = true;
backButtonDisabled.value = true;
let data = await getTaskNodeList(task.value.processInstanceId);
taskNodeList.value = data.data;
backLoading.value = false;
backButtonDisabled.value = false;
backForm.value.targetActivityId = taskNodeList.value[0].nodeId;
};
/** 驳回流程 */
const handleBackProcess = async () => {
backForm.value.taskId = taskId.value;
await proxy?.$modal.confirm('是否确认驳回到申请人?');
loading.value = true;
backLoading.value = true;
backButtonDisabled.value = true;
await backProcess(backForm.value).finally(() => (loading.value = false));
dialog.visible = false;
backLoading.value = false;
backButtonDisabled.value = false;
emits('submitCallback');
proxy?.$modal.msgSuccess('操作成功');
};
//
const cancel = async () => {
dialog.visible = false;
buttonDisabled.value = false;
emits('cancelCallback');
};
//
const openUserSelectCopy = () => {
userSelectCopyRef.value.open();
};
//
const userSelectCopyCallBack = (data: UserVO[]) => {
if (data && data.length > 0) {
selectCopyUserList.value = data;
selectCopyUserIds.value = selectCopyUserList.value.map((item) => item.userId).join(',');
}
};
//
const handleCopyCloseTag = (user: UserVO) => {
const userId = user.userId;
// 使split
const index = selectCopyUserList.value.findIndex((item) => item.userId === userId);
selectCopyUserList.value.splice(index, 1);
selectCopyUserIds.value = selectCopyUserList.value.map((item) => item.userId).join(',');
};
//
const addMultiInstanceUser = () => {
if (multiInstanceUserRef.value) {
title.value = '加签人员';
multiInstanceUserRef.value.getAddMultiInstanceList(taskId.value, []);
}
};
//
const deleteMultiInstanceUser = () => {
if (multiInstanceUserRef.value) {
title.value = '减签人员';
multiInstanceUserRef.value.getDeleteMultiInstanceList(taskId.value);
}
};
//
const openTransferTask = () => {
transferTaskRef.value.open();
};
//
const handleTransferTask = async (data) => {
if (data && data.length > 0) {
let params = {
taskId: taskId.value,
userId: data[0].userId,
comment: form.value.message
};
await proxy?.$modal.confirm('是否确认提交?');
loading.value = true;
buttonDisabled.value = true;
await transferTask(params).finally(() => (loading.value = false));
dialog.visible = false;
emits('submitCallback');
proxy?.$modal.msgSuccess('操作成功');
} else {
proxy?.$modal.msgWarning('请选择用户!');
}
};
//
const openDelegateTask = () => {
delegateTaskRef.value.open();
};
//
const handleDelegateTask = async (data) => {
if (data && data.length > 0) {
let params = {
taskId: taskId.value,
userId: data[0].userId,
nickName: data[0].nickName
};
await proxy?.$modal.confirm('是否确认提交?');
loading.value = true;
buttonDisabled.value = true;
await delegateTask(params).finally(() => (loading.value = false));
dialog.visible = false;
emits('submitCallback');
proxy?.$modal.msgSuccess('操作成功');
} else {
proxy?.$modal.msgWarning('请选择用户!');
}
};
//
const handleTerminationTask = async (data) => {
let params = {
taskId: taskId.value,
comment: form.value.message
};
await proxy?.$modal.confirm('是否确认终止?');
loading.value = true;
buttonDisabled.value = true;
await terminationTask(params).finally(() => (loading.value = false));
dialog.visible = false;
emits('submitCallback');
proxy?.$modal.msgSuccess('操作成功');
};
/**
* 对外暴露子组件方法
*/
defineExpose({
openDialog
});
</script>

View File

@ -1,13 +0,0 @@
<template>
<div>
<svg-icon icon-class="question" @click="goto" />
</div>
</template>
<script setup>
const url = ref('https://plus-doc.dromara.org/');
function goto() {
window.open(url.value);
}
</script>

View File

@ -1,13 +0,0 @@
<template>
<div>
<svg-icon icon-class="github" @click="goto" />
</div>
</template>
<script setup>
const url = ref('https://gitee.com/dromara/RuoYi-Vue-Plus');
function goto() {
window.open(url.value);
}
</script>

View File

@ -1,17 +0,0 @@
export enum AllocationTypeEnum {
USER = 'user',
CANDIDATE = 'candidate',
YOURSELF = 'yourself',
SPECIFY = 'specify'
}
export enum SpecifyDescEnum {
SPECIFY_MULTIPLE = 'specifyMultiple',
SPECIFY_SINGLE = 'specifySingle'
}
export enum MultiInstanceTypeEnum {
SERIAL = 'serial',
PARALLEL = 'parallel',
NONE = 'none'
}

View File

@ -1,7 +1,6 @@
{
"route": {
"dashboard": "首页",
"document": "项目文档"
},
"login": {
"username": "用户名",
@ -14,7 +13,6 @@
"full": "全屏",
"language": "语言",
"dashboard": "首页",
"document": "项目文档",
"message": "消息",
"layoutSize": "布局大小",
"layoutSetting": "布局设置",

View File

@ -2,7 +2,6 @@ export default {
// 路由国际化
route: {
dashboard: '首页',
document: '项目文档'
},
// 登录页面国际化
login: {
@ -16,7 +15,6 @@ export default {
full: '全屏',
language: '语言',
dashboard: '首页',
document: '项目文档',
message: '消息',
layoutSize: '布局大小',
layoutSetting: '布局设置',

View File

@ -28,13 +28,6 @@
</el-popover>
</div>
</el-tooltip>
<el-tooltip content="Github" effect="dark" placement="bottom">
<ruo-yi-git id="ruoyi-git" class="right-menu-item hover-effect" />
</el-tooltip>
<el-tooltip :content="$t('navbar.document')" effect="dark" placement="bottom">
<ruo-yi-doc id="ruoyi-doc" class="right-menu-item hover-effect" />
</el-tooltip>
<el-tooltip :content="$t('navbar.full')" effect="dark" placement="bottom">
<screenfull id="screenfull" class="right-menu-item hover-effect" />

View File

@ -34,7 +34,7 @@ defineProps({
}
});
const title = ref('RuoYi-Vue-Plus');
const title = ref('后台管理系统');
const settingsStore = useSettingsStore();
const sideTheme = computed(() => settingsStore.sideTheme);
</script>

View File

@ -19,7 +19,6 @@
</template>
<el-empty v-else :description="'消息为空'"></el-empty>
</div>
<div v-if="newsList.length > 0" class="foot-box" @click="onGoToGiteeClick">前往gitee</div>
</div>
</template>
@ -53,10 +52,6 @@ const onNewsClick = (item: any) => {
noticeStore.state.value.notices = newsList.value;
};
//
const onGoToGiteeClick = () => {
window.open('https://gitee.com/dromara/RuoYi-Vue-Plus/tree/5.X/');
};
onMounted(() => {
nextTick(() => {

View File

@ -163,20 +163,6 @@ export const dynamicRoutes: RouteRecordRaw[] = [
}
]
},
{
path: '/workflow/leaveEdit',
component: Layout,
hidden: true,
permissions: ['workflow:leave:edit'],
children: [
{
path: 'index',
component: () => import('@/views/workflow/leave/leaveEdit.vue'),
name: 'leaveEdit',
meta: { title: '请假申请', activeMenu: '/workflow/leave', noCache: true }
}
]
}
];
/**

View File

@ -1,13 +0,0 @@
import { MessageApiInjection } from 'naive-ui/lib/message/src/MessageProvider';
declare global {
interface Window {
bpmnInstances: any;
__messageBox: MessageApiInjection;
URL: any;
}
}
declare interface Window {
bpmnInstances: any;
}

View File

@ -1,15 +0,0 @@
declare module 'bpmn' {
import type modeler from 'bpmn-js/lib/Modeler';
import type modeling from 'bpmn-js/lib/features/modeling/Modeling';
import type canvas from 'diagram-js/lib/core/Canvas';
import type elementRegistry from 'diagram-js/lib/core/ElementRegistry';
import type bpmnFactory from 'bpmn-js/lib/features/modeling/BpmnFactory';
export type Modeler = modeler;
export type Modeling = modeling;
export type Canvas = canvas;
export type ElementRegistry = elementRegistry;
export type Moddle = import('moddle').Moddle;
export type ModdleElement = import('moddle').ModdleElement;
export type BpmnFactory = bpmnFactory;
}

View File

@ -1,37 +0,0 @@
declare module 'moddle' {
import type { Element as element } from 'bpmn-js/lib/model/Types';
export type Element = {
get<T>(name: string): T;
set(name: string, value: any): void;
} & element;
export interface ModdleElement extends Element {
$model: Moddle;
readonly $type: string;
$attrs: object | {};
$parent: any;
businessObject: ModdleElement;
type: string;
[field: string]: any;
hasType(element: ModdleElement, type?: string): boolean;
}
export interface Package {
name: string;
prefix: string;
}
export interface Moddle {
typeCache: Record<string, ModdleElement>;
getPackage: typeof Registry.prototype.getPackage;
getPackages: typeof Registry.prototype.getPackages;
create(type: string, attrs?: any): ModdleElement;
}
}

View File

@ -1,92 +0,0 @@
declare module 'bpmnDesign' {
import { AllocationTypeEnum, SpecifyDescEnum, MultiInstanceTypeEnum } from '@/enums/bpmn/IndexEnums';
export interface ParamVO {
type: string;
name: string;
value: string;
}
export interface TaskListenerVO {
event: string;
type: string;
name: string;
className: string;
params: ParamVO[];
}
export interface ExecutionListenerVO {
event: string;
type: string;
className: string;
params: ParamVO[];
}
interface BasePanel {
id: string;
name: string;
}
export interface ProcessPanel extends BasePanel {}
export interface TaskPanel extends BasePanel {
allocationType: AllocationTypeEnum;
specifyDesc: SpecifyDescEnum;
multiInstanceType: MultiInstanceTypeEnum;
async?: boolean;
priority?: number;
formKey?: string;
skipExpression?: string;
isForCompensation?: boolean;
triggerServiceTask?: boolean;
autoStoreVariables?: boolean;
ruleVariablesInput?: string;
excludeTaskListener?: boolean;
exclude?: boolean;
class?: string;
dueDate?: string;
fixedAssignee?: string;
candidateUsers?: string;
assignee?: string;
candidateGroups?: string;
collection?: string;
elementVariable?: string;
completionCondition?: string;
isSequential?: boolean;
loopCharacteristics?: {
collection: string;
elementVariable: string;
isSequential: boolean;
completionCondition: {
body: string;
};
};
}
export interface StartEndPanel extends BasePanel {}
export interface GatewayPanel extends BasePanel {}
export interface SequenceFlowPanel extends BasePanel {
conditionExpression: {
body: string;
};
conditionExpressionValue: string;
skipExpression: string;
}
export interface ParticipantPanel extends BasePanel {}
export interface SubProcessPanel extends BasePanel {
multiInstanceType: MultiInstanceTypeEnum;
collection?: string;
elementVariable?: string;
completionCondition?: string;
loopCharacteristics?: {
collection: string;
elementVariable: string;
isSequential: boolean;
completionCondition: {
body: string;
};
};
}
}

View File

@ -1,254 +0,0 @@
<template>
<div class="p-2">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover">
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
<el-form-item label="部门id" prop="deptId">
<el-input v-model="queryParams.deptId" placeholder="请输入部门id" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="用户id" prop="userId">
<el-input v-model="queryParams.userId" placeholder="请输入用户id" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="排序号" prop="orderNum">
<el-input v-model="queryParams.orderNum" placeholder="请输入排序号" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="key键" prop="testKey">
<el-input v-model="queryParams.testKey" placeholder="请输入key键" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="值" prop="value">
<el-input v-model="queryParams.value" placeholder="请输入值" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
</el-card>
</div>
</transition>
<el-card shadow="hover">
<template #header>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button v-hasPermi="['demo:demo:add']" type="primary" plain icon="Plus" @click="handleAdd">新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button v-hasPermi="['demo:demo:edit']" type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()">修改</el-button>
</el-col>
<el-col :span="1.5">
<el-button v-hasPermi="['demo:demo:remove']" type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()"
>删除</el-button
>
</el-col>
<el-col :span="1.5">
<el-button v-hasPermi="['demo:demo:export']" type="warning" plain icon="Download" @click="handleExport">导出</el-button>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @query-table="getList"></right-toolbar>
</el-row>
</template>
<el-table v-loading="loading" :data="demoList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column v-if="true" label="主键" align="center" prop="id" />
<el-table-column label="部门id" align="center" prop="deptId" />
<el-table-column label="用户id" align="center" prop="userId" />
<el-table-column label="排序号" align="center" prop="orderNum" />
<el-table-column label="key键" align="center" prop="testKey" />
<el-table-column label="值" align="center" prop="value" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template #default="scope">
<el-tooltip content="修改" placement="top">
<el-button v-hasPermi="['demo:demo:edit']" link type="primary" icon="Edit" @click="handleUpdate(scope.row)"></el-button>
</el-tooltip>
<el-tooltip content="删除" placement="top">
<el-button v-hasPermi="['demo:demo:remove']" link type="primary" icon="Delete" @click="handleDelete(scope.row)"></el-button>
</el-tooltip>
</template>
</el-table-column>
</el-table>
<pagination v-show="total > 0" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" :total="total" @pagination="getList" />
</el-card>
<!-- 添加或修改测试单对话框 -->
<el-dialog v-model="dialog.visible" :title="dialog.title" width="500px" append-to-body>
<el-form ref="demoFormRef" :model="form" :rules="rules" label-width="80px">
<el-form-item label="部门id" prop="deptId">
<el-input v-model="form.deptId" placeholder="请输入部门id" />
</el-form-item>
<el-form-item label="用户id" prop="userId">
<el-input v-model="form.userId" placeholder="请输入用户id" />
</el-form-item>
<el-form-item label="排序号" prop="orderNum">
<el-input v-model="form.orderNum" placeholder="请输入排序号" />
</el-form-item>
<el-form-item label="key键" prop="testKey">
<el-input v-model="form.testKey" placeholder="请输入key键" />
</el-form-item>
<el-form-item label="值" prop="value">
<el-input v-model="form.value" placeholder="请输入值" />
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button :loading="buttonLoading" type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup name="Demo" lang="ts">
import { listDemo, getDemo, delDemo, addDemo, updateDemo } from '@/api/demo/demo';
import { DemoVO, DemoQuery, DemoForm } from '@/api/demo/demo/types';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const demoList = ref<DemoVO[]>([]);
const buttonLoading = ref(false);
const loading = ref(true);
const showSearch = ref(true);
const ids = ref<Array<string | number>>([]);
const single = ref(true);
const multiple = ref(true);
const total = ref(0);
const queryFormRef = ref<ElFormInstance>();
const demoFormRef = ref<ElFormInstance>();
const dialog = reactive<DialogOption>({
visible: false,
title: ''
});
const initFormData: DemoForm = {
id: undefined,
deptId: undefined,
userId: undefined,
orderNum: undefined,
testKey: undefined,
value: undefined
};
const data = reactive<PageData<DemoForm, DemoQuery>>({
form: { ...initFormData },
queryParams: {
pageNum: 1,
pageSize: 10,
deptId: undefined,
userId: undefined,
orderNum: undefined,
testKey: undefined,
value: undefined
},
rules: {
id: [{ required: true, message: '主键不能为空', trigger: 'blur' }],
deptId: [{ required: true, message: '部门id不能为空', trigger: 'blur' }],
userId: [{ required: true, message: '用户id不能为空', trigger: 'blur' }],
orderNum: [{ required: true, message: '排序号不能为空', trigger: 'blur' }],
testKey: [{ required: true, message: 'key键不能为空', trigger: 'blur' }],
value: [{ required: true, message: '值不能为空', trigger: 'blur' }]
}
});
const { queryParams, form, rules } = toRefs(data);
/** 查询测试单列表 */
const getList = async () => {
loading.value = true;
const res = await listDemo(queryParams.value);
demoList.value = res.rows;
total.value = res.total;
loading.value = false;
};
/** 取消按钮 */
const cancel = () => {
reset();
dialog.visible = false;
};
/** 表单重置 */
const reset = () => {
form.value = { ...initFormData };
demoFormRef.value?.resetFields();
};
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.value.pageNum = 1;
getList();
};
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value?.resetFields();
handleQuery();
};
/** 多选框选中数据 */
const handleSelectionChange = (selection: DemoVO[]) => {
ids.value = selection.map((item) => item.id);
single.value = selection.length != 1;
multiple.value = !selection.length;
};
/** 新增按钮操作 */
const handleAdd = () => {
reset();
dialog.visible = true;
dialog.title = '添加测试单';
};
/** 修改按钮操作 */
const handleUpdate = async (row?: DemoVO) => {
reset();
const _id = row?.id || ids.value[0];
const res = await getDemo(_id);
Object.assign(form.value, res.data);
dialog.visible = true;
dialog.title = '修改测试单';
};
/** 提交按钮 */
const submitForm = () => {
demoFormRef.value?.validate(async (valid: boolean) => {
if (valid) {
buttonLoading.value = true;
if (form.value.id) {
await updateDemo(form.value).finally(() => (buttonLoading.value = false));
} else {
await addDemo(form.value).finally(() => (buttonLoading.value = false));
}
proxy?.$modal.msgSuccess('修改成功');
dialog.visible = false;
await getList();
}
});
};
/** 删除按钮操作 */
const handleDelete = async (row?: DemoVO) => {
const _ids = row?.id || ids.value;
await proxy?.$modal.confirm('是否确认删除测试单编号为"' + _ids + '"的数据项?').finally(() => (loading.value = false));
await delDemo(_ids);
proxy?.$modal.msgSuccess('删除成功');
await getList();
};
/** 导出按钮操作 */
const handleExport = () => {
proxy?.download(
'demo/demo/export',
{
...queryParams.value
},
`demo_${new Date().getTime()}.xlsx`
);
};
onMounted(() => {
getList();
});
</script>

View File

@ -1,258 +0,0 @@
<template>
<div class="p-2">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover">
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
<el-form-item label="树节点名" prop="treeName">
<el-input v-model="queryParams.treeName" placeholder="请输入树节点名" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
</el-card>
</div>
</transition>
<el-card shadow="hover">
<template #header>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button v-hasPermi="['demo:tree:add']" type="primary" plain icon="Plus" @click="handleAdd()">新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="info" plain icon="Sort" @click="handleToggleExpandAll">展开/折叠</el-button>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @query-table="getList"></right-toolbar>
</el-row>
</template>
<el-table
ref="treeTableRef"
v-loading="loading"
:data="treeList"
row-key="id"
:default-expand-all="isExpandAll"
:tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
>
<el-table-column label="父id" align="center" prop="parentId" />
<el-table-column label="部门id" align="center" prop="deptId" />
<el-table-column label="用户id" align="center" prop="userId" />
<el-table-column label="树节点名" align="center" prop="treeName" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template #default="scope">
<el-tooltip content="修改" placement="top">
<el-button v-hasPermi="['demo:tree:edit']" link type="primary" icon="Edit" @click="handleUpdate(scope.row)" />
</el-tooltip>
<el-tooltip content="新增" placement="top">
<el-button v-hasPermi="['demo:tree:add']" link type="primary" icon="Plus" @click="handleAdd(scope.row)" />
</el-tooltip>
<el-tooltip content="删除" placement="top">
<el-button v-hasPermi="['demo:tree:remove']" link type="primary" icon="Delete" @click="handleDelete(scope.row)" />
</el-tooltip>
</template>
</el-table-column>
</el-table>
</el-card>
<!-- 添加或修改测试树对话框 -->
<el-dialog v-model="dialog.visible" :title="dialog.title" width="500px" append-to-body>
<el-form ref="treeFormRef" :model="form" :rules="rules" label-width="80px">
<el-form-item label="父id" prop="parentId">
<el-tree-select
v-model="form.parentId"
:data="treeOptions"
:props="{ value: 'id', label: 'treeName', children: 'children' }"
value-key="id"
placeholder="请选择父id"
check-strictly
/>
</el-form-item>
<el-form-item label="部门id" prop="deptId">
<el-input v-model="form.deptId" placeholder="请输入部门id" />
</el-form-item>
<el-form-item label="用户id" prop="userId">
<el-input v-model="form.userId" placeholder="请输入用户id" />
</el-form-item>
<el-form-item label="值" prop="treeName">
<el-input v-model="form.treeName" placeholder="请输入值" />
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button :loading="buttonLoading" type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup name="Tree" lang="ts">
import { listTree, getTree, delTree, addTree, updateTree } from '@/api/demo/tree';
import { TreeVO, TreeQuery, TreeForm } from '@/api/demo/tree/types';
type TreeOption = {
id: number;
treeName: string;
children?: TreeOption[];
};
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const treeList = ref<TreeVO[]>([]);
const treeOptions = ref<TreeOption[]>([]);
const buttonLoading = ref(false);
const showSearch = ref(true);
const isExpandAll = ref(true);
const loading = ref(false);
const queryFormRef = ref<ElFormInstance>();
const treeFormRef = ref<ElFormInstance>();
const treeTableRef = ref<ElTableInstance>();
const dialog = reactive<DialogOption>({
visible: false,
title: ''
});
const initFormData: TreeForm = {
id: undefined,
parentId: undefined,
deptId: undefined,
userId: undefined,
treeName: undefined
};
const data = reactive<PageData<TreeForm, TreeQuery>>({
form: { ...initFormData },
queryParams: {
parentId: undefined,
deptId: undefined,
userId: undefined,
treeName: undefined
},
rules: {
id: [{ required: true, message: '主键不能为空', trigger: 'blur' }],
parentId: [{ required: true, message: '父id不能为空', trigger: 'blur' }],
deptId: [{ required: true, message: '部门id不能为空', trigger: 'blur' }],
userId: [{ required: true, message: '用户id不能为空', trigger: 'blur' }],
treeName: [{ required: true, message: '值不能为空', trigger: 'blur' }]
}
});
const { queryParams, form, rules } = toRefs(data);
/** 查询测试树列表 */
const getList = async () => {
loading.value = true;
const res = await listTree(queryParams.value);
const data = proxy?.handleTree<TreeVO>(res.data, 'id', 'parentId');
if (data) {
treeList.value = data;
loading.value = false;
}
};
/** 查询测试树下拉树结构 */
const getTreeselect = async () => {
const res = await listTree();
treeOptions.value = [];
const data: TreeOption = { id: 0, treeName: '顶级节点', children: [] };
data.children = proxy?.handleTree<TreeOption>(res.data, 'id', 'parentId');
treeOptions.value.push(data);
};
//
const cancel = () => {
reset();
dialog.visible = false;
};
//
const reset = () => {
form.value = { ...initFormData };
treeFormRef.value?.resetFields();
};
/** 搜索按钮操作 */
const handleQuery = () => {
getList();
};
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value?.resetFields();
handleQuery();
};
/** 新增按钮操作 */
const handleAdd = (row?: TreeVO) => {
reset();
getTreeselect();
if (row && row.id) {
form.value.parentId = row.id;
} else {
form.value.parentId = 0;
}
dialog.visible = true;
dialog.title = '添加测试树';
};
/** 展开/折叠操作 */
const handleToggleExpandAll = () => {
isExpandAll.value = !isExpandAll.value;
toggleExpandAll(treeList.value, isExpandAll.value);
};
/** 展开/折叠操作 */
const toggleExpandAll = (data: TreeVO[], status: boolean) => {
data.forEach((item) => {
treeTableRef.value?.toggleRowExpansion(item, status);
if (item.children && item.children.length > 0) toggleExpandAll(item.children, status);
});
};
/** 修改按钮操作 */
const handleUpdate = async (row: TreeVO) => {
reset();
await getTreeselect();
if (row) {
form.value.parentId = row.id;
}
const res = await getTree(row.id);
Object.assign(form.value, res.data);
dialog.visible = true;
dialog.title = '修改测试树';
};
/** 提交按钮 */
const submitForm = () => {
treeFormRef.value?.validate(async (valid: boolean) => {
if (valid) {
buttonLoading.value = true;
if (form.value.id) {
await updateTree(form.value).finally(() => (buttonLoading.value = false));
} else {
await addTree(form.value).finally(() => (buttonLoading.value = false));
}
proxy?.$modal.msgSuccess('操作成功');
dialog.visible = false;
await getList();
}
});
};
/** 删除按钮操作 */
const handleDelete = async (row: TreeVO) => {
await proxy?.$modal.confirm('是否确认删除测试树编号为"' + row.id + '"的数据项?');
loading.value = true;
await delTree(row.id).finally(() => (loading.value = false));
await getList();
proxy?.$modal.msgSuccess('删除成功');
};
onMounted(() => {
getList();
});
</script>

View File

@ -1,165 +1,4 @@
<template>
<div class="app-container home">
<el-row :gutter="20">
<el-col :sm="24" :lg="12" style="padding-left: 20px">
<h2>RuoYi-Vue-Plus多租户管理系统</h2>
<p>
RuoYi-Vue-Plus 是基于 RuoYi-Vue 针对 分布式集群 场景升级(不兼容原框架)
<br />
* 前端开发框架 Vue3TSElement Plus<br />
* 后端开发框架 Spring Boot<br />
* 容器框架 Undertow 基于 Netty 的高性能容器<br />
* 权限认证框架 Sa-Token 支持多终端认证系统<br />
* 关系数据库 MySQL 适配 8.X 最低 5.7<br />
* 缓存数据库 Redis 适配 6.X 最低 4.X<br />
* 数据库框架 Mybatis-Plus 快速 CRUD 增加开发效率<br />
* 数据库框架 p6spy 更强劲的 SQL 分析<br />
* 多数据源框架 dynamic-datasource 支持主从与多种类数据库异构<br />
* 序列化框架 Jackson 统一使用 jackson 高效可靠<br />
* Redis客户端 Redisson 性能强劲API丰富<br />
* 分布式限流 Redisson 全局请求IP集群ID 多种限流<br />
* 分布式锁 Lock4j 注解锁工具锁 多种多样<br />
* 分布式幂等 Lock4j 基于分布式锁实现<br />
* 分布式链路追踪 SkyWalking 支持链路追踪网格分析度量聚合可视化<br />
* 分布式任务调度 SnailJob 高性能 高可靠 易扩展<br />
* 文件存储 Minio 本地存储<br />
* 文件存储 七牛阿里腾讯 云存储<br />
* 监控框架 SpringBoot-Admin 全方位服务监控<br />
* 校验框架 Validation 增强接口安全性 严谨性<br />
* Excel框架 Alibaba EasyExcel 性能优异 扩展性强<br />
* 文档框架 SpringDocjavadoc 无注解零入侵基于java注释<br />
* 工具类框架 HutoolLombok 减少代码冗余 增加安全性<br />
* 代码生成器 适配MPSpringDoc规范化代码 一键生成前后端代码<br />
* 部署方式 Docker 容器编排 一键部署业务集群<br />
* 国际化 SpringMessage Spring标准国际化方案<br />
</p>
<p><b>当前版本:</b> <span>v5.2.3</span></p>
<p>
<el-tag type="danger">&yen;免费开源</el-tag>
</p>
<p>
<el-button type="primary" icon="Cloudy" plain @click="goTarget('https://gitee.com/dromara/RuoYi-Vue-Plus')">访问码云</el-button>
<el-button type="primary" icon="Cloudy" plain @click="goTarget('https://github.com/dromara/RuoYi-Vue-Plus')">访问GitHub</el-button>
<el-button type="primary" icon="Cloudy" plain @click="goTarget('https://plus-doc.dromara.org/#/ruoyi-vue-plus/changlog')"
>更新日志</el-button
>
</p>
</el-col>
<el-col :sm="24" :lg="12" style="padding-left: 20px">
<h2>RuoYi-Cloud-Plus多租户微服务管理系统</h2>
<p>
RuoYi-Cloud-Plus 微服务通用权限管理系统 重写 RuoYi-Cloud 全方位升级(不兼容原框架)
<br />
* 前端开发框架 Vue3TSElement UI<br />
* 后端开发框架 Spring Boot<br />
* 微服务开发框架 Spring CloudSpring Cloud Alibaba<br />
* 容器框架 Undertow 基于 XNIO 的高性能容器<br />
* 权限认证框架 Sa-TokenJwt 支持多终端认证系统<br />
* 关系数据库 MySQL 适配 8.X 最低 5.7<br />
* 关系数据库 Oracle 适配 11g 12c<br />
* 关系数据库 PostgreSQL 适配 13 14<br />
* 关系数据库 SQLServer 适配 2017 2019<br />
* 缓存数据库 Redis 适配 6.X 最低 5.X<br />
* 分布式注册中心 Alibaba Nacos 采用2.X 基于GRPC通信高性能<br />
* 分布式配置中心 Alibaba Nacos 采用2.X 基于GRPC通信高性能<br />
* 服务网关 Spring Cloud Gateway 响应式高性能网关<br />
* 负载均衡 Spring Cloud Loadbalancer 负载均衡处理<br />
* RPC远程调用 Apache Dubbo 原生态使用体验高性能<br />
* 分布式限流熔断 Alibaba Sentinel 无侵入高扩展<br />
* 分布式事务 Alibaba Seata 无侵入高扩展 支持 四种模式<br />
* 分布式消息队列 Apache Kafka 高性能高速度<br />
* 分布式消息队列 Apache RocketMQ 高可用功能多样<br />
* 分布式消息队列 RabbitMQ 支持各种扩展插件功能多样性<br />
* 分布式搜索引擎 ElasticSearch 业界知名<br />
* 分布式链路追踪 Apache SkyWalking 链路追踪网格分析度量聚合可视化<br />
* 分布式日志中心 ELK 业界成熟解决方案<br />
* 分布式监控 PrometheusGrafana 全方位性能监控<br />
* 其余与 Vue 版本一致<br />
</p>
<p><b>当前版本:</b> <span>v2.2.2</span></p>
<p>
<el-tag type="danger">&yen;免费开源</el-tag>
</p>
<p>
<el-button type="primary" icon="Cloudy" plain @click="goTarget('https://gitee.com/dromara/RuoYi-Cloud-Plus')">访问码云</el-button>
<el-button type="primary" icon="Cloudy" plain @click="goTarget('https://github.com/dromara/RuoYi-Cloud-Plus')">访问GitHub</el-button>
<el-button type="primary" icon="Cloudy" plain @click="goTarget('https://plus-doc.dromara.org/#/ruoyi-cloud-plus/changlog')"
>更新日志</el-button
>
</p>
</el-col>
</el-row>
<el-divider />
</div>
</template>
<script setup name="Index" lang="ts">
const goTarget = (url: string) => {
window.open(url, '__blank');
};
</script>
<style scoped lang="scss">
.home {
blockquote {
padding: 10px 20px;
margin: 0 0 20px;
font-size: 17.5px;
border-left: 5px solid #eee;
}
hr {
margin-top: 20px;
margin-bottom: 20px;
border: 0;
border-top: 1px solid #eee;
}
.col-item {
margin-bottom: 20px;
}
ul {
padding: 0;
margin: 0;
}
font-family: 'open sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;
font-size: 13px;
color: #676a6c;
overflow-x: hidden;
ul {
list-style-type: none;
}
h4 {
margin-top: 0px;
}
h2 {
margin-top: 10px;
font-size: 26px;
font-weight: 100;
}
p {
margin-top: 10px;
b {
font-weight: 700;
}
}
.update-log {
ol {
display: block;
list-style-type: decimal;
margin-block-start: 1em;
margin-block-end: 1em;
margin-inline-start: 0;
margin-inline-end: 0;
padding-inline-start: 40px;
}
}
}
</style>

View File

@ -0,0 +1,59 @@
services:
# 此镜像仅用于测试 正式环境需自行安装数据库
# SID: XE user: system password: oracle
oracle:
image: tekintian/oracle12c:latest
container_name: oracle
environment:
# 时区上海
TZ: Asia/Shanghai
DBCA_TOTAL_MEMORY: 16192
ports:
- "18080:8080"
- "1521:1521"
volumes:
# 数据挂载
- "/docker/oracle/data:/u01/app/oracle"
network_mode: "host"
# 此镜像仅用于测试 正式环境需自行安装数据库
sqlserver:
image: mcr.microsoft.com/mssql/server:2017-latest
container_name: sqlserver
environment:
# 时区上海
TZ: Asia/Shanghai
ACCEPT_EULA: "Y"
SA_PASSWORD: "Ruoyi@123"
ports:
- "1433:1433"
volumes:
# 数据挂载
- "/docker/sqlserver/data:/var/opt/mssql"
network_mode: "host"
postgres:
image: postgres:14.2
container_name: postgres
environment:
POSTGRES_USER: root
POSTGRES_PASSWORD: root
POSTGRES_DB: postgres
ports:
- "5432:5432"
volumes:
- /docker/postgres/data:/var/lib/postgresql/data
network_mode: "host"
postgres13:
image: postgres:13.6
container_name: postgres13
environment:
POSTGRES_USER: root
POSTGRES_PASSWORD: root
POSTGRES_DB: postgres
ports:
- "5433:5432"
volumes:
- /docker/postgres13/data:/var/lib/postgresql/data
network_mode: "host"

View File

@ -0,0 +1,157 @@
services:
mysql:
image: mysql:8.0.42
container_name: mysql
environment:
# 时区上海
TZ: Asia/Shanghai
# root 密码
MYSQL_ROOT_PASSWORD: root
# 初始化数据库(后续的初始化sql会在这个库执行)
MYSQL_DATABASE: ry-vue
ports:
- "3306:3306"
volumes:
# 数据挂载
- /docker/mysql/data/:/var/lib/mysql/
# 配置挂载
- /docker/mysql/conf/:/etc/mysql/conf.d/
command:
# 将mysql8.0默认密码策略 修改为 原先 策略 (mysql8.0对其默认策略做了更改 会导致密码无法匹配)
--default-authentication-plugin=mysql_native_password
--character-set-server=utf8mb4
--collation-server=utf8mb4_general_ci
--explicit_defaults_for_timestamp=true
--lower_case_table_names=1
privileged: true
network_mode: "host"
nginx-web:
image: nginx:1.23.4
container_name: nginx-web
environment:
# 时区上海
TZ: Asia/Shanghai
ports:
- "80:80"
- "443:443"
volumes:
# 证书映射
- /docker/nginx/cert:/etc/nginx/cert
# 配置文件映射
- /docker/nginx/conf/nginx.conf:/etc/nginx/nginx.conf
# 页面目录
- /docker/nginx/html:/usr/share/nginx/html
# 日志目录
- /docker/nginx/log:/var/log/nginx
privileged: true
network_mode: "host"
redis:
image: redis:7.2.8
container_name: redis
ports:
- "6379:6379"
environment:
# 时区上海
TZ: Asia/Shanghai
volumes:
# 配置文件
- /docker/redis/conf:/redis/config:rw
# 数据文件
- /docker/redis/data/:/redis/data/:rw
command: "redis-server /redis/config/redis.conf"
privileged: true
network_mode: "host"
minio:
# minio 最后一个未阉割版本 不能再进行升级 在往上的版本功能被阉割
image: minio/minio:RELEASE.2025-04-22T22-12-26Z
container_name: minio
ports:
# api 端口
- "9000:9000"
# 控制台端口
- "9001:9001"
environment:
# 时区上海
TZ: Asia/Shanghai
# 管理后台用户名
MINIO_ROOT_USER: ruoyi
# 管理后台密码最小8个字符
MINIO_ROOT_PASSWORD: ruoyi123
# https需要指定域名
#MINIO_SERVER_URL: "https://xxx.com:9000"
#MINIO_BROWSER_REDIRECT_URL: "https://xxx.com:9001"
# 开启压缩 on 开启 off 关闭
MINIO_COMPRESS: "off"
# 扩展名 .pdf,.doc 为空 所有类型均压缩
MINIO_COMPRESS_EXTENSIONS: ""
# mime 类型 application/pdf 为空 所有类型均压缩
MINIO_COMPRESS_MIME_TYPES: ""
volumes:
# 映射当前目录下的data目录至容器内/data目录
- /docker/minio/data:/data
# 映射配置目录
- /docker/minio/config:/root/.minio/
command: server --address ':9000' --console-address ':9001' /data # 指定容器中的目录 /data
privileged: true
network_mode: "host"
ruoyi-server1:
image: ruoyi/ruoyi-server:5.4.0
container_name: ruoyi-server1
environment:
# 时区上海
TZ: Asia/Shanghai
SERVER_PORT: 8080
SNAIL_PORT: 28080
volumes:
# 配置文件
- /docker/server1/logs/:/ruoyi/server/logs/
# skywalking 探针
# - /docker/skywalking/agent/:/ruoyi/skywalking/agent
privileged: true
network_mode: "host"
ruoyi-server2:
image: ruoyi/ruoyi-server:5.4.0
container_name: ruoyi-server2
environment:
# 时区上海
TZ: Asia/Shanghai
SERVER_PORT: 8081
SNAIL_PORT: 28081
volumes:
# 配置文件
- /docker/server2/logs/:/ruoyi/server/logs/
# skywalking 探针
# - /docker/skywalking/agent/:/ruoyi/skywalking/agent
privileged: true
network_mode: "host"
ruoyi-monitor-admin:
image: ruoyi/ruoyi-monitor-admin:5.4.0
container_name: ruoyi-monitor-admin
environment:
# 时区上海
TZ: Asia/Shanghai
volumes:
# 配置文件
- /docker/monitor/logs/:/ruoyi/monitor/logs
privileged: true
network_mode: "host"
ruoyi-snailjob-server:
image: ruoyi/ruoyi-snailjob-server:5.4.0
container_name: ruoyi-snailjob-server
environment:
# 时区上海
TZ: Asia/Shanghai
ports:
- "8800:8800"
- "17888:17888"
volumes:
- /docker/snailjob/logs/:/ruoyi/snailjob/logs
privileged: true
network_mode: "host"

View File

@ -0,0 +1,123 @@
worker_processes 1;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
# 限制body大小
client_max_body_size 100m;
# 开启静态资源压缩
gzip_static on;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
upstream server {
ip_hash;
server 127.0.0.1:8080;
server 127.0.0.1:8081;
}
upstream monitor-admin {
server 127.0.0.1:9090;
}
upstream snailjob-server {
server 127.0.0.1:8800;
}
server {
listen 80;
server_name localhost;
# https配置参考 start
#listen 443 ssl;
# 证书直接存放 /docker/nginx/cert/ 目录下即可 更改证书名称即可 无需更改证书路径
#ssl on;
#ssl_certificate /etc/nginx/cert/xxx.local.crt; # /etc/nginx/cert/ 为docker映射路径 不允许更改
#ssl_certificate_key /etc/nginx/cert/xxx.local.key; # /etc/nginx/cert/ 为docker映射路径 不允许更改
#ssl_session_timeout 5m;
#ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
#ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
#ssl_prefer_server_ciphers on;
# https配置参考 end
# 演示环境配置 拦截除 GET POST 之外的所有请求
# if ($request_method !~* GET|POST) {
# rewrite ^/(.*)$ /403;
# }
# location = /403 {
# default_type application/json;
# return 200 '{"msg":"演示模式,不允许操作","code":500}';
# }
# 限制外网访问内网 actuator 相关路径
location ~ ^(/[^/]*)?/actuator.*(/.*)?$ {
return 403;
}
location / {
root /usr/share/nginx/html; # docker映射路径 不允许更改
try_files $uri $uri/ /index.html;
index index.html index.htm;
}
location /prod-api/ {
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header REMOTE-HOST $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_read_timeout 86400s;
# sse websocket参数
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_buffering off;
proxy_cache off;
proxy_pass http://server/;
}
# https 会拦截内链所有的 http 请求 造成功能无法使用
# 解决方案1 admin 服务 也配置成 https
# 解决方案2 将菜单配置为外链访问 走独立页面 http 访问
location /admin/ {
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header REMOTE-HOST $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://monitor-admin/admin/;
}
location /snail-job/ {
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header REMOTE-HOST $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# sse websocket参数
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_buffering off;
proxy_cache off;
proxy_pass http://snailjob-server/snail-job/;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
}

View File

@ -0,0 +1,28 @@
# redis 密码
requirepass ruoyi123
# key 监听器配置
# notify-keyspace-events Ex
# 配置持久化文件存储路径
dir /redis/data
# 配置rdb
# 15分钟内有至少1个key被更改则进行快照
save 900 1
# 5分钟内有至少10个key被更改则进行快照
save 300 10
# 1分钟内有至少10000个key被更改则进行快照
save 60 10000
# 开启压缩
rdbcompression yes
# rdb文件名 用默认的即可
dbfilename dump.rdb
# 开启aof
appendonly yes
# 文件名
appendfilename "appendonly.aof"
# 持久化策略,no:不同步,everysec:每秒一次,always:总是同步,速度比较慢
# appendfsync always
appendfsync everysec
# appendfsync no

View File

@ -0,0 +1 @@
数据目录 请执行 `chmod 777 /docker/redis/data` 赋予读写权限 否则将无法写入数据

836
script/sql/boot-base.sql Normal file
View File

@ -0,0 +1,836 @@
/*
Navicat Premium Dump SQL
Source Server :
Source Server Type : MySQL
Source Server Version : 80042 (8.0.42)
Source Host : localhost:3306
Source Schema : boot-base
Target Server Type : MySQL
Target Server Version : 80042 (8.0.42)
File Encoding : 65001
Date: 10/06/2025 16:34:52
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for gen_table
-- ----------------------------
DROP TABLE IF EXISTS `gen_table`;
CREATE TABLE `gen_table` (
`table_id` bigint NOT NULL COMMENT '编号',
`data_name` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '数据源名称',
`table_name` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '表名称',
`table_comment` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '表描述',
`sub_table_name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '关联子表的表名',
`sub_table_fk_name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '子表关联的外键名',
`class_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '实体类名称',
`tpl_category` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT 'crud' COMMENT '使用的模板crud单表操作 tree树表操作',
`package_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '生成包路径',
`module_name` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '生成模块名',
`business_name` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '生成业务名',
`function_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '生成功能名',
`function_author` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '生成功能作者',
`gen_type` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '0' COMMENT '生成代码方式0zip压缩包 1自定义路径',
`gen_path` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '/' COMMENT '生成路径(不填默认项目路径)',
`options` varchar(1000) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '其它生成选项',
`create_dept` bigint NULL DEFAULT NULL COMMENT '创建部门',
`create_by` bigint NULL DEFAULT NULL COMMENT '创建者',
`create_time` datetime NULL DEFAULT NULL COMMENT '创建时间',
`update_by` bigint NULL DEFAULT NULL COMMENT '更新者',
`update_time` datetime NULL DEFAULT NULL COMMENT '更新时间',
`remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '备注',
PRIMARY KEY (`table_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '代码生成业务表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of gen_table
-- ----------------------------
-- ----------------------------
-- Table structure for gen_table_column
-- ----------------------------
DROP TABLE IF EXISTS `gen_table_column`;
CREATE TABLE `gen_table_column` (
`column_id` bigint NOT NULL COMMENT '编号',
`table_id` bigint NULL DEFAULT NULL COMMENT '归属表编号',
`column_name` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '列名称',
`column_comment` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '列描述',
`column_type` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '列类型',
`java_type` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT 'JAVA类型',
`java_field` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT 'JAVA字段名',
`is_pk` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '是否主键1是',
`is_increment` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '是否自增1是',
`is_required` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '是否必填1是',
`is_insert` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '是否为插入字段1是',
`is_edit` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '是否编辑字段1是',
`is_list` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '是否列表字段1是',
`is_query` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '是否查询字段1是',
`query_type` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT 'EQ' COMMENT '查询方式(等于、不等于、大于、小于、范围)',
`html_type` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '显示类型(文本框、文本域、下拉框、复选框、单选框、日期控件)',
`dict_type` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '字典类型',
`sort` int NULL DEFAULT NULL COMMENT '排序',
`create_dept` bigint NULL DEFAULT NULL COMMENT '创建部门',
`create_by` bigint NULL DEFAULT NULL COMMENT '创建者',
`create_time` datetime NULL DEFAULT NULL COMMENT '创建时间',
`update_by` bigint NULL DEFAULT NULL COMMENT '更新者',
`update_time` datetime NULL DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`column_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '代码生成业务表字段' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of gen_table_column
-- ----------------------------
-- ----------------------------
-- Table structure for sys_client
-- ----------------------------
DROP TABLE IF EXISTS `sys_client`;
CREATE TABLE `sys_client` (
`id` bigint NOT NULL COMMENT 'id',
`client_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '客户端id',
`client_key` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '客户端key',
`client_secret` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '客户端秘钥',
`grant_type` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '授权类型',
`device_type` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '设备类型',
`active_timeout` int NULL DEFAULT 1800 COMMENT 'token活跃超时时间',
`timeout` int NULL DEFAULT 604800 COMMENT 'token固定超时',
`status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '0' COMMENT '状态0正常 1停用',
`del_flag` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '0' COMMENT '删除标志0代表存在 1代表删除',
`create_dept` bigint NULL DEFAULT NULL COMMENT '创建部门',
`create_by` bigint NULL DEFAULT NULL COMMENT '创建者',
`create_time` datetime NULL DEFAULT NULL COMMENT '创建时间',
`update_by` bigint NULL DEFAULT NULL COMMENT '更新者',
`update_time` datetime NULL DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '系统授权表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of sys_client
-- ----------------------------
INSERT INTO `sys_client` VALUES (1, 'e5cd7e4891bf95d1d19206ce24a7b32e', 'pc', 'pc123', 'password,social', 'pc', 1800, 604800, '0', '0', 103, 1, '2025-06-10 12:10:29', 1, '2025-06-10 12:10:29');
INSERT INTO `sys_client` VALUES (2, '428a8310cd442757ae699df5d894f051', 'app', 'app123', 'password,sms,social', 'android', 1800, 604800, '0', '0', 103, 1, '2025-06-10 12:10:29', 1, '2025-06-10 12:10:29');
-- ----------------------------
-- Table structure for sys_config
-- ----------------------------
DROP TABLE IF EXISTS `sys_config`;
CREATE TABLE `sys_config` (
`config_id` bigint NOT NULL COMMENT '参数主键',
`config_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '参数名称',
`config_key` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '参数键名',
`config_value` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '参数键值',
`config_type` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT 'N' COMMENT '系统内置Y是 N否',
`create_dept` bigint NULL DEFAULT NULL COMMENT '创建部门',
`create_by` bigint NULL DEFAULT NULL COMMENT '创建者',
`create_time` datetime NULL DEFAULT NULL COMMENT '创建时间',
`update_by` bigint NULL DEFAULT NULL COMMENT '更新者',
`update_time` datetime NULL DEFAULT NULL COMMENT '更新时间',
`remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '备注',
PRIMARY KEY (`config_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '参数配置表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of sys_config
-- ----------------------------
INSERT INTO `sys_config` VALUES (1, '主框架页-默认皮肤样式名称', 'sys.index.skinName', 'skin-blue', 'Y', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '蓝色 skin-blue、绿色 skin-green、紫色 skin-purple、红色 skin-red、黄色 skin-yellow');
INSERT INTO `sys_config` VALUES (2, '用户管理-账号初始密码', 'sys.user.initPassword', '123456', 'Y', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '初始化密码 123456');
INSERT INTO `sys_config` VALUES (3, '主框架页-侧边栏主题', 'sys.index.sideTheme', 'theme-dark', 'Y', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '深色主题theme-dark浅色主题theme-light');
INSERT INTO `sys_config` VALUES (5, '账号自助-是否开启用户注册功能', 'sys.account.registerUser', 'false', 'Y', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '是否开启注册用户功能true开启false关闭');
INSERT INTO `sys_config` VALUES (11, 'OSS预览列表资源开关', 'sys.oss.previewListResource', 'true', 'Y', 103, 1, '2025-06-10 12:10:29', NULL, NULL, 'true:开启, false:关闭');
-- ----------------------------
-- Table structure for sys_dept
-- ----------------------------
DROP TABLE IF EXISTS `sys_dept`;
CREATE TABLE `sys_dept` (
`dept_id` bigint NOT NULL COMMENT '部门id',
`parent_id` bigint NULL DEFAULT 0 COMMENT '父部门id',
`ancestors` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '祖级列表',
`dept_name` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '部门名称',
`dept_category` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '部门类别编码',
`order_num` int NULL DEFAULT 0 COMMENT '显示顺序',
`leader` bigint NULL DEFAULT NULL COMMENT '负责人',
`phone` varchar(11) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '联系电话',
`email` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '邮箱',
`status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '0' COMMENT '部门状态0正常 1停用',
`del_flag` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '0' COMMENT '删除标志0代表存在 1代表删除',
`create_dept` bigint NULL DEFAULT NULL COMMENT '创建部门',
`create_by` bigint NULL DEFAULT NULL COMMENT '创建者',
`create_time` datetime NULL DEFAULT NULL COMMENT '创建时间',
`update_by` bigint NULL DEFAULT NULL COMMENT '更新者',
`update_time` datetime NULL DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`dept_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '部门表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of sys_dept
-- ----------------------------
INSERT INTO `sys_dept` VALUES (100, 0, '0', 'XXX科技', NULL, 0, NULL, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, '2025-06-10 12:10:28', NULL, NULL);
INSERT INTO `sys_dept` VALUES (101, 100, '0,100', '深圳总公司', NULL, 1, NULL, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, '2025-06-10 12:10:28', NULL, NULL);
INSERT INTO `sys_dept` VALUES (102, 100, '0,100', '长沙分公司', NULL, 2, NULL, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, '2025-06-10 12:10:28', NULL, NULL);
INSERT INTO `sys_dept` VALUES (103, 101, '0,100,101', '研发部门', NULL, 1, 1, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, '2025-06-10 12:10:28', NULL, NULL);
INSERT INTO `sys_dept` VALUES (104, 101, '0,100,101', '市场部门', NULL, 2, NULL, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, '2025-06-10 12:10:28', NULL, NULL);
INSERT INTO `sys_dept` VALUES (105, 101, '0,100,101', '测试部门', NULL, 3, NULL, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, '2025-06-10 12:10:28', NULL, NULL);
INSERT INTO `sys_dept` VALUES (106, 101, '0,100,101', '财务部门', NULL, 4, NULL, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, '2025-06-10 12:10:28', NULL, NULL);
INSERT INTO `sys_dept` VALUES (107, 101, '0,100,101', '运维部门', NULL, 5, NULL, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, '2025-06-10 12:10:28', NULL, NULL);
INSERT INTO `sys_dept` VALUES (108, 102, '0,100,102', '市场部门', NULL, 1, NULL, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, '2025-06-10 12:10:28', NULL, NULL);
INSERT INTO `sys_dept` VALUES (109, 102, '0,100,102', '财务部门', NULL, 2, NULL, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, '2025-06-10 12:10:28', NULL, NULL);
-- ----------------------------
-- Table structure for sys_dict_data
-- ----------------------------
DROP TABLE IF EXISTS `sys_dict_data`;
CREATE TABLE `sys_dict_data` (
`dict_code` bigint NOT NULL COMMENT '字典编码',
`dict_sort` int NULL DEFAULT 0 COMMENT '字典排序',
`dict_label` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '字典标签',
`dict_value` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '字典键值',
`dict_type` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '字典类型',
`css_class` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '样式属性(其他样式扩展)',
`list_class` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '表格回显样式',
`is_default` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT 'N' COMMENT '是否默认Y是 N否',
`create_dept` bigint NULL DEFAULT NULL COMMENT '创建部门',
`create_by` bigint NULL DEFAULT NULL COMMENT '创建者',
`create_time` datetime NULL DEFAULT NULL COMMENT '创建时间',
`update_by` bigint NULL DEFAULT NULL COMMENT '更新者',
`update_time` datetime NULL DEFAULT NULL COMMENT '更新时间',
`remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '备注',
PRIMARY KEY (`dict_code`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '字典数据表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of sys_dict_data
-- ----------------------------
INSERT INTO `sys_dict_data` VALUES (1, 1, '', '0', 'sys_user_sex', '', '', 'Y', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '性别男');
INSERT INTO `sys_dict_data` VALUES (2, 2, '', '1', 'sys_user_sex', '', '', 'N', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '性别女');
INSERT INTO `sys_dict_data` VALUES (3, 3, '未知', '2', 'sys_user_sex', '', '', 'N', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '性别未知');
INSERT INTO `sys_dict_data` VALUES (4, 1, '显示', '0', 'sys_show_hide', '', 'primary', 'Y', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '显示菜单');
INSERT INTO `sys_dict_data` VALUES (5, 2, '隐藏', '1', 'sys_show_hide', '', 'danger', 'N', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '隐藏菜单');
INSERT INTO `sys_dict_data` VALUES (6, 1, '正常', '0', 'sys_normal_disable', '', 'primary', 'Y', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '正常状态');
INSERT INTO `sys_dict_data` VALUES (7, 2, '停用', '1', 'sys_normal_disable', '', 'danger', 'N', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '停用状态');
INSERT INTO `sys_dict_data` VALUES (12, 1, '', 'Y', 'sys_yes_no', '', 'primary', 'Y', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '系统默认是');
INSERT INTO `sys_dict_data` VALUES (13, 2, '', 'N', 'sys_yes_no', '', 'danger', 'N', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '系统默认否');
INSERT INTO `sys_dict_data` VALUES (14, 1, '通知', '1', 'sys_notice_type', '', 'warning', 'Y', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '通知');
INSERT INTO `sys_dict_data` VALUES (15, 2, '公告', '2', 'sys_notice_type', '', 'success', 'N', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '公告');
INSERT INTO `sys_dict_data` VALUES (16, 1, '正常', '0', 'sys_notice_status', '', 'primary', 'Y', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '正常状态');
INSERT INTO `sys_dict_data` VALUES (17, 2, '关闭', '1', 'sys_notice_status', '', 'danger', 'N', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '关闭状态');
INSERT INTO `sys_dict_data` VALUES (18, 1, '新增', '1', 'sys_oper_type', '', 'info', 'N', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '新增操作');
INSERT INTO `sys_dict_data` VALUES (19, 2, '修改', '2', 'sys_oper_type', '', 'info', 'N', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '修改操作');
INSERT INTO `sys_dict_data` VALUES (20, 3, '删除', '3', 'sys_oper_type', '', 'danger', 'N', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '删除操作');
INSERT INTO `sys_dict_data` VALUES (21, 4, '授权', '4', 'sys_oper_type', '', 'primary', 'N', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '授权操作');
INSERT INTO `sys_dict_data` VALUES (22, 5, '导出', '5', 'sys_oper_type', '', 'warning', 'N', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '导出操作');
INSERT INTO `sys_dict_data` VALUES (23, 6, '导入', '6', 'sys_oper_type', '', 'warning', 'N', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '导入操作');
INSERT INTO `sys_dict_data` VALUES (24, 7, '强退', '7', 'sys_oper_type', '', 'danger', 'N', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '强退操作');
INSERT INTO `sys_dict_data` VALUES (25, 8, '生成代码', '8', 'sys_oper_type', '', 'warning', 'N', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '生成操作');
INSERT INTO `sys_dict_data` VALUES (26, 9, '清空数据', '9', 'sys_oper_type', '', 'danger', 'N', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '清空操作');
INSERT INTO `sys_dict_data` VALUES (27, 1, '成功', '0', 'sys_common_status', '', 'primary', 'N', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '正常状态');
INSERT INTO `sys_dict_data` VALUES (28, 2, '失败', '1', 'sys_common_status', '', 'danger', 'N', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '停用状态');
INSERT INTO `sys_dict_data` VALUES (29, 99, '其他', '0', 'sys_oper_type', '', 'info', 'N', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '其他操作');
INSERT INTO `sys_dict_data` VALUES (30, 0, '密码认证', 'password', 'sys_grant_type', 'el-check-tag', 'default', 'N', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '密码认证');
INSERT INTO `sys_dict_data` VALUES (31, 0, '短信认证', 'sms', 'sys_grant_type', 'el-check-tag', 'default', 'N', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '短信认证');
INSERT INTO `sys_dict_data` VALUES (32, 0, '邮件认证', 'email', 'sys_grant_type', 'el-check-tag', 'default', 'N', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '邮件认证');
INSERT INTO `sys_dict_data` VALUES (33, 0, '小程序认证', 'xcx', 'sys_grant_type', 'el-check-tag', 'default', 'N', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '小程序认证');
INSERT INTO `sys_dict_data` VALUES (34, 0, '三方登录认证', 'social', 'sys_grant_type', 'el-check-tag', 'default', 'N', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '三方登录认证');
INSERT INTO `sys_dict_data` VALUES (35, 0, 'PC', 'pc', 'sys_device_type', '', 'default', 'N', 103, 1, '2025-06-10 12:10:29', NULL, NULL, 'PC');
INSERT INTO `sys_dict_data` VALUES (36, 0, '安卓', 'android', 'sys_device_type', '', 'default', 'N', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '安卓');
INSERT INTO `sys_dict_data` VALUES (37, 0, 'iOS', 'ios', 'sys_device_type', '', 'default', 'N', 103, 1, '2025-06-10 12:10:29', NULL, NULL, 'iOS');
INSERT INTO `sys_dict_data` VALUES (38, 0, '小程序', 'xcx', 'sys_device_type', '', 'default', 'N', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '小程序');
-- ----------------------------
-- Table structure for sys_dict_type
-- ----------------------------
DROP TABLE IF EXISTS `sys_dict_type`;
CREATE TABLE `sys_dict_type` (
`dict_id` bigint NOT NULL COMMENT '字典主键',
`dict_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '字典名称',
`dict_type` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '字典类型',
`create_dept` bigint NULL DEFAULT NULL COMMENT '创建部门',
`create_by` bigint NULL DEFAULT NULL COMMENT '创建者',
`create_time` datetime NULL DEFAULT NULL COMMENT '创建时间',
`update_by` bigint NULL DEFAULT NULL COMMENT '更新者',
`update_time` datetime NULL DEFAULT NULL COMMENT '更新时间',
`remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '备注',
PRIMARY KEY (`dict_id`) USING BTREE,
UNIQUE INDEX `dict_type`(`dict_type` ASC) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '字典类型表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of sys_dict_type
-- ----------------------------
INSERT INTO `sys_dict_type` VALUES (1, '用户性别', 'sys_user_sex', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '用户性别列表');
INSERT INTO `sys_dict_type` VALUES (2, '菜单状态', 'sys_show_hide', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '菜单状态列表');
INSERT INTO `sys_dict_type` VALUES (3, '系统开关', 'sys_normal_disable', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '系统开关列表');
INSERT INTO `sys_dict_type` VALUES (6, '系统是否', 'sys_yes_no', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '系统是否列表');
INSERT INTO `sys_dict_type` VALUES (7, '通知类型', 'sys_notice_type', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '通知类型列表');
INSERT INTO `sys_dict_type` VALUES (8, '通知状态', 'sys_notice_status', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '通知状态列表');
INSERT INTO `sys_dict_type` VALUES (9, '操作类型', 'sys_oper_type', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '操作类型列表');
INSERT INTO `sys_dict_type` VALUES (10, '系统状态', 'sys_common_status', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '登录状态列表');
INSERT INTO `sys_dict_type` VALUES (11, '授权类型', 'sys_grant_type', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '认证授权类型');
INSERT INTO `sys_dict_type` VALUES (12, '设备类型', 'sys_device_type', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '客户端设备类型');
-- ----------------------------
-- Table structure for sys_logininfor
-- ----------------------------
DROP TABLE IF EXISTS `sys_logininfor`;
CREATE TABLE `sys_logininfor` (
`info_id` bigint NOT NULL COMMENT '访问ID',
`user_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '用户账号',
`client_key` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '客户端',
`device_type` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '设备类型',
`ipaddr` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '登录IP地址',
`login_location` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '登录地点',
`browser` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '浏览器类型',
`os` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '操作系统',
`status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '0' COMMENT '登录状态0成功 1失败',
`msg` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '提示消息',
`login_time` datetime NULL DEFAULT NULL COMMENT '访问时间',
PRIMARY KEY (`info_id`) USING BTREE,
INDEX `idx_sys_logininfor_s`(`status` ASC) USING BTREE,
INDEX `idx_sys_logininfor_lt`(`login_time` ASC) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '系统访问记录' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of sys_logininfor
-- ----------------------------
INSERT INTO `sys_logininfor` VALUES (1932353803513761793, 'admin', 'pc', 'pc', '0:0:0:0:0:0:0:1', '内网IP', 'Chrome', 'Windows 10 or Windows Server 2016', '0', '登录成功', '2025-06-10 16:27:03');
-- ----------------------------
-- Table structure for sys_menu
-- ----------------------------
DROP TABLE IF EXISTS `sys_menu`;
CREATE TABLE `sys_menu` (
`menu_id` bigint NOT NULL COMMENT '菜单ID',
`menu_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '菜单名称',
`parent_id` bigint NULL DEFAULT 0 COMMENT '父菜单ID',
`order_num` int NULL DEFAULT 0 COMMENT '显示顺序',
`path` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '路由地址',
`component` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '组件路径',
`query_param` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '路由参数',
`is_frame` int NULL DEFAULT 1 COMMENT '是否为外链0是 1否',
`is_cache` int NULL DEFAULT 0 COMMENT '是否缓存0缓存 1不缓存',
`menu_type` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '菜单类型M目录 C菜单 F按钮',
`visible` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '0' COMMENT '显示状态0显示 1隐藏',
`status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '0' COMMENT '菜单状态0正常 1停用',
`perms` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '权限标识',
`icon` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '#' COMMENT '菜单图标',
`create_dept` bigint NULL DEFAULT NULL COMMENT '创建部门',
`create_by` bigint NULL DEFAULT NULL COMMENT '创建者',
`create_time` datetime NULL DEFAULT NULL COMMENT '创建时间',
`update_by` bigint NULL DEFAULT NULL COMMENT '更新者',
`update_time` datetime NULL DEFAULT NULL COMMENT '更新时间',
`remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '备注',
PRIMARY KEY (`menu_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '菜单权限表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of sys_menu
-- ----------------------------
INSERT INTO `sys_menu` VALUES (1, '系统管理', 0, 1, 'system', NULL, '', 1, 0, 'M', '0', '0', '', 'system', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '系统管理目录');
INSERT INTO `sys_menu` VALUES (2, '系统监控', 0, 3, 'monitor', NULL, '', 1, 0, 'M', '0', '0', '', 'monitor', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '系统监控目录');
INSERT INTO `sys_menu` VALUES (3, '系统工具', 0, 4, 'tool', NULL, '', 1, 0, 'M', '0', '0', '', 'tool', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '系统工具目录');
INSERT INTO `sys_menu` VALUES (100, '用户管理', 1, 1, 'user', 'system/user/index', '', 1, 0, 'C', '0', '0', 'system:user:list', 'user', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '用户管理菜单');
INSERT INTO `sys_menu` VALUES (101, '角色管理', 1, 2, 'role', 'system/role/index', '', 1, 0, 'C', '0', '0', 'system:role:list', 'peoples', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '角色管理菜单');
INSERT INTO `sys_menu` VALUES (102, '菜单管理', 1, 3, 'menu', 'system/menu/index', '', 1, 0, 'C', '0', '0', 'system:menu:list', 'tree-table', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '菜单管理菜单');
INSERT INTO `sys_menu` VALUES (103, '部门管理', 1, 4, 'dept', 'system/dept/index', '', 1, 0, 'C', '0', '0', 'system:dept:list', 'tree', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '部门管理菜单');
INSERT INTO `sys_menu` VALUES (104, '岗位管理', 1, 5, 'post', 'system/post/index', '', 1, 0, 'C', '0', '0', 'system:post:list', 'post', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '岗位管理菜单');
INSERT INTO `sys_menu` VALUES (105, '字典管理', 1, 6, 'dict', 'system/dict/index', '', 1, 0, 'C', '0', '0', 'system:dict:list', 'dict', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '字典管理菜单');
INSERT INTO `sys_menu` VALUES (106, '参数设置', 1, 7, 'config', 'system/config/index', '', 1, 0, 'C', '0', '0', 'system:config:list', 'edit', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '参数设置菜单');
INSERT INTO `sys_menu` VALUES (107, '通知公告', 1, 8, 'notice', 'system/notice/index', '', 1, 0, 'C', '0', '0', 'system:notice:list', 'message', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '通知公告菜单');
INSERT INTO `sys_menu` VALUES (108, '日志管理', 1, 9, 'log', '', '', 1, 0, 'M', '0', '0', '', 'log', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '日志管理菜单');
INSERT INTO `sys_menu` VALUES (109, '在线用户', 2, 1, 'online', 'monitor/online/index', '', 1, 0, 'C', '0', '0', 'monitor:online:list', 'online', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '在线用户菜单');
INSERT INTO `sys_menu` VALUES (113, '缓存监控', 2, 5, 'cache', 'monitor/cache/index', '', 1, 0, 'C', '0', '0', 'monitor:cache:list', 'redis', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '缓存监控菜单');
INSERT INTO `sys_menu` VALUES (115, '代码生成', 3, 2, 'gen', 'tool/gen/index', '', 1, 0, 'C', '0', '0', 'tool:gen:list', 'code', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '代码生成菜单');
INSERT INTO `sys_menu` VALUES (116, '修改生成配置', 3, 2, 'gen-edit/index/:tableId(\\d+)', 'tool/gen/editTable', '', 1, 1, 'C', '1', '0', 'tool:gen:edit', '#', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '');
INSERT INTO `sys_menu` VALUES (118, '文件管理', 1, 10, 'oss', 'system/oss/index', '', 1, 0, 'C', '0', '0', 'system:oss:list', 'upload', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '文件管理菜单');
INSERT INTO `sys_menu` VALUES (123, '客户端管理', 1, 11, 'client', 'system/client/index', '', 1, 0, 'C', '0', '0', 'system:client:list', 'international', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '客户端管理菜单');
INSERT INTO `sys_menu` VALUES (130, '分配用户', 1, 2, 'role-auth/user/:roleId(\\d+)', 'system/role/authUser', '', 1, 1, 'C', '1', '0', 'system:role:edit', '#', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '');
INSERT INTO `sys_menu` VALUES (131, '分配角色', 1, 1, 'user-auth/role/:userId(\\d+)', 'system/user/authRole', '', 1, 1, 'C', '1', '0', 'system:user:edit', '#', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '');
INSERT INTO `sys_menu` VALUES (132, '字典数据', 1, 6, 'dict-data/index/:dictId(\\d+)', 'system/dict/data', '', 1, 1, 'C', '1', '0', 'system:dict:list', '#', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '');
INSERT INTO `sys_menu` VALUES (133, '文件配置管理', 1, 10, 'oss-config/index', 'system/oss/config', '', 1, 1, 'C', '1', '0', 'system:ossConfig:list', '#', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '');
INSERT INTO `sys_menu` VALUES (500, '操作日志', 108, 1, 'operlog', 'monitor/operlog/index', '', 1, 0, 'C', '0', '0', 'monitor:operlog:list', 'form', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '操作日志菜单');
INSERT INTO `sys_menu` VALUES (501, '登录日志', 108, 2, 'logininfor', 'monitor/logininfor/index', '', 1, 0, 'C', '0', '0', 'monitor:logininfor:list', 'logininfor', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '登录日志菜单');
INSERT INTO `sys_menu` VALUES (1001, '用户查询', 100, 1, '', '', '', 1, 0, 'F', '0', '0', 'system:user:query', '#', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '');
INSERT INTO `sys_menu` VALUES (1002, '用户新增', 100, 2, '', '', '', 1, 0, 'F', '0', '0', 'system:user:add', '#', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '');
INSERT INTO `sys_menu` VALUES (1003, '用户修改', 100, 3, '', '', '', 1, 0, 'F', '0', '0', 'system:user:edit', '#', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '');
INSERT INTO `sys_menu` VALUES (1004, '用户删除', 100, 4, '', '', '', 1, 0, 'F', '0', '0', 'system:user:remove', '#', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '');
INSERT INTO `sys_menu` VALUES (1005, '用户导出', 100, 5, '', '', '', 1, 0, 'F', '0', '0', 'system:user:export', '#', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '');
INSERT INTO `sys_menu` VALUES (1006, '用户导入', 100, 6, '', '', '', 1, 0, 'F', '0', '0', 'system:user:import', '#', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '');
INSERT INTO `sys_menu` VALUES (1007, '重置密码', 100, 7, '', '', '', 1, 0, 'F', '0', '0', 'system:user:resetPwd', '#', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '');
INSERT INTO `sys_menu` VALUES (1008, '角色查询', 101, 1, '', '', '', 1, 0, 'F', '0', '0', 'system:role:query', '#', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '');
INSERT INTO `sys_menu` VALUES (1009, '角色新增', 101, 2, '', '', '', 1, 0, 'F', '0', '0', 'system:role:add', '#', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '');
INSERT INTO `sys_menu` VALUES (1010, '角色修改', 101, 3, '', '', '', 1, 0, 'F', '0', '0', 'system:role:edit', '#', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '');
INSERT INTO `sys_menu` VALUES (1011, '角色删除', 101, 4, '', '', '', 1, 0, 'F', '0', '0', 'system:role:remove', '#', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '');
INSERT INTO `sys_menu` VALUES (1012, '角色导出', 101, 5, '', '', '', 1, 0, 'F', '0', '0', 'system:role:export', '#', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '');
INSERT INTO `sys_menu` VALUES (1013, '菜单查询', 102, 1, '', '', '', 1, 0, 'F', '0', '0', 'system:menu:query', '#', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '');
INSERT INTO `sys_menu` VALUES (1014, '菜单新增', 102, 2, '', '', '', 1, 0, 'F', '0', '0', 'system:menu:add', '#', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '');
INSERT INTO `sys_menu` VALUES (1015, '菜单修改', 102, 3, '', '', '', 1, 0, 'F', '0', '0', 'system:menu:edit', '#', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '');
INSERT INTO `sys_menu` VALUES (1016, '菜单删除', 102, 4, '', '', '', 1, 0, 'F', '0', '0', 'system:menu:remove', '#', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '');
INSERT INTO `sys_menu` VALUES (1017, '部门查询', 103, 1, '', '', '', 1, 0, 'F', '0', '0', 'system:dept:query', '#', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '');
INSERT INTO `sys_menu` VALUES (1018, '部门新增', 103, 2, '', '', '', 1, 0, 'F', '0', '0', 'system:dept:add', '#', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '');
INSERT INTO `sys_menu` VALUES (1019, '部门修改', 103, 3, '', '', '', 1, 0, 'F', '0', '0', 'system:dept:edit', '#', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '');
INSERT INTO `sys_menu` VALUES (1020, '部门删除', 103, 4, '', '', '', 1, 0, 'F', '0', '0', 'system:dept:remove', '#', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '');
INSERT INTO `sys_menu` VALUES (1021, '岗位查询', 104, 1, '', '', '', 1, 0, 'F', '0', '0', 'system:post:query', '#', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '');
INSERT INTO `sys_menu` VALUES (1022, '岗位新增', 104, 2, '', '', '', 1, 0, 'F', '0', '0', 'system:post:add', '#', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '');
INSERT INTO `sys_menu` VALUES (1023, '岗位修改', 104, 3, '', '', '', 1, 0, 'F', '0', '0', 'system:post:edit', '#', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '');
INSERT INTO `sys_menu` VALUES (1024, '岗位删除', 104, 4, '', '', '', 1, 0, 'F', '0', '0', 'system:post:remove', '#', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '');
INSERT INTO `sys_menu` VALUES (1025, '岗位导出', 104, 5, '', '', '', 1, 0, 'F', '0', '0', 'system:post:export', '#', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '');
INSERT INTO `sys_menu` VALUES (1026, '字典查询', 105, 1, '#', '', '', 1, 0, 'F', '0', '0', 'system:dict:query', '#', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '');
INSERT INTO `sys_menu` VALUES (1027, '字典新增', 105, 2, '#', '', '', 1, 0, 'F', '0', '0', 'system:dict:add', '#', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '');
INSERT INTO `sys_menu` VALUES (1028, '字典修改', 105, 3, '#', '', '', 1, 0, 'F', '0', '0', 'system:dict:edit', '#', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '');
INSERT INTO `sys_menu` VALUES (1029, '字典删除', 105, 4, '#', '', '', 1, 0, 'F', '0', '0', 'system:dict:remove', '#', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '');
INSERT INTO `sys_menu` VALUES (1030, '字典导出', 105, 5, '#', '', '', 1, 0, 'F', '0', '0', 'system:dict:export', '#', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '');
INSERT INTO `sys_menu` VALUES (1031, '参数查询', 106, 1, '#', '', '', 1, 0, 'F', '0', '0', 'system:config:query', '#', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '');
INSERT INTO `sys_menu` VALUES (1032, '参数新增', 106, 2, '#', '', '', 1, 0, 'F', '0', '0', 'system:config:add', '#', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '');
INSERT INTO `sys_menu` VALUES (1033, '参数修改', 106, 3, '#', '', '', 1, 0, 'F', '0', '0', 'system:config:edit', '#', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '');
INSERT INTO `sys_menu` VALUES (1034, '参数删除', 106, 4, '#', '', '', 1, 0, 'F', '0', '0', 'system:config:remove', '#', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '');
INSERT INTO `sys_menu` VALUES (1035, '参数导出', 106, 5, '#', '', '', 1, 0, 'F', '0', '0', 'system:config:export', '#', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '');
INSERT INTO `sys_menu` VALUES (1036, '公告查询', 107, 1, '#', '', '', 1, 0, 'F', '0', '0', 'system:notice:query', '#', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '');
INSERT INTO `sys_menu` VALUES (1037, '公告新增', 107, 2, '#', '', '', 1, 0, 'F', '0', '0', 'system:notice:add', '#', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '');
INSERT INTO `sys_menu` VALUES (1038, '公告修改', 107, 3, '#', '', '', 1, 0, 'F', '0', '0', 'system:notice:edit', '#', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '');
INSERT INTO `sys_menu` VALUES (1039, '公告删除', 107, 4, '#', '', '', 1, 0, 'F', '0', '0', 'system:notice:remove', '#', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '');
INSERT INTO `sys_menu` VALUES (1040, '操作查询', 500, 1, '#', '', '', 1, 0, 'F', '0', '0', 'monitor:operlog:query', '#', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '');
INSERT INTO `sys_menu` VALUES (1041, '操作删除', 500, 2, '#', '', '', 1, 0, 'F', '0', '0', 'monitor:operlog:remove', '#', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '');
INSERT INTO `sys_menu` VALUES (1042, '日志导出', 500, 4, '#', '', '', 1, 0, 'F', '0', '0', 'monitor:operlog:export', '#', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '');
INSERT INTO `sys_menu` VALUES (1043, '登录查询', 501, 1, '#', '', '', 1, 0, 'F', '0', '0', 'monitor:logininfor:query', '#', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '');
INSERT INTO `sys_menu` VALUES (1044, '登录删除', 501, 2, '#', '', '', 1, 0, 'F', '0', '0', 'monitor:logininfor:remove', '#', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '');
INSERT INTO `sys_menu` VALUES (1045, '日志导出', 501, 3, '#', '', '', 1, 0, 'F', '0', '0', 'monitor:logininfor:export', '#', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '');
INSERT INTO `sys_menu` VALUES (1046, '在线查询', 109, 1, '#', '', '', 1, 0, 'F', '0', '0', 'monitor:online:query', '#', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '');
INSERT INTO `sys_menu` VALUES (1047, '批量强退', 109, 2, '#', '', '', 1, 0, 'F', '0', '0', 'monitor:online:batchLogout', '#', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '');
INSERT INTO `sys_menu` VALUES (1048, '单条强退', 109, 3, '#', '', '', 1, 0, 'F', '0', '0', 'monitor:online:forceLogout', '#', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '');
INSERT INTO `sys_menu` VALUES (1050, '账户解锁', 501, 4, '#', '', '', 1, 0, 'F', '0', '0', 'monitor:logininfor:unlock', '#', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '');
INSERT INTO `sys_menu` VALUES (1055, '生成查询', 115, 1, '#', '', '', 1, 0, 'F', '0', '0', 'tool:gen:query', '#', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '');
INSERT INTO `sys_menu` VALUES (1056, '生成修改', 115, 2, '#', '', '', 1, 0, 'F', '0', '0', 'tool:gen:edit', '#', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '');
INSERT INTO `sys_menu` VALUES (1057, '生成删除', 115, 3, '#', '', '', 1, 0, 'F', '0', '0', 'tool:gen:remove', '#', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '');
INSERT INTO `sys_menu` VALUES (1058, '导入代码', 115, 2, '#', '', '', 1, 0, 'F', '0', '0', 'tool:gen:import', '#', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '');
INSERT INTO `sys_menu` VALUES (1059, '预览代码', 115, 4, '#', '', '', 1, 0, 'F', '0', '0', 'tool:gen:preview', '#', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '');
INSERT INTO `sys_menu` VALUES (1060, '生成代码', 115, 5, '#', '', '', 1, 0, 'F', '0', '0', 'tool:gen:code', '#', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '');
INSERT INTO `sys_menu` VALUES (1061, '客户端管理查询', 123, 1, '#', '', '', 1, 0, 'F', '0', '0', 'system:client:query', '#', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '');
INSERT INTO `sys_menu` VALUES (1062, '客户端管理新增', 123, 2, '#', '', '', 1, 0, 'F', '0', '0', 'system:client:add', '#', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '');
INSERT INTO `sys_menu` VALUES (1063, '客户端管理修改', 123, 3, '#', '', '', 1, 0, 'F', '0', '0', 'system:client:edit', '#', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '');
INSERT INTO `sys_menu` VALUES (1064, '客户端管理删除', 123, 4, '#', '', '', 1, 0, 'F', '0', '0', 'system:client:remove', '#', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '');
INSERT INTO `sys_menu` VALUES (1065, '客户端管理导出', 123, 5, '#', '', '', 1, 0, 'F', '0', '0', 'system:client:export', '#', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '');
INSERT INTO `sys_menu` VALUES (1600, '文件查询', 118, 1, '#', '', '', 1, 0, 'F', '0', '0', 'system:oss:query', '#', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '');
INSERT INTO `sys_menu` VALUES (1601, '文件上传', 118, 2, '#', '', '', 1, 0, 'F', '0', '0', 'system:oss:upload', '#', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '');
INSERT INTO `sys_menu` VALUES (1602, '文件下载', 118, 3, '#', '', '', 1, 0, 'F', '0', '0', 'system:oss:download', '#', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '');
INSERT INTO `sys_menu` VALUES (1603, '文件删除', 118, 4, '#', '', '', 1, 0, 'F', '0', '0', 'system:oss:remove', '#', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '');
INSERT INTO `sys_menu` VALUES (1620, '配置列表', 118, 5, '#', '', '', 1, 0, 'F', '0', '0', 'system:ossConfig:list', '#', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '');
INSERT INTO `sys_menu` VALUES (1621, '配置添加', 118, 6, '#', '', '', 1, 0, 'F', '0', '0', 'system:ossConfig:add', '#', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '');
INSERT INTO `sys_menu` VALUES (1622, '配置编辑', 118, 6, '#', '', '', 1, 0, 'F', '0', '0', 'system:ossConfig:edit', '#', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '');
INSERT INTO `sys_menu` VALUES (1623, '配置删除', 118, 6, '#', '', '', 1, 0, 'F', '0', '0', 'system:ossConfig:remove', '#', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '');
-- ----------------------------
-- Table structure for sys_notice
-- ----------------------------
DROP TABLE IF EXISTS `sys_notice`;
CREATE TABLE `sys_notice` (
`notice_id` bigint NOT NULL COMMENT '公告ID',
`notice_title` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '公告标题',
`notice_type` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '公告类型1通知 2公告',
`notice_content` longblob NULL COMMENT '公告内容',
`status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '0' COMMENT '公告状态0正常 1关闭',
`create_dept` bigint NULL DEFAULT NULL COMMENT '创建部门',
`create_by` bigint NULL DEFAULT NULL COMMENT '创建者',
`create_time` datetime NULL DEFAULT NULL COMMENT '创建时间',
`update_by` bigint NULL DEFAULT NULL COMMENT '更新者',
`update_time` datetime NULL DEFAULT NULL COMMENT '更新时间',
`remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '备注',
PRIMARY KEY (`notice_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '通知公告表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of sys_notice
-- ----------------------------
INSERT INTO `sys_notice` VALUES (1, '温馨提醒2018-07-01 新版本发布啦', '2', 0xE696B0E78988E69CACE58685E5AEB9, '0', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '管理员');
INSERT INTO `sys_notice` VALUES (2, '维护通知2018-07-01 系统凌晨维护', '1', 0xE7BBB4E68AA4E58685E5AEB9, '0', 103, 1, '2025-06-10 12:10:29', NULL, NULL, '管理员');
-- ----------------------------
-- Table structure for sys_oper_log
-- ----------------------------
DROP TABLE IF EXISTS `sys_oper_log`;
CREATE TABLE `sys_oper_log` (
`oper_id` bigint NOT NULL COMMENT '日志主键',
`title` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '模块标题',
`business_type` int NULL DEFAULT 0 COMMENT '业务类型0其它 1新增 2修改 3删除',
`method` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '方法名称',
`request_method` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '请求方式',
`operator_type` int NULL DEFAULT 0 COMMENT '操作类别0其它 1后台用户 2手机端用户',
`oper_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '操作人员',
`dept_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '部门名称',
`oper_url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '请求URL',
`oper_ip` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '主机地址',
`oper_location` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '操作地点',
`oper_param` varchar(4000) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '请求参数',
`json_result` varchar(4000) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '返回参数',
`status` int NULL DEFAULT 0 COMMENT '操作状态0正常 1异常',
`error_msg` varchar(4000) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '错误消息',
`oper_time` datetime NULL DEFAULT NULL COMMENT '操作时间',
`cost_time` bigint NULL DEFAULT 0 COMMENT '消耗时间',
PRIMARY KEY (`oper_id`) USING BTREE,
INDEX `idx_sys_oper_log_bt`(`business_type` ASC) USING BTREE,
INDEX `idx_sys_oper_log_s`(`status` ASC) USING BTREE,
INDEX `idx_sys_oper_log_ot`(`oper_time` ASC) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '操作日志记录' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of sys_oper_log
-- ----------------------------
INSERT INTO `sys_oper_log` VALUES (1932354277142958082, '菜单管理', 3, 'com.base.system.controller.system.SysMenuController.remove()', 'DELETE', 1, 'admin', '研发部门', '/system/menu/117', '0:0:0:0:0:0:0:1', '内网IP', '117', '{\"code\":200,\"msg\":\"操作成功\",\"data\":null}', 0, '', '2025-06-10 16:28:56', 16);
INSERT INTO `sys_oper_log` VALUES (1932354317420859394, '菜单管理', 3, 'com.base.system.controller.system.SysMenuController.remove()', 'DELETE', 1, 'admin', '研发部门', '/system/menu/4', '0:0:0:0:0:0:0:1', '内网IP', '4', '{\"code\":200,\"msg\":\"操作成功\",\"data\":null}', 0, '', '2025-06-10 16:29:05', 7);
INSERT INTO `sys_oper_log` VALUES (1932354332180615170, '菜单管理', 3, 'com.base.system.controller.system.SysMenuController.remove()', 'DELETE', 1, 'admin', '研发部门', '/system/menu/5', '0:0:0:0:0:0:0:1', '内网IP', '5', '{\"code\":601,\"msg\":\"存在子菜单,不允许删除\",\"data\":null}', 0, '', '2025-06-10 16:29:09', 6);
INSERT INTO `sys_oper_log` VALUES (1932354356197199873, '菜单管理', 3, 'com.base.system.controller.system.SysMenuController.remove()', 'DELETE', 1, 'admin', '研发部门', '/system/menu/1500', '0:0:0:0:0:0:0:1', '内网IP', '1500', '{\"code\":601,\"msg\":\"存在子菜单,不允许删除\",\"data\":null}', 0, '', '2025-06-10 16:29:15', 2);
INSERT INTO `sys_oper_log` VALUES (1932355178373058561, '菜单管理', 3, 'com.base.system.controller.system.SysMenuController.remove()', 'DELETE', 1, 'admin', '研发部门', '/system/menu/1501', '0:0:0:0:0:0:0:1', '内网IP', '1501', '{\"code\":601,\"msg\":\"菜单已分配,不允许删除\",\"data\":null}', 0, '', '2025-06-10 16:32:31', 6);
INSERT INTO `sys_oper_log` VALUES (1932355232181784578, '角色管理', 2, 'com.base.system.controller.system.SysRoleController.edit()', 'PUT', 1, 'admin', '研发部门', '/system/role', '0:0:0:0:0:0:0:1', '内网IP', '{\"createDept\":null,\"createBy\":null,\"createTime\":\"2025-06-10 12:10:28\",\"updateBy\":null,\"updateTime\":null,\"roleId\":3,\"roleName\":\"本部门及以下\",\"roleKey\":\"test1\",\"roleSort\":3,\"dataScope\":\"4\",\"menuCheckStrictly\":true,\"deptCheckStrictly\":true,\"status\":\"0\",\"remark\":\"\",\"menuIds\":[1,100,1001,1002,1003,1004,1005,1006,1007,131,101,1008,1009,1010,1011,1012,130,102,1013,1014,1015,1016,103,1017,1018,1019,1020,104,1021,1022,1023,1024,1025,105,1026,1027,1028,1029,1030,132,106,1031,1032,1033,1034,1035,107,1036,1037,1038,1039,108,500,1040,1041,1042,501,1043,1044,1045,1050,118,1600,1601,1602,1603,1620,1621,1622,1623,133,123,1061,1062,1063,1064,1065],\"deptIds\":[],\"superAdmin\":false}', '{\"code\":200,\"msg\":\"操作成功\",\"data\":null}', 0, '', '2025-06-10 16:32:43', 207);
INSERT INTO `sys_oper_log` VALUES (1932355255103655937, '菜单管理', 3, 'com.base.system.controller.system.SysMenuController.remove()', 'DELETE', 1, 'admin', '研发部门', '/system/menu/1501', '0:0:0:0:0:0:0:1', '内网IP', '1501', '{\"code\":601,\"msg\":\"菜单已分配,不允许删除\",\"data\":null}', 0, '', '2025-06-10 16:32:49', 3);
INSERT INTO `sys_oper_log` VALUES (1932355288830054401, '角色管理', 2, 'com.base.system.controller.system.SysRoleController.edit()', 'PUT', 1, 'admin', '研发部门', '/system/role', '0:0:0:0:0:0:0:1', '内网IP', '{\"createDept\":null,\"createBy\":null,\"createTime\":\"2025-06-10 12:10:28\",\"updateBy\":null,\"updateTime\":null,\"roleId\":4,\"roleName\":\"仅本人\",\"roleKey\":\"test2\",\"roleSort\":4,\"dataScope\":\"5\",\"menuCheckStrictly\":true,\"deptCheckStrictly\":true,\"status\":\"0\",\"remark\":\"\",\"menuIds\":[],\"deptIds\":[],\"superAdmin\":false}', '{\"code\":200,\"msg\":\"操作成功\",\"data\":null}', 0, '', '2025-06-10 16:32:57', 11);
INSERT INTO `sys_oper_log` VALUES (1932355313685499905, '菜单管理', 3, 'com.base.system.controller.system.SysMenuController.remove()', 'DELETE', 1, 'admin', '研发部门', '/system/menu/1505', '0:0:0:0:0:0:0:1', '内网IP', '1505', '{\"code\":200,\"msg\":\"操作成功\",\"data\":null}', 0, '', '2025-06-10 16:33:03', 5);
INSERT INTO `sys_oper_log` VALUES (1932355329598689282, '菜单管理', 3, 'com.base.system.controller.system.SysMenuController.remove()', 'DELETE', 1, 'admin', '研发部门', '/system/menu/1504', '0:0:0:0:0:0:0:1', '内网IP', '1504', '{\"code\":200,\"msg\":\"操作成功\",\"data\":null}', 0, '', '2025-06-10 16:33:07', 6);
INSERT INTO `sys_oper_log` VALUES (1932355342206767106, '菜单管理', 3, 'com.base.system.controller.system.SysMenuController.remove()', 'DELETE', 1, 'admin', '研发部门', '/system/menu/1503', '0:0:0:0:0:0:0:1', '内网IP', '1503', '{\"code\":200,\"msg\":\"操作成功\",\"data\":null}', 0, '', '2025-06-10 16:33:10', 5);
INSERT INTO `sys_oper_log` VALUES (1932355356215742466, '菜单管理', 3, 'com.base.system.controller.system.SysMenuController.remove()', 'DELETE', 1, 'admin', '研发部门', '/system/menu/1502', '0:0:0:0:0:0:0:1', '内网IP', '1502', '{\"code\":200,\"msg\":\"操作成功\",\"data\":null}', 0, '', '2025-06-10 16:33:13', 5);
INSERT INTO `sys_oper_log` VALUES (1932355368433750017, '菜单管理', 3, 'com.base.system.controller.system.SysMenuController.remove()', 'DELETE', 1, 'admin', '研发部门', '/system/menu/1501', '0:0:0:0:0:0:0:1', '内网IP', '1501', '{\"code\":200,\"msg\":\"操作成功\",\"data\":null}', 0, '', '2025-06-10 16:33:16', 7);
INSERT INTO `sys_oper_log` VALUES (1932355384191750145, '菜单管理', 3, 'com.base.system.controller.system.SysMenuController.remove()', 'DELETE', 1, 'admin', '研发部门', '/system/menu/1500', '0:0:0:0:0:0:0:1', '内网IP', '1500', '{\"code\":200,\"msg\":\"操作成功\",\"data\":null}', 0, '', '2025-06-10 16:33:20', 7);
INSERT INTO `sys_oper_log` VALUES (1932355397475110913, '菜单管理', 3, 'com.base.system.controller.system.SysMenuController.remove()', 'DELETE', 1, 'admin', '研发部门', '/system/menu/1506', '0:0:0:0:0:0:0:1', '内网IP', '1506', '{\"code\":601,\"msg\":\"存在子菜单,不允许删除\",\"data\":null}', 0, '', '2025-06-10 16:33:23', 1);
INSERT INTO `sys_oper_log` VALUES (1932355418870255618, '菜单管理', 3, 'com.base.system.controller.system.SysMenuController.remove()', 'DELETE', 1, 'admin', '研发部门', '/system/menu/1511', '0:0:0:0:0:0:0:1', '内网IP', '1511', '{\"code\":200,\"msg\":\"操作成功\",\"data\":null}', 0, '', '2025-06-10 16:33:28', 7);
INSERT INTO `sys_oper_log` VALUES (1932355432837287938, '菜单管理', 3, 'com.base.system.controller.system.SysMenuController.remove()', 'DELETE', 1, 'admin', '研发部门', '/system/menu/1510', '0:0:0:0:0:0:0:1', '内网IP', '1510', '{\"code\":200,\"msg\":\"操作成功\",\"data\":null}', 0, '', '2025-06-10 16:33:31', 5);
INSERT INTO `sys_oper_log` VALUES (1932355445868990466, '菜单管理', 3, 'com.base.system.controller.system.SysMenuController.remove()', 'DELETE', 1, 'admin', '研发部门', '/system/menu/1509', '0:0:0:0:0:0:0:1', '内网IP', '1509', '{\"code\":200,\"msg\":\"操作成功\",\"data\":null}', 0, '', '2025-06-10 16:33:34', 7);
INSERT INTO `sys_oper_log` VALUES (1932355457210388482, '菜单管理', 3, 'com.base.system.controller.system.SysMenuController.remove()', 'DELETE', 1, 'admin', '研发部门', '/system/menu/1508', '0:0:0:0:0:0:0:1', '内网IP', '1508', '{\"code\":200,\"msg\":\"操作成功\",\"data\":null}', 0, '', '2025-06-10 16:33:37', 8);
INSERT INTO `sys_oper_log` VALUES (1932355470703464449, '菜单管理', 3, 'com.base.system.controller.system.SysMenuController.remove()', 'DELETE', 1, 'admin', '研发部门', '/system/menu/1507', '0:0:0:0:0:0:0:1', '内网IP', '1507', '{\"code\":200,\"msg\":\"操作成功\",\"data\":null}', 0, '', '2025-06-10 16:33:40', 7);
INSERT INTO `sys_oper_log` VALUES (1932355484519501825, '菜单管理', 3, 'com.base.system.controller.system.SysMenuController.remove()', 'DELETE', 1, 'admin', '研发部门', '/system/menu/1506', '0:0:0:0:0:0:0:1', '内网IP', '1506', '{\"code\":200,\"msg\":\"操作成功\",\"data\":null}', 0, '', '2025-06-10 16:33:44', 8);
INSERT INTO `sys_oper_log` VALUES (1932355496859144193, '菜单管理', 3, 'com.base.system.controller.system.SysMenuController.remove()', 'DELETE', 1, 'admin', '研发部门', '/system/menu/5', '0:0:0:0:0:0:0:1', '内网IP', '5', '{\"code\":200,\"msg\":\"操作成功\",\"data\":null}', 0, '', '2025-06-10 16:33:47', 5);
-- ----------------------------
-- Table structure for sys_oss
-- ----------------------------
DROP TABLE IF EXISTS `sys_oss`;
CREATE TABLE `sys_oss` (
`oss_id` bigint NOT NULL COMMENT '对象存储主键',
`file_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '文件名',
`original_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '原名',
`file_suffix` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '文件后缀名',
`url` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT 'URL地址',
`ext1` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT '扩展字段',
`create_dept` bigint NULL DEFAULT NULL COMMENT '创建部门',
`create_time` datetime NULL DEFAULT NULL COMMENT '创建时间',
`create_by` bigint NULL DEFAULT NULL COMMENT '上传人',
`update_time` datetime NULL DEFAULT NULL COMMENT '更新时间',
`update_by` bigint NULL DEFAULT NULL COMMENT '更新人',
`service` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT 'minio' COMMENT '服务商',
PRIMARY KEY (`oss_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'OSS对象存储表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of sys_oss
-- ----------------------------
-- ----------------------------
-- Table structure for sys_oss_config
-- ----------------------------
DROP TABLE IF EXISTS `sys_oss_config`;
CREATE TABLE `sys_oss_config` (
`oss_config_id` bigint NOT NULL COMMENT '主键',
`config_key` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '配置key',
`access_key` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT 'accessKey',
`secret_key` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '秘钥',
`bucket_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '桶名称',
`prefix` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '前缀',
`endpoint` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '访问站点',
`domain` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '自定义域名',
`is_https` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT 'N' COMMENT '是否httpsY=是,N=否)',
`region` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '',
`access_policy` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '1' COMMENT '桶权限类型(0=private 1=public 2=custom)',
`status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '1' COMMENT '是否默认0=是,1=否)',
`ext1` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '扩展字段',
`create_dept` bigint NULL DEFAULT NULL COMMENT '创建部门',
`create_by` bigint NULL DEFAULT NULL COMMENT '创建者',
`create_time` datetime NULL DEFAULT NULL COMMENT '创建时间',
`update_by` bigint NULL DEFAULT NULL COMMENT '更新者',
`update_time` datetime NULL DEFAULT NULL COMMENT '更新时间',
`remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '备注',
PRIMARY KEY (`oss_config_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '对象存储配置表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of sys_oss_config
-- ----------------------------
INSERT INTO `sys_oss_config` VALUES (1, 'minio', 'boot', 'boot123', 'boot', '', '127.0.0.1:9000', '', 'N', '', '1', '0', '', 103, 1, '2025-06-10 12:10:29', 1, '2025-06-10 12:10:29', NULL);
INSERT INTO `sys_oss_config` VALUES (2, 'qiniu', 'XXXXXXXXXXXXXXX', 'XXXXXXXXXXXXXXX', 'boot', '', 's3-cn-north-1.qiniucs.com', '', 'N', '', '1', '1', '', 103, 1, '2025-06-10 12:10:29', 1, '2025-06-10 12:10:29', NULL);
INSERT INTO `sys_oss_config` VALUES (3, 'aliyun', 'XXXXXXXXXXXXXXX', 'XXXXXXXXXXXXXXX', 'boot', '', 'oss-cn-beijing.aliyuncs.com', '', 'N', '', '1', '1', '', 103, 1, '2025-06-10 12:10:29', 1, '2025-06-10 12:10:29', NULL);
INSERT INTO `sys_oss_config` VALUES (4, 'qcloud', 'XXXXXXXXXXXXXXX', 'XXXXXXXXXXXXXXX', 'boot-1240000000', '', 'cos.ap-beijing.myqcloud.com', '', 'N', 'ap-beijing', '1', '1', '', 103, 1, '2025-06-10 12:10:29', 1, '2025-06-10 12:10:29', NULL);
INSERT INTO `sys_oss_config` VALUES (5, 'image', 'boot', 'boot123', 'boot', 'image', '127.0.0.1:9000', '', 'N', '', '1', '1', '', 103, 1, '2025-06-10 12:10:29', 1, '2025-06-10 12:10:29', NULL);
-- ----------------------------
-- Table structure for sys_post
-- ----------------------------
DROP TABLE IF EXISTS `sys_post`;
CREATE TABLE `sys_post` (
`post_id` bigint NOT NULL COMMENT '岗位ID',
`dept_id` bigint NOT NULL COMMENT '部门id',
`post_code` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '岗位编码',
`post_category` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '岗位类别编码',
`post_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '岗位名称',
`post_sort` int NOT NULL COMMENT '显示顺序',
`status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '状态0正常 1停用',
`create_dept` bigint NULL DEFAULT NULL COMMENT '创建部门',
`create_by` bigint NULL DEFAULT NULL COMMENT '创建者',
`create_time` datetime NULL DEFAULT NULL COMMENT '创建时间',
`update_by` bigint NULL DEFAULT NULL COMMENT '更新者',
`update_time` datetime NULL DEFAULT NULL COMMENT '更新时间',
`remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '备注',
PRIMARY KEY (`post_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '岗位信息表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of sys_post
-- ----------------------------
INSERT INTO `sys_post` VALUES (1, 103, 'ceo', NULL, '董事长', 1, '0', 103, 1, '2025-06-10 12:10:28', NULL, NULL, '');
INSERT INTO `sys_post` VALUES (2, 100, 'se', NULL, '项目经理', 2, '0', 103, 1, '2025-06-10 12:10:28', NULL, NULL, '');
INSERT INTO `sys_post` VALUES (3, 100, 'hr', NULL, '人力资源', 3, '0', 103, 1, '2025-06-10 12:10:28', NULL, NULL, '');
INSERT INTO `sys_post` VALUES (4, 100, 'user', NULL, '普通员工', 4, '0', 103, 1, '2025-06-10 12:10:28', NULL, NULL, '');
-- ----------------------------
-- Table structure for sys_role
-- ----------------------------
DROP TABLE IF EXISTS `sys_role`;
CREATE TABLE `sys_role` (
`role_id` bigint NOT NULL COMMENT '角色ID',
`role_name` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '角色名称',
`role_key` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '角色权限字符串',
`role_sort` int NOT NULL COMMENT '显示顺序',
`data_scope` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '1' COMMENT '数据范围1全部数据权限 2自定数据权限 3本部门数据权限 4本部门及以下数据权限 5仅本人数据权限 6部门及以下或本人数据权限',
`menu_check_strictly` tinyint(1) NULL DEFAULT 1 COMMENT '菜单树选择项是否关联显示',
`dept_check_strictly` tinyint(1) NULL DEFAULT 1 COMMENT '部门树选择项是否关联显示',
`status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '角色状态0正常 1停用',
`del_flag` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '0' COMMENT '删除标志0代表存在 1代表删除',
`create_dept` bigint NULL DEFAULT NULL COMMENT '创建部门',
`create_by` bigint NULL DEFAULT NULL COMMENT '创建者',
`create_time` datetime NULL DEFAULT NULL COMMENT '创建时间',
`update_by` bigint NULL DEFAULT NULL COMMENT '更新者',
`update_time` datetime NULL DEFAULT NULL COMMENT '更新时间',
`remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '备注',
PRIMARY KEY (`role_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '角色信息表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of sys_role
-- ----------------------------
INSERT INTO `sys_role` VALUES (1, '超级管理员', 'superadmin', 1, '1', 1, 1, '0', '0', 103, 1, '2025-06-10 12:10:28', NULL, NULL, '超级管理员');
INSERT INTO `sys_role` VALUES (3, '本部门及以下', 'test1', 3, '4', 1, 1, '0', '0', 103, 1, '2025-06-10 12:10:28', 1, '2025-06-10 16:32:43', '');
INSERT INTO `sys_role` VALUES (4, '仅本人', 'test2', 4, '5', 1, 1, '0', '0', 103, 1, '2025-06-10 12:10:28', 1, '2025-06-10 16:32:57', '');
-- ----------------------------
-- Table structure for sys_role_dept
-- ----------------------------
DROP TABLE IF EXISTS `sys_role_dept`;
CREATE TABLE `sys_role_dept` (
`role_id` bigint NOT NULL COMMENT '角色ID',
`dept_id` bigint NOT NULL COMMENT '部门ID',
PRIMARY KEY (`role_id`, `dept_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '角色和部门关联表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of sys_role_dept
-- ----------------------------
-- ----------------------------
-- Table structure for sys_role_menu
-- ----------------------------
DROP TABLE IF EXISTS `sys_role_menu`;
CREATE TABLE `sys_role_menu` (
`role_id` bigint NOT NULL COMMENT '角色ID',
`menu_id` bigint NOT NULL COMMENT '菜单ID',
PRIMARY KEY (`role_id`, `menu_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '角色和菜单关联表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of sys_role_menu
-- ----------------------------
INSERT INTO `sys_role_menu` VALUES (3, 1);
INSERT INTO `sys_role_menu` VALUES (3, 100);
INSERT INTO `sys_role_menu` VALUES (3, 101);
INSERT INTO `sys_role_menu` VALUES (3, 102);
INSERT INTO `sys_role_menu` VALUES (3, 103);
INSERT INTO `sys_role_menu` VALUES (3, 104);
INSERT INTO `sys_role_menu` VALUES (3, 105);
INSERT INTO `sys_role_menu` VALUES (3, 106);
INSERT INTO `sys_role_menu` VALUES (3, 107);
INSERT INTO `sys_role_menu` VALUES (3, 108);
INSERT INTO `sys_role_menu` VALUES (3, 118);
INSERT INTO `sys_role_menu` VALUES (3, 123);
INSERT INTO `sys_role_menu` VALUES (3, 130);
INSERT INTO `sys_role_menu` VALUES (3, 131);
INSERT INTO `sys_role_menu` VALUES (3, 132);
INSERT INTO `sys_role_menu` VALUES (3, 133);
INSERT INTO `sys_role_menu` VALUES (3, 500);
INSERT INTO `sys_role_menu` VALUES (3, 501);
INSERT INTO `sys_role_menu` VALUES (3, 1001);
INSERT INTO `sys_role_menu` VALUES (3, 1002);
INSERT INTO `sys_role_menu` VALUES (3, 1003);
INSERT INTO `sys_role_menu` VALUES (3, 1004);
INSERT INTO `sys_role_menu` VALUES (3, 1005);
INSERT INTO `sys_role_menu` VALUES (3, 1006);
INSERT INTO `sys_role_menu` VALUES (3, 1007);
INSERT INTO `sys_role_menu` VALUES (3, 1008);
INSERT INTO `sys_role_menu` VALUES (3, 1009);
INSERT INTO `sys_role_menu` VALUES (3, 1010);
INSERT INTO `sys_role_menu` VALUES (3, 1011);
INSERT INTO `sys_role_menu` VALUES (3, 1012);
INSERT INTO `sys_role_menu` VALUES (3, 1013);
INSERT INTO `sys_role_menu` VALUES (3, 1014);
INSERT INTO `sys_role_menu` VALUES (3, 1015);
INSERT INTO `sys_role_menu` VALUES (3, 1016);
INSERT INTO `sys_role_menu` VALUES (3, 1017);
INSERT INTO `sys_role_menu` VALUES (3, 1018);
INSERT INTO `sys_role_menu` VALUES (3, 1019);
INSERT INTO `sys_role_menu` VALUES (3, 1020);
INSERT INTO `sys_role_menu` VALUES (3, 1021);
INSERT INTO `sys_role_menu` VALUES (3, 1022);
INSERT INTO `sys_role_menu` VALUES (3, 1023);
INSERT INTO `sys_role_menu` VALUES (3, 1024);
INSERT INTO `sys_role_menu` VALUES (3, 1025);
INSERT INTO `sys_role_menu` VALUES (3, 1026);
INSERT INTO `sys_role_menu` VALUES (3, 1027);
INSERT INTO `sys_role_menu` VALUES (3, 1028);
INSERT INTO `sys_role_menu` VALUES (3, 1029);
INSERT INTO `sys_role_menu` VALUES (3, 1030);
INSERT INTO `sys_role_menu` VALUES (3, 1031);
INSERT INTO `sys_role_menu` VALUES (3, 1032);
INSERT INTO `sys_role_menu` VALUES (3, 1033);
INSERT INTO `sys_role_menu` VALUES (3, 1034);
INSERT INTO `sys_role_menu` VALUES (3, 1035);
INSERT INTO `sys_role_menu` VALUES (3, 1036);
INSERT INTO `sys_role_menu` VALUES (3, 1037);
INSERT INTO `sys_role_menu` VALUES (3, 1038);
INSERT INTO `sys_role_menu` VALUES (3, 1039);
INSERT INTO `sys_role_menu` VALUES (3, 1040);
INSERT INTO `sys_role_menu` VALUES (3, 1041);
INSERT INTO `sys_role_menu` VALUES (3, 1042);
INSERT INTO `sys_role_menu` VALUES (3, 1043);
INSERT INTO `sys_role_menu` VALUES (3, 1044);
INSERT INTO `sys_role_menu` VALUES (3, 1045);
INSERT INTO `sys_role_menu` VALUES (3, 1050);
INSERT INTO `sys_role_menu` VALUES (3, 1061);
INSERT INTO `sys_role_menu` VALUES (3, 1062);
INSERT INTO `sys_role_menu` VALUES (3, 1063);
INSERT INTO `sys_role_menu` VALUES (3, 1064);
INSERT INTO `sys_role_menu` VALUES (3, 1065);
INSERT INTO `sys_role_menu` VALUES (3, 1600);
INSERT INTO `sys_role_menu` VALUES (3, 1601);
INSERT INTO `sys_role_menu` VALUES (3, 1602);
INSERT INTO `sys_role_menu` VALUES (3, 1603);
INSERT INTO `sys_role_menu` VALUES (3, 1620);
INSERT INTO `sys_role_menu` VALUES (3, 1621);
INSERT INTO `sys_role_menu` VALUES (3, 1622);
INSERT INTO `sys_role_menu` VALUES (3, 1623);
-- ----------------------------
-- Table structure for sys_social
-- ----------------------------
DROP TABLE IF EXISTS `sys_social`;
CREATE TABLE `sys_social` (
`id` bigint NOT NULL COMMENT '主键',
`user_id` bigint NOT NULL COMMENT '用户ID',
`auth_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '平台+平台唯一id',
`source` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '用户来源',
`open_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '平台编号唯一id',
`user_name` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '登录账号',
`nick_name` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '用户昵称',
`email` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '用户邮箱',
`avatar` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '头像地址',
`access_token` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '用户的授权令牌',
`expire_in` int NULL DEFAULT NULL COMMENT '用户的授权令牌的有效期,部分平台可能没有',
`refresh_token` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '刷新令牌,部分平台可能没有',
`access_code` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '平台的授权信息,部分平台可能没有',
`union_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '用户的 unionid',
`scope` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '授予的权限,部分平台可能没有',
`token_type` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '个别平台的授权信息,部分平台可能没有',
`id_token` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT 'id token部分平台可能没有',
`mac_algorithm` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '小米平台用户的附带属性,部分平台可能没有',
`mac_key` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '小米平台用户的附带属性,部分平台可能没有',
`code` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '用户的授权code部分平台可能没有',
`oauth_token` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT 'Twitter平台用户的附带属性部分平台可能没有',
`oauth_token_secret` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT 'Twitter平台用户的附带属性部分平台可能没有',
`create_dept` bigint NULL DEFAULT NULL COMMENT '创建部门',
`create_by` bigint NULL DEFAULT NULL COMMENT '创建者',
`create_time` datetime NULL DEFAULT NULL COMMENT '创建时间',
`update_by` bigint NULL DEFAULT NULL COMMENT '更新者',
`update_time` datetime NULL DEFAULT NULL COMMENT '更新时间',
`del_flag` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '0' COMMENT '删除标志0代表存在 1代表删除',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '社会化关系表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of sys_social
-- ----------------------------
-- ----------------------------
-- Table structure for sys_user
-- ----------------------------
DROP TABLE IF EXISTS `sys_user`;
CREATE TABLE `sys_user` (
`user_id` bigint NOT NULL COMMENT '用户ID',
`dept_id` bigint NULL DEFAULT NULL COMMENT '部门ID',
`user_name` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '用户账号',
`nick_name` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '用户昵称',
`user_type` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT 'sys_user' COMMENT '用户类型sys_user系统用户',
`email` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '用户邮箱',
`phonenumber` varchar(11) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '手机号码',
`sex` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '0' COMMENT '用户性别0男 1女 2未知',
`avatar` bigint NULL DEFAULT NULL COMMENT '头像地址',
`password` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '密码',
`status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '0' COMMENT '帐号状态0正常 1停用',
`del_flag` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '0' COMMENT '删除标志0代表存在 1代表删除',
`login_ip` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '最后登录IP',
`login_date` datetime NULL DEFAULT NULL COMMENT '最后登录时间',
`create_dept` bigint NULL DEFAULT NULL COMMENT '创建部门',
`create_by` bigint NULL DEFAULT NULL COMMENT '创建者',
`create_time` datetime NULL DEFAULT NULL COMMENT '创建时间',
`update_by` bigint NULL DEFAULT NULL COMMENT '更新者',
`update_time` datetime NULL DEFAULT NULL COMMENT '更新时间',
`remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '备注',
PRIMARY KEY (`user_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '用户信息表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of sys_user
-- ----------------------------
INSERT INTO `sys_user` VALUES (1, 103, 'admin', '疯狂的狮子Li', 'sys_user', 'crazyLionLi@163.com', '15888888888', '1', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '0:0:0:0:0:0:0:1', '2025-06-10 16:27:03', 103, 1, '2025-06-10 12:10:28', 1, '2025-06-10 16:27:03', '管理员');
INSERT INTO `sys_user` VALUES (3, 108, 'test', '本部门及以下 密码666666', 'sys_user', '', '', '0', NULL, '$2a$10$b8yUzN0C71sbz.PhNOCgJe.Tu1yWC3RNrTyjSQ8p1W0.aaUXUJ.Ne', '0', '0', '127.0.0.1', '2025-06-10 12:10:28', 103, 1, '2025-06-10 12:10:28', 3, '2025-06-10 12:10:28', NULL);
INSERT INTO `sys_user` VALUES (4, 102, 'test1', '仅本人 密码666666', 'sys_user', '', '', '0', NULL, '$2a$10$b8yUzN0C71sbz.PhNOCgJe.Tu1yWC3RNrTyjSQ8p1W0.aaUXUJ.Ne', '0', '0', '127.0.0.1', '2025-06-10 12:10:28', 103, 1, '2025-06-10 12:10:28', 4, '2025-06-10 12:10:28', NULL);
-- ----------------------------
-- Table structure for sys_user_post
-- ----------------------------
DROP TABLE IF EXISTS `sys_user_post`;
CREATE TABLE `sys_user_post` (
`user_id` bigint NOT NULL COMMENT '用户ID',
`post_id` bigint NOT NULL COMMENT '岗位ID',
PRIMARY KEY (`user_id`, `post_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '用户与岗位关联表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of sys_user_post
-- ----------------------------
INSERT INTO `sys_user_post` VALUES (1, 1);
-- ----------------------------
-- Table structure for sys_user_role
-- ----------------------------
DROP TABLE IF EXISTS `sys_user_role`;
CREATE TABLE `sys_user_role` (
`user_id` bigint NOT NULL COMMENT '用户ID',
`role_id` bigint NOT NULL COMMENT '角色ID',
PRIMARY KEY (`user_id`, `role_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '用户和角色关联表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of sys_user_role
-- ----------------------------
INSERT INTO `sys_user_role` VALUES (1, 1);
INSERT INTO `sys_user_role` VALUES (3, 3);
INSERT INTO `sys_user_role` VALUES (4, 4);
SET FOREIGN_KEY_CHECKS = 1;