dark
3 months ago
33 changed files with 816 additions and 1260 deletions
-
43.eslintrc.cjs
-
6package.json
-
2src/components/cascader/Cascader.tsx
-
21src/components/crazy-form/context.ts
-
222src/components/crazy-form/index.tsx
-
80src/components/crazy-form/tabs-form/TabForm.tsx
-
375src/components/crazy-form/tabs-form/index.tsx
-
74src/components/crazy-form/typeing.d.ts
-
319src/components/x-form/index.tsx
-
13src/components/x-form/style.ts
-
155src/components/x-form/utils/index.tsx
-
19src/context.ts
-
0src/hooks/useNavigate.ts
-
2src/layout/EmptyLayout.tsx
-
31src/layout/RootLayout.tsx
-
2src/pages/app/package/index.tsx
-
16src/pages/system/menus/index.tsx
-
3src/pages/system/roles/index.tsx
-
20src/pages/x-form/hooks/useApi.tsx
-
298src/pages/x-form/index.tsx
-
8src/pages/x-form/utils/index.tsx
-
1src/request.ts
-
33src/routes.tsx
-
4src/service/base.ts
-
6src/service/x-form/model.ts
-
4src/store/app/package.ts
-
3src/store/system/menu.ts
-
259src/store/x-form/model.ts
-
2src/types/x-form/model.d.ts
-
1src/utils/index.ts
-
13src/utils/tree.ts
-
5tsconfig.json
-
34vite.config.ts
@ -1,24 +1,25 @@ |
|||||
module.exports = { |
module.exports = { |
||||
root: true, |
|
||||
env: { browser: true, es2020: true }, |
|
||||
extends: [ |
|
||||
'eslint:recommended', |
|
||||
'plugin:@typescript-eslint/recommended', |
|
||||
'plugin:react-hooks/recommended', |
|
||||
|
root: true, |
||||
|
env: { browser: true, es2020: true }, |
||||
|
extends: [ |
||||
|
'eslint:recommended', |
||||
|
'plugin:@typescript-eslint/recommended', |
||||
|
'plugin:react-hooks/recommended', |
||||
|
], |
||||
|
ignorePatterns: [ 'dist', '.eslintrc.cjs' ], |
||||
|
parser: '@typescript-eslint/parser', |
||||
|
plugins: [ 'react-refresh' ], |
||||
|
rules: { |
||||
|
'react-refresh/only-export-components': [ |
||||
|
'warn', |
||||
|
{ allowConstantExport: true }, |
||||
], |
], |
||||
ignorePatterns: [ 'dist', '.eslintrc.cjs' ], |
|
||||
parser: '@typescript-eslint/parser', |
|
||||
plugins: [ 'react-refresh' ], |
|
||||
rules: { |
|
||||
'react-refresh/only-export-components': [ |
|
||||
'warn', |
|
||||
{ allowConstantExport: true }, |
|
||||
], |
|
||||
'@typescript-eslint/ban-ts-comment': [ 'error', { |
|
||||
'ts-expect-error': 'allow-with-description', |
|
||||
'ts-ignore': 'allow-with-description', |
|
||||
'minimumDescriptionLength': 10 |
|
||||
} ], |
|
||||
'@typescript-eslint/no-explicit-any': 'off', |
|
||||
}, |
|
||||
|
'@typescript-eslint/ban-ts-comment': [ 'error', { |
||||
|
'ts-expect-error': 'allow-with-description', |
||||
|
'ts-ignore': 'allow-with-description', |
||||
|
'minimumDescriptionLength': 10 |
||||
|
} ], |
||||
|
'@typescript-eslint/no-explicit-any': 'off', |
||||
|
'no-unused-vars': 'off', |
||||
|
}, |
||||
} |
} |
@ -1,21 +0,0 @@ |
|||||
import React from 'react' |
|
||||
import { FormInstance } from 'antd/lib' |
|
||||
import { CrazyChildFormProps } from './typeing' |
|
||||
|
|
||||
|
|
||||
export const CrazyFormProvide = React.createContext< |
|
||||
| { |
|
||||
regForm: (name: string, props: CrazyChildFormProps<any>) => void; |
|
||||
unRegForm: (name: string) => void; |
|
||||
onFormFinish: (name: string, formData: any) => void; |
|
||||
keyArray: string[]; |
|
||||
formArrayRef: React.MutableRefObject< |
|
||||
React.MutableRefObject<FormInstance<any> | undefined>[] |
|
||||
>; |
|
||||
loading: boolean; |
|
||||
setLoading: (loading: boolean) => void; |
|
||||
formMapRef: React.MutableRefObject<Map<string, CrazyChildFormProps>>; |
|
||||
} |
|
||||
| undefined |
|
||||
>(undefined) |
|
||||
|
|
@ -1,222 +0,0 @@ |
|||||
import { |
|
||||
FormSchema, |
|
||||
ItemType, |
|
||||
ProFormRenderValueTypeHelpers |
|
||||
} from '@ant-design/pro-form/es/components/SchemaForm/typing' |
|
||||
import { StepsForm, Embed } from '@ant-design/pro-form/es/components/SchemaForm/layoutType' |
|
||||
import { renderValueType } from '@ant-design/pro-form/es/components/SchemaForm/valueType' |
|
||||
import { |
|
||||
DrawerForm, FormProps, LabelIconTip, |
|
||||
LightFilter, |
|
||||
ModalForm, omitUndefined, |
|
||||
ProForm, ProFormColumnsType, ProFormInstance, ProFormProps, |
|
||||
QueryFilter, runFunction, |
|
||||
StepsForm as ProStepsForm, stringify, useDeepCompareMemo, useLatest, useReactiveRef, useRefFunction |
|
||||
} from '@ant-design/pro-components' |
|
||||
import React, { useCallback, useImperativeHandle, useRef, useState } from 'react' |
|
||||
import { TabsForm } from './tabs-form' |
|
||||
import { Form } from 'antd' |
|
||||
|
|
||||
export type CrazyBateFormProps<T, ValueType> = { |
|
||||
layoutType: FormSchema<T, ValueType>['layoutType'] | 'TabsForm' |
|
||||
} & Omit<FormSchema<T, ValueType>, 'layout'> |
|
||||
|
|
||||
|
|
||||
const FormLayoutType = { |
|
||||
DrawerForm, |
|
||||
QueryFilter, |
|
||||
LightFilter, StepForm: ProStepsForm.StepForm, |
|
||||
|
|
||||
StepsForm: StepsForm, |
|
||||
ModalForm, |
|
||||
Embed, |
|
||||
Form: ProForm, |
|
||||
TabsForm, |
|
||||
} |
|
||||
|
|
||||
const CrazyBateForm = <T, ValueType = 'text'>(props: CrazyBateFormProps<T, ValueType>) => { |
|
||||
|
|
||||
const { |
|
||||
columns, |
|
||||
layoutType = 'TabsForm', |
|
||||
type = 'form', |
|
||||
action, |
|
||||
shouldUpdate = (pre, next) => stringify(pre) !== stringify(next), |
|
||||
formRef: propsFormRef, |
|
||||
...restProps |
|
||||
} = props |
|
||||
|
|
||||
const FormRenderComponents = (FormLayoutType[layoutType as 'TabsForm'] || |
|
||||
ProForm) as React.FC<ProFormProps<T>> |
|
||||
|
|
||||
const [ form ] = Form.useForm() |
|
||||
const formInstance = Form.useFormInstance() |
|
||||
|
|
||||
const [ , forceUpdate ] = useState<[]>([]) |
|
||||
const [ formDomsDeps, updatedFormDoms ] = useState<[]>(() => []) |
|
||||
|
|
||||
const formRef = useReactiveRef<ProFormInstance | undefined>( |
|
||||
props.form || formInstance || form, |
|
||||
) |
|
||||
const oldValuesRef = useRef<T>() |
|
||||
const propsRef = useLatest(props) |
|
||||
|
|
||||
/** |
|
||||
* 生成子项,方便被 table 接入 |
|
||||
* |
|
||||
* @param items |
|
||||
*/ |
|
||||
const genItems: ProFormRenderValueTypeHelpers<T, ValueType>['genItems'] = |
|
||||
useRefFunction((items: ProFormColumnsType<T, ValueType>[]) => { |
|
||||
return items |
|
||||
.filter((originItem) => { |
|
||||
return !(originItem.hideInForm && type === 'form') |
|
||||
}) |
|
||||
.sort((a, b) => { |
|
||||
if (b.order || a.order) { |
|
||||
return (b.order || 0) - (a.order || 0) |
|
||||
} |
|
||||
return (b.index || 0) - (a.index || 0) |
|
||||
}) |
|
||||
.map((originItem, index) => { |
|
||||
const title = runFunction( |
|
||||
originItem.title, |
|
||||
originItem, |
|
||||
'form', |
|
||||
<LabelIconTip |
|
||||
label={originItem.title as string} |
|
||||
//@ts-ignore @ts-expect-error
|
|
||||
tooltip={originItem.tooltip || originItem.tip} |
|
||||
/>, |
|
||||
) |
|
||||
|
|
||||
const item = omitUndefined({ |
|
||||
title, |
|
||||
label: title, |
|
||||
name: originItem.name, |
|
||||
valueType: runFunction(originItem.valueType, {}), |
|
||||
key: originItem.key || originItem.dataIndex || index, |
|
||||
columns: originItem.columns, |
|
||||
valueEnum: originItem.valueEnum, |
|
||||
dataIndex: originItem.dataIndex || originItem.key, |
|
||||
initialValue: originItem.initialValue, |
|
||||
width: originItem.width, |
|
||||
index: originItem.index, |
|
||||
readonly: originItem.readonly, |
|
||||
colSize: originItem.colSize, |
|
||||
colProps: originItem.colProps, |
|
||||
rowProps: originItem.rowProps, |
|
||||
className: originItem.className, |
|
||||
//@ts-ignore @ts-expect-error
|
|
||||
tooltip: originItem.tooltip || originItem.tip, |
|
||||
dependencies: originItem.dependencies, |
|
||||
proFieldProps: originItem.proFieldProps, |
|
||||
ignoreFormItem: originItem.ignoreFormItem, |
|
||||
getFieldProps: originItem.fieldProps |
|
||||
? () => |
|
||||
runFunction( |
|
||||
originItem.fieldProps, |
|
||||
formRef.current, |
|
||||
originItem, |
|
||||
) |
|
||||
: undefined, |
|
||||
getFormItemProps: originItem.formItemProps |
|
||||
? () => |
|
||||
runFunction( |
|
||||
originItem.formItemProps, |
|
||||
formRef.current, |
|
||||
originItem, |
|
||||
) |
|
||||
: undefined, |
|
||||
render: originItem.render, |
|
||||
renderFormItem: originItem.renderFormItem, |
|
||||
renderText: originItem.renderText, |
|
||||
request: originItem.request, |
|
||||
params: originItem.params, |
|
||||
transform: originItem.transform, |
|
||||
convertValue: originItem.convertValue, |
|
||||
debounceTime: originItem.debounceTime, |
|
||||
defaultKeyWords: originItem.defaultKeyWords, |
|
||||
}) as ItemType<any, any> |
|
||||
|
|
||||
return renderValueType(item, { |
|
||||
action, |
|
||||
type, |
|
||||
originItem, |
|
||||
formRef, |
|
||||
genItems, |
|
||||
}) |
|
||||
}) |
|
||||
.filter((field) => { |
|
||||
return Boolean(field) |
|
||||
}) |
|
||||
}) |
|
||||
|
|
||||
const onValuesChange: FormProps<T>['onValuesChange'] = useCallback( |
|
||||
(changedValues: any, values: T) => { |
|
||||
const { onValuesChange: propsOnValuesChange } = propsRef.current |
|
||||
if ( |
|
||||
shouldUpdate === true || |
|
||||
(typeof shouldUpdate === 'function' && |
|
||||
shouldUpdate(values, oldValuesRef.current)) |
|
||||
) { |
|
||||
updatedFormDoms([]) |
|
||||
} |
|
||||
oldValuesRef.current = values |
|
||||
propsOnValuesChange?.(changedValues, values) |
|
||||
}, |
|
||||
[ propsRef, shouldUpdate ], |
|
||||
) |
|
||||
|
|
||||
const formChildrenDoms = useDeepCompareMemo(() => { |
|
||||
if (!formRef.current) return |
|
||||
// like StepsForm's columns but not only for StepsForm
|
|
||||
if (columns.length && Array.isArray(columns[0])) return |
|
||||
return genItems(columns as ProFormColumnsType<T, ValueType>[]) |
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||
}, [ columns, restProps?.open, action, type, formDomsDeps, !!formRef.current ]) |
|
||||
|
|
||||
/** |
|
||||
* Append layoutType component specific props |
|
||||
*/ |
|
||||
const specificProps = useDeepCompareMemo(() => { |
|
||||
if (layoutType === 'StepsForm') { |
|
||||
return { |
|
||||
forceUpdate: forceUpdate, |
|
||||
columns: columns as ProFormColumnsType<T, ValueType>[][], |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
return {} |
|
||||
}, [ columns, layoutType ]) |
|
||||
|
|
||||
useImperativeHandle( |
|
||||
propsFormRef, |
|
||||
() => { |
|
||||
return formRef.current |
|
||||
}, |
|
||||
[ formRef.current ], |
|
||||
) |
|
||||
|
|
||||
return ( |
|
||||
<FormRenderComponents |
|
||||
{...specificProps} |
|
||||
{...restProps} |
|
||||
onInit={(_, initForm) => { |
|
||||
if (propsFormRef) { |
|
||||
(propsFormRef as React.MutableRefObject<ProFormInstance<T>>).current = |
|
||||
initForm |
|
||||
} |
|
||||
restProps?.onInit?.(_, initForm) |
|
||||
formRef.current = initForm |
|
||||
}} |
|
||||
form={props.form || form} |
|
||||
formRef={formRef} |
|
||||
onValuesChange={onValuesChange} |
|
||||
> |
|
||||
{formChildrenDoms} |
|
||||
</FormRenderComponents> |
|
||||
) |
|
||||
} |
|
||||
|
|
||||
export default CrazyBateForm |
|
@ -1,80 +0,0 @@ |
|||||
import { useContext, useEffect, useImperativeHandle, useRef } from 'react' |
|
||||
import { TabsProps } from 'antd' |
|
||||
import { noteOnce } from 'rc-util/lib/warning' |
|
||||
import { FormInstance } from 'antd/lib' |
|
||||
import { CrazyFormProvide } from '../context.ts' |
|
||||
import { TabFormProvide } from './index.tsx' |
|
||||
import { CrazyChildFormProps } from '../typeing' |
|
||||
|
|
||||
export type TabFormProps<T = Record<string, any>> = { |
|
||||
tab?: string; |
|
||||
tabProps?: TabsProps; |
|
||||
} & CrazyChildFormProps<T> |
|
||||
|
|
||||
const TabForm = <T = Record<string, any>>(props: TabFormProps<T>) => { |
|
||||
|
|
||||
const formRef = useRef<FormInstance | undefined>() |
|
||||
const context = useContext(CrazyFormProvide) |
|
||||
const tabContext = useContext(TabFormProvide) |
|
||||
|
|
||||
const { |
|
||||
onFinish, |
|
||||
tab, |
|
||||
formRef: propFormRef, |
|
||||
tabProps, |
|
||||
...restProps |
|
||||
} = props |
|
||||
|
|
||||
noteOnce(!(restProps as any).submitter, 'TabForm 不包含提交按钮,请在 TabsForm 上') |
|
||||
|
|
||||
/** 重置 formRef */ |
|
||||
useImperativeHandle(propFormRef, () => formRef.current, [ |
|
||||
propFormRef?.current, |
|
||||
]) |
|
||||
|
|
||||
/** Dom 不存在的时候解除挂载 */ |
|
||||
useEffect(() => { |
|
||||
if (!(props.name || props.tab)) return |
|
||||
const name = (props.name || props.tab)!.toString() |
|
||||
context?.regForm(name, props) |
|
||||
return () => { |
|
||||
context?.unRegForm(name) |
|
||||
} |
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||
}, []) |
|
||||
|
|
||||
if (context && context?.formArrayRef) { |
|
||||
context.formArrayRef.current[tab || 0] = formRef |
|
||||
} |
|
||||
|
|
||||
return ( |
|
||||
<BaseForm |
|
||||
formRef={formRef} |
|
||||
onFinish={async (values) => { |
|
||||
if (restProps.name) { |
|
||||
context?.onFormFinish(restProps.name, values) |
|
||||
} |
|
||||
if (onFinish) { |
|
||||
context?.setLoading(true) |
|
||||
// 如果报错,直接抛出
|
|
||||
await onFinish?.(values) |
|
||||
|
|
||||
context?.setLoading(false) |
|
||||
return |
|
||||
} |
|
||||
|
|
||||
}} |
|
||||
onInit={(_, form) => { |
|
||||
formRef.current = form |
|
||||
if (context && context?.formArrayRef) { |
|
||||
context.formArrayRef.current[tab || 0] = formRef |
|
||||
} |
|
||||
restProps?.onInit?.(_, form) |
|
||||
}} |
|
||||
layout="vertical" |
|
||||
{...omit(restProps, [ 'layoutType', 'columns' ] as any[])} |
|
||||
/> |
|
||||
) |
|
||||
} |
|
||||
|
|
||||
export default TabForm |
|
@ -1,375 +0,0 @@ |
|||||
import React, { useCallback, useImperativeHandle, useMemo, useRef, useState } from 'react' |
|
||||
import TabForm, { TabFormProps } from './TabForm' |
|
||||
import { CrazyFormProps } from '../typeing' |
|
||||
import { Button, Col, Form, Row, Space, Tabs, TabsProps } from 'antd' |
|
||||
import { FormInstance } from 'antd/lib' |
|
||||
import toArray from 'rc-util/lib/Children/toArray' |
|
||||
import useMergedState from 'rc-util/lib/hooks/useMergedState' |
|
||||
import { merge, ProConfigProvider, useRefFunction } from '@ant-design/pro-components' |
|
||||
import { t } from '@/i18n' |
|
||||
import classnames from 'classnames' |
|
||||
import { CrazyFormProvide } from '../context' |
|
||||
|
|
||||
type TabsFormProps<T = Record<string, any>> = { |
|
||||
toggleProps: TabsProps |
|
||||
direction?: 'horizontal' | 'vertical' |
|
||||
} & Omit<CrazyFormProps<T>, 'toggleProps'> |
|
||||
|
|
||||
export const TabFormProvide = React.createContext<TabFormProps<any> | null>(null) |
|
||||
|
|
||||
|
|
||||
const TabsLayoutStrategy: Record< |
|
||||
string, |
|
||||
(dom: LayoutRenderDom) => React.ReactNode |
|
||||
> = { |
|
||||
horizontal({ toggleDom, formDom }) { |
|
||||
return ( |
|
||||
<> |
|
||||
<Row gutter={{ xs: 8, sm: 16, md: 24 }}> |
|
||||
<Col span={24}>{toggleDom}</Col> |
|
||||
</Row> |
|
||||
<Row gutter={{ xs: 8, sm: 16, md: 24 }}> |
|
||||
<Col span={24}>{formDom}</Col> |
|
||||
</Row> |
|
||||
</> |
|
||||
) |
|
||||
}, |
|
||||
vertical({ stepsDom, formDom }) { |
|
||||
return ( |
|
||||
<Row align="stretch" wrap={true} gutter={{ xs: 8, sm: 16, md: 24 }}> |
|
||||
<Col xxl={4} xl={6} lg={7} md={8} sm={10} xs={12}> |
|
||||
{React.cloneElement(stepsDom, { |
|
||||
style: { |
|
||||
height: '100%', |
|
||||
}, |
|
||||
})} |
|
||||
</Col> |
|
||||
<Col> |
|
||||
<div |
|
||||
style={{ |
|
||||
display: 'flex', |
|
||||
alignItems: 'center', |
|
||||
width: '100%', |
|
||||
height: '100%', |
|
||||
}} |
|
||||
> |
|
||||
{formDom} |
|
||||
</div> |
|
||||
</Col> |
|
||||
</Row> |
|
||||
) |
|
||||
}, |
|
||||
} |
|
||||
|
|
||||
|
|
||||
const TabsForm = <T = Record<string, any>>( |
|
||||
props: TabsFormProps<T> & { |
|
||||
children: React.ReactNode |
|
||||
} |
|
||||
) => { |
|
||||
|
|
||||
const { |
|
||||
toggleProps, |
|
||||
toggleFormRender, |
|
||||
direction = 'horizontal', |
|
||||
current: tab, |
|
||||
onCurrentChange, |
|
||||
submitter, |
|
||||
formRender, |
|
||||
onFinish, |
|
||||
formProps, |
|
||||
containerStyle, |
|
||||
formRef, |
|
||||
formMapRef: propsFormMapRef, |
|
||||
layoutRender: propsLayoutRender, |
|
||||
...rest |
|
||||
} = props |
|
||||
|
|
||||
|
|
||||
const formDataRef = useRef(new Map<string, Record<string, any>>()) |
|
||||
const formMapRef = useRef(new Map<string, TabFormProps>()) |
|
||||
const formArrayRef = useRef< |
|
||||
React.MutableRefObject<FormInstance<any> | undefined>[] |
|
||||
>([]) |
|
||||
const [ formArray, setFormArray ] = useState<string[]>([]) |
|
||||
const [ loading, setLoading ] = useState<boolean>(false) |
|
||||
|
|
||||
/** |
|
||||
* 受控的方式来操作表单 |
|
||||
*/ |
|
||||
const [ tab, setTab ] = useMergedState<number>(0, { |
|
||||
value: props.current, |
|
||||
onChange: props.onCurrentChange, |
|
||||
}) |
|
||||
|
|
||||
const layoutRender = useMemo(() => { |
|
||||
return TabsLayoutStrategy[direction] |
|
||||
}, [ direction ]) |
|
||||
|
|
||||
|
|
||||
/** |
|
||||
* 注册一个form进入,方便进行 props 的修改 |
|
||||
*/ |
|
||||
const regForm = useCallback( |
|
||||
(name: string, childrenFormProps: TabFormProps) => { |
|
||||
if (!formMapRef.current.has(name)) { |
|
||||
setFormArray((oldFormArray) => [ ...oldFormArray, name ]) |
|
||||
} |
|
||||
formMapRef.current.set(name, childrenFormProps) |
|
||||
}, |
|
||||
[], |
|
||||
) |
|
||||
|
|
||||
/** |
|
||||
* 解除挂载掉这个 form,同时步数 -1 |
|
||||
*/ |
|
||||
const unRegForm = useCallback((name: string) => { |
|
||||
setFormArray((oldFormArray) => oldFormArray.filter((n) => n !== name)) |
|
||||
formMapRef.current.delete(name) |
|
||||
formDataRef.current.delete(name) |
|
||||
}, []) |
|
||||
|
|
||||
useImperativeHandle(propsFormMapRef, () => formArrayRef.current, [ |
|
||||
formArrayRef.current, |
|
||||
]) |
|
||||
|
|
||||
useImperativeHandle( |
|
||||
formRef, |
|
||||
() => { |
|
||||
return formArrayRef.current[tab || 0]?.current |
|
||||
}, |
|
||||
[ tab, formArrayRef.current ], |
|
||||
) |
|
||||
|
|
||||
/** |
|
||||
* ProForm处理了一下 from 的数据,在其中做了一些操作 如果使用 Provider 自带的,自带的数据处理就无法生效了 |
|
||||
*/ |
|
||||
const onFormFinish = useCallback( |
|
||||
async (name: string, formData: any) => { |
|
||||
formDataRef.current.set(name, formData) |
|
||||
|
|
||||
|
|
||||
setLoading(true) |
|
||||
const values: any = merge( |
|
||||
{}, |
|
||||
...Array.from(formDataRef.current.values()), |
|
||||
) |
|
||||
try { |
|
||||
const success = await onFinish(values) |
|
||||
if (success) { |
|
||||
formArrayRef.current.forEach((form) => form.current?.resetFields()) |
|
||||
} |
|
||||
} catch (error) { |
|
||||
console.log(error) |
|
||||
} finally { |
|
||||
setLoading(false) |
|
||||
} |
|
||||
}, |
|
||||
[ lastStep, onFinish, setLoading, setTab ], |
|
||||
) |
|
||||
|
|
||||
const toggleDoms = useMemo(() => { |
|
||||
const itemsProps = { |
|
||||
items: formArray.map((item) => { |
|
||||
const itemProps = formMapRef.current.get(item) |
|
||||
return { |
|
||||
key: item, |
|
||||
title: itemProps?.title, |
|
||||
...itemProps?.tabProps, |
|
||||
} |
|
||||
}), |
|
||||
} |
|
||||
|
|
||||
return ( |
|
||||
<div className={`crazy-tabs-container`.trim()} |
|
||||
|
|
||||
> |
|
||||
<Tabs |
|
||||
{...toggleProps} |
|
||||
{...itemsProps} |
|
||||
activeKey={tab} |
|
||||
onChange={onCurrentChange} |
|
||||
> |
|
||||
</Tabs> |
|
||||
</div> |
|
||||
) |
|
||||
}, [ formArray, tab, toggleProps, onCurrentChange ]) |
|
||||
|
|
||||
const onSubmit = useRefFunction(() => { |
|
||||
const from = formArrayRef.current[tab] |
|
||||
from.current?.submit() |
|
||||
}) |
|
||||
|
|
||||
const submit = useMemo(() => { |
|
||||
return ( |
|
||||
submitter !== false && ( |
|
||||
<Button |
|
||||
key="submit" |
|
||||
type="primary" |
|
||||
loading={loading} |
|
||||
{...submitter?.submitButtonProps} |
|
||||
onClick={() => { |
|
||||
submitter?.onSubmit?.() |
|
||||
onSubmit() |
|
||||
}} |
|
||||
> |
|
||||
{t('actions.submit', '提交')} |
|
||||
</Button> |
|
||||
) |
|
||||
) |
|
||||
}, [ loading, onSubmit, submitter ]) |
|
||||
|
|
||||
|
|
||||
const submitterDom = useMemo(() => { |
|
||||
let buttons: (React.ReactElement | false)[] = [ submit ] |
|
||||
buttons = buttons.filter(React.isValidElement) |
|
||||
|
|
||||
if (submitter && submitter.render) { |
|
||||
const submitterProps: any = { |
|
||||
form: formArrayRef.current[tab]?.current, |
|
||||
onSubmit, |
|
||||
current: tab, |
|
||||
} |
|
||||
|
|
||||
return submitter.render( |
|
||||
submitterProps, |
|
||||
buttons as React.ReactElement[], |
|
||||
) as React.ReactNode |
|
||||
} |
|
||||
if (submitter && submitter?.render === false) { |
|
||||
return null |
|
||||
} |
|
||||
return buttons as React.ReactElement[] |
|
||||
}, [ formArray.length, onSubmit, tab, submit, submitter ]) |
|
||||
|
|
||||
const formDom = useMemo(() => { |
|
||||
return toArray(props.children).map((item, index) => { |
|
||||
const itemProps = item.props as TabFormProps |
|
||||
const name = itemProps.name || `${index}` |
|
||||
/** 是否是当前的表单 */ |
|
||||
const isShow = tab === name |
|
||||
|
|
||||
const config = isShow |
|
||||
? { |
|
||||
contentRender: formRender, |
|
||||
} |
|
||||
: {} |
|
||||
return ( |
|
||||
<div |
|
||||
className={classnames(`crazy-tab`, { |
|
||||
[`crazy-tab-active`]: isShow, |
|
||||
})} |
|
||||
key={name} |
|
||||
> |
|
||||
<TabFormProvide.Provider |
|
||||
value={{ |
|
||||
...config, |
|
||||
...formProps, |
|
||||
...itemProps, |
|
||||
name, |
|
||||
index, |
|
||||
tab: name, |
|
||||
}} |
|
||||
> |
|
||||
{item} |
|
||||
</TabFormProvide.Provider> |
|
||||
</div> |
|
||||
) |
|
||||
}) |
|
||||
}, [ formProps, props.children, tab, formRender ]) |
|
||||
|
|
||||
const finalTabsDom = useMemo(() => { |
|
||||
if (toggleFormRender) { |
|
||||
return toggleFormRender( |
|
||||
formArray.map((item) => ({ |
|
||||
key: item, |
|
||||
title: formMapRef.current.get(item)?.title, |
|
||||
})), |
|
||||
toggleDoms, |
|
||||
) as React.ReactElement |
|
||||
} |
|
||||
return toggleDoms |
|
||||
}, [ formArray, toggleDoms, toggleFormRender ]) |
|
||||
|
|
||||
const formContainer = useMemo( |
|
||||
() => ( |
|
||||
<div |
|
||||
className={`crazy-container`.trim()} |
|
||||
style={containerStyle} |
|
||||
> |
|
||||
{formDom} |
|
||||
{toggleFormRender ? null : <Space>{submitterDom}</Space>} |
|
||||
</div> |
|
||||
), |
|
||||
[ containerStyle, formDom, toggleFormRender, submitterDom ], |
|
||||
) |
|
||||
|
|
||||
const tabsFormDom = useMemo(() => { |
|
||||
const doms = { |
|
||||
toggleDom: finalTabsDom, |
|
||||
formDom: formContainer, |
|
||||
} |
|
||||
|
|
||||
if (toggleFormRender) { |
|
||||
if (propsLayoutRender) { |
|
||||
return toggleFormRender(propsLayoutRender(doms), submitterDom) |
|
||||
} else { |
|
||||
return toggleFormRender(layoutRender(doms), submitterDom) |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
if (propsLayoutRender) { |
|
||||
return propsLayoutRender(doms) |
|
||||
} |
|
||||
|
|
||||
return layoutRender(doms) |
|
||||
}, [ |
|
||||
finalTabsDom, |
|
||||
formContainer, |
|
||||
layoutRender, |
|
||||
toggleFormRender, |
|
||||
submitterDom, |
|
||||
propsLayoutRender, |
|
||||
]) |
|
||||
|
|
||||
|
|
||||
return ( |
|
||||
<div> |
|
||||
<Form.Provider {...rest}> |
|
||||
<CrazyFormProvide.Provider |
|
||||
value={{ |
|
||||
loading, |
|
||||
setLoading, |
|
||||
regForm, |
|
||||
keyArray: formArray, |
|
||||
formArrayRef, |
|
||||
formMapRef, |
|
||||
unRegForm, |
|
||||
onFormFinish, |
|
||||
}} |
|
||||
> |
|
||||
{tabsFormDom} |
|
||||
</CrazyFormProvide.Provider> |
|
||||
</Form.Provider> |
|
||||
</div> |
|
||||
) |
|
||||
|
|
||||
} |
|
||||
|
|
||||
function TabsFormWarp<T = Record<string, any>>( |
|
||||
props: CrazyFormProps<T> & { |
|
||||
children: any; |
|
||||
}, |
|
||||
) { |
|
||||
return ( |
|
||||
<ProConfigProvider needDeps> |
|
||||
<TabsForm<T> {...props} /> |
|
||||
</ProConfigProvider> |
|
||||
) |
|
||||
} |
|
||||
|
|
||||
TabsFormWarp.TabForm = TabForm |
|
||||
TabsFormWarp.useForm = Form.useForm |
|
||||
|
|
||||
export { TabsFormWarp as TabsForm } |
|
||||
export type { TabFormProps, CrazyFormProps as TabsFormProps } |
|
@ -1,74 +0,0 @@ |
|||||
import { FormInstance } from 'antd/lib' |
|
||||
import { FormProps, ProFormInstance, ProFormProps, SubmitterProps } from '@ant-design/pro-components' |
|
||||
import React from 'react' |
|
||||
import { FormProviderProps } from 'antd/es/form/context' |
|
||||
import type { CommonFormProps } from '@ant-design/pro-form/es/BaseForm/BaseForm' |
|
||||
|
|
||||
export type CrazyFormProps<T = Record<string, any>> = { |
|
||||
|
|
||||
onFinish?: (values: T) => Promise<boolean | void>; |
|
||||
current?: string; |
|
||||
/**、 |
|
||||
* 切换区域传透的Props |
|
||||
*/ |
|
||||
toggleProps?: Record<any, any>; |
|
||||
formProps?: ProFormProps<T>; |
|
||||
onCurrentChange?: (current: string) => void; |
|
||||
/** 自定义步骤器 */ |
|
||||
toggleRender?: ( |
|
||||
items: { |
|
||||
key: string; |
|
||||
title?: React.ReactNode; |
|
||||
[key: string]: any |
|
||||
}[], |
|
||||
defaultDom: React.ReactNode, |
|
||||
) => React.ReactNode; |
|
||||
/** @name 当前展示表单的 formRef */ |
|
||||
formRef?: React.MutableRefObject<ProFormInstance<any> | undefined | null>; |
|
||||
/** @name 所有表单的 formMapRef */ |
|
||||
formMapRef?: React.MutableRefObject< |
|
||||
React.MutableRefObject<FormInstance<any> | undefined>[] |
|
||||
>; |
|
||||
/** |
|
||||
* 自定义单个表单 |
|
||||
* |
|
||||
* @param form From 的 dom,可以放置到别的位置 |
|
||||
*/ |
|
||||
toggleFormRender?: (from: React.ReactNode) => React.ReactNode; |
|
||||
|
|
||||
/** |
|
||||
* 自定义整个表单区域 |
|
||||
* |
|
||||
* @param form From 的 dom,可以放置到别的位置 |
|
||||
* @param submitter 操作按钮 |
|
||||
*/ |
|
||||
formRender?: ( |
|
||||
from: React.ReactNode, |
|
||||
submitter: React.ReactNode, |
|
||||
) => React.ReactNode; |
|
||||
/** 按钮的统一配置,优先级低于分步表单的配置 */ |
|
||||
submitter?: |
|
||||
| SubmitterProps<{ |
|
||||
current: string; //当前激活的toggle
|
|
||||
form?: FormInstance<any>; |
|
||||
}> |
|
||||
| false; |
|
||||
|
|
||||
containerStyle?: React.CSSProperties; |
|
||||
/** |
|
||||
* 自定義整個佈局。 |
|
||||
* |
|
||||
* @param layoutDom toggleDom 和 formDom 元素可以放置在任何地方。 |
|
||||
*/ |
|
||||
layoutRender?: (layoutDom: { |
|
||||
toggleDom: React.ReactElement; |
|
||||
formDom: React.ReactElement; |
|
||||
}) => React.ReactNode; |
|
||||
} & Omit<FormProviderProps, 'children'>; |
|
||||
|
|
||||
|
|
||||
export type CrazyChildFormProps<T = Record<string, any>, U = Record<string, any>> = { |
|
||||
|
|
||||
index?: number; |
|
||||
} & Omit<FormProps<T>, 'onFinish' | 'form'> & |
|
||||
Omit<CommonFormProps<T, U>, 'submitter' | 'form'>; |
|
@ -0,0 +1,319 @@ |
|||||
|
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/x-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 { XFormTypes } from '@/types/x-form/model' |
||||
|
|
||||
|
|
||||
|
export interface XFormProps { |
||||
|
title?: ReactNode |
||||
|
namespace?: string |
||||
|
columns?: ProColumns[] //重写columns
|
||||
|
} |
||||
|
|
||||
|
const XForm = ({ namespace, columns: propColumns = [], title }: XFormProps) => { |
||||
|
|
||||
|
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<XFormTypes.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?.column || [], 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?.column, 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?.dialogType === 'drawer' ? { |
||||
|
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 |
||||
|
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 |
||||
|
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 |
||||
|
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 XForm |
@ -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,155 @@ |
|||||
|
import { XFormTypes } 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' |
||||
|
|
||||
|
|
||||
|
const getValueType = (column: XFormTypes.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: XFormTypes.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: XFormTypes.IColumn[], overwriteColumns?: ProColumns[]) => { |
||||
|
|
||||
|
const overwriteKeys = [] as string[] |
||||
|
|
||||
|
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, |
||||
|
} |
||||
|
|
||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
|
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) |
||||
|
|
||||
|
const overwrite = overwriteColumns?.find(i => i.dataIndex === item.prop) |
||||
|
if (overwrite) { |
||||
|
overwriteKeys.push(item.prop) |
||||
|
} |
||||
|
|
||||
|
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 <Component {...props} treeData={options}/> |
||||
|
} |
||||
|
if (_scheam.valueType as string === 'select') { |
||||
|
return <Select {...props} options={options}/> |
||||
|
} |
||||
|
|
||||
|
return <Component {...config} /> |
||||
|
}, |
||||
|
render: (text: any, record: any) => { |
||||
|
if (type === 'switch' || type === 'checkbox' || type === 'radio') { |
||||
|
return <Switch size={'small'} value={record[item.prop]}/> |
||||
|
} |
||||
|
if (item.colorFormat) { |
||||
|
return <span style={{ color: item.colorFormat }}>{text}</span> |
||||
|
} |
||||
|
return text |
||||
|
}, |
||||
|
...overwrite |
||||
|
} as ProColumns |
||||
|
}).concat(overwriteColumns?.filter(i => !overwriteKeys.includes(i.dataIndex)) || []) |
||||
|
|
||||
|
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue