Commit dc84ebde9ddc476c405c4a14f410bcdaf40b5387
1 parent
1a45526a
perf: vxe demo
Showing
5 changed files
with
113 additions
and
68 deletions
yudao-ui-admin-vue3/src/api/system/post/types.ts
yudao-ui-admin-vue3/src/hooks/web/useVxeCrudSchemas.ts
| ... | ... | @@ -3,6 +3,7 @@ import { getIntDictOptions } from '@/utils/dict' |
| 3 | 3 | import { reactive } from 'vue' |
| 4 | 4 | import { |
| 5 | 5 | FormItemRenderOptions, |
| 6 | + VxeColumnPropTypes, | |
| 6 | 7 | VxeFormItemProps, |
| 7 | 8 | VxeGridPropTypes, |
| 8 | 9 | VxeTableDefines |
| ... | ... | @@ -10,10 +11,13 @@ import { |
| 10 | 11 | import { eachTree } from 'xe-utils' |
| 11 | 12 | import { useI18n } from '@/hooks/web/useI18n' |
| 12 | 13 | import { VxeTableColumn } from '@/types/table' |
| 14 | +import { FormSchema } from '@/types/form' | |
| 15 | +import { ComponentOptions } from '@/types/components' | |
| 13 | 16 | |
| 14 | 17 | export type VxeCrudSchema = Omit<VxeTableColumn, 'children'> & { |
| 15 | 18 | field: string |
| 16 | 19 | title?: string |
| 20 | + formatter?: VxeColumnPropTypes.Formatter | |
| 17 | 21 | search?: CrudSearchParams |
| 18 | 22 | table?: CrudTableParams |
| 19 | 23 | form?: CrudFormParams |
| ... | ... | @@ -35,7 +39,7 @@ type CrudTableParams = { |
| 35 | 39 | type CrudFormParams = { |
| 36 | 40 | // 是否显示表单项 |
| 37 | 41 | show?: boolean |
| 38 | -} & Omit<VxeFormItemProps, 'field'> | |
| 42 | +} & Omit<FormSchema, 'field'> | |
| 39 | 43 | |
| 40 | 44 | type CrudDescriptionsParams = { |
| 41 | 45 | // 是否显示表单项 |
| ... | ... | @@ -47,10 +51,10 @@ type CrudPrintParams = { |
| 47 | 51 | show?: boolean |
| 48 | 52 | } & Omit<VxeTableDefines.ColumnInfo[], 'field'> |
| 49 | 53 | |
| 50 | -interface VxeAllSchemas { | |
| 54 | +export type VxeAllSchemas = { | |
| 51 | 55 | searchSchema: VxeFormItemProps[] |
| 52 | 56 | tableSchema: VxeGridPropTypes.Columns |
| 53 | - formSchema: VxeFormItemProps[] | |
| 57 | + formSchema: FormSchema[] | |
| 54 | 58 | detailSchema: DescriptionsSchema[] |
| 55 | 59 | printSchema: VxeTableDefines.ColumnInfo[] |
| 56 | 60 | } |
| ... | ... | @@ -160,6 +164,9 @@ const filterTableSchema = (crudSchema: VxeCrudSchema[]): VxeGridPropTypes.Column |
| 160 | 164 | field: schemaItem.field, |
| 161 | 165 | title: schemaItem.table?.title || schemaItem.title |
| 162 | 166 | } |
| 167 | + if (schemaItem?.formatter) { | |
| 168 | + tableSchemaItem.formatter = schemaItem.formatter | |
| 169 | + } | |
| 163 | 170 | |
| 164 | 171 | // 删除不必要的字段 |
| 165 | 172 | delete tableSchemaItem.show |
| ... | ... | @@ -171,32 +178,31 @@ const filterTableSchema = (crudSchema: VxeCrudSchema[]): VxeGridPropTypes.Column |
| 171 | 178 | } |
| 172 | 179 | |
| 173 | 180 | // 过滤 form 结构 |
| 174 | -const filterFormSchema = (crudSchema: VxeCrudSchema[]): VxeFormItemProps[] => { | |
| 175 | - const formSchema: VxeFormItemProps[] = [] | |
| 176 | - const { t } = useI18n() | |
| 181 | +const filterFormSchema = (crudSchema: VxeCrudSchema[]): FormSchema[] => { | |
| 182 | + const formSchema: FormSchema[] = [] | |
| 183 | + | |
| 177 | 184 | eachTree(crudSchema, (schemaItem: VxeCrudSchema) => { |
| 178 | 185 | // 判断是否显示 |
| 179 | 186 | if (schemaItem?.form?.show !== false) { |
| 180 | - let itemRenderName = schemaItem?.form?.itemRender?.name || '$input' | |
| 181 | - let itemRender: FormItemRenderOptions = { | |
| 182 | - name: itemRenderName, | |
| 183 | - props: { placeholder: t('common.inputText') } | |
| 184 | - } | |
| 187 | + let component = schemaItem?.form?.component || 'Input' | |
| 188 | + const options: ComponentOptions[] = [] | |
| 189 | + let comonentProps = {} | |
| 185 | 190 | if (schemaItem.dictType) { |
| 186 | - if (!(schemaItem.form && schemaItem.form.itemRender?.name)) itemRenderName = '$select' | |
| 187 | - itemRender = { | |
| 188 | - name: itemRenderName, | |
| 189 | - options: getIntDictOptions(schemaItem.dictType), | |
| 190 | - props: { placeholder: t('common.selectText') } | |
| 191 | + getIntDictOptions(schemaItem.dictType).forEach((dict) => { | |
| 192 | + options.push(dict) | |
| 193 | + }) | |
| 194 | + comonentProps = { | |
| 195 | + options: options | |
| 191 | 196 | } |
| 197 | + if (!(schemaItem.form && schemaItem.form.component)) component = 'Select' | |
| 192 | 198 | } |
| 193 | 199 | const formSchemaItem = { |
| 194 | 200 | // 默认为 input |
| 195 | - itemRender: itemRender, | |
| 201 | + component: component, | |
| 202 | + componentProps: comonentProps, | |
| 196 | 203 | ...schemaItem.form, |
| 197 | - span: schemaItem.form?.span || 12, | |
| 198 | 204 | field: schemaItem.field, |
| 199 | - title: schemaItem.form?.title || schemaItem.title | |
| 205 | + label: schemaItem.form?.label || schemaItem.title | |
| 200 | 206 | } |
| 201 | 207 | |
| 202 | 208 | // 删除不必要的字段 | ... | ... |
yudao-ui-admin-vue3/src/hooks/web/useVxeGrid.ts
| 1 | 1 | import { computed, reactive } from 'vue' |
| 2 | 2 | import { VxeGridProps } from 'vxe-table' |
| 3 | 3 | import { useAppStore } from '@/store/modules/app' |
| 4 | +import { VxeAllSchemas } from './useVxeCrudSchemas' | |
| 5 | +import { useI18n } from '@/hooks/web/useI18n' | |
| 6 | +import { useMessage } from '@/hooks/web/useMessage' | |
| 7 | + | |
| 8 | +const { t } = useI18n() | |
| 9 | +const message = useMessage() // 消息弹窗 | |
| 10 | + | |
| 11 | +interface UseVxeGridConfig<T = any> { | |
| 12 | + allSchemas: VxeAllSchemas | |
| 13 | + getListApi: (option: any) => Promise<T> | |
| 14 | + delListApi?: (option: any) => Promise<T> | |
| 15 | + exportListApi?: (option: any) => Promise<T> | |
| 16 | +} | |
| 4 | 17 | |
| 5 | 18 | const appStore = useAppStore() |
| 6 | 19 | |
| ... | ... | @@ -14,36 +27,40 @@ const currentSize = computed(() => { |
| 14 | 27 | } |
| 15 | 28 | }) |
| 16 | 29 | |
| 17 | -export const useVxeGrid = (allSchemas, getPageApi) => { | |
| 30 | +export const useVxeGrid = <T = any>(config?: UseVxeGridConfig<T>) => { | |
| 18 | 31 | const gridOptions = reactive<VxeGridProps>({ |
| 19 | - loading: false, | |
| 32 | + loading: true, | |
| 20 | 33 | size: currentSize.value, |
| 21 | 34 | height: 800, |
| 22 | 35 | rowConfig: { |
| 23 | - keyField: 'id', | |
| 24 | - isCurrent: true, | |
| 25 | - isHover: true | |
| 36 | + isCurrent: true, // 当鼠标点击行时,是否要高亮当前行 | |
| 37 | + isHover: true // 当鼠标移到行时,是否要高亮当前行 | |
| 38 | + }, | |
| 39 | + showOverflow: 'tooltip', // 当内容溢出时显示为省略号 | |
| 40 | + tooltipConfig: { | |
| 41 | + showAll: true // 开启全表工具提示 | |
| 26 | 42 | }, |
| 27 | 43 | toolbarConfig: { |
| 28 | 44 | custom: true, |
| 29 | 45 | slots: { buttons: 'toolbar_buttons' } |
| 30 | 46 | }, |
| 31 | 47 | printConfig: { |
| 32 | - columns: allSchemas.printSchema | |
| 48 | + columns: config?.allSchemas.printSchema | |
| 33 | 49 | }, |
| 34 | 50 | formConfig: { |
| 35 | 51 | titleWidth: 100, |
| 36 | 52 | titleAlign: 'right', |
| 37 | - items: allSchemas.searchSchema | |
| 53 | + items: config?.allSchemas.searchSchema | |
| 38 | 54 | }, |
| 39 | - columns: allSchemas.tableSchema, | |
| 55 | + columns: config?.allSchemas.tableSchema, | |
| 40 | 56 | pagerConfig: { |
| 41 | - border: false, | |
| 42 | - background: false, | |
| 43 | - perfect: true, | |
| 44 | - pageSize: 10, | |
| 45 | - pagerCount: 7, | |
| 46 | - pageSizes: [5, 10, 15, 20, 50, 100, 200, 500], | |
| 57 | + border: false, // 带边框 | |
| 58 | + background: true, // 带背景颜色 | |
| 59 | + perfect: true, // 配套的样式 | |
| 60 | + pageSize: 10, // 每页大小 | |
| 61 | + pagerCount: 7, // 显示页码按钮的数量 | |
| 62 | + autoHidden: true, // 当只有一页时自动隐藏 | |
| 63 | + pageSizes: [5, 10, 15, 20, 50, 100, 200, 500], // 每页大小选项列表 | |
| 47 | 64 | layouts: [ |
| 48 | 65 | 'PrevJump', |
| 49 | 66 | 'PrevPage', |
| ... | ... | @@ -61,17 +78,25 @@ export const useVxeGrid = (allSchemas, getPageApi) => { |
| 61 | 78 | props: { result: 'list', total: 'total' }, |
| 62 | 79 | ajax: { |
| 63 | 80 | query: ({ page, form }) => { |
| 64 | - gridOptions.loading = true | |
| 65 | 81 | const queryParams = Object.assign({}, form) |
| 66 | 82 | queryParams.pageSize = page.pageSize |
| 67 | 83 | queryParams.pageNo = page.currentPage |
| 68 | 84 | gridOptions.loading = false |
| 69 | 85 | return new Promise(async (resolve) => { |
| 70 | - resolve(await getPageApi(queryParams)) | |
| 86 | + resolve(await config?.getListApi(queryParams)) | |
| 71 | 87 | }) |
| 72 | 88 | } |
| 73 | 89 | } |
| 74 | 90 | } |
| 75 | 91 | }) |
| 76 | - return gridOptions | |
| 92 | + const delList = (ids: string | number | string[] | number[]) => { | |
| 93 | + message.delConfirm().then(() => { | |
| 94 | + config?.delListApi && config?.delListApi(ids) | |
| 95 | + message.success(t('common.delSuccess')) | |
| 96 | + }) | |
| 97 | + } | |
| 98 | + return { | |
| 99 | + gridOptions, | |
| 100 | + delList | |
| 101 | + } | |
| 77 | 102 | } | ... | ... |
yudao-ui-admin-vue3/src/views/system/post/index.vue
| ... | ... | @@ -44,12 +44,11 @@ |
| 44 | 44 | <XModal id="postModel" v-model="dialogVisible" :title="dialogTitle"> |
| 45 | 45 | <template #default> |
| 46 | 46 | <!-- 对话框(添加 / 修改) --> |
| 47 | - <vxe-form | |
| 48 | - ref="xForm" | |
| 47 | + <Form | |
| 49 | 48 | v-if="['create', 'update'].includes(actionType)" |
| 50 | - :data="formData" | |
| 51 | - :items="formItems" | |
| 49 | + :schema="allSchemas.formSchema" | |
| 52 | 50 | :rules="rules" |
| 51 | + ref="formRef" | |
| 53 | 52 | /> |
| 54 | 53 | <Descriptions |
| 55 | 54 | v-if="actionType === 'detail'" |
| ... | ... | @@ -77,7 +76,7 @@ |
| 77 | 76 | </XModal> |
| 78 | 77 | </template> |
| 79 | 78 | <script setup lang="ts"> |
| 80 | -import { ref } from 'vue' | |
| 79 | +import { ref, unref } from 'vue' | |
| 81 | 80 | import dayjs from 'dayjs' |
| 82 | 81 | import { DICT_TYPE } from '@/utils/dict' |
| 83 | 82 | import * as PostApi from '@/api/system/post' |
| ... | ... | @@ -86,20 +85,22 @@ import { rules, allSchemas } from './post.data' |
| 86 | 85 | import { useI18n } from '@/hooks/web/useI18n' |
| 87 | 86 | import { useMessage } from '@/hooks/web/useMessage' |
| 88 | 87 | import { useVxeGrid } from '@/hooks/web/useVxeGrid' |
| 89 | -import { VxeFormEvents, VxeFormInstance, VxeFormItemProps, VxeGridInstance } from 'vxe-table' | |
| 88 | +import { VxeFormEvents, VxeGridInstance } from 'vxe-table' | |
| 89 | +import { FormExpose } from '@/components/Form' | |
| 90 | 90 | |
| 91 | 91 | const { t } = useI18n() // 国际化 |
| 92 | 92 | const message = useMessage() // 消息弹窗 |
| 93 | 93 | const xGrid = ref<VxeGridInstance>() |
| 94 | -const xForm = ref<VxeFormInstance>() | |
| 94 | +const formRef = ref<FormExpose>() // 表单 Ref | |
| 95 | 95 | const dialogVisible = ref(false) // 是否显示弹出层 |
| 96 | 96 | const dialogTitle = ref('edit') // 弹出层标题 |
| 97 | 97 | const actionType = ref('') // 操作按钮的类型 |
| 98 | 98 | const actionLoading = ref(false) // 按钮Loading |
| 99 | 99 | |
| 100 | -const gridOptions = useVxeGrid(allSchemas, PostApi.getPostPageApi) | |
| 101 | -const formData = ref<PostVO>() | |
| 102 | -const formItems = ref<VxeFormItemProps[]>(allSchemas.formSchema) | |
| 100 | +const { gridOptions } = useVxeGrid<PostVO>({ | |
| 101 | + allSchemas: allSchemas, | |
| 102 | + getListApi: PostApi.getPostPageApi | |
| 103 | +}) | |
| 103 | 104 | // 设置标题 |
| 104 | 105 | const setDialogTile = (type: string) => { |
| 105 | 106 | dialogTitle.value = t('action.' + type) |
| ... | ... | @@ -116,7 +117,8 @@ const handleDetail = (row: PostVO) => { |
| 116 | 117 | // 新增操作 |
| 117 | 118 | const handleCreate = () => { |
| 118 | 119 | setDialogTile('create') |
| 119 | - formData.value = undefined | |
| 120 | + // 重置表单 | |
| 121 | + unref(formRef)?.getElFormRef()?.resetFields() | |
| 120 | 122 | } |
| 121 | 123 | |
| 122 | 124 | // 修改操作 |
| ... | ... | @@ -124,10 +126,10 @@ const handleUpdate = async (rowId: number) => { |
| 124 | 126 | setDialogTile('update') |
| 125 | 127 | // 设置数据 |
| 126 | 128 | const res = await PostApi.getPostApi(rowId) |
| 127 | - formData.value = res | |
| 129 | + unref(formRef)?.setValues(res) | |
| 128 | 130 | } |
| 129 | 131 | // 删除操作 |
| 130 | -const handleDelete = (rowId: number) => { | |
| 132 | +const handleDelete = async (rowId: number) => { | |
| 131 | 133 | message |
| 132 | 134 | .delConfirm() |
| 133 | 135 | .then(async () => { |
| ... | ... | @@ -140,22 +142,28 @@ const handleDelete = (rowId: number) => { |
| 140 | 142 | } |
| 141 | 143 | // 提交按钮 |
| 142 | 144 | const submitForm: VxeFormEvents.Submit = async () => { |
| 143 | - actionLoading.value = true | |
| 144 | - // 提交请求 | |
| 145 | - try { | |
| 146 | - const data = formData.value as PostVO | |
| 147 | - if (actionType.value === 'create') { | |
| 148 | - await PostApi.createPostApi(data) | |
| 149 | - message.success(t('common.createSuccess')) | |
| 150 | - } else { | |
| 151 | - await PostApi.updatePostApi(data) | |
| 152 | - message.success(t('common.updateSuccess')) | |
| 145 | + const elForm = unref(formRef)?.getElFormRef() | |
| 146 | + if (!elForm) return | |
| 147 | + elForm.validate(async (valid) => { | |
| 148 | + if (valid) { | |
| 149 | + actionLoading.value = true | |
| 150 | + // 提交请求 | |
| 151 | + try { | |
| 152 | + const data = unref(formRef)?.formModel as PostVO | |
| 153 | + if (actionType.value === 'create') { | |
| 154 | + await PostApi.createPostApi(data) | |
| 155 | + message.success(t('common.createSuccess')) | |
| 156 | + } else { | |
| 157 | + await PostApi.updatePostApi(data) | |
| 158 | + message.success(t('common.updateSuccess')) | |
| 159 | + } | |
| 160 | + // 操作成功,重新加载列表 | |
| 161 | + dialogVisible.value = false | |
| 162 | + } finally { | |
| 163 | + actionLoading.value = false | |
| 164 | + xGrid.value?.commitProxy('query') | |
| 165 | + } | |
| 153 | 166 | } |
| 154 | - // 操作成功,重新加载列表 | |
| 155 | - dialogVisible.value = false | |
| 156 | - } finally { | |
| 157 | - actionLoading.value = false | |
| 158 | - xGrid.value?.commitProxy('query') | |
| 159 | - } | |
| 167 | + }) | |
| 160 | 168 | } |
| 161 | 169 | </script> | ... | ... |
yudao-ui-admin-vue3/src/views/system/post/post.data.ts
| ... | ... | @@ -57,8 +57,16 @@ const crudSchemas = reactive<VxeCrudSchema[]>([ |
| 57 | 57 | } |
| 58 | 58 | }, |
| 59 | 59 | { |
| 60 | + title: '备注', | |
| 61 | + field: 'remark', | |
| 62 | + table: { | |
| 63 | + show: false | |
| 64 | + } | |
| 65 | + }, | |
| 66 | + { | |
| 60 | 67 | title: t('common.createTime'), |
| 61 | 68 | field: 'createTime', |
| 69 | + formatter: 'formatDate', | |
| 62 | 70 | form: { |
| 63 | 71 | show: false |
| 64 | 72 | } |
| ... | ... | @@ -66,10 +74,8 @@ const crudSchemas = reactive<VxeCrudSchema[]>([ |
| 66 | 74 | { |
| 67 | 75 | title: t('table.action'), |
| 68 | 76 | field: 'action', |
| 69 | - width: '240px', | |
| 70 | 77 | table: { |
| 71 | 78 | width: '240px', |
| 72 | - showOverflow: true, | |
| 73 | 79 | slots: { |
| 74 | 80 | default: 'action_default' |
| 75 | 81 | } | ... | ... |