From 7079054c8c7e5064905223112d2e87fd472dd7b2 Mon Sep 17 00:00:00 2001 From: dark Date: Sun, 18 Aug 2024 23:24:23 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0XForm=E6=A8=A1=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/table/Table.tsx | 13 +++- src/pages/db/vod/index.tsx | 8 +-- src/pages/x-form/hooks/useApi.tsx | 6 +- src/pages/x-form/index.tsx | 61 +++++++++------- src/pages/x-form/style.ts | 15 ++++ src/pages/x-form/utils/index.tsx | 146 ++++++++++++++++++++++++++++++++++++++ src/service/base.ts | 54 ++++++++++---- src/service/x-form/model.ts | 12 ++++ src/store/x-form/model.ts | 45 ++++++++---- src/types/x-form/model.d.ts | 20 +++--- src/utils/index.ts | 14 ++++ 11 files changed, 325 insertions(+), 69 deletions(-) create mode 100644 src/pages/x-form/utils/index.tsx diff --git a/src/components/table/Table.tsx b/src/components/table/Table.tsx index ab301c8..8ed32da 100644 --- a/src/components/table/Table.tsx +++ b/src/components/table/Table.tsx @@ -1,13 +1,18 @@ import { ProTable, ProTableProps, ProCard } from '@ant-design/pro-components' import React, { useEffect, useRef, useState } from 'react' import { useStyle } from './style' -import { Pagination } from 'antd' +import { Pagination, Select as AntSelect, SelectProps } from 'antd' import { t } from '@/i18n.ts' export interface TableProps extends ProTableProps { } +const Select = (props: SelectProps) => { + return document.body}/> +} +Select.Option = AntSelect.Option + export const Table = = any, D = any>(props: TableProps) => { const { styles } = useStyle() @@ -102,9 +107,13 @@ export const Table = = any, D = any>(props: Table `${t('pagination.total.range', '第')} ${range[0]}-${range[1]} ${t('pagination.total.total', '条/总共')} ${total} ${t('pagination.total.item', '条')}`} showQuickJumper={true} - showSizeChanger={true}/> + showSizeChanger={true} + /> }} diff --git a/src/pages/db/vod/index.tsx b/src/pages/db/vod/index.tsx index 5e2ea27..1037c9e 100644 --- a/src/pages/db/vod/index.tsx +++ b/src/pages/db/vod/index.tsx @@ -1,5 +1,5 @@ -import { t, useTranslation } from '@/i18n.ts' -import { Button, Form, Popconfirm, Divider, Space, Tooltip, Badge, Layout, Menu, Spin, Select } from 'antd' +import { useTranslation } from '@/i18n.ts' +import { Button, Form, Popconfirm, Divider, Space, Tooltip, Badge,Select } from 'antd' import { useAtom, useAtomValue } from 'jotai' import { deleteVodAtom, @@ -9,12 +9,12 @@ import { useEffect, useMemo, useState } from 'react' import Action from '@/components/action/Action.tsx' import { BetaSchemaForm, - ProColumns, ProForm, + ProColumns, ProFormColumnsType, } from '@ant-design/pro-components' import ListPageLayout from '@/layout/ListPageLayout.tsx' import { useStyle } from './style' -import { ExportOutlined, FilterOutlined } from '@ant-design/icons' +import { FilterOutlined } from '@ant-design/icons' import { genProTableColumnWidthProps, getValueCount } from '@/utils' import { Table as ProTable } from '@/components/table' import InteractPopup from '@/components/interact-popup' diff --git a/src/pages/x-form/hooks/useApi.tsx b/src/pages/x-form/hooks/useApi.tsx index d64baa8..9e007b2 100644 --- a/src/pages/x-form/hooks/useApi.tsx +++ b/src/pages/x-form/hooks/useApi.tsx @@ -10,9 +10,10 @@ export const useApi = () => { const nav = useNavigate() const [ api, setApi ] = useAtom(apiAtom) const { api: apiParam } = Route.useSearch() + const [ isChange, setChange ] = useState(false) const [ innerApi, setInnerApi ] = useState('') const [ open, setOpen ] = useState(false) - const apiRef = useRef() + const apiRef = useRef(apiParam) useEffect(() => { @@ -60,8 +61,10 @@ export const useApi = () => { message.error('请填写 api 参数') return } + setChange(false) setOpen(false) setApi(innerApi) + setChange(true) nav({ to: '/x-form', search: { @@ -80,6 +83,7 @@ export const useApi = () => { holderElement, updateApi: setOpen, setApi, + apiChange: isChange, api, } as const diff --git a/src/pages/x-form/index.tsx b/src/pages/x-form/index.tsx index 3e54b1c..19a086e 100644 --- a/src/pages/x-form/index.tsx +++ b/src/pages/x-form/index.tsx @@ -1,9 +1,10 @@ import { createFileRoute } from '@tanstack/react-router' import ListPageLayout from '@/layout/ListPageLayout.tsx' import { useApi } from './hooks/useApi.tsx' -import { Badge, Button, Divider, Form, Popconfirm, Space, Tabs, Tag, Tooltip } from 'antd' +import { Badge, Button, Divider, Form, Popconfirm, Space, Tag, Tooltip } from 'antd' import { EditOutlined, FilterOutlined } from '@ant-design/icons' -import { BetaSchemaForm, ProColumns, ProFormColumnsType, ProTable } from '@ant-design/pro-components' +import { BetaSchemaForm, ProFormColumnsType } from '@ant-design/pro-components' +import { Table as ProTable } from '@/components/table' import { useEffect, useMemo, useState } from 'react' import { useAtomValue } from 'jotai' import { @@ -17,11 +18,12 @@ import { useAtom } from 'jotai/index' import { getValueCount, unSetColumnRules } from '@/utils' import { useStyle } from './style.ts' import Action from '@/components/action/Action.tsx' +import { transformAntdTableProColumns } from './utils' const XForm = () => { const { styles, cx } = useStyle() - const { holderElement, updateApi, api } = useApi() + const { holderElement, updateApi, api, apiChange } = useApi() const [ form ] = Form.useForm() const [ filterForm ] = Form.useForm() @@ -30,23 +32,13 @@ const XForm = () => { const [ search, setSearch ] = useAtom(modelSearchAtom) const { data, isFetching, isLoading, refetch } = useAtomValue(modelsAtom) const { mutate: deleteModel, isPending: isDeleting } = useAtomValue(deleteModelAtom) - const { data: curdModal, isLoading: curdLoading } = useAtomValue(modelCURDAtom) + const { data: curdModal, isLoading: curdLoading, refetch: reloadCURDModal } = useAtomValue(modelCURDAtom) const [ open, setOpen ] = useState(false) const [ openFilter, setFilterOpen ] = useState(false) const [ searchKey, setSearchKey ] = useState(search?.key) const columns = useMemo(() => { - return (curdModal?.column?.map((item) => { - return { - title: item.label, - dataIndex: item.prop, - key: item.prop, - valueType: item.type, - hideInSearch: !item.search, - hideInTable: item.hide, - - } as ProColumns - }) || []).concat([ + return transformAntdTableProColumns(curdModal?.column || []).concat([ { title: 'ID', dataIndex: 'id', @@ -95,6 +87,25 @@ const XForm = () => { } }, [ isSuccess ]) + useEffect(() => { + console.log('apiChange') + if (apiChange) { + reloadCURDModal() + } + }, [ apiChange ]) + + const formProps = curdModal?.dialogType === 'drawer' ? { + layoutType: 'DrawerForm', + drawerProps: { + maskClosable: false, + } + } : { + layoutType: 'ModalForm', + modalProps: { + maskClosable: false, + } + } + return ( <> {holderElement} @@ -113,7 +124,7 @@ const XForm = () => { rowKey="id" headerTitle={api} toolbar={{ - search: { + /*search: { loading: isFetching && !!search?.key, onSearch: (value: string) => { setSearch(prev => ({ @@ -127,7 +138,7 @@ const XForm = () => { }, value: searchKey, placeholder: '输入关键字搜索', - }, + },*/ actions: [ @@ -151,7 +162,7 @@ const XForm = () => { ] }} scroll={{ - // x: 3500, + x: (columns?.length || 1) * 200, y: 'calc(100vh - 290px)' }} search={false} @@ -205,16 +216,12 @@ const XForm = () => { layout={'vertical'} scrollToFirstError={true} title={form.getFieldValue('id') !== 0 ? '编辑' : '添加'} - layoutType={'DrawerForm'} + {...formProps as any} open={open} - drawerProps={{ - maskClosable: false, - }} onOpenChange={(open) => { setOpen(open) }} loading={isSubmitting} - onFinish={async (values) => { saveOrUpdate(values) }} @@ -231,9 +238,13 @@ const XForm = () => { }} layout={'vertical'} scrollToFirstError={true} - layoutType={'DrawerForm'} + layoutType={formProps.layoutType as any} drawerProps={{ - maskClosable: false, + ...formProps.drawerProps, + mask: false, + }} + modalProps={{ + ...formProps.modalProps, mask: false, }} submitter={{ diff --git a/src/pages/x-form/style.ts b/src/pages/x-form/style.ts index 71c685a..2ac95b2 100644 --- a/src/pages/x-form/style.ts +++ b/src/pages/x-form/style.ts @@ -5,7 +5,22 @@ export const useStyle = createStyles(({ token, css, cx, prefixCls }, props: any) const prefix = `${prefixCls}-${token?.proPrefix}-x-form-page` const container = css` + .ant-table-cell { + .ant-tag { + padding-inline: 3px; + margin-inline-end: 3px; + } + } + .ant-table-empty { + .ant-table-body { + height: calc(100vh - 350px) + } + } + + .ant-pro-table-highlight { + + } ` return { diff --git a/src/pages/x-form/utils/index.tsx b/src/pages/x-form/utils/index.tsx new file mode 100644 index 0000000..da6d495 --- /dev/null +++ b/src/pages/x-form/utils/index.tsx @@ -0,0 +1,146 @@ +import { XForm } from '@/types/x-form/model' +import { ProColumns } from '@ant-design/pro-components' +import Switch from '@/components/switch' +import { Checkbox, DatePicker, Input, Radio, Select, TreeSelect } from 'antd' +import request from '@/request' +import { convertToBool, genProTableColumnWidthProps } from '@/utils' +import { ReactNode } from 'react' + +const getValueType = (column: XForm.IColumn) => { + switch (column.type) { + case 'input': + return 'text' + case 'select': + return 'select' + case 'date': + return 'date' + case 'switch': + return 'switch' + case 'radio': + return 'radio' + case 'checkbox': + return 'checkbox' + case 'textarea': + return 'textarea' + case 'tree': + return 'treeSelect' + default: + return 'text' + } +} + +//根据type返回对应的组件 +const getComponent = (column: XForm.IColumn) => { + const type = getValueType(column) as any + switch (type) { + case 'input': + return Input + case 'select': + return Select + case 'date': + return DatePicker + case 'switch': + return Switch + case 'radio': + return Radio + case 'checkbox': + return Checkbox + case 'textarea': + return Input.TextArea + case 'tree': + case 'treeSelect': + return TreeSelect + default: + return Input + } +} + + +export const transformAntdTableProColumns = (columns: XForm.IColumn[]) => { + + return (columns || []).map(item => { + const { value, props, multiple, checkStrictly } = item + + const { width, fieldProps: _fieldProps } = genProTableColumnWidthProps(item.width) + const fieldProps: ProColumns['fieldProps'] = { + dataFiledNames: props, + ...(multiple ? { multiple: true } : {}), + ...(checkStrictly ? { treeCheckStrictly: true } : {}), + ..._fieldProps, + } + + const formItemProps: ProColumns['formItemProps'] = (form, config) => { + + return { + rules: item.rules?.map(i => { + return { + required: i.required, + message: i.message + } + }), + ...(value ? { valuePropName: value } : {}) + } + } + + const rowProps = item.gutter ? { gutter: item.gutter } : { gutter: [ 16, 0 ], } + const colProps = item.span ? { span: item.span } : {} + + const type = getValueType(item) + return { + title: item.label, + dataIndex: item.prop, + key: item.prop, + width, + valueType: type, + hideInSearch: !item.search, + hideInTable: item.hide, + fieldProps, + formItemProps, + colProps, + rowProps, + request: item.dicUrl ? async (params, props) => { + const { fieldProps: { dataFiledNames } } = props + const { value, res: resKey, label } = dataFiledNames || {} + const url = `/${item.dicUrl.replace(/^:/, '/')}` + return request[item.dicMethod || 'get'](url, params).then(res => { + return (res.data?.[resKey] || res.data || []).map((i: any) => { + // console.log(i) + const disabled = 'disabled' in i ? i.disabled : + ('status' in i ? !convertToBool(i.status) : false) + return { + title: i[label || 'label'], + label: i[label || 'label'], + value: i[value || 'id'], + disabled, + data: i + } + }) + }) + } : undefined, + renderFormItem: (_scheam, config) => { + const Component = getComponent(item) as any + const { options, ...props } = config as any + + if ([ 'tree', 'treeSelect' ].includes(_scheam.valueType as string)) { + return + } + if (_scheam.valueType as string === 'select') { + return