) => {
- const { data } = response
- const config = response.config
- if (!data) {
- // 返回“[HTTP]请求没有返回值”;
- throw new Error()
- }
- const { t } = useI18n()
- // 未设置状态码则默认成功状态
- const code = data.code || result_code
- // 二进制数据则直接返回
- if (
- response.request.responseType === 'blob' ||
- response.request.responseType === 'arraybuffer'
- ) {
- return response.data
- }
- // 获取错误信息
- const msg = data.msg || errorCode[code] || errorCode['default']
- if (ignoreMsgs.indexOf(msg) !== -1) {
- // 如果是忽略的错误码,直接返回 msg 异常
- return Promise.reject(msg)
- } else if (code === 401) {
- // 如果未认证,并且未进行刷新令牌,说明可能是访问令牌过期了
- if (!isRefreshToken) {
- isRefreshToken = true
- // 1. 如果获取不到刷新令牌,则只能执行登出操作
- if (!getRefreshToken()) {
- return handleAuthorized()
- }
- // 2. 进行刷新访问令牌
- try {
- const refreshTokenRes = await refreshToken()
- // 2.1 刷新成功,则回放队列的请求 + 当前请求
- setToken(refreshTokenRes.data.data)
- config.headers!.Authorization = 'Bearer ' + getAccessToken()
- requestList.forEach((cb: any) => {
- cb()
- })
- requestList = []
- return service(config)
- } catch (e) {
- // 为什么需要 catch 异常呢?刷新失败时,请求因为 Promise.reject 触发异常。
- // 2.2 刷新失败,只回放队列的请求
- requestList.forEach((cb: any) => {
- cb()
- })
- // 提示是否要登出。即不回放当前请求!不然会形成递归
- return handleAuthorized()
- } finally {
- requestList = []
- isRefreshToken = false
- }
- } else {
- // 添加到队列,等待刷新获取到新的令牌
- return new Promise((resolve) => {
- requestList.push(() => {
- config.headers!.Authorization = 'Bearer ' + getAccessToken() // 让每个请求携带自定义token 请根据实际情况自行修改
- resolve(service(config))
- })
- })
- }
- } else if (code === 500) {
- ElMessage.error(t('sys.api.errMsg500'))
- return Promise.reject(new Error(msg))
- } else if (code === 901) {
- ElMessage.error(
- '' +
- t('sys.api.errMsg901') +
- '
' +
- '
' +
- '参考 https://doc.iocoder.cn/ 教程
' +
- '
' +
- '5 分钟搭建本地环境
'
- )
- return Promise.reject(new Error(msg))
- } else if (code !== 200) {
- if (msg === '无效的刷新令牌') {
- // hard coding:忽略这个提示,直接登出
- console.log(msg)
- } else {
- ElNotification.error({
- title: msg
- })
- }
- return Promise.reject('error')
- } else {
- return data
- }
- },
- (error: AxiosError) => {
- console.log('err' + error) // for debug
- let { message } = error
- const { t } = useI18n()
- if (message === 'Network Error') {
- message = t('sys.api.errorMessage')
- } else if (message.includes('timeout')) {
- message = t('sys.api.apiTimeoutMessage')
- } else if (message.includes('Request failed with status code')) {
- message = t('sys.api.apiRequestFailed') + message.substr(message.length - 3)
- }
- ElMessage.error(message)
- return Promise.reject(error)
- }
-)
-
-const refreshToken = async () => {
- return await axios.post(base_url + '/system/auth/refresh-token?refreshToken=' + getRefreshToken())
+ })
}
-const handleAuthorized = () => {
- const { t } = useI18n()
- if (!isRelogin.show) {
- isRelogin.show = true
- ElMessageBox.confirm(t('sys.api.timeoutMessage'), t('common.confirmTitle'), {
- confirmButtonText: t('login.relogin'),
- cancelButtonText: t('common.cancel'),
- type: 'warning'
- })
- .then(() => {
- const { wsCache } = useCache()
- resetRouter() // 重置静态路由表
- wsCache.clear()
- removeToken()
- isRelogin.show = false
- window.location.href = '/'
- })
- .catch(() => {
- isRelogin.show = false
- })
+export default {
+ get: async (option: any) => {
+ const res = await request({ method: 'GET', ...option })
+ return res.data as unknown as T
+ },
+ post: async (option: any) => {
+ const res = await request({ method: 'POST', ...option })
+ return res.data as unknown as T
+ },
+ delete: async (option: any) => {
+ const res = await request({ method: 'DELETE', ...option })
+ return res.data as unknown as T
+ },
+ put: async (option: any) => {
+ const res = await request({ method: 'PUT', ...option })
+ return res.data as unknown as T
+ },
+ download: async (option: any) => {
+ const res = await request({ method: 'GET', responseType: 'blob', ...option })
+ return res as unknown as Promise
+ },
+ upload: async (option: any) => {
+ option.headersType = 'multipart/form-data'
+ const res = await request({ method: 'PUT', ...option })
+ return res as unknown as Promise
}
- return Promise.reject(t('sys.api.timeoutMessage'))
}
-export { service }
diff --git a/yudao-ui-admin-vue3/src/config/axios/service.ts b/yudao-ui-admin-vue3/src/config/axios/service.ts
new file mode 100644
index 0000000000..60dad6aa06
--- /dev/null
+++ b/yudao-ui-admin-vue3/src/config/axios/service.ts
@@ -0,0 +1,229 @@
+import axios, {
+ AxiosInstance,
+ AxiosRequestConfig,
+ AxiosRequestHeaders,
+ AxiosResponse,
+ AxiosError
+} from 'axios'
+import { ElMessage, ElMessageBox, ElNotification } from 'element-plus'
+import qs from 'qs'
+import { config } from '@/config/axios/config'
+import { getAccessToken, getRefreshToken, getTenantId, removeToken, setToken } from '@/utils/auth'
+import errorCode from './errorCode'
+import { useI18n } from '@/hooks/web/useI18n'
+import { resetRouter } from '@/router'
+import { useCache } from '@/hooks/web/useCache'
+
+const tenantEnable = import.meta.env.VITE_APP_TENANT_ENABLE
+const { result_code, base_url, request_timeout } = config
+
+// 需要忽略的提示。忽略后,自动 Promise.reject('error')
+const ignoreMsgs = [
+ '无效的刷新令牌', // 刷新令牌被删除时,不用提示
+ '刷新令牌已过期' // 使用刷新令牌,刷新获取新的访问令牌时,结果因为过期失败,此时需要忽略。否则,会导致继续 401,无法跳转到登出界面
+]
+// 是否显示重新登录
+export const isRelogin = { show: false }
+// Axios 无感知刷新令牌,参考 https://www.dashingdog.cn/article/11 与 https://segmentfault.com/a/1190000020210980 实现
+// 请求队列
+let requestList: any[] = []
+// 是否正在刷新中
+let isRefreshToken = false
+
+// 创建axios实例
+const service: AxiosInstance = axios.create({
+ baseURL: base_url, // api 的 base_url
+ timeout: request_timeout, // 请求超时时间
+ withCredentials: false // 禁用 Cookie 等信息
+})
+
+// request拦截器
+service.interceptors.request.use(
+ (config: AxiosRequestConfig) => {
+ // 是否需要设置 token
+ const isToken = (config!.headers || {}).isToken === false
+ if (getAccessToken() && !isToken) {
+ ;(config as Recordable).headers.Authorization = 'Bearer ' + getAccessToken() // 让每个请求携带自定义token
+ }
+ // 设置租户
+ if (tenantEnable) {
+ const tenantId = getTenantId()
+ if (tenantId) (config as Recordable).headers.common['tenant-id'] = tenantId
+ }
+ const params = config.params || {}
+ const data = config.data || false
+ if (
+ config.method?.toUpperCase() === 'POST' &&
+ (config.headers as AxiosRequestHeaders)['Content-Type'] ===
+ 'application/x-www-form-urlencoded'
+ ) {
+ config.data = qs.stringify(data)
+ }
+ // get参数编码
+ if (config.method?.toUpperCase() === 'GET' && params) {
+ let url = config.url + '?'
+ for (const propName of Object.keys(params)) {
+ const value = params[propName]
+ if (value !== void 0 && value !== null && typeof value !== 'undefined') {
+ if (typeof value === 'object') {
+ for (const val of Object.keys(value)) {
+ const params = propName + '[' + val + ']'
+ const subPart = encodeURIComponent(params) + '='
+ url += subPart + encodeURIComponent(value[val]) + '&'
+ }
+ } else {
+ url += `${propName}=${encodeURIComponent(value)}&`
+ }
+ }
+ }
+ // 给 get 请求加上时间戳参数,避免从缓存中拿数据
+ // const now = new Date().getTime()
+ // params = params.substring(0, url.length - 1) + `?_t=${now}`
+ url = url.slice(0, -1)
+ config.params = {}
+ config.url = url
+ }
+ return config
+ },
+ (error: AxiosError) => {
+ // Do something with request error
+ console.log(error) // for debug
+ Promise.reject(error)
+ }
+)
+
+// response 拦截器
+service.interceptors.response.use(
+ async (response: AxiosResponse) => {
+ const { data } = response
+ const config = response.config
+ if (!data) {
+ // 返回“[HTTP]请求没有返回值”;
+ throw new Error()
+ }
+ const { t } = useI18n()
+ // 未设置状态码则默认成功状态
+ const code = data.code || result_code
+ // 二进制数据则直接返回
+ if (
+ response.request.responseType === 'blob' ||
+ response.request.responseType === 'arraybuffer'
+ ) {
+ return response.data
+ }
+ // 获取错误信息
+ const msg = data.msg || errorCode[code] || errorCode['default']
+ if (ignoreMsgs.indexOf(msg) !== -1) {
+ // 如果是忽略的错误码,直接返回 msg 异常
+ return Promise.reject(msg)
+ } else if (code === 401) {
+ // 如果未认证,并且未进行刷新令牌,说明可能是访问令牌过期了
+ if (!isRefreshToken) {
+ isRefreshToken = true
+ // 1. 如果获取不到刷新令牌,则只能执行登出操作
+ if (!getRefreshToken()) {
+ return handleAuthorized()
+ }
+ // 2. 进行刷新访问令牌
+ try {
+ const refreshTokenRes = await refreshToken()
+ // 2.1 刷新成功,则回放队列的请求 + 当前请求
+ setToken(refreshTokenRes.data.data)
+ config.headers!.Authorization = 'Bearer ' + getAccessToken()
+ requestList.forEach((cb: any) => {
+ cb()
+ })
+ requestList = []
+ return service(config)
+ } catch (e) {
+ // 为什么需要 catch 异常呢?刷新失败时,请求因为 Promise.reject 触发异常。
+ // 2.2 刷新失败,只回放队列的请求
+ requestList.forEach((cb: any) => {
+ cb()
+ })
+ // 提示是否要登出。即不回放当前请求!不然会形成递归
+ return handleAuthorized()
+ } finally {
+ requestList = []
+ isRefreshToken = false
+ }
+ } else {
+ // 添加到队列,等待刷新获取到新的令牌
+ return new Promise((resolve) => {
+ requestList.push(() => {
+ config.headers!.Authorization = 'Bearer ' + getAccessToken() // 让每个请求携带自定义token 请根据实际情况自行修改
+ resolve(service(config))
+ })
+ })
+ }
+ } else if (code === 500) {
+ ElMessage.error(t('sys.api.errMsg500'))
+ return Promise.reject(new Error(msg))
+ } else if (code === 901) {
+ ElMessage.error(
+ '' +
+ t('sys.api.errMsg901') +
+ '
' +
+ '
' +
+ '参考 https://doc.iocoder.cn/ 教程
' +
+ '
' +
+ '5 分钟搭建本地环境
'
+ )
+ return Promise.reject(new Error(msg))
+ } else if (code !== 200) {
+ if (msg === '无效的刷新令牌') {
+ // hard coding:忽略这个提示,直接登出
+ console.log(msg)
+ } else {
+ ElNotification.error({
+ title: msg
+ })
+ }
+ return Promise.reject('error')
+ } else {
+ return data
+ }
+ },
+ (error: AxiosError) => {
+ console.log('err' + error) // for debug
+ let { message } = error
+ const { t } = useI18n()
+ if (message === 'Network Error') {
+ message = t('sys.api.errorMessage')
+ } else if (message.includes('timeout')) {
+ message = t('sys.api.apiTimeoutMessage')
+ } else if (message.includes('Request failed with status code')) {
+ message = t('sys.api.apiRequestFailed') + message.substr(message.length - 3)
+ }
+ ElMessage.error(message)
+ return Promise.reject(error)
+ }
+)
+
+const refreshToken = async () => {
+ return await axios.post(base_url + '/system/auth/refresh-token?refreshToken=' + getRefreshToken())
+}
+const handleAuthorized = () => {
+ const { t } = useI18n()
+ if (!isRelogin.show) {
+ isRelogin.show = true
+ ElMessageBox.confirm(t('sys.api.timeoutMessage'), t('common.confirmTitle'), {
+ confirmButtonText: t('login.relogin'),
+ cancelButtonText: t('common.cancel'),
+ type: 'warning'
+ })
+ .then(() => {
+ const { wsCache } = useCache()
+ resetRouter() // 重置静态路由表
+ wsCache.clear()
+ removeToken()
+ isRelogin.show = false
+ window.location.href = '/'
+ })
+ .catch(() => {
+ isRelogin.show = false
+ })
+ }
+ return Promise.reject(t('sys.api.timeoutMessage'))
+}
+export { service }
diff --git a/yudao-ui-admin-vue3/src/hooks/web/useAxios.ts b/yudao-ui-admin-vue3/src/hooks/web/useAxios.ts
deleted file mode 100644
index 331969c6e0..0000000000
--- a/yudao-ui-admin-vue3/src/hooks/web/useAxios.ts
+++ /dev/null
@@ -1,60 +0,0 @@
-import { service } from '@/config/axios'
-
-import { config } from '@/config/axios/config'
-
-const { default_headers } = config
-
-const request = (option: AxiosConfig) => {
- const { url, method, params, data, headersType, responseType } = option
- return service({
- url: url,
- method,
- params,
- data,
- responseType: responseType,
- headers: {
- 'Content-Type': headersType || default_headers
- }
- })
-}
-
-async function getFn(option: AxiosConfig): Promise {
- const res = await request({ method: 'GET', ...option })
- return res.data
-}
-
-async function postFn(option: AxiosConfig): Promise {
- const res = await request({ method: 'POST', ...option })
- return res.data
-}
-
-async function deleteFn(option: AxiosConfig): Promise {
- const res = await request({ method: 'DELETE', ...option })
- return res.data
-}
-
-async function putFn(option: AxiosConfig): Promise {
- const res = await request({ method: 'PUT', ...option })
- return res.data
-}
-async function downloadFn(option: AxiosConfig): Promise {
- const res = await request({ method: 'GET', responseType: 'blob', ...option })
- return res as unknown as Promise
-}
-
-async function uploadFn(option: AxiosConfig): Promise {
- option.headersType = 'multipart/form-data'
- const res = await request({ method: 'PUT', ...option })
- return res as unknown as Promise
-}
-
-export const useAxios = () => {
- return {
- get: getFn,
- post: postFn,
- delete: deleteFn,
- put: putFn,
- download: downloadFn,
- upload: uploadFn
- }
-}
diff --git a/yudao-ui-admin-vue3/src/hooks/web/useVxeCrudSchemas.ts b/yudao-ui-admin-vue3/src/hooks/web/useVxeCrudSchemas.ts
new file mode 100644
index 0000000000..b70140743d
--- /dev/null
+++ b/yudao-ui-admin-vue3/src/hooks/web/useVxeCrudSchemas.ts
@@ -0,0 +1,246 @@
+import { DescriptionsSchema } from '@/types/descriptions'
+import { getIntDictOptions } from '@/utils/dict'
+import { reactive } from 'vue'
+import {
+ FormItemRenderOptions,
+ VxeFormItemProps,
+ VxeGridPropTypes,
+ VxeTableDefines
+} from 'vxe-table'
+import { eachTree } from 'xe-utils'
+import { useI18n } from '@/hooks/web/useI18n'
+import { VxeTableColumn } from '@/types/table'
+
+export type VxeCrudSchema = Omit & {
+ field: string
+ title?: string
+ search?: CrudSearchParams
+ table?: CrudTableParams
+ form?: CrudFormParams
+ detail?: CrudDescriptionsParams
+ print?: boolean
+ children?: VxeCrudSchema[]
+ dictType?: string
+}
+type CrudSearchParams = {
+ // 是否显示在查询项
+ show?: boolean
+} & Omit
+
+type CrudTableParams = {
+ // 是否显示表头
+ show?: boolean
+} & Omit
+
+type CrudFormParams = {
+ // 是否显示表单项
+ show?: boolean
+} & Omit
+
+type CrudDescriptionsParams = {
+ // 是否显示表单项
+ show?: boolean
+} & Omit
+
+interface VxeAllSchemas {
+ searchSchema: VxeFormItemProps[]
+ tableSchema: VxeGridPropTypes.Columns
+ formSchema: VxeFormItemProps[]
+ detailSchema: DescriptionsSchema[]
+ printSchema: VxeTableDefines.ColumnInfo[]
+}
+
+// 过滤所有结构
+export const useVxeCrudSchemas = (
+ crudSchema: VxeCrudSchema[]
+): {
+ allSchemas: VxeAllSchemas
+} => {
+ // 所有结构数据
+ const allSchemas = reactive({
+ searchSchema: [],
+ tableSchema: [],
+ formSchema: [],
+ detailSchema: [],
+ printSchema: []
+ })
+
+ const searchSchema = filterSearchSchema(crudSchema)
+ allSchemas.searchSchema = searchSchema || []
+
+ const tableSchema = filterTableSchema(crudSchema)
+ allSchemas.tableSchema = tableSchema || []
+
+ const formSchema = filterFormSchema(crudSchema)
+ allSchemas.formSchema = formSchema
+
+ const detailSchema = filterDescriptionsSchema(crudSchema)
+ allSchemas.detailSchema = detailSchema
+
+ const printSchema = filterPrintSchema(crudSchema)
+ allSchemas.printSchema = printSchema
+
+ return {
+ allSchemas
+ }
+}
+
+// 过滤 Search 结构
+const filterSearchSchema = (crudSchema: VxeCrudSchema[]): VxeFormItemProps[] => {
+ const searchSchema: VxeFormItemProps[] = []
+ const { t } = useI18n()
+ eachTree(crudSchema, (schemaItem: VxeCrudSchema) => {
+ // 判断是否显示
+ if (schemaItem?.search?.show) {
+ let itemRenderName = schemaItem?.search?.itemRender?.name || '$input'
+ const options: any[] = []
+ let itemRender: FormItemRenderOptions = {
+ name: itemRenderName,
+ props: { placeholder: t('common.inputText') }
+ }
+ if (schemaItem.dictType) {
+ const allOptions = { label: '全部', value: '' }
+ options.push(allOptions)
+ getIntDictOptions(schemaItem.dictType).forEach((dict) => {
+ options.push(dict)
+ })
+ itemRender.options = options
+ if (!schemaItem.search.itemRender?.name) itemRenderName = '$select'
+ itemRender = {
+ name: itemRenderName,
+ options: options,
+ props: { placeholder: t('common.selectText') }
+ }
+ }
+
+ const searchSchemaItem = {
+ // 默认为 input
+ span: 6,
+ itemRender: itemRender,
+ ...schemaItem.search,
+ field: schemaItem.field,
+ title: schemaItem.search?.title || schemaItem.title
+ }
+ // 删除不必要的字段
+ delete searchSchemaItem.show
+
+ searchSchema.push(searchSchemaItem)
+ }
+ })
+ // 添加搜索按钮
+ const buttons: VxeFormItemProps = {
+ span: 24,
+ align: 'center',
+ collapseNode: true,
+ itemRender: {
+ name: '$buttons',
+ children: [
+ { props: { type: 'submit', content: t('common.query'), status: 'primary' } },
+ { props: { type: 'reset', content: t('common.reset') } }
+ ]
+ }
+ }
+ searchSchema.push(buttons)
+ return searchSchema
+}
+
+// 过滤 table 结构
+const filterTableSchema = (crudSchema: VxeCrudSchema[]): VxeGridPropTypes.Columns => {
+ const tableSchema: VxeGridPropTypes.Columns = []
+ eachTree(crudSchema, (schemaItem: VxeCrudSchema) => {
+ // 判断是否显示
+ if (schemaItem?.table?.show !== false) {
+ const tableSchemaItem = {
+ ...schemaItem.table,
+ field: schemaItem.field,
+ title: schemaItem.table?.title || schemaItem.title
+ }
+
+ // 删除不必要的字段
+ delete tableSchemaItem.show
+
+ tableSchema.push(tableSchemaItem)
+ }
+ })
+ return tableSchema
+}
+
+// 过滤 form 结构
+const filterFormSchema = (crudSchema: VxeCrudSchema[]): VxeFormItemProps[] => {
+ const formSchema: VxeFormItemProps[] = []
+ const { t } = useI18n()
+ eachTree(crudSchema, (schemaItem: VxeCrudSchema) => {
+ // 判断是否显示
+ if (schemaItem?.form?.show !== false) {
+ let itemRenderName = schemaItem?.form?.itemRender?.name || '$input'
+ let itemRender: FormItemRenderOptions = {
+ name: itemRenderName,
+ props: { placeholder: t('common.inputText') }
+ }
+ if (schemaItem.dictType) {
+ if (!(schemaItem.form && schemaItem.form.itemRender?.name)) itemRenderName = '$select'
+ itemRender = {
+ name: itemRenderName,
+ options: getIntDictOptions(schemaItem.dictType),
+ props: { placeholder: t('common.selectText') }
+ }
+ }
+ const formSchemaItem = {
+ // 默认为 input
+ itemRender: itemRender,
+ ...schemaItem.form,
+ span: schemaItem.form?.span || 12,
+ field: schemaItem.field,
+ title: schemaItem.form?.title || schemaItem.title
+ }
+
+ // 删除不必要的字段
+ delete formSchemaItem.show
+
+ formSchema.push(formSchemaItem)
+ }
+ })
+
+ return formSchema
+}
+
+// 过滤 descriptions 结构
+const filterDescriptionsSchema = (crudSchema: VxeCrudSchema[]): DescriptionsSchema[] => {
+ const descriptionsSchema: DescriptionsSchema[] = []
+
+ eachTree(crudSchema, (schemaItem: VxeCrudSchema) => {
+ // 判断是否显示
+ if (schemaItem?.detail?.show !== false) {
+ const descriptionsSchemaItem = {
+ ...schemaItem.detail,
+ field: schemaItem.field,
+ label: schemaItem.detail?.label || schemaItem.title
+ }
+
+ // 删除不必要的字段
+ delete descriptionsSchemaItem.show
+
+ descriptionsSchema.push(descriptionsSchemaItem)
+ }
+ })
+
+ return descriptionsSchema
+}
+
+// 过滤 打印 结构
+const filterPrintSchema = (crudSchema: VxeCrudSchema[]): any[] => {
+ const printSchema: any[] = []
+
+ eachTree(crudSchema, (schemaItem: VxeCrudSchema) => {
+ // 判断是否显示
+ if (schemaItem?.detail?.show !== false) {
+ const printSchemaItem = {
+ field: schemaItem.field
+ }
+
+ printSchema.push(printSchemaItem)
+ }
+ })
+
+ return printSchema
+}
diff --git a/yudao-ui-admin-vue3/src/hooks/web/useVxeGrid.ts b/yudao-ui-admin-vue3/src/hooks/web/useVxeGrid.ts
new file mode 100644
index 0000000000..0469a2207c
--- /dev/null
+++ b/yudao-ui-admin-vue3/src/hooks/web/useVxeGrid.ts
@@ -0,0 +1,60 @@
+import { reactive } from 'vue'
+import { VxeGridProps } from 'vxe-table'
+
+export const useVxeGrid = (allSchemas, getPageApi) => {
+ const gridOptions = reactive({
+ loading: false,
+ height: 800,
+ rowConfig: {
+ keyField: 'id',
+ isHover: true
+ },
+ toolbarConfig: {
+ custom: true,
+ slots: { buttons: 'toolbar_buttons' }
+ },
+ printConfig: {
+ columns: allSchemas.printSchema
+ },
+ formConfig: {
+ titleWidth: 100,
+ titleAlign: 'right',
+ items: allSchemas.searchSchema
+ },
+ columns: allSchemas.tableSchema,
+ pagerConfig: {
+ border: false,
+ background: false,
+ perfect: true,
+ pageSize: 10,
+ pagerCount: 7,
+ pageSizes: [5, 10, 15, 20, 50, 100, 200, 500],
+ layouts: [
+ 'PrevJump',
+ 'PrevPage',
+ 'Jump',
+ 'PageCount',
+ 'NextPage',
+ 'NextJump',
+ 'Sizes',
+ 'Total'
+ ]
+ },
+ proxyConfig: {
+ seq: true, // 启用动态序号代理(分页之后索引自动计算为当前页的起始序号)
+ form: true, // 启用表单代理,当点击表单提交按钮时会自动触发 reload 行为
+ props: { result: 'list', total: 'total' },
+ ajax: {
+ query: ({ page, form }) => {
+ const queryParams = Object.assign({}, form)
+ queryParams.pageSize = page.pageSize
+ queryParams.pageNo = page.currentPage
+ return new Promise(async (resolve) => {
+ resolve(await getPageApi(queryParams))
+ })
+ }
+ }
+ }
+ })
+ return gridOptions
+}
diff --git a/yudao-ui-admin-vue3/src/plugins/vxeTable/index.ts b/yudao-ui-admin-vue3/src/plugins/vxeTable/index.ts
index bfe355d05f..b3e3cc8ac2 100644
--- a/yudao-ui-admin-vue3/src/plugins/vxeTable/index.ts
+++ b/yudao-ui-admin-vue3/src/plugins/vxeTable/index.ts
@@ -60,7 +60,14 @@ VXETable.setup({
autoResize: true, // 自动监听父元素的变化去重新计算表格
resizable: true, // 列是否允许拖动列宽调整大小
emptyText: '暂无数据', // 空表单
- highlightHoverRow: true // 自动监听父元素的变化去重新计算表格
+ highlightHoverRow: true, // 自动监听父元素的变化去重新计算表格
+ treeConfig: {
+ rowField: 'id',
+ parentField: 'parentId',
+ children: 'children',
+ indent: 20,
+ showIcon: true
+ }
},
grid: {
toolbarConfig: {
@@ -107,8 +114,10 @@ VXETable.setup({
titleColon: true // 是否显示标题冒号
},
modal: {
- width: 600, // 窗口的宽度
- height: 400, // 窗口的高度
+ width: 800, // 窗口的宽度
+ height: 600, // 窗口的高度
+ minWidth: 460,
+ minHeight: 320,
showZoom: true, // 标题是否标显示最大化与还原按钮
resize: true, // 是否允许窗口边缘拖动调整窗口大小
marginSize: 0, // 只对 resize 启用后有效,用于设置可拖动界限范围,如果为负数则允许拖动超出屏幕边界
@@ -127,13 +136,28 @@ VXETable.setup({
: XEUtils.toFormatString(XEUtils.get(enUS, key), args)
}
})
-// 格式金额,默认2位数
-VXETable.formats.add('formatAmount', ({ cellValue }, digits = 2) => {
- return XEUtils.commafy(XEUtils.toNumber(cellValue), { digits })
-})
-// 格式日期,默认 yyyy-MM-dd HH:mm:ss
-VXETable.formats.add('formatDate', ({ cellValue }, format = 'yyyy-MM-dd HH:mm:ss') => {
- return XEUtils.toDateString(cellValue, format)
+// 自定义全局的格式化处理函数
+VXETable.formats.mixin({
+ // 格式日期,默认 yyyy-MM-dd HH:mm:ss
+ formatDate({ cellValue }, format) {
+ return XEUtils.toDateString(cellValue, format || 'yyyy-MM-dd HH:mm:ss')
+ },
+ // 四舍五入金额,每隔3位逗号分隔,默认2位数
+ formatAmount({ cellValue }, digits = 2) {
+ return XEUtils.commafy(Number(cellValue), { digits })
+ },
+ // 格式化银行卡,默认每4位空格隔开
+ formatBankcard({ cellValue }) {
+ return XEUtils.commafy(XEUtils.toValueString(cellValue), { spaceNumber: 4, separator: ' ' })
+ },
+ // 四舍五入,默认两位数
+ formatFixedNumber({ cellValue }, digits = 2) {
+ return XEUtils.toFixed(XEUtils.round(cellValue, digits), digits)
+ },
+ // 向下舍入,默认两位数
+ formatCutNumber({ cellValue }, digits = 2) {
+ return XEUtils.toFixed(XEUtils.floor(cellValue, digits), digits)
+ }
})
export const setupVxeTable = (app: App) => {
// 表格功能
diff --git a/yudao-ui-admin-vue3/src/router/index.ts b/yudao-ui-admin-vue3/src/router/index.ts
index 6cdec5ee10..2dc045062d 100644
--- a/yudao-ui-admin-vue3/src/router/index.ts
+++ b/yudao-ui-admin-vue3/src/router/index.ts
@@ -10,7 +10,7 @@ import { usePermissionStoreWithOut } from '@/store/modules/permission'
import { useDictStoreWithOut } from '@/store/modules/dict'
import { useUserStoreWithOut } from '@/store/modules/user'
import { listSimpleDictDataApi } from '@/api/system/dict/dict.data'
-import { isRelogin } from '@/config/axios'
+import { isRelogin } from '@/config/axios/service'
import { getInfoApi } from '@/api/login'
const { start, done } = useNProgress()
diff --git a/yudao-ui-admin-vue3/src/types/table.d.ts b/yudao-ui-admin-vue3/src/types/table.d.ts
index 5adf91f473..3294234b87 100644
--- a/yudao-ui-admin-vue3/src/types/table.d.ts
+++ b/yudao-ui-admin-vue3/src/types/table.d.ts
@@ -4,6 +4,12 @@ export type TableColumn = {
children?: TableColumn[]
} & Recordable
+export type VxeTableColumn = {
+ field: string
+ title?: string
+ children?: TableColumn[]
+} & Recordable
+
export type TableSlotDefault = {
row: Recordable
column: TableColumn
diff --git a/yudao-ui-admin-vue3/src/views/system/menu/index.vue b/yudao-ui-admin-vue3/src/views/system/menu/index.vue
index 73cb785878..01f45da5a7 100644
--- a/yudao-ui-admin-vue3/src/views/system/menu/index.vue
+++ b/yudao-ui-admin-vue3/src/views/system/menu/index.vue
@@ -1,16 +1,262 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ row.name }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{ t('common.query') }}
-
-
-
- {{ t('common.reset') }}
-
-
-
-
-
-
-
-
- {{ t('action.add') }}
-
-
-
-
-
-
- {{ scope.row.name }}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{ dayjs(scope.row.createTime).format('YYYY-MM-DD HH:mm:ss') }}
-
-
-
-
-
- {{ t('action.edit') }}
-
-
- {{ t('action.del') }}
-
-
-
-
-
-
-
-
-
diff --git a/yudao-ui-admin-vue3/src/views/system/post/index.vue b/yudao-ui-admin-vue3/src/views/system/post/index.vue
index c7b35b9c82..6ba1e2be70 100644
--- a/yudao-ui-admin-vue3/src/views/system/post/index.vue
+++ b/yudao-ui-admin-vue3/src/views/system/post/index.vue
@@ -1,24 +1,100 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ dayjs(row.createTime).format('YYYY-MM-DD HH:mm:ss') }}
+
+
+
+
+
+
+
+
+
-
-
-
-
-
- {{ t('action.add') }}
-
-
-
-
-
-
-
- {{ t('action.edit') }}
-
-
- {{ t('action.detail') }}
-
-
- {{ t('action.del') }}
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{ dayjs(row.createTime).format('YYYY-MM-DD HH:mm:ss') }}
-
-
-
-
-
diff --git a/yudao-ui-admin-vue3/src/views/system/post/indexd.vue b/yudao-ui-admin-vue3/src/views/system/post/indexd.vue
deleted file mode 100644
index d8808fc9fc..0000000000
--- a/yudao-ui-admin-vue3/src/views/system/post/indexd.vue
+++ /dev/null
@@ -1,194 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
- {{ t('action.add') }}
-
-
- {{ t('action.export') }}
-
-
-
-
-
-
-
-
- {{ dayjs(row.createTime).format('YYYY-MM-DD HH:mm:ss') }}
-
-
-
- {{ t('action.edit') }}
-
-
- {{ t('action.detail') }}
-
-
- {{ t('action.del') }}
-
-
-
-
-
-
-
diff --git a/yudao-ui-admin-vue3/src/views/system/post/post.data.ts b/yudao-ui-admin-vue3/src/views/system/post/post.data.ts
index a3383241f1..6b19a98f1c 100644
--- a/yudao-ui-admin-vue3/src/views/system/post/post.data.ts
+++ b/yudao-ui-admin-vue3/src/views/system/post/post.data.ts
@@ -1,7 +1,7 @@
import { reactive } from 'vue'
import { useI18n } from '@/hooks/web/useI18n'
import { required } from '@/utils/formRules'
-import { CrudSchema, useCrudSchemas } from '@/hooks/web/useCrudSchemas'
+import { VxeCrudSchema, useVxeCrudSchemas } from '@/hooks/web/useVxeCrudSchemas'
import { DICT_TYPE } from '@/utils/dict'
const { t } = useI18n() // 国际化
@@ -13,9 +13,9 @@ export const rules = reactive({
})
// CrudSchema
-const crudSchemas = reactive([
+const crudSchemas = reactive([
{
- label: t('common.index'),
+ title: t('common.index'),
field: 'id',
type: 'index',
form: {
@@ -26,42 +26,54 @@ const crudSchemas = reactive([
}
},
{
- label: '岗位名称',
+ title: '岗位名称',
field: 'name',
search: {
show: true
}
},
{
- label: '岗位编码',
+ title: '岗位编码',
field: 'code',
search: {
show: true
}
},
{
- label: '岗位顺序',
+ title: '岗位顺序',
field: 'sort'
},
{
- label: t('common.status'),
+ title: t('common.status'),
field: 'status',
dictType: DICT_TYPE.COMMON_STATUS,
+ table: {
+ slots: {
+ default: 'status_default'
+ }
+ },
search: {
show: true
}
},
{
- label: t('common.createTime'),
+ title: t('common.createTime'),
field: 'createTime',
form: {
show: false
}
},
{
- label: t('table.action'),
+ title: t('table.action'),
field: 'action',
width: '240px',
+ table: {
+ width: '240px',
+ showOverflow: true,
+ slots: {
+ default: 'action_default'
+ }
+ },
form: {
show: false
},
@@ -70,4 +82,4 @@ const crudSchemas = reactive([
}
}
])
-export const { allSchemas } = useCrudSchemas(crudSchemas)
+export const { allSchemas } = useVxeCrudSchemas(crudSchemas)