You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

362 lines
13 KiB

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