mirror of
https://gitee.com/nocobase/nocobase.git
synced 2024-11-29 18:58:26 +08:00
feat: tree block (#4566)
* feat: tree block * fix: remove showSettings * fix: bug * fix: bug * fix: bug * fix: bug * fix: table tree expand * fix: bug * fix: connection bug T-4501 * fix: template settings bug
This commit is contained in:
parent
868813b5fc
commit
a5fc22c6b0
@ -110,11 +110,11 @@ const mocks = {
|
||||
},
|
||||
],
|
||||
},
|
||||
[`${collection}:get/1`]: {
|
||||
[`${collection}:get?filter[id]=1`]: {
|
||||
id: 1,
|
||||
username: 'Tom',
|
||||
},
|
||||
[`${collection}:get/2`]: {
|
||||
[`${collection}:get?filter[id]=2`]: {
|
||||
id: 1,
|
||||
username: 'Jack',
|
||||
},
|
||||
|
@ -12,3 +12,4 @@ export * from './SchemaSettingsManager';
|
||||
export * from './components';
|
||||
export * from './context/SchemaSettingItemContext';
|
||||
export * from './types';
|
||||
export * from './utils';
|
||||
|
@ -0,0 +1,66 @@
|
||||
/**
|
||||
* This file is part of the NocoBase (R) project.
|
||||
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
||||
* Authors: NocoBase Team.
|
||||
*
|
||||
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||
*/
|
||||
|
||||
import { SchemaSettingsItemType, useCompile, useDesignable } from '@nocobase/client';
|
||||
import { ISchema, useFieldSchema } from '@formily/react';
|
||||
import _ from 'lodash';
|
||||
import { TFunction, useTranslation } from 'react-i18next';
|
||||
|
||||
import { getNewSchema, useHookDefault } from './util';
|
||||
|
||||
export interface CreateModalSchemaSettingsItemProps {
|
||||
name: string;
|
||||
title: string | ((t: TFunction<'translation', undefined>) => string);
|
||||
parentSchemaKey: string;
|
||||
defaultValue?: any;
|
||||
useDefaultValue?: () => any;
|
||||
schema: (defaultValue: any) => ISchema;
|
||||
valueKeys?: string[];
|
||||
useVisible?: () => boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* create `switch` type schema settings item
|
||||
*
|
||||
* @internal
|
||||
* @unstable
|
||||
*/
|
||||
export function createModalSettingsItem(options: CreateModalSchemaSettingsItemProps): SchemaSettingsItemType {
|
||||
const {
|
||||
name,
|
||||
parentSchemaKey,
|
||||
valueKeys,
|
||||
schema,
|
||||
title,
|
||||
useVisible,
|
||||
defaultValue: propsDefaultValue,
|
||||
useDefaultValue = useHookDefault,
|
||||
} = options;
|
||||
return {
|
||||
name,
|
||||
type: 'actionModal',
|
||||
useComponentProps() {
|
||||
const fieldSchema = useFieldSchema();
|
||||
const { deepMerge } = useDesignable();
|
||||
const defaultValue = useDefaultValue(propsDefaultValue);
|
||||
const values = _.get(fieldSchema, parentSchemaKey);
|
||||
const compile = useCompile();
|
||||
const { t } = useTranslation();
|
||||
|
||||
return {
|
||||
title: typeof title === 'function' ? title(t) : compile(title),
|
||||
useVisible,
|
||||
schema: schema({ ...defaultValue, ...values }),
|
||||
onSubmit(values) {
|
||||
deepMerge(getNewSchema({ fieldSchema, schemaKey: parentSchemaKey, value: values, valueKeys }));
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
@ -0,0 +1,68 @@
|
||||
/**
|
||||
* This file is part of the NocoBase (R) project.
|
||||
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
||||
* Authors: NocoBase Team.
|
||||
*
|
||||
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||
*/
|
||||
|
||||
import _ from 'lodash';
|
||||
import { useFieldSchema } from '@formily/react';
|
||||
import { SchemaSettingsItemType, SelectProps, useCompile, useDesignable } from '@nocobase/client';
|
||||
import { getNewSchema, useHookDefault } from './util';
|
||||
import { TFunction, useTranslation } from 'react-i18next';
|
||||
|
||||
interface CreateSelectSchemaSettingsItemProps {
|
||||
name: string;
|
||||
title: string | ((t: TFunction<'translation', undefined>) => string);
|
||||
options?: SelectProps['options'];
|
||||
useOptions?: () => SelectProps['options'];
|
||||
schemaKey: string;
|
||||
defaultValue?: string | number;
|
||||
useDefaultValue?: () => string | number;
|
||||
useVisible?: () => boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* create `select` type schema settings item
|
||||
*
|
||||
* @internal
|
||||
* @unstable
|
||||
*/
|
||||
export const createSelectSchemaSettingsItem = (
|
||||
options: CreateSelectSchemaSettingsItemProps,
|
||||
): SchemaSettingsItemType => {
|
||||
const {
|
||||
name,
|
||||
title,
|
||||
options: propsOptions,
|
||||
useOptions = useHookDefault,
|
||||
schemaKey,
|
||||
useVisible,
|
||||
defaultValue: propsDefaultValue,
|
||||
useDefaultValue = useHookDefault,
|
||||
} = options;
|
||||
return {
|
||||
name,
|
||||
type: 'select',
|
||||
useComponentProps() {
|
||||
const filedSchema = useFieldSchema();
|
||||
const { deepMerge } = useDesignable();
|
||||
const options = useOptions(propsOptions);
|
||||
const defaultValue = useDefaultValue(propsDefaultValue);
|
||||
const compile = useCompile();
|
||||
const { t } = useTranslation();
|
||||
|
||||
return {
|
||||
title: typeof title === 'function' ? title(t) : compile(title),
|
||||
options,
|
||||
useVisible,
|
||||
value: _.get(filedSchema, schemaKey, defaultValue),
|
||||
onChange(v) {
|
||||
deepMerge(getNewSchema({ fieldSchema: filedSchema, schemaKey, value: v }));
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
};
|
@ -0,0 +1,61 @@
|
||||
/**
|
||||
* This file is part of the NocoBase (R) project.
|
||||
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
||||
* Authors: NocoBase Team.
|
||||
*
|
||||
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||
*/
|
||||
|
||||
import { SchemaSettingsItemType, useCompile, useDesignable } from '@nocobase/client';
|
||||
import { useFieldSchema } from '@formily/react';
|
||||
import _ from 'lodash';
|
||||
|
||||
import { getNewSchema, useHookDefault } from './util';
|
||||
import { TFunction, useTranslation } from 'react-i18next';
|
||||
|
||||
export interface CreateSwitchSchemaSettingsItemProps {
|
||||
name: string;
|
||||
title: string | ((t: TFunction<'translation', undefined>) => string);
|
||||
schemaKey: string;
|
||||
defaultValue?: boolean;
|
||||
useDefaultValue?: () => boolean;
|
||||
useVisible?: () => boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* create `switch` type schema settings item
|
||||
*
|
||||
* @internal
|
||||
* @unstable
|
||||
*/
|
||||
export function createSwitchSettingsItem(options: CreateSwitchSchemaSettingsItemProps): SchemaSettingsItemType {
|
||||
const {
|
||||
name,
|
||||
useVisible,
|
||||
schemaKey,
|
||||
title,
|
||||
defaultValue: propsDefaultValue,
|
||||
useDefaultValue = useHookDefault,
|
||||
} = options;
|
||||
return {
|
||||
name,
|
||||
useVisible,
|
||||
type: 'switch',
|
||||
useComponentProps() {
|
||||
const filedSchema = useFieldSchema();
|
||||
const { deepMerge } = useDesignable();
|
||||
const defaultValue = useDefaultValue(propsDefaultValue);
|
||||
const compile = useCompile();
|
||||
const { t } = useTranslation();
|
||||
|
||||
return {
|
||||
title: typeof title === 'function' ? title(t) : compile(title),
|
||||
checked: !!_.get(filedSchema, schemaKey, defaultValue),
|
||||
onChange(v) {
|
||||
deepMerge(getNewSchema({ fieldSchema: filedSchema, schemaKey, value: v }));
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
/**
|
||||
* This file is part of the NocoBase (R) project.
|
||||
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
||||
* Authors: NocoBase Team.
|
||||
*
|
||||
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||
*/
|
||||
|
||||
export * from './createSelectSettingsItem';
|
||||
export * from './createSwitchSettingsItem';
|
||||
export * from './createModalSettingsItem';
|
@ -0,0 +1,39 @@
|
||||
/**
|
||||
* This file is part of the NocoBase (R) project.
|
||||
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
||||
* Authors: NocoBase Team.
|
||||
*
|
||||
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||
*/
|
||||
|
||||
import { ISchema } from '@formily/json-schema';
|
||||
import _ from 'lodash';
|
||||
|
||||
type IGetNewSchema = {
|
||||
fieldSchema: ISchema;
|
||||
schemaKey: string;
|
||||
value: any;
|
||||
valueKeys?: string[];
|
||||
};
|
||||
|
||||
export function getNewSchema(options: IGetNewSchema) {
|
||||
const { fieldSchema, schemaKey, value, valueKeys } = options as any;
|
||||
const schemaKeyArr = schemaKey.split('.');
|
||||
const clonedSchema = _.cloneDeep(fieldSchema[schemaKeyArr[0]]);
|
||||
|
||||
if (value != undefined && typeof value === 'object') {
|
||||
Object.keys(value).forEach((key) => {
|
||||
if (valueKeys && !valueKeys.includes(key)) return;
|
||||
_.set(clonedSchema, `${schemaKeyArr.slice(1)}.${key}`, value[key]);
|
||||
});
|
||||
} else {
|
||||
_.set(clonedSchema, schemaKeyArr.slice(1), value);
|
||||
}
|
||||
return {
|
||||
'x-uid': fieldSchema['x-uid'],
|
||||
[schemaKeyArr[0]]: clonedSchema,
|
||||
};
|
||||
}
|
||||
|
||||
export const useHookDefault = (defaultValues?: any) => defaultValues;
|
@ -9,7 +9,7 @@
|
||||
|
||||
import { createForm } from '@formily/core';
|
||||
import { FormContext, useField, useFieldSchema } from '@formily/react';
|
||||
import React, { createContext, useCallback, useContext, useMemo, useState } from 'react';
|
||||
import React, { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react';
|
||||
import { useCollectionManager_deprecated } from '../collection-manager';
|
||||
import { withDynamicSchemaProps } from '../hoc/withDynamicSchemaProps';
|
||||
import { useTableBlockParams } from '../modules/blocks/data-blocks/table';
|
||||
@ -49,14 +49,29 @@ interface Props {
|
||||
*/
|
||||
collection?: string;
|
||||
children?: any;
|
||||
expandFlag?: boolean;
|
||||
}
|
||||
|
||||
const InternalTableBlockProvider = (props: Props) => {
|
||||
const { params, showIndex, dragSort, rowKey, childrenColumnName, fieldNames, ...others } = props;
|
||||
const {
|
||||
params,
|
||||
showIndex,
|
||||
dragSort,
|
||||
rowKey,
|
||||
childrenColumnName,
|
||||
expandFlag: propsExpandFlag = false,
|
||||
fieldNames,
|
||||
...others
|
||||
} = props;
|
||||
const field: any = useField();
|
||||
const { resource, service } = useBlockRequestContext();
|
||||
const fieldSchema = useFieldSchema();
|
||||
const [expandFlag, setExpandFlag] = useState(fieldNames ? true : false);
|
||||
const [expandFlag, setExpandFlag] = useState(fieldNames || propsExpandFlag ? true : false);
|
||||
|
||||
useEffect(() => {
|
||||
setExpandFlag(fieldNames || propsExpandFlag);
|
||||
}, [fieldNames || propsExpandFlag]);
|
||||
|
||||
const allIncludesChildren = useMemo(() => {
|
||||
const { treeTable } = fieldSchema?.['x-decorator-props'] || {};
|
||||
const data = service?.data?.data;
|
||||
|
@ -61,10 +61,10 @@ export const TableSelectorParamsProvider = ({ params, children }: { params: Para
|
||||
};
|
||||
|
||||
const InternalTableSelectorProvider = (props) => {
|
||||
const { params, rowKey, extraFilter } = props;
|
||||
const { params, rowKey, extraFilter, expandFlag: propsExpandFlag = false } = props;
|
||||
const field = useField();
|
||||
const { resource, service } = useBlockRequestContext();
|
||||
const [expandFlag, setExpandFlag] = useState(false);
|
||||
const [expandFlag, setExpandFlag] = useState(propsExpandFlag);
|
||||
const parentRecordData = useCollectionParentRecordData();
|
||||
// if (service.loading) {
|
||||
// return <Spin />;
|
||||
|
@ -287,4 +287,8 @@ export class Collection {
|
||||
hasField(name: SchemaKey) {
|
||||
return !!this.getField(name);
|
||||
}
|
||||
|
||||
isTitleField(field: CollectionFieldOptions) {
|
||||
return this.app.dataSourceManager.collectionFieldInterfaceManager.getFieldInterface(field.interface)?.titleUsable;
|
||||
}
|
||||
}
|
||||
|
@ -815,5 +815,216 @@
|
||||
"template": "general",
|
||||
"view": false,
|
||||
"filterTargetKey": "id"
|
||||
},
|
||||
{
|
||||
"key": "knautlbeoqj",
|
||||
"name": "tree-collection",
|
||||
"title": "tree-collection",
|
||||
"inherit": false,
|
||||
"hidden": false,
|
||||
"description": null,
|
||||
"fields": [
|
||||
{
|
||||
"key": "h4ixmucmj41",
|
||||
"name": "parentId",
|
||||
"type": "bigInt",
|
||||
"interface": "integer",
|
||||
"description": null,
|
||||
"collectionName": "tree-collection",
|
||||
"parentKey": null,
|
||||
"reverseKey": null,
|
||||
"isForeignKey": true,
|
||||
"uiSchema": {
|
||||
"type": "number",
|
||||
"title": "{{t(\"Parent ID\")}}",
|
||||
"x-component": "InputNumber",
|
||||
"x-read-pretty": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"key": "noozlvzzcxs",
|
||||
"name": "parent",
|
||||
"type": "belongsTo",
|
||||
"interface": "m2o",
|
||||
"description": null,
|
||||
"collectionName": "tree-collection",
|
||||
"parentKey": null,
|
||||
"reverseKey": null,
|
||||
"foreignKey": "parentId",
|
||||
"treeParent": true,
|
||||
"onDelete": "CASCADE",
|
||||
"uiSchema": {
|
||||
"title": "{{t(\"Parent\")}}",
|
||||
"x-component": "AssociationField",
|
||||
"x-component-props": {
|
||||
"multiple": false,
|
||||
"fieldNames": {
|
||||
"label": "id",
|
||||
"value": "id"
|
||||
}
|
||||
}
|
||||
},
|
||||
"target": "tree-collection",
|
||||
"targetKey": "id"
|
||||
},
|
||||
{
|
||||
"key": "0ozo4vvd28w",
|
||||
"name": "children",
|
||||
"type": "hasMany",
|
||||
"interface": "o2m",
|
||||
"description": null,
|
||||
"collectionName": "tree-collection",
|
||||
"parentKey": null,
|
||||
"reverseKey": null,
|
||||
"foreignKey": "parentId",
|
||||
"treeChildren": true,
|
||||
"onDelete": "CASCADE",
|
||||
"uiSchema": {
|
||||
"title": "{{t(\"Children\")}}",
|
||||
"x-component": "AssociationField",
|
||||
"x-component-props": {
|
||||
"multiple": true,
|
||||
"fieldNames": {
|
||||
"label": "id",
|
||||
"value": "id"
|
||||
}
|
||||
}
|
||||
},
|
||||
"target": "tree-collection",
|
||||
"targetKey": "id",
|
||||
"sourceKey": "id"
|
||||
},
|
||||
{
|
||||
"key": "dsqipnyzywx",
|
||||
"name": "id",
|
||||
"type": "bigInt",
|
||||
"interface": "integer",
|
||||
"description": null,
|
||||
"collectionName": "tree-collection",
|
||||
"parentKey": null,
|
||||
"reverseKey": null,
|
||||
"autoIncrement": true,
|
||||
"primaryKey": true,
|
||||
"allowNull": false,
|
||||
"uiSchema": {
|
||||
"type": "number",
|
||||
"title": "{{t(\"ID\")}}",
|
||||
"x-component": "InputNumber",
|
||||
"x-read-pretty": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"key": "51j7ynbyajo",
|
||||
"name": "createdAt",
|
||||
"type": "date",
|
||||
"interface": "createdAt",
|
||||
"description": null,
|
||||
"collectionName": "tree-collection",
|
||||
"parentKey": null,
|
||||
"reverseKey": null,
|
||||
"field": "createdAt",
|
||||
"uiSchema": {
|
||||
"type": "datetime",
|
||||
"title": "{{t(\"Created at\")}}",
|
||||
"x-component": "DatePicker",
|
||||
"x-component-props": {},
|
||||
"x-read-pretty": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"key": "fn6dpvd9ms1",
|
||||
"name": "createdBy",
|
||||
"type": "belongsTo",
|
||||
"interface": "createdBy",
|
||||
"description": null,
|
||||
"collectionName": "tree-collection",
|
||||
"parentKey": null,
|
||||
"reverseKey": null,
|
||||
"target": "users",
|
||||
"foreignKey": "createdById",
|
||||
"uiSchema": {
|
||||
"type": "object",
|
||||
"title": "{{t(\"Created by\")}}",
|
||||
"x-component": "AssociationField",
|
||||
"x-component-props": {
|
||||
"fieldNames": {
|
||||
"value": "id",
|
||||
"label": "nickname"
|
||||
}
|
||||
},
|
||||
"x-read-pretty": true
|
||||
},
|
||||
"targetKey": "id"
|
||||
},
|
||||
{
|
||||
"key": "n7zolkpe5rh",
|
||||
"name": "updatedAt",
|
||||
"type": "date",
|
||||
"interface": "updatedAt",
|
||||
"description": null,
|
||||
"collectionName": "tree-collection",
|
||||
"parentKey": null,
|
||||
"reverseKey": null,
|
||||
"field": "updatedAt",
|
||||
"uiSchema": {
|
||||
"type": "string",
|
||||
"title": "{{t(\"Last updated at\")}}",
|
||||
"x-component": "DatePicker",
|
||||
"x-component-props": {},
|
||||
"x-read-pretty": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"key": "ogtun00ozu2",
|
||||
"name": "updatedBy",
|
||||
"type": "belongsTo",
|
||||
"interface": "updatedBy",
|
||||
"description": null,
|
||||
"collectionName": "tree-collection",
|
||||
"parentKey": null,
|
||||
"reverseKey": null,
|
||||
"target": "users",
|
||||
"foreignKey": "updatedById",
|
||||
"uiSchema": {
|
||||
"type": "object",
|
||||
"title": "{{t(\"Last updated by\")}}",
|
||||
"x-component": "AssociationField",
|
||||
"x-component-props": {
|
||||
"fieldNames": {
|
||||
"value": "id",
|
||||
"label": "nickname"
|
||||
}
|
||||
},
|
||||
"x-read-pretty": true
|
||||
},
|
||||
"targetKey": "id"
|
||||
},
|
||||
{
|
||||
"key": "4tiwdomlf6g",
|
||||
"name": "title",
|
||||
"type": "string",
|
||||
"interface": "input",
|
||||
"description": null,
|
||||
"collectionName": "tree-collection",
|
||||
"parentKey": null,
|
||||
"reverseKey": null,
|
||||
"uiSchema": {
|
||||
"type": "string",
|
||||
"x-component": "Input",
|
||||
"title": "title"
|
||||
}
|
||||
}
|
||||
],
|
||||
"logging": true,
|
||||
"autoGenId": true,
|
||||
"createdAt": true,
|
||||
"createdBy": true,
|
||||
"updatedAt": true,
|
||||
"updatedBy": true,
|
||||
"template": "tree",
|
||||
"view": false,
|
||||
"tree": "adjacencyList",
|
||||
"schema": "public",
|
||||
"filterTargetKey": "id"
|
||||
}
|
||||
]
|
||||
|
@ -283,5 +283,148 @@
|
||||
"destroy": [1, 2]
|
||||
}
|
||||
}
|
||||
},
|
||||
"tree-collection:list": {
|
||||
"data": [
|
||||
{
|
||||
"id": 1,
|
||||
"createdAt": "2024-06-05T02:58:23.961Z",
|
||||
"updatedAt": "2024-06-05T02:58:23.961Z",
|
||||
"parentId": null,
|
||||
"createdById": 1,
|
||||
"updatedById": 1,
|
||||
"title": "title-0",
|
||||
"children": [
|
||||
{
|
||||
"id": 4,
|
||||
"createdAt": "2024-06-05T02:59:21.455Z",
|
||||
"updatedAt": "2024-06-05T02:59:21.462Z",
|
||||
"parentId": 1,
|
||||
"createdById": 1,
|
||||
"updatedById": 1,
|
||||
"title": "title-0-0",
|
||||
"children": [
|
||||
{
|
||||
"id": 10,
|
||||
"createdAt": "2024-06-05T03:00:14.122Z",
|
||||
"updatedAt": "2024-06-05T03:00:14.129Z",
|
||||
"parentId": 4,
|
||||
"createdById": 1,
|
||||
"updatedById": 1,
|
||||
"title": "title-0-0-0",
|
||||
"__index": "0.children.0.children.0"
|
||||
},
|
||||
{
|
||||
"id": 11,
|
||||
"createdAt": "2024-06-05T03:00:21.816Z",
|
||||
"updatedAt": "2024-06-05T03:00:21.822Z",
|
||||
"parentId": 4,
|
||||
"createdById": 1,
|
||||
"updatedById": 1,
|
||||
"title": "title-0-0-1",
|
||||
"__index": "0.children.0.children.1"
|
||||
},
|
||||
{
|
||||
"id": 12,
|
||||
"createdAt": "2024-06-05T03:00:34.253Z",
|
||||
"updatedAt": "2024-06-05T03:00:34.261Z",
|
||||
"parentId": 4,
|
||||
"createdById": 1,
|
||||
"updatedById": 1,
|
||||
"title": "title-0-0-2",
|
||||
"__index": "0.children.0.children.2"
|
||||
}
|
||||
],
|
||||
"__index": "0.children.0"
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"createdAt": "2024-06-05T02:59:27.667Z",
|
||||
"updatedAt": "2024-06-05T02:59:27.676Z",
|
||||
"parentId": 1,
|
||||
"createdById": 1,
|
||||
"updatedById": 1,
|
||||
"title": "title-0-1",
|
||||
"__index": "0.children.1"
|
||||
},
|
||||
{
|
||||
"id": 6,
|
||||
"createdAt": "2024-06-05T02:59:33.167Z",
|
||||
"updatedAt": "2024-06-05T02:59:33.174Z",
|
||||
"parentId": 1,
|
||||
"createdById": 1,
|
||||
"updatedById": 1,
|
||||
"title": "title-0-2",
|
||||
"__index": "0.children.2"
|
||||
}
|
||||
],
|
||||
"__index": "0"
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"createdAt": "2024-06-05T02:58:32.009Z",
|
||||
"updatedAt": "2024-06-05T02:58:32.009Z",
|
||||
"parentId": null,
|
||||
"createdById": 1,
|
||||
"updatedById": 1,
|
||||
"title": "title-1",
|
||||
"children": [
|
||||
{
|
||||
"id": 7,
|
||||
"createdAt": "2024-06-05T02:59:39.049Z",
|
||||
"updatedAt": "2024-06-05T02:59:39.055Z",
|
||||
"parentId": 2,
|
||||
"createdById": 1,
|
||||
"updatedById": 1,
|
||||
"title": "title-1-0",
|
||||
"__index": "1.children.0"
|
||||
},
|
||||
{
|
||||
"id": 8,
|
||||
"createdAt": "2024-06-05T02:59:47.298Z",
|
||||
"updatedAt": "2024-06-05T02:59:47.305Z",
|
||||
"parentId": 2,
|
||||
"createdById": 1,
|
||||
"updatedById": 1,
|
||||
"title": "title-1-1",
|
||||
"__index": "1.children.1"
|
||||
}
|
||||
],
|
||||
"__index": "1"
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"createdAt": "2024-06-05T02:58:34.289Z",
|
||||
"updatedAt": "2024-06-05T02:58:34.289Z",
|
||||
"parentId": null,
|
||||
"createdById": 1,
|
||||
"updatedById": 1,
|
||||
"title": "title-2",
|
||||
"children": [
|
||||
{
|
||||
"id": 9,
|
||||
"createdAt": "2024-06-05T02:59:54.611Z",
|
||||
"updatedAt": "2024-06-05T02:59:54.617Z",
|
||||
"parentId": 3,
|
||||
"createdById": 1,
|
||||
"updatedById": 1,
|
||||
"title": "title-2-0",
|
||||
"__index": "2.children.0"
|
||||
}
|
||||
],
|
||||
"__index": "2"
|
||||
}
|
||||
],
|
||||
"meta": {
|
||||
"count": 3,
|
||||
"page": 1,
|
||||
"pageSize": 20,
|
||||
"totalPage": 1,
|
||||
"allowedActions": {
|
||||
"view": [1, 4, 10, 11, 12, 5, 6, 2, 7, 8, 3, 9],
|
||||
"update": [1, 4, 10, 11, 12, 5, 6, 2, 7, 8, 3, 9],
|
||||
"destroy": [1, 4, 10, 11, 12, 5, 6, 2, 7, 8, 3, 9]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,9 @@ import {
|
||||
AntdSchemaComponentPlugin,
|
||||
Application,
|
||||
ApplicationOptions,
|
||||
BlockSchemaToolbar,
|
||||
CollectionPlugin,
|
||||
DataBlockInitializer,
|
||||
LocalDataSource,
|
||||
SchemaSettingsPlugin,
|
||||
} from '../index';
|
||||
@ -108,7 +110,11 @@ export const mockApp = (options: MockAppOptions) => {
|
||||
app.pluginManager.add(AntdSchemaComponentPlugin);
|
||||
app.pluginManager.add(SchemaSettingsPlugin);
|
||||
app.pluginManager.add(CollectionPlugin, { config: { enableRemoteDataSource: false } });
|
||||
app.addComponents({ ActionInitializer });
|
||||
app.addComponents({
|
||||
ActionInitializer,
|
||||
DataBlockInitializer,
|
||||
BlockSchemaToolbar,
|
||||
});
|
||||
|
||||
const apis = Object.assign({}, defaultApis, optionsApis);
|
||||
|
||||
|
@ -190,7 +190,7 @@ export const useFilterAPI = () => {
|
||||
|
||||
useEffect(() => {
|
||||
setIsConnected(targets && targets.some((target) => dataBlocks.some((dataBlock) => dataBlock.uid === target.uid)));
|
||||
}, [targetsKeys.length, dataBlocks]);
|
||||
}, [targetsKeys.length, targets, dataBlocks]);
|
||||
|
||||
const doFilter = useCallback(
|
||||
(
|
||||
@ -242,7 +242,7 @@ export const useFilterAPI = () => {
|
||||
);
|
||||
});
|
||||
},
|
||||
[dataBlocks],
|
||||
[dataBlocks, targets, uid],
|
||||
);
|
||||
|
||||
return {
|
||||
|
@ -828,5 +828,6 @@
|
||||
"Drag and drop sorting field": "Drag and drop sorting field",
|
||||
"This variable has been deprecated and can be replaced with \"Current form\"": "This variable has been deprecated and can be replaced with \"Current form\"",
|
||||
"The value of this variable is derived from the query string of the page URL. This variable can only be used normally when the page has a query string.": "The value of this variable is derived from the query string of the page URL. This variable can only be used normally when the page has a query string.",
|
||||
"URL search params": "URL search params"
|
||||
"URL search params": "URL search params",
|
||||
"Expand All": "Expand All"
|
||||
}
|
||||
|
@ -760,5 +760,6 @@
|
||||
"License": "Licencia",
|
||||
"This variable has been deprecated and can be replaced with \"Current form\"": "La variable ha sido obsoleta; \"Formulario actual\" puede ser utilizada como sustituto",
|
||||
"The value of this variable is derived from the query string of the page URL. This variable can only be used normally when the page has a query string.": "El valor de esta variable se deriva de la cadena de consulta de la URL de la página. Esta variable sólo puede utilizarse normalmente cuando la página tiene una cadena de consulta.",
|
||||
"URL search params": "Parámetros de búsqueda de URL"
|
||||
"URL search params": "Parámetros de búsqueda de URL",
|
||||
"Expand All": "Expandir todo"
|
||||
}
|
||||
|
@ -780,5 +780,6 @@
|
||||
"License": "Licence",
|
||||
"This variable has been deprecated and can be replaced with \"Current form\"": "La variable a été obsolète ; \"Formulaire actuel\" peut être utilisé comme substitut",
|
||||
"The value of this variable is derived from the query string of the page URL. This variable can only be used normally when the page has a query string.": "La valeur de cette variable est dérivée de la chaîne de requête de l'URL de la page. Cette variable ne peut être utilisée normalement que lorsque la page a une chaîne de requête.",
|
||||
"URL search params": "Paramètres de recherche d'URL"
|
||||
"URL search params": "Paramètres de recherche d'URL",
|
||||
"Expand All": "Tout déplier"
|
||||
}
|
||||
|
@ -699,5 +699,6 @@
|
||||
"License": "ライセンス",
|
||||
"This variable has been deprecated and can be replaced with \"Current form\"": "この変数は非推奨です。代わりに「現在のフォーム」を使用してください",
|
||||
"The value of this variable is derived from the query string of the page URL. This variable can only be used normally when the page has a query string.": "この変数の値はページURLのクエリ文字列から取得されます。この変数は、ページにクエリ文字列がある場合にのみ正常に使用できます。",
|
||||
"URL search params": "URL検索パラメータ"
|
||||
"URL search params": "URL検索パラメータ",
|
||||
"Expand All": "すべて展開"
|
||||
}
|
||||
|
@ -871,5 +871,6 @@
|
||||
"License": "라이선스",
|
||||
"This variable has been deprecated and can be replaced with \"Current form\"": "변수가 폐기되었습니다. \"현재 폼\"을 대체로 사용할 수 있습니다",
|
||||
"The value of this variable is derived from the query string of the page URL. This variable can only be used normally when the page has a query string.": "이 변수의 값은 페이지 URL의 쿼리 문자열에서 파생됩니다. 이 변수는 페이지에 쿼리 문자열이 있는 경우에만 정상적으로 사용할 수 있습니다.",
|
||||
"URL search params": "URL 검색 매개변수"
|
||||
"URL search params": "URL 검색 매개변수",
|
||||
"Expand All": "모두 펼치기"
|
||||
}
|
||||
|
@ -736,5 +736,6 @@
|
||||
"License": "Licença",
|
||||
"This variable has been deprecated and can be replaced with \"Current form\"": "A variável foi descontinuada; \"Formulário atual\" pode ser usada como substituto",
|
||||
"The value of this variable is derived from the query string of the page URL. This variable can only be used normally when the page has a query string.": "O valor desta variável é derivado da string de consulta da URL da página. Esta variável só pode ser usada normalmente quando a página tem uma string de consulta.",
|
||||
"URL search params": "Parâmetros de pesquisa de URL"
|
||||
"URL search params": "Parâmetros de pesquisa de URL",
|
||||
"Expand All": "Expandir tudo"
|
||||
}
|
||||
|
@ -574,5 +574,6 @@
|
||||
"License": "Лицензия",
|
||||
"This variable has been deprecated and can be replaced with \"Current form\"": "Переменная устарела; \"Текущая форма\" может быть использована в качестве замены",
|
||||
"The value of this variable is derived from the query string of the page URL. This variable can only be used normally when the page has a query string.": "Значение этой переменной происходит из строки запроса URL страницы. Эта переменная может использоваться только в том случае, если у страницы есть строка запроса.",
|
||||
"URL search params": "Параметры поиска URL"
|
||||
"URL search params": "Параметры поиска URL",
|
||||
"Expand All": "Развернуть все"
|
||||
}
|
||||
|
@ -572,5 +572,6 @@
|
||||
"License": "Lisans",
|
||||
"This variable has been deprecated and can be replaced with \"Current form\"": "Değişken kullanımdan kaldırıldı; \"Geçerli form\" yerine kullanılabilir",
|
||||
"The value of this variable is derived from the query string of the page URL. This variable can only be used normally when the page has a query string.": "Bu değişkenin değeri sayfa URL'sinin sorgu dizgisinden türetilir. Bu değişken, sayfanın bir sorgu dizgisi olduğunda yalnızca normal olarak kullanılabilir.",
|
||||
"URL search params": "URL arama parametreleri"
|
||||
"URL search params": "URL arama parametreleri",
|
||||
"Expand All": "Tümünü genişlet"
|
||||
}
|
||||
|
@ -780,5 +780,6 @@
|
||||
"License": "Ліцензія",
|
||||
"This variable has been deprecated and can be replaced with \"Current form\"": "Змінна була застарілою; \"Поточна форма\" може бути використана як заміна",
|
||||
"The value of this variable is derived from the query string of the page URL. This variable can only be used normally when the page has a query string.": "Значення цієї змінної походить з рядка запиту URL-адреси сторінки. Цю змінну можна використовувати нормально лише тоді, коли у сторінки є рядок запиту.",
|
||||
"URL search params": "Параметри пошуку URL"
|
||||
"URL search params": "Параметри пошуку URL",
|
||||
"Expand All": "Розгорнути все"
|
||||
}
|
||||
|
@ -957,5 +957,6 @@
|
||||
"The value of this variable is derived from the query string of the page URL. This variable can only be used normally when the page has a query string.": "该变量的值是根据页面 URL 的 query string 得来的,只有当页面存在 query string 的时候,该变量才能正常使用。",
|
||||
"Edit link":"编辑链接",
|
||||
"Add parameter":"添加参数",
|
||||
"URL search params": "URL 查询参数"
|
||||
"URL search params": "URL 查询参数",
|
||||
"Expand All": "展开全部"
|
||||
}
|
||||
|
@ -869,5 +869,6 @@
|
||||
"License": "許可證",
|
||||
"This variable has been deprecated and can be replaced with \"Current form\"": "該變數已被棄用,可以使用“當前表單”作為替代",
|
||||
"The value of this variable is derived from the query string of the page URL. This variable can only be used normally when the page has a query string.": "該變數的值來自頁面 URL 的查詢字符串,只有當頁面有查詢字符串時,該變數才能正常使用。",
|
||||
"URL search params": "URL 查詢參數"
|
||||
"URL search params": "URL 查詢參數",
|
||||
"Expand All": "展開全部"
|
||||
}
|
||||
|
@ -7,29 +7,23 @@
|
||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||
*/
|
||||
|
||||
import { ArrayItems } from '@formily/antd-v5';
|
||||
import { ISchema } from '@formily/json-schema';
|
||||
import { useField, useFieldSchema } from '@formily/react';
|
||||
import { useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useAPIClient } from '../../../../api-client';
|
||||
import { SchemaSettings } from '../../../../application/schema-settings/SchemaSettings';
|
||||
import { useTableBlockContext } from '../../../../block-provider';
|
||||
import { useFormBlockContext } from '../../../../block-provider/FormBlockProvider';
|
||||
import {
|
||||
useCollectionManager_deprecated,
|
||||
useCollection_deprecated,
|
||||
useSortFields,
|
||||
} from '../../../../collection-manager';
|
||||
import { useCollectionManager_deprecated, useCollection_deprecated } from '../../../../collection-manager';
|
||||
import { FilterBlockType } from '../../../../filter-provider/utils';
|
||||
import { removeNullCondition, useDesignable } from '../../../../schema-component';
|
||||
import { useDesignable } from '../../../../schema-component';
|
||||
import { SchemaSettingsBlockHeightItem } from '../../../../schema-settings/SchemaSettingsBlockHeightItem';
|
||||
import { SchemaSettingsBlockTitleItem } from '../../../../schema-settings/SchemaSettingsBlockTitleItem';
|
||||
import { SchemaSettingsConnectDataBlocks } from '../../../../schema-settings/SchemaSettingsConnectDataBlocks';
|
||||
import { SchemaSettingsDataScope } from '../../../../schema-settings/SchemaSettingsDataScope';
|
||||
import { SchemaSettingsSortField } from '../../../../schema-settings/SchemaSettingsSortField';
|
||||
import { SchemaSettingsTemplate } from '../../../../schema-settings/SchemaSettingsTemplate';
|
||||
import { setDataLoadingModeSettingsItem } from '../details-multi/setDataLoadingModeSettingsItem';
|
||||
import { setDefaultSortingRulesSchemaSettingsItem } from '../../../../schema-settings/setDefaultSortingRulesSchemaSettingsItem';
|
||||
import { setTheDataScopeSchemaSettingsItem } from '../../../../schema-settings/setTheDataScopeSchemaSettingsItem';
|
||||
import { createSwitchSettingsItem } from '../../../../application/schema-settings/utils';
|
||||
|
||||
export const tableBlockSettings = new SchemaSettings({
|
||||
name: 'blockSettings:table',
|
||||
@ -66,6 +60,11 @@ export const tableBlockSettings = new SchemaSettings({
|
||||
onChange: (flag) => {
|
||||
field.decoratorProps.treeTable = flag;
|
||||
fieldSchema['x-decorator-props'].treeTable = flag;
|
||||
|
||||
if (flag === false) {
|
||||
fieldSchema['x-decorator-props'].expandFlag = false;
|
||||
}
|
||||
|
||||
const params = {
|
||||
...service.params?.[0],
|
||||
tree: flag ? true : null,
|
||||
@ -88,6 +87,16 @@ export const tableBlockSettings = new SchemaSettings({
|
||||
return collection?.tree && collectionField?.collectionName === collectionField?.target;
|
||||
},
|
||||
},
|
||||
createSwitchSettingsItem({
|
||||
name: 'expandFlag',
|
||||
title: (t) => t('Expand All'),
|
||||
defaultValue: false,
|
||||
schemaKey: 'x-decorator-props.expandFlag',
|
||||
useVisible() {
|
||||
const field = useField();
|
||||
return field.decoratorProps.treeTable;
|
||||
},
|
||||
}),
|
||||
{
|
||||
name: 'enableDragAndDropSorting',
|
||||
type: 'switch',
|
||||
@ -137,165 +146,8 @@ export const tableBlockSettings = new SchemaSettings({
|
||||
return field.decoratorProps.dragSort;
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'SetTheDataScope',
|
||||
Component: SchemaSettingsDataScope,
|
||||
useComponentProps: () => {
|
||||
const { name } = useCollection_deprecated();
|
||||
const field = useField();
|
||||
const fieldSchema = useFieldSchema();
|
||||
const { form } = useFormBlockContext();
|
||||
const { service } = useTableBlockContext();
|
||||
const { dn } = useDesignable();
|
||||
const onDataScopeSubmit = useCallback(
|
||||
({ filter }) => {
|
||||
filter = removeNullCondition(filter);
|
||||
const params = field.decoratorProps.params || {};
|
||||
params.filter = filter;
|
||||
field.decoratorProps.params = params;
|
||||
fieldSchema['x-decorator-props']['params'] = params;
|
||||
|
||||
dn.emit('patch', {
|
||||
schema: {
|
||||
['x-uid']: fieldSchema['x-uid'],
|
||||
'x-decorator-props': fieldSchema['x-decorator-props'],
|
||||
},
|
||||
});
|
||||
service.params[0].page = 1;
|
||||
},
|
||||
[dn, field.decoratorProps, fieldSchema, service],
|
||||
);
|
||||
|
||||
return {
|
||||
collectionName: name,
|
||||
defaultFilter: fieldSchema?.['x-decorator-props']?.params?.filter || {},
|
||||
form: form,
|
||||
onSubmit: onDataScopeSubmit,
|
||||
};
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'SetDefaultSortingRules',
|
||||
type: 'modal',
|
||||
useComponentProps() {
|
||||
const { name } = useCollection_deprecated();
|
||||
const field = useField();
|
||||
const fieldSchema = useFieldSchema();
|
||||
const sortFields = useSortFields(name);
|
||||
const { service } = useTableBlockContext();
|
||||
const { t } = useTranslation();
|
||||
const { dn } = useDesignable();
|
||||
const defaultSort = fieldSchema?.['x-decorator-props']?.params?.sort || [];
|
||||
const sort = defaultSort?.map((item: string) => {
|
||||
return item?.startsWith('-')
|
||||
? {
|
||||
field: item.substring(1),
|
||||
direction: 'desc',
|
||||
}
|
||||
: {
|
||||
field: item,
|
||||
direction: 'asc',
|
||||
};
|
||||
});
|
||||
|
||||
return {
|
||||
title: t('Set default sorting rules'),
|
||||
components: { ArrayItems },
|
||||
schema: {
|
||||
type: 'object',
|
||||
title: t('Set default sorting rules'),
|
||||
properties: {
|
||||
sort: {
|
||||
type: 'array',
|
||||
default: sort,
|
||||
'x-component': 'ArrayItems',
|
||||
'x-decorator': 'FormItem',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
space: {
|
||||
type: 'void',
|
||||
'x-component': 'Space',
|
||||
properties: {
|
||||
sort: {
|
||||
type: 'void',
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'ArrayItems.SortHandle',
|
||||
},
|
||||
field: {
|
||||
type: 'string',
|
||||
enum: sortFields,
|
||||
required: true,
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'Select',
|
||||
'x-component-props': {
|
||||
style: {
|
||||
width: 260,
|
||||
},
|
||||
},
|
||||
},
|
||||
direction: {
|
||||
type: 'string',
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'Radio.Group',
|
||||
'x-component-props': {
|
||||
optionType: 'button',
|
||||
},
|
||||
enum: [
|
||||
{
|
||||
label: t('ASC'),
|
||||
value: 'asc',
|
||||
},
|
||||
{
|
||||
label: t('DESC'),
|
||||
value: 'desc',
|
||||
},
|
||||
],
|
||||
},
|
||||
remove: {
|
||||
type: 'void',
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'ArrayItems.Remove',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
properties: {
|
||||
add: {
|
||||
type: 'void',
|
||||
title: t('Add sort field'),
|
||||
'x-component': 'ArrayItems.Addition',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
} as ISchema,
|
||||
onSubmit: ({ sort }) => {
|
||||
const sortArr = sort.map((item) => {
|
||||
return item.direction === 'desc' ? `-${item.field}` : item.field;
|
||||
});
|
||||
const params = field.decoratorProps.params || {};
|
||||
params.sort = sortArr;
|
||||
field.decoratorProps.params = params;
|
||||
fieldSchema['x-decorator-props']['params'] = params;
|
||||
dn.emit('patch', {
|
||||
schema: {
|
||||
['x-uid']: fieldSchema['x-uid'],
|
||||
'x-decorator-props': fieldSchema['x-decorator-props'],
|
||||
},
|
||||
});
|
||||
service.run({ ...service.params?.[0], sort: sortArr });
|
||||
},
|
||||
};
|
||||
},
|
||||
useVisible() {
|
||||
const field = useField();
|
||||
const { dragSort } = field.decoratorProps;
|
||||
|
||||
return !dragSort;
|
||||
},
|
||||
},
|
||||
setTheDataScopeSchemaSettingsItem,
|
||||
setDefaultSortingRulesSchemaSettingsItem,
|
||||
setDataLoadingModeSettingsItem,
|
||||
{
|
||||
name: 'RecordsPerPage',
|
||||
|
@ -17,7 +17,7 @@ import { BlockItemCard } from '../block-item/BlockItemCard';
|
||||
import { BlockItemError } from '../block-item/BlockItemError';
|
||||
import useStyles from './style';
|
||||
|
||||
interface CardItemProps extends CardProps {
|
||||
export interface CardItemProps extends CardProps {
|
||||
name?: string;
|
||||
children?: React.ReactNode;
|
||||
/**
|
||||
@ -27,6 +27,7 @@ interface CardItemProps extends CardProps {
|
||||
*/
|
||||
lazyRender?: IntersectionOptions & { element?: React.JSX.Element };
|
||||
heightMode?: string;
|
||||
height?: number;
|
||||
}
|
||||
|
||||
export const CardItem: FC<CardItemProps> = (props) => {
|
||||
|
@ -455,7 +455,7 @@ export const Table: any = withDynamicSchemaProps(
|
||||
if (!_.isEqual(newExpandesKeys, expandedKeys)) {
|
||||
setExpandesKeys(newExpandesKeys);
|
||||
}
|
||||
}, [expandFlag]);
|
||||
}, [expandFlag, allIncludesChildren]);
|
||||
|
||||
/**
|
||||
* 为没有设置 key 属性的表格行生成一个唯一的 key
|
||||
|
@ -0,0 +1,86 @@
|
||||
import React from 'react';
|
||||
import { TableBlockProvider, useTableBlockProps, SchemaComponent, Plugin, ISchema } from '@nocobase/client';
|
||||
import { mockApp } from '@nocobase/client/demo-utils';
|
||||
|
||||
const schema: ISchema = {
|
||||
type: 'void',
|
||||
name: 'root',
|
||||
properties: {
|
||||
test: {
|
||||
type: 'void',
|
||||
'x-decorator': 'TableBlockProvider',
|
||||
'x-decorator-props': {
|
||||
collection: 'tree-collection',
|
||||
action: 'list',
|
||||
params: {
|
||||
tree: true,
|
||||
pageSize: 2,
|
||||
},
|
||||
treeTable: true,
|
||||
showIndex: true,
|
||||
expandFlag: true,
|
||||
dragSort: false,
|
||||
},
|
||||
properties: {
|
||||
table: {
|
||||
type: 'array',
|
||||
'x-component': 'TableV2',
|
||||
'x-use-component-props': 'useTableBlockProps', // 自动注入 Table 所需的 props
|
||||
'x-component-props': {
|
||||
rowKey: 'id',
|
||||
rowSelection: {
|
||||
type: 'checkbox',
|
||||
},
|
||||
},
|
||||
properties: {
|
||||
column1: {
|
||||
type: 'void',
|
||||
title: 'Role UID',
|
||||
'x-component': 'TableV2.Column',
|
||||
properties: {
|
||||
name: {
|
||||
type: 'string',
|
||||
'x-component': 'CollectionField',
|
||||
'x-pattern': 'readPretty', // 这里要设置为 true
|
||||
},
|
||||
},
|
||||
},
|
||||
column2: {
|
||||
type: 'void',
|
||||
title: 'Role name',
|
||||
'x-component': 'TableV2.Column',
|
||||
properties: {
|
||||
title: {
|
||||
type: 'string',
|
||||
'x-component': 'CollectionField',
|
||||
'x-pattern': 'readPretty',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
const Demo = () => {
|
||||
return <SchemaComponent schema={schema} />;
|
||||
};
|
||||
|
||||
class DemoPlugin extends Plugin {
|
||||
async load() {
|
||||
this.app.router.add('root', { path: '/', Component: Demo });
|
||||
}
|
||||
}
|
||||
|
||||
const app = mockApp({
|
||||
plugins: [DemoPlugin],
|
||||
components: {
|
||||
TableBlockProvider,
|
||||
},
|
||||
scopes: {
|
||||
useTableBlockProps,
|
||||
},
|
||||
});
|
||||
|
||||
export default app.getRootComponent();
|
@ -0,0 +1,85 @@
|
||||
import React from 'react';
|
||||
import { TableBlockProvider, useTableBlockProps, SchemaComponent, Plugin, ISchema } from '@nocobase/client';
|
||||
import { mockApp } from '@nocobase/client/demo-utils';
|
||||
|
||||
const schema: ISchema = {
|
||||
type: 'void',
|
||||
name: 'root',
|
||||
properties: {
|
||||
test: {
|
||||
type: 'void',
|
||||
'x-decorator': 'TableBlockProvider',
|
||||
'x-decorator-props': {
|
||||
collection: 'tree-collection',
|
||||
action: 'list',
|
||||
params: {
|
||||
tree: true,
|
||||
pageSize: 2,
|
||||
},
|
||||
treeTable: true,
|
||||
showIndex: true,
|
||||
dragSort: false,
|
||||
},
|
||||
properties: {
|
||||
table: {
|
||||
type: 'array',
|
||||
'x-component': 'TableV2',
|
||||
'x-use-component-props': 'useTableBlockProps', // 自动注入 Table 所需的 props
|
||||
'x-component-props': {
|
||||
rowKey: 'id',
|
||||
rowSelection: {
|
||||
type: 'checkbox',
|
||||
},
|
||||
},
|
||||
properties: {
|
||||
column1: {
|
||||
type: 'void',
|
||||
title: 'Role UID',
|
||||
'x-component': 'TableV2.Column',
|
||||
properties: {
|
||||
name: {
|
||||
type: 'string',
|
||||
'x-component': 'CollectionField',
|
||||
'x-pattern': 'readPretty', // 这里要设置为 true
|
||||
},
|
||||
},
|
||||
},
|
||||
column2: {
|
||||
type: 'void',
|
||||
title: 'Role name',
|
||||
'x-component': 'TableV2.Column',
|
||||
properties: {
|
||||
title: {
|
||||
type: 'string',
|
||||
'x-component': 'CollectionField',
|
||||
'x-pattern': 'readPretty',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
const Demo = () => {
|
||||
return <SchemaComponent schema={schema} />;
|
||||
};
|
||||
|
||||
class DemoPlugin extends Plugin {
|
||||
async load() {
|
||||
this.app.router.add('root', { path: '/', Component: Demo });
|
||||
}
|
||||
}
|
||||
|
||||
const app = mockApp({
|
||||
plugins: [DemoPlugin],
|
||||
components: {
|
||||
TableBlockProvider,
|
||||
},
|
||||
scopes: {
|
||||
useTableBlockProps,
|
||||
},
|
||||
});
|
||||
|
||||
export default app.getRootComponent();
|
@ -40,3 +40,31 @@ Table 会将当前行的数据通过 [CollectionRecordProvider](/core/data-block
|
||||
在编辑场景下,在通过 [useDataBlockResource()](/core/data-block/data-block-resource-provider) 和 [useDataBlockRequest()](/core/data-block/data-block-request-provider#usedatablockrequest) 对数据进行更新,以及对列表进行刷新。
|
||||
|
||||
<code src="./demos/new-demos/record.tsx"></code>
|
||||
|
||||
## Tree
|
||||
|
||||
### Basic
|
||||
|
||||
树表需要设置:
|
||||
|
||||
```diff
|
||||
'x-decorator-props': {
|
||||
collection: 'tree-collection',
|
||||
action: 'list',
|
||||
params: {
|
||||
+ tree: true,
|
||||
pageSize: 2,
|
||||
},
|
||||
+ treeTable: true,
|
||||
showIndex: true,
|
||||
dragSort: false,
|
||||
},
|
||||
```
|
||||
|
||||
<code src="./demos/new-demos/tree.tsx"></code>
|
||||
|
||||
### expandFlag
|
||||
|
||||
默认展开树表的节点,需要设置 `expandFlag` 属性。
|
||||
|
||||
<code src="./demos/new-demos/tree-expandFlag.tsx"></code>
|
||||
|
@ -108,16 +108,30 @@ const useTableHeight = () => {
|
||||
};
|
||||
|
||||
// 常规数据区块高度计算
|
||||
export const useDataBlockHeight = () => {
|
||||
interface UseDataBlockHeightOptions {
|
||||
removeBlockHeaderHeight?: boolean;
|
||||
innerExtraHeight?: number;
|
||||
}
|
||||
export const useDataBlockHeight = (options?: UseDataBlockHeightOptions) => {
|
||||
const { heightProps } = useBlockHeightProps();
|
||||
const pageFullScreenHeight = useFullScreenHeight();
|
||||
const { heightMode, height } = heightProps || {};
|
||||
const { token } = theme.useToken();
|
||||
|
||||
const { heightMode, height, title } = heightProps || {};
|
||||
const blockHeaderHeight = title ? token.fontSizeLG * token.lineHeightLG + token.padding * 2 - 1 : 0;
|
||||
|
||||
if (!heightProps?.heightMode || heightMode === HeightMode.DEFAULT) {
|
||||
return;
|
||||
}
|
||||
if (heightMode === HeightMode.FULL_HEIGHT) {
|
||||
return window.innerHeight - pageFullScreenHeight;
|
||||
let res = window.innerHeight - pageFullScreenHeight;
|
||||
if (options?.removeBlockHeaderHeight) {
|
||||
res = res - blockHeaderHeight;
|
||||
}
|
||||
if (options?.innerExtraHeight) {
|
||||
res = res - options.innerExtraHeight;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
return height;
|
||||
};
|
||||
|
@ -9,7 +9,7 @@
|
||||
|
||||
import Icon, { TableOutlined } from '@ant-design/icons';
|
||||
import { Divider, Empty, Input, MenuProps, Spin } from 'antd';
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import React, { FC, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import {
|
||||
SchemaInitializerItem,
|
||||
@ -305,9 +305,10 @@ export interface DataBlockInitializerProps {
|
||||
currentText?: string;
|
||||
/** 用于更改 Other records 的文案 */
|
||||
otherText?: string;
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
|
||||
export const DataBlockInitializer = (props: DataBlockInitializerProps) => {
|
||||
export const DataBlockInitializer: FC<DataBlockInitializerProps> = (props) => {
|
||||
const {
|
||||
templateWrap,
|
||||
onCreateBlockSchema,
|
||||
|
@ -151,6 +151,7 @@ export const SchemaSettingsProvider: React.FC<SchemaSettingsProviderProps> = (pr
|
||||
|
||||
export const SchemaSettingsDropdown: React.FC<SchemaSettingsProps> = (props) => {
|
||||
const { title, dn, ...others } = props;
|
||||
const app = useApp();
|
||||
const [visible, setVisible] = useState(false);
|
||||
const { Component, getMenuItems } = useMenuItem();
|
||||
const [, startTransition] = useReactTransition();
|
||||
|
@ -24,8 +24,8 @@ import { useBlockTemplateContext } from '../schema-templates/BlockTemplate';
|
||||
import { SchemaSettingsItem, useSchemaSettings } from './SchemaSettings';
|
||||
|
||||
export function SchemaSettingsTemplate(props) {
|
||||
const { componentName, collectionName, resourceName, needRender } = props;
|
||||
const { t } = useTranslation();
|
||||
const { componentName, collectionName, resourceName, needRender, useTranslationHooks = useTranslation } = props;
|
||||
const { t } = useTranslationHooks();
|
||||
const { getCollection } = useCollectionManager_deprecated();
|
||||
const { dn, setVisible, template, fieldSchema } = useSchemaSettings();
|
||||
const compile = useCompile();
|
||||
|
@ -20,6 +20,8 @@ export * from './SchemaSettingsNumberFormat';
|
||||
export * from './SchemaSettingsSortingRule';
|
||||
export * from './SchemaSettingsTemplate';
|
||||
export * from './SchemaSettingsBlockHeightItem';
|
||||
export * from './setDefaultSortingRulesSchemaSettingsItem';
|
||||
export * from './setTheDataScopeSchemaSettingsItem';
|
||||
export * from './hooks/useGetAriaLabelOfDesigner';
|
||||
export * from './hooks/useIsAllowToSetDefaultValue';
|
||||
export { default as useParseDataScopeFilter } from './hooks/useParseDataScopeFilter';
|
||||
|
@ -0,0 +1,140 @@
|
||||
/**
|
||||
* This file is part of the NocoBase (R) project.
|
||||
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
||||
* Authors: NocoBase Team.
|
||||
*
|
||||
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||
*/
|
||||
|
||||
import { ArrayItems } from '@formily/antd-v5';
|
||||
import { ISchema } from '@formily/json-schema';
|
||||
import { useField, useFieldSchema } from '@formily/react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useTableBlockContext } from '../block-provider';
|
||||
import { useCollection_deprecated, useSortFields } from '../collection-manager';
|
||||
import { useDesignable } from '../schema-component';
|
||||
import { SchemaSettingsItemType } from '../application';
|
||||
|
||||
export const setDefaultSortingRulesSchemaSettingsItem: SchemaSettingsItemType = {
|
||||
name: 'SetDefaultSortingRules',
|
||||
type: 'modal',
|
||||
useComponentProps() {
|
||||
const { name } = useCollection_deprecated();
|
||||
const field = useField();
|
||||
const fieldSchema = useFieldSchema();
|
||||
const sortFields = useSortFields(name);
|
||||
const { service } = useTableBlockContext();
|
||||
const { t } = useTranslation();
|
||||
const { dn } = useDesignable();
|
||||
const defaultSort = fieldSchema?.['x-decorator-props']?.params?.sort || [];
|
||||
const sort = defaultSort?.map((item: string) => {
|
||||
return item?.startsWith('-')
|
||||
? {
|
||||
field: item.substring(1),
|
||||
direction: 'desc',
|
||||
}
|
||||
: {
|
||||
field: item,
|
||||
direction: 'asc',
|
||||
};
|
||||
});
|
||||
|
||||
return {
|
||||
title: t('Set default sorting rules'),
|
||||
components: { ArrayItems },
|
||||
schema: {
|
||||
type: 'object',
|
||||
title: t('Set default sorting rules'),
|
||||
properties: {
|
||||
sort: {
|
||||
type: 'array',
|
||||
default: sort,
|
||||
'x-component': 'ArrayItems',
|
||||
'x-decorator': 'FormItem',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
space: {
|
||||
type: 'void',
|
||||
'x-component': 'Space',
|
||||
properties: {
|
||||
sort: {
|
||||
type: 'void',
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'ArrayItems.SortHandle',
|
||||
},
|
||||
field: {
|
||||
type: 'string',
|
||||
enum: sortFields,
|
||||
required: true,
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'Select',
|
||||
'x-component-props': {
|
||||
style: {
|
||||
width: 260,
|
||||
},
|
||||
},
|
||||
},
|
||||
direction: {
|
||||
type: 'string',
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'Radio.Group',
|
||||
'x-component-props': {
|
||||
optionType: 'button',
|
||||
},
|
||||
enum: [
|
||||
{
|
||||
label: t('ASC'),
|
||||
value: 'asc',
|
||||
},
|
||||
{
|
||||
label: t('DESC'),
|
||||
value: 'desc',
|
||||
},
|
||||
],
|
||||
},
|
||||
remove: {
|
||||
type: 'void',
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'ArrayItems.Remove',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
properties: {
|
||||
add: {
|
||||
type: 'void',
|
||||
title: t('Add sort field'),
|
||||
'x-component': 'ArrayItems.Addition',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
} as ISchema,
|
||||
onSubmit: ({ sort }) => {
|
||||
const sortArr = sort.map((item) => {
|
||||
return item.direction === 'desc' ? `-${item.field}` : item.field;
|
||||
});
|
||||
const params = field.decoratorProps.params || {};
|
||||
params.sort = sortArr;
|
||||
field.decoratorProps.params = params;
|
||||
fieldSchema['x-decorator-props']['params'] = params;
|
||||
dn.emit('patch', {
|
||||
schema: {
|
||||
['x-uid']: fieldSchema['x-uid'],
|
||||
'x-decorator-props': fieldSchema['x-decorator-props'],
|
||||
},
|
||||
});
|
||||
service.run({ ...service.params?.[0], sort: sortArr });
|
||||
},
|
||||
};
|
||||
},
|
||||
useVisible() {
|
||||
const field = useField();
|
||||
const { dragSort } = field.decoratorProps;
|
||||
|
||||
return !dragSort;
|
||||
},
|
||||
};
|
@ -0,0 +1,54 @@
|
||||
/**
|
||||
* This file is part of the NocoBase (R) project.
|
||||
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
||||
* Authors: NocoBase Team.
|
||||
*
|
||||
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||
*/
|
||||
|
||||
import { useField, useFieldSchema } from '@formily/react';
|
||||
import { useCallback } from 'react';
|
||||
import { SchemaSettingsItemType } from '../application';
|
||||
import { useFormBlockContext, useTableBlockContext } from '../block-provider';
|
||||
import { useCollection_deprecated } from '../collection-manager';
|
||||
import { useDesignable, removeNullCondition } from '../schema-component';
|
||||
import { SchemaSettingsDataScope } from './SchemaSettingsDataScope';
|
||||
|
||||
export const setTheDataScopeSchemaSettingsItem: SchemaSettingsItemType = {
|
||||
name: 'SetTheDataScope',
|
||||
Component: SchemaSettingsDataScope,
|
||||
useComponentProps: () => {
|
||||
const { name } = useCollection_deprecated();
|
||||
const field = useField();
|
||||
const fieldSchema = useFieldSchema();
|
||||
const { form } = useFormBlockContext();
|
||||
const { service } = useTableBlockContext();
|
||||
const { dn } = useDesignable();
|
||||
const onDataScopeSubmit = useCallback(
|
||||
({ filter }) => {
|
||||
filter = removeNullCondition(filter);
|
||||
const params = field.decoratorProps.params || {};
|
||||
params.filter = filter;
|
||||
field.decoratorProps.params = params;
|
||||
fieldSchema['x-decorator-props']['params'] = params;
|
||||
|
||||
dn.emit('patch', {
|
||||
schema: {
|
||||
['x-uid']: fieldSchema['x-uid'],
|
||||
'x-decorator-props': fieldSchema['x-decorator-props'],
|
||||
},
|
||||
});
|
||||
service.params[0].page = 1;
|
||||
},
|
||||
[dn, field.decoratorProps, fieldSchema, service],
|
||||
);
|
||||
|
||||
return {
|
||||
collectionName: name,
|
||||
defaultFilter: fieldSchema?.['x-decorator-props']?.params?.filter || {},
|
||||
form: form,
|
||||
onSubmit: onDataScopeSubmit,
|
||||
};
|
||||
},
|
||||
};
|
@ -1,3 +1,12 @@
|
||||
/**
|
||||
* This file is part of the NocoBase (R) project.
|
||||
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
||||
* Authors: NocoBase Team.
|
||||
*
|
||||
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||
*/
|
||||
|
||||
import { Plugin } from '@nocobase/client';
|
||||
|
||||
export class PluginCollectionSqlClient extends Plugin {
|
||||
@ -9,7 +18,6 @@ export class PluginCollectionSqlClient extends Plugin {
|
||||
|
||||
// You can get and modify the app instance here
|
||||
async load() {
|
||||
console.log(this.app);
|
||||
// this.app.addComponents({})
|
||||
// this.app.addScopes({})
|
||||
// this.app.addProvider()
|
||||
|
Loading…
Reference in New Issue
Block a user