dark
3 months ago
10 changed files with 828 additions and 84 deletions
-
322src/components/r-form/index.tsx
-
13src/components/r-form/style.ts
-
90src/components/r-form/utils/index.tsx
-
15src/components/x-form/utils/index.tsx
-
26src/pages/r-form/index.tsx
-
29src/pages/r-form/style.ts
-
22src/service/r-form/model.ts
-
189src/store/r-form/model.ts
-
25src/types/r-form/model.d.ts
-
25src/utils/tree.ts
@ -0,0 +1,322 @@ |
|||
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' |
|||
|
|||
|
|||
export interface RFormProps { |
|||
title?: ReactNode |
|||
namespace?: string |
|||
columns?: ProColumns[] //重写columns
|
|||
} |
|||
|
|||
const RForm = ({ namespace, 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(() => { |
|||
|
|||
const res = transformAntdTableProColumns(curdModal?.columns || [], propColumns) |
|||
const _columns = [ { |
|||
title: 'ID', |
|||
dataIndex: 'id', |
|||
hideInTable: true, |
|||
hideInSearch: true, |
|||
formItemProps: { hidden: true } |
|||
} ].concat(res as any).concat([ |
|||
{ |
|||
title: '操作', |
|||
dataIndex: 'option', |
|||
valueType: 'option', |
|||
fixed: 'right', |
|||
render: (_, record) => [ |
|||
<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> |
|||
] |
|||
} as any |
|||
]) |
|||
setColumns(_columns) |
|||
}, [ curdModal?.columns, propColumns, 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'}>{'添加'}</Button> |
|||
</> |
|||
|
|||
return ( |
|||
<> |
|||
<ListPageLayout |
|||
className={styles.container} |
|||
title={renderTitle()}> |
|||
|
|||
<ProTable |
|||
{...curdModal?.table} |
|||
rowKey="id" |
|||
headerTitle={tableTitle} |
|||
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={'高级查询'}> |
|||
<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 ? '编辑' : '添加'} |
|||
{...formProps as any} |
|||
open={open} |
|||
onOpenChange={(open) => { |
|||
setOpen(open) |
|||
}} |
|||
loading={isSubmitting} |
|||
onFinish={async (values) => { |
|||
saveOrUpdate(values as any) |
|||
}} |
|||
columns={columns as ProFormColumnsType[]}/> |
|||
<BetaSchemaForm |
|||
{...curdModal?.form} |
|||
title={'高级查询'} |
|||
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: '清空', |
|||
submitText: '查询', |
|||
}, |
|||
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) => { |
|||
//处理,变成数组
|
|||
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 |
@ -0,0 +1,13 @@ |
|||
import { createStyles } from '@/theme' |
|||
|
|||
export const useStyle = createStyles(({ token, css, cx, prefixCls }, props: any) => { |
|||
const prefix = `${prefixCls}-${token?.proPrefix}-x-form-component` |
|||
|
|||
const container = css`
|
|||
|
|||
`
|
|||
|
|||
return { |
|||
container: cx(prefix, props?.className, container), |
|||
} |
|||
}) |
@ -0,0 +1,90 @@ |
|||
import { RFormTypes } from '@/types/r-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 } from '@/utils' |
|||
import { has, get } from 'lodash' |
|||
import { mapTree } from '@/utils/tree.ts' |
|||
|
|||
|
|||
const getValueType = (column: RFormTypes.IColumn) => { |
|||
|
|||
return column.valueType |
|||
} |
|||
|
|||
//根据type返回对应的组件
|
|||
const getComponent = (column: RFormTypes.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: RFormTypes.IColumn[], overwriteColumns?: ProColumns[]) => { |
|||
|
|||
const overwriteKeys = [] as string[] |
|||
|
|||
return (columns || []).map(item => { |
|||
|
|||
|
|||
const type = getValueType(item) |
|||
|
|||
const overwrite = overwriteColumns?.find(i => i.dataIndex === item.dataIndex || item.name) |
|||
if (overwrite) { |
|||
overwriteKeys.push(item.prop) |
|||
} |
|||
|
|||
return { |
|||
...item, |
|||
request: item.dicUrl ? async (params, props) => { |
|||
const { fieldProps: { dataFiledNames } } = props |
|||
const { value, res: resKey, label, disabled: disabledKey, children } = dataFiledNames || {} |
|||
const url = `/${item.dicUrl.replace(/^:/, '/')}` |
|||
return request[item.dicMethod || 'get'](url, params).then(res => { |
|||
const data = has(res.data, resKey) ? get(res.data, resKey) : res.data |
|||
|
|||
return mapTree(data || [], (i: any) => { |
|||
const disabled = has(i, disabledKey) ? get(i, disabledKey) : ('status' in i ? !convertToBool(i.status) : false) |
|||
return { |
|||
title: i[label || 'label'], |
|||
label: i[label || 'label'], |
|||
value: i[value || 'id'], |
|||
disabled, |
|||
data: i |
|||
} |
|||
|
|||
}, { children }) |
|||
}) |
|||
} : undefined, |
|||
// renderFormItem: (_scheam, config, dom) => {
|
|||
//
|
|||
// },
|
|||
render: (text: any, _record: any) => { |
|||
|
|||
return text |
|||
}, |
|||
...overwrite |
|||
} as ProColumns |
|||
}).concat(overwriteColumns?.filter(i => !overwriteKeys.includes(i.dataIndex)) || []) |
|||
|
|||
} |
@ -0,0 +1,26 @@ |
|||
import { createFileRoute } from '@tanstack/react-router' |
|||
import RForm from '@/components/r-form' |
|||
|
|||
const RFormRender = () => { |
|||
|
|||
return <> |
|||
<RForm/> |
|||
</> |
|||
} |
|||
|
|||
type RFormRouteSearch = { |
|||
api: string |
|||
} |
|||
|
|||
// @ts-ignore fix route id
|
|||
export const Route = createFileRoute('x-form')({ |
|||
validateSearch: (search: Record<string, unknown>): RFormRouteSearch => { |
|||
// validate and parse the search params into a typed state
|
|||
// console.log(search.id)
|
|||
return { |
|||
api: (search.api ?? '') as string |
|||
} as RFormRouteSearch |
|||
}, |
|||
}) |
|||
|
|||
export default RFormRender |
@ -0,0 +1,29 @@ |
|||
|
|||
import { createStyles } from '@/theme' |
|||
|
|||
export const useStyle = createStyles(({ token, css, cx, prefixCls }, props: any) => { |
|||
const prefix = `${prefixCls}-${token?.proPrefix}-r-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 { |
|||
container: cx(prefix, props?.className, container), |
|||
} |
|||
}) |
@ -0,0 +1,22 @@ |
|||
import { createCURD } from '@/service/base.ts' |
|||
import { RFormTypes } from '@/types/r-form/model' |
|||
import request from '@/request.ts' |
|||
|
|||
const model = (api: string) => { |
|||
return { |
|||
...createCURD<any, RFormTypes.IModel>(api), |
|||
proxy: async <T = RFormTypes.IModelCURD>(params?: { |
|||
path: string, |
|||
body: any, |
|||
method?: string |
|||
}) => { |
|||
return request.post<T>(`/x_form/proxy`, { |
|||
api: `${api}/ui/curd`, |
|||
method: 'post', |
|||
...params |
|||
}) |
|||
} |
|||
} |
|||
} |
|||
|
|||
export { model } |
@ -0,0 +1,189 @@ |
|||
import { Atom, atom } from 'jotai' |
|||
import { IApiResult, IPage, IPageResult } from '@/global' |
|||
import { |
|||
atomWithMutation, |
|||
atomWithQuery, |
|||
AtomWithQueryResult, |
|||
AtomWithMutationResult, |
|||
queryClientAtom |
|||
} from 'jotai-tanstack-query' |
|||
import { message } from 'antd' |
|||
import { t } from 'i18next' |
|||
import { RFormTypes } from '@/types/r-form/model' |
|||
import * as modelServ from '@/service/r-form/model' |
|||
import { PrimitiveAtom } from 'jotai/vanilla/atom' |
|||
|
|||
type SearchParams = IPage & { |
|||
key?: string |
|||
api: string |
|||
} |
|||
|
|||
|
|||
export type ModelContext = { |
|||
apiAtom: PrimitiveAtom<string> |
|||
modelIdAtom: PrimitiveAtom<number> |
|||
modelIdsAtom: PrimitiveAtom<number[]> |
|||
modelAtom: PrimitiveAtom<RFormTypes.IModel> |
|||
modelSearchAtom: PrimitiveAtom<SearchParams> |
|||
modelPageAtom: PrimitiveAtom<IPage> |
|||
modelCURDAtom: Atom<AtomWithQueryResult<RFormTypes.IModelCURD['page']>> |
|||
modelsAtom: Atom<AtomWithQueryResult<IPageResult<any>>> |
|||
saveOrUpdateModelAtom: Atom<AtomWithMutationResult<any, any, any, any>> |
|||
deleteModelAtom: Atom<AtomWithMutationResult<any, any, any, any>> |
|||
} |
|||
|
|||
export const modelContext = new Map<string, ModelContext>() |
|||
|
|||
// @ts-ignore fix debug modelContext
|
|||
window.__MODELCONTEXT__ = modelContext |
|||
|
|||
export const useSpanModel = (name: string) => { |
|||
|
|||
if (modelContext.has(name)) { |
|||
return modelContext.get(name) |
|||
} |
|||
|
|||
const apiAtom = atom('') |
|||
|
|||
const modelIdAtom = atom(0) |
|||
|
|||
const modelIdsAtom = atom<number[]>([]) |
|||
|
|||
const modelAtom = atom<RFormTypes.IModel>(undefined as unknown as RFormTypes.IModel) |
|||
|
|||
const modelSearchAtom = atom<SearchParams>({ |
|||
key: '', |
|||
pageSize: 10, |
|||
page: 1, |
|||
} as SearchParams) |
|||
|
|||
const modelPageAtom = atom<IPage>({ |
|||
pageSize: 10, |
|||
page: 1, |
|||
}) |
|||
|
|||
const modelCURDAtom = atomWithQuery<IApiResult<RFormTypes.IModelCURD>, any, any, any>((get) => { |
|||
|
|||
return { |
|||
enabled: !!get(apiAtom), |
|||
queryKey: [ 'modelCURD', get(apiAtom) ], |
|||
queryFn: async ({ queryKey: [ , api ] }) => { |
|||
return await modelServ.model(api).proxy() |
|||
}, |
|||
select: (res) => { |
|||
return res.data.page |
|||
} |
|||
} |
|||
}) |
|||
|
|||
const modelsAtom = atomWithQuery<IApiResult<IPageResult<any>>, any, any, any>((get) => { |
|||
|
|||
const curd = get(modelCURDAtom) |
|||
return { |
|||
enabled: curd.isSuccess && !!get(apiAtom), |
|||
queryKey: [ 'models', get(modelSearchAtom), get(apiAtom) ], |
|||
queryFn: async ({ queryKey: [ , params, api ] }) => { |
|||
|
|||
if (api.startsWith('http')) { |
|||
return await modelServ.model(api).proxy<RFormTypes.IModel>({ |
|||
path: '/list', |
|||
body: params, |
|||
}) |
|||
} |
|||
return await modelServ.model(api).list(params as SearchParams) |
|||
}, |
|||
select: res => { |
|||
const data = res.data |
|||
data.rows = data.rows?.map(row => { |
|||
return { |
|||
...row, |
|||
//status: convertToBool(row.status)
|
|||
} |
|||
}) |
|||
return data |
|||
} |
|||
} |
|||
}) |
|||
|
|||
//saveOrUpdateAtom
|
|||
const saveOrUpdateModelAtom = atomWithMutation<IApiResult, RFormTypes.IModel>((get) => { |
|||
|
|||
return { |
|||
mutationKey: [ 'updateModel', get(apiAtom) ], |
|||
mutationFn: async (data) => { |
|||
const api = get(apiAtom) |
|||
if (!api) { |
|||
return Promise.reject('api 不能为空') |
|||
} |
|||
if (data.id === 0) { |
|||
if (api.startsWith('http')) { |
|||
return await modelServ.model(api).proxy<RFormTypes.IModel>({ |
|||
body: data, |
|||
path: '/add', |
|||
}) |
|||
} |
|||
return await modelServ.model(api).add(data) |
|||
} |
|||
if (api.startsWith('http')) { |
|||
return await modelServ.model(api).proxy<RFormTypes.IModel>({ |
|||
body: data, |
|||
path: '/edit', |
|||
}) |
|||
} |
|||
return await modelServ.model(api).update(data) |
|||
}, |
|||
onSuccess: (res) => { |
|||
const isAdd = !!res.data?.id |
|||
message.success(t(isAdd ? 'message.saveSuccess' : 'message.editSuccess', '保存成功')) |
|||
|
|||
//更新列表
|
|||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|||
// @ts-ignore fix
|
|||
get(queryClientAtom).invalidateQueries({ queryKey: [ 'models', get(modelSearchAtom) ] }) |
|||
|
|||
return res |
|||
} |
|||
} |
|||
}) |
|||
|
|||
const deleteModelAtom = atomWithMutation((get) => { |
|||
|
|||
return { |
|||
mutationKey: [ 'deleteModel', get(apiAtom) ], |
|||
mutationFn: async (ids: number[]) => { |
|||
const api = get(apiAtom) |
|||
if (api.startsWith('http')) { |
|||
return await modelServ.model(api).proxy<RFormTypes.IModel>({ |
|||
body: ids ?? get(modelIdsAtom), |
|||
path: '/deletes', |
|||
}) |
|||
} |
|||
return await modelServ.model(api).batchDelete(ids ?? get(modelIdsAtom)) |
|||
}, |
|||
onSuccess: (res) => { |
|||
message.success('message.deleteSuccess') |
|||
//更新列表
|
|||
get(queryClientAtom).invalidateQueries({ queryKey: [ 'models', get(modelSearchAtom) ] }) |
|||
return res |
|||
} |
|||
} |
|||
}) |
|||
|
|||
|
|||
const value = { |
|||
apiAtom, |
|||
modelIdAtom, |
|||
modelIdsAtom, |
|||
modelAtom, |
|||
modelSearchAtom, |
|||
modelPageAtom, |
|||
modelCURDAtom, |
|||
modelsAtom, |
|||
saveOrUpdateModelAtom, |
|||
deleteModelAtom, |
|||
} as ModelContext |
|||
|
|||
modelContext.set(name, value) |
|||
|
|||
return value |
|||
} |
@ -0,0 +1,25 @@ |
|||
import { ProColumns, ProTableProps, } from '@ant-design/pro-components' |
|||
import { FormSchema } from '@ant-design/pro-form/es/components/SchemaForm/typing' |
|||
|
|||
export namespace RFormTypes { |
|||
export interface IModel { |
|||
id: number; |
|||
created_at: string; |
|||
updated_at: string; |
|||
|
|||
[key: string]: any; |
|||
} |
|||
|
|||
export interface IModelCURD { |
|||
|
|||
page: { |
|||
columns: ProColumns[]; |
|||
config: any; |
|||
table: ProTableProps<any, any> |
|||
form: FormSchema |
|||
} |
|||
} |
|||
|
|||
|
|||
} |
|||
|
Write
Preview
Loading…
Cancel
Save
Reference in new issue