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
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
|