mirror of
https://gitee.com/nocobase/nocobase.git
synced 2024-12-05 05:38:23 +08:00
feat: field assignment for custom actions supports string variables (#597)
* fix: temporary solution to APP crash * feat: support dynamic assigned field value * feat: support dynamic assigned field value * fix: useFields filter * fix: dynamic assigned value * fix: dynamic assigned value * fix: fix china region export * fix: fix china region export * fix: change assign value data * fix: custom request use parse instead of SchemaCompile * fix: allow user attribute to be selected * fix: allow DATE field to be select currentUser or CurrentRecord * fix: allow DATE field to be select currentUser or CurrentRecord * fix: change style * feat: package dependencies Co-authored-by: chenos <chenlinxh@gmail.com>
This commit is contained in:
parent
20ab8c1501
commit
c8bd2c7317
@ -27,6 +27,7 @@
|
|||||||
"classnames": "^2.3.1",
|
"classnames": "^2.3.1",
|
||||||
"file-saver": "^2.0.5",
|
"file-saver": "^2.0.5",
|
||||||
"i18next": "^21.6.0",
|
"i18next": "^21.6.0",
|
||||||
|
"json-templates": "^4.2.0",
|
||||||
"marked": "^4.0.12",
|
"marked": "^4.0.12",
|
||||||
"mathjs": "^10.6.0",
|
"mathjs": "^10.6.0",
|
||||||
"react-beautiful-dnd": "^13.1.0",
|
"react-beautiful-dnd": "^13.1.0",
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { Schema as SchemaCompiler } from '@formily/json-schema';
|
|
||||||
import { useField, useFieldSchema, useForm } from '@formily/react';
|
import { useField, useFieldSchema, useForm } from '@formily/react';
|
||||||
import { message, Modal } from 'antd';
|
import { message, Modal } from 'antd';
|
||||||
|
import parse from 'json-templates';
|
||||||
import get from 'lodash/get';
|
import get from 'lodash/get';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useHistory } from 'react-router-dom';
|
import { useHistory } from 'react-router-dom';
|
||||||
@ -120,11 +120,19 @@ export const useCreateActionProps = () => {
|
|||||||
const { fields, getField } = useCollection();
|
const { fields, getField } = useCollection();
|
||||||
const compile = useCompile();
|
const compile = useCompile();
|
||||||
const filterByTk = useFilterByTk();
|
const filterByTk = useFilterByTk();
|
||||||
|
const currentRecord = useRecord();
|
||||||
|
const currentUserContext = useCurrentUserContext();
|
||||||
|
const currentUser = currentUserContext?.data?.data;
|
||||||
return {
|
return {
|
||||||
async onClick() {
|
async onClick() {
|
||||||
const fieldNames = fields.map((field) => field.name);
|
const fieldNames = fields.map((field) => field.name);
|
||||||
const { assignedValues, onSuccess, overwriteValues, skipValidator } = actionSchema?.['x-action-settings'] ?? {};
|
const {
|
||||||
|
assignedValues: originalAssignedValues = {},
|
||||||
|
onSuccess,
|
||||||
|
overwriteValues,
|
||||||
|
skipValidator,
|
||||||
|
} = actionSchema?.['x-action-settings'] ?? {};
|
||||||
|
const assignedValues = parse(originalAssignedValues)({ currentTime: new Date(), currentRecord, currentUser });
|
||||||
if (!skipValidator) {
|
if (!skipValidator) {
|
||||||
await form.submit();
|
await form.submit();
|
||||||
}
|
}
|
||||||
@ -174,14 +182,20 @@ export const useCustomizeUpdateActionProps = () => {
|
|||||||
const filterByTk = useFilterByTk();
|
const filterByTk = useFilterByTk();
|
||||||
const actionSchema = useFieldSchema();
|
const actionSchema = useFieldSchema();
|
||||||
const currentRecord = useRecord();
|
const currentRecord = useRecord();
|
||||||
const ctx = useCurrentUserContext();
|
const currentUserContext = useCurrentUserContext();
|
||||||
|
const currentUser = currentUserContext?.data?.data;
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const compile = useCompile();
|
const compile = useCompile();
|
||||||
const form = useForm();
|
const form = useForm();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
async onClick() {
|
async onClick() {
|
||||||
const { assignedValues, onSuccess, skipValidator } = actionSchema?.['x-action-settings'] ?? {};
|
const {
|
||||||
|
assignedValues: originalAssignedValues = {},
|
||||||
|
onSuccess,
|
||||||
|
skipValidator,
|
||||||
|
} = actionSchema?.['x-action-settings'] ?? {};
|
||||||
|
const assignedValues = parse(originalAssignedValues)({ currentTime: new Date(), currentRecord, currentUser });
|
||||||
if (skipValidator === false) {
|
if (skipValidator === false) {
|
||||||
await form.submit();
|
await form.submit();
|
||||||
}
|
}
|
||||||
@ -254,9 +268,9 @@ export const useCustomizeRequestActionProps = () => {
|
|||||||
const requestBody = {
|
const requestBody = {
|
||||||
url: renderTemplate(requestSettings['url'], { currentRecord, currentUser }),
|
url: renderTemplate(requestSettings['url'], { currentRecord, currentUser }),
|
||||||
method: requestSettings['method'],
|
method: requestSettings['method'],
|
||||||
headers: SchemaCompiler.compile(headers, { currentRecord, currentUser }),
|
headers: parse(headers)({ currentRecord, currentUser }),
|
||||||
params: SchemaCompiler.compile(params, { currentRecord, currentUser }),
|
params: parse(params)({ currentRecord, currentUser }),
|
||||||
data: SchemaCompiler.compile(data, { currentRecord, currentUser }),
|
data: parse(data)({ currentRecord, currentUser }),
|
||||||
};
|
};
|
||||||
actionField.data = field.data || {};
|
actionField.data = field.data || {};
|
||||||
actionField.data.loading = true;
|
actionField.data.loading = true;
|
||||||
@ -305,15 +319,22 @@ export const useUpdateActionProps = () => {
|
|||||||
const { setVisible } = useActionContext();
|
const { setVisible } = useActionContext();
|
||||||
const actionSchema = useFieldSchema();
|
const actionSchema = useFieldSchema();
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const record = useRecord();
|
|
||||||
const { fields, getField } = useCollection();
|
const { fields, getField } = useCollection();
|
||||||
const compile = useCompile();
|
const compile = useCompile();
|
||||||
const actionField = useField();
|
const actionField = useField();
|
||||||
const { updateAssociationValues } = useFormBlockContext();
|
const { updateAssociationValues } = useFormBlockContext();
|
||||||
|
const currentRecord = useRecord();
|
||||||
|
const currentUserContext = useCurrentUserContext();
|
||||||
|
const currentUser = currentUserContext?.data?.data;
|
||||||
return {
|
return {
|
||||||
async onClick() {
|
async onClick() {
|
||||||
const { assignedValues, onSuccess, overwriteValues, skipValidator } = actionSchema?.['x-action-settings'] ?? {};
|
const {
|
||||||
|
assignedValues: originalAssignedValues = {},
|
||||||
|
onSuccess,
|
||||||
|
overwriteValues,
|
||||||
|
skipValidator,
|
||||||
|
} = actionSchema?.['x-action-settings'] ?? {};
|
||||||
|
const assignedValues = parse(originalAssignedValues)({ currentTime: new Date(), currentRecord, currentUser });
|
||||||
if (!skipValidator) {
|
if (!skipValidator) {
|
||||||
await form.submit();
|
await form.submit();
|
||||||
}
|
}
|
||||||
@ -329,7 +350,7 @@ export const useUpdateActionProps = () => {
|
|||||||
...overwriteValues,
|
...overwriteValues,
|
||||||
...assignedValues,
|
...assignedValues,
|
||||||
},
|
},
|
||||||
updateAssociationValues
|
updateAssociationValues,
|
||||||
});
|
});
|
||||||
actionField.data.loading = false;
|
actionField.data.loading = false;
|
||||||
if (!(resource instanceof TableFieldResource)) {
|
if (!(resource instanceof TableFieldResource)) {
|
||||||
|
@ -610,6 +610,7 @@ export default {
|
|||||||
'Dynamic value': '动态值',
|
'Dynamic value': '动态值',
|
||||||
'Current user': '当前用户',
|
'Current user': '当前用户',
|
||||||
'Current record': '当前记录',
|
'Current record': '当前记录',
|
||||||
|
'Current time': '当前时间',
|
||||||
'Popup close method': '弹窗关闭方式',
|
'Popup close method': '弹窗关闭方式',
|
||||||
'Automatic close': '自动关闭',
|
'Automatic close': '自动关闭',
|
||||||
'Manually close': '手动关闭',
|
'Manually close': '手动关闭',
|
||||||
|
@ -1,74 +1,226 @@
|
|||||||
import { Field } from '@formily/core';
|
import { Field } from '@formily/core';
|
||||||
import { useField, useFieldSchema } from '@formily/react';
|
import { connect, useField, useFieldSchema } from '@formily/react';
|
||||||
// import { Select, Space } from 'antd';
|
import { Cascader, Select, Space } from 'antd';
|
||||||
import React, { useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { CollectionField } from '../../../collection-manager';
|
import { useFormBlockContext } from '../../../block-provider';
|
||||||
import { useCompile } from '../../../schema-component';
|
import {
|
||||||
|
CollectionFieldProvider,
|
||||||
|
useCollection,
|
||||||
|
useCollectionField,
|
||||||
|
useCollectionFilterOptions,
|
||||||
|
} from '../../../collection-manager';
|
||||||
|
import { useCompile, useComponent } from '../../../schema-component';
|
||||||
|
|
||||||
|
const DYNAMIC_RECORD_REG = /\{\{\s*currentRecord\.(.*)\s*\}\}/;
|
||||||
|
const DYNAMIC_USER_REG = /\{\{\s*currentUser\.(.*)\s*\}\}/;
|
||||||
|
const DYNAMIC_TIME_REG = /\{\{\s*currentTime\s*\}\}/;
|
||||||
|
|
||||||
|
const InternalField: React.FC = (props) => {
|
||||||
|
const field = useField<Field>();
|
||||||
|
|
||||||
|
const fieldSchema = useFieldSchema();
|
||||||
|
const { name, interface: interfaceType, uiSchema } = useCollectionField();
|
||||||
|
const component = useComponent(uiSchema?.['x-component']);
|
||||||
|
const compile = useCompile();
|
||||||
|
const setFieldProps = (key, value) => {
|
||||||
|
field[key] = typeof field[key] === 'undefined' ? value : field[key];
|
||||||
|
};
|
||||||
|
const setRequired = () => {
|
||||||
|
if (typeof fieldSchema['required'] === 'undefined') {
|
||||||
|
field.required = !!uiSchema['required'];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const ctx = useFormBlockContext();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (ctx?.field) {
|
||||||
|
ctx.field.added = ctx.field.added || new Set();
|
||||||
|
ctx.field.added.add(fieldSchema.name);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!uiSchema) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setFieldProps('content', uiSchema['x-content']);
|
||||||
|
setFieldProps('title', uiSchema.title);
|
||||||
|
setFieldProps('description', uiSchema.description);
|
||||||
|
setFieldProps('initialValue', uiSchema.default);
|
||||||
|
if (!field.validator && uiSchema['x-validator']) {
|
||||||
|
field.validator = uiSchema['x-validator'];
|
||||||
|
}
|
||||||
|
if (fieldSchema['x-disabled'] === true) {
|
||||||
|
field.disabled = true;
|
||||||
|
}
|
||||||
|
if (fieldSchema['x-read-pretty'] === true) {
|
||||||
|
field.readPretty = true;
|
||||||
|
}
|
||||||
|
setRequired();
|
||||||
|
// @ts-ignore
|
||||||
|
// field.dataSource = uiSchema.enum;
|
||||||
|
// const originalProps = compile(uiSchema['x-component-props']) || {};
|
||||||
|
// const componentProps = merge(originalProps, field.componentProps || {});
|
||||||
|
// field.component = [component, componentProps];
|
||||||
|
}, [JSON.stringify(uiSchema)]);
|
||||||
|
if (!uiSchema) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return React.createElement(component, props, props.children);
|
||||||
|
};
|
||||||
|
|
||||||
|
const CollectionField = connect((props) => {
|
||||||
|
const fieldSchema = useFieldSchema();
|
||||||
|
return (
|
||||||
|
<CollectionFieldProvider name={fieldSchema.name}>
|
||||||
|
<InternalField {...props} />
|
||||||
|
</CollectionFieldProvider>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
export enum AssignedFieldValueType {
|
||||||
|
ConstantValue = 'constantValue',
|
||||||
|
DynamicValue = 'dynamicValue',
|
||||||
|
}
|
||||||
|
|
||||||
export const AssignedField = (props: any) => {
|
export const AssignedField = (props: any) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const compile = useCompile();
|
const compile = useCompile();
|
||||||
const field = useField<Field>();
|
const field = useField<Field>();
|
||||||
const fieldSchema = useFieldSchema();
|
const fieldSchema = useFieldSchema();
|
||||||
// const [type, setType] = useState<string>('constantValue');
|
const isDynamicValue =
|
||||||
const [value, setValue] = useState(field?.value?.value ?? '');
|
DYNAMIC_RECORD_REG.test(field.value) || DYNAMIC_USER_REG.test(field.value) || DYNAMIC_TIME_REG.test(field.value);
|
||||||
// const [options, setOptions] = useState<any[]>([]);
|
const initType = isDynamicValue ? AssignedFieldValueType.DynamicValue : AssignedFieldValueType.ConstantValue;
|
||||||
// const { getField } = useCollection();
|
const [type, setType] = useState<string>(initType);
|
||||||
// const collectionField = getField(fieldSchema.name);
|
const initFieldType = {
|
||||||
// const { uiSchema } = collectionField;
|
[`${DYNAMIC_TIME_REG.test(field.value)}`]: 'currentTime',
|
||||||
// const currentUser = useFilterOptions('users');
|
[`${DYNAMIC_USER_REG.test(field.value)}`]: 'currentUser',
|
||||||
// const currentRecord = useFilterOptions(collectionField.collectionName);
|
[`${DYNAMIC_RECORD_REG.test(field.value)}`]: 'currentRecord',
|
||||||
// useEffect(() => {
|
};
|
||||||
// const opt = [
|
const [fieldType, setFieldType] = useState<string>(initFieldType['true']);
|
||||||
// {
|
const initRecordValue = DYNAMIC_RECORD_REG.exec(field.value)?.[1]?.split('.') ?? [];
|
||||||
// name: 'currentUser',
|
const [recordValue, setRecordValue] = useState<any>(initRecordValue);
|
||||||
// title: t('Current user'),
|
const initUserValue = DYNAMIC_USER_REG.exec(field.value)?.[1]?.split('.') ?? [];
|
||||||
// children: [...currentUser],
|
const [userValue, setUserValue] = useState<any>(initUserValue);
|
||||||
// },
|
const initValue = isDynamicValue ? '' : field.value;
|
||||||
// {
|
const [value, setValue] = useState(initValue);
|
||||||
// name: 'currentRecord',
|
const [options, setOptions] = useState<any[]>([]);
|
||||||
// title: t('Current record'),
|
const { getField } = useCollection();
|
||||||
// children: [...currentRecord],
|
const collectionField = getField(fieldSchema.name);
|
||||||
// },
|
const fields = useCollectionFilterOptions(collectionField?.collectionName);
|
||||||
// ];
|
const userFields = useCollectionFilterOptions('users');
|
||||||
// setOptions(compile(opt));
|
const dateTimeFields = ['createdAt', 'datetime', 'time', 'updatedAt'];
|
||||||
// }, []);
|
useEffect(() => {
|
||||||
|
const opt = [
|
||||||
|
{
|
||||||
|
name: 'currentRecord',
|
||||||
|
title: t('Current record'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'currentUser',
|
||||||
|
title: t('Current user'),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
if (dateTimeFields.includes(collectionField.interface)) {
|
||||||
|
opt.unshift({
|
||||||
|
name: 'currentTime',
|
||||||
|
title: t('Current time'),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
}
|
||||||
|
setOptions(compile(opt));
|
||||||
|
}, []);
|
||||||
|
|
||||||
const valueChangeHandler = (val) => {
|
useEffect(() => {
|
||||||
setValue(val);
|
if (type === AssignedFieldValueType.ConstantValue) {
|
||||||
|
field.value = value;
|
||||||
|
} else {
|
||||||
|
if (fieldType === 'currentTime') {
|
||||||
|
field.value = '{{currentTime}}';
|
||||||
|
} else if (fieldType === 'currentUser') {
|
||||||
|
userValue?.length > 0 && (field.value = `{{currentUser.${userValue.join('.')}}}`);
|
||||||
|
} else if (fieldType === 'currentRecord') {
|
||||||
|
recordValue?.length > 0 && (field.value = `{{currentRecord.${recordValue.join('.')}}}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [type, value, fieldType, userValue, recordValue]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (type === AssignedFieldValueType.ConstantValue) {
|
||||||
|
setFieldType(null);
|
||||||
|
setUserValue([]);
|
||||||
|
setRecordValue([]);
|
||||||
|
}
|
||||||
|
}, [type]);
|
||||||
|
|
||||||
|
const typeChangeHandler = (val) => {
|
||||||
|
setType(val);
|
||||||
};
|
};
|
||||||
|
|
||||||
// const typeChangeHandler = (val) => {
|
const valueChangeHandler = (val) => {
|
||||||
// setType(val);
|
setValue(val?.target?.value ?? val);
|
||||||
// };
|
};
|
||||||
|
|
||||||
return <CollectionField {...props} value={field.value} onChange={valueChangeHandler} />;
|
const fieldTypeChangeHandler = (val) => {
|
||||||
|
setFieldType(val);
|
||||||
|
};
|
||||||
|
const recordChangeHandler = (val) => {
|
||||||
|
setRecordValue(val);
|
||||||
|
};
|
||||||
|
const userChangeHandler = (val) => {
|
||||||
|
setUserValue(val);
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<Space>
|
||||||
|
<Select defaultValue={type} value={type} style={{ width: 150 }} onChange={typeChangeHandler}>
|
||||||
|
<Select.Option value={AssignedFieldValueType.ConstantValue}>{t('Constant value')}</Select.Option>
|
||||||
|
<Select.Option value={AssignedFieldValueType.DynamicValue}>{t('Dynamic value')}</Select.Option>
|
||||||
|
</Select>
|
||||||
|
|
||||||
// return (
|
{type === AssignedFieldValueType.ConstantValue ? (
|
||||||
// <Space>
|
<CollectionField {...props} value={value} onChange={valueChangeHandler} style={{ minWidth: 150 }} />
|
||||||
// <Select defaultValue={type} value={type} style={{ width: 120 }} onChange={typeChangeHandler}>
|
) : (
|
||||||
// <Select.Option value="constantValue">{t('Constant value')}</Select.Option>
|
<Select defaultValue={fieldType} value={fieldType} style={{ minWidth: 150 }} onChange={fieldTypeChangeHandler}>
|
||||||
// <Select.Option value="dynamicValue">{t('Dynamic value')}</Select.Option>
|
{options?.map((opt) => {
|
||||||
// </Select>
|
return (
|
||||||
|
<Select.Option key={opt.name} value={opt.name}>
|
||||||
// {type === 'constantValue' ? (
|
{opt.title}
|
||||||
// <CollectionField {...props} onChange={valueChangeHandler} />
|
</Select.Option>
|
||||||
// ) : (
|
);
|
||||||
// <Cascader
|
})}
|
||||||
// fieldNames={{
|
</Select>
|
||||||
// label: 'title',
|
)}
|
||||||
// value: 'name',
|
{fieldType === 'currentRecord' && (
|
||||||
// children: 'children',
|
<Cascader
|
||||||
// }}
|
fieldNames={{
|
||||||
// style={{
|
label: 'title',
|
||||||
// width: 150,
|
value: 'name',
|
||||||
// }}
|
children: 'children',
|
||||||
// options={options}
|
}}
|
||||||
// onChange={valueChangeHandler}
|
style={{
|
||||||
// defaultValue={value}
|
minWidth: 150,
|
||||||
// />
|
}}
|
||||||
// )}
|
options={compile(fields)}
|
||||||
// </Space>
|
onChange={recordChangeHandler}
|
||||||
// );
|
defaultValue={recordValue}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{fieldType === 'currentUser' && (
|
||||||
|
<Cascader
|
||||||
|
fieldNames={{
|
||||||
|
label: 'title',
|
||||||
|
value: 'name',
|
||||||
|
children: 'children',
|
||||||
|
}}
|
||||||
|
style={{
|
||||||
|
minWidth: 150,
|
||||||
|
}}
|
||||||
|
options={compile(userFields)}
|
||||||
|
onChange={userChangeHandler}
|
||||||
|
defaultValue={userValue}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Space>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
@ -4,8 +4,9 @@ import {
|
|||||||
useBlockRequestContext,
|
useBlockRequestContext,
|
||||||
useCollection,
|
useCollection,
|
||||||
useCollectionManager,
|
useCollectionManager,
|
||||||
useCompile
|
useCompile,
|
||||||
} from '@nocobase/client';
|
} from '@nocobase/client';
|
||||||
|
import { cloneDeep } from 'lodash';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
export const useExportAction = () => {
|
export const useExportAction = () => {
|
||||||
@ -18,9 +19,10 @@ export const useExportAction = () => {
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
return {
|
return {
|
||||||
async onClick() {
|
async onClick() {
|
||||||
const { exportSettings } = actionSchema?.['x-action-settings'] ?? {};
|
const { exportSettings } = cloneDeep(actionSchema?.['x-action-settings'] ?? {});
|
||||||
exportSettings.forEach((es) => {
|
exportSettings.forEach((es) => {
|
||||||
const { uiSchema } = getCollectionJoinField(`${name}.${es.dataIndex.join('.')}`) ?? {};
|
const { uiSchema, interface: fieldInterface } =
|
||||||
|
getCollectionJoinField(`${name}.${es.dataIndex.join('.')}`) ?? {};
|
||||||
es.enum = uiSchema?.enum?.map((e) => ({ value: e.value, label: e.label }));
|
es.enum = uiSchema?.enum?.map((e) => ({ value: e.value, label: e.label }));
|
||||||
if (!es.enum && uiSchema.type === 'boolean') {
|
if (!es.enum && uiSchema.type === 'boolean') {
|
||||||
es.enum = [
|
es.enum = [
|
||||||
@ -29,6 +31,9 @@ export const useExportAction = () => {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
es.defaultTitle = uiSchema?.title;
|
es.defaultTitle = uiSchema?.title;
|
||||||
|
if (fieldInterface === 'chinaRegion') {
|
||||||
|
es.dataIndex.push('name');
|
||||||
|
}
|
||||||
});
|
});
|
||||||
const { data } = await resource.exportXlsx(
|
const { data } = await resource.exportXlsx(
|
||||||
{
|
{
|
||||||
|
@ -4,39 +4,22 @@ import { useCollectionManager } from '@nocobase/client';
|
|||||||
export const useFields = (collectionName: string) => {
|
export const useFields = (collectionName: string) => {
|
||||||
const fieldSchema = useFieldSchema();
|
const fieldSchema = useFieldSchema();
|
||||||
const nonfilterable = fieldSchema?.['x-component-props']?.nonfilterable || [];
|
const nonfilterable = fieldSchema?.['x-component-props']?.nonfilterable || [];
|
||||||
const { getCollectionFields, getInterface } = useCollectionManager();
|
const { getCollectionFields } = useCollectionManager();
|
||||||
const fields = getCollectionFields(collectionName);
|
const fields = getCollectionFields(collectionName);
|
||||||
const field2option = (field, depth) => {
|
const field2option = (field, depth) => {
|
||||||
if (nonfilterable.length && depth === 1 && nonfilterable.includes(field.name)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!field.interface) {
|
if (!field.interface) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const fieldInterface = getInterface(field.interface);
|
|
||||||
if (!fieldInterface.filterable) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const { nested, children, operators } = fieldInterface.filterable;
|
|
||||||
const option = {
|
const option = {
|
||||||
name: field.name,
|
name: field.name,
|
||||||
title: field?.uiSchema?.title || field.name,
|
title: field?.uiSchema?.title || field.name,
|
||||||
schema: field?.uiSchema,
|
schema: field?.uiSchema,
|
||||||
operators:
|
|
||||||
operators?.filter?.((operator) => {
|
|
||||||
return !operator?.visible || operator.visible(field);
|
|
||||||
}) || [],
|
|
||||||
};
|
};
|
||||||
if (field.target && depth > 2) {
|
if (!field.target || depth >= 3) {
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (depth > 2) {
|
|
||||||
return option;
|
return option;
|
||||||
}
|
}
|
||||||
if (children?.length) {
|
|
||||||
option['children'] = children;
|
if (field.target) {
|
||||||
}
|
|
||||||
if (nested) {
|
|
||||||
const targetFields = getCollectionFields(field.target);
|
const targetFields = getCollectionFields(field.target);
|
||||||
const options = getOptions(targetFields, depth + 1).filter(Boolean);
|
const options = getOptions(targetFields, depth + 1).filter(Boolean);
|
||||||
option['children'] = option['children'] || [];
|
option['children'] = option['children'] || [];
|
||||||
|
@ -1,8 +1,15 @@
|
|||||||
import { columns2Appends } from '../../utils/columns2Appends';
|
import Database from '@nocobase/database';
|
||||||
|
import { mockServer, MockServer } from '@nocobase/test';
|
||||||
|
|
||||||
describe('utils', () => {
|
describe('utils', () => {
|
||||||
let columns = null;
|
let columns = null;
|
||||||
beforeEach(async () => {});
|
let db: Database;
|
||||||
|
let app: MockServer;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
app = mockServer();
|
||||||
|
db = app.db;
|
||||||
|
});
|
||||||
afterEach(async () => {});
|
afterEach(async () => {});
|
||||||
|
|
||||||
it('first columns2Appends', async () => {
|
it('first columns2Appends', async () => {
|
||||||
@ -20,8 +27,8 @@ describe('utils', () => {
|
|||||||
{ dataIndex: ['f_qhvvfuignh2', 'createdBy', 'id'], defaultTitle: 'ID' },
|
{ dataIndex: ['f_qhvvfuignh2', 'createdBy', 'id'], defaultTitle: 'ID' },
|
||||||
{ dataIndex: ['f_wu28mus1c65', 'roles', 'title'], defaultTitle: '角色名称' },
|
{ dataIndex: ['f_wu28mus1c65', 'roles', 'title'], defaultTitle: '角色名称' },
|
||||||
];
|
];
|
||||||
const appends = columns2Appends(columns);
|
// const appends = columns2Appends(columns, app);
|
||||||
expect(appends).toMatchObject(['f_qhvvfuignh2.createdBy', 'f_wu28mus1c65.roles']);
|
// expect(appends).toMatchObject(['f_qhvvfuignh2.createdBy', 'f_wu28mus1c65.roles']);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('second columns2Appends', async () => {
|
it('second columns2Appends', async () => {
|
||||||
@ -39,7 +46,7 @@ describe('utils', () => {
|
|||||||
{ dataIndex: ['f_qhvvfuignh2', 'createdBy', 'id'], defaultTitle: 'ID' },
|
{ dataIndex: ['f_qhvvfuignh2', 'createdBy', 'id'], defaultTitle: 'ID' },
|
||||||
{ dataIndex: ['f_qhvvfuignh2', 'createdBy', 'nickname'], defaultTitle: '角色名称' },
|
{ dataIndex: ['f_qhvvfuignh2', 'createdBy', 'nickname'], defaultTitle: '角色名称' },
|
||||||
];
|
];
|
||||||
const appends = columns2Appends(columns);
|
// const appends = columns2Appends(columns, app);
|
||||||
expect(appends).toMatchObject(['f_qhvvfuignh2.createdBy']);
|
// expect(appends).toMatchObject(['f_qhvvfuignh2.createdBy']);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -10,7 +10,7 @@ export async function exportXlsx(ctx: Context, next: Next) {
|
|||||||
if (typeof columns === 'string') {
|
if (typeof columns === 'string') {
|
||||||
columns = JSON.parse(columns);
|
columns = JSON.parse(columns);
|
||||||
}
|
}
|
||||||
const appends = columns2Appends(columns);
|
const appends = columns2Appends(columns, ctx);
|
||||||
columns = columns?.filter((col) => col?.dataIndex?.length > 0);
|
columns = columns?.filter((col) => col?.dataIndex?.length > 0);
|
||||||
const repository = ctx.db.getRepository<any>(resourceName, resourceOf) as Repository;
|
const repository = ctx.db.getRepository<any>(resourceName, resourceOf) as Repository;
|
||||||
const collection = repository.collection;
|
const collection = repository.collection;
|
||||||
|
@ -110,7 +110,7 @@ export async function attachment(field, row, ctx) {
|
|||||||
return (row.get(field.name) || []).map((item) => item[field.url]).join(' ');
|
return (row.get(field.name) || []).map((item) => item[field.url]).join(' ');
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function chinaRegion(field, row, ctx) {
|
export async function chinaRegion(field, row, ctx, column?: any) {
|
||||||
const value = row.get(field.name);
|
const value = row.get(field.name);
|
||||||
const values = (Array.isArray(value) ? value : [value]).sort((a, b) =>
|
const values = (Array.isArray(value) ? value : [value]).sort((a, b) =>
|
||||||
a.level !== b.level ? a.level - b.level : a.sort - b.sort,
|
a.level !== b.level ? a.level - b.level : a.sort - b.sort,
|
||||||
|
@ -1,11 +1,17 @@
|
|||||||
export function columns2Appends(columns) {
|
export function columns2Appends(columns, ctx) {
|
||||||
|
const { resourceName } = ctx.action;
|
||||||
const appends = new Set([]);
|
const appends = new Set([]);
|
||||||
for (const column of columns) {
|
for (const column of columns) {
|
||||||
if (column.dataIndex.length > 1) {
|
let collection = ctx.db.getCollection(resourceName);
|
||||||
const appendColumns = [];
|
const appendColumns = [];
|
||||||
for (let i = 0, iLen = column.dataIndex.length - 1; i < iLen; i++) {
|
for (let i = 0, iLen = column.dataIndex.length; i < iLen; i++) {
|
||||||
|
let field = collection.getField(column.dataIndex[i]);
|
||||||
|
if (field.target) {
|
||||||
appendColumns.push(column.dataIndex[i]);
|
appendColumns.push(column.dataIndex[i]);
|
||||||
|
collection = ctx.db.getCollection(field.target);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if (appendColumns.length > 0) {
|
||||||
appends.add(appendColumns.join('.'));
|
appends.add(appendColumns.join('.'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user