diff --git a/packages/app/src/pages/index.tsx b/packages/app/src/pages/index.tsx index c997e283f..405a63937 100644 --- a/packages/app/src/pages/index.tsx +++ b/packages/app/src/pages/index.tsx @@ -5,6 +5,7 @@ import { AntdSchemaComponentProvider, APIClientProvider, AuthLayout, + ChinaRegionProvider, CollectionManagerShortcut, compose, DesignableSwitch, @@ -53,6 +54,7 @@ const providers = [ RemoteCollectionManagerProvider, [SchemaInitializerProvider, { initializers: { MenuItemInitializers } }], AntdSchemaComponentProvider, + ChinaRegionProvider, [DocumentTitleProvider, { addonAfter: 'NocoBase' }], [ RouteSwitchProvider, diff --git a/packages/client/src/china-region/index.tsx b/packages/client/src/china-region/index.tsx new file mode 100644 index 000000000..364c2e8e3 --- /dev/null +++ b/packages/client/src/china-region/index.tsx @@ -0,0 +1,76 @@ +import { ArrayField } from '@formily/core'; +import { useField } from '@formily/react'; +import React from 'react'; +import { SchemaComponentOptions } from '..'; +import { useAPIClient, useRequest } from '../api-client'; + +const useChinaRegionDataSource = (options) => { + const field = useField(); + const maxLevel = field.componentProps.maxLevel; + return useRequest( + { + resource: 'chinaRegions', + action: 'list', + params: { + sort: 'code', + filter: { + level: 1, + }, + }, + }, + { + ...options, + onSuccess(data) { + options?.onSuccess({ + data: + data?.data?.map((item) => { + if (maxLevel !== 1) { + item.isLeaf = false; + } + return item; + }) || [], + }); + }, + }, + ); +}; + +const useChinaRegionLoadData = () => { + const api = useAPIClient(); + const field = useField(); + const maxLevel = field.componentProps.maxLevel; + return (selectedOptions) => { + const targetOption = selectedOptions[selectedOptions.length - 1]; + if (targetOption?.children?.length > 0) { + return; + } + targetOption.loading = true; + api + .resource('chinaRegions') + .list({ + sort: 'code', + filter: { + parentCode: targetOption.code, + }, + }) + .then(({ data }) => { + targetOption.loading = false; + targetOption.children = + data?.data?.map((item) => { + if (maxLevel > item.level) { + item.isLeaf = false; + } + return item; + }) || []; + field.dataSource = [...field.dataSource]; + }); + }; +}; + +export const ChinaRegionProvider = (props) => { + return ( + + {props.children} + + ); +}; diff --git a/packages/client/src/collection-manager/CollectionField.tsx b/packages/client/src/collection-manager/CollectionField.tsx index d4014f4fb..dd65948ee 100644 --- a/packages/client/src/collection-manager/CollectionField.tsx +++ b/packages/client/src/collection-manager/CollectionField.tsx @@ -1,7 +1,7 @@ import { Field } from '@formily/core'; import { connect, useField, useFieldSchema } from '@formily/react'; import React, { useEffect } from 'react'; -import { useComponent } from '..'; +import { useCompile, useComponent } from '..'; import { CollectionFieldProvider } from './CollectionFieldProvider'; import { useCollectionField } from './hooks'; @@ -11,6 +11,7 @@ const InternalField: React.FC = (props) => { const fieldSchema = useFieldSchema(); const { uiSchema } = useCollectionField(); const component = useComponent(uiSchema?.['x-component']); + const compile = useCompile(); const setFieldProps = (key, value) => { field[key] = typeof field[key] === 'undefined' ? value : field[key]; }; @@ -24,6 +25,7 @@ const InternalField: React.FC = (props) => { if (!uiSchema) { return; } + console.log('uiSchema', uiSchema); setFieldProps('title', uiSchema.title); setFieldProps('description', uiSchema.description); setFieldProps('initialValue', uiSchema.default); diff --git a/packages/client/src/collection-manager/CollectionManagerProvider.tsx b/packages/client/src/collection-manager/CollectionManagerProvider.tsx index bf23a7b11..5e0d32174 100644 --- a/packages/client/src/collection-manager/CollectionManagerProvider.tsx +++ b/packages/client/src/collection-manager/CollectionManagerProvider.tsx @@ -32,7 +32,7 @@ export const RemoteCollectionManagerProvider = (props: any) => { resource: 'collections', action: 'list', params: { - pageSize: 999, + paginate: false, appends: ['fields', 'fields.uiSchema'], }, }; diff --git a/packages/client/src/collection-manager/Configuration/AddFieldAction.tsx b/packages/client/src/collection-manager/Configuration/AddFieldAction.tsx index 369fad7a4..1fce39b09 100644 --- a/packages/client/src/collection-manager/Configuration/AddFieldAction.tsx +++ b/packages/client/src/collection-manager/Configuration/AddFieldAction.tsx @@ -1,18 +1,29 @@ import { PlusOutlined } from '@ant-design/icons'; -import { ArrayTable } from '@formily/antd'; -import { ISchema } from '@formily/react'; +import { ISchema, useForm } from '@formily/react'; import { uid } from '@formily/shared'; import { Button, Dropdown, Menu } from 'antd'; +import { cloneDeep } from 'lodash'; import React, { useState } from 'react'; +import { useRequest } from '../../api-client'; import { ActionContext, SchemaComponent, useCompile } from '../../schema-component'; +import { useCreateAction } from '../action-hooks'; import { useCollectionManager } from '../hooks'; import { IField } from '../interfaces/types'; +import { ArrayTable } from './ArrayTable'; import { options } from './interfaces'; const getSchema = (schema: IField): ISchema => { if (!schema) { return; } + const properties = cloneDeep(schema.properties) as any; + const initialValue = { + ...cloneDeep(schema.default), + interface: schema.name, + name: `f_${uid()}`, + }; + initialValue.uiSchema.title = schema.title; + console.log('initialValue', initialValue); return { type: 'object', properties: { @@ -21,16 +32,20 @@ const getSchema = (schema: IField): ISchema => { 'x-component': 'Action.Drawer', 'x-decorator': 'Form', 'x-decorator-props': { - initialValue: { - interface: schema.name, - ...schema.default, - name: `f_${uid()}`, + useValues(options) { + return useRequest( + () => + Promise.resolve({ + data: initialValue, + }), + options, + ); }, }, title: '{{ t("Add field") }}', properties: { // @ts-ignore - ...schema.properties, + ...properties, footer: { type: 'void', 'x-component': 'Action.Drawer.Footer', @@ -47,7 +62,7 @@ const getSchema = (schema: IField): ISchema => { 'x-component': 'Action', 'x-component-props': { type: 'primary', - useAction: '{{ cm.useCreateActionAndRefreshCM }}', + useAction: '{{ useCreateCollectionField }}', }, }, }, @@ -58,6 +73,30 @@ const getSchema = (schema: IField): ISchema => { }; }; +const useCreateCollectionField = () => { + const form = useForm(); + const { run } = useCreateAction(); + const { refreshCM } = useCollectionManager(); + return { + async run() { + await form.submit(); + const options = form?.values?.uiSchema?.enum?.slice() || []; + form.setValuesIn( + 'uiSchema.enum', + options.map((option) => { + return { + value: uid(), + ...option, + }; + }), + ); + console.log('form.values', form.values); + await run(); + await refreshCM(); + }, + }; +}; + export const AddFieldAction = () => { const { getInterface } = useCollectionManager(); const [visible, setVisible] = useState(false); @@ -78,7 +117,6 @@ export const AddFieldAction = () => { return ( {option.children.map((child) => { - console.log(child); return {compile(child.title)}; })} @@ -91,7 +129,7 @@ export const AddFieldAction = () => { 添加字段 - + ); }; diff --git a/packages/client/src/collection-manager/Configuration/ArrayTable.tsx b/packages/client/src/collection-manager/Configuration/ArrayTable.tsx new file mode 100644 index 000000000..ae32a36c5 --- /dev/null +++ b/packages/client/src/collection-manager/Configuration/ArrayTable.tsx @@ -0,0 +1,25 @@ +import { ArrayBase, ArrayTable as FormilyArrayTable } from '@formily/antd'; +import { connect } from '@formily/react'; +import React, { Fragment } from 'react'; + +export const ArrayTable: any = connect((props) => { + const { onChange } = props; + return ( + { + console.log('onChange', value); + onChange(value); + }} + /> + ); +}); + +ArrayTable.displayName = 'ArrayTable' + +ArrayTable.Column = () => { + return +} + +ArrayBase.mixin(ArrayTable) + diff --git a/packages/client/src/collection-manager/Configuration/ConfigurationTable.tsx b/packages/client/src/collection-manager/Configuration/ConfigurationTable.tsx index 2ce6e68e9..5689953d8 100644 --- a/packages/client/src/collection-manager/Configuration/ConfigurationTable.tsx +++ b/packages/client/src/collection-manager/Configuration/ConfigurationTable.tsx @@ -23,6 +23,8 @@ const useCollectionValues = (options) => { Promise.resolve({ data: { name: `t_${uid()}`, + createdBy: true, + updatedBy: true, }, }), { @@ -38,7 +40,7 @@ const useCollectionValues = (options) => { }, [visible]); return result; -} +}; export const ConfigurationTable = () => { const { collections = [] } = useCollectionManager(); @@ -50,7 +52,7 @@ export const ConfigurationTable = () => { }; return (
- +
); }; diff --git a/packages/client/src/collection-manager/Configuration/EditFieldAction.tsx b/packages/client/src/collection-manager/Configuration/EditFieldAction.tsx index 62d645d97..07b9021d6 100644 --- a/packages/client/src/collection-manager/Configuration/EditFieldAction.tsx +++ b/packages/client/src/collection-manager/Configuration/EditFieldAction.tsx @@ -1,18 +1,22 @@ -import { ISchema } from '@formily/react'; +import { ISchema, useForm } from '@formily/react'; import { uid } from '@formily/shared'; import cloneDeep from 'lodash/cloneDeep'; import React, { useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { useAPIClient } from '../../api-client'; +import { useAPIClient, useRequest } from '../../api-client'; import { useRecord } from '../../record-provider'; import { ActionContext, SchemaComponent } from '../../schema-component'; +import { useUpdateAction } from '../action-hooks'; import { useCollectionManager } from '../hooks'; import { IField } from '../interfaces/types'; +import { ArrayTable } from './ArrayTable'; const getSchema = (schema: IField): ISchema => { if (!schema) { return; } + const properties = cloneDeep(schema.properties) as any; + properties.name['x-disabled'] = true; return { type: 'object', properties: { @@ -21,12 +25,20 @@ const getSchema = (schema: IField): ISchema => { 'x-component': 'Action.Drawer', 'x-decorator': 'Form', 'x-decorator-props': { - initialValue: cloneDeep(schema.default), + useValues(options) { + return useRequest( + () => + Promise.resolve({ + data: cloneDeep(schema.default), + }), + options, + ); + }, }, title: '{{ t("Edit field") }}', properties: { // @ts-ignore - ...schema.properties, + ...properties, footer: { type: 'void', 'x-component': 'Action.Drawer.Footer', @@ -43,7 +55,7 @@ const getSchema = (schema: IField): ISchema => { 'x-component': 'Action', 'x-component-props': { type: 'primary', - useAction: '{{ cm.useUpdateActionAndRefreshCM }}', + useAction: '{{ useUpdateCollectionField }}', }, }, }, @@ -54,6 +66,29 @@ const getSchema = (schema: IField): ISchema => { }; }; +const useUpdateCollectionField = () => { + const form = useForm(); + const { run } = useUpdateAction(); + const { refreshCM } = useCollectionManager(); + return { + async run() { + await form.submit(); + const options = form?.values?.uiSchema?.enum?.slice() || []; + form.setValuesIn( + 'uiSchema.enum', + options.map((option) => { + return { + value: uid(), + ...option, + }; + }), + ); + await run(); + await refreshCM(); + }, + }; +}; + export const EditFieldAction = (props) => { const record = useRecord(); const { getInterface } = useCollectionManager(); @@ -79,7 +114,7 @@ export const EditFieldAction = (props) => { > {t('Edit')} - + ); }; diff --git a/packages/client/src/collection-manager/hooks/useCollectionField.ts b/packages/client/src/collection-manager/hooks/useCollectionField.ts index cdb743818..10771ee27 100644 --- a/packages/client/src/collection-manager/hooks/useCollectionField.ts +++ b/packages/client/src/collection-manager/hooks/useCollectionField.ts @@ -1,6 +1,7 @@ import { useContext } from 'react'; import { useAPIClient } from '../../api-client'; import { useRecord } from '../../record-provider'; +import { useCompile } from '../../schema-component'; import { CollectionFieldContext } from '../context'; import { useCollection } from './useCollection'; @@ -8,6 +9,7 @@ export const useCollectionField = () => { const collection = useCollection(); const record = useRecord(); const api = useAPIClient(); + const compile = useCompile(); const ctx = useContext(CollectionFieldContext); if (!ctx) { return {} as any; @@ -16,6 +18,7 @@ export const useCollectionField = () => { const resource = api?.resource(resourceName, record[ctx.sourceKey]); return { ...ctx, + uiSchema: compile(ctx.uiSchema), resource, }; }; diff --git a/packages/client/src/collection-manager/interfaces/attachment.ts b/packages/client/src/collection-manager/interfaces/attachment.ts index 9691b9c14..7b76dfc7d 100644 --- a/packages/client/src/collection-manager/interfaces/attachment.ts +++ b/packages/client/src/collection-manager/interfaces/attachment.ts @@ -16,8 +16,9 @@ export const attachment: IField = { type: 'array', // title, 'x-component': 'Upload.Attachment', - 'x-decorator': 'FormItem', - 'x-designable-bar': 'Upload.DesignableBar', + 'x-component-props': { + action: 'attachments:upload', + }, }, }, initialize: (values: any) => { diff --git a/packages/client/src/collection-manager/interfaces/chinaRegion.ts b/packages/client/src/collection-manager/interfaces/chinaRegion.ts index 6c95cdb49..ee03f8d1a 100644 --- a/packages/client/src/collection-manager/interfaces/chinaRegion.ts +++ b/packages/client/src/collection-manager/interfaces/chinaRegion.ts @@ -20,6 +20,8 @@ export const chinaRegion: IField = { // title, 'x-component': 'Cascader', 'x-component-props': { + useDataSource: '{{ useChinaRegionDataSource }}', + useLoadData: '{{ useChinaRegionLoadData }}', changeOnSelectLast: false, labelInValue: true, maxLevel: 3, diff --git a/packages/client/src/collection-manager/interfaces/linkTo.ts b/packages/client/src/collection-manager/interfaces/linkTo.ts index a99715326..88b1c48e2 100644 --- a/packages/client/src/collection-manager/interfaces/linkTo.ts +++ b/packages/client/src/collection-manager/interfaces/linkTo.ts @@ -15,12 +15,12 @@ export const linkTo: IField = { uiSchema: { type: 'array', // title, - 'x-component': 'Select.Drawer', + 'x-component': 'RecordPicker', 'x-component-props': {}, }, }, initialize: (values: any) => { - if (values.dataType === 'belongsToMany') { + if (values.type === 'belongsToMany') { if (!values.through) { values.through = `t_${uid()}`; } @@ -34,11 +34,7 @@ export const linkTo: IField = { values.sourceKey = 'id'; } if (!values.targetKey) { - if (values.target === 'roles') { - values.targetKey = 'name'; - } else { - values.targetKey = 'id'; - } + values.targetKey = 'id'; } } }, diff --git a/packages/client/src/collection-manager/interfaces/updatedBy.ts b/packages/client/src/collection-manager/interfaces/updatedBy.ts index fd7474e72..11b587dd6 100644 --- a/packages/client/src/collection-manager/interfaces/updatedBy.ts +++ b/packages/client/src/collection-manager/interfaces/updatedBy.ts @@ -15,7 +15,7 @@ export const updatedBy: IField = { uiSchema: { type: 'object', title: '{{t("Last updated by")}}', - 'x-component': 'Select.Drawer', + 'x-component': 'RecordPicker', 'x-component-props': { fieldNames: { value: 'id', diff --git a/packages/client/src/index.tsx b/packages/client/src/index.tsx index d990f992f..6b7700106 100644 --- a/packages/client/src/index.tsx +++ b/packages/client/src/index.tsx @@ -7,6 +7,7 @@ export * from './api-client'; export * from './application'; export * from './async-data-provider'; export * from './board'; +export * from './china-region'; export * from './collection-manager'; export * from './document-title'; export * from './i18n'; diff --git a/packages/client/src/locale/zh_CN.json b/packages/client/src/locale/zh_CN.json index 4895c9597..bbf98147f 100644 --- a/packages/client/src/locale/zh_CN.json +++ b/packages/client/src/locale/zh_CN.json @@ -131,7 +131,7 @@ "Created at": "创建日期", "Last updated at": "最后修改日期", "Created by": "创建人", - "Last updated by": "最后更新日期", + "Last updated by": "最后修改人", "Add field": "添加字段", "Field display name": "字段名称", "Field type": "字段类型", diff --git a/packages/client/src/schema-component/antd/cascader/Cascader.tsx b/packages/client/src/schema-component/antd/cascader/Cascader.tsx index 786c236c6..dcdfc971d 100644 --- a/packages/client/src/schema-component/antd/cascader/Cascader.tsx +++ b/packages/client/src/schema-component/antd/cascader/Cascader.tsx @@ -5,10 +5,18 @@ import { toArr } from '@formily/shared'; import { Cascader as AntdCascader } from 'antd'; import { isBoolean, omit } from 'lodash'; import React from 'react'; +import { useRequest } from '../../../api-client'; import { defaultFieldNames } from './defaultFieldNames'; import { ReadPretty } from './ReadPretty'; -const useDefLoadData = (props: any) => props.loadData; +const useDefDataSource = (options) => { + const field = useField(); + return useRequest(() => Promise.resolve({ data: field.dataSource || [] }), options); +}; + +const useDefLoadData = (props: any) => { + return props?.loadData; +}; export const Cascader = connect( (props: any) => { @@ -18,6 +26,7 @@ export const Cascader = connect( onChange, labelInValue, fieldNames = defaultFieldNames, + useDataSource = useDefDataSource, useLoadData = useDefLoadData, changeOnSelectLast, changeOnSelect, @@ -25,6 +34,11 @@ export const Cascader = connect( ...others } = props; const loadData = useLoadData(props); + const { loading } = useDataSource({ + onSuccess(data) { + field.dataSource = data?.data || []; + }, + }); // 兼容值为 object[] 的情况 const toValue = () => { return toArr(value).map((item) => { @@ -54,17 +68,11 @@ export const Cascader = connect( return {option[fieldNames.label]} / ; }); }; - // if (loadData) { - // Object.assign(others, { - // loadData: (selectedOptions: any[]) => { - // // 将 field 传给 loadData - // loadData(selectedOptions, field); - // }, - // }); - // } return (