!173 合并master上1.7.2和1.7.3的修改到master-vxe

Merge pull request !173 from clockdotnet/master-vxe
This commit is contained in:
芋道源码 2023-06-17 10:37:49 +00:00 committed by Gitee
commit ec4a5ec961
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
282 changed files with 10890 additions and 6298 deletions

2
.env
View File

@ -13,3 +13,5 @@ VITE_APP_TENANT_ENABLE=true
# 验证码的开关 # 验证码的开关
VITE_APP_CAPTCHA_ENABLE=true VITE_APP_CAPTCHA_ENABLE=true
# 百度统计
VITE_APP_BAIDU_CODE = a1ff8825baa73c3a78eb96aa40325abc

View File

@ -16,7 +16,7 @@ VITE_API_BASEPATH=/dev-api
VITE_API_URL=/admin-api VITE_API_URL=/admin-api
# 打包路径 # 打包路径
VITE_BASE_PATH=/dist-dev/ VITE_BASE_PATH=/
# 是否删除debugger # 是否删除debugger
VITE_DROP_DEBUGGER=false VITE_DROP_DEBUGGER=false

View File

@ -17,3 +17,18 @@ VITE_API_URL=/admin-api
# 打包路径 # 打包路径
VITE_BASE_PATH=/ VITE_BASE_PATH=/
# 项目本地运行端口号, 与.vscode/launch.json配合
VITE_PORT=80
# 是否删除debugger
VITE_DROP_DEBUGGER=false
# 是否删除console.log
VITE_DROP_CONSOLE=false
# 是否sourcemap
VITE_SOURCEMAP=true
# 验证码的开关
VITE_APP_CAPTCHA_ENABLE=false

31
.env.stage Normal file
View File

@ -0,0 +1,31 @@
# 生产环境
NODE_ENV=production
VITE_DEV=false
# 请求路径
VITE_BASE_URL='http://api-dashboard.yudao.iocoder.cn'
# 上传路径
VITE_UPLOAD_URL='http://api-dashboard.yudao.iocoder.cn/admin-api/infra/file/upload'
# 接口前缀
VITE_API_BASEPATH=
# 接口地址
VITE_API_URL=/admin-api
# 是否删除debugger
VITE_DROP_DEBUGGER=true
# 是否删除console.log
VITE_DROP_CONSOLE=true
# 是否sourcemap
VITE_SOURCEMAP=false
# 打包路径
VITE_BASE_PATH='http://static-vue3.yudao.iocoder.cn/'
# 输出路径
VITE_OUT_DIR=dist-stage

View File

@ -25,7 +25,7 @@ VITE_DROP_CONSOLE=false
VITE_SOURCEMAP=true VITE_SOURCEMAP=true
# 打包路径 # 打包路径
VITE_BASE_PATH=/dist-test/ VITE_BASE_PATH=/
# 输出路径 # 输出路径
VITE_OUT_DIR=dist-test VITE_OUT_DIR=dist-test

5
.gitignore vendored
View File

@ -6,6 +6,9 @@ dist-ssr
/dist* /dist*
*-lock.* *-lock.*
pnpm-debug pnpm-debug
auto-*.d.ts
.idea .idea
.history .history
yarn.lock
*.iml

12
.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,12 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "Launch debug",
"request": "launch",
"type": "chrome",
"url": "http://localhost",
"webRoot": "${workspaceFolder}/src"
}
]
}

14
.vscode/settings.json vendored
View File

@ -8,7 +8,7 @@
"source.fixAll.eslint": true "source.fixAll.eslint": true
}, },
"[vue]": { "[vue]": {
"editor.defaultFormatter": "esbenp.prettier-vscode" "editor.defaultFormatter": "Vue.volar"
}, },
"[javascript]": { "[javascript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode" "editor.defaultFormatter": "esbenp.prettier-vscode"
@ -40,5 +40,15 @@
"i18n-ally.displayLanguage": "zh-CN", "i18n-ally.displayLanguage": "zh-CN",
"i18n-ally.enabledFrameworks": ["vue", "react"], "i18n-ally.enabledFrameworks": ["vue", "react"],
"god.tsconfig": "./tsconfig.json", "god.tsconfig": "./tsconfig.json",
"vue-i18n.i18nPaths": "src/locales" "vue-i18n.i18nPaths": "src/locales",
"explorer.fileNesting.enabled": true,
"explorer.fileNesting.expand": false,
"explorer.fileNesting.patterns": {
"*.ts": "$(capture).test.ts, $(capture).test.tsx",
"*.tsx": "$(capture).test.ts, $(capture).test.tsx",
"*.env": "$(capture).env.*",
"CHANGELOG.md": "CHANGELOG*",
"package.json": "pnpm-lock.yaml,pnpm-workspace.yaml,LICENSE,.gitattributes,.gitignore,.gitpod.yml,CNAME,README*,.npmrc,.browserslistrc,vite.config.*,windi.*,tailwind.*,tsconfig.*,postcss*",
".eslintrc.js": ".eslintignore,.eslintrc-*,.prettierignore,.stylelintignore,.commitlintrc.js,.prettierrc.js,.stylelint*,stylelint*,prettier.*,.editorconfig"
}
} }

View File

@ -9,7 +9,10 @@
## 🐶 新手必读 ## 🐶 新手必读
* 演示地址:<http://dashboard.yudao.iocoder.cn> * nodejs > 16.0.0 && pnpm > 7.30.0
* 演示地址【Vue3 + element-plus】<http://dashboard-vue3.yudao.iocoder.cn>
* 演示地址【Vue3 + vben(ant-design-vue)】:<http://dashboard-vben.yudao.iocoder.cn>
* 演示地址【Vue2 + element-ui】<http://dashboard.yudao.iocoder.cn>
* 启动文档:<https://doc.iocoder.cn/quick-start/> * 启动文档:<https://doc.iocoder.cn/quick-start/>
* 视频教程:<https://doc.iocoder.cn/video/> * 视频教程:<https://doc.iocoder.cn/video/>
@ -17,8 +20,8 @@
**芋道**,以开发者为中心,打造中国第一流的快速开发平台,全部开源,个人与企业可 100% 免费使用。 **芋道**,以开发者为中心,打造中国第一流的快速开发平台,全部开源,个人与企业可 100% 免费使用。
* 采用 [vue-element-plus-admin](https://gitee.com/kailong110120130/vue-element-plus-admin) * 采用 [vue-element-plus-admin](https://gitee.com/kailong110120130/vue-element-plus-admin) 实现
* 改换saas自动引入等功能 [vue-element-plus-admin](https://gitee.com/yudaocode/vue-element-plus-admin) * 改换 saas自动引入等功能
* 使用 Element Plus 免费开源的中后台模版,具备如下特性: * 使用 Element Plus 免费开源的中后台模版,具备如下特性:
![首页](preview/home.png) ![首页](preview/home.png)
@ -35,15 +38,14 @@
| 框架 | 说明 | 版本 | | 框架 | 说明 | 版本 |
|----------------------------------------------------------------------|------------------|--------| |----------------------------------------------------------------------|------------------|--------|
| [Vue](https://staging-cn.vuejs.org/) | Vue 框架 | 3.2.47 | | [Vue](https://staging-cn.vuejs.org/) | Vue 框架 | 3.3.4 |
| [Vite](https://cn.vitejs.dev//) | 开发与构建工具 | 4.1.4 | | [Vite](https://cn.vitejs.dev//) | 开发与构建工具 | 4.3.8 |
| [Element Plus](https://element-plus.org/zh-CN/) | Element Plus | 2.2.34 | | [Element Plus](https://element-plus.org/zh-CN/) | Element Plus | 2.3.4 |
| [TypeScript](https://www.typescriptlang.org/docs/) | JavaScript 的超集 | 4.9.5 | | [TypeScript](https://www.typescriptlang.org/docs/) | JavaScript 的超集 | 5.0.4 |
| [pinia](https://pinia.vuejs.org/) | Vue 存储库 替代 vuex5 | 2.0.33 | | [pinia](https://pinia.vuejs.org/) | Vue 存储库 替代 vuex5 | 2.1.3 |
| [vueuse](https://vueuse.org/) | 常用工具集 | 9.13.0 | | [vueuse](https://vueuse.org/) | 常用工具集 | 10.1.2 |
| [vxe-table](https://vxetable.cn/) | Vue 最强表单 | 4.3.10 |
| [vue-i18n](https://kazupon.github.io/vue-i18n/zh/introduction.html/) | 国际化 | 9.2.2 | | [vue-i18n](https://kazupon.github.io/vue-i18n/zh/introduction.html/) | 国际化 | 9.2.2 |
| [vue-router](https://router.vuejs.org/) | Vue 路由 | 4.1.6 | | [vue-router](https://router.vuejs.org/) | Vue 路由 | 4.2.1 |
| [windicss](https://cn.windicss.org/) | 下一代工具优先的 CSS 框架 | 3.5.6 | | [windicss](https://cn.windicss.org/) | 下一代工具优先的 CSS 框架 | 3.5.6 |
| [iconify](https://icon-sets.iconify.design/) | 在线图标库 | 3.1.0 | | [iconify](https://icon-sets.iconify.design/) | 在线图标库 | 3.1.0 |
| [wangeditor](https://www.wangeditor.com/) | 富文本编辑器 | 5.1.23 | | [wangeditor](https://www.wangeditor.com/) | 富文本编辑器 | 5.1.23 |

View File

@ -11,7 +11,8 @@ import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite' import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers' import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
import viteCompression from 'vite-plugin-compression' import viteCompression from 'vite-plugin-compression'
import vueSetupExtend from 'vite-plugin-vue-setup-extend' import topLevelAwait from 'vite-plugin-top-level-await'
import vueSetupExtend from 'vite-plugin-vue-setup-extend-plus'
import VueI18nPlugin from '@intlify/unplugin-vue-i18n/vite' import VueI18nPlugin from '@intlify/unplugin-vue-i18n/vite'
import { createSvgIconsPlugin } from 'vite-plugin-svg-icons' import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'
@ -95,6 +96,13 @@ export function createVitePlugins() {
ext: '.gz', // 生成的压缩包后缀 ext: '.gz', // 生成的压缩包后缀
deleteOriginFile: false //压缩后是否删除源文件 deleteOriginFile: false //压缩后是否删除源文件
}), }),
ViteEjsPlugin() ViteEjsPlugin(),
topLevelAwait({
// https://juejin.cn/post/7152191742513512485
// The export name of top-level await promise for each chunk module
promiseExportName: '__tla',
// The function to generate import names of top-level await promise in each chunk module
promiseImportName: (i) => `__tla_${i}`
})
] ]
} }

View File

@ -37,37 +37,58 @@ const include = [
'element-plus/es', 'element-plus/es',
'element-plus/es/locale/lang/zh-cn', 'element-plus/es/locale/lang/zh-cn',
'element-plus/es/locale/lang/en', 'element-plus/es/locale/lang/en',
'element-plus/es/components/backtop/style/index', 'element-plus/es/components/backtop/style/css',
'element-plus/es/components/form/style/index', 'element-plus/es/components/form/style/css',
'element-plus/es/components/radio-group/style/index', 'element-plus/es/components/radio-group/style/css',
'element-plus/es/components/radio/style/index', 'element-plus/es/components/radio/style/css',
'element-plus/es/components/checkbox/style/index', 'element-plus/es/components/checkbox/style/css',
'element-plus/es/components/checkbox-group/style/index', 'element-plus/es/components/checkbox-group/style/css',
'element-plus/es/components/switch/style/index', 'element-plus/es/components/switch/style/css',
'element-plus/es/components/time-picker/style/index', 'element-plus/es/components/time-picker/style/css',
'element-plus/es/components/date-picker/style/index', 'element-plus/es/components/date-picker/style/css',
'element-plus/es/components/col/style/index', 'element-plus/es/components/descriptions/style/css',
'element-plus/es/components/form-item/style/index', 'element-plus/es/components/descriptions-item/style/css',
'element-plus/es/components/alert/style/index', 'element-plus/es/components/link/style/css',
'element-plus/es/components/breadcrumb/style/index', 'element-plus/es/components/tooltip/style/css',
'element-plus/es/components/select/style/index', 'element-plus/es/components/drawer/style/css',
'element-plus/es/components/input/style/index', 'element-plus/es/components/dialog/style/css',
'element-plus/es/components/breadcrumb-item/style/index', 'element-plus/es/components/checkbox-button/style/css',
'element-plus/es/components/tag/style/index', 'element-plus/es/components/option-group/style/css',
'element-plus/es/components/pagination/style/index', 'element-plus/es/components/radio-button/style/css',
'element-plus/es/components/table/style/index', 'element-plus/es/components/cascader/style/css',
'element-plus/es/components/table-column/style/index', 'element-plus/es/components/color-picker/style/css',
'element-plus/es/components/card/style/index', 'element-plus/es/components/input-number/style/css',
'element-plus/es/components/row/style/index', 'element-plus/es/components/rate/style/css',
'element-plus/es/components/button/style/index', 'element-plus/es/components/select-v2/style/css',
'element-plus/es/components/menu/style/index', 'element-plus/es/components/tree-select/style/css',
'element-plus/es/components/sub-menu/style/index', 'element-plus/es/components/slider/style/css',
'element-plus/es/components/menu-item/style/index', 'element-plus/es/components/time-select/style/css',
'element-plus/es/components/option/style/index', 'element-plus/es/components/autocomplete/style/css',
'element-plus/es/components/dropdown/style/index', 'element-plus/es/components/image-viewer/style/css',
'element-plus/es/components/dropdown-menu/style/index', 'element-plus/es/components/upload/style/css',
'element-plus/es/components/dropdown-item/style/index', 'element-plus/es/components/col/style/css',
'element-plus/es/components/skeleton/style/index', 'element-plus/es/components/form-item/style/css',
'element-plus/es/components/alert/style/css',
'element-plus/es/components/breadcrumb/style/css',
'element-plus/es/components/select/style/css',
'element-plus/es/components/input/style/css',
'element-plus/es/components/breadcrumb-item/style/css',
'element-plus/es/components/tag/style/css',
'element-plus/es/components/pagination/style/css',
'element-plus/es/components/table/style/css',
'element-plus/es/components/table-v2/style/css',
'element-plus/es/components/table-column/style/css',
'element-plus/es/components/card/style/css',
'element-plus/es/components/row/style/css',
'element-plus/es/components/button/style/css',
'element-plus/es/components/menu/style/css',
'element-plus/es/components/sub-menu/style/css',
'element-plus/es/components/menu-item/style/css',
'element-plus/es/components/option/style/css',
'element-plus/es/components/dropdown/style/css',
'element-plus/es/components/dropdown-menu/style/css',
'element-plus/es/components/dropdown-item/style/css',
'element-plus/es/components/skeleton/style/css',
'element-plus/es/components/skeleton/style/css', 'element-plus/es/components/skeleton/style/css',
'element-plus/es/components/backtop/style/css', 'element-plus/es/components/backtop/style/css',
'element-plus/es/components/menu/style/css', 'element-plus/es/components/menu/style/css',
@ -79,7 +100,15 @@ const include = [
'element-plus/es/components/dropdown-item/style/css', 'element-plus/es/components/dropdown-item/style/css',
'element-plus/es/components/badge/style/css', 'element-plus/es/components/badge/style/css',
'element-plus/es/components/breadcrumb/style/css', 'element-plus/es/components/breadcrumb/style/css',
'element-plus/es/components/breadcrumb-item/style/css' 'element-plus/es/components/breadcrumb-item/style/css',
'element-plus/es/components/image/style/css',
'element-plus/es/components/collapse-transition/style/css',
'element-plus/es/components/timeline/style/css',
'element-plus/es/components/timeline-item/style/css',
'element-plus/es/components/collapse/style/css',
'element-plus/es/components/collapse-item/style/css',
'element-plus/es/components/button-group/style/css',
'element-plus/es/components/text/style/css'
] ]
const exclude = ['@iconify/json'] const exclude = ['@iconify/json']

View File

@ -1,6 +1,6 @@
{ {
"name": "yudao-ui-admin-vue3", "name": "yudao-ui-admin-vue3",
"version": "1.7.1-snapshot.1961", "version": "1.7.3-snapshot",
"description": "基于vue3、vite4、element-plus、typesScript", "description": "基于vue3、vite4、element-plus、typesScript",
"author": "xingyu", "author": "xingyu",
"private": false, "private": false,
@ -11,11 +11,14 @@
"ts:check": "vue-tsc --noEmit", "ts:check": "vue-tsc --noEmit",
"build:pro": "node --max_old_space_size=8000 ./node_modules/vite/bin/vite.js build --mode pro", "build:pro": "node --max_old_space_size=8000 ./node_modules/vite/bin/vite.js build --mode pro",
"build:dev": "node --max_old_space_size=8000 ./node_modules/vite/bin/vite.js build --mode dev", "build:dev": "node --max_old_space_size=8000 ./node_modules/vite/bin/vite.js build --mode dev",
"build:stage": "node --max_old_space_size=8000 ./node_modules/vite/bin/vite.js build --mode stage",
"build:test": "node --max_old_space_size=8000 ./node_modules/vite/bin/vite.js build --mode test", "build:test": "node --max_old_space_size=8000 ./node_modules/vite/bin/vite.js build --mode test",
"build:static": "node --max_old_space_size=8000 ./node_modules/vite/bin/vite.js build --mode static", "build:static": "node --max_old_space_size=8000 ./node_modules/vite/bin/vite.js build --mode static",
"build:front": "node --max_old_space_size=8000 ./node_modules/vite/bin/vite.js build --mode front",
"serve:pro": "vite preview --mode pro", "serve:pro": "vite preview --mode pro",
"serve:dev": "vite preview --mode dev", "serve:dev": "vite preview --mode dev",
"serve:test": "vite preview --mode test", "serve:test": "vite preview --mode test",
"preview": "pnpm build && vite preview",
"npm:check": "npx npm-check-updates", "npm:check": "npx npm-check-updates",
"clean": "npx rimraf node_modules", "clean": "npx rimraf node_modules",
"clean:cache": "npx rimraf node_modules/.cache", "clean:cache": "npx rimraf node_modules/.cache",
@ -26,16 +29,17 @@
"lint:pretty": "pretty-quick --staged" "lint:pretty": "pretty-quick --staged"
}, },
"dependencies": { "dependencies": {
"@form-create/designer": "^3.1.0", "@element-plus/icons-vue": "^2.1.0",
"@form-create/element-ui": "^3.1.17", "@form-create/designer": "^3.1.3",
"@form-create/element-ui": "^3.1.18",
"@iconify/iconify": "^3.1.0", "@iconify/iconify": "^3.1.0",
"@videojs-player/vue": "^1.0.0", "@videojs-player/vue": "^1.0.0",
"@vueuse/core": "^9.13.0", "@vueuse/core": "^10.1.2",
"@wangeditor/editor": "^5.1.23", "@wangeditor/editor": "^5.1.23",
"@wangeditor/editor-for-vue": "^5.1.10", "@wangeditor/editor-for-vue": "^5.1.10",
"@zxcvbn-ts/core": "^2.2.1", "@zxcvbn-ts/core": "^3.0.1",
"animate.css": "^4.1.1", "animate.css": "^4.1.1",
"axios": "^1.3.4", "axios": "^1.4.0",
"benz-amr-recorder": "^1.1.5", "benz-amr-recorder": "^1.1.5",
"bpmn-js-token-simulation": "^0.10.0", "bpmn-js-token-simulation": "^0.10.0",
"camunda-bpmn-moddle": "^7.0.1", "camunda-bpmn-moddle": "^7.0.1",
@ -43,27 +47,27 @@
"crypto-js": "^4.1.1", "crypto-js": "^4.1.1",
"dayjs": "^1.11.7", "dayjs": "^1.11.7",
"diagram-js": "^11.6.0", "diagram-js": "^11.6.0",
"echarts": "^5.4.1", "echarts": "^5.4.2",
"echarts-wordcloud": "^2.1.0", "echarts-wordcloud": "^2.1.0",
"element-plus": "2.3.1", "element-plus": "2.3.4",
"fast-xml-parser": "^4.1.3", "fast-xml-parser": "^4.2.2",
"highlight.js": "^11.7.0", "highlight.js": "^11.8.0",
"intro.js": "^6.0.0", "intro.js": "^7.0.1",
"jsencrypt": "^3.3.2", "jsencrypt": "^3.3.2",
"lodash-es": "^4.17.21", "lodash-es": "^4.17.21",
"min-dash": "^4.0.0", "min-dash": "^4.1.1",
"mitt": "^3.0.0", "mitt": "^3.0.0",
"nprogress": "^0.2.0", "nprogress": "^0.2.0",
"pinia": "^2.0.33", "pinia": "^2.1.3",
"qrcode": "^1.5.1", "qrcode": "^1.5.3",
"qs": "^6.11.1", "qs": "^6.11.2",
"steady-xml": "^0.1.0", "steady-xml": "^0.1.0",
"url": "^0.11.0", "url": "^0.11.0",
"video.js": "^8.0.4", "video.js": "^8.3.0",
"vue": "3.2.47", "vue": "3.3.4",
"vue-i18n": "9.2.2", "vue-i18n": "9.2.2",
"vue-router": "^4.1.6", "vue-router": "^4.2.1",
"vue-types": "^5.0.2", "vue-types": "^5.0.3",
"vuedraggable": "^4.1.0", "vuedraggable": "^4.1.0",
"vxe-table": "^4.3.11", "vxe-table": "^4.3.11",
"web-storage-cache": "^1.1.1", "web-storage-cache": "^1.1.1",
@ -71,64 +75,64 @@
"xml-js": "^1.6.11" "xml-js": "^1.6.11"
}, },
"devDependencies": { "devDependencies": {
"@commitlint/cli": "^17.5.0", "@commitlint/cli": "^17.6.3",
"@commitlint/config-conventional": "^17.4.4", "@commitlint/config-conventional": "^17.6.3",
"@iconify/json": "^2.2.38", "@iconify/json": "^2.2.67",
"@intlify/unplugin-vue-i18n": "^0.10.0", "@intlify/unplugin-vue-i18n": "^0.10.0",
"@purge-icons/generated": "^0.9.0", "@purge-icons/generated": "^0.9.0",
"@types/intro.js": "^5.1.1", "@types/intro.js": "^5.1.1",
"@types/lodash-es": "^4.17.7", "@types/lodash-es": "^4.17.7",
"@types/node": "^18.15.5", "@types/node": "^18.16.0",
"@types/nprogress": "^0.2.0", "@types/nprogress": "^0.2.0",
"@types/qrcode": "^1.5.0", "@types/qrcode": "^1.5.0",
"@types/qs": "^6.9.7", "@types/qs": "^6.9.7",
"@typescript-eslint/eslint-plugin": "^5.56.0", "@typescript-eslint/eslint-plugin": "^5.59.6",
"@typescript-eslint/parser": "^5.56.0", "@typescript-eslint/parser": "^5.59.6",
"@vitejs/plugin-legacy": "^4.0.2", "@vitejs/plugin-legacy": "^4.0.3",
"@vitejs/plugin-vue": "^4.1.0", "@vitejs/plugin-vue": "^4.2.3",
"@vitejs/plugin-vue-jsx": "^3.0.1", "@vitejs/plugin-vue-jsx": "^3.0.1",
"autoprefixer": "^10.4.14", "autoprefixer": "^10.4.14",
"bpmn-js": "^8.9.0", "bpmn-js": "^8.9.0",
"bpmn-js-properties-panel": "^0.46.0", "bpmn-js-properties-panel": "^0.46.0",
"consola": "^2.15.3", "consola": "^3.1.0",
"eslint": "^8.36.0", "eslint": "^8.40.0",
"eslint-config-prettier": "^8.8.0", "eslint-config-prettier": "^8.8.0",
"eslint-define-config": "^1.17.0", "eslint-define-config": "^1.20.0",
"eslint-plugin-prettier": "^4.2.1", "eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-vue": "^9.9.0", "eslint-plugin-vue": "^9.13.0",
"lint-staged": "^13.2.0", "lint-staged": "^13.2.2",
"postcss": "^8.4.21", "postcss": "^8.4.23",
"postcss-html": "^1.5.0", "postcss-html": "^1.5.0",
"postcss-scss": "^4.0.6", "postcss-scss": "^4.0.6",
"prettier": "^2.8.6", "prettier": "^2.8.8",
"rimraf": "^4.4.1", "rimraf": "^5.0.1",
"rollup": "^3.20.0", "rollup": "^3.22.0",
"sass": "^1.59.3", "sass": "^1.62.1",
"stylelint": "^15.3.0", "stylelint": "^15.6.2",
"stylelint-config-html": "^1.1.0", "stylelint-config-html": "^1.1.0",
"stylelint-config-prettier": "^9.0.5", "stylelint-config-recommended": "^12.0.0",
"stylelint-config-recommended": "^11.0.0", "stylelint-config-standard": "^33.0.0",
"stylelint-config-standard": "^31.0.0",
"stylelint-order": "^6.0.3", "stylelint-order": "^6.0.3",
"terser": "^5.16.6", "terser": "^5.17.4",
"typescript": "5.0.2", "typescript": "5.0.4",
"unplugin-auto-import": "^0.15.1", "unplugin-auto-import": "^0.16.0",
"unplugin-element-plus": "^0.7.0", "unplugin-element-plus": "^0.7.1",
"unplugin-vue-components": "^0.24.1", "unplugin-vue-components": "^0.24.1",
"vite": "4.2.1", "vite": "4.3.8",
"vite-plugin-compression": "^0.5.1", "vite-plugin-compression": "^0.5.1",
"vite-plugin-ejs": "^1.6.4", "vite-plugin-ejs": "^1.6.4",
"vite-plugin-eslint": "^1.8.1", "vite-plugin-eslint": "^1.8.1",
"vite-plugin-progress": "^0.0.6", "vite-plugin-progress": "^0.0.7",
"vite-plugin-purge-icons": "^0.9.2", "vite-plugin-purge-icons": "^0.9.2",
"vite-plugin-svg-icons": "^2.0.1", "vite-plugin-svg-icons": "^2.0.1",
"vite-plugin-vue-setup-extend": "^0.4.0", "vite-plugin-top-level-await": "^1.3.0",
"vite-plugin-windicss": "^1.8.10", "vite-plugin-vue-setup-extend-plus": "^0.1.0",
"vue-tsc": "^1.2.0", "vite-plugin-windicss": "^1.9.0",
"vue-tsc": "^1.6.5",
"windicss": "^3.5.6" "windicss": "^3.5.6"
}, },
"engines": { "engines": {
"node": ">=16.18.0" "node": ">=16.0.0"
}, },
"license": "MIT", "license": "MIT",
"repository": { "repository": {

View File

@ -1,8 +1,9 @@
<script setup lang="ts"> <script lang="ts" name="APP" setup>
import { isDark } from '@/utils/is' import { isDark } from '@/utils/is'
import { useAppStore } from '@/store/modules/app' import { useAppStore } from '@/store/modules/app'
import { useDesign } from '@/hooks/web/useDesign' import { useDesign } from '@/hooks/web/useDesign'
import { CACHE_KEY, useCache } from '@/hooks/web/useCache' import { CACHE_KEY, useCache } from '@/hooks/web/useCache'
import routerSearch from '@/components/RouterSearch/index.vue'
const { getPrefixCls } = useDesign() const { getPrefixCls } = useDesign()
const prefixCls = getPrefixCls('app') const prefixCls = getPrefixCls('app')
@ -24,14 +25,17 @@ setDefaultTheme()
<template> <template>
<ConfigGlobal :size="currentSize"> <ConfigGlobal :size="currentSize">
<RouterView :class="greyMode ? `${prefixCls}-grey-mode` : ''" /> <RouterView :class="greyMode ? `${prefixCls}-grey-mode` : ''" />
<routerSearch />
</ConfigGlobal> </ConfigGlobal>
</template> </template>
<style lang="scss"> <style lang="scss">
$prefix-cls: #{$namespace}-app; $prefix-cls: #{$namespace}-app;
.size { .size {
width: 100%; width: 100%;
height: 100%; height: 100%;
} }
html, html,
body { body {
padding: 0 !important; padding: 0 !important;
@ -43,6 +47,7 @@ body {
@extend .size; @extend .size;
} }
} }
.#{$prefix-cls}-grey-mode { .#{$prefix-cls}-grey-mode {
filter: grayscale(100%); filter: grayscale(100%);
} }

View File

@ -22,6 +22,6 @@ export const getLeaveApi = async (id: number) => {
} }
// 获得请假申请分页 // 获得请假申请分页
export const getLeavePageApi = async (params) => { export const getLeavePageApi = async (params: PageParam) => {
return await request.get({ url: '/bpm/oa/leave/page', params }) return await request.get({ url: '/bpm/oa/leave/page', params })
} }

View File

@ -1,5 +1,9 @@
import request from '@/config/axios' import request from '@/config/axios'
export type TaskVO = {
id: number
}
export const getTodoTaskPage = async (params) => { export const getTodoTaskPage = async (params) => {
return await request.get({ url: '/bpm/task/todo-page', params }) return await request.get({ url: '/bpm/task/todo-page', params })
} }
@ -32,3 +36,8 @@ export const getTaskListByProcessInstanceId = async (processInstanceId) => {
url: '/bpm/task/list-by-process-instance-id?processInstanceId=' + processInstanceId url: '/bpm/task/list-by-process-instance-id?processInstanceId=' + processInstanceId
}) })
} }
// 导出任务
export const exportTask = async (params) => {
return await request.download({ url: '/bpm/task/export', params })
}

View File

@ -2,15 +2,11 @@ import request from '@/config/axios'
import { getRefreshToken } from '@/utils/auth' import { getRefreshToken } from '@/utils/auth'
import type { UserLoginVO } from './types' import type { UserLoginVO } from './types'
export interface CodeImgResult {
captchaOnOff: boolean
img: string
uuid: string
}
export interface SmsCodeVO { export interface SmsCodeVO {
mobile: string mobile: string
scene: number scene: number
} }
export interface SmsLoginVO { export interface SmsLoginVO {
mobile: string mobile: string
code: string code: string
@ -62,7 +58,7 @@ export const socialAuthRedirectApi = (type: number, redirectUri: string) => {
url: '/system/auth/social-auth-redirect?type=' + type + '&redirectUri=' + redirectUri url: '/system/auth/social-auth-redirect?type=' + type + '&redirectUri=' + redirectUri
}) })
} }
// 获取验证图片 以及token // 获取验证图片以及token
export const getCodeApi = (data) => { export const getCodeApi = (data) => {
return request.postOriginal({ url: 'system/captcha/get', data }) return request.postOriginal({ url: 'system/captcha/get', data })
} }

View File

@ -0,0 +1,41 @@
import request from '@/config/axios'
// 获得授权信息
export const getAuthorize = (clientId: string) => {
return request.get({ url: '/system/oauth2/authorize?clientId=' + clientId })
}
// 发起授权
export const authorize = (
responseType: string,
clientId: string,
redirectUri: string,
state: string,
autoApprove: boolean,
checkedScopes: string[],
uncheckedScopes: string[]
) => {
// 构建 scopes
const scopes = {}
for (const scope of checkedScopes) {
scopes[scope] = true
}
for (const scope of uncheckedScopes) {
scopes[scope] = false
}
// 发起请求
return request.post({
url: '/system/oauth2/authorize',
headers: {
'Content-type': 'application/x-www-form-urlencoded'
},
params: {
response_type: responseType,
client_id: clientId,
redirect_uri: redirectUri,
state: state,
auto_approve: autoApprove,
scope: JSON.stringify(scopes)
}
})
}

View File

@ -26,17 +26,3 @@ export type UserVO = {
loginIp: string loginIp: string
loginDate: string loginDate: string
} }
export type UserInfoVO = {
permissions: []
roles: []
user: {
avatar: string
id: number
nickname: string
}
}
export type TentantNameVO = {
name: string
}

View File

@ -0,0 +1,61 @@
import request from '@/config/axios'
/**
*
*/
export interface BrandVO {
/**
*
*/
id?: number
/**
*
*/
name: string
/**
*
*/
picUrl: string
/**
*
*/
sort?: number
/**
*
*/
description?: string
/**
*
*/
status: number
}
// 创建商品品牌
export const createBrand = (data: BrandVO) => {
return request.post({ url: '/product/brand/create', data })
}
// 更新商品品牌
export const updateBrand = (data: BrandVO) => {
return request.put({ url: '/product/brand/update', data })
}
// 删除商品品牌
export const deleteBrand = (id: number) => {
return request.delete({ url: `/product/brand/delete?id=${id}` })
}
// 获得商品品牌
export const getBrand = (id: number) => {
return request.get({ url: `/product/brand/get?id=${id}` })
}
// 获得商品品牌列表
export const getBrandParam = (params: PageParam) => {
return request.get({ url: '/product/brand/page', params })
}
// 获得商品品牌精简信息列表
export const getSimpleBrandList = () => {
return request.get({ url: '/product/brand/list-all-simple' })
}

View File

@ -17,17 +17,17 @@ export interface CategoryVO {
*/ */
name: string name: string
/** /**
* *
*/ */
picUrl: string picUrl: string
/**
* PC
*/
bigPicUrl?: string
/** /**
* *
*/ */
sort?: number sort: number
/**
*
*/
description?: string
/** /**
* *
*/ */

View File

@ -71,8 +71,8 @@ export const getPropertyList = (params: any) => {
} }
// 获得属性项列表 // 获得属性项列表
export const getPropertyListAndValue = (params: any) => { export const getPropertyListAndValue = (data: any) => {
return request.get({ url: '/product/property/get-value-list', params }) return request.post({ url: '/product/property/get-value-list', data })
} }
// ------------------------ 属性值 ------------------- // ------------------------ 属性值 -------------------

View File

@ -0,0 +1,92 @@
import request from '@/config/axios'
export interface Property {
propertyId?: number // 属性编号
propertyName?: string // 属性名称
valueId?: number // 属性值编号
valueName?: string // 属性值名称
}
// TODO puhui999是不是直接叫 Sku 更简洁一点哈。type 待后面,总感觉有个类型?
export interface SkuType {
id?: number // 商品 SKU 编号
spuId?: number // SPU 编号
properties?: Property[] // 属性数组
price?: number // 商品价格
marketPrice?: number // 市场价
costPrice?: number // 成本价
barCode?: string // 商品条码
picUrl?: string // 图片地址
stock?: number // 库存
weight?: number // 商品重量单位kg 千克
volume?: number // 商品体积单位m^3 平米
subCommissionFirstPrice?: number // 一级分销的佣金
subCommissionSecondPrice?: number // 二级分销的佣金
salesCount?: number // 商品销量
}
// TODO puhui999是不是直接叫 Spu 更简洁一点哈。type 待后面,总感觉有个类型?
export interface SpuType {
id?: number
name?: string // 商品名称
categoryId?: number | null // 商品分类
keyword?: string // 关键字
unit?: number | null // 单位
picUrl?: string // 商品封面图
sliderPicUrls?: string[] // 商品轮播图
introduction?: string // 商品简介
deliveryTemplateId?: number | null // 运费模版
brandId?: number | null // 商品品牌编号
specType?: boolean // 商品规格
subCommissionType?: boolean // 分销类型
skus: SkuType[] // sku数组
description?: string // 商品详情
sort?: string // 商品排序
giveIntegral?: number // 赠送积分
virtualSalesCount?: number // 虚拟销量
recommendHot?: boolean // 是否热卖
recommendBenefit?: boolean // 是否优惠
recommendBest?: boolean // 是否精品
recommendNew?: boolean // 是否新品
recommendGood?: boolean // 是否优品
}
// 获得 Spu 列表
export const getSpuPage = (params: PageParam) => {
return request.get({ url: '/product/spu/page', params })
}
// 获得 Spu 列表 tabsCount
export const getTabsCount = () => {
return request.get({ url: '/product/spu/get-count' })
}
// 创建商品 Spu
export const createSpu = (data: SpuType) => {
return request.post({ url: '/product/spu/create', data })
}
// 更新商品 Spu
export const updateSpu = (data: SpuType) => {
return request.put({ url: '/product/spu/update', data })
}
// 更新商品 Spu status
export const updateStatus = (data: { id: number; status: number }) => {
return request.put({ url: '/product/spu/update-status', data })
}
// 获得商品 Spu
export const getSpu = (id: number) => {
return request.get({ url: `/product/spu/get-detail?id=${id}` })
}
// 删除商品 Spu
export const deleteSpu = (id: number) => {
return request.delete({ url: `/product/spu/delete?id=${id}` })
}
// 导出商品 Spu Excel
export const exportSpu = async (params) => {
return await request.download({ url: '/product/spu/export', params })
}

View File

@ -0,0 +1,40 @@
import request from '@/config/axios'
export interface DeliveryExpressVO {
id: number
code: string
name: string
logo: string
sort: number
status: number
}
// 查询快递公司列表
export const getDeliveryExpressPage = async (params: PageParam) => {
return await request.get({ url: '/trade/delivery/express/page', params })
}
// 查询快递公司详情
export const getDeliveryExpress = async (id: number) => {
return await request.get({ url: '/trade/delivery/express/get?id=' + id })
}
// 新增快递公司
export const createDeliveryExpress = async (data: DeliveryExpressVO) => {
return await request.post({ url: '/trade/delivery/express/create', data })
}
// 修改快递公司
export const updateDeliveryExpress = async (data: DeliveryExpressVO) => {
return await request.put({ url: '/trade/delivery/express/update', data })
}
// 删除快递公司
export const deleteDeliveryExpress = async (id: number) => {
return await request.delete({ url: '/trade/delivery/express/delete?id=' + id })
}
// 导出快递公司 Excel
export const exportDeliveryExpressApi = async (params) => {
return await request.download({ url: '/trade/delivery/express/export-excel', params })
}

View File

@ -0,0 +1,54 @@
import request from '@/config/axios'
export interface DeliveryExpressTemplateVO {
id: number
name: string
chargeMode: number
sort: number
templateCharge: ExpressTemplateChargeVO[]
templateFree: ExpressTemplateFreeVO[]
}
export declare type ExpressTemplateChargeVO = {
areaIds: number[]
startCount: number
startPrice: number
extraCount: number
extraPrice: number
}
export declare type ExpressTemplateFreeVO = {
areaIds: number[]
freeCount: number
freePrice: number
}
// 查询快递运费模板列表
export const getDeliveryExpressTemplatePage = async (params: PageParam) => {
return await request.get({ url: '/trade/delivery/express-template/page', params })
}
// 查询快递运费模板详情
export const getDeliveryExpressTemplate = async (id: number) => {
return await request.get({ url: '/trade/delivery/express-template/get?id=' + id })
}
// 新增快递运费模板
export const createDeliveryExpressTemplate = async (data: DeliveryExpressTemplateVO) => {
return await request.post({ url: '/trade/delivery/express-template/create', data })
}
// 修改快递运费模板
export const updateDeliveryExpressTemplate = async (data: DeliveryExpressTemplateVO) => {
return await request.put({ url: '/trade/delivery/express-template/update', data })
}
// 删除快递运费模板
export const deleteDeliveryExpressTemplate = async (id: number) => {
return await request.delete({ url: '/trade/delivery/express-template/delete?id=' + id })
}
// 导出快递运费模板 Excel
export const exportDeliveryExpressTemplateApi = async (params) => {
return await request.download({ url: '/trade/delivery/express-template/export-excel', params })
}

View File

@ -1,7 +1,7 @@
import request from '@/config/axios' import request from '@/config/axios'
export interface AccountVO { export interface AccountVO {
id?: number id: number
name: string name: string
} }

View File

@ -39,9 +39,9 @@ export const getMerchantApi = (id: number) => {
} }
// 根据商户名称搜索商户列表 // 根据商户名称搜索商户列表
export const getMerchantListByNameApi = (name: string) => { export const getMerchantListByNameApi = (name?: string) => {
return request.get({ return request.get({
url: '/pay/merchant/list-by-name?id=', url: '/pay/merchant/list-by-name',
params: { params: {
name: name name: name
} }
@ -67,6 +67,7 @@ export const deleteMerchantApi = (id: number) => {
export const exportMerchantApi = (params: MerchantExportReqVO) => { export const exportMerchantApi = (params: MerchantExportReqVO) => {
return request.download({ url: '/pay/merchant/export-excel', params }) return request.download({ url: '/pay/merchant/export-excel', params })
} }
// 支付商户状态修改 // 支付商户状态修改
export const changeMerchantStatusApi = (id: number, status: number) => { export const changeMerchantStatusApi = (id: number, status: number) => {
const data = { const data = {

View File

@ -88,6 +88,11 @@ export const getOrderApi = async (id: number) => {
return await request.get({ url: '/pay/order/get?id=' + id }) return await request.get({ url: '/pay/order/get?id=' + id })
} }
// 获得支付订单的明细
export const getOrderDetailApi = async (id: number) => {
return await request.get({ url: '/pay/order/get-detail?id=' + id })
}
// 新增支付订单 // 新增支付订单
export const createOrderApi = async (data: OrderVO) => { export const createOrderApi = async (data: OrderVO) => {
return await request.post({ url: '/pay/order/create', data }) return await request.post({ url: '/pay/order/create', data })

View File

@ -1,15 +1,19 @@
import request from '@/config/axios/request' import request from '@/config/axios'
// 获得地区树 // 获得地区树
export const getAreaTree = async (content?: any) => { export const getAreaTree = async () => {
return await request.get({ return await request.get({ url: '/system/area/tree' })
url: '/system/area/tree',
params: content
})
} }
export const getChildrenArea = async (id: number) => {
return await request.get({ url: '/system/area/get-children?id=' + id })
}
export const getAreaListByIds = async (ids) => {
return await request.get({ url: '/system/area/get-by-ids?ids=' + ids })
}
// 获得 IP 对应的地区名 // 获得 IP 对应的地区名
export const getAreaByIp = async (ip) => { export const getAreaByIp = async (ip: string) => {
return await request.get({ return await request.get({ url: '/system/area/get-by-ip?ip=' + ip })
url: '/system/area/get-by-ip?ip=' + ip
})
} }

View File

@ -11,27 +11,32 @@ export interface NoticeVO {
createTime: Date createTime: Date
} }
export interface NoticePageReqVO extends PageParam {
title?: string
status?: number
}
// 查询公告列表 // 查询公告列表
export const getNoticePage = (params: PageParam) => { export const getNoticePageApi = (params: NoticePageReqVO) => {
return request.get({ url: '/system/notice/page', params }) return request.get({ url: '/system/notice/page', params })
} }
// 查询公告详情 // 查询公告详情
export const getNotice = (id: number) => { export const getNoticeApi = (id: number) => {
return request.get({ url: '/system/notice/get?id=' + id }) return request.get({ url: '/system/notice/get?id=' + id })
} }
// 新增公告 // 新增公告
export const createNotice = (data: NoticeVO) => { export const createNoticeApi = (data: NoticeVO) => {
return request.post({ url: '/system/notice/create', data }) return request.post({ url: '/system/notice/create', data })
} }
// 修改公告 // 修改公告
export const updateNotice = (data: NoticeVO) => { export const updateNoticeApi = (data: NoticeVO) => {
return request.put({ url: '/system/notice/update', data }) return request.put({ url: '/system/notice/update', data })
} }
// 删除公告 // 删除公告
export const deleteNotice = (id: number) => { export const deleteNoticeApi = (id: number) => {
return request.delete({ url: '/system/notice/delete?id=' + id }) return request.delete({ url: '/system/notice/delete?id=' + id })
} }

View File

@ -23,6 +23,6 @@ export const getAccessTokenPageApi = (params: OAuth2TokenPageReqVO) => {
} }
// 删除 token // 删除 token
export const deleteAccessTokenApi = (accessToken: number) => { export const deleteAccessTokenApi = (accessToken: string) => {
return request.delete({ url: '/system/oauth2-token/delete?accessToken=' + accessToken }) return request.delete({ url: '/system/oauth2-token/delete?accessToken=' + accessToken })
} }

View File

@ -1,4 +1,4 @@
<script setup lang="ts"> <script lang="ts" name="BackTop" setup>
import { ElBacktop } from 'element-plus' import { ElBacktop } from 'element-plus'
import { useDesign } from '@/hooks/web/useDesign' import { useDesign } from '@/hooks/web/useDesign'

View File

@ -1,4 +1,4 @@
<script setup lang="ts"> <script lang="ts" name="ConfigGlobal" setup>
import { propTypes } from '@/utils/propTypes' import { propTypes } from '@/utils/propTypes'
import { useLocaleStore } from '@/store/modules/locale' import { useLocaleStore } from '@/store/modules/locale'
import { useAppStore } from '@/store/modules/app' import { useAppStore } from '@/store/modules/app'
@ -51,9 +51,9 @@ const currentLocale = computed(() => localeStore.currentLocale)
<template> <template>
<ElConfigProvider <ElConfigProvider
:namespace="variables.elNamespace"
:locale="currentLocale.elLocale" :locale="currentLocale.elLocale"
:message="{ max: 1 }" :message="{ max: 1 }"
:namespace="variables.elNamespace"
:size="size" :size="size"
> >
<slot></slot> <slot></slot>

View File

@ -1,4 +1,4 @@
<script setup lang="ts"> <script lang="ts" name="ContentDetailWrap" setup>
import { propTypes } from '@/utils/propTypes' import { propTypes } from '@/utils/propTypes'
import { useDesign } from '@/hooks/web/useDesign' import { useDesign } from '@/hooks/web/useDesign'
@ -21,7 +21,7 @@ onMounted(() => {
</script> </script>
<template> <template>
<div :class="[`${prefixCls}-container`]" ref="contentDetailWrap"> <div ref="contentDetailWrap" :class="[`${prefixCls}-container`]">
<Sticky :offset="offset"> <Sticky :offset="offset">
<div <div
:class="[ :class="[
@ -31,7 +31,7 @@ onMounted(() => {
> >
<div :class="[`${prefixCls}-header__back`, 'flex pl-10px pr-10px ']"> <div :class="[`${prefixCls}-header__back`, 'flex pl-10px pr-10px ']">
<ElButton @click="emit('back')"> <ElButton @click="emit('back')">
<Icon icon="ep:arrow-left" class="mr-5px" /> <Icon class="mr-5px" icon="ep:arrow-left" />
{{ t('common.back') }} {{ t('common.back') }}
</ElButton> </ElButton>
</div> </div>
@ -47,7 +47,9 @@ onMounted(() => {
</Sticky> </Sticky>
<div style="padding: var(--app-content-padding)"> <div style="padding: var(--app-content-padding)">
<ElCard :class="[`${prefixCls}-body`, 'mb-20px']" shadow="never"> <ElCard :class="[`${prefixCls}-body`, 'mb-20px']" shadow="never">
<div> <slot></slot> </div> <div>
<slot></slot>
</div>
</ElCard> </ElCard>
</div> </div>
</div> </div>

View File

@ -1,4 +1,4 @@
<script setup lang="ts"> <script lang="ts" name="ContentWrap" setup>
import { propTypes } from '@/utils/propTypes' import { propTypes } from '@/utils/propTypes'
import { useDesign } from '@/hooks/web/useDesign' import { useDesign } from '@/hooks/web/useDesign'
@ -21,7 +21,7 @@ defineProps({
<template #content> <template #content>
<div class="max-w-200px">{{ message }}</div> <div class="max-w-200px">{{ message }}</div>
</template> </template>
<Icon class="ml-5px" icon="ep:question-filled" :size="14" /> <Icon :size="14" class="ml-5px" icon="ep:question-filled" />
</ElTooltip> </ElTooltip>
</div> </div>
</template> </template>

View File

@ -1,4 +1,4 @@
<script setup lang="ts"> <script lang="ts" name="CountTo" setup>
import { PropType } from 'vue' import { PropType } from 'vue'
import { isNumber } from '@/utils/is' import { isNumber } from '@/utils/is'
import { propTypes } from '@/utils/propTypes' import { propTypes } from '@/utils/propTypes'

View File

@ -1,10 +1,12 @@
<script setup lang="ts"> <script lang="ts" name="Crontab" setup>
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import { PropType } from 'vue' import { PropType } from 'vue'
interface shortcutsType { interface shortcutsType {
text: string text: string
value: string value: string
} }
const props = defineProps({ const props = defineProps({
modelValue: { modelValue: {
type: String, type: String,
@ -501,7 +503,7 @@ const submit = () => {
} }
</script> </script>
<template> <template>
<el-input v-model="defaultValue" v-bind="$attrs" class="input-with-select"> <el-input v-model="defaultValue" class="input-with-select" v-bind="$attrs">
<template #append> <template #append>
<el-select v-model="select" placeholder="生成器" style="width: 115px"> <el-select v-model="select" placeholder="生成器" style="width: 115px">
<el-option label="每分钟" value="0 * * * * ?" /> <el-option label="每分钟" value="0 * * * * ?" />
@ -522,11 +524,11 @@ const submit = () => {
</el-input> </el-input>
<el-dialog <el-dialog
title="cron规则生成器"
v-model="dialogVisible" v-model="dialogVisible"
:width="580" :width="580"
destroy-on-close
append-to-body append-to-body
destroy-on-close
title="cron规则生成器"
> >
<div class="sc-cron"> <div class="sc-cron">
<el-tabs> <el-tabs>
@ -546,38 +548,38 @@ const submit = () => {
<el-radio-button label="3">指定</el-radio-button> <el-radio-button label="3">指定</el-radio-button>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<el-form-item label="范围" v-if="cronValue.second.type == '1'"> <el-form-item v-if="cronValue.second.type == '1'" label="范围">
<el-input-number <el-input-number
v-model="cronValue.second.range.start" v-model="cronValue.second.range.start"
:min="0"
:max="59" :max="59"
:min="0"
controls-position="right" controls-position="right"
/> />
<span style="padding: 0 15px">-</span> <span style="padding: 0 15px">-</span>
<el-input-number <el-input-number
v-model="cronValue.second.range.end" v-model="cronValue.second.range.end"
:min="0"
:max="59" :max="59"
:min="0"
controls-position="right" controls-position="right"
/> />
</el-form-item> </el-form-item>
<el-form-item label="间隔" v-if="cronValue.second.type == '2'"> <el-form-item v-if="cronValue.second.type == '2'" label="间隔">
<el-input-number <el-input-number
v-model="cronValue.second.loop.start" v-model="cronValue.second.loop.start"
:min="0"
:max="59" :max="59"
:min="0"
controls-position="right" controls-position="right"
/> />
秒开始 秒开始
<el-input-number <el-input-number
v-model="cronValue.second.loop.end" v-model="cronValue.second.loop.end"
:min="0"
:max="59" :max="59"
:min="0"
controls-position="right" controls-position="right"
/> />
秒执行一次 秒执行一次
</el-form-item> </el-form-item>
<el-form-item label="指定" v-if="cronValue.second.type == '3'"> <el-form-item v-if="cronValue.second.type == '3'" label="指定">
<el-select v-model="cronValue.second.appoint" multiple style="width: 100%"> <el-select v-model="cronValue.second.appoint" multiple style="width: 100%">
<el-option <el-option
v-for="(item, index) in data.second" v-for="(item, index) in data.second"
@ -605,38 +607,38 @@ const submit = () => {
<el-radio-button label="3">指定</el-radio-button> <el-radio-button label="3">指定</el-radio-button>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<el-form-item label="范围" v-if="cronValue.minute.type == '1'"> <el-form-item v-if="cronValue.minute.type == '1'" label="范围">
<el-input-number <el-input-number
v-model="cronValue.minute.range.start" v-model="cronValue.minute.range.start"
:min="0"
:max="59" :max="59"
:min="0"
controls-position="right" controls-position="right"
/> />
<span style="padding: 0 15px">-</span> <span style="padding: 0 15px">-</span>
<el-input-number <el-input-number
v-model="cronValue.minute.range.end" v-model="cronValue.minute.range.end"
:min="0"
:max="59" :max="59"
:min="0"
controls-position="right" controls-position="right"
/> />
</el-form-item> </el-form-item>
<el-form-item label="间隔" v-if="cronValue.minute.type == '2'"> <el-form-item v-if="cronValue.minute.type == '2'" label="间隔">
<el-input-number <el-input-number
v-model="cronValue.minute.loop.start" v-model="cronValue.minute.loop.start"
:min="0"
:max="59" :max="59"
:min="0"
controls-position="right" controls-position="right"
/> />
分钟开始 分钟开始
<el-input-number <el-input-number
v-model="cronValue.minute.loop.end" v-model="cronValue.minute.loop.end"
:min="0"
:max="59" :max="59"
:min="0"
controls-position="right" controls-position="right"
/> />
分钟执行一次 分钟执行一次
</el-form-item> </el-form-item>
<el-form-item label="指定" v-if="cronValue.minute.type == '3'"> <el-form-item v-if="cronValue.minute.type == '3'" label="指定">
<el-select v-model="cronValue.minute.appoint" multiple style="width: 100%"> <el-select v-model="cronValue.minute.appoint" multiple style="width: 100%">
<el-option <el-option
v-for="(item, index) in data.minute" v-for="(item, index) in data.minute"
@ -664,38 +666,38 @@ const submit = () => {
<el-radio-button label="3">指定</el-radio-button> <el-radio-button label="3">指定</el-radio-button>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<el-form-item label="范围" v-if="cronValue.hour.type == '1'"> <el-form-item v-if="cronValue.hour.type == '1'" label="范围">
<el-input-number <el-input-number
v-model="cronValue.hour.range.start" v-model="cronValue.hour.range.start"
:min="0"
:max="23" :max="23"
:min="0"
controls-position="right" controls-position="right"
/> />
<span style="padding: 0 15px">-</span> <span style="padding: 0 15px">-</span>
<el-input-number <el-input-number
v-model="cronValue.hour.range.end" v-model="cronValue.hour.range.end"
:min="0"
:max="23" :max="23"
:min="0"
controls-position="right" controls-position="right"
/> />
</el-form-item> </el-form-item>
<el-form-item label="间隔" v-if="cronValue.hour.type == '2'"> <el-form-item v-if="cronValue.hour.type == '2'" label="间隔">
<el-input-number <el-input-number
v-model="cronValue.hour.loop.start" v-model="cronValue.hour.loop.start"
:min="0"
:max="23" :max="23"
:min="0"
controls-position="right" controls-position="right"
/> />
小时开始 小时开始
<el-input-number <el-input-number
v-model="cronValue.hour.loop.end" v-model="cronValue.hour.loop.end"
:min="0"
:max="23" :max="23"
:min="0"
controls-position="right" controls-position="right"
/> />
小时执行一次 小时执行一次
</el-form-item> </el-form-item>
<el-form-item label="指定" v-if="cronValue.hour.type == '3'"> <el-form-item v-if="cronValue.hour.type == '3'" label="指定">
<el-select v-model="cronValue.hour.appoint" multiple style="width: 100%"> <el-select v-model="cronValue.hour.appoint" multiple style="width: 100%">
<el-option <el-option
v-for="(item, index) in data.hour" v-for="(item, index) in data.hour"
@ -725,38 +727,38 @@ const submit = () => {
<el-radio-button label="5">不指定</el-radio-button> <el-radio-button label="5">不指定</el-radio-button>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<el-form-item label="范围" v-if="cronValue.day.type == '1'"> <el-form-item v-if="cronValue.day.type == '1'" label="范围">
<el-input-number <el-input-number
v-model="cronValue.day.range.start" v-model="cronValue.day.range.start"
:min="1"
:max="31" :max="31"
:min="1"
controls-position="right" controls-position="right"
/> />
<span style="padding: 0 15px">-</span> <span style="padding: 0 15px">-</span>
<el-input-number <el-input-number
v-model="cronValue.day.range.end" v-model="cronValue.day.range.end"
:min="1"
:max="31" :max="31"
:min="1"
controls-position="right" controls-position="right"
/> />
</el-form-item> </el-form-item>
<el-form-item label="间隔" v-if="cronValue.day.type == '2'"> <el-form-item v-if="cronValue.day.type == '2'" label="间隔">
<el-input-number <el-input-number
v-model="cronValue.day.loop.start" v-model="cronValue.day.loop.start"
:min="1"
:max="31" :max="31"
:min="1"
controls-position="right" controls-position="right"
/> />
号开始 号开始
<el-input-number <el-input-number
v-model="cronValue.day.loop.end" v-model="cronValue.day.loop.end"
:min="1"
:max="31" :max="31"
:min="1"
controls-position="right" controls-position="right"
/> />
天执行一次 天执行一次
</el-form-item> </el-form-item>
<el-form-item label="指定" v-if="cronValue.day.type == '3'"> <el-form-item v-if="cronValue.day.type == '3'" label="指定">
<el-select v-model="cronValue.day.appoint" multiple style="width: 100%"> <el-select v-model="cronValue.day.appoint" multiple style="width: 100%">
<el-option <el-option
v-for="(item, index) in data.day" v-for="(item, index) in data.day"
@ -784,38 +786,38 @@ const submit = () => {
<el-radio-button label="3">指定</el-radio-button> <el-radio-button label="3">指定</el-radio-button>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<el-form-item label="范围" v-if="cronValue.month.type == '1'"> <el-form-item v-if="cronValue.month.type == '1'" label="范围">
<el-input-number <el-input-number
v-model="cronValue.month.range.start" v-model="cronValue.month.range.start"
:min="1"
:max="12" :max="12"
:min="1"
controls-position="right" controls-position="right"
/> />
<span style="padding: 0 15px">-</span> <span style="padding: 0 15px">-</span>
<el-input-number <el-input-number
v-model="cronValue.month.range.end" v-model="cronValue.month.range.end"
:min="1"
:max="12" :max="12"
:min="1"
controls-position="right" controls-position="right"
/> />
</el-form-item> </el-form-item>
<el-form-item label="间隔" v-if="cronValue.month.type == '2'"> <el-form-item v-if="cronValue.month.type == '2'" label="间隔">
<el-input-number <el-input-number
v-model="cronValue.month.loop.start" v-model="cronValue.month.loop.start"
:min="1"
:max="12" :max="12"
:min="1"
controls-position="right" controls-position="right"
/> />
月开始 月开始
<el-input-number <el-input-number
v-model="cronValue.month.loop.end" v-model="cronValue.month.loop.end"
:min="1"
:max="12" :max="12"
:min="1"
controls-position="right" controls-position="right"
/> />
月执行一次 月执行一次
</el-form-item> </el-form-item>
<el-form-item label="指定" v-if="cronValue.month.type == '3'"> <el-form-item v-if="cronValue.month.type == '3'" label="指定">
<el-select v-model="cronValue.month.appoint" multiple style="width: 100%"> <el-select v-model="cronValue.month.appoint" multiple style="width: 100%">
<el-option <el-option
v-for="(item, index) in data.month" v-for="(item, index) in data.month"
@ -846,7 +848,7 @@ const submit = () => {
<el-radio-button label="5">不指定</el-radio-button> <el-radio-button label="5">不指定</el-radio-button>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<el-form-item label="范围" v-if="cronValue.week.type == '1'"> <el-form-item v-if="cronValue.week.type == '1'" label="范围">
<el-select v-model="cronValue.week.range.start"> <el-select v-model="cronValue.week.range.start">
<el-option <el-option
v-for="(item, index) in data.week" v-for="(item, index) in data.week"
@ -865,12 +867,12 @@ const submit = () => {
/> />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="间隔" v-if="cronValue.week.type == '2'"> <el-form-item v-if="cronValue.week.type == '2'" label="间隔">
<el-input-number <el-input-number
v-model="cronValue.week.loop.start" v-model="cronValue.week.loop.start"
:min="1"
:max="4" :max="4"
:min="1"
controls-position="right" controls-position="right"
/> />
周的星期 周的星期
@ -884,7 +886,7 @@ const submit = () => {
</el-select> </el-select>
执行一次 执行一次
</el-form-item> </el-form-item>
<el-form-item label="指定" v-if="cronValue.week.type == '3'"> <el-form-item v-if="cronValue.week.type == '3'" label="指定">
<el-select v-model="cronValue.week.appoint" multiple style="width: 100%"> <el-select v-model="cronValue.week.appoint" multiple style="width: 100%">
<el-option <el-option
v-for="(item, index) in data.week" v-for="(item, index) in data.week"
@ -894,7 +896,7 @@ const submit = () => {
/> />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="最后一周" v-if="cronValue.week.type == '4'"> <el-form-item v-if="cronValue.week.type == '4'" label="最后一周">
<el-select v-model="cronValue.week.last"> <el-select v-model="cronValue.week.last">
<el-option <el-option
v-for="(item, index) in data.week" v-for="(item, index) in data.week"
@ -924,12 +926,12 @@ const submit = () => {
<el-radio-button label="3">指定</el-radio-button> <el-radio-button label="3">指定</el-radio-button>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<el-form-item label="范围" v-if="cronValue.year.type == '1'"> <el-form-item v-if="cronValue.year.type == '1'" label="范围">
<el-input-number v-model="cronValue.year.range.start" controls-position="right" /> <el-input-number v-model="cronValue.year.range.start" controls-position="right" />
<span style="padding: 0 15px">-</span> <span style="padding: 0 15px">-</span>
<el-input-number v-model="cronValue.year.range.end" controls-position="right" /> <el-input-number v-model="cronValue.year.range.end" controls-position="right" />
</el-form-item> </el-form-item>
<el-form-item label="间隔" v-if="cronValue.year.type == '2'"> <el-form-item v-if="cronValue.year.type == '2'" label="间隔">
<el-input-number v-model="cronValue.year.loop.start" controls-position="right" /> <el-input-number v-model="cronValue.year.loop.start" controls-position="right" />
年开始 年开始
<el-input-number <el-input-number
@ -939,7 +941,7 @@ const submit = () => {
/> />
年执行一次 年执行一次
</el-form-item> </el-form-item>
<el-form-item label="指定" v-if="cronValue.year.type == '3'"> <el-form-item v-if="cronValue.year.type == '3'" label="指定">
<el-select v-model="cronValue.year.appoint" multiple style="width: 100%"> <el-select v-model="cronValue.year.appoint" multiple style="width: 100%">
<el-option <el-option
v-for="(item, index) in data.year" v-for="(item, index) in data.year"
@ -968,16 +970,19 @@ const submit = () => {
padding: 0 7px; padding: 0 7px;
vertical-align: bottom; vertical-align: bottom;
} }
.sc-cron-num { .sc-cron-num {
text-align: center; text-align: center;
margin-bottom: 15px; margin-bottom: 15px;
width: 100%; width: 100%;
} }
.sc-cron-num h2 { .sc-cron-num h2 {
font-size: 12px; font-size: 12px;
margin-bottom: 15px; margin-bottom: 15px;
font-weight: normal; font-weight: normal;
} }
.sc-cron-num h4 { .sc-cron-num h4 {
display: block; display: block;
height: 32px; height: 32px;
@ -988,13 +993,16 @@ const submit = () => {
background: var(--el-color-primary-light-9); background: var(--el-color-primary-light-9);
border-radius: 4px; border-radius: 4px;
} }
.sc-cron:deep(.el-tabs__item.is-active) .sc-cron-num h4 { .sc-cron:deep(.el-tabs__item.is-active) .sc-cron-num h4 {
background: var(--el-color-primary); background: var(--el-color-primary);
color: #fff; color: #fff;
} }
[data-theme='dark'] .sc-cron-num h4 { [data-theme='dark'] .sc-cron-num h4 {
background: var(--el-color-white); background: var(--el-color-white);
} }
.input-with-select .el-input-group__prepend { .input-with-select .el-input-group__prepend {
background-color: var(--el-fill-color-blank); background-color: var(--el-fill-color-blank);
} }

View File

@ -2,26 +2,26 @@
<div> <div>
<Dialog <Dialog
v-model="dialogVisible" v-model="dialogVisible"
:title="t('cropper.modalTitle')"
width="800px"
maxHeight="380px"
:canFullscreen="false" :canFullscreen="false"
:title="t('cropper.modalTitle')"
maxHeight="380px"
width="800px"
> >
<div :class="prefixCls"> <div :class="prefixCls">
<div :class="`${prefixCls}-left`"> <div :class="`${prefixCls}-left`">
<div :class="`${prefixCls}-cropper`"> <div :class="`${prefixCls}-cropper`">
<CropperImage <CropperImage
v-if="src" v-if="src"
:circled="circled"
:src="src" :src="src"
height="300px" height="300px"
:circled="circled"
@cropend="handleCropend" @cropend="handleCropend"
@ready="handleReady" @ready="handleReady"
/> />
</div> </div>
<div :class="`${prefixCls}-toolbar`"> <div :class="`${prefixCls}-toolbar`">
<el-upload :fileList="[]" accept="image/*" :beforeUpload="handleBeforeUpload"> <el-upload :beforeUpload="handleBeforeUpload" :fileList="[]" accept="image/*">
<el-tooltip :content="t('cropper.selectImage')" placement="bottom"> <el-tooltip :content="t('cropper.selectImage')" placement="bottom">
<XButton preIcon="ant-design:upload-outlined" type="primary" /> <XButton preIcon="ant-design:upload-outlined" type="primary" />
</el-tooltip> </el-tooltip>
@ -29,64 +29,64 @@
<el-space> <el-space>
<el-tooltip :content="t('cropper.btn_reset')" placement="bottom"> <el-tooltip :content="t('cropper.btn_reset')" placement="bottom">
<XButton <XButton
type="primary" :disabled="!src"
preIcon="ant-design:reload-outlined" preIcon="ant-design:reload-outlined"
size="small" size="small"
:disabled="!src" type="primary"
@click="handlerToolbar('reset')" @click="handlerToolbar('reset')"
/> />
</el-tooltip> </el-tooltip>
<el-tooltip :content="t('cropper.btn_rotate_left')" placement="bottom"> <el-tooltip :content="t('cropper.btn_rotate_left')" placement="bottom">
<XButton <XButton
type="primary" :disabled="!src"
preIcon="ant-design:rotate-left-outlined" preIcon="ant-design:rotate-left-outlined"
size="small" size="small"
:disabled="!src" type="primary"
@click="handlerToolbar('rotate', -45)" @click="handlerToolbar('rotate', -45)"
/> />
</el-tooltip> </el-tooltip>
<el-tooltip :content="t('cropper.btn_rotate_right')" placement="bottom"> <el-tooltip :content="t('cropper.btn_rotate_right')" placement="bottom">
<XButton <XButton
type="primary" :disabled="!src"
preIcon="ant-design:rotate-right-outlined" preIcon="ant-design:rotate-right-outlined"
size="small" size="small"
:disabled="!src" type="primary"
@click="handlerToolbar('rotate', 45)" @click="handlerToolbar('rotate', 45)"
/> />
</el-tooltip> </el-tooltip>
<el-tooltip :content="t('cropper.btn_scale_x')" placement="bottom"> <el-tooltip :content="t('cropper.btn_scale_x')" placement="bottom">
<XButton <XButton
type="primary" :disabled="!src"
preIcon="vaadin:arrows-long-h" preIcon="vaadin:arrows-long-h"
size="small" size="small"
:disabled="!src" type="primary"
@click="handlerToolbar('scaleX')" @click="handlerToolbar('scaleX')"
/> />
</el-tooltip> </el-tooltip>
<el-tooltip :content="t('cropper.btn_scale_y')" placement="bottom"> <el-tooltip :content="t('cropper.btn_scale_y')" placement="bottom">
<XButton <XButton
type="primary" :disabled="!src"
preIcon="vaadin:arrows-long-v" preIcon="vaadin:arrows-long-v"
size="small" size="small"
:disabled="!src" type="primary"
@click="handlerToolbar('scaleY')" @click="handlerToolbar('scaleY')"
/> />
</el-tooltip> </el-tooltip>
<el-tooltip :content="t('cropper.btn_zoom_in')" placement="bottom"> <el-tooltip :content="t('cropper.btn_zoom_in')" placement="bottom">
<XButton <XButton
type="primary" :disabled="!src"
preIcon="ant-design:zoom-in-outlined" preIcon="ant-design:zoom-in-outlined"
size="small" size="small"
:disabled="!src" type="primary"
@click="handlerToolbar('zoom', 0.1)" @click="handlerToolbar('zoom', 0.1)"
/> />
</el-tooltip> </el-tooltip>
<el-tooltip :content="t('cropper.btn_zoom_out')" placement="bottom"> <el-tooltip :content="t('cropper.btn_zoom_out')" placement="bottom">
<XButton <XButton
type="primary" :disabled="!src"
preIcon="ant-design:zoom-out-outlined" preIcon="ant-design:zoom-out-outlined"
size="small" size="small"
:disabled="!src" type="primary"
@click="handlerToolbar('zoom', -0.1)" @click="handlerToolbar('zoom', -0.1)"
/> />
</el-tooltip> </el-tooltip>
@ -95,14 +95,14 @@
</div> </div>
<div :class="`${prefixCls}-right`"> <div :class="`${prefixCls}-right`">
<div :class="`${prefixCls}-preview`"> <div :class="`${prefixCls}-preview`">
<img :src="previewSource" v-if="previewSource" :alt="t('cropper.preview')" /> <img v-if="previewSource" :alt="t('cropper.preview')" :src="previewSource" />
</div> </div>
<template v-if="previewSource"> <template v-if="previewSource">
<div :class="`${prefixCls}-group`"> <div :class="`${prefixCls}-group`">
<el-avatar :src="previewSource" size="large" /> <el-avatar :src="previewSource" size="large" />
<el-avatar :src="previewSource" :size="48" /> <el-avatar :size="48" :src="previewSource" />
<el-avatar :src="previewSource" :size="64" /> <el-avatar :size="64" :src="previewSource" />
<el-avatar :src="previewSource" :size="80" /> <el-avatar :size="80" :src="previewSource" />
</div> </div>
</template> </template>
</div> </div>
@ -113,7 +113,7 @@
</Dialog> </Dialog>
</div> </div>
</template> </template>
<script setup lang="ts"> <script lang="ts" name="CopperModal" setup>
import { useDesign } from '@/hooks/web/useDesign' import { useDesign } from '@/hooks/web/useDesign'
import { dataURLtoBlob } from '@/utils/filt' import { dataURLtoBlob } from '@/utils/filt'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
@ -173,12 +173,15 @@ async function handleOk() {
const blob = dataURLtoBlob(previewSource.value) const blob = dataURLtoBlob(previewSource.value)
emit('uploadSuccess', { source: previewSource.value, data: blob, filename: filename }) emit('uploadSuccess', { source: previewSource.value, data: blob, filename: filename })
} }
function openModal() { function openModal() {
dialogVisible.value = true dialogVisible.value = true
} }
function closeModal() { function closeModal() {
dialogVisible.value = false dialogVisible.value = false
} }
defineExpose({ openModal, closeModal }) defineExpose({ openModal, closeModal })
</script> </script>
<style lang="scss"> <style lang="scss">

View File

@ -3,14 +3,14 @@
<img <img
v-show="isReady" v-show="isReady"
ref="imgElRef" ref="imgElRef"
:src="src"
:alt="alt" :alt="alt"
:crossorigin="crossorigin" :crossorigin="crossorigin"
:src="src"
:style="getImageStyle" :style="getImageStyle"
/> />
</div> </div>
</template> </template>
<script setup lang="ts"> <script lang="ts" name="Cropper" setup>
import { CSSProperties, PropType } from 'vue' import { CSSProperties, PropType } from 'vue'
import Cropper from 'cropperjs' import Cropper from 'cropperjs'
import 'cropperjs/dist/cropper.css' import 'cropperjs/dist/cropper.css'

View File

@ -1,17 +1,17 @@
<template> <template>
<div class="user-info-head" @click="open()"> <div class="user-info-head" @click="open()">
<img :src="sourceValue" v-if="sourceValue" class="img-circle img-lg" alt="avatar" /> <img v-if="sourceValue" :src="sourceValue" alt="avatar" class="img-circle img-lg" />
<el-button :class="`${prefixCls}-upload-btn`" @click="open()" v-if="showBtn"> <el-button v-if="showBtn" :class="`${prefixCls}-upload-btn`" @click="open()">
{{ btnText ? btnText : t('cropper.selectImage') }} {{ btnText ? btnText : t('cropper.selectImage') }}
</el-button> </el-button>
<CopperModal <CopperModal
ref="cropperModelRef" ref="cropperModelRef"
@upload-success="handleUploadSuccess"
:srcValue="sourceValue" :srcValue="sourceValue"
@upload-success="handleUploadSuccess"
/> />
</div> </div>
</template> </template>
<script setup lang="ts"> <script lang="ts" name="CropperAvatar" setup>
import { useDesign } from '@/hooks/web/useDesign' import { useDesign } from '@/hooks/web/useDesign'
import { propTypes } from '@/utils/propTypes' import { propTypes } from '@/utils/propTypes'
@ -54,9 +54,11 @@ function handleUploadSuccess({ source, data, filename }) {
function open() { function open() {
cropperModelRef.value.openModal() cropperModelRef.value.openModal()
} }
function close() { function close() {
cropperModelRef.value.closeModal() cropperModelRef.value.closeModal()
} }
defineExpose({ defineExpose({
open, open,
close close
@ -104,17 +106,21 @@ $prefix-cls: #{$namespace}--cropper-avatar;
margin: 10px auto; margin: 10px auto;
} }
} }
.user-info-head { .user-info-head {
position: relative; position: relative;
display: inline-block; display: inline-block;
} }
.img-circle { .img-circle {
border-radius: 50%; border-radius: 50%;
} }
.img-lg { .img-lg {
width: 120px; width: 120px;
height: 120px; height: 120px;
} }
.user-info-head:hover:after { .user-info-head:hover:after {
content: '+'; content: '+';
position: absolute; position: absolute;

View File

@ -1,4 +1,4 @@
<script setup lang="ts"> <script lang="ts" name="Descriptions" setup>
import { PropType } from 'vue' import { PropType } from 'vue'
import dayjs from 'dayjs' import dayjs from 'dayjs'
import { useDesign } from '@/hooks/web/useDesign' import { useDesign } from '@/hooks/web/useDesign'
@ -84,7 +84,7 @@ const toggleClick = () => {
<div class="flex items-center"> <div class="flex items-center">
{{ title }} {{ title }}
<ElTooltip v-if="message" :content="message" placement="right"> <ElTooltip v-if="message" :content="message" placement="right">
<Icon icon="ep:warning" class="ml-5px" /> <Icon class="ml-5px" icon="ep:warning" />
</ElTooltip> </ElTooltip>
</div> </div>
</div> </div>
@ -95,8 +95,8 @@ const toggleClick = () => {
<div v-show="show" :class="[`${prefixCls}-content`, 'p-10px']"> <div v-show="show" :class="[`${prefixCls}-content`, 'p-10px']">
<ElDescriptions <ElDescriptions
:column="props.columns" :column="props.columns"
border
:direction="mobile ? 'vertical' : 'horizontal'" :direction="mobile ? 'vertical' : 'horizontal'"
border
v-bind="getBindValue" v-bind="getBindValue"
> >
<template v-if="slots['extra']" #extra> <template v-if="slots['extra']" #extra>
@ -114,8 +114,8 @@ const toggleClick = () => {
:row="{ :row="{
label: item.label label: item.label
}" }"
>{{ item.label }}</slot >{{ item.label }}
> </slot>
</template> </template>
<template #default> <template #default>

View File

@ -1,4 +1,4 @@
<script setup lang="ts"> <script lang="ts" name="Dialog" setup>
import { propTypes } from '@/utils/propTypes' import { propTypes } from '@/utils/propTypes'
import { isNumber } from '@/utils/is' import { isNumber } from '@/utils/is'
@ -59,13 +59,13 @@ const dialogStyle = computed(() => {
<template> <template>
<ElDialog <ElDialog
v-bind="getBindValue"
:fullscreen="isFullscreen"
destroy-on-close
lock-scroll
draggable
:width="width"
:close-on-click-modal="true" :close-on-click-modal="true"
:fullscreen="isFullscreen"
:width="width"
destroy-on-close
draggable
lock-scroll
v-bind="getBindValue"
> >
<template #header> <template #header>
<div class="flex justify-between"> <div class="flex justify-between">
@ -74,8 +74,8 @@ const dialogStyle = computed(() => {
</slot> </slot>
<Icon <Icon
v-if="fullscreen" v-if="fullscreen"
class="mr-22px cursor-pointer is-hover mt-2px z-10"
:icon="isFullscreen ? 'zmdi:fullscreen-exit' : 'zmdi:fullscreen'" :icon="isFullscreen ? 'zmdi:fullscreen-exit' : 'zmdi:fullscreen'"
class="mr-22px cursor-pointer is-hover mt-2px z-10"
color="var(--el-color-info)" color="var(--el-color-info)"
@click="toggleFull" @click="toggleFull"
/> />
@ -83,7 +83,7 @@ const dialogStyle = computed(() => {
</template> </template>
<!-- 情况一如果 scroll true说明开启滚动条 --> <!-- 情况一如果 scroll true说明开启滚动条 -->
<ElScrollbar :style="dialogStyle" v-if="scroll"> <ElScrollbar v-if="scroll" :style="dialogStyle">
<slot></slot> <slot></slot>
</ElScrollbar> </ElScrollbar>
<!-- 情况二如果 scroll false说明关闭滚动条滚动条 --> <!-- 情况二如果 scroll false说明关闭滚动条滚动条 -->

View File

@ -48,6 +48,7 @@ export default defineComponent({
? dictData.value?.cssClass ? dictData.value?.cssClass
: '' : ''
} }
disableTransitions={true}
> >
{dictData.value?.label} {dictData.value?.label}
</ElTag> </ElTag>

View File

@ -0,0 +1,32 @@
<template>
<el-alert v-if="getEnable()" type="success" show-icon>
<template #title>
<div @click="goToUrl">{{ '' + title + '文档地址' + url }}</div>
</template>
</el-alert>
</template>
<script setup lang="tsx" name="DocAlert">
import { propTypes } from '@/utils/propTypes'
const props = defineProps({
title: propTypes.string,
url: propTypes.string
})
/** 跳转 URL 链接 */
const goToUrl = () => {
window.open(props.url)
}
/** 是否开启 */
const getEnable = () => {
return import.meta.env.VITE_APP_TENANT_ENABLE === 'true'
}
</script>
<style scoped>
.el-alert--success.is-light {
border: 1px solid green;
margin-bottom: 10px;
cursor: pointer;
}
</style>

View File

@ -1,4 +1,4 @@
<script setup lang="ts"> <script lang="ts" name="EChart" setup>
import type { EChartsOption } from 'echarts' import type { EChartsOption } from 'echarts'
import echarts from '@/plugins/echarts' import echarts from '@/plugins/echarts'
import { debounce } from 'lodash-es' import { debounce } from 'lodash-es'

View File

@ -1,7 +1,7 @@
<script setup lang="ts"> <script lang="ts" name="Editor" setup>
import { PropType } from 'vue' import { PropType } from 'vue'
import { Editor, Toolbar } from '@wangeditor/editor-for-vue' import { Editor, Toolbar } from '@wangeditor/editor-for-vue'
import { IDomEditor, IEditorConfig, i18nChangeLanguage } from '@wangeditor/editor' import { i18nChangeLanguage, IDomEditor, IEditorConfig } from '@wangeditor/editor'
import { propTypes } from '@/utils/propTypes' import { propTypes } from '@/utils/propTypes'
import { isNumber } from '@/utils/is' import { isNumber } from '@/utils/is'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
@ -20,7 +20,7 @@ const props = defineProps({
editorId: propTypes.string.def('wangeEditor-1'), editorId: propTypes.string.def('wangeEditor-1'),
height: propTypes.oneOfType([Number, String]).def('500px'), height: propTypes.oneOfType([Number, String]).def('500px'),
editorConfig: { editorConfig: {
type: Object as PropType<IEditorConfig>, type: Object as PropType<Partial<IEditorConfig>>,
default: () => undefined default: () => undefined
}, },
readonly: propTypes.bool.def(false), readonly: propTypes.bool.def(false),
@ -147,6 +147,7 @@ const editorConfig = computed((): IEditorConfig => {
props.editorConfig || {} props.editorConfig || {}
) )
}) })
const editorStyle = computed(() => { const editorStyle = computed(() => {
return { return {
height: isNumber(props.height) ? `${props.height}px` : props.height height: isNumber(props.height) ? `${props.height}px` : props.height
@ -188,8 +189,8 @@ defineExpose({
<!-- 编辑器 --> <!-- 编辑器 -->
<Editor <Editor
v-model="valueHtml" v-model="valueHtml"
:editorId="editorId"
:defaultConfig="editorConfig" :defaultConfig="editorConfig"
:editorId="editorId"
:style="editorStyle" :style="editorStyle"
@on-change="handleChange" @on-change="handleChange"
@on-created="handleCreated" @on-created="handleCreated"

View File

@ -1,4 +1,4 @@
<script setup lang="ts"> <script lang="ts" name="Error" setup>
import pageError from '@/assets/svgs/404.svg' import pageError from '@/assets/svgs/404.svg'
import networkError from '@/assets/svgs/500.svg' import networkError from '@/assets/svgs/500.svg'
import noPermission from '@/assets/svgs/403.svg' import noPermission from '@/assets/svgs/403.svg'
@ -46,7 +46,7 @@ const btnClick = () => {
<template> <template>
<div class="flex justify-center"> <div class="flex justify-center">
<div class="text-center"> <div class="text-center">
<img width="350" :src="errorMap[type].url" alt="" /> <img :src="errorMap[type].url" alt="" width="350" />
<div class="text-14px text-[var(--el-color-info)]">{{ errorMap[type].message }}</div> <div class="text-14px text-[var(--el-color-info)]">{{ errorMap[type].message }}</div>
<div class="mt-20px"> <div class="mt-20px">
<ElButton type="primary" @click="btnClick">{{ errorMap[type].buttonText }}</ElButton> <ElButton type="primary" @click="btnClick">{{ errorMap[type].buttonText }}</ElButton>

View File

@ -1,6 +1,6 @@
import type { Slots } from 'vue' import type { Slots } from 'vue'
import { getSlot } from '@/utils/tsxHelper' import { getSlot } from '@/utils/tsxHelper'
import { PlaceholderMoel } from './types' import { PlaceholderModel } from './types'
import { FormSchema } from '@/types/form' import { FormSchema } from '@/types/form'
import { ColProps } from '@/types/components' import { ColProps } from '@/types/components'
@ -10,7 +10,7 @@ import { ColProps } from '@/types/components'
* @returns * @returns
* @description placeholder * @description placeholder
*/ */
export const setTextPlaceholder = (schema: FormSchema): PlaceholderMoel => { export const setTextPlaceholder = (schema: FormSchema): PlaceholderModel => {
const { t } = useI18n() const { t } = useI18n()
const textMap = ['Input', 'Autocomplete', 'InputNumber', 'InputPassword'] const textMap = ['Input', 'Autocomplete', 'InputNumber', 'InputPassword']
const selectMap = ['Select', 'SelectV2', 'TimePicker', 'DatePicker', 'TimeSelect', 'TimeSelect'] const selectMap = ['Select', 'SelectV2', 'TimePicker', 'DatePicker', 'TimeSelect', 'TimeSelect']
@ -108,8 +108,8 @@ export const setItemComponentSlots = (
/** /**
* *
* @param schema Form表单结构化数组 * @param schema Form表单结构化数组
* @param formModel FormMoel * @param formModel FormModel
* @returns FormMoel * @returns FormModel
* @description formModel * @description formModel
*/ */
export const initModel = (schema: FormSchema[], formModel: Recordable) => { export const initModel = (schema: FormSchema[], formModel: Recordable) => {

View File

@ -1,6 +1,6 @@
import { FormSchema } from '@/types/form' import { FormSchema } from '@/types/form'
export interface PlaceholderMoel { export interface PlaceholderModel {
placeholder?: string placeholder?: string
startPlaceholder?: string startPlaceholder?: string
endPlaceholder?: string endPlaceholder?: string

View File

@ -1,4 +1,4 @@
<script setup lang="ts"> <script lang="ts" name="IFrame" setup>
import { propTypes } from '@/utils/propTypes' import { propTypes } from '@/utils/propTypes'
const props = defineProps({ const props = defineProps({
@ -20,11 +20,11 @@ onMounted(() => {
<template> <template>
<div v-loading="loading" :style="'height:' + height"> <div v-loading="loading" :style="'height:' + height">
<iframe <iframe
ref="frameRef"
:src="props.src" :src="props.src"
style="width: 100%; height: 100%"
frameborder="no" frameborder="no"
scrolling="auto" scrolling="auto"
ref="frameRef" style="width: 100%; height: 100%"
></iframe> ></iframe>
</div> </div>
</template> </template>

View File

@ -1,4 +1,4 @@
<script setup lang="ts"> <script lang="ts" name="Icon" setup>
import { propTypes } from '@/utils/propTypes' import { propTypes } from '@/utils/propTypes'
import Iconify from '@purge-icons/generated' import Iconify from '@purge-icons/generated'
import { useDesign } from '@/hooks/web/useDesign' import { useDesign } from '@/hooks/web/useDesign'
@ -72,7 +72,7 @@ watch(
<template> <template>
<ElIcon :class="prefixCls" :color="color" :size="size"> <ElIcon :class="prefixCls" :color="color" :size="size">
<svg v-if="isLocal" aria-hidden="true" :class="getSvgClass"> <svg v-if="isLocal" :class="getSvgClass" aria-hidden="true">
<use :xlink:href="symbolId" /> <use :xlink:href="symbolId" />
</svg> </svg>

View File

@ -1,4 +1,4 @@
<script setup lang="ts"> <script lang="ts" name="IconSelect" setup>
import { CSSProperties } from 'vue' import { CSSProperties } from 'vue'
import { cloneDeep } from 'lodash-es' import { cloneDeep } from 'lodash-es'
import { IconJson } from '@/components/Icon/src/data' import { IconJson } from '@/components/Icon/src/data'
@ -95,7 +95,7 @@ watch(
return props.modelValue return props.modelValue
}, },
() => { () => {
if (props.modelValue) { if (props.modelValue && props.modelValue.indexOf(':') >= 0) {
currentActiveType.value = props.modelValue.substring(0, props.modelValue.indexOf(':') + 1) currentActiveType.value = props.modelValue.substring(0, props.modelValue.indexOf(':') + 1)
icon.value = props.modelValue.substring(props.modelValue.indexOf(':') + 1) icon.value = props.modelValue.substring(props.modelValue.indexOf(':') + 1)
} }
@ -116,13 +116,13 @@ watch(
<ElInput v-model="inputValue" @click="visible = !visible"> <ElInput v-model="inputValue" @click="visible = !visible">
<template #append> <template #append>
<ElPopover <ElPopover
:width="350"
trigger="click"
popper-class="pure-popper"
:popper-options="{ :popper-options="{
placement: 'auto' placement: 'auto'
}" }"
:visible="visible" :visible="visible"
:width="350"
popper-class="pure-popper"
trigger="click"
> >
<template #reference> <template #reference>
<div <div
@ -133,7 +133,7 @@ watch(
</div> </div>
</template> </template>
<ElInput class="p-2" v-model="filterValue" placeholder="搜索图标" clearable /> <ElInput v-model="filterValue" class="p-2" clearable placeholder="搜索图标" />
<ElDivider border-style="dashed" /> <ElDivider border-style="dashed" />
<ElTabs v-model="currentActiveType" @tab-click="handleClick"> <ElTabs v-model="currentActiveType" @tab-click="handleClick">
@ -143,15 +143,15 @@ watch(
:label="pane.label" :label="pane.label"
:name="pane.name" :name="pane.name"
> >
<ElDivider class="tab-divider" border-style="dashed" /> <ElDivider border-style="dashed" class="tab-divider" />
<ElScrollbar height="220px"> <ElScrollbar height="220px">
<ul class="flex flex-wrap px-2 ml-2"> <ul class="flex flex-wrap px-2 ml-2">
<li <li
v-for="(item, key) in pageList" v-for="(item, key) in pageList"
:key="key" :key="key"
:style="iconItemStyle(item)"
:title="item" :title="item"
class="icon-item p-2 w-1/10 cursor-pointer mr-2 mt-1 flex justify-center items-center border border-solid" class="icon-item p-2 w-1/10 cursor-pointer mr-2 mt-1 flex justify-center items-center border border-solid"
:style="iconItemStyle(item)"
@click="onChangeIcon(item)" @click="onChangeIcon(item)"
> >
<Icon :icon="currentActiveType + item" /> <Icon :icon="currentActiveType + item" />
@ -163,13 +163,13 @@ watch(
<ElDivider border-style="dashed" /> <ElDivider border-style="dashed" />
<ElPagination <ElPagination
small
:total="iconCount"
:page-size="pageSize"
:current-page="currentPage" :current-page="currentPage"
:page-size="pageSize"
:total="iconCount"
background background
layout="prev, pager, next"
class="flex items-center justify-center h-10" class="flex items-center justify-center h-10"
layout="prev, pager, next"
small
@current-change="onCurrentChange" @current-change="onCurrentChange"
/> />
</ElPopover> </ElPopover>

View File

@ -1,4 +1,4 @@
<script setup lang="ts"> <script lang="ts" name="ImageViewer" setup>
import { PropType } from 'vue' import { PropType } from 'vue'
import { propTypes } from '@/utils/propTypes' import { propTypes } from '@/utils/propTypes'

View File

@ -1,4 +1,4 @@
<script setup lang="ts"> <script lang="ts" name="InfoTip" setup>
import { PropType } from 'vue' import { PropType } from 'vue'
import { useDesign } from '@/hooks/web/useDesign' import { useDesign } from '@/hooks/web/useDesign'
import { propTypes } from '@/utils/propTypes' import { propTypes } from '@/utils/propTypes'
@ -34,14 +34,14 @@ const keyClick = (key: string) => {
]" ]"
> >
<div v-if="title" :class="[`${prefixCls}__header`, 'flex items-center']"> <div v-if="title" :class="[`${prefixCls}__header`, 'flex items-center']">
<Icon icon="ep:warning-filled" :size="22" color="var(--el-color-primary)" /> <Icon :size="22" color="var(--el-color-primary)" icon="ep:warning-filled" />
<span :class="[`${prefixCls}__title`, 'pl-5px text-16px font-bold']">{{ title }}</span> <span :class="[`${prefixCls}__title`, 'pl-5px text-16px font-bold']">{{ title }}</span>
</div> </div>
<div :class="`${prefixCls}__content`"> <div :class="`${prefixCls}__content`">
<p v-for="(item, $index) in schema" :key="$index" class="text-14px mt-15px"> <p v-for="(item, $index) in schema" :key="$index" class="text-14px mt-15px">
<Highlight <Highlight
:keys="typeof item === 'string' ? [] : item.keys"
:color="highlightColor" :color="highlightColor"
:keys="typeof item === 'string' ? [] : item.keys"
@click="keyClick" @click="keyClick"
> >
{{ showIndex ? `${$index + 1}` : '' }}{{ typeof item === 'string' ? item : item.label }} {{ showIndex ? `${$index + 1}` : '' }}{{ typeof item === 'string' ? item : item.label }}

View File

@ -1,8 +1,8 @@
<script setup lang="ts"> <script lang="ts" name="InputPassword" setup>
import { propTypes } from '@/utils/propTypes' import { propTypes } from '@/utils/propTypes'
import { useConfigGlobal } from '@/hooks/web/useConfigGlobal' import { useConfigGlobal } from '@/hooks/web/useConfigGlobal'
import { zxcvbn } from '@zxcvbn-ts/core'
import type { ZxcvbnResult } from '@zxcvbn-ts/core' import type { ZxcvbnResult } from '@zxcvbn-ts/core'
import { zxcvbn } from '@zxcvbn-ts/core'
import { useDesign } from '@/hooks/web/useDesign' import { useDesign } from '@/hooks/web/useDesign'
const { getPrefixCls } = useDesign() const { getPrefixCls } = useDesign()
@ -57,9 +57,9 @@ const getIconName = computed(() => (unref(textType) === 'password' ? 'ep:hide' :
<template> <template>
<div :class="[prefixCls, `${prefixCls}--${configGlobal?.size}`]"> <div :class="[prefixCls, `${prefixCls}--${configGlobal?.size}`]">
<ElInput v-bind="$attrs" v-model="valueRef" :type="textType"> <ElInput v-model="valueRef" :type="textType" v-bind="$attrs">
<template #suffix> <template #suffix>
<Icon class="el-input__icon cursor-pointer" :icon="getIconName" @click="changeTextType" /> <Icon :icon="getIconName" class="el-input__icon cursor-pointer" @click="changeTextType" />
</template> </template>
</ElInput> </ElInput>
<div <div

View File

@ -2,19 +2,19 @@
<template> <template>
<el-pagination <el-pagination
v-show="total > 0" v-show="total > 0"
class="float-right mt-15px mb-15px"
:background="true"
layout="total, sizes, prev, pager, next, jumper"
:page-sizes="[10, 20, 30, 50, 100]"
v-model:current-page="currentPage" v-model:current-page="currentPage"
v-model:page-size="pageSize" v-model:page-size="pageSize"
:background="true"
:page-sizes="[10, 20, 30, 50, 100]"
:pager-count="pagerCount" :pager-count="pagerCount"
:total="total" :total="total"
class="float-right mt-15px mb-15px"
layout="total, sizes, prev, pager, next, jumper"
@size-change="handleSizeChange" @size-change="handleSizeChange"
@current-change="handleCurrentChange" @current-change="handleCurrentChange"
/> />
</template> </template>
<script setup> <script name="Pagination" setup>
import { computed } from 'vue' import { computed } from 'vue'
const props = defineProps({ const props = defineProps({

View File

@ -1,7 +1,6 @@
<script setup lang="ts"> <script lang="ts" name="Qrcode" setup>
import { PropType, nextTick, ref, watch, computed, unref } from 'vue' import { computed, nextTick, PropType, ref, unref, watch } from 'vue'
import QRCode from 'qrcode' import QRCode, { QRCodeRenderersOptions } from 'qrcode'
import { QRCodeRenderersOptions } from 'qrcode'
import { cloneDeep } from 'lodash-es' import { cloneDeep } from 'lodash-es'
import { propTypes } from '@/utils/propTypes' import { propTypes } from '@/utils/propTypes'
import { useDesign } from '@/hooks/web/useDesign' import { useDesign } from '@/hooks/web/useDesign'
@ -230,7 +229,7 @@ const disabledClick = () => {
@click="disabledClick" @click="disabledClick"
> >
<div class="absolute top-[50%] left-[50%] font-bold"> <div class="absolute top-[50%] left-[50%] font-bold">
<Icon icon="ep:refresh-right" :size="30" color="var(--el-color-primary)" /> <Icon :size="30" color="var(--el-color-primary)" icon="ep:refresh-right" />
<div>{{ disabledText }}</div> <div>{{ disabledText }}</div>
</div> </div>
</div> </div>

View File

@ -0,0 +1,76 @@
<template>
<ElDialog v-model="showSearch" :show-close="false" title="菜单搜索">
<el-select
filterable
:reserve-keyword="false"
remote
placeholder="请输入菜单内容"
:remote-method="remoteMethod"
style="width: 100%"
@change="handleChange"
>
<el-option
v-for="item in options"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</ElDialog>
</template>
<script setup lang="ts">
const router = useRouter() //
const showSearch = ref(false) //
const value: Ref = ref('') //
const routers = router.getRoutes() //
const options = computed(() => {
//
if (!value.value) {
return []
}
const list = routers.filter((item: any) => {
if (item.meta.title?.indexOf(value.value) > -1 || item.path.indexOf(value.value) > -1) {
return true
}
})
return list.map((item) => {
return {
label: `${item.meta.title}${item.path}`,
value: item.path
}
})
})
function remoteMethod(data) {
//
value.value = data
}
function handleChange(path) {
router.push({ path })
}
onMounted(() => {
window.addEventListener('keydown', listenKey)
})
onUnmounted(() => {
window.removeEventListener('keydown', listenKey)
})
// ctrl + k
function listenKey(event) {
if ((event.ctrlKey || event.metaKey) && event.key === 'k') {
showSearch.value = !showSearch.value
//
}
}
defineExpose({
openSearch: () => {
showSearch.value = true
}
})
</script>

View File

@ -1,4 +1,4 @@
<script setup lang="ts"> <script lang="ts" name="Search" setup>
import { PropType } from 'vue' import { PropType } from 'vue'
import { propTypes } from '@/utils/propTypes' import { propTypes } from '@/utils/propTypes'
@ -100,25 +100,25 @@ const setVisible = () => {
<template> <template>
<!-- update by 芋艿class="-mb-15px" 用于降低和 ContentWrap 组件的底部距离避免空隙过大 --> <!-- update by 芋艿class="-mb-15px" 用于降低和 ContentWrap 组件的底部距离避免空隙过大 -->
<Form <Form
:is-custom="false"
:label-width="labelWidth"
hide-required-asterisk
:inline="inline" :inline="inline"
:is-col="isCol" :is-col="isCol"
:is-custom="false"
:label-width="labelWidth"
:schema="newSchema" :schema="newSchema"
@register="register"
class="-mb-15px" class="-mb-15px"
hide-required-asterisk
@register="register"
> >
<template #action> <template #action>
<div v-if="layout === 'inline'"> <div v-if="layout === 'inline'">
<!-- update by 芋艿去除搜索的 type="primary"颜色变淡一点 --> <!-- update by 芋艿去除搜索的 type="primary"颜色变淡一点 -->
<ElButton v-if="showSearch" @click="search"> <ElButton v-if="showSearch" @click="search">
<Icon icon="ep:search" class="mr-5px" /> <Icon class="mr-5px" icon="ep:search" />
{{ t('common.query') }} {{ t('common.query') }}
</ElButton> </ElButton>
<!-- update by 芋艿 icon="ep:refresh-right" 修改成 icon="ep:refresh" ruoyi-vue 搜索保持一致 --> <!-- update by 芋艿 icon="ep:refresh-right" 修改成 icon="ep:refresh" ruoyi-vue 搜索保持一致 -->
<ElButton v-if="showReset" @click="reset"> <ElButton v-if="showReset" @click="reset">
<Icon icon="ep:refresh" class="mr-5px" /> <Icon class="mr-5px" icon="ep:refresh" />
{{ t('common.reset') }} {{ t('common.reset') }}
</ElButton> </ElButton>
<ElButton v-if="expand" text @click="setVisible"> <ElButton v-if="expand" text @click="setVisible">
@ -129,19 +129,19 @@ const setVisible = () => {
<slot name="actionMore"></slot> <slot name="actionMore"></slot>
</div> </div>
</template> </template>
<template #[name] v-for="name in Object.keys($slots)" :key="name" <template v-for="name in Object.keys($slots)" :key="name" #[name]>
><slot :name="name"></slot <slot :name="name"></slot>
></template> </template>
</Form> </Form>
<template v-if="layout === 'bottom'"> <template v-if="layout === 'bottom'">
<div :style="bottonButtonStyle"> <div :style="bottonButtonStyle">
<ElButton v-if="showSearch" type="primary" @click="search"> <ElButton v-if="showSearch" type="primary" @click="search">
<Icon icon="ep:search" class="mr-5px" /> <Icon class="mr-5px" icon="ep:search" />
{{ t('common.query') }} {{ t('common.query') }}
</ElButton> </ElButton>
<ElButton v-if="showReset" @click="reset"> <ElButton v-if="showReset" @click="reset">
<Icon icon="ep:refresh-right" class="mr-5px" /> <Icon class="mr-5px" icon="ep:refresh-right" />
{{ t('common.reset') }} {{ t('common.reset') }}
</ElButton> </ElButton>
<ElButton v-if="expand" text @click="setVisible"> <ElButton v-if="expand" text @click="setVisible">

View File

@ -1,7 +1,8 @@
<script setup lang="ts"> <script lang="ts" name="Sticky" setup>
import { propTypes } from '@/utils/propTypes' import { propTypes } from '@/utils/propTypes'
import { isClient, useEventListener, useWindowSize } from '@vueuse/core' import { isClient, useEventListener, useWindowSize } from '@vueuse/core'
import type { CSSProperties } from 'vue' import type { CSSProperties } from 'vue'
const props = defineProps({ const props = defineProps({
// (px) // (px)
offset: propTypes.number.def(0), offset: propTypes.number.def(0),
@ -120,7 +121,7 @@ const reset = () => {
} }
</script> </script>
<template> <template>
<div :style="{ height: height, zIndex: zIndex }" ref="refSticky"> <div ref="refSticky" :style="{ height: height, zIndex: zIndex }">
<div <div
:class="className" :class="className"
:style="{ :style="{

View File

@ -1,5 +1,6 @@
<script setup lang="ts"> <script lang="ts" name="Tooltip" setup>
import { propTypes } from '@/utils/propTypes' import { propTypes } from '@/utils/propTypes'
defineProps({ defineProps({
titel: propTypes.string.def(''), titel: propTypes.string.def(''),
message: propTypes.string.def(''), message: propTypes.string.def(''),

View File

@ -1,19 +1,19 @@
<template> <template>
<div class="upload-box"> <div class="upload-box">
<el-upload <el-upload
:action="updateUrl"
list-type="picture-card"
:class="['upload', drag ? 'no-border' : '']"
v-model:file-list="fileList" v-model:file-list="fileList"
:multiple="true" :accept="fileType.join(',')"
:limit="limit" :action="updateUrl"
:headers="uploadHeaders"
:before-upload="beforeUpload" :before-upload="beforeUpload"
:class="['upload', drag ? 'no-border' : '']"
:drag="drag"
:headers="uploadHeaders"
:limit="limit"
:multiple="true"
:on-error="uploadError"
:on-exceed="handleExceed" :on-exceed="handleExceed"
:on-success="uploadSuccess" :on-success="uploadSuccess"
:on-error="uploadError" list-type="picture-card"
:drag="drag"
:accept="fileType.join(',')"
> >
<div class="upload-empty"> <div class="upload-empty">
<slot name="empty"> <slot name="empty">
@ -40,15 +40,15 @@
</div> </div>
<el-image-viewer <el-image-viewer
v-if="imgViewVisible" v-if="imgViewVisible"
@close="imgViewVisible = false"
:url-list="[viewImageUrl]" :url-list="[viewImageUrl]"
@close="imgViewVisible = false"
/> />
</div> </div>
</template> </template>
<script setup lang="ts" name="UploadImgs"> <script lang="ts" name="UploadImgs" setup>
import { PropType } from 'vue' import { PropType } from 'vue'
import type { UploadFile, UploadProps, UploadUserFile } from 'element-plus'
import { ElNotification } from 'element-plus' import { ElNotification } from 'element-plus'
import type { UploadProps, UploadFile, UploadUserFile } from 'element-plus'
import { propTypes } from '@/utils/propTypes' import { propTypes } from '@/utils/propTypes'
import { getAccessToken, getTenantId } from '@/utils/auth' import { getAccessToken, getTenantId } from '@/utils/auth'
@ -88,8 +88,19 @@ const uploadHeaders = ref({
'tenant-id': getTenantId() 'tenant-id': getTenantId()
}) })
const fileList = ref<UploadUserFile[]>(props.modelValue) const fileList = ref<UploadUserFile[]>()
// fix:
watch(
() => props.modelValue,
(data) => {
if (!data) return
fileList.value = data
},
{
deep: true,
immediate: true
}
)
/** /**
* @description 文件上传之前判断 * @description 文件上传之前判断
* @param rawFile 上传的文件 * @param rawFile 上传的文件
@ -116,9 +127,11 @@ const beforeUpload: UploadProps['beforeUpload'] = (rawFile) => {
interface UploadEmits { interface UploadEmits {
(e: 'update:modelValue', value: UploadUserFile[]): void (e: 'update:modelValue', value: UploadUserFile[]): void
} }
const emit = defineEmits<UploadEmits>() const emit = defineEmits<UploadEmits>()
const uploadSuccess = (response, uploadFile: UploadFile) => { const uploadSuccess = (response, uploadFile: UploadFile) => {
if (!response) return if (!response) return
// TODO urlfileList
uploadFile.url = response.data uploadFile.url = response.data
emit('update:modelValue', fileList.value) emit('update:modelValue', fileList.value)
message.success('上传成功') message.success('上传成功')
@ -159,35 +172,40 @@ const handlePictureCardPreview: UploadProps['onPreview'] = (uploadFile) => {
} }
</script> </script>
<style scoped lang="scss"> <style lang="scss" scoped>
.is-error { .is-error {
.upload { .upload {
:deep(.el-upload--picture-card), :deep(.el-upload--picture-card),
:deep(.el-upload-dragger) { :deep(.el-upload-dragger) {
border: 1px dashed var(--el-color-danger) !important; border: 1px dashed var(--el-color-danger) !important;
&:hover { &:hover {
border-color: var(--el-color-primary) !important; border-color: var(--el-color-primary) !important;
} }
} }
} }
} }
:deep(.disabled) { :deep(.disabled) {
.el-upload--picture-card, .el-upload--picture-card,
.el-upload-dragger { .el-upload-dragger {
cursor: not-allowed; cursor: not-allowed;
background: var(--el-disabled-bg-color) !important; background: var(--el-disabled-bg-color) !important;
border: 1px dashed var(--el-border-color-darker); border: 1px dashed var(--el-border-color-darker);
&:hover { &:hover {
border-color: var(--el-border-color-darker) !important; border-color: var(--el-border-color-darker) !important;
} }
} }
} }
.upload-box { .upload-box {
.no-border { .no-border {
:deep(.el-upload--picture-card) { :deep(.el-upload--picture-card) {
border: none !important; border: none !important;
} }
} }
:deep(.upload) { :deep(.upload) {
.el-upload-dragger { .el-upload-dragger {
display: flex; display: flex;
@ -199,14 +217,17 @@ const handlePictureCardPreview: UploadProps['onPreview'] = (uploadFile) => {
overflow: hidden; overflow: hidden;
border: 1px dashed var(--el-border-color-darker); border: 1px dashed var(--el-border-color-darker);
border-radius: v-bind(borderRadius); border-radius: v-bind(borderRadius);
&:hover { &:hover {
border: 1px dashed var(--el-color-primary); border: 1px dashed var(--el-color-primary);
} }
} }
.el-upload-dragger.is-dragover { .el-upload-dragger.is-dragover {
background-color: var(--el-color-primary-light-9); background-color: var(--el-color-primary-light-9);
border: 2px dashed var(--el-color-primary) !important; border: 2px dashed var(--el-color-primary) !important;
} }
.el-upload-list__item, .el-upload-list__item,
.el-upload--picture-card { .el-upload--picture-card {
width: v-bind(width); width: v-bind(width);
@ -214,11 +235,13 @@ const handlePictureCardPreview: UploadProps['onPreview'] = (uploadFile) => {
background-color: transparent; background-color: transparent;
border-radius: v-bind(borderRadius); border-radius: v-bind(borderRadius);
} }
.upload-image { .upload-image {
width: 100%; width: 100%;
height: 100%; height: 100%;
object-fit: contain; object-fit: contain;
} }
.upload-handle { .upload-handle {
position: absolute; position: absolute;
top: 0; top: 0;
@ -233,6 +256,7 @@ const handlePictureCardPreview: UploadProps['onPreview'] = (uploadFile) => {
background: rgb(0 0 0 / 60%); background: rgb(0 0 0 / 60%);
opacity: 0; opacity: 0;
transition: var(--el-transition-duration-fast); transition: var(--el-transition-duration-fast);
.handle-icon { .handle-icon {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@ -240,15 +264,18 @@ const handlePictureCardPreview: UploadProps['onPreview'] = (uploadFile) => {
justify-content: center; justify-content: center;
padding: 0 6%; padding: 0 6%;
color: aliceblue; color: aliceblue;
.el-icon { .el-icon {
margin-bottom: 15%; margin-bottom: 15%;
font-size: 140%; font-size: 140%;
} }
span { span {
font-size: 100%; font-size: 100%;
} }
} }
} }
.el-upload-list__item { .el-upload-list__item {
&:hover { &:hover {
.upload-handle { .upload-handle {
@ -256,6 +283,7 @@ const handlePictureCardPreview: UploadProps['onPreview'] = (uploadFile) => {
} }
} }
} }
.upload-empty { .upload-empty {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@ -263,12 +291,14 @@ const handlePictureCardPreview: UploadProps['onPreview'] = (uploadFile) => {
font-size: 12px; font-size: 12px;
line-height: 30px; line-height: 30px;
color: var(--el-color-info); color: var(--el-color-info);
.el-icon { .el-icon {
font-size: 28px; font-size: 28px;
color: var(--el-text-color-secondary); color: var(--el-text-color-secondary);
} }
} }
} }
.el-upload__tip { .el-upload__tip {
line-height: 15px; line-height: 15px;
text-align: center; text-align: center;

View File

@ -1,42 +1,42 @@
<template> <template>
<div :class="mode == 'pop' ? 'mask' : ''" v-show="showBox"> <div v-show="showBox" :class="mode == 'pop' ? 'mask' : ''">
<div <div
:class="mode == 'pop' ? 'verifybox' : ''" :class="mode == 'pop' ? 'verifybox' : ''"
:style="{ 'max-width': parseInt(imgSize.width) + 20 + 'px' }" :style="{ 'max-width': parseInt(imgSize.width) + 20 + 'px' }"
> >
<div class="verifybox-top" v-if="mode == 'pop'"> <div v-if="mode == 'pop'" class="verifybox-top">
{{ t('captcha.verification') }} {{ t('captcha.verification') }}
<span class="verifybox-close" @click="closeBox"> <span class="verifybox-close" @click="closeBox">
<i class="iconfont icon-close"></i> <i class="iconfont icon-close"></i>
</span> </span>
</div> </div>
<div class="verifybox-bottom" :style="{ padding: mode == 'pop' ? '10px' : '0' }"> <div :style="{ padding: mode == 'pop' ? '10px' : '0' }" class="verifybox-bottom">
<!-- 验证码容器 --> <!-- 验证码容器 -->
<component <component
v-if="componentType"
:is="componentType" :is="componentType"
:captchaType="captchaType" v-if="componentType"
:type="verifyType"
:figure="figure"
:arith="arith"
:mode="mode"
:vSpace="vSpace"
:explain="explain"
:imgSize="imgSize"
:blockSize="blockSize"
:barSize="barSize"
ref="instance" ref="instance"
:arith="arith"
:barSize="barSize"
:blockSize="blockSize"
:captchaType="captchaType"
:explain="explain"
:figure="figure"
:imgSize="imgSize"
:mode="mode"
:type="verifyType"
:vSpace="vSpace"
/> />
</div> </div>
</div> </div>
</div> </div>
</template> </template>
<script type="text/babel"> <script name="Verify" type="text/babel">
/** /**
* Verify 验证码组件 * Verify 验证码组件
* @description 分发验证码使用 * @description 分发验证码使用
* */ * */
import { VerifySlide, VerifyPoints } from './Verify' import { VerifyPoints, VerifySlide } from './Verify'
import { computed, ref, toRefs, watchEffect } from 'vue' import { computed, ref, toRefs, watchEffect } from 'vue'
export default { export default {
@ -155,6 +155,7 @@ export default {
border-radius: 5px; border-radius: 5px;
transform: translate(-50%, -50%); transform: translate(-50%, -50%);
} }
.verifybox-top { .verifybox-top {
padding: 0 15px; padding: 0 15px;
height: 40px; height: 40px;
@ -165,10 +166,12 @@ export default {
border-bottom: 1px solid #e4e7eb; border-bottom: 1px solid #e4e7eb;
box-sizing: border-box; box-sizing: border-box;
} }
.verifybox-bottom { .verifybox-bottom {
padding: 10px; padding: 10px;
box-sizing: border-box; box-sizing: border-box;
} }
.verifybox-close { .verifybox-close {
position: absolute; position: absolute;
top: 13px; top: 13px;
@ -178,6 +181,7 @@ export default {
text-align: center; text-align: center;
cursor: pointer; cursor: pointer;
} }
.mask { .mask {
position: fixed; position: fixed;
top: 0; top: 0;
@ -189,6 +193,7 @@ export default {
/* display: none; */ /* display: none; */
transition: all 0.5s; transition: all 0.5s;
} }
.verify-tips { .verify-tips {
text-indent: 10px; text-indent: 10px;
position: absolute; position: absolute;
@ -199,22 +204,27 @@ export default {
line-height: 30px; line-height: 30px;
color: #fff; color: #fff;
} }
.suc-bg { .suc-bg {
background-color: rgba(92, 184, 92, 0.5); background-color: rgba(92, 184, 92, 0.5);
filter: progid:DXImageTransform.Microsoft.gradient(startcolorstr=#7f5CB85C, endcolorstr=#7f5CB85C); filter: progid:DXImageTransform.Microsoft.gradient(startcolorstr=#7f5CB85C, endcolorstr=#7f5CB85C);
} }
.err-bg { .err-bg {
background-color: rgba(217, 83, 79, 0.5); background-color: rgba(217, 83, 79, 0.5);
filter: progid:DXImageTransform.Microsoft.gradient(startcolorstr=#7fD9534F, endcolorstr=#7fD9534F); filter: progid:DXImageTransform.Microsoft.gradient(startcolorstr=#7fD9534F, endcolorstr=#7fD9534F);
} }
.tips-enter, .tips-enter,
.tips-leave-to { .tips-leave-to {
bottom: -30px; bottom: -30px;
} }
.tips-enter-active, .tips-enter-active,
.tips-leave-active { .tips-leave-active {
transition: bottom 0.5s; transition: bottom 0.5s;
} }
/* ---------------------------- */ /* ---------------------------- */
/*常规验证码*/ /*常规验证码*/
.verify-code { .verify-code {

View File

@ -2,20 +2,20 @@
<div style="position: relative"> <div style="position: relative">
<div class="verify-img-out"> <div class="verify-img-out">
<div <div
class="verify-img-panel"
:style="{ :style="{
width: setSize.imgWidth, width: setSize.imgWidth,
height: setSize.imgHeight, height: setSize.imgHeight,
'background-size': setSize.imgWidth + ' ' + setSize.imgHeight, 'background-size': setSize.imgWidth + ' ' + setSize.imgHeight,
'margin-bottom': vSpace + 'px' 'margin-bottom': vSpace + 'px'
}" }"
class="verify-img-panel"
> >
<div class="verify-refresh" style="z-index: 3" @click="refresh" v-show="showRefresh"> <div v-show="showRefresh" class="verify-refresh" style="z-index: 3" @click="refresh">
<i class="iconfont icon-refresh"></i> <i class="iconfont icon-refresh"></i>
</div> </div>
<img <img
:src="'data:image/png;base64,' + pointBackImgBase"
ref="canvas" ref="canvas"
:src="'data:image/png;base64,' + pointBackImgBase"
alt="" alt=""
style="width: 100%; height: 100%; display: block" style="width: 100%; height: 100%; display: block"
@click="bindingClick ? canvasClick($event) : undefined" @click="bindingClick ? canvasClick($event) : undefined"
@ -24,7 +24,6 @@
<div <div
v-for="(tempPoint, index) in tempPoints" v-for="(tempPoint, index) in tempPoints"
:key="index" :key="index"
class="point-area"
:style="{ :style="{
'background-color': '#1abd6c', 'background-color': '#1abd6c',
color: '#fff', color: '#fff',
@ -38,6 +37,7 @@
top: parseInt(tempPoint.y - 10) + 'px', top: parseInt(tempPoint.y - 10) + 'px',
left: parseInt(tempPoint.x - 10) + 'px' left: parseInt(tempPoint.x - 10) + 'px'
}" }"
class="point-area"
> >
{{ index + 1 }} {{ index + 1 }}
</div> </div>
@ -45,19 +45,19 @@
</div> </div>
<!-- 'height': this.barSize.height, --> <!-- 'height': this.barSize.height, -->
<div <div
class="verify-bar-area"
:style="{ :style="{
width: setSize.imgWidth, width: setSize.imgWidth,
color: barAreaColor, color: barAreaColor,
'border-color': barAreaBorderColor, 'border-color': barAreaBorderColor,
'line-height': barSize.height 'line-height': barSize.height
}" }"
class="verify-bar-area"
> >
<span class="verify-msg">{{ text }}</span> <span class="verify-msg">{{ text }}</span>
</div> </div>
</div> </div>
</template> </template>
<script type="text/babel" setup> <script name="VerifyPoints" setup type="text/babel">
/** /**
* VerifyPoints * VerifyPoints
* @description 点选 * @description 点选
@ -65,7 +65,7 @@
import { resetSize } from './../utils/util' import { resetSize } from './../utils/util'
import { aesEncrypt } from './../utils/ase' import { aesEncrypt } from './../utils/ase'
import { getCodeApi, reqCheckApi } from '@/api/login' import { getCodeApi, reqCheckApi } from '@/api/login'
import { onMounted, reactive, ref, nextTick, toRefs, getCurrentInstance } from 'vue' import { getCurrentInstance, nextTick, onMounted, reactive, ref, toRefs } from 'vue'
const props = defineProps({ const props = defineProps({
//popfixed //popfixed

View File

@ -2,20 +2,20 @@
<div style="position: relative"> <div style="position: relative">
<div <div
v-if="type === '2'" v-if="type === '2'"
class="verify-img-out"
:style="{ height: parseInt(setSize.imgHeight) + vSpace + 'px' }" :style="{ height: parseInt(setSize.imgHeight) + vSpace + 'px' }"
class="verify-img-out"
> >
<div class="verify-img-panel" :style="{ width: setSize.imgWidth, height: setSize.imgHeight }"> <div :style="{ width: setSize.imgWidth, height: setSize.imgHeight }" class="verify-img-panel">
<img <img
:src="'data:image/png;base64,' + backImgBase" :src="'data:image/png;base64,' + backImgBase"
alt="" alt=""
style="width: 100%; height: 100%; display: block" style="width: 100%; height: 100%; display: block"
/> />
<div class="verify-refresh" @click="refresh" v-show="showRefresh"> <div v-show="showRefresh" class="verify-refresh" @click="refresh">
<i class="iconfont icon-refresh"></i> <i class="iconfont icon-refresh"></i>
</div> </div>
<transition name="tips"> <transition name="tips">
<span class="verify-tips" v-if="tipWords" :class="passFlag ? 'suc-bg' : 'err-bg'"> <span v-if="tipWords" :class="passFlag ? 'suc-bg' : 'err-bg'" class="verify-tips">
{{ tipWords }} {{ tipWords }}
</span> </span>
</transition> </transition>
@ -23,24 +23,21 @@
</div> </div>
<!-- 公共部分 --> <!-- 公共部分 -->
<div <div
class="verify-bar-area"
:style="{ width: setSize.imgWidth, height: barSize.height, 'line-height': barSize.height }" :style="{ width: setSize.imgWidth, height: barSize.height, 'line-height': barSize.height }"
class="verify-bar-area"
> >
<span class="verify-msg" v-text="text"></span> <span class="verify-msg" v-text="text"></span>
<div <div
class="verify-left-bar"
:style="{ :style="{
width: leftBarWidth !== undefined ? leftBarWidth : barSize.height, width: leftBarWidth !== undefined ? leftBarWidth : barSize.height,
height: barSize.height, height: barSize.height,
'border-color': leftBarBorderColor, 'border-color': leftBarBorderColor,
transaction: transitionWidth transaction: transitionWidth
}" }"
class="verify-left-bar"
> >
<span class="verify-msg" v-text="finishText"></span> <span class="verify-msg" v-text="finishText"></span>
<div <div
class="verify-move-block"
@touchstart="start"
@mousedown="start"
:style="{ :style="{
width: barSize.height, width: barSize.height,
height: barSize.height, height: barSize.height,
@ -48,17 +45,20 @@
left: moveBlockLeft, left: moveBlockLeft,
transition: transitionLeft transition: transitionLeft
}" }"
class="verify-move-block"
@mousedown="start"
@touchstart="start"
> >
<i :class="['verify-icon iconfont', iconClass]" :style="{ color: iconColor }"></i> <i :class="['verify-icon iconfont', iconClass]" :style="{ color: iconColor }"></i>
<div <div
v-if="type === '2'" v-if="type === '2'"
class="verify-sub-block"
:style="{ :style="{
width: Math.floor((parseInt(setSize.imgWidth) * 47) / 310) + 'px', width: Math.floor((parseInt(setSize.imgWidth) * 47) / 310) + 'px',
height: setSize.imgHeight, height: setSize.imgHeight,
top: '-' + (parseInt(setSize.imgHeight) + vSpace) + 'px', top: '-' + (parseInt(setSize.imgHeight) + vSpace) + 'px',
'background-size': setSize.imgWidth + ' ' + setSize.imgHeight 'background-size': setSize.imgWidth + ' ' + setSize.imgHeight
}" }"
class="verify-sub-block"
> >
<img <img
:src="'data:image/png;base64,' + blockBackImgBase" :src="'data:image/png;base64,' + blockBackImgBase"
@ -71,7 +71,7 @@
</div> </div>
</div> </div>
</template> </template>
<script type="text/babel" setup> <script name="VerifySlide" setup type="text/babel">
/** /**
* VerifySlide * VerifySlide
* @description 滑块 * @description 滑块

View File

@ -1,4 +1,4 @@
<script setup lang="ts"> <script lang="ts" name="XButton" setup>
import { PropType } from 'vue' import { PropType } from 'vue'
import { propTypes } from '@/utils/propTypes' import { propTypes } from '@/utils/propTypes'
@ -30,9 +30,9 @@ const getBindValue = computed(() => {
<template> <template>
<el-button v-bind="getBindValue" @click="onClick"> <el-button v-bind="getBindValue" @click="onClick">
<Icon :icon="preIcon" v-if="preIcon" class="mr-1px" /> <Icon v-if="preIcon" :icon="preIcon" class="mr-1px" />
{{ title ? title : '' }} {{ title ? title : '' }}
<Icon :icon="postIcon" v-if="postIcon" class="mr-1px" /> <Icon v-if="postIcon" :icon="postIcon" class="mr-1px" />
</el-button> </el-button>
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>
@ -40,6 +40,7 @@ const getBindValue = computed(() => {
margin-left: 0; margin-left: 0;
padding: 8px 4px; padding: 8px 4px;
} }
:deep(.el-button.is-link) { :deep(.el-button.is-link) {
margin-left: 0; margin-left: 0;
padding: 8px 4px; padding: 8px 4px;

View File

@ -1,4 +1,4 @@
<script setup lang="ts"> <script lang="ts" name="XTextButton" setup>
import { propTypes } from '@/utils/propTypes' import { propTypes } from '@/utils/propTypes'
import { PropType } from 'vue' import { PropType } from 'vue'
@ -29,9 +29,9 @@ const getBindValue = computed(() => {
<template> <template>
<el-button link v-bind="getBindValue" @click="onClick"> <el-button link v-bind="getBindValue" @click="onClick">
<Icon :icon="preIcon" v-if="preIcon" class="mr-1px" /> <Icon v-if="preIcon" :icon="preIcon" class="mr-1px" />
{{ title ? title : '' }} {{ title ? title : '' }}
<Icon :icon="postIcon" v-if="postIcon" class="mr-1px" /> <Icon v-if="postIcon" :icon="postIcon" class="mr-1px" />
</el-button> </el-button>
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>
@ -39,6 +39,7 @@ const getBindValue = computed(() => {
margin-left: 0; margin-left: 0;
padding: 8px 4px; padding: 8px 4px;
} }
:deep(.el-button.is-link) { :deep(.el-button.is-link) {
margin-left: 0; margin-left: 0;
padding: 8px 4px; padding: 8px 4px;

View File

@ -188,18 +188,18 @@
<!-- <div id="js-properties-panel" class="panel"></div> --> <!-- <div id="js-properties-panel" class="panel"></div> -->
<!-- <div class="my-process-designer__canvas" ref="bpmn-canvas"></div> --> <!-- <div class="my-process-designer__canvas" ref="bpmn-canvas"></div> -->
</div> </div>
<XModal title="预览" width="80%" height="90%" v-model="previewModelVisible" destroy-on-close> <Dialog
title="预览"
v-model="previewModelVisible"
width="80%"
:scroll="true"
max-height="600px"
>
<!-- append-to-body --> <!-- append-to-body -->
<div v-highlight> <div>
<code class="hljs"> <pre><code class="hljs" v-html="highlightedCode(previewResult)"></code></pre>
<!-- 高亮代码块 -->
{{ previewResult }}
</code>
</div> </div>
<!-- <pre> </Dialog>
<code class="hljs" v-html="highlightedCode(previewType, previewResult)"></code>
</pre> -->
</XModal>
</div> </div>
</template> </template>
@ -231,16 +231,8 @@ import activitiModdleExtension from './plugins/extension-moddle/activiti'
import flowableModdleExtension from './plugins/extension-moddle/flowable' import flowableModdleExtension from './plugins/extension-moddle/flowable'
// json // json
// import xml2js from 'xml-js' // import xml2js from 'xml-js'
import xml2js from 'fast-xml-parser' // import xml2js from 'fast-xml-parser'
import { XmlNode, XmlNodeType, parseXmlString } from 'steady-xml' import { XmlNode, XmlNodeType, parseXmlString } from 'steady-xml'
//
// import hljs from 'highlight.js/lib/highlight'
// import 'highlight.js/styles/github-gist.css'
// hljs.registerLanguage('xml', 'highlight.js/lib/languages/xml')
// hljs.registerLanguage('json', 'highlight.js/lib/languages/json')
// const eventName = reactive({
// name: ''
// })
const bpmnCanvas = ref() const bpmnCanvas = ref()
const refFile = ref() const refFile = ref()
const emit = defineEmits([ const emit = defineEmits([
@ -365,9 +357,9 @@ const additionalModules = computed(() => {
return Modules return Modules
}) })
const moddleExtensions = computed(() => { const moddleExtensions = computed(() => {
console.log(props.onlyCustomizeModdle, 'props.onlyCustomizeModdle') // console.log(props.onlyCustomizeModdle, 'props.onlyCustomizeModdle')
console.log(props.moddleExtension, 'props.moddleExtension') // console.log(props.moddleExtension, 'props.moddleExtension')
console.log(props.prefix, 'props.prefix') // console.log(props.prefix, 'props.prefix')
const Extensions: any = {} const Extensions: any = {}
// 使 // 使
if (props.onlyCustomizeModdle) { if (props.onlyCustomizeModdle) {
@ -430,22 +422,22 @@ const initBpmnModeler = () => {
// bpmnModeler.createDiagram() // bpmnModeler.createDiagram()
console.log(bpmnModeler, 'bpmnModeler111111') // console.log(bpmnModeler, 'bpmnModeler111111')
emit('init-finished', bpmnModeler) emit('init-finished', bpmnModeler)
initModelListeners() initModelListeners()
} }
const initModelListeners = () => { const initModelListeners = () => {
const EventBus = bpmnModeler.get('eventBus') const EventBus = bpmnModeler.get('eventBus')
console.log(EventBus, 'EventBus') // console.log(EventBus, 'EventBus')
// , . - , // , . - ,
props.events.forEach((event: any) => { props.events.forEach((event: any) => {
EventBus.on(event, function (eventObj) { EventBus.on(event, function (eventObj) {
let eventName = event.replace(/\./g, '-') // let eventName = event.replace(/\./g, '-')
// eventName.name = eventName // eventName.name = eventName
let element = eventObj ? eventObj.element : null let element = eventObj ? eventObj.element : null
console.log(eventName, 'eventName') // console.log(eventName, 'eventName')
console.log(element, 'element') // console.log(element, 'element')
emit('element-click', element, eventObj) emit('element-click', element, eventObj)
// emit(eventName, element, eventObj) // emit(eventName, element, eventObj)
}) })
@ -472,7 +464,7 @@ const initModelListeners = () => {
} }
/* 创建新的流程图 */ /* 创建新的流程图 */
const createNewDiagram = async (xml) => { const createNewDiagram = async (xml) => {
console.log(xml, 'xml') // console.log(xml, 'xml')
// //
let newId = props.processId || `Process_${new Date().getTime()}` let newId = props.processId || `Process_${new Date().getTime()}`
let newName = props.processName || `业务流程_${new Date().getTime()}` let newName = props.processName || `业务流程_${new Date().getTime()}`
@ -481,7 +473,7 @@ const createNewDiagram = async (xml) => {
// console.log(xmlString, 'xmlString') // console.log(xmlString, 'xmlString')
// console.log(this.bpmnModeler.importXML); // console.log(this.bpmnModeler.importXML);
let { warnings } = await bpmnModeler.importXML(xmlString) let { warnings } = await bpmnModeler.importXML(xmlString)
console.log(warnings, 'warnings') // console.log(warnings, 'warnings')
if (warnings && warnings.length) { if (warnings && warnings.length) {
warnings.forEach((warn) => console.warn(warn)) warnings.forEach((warn) => console.warn(warn))
} }
@ -561,7 +553,7 @@ const downloadProcessAsSvg = () => {
} }
const processSimulation = () => { const processSimulation = () => {
simulationStatus.value = !simulationStatus.value simulationStatus.value = !simulationStatus.value
console.log(bpmnModeler.get('toggleMode', 'strict'), "bpmnModeler.get('toggleMode')") // console.log(bpmnModeler.get('toggleMode', 'strict'), "bpmnModeler.get('toggleMode')")
props.simulation && bpmnModeler.get('toggleMode', 'strict').toggleMode() props.simulation && bpmnModeler.get('toggleMode', 'strict').toggleMode()
} }
const processRedo = () => { const processRedo = () => {
@ -624,9 +616,9 @@ const elementsAlign = (align) => {
} }
/*----------------------------- 方法结束 ---------------------------------*/ /*----------------------------- 方法结束 ---------------------------------*/
const previewProcessXML = () => { const previewProcessXML = () => {
console.log(bpmnModeler.saveXML, 'bpmnModeler') // console.log(bpmnModeler.saveXML, 'bpmnModeler')
bpmnModeler.saveXML({ format: true }).then(({ xml }) => { bpmnModeler.saveXML({ format: true }).then(({ xml }) => {
console.log(xml, 'xml111111') // console.log(xml, 'xml111111')
previewResult.value = xml previewResult.value = xml
previewType.value = 'xml' previewType.value = 'xml'
previewModelVisible.value = true previewModelVisible.value = true
@ -634,7 +626,7 @@ const previewProcessXML = () => {
} }
const previewProcessJson = () => { const previewProcessJson = () => {
bpmnModeler.saveXML({ format: true }).then(({ xml }) => { bpmnModeler.saveXML({ format: true }).then(({ xml }) => {
console.log(xml, 'xml') // console.log(xml, 'xml')
// const rootNode = parseXmlString(xml) // const rootNode = parseXmlString(xml)
// console.log(rootNode, 'rootNoderootNode') // console.log(rootNode, 'rootNoderootNode')
@ -644,9 +636,9 @@ const previewProcessJson = () => {
// console.log(JSON.stringify(rootNodes.parent.toJsObject()), 'rootNodes.toJSON()') // console.log(JSON.stringify(rootNodes.parent.toJsObject()), 'rootNodes.toJSON()')
// console.log(JSON.stringify(rootNodes.parent.toJSON()), 'rootNodes.toJSON()') // console.log(JSON.stringify(rootNodes.parent.toJSON()), 'rootNodes.toJSON()')
const parser = new xml2js.XMLParser() // const parser = new xml2js.XMLParser()
let jObj = parser.parse(xml) // let jObj = parser.parse(xml)
console.log(jObj, 'jObjjObjjObjjObjjObj') // console.log(jObj, 'jObjjObjjObjjObjjObj')
// const builder = new xml2js.XMLBuilder(xml) // const builder = new xml2js.XMLBuilder(xml)
// const xmlContent = builder // const xmlContent = builder
// console.log(xmlContent, 'xmlContent') // console.log(xmlContent, 'xmlContent')
@ -660,10 +652,10 @@ const previewProcessJson = () => {
} }
/* ------------------------------------------------ 芋道源码 methods ------------------------------------------------------ */ /* ------------------------------------------------ 芋道源码 methods ------------------------------------------------------ */
const processSave = async () => { const processSave = async () => {
console.log(bpmnModeler, 'bpmnModelerbpmnModelerbpmnModelerbpmnModeler') // console.log(bpmnModeler, 'bpmnModelerbpmnModelerbpmnModelerbpmnModeler')
const { err, xml } = await bpmnModeler.saveXML() const { err, xml } = await bpmnModeler.saveXML()
console.log(err, 'errerrerrerrerr') // console.log(err, 'errerrerrerrerr')
console.log(xml, 'xmlxmlxmlxmlxml') // console.log(xml, 'xmlxmlxmlxmlxml')
// //
if (err) { if (err) {
// this.$modal.msgError('') // this.$modal.msgError('')
@ -673,20 +665,38 @@ const processSave = async () => {
// save // save
emit('save', xml) emit('save', xml)
} }
/** 高亮显示 */
// const highlightedCode = (previewType, previewResult) => {
// console.log(previewType, 'previewType, previewResult')
// console.log(previewResult, 'previewType, previewResult')
// console.log(hljs.highlight, 'hljs.highlight')
// const result = hljs.highlight(previewType, previewResult.value || '', true)
// return result.value || '&nbsp;'
// }
onBeforeMount(() => { onBeforeMount(() => {
console.log(props, 'propspropspropsprops') // console.log(props, 'propspropspropsprops')
}) })
onMounted(() => {
/**
* 代码高亮
*/
import hljs from 'highlight.js' //
import 'highlight.js/styles/github.css' //
import java from 'highlight.js/lib/languages/java'
import xml from 'highlight.js/lib/languages/java'
import javascript from 'highlight.js/lib/languages/javascript'
import sql from 'highlight.js/lib/languages/sql'
import typescript from 'highlight.js/lib/languages/typescript'
const highlightedCode = (item) => {
const language = item.filePath.substring(item.filePath.lastIndexOf('.') + 1)
const result = hljs.highlight(language, item.code || '', true)
return result.value || '&nbsp;'
}
/** 初始化 **/
onMounted(async () => {
initBpmnModeler() initBpmnModeler()
createNewDiagram(props.value) createNewDiagram(props.value)
//
hljs.registerLanguage('java', java)
hljs.registerLanguage('xml', xml)
hljs.registerLanguage('html', xml)
hljs.registerLanguage('vue', xml)
hljs.registerLanguage('javascript', javascript)
hljs.registerLanguage('sql', sql)
hljs.registerLanguage('typescript', typescript)
}) })
onBeforeUnmount(() => { onBeforeUnmount(() => {
// this.$once('hook:beforeDestroy', () => { // this.$once('hook:beforeDestroy', () => {

View File

@ -280,7 +280,7 @@ const elementHover = (element) => {
if (element.value.type === 'bpmn:StartEvent' && processInstance.value) { if (element.value.type === 'bpmn:StartEvent' && processInstance.value) {
html = `<p>发起人:${processInstance.value.startUser.nickname}</p> html = `<p>发起人:${processInstance.value.startUser.nickname}</p>
<p>部门${processInstance.value.startUser.deptName}</p> <p>部门${processInstance.value.startUser.deptName}</p>
<p>创建时间${parseTime(processInstance.value.createTime)}` <p>创建时间${formatDate(processInstance.value.createTime)}`
} else if (element.value.type === 'bpmn:UserTask') { } else if (element.value.type === 'bpmn:UserTask') {
// debugger // debugger
let task = taskList.value.find((m) => m.id === activity.taskId) // taskId let task = taskList.value.find((m) => m.id === activity.taskId) // taskId
@ -297,26 +297,26 @@ const elementHover = (element) => {
html = `<p>审批人:${task.assigneeUser.nickname}</p> html = `<p>审批人:${task.assigneeUser.nickname}</p>
<p>部门${task.assigneeUser.deptName}</p> <p>部门${task.assigneeUser.deptName}</p>
<p>结果${dataResult}</p> <p>结果${dataResult}</p>
<p>创建时间${parseTime(task.createTime)}</p>` <p>创建时间${formatDate(task.createTime)}</p>`
// html = `<p>${task.assigneeUser.nickname}</p> // html = `<p>${task.assigneeUser.nickname}</p>
// <p>${task.assigneeUser.deptName}</p> // <p>${task.assigneeUser.deptName}</p>
// <p>${getIntDictOptions( // <p>${getIntDictOptions(
// DICT_TYPE.BPM_PROCESS_INSTANCE_RESULT, // DICT_TYPE.BPM_PROCESS_INSTANCE_RESULT,
// task.result // task.result
// )}</p> // )}</p>
// <p>${parseTime(task.createTime)}</p>` // <p>${formatDate(task.createTime)}</p>`
if (task.endTime) { if (task.endTime) {
html += `<p>结束时间:${parseTime(task.endTime)}</p>` html += `<p>结束时间:${formatDate(task.endTime)}</p>`
} }
if (task.reason) { if (task.reason) {
html += `<p>审批建议:${task.reason}</p>` html += `<p>审批建议:${task.reason}</p>`
} }
} else if (element.value.type === 'bpmn:ServiceTask' && processInstance.value) { } else if (element.value.type === 'bpmn:ServiceTask' && processInstance.value) {
if (activity.startTime > 0) { if (activity.startTime > 0) {
html = `<p>创建时间:${parseTime(activity.startTime)}</p>` html = `<p>创建时间:${formatDate(activity.startTime)}</p>`
} }
if (activity.endTime > 0) { if (activity.endTime > 0) {
html += `<p>结束时间:${parseTime(activity.endTime)}</p>` html += `<p>结束时间:${formatDate(activity.endTime)}</p>`
} }
console.log(html) console.log(html)
} else if (element.value.type === 'bpmn:EndEvent' && processInstance.value) { } else if (element.value.type === 'bpmn:EndEvent' && processInstance.value) {
@ -333,7 +333,7 @@ const elementHover = (element) => {
// processInstance.value.result // processInstance.value.result
// )}</p>` // )}</p>`
if (processInstance.value.endTime) { if (processInstance.value.endTime) {
html += `<p>结束时间:${parseTime(processInstance.value.endTime)}</p>` html += `<p>结束时间:${formatDate(processInstance.value.endTime)}</p>`
} }
} }
console.log(html, 'html111111111111111') console.log(html, 'html111111111111111')
@ -348,50 +348,6 @@ const elementOut = (element) => {
toRaw(overlays.value).remove({ element }) toRaw(overlays.value).remove({ element })
elementOverlayIds.value[element.id] = null elementOverlayIds.value[element.id] = null
} }
const parseTime = (time) => {
if (!time) {
return null
}
const format = '{y}-{m}-{d} {h}:{i}:{s}'
let date
if (typeof time === 'object') {
date = time
} else {
if (typeof time === 'string' && /^[0-9]+$/.test(time)) {
time = parseInt(time)
} else if (typeof time === 'string') {
time = time
.replace(new RegExp(/-/gm), '/')
.replace('T', ' ')
.replace(new RegExp(/\.[\d]{3}/gm), '')
}
if (typeof time === 'number' && time.toString().length === 10) {
time = time * 1000
}
date = new Date(time)
}
const formatObj = {
y: date.getFullYear(),
m: date.getMonth() + 1,
d: date.getDate(),
h: date.getHours(),
i: date.getMinutes(),
s: date.getSeconds(),
a: date.getDay()
}
const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => {
let value = formatObj[key]
// Note: getDay() returns 0 on Sunday
if (key === 'a') {
return ['日', '一', '二', '三', '四', '五', '六'][value]
}
if (result.length > 0 && value < 10) {
value = '0' + value
}
return value || 0
})
return time_str
}
onMounted(() => { onMounted(() => {
xml.value = props.value xml.value = props.value

View File

@ -1,37 +1,11 @@
import { App } from 'vue'
import MyProcessDesigner from './designer' import MyProcessDesigner from './designer'
import MyProcessPenal from './penal' import MyProcessPenal from './penal'
import MyProcessViewer from './designer/index2' import MyProcessViewer from './designer/index2'
const components = [MyProcessDesigner, MyProcessPenal, MyProcessViewer] import './theme/index.scss'
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'
// const install = function (Vue) { export { MyProcessDesigner, MyProcessPenal, MyProcessViewer }
// components.forEach(component => {
// Vue.component(component.name, component)
// })
// }
// if (typeof window !== "undefined" && window.Vue) {
// install(window.Vue)
// }
// components.forEach(component => {
// Vue.component(component.name, component)
// })
const componentss = {
install: (Vue: App): void => {
components.forEach((component) => {
Vue.component(component.name, component)
})
}
}
// let version = "0.0.1"
export const MyPD = (app) => {
// export default {
// app.use(version)
// app.use(install)
// app.use(MyProcessDesigner)
// app.use(MyProcessPenal)
// app.use(MyProcessViewer)
// app.use(components)
app.use(componentss)
}

View File

@ -33,9 +33,15 @@
/> />
<el-table-column label="操作" width="90px"> <el-table-column label="操作" width="90px">
<template #default="scope"> <template #default="scope">
<el-button type="text" @click="openFieldForm(scope, scope.$index)">编辑</el-button> <el-button type="primary" link @click="openFieldForm(scope, scope.$index)"
>编辑</el-button
>
<el-divider direction="vertical" /> <el-divider direction="vertical" />
<el-button type="text" style="color: #ff4d4f" @click="removeField(scope, scope.$index)" <el-button
type="primary"
link
style="color: #ff4d4f"
@click="removeField(scope, scope.$index)"
>移除</el-button >移除</el-button
> >
</template> </template>
@ -97,12 +103,16 @@
<el-table-column label="枚举值名称" prop="name" min-width="100px" show-overflow-tooltip /> <el-table-column label="枚举值名称" prop="name" min-width="100px" show-overflow-tooltip />
<el-table-column label="操作" width="90px"> <el-table-column label="操作" width="90px">
<template #default="scope"> <template #default="scope">
<el-button type="text" @click="openFieldOptionForm(scope, scope.$index, 'enum')" <el-button
type="primary"
link
@click="openFieldOptionForm(scope, scope.$index, 'enum')"
>编辑</el-button >编辑</el-button
> >
<el-divider direction="vertical" /> <el-divider direction="vertical" />
<el-button <el-button
type="text" type="primary"
link
style="color: #ff4d4f" style="color: #ff4d4f"
@click="removeFieldOptionItem(scope, scope.$index, 'enum')" @click="removeFieldOptionItem(scope, scope.$index, 'enum')"
>移除</el-button >移除</el-button
@ -126,12 +136,16 @@
<el-table-column label="约束配置" prop="config" min-width="100px" show-overflow-tooltip /> <el-table-column label="约束配置" prop="config" min-width="100px" show-overflow-tooltip />
<el-table-column label="操作" width="90px"> <el-table-column label="操作" width="90px">
<template #default="scope"> <template #default="scope">
<el-button type="text" @click="openFieldOptionForm(scope, scope.$index, 'constraint')" <el-button
type="primary"
link
@click="openFieldOptionForm(scope, scope.$index, 'constraint')"
>编辑</el-button >编辑</el-button
> >
<el-divider direction="vertical" /> <el-divider direction="vertical" />
<el-button <el-button
type="text" type="primary"
link
style="color: #ff4d4f" style="color: #ff4d4f"
@click="removeFieldOptionItem(scope, scope.$index, 'constraint')" @click="removeFieldOptionItem(scope, scope.$index, 'constraint')"
>移除</el-button >移除</el-button
@ -154,12 +168,16 @@
<el-table-column label="属性值" prop="value" min-width="100px" show-overflow-tooltip /> <el-table-column label="属性值" prop="value" min-width="100px" show-overflow-tooltip />
<el-table-column label="操作" width="90px"> <el-table-column label="操作" width="90px">
<template #default="scope"> <template #default="scope">
<el-button type="text" @click="openFieldOptionForm(scope, scope.$index, 'property')" <el-button
type="primary"
link
@click="openFieldOptionForm(scope, scope.$index, 'property')"
>编辑</el-button >编辑</el-button
> >
<el-divider direction="vertical" /> <el-divider direction="vertical" />
<el-button <el-button
type="text" type="primary"
link
style="color: #ff4d4f" style="color: #ff4d4f"
@click="removeFieldOptionItem(scope, scope.$index, 'property')" @click="removeFieldOptionItem(scope, scope.$index, 'property')"
>移除</el-button >移除</el-button

View File

@ -7,9 +7,9 @@ export const template = (isTaskListener) => {
<el-table-column label="监听器类型" min-width="100px" show-overflow-tooltip :formatter="row => listenerTypeObject[row.listenerType]" /> <el-table-column label="监听器类型" min-width="100px" show-overflow-tooltip :formatter="row => listenerTypeObject[row.listenerType]" />
<el-table-column label="操作" width="90px"> <el-table-column label="操作" width="90px">
<template #default="scope"> <template #default="scope">
<el-button size="small" type="text" @click="openListenerForm(scope, scope.$index)">编辑</el-button> <el-button size="small" type="primary" link @click="openListenerForm(scope, scope.$index)">编辑</el-button>
<el-divider direction="vertical" /> <el-divider direction="vertical" />
<el-button size="small" type="text" style="color: #ff4d4f" @click="removeListener(scope, scope.$index)">移除</el-button> <el-button size="small" type="primary" link style="color: #ff4d4f" @click="removeListener(scope, scope.$index)">移除</el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@ -125,9 +125,9 @@ export const template = (isTaskListener) => {
<el-table-column label="字段值/表达式" min-width="100px" show-overflow-tooltip :formatter="row => row.string || row.expression" /> <el-table-column label="字段值/表达式" min-width="100px" show-overflow-tooltip :formatter="row => row.string || row.expression" />
<el-table-column label="操作" width="100px"> <el-table-column label="操作" width="100px">
<template #default="scope"> <template #default="scope">
<el-button size="small" type="text" @click="openListenerFieldForm(scope, scope.$index)">编辑</el-button> <el-button size="small" type="primary" link @click="openListenerFieldForm(scope, scope.$index)">编辑</el-button>
<el-divider direction="vertical" /> <el-divider direction="vertical" />
<el-button size="small" type="text" style="color: #ff4d4f" @click="removeListenerField(scope, scope.$index)">移除</el-button> <el-button size="small" type="primary" link style="color: #ff4d4f" @click="removeListenerField(scope, scope.$index)">移除</el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>

View File

@ -23,7 +23,7 @@
</el-table> </el-table>
<el-dialog <el-dialog
v-model="modelVisible" v-model="dialogVisible"
:title="modelConfig.title" :title="modelConfig.title"
:close-on-click-modal="false" :close-on-click-modal="false"
width="400px" width="400px"
@ -39,7 +39,7 @@
</el-form-item> </el-form-item>
</el-form> </el-form>
<template #footer> <template #footer>
<el-button @click="modelVisible = false"> </el-button> <el-button @click="dialogVisible = false"> </el-button>
<el-button type="primary" @click="addNewObject"> </el-button> <el-button type="primary" @click="addNewObject"> </el-button>
</template> </template>
</el-dialog> </el-dialog>
@ -49,7 +49,7 @@
const message = useMessage() const message = useMessage()
const signalList = ref<any[]>([]) const signalList = ref<any[]>([])
const messageList = ref<any[]>([]) const messageList = ref<any[]>([])
const modelVisible = ref(false) const dialogVisible = ref(false)
const modelType = ref('') const modelType = ref('')
const modelObjectForm = ref<any>({}) const modelObjectForm = ref<any>({})
const rootElements = ref() const rootElements = ref()
@ -85,7 +85,7 @@ const initDataList = () => {
const openModel = (type) => { const openModel = (type) => {
modelType.value = type modelType.value = type
modelObjectForm.value = {} modelObjectForm.value = {}
modelVisible.value = true dialogVisible.value = true
} }
const addNewObject = () => { const addNewObject = () => {
if (modelType.value === 'message') { if (modelType.value === 'message') {
@ -101,7 +101,7 @@ const addNewObject = () => {
const signalRef = bpmnInstances().moddle.create('bpmn:Signal', modelObjectForm.value) const signalRef = bpmnInstances().moddle.create('bpmn:Signal', modelObjectForm.value)
rootElements.value.push(signalRef) rootElements.value.push(signalRef)
} }
modelVisible.value = false dialogVisible.value = false
initDataList() initDataList()
} }

View File

@ -1,25 +1,6 @@
import type { App } from 'vue' import type { App } from 'vue'
import { Icon } from './Icon' import { Icon } from './Icon'
import { Form } from '@/components/Form'
import { Table } from '@/components/Table'
import { Search } from '@/components/Search'
import { XModal } from '@/components/XModal'
import { XTable } from '@/components/XTable'
import { XButton, XTextButton } from '@/components/XButton'
import { DictTag } from '@/components/DictTag'
import { ContentWrap } from '@/components/ContentWrap'
import { Descriptions } from '@/components/Descriptions'
export const setupGlobCom = (app: App<Element>): void => { export const setupGlobCom = (app: App<Element>): void => {
app.component('Icon', Icon) app.component('Icon', Icon)
app.component('Form', Form)
app.component('Table', Table)
app.component('Search', Search)
app.component('XModal', XModal)
app.component('XTable', XTable)
app.component('XButton', XButton)
app.component('XTextButton', XTextButton)
app.component('DictTag', DictTag)
app.component('ContentWrap', ContentWrap)
app.component('Descriptions', Descriptions)
} }

View File

@ -1,8 +1,8 @@
import axios, { import axios, {
AxiosError,
AxiosInstance, AxiosInstance,
AxiosRequestHeaders, AxiosRequestHeaders,
AxiosResponse, AxiosResponse,
AxiosError,
InternalAxiosRequestConfig InternalAxiosRequestConfig
} from 'axios' } from 'axios'
@ -219,20 +219,19 @@ const handleAuthorized = () => {
if (!isRelogin.show) { if (!isRelogin.show) {
isRelogin.show = true isRelogin.show = true
ElMessageBox.confirm(t('sys.api.timeoutMessage'), t('common.confirmTitle'), { ElMessageBox.confirm(t('sys.api.timeoutMessage'), t('common.confirmTitle'), {
showCancelButton: false,
closeOnClickModal: false,
showClose: false,
confirmButtonText: t('login.relogin'), confirmButtonText: t('login.relogin'),
cancelButtonText: t('common.cancel'),
type: 'warning' type: 'warning'
}) }).then(() => {
.then(() => {
const { wsCache } = useCache() const { wsCache } = useCache()
resetRouter() // 重置静态路由表 resetRouter() // 重置静态路由表
wsCache.clear() wsCache.clear()
removeToken() removeToken()
isRelogin.show = false isRelogin.show = false
window.location.href = '/' // 干掉token后再走一次路由让它过router.beforeEach的校验
}) window.location.href = window.location.href
.catch(() => {
isRelogin.show = false
}) })
} }
return Promise.reject(t('sys.api.timeoutMessage')) return Promise.reject(t('sys.api.timeoutMessage'))

View File

@ -29,6 +29,8 @@ type CrudSearchParams = {
show?: boolean show?: boolean
// 接口 // 接口
api?: () => Promise<any> api?: () => Promise<any>
// 搜索字段
field?: string
} & Omit<FormSchema, 'field'> } & Omit<FormSchema, 'field'>
type CrudTableParams = { type CrudTableParams = {

View File

@ -1,4 +1,4 @@
<script setup lang="ts"> <script lang="ts" name="AppView" setup>
import { useTagsViewStore } from '@/store/modules/tagsView' import { useTagsViewStore } from '@/store/modules/tagsView'
import { useAppStore } from '@/store/modules/app' import { useAppStore } from '@/store/modules/app'
import { Footer } from '@/layout/components/Footer' import { Footer } from '@/layout/components/Footer'
@ -21,16 +21,17 @@ const getCaches = computed((): string[] => {
<template> <template>
<section <section
:class="[ :class="[
'p-[var(--app-content-padding)] w-[100%] bg-[var(--app-contnet-bg-color)] dark:bg-[var(--el-bg-color)]', 'p-[var(--app-content-padding)] w-[100%] bg-[var(--app-content-bg-color)] dark:bg-[var(--el-bg-color)]',
{ {
'!min-h-[calc(100%-var(--app-footer-height))]': '!min-h-[calc(100%-var(--app-footer-height))]':
fixedHeader && (layout === 'classic' || layout === 'topLeft') && footer, ((fixedHeader && (layout === 'classic' || layout === 'topLeft')) || layout === 'top') &&
footer,
'!min-h-[calc(100%-var(--tags-view-height)-var(--top-tool-height)-var(--app-footer-height))]': '!min-h-[calc(100%-var(--tags-view-height)-var(--top-tool-height)-var(--app-footer-height))]':
((!fixedHeader && layout === 'classic') || layout === 'top') && footer, !fixedHeader && layout === 'classic' && footer,
'!min-h-[calc(100%-var(--tags-view-height)-var(--app-footer-height))]': '!min-h-[calc(100%-var(--tags-view-height)-var(--app-footer-height))]':
!fixedHeader && layout === 'topLeft' && footer, !fixedHeader && (layout === 'topLeft' || layout === 'top') && footer,
'!min-h-[calc(100%-var(--top-tool-height))]': fixedHeader && layout === 'cutMenu' && footer, '!min-h-[calc(100%-var(--top-tool-height))]': fixedHeader && layout === 'cutMenu' && footer,

View File

@ -1,4 +1,4 @@
<script setup lang="ts"> <script lang="ts" name="Collapse" setup>
import { useAppStore } from '@/store/modules/app' import { useAppStore } from '@/store/modules/app'
import { propTypes } from '@/utils/propTypes' import { propTypes } from '@/utils/propTypes'
import { useDesign } from '@/hooks/web/useDesign' import { useDesign } from '@/hooks/web/useDesign'
@ -24,9 +24,9 @@ const toggleCollapse = () => {
<template> <template>
<div :class="prefixCls"> <div :class="prefixCls">
<Icon <Icon
:size="18"
:icon="collapse ? 'ep:expand' : 'ep:fold'"
:color="color" :color="color"
:icon="collapse ? 'ep:expand' : 'ep:fold'"
:size="18"
class="cursor-pointer" class="cursor-pointer"
@click="toggleCollapse" @click="toggleCollapse"
/> />

View File

@ -1,4 +1,4 @@
<script setup lang="ts"> <script lang="ts" name="ContextMenu" setup>
import { PropType } from 'vue' import { PropType } from 'vue'
import { useDesign } from '@/hooks/web/useDesign' import { useDesign } from '@/hooks/web/useDesign'
@ -51,9 +51,9 @@ defineExpose({
:class="prefixCls" :class="prefixCls"
:trigger="trigger" :trigger="trigger"
placement="bottom-start" placement="bottom-start"
popper-class="v-context-menu-popper"
@command="command" @command="command"
@visible-change="visibleChange" @visible-change="visibleChange"
popper-class="v-context-menu-popper"
> >
<slot></slot> <slot></slot>
<template #dropdown> <template #dropdown>
@ -61,11 +61,12 @@ defineExpose({
<ElDropdownItem <ElDropdownItem
v-for="(item, index) in schema" v-for="(item, index) in schema"
:key="`dropdown${index}`" :key="`dropdown${index}`"
:divided="item.divided"
:disabled="item.disabled"
:command="item" :command="item"
:disabled="item.disabled"
:divided="item.divided"
> >
<Icon :icon="item.icon" /> {{ t(item.label) }} <Icon :icon="item.icon" />
{{ t(item.label) }}
</ElDropdownItem> </ElDropdownItem>
</ElDropdownMenu> </ElDropdownMenu>
</template> </template>

View File

@ -1,4 +1,4 @@
<script setup lang="ts"> <script lang="ts" name="Footer" setup>
import { useAppStore } from '@/store/modules/app' import { useAppStore } from '@/store/modules/app'
import { useDesign } from '@/hooks/web/useDesign' import { useDesign } from '@/hooks/web/useDesign'

View File

@ -1,4 +1,4 @@
<script setup lang="ts"> <script lang="ts" name="LocaleDropdown" setup>
import { useLocaleStore } from '@/store/modules/locale' import { useLocaleStore } from '@/store/modules/locale'
import { useLocale } from '@/hooks/web/useLocale' import { useLocale } from '@/hooks/web/useLocale'
import { propTypes } from '@/utils/propTypes' import { propTypes } from '@/utils/propTypes'
@ -33,11 +33,11 @@ const setLang = (lang: LocaleType) => {
<template> <template>
<ElDropdown :class="prefixCls" trigger="click" @command="setLang"> <ElDropdown :class="prefixCls" trigger="click" @command="setLang">
<Icon <Icon
:size="18"
icon="ion:language-sharp"
class="cursor-pointer"
:class="$attrs.class" :class="$attrs.class"
:color="color" :color="color"
:size="18"
class="cursor-pointer"
icon="ion:language-sharp"
/> />
<template #dropdown> <template #dropdown>
<ElDropdownMenu> <ElDropdownMenu>

View File

@ -1,5 +1,5 @@
<script setup lang="ts"> <script lang="ts" name="Logo" setup>
import { ref, watch, computed, onMounted, unref } from 'vue' import { computed, onMounted, ref, unref, watch } from 'vue'
import { useAppStore } from '@/store/modules/app' import { useAppStore } from '@/store/modules/app'
import { useDesign } from '@/hooks/web/useDesign' import { useDesign } from '@/hooks/web/useDesign'
@ -66,8 +66,8 @@ watch(
to="/" to="/"
> >
<img <img
src="@/assets/imgs/logo.png"
class="w-[calc(var(--logo-height)-10px)] h-[calc(var(--logo-height)-10px)]" class="w-[calc(var(--logo-height)-10px)] h-[calc(var(--logo-height)-10px)]"
src="@/assets/imgs/logo.png"
/> />
<div <div
v-if="show" v-if="show"

View File

@ -1,5 +1,5 @@
<script setup lang="ts"> <script lang="ts" name="Message" setup>
import { parseTime } from '@/utils/formatTime' import { formatDate } from '@/utils/formatTime'
import * as NotifyMessageApi from '@/api/system/notify/message' import * as NotifyMessageApi from '@/api/system/notify/message'
const { push } = useRouter() const { push } = useRouter()
@ -43,10 +43,10 @@ onUnmounted(() => {
</script> </script>
<template> <template>
<div class="message"> <div class="message">
<ElPopover placement="bottom" :width="400" trigger="click"> <ElPopover :width="400" placement="bottom" trigger="click">
<template #reference> <template #reference>
<ElBadge :is-dot="unreadCount > 0" class="item"> <ElBadge :is-dot="unreadCount > 0" class="item">
<Icon icon="ep:bell" :size="18" class="cursor-pointer" @click="getList" /> <Icon :size="18" class="cursor-pointer" icon="ep:bell" @click="getList" />
</ElBadge> </ElBadge>
</template> </template>
<ElTabs v-model="activeName"> <ElTabs v-model="activeName">
@ -54,13 +54,13 @@ onUnmounted(() => {
<div class="message-list"> <div class="message-list">
<template v-for="item in list" :key="item.id"> <template v-for="item in list" :key="item.id">
<div class="message-item"> <div class="message-item">
<img src="@/assets/imgs/avatar.gif" alt="" class="message-icon" /> <img alt="" class="message-icon" src="@/assets/imgs/avatar.gif" />
<div class="message-content"> <div class="message-content">
<span class="message-title"> <span class="message-title">
{{ item.templateNickname }}{{ item.templateContent }} {{ item.templateNickname }}{{ item.templateContent }}
</span> </span>
<span class="message-date"> <span class="message-date">
{{ parseTime(item.createTime) }} {{ formatDate(item.createTime) }}
</span> </span>
</div> </div>
</div> </div>
@ -70,12 +70,12 @@ onUnmounted(() => {
</ElTabs> </ElTabs>
<!-- 更多 --> <!-- 更多 -->
<div style="text-align: right; margin-top: 10px"> <div style="text-align: right; margin-top: 10px">
<XButton type="primary" preIcon="ep:view" title="查看全部" @click="goMyList" /> <XButton preIcon="ep:view" title="查看全部" type="primary" @click="goMyList" />
</div> </div>
</ElPopover> </ElPopover>
</div> </div>
</template> </template>
<style scoped lang="scss"> <style lang="scss" scoped>
.message-empty { .message-empty {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@ -84,28 +84,35 @@ onUnmounted(() => {
height: 260px; height: 260px;
line-height: 45px; line-height: 45px;
} }
.message-list { .message-list {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
.message-item { .message-item {
display: flex; display: flex;
align-items: center; align-items: center;
padding: 20px 0; padding: 20px 0;
border-bottom: 1px solid var(--el-border-color-light); border-bottom: 1px solid var(--el-border-color-light);
&:last-child { &:last-child {
border: none; border: none;
} }
.message-icon { .message-icon {
width: 40px; width: 40px;
height: 40px; height: 40px;
margin: 0 20px 0 5px; margin: 0 20px 0 5px;
} }
.message-content { .message-content {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
.message-title { .message-title {
margin-bottom: 5px; margin-bottom: 5px;
} }
.message-date { .message-date {
font-size: 12px; font-size: 12px;
color: var(--el-text-color-secondary); color: var(--el-text-color-secondary);

View File

@ -1,4 +1,4 @@
<script setup lang="ts"> <script lang="ts" name="ScreenFull" setup>
import { Icon } from '@/components/Icon' import { Icon } from '@/components/Icon'
import { useFullscreen } from '@vueuse/core' import { useFullscreen } from '@vueuse/core'
import { propTypes } from '@/utils/propTypes' import { propTypes } from '@/utils/propTypes'
@ -22,9 +22,9 @@ const toggleFullscreen = () => {
<template> <template>
<div :class="prefixCls" @click="toggleFullscreen"> <div :class="prefixCls" @click="toggleFullscreen">
<Icon <Icon
:size="18"
:icon="isFullscreen ? 'zmdi:fullscreen-exit' : 'zmdi:fullscreen'"
:color="color" :color="color"
:icon="isFullscreen ? 'zmdi:fullscreen-exit' : 'zmdi:fullscreen'"
:size="18"
/> />
</div> </div>
</template> </template>

View File

@ -1,12 +1,12 @@
<script setup lang="ts"> <script lang="ts" name="Setting" setup>
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import { useCssVar, useClipboard } from '@vueuse/core' import { useClipboard, useCssVar } from '@vueuse/core'
import { CACHE_KEY, useCache } from '@/hooks/web/useCache' import { CACHE_KEY, useCache } from '@/hooks/web/useCache'
import { useDesign } from '@/hooks/web/useDesign' import { useDesign } from '@/hooks/web/useDesign'
import { trim, setCssVar } from '@/utils' import { setCssVar, trim } from '@/utils'
import { colorIsDark, lighten, hexToRGB } from '@/utils/color' import { colorIsDark, hexToRGB, lighten } from '@/utils/color'
import { useAppStore } from '@/store/modules/app' import { useAppStore } from '@/store/modules/app'
import { ThemeSwitch } from '@/layout/components/ThemeSwitch' import { ThemeSwitch } from '@/layout/components/ThemeSwitch'
import ColorRadioPicker from './components/ColorRadioPicker.vue' import ColorRadioPicker from './components/ColorRadioPicker.vue'
@ -202,10 +202,10 @@ const clear = () => {
class="fixed top-[45%] right-0 w-40px h-40px text-center leading-40px bg-[var(--el-color-primary)] cursor-pointer" class="fixed top-[45%] right-0 w-40px h-40px text-center leading-40px bg-[var(--el-color-primary)] cursor-pointer"
@click="drawer = true" @click="drawer = true"
> >
<Icon icon="ep:setting" color="#fff" /> <Icon color="#fff" icon="ep:setting" />
</div> </div>
<ElDrawer v-model="drawer" direction="rtl" size="350px" :z-index="4000"> <ElDrawer v-model="drawer" :z-index="4000" direction="rtl" size="350px">
<template #header> <template #header>
<span class="text-16px font-700">{{ t('setting.projectSetting') }}</span> <span class="text-16px font-700">{{ t('setting.projectSetting') }}</span>
</template> </template>
@ -279,10 +279,10 @@ const clear = () => {
<ElDivider /> <ElDivider />
<div> <div>
<ElButton type="primary" class="w-full" @click="copyConfig">{{ t('setting.copy') }}</ElButton> <ElButton class="w-full" type="primary" @click="copyConfig">{{ t('setting.copy') }}</ElButton>
</div> </div>
<div class="mt-5px"> <div class="mt-5px">
<ElButton type="danger" class="w-full" @click="clear"> <ElButton class="w-full" type="danger" @click="clear">
{{ t('setting.clearAndReset') }} {{ t('setting.clearAndReset') }}
</ElButton> </ElButton>
</div> </div>

View File

@ -1,4 +1,4 @@
<script setup lang="ts"> <script lang="ts" name="ColorRadioPicker" setup>
import { PropType } from 'vue' import { PropType } from 'vue'
import { propTypes } from '@/utils/propTypes' import { propTypes } from '@/utils/propTypes'
import { useDesign } from '@/hooks/web/useDesign' import { useDesign } from '@/hooks/web/useDesign'
@ -42,14 +42,14 @@ watch(
<span <span
v-for="(item, i) in schema" v-for="(item, i) in schema"
:key="`radio-${i}`" :key="`radio-${i}`"
class="w-20px h-20px cursor-pointer rounded-2px border-solid border-gray-300 border-2px text-center leading-20px mb-5px"
:class="{ 'is-active': colorVal === item }" :class="{ 'is-active': colorVal === item }"
:style="{ :style="{
background: item background: item
}" }"
class="w-20px h-20px cursor-pointer rounded-2px border-solid border-gray-300 border-2px text-center leading-20px mb-5px"
@click="colorVal = item" @click="colorVal = item"
> >
<Icon v-if="colorVal === item" color="#fff" icon="ep:check" :size="16" /> <Icon v-if="colorVal === item" :size="16" color="#fff" icon="ep:check" />
</span> </span>
</div> </div>
</template> </template>

View File

@ -1,4 +1,4 @@
<script setup lang="ts"> <script lang="ts" name="InterfaceDisplay" setup>
import { setCssVar } from '@/utils' import { setCssVar } from '@/utils'
import { useDesign } from '@/hooks/web/useDesign' import { useDesign } from '@/hooks/web/useDesign'

View File

@ -1,4 +1,4 @@
<script setup lang="ts"> <script lang="ts" name="LayoutRadioPicker" setup>
import { useAppStore } from '@/store/modules/app' import { useAppStore } from '@/store/modules/app'
import { useDesign } from '@/hooks/web/useDesign' import { useDesign } from '@/hooks/web/useDesign'

View File

@ -1,4 +1,4 @@
<script setup lang="ts"> <script lang="ts" name="SizeDropdown" setup>
import { useAppStore } from '@/store/modules/app' import { useAppStore } from '@/store/modules/app'
import { propTypes } from '@/utils/propTypes' import { propTypes } from '@/utils/propTypes'
@ -26,7 +26,7 @@ const setCurrentSize = (size: ElementPlusSize) => {
<template> <template>
<ElDropdown :class="prefixCls" trigger="click" @command="setCurrentSize"> <ElDropdown :class="prefixCls" trigger="click" @command="setCurrentSize">
<Icon :size="18" icon="mdi:format-size" :color="color" class="cursor-pointer" /> <Icon :color="color" :size="18" class="cursor-pointer" icon="mdi:format-size" />
<template #dropdown> <template #dropdown>
<ElDropdownMenu> <ElDropdownMenu>
<ElDropdownItem v-for="item in sizeMap" :key="item" :command="item"> <ElDropdownItem v-for="item in sizeMap" :key="item" :command="item">

View File

@ -1,4 +1,4 @@
<script setup lang="ts"> <script lang="ts" name="TagsView" setup>
import type { RouteLocationNormalizedLoaded, RouterLinkProps } from 'vue-router' import type { RouteLocationNormalizedLoaded, RouterLinkProps } from 'vue-router'
import { usePermissionStore } from '@/store/modules/permission' import { usePermissionStore } from '@/store/modules/permission'
import { useTagsViewStore } from '@/store/modules/tagsView' import { useTagsViewStore } from '@/store/modules/tagsView'
@ -266,15 +266,24 @@ watch(
@click="move(-200)" @click="move(-200)"
> >
<Icon <Icon
icon="ep:d-arrow-left"
:color="appStore.getIsDark ? 'var(--el-text-color-regular)' : '#333'" :color="appStore.getIsDark ? 'var(--el-text-color-regular)' : '#333'"
icon="ep:d-arrow-left"
/> />
</span> </span>
<div class="overflow-hidden flex-1"> <div class="overflow-hidden flex-1">
<ElScrollbar ref="scrollbarRef" class="h-full" @scroll="scroll"> <ElScrollbar ref="scrollbarRef" class="h-full" @scroll="scroll">
<div class="flex h-full"> <div class="flex h-full">
<ContextMenu <ContextMenu
v-for="item in visitedViews"
:key="item.fullPath"
:ref="itemRefs.set" :ref="itemRefs.set"
:class="[
`${prefixCls}__item`,
item?.meta?.affix ? `${prefixCls}__item--affix` : '',
{
'is-active': isActive(item)
}
]"
:schema="[ :schema="[
{ {
icon: 'ep:refresh', icon: 'ep:refresh',
@ -332,23 +341,14 @@ watch(
} }
} }
]" ]"
v-for="item in visitedViews"
:key="item.fullPath"
:tag-item="item" :tag-item="item"
:class="[
`${prefixCls}__item`,
item?.meta?.affix ? `${prefixCls}__item--affix` : '',
{
'is-active': isActive(item)
}
]"
@visible-change="visibleChange" @visible-change="visibleChange"
> >
<div> <div>
<router-link :ref="tagLinksRefs.set" :to="{ ...item }" custom v-slot="{ navigate }"> <router-link :ref="tagLinksRefs.set" v-slot="{ navigate }" :to="{ ...item }" custom>
<div <div
@click="navigate"
class="h-full flex justify-center items-center whitespace-nowrap pl-15px" class="h-full flex justify-center items-center whitespace-nowrap pl-15px"
@click="navigate"
> >
<Icon <Icon
v-if=" v-if="
@ -364,9 +364,9 @@ watch(
{{ t(item?.meta?.title as string) }} {{ t(item?.meta?.title as string) }}
<Icon <Icon
:class="`${prefixCls}__item--close`" :class="`${prefixCls}__item--close`"
:size="12"
color="#333" color="#333"
icon="ep:close" icon="ep:close"
:size="12"
@click.prevent.stop="closeSelectedTag(item)" @click.prevent.stop="closeSelectedTag(item)"
/> />
</div> </div>
@ -382,8 +382,8 @@ watch(
@click="move(200)" @click="move(200)"
> >
<Icon <Icon
icon="ep:d-arrow-right"
:color="appStore.getIsDark ? 'var(--el-text-color-regular)' : '#333'" :color="appStore.getIsDark ? 'var(--el-text-color-regular)' : '#333'"
icon="ep:d-arrow-right"
/> />
</span> </span>
<span <span
@ -392,12 +392,11 @@ watch(
@click="refreshSelectedTag(selectedTag)" @click="refreshSelectedTag(selectedTag)"
> >
<Icon <Icon
icon="ep:refresh-right"
:color="appStore.getIsDark ? 'var(--el-text-color-regular)' : '#333'" :color="appStore.getIsDark ? 'var(--el-text-color-regular)' : '#333'"
icon="ep:refresh-right"
/> />
</span> </span>
<ContextMenu <ContextMenu
trigger="click"
:schema="[ :schema="[
{ {
icon: 'ep:refresh', icon: 'ep:refresh',
@ -449,14 +448,15 @@ watch(
} }
} }
]" ]"
trigger="click"
> >
<span <span
:class="`${prefixCls}__tool`" :class="`${prefixCls}__tool`"
class="w-[var(--tags-view-height)] h-[var(--tags-view-height)] text-center leading-[var(--tags-view-height)] cursor-pointer block" class="w-[var(--tags-view-height)] h-[var(--tags-view-height)] text-center leading-[var(--tags-view-height)] cursor-pointer block"
> >
<Icon <Icon
icon="ep:menu"
:color="appStore.getIsDark ? 'var(--el-text-color-regular)' : '#333'" :color="appStore.getIsDark ? 'var(--el-text-color-regular)' : '#333'"
icon="ep:menu"
/> />
</span> </span>
</ContextMenu> </ContextMenu>
@ -513,6 +513,7 @@ $prefix-cls: #{$namespace}-tags-view;
display: none; display: none;
transform: translate(0, -50%); transform: translate(0, -50%);
} }
&:not(.#{$prefix-cls}__item--affix):hover { &:not(.#{$prefix-cls}__item--affix):hover {
.#{$prefix-cls}__item--close { .#{$prefix-cls}__item--close {
display: block; display: block;
@ -530,6 +531,7 @@ $prefix-cls: #{$namespace}-tags-view;
color: var(--el-color-white); color: var(--el-color-white);
background-color: var(--el-color-primary); background-color: var(--el-color-primary);
border: 1px solid var(--el-color-primary); border: 1px solid var(--el-color-primary);
.#{$prefix-cls}__item--close { .#{$prefix-cls}__item--close {
:deep(span) { :deep(span) {
color: var(--el-color-white) !important; color: var(--el-color-white) !important;
@ -573,6 +575,7 @@ $prefix-cls: #{$namespace}-tags-view;
&__item.is-active { &__item.is-active {
color: var(--el-color-white); color: var(--el-color-white);
background-color: var(--el-color-primary); background-color: var(--el-color-primary);
.#{$prefix-cls}__item--close { .#{$prefix-cls}__item--close {
:deep(span) { :deep(span) {
color: var(--el-color-white) !important; color: var(--el-color-white) !important;

View File

@ -1,4 +1,4 @@
<script setup lang="ts"> <script lang="ts" name="ThemeSwitch" setup>
import { useAppStore } from '@/store/modules/app' import { useAppStore } from '@/store/modules/app'
import { useIcon } from '@/hooks/web/useIcon' import { useIcon } from '@/hooks/web/useIcon'
import { useDesign } from '@/hooks/web/useDesign' import { useDesign } from '@/hooks/web/useDesign'
@ -26,14 +26,14 @@ const themeChange = (val: boolean) => {
<template> <template>
<ElSwitch <ElSwitch
:class="prefixCls"
v-model="isDark" v-model="isDark"
inline-prompt
:border-color="blackColor"
:inactive-color="blackColor"
:active-color="blackColor" :active-color="blackColor"
:active-icon="Sun" :active-icon="Sun"
:border-color="blackColor"
:class="prefixCls"
:inactive-color="blackColor"
:inactive-icon="CrescentMoon" :inactive-icon="CrescentMoon"
inline-prompt
@change="themeChange" @change="themeChange"
/> />
</template> </template>

View File

@ -1,4 +1,4 @@
<script setup lang="ts"> <script lang="ts" name="UserInfo" setup>
import { ElMessageBox } from 'element-plus' import { ElMessageBox } from 'element-plus'
import { CACHE_KEY, useCache } from '@/hooks/web/useCache' import { CACHE_KEY, useCache } from '@/hooks/web/useCache'

View File

@ -177,7 +177,9 @@ export const useRenderLayout = () => {
class={[ class={[
`${prefixCls}-content-scrollbar`, `${prefixCls}-content-scrollbar`,
{ {
'mt-[var(--tags-view-height)]': fixedHeader.value 'mt-[var(--tags-view-height)] !pb-[calc(var(--tags-view-height)+var(--app-footer-height))]':
fixedHeader.value,
'pb-[var(--app-footer-height)]': !fixedHeader.value
} }
]} ]}
> >

View File

@ -443,5 +443,6 @@ export default {
btn_zoom_in: 'Zoom in', btn_zoom_in: 'Zoom in',
btn_zoom_out: 'Zoom out', btn_zoom_out: 'Zoom out',
preview: 'Preivew' preview: 'Preivew'
} },
'OAuth 2.0': 'OAuth 2.0'
} }

View File

@ -297,8 +297,7 @@ export default {
typeCreate: '字典类型新增', typeCreate: '字典类型新增',
typeUpdate: '字典类型编辑', typeUpdate: '字典类型编辑',
dataCreate: '字典数据新增', dataCreate: '字典数据新增',
dataUpdate: '字典数据编辑', dataUpdate: '字典数据编辑'
fileUpload: '上传文件'
}, },
dialog: { dialog: {
dialog: '弹窗', dialog: '弹窗',
@ -353,6 +352,7 @@ export default {
login: { login: {
backSignIn: '返回', backSignIn: '返回',
signInFormTitle: '登录', signInFormTitle: '登录',
ssoFormTitle: '三方授权',
mobileSignInFormTitle: '手机登录', mobileSignInFormTitle: '手机登录',
qrSignInFormTitle: '二维码登录', qrSignInFormTitle: '二维码登录',
signUpFormTitle: '注册', signUpFormTitle: '注册',
@ -436,5 +436,6 @@ export default {
btn_zoom_in: '放大', btn_zoom_in: '放大',
btn_zoom_out: '缩小', btn_zoom_out: '缩小',
preview: '预览' preview: '预览'
} },
'OAuth 2.0': 'OAuth 2.0'
} }

View File

@ -40,41 +40,16 @@ import App from './App.vue'
import './permission' import './permission'
import { isDevMode } from '@/utils/env' import '@/plugins/tongji' // 百度统计
import { MyPD } from '@/components/bpmnProcessDesigner/package/index.js'
import '@/components/bpmnProcessDesigner/package/theme/index.scss'
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 hljs from 'highlight.js' //导入代码高亮文件
import 'highlight.js/styles/github.css' //导入代码高亮样式 新版
import Logger from '@/utils/Logger' import Logger from '@/utils/Logger'
// 本地开发模式 全局引入 element-plus 样式,加快第一次进入速度
if (isDevMode()) {
import('element-plus/dist/index.css')
}
// 创建实例 // 创建实例
const setupAll = async () => { const setupAll = async () => {
const app = createApp(App) const app = createApp(App)
//自定义一个代码高亮指令
app.directive('highlight', function (el) {
const blocks = el.querySelectorAll('code')
blocks.forEach((block: any) => {
hljs.highlightElement(block)
})
})
await setupI18n(app) await setupI18n(app)
MyPD(app)
setupStore(app) setupStore(app)
setupGlobCom(app) setupGlobCom(app)

View File

@ -6,7 +6,8 @@ import {
PieChart, PieChart,
MapChart, MapChart,
PictorialBarChart, PictorialBarChart,
RadarChart RadarChart,
GaugeChart
} from 'echarts/charts' } from 'echarts/charts'
import { import {
@ -16,7 +17,8 @@ import {
PolarComponent, PolarComponent,
AriaComponent, AriaComponent,
ParallelComponent, ParallelComponent,
LegendComponent LegendComponent,
ToolboxComponent
} from 'echarts/components' } from 'echarts/components'
import { CanvasRenderer } from 'echarts/renderers' import { CanvasRenderer } from 'echarts/renderers'
@ -25,6 +27,7 @@ echarts.use([
LegendComponent, LegendComponent,
TitleComponent, TitleComponent,
TooltipComponent, TooltipComponent,
ToolboxComponent,
GridComponent, GridComponent,
PolarComponent, PolarComponent,
AriaComponent, AriaComponent,
@ -35,7 +38,8 @@ echarts.use([
MapChart, MapChart,
CanvasRenderer, CanvasRenderer,
PictorialBarChart, PictorialBarChart,
RadarChart RadarChart,
GaugeChart
]) ])
export default echarts export default echarts

View File

@ -17,7 +17,6 @@ import {
import formCreate from '@form-create/element-ui' import formCreate from '@form-create/element-ui'
import install from '@form-create/element-ui/auto-import' import install from '@form-create/element-ui/auto-import'
import FcDesigner from '@form-create/designer'
const components = [ const components = [
ElAside, ElAside,
@ -34,14 +33,11 @@ const components = [
ElTabPane ElTabPane
] ]
// 参考 http://www.form-create.com/v3/element-ui/auto-import.html 文档
export const setupFormCreate = (app: App<Element>) => { export const setupFormCreate = (app: App<Element>) => {
components.forEach((component) => { components.forEach((component) => {
app.component(component.name, component) app.component(component.name, component)
}) })
formCreate.use(install) formCreate.use(install)
app.use(formCreate) app.use(formCreate)
app.use(FcDesigner)
} }

View File

@ -0,0 +1,23 @@
import router from '@/router'
// 用于 router push
window._hmt = window._hmt || []
// HM_ID
const HM_ID = import.meta.env.VITE_APP_BAIDU_CODE
;(function () {
// 有值的时候,才开启
if (!HM_ID) {
return
}
const hm = document.createElement('script')
hm.src = 'https://hm.baidu.com/hm.js?' + HM_ID
const s = document.getElementsByTagName('script')[0]
s.parentNode.insertBefore(hm, s)
})()
router.afterEach(function (to) {
if (!HM_ID) {
return
}
_hmt.push(['_trackPageview', to.fullPath])
})

View File

@ -1,11 +1,11 @@
import type { App } from 'vue' import type { App } from 'vue'
import type { RouteRecordRaw } from 'vue-router' import type { RouteRecordRaw } from 'vue-router'
import { createRouter, createWebHashHistory } from 'vue-router' import { createRouter, createWebHistory } from 'vue-router'
import remainingRouter from './modules/remaining' import remainingRouter from './modules/remaining'
// 创建路由实例 // 创建路由实例
const router = createRouter({ const router = createRouter({
history: createWebHashHistory(), // createWebHashHistory URL带#createWebHistory URL不带# history: createWebHistory(), // createWebHashHistory URL带#createWebHistory URL不带#
strict: true, strict: true,
routes: remainingRouter as RouteRecordRaw[], routes: remainingRouter as RouteRecordRaw[],
scrollBehavior: () => ({ left: 0, top: 0 }) scrollBehavior: () => ({ left: 0, top: 0 })

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