diff --git a/src/components/ContentDetailWrap/src/ContentDetailWrap.vue b/src/components/ContentDetailWrap/src/ContentDetailWrap.vue index 7591342..29b4ff7 100644 --- a/src/components/ContentDetailWrap/src/ContentDetailWrap.vue +++ b/src/components/ContentDetailWrap/src/ContentDetailWrap.vue @@ -1,11 +1,7 @@ <script setup lang="ts"> -import { ElCard, ElButton } from 'element-plus' +import { ElCard } from 'element-plus' import { propTypes } from '@/utils/propTypes' import { useDesign } from '@/hooks/web/useDesign' -import { ref, onMounted, defineEmits } from 'vue' -import { Sticky } from '@/components/Sticky' -import { useI18n } from '@/hooks/web/useI18n' -const { t } = useI18n() const { getPrefixCls } = useDesign() @@ -15,45 +11,15 @@ defineProps({ title: propTypes.string.def(''), message: propTypes.string.def('') }) -const emit = defineEmits(['back']) -const offset = ref(85) -const contentDetailWrap = ref() -onMounted(() => { - offset.value = contentDetailWrap.value.getBoundingClientRect().top -}) </script> <template> - <div :class="[`${prefixCls}-container`, 'relative bg-[#fff]']" ref="contentDetailWrap"> - <Sticky :offset="offset"> - <div - :class="[ - `${prefixCls}-header`, - 'flex b-b-1 h-50px items-center text-center bg-white pr-10px' - ]" - > - <div :class="[`${prefixCls}-header__back`, 'flex pl-10px pr-10px ']"> - <el-button @click="emit('back')"> - <Icon icon="ep:arrow-left" class="mr-5px" /> - {{ t('common.back') }} - </el-button> - </div> - <div :class="[`${prefixCls}-header__title`, 'flex flex-1 justify-center']"> - <slot name="title"> - <label class="text-16px font-700">{{ title }}</label> - </slot> - </div> - <div :class="[`${prefixCls}-header__right`, 'flex pl-10px pr-10px']"> - <slot name="right"></slot> - </div> + <div :class="[`${prefixCls}-container`, 'relative']"> + <ElCard :class="[`${prefixCls}-body`, 'mb-20px']" shadow="never"> + <div class="mb-20px pb-20px" style="border-bottom: 1px solid var(--el-border-color)"> + <slot name="header"></slot> </div> - </Sticky> - <div style="padding: var(--app-content-padding)"> - <ElCard :class="[`${prefixCls}-body`, 'mb-20px']" shadow="never"> - <div> - <slot></slot> - </div> - </ElCard> - </div> + <slot></slot> + </ElCard> </div> </template> diff --git a/src/components/Descriptions/src/Descriptions.vue b/src/components/Descriptions/src/Descriptions.vue index ef5fd6b..effaca7 100644 --- a/src/components/Descriptions/src/Descriptions.vue +++ b/src/components/Descriptions/src/Descriptions.vue @@ -113,7 +113,7 @@ export default defineComponent({ label: () => (item.slots?.label ? item.slots?.label(item) : item.label), default: () => item.slots?.default - ? item.slots?.default(item) + ? item.slots?.default(props.data) : props.data[item.field] }} </ElDescriptionsItem> diff --git a/src/components/Form/src/Form.vue b/src/components/Form/src/Form.vue index bd2c756..4e23ff8 100644 --- a/src/components/Form/src/Form.vue +++ b/src/components/Form/src/Form.vue @@ -111,7 +111,7 @@ export default defineComponent({ const formItemComponents = ref({}) // 表单数据 - const formModel = ref<Recordable>({}) + const formModel = ref<Recordable>(props.model) onMounted(() => { emit('register', unref(elFormRef)?.$parent, unref(elFormRef)) diff --git a/src/components/Search/src/Search.vue b/src/components/Search/src/Search.vue index 340f8b5..354fefe 100644 --- a/src/components/Search/src/Search.vue +++ b/src/components/Search/src/Search.vue @@ -48,7 +48,7 @@ const emit = defineEmits(['search', 'reset', 'register', 'validate']) const visible = ref(true) // 表单数据 -const formModel = ref<Recordable>({}) +const formModel = ref<Recordable>(props.model) const newSchema = computed(() => { const propsComputed = unref(getProps) @@ -179,7 +179,7 @@ const setSchema = (schemaProps: FormSetProps[]) => { // 对表单赋值 const setValues = async (data: Recordable = {}) => { - formModel.value = Object.assign(unref(formModel), data) + formModel.value = Object.assign(props.model, unref(formModel), data) const formExpose = await getFormExpose() formExpose?.setValues(data) } diff --git a/src/hooks/web/useCrudSchemas.ts b/src/hooks/web/useCrudSchemas.ts index e5ca399..5cef69b 100644 --- a/src/hooks/web/useCrudSchemas.ts +++ b/src/hooks/web/useCrudSchemas.ts @@ -1,12 +1,8 @@ import { reactive } from 'vue' import { eachTree, treeMap, filter } from '@/utils/tree' -import { findIndex } from '@/utils' -import { useDictStoreWithOut } from '@/store/modules/dict' -import { useI18n } from '@/hooks/web/useI18n' -import type { AxiosPromise } from 'axios' -import { FormSchema } from '@/types/form' -import { TableColumn } from '@/types/table' -import { DescriptionsSchema } from '@/types/descriptions' +import { FormSchema } from '@/components/Form' +import { TableColumn } from '@/components/Table' +import { DescriptionsSchema } from '@/components/Descriptions' export type CrudSchema = Omit<TableColumn, 'children'> & { search?: CrudSearchParams @@ -16,39 +12,25 @@ export type CrudSchema = Omit<TableColumn, 'children'> & { children?: CrudSchema[] } -type CrudSearchParams = { +interface CrudSearchParams extends Omit<FormSchema, 'field'> { // 是否显示在查询项 show?: boolean - // 字典名称,会去取全局的字典 - dictName?: string - // 接口 - api?: () => Promise<any> - // 搜索字段 - field?: string -} & Omit<FormSchema, 'field'> +} -type CrudTableParams = { +interface CrudTableParams extends Omit<TableColumn, 'field'> { // 是否显示表头 show?: boolean -} & Omit<FormSchema, 'field'> +} -type CrudFormParams = { - // 字典名称,会去取全局的字典 - dictName?: string - // 接口 - api?: () => Promise<any> +interface CrudFormParams extends Omit<FormSchema, 'field'> { // 是否显示表单项 show?: boolean -} & Omit<FormSchema, 'field'> +} -type CrudDescriptionsParams = { +interface CrudDescriptionsParams extends Omit<DescriptionsSchema, 'field'> { // 是否显示表单项 show?: boolean -} & Omit<DescriptionsSchema, 'field'> - -const dictStore = useDictStoreWithOut() - -const { t } = useI18n() +} interface AllSchemas { searchSchema: FormSchema[] @@ -71,13 +53,14 @@ export const useCrudSchemas = ( detailSchema: [] }) - const searchSchema = filterSearchSchema(crudSchema, allSchemas) + const searchSchema = filterSearchSchema(crudSchema) + // @ts-ignore allSchemas.searchSchema = searchSchema || [] const tableColumns = filterTableSchema(crudSchema) allSchemas.tableColumns = tableColumns || [] - const formSchema = filterFormSchema(crudSchema, allSchemas) + const formSchema = filterFormSchema(crudSchema) allSchemas.formSchema = formSchema const detailSchema = filterDescriptionsSchema(crudSchema) @@ -89,55 +72,26 @@ export const useCrudSchemas = ( } // 过滤 Search 结构 -const filterSearchSchema = (crudSchema: CrudSchema[], allSchemas: AllSchemas): FormSchema[] => { +const filterSearchSchema = (crudSchema: CrudSchema[]): FormSchema[] => { const searchSchema: FormSchema[] = [] + const length = crudSchema.length - // 获取字典列表队列 - const searchRequestTask: Array<() => Promise<void>> = [] - - eachTree(crudSchema, (schemaItem: CrudSchema) => { + for (let i = 0; i < length; i++) { + const schemaItem = crudSchema[i] // 判断是否显示 if (schemaItem?.search?.show) { const searchSchemaItem = { - // 默认为 input - component: schemaItem.search.component || 'Input', - componentProps: {}, + component: schemaItem.search.component, ...schemaItem.search, - field: schemaItem?.search?.field || schemaItem.field, - label: schemaItem.search?.label || schemaItem.label - } - - if (searchSchemaItem.dictName) { - // 如果有 dictName 则证明是从字典中获取数据 - const dictArr = dictStore.getDictObj[searchSchemaItem.dictName] - searchSchemaItem.componentProps!.options = filterOptions(dictArr) - } else if (searchSchemaItem.api) { - searchRequestTask.push(async () => { - const res = await (searchSchemaItem.api as () => AxiosPromise)() - if (res) { - const index = findIndex(allSchemas.searchSchema, (v: FormSchema) => { - return v.field === searchSchemaItem.field - }) - if (index !== -1) { - allSchemas.searchSchema[index]!.componentProps!.options = filterOptions( - res, - searchSchemaItem.componentProps.optionsAlias?.labelField - ) - } - } - }) + field: schemaItem.field, + label: schemaItem.label } // 删除不必要的字段 delete searchSchemaItem.show - delete searchSchemaItem.dictName searchSchema.push(searchSchemaItem) } - }) - - for (const task of searchRequestTask) { - task() } return searchSchema @@ -166,56 +120,28 @@ const filterTableSchema = (crudSchema: CrudSchema[]): TableColumn[] => { } // 过滤 form 结构 -const filterFormSchema = (crudSchema: CrudSchema[], allSchemas: AllSchemas): FormSchema[] => { +const filterFormSchema = (crudSchema: CrudSchema[]): FormSchema[] => { const formSchema: FormSchema[] = [] + const length = crudSchema.length - // 获取字典列表队列 - const formRequestTask: Array<() => Promise<void>> = [] - - eachTree(crudSchema, (schemaItem: CrudSchema) => { + for (let i = 0; i < length; i++) { + const formItem = crudSchema[i] // 判断是否显示 - if (schemaItem?.form?.show !== false) { + if (formItem?.form?.show) { const formSchemaItem = { - // 默认为 input - component: schemaItem?.form?.component || 'Input', - componentProps: {}, - ...schemaItem.form, - field: schemaItem.field, - label: schemaItem.search?.label || schemaItem.label - } - - if (formSchemaItem.dictName) { - // 如果有 dictName 则证明是从字典中获取数据 - const dictArr = dictStore.getDictObj[formSchemaItem.dictName] - formSchemaItem.componentProps!.options = filterOptions(dictArr) - } else if (formSchemaItem.api) { - formRequestTask.push(async () => { - const res = await (formSchemaItem.api as () => AxiosPromise)() - if (res) { - const index = findIndex(allSchemas.formSchema, (v: FormSchema) => { - return v.field === formSchemaItem.field - }) - if (index !== -1) { - allSchemas.formSchema[index]!.componentProps!.options = filterOptions( - res, - formSchemaItem.componentProps.optionsAlias?.labelField - ) - } - } - }) + component: formItem.form.component, + ...formItem.form, + field: formItem.field, + label: formItem.label } // 删除不必要的字段 delete formSchemaItem.show - delete formSchemaItem.dictName formSchema.push(formSchemaItem) } - }) - - for (const task of formRequestTask) { - task() } + return formSchema } @@ -241,15 +167,3 @@ const filterDescriptionsSchema = (crudSchema: CrudSchema[]): DescriptionsSchema[ return descriptionsSchema } - -// 给options添加国际化 -const filterOptions = (options: Recordable, labelField?: string) => { - return options?.map((v: Recordable) => { - if (labelField) { - v['labelField'] = t(v.labelField) - } else { - v['label'] = t(v.label) - } - return v - }) -} diff --git a/src/hooks/web/useTable.ts b/src/hooks/web/useTable.ts index e9cfad5..dc53872 100644 --- a/src/hooks/web/useTable.ts +++ b/src/hooks/web/useTable.ts @@ -1,7 +1,10 @@ +import { useI18n } from '@/hooks/web/useI18n' import { Table, TableExpose, TableProps, TableSetProps, TableColumn } from '@/components/Table' -import { ElTable } from 'element-plus' +import { ElTable, ElMessageBox, ElMessage } from 'element-plus' import { ref, watch, unref, nextTick, onMounted } from 'vue' +const { t } = useI18n() + interface UseTableConfig { /** * 是否初始化的时候请求一次 @@ -11,6 +14,7 @@ interface UseTableConfig { list: any[] total: number }> + fetchDelApi?: () => Promise<boolean> } export const useTable = (config: UseTableConfig) => { @@ -68,24 +72,6 @@ export const useTable = (config: UseTableConfig) => { return table } - // const delData = async (ids: string[] | number[]) => { - // const res = await (config?.delListApi && config?.delListApi(ids)) - // if (res) { - // ElMessage.success(t('common.delSuccess')) - - // // 计算出临界点 - // const currentPage = - // tableObject.total % tableObject.pageSize === ids.length || tableObject.pageSize === 1 - // ? tableObject.currentPage > 1 - // ? tableObject.currentPage - 1 - // : tableObject.currentPage - // : tableObject.currentPage - - // tableObject.currentPage = currentPage - // methods.getList() - // } - // } - const methods = { /** * 获取表单数据 @@ -154,7 +140,7 @@ export const useTable = (config: UseTableConfig) => { refresh: () => { methods.getList() - } + }, // sortableChange: (e: any) => { // console.log('sortableChange', e) @@ -162,32 +148,35 @@ export const useTable = (config: UseTableConfig) => { // dataList.value.splice(newIndex, 0, dataList.value.splice(oldIndex, 1)[0]) // // to do something // } - // // 删除数据 - // delList: async (ids: string[] | number[], multiple: boolean, message = true) => { - // const tableRef = await getTable() - // if (multiple) { - // if (!tableRef?.selections.length) { - // ElMessage.warning(t('common.delNoData')) - // return - // } - // } else { - // if (!tableObject.currentRow) { - // ElMessage.warning(t('common.delNoData')) - // return - // } - // } - // if (message) { - // ElMessageBox.confirm(t('common.delMessage'), t('common.delWarning'), { - // confirmButtonText: t('common.delOk'), - // cancelButtonText: t('common.delCancel'), - // type: 'warning' - // }).then(async () => { - // await delData(ids) - // }) - // } else { - // await delData(ids) - // } - // } + // 删除数据 + delList: async (idsLength: number) => { + const { fetchDelApi } = config + if (!fetchDelApi) { + console.warn('fetchDelApi is undefined') + return + } + ElMessageBox.confirm(t('common.delMessage'), t('common.delWarning'), { + confirmButtonText: t('common.delOk'), + cancelButtonText: t('common.delCancel'), + type: 'warning' + }).then(async () => { + const res = await fetchDelApi() + if (res) { + ElMessage.success(t('common.delSuccess')) + + // 计算出临界点 + const current = + unref(total) % unref(pageSize) === idsLength || unref(pageSize) === 1 + ? unref(currentPage) > 1 + ? unref(currentPage) - 1 + : unref(currentPage) + : unref(currentPage) + + currentPage.value = current + methods.getList() + } + }) + } } return { diff --git a/src/router/index.ts b/src/router/index.ts index 5c22c16..a0f63cb 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -409,74 +409,74 @@ export const asyncRouterMap: AppRouteRecordRaw[] = [ } ] }, - // { - // path: '/example', - // component: Layout, - // redirect: '/example/example-dialog', - // name: 'Example', - // meta: { - // title: t('router.example'), - // icon: 'ep:management', - // alwaysShow: true - // }, - // children: [ - // { - // path: 'example-dialog', - // component: () => import('@/views/Example/Dialog/ExampleDialog.vue'), - // name: 'ExampleDialog', - // meta: { - // title: t('router.exampleDialog') - // } - // }, - // { - // path: 'example-page', - // component: () => import('@/views/Example/Page/ExamplePage.vue'), - // name: 'ExamplePage', - // meta: { - // title: t('router.examplePage') - // } - // }, - // { - // path: 'example-add', - // component: () => import('@/views/Example/Page/ExampleAdd.vue'), - // name: 'ExampleAdd', - // meta: { - // title: t('router.exampleAdd'), - // noTagsView: true, - // noCache: true, - // hidden: true, - // canTo: true, - // activeMenu: '/example/example-page' - // } - // }, - // { - // path: 'example-edit', - // component: () => import('@/views/Example/Page/ExampleEdit.vue'), - // name: 'ExampleEdit', - // meta: { - // title: t('router.exampleEdit'), - // noTagsView: true, - // noCache: true, - // hidden: true, - // canTo: true, - // activeMenu: '/example/example-page' - // } - // }, - // { - // path: 'example-detail', - // component: () => import('@/views/Example/Page/ExampleDetail.vue'), - // name: 'ExampleDetail', - // meta: { - // title: t('router.exampleDetail'), - // noTagsView: true, - // noCache: true, - // hidden: true, - // canTo: true, - // activeMenu: '/example/example-page' - // } - // } - // ] - // }, + { + path: '/example', + component: Layout, + redirect: '/example/example-dialog', + name: 'Example', + meta: { + title: t('router.example'), + icon: 'ep:management', + alwaysShow: true + }, + children: [ + { + path: 'example-dialog', + component: () => import('@/views/Example/Dialog/ExampleDialog.vue'), + name: 'ExampleDialog', + meta: { + title: t('router.exampleDialog') + } + }, + { + path: 'example-page', + component: () => import('@/views/Example/Page/ExamplePage.vue'), + name: 'ExamplePage', + meta: { + title: t('router.examplePage') + } + }, + { + path: 'example-add', + component: () => import('@/views/Example/Page/ExampleAdd.vue'), + name: 'ExampleAdd', + meta: { + title: t('router.exampleAdd'), + noTagsView: true, + noCache: true, + hidden: true, + canTo: true, + activeMenu: '/example/example-page' + } + }, + { + path: 'example-edit', + component: () => import('@/views/Example/Page/ExampleEdit.vue'), + name: 'ExampleEdit', + meta: { + title: t('router.exampleEdit'), + noTagsView: true, + noCache: true, + hidden: true, + canTo: true, + activeMenu: '/example/example-page' + } + }, + { + path: 'example-detail', + component: () => import('@/views/Example/Page/ExampleDetail.vue'), + name: 'ExampleDetail', + meta: { + title: t('router.exampleDetail'), + noTagsView: true, + noCache: true, + hidden: true, + canTo: true, + activeMenu: '/example/example-page' + } + } + ] + }, { path: '/error', component: Layout, diff --git a/src/views/Example/Dialog/ExampleDialog.vue b/src/views/Example/Dialog/ExampleDialog.vue index 2024a4e..728d967 100644 --- a/src/views/Example/Dialog/ExampleDialog.vue +++ b/src/views/Example/Dialog/ExampleDialog.vue @@ -1,10 +1,10 @@ -<script setup lang="ts"> +<script setup lang="tsx"> import { ContentWrap } from '@/components/ContentWrap' import { Search } from '@/components/Search' import { Dialog } from '@/components/Dialog' import { useI18n } from '@/hooks/web/useI18n' import { ElButton, ElTag } from 'element-plus' -import { Table } from '@/components/Table' +import { Table, TableColumn } from '@/components/Table' import { getTableListApi, saveTableApi, delTableListApi } from '@/api/table' import { useTable } from '@/hooks/web/useTable' import { TableData } from '@/api/table/types' @@ -12,27 +12,51 @@ import { h, ref, unref, reactive } from 'vue' import Write from './components/Write.vue' import Detail from './components/Detail.vue' import { CrudSchema, useCrudSchemas } from '@/hooks/web/useCrudSchemas' -import { TableColumn } from '@/types/table' -const { register, tableObject, methods } = useTable<TableData>({ - getListApi: getTableListApi, - delListApi: delTableListApi, - response: { - list: 'list', - total: 'total' +const ids = ref<string[]>([]) + +const { tableRegister, tableState, tableMethods } = useTable({ + fetchDataApi: async () => { + const { currentPage, pageSize } = tableState + const res = await getTableListApi({ + pageIndex: unref(currentPage), + pageSize: unref(pageSize), + ...unref(searchParams) + }) + return { + list: res.data.list, + total: res.data.total + } }, - defaultParams: { - title: 's' + fetchDelApi: async () => { + const res = await delTableListApi(unref(ids)) + return !!res } }) +const { loading, dataList, total, currentPage, pageSize } = tableState +const { getList, getElTableExpose, delList } = tableMethods -const { getList, setSearchParams } = methods - -getList() +const searchParams = ref({}) +const setSearchParams = (params: any) => { + searchParams.value = params + getList() +} const { t } = useI18n() const crudSchemas = reactive<CrudSchema[]>([ + { + field: 'selection', + form: { + show: false + }, + detail: { + show: false + }, + table: { + type: 'selection' + } + }, { field: 'index', label: t('tableDemo.index'), @@ -48,9 +72,12 @@ const crudSchemas = reactive<CrudSchema[]>([ field: 'title', label: t('tableDemo.title'), search: { - show: true + show: true, + component: 'Input' }, form: { + show: true, + component: 'Input', colProps: { span: 24 } @@ -67,6 +94,7 @@ const crudSchemas = reactive<CrudSchema[]>([ field: 'display_time', label: t('tableDemo.displayTime'), form: { + show: true, component: 'DatePicker', componentProps: { type: 'datetime', @@ -92,6 +120,7 @@ const crudSchemas = reactive<CrudSchema[]>([ ) }, form: { + show: true, component: 'Select', componentProps: { style: { @@ -112,12 +141,32 @@ const crudSchemas = reactive<CrudSchema[]>([ } ] } + }, + detail: { + slots: { + default: (data: any) => { + return ( + <ElTag + type={ + data.importance === 1 ? 'success' : data.importance === 2 ? 'warning' : 'danger' + } + > + {data.importance === 1 + ? t('tableDemo.important') + : data.importance === 2 + ? t('tableDemo.good') + : t('tableDemo.commonly')} + </ElTag> + ) + } + } } }, { field: 'pageviews', label: t('tableDemo.pageviews'), form: { + show: true, component: 'InputNumber', value: 0 } @@ -129,13 +178,19 @@ const crudSchemas = reactive<CrudSchema[]>([ show: false }, form: { + show: true, component: 'Editor', colProps: { span: 24 } }, detail: { - span: 24 + span: 24, + slots: { + default: (data: any) => { + return <div innerHTML={data.content}></div> + } + } } }, { @@ -147,115 +202,108 @@ const crudSchemas = reactive<CrudSchema[]>([ }, detail: { show: false + }, + table: { + slots: { + default: (data: any) => { + return ( + <> + <ElButton type="primary" onClick={() => action(data[0].row, 'edit')}> + {t('exampleDemo.edit')} + </ElButton> + <ElButton type="success" onClick={() => action(data[0].row, 'detail')}> + {t('exampleDemo.detail')} + </ElButton> + <ElButton type="danger" onClick={() => delData(data[0].row)}> + {t('exampleDemo.del')} + </ElButton> + </> + ) + } + } } } ]) +// @ts-ignore const { allSchemas } = useCrudSchemas(crudSchemas) const dialogVisible = ref(false) - const dialogTitle = ref('') +const currentRow = ref<TableData | null>(null) +const actionType = ref('') + const AddAction = () => { dialogTitle.value = t('exampleDemo.add') - tableObject.currentRow = null + currentRow.value = null dialogVisible.value = true actionType.value = '' } const delLoading = ref(false) -const delData = async (row: TableData | null, multiple: boolean) => { - tableObject.currentRow = row - const { delList, getSelections } = methods - const selections = await getSelections() +const delData = async (row: TableData | null) => { + const elTableExpose = await getElTableExpose() + ids.value = row ? [row.id] : elTableExpose?.getSelectionRows().map((v: TableData) => v.id) || [] delLoading.value = true - await delList( - multiple ? selections.map((v) => v.id) : [tableObject.currentRow?.id as string], - multiple - ).finally(() => { + await delList(unref(ids).length).finally(() => { delLoading.value = false }) } -const actionType = ref('') - const action = (row: TableData, type: string) => { dialogTitle.value = t(type === 'edit' ? 'exampleDemo.edit' : 'exampleDemo.detail') actionType.value = type - tableObject.currentRow = row + currentRow.value = row dialogVisible.value = true } const writeRef = ref<ComponentRef<typeof Write>>() -const loading = ref(false) +const saveLoading = ref(false) const save = async () => { const write = unref(writeRef) - await write?.elFormRef?.validate(async (isValid) => { - if (isValid) { - loading.value = true - const data = (await write?.getFormData()) as TableData - const res = await saveTableApi(data) - .catch(() => {}) - .finally(() => { - loading.value = false - }) - if (res) { - dialogVisible.value = false - tableObject.currentPage = 1 - getList() - } + const formData = await write?.submit() + if (formData) { + saveLoading.value = true + const res = await saveTableApi(formData) + .catch(() => {}) + .finally(() => { + saveLoading.value = false + }) + if (res) { + dialogVisible.value = false + currentPage.value = 1 + getList() } - }) + } } </script> <template> <ContentWrap> - <Search - :model="{ title: 's' }" - :schema="allSchemas.searchSchema" - @search="setSearchParams" - @reset="setSearchParams" - /> + <Search :schema="allSchemas.searchSchema" @search="setSearchParams" @reset="setSearchParams" /> <div class="mb-10px"> <ElButton type="primary" @click="AddAction">{{ t('exampleDemo.add') }}</ElButton> - <ElButton :loading="delLoading" type="danger" @click="delData(null, true)"> + <ElButton :loading="delLoading" type="danger" @click="delData(null)"> {{ t('exampleDemo.del') }} </ElButton> </div> <Table - v-model:pageSize="tableObject.pageSize" - v-model:currentPage="tableObject.currentPage" + v-model:pageSize="pageSize" + v-model:currentPage="currentPage" :columns="allSchemas.tableColumns" - :data="tableObject.tableList" - :loading="tableObject.loading" + :data="dataList" + :loading="loading" :pagination="{ - total: tableObject.total + total: total }" - @register="register" - > - <template #action="{ row }"> - <ElButton type="primary" v-hasPermi="['example:dialog:edit']" @click="action(row, 'edit')"> - {{ t('exampleDemo.edit') }} - </ElButton> - <ElButton - type="success" - v-hasPermi="['example:dialog:view']" - @click="action(row, 'detail')" - > - {{ t('exampleDemo.detail') }} - </ElButton> - <ElButton type="danger" v-hasPermi="['example:dialog:delete']" @click="delData(row, false)"> - {{ t('exampleDemo.del') }} - </ElButton> - </template> - </Table> + @register="tableRegister" + /> </ContentWrap> <Dialog v-model="dialogVisible" :title="dialogTitle"> @@ -263,17 +311,17 @@ const save = async () => { v-if="actionType !== 'detail'" ref="writeRef" :form-schema="allSchemas.formSchema" - :current-row="tableObject.currentRow" + :current-row="currentRow" /> <Detail v-if="actionType === 'detail'" :detail-schema="allSchemas.detailSchema" - :current-row="tableObject.currentRow" + :current-row="currentRow" /> <template #footer> - <ElButton v-if="actionType !== 'detail'" type="primary" :loading="loading" @click="save"> + <ElButton v-if="actionType !== 'detail'" type="primary" :loading="saveLoading" @click="save"> {{ t('exampleDemo.save') }} </ElButton> <ElButton @click="dialogVisible = false">{{ t('dialogDemo.close') }}</ElButton> diff --git a/src/views/Example/Dialog/components/Detail.vue b/src/views/Example/Dialog/components/Detail.vue index 42c5650..1fafae8 100644 --- a/src/views/Example/Dialog/components/Detail.vue +++ b/src/views/Example/Dialog/components/Detail.vue @@ -1,10 +1,9 @@ <script setup lang="ts"> import { PropType } from 'vue' import type { TableData } from '@/api/table/types' -import { Descriptions } from '@/components/Descriptions' +import { Descriptions, DescriptionsSchema } from '@/components/Descriptions' import { useI18n } from '@/hooks/web/useI18n' import { ElTag } from 'element-plus' -import { DescriptionsSchema } from '@/types/descriptions' const { t } = useI18n() diff --git a/src/views/Example/Dialog/components/Write.vue b/src/views/Example/Dialog/components/Write.vue index b78e19f..5dc8ac1 100644 --- a/src/views/Example/Dialog/components/Write.vue +++ b/src/views/Example/Dialog/components/Write.vue @@ -1,10 +1,9 @@ <script setup lang="ts"> -import { Form } from '@/components/Form' +import { Form, FormSchema } from '@/components/Form' import { useForm } from '@/hooks/web/useForm' import { PropType, reactive, watch } from 'vue' import { TableData } from '@/api/table/types' import { useValidator } from '@/hooks/web/useValidator' -import { FormSchema } from '@/types/form' const { required } = useValidator() @@ -28,15 +27,24 @@ const rules = reactive({ content: [required()] }) -const { register, methods, elFormRef } = useForm({ - schema: props.formSchema -}) +const { formRegister, formMethods } = useForm() +const { setValues, getFormData, getElFormExpose } = formMethods + +const submit = async () => { + const elForm = await getElFormExpose() + const valid = await elForm?.validate().catch((err) => { + console.log(err) + }) + if (valid) { + const formData = getFormData() + return formData + } +} watch( () => props.currentRow, (currentRow) => { if (!currentRow) return - const { setValues } = methods setValues(currentRow) }, { @@ -46,11 +54,10 @@ watch( ) defineExpose({ - elFormRef, - getFormData: methods.getFormData + submit }) </script> <template> - <Form :rules="rules" @register="register" /> + <Form :rules="rules" @register="formRegister" :schema="formSchema" /> </template> diff --git a/src/views/Example/Page/ExampleAdd.vue b/src/views/Example/Page/ExampleAdd.vue index a29413d..95290af 100644 --- a/src/views/Example/Page/ExampleAdd.vue +++ b/src/views/Example/Page/ExampleAdd.vue @@ -6,12 +6,11 @@ import { ElButton } from 'element-plus' import { useI18n } from '@/hooks/web/useI18n' import { useRouter } from 'vue-router' import { saveTableApi } from '@/api/table' -import { TableData } from '@/api/table/types' import { useEmitt } from '@/hooks/web/useEmitt' const { emitter } = useEmitt() -const { push } = useRouter() +const { push, go } = useRouter() const { t } = useI18n() @@ -21,21 +20,19 @@ const loading = ref(false) const save = async () => { const write = unref(writeRef) - await write?.elFormRef?.validate(async (isValid) => { - if (isValid) { - loading.value = true - const data = (await write?.getFormData()) as TableData - const res = await saveTableApi(data) - .catch(() => {}) - .finally(() => { - loading.value = false - }) - if (res) { - emitter.emit('getList', 'add') - push('/example/example-page') - } + const formData = await write?.submit() + if (formData) { + loading.value = true + const res = await saveTableApi(formData) + .catch(() => {}) + .finally(() => { + loading.value = false + }) + if (res) { + emitter.emit('getList', 'add') + push('/example/example-page') } - }) + } } </script> @@ -43,7 +40,10 @@ const save = async () => { <ContentDetailWrap :title="t('exampleDemo.add')" @back="push('/example/example-page')"> <Write ref="writeRef" /> - <template #right> + <template #header> + <ElButton @click="go(-1)"> + {{ t('common.back') }} + </ElButton> <ElButton type="primary" :loading="loading" @click="save"> {{ t('exampleDemo.save') }} </ElButton> diff --git a/src/views/Example/Page/ExampleDetail.vue b/src/views/Example/Page/ExampleDetail.vue index 39f170f..77875ba 100644 --- a/src/views/Example/Page/ExampleDetail.vue +++ b/src/views/Example/Page/ExampleDetail.vue @@ -6,8 +6,9 @@ import { useI18n } from '@/hooks/web/useI18n' import { useRouter, useRoute } from 'vue-router' import { getTableDetApi } from '@/api/table' import { TableData } from '@/api/table/types' +import { ElButton } from 'element-plus' -const { push } = useRouter() +const { push, go } = useRouter() const { query } = useRoute() @@ -27,6 +28,11 @@ getTableDet() <template> <ContentDetailWrap :title="t('exampleDemo.detail')" @back="push('/example/example-page')"> + <template #header> + <ElButton @click="go(-1)"> + {{ t('common.back') }} + </ElButton> + </template> <Detail :current-row="currentRow" /> </ContentDetailWrap> </template> diff --git a/src/views/Example/Page/ExampleEdit.vue b/src/views/Example/Page/ExampleEdit.vue index 6818e9c..df382da 100644 --- a/src/views/Example/Page/ExampleEdit.vue +++ b/src/views/Example/Page/ExampleEdit.vue @@ -11,7 +11,7 @@ import { useEmitt } from '@/hooks/web/useEmitt' const { emitter } = useEmitt() -const { push } = useRouter() +const { push, go } = useRouter() const { query } = useRoute() @@ -34,17 +34,16 @@ const loading = ref(false) const save = async () => { const write = unref(writeRef) - const validate = await write?.elFormRef?.validate()?.catch(() => {}) - if (validate) { + const formData = await write?.submit() + if (formData) { loading.value = true - const data = (await write?.getFormData()) as TableData - const res = await saveTableApi(data) + const res = await saveTableApi(formData) .catch(() => {}) .finally(() => { loading.value = false }) if (res) { - emitter.emit('getList', 'edit') + emitter.emit('getList', 'editor') push('/example/example-page') } } @@ -55,7 +54,10 @@ const save = async () => { <ContentDetailWrap :title="t('exampleDemo.edit')" @back="push('/example/example-page')"> <Write ref="writeRef" :current-row="currentRow" /> - <template #right> + <template #header> + <ElButton @click="go(-1)"> + {{ t('common.back') }} + </ElButton> <ElButton type="primary" :loading="loading" @click="save"> {{ t('exampleDemo.save') }} </ElButton> diff --git a/src/views/Example/Page/ExamplePage.vue b/src/views/Example/Page/ExamplePage.vue index 899fe01..5a7d391 100644 --- a/src/views/Example/Page/ExamplePage.vue +++ b/src/views/Example/Page/ExamplePage.vue @@ -1,17 +1,16 @@ -<script setup lang="ts"> +<script setup lang="tsx"> import { ContentWrap } from '@/components/ContentWrap' import { Search } from '@/components/Search' import { useI18n } from '@/hooks/web/useI18n' import { ElButton, ElTag } from 'element-plus' -import { Table } from '@/components/Table' +import { Table, TableColumn } from '@/components/Table' import { getTableListApi, delTableListApi } from '@/api/table' import { useTable } from '@/hooks/web/useTable' import { TableData } from '@/api/table/types' -import { h, reactive, ref } from 'vue' +import { h, reactive, ref, unref } from 'vue' import { useRouter } from 'vue-router' import { useEmitt } from '@/hooks/web/useEmitt' import { CrudSchema, useCrudSchemas } from '@/hooks/web/useCrudSchemas' -import { TableColumn } from '@/types/table' defineOptions({ name: 'ExamplePage' @@ -19,16 +18,34 @@ defineOptions({ const { push } = useRouter() -const { register, tableObject, methods } = useTable<TableData>({ - getListApi: getTableListApi, - delListApi: delTableListApi, - response: { - list: 'list', - total: 'total' +const ids = ref<string[]>([]) + +const searchParams = ref({}) +const setSearchParams = (params: any) => { + searchParams.value = params + getList() +} + +const { tableRegister, tableState, tableMethods } = useTable({ + fetchDataApi: async () => { + const { currentPage, pageSize } = tableState + const res = await getTableListApi({ + pageIndex: unref(currentPage), + pageSize: unref(pageSize), + ...unref(searchParams) + }) + return { + list: res.data.list, + total: res.data.total + } + }, + fetchDelApi: async () => { + const res = await delTableListApi(unref(ids)) + return !!res } }) - -const { getList, setSearchParams } = methods +const { loading, dataList, total, currentPage, pageSize } = tableState +const { getList, getElTableExpose, delList } = tableMethods getList() @@ -36,7 +53,7 @@ useEmitt({ name: 'getList', callback: (type: string) => { if (type === 'add') { - tableObject.currentPage = 1 + currentPage.value = 1 } getList() } @@ -45,16 +62,45 @@ useEmitt({ const { t } = useI18n() const crudSchemas = reactive<CrudSchema[]>([ + { + field: 'selection', + form: { + show: false + }, + detail: { + show: false + }, + table: { + type: 'selection' + } + }, { field: 'index', label: t('tableDemo.index'), - type: 'index' + type: 'index', + form: { + show: false + }, + detail: { + show: false + } }, { field: 'title', label: t('tableDemo.title'), search: { - show: true + show: true, + component: 'Input' + }, + form: { + show: true, + component: 'Input', + colProps: { + span: 24 + } + }, + detail: { + span: 24 } }, { @@ -63,7 +109,15 @@ const crudSchemas = reactive<CrudSchema[]>([ }, { field: 'display_time', - label: t('tableDemo.displayTime') + label: t('tableDemo.displayTime'), + form: { + show: true, + component: 'DatePicker', + componentProps: { + type: 'datetime', + valueFormat: 'YYYY-MM-DD HH:mm:ss' + } + } }, { field: 'importance', @@ -81,26 +135,90 @@ const crudSchemas = reactive<CrudSchema[]>([ ? t('tableDemo.good') : t('tableDemo.commonly') ) + }, + form: { + show: true, + component: 'Select', + componentProps: { + style: { + width: '100%' + }, + options: [ + { + label: '重要', + value: 3 + }, + { + label: '良好', + value: 2 + }, + { + label: '一般', + value: 1 + } + ] + } } }, { field: 'pageviews', - label: t('tableDemo.pageviews') + label: t('tableDemo.pageviews'), + form: { + show: true, + component: 'InputNumber', + value: 0 + } }, { field: 'content', label: t('exampleDemo.content'), table: { show: false + }, + form: { + show: true, + component: 'Editor', + colProps: { + span: 24 + } + }, + detail: { + span: 24 } }, { field: 'action', width: '260px', - label: t('tableDemo.action') + label: t('tableDemo.action'), + form: { + show: false + }, + detail: { + show: false + }, + table: { + slots: { + default: (data: any) => { + return ( + <> + <ElButton type="primary" onClick={() => action(data[0].row, 'edit')}> + {t('exampleDemo.edit')} + </ElButton> + <ElButton type="success" onClick={() => action(data[0].row, 'detail')}> + {t('exampleDemo.detail')} + </ElButton> + <ElButton type="danger" onClick={() => delData(data[0].row)}> + {t('exampleDemo.del')} + </ElButton> + </> + ) + } + } + } } ]) +// @ts-ignore const { allSchemas } = useCrudSchemas(crudSchemas) const AddAction = () => { @@ -109,15 +227,11 @@ const AddAction = () => { const delLoading = ref(false) -const delData = async (row: TableData | null, multiple: boolean) => { - tableObject.currentRow = row - const { delList, getSelections } = methods - const selections = await getSelections() +const delData = async (row: TableData | null) => { + const elTableExpose = await getElTableExpose() + ids.value = row ? [row.id] : elTableExpose?.getSelectionRows().map((v: TableData) => v.id) || [] delLoading.value = true - await delList( - multiple ? selections.map((v) => v.id) : [tableObject.currentRow?.id as string], - multiple - ).finally(() => { + await delList(unref(ids).length).finally(() => { delLoading.value = false }) } @@ -133,33 +247,21 @@ const action = (row: TableData, type: string) => { <div class="mb-10px"> <ElButton type="primary" @click="AddAction">{{ t('exampleDemo.add') }}</ElButton> - <ElButton :loading="delLoading" type="danger" @click="delData(null, true)"> + <ElButton :loading="delLoading" type="danger" @click="delData(null)"> {{ t('exampleDemo.del') }} </ElButton> </div> <Table - v-model:pageSize="tableObject.pageSize" - v-model:currentPage="tableObject.currentPage" + v-model:pageSize="pageSize" + v-model:currentPage="currentPage" :columns="allSchemas.tableColumns" - :data="tableObject.tableList" - :loading="tableObject.loading" + :data="dataList" + :loading="loading" :pagination="{ - total: tableObject.total + total: total }" - @register="register" - > - <template #action="{ row }"> - <ElButton type="primary" @click="action(row, 'edit')"> - {{ t('exampleDemo.edit') }} - </ElButton> - <ElButton type="success" @click="action(row, 'detail')"> - {{ t('exampleDemo.detail') }} - </ElButton> - <ElButton type="danger" @click="delData(row, false)"> - {{ t('exampleDemo.del') }} - </ElButton> - </template> - </Table> + @register="tableRegister" + /> </ContentWrap> </template> diff --git a/src/views/Example/Page/components/Detail.vue b/src/views/Example/Page/components/Detail.vue index 4f86a92..dd390be 100644 --- a/src/views/Example/Page/components/Detail.vue +++ b/src/views/Example/Page/components/Detail.vue @@ -1,10 +1,9 @@ -<script setup lang="ts"> +<script setup lang="tsx"> import { PropType, reactive } from 'vue' import type { TableData } from '@/api/table/types' -import { Descriptions } from '@/components/Descriptions' +import { Descriptions, DescriptionsSchema } from '@/components/Descriptions' import { useI18n } from '@/hooks/web/useI18n' import { ElTag } from 'element-plus' -import { DescriptionsSchema } from '@/types/descriptions' const { t } = useI18n() @@ -31,7 +30,22 @@ const schema = reactive<DescriptionsSchema[]>([ }, { field: 'importance', - label: t('exampleDemo.importance') + label: t('exampleDemo.importance'), + slots: { + default: (data: any) => { + return ( + <ElTag + type={data.importance === 1 ? 'success' : data.importance === 2 ? 'warning' : 'danger'} + > + {data.importance === 1 + ? t('tableDemo.important') + : data.importance === 2 + ? t('tableDemo.good') + : t('tableDemo.commonly')} + </ElTag> + ) + } + } }, { field: 'pageviews', @@ -40,7 +54,12 @@ const schema = reactive<DescriptionsSchema[]>([ { field: 'content', label: t('exampleDemo.content'), - span: 24 + span: 24, + slots: { + default: (data: any) => { + return <div innerHTML={data.content}></div> + } + } } ]) </script> diff --git a/src/views/Example/Page/components/Write.vue b/src/views/Example/Page/components/Write.vue index 65c8d69..22eaac5 100644 --- a/src/views/Example/Page/components/Write.vue +++ b/src/views/Example/Page/components/Write.vue @@ -1,12 +1,11 @@ <script setup lang="ts"> -import { Form } from '@/components/Form' +import { Form, FormSchema } from '@/components/Form' import { useForm } from '@/hooks/web/useForm' import { PropType, reactive, watch } from 'vue' import { TableData } from '@/api/table/types' import { useI18n } from '@/hooks/web/useI18n' import { useValidator } from '@/hooks/web/useValidator' import { IDomEditor } from '@wangeditor/editor' -import { FormSchema } from '@/types/form' const { required } = useValidator() @@ -19,6 +18,9 @@ const props = defineProps({ const { t } = useI18n() +const { formRegister, formMethods } = useForm() +const { setValues, getFormData, getElFormExpose, setSchema } = formMethods + const schema = reactive<FormSchema[]>([ { field: 'title', @@ -92,8 +94,8 @@ const schema = reactive<FormSchema[]>([ }, componentProps: { defaultHtml: '', + // @ts-ignore onChange: (edit: IDomEditor) => { - const { setValues } = methods setValues({ content: edit.getHtml() }) @@ -112,15 +114,21 @@ const rules = reactive({ content: [required()] }) -const { register, methods, elFormRef } = useForm({ - schema -}) +const submit = async () => { + const elForm = await getElFormExpose() + const valid = await elForm?.validate().catch((err) => { + console.log(err) + }) + if (valid) { + const formData = getFormData() + return formData + } +} watch( () => props.currentRow, (currentRow) => { if (!currentRow) return - const { setValues, setSchema } = methods setValues(currentRow) setSchema([ { @@ -137,11 +145,10 @@ watch( ) defineExpose({ - elFormRef, - getFormData: methods.getFormData + submit }) </script> <template> - <Form :rules="rules" @register="register" /> + <Form :rules="rules" @register="formRegister" :schema="schema" /> </template>