|
|
import { useStyle } from './style.ts' import { Badge, Button, Divider, Form, Popconfirm, Space, Tooltip } from 'antd' import { useAtom, useAtomValue, useSetAtom } from 'jotai' import { ModelContext, useSpanModel } from '@/store/r-form/model.ts' import { ReactNode, useEffect, useState } from 'react' import { transformAntdTableProColumns } from './utils' import Action from '@/components/action/Action.tsx' import { FilterOutlined } from '@ant-design/icons' import ListPageLayout from '@/layout/ListPageLayout.tsx' import { Table as ProTable } from '@/components/table' import { getValueCount, unSetColumnRules } from '@/utils' import { BetaSchemaForm, ProColumns, ProFormColumnsType } from '@ant-design/pro-components' import { useApiContext } from '@/context.ts' import { useDeepCompareEffect } from 'react-use' import { RFormTypes } from '@/types/r-form/model' import { ProCoreActionType } from '@ant-design/pro-utils/es/typing' import { getI18nTitle } from '@/i18n.ts'
export interface RFormProps { title?: ReactNode namespace?: string columns?: ProColumns[] //重写columns
actions?: ReactNode[] | JSX.Element[] //左上角的操作按钮
toolbar?: ReactNode //工具栏
renderActions?: (addAction: ReactNode) => ReactNode //渲染操作按钮
resolveColumns?: (columns: ProColumns[]) => ProColumns[] //处理columns
renderColumnOptions?: (record: any, defaultOptions: ReactNode[], index: number, action: ProCoreActionType | undefined) => ReactNode //渲染列的操作
}
const RForm = ( { namespace, actions = [], toolbar, resolveColumns, renderActions, renderColumnOptions, columns: propColumns = [], title }: RFormProps) => {
const { styles, cx } = useStyle() const apiCtx = useApiContext() const { apiAtom, deleteModelAtom, modelAtom, modelCURDAtom, modelsAtom, modelSearchAtom, saveOrUpdateModelAtom } = useSpanModel(namespace || apiCtx?.menu?.meta?.name || 'default') as ModelContext const [ form ] = Form.useForm() const [ filterForm ] = Form.useForm() const setApi = useSetAtom(apiAtom) const [ model, setModel ] = useAtom<RFormTypes.IModel>(modelAtom) const { mutate: saveOrUpdate, isPending: isSubmitting, isSuccess } = useAtomValue(saveOrUpdateModelAtom) const [ search, setSearch ] = useAtom(modelSearchAtom) const { data, isFetching, isLoading, refetch } = useAtomValue(modelsAtom) const { mutate: deleteModel, isPending: isDeleting } = useAtomValue(deleteModelAtom) 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, setColumns ] = useState<ProColumns[]>([])
useDeepCompareEffect(() => {
let res = transformAntdTableProColumns(curdModal?.columns || [], propColumns, curdModal?.config?.i18n) if (resolveColumns) { res = resolveColumns(res) }
const options = (record: any) => { return [ <Action key="edit" as={'a'} onClick={() => { form.setFieldsValue(record) setOpen(true) }}>{'编辑'}</Action>, <Popconfirm key={'del_confirm'} disabled={isDeleting} onConfirm={() => { deleteModel([ record.id ]) }} title={'确定要删除吗?'}> <a key="del"> 删除 </a> </Popconfirm> ] }
const _columns = [ { title: 'ID', dataIndex: 'id', hideInTable: true, hideInSearch: true, formItemProps: { hidden: true } } ].concat(res as any).concat([ { title: getI18nTitle(curdModal?.config?.i18n, { dataIndex: 'option', title: '操作' },), dataIndex: 'option', valueType: 'option', fixed: 'right', render: (_, record, index, action) => { if (renderColumnOptions) { return renderColumnOptions(record, options(record), index, action) } return options(record) } } as any ]) setColumns(_columns) }, [ curdModal?.columns, curdModal?.config?.i18n, propColumns, renderColumnOptions, resolveColumns, deleteModel, form, isDeleting, setOpen, ])
useEffect(() => { if (apiCtx.isApi && apiCtx.api) { setApi(apiCtx.api) reloadCURDModal() } }, [ apiCtx.isApi, apiCtx.api ])
useDeepCompareEffect(() => {
setSearchKey(search?.key)
filterForm.setFieldsValue(search)
}, [ search ])
useEffect(() => { if (isSuccess) { setOpen(false) } }, [ isSuccess ])
const formProps = curdModal?.form.layoutType === 'DrawerForm' ? { layoutType: 'DrawerForm', drawerProps: { maskClosable: false, } } : { layoutType: 'ModalForm', modalProps: { maskClosable: false, } }
const renderTitle = () => { if (title) { return title }
if (apiCtx.menu) { const { menu } = apiCtx return menu.title } return null }
const tableTitle = <> <Button key={'add'} onClick={() => { form.resetFields() form.setFieldsValue({ id: 0, }) setOpen(true) }} type={'primary'}>{getI18nTitle('actions.add','添加')}</Button> </>
const _renderActions = () => { if (renderActions) { return renderActions(tableTitle) } return <Space> {[ tableTitle, ...actions ]} </Space> }
return ( <> <ListPageLayout className={styles.container} title={renderTitle()}>
<ProTable {...curdModal?.table} rowKey="id" headerTitle={_renderActions()} toolbar={{ /*search: { loading: isFetching && !!search?.key, onSearch: (value: string) => { setSearch(prev => ({ ...prev, title: value })) }, allowClear: true, onChange: (e) => { setSearchKey(e.target?.value) }, value: searchKey, placeholder: '输入关键字搜索', },*/ actions: [ <Tooltip key={'filter'} title={getI18nTitle('actions.advanceSearch','高级查询')}> <Badge count={getValueCount(search)}> <Button onClick={() => { setFilterOpen(true) }} icon={<FilterOutlined/>} shape={'circle'} size={'small'}/> </Badge> </Tooltip>, <Divider type={'vertical'} key={'divider'}/>,
] }} scroll={{ x: (columns?.length || 1) * 100, y: 'calc(100vh - 290px)' }} search={false} onRow={(record) => { return { className: cx({ // 'ant-table-row-selected': currentMovie?.id === record.id
}), onClick: () => { setModel(record) } } }} dateFormatter="string" loading={isLoading || isFetching || curdLoading} dataSource={data?.rows ?? []} columns={columns} options={{ reload: () => { refetch() }, }} pagination={{ total: data?.total, pageSize: search.pageSize, current: search.page, onShowSizeChange: (current: number, size: number) => { setSearch({ ...search, pageSize: size, page: current }) }, onChange: (current, pageSize) => { setSearch(prev => { return { ...prev, page: current, pageSize: pageSize, } }) }, }} />
<BetaSchemaForm {...curdModal?.form} grid={true} shouldUpdate={false} width={1000} form={form} layout={'vertical'} scrollToFirstError={true} title={model?.id !== 0 ? getI18nTitle('actions.edit','编辑') : getI18nTitle('actions.add','添加')} {...formProps as any} open={open} onOpenChange={(open) => { setOpen(open) }} loading={isSubmitting} onFinish={async (values) => { console.log(values) saveOrUpdate(values as any) }} columns={columns as ProFormColumnsType[]}/> <BetaSchemaForm {...curdModal?.form} title={getI18nTitle('actions.advanceSearch','高级查询')} grid={true} shouldUpdate={false} width={500} form={filterForm} open={openFilter} onOpenChange={open => { setFilterOpen(open) }} layout={'vertical'} scrollToFirstError={true} layoutType={formProps.layoutType as any} drawerProps={{ ...formProps.drawerProps, mask: false, }} modalProps={{ ...formProps.modalProps, mask: false, }} submitter={{ searchConfig: { resetText: getI18nTitle('actions.clear', '清空'), submitText: getI18nTitle('actions.search', '查询'), }, onReset: () => { filterForm.resetFields() }, render: (props,) => { return ( <div style={{ textAlign: 'right' }}> <Space> <Button onClick={() => { props.reset()
}}>{props.searchConfig?.resetText}</Button> <Button type="primary" onClick={() => { props.submit() }} >{props.searchConfig?.submitText}</Button> </Space> </div> ) },
}}
onFinish={async (values: any) => { //处理,变成数组
Object.keys(values).forEach(key => { if (typeof values[key] === 'string' && values[key].includes(',')) { values[key] = values[key].split(',') } })
setSearch(values)
}} columns={unSetColumnRules(columns.filter(item => !item.hideInSearch) as ProFormColumnsType[])}/>
</ListPageLayout> </> ) }
export default RForm
|