老版本归档与清理

This commit is contained in:
vben
2025-11-15 23:42:42 +08:00
parent f68d2afa28
commit 5949827664
448 changed files with 0 additions and 61618 deletions
-12
View File
@@ -1,12 +0,0 @@
# port 端口号
VITE_PORT = 868
# open 运行 npm run dev 时自动打开浏览器
VITE_OPEN = false
# 打包是否开启 cdn(源文件 utils/build.ts),可自行修改
VITE_OPEN_CDN = false
# public path 配置线上环境路径(打包)、本地通过 http-server 访问时,请置空即可
#VITE_PUBLIC_PATH = /vue/
VITE_PUBLIC_PATH = /
-9
View File
@@ -1,9 +0,0 @@
# 本地环境
ENV = development
# 本地环境接口地址
VITE_API_URL = 'http://localhost:5000/'
#VITE_API_URL = 'http://localhost:9530/'
VITE_UI_URL = 'http://localhost:868/'
-10
View File
@@ -1,10 +0,0 @@
# 线上环境
ENV = production
# 线上环境接口地址
#VITE_API_URL = https://lyt-top.gitee.io/vue-next-admin-preview/
VITE_API_URL = 'http://localhost:5000/'
-18
View File
@@ -1,18 +0,0 @@
*.sh
node_modules
lib
*.md
*.scss
*.woff
*.ttf
.vscode
.idea
dist
mock
public
bin
build
config
index.html
src/assets
-76
View File
@@ -1,76 +0,0 @@
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'],
overrides: [
{
files: ['*.ts', '*.tsx', '*.vue'],
rules: {
'no-undef': 'off',
},
},
],
rules: {
// http://eslint.cn/docs/rules/
// https://eslint.vuejs.org/rules/
// https://typescript-eslint.io/rules/no-unused-vars/
'@typescript-eslint/ban-ts-ignore': 'off',
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-var-requires': 'off',
'@typescript-eslint/no-empty-function': 'off',
'@typescript-eslint/no-use-before-define': 'off',
'@typescript-eslint/ban-ts-comment': 'off',
'@typescript-eslint/ban-types': 'off',
'@typescript-eslint/no-non-null-assertion': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/no-redeclare': 'error',
'@typescript-eslint/no-non-null-asserted-optional-chain': 'off',
'@typescript-eslint/no-unused-vars': [2],
'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',
'no-redeclare': 'off',
},
};
-39
View File
@@ -1,39 +0,0 @@
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',
};
-490
View File
@@ -1,490 +0,0 @@
# <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.4.33
`2023.04.11`
- 🌟 更新 依赖更新最新版本
- 🎉 新增 `/make/tableDemo` 中添加打印、图片预览功能
- 🐞 修复 菜单收起时(isCollapse),图标不居中问题
- 🐞 修复 演示 `权限管理 -> 前端控制 -> 页面权限` 切换不生效,感谢群友@傲世盛唐
- 🐞 修复 `"typescript": "5.x"` 中 `tsconfig.json`,`compilerOptions.suppressImplicitAnyIndexErrors` 弃用问题 [TypeScript/issues/51909](https://github.com/microsoft/TypeScript/issues/51909)、[suppressImplicitAnyIndexErrors](https://www.typescriptlang.org/tsconfig#suppressImplicitAnyIndexErrors)
- 🎨 合并 [!47cdn 打包支持 pnpm,消除无 external 的报错](https://gitee.com/lyt-top/vue-next-admin/commit/8de54a844bb54468d0bdccca158bf9bcb449f270),感谢[@yujiacheng](https://gitee.com/YujiaCheng1996)
- 🎯 优化 `layout/navBars/breadcrumb` 文件夹名称改成 `layout/navBars/topBar` 更易理解(`可全局替换`),感谢群友@傲世盛唐
- 🎯 优化 `layout/navBars/topBar/user.vue` 组件,`UserNews` 点击消息图标触发范围,改用 [element plus Popover 气泡卡片 虚拟触发方式](https://element-plus.org/zh-CN/component/popover.html#%E8%99%9A%E6%8B%9F%E8%A7%A6%E5%8F%91),防止点击消息通知背景色时不触发 `Popover` 弹出框
## 2.4.32
💔💔💔 图片不显示问题(README.md、演示中使用的图片,[vue-next-admin-images](https://gitee.com/lyt-top/vue-next-admin-images)),通过网站 [https://www.hd-r.cn/](https://www.hd-r.cn/) 转在线链接,如若侵权请联系作者 qq:1105290566
`2023.03.26`
- 🌟 更新 依赖更新最新版本
- 🐞 修复 [关于开发环境 sourceMap 的问题](https://gitee.com/lyt-top/vue-next-admin/issues/I6DNDQ),感谢[@XiaoSongJiang](https://gitee.com/XiaoSongJiang)
- 🐞 修复 打包提示 `[@vue/compiler-sfc] ::v-deep usage as a combinator has been deprecated. Use :deep(<inner-selector>) instead.`,不能使用 `:deep {}`,而应使用 `:deep() {}`
- 🎨 合并 [feat: 一级菜单重定向为空,分栏模式下,点击一次菜单时现在会切换子菜单列表,而不是打开空白页](https://gitee.com/lyt-top/vue-next-admin/commit/a91f84e3a1a86d8d303a5b46171622913d9d0737),感谢[@写意](https://gitee.com/xjj_0906)
- 🎯 优化 经典布局分割菜单只有一项子级时,收起左侧导航菜单
- 🎯 优化 watch 监听范围
- 🎯 优化 打包:分包(manualChunks)、gzip 压缩、cdn 加速 `默认关闭 .env 中开启`(可查看文章[vue-next-admin vue3 + vite 打包 gzip 压缩、cdn 加速](https://blog.csdn.net/qq_34450741/article/details/129766676))
## 2.4.31
`2023.03.10`
- 🌟 更新 依赖更新最新版本
- 🐞 修复 顶栏背景渐变设置不生效
- 🐞 修复 顶栏背景渐变、菜单背景渐变时,深色主题不生效
- 🐞 修复 顶栏搜索框移动端显示问题
- 🎯 优化 `main.ts`,相关 issues [#I6KNFH](https://gitee.com/lyt-top/vue-next-admin/issues/I6KNFH)、[#I6JRH6](https://gitee.com/lyt-top/vue-next-admin/issues/I6JRH6)
- 🎯 优化 菜单横向模式显示(horizontal)
- 🎯 优化 分栏布局,[希望分栏布局做一下优化,在没有二级菜单的时候,直接全屏展示一级菜单链接](https://gitee.com/lyt-top/vue-next-admin/issues/I6HW7H),感谢[@jiuping](https://gitee.com/jiuping),`tagsview` 点击时处理 `收起/展开` 菜单
## 2.4.3
`2023.02.22`
🚩🚩🚩 感谢 [驰骋工作流引擎-表单引擎-低代码开发平台](http://www.ccflow.org/) 赞助商的赞助。驰骋公司为社会提供流程引擎+表单引擎+低代码开发平台一体的开源软件解决方案,欢迎广大开发者前去体验!
- 🌟 更新 依赖更新最新版本
- 🎉 新增 赞助商组件(`/src/layout/sponsors`),[项目目录结构查看](https://lyt-top.gitee.io/vue-next-admin-doc-preview/config/)
- 🐞 修复 [过滤筛选组件展开点击不了](https://gitee.com/lyt-top/vue-next-admin/issues/I688WG)
- 🐞 修复 [设置锁屏时间时直接白屏了不能恢复,除非删除主题配置才会重新加载](https://gitee.com/lyt-top/vue-next-admin/issues/I6AF8P),感谢[@baizunxian](https://gitee.com/xb_xiaobai)
- 🐞 修复 `分栏布局` 地址栏输入不存在的路由报错问题
- 🎨 合并 [!44 tagsViewName 正则匹配错误,匹配到含 en 单词](https://gitee.com/lyt-top/vue-next-admin/pulls/44/files),感谢[@tony 星](https://gitee.com/tony_tong_xin)
- 🎨 合并 [!45 fix 地址栏出现 false 问题](https://gitee.com/lyt-top/vue-next-admin/pulls/45),感谢[@随心](https://gitee.com/jiangqiang1996)
- 🎯 优化 `/src/utils/storage` 下 `key` 编写成 `${__NEXT_NAME__}:${key}`,防止部署多套系统到同一域名不同目录时,变量共用的问题(`__NEXT_NAME__`为 `package.json` 中的 `name`)
- 🎯 优化 watermark 单词拼写错误
## 2.4.21
`2022.12.12`
- 🌟 更新 依赖更新最新版本
- 🎉 新增 菜单背景高亮颜色可自定义,通过 `布局配置 -> 菜单设置 -> 菜单高亮背景色` 进行设置
- 🐞 修复 `分栏布局` 二级导航菜单内容多时,无法滚动问题,感谢群友@静雨轩主人
- 🐞 修复 [!42 修复 工作流无法添加新节点问题](https://gitee.com/lyt-top/vue-next-admin/pulls/42),感谢[@beta](https://gitee.com/beta_dz)
- 🎯 优化 `/make/tableDemo` 表头很多时,无法滚动问题,感谢群友@糊涂涂涂
## 2.4.2
`2022.12.09`
- 🌟 更新 依赖更新最新版本
- 🎉 新增 国际化自动导入文件功能,只需在 `/src/i18n/pages` 下新建文件夹定义即可
- 🎉 新增 `/make/tableDemo` 中 [搜索框展开,收缩功能,高级筛选组件 有计划做吗](https://gitee.com/lyt-top/vue-next-admin/issues/I6511L)
- 🐞 修复 [!40 开启 TagsView 缓存后,刷新后所有的路由都变成组件缓存了](https://gitee.com/lyt-top/vue-next-admin/pulls/40),感谢[@mrjimin](https://gitee.com/mrjimin)
- 🐞 修复 [!41 修复 get 请求传递嵌套对象或数组时无法正常编码问题](https://gitee.com/lyt-top/vue-next-admin/pulls/41),感谢[@随心](https://gitee.com/jiangqiang1996)
- 🐞 修复 组件 wangEditor 回显值的问题
- 🐞 修复 `/fun/echartsMap`(地理坐标/地图)、`visualizingDemo2`(数据可视化演示 2) 演示报错问题
- 🎯 优化 版本升级提示
- 🎯 优化 无权限登录时增加提示信息,[BUG:因前端加载路由(initFrontEndControlRoutes)中当前用户角色为一个陌生角色, 导致 router.beforeEach 会死循环 浏览器崩溃](https://gitee.com/lyt-top/vue-next-admin/issues/I64HVO),感谢[@canroc](https://gitee.com/canroc)、[@随心](https://gitee.com/jiangqiang1996)
- 🌈 重构 `/views/system` 新增修改组件合并。[可以把新增修改组件合并成一个吧](https://gitee.com/lyt-top/vue-next-admin/issues/I64WES)
- 🌈 重构 图标选择器,[图标选择器没办法筛选,只能筛选 ali 的](https://gitee.com/lyt-top/vue-next-admin/issues/I64HZD),感谢[@随心](https://gitee.com/jiangqiang1996)
## 2.4.1
`2022.11.30`
- 🎉 新增 版本升级提示
- 🐞 修复 [先打开 F12 再登录进去,然后改变浏览器大小 js 报错](https://gitee.com/lyt-top/vue-next-admin/issues/I63ZZT),感谢[@Quber](https://gitee.com/quber)
## 2.4.0
`2022.11.29`
⚡⚡⚡ 此版为破坏性更新,应群友建议 `script lang="ts"` 改 `script lang="ts" setup 语法糖`。
- 🌟 更新 依赖更新最新版本
- 🎉 新增 表格封装演示,路径:`组件封装 -> 表格封装演示`
- 🎉 新增 master 分支 script lang="ts" 改成 script lang="ts" setup 语法糖,将同步基础分支
- 🐞 修复 [v2.3.0 版本报错问题处理](https://gitee.com/lyt-top/vue-next-admin/issues/I623RP)
- 🐞 修复 [el-backtop 滚动高度不触发(固定了 header)](https://gitee.com/lyt-top/vue-next-admin/issues/I63N0D),感谢[@dejavuuuuu](https://gitee.com/zc19951010)
- 🎯 优化 完善 ts 类型,删除根目录 `plugins.d.ts、shim.d.ts、source.d.ts`,移入到 `/src/types/global.d.ts`
- 🎯 优化 代码 `watch` 移动到 `生命周期钩子` 最后,文字注释等
## 2.3.0
`2022.11.16`
- 🌟 更新 依赖更新最新版本
- 🎉 新增 新版登录页
- 🎉 新增 tagsview 鼠标中键 `关闭当前 tagsview`
- 🎉 新增 `分栏菜单鼠标悬停预加载`。[分栏模式如何去掉鼠标悬浮父级菜单,分栏菜单自动加载的功能啊](https://gitee.com/lyt-top/vue-next-admin/issues/I5RUY7)。操作路径:`布局配置 -> 分栏设置`
- 🐞 修复 [vue-i18n](https://vue-i18n.intlify.dev/api/general.html#createi18n) 报错,[!39 修复 i18n 兼容性问题](https://toscode.gitee.com/lyt-top/vue-next-admin/pulls/39/files),感谢[@随心](https://toscode.gitee.com/jiangqiang1996)
- 🐞 修复 顶栏搜索功能点击蒙蔽弹窗不关闭
- 🐞 修复 [!38 fix: bug refreshRouterViewKey 值为 null 导致路由缓存第一次无效](https://toscode.gitee.com/lyt-top/vue-next-admin/pulls/38/files),感谢[@P)](https://toscode.gitee.com/foxp8y)
- 🐞 修复 `路由参数 -> 普通路由/动态路由` 国际化演示时,`tagsView` 和 `浏览器标题` 显示异常。[演示中:路由参数界面 -> 动态路由,国际化显示时面包屑、浏览器标题有 bug](https://gitee.com/lyt-top/vue-next-admin/issues/I5JRJG)
- 🐞 修复 `路由参数 -> 普通路由/动态路由` 动态设置 `tagsViewName` 时,`tagsView 右键菜单刷新` 功能失效(也就是路由后面有参数时,query、params)。[普通或动态路由新建页面后点击 tagview 刷新无效](https://gitee.com/lyt-top/vue-next-admin/issues/I5K3YO),感谢[@dejavuuuuu](https://gitee.com/zc19951010)
- 🐞 修复 [表单(el-form)中,字体图标偏移问题](https://gitee.com/lyt-top/vue-next-admin/issues/I5K1PM)
- 🐞 修复 路由 `router.addRoute` 时,一直提示 `No match found for location with path 'xxx'`
- 🎯 优化 全局 `getCurrentInstance` 替换成 [`provide/inject`](https://cn.vuejs.org/api/application.html#app-provide) 或通过 `ref` 处理
- 🎯 优化 引入组件方式 `(import xxx from xxx)` 改成 `defineAsyncComponent(() => import(xxx))`
- 🎯 优化 页面高度 100% 问题,重写布局配置 `界面设置 -> 固定 Header` 多余的 `el-scrollbar` 逻辑、重写各界面需 `计算属性 computed` 设置动态高度问题(改为 css `flex` 设置自适应高度,具体查看文档:[设置可视区高度 100%](https://lyt-top.gitee.io/vue-next-admin-doc-preview/config/otherIssues/#%E8%AE%BE%E7%BD%AE%E5%8F%AF%E8%A7%86%E5%8C%BA%E9%AB%98%E5%BA%A6-100)。[!31 修复页面样式无法通过百分比设置的问题](https://toscode.gitee.com/lyt-top/vue-next-admin/pulls/31),感谢[@LostDeer](https://toscode.gitee.com/lyt-top/vue-next-admin/pulls/31/files)。`(改动较大,删除多余代码)`
- 🎯 优化 [wangeditor](https://www.wangeditor.com/) 组件,`@wangeditor/editor-for-vue`。可自行修改,组件位置:`/src/components/editor`。相关 Issues:[wangeditor 编辑器多个菜单不能回弹](https://gitee.com/lyt-top/vue-next-admin/issues/I5M5H7)
- 🌈 重构 外链、内嵌 iframe 逻辑 + 美化,iframe 支持缓存
## 2.2.0
`2022.07.10`
⚡⚡⚡ [/sec/stores/userInfo.ts](https://gitee.com/lyt-top/vue-next-admin/blob/master/src/stores/userInfo.ts) 下添加了 `getApiUserInfo` 接口模拟数据 `setTimeout` 为 3 秒
- 🌟 更新 依赖更新最新版本
- 🐞 修复 [主界面重新授权按钮点击卡死不跳转登录界面#I5C3JS](https://gitee.com/lyt-top/vue-next-admin/issues/I5C3JS),感谢[@Hero-Typ](https://gitee.com/tian_yu_peng)
- 🐞 修复 编译警告[#I5CVSB](https://gitee.com/lyt-top/vue-next-admin/issues/I5CVSB),全局替换成 `:deep(attr)`,感谢[@Linvas](https://gitee.com/linvas)。参考文档:[vue3 sfc-style](https://v3.cn.vuejs.org/api/sfc-style.html#style-scoped)。`node_modules\print-js\dist\print.js` 需 `print-js` 作者适配或去除 `package.json` 中的 `"print-js": "^1.6.0"`
- 🐞 修复 [vue-next-admin-template-js 版本前端控制路由:userInfo.js 请求用户信息接口报错,加载不到路由 可以写个定时器模拟一下接口 一样的报错#I5F1HP](https://gitee.com/lyt-top/vue-next-admin/issues/I5F1HP),感谢[@白开水](https://gitee.com/libin951223)
## 2.1.1
`2022.05.27`
- 🌟 更新 依赖更新最新版本
- 🎯 优化 深色模式下,`<el-button text></el-button>` 时,`:active` 样式
- 🎯 优化 [页面缓存在刷新之后失效 #I58U75](https://gitee.com/lyt-top/vue-next-admin/issues/I58U75)),感谢[@ls0428](https://gitee.com/ls0428)
- 🎯 优化 [SvgIcon 对下载的 Svg 图像设置颜色无效 #I59ND0](https://gitee.com/lyt-top/vue-next-admin/issues/I59ND0)),感谢[@elus_z](https://gitee.com/elus_z)
- 🎯 优化 `/src/utils/toolsValidate.ts` 工具类
- 🐞 修复 [布局切换,TagsView 显示的 tab 会多一个出来 #I58WGM](https://gitee.com/lyt-top/vue-next-admin/issues/I58WGM),感谢[@lg_boy](https://gitee.com/lg_boy)
- 🐞 修复 [如果设置顶部面包屑导航开启图标 isBreadcrumbIcon=true 后,样式有点问题 如果不开启就是正常的 #I58VB8](https://gitee.com/lyt-top/vue-next-admin/issues/I58VB8)
- 🐞 修复 地址栏路由地址输入错误时,返回首页后,再次输入路由地址错误时,不跳转 404 问题
- 🐞 修复 [2.1.0 版本的图标选择组件多次点击后功能失效 #I590TH](https://gitee.com/lyt-top/vue-next-admin/issues/I590TH),感谢[@quber](https://gitee.com/quber)
## 2.1.0
`2022.04.18`
⚡⚡⚡ 此版本为破环性更新,优化内容如下:(谨慎更新!谨慎更新!!谨慎更新!!!)。因为 `vuex` 替换成 `pinia`
- 🌟 更新 依赖更新最新版本
- 🎯 优化 部分界面图片不显示问题(更换 gitee 在线图片地址源)
- 🎯 优化 各界面方法引入与逻辑之间添加一行空行,方便区分内容
- 🎯 优化 图标选择器 [#I4YAHB](https://gitee.com/lyt-top/vue-next-admin/issues/I4YAHB),感谢[@真有你的](https://gitee.com/sunliusen)
- 🎯 优化 图标选择器 icon type 类型为 all 时,类型 ali、ele、awe 回显问题
- 🎯 优化 去掉开发环境 i18n 控制台警告,页面代码:[i18n/index.ts](https://gitee.com/lyt-top/vue-next-admin/blob/master/src/i18n/index.ts)
- 🎯 优化 `NextLoading.start()` 方法,防止第一次进入界面时出现短暂空白
- 🎯 优化 地址栏有参数退出登录,再次登录不跳之前界面问题 `src/layout/navBars/topBar/user.vue`
- 🎯 优化 `SvgIcon` 组件,防止 `开启 Tagsview 图标` 时,`tagsView 右键菜单关闭` 报错问题,工作流不可连线、全屏时关闭按钮消失问题
- 🎯 优化 [如果 url 中有中文等特殊字符,第一次切换该 tab 时 keep-alive 失效#I55JS7](https://gitee.com/lyt-top/vue-next-admin/issues/I55JS7),感谢[yuyong1566](https://gitee.com/yuyong1566)
- 🎯 优化 [wangEditor](https://www.wangeditor.com/) 更新到 v5,[vue3 版本线上示例中 wangeditor 富文本编辑器 demo 实例,无法换行#I5565B](https://gitee.com/lyt-top/vue-next-admin/issues/I5565B),感谢@[jenchih](https://gitee.com/jenchih)
- 🎯 优化 [在关闭 tagview 时,高度刷新时会会变化,出现滚动条](https://gitee.com/lyt-top/vue-next-admin/issues/I55FHM),感谢[张松](https://gitee.com/zs310071113)
- 🎯 优化 [路由参数](https://lyt-top.gitee.io/vue-next-admin-preview/#/params/common)演示
- 🎉 新增 [vuex](https://vuex.vuejs.org/) 替换成 [pinia](https://pinia.vuejs.org/getting-started.html)
- 🎉 新增 tagsView 支持自定义 tagsView 名称(文章详情时有用),前往体验:[路由参数/普通路由](https://lyt-top.gitee.io/vue-next-admin-preview/#/params/common)。新增 tagsView 支持自定义名称国际化,感谢[@q7but](https://gitee.com/q7but)、[!22 add 添加自定义 tagVIewName 拓展,支持国际化](https://gitee.com/lyt-top/vue-next-admin/pulls/22/files)、感谢[@tony_tong_xin](https://gitee.com/tony_tong_xin)
- 🐞 修复 适配 `"element-plus": "^2.1.9",2.2.0` 版本
- 🐞 修复 [导航栏横向布局后,一级菜单显示问题#I4Z3M3](https://gitee.com/lyt-top/vue-next-admin/issues/I4Z3M3)
- 🐞 修复 横向布局三级及以上导航菜单高亮、导航高度不统一问题
- 🐞 修复 分栏模式下,选中的菜单是 primary 样式,鼠标移入字也变成 primary 色了,感谢群友@孤夜-流殇
- 🐞 修复 [vuex 里面改了颜色 但是不生效 #I4WFMA](https://gitee.com/lyt-top/vue-next-admin/issues/I4WFMA)
- 🐞 修复 全局主题 primary 清空颜色后报错,[#I4X0LG](https://gitee.com/lyt-top/vue-next-admin/issues/I4X0LG),感谢[面向 BUG 编程](https://gitee.com/fhtfy)
- 🐞 修复 [.eslintrc.js 文件 rules 标签名错误 #I53IPK](https://gitee.com/lyt-top/vue-next-admin/issues/I53IPK),感谢[yuyong1566](https://gitee.com/yuyong1566)
- 🐞 修复 `开启 Tagsview 图标` 时,`tagsView 右键菜单关闭` 报错问题
- 🐞 修复 `router.push` 路径找不到时报错问题,`404、401 界面` 已移入到 `main` 主布局里(之前全屏)
- 🐞 修复 [全局修改组件大小失效了](https://gitee.com/lyt-top/vue-next-admin/issues/I551RP),感谢[lg_boy](https://gitee.com/lg_boy)
- 🐞 修复 [修改一下配置时,需要每次都清理 `window.localStorage` 浏览器永久缓存,配置才会生效,问题解决#I567R1](https://gitee.com/lyt-top/vue-next-admin/issues/I567R1),感谢[@lanbao123](https://gitee.com/lanbao123)
- 🐞 修复 [标记为需要缓存的 tab 页后,再次从左侧菜单打开,还是显示被缓存的页面内容#I4UY3G](https://gitee.com/lyt-top/vue-next-admin/issues/I4UY3G),感谢@axcc1234、特别感谢群友@华仔
- 🌈 重构 路由(`/src/router/index.ts`)解决 No match found for location with path "xxx"(前端控制,后端控制未解决) 问题
## 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` 左侧菜单数据不变问题
-21
View File
@@ -1,21 +0,0 @@
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.
-49
View File
@@ -1,49 +0,0 @@
<div align="center"><h1 align="center">vben-ui</h1></div>
<div align="center"><h3 align="center">一个开箱即用的快速开发平台</h3></div>
# 🍿 相关地址
* PC端体验地址:[http://8.153.168.178/](http://8.153.168.178/)(演示版本采用最新vben5)
* 移动端体验地址:[http://8.153.168.178/unibest/](http://8.153.168.178/unibest/)
* 文档地址 :[http://8.153.168.178/doc.html](http://8.153.168.178/doc.html)
* QQ交流群:[795417789](https://jq.qq.com/?_wv=1027&k=yoKKIlIG)
### 🍟 概述
* 特别说明:vben系列项目是由https://gitee.com/zsvg 大哥原vboot项目转移的。
* 特别说明:目前前端是基于vue-next-admin的,vben5 ui目前正在适配中。
* 基于JAVA/.NET实现的快速开发平台。模块化插件式开发,前后端分离,开箱即用。
* JAVA后端基于SpringBoot框架,数据库访问同时集成了Jpa与MyBatis-Plus,即可自动生成数据库表结构,又可灵活编写sql。
* .NET后端基于Furion框架,数据库访问使用Sqlsugar,codeFirst方式。
* 前端基于vue-next-admin/vben,引入了bpmn.js工作流、VForm可视化表单。
### 🍖 详细功能
1. 主控面板:控制台页面,可进行工作台,分析页,统计等功能的展示。
2. 部门管理:部门维护,支持多层级结构的树形结构。
3. 用户管理:用户维护,可设置用户部门,岗位,群组,职务,角色,数据权限等。
4. 岗位管理:岗位维护,岗位可作为用户的一个标签,岗位也可与权限等其他功能挂钩。
5. 群组管理:群组维护,群组可设置部门,用户,岗位,用于更广泛的权限设置。
6. 菜单管理:菜单目录,菜单,和按钮的维护是权限控制的基本单位。
7. 角色管理:角色绑定菜单后,可限制相关角色的人员登录系统的功能范围。
8. 字典管理:系统内各种枚举类型的维护。
9. 访问日志:用户的登录和退出日志的查看和管理。
10. 操作日志:用户的操作业务的日志的查看和管理。
11. 定时任务:定时任务的维护,通过cron表达式控制任务的执行频率。
12. 流程引擎:流程图展示,自解析bpmn.js,支持驳回、转办、废弃,跳转等功能
13. 在线表单:在线表单设计,配合流程可以实现表单数据流转
14. 消息机制:待办待阅功能,联通钉钉与企业微信接口
15. 代码生成:在线配置,一键生成前后端代码
### ⚡ 近期计划
- [ ] 完善文档
- [ ] 完善工作流
### 💐 特别鸣谢
- 👉 eladmin:[https://gitee.com/elunez/eladmin](https://gitee.com/elunez/eladmin)
- 👉 ruoyi:[https://gitee.com/y_project/RuoYi-Vue](https://gitee.com/y_project/RuoYi-Vue)
- 👉 Vben-Admin:[https://vvbin.cn/doc-next](https://vvbin.cn/doc-next)
- 👉 vue-next-admin:[https://gitee.com/lyt-top/vue-next-admin](https://gitee.com/lyt-top/vue-next-admin)
- 👉 vxe-table:[https://gitee.com/xuliangzhan_admin/vxe-table](https://gitee.com/xuliangzhan_admin/vxe-table)
- 👉 VForm:[https://www.vform666.com](https://www.vform666.com/)
```
如果对您有帮助,点击右上角⭐Star⭐关注 ,感谢支持开源!
```
-33
View File
@@ -1,33 +0,0 @@
<!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=""
/>
<link rel="icon" href="/favicon.ico" />
<title>Vben快速开发平台</title>
<!-- <title>vue-next-admin</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>
-432
View File
@@ -1,432 +0,0 @@
html {
font-size: 12px;
}
.amap-copyright{
box-sizing: content-box
}
* {
box-sizing: border-box
}
.input-textarea{
color:grey;
height:8em;
overflow:auto;
border-radius:0.4rem;
border:1px solid #ced4da;
margin-bottom:1rem;
}
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
line-height: 1.5;
font-weight: 300;
color: #111213;
}
hr {
margin: 0.5rem 0;
box-sizing: content-box;
height: 0;
overflow: visible;
border: 0;
border-top: 1px solid rgba(0, 0, 0, .1)
}
p {
margin-top: 0;
margin-bottom: 0;
}
label {
display: inline-block;
margin-bottom: 0.4rem;
}
label, .btn {
margin-left: 0;
font-size: 1rem;
}
button, input, select {
margin: 0;
font-family: inherit;
font-size: inherit;
line-height: inherit;
overflow: visible;
text-transform: none
}
[type=button]::-moz-focus-inner, [type=reset]::-moz-focus-inner, [type=submit]::-moz-focus-inner, button::-moz-focus-inner {
padding: 0;
border-style: none;
}
input[type=checkbox], input[type=radio] {
box-sizing: border-box;
padding: 0;
-webkit-box-sizing: border-box;
box-sizing: border-box;
padding: 0;
margin: 0 0.5rem 0 0;
}
h4 {
font-family: inherit;
line-height: 1.8;
font-weight: 300;
color: inherit;
font-size: 1.1rem;
margin-top: 0;
margin-bottom: .5rem
}
.btn {
display: inline-block;
font-weight: 400;
text-align: center;
white-space: nowrap;
vertical-align: middle;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
border: 1px solid transparent;
transition: color .15s ease-in-out, background-color .15s ease-in-out, border-color .15s ease-in-out, box-shadow .15s ease-in-out;
background-color: transparent;
background-image: none;
color: #25A5F7;
border-color: #25A5F7;
padding: .25rem .5rem;
line-height: 1.5;
border-radius: 1rem;
-webkit-appearance: button;
cursor:pointer;
}
.btn:hover {
color: #fff;
background-color: #25A5F7;
border-color: #25A5F7
}
.btn:hover {
text-decoration: none
}
.input-item {
position: relative;
display: -ms-flexbox;
display: flex;
-ms-flex-wrap: wrap;
flex-wrap: wrap;
-ms-flex-align: center;
align-items: center;
width: 100%;
height: 3rem;
}
.input-item:last-child {
margin-bottom: 0;
}
.input-item>select, .input-item>input[type=text], .input-item>input[type=date] {
position: relative;
-ms-flex: 1 1 auto;
flex: 1 1 auto;
width: 1%;
margin-bottom: 0;
}
.input-item>select:not(:last-child), .input-item>input[type=text]:not(:last-child), .input-item>input[type=date]:not(:last-child) {
border-top-right-radius: 0;
border-bottom-right-radius: 0
}
.input-item>select:not(:first-child), .input-item>input[type=text]:not(:first-child), .input-item>input[type=date]:not(:first-child) {
border-top-left-radius: 0;
border-bottom-left-radius: 0
}
.input-item-prepend {
margin-right: -1px;
}
.input-item-text, input[type=text],input[type=date], select {
height: calc(2.2rem + 2px);
}
.input-item-text {
width: 6rem;
text-align: justify;
padding: 0.4rem 0.7rem;
display: inline-block;
text-justify: distribute-all-lines;
/*ie6-8*/
text-align-last: justify;
/* ie9*/
-moz-text-align-last: justify;
/*ff*/
-webkit-text-align-last: justify;
/*chrome 20+*/
-ms-flex-align: center;
align-items: center;
margin-bottom: 0;
font-size: 1rem;
font-weight: 400;
line-height: 1.5;
color: #495057;
text-align: center;
white-space: nowrap;
background-color: #e9ecef;
border: 1px solid #ced4da;
border-radius: .25rem;
border-bottom-right-radius: 0;
border-top-right-radius: 0;
}
.input-item-text input[type=checkbox], .input-item-text input[type=radio] {
margin-top: 0
}
.input-card {
display: flex;
flex-direction: column;
min-width: 0;
word-wrap: break-word;
background-color: #fff;
background-clip: border-box;
border-radius: .25rem;
width: 28rem;
border-width: 0;
border-radius: 0.4rem;
box-shadow: 0 2px 6px 0 rgba(114, 124, 245, .5);
position: fixed;
top: 1rem;
right: 1rem;
-ms-flex: 1 1 auto;
flex: 1 1 auto;
padding: 0.75rem 1.25rem;
}
.input-text {
line-height: 2rem;
margin-right: 2rem;
}
.info hr {
margin-right: 0;
margin-left: 0;
border-top-color: grey;
}
.info {
padding: .75rem 1.25rem;
margin-bottom: 1rem;
border-radius: .25rem;
position: fixed;
top: 1rem;
background-color: white;
width: auto;
min-width: 22rem;
border-width: 0;
left: 1rem;
box-shadow: 0 2px 6px 0 rgba(114, 124, 245, .5);
}
.code {
left: 1.5rem;
right: 1.5rem;
top: 1.5rem;
bottom: 1.5rem;
overflow: auto;
margin-bottom: 0rem;
}
.code .btn {
top: 1rem;
position: absolute;
right: 1rem;
}
.code .result {
border: 1px solid rgba(0, 0, 0, 0.1);
border-radius: 0.5rem;
padding: 1rem;
bottom: 1rem;
position: absolute;
top: 5.5rem;
right: 1rem;
left: 1rem;
overflow: auto;
}
.code .status {
color: #80adff;
display: inline-block;
font-size: 14px;
}
.code h4 {
display: inline-block;
max-width: 20rem;
margin-right: 1rem;
margin-bottom: 1rem;
}
select, input[type=text], input[type=date] {
display: inline-block;
width: 100%;
padding: .375rem 1.75rem .375rem .75rem;
line-height: 1.5;
color: #495057;
vertical-align: middle;
background: #fff url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3E%3Cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3E%3C/svg%3E") no-repeat right .75rem center;
background-size: 8px 10px;
border: 1px solid #ced4da;
border-radius: .25rem;
-webkit-appearance: none;
-moz-appearance: none;
appearance: none
}
input[type=text],input[type=date] {
background: #fff;
padding: .375rem .75rem;
}
select:focus, input[type=text]:focus, input[type=date]:focus {
border-color: #80bdff;
outline: 0;
box-shadow: 0 0 0 .1rem rgba(128, 189, 255, .1)
}
.btn:focus {
outline: 0;
box-shadow: none;
}
select:focus::-ms-value, input[type=text]:focus::-ms-value,input[type=date]:focus::-ms-value {
color: #495057;
background-color: #fff
}
/* native toastr */
.native-toast {
position: fixed;
background-color: rgba(50, 50, 50, .8);
border-radius: 33px;
color: white;
left: 50%;
text-align: center;
padding: 6px 12px;
opacity: 0;
z-index: 99999;
transition: transform .25s, opacity .25s, top .25s;
box-sizing: border-box;
}
.native-toast-bottom {
bottom: 50px;
-ms-transform: translateX(-50%) translateY(50px);
transform: translateX(-50%) translateY(50px)
}
.native-toast-bottom.native-toast-shown {
opacity: 1;
-ms-transform: translateX(-50%) translateY(0);
transform: translateX(-50%) translateY(0);
}
.native-toast-bottom.native-toast-edge {
bottom: 0;
}
.native-toast-top {
top: 50px;
-ms-transform: translateX(-50%) translateY(-50px);
transform: translateX(-50%) translateY(-50px)
}
.native-toast-top.native-toast-shown {
opacity: 1;
-ms-transform: translateX(-50%) translateY(0);
transform: translateX(-50%) translateY(0);
}
.native-toast-top.native-toast-edge {
top: 0;
}
.native-toast-center {
top: 0;
-ms-transform: translateX(-50%) translateY(-50px);
transform: translateX(-50%) translateY(-50px)
}
.native-toast-center.native-toast-shown {
opacity: 1;
top: 50%;
-ms-transform: translateX(-50%) translateY(-50%);
transform: translateX(-50%) translateY(-50%);
}
.native-toast-edge {
border-radius: 0;
width: 100%;
text-align: left;
}
@media screen and (min-width: 40rem) {
.native-toast:not(.native-toast-edge) {
max-width: 18rem;
}
}
/*
max-width does not seem to work in small screen?
*/
/*@media screen and (max-width: 768px) {
.native-toast:not(.native-toast-edge) {
max-width: 400px;
}
}
@media screen and (max-width: 468px) {
.native-toast:not(.native-toast-edge) {
max-width: 300px;
}
}*/
/* types */
.native-toast-error {
background-color: #d92727;
color: white;
}
.native-toast-success {
background-color: #62a465;
color: white;
}
.native-toast-warning {
background-color: #fdaf17;
color: white;
}
.native-toast-info {
background-color: #5060ba;
color: white;
}
[class^="native-toast-icon-"] {
vertical-align: middle;
margin-right: 8px
}
[class^="native-toast-icon-"] svg {
width: 16px;
height: 16px;
}
-199
View File
@@ -1,199 +0,0 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
<title>地址选择</title>
<link rel="stylesheet" href="amap.css?v=3"/>
<style type="text/css">
html, body, #container {
height: 100%;
}
html {
overflow-y: hidden;
}
.amap-logo {
display: none;
opacity: 0;
}
.amap-copyright {
opacity: 0;
}
.btn {
width: 10rem;
/*margin-left:6.8rem;*/
}
</style>
</head>
<body>
<div id="container"></div>
<div class="info">
<div class="input-item">
<div class="input-item-prepend">
<span class="input-item-text" style="width:8rem;">请输入关键字</span>
</div>
<input id='tipinput' type="text">
</div>
</div>
<div class="input-card">
<label style='color:grey'>点击地图选择地址,详细地址可手动修改</label>
<div class="input-item">
<div class="input-item-prepend"><span class="input-item-text">经纬度</span></div>
<input id='adcoo' type="text" value='116.39,39.9' disabled style="color: green" placeholder="点击地址自动带出">
</div>
<div class="input-item">
<div class="input-item-prepend"><span class="input-item-text">所在地区</span></div>
<input id='adreg' type="text" disabled style="color: green" placeholder="省市区,通过定位解析">
</div>
<div class="input-item">
<div class="input-item-prepend"><span class="input-item-text">详细地址</span></div>
<input id='addet' type="text" style="color: blue" placeholder="如道路、门牌号、小区、楼栋等">
</div>
<input id='adpro' type="hidden">
<input id='adcit' type="hidden">
<input id='addis' type="hidden">
<input id='adcco' type="hidden">
<input id='addco' type="hidden">
<div class="input-item">
<input id="yesBtn" type="button" class="btn" value="确定" style="border-radius: 0.1rem;margin-left:2rem;">
<input id="noBtn" type="button" class="btn" value="关闭" style="border-radius: 0.1rem;margin-left:2rem;">
</div>
</div>
<script type="text/javascript"
src="https://webapi.amap.com/maps?v=1.4.15&key=43214321fdsafdsafdsa&plugin=AMap.Autocomplete,AMap.Geocoder,AMap.PlaceSearch"></script>
<script type="text/javascript">
// console.log(window.location.href);
// console.log(window.location.href.split("adzoo=")[1]);
// console.log(window.location.href.split("adzoo=")[1].split("&"));
document.getElementById('adcoo').value = window.location.href.split("adcoo=")[1].split("&")[0];
document.getElementById('adreg').value = decodeURIComponent(window.location.href.split("adreg=")[1].split("&")[0]);
document.getElementById('addet').value = decodeURIComponent(window.location.href.split("addet=")[1].split("&")[0]);
var map;
var marker = new AMap.Marker();
if (document.getElementById('adcoo').value) {
var cooArr = document.getElementById('adcoo').value.split(',');
var zoom = window.location.href.split("adzoo=")[1].split("&")[0];
map = new AMap.Map('container', {
resizeEnable: true,
center: cooArr,
zoom: zoom ? zoom : 11
});
marker.setPosition(cooArr);
marker.setMap(map);
} else {
map = new AMap.Map('container', {
resizeEnable: true,
zoom: 11
});
}
//输入提示
var autoOptions = {
input: "tipinput"
};
var auto = new AMap.Autocomplete(autoOptions);
var placeSearch = new AMap.PlaceSearch({
map: map
}); //构造地点查询类
AMap.event.addListener(auto, "select", select);//注册监听,当选中某条记录时会触发
function select(e) {
if (e.poi.location) {
document.getElementById('adcoo').value = e.poi.location.lng + ',' + e.poi.location.lat;
} else {
document.getElementById('adcoo').value = '';
}
placeSearch.setCity(e.poi.adcode);
placeSearch.search(e.poi.name);
regeoCode();
}
var geocoder = new AMap.Geocoder({
city: "010", //城市设为北京,默认:“全国”
radius: 500 //范围,默认:500
});
function regeoCode() {
var adcoo = document.getElementById('adcoo').value.split(',');
map.add(marker);
marker.setPosition(adcoo);
geocoder.getAddress(adcoo, function (status, result) {
if (status === 'complete' && result.regeocode) {
// var address = result.regeocode.formattedAddress;
var addrComp = result.regeocode.addressComponent;
var adreg = addrComp.province + addrComp.city + addrComp.district;
var addet = addrComp.township + addrComp.street + addrComp.streetNumber;
if (addet === '') {
var adarr = result.regeocode.formattedAddress.split(adreg);
if (adarr.length > 1) {
addet = adarr[1];
}
}
document.getElementById('adreg').value = adreg;
document.getElementById('addet').value = addet;
document.getElementById('adpro').value = addrComp.province;
document.getElementById('adcit').value = addrComp.city;
document.getElementById('addis').value = addrComp.district;
document.getElementById('adcco').value = addrComp.citycode;
document.getElementById('addco').value = addrComp.adcode;
// console.log(result);
} else {
log.error('根据经纬度查询地址失败')
}
});
}
map.on('click', function (e) {
document.getElementById('adcoo').value = e.lnglat;
regeoCode();
});
document.getElementById('adcoo').onkeydown = function (e) {
if (e.keyCode === 13) {
regeoCode();
return false;
}
return true;
};
function yesBtnClick() {
var back = {};
back.adcoo = document.getElementById('adcoo').value;
back.adreg = document.getElementById('adreg').value;
back.addet = document.getElementById('addet').value;
back.adpro = document.getElementById('adpro').value;
back.adcit = document.getElementById('adcit').value;
back.addis = document.getElementById('addis').value;
back.adcco = document.getElementById('adcco').value;
back.addco = document.getElementById('addco').value;
back.adzoo = map.getZoom();
if (back.adcoo === '') {
alert("请在地图上定位地址");
} else if (back.addet === '') {
alert("请填写详细地址");
} else {
window.parent.addressChooseBack(back);
}
}
document.getElementById("yesBtn").onclick = yesBtnClick;
function noBtnClick() {
window.parent.addressChooseBack();
}
document.getElementById("noBtn").onclick = noBtnClick;
</script>
</body>
</html>
-144
View File
@@ -1,144 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
<title>点聚合</title>
<link rel="stylesheet" href="https://a.amap.com/jsapi_demos/static/demo-center/css/demo-center.css"/>
<style>
html, body, #container {
height: 100%;
width: 100%;
}
.input-card {
width: 25rem;
}
.input-card .btn {
width: 7rem;
margin-right: .7rem;
}
.input-card .btn:last-child {
margin-right: 0;
}
.amap-logo {
display: none;
opacity: 0;
}
.amap-copyright {
opacity: 0;
}
</style>
</head>
<body>
<div id="container" class="map" tabindex="0"></div>
<div class="input-card">
<h4>聚合点效果切换</h4>
<div class="input-item">
<input type="button" class="btn" value="默认样式" id="add0" onclick='addCluster(0)'/>
<input type="button" class="btn" value="自定义图标" id="add1" onclick='addCluster(1)'/>
<input type="button" class="btn" value="完全自定义" id="add2" onclick='addCluster(2)'/>
</div>
</div>
<script src="//a.amap.com/jsapi_demos/static/china.js"></script>
<script src="https://webapi.amap.com/maps?v=2.0&key=c2c9205ab3668d57d66084daf6f80eb2&plugin=AMap.MarkerCluster"></script>
<script type="text/javascript">
var cluster;
var map = new AMap.Map("container", {
center: [104.937478,35.439575],
zoom: 5
});
var gridSize = 60
// 数据中需包含经纬度信息字段 lnglat
// var points = [
// { lnglat: ["108.939621", "34.343147"] },
// { lnglat: ["112.985037", "23.15046"] },
// ...
// ...
// ]
var count = points.length;
var _renderClusterMarker = function (context) {
var factor = Math.pow(context.count / count, 1 / 18);
var div = document.createElement('div');
var Hue = 180 - factor * 180;
var bgColor = 'hsla(' + Hue + ',100%,40%,0.7)';
var fontColor = 'hsla(' + Hue + ',100%,90%,1)';
var borderColor = 'hsla(' + Hue + ',100%,40%,1)';
var shadowColor = 'hsla(' + Hue + ',100%,90%,1)';
div.style.backgroundColor = bgColor;
var size = Math.round(30 + Math.pow(context.count / count, 1 / 5) * 20);
div.style.width = div.style.height = size + 'px';
div.style.border = 'solid 1px ' + borderColor;
div.style.borderRadius = size / 2 + 'px';
div.style.boxShadow = '0 0 5px ' + shadowColor;
div.innerHTML = context.count;
div.style.lineHeight = size + 'px';
div.style.color = fontColor;
div.style.fontSize = '14px';
div.style.textAlign = 'center';
context.marker.setOffset(new AMap.Pixel(-size / 2, -size / 2));
context.marker.setContent(div)
};
var _renderMarker = function(context) {
var content = '<div style="background-color: hsla(180, 100%, 50%, 0.3); height: 18px; width: 18px; border: 1px solid hsl(180, 100%, 40%); border-radius: 12px; box-shadow: hsl(180, 100%, 50%) 0px 0px 3px;"></div>';
var offset = new AMap.Pixel(-9, -9);
context.marker.setContent(content)
context.marker.setOffset(offset)
}
addCluster(2);
function addCluster(tag) {
if (cluster) {
cluster.setMap(null);
}
if (tag == 2) {//完全自定义
cluster = new AMap.MarkerCluster(map, points, {
gridSize: gridSize, // 设置网格像素大小
renderClusterMarker: _renderClusterMarker, // 自定义聚合点样式
renderMarker: _renderMarker, // 自定义非聚合点样式
});
} else if (tag == 1) {//自定义图标
var sts = [{
url: "//a.amap.com/jsapi_demos/static/images/blue.png",
size: new AMap.Size(32, 32),
offset: new AMap.Pixel(-16, -16)
}, {
url: "//a.amap.com/jsapi_demos/static/images/green.png",
size: new AMap.Size(32, 32),
offset: new AMap.Pixel(-16, -16)
}, {
url: "//a.amap.com/jsapi_demos/static/images/orange.png",
size: new AMap.Size(36, 36),
offset: new AMap.Pixel(-18, -18)
}, {
url: "//a.amap.com/jsapi_demos/static/images/red.png",
size: new AMap.Size(48, 48),
offset: new AMap.Pixel(-24, -24)
}, {
url: "//a.amap.com/jsapi_demos/static/images/darkRed.png",
size: new AMap.Size(48, 48),
offset: new AMap.Pixel(-24, -24)
}];
cluster = new AMap.MarkerCluster(map, points, {
styles: sts,
gridSize: gridSize
});
} else {//默认样式
cluster = new AMap.MarkerCluster(map, points, {gridSize: gridSize});
}
}
</script>
</body>
</html>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

-101
View File
@@ -1,101 +0,0 @@
<template>
<el-config-provider :size="getGlobalComponentSize" :locale="getGlobalI18n">
<router-view v-show="setLockScreen" />
<LockScreen v-if="themeConfig.isLockScreen" />
<Setings ref="setingsRef" v-show="setLockScreen" />
<CloseFull v-if="!themeConfig.isLockScreen" />
<!-- <Upgrade v-if="getVersion" />-->
<!-- <Sponsors />-->
</el-config-provider>
</template>
<script setup lang="ts" name="app">
import { defineAsyncComponent, computed, ref, onBeforeMount, onMounted, onUnmounted, nextTick, watch } from 'vue';
import { useRoute } from 'vue-router';
import { useI18n } from 'vue-i18n';
import { storeToRefs } from 'pinia';
import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes';
import { useThemeConfig } from '/@/stores/themeConfig';
import other from '/@/utils/other';
import { Local, Session } from '/@/utils/storage';
import mittBus from '/@/utils/mitt';
import setIntroduction from '/@/utils/setIconfont';
// 引入组件
const LockScreen = defineAsyncComponent(() => import('/@/layout/lockScreen/index.vue'));
const Setings = defineAsyncComponent(() => import('/@/layout/navBars/topBar/setings.vue'));
const CloseFull = defineAsyncComponent(() => import('/@/layout/navBars/topBar/closeFull.vue'));
const Upgrade = defineAsyncComponent(() => import('/@/layout/upgrade/index.vue'));
const Sponsors = defineAsyncComponent(() => import('/@/layout/sponsors/index.vue'));
// 定义变量内容
const { messages, locale } = useI18n();
const setingsRef = ref();
const route = useRoute();
const stores = useTagsViewRoutes();
const storesThemeConfig = useThemeConfig();
const { themeConfig } = storeToRefs(storesThemeConfig);
// 设置锁屏时组件显示隐藏
const setLockScreen = computed(() => {
// 防止锁屏后,刷新出现不相关界面
// https://gitee.com/lyt-top/vue-next-admin/issues/I6AF8P
return themeConfig.value.isLockScreen ? themeConfig.value.lockScreenTime > 1 : themeConfig.value.lockScreenTime >= 0;
});
// 获取版本号
const getVersion = computed(() => {
let isVersion = false;
if (route.path !== '/login') {
// @ts-ignore
if ((Local.get('version') && Local.get('version') !== __NEXT_VERSION__) || !Local.get('version')) isVersion = true;
}
return isVersion;
});
// 获取全局组件大小
const getGlobalComponentSize = computed(() => {
return other.globalComponentSize();
});
// 获取全局 i18n
const getGlobalI18n = computed(() => {
return messages.value[locale.value];
});
// 设置初始化,防止刷新时恢复默认
onBeforeMount(() => {
// 设置批量第三方 icon 图标
setIntroduction.cssCdn();
// 设置批量第三方 js
setIntroduction.jsCdn();
});
// 页面加载时
onMounted(() => {
nextTick(() => {
// 监听布局配'置弹窗点击打开
mittBus.on('openSetingsDrawer', () => {
setingsRef.value.openDrawer();
});
// 获取缓存中的布局配置
if (Local.get('themeConfig')) {
storesThemeConfig.setThemeConfig({ themeConfig: Local.get('themeConfig') });
document.documentElement.style.cssText = Local.get('themeConfigStyle');
}
// 获取缓存中的全屏配置
if (Session.get('isTagsViewCurrenFull')) {
stores.setCurrenFullscreen(Session.get('isTagsViewCurrenFull'));
}
});
});
// 页面销毁时,关闭监听布局配置/i18n监听
onUnmounted(() => {
mittBus.off('openSetingsDrawer', () => {});
});
// 监听路由的变化,设置网站标题
watch(
() => route.path,
() => {
other.useTitle();
},
{
deep: true,
}
);
</script>
-31
View File
@@ -1,31 +0,0 @@
import {post} from '/@/utils/req';
/**
* (不建议写成 request.post(xxx),因为这样 post 时,无法 params 与 data 同时传参)
*
* 登录api接口集合
* @method signIn 用户登录
* @method signOut 用户退出登录
*/
export function useLoginApi() {
return {
signIn: (data: object) => {
return post({
url: '/login',
data,
});
},
signInByCode: (data: object) => {
return post({
url: '/auth/loginByCode',
data,
});
},
signOut: (data: object) => {
return post({
url: '/logout',
data,
});
},
};
}
-30
View File
@@ -1,30 +0,0 @@
import {get} from '/@/utils/req';
/**
* 以下为模拟接口地址,gitee 的不通,就换自己的真实接口地址
*
* (不建议写成 request.post(xxx),因为这样 post 时,无法 params 与 data 同时传参)
*
* 后端控制菜单模拟json,路径在 https://gitee.com/lyt-top/vue-next-admin-images/tree/master/menu
* 后端控制路由,isRequestRoutes 为 true,则开启后端控制路由
* @method getAdminMenu 获取后端动态路由菜单(admin)
* @method getTestMenu 获取后端动态路由菜单(test)
*/
export function useMenuApi() {
return {
getAdminMenu: (params?: object) => {
return get({
// url: '/gitee/lyt-top/vue-next-admin-images/raw/master/menu/adminMenu.json',
url: 'getMenuList',
params,
});
},
getTestMenu: (params?: object) => {
return get({
// url: '/gitee/lyt-top/vue-next-admin-images/raw/master/menu/testMenu.json',
url: 'getMenuList',
params,
});
},
};
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 101 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 8.4 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 26 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 36 KiB

-9
View File
@@ -1,9 +0,0 @@
<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>

Before

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 103 KiB

-105
View File
@@ -1,105 +0,0 @@
//流程类型名称匹配
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;
};
-351
View File
@@ -1,351 +0,0 @@
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: {},
// };
-35
View File
@@ -1,35 +0,0 @@
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();
-28
View File
@@ -1,28 +0,0 @@
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,
};
-120
View File
@@ -1,120 +0,0 @@
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,
],
};
-22
View File
@@ -1,22 +0,0 @@
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,
};
-52
View File
@@ -1,52 +0,0 @@
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,
};
-26
View File
@@ -1,26 +0,0 @@
import {
CommonGroupProperties,
ExtensionGroupProperties,
DocumentGroupProperties,
getElementTypeListenerProperties,
} from '../common';
//流程数据属性配置数组
const ProcessGroupPropertiesArray = [
CommonGroupProperties,
/**
* 监听器组
*/
getElementTypeListenerProperties({
name: '全局监听器',
}),
ExtensionGroupProperties,
DocumentGroupProperties,
];
export default {
//流程
'bpmn:Process': ProcessGroupPropertiesArray,
//子流程
'bpmn:SubProcess': ProcessGroupPropertiesArray,
};
-256
View File
@@ -1,256 +0,0 @@
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: 'z6' },
{ 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,
};
-19
View File
@@ -1,19 +0,0 @@
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>`;
};
-2
View File
@@ -1,2 +0,0 @@
import translate from './translate';
export default translate;
-22
View File
@@ -1,22 +0,0 @@
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 + '}';
});
};
}
-239
View File
@@ -1,239 +0,0 @@
/**
* 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.': '指定多个组作为逗号分隔的列表。',
};
-143
View File
@@ -1,143 +0,0 @@
<?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
-149
View File
@@ -1,149 +0,0 @@
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 });
},
};
-141
View File
@@ -1,141 +0,0 @@
// 模型节点
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;
}
-26
View File
@@ -1,26 +0,0 @@
<template>
<slot v-if="getUserAuthBtnList" />
</template>
<script setup lang="ts" name="auth">
import { computed } from 'vue';
import { storeToRefs } from 'pinia';
import { useUserInfo } from '/@/stores/userInfo';
// 定义父组件传过来的值
const props = defineProps({
value: {
type: String,
default: () => '',
},
});
// 定义变量内容
const stores = useUserInfo();
const { userInfos } = storeToRefs(stores);
// 获取 pinia 中的用户权限
const getUserAuthBtnList = computed(() => {
return userInfos.value.authBtnList.some((v: string) => v === props.value);
});
</script>
-27
View File
@@ -1,27 +0,0 @@
<template>
<slot v-if="getUserAuthBtnList" />
</template>
<script setup lang="ts" name="authAll">
import { computed } from 'vue';
import { storeToRefs } from 'pinia';
import { useUserInfo } from '/@/stores/userInfo';
import { judementSameArr } from '/@/utils/arrayOperation';
// 定义父组件传过来的值
const props = defineProps({
value: {
type: Array,
default: () => [],
},
});
// 定义变量内容
const stores = useUserInfo();
const { userInfos } = storeToRefs(stores);
// 获取 pinia 中的用户权限
const getUserAuthBtnList = computed(() => {
return judementSameArr(props.value, userInfos.value.authBtnList);
});
</script>
-32
View File
@@ -1,32 +0,0 @@
<template>
<slot v-if="getUserAuthBtnList" />
</template>
<script setup lang="ts" name="auths">
import { computed } from 'vue';
import { storeToRefs } from 'pinia';
import { useUserInfo } from '/@/stores/userInfo';
// 定义父组件传过来的值
const props = defineProps({
value: {
type: Array,
default: () => [],
},
});
// 定义变量内容
const stores = useUserInfo();
const { userInfos } = storeToRefs(stores);
// 获取 pinia 中的用户权限
const getUserAuthBtnList = computed(() => {
let flag = false;
userInfos.value.authBtnList.map((val: string) => {
props.value.map((v) => {
if (val === v) flag = true;
});
});
return flag;
});
</script>
-143
View File
@@ -1,143 +0,0 @@
<template>
<div>
<el-dialog title="更换头像" v-model="state.isShowDialog" width="769px">
<div class="cropper-warp">
<div class="cropper-warp-left">
<img :src="state.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="state.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="state.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 setup lang="ts" name="cropper">
import { reactive, nextTick } from 'vue';
import Cropper from 'cropperjs';
import 'cropperjs/dist/cropper.css';
// 定义变量内容
const state = reactive({
isShowDialog: false,
cropperImg: '',
cropperImgBase64: '',
cropper: '' as RefType,
});
// 打开弹窗
const openDialog = (imgs: string) => {
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 = <HTMLImageElement>document.querySelector('.cropper-warp-left-img');
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 = state.cropper.getCroppedCanvas().toDataURL('image/jpeg');
},
});
};
// 暴露变量
defineExpose({
openDialog,
});
</script>
<style scoped lang="scss">
.cropper-warp {
display: flex;
.cropper-warp-left {
position: relative;
display: inline-block;
height: 350px;
flex: 1;
border: 1px solid var(--el-border-color);
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>
-101
View File
@@ -1,101 +0,0 @@
<template>
<div class="editor-container">
<Toolbar :editor="editorRef" :mode="mode" />
<Editor
:mode="mode"
:defaultConfig="state.editorConfig"
:style="{ height }"
v-model="state.editorVal"
@onCreated="handleCreated"
@onChange="handleChange"
/>
</div>
</template>
<script setup lang="ts" name="wngEditor">
// https://www.wangeditor.com/v5/for-frame.html#vue3
import '@wangeditor/editor/dist/css/style.css';
import { reactive, shallowRef, watch, onBeforeUnmount } from 'vue';
import { IDomEditor } from '@wangeditor/editor';
import { Toolbar, Editor } from '@wangeditor/editor-for-vue';
// 定义父组件传过来的值
const props = defineProps({
// 是否禁用
disable: {
type: Boolean,
default: () => false,
},
// 内容框默认 placeholder
placeholder: {
type: String,
default: () => '请输入内容...',
},
// https://www.wangeditor.com/v5/getting-started.html#mode-%E6%A8%A1%E5%BC%8F
// 模式,可选 <default|simple>,默认 default
mode: {
type: String,
default: () => 'default',
},
// 高度
height: {
type: String,
default: () => '310px',
},
// 双向绑定,用于获取 editor.getHtml()
getHtml: String,
// 双向绑定,用于获取 editor.getText()
getText: String,
});
// 定义子组件向父组件传值/事件
const emit = defineEmits(['update:getHtml', 'update:getText']);
// 定义变量内容
const editorRef = shallowRef();
const state = reactive({
editorConfig: {
placeholder: props.placeholder,
},
editorVal: props.getHtml,
});
// 编辑器回调函数
const handleCreated = (editor: IDomEditor) => {
editorRef.value = editor;
};
// 编辑器内容改变时
const handleChange = (editor: IDomEditor) => {
emit('update:getHtml', editor.getHtml());
emit('update:getText', editor.getText());
};
// 页面销毁时
onBeforeUnmount(() => {
const editor = editorRef.value;
if (editor == null) return;
editor.destroy();
});
// 监听是否禁用改变
// https://gitee.com/lyt-top/vue-next-admin/issues/I4LM7I
watch(
() => props.disable,
(bool) => {
const editor = editorRef.value;
if (editor == null) return;
bool ? editor.disable() : editor.enable();
},
{
deep: true,
}
);
// 监听双向绑定值改变,用于回显
watch(
() => props.getHtml,
(val) => {
state.editorVal = val;
},
{
deep: true,
}
);
</script>
-243
View File
@@ -1,243 +0,0 @@
<template>
<div class="icon-selector w100 h100">
<el-input
v-model="state.fontIconSearch"
:placeholder="state.fontIconPlaceholder"
:clearable="clearable"
:disabled="disabled"
:size="size"
ref="inputWidthRef"
@clear="onClearFontIcon"
@focus="onIconFocus"
@blur="onIconBlur"
>
<template #prepend>
<SvgIcon
:name="state.fontIconPrefix === '' ? prepend : state.fontIconPrefix"
class="font14"
v-if="state.fontIconPrefix === '' ? prepend?.indexOf('ele-') > -1 : state.fontIconPrefix?.indexOf('ele-') > -1"
/>
<i v-else :class="state.fontIconPrefix === '' ? prepend : state.fontIconPrefix" class="font14"></i>
</template>
</el-input>
<el-popover
placement="bottom"
:width="state.fontIconWidth"
transition="el-zoom-in-top"
popper-class="icon-selector-popper"
trigger="click"
:virtual-ref="inputWidthRef"
virtual-triggering
>
<template #default>
<div class="icon-selector-warp">
<div class="icon-selector-warp-title">{{ title }}</div>
<el-tabs v-model="state.fontIconTabActive" @tab-click="onIconClick">
<el-tab-pane lazy label="ali" name="ali">
<IconList :list="fontIconSheetsFilterList" :empty="emptyDescription" :prefix="state.fontIconPrefix" @get-icon="onColClick" />
</el-tab-pane>
<el-tab-pane lazy label="ele" name="ele">
<IconList :list="fontIconSheetsFilterList" :empty="emptyDescription" :prefix="state.fontIconPrefix" @get-icon="onColClick" />
</el-tab-pane>
<el-tab-pane lazy label="awe" name="awe">
<IconList :list="fontIconSheetsFilterList" :empty="emptyDescription" :prefix="state.fontIconPrefix" @get-icon="onColClick" />
</el-tab-pane>
</el-tabs>
</div>
</template>
</el-popover>
</div>
</template>
<script setup lang="ts" name="iconSelector">
import { defineAsyncComponent, ref, reactive, onMounted, nextTick, computed, watch } from 'vue';
import type { TabsPaneContext } from 'element-plus';
import initIconfont from '/@/utils/getStyleSheets';
import '/@/theme/iconSelector.scss';
// 定义父组件传过来的值
const props = defineProps({
// 输入框前置内容
prepend: {
type: String,
default: () => 'ele-Pointer',
},
// 输入框占位文本
placeholder: {
type: String,
default: () => '请输入内容搜索图标或者选择图标',
},
// 输入框占位文本
size: {
type: String,
default: () => 'default',
},
// 弹窗标题
title: {
type: String,
default: () => '请选择图标',
},
// 禁用
disabled: {
type: Boolean,
default: () => false,
},
// 是否可清空
clearable: {
type: Boolean,
default: () => true,
},
// 自定义空状态描述文字
emptyDescription: {
type: String,
default: () => '无相关图标',
},
// 双向绑定值,默认为 modelValue,
// 参考:https://v3.cn.vuejs.org/guide/migration/v-model.html#%E8%BF%81%E7%A7%BB%E7%AD%96%E7%95%A5
// 参考:https://v3.cn.vuejs.org/guide/component-custom-events.html#%E5%A4%9A%E4%B8%AA-v-model-%E7%BB%91%E5%AE%9A
modelValue: String,
});
// 定义子组件向父组件传值/事件
const emit = defineEmits(['update:modelValue', 'get', 'clear']);
// 引入组件
const IconList = defineAsyncComponent(() => import('/@/components/iconSelector/list.vue'));
// 定义变量内容
const inputWidthRef = ref();
const state = reactive({
fontIconPrefix: '',
fontIconWidth: 0,
fontIconSearch: '',
fontIconPlaceholder: '',
fontIconTabActive: 'ali',
fontIconList: {
ali: [],
ele: [],
awe: [],
},
});
// 处理 input 获取焦点时,modelValue 有值时,改变 input 的 placeholder 值
const onIconFocus = () => {
if (!props.modelValue) return false;
state.fontIconSearch = '';
state.fontIconPlaceholder = props.modelValue;
};
// 处理 input 失去焦点时,为空将清空 input 值,为点击选中图标时,将取原先值
const onIconBlur = () => {
const list = fontIconTabNameList();
setTimeout(() => {
const icon = list.filter((icon: string) => icon === state.fontIconSearch);
if (icon.length <= 0) state.fontIconSearch = '';
}, 300);
};
// 图标搜索及图标数据显示
const fontIconSheetsFilterList = computed(() => {
const list = fontIconTabNameList();
if (!state.fontIconSearch) return list;
let search = state.fontIconSearch.trim().toLowerCase();
return list.filter((item: string) => {
if (item.toLowerCase().indexOf(search) !== -1) return item;
});
});
// 根据 tab name 类型设置图标
const fontIconTabNameList = () => {
let iconList: any = [];
if (state.fontIconTabActive === 'ali') iconList = state.fontIconList.ali;
else if (state.fontIconTabActive === 'ele') iconList = state.fontIconList.ele;
else if (state.fontIconTabActive === 'awe') iconList = state.fontIconList.awe;
return iconList;
};
// 处理 icon 双向绑定数值回显
const initModeValueEcho = () => {
if (props.modelValue === '') return ((<string | undefined>state.fontIconPlaceholder) = props.placeholder);
(<string | undefined>state.fontIconPlaceholder) = props.modelValue;
(<string | undefined>state.fontIconPrefix) = props.modelValue;
};
// 处理 icon 类型,用于回显时,tab 高亮与初始化数据
const initFontIconName = () => {
let name = 'ali';
if(props.modelValue){
if (props.modelValue!.indexOf('iconfont') > -1) name = 'ali';
else if (props.modelValue!.indexOf('ele-') > -1) name = 'ele';
else if (props.modelValue!.indexOf('fa') > -1) name = 'awe';
}
// 初始化 tab 高亮回显
state.fontIconTabActive = name;
return name;
};
// 初始化数据
const initFontIconData = async (name: string) => {
if (name === 'ali') {
// 阿里字体图标使用 `iconfont xxx`
if (state.fontIconList.ali.length > 0) return;
await initIconfont.ali().then((res: any) => {
state.fontIconList.ali = res.map((i: string) => `iconfont ${i}`);
});
} else if (name === 'ele') {
// element plus 图标
if (state.fontIconList.ele.length > 0) return;
await initIconfont.ele().then((res: any) => {
state.fontIconList.ele = res;
});
} else if (name === 'awe') {
// fontawesome字体图标使用 `fa xxx`
if (state.fontIconList.awe.length > 0) return;
await initIconfont.awe().then((res: any) => {
state.fontIconList.awe = 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();
};
// 图标点击切换
const onIconClick = (pane: TabsPaneContext) => {
initFontIconData(pane.paneName as string);
inputWidthRef.value.focus();
};
// 获取当前点击的 icon 图标
const onColClick = (v: string) => {
state.fontIconPlaceholder = v;
state.fontIconPrefix = v;
emit('get', state.fontIconPrefix);
emit('update:modelValue', state.fontIconPrefix);
inputWidthRef.value.focus();
};
// 清空当前点击的 icon 图标
const onClearFontIcon = () => {
state.fontIconPrefix = '';
emit('clear', state.fontIconPrefix);
emit('update:modelValue', state.fontIconPrefix);
};
// 获取 input 的宽度
const getInputWidth = () => {
nextTick(() => {
state.fontIconWidth = inputWidthRef.value.$el.offsetWidth;
});
};
// 监听页面宽度改变
const initResize = () => {
window.addEventListener('resize', () => {
getInputWidth();
});
};
// 页面加载时
onMounted(() => {
initFontIconData(initFontIconName());
initResize();
getInputWidth();
});
// 监听双向绑定 modelValue 的变化
watch(
() => props.modelValue,
() => {
initModeValueEcho();
initFontIconName();
}
);
</script>
-84
View File
@@ -1,84 +0,0 @@
<template>
<div class="icon-selector-warp-row">
<el-scrollbar ref="selectorScrollbarRef">
<el-row :gutter="10" v-if="props.list.length > 0">
<el-col :xs="6" :sm="4" :md="4" :lg="4" :xl="4" v-for="(v, k) in list" :key="k" @click="onColClick(v)">
<div class="icon-selector-warp-item" :class="{ 'icon-selector-active': prefix === v }">
<SvgIcon :name="v" />
</div>
</el-col>
</el-row>
<el-empty :image-size="100" v-if="list.length <= 0" :description="empty"></el-empty>
</el-scrollbar>
</div>
</template>
<script setup lang="ts" name="iconSelectorList">
// 定义父组件传过来的值
const props = defineProps({
// 图标列表数据
list: {
type: Array,
default: () => [],
},
// 自定义空状态描述文字
empty: {
type: String,
default: () => '无相关图标',
},
// 高亮当前选中图标
prefix: {
type: String,
default: () => '',
},
});
// 定义子组件向父组件传值/事件
const emit = defineEmits(['get-icon']);
// 当前 icon 图标点击时
const onColClick = (v: unknown | string) => {
emit('get-icon', v);
};
</script>
<style scoped lang="scss">
.icon-selector-warp-row {
height: 230px;
overflow: hidden;
.el-row {
padding: 15px;
}
.el-scrollbar__bar.is-horizontal {
display: none;
}
.icon-selector-warp-item {
display: flex;
justify-content: center;
align-items: center;
border: 1px solid var(--el-border-color);
border-radius: 5px;
margin-bottom: 10px;
height: 30px;
i {
font-size: 20px;
color: var(--el-text-color-regular);
}
&:hover {
cursor: pointer;
background-color: var(--el-color-primary-light-9);
border: 1px solid var(--el-color-primary-light-5);
i {
color: var(--el-color-primary);
}
}
}
.icon-selector-active {
background-color: var(--el-color-primary-light-9);
border: 1px solid var(--el-color-primary-light-5);
i {
color: var(--el-color-primary);
}
}
}
</style>
-191
View File
@@ -1,191 +0,0 @@
<template>
<div class="notice-bar" :style="{ background, height: `${height}px` }" v-show="!state.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 setup lang="ts" name="noticeBar">
import { reactive, ref, onMounted, nextTick } from 'vue';
// 定义父组件传过来的值
const props = defineProps({
// 通知栏模式,可选值为 closeable link
mode: {
type: String,
default: () => '',
},
// 通知文本内容
text: {
type: String,
default: () => '',
},
// 通知文本颜色
color: {
type: String,
default: () => 'var(--el-color-warning)',
},
// 通知背景色
background: {
type: String,
default: () => 'var(--el-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: () => '',
},
});
// 定义子组件向父组件传值/事件
const emit = defineEmits(['close', 'link']);
// 定义变量内容
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();
});
</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;
: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>
-63
View File
@@ -1,63 +0,0 @@
<template>
<i v-if="isShowIconSvg" class="el-icon" :style="setIconSvgStyle">
<component :is="getIconName" />
</i>
<div v-else-if="isShowIconImg" :style="setIconImgOutStyle">
<img :src="getIconName" :style="setIconSvgInsStyle" />
</div>
<i v-else :class="getIconName" :style="setIconSvgStyle" />
</template>
<script setup lang="ts" name="svgIcon">
import { computed } from 'vue';
// 定义父组件传过来的值
const props = defineProps({
// svg 图标组件名字
name: {
type: String,
},
// svg 大小
size: {
type: Number,
default: () => 14,
},
// svg 颜色
color: {
type: String,
},
});
// 在线链接、本地引入地址前缀
// https://gitee.com/lyt-top/vue-next-admin/issues/I62OVL
const linesString = ['https', 'http', '/src', '/assets', 'data:image', import.meta.env.VITE_PUBLIC_PATH];
// 获取 icon 图标名称
const getIconName = computed(() => {
return props?.name;
});
// 用于判断 element plus 自带 svg 图标的显示、隐藏
const isShowIconSvg = computed(() => {
return props?.name?.startsWith('ele-');
});
// 用于判断在线链接、本地引入等图标显示、隐藏
const isShowIconImg = computed(() => {
return linesString.find((str) => props.name?.startsWith(str));
});
// 设置图标样式
const setIconSvgStyle = computed(() => {
return `font-size: ${props.size}px;color: ${props.color};`;
});
// 设置图片样式
const setIconImgOutStyle = computed(() => {
return `width: ${props.size}px;height: ${props.size}px;display: inline-block;overflow: hidden;`;
});
// 设置图片样式
// https://gitee.com/lyt-top/vue-next-admin/issues/I59ND0
const setIconSvgInsStyle = computed(() => {
const filterStyle: string[] = [];
const compatibles: string[] = ['-webkit', '-ms', '-o', '-moz'];
compatibles.forEach((j) => filterStyle.push(`${j}-filter: drop-shadow(${props.color} 30px 0);`));
return `width: ${props.size}px;height: ${props.size}px;position: relative;left: -${props.size}px;${filterStyle.join('')}`;
});
</script>
-301
View File
@@ -1,301 +0,0 @@
<template>
<div class="table-container">
<el-table
:data="data"
:border="setBorder"
v-bind="$attrs"
row-key="id"
stripe
style="width: 100%"
v-loading="config.loading"
@selection-change="onSelectionChange"
>
<el-table-column type="selection" :reserve-selection="true" width="30" v-if="config.isSelection" />
<el-table-column type="index" label="序号" width="60" v-if="config.isSerialNo" />
<el-table-column
v-for="(item, index) in setHeader"
:key="index"
show-overflow-tooltip
:prop="item.key"
:width="item.colWidth"
:label="item.title"
>
<template v-slot="scope">
<template v-if="item.type === 'image'">
<el-image
:style="{ width: `${item.width}px`, height: `${item.height}px` }"
:src="scope.row[item.key]"
:zoom-rate="1.2"
:preview-src-list="[scope.row[item.key]]"
preview-teleported
fit="cover"
/>
</template>
<template v-else>
{{ scope.row[item.key] }}
</template>
</template>
</el-table-column>
<el-table-column label="操作" width="100" v-if="config.isOperate">
<template v-slot="scope">
<el-popconfirm title="确定删除吗?" @confirm="onDelRow(scope.row)">
<template #reference>
<el-button text type="primary">删除</el-button>
</template>
</el-popconfirm>
</template>
</el-table-column>
<template #empty>
<el-empty description="暂无数据" />
</template>
</el-table>
<div class="table-footer mt15">
<el-pagination
v-model:current-page="state.page.pageNum"
v-model:page-size="state.page.pageSize"
:pager-count="5"
:page-sizes="[10, 20, 30]"
:total="config.total"
layout="total, sizes, prev, pager, next, jumper"
background
@size-change="onHandleSizeChange"
@current-change="onHandleCurrentChange"
>
</el-pagination>
<div class="table-footer-tool">
<SvgIcon name="iconfont icon-dayin" :size="19" title="打印" @click="onPrintTable" />
<SvgIcon name="iconfont icon-yunxiazai_o" :size="22" title="导出" @click="onImportTable" />
<SvgIcon name="iconfont icon-shuaxin" :size="22" title="刷新" @click="onRefreshTable" />
<el-popover
placement="top-end"
trigger="click"
transition="el-zoom-in-top"
popper-class="table-tool-popper"
:width="300"
:persistent="false"
@show="onSetTable"
>
<template #reference>
<SvgIcon name="iconfont icon-quanjushezhi_o" :size="22" title="设置" />
</template>
<template #default>
<div class="tool-box">
<el-tooltip content="拖动进行排序" placement="top-start">
<SvgIcon name="fa fa-question-circle-o" :size="17" class="ml11" color="#909399" />
</el-tooltip>
<el-checkbox
v-model="state.checkListAll"
:indeterminate="state.checkListIndeterminate"
class="ml10 mr1"
label="列显示"
@change="onCheckAllChange"
/>
<el-checkbox v-model="getConfig.isSerialNo" class="ml12 mr1" label="序号" />
<el-checkbox v-model="getConfig.isSelection" class="ml12 mr1" label="多选" />
</div>
<el-scrollbar>
<div ref="toolSetRef" class="tool-sortable">
<div class="tool-sortable-item" v-for="v in header" :key="v.key" :data-key="v.key">
<i class="fa fa-arrows-alt handle cursor-pointer"></i>
<el-checkbox v-model="v.isCheck" size="default" class="ml12 mr8" :label="v.title" @change="onCheckChange" />
</div>
</div>
</el-scrollbar>
</template>
</el-popover>
</div>
</div>
</div>
</template>
<script setup lang="ts" name="netxTable">
import { reactive, computed, nextTick, ref } from 'vue';
import { ElMessage } from 'element-plus';
import printJs from 'print-js';
import table2excel from 'js-table2excel';
import Sortable from 'sortablejs';
import { storeToRefs } from 'pinia';
import { useThemeConfig } from '/@/stores/themeConfig';
import '/@/theme/tableTool.scss';
// 定义父组件传过来的值
const props = defineProps({
// 列表内容
data: {
type: Array<EmptyObjectType>,
default: () => [],
},
// 表头内容
header: {
type: Array<EmptyObjectType>,
default: () => [],
},
// 配置项
config: {
type: Object,
default: () => {},
},
// 打印标题
printName: {
type: String,
default: () => '',
},
});
// 定义子组件向父组件传值/事件
const emit = defineEmits(['delRow', 'pageChange', 'sortHeader']);
// 定义变量内容
const toolSetRef = ref();
const storesThemeConfig = useThemeConfig();
const { themeConfig } = storeToRefs(storesThemeConfig);
const state = reactive({
page: {
pageNum: 1,
pageSize: 10,
},
selectlist: [] as EmptyObjectType[],
checkListAll: true,
checkListIndeterminate: false,
});
// 设置边框显示/隐藏
const setBorder = computed(() => {
return props.config.isBorder ? true : false;
});
// 获取父组件 配置项(必传)
const getConfig = computed(() => {
return props.config;
});
// 设置 tool header 数据
const setHeader = computed(() => {
return props.header.filter((v) => v.isCheck);
});
// tool 列显示全选改变时
const onCheckAllChange = <T>(val: T) => {
if (val) props.header.forEach((v) => (v.isCheck = true));
else props.header.forEach((v) => (v.isCheck = false));
state.checkListIndeterminate = false;
};
// tool 列显示当前项改变时
const onCheckChange = () => {
const headers = props.header.filter((v) => v.isCheck).length;
state.checkListAll = headers === props.header.length;
state.checkListIndeterminate = headers > 0 && headers < props.header.length;
};
// 表格多选改变时,用于导出
const onSelectionChange = (val: EmptyObjectType[]) => {
state.selectlist = val;
};
// 删除当前项
const onDelRow = (row: EmptyObjectType) => {
emit('delRow', row);
};
// 分页改变
const onHandleSizeChange = (val: number) => {
state.page.pageSize = val;
emit('pageChange', state.page);
};
// 分页改变
const onHandleCurrentChange = (val: number) => {
state.page.pageNum = val;
emit('pageChange', state.page);
};
// 搜索时,分页还原成默认
const pageReset = () => {
state.page.pageNum = 1;
state.page.pageSize = 10;
emit('pageChange', state.page);
};
// 打印
const onPrintTable = () => {
// https://printjs.crabbly.com/#documentation
// 自定义打印
let tableTh = '';
let tableTrTd = '';
let tableTd: any = {};
// 表头
props.header.forEach((v) => {
tableTh += `<th class="table-th">${v.title}</th>`;
});
// 表格内容
props.data.forEach((val, key) => {
if (!tableTd[key]) tableTd[key] = [];
props.header.forEach((v) => {
if (v.type === 'text') {
tableTd[key].push(`<td class="table-th table-center">${val[v.key]}</td>`);
} else if (v.type === 'image') {
tableTd[key].push(`<td class="table-th table-center"><img src="${val[v.key]}" style="width:${v.width}px;height:${v.height}px;"/></td>`);
}
});
tableTrTd += `<tr>${tableTd[key].join('')}</tr>`;
});
// 打印
printJs({
printable: `<div style=display:flex;flex-direction:column;text-align:center><h3>${props.printName}</h3></div><table border=1 cellspacing=0><tr>${tableTh}${tableTrTd}</table>`,
type: 'raw-html',
css: ['//at.alicdn.com/t/c/font_2298093_rnp72ifj3ba.css', '//unpkg.com/element-plus/dist/index.css'],
style: `@media print{.mb15{margin-bottom:15px;}.el-button--small i.iconfont{font-size: 12px !important;margin-right: 5px;}}; .table-th{word-break: break-all;white-space: pre-wrap;}.table-center{text-align: center;}`,
});
};
// 导出
const onImportTable = () => {
if (state.selectlist.length <= 0) return ElMessage.warning('请先选择要导出的数据');
table2excel(props.header, state.selectlist, `${themeConfig.value.globalTitle} ${new Date().toLocaleString()}`);
};
// 刷新
const onRefreshTable = () => {
emit('pageChange', state.page);
};
// 设置
const onSetTable = () => {
nextTick(() => {
const sortable = Sortable.create(toolSetRef.value, {
handle: '.handle',
dataIdAttr: 'data-key',
animation: 150,
onEnd: () => {
const headerList: EmptyObjectType[] = [];
sortable.toArray().forEach((val: string) => {
props.header.forEach((v) => {
if (v.key === val) headerList.push({ ...v });
});
});
emit('sortHeader', headerList);
},
});
});
};
// 暴露变量
defineExpose({
pageReset,
});
</script>
<style scoped lang="scss">
.table-container {
display: flex;
flex-direction: column;
.el-table {
flex: 1;
}
.table-footer {
display: flex;
.table-footer-tool {
flex: 1;
display: flex;
align-items: center;
justify-content: flex-end;
i {
margin-right: 10px;
cursor: pointer;
color: var(--el-text-color-regular);
&:last-of-type {
margin-right: 0;
}
}
}
}
}
</style>
@@ -1,200 +0,0 @@
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对象
};
@@ -1,56 +0,0 @@
.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;
}
-2
View File
@@ -1,2 +0,0 @@
import BpmnActions from './BpmnActions';
export default BpmnActions;
@@ -1,70 +0,0 @@
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>
);
},
});
-13
View File
@@ -1,13 +0,0 @@
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;
@@ -1,172 +0,0 @@
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) || '',
});
}
@@ -1,22 +0,0 @@
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
-45
View File
@@ -1,45 +0,0 @@
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" />;
},
});
-2
View File
@@ -1,2 +0,0 @@
import Modeler from './Modeler';
export default Modeler;
-24
View File
@@ -1,24 +0,0 @@
.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;
}
-54
View File
@@ -1,54 +0,0 @@
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" />;
},
});
@@ -1,54 +0,0 @@
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" />;
},
});
-2
View File
@@ -1,2 +0,0 @@
import Modeler2 from './Modeler2';
export default Modeler2;
-27
View File
@@ -1,27 +0,0 @@
.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;
}
.dark #modeler-container {
background-color: #666 !important;
}
.djs-palette {
border: none !important;
background-color: #fff !important;
box-shadow: 0 3px 3px rgba(0, 0, 0, 0.2);
top: 65px !important;
}
-95
View File
@@ -1,95 +0,0 @@
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>
</>
)}
</>
);
},
});
-2
View File
@@ -1,2 +0,0 @@
import Pannel from './Panel';
export default Pannel;
-54
View File
@@ -1,54 +0,0 @@
.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;
}
@@ -1,36 +0,0 @@
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;
@@ -1,3 +0,0 @@
import PrefixAutoComplete from './PrefixAutoComplete';
export default PrefixAutoComplete;
@@ -1,34 +0,0 @@
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;
@@ -1,3 +0,0 @@
import PrefixLabelSelect from './PrefixLabelSelect';
export default PrefixLabelSelect;
@@ -1,29 +0,0 @@
.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;
}
-290
View File
@@ -1,290 +0,0 @@
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>
);
};
}
-3
View File
@@ -1,3 +0,0 @@
import SubList from './SubList';
export default SubList;
-67
View File
@@ -1,67 +0,0 @@
.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%;
}
-20
View File
@@ -1,20 +0,0 @@
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;
}
-58
View File
@@ -1,58 +0,0 @@
<template>
<div class="amap-dialog">
<el-dialog v-model='dialogVisible' title='地址选择' draggable width='90%' >
<div style="height: 700px;overflow: hidden">
<iframe :src="state.amapSrc" frameborder="0" height="100%" width="100%"></iframe>
<!-- <iframe :src="http://localhost:868/ass/amap.html?adcoo=&adreg=&addet=&adzoo=&v=" frameborder="0" height="100%" width="100%"></iframe>-->
</div>
<!-- <template #footer>-->
<!-- <span class='dialog-footer'>-->
<!-- <el-button type='primary' @click='handleConfirm'>确 认</el-button>-->
<!-- <el-button @click='dialogVisible = false'>取 消</el-button>-->
<!-- </span>-->
<!-- </template>-->
</el-dialog>
</div>
</template>
<script lang='ts' setup>
import {defineExpose, onMounted, reactive, ref} from 'vue';
const state = reactive({
data: [] as any,
amapSrc:'',
});
const dialogVisible = ref(false);
const openModal = async (data:any) => {
if(data&&data.adcoo){
state.amapSrc=import.meta.env.VITE_PUBLIC_PATH+'ass/amap.html?adcoo='
+data.adcoo+'&adreg='+data.adreg+'&addet='+data.addet+'&adzoo='+data.adzoo+'&v=';
}else{
state.amapSrc=import.meta.env.VITE_PUBLIC_PATH+'ass/amap.html?adcoo=&adreg=&addet=&adzoo=&v=';
}
dialogVisible.value = true;
};
defineExpose({openModal});
onMounted(() => {
});
const emits = defineEmits(['close']);
const handleConfirm = () => {
emits('close', {id: '333'});
dialogVisible.value = false;
};
window.addressChooseBack=function (data:any){
emits('close', data);
dialogVisible.value = false;
}
</script>
<style scoped>
</style>
-219
View File
@@ -1,219 +0,0 @@
<template>
<el-form class='zform zform100' :inline="true" :model='form' label-width='160px' label-position='left'>
<div class="zform-div">
<el-form-item label='流程说明'>
这是一个测试的流程,目前功能还不完善
</el-form-item>
<el-form-item label='紧急程度'>
<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>
</el-form-item>
<el-form-item label='即将流向'>
{{ state.toExmen }}
</el-form-item>
<el-form-item label='处理意见'>
<el-input v-model='form.opnot' type='textarea' :rows='5' placeholder=' '/>
</el-form-item>
<el-form-item label='附件'>
<el-upload
:action='state.uploadUrl' :headers='state.headers'
:on-preview='handlePreview' style='width:800px' :on-success='handleSuccess'
:on-remove='handleRemove'
multiple :limit='10' :on-exceed='handleExceed' v-model:file-list='form.atts'>
<el-button type='primary'>上传附件</el-button>
</el-upload>
</el-form-item>
<div class="zform-item" style="padding-left: 6px">
<el-checkbox v-model='state.chtag' @change='toggleFlowChart'>流程图</el-checkbox>
</div>
<div class="zform-item" v-show='state.chtag'>
<div style='background-color: white;height:1000px'>
<Modeler2 />
</div>
</div>
</div>
</el-form>
</template>
<script lang='ts' setup>
import {defineExpose, onMounted, reactive, toRaw, toRefs} from 'vue';
import {useRoute} from 'vue-router';
import {get} from "/@/utils/req";
import Modeler2 from '/@/comps/Activiti/modeler2/index';
import {BpmnStore} from "/@/bpmn/store";
import { Session } from '/@/utils/storage';
import { ElMessage, ElMessageBox, UploadProps } from 'element-plus';
const route = useRoute();
const state = reactive({
form: {opurg:'3'} as any,
chtag: true,
params: {
path: '',
query: '',
},
toExmen: '',
tarno:'',
xml: null as any,
uploadUrl: '', headers: {} as any,
});
const {
form, params,
} = toRefs(state);
onMounted(async () => {
state.uploadUrl = `${import.meta.env.VITE_API_URL}gen/oss/upload`;
state.headers = { 'Authorization': Session.get('token') };
state.params = <any>route;
if (params.value.query?.id) {
}
toggleFlowChart();
});
const getOperateInfo = () => {
const formInfo=toRaw(form.value);
let atids="";
if(formInfo.atts){
for (const att of formInfo.atts) {
atids+=att.id+",";
}
if(atids){
atids=atids.substring(0,atids.length-1);
}
}
formInfo.atids=atids;
return formInfo;
};
defineExpose({getOperateInfo});
const props = defineProps({
temid: String,
});
const toggleFlowChart = async () => {
if (!state.xml) {
const map= await get({
url: '/bpm/proc/main/texml',
params:{temid:props.temid}
}) as any;
form.value.tarno = map.tarno;
form.value.tarna = map.tarna;
form.value.tamen = map.tamen;
state.toExmen = map.tarno + '.' + map.tarna + '(' + map.tamen + ')';
state.xml=map.xml;
await BpmnStore.importXML(state.xml);
const elementRegistry = BpmnStore.getModeler().get('elementRegistry');
const list1 = [] as any;
elementRegistry.forEach((item: any) => {
if (item.id == 'NS') {
list1.push(item);
}
});
const list2 = [] as any;
elementRegistry.forEach((item: any) => {
if (item.id == 'L1') {
list2.push(item);
}
});
const list3 = [] as any;
elementRegistry.forEach((item: any) => {
if (item.id == 'N1') {
list3.push(item);
}
});
const list4 = [] as any;
elementRegistry.forEach((item: any) => {
if (item.id == map.tarno) {
list4.push(item);
}
});
let modeling = BpmnStore.modeler.get('modeling');
modeling.setColor(list1, {fill: '#e4feef'});
modeling.setColor(list2, {stroke: '#009900'});
modeling.setColor(list3, {fill: '#fed6d6'});
modeling.setColor(list4, {fill: '#e3f1ff'});
}
};
//region -----附件逻辑-----
const handleRemove: UploadProps['onRemove'] = (file, uploadFiles) => {
console.log(file, uploadFiles);
};
const handlePreview: UploadProps['onPreview'] = (uploadFile) => {
console.log(uploadFile);
ElMessageBox.confirm('请选择对应的附件操作', '附件操作',
{
confirmButtonText: '下载',
cancelButtonText: '预览',
type: 'info',
},
).then(async () => {
await get({
url: '/gen/oss/download',
// params: { name: uploadFile.name, path: uploadFile.addre + '/' + uploadFile.id + '.' + uploadFile.sname },
params: { table: 'bpm_audit_att', id: uploadFile.id },
responseType: 'blob',
});
}).catch(() => {
});
};
const handleSuccess = (a: any, b: any, c: any) => {
// c[c.length - 1] = { ...a };
c[c.length - 1].id = a.id;
// c[c.length - 1].name = a.name+" ["+a.size+"]";
c[c.length - 1].name = a.name;
c[c.length - 1].filid = a.filid;
c[c.length - 1].filna = a.filna;
c[c.length - 1].path = a.path;
c[c.length - 1].ornum = c.length;
if (form.value.id) {
c[c.length - 1].audid = form.value.id;
}
};
const handleExceed: UploadProps['onExceed'] = (files, uploadFiles) => {
ElMessage.warning(
`The limit is 3, you selected ${files.length} files this time, add up to ${
files.length + uploadFiles.length
} totally`,
);
};
//endregion
// 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>
-71
View File
@@ -1,71 +0,0 @@
<template>
<div>
<OrgModal ref='orgModal' @close='closeOrgModal' />
<el-dialog v-model='state.isShow' :title='state.title' width='600px' draggable>
<div style='height: 500px'>
<el-form ref='formRef' :model='form' label-width='140px'>
<div class='zform-div'>
<el-form-item label='处理意见'>
<el-input v-model='form.opnot' type='textarea' :rows='4' placeholder='待实现' />
</el-form-item>
<el-form-item label='操作'>
<el-radio-group v-model='form.opkey' @change='opChange'>
<el-radio label='finalpass'>终审通过</el-radio>
<el-radio label='jump'>前后跳转</el-radio>
<el-radio label='abandon'>直接废弃</el-radio>
</el-radio-group>
</el-form-item>
</div>
</el-form>
</div>
<template #footer>
<span class='dialog-footer'>
<el-button type='primary' @click='closeModal'>确认</el-button>
<el-button @click='state.isShow = false'>取消</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script lang='ts' setup>
import { defineExpose, reactive, toRefs } from 'vue';
import OrgModal from '/@/comps/sys/OrgModal.vue';
const state = reactive({
isShow: false, basid: '',title:'',cotag:false,name:'节点名称:',notes:'节点备注:',
list: [] as any, form: {
flway: '1', remod: '1', opway: '1',type:'bpmn:UserTask',
crops: [
{ ornum: 1, type: '1', name: '催办' },
{ ornum: 2, type: '2', name: '撤回' },
{ ornum: 3, type: '3', name: '废弃' },
],
haops: [
{ ornum: 1, type: '1', name: '通过' },
{ ornum: 2, type: '2', name: '驳回' },
{ ornum: 3, type: '3', name: '转办' },
{ ornum: 4, type: '4', name: '沟通' },
{ ornum: 5, type: '5', name: '废弃' },
],
events: [],
} as any,
tabna: '',
});
const open = async () => {
// console.log(shape.businessObject.$attrs);
state.isShow = true;
};
defineExpose({ open });
const { form } = toRefs(state);
</script>
<style scoped>
</style>
-55
View File
@@ -1,55 +0,0 @@
<template>
<el-dialog v-if="state.show" v-model='state.show' title='代码预览' draggable width='90%'>
<CodeMirror
v-model:value="state.code"
:options="state.cmOptions"
border
placeholder="test placeholder"
height="500px"
/>
</el-dialog>
</template>
<script lang='ts' setup>
import {defineExpose, reactive} from 'vue';
// import CodeMirror from 'codemirror';
import CodeMirror from "codemirror-editor-vue3";
import 'codemirror/lib/codemirror.css'; // 引入css文件
// import 'codemirror/theme/idea.css';
import 'codemirror/theme/darcula.css';
// import 'codemirror/mode/javascript/javascript.js'; // 引入语言模式 可以从 codemirror/mode/ 下引入多个
import 'codemirror/mode/clike/clike.js'; // 引入语言模式 可以从 codemirror/mode/ 下引入多个
//https://renncheung.github.io/codemirror-editor-vue3/instructions/props.html
const state = reactive({
cmShow:true,
show: false,
code:'',
cmOptions: {
readOnly: true,
// mode: 'text/x-nginx-conf', // Language mode
// mode: 'javascript', // Language mode
// mode: 'text/x-csharp', // Language mode
mode: 'text/x-java', // Language mode
// theme: 'darcula', // Theme
lineNumbers: true, // Show line number
smartIndent: true, // Smart indent
indentUnit: 4, // The smart indent unit is 2 spaces in length
foldGutter: true, // Code folding
matchBrackets: true,
autoCloseBrackets: true,
styleActiveLine: true, // Display the style of the selected row
}
});
const openModal = async (data: any) => {
state.code = data;
state.show = true;
};
defineExpose({openModal});
</script>
@@ -1,56 +0,0 @@
// CustomPalette.js
export default class CustomPalette {
constructor(bpmnFactory, create, elementFactory, palette, translate) {
this.bpmnFactory = bpmnFactory;
this.create = create;
this.elementFactory = elementFactory;
this.translate = translate;
palette.registerProvider(this);
}
getPaletteEntries(element) {
const {
bpmnFactory,
create,
elementFactory,
translate
} = this;
function createTask() {
return function(event) {
const businessObject = bpmnFactory.create('bpmn:Task'); // 其实这个也可以不要
const shape = elementFactory.createShape({
type: 'bpmn:Task',
businessObject
});
console.log(1111)
console.log(shape) // 只在拖动或者点击时触发
create.start(event, shape);
console.log(2222)
}
}
return {
'create.lindaidai-task': {
group: 'model',
className: 'icon-custom lindaidai-task',
title: translate('创建一个类型为lindaidai-task的任务节点'),
action: {
dragstart: createTask(),
click: createTask(),
}
}
}
}
}
CustomPalette.$inject = [
'bpmnFactory',
'create',
'elementFactory',
'palette',
'translate'
]
-125
View File
@@ -1,125 +0,0 @@
import BaseRenderer from 'diagram-js/lib/draw/BaseRenderer';
import {
append as svgAppend,
attr as svgAttr,
create as svgCreate
} from 'tiny-svg';
import { customElements, customConfig, hasLabelElements } from '../utils/util'
import { is } from 'bpmn-js/lib/util/ModelUtil';
const HIGH_PRIORITY = 1500
export default class CustomRenderer extends BaseRenderer {
constructor(eventBus, bpmnRenderer, modeling) {
super(eventBus, HIGH_PRIORITY);
this.bpmnRenderer = bpmnRenderer;
this.modeling = modeling;
}
canRender(element) {
// ignore labels
return !element.labelTarget;
}
drawShape(parentNode, element) {
console.log(element)
const type = element.type // 获取到类型
// if (customElements.includes(type)) { // or customConfig[type]
if (type === 'bpmn:Task') { // or customConfig[type]
const { url, attr } = customConfig[type]
// const customIcon = svgCreate('image', {
// ...attr,
// href: url
// })
// let color = element.businessObject.color;
// console.log(element.businessObject.di);
// element.businessObject.di.set('bioc:stroke', "red")
// const shape = this.bpmnRenderer.drawShape(parentNode, element);
const myShape = svgCreate('rect',{
"width":attr.width,
"height":attr.height,
"fill":"transparent",
"stroke":"black",
"stroke-width":"1",
})
svgAppend(parentNode, myShape)
const myLine = svgCreate('line',{
"x1":"0",
"y1":"1.5em",
"x2":attr.width,
"y2":"1.5em",
"stroke":"black",
"stroke-width":"1",
})
svgAppend(parentNode, myLine);
const tableName = svgCreate('text', {
"font-size": "14",
"fill": "blue",
"x":attr.width/2,
"dy":"15",
"text-anchor":"middle",
})
tableName.innerHTML="测试一下 [sys_org_dept]";
svgAppend(parentNode, tableName);
const customDiv = svgCreate('text', {
"font-size": "14",
"font-family":'Courier New',
"fill": "#000"
})
customDiv.innerHTML="<tspan x='8' dy='38' fill='green'>名称</tspan>\n" +
" <tspan x='8' dy='1.3em'>crtim</tspan>\n" +
" <tspan x='8' dy='1.3em'>uptim</tspan>\n" +
" <tspan x='8' dy='1.3em'>crman</tspan>\n" +
" <tspan x='70' y='38' fill='green'>描述</tspan>\n" +
" <tspan x='70' dy='1.3em'>创建时间</tspan>\n" +
" <tspan x='70' dy='1.3em'>更新时间</tspan>\n" +
" <tspan x='70' dy='1.3em'>创建人</tspan>" ;
element['width'] = attr.width // 这里我是取了巧, 直接修改了元素的宽高
element['height'] = attr.height
// let color = element.businessObject.color
// element.businessObject.di.set('bioc:stroke', color)
// svgAppend(parentNode, customIcon)
svgAppend(parentNode, customDiv)
// 判断是否有name属性来决定是否要渲染出label
// if (!hasLabelElements.includes(type) && element.businessObject.name) {
// const text = svgCreate('text', {
// x: attr.x,
// y: attr.y + attr.height + 20,
// "font-size": "14",
// "fill": "#000"
// })
// text.innerHTML = element.businessObject.name
// svgAppend(parentNode, text)
// console.log(text)
// }
// this.modeling.resizeShape(element, {
// x: element.x,
// y: element.y,
// width: element['width'] / 2,
// height: element['height'] / 2
// })
// return customIcon
return myShape
}
// else if (true) {
// console.log('我是绿色的')
// // let color = element.businessObject.color
// // element.businessObject.di.set('bioc:stroke', color)
// const shape = this.bpmnRenderer.drawShape(parentNode, element)
// return shape
// }
const shape = this.bpmnRenderer.drawShape(parentNode, element)
return shape
}
getShapePath(shape) {
return this.bpmnRenderer.getShapePath(shape);
}
}
CustomRenderer.$inject = ['eventBus', 'bpmnRenderer', 'modeling'];
-9
View File
@@ -1,9 +0,0 @@
import CustomPalette from './CustomPalette'
import CustomRenderer from './CustomRenderer'
export default {
__init__: ['customPalette', 'customRenderer'],
customPalette: ['type', CustomPalette],
customRenderer: ['type', CustomRenderer]
}
@@ -1,298 +0,0 @@
/**
* A palette that allows you to create BPMN _and_ custom elements.
*/
import { assign } from "min-dash";
import { getNodeNum } from './store';
let numFlag="x";
export default function PaletteProvider(palette, create, elementFactory,handTool,lassoTool,
spaceTool, globalConnect, bpmnFactory) {
this.create = create
this.elementFactory = elementFactory
this.handTool = handTool;
this.lassoTool = lassoTool;
this.spaceTool = spaceTool;
this.globalConnect = globalConnect
this.bpmnFactory = bpmnFactory
palette.registerProvider(this);
if(window.location.hash.indexOf("uuid=")>0){
numFlag = window.location.hash.split("uuid=")[1];
}else if(window.location.hash.indexOf("?id=")>0){
numFlag = window.location.hash.split("?id=")[1];
}
console.log("numFlag1:"+numFlag);
}
PaletteProvider.$inject = [
'palette',
'create',
'elementFactory',
"handTool",
"lassoTool",
"spaceTool",
'globalConnect',
'bpmnFactory'
]
PaletteProvider.prototype.getPaletteEntries = function(element) {
const {
create,
elementFactory,
bpmnFactory,
handTool,
lassoTool,
spaceTool,
globalConnect,
translate,
} = this;
// function createTask() {
// return function(event) {
// const id ="N"+(getNodeNum(numFlag)-0+1);
// const businessObject = bpmnFactory.create('bpmn:Task', { id,custom: 2 });
// // businessObject['custom'] = 1 // 这样不行
// const shape = elementFactory.createShape({
// type: 'bpmn:Task',
// businessObject
// });
// const label = elementFactory.createLabel();
// console.log(shape) // 只在拖动或者点击时触发
// console.log(label) // 只在拖动或者点击时触发
// create.start(event, shape);
// // create.start(event, label);
// }
// }
function createUserNode() {
return function(event) {
console.log(numFlag);
const id ="N"+(getNodeNum(numFlag)-0+1);
const businessObject = bpmnFactory.create('bpmn:UserTask', {id, custom: 2,name:"审批节点" });
// businessObject['custom'] = 1 // 这样不行
const shape = elementFactory.createShape({
type: 'bpmn:UserTask',
businessObject
});
const label = elementFactory.createLabel();
console.log(shape) // 只在拖动或者点击时触发
console.log(label) // 只在拖动或者点击时触发
create.start(event, shape);
// create.start(event, label);
}
}
// function createStratEvent() {
// return function(event) {
// const shape = elementFactory.createShape({
// type: 'bpmn:StartEvent'
// });
// create.start(event, shape);
// }
// }
// function createGateway() {
// return function(event) {
// const shape = elementFactory.createShape({
// type: 'bpmn:ExclusiveGateway'
// });
// create.start(event, shape);
// }
// }
function createAction(type, group, className, title, options) {
function createListener(event) {
const id ="N"+(getNodeNum(numFlag)-0+1);
let name="";
if(type==="bpmn:ReceiveTask"){
name="抄送节点";
}else if(type==="bpmn:ScriptTask"){
name="脚本节点";
}else if(type==="bpmn:ServiceTask"){
name="服务节点";
}else if(type==="bpmn:ExclusiveGateway"){
name="互斥网关";
}else if(type==="bpmn:ParallelGateway"){
name="并行网关";
}
console.log(type);
const taskBusinessObject = bpmnFactory.create(type, { id,name,custom:2 })
var shape = elementFactory.createShape(assign({ type: type }, options,{ businessObject: taskBusinessObject }));
// var shape = elementFactory.createShape(assign({ type: type }, options));
if (options) {
shape.businessObject.di.isExpanded = options.isExpanded;
}
create.start(event, shape);
}
var shortType = type.replace(/^bpmn:/, "");
return {
group: group,
className: className,
title: title || translate("Create {type}", { type: shortType }),
action: {
dragstart: createListener,
click: createListener
}
};
}
return {
"hand-tool": {
group: "tools",
className: "bpmn-icon-hand-tool",
title: "激活抓手工具",
action: {
click: function (event) {
handTool.activateHand(event);
}
}
},
"lasso-tool": {
group: "tools",
className: "bpmn-icon-lasso-tool",
title: "激活套索工具",
action: {
click: function (event) {
lassoTool.activateSelection(event);
}
}
},
"space-tool": {
group: "tools",
className: "bpmn-icon-space-tool",
title: "激活空间工具",
action: {
click: function (event) {
spaceTool.activateSelection(event);
}
}
},
"global-connect-tool": {
group: "tools",
className: "bpmn-icon-connection-multi",
title: "激活连接工具",
action: {
click: function (event) {
globalConnect.toggle(event);
}
}
},
"tool-separator": {//工具分割线
group: 'tools',
separator: true
},
// "create.user-task": createAction(
// "bpmn:UserTask",
// "activity",
// "bpmn-icon-user-task",
// "添加审批节点"
// ),
// 'create.user-task2': {
// group: 'model',
// className: 'bpmn-icon-user-task',
// title: '添加审批节点',
// action: {
// dragstart: createUserNode(),
// click: createUserNode()
// }
// },
'create.user-task': {
group: 'model',
className: 'bpmn-icon-user-task',
title: '添加审批节点',
action: {
dragstart: createUserNode(),
click: createUserNode()
}
},
"create.receive-task": createAction(
"bpmn:ReceiveTask",
"activity",
"bpmn-icon-receive-task",
"添加抄送节点"
),
"create.script-task": createAction(
"bpmn:ScriptTask",
"activity",
"bpmn-icon-script-task",
"添加脚本节点"
),
"create.service-task": createAction(
"bpmn:ServiceTask",
"activity",
"bpmn-icon-service-task",
"添加服务节点"
),
"create.exclusive-gateway": createAction(
"bpmn:ExclusiveGateway",
"activity",
"bpmn-icon-gateway-xor",
"添加互斥网关"
),
"create.parallel-gateway": createAction(
"bpmn:ParallelGateway",
"activity",
"bpmn-icon-gateway-parallel",
"添加并行网关"
),
// "create.data-object": createAction(
// "bpmn:DataObject",
// "activity",
// "bpmn-icon-data-object",
// "添加数据对象"
// ),
// "create.data-store": createAction(
// "bpmn:DataStore",
// "activity",
// "bpmn-icon-data-store",
// "添加数据存储"
// ),
"create.participant": createAction(
"bpmn:Participant",
"activity",
"bpmn-icon-participant",
"添加泳道"
),
"create.group": createAction(
"bpmn:Group",
"activity",
"bpmn-icon-group",
"添加分组"
),
// "create.data-store": createAction(
// "bpmn:DataStore",
// "activity",
// "bpmn-icon-data-store",
// "添加数据存储"
// ),
// 'create.user-node': {
// group: 'model',
// className: 'bpmn-icon-user-task',
// title: '添加审批节点',
// action: {
// dragstart: createUserNode(),
// click: createUserNode()
// }
// },
// 'create.lindaidai-task': {
// group: 'model',
// className: 'icon-custom lindaidai-task',
// title: '创建一个类型为lindaidai-task的任务节点',
// action: {
// dragstart: createTask(),
// click: createTask()
// }
// },
// 'create.exclusive-gateway': {
// group: 'gateway',
// className: 'bpmn-icon-gateway-none',
// title: '创建一个网关',
// action: {
// dragstart: createGateway(),
// click: createGateway()
// }
// }
}
}
@@ -1,112 +0,0 @@
import { assign } from "min-dash";
export default function PaletteProvider(
palette,
create,
elementFactory,
handTool,
lassoTool,
spaceTool,
globalConnect,
translate
) {
this.create = create;
this.elementFactory = elementFactory;
this.handTool = handTool;
this.lassoTool = lassoTool;
this.spaceTool = spaceTool;
this.globalConnect = globalConnect;
this.translate = translate;
palette.registerProvider(this);
}
PaletteProvider.$inject = [
"palette",
"create",
"elementFactory",
"handTool",
"lassoTool",
"spaceTool",
"globalConnect",
"translate"
];
PaletteProvider.prototype.getPaletteEntries = function (element) {
const {
create,
elementFactory,
handTool,
lassoTool,
spaceTool,
globalConnect,
translate
} = this;
function createAction(type, group, className, title, options) {
function createListener(event) {
var shape = elementFactory.createShape(assign({ type: type }, options));
if (options) {
shape.businessObject.di.isExpanded = options.isExpanded;
}
create.start(event, shape);
}
var shortType = type.replace(/^bpmn:/, "");
return {
group: group,
className: className,
title: title || translate("Create {type}", { type: shortType }),
action: {
dragstart: createListener,
click: createListener
}
};
}
return {
"lasso-tool": {
group: "tools",
className: "bpmn-icon-lasso-tool",
title: "Activate the lasso tool",
action: {
click: function (event) {
lassoTool.activateSelection(event);
}
}
},
"tool-separator": {
group: "tools",
separator: true
},
"create.start-event": createAction(
"bpmn:StartEvent",
"event",
"bpmn-icon-start-event-none",
"创建开始节点"
),
"create.end-event": createAction(
"bpmn:EndEvent",
"event",
"bpmn-icon-end-event-none",
"创建结束节点"
),
"create.user-task": createAction(
"bpmn:UserTask",
"activity",
"bpmn-icon-user-task",
"创建用户任务"
),
"create.exclusive-gateway": createAction(
"bpmn:ExclusiveGateway",
"gateway",
"bpmn-icon-gateway-xor",
"创建排他网关"
)
};
};
@@ -1,284 +0,0 @@
/* eslint-disable no-unused-vars */
import inherits from 'inherits'
import BaseRenderer from 'diagram-js/lib/draw/BaseRenderer'
import {
isObject,
assign,
forEach
} from 'min-dash';
import {
append as svgAppend,
create as svgCreate,
classes as svgClasses
} from 'tiny-svg'
import { customElements, customConfig, hasLabelElements } from '../../utils/util'
/**
* A renderer that knows how to render custom elements.
*/
export default function CustomRenderer(eventBus, styles, textRenderer) {
BaseRenderer.call(this, eventBus, 2000)
var computeStyle = styles.computeStyle
function renderLabel(parentGfx, label, options) {
options = assign({
size: {
width: 100
}
}, options);
var text = textRenderer.createText(label || '', options);
svgClasses(text).add('djs-label');
svgAppend(parentGfx, text);
return text;
}
this.drawCustomElements = function(parentNode, element) {
// console.log(678)
// console.log(parentNode)
console.log("自定义渲染")
console.log(element)
const type = element.type // 获取到类型
if (type !== 'label') {
console.log("type="+type);
if (type === 'bpmn:Task') { // or customConfig[type]
console.log("重构")
let data={};
data.tabna = "sys_org [组织架构汇总表]";
data.fields = [
{name:'id',comet:'主键'},
{name:'name',comet:'名称'}
];
if(element.businessObject.$attrs.data){
data=JSON.parse(element.businessObject.$attrs.data)
}
const { url, attr } = customConfig[type];
attr.height=44+data.fields.length*18.3;
// const customIcon = svgCreate('image', {
// ...attr,
// href: url
// })
// let color = element.businessObject.color;
// console.log(element.businessObject.di);
// element.businessObject.di.set('bioc:stroke', "red")
// const shape = this.bpmnRenderer.drawShape(parentNode, element);
const myShape = svgCreate('rect',{
"width":attr.width,
"height":attr.height,
"fill":"transparent",
"stroke":"black",
"stroke-width":"1",
})
svgAppend(parentNode, myShape)
const myLine = svgCreate('line',{
"x1":"0",
"y1":"1.5em",
"x2":attr.width,
"y2":"1.5em",
"stroke":"black",
"stroke-width":"1",
})
svgAppend(parentNode, myLine);
const tableName = svgCreate('text', {
"font-size": "14",
"fill": "blue",
"x":attr.width/2,
"dy":"15",
"text-anchor":"middle",
})
// tableName.innerHTML="测试一下 [sys_org_dept]";
tableName.innerHTML=data.tabna;
svgAppend(parentNode, tableName);
const customDiv = svgCreate('text', {
"font-size": "14",
"font-family":'Courier New',
"fill": "#000"
})
customDiv.innerHTML="<tspan x='8' dy='38' fill='green'>名称</tspan>";
for (const field of data.fields) {
customDiv.innerHTML+="<tspan x='8' dy='1.3em'>"+field.name+"</tspan>";
}
customDiv.innerHTML+="<tspan x='70' y='38' fill='green'>描述</tspan>";
for (const field of data.fields) {
customDiv.innerHTML+="<tspan x='70' dy='1.3em'>"+field.comet+"</tspan>";
}
element['width'] = attr.width // 这里我是取了巧, 直接修改了元素的宽高
element['height'] = attr.height
// let color = element.businessObject.color
// element.businessObject.di.set('bioc:stroke', color)
// svgAppend(parentNode, customIcon)
svgAppend(parentNode, customDiv)
// 判断是否有name属性来决定是否要渲染出label
// if (!hasLabelElements.includes(type) && element.businessObject.name) {
// const text = svgCreate('text', {
// x: attr.x,
// y: attr.y + attr.height + 20,
// "font-size": "14",
// "fill": "#000"
// })
// text.innerHTML = element.businessObject.name
// svgAppend(parentNode, text)
// console.log(text)
// }
// this.modeling.resizeShape(element, {
// x: element.x,
// y: element.y,
// width: element['width'] / 2,
// height: element['height'] / 2
// })
// return customIcon
return myShape
}else if(type === 'bpmn:UserTask'){
const { url, attr } = customConfig[type];
const myShape = svgCreate('rect',{
"width":attr.width,
"height":attr.height,
"fill":"transparent",
"stroke":"black",
"stroke-width":"2",
"rx":'10'
})
svgAppend(parentNode, myShape);
const titleDiv = svgCreate('text', {
"font-size": "14",
"font-family":'Courier New',
"fill": "#000",
"text-anchor":"middle",
})
titleDiv.innerHTML="<tspan x='50' dy='20' fill='black'>"+element.id+"</tspan>";
svgAppend(parentNode, titleDiv);
const contDiv = svgCreate('text', {
"font-size": "12",
"font-family":"Courier New",
"fill": "#000",
"text-anchor":"middle",
})
contDiv.innerHTML="";
if(element.businessObject.name){
const size= Math.ceil(element.businessObject.name.length/7);
if(size===1){
contDiv.innerHTML+="<tspan x='50' dy='48'>"+element.businessObject.name+"</tspan>";
}else if(size===2){
contDiv.innerHTML+="<tspan x='50' dy='45'>"+element.businessObject.name.substring(0,7)+"</tspan>";
contDiv.innerHTML+="<tspan x='50' dy='16'>"+element.businessObject.name.substring(7)+"</tspan>";
}else if(size===3){
contDiv.innerHTML+="<tspan x='50' dy='42'>"+element.businessObject.name.substring(0,7)+"</tspan>";
contDiv.innerHTML+="<tspan x='50' dy='16'>"+element.businessObject.name.substring(7,14)+"</tspan>";
contDiv.innerHTML+="<tspan x='50' dy='16'>"+element.businessObject.name.substring(14)+"</tspan>";
}else if(size>3){
contDiv.innerHTML+="<tspan x='50' dy='42'>"+element.businessObject.name.substring(0,7)+"</tspan>";
contDiv.innerHTML+="<tspan x='50' dy='16'>"+element.businessObject.name.substring(7,14)+"</tspan>";
contDiv.innerHTML+="<tspan x='50' dy='16'>"+element.businessObject.name.substring(14,19)+"..."+"</tspan>";
}
// else{
// for(let i=0;i<size;i++){
// if(i!==size-1){
// if(i===0){
// contDiv.innerHTML+="<tspan x='50' dy='36'>"+element.businessObject.name.substring(i*7,7*(i+1))+"</tspan>";
// }else{
// contDiv.innerHTML+="<tspan x='50' dy='16'>"+element.businessObject.name.substring(i*7,7*(i+1))+"</tspan>";
// }
// }else{
// if(i===0){
// contDiv.innerHTML+="<tspan x='50' dy='38'>"+element.businessObject.name.substring(i*7)+"</tspan>";
// }else{
// contDiv.innerHTML+="<tspan x='50' dy='16'>"+element.businessObject.name.substring(i*7)+"</tspan>";
// }
// }
// }
// }
}
// contDiv.innerHTML="<tspan x='8' dy='38'>"+element.businessObject.name+"</tspan>";
svgAppend(parentNode, contDiv);
if(element.id==="N1"){
const imgPath = svgCreate('path', {
"d":"m 17,15 c 0.234,-0.01 5.604,0.008 8.029,0.004 0.808,0 1.271,-0.172 1.417,-0.752 0.227,-0.898 -0.334,-1.314 -1.338,-1.316 -2.467,-0.01 -7.886,-0.004 -8.108,-0.004 -0.014,-0.079 0.016,-0.533 0,-0.61 0.195,-0.042 8.507,0.006 9.616,0.002 0.877,-0.007 1.35,-0.438 1.353,-1.208 0.003,-0.768 -0.479,-1.09 -1.35,-1.091 -2.968,-0.002 -9.619,-0.013 -9.619,-0.013 v -0.591 c 0,0 5.052,-0.016 7.225,-0.016 0.888,-0.002 1.354,-0.416 1.351,-1.193 -0.006,-0.761 -0.492,-1.196 -1.361,-1.196 -3.473,-0.005 -10.86,-0.003 -11.0829995,-0.003 -0.022,-0.047 -0.045,-0.094 -0.069,-0.139 0.3939995,-0.319 2.0409995,-1.626 2.4149995,-2.017 0.469,-0.4870005 0.519,-1.1650005 0.162,-1.6040005 -0.414,-0.511 -0.973,-0.5 -1.48,-0.236 -1.4609995,0.764 -6.5999995,3.6430005 -7.7329995,4.2710005 -0.9,0.499 -1.516,1.253 -1.882,2.19 -0.37000002,0.95 -0.17,2.01 -0.166,2.979 0.004,0.718 -0.27300002,1.345 -0.055,2.063 0.629,2.087 2.425,3.312 4.859,3.318 4.6179995,0.014 9.2379995,-0.139 13.8569995,-0.158 0.755,-0.004 1.171,-0.301 1.182,-1.033 0.012,-0.754 -0.423,-0.969 -1.183,-0.973 -1.778,-0.01 -5.824,-0.004 -6.04,-0.004 10e-4,-0.084 0.003,-0.586 10e-4,-0.67 z",
"fill": "white",
"stroke-width": "0.5px",
"stroke": "black"
})
svgAppend(parentNode, imgPath);
}else{
const imgPath1 = svgCreate('path', {
"d":"m 15,12 c 0.909,-0.845 1.594,-2.049 1.594,-3.385 0,-2.554 -1.805,-4.62199999 -4.357,-4.62199999 -2.55199998,0 -4.28799998,2.06799999 -4.28799998,4.62199999 0,1.348 0.974,2.562 1.89599998,3.405 -0.52899998,0.187 -5.669,2.097 -5.794,4.7560005 v 6.718 h 17 v -6.718 c 0,-2.2980005 -5.5279996,-4.5950005 -6.0509996,-4.7760005 zm -8,6 l 0,5.5 m 11,0 l 0,-5",
"fill": "white",
"stroke-width": "0.5px",
"stroke": "black"
})
const imgPath2 = svgCreate('path', {
"d":"m 15,12 m 2.162,1.009 c 0,2.4470005 -2.158,4.4310005 -4.821,4.4310005 -2.66499998,0 -4.822,-1.981 -4.822,-4.4310005 ",
"fill": "white",
"stroke-width": "0.5px",
"stroke": "black"
})
const imgPath3 = svgCreate('path', {
"d":"m 15,12 m -6.9,-3.80 c 0,0 2.25099998,-2.358 4.27399998,-1.177 2.024,1.181 4.221,1.537 4.124,0.965 -0.098,-0.57 -0.117,-3.79099999 -4.191,-4.13599999 -3.57499998,0.001 -4.20799998,3.36699999 -4.20699998,4.34799999 z",
"fill": "black",
"stroke-width": "0.5px",
"stroke": "black"
})
svgAppend(parentNode, imgPath1);
svgAppend(parentNode, imgPath2);
svgAppend(parentNode, imgPath3);
}
return myShape
}
// else if (true) {
// console.log('我是绿色的')
// // let color = element.businessObject.color
// // element.businessObject.di.set('bioc:stroke', color)
// const shape = this.bpmnRenderer.drawShape(parentNode, element)
// return shape
// }
const shape = this.bpmnRenderer.drawShape(parentNode, element)
return shape
} else {
// element
const shape = this.bpmnRenderer.drawShape(parentNode, element)
return shape
}
}
}
inherits(CustomRenderer, BaseRenderer)
CustomRenderer.$inject = ['eventBus', 'styles', 'textRenderer']
CustomRenderer.prototype.canRender = function(element) {
// ignore labels
return true
// return !element.labelTarget;
}
CustomRenderer.prototype.drawShape = function(p, element) {
console.log(element)
console.log(element.type)
if (customElements.includes(element.type)) {
return this.drawCustomElements(p, element)
}
}
CustomRenderer.prototype.getShapePath = function(shape) {
console.log(shape)
}
@@ -1,9 +0,0 @@
import CustomPalette from './CustomPalette'
import CustomRenderer from './CustomRenderer'
// import CustomContextPadProvider from './CustomContextPadProvider'
export default {
__init__: ['paletteProvider', 'customRenderer', 'contextPadProvider'],
paletteProvider: ['type', CustomPalette],
customRenderer: ['type', CustomRenderer],
// contextPadProvider: ['type', CustomContextPadProvider]
}
@@ -1,53 +0,0 @@
const nodeNumMap = { } as any;
const lineNumMap = { } as any;
export const putNodeNum = (id:string,num: Number) => {
let flag=false;
for (let key in nodeNumMap) {
if(key==id){
flag=true;
if(num>nodeNumMap[key]){
nodeNumMap[key]=num;
}
break;
}
}
if(!flag){
nodeNumMap[id]=num;
}
};
export const getNodeNum = (id:string) => {
for (let key in nodeNumMap) {
if(key==id){
return nodeNumMap[id];
}
}
return null;
};
export const putLineNum = (id:string,num: Number) => {
let flag=false;
for (let key in lineNumMap) {
if(key==id){
flag=true;
if(num>lineNumMap[key]){
lineNumMap[key]=num;
}
break;
}
}
if(!flag){
lineNumMap[id]=num;
}
};
export const getLineNum = (id:string) => {
for (let key in lineNumMap) {
if(key==id){
return lineNumMap[id];
}
}
return null;
};
-19
View File
@@ -1,19 +0,0 @@
import Modeler from 'bpmn-js/lib/Modeler'
import inherits from 'inherits'
import CustomModule from './custom'
export default function CustomModeler(options) {
Modeler.call(this, options)
this._customElements = []
}
inherits(CustomModeler, Modeler)
CustomModeler.prototype._modules = [].concat(
CustomModeler.prototype._modules, [
CustomModule
]
)
-343
View File
@@ -1,343 +0,0 @@
<template>
<div>
<div style="margin-bottom: 10px">
<!-- <svg class="icon" aria-hidden="true" style="height: 25px;width: 40px">-->
<!-- <use xlink:href="#icon-shangchuan"></use>-->
<!-- </svg>-->
<!-- <el-button type='primary' @click='impXml' plain :icon='Upload'>导入</el-button>-->
<!-- <el-button type='primary' @click='expXML' plain :icon='Download'>导出XML</el-button>-->
<el-button type='primary' @click='expSVG' plain :icon='Download'>导出SVG</el-button>
<el-button type='primary' @click='codeShow' plain :icon='View'>代码预览</el-button>
<span style="color: red;margin-left: 10px">注:双击节点或连线编辑信息</span>
</div>
<div class='containers'>
<div class='canvas' ref='canvasRef' style='height: 1000px;'></div>
<NodeModal ref='nodeModalRef' @close='closeNodeModal' />
</div>
<CodeModal ref='codeModalRef'/>
</div>
</template>
<script lang='ts' setup>
// import BpmnModeler from 'bpmn-js/lib/Modeler'
import { Download,Upload,View} from '@element-plus/icons-vue';
import { defineExpose, onMounted, reactive, ref, toRefs } from 'vue';
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 customModule from './custom';
import CustomModeler from './customModeler';
import NodeModal from './modal.vue';
import CodeModal from './code.vue';
import { putNodeNum, putLineNum, getLineNum, getNodeNum } from './customModeler/custom/store';
import { useRoute } from 'vue-router';
const route = useRoute();
const canvasRef = ref();
const state = reactive({
data: [] as any,
bpmnModeler: {} as any,
numFlag: 'x' as any,
});
const props = defineProps({
prxml: String,
});
const { bpmnModeler } = toRefs(state);
onMounted(async () => {
if (route.query?.uuid) {
state.numFlag = route.query?.uuid;
} else if (route.query?.id) {
state.numFlag = route.query?.id;
}
console.log('numFlag2:' + state.numFlag);
bpmnModeler.value = new CustomModeler({
container: canvasRef.value,
additionalModules: [{
__init__: [
'labelEditingProvider',
],
labelEditingProvider: ['value', null],
},
],
});
bpmnModeler.value.get('keyboard').bind(document);
if (props.prxml) {
bpmnModeler.value.importXML(props.prxml, (err: any) => {
if (err) {
console.error(err);
} else {
console.log('ok');
}
});
} else {
bpmnModeler.value.importXML(defxml, (err: any) => {
if (err) {
console.error(err);
} else {
console.log('ok');
}
});
}
addModelerListener();
addEventBusListener();
});
const addModelerListener = () => {
// const bpmnjs = this.bpmnModeler
// const that = this
// 这里我是用了一个forEach给modeler上添加要绑定的事件
const events = ['shape.added', 'shape.move.end', 'shape.removed','connection.added', 'connect.end', 'connect.move'];
events.forEach(function(event) {
bpmnModeler.value.on(event, e => {
const elementRegistry = bpmnModeler.value.get('elementRegistry');
const shape = e.element ? elementRegistry.get(e.element.id) : e.shape;
if (event == 'shape.added') {
if (e.element.id.substring(0, 1) == 'N' && e.element.id.substring(0, 2) !== 'NS' && e.element.id.substring(0, 2) !== 'NE') {
// shape.name="审批节点"
putNodeNum(state.numFlag, e.element.id.substring(1) - 0);
}
} else if (event == 'connection.added') {
// console.log("创建了线段");
// console.log(e.element);
if (e.element.id.substring(0, 1) == 'L') {
putLineNum(state.numFlag, e.element.id.substring(1) - 0);
}else{
//如果更新Line的ID?
// bpmnModeler.value.get('modeling').updateProperties(e.element, {
// id: "L"+(getLineNum(state.numFlag)-0+1),
// // data:JSON.stringify(data),
// });
// putLineNum(state.numFlag, (getLineNum(state.numFlag)-0+1));
}
}
});
});
};
let currNode = {};
// let lastNum=10;
const addEventBusListener = () => {
const eventBus = bpmnModeler.value.get('eventBus'); // 需要使用eventBus
const eventTypes = ['element.dblclick']; // 需要监听的事件集合
eventTypes.forEach(function(eventType) {
eventBus.on(eventType, function(e) {
if (!e || e.element.type == 'bpmn:Process') return;
const elementRegistry = bpmnModeler.value.get('elementRegistry');
const shape = elementRegistry.get(e.element.id); // 传递id进去
// if(e.element.id.substring(0,1)!="N"){
//
// // bpmnModeler.value.get('modeling').updateProperties(shape,{
// // name: '我哎你'
// // })
//
// // // e.element.id="N"+(++lastNum);
// // shape.id="N"+(++lastNum);
// ++lastNum;
// shape.id="N"+lastNum;
// shape.businessObject.id="N"+lastNum;
//
// console.log('新增了shape');
// // console.log(e.element.id);
// // 展示新增图形的属性
// }
console.log('双击了shape');
console.log(shape);
nodeModalRef.value.open(shape);
currNode = shape;
// openNodeModal(shape);
// console.log(shape.businessObject.id);
// shape.businessObject.name='李四';
// bpmnModeler.value.get('modeling').updateProperties(shape,{
// name: '我是修改后的Task名称'
// })
// this.getModeling().updateProperties(this.getShape(), {
// [modelName]: multiple ? [newElement] : newElement,
// });
console.log(shape); // {Shape}
// console.log(e.element) // {Shape}
// console.log(JSON.stringify(shape)===JSON.stringify(e.element)) // true
});
});
};
const nodeModalRef = ref();
// const openNodeModal = () => {
// nodeModalRef.value.open();
// };
const closeNodeModal = (data: any) => {
if(data.conds){
bpmnModeler.value.get('modeling').updateProperties(currNode, {
name: data.name,
conds: data.conds,
});
}else{
bpmnModeler.value.get('modeling').updateProperties(currNode, {
name: data.name,
hamen: data.hamen,
flway: data.flway,
hatyp: data.hatyp,
// data:JSON.stringify(data),
});
}
};
const getXml = async () => {
let backXml = '';
await bpmnModeler.value.saveXML({ format: true }).then(res => {
backXml = res.xml;
});
return backXml;
};
defineExpose({ getXml });
const defxml = '<?xml version="1.0" encoding="UTF-8"?>\n' +
'<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" xmlns:activiti="http://activiti.org/bpmn" id="sample-diagram" targetNamespace="http://activiti.org/bpmn" xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL BPMN20.xsd">\n' +
' <bpmn2:process id="Process_1" name="1" isExecutable="true">\n' +
' <bpmn2:startEvent id="NS" name="开始节点">\n' +
' <bpmn2:outgoing>L1</bpmn2:outgoing>\n' +
' </bpmn2:startEvent>\n' +
' <bpmn2:sequenceFlow id="L1" sourceRef="NS" targetRef="N1" />\n' +
' <bpmn2:endEvent id="NE" name="结束节点">\n' +
' <bpmn2:incoming>L2</bpmn2:incoming>\n' +
' </bpmn2:endEvent>\n' +
' <bpmn2:userTask id="N1" name="起草节点" activiti:assignee="l4" activiti:candidateUsers="">\n' +
' <bpmn2:documentation>起草节点,表单数据一般从绑定的表单提取</bpmn2:documentation>\n' +
' <bpmn2:extensionElements>\n' +
' <activiti:formProperty id="userid" type="string" />\n' +
' <activiti:formProperty id="money" type="int" />\n' +
' <activiti:properties>\n' +
' <activiti:property name="编辑" value="edit" />\n' +
' <activiti:property name="撤回" value="back" />\n' +
' <activiti:property name="提交" value="commit" />\n' +
' </activiti:properties>\n' +
' </bpmn2:extensionElements>\n' +
' <bpmn2:incoming>L1</bpmn2:incoming>\n' +
' <bpmn2:outgoing>L2</bpmn2:outgoing>\n' +
' </bpmn2:userTask>\n' +
' <bpmn2:sequenceFlow id="L2" sourceRef="N1" targetRef="NE" />\n' +
' </bpmn2:process>\n' +
' <bpmndi:BPMNDiagram id="BPMNDiagram_1">\n' +
' <bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_1">\n' +
' <bpmndi:BPMNEdge id="Flow_1u6pmzo_di" bpmnElement="L1">\n' +
' <di:waypoint x="360" y="78" />\n' +
' <di:waypoint x="360" y="160" />\n' +
' </bpmndi:BPMNEdge>\n' +
' <bpmndi:BPMNEdge id="Flow_0rj5mf6_di" bpmnElement="L2">\n' +
' <di:waypoint x="360" y="240" />\n' +
' <di:waypoint x="360" y="452" />\n' +
' </bpmndi:BPMNEdge>\n' +
' <bpmndi:BPMNShape id="Event_0byql27_di" bpmnElement="NS">\n' +
' <dc:Bounds x="342" y="42" width="36" height="36" />\n' +
' <bpmndi:BPMNLabel>\n' +
' <dc:Bounds x="339" y="12" width="43" height="14" />\n' +
' </bpmndi:BPMNLabel>\n' +
' </bpmndi:BPMNShape>\n' +
' <bpmndi:BPMNShape id="Activity_0g48n8q_di" bpmnElement="N1">\n' +
' <dc:Bounds x="310" y="160" width="100" height="80" />\n' +
' </bpmndi:BPMNShape>\n' +
' <bpmndi:BPMNShape id="Event_1h4oob7_di" bpmnElement="NE">\n' +
' <dc:Bounds x="342" y="452" width="36" height="36" />\n' +
' <bpmndi:BPMNLabel>\n' +
' <dc:Bounds x="339" y="495" width="43" height="14" />\n' +
' </bpmndi:BPMNLabel>\n' +
' </bpmndi:BPMNShape>\n' +
' </bpmndi:BPMNPlane>\n' +
' </bpmndi:BPMNDiagram>\n' +
'</bpmn2:definitions>\n';
const impXml=()=>{
}
const expXML=async ()=>{
await bpmnModeler.value.saveXML({ format: true }).then(res => {
download(res.xml, 'process', 'bpmn');
});
}
const expSVG=async ()=>{
await bpmnModeler.value.saveSVG({ format: true }).then(res => {
download(res.svg, 'process', 'svg');
});
}
//文本下载
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对象
};
const codeModalRef = ref() as any;
const codeShow=async ()=>{
await bpmnModeler.value.saveXML({ format: true }).then(res => {
codeModalRef.value.openModal(res.xml);
});
}
</script>
<style scoped>
.containers {
position: absolute;
background-color: #ffffff;
width: 100%;
height: 100%;
background: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAiIGhlaWdodD0iNDAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PHBhdHRlcm4gaWQ9ImEiIHdpZHRoPSI0MCIgaGVpZ2h0PSI0MCIgcGF0dGVyblVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHBhdGggZD0iTTAgMTBoNDBNMTAgMHY0ME0wIDIwaDQwTTIwIDB2NDBNMCAzMGg0ME0zMCAwdjQwIiBmaWxsPSJub25lIiBzdHJva2U9IiNlMGUwZTAiIG9wYWNpdHk9Ii4yIi8+PHBhdGggZD0iTTQwIDBIMHY0MCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjZTBlMGUwIi8+PC9wYXR0ZXJuPjwvZGVmcz48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSJ1cmwoI2EpIi8+PC9zdmc+') repeat !important;
}
.dark .containers{
background-color: #666 !important;
}
.canvas {
width: 100%;
height: 100%;
}
.panel {
position: absolute;
right: 0;
top: 0;
width: 300px;
}
</style>
-121
View File
@@ -1,121 +0,0 @@
<template>
<el-dialog v-model='dialogVisible' title='公式定义器' draggable width='900px'>
<div style='height: 440px'>
<el-row gutter='8'>
<el-col :span='8'>
<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: 8px;width: 34px'>
<el-icon class='el-icon--center'>
<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='rootNode'>根节点</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 highlight-current ref='treeRef' class='filter-tree' :data='state.data' :props='defaultProps' :filter-node-method='filterNode' @node-click='nodeClick' />
</div>
</el-card>
</el-col>
<el-col :span='16' >
<el-card class='box-card' style='height: 100%;' body-style='height: 100%;overflow: auto'>
<template #header>
<div class='card-header'>
<div class='zjustify'>
<div>
<!-- <el-input v-model='state.form.name' placeholder='输入名称回车查询' class='list-search' clearable @keyup.enter='listQuery()' />-->
<!-- <el-button type='primary' @click='listQuery()' plain>查 询</el-button>-->
</div>
<div>
<el-button type='primary' @click='handleConfirm'>确 认</el-button>
<el-button @click='dialogVisible = false'>取 消</el-button>
</div>
</div>
</div>
</template>
<div style='margin-bottom: 45px'>
<el-input type='textarea' placeholder='测试' :rows='8' v-model='state.form.func'/>
</div>
</el-card>
</el-col>
</el-row>
</div>
</el-dialog>
</template>
<script lang='ts' setup>
import { defineExpose, onMounted, reactive, ref } from 'vue';
import { ElMessage } from 'element-plus';
const state = reactive({
loading: true, ids: [],
form: {func:''}, single: true, multiple: true, list: [], total: 0,
});
const props = defineProps({
url: String,
maInit: Boolean,
});
onMounted(() => {
});
const dialogVisible = ref(false);
const open = async (data: any) => {
dialogVisible.value = true;
};
defineExpose({ open });
const emits = defineEmits(['close']);
const handleConfirm = () => {
// const chooseIds = state.ids.join(',');
// if (chooseIds.length <= 0) {
// ElMessage.warning('请先点击选择项');
// return;
// } else if (state.ids.length >= 2) {
// ElMessage.warning('请选择单个选择项,不要多选');
// return;
// }
emits('close', { func: state.form.func });
dialogVisible.value = false;
};
</script>
<style scoped>
.tree-h-flex {
display: flex;
}
.tree-h-left {
flex: 1;
width: 100%;
}
.tree-h-right {
width: 42px;
min-width: 42px;
}
</style>
-478
View File
@@ -1,478 +0,0 @@
<template>
<div>
<OrgModal ref='orgModal' @close='closeOrgModal' />
<FuncModal ref='funcModal' @close='closeFuncModal' />
<el-dialog v-model='state.isShow' :title='state.title' width='600px' draggable>
<div style='height: 500px'>
<el-form ref='formRef' :model='form' label-width='140px'>
<el-tabs v-model='activeName' type='card' @tab-click='tabChange'>
<el-tab-pane label='基本' name='k1' class='zform zform100 el-form--inline'>
<div class='zform-div'>
<el-form-item :label='state.name' prop='name' :rules="[{ required: true, message: '名称不能为空'}]">
<el-input v-model='form.name' />
</el-form-item>
<el-form-item label='审批人类型:' v-show='cdata.show1||cdata.show2'>
<el-radio-group v-model='form.hatyp' @change='hatypChange'>
<el-radio label='1'>从组织架构选择</el-radio>
<el-radio label='2'>使用公式定义器</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label='审批人:' v-show='cdata.show1||cdata.show2'>
<el-input v-model='cdata.hamen' readonly @click='hamenModal' />
</el-form-item>
<el-form-item label='流转方式:' prop='name' v-show='cdata.show1'>
<el-radio-group v-model='form.flway'>
<el-radio label='1'>顺签</el-radio>
<el-radio label='2'>或签</el-radio>
<el-radio label='3'>会签</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label='身份重复跳过配置:' v-show='cdata.show1'>
<el-select v-model='form.remod' style='width: 100%'>
<el-option value='1' label='仅相邻处理人身份重复跳过' />
<el-option value='2' label='处理人身份重复跳过' />
<el-option value='3' label='处理人身份重复不跳过' />
</el-select>
</el-form-item>
<el-form-item label='权限:' v-show='cdata.show1'>
<el-checkbox v-model='form.edtag' label='编辑主文档' />
</el-form-item>
<el-form-item label='流转条件:' v-show='state.cotag'>
<el-input v-model='form.conds' type='textarea'
placeholder='编写JS代码,返回true,则往下流。(注1:$代表form对象;注2:如果是表达式,则表达式为返回结果;注3:如果是复杂的多条语句,则z代表返回结果;)' :rows='8' />
</el-form-item>
<el-form-item :label='state.notes'>
<el-input v-model='form.notes' type='textarea' :rows='4' />
</el-form-item>
</div>
</el-tab-pane>
<el-tab-pane label='操作' name='k2' class='zform zform100 el-form--inline' v-if='cdata.show1'>
<div class='zform-div'>
<el-form-item label='操作方式:' prop='name' :rules="[{ required: true, message: '名称不能为空'}]">
<el-select v-model='form.opway' style='width: 100%' @change='opwayChange'>
<el-option value='1' label='标准审批' />
<el-option value='2' label='自定义...' />
</el-select>
</el-form-item>
<el-form-item label='创建人:'>
<el-table size='small' border :data='form.crops' :row-style="{height: '36px'}" style='width: 100%'>
<el-table-column label='操作类型' width='100'>
<template #default='scope'>
<el-select v-model='scope.row.type' style='width: 100%'>
<el-option value='1' label='催办' />
<el-option value='2' label='撤回' />
<el-option value='3' label='废弃' />
</el-select>
</template>
</el-table-column>
<el-table-column label='操作名称'>
<template #default='scope'>
<el-input v-model='scope.row.name' />
</template>
</el-table-column>
<el-table-column fixed='right' label='操作' width='50' align='center' v-if='form.opway!=="1"'>
<template #header>
<el-icon :size='22' style='top: 5px;cursor: pointer' color='#666'>
<ele-CirclePlus @click='addCroper' />
</el-icon>
</template>
<template #default='scope'>
<el-icon :size='22' style='top: 3px;cursor: pointer'>
<ele-CircleClose @click='form.crops.splice(scope.$index, 1)' />
</el-icon>
</template>
</el-table-column>
</el-table>
</el-form-item>
<el-form-item label='处理人:'>
<el-table size='small' border :data='form.haops' :row-style="{height: '36px'}" style='width: 100%'>
<el-table-column label='操作类型' width='100'>
<template #default='scope'>
<el-select v-model='scope.row.type' style='width: 100%'>
<el-option value='1' label='通过' />
<el-option value='2' label='驳回' />
<el-option value='3' label='转办' />
<el-option value='4' label='沟通' />
<el-option value='5' label='废弃' />
</el-select>
</template>
</el-table-column>
<el-table-column label='操作名称'>
<template #default='scope'>
<el-input v-model='scope.row.name' />
</template>
</el-table-column>
<el-table-column fixed='right' label='操作' width='50' align='center' v-if='form.opway!=="1"'>
<template #header>
<el-icon :size='22' style='top: 5px;cursor: pointer' color='#666'>
<ele-CirclePlus @click='addHaoper' />
</el-icon>
</template>
<template #default='scope'>
<el-icon :size='22' style='top: 3px;cursor: pointer'>
<ele-CircleClose @click='form.haops.splice(scope.$index, 1)' />
</el-icon>
</template>
</el-table-column>
</el-table>
</el-form-item>
</div>
</el-tab-pane>
<el-tab-pane label='事件' name='k3'>
<el-table size='small' border :data='form.events' :row-style="{height: '36px'}" style='width: 100%'>
<el-table-column label='事件类型' width='150'>
<template #default='scope'>
<el-select v-model='scope.row.type' style='width: 100%'>
<el-option value='1' label='通过' />
<el-option value='2' label='驳回' />
<el-option value='3' label='转办' />
<el-option value='4' label='沟通' />
<el-option value='5' label='废弃' />
</el-select>
</template>
</el-table-column>
<el-table-column label='事件名称'>
<template #default='scope'>
<el-input v-model='scope.row.name' />
</template>
</el-table-column>
<el-table-column label='事件监听器'>
<template #default='scope'>
<el-select v-model='scope.row.type' style='width: 100%'>
<el-option value='1' label='测试01' />
<el-option value='2' label='测试02' />
<el-option value='3' label='测试03' />
</el-select>
</template>
</el-table-column>
<el-table-column fixed='right' label='操作' width='50' align='center'>
<template #header>
<el-icon :size='22' style='top: 5px;cursor: pointer' color='#666'>
<ele-CirclePlus @click='addEvent' />
</el-icon>
</template>
<template #default='scope'>
<el-icon :size='22' style='top: 3px;cursor: pointer'>
<ele-CircleClose @click='form.events.splice(scope.$index, 1)' />
</el-icon>
</template>
</el-table-column>
</el-table>
</el-tab-pane>
</el-tabs>
</el-form>
<!-- <el-row gutter="8">-->
<!-- <el-col :span='8'>-->
<!-- <div style='height: 600px'>-->
<!-- </div>-->
<!-- </el-col>-->
<!-- <el-col :span='16'>-->
<!-- </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>
</span>
</template>
</el-dialog>
</div>
</template>
<script lang='ts' setup>
import { computed, defineExpose, reactive, ref, toRaw, toRefs } from 'vue';
import OrgModal from '/@/comps/sys/OrgModal.vue';
import FuncModal from './func.vue';
import { uuid } from '/@/utils/xutil';
import {get} from '/@/utils/req';
const state = reactive({
isShow: false, basid: '',title:'',cotag:false,name:'节点名称:',notes:'节点备注:',
list: [] as any, form: {
flway: '1', remod: '1', opway: '1',type:'bpmn:UserTask',
crops: [
{ ornum: 1, type: '1', name: '催办' },
{ ornum: 2, type: '2', name: '撤回' },
{ ornum: 3, type: '3', name: '废弃' },
],
haops: [
{ ornum: 1, type: '1', name: '通过' },
{ ornum: 2, type: '2', name: '驳回' },
{ ornum: 3, type: '3', name: '转办' },
{ ornum: 4, type: '4', name: '沟通' },
{ ornum: 5, type: '5', name: '废弃' },
],
events: [],
} as any,
tabna: '',
});
const { form } = toRefs(state);
const activeName = ref('k1');
const open = async (shape: any) => {
state.form.type=shape.type;
state.form.id = shape.businessObject.id;
console.log(state.form.type);
state.cotag=false;
state.name='节点名称:';
state.notes='节点备注:';
switch (state.form.type) {
case "bpmn:UserTask":
state.title="审批节点 "+state.form.id;
if(state.form.id=="N1"){
state.title="起草节点 "+state.form.id;
}
break;
case "bpmn:ReceiveTask":
state.title="抄送节点 "+state.form.id;
break;
case "bpmn:StartEvent":
state.title="开始节点 "+state.form.id;
break;
case "bpmn:EndEvent":
state.title="结束节点 "+state.form.id;
break;
case "bpmn:ScriptTask":
state.title="脚本节点 "+state.form.id;
break;
case "bpmn:ServiceTask":
state.title="服务节点 "+state.form.id;
break;
case "bpmn:ExclusiveGateway":
state.title="互斥网关 "+state.form.id;
break;
case "bpmn:ParallelGateway":
state.title="并行网关 "+state.form.id;
break;
case "bpmn:SequenceFlow":
state.title="连线 "+state.form.id;
state.name='连线名称:';
state.notes='连线备注:';
if(shape.source.type=="bpmn:ExclusiveGateway"){
state.cotag=true;
}
break;
default:
state.title="未知节点 "+state.form.id;
break;
}
state.form.name = shape.businessObject.name;
state.form.flway = shape.businessObject.$attrs.flway;
if(!state.form.flway){
state.form.flway="1";
}
state.form.hatyp = shape.businessObject.$attrs.hatyp;
if(!state.form.hatyp){
state.form.hatyp="1";
}
state.form.conds = shape.businessObject.$attrs.conds;
if (shape.businessObject.$attrs.hamen) {
if(state.form.hatyp=="1"){
state.form.hamen = await get({
url: '/gen/org/main/list',
params: { ids: shape.businessObject.$attrs.hamen },
});
}else{
state.form.hamen=shape.businessObject.$attrs.hamen;
}
} else {
state.form.hamen = [];
}
// console.log(shape.businessObject.$attrs);
state.isShow = true;
};
defineExpose({ open });
const emits = defineEmits(['close']);
const closeModal = () => {
const backData = {} as any;
backData.name = state.form.name;
if(state.form.hatyp=="1"){
let hamen = '';
for (const haman of state.form.hamen) {
hamen += haman.id + ';';
}
if (hamen != '') {
hamen = hamen.substring(0, hamen.length - 1);
}
backData.hamen = hamen;
}else{
backData.hamen = state.form.hamen;
}
backData.flway = state.form.flway;
backData.hatyp = state.form.hatyp;
if(state.cotag){
backData.conds=state.form.conds;
}
emits('close', backData);
state.isShow = false;
};
const cdata = reactive({}) as any;
//region -----组织架构逻辑-----
const orgModal = ref();
const funcModal = ref();
const hatypChange=()=>{
form.value.hamen=null;
}
const hamenModal = () => {
if(state.form.hatyp=="1"){
orgModal.value.openModal({
opener: 'hamen',
orgType: 44,
selectMode: 2,
orgs: toRaw(form.value.hamen),
});
}else if(state.form.hatyp=="2"){
funcModal.value.open({});
}
};
const closeOrgModal = (data: any) => {
if (data.opener == 'hamen') {
if (data.orgs && data.orgs.length > 0) {
form.value.hamen = data.orgs;
} else {
form.value.hamen = null;
}
if (form.value.name === '审批节点'||form.value.name === '') {
let names = '';
if (form.value.hamen && form.value.hamen.length > 0) {
for (const user of form.value.hamen) {
names += user.name + ';';
}
}
if (names != '') {
names = names.substring(0, names.length - 1);
}
form.value.name = names;
}
}
};
const closeFuncModal = (data: any) => {
form.value.hamen = data.func;
};
cdata.hamen = computed(() => {
let names = '';
if(form.value.hatyp=='1'){
if (form.value.hamen && form.value.hamen.length > 0) {
for (const user of form.value.hamen) {
names += user.name + ';';
}
}
if (names != '') {
names = names.substring(0, names.length - 1);
}
return names;
}else if(form.value.hatyp=='2'){
return form.value.hamen;
}
});
cdata.show1 = computed(() => {
if(state.form.id=="NS"||state.form.id=="N1"||state.form.id=="NE"){
return false;
}else if(state.form.type!=="bpmn:UserTask"){
return false;
}else{
return true;
}
});
cdata.show2 = computed(() => {
if(state.form.type==="bpmn:ReceiveTask"){
return true;
}else{
return false;
}
});
//endregion
//region -----操作逻辑-----
const addCroper = () => {
let newOrnum = 1;
if (form.value.crops.length > 0) {
newOrnum = form.value.crops[form.value.crops.length - 1].ornum + 1;
}
if (form.value.id) {
form.value.crops.push({
id: uuid(), maiid: form.value.id, ornum: newOrnum,
});
} else {
form.value.crops.push({
id: uuid(), ornum: newOrnum,
});
}
};
const addHaoper = () => {
let newOrnum = 1;
if (form.value.haops.length > 0) {
newOrnum = form.value.haops[form.value.haops.length - 1].ornum + 1;
}
if (form.value.id) {
form.value.haops.push({
id: uuid(), maiid: form.value.id, ornum: newOrnum,
});
} else {
form.value.haops.push({
id: uuid(), ornum: newOrnum,
});
}
};
const opwayChange = () => {
if (state.form.opway == '1') {
state.form.crops = [
{ ornum: 1, type: '1', name: '催办' },
{ ornum: 2, type: '2', name: '撤回' },
{ ornum: 3, type: '3', name: '废弃' },
];
state.form.haops = [
{ ornum: 1, type: '1', name: '通过' },
{ ornum: 2, type: '2', name: '驳回' },
{ ornum: 3, type: '3', name: '转办' },
{ ornum: 4, type: '4', name: '沟通' },
{ ornum: 5, type: '5', name: '废弃' },
];
} else {
// state.form.crops = [];
// state.form.haops = [];
}
};
//endregiton
const addEvent = () => {
let newOrnum = 1;
if (form.value.events.length > 0) {
newOrnum = form.value.events[form.value.events.length - 1].ornum + 1;
}
if (form.value.id) {
form.value.events.push({
id: uuid(), maiid: form.value.id, ornum: newOrnum,
});
} else {
form.value.events.push({
id: uuid(), ornum: newOrnum,
});
}
};
</script>
<style scoped>
</style>

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