Merge remote-tracking branch 'yudao/master'

pull/162/head
dhb52 2023-04-24 09:27:44 +08:00
commit 21cb44290d
229 changed files with 5561 additions and 4438 deletions

3
.env
View File

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

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

2
.gitignore vendored
View File

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

View File

@ -10,7 +10,9 @@
## 🐶 新手必读 ## 🐶 新手必读
* nodejs > 16.0.0 && pnpm > 7.30.0 * nodejs > 16.0.0 && pnpm > 7.30.0
* 演示地址:<http://dashboard.yudao.iocoder.cn> * 演示地址【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/>
@ -18,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)
@ -37,11 +39,11 @@
| 框架 | 说明 | 版本 | | 框架 | 说明 | 版本 |
|----------------------------------------------------------------------|------------------|--------| |----------------------------------------------------------------------|------------------|--------|
| [Vue](https://staging-cn.vuejs.org/) | Vue 框架 | 3.2.47 | | [Vue](https://staging-cn.vuejs.org/) | Vue 框架 | 3.2.47 |
| [Vite](https://cn.vitejs.dev//) | 开发与构建工具 | 4.1.4 | | [Vite](https://cn.vitejs.dev//) | 开发与构建工具 | 4.3.1 |
| [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.3 |
| [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.0.35 |
| [vueuse](https://vueuse.org/) | 常用工具集 | 9.13.0 | | [vueuse](https://vueuse.org/) | 常用工具集 | 10.1.0 |
| [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.1.6 |
| [windicss](https://cn.windicss.org/) | 下一代工具优先的 CSS 框架 | 3.5.6 | | [windicss](https://cn.windicss.org/) | 下一代工具优先的 CSS 框架 | 3.5.6 |

View File

@ -19,10 +19,12 @@ import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'
export function createVitePlugins() { export function createVitePlugins() {
const root = process.cwd() const root = process.cwd()
// 路径查找 // 路径查找
function pathResolve(dir: string) { function pathResolve(dir: string) {
return resolve(root, '.', dir) return resolve(root, '.', dir)
} }
return [ return [
Vue(), Vue(),
VueJsx(), VueJsx(),
@ -45,8 +47,6 @@ export function createVitePlugins() {
{ {
'@/hooks/web/useI18n': ['useI18n'], '@/hooks/web/useI18n': ['useI18n'],
'@/hooks/web/useMessage': ['useMessage'], '@/hooks/web/useMessage': ['useMessage'],
'@/hooks/web/useXTable': ['useXTable'],
'@/hooks/web/useVxeCrudSchemas': ['useVxeCrudSchemas'],
'@/hooks/web/useTable': ['useTable'], '@/hooks/web/useTable': ['useTable'],
'@/hooks/web/useCrudSchemas': ['useCrudSchemas'], '@/hooks/web/useCrudSchemas': ['useCrudSchemas'],
'@/utils/formRules': ['required'], '@/utils/formRules': ['required'],

View File

@ -17,7 +17,6 @@ const include = [
'cropperjs', 'cropperjs',
'lodash-es', 'lodash-es',
'nprogress', 'nprogress',
'animate.css',
'web-storage-cache', 'web-storage-cache',
'@iconify/iconify', '@iconify/iconify',
'@vueuse/core', '@vueuse/core',
@ -33,37 +32,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',
@ -76,7 +96,13 @@ const include = [
'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/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'
] ]
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.2-snapshot",
"description": "基于vue3、vite4、element-plus、typesScript", "description": "基于vue3、vite4、element-plus、typesScript",
"author": "xingyu", "author": "xingyu",
"private": false, "private": false,
@ -11,6 +11,7 @@
"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", "build:front": "node --max_old_space_size=8000 ./node_modules/vite/bin/vite.js build --mode front",
@ -32,12 +33,12 @@
"@form-create/element-ui": "^3.1.17", "@form-create/element-ui": "^3.1.17",
"@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.0",
"@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": "^2.2.1",
"animate.css": "^4.1.1", "animate.css": "^4.1.1",
"axios": "^1.3.5", "axios": "^1.3.6",
"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",
@ -45,19 +46,19 @@
"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.3", "element-plus": "2.3.3",
"fast-xml-parser": "^4.1.3", "fast-xml-parser": "^4.2.2",
"highlight.js": "^11.7.0", "highlight.js": "^11.7.0",
"intro.js": "^7.0.1", "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.0",
"mitt": "^3.0.0", "mitt": "^3.0.0",
"nprogress": "^0.2.0", "nprogress": "^0.2.0",
"pinia": "^2.0.34", "pinia": "^2.0.35",
"qrcode": "^1.5.1", "qrcode": "^1.5.3",
"qs": "^6.11.1", "qs": "^6.11.1",
"steady-xml": "^0.1.0", "steady-xml": "^0.1.0",
"url": "^0.11.0", "url": "^0.11.0",
@ -72,61 +73,60 @@
"xml-js": "^1.6.11" "xml-js": "^1.6.11"
}, },
"devDependencies": { "devDependencies": {
"@commitlint/cli": "^17.5.0", "@commitlint/cli": "^17.6.1",
"@commitlint/config-conventional": "^17.4.4", "@commitlint/config-conventional": "^17.6.1",
"@iconify/json": "^2.2.38", "@iconify/json": "^2.2.54",
"@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.0",
"@typescript-eslint/parser": "^5.56.0", "@typescript-eslint/parser": "^5.59.0",
"@vitejs/plugin-legacy": "^4.0.2", "@vitejs/plugin-legacy": "^4.0.2",
"@vitejs/plugin-vue": "^4.1.0", "@vitejs/plugin-vue": "^4.1.0",
"@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.39.0",
"eslint-config-prettier": "^8.8.0", "eslint-config-prettier": "^8.8.0",
"eslint-define-config": "^1.17.0", "eslint-define-config": "^1.18.0",
"eslint-plugin-prettier": "^4.2.1", "eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-vue": "^9.9.0", "eslint-plugin-vue": "^9.11.0",
"lint-staged": "^13.2.0", "lint-staged": "^13.2.1",
"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.0",
"rollup": "^3.20.0", "rollup": "^3.20.7",
"sass": "^1.59.3", "sass": "^1.62.0",
"stylelint": "^15.3.0", "stylelint": "^15.6.0",
"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.1",
"typescript": "5.0.2", "typescript": "5.0.4",
"unplugin-auto-import": "^0.15.1", "unplugin-auto-import": "^0.15.3",
"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.1",
"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-top-level-await": "^1.3.0", "vite-plugin-top-level-await": "^1.3.0",
"vite-plugin-vue-setup-extend-plus": "^0.1.0", "vite-plugin-vue-setup-extend-plus": "^0.1.0",
"vite-plugin-windicss": "^1.8.10", "vite-plugin-windicss": "^1.8.10",
"vue-tsc": "^1.2.0", "vue-tsc": "^1.4.4",
"windicss": "^3.5.6" "windicss": "^3.5.6"
}, },
"engines": { "engines": {

View File

@ -1,4 +1,4 @@
<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'
@ -32,6 +32,7 @@ $prefix-cls: #{$namespace}-app;
width: 100%; width: 100%;
height: 100%; height: 100%;
} }
html, html,
body { body {
padding: 0 !important; padding: 0 !important;
@ -43,6 +44,7 @@ body {
@extend .size; @extend .size;
} }
} }
.#{$prefix-cls}-grey-mode { .#{$prefix-cls}-grey-mode {
filter: grayscale(100%); filter: grayscale(100%);
} }

View File

@ -1,8 +1,9 @@
import request from '@/config/axios' import request from '@/config/axios'
export interface NotifyTemplateVO { export interface NotifyTemplateVO {
id: number id?: number
name: string name: string
nickname: string
code: string code: string
content: string content: string
type: number type: number
@ -11,45 +12,38 @@ export interface NotifyTemplateVO {
remark: string remark: string
} }
export interface NotifyTemplatePageReqVO extends PageParam {
name?: string
code?: string
status?: number
createTime?: Date[]
}
export interface NotifySendReqVO { export interface NotifySendReqVO {
userId: number userId: number | null
templateCode: string templateCode: string
templateParams: Map<String, Object> templateParams: Map<String, Object>
} }
// 查询站内信模板列表 // 查询站内信模板列表
export const getNotifyTemplatePageApi = async (params: NotifyTemplatePageReqVO) => { export const getNotifyTemplatePage = async (params: PageParam) => {
return await request.get({ url: '/system/notify-template/page', params }) return await request.get({ url: '/system/notify-template/page', params })
} }
// 查询站内信模板详情 // 查询站内信模板详情
export const getNotifyTemplateApi = async (id: number) => { export const getNotifyTemplate = async (id: number) => {
return await request.get({ url: '/system/notify-template/get?id=' + id }) return await request.get({ url: '/system/notify-template/get?id=' + id })
} }
// 新增站内信模板 // 新增站内信模板
export const createNotifyTemplateApi = async (data: NotifyTemplateVO) => { export const createNotifyTemplate = async (data: NotifyTemplateVO) => {
return await request.post({ url: '/system/notify-template/create', data }) return await request.post({ url: '/system/notify-template/create', data })
} }
// 修改站内信模板 // 修改站内信模板
export const updateNotifyTemplateApi = async (data: NotifyTemplateVO) => { export const updateNotifyTemplate = async (data: NotifyTemplateVO) => {
return await request.put({ url: '/system/notify-template/update', data }) return await request.put({ url: '/system/notify-template/update', data })
} }
// 删除站内信模板 // 删除站内信模板
export const deleteNotifyTemplateApi = async (id: number) => { export const deleteNotifyTemplate = async (id: number) => {
return await request.delete({ url: '/system/notify-template/delete?id=' + id }) return await request.delete({ url: '/system/notify-template/delete?id=' + id })
} }
// 发送站内信 // 发送站内信
export const sendNotifyApi = (data: NotifySendReqVO) => { export const sendNotify = (data: NotifySendReqVO) => {
return request.post({ url: '/system/notify-template/send-notify', data }) return request.post({ url: '/system/notify-template/send-notify', data })
} }

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

@ -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'
@ -189,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,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'
@ -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

@ -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,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 { getCode, reqCheck } from '@/api/login' import { getCode, reqCheck } 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

@ -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

@ -1,21 +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 { 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('XButton', XButton)
app.component('XTextButton', XTextButton)
app.component('DictTag', DictTag)
app.component('ContentWrap', ContentWrap)
app.component('Descriptions', Descriptions)
} }

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'

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,4 +1,4 @@
<script setup lang="ts"> <script lang="ts" name="Message" setup>
import { formatDate } from '@/utils/formatTime' import { formatDate } from '@/utils/formatTime'
import * as NotifyMessageApi from '@/api/system/notify/message' import * as NotifyMessageApi from '@/api/system/notify/message'
@ -40,10 +40,10 @@ onMounted(() => {
</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">
@ -51,7 +51,7 @@ onMounted(() => {
<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 }}
@ -67,12 +67,12 @@ onMounted(() => {
</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;
@ -81,28 +81,35 @@ onMounted(() => {
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

@ -37,43 +37,16 @@ import App from './App.vue'
import './permission' import './permission'
import { isDevMode } from '@/utils/env'
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 '@/plugins/tongji' // 百度统计 import '@/plugins/tongji' // 百度统计
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

@ -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

@ -272,7 +272,7 @@ const remainingRouter: AppRouteRecordRaw[] = [
{ {
path: '/manager/task-assign-rule', path: '/manager/task-assign-rule',
component: () => import('@/views/bpm/taskAssignRule/index.vue'), component: () => import('@/views/bpm/taskAssignRule/index.vue'),
name: 'BpmTaskAssignRule', name: 'BpmTaskAssignRuleList',
meta: { meta: {
noCache: true, noCache: true,
hidden: true, hidden: true,
@ -313,7 +313,19 @@ const remainingRouter: AppRouteRecordRaw[] = [
hidden: true, hidden: true,
canTo: true, canTo: true,
title: '发起 OA 请假', title: '发起 OA 请假',
activeMenu: 'bpm/oa/leave/create' activeMenu: '/bpm/oa/leave'
}
},
{
path: '/bpm/oa/leave/detail',
component: () => import('@/views/bpm/oa/leave/detail.vue'),
name: 'OALeaveDetail',
meta: {
noCache: true,
hidden: true,
canTo: true,
title: '查看 OA 请假',
activeMenu: '/bpm/oa/leave'
} }
} }
] ]

View File

@ -1,5 +1,4 @@
@import './var.css'; @import './var.css';
@import './vxe.css';
@import 'element-plus/theme-chalk/dark/css-vars.css'; @import 'element-plus/theme-chalk/dark/css-vars.css';
.reset-margin [class*='el-icon'] + span { .reset-margin [class*='el-icon'] + span {

View File

@ -2,5 +2,3 @@
$namespace: v; $namespace: v;
// el // el
$elNamespace: el; $elNamespace: el;
// vxe
$vxeNamespace: vxe;

View File

@ -1,30 +0,0 @@
/*滚动条整体部分*/
.xtable-scrollbar ::-webkit-scrollbar {
width: 10px;
height: 10px;
}
/*滚动条的轨道*/
.xtable-scrollbar ::-webkit-scrollbar-track {
background-color: #ffffff;
}
/*滚动条里面的小方块,能向上向下移动*/
.xtable-scrollbar ::-webkit-scrollbar-thumb {
background-color: #bfbfbf;
border-radius: 5px;
border: 1px solid #f1f1f1;
box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
}
.xtable-scrollbar ::-webkit-scrollbar-thumb:hover {
background-color: #a8a8a8;
}
.xtable-scrollbar ::-webkit-scrollbar-thumb:active {
background-color: #787878;
}
/*边角,即两个滚动条的交汇处*/
.xtable-scrollbar ::-webkit-scrollbar-corner {
background-color: #ffffff;
}
/*移除 vxe-grid 右侧外框*/
.vxe-button.size--medium.type--button.is--circle {
border: 0;
}

View File

@ -1 +0,0 @@
auto-*.d.ts

View File

@ -1,8 +0,0 @@
export const isDevMode = () => {
const dev = import.meta.env.VITE_DEV
if (dev && dev === 'true') {
return true
} else {
return false
}
}

View File

@ -1,10 +1,22 @@
import type { RouteLocationNormalized, Router, RouteRecordNormalized } from 'vue-router'
import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router' import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router'
import type { Router, RouteLocationNormalized, RouteRecordNormalized } from 'vue-router'
import { isUrl } from '@/utils/is' import { isUrl } from '@/utils/is'
import { omit, cloneDeep } from 'lodash-es' import { cloneDeep, omit } from 'lodash-es'
const modules = import.meta.glob('../views/**/*.{vue,tsx}') const modules = import.meta.glob('../views/**/*.{vue,tsx}')
/**
*
* @param componentPath :/bpm/oa/leave/detail
*/
export const registerComponent = (componentPath: string) => {
for (const item in modules) {
if (item.includes(componentPath)) {
// 使用异步组件的方式来动态加载组件
// @ts-ignore
return defineAsyncComponent(modules[item])
}
}
}
/* Layout */ /* Layout */
export const Layout = () => import('@/layout/Layout.vue') export const Layout = () => import('@/layout/Layout.vue')

View File

@ -1,6 +1,6 @@
<template> <template>
<Error type="403" @error-click="push('/')" /> <Error type="403" @error-click="push('/')" />
</template> </template>
<script setup lang="ts"> <script lang="ts" name="Error403" setup>
const { push } = useRouter() const { push } = useRouter()
</script> </script>

View File

@ -1,6 +1,6 @@
<template> <template>
<Error @error-click="push('/')" /> <Error @error-click="push('/')" />
</template> </template>
<script setup lang="ts"> <script lang="ts" name="Error404" setup>
const { push } = useRouter() const { push } = useRouter()
</script> </script>

View File

@ -1,6 +1,6 @@
<template> <template>
<Error type="500" @error-click="push('/')" /> <Error type="500" @error-click="push('/')" />
</template> </template>
<script setup lang="ts"> <script lang="ts" name="Error500" setup>
const { push } = useRouter() const { push } = useRouter()
</script> </script>

View File

@ -1,26 +1,26 @@
<template> <template>
<el-row :gutter="20" justify="space-between" :class="prefixCls"> <el-row :class="prefixCls" :gutter="20" justify="space-between">
<el-col :xl="6" :lg="6" :md="12" :sm="12" :xs="24"> <el-col :lg="6" :md="12" :sm="12" :xl="6" :xs="24">
<el-card shadow="hover" class="mb-20px"> <el-card class="mb-20px" shadow="hover">
<el-skeleton :loading="loading" animated :rows="2"> <el-skeleton :loading="loading" :rows="2" animated>
<template #default> <template #default>
<div :class="`${prefixCls}__item flex justify-between`"> <div :class="`${prefixCls}__item flex justify-between`">
<div> <div>
<div <div
:class="`${prefixCls}__item--icon ${prefixCls}__item--peoples p-16px inline-block rounded-6px`" :class="`${prefixCls}__item--icon ${prefixCls}__item--peoples p-16px inline-block rounded-6px`"
> >
<Icon icon="svg-icon:peoples" :size="40" /> <Icon :size="40" icon="svg-icon:peoples" />
</div> </div>
</div> </div>
<div class="flex flex-col justify-between"> <div class="flex flex-col justify-between">
<div :class="`${prefixCls}__item--text text-16px text-gray-500 text-right`">{{ <div :class="`${prefixCls}__item--text text-16px text-gray-500 text-right`"
t('analysis.newUser') >{{ t('analysis.newUser') }}
}}</div> </div>
<CountTo <CountTo
class="text-20px font-700 text-right"
:start-val="0"
:end-val="102400"
:duration="2600" :duration="2600"
:end-val="102400"
:start-val="0"
class="text-20px font-700 text-right"
/> />
</div> </div>
</div> </div>
@ -29,27 +29,27 @@
</el-card> </el-card>
</el-col> </el-col>
<el-col :xl="6" :lg="6" :md="12" :sm="12" :xs="24"> <el-col :lg="6" :md="12" :sm="12" :xl="6" :xs="24">
<el-card shadow="hover" class="mb-20px"> <el-card class="mb-20px" shadow="hover">
<el-skeleton :loading="loading" animated :rows="2"> <el-skeleton :loading="loading" :rows="2" animated>
<template #default> <template #default>
<div :class="`${prefixCls}__item flex justify-between`"> <div :class="`${prefixCls}__item flex justify-between`">
<div> <div>
<div <div
:class="`${prefixCls}__item--icon ${prefixCls}__item--message p-16px inline-block rounded-6px`" :class="`${prefixCls}__item--icon ${prefixCls}__item--message p-16px inline-block rounded-6px`"
> >
<Icon icon="svg-icon:message" :size="40" /> <Icon :size="40" icon="svg-icon:message" />
</div> </div>
</div> </div>
<div class="flex flex-col justify-between"> <div class="flex flex-col justify-between">
<div :class="`${prefixCls}__item--text text-16px text-gray-500 text-right`">{{ <div :class="`${prefixCls}__item--text text-16px text-gray-500 text-right`"
t('analysis.unreadInformation') >{{ t('analysis.unreadInformation') }}
}}</div> </div>
<CountTo <CountTo
class="text-20px font-700 text-right"
:start-val="0"
:end-val="81212"
:duration="2600" :duration="2600"
:end-val="81212"
:start-val="0"
class="text-20px font-700 text-right"
/> />
</div> </div>
</div> </div>
@ -58,27 +58,27 @@
</el-card> </el-card>
</el-col> </el-col>
<el-col :xl="6" :lg="6" :md="12" :sm="12" :xs="24"> <el-col :lg="6" :md="12" :sm="12" :xl="6" :xs="24">
<el-card shadow="hover" class="mb-20px"> <el-card class="mb-20px" shadow="hover">
<el-skeleton :loading="loading" animated :rows="2"> <el-skeleton :loading="loading" :rows="2" animated>
<template #default> <template #default>
<div :class="`${prefixCls}__item flex justify-between`"> <div :class="`${prefixCls}__item flex justify-between`">
<div> <div>
<div <div
:class="`${prefixCls}__item--icon ${prefixCls}__item--money p-16px inline-block rounded-6px`" :class="`${prefixCls}__item--icon ${prefixCls}__item--money p-16px inline-block rounded-6px`"
> >
<Icon icon="svg-icon:money" :size="40" /> <Icon :size="40" icon="svg-icon:money" />
</div> </div>
</div> </div>
<div class="flex flex-col justify-between"> <div class="flex flex-col justify-between">
<div :class="`${prefixCls}__item--text text-16px text-gray-500 text-right`">{{ <div :class="`${prefixCls}__item--text text-16px text-gray-500 text-right`"
t('analysis.transactionAmount') >{{ t('analysis.transactionAmount') }}
}}</div> </div>
<CountTo <CountTo
class="text-20px font-700 text-right"
:start-val="0"
:end-val="9280"
:duration="2600" :duration="2600"
:end-val="9280"
:start-val="0"
class="text-20px font-700 text-right"
/> />
</div> </div>
</div> </div>
@ -87,27 +87,27 @@
</el-card> </el-card>
</el-col> </el-col>
<el-col :xl="6" :lg="6" :md="12" :sm="12" :xs="24"> <el-col :lg="6" :md="12" :sm="12" :xl="6" :xs="24">
<el-card shadow="hover" class="mb-20px"> <el-card class="mb-20px" shadow="hover">
<el-skeleton :loading="loading" animated :rows="2"> <el-skeleton :loading="loading" :rows="2" animated>
<template #default> <template #default>
<div :class="`${prefixCls}__item flex justify-between`"> <div :class="`${prefixCls}__item flex justify-between`">
<div> <div>
<div <div
:class="`${prefixCls}__item--icon ${prefixCls}__item--shopping p-16px inline-block rounded-6px`" :class="`${prefixCls}__item--icon ${prefixCls}__item--shopping p-16px inline-block rounded-6px`"
> >
<Icon icon="svg-icon:shopping" :size="40" /> <Icon :size="40" icon="svg-icon:shopping" />
</div> </div>
</div> </div>
<div class="flex flex-col justify-between"> <div class="flex flex-col justify-between">
<div :class="`${prefixCls}__item--text text-16px text-gray-500 text-right`">{{ <div :class="`${prefixCls}__item--text text-16px text-gray-500 text-right`"
t('analysis.totalShopping') >{{ t('analysis.totalShopping') }}
}}</div> </div>
<CountTo <CountTo
class="text-20px font-700 text-right"
:start-val="0"
:end-val="13600"
:duration="2600" :duration="2600"
:end-val="13600"
:start-val="0"
class="text-20px font-700 text-right"
/> />
</div> </div>
</div> </div>
@ -117,36 +117,36 @@
</el-col> </el-col>
</el-row> </el-row>
<el-row :gutter="20" justify="space-between"> <el-row :gutter="20" justify="space-between">
<el-col :xl="10" :lg="10" :md="24" :sm="24" :xs="24"> <el-col :lg="10" :md="24" :sm="24" :xl="10" :xs="24">
<el-card shadow="hover" class="mb-20px"> <el-card class="mb-20px" shadow="hover">
<el-skeleton :loading="loading" animated> <el-skeleton :loading="loading" animated>
<Echart :options="pieOptionsData" :height="300" /> <Echart :height="300" :options="pieOptionsData" />
</el-skeleton> </el-skeleton>
</el-card> </el-card>
</el-col> </el-col>
<el-col :xl="14" :lg="14" :md="24" :sm="24" :xs="24"> <el-col :lg="14" :md="24" :sm="24" :xl="14" :xs="24">
<el-card shadow="hover" class="mb-20px"> <el-card class="mb-20px" shadow="hover">
<el-skeleton :loading="loading" animated> <el-skeleton :loading="loading" animated>
<Echart :options="barOptionsData" :height="300" /> <Echart :height="300" :options="barOptionsData" />
</el-skeleton> </el-skeleton>
</el-card> </el-card>
</el-col> </el-col>
<el-col :span="24"> <el-col :span="24">
<el-card shadow="hover" class="mb-20px"> <el-card class="mb-20px" shadow="hover">
<el-skeleton :loading="loading" animated :rows="4"> <el-skeleton :loading="loading" :rows="4" animated>
<Echart :options="lineOptionsData" :height="350" /> <Echart :height="350" :options="lineOptionsData" />
</el-skeleton> </el-skeleton>
</el-card> </el-card>
</el-col> </el-col>
</el-row> </el-row>
</template> </template>
<script setup lang="ts"> <script lang="ts" name="Home2" setup>
import { set } from 'lodash-es' import { set } from 'lodash-es'
import { EChartsOption } from 'echarts' import { EChartsOption } from 'echarts'
import { useDesign } from '@/hooks/web/useDesign' import { useDesign } from '@/hooks/web/useDesign'
import type { AnalysisTotalTypes } from './types' import type { AnalysisTotalTypes } from './types'
import { pieOptions, barOptions, lineOptions } from './echarts-data' import { barOptions, lineOptions, pieOptions } from './echarts-data'
const { t } = useI18n() const { t } = useI18n()
const loading = ref(true) const loading = ref(true)
@ -291,18 +291,23 @@ $prefix-cls: #{$namespace}-panel;
:deep(.#{$namespace}-icon) { :deep(.#{$namespace}-icon) {
color: #fff !important; color: #fff !important;
} }
.#{$prefix-cls}__item--icon { .#{$prefix-cls}__item--icon {
transition: all 0.38s ease-out; transition: all 0.38s ease-out;
} }
.#{$prefix-cls}__item--peoples { .#{$prefix-cls}__item--peoples {
background: #40c9c6; background: #40c9c6;
} }
.#{$prefix-cls}__item--message { .#{$prefix-cls}__item--message {
background: #36a3f7; background: #36a3f7;
} }
.#{$prefix-cls}__item--money { .#{$prefix-cls}__item--money {
background: #f4516c; background: #f4516c;
} }
.#{$prefix-cls}__item--shopping { .#{$prefix-cls}__item--shopping {
background: #34bfa3; background: #34bfa3;
} }

View File

@ -60,7 +60,7 @@
</div> </div>
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" name="Login" setup>
import { underlineToHump } from '@/utils' import { underlineToHump } from '@/utils'
import { useDesign } from '@/hooks/web/useDesign' import { useDesign } from '@/hooks/web/useDesign'

View File

@ -1,13 +1,13 @@
<template> <template>
<el-form <el-form
:model="loginData.loginForm"
:rules="LoginRules"
label-position="top"
class="login-form"
label-width="120px"
size="large"
v-show="getShow" v-show="getShow"
ref="formLogin" ref="formLogin"
:model="loginData.loginForm"
:rules="LoginRules"
class="login-form"
label-position="top"
label-width="120px"
size="large"
> >
<el-row style="maring-left: -10px; maring-right: -10px"> <el-row style="maring-left: -10px; maring-right: -10px">
<el-col :span="24" style="padding-left: 10px; padding-right: 10px"> <el-col :span="24" style="padding-left: 10px; padding-right: 10px">
@ -16,13 +16,13 @@
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="24" style="padding-left: 10px; padding-right: 10px"> <el-col :span="24" style="padding-left: 10px; padding-right: 10px">
<el-form-item prop="tenantName" v-if="loginData.tenantEnable === 'true'"> <el-form-item v-if="loginData.tenantEnable === 'true'" prop="tenantName">
<el-input <el-input
type="primary"
link
v-model="loginData.loginForm.tenantName" v-model="loginData.loginForm.tenantName"
:placeholder="t('login.tenantNamePlaceholder')" :placeholder="t('login.tenantNamePlaceholder')"
:prefix-icon="iconHouse" :prefix-icon="iconHouse"
type="primary"
link
/> />
</el-form-item> </el-form-item>
</el-col> </el-col>
@ -39,11 +39,11 @@
<el-form-item prop="password"> <el-form-item prop="password">
<el-input <el-input
v-model="loginData.loginForm.password" v-model="loginData.loginForm.password"
type="password"
:placeholder="t('login.passwordPlaceholder')" :placeholder="t('login.passwordPlaceholder')"
show-password
@keyup.enter="getCode()"
:prefix-icon="iconLock" :prefix-icon="iconLock"
show-password
type="password"
@keyup.enter="getCode()"
/> />
</el-form-item> </el-form-item>
</el-col> </el-col>
@ -58,8 +58,8 @@
{{ t('login.remember') }} {{ t('login.remember') }}
</el-checkbox> </el-checkbox>
</el-col> </el-col>
<el-col :span="12" :offset="6"> <el-col :offset="6" :span="12">
<el-link type="primary" style="float: right">{{ t('login.forgetPassword') }}</el-link> <el-link style="float: right" type="primary">{{ t('login.forgetPassword') }}</el-link>
</el-col> </el-col>
</el-row> </el-row>
</el-form-item> </el-form-item>
@ -68,41 +68,41 @@
<el-form-item> <el-form-item>
<XButton <XButton
:loading="loginLoading" :loading="loginLoading"
type="primary"
class="w-[100%]"
:title="t('login.login')" :title="t('login.login')"
class="w-[100%]"
type="primary"
@click="getCode()" @click="getCode()"
/> />
</el-form-item> </el-form-item>
</el-col> </el-col>
<Verify <Verify
ref="verify" ref="verify"
mode="pop"
:captchaType="captchaType" :captchaType="captchaType"
:imgSize="{ width: '400px', height: '200px' }" :imgSize="{ width: '400px', height: '200px' }"
mode="pop"
@success="handleLogin" @success="handleLogin"
/> />
<el-col :span="24" style="padding-left: 10px; padding-right: 10px"> <el-col :span="24" style="padding-left: 10px; padding-right: 10px">
<el-form-item> <el-form-item>
<el-row justify="space-between" style="width: 100%" :gutter="5"> <el-row :gutter="5" justify="space-between" style="width: 100%">
<el-col :span="8"> <el-col :span="8">
<XButton <XButton
class="w-[100%]"
:title="t('login.btnMobile')" :title="t('login.btnMobile')"
class="w-[100%]"
@click="setLoginState(LoginStateEnum.MOBILE)" @click="setLoginState(LoginStateEnum.MOBILE)"
/> />
</el-col> </el-col>
<el-col :span="8"> <el-col :span="8">
<XButton <XButton
class="w-[100%]"
:title="t('login.btnQRCode')" :title="t('login.btnQRCode')"
class="w-[100%]"
@click="setLoginState(LoginStateEnum.QR_CODE)" @click="setLoginState(LoginStateEnum.QR_CODE)"
/> />
</el-col> </el-col>
<el-col :span="8"> <el-col :span="8">
<XButton <XButton
class="w-[100%]"
:title="t('login.btnRegister')" :title="t('login.btnRegister')"
class="w-[100%]"
@click="setLoginState(LoginStateEnum.REGISTER)" @click="setLoginState(LoginStateEnum.REGISTER)"
/> />
</el-col> </el-col>
@ -125,10 +125,25 @@
</div> </div>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-divider content-position="center">萌新必读</el-divider>
<el-col :span="24" style="padding-left: 10px; padding-right: 10px">
<el-form-item>
<div class="flex justify-between w-[100%]">
<el-link href="https://doc.iocoder.cn/" target="_blank">📚开发指南</el-link>
<el-link href="https://doc.iocoder.cn/video/" target="_blank">🔥视频教程</el-link>
<el-link href="https://www.iocoder.cn/Interview/good-collection/" target="_blank">
面试手册
</el-link>
<el-link href="http://static.yudao.iocoder.cn/mp/Aix9975.jpeg" target="_blank">
🤝外包咨询
</el-link>
</div>
</el-form-item>
</el-col>
</el-row> </el-row>
</el-form> </el-form>
</template> </template>
<script setup lang="ts"> <script lang="ts" name="LoginForm" setup>
import { ElLoading } from 'element-plus' import { ElLoading } from 'element-plus'
import LoginFormTitle from './LoginFormTitle.vue' import LoginFormTitle from './LoginFormTitle.vue'
import type { RouteLocationNormalizedLoaded } from 'vue-router' import type { RouteLocationNormalizedLoaded } from 'vue-router'

View File

@ -3,7 +3,7 @@
{{ getFormTitle }} {{ getFormTitle }}
</h2> </h2>
</template> </template>
<script setup lang="ts"> <script lang="ts" name="LoginFormTitle" setup>
import { LoginStateEnum, useLoginState } from './useLogin' import { LoginStateEnum, useLoginState } from './useLogin'
const { t } = useI18n() const { t } = useI18n()

View File

@ -1,13 +1,13 @@
<template> <template>
<el-form <el-form
:model="loginData.loginForm"
:rules="rules"
label-position="top"
class="login-form"
label-width="120px"
size="large"
v-show="getShow" v-show="getShow"
ref="formSmsLogin" ref="formSmsLogin"
:model="loginData.loginForm"
:rules="rules"
class="login-form"
label-position="top"
label-width="120px"
size="large"
> >
<el-row style="margin-left: -10px; margin-right: -10px"> <el-row style="margin-left: -10px; margin-right: -10px">
<!-- 租户名 --> <!-- 租户名 -->
@ -17,13 +17,13 @@
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="24" style="padding-left: 10px; padding-right: 10px"> <el-col :span="24" style="padding-left: 10px; padding-right: 10px">
<el-form-item prop="tenantName" v-if="loginData.tenantEnable === 'true'"> <el-form-item v-if="loginData.tenantEnable === 'true'" prop="tenantName">
<el-input <el-input
type="primary"
link
v-model="loginData.loginForm.tenantName" v-model="loginData.loginForm.tenantName"
:placeholder="t('login.tenantNamePlaceholder')" :placeholder="t('login.tenantNamePlaceholder')"
:prefix-icon="iconHouse" :prefix-icon="iconHouse"
type="primary"
link
/> />
</el-form-item> </el-form-item>
</el-col> </el-col>
@ -40,7 +40,7 @@
<!-- 验证码 --> <!-- 验证码 -->
<el-col :span="24" style="padding-left: 10px; padding-right: 10px"> <el-col :span="24" style="padding-left: 10px; padding-right: 10px">
<el-form-item prop="code"> <el-form-item prop="code">
<el-row justify="space-between" style="width: 100%" :gutter="5"> <el-row :gutter="5" justify="space-between" style="width: 100%">
<el-col :span="24"> <el-col :span="24">
<el-input <el-input
v-model="loginData.loginForm.code" v-model="loginData.loginForm.code"
@ -51,9 +51,9 @@
<template #append> <template #append>
<span <span
v-if="mobileCodeTimer <= 0" v-if="mobileCodeTimer <= 0"
@click="getSmsCode"
class="getMobileCode" class="getMobileCode"
style="cursor: pointer" style="cursor: pointer"
@click="getSmsCode"
> >
{{ t('login.getSmsCode') }} {{ t('login.getSmsCode') }}
</span> </span>
@ -72,9 +72,9 @@
<el-form-item> <el-form-item>
<XButton <XButton
:loading="loginLoading" :loading="loginLoading"
type="primary"
class="w-[100%]"
:title="t('login.login')" :title="t('login.login')"
class="w-[100%]"
type="primary"
@click="signIn()" @click="signIn()"
/> />
</el-form-item> </el-form-item>
@ -83,8 +83,8 @@
<el-form-item> <el-form-item>
<XButton <XButton
:loading="loginLoading" :loading="loginLoading"
class="w-[100%]"
:title="t('login.backLogin')" :title="t('login.backLogin')"
class="w-[100%]"
@click="handleBackLogin()" @click="handleBackLogin()"
/> />
</el-form-item> </el-form-item>
@ -92,7 +92,7 @@
</el-row> </el-row>
</el-form> </el-form>
</template> </template>
<script setup lang="ts"> <script lang="ts" name="MobileForm" setup>
import type { RouteLocationNormalizedLoaded } from 'vue-router' import type { RouteLocationNormalizedLoaded } from 'vue-router'
import { useIcon } from '@/hooks/web/useIcon' import { useIcon } from '@/hooks/web/useIcon'
@ -101,7 +101,7 @@ import { setTenantId, setToken } from '@/utils/auth'
import { usePermissionStore } from '@/store/modules/permission' import { usePermissionStore } from '@/store/modules/permission'
import { getTenantIdByName, sendSmsCode, smsLogin } from '@/api/login' import { getTenantIdByName, sendSmsCode, smsLogin } from '@/api/login'
import LoginFormTitle from './LoginFormTitle.vue' import LoginFormTitle from './LoginFormTitle.vue'
import { useLoginState, LoginStateEnum, useFormValid } from './useLogin' import { LoginStateEnum, useFormValid, useLoginState } from './useLogin'
const { t } = useI18n() const { t } = useI18n()
const message = useMessage() const message = useMessage()

View File

@ -4,23 +4,23 @@
<LoginFormTitle style="width: 100%" /> <LoginFormTitle style="width: 100%" />
</el-col> </el-col>
<el-col :span="24" style="padding-left: 10px; padding-right: 10px"> <el-col :span="24" style="padding-left: 10px; padding-right: 10px">
<el-card shadow="hover" class="mb-10px text-center"> <el-card class="mb-10px text-center" shadow="hover">
<Qrcode :logo="logoImg" /> <Qrcode :logo="logoImg" />
</el-card> </el-card>
</el-col> </el-col>
<el-divider class="enter-x">{{ t('login.qrcode') }}</el-divider> <el-divider class="enter-x">{{ t('login.qrcode') }}</el-divider>
<el-col :span="24" style="padding-left: 10px; padding-right: 10px"> <el-col :span="24" style="padding-left: 10px; padding-right: 10px">
<div class="w-[100%] mt-15px"> <div class="w-[100%] mt-15px">
<XButton class="w-[100%]" :title="t('login.backLogin')" @click="handleBackLogin()" /> <XButton :title="t('login.backLogin')" class="w-[100%]" @click="handleBackLogin()" />
</div> </div>
</el-col> </el-col>
</el-row> </el-row>
</template> </template>
<script setup lang="ts"> <script lang="ts" name="QrCodeForm" setup>
import logoImg from '@/assets/imgs/logo.png' import logoImg from '@/assets/imgs/logo.png'
import LoginFormTitle from './LoginFormTitle.vue' import LoginFormTitle from './LoginFormTitle.vue'
import { useLoginState, LoginStateEnum } from './useLogin' import { LoginStateEnum, useLoginState } from './useLogin'
const { t } = useI18n() const { t } = useI18n()
const { handleBackLogin, getLoginState } = useLoginState() const { handleBackLogin, getLoginState } = useLoginState()

View File

@ -1,12 +1,12 @@
<template> <template>
<Form <Form
:schema="schema"
:rules="rules"
label-position="top"
hide-required-asterisk
size="large"
v-show="getShow" v-show="getShow"
:rules="rules"
:schema="schema"
class="dark:(border-1 border-[var(--el-border-color)] border-solid)" class="dark:(border-1 border-[var(--el-border-color)] border-solid)"
hide-required-asterisk
label-position="top"
size="large"
@register="register" @register="register"
> >
<template #title> <template #title>
@ -23,25 +23,25 @@
<div class="w-[100%]"> <div class="w-[100%]">
<XButton <XButton
:loading="loading" :loading="loading"
type="primary"
class="w-[100%]"
:title="t('login.register')" :title="t('login.register')"
class="w-[100%]"
type="primary"
@click="loginRegister()" @click="loginRegister()"
/> />
</div> </div>
<div class="w-[100%] mt-15px"> <div class="w-[100%] mt-15px">
<XButton class="w-[100%]" :title="t('login.hasUser')" @click="handleBackLogin()" /> <XButton :title="t('login.hasUser')" class="w-[100%]" @click="handleBackLogin()" />
</div> </div>
</template> </template>
</Form> </Form>
</template> </template>
<script setup lang="ts"> <script lang="ts" name="RegisterForm" setup>
import type { FormRules } from 'element-plus' import type { FormRules } from 'element-plus'
import { useForm } from '@/hooks/web/useForm' import { useForm } from '@/hooks/web/useForm'
import { useValidator } from '@/hooks/web/useValidator' import { useValidator } from '@/hooks/web/useValidator'
import LoginFormTitle from './LoginFormTitle.vue' import LoginFormTitle from './LoginFormTitle.vue'
import { useLoginState, LoginStateEnum } from './useLogin' import { LoginStateEnum, useLoginState } from './useLogin'
import { FormSchema } from '@/types/form' import { FormSchema } from '@/types/form'
const { t } = useI18n() const { t } = useI18n()

View File

@ -1,5 +1,5 @@
<template> <template>
<Form ref="formRef" :rules="rules" :schema="schema" :labelWidth="80"> <Form ref="formRef" :labelWidth="80" :rules="rules" :schema="schema">
<template #sex="form"> <template #sex="form">
<el-radio-group v-model="form['sex']"> <el-radio-group v-model="form['sex']">
<el-radio :label="1">{{ t('profile.user.man') }}</el-radio> <el-radio :label="1">{{ t('profile.user.man') }}</el-radio>
@ -8,9 +8,9 @@
</template> </template>
</Form> </Form>
<XButton :title="t('common.save')" @click="submit()" /> <XButton :title="t('common.save')" @click="submit()" />
<XButton type="danger" :title="t('common.reset')" @click="init()" /> <XButton :title="t('common.reset')" type="danger" @click="init()" />
</template> </template>
<script setup lang="ts"> <script lang="ts" name="BasicInfo" setup>
import type { FormRules } from 'element-plus' import type { FormRules } from 'element-plus'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'

View File

@ -5,41 +5,48 @@
</div> </div>
<ul class="list-group list-group-striped"> <ul class="list-group list-group-striped">
<li class="list-group-item"> <li class="list-group-item">
<Icon icon="ep:user" class="mr-5px" />{{ t('profile.user.username') }} <Icon class="mr-5px" icon="ep:user" />
{{ t('profile.user.username') }}
<div class="pull-right">{{ userInfo?.username }}</div> <div class="pull-right">{{ userInfo?.username }}</div>
</li> </li>
<li class="list-group-item"> <li class="list-group-item">
<Icon icon="ep:phone" class="mr-5px" />{{ t('profile.user.mobile') }} <Icon class="mr-5px" icon="ep:phone" />
{{ t('profile.user.mobile') }}
<div class="pull-right">{{ userInfo?.mobile }}</div> <div class="pull-right">{{ userInfo?.mobile }}</div>
</li> </li>
<li class="list-group-item"> <li class="list-group-item">
<Icon icon="fontisto:email" class="mr-5px" />{{ t('profile.user.email') }} <Icon class="mr-5px" icon="fontisto:email" />
{{ t('profile.user.email') }}
<div class="pull-right">{{ userInfo?.email }}</div> <div class="pull-right">{{ userInfo?.email }}</div>
</li> </li>
<li class="list-group-item"> <li class="list-group-item">
<Icon icon="carbon:tree-view-alt" class="mr-5px" />{{ t('profile.user.dept') }} <Icon class="mr-5px" icon="carbon:tree-view-alt" />
<div class="pull-right" v-if="userInfo?.dept">{{ userInfo?.dept.name }}</div> {{ t('profile.user.dept') }}
<div v-if="userInfo?.dept" class="pull-right">{{ userInfo?.dept.name }}</div>
</li> </li>
<li class="list-group-item"> <li class="list-group-item">
<Icon icon="ep:suitcase" class="mr-5px" />{{ t('profile.user.posts') }} <Icon class="mr-5px" icon="ep:suitcase" />
<div class="pull-right" v-if="userInfo?.posts"> {{ t('profile.user.posts') }}
<div v-if="userInfo?.posts" class="pull-right">
{{ userInfo?.posts.map((post) => post.name).join(',') }} {{ userInfo?.posts.map((post) => post.name).join(',') }}
</div> </div>
</li> </li>
<li class="list-group-item"> <li class="list-group-item">
<Icon icon="icon-park-outline:peoples" class="mr-5px" />{{ t('profile.user.roles') }} <Icon class="mr-5px" icon="icon-park-outline:peoples" />
<div class="pull-right" v-if="userInfo?.roles"> {{ t('profile.user.roles') }}
<div v-if="userInfo?.roles" class="pull-right">
{{ userInfo?.roles.map((role) => role.name).join(',') }} {{ userInfo?.roles.map((role) => role.name).join(',') }}
</div> </div>
</li> </li>
<li class="list-group-item"> <li class="list-group-item">
<Icon icon="ep:calendar" class="mr-5px" />{{ t('profile.user.createTime') }} <Icon class="mr-5px" icon="ep:calendar" />
{{ t('profile.user.createTime') }}
<div class="pull-right">{{ formatDate(userInfo?.createTime) }}</div> <div class="pull-right">{{ formatDate(userInfo?.createTime) }}</div>
</li> </li>
</ul> </ul>
</div> </div>
</template> </template>
<script setup lang="ts"> <script lang="ts" name="ProfileUser" setup>
import { formatDate } from '@/utils/formatTime' import { formatDate } from '@/utils/formatTime'
import UserAvatar from './UserAvatar.vue' import UserAvatar from './UserAvatar.vue'
@ -62,6 +69,7 @@ onMounted(async () => {
position: relative; position: relative;
height: 120px; height: 120px;
} }
.list-group-striped > .list-group-item { .list-group-striped > .list-group-item {
border-left: 0; border-left: 0;
border-right: 0; border-right: 0;
@ -82,6 +90,7 @@ onMounted(async () => {
padding: 11px 0px; padding: 11px 0px;
font-size: 13px; font-size: 13px;
} }
.pull-right { .pull-right {
float: right !important; float: right !important;
} }

View File

@ -10,13 +10,13 @@
<InputPassword v-model="password.confirmPassword" strength /> <InputPassword v-model="password.confirmPassword" strength />
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<XButton type="primary" @click="submit(formRef)" :title="t('common.save')" /> <XButton :title="t('common.save')" type="primary" @click="submit(formRef)" />
<XButton type="danger" :title="t('common.reset')" @click="reset(formRef)" /> <XButton :title="t('common.reset')" type="danger" @click="reset(formRef)" />
</el-form-item> </el-form-item>
</el-form> </el-form>
</template> </template>
<script setup lang="ts"> <script lang="ts" name="ResetPwd" setup>
import type { FormRules, FormInstance } from 'element-plus' import type { FormInstance, FormRules } from 'element-plus'
import { InputPassword } from '@/components/InputPassword' import { InputPassword } from '@/components/InputPassword'
import { updateUserPassword } from '@/api/system/user/profile' import { updateUserPassword } from '@/api/system/user/profile'

View File

@ -2,17 +2,18 @@
<div class="change-avatar"> <div class="change-avatar">
<CropperAvatar <CropperAvatar
ref="cropperRef" ref="cropperRef"
:value="avatar"
:showBtn="false"
@change="handelUpload"
:btnProps="{ preIcon: 'ant-design:cloud-upload-outlined' }" :btnProps="{ preIcon: 'ant-design:cloud-upload-outlined' }"
:showBtn="false"
:value="avatar"
width="120px" width="120px"
@change="handelUpload"
/> />
</div> </div>
</template> </template>
<script setup lang="ts"> <script lang="ts" name="UserAvatar" setup>
import { propTypes } from '@/utils/propTypes' import { propTypes } from '@/utils/propTypes'
import { uploadAvatar } from '@/api/system/user/profile' import { uploadAvatar } from '@/api/system/user/profile'
const props = defineProps({ const props = defineProps({
img: propTypes.string.def('') img: propTypes.string.def('')
}) })
@ -27,7 +28,7 @@ const handelUpload = async ({ data }) => {
} }
</script> </script>
<style scoped lang="scss"> <style lang="scss" scoped>
.change-avatar { .change-avatar {
img { img {
display: block; display: block;

View File

@ -1,30 +1,30 @@
<template> <template>
<el-table :data="socialUsers" :show-header="false"> <el-table :data="socialUsers" :show-header="false">
<el-table-column type="seq" title="序号" width="60" fixed="left" /> <el-table-column fixed="left" title="序号" type="seq" width="60" />
<el-table-column label="社交平台" align="left" width="120"> <el-table-column align="left" label="社交平台" width="120">
<template #default="{ row }"> <template #default="{ row }">
<img class="h-5 align-middle" :src="row.img" alt="" /> <img :src="row.img" alt="" class="h-5 align-middle" />
<p class="mr-5">{{ row.title }}</p> <p class="mr-5">{{ row.title }}</p>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="操作" align="center"> <el-table-column align="center" label="操作">
<template #default="{ row }"> <template #default="{ row }">
<template v-if="row.openid"> <template v-if="row.openid">
已绑定 已绑定
<XTextButton type="primary" class="mr-5" @click="unbind(row)" title="(解绑)" /> <XTextButton class="mr-5" title="(解绑)" type="primary" @click="unbind(row)" />
</template> </template>
<template v-else> <template v-else>
未绑定 未绑定
<XTextButton type="primary" class="mr-5" @click="bind(row)" title="(绑定)" /> <XTextButton class="mr-5" title="(绑定)" type="primary" @click="bind(row)" />
</template> </template>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
</template> </template>
<script setup lang="ts"> <script lang="ts" name="UserSocial" setup>
import { SystemUserSocialTypeEnum } from '@/utils/constants' import { SystemUserSocialTypeEnum } from '@/utils/constants'
import { getUserProfile, ProfileVO } from '@/api/system/user/profile' import { getUserProfile, ProfileVO } from '@/api/system/user/profile'
import { socialAuthRedirect, socialUnbind } from '@/api/system/user/socialUser' import { socialAuthRedirect, socialBind, socialUnbind } from '@/api/system/user/socialUser'
const message = useMessage() const message = useMessage()
const socialUsers = ref<any[]>([]) const socialUsers = ref<any[]>([])
@ -46,11 +46,25 @@ const initSocial = async () => {
} }
} }
} }
const route = useRoute()
const bindSocial = () => {
//
const type = route.query.type
const code = route.query.code
const state = route.query.state
if (!code) {
return
}
socialBind(type, code, state).then(() => {
message.success('绑定成功')
initSocial()
})
}
const bind = (row) => { const bind = (row) => {
const redirectUri = location.origin + '/user/profile?type=' + row.type const redirectUri = location.origin + '/user/profile?type=' + row.type
// //
socialAuthRedirect(row.type, encodeURIComponent(redirectUri)).then((res) => { socialAuthRedirect(row.type, encodeURIComponent(redirectUri)).then((res) => {
window.location.href = res.data window.location.href = res
}) })
} }
const unbind = async (row) => { const unbind = async (row) => {
@ -64,4 +78,15 @@ const unbind = async (row) => {
onMounted(async () => { onMounted(async () => {
await initSocial() await initSocial()
}) })
watch(
() => route,
(newRoute) => {
bindSocial()
console.log(newRoute)
},
{
immediate: true
}
)
</script> </script>

View File

@ -84,10 +84,10 @@
<!-- 弹窗流程模型图的预览 --> <!-- 弹窗流程模型图的预览 -->
<Dialog title="流程图" v-model="bpmnDetailVisible" width="800"> <Dialog title="流程图" v-model="bpmnDetailVisible" width="800">
<my-process-viewer <MyProcessViewer
key="designer" key="designer"
v-model="bpmnXML" v-model="bpmnXML"
:value="bpmnXML" :value="bpmnXML as any"
v-bind="bpmnControlForm" v-bind="bpmnControlForm"
:prefix="bpmnControlForm.prefix" :prefix="bpmnControlForm.prefix"
/> />
@ -97,6 +97,7 @@
<script setup lang="ts" name="BpmProcessDefinition"> <script setup lang="ts" name="BpmProcessDefinition">
import { DICT_TYPE } from '@/utils/dict' import { DICT_TYPE } from '@/utils/dict'
import { dateFormatter } from '@/utils/formatTime' import { dateFormatter } from '@/utils/formatTime'
import { MyProcessViewer } from '@/components/bpmnProcessDesigner/package'
import * as DefinitionApi from '@/api/bpm/definition' import * as DefinitionApi from '@/api/bpm/definition'
import { setConfAndFields2 } from '@/utils/formCreate' import { setConfAndFields2 } from '@/utils/formCreate'
const { push } = useRouter() // const { push } = useRouter() //

View File

@ -1,17 +1,18 @@
<template> <template>
<ContentWrap> <ContentWrap>
<!-- 表单设计器 --> <!-- 表单设计器 -->
<fc-designer ref="designer" height="780px"> <FcDesigner ref="designer" height="780px">
<template #handle> <template #handle>
<el-button round size="small" type="primary" @click="handleSave"> <el-button round size="small" type="primary" @click="handleSave">
<Icon icon="ep:plus" class="mr-5px" /> 保存 <Icon class="mr-5px" icon="ep:plus" />
保存
</el-button> </el-button>
</template> </template>
</fc-designer> </FcDesigner>
</ContentWrap> </ContentWrap>
<!-- 表单保存的弹窗 --> <!-- 表单保存的弹窗 -->
<Dialog title="保存表单" v-model="dialogVisible" width="600"> <Dialog v-model="dialogVisible" title="保存表单" width="600">
<el-form ref="formRef" :model="formData" :rules="formRules" label-width="80px"> <el-form ref="formRef" :model="formData" :rules="formRules" label-width="80px">
<el-form-item label="表单名" prop="name"> <el-form-item label="表单名" prop="name">
<el-input v-model="formData.name" placeholder="请输入表单名" /> <el-input v-model="formData.name" placeholder="请输入表单名" />
@ -28,24 +29,28 @@
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<el-form-item label="备注" prop="remark"> <el-form-item label="备注" prop="remark">
<el-input v-model="formData.remark" type="textarea" placeholder="请输入备注" /> <el-input v-model="formData.remark" placeholder="请输入备注" type="textarea" />
</el-form-item> </el-form-item>
</el-form> </el-form>
<template #footer> <template #footer>
<el-button @click="submitForm" type="primary" :disabled="formLoading"> </el-button> <el-button :disabled="formLoading" type="primary" @click="submitForm"> </el-button>
<el-button @click="dialogVisible = false"> </el-button> <el-button @click="dialogVisible = false"> </el-button>
</template> </template>
</Dialog> </Dialog>
</template> </template>
<script setup lang="ts"> <script lang="ts" name="BpmFormEditor" setup>
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict' import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
import { CommonStatusEnum } from '@/utils/constants' import { CommonStatusEnum } from '@/utils/constants'
import * as FormApi from '@/api/bpm/form' import * as FormApi from '@/api/bpm/form'
import FcDesigner from '@form-create/designer'
import { encodeConf, encodeFields, setConfAndFields } from '@/utils/formCreate' import { encodeConf, encodeFields, setConfAndFields } from '@/utils/formCreate'
import { useTagsViewStore } from '@/store/modules/tagsView'
const { t } = useI18n() // const { t } = useI18n() //
const message = useMessage() // const message = useMessage() //
const { query } = useRoute() // const { push, currentRoute } = useRouter() //
const { query } = useRoute() //
const { delView } = useTagsViewStore() //
const designer = ref() // const designer = ref() //
const dialogVisible = ref(false) // const dialogVisible = ref(false) //
@ -86,10 +91,16 @@ const submitForm = async () => {
message.success(t('common.updateSuccess')) message.success(t('common.updateSuccess'))
} }
dialogVisible.value = false dialogVisible.value = false
close()
} finally { } finally {
formLoading.value = false formLoading.value = false
} }
} }
/** 关闭按钮 */
const close = () => {
delView(unref(currentRoute))
push('/bpm/manager/form')
}
/** 初始化 **/ /** 初始化 **/
onMounted(async () => { onMounted(async () => {

View File

@ -2,26 +2,33 @@
<ContentWrap> <ContentWrap>
<!-- 搜索工作栏 --> <!-- 搜索工作栏 -->
<el-form <el-form
class="-mb-15px"
:model="queryParams"
ref="queryFormRef" ref="queryFormRef"
:inline="true" :inline="true"
:model="queryParams"
class="-mb-15px"
label-width="68px" label-width="68px"
> >
<el-form-item label="表单名" prop="name"> <el-form-item label="表单名" prop="name">
<el-input <el-input
v-model="queryParams.name" v-model="queryParams.name"
placeholder="请输入表单名"
clearable
@keyup.enter="handleQuery"
class="!w-240px" class="!w-240px"
clearable
placeholder="请输入表单名"
@keyup.enter="handleQuery"
/> />
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button> <el-button @click="handleQuery">
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button> <Icon class="mr-5px" icon="ep:search" />
<el-button type="primary" plain @click="openForm" v-hasPermi="['bpm:form:create']"> 搜索
<Icon icon="ep:plus" class="mr-5px" /> 新增 </el-button>
<el-button @click="resetQuery">
<Icon class="mr-5px" icon="ep:refresh" />
重置
</el-button>
<el-button v-hasPermi="['bpm:form:create']" plain type="primary" @click="openForm">
<Icon class="mr-5px" icon="ep:plus" />
新增
</el-button> </el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
@ -30,38 +37,38 @@
<!-- 列表 --> <!-- 列表 -->
<ContentWrap> <ContentWrap>
<el-table v-loading="loading" :data="list"> <el-table v-loading="loading" :data="list">
<el-table-column label="编号" align="center" prop="id" /> <el-table-column align="center" label="编号" prop="id" />
<el-table-column label="表单名" align="center" prop="name" /> <el-table-column align="center" label="表单名" prop="name" />
<el-table-column label="状态" align="center" prop="status"> <el-table-column align="center" label="状态" prop="status">
<template #default="scope"> <template #default="scope">
<dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status" /> <dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status" />
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="备注" align="center" prop="remark" /> <el-table-column align="center" label="备注" prop="remark" />
<el-table-column <el-table-column
label="创建时间"
align="center"
prop="createTime"
:formatter="dateFormatter" :formatter="dateFormatter"
align="center"
label="创建时间"
prop="createTime"
/> />
<el-table-column label="操作" align="center"> <el-table-column align="center" label="操作">
<template #default="scope"> <template #default="scope">
<el-button <el-button
v-hasPermi="['bpm:form:update']"
link link
type="primary" type="primary"
@click="openForm(scope.row.id)" @click="openForm(scope.row.id)"
v-hasPermi="['bpm:form:update']"
> >
编辑 编辑
</el-button> </el-button>
<el-button link @click="openDetail(scope.row.id)" v-hasPermi="['bpm:form:query']"> <el-button v-hasPermi="['bpm:form:query']" link @click="openDetail(scope.row.id)">
详情 详情
</el-button> </el-button>
<el-button <el-button
v-hasPermi="['bpm:form:delete']"
link link
type="danger" type="danger"
@click="handleDelete(scope.row.id)" @click="handleDelete(scope.row.id)"
v-hasPermi="['bpm:form:delete']"
> >
删除 删除
</el-button> </el-button>
@ -70,27 +77,28 @@
</el-table> </el-table>
<!-- 分页 --> <!-- 分页 -->
<Pagination <Pagination
:total="total"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize" v-model:limit="queryParams.pageSize"
v-model:page="queryParams.pageNo"
:total="total"
@pagination="getList" @pagination="getList"
/> />
</ContentWrap> </ContentWrap>
<!-- 表单详情的弹窗 --> <!-- 表单详情的弹窗 -->
<Dialog title="表单详情" v-model="detailVisible" width="800"> <Dialog v-model="detailVisible" title="表单详情" width="800">
<form-create :rule="detailData.rule" :option="detailData.option" /> <form-create :option="detailData.option" :rule="detailData.rule" />
</Dialog> </Dialog>
</template> </template>
<script setup lang="ts" name="BpmForm"> <script lang="ts" name="BpmForm" setup>
import { DICT_TYPE } from '@/utils/dict' import { DICT_TYPE } from '@/utils/dict'
import { dateFormatter } from '@/utils/formatTime' import { dateFormatter } from '@/utils/formatTime'
import * as FormApi from '@/api/bpm/form' import * as FormApi from '@/api/bpm/form'
import { setConfAndFields2 } from '@/utils/formCreate' import { setConfAndFields2 } from '@/utils/formCreate'
const message = useMessage() // const message = useMessage() //
const { t } = useI18n() // const { t } = useI18n() //
const { push } = useRouter() // const { currentRoute, push } = useRouter() //
const loading = ref(true) // const loading = ref(true) //
const total = ref(0) // const total = ref(0) //
@ -128,12 +136,16 @@ const resetQuery = () => {
/** 添加/修改操作 */ /** 添加/修改操作 */
const openForm = (id?: number) => { const openForm = (id?: number) => {
push({ const toRouter: { name: string; query?: { id: number } } = {
name: 'BpmFormEditor', name: 'BpmFormEditor'
query: { }
// idevent
if (typeof id === 'number') {
toRouter.query = {
id id
} }
}) }
push(toRouter)
} }
/** 删除按钮操作 */ /** 删除按钮操作 */
@ -162,7 +174,16 @@ const openDetail = async (rowId: number) => {
// //
detailVisible.value = true detailVisible.value = true
} }
/**表单保存返回后重新加载列表 */
watch(
() => currentRoute.value,
() => {
getList()
},
{
immediate: true
}
)
/** 初始化 **/ /** 初始化 **/
onMounted(() => { onMounted(() => {
getList() getList()

View File

@ -1,17 +1,17 @@
<template> <template>
<Dialog :title="dialogTitle" v-model="dialogVisible"> <Dialog v-model="dialogVisible" :title="dialogTitle">
<el-form <el-form
ref="formRef" ref="formRef"
v-loading="formLoading"
:model="formData" :model="formData"
:rules="formRules" :rules="formRules"
label-width="100px" label-width="100px"
v-loading="formLoading"
> >
<el-form-item label="组名" prop="name"> <el-form-item label="组名" prop="name">
<el-input v-model="formData.name" placeholder="请输入组名" /> <el-input v-model="formData.name" placeholder="请输入组名" />
</el-form-item> </el-form-item>
<el-form-item label="描述"> <el-form-item label="描述">
<el-input type="textarea" v-model="formData.name" placeholder="请输入描述" /> <el-input v-model="formData.description" placeholder="请输入描述" type="textarea" />
</el-form-item> </el-form-item>
<el-form-item label="成员" prop="memberUserIds"> <el-form-item label="成员" prop="memberUserIds">
<el-select v-model="formData.memberUserIds" multiple placeholder="请选择成员"> <el-select v-model="formData.memberUserIds" multiple placeholder="请选择成员">
@ -36,12 +36,12 @@
</el-form-item> </el-form-item>
</el-form> </el-form>
<template #footer> <template #footer>
<el-button @click="submitForm" type="primary" :disabled="formLoading"> </el-button> <el-button :disabled="formLoading" type="primary" @click="submitForm"> </el-button>
<el-button @click="dialogVisible = false"> </el-button> <el-button @click="dialogVisible = false"> </el-button>
</template> </template>
</Dialog> </Dialog>
</template> </template>
<script setup lang="ts"> <script lang="ts" name="UserGroupForm" setup>
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict' import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
import { CommonStatusEnum } from '@/utils/constants' import { CommonStatusEnum } from '@/utils/constants'
import * as UserGroupApi from '@/api/bpm/userGroup' import * as UserGroupApi from '@/api/bpm/userGroup'
@ -68,7 +68,7 @@ const formRules = reactive({
status: [{ required: true, message: '状态不能为空', trigger: 'blur' }] status: [{ required: true, message: '状态不能为空', trigger: 'blur' }]
}) })
const formRef = ref() // Ref const formRef = ref() // Ref
const userList = ref([]) // const userList = ref<any[]>([]) //
/** 打开弹窗 */ /** 打开弹窗 */
const open = async (type: string, id?: number) => { const open = async (type: string, id?: number) => {

View File

@ -117,6 +117,7 @@ import { dateFormatter } from '@/utils/formatTime'
import * as UserGroupApi from '@/api/bpm/userGroup' import * as UserGroupApi from '@/api/bpm/userGroup'
import * as UserApi from '@/api/system/user' import * as UserApi from '@/api/system/user'
import UserGroupForm from './UserGroupForm.vue' import UserGroupForm from './UserGroupForm.vue'
import { UserVO } from '@/api/system/user'
const message = useMessage() // const message = useMessage() //
const { t } = useI18n() // const { t } = useI18n() //
@ -131,7 +132,7 @@ const queryParams = reactive({
createTime: [] createTime: []
}) })
const queryFormRef = ref() // const queryFormRef = ref() //
const userList = ref([]) // const userList = ref<UserVO[]>([]) //
/** 查询列表 */ /** 查询列表 */
const getList = async () => { const getList = async () => {

View File

@ -1,45 +1,45 @@
<template> <template>
<Dialog :title="dialogTitle" v-model="dialogVisible" width="600"> <Dialog v-model="dialogVisible" :title="dialogTitle" width="600">
<el-form <el-form
ref="formRef" ref="formRef"
v-loading="formLoading"
:model="formData" :model="formData"
:rules="formRules" :rules="formRules"
label-width="110px" label-width="110px"
v-loading="formLoading"
> >
<el-form-item label="流程标识" prop="key"> <el-form-item label="流程标识" prop="key">
<el-input <el-input
v-model="formData.key" v-model="formData.key"
:disabled="!!formData.id"
placeholder="请输入流标标识" placeholder="请输入流标标识"
style="width: 330px" style="width: 330px"
:disabled="!!formData.id"
/> />
<el-tooltip <el-tooltip
v-if="!formData.id" v-if="!formData.id"
class="item" class="item"
effect="light"
content="新建后,流程标识不可修改!" content="新建后,流程标识不可修改!"
effect="light"
placement="top" placement="top"
> >
<i style="padding-left: 5px" class="el-icon-question"></i> <i class="el-icon-question" style="padding-left: 5px"></i>
</el-tooltip> </el-tooltip>
<el-tooltip v-else class="item" effect="light" content="流程标识不可修改!" placement="top"> <el-tooltip v-else class="item" content="流程标识不可修改!" effect="light" placement="top">
<i style="padding-left: 5px" class="el-icon-question"></i> <i class="el-icon-question" style="padding-left: 5px"></i>
</el-tooltip> </el-tooltip>
</el-form-item> </el-form-item>
<el-form-item label="流程名称" prop="name"> <el-form-item label="流程名称" prop="name">
<el-input <el-input
v-model="formData.name" v-model="formData.name"
placeholder="请输入流程名称"
:disabled="!!formData.id" :disabled="!!formData.id"
clearable clearable
placeholder="请输入流程名称"
/> />
</el-form-item> </el-form-item>
<el-form-item v-if="formData.id" label="流程分类" prop="category"> <el-form-item v-if="formData.id" label="流程分类" prop="category">
<el-select <el-select
v-model="formData.category" v-model="formData.category"
placeholder="请选择流程分类"
clearable clearable
placeholder="请选择流程分类"
style="width: 100%" style="width: 100%"
> >
<el-option <el-option
@ -51,7 +51,7 @@
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="流程描述" prop="description"> <el-form-item label="流程描述" prop="description">
<el-input type="textarea" v-model="formData.description" clearable /> <el-input v-model="formData.description" clearable type="textarea" />
</el-form-item> </el-form-item>
<div v-if="formData.id"> <div v-if="formData.id">
<el-form-item label="表单类型" prop="formType"> <el-form-item label="表单类型" prop="formType">
@ -87,11 +87,11 @@
/> />
<el-tooltip <el-tooltip
class="item" class="item"
effect="light"
content="自定义表单的提交路径,使用 Vue 的路由地址例如说bpm/oa/leave/create" content="自定义表单的提交路径,使用 Vue 的路由地址例如说bpm/oa/leave/create"
effect="light"
placement="top" placement="top"
> >
<i style="padding-left: 5px" class="el-icon-question"></i> <i class="el-icon-question" style="padding-left: 5px"></i>
</el-tooltip> </el-tooltip>
</el-form-item> </el-form-item>
<el-form-item <el-form-item
@ -106,26 +106,27 @@
/> />
<el-tooltip <el-tooltip
class="item" class="item"
effect="light"
content="自定义表单的查看路径,使用 Vue 的路由地址例如说bpm/oa/leave/view" content="自定义表单的查看路径,使用 Vue 的路由地址例如说bpm/oa/leave/view"
effect="light"
placement="top" placement="top"
> >
<i style="padding-left: 5px" class="el-icon-question"></i> <i class="el-icon-question" style="padding-left: 5px"></i>
</el-tooltip> </el-tooltip>
</el-form-item> </el-form-item>
</div> </div>
</el-form> </el-form>
<template #footer> <template #footer>
<el-button @click="submitForm" type="primary" :disabled="formLoading"> </el-button> <el-button :disabled="formLoading" type="primary" @click="submitForm"> </el-button>
<el-button @click="dialogVisible = false"> </el-button> <el-button @click="dialogVisible = false"> </el-button>
</template> </template>
</Dialog> </Dialog>
</template> </template>
<script setup lang="ts"> <script lang="ts" name="ModelForm" setup>
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict' import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
import { ElMessageBox } from 'element-plus' import { ElMessageBox } from 'element-plus'
import * as ModelApi from '@/api/bpm/model' import * as ModelApi from '@/api/bpm/model'
import * as FormApi from '@/api/bpm/form' import * as FormApi from '@/api/bpm/form'
const { t } = useI18n() // const { t } = useI18n() //
const message = useMessage() // const message = useMessage() //

View File

@ -1,24 +1,24 @@
<template> <template>
<Dialog title="导入流程" v-model="dialogVisible" width="400"> <Dialog v-model="dialogVisible" title="导入流程" width="400">
<div> <div>
<el-upload <el-upload
ref="uploadRef" ref="uploadRef"
:action="importUrl"
:headers="uploadHeaders"
:data="formData"
name="bpmnFile"
v-model:file-list="fileList" v-model:file-list="fileList"
drag :action="importUrl"
:auto-upload="false" :auto-upload="false"
accept=".bpmn, .xml" :data="formData"
:disabled="formLoading"
:headers="uploadHeaders"
:limit="1" :limit="1"
:on-error="submitFormError"
:on-exceed="handleExceed" :on-exceed="handleExceed"
:on-success="submitFormSuccess" :on-success="submitFormSuccess"
:on-error="submitFormError" accept=".bpmn, .xml"
:disabled="formLoading" drag
name="bpmnFile"
> >
<Icon class="el-icon--upload" icon="ep:upload-filled" /> <Icon class="el-icon--upload" icon="ep:upload-filled" />
<div class="el-upload__text"> 将文件拖到此处 <em>点击上传</em> </div> <div class="el-upload__text"> 将文件拖到此处 <em>点击上传</em></div>
<template #tip> <template #tip>
<div class="el-upload__tip" style="color: red"> <div class="el-upload__tip" style="color: red">
提示仅允许导入bpmxml格式文件 提示仅允许导入bpmxml格式文件
@ -33,10 +33,10 @@
/> />
</el-form-item> </el-form-item>
<el-form-item label="流程名称" prop="name"> <el-form-item label="流程名称" prop="name">
<el-input v-model="formData.name" placeholder="请输入流程名称" clearable /> <el-input v-model="formData.name" clearable placeholder="请输入流程名称" />
</el-form-item> </el-form-item>
<el-form-item label="流程描述" prop="description"> <el-form-item label="流程描述" prop="description">
<el-input type="textarea" v-model="formData.description" clearable /> <el-input v-model="formData.description" clearable type="textarea" />
</el-form-item> </el-form-item>
</el-form> </el-form>
</div> </div>
@ -44,13 +44,14 @@
</el-upload> </el-upload>
</div> </div>
<template #footer> <template #footer>
<el-button @click="submitForm" type="primary" :disabled="formLoading"> </el-button> <el-button :disabled="formLoading" type="primary" @click="submitForm"> </el-button>
<el-button @click="dialogVisible = false"> </el-button> <el-button @click="dialogVisible = false"> </el-button>
</template> </template>
</Dialog> </Dialog>
</template> </template>
<script setup lang="ts"> <script lang="ts" name="ModelImportForm" setup>
import { getAccessToken, getTenantId } from '@/utils/auth' import { getAccessToken, getTenantId } from '@/utils/auth'
const message = useMessage() // const message = useMessage() //
const dialogVisible = ref(false) // const dialogVisible = ref(false) //

View File

@ -1,7 +1,7 @@
<template> <template>
<ContentWrap> <ContentWrap>
<!-- 流程设计器负责绘制流程等 --> <!-- 流程设计器负责绘制流程等 -->
<my-process-designer <MyProcessDesigner
key="designer" key="designer"
v-if="xmlString !== undefined" v-if="xmlString !== undefined"
v-model="xmlString" v-model="xmlString"
@ -14,9 +14,9 @@
@save="save" @save="save"
/> />
<!-- 流程属性器负责编辑每个流程节点的属性 --> <!-- 流程属性器负责编辑每个流程节点的属性 -->
<my-properties-panel <MyProcessPenal
key="penal" key="penal"
:bpmnModeler="modeler" :bpmnModeler="modeler as any"
:prefix="controlForm.prefix" :prefix="controlForm.prefix"
class="process-panel" class="process-panel"
:model="model" :model="model"
@ -25,6 +25,7 @@
</template> </template>
<script setup lang="ts" name="BpmModelEditor"> <script setup lang="ts" name="BpmModelEditor">
import { MyProcessDesigner, MyProcessPenal } from '@/components/bpmnProcessDesigner/package'
// //
import CustomContentPadProvider from '@/components/bpmnProcessDesigner/package/designer/plugins/content-pad' import CustomContentPadProvider from '@/components/bpmnProcessDesigner/package/designer/plugins/content-pad'
// //

View File

@ -216,10 +216,10 @@
<!-- 弹窗流程模型图的预览 --> <!-- 弹窗流程模型图的预览 -->
<Dialog title="流程图" v-model="bpmnDetailVisible" width="800"> <Dialog title="流程图" v-model="bpmnDetailVisible" width="800">
<my-process-viewer <MyProcessViewer
key="designer" key="designer"
v-model="bpmnXML" v-model="bpmnXML"
:value="bpmnXML" :value="bpmnXML as any"
v-bind="bpmnControlForm" v-bind="bpmnControlForm"
:prefix="bpmnControlForm.prefix" :prefix="bpmnControlForm.prefix"
/> />
@ -229,6 +229,7 @@
<script setup lang="ts" name="BpmModel"> <script setup lang="ts" name="BpmModel">
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict' import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
import { dateFormatter, formatDate } from '@/utils/formatTime' import { dateFormatter, formatDate } from '@/utils/formatTime'
import { MyProcessViewer } from '@/components/bpmnProcessDesigner/package'
import * as ModelApi from '@/api/bpm/model' import * as ModelApi from '@/api/bpm/model'
import * as FormApi from '@/api/bpm/form' import * as FormApi from '@/api/bpm/form'
import ModelForm from './ModelForm.vue' import ModelForm from './ModelForm.vue'

View File

@ -1,56 +1,55 @@
<template> <template>
<Dialog title="发起 OA 请假流程" v-model="dialogVisible"> <el-form
<el-form ref="formRef"
ref="formRef" :model="formData"
:model="formData" :rules="formRules"
:rules="formRules" label-width="80px"
label-width="80px" v-loading="formLoading"
v-loading="formLoading" >
> <el-form-item label="请假类型" prop="type">
<el-form-item label="请假类型" prop="type"> <el-select v-model="formData.type" placeholder="请选择请假类型" clearable>
<el-select v-model="formData.type" placeholder="请选择请假类型" clearable> <el-option
<el-option v-for="dict in getIntDictOptions(DICT_TYPE.BPM_OA_LEAVE_TYPE)"
v-for="dict in getIntDictOptions(DICT_TYPE.BPM_OA_LEAVE_TYPE)" :key="dict.value"
:key="dict.value" :label="dict.label"
:label="dict.label" :value="dict.value"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item label="开始时间" prop="startTime">
<el-date-picker
clearable
v-model="formData.startTime"
type="datetime"
value-format="x"
placeholder="请选择开始时间"
/> />
</el-form-item> </el-select>
<el-form-item label="结束时间" prop="endTime"> </el-form-item>
<el-date-picker <el-form-item label="开始时间" prop="startTime">
clearable <el-date-picker
v-model="formData.endTime" clearable
type="datetime" v-model="formData.startTime"
value-format="x" type="datetime"
placeholder="请选择结束时间" value-format="x"
/> placeholder="请选择开始时间"
</el-form-item> />
<el-form-item label="原因" prop="reason"> </el-form-item>
<el-input v-model="formData.reason" type="textarea" placeholder="请输请假原因" /> <el-form-item label="结束时间" prop="endTime">
</el-form-item> <el-date-picker
</el-form> clearable
<template #footer> v-model="formData.endTime"
type="datetime"
value-format="x"
placeholder="请选择结束时间"
/>
</el-form-item>
<el-form-item label="原因" prop="reason">
<el-input v-model="formData.reason" type="textarea" placeholder="请输请假原因" />
</el-form-item>
<el-form-item>
<el-button @click="submitForm" type="primary" :disabled="formLoading"> </el-button> <el-button @click="submitForm" type="primary" :disabled="formLoading"> </el-button>
<el-button @click="dialogVisible = false"> </el-button> </el-form-item>
</template> </el-form>
</Dialog>
</template> </template>
<script setup lang="ts"> <script setup name="BpmOALeaveCreate" lang="ts">
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict' import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
import * as LeaveApi from '@/api/bpm/leave' import * as LeaveApi from '@/api/bpm/leave'
import { useTagsViewStore } from '@/store/modules/tagsView'
const message = useMessage() // const message = useMessage() //
const { delView } = useTagsViewStore() //
const { currentRoute } = useRouter() //
const dialogVisible = ref(false) //
const formLoading = ref(false) // 12 const formLoading = ref(false) // 12
const formData = ref({ const formData = ref({
type: undefined, type: undefined,
@ -66,15 +65,7 @@ const formRules = reactive({
}) })
const formRef = ref() // Ref const formRef = ref() // Ref
/** 打开弹窗 */
const open = async () => {
dialogVisible.value = true
resetForm()
}
defineExpose({ open }) // open
/** 提交表单 */ /** 提交表单 */
const emit = defineEmits(['success']) // success
const submitForm = async () => { const submitForm = async () => {
// //
if (!formRef) return if (!formRef) return
@ -85,23 +76,11 @@ const submitForm = async () => {
try { try {
const data = formData.value as unknown as LeaveApi.LeaveVO const data = formData.value as unknown as LeaveApi.LeaveVO
await LeaveApi.createLeave(data) await LeaveApi.createLeave(data)
message.success('新增成功') message.success('发起成功')
dialogVisible.value = false // Tab
// delView(unref(currentRoute))
emit('success')
} finally { } finally {
formLoading.value = false formLoading.value = false
} }
} }
/** 重置表单 */
const resetForm = () => {
formData.value = {
type: undefined,
reason: undefined,
startTime: undefined,
endTime: undefined
}
formRef.value?.resetFields()
}
</script> </script>

View File

@ -1,40 +1,49 @@
<template> <template>
<Dialog title="详情" v-model="dialogVisible" :scroll="true" :max-height="200"> <ContentWrap>
<el-descriptions border :column="1"> <el-descriptions :column="1" border>
<el-descriptions-item label="请假类型"> <el-descriptions-item label="请假类型">
<dict-tag :type="DICT_TYPE.BPM_OA_LEAVE_TYPE" :value="detailData.type" /> <dict-tag :type="DICT_TYPE.BPM_OA_LEAVE_TYPE" :value="detailData.type" />
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="开始时间"> <el-descriptions-item label="开始时间">
{{ formatDate(detailData.startTime) }} {{ formatDate(detailData.startTime, 'YYYY-MM-DD') }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="结束时间"> <el-descriptions-item label="结束时间">
{{ formatDate(detailData.endTime) }} {{ formatDate(detailData.endTime, 'YYYY-MM-DD') }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="原因"> <el-descriptions-item label="原因">
{{ detailData.reason }} {{ detailData.reason }}
</el-descriptions-item> </el-descriptions-item>
</el-descriptions> </el-descriptions>
</Dialog> </ContentWrap>
</template> </template>
<script setup lang="ts"> <script lang="ts" name="BpmOALeaveDetail" setup>
import { DICT_TYPE } from '@/utils/dict' import { DICT_TYPE } from '@/utils/dict'
import { formatDate } from '@/utils/formatTime' import { formatDate } from '@/utils/formatTime'
import { propTypes } from '@/utils/propTypes'
import * as LeaveApi from '@/api/bpm/leave' import * as LeaveApi from '@/api/bpm/leave'
const dialogVisible = ref(false) // const { query } = useRoute() //
const detailLoading = ref(false) //
const detailData = ref() //
/** 打开弹窗 */ const props = defineProps({
const open = async (data: LeaveApi.LeaveVO) => { id: propTypes.number.def(undefined)
dialogVisible.value = true })
// const detailLoading = ref(false) //
const detailData = ref<any>({}) //
const queryId = query.id as unknown as number // URL id
/** 获得数据 */
const getInfo = async () => {
detailLoading.value = true detailLoading.value = true
try { try {
detailData.value = data detailData.value = await LeaveApi.getLeave(props.id || queryId)
} finally { } finally {
detailLoading.value = false detailLoading.value = false
} }
} }
defineExpose({ open }) // open defineExpose({ open: getInfo }) // open
/** 初始化 **/
onMounted(() => {
getInfo()
})
</script> </script>

View File

@ -137,19 +137,12 @@
@pagination="getList" @pagination="getList"
/> />
</ContentWrap> </ContentWrap>
<!-- 表单弹窗详情 -->
<LeaveDetail ref="detailRef" />
<!-- 表单弹窗添加 -->
<LeaveForm ref="formRef" @success="getList" />
</template> </template>
<script setup lang="ts" name="BpmOALeave"> <script setup lang="ts" name="BpmOALeave">
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict' import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
import { dateFormatter } from '@/utils/formatTime' import { dateFormatter } from '@/utils/formatTime'
import * as LeaveApi from '@/api/bpm/leave' import * as LeaveApi from '@/api/bpm/leave'
import * as ProcessInstanceApi from '@/api/bpm/processInstance' import * as ProcessInstanceApi from '@/api/bpm/processInstance'
import LeaveDetail from './detail.vue'
import LeaveForm from './create.vue'
const message = useMessage() // const message = useMessage() //
const router = useRouter() // const router = useRouter() //
const { t } = useI18n() // const { t } = useI18n() //
@ -192,15 +185,18 @@ const resetQuery = () => {
} }
/** 添加操作 */ /** 添加操作 */
const formRef = ref()
const handleCreate = () => { const handleCreate = () => {
formRef.value.open() router.push({ name: 'OALeaveCreate' })
} }
/** 详情操作 */ /** 详情操作 */
const detailRef = ref() const handleDetail = (row: LeaveApi.LeaveVO) => {
const handleDetail = (data: LeaveApi.LeaveVO) => { router.push({
detailRef.value.open(data) name: 'OALeaveDetail',
query: {
id: row.id
}
})
} }
/** 取消请假操作 */ /** 取消请假操作 */

View File

@ -43,7 +43,7 @@
</el-col> </el-col>
</el-card> </el-card>
<!-- 流程图预览 --> <!-- 流程图预览 -->
<ProcessInstanceBpmnViewer :bpmn-xml="bpmnXML" /> <ProcessInstanceBpmnViewer :bpmn-xml="bpmnXML as any" />
</ContentWrap> </ContentWrap>
</template> </template>
<script setup lang="ts" name="BpmProcessInstanceCreate"> <script setup lang="ts" name="BpmProcessInstanceCreate">

View File

@ -1,23 +1,23 @@
<template> <template>
<el-card class="box-card" v-loading="loading"> <el-card v-loading="loading" class="box-card">
<template #header> <template #header>
<span class="el-icon-picture-outline">流程图</span> <span class="el-icon-picture-outline">流程图</span>
</template> </template>
<my-process-viewer <MyProcessViewer
key="designer" key="designer"
:value="bpmnXml"
v-bind="bpmnControlForm"
:prefix="bpmnControlForm.prefix"
:activityData="activityList" :activityData="activityList"
:prefix="bpmnControlForm.prefix"
:processInstanceData="processInstance" :processInstanceData="processInstance"
:taskData="tasks" :taskData="tasks"
:value="bpmnXml"
v-bind="bpmnControlForm"
/> />
</el-card> </el-card>
</template> </template>
<script setup lang="ts"> <script lang="ts" name="BpmProcessInstanceBpmnViewer" setup>
import { propTypes } from '@/utils/propTypes' import { propTypes } from '@/utils/propTypes'
import { MyProcessViewer } from '@/components/bpmnProcessDesigner/package'
import * as ActivityApi from '@/api/bpm/activity' import * as ActivityApi from '@/api/bpm/activity'
// import * as DefinitionApi from '@/api/bpm/definition'
const props = defineProps({ const props = defineProps({
loading: propTypes.bool, // loading: propTypes.bool, //

View File

@ -1,9 +1,9 @@
<template> <template>
<el-card class="box-card" v-loading="loading"> <el-card v-loading="loading" class="box-card">
<template #header> <template #header>
<span class="el-icon-picture-outline">审批记录</span> <span class="el-icon-picture-outline">审批记录</span>
</template> </template>
<el-col :span="16" :offset="4"> <el-col :offset="4" :span="16">
<div class="block"> <div class="block">
<el-timeline> <el-timeline>
<el-timeline-item <el-timeline-item
@ -16,9 +16,9 @@
<el-card :body-style="{ padding: '10px' }"> <el-card :body-style="{ padding: '10px' }">
<label v-if="item.assigneeUser" style="font-weight: normal; margin-right: 30px"> <label v-if="item.assigneeUser" style="font-weight: normal; margin-right: 30px">
审批人{{ item.assigneeUser.nickname }} 审批人{{ item.assigneeUser.nickname }}
<el-tag type="info" size="small">{{ item.assigneeUser.deptName }}</el-tag> <el-tag size="small" type="info">{{ item.assigneeUser.deptName }}</el-tag>
</label> </label>
<label style="font-weight: normal" v-if="item.createTime"></label> <label v-if="item.createTime" style="font-weight: normal"></label>
<label style="color: #8a909c; font-weight: normal"> <label style="color: #8a909c; font-weight: normal">
{{ formatDate(item?.createTime) }} {{ formatDate(item?.createTime) }}
</label> </label>
@ -44,7 +44,7 @@
</el-col> </el-col>
</el-card> </el-card>
</template> </template>
<script setup lang="ts"> <script lang="ts" name="BpmProcessInstanceTaskList" setup>
import { formatDate, formatPast2 } from '@/utils/formatTime' import { formatDate, formatPast2 } from '@/utils/formatTime'
import { propTypes } from '@/utils/propTypes' import { propTypes } from '@/utils/propTypes'

View File

@ -1,11 +1,11 @@
<template> <template>
<Dialog title="转派审批人" v-model="dialogVisible" width="500"> <Dialog v-model="dialogVisible" title="转派审批人" width="500">
<el-form <el-form
ref="formRef" ref="formRef"
v-loading="formLoading"
:model="formData" :model="formData"
:rules="formRules" :rules="formRules"
label-width="110px" label-width="110px"
v-loading="formLoading"
> >
<el-form-item label="新审批人" prop="assigneeUserId"> <el-form-item label="新审批人" prop="assigneeUserId">
<el-select v-model="formData.assigneeUserId" clearable style="width: 100%"> <el-select v-model="formData.assigneeUserId" clearable style="width: 100%">
@ -19,12 +19,12 @@
</el-form-item> </el-form-item>
</el-form> </el-form>
<template #footer> <template #footer>
<el-button @click="submitForm" type="primary" :disabled="formLoading"> </el-button> <el-button :disabled="formLoading" type="primary" @click="submitForm"> </el-button>
<el-button @click="dialogVisible = false"> </el-button> <el-button @click="dialogVisible = false"> </el-button>
</template> </template>
</Dialog> </Dialog>
</template> </template>
<script setup lang="ts"> <script lang="ts" name="BpmTaskUpdateAssigneeForm" setup>
import * as TaskApi from '@/api/bpm/task' import * as TaskApi from '@/api/bpm/task'
import * as UserApi from '@/api/system/user' import * as UserApi from '@/api/system/user'

View File

@ -2,81 +2,78 @@
<ContentWrap> <ContentWrap>
<!-- 审批信息 --> <!-- 审批信息 -->
<el-card <el-card
class="box-card"
v-loading="processInstanceLoading"
v-for="(item, index) in runningTasks" v-for="(item, index) in runningTasks"
:key="index" :key="index"
v-loading="processInstanceLoading"
class="box-card"
> >
<template #header> <template #header>
<span class="el-icon-picture-outline">审批任务{{ item.name }}</span> <span class="el-icon-picture-outline">审批任务{{ item.name }}</span>
</template> </template>
<el-col :span="16" :offset="6"> <el-col :offset="6" :span="16">
<el-form <el-form
:ref="'form' + index" :ref="'form' + index"
:model="auditForms[index]" :model="auditForms[index]"
:rules="auditRule" :rules="auditRule"
label-width="100px" label-width="100px"
> >
<el-form-item label="流程名" v-if="processInstance && processInstance.name"> <el-form-item v-if="processInstance && processInstance.name" label="流程名">
{{ processInstance.name }} {{ processInstance.name }}
</el-form-item> </el-form-item>
<el-form-item label="流程发起人" v-if="processInstance && processInstance.startUser"> <el-form-item v-if="processInstance && processInstance.startUser" label="流程发起人">
{{ processInstance.startUser.nickname }} {{ processInstance.startUser.nickname }}
<el-tag type="info" size="small">{{ processInstance.startUser.deptName }}</el-tag> <el-tag size="small" type="info">{{ processInstance.startUser.deptName }}</el-tag>
</el-form-item> </el-form-item>
<el-form-item label="审批建议" prop="reason"> <el-form-item label="审批建议" prop="reason">
<el-input <el-input
type="textarea"
v-model="auditForms[index].reason" v-model="auditForms[index].reason"
placeholder="请输入审批建议" placeholder="请输入审批建议"
type="textarea"
/> />
</el-form-item> </el-form-item>
</el-form> </el-form>
<div style="margin-left: 10%; margin-bottom: 20px; font-size: 14px"> <div style="margin-left: 10%; margin-bottom: 20px; font-size: 14px">
<el-button type="success" @click="handleAudit(item, true)"> <el-button type="success" @click="handleAudit(item, true)">
<Icon icon="ep:select" /> 通过 <Icon icon="ep:select" />
通过
</el-button> </el-button>
<el-button type="danger" @click="handleAudit(item, false)"> <el-button type="danger" @click="handleAudit(item, false)">
<Icon icon="ep:close" /> 不通过 <Icon icon="ep:close" />
不通过
</el-button> </el-button>
<el-button type="primary" @click="openTaskUpdateAssigneeForm(item.id)"> <el-button type="primary" @click="openTaskUpdateAssigneeForm(item.id)">
<Icon icon="ep:edit" /> 转办 <Icon icon="ep:edit" />
转办
</el-button> </el-button>
<el-button type="primary" @click="handleDelegate(item)"> <el-button type="primary" @click="handleDelegate(item)">
<Icon icon="ep:position" /> 委派 <Icon icon="ep:position" />
委派
</el-button> </el-button>
<el-button type="warning" @click="handleBack(item)"> <el-button type="warning" @click="handleBack(item)">
<Icon icon="ep:back" /> 回退 <Icon icon="ep:back" />
回退
</el-button> </el-button>
</div> </div>
</el-col> </el-col>
</el-card> </el-card>
<!-- 申请信息 --> <!-- 申请信息 -->
<el-card class="box-card" v-loading="processInstanceLoading"> <el-card v-loading="processInstanceLoading" class="box-card">
<template #header> <template #header>
<span class="el-icon-document">申请信息{{ processInstance.name }}</span> <span class="el-icon-document">申请信息{{ processInstance.name }}</span>
</template> </template>
<!-- 情况一流程表单 --> <!-- 情况一流程表单 -->
<el-col v-if="processInstance?.processDefinition?.formType === 10" :span="16" :offset="6"> <el-col v-if="processInstance?.processDefinition?.formType === 10" :offset="6" :span="16">
<form-create <form-create
ref="fApi" ref="fApi"
:rule="detailForm.rule"
:option="detailForm.option"
v-model="detailForm.value" v-model="detailForm.value"
:option="detailForm.option"
:rule="detailForm.rule"
/> />
</el-col> </el-col>
<!-- 情况二流程表单 --> <!-- 情况二业务表单 -->
<div v-if="processInstance?.processDefinition?.formType === 20"> <div v-if="processInstance?.processDefinition?.formType === 20">
<router-link <BusinessFormComponent :id="processInstance.businessKey" />
:to="
processInstance.processDefinition.formCustomViewPath +
'?id=' +
processInstance.businessKey
"
>
<el-button type="primary"><Icon icon="ep:view" /> 点击查看</el-button>
</router-link>
</div> </div>
</el-card> </el-card>
@ -85,18 +82,18 @@
<!-- 高亮流程图 --> <!-- 高亮流程图 -->
<ProcessInstanceBpmnViewer <ProcessInstanceBpmnViewer
:id="id" :id="`${id}`"
:process-instance="processInstance"
:loading="processInstanceLoading"
:tasks="tasks"
:bpmn-xml="bpmnXML" :bpmn-xml="bpmnXML"
:loading="processInstanceLoading"
:process-instance="processInstance"
:tasks="tasks"
/> />
<!-- 弹窗转派审批人 --> <!-- 弹窗转派审批人 -->
<TaskUpdateAssigneeForm ref="taskUpdateAssigneeFormRef" @success="getDetail" /> <TaskUpdateAssigneeForm ref="taskUpdateAssigneeFormRef" @success="getDetail" />
</ContentWrap> </ContentWrap>
</template> </template>
<script setup lang="ts" name="BpmProcessInstanceDetail"> <script lang="ts" name="BpmProcessInstanceDetail" setup>
import { useUserStore } from '@/store/modules/user' import { useUserStore } from '@/store/modules/user'
import { setConfAndFields2 } from '@/utils/formCreate' import { setConfAndFields2 } from '@/utils/formCreate'
import type { ApiAttrs } from '@form-create/element-ui/types/config' import type { ApiAttrs } from '@form-create/element-ui/types/config'
@ -106,6 +103,8 @@ import * as TaskApi from '@/api/bpm/task'
import TaskUpdateAssigneeForm from './TaskUpdateAssigneeForm.vue' import TaskUpdateAssigneeForm from './TaskUpdateAssigneeForm.vue'
import ProcessInstanceBpmnViewer from './ProcessInstanceBpmnViewer.vue' import ProcessInstanceBpmnViewer from './ProcessInstanceBpmnViewer.vue'
import ProcessInstanceTaskList from './ProcessInstanceTaskList.vue' import ProcessInstanceTaskList from './ProcessInstanceTaskList.vue'
import { registerComponent } from '@/utils/routerHelper'
const { query } = useRoute() // const { query } = useRoute() //
const message = useMessage() // const message = useMessage() //
const { proxy } = getCurrentInstance() as any const { proxy } = getCurrentInstance() as any
@ -186,6 +185,7 @@ const getDetail = () => {
} }
/** 加载流程实例 */ /** 加载流程实例 */
const BusinessFormComponent = ref(null) //
const getProcessInstance = async () => { const getProcessInstance = async () => {
try { try {
processInstanceLoading.value = true processInstanceLoading.value = true
@ -210,10 +210,12 @@ const getProcessInstance = async () => {
fApi.value?.fapi?.resetBtn.show(false) fApi.value?.fapi?.resetBtn.show(false)
fApi.value?.fapi?.disabled(true) fApi.value?.fapi?.disabled(true)
}) })
} else {
BusinessFormComponent.value = registerComponent(data.processDefinition.formCustomViewPath)
} }
// //
bpmnXML.value = await DefinitionApi.getProcessDefinitionBpmnXML(processDefinition.id) bpmnXML.value = await DefinitionApi.getProcessDefinitionBpmnXML(processDefinition.id as number)
} finally { } finally {
processInstanceLoading.value = false processInstanceLoading.value = false
} }

View File

@ -1,6 +1,6 @@
<template> <template>
<Dialog title="详情" v-model="dialogVisible" :scroll="true" :max-height="500"> <Dialog v-model="dialogVisible" :max-height="500" :scroll="true" title="详情">
<el-descriptions border :column="1"> <el-descriptions :column="1" border>
<el-descriptions-item label="任务编号" min-width="120"> <el-descriptions-item label="任务编号" min-width="120">
{{ detailData.id }} {{ detailData.id }}
</el-descriptions-item> </el-descriptions-item>
@ -25,7 +25,7 @@
</el-descriptions> </el-descriptions>
</Dialog> </Dialog>
</template> </template>
<script setup lang="ts"> <script lang="ts" name="BpmTaskDetail" setup>
import { DICT_TYPE } from '@/utils/dict' import { DICT_TYPE } from '@/utils/dict'
import { formatDate } from '@/utils/formatTime' import { formatDate } from '@/utils/formatTime'
import * as TaskApi from '@/api/bpm/task' import * as TaskApi from '@/api/bpm/task'

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