Commit 79d55aa3e91242c30bfcb9e90b3acf95e3df0288
Committed by
Gitee
!245 优化表单校验和查询参数,增加 vue3 简单使用说明
Merge pull request !245 from xingyu/master
Showing
30 changed files
with
310 additions
and
96 deletions
README.md
| ... | ... | @@ -192,14 +192,14 @@ ps:核心功能已经实现,正在对接微信小程序中... |
| 192 | 192 | | [JUnit](https://junit.org/junit5/) | Java 单元测试框架 | 5.8.2 | - | |
| 193 | 193 | | [Mockito](https://github.com/mockito/mockito) | Java Mock 框架 | 4.0.0 | - | |
| 194 | 194 | |
| 195 | -### Vue2 前端 | |
| 195 | +### [Vue2 前端](./yudao-ui-admin) | |
| 196 | 196 | |
| 197 | 197 | | 框架 | 说明 | 版本 | |
| 198 | 198 | |------------------------------------------------------------------------------|---------------|--------| |
| 199 | 199 | | [Vue](https://cn.vuejs.org/index.html) | JavaScript 框架 | 2.6.12 | |
| 200 | 200 | | [Vue Element Admin](https://panjiachen.github.io/vue-element-admin-site/zh/) | 后台前端解决方案 | - | |
| 201 | 201 | |
| 202 | -### Vue3 前端 | |
| 202 | +### [Vue3 前端](./yudao-ui-admin-vue3) | |
| 203 | 203 | |
| 204 | 204 | | 框架 | 说明 | 版本 | |
| 205 | 205 | |----------------------------------------------------------------------|------------------|--------| | ... | ... |
yudao-ui-admin-vue3/README.md
yudao-ui-admin-vue3/package.json
| ... | ... | @@ -65,8 +65,8 @@ |
| 65 | 65 | "@types/nprogress": "^0.2.0", |
| 66 | 66 | "@types/qrcode": "^1.4.2", |
| 67 | 67 | "@types/qs": "^6.9.7", |
| 68 | - "@typescript-eslint/eslint-plugin": "^5.31.0", | |
| 69 | - "@typescript-eslint/parser": "^5.31.0", | |
| 68 | + "@typescript-eslint/eslint-plugin": "^5.32.0", | |
| 69 | + "@typescript-eslint/parser": "^5.32.0", | |
| 70 | 70 | "@vitejs/plugin-vue": "^3.0.1", |
| 71 | 71 | "@vitejs/plugin-vue-jsx": "^2.0.0", |
| 72 | 72 | "autoprefixer": "^10.4.8", |
| ... | ... | @@ -91,7 +91,7 @@ |
| 91 | 91 | "stylelint-config-standard": "^26.0.0", |
| 92 | 92 | "stylelint-order": "^5.0.0", |
| 93 | 93 | "typescript": "4.7.4", |
| 94 | - "unplugin-vue-define-options": "^0.6.2", | |
| 94 | + "unplugin-vue-define-options": "^0.7.1", | |
| 95 | 95 | "vite": "3.0.4", |
| 96 | 96 | "vite-plugin-compression": "^0.5.1", |
| 97 | 97 | "vite-plugin-eslint": "^1.7.0", | ... | ... |
yudao-ui-admin-vue3/src/api/system/dept/types.ts
yudao-ui-admin-vue3/src/config/axios/index.ts
| 1 | 1 | import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse, AxiosError } from 'axios' |
| 2 | -import { ElMessage, ElMessageBox, ElNotification } from 'element-plus' | |
| 2 | +import { ElMessage, ElNotification } from 'element-plus' | |
| 3 | 3 | import qs from 'qs' |
| 4 | 4 | import { config } from '@/config/axios/config' |
| 5 | -import { getAccessToken, getRefreshToken, getTenantId } from '@/utils/auth' | |
| 5 | +import { getAccessToken, getTenantId, removeToken } from '@/utils/auth' | |
| 6 | 6 | import errorCode from './errorCode' |
| 7 | 7 | import { useI18n } from '@/hooks/web/useI18n' |
| 8 | 8 | |
| ... | ... | @@ -22,7 +22,7 @@ export const isRelogin = { show: false } |
| 22 | 22 | // 请求队列 |
| 23 | 23 | // const requestList = [] |
| 24 | 24 | // 是否正在刷新中 |
| 25 | -let isRefreshToken = false | |
| 25 | +// const isRefreshToken = false | |
| 26 | 26 | |
| 27 | 27 | export const PATH_URL = base_url[import.meta.env.VITE_API_BASEPATH] |
| 28 | 28 | |
| ... | ... | @@ -55,18 +55,26 @@ service.interceptors.request.use( |
| 55 | 55 | config.data = qs.stringify(data) |
| 56 | 56 | } |
| 57 | 57 | // get参数编码 |
| 58 | - if (config.method?.toUpperCase() === 'GET' && config.params) { | |
| 59 | - let url = config.url as string | |
| 58 | + if (config.method?.toUpperCase() === 'GET' && params) { | |
| 59 | + let url = config.url + '?' | |
| 60 | + for (const propName of Object.keys(params)) { | |
| 61 | + const value = params[propName] | |
| 62 | + if (value !== void 0 && value !== null && typeof value !== 'undefined') { | |
| 63 | + if (typeof value === 'object') { | |
| 64 | + for (const val of Object.keys(value)) { | |
| 65 | + const params = propName + '[' + val + ']' | |
| 66 | + const subPart = encodeURIComponent(params) + '=' | |
| 67 | + url += subPart + encodeURIComponent(value[val]) + '&' | |
| 68 | + } | |
| 69 | + } else { | |
| 70 | + url += `${propName}=${encodeURIComponent(value)}&` | |
| 71 | + } | |
| 72 | + } | |
| 73 | + } | |
| 60 | 74 | // 给 get 请求加上时间戳参数,避免从缓存中拿数据 |
| 61 | 75 | // const now = new Date().getTime() |
| 62 | 76 | // params = params.substring(0, url.length - 1) + `?_t=${now}` |
| 63 | - url += '?' | |
| 64 | - const keys = Object.keys(params) | |
| 65 | - for (const key of keys) { | |
| 66 | - if (params[key] !== void 0 && params[key] !== null) { | |
| 67 | - url += `${key}=${encodeURIComponent(params[key])}&` | |
| 68 | - } | |
| 69 | - } | |
| 77 | + url = url.slice(0, -1) | |
| 70 | 78 | config.params = {} |
| 71 | 79 | config.url = url |
| 72 | 80 | } |
| ... | ... | @@ -90,6 +98,13 @@ service.interceptors.response.use( |
| 90 | 98 | const { t } = useI18n() |
| 91 | 99 | // 未设置状态码则默认成功状态 |
| 92 | 100 | const code = data.code || result_code |
| 101 | + // 二进制数据则直接返回 | |
| 102 | + if ( | |
| 103 | + response.request.responseType === 'blob' || | |
| 104 | + response.request.responseType === 'arraybuffer' | |
| 105 | + ) { | |
| 106 | + return response.data | |
| 107 | + } | |
| 93 | 108 | // 获取错误信息 |
| 94 | 109 | const msg = data.msg || errorCode[code] || errorCode['default'] |
| 95 | 110 | if (ignoreMsgs.indexOf(msg) !== -1) { |
| ... | ... | @@ -97,15 +112,16 @@ service.interceptors.response.use( |
| 97 | 112 | return Promise.reject(msg) |
| 98 | 113 | } else if (code === 401) { |
| 99 | 114 | // 如果未认证,并且未进行刷新令牌,说明可能是访问令牌过期了 |
| 100 | - if (!isRefreshToken) { | |
| 101 | - isRefreshToken = true | |
| 102 | - // 1. 如果获取不到刷新令牌,则只能执行登出操作 | |
| 103 | - if (!getRefreshToken()) { | |
| 104 | - return handleAuthorized() | |
| 105 | - } | |
| 106 | - // 2. 进行刷新访问令牌 | |
| 107 | - // TODO: 引入refreshToken会循环依赖报错 | |
| 108 | - } | |
| 115 | + return handleAuthorized() | |
| 116 | + // if (!isRefreshToken) { | |
| 117 | + // isRefreshToken = true | |
| 118 | + // // 1. 如果获取不到刷新令牌,则只能执行登出操作 | |
| 119 | + // if (!getRefreshToken()) { | |
| 120 | + // return handleAuthorized() | |
| 121 | + // } | |
| 122 | + // // 2. 进行刷新访问令牌 | |
| 123 | + // // TODO: 引入refreshToken会循环依赖报错 | |
| 124 | + // } | |
| 109 | 125 | } else if (code === 500) { |
| 110 | 126 | ElMessage.error(t('sys.api.errMsg500')) |
| 111 | 127 | return Promise.reject(new Error(msg)) |
| ... | ... | @@ -149,21 +165,12 @@ service.interceptors.response.use( |
| 149 | 165 | return Promise.reject(error) |
| 150 | 166 | } |
| 151 | 167 | ) |
| 152 | -function handleAuthorized() { | |
| 168 | +const handleAuthorized = () => { | |
| 153 | 169 | const { t } = useI18n() |
| 154 | 170 | if (!isRelogin.show) { |
| 171 | + removeToken() | |
| 155 | 172 | isRelogin.show = true |
| 156 | - ElMessageBox.confirm(t('sys.api.timeoutMessage'), t('common.confirmTitle'), { | |
| 157 | - confirmButtonText: t('login.relogin'), | |
| 158 | - cancelButtonText: t('common.cancel'), | |
| 159 | - type: 'warning' | |
| 160 | - }) | |
| 161 | - .then(() => { | |
| 162 | - isRelogin.show = false | |
| 163 | - }) | |
| 164 | - .catch(() => { | |
| 165 | - isRelogin.show = false | |
| 166 | - }) | |
| 173 | + ElNotification.error(t('sys.api.timeoutMessage')) | |
| 167 | 174 | } |
| 168 | 175 | return Promise.reject(t('sys.api.timeoutMessage')) |
| 169 | 176 | } | ... | ... |
yudao-ui-admin-vue3/src/utils/tree.ts
| ... | ... | @@ -213,7 +213,7 @@ export const eachTree = (treeDatas: any[], callBack: Fn, parentNode = {}) => { |
| 213 | 213 | * @param {*} parentId 父节点字段 默认 'parentId' |
| 214 | 214 | * @param {*} children 孩子节点字段 默认 'children' |
| 215 | 215 | */ |
| 216 | -export const handleTree = (data, id?: string, parentId?: string, children?: string) => { | |
| 216 | +export const handleTree = (data: any[], id?: string, parentId?: string, children?: string) => { | |
| 217 | 217 | const config = { |
| 218 | 218 | id: id || 'id', |
| 219 | 219 | parentId: parentId || 'parentId', |
| ... | ... | @@ -222,7 +222,7 @@ export const handleTree = (data, id?: string, parentId?: string, children?: stri |
| 222 | 222 | |
| 223 | 223 | const childrenListMap = {} |
| 224 | 224 | const nodeIds = {} |
| 225 | - const tree = [] | |
| 225 | + const tree: any[] = [] | |
| 226 | 226 | |
| 227 | 227 | for (const d of data) { |
| 228 | 228 | const parentId = d[config.parentId] | ... | ... |
yudao-ui-admin-vue3/src/views/infra/apiAccessLog/apiAccessLog.data.ts
| ... | ... | @@ -50,7 +50,16 @@ const crudSchemas = reactive<CrudSchema[]>([ |
| 50 | 50 | }, |
| 51 | 51 | { |
| 52 | 52 | label: '请求时间', |
| 53 | - field: 'beginTime' | |
| 53 | + field: 'beginTime', | |
| 54 | + search: { | |
| 55 | + show: true, | |
| 56 | + component: 'DatePicker', | |
| 57 | + componentProps: { | |
| 58 | + type: 'datetimerange', | |
| 59 | + valueFormat: 'YYYY-MM-DD HH:mm:ss', | |
| 60 | + defaultTime: [new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 2, 1, 23, 59, 59)] | |
| 61 | + } | |
| 62 | + } | |
| 54 | 63 | }, |
| 55 | 64 | { |
| 56 | 65 | label: '执行时长', | ... | ... |
yudao-ui-admin-vue3/src/views/infra/apiErrorLog/apiErrorLog.data.ts
| ... | ... | @@ -50,7 +50,16 @@ const crudSchemas = reactive<CrudSchema[]>([ |
| 50 | 50 | }, |
| 51 | 51 | { |
| 52 | 52 | label: '异常发生时间', |
| 53 | - field: 'exceptionTime' | |
| 53 | + field: 'exceptionTime', | |
| 54 | + search: { | |
| 55 | + show: true, | |
| 56 | + component: 'DatePicker', | |
| 57 | + componentProps: { | |
| 58 | + type: 'datetimerange', | |
| 59 | + valueFormat: 'YYYY-MM-DD HH:mm:ss', | |
| 60 | + defaultTime: [new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 2, 1, 23, 59, 59)] | |
| 61 | + } | |
| 62 | + } | |
| 54 | 63 | }, |
| 55 | 64 | { |
| 56 | 65 | label: '异常名', | ... | ... |
yudao-ui-admin-vue3/src/views/infra/codegen/codegen.data.ts
| ... | ... | @@ -50,6 +50,15 @@ const crudSchemas = reactive<CrudSchema[]>([ |
| 50 | 50 | field: 'createTime', |
| 51 | 51 | form: { |
| 52 | 52 | show: false |
| 53 | + }, | |
| 54 | + search: { | |
| 55 | + show: true, | |
| 56 | + component: 'DatePicker', | |
| 57 | + componentProps: { | |
| 58 | + type: 'datetimerange', | |
| 59 | + valueFormat: 'YYYY-MM-DD HH:mm:ss', | |
| 60 | + defaultTime: [new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 2, 1, 23, 59, 59)] | |
| 61 | + } | |
| 53 | 62 | } |
| 54 | 63 | }, |
| 55 | 64 | { | ... | ... |
yudao-ui-admin-vue3/src/views/infra/config/config.data.ts
| ... | ... | @@ -91,6 +91,15 @@ const crudSchemas = reactive<CrudSchema[]>([ |
| 91 | 91 | field: 'createTime', |
| 92 | 92 | form: { |
| 93 | 93 | show: false |
| 94 | + }, | |
| 95 | + search: { | |
| 96 | + show: true, | |
| 97 | + component: 'DatePicker', | |
| 98 | + componentProps: { | |
| 99 | + type: 'datetimerange', | |
| 100 | + valueFormat: 'YYYY-MM-DD HH:mm:ss', | |
| 101 | + defaultTime: [new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 2, 1, 23, 59, 59)] | |
| 102 | + } | |
| 94 | 103 | } |
| 95 | 104 | }, |
| 96 | 105 | { | ... | ... |
yudao-ui-admin-vue3/src/views/infra/dataSourceConfig/dataSourceConfig.data.ts
| ... | ... | @@ -58,6 +58,15 @@ const crudSchemas = reactive<CrudSchema[]>([ |
| 58 | 58 | field: 'createTime', |
| 59 | 59 | form: { |
| 60 | 60 | show: false |
| 61 | + }, | |
| 62 | + search: { | |
| 63 | + show: true, | |
| 64 | + component: 'DatePicker', | |
| 65 | + componentProps: { | |
| 66 | + type: 'datetimerange', | |
| 67 | + valueFormat: 'YYYY-MM-DD HH:mm:ss', | |
| 68 | + defaultTime: [new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 2, 1, 23, 59, 59)] | |
| 69 | + } | |
| 61 | 70 | } |
| 62 | 71 | }, |
| 63 | 72 | { | ... | ... |
yudao-ui-admin-vue3/src/views/infra/fileConfig/fileConfig.data.ts
| ... | ... | @@ -76,6 +76,15 @@ const crudSchemas = reactive<CrudSchema[]>([ |
| 76 | 76 | field: 'createTime', |
| 77 | 77 | form: { |
| 78 | 78 | show: false |
| 79 | + }, | |
| 80 | + search: { | |
| 81 | + show: true, | |
| 82 | + component: 'DatePicker', | |
| 83 | + componentProps: { | |
| 84 | + type: 'datetimerange', | |
| 85 | + valueFormat: 'YYYY-MM-DD HH:mm:ss', | |
| 86 | + defaultTime: [new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 2, 1, 23, 59, 59)] | |
| 87 | + } | |
| 79 | 88 | } |
| 80 | 89 | }, |
| 81 | 90 | { | ... | ... |
yudao-ui-admin-vue3/src/views/infra/fileList/fileList.data.ts
| ... | ... | @@ -36,6 +36,15 @@ const crudSchemas = reactive<CrudSchema[]>([ |
| 36 | 36 | field: 'createTime', |
| 37 | 37 | form: { |
| 38 | 38 | show: false |
| 39 | + }, | |
| 40 | + search: { | |
| 41 | + show: true, | |
| 42 | + component: 'DatePicker', | |
| 43 | + componentProps: { | |
| 44 | + type: 'datetimerange', | |
| 45 | + valueFormat: 'YYYY-MM-DD HH:mm:ss', | |
| 46 | + defaultTime: [new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 2, 1, 23, 59, 59)] | |
| 47 | + } | |
| 39 | 48 | } |
| 40 | 49 | }, |
| 41 | 50 | { | ... | ... |
yudao-ui-admin-vue3/src/views/pay/app/app.data.ts
| ... | ... | @@ -8,9 +8,10 @@ const { t } = useI18n() // 国际化 |
| 8 | 8 | // 表单校验 |
| 9 | 9 | export const rules = reactive({ |
| 10 | 10 | name: [required], |
| 11 | - code: [required], | |
| 12 | - sort: [required], | |
| 13 | - status: [required] | |
| 11 | + status: [required], | |
| 12 | + payNotifyUrl: [required], | |
| 13 | + refundNotifyUrl: [required], | |
| 14 | + merchantId: [required] | |
| 14 | 15 | }) |
| 15 | 16 | |
| 16 | 17 | // CrudSchema |
| ... | ... | @@ -53,6 +54,15 @@ const crudSchemas = reactive<CrudSchema[]>([ |
| 53 | 54 | field: 'createTime', |
| 54 | 55 | form: { |
| 55 | 56 | show: false |
| 57 | + }, | |
| 58 | + search: { | |
| 59 | + show: true, | |
| 60 | + component: 'DatePicker', | |
| 61 | + componentProps: { | |
| 62 | + type: 'datetimerange', | |
| 63 | + valueFormat: 'YYYY-MM-DD HH:mm:ss', | |
| 64 | + defaultTime: [new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 2, 1, 23, 59, 59)] | |
| 65 | + } | |
| 56 | 66 | } |
| 57 | 67 | }, |
| 58 | 68 | { | ... | ... |
yudao-ui-admin-vue3/src/views/pay/merchant/merchant.data.ts
| ... | ... | @@ -7,9 +7,9 @@ const { t } = useI18n() // 国际化 |
| 7 | 7 | |
| 8 | 8 | // 表单校验 |
| 9 | 9 | export const rules = reactive({ |
| 10 | + no: [required], | |
| 10 | 11 | name: [required], |
| 11 | - code: [required], | |
| 12 | - sort: [required], | |
| 12 | + shortName: [required], | |
| 13 | 13 | status: [required] |
| 14 | 14 | }) |
| 15 | 15 | |
| ... | ... | @@ -77,6 +77,15 @@ const crudSchemas = reactive<CrudSchema[]>([ |
| 77 | 77 | field: 'createTime', |
| 78 | 78 | form: { |
| 79 | 79 | show: false |
| 80 | + }, | |
| 81 | + search: { | |
| 82 | + show: true, | |
| 83 | + component: 'DatePicker', | |
| 84 | + componentProps: { | |
| 85 | + type: 'datetimerange', | |
| 86 | + valueFormat: 'YYYY-MM-DD HH:mm:ss', | |
| 87 | + defaultTime: [new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 2, 1, 23, 59, 59)] | |
| 88 | + } | |
| 80 | 89 | } |
| 81 | 90 | }, |
| 82 | 91 | { | ... | ... |
yudao-ui-admin-vue3/src/views/pay/order/order.data.ts
| ... | ... | @@ -158,6 +158,22 @@ const crudSchemas = reactive<CrudSchema[]>([ |
| 158 | 158 | field: 'channelOrderNo' |
| 159 | 159 | }, |
| 160 | 160 | { |
| 161 | + label: t('common.createTime'), | |
| 162 | + field: 'createTime', | |
| 163 | + form: { | |
| 164 | + show: false | |
| 165 | + }, | |
| 166 | + search: { | |
| 167 | + show: true, | |
| 168 | + component: 'DatePicker', | |
| 169 | + componentProps: { | |
| 170 | + type: 'datetimerange', | |
| 171 | + valueFormat: 'YYYY-MM-DD HH:mm:ss', | |
| 172 | + defaultTime: [new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 2, 1, 23, 59, 59)] | |
| 173 | + } | |
| 174 | + } | |
| 175 | + }, | |
| 176 | + { | |
| 161 | 177 | label: t('table.action'), |
| 162 | 178 | field: 'action', |
| 163 | 179 | width: '270px', | ... | ... |
yudao-ui-admin-vue3/src/views/pay/refund/refund.data.ts
| ... | ... | @@ -84,6 +84,15 @@ const crudSchemas = reactive<CrudSchema[]>([ |
| 84 | 84 | field: 'createTime', |
| 85 | 85 | form: { |
| 86 | 86 | show: false |
| 87 | + }, | |
| 88 | + search: { | |
| 89 | + show: true, | |
| 90 | + component: 'DatePicker', | |
| 91 | + componentProps: { | |
| 92 | + type: 'datetimerange', | |
| 93 | + valueFormat: 'YYYY-MM-DD HH:mm:ss', | |
| 94 | + defaultTime: [new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 2, 1, 23, 59, 59)] | |
| 95 | + } | |
| 87 | 96 | } |
| 88 | 97 | }, |
| 89 | 98 | { | ... | ... |
yudao-ui-admin-vue3/src/views/system/dept/dept.data.ts
| 1 | 1 | import { required } from '@/utils/formRules' |
| 2 | 2 | import { reactive } from 'vue' |
| 3 | +// 表单校验 | |
| 4 | +export const rules = reactive({ | |
| 5 | + name: [required], | |
| 6 | + sort: [required], | |
| 7 | + email: [required], | |
| 8 | + phone: [ | |
| 9 | + { | |
| 10 | + pattern: | |
| 11 | + /^(?:(?:\+|00)86)?1(?:(?:3[\d])|(?:4[5-79])|(?:5[0-35-9])|(?:6[5-7])|(?:7[0-8])|(?:8[\d])|(?:9[189]))\d{8}$/, | |
| 12 | + trigger: 'blur', | |
| 13 | + message: '请输入正确的手机号码' | |
| 14 | + } | |
| 15 | + ] | |
| 16 | +}) | |
| 3 | 17 | |
| 4 | 18 | export const modelSchema = reactive<FormSchema[]>([ |
| 5 | 19 | { |
| ... | ... | @@ -17,7 +31,7 @@ export const modelSchema = reactive<FormSchema[]>([ |
| 17 | 31 | }, |
| 18 | 32 | { |
| 19 | 33 | label: '负责人', |
| 20 | - field: 'email', | |
| 34 | + field: 'leaderUserId', | |
| 21 | 35 | component: 'Input' |
| 22 | 36 | }, |
| 23 | 37 | { |
| ... | ... | @@ -33,7 +47,7 @@ export const modelSchema = reactive<FormSchema[]>([ |
| 33 | 47 | { |
| 34 | 48 | label: '显示排序', |
| 35 | 49 | field: 'sort', |
| 36 | - component: 'Input' | |
| 50 | + component: 'InputNumber' | |
| 37 | 51 | }, |
| 38 | 52 | { |
| 39 | 53 | label: '状态', | ... | ... |
yudao-ui-admin-vue3/src/views/system/dept/index.vue
| 1 | 1 | <script setup lang="ts"> |
| 2 | 2 | import { useI18n } from '@/hooks/web/useI18n' |
| 3 | -import { ElInput, ElCard, ElTree, ElTreeSelect } from 'element-plus' | |
| 3 | +import { ElInput, ElCard, ElTree, ElTreeSelect, ElSelect, ElOption } from 'element-plus' | |
| 4 | 4 | import { handleTree } from '@/utils/tree' |
| 5 | 5 | import { onMounted, ref, unref, watch } from 'vue' |
| 6 | 6 | import * as DeptApi from '@/api/system/dept' |
| 7 | 7 | import { Form, FormExpose } from '@/components/Form' |
| 8 | -import { modelSchema } from './dept.data' | |
| 8 | +import { modelSchema, rules } from './dept.data' | |
| 9 | 9 | import { DeptVO } from '@/api/system/dept/types' |
| 10 | 10 | import { useMessage } from '@/hooks/web/useMessage' |
| 11 | +import { getListSimpleUsersApi } from '@/api/system/user' | |
| 11 | 12 | const message = useMessage() |
| 12 | 13 | interface Tree { |
| 13 | 14 | id: number |
| ... | ... | @@ -34,7 +35,7 @@ const filterText = ref('') |
| 34 | 35 | const deptOptions = ref() // 树形结构 |
| 35 | 36 | const treeRef = ref<InstanceType<typeof ElTree>>() |
| 36 | 37 | const getTree = async () => { |
| 37 | - const res = await DeptApi.getDeptPageApi(null) | |
| 38 | + const res = await DeptApi.listSimpleDeptApi() | |
| 38 | 39 | deptOptions.value = handleTree(res) |
| 39 | 40 | } |
| 40 | 41 | const filterNode = (value: string, data: Tree) => { |
| ... | ... | @@ -44,6 +45,13 @@ const filterNode = (value: string, data: Tree) => { |
| 44 | 45 | watch(filterText, (val) => { |
| 45 | 46 | treeRef.value!.filter(val) |
| 46 | 47 | }) |
| 48 | +// 用户列表 | |
| 49 | +const userOption = ref() | |
| 50 | +const leaderUserId = ref() | |
| 51 | +const getUserList = async () => { | |
| 52 | + const res = await getListSimpleUsersApi() | |
| 53 | + userOption.value = res | |
| 54 | +} | |
| 47 | 55 | // 新增 |
| 48 | 56 | const handleAdd = (data: { id: number }) => { |
| 49 | 57 | // 重置表单 |
| ... | ... | @@ -54,11 +62,12 @@ const handleAdd = (data: { id: number }) => { |
| 54 | 62 | } |
| 55 | 63 | // 编辑 |
| 56 | 64 | const handleUpdate = async (data: { id: number }) => { |
| 57 | - showForm.value = true | |
| 58 | 65 | const res = await DeptApi.getDeptApi(data.id) |
| 59 | 66 | formTitle.value = '修改- ' + res?.name |
| 60 | 67 | deptParentId.value = res.parentId |
| 68 | + leaderUserId.value = res.leaderUserId | |
| 61 | 69 | unref(formRef)?.setValues(res) |
| 70 | + showForm.value = true | |
| 62 | 71 | } |
| 63 | 72 | // 删除 |
| 64 | 73 | const handleDelete = async (data: { id: number }) => { |
| ... | ... | @@ -78,7 +87,7 @@ const submitForm = async () => { |
| 78 | 87 | try { |
| 79 | 88 | const data = unref(formRef)?.formModel as DeptVO |
| 80 | 89 | data.parentId = deptParentId.value |
| 81 | - // TODO: 表单提交待完善 | |
| 90 | + data.leaderUserId = leaderUserId.value | |
| 82 | 91 | if (formTitle.value.startsWith('新增')) { |
| 83 | 92 | await DeptApi.createDeptApi(data) |
| 84 | 93 | } else if (formTitle.value.startsWith('修改')) { |
| ... | ... | @@ -92,6 +101,7 @@ const submitForm = async () => { |
| 92 | 101 | } |
| 93 | 102 | onMounted(async () => { |
| 94 | 103 | await getTree() |
| 104 | + await getUserList() | |
| 95 | 105 | }) |
| 96 | 106 | </script> |
| 97 | 107 | <template> |
| ... | ... | @@ -123,14 +133,29 @@ onMounted(async () => { |
| 123 | 133 | <span class="custom-tree-node"> |
| 124 | 134 | <span>{{ node.label }}</span> |
| 125 | 135 | <span> |
| 126 | - <el-button link v-hasPermi="['system:dept:create']" @click="handleAdd(data)"> | |
| 127 | - <Icon icon="ep:plus" class="mr-1px" /> | |
| 136 | + <el-button | |
| 137 | + link | |
| 138 | + type="primary" | |
| 139 | + v-hasPermi="['system:dept:create']" | |
| 140 | + @click="handleAdd(data)" | |
| 141 | + > | |
| 142 | + <Icon icon="ep:zoom-in" class="mr-5px" /> {{ t('action.add') }} | |
| 128 | 143 | </el-button> |
| 129 | - <el-button link v-hasPermi="['system:dept:update']" @click="handleUpdate(data)"> | |
| 130 | - <Icon icon="ep:edit" class="mr-1px" /> | |
| 144 | + <el-button | |
| 145 | + link | |
| 146 | + type="primary" | |
| 147 | + v-hasPermi="['system:dept:update']" | |
| 148 | + @click="handleUpdate(data)" | |
| 149 | + > | |
| 150 | + <Icon icon="ep:edit" class="mr-1px" /> {{ t('action.edit') }} | |
| 131 | 151 | </el-button> |
| 132 | - <el-button link v-hasPermi="['system:dept:delete']" @click="handleDelete(data)"> | |
| 133 | - <Icon icon="ep:delete" class="mr-1px" /> | |
| 152 | + <el-button | |
| 153 | + link | |
| 154 | + type="primary" | |
| 155 | + v-hasPermi="['system:dept:delete']" | |
| 156 | + @click="handleDelete(data)" | |
| 157 | + > | |
| 158 | + <Icon icon="ep:delete" class="mr-1px" /> {{ t('action.del') }} | |
| 134 | 159 | </el-button> |
| 135 | 160 | </span> |
| 136 | 161 | </span> |
| ... | ... | @@ -149,7 +174,7 @@ onMounted(async () => { |
| 149 | 174 | </div> |
| 150 | 175 | <div v-if="showForm"> |
| 151 | 176 | <!-- 操作工具栏 --> |
| 152 | - <Form :schema="modelSchema" ref="formRef"> | |
| 177 | + <Form ref="formRef" :schema="modelSchema" :rules="rules"> | |
| 153 | 178 | <template #parentId> |
| 154 | 179 | <el-tree-select |
| 155 | 180 | node-key="id" |
| ... | ... | @@ -159,6 +184,16 @@ onMounted(async () => { |
| 159 | 184 | check-strictly |
| 160 | 185 | /> |
| 161 | 186 | </template> |
| 187 | + <template #leaderUserId> | |
| 188 | + <el-select v-model="leaderUserId"> | |
| 189 | + <el-option | |
| 190 | + v-for="item in userOption" | |
| 191 | + :key="parseInt(item.id)" | |
| 192 | + :label="item.nickname" | |
| 193 | + :value="parseInt(item.id)" | |
| 194 | + /> | |
| 195 | + </el-select> | |
| 196 | + </template> | |
| 162 | 197 | </Form> |
| 163 | 198 | <!-- 操作按钮 --> |
| 164 | 199 | <el-button | ... | ... |
yudao-ui-admin-vue3/src/views/system/dict/dict.data.ts
| ... | ... | @@ -7,9 +7,9 @@ import { CrudSchema, useCrudSchemas } from '@/hooks/web/useCrudSchemas' |
| 7 | 7 | const { t } = useI18n() |
| 8 | 8 | // 表单校验 |
| 9 | 9 | export const dictDataRules = reactive({ |
| 10 | - dictType: [required], | |
| 11 | 10 | label: [required], |
| 12 | - value: [required] | |
| 11 | + value: [required], | |
| 12 | + sort: [required] | |
| 13 | 13 | }) |
| 14 | 14 | // crudSchemas |
| 15 | 15 | export const crudSchemas = reactive<CrudSchema[]>([ |
| ... | ... | @@ -29,6 +29,9 @@ export const crudSchemas = reactive<CrudSchema[]>([ |
| 29 | 29 | field: 'dictType', |
| 30 | 30 | table: { |
| 31 | 31 | show: false |
| 32 | + }, | |
| 33 | + form: { | |
| 34 | + show: false | |
| 32 | 35 | } |
| 33 | 36 | }, |
| 34 | 37 | { | ... | ... |
yudao-ui-admin-vue3/src/views/system/menu/index.vue
| ... | ... | @@ -25,11 +25,12 @@ import { MenuVO } from '@/api/system/menu/types' |
| 25 | 25 | import { SystemMenuTypeEnum, CommonStatusEnum } from '@/utils/constants' |
| 26 | 26 | import { DICT_TYPE, getIntDictOptions } from '@/utils/dict' |
| 27 | 27 | import { useMessage } from '@/hooks/web/useMessage' |
| 28 | +import { required } from '@/utils/formRules.js' | |
| 28 | 29 | const message = useMessage() |
| 29 | 30 | const { t } = useI18n() // 国际化 |
| 30 | 31 | // ========== 创建菜单树结构 ========== |
| 31 | 32 | const loading = ref(true) |
| 32 | -const menuData = ref([]) // 树形结构 | |
| 33 | +const menuData = ref<any[]>([]) // 树形结构 | |
| 33 | 34 | const getList = async () => { |
| 34 | 35 | const res = await MenuApi.getMenuListApi(queryParams) |
| 35 | 36 | menuData.value = handleTree(res) |
| ... | ... | @@ -44,7 +45,7 @@ const menuProps = { |
| 44 | 45 | const menuOptions = ref() // 树形结构 |
| 45 | 46 | const getTree = async () => { |
| 46 | 47 | const res = await MenuApi.listSimpleMenusApi() |
| 47 | - const menu = { id: 0, name: '主类目', children: [] } | |
| 48 | + const menu = { id: 0, name: '主类目', children: [] as any[] } | |
| 48 | 49 | menu.children = handleTree(res) |
| 49 | 50 | console.info(menu) |
| 50 | 51 | menuOptions.value = menu |
| ... | ... | @@ -85,12 +86,12 @@ const menuForm = ref<MenuVO>({ |
| 85 | 86 | createTime: '' |
| 86 | 87 | }) |
| 87 | 88 | // 表单校验 |
| 88 | -const rules = { | |
| 89 | - name: [{ required: true, message: '菜单名称不能为空', trigger: 'blur' }], | |
| 90 | - sort: [{ required: true, message: '菜单顺序不能为空', trigger: 'blur' }], | |
| 91 | - path: [{ required: true, message: '路由地址不能为空', trigger: 'blur' }], | |
| 92 | - status: [{ required: true, message: '状态不能为空', trigger: 'blur' }] | |
| 93 | -} | |
| 89 | +const rules = reactive({ | |
| 90 | + name: [required], | |
| 91 | + sort: [required], | |
| 92 | + path: [required], | |
| 93 | + status: [required] | |
| 94 | +}) | |
| 94 | 95 | // 设置标题 |
| 95 | 96 | const setDialogTile = (type: string) => { |
| 96 | 97 | dialogTitle.value = t('action.' + type) | ... | ... |
yudao-ui-admin-vue3/src/views/system/notice/notice.data.ts
yudao-ui-admin-vue3/src/views/system/post/post.data.ts
yudao-ui-admin-vue3/src/views/system/sms/smsTemplate/sms.template.data.ts
| ... | ... | @@ -8,13 +8,12 @@ const { t } = useI18n() // 国际化 |
| 8 | 8 | // 表单校验 |
| 9 | 9 | export const rules = reactive({ |
| 10 | 10 | type: [required], |
| 11 | + status: [required], | |
| 12 | + code: [required], | |
| 11 | 13 | name: [required], |
| 12 | 14 | content: [required], |
| 13 | 15 | apiTemplateId: [required], |
| 14 | - channelId: [required], | |
| 15 | - code: [required], | |
| 16 | - sort: [required], | |
| 17 | - status: [required] | |
| 16 | + channelId: [required] | |
| 18 | 17 | }) |
| 19 | 18 | |
| 20 | 19 | // CrudSchema | ... | ... |
yudao-ui-admin-vue3/src/views/system/user/index.vue
| ... | ... | @@ -9,6 +9,7 @@ import { |
| 9 | 9 | ElTreeSelect, |
| 10 | 10 | ElSelect, |
| 11 | 11 | ElOption, |
| 12 | + ElTransfer, | |
| 12 | 13 | ElForm, |
| 13 | 14 | ElFormItem, |
| 14 | 15 | ElUpload, |
| ... | ... | @@ -62,7 +63,7 @@ const { getList, setSearchParams, delList, exportList } = methods |
| 62 | 63 | |
| 63 | 64 | // ========== 创建部门树结构 ========== |
| 64 | 65 | const filterText = ref('') |
| 65 | -const deptOptions = ref([]) // 树形结构 | |
| 66 | +const deptOptions = ref<any[]>([]) // 树形结构 | |
| 66 | 67 | const searchForm = ref<FormExpose>() |
| 67 | 68 | const treeRef = ref<InstanceType<typeof ElTree>>() |
| 68 | 69 | const getTree = async () => { |
| ... | ... | @@ -268,6 +269,7 @@ const handleFileSuccess = (response: any): void => { |
| 268 | 269 | text += '< ' + username + ': ' + data.failureUsernames[username] + ' >' |
| 269 | 270 | } |
| 270 | 271 | message.alert(text) |
| 272 | + getList() | |
| 271 | 273 | } |
| 272 | 274 | // 文件数超出提示 |
| 273 | 275 | const handleExceed = (): void => { |
| ... | ... | @@ -281,8 +283,8 @@ const excelUploadError = (): void => { |
| 281 | 283 | onMounted(async () => { |
| 282 | 284 | await getTree() |
| 283 | 285 | await getPostOptions() |
| 286 | + await getList() | |
| 284 | 287 | }) |
| 285 | -getList() | |
| 286 | 288 | </script> |
| 287 | 289 | |
| 288 | 290 | <template> |
| ... | ... | @@ -482,7 +484,7 @@ getList() |
| 482 | 484 | </template> |
| 483 | 485 | </Dialog> |
| 484 | 486 | <!-- 分配用户角色 --> |
| 485 | - <Dialog v-model="roleDialogVisible" title="分配角色"> | |
| 487 | + <Dialog v-model="roleDialogVisible" title="分配角色" maxHeight="450px"> | |
| 486 | 488 | <el-form :model="userRole" label-width="80px"> |
| 487 | 489 | <el-form-item label="用户名称"> |
| 488 | 490 | <el-input v-model="userRole.username" :disabled="true" /> |
| ... | ... | @@ -491,14 +493,15 @@ getList() |
| 491 | 493 | <el-input v-model="userRole.nickname" :disabled="true" /> |
| 492 | 494 | </el-form-item> |
| 493 | 495 | <el-form-item label="角色"> |
| 494 | - <el-select v-model="userRole.roleIds" multiple> | |
| 495 | - <el-option | |
| 496 | - v-for="item in roleOptions" | |
| 497 | - :key="parseInt(item.id)" | |
| 498 | - :label="item.name" | |
| 499 | - :value="parseInt(item.id)" | |
| 500 | - /> | |
| 501 | - </el-select> | |
| 496 | + <el-transfer | |
| 497 | + v-model="userRole.roleIds" | |
| 498 | + :titles="['角色列表', '已选择']" | |
| 499 | + :props="{ | |
| 500 | + key: 'id', | |
| 501 | + label: 'name' | |
| 502 | + }" | |
| 503 | + :data="roleOptions" | |
| 504 | + /> | |
| 502 | 505 | </el-form-item> |
| 503 | 506 | </el-form> |
| 504 | 507 | <!-- 操作按钮 --> | ... | ... |
yudao-ui-admin-vue3/src/views/system/user/user.data.ts
| ... | ... | @@ -7,8 +7,18 @@ import { CrudSchema, useCrudSchemas } from '@/hooks/web/useCrudSchemas' |
| 7 | 7 | const { t } = useI18n() |
| 8 | 8 | // 表单校验 |
| 9 | 9 | export const rules = reactive({ |
| 10 | + username: [required], | |
| 10 | 11 | nickname: [required], |
| 11 | - status: [required] | |
| 12 | + email: [required], | |
| 13 | + status: [required], | |
| 14 | + mobile: [ | |
| 15 | + { | |
| 16 | + pattern: | |
| 17 | + /^(?:(?:\+|00)86)?1(?:(?:3[\d])|(?:4[5-79])|(?:5[0-35-9])|(?:6[5-7])|(?:7[0-8])|(?:8[\d])|(?:9[189]))\d{8}$/, | |
| 18 | + trigger: 'blur', | |
| 19 | + message: '请输入正确的手机号码' | |
| 20 | + } | |
| 21 | + ] | |
| 12 | 22 | }) |
| 13 | 23 | // crudSchemas |
| 14 | 24 | const crudSchemas = reactive<CrudSchema[]>([ | ... | ... |
yudao-ui-admin-vue3/use.md
0 → 100644
| 1 | +# 注意事项 | |
| 2 | + | |
| 3 | +- 项目路径请不要使用中文命名!!!会造成解析乱码!!!请使用全英文路径!!!全英文路径!!!全英文路径!!! | |
| 4 | +- node >=16 , pnpm >=7,非node16+ pnpm 7+ 安装问题不予解决、不适配 | |
| 5 | +- 开发建议使用 [谷歌浏览器-开发者版](https://www.google.cn/intl/zh-CN/chrome/dev/) 不支持 IE\QQ 等浏览器 | |
| 6 | +- 本框架使用 TypeScript ,简称ts,和java的类型差不多,为了简化没过多使用 type | |
| 7 | +- 本框架使用 Vue3.2 + setup语法糖,请自行学习相关内容 | |
| 8 | +- [点击查看:为什么Vue3.2,什么是setup,Vite为什么第一次加载速度慢](https://www.baidu.com) | |
| 9 | +- idea 怎么开发? 不知道。 | |
| 10 | +- 启动方式详见[README.md](./README.md) | |
| 11 | + | |
| 12 | +## 简单使用 | |
| 13 | + | |
| 14 | +- 目录结构与 vue2 版本基本保持一致 | |
| 15 | +- 一个页面(以post为例)由4部分组成 | |
| 16 | + | |
| 17 | +```bash | |
| 18 | +/src/api/system/post/ [index.ts | types.ts] | |
| 19 | +/src/views/system/post/ [index.vue | post.data.ts] | |
| 20 | +``` | |
| 21 | + | |
| 22 | +- 其中api内index.ts 与 vue2 基本一致,只不过axios封装了get post put delete upload download 等方法,不用写method: 'get' 了 | |
| 23 | +- api内types.ts,是接口中的类型声明,与java中vo等保持一致, java中long int => ts 中 number | |
| 24 | +- views中,index.vue 与 vue2 基本一致,本框架封装了Search Table Form Descriptions等组件,也可以按照vue2方式去写,参考menu | |
| 25 | +- post.data.ts 中主要是表单校验 rules 和表单 crudSchemas ,通过修改crudSchemas 就可以控制增删改查的字段、输入框还是下拉框等等 | |
| 26 | +- 本框架集成了国际化,不需要可以自己想办法移除,后期不会提供删减版 使用方式 | |
| 27 | + | |
| 28 | +```bash | |
| 29 | +import { useI18n } from '@/hooks/web/useI18n' | |
| 30 | +const { t } = useI18n() | |
| 31 | +t('common.createTime') | |
| 32 | +对应翻译文档在 src/locales | |
| 33 | +``` | ... | ... |
yudao-ui-admin-vue3/vite.config.ts
| ... | ... | @@ -138,6 +138,7 @@ export default ({ command, mode }: ConfigEnv): UserConfig => { |
| 138 | 138 | 'vue-router', |
| 139 | 139 | 'vue-types', |
| 140 | 140 | 'vue-i18n', |
| 141 | + 'element-plus/es', | |
| 141 | 142 | 'element-plus/es/locale/lang/zh-cn', |
| 142 | 143 | 'element-plus/es/locale/lang/en', |
| 143 | 144 | '@iconify/iconify', | ... | ... |
yudao-ui-admin/src/views/system/dept/index.vue
| ... | ... | @@ -167,7 +167,7 @@ export default { |
| 167 | 167 | ], |
| 168 | 168 | phone: [ |
| 169 | 169 | { |
| 170 | - pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/, | |
| 170 | + pattern: /^(?:(?:\+|00)86)?1(?:(?:3[\d])|(?:4[5-79])|(?:5[0-35-9])|(?:6[5-7])|(?:7[0-8])|(?:8[\d])|(?:9[189]))\d{8}$/, | |
| 171 | 171 | message: "请输入正确的手机号码", |
| 172 | 172 | trigger: "blur" |
| 173 | 173 | } | ... | ... |
yudao-ui-admin/src/views/system/tenant/index.vue
| ... | ... | @@ -183,8 +183,6 @@ export default { |
| 183 | 183 | accountCount: [{ required: true, message: "账号额度不能为空", trigger: "blur" }], |
| 184 | 184 | expireTime: [{ required: true, message: "过期时间不能为空", trigger: "blur" }], |
| 185 | 185 | domain: [{ required: true, message: "绑定域名不能为空", trigger: "blur" }], |
| 186 | - username: [{ required: true, message: "用户名称不能为空", trigger: "blur" }], | |
| 187 | - password: [{ required: true, message: "用户密码不能为空", trigger: "blur" }], | |
| 188 | 186 | } |
| 189 | 187 | }; |
| 190 | 188 | }, | ... | ... |