mirror of
https://gitee.com/vben/vben-web.git
synced 2026-10-06 14:13:10 +08:00
Initial commit
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
# port 端口号
|
||||
VITE_PORT = 8888
|
||||
|
||||
# open 运行 npm run dev 时自动打开浏览器
|
||||
VITE_OPEN = false
|
||||
|
||||
# public path 配置线上环境路径(打包)、本地通过 http-server 访问时,请置空即可
|
||||
#VITE_PUBLIC_PATH = /vue-next-admin-preview/
|
||||
VITE_PUBLIC_PATH = ''
|
||||
@@ -0,0 +1,7 @@
|
||||
# 本地环境
|
||||
ENV = 'development'
|
||||
|
||||
# 本地环境接口地址
|
||||
#VITE_API_URL = 'http://localhost:5301/'
|
||||
VITE_API_URL = 'http://localhost:5000/'
|
||||
VITE_UI_URL = 'http://localhost:8888/'
|
||||
@@ -0,0 +1,7 @@
|
||||
# 线上环境
|
||||
ENV = 'production'
|
||||
|
||||
# 线上环境接口地址
|
||||
#VITE_API_URL = 'https://lyt-top.gitee.io/vue-next-admin-preview/'
|
||||
|
||||
VITE_API_URL = 'http://222.92.228.219:5301/'
|
||||
@@ -0,0 +1,18 @@
|
||||
|
||||
*.sh
|
||||
node_modules
|
||||
lib
|
||||
*.md
|
||||
*.scss
|
||||
*.woff
|
||||
*.ttf
|
||||
.vscode
|
||||
.idea
|
||||
dist
|
||||
mock
|
||||
public
|
||||
bin
|
||||
build
|
||||
config
|
||||
index.html
|
||||
src/assets
|
||||
@@ -0,0 +1,63 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
env: {
|
||||
browser: true,
|
||||
es2021: true,
|
||||
node: true,
|
||||
},
|
||||
parser: 'vue-eslint-parser',
|
||||
parserOptions: {
|
||||
ecmaVersion: 12,
|
||||
parser: '@typescript-eslint/parser',
|
||||
sourceType: 'module',
|
||||
},
|
||||
extends: ['plugin:vue/vue3-essential', 'plugin:vue/essential', 'eslint:recommended'],
|
||||
plugins: ['vue', '@typescript-eslint'],
|
||||
rules: {
|
||||
// http://eslint.cn/docs/rules/
|
||||
// https://eslint.vuejs.org/rules/
|
||||
'@type-eslint/ban-ts-ignore': 'off',
|
||||
'@type-eslint/explicit-function-return-type': 'off',
|
||||
'@type-eslint/no-explicit-any': 'off',
|
||||
'@type-eslint/no-var-requires': 'off',
|
||||
'@type-eslint/no-empty-function': 'off',
|
||||
'@type-eslint/no-use-before-define': 'off',
|
||||
'@type-eslint/ban-ts-comment': 'off',
|
||||
'@type-eslint/ban-types': 'off',
|
||||
'@type-eslint/no-non-null-assertion': 'off',
|
||||
'@type-eslint/explicit-module-boundary-types': 'off',
|
||||
'vue/custom-event-name-casing': 'off',
|
||||
'vue/attributes-order': 'off',
|
||||
'vue/one-component-per-file': 'off',
|
||||
'vue/html-closing-bracket-newline': 'off',
|
||||
'vue/max-attributes-per-line': 'off',
|
||||
'vue/multiline-html-element-content-newline': 'off',
|
||||
'vue/singleline-html-element-content-newline': 'off',
|
||||
'vue/attribute-hyphenation': 'off',
|
||||
'vue/html-self-closing': 'off',
|
||||
'vue/no-multiple-template-root': 'off',
|
||||
'vue/require-default-prop': 'off',
|
||||
'vue/no-v-model-argument': 'off',
|
||||
'vue/no-arrow-functions-in-watch': 'off',
|
||||
'vue/no-template-key': 'off',
|
||||
'vue/no-v-html': 'off',
|
||||
'vue/comment-directive': 'off',
|
||||
'vue/no-parsing-error': 'off',
|
||||
'vue/no-deprecated-v-on-native-modifier': 'off',
|
||||
'vue/multi-word-component-names': 'off',
|
||||
'no-useless-escape': 'off',
|
||||
'no-sparse-arrays': 'off',
|
||||
'no-prototype-builtins': 'off',
|
||||
'no-constant-condition': 'off',
|
||||
'no-use-before-define': 'off',
|
||||
'no-restricted-globals': 'off',
|
||||
'no-restricted-syntax': 'off',
|
||||
'generator-star-spacing': 'off',
|
||||
'no-unreachable': 'off',
|
||||
'no-multiple-template-root': 'off',
|
||||
'no-unused-vars': 'error',
|
||||
'no-v-model-argument': 'off',
|
||||
'no-case-declarations': 'off',
|
||||
'no-console': 'error',
|
||||
},
|
||||
};
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
.DS_Store
|
||||
node_modules
|
||||
/dist
|
||||
|
||||
|
||||
# local env files
|
||||
.env.local
|
||||
.env.*.local
|
||||
|
||||
# Log files
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
|
||||
# Editor directories and files
|
||||
.idea
|
||||
.vscode
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
@@ -0,0 +1,39 @@
|
||||
module.exports = {
|
||||
// 一行最多多少个字符
|
||||
printWidth: 150,
|
||||
// 指定每个缩进级别的空格数
|
||||
tabWidth: 2,
|
||||
// 使用制表符而不是空格缩进行
|
||||
useTabs: true,
|
||||
// 在语句末尾打印分号
|
||||
semi: true,
|
||||
// 使用单引号而不是双引号
|
||||
singleQuote: true,
|
||||
// 更改引用对象属性的时间 可选值"<as-needed|consistent|preserve>"
|
||||
quoteProps: 'as-needed',
|
||||
// 在JSX中使用单引号而不是双引号
|
||||
jsxSingleQuote: false,
|
||||
// 多行时尽可能打印尾随逗号。(例如,单行数组永远不会出现逗号结尾。) 可选值"<none|es5|all>",默认none
|
||||
trailingComma: 'es5',
|
||||
// 在对象文字中的括号之间打印空格
|
||||
bracketSpacing: true,
|
||||
// jsx 标签的反尖括号需要换行
|
||||
jsxBracketSameLine: false,
|
||||
// 在单独的箭头函数参数周围包括括号 always:(x) => x \ avoid:x => x
|
||||
arrowParens: 'always',
|
||||
// 这两个选项可用于格式化以给定字符偏移量(分别包括和不包括)开始和结束的代码
|
||||
rangeStart: 0,
|
||||
rangeEnd: Infinity,
|
||||
// 指定要使用的解析器,不需要写文件开头的 @prettier
|
||||
requirePragma: false,
|
||||
// 不需要自动在文件开头插入 @prettier
|
||||
insertPragma: false,
|
||||
// 使用默认的折行标准 always\never\preserve
|
||||
proseWrap: 'preserve',
|
||||
// 指定HTML文件的全局空格敏感度 css\strict\ignore
|
||||
htmlWhitespaceSensitivity: 'css',
|
||||
// Vue文件脚本和样式标签缩进
|
||||
vueIndentScriptAndStyle: false,
|
||||
// 换行符使用 lf 结尾是 可选值"<auto|lf|crlf|cr>"
|
||||
endOfLine: 'lf',
|
||||
};
|
||||
+307
@@ -0,0 +1,307 @@
|
||||
# <a href="https://gitee.com/lyt-top/vue-next-admin" target="_blank">vue-next-admin 更新日志</a>
|
||||
|
||||
🎉🎉🔥 `vue-next-admin` 基于 vue3.x 、Typescript、vite、Element plus 等,适配手机、平板、pc 的后台开源免费模板库(vue2.x 请切换 vue-prev-admin 分支)
|
||||
|
||||
## 2.0.2
|
||||
|
||||
`2022.03.04`
|
||||
|
||||
- 🌟 更新 依赖更新最新版本
|
||||
- 🎯 优化 Alert 提示添加边框
|
||||
- 🎯 优化 功能 / 数字滚动 演示界面
|
||||
- 🐞 修复 全局主题按钮颜色 :active 问题
|
||||
- 🐞 修复 Dropdown 下拉菜单样式问题
|
||||
- 🐞 修复 SvgIcon 图标组件动态切换时报警告问题,[SvgIcon 改变 name 时可能导致图像不显示](https://gitee.com/lyt-top/vue-next-admin/issues/I4VGE0),感谢@axcc1234
|
||||
|
||||
## 2.0.1
|
||||
|
||||
`2022.02.25`
|
||||
|
||||
- 🌟 更新 依赖更新最新版本
|
||||
- 🎯 优化 svgIcon 图标组件
|
||||
- 🎯 优化 vite.config.ts 打包,感谢群友@YourObjec
|
||||
- 🐞 修复 tagViews 开启图标不显示问题(风格 5),感谢群友@坏人
|
||||
- 🐞 修复 [Element Plus 1.2.0-beta.6 以后的版本 el-table 在移动端无法左右滑动](https://gitee.com/lyt-top/vue-next-admin/issues/I4UPTP),感谢@YGDada
|
||||
|
||||
## 2.0.0
|
||||
|
||||
`2022.02.21`
|
||||
|
||||
⚡⚡⚡ 此版本为破环性更新,优化内容如下:(谨慎更新!谨慎更新!!谨慎更新!!!)。演示界面建议直接覆盖文件。如需使用之前版本,请前往[gitee 发行版](https://gitee.com/lyt-top/vue-next-admin/releases) 进行对应版本下载。基础版会基于 `master` 分支进行修改
|
||||
|
||||
- 🌟 更新 依赖更新最新版本
|
||||
- 🌟 更新 登录页、首页
|
||||
- 💔 移除 vue-web-screen-shot
|
||||
- 💔 移除 城市多级联动,完整 json 数据请去 [vue-next-admin-images/menu](https://gitee.com/lyt-top/vue-next-admin-images/tree/master/menu) 仓库查看
|
||||
- 💔 移除 功能/echartsTree 树图
|
||||
- 💔 移除 其它设置/Tagsview 风格 2、Tagsview 风格 3
|
||||
- 💔 移除 功能/验证器
|
||||
- 🚧 调整 src/api 编写方式
|
||||
- 🚧 调整 自定义封装公用组件演示,更好的维护
|
||||
- 🎉 新增 Volar 支持,vs code 配置参考 [Vue Language Features (Volar)](https://lyt-top.gitee.io/vue-next-admin-doc-preview/home/vscode/)
|
||||
- 🎉 新增 `SvgIcon` 支持本地 svg 图标使用
|
||||
- 🎉 新增 表单表格验证演示
|
||||
- 🎯 优化 全局主题(移除 success、info、warning、danger)
|
||||
- 🎯 优化 工作流(开源)
|
||||
- 🎯 优化 element plus svg 图标,`elementXXX` 改成 `ele-XXX`
|
||||
- 🌈 重构 深色模式
|
||||
- 🌹 合并 [处理 parent 的 h100 由于外层有 min-height 导致失效的问题](https://gitee.com/lyt-top/vue-next-admin/pulls/20),感谢@MaxNull、@21030442-mao
|
||||
- 🐞 修复 element plus 升级 `^1.3.0-beta.5` 后 组件 size 大小问题(大改:涉及布局、演示界面)
|
||||
- 🐞 修复 vs code 使用 Vue Language Features (Volar) 插件 代码报红问题(可以把公用的 ts 类型定义封装起来公用)
|
||||
|
||||
## 1.2.2
|
||||
|
||||
`2021.12.21`
|
||||
|
||||
- 🌟 更新 依赖更新最新版本
|
||||
- 🎯 优化 iframes 滚动条问题
|
||||
- 🎯 优化 部署后每次都要强制刷新清浏览器缓存问题
|
||||
- 🎉 新增 工具类百分比验证演示
|
||||
- 🐞 修复 [tag-view 标签右键会超出浏览器 #I4KN78](https://gitee.com/lyt-top/vue-next-admin/issues/I4KN78)
|
||||
|
||||
## 1.2.1
|
||||
|
||||
`2021.12.12`
|
||||
|
||||
- 🌟 更新 依赖更新最新版本
|
||||
- 🎯 优化 cropper 裁剪时卡顿问题 [#I4M2VQ](https://gitee.com/lyt-top/vue-next-admin/issues/I4M2VQ)
|
||||
- 🎯 优化 Wangeditor 富文本编辑器的问题 [#I4LPC1](https://gitee.com/lyt-top/vue-next-admin/issues/I4LPC1)、[#I4LM7I](https://gitee.com/lyt-top/vue-next-admin/issues/I4LM7I)
|
||||
- 🐞 修复 浏览器标题问题
|
||||
- 🐞 修复 element plus svg 图标引入
|
||||
- 🐞 修复 工作流不可以拖线连接问题
|
||||
|
||||
## 1.2.0
|
||||
|
||||
`2021.11.28`
|
||||
|
||||
- 🌟 更新 依赖更新最新版本
|
||||
- 🎯 优化 深色模式
|
||||
- 🎯 优化 `/@/utils` 文件夹,合并删除单一内容
|
||||
- 🎯 优化 系统设置:菜单管理(新增、修改)、角色管理(新增菜单权限)、用户管理、部门管理、字典管理
|
||||
- 🎯 优化 登录界面逻辑、权限管理逻辑
|
||||
- 🎯 优化 同步 [vue-next-admin-images](https://gitee.com/lyt-top/vue-next-admin-images/tree/master/menu) 后端控制菜单模拟数据
|
||||
- 🎉 新增 适配 Font Icon 向 SVG Icon 迁移(改动大,"element-plus": "^1.2.0-beta.4" 谨慎更新)
|
||||
- 🐞 修复 热更新问题,感谢@甜蜜蜜
|
||||
- 🐞 修复 页面/element 字体图标演示
|
||||
- 🐞 修复 功能/图标选择器演示,新增高级功能 [issues #I4GJXQ](https://gitee.com/lyt-top/vue-next-admin/issues/I4GJXQ)
|
||||
|
||||
## 1.1.2
|
||||
|
||||
`2021.10.17`
|
||||
|
||||
- 🌟 更新 依赖更新最新版本
|
||||
- 🐞 修复 开启全屏时,刷新界面被还原成未全屏的状态
|
||||
- 🎯 优化 tagsView 右键菜单关闭逻辑
|
||||
- 🎯 优化 wangeditor 富文本编辑器(增加双向绑定)
|
||||
- 🎉 新增 工作流(暂不开源)
|
||||
- 🎉 新增 基础版 ts(不带国际化),切换 `vue-next-admin-template` 分支
|
||||
|
||||
## 1.1.1
|
||||
|
||||
`2021.09.25`
|
||||
|
||||
- 🌟 更新 依赖更新最新版本(`"element-plus": "^1.1.0-beta.13"` 版本运行错误,`^1.1.0-beta.16`修复横向菜单卡死问题)
|
||||
- 🐞 修复 Dialog 弹窗位置错误、Drawer 抽屉内边距、el-menu 菜单收起时背景色问题
|
||||
- 🎯 优化 锁屏界面自动锁屏(s/秒)必须设置至少 1 秒
|
||||
- 🎉 新增 分栏布局,鼠标移入当前项时,显示当前项菜单内容
|
||||
- 🎉 新增 工作流(未完成)
|
||||
|
||||
## 1.1.0
|
||||
|
||||
`2021.09.10`
|
||||
|
||||
- 🌟 更新 依赖更新最新版本
|
||||
- 🎯 优化 小屏模式下登录页二维码遮挡标题问题
|
||||
- 🎉 新增 图片验证器
|
||||
- 🎉 新增 动态复杂表单
|
||||
- 🎉 新增 工作流(未完成)
|
||||
- 🎉 新增 深色主题(伪深色,样式变动大,谨慎更新)
|
||||
|
||||
## 1.0.18
|
||||
|
||||
`2021.08.29`
|
||||
|
||||
- 🌟 更新 依赖更新最新版本
|
||||
- 🎯 优化 权限组件去掉顶级 div(`/src/components/auth`)
|
||||
- 🎉 新增 布局配置添加恢复默认按钮
|
||||
- 🐞 修复 升级 <a href="https://element-plus.gitee.io/#/zh-CN/component/changelog" target="_blank">element plus 1.1.0-beta.7</a>后项目无法启动、el-menu 菜单
|
||||
- 🐞 修复 表格固定列时的层级、设置了相对定位时,遮挡左侧导航菜单问题
|
||||
|
||||
## 1.0.17
|
||||
|
||||
`2021.08.22`
|
||||
|
||||
- 🌟 更新 依赖更新最新版本
|
||||
- 🎯 优化 去除设置布局切换,重置主题样式(initSetLayoutChange),切换布局需手动设置样式,设置的样式自动同步各布局
|
||||
- 🎯 优化 Dropdown 下拉菜单用户账号靠边时换行问题
|
||||
- 🎯 优化 左侧导航菜单,共用菜单树,防止 `布局配置` 设置 `菜单 / 顶栏` 时,样式丢失等问题
|
||||
- 🐞 修复 固定 header 后没有回到顶部的 bug,拉取项目后运行不起来的 bug。<a href="https://gitee.com/lyt-top/vue-next-admin/pulls/14" target="_blank">!14</a>,感谢<a href="https://gitee.com/wjs0509" target="_blank">@wjs0509</a>
|
||||
- 🐞 修复 tagView 右键全屏后,浏览器窗口大小发生任何变化都会导致左边菜单显示出来,并且可点击打开对应页面。<a href="https://gitee.com/lyt-top/vue-next-admin/issues/I46E6T" target="_blank">I46E6T</a>
|
||||
- 🐞 修复 默认设置 `菜单 / 顶栏` 样式不生效问题(/@/src/store/modules/themeConfig.ts)
|
||||
|
||||
## 1.0.16
|
||||
|
||||
`2021.08.14`
|
||||
|
||||
- 🌟 更新 依赖更新最新版本
|
||||
- 🎯 优化 菜单高亮(详情且详情设置了 meta.isHide 时,顶级菜单高亮),感谢群友@YourObject
|
||||
- 🎯 优化 详情路径写法:如父级(/pages/filtering),那么详情为(/pages/filtering/details?id=1)。这样写可实现(详情时,父级菜单高亮),否则写成(/pages/filteringDetails?id=1)顶级菜单将不会高亮。可参考:`页面/过滤筛选组件`,点击当前图片进行测试
|
||||
- 🎯 优化 tagsView 右键菜单全屏时,打开的界面高度问题
|
||||
- 🎯 优化 图表批量 resize 问题
|
||||
- 🐞 修复 菜单收起时(设置全局主题:primary 且有二级菜单时),文字高亮颜色不对
|
||||
- 🐞 修复 国际化 <a href="https://gitee.com/lyt-top/vue-next-admin/issues/I43NPE" target="_blank">#I43NPE</a>。可参考:`页面/过滤筛选组件`,点击顶部语言切换,进行底部分页国际化查看
|
||||
|
||||
## 1.0.15
|
||||
|
||||
`2021.08.06`
|
||||
|
||||
- 🌟 更新 依赖更新最新版本
|
||||
- 🎯 优化 tagsView 右键菜单点击时的字段名(id 已修改成 contextMenuClickId)与路由中返回的 id 名冲突问题,感谢群友@伯牙已遇钟子期
|
||||
- 🎉 新增 多个 form 表单验证界面演示
|
||||
|
||||
## 1.0.14
|
||||
|
||||
`2021.07.29`
|
||||
|
||||
- 🌟 更新 依赖更新最新版本(vue、vuex、vue-router),出现问题,请手动降级。版本查看:<a href="https://www.npmjs.com/" target="_blank">vnpm</a>
|
||||
- 🎯 优化 数据可视化图表演示加载卡顿问题、优化有图表的演示界面
|
||||
- 🎯 优化 路由参数演示界面
|
||||
- 🎯 优化 tagsView 操作演示界面,由于存在相同路由多标签,必须要传全部参数值(query 或者 params)
|
||||
- 🎉 新增 开启 TagsView 共用,开启时:(多个路由菜单共用一个详情组件(参数为后点击的覆盖前面点击的),tagsView 中只会出现一个(不支持同时出现多个 tagsView 标签))。关闭时:(多个路由菜单共用一个详情组件,参数不同,会同时出现多个 tagsView 标签)
|
||||
- 🐞 修复 tagsView 共用(单标签)时,右键菜单功能点击,参数不对的问题(第 2n+个参数未覆盖第一个参数值)
|
||||
- 🐞 修复 多 tagsView 标签(参数不同)、单个 tagsView 标签公用(参数不同)所带来的刷新功能、横向自动滚动等问题
|
||||
- 🐞 修复 处理全屏若干问题,<a href="https://gitee.com/lyt-top/vue-next-admin/pulls/12" target="_blank">pr!12</a>,感谢群友@另一个前端
|
||||
|
||||
## 1.0.13
|
||||
|
||||
`2021.07.25`
|
||||
|
||||
- 🌟 更新 依赖更新最新版本
|
||||
- 🎉 新增 数据可视化演示界面(/visualizingDemo1、/visualizingDemo2)
|
||||
- 🎉 新增 登录页扫码登录
|
||||
|
||||
## 1.0.12
|
||||
|
||||
`2021.07.16`
|
||||
|
||||
- 🌟 更新 依赖更新最新版本
|
||||
- 🎉 新增 数据可视化演示空界面(待完善)
|
||||
- 🎯 优化 tagsView 动态路由(xxx/:id/:name)时的右键菜单刷新、关闭其它时参数丢失问题(2021.07.15 优化)
|
||||
- 🐞 修复 路由带参数时,复制路径到登录页,跳转后参数消失的问题
|
||||
- 🐞 修复 设置多个外链,点击后,页面内容停留在上一个内容(内容未改变)、国际化处理、打开新窗口 sessionStorage 共享等
|
||||
|
||||
## 1.0.11
|
||||
|
||||
`2021.07.14`
|
||||
|
||||
- 🌟 更新 依赖更新最新版本
|
||||
- 🎉 新增 路由参数、图片懒加载界面演示
|
||||
- ⚠️ 警告 Form 表单 `binding value must be a string or number`,解决:加上 `label-position="top"` 不报警告(等待官方修复)
|
||||
- 🎯 优化 锁屏界面动画效果、首页图表显示
|
||||
- 🎯 优化 tagsView 右键菜单 `关闭` 功能逻辑
|
||||
- 🐞 修复 开启 TagsView 拖拽报错及小于 `1000px` 时自动设置禁止拖拽(<a href="https://gitee.com/lyt-top/vue-next-admin/issues/I3ZRRI" target="_blank">#I3ZRRI</a>)
|
||||
- 🐞 修复 `iframe 内嵌、外链` 高度问题,使用 computed 进行计算
|
||||
- 🐞 修复 默认布局开启 `侧边栏 Logo` 与关闭 `菜单水平折叠`,切换到横向布局时,菜单看不见的问题
|
||||
- 🐞 修复 切换不同布局时,再去开启 `经典布局分割菜单` 功能不生效问题
|
||||
- 🐞 修复 浏览器窗口标题中/英文切换不实时生效的问题
|
||||
- 🐞 修复 切换布局时,某些功能不可以使用。部分界面不需要取消事件监听(proxy.mittBus.off('xxx'))
|
||||
- 🐞 修复 动态路由带参数,router-link 跳转问题(<a href="hhttps://gitee.com/lyt-top/vue-next-admin/issues/I3YX6G" target="_blank">#I3YX6G</a>)
|
||||
- 🐞 修复 横向菜单有二级菜单时,点击子级菜单不高亮问题
|
||||
- 🐞 修复 功能 tagsView 操作演示不生效
|
||||
|
||||
## 1.0.10
|
||||
|
||||
`2021.07.07`
|
||||
|
||||
- 🌟 更新 依赖更新最新版本(字体图标无问题)
|
||||
- 🎯 优化 内嵌 iframe、外链,解决 tagsView 刷新问题
|
||||
|
||||
## 1.0.9
|
||||
|
||||
`2021.07.02`
|
||||
|
||||
- 🌟 更新 依赖更新最新版本
|
||||
- 🎯 优化 图标选择器设置宽度、v-model 等问题
|
||||
- 🎯 优化 滚动通知栏在手机上的体验
|
||||
- 🎯 优化 系统管理/新增菜单(编辑菜单),使用 `图标选择器` 进行模拟
|
||||
- 🎯 优化 字体图标(自动载入) 逻辑
|
||||
- 🐞 修复 screenfull 全屏时,按键盘 esc 键图标不改变问题,感谢群友@伯牙已遇钟子期
|
||||
|
||||
## 1.0.8
|
||||
|
||||
`2021.06.29`
|
||||
|
||||
- 🌟 更新 依赖更新最新版本
|
||||
- 🎉 新增 表单中英文切换演示
|
||||
- 🎯 优化 登录页查看密码 icon 图标
|
||||
- 🎯 优化 图标选择器
|
||||
- 🎯 优化 拖动指令
|
||||
- 🐞 修复 form 表单在页面小于 576px 时的排版问题
|
||||
|
||||
## 1.0.7
|
||||
|
||||
`2021.06.24`
|
||||
|
||||
- 🌟 更新 依赖更新最新版本
|
||||
- 🎉 新增 拖动指令及其演示界面
|
||||
- 🎯 优化 锁屏界面,解锁提示
|
||||
- 🎯 优化 登录页在手机上显示的效果
|
||||
|
||||
## 1.0.6
|
||||
|
||||
`2021.06.23`
|
||||
|
||||
- 🎯 优化 去掉内嵌 iframe 内边距(padding)
|
||||
- 🎯 优化 城市多级联动组件
|
||||
- 🎯 优化 Tree 树形控件改成表格组件
|
||||
- 🐞 修复 Cascader 级联选择器高度问题
|
||||
|
||||
## 1.0.5
|
||||
|
||||
`2021.06.22`
|
||||
|
||||
- 🌟 更新 vite 降级为@vite2.3.7,降级方法 `cnpm install vite@2.3.7`,防止 element plus 字体图标消失
|
||||
- 🐞 修复 开启后端控制路由(isRequestRoutes = true)时,内嵌 iframe、外链不可使用的问题
|
||||
|
||||
## 1.0.4
|
||||
|
||||
`2021.06.19`
|
||||
|
||||
- 🌟 更新 依赖更新最新版本("vite": "^2.3.7")热更新无问题
|
||||
- 🎉 新增 深克隆工具,方便开发,感谢<a href="https://gitee.com/kangert" target="_blank">@kangert</a>(<a href="https://gitee.com/lyt-top/vue-next-admin/pulls/6" target="_blank">#6</a>)
|
||||
- 🎯 优化 vuex 模块自动导入。感谢<a href="https://gitee.com/kangert" target="_blank">@kangert</a>(<a href="https://gitee.com/lyt-top/vue-next-admin/pulls/4" target="_blank">#4</a>),感谢群友@web 小学生-第五君
|
||||
- 🎯 优化 类型定义提高编码体验,修复不能将类型“string | undefined”分配给类型“string”的问题。感谢<a href="https://gitee.com/kangert" target="_blank">@kangert</a>(<a href="https://gitee.com/lyt-top/vue-next-admin/pulls/5" target="_blank">#5</a>)
|
||||
- 🎯 优化 `layout` 文件夹移动到与 `views` 文件夹同级(改动较大,`/@/views/layout` 变成 `/@/layout`)
|
||||
- 🎯 优化 页面有 `console.log` 时 `eslint` 不生效问题
|
||||
- 🎯 优化 页面、ts 中 `any` 类型问题(改动较大)
|
||||
- 🎯 优化 登录页在手机上显示的效果
|
||||
- 🎯 优化 多行注释信息,鼠标放到方法名即可查看,更加直观的知道方法参数等。引入方法时需去掉以 `.ts` 结尾的后缀(改动较大)
|
||||
- 🎯 优化 移除 `utils/storage.ts` 下的旧写法(改动较大)
|
||||
- 🎯 优化 拆分 `router` 下内容,路由、前端、后端控制分开写,方便理解
|
||||
- 🐞 修复 鼠标移入顶部用户信息栏 `开/关全屏` 文字反向问题
|
||||
- 🐞 修复 热更新时,NextLoading(界面 loading) 不消失问题 `window.nextLoading === undefined`
|
||||
- 🐞 修复 vuex 中不可以使用 `/@/api/xxx` 下的接口调用问题
|
||||
|
||||
## 1.0.3
|
||||
|
||||
`2021.06.02`
|
||||
|
||||
- ❄️ 删除 G6 思维导图界面
|
||||
- 🌟 更新 手动更新 vue、vue-router、vuex 到最近最多人使用的版本,出现不可预测的问题请降低版本。版本查看:<a href="https://www.npmjs.com/package/vue" target="_blank">vue 版本查看</a>
|
||||
- 🐞 修复 开启后端控制路由 `isRequestRoutes` 在非首页刷新页面后,回到首页的问题,感谢群友@伯牙已遇钟子期
|
||||
|
||||
## 1.0.2
|
||||
|
||||
`2021.06.01`
|
||||
|
||||
- 🌟 更新 依赖更新最新版本
|
||||
- 🐞 修复 菜单搜索中文不可以搜索的问题,感谢群友@逍遥天意
|
||||
|
||||
## 1.0.1
|
||||
|
||||
`2021.05.31`
|
||||
|
||||
- 🎉 新增 更新日志文件 `CHANGELOG.md`,以后每次更新都会在这里显示对应内容
|
||||
- 🌟 更新 依赖更新最新版本
|
||||
- 🐞 修复 分栏、经典布局路由设置 `meta.isHide` 为 `true` 时报错问题,感谢群友@29、@芭芭拉
|
||||
- 🐞 修复 经典布局点击 `tagsView` 左侧菜单数据不变问题
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 lyt-Top
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,135 @@
|
||||
<div align="center">
|
||||
<img src="https://gitee.com/lyt-top/vue-next-admin-images/raw/master/logo/logo-text.svg">
|
||||
<p align="center">
|
||||
<a href="https://v3.vuejs.org/" target="_blank">
|
||||
<img src="https://img.shields.io/badge/vue.js-vue3.x-green" alt="vue">
|
||||
</a>
|
||||
<a href="https://element-plus.gitee.io/#/zh-CN/component/changelog" target="_blank">
|
||||
<img src="https://img.shields.io/badge/element--plus-%3E1.0.0-blue" alt="element plus">
|
||||
</a>
|
||||
<a href="https://www.tslang.cn/" target="_blank">
|
||||
<img src="https://img.shields.io/badge/typescript-%3E4.0.0-blue" alt="typescript">
|
||||
</a>
|
||||
<a href="https://vitejs.dev/" target="_blank">
|
||||
<img src="https://img.shields.io/badge/vite-%3E2.0.0-yellow" alt="vite">
|
||||
</a>
|
||||
<a href="https://gitee.com/lyt-top/vue-next-admin/blob/master/LICENSE" target="_blank">
|
||||
<img src="https://img.shields.io/badge/license-MIT-success" alt="license">
|
||||
</a>
|
||||
</p>
|
||||
<p> </p>
|
||||
</div>
|
||||
|
||||
#### 🌈 介绍
|
||||
|
||||
基于 vue3.x + CompositionAPI + typescript + vite + element plus + vue-router-next + next.vuex,适配手机、平板、pc 的后台开源免费模板,希望减少工作量,帮助大家实现快速开发。
|
||||
|
||||
#### ⛱️ 线上预览
|
||||
|
||||
- vue3.x 版本预览(vue-next-admin)<a href="https://lyt-top.gitee.io/vue-next-admin-preview/#/login" target="_blank">https://lyt-top.gitee.io/vue-next-admin-preview/#/login</a>
|
||||
- vue2.x 版本预览(vue-prev-admin)<a href="https://lyt-top.gitee.io/vue-prev-admin-preview/#/login" target="_blank">https://lyt-top.gitee.io/vue-prev-admin-preview/#/login</a>
|
||||
|
||||
#### 💒 代码仓库
|
||||
|
||||
- vue3.x 版本 <a href="https://gitee.com/lyt-top/vue-next-admin" target="_blank">https://gitee.com/lyt-top/vue-next-admin</a>
|
||||
- vue2.x 版本 <a href="https://gitee.com/lyt-top/vue-next-admin/tree/vue-prev-admin" target="_blank">https://gitee.com/lyt-top/vue-next-admin/tree/vue-prev-admin</a>
|
||||
|
||||
#### 🚧 安装 cnpm、yarn
|
||||
|
||||
- 复制代码(桌面 cmd 运行) `npm install -g cnpm --registry=https://registry.npm.taobao.org`
|
||||
- 复制代码(桌面 cmd 运行) `npm install -g yarn`
|
||||
|
||||
#### 🏭 环境支持
|
||||
|
||||
| Edge | last 2 versions | last 2 versions | last 2 versions |
|
||||
| ------------------------------------------------------------------------ | --------------------------------------------------------------------------------- | ------------------------------------------------------------------------------ | ------------------------------------------------------------------------------ |
|
||||
|  |  |  |  |
|
||||
|
||||
> 由于 Vue3 不再支持 IE11,故而 ElementPlus 也不支持 IE11 及之前版本。
|
||||
|
||||
#### ⚡ 使用说明
|
||||
|
||||
建议使用 cnpm,因为 yarn 有时会报错。<a href="http://nodejs.cn/" target="_blank">node 版本 > 12xx.xx.x</a>
|
||||
|
||||
```bash
|
||||
# 克隆项目
|
||||
git clone https://gitee.com/lyt-top/vue-next-admin.git
|
||||
|
||||
# 进入项目
|
||||
cd vue-next-admin
|
||||
|
||||
# 安装依赖
|
||||
cnpm install
|
||||
|
||||
# 运行项目
|
||||
cnpm run dev
|
||||
|
||||
# 打包发布
|
||||
cnpm run build
|
||||
```
|
||||
|
||||
#### 💯 学习交流加 QQ 群
|
||||
|
||||
- 若加群了没同意(一般秒过),那就是群满了(500 人群),请换一个群试试,3 群未满
|
||||
- 查看开发文档:<a href="https://lyt-top.gitee.io/vue-next-admin-doc-preview" target="_blank">vue-next-admin-doc</a>
|
||||
- 群号码:
|
||||
1 群:<a target="_blank" href="https://qm.qq.com/cgi-bin/qm/qr?k=RdUY97Vx0T0vZ_1OOu-X1yFNkWgDwbjC&jump_from=webapi">665452019</a>
|
||||
2 群:<a target="_blank" href="https://qm.qq.com/cgi-bin/qm/qr?k=zVfy3gNy7pNWVK3kMduDzwU369PZg2fw&jump_from=webapi">766356862</a>
|
||||
3 群:<a target="_blank" href="https://qm.qq.com/cgi-bin/qm/qr?k=02EWb5P2JkP-8iwzaDadgFdxA0HSHPpn&jump_from=webapi">795345435</a>
|
||||
|
||||
<a target="_blank" href="https://qm.qq.com/cgi-bin/qm/qr?k=RdUY97Vx0T0vZ_1OOu-X1yFNkWgDwbjC&jump_from=webapi">
|
||||
<img src="https://gitee.com/lyt-top/vue-next-admin-images/raw/master/user/qq1.png" width="220" height="220" alt="vue-next-admin 讨论群1" title="vue-next-admin 讨论群1"/>
|
||||
</a>
|
||||
<a target="_blank" href="https://qm.qq.com/cgi-bin/qm/qr?k=zVfy3gNy7pNWVK3kMduDzwU369PZg2fw&jump_from=webapi">
|
||||
<img src="https://gitee.com/lyt-top/vue-next-admin-images/raw/master/user/qq2.png" width="220" height="220" alt="vue-next-admin 讨论群2" title="vue-next-admin 讨论群2"/>
|
||||
</a>
|
||||
<a target="_blank" href="https://qm.qq.com/cgi-bin/qm/qr?k=02EWb5P2JkP-8iwzaDadgFdxA0HSHPpn&jump_from=webapi">
|
||||
<img src="https://gitee.com/lyt-top/vue-next-admin-images/raw/master/user/qq3.png" width="220" height="220" alt="vue-next-admin 讨论群3" title="vue-next-admin 讨论群3"/>
|
||||
</a>
|
||||
|
||||
#### 💒 集成后端
|
||||
|
||||
- <a target="_blank" href="https://github.com/PandaGoAdmin/PandaX">@熊猫 PandaGoAdmin</a>
|
||||
- <a target="_blank" href="https://www.gnet.top/public">@甜蜜蜜 GoPro 平台</a>
|
||||
|
||||
#### ❤️ 鸣谢列表
|
||||
|
||||
- <a href="https://github.com/vuejs/vue" target="_blank">vue</a>
|
||||
- <a href="https://github.com/vuejs/vue-next" target="_blank">vue-next</a>
|
||||
- <a href="https://github.com/ElemeFE/element" target="_blank">element-ui</a>
|
||||
- <a href="https://github.com/element-plus/element-plus" target="_blank">element-plus</a>
|
||||
- <a href="https://github.com/vuejs/vue-router-next" target="_blank">vue-router-next</a>
|
||||
- <a href="https://github.com/vuejs/vuex" target="_blank">vuex</a>
|
||||
- <a href="https://github.com/apache/echarts" target="_blank">echarts</a>
|
||||
- <a href="https://github.com/axios/axios" target="_blank">axios</a>
|
||||
- <a href="https://github.com/zenorocha/clipboard.js" target="_blank">clipboard</a>
|
||||
- <a href="https://github.com/inorganik/countUp.js" target="_blank">countUp</a>
|
||||
- <a href="https://github.com/developit/mitt" target="_blank">mitt</a>
|
||||
- <a href="https://github.com/rstacruz/nprogress" target="_blank">nprogress</a>
|
||||
- <a href="https://github.com/sindresorhus/screenfull.js" target="_blank">screenfull</a>
|
||||
- <a href="https://github.com/SortableJS/Sortable" target="_blank">sortablejs</a>
|
||||
- <a href="https://github.com/sass/sass" target="_blank">sass</a>
|
||||
- <a href="https://github.com/microsoft/TypeScript" target="_blank">typescript</a>
|
||||
- <a href="https://github.com/vitejs/vite" target="_blank">vite</a>
|
||||
- <a href="https://github.com/wangeditor-team/wangEditor" target="_blank">wangeditor</a>
|
||||
- <a href="https://github.com/fengyuanchen/cropperjs" target="_blank">cropperjs</a>
|
||||
- <a href="https://github.com/davidshimjs/qrcodejs" target="_blank">qrcodejs</a>
|
||||
- <a href="https://github.com/crabbly/Print.js" target="_blank">print-js</a>
|
||||
- <a href="https://github.com/likaia/screen-shot" target="_blank">vue-web-screen-shot</a>
|
||||
- <a href="https://github.com/jbaysolutions/vue-grid-layout" target="_blank">vue-grid-layout</a>
|
||||
- <a href="https://github.com/antoniandre/splitpanes" target="_blank">splitpanes</a>
|
||||
- <a href="https://github.com/yimijianfang/vue-drag-verify" target="_blank">vue-drag-verify</a>
|
||||
- <a href="https://github.com/jsplumb/jsplumb" target="_blank">jsplumb</a>
|
||||
|
||||
#### 💕 特别感谢
|
||||
|
||||
特别感谢老哥们的建议、指导与帮忙。谢谢!
|
||||
|
||||
- <a href="https://gitee.com/click33/sa-plus" target="_blank">@省长</a>
|
||||
- <a href="https://gitee.com/jskz/Jskz-SpringCloud" target="_blank">@唐参
|
||||
- <a href="https://gitee.com/chuange" target="_blank">@川歌</a>
|
||||
|
||||
#### 💌 支持作者
|
||||
|
||||
如果觉得框架不错,或者已经在使用了,希望你可以去 <a target="_blank" href="https://github.com/lyt-Top/vue-next-admin">Github</a> 或者
|
||||
<a target="_blank" href="https://gitee.com/lyt-top/vue-next-admin">Gitee</a> 帮我点个 ⭐ Star,这将是对我极大的鼓励与支持。
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta
|
||||
name="keywords"
|
||||
content="vue-next-admin,vue-prev-admin,vue-admin-wonderful,后台管理系统一站式平台模板,希望可以帮你完成快速开发。vue2.x,vue2.0,vue2,vue3,vue3.x,vue3.0,CompositionAPI,typescript,element plus,element,plus,admin,wonderful,wonderful-next,vue-next-admin,vite,vite-admin,快速,高效,后台模板,后台系统,管理系统"
|
||||
/>
|
||||
<meta
|
||||
name="description"
|
||||
content="vue-next-admin,基于 vue3 + CompositionAPI + typescript + vite + element plus,适配手机、平板、pc 的后台开源免费管理系统模板!vue-prev-admin,基于 vue2 + element ui,适配手机、平板、pc 的后台开源免费管理系统模板!"
|
||||
/>
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<title>Vboot</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="text/javascript">
|
||||
var _hmt = _hmt || [];
|
||||
(function () {
|
||||
var hm = document.createElement('script');
|
||||
hm.src = 'https://hm.baidu.com/hm.js?d9c8b87d10717013641458b300c552e4';
|
||||
var s = document.getElementsByTagName('script')[0];
|
||||
s.parentNode.insertBefore(hm, s);
|
||||
})();
|
||||
</script>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
<script type="text/javascript" src="https://api.map.baidu.com/api?v=3.0&ak=wsijQt8sLXrCW71YesmispvYHitfG9gv&s=1"></script>
|
||||
</body>
|
||||
</html>
|
||||
Generated
+8306
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,85 @@
|
||||
{
|
||||
"name": "vue-next-admin",
|
||||
"version": "2.0.2",
|
||||
"description": "vue3 vite next admin template",
|
||||
"author": "lyt_20201208",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"dev": "vite --force",
|
||||
"build": "vite build",
|
||||
"lint-fix": "eslint --fix --ext .js --ext .jsx --ext .vue src/"
|
||||
},
|
||||
"dependencies": {
|
||||
"@element-plus/icons-vue": "^1.0.0",
|
||||
"axios": "^0.26.0",
|
||||
"bpmn-js": "^8.1.0",
|
||||
"bpmn-js-properties-panel": "^0.39.0",
|
||||
"codemirror": "^5.59.2",
|
||||
"countup.js": "^2.1.0",
|
||||
"cropperjs": "^1.5.12",
|
||||
"echarts": "^5.3.0",
|
||||
"echarts-gl": "^2.0.9",
|
||||
"echarts-wordcloud": "^2.0.0",
|
||||
"element-plus": "^2.0.4",
|
||||
"jsplumb": "^2.15.6",
|
||||
"mitt": "^3.0.0",
|
||||
"nprogress": "^0.2.0",
|
||||
"print-js": "^1.6.0",
|
||||
"qrcodejs2-fixes": "^0.0.2",
|
||||
"screenfull": "^6.0.1",
|
||||
"sortablejs": "^1.14.0",
|
||||
"splitpanes": "^3.1.1",
|
||||
"vform3-builds": "^3.0.6",
|
||||
"vue": "^3.2.31",
|
||||
"vue-clipboard3": "^1.0.1",
|
||||
"vue-grid-layout": "^3.0.0-beta1",
|
||||
"vue-i18n": "^9.1.9",
|
||||
"vue-router": "^4.0.13",
|
||||
"vuex": "^4.0.2",
|
||||
"wangeditor": "^4.7.12"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^17.0.21",
|
||||
"@types/nprogress": "^0.2.0",
|
||||
"@types/sortablejs": "^1.10.7",
|
||||
"@typescript-eslint/eslint-plugin": "^5.13.0",
|
||||
"@typescript-eslint/parser": "^5.13.0",
|
||||
"@vitejs/plugin-vue": "^2.2.4",
|
||||
"@vitejs/plugin-vue-jsx": "^1.1.6",
|
||||
"@vue/compiler-sfc": "^3.2.31",
|
||||
"dotenv": "^16.0.0",
|
||||
"eslint": "^8.10.0",
|
||||
"eslint-plugin-vue": "^8.5.0",
|
||||
"prettier": "^2.5.1",
|
||||
"sass": "^1.49.9",
|
||||
"sass-loader": "^12.6.0",
|
||||
"typescript": "^4.6.2",
|
||||
"vite": "^2.8.6",
|
||||
"vue-eslint-parser": "^8.3.0"
|
||||
},
|
||||
"browserslist": [
|
||||
"> 1%",
|
||||
"last 2 versions",
|
||||
"not dead"
|
||||
],
|
||||
"bugs": {
|
||||
"url": "https://gitee.com/lyt-top/vue-next-admin/issues"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.0.0",
|
||||
"npm": ">= 6.0.0"
|
||||
},
|
||||
"keywords": [
|
||||
"vue",
|
||||
"vue3",
|
||||
"vuejs/vue-next",
|
||||
"element-ui",
|
||||
"element-plus",
|
||||
"vue-next-admin",
|
||||
"next-admin"
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://gitee.com/lyt-top/vue-next-admin.git"
|
||||
}
|
||||
}
|
||||
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
declare module 'vue-grid-layout';
|
||||
declare module 'qrcodejs2-fixes';
|
||||
declare module 'splitpanes';
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
@@ -0,0 +1,13 @@
|
||||
/* eslint-disable */
|
||||
|
||||
// 声明文件,*.vue 后缀的文件交给 vue 模块来处理
|
||||
declare module '*.vue' {
|
||||
import type { DefineComponent } from 'vue';
|
||||
const component: DefineComponent<{}, {}, any>;
|
||||
export default component;
|
||||
}
|
||||
|
||||
// 声明文件,定义全局变量。其它 app.config.globalProperties.xxx,使用 getCurrentInstance() 来获取
|
||||
interface Window {
|
||||
nextLoading: boolean;
|
||||
}
|
||||
Vendored
+6
@@ -0,0 +1,6 @@
|
||||
declare module '*.json';
|
||||
declare module '*.png';
|
||||
declare module '*.jpg';
|
||||
declare module '*.scss';
|
||||
declare module '*.ts';
|
||||
declare module '*.js';
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
<template>
|
||||
<el-config-provider :size="getGlobalComponentSize" :locale="i18nLocale">
|
||||
<router-view v-show="getThemeConfig.lockScreenTime !== 0" />
|
||||
<LockScreen v-if="getThemeConfig.isLockScreen" />
|
||||
<Setings ref="setingsRef" v-show="getThemeConfig.lockScreenTime !== 0" />
|
||||
<CloseFull />
|
||||
</el-config-provider>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, ref, getCurrentInstance, onBeforeMount, onMounted, onUnmounted, nextTick, defineComponent, watch, reactive, toRefs } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { useStore } from '/@/store/index';
|
||||
import other from '/@/utils/other';
|
||||
import { Local, Session } from '/@/utils/storage';
|
||||
import setIntroduction from '/@/utils/setIconfont';
|
||||
import LockScreen from '/@/layout/lockScreen/index.vue';
|
||||
import Setings from '/@/layout/navBars/breadcrumb/setings.vue';
|
||||
import CloseFull from '/@/layout/navBars/breadcrumb/closeFull.vue';
|
||||
export default defineComponent({
|
||||
name: 'app',
|
||||
components: { LockScreen, Setings, CloseFull },
|
||||
setup() {
|
||||
const { proxy } = <any>getCurrentInstance();
|
||||
const setingsRef = ref();
|
||||
const route = useRoute();
|
||||
const store = useStore();
|
||||
const state = reactive({
|
||||
i18nLocale: null,
|
||||
});
|
||||
// 获取布局配置信息
|
||||
const getThemeConfig = computed(() => {
|
||||
return store.state.themeConfig.themeConfig;
|
||||
});
|
||||
// 获取全局组件大小
|
||||
const getGlobalComponentSize = computed(() => {
|
||||
return other.globalComponentSize;
|
||||
});
|
||||
// 布局配置弹窗打开
|
||||
const openSetingsDrawer = () => {
|
||||
setingsRef.value.openDrawer();
|
||||
};
|
||||
// 设置初始化,防止刷新时恢复默认
|
||||
onBeforeMount(() => {
|
||||
// 设置批量第三方 icon 图标
|
||||
setIntroduction.cssCdn();
|
||||
// 设置批量第三方 js
|
||||
setIntroduction.jsCdn();
|
||||
});
|
||||
// 页面加载时
|
||||
onMounted(() => {
|
||||
nextTick(() => {
|
||||
// 监听布局配置弹窗点击打开
|
||||
proxy.mittBus.on('openSetingsDrawer', () => {
|
||||
openSetingsDrawer();
|
||||
});
|
||||
// 设置 i18n,App.vue 中的 el-config-provider
|
||||
proxy.mittBus.on('getI18nConfig', (locale: string) => {
|
||||
(state.i18nLocale as string | null) = locale;
|
||||
});
|
||||
// 获取缓存中的布局配置
|
||||
if (Local.get('themeConfig')) {
|
||||
store.dispatch('themeConfig/setThemeConfig', Local.get('themeConfig'));
|
||||
document.documentElement.style.cssText = Local.get('themeConfigStyle');
|
||||
}
|
||||
// 获取缓存中的全屏配置
|
||||
if (Session.get('isTagsViewCurrenFull')) {
|
||||
store.dispatch('tagsViewRoutes/setCurrenFullscreen', Session.get('isTagsViewCurrenFull'));
|
||||
}
|
||||
});
|
||||
});
|
||||
// 页面销毁时,关闭监听布局配置/i18n监听
|
||||
onUnmounted(() => {
|
||||
proxy.mittBus.off('openSetingsDrawer', () => {});
|
||||
proxy.mittBus.off('getI18nConfig', () => {});
|
||||
});
|
||||
// 监听路由的变化,设置网站标题
|
||||
watch(
|
||||
() => route.path,
|
||||
() => {
|
||||
other.useTitle();
|
||||
}
|
||||
);
|
||||
return {
|
||||
setingsRef,
|
||||
getThemeConfig,
|
||||
getGlobalComponentSize,
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,25 @@
|
||||
import request from '/@/utils/request';
|
||||
|
||||
/**
|
||||
* 登录api接口集合
|
||||
* @method signIn 用户登录
|
||||
* @method signOut 用户退出登录
|
||||
*/
|
||||
export function useLoginApi() {
|
||||
return {
|
||||
signIn: (params: object) => {
|
||||
return request({
|
||||
url: '/user/signIn',
|
||||
method: 'post',
|
||||
data: params,
|
||||
});
|
||||
},
|
||||
signOut: (params: object) => {
|
||||
return request({
|
||||
url: '/user/signOut',
|
||||
method: 'post',
|
||||
data: params,
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import request from '/@/utils/request';
|
||||
|
||||
/**
|
||||
* 后端控制菜单模拟json,路径在 https://gitee.com/lyt-top/vue-next-admin-images/tree/master/menu
|
||||
* 后端控制路由,isRequestRoutes 为 true,则开启后端控制路由
|
||||
* @method getMenuAdmin 获取后端动态路由菜单(admin)
|
||||
* @method getMenuTest 获取后端动态路由菜单(test)
|
||||
*/
|
||||
export function useMenuApi() {
|
||||
return {
|
||||
getMenuAdmin: (params?: object) => {
|
||||
return request({
|
||||
url: '/gitee/lyt-top/vue-next-admin-images/raw/master/menu/adminMenu.json',
|
||||
method: 'get',
|
||||
params,
|
||||
});
|
||||
},
|
||||
getMenuTest: (params?: object) => {
|
||||
return request({
|
||||
url: '/gitee/lyt-top/vue-next-admin-images/raw/master/menu/testMenu.json',
|
||||
method: 'get',
|
||||
params,
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 26 KiB |
@@ -0,0 +1,9 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 50" width="64" height="50">
|
||||
<defs>
|
||||
<image width="64" height="50" id="img1" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAAAyCAYAAADsg90UAAAAAXNSR0IB2cksfwAACI5JREFUeJy1WgtsW9UZdvNq0xU6WhZoCCVx7Gunbld1GSsMdYpGREhwr+2IMbQWqRoTk+iWPZi2wWhm1kwEmqV52feRJrbvvd6DiUnbQCAemzY2xh4tSB0wOqZIqAtLYzultIWEpt13btLq+sap/R8nRzpK4vh+5/++85///8851yEk1V+hX6B0tyE3OWwt0x789VR78AKlZ0KBz1oxBE3+BdUW9PNuQ7nJbk/BDQ/v4hi0x44z1R64jyrAVCjQ/fLNN8/ZoUWdgqGc57AFE6I86giH+QRw6XI9QE4RBz3iTAyVW3EyQdEDUmeIIhyeFNtMHO/o0B4e8qYASfVvLk2p4BKgdnioFCAvEQeddhnyFivOpOgvBaFXiAKcygQDrk927S/zjkae5xUA/X3YI/C5AFwHLrSPw+2+YYfCMugiCvDaCbGt0hc9uAXuP1OEAPACZQ+fAGguQ9nGgglx0OfsOAhq24lBcG/YYQa/kWLIz3XlCW4B6mKRCoCMEQdNu3VpnRVnMhBYA2L/LVCATCooXuNJSHXFzv58P+mMyyu5RQDAKNntdLnNjoN0+NMCM0D8901NDkGXuyBAseRZn3Ubagu3AFjTd1GXAdZdpCqurMgSIBS4BwRn8wgwje/d4pV6yz2adGQJyM/bo/ZyC+DSlWqApIiDHqtPyFdlCyBeD4Lv5xHgr5mAWO4ZjTRh9s8tlQDoryIbrOISoLYvvAIALxIHnIHn3GnFmfDfztLhH/IEv/vYd72xyKElJM/6Gdjj4fYCuNA3yYMa6ogdBwQfvIwAk+mguHZz3+MbMPsfLrEALD3v5RfAkH3UOID+Jqqwj2ULIDZeJvhF2He8I0NfX2ry8wI85VSy41LBzaUPsXT4L/Iy0OUbrTgpsXUlyI7lEOAsvGOboPRdKejK28shAPqpek1etxjHArxAiXCo/pAdB2QPLRQg8BLcv8ITi4h4bnaZBGD2+IsQQG0DCDUyv2LfjcHVQyB93iLAecz+bvY/T1x6YrnImwIkVakIAeSrAfIucdCz7qRcY8VJh8RrQDplWfvHxttaVvkGe6vg/h8tpwDoR926wpcOb9CkEhQ4Mbrqym4rTkrcydLhs5bU95Wwwwx+311m8qxPQ4CtvE6AdKjcLlCrQkNJXNXdnYUD0t+eF+AE1v76rV0/qvTEo/8kkkGJq/yFLIKhPsgtgEszl8E4cdAxPHdllgDBwGZW9k6Fguaa9A4PtMEwkrDe0chhCPAZ/E5dNn/kPiWqGe5mVSH1rJCdze2w4rDNDsi/gdn/FPtb0KTfUmeyQenvuE45WCLQd6unsc3fwKeAw0yHX+Zwu247DpbB3emQv3R7x7du8GgybdtrKGc39Ty23rQnqfKk5y9wCwD1XACZJg56xGUMZFVhx1taytjPBnWAfuihyT+3TEiAQwC1Mhzmqwrr9QhbBoeJg36A6Ou0Y/kGe2qQ+mhiGspHDcODTZfs0eRr8fkE0Z4xpyGt4RJgTnX1carq8Jw9dhzv6BB5k+VJyK9t6+y8dNK74bGHMSHK00Scc/CC7fwC6MotAjX6GuqTzsTBkosYW/f9YKUnIb1OFiAW/c4Ce5Lq18jLKKnu5xaApTWBHn0n8Nz6ixhw4zvJ5HXlvU2R3gWui02XizwhrEznbTW9vQ64nUEckBUut13EQOHzFFUA70jErBv+09xcYrXHqUXK8H9qIXXapSs1dm4FN1SF95DdzlD72LMNUt/15CMvXT65NfzIRtQOq5FCv7jAHkPppdqDZ3YvZFZgY8WEQN8dvvFxpB8hLvWT3T8uyWxckG9CEfX38fk0ahGgmboMMIk/2xg5UJKbYZ62sf9RbI7UPxGJzLgT0R2Y/RM0z1HO+QZ6Gl/3+dh2Wmdb6HQwUJc9IfI6fPc40Z5xCLeW2wvw8Pfoy0B5mfoMAuYLn+7oKE0FxZqp+YvWTHugw24PvvtLIjZLh83cAghzmxHqASb5qntz/4EvsfEy7UHroepvJoKtWdUcPPKrVGwsA6k2FuYTAPXAGoC8RfYCStflqRvv3VuB4Hc1SI9bBJjAZ1VWe+p1+TqBHpfG+M8Ksa0EgLacAnhiETNzIPjdbztLPIfPWrME0CSkQ+UfxDFmkQ5v5RPAYcYBckFTcDfUWc+hwZsw06tA+EiOi5RoDns66ctA/Qm3AE4tuhYgZ5Zl9uPR32165PtlINoMwjM5jtPfSvn9WTe/iEufw7PUW+U3a2NSKZ8CTU0s+DyzHAJgq9yuNDaywxNtkQuVWaTDBqs5SIdsQt4hjsXOCvmvzrDu6Fdnebo3Hj3u69xXngoFqkH09KL3ie2BB6y2XCsfZNt1apnOqsL7uY/KoB57k+SDJRUhIZlX2iD4UJ4b5ef+3dqabY9hlunUdPt0XXyIryqs1yKVAv3qbPFuKNNedaAR5FeD4NE8AqRTwZ1ZmxqnHv0EcI4Rxz3hNqSqxThevrEXqpJq31IJgOD3TN3+h0tYmmPpLt9bJex80W4SRBzmEF7kE8Bhbo5aONwutwCx6K4JUVyByu/JfOTn+6EFAiTNN1uoAgxyC1BvqFcAZLJo8pr8rlvuq8wExfpFUl+u/s7/dt6R9UKkS5ergHeaOP6Yc2SQLw7Mqa6SDznsHakvzLAyoeAPCyRvVoXpUGCb1ZZqRWFnhdQXPWcg3Jac5App7C2MImf/5Kb+nupJ0X8FSL1NEOACEyyHPQ9QbWA73CIEkBuEIqrCzUO9CsNB6esCqQ8pAqD/Oe33l9vs2SLQd6svuhJSWW6GeVptLFKGbMD9eltjZ2cjw5m4o60ChF4lCvBeJhjIuntAYF4N3KNEO9Lwgmo+F/D5mNt1cbr/2OeD916qx+HSB4gCsDdNdmXZM7dbjRJtYXeZd/EJ4DDX3Q4uARLSj604yO23UgVA2jRy2EO+OkNX/w+fZNm8pw5QbAAAAABJRU5ErkJggg=="/>
|
||||
</defs>
|
||||
<style>
|
||||
tspan { white-space:pre }
|
||||
</style>
|
||||
<use id="Background" href="#img1" x="0" y="0" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.2 KiB |
@@ -0,0 +1,105 @@
|
||||
//流程类型名称匹配
|
||||
import { ModdleElement } from '../type';
|
||||
|
||||
export const ProcessNameMapping = {
|
||||
//流程
|
||||
'bpmn:Process': '流程',
|
||||
//子流程
|
||||
'bpmn:SubProcess': '子流程',
|
||||
};
|
||||
|
||||
// 事件名字匹配
|
||||
export const EventNameMapping = {
|
||||
'bpmn:StartEvent': '开始事件',
|
||||
'bpmn:EndEvent': '结束事件',
|
||||
'bpmn:MessageEventDefinition': '消息',
|
||||
'bpmn:TimerEventDefinition': '定时',
|
||||
'bpmn:ConditionalEventDefinition': '条件',
|
||||
'bpmn:SignalEventDefinition': '信号',
|
||||
};
|
||||
|
||||
//流名称匹配
|
||||
export const FlowNameMapping = {
|
||||
'bpmn:SequenceFlow': (businessObject: ModdleElement) => {
|
||||
const defaultName = '顺序流';
|
||||
if (businessObject.conditionExpression) {
|
||||
return '条件' + defaultName;
|
||||
}
|
||||
|
||||
if (businessObject.sourceRef.default) {
|
||||
return '默认' + defaultName;
|
||||
}
|
||||
|
||||
return defaultName;
|
||||
},
|
||||
};
|
||||
|
||||
//网关类型名称匹配
|
||||
export const GatewayNameMapping = {
|
||||
//互斥网关
|
||||
'bpmn:ExclusiveGateway': '互斥网关',
|
||||
//并行网关
|
||||
'bpmn:ParallelGateway': '并行网关',
|
||||
//复杂网关
|
||||
'bpmn:ComplexGateway': '复杂网关',
|
||||
//事件网关
|
||||
'bpmn:EventBasedGateway': '事件网关',
|
||||
};
|
||||
|
||||
//任务类型名称匹配
|
||||
export const TaskNameMapping = {
|
||||
//普通任务
|
||||
'bpmn:Task': '普通任务',
|
||||
//用户任务
|
||||
'bpmn:UserTask': '用户任务',
|
||||
//接收任务
|
||||
'bpmn:ReceiveTask': '接收任务',
|
||||
//发送任务
|
||||
'bpmn:SendTask': '发送任务',
|
||||
//手工任务
|
||||
'bpmn:ManualTask': '手工任务',
|
||||
//业务规则任务
|
||||
'bpmn:BusinessRuleTask': '业务规则任务',
|
||||
//服务任务
|
||||
'bpmn:ServiceTask': '服务任务',
|
||||
//脚本任务
|
||||
'bpmn:ScriptTask': '脚本任务',
|
||||
//调用任务
|
||||
'bpmn:CallActivity': '调用任务',
|
||||
};
|
||||
|
||||
//其他类型名称匹配
|
||||
export const OtherNameMapping = {
|
||||
//池
|
||||
'bpmn:Participant': '池/参与者',
|
||||
//分组
|
||||
'bpmn:Group': '分组',
|
||||
//数据存储
|
||||
'bpmn:DataStoreReference': '数据存储',
|
||||
//数据对象
|
||||
'bpmn:DataObjectReference': '数据对象',
|
||||
};
|
||||
|
||||
export const NameMapping: { [key: string]: ((obj: any) => string) | string } = {
|
||||
...ProcessNameMapping,
|
||||
...EventNameMapping,
|
||||
...FlowNameMapping,
|
||||
...GatewayNameMapping,
|
||||
...TaskNameMapping,
|
||||
...OtherNameMapping,
|
||||
};
|
||||
|
||||
/**
|
||||
* 根据流程类型节点业务对象解析业务对象节点类型的名称
|
||||
* @param businessObject 节点的业务流程对象
|
||||
*/
|
||||
export const resolveTypeName = (businessObject: ModdleElement): string => {
|
||||
const eventDefinitions = businessObject.eventDefinitions;
|
||||
const nameMappingElement = NameMapping[businessObject.$type];
|
||||
if (typeof nameMappingElement === 'function') {
|
||||
return nameMappingElement(businessObject);
|
||||
}
|
||||
return eventDefinitions
|
||||
? NameMapping[eventDefinitions[0].$type] + nameMappingElement
|
||||
: nameMappingElement;
|
||||
};
|
||||
@@ -0,0 +1,351 @@
|
||||
import { ElFormItem, ElInput, ElOption, ElSelect } from 'element-plus';
|
||||
import { FieldDefine } from '/@/comps/Activiti/dynamic-binder';
|
||||
import { PropertiesMap, GroupProperties } from './index';
|
||||
import SubList from '/@/comps/Activiti/sublist/SubList';
|
||||
import { SubListState } from '/@/comps/Activiti/sublist/type';
|
||||
import { ModdleElement } from '../type';
|
||||
import { BpmnStore } from '../store';
|
||||
|
||||
/**
|
||||
* 所有通用节点的属性(每个节点都有的)
|
||||
*/
|
||||
const commonProperties: PropertiesMap<FieldDefine> = {
|
||||
id: {
|
||||
component: ElInput,
|
||||
placeholder: '节点ID',
|
||||
vSlots: {
|
||||
prepend: (): JSX.Element => <div>节点ID</div>,
|
||||
},
|
||||
setValue(sourceObject: ModdleElement, key: string, value: string) {
|
||||
const isNotNull = value;
|
||||
const latestValue = value || ' ';
|
||||
const shape = BpmnStore.getShape();
|
||||
BpmnStore.getModeling().updateProperties(shape, {
|
||||
[key]: isNotNull ? latestValue.trim() : latestValue,
|
||||
});
|
||||
},
|
||||
},
|
||||
name: {
|
||||
component: ElInput,
|
||||
// prefix: '节点名称',
|
||||
placeholder: '节点名称',
|
||||
vSlots: {
|
||||
prepend: (): JSX.Element => <div>节点名称</div>,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* (基础信息)每个节点都有的
|
||||
*/
|
||||
export const CommonGroupProperties: GroupProperties = {
|
||||
name: '基础信息',
|
||||
icon: 'el-icon-info',
|
||||
properties: { ...commonProperties },
|
||||
};
|
||||
|
||||
interface Documentation {
|
||||
text: string;
|
||||
}
|
||||
|
||||
export const DocumentGroupProperties: GroupProperties = {
|
||||
name: '元素文档',
|
||||
icon: 'el-icon-document',
|
||||
properties: {
|
||||
'documentation.text': {
|
||||
component: ElInput,
|
||||
type: 'textarea',
|
||||
getValue: (obj: { documentation: Array<Documentation> }): string => {
|
||||
return obj['documentation']?.[0]?.['text'];
|
||||
},
|
||||
setValue(businessObject: ModdleElement, key: string, value: unknown): void {
|
||||
BpmnStore.createElement('bpmn:Documentation', 'documentation', { text: value }, true);
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
interface PropertyElement {
|
||||
$type: string;
|
||||
name: string;
|
||||
value: unknown;
|
||||
}
|
||||
|
||||
/**
|
||||
* 流程事件类型选项
|
||||
*/
|
||||
const EVENT_OPTIONS = [
|
||||
{ label: '开始', value: 'start' },
|
||||
{ label: '结束', value: 'end' },
|
||||
];
|
||||
|
||||
/**
|
||||
* 监听器类型选项
|
||||
*/
|
||||
const TYPE_OPTIONS = [
|
||||
{ label: 'java类', value: 'class' },
|
||||
{ label: '调用表达式', value: 'expression' },
|
||||
{ label: '注入表达式', value: 'delegateExpression' },
|
||||
];
|
||||
|
||||
/**
|
||||
* 获取节点类型的监听器属性配置组
|
||||
* @param options 参数
|
||||
*/
|
||||
import { TaskNameMapping } from './TypeNameMapping';
|
||||
|
||||
const taskTags = Object.keys(TaskNameMapping);
|
||||
export const getElementTypeListenerProperties = function (options: {
|
||||
name: string;
|
||||
icon?: string;
|
||||
//时间类型选项
|
||||
eventOptions?: Array<{ label: string; value: string }>;
|
||||
}): GroupProperties {
|
||||
const eventOptions = options.eventOptions || EVENT_OPTIONS;
|
||||
return {
|
||||
name: options.name || '监听器',
|
||||
icon: options.icon || 'el-icon-bell',
|
||||
properties: {
|
||||
'extensionElements.listeners': {
|
||||
component: SubList,
|
||||
columns: [
|
||||
{
|
||||
type: 'index',
|
||||
label: '序号',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
prop: 'event',
|
||||
label: '事件',
|
||||
align: 'center',
|
||||
formatter: (row: any, column: any): string => {
|
||||
return eventOptions.filter((item) => item.value === row[column.property])[0].label;
|
||||
},
|
||||
editComponent: function (scope: any, state: SubListState<any>): JSX.Element {
|
||||
return (
|
||||
<ElFormItem
|
||||
class="sublist-form-item"
|
||||
label={scope.column.name}
|
||||
prop={scope.column.property}
|
||||
>
|
||||
<ElSelect v-model={state.editItem.event}>
|
||||
{eventOptions.map((option) => {
|
||||
return (
|
||||
<ElOption key={option.value} label={option.label} value={option.value} />
|
||||
);
|
||||
})}
|
||||
</ElSelect>
|
||||
</ElFormItem>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
prop: 'type',
|
||||
label: '执行类型',
|
||||
align: 'center',
|
||||
formatter: (row: any, column: any) => {
|
||||
return TYPE_OPTIONS.filter((item) => item.value === row[column.property])[0].label;
|
||||
},
|
||||
editComponent: function (scope: any, state: SubListState<any>): JSX.Element {
|
||||
return (
|
||||
<ElFormItem
|
||||
class="sublist-form-item"
|
||||
label={scope.column.name}
|
||||
prop={scope.column.property}
|
||||
>
|
||||
<ElSelect v-model={state.editItem.type}>
|
||||
{TYPE_OPTIONS.map((option) => {
|
||||
return (
|
||||
<ElOption key={option.value} label={option.label} value={option.value} />
|
||||
);
|
||||
})}
|
||||
</ElSelect>
|
||||
</ElFormItem>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
prop: 'content',
|
||||
label: '执行内容',
|
||||
align: 'center',
|
||||
},
|
||||
],
|
||||
rules: {
|
||||
event: [{ required: true, message: '事件不能为空' }],
|
||||
type: [{ required: true, message: '类型不能为空' }],
|
||||
content: [{ required: true, message: '执行内容不能为空' }],
|
||||
},
|
||||
getValue: (businessObject: ModdleElement): Array<any> => {
|
||||
const listenerTagName = taskTags.includes(businessObject.$type)
|
||||
? 'activiti:TaskListener'
|
||||
: 'activiti:ExecutionListener';
|
||||
return businessObject?.extensionElements?.values
|
||||
?.filter((item: ModdleElement) => item.$type === listenerTagName)
|
||||
?.map((item: ModdleElement) => {
|
||||
const type = item.expression
|
||||
? 'expression'
|
||||
: item.delegateExpression
|
||||
? 'delegateExpression'
|
||||
: 'class';
|
||||
return {
|
||||
event: item.event,
|
||||
type: type,
|
||||
content: item[type],
|
||||
};
|
||||
});
|
||||
},
|
||||
setValue(businessObject: ModdleElement, key: string, value: []): void {
|
||||
const bpmnContext = BpmnStore;
|
||||
console.warn('activeBusinessObject', businessObject);
|
||||
const moddle = bpmnContext.getModeler().get('moddle');
|
||||
//判断当前活动的模型类型,使用不同类型的标签监听器
|
||||
const listenerTagName = taskTags.includes(businessObject.$type)
|
||||
? 'activiti:TaskListener'
|
||||
: 'activiti:ExecutionListener';
|
||||
bpmnContext.updateExtensionElements(
|
||||
listenerTagName,
|
||||
value.map((attr: { event: string; type: string; content: string }) => {
|
||||
return moddle.create(listenerTagName, {
|
||||
event: attr.event,
|
||||
[attr.type]: attr.content,
|
||||
});
|
||||
}),
|
||||
);
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* 扩展属性组配置
|
||||
*/
|
||||
export const ExtensionGroupProperties: GroupProperties = {
|
||||
name: '扩展属性',
|
||||
icon: 'el-icon-document-add',
|
||||
properties: {
|
||||
'extensionElements.properties': {
|
||||
component: SubList,
|
||||
columns: [
|
||||
{
|
||||
type: 'index',
|
||||
label: '序号',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
prop: 'name',
|
||||
label: '属性名',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
prop: 'value',
|
||||
label: '属性值',
|
||||
align: 'center',
|
||||
},
|
||||
],
|
||||
rules: {
|
||||
name: [{ required: true, message: '属性名不能为空' }],
|
||||
value: [{ required: true, message: '属性值不能为空' }],
|
||||
},
|
||||
getValue: (businessObject: ModdleElement): Array<any> => {
|
||||
return businessObject?.extensionElements?.values
|
||||
?.filter((item: PropertyElement) => item.$type === 'activiti:Properties')[0]
|
||||
?.values.map((item: PropertyElement) => ({
|
||||
name: item.name,
|
||||
value: item.value,
|
||||
}));
|
||||
},
|
||||
setValue(businessObject: ModdleElement, key: string, value: []): void {
|
||||
const bpmnContext = BpmnStore;
|
||||
const moddle = bpmnContext.getModeler().get('moddle');
|
||||
const properties = moddle.create(`activiti:Properties`, {
|
||||
values: value.map((attr: { name: string; value: unknown }) => {
|
||||
return moddle.create(`activiti:Property`, { name: attr.name, value: attr.value });
|
||||
}),
|
||||
});
|
||||
bpmnContext.updateExtensionElements('activiti:Properties', properties);
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
interface FromPropertyElement {
|
||||
$type: string;
|
||||
id: string;
|
||||
type: string;
|
||||
$attrs: FromPropertyAttrsElement;
|
||||
}
|
||||
|
||||
interface FromPropertyAttrsElement {
|
||||
name: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* (基础信息)表单
|
||||
*/
|
||||
export const FormGroupProperties: GroupProperties = {
|
||||
name: '表单信息',
|
||||
icon: 'el-icon-edit',
|
||||
properties: {
|
||||
formKey: {
|
||||
component: ElInput,
|
||||
placeholder: '表单key',
|
||||
vSlots: {
|
||||
prepend: (): JSX.Element => <div>表单key</div>,
|
||||
},
|
||||
},
|
||||
'extensionElements.formProperty': {
|
||||
component: SubList,
|
||||
columns: [
|
||||
{
|
||||
prop: 'id',
|
||||
label: '编码',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
prop: 'type',
|
||||
label: '类型',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
prop: 'name',
|
||||
label: '名称',
|
||||
align: 'center',
|
||||
},
|
||||
],
|
||||
rules: {
|
||||
id: [{ required: true, message: '编码不能为空' }],
|
||||
type: [{ required: true, message: '类型不能为空' }],
|
||||
name: [{ required: true, message: '名称不能为空' }],
|
||||
},
|
||||
getValue: (businessObject: ModdleElement): Array<FromPropertyElement> => {
|
||||
return businessObject?.extensionElements?.values
|
||||
?.filter((item: FromPropertyElement) => item.$type === 'activiti:FormProperty')
|
||||
.map((elem: FromPropertyElement) => {
|
||||
console.warn('elem', elem);
|
||||
return { id: elem?.id, type: elem.type, name: elem?.$attrs?.name };
|
||||
});
|
||||
},
|
||||
setValue(businessObject: ModdleElement, key: string, value: []): void {
|
||||
const bpmnContext = BpmnStore;
|
||||
const moddle = bpmnContext.getModeler().get('moddle');
|
||||
//表单数据值对象
|
||||
const formProperties = value.map((attr: { id: string; type: string; name: string }) => {
|
||||
return moddle.create('activiti:FormProperty', {
|
||||
id: attr.id,
|
||||
name: attr.name,
|
||||
type: attr.type,
|
||||
});
|
||||
});
|
||||
|
||||
bpmnContext.updateExtensionElements('activiti:FormProperty', formProperties);
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
// export const FormProperties: GroupProperties = {
|
||||
// name: '表单属性',
|
||||
// icon: 'el-icon-document-add',
|
||||
// properties: {},
|
||||
// };
|
||||
@@ -0,0 +1,35 @@
|
||||
import { FieldDefine } from '/@/comps/Activiti/dynamic-binder';
|
||||
|
||||
/**
|
||||
* 属性Map接口
|
||||
*/
|
||||
export interface PropertiesMap<T> {
|
||||
[key: string]: T;
|
||||
}
|
||||
|
||||
/**
|
||||
* 组属性(属性分组)
|
||||
*/
|
||||
export interface GroupProperties {
|
||||
name?: string;
|
||||
icon?: string;
|
||||
properties: PropertiesMap<FieldDefine>;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
//@ts-ignore
|
||||
const modules = import.meta.glob('./modules/*.tsx');
|
||||
const getBpmnGroupPropertiesConfig = () => {
|
||||
const BpmnGroupPropertiesConfig: PropertiesMap<Array<GroupProperties>> = {};
|
||||
for (const path in modules) {
|
||||
modules[path]().then((mod: any) => {
|
||||
const moduleDefuatlExport = mod.default;
|
||||
for (const moduleKey in moduleDefuatlExport) {
|
||||
BpmnGroupPropertiesConfig[moduleKey] = moduleDefuatlExport[moduleKey];
|
||||
}
|
||||
});
|
||||
}
|
||||
return BpmnGroupPropertiesConfig;
|
||||
};
|
||||
|
||||
export default getBpmnGroupPropertiesConfig();
|
||||
@@ -0,0 +1,28 @@
|
||||
import {
|
||||
CommonGroupProperties,
|
||||
ExtensionGroupProperties,
|
||||
DocumentGroupProperties,
|
||||
FormGroupProperties,
|
||||
} from '../common';
|
||||
|
||||
const CommonGroupPropertiesArray = [
|
||||
CommonGroupProperties,
|
||||
ExtensionGroupProperties,
|
||||
DocumentGroupProperties,
|
||||
];
|
||||
|
||||
export default {
|
||||
//开始事件、消息开始事件、定时开始事件、条件开始事件(这些都属于开始事件bpmn:StartEvent)
|
||||
'bpmn:StartEvent': [
|
||||
CommonGroupProperties,
|
||||
FormGroupProperties,
|
||||
ExtensionGroupProperties,
|
||||
DocumentGroupProperties,
|
||||
],
|
||||
//结束事件,也包含类似开始事件的所有结束事件
|
||||
'bpmn:EndEvent': CommonGroupPropertiesArray,
|
||||
//中间抛出事件(包含:消息抛出、升级抛出、链接抛出、补偿抛出、信号抛出等)
|
||||
'bpmn:IntermediateThrowEvent': CommonGroupPropertiesArray,
|
||||
//中间捕获事件(包含:小心捕获、定时捕获、条件捕获、信号捕获等)
|
||||
'bpmn:IntermediateCatchEvent': CommonGroupPropertiesArray,
|
||||
};
|
||||
@@ -0,0 +1,120 @@
|
||||
import {
|
||||
CommonGroupProperties,
|
||||
ExtensionGroupProperties,
|
||||
DocumentGroupProperties,
|
||||
getElementTypeListenerProperties,
|
||||
} from '../common';
|
||||
|
||||
import { ElInput, ElOption } from 'element-plus';
|
||||
import PrefixLabelSelect from '/@/comps/Activiti/prefix-label-select';
|
||||
import { ModdleElement } from '../../type';
|
||||
import { BpmnStore } from '../../store';
|
||||
|
||||
const FLOW_TYPE_OPTIONS = [
|
||||
{ label: '普通顺序流', value: 'normal' },
|
||||
{ label: '默认顺序流', value: 'default' },
|
||||
{ label: '条件顺序流', value: 'condition' },
|
||||
];
|
||||
|
||||
//获取顺序流的类型
|
||||
const getSequenceFlowType = (businessObject: ModdleElement) => {
|
||||
if (businessObject?.conditionExpression) {
|
||||
return 'condition';
|
||||
}
|
||||
|
||||
if (businessObject?.sourceRef?.default) {
|
||||
return 'default';
|
||||
}
|
||||
|
||||
return 'normal';
|
||||
};
|
||||
|
||||
/**
|
||||
* 常规配置
|
||||
*/
|
||||
const BaseProperties = {
|
||||
...CommonGroupProperties,
|
||||
properties: {
|
||||
...CommonGroupProperties.properties,
|
||||
//条件类型
|
||||
'sequenceFlow.type': {
|
||||
component: PrefixLabelSelect,
|
||||
prefixTitle: '顺序流类型',
|
||||
predicate: (businessObject: ModdleElement): boolean => {
|
||||
return businessObject?.sourceRef?.$type !== 'bpmn:StartEvent';
|
||||
},
|
||||
vSlots: {
|
||||
default: (): JSX.Element => (
|
||||
<>
|
||||
{FLOW_TYPE_OPTIONS.map((item) => {
|
||||
return <ElOption {...item} />;
|
||||
})}
|
||||
</>
|
||||
),
|
||||
},
|
||||
getValue(businessObject: ModdleElement): string {
|
||||
return getSequenceFlowType(businessObject);
|
||||
},
|
||||
setValue(businessObject: ModdleElement, key: string, value: string): void {
|
||||
const bpmnContext = BpmnStore;
|
||||
const line = bpmnContext.getShape();
|
||||
const sourceShape = bpmnContext
|
||||
.getModeler()
|
||||
.get('elementRegistry')
|
||||
.get(line.businessObject.sourceRef.id);
|
||||
const modeling = bpmnContext.getModeling();
|
||||
if (!value || value === 'normal') {
|
||||
modeling.updateProperties(line, { conditionExpression: null });
|
||||
delete line.businessObject.conditionExpression;
|
||||
}
|
||||
|
||||
if (value === 'default') {
|
||||
modeling.updateProperties(sourceShape, { default: line });
|
||||
delete line.businessObject.conditionExpression;
|
||||
}
|
||||
|
||||
if (value === 'condition') {
|
||||
modeling.updateProperties(line, {
|
||||
conditionExpression: bpmnContext
|
||||
.getModeler()
|
||||
.get('moddle')
|
||||
.create('bpmn:FormalExpression'),
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
'conditionExpression.body': {
|
||||
component: ElInput,
|
||||
placeholder: '条件表达式',
|
||||
vSlots: {
|
||||
prepend: (): JSX.Element => <div>条件表达式</div>,
|
||||
},
|
||||
predicate: (businessObject: ModdleElement): boolean => {
|
||||
return 'condition' === getSequenceFlowType(businessObject);
|
||||
},
|
||||
getValue(businessObject: ModdleElement): string {
|
||||
return businessObject?.conditionExpression?.body;
|
||||
},
|
||||
setValue(businessObject: ModdleElement, key: string, value: unknown): void {
|
||||
const bpmnContext = BpmnStore;
|
||||
const moddle = bpmnContext.getModeler().get('moddle');
|
||||
bpmnContext.getModeling().updateProperties(bpmnContext.getShape(), {
|
||||
conditionExpression: moddle.create('bpmn:FormalExpression', { body: value }),
|
||||
});
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default {
|
||||
//顺序流
|
||||
'bpmn:SequenceFlow': [
|
||||
BaseProperties,
|
||||
getElementTypeListenerProperties({
|
||||
name: '条件监听器',
|
||||
eventOptions: [{ label: 'take', value: 'take' }],
|
||||
}),
|
||||
ExtensionGroupProperties,
|
||||
DocumentGroupProperties,
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,22 @@
|
||||
import {
|
||||
CommonGroupProperties,
|
||||
ExtensionGroupProperties,
|
||||
DocumentGroupProperties,
|
||||
} from '../common';
|
||||
|
||||
const CommonGroupPropertiesArray = [
|
||||
CommonGroupProperties,
|
||||
ExtensionGroupProperties,
|
||||
DocumentGroupProperties,
|
||||
];
|
||||
|
||||
export default {
|
||||
//互斥网关
|
||||
'bpmn:ExclusiveGateway': CommonGroupPropertiesArray,
|
||||
//并行网关
|
||||
'bpmn:ParallelGateway': CommonGroupPropertiesArray,
|
||||
//复杂网关
|
||||
'bpmn:ComplexGateway': CommonGroupPropertiesArray,
|
||||
//事件网关
|
||||
'bpmn:EventBasedGateway': CommonGroupPropertiesArray,
|
||||
};
|
||||
@@ -0,0 +1,52 @@
|
||||
import {
|
||||
CommonGroupProperties,
|
||||
ExtensionGroupProperties,
|
||||
DocumentGroupProperties,
|
||||
} from '../common';
|
||||
import { ElInput } from 'element-plus';
|
||||
|
||||
const CommonGroupPropertiesArray = [
|
||||
CommonGroupProperties,
|
||||
ExtensionGroupProperties,
|
||||
DocumentGroupProperties,
|
||||
];
|
||||
|
||||
interface CategoryValueRef {
|
||||
value: string;
|
||||
}
|
||||
|
||||
const BpmnGroupBaseProperties = {
|
||||
name: '基础信息',
|
||||
icon: 'el-icon-info',
|
||||
properties: {
|
||||
id: {
|
||||
component: ElInput,
|
||||
placeholder: '节点ID',
|
||||
vSlots: {
|
||||
prepend: (): JSX.Element => <div>节点ID</div>,
|
||||
},
|
||||
},
|
||||
name: {
|
||||
component: ElInput,
|
||||
// prefix: '节点名称',
|
||||
placeholder: '节点名称',
|
||||
vSlots: {
|
||||
prepend: (): JSX.Element => <div>节点名称</div>,
|
||||
},
|
||||
getValue: (obj: { categoryValueRef: CategoryValueRef }): string => {
|
||||
return obj?.categoryValueRef?.value;
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default {
|
||||
//池
|
||||
'bpmn:Participant': CommonGroupPropertiesArray,
|
||||
//分组
|
||||
'bpmn:Group': [BpmnGroupBaseProperties, ExtensionGroupProperties, DocumentGroupProperties],
|
||||
//数据存储
|
||||
'bpmn:DataStoreReference': CommonGroupPropertiesArray,
|
||||
//数据对象
|
||||
'bpmn:DataObjectReference': CommonGroupPropertiesArray,
|
||||
};
|
||||
@@ -0,0 +1,26 @@
|
||||
import {
|
||||
CommonGroupProperties,
|
||||
ExtensionGroupProperties,
|
||||
DocumentGroupProperties,
|
||||
getElementTypeListenerProperties,
|
||||
} from '../common';
|
||||
|
||||
//流程数据属性配置数组
|
||||
const ProcessGroupPropertiesArray = [
|
||||
CommonGroupProperties,
|
||||
/**
|
||||
* 监听器组
|
||||
*/
|
||||
getElementTypeListenerProperties({
|
||||
name: '全局监听器',
|
||||
}),
|
||||
ExtensionGroupProperties,
|
||||
DocumentGroupProperties,
|
||||
];
|
||||
|
||||
export default {
|
||||
//流程
|
||||
'bpmn:Process': ProcessGroupPropertiesArray,
|
||||
//子流程
|
||||
'bpmn:SubProcess': ProcessGroupPropertiesArray,
|
||||
};
|
||||
@@ -0,0 +1,256 @@
|
||||
import {
|
||||
CommonGroupProperties,
|
||||
FormGroupProperties,
|
||||
DocumentGroupProperties,
|
||||
ExtensionGroupProperties,
|
||||
getElementTypeListenerProperties,
|
||||
} from '../common';
|
||||
import { GroupProperties } from '../index';
|
||||
import PrefixLabelSelect from '/@/comps/Activiti/prefix-label-select';
|
||||
import { ElInput, ElOption } from 'element-plus';
|
||||
import { ModdleElement } from '../../type';
|
||||
import { BpmnStore } from '../../store';
|
||||
|
||||
const TASK_EVENT_OPTIONS = [
|
||||
{ label: '通过', value: 'pass' },
|
||||
{ label: '驳回', value: 'refuse' },
|
||||
{ label: '转办', value: 'zhuanhuan' },
|
||||
{ label: '沟通', value: 'goutong' },
|
||||
{ label: '废弃', value: 'feiqi' },
|
||||
];
|
||||
|
||||
const TaskListenerProperties = getElementTypeListenerProperties({
|
||||
name: '任务监听器',
|
||||
eventOptions: TASK_EVENT_OPTIONS,
|
||||
});
|
||||
|
||||
const USER_OPTIONS = [
|
||||
{ label: '张三', value: 'z3' },
|
||||
{ label: '李四', value: 'l4' },
|
||||
{ label: '王五', value: 'w5' },
|
||||
{ label: '赵六', value: 'zhao6' },
|
||||
{ label: '孙七', value: 's7' },
|
||||
];
|
||||
|
||||
const UserOption: JSX.Element = (
|
||||
<>
|
||||
{USER_OPTIONS.map((item) => {
|
||||
return <ElOption {...item} />;
|
||||
})}
|
||||
</>
|
||||
);
|
||||
|
||||
/**
|
||||
* 用户任务属性配置
|
||||
*/
|
||||
export const BpmnUserGroupProperties: GroupProperties = {
|
||||
name: '人员设置',
|
||||
icon: 'el-icon-user-solid',
|
||||
properties: {
|
||||
/**
|
||||
* 处理人属性
|
||||
*/
|
||||
assignee: {
|
||||
component: PrefixLabelSelect,
|
||||
prefixTitle: '处理人',
|
||||
allowCreate: true,
|
||||
filterable: true,
|
||||
vSlots: {
|
||||
default: (): JSX.Element => UserOption,
|
||||
},
|
||||
},
|
||||
/**
|
||||
* 候选人属性
|
||||
*/
|
||||
candidateUsers: {
|
||||
component: PrefixLabelSelect,
|
||||
prefixTitle: '候选人',
|
||||
filterable: true,
|
||||
multiple: true,
|
||||
allowCreate: true,
|
||||
vSlots: {
|
||||
default: (): JSX.Element => UserOption,
|
||||
},
|
||||
getValue(businessObject: ModdleElement): string {
|
||||
console.warn('businessObject', businessObject);
|
||||
|
||||
return 'string' === typeof businessObject.candidateUsers
|
||||
? businessObject.candidateUsers.split(',')
|
||||
: businessObject.candidateUsers;
|
||||
},
|
||||
},
|
||||
/**
|
||||
* 循环基数
|
||||
*/
|
||||
loopCardinality: {
|
||||
component: ElInput,
|
||||
placeholder: '循环基数',
|
||||
type: 'number',
|
||||
vSlots: {
|
||||
prepend: (): JSX.Element => <div>循环基数</div>,
|
||||
},
|
||||
predicate(businessObject: ModdleElement): boolean {
|
||||
return businessObject.loopCharacteristics;
|
||||
},
|
||||
getValue(businessObject: ModdleElement): string {
|
||||
const loopCharacteristics = businessObject.loopCharacteristics;
|
||||
if (!loopCharacteristics) {
|
||||
return '';
|
||||
}
|
||||
return loopCharacteristics.loopCardinality?.body;
|
||||
},
|
||||
setValue(businessObject: ModdleElement, key: string, value: string): void {
|
||||
const bpmnContext = BpmnStore;
|
||||
const moddle = bpmnContext.getModeler().get('moddle');
|
||||
const loopCharacteristics = businessObject.loopCharacteristics;
|
||||
loopCharacteristics.loopCardinality = moddle.create('bpmn:FormalExpression', {
|
||||
body: value,
|
||||
});
|
||||
bpmnContext.getModeling().updateProperties(bpmnContext.getShape(), {
|
||||
loopCharacteristics: loopCharacteristics,
|
||||
});
|
||||
},
|
||||
},
|
||||
/**
|
||||
* 多实例完成条件
|
||||
* nr是number单词缩写
|
||||
* 1.nrOfInstances 实例总数。
|
||||
* 2.nrOfCompletedInstances 已经完成的实例个数
|
||||
* 3.loopCounter 已经循环的次数。
|
||||
* 4.nrOfActiveInstances 当前还没有完成的实例
|
||||
*/
|
||||
completionCondition: {
|
||||
component: ElInput,
|
||||
|
||||
placeholder:
|
||||
'如:${nrOfCompletedInstances/nrOfInstances >= 0.25} 表示完成数大于等于4分1时任务完成',
|
||||
vSlots: {
|
||||
prepend: (): JSX.Element => <div>完成条件</div>,
|
||||
},
|
||||
predicate(businessObject: ModdleElement): boolean {
|
||||
return businessObject.loopCharacteristics;
|
||||
},
|
||||
getValue(businessObject: ModdleElement): string {
|
||||
const loopCharacteristics = businessObject.loopCharacteristics;
|
||||
if (!loopCharacteristics) {
|
||||
return '';
|
||||
}
|
||||
return loopCharacteristics.completionCondition?.body;
|
||||
},
|
||||
setValue(businessObject: ModdleElement, key: string, value: string): void {
|
||||
const bpmnContext = BpmnStore;
|
||||
const moddle = bpmnContext.getModeler().get('moddle');
|
||||
const loopCharacteristics = businessObject.loopCharacteristics;
|
||||
loopCharacteristics.completionCondition = moddle.create('bpmn:FormalExpression', {
|
||||
body: value,
|
||||
});
|
||||
bpmnContext.getModeling().updateProperties(bpmnContext.getShape(), {
|
||||
loopCharacteristics: loopCharacteristics,
|
||||
});
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const LOOP_OPTIONS = [
|
||||
{ label: '无', value: 'Null' },
|
||||
{ label: '并行多重事件', value: 'Parallel' },
|
||||
{ label: '时序多重事件', value: 'Sequential' },
|
||||
{ label: '循环事件', value: 'StandardLoop' },
|
||||
];
|
||||
|
||||
const LoopOptions: JSX.Element = (
|
||||
<>
|
||||
{LOOP_OPTIONS.map((item) => {
|
||||
return <ElOption {...item} />;
|
||||
})}
|
||||
</>
|
||||
);
|
||||
/**
|
||||
* 任务的基本属性配置
|
||||
*/
|
||||
const BaseTaskProperties = {
|
||||
...CommonGroupProperties,
|
||||
properties: {
|
||||
...CommonGroupProperties.properties,
|
||||
loopCharacteristics: {
|
||||
component: PrefixLabelSelect,
|
||||
prefixTitle: '回路特性',
|
||||
vSlots: {
|
||||
default: (): JSX.Element => LoopOptions,
|
||||
},
|
||||
getValue(businessObject: ModdleElement): string {
|
||||
const loopCharacteristics = businessObject.loopCharacteristics;
|
||||
if (!loopCharacteristics) {
|
||||
return 'Null';
|
||||
}
|
||||
|
||||
if (loopCharacteristics.$type === 'bpmn:MultiInstanceLoopCharacteristics') {
|
||||
return loopCharacteristics.isSequential ? 'Sequential' : 'Parallel';
|
||||
} else {
|
||||
return 'StandardLoop';
|
||||
}
|
||||
},
|
||||
setValue(businessObject: ModdleElement, key: string, value: string): () => void {
|
||||
const shape = BpmnStore.getShape();
|
||||
const modeling = BpmnStore.getModeling();
|
||||
switch (value) {
|
||||
case 'Null':
|
||||
modeling.updateProperties(shape, {
|
||||
loopCharacteristics: null,
|
||||
});
|
||||
// delete businessObject.loopCharacteristics;
|
||||
break;
|
||||
case 'StandardLoop':
|
||||
BpmnStore.createElement('bpmn:StandardLoopCharacteristics', 'loopCharacteristics');
|
||||
break;
|
||||
default:
|
||||
BpmnStore.createElement(
|
||||
'bpmn:MultiInstanceLoopCharacteristics',
|
||||
'loopCharacteristics',
|
||||
{
|
||||
isSequential: value === 'Sequential',
|
||||
},
|
||||
);
|
||||
}
|
||||
return () => BpmnStore.refresh();
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const CommonGroupPropertiesArray = [
|
||||
BaseTaskProperties,
|
||||
TaskListenerProperties,
|
||||
FormGroupProperties,
|
||||
ExtensionGroupProperties,
|
||||
DocumentGroupProperties,
|
||||
];
|
||||
|
||||
export default {
|
||||
//普通任务
|
||||
'bpmn:Task': CommonGroupPropertiesArray,
|
||||
//用户任务
|
||||
'bpmn:UserTask': [
|
||||
BaseTaskProperties,
|
||||
BpmnUserGroupProperties,
|
||||
TaskListenerProperties,
|
||||
FormGroupProperties,
|
||||
ExtensionGroupProperties,
|
||||
DocumentGroupProperties,
|
||||
],
|
||||
//接收任务
|
||||
'bpmn:ReceiveTask': CommonGroupPropertiesArray,
|
||||
//发送任务
|
||||
'bpmn:SendTask': CommonGroupPropertiesArray,
|
||||
//手工任务
|
||||
'bpmn:ManualTask': CommonGroupPropertiesArray,
|
||||
//业务规则任务
|
||||
'bpmn:BusinessRuleTask': CommonGroupPropertiesArray,
|
||||
//服务任务
|
||||
'bpmn:ServiceTask': CommonGroupPropertiesArray,
|
||||
//脚本任务
|
||||
'bpmn:ScriptTask': CommonGroupPropertiesArray,
|
||||
//调用任务
|
||||
'bpmn:CallActivity': CommonGroupPropertiesArray,
|
||||
};
|
||||
@@ -0,0 +1,19 @@
|
||||
export default (key: string, name: string): string => {
|
||||
return `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<bpmn2:definitions
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:bpmn2="http://www.omg.org/spec/BPMN/20100524/MODEL"
|
||||
xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI"
|
||||
xmlns:dc="http://www.omg.org/spec/DD/20100524/DC"
|
||||
xmlns:di="http://www.omg.org/spec/DD/20100524/DI"
|
||||
xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL BPMN20.xsd"
|
||||
id="sample-diagram"
|
||||
targetNamespace="http://activiti.org/bpmn">
|
||||
<bpmn2:process id="Process_${key}" name="${name}" isExecutable="true">
|
||||
</bpmn2:process>
|
||||
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
|
||||
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_${key}">
|
||||
</bpmndi:BPMNPlane>
|
||||
</bpmndi:BPMNDiagram>
|
||||
</bpmn2:definitions>`;
|
||||
};
|
||||
@@ -0,0 +1,2 @@
|
||||
import translate from './translate';
|
||||
export default translate;
|
||||
@@ -0,0 +1,22 @@
|
||||
import i18nTranslate_zh from './zh';
|
||||
|
||||
const i18nMap: any = {
|
||||
zh: i18nTranslate_zh,
|
||||
};
|
||||
|
||||
export default function getI18nTranslate(
|
||||
i18n: string,
|
||||
): (template: string, replacements: { [index: string]: string }) => any {
|
||||
const i18nTranslate: any = i18nMap[i18n];
|
||||
return function customTranslate(template, replacements) {
|
||||
replacements = replacements || {};
|
||||
|
||||
// Translate
|
||||
template = i18nTranslate[template] || template;
|
||||
|
||||
// Replace
|
||||
return template.replace(/{([^}]+)}/g, function (_: string, key: string) {
|
||||
return replacements[key] || '{' + key + '}';
|
||||
});
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,239 @@
|
||||
/**
|
||||
* This is a sample file that should be replaced with the actual translation.
|
||||
*
|
||||
* Checkout https://github.com/bpmn-io/bpmn-js-i18n for a list of available
|
||||
* translations and labels to translate.
|
||||
*/
|
||||
export default {
|
||||
// 添加部分
|
||||
'Append EndEvent': '追加结束事件',
|
||||
'Append Gateway': '追加网关',
|
||||
'Append Task': '追加任务',
|
||||
'Append Intermediate/Boundary Event': '追加中间抛出事件/边界事件',
|
||||
|
||||
'Activate the global connect tool': '激活全局连接工具',
|
||||
'Append {type}': '添加 {type}',
|
||||
'Add Lane above': '在上面添加道',
|
||||
'Divide into two Lanes': '分割成两个道',
|
||||
'Divide into three Lanes': '分割成三个道',
|
||||
'Add Lane below': '在下面添加道',
|
||||
'Append compensation activity': '追加补偿活动',
|
||||
'Change type': '修改类型',
|
||||
'Connect using Association': '使用关联连接',
|
||||
'Connect using Sequence/MessageFlow or Association': '使用顺序/消息流或者关联连接',
|
||||
'Connect using DataInputAssociation': '使用数据输入关联连接',
|
||||
Remove: '移除',
|
||||
'Activate the hand tool': '激活抓手工具',
|
||||
'Activate the lasso tool': '激活套索工具',
|
||||
'Activate the create/remove space tool': '激活创建/删除空间工具',
|
||||
'Create expanded SubProcess': '创建扩展子过程',
|
||||
'Create IntermediateThrowEvent/BoundaryEvent': '创建中间抛出事件/边界事件',
|
||||
'Create Pool/Participant': '创建池/参与者',
|
||||
'Parallel Multi Instance': '并行多重事件',
|
||||
'Sequential Multi Instance': '时序多重事件',
|
||||
DataObjectReference: '数据对象参考',
|
||||
DataStoreReference: '数据存储参考',
|
||||
Loop: '循环',
|
||||
'Ad-hoc': '即席',
|
||||
'Create {type}': '创建 {type}',
|
||||
Task: '任务',
|
||||
'Send Task': '发送任务',
|
||||
'Receive Task': '接收任务',
|
||||
'User Task': '用户任务',
|
||||
'Manual Task': '手工任务',
|
||||
'Business Rule Task': '业务规则任务',
|
||||
'Service Task': '服务任务',
|
||||
'Script Task': '脚本任务',
|
||||
'Call Activity': '调用活动',
|
||||
'Sub Process (collapsed)': '子流程(折叠的)',
|
||||
'Sub Process (expanded)': '子流程(展开的)',
|
||||
'Start Event': '开始事件',
|
||||
StartEvent: '开始事件',
|
||||
'Intermediate Throw Event': '中间事件',
|
||||
'End Event': '结束事件',
|
||||
EndEvent: '结束事件',
|
||||
'Create StartEvent': '创建开始事件',
|
||||
'Create EndEvent': '创建结束事件',
|
||||
'Create Task': '创建任务',
|
||||
'Create Gateway': '创建网关',
|
||||
'Create DataObjectReference': '创建数据对象',
|
||||
'Create DataStoreReference': '创建数据存储',
|
||||
'Create Group': '创建分组',
|
||||
'Create Intermediate/Boundary Event': '创建中间/边界事件',
|
||||
'Message Start Event': '消息开始事件',
|
||||
'Timer Start Event': '定时开始事件',
|
||||
'Conditional Start Event': '条件开始事件',
|
||||
'Signal Start Event': '信号开始事件',
|
||||
'Error Start Event': '错误开始事件',
|
||||
'Escalation Start Event': '升级开始事件',
|
||||
'Compensation Start Event': '补偿开始事件',
|
||||
'Message Start Event (non-interrupting)': '消息开始事件(非中断)',
|
||||
'Timer Start Event (non-interrupting)': '定时开始事件(非中断)',
|
||||
'Conditional Start Event (non-interrupting)': '条件开始事件(非中断)',
|
||||
'Signal Start Event (non-interrupting)': '信号开始事件(非中断)',
|
||||
'Escalation Start Event (non-interrupting)': '升级开始事件(非中断)',
|
||||
'Message Intermediate Catch Event': '消息中间捕获事件',
|
||||
'Message Intermediate Throw Event': '消息中间抛出事件',
|
||||
'Timer Intermediate Catch Event': '定时中间捕获事件',
|
||||
'Escalation Intermediate Throw Event': '升级中间抛出事件',
|
||||
'Conditional Intermediate Catch Event': '条件中间捕获事件',
|
||||
'Link Intermediate Catch Event': '链接中间捕获事件',
|
||||
'Link Intermediate Throw Event': '链接中间抛出事件',
|
||||
'Compensation Intermediate Throw Event': '补偿中间抛出事件',
|
||||
'Signal Intermediate Catch Event': '信号中间捕获事件',
|
||||
'Signal Intermediate Throw Event': '信号中间抛出事件',
|
||||
'Message End Event': '消息结束事件',
|
||||
'Escalation End Event': '定时结束事件',
|
||||
'Error End Event': '错误结束事件',
|
||||
'Cancel End Event': '取消结束事件',
|
||||
'Compensation End Event': '补偿结束事件',
|
||||
'Signal End Event': '信号结束事件',
|
||||
'Terminate End Event': '终止结束事件',
|
||||
'Message Boundary Event': '消息边界事件',
|
||||
'Message Boundary Event (non-interrupting)': '消息边界事件(非中断)',
|
||||
'Timer Boundary Event': '定时边界事件',
|
||||
'Timer Boundary Event (non-interrupting)': '定时边界事件(非中断)',
|
||||
'Escalation Boundary Event': '升级边界事件',
|
||||
'Escalation Boundary Event (non-interrupting)': '升级边界事件(非中断)',
|
||||
'Conditional Boundary Event': '条件边界事件',
|
||||
'Conditional Boundary Event (non-interrupting)': '条件边界事件(非中断)',
|
||||
'Error Boundary Event': '错误边界事件',
|
||||
'Cancel Boundary Event': '取消边界事件',
|
||||
'Signal Boundary Event': '信号边界事件',
|
||||
'Signal Boundary Event (non-interrupting)': '信号边界事件(非中断)',
|
||||
'Compensation Boundary Event': '补偿边界事件',
|
||||
'Exclusive Gateway': '互斥网关',
|
||||
'Parallel Gateway': '并行网关',
|
||||
'Inclusive Gateway': '相容网关',
|
||||
'Complex Gateway': '复杂网关',
|
||||
'Event based Gateway': '事件网关',
|
||||
Transaction: '转运',
|
||||
'Sub Process': '子流程',
|
||||
'Event Sub Process': '事件子流程',
|
||||
'Collapsed Pool': '折叠池',
|
||||
'Expanded Pool': '展开池',
|
||||
|
||||
// Errors
|
||||
'no parent for {element} in {parent}': '在{parent}里,{element}没有父类',
|
||||
'no shape type specified': '没有指定的形状类型',
|
||||
'flow elements must be children of pools/participants': '流元素必须是池/参与者的子类',
|
||||
'out of bounds release': 'out of bounds release',
|
||||
'more than {count} child lanes': '子道大于{count} ',
|
||||
'element required': '元素不能为空',
|
||||
'diagram not part of bpmn:Definitions': '流程图不符合bpmn规范',
|
||||
'no diagram to display': '没有可展示的流程图',
|
||||
'no process or collaboration to display': '没有可展示的流程/协作',
|
||||
'element {element} referenced by {referenced}#{property} not yet drawn':
|
||||
'由{referenced}#{property}引用的{element}元素仍未绘制',
|
||||
'already rendered {element}': '{element} 已被渲染',
|
||||
'failed to import {element}': '导入{element}失败',
|
||||
//属性面板的参数
|
||||
Id: '编号',
|
||||
Name: '名称',
|
||||
General: '常规',
|
||||
Details: '详情',
|
||||
'Message Name': '消息名称',
|
||||
Message: '消息',
|
||||
Initiator: '创建者',
|
||||
'Asynchronous Continuations': '持续异步',
|
||||
'Asynchronous Before': '异步前',
|
||||
'Asynchronous After': '异步后',
|
||||
'Job Configuration': '工作配置',
|
||||
Exclusive: '排除',
|
||||
'Job Priority': '工作优先级',
|
||||
'Retry Time Cycle': '重试时间周期',
|
||||
Documentation: '文档',
|
||||
'Element Documentation': '元素文档',
|
||||
'History Configuration': '历史配置',
|
||||
'History Time To Live': '历史的生存时间',
|
||||
Forms: '表单',
|
||||
'Form Key': '表单key',
|
||||
'Form Fields': '表单字段',
|
||||
'Business Key': '业务key',
|
||||
'Form Field': '表单字段',
|
||||
ID: '编号',
|
||||
Type: '类型',
|
||||
Label: '名称',
|
||||
'Default Value': '默认值',
|
||||
'Default Flow': '默认流转路径',
|
||||
'Conditional Flow': '条件流转路径',
|
||||
'Sequence Flow': '普通流转路径',
|
||||
Validation: '校验',
|
||||
'Add Constraint': '添加约束',
|
||||
Config: '配置',
|
||||
Properties: '属性',
|
||||
'Add Property': '添加属性',
|
||||
Value: '值',
|
||||
Listeners: '监听器',
|
||||
'Execution Listener': '执行监听',
|
||||
'Event Type': '事件类型',
|
||||
'Listener Type': '监听器类型',
|
||||
'Java Class': 'Java类',
|
||||
Expression: '表达式',
|
||||
'Must provide a value': '必须提供一个值',
|
||||
'Delegate Expression': '代理表达式',
|
||||
Script: '脚本',
|
||||
'Script Format': '脚本格式',
|
||||
'Script Type': '脚本类型',
|
||||
'Inline Script': '内联脚本',
|
||||
'External Script': '外部脚本',
|
||||
Resource: '资源',
|
||||
'Field Injection': '字段注入',
|
||||
Extensions: '扩展',
|
||||
'Input/Output': '输入/输出',
|
||||
'Input Parameters': '输入参数',
|
||||
'Output Parameters': '输出参数',
|
||||
Parameters: '参数',
|
||||
'Output Parameter': '输出参数',
|
||||
'Timer Definition Type': '定时器定义类型',
|
||||
'Timer Definition': '定时器定义',
|
||||
Date: '日期',
|
||||
Duration: '持续',
|
||||
Cycle: '循环',
|
||||
Signal: '信号',
|
||||
'Signal Name': '信号名称',
|
||||
Escalation: '升级',
|
||||
Error: '错误',
|
||||
'Link Name': '链接名称',
|
||||
Condition: '条件名称',
|
||||
'Variable Name': '变量名称',
|
||||
'Variable Event': '变量事件',
|
||||
'Specify more than one variable change event as a comma separated list.':
|
||||
'多个变量事件以逗号隔开',
|
||||
'Wait for Completion': '等待完成',
|
||||
'Activity Ref': '活动参考',
|
||||
'Version Tag': '版本标签',
|
||||
Executable: '可执行文件',
|
||||
'External Task Configuration': '扩展任务配置',
|
||||
'Task Priority': '任务优先级',
|
||||
External: '外部',
|
||||
Connector: '连接器',
|
||||
'Must configure Connector': '必须配置连接器',
|
||||
'Connector Id': '连接器编号',
|
||||
Implementation: '实现方式',
|
||||
'Field Injections': '字段注入',
|
||||
Fields: '字段',
|
||||
'Result Variable': '结果变量',
|
||||
Topic: '主题',
|
||||
'Configure Connector': '配置连接器',
|
||||
'Input Parameter': '输入参数',
|
||||
Assignee: '代理人',
|
||||
'Candidate Users': '候选用户',
|
||||
'Candidate Groups': '候选组',
|
||||
'Due Date': '到期时间',
|
||||
'Follow Up Date': '跟踪日期',
|
||||
Priority: '优先级',
|
||||
'The follow up date as an EL expression (e.g. ${someDate} or an ISO date (e.g. 2015-06-26T09:54:00)':
|
||||
'跟踪日期必须符合EL表达式,如: ${someDate} ,或者一个ISO标准日期,如:2015-06-26T09:54:00',
|
||||
'The due date as an EL expression (e.g. ${someDate} or an ISO date (e.g. 2015-06-26T09:54:00)':
|
||||
'跟踪日期必须符合EL表达式,如: ${someDate} ,或者一个ISO标准日期,如:2015-06-26T09:54:00',
|
||||
Variables: '变量',
|
||||
'Candidate Starter Configuration': '候选人起动器配置',
|
||||
'Candidate Starter Groups': '候选人起动器组',
|
||||
'This maps to the process definition key.': '这映射到流程定义键。',
|
||||
'Candidate Starter Users': '候选人起动器的用户',
|
||||
'Specify more than one user as a comma separated list.': '指定多个用户作为逗号分隔的列表。',
|
||||
'Tasklist Configuration': 'Tasklist配置',
|
||||
Startable: '启动',
|
||||
'Specify more than one group as a comma separated list.': '指定多个组作为逗号分隔的列表。',
|
||||
};
|
||||
@@ -0,0 +1,143 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:activiti="http://activiti.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" xmlns:tns="http://www.activiti.org/test" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:yaoqiang="http://bpmn.sourceforge.net" exporter="Yaoqiang BPMN Editor" exporterVersion="5.3" expressionLanguage="http://www.w3.org/1999/XPath" id="m1555922186343" name="" targetNamespace="http://www.activiti.org/test" typeLanguage="http://www.w3.org/2001/XMLSchema" xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL http://bpmn.sourceforge.net/schemas/BPMN20.xsd">
|
||||
<process id="com.github.yiuman.citrus.workflow.Leave" isClosed="false" isExecutable="true" processType="None">
|
||||
<extensionElements>
|
||||
<yaoqiang:description/>
|
||||
<yaoqiang:pageFormat height="841.8897637795276" imageableHeight="831.8897637795276" imageableWidth="588.1102362204724" imageableX="5.0" imageableY="5.0" orientation="0" width="598.1102362204724"/>
|
||||
<yaoqiang:page background="#FFFFFF" horizontalCount="1" verticalCount="1"/>
|
||||
<yaoqiang:pageFormat height="600.0" imageableHeight="831.8897637795276" imageableWidth="600.0" imageableX="5.0" imageableY="5.0" orientation="0" width="600.0"/>
|
||||
<yaoqiang:page background="#FFFFFF" horizontalCount="1" verticalCount="1"/>
|
||||
</extensionElements>
|
||||
<userTask activiti:assignee="${currentUserId}" activiti:exclusive="true" completionQuantity="1" id="_4" implementation="##unspecified" isForCompensation="false" name="提交请假" startQuantity="1">
|
||||
<incoming>_7</incoming>
|
||||
<incoming>_12</incoming>
|
||||
<incoming>_13</incoming>
|
||||
<outgoing>_8</outgoing>
|
||||
</userTask>
|
||||
<userTask activiti:assignee="${currentUserId}" activiti:exclusive="true" completionQuantity="1" id="_5" implementation="##unspecified" isForCompensation="false" name="部门经理审批" startQuantity="1">
|
||||
<incoming>_8</incoming>
|
||||
<outgoing>_9</outgoing>
|
||||
<outgoing>_12</outgoing>
|
||||
</userTask>
|
||||
<userTask activiti:assignee="${currentUserId}" activiti:exclusive="true" completionQuantity="1" id="_6" implementation="##unspecified" isForCompensation="false" name="总经理审批" startQuantity="1">
|
||||
<incoming>_9</incoming>
|
||||
<outgoing>_10</outgoing>
|
||||
<outgoing>_13</outgoing>
|
||||
</userTask>
|
||||
<sequenceFlow id="_7" sourceRef="_2" targetRef="_4"/>
|
||||
<sequenceFlow id="_8" sourceRef="_4" targetRef="_5"/>
|
||||
<sequenceFlow id="_9" sourceRef="_5" targetRef="_6">
|
||||
<conditionExpression xsi:type="tFormalExpression">
|
||||
<![CDATA[
|
||||
]]>
|
||||
</conditionExpression>
|
||||
</sequenceFlow>
|
||||
<sequenceFlow id="_10" sourceRef="_6" targetRef="_3">
|
||||
<conditionExpression xsi:type="tFormalExpression">
|
||||
<![CDATA[
|
||||
]]>
|
||||
</conditionExpression>
|
||||
</sequenceFlow>
|
||||
<sequenceFlow id="_12" name="驳回" sourceRef="_5" targetRef="_4">
|
||||
<conditionExpression xsi:type="tFormalExpression">
|
||||
<![CDATA[
|
||||
]]>
|
||||
</conditionExpression>
|
||||
</sequenceFlow>
|
||||
<sequenceFlow id="_13" name="驳回" sourceRef="_6" targetRef="_4">
|
||||
<conditionExpression xsi:type="tFormalExpression">
|
||||
<![CDATA[
|
||||
]]>
|
||||
</conditionExpression>
|
||||
</sequenceFlow>
|
||||
<startEvent id="_2" isInterrupting="true" name="StartEvent" parallelMultiple="false">
|
||||
<outgoing>_7</outgoing>
|
||||
<outputSet/>
|
||||
</startEvent>
|
||||
<endEvent id="_3" name="EndEvent">
|
||||
<incoming>_10</incoming>
|
||||
<inputSet/>
|
||||
</endEvent>
|
||||
</process>
|
||||
<bpmndi:BPMNDiagram id="Yaoqiang_Diagram-com.github.yiuman.citrus.workflow.Leave" name="New Diagram" resolution="96.0">
|
||||
<bpmndi:BPMNPlane bpmnElement="com.github.yiuman.citrus.workflow.Leave">
|
||||
<bpmndi:BPMNShape bpmnElement="_4" id="Yaoqiang-_4">
|
||||
<omgdc:Bounds height="55.0" width="85.0" x="338.0" y="110.0"/>
|
||||
<bpmndi:BPMNLabel>
|
||||
<omgdc:Bounds height="18.96" width="50.0" x="355.5" y="130.02"/>
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape bpmnElement="_5" id="Yaoqiang-_5">
|
||||
<omgdc:Bounds height="55.0" width="85.0" x="338.0" y="218.0"/>
|
||||
<bpmndi:BPMNLabel>
|
||||
<omgdc:Bounds height="18.96" width="72.0" x="344.5" y="238.02"/>
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape bpmnElement="_6" id="Yaoqiang-_6">
|
||||
<omgdc:Bounds height="55.0" width="85.0" x="338.0" y="319.0"/>
|
||||
<bpmndi:BPMNLabel>
|
||||
<omgdc:Bounds height="18.96" width="61.0" x="350.0" y="339.02"/>
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape bpmnElement="_2" id="Yaoqiang-_2">
|
||||
<omgdc:Bounds height="32.0" width="32.0" x="363.0" y="30.0"/>
|
||||
<bpmndi:BPMNLabel>
|
||||
<omgdc:Bounds height="18.96" width="60.0" x="349.0" y="70.52"/>
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape bpmnElement="_3" id="Yaoqiang-_3">
|
||||
<omgdc:Bounds height="32.0" width="32.0" x="363.0" y="412.0"/>
|
||||
<bpmndi:BPMNLabel>
|
||||
<omgdc:Bounds height="18.96" width="55.0" x="351.5" y="452.52"/>
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNEdge bpmnElement="_13" id="Yaoqiang-_13">
|
||||
<omgdi:waypoint x="338.0" y="346.0"/>
|
||||
<omgdi:waypoint x="240.0" y="346.0"/>
|
||||
<omgdi:waypoint x="240.0" y="300.0"/>
|
||||
<omgdi:waypoint x="240.0" y="137.0"/>
|
||||
<omgdi:waypoint x="338.0" y="137.5"/>
|
||||
<bpmndi:BPMNLabel>
|
||||
<omgdc:Bounds height="18.96" width="28.0" x="226.0" y="232.27"/>
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge bpmnElement="_12" id="Yaoqiang-_12">
|
||||
<omgdi:waypoint x="423.0" y="245.0"/>
|
||||
<omgdi:waypoint x="514.0" y="245.0"/>
|
||||
<omgdi:waypoint x="514.0" y="137.0"/>
|
||||
<omgdi:waypoint x="423.0" y="137.5"/>
|
||||
<bpmndi:BPMNLabel>
|
||||
<omgdc:Bounds height="18.96" width="28.0" x="500.0" y="181.77"/>
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge bpmnElement="_9" id="Yaoqiang-_9">
|
||||
<omgdi:waypoint x="380.5" y="273.0"/>
|
||||
<omgdi:waypoint x="380.5" y="319.0"/>
|
||||
<bpmndi:BPMNLabel>
|
||||
<omgdc:Bounds height="18.96" width="6.0" x="377.5" y="286.52"/>
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge bpmnElement="_8" id="Yaoqiang-_8">
|
||||
<omgdi:waypoint x="380.5" y="165.0"/>
|
||||
<omgdi:waypoint x="380.5" y="218.0"/>
|
||||
<bpmndi:BPMNLabel>
|
||||
<omgdc:Bounds height="18.96" width="6.0" x="377.5" y="182.02"/>
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge bpmnElement="_10" id="Yaoqiang-_10">
|
||||
<omgdi:waypoint x="379.0" y="374.0"/>
|
||||
<omgdi:waypoint x="379.0" y="412.0"/>
|
||||
<bpmndi:BPMNLabel>
|
||||
<omgdc:Bounds height="18.96" width="6.0" x="376.0" y="383.52"/>
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge bpmnElement="_7" id="Yaoqiang-_7">
|
||||
<omgdi:waypoint x="379.0" y="62.0"/>
|
||||
<omgdi:waypoint x="379.0" y="110.0"/>
|
||||
<bpmndi:BPMNLabel>
|
||||
<omgdc:Bounds height="18.96" width="6.0" x="376.0" y="76.52"/>
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNEdge>
|
||||
</bpmndi:BPMNPlane>
|
||||
</bpmndi:BPMNDiagram>
|
||||
</definitions>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,149 @@
|
||||
import { reactive, toRaw, nextTick } from 'vue';
|
||||
import BpmnGroupPropertiesConfig from './config';
|
||||
import { resolveTypeName } from './config/TypeNameMapping';
|
||||
import Modeler from 'bpmn-js/lib/Modeler';
|
||||
import { BpmnState, BpmnContext, ModdleElement } from './type';
|
||||
|
||||
const bpmnState = reactive<BpmnState>({
|
||||
activeElement: null,
|
||||
businessObject: null,
|
||||
activeBindDefine: null,
|
||||
isActive: false,
|
||||
});
|
||||
|
||||
//刷新状态
|
||||
function refreshState(elementRegistry: any, elementAction: any): void {
|
||||
if (!bpmnState || !elementAction) {
|
||||
return;
|
||||
}
|
||||
bpmnState.activeElement = elementAction;
|
||||
const shape = elementRegistry.get(elementAction.element.id);
|
||||
bpmnState.businessObject = shape ? shape.businessObject : {};
|
||||
bpmnState.isActive = true;
|
||||
bpmnState.activeBindDefine = shape ? BpmnGroupPropertiesConfig[elementAction.element.type] : null;
|
||||
}
|
||||
|
||||
export const BpmnStore: BpmnContext = {
|
||||
modeler: null,
|
||||
state: bpmnState,
|
||||
getState() {
|
||||
return this.state;
|
||||
},
|
||||
initModeler(options) {
|
||||
this.modeler = new Modeler(options);
|
||||
this.modeler.get('keyboard').bind(document);
|
||||
const elementRegistry = this.modeler.get('elementRegistry');
|
||||
//添加click、shapeAdd事件,属性更新后的事件,用于变更当前的业务对象,刷新属性配置栏
|
||||
['element.click', 'shape.added'].forEach((event) => {
|
||||
BpmnStore.addEventListener(event, function (elementAction) {
|
||||
// console.warn('elementAction', elementAction);
|
||||
const element = elementAction.element || elementAction.context.element;
|
||||
if (element && (!bpmnState.activeElement || bpmnState.activeElement.id !== element.id)) {
|
||||
bpmnState.businessObject = null;
|
||||
nextTick().then(() => {
|
||||
refreshState(elementRegistry, elementAction);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
// this.addEventListener('element.changed', function (elementAction: any) {
|
||||
// 这里是处理修改shape中的label后导致的不及时更新问题
|
||||
// 现将业务对象至为空对象,视图更新后,再重新进行渲染
|
||||
// bpmnState.businessObject = {};
|
||||
// nextTick(() => {
|
||||
// refreshState(context.modeler.get('elementRegistry'), elementAction);
|
||||
// });
|
||||
// });
|
||||
},
|
||||
getModeler() {
|
||||
return this.modeler;
|
||||
},
|
||||
refresh() {
|
||||
bpmnState.businessObject = null;
|
||||
nextTick().then(() => {
|
||||
refreshState(this.modeler.get('elementRegistry'), bpmnState.activeElement);
|
||||
});
|
||||
// nextTick(() => {
|
||||
//
|
||||
// });
|
||||
},
|
||||
getShape() {
|
||||
return this.getShapeById(this.getState().activeElement.element.id);
|
||||
},
|
||||
getShapeById(id) {
|
||||
const elementRegistry = this.getModeler().get('elementRegistry');
|
||||
return elementRegistry.get(id);
|
||||
},
|
||||
getBpmnFactory() {
|
||||
return this.modeler.get('bpmnFactory');
|
||||
},
|
||||
createElement(nodeName, modelName, value, multiple) {
|
||||
const newElement = this.getBpmnFactory().create(nodeName, value);
|
||||
this.getModeling().updateProperties(this.getShape(), {
|
||||
[modelName]: multiple ? [newElement] : newElement,
|
||||
});
|
||||
},
|
||||
importXML(string) {
|
||||
return this.modeler.importXML(string);
|
||||
},
|
||||
getXML() {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.getModeler()
|
||||
.saveXML({ format: true })
|
||||
.then((response: { xml: string }) => {
|
||||
resolve(response);
|
||||
})
|
||||
.catch((err: unknown) => {
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
},
|
||||
getSVG() {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.getModeler()
|
||||
.saveSVG()
|
||||
.then((response: { svg: string }) => {
|
||||
resolve(response);
|
||||
})
|
||||
.catch((err: unknown) => {
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
},
|
||||
getModeling() {
|
||||
return this.getModeler().get('modeling');
|
||||
},
|
||||
getActiveElement() {
|
||||
return this.getState().activeElement;
|
||||
},
|
||||
getActiveElementName() {
|
||||
const businessObject = this.getBusinessObject();
|
||||
return businessObject ? resolveTypeName(businessObject) : '';
|
||||
},
|
||||
getBusinessObject() {
|
||||
return this.getState().businessObject;
|
||||
},
|
||||
addEventListener(string, func) {
|
||||
this.getModeler()
|
||||
.get('eventBus')
|
||||
.on(string, function (e: any) {
|
||||
func(e);
|
||||
});
|
||||
},
|
||||
updateExtensionElements(elementName, value) {
|
||||
const moddle = this.getModeler().get('moddle');
|
||||
const element = this.getShape();
|
||||
const extensionElements = this.getBusinessObject()?.extensionElements;
|
||||
// 截取不是扩展属性的属性
|
||||
const otherExtensions =
|
||||
extensionElements?.values
|
||||
?.filter((ex: any) => ex.$type !== elementName)
|
||||
.map((item: ModdleElement) => toRaw(item)) || [];
|
||||
|
||||
// 重建扩展属性
|
||||
const extensions = moddle.create('bpmn:ExtensionElements', {
|
||||
values: otherExtensions.concat(value instanceof Array ? value : [value]),
|
||||
});
|
||||
this.getModeling().updateProperties(element, { extensionElements: extensions });
|
||||
},
|
||||
};
|
||||
Vendored
+141
@@ -0,0 +1,141 @@
|
||||
// 模型节点
|
||||
import { UnwrapRef } from 'vue';
|
||||
import Modeler from 'bpmn-js/lib/Modeler';
|
||||
import { GroupProperties } from './config';
|
||||
|
||||
export interface BpmnState {
|
||||
/**
|
||||
* 当前活动的节点
|
||||
*/
|
||||
activeElement: any;
|
||||
/**
|
||||
* 当前活动节点的业务对象
|
||||
*/
|
||||
businessObject: any;
|
||||
/**
|
||||
* 是否活动
|
||||
*/
|
||||
isActive: boolean;
|
||||
|
||||
/**
|
||||
* 当前活动节点的绑定字段配置
|
||||
*/
|
||||
activeBindDefine: Array<GroupProperties> | null | never;
|
||||
}
|
||||
|
||||
export interface ModdleElement {
|
||||
id: string;
|
||||
$type: string;
|
||||
value?: [ModdleElement];
|
||||
$attrs: { [key: string]: any };
|
||||
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
/**
|
||||
* 流程管理的上下文
|
||||
*/
|
||||
export interface BpmnContext {
|
||||
/**
|
||||
* 流程设计器
|
||||
*/
|
||||
modeler: any;
|
||||
/**
|
||||
* 状态管理
|
||||
*/
|
||||
state: UnwrapRef<BpmnState>;
|
||||
|
||||
/**
|
||||
* 获取当前的状态
|
||||
*/
|
||||
getState(): UnwrapRef<BpmnState>;
|
||||
|
||||
refresh: () => void;
|
||||
|
||||
/**
|
||||
* 初始化流程设计器
|
||||
* @param options 流程设计器参数
|
||||
*/
|
||||
initModeler(options: unknown): void;
|
||||
|
||||
/**
|
||||
*获取设计器
|
||||
*/
|
||||
getModeler(): typeof Modeler;
|
||||
|
||||
/**
|
||||
* 导入xml
|
||||
* @param xml xml字符串
|
||||
*/
|
||||
importXML(xml: string): Promise<Array<string> | any>;
|
||||
|
||||
/**
|
||||
* 获取流程xml
|
||||
*/
|
||||
getXML(): Promise<{ xml: string }>;
|
||||
|
||||
/**
|
||||
* 获取流程的SVG图
|
||||
*/
|
||||
getSVG(): Promise<{ svg: string }>;
|
||||
|
||||
/**
|
||||
* 获取当前节点的Shape对象,此对象用于操作节点与业务流程对象等
|
||||
*/
|
||||
getShape(): any;
|
||||
|
||||
getShapeById(id: string): any;
|
||||
|
||||
/**
|
||||
* 获取当前的流程的业务对象
|
||||
*/
|
||||
getBusinessObject(): any;
|
||||
|
||||
/**
|
||||
* 获取当前的活动节点
|
||||
*/
|
||||
getActiveElement(): any;
|
||||
|
||||
/**
|
||||
* 获取当前节点的名称
|
||||
*/
|
||||
getActiveElementName(): string;
|
||||
|
||||
/**
|
||||
* 获取当前节点的modeling
|
||||
*/
|
||||
getModeling(): any;
|
||||
|
||||
/**
|
||||
* 获取bpmnFactory
|
||||
*/
|
||||
getBpmnFactory(): any;
|
||||
|
||||
/**
|
||||
* 创建节点
|
||||
* @param nodeName 节点名称
|
||||
* @param modelName 模型名称
|
||||
* @param value 几点值
|
||||
* @param multiple 是否是多节点
|
||||
*/
|
||||
createElement(
|
||||
nodeName: string,
|
||||
modelName: string,
|
||||
value?: { [key: string]: any } | never,
|
||||
multiple?: boolean,
|
||||
): void;
|
||||
|
||||
/**
|
||||
* 添加设计器事件监听
|
||||
* @param name 事件名称
|
||||
* @param func 触发事件的回调
|
||||
*/
|
||||
addEventListener(name: string, func: (e: any) => void): void;
|
||||
|
||||
/**
|
||||
* 更新扩展节点
|
||||
* @param elementName 节点名
|
||||
* @param value 值
|
||||
*/
|
||||
updateExtensionElements(elementName: string, value: ModdleElement | Array<ModdleElement>): void;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
<template>
|
||||
<slot v-if="getUserAuthBtnList" />
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent } from 'vue';
|
||||
import { useStore } from '/@/store/index';
|
||||
export default defineComponent({
|
||||
name: 'auth',
|
||||
props: {
|
||||
value: {
|
||||
type: String,
|
||||
default: () => '',
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
const store = useStore();
|
||||
// 获取 vuex 中的用户权限
|
||||
const getUserAuthBtnList = computed(() => {
|
||||
return store.state.userInfos.userInfos.authBtnList.some((v: string) => v === props.value);
|
||||
});
|
||||
return {
|
||||
getUserAuthBtnList,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,28 @@
|
||||
<template>
|
||||
<slot v-if="getUserAuthBtnList" />
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent } from 'vue';
|
||||
import { useStore } from '/@/store/index';
|
||||
import { judementSameArr } from '/@/utils/arrayOperation';
|
||||
export default defineComponent({
|
||||
name: 'authAll',
|
||||
props: {
|
||||
value: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
const store = useStore();
|
||||
// 获取 vuex 中的用户权限
|
||||
const getUserAuthBtnList = computed(() => {
|
||||
return judementSameArr(props.value, store.state.userInfos.userInfos.authBtnList);
|
||||
});
|
||||
return {
|
||||
getUserAuthBtnList,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,33 @@
|
||||
<template>
|
||||
<slot v-if="getUserAuthBtnList" />
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent } from 'vue';
|
||||
import { useStore } from '/@/store/index';
|
||||
export default defineComponent({
|
||||
name: 'auths',
|
||||
props: {
|
||||
value: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
const store = useStore();
|
||||
// 获取 vuex 中的用户权限
|
||||
const getUserAuthBtnList = computed(() => {
|
||||
let flag = false;
|
||||
store.state.userInfos.userInfos.authBtnList.map((val: string) => {
|
||||
props.value.map((v) => {
|
||||
if (val === v) flag = true;
|
||||
});
|
||||
});
|
||||
return flag;
|
||||
});
|
||||
return {
|
||||
getUserAuthBtnList,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,148 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-dialog title="更换头像" v-model="isShowDialog" width="769px">
|
||||
<div class="cropper-warp">
|
||||
<div class="cropper-warp-left">
|
||||
<img :src="cropperImg" class="cropper-warp-left-img" />
|
||||
</div>
|
||||
<div class="cropper-warp-right">
|
||||
<div class="cropper-warp-right-title">预览</div>
|
||||
<div class="cropper-warp-right-item">
|
||||
<div class="cropper-warp-right-value">
|
||||
<img :src="cropperImgBase64" class="cropper-warp-right-value-img" />
|
||||
</div>
|
||||
<div class="cropper-warp-right-label">100 x 100</div>
|
||||
</div>
|
||||
<div class="cropper-warp-right-item">
|
||||
<div class="cropper-warp-right-value">
|
||||
<img :src="cropperImgBase64" class="cropper-warp-right-value-img cropper-size" />
|
||||
</div>
|
||||
<div class="cropper-warp-right-label">50 x 50</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="onCancel" size="default">取 消</el-button>
|
||||
<el-button type="primary" @click="onSubmit" size="default">更 换</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { reactive, toRefs, nextTick, defineComponent } from 'vue';
|
||||
import Cropper from 'cropperjs';
|
||||
import 'cropperjs/dist/cropper.css';
|
||||
export default defineComponent({
|
||||
name: 'cropperIndex',
|
||||
setup() {
|
||||
const state = reactive({
|
||||
isShowDialog: false,
|
||||
cropperImg: '',
|
||||
cropperImgBase64: '',
|
||||
cropper: null,
|
||||
});
|
||||
// 打开弹窗
|
||||
const openDialog = (imgs: any) => {
|
||||
state.cropperImg = imgs;
|
||||
state.isShowDialog = true;
|
||||
nextTick(() => {
|
||||
initCropper();
|
||||
});
|
||||
};
|
||||
// 关闭弹窗
|
||||
const closeDialog = () => {
|
||||
state.isShowDialog = false;
|
||||
};
|
||||
// 取消
|
||||
const onCancel = () => {
|
||||
closeDialog();
|
||||
};
|
||||
// 更换
|
||||
const onSubmit = () => {
|
||||
// state.cropperImgBase64 = state.cropper.getCroppedCanvas().toDataURL('image/jpeg');
|
||||
};
|
||||
// 初始化cropperjs图片裁剪
|
||||
const initCropper = () => {
|
||||
const letImg: any = document.querySelector('.cropper-warp-left-img');
|
||||
(<any>state.cropper) = new Cropper(letImg, {
|
||||
viewMode: 1,
|
||||
dragMode: 'none',
|
||||
initialAspectRatio: 1,
|
||||
aspectRatio: 1,
|
||||
preview: '.before',
|
||||
background: false,
|
||||
autoCropArea: 0.6,
|
||||
zoomOnWheel: false,
|
||||
crop: () => {
|
||||
state.cropperImgBase64 = (<any>state.cropper).getCroppedCanvas().toDataURL('image/jpeg');
|
||||
},
|
||||
});
|
||||
};
|
||||
return {
|
||||
openDialog,
|
||||
closeDialog,
|
||||
onCancel,
|
||||
onSubmit,
|
||||
initCropper,
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.cropper-warp {
|
||||
display: flex;
|
||||
.cropper-warp-left {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
height: 350px;
|
||||
flex: 1;
|
||||
border: var(--el-border-base);
|
||||
background: var(--el-color-white);
|
||||
overflow: hidden;
|
||||
background-repeat: no-repeat;
|
||||
cursor: move;
|
||||
border-radius: var(--el-border-radius-base);
|
||||
.cropper-warp-left-img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
.cropper-warp-right {
|
||||
width: 150px;
|
||||
height: 350px;
|
||||
.cropper-warp-right-title {
|
||||
text-align: center;
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
}
|
||||
.cropper-warp-right-item {
|
||||
margin: 15px 0;
|
||||
.cropper-warp-right-value {
|
||||
display: flex;
|
||||
.cropper-warp-right-value-img {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
border-radius: var(--el-border-radius-circle);
|
||||
margin: auto;
|
||||
}
|
||||
.cropper-size {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
}
|
||||
}
|
||||
.cropper-warp-right-label {
|
||||
text-align: center;
|
||||
font-size: 12px;
|
||||
color: var(--el-text-color-primary);
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,79 @@
|
||||
<template>
|
||||
<div class="editor-container">
|
||||
<div :id="id"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { toRefs, reactive, onMounted, watch, defineComponent } from 'vue';
|
||||
import wangeditor from 'wangeditor';
|
||||
|
||||
// 定义接口来定义对象的类型
|
||||
interface WangeditorState {
|
||||
editor: any;
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
name: 'wngEditor',
|
||||
props: {
|
||||
// 节点 id
|
||||
id: {
|
||||
type: String,
|
||||
default: () => 'wangeditor',
|
||||
},
|
||||
// 是否禁用
|
||||
isDisable: {
|
||||
type: Boolean,
|
||||
default: () => false,
|
||||
},
|
||||
// 内容框默认 placeholder
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: () => '请输入内容',
|
||||
},
|
||||
// 双向绑定
|
||||
// 双向绑定值,字段名为固定,改了之后将不生效
|
||||
// 参考:https://v3.cn.vuejs.org/guide/migration/v-model.html#%E8%BF%81%E7%A7%BB%E7%AD%96%E7%95%A5
|
||||
modelValue: String,
|
||||
},
|
||||
setup(props, { emit }) {
|
||||
const state = reactive<WangeditorState>({
|
||||
editor: null,
|
||||
});
|
||||
// 初始化富文本
|
||||
// https://doc.wangeditor.com/
|
||||
const initWangeditor = () => {
|
||||
state.editor = new wangeditor(`#${props.id}`);
|
||||
state.editor.config.zIndex = 1;
|
||||
state.editor.config.placeholder = props.placeholder;
|
||||
state.editor.config.uploadImgShowBase64 = true;
|
||||
state.editor.config.showLinkImg = false;
|
||||
onWangeditorChange();
|
||||
state.editor.create();
|
||||
state.editor.txt.html(props.modelValue);
|
||||
props.isDisable ? state.editor.disable() : state.editor.enable();
|
||||
};
|
||||
// 内容改变时
|
||||
const onWangeditorChange = () => {
|
||||
state.editor.config.onchange = (html: string) => {
|
||||
emit('update:modelValue', html);
|
||||
};
|
||||
};
|
||||
// 页面加载时
|
||||
onMounted(() => {
|
||||
initWangeditor();
|
||||
});
|
||||
// 监听双向绑定值的改变
|
||||
// https://gitee.com/lyt-top/vue-next-admin/issues/I4LM7I
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(value) => {
|
||||
state.editor.txt.html(value);
|
||||
}
|
||||
);
|
||||
return {
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,242 @@
|
||||
<template>
|
||||
<div class="icon-selector">
|
||||
<el-popover placement="bottom" :width="fontIconWidth" v-model:visible="fontIconVisible" popper-class="icon-selector-popper">
|
||||
<template #reference>
|
||||
<el-input
|
||||
v-model="fontIconSearch"
|
||||
:placeholder="fontIconPlaceholder"
|
||||
:clearable="clearable"
|
||||
:disabled="disabled"
|
||||
:size="size"
|
||||
ref="inputWidthRef"
|
||||
@clear="onClearFontIcon"
|
||||
@focus="onIconFocus"
|
||||
@blur="onIconBlur"
|
||||
>
|
||||
<template #prepend>
|
||||
<SvgIcon
|
||||
:name="fontIconPrefix === '' ? prepend : fontIconPrefix"
|
||||
class="font14"
|
||||
v-if="fontIconPrefix === '' ? prepend?.indexOf('ele-') > -1 : fontIconPrefix?.indexOf('ele-') > -1"
|
||||
/>
|
||||
<i v-else :class="fontIconPrefix === '' ? prepend : fontIconPrefix" class="font14"></i>
|
||||
</template>
|
||||
</el-input>
|
||||
</template>
|
||||
<transition name="el-zoom-in-top">
|
||||
<div class="icon-selector-warp" v-show="fontIconVisible">
|
||||
<div class="icon-selector-warp-title flex">
|
||||
<div class="flex-auto">{{ title }}</div>
|
||||
<div class="icon-selector-warp-title-tab" v-if="type === 'all'">
|
||||
<span :class="{ 'span-active': fontIconType === 'ali' }" @click="onIconChange('ali')" class="ml10" title="iconfont 图标">ali</span>
|
||||
<span :class="{ 'span-active': fontIconType === 'ele' }" @click="onIconChange('ele')" class="ml10" title="elementPlus 图标">ele</span>
|
||||
<span :class="{ 'span-active': fontIconType === 'awe' }" @click="onIconChange('awe')" class="ml10" title="fontawesome 图标">awe</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="icon-selector-warp-row">
|
||||
<el-scrollbar ref="selectorScrollbarRef">
|
||||
<el-row :gutter="10" v-if="fontIconSheetsFilterList.length > 0">
|
||||
<el-col :xs="6" :sm="4" :md="4" :lg="4" :xl="4" @click="onColClick(v)" v-for="(v, k) in fontIconSheetsFilterList" :key="k">
|
||||
<div class="icon-selector-warp-item" :class="{ 'icon-selector-active': fontIconPrefix === v }">
|
||||
<div class="flex-margin">
|
||||
<div class="icon-selector-warp-item-value">
|
||||
<SvgIcon :name="v" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-empty :image-size="100" v-if="fontIconSheetsFilterList.length <= 0" :description="emptyDescription"></el-empty>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
</el-popover>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { ref, toRefs, reactive, onMounted, nextTick, computed, watch, defineComponent } from 'vue';
|
||||
import initIconfont from '/@/utils/getStyleSheets';
|
||||
export default defineComponent({
|
||||
name: 'iconSelector',
|
||||
emits: ['update:modelValue', 'get', 'clear'],
|
||||
props: {
|
||||
// 输入框前置内容
|
||||
prepend: {
|
||||
type: String,
|
||||
default: () => 'ele-Pointer',
|
||||
},
|
||||
// 输入框占位文本
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: () => '请输入内容搜索图标或者选择图标',
|
||||
},
|
||||
// 输入框占位文本
|
||||
size: {
|
||||
type: String,
|
||||
default: () => 'default',
|
||||
},
|
||||
// 弹窗标题
|
||||
title: {
|
||||
type: String,
|
||||
default: () => '请选择图标',
|
||||
},
|
||||
// icon 图标类型
|
||||
type: {
|
||||
type: String,
|
||||
default: () => 'ele',
|
||||
},
|
||||
// 禁用
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: () => false,
|
||||
},
|
||||
// 是否可清空
|
||||
clearable: {
|
||||
type: Boolean,
|
||||
default: () => true,
|
||||
},
|
||||
// 自定义空状态描述文字
|
||||
emptyDescription: {
|
||||
type: String,
|
||||
default: () => '无相关图标',
|
||||
},
|
||||
// 双向绑定值,字段名为固定,改了之后将不生效
|
||||
// 参考:https://v3.cn.vuejs.org/guide/migration/v-model.html#%E8%BF%81%E7%A7%BB%E7%AD%96%E7%95%A5
|
||||
modelValue: String,
|
||||
},
|
||||
setup(props, { emit }) {
|
||||
const inputWidthRef = ref();
|
||||
const selectorScrollbarRef = ref();
|
||||
const state = reactive({
|
||||
fontIconPrefix: '',
|
||||
fontIconVisible: false,
|
||||
fontIconWidth: 0,
|
||||
fontIconSearch: '',
|
||||
fontIconTabsIndex: 0,
|
||||
fontIconSheetsList: [],
|
||||
fontIconPlaceholder: '',
|
||||
fontIconType: 'ali',
|
||||
fontIconShow: true,
|
||||
});
|
||||
// 处理 input 获取焦点时,modelValue 有值时,改变 input 的 placeholder 值
|
||||
const onIconFocus = () => {
|
||||
state.fontIconVisible = true;
|
||||
if (!props.modelValue) return false;
|
||||
state.fontIconSearch = '';
|
||||
state.fontIconPlaceholder = props.modelValue;
|
||||
};
|
||||
// 处理 input 失去焦点时,为空将清空 input 值,为点击选中图标时,将取原先值
|
||||
const onIconBlur = () => {
|
||||
setTimeout(() => {
|
||||
const icon = state.fontIconSheetsList.filter((icon: string) => icon === state.fontIconSearch);
|
||||
if (icon.length <= 0) state.fontIconSearch = '';
|
||||
}, 300);
|
||||
};
|
||||
// 处理 icon 双向绑定数值回显
|
||||
const initModeValueEcho = () => {
|
||||
if (props.modelValue === '') return false;
|
||||
(<string | undefined>state.fontIconPlaceholder) = props.modelValue;
|
||||
(<string | undefined>state.fontIconPrefix) = props.modelValue;
|
||||
};
|
||||
// 图标搜索及图标数据显示
|
||||
const fontIconSheetsFilterList = computed(() => {
|
||||
if (!state.fontIconSearch) return state.fontIconSheetsList;
|
||||
let search = state.fontIconSearch.trim().toLowerCase();
|
||||
return state.fontIconSheetsList.filter((item: any) => {
|
||||
if (item.toLowerCase().indexOf(search) !== -1) return item;
|
||||
});
|
||||
});
|
||||
// 获取 input 的宽度
|
||||
const getInputWidth = () => {
|
||||
nextTick(() => {
|
||||
state.fontIconWidth = inputWidthRef.value.$el.offsetWidth;
|
||||
});
|
||||
};
|
||||
// 监听页面宽度改变
|
||||
const initResize = () => {
|
||||
window.addEventListener('resize', () => {
|
||||
getInputWidth();
|
||||
});
|
||||
};
|
||||
// 初始化数据
|
||||
const initFontIconData = async (type: string) => {
|
||||
state.fontIconSheetsList = [];
|
||||
if (type === 'ali') {
|
||||
await initIconfont.ali().then((res: any) => {
|
||||
// 阿里字体图标使用 `iconfont xxx`
|
||||
state.fontIconSheetsList = res.map((i: string) => `iconfont ${i}`);
|
||||
});
|
||||
} else if (type === 'ele') {
|
||||
await initIconfont.ele().then((res: any) => {
|
||||
state.fontIconSheetsList = res;
|
||||
});
|
||||
} else if (type === 'awe') {
|
||||
await initIconfont.awe().then((res: any) => {
|
||||
// fontawesome字体图标使用 `fa xxx`
|
||||
state.fontIconSheetsList = res.map((i: string) => `fa ${i}`);
|
||||
});
|
||||
}
|
||||
// 初始化 input 的 placeholder
|
||||
// 参考(单项数据流):https://cn.vuejs.org/v2/guide/components-props.html?#%E5%8D%95%E5%90%91%E6%95%B0%E6%8D%AE%E6%B5%81
|
||||
state.fontIconPlaceholder = props.placeholder;
|
||||
// 初始化双向绑定回显
|
||||
initModeValueEcho();
|
||||
// 切换时,滚动条置顶。感兴趣可以使用 keep-alive <component :is="xxx"/> 进行缓存
|
||||
selectorScrollbarRef.value.wrap$.scrollTop = 0;
|
||||
};
|
||||
// 图标点击切换
|
||||
const onIconChange = (type: string) => {
|
||||
state.fontIconType = type;
|
||||
initFontIconData(type);
|
||||
};
|
||||
// 获取当前点击的 icon 图标
|
||||
const onColClick = (v: any) => {
|
||||
state.fontIconPlaceholder = v;
|
||||
state.fontIconVisible = false;
|
||||
state.fontIconPrefix = v;
|
||||
emit('get', state.fontIconPrefix);
|
||||
emit('update:modelValue', state.fontIconPrefix);
|
||||
};
|
||||
// 清空当前点击的 icon 图标
|
||||
const onClearFontIcon = () => {
|
||||
state.fontIconPrefix = '';
|
||||
emit('clear', state.fontIconPrefix);
|
||||
emit('update:modelValue', state.fontIconPrefix);
|
||||
};
|
||||
// 页面加载时
|
||||
onMounted(() => {
|
||||
// 判断默认进来是什么类型图标,进行 tab 回显
|
||||
if (props.type === 'all') {
|
||||
if ((<any>props.modelValue)?.indexOf('iconfont') > -1) onIconChange('ali');
|
||||
else if ((<any>props.modelValue)?.indexOf('ele-') > -1) onIconChange('ele');
|
||||
else if ((<any>props.modelValue)?.indexOf('fa') > -1) onIconChange('awe');
|
||||
else onIconChange('ali');
|
||||
} else {
|
||||
onIconChange(props.type);
|
||||
}
|
||||
initResize();
|
||||
getInputWidth();
|
||||
});
|
||||
// 监听双向绑定 modelValue 的变化
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
() => {
|
||||
initModeValueEcho();
|
||||
}
|
||||
);
|
||||
return {
|
||||
inputWidthRef,
|
||||
selectorScrollbarRef,
|
||||
fontIconSheetsFilterList,
|
||||
onColClick,
|
||||
onIconChange,
|
||||
onClearFontIcon,
|
||||
onIconFocus,
|
||||
onIconBlur,
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,194 @@
|
||||
<template>
|
||||
<div class="notice-bar" :style="{ background, height: `${height}px` }" v-show="!isMode">
|
||||
<div class="notice-bar-warp" :style="{ color, fontSize: `${size}px` }">
|
||||
<i v-if="leftIcon" class="notice-bar-warp-left-icon" :class="leftIcon"></i>
|
||||
<div class="notice-bar-warp-text-box" ref="noticeBarWarpRef">
|
||||
<div class="notice-bar-warp-text" ref="noticeBarTextRef" v-if="!scrollable">{{ text }}</div>
|
||||
<div class="notice-bar-warp-slot" v-else><slot /></div>
|
||||
</div>
|
||||
<SvgIcon :name="rightIcon" v-if="rightIcon" class="notice-bar-warp-right-icon" @click="onRightIconClick" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { toRefs, reactive, defineComponent, ref, onMounted, nextTick } from 'vue';
|
||||
export default defineComponent({
|
||||
name: 'noticeBar',
|
||||
props: {
|
||||
// 通知栏模式,可选值为 closeable link
|
||||
mode: {
|
||||
type: String,
|
||||
default: () => '',
|
||||
},
|
||||
// 通知文本内容
|
||||
text: {
|
||||
type: String,
|
||||
default: () => '',
|
||||
},
|
||||
// 通知文本颜色
|
||||
color: {
|
||||
type: String,
|
||||
default: () => 'var(--color-warning)',
|
||||
},
|
||||
// 通知背景色
|
||||
background: {
|
||||
type: String,
|
||||
default: () => 'var(--color-warning-light-9)',
|
||||
},
|
||||
// 字体大小,单位px
|
||||
size: {
|
||||
type: [Number, String],
|
||||
default: () => 14,
|
||||
},
|
||||
// 通知栏高度,单位px
|
||||
height: {
|
||||
type: Number,
|
||||
default: () => 40,
|
||||
},
|
||||
// 动画延迟时间 (s)
|
||||
delay: {
|
||||
type: Number,
|
||||
default: () => 1,
|
||||
},
|
||||
// 滚动速率 (px/s)
|
||||
speed: {
|
||||
type: Number,
|
||||
default: () => 100,
|
||||
},
|
||||
// 是否开启垂直滚动
|
||||
scrollable: {
|
||||
type: Boolean,
|
||||
default: () => false,
|
||||
},
|
||||
// 自定义左侧图标
|
||||
leftIcon: {
|
||||
type: String,
|
||||
default: () => '',
|
||||
},
|
||||
// 自定义右侧图标
|
||||
rightIcon: {
|
||||
type: String,
|
||||
default: () => '',
|
||||
},
|
||||
},
|
||||
setup(props, { emit }) {
|
||||
const noticeBarWarpRef = ref();
|
||||
const noticeBarTextRef = ref();
|
||||
const state = reactive({
|
||||
order: 1,
|
||||
oneTime: 0,
|
||||
twoTime: 0,
|
||||
warpOWidth: 0,
|
||||
textOWidth: 0,
|
||||
isMode: false,
|
||||
});
|
||||
// 初始化 animation 各项参数
|
||||
const initAnimation = () => {
|
||||
nextTick(() => {
|
||||
state.warpOWidth = noticeBarWarpRef.value.offsetWidth;
|
||||
state.textOWidth = noticeBarTextRef.value.offsetWidth;
|
||||
document.styleSheets[0].insertRule(`@keyframes oneAnimation {0% {left: 0px;} 100% {left: -${state.textOWidth}px;}}`);
|
||||
document.styleSheets[0].insertRule(`@keyframes twoAnimation {0% {left: ${state.warpOWidth}px;} 100% {left: -${state.textOWidth}px;}}`);
|
||||
computeAnimationTime();
|
||||
setTimeout(() => {
|
||||
changeAnimation();
|
||||
}, props.delay * 1000);
|
||||
});
|
||||
};
|
||||
// 计算 animation 滚动时长
|
||||
const computeAnimationTime = () => {
|
||||
state.oneTime = state.textOWidth / props.speed;
|
||||
state.twoTime = (state.textOWidth + state.warpOWidth) / props.speed;
|
||||
};
|
||||
// 改变 animation 动画调用
|
||||
const changeAnimation = () => {
|
||||
if (state.order === 1) {
|
||||
noticeBarTextRef.value.style.cssText = `animation: oneAnimation ${state.oneTime}s linear; opactity: 1;}`;
|
||||
state.order = 2;
|
||||
} else {
|
||||
noticeBarTextRef.value.style.cssText = `animation: twoAnimation ${state.twoTime}s linear infinite; opacity: 1;`;
|
||||
}
|
||||
};
|
||||
// 监听 animation 动画的结束
|
||||
const listenerAnimationend = () => {
|
||||
noticeBarTextRef.value.addEventListener(
|
||||
'animationend',
|
||||
() => {
|
||||
changeAnimation();
|
||||
},
|
||||
false
|
||||
);
|
||||
};
|
||||
// 右侧 icon 图标点击
|
||||
const onRightIconClick = () => {
|
||||
if (!props.mode) return false;
|
||||
if (props.mode === 'closeable') {
|
||||
state.isMode = true;
|
||||
emit('close');
|
||||
} else if (props.mode === 'link') {
|
||||
emit('link');
|
||||
}
|
||||
};
|
||||
// 页面加载时
|
||||
onMounted(() => {
|
||||
if (props.scrollable) return false;
|
||||
initAnimation();
|
||||
listenerAnimationend();
|
||||
});
|
||||
return {
|
||||
noticeBarWarpRef,
|
||||
noticeBarTextRef,
|
||||
onRightIconClick,
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.notice-bar {
|
||||
padding: 0 15px;
|
||||
width: 100%;
|
||||
border-radius: 4px;
|
||||
.notice-bar-warp {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
height: inherit;
|
||||
.notice-bar-warp-text-box {
|
||||
flex: 1;
|
||||
height: inherit;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
.notice-bar-warp-text {
|
||||
white-space: nowrap;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
}
|
||||
.notice-bar-warp-slot {
|
||||
width: 100%;
|
||||
white-space: nowrap;
|
||||
::v-deep(.el-carousel__item) {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
.notice-bar-warp-left-icon {
|
||||
width: 24px;
|
||||
font-size: inherit !important;
|
||||
}
|
||||
.notice-bar-warp-right-icon {
|
||||
width: 24px;
|
||||
text-align: right;
|
||||
font-size: inherit !important;
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,42 @@
|
||||
<script lang="ts">
|
||||
// 渲染函数:https://v3.cn.vuejs.org/guide/render-function.html
|
||||
import { h, resolveComponent } from 'vue';
|
||||
|
||||
// 定义接口来定义对象的类型
|
||||
interface SvgIconProps {
|
||||
name: string;
|
||||
size: number;
|
||||
color: string;
|
||||
}
|
||||
|
||||
export default {
|
||||
name: 'svgIcon',
|
||||
props: {
|
||||
// svg 图标组件名字
|
||||
name: {
|
||||
type: String,
|
||||
},
|
||||
// svg 大小
|
||||
size: {
|
||||
type: Number,
|
||||
default: () => 14,
|
||||
},
|
||||
// svg 颜色
|
||||
color: {
|
||||
type: String,
|
||||
},
|
||||
},
|
||||
setup(props: SvgIconProps) {
|
||||
// 定义变量
|
||||
const linesString: any[] = ['https', 'http', '/src', '/assets', import.meta.env.VITE_PUBLIC_PATH];
|
||||
const onLineStyle: string = `font-size: ${props.size}px;color: ${props.color}`;
|
||||
const localsStyle: string = `width: ${props.size}px;height: ${props.size}px`;
|
||||
const eleSetStyle = { class: 'el-icon', style: onLineStyle };
|
||||
|
||||
// 逻辑判断
|
||||
if (props.name?.startsWith('ele-')) return () => h('i', eleSetStyle, [props.name === 'ele-' ? '' : h(resolveComponent(props.name))]);
|
||||
else if (linesString.find((str) => props.name?.startsWith(str))) return () => h('img', { src: props.name, style: localsStyle });
|
||||
else return () => h('i', { class: props.name, style: onLineStyle });
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,200 @@
|
||||
import { defineComponent, ref, nextTick } from 'vue';
|
||||
import ButtonRender, { ButtonRenderProps } from '/@/comps/Activiti/button-render';
|
||||
import { BpmnStore } from '/@/bpmn/store';
|
||||
import CodeMirror from 'codemirror';
|
||||
import 'codemirror/mode/xml/xml.js';
|
||||
import 'codemirror/addon/hint/xml-hint.js';
|
||||
import 'codemirror/lib/codemirror.css';
|
||||
import 'codemirror/theme/material.css';
|
||||
|
||||
import './bpmn-actions.css';
|
||||
import { ModdleElement } from '/@/bpmn/type';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'BpmnActions',
|
||||
setup() {
|
||||
//放大缩小
|
||||
const zoom = ref(1);
|
||||
//预览xml的抽屉控制器
|
||||
const previewActive = ref(false);
|
||||
//取到的xml
|
||||
const xml = ref('');
|
||||
|
||||
return {
|
||||
zoom,
|
||||
previewActive,
|
||||
xml,
|
||||
};
|
||||
},
|
||||
render() {
|
||||
const bpmnContext = BpmnStore;
|
||||
//codemirror编辑器
|
||||
let coder: CodeMirror.EditorFromTextArea;
|
||||
|
||||
const importFile = function (event: Event) {
|
||||
const eventTarget = event.target as HTMLInputElement;
|
||||
if (eventTarget.files) {
|
||||
const file = eventTarget.files[0];
|
||||
const reader = new FileReader();
|
||||
reader.readAsText(file);
|
||||
reader.onload = function () {
|
||||
if (this.result) {
|
||||
bpmnContext.importXML(this.result as string);
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
const buttonRenderProps: ButtonRenderProps = {
|
||||
buttons: [
|
||||
{
|
||||
label: '导入',
|
||||
icon: 'icon-shangchuan',
|
||||
action: () => {
|
||||
document.getElementById('bpmn-upload-element')?.click();
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '导出SVG',
|
||||
icon: 'icon-zu920',
|
||||
action: () => {
|
||||
const rootElement: ModdleElement = bpmnContext
|
||||
.getModeler()
|
||||
.get('canvas')
|
||||
.getRootElement();
|
||||
bpmnContext
|
||||
.getSVG()
|
||||
.then((response) => {
|
||||
download(response.svg, rootElement.id || 'process', 'svg');
|
||||
})
|
||||
.catch((err: unknown) => {
|
||||
console.warn(err);
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '导出XML',
|
||||
icon: 'icon-zu1359',
|
||||
action: () => {
|
||||
const rootElement: ModdleElement = bpmnContext
|
||||
.getModeler()
|
||||
.get('canvas')
|
||||
.getRootElement();
|
||||
bpmnContext
|
||||
.getXML()
|
||||
.then((response: { xml: string }) => {
|
||||
download(response.xml, rootElement.id || 'process', 'bpmn');
|
||||
})
|
||||
.catch((err: unknown) => {
|
||||
console.warn(err);
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '放大',
|
||||
icon: 'icon-fangda',
|
||||
action: () => {
|
||||
this.zoom = Math.floor(this.zoom * 100 + 0.1 * 100) / 100;
|
||||
bpmnContext.getModeler().get('canvas').zoom(this.zoom);
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '缩小',
|
||||
icon: 'icon-suoxiao',
|
||||
action: () => {
|
||||
this.zoom = Math.floor(this.zoom * 100 - 0.1 * 100) / 100;
|
||||
bpmnContext.getModeler().get('canvas').zoom(this.zoom);
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '还原并居中',
|
||||
icon: 'icon-quxiaoquanping',
|
||||
action: () => {
|
||||
this.zoom = 1;
|
||||
bpmnContext.getModeler().get('canvas').zoom('fit-viewport', 'auto');
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '预览',
|
||||
icon: 'icon-xianshi',
|
||||
action: () => {
|
||||
console.warn();
|
||||
bpmnContext
|
||||
.getXML()
|
||||
.then((response) => {
|
||||
this.xml = response.xml;
|
||||
this.previewActive = true;
|
||||
|
||||
nextTick(() => {
|
||||
if (!coder) {
|
||||
coder = CodeMirror.fromTextArea(
|
||||
document.getElementById('xml-highlight-container') as HTMLTextAreaElement,
|
||||
{
|
||||
lineWrapping: true,
|
||||
mode: 'application/xml', // HMTL混合模式
|
||||
theme: 'material',
|
||||
lineNumbers: true,
|
||||
lint: true,
|
||||
// theme: 'monokai', // 使用monokai模版
|
||||
},
|
||||
);
|
||||
coder.setSize('100%', '100%');
|
||||
} else {
|
||||
coder.setValue(this.xml);
|
||||
}
|
||||
});
|
||||
})
|
||||
.catch((err: unknown) => {
|
||||
console.warn(err);
|
||||
});
|
||||
},
|
||||
},
|
||||
// {
|
||||
// label: '撤销',
|
||||
// icon: 'icon-weibiaoti545',
|
||||
// action: () => {
|
||||
// bpmnContext.getModeler().get('commandStack').undo();
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// label: '恢复',
|
||||
// icon: 'icon-weibiaoti546',
|
||||
// action: () => {
|
||||
// bpmnContext.getModeler().get('commandStack').redo();
|
||||
// },
|
||||
// },
|
||||
],
|
||||
};
|
||||
return (
|
||||
<div class="bpmn-actions">
|
||||
<ButtonRender {...buttonRenderProps} />
|
||||
<el-drawer size="35%" direction="ltr" withHeader={false} v-model={this.previewActive}>
|
||||
<textarea id="xml-highlight-container" v-model={this.xml} />
|
||||
</el-drawer>
|
||||
<input
|
||||
type="file"
|
||||
id="bpmn-upload-element"
|
||||
ref="refFile"
|
||||
style="display: none"
|
||||
accept=".xml, .bpmn"
|
||||
onChange={importFile}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
//文本下载
|
||||
const download = (data: string, filename: string, type: string): void => {
|
||||
const blob = new Blob([data]);
|
||||
const tempLink = document.createElement('a'); // 创建a标签
|
||||
const href = window.URL.createObjectURL(blob); // 创建下载的链接
|
||||
//filename
|
||||
const fileName = `${filename}.${type}`;
|
||||
tempLink.href = href;
|
||||
tempLink.target = '_blank';
|
||||
tempLink.download = fileName;
|
||||
document.body.appendChild(tempLink);
|
||||
tempLink.click(); // 点击下载
|
||||
document.body.removeChild(tempLink); // 下载完成移除元素
|
||||
window.URL.revokeObjectURL(href); // 释放掉blob对象
|
||||
};
|
||||
@@ -0,0 +1,56 @@
|
||||
.bpmn-actions {
|
||||
position: absolute;
|
||||
bottom: 5px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.bpmn-actions button {
|
||||
margin: 0 20px;
|
||||
}
|
||||
|
||||
.render-icon {
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.render-icon .icon {
|
||||
margin: 0 10px;
|
||||
font-size: 30px;
|
||||
padding: 3px;
|
||||
}
|
||||
|
||||
.render-icon .icon:hover {
|
||||
position: relative;
|
||||
bottom: 6px;
|
||||
}
|
||||
|
||||
.render-icon-tooltip {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.el-drawer__body {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.CodeMirror {
|
||||
height: 100%;
|
||||
font-family: Monaco, Menlo, Consolas, 'COURIER NEW', monospace;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.CodeMirror-scroll {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.icon {
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
vertical-align: -0.15em;
|
||||
fill: currentColor;
|
||||
overflow: hidden;
|
||||
}
|
||||
/* stylelint-disable-next-line rule-empty-line-before */
|
||||
.ant-drawer-body {
|
||||
height: 100%;
|
||||
padding: 0;
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
import BpmnActions from './BpmnActions';
|
||||
export default BpmnActions;
|
||||
@@ -0,0 +1,70 @@
|
||||
import { defineComponent, PropType } from 'vue';
|
||||
import { Button, ButtonRenderProps } from './index';
|
||||
import { ElButton, ElTooltip } from 'element-plus';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'ButtonRender',
|
||||
|
||||
props: {
|
||||
buttons: {
|
||||
type: Array as PropType<Array<Button>>,
|
||||
require: true,
|
||||
default: () => [],
|
||||
},
|
||||
buttonClick: {
|
||||
type: Function as PropType<(btn: Button) => void>,
|
||||
default: () => (item: any) => {
|
||||
console.log(item);
|
||||
},
|
||||
},
|
||||
},
|
||||
setup(props: ButtonRenderProps) {
|
||||
const globalClick = (item: any) => {
|
||||
if (props.buttonClick) {
|
||||
props.buttonClick(item);
|
||||
}
|
||||
};
|
||||
return () => (
|
||||
<div class="button-render">
|
||||
{props.buttons.map((item) => {
|
||||
if (!item.icon) {
|
||||
return (
|
||||
<ElButton
|
||||
round
|
||||
type="info"
|
||||
{...{
|
||||
onClick: (): void => (item.action ? item.action() : globalClick(item)),
|
||||
}}
|
||||
>
|
||||
{item.label}
|
||||
</ElButton>
|
||||
);
|
||||
} else {
|
||||
const tooltipProps = {
|
||||
effect: 'light',
|
||||
content: item.label,
|
||||
placement: 'top',
|
||||
offset: 10,
|
||||
'show-after': 300,
|
||||
};
|
||||
|
||||
return (
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
<ElTooltip class="render-icon-tooltip" {...tooltipProps}>
|
||||
<div
|
||||
class="render-icon"
|
||||
onClick={() => (item.action ? item.action() : globalClick(item))}
|
||||
>
|
||||
<svg class="icon" aria-hidden="true">
|
||||
<use xlinkHref={`#${item.icon}`} />
|
||||
</svg>
|
||||
</div>
|
||||
</ElTooltip>
|
||||
);
|
||||
}
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,13 @@
|
||||
import ButtonRender from './ButtonRender';
|
||||
|
||||
export interface Button {
|
||||
label?: string;
|
||||
icon?: string;
|
||||
action?: () => void;
|
||||
}
|
||||
|
||||
export interface ButtonRenderProps {
|
||||
buttons: Array<Button>;
|
||||
buttonClick?: (btn: Button) => void;
|
||||
}
|
||||
export default ButtonRender;
|
||||
@@ -0,0 +1,172 @@
|
||||
import { defineComponent, PropType, reactive, toRaw, watchEffect, watch } from 'vue';
|
||||
import ScriptHelper, { resolve } from '/@/utils/script-helper';
|
||||
import { FieldDefine } from './index';
|
||||
|
||||
/**
|
||||
* 动态绑定组件
|
||||
* 传入源对象及数据绑定的定义,根据组件名动态渲染出组件进行数据绑定
|
||||
*/
|
||||
export default defineComponent({
|
||||
name: 'DynamicBinder',
|
||||
props: {
|
||||
//传进来的源对象,这里需要通过动态组件修改源对象的值进行数据动态绑定
|
||||
modelValue: {
|
||||
type: Object as PropType<unknown>,
|
||||
default: () => Object.assign({}),
|
||||
required: true,
|
||||
},
|
||||
/**
|
||||
* 组件的需要绑定数据的定义
|
||||
*/
|
||||
fieldDefine: {
|
||||
type: Object as PropType<FieldDefine>,
|
||||
default: () => ({}),
|
||||
required: true,
|
||||
},
|
||||
//绑定数据的转换器
|
||||
bindTransformer: {
|
||||
type: Function,
|
||||
default: undefined,
|
||||
},
|
||||
},
|
||||
emits: ['update:modelValue', 'fieldChange'],
|
||||
setup(props, context) {
|
||||
const state = reactive({
|
||||
flatFieldDefine: flatObject(props.fieldDefine || {}, {}),
|
||||
handingModel: Object.assign({}),
|
||||
});
|
||||
watchEffect(() => {
|
||||
state.handingModel = props.modelValue;
|
||||
state.flatFieldDefine = flatObject(props.fieldDefine, {});
|
||||
});
|
||||
|
||||
//绑定转换函数赋值,然props有则用props的否则用默认的
|
||||
const bindTransformer = props.bindTransformer || defaultTransformer;
|
||||
const dataBindTransformer = function (key: string, value: unknown) {
|
||||
return bindTransformer(state.handingModel, key, value);
|
||||
};
|
||||
|
||||
return () => (
|
||||
<div class="dynamic-binder">
|
||||
{Object.keys(state.flatFieldDefine).map((key) => {
|
||||
const define = state.flatFieldDefine[key];
|
||||
|
||||
if (define && predicate(define, toRaw(props.modelValue))) {
|
||||
const bindData = dataBindTransformer(key, define);
|
||||
//组件不能是代理对象,这里直接用目标对象
|
||||
const Component = toRaw(define.component);
|
||||
|
||||
watch(
|
||||
() => bindData.value,
|
||||
() => {
|
||||
// state.handingModel[bindData.bindKey] = bindData.value;
|
||||
context.emit('update:modelValue', state.handingModel);
|
||||
|
||||
//如果有setValue还是则直接使用独立的setValue
|
||||
if (bindData.setValue) {
|
||||
//setValue有返回值,值进行赋值后执行
|
||||
const setValueCallBack = bindData.setValue(
|
||||
toRaw(props.modelValue),
|
||||
bindData.bindKey,
|
||||
bindData.value,
|
||||
);
|
||||
if (setValueCallBack) {
|
||||
setValueCallBack();
|
||||
}
|
||||
} else {
|
||||
context.emit('fieldChange', bindData.bindKey, bindData.value);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
return (
|
||||
<Component
|
||||
{...bindData}
|
||||
v-model={bindData.value}
|
||||
v-slots={bindData.vSlots}
|
||||
class={`${Component.name}-${key} dynamic-binder-item`}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* 将对象结构扁平化
|
||||
* 例如:
|
||||
*
|
||||
const obj = {
|
||||
a: {
|
||||
b: {
|
||||
c: 'xxx',
|
||||
},
|
||||
},
|
||||
};
|
||||
出来的对象会变成,{a.b.c:'xxx'}
|
||||
*
|
||||
* @param source 源对象
|
||||
* @param target 目标对象
|
||||
* @param prefix 前缀
|
||||
*/
|
||||
function flatObject(source: FieldDefine, target: FieldDefine, prefix = ''): FieldDefine {
|
||||
Object.keys(source).forEach((key) => {
|
||||
const currentKeyObj = source[key];
|
||||
if (!currentKeyObj || !(typeof currentKeyObj === 'object')) {
|
||||
return;
|
||||
}
|
||||
const component = currentKeyObj.component;
|
||||
if (component) {
|
||||
target[prefix + key] = currentKeyObj;
|
||||
} else {
|
||||
flatObject(currentKeyObj, target, `${key}.`);
|
||||
}
|
||||
});
|
||||
return target;
|
||||
}
|
||||
|
||||
/**
|
||||
* 断言
|
||||
* 若对象中有predicate断言属性,则执行断言 返回true/false
|
||||
* 1.若属性为string,则使用脚本处理器执行,如 predicate='obj.a ===1' 则会传入当前对象进行判断;
|
||||
* 2.若属性为function,则直接传入目标对象直接执行
|
||||
* @param fieldDefine 断言的对象
|
||||
* @param modelValue 模式值
|
||||
*/
|
||||
function predicate(fieldDefine: FieldDefine, modelValue: unknown): boolean {
|
||||
const bindDefinePredicate = fieldDefine.predicate;
|
||||
if (bindDefinePredicate) {
|
||||
if (typeof bindDefinePredicate === 'string') {
|
||||
return ScriptHelper.executeEl(modelValue, bindDefinePredicate) as boolean;
|
||||
}
|
||||
|
||||
if (typeof bindDefinePredicate === 'function') {
|
||||
return bindDefinePredicate(modelValue);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 默认的转换函数
|
||||
* @param sourceModel 源模型对象
|
||||
* @param bindKey 绑定数据的Key
|
||||
* @param bindDefine 绑定定义
|
||||
*/
|
||||
function defaultTransformer(
|
||||
sourceModel: unknown,
|
||||
bindKey: string,
|
||||
bindDefine: FieldDefine,
|
||||
): unknown {
|
||||
return reactive({
|
||||
bindKey,
|
||||
...bindDefine,
|
||||
sourceModel,
|
||||
value: bindDefine.getValue
|
||||
? bindDefine.getValue(toRaw(sourceModel))
|
||||
: resolve(bindKey, sourceModel) || '',
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import { Component } from 'vue';
|
||||
import DynamicBinder from './DynamicBinder';
|
||||
|
||||
/**
|
||||
* 绑定的定义
|
||||
*/
|
||||
export interface FieldDefine extends Map {
|
||||
//用于断言此绑定的组件是否是否显示
|
||||
predicate?: string | ((obj: any) => boolean);
|
||||
component?: string | Component | JSX.Element;
|
||||
//获取value的值,根据绑定的对象获取对象的值
|
||||
getValue?: (sourceObject: any) => any;
|
||||
setValue?: (sourceObject: any, key: string, value: any) => void | (() => void);
|
||||
}
|
||||
|
||||
export interface Map {
|
||||
[key: string]: any;
|
||||
|
||||
[index: number]: any;
|
||||
}
|
||||
|
||||
export default DynamicBinder;
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,45 @@
|
||||
import './modeler.css';
|
||||
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 { defineComponent, onMounted } from 'vue';
|
||||
import createDefaultBpmnXml from '/@/bpmn/defaultBpmnXml';
|
||||
import activitiModdel from '/@/bpmn/resources/activiti-moddel.json';
|
||||
import translate from '/@/bpmn/i18n';
|
||||
import { BpmnStore } from '/@/bpmn/store';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Modeler',
|
||||
emits: ['bpmnMounted'],
|
||||
setup( props, { emit }) {
|
||||
const bpmnContext = BpmnStore;
|
||||
onMounted(() => {
|
||||
bpmnContext.initModeler({
|
||||
container: '#modeler-container',
|
||||
additionalModules: [
|
||||
//添加翻译
|
||||
{ translate: ['value', translate('zh')] },
|
||||
],
|
||||
moddleExtensions: {
|
||||
activiti: activitiModdel,
|
||||
},
|
||||
});
|
||||
const defaultProcessIdAndName = '1';
|
||||
bpmnContext
|
||||
.importXML(createDefaultBpmnXml(defaultProcessIdAndName, defaultProcessIdAndName))
|
||||
.then((result: Array<string>) => {
|
||||
if (result.length) {
|
||||
console.warn('importSuccess warnings', result);
|
||||
}
|
||||
})
|
||||
.catch((err: any) => {
|
||||
console.warn('importFail errors ', err);
|
||||
});
|
||||
emit("bpmnMounted");
|
||||
});
|
||||
|
||||
return () => <div id="modeler-container" />;
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,2 @@
|
||||
import Modeler from './Modeler';
|
||||
export default Modeler;
|
||||
@@ -0,0 +1,23 @@
|
||||
.bjs-powered-by {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.bpmn-container {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#modeler-container {
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
background: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAiIGhlaWdodD0iNDAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PHBhdHRlcm4gaWQ9ImEiIHdpZHRoPSI0MCIgaGVpZ2h0PSI0MCIgcGF0dGVyblVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHBhdGggZD0iTTAgMTBoNDBNMTAgMHY0ME0wIDIwaDQwTTIwIDB2NDBNMCAzMGg0ME0zMCAwdjQwIiBmaWxsPSJub25lIiBzdHJva2U9IiNlMGUwZTAiIG9wYWNpdHk9Ii4yIi8+PHBhdGggZD0iTTQwIDBIMHY0MCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjZTBlMGUwIi8+PC9wYXR0ZXJuPjwvZGVmcz48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSJ1cmwoI2EpIi8+PC9zdmc+') repeat !important;
|
||||
}
|
||||
|
||||
.djs-palette {
|
||||
border: none !important;
|
||||
background-color: #fff !important;
|
||||
box-shadow: 0 3px 3px rgba(0, 0, 0, 0.2);
|
||||
top: 65px !important;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
import './modeler.css';
|
||||
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 { defineComponent, onMounted } from 'vue';
|
||||
import createDefaultBpmnXml from '/@/bpmn/defaultBpmnXml';
|
||||
import activitiModdel from '/@/bpmn/resources/activiti-moddel.json';
|
||||
import translate from '/@/bpmn/i18n';
|
||||
import { BpmnStore } from '/@/bpmn/store';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Modeler2',
|
||||
emits: ['bpmnMounted'],
|
||||
setup( props, { emit }) {
|
||||
const bpmnContext = BpmnStore;
|
||||
onMounted(() => {
|
||||
bpmnContext.initModeler({
|
||||
container: '#modeler-container',
|
||||
additionalModules: [
|
||||
//添加翻译
|
||||
{
|
||||
translate: ['value', translate('zh')],
|
||||
paletteProvider:["value",''],//禁用/清空左侧工具栏
|
||||
labelEditingProvider:["value",''],//禁用节点编辑
|
||||
contextPadProvider:["value",''],//禁用图形菜单
|
||||
bendpoints:["value",{}],//禁用连线拖动
|
||||
zoomScroll:["value",''],//禁用滚动
|
||||
// moveCanvas:['value',''],//禁用拖动整个流程图
|
||||
move:['value','']//禁用单个图形拖动
|
||||
},
|
||||
],
|
||||
moddleExtensions: {
|
||||
activiti: activitiModdel,
|
||||
},
|
||||
});
|
||||
const defaultProcessIdAndName = '1';
|
||||
bpmnContext
|
||||
.importXML(createDefaultBpmnXml(defaultProcessIdAndName, defaultProcessIdAndName))
|
||||
.then((result: Array<string>) => {
|
||||
if (result.length) {
|
||||
console.warn('importSuccess warnings', result);
|
||||
}
|
||||
})
|
||||
.catch((err: any) => {
|
||||
console.warn('importFail errors ', err);
|
||||
});
|
||||
emit("bpmnMounted");
|
||||
});
|
||||
|
||||
return () => <div id="modeler-container" />;
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,54 @@
|
||||
import './modeler.css';
|
||||
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 { defineComponent, onMounted } from 'vue';
|
||||
import createDefaultBpmnXml from '/@/bpmn/defaultBpmnXml';
|
||||
import activitiModdel from '/@/bpmn/resources/activiti-moddel.json';
|
||||
import translate from '/@/bpmn/i18n';
|
||||
import { BpmnStore } from '/@/bpmn/store';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Modeler2',
|
||||
emits: ['bpmnMounted'],
|
||||
setup( props, { emit }) {
|
||||
const bpmnContext = BpmnStore;
|
||||
onMounted(() => {
|
||||
bpmnContext.initModeler({
|
||||
container: '#modeler-container',
|
||||
additionalModules: [
|
||||
//添加翻译
|
||||
{
|
||||
translate: ['value', translate('zh')],
|
||||
paletteProvider:["value",''],//禁用/清空左侧工具栏
|
||||
labelEditingProvider:["value",''],//禁用节点编辑
|
||||
contextPadProvider:["value",''],//禁用图形菜单
|
||||
bendpoints:["value",{}],//禁用连线拖动
|
||||
// zoomScroll:["value",''],//禁用滚动
|
||||
// moveCanvas:['value',''],//禁用拖动整个流程图
|
||||
move:['value','']//禁用单个图形拖动
|
||||
},
|
||||
],
|
||||
moddleExtensions: {
|
||||
activiti: activitiModdel,
|
||||
},
|
||||
});
|
||||
const defaultProcessIdAndName = '1';
|
||||
bpmnContext
|
||||
.importXML(createDefaultBpmnXml(defaultProcessIdAndName, defaultProcessIdAndName))
|
||||
.then((result: Array<string>) => {
|
||||
if (result.length) {
|
||||
console.warn('importSuccess warnings', result);
|
||||
}
|
||||
})
|
||||
.catch((err: any) => {
|
||||
console.warn('importFail errors ', err);
|
||||
});
|
||||
emit("bpmnMounted");
|
||||
});
|
||||
|
||||
return () => <div id="modeler-container" />;
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,2 @@
|
||||
import Modeler2 from './Modeler2';
|
||||
export default Modeler2;
|
||||
@@ -0,0 +1,23 @@
|
||||
.bjs-powered-by {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.bpmn-container {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#modeler-container {
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
background: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAiIGhlaWdodD0iNDAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PHBhdHRlcm4gaWQ9ImEiIHdpZHRoPSI0MCIgaGVpZ2h0PSI0MCIgcGF0dGVyblVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHBhdGggZD0iTTAgMTBoNDBNMTAgMHY0ME0wIDIwaDQwTTIwIDB2NDBNMCAzMGg0ME0zMCAwdjQwIiBmaWxsPSJub25lIiBzdHJva2U9IiNlMGUwZTAiIG9wYWNpdHk9Ii4yIi8+PHBhdGggZD0iTTQwIDBIMHY0MCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjZTBlMGUwIi8+PC9wYXR0ZXJuPjwvZGVmcz48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSJ1cmwoI2EpIi8+PC9zdmc+') repeat !important;
|
||||
}
|
||||
|
||||
.djs-palette {
|
||||
border: none !important;
|
||||
background-color: #fff !important;
|
||||
box-shadow: 0 3px 3px rgba(0, 0, 0, 0.2);
|
||||
top: 65px !important;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
import { defineComponent, reactive, watch } from 'vue';
|
||||
import { BpmnStore } from '/@/bpmn/store';
|
||||
import DynamicBinder from '/@/comps/Activiti/dynamic-binder';
|
||||
import { ElCollapse, ElCollapseItem } from 'element-plus';
|
||||
import { GroupProperties } from '/@/bpmn/config';
|
||||
import { Fold,Expand} from '@element-plus/icons-vue';
|
||||
|
||||
import './panel.css';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Panel',
|
||||
setup() {
|
||||
const bpmnContext = BpmnStore;
|
||||
const contextState = bpmnContext.getState();
|
||||
|
||||
//动态数据绑定器的字段变化后更新到xml,视图刷新
|
||||
//需要注意,如果字段定义里边属性定义了`setValue`方法,则不会进这里了
|
||||
function onFieldChange(key: string, value: unknown): void {
|
||||
const shape = bpmnContext.getShape();
|
||||
bpmnContext.getModeling().updateProperties(shape, { [key]: value });
|
||||
}
|
||||
|
||||
const panelState = reactive({
|
||||
//活动的数据配置组
|
||||
elCollapses: Object.assign([]),
|
||||
//panel面板的开关
|
||||
shrinkageOff: false,
|
||||
});
|
||||
|
||||
//打开所有抽屉
|
||||
watch(
|
||||
() => contextState.activeBindDefine,
|
||||
() => {
|
||||
if (contextState.activeBindDefine) {
|
||||
panelState.elCollapses = contextState.activeBindDefine.map((groupItem) => groupItem.name);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* 获取字段配置组的插槽
|
||||
* @param groupItem 组对象项
|
||||
*/
|
||||
function getSlotObject(groupItem: GroupProperties) {
|
||||
return {
|
||||
title: () => (
|
||||
<div class="group-title-block">
|
||||
{groupItem.icon && <i class={groupItem.icon} />}
|
||||
{groupItem.name}
|
||||
</div>
|
||||
),
|
||||
default: () => (
|
||||
<DynamicBinder
|
||||
{...{ onFieldChange: onFieldChange }}
|
||||
fieldDefine={groupItem.properties}
|
||||
v-model={contextState.businessObject}
|
||||
/>
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
return () => (
|
||||
<>
|
||||
{contextState.isActive && contextState.businessObject && contextState.activeBindDefine && (
|
||||
<>
|
||||
<div
|
||||
class="bpmn-panel-shrinkage"
|
||||
onClick={() => (panelState.shrinkageOff = !panelState.shrinkageOff)}
|
||||
>
|
||||
{panelState.shrinkageOff ? (
|
||||
<el-icon>
|
||||
<Fold/>
|
||||
</el-icon>
|
||||
) : (
|
||||
<el-icon>
|
||||
<Expand/>
|
||||
</el-icon>
|
||||
)}
|
||||
</div>
|
||||
<div class="bpmn-panel" v-show={!panelState.shrinkageOff}>
|
||||
<div class="title">{bpmnContext.getActiveElementName()}</div>
|
||||
<ElCollapse class="bpmn-panel-collapse" v-model={panelState.elCollapses}>
|
||||
{contextState.activeBindDefine.map((groupItem) => {
|
||||
return (
|
||||
<ElCollapseItem name={groupItem.name} v-slots={getSlotObject(groupItem)} />
|
||||
);
|
||||
})}
|
||||
</ElCollapse>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,2 @@
|
||||
import Pannel from './Panel';
|
||||
export default Pannel;
|
||||
@@ -0,0 +1,54 @@
|
||||
.bpmn-panel {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
background-color: #fff !important;
|
||||
box-shadow: 0 3px 3px rgba(0, 0, 0, 0.2);
|
||||
width: 45%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.bpmn-panel .dynamic-binder {
|
||||
padding: 0 15px;
|
||||
}
|
||||
|
||||
.bpmn-panel .title {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
padding: 10px;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
.group-title-block {
|
||||
font-size: 16px;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.group-title-block i {
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.dynamic-binder > * {
|
||||
padding-top: 15px;
|
||||
}
|
||||
|
||||
.bpmn-panel-collapse {
|
||||
max-height: calc(100% - 60px);
|
||||
overflow-y: auto;
|
||||
padding: 5px;
|
||||
/* padding: 10px; */
|
||||
}
|
||||
|
||||
.bpmn-panel-shrinkage {
|
||||
z-index: 99;
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
font-size: 20px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.el-collapse{
|
||||
border-bottom: none !important;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
import { ElAutocomplete } from 'element-plus';
|
||||
import { defineComponent, PropType, computed, toRaw } from 'vue';
|
||||
import '../prefix-label-select/prefix-label-select.css';
|
||||
|
||||
/**
|
||||
* 带前缀Label的AutoComplete
|
||||
*/
|
||||
const PrefixAutoComplete = defineComponent({
|
||||
props: {
|
||||
...ElAutocomplete.props,
|
||||
prefixTitle: {
|
||||
type: String as PropType<string>,
|
||||
default: () => '',
|
||||
},
|
||||
},
|
||||
emits: ['update:modelValue'],
|
||||
setup(props, ctx) {
|
||||
const computedModelValue = computed({
|
||||
get: () => props.value,
|
||||
set: (val) => ctx.emit('update:modelValue', val),
|
||||
});
|
||||
return () => (
|
||||
<div class="prefix-label-select-container">
|
||||
{props.prefixTitle && <div class="prefix-title ">{props.prefixTitle}</div>}
|
||||
<ElAutocomplete
|
||||
class="prefix-label-select"
|
||||
v-model={computedModelValue.value}
|
||||
{...props}
|
||||
v-slots={toRaw(ctx.slots)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
export default PrefixAutoComplete;
|
||||
@@ -0,0 +1,3 @@
|
||||
import PrefixAutoComplete from './PrefixAutoComplete';
|
||||
|
||||
export default PrefixAutoComplete;
|
||||
@@ -0,0 +1,34 @@
|
||||
import { ElSelect } from 'element-plus';
|
||||
import { defineComponent, PropType, computed } from 'vue';
|
||||
import './prefix-label-select.css';
|
||||
|
||||
const PrefixLabelSelect = defineComponent({
|
||||
props: {
|
||||
...ElSelect.props,
|
||||
prefixTitle: {
|
||||
type: String as PropType<string>,
|
||||
default: () => '',
|
||||
},
|
||||
},
|
||||
emits: ['update:modelValue'],
|
||||
setup(props, { emit, slots }) {
|
||||
const computedModelValue = computed({
|
||||
get: () => props.value,
|
||||
set: (val) => emit('update:modelValue', val),
|
||||
});
|
||||
|
||||
return () => (
|
||||
<div class="prefix-label-select-container">
|
||||
{props.prefixTitle && <div class="prefix-title ">{props.prefixTitle}</div>}
|
||||
<ElSelect
|
||||
class="prefix-label-select"
|
||||
v-model={computedModelValue.value}
|
||||
{...props}
|
||||
v-slots={slots}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
export default PrefixLabelSelect;
|
||||
@@ -0,0 +1,3 @@
|
||||
import PrefixLabelSelect from './PrefixLabelSelect';
|
||||
|
||||
export default PrefixLabelSelect;
|
||||
@@ -0,0 +1,29 @@
|
||||
.prefix-label-select-container {
|
||||
display: inline-table;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.prefix-title {
|
||||
display: table-cell;
|
||||
padding: 0 20px;
|
||||
border: 1px solid #DCDFE6;
|
||||
border-right: 0;
|
||||
border-radius: 4px 0 0 4px;
|
||||
color: #909399;
|
||||
background-color: #F5F7FA;
|
||||
width: 1px;
|
||||
white-space: nowrap
|
||||
}
|
||||
|
||||
.prefix-label-select {
|
||||
display: table-cell;
|
||||
}
|
||||
|
||||
.prefix-label-select .el-input, .el-select {
|
||||
line-height: normal;
|
||||
}
|
||||
|
||||
.prefix-label-select .el-input__inner {
|
||||
border-radius: 0 4px 4px 0 !important;
|
||||
line-height: normal;
|
||||
}
|
||||
@@ -0,0 +1,290 @@
|
||||
import { defineComponent, PropType, reactive, watch, ref, onMounted, toRaw } from 'vue';
|
||||
import { ElInput, ElTable, ElTableColumn, ElForm, ElFormItem } from 'element-plus';
|
||||
import { TableProps } from 'element-plus/lib/el-table/src/table/defaults';
|
||||
import { TableColumn, SubListState } from './type';
|
||||
import './sublist.css';
|
||||
import { SetupContext } from '@vue/runtime-core';
|
||||
|
||||
/**
|
||||
* 深拷贝
|
||||
* @param prototype 原型
|
||||
*/
|
||||
const deepCopy = (prototype: any): typeof prototype => {
|
||||
return JSON.parse(JSON.stringify(prototype));
|
||||
};
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
/**
|
||||
* v-model
|
||||
*/
|
||||
modelValue: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
required: true,
|
||||
},
|
||||
/**
|
||||
* 列的描述信息
|
||||
* @see TableColumn
|
||||
*/
|
||||
columns: {
|
||||
type: Array as PropType<Array<TableColumn>>,
|
||||
required: true,
|
||||
},
|
||||
/**
|
||||
* 原型对象(用于new)
|
||||
*/
|
||||
model: {
|
||||
type: Object as PropType<{ [key: string]: string }>,
|
||||
default: () => Object.assign({}),
|
||||
},
|
||||
rules: {
|
||||
type: Object as PropType<{ [key: string]: Array<ObjectConstructor> }>,
|
||||
default: () => null,
|
||||
},
|
||||
/**
|
||||
* 表格的参数
|
||||
* @see TableProps
|
||||
*/
|
||||
tableProps: {
|
||||
type: Object as PropType<TableProps<any>>,
|
||||
default: () => ({
|
||||
stripe: true,
|
||||
border: true,
|
||||
size: 'small',
|
||||
'empty-text': '没有数据',
|
||||
}),
|
||||
},
|
||||
/**
|
||||
* 添加按钮的标题
|
||||
*/
|
||||
addTitle: {
|
||||
type: String as PropType<string>,
|
||||
default: () => '+ 添加',
|
||||
},
|
||||
},
|
||||
emits: ['update:modelValue'],
|
||||
setup(props, ctx) {
|
||||
const sublistState: SubListState<any> = reactive({
|
||||
data: props.modelValue ? JSON.parse(JSON.stringify(props.modelValue)) : [],
|
||||
editing: false,
|
||||
editItem: {},
|
||||
editIndex: undefined,
|
||||
isNew: false,
|
||||
sublistForm: null,
|
||||
});
|
||||
|
||||
// SubList表单
|
||||
const form = ref();
|
||||
onMounted(() => {
|
||||
sublistState.sublistForm = form.value;
|
||||
});
|
||||
|
||||
//重置状态
|
||||
const restoreState = () => {
|
||||
sublistState.data = props.modelValue ? JSON.parse(JSON.stringify(props.modelValue)) : [];
|
||||
sublistState.editing = false;
|
||||
sublistState.editItem = undefined;
|
||||
sublistState.editIndex = undefined;
|
||||
sublistState.isNew = false;
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
() => {
|
||||
restoreState();
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* 添加数据项并进行编辑
|
||||
*/
|
||||
const addData = (): void => {
|
||||
sublistState.data.push(deepCopy(props.model));
|
||||
sublistState.editIndex = sublistState.data.length - 1;
|
||||
sublistState.editing = true;
|
||||
sublistState.editItem = deepCopy(deepCopy(props.model));
|
||||
sublistState.isNew = true;
|
||||
};
|
||||
|
||||
//创建操作列
|
||||
const actionColumnProps = buildActionColumnProps(sublistState, ctx);
|
||||
return {
|
||||
sublistState,
|
||||
addData,
|
||||
form,
|
||||
actionColumnProps,
|
||||
};
|
||||
},
|
||||
render() {
|
||||
const props = this.$props;
|
||||
const sublistState = this.sublistState;
|
||||
const tableProps = deepCopy(props.tableProps);
|
||||
|
||||
const formProps = {
|
||||
size: 'default',
|
||||
inline: true,
|
||||
'inline-message': true,
|
||||
'show-message': true,
|
||||
rules: props.rules,
|
||||
model: sublistState.editItem,
|
||||
};
|
||||
return (
|
||||
<div class="sublist-div">
|
||||
{sublistState.data && (
|
||||
<ElForm ref="form" {...formProps}>
|
||||
<ElTable {...tableProps} data={sublistState.data}>
|
||||
{props.columns.map((column) => {
|
||||
const rawColum = toRaw(column);
|
||||
if (sublistState.editing && rawColum.type !== 'index') {
|
||||
const editComponentBuilder = rawColum.editComponent || getDefaultEditComponent();
|
||||
const slots = {
|
||||
default: (scope: any) => {
|
||||
//获取列的像是方式,如果是正在编辑的行,则使用编辑组件,
|
||||
const cellValue = scope.row[scope.column.property];
|
||||
const getRowColumnValue = () => {
|
||||
return scope.column.formatter
|
||||
? scope.column.formatter(scope.row, scope.column, cellValue, scope.$index)
|
||||
: cellValue;
|
||||
};
|
||||
|
||||
return sublistState.editIndex === scope.$index
|
||||
? editComponentBuilder(scope, sublistState)
|
||||
: getRowColumnValue();
|
||||
},
|
||||
};
|
||||
return <ElTableColumn v-slots={slots} {...rawColum} />;
|
||||
} else {
|
||||
return <ElTableColumn {...rawColum} />;
|
||||
}
|
||||
})}
|
||||
<ElTableColumn {...this.actionColumnProps} v-slots={this.actionColumnProps.vSlots} />
|
||||
</ElTable>
|
||||
</ElForm>
|
||||
)}
|
||||
|
||||
{/*新增按钮*/}
|
||||
{!sublistState.editing && (
|
||||
<div class="sublist-add" onClick={() => this.addData()}>
|
||||
{props.addTitle}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* 创建执行操作的列(编辑、删除)
|
||||
*/
|
||||
const buildActionColumnProps = (state: SubListState<any>, ctx: SetupContext<any>): any => {
|
||||
//编辑
|
||||
function actionEdit(scope: any): void {
|
||||
state.editIndex = scope.$index;
|
||||
state.editing = true;
|
||||
state.editItem = deepCopy(scope.row);
|
||||
}
|
||||
|
||||
//移除
|
||||
function actionRemove(index: number): void {
|
||||
state?.data?.splice(index, 1);
|
||||
ctx.emit('update:modelValue', state.data);
|
||||
}
|
||||
|
||||
//确认
|
||||
function actionConfirm() {
|
||||
state.sublistForm.validate((valid: boolean): void | boolean => {
|
||||
if (valid) {
|
||||
if (typeof state?.editIndex === 'number') {
|
||||
state.data.splice(state?.editIndex, 1, deepCopy(toRaw(state.editItem)));
|
||||
}
|
||||
state.editIndex = undefined;
|
||||
state.editItem = undefined;
|
||||
state.editing = false;
|
||||
ctx.emit('update:modelValue', state.data);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
//取消
|
||||
function actionCancel() {
|
||||
if (state.isNew) {
|
||||
state.data.splice(state.data.length - 1, 1);
|
||||
}
|
||||
state.editItem = undefined;
|
||||
state.editing = false;
|
||||
state.editIndex = undefined;
|
||||
}
|
||||
|
||||
function isReadonly(): string {
|
||||
return state.editing ? 'readonly' : '';
|
||||
}
|
||||
|
||||
return {
|
||||
align: 'center',
|
||||
label: '操作',
|
||||
vSlots: {
|
||||
default: (scope: any) => (
|
||||
<div class="sublist-actions">
|
||||
{state.editing && scope.$index === state.editIndex ? (
|
||||
<div>
|
||||
<span class="sublist-confirm sublist-btn" onClick={() => actionConfirm()}>
|
||||
确认
|
||||
</span>
|
||||
<span class="sublist-split">|</span>
|
||||
<span class="sublist-cancel sublist-btn" onClick={() => actionCancel()}>
|
||||
取消
|
||||
</span>
|
||||
</div>
|
||||
) : (
|
||||
<div>
|
||||
<span
|
||||
class={`${isReadonly()} sublist-edit sublist-btn`}
|
||||
onClick={() => {
|
||||
if (!state.editing) {
|
||||
actionEdit(scope);
|
||||
}
|
||||
}}
|
||||
>
|
||||
编辑
|
||||
</span>
|
||||
<span class={`${isReadonly()} `}>|</span>
|
||||
<span
|
||||
class={`${isReadonly()} sublist-delete sublist-btn`}
|
||||
onClick={() => {
|
||||
if (!state.editing) {
|
||||
actionRemove(scope.$index);
|
||||
}
|
||||
}}
|
||||
>
|
||||
删除
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
),
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* 默认的编辑组件,ElInput 用于编辑或新增
|
||||
*/
|
||||
function getDefaultEditComponent(): (scope: any, state: SubListState<any>) => JSX.Element {
|
||||
return function (scope, state) {
|
||||
return (
|
||||
<ElFormItem
|
||||
class="sublist-form-item"
|
||||
label={scope.column.name}
|
||||
prop={scope.column.property}
|
||||
>
|
||||
<ElInput
|
||||
label={scope.column.label}
|
||||
v-model={state.editItem[scope.column.property]}
|
||||
/>
|
||||
</ElFormItem>
|
||||
);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
import SubList from './SubList';
|
||||
|
||||
export default SubList;
|
||||
@@ -0,0 +1,67 @@
|
||||
.sublist-add {
|
||||
margin-top: 8px;
|
||||
border: 1px solid #409eff;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
color: #fff;
|
||||
font-weight: bold;
|
||||
border-radius: 4px;
|
||||
background-color: #409eff;
|
||||
}
|
||||
|
||||
.sublist-add:hover {
|
||||
border-color: #66b1ff;
|
||||
background-color: #66b1ff;
|
||||
}
|
||||
|
||||
.sublist-actions .sublist-btn {
|
||||
margin: 0 5px;
|
||||
}
|
||||
|
||||
.sublist-edit, .sublist-confirm {
|
||||
color: #409eff;
|
||||
}
|
||||
|
||||
.sublist-split {
|
||||
color: #bbbec3;
|
||||
}
|
||||
|
||||
.sublist-btn {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.sublist-btn:hover {
|
||||
border-bottom: 2px solid;
|
||||
}
|
||||
|
||||
.sublist-edit :hover, .sublist-confirm:hover {
|
||||
color: #66b1ff;
|
||||
}
|
||||
|
||||
.sublist-delete {
|
||||
color: #fa3b3b;
|
||||
}
|
||||
|
||||
.sublist-delete:hover {
|
||||
color: #f78989;
|
||||
}
|
||||
|
||||
.sublist-actions .readonly {
|
||||
color: #bbbec3;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.sublist-actions .readonly:hover {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.sublist-form-item {
|
||||
width: 100%;
|
||||
margin-right: 0 !important;
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
|
||||
.sublist-form-item .el-form-item__content {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
Vendored
+20
@@ -0,0 +1,20 @@
|
||||
import { TableColumnCtx } from 'element-plus/lib/el-table/src/table-column/defaults';
|
||||
|
||||
/**
|
||||
* 状态
|
||||
*/
|
||||
export interface SubListState<T> {
|
||||
data: Array<T>;
|
||||
editing: boolean;
|
||||
editItem: T | undefined | null;
|
||||
editIndex: number | undefined;
|
||||
isNew: boolean;
|
||||
sublistForm: any | null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 扩展的的可编辑列
|
||||
*/
|
||||
export interface TableColumn extends TableColumnCtx<any> {
|
||||
editComponent?: (scope: any | undefined | null) => JSX.Element;
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
<template>
|
||||
|
||||
<el-dialog v-model='dialogVisible' title='弹框选择' draggable width='500px' :before-close='handleClose'>
|
||||
<el-card class='box-card' style='height: 500px;' body-style='height: 100%;overflow: auto'>
|
||||
<template #header>
|
||||
<div class='card-header'>
|
||||
<div class='tree-h-flex'>
|
||||
<div class='tree-h-left'>
|
||||
<el-input :prefix-icon='Search' v-model='filterText' placeholder='部门名称' />
|
||||
</div>
|
||||
<div class='tree-h-right'>
|
||||
<el-dropdown @command='handleCommand'>
|
||||
<el-button style='margin-left: 10px;width: 30px'>
|
||||
<el-icon class='el-icon--right'>
|
||||
<more-filled />
|
||||
</el-icon>
|
||||
</el-button>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item command='expandAll'>全部展开</el-dropdown-item>
|
||||
<el-dropdown-item command='collapseAll'>全部折叠</el-dropdown-item>
|
||||
<el-dropdown-item command='refresh'>刷新</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<div style='margin-bottom: 45px'>
|
||||
<el-tree @node-click='nodeClick' ref='treeRef' class='filter-tree' :data='state.data' :props='defaultProps' :filter-node-method='filterNode' />
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<template #footer>
|
||||
<span class='dialog-footer'>
|
||||
<el-button @click='dialogVisible = false'>取消</el-button>
|
||||
<el-button type='primary' @click='handleConfirm'>确认</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
</template>
|
||||
|
||||
<script lang='ts' setup>
|
||||
import { defineExpose, onMounted, reactive, ref, watch } from 'vue';
|
||||
import type { ElTree } from 'element-plus';
|
||||
import { Search, MoreFilled } from '@element-plus/icons-vue';
|
||||
import request from '/@/utils/request';
|
||||
import { ElMessage } from 'element-plus';
|
||||
|
||||
|
||||
interface Tree {
|
||||
id: number;
|
||||
name: string;
|
||||
children?: Tree[];
|
||||
}
|
||||
|
||||
const filterText = ref('');
|
||||
const treeRef = ref<InstanceType<typeof ElTree>>();
|
||||
|
||||
|
||||
const defaultProps = {
|
||||
children: 'children',
|
||||
label: 'name',
|
||||
};
|
||||
|
||||
watch(filterText, (val) => {
|
||||
treeRef.value!.filter(val);
|
||||
});
|
||||
|
||||
const filterNode = (value: string, data: Tree) => {
|
||||
if (!value) return true;
|
||||
return data.name.includes(value);
|
||||
};
|
||||
|
||||
|
||||
const state = reactive({
|
||||
data: [] as any,
|
||||
});
|
||||
|
||||
|
||||
const handleCommand = (command: string | number | object) => {
|
||||
if ('expandAll' == command) {
|
||||
for (let i = 0; i < treeRef.value!.store._getAllNodes().length; i++) {
|
||||
treeRef.value!.store._getAllNodes()[i].expanded = true;
|
||||
}
|
||||
} else if ('collapseAll' == command) {
|
||||
for (let i = 0; i < treeRef.value!.store._getAllNodes().length; i++) {
|
||||
treeRef.value!.store._getAllNodes()[i].expanded = false;
|
||||
}
|
||||
}
|
||||
ElMessage(`click on item ${command}`);
|
||||
};
|
||||
|
||||
|
||||
// // 初始化表格数据
|
||||
const initTreeData = async () => {
|
||||
state.data = await request({
|
||||
url: '/te/feat/screen/tree',
|
||||
method: 'get',
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
const dialogVisible = ref(false);
|
||||
const openModal = async (id: string) => {
|
||||
dialogVisible.value = true;
|
||||
};
|
||||
|
||||
defineExpose({ openModal });
|
||||
|
||||
onMounted(() => {
|
||||
initTreeData();
|
||||
});
|
||||
|
||||
const emits = defineEmits(['close']);
|
||||
const handleConfirm=()=>{
|
||||
emits('close',{id:state.id,name:state.name,pid:state.pid});
|
||||
dialogVisible.value = false;
|
||||
}
|
||||
|
||||
const nodeClick=(node:any)=>{
|
||||
state.id=node.id;
|
||||
state.name=node.name;
|
||||
state.pid=node.pid;
|
||||
}
|
||||
|
||||
|
||||
</script>
|
||||
<style scoped>
|
||||
.tree-h-flex {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.tree-h-left {
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.tree-h-right {
|
||||
width: 40px;
|
||||
min-width: 40px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,118 @@
|
||||
<template>
|
||||
<el-form class='zform' :model='form' label-width='180px'>
|
||||
<el-row style='border-top: 1px solid #d2d2d2;'>
|
||||
<el-col :span='24'>
|
||||
<el-form-item label='流程说明:'>
|
||||
<div class='zinput'>
|
||||
xxxxxx
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span='24'>
|
||||
<el-form-item label='紧急程度:'>
|
||||
<div class='zinput'>
|
||||
<el-radio-group v-model='form.opurg'>
|
||||
<el-radio :label='1'><span style='color:red'>紧急</span></el-radio>
|
||||
<el-radio :label='2'><span style='color:blue'>急</span></el-radio>
|
||||
<el-radio :label='3'><span>一般</span></el-radio>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span='24'>
|
||||
<el-form-item label='即将流向:'>
|
||||
<div class='zinput'>
|
||||
N5.部门负责人(张三)
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span='24'>
|
||||
<el-form-item label='处理意见:'>
|
||||
<div class='zinput' style='height: auto'>
|
||||
<el-input v-model='form.opnot' type='textarea' :rows='5' placeholder=' ' />
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span='24'>
|
||||
<el-form-item label='附件:'>
|
||||
<div class='zinput' style='height: auto'>
|
||||
<el-upload class='upload-demo' action='https://jsonplaceholder.typicode.com/posts/' :file-list='fileList'>
|
||||
<el-button type='primary'>上 传</el-button>
|
||||
</el-upload>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span='24'>
|
||||
<div style='padding-left: 105px;border-right: 1px solid #ccc'>
|
||||
<el-checkbox v-model="checked2">流程图</el-checkbox>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span='24'>
|
||||
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
<script lang='ts' setup>
|
||||
import { onMounted, reactive, toRaw, toRefs } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
const state = reactive({
|
||||
form: {
|
||||
|
||||
},
|
||||
params: {
|
||||
path: '',
|
||||
query: '',
|
||||
},
|
||||
});
|
||||
|
||||
const {
|
||||
form, params,
|
||||
} = toRefs(state);
|
||||
|
||||
|
||||
onMounted(() => {
|
||||
state.params = <any>route;
|
||||
if (params.value.query?.id) {
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
const getOperateInfo = () => {
|
||||
return toRaw(form.value);
|
||||
};
|
||||
defineExpose({ getOperateInfo });
|
||||
|
||||
|
||||
// const fileList = ref<UploadUserFile[]>([
|
||||
// {
|
||||
// name: 'food.jpeg',
|
||||
// url: 'https://fuss10.elemecdn.com/3/63/4e7f3a15429bfda99bce42a18cdd1jpeg.jpeg?imageMogr2/thumbnail/360x360/format/webp/quality/100',
|
||||
// },
|
||||
// {
|
||||
// name: 'food2.jpeg',
|
||||
// url: 'https://fuss10.elemecdn.com/3/63/4e7f3a15429bfda99bce42a18cdd1jpeg.jpeg?imageMogr2/thumbnail/360x360/format/webp/quality/100',
|
||||
// },
|
||||
// ])
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,357 @@
|
||||
<template>
|
||||
<el-form class='zform' :model='form' label-width='160px' label-position='left'>
|
||||
<el-row style='border-top: 1px solid #d2d2d2;'>
|
||||
<el-col :span='24'>
|
||||
<el-form-item label='流程说明' style='margin-left: 10px;'>
|
||||
<div class='zinput'>
|
||||
xxxxxx
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span='24'>
|
||||
<div style='padding-left: 10px;border-right: 1px solid #ccc'>
|
||||
<el-checkbox v-model='state.autag'>显示审批记录</el-checkbox>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row v-show='state.autag'>
|
||||
<el-col :span='24' style='background-color: white;'>
|
||||
<div style='border-right: 1px solid #ccc'>
|
||||
<el-table :data='audits' style='width: 100%'>
|
||||
<el-table-column prop='crtim' label='时间' width='160' />
|
||||
<el-table-column prop='facno' label='节点编号' width='80' />
|
||||
<el-table-column prop='facna' label='节点名称' width='200' />
|
||||
<el-table-column prop='haman' label='操作者' width='90' />
|
||||
<el-table-column prop='opinf' label='操作' width='300' />
|
||||
<el-table-column prop='opnot' label='处理意见' />
|
||||
</el-table>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<div v-if='state.cutag'>
|
||||
<el-row>
|
||||
<el-col :span='24'>
|
||||
<el-form-item label='通知紧急程度' style='margin-left: 10px'>
|
||||
<div class='zinput'>
|
||||
<el-radio-group v-model='form.opurg'>
|
||||
<el-radio label='a'><span>一般</span></el-radio>
|
||||
<el-radio label='b'><span style='color:blue'>急</span></el-radio>
|
||||
<el-radio label='c'><span style='color:red'>紧急</span></el-radio>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span='24'>
|
||||
<el-form-item label='操作' style='margin-left: 10px'>
|
||||
<div class='zinput'>
|
||||
<el-radio-group v-model='form.opkey'>
|
||||
<el-radio label='pass'>通过</el-radio>
|
||||
<el-radio label='refuse'>驳回</el-radio>
|
||||
<el-radio label='commission'>转办</el-radio>
|
||||
<el-radio label='communicate'>沟通</el-radio>
|
||||
<el-radio label='abandon'>废弃</el-radio>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row v-if="form.opkey=='refuse'">
|
||||
<el-col :span='24'>
|
||||
<el-form-item label='驳回到' style='margin-left: 10px'>
|
||||
<div class='zinput'>
|
||||
<el-select v-model='form.refno' placeholder='请选择' style='width:300px;margin-right: 8px' @change='refChange'>
|
||||
<el-option v-for='item in state.refNodes' :key='item.id' :value='item.id' :label="item.id+'.'+item.name"></el-option>
|
||||
</el-select>
|
||||
<el-checkbox v-model='form.retag'>驳回的节点通过后直接返回本节点</el-checkbox>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row v-if="form.opkey=='pass'">
|
||||
<el-col :span='24'>
|
||||
<el-form-item label='即将流向' style='margin-left: 10px'>
|
||||
<div class='zinput'>
|
||||
{{ state.toExmen }}
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span='24'>
|
||||
<el-form-item label='处理意见' style='margin-left: 10px'>
|
||||
<div class='zinput' style='height: auto'>
|
||||
<div class='au-flex'>
|
||||
<div class='au-msg' style='padding:3px'>
|
||||
<el-input v-model='form.opnot' type='textarea' :rows='4' placeholder=' ' />
|
||||
</div>
|
||||
|
||||
<div class='au-button'>
|
||||
<el-button type='primary' @click='bpmSubmit()' style='width: 94px;height: 94px;margin-top: 3px;margin-left: 6px'>提 交</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span='24'>
|
||||
<el-form-item label='附件' style='margin-left: 10px'>
|
||||
<div class='zinput' style='height: auto'>
|
||||
<el-upload class='upload-demo' action='https://jsonplaceholder.typicode.com/posts/' :file-list='fileList'>
|
||||
<el-button type='primary'>上 传</el-button>
|
||||
</el-upload>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
<el-row>
|
||||
<el-col :span='24'>
|
||||
<el-form-item label='当前处理人' style='margin-left: 10px'>
|
||||
<div class='zinput'>
|
||||
{{ form.facno + '.' + form.facna + '(' + state.cuExmen + ')' }}
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span='24'>
|
||||
<el-form-item label='已经处理人' style='margin-left: 10px'>
|
||||
<div class='zinput'>
|
||||
{{ state.hiHamen }}
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span='24'>
|
||||
<div style='padding-left: 10px;border-right: 1px solid #ccc'>
|
||||
<el-checkbox v-model='state.chtag' @change='toggleFlowChart'>流程图</el-checkbox>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row v-show='state.chtag'>
|
||||
<el-col :span='24' style='background-color: white;'>
|
||||
<div style='padding: 10px;border-right: 1px solid #ccc;height:1000px'>
|
||||
<Modeler2 />
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
<script lang='ts' setup>
|
||||
import { onMounted, reactive, toRaw, toRefs } from 'vue';
|
||||
import request from '/@/utils/request';
|
||||
// import { useRoute } from 'vue-router';
|
||||
// import { Cookie, Session } from '/@/utils/storage';
|
||||
// import { store } from '/@/store';
|
||||
// const route = useRoute();
|
||||
import Modeler2 from '/@/comps/Activiti/modeler2/index';
|
||||
import { BpmnStore } from '/@/bpmn/store';
|
||||
// import Viewer from 'bpmn-js/lib/NavigatedViewer';
|
||||
// import Viewer from 'bpmn-js/lib/Modeler';
|
||||
// import Viewer from 'bpmn-js/lib/Viewer';
|
||||
|
||||
|
||||
const state = reactive({
|
||||
form: {
|
||||
items: [],
|
||||
} as any,
|
||||
params: {
|
||||
path: '',
|
||||
query: '',
|
||||
},
|
||||
audits: [] as any,
|
||||
chtag: false,
|
||||
autag: true,
|
||||
xml: null as any,
|
||||
refNodes: [] as any,
|
||||
cutag: false,
|
||||
hiHamen: '',
|
||||
cuExmen: '',
|
||||
toExmen: '',
|
||||
});
|
||||
|
||||
const {
|
||||
form, params, audits,
|
||||
} = toRefs(state);
|
||||
|
||||
state.refNodes = [
|
||||
{ id: 'N2', name: '起草节点', exman: 'sa' },
|
||||
{ id: 'N4', name: '张三', exman: 'z3' },
|
||||
{ id: 'N5', name: '王五', exman: 'w5' },
|
||||
];
|
||||
|
||||
|
||||
const props = defineProps({
|
||||
proid: String,
|
||||
});
|
||||
|
||||
|
||||
onMounted(async () => {
|
||||
await bpmInit();
|
||||
// state.params = <any>route;
|
||||
// if (params.value.query?.id) {
|
||||
//也可以拿到alert(params.value.query?.id);
|
||||
// }
|
||||
});
|
||||
|
||||
|
||||
const bpmInit = async () => {
|
||||
// form.value.proid = props.proid;
|
||||
const result: any = await request({
|
||||
url: '/bpm/proc/main/zbpm/' + props.proid,
|
||||
method: 'get',
|
||||
});
|
||||
audits.value = result.audits;
|
||||
state.hiHamen = result.hiHamen;
|
||||
state.cuExmen = result.cuExmen;
|
||||
|
||||
state.cutag = result.cutag;
|
||||
form.value = result.zbpm;
|
||||
if (state.cutag) {
|
||||
const target: any = await request({
|
||||
url: '/bpm/proc/main/target',
|
||||
method: 'get',
|
||||
params: { proid: form.value.proid, facno: form.value.facno },
|
||||
});
|
||||
form.value.tarno = target.tarno;
|
||||
form.value.tarna = target.tarna;
|
||||
form.value.tamen = target.tamen;
|
||||
form.value.bacid = target.bacid;
|
||||
if (target.tarno == 'N3') {
|
||||
state.toExmen = target.tarno + '.' + target.tarna;
|
||||
} else {
|
||||
state.toExmen = target.tarno + '.' + target.tarna + '(' + target.tamen + ')';
|
||||
}
|
||||
|
||||
form.value.opkey = 'pass';
|
||||
form.value.opurg = 'a';
|
||||
}
|
||||
|
||||
|
||||
// audits.value = await request({
|
||||
// url: '/bpm/audit/main/list/' + props.proid,
|
||||
// method: 'get',
|
||||
// });
|
||||
|
||||
// form.value = await request({
|
||||
// url: '/bpm/proc/main/zbpm/' + props.proid,
|
||||
// method: 'get',
|
||||
// });
|
||||
|
||||
};
|
||||
|
||||
const emit = defineEmits(['submit']);
|
||||
|
||||
const bpmSubmit = async () => {
|
||||
console.log('86811');
|
||||
emit('submit');
|
||||
console.log('86822');
|
||||
|
||||
if (form.value.opkey == 'pass') {
|
||||
await request({
|
||||
url: '/bpm/proc/main/hpass',
|
||||
method: 'post',
|
||||
data: form.value,
|
||||
});
|
||||
} else if (form.value.opkey == 'refuse') {
|
||||
const refInfo = toRaw(form.value);
|
||||
refInfo.tarno=refInfo.refno;
|
||||
refInfo.tarna=refInfo.refna;
|
||||
refInfo.exman=refInfo.reman;
|
||||
await request({
|
||||
url: '/bpm/proc/main/hrefuse',
|
||||
method: 'post',
|
||||
data: refInfo,
|
||||
});
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
const toggleFlowChart = async () => {
|
||||
if (!state.xml) {
|
||||
state.xml = await request({
|
||||
url: '/bpm/proc/main/xml/' + props.proid,
|
||||
method: 'get',
|
||||
});
|
||||
await BpmnStore.importXML(state.xml);
|
||||
|
||||
// const bpmnViewer = new Viewer({
|
||||
// container: '#modeler-container',
|
||||
// }) as any;
|
||||
// try {
|
||||
// const { warnings } = await bpmnViewer.importXML(state.xml);
|
||||
// // bpmnViewer.get("canvas").zoom("fit-viewport", "auto");
|
||||
// console.log('rendered');
|
||||
// } catch (err) {
|
||||
// console.log('error rendering', err);
|
||||
// }
|
||||
|
||||
const elementRegistry = BpmnStore.getModeler().get('elementRegistry');
|
||||
// const userTaskList = elementRegistry.filter(
|
||||
// (item) => item.type === 'bpmn:Task'
|
||||
// );
|
||||
console.log(elementRegistry);
|
||||
const list1 = [] as any;
|
||||
elementRegistry.forEach((item: any) => {
|
||||
if (item.id == 'L7' || item.id == 'N1' || item.id == 'N2') {
|
||||
list1.push(item);
|
||||
}
|
||||
});
|
||||
|
||||
const list2 = [] as any;
|
||||
elementRegistry.forEach((item: any) => {
|
||||
if (item.id == 'N4') {
|
||||
list2.push(item);
|
||||
}
|
||||
});
|
||||
|
||||
const list3 = [] as any;
|
||||
elementRegistry.forEach((item: any) => {
|
||||
if (item.id == 'L1' || item.id == 'L3') {
|
||||
list3.push(item);
|
||||
}
|
||||
});
|
||||
let modeling = BpmnStore.modeler.get('modeling');
|
||||
modeling.setColor(list1, { fill: '#e4feef' });
|
||||
modeling.setColor(list2, { fill: '#fed6d6' });
|
||||
modeling.setColor(list3, { stroke: '#009900' });
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
const refChange = (id: any) => {
|
||||
for (let i = 0; i < state.refNodes.length; i++) {
|
||||
if (state.refNodes[i].id == id) {
|
||||
form.value.refna = state.refNodes[i].name;
|
||||
form.value.reman = state.refNodes[i].exman;
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.au-flex {
|
||||
display: flex;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
.au-msg {
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.au-button {
|
||||
width: 105px;
|
||||
min-width: 105px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,147 @@
|
||||
<template>
|
||||
|
||||
<el-dialog v-model='dialogVisible' title='弹框选择' draggable width='500px' :before-close='handleClose'>
|
||||
<el-card class='box-card' style='height: 500px;' body-style='height: 100%;overflow: auto'>
|
||||
<template #header>
|
||||
<div class='card-header'>
|
||||
<div class='tree-h-flex'>
|
||||
<div class='tree-h-left'>
|
||||
<el-input :prefix-icon='Search' v-model='filterText' placeholder=' ' />
|
||||
</div>
|
||||
<div class='tree-h-right'>
|
||||
<el-dropdown @command='handleCommand'>
|
||||
<el-button style='margin-left: 10px;width: 30px'>
|
||||
<el-icon class='el-icon--right'>
|
||||
<more-filled />
|
||||
</el-icon>
|
||||
</el-button>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item command='expandAll'>全部展开</el-dropdown-item>
|
||||
<el-dropdown-item command='collapseAll'>全部折叠</el-dropdown-item>
|
||||
<el-dropdown-item command='refresh'>刷新</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<div style='margin-bottom: 45px'>
|
||||
<el-tree @node-click='nodeClick' ref='treeRef' class='filter-tree' :data='state.data' :props='defaultProps' :filter-node-method='filterNode' />
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<template #footer>
|
||||
<span class='dialog-footer'>
|
||||
<el-button @click='dialogVisible = false'>取消</el-button>
|
||||
<el-button type='primary' @click='handleConfirm'>确认</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
</template>
|
||||
|
||||
<script lang='ts' setup>
|
||||
import { defineExpose, onMounted, reactive, ref, watch } from 'vue';
|
||||
import type { ElTree } from 'element-plus';
|
||||
import { Search, MoreFilled } from '@element-plus/icons-vue';
|
||||
import request from '/@/utils/request';
|
||||
import { ElMessage } from 'element-plus';
|
||||
|
||||
const props = defineProps({
|
||||
url: String,
|
||||
});
|
||||
|
||||
interface Tree {
|
||||
id: number;
|
||||
name: string;
|
||||
children?: Tree[];
|
||||
}
|
||||
|
||||
const filterText = ref('');
|
||||
const treeRef = ref<InstanceType<typeof ElTree>>();
|
||||
|
||||
|
||||
const defaultProps = {
|
||||
children: 'children',
|
||||
label: 'name',
|
||||
};
|
||||
|
||||
watch(filterText, (val) => {
|
||||
treeRef.value!.filter(val);
|
||||
});
|
||||
|
||||
const filterNode = (value: string, data: Tree) => {
|
||||
if (!value) return true;
|
||||
return data.name.includes(value);
|
||||
};
|
||||
|
||||
|
||||
const state = reactive({
|
||||
data: [] as any,
|
||||
});
|
||||
|
||||
|
||||
const handleCommand = (command: string | number | object) => {
|
||||
if ('expandAll' == command) {
|
||||
for (let i = 0; i < treeRef.value!.store._getAllNodes().length; i++) {
|
||||
treeRef.value!.store._getAllNodes()[i].expanded = true;
|
||||
}
|
||||
} else if ('collapseAll' == command) {
|
||||
for (let i = 0; i < treeRef.value!.store._getAllNodes().length; i++) {
|
||||
treeRef.value!.store._getAllNodes()[i].expanded = false;
|
||||
}
|
||||
}
|
||||
ElMessage(`click on item ${command}`);
|
||||
};
|
||||
|
||||
|
||||
// // 初始化表格数据
|
||||
const initTreeData = async () => {
|
||||
state.data = await request({
|
||||
url: props.url,
|
||||
method: 'get',
|
||||
});
|
||||
};
|
||||
|
||||
const dialogVisible = ref(false);
|
||||
const openModal = async (id: string) => {
|
||||
dialogVisible.value = true;
|
||||
};
|
||||
|
||||
defineExpose({ openModal });
|
||||
|
||||
onMounted(() => {
|
||||
initTreeData();
|
||||
});
|
||||
|
||||
const emits = defineEmits(['close']);
|
||||
const handleConfirm = () => {
|
||||
emits('close', { id: state.id, name: state.name, pid: state.pid });
|
||||
dialogVisible.value = false;
|
||||
};
|
||||
|
||||
const nodeClick = (node: any) => {
|
||||
state.id = node.id;
|
||||
state.name = node.name;
|
||||
state.pid = node.pid;
|
||||
};
|
||||
|
||||
|
||||
</script>
|
||||
<style scoped>
|
||||
.tree-h-flex {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.tree-h-left {
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.tree-h-right {
|
||||
width: 40px;
|
||||
min-width: 40px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,120 @@
|
||||
<template>
|
||||
<el-card class='box-card' style='height: 100%;' body-style='height: 100%;overflow: auto'>
|
||||
<template #header>
|
||||
<div class='card-header'>
|
||||
<div class='tree-h-flex'>
|
||||
<div class='tree-h-left'>
|
||||
<el-input :prefix-icon='Search' v-model='filterText' placeholder=' ' />
|
||||
</div>
|
||||
<div class='tree-h-right'>
|
||||
<el-dropdown @command='handleCommand'>
|
||||
<el-button style='margin-left: 10px;width: 30px'>
|
||||
<el-icon class='el-icon--right'>
|
||||
<MoreFilled />
|
||||
</el-icon>
|
||||
</el-button>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item command='expandAll'>全部展开</el-dropdown-item>
|
||||
<el-dropdown-item command='collapseAll'>全部折叠</el-dropdown-item>
|
||||
<el-dropdown-item command='refresh'>刷新</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<div style='margin-bottom: 45px'>
|
||||
<el-tree ref='treeRef' class='filter-tree' :data='state.data' :props='defaultProps' :filter-node-method='filterNode' />
|
||||
</div>
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<script lang='ts' setup>
|
||||
import { onMounted, reactive, ref, watch } from 'vue';
|
||||
import type { ElTree } from 'element-plus';
|
||||
import { Search, MoreFilled } from '@element-plus/icons-vue';
|
||||
import request from '/@/utils/request';
|
||||
import { ElMessage } from 'element-plus';
|
||||
|
||||
const props = defineProps({
|
||||
url: String,
|
||||
});
|
||||
|
||||
|
||||
interface Tree {
|
||||
id: number;
|
||||
name: string;
|
||||
children?: Tree[];
|
||||
}
|
||||
|
||||
const filterText = ref('');
|
||||
const treeRef = ref<InstanceType<typeof ElTree>>();
|
||||
|
||||
|
||||
const defaultProps = {
|
||||
children: 'children',
|
||||
label: 'name',
|
||||
};
|
||||
|
||||
watch(filterText, (val) => {
|
||||
treeRef.value!.filter(val);
|
||||
});
|
||||
|
||||
const filterNode = (value: string, data: Tree) => {
|
||||
if (!value) return true;
|
||||
return data.name.includes(value);
|
||||
};
|
||||
|
||||
|
||||
const state = reactive({
|
||||
data: [] as any,
|
||||
});
|
||||
|
||||
|
||||
const handleCommand = (command: string | number | object) => {
|
||||
if ('expandAll' == command) {
|
||||
for (let i = 0; i < treeRef.value!.store._getAllNodes().length; i++) {
|
||||
treeRef.value!.store._getAllNodes()[i].expanded = true;
|
||||
}
|
||||
} else if ('collapseAll' == command) {
|
||||
for (let i = 0; i < treeRef.value!.store._getAllNodes().length; i++) {
|
||||
treeRef.value!.store._getAllNodes()[i].expanded = false;
|
||||
}
|
||||
}
|
||||
ElMessage(`click on item ${command}`);
|
||||
};
|
||||
|
||||
|
||||
// // 初始化表格数据
|
||||
const initTreeData = async () => {
|
||||
state.data = await request({
|
||||
url: props.url,
|
||||
method: 'get',
|
||||
});
|
||||
// for (let i = 0; i < treeRef.value!.store._getAllNodes().length; i++) {
|
||||
// treeRef.value!.store._getAllNodes()[i].expanded = true;
|
||||
// }
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
initTreeData();
|
||||
});
|
||||
|
||||
</script>
|
||||
<style scoped>
|
||||
.tree-h-flex {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.tree-h-left {
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.tree-h-right {
|
||||
width: 40px;
|
||||
min-width: 40px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,25 @@
|
||||
<template>
|
||||
<div class='p-flex' :style='{ height: `calc(100vh - ${initTagViewHeight}`}'>
|
||||
<slot></slot>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang='ts' setup>
|
||||
|
||||
//设置高度为100%
|
||||
import { useStore } from '/@/store';
|
||||
import { computed } from 'vue';
|
||||
|
||||
const store = useStore();
|
||||
const initTagViewHeight = computed(() => {
|
||||
let { isTagsview } = store.state.themeConfig.themeConfig;
|
||||
let { isTagsViewCurrenFull } = store.state.tagsViewRoutes;
|
||||
if (isTagsViewCurrenFull) {
|
||||
return `13px`;
|
||||
} else {
|
||||
if (isTagsview) return `97px`;
|
||||
else return `63px`;
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
@@ -0,0 +1,88 @@
|
||||
import request from '/@/utils/request';
|
||||
|
||||
export const editInit = async (state: any, route: any) => {
|
||||
state.params = <any>route;
|
||||
let id = state.params.query?.id;
|
||||
if (id) {
|
||||
state.form = await request({
|
||||
url: state.url + '/one/' + id,
|
||||
method: 'get',
|
||||
});
|
||||
} else {
|
||||
state.form.avtag = true;
|
||||
}
|
||||
};
|
||||
|
||||
export const tabSave = async (state: any, proxy: any, route: any) => {
|
||||
if (!state.form.crtim) {
|
||||
await request({
|
||||
url: state.url,
|
||||
method: 'post',
|
||||
data: state.form,
|
||||
});
|
||||
} else {
|
||||
await request({
|
||||
url: state.url,
|
||||
method: 'put',
|
||||
data: state.form,
|
||||
});
|
||||
}
|
||||
tabClose(proxy, route);
|
||||
};
|
||||
|
||||
export const tabClose = (proxy: any, route: any) => {
|
||||
proxy.mittBus.emit('onCurrentContextmenuClick', Object.assign({}, { contextMenuClickId: 1, ...route }));
|
||||
};
|
||||
|
||||
export const pageSave = async (state: any) => {
|
||||
if (!state.form.crtim) {
|
||||
await request({
|
||||
url: state.url,
|
||||
method: 'post',
|
||||
data: state.form,
|
||||
});
|
||||
} else {
|
||||
await request({
|
||||
url: state.url,
|
||||
method: 'put',
|
||||
data: state.form,
|
||||
});
|
||||
}
|
||||
window.close();
|
||||
};
|
||||
|
||||
export const pageClose = () => {
|
||||
window.close();
|
||||
};
|
||||
|
||||
export const drawerSave = async (state: any) => {
|
||||
if (!state.form.crtim) {
|
||||
await request({
|
||||
url: state.url,
|
||||
method: 'post',
|
||||
data: state.form,
|
||||
});
|
||||
} else {
|
||||
await request({
|
||||
url: state.url,
|
||||
method: 'put',
|
||||
data: state.form,
|
||||
});
|
||||
}
|
||||
state.show = false;
|
||||
};
|
||||
|
||||
export const drawerOpen = async (state: any, id: string) => {
|
||||
if (id) {
|
||||
state.form = await request({
|
||||
url: state.url + '/one/' + id,
|
||||
method: 'get',
|
||||
});
|
||||
} else {
|
||||
state.form={avtag:true};
|
||||
}
|
||||
state.show = true;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
import request from '/@/utils/request';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import router from '/@/router';
|
||||
import { uuid } from '/@/utils/xutil';
|
||||
|
||||
export async function listQuery(state: any) {
|
||||
const data: any = await request({
|
||||
url: state.url,
|
||||
method: 'get',
|
||||
params: state.form,
|
||||
});
|
||||
state.list = data.items;
|
||||
state.total = data.total;
|
||||
state.loading = false;
|
||||
}
|
||||
|
||||
export function listSelect(selection: any, state: any) {
|
||||
state.ids = selection.map((item: any) => item.id);
|
||||
state.single = selection.length !== 1;
|
||||
state.multiple = !selection.length;
|
||||
}
|
||||
|
||||
export async function listDelete(state: any) {
|
||||
const delIds = state.ids.join(',');
|
||||
if (delIds.length <= 0) {
|
||||
ElMessage.warning('请选择后再进行删除');
|
||||
return;
|
||||
}
|
||||
ElMessageBox.confirm('确认删除已选中的数据项?', '警告', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(async () => {
|
||||
await request({
|
||||
url: state.url + '/' + delIds,
|
||||
method: 'delete',
|
||||
});
|
||||
await listQuery(state);
|
||||
})
|
||||
.catch(() => ElMessage.info('已取消删除'));
|
||||
}
|
||||
|
||||
|
||||
export const tabAdd = (url: string) => {
|
||||
router.push({
|
||||
path: url + '/edit',
|
||||
query: { uuid: uuid() },
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
export const tabEdit = (url: string, id: string) => {
|
||||
router.push({
|
||||
path: url + '/edit',
|
||||
query: { id: id },
|
||||
});
|
||||
};
|
||||
|
||||
export const tabView = (url: string, id: string) => {
|
||||
router.push({
|
||||
path: url + '/view',
|
||||
query: { id: id },
|
||||
});
|
||||
};
|
||||
|
||||
export const pageAdd = (url: string) => {
|
||||
// if(url.indexOf("?")>0){
|
||||
// window.open('/#/page/'+url+'&type=add');
|
||||
// }else{
|
||||
// window.open('/#/page/'+url+'?type=add');
|
||||
// }
|
||||
window.open('/#/page/'+url);
|
||||
};
|
||||
|
||||
export const pageEdit = (url: string) => {
|
||||
window.open('/#/page/'+url);
|
||||
};
|
||||
|
||||
export const pageView = (url: string) => {
|
||||
window.open('/#/page/'+url);
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
import request from '/@/utils/request';
|
||||
import router from '/@/router';
|
||||
|
||||
export const viewClose = (proxy: any, route: any) => {
|
||||
proxy.mittBus.emit('onCurrentContextmenuClick', Object.assign({}, { contextMenuClickId: 1, ...route }));
|
||||
};
|
||||
|
||||
export const viewInit = async (state: any, route: any) => {
|
||||
state.params = <any>route;
|
||||
let id = state.params.query?.id;
|
||||
if (id) {
|
||||
state.form = await request({
|
||||
url: state.url + '/one/' + id,
|
||||
method: 'get',
|
||||
});
|
||||
} else {
|
||||
state.form = { avtag: true };
|
||||
}
|
||||
};
|
||||
|
||||
export const viewEdit = async (state: any, proxy: any, route: any) => {
|
||||
viewClose(proxy, route);
|
||||
router.push({
|
||||
path: state.url + '/edit',
|
||||
query: { id: state.form.id },
|
||||
});
|
||||
};
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
<template>
|
||||
<el-card class='box-card' style='height: 100%;' body-style='height: 100%;overflow: auto'>
|
||||
<template #header>
|
||||
<div class='card-header'>
|
||||
<div class='tree-h-flex'>
|
||||
<div class='tree-h-left'>
|
||||
<el-input :prefix-icon='Search' v-model='filterText' placeholder='部门名称' />
|
||||
</div>
|
||||
<div class='tree-h-right'>
|
||||
<el-dropdown @command='handleCommand'>
|
||||
<el-button style='margin-left: 10px;width: 30px'>
|
||||
<el-icon class='el-icon--right'>
|
||||
<more-filled />
|
||||
</el-icon>
|
||||
</el-button>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item command='expandAll'>全部展开</el-dropdown-item>
|
||||
<el-dropdown-item command='collapseAll'>全部折叠</el-dropdown-item>
|
||||
<el-dropdown-item command='refresh'>刷新</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<div style='margin-bottom: 45px'>
|
||||
<el-tree ref='treeRef' class='filter-tree' :data='state.data' :props='defaultProps' :filter-node-method='filterNode' @node-click="nodeClick"/>
|
||||
</div>
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<script lang='ts' setup>
|
||||
import { defineExpose, onMounted, reactive, ref, watch } from 'vue';
|
||||
import type { ElTree } from 'element-plus';
|
||||
import { Search, MoreFilled } from '@element-plus/icons-vue';
|
||||
import request from '/@/utils/request';
|
||||
import { ElMessage } from 'element-plus';
|
||||
|
||||
|
||||
interface Tree {
|
||||
id: number;
|
||||
name: string;
|
||||
children?: Tree[];
|
||||
}
|
||||
|
||||
const filterText = ref('');
|
||||
const treeRef = ref<InstanceType<typeof ElTree>>();
|
||||
|
||||
|
||||
const defaultProps = {
|
||||
children: 'children',
|
||||
label: 'name',
|
||||
};
|
||||
|
||||
watch(filterText, (val) => {
|
||||
treeRef.value!.filter(val);
|
||||
});
|
||||
|
||||
const filterNode = (value: string, data: Tree) => {
|
||||
if (!value) return true;
|
||||
return data.name.includes(value);
|
||||
};
|
||||
|
||||
|
||||
const state = reactive({
|
||||
data: [] as any,
|
||||
willInit:true
|
||||
});
|
||||
|
||||
|
||||
const handleCommand = (command: string | number | object) => {
|
||||
if ('expandAll' == command) {
|
||||
for (let i = 0; i < treeRef.value!.store._getAllNodes().length; i++) {
|
||||
treeRef.value!.store._getAllNodes()[i].expanded = true;
|
||||
}
|
||||
} else if ('collapseAll' == command) {
|
||||
for (let i = 0; i < treeRef.value!.store._getAllNodes().length; i++) {
|
||||
treeRef.value!.store._getAllNodes()[i].expanded = false;
|
||||
}
|
||||
}
|
||||
ElMessage(`click on item ${command}`);
|
||||
};
|
||||
|
||||
|
||||
// // 初始化表格数据
|
||||
const initTreeData = async () => {
|
||||
state.data = await request({
|
||||
url: '/gen/org/dept/tree',
|
||||
method: 'get',
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
const props = defineProps({
|
||||
maInit: Boolean,
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
if(props.maInit==false){
|
||||
initTreeData();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
//region 与父组件的交互逻辑
|
||||
const emits = defineEmits(['node-click']);
|
||||
const nodeClick = (node:any) => {
|
||||
emits('node-click', { id: node.id, name: node.name });
|
||||
};
|
||||
|
||||
const init = async () => {
|
||||
await initTreeData();
|
||||
};
|
||||
defineExpose({ init });
|
||||
|
||||
|
||||
//endregion
|
||||
</script>
|
||||
<style scoped>
|
||||
.tree-h-flex {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.tree-h-left {
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.tree-h-right {
|
||||
width: 40px;
|
||||
min-width: 40px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,511 @@
|
||||
<template>
|
||||
<el-dialog v-model='state.isShow' title='组织架构选择' width='1000px' draggable>
|
||||
<div style='height: 440px'>
|
||||
<el-row>
|
||||
<el-col :span='16'>
|
||||
<el-tabs v-model='activeName' type='card' @tab-click='tabChange'>
|
||||
<el-tab-pane label='快捷选择' name='k1'>
|
||||
<div>
|
||||
<el-input v-model='state.receSearch' placeholder='请输入关键字查询' style='width: 200px;' @keyup.enter='onSearch' />
|
||||
<el-button type='primary' style='margin-left: 10px' @click='onSearch'>查询</el-button>
|
||||
</div>
|
||||
<div style='margin-top: 5px;overflow: auto;height:353px;border: 1px solid #ccc'>
|
||||
<ul class='z-org-search' id='zOrgSearch' style='padding: 2px'>
|
||||
<li v-for='item in receItems' :key='item.id' class='f-user' @click='receItemClick(item)'>
|
||||
<el-checkbox v-model='item.checked' />
|
||||
<img src='https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=1813762643,1914315241&fm=26&gp=0.jpg'>
|
||||
<span class='layui-elip f-user-name'>{{ item.name }}</span>
|
||||
<span class='layui-elip f-user-dept'>{{ item.dept }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label='层级架构' name='k2'>
|
||||
<el-row>
|
||||
<el-col :span='12'>
|
||||
<div style='height: 390px;'>
|
||||
<DeptTree @node-click='nodeClick' :maInit='true' ref='deptTreeRef' />
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span='12'>
|
||||
<div style='border: 1px solid #ccc;height: 390px;margin-left: 5px;overflow: auto'>
|
||||
<ul class='z-org-tree'>
|
||||
<li v-for='item in tierItems' :key='item.id' class='f-user' title='444444444' @click='tierItemClick(item)'>
|
||||
<el-checkbox v-model='item.checked' />
|
||||
<img src='https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=1813762643,1914315241&fm=26&gp=0.jpg'>
|
||||
<span class='layui-elip f-user-name'>{{ item.name }}</span>
|
||||
<span class='layui-elip f-user-dept'>{{ item.dept }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label='常用群组' name='k3'>暂不支持,待开发</el-tab-pane>
|
||||
<el-tab-pane label='我的收藏' name='k4'>这个也暂不支持,待开发</el-tab-pane>
|
||||
</el-tabs>
|
||||
</el-col>
|
||||
<el-col :span='8'>
|
||||
<div style='margin-left: 5px;'>
|
||||
<div style='height: 36px;'>
|
||||
<div style='margin-top: 20px'>已 选</div>
|
||||
</div>
|
||||
<div style='border: 1px solid #ccc;height: 390px;overflow: auto;'>
|
||||
<ul class='z-org-selected' style='position: relative'>
|
||||
<li v-for='item in seldItems' :key='item.id' class='f-user' style='overflow: hidden;'>
|
||||
<el-icon @click='selectedItemClick(item)'>
|
||||
<circle-close />
|
||||
</el-icon>
|
||||
<img src='https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=1813762643,1914315241&fm=26&gp=0.jpg'>
|
||||
<span class='layui-elip f-user-name2'>{{ item.name }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
<template #footer>
|
||||
<span class='dialog-footer'>
|
||||
<el-button type='primary' @click='closeModal'>确认</el-button>
|
||||
<el-button @click='state.isShow = false'>取消</el-button>
|
||||
<el-button @click='clearAndcloseModal' style='margin-left: 20px'>清空选择</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script lang='ts' setup>
|
||||
import { defineExpose, reactive, ref, toRaw, toRefs } from 'vue';
|
||||
import { CircleClose } from '@element-plus/icons-vue';
|
||||
import DeptTree from '/@/comps/sys/DeptTree.vue';
|
||||
import request from '/@/utils/request';
|
||||
|
||||
let p_org_type = -1;//0为综合,1为机构,2为部门,4为岗位,8为用户,16为常用群组,32为角色线
|
||||
let p_tab_key = 'k1';
|
||||
let p_select_mode = 1;//1单选,2多选
|
||||
let p_opener = null as any;//弹框打开者标记
|
||||
// let p_tier_init_flag = false;//层级架构页签初始化标记
|
||||
let p_tier_init_flag = false;//层级架构页签初始化标记
|
||||
|
||||
const deptTreeRef = ref();
|
||||
|
||||
const state = reactive({
|
||||
isShow: false,
|
||||
receSearch: '' as string,
|
||||
receItems: [] as any,
|
||||
tierItems: [] as any,
|
||||
seldItems: [] as any,
|
||||
});
|
||||
|
||||
const { receItems, tierItems, seldItems } = toRefs(state);
|
||||
|
||||
const activeName = ref('k1');
|
||||
|
||||
//region z Tab切换
|
||||
async function tabChange(tab: any) {
|
||||
p_tab_key = tab.paneName;
|
||||
if (p_tab_key === 'k1') {
|
||||
for (const rItem of receItems.value) {
|
||||
let flag = false;
|
||||
for (const sItem of seldItems.value) {
|
||||
if (sItem.id == rItem.id) {
|
||||
rItem.checked = true;
|
||||
flag = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!flag) {
|
||||
rItem.checked = false;
|
||||
}
|
||||
}
|
||||
} else if (p_tab_key === 'k2') {
|
||||
if (!p_tier_init_flag) {
|
||||
await deptTreeRef.value.init();
|
||||
p_tier_init_flag = true;
|
||||
}
|
||||
for (const tItem of tierItems.value) {
|
||||
let flag = false;
|
||||
for (const sItem of seldItems.value) {
|
||||
if (sItem.id == tItem.id) {
|
||||
tItem.checked = true;
|
||||
flag = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!flag) {
|
||||
tItem.checked = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//endregion
|
||||
|
||||
|
||||
//region x.1 最近使用
|
||||
|
||||
async function onSearch() {
|
||||
receItems.value = await request({
|
||||
url: '/gen/org/main?type=' + p_org_type,
|
||||
method: 'get',
|
||||
params: { name: state.receSearch },
|
||||
});
|
||||
for (const rItem of receItems.value) {
|
||||
let flag = false;
|
||||
for (const sItem of seldItems.value) {
|
||||
if (sItem.id == rItem.id) {
|
||||
rItem.checked = true;
|
||||
flag = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!flag) {
|
||||
rItem.checked = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const receInit = async () => {
|
||||
receItems.value = await request({
|
||||
url: '/gen/org/rece?type=' + p_org_type,
|
||||
method: 'get',
|
||||
});
|
||||
};
|
||||
|
||||
function receItemClick(item: any) {
|
||||
item.checked = !item.checked;
|
||||
if (p_select_mode == 1) {//单选
|
||||
if (seldItems.value.length > 0) {
|
||||
seldItems.value.splice(0, 1);
|
||||
}
|
||||
seldItems.value.push(item);
|
||||
for (let i = 0; i < receItems.value.length; i++) {
|
||||
if (receItems.value[i].id !== item.id) {
|
||||
receItems.value[i].checked = false;
|
||||
}
|
||||
}
|
||||
// closeOrgModal();改成不自动关
|
||||
} else {//多选
|
||||
if (item.checked) {
|
||||
let flag = false;
|
||||
for (const sItem of seldItems.value) {
|
||||
if (sItem.id === item.id) {
|
||||
flag = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!flag) {
|
||||
seldItems.value.push(item);
|
||||
}
|
||||
} else {
|
||||
for (let i = 0; i < seldItems.value.length; i++) {
|
||||
if (seldItems.value[i].id === item.id) {
|
||||
seldItems.value.splice(i, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//endregion
|
||||
|
||||
|
||||
//region x.2 层级架构
|
||||
|
||||
async function nodeClick(node: any) {
|
||||
if (node && node.id) {
|
||||
tierItems.value = await request({
|
||||
url: '/gen/org/main?type=' + p_org_type,
|
||||
method: 'get',
|
||||
params: { deptid: node.id },
|
||||
});
|
||||
for (const tItem of tierItems.value) {
|
||||
for (const sItem of seldItems.value) {
|
||||
if (sItem.id === tItem.id) {
|
||||
tItem.checked = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function tierItemClick(item: any) {
|
||||
item.checked = !item.checked;
|
||||
if (p_select_mode == 1) {//单选
|
||||
if (seldItems.value.length > 0) {
|
||||
seldItems.value.splice(0, 1);
|
||||
}
|
||||
seldItems.value.push(item);
|
||||
for (let i = 0; i < tierItems.value.length; i++) {
|
||||
if (tierItems.value[i].id !== item.id) {
|
||||
tierItems.value[i].checked = false;
|
||||
}
|
||||
}
|
||||
// closeOrgModal();改成不自动关
|
||||
} else {//多选
|
||||
if (item.checked) {
|
||||
let flag = false;
|
||||
for (const sItem of seldItems.value) {
|
||||
if (sItem.id === item.id) {
|
||||
flag = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!flag) {
|
||||
seldItems.value.push(item);
|
||||
}
|
||||
} else {
|
||||
for (let i = 0; i < seldItems.value.length; i++) {
|
||||
if (seldItems.value[i].id === item.id) {
|
||||
seldItems.value.splice(i, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//endregion
|
||||
|
||||
//region x.3 常用群组
|
||||
|
||||
//endregion
|
||||
|
||||
//region x.4 我的收藏
|
||||
|
||||
//endregion
|
||||
|
||||
//region c.1 已选逻辑
|
||||
function selectedItemClick(item: any) {
|
||||
if (p_tab_key === 'k1') {
|
||||
for (let i = 0; i < receItems.value.length; i++) {
|
||||
if (receItems.value[i].id === item.id) {
|
||||
receItems.value[i].checked = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (p_tab_key === 'k2') {
|
||||
for (let i = 0; i < tierItems.value.length; i++) {
|
||||
if (tierItems.value[i].id === item.id) {
|
||||
tierItems.value[i].checked = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (let j = 0; j < seldItems.value.length; j++) {
|
||||
if (seldItems.value[j].id === item.id) {
|
||||
seldItems.value.splice(j, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//endregion
|
||||
|
||||
|
||||
//region 与父组件的交互逻辑
|
||||
const openModal = async (data: any) => {
|
||||
if (!data.orgType) {
|
||||
data.orgType = 8;
|
||||
}
|
||||
if (!data.selectMode) {
|
||||
data.selectMode = 1;
|
||||
}
|
||||
if (p_org_type != data.orgType) {
|
||||
p_org_type = data.orgType;
|
||||
await receInit();
|
||||
tierItems.value.splice(0, tierItems.value.length);
|
||||
}
|
||||
p_select_mode = data.selectMode;
|
||||
p_opener = data.opener;
|
||||
//1.初始化
|
||||
seldItems.value = data.orgs ? data.orgs : [];
|
||||
// //2.设置最近使用与层级架构里item的check状态
|
||||
if (p_tab_key === 'k1') {
|
||||
for (const rItem of receItems.value) {
|
||||
for (const sItem of seldItems.value) {
|
||||
let flag = false;
|
||||
if (sItem.id == rItem.id) {
|
||||
rItem.checked = true;
|
||||
flag = true;
|
||||
break;
|
||||
}
|
||||
if (!flag) {
|
||||
rItem.checked = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (p_tab_key === 'k2') {
|
||||
for (const tItem of tierItems.value) {
|
||||
for (const sItem of seldItems.value) {
|
||||
let flag = false;
|
||||
if (sItem.id == tItem.id) {
|
||||
tItem.checked = true;
|
||||
flag = true;
|
||||
break;
|
||||
}
|
||||
if (!flag) {
|
||||
tItem.checked = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
state.isShow = true;
|
||||
};
|
||||
|
||||
defineExpose({ openModal });
|
||||
|
||||
const emits = defineEmits(['close']);
|
||||
const closeModal = () => {
|
||||
const rawSeldItems = toRaw(seldItems.value);
|
||||
if (rawSeldItems && rawSeldItems.length > 0) {
|
||||
//更新后台数据库最近使用的ORGS
|
||||
request({
|
||||
url: '/gen/org/rece',
|
||||
method: 'post',
|
||||
data: rawSeldItems,
|
||||
});
|
||||
}
|
||||
emits('close', { opener: p_opener, orgs: toRaw(rawSeldItems) });
|
||||
state.isShow = false;
|
||||
};
|
||||
|
||||
const clearAndcloseModal = () => {
|
||||
emits('close', { opener: p_opener, orgs: null });
|
||||
state.isShow = false;
|
||||
};
|
||||
//endregion
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.dialog-footer button:first-child {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.layui-elip {
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
|
||||
.f-user:before,
|
||||
.f-user:after {
|
||||
content: " ";
|
||||
display: table;
|
||||
}
|
||||
|
||||
.f-user:after {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.f-user input, .f-user label {
|
||||
float: left;
|
||||
margin-top: 10px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.f-user img {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
float: left;
|
||||
border-radius: 100%;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
|
||||
.f-user .f-user-name,
|
||||
.f-user .f-user-dept {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.f-user .f-user-name {
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
.f-user .f-user-dept {
|
||||
color: #a599b2;
|
||||
}
|
||||
|
||||
/*最近*/
|
||||
.z-org-recently li {
|
||||
width: 33%;
|
||||
float: left;
|
||||
cursor: pointer;
|
||||
padding: 5px;
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
.z-org-recently li:hover {
|
||||
background-color: #f7efec;
|
||||
}
|
||||
|
||||
/*搜索查询*/
|
||||
.z-org-search li {
|
||||
/*width: 280px;*/
|
||||
width: 50%;
|
||||
float: left;
|
||||
cursor: pointer;
|
||||
padding: 2px;
|
||||
list-style-type: none;
|
||||
/*margin: 3px;*/
|
||||
}
|
||||
|
||||
.z-org-search li:hover {
|
||||
background-color: #f7efec;
|
||||
}
|
||||
|
||||
/*!*组织架构树*!*/
|
||||
.z-org-tree li {
|
||||
margin: 3px;
|
||||
cursor: pointer;
|
||||
padding: 3px;
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
.z-org-tree li:hover {
|
||||
background-color: #f7efec;
|
||||
}
|
||||
|
||||
/*已选的*/
|
||||
.z-org-selected li {
|
||||
margin: 3px;
|
||||
cursor: pointer;
|
||||
padding: 3px;
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
.z-org-selected li:hover {
|
||||
background-color: #f7efec;
|
||||
}
|
||||
|
||||
.z-org-selected .f-user .el-icon {
|
||||
/*float: left;*/
|
||||
/*position:relative;*/
|
||||
position: absolute;
|
||||
/*position:fixed;*/
|
||||
color: #f26b96;
|
||||
font-size: 20px;
|
||||
display: none;
|
||||
margin-left: 42px;
|
||||
}
|
||||
|
||||
.z-org-selected li:hover .el-icon {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.z-org-selected .f-user img {
|
||||
display: inline;
|
||||
/*float: none;*/
|
||||
}
|
||||
|
||||
.z-org-selected .f-user-name2 {
|
||||
display: block;
|
||||
margin-top: 14px;
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,56 @@
|
||||
import { createI18n } from 'vue-i18n';
|
||||
import zhcnLocale from 'element-plus/lib/locale/lang/zh-cn';
|
||||
import enLocale from 'element-plus/lib/locale/lang/en';
|
||||
import zhtwLocale from 'element-plus/lib/locale/lang/zh-tw';
|
||||
import { store } from '/@/store/index';
|
||||
|
||||
import nextZhcn from '/@/i18n/lang/zh-cn';
|
||||
import nextEn from '/@/i18n/lang/en';
|
||||
import nextZhtw from '/@/i18n/lang/zh-tw';
|
||||
|
||||
import pagesLoginZhcn from '/@/i18n/pages/login/zh-cn';
|
||||
import pagesLoginEn from '/@/i18n/pages/login/en';
|
||||
import pagesLoginZhtw from '/@/i18n/pages/login/zh-tw';
|
||||
import pagesFormI18nZhcn from '/@/i18n/pages/formI18n/zh-cn';
|
||||
import pagesFormI18nEn from '/@/i18n/pages/formI18n/en';
|
||||
import pagesFormI18nZhtw from '/@/i18n/pages/formI18n/zh-tw';
|
||||
|
||||
// 定义语言国际化内容
|
||||
/**
|
||||
* 说明:
|
||||
* /src/i18n/lang 下的 ts 为框架的国际化内容
|
||||
* /src/i18n/pages 下的 ts 为各界面的国际化内容
|
||||
*/
|
||||
const messages = {
|
||||
[zhcnLocale.name]: {
|
||||
...zhcnLocale,
|
||||
message: {
|
||||
...nextZhcn,
|
||||
...pagesLoginZhcn,
|
||||
...pagesFormI18nZhcn,
|
||||
},
|
||||
},
|
||||
[enLocale.name]: {
|
||||
...enLocale,
|
||||
message: {
|
||||
...nextEn,
|
||||
...pagesLoginEn,
|
||||
...pagesFormI18nEn,
|
||||
},
|
||||
},
|
||||
[zhtwLocale.name]: {
|
||||
...zhtwLocale,
|
||||
message: {
|
||||
...nextZhtw,
|
||||
...pagesLoginZhtw,
|
||||
...pagesFormI18nZhtw,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
// 导出语言国际化
|
||||
export const i18n = createI18n({
|
||||
locale: store.state.themeConfig.themeConfig.globalI18n,
|
||||
fallbackLocale: zhcnLocale.name,
|
||||
messages,
|
||||
});
|
||||
@@ -0,0 +1,181 @@
|
||||
// 定义内容
|
||||
export default {
|
||||
router: {
|
||||
home: 'home',
|
||||
system: 'system',
|
||||
systemMenu: 'systemMenu',
|
||||
systemRole: 'systemRole',
|
||||
systemUser: 'systemUser',
|
||||
systemDept: 'systemDept',
|
||||
systemDic: 'systemDic',
|
||||
limits: 'limits',
|
||||
limitsFrontEnd: 'FrontEnd',
|
||||
limitsFrontEndPage: 'FrontEndPage',
|
||||
limitsFrontEndBtn: 'FrontEndBtn',
|
||||
limitsBackEnd: 'BackEnd',
|
||||
limitsBackEndEndPage: 'BackEndEndPage',
|
||||
menu: 'menu',
|
||||
menu1: 'menu1',
|
||||
menu11: 'menu11',
|
||||
menu12: 'menu12',
|
||||
menu121: 'menu121',
|
||||
menu122: 'menu122',
|
||||
menu13: 'menu13',
|
||||
menu2: 'menu2',
|
||||
funIndex: 'function',
|
||||
funTagsView: 'funTagsView',
|
||||
funCountup: 'countup',
|
||||
funWangEditor: 'wangEditor',
|
||||
funCropper: 'cropper',
|
||||
funQrcode: 'qrcode',
|
||||
funEchartsMap: 'EchartsMap',
|
||||
funPrintJs: 'PrintJs',
|
||||
funClipboard: 'Copy cut',
|
||||
funGridLayout: 'Drag layout',
|
||||
funSplitpanes: 'Pane splitter',
|
||||
funDragVerify: 'Validator',
|
||||
pagesIndex: 'pages',
|
||||
pagesFiltering: 'Filtering',
|
||||
pagesFilteringDetails: 'FilteringDetails',
|
||||
pagesFilteringDetails1: 'FilteringDetails1',
|
||||
pagesIocnfont: 'iconfont icon',
|
||||
pagesElement: 'element icon',
|
||||
pagesAwesome: 'awesome icon',
|
||||
pagesFormAdapt: 'FormAdapt',
|
||||
pagesTableRules: 'pagesTableRules',
|
||||
pagesFormI18n: 'FormI18n',
|
||||
pagesFormRules: 'Multi form validation',
|
||||
pagesDynamicForm: 'Dynamic complex form',
|
||||
pagesWorkflow: 'Workflow',
|
||||
pagesListAdapt: 'ListAdapt',
|
||||
pagesWaterfall: 'Waterfall',
|
||||
pagesSteps: 'Steps',
|
||||
pagesPreview: 'Large preview',
|
||||
pagesWaves: 'Wave effect',
|
||||
pagesTree: 'tree alter table',
|
||||
pagesDrag: 'Drag command',
|
||||
pagesLazyImg: 'Image lazy loading',
|
||||
makeIndex: 'makeIndex',
|
||||
makeSelector: 'Icon selector',
|
||||
makeNoticeBar: 'notification bar',
|
||||
makeSvgDemo: 'Svgicon demo',
|
||||
paramsIndex: 'Routing parameters',
|
||||
paramsCommon: 'General routing',
|
||||
paramsDynamic: 'Dynamic routing',
|
||||
paramsCommonDetails: 'General routing details',
|
||||
paramsDynamicDetails: 'Dynamic routing details',
|
||||
chartIndex: 'chartIndex',
|
||||
visualizingIndex: 'visualizingIndex',
|
||||
visualizingLinkDemo1: 'visualizingLinkDemo1',
|
||||
visualizingLinkDemo2: 'visualizingLinkDemo2',
|
||||
personal: 'personal',
|
||||
tools: 'tools',
|
||||
layoutLinkView: 'LinkView',
|
||||
layoutIfameView: 'IfameView',
|
||||
},
|
||||
staticRoutes: {
|
||||
signIn: 'signIn',
|
||||
notFound: 'notFound',
|
||||
noPower: 'noPower',
|
||||
},
|
||||
user: {
|
||||
title0: 'Component size',
|
||||
title1: 'Language switching',
|
||||
title2: 'Menu search',
|
||||
title3: 'Layout configuration',
|
||||
title4: 'news',
|
||||
title5: 'Full screen on',
|
||||
title6: 'Full screen off',
|
||||
dropdownLarge: 'large',
|
||||
dropdownDefault: 'default',
|
||||
dropdownSmall: 'small',
|
||||
dropdown1: 'home page',
|
||||
dropdown2: 'Personal Center',
|
||||
dropdown3: '404',
|
||||
dropdown4: '401',
|
||||
dropdown5: 'Log out',
|
||||
dropdown6: 'Code warehouse',
|
||||
searchPlaceholder: 'Menu search: support Chinese, routing path',
|
||||
newTitle: 'notice',
|
||||
newBtn: 'All read',
|
||||
newGo: 'Go to the notification center',
|
||||
newDesc: 'No notice',
|
||||
logOutTitle: 'Tips',
|
||||
logOutMessage: 'This operation will log out. Do you want to continue?',
|
||||
logOutConfirm: 'determine',
|
||||
logOutCancel: 'cancel',
|
||||
logOutExit: 'Exiting',
|
||||
logOutSuccess: 'Exit successfully!',
|
||||
},
|
||||
tagsView: {
|
||||
refresh: 'refresh',
|
||||
close: 'close',
|
||||
closeOther: 'closeOther',
|
||||
closeAll: 'closeAll',
|
||||
fullscreen: 'fullscreen',
|
||||
closeFullscreen: 'closeFullscreen',
|
||||
},
|
||||
notFound: {
|
||||
foundTitle: 'Wrong address input, please re-enter the address~',
|
||||
foundMsg: 'You can check the web address first, and then re-enter or give us feedback.',
|
||||
foundBtn: 'Back to home page',
|
||||
},
|
||||
noAccess: {
|
||||
accessTitle: 'You are not authorized to operate~',
|
||||
accessMsg: 'Contact information: add QQ group discussion 665452019',
|
||||
accessBtn: 'Reauthorization',
|
||||
},
|
||||
layout: {
|
||||
configTitle: 'Layout configuration',
|
||||
oneTitle: 'Global Themes',
|
||||
twoTopTitle: 'top bar set up',
|
||||
twoMenuTitle: 'Menu set up',
|
||||
twoColumnsTitle: 'Columns set up',
|
||||
twoTopBar: 'Top bar background',
|
||||
twoTopBarColor: 'Top bar default font color',
|
||||
twoIsTopBarColorGradual: 'Top bar gradient',
|
||||
twoMenuBar: 'Menu background',
|
||||
twoMenuBarColor: 'Menu default font color',
|
||||
twoIsMenuBarColorGradual: 'Menu gradient',
|
||||
twoColumnsMenuBar: 'Column menu background',
|
||||
twoColumnsMenuBarColor: 'Default font color bar menu',
|
||||
twoIsColumnsMenuBarColorGradual: 'Column gradient',
|
||||
threeTitle: 'Interface settings',
|
||||
threeIsCollapse: 'Menu horizontal collapse',
|
||||
threeIsUniqueOpened: 'Menu accordion',
|
||||
threeIsFixedHeader: 'Fixed header',
|
||||
threeIsClassicSplitMenu: 'Classic layout split menu',
|
||||
threeIsLockScreen: 'Open the lock screen',
|
||||
threeLockScreenTime: 'screen locking(s/s)',
|
||||
fourTitle: 'Interface display',
|
||||
fourIsShowLogo: 'Sidebar logo',
|
||||
fourIsBreadcrumb: 'Open breadcrumb',
|
||||
fourIsBreadcrumbIcon: 'Open breadcrumb icon',
|
||||
fourIsTagsview: 'Open tagsview',
|
||||
fourIsTagsviewIcon: 'Open tagsview Icon',
|
||||
fourIsCacheTagsView: 'Enable tagsview cache',
|
||||
fourIsSortableTagsView: 'Enable tagsview drag',
|
||||
fourIsShareTagsView: 'Enable tagsview sharing',
|
||||
fourIsFooter: 'Open footer',
|
||||
fourIsGrayscale: 'Grey model',
|
||||
fourIsInvert: 'Color weak mode',
|
||||
fourIsDark: 'Dark Mode',
|
||||
fourIsWartermark: 'Turn on watermark',
|
||||
fourWartermarkText: 'Watermark copy',
|
||||
fiveTitle: 'Other settings',
|
||||
fiveTagsStyle: 'Tagsview style',
|
||||
fiveAnimation: 'page animation',
|
||||
fiveColumnsAsideStyle: 'Column style',
|
||||
fiveColumnsAsideLayout: 'Column layout',
|
||||
sixTitle: 'Layout switch',
|
||||
sixDefaults: 'One',
|
||||
sixClassic: 'Two',
|
||||
sixTransverse: 'Three',
|
||||
sixColumns: 'Four',
|
||||
tipText: 'Click the button below to copy the layout configuration to `/src/store/modules/themeConfig.ts` It has been modified in.',
|
||||
copyText: 'replication configuration',
|
||||
resetText: 'restore default',
|
||||
copyTextSuccess: 'Copy succeeded!',
|
||||
copyTextError: 'Copy failed!',
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,181 @@
|
||||
// 定义内容
|
||||
export default {
|
||||
router: {
|
||||
home: '首页',
|
||||
system: '系统设置',
|
||||
systemMenu: '菜单管理',
|
||||
systemRole: '角色管理',
|
||||
systemUser: '用户管理',
|
||||
systemDept: '部门管理',
|
||||
systemDic: '字典管理',
|
||||
limits: '权限管理',
|
||||
limitsFrontEnd: '前端控制',
|
||||
limitsFrontEndPage: '页面权限',
|
||||
limitsFrontEndBtn: '按钮权限',
|
||||
limitsBackEnd: '后端控制',
|
||||
limitsBackEndEndPage: '页面权限',
|
||||
menu: '菜单嵌套',
|
||||
menu1: '菜单1',
|
||||
menu11: '菜单11',
|
||||
menu12: '菜单12',
|
||||
menu121: '菜单121',
|
||||
menu122: '菜单122',
|
||||
menu13: '菜单13',
|
||||
menu2: '菜单2',
|
||||
funIndex: '功能',
|
||||
funTagsView: 'tagsView 操作',
|
||||
funCountup: '数字滚动',
|
||||
funWangEditor: 'Editor 编辑器',
|
||||
funCropper: '图片裁剪',
|
||||
funQrcode: '二维码生成',
|
||||
funEchartsMap: '地理坐标/地图',
|
||||
funPrintJs: '页面打印',
|
||||
funClipboard: '复制剪切',
|
||||
funGridLayout: '拖拽布局',
|
||||
funSplitpanes: '窗格拆分器',
|
||||
funDragVerify: '验证器',
|
||||
pagesIndex: '页面',
|
||||
pagesFiltering: '过滤筛选组件',
|
||||
pagesFilteringDetails: '过滤筛选组件详情',
|
||||
pagesFilteringDetails1: '过滤筛选组件详情111',
|
||||
pagesIocnfont: 'ali 字体图标',
|
||||
pagesElement: 'ele 字体图标',
|
||||
pagesAwesome: 'awe 字体图标',
|
||||
pagesFormAdapt: '表单自适应',
|
||||
pagesTableRules: '表单表格验证',
|
||||
pagesFormI18n: '表单国际化',
|
||||
pagesFormRules: '多表单验证',
|
||||
pagesDynamicForm: '动态复杂表单',
|
||||
pagesWorkflow: '工作流',
|
||||
pagesListAdapt: '列表自适应',
|
||||
pagesWaterfall: '瀑布屏',
|
||||
pagesSteps: '步骤条',
|
||||
pagesPreview: '大图预览',
|
||||
pagesWaves: '波浪效果',
|
||||
pagesTree: '树形改表格',
|
||||
pagesDrag: '拖动指令',
|
||||
pagesLazyImg: '图片懒加载',
|
||||
makeIndex: '组件封装',
|
||||
makeSelector: '图标选择器',
|
||||
makeNoticeBar: '滚动通知栏',
|
||||
makeSvgDemo: 'svgIcon 演示',
|
||||
paramsIndex: '路由参数',
|
||||
paramsCommon: '普通路由',
|
||||
paramsDynamic: '动态路由',
|
||||
paramsCommonDetails: '普通路由详情',
|
||||
paramsDynamicDetails: '动态路由详情',
|
||||
chartIndex: '大数据图表',
|
||||
visualizingIndex: '数据可视化',
|
||||
visualizingLinkDemo1: '数据可视化演示1',
|
||||
visualizingLinkDemo2: '数据可视化演示2',
|
||||
personal: '个人中心',
|
||||
tools: '工具类集合',
|
||||
layoutLinkView: '外链',
|
||||
layoutIfameView: '内嵌 iframe',
|
||||
},
|
||||
staticRoutes: {
|
||||
signIn: '登录',
|
||||
notFound: '找不到此页面',
|
||||
noPower: '没有权限',
|
||||
},
|
||||
user: {
|
||||
title0: '组件大小',
|
||||
title1: '语言切换',
|
||||
title2: '菜单搜索',
|
||||
title3: '布局配置',
|
||||
title4: '消息',
|
||||
title5: '开全屏',
|
||||
title6: '关全屏',
|
||||
dropdownLarge: '大型',
|
||||
dropdownDefault: '默认',
|
||||
dropdownSmall: '小型',
|
||||
dropdown1: '首页',
|
||||
dropdown2: '个人中心',
|
||||
dropdown3: '404',
|
||||
dropdown4: '401',
|
||||
dropdown5: '退出登录',
|
||||
dropdown6: '代码仓库',
|
||||
searchPlaceholder: '菜单搜索:支持中文、路由路径',
|
||||
newTitle: '通知',
|
||||
newBtn: '全部已读',
|
||||
newGo: '前往通知中心',
|
||||
newDesc: '暂无通知',
|
||||
logOutTitle: '提示',
|
||||
logOutMessage: '此操作将退出登录, 是否继续?',
|
||||
logOutConfirm: '确定',
|
||||
logOutCancel: '取消',
|
||||
logOutExit: '退出中',
|
||||
logOutSuccess: '安全退出成功!',
|
||||
},
|
||||
tagsView: {
|
||||
refresh: '刷新',
|
||||
close: '关闭',
|
||||
closeOther: '关闭其它',
|
||||
closeAll: '全部关闭',
|
||||
fullscreen: '当前页全屏',
|
||||
closeFullscreen: '关闭全屏',
|
||||
},
|
||||
notFound: {
|
||||
foundTitle: '地址输入错误,请重新输入地址~',
|
||||
foundMsg: '您可以先检查网址,然后重新输入或给我们反馈问题。',
|
||||
foundBtn: '返回首页',
|
||||
},
|
||||
noAccess: {
|
||||
accessTitle: '您未被授权,没有操作权限~',
|
||||
accessMsg: '联系方式:加QQ群探讨 665452019',
|
||||
accessBtn: '重新授权',
|
||||
},
|
||||
layout: {
|
||||
configTitle: '布局配置',
|
||||
oneTitle: '全局主题',
|
||||
twoTopTitle: '顶栏设置',
|
||||
twoMenuTitle: '菜单设置',
|
||||
twoColumnsTitle: '分栏设置',
|
||||
twoTopBar: '顶栏背景',
|
||||
twoTopBarColor: '顶栏默认字体颜色',
|
||||
twoIsTopBarColorGradual: '顶栏背景渐变',
|
||||
twoMenuBar: '菜单背景',
|
||||
twoMenuBarColor: '菜单默认字体颜色',
|
||||
twoIsMenuBarColorGradual: '菜单背景渐变',
|
||||
twoColumnsMenuBar: '分栏菜单背景',
|
||||
twoColumnsMenuBarColor: '分栏菜单默认字体颜色',
|
||||
twoIsColumnsMenuBarColorGradual: '分栏菜单背景渐变',
|
||||
threeTitle: '界面设置',
|
||||
threeIsCollapse: '菜单水平折叠',
|
||||
threeIsUniqueOpened: '菜单手风琴',
|
||||
threeIsFixedHeader: '固定 Header',
|
||||
threeIsClassicSplitMenu: '经典布局分割菜单',
|
||||
threeIsLockScreen: '开启锁屏',
|
||||
threeLockScreenTime: '自动锁屏(s/秒)',
|
||||
fourTitle: '界面显示',
|
||||
fourIsShowLogo: '侧边栏 Logo',
|
||||
fourIsBreadcrumb: '开启 Breadcrumb',
|
||||
fourIsBreadcrumbIcon: '开启 Breadcrumb 图标',
|
||||
fourIsTagsview: '开启 Tagsview',
|
||||
fourIsTagsviewIcon: '开启 Tagsview 图标',
|
||||
fourIsCacheTagsView: '开启 TagsView 缓存',
|
||||
fourIsSortableTagsView: '开启 TagsView 拖拽',
|
||||
fourIsShareTagsView: '开启 TagsView 共用',
|
||||
fourIsFooter: '开启 Footer',
|
||||
fourIsGrayscale: '灰色模式',
|
||||
fourIsInvert: '色弱模式',
|
||||
fourIsDark: '深色模式',
|
||||
fourIsWartermark: '开启水印',
|
||||
fourWartermarkText: '水印文案',
|
||||
fiveTitle: '其它设置',
|
||||
fiveTagsStyle: 'Tagsview 风格',
|
||||
fiveAnimation: '主页面切换动画',
|
||||
fiveColumnsAsideStyle: '分栏高亮风格',
|
||||
fiveColumnsAsideLayout: '分栏布局风格',
|
||||
sixTitle: '布局切换',
|
||||
sixDefaults: '默认',
|
||||
sixClassic: '经典',
|
||||
sixTransverse: '横向',
|
||||
sixColumns: '分栏',
|
||||
tipText: '点击下方按钮,复制布局配置去 `src/store/modules/themeConfig.ts` 中修改。',
|
||||
copyText: '一键复制配置',
|
||||
resetText: '一键恢复默认',
|
||||
copyTextSuccess: '复制成功!',
|
||||
copyTextError: '复制失败!',
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,181 @@
|
||||
// 定义内容
|
||||
export default {
|
||||
router: {
|
||||
home: '首頁',
|
||||
system: '系統設置',
|
||||
systemMenu: '選單管理',
|
||||
systemRole: '角色管理',
|
||||
systemUser: '用戶管理',
|
||||
systemDept: '部門管理',
|
||||
systemDic: '字典管理',
|
||||
limits: '許可權管理',
|
||||
limitsFrontEnd: '前端控制',
|
||||
limitsFrontEndPage: '頁面許可權',
|
||||
limitsFrontEndBtn: '按鈕許可權',
|
||||
limitsBackEnd: '後端控制',
|
||||
limitsBackEndEndPage: '頁面許可權',
|
||||
menu: '選單嵌套',
|
||||
menu1: '選單1',
|
||||
menu11: '選單11',
|
||||
menu12: '選單12',
|
||||
menu121: '選單121',
|
||||
menu122: '選單122',
|
||||
menu13: '選單13',
|
||||
menu2: '選單2',
|
||||
funIndex: '功能',
|
||||
funTagsView: 'tagsView 操作',
|
||||
funCountup: '數位滾動',
|
||||
funWangEditor: 'Editor 編輯器',
|
||||
funCropper: '圖片裁剪',
|
||||
funQrcode: '二維碼生成',
|
||||
funEchartsMap: '地理座標/地圖',
|
||||
funPrintJs: '頁面列印',
|
||||
funClipboard: '複製剪切',
|
||||
funGridLayout: '拖拽佈局',
|
||||
funSplitpanes: '窗格折開器',
|
||||
funDragVerify: '驗證器',
|
||||
pagesIndex: '頁面',
|
||||
pagesFiltering: '過濾篩選組件',
|
||||
pagesFilteringDetails: '過濾篩選組件詳情',
|
||||
pagesFilteringDetails1: '過濾篩選組件詳情111',
|
||||
pagesIocnfont: 'ali 字體圖標',
|
||||
pagesElement: 'ele 字體圖標',
|
||||
pagesAwesome: 'awe 字體圖標',
|
||||
pagesFormAdapt: '表單自我調整',
|
||||
pagesTableRules: '表單表格驗證',
|
||||
pagesFormI18n: '表單國際化',
|
||||
pagesFormRules: '多表單驗證',
|
||||
pagesDynamicForm: '動態複雜表單',
|
||||
pagesWorkflow: '工作流',
|
||||
pagesListAdapt: '清單自我調整',
|
||||
pagesWaterfall: '瀑布屏',
|
||||
pagesSteps: '步驟條',
|
||||
pagesPreview: '大圖預覽',
|
||||
pagesWaves: '波浪效果',
|
||||
pagesTree: '樹形改表格',
|
||||
pagesDrag: '拖動指令',
|
||||
pagesLazyImg: '圖片懶加載',
|
||||
makeIndex: '組件封裝',
|
||||
makeSelector: '圖標選擇器',
|
||||
makeNoticeBar: '滾動通知欄',
|
||||
makeSvgDemo: 'svgIcon 演示',
|
||||
paramsIndex: '路由參數',
|
||||
paramsCommon: '普通路由',
|
||||
paramsDynamic: '動態路由',
|
||||
paramsCommonDetails: '普通路由詳情',
|
||||
paramsDynamicDetails: '動態路由詳情',
|
||||
chartIndex: '大資料圖表',
|
||||
visualizingIndex: '數據視覺化',
|
||||
visualizingLinkDemo1: '數據視覺化演示1',
|
||||
visualizingLinkDemo2: '數據視覺化演示2',
|
||||
personal: '個人中心',
|
||||
tools: '工具類集合',
|
||||
layoutLinkView: '外鏈',
|
||||
layoutIfameView: '内嵌 iframe',
|
||||
},
|
||||
staticRoutes: {
|
||||
signIn: '登入',
|
||||
notFound: '找不到此頁面',
|
||||
noPower: '沒有許可權',
|
||||
},
|
||||
user: {
|
||||
title0: '組件大小',
|
||||
title1: '語言切換',
|
||||
title2: '選單蒐索',
|
||||
title3: '佈局配寘',
|
||||
title4: '消息',
|
||||
title5: '開全屏',
|
||||
title6: '關全屏',
|
||||
dropdownLarge: '大型',
|
||||
dropdownDefault: '默認',
|
||||
dropdownSmall: '小型',
|
||||
dropdown1: '首頁',
|
||||
dropdown2: '個人中心',
|
||||
dropdown3: '404',
|
||||
dropdown4: '401',
|
||||
dropdown5: '登出',
|
||||
dropdown6: '程式碼倉庫',
|
||||
searchPlaceholder: '選單蒐索:支援中文、路由路徑',
|
||||
newTitle: '通知',
|
||||
newBtn: '全部已讀',
|
||||
newGo: '前往通知中心',
|
||||
newDesc: '暫無通知',
|
||||
logOutTitle: '提示',
|
||||
logOutMessage: '此操作將登出,是否繼續?',
|
||||
logOutConfirm: '確定',
|
||||
logOutCancel: '取消',
|
||||
logOutExit: '退出中',
|
||||
logOutSuccess: '安全退出成功!',
|
||||
},
|
||||
tagsView: {
|
||||
refresh: '重繪',
|
||||
close: '關閉',
|
||||
closeOther: '關閉其它',
|
||||
closeAll: '全部關閉',
|
||||
fullscreen: '當前頁全屏',
|
||||
closeFullscreen: '關閉全屏',
|
||||
},
|
||||
notFound: {
|
||||
foundTitle: '地址輸入錯誤,請重新輸入地址~',
|
||||
foundMsg: '您可以先檢查網址,然後重新輸入或給我們迴響問題。',
|
||||
foundBtn: '返回首頁',
|
||||
},
|
||||
noAccess: {
|
||||
accessTitle: '您未被授權,沒有操作許可權~',
|
||||
accessMsg: '聯繫方式:加QQ群探討665452019',
|
||||
accessBtn: '重新授權',
|
||||
},
|
||||
layout: {
|
||||
configTitle: '佈局配寘',
|
||||
oneTitle: '全域主題',
|
||||
twoTopTitle: '頂欄設定',
|
||||
twoMenuTitle: '選單設定',
|
||||
twoColumnsTitle: '分欄設定',
|
||||
twoTopBar: '頂欄背景',
|
||||
twoTopBarColor: '頂欄默認字體顏色',
|
||||
twoIsTopBarColorGradual: '頂欄背景漸變',
|
||||
twoMenuBar: '選單背景',
|
||||
twoMenuBarColor: '選單默認字體顏色',
|
||||
twoIsMenuBarColorGradual: '選單背景漸變',
|
||||
twoColumnsMenuBar: '分欄選單背景',
|
||||
twoColumnsMenuBarColor: '分欄選單默認字體顏色',
|
||||
twoIsColumnsMenuBarColorGradual: '分欄選單背景漸變',
|
||||
threeTitle: '介面設定',
|
||||
threeIsCollapse: '選單水准折疊',
|
||||
threeIsUniqueOpened: '選單手風琴',
|
||||
threeIsFixedHeader: '固定 Header',
|
||||
threeIsClassicSplitMenu: '經典佈局分割選單',
|
||||
threeIsLockScreen: '開啟鎖屏',
|
||||
threeLockScreenTime: '自動鎖屏(s/秒)',
|
||||
fourTitle: '介面顯示',
|
||||
fourIsShowLogo: '側邊欄 Logo',
|
||||
fourIsBreadcrumb: '開啟 Breadcrumb',
|
||||
fourIsBreadcrumbIcon: '開啟 Breadcrumb 圖標',
|
||||
fourIsTagsview: '開啟 Tagsview',
|
||||
fourIsTagsviewIcon: '開啟 Tagsview 圖標',
|
||||
fourIsCacheTagsView: '開啟 TagsView 緩存',
|
||||
fourIsSortableTagsView: '開啟 TagsView 拖拽',
|
||||
fourIsShareTagsView: '開啟 TagsView 共用',
|
||||
fourIsFooter: '開啟 Footer',
|
||||
fourIsGrayscale: '灰色模式',
|
||||
fourIsInvert: '色弱模式',
|
||||
fourIsDark: '深色模式',
|
||||
fourIsWartermark: '開啟浮水印',
|
||||
fourWartermarkText: '浮水印文案',
|
||||
fiveTitle: '其它設定',
|
||||
fiveTagsStyle: 'Tagsview 風格',
|
||||
fiveAnimation: '主頁面切換動畫',
|
||||
fiveColumnsAsideStyle: '分欄高亮風格',
|
||||
fiveColumnsAsideLayout: '分欄佈局風格',
|
||||
sixTitle: '佈局切換',
|
||||
sixDefaults: '默認',
|
||||
sixClassic: '經典',
|
||||
sixTransverse: '橫向',
|
||||
sixColumns: '分欄',
|
||||
tipText: '點擊下方按鈕,複製佈局配寘去`src/store/modules/themeConfig.ts`中修改。',
|
||||
copyText: '一鍵複製配寘',
|
||||
resetText: '一鍵恢復默認',
|
||||
copyTextSuccess: '複製成功!',
|
||||
copyTextError: '複製失敗!',
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,13 @@
|
||||
// 定义内容
|
||||
export default {
|
||||
formI18nLabel: {
|
||||
name: 'name',
|
||||
email: 'email',
|
||||
autograph: 'autograph',
|
||||
},
|
||||
formI18nPlaceholder: {
|
||||
name: 'Please enter your name',
|
||||
email: 'Please enter the users Department',
|
||||
autograph: 'Please enter the login account name',
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,13 @@
|
||||
// 定义内容
|
||||
export default {
|
||||
formI18nLabel: {
|
||||
name: '姓名',
|
||||
email: '用户归属部门',
|
||||
autograph: '登陆账户名',
|
||||
},
|
||||
formI18nPlaceholder: {
|
||||
name: '请输入姓名',
|
||||
email: '请输入用户归属部门',
|
||||
autograph: '请输入登陆账户名',
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,13 @@
|
||||
// 定义内容
|
||||
export default {
|
||||
formI18nLabel: {
|
||||
name: '姓名',
|
||||
email: '用戶歸屬部門',
|
||||
autograph: '登入帳戶名',
|
||||
},
|
||||
formI18nPlaceholder: {
|
||||
name: '請輸入姓名',
|
||||
email: '請輸入用戶歸屬部門',
|
||||
autograph: '請輸入登入帳戶名',
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,29 @@
|
||||
// 定义内容
|
||||
export default {
|
||||
label: {
|
||||
one1: 'User name login',
|
||||
two2: 'Mobile number',
|
||||
},
|
||||
link: {
|
||||
one3: 'Third party login',
|
||||
two4: 'Links',
|
||||
},
|
||||
account: {
|
||||
accountPlaceholder1: 'The user name admin or not is common',
|
||||
accountPlaceholder2: 'Password: 123456',
|
||||
accountPlaceholder3: 'Please enter the verification code',
|
||||
accountBtnText: 'Sign in',
|
||||
},
|
||||
mobile: {
|
||||
placeholder1: 'Please input mobile phone number',
|
||||
placeholder2: 'Please enter the verification code',
|
||||
codeText: 'Get code',
|
||||
btnText: 'Sign in',
|
||||
msgText:
|
||||
'Warm tip: it is recommended to use Google, Microsoft edge, version 79.0.1072.62 and above browsers, and 360 browser, please use speed mode',
|
||||
},
|
||||
scan: {
|
||||
text: 'Open the mobile phone to scan and quickly log in / register',
|
||||
},
|
||||
signInText: 'welcome back!',
|
||||
};
|
||||
@@ -0,0 +1,28 @@
|
||||
// 定义内容
|
||||
export default {
|
||||
label: {
|
||||
one1: '用户名登录',
|
||||
two2: '手机号登录',
|
||||
},
|
||||
link: {
|
||||
one3: '第三方登录',
|
||||
two4: '友情链接',
|
||||
},
|
||||
account: {
|
||||
accountPlaceholder1: '用户名 admin 或不输均为 common',
|
||||
accountPlaceholder2: '密码:123456',
|
||||
accountPlaceholder3: '请输入验证码',
|
||||
accountBtnText: '登 录',
|
||||
},
|
||||
mobile: {
|
||||
placeholder1: '请输入手机号',
|
||||
placeholder2: '请输入验证码',
|
||||
codeText: '获取验证码',
|
||||
btnText: '登 录',
|
||||
msgText: '* 温馨提示:建议使用谷歌、Microsoft Edge,版本 79.0.1072.62 及以上浏览器,360浏览器请使用极速模式',
|
||||
},
|
||||
scan: {
|
||||
text: '打开手机扫一扫,快速登录/注册',
|
||||
},
|
||||
signInText: '欢迎回来!',
|
||||
};
|
||||
@@ -0,0 +1,28 @@
|
||||
// 定义内容
|
||||
export default {
|
||||
label: {
|
||||
one1: '用戶名登入',
|
||||
two2: '手機號登入',
|
||||
},
|
||||
link: {
|
||||
one3: '協力廠商登入',
|
||||
two4: '友情連結',
|
||||
},
|
||||
account: {
|
||||
accountPlaceholder1: '用戶名admin或不輸均為common',
|
||||
accountPlaceholder2: '密碼:123456',
|
||||
accountPlaceholder3: '請輸入驗證碼',
|
||||
accountBtnText: '登入',
|
||||
},
|
||||
mobile: {
|
||||
placeholder1: '請輸入手機號',
|
||||
placeholder2: '請輸入驗證碼',
|
||||
codeText: '獲取驗證碼',
|
||||
btnText: '登入',
|
||||
msgText: '* 溫馨提示:建議使用穀歌、Microsoft Edge,版本79.0.1072.62及以上瀏覽器,360瀏覽器請使用極速模式',
|
||||
},
|
||||
scan: {
|
||||
text: '打開手機掃一掃,快速登錄/注册',
|
||||
},
|
||||
signInText: '歡迎回來!',
|
||||
};
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,157 @@
|
||||
<template>
|
||||
<div class="h100" v-show="!isTagsViewCurrenFull">
|
||||
<el-aside class="layout-aside" :class="setCollapseStyle">
|
||||
<Logo v-if="setShowLogo" />
|
||||
<el-scrollbar class="flex-auto" ref="layoutAsideScrollbarRef" @mouseenter="onAsideEnterLeave(true)" @mouseleave="onAsideEnterLeave(false)">
|
||||
<Vertical :menuList="menuList" />
|
||||
</el-scrollbar>
|
||||
</el-aside>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { toRefs, reactive, computed, watch, getCurrentInstance, onBeforeMount, defineComponent } from 'vue';
|
||||
import { useStore } from '/@/store/index';
|
||||
import Logo from '/@/layout/logo/index.vue';
|
||||
import Vertical from '/@/layout/navMenu/vertical.vue';
|
||||
export default defineComponent({
|
||||
name: 'layoutAside',
|
||||
components: { Logo, Vertical },
|
||||
setup() {
|
||||
const { proxy } = <any>getCurrentInstance();
|
||||
const store = useStore();
|
||||
const state = reactive({
|
||||
menuList: [],
|
||||
clientWidth: 0,
|
||||
});
|
||||
// 获取卡片全屏信息
|
||||
const isTagsViewCurrenFull = computed(() => {
|
||||
return store.state.tagsViewRoutes.isTagsViewCurrenFull;
|
||||
});
|
||||
// 设置菜单展开/收起时的宽度
|
||||
const setCollapseStyle = computed(() => {
|
||||
const { layout, isCollapse, menuBar } = store.state.themeConfig.themeConfig;
|
||||
const asideBrTheme = ['#FFFFFF', '#FFF', '#fff', '#ffffff'];
|
||||
const asideBrColor = asideBrTheme.includes(menuBar) ? 'layout-el-aside-br-color' : '';
|
||||
// 判断是否是手机端
|
||||
if (state.clientWidth <= 1000) {
|
||||
if (isCollapse) {
|
||||
document.body.setAttribute('class', 'el-popup-parent--hidden');
|
||||
const asideEle = document.querySelector('.layout-container') as HTMLElement;
|
||||
const modeDivs = document.createElement('div');
|
||||
modeDivs.setAttribute('class', 'layout-aside-mobile-mode');
|
||||
asideEle.appendChild(modeDivs);
|
||||
modeDivs.addEventListener('click', closeLayoutAsideMobileMode);
|
||||
return [asideBrColor, 'layout-aside-mobile', 'layout-aside-mobile-open'];
|
||||
} else {
|
||||
// 关闭弹窗
|
||||
closeLayoutAsideMobileMode();
|
||||
return [asideBrColor, 'layout-aside-mobile', 'layout-aside-mobile-close'];
|
||||
}
|
||||
} else {
|
||||
if (layout === 'columns') {
|
||||
// 分栏布局,菜单收起时宽度给 1px
|
||||
if (isCollapse) {
|
||||
return [asideBrColor, 'layout-aside-pc-1'];
|
||||
} else {
|
||||
return [asideBrColor, 'layout-aside-pc-220'];
|
||||
}
|
||||
} else {
|
||||
// 其它布局给 64px
|
||||
if (isCollapse) {
|
||||
return [asideBrColor, 'layout-aside-pc-64'];
|
||||
} else {
|
||||
return [asideBrColor, 'layout-aside-pc-220'];
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
// 关闭移动端蒙版
|
||||
const closeLayoutAsideMobileMode = () => {
|
||||
const el = document.querySelector('.layout-aside-mobile-mode');
|
||||
el?.setAttribute('style', 'animation: error-img-two 0.3s');
|
||||
setTimeout(() => {
|
||||
el?.parentNode?.removeChild(el);
|
||||
}, 300);
|
||||
const clientWidth = document.body.clientWidth;
|
||||
if (clientWidth < 1000) store.state.themeConfig.themeConfig.isCollapse = false;
|
||||
document.body.setAttribute('class', '');
|
||||
};
|
||||
// 设置显示/隐藏 logo
|
||||
const setShowLogo = computed(() => {
|
||||
let { layout, isShowLogo } = store.state.themeConfig.themeConfig;
|
||||
return (isShowLogo && layout === 'defaults') || (isShowLogo && layout === 'columns');
|
||||
});
|
||||
// 设置/过滤路由(非静态路由/是否显示在菜单中)
|
||||
const setFilterRoutes = () => {
|
||||
if (store.state.themeConfig.themeConfig.layout === 'columns') return false;
|
||||
(state.menuList as any) = filterRoutesFun(store.state.routesList.routesList);
|
||||
};
|
||||
// 路由过滤递归函数
|
||||
const filterRoutesFun = (arr: Array<object>) => {
|
||||
return arr
|
||||
.filter((item: any) => !item.meta.isHide)
|
||||
.map((item: any) => {
|
||||
item = Object.assign({}, item);
|
||||
if (item.children) item.children = filterRoutesFun(item.children);
|
||||
return item;
|
||||
});
|
||||
};
|
||||
// 设置菜单导航是否固定(移动端)
|
||||
const initMenuFixed = (clientWidth: number) => {
|
||||
state.clientWidth = clientWidth;
|
||||
};
|
||||
// 鼠标移入、移出
|
||||
const onAsideEnterLeave = (bool: Boolean) => {
|
||||
let { layout } = store.state.themeConfig.themeConfig;
|
||||
if (layout !== 'columns') return false;
|
||||
if (!bool) proxy.mittBus.emit('restoreDefault');
|
||||
store.dispatch('routesList/setColumnsMenuHover', bool);
|
||||
};
|
||||
// 监听 themeConfig 配置文件的变化,更新菜单 el-scrollbar 的高度
|
||||
watch(store.state.themeConfig.themeConfig, (val) => {
|
||||
if (val.isShowLogoChange !== val.isShowLogo) {
|
||||
if (!proxy.$refs.layoutAsideScrollbarRef) return false;
|
||||
proxy.$refs.layoutAsideScrollbarRef.update();
|
||||
}
|
||||
});
|
||||
// 监听vuex值的变化,动态赋值给菜单中
|
||||
watch(store.state, (val) => {
|
||||
let { layout, isClassicSplitMenu } = val.themeConfig.themeConfig;
|
||||
if (layout === 'classic' && isClassicSplitMenu) return false;
|
||||
setFilterRoutes();
|
||||
});
|
||||
// 页面加载前
|
||||
onBeforeMount(() => {
|
||||
initMenuFixed(document.body.clientWidth);
|
||||
setFilterRoutes();
|
||||
// 此界面不需要取消监听(proxy.mittBus.off('setSendColumnsChildren))
|
||||
// 因为切换布局时有的监听需要使用,取消了监听,某些操作将不生效
|
||||
proxy.mittBus.on('setSendColumnsChildren', (res: any) => {
|
||||
state.menuList = res.children;
|
||||
});
|
||||
proxy.mittBus.on('setSendClassicChildren', (res: any) => {
|
||||
let { layout, isClassicSplitMenu } = store.state.themeConfig.themeConfig;
|
||||
if (layout === 'classic' && isClassicSplitMenu) {
|
||||
state.menuList = [];
|
||||
state.menuList = res.children;
|
||||
}
|
||||
});
|
||||
proxy.mittBus.on('getBreadcrumbIndexSetFilterRoutes', () => {
|
||||
setFilterRoutes();
|
||||
});
|
||||
proxy.mittBus.on('layoutMobileResize', (res: any) => {
|
||||
initMenuFixed(res.clientWidth);
|
||||
closeLayoutAsideMobileMode();
|
||||
});
|
||||
});
|
||||
return {
|
||||
setCollapseStyle,
|
||||
setShowLogo,
|
||||
isTagsViewCurrenFull,
|
||||
onAsideEnterLeave,
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,294 @@
|
||||
<template>
|
||||
<div class="layout-columns-aside">
|
||||
<el-scrollbar>
|
||||
<ul @mouseleave="onColumnsAsideMenuMouseleave()">
|
||||
<li
|
||||
v-for="(v, k) in columnsAsideList"
|
||||
:key="k"
|
||||
@click="onColumnsAsideMenuClick(v, k)"
|
||||
@mouseenter="onColumnsAsideMenuMouseenter(v, k)"
|
||||
:ref="
|
||||
(el) => {
|
||||
if (el) columnsAsideOffsetTopRefs[k] = el;
|
||||
}
|
||||
"
|
||||
:class="{ 'layout-columns-active': liIndex === k, 'layout-columns-hover': liHoverIndex === k }"
|
||||
:title="$t(v.meta.title)"
|
||||
>
|
||||
<div :class="setColumnsAsidelayout" v-if="!v.meta.isLink || (v.meta.isLink && v.meta.isIframe)">
|
||||
<SvgIcon :name="v.meta.icon" />
|
||||
<div class="columns-vertical-title font12">
|
||||
{{
|
||||
$t(v.meta.title) && $t(v.meta.title).length >= 4
|
||||
? $t(v.meta.title).substr(0, setColumnsAsidelayout === 'columns-vertical' ? 4 : 3)
|
||||
: $t(v.meta.title)
|
||||
}}
|
||||
</div>
|
||||
</div>
|
||||
<div :class="setColumnsAsidelayout" v-else>
|
||||
<a :href="v.meta.isLink" target="_blank">
|
||||
<SvgIcon :name="v.meta.icon" />
|
||||
<div class="columns-vertical-title font12">
|
||||
{{
|
||||
$t(v.meta.title) && $t(v.meta.title).length >= 4
|
||||
? $t(v.meta.title).substr(0, setColumnsAsidelayout === 'columns-vertical' ? 4 : 3)
|
||||
: $t(v.meta.title)
|
||||
}}
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</li>
|
||||
<div ref="columnsAsideActiveRef" :class="setColumnsAsideStyle"></div>
|
||||
</ul>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { reactive, toRefs, ref, computed, onMounted, nextTick, getCurrentInstance, watch, onUnmounted, defineComponent } from 'vue';
|
||||
import { useRoute, useRouter, onBeforeRouteUpdate, RouteRecordRaw } from 'vue-router';
|
||||
import { useStore } from '/@/store/index';
|
||||
|
||||
// 定义接口来定义对象的类型
|
||||
interface ColumnsAsideState {
|
||||
columnsAsideList: any[];
|
||||
liIndex: number;
|
||||
liOldIndex: null | number;
|
||||
liHoverIndex: null | number;
|
||||
liOldPath: null | string;
|
||||
difference: number;
|
||||
routeSplit: string[];
|
||||
isNavHover: boolean;
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
name: 'layoutColumnsAside',
|
||||
setup() {
|
||||
const columnsAsideOffsetTopRefs: any = ref([]);
|
||||
const columnsAsideActiveRef = ref();
|
||||
const { proxy } = <any>getCurrentInstance();
|
||||
const store = useStore();
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const state = reactive<ColumnsAsideState>({
|
||||
columnsAsideList: [],
|
||||
liIndex: 0,
|
||||
liOldIndex: null,
|
||||
liHoverIndex: null,
|
||||
liOldPath: null,
|
||||
difference: 0,
|
||||
routeSplit: [],
|
||||
isNavHover: false,
|
||||
});
|
||||
// 设置分栏高亮风格
|
||||
const setColumnsAsideStyle = computed(() => {
|
||||
return store.state.themeConfig.themeConfig.columnsAsideStyle;
|
||||
});
|
||||
// 设置分栏布局风格
|
||||
const setColumnsAsidelayout = computed(() => {
|
||||
return store.state.themeConfig.themeConfig.columnsAsideLayout;
|
||||
});
|
||||
// 设置菜单高亮位置移动
|
||||
const setColumnsAsideMove = (k: number) => {
|
||||
state.liIndex = k;
|
||||
columnsAsideActiveRef.value.style.top = `${columnsAsideOffsetTopRefs.value[k].offsetTop + state.difference}px`;
|
||||
};
|
||||
// 菜单高亮点击事件
|
||||
const onColumnsAsideMenuClick = (v: Object, k: number) => {
|
||||
setColumnsAsideMove(k);
|
||||
let { path, redirect } = v as any;
|
||||
if (redirect) router.push(redirect);
|
||||
else router.push(path);
|
||||
};
|
||||
// 鼠标移入时,显示当前的子级菜单
|
||||
const onColumnsAsideMenuMouseenter = (v: RouteRecordRaw, k: number) => {
|
||||
let { path } = v;
|
||||
state.liOldPath = path;
|
||||
state.liOldIndex = k;
|
||||
state.liHoverIndex = k;
|
||||
proxy.mittBus.emit('setSendColumnsChildren', setSendChildren(path));
|
||||
store.dispatch('routesList/setColumnsMenuHover', false);
|
||||
store.dispatch('routesList/setColumnsNavHover', true);
|
||||
state.isNavHover = true;
|
||||
};
|
||||
// 鼠标移走时,显示原来的子级菜单
|
||||
const onColumnsAsideMenuMouseleave = async () => {
|
||||
await store.dispatch('routesList/setColumnsNavHover', false);
|
||||
// 添加延时器,防止拿到的 store.state.routesList 值不是最新的
|
||||
setTimeout(() => {
|
||||
const { isColumnsMenuHover, isColumnsNavHover } = store.state.routesList;
|
||||
if (!isColumnsMenuHover && !isColumnsNavHover) proxy.mittBus.emit('restoreDefault');
|
||||
}, 100);
|
||||
// state.isNavHover = false;
|
||||
};
|
||||
// 设置高亮动态位置
|
||||
const onColumnsAsideDown = (k: number) => {
|
||||
nextTick(() => {
|
||||
setColumnsAsideMove(k);
|
||||
});
|
||||
};
|
||||
// 设置/过滤路由(非静态路由/是否显示在菜单中)
|
||||
const setFilterRoutes = () => {
|
||||
state.columnsAsideList = filterRoutesFun(store.state.routesList.routesList);
|
||||
const resData: any = setSendChildren(route.path);
|
||||
if (Object.keys(resData).length <= 0) return false;
|
||||
onColumnsAsideDown(resData.item[0].k);
|
||||
proxy.mittBus.emit('setSendColumnsChildren', resData);
|
||||
};
|
||||
// 传送当前子级数据到菜单中
|
||||
const setSendChildren = (path: string) => {
|
||||
const currentPathSplit = path.split('/');
|
||||
let currentData: any = {};
|
||||
state.columnsAsideList.map((v: any, k: number) => {
|
||||
if (v.path === `/${currentPathSplit[1]}`) {
|
||||
v['k'] = k;
|
||||
currentData['item'] = [{ ...v }];
|
||||
currentData['children'] = [{ ...v }];
|
||||
if (v.children) currentData['children'] = v.children;
|
||||
}
|
||||
});
|
||||
return currentData;
|
||||
};
|
||||
// 路由过滤递归函数
|
||||
const filterRoutesFun = (arr: Array<object>) => {
|
||||
return arr
|
||||
.filter((item: any) => !item.meta.isHide)
|
||||
.map((item: any) => {
|
||||
item = Object.assign({}, item);
|
||||
if (item.children) item.children = filterRoutesFun(item.children);
|
||||
return item;
|
||||
});
|
||||
};
|
||||
// tagsView 点击时,根据路由查找下标 columnsAsideList,实现左侧菜单高亮
|
||||
const setColumnsMenuHighlight = (path: string) => {
|
||||
state.routeSplit = path.split('/');
|
||||
state.routeSplit.shift();
|
||||
const routeFirst = `/${state.routeSplit[0]}`;
|
||||
const currentSplitRoute = state.columnsAsideList.find((v: any) => v.path === routeFirst);
|
||||
if (!currentSplitRoute) return false;
|
||||
// 延迟拿值,防止取不到
|
||||
setTimeout(() => {
|
||||
onColumnsAsideDown((<any>currentSplitRoute).k);
|
||||
}, 0);
|
||||
};
|
||||
// 监听布局配置信息的变化,动态增加菜单高亮位置移动像素
|
||||
watch(store.state, (val) => {
|
||||
val.themeConfig.themeConfig.columnsAsideStyle === 'columnsRound' ? (state.difference = 3) : (state.difference = 0);
|
||||
if (!val.routesList.isColumnsMenuHover && !val.routesList.isColumnsNavHover) {
|
||||
state.liHoverIndex = null;
|
||||
proxy.mittBus.emit('setSendColumnsChildren', setSendChildren(route.path));
|
||||
} else {
|
||||
state.liHoverIndex = state.liOldIndex;
|
||||
if (!state.liOldPath) return false;
|
||||
proxy.mittBus.emit('setSendColumnsChildren', setSendChildren(state.liOldPath));
|
||||
}
|
||||
});
|
||||
// 页面加载时
|
||||
onMounted(() => {
|
||||
setFilterRoutes();
|
||||
// 销毁变量,防止鼠标再次移入时,保留了上次的记录
|
||||
proxy.mittBus.on('restoreDefault', () => {
|
||||
state.liOldIndex = null;
|
||||
state.liOldPath = null;
|
||||
});
|
||||
});
|
||||
// 页面卸载时
|
||||
onUnmounted(() => {
|
||||
proxy.mittBus.off('restoreDefault', () => {});
|
||||
});
|
||||
// 路由更新时
|
||||
onBeforeRouteUpdate((to) => {
|
||||
setColumnsMenuHighlight(to.path);
|
||||
proxy.mittBus.emit('setSendColumnsChildren', setSendChildren(to.path));
|
||||
});
|
||||
return {
|
||||
columnsAsideOffsetTopRefs,
|
||||
columnsAsideActiveRef,
|
||||
onColumnsAsideDown,
|
||||
setColumnsAsideStyle,
|
||||
setColumnsAsidelayout,
|
||||
onColumnsAsideMenuClick,
|
||||
onColumnsAsideMenuMouseenter,
|
||||
onColumnsAsideMenuMouseleave,
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.layout-columns-aside {
|
||||
width: 70px;
|
||||
height: 100%;
|
||||
background: var(--next-bg-columnsMenuBar);
|
||||
ul {
|
||||
position: relative;
|
||||
li {
|
||||
color: var(--next-bg-columnsMenuBarColor);
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
.columns-vertical {
|
||||
margin: auto;
|
||||
.columns-vertical-title {
|
||||
padding-top: 1px;
|
||||
}
|
||||
}
|
||||
.columns-horizontal {
|
||||
display: flex;
|
||||
height: 50px;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
padding: 0 5px;
|
||||
i {
|
||||
margin-right: 3px;
|
||||
}
|
||||
a {
|
||||
display: flex;
|
||||
.columns-horizontal-title {
|
||||
padding-top: 1px;
|
||||
}
|
||||
}
|
||||
}
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: var(--next-bg-columnsMenuBarColor);
|
||||
}
|
||||
}
|
||||
.layout-columns-active {
|
||||
color: var(--el-color-white);
|
||||
transition: 0.3s ease-in-out;
|
||||
}
|
||||
.layout-columns-hover {
|
||||
color: var(--el-color-primary);
|
||||
a {
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
}
|
||||
.columns-round {
|
||||
background: var(--el-color-primary);
|
||||
color: var(--el-color-white);
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 2px;
|
||||
height: 44px;
|
||||
width: 65px;
|
||||
transform: translateX(-50%);
|
||||
z-index: 0;
|
||||
transition: 0.3s ease-in-out;
|
||||
border-radius: 5px;
|
||||
}
|
||||
.columns-card {
|
||||
@extend .columns-round;
|
||||
top: 0;
|
||||
height: 50px;
|
||||
width: 100%;
|
||||
border-radius: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,32 @@
|
||||
<template>
|
||||
<el-header class="layout-header" :height="setHeaderHeight" v-show="!isTagsViewCurrenFull">
|
||||
<NavBarsIndex />
|
||||
</el-header>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent } from 'vue';
|
||||
import { useStore } from '/@/store/index';
|
||||
import NavBarsIndex from '/@/layout/navBars/index.vue';
|
||||
export default defineComponent({
|
||||
name: 'layoutHeader',
|
||||
components: { NavBarsIndex },
|
||||
setup() {
|
||||
const store = useStore();
|
||||
// 设置 header 的高度
|
||||
const setHeaderHeight = computed(() => {
|
||||
let { isTagsview, layout } = store.state.themeConfig.themeConfig;
|
||||
if (isTagsview && layout !== 'classic') return '84px';
|
||||
else return '50px';
|
||||
});
|
||||
// 获取卡片全屏信息
|
||||
const isTagsViewCurrenFull = computed(() => {
|
||||
return store.state.tagsViewRoutes.isTagsViewCurrenFull;
|
||||
});
|
||||
return {
|
||||
setHeaderHeight,
|
||||
isTagsViewCurrenFull,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,84 @@
|
||||
<template>
|
||||
<el-main class="layout-main">
|
||||
<el-scrollbar
|
||||
class="layout-scrollbar"
|
||||
ref="layoutScrollbarRef"
|
||||
:style="{ padding: currentRouteMeta.isLink && currentRouteMeta.isIframe ? 0 : '', transition: 'padding 0.3s ease-in-out' }"
|
||||
>
|
||||
<LayoutParentView :style="{ minHeight: `calc(100vh - ${headerHeight})` }" />
|
||||
<Footer v-if="getThemeConfig.isFooter" />
|
||||
</el-scrollbar>
|
||||
</el-main>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, toRefs, reactive, getCurrentInstance, watch, onMounted } from 'vue';
|
||||
import { useStore } from '/@/store/index';
|
||||
import { useRoute } from 'vue-router';
|
||||
import LayoutParentView from '/@/layout/routerView/parent.vue';
|
||||
import Footer from '/@/layout/footer/index.vue';
|
||||
|
||||
// 定义接口来定义对象的类型
|
||||
interface MainState {
|
||||
headerHeight: string | number;
|
||||
currentRouteMeta: any;
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
name: 'layoutMain',
|
||||
components: { LayoutParentView, Footer },
|
||||
setup() {
|
||||
const { proxy } = <any>getCurrentInstance();
|
||||
const route = useRoute();
|
||||
const store = useStore();
|
||||
const state = reactive<MainState>({
|
||||
headerHeight: '',
|
||||
currentRouteMeta: {},
|
||||
});
|
||||
// 获取布局配置信息
|
||||
const getThemeConfig = computed(() => {
|
||||
return store.state.themeConfig.themeConfig;
|
||||
});
|
||||
// 设置 main 的高度
|
||||
const initHeaderHeight = () => {
|
||||
const bool = state.currentRouteMeta.isLink && state.currentRouteMeta.isIframe;
|
||||
let { isTagsview } = store.state.themeConfig.themeConfig;
|
||||
if (isTagsview) return (state.headerHeight = bool ? `85px` : `114px`);
|
||||
else return (state.headerHeight = `51px`);
|
||||
};
|
||||
// 初始化获取当前路由 meta,用于设置 iframes padding
|
||||
const initGetMeta = () => {
|
||||
state.currentRouteMeta = route.meta;
|
||||
};
|
||||
// 页面加载前
|
||||
onMounted(async () => {
|
||||
await initGetMeta();
|
||||
initHeaderHeight();
|
||||
});
|
||||
// 监听路由变化
|
||||
watch(
|
||||
() => route.path,
|
||||
() => {
|
||||
state.currentRouteMeta = route.meta;
|
||||
const bool = state.currentRouteMeta.isLink && state.currentRouteMeta.isIframe;
|
||||
state.headerHeight = bool ? `85px` : `114px`;
|
||||
proxy.$refs.layoutScrollbarRef.update();
|
||||
}
|
||||
);
|
||||
// 监听 themeConfig 配置文件的变化,更新菜单 el-scrollbar 的高度
|
||||
watch(store.state.themeConfig.themeConfig, (val) => {
|
||||
state.currentRouteMeta = route.meta;
|
||||
const bool = state.currentRouteMeta.isLink && state.currentRouteMeta.isIframe;
|
||||
state.headerHeight = val.isTagsview ? (bool ? `85px` : `114px`) : '51px';
|
||||
if (val.isFixedHeaderChange !== val.isFixedHeader) {
|
||||
if (!proxy.$refs.layoutScrollbarRef) return false;
|
||||
proxy.$refs.layoutScrollbarRef.update();
|
||||
}
|
||||
});
|
||||
return {
|
||||
getThemeConfig,
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user