diff --git a/packages/app/src/pages/index.tsx b/packages/app/src/pages/index.tsx
index a72eb11b8..6507e8644 100644
--- a/packages/app/src/pages/index.tsx
+++ b/packages/app/src/pages/index.tsx
@@ -5,6 +5,7 @@ import {
AntdSchemaComponentProvider,
APIClientProvider,
AuthLayout,
+ BlockSchemaComponentProvider,
BlockTemplateDetails,
BlockTemplatePage,
ChinaRegionProvider,
@@ -95,6 +96,7 @@ const providers = [
},
},
],
+ BlockSchemaComponentProvider,
AntdSchemaComponentProvider,
ChinaRegionProvider,
WorkflowRouteProvider,
diff --git a/packages/client/src/acl/ACLShortcut.tsx b/packages/client/src/acl/ACLShortcut.tsx
index 58cc24c55..25d795ec5 100644
--- a/packages/client/src/acl/ACLShortcut.tsx
+++ b/packages/client/src/acl/ACLShortcut.tsx
@@ -1,25 +1,12 @@
import { LockOutlined } from '@ant-design/icons';
-import { ISchema, useForm } from '@formily/react';
+import { ISchema } from '@formily/react';
import { uid } from '@formily/shared';
import React, { useState } from 'react';
import { useTranslation } from 'react-i18next';
import { PluginManager } from '../plugin-manager';
-import { ActionContext, SchemaComponent, useActionContext } from '../schema-component';
+import { ActionContext, SchemaComponent } from '../schema-component';
import * as components from './Configuration';
-const useCloseAction = () => {
- const { setVisible } = useActionContext();
- const form = useForm();
- return {
- async run() {
- setVisible(false);
- form.submit((values) => {
- console.log(values);
- });
- },
- };
-};
-
const schema: ISchema = {
type: 'object',
properties: {
@@ -49,7 +36,7 @@ export const ACLShortcut = () => {
setVisible(true);
}}
/>
-
+
);
};
diff --git a/packages/client/src/acl/RolePermissionManager.tsx b/packages/client/src/acl/RolePermissionManager.tsx
deleted file mode 100644
index b7b7c15d4..000000000
--- a/packages/client/src/acl/RolePermissionManager.tsx
+++ /dev/null
@@ -1,233 +0,0 @@
-import { Button, Checkbox, Divider, Drawer, Space, Table, Tabs, Tag, Typography } from 'antd';
-import React, { useState } from 'react';
-import { ScopeRecordPicker } from './ScopeRecordPicker';
-
-export function RoleManager() {}
-
-export const RoleTable = () => {
- return (
-
-
-
-
-
-
value && '是',
- },
- {
- title: '操作',
- dataIndex: 'actions',
- key: 'actions',
- render: () => (
- }>
-
- Edit role
- Delete
-
- ),
- },
- ]}
- dataSource={[
- {
- name: 'root',
- title: '管理员',
- description: '描述',
- },
- {
- name: 'member',
- title: '普通成员',
- description: '描述',
- default: true,
- },
- ]}
- />
-
- );
-};
-
-const ConfigurePermissions = () => {
- const [visible, setVisible] = useState(false);
- return (
- <>
- setVisible(true)}>Configure
- setVisible(false)}>
-
-
- >
- );
-};
-
-export const RolePermissions = () => {
- return (
-
-
-
- (value ? 单独配置 : 通用配置),
- },
- {
- title: '操作',
- dataIndex: 'actions',
- key: 'actions',
- render: () => ,
- },
- ]}
- dataSource={[
- {
- title: '用户',
- name: 'users',
- },
- ]}
- />
-
-
-
- 允许访问
- >
- ),
- dataIndex: 'accessible',
- key: 'accessible',
- render: () => ,
- },
- ]}
- dataSource={[
- {
- title: '页面1',
- },
- {
- title: '页面2',
- accessible: true,
- },
- ]}
- />
-
-
- );
-};
-
-const ResourceActionsConfigure = () => {
- const [visible, setVisible] = useState(false);
- return (
- <>
- setVisible(true)}>Configure
- setVisible(false)}
- footer={
-
-
-
-
- }
- >
-
-
- >
- );
-};
-
-export const ResourceActionsForm = () => {
- return (
-
-
- 允许操作
- >
- ),
- dataIndex: 'enable',
- key: 'enable',
- render: () => ,
- },
- {
- title: '可操作的数据范围',
- dataIndex: 'scope',
- key: 'scope',
- render: () => ,
- },
- ]}
- dataSource={[
- {
- title: '添加',
- },
- {
- title: '导入',
- enable: true,
- },
- ]}
- />
-
- );
-};
diff --git a/packages/client/src/acl/ScopeRecordPicker.tsx b/packages/client/src/acl/ScopeRecordPicker.tsx
deleted file mode 100644
index 41c3571c0..000000000
--- a/packages/client/src/acl/ScopeRecordPicker.tsx
+++ /dev/null
@@ -1,77 +0,0 @@
-import { createForm } from '@formily/core';
-import { FormContext, ISchema } from '@formily/react';
-import { Button, Space } from 'antd';
-import React, { useMemo } from 'react';
-import { SchemaComponent } from '../';
-import { AntdSchemaComponentProvider } from '../schema-component';
-
-const schema: ISchema = {
- type: 'object',
- properties: {
- scope: {
- type: 'array',
- default: [
- { id: 1, name: 'name1' },
- { id: 2, name: 'name2' },
- ],
- 'x-component': 'RecordPicker',
- properties: {
- actions: {
- 'x-component': 'ScopeActions',
- },
- rowSelection: {
- 'x-component': 'RowSelection',
- 'x-component-props': {
- rowKey: 'id',
- objectValue: true,
- rowSelection: {
- type: 'radio',
- },
- dataSource: [
- { id: 1, title: '全部数据' },
- { id: 2, title: '用户自己创建的数据' },
- { id: 3, title: '待审核的文章' },
- ],
- },
- properties: {
- column1: {
- type: 'void',
- title: '数据范围',
- 'x-component': 'RowSelection.Column',
- 'x-read-pretty': true,
- properties: {
- title: {
- type: 'string',
- 'x-component': 'Input',
- 'x-read-pretty': true,
- },
- },
- },
- },
- },
- },
- },
- },
-};
-
-const ScopeActions = () => {
- return (
-
-
-
-
- );
-};
-
-export const ScopeRecordPicker = () => {
- const form = useMemo(() => createForm({}), []);
- return (
-
-
-
-
-
- );
-};
diff --git a/packages/client/src/acl/demos/demo1.tsx b/packages/client/src/acl/demos/demo1.tsx
deleted file mode 100644
index e48a39da3..000000000
--- a/packages/client/src/acl/demos/demo1.tsx
+++ /dev/null
@@ -1,6 +0,0 @@
-import { RoleTable } from '@nocobase/client';
-import React from 'react';
-
-export default () => {
- return ;
-};
diff --git a/packages/client/src/acl/demos/demo2.tsx b/packages/client/src/acl/demos/demo2.tsx
deleted file mode 100644
index 5571f3838..000000000
--- a/packages/client/src/acl/demos/demo2.tsx
+++ /dev/null
@@ -1,6 +0,0 @@
-import { RolePermissions } from '@nocobase/client';
-import React from 'react';
-
-export default () => {
- return ;
-};
diff --git a/packages/client/src/acl/demos/demo3.tsx b/packages/client/src/acl/demos/demo3.tsx
deleted file mode 100644
index 3e0778be5..000000000
--- a/packages/client/src/acl/demos/demo3.tsx
+++ /dev/null
@@ -1,6 +0,0 @@
-import { ResourceActionsForm } from '@nocobase/client';
-import React from 'react';
-
-export default () => {
- return ;
-};
diff --git a/packages/client/src/acl/index.md b/packages/client/src/acl/index.md
index 4a8bb558f..fa0ae96ff 100644
--- a/packages/client/src/acl/index.md
+++ b/packages/client/src/acl/index.md
@@ -7,10 +7,6 @@ group:
path: /client
---
-# ACL 待定
+# ACL
-访问控制列表,plugin-acl 的前端模块
-
-
-
-
+访问控制列表,plugin-acl 的前端模块,详细文档待补充。
diff --git a/packages/client/src/acl/index.tsx b/packages/client/src/acl/index.tsx
index eb4cb16c4..7a540754a 100644
--- a/packages/client/src/acl/index.tsx
+++ b/packages/client/src/acl/index.tsx
@@ -1,3 +1,3 @@
export * from './ACLProvider';
-export * from './RolePermissionManager';
export * from './ACLShortcut';
+
diff --git a/packages/client/src/action-logs/ActionLogProvider.tsx b/packages/client/src/action-logs/ActionLogProvider.tsx
index 544f5baa0..7ba24a051 100644
--- a/packages/client/src/action-logs/ActionLogProvider.tsx
+++ b/packages/client/src/action-logs/ActionLogProvider.tsx
@@ -1,19 +1,20 @@
-import React from 'react';
-import { SchemaInitializerProvider } from '../schema-initializer';
-import { initializes } from '../schema-initializer/Initializers';
+import React, { useContext } from 'react';
+import { SchemaInitializerContext, SchemaInitializerProvider } from '../schema-initializer';
+
+const actionLogsItemGroup = {
+ type: 'itemGroup',
+ title: '{{t("Others")}}',
+ children: [
+ {
+ type: 'item',
+ title: '{{t("Action logs")}}',
+ component: 'ActionLogBlockInitializer',
+ },
+ ],
+};
export const ActionLogProvider = (props: any) => {
- const actionLogsItemGroup = {
- type: 'itemGroup',
- title: '{{t("Others")}}',
- children: [
- {
- type: 'item',
- title: '{{t("Action logs")}}',
- component: 'ActionLogBlockInitializer',
- },
- ],
- };
+ const initializes = useContext(SchemaInitializerContext);
initializes.BlockInitializers.items.push({ ...actionLogsItemGroup });
return {props.children};
};
diff --git a/packages/client/src/application/index.md b/packages/client/src/application/index.md
index b60e014ff..40131982e 100644
--- a/packages/client/src/application/index.md
+++ b/packages/client/src/application/index.md
@@ -12,5 +12,3 @@ group:
## compose
-
-
diff --git a/packages/client/src/block-provider/BlockProvider.tsx b/packages/client/src/block-provider/BlockProvider.tsx
new file mode 100644
index 000000000..24ffbd9f9
--- /dev/null
+++ b/packages/client/src/block-provider/BlockProvider.tsx
@@ -0,0 +1,177 @@
+import { Field } from '@formily/core';
+import { useField } from '@formily/react';
+import { useRequest } from 'ahooks';
+import React, { createContext, useContext } from 'react';
+import { TableFieldResource, useAPIClient, useRecord } from '../';
+import { CollectionProvider, useCollection, useCollectionManager } from '../collection-manager';
+import { useRecordIndex } from '../record-provider';
+
+export const BlockResourceContext = createContext(null);
+export const BlockAssociationContext = createContext(null);
+const BlockRequestContext = createContext(null);
+
+export const useBlockResource = () => {
+ return useContext(BlockResourceContext);
+};
+
+interface UseReousrceProps {
+ resource: any;
+ association?: any;
+ useSourceId?: any;
+ block?: any;
+}
+
+const useAssociation = (props) => {
+ const { association } = props;
+ const { getCollectionField } = useCollectionManager();
+ if (typeof association === 'string') {
+ return getCollectionField(association);
+ } else if (association?.collectionName && association?.name) {
+ return getCollectionField(`${association?.collectionName}.${association?.name}`);
+ }
+};
+
+const useReousrce = (props: UseReousrceProps) => {
+ const { block, resource, useSourceId } = props;
+ const record = useRecord();
+ const api = useAPIClient();
+ const association = useAssociation(props);
+ const sourceId = useSourceId?.();
+ const field = useField();
+ if (block === 'TableField') {
+ const options = {
+ field,
+ api,
+ resource,
+ sourceId: sourceId || record[association?.sourceKey || 'id'],
+ };
+ return new TableFieldResource(options);
+ }
+ const __parent = useContext(BlockRequestContext);
+ if (__parent?.block === 'TableField' && __parent?.resource instanceof TableFieldResource) {
+ return __parent.resource;
+ }
+ if (!association) {
+ return api.resource(resource);
+ }
+ if (sourceId) {
+ return api.resource(resource, sourceId);
+ }
+ return api.resource(resource, record[association?.sourceKey || 'id']);
+};
+
+const useActionParams = (props) => {
+ const { useParams } = props;
+ const params = useParams?.() || {};
+ return { ...props.params, ...params };
+};
+
+export const useResourceAction = (props, opts = {}) => {
+ const { resource, action } = props;
+ const { fields } = useCollection();
+ const appends = fields?.filter((field) => field.target).map((field) => field.name);
+ const params = useActionParams(props);
+ if (appends?.length) {
+ params['appends'] = appends;
+ }
+ const result = useRequest(
+ (params) => (action ? resource[action](params).then((res) => res.data) : Promise.resolve({})),
+ {
+ ...opts,
+ defaultParams: [params],
+ },
+ );
+ return result;
+};
+
+const MaybeCollectionProvider = (props) => {
+ const { collection } = props;
+ return collection ? (
+ {props.children}
+ ) : (
+ <>{props.children}>
+ );
+};
+
+const BlockRequestProvider = (props) => {
+ const field = useField();
+ const resource = useBlockResource();
+ const service = useResourceAction(
+ { ...props, resource },
+ {
+ ...props.requestOptions,
+ },
+ );
+ const __parent = useContext(BlockRequestContext);
+ return (
+
+ {props.children}
+
+ );
+};
+
+export const useBlockRequestContext = () => {
+ return useContext(BlockRequestContext);
+};
+
+export const BlockProvider = (props) => {
+ const { collection, association } = props;
+ const resource = useReousrce(props);
+ return (
+
+
+
+ {props.children}
+
+
+
+ );
+};
+
+export const useBlockAssociationContext = () => {
+ return useContext(BlockAssociationContext);
+};
+
+export const useFilterByTk = () => {
+ const { resource, __parent } = useContext(BlockRequestContext);
+ const recordIndex = useRecordIndex();
+ const record = useRecord();
+ const collection = useCollection();
+ const { getCollectionField } = useCollectionManager();
+ const assoc = useContext(BlockAssociationContext);
+ if (resource instanceof TableFieldResource || __parent?.block === 'TableField') {
+ return recordIndex;
+ }
+ if (assoc) {
+ const association = getCollectionField(assoc);
+ return record?.[association.targetKey || 'id'];
+ }
+ return record?.[collection.filterTargetKey || 'id'];
+};
+
+export const useSourceIdFromRecord = () => {
+ const record = useRecord();
+ const { getCollectionField } = useCollectionManager();
+ const assoc = useContext(BlockAssociationContext);
+ if (assoc) {
+ const association = getCollectionField(assoc);
+ return record?.[association.sourceKey || 'id'];
+ }
+};
+
+export const useSourceIdFromParentRecord = () => {
+ const record = useRecord();
+ const { getCollectionField } = useCollectionManager();
+ const assoc = useContext(BlockAssociationContext);
+ if (assoc) {
+ const association = getCollectionField(assoc);
+ return record?.__parent?.[association.sourceKey || 'id'];
+ }
+};
+
+export const useParamsFromRecord = () => {
+ const filterByTk = useFilterByTk();
+ return {
+ filterByTk: filterByTk,
+ };
+};
diff --git a/packages/client/src/block-provider/BlockSchemaComponentProvider.tsx b/packages/client/src/block-provider/BlockSchemaComponentProvider.tsx
new file mode 100644
index 000000000..83f087a14
--- /dev/null
+++ b/packages/client/src/block-provider/BlockSchemaComponentProvider.tsx
@@ -0,0 +1,38 @@
+import React from 'react';
+import { SchemaComponentOptions } from '../schema-component/core/SchemaComponentOptions';
+import { useParamsFromRecord, useSourceIdFromParentRecord, useSourceIdFromRecord } from './BlockProvider';
+import { CalendarBlockProvider, useCalendarBlockProps } from './CalendarBlockProvider';
+import { FormBlockProvider, useFormBlockProps } from './FormBlockProvider';
+import * as bp from './hooks';
+import { KanbanBlockProvider, useKanbanBlockProps } from './KanbanBlockProvider';
+import { TableBlockProvider, useTableBlockProps } from './TableBlockProvider';
+import { TableFieldProvider, useTableFieldProps } from './TableFieldProvider';
+import { TableSelectorProvider, useTableSelectorProps } from './TableSelectorProvider';
+export const BlockSchemaComponentProvider: React.FC = (props) => {
+ return (
+
+ {props.children}
+
+ );
+};
diff --git a/packages/client/src/block-provider/CalendarBlockProvider.tsx b/packages/client/src/block-provider/CalendarBlockProvider.tsx
new file mode 100644
index 000000000..bb3376135
--- /dev/null
+++ b/packages/client/src/block-provider/CalendarBlockProvider.tsx
@@ -0,0 +1,53 @@
+import { ArrayField } from '@formily/core';
+import { useField } from '@formily/react';
+import { Spin } from 'antd';
+import React, { createContext, useContext, useEffect } from 'react';
+import { BlockProvider, useBlockRequestContext } from './BlockProvider';
+
+export const CalendarBlockContext = createContext({});
+
+const InternalCalendarBlockProvider = (props) => {
+ const { fieldNames } = props;
+ const field = useField();
+ const { resource, service } = useBlockRequestContext();
+ if (service.loading) {
+ return ;
+ }
+ return (
+
+ {props.children}
+
+ );
+};
+
+export const CalendarBlockProvider = (props) => {
+ return (
+
+
+
+ );
+};
+
+export const useCalendarBlockContext = () => {
+ return useContext(CalendarBlockContext);
+};
+
+export const useCalendarBlockProps = () => {
+ const ctx = useCalendarBlockContext();
+ const field = useField();
+ useEffect(() => {
+ if (!ctx?.service?.loading) {
+ field.componentProps.dataSource = ctx?.service?.data?.data;
+ }
+ }, [ctx?.service?.loading]);
+ return {
+ fieldNames: ctx.fieldNames,
+ };
+};
diff --git a/packages/client/src/block-provider/FormBlockProvider.tsx b/packages/client/src/block-provider/FormBlockProvider.tsx
new file mode 100644
index 000000000..7d87bd548
--- /dev/null
+++ b/packages/client/src/block-provider/FormBlockProvider.tsx
@@ -0,0 +1,58 @@
+import { createForm } from '@formily/core';
+import { useField } from '@formily/react';
+import { Spin } from 'antd';
+import React, { createContext, useContext, useEffect, useMemo } from 'react';
+import { BlockProvider, useBlockRequestContext } from './BlockProvider';
+
+export const FormBlockContext = createContext({});
+
+const InternalFormBlockProvider = (props) => {
+ const { action, readPretty } = props;
+ const field = useField();
+ const form = useMemo(
+ () =>
+ createForm({
+ readPretty,
+ }),
+ [],
+ );
+ const { resource, service } = useBlockRequestContext();
+ if (service.loading) {
+ return ;
+ }
+ return (
+
+ {props.children}
+
+ );
+};
+
+export const FormBlockProvider = (props) => {
+ return (
+
+
+
+ );
+};
+
+export const useFormBlockContext = () => {
+ return useContext(FormBlockContext);
+};
+
+export const useFormBlockProps = () => {
+ const ctx = useFormBlockContext();
+ useEffect(() => {
+ ctx.form.setInitialValues(ctx.service?.data?.data);
+ }, []);
+ return {
+ form: ctx.form,
+ };
+};
diff --git a/packages/client/src/block-provider/KanbanBlockProvider.tsx b/packages/client/src/block-provider/KanbanBlockProvider.tsx
new file mode 100644
index 000000000..7c33536be
--- /dev/null
+++ b/packages/client/src/block-provider/KanbanBlockProvider.tsx
@@ -0,0 +1,88 @@
+import { ArrayField } from '@formily/core';
+import { useField } from '@formily/react';
+import { Spin } from 'antd';
+import React, { createContext, useContext, useEffect } from 'react';
+import { useCollection } from '../collection-manager';
+import { toColumns } from '../schema-component/antd/kanban-v2/Kanban';
+import { BlockProvider, useBlockRequestContext } from './BlockProvider';
+
+export const KanbanBlockContext = createContext({});
+
+const useGroupField = (props) => {
+ const { getField } = useCollection();
+ const { groupField } = props;
+ if (typeof groupField === 'string') {
+ return getField(groupField);
+ }
+ if (groupField?.name) {
+ return getField(groupField?.name);
+ }
+};
+
+const InternalKanbanBlockProvider = (props) => {
+ const field = useField();
+ const { resource, service } = useBlockRequestContext();
+ const groupField = useGroupField(props);
+ if (!groupField) {
+ return null;
+ }
+ if (service.loading) {
+ return ;
+ }
+ return (
+
+ {props.children}
+
+ );
+};
+
+export const KanbanBlockProvider = (props) => {
+ return (
+
+
+
+ );
+};
+
+export const useKanbanBlockContext = () => {
+ return useContext(KanbanBlockContext);
+};
+
+export const useKanbanBlockProps = () => {
+ const field = useField();
+ const ctx = useKanbanBlockContext();
+ useEffect(() => {
+ if (!ctx?.service?.loading) {
+ field.value = toColumns(ctx.groupField, ctx?.service?.data?.data);
+ }
+ // field.loading = ctx?.service?.loading;
+ }, [ctx?.service?.loading]);
+ return {
+ groupField: ctx.groupField,
+ async onCardDragEnd({ columns, groupField }, { fromColumnId, fromPosition }, { toColumnId, toPosition }) {
+ const sourceColumn = columns.find((column) => column.id === fromColumnId);
+ const destinationColumn = columns.find((column) => column.id === toColumnId);
+ const sourceCard = sourceColumn?.cards?.[fromPosition];
+ const targetCard = destinationColumn?.cards?.[toPosition];
+ const values = {
+ sourceId: sourceCard.id,
+ sortField: `${groupField.name}_sort`,
+ };
+ if (targetCard) {
+ values['targetId'] = targetCard.id;
+ } else {
+ values['targetScope'] = {
+ [groupField.name]: toColumnId,
+ };
+ }
+ await ctx.resource.move(values);
+ },
+ };
+};
diff --git a/packages/client/src/block-provider/TableBlockProvider.tsx b/packages/client/src/block-provider/TableBlockProvider.tsx
new file mode 100644
index 000000000..7b21fc957
--- /dev/null
+++ b/packages/client/src/block-provider/TableBlockProvider.tsx
@@ -0,0 +1,123 @@
+import { ArrayField } from '@formily/core';
+import { Schema, useField, useFieldSchema } from '@formily/react';
+import React, { createContext, useContext, useEffect } from 'react';
+import { useCollectionManager } from '../collection-manager';
+import { BlockProvider, useBlockRequestContext } from './BlockProvider';
+
+export const TableBlockContext = createContext({});
+
+const InternalTableBlockProvider = (props) => {
+ const { params, showIndex, dragSort, rowKey } = props;
+ const field = useField();
+ const { resource, service } = useBlockRequestContext();
+ // if (service.loading) {
+ // return ;
+ // }
+ return (
+
+ {props.children}
+
+ );
+};
+
+const useAssociationNames = (collection) => {
+ const { getCollectionFields } = useCollectionManager();
+ const names = getCollectionFields(collection)
+ ?.filter((field) => field.target)
+ .map((field) => field.name);
+ return names;
+ const fieldSchema = useFieldSchema();
+ const tableSchema = fieldSchema.reduceProperties((buf, schema) => {
+ if (schema['x-component'] === 'TableV2') {
+ return schema;
+ }
+ return buf;
+ }, null) as Schema;
+ return tableSchema.reduceProperties((buf, schema) => {
+ if (schema['x-component'] === 'TableV2.Column') {
+ const s = schema.reduceProperties((buf, s) => {
+ if (s['x-collection-field'] && names.includes(s.name)) {
+ return s;
+ }
+ return buf;
+ }, null);
+ if (s) {
+ buf.push(s.name);
+ }
+ }
+ return buf;
+ }, []);
+};
+
+export const TableBlockProvider = (props) => {
+ const params = { ...props.params };
+ const appends = useAssociationNames(props.collection);
+ if (props.dragSort) {
+ params['sort'] = ['sort'];
+ }
+ if (appends?.length) {
+ params['appends'] = appends;
+ }
+ return (
+
+
+
+ );
+};
+
+export const useTableBlockContext = () => {
+ return useContext(TableBlockContext);
+};
+
+export const useTableBlockProps = () => {
+ const field = useField();
+ const ctx = useTableBlockContext();
+ useEffect(() => {
+ if (!ctx?.service?.loading) {
+ field.value = ctx?.service?.data?.data;
+ field.data = field.data || {};
+ field.data.selectedRowKeys = ctx?.field?.data?.selectedRowKeys;
+ field.componentProps.pagination = field.componentProps.pagination || {};
+ field.componentProps.pagination.pageSize = ctx?.service?.data?.meta?.pageSize;
+ field.componentProps.pagination.total = ctx?.service?.data?.meta?.count;
+ field.componentProps.pagination.current = ctx?.service?.data?.meta?.page;
+ }
+ }, [ctx?.service?.loading]);
+ return {
+ loading: ctx?.service?.loading,
+ showIndex: ctx.showIndex,
+ dragSort: ctx.dragSort,
+ rowKey: ctx.rowKey || 'id',
+ pagination:
+ ctx?.params?.paginate !== false
+ ? {
+ defaultCurrent: ctx?.params?.page || 1,
+ defaultPageSize: ctx?.params?.pageSize,
+ }
+ : false,
+ onRowSelectionChange(selectedRowKeys) {
+ ctx.field.data = ctx?.field?.data || {};
+ ctx.field.data.selectedRowKeys = selectedRowKeys;
+ },
+ async onRowDragEnd({ from, to }) {
+ await ctx.resource.move({
+ sourceId: from[ctx.rowKey || 'id'],
+ targetId: to[ctx.rowKey || 'id'],
+ });
+ ctx.service.refresh();
+ },
+ onChange({ current, pageSize }) {
+ ctx.service.run({ ...ctx.service.params?.[0], page: current, pageSize });
+ },
+ };
+};
diff --git a/packages/client/src/block-provider/TableFieldProvider.tsx b/packages/client/src/block-provider/TableFieldProvider.tsx
new file mode 100644
index 000000000..680585a75
--- /dev/null
+++ b/packages/client/src/block-provider/TableFieldProvider.tsx
@@ -0,0 +1,147 @@
+import { ArrayField, Field } from '@formily/core';
+import { useField } from '@formily/react';
+import React, { createContext, useContext, useEffect } from 'react';
+import { APIClient } from '../api-client';
+import { BlockProvider, useBlockRequestContext } from './BlockProvider';
+
+export const TableFieldContext = createContext({});
+
+const InternalTableFieldProvider = (props) => {
+ const { params = {}, showIndex, dragSort } = props;
+ const field = useField();
+ const { resource, service } = useBlockRequestContext();
+ // if (service.loading) {
+ // return ;
+ // }
+ return (
+
+ {props.children}
+
+ );
+};
+
+export class TableFieldResource {
+ field: Field;
+ api: APIClient;
+ sourceId: any;
+ resource?: any;
+
+ constructor(options) {
+ this.field = options.field;
+ this.api = options.api;
+ this.sourceId = options.sourceId;
+ this.resource = this.api.resource(options.resource, this.sourceId);
+ }
+
+ async list(options) {
+ this.field.data = this.field.data || {};
+ if (this.field?.data?.dataSource?.length) {
+ console.log('list', this.field.data.dataSource);
+ return {
+ data: {
+ data: this.field.data.dataSource,
+ },
+ };
+ }
+ if (!this.sourceId) {
+ console.log('list.sourceId', this.field.data.dataSource);
+ this.field.data.dataSource = [];
+ return {
+ data: {
+ data: [],
+ },
+ };
+ }
+ const response = await this.resource.list(options);
+ console.log('list', response);
+ this.field.data.dataSource = response.data.data;
+ return {
+ data: {
+ data: response.data.data,
+ },
+ };
+ }
+
+ async get(options) {
+ console.log('get', options);
+ const { filterByTk } = options;
+ return {
+ data: {
+ data: this.field.data.dataSource[filterByTk],
+ },
+ };
+ }
+
+ async create(options) {
+ console.log('create', options);
+ const { values } = options;
+ this.field.data.dataSource.push(values);
+ }
+
+ async update(options) {
+ console.log('update', options);
+ const { filterByTk, values } = options;
+ this.field.data.dataSource[filterByTk] = values;
+ }
+
+ async destroy(options) {
+ console.log('destroy', options);
+ let { filterByTk } = options;
+ if (!Array.isArray(filterByTk)) {
+ filterByTk = [filterByTk];
+ }
+ this.field.data.dataSource = this.field.data.dataSource.filter((item, index) => {
+ return !filterByTk.includes(index);
+ });
+ }
+}
+
+export const TableFieldProvider = (props) => {
+ return (
+
+
+
+ );
+};
+
+export const useTableFieldContext = () => {
+ return useContext(TableFieldContext);
+};
+
+export const useTableFieldProps = () => {
+ const field = useField();
+ const ctx = useTableFieldContext();
+ useEffect(() => {
+ if (!ctx?.service?.loading) {
+ field.value = ctx?.service?.data?.data;
+ field.data = field.data || {};
+ field.data.selectedRowKeys = ctx?.field?.data?.selectedRowKeys;
+ }
+ }, [ctx?.service?.loading]);
+ return {
+ size: 'middle',
+ loading: ctx?.service?.loading,
+ showIndex: ctx.showIndex,
+ dragSort: ctx.dragSort,
+ pagination: false,
+ rowKey: (record: any) => {
+ return field.value?.indexOf?.(record);
+ },
+ onRowSelectionChange(selectedRowKeys) {
+ ctx.field.data = ctx?.field?.data || {};
+ ctx.field.data.selectedRowKeys = selectedRowKeys;
+ },
+ onChange({ current, pageSize }) {
+ ctx.service.run({ page: current, pageSize });
+ },
+ };
+};
diff --git a/packages/client/src/block-provider/TableSelectorProvider.tsx b/packages/client/src/block-provider/TableSelectorProvider.tsx
new file mode 100644
index 000000000..86a5fc601
--- /dev/null
+++ b/packages/client/src/block-provider/TableSelectorProvider.tsx
@@ -0,0 +1,100 @@
+import { ArrayField } from '@formily/core';
+import { useField } from '@formily/react';
+import React, { createContext, useContext, useEffect } from 'react';
+import { useCollectionManager } from '../collection-manager';
+import { BlockProvider, useBlockRequestContext } from './BlockProvider';
+
+export const TableSelectorContext = createContext({});
+
+const InternalTableSelectorProvider = (props) => {
+ const { params, rowKey } = props;
+ const field = useField();
+ const { resource, service } = useBlockRequestContext();
+ // if (service.loading) {
+ // return ;
+ // }
+ return (
+
+ {props.children}
+
+ );
+};
+
+const useAssociationNames = (collection) => {
+ const { getCollectionFields } = useCollectionManager();
+ const names = getCollectionFields(collection)
+ ?.filter((field) => field.target)
+ .map((field) => field.name);
+ return names;
+};
+
+export const TableSelectorProvider = (props) => {
+ const params = { ...props.params };
+ const appends = useAssociationNames(props.collection);
+ if (props.dragSort) {
+ params['sort'] = ['sort'];
+ }
+ if (appends?.length) {
+ params['appends'] = appends;
+ }
+ return (
+
+
+
+ );
+};
+
+export const useTableSelectorContext = () => {
+ return useContext(TableSelectorContext);
+};
+
+export const useTableSelectorProps = () => {
+ const field = useField();
+ const ctx = useTableSelectorContext();
+ useEffect(() => {
+ if (!ctx?.service?.loading) {
+ field.value = ctx?.service?.data?.data;
+ field.data = field.data || {};
+ field.data.selectedRowKeys = ctx?.field?.data?.selectedRowKeys;
+ field.componentProps.pagination = field.componentProps.pagination || {};
+ field.componentProps.pagination.pageSize = ctx?.service?.data?.meta?.pageSize;
+ field.componentProps.pagination.total = ctx?.service?.data?.meta?.count;
+ field.componentProps.pagination.current = ctx?.service?.data?.meta?.page;
+ }
+ }, [ctx?.service?.loading]);
+ return {
+ loading: ctx?.service?.loading,
+ showIndex: false,
+ dragSort: false,
+ rowKey: ctx.rowKey || 'id',
+ pagination:
+ ctx?.params?.paginate !== false
+ ? {
+ defaultCurrent: ctx?.params?.page || 1,
+ defaultPageSize: ctx?.params?.pageSize,
+ }
+ : false,
+ onRowSelectionChange(selectedRowKeys, selectedRows) {
+ ctx.field.data = ctx?.field?.data || {};
+ ctx.field.data.selectedRowKeys = selectedRowKeys;
+ },
+ async onRowDragEnd({ from, to }) {
+ await ctx.resource.move({
+ sourceId: from[ctx.rowKey || 'id'],
+ targetId: to[ctx.rowKey || 'id'],
+ });
+ ctx.service.refresh();
+ },
+ onChange({ current, pageSize }) {
+ ctx.service.run({ ...ctx.service.params?.[0], page: current, pageSize });
+ },
+ };
+};
diff --git a/packages/client/src/block-provider/hooks/index.ts b/packages/client/src/block-provider/hooks/index.ts
new file mode 100644
index 000000000..17ba92400
--- /dev/null
+++ b/packages/client/src/block-provider/hooks/index.ts
@@ -0,0 +1,76 @@
+import { useForm } from '@formily/react';
+import { useActionContext } from '../../schema-component';
+import { useBlockRequestContext, useFilterByTk } from '../BlockProvider';
+
+export const usePickActionProps = () => {
+ const form = useForm();
+ return {
+ onClick() {
+ console.log('usePickActionProps', form.values);
+ },
+ };
+};
+
+export const useCreateActionProps = () => {
+ const form = useForm();
+ const { resource, __parent } = useBlockRequestContext();
+ const { setVisible } = useActionContext();
+ return {
+ async onClick() {
+ await form.submit();
+ await resource.create({
+ values: form.values,
+ });
+ __parent?.service?.refresh?.();
+ setVisible?.(false);
+ },
+ };
+};
+
+export const useUpdateActionProps = () => {
+ const form = useForm();
+ const filterByTk = useFilterByTk();
+ const { resource, __parent } = useBlockRequestContext();
+ const { setVisible } = useActionContext();
+ return {
+ async onClick() {
+ await form.submit();
+
+ await resource.update({
+ filterByTk,
+ values: form.values,
+ });
+ __parent?.service?.refresh?.();
+ setVisible?.(false);
+ },
+ };
+};
+
+export const useDestroyActionProps = () => {
+ const filterByTk = useFilterByTk();
+ const { resource, service } = useBlockRequestContext();
+ return {
+ async onClick() {
+ await resource.destroy({
+ filterByTk,
+ });
+ service?.refresh?.();
+ },
+ };
+};
+
+export const useBulkDestroyActionProps = () => {
+ const { field } = useBlockRequestContext();
+ const { resource, service } = useBlockRequestContext();
+ return {
+ async onClick() {
+ if (!field?.data?.selectedRowKeys?.length) {
+ return;
+ }
+ await resource.destroy({
+ filterByTk: field.data?.selectedRowKeys,
+ });
+ service?.refresh?.();
+ },
+ };
+};
diff --git a/packages/client/src/block-provider/index.tsx b/packages/client/src/block-provider/index.tsx
new file mode 100644
index 000000000..e9d77ea6b
--- /dev/null
+++ b/packages/client/src/block-provider/index.tsx
@@ -0,0 +1,9 @@
+
+export * from './BlockProvider';
+export * from './BlockSchemaComponentProvider';
+export * from './CalendarBlockProvider';
+export * from './FormBlockProvider';
+export * from './KanbanBlockProvider';
+export * from './TableBlockProvider';
+export * from './TableFieldProvider';
+
diff --git a/packages/client/src/board/demos/demo2.tsx b/packages/client/src/board/demos/demo2.tsx
index fe52882e5..76c5c15a8 100644
--- a/packages/client/src/board/demos/demo2.tsx
+++ b/packages/client/src/board/demos/demo2.tsx
@@ -95,7 +95,7 @@ export default function App() {
onNewCardConfirm={(draftCard) => draftCard}
onCardNew={console.log}
renderCard={(card, { column, dragging }) => {
- return {card.title};
+ return {card.title};
}}
renderCardAdder={({ column }) => {
return (
diff --git a/packages/client/src/collection-manager/CollectionField.tsx b/packages/client/src/collection-manager/CollectionField.tsx
index 48c2910a8..8da074bed 100644
--- a/packages/client/src/collection-manager/CollectionField.tsx
+++ b/packages/client/src/collection-manager/CollectionField.tsx
@@ -10,7 +10,7 @@ import { useCollectionField } from './hooks';
const InternalField: React.FC = (props) => {
const field = useField();
const fieldSchema = useFieldSchema();
- const { name, uiSchema } = useCollectionField();
+ const { name, interface: interfaceType, uiSchema } = useCollectionField();
const component = useComponent(uiSchema?.['x-component']);
const compile = useCompile();
const setFieldProps = (key, value) => {
@@ -29,6 +29,9 @@ const InternalField: React.FC = (props) => {
setFieldProps('title', uiSchema.title);
setFieldProps('description', uiSchema.description);
setFieldProps('initialValue', uiSchema.default);
+ if (!field.validator && uiSchema['x-validator']) {
+ field.validator = uiSchema['x-validator'];
+ }
field.readPretty = uiSchema['x-read-pretty'];
setRequired();
// @ts-ignore
@@ -36,8 +39,16 @@ const InternalField: React.FC = (props) => {
const originalProps = compile(uiSchema['x-component-props']) || {};
const componentProps = merge(originalProps, field.componentProps || {});
field.component = [component, componentProps];
- // console.log('componentProps', uiSchema?.title, field.componentProps);
- }, [uiSchema?.title, uiSchema?.description, uiSchema?.required]);
+ // if (interfaceType === 'input') {
+ // field.componentProps.ellipsis = true;
+ // } else if (interfaceType === 'textarea') {
+ // field.componentProps.ellipsis = true;
+ // } else if (interfaceType === 'markdown') {
+ // field.componentProps.ellipsis = true;
+ // } else if (interfaceType === 'attachment') {
+ // field.componentProps.size = 'small';
+ // }
+ }, [JSON.stringify(uiSchema)]);
if (!uiSchema) {
return null;
}
diff --git a/packages/client/src/collection-manager/CollectionProvider.tsx b/packages/client/src/collection-manager/CollectionProvider.tsx
index 2ecf82d61..7dce442ca 100644
--- a/packages/client/src/collection-manager/CollectionProvider.tsx
+++ b/packages/client/src/collection-manager/CollectionProvider.tsx
@@ -5,10 +5,6 @@ import { CollectionOptions } from './types';
export const CollectionProvider: React.FC<{ name?: string; collection?: CollectionOptions }> = (props) => {
const { name, collection, children } = props;
- const { get } = useCollectionManager();
- return (
-
- {children}
-
- );
+ const { getCollection } = useCollectionManager();
+ return {children};
};
diff --git a/packages/client/src/collection-manager/hooks/useCollectionManager.ts b/packages/client/src/collection-manager/hooks/useCollectionManager.ts
index 2c759ed85..2326b2be8 100644
--- a/packages/client/src/collection-manager/hooks/useCollectionManager.ts
+++ b/packages/client/src/collection-manager/hooks/useCollectionManager.ts
@@ -12,13 +12,27 @@ export const useCollectionManager = () => {
get(name: string) {
return collections?.find((collection) => collection.name === name);
},
- getCollection(name: string) {
+ getCollection(name: any) {
+ if (typeof name !== 'string') {
+ return name;
+ }
return collections?.find((collection) => collection.name === name);
},
getCollectionFields(name: string) {
const collection = collections?.find((collection) => collection.name === name);
return collection?.fields || [];
},
+ getCollectionField(name: string) {
+ const [collectionName, fieldName] = name.split('.');
+ if (!fieldName) {
+ return;
+ }
+ const collection = collections?.find((collection) => collection.name === collectionName);
+ if (!collection) {
+ return;
+ }
+ return collection?.fields?.find((field) => field.name === fieldName);
+ },
getInterface(name: string) {
return interfaces[name] ? clone(interfaces[name]) : null;
},
diff --git a/packages/client/src/collection-manager/interfaces/attachment.ts b/packages/client/src/collection-manager/interfaces/attachment.ts
index 28225f9e3..a61de8663 100644
--- a/packages/client/src/collection-manager/interfaces/attachment.ts
+++ b/packages/client/src/collection-manager/interfaces/attachment.ts
@@ -1,3 +1,4 @@
+import { ISchema } from '@formily/react';
import { uid } from '@formily/shared';
import { defaultProps, operators } from './properties';
import { IField } from './types';
@@ -21,6 +22,12 @@ export const attachment: IField = {
},
},
},
+ schemaInitialize(schema: ISchema, { block }) {
+ if (['Table', 'Kanban'].includes(block)) {
+ schema['x-component-props'] = schema['x-component-props'] || {};
+ schema['x-component-props']['size'] = 'small';
+ }
+ },
initialize: (values: any) => {
if (!values.through) {
values.through = `t_${uid()}`;
diff --git a/packages/client/src/collection-manager/interfaces/createdBy.ts b/packages/client/src/collection-manager/interfaces/createdBy.ts
index 55fdd592d..e1ede4e61 100644
--- a/packages/client/src/collection-manager/interfaces/createdBy.ts
+++ b/packages/client/src/collection-manager/interfaces/createdBy.ts
@@ -1,4 +1,5 @@
-import { defaultProps, operators } from './properties';
+import { ISchema } from '@formily/react';
+import { defaultProps, operators, recordPickerViewer } from './properties';
import { IField } from './types';
export const createdBy: IField = {
@@ -43,4 +44,9 @@ export const createdBy: IField = {
},
],
},
+ schemaInitialize(schema: ISchema, { readPretty }) {
+ schema['properties'] = {
+ viewer: recordPickerViewer,
+ };
+ },
};
diff --git a/packages/client/src/collection-manager/interfaces/datetime.ts b/packages/client/src/collection-manager/interfaces/datetime.ts
index 8cc3d8b1a..aeefb0379 100644
--- a/packages/client/src/collection-manager/interfaces/datetime.ts
+++ b/packages/client/src/collection-manager/interfaces/datetime.ts
@@ -12,7 +12,7 @@ export const datetime: IField = {
type: 'date',
// name,
uiSchema: {
- type: 'datetime',
+ type: 'string',
// title,
'x-component': 'DatePicker',
'x-component-props': {
diff --git a/packages/client/src/collection-manager/interfaces/input.ts b/packages/client/src/collection-manager/interfaces/input.ts
index b43999dd1..0f973f1de 100644
--- a/packages/client/src/collection-manager/interfaces/input.ts
+++ b/packages/client/src/collection-manager/interfaces/input.ts
@@ -1,3 +1,4 @@
+import { ISchema } from '@formily/react';
import { defaultProps, operators } from './properties';
import { IField } from './types';
@@ -22,4 +23,10 @@ export const input: IField = {
filterable: {
operators: operators.string,
},
+ schemaInitialize(schema: ISchema, { block }) {
+ if (['Table', 'Kanban'].includes(block)) {
+ schema['x-component-props'] = schema['x-component-props'] || {};
+ schema['x-component-props']['ellipsis'] = true;
+ }
+ },
};
diff --git a/packages/client/src/collection-manager/interfaces/linkTo.ts b/packages/client/src/collection-manager/interfaces/linkTo.ts
index 000a87dfa..9b7c6b0b6 100644
--- a/packages/client/src/collection-manager/interfaces/linkTo.ts
+++ b/packages/client/src/collection-manager/interfaces/linkTo.ts
@@ -1,5 +1,7 @@
+import { ISchema } from '@formily/react';
import { uid } from '@formily/shared';
-import { defaultProps } from './properties';
+import { cloneDeep } from 'lodash';
+import { defaultProps, recordPickerSelector, recordPickerViewer } from './properties';
import { IField } from './types';
export const linkTo: IField = {
@@ -42,6 +44,17 @@ export const linkTo: IField = {
},
},
},
+ schemaInitialize(schema: ISchema, { readPretty }) {
+ if (readPretty) {
+ schema['properties'] = {
+ viewer: cloneDeep(recordPickerViewer),
+ };
+ } else {
+ schema['properties'] = {
+ selector: cloneDeep(recordPickerSelector),
+ };
+ }
+ },
initialize: (values: any) => {
if (values.type === 'belongsToMany') {
if (!values.through) {
diff --git a/packages/client/src/collection-manager/interfaces/markdown.ts b/packages/client/src/collection-manager/interfaces/markdown.ts
index fe9a1eec0..687987d28 100644
--- a/packages/client/src/collection-manager/interfaces/markdown.ts
+++ b/packages/client/src/collection-manager/interfaces/markdown.ts
@@ -1,3 +1,4 @@
+import { ISchema } from '@formily/react';
import { defaultProps } from './properties';
import { IField } from './types';
@@ -18,4 +19,10 @@ export const markdown: IField = {
properties: {
...defaultProps,
},
+ schemaInitialize(schema: ISchema, { block }) {
+ if (['Table', 'Kanban'].includes(block)) {
+ schema['x-component-props'] = schema['x-component-props'] || {};
+ schema['x-component-props']['ellipsis'] = true;
+ }
+ },
};
diff --git a/packages/client/src/collection-manager/interfaces/properties/index.ts b/packages/client/src/collection-manager/interfaces/properties/index.ts
index 0afcc9666..632f908ca 100644
--- a/packages/client/src/collection-manager/interfaces/properties/index.ts
+++ b/packages/client/src/collection-manager/interfaces/properties/index.ts
@@ -1,5 +1,7 @@
import { ISchema } from '@formily/react';
+export * as operators from './operators';
+
export const type: ISchema = {
type: 'string',
title: '{{t("Storage type")}}',
@@ -199,5 +201,77 @@ export const defaultProps = {
type,
};
+export const recordPickerSelector: ISchema = {
+ type: 'void',
+ title: '{{ t("Select record") }}',
+ 'x-component': 'RecordPicker.Selector',
+ 'x-component-props': {
+ className: 'nb-record-picker-selector'
+ },
+ properties: {
+ grid: {
+ type: 'void',
+ 'x-component': 'Grid',
+ 'x-initializer': 'TableSelectorInitializers',
+ properties: {},
+ },
+ footer: {
+ 'x-component': 'Action.Container.Footer',
+ 'x-component-props': {},
+ properties: {
+ actions: {
+ type: 'void',
+ 'x-component': 'ActionBar',
+ 'x-component-props': {},
+ properties: {
+ submit: {
+ title: '{{ t("Submit") }}',
+ 'x-action': 'submit',
+ 'x-component': 'Action',
+ 'x-designer': 'Action.Designer',
+ 'x-component-props': {
+ type: 'primary',
+ htmlType: 'submit',
+ useProps: '{{ usePickActionProps }}',
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+};
-export * as operators from './operators';
+export const recordPickerViewer = {
+ type: 'void',
+ title: '{{ t("View record") }}',
+ 'x-component': 'RecordPicker.Viewer',
+ 'x-component-props': {
+ className: 'nb-action-popup',
+ },
+ properties: {
+ tabs: {
+ type: 'void',
+ 'x-component': 'Tabs',
+ 'x-component-props': {},
+ 'x-initializer': 'TabPaneInitializers',
+ properties: {
+ tab1: {
+ type: 'void',
+ title: '{{t("Details")}}',
+ 'x-component': 'Tabs.TabPane',
+ 'x-designer': 'Tabs.Designer',
+ 'x-component-props': {},
+ properties: {
+ grid: {
+ type: 'void',
+ 'x-component': 'Grid',
+ 'x-initializer': 'RecordBlockInitializers',
+ properties: {},
+ },
+ },
+ },
+ },
+ },
+ },
+};
diff --git a/packages/client/src/collection-manager/interfaces/subTable.ts b/packages/client/src/collection-manager/interfaces/subTable.ts
index 6bc8813e3..da3bfe95b 100644
--- a/packages/client/src/collection-manager/interfaces/subTable.ts
+++ b/packages/client/src/collection-manager/interfaces/subTable.ts
@@ -1,3 +1,4 @@
+import { ISchema } from '@formily/react';
import { uid } from '@formily/shared';
import { defaultProps } from './properties';
import { IField } from './types';
@@ -13,13 +14,53 @@ export const subTable: IField = {
type: 'hasMany',
// name,
uiSchema: {
- type: 'array',
+ type: 'void',
// title,
- 'x-component': 'Table',
+ 'x-component': 'TableField',
'x-component-props': {},
- enum: [],
},
},
+ schemaInitialize(schema: ISchema, { field, readPretty }) {
+ const association = `${field.collectionName}.${field.name}`;
+ schema['type'] = 'void';
+ schema['x-component'] = 'TableField';
+ schema['properties'] = {
+ block: {
+ type: 'void',
+ 'x-decorator': 'TableFieldProvider',
+ 'x-decorator-props': {
+ collection: field.target,
+ association: association,
+ resource: association,
+ action: 'list',
+ params: {
+ paginate: false,
+ },
+ showIndex: true,
+ dragSort: false,
+ },
+ properties: {
+ actions: {
+ type: 'void',
+ 'x-initializer': 'SubTableActionInitializers',
+ 'x-component': 'TableField.ActionBar',
+ 'x-component-props': {},
+ },
+ [field.name]: {
+ type: 'array',
+ 'x-initializer': 'TableColumnInitializers',
+ 'x-component': 'TableV2',
+ 'x-component-props': {
+ rowSelection: {
+ type: 'checkbox',
+ },
+ useProps: '{{ useTableFieldProps }}',
+ },
+ },
+ },
+ },
+ };
+ },
initialize: (values: any) => {
if (!values.target) {
values.target = `t_${uid()}`;
diff --git a/packages/client/src/collection-manager/interfaces/textarea.ts b/packages/client/src/collection-manager/interfaces/textarea.ts
index 9a5cbde3b..3561d51d6 100644
--- a/packages/client/src/collection-manager/interfaces/textarea.ts
+++ b/packages/client/src/collection-manager/interfaces/textarea.ts
@@ -1,3 +1,4 @@
+import { ISchema } from '@formily/react';
import { defaultProps } from './properties';
import { IField } from './types';
@@ -19,4 +20,10 @@ export const textarea: IField = {
properties: {
...defaultProps,
},
+ schemaInitialize(schema: ISchema, { block }) {
+ if (['Table', 'Kanban'].includes(block)) {
+ schema['x-component-props'] = schema['x-component-props'] || {};
+ schema['x-component-props']['ellipsis'] = true;
+ }
+ },
};
diff --git a/packages/client/src/collection-manager/interfaces/updatedAt.ts b/packages/client/src/collection-manager/interfaces/updatedAt.ts
index 5eb008706..4498c5ee3 100644
--- a/packages/client/src/collection-manager/interfaces/updatedAt.ts
+++ b/packages/client/src/collection-manager/interfaces/updatedAt.ts
@@ -13,7 +13,7 @@ export const updatedAt: IField = {
field: 'updatedAt',
// name,
uiSchema: {
- type: 'datetime',
+ type: 'string',
title: '{{t("Last updated at")}}',
'x-component': 'DatePicker',
'x-component-props': {},
diff --git a/packages/client/src/collection-manager/interfaces/updatedBy.ts b/packages/client/src/collection-manager/interfaces/updatedBy.ts
index 282103a26..d809232e1 100644
--- a/packages/client/src/collection-manager/interfaces/updatedBy.ts
+++ b/packages/client/src/collection-manager/interfaces/updatedBy.ts
@@ -1,4 +1,5 @@
-import { defaultProps, operators } from './properties';
+import { ISchema } from '@formily/react';
+import { defaultProps, operators, recordPickerViewer } from './properties';
import { IField } from './types';
export const updatedBy: IField = {
@@ -42,4 +43,9 @@ export const updatedBy: IField = {
},
],
},
+ schemaInitialize(schema: ISchema, { readPretty }) {
+ schema['properties'] = {
+ viewer: recordPickerViewer,
+ };
+ },
};
diff --git a/packages/client/src/i18n/i18n.ts b/packages/client/src/i18n/i18n.ts
index 16afc9b3f..791fb1720 100644
--- a/packages/client/src/i18n/i18n.ts
+++ b/packages/client/src/i18n/i18n.ts
@@ -1,34 +1,21 @@
import i18next from 'i18next';
import moment from 'moment';
import { initReactI18next } from 'react-i18next';
-
-const zhCN = require('../locale/zh_CN');
-const enUS = require('../locale/en_US');
+import { resources } from '../locale';
const log = require('debug')('i18next');
export const i18n = i18next.createInstance();
i18n.use(initReactI18next).init({
lng: localStorage.getItem('locale') || 'en-US',
- debug: false,
+ // debug: true,
defaultNS: 'client',
// parseMissingKeyHandler: (key) => {
// console.log('parseMissingKeyHandler', `'${key}': '${key}',`);
// return key;
// },
// ns: ['client'],
- resources: {
- 'en-US': {
- client: {
- ...enUS,
- },
- },
- 'zh-CN': {
- client: {
- ...zhCN,
- },
- },
- },
+ resources,
});
const momentLngs = {
diff --git a/packages/client/src/index.tsx b/packages/client/src/index.tsx
index f6c861875..5a27f5422 100644
--- a/packages/client/src/index.tsx
+++ b/packages/client/src/index.tsx
@@ -6,6 +6,7 @@ export * from './antd-config-provider';
export * from './api-client';
export * from './application';
export * from './async-data-provider';
+export * from './block-provider';
export * from './board';
export * from './china-region';
export * from './collection-manager';
diff --git a/packages/client/src/locale/en_US.json b/packages/client/src/locale/en_US.ts
similarity index 98%
rename from packages/client/src/locale/en_US.json
rename to packages/client/src/locale/en_US.ts
index f2db3d52c..150f3eb9c 100644
--- a/packages/client/src/locale/en_US.json
+++ b/packages/client/src/locale/en_US.ts
@@ -1,8 +1,10 @@
-{
+export default {
"{{count}} filter items_one": "{{count}} filter item",
"{{count}} filter items_other": "{{count}} filter items",
"{{count}} more items_one": "{{count}} more item",
"{{count}} more items_other": "{{count}} more items",
+ "Total {{count}} items_one": "Total {{count}} item",
+ "Total {{count}} items_other": "Total {{count}} items",
"Delete menu item": "Delete menu item",
"Today": "Today",
"Month": "Month",
diff --git a/packages/client/src/locale/index.ts b/packages/client/src/locale/index.ts
new file mode 100644
index 000000000..66fca384b
--- /dev/null
+++ b/packages/client/src/locale/index.ts
@@ -0,0 +1,15 @@
+import enUS from './en_US';
+import zhCN from './zh_CN';
+
+export const resources = {
+ 'en-US': {
+ client: {
+ ...enUS,
+ },
+ },
+ 'zh-CN': {
+ client: {
+ ...zhCN,
+ },
+ },
+};
diff --git a/packages/client/src/locale/zh_CN.json b/packages/client/src/locale/zh_CN.ts
similarity index 98%
rename from packages/client/src/locale/zh_CN.json
rename to packages/client/src/locale/zh_CN.ts
index 5faad51aa..ef5d571ec 100644
--- a/packages/client/src/locale/zh_CN.json
+++ b/packages/client/src/locale/zh_CN.ts
@@ -1,10 +1,10 @@
-{
+export default {
"Display <1><0>100><1>201><2>502><3>1003>1> items per page": "每页显示 <1><0>100><1>201><2>502><3>1003>1> 条",
"Meet <1><0>All0><1>Any1>1> conditions in the group": "满足组内 <1><0>全部0><1>任意1>1> 条件",
"Open in<1><0>Modal0><1>Drawer1><2>Window2>1>": "在 <1><0>对话框0><1>抽屉1><2>窗口2>1> 内打开",
"{{count}} filter items": "{{count}} 个筛选项",
"{{count}} more items": "还有 {{count}} 项",
-
+ "Total {{count}} items": "总共 {{count}} 条",
"Today": "今天",
"Month": "月",
"Week": "周",
@@ -285,5 +285,7 @@
"Block templates": "区块模板",
"Convert reference to duplicate": "模板引用转为复制",
"Template name": "模板名称",
- "Block type": "区块类型"
+ "Block type": "区块类型",
+ "Action column": "操作列",
+ "Records per page": "每页显示数量"
}
diff --git a/packages/client/src/record-provider/index.tsx b/packages/client/src/record-provider/index.tsx
index f2f758d5f..f5bab9775 100644
--- a/packages/client/src/record-provider/index.tsx
+++ b/packages/client/src/record-provider/index.tsx
@@ -5,7 +5,8 @@ export const RecordIndexContext = createContext(null);
export const RecordProvider: React.FC<{ record: any }> = (props) => {
const { record, children } = props;
- return {children};
+ const __parent = useContext(RecordContext);
+ return {children};
};
export const RecordIndexProvider: React.FC<{ index: any }> = (props) => {
diff --git a/packages/client/src/schema-component/antd/AntdSchemaComponentProvider.tsx b/packages/client/src/schema-component/antd/AntdSchemaComponentProvider.tsx
index 4e7d0c035..15ed7644a 100644
--- a/packages/client/src/schema-component/antd/AntdSchemaComponentProvider.tsx
+++ b/packages/client/src/schema-component/antd/AntdSchemaComponentProvider.tsx
@@ -2,8 +2,9 @@ import React from 'react';
import * as components from '.';
import * as common from '../common';
import { SchemaComponentOptions } from '../core/SchemaComponentOptions';
+import { useFilterActionProps } from './filter/useFilterActionProps';
export const AntdSchemaComponentProvider = (props) => {
const { children } = props;
- return {children};
+ return {children};
};
diff --git a/packages/client/src/schema-component/antd/action/Action.Designer.tsx b/packages/client/src/schema-component/antd/action/Action.Designer.tsx
index 0e20b016c..1ea3fa2c0 100644
--- a/packages/client/src/schema-component/antd/action/Action.Designer.tsx
+++ b/packages/client/src/schema-component/antd/action/Action.Designer.tsx
@@ -4,7 +4,6 @@ import { useDesignable } from '../..';
import { GeneralSchemaDesigner, SchemaSettings } from '../../../schema-settings';
export const ActionDesigner = (props) => {
- const initialValue = {};
const field = useField();
const fieldSchema = useFieldSchema();
const { dn } = useDesignable();
@@ -22,21 +21,35 @@ export const ActionDesigner = (props) => {
'x-decorator': 'FormItem',
'x-component': 'Input',
title: '按钮标题',
+ default: fieldSchema.title,
+ 'x-component-props': {},
+ // description: `原字段标题:${collectionField?.uiSchema?.title}`,
+ },
+ icon: {
+ 'x-decorator': 'FormItem',
+ 'x-component': 'IconPicker',
+ title: '按钮图标',
+ default: fieldSchema?.['x-component-props']?.icon,
'x-component-props': {},
// description: `原字段标题:${collectionField?.uiSchema?.title}`,
},
},
} as ISchema
}
- initialValues={{ title: field.title }}
- onSubmit={({ title }) => {
+ onSubmit={({ title, icon }) => {
if (title) {
fieldSchema.title = title;
field.title = title;
+ field.componentProps.icon = icon;
+ fieldSchema['x-component-props'] = fieldSchema['x-component-props'] || {};
+ fieldSchema['x-component-props'].icon = icon;
dn.emit('patch', {
schema: {
['x-uid']: fieldSchema['x-uid'],
title,
+ 'x-component-props': {
+ ...fieldSchema['x-component-props'],
+ },
},
});
dn.refresh();
diff --git a/packages/client/src/schema-component/antd/action/Action.Drawer.tsx b/packages/client/src/schema-component/antd/action/Action.Drawer.tsx
index 95f7e702d..c55451e14 100644
--- a/packages/client/src/schema-component/antd/action/Action.Drawer.tsx
+++ b/packages/client/src/schema-component/antd/action/Action.Drawer.tsx
@@ -36,6 +36,17 @@ export const ActionDrawer: ComposedActionDrawer = observer((props) => {
background: #f0f2f5;
}
}
+ &.nb-record-picker-selector {
+ .nb-block-item {
+ margin-bottom: 24px;
+ .general-schema-designer {
+ top: -8px;
+ bottom: -8px;
+ left: -8px;
+ right: -8px;
+ }
+ }
+ }
`,
)}
footer={
diff --git a/packages/client/src/schema-component/antd/action/Action.tsx b/packages/client/src/schema-component/antd/action/Action.tsx
index e49714912..ad0cdea79 100644
--- a/packages/client/src/schema-component/antd/action/Action.tsx
+++ b/packages/client/src/schema-component/antd/action/Action.tsx
@@ -4,8 +4,10 @@ import { Button, Modal, Popover } from 'antd';
import classnames from 'classnames';
import React, { useState } from 'react';
import { useActionContext } from '../..';
+import { Icon } from '../../../icon';
import { SortableItem } from '../../common';
import { useDesigner } from '../../hooks';
+import { useProps } from '../../hooks/useProps';
import ActionContainer from './Action.Container';
import { ActionDesigner } from './Action.Designer';
import { ActionDrawer } from './Action.Drawer';
@@ -71,10 +73,12 @@ export const Action: ComposedAction = observer((props: any) => {
containerRefKey,
component,
useAction = useA,
- onClick,
className,
+ disabled,
+ icon,
...others
} = props;
+ const { onClick } = useProps(props);
const [visible, setVisible] = useState(false);
const Designer = useDesigner();
const field = useField();
@@ -84,6 +88,7 @@ export const Action: ComposedAction = observer((props: any) => {
const renderButton = () => (
}
onClick={(e: React.MouseEvent) => {
e.preventDefault();
e.stopPropagation();
@@ -104,8 +109,8 @@ export const Action: ComposedAction = observer((props: any) => {
component={component || Button}
className={classnames(className, actionDesignerCss)}
>
-
{field.title}
+
);
return (
diff --git a/packages/client/src/schema-component/antd/calendar-v2/Calendar.Designer.tsx b/packages/client/src/schema-component/antd/calendar-v2/Calendar.Designer.tsx
new file mode 100644
index 000000000..a1219566f
--- /dev/null
+++ b/packages/client/src/schema-component/antd/calendar-v2/Calendar.Designer.tsx
@@ -0,0 +1,142 @@
+import { ISchema, useField, useFieldSchema } from '@formily/react';
+import React from 'react';
+import { useTranslation } from 'react-i18next';
+import { useCompile, useDesignable } from '../..';
+import { useCalendarBlockContext } from '../../../block-provider';
+import { useCollection } from '../../../collection-manager';
+import { useCollectionFilterOptions } from '../../../collection-manager/action-hooks';
+import { GeneralSchemaDesigner, SchemaSettings } from '../../../schema-settings';
+import { useSchemaTemplate } from '../../../schema-templates';
+
+const useOptions = () => {
+ const compile = useCompile();
+ const { fields } = useCollection();
+ const options = fields?.map((field) => {
+ return {
+ value: field.name,
+ label: compile(field?.uiSchema?.title),
+ };
+ });
+ return options;
+}
+
+export const CalendarDesigner = () => {
+ const field = useField();
+ const fieldSchema = useFieldSchema();
+ const { name, title, fields } = useCollection();
+ const dataSource = useCollectionFilterOptions(name);
+ const { service } = useCalendarBlockContext();
+ const { dn } = useDesignable();
+ const compile = useCompile();
+ const { t } = useTranslation();
+ const template = useSchemaTemplate();
+ const defaultFilter = fieldSchema?.['x-decorator-props']?.params?.filter || {};
+ const options = useOptions();
+ const fieldNames = fieldSchema?.['x-decorator-props']?.['fieldNames'] || {};
+ return (
+
+ {
+ const fieldNames = field.decoratorProps.fieldNames || {};
+ fieldNames['title'] = title;
+ field.decoratorProps.params = fieldNames;
+ fieldSchema['x-decorator-props']['params'] = fieldNames;
+ service.refresh();
+ dn.emit('patch', {
+ schema: {
+ ['x-uid']: fieldSchema['x-uid'],
+ 'x-decorator-props': field.decoratorProps,
+ },
+ });
+ dn.refresh();
+ }}
+ />
+ {
+ const fieldNames = field.decoratorProps.fieldNames || {};
+ fieldNames['start'] = start;
+ field.decoratorProps.params = fieldNames;
+ fieldSchema['x-decorator-props']['params'] = fieldNames;
+ service.refresh();
+ dn.emit('patch', {
+ schema: {
+ ['x-uid']: fieldSchema['x-uid'],
+ 'x-decorator-props': field.decoratorProps,
+ },
+ });
+ dn.refresh();
+ }}
+ />
+ {
+ const fieldNames = field.decoratorProps.fieldNames || {};
+ fieldNames['end'] = end;
+ field.decoratorProps.params = fieldNames;
+ fieldSchema['x-decorator-props']['params'] = fieldNames;
+ service.refresh();
+ dn.emit('patch', {
+ schema: {
+ ['x-uid']: fieldSchema['x-uid'],
+ 'x-decorator-props': field.decoratorProps,
+ },
+ });
+ dn.refresh();
+ }}
+ />
+ {
+ const params = field.decoratorProps.params || {};
+ params.filter = filter;
+ field.decoratorProps.params = params;
+ fieldSchema['x-decorator-props']['params'] = params;
+ service.run({ ...service?.params?.[0], filter });
+ dn.emit('patch', {
+ schema: {
+ ['x-uid']: fieldSchema['x-uid'],
+ 'x-decorator-props': field.decoratorProps,
+ },
+ });
+ }}
+ />
+
+
+
+
+
+ );
+};
diff --git a/packages/client/src/schema-component/antd/calendar-v2/Calendar.tsx b/packages/client/src/schema-component/antd/calendar-v2/Calendar.tsx
new file mode 100644
index 000000000..6d7a7181f
--- /dev/null
+++ b/packages/client/src/schema-component/antd/calendar-v2/Calendar.tsx
@@ -0,0 +1,156 @@
+import { LeftOutlined, RightOutlined } from '@ant-design/icons';
+import { createForm } from '@formily/core';
+import { observer, RecursionField, Schema, useFieldSchema } from '@formily/react';
+import get from 'lodash/get';
+import moment from 'moment';
+import React, { useMemo, useState } from 'react';
+import { Calendar as BigCalendar, momentLocalizer } from 'react-big-calendar';
+import * as dates from 'react-big-calendar/lib/utils/dates';
+import { useTranslation } from 'react-i18next';
+import { RecordProvider } from '../../../';
+import { i18n } from '../../../i18n';
+import { useProps } from '../../hooks/useProps';
+import { ActionContext } from '../action';
+import { CalendarToolbarContext } from './context';
+import './style.less';
+import type { ToolbarProps } from './types';
+
+const localizer = momentLocalizer(moment);
+
+function Toolbar(props: ToolbarProps) {
+ const fieldSchema = useFieldSchema();
+ const toolBarSchema: Schema = useMemo(
+ () =>
+ fieldSchema.reduceProperties((buf, current) => {
+ if (current['x-component'].endsWith('.ActionBar')) {
+ return current;
+ }
+ return buf;
+ }, null),
+ [],
+ );
+ return (
+
+
+
+ );
+}
+
+const messages: any = {
+ allDay: '',
+ previous: (
+
+
+
+ ),
+ next: (
+
+
+
+ ),
+ today: i18n.t('Today'),
+ month: i18n.t('Month'),
+ week: i18n.t('Week'),
+ work_week: i18n.t('Work week'),
+ day: i18n.t('Day'),
+ agenda: i18n.t('Agenda'),
+ date: i18n.t('Date'),
+ time: i18n.t('Time'),
+ event: i18n.t('Event'),
+ noEventsInRange: i18n.t('None'),
+ showMore: (count) => i18n.t('{{count}} more items', { count }),
+};
+
+const useEvents = (dataSource: any, fieldNames: any) => {
+ const { t } = useTranslation();
+ return useMemo(
+ () =>
+ Array.isArray(dataSource)
+ ? dataSource?.map((item) => {
+ return {
+ id: get(item, fieldNames.id || 'id'),
+ title: get(item, fieldNames.title) || t('Untitle'),
+ start: new Date(get(item, fieldNames.start)),
+ end: new Date(get(item, fieldNames.end || fieldNames.start)),
+ };
+ })
+ : [],
+ [dataSource, fieldNames],
+ );
+};
+
+const CalendarRecordViewer = (props) => {
+ const { visible, setVisible, record } = props;
+ const form = useMemo(() => createForm(), [record]);
+ const fieldSchema = useFieldSchema();
+ const eventSchema: Schema = useMemo(
+ () =>
+ fieldSchema.reduceProperties((buf, current) => {
+ if (current['x-component'].endsWith('.Event')) {
+ return current;
+ }
+ return buf;
+ }, null),
+ [],
+ );
+ return (
+ eventSchema && (
+
+
+
+
+
+ )
+ );
+};
+
+export const Calendar: any = observer((props: any) => {
+ const { dataSource, fieldNames } = useProps(props);
+ const events = useEvents(dataSource, fieldNames);
+ const [visible, setVisible] = useState(false);
+ const [record, setRecord] = useState({});
+ return (
+
+
+ {
+ console.log('onSelectSlot', slotInfo);
+ }}
+ onDoubleClickEvent={(event) => {
+ console.log('onDoubleClickEvent');
+ }}
+ onSelectEvent={(event) => {
+ const record = dataSource?.find((item) => item[fieldNames.id] === event.id);
+ if (!record) {
+ return;
+ }
+ setRecord(record);
+ setVisible(true);
+ }}
+ formats={{
+ monthHeaderFormat: 'Y-M',
+ agendaDateFormat: 'M-DD',
+ dayHeaderFormat: 'Y-M-DD',
+ dayRangeHeaderFormat: ({ start, end }, culture, local) => {
+ if (dates.eq(start, end, 'month')) {
+ return local.format(start, 'Y-M', culture);
+ }
+ return `${local.format(start, 'Y-M', culture)} - ${local.format(end, 'Y-M', culture)}`;
+ },
+ }}
+ defaultDate={new Date()}
+ components={{
+ toolbar: Toolbar,
+ }}
+ localizer={localizer}
+ />
+
+ );
+});
diff --git a/packages/client/src/schema-component/antd/calendar-v2/Event.tsx b/packages/client/src/schema-component/antd/calendar-v2/Event.tsx
new file mode 100644
index 000000000..73a9f26f6
--- /dev/null
+++ b/packages/client/src/schema-component/antd/calendar-v2/Event.tsx
@@ -0,0 +1,6 @@
+import { observer } from '@formily/react';
+import React from 'react';
+
+export const Event = observer((props) => {
+ return <>{props.children}>;
+});
diff --git a/packages/client/src/schema-component/antd/calendar-v2/Nav.tsx b/packages/client/src/schema-component/antd/calendar-v2/Nav.tsx
new file mode 100644
index 000000000..4a8bd3ca3
--- /dev/null
+++ b/packages/client/src/schema-component/antd/calendar-v2/Nav.tsx
@@ -0,0 +1,19 @@
+import { LeftOutlined, RightOutlined } from '@ant-design/icons';
+import { observer } from '@formily/react';
+import { Button } from 'antd';
+import React, { useContext } from 'react';
+import { navigate } from 'react-big-calendar/lib/utils/constants';
+import { useDesignable } from '../../hooks';
+import { CalendarToolbarContext } from './context';
+
+export const Nav = observer((props) => {
+ const { DesignableBar } = useDesignable();
+ const { onNavigate } = useContext(CalendarToolbarContext);
+ return (
+
+ } onClick={() => onNavigate(navigate.PREVIOUS)}>
+ } onClick={() => onNavigate(navigate.NEXT)}>
+
+
+ );
+});
diff --git a/packages/client/src/schema-component/antd/calendar-v2/Title.tsx b/packages/client/src/schema-component/antd/calendar-v2/Title.tsx
new file mode 100644
index 000000000..379a6a70f
--- /dev/null
+++ b/packages/client/src/schema-component/antd/calendar-v2/Title.tsx
@@ -0,0 +1,15 @@
+import { observer } from '@formily/react';
+import React, { useContext } from 'react';
+import { useDesignable } from '../../hooks';
+import { CalendarToolbarContext } from './context';
+
+export const Title = observer(() => {
+ const { DesignableBar } = useDesignable();
+ const { label } = useContext(CalendarToolbarContext);
+ return (
+
+ {label}
+
+
+ );
+});
diff --git a/packages/client/src/schema-component/antd/calendar-v2/Today.tsx b/packages/client/src/schema-component/antd/calendar-v2/Today.tsx
new file mode 100644
index 000000000..dd8804f7b
--- /dev/null
+++ b/packages/client/src/schema-component/antd/calendar-v2/Today.tsx
@@ -0,0 +1,23 @@
+import { observer } from '@formily/react';
+import { Button } from 'antd';
+import React, { useContext } from 'react';
+import { navigate } from 'react-big-calendar/lib/utils/constants';
+import { useTranslation } from 'react-i18next';
+import { useDesignable } from '../../hooks';
+import { CalendarToolbarContext } from './context';
+
+export const Today = observer((props) => {
+ const { DesignableBar } = useDesignable();
+ const { onNavigate } = useContext(CalendarToolbarContext);
+ const { t } = useTranslation();
+ return (
+
+ );
+});
diff --git a/packages/client/src/schema-component/antd/calendar-v2/ViewSelect.tsx b/packages/client/src/schema-component/antd/calendar-v2/ViewSelect.tsx
new file mode 100644
index 000000000..027ea36e3
--- /dev/null
+++ b/packages/client/src/schema-component/antd/calendar-v2/ViewSelect.tsx
@@ -0,0 +1,25 @@
+import { observer } from '@formily/react';
+import { Select } from 'antd';
+import React, { useContext } from 'react';
+import { useDesignable } from '../../hooks';
+import { CalendarToolbarContext } from './context';
+
+export const ViewSelect = observer((props) => {
+ const { DesignableBar } = useDesignable();
+ const {
+ views,
+ view,
+ onView,
+ localizer: { messages },
+ } = useContext(CalendarToolbarContext);
+ return (
+
+
+
+
+ );
+});
diff --git a/packages/client/src/schema-component/antd/calendar-v2/context.ts b/packages/client/src/schema-component/antd/calendar-v2/context.ts
new file mode 100644
index 000000000..31dc497ba
--- /dev/null
+++ b/packages/client/src/schema-component/antd/calendar-v2/context.ts
@@ -0,0 +1,5 @@
+import { createContext } from 'react';
+import type { ToolbarProps } from './types';
+
+export const CalendarToolbarContext = createContext(null);
+export const CalendarContext = createContext(null);
diff --git a/packages/client/src/schema-component/antd/calendar-v2/demos/collections.ts b/packages/client/src/schema-component/antd/calendar-v2/demos/collections.ts
new file mode 100644
index 000000000..2eed0197b
--- /dev/null
+++ b/packages/client/src/schema-component/antd/calendar-v2/demos/collections.ts
@@ -0,0 +1,1321 @@
+export default {
+ data: [
+ {
+ key: '17zb9wfq0vt',
+ name: 'product',
+ title: 's',
+ inherit: false,
+ fields: [],
+ createdBy: true,
+ updatedBy: true,
+ sortable: true,
+ },
+ {
+ key: '1s4aq7jbxdh',
+ name: 'roles',
+ title: '{{t("Roles")}}',
+ inherit: false,
+ fields: [
+ {
+ key: 'gs6190e27z5',
+ name: 'name',
+ type: 'uid',
+ interface: 'input',
+ collectionName: 'roles',
+ parentKey: null,
+ reverseKey: null,
+ uiSchemaUid: 'vy6i30jm9jm',
+ uiSchema: {
+ 'x-uid': 'vy6i30jm9jm',
+ name: 'vlldvknkn3t',
+ type: 'string',
+ title: '{{t("Role UID")}}',
+ 'x-component': 'Input',
+ },
+ prefix: 'r_',
+ primaryKey: true,
+ },
+ {
+ key: 'w0hvovoxnjh',
+ name: 'title',
+ type: 'string',
+ interface: 'input',
+ collectionName: 'roles',
+ parentKey: null,
+ reverseKey: null,
+ uiSchemaUid: 'pyq24tp0zkj',
+ uiSchema: {
+ 'x-uid': 'pyq24tp0zkj',
+ name: 'wtku9z07h69',
+ type: 'string',
+ title: '{{t("Role name")}}',
+ 'x-component': 'Input',
+ },
+ unique: true,
+ },
+ {
+ key: 'lxysmomsdhy',
+ name: 'description',
+ type: 'string',
+ interface: null,
+ collectionName: 'roles',
+ parentKey: null,
+ reverseKey: null,
+ uiSchemaUid: null,
+ uiSchema: {},
+ },
+ {
+ key: 'ynyhe4kgxku',
+ name: 'strategy',
+ type: 'json',
+ interface: null,
+ collectionName: 'roles',
+ parentKey: null,
+ reverseKey: null,
+ uiSchemaUid: null,
+ uiSchema: {},
+ },
+ {
+ key: 'qr9949ps9dv',
+ name: 'default',
+ type: 'boolean',
+ interface: null,
+ collectionName: 'roles',
+ parentKey: null,
+ reverseKey: null,
+ uiSchemaUid: null,
+ uiSchema: {},
+ defaultValue: false,
+ },
+ {
+ key: '11ts4nwrm8c',
+ name: 'allowConfigure',
+ type: 'boolean',
+ interface: null,
+ collectionName: 'roles',
+ parentKey: null,
+ reverseKey: null,
+ uiSchemaUid: null,
+ uiSchema: {},
+ },
+ {
+ key: 'nxj4s7xev9g',
+ name: 'allowNewMenu',
+ type: 'boolean',
+ interface: null,
+ collectionName: 'roles',
+ parentKey: null,
+ reverseKey: null,
+ uiSchemaUid: null,
+ uiSchema: {},
+ },
+ {
+ key: 'ibwisd53xeg',
+ name: 'menuUiSchemas',
+ type: 'belongsToMany',
+ interface: null,
+ collectionName: 'roles',
+ parentKey: null,
+ reverseKey: null,
+ uiSchemaUid: null,
+ uiSchema: {},
+ target: 'uiSchemas',
+ targetKey: 'x-uid',
+ foreignKey: 'roleName',
+ sourceKey: 'name',
+ otherKey: 'uiSchemaXUid',
+ through: 'rolesUischemas',
+ },
+ {
+ key: '0vt1uoy8f47',
+ name: 'resources',
+ type: 'hasMany',
+ interface: null,
+ collectionName: 'roles',
+ parentKey: null,
+ reverseKey: null,
+ uiSchemaUid: null,
+ uiSchema: {},
+ target: 'rolesResources',
+ sourceKey: 'name',
+ targetKey: 'id',
+ foreignKey: 'roleName',
+ },
+ ],
+ autoGenId: false,
+ model: 'RoleModel',
+ filterTargetKey: 'name',
+ },
+ {
+ key: 'nhng5sgypw8',
+ name: 't_j6omof6tza8',
+ title: '任务',
+ inherit: false,
+ fields: [
+ {
+ key: 'aabs1ya9aux',
+ name: 'f_g8j5jvalqh0',
+ type: 'string',
+ interface: 'input',
+ collectionName: 't_j6omof6tza8',
+ parentKey: null,
+ reverseKey: null,
+ uiSchemaUid: 'tzb6h0tsbis',
+ uiSchema: {
+ 'x-uid': 'tzb6h0tsbis',
+ name: 'y94c05v2hp2',
+ type: 'string',
+ 'x-component': 'Input',
+ title: '名称',
+ enum: [],
+ },
+ },
+ {
+ key: 'kidn54kisne',
+ name: 'f_tegyd222bcc',
+ type: 'text',
+ interface: 'textarea',
+ collectionName: 't_j6omof6tza8',
+ parentKey: null,
+ reverseKey: null,
+ uiSchemaUid: 'p4uucjm3rrx',
+ uiSchema: {
+ 'x-uid': 'p4uucjm3rrx',
+ name: 'ri9ulk87owm',
+ type: 'string',
+ 'x-component': 'Input.TextArea',
+ title: '描述',
+ enum: [],
+ },
+ },
+ {
+ key: 'kwdiruosgsb',
+ name: 'f_u007sq2jg93',
+ type: 'belongsToMany',
+ interface: 'linkTo',
+ collectionName: 't_j6omof6tza8',
+ parentKey: null,
+ reverseKey: 's5w3l6pl1hn',
+ uiSchemaUid: 'iea79k3kmao',
+ uiSchema: {
+ 'x-uid': 'iea79k3kmao',
+ name: 'njked4wch3i',
+ 'x-component': 'RecordPicker',
+ 'x-component-props': { multiple: true, fieldNames: { label: 'id', value: 'id' } },
+ title: '负责人',
+ enum: [],
+ },
+ target: 'users',
+ targetKey: 'id',
+ sourceKey: 'id',
+ through: 't_7476m1wwsfe',
+ foreignKey: 'f_9zwkbo7dd18',
+ otherKey: 'f_q84aphtx7eb',
+ },
+ {
+ key: 'w7civak6gp3',
+ name: 'f_hpmvdltzs6m',
+ type: 'string',
+ interface: 'radioGroup',
+ collectionName: 't_j6omof6tza8',
+ parentKey: null,
+ reverseKey: null,
+ uiSchemaUid: '481uv9zj072',
+ uiSchema: {
+ 'x-uid': '481uv9zj072',
+ name: '8fyelcfuzfu',
+ enum: [
+ { value: '67snt3e6yld', label: '未开始', color: 'default' },
+ { value: 'ht963n365al', label: '进行中', color: 'green' },
+ { value: '2s2s8wzcnm0', label: '测试中', color: 'volcano' },
+ { value: 'irepvdzp1ac', label: '已完成', color: 'blue' },
+ ],
+ type: 'string',
+ 'x-component': 'Radio.Group',
+ title: '状态',
+ },
+ },
+ {
+ key: '9x87w5z0ea6',
+ name: 'f_jj9cyhron1d',
+ type: 'hasMany',
+ interface: 'subTable',
+ collectionName: 't_j6omof6tza8',
+ parentKey: null,
+ reverseKey: null,
+ uiSchemaUid: 'pndvx008mmn',
+ uiSchema: {
+ 'x-uid': 'pndvx008mmn',
+ name: 'l8xqzpkxj3p',
+ type: 'array',
+ 'x-component': 'Table',
+ 'x-component-props': {},
+ enum: [],
+ title: '子任务',
+ },
+ targetKey: 'id',
+ sourceKey: 'id',
+ foreignKey: 'f_yzivojrp6l8',
+ target: 't_ab12qiwruwk',
+ },
+ {
+ key: '3f4y1iq16ux',
+ name: 'f_ooar0pto2ko',
+ type: 'belongsToMany',
+ interface: 'attachment',
+ collectionName: 't_j6omof6tza8',
+ parentKey: null,
+ reverseKey: null,
+ uiSchemaUid: '5xf7izxjny5',
+ uiSchema: {
+ 'x-uid': '5xf7izxjny5',
+ name: '9az9003ijcm',
+ 'x-component-props': { multiple: true, action: 'attachments:upload' },
+ type: 'array',
+ 'x-component': 'Upload.Attachment',
+ title: '附件',
+ enum: [],
+ },
+ target: 'attachments',
+ targetKey: 'id',
+ sourceKey: 'id',
+ through: 't_5a7wh2qhk4l',
+ foreignKey: 'f_tqfsxclrexu',
+ otherKey: 'f_j2krlyq6oka',
+ },
+ {
+ key: '4um5urxskg0',
+ name: 'f_f7txg1oc3nt',
+ type: 'date',
+ interface: 'createdAt',
+ collectionName: 't_j6omof6tza8',
+ parentKey: null,
+ reverseKey: null,
+ uiSchemaUid: 'jjax6r7c84m',
+ uiSchema: {
+ 'x-uid': 'jjax6r7c84m',
+ name: 'clidfmw96u3',
+ 'x-component-props': { dateFormat: 'YYYY-MM-DD', showTime: true, timeFormat: 'HH:mm:ss' },
+ type: 'datetime',
+ title: '创建时间',
+ 'x-component': 'DatePicker',
+ 'x-read-pretty': true,
+ enum: [],
+ },
+ field: 'createdAt',
+ },
+ {
+ key: 'dnwqg2s3oph',
+ name: 'f_2dpc76bszit',
+ type: 'belongsTo',
+ interface: 'createdBy',
+ collectionName: 't_j6omof6tza8',
+ parentKey: null,
+ reverseKey: null,
+ uiSchemaUid: 'po3y2pc112z',
+ uiSchema: {
+ 'x-uid': 'po3y2pc112z',
+ name: 'ke2rte2zikm',
+ type: 'object',
+ title: '创建人',
+ 'x-component': 'RecordPicker',
+ 'x-component-props': { fieldNames: { value: 'id', label: 'nickname' } },
+ 'x-read-pretty': true,
+ enum: [],
+ },
+ target: 'users',
+ foreignKey: 'createdById',
+ targetKey: 'id',
+ },
+ {
+ key: '1sayjktagk3',
+ name: 'f_z27302tl2bf',
+ type: 'string',
+ interface: 'select',
+ collectionName: 't_j6omof6tza8',
+ parentKey: null,
+ reverseKey: null,
+ uiSchemaUid: '0a28rxnov8o',
+ uiSchema: {
+ 'x-uid': '0a28rxnov8o',
+ name: '2vg44ojdq4j',
+ enum: [
+ { value: 'mkvgt7ndsqd', label: '缺陷', color: 'volcano' },
+ { value: '9yyv2tawmry', label: '交互', color: 'green' },
+ { value: 'vx4bhmtsuus', label: '需求', color: 'cyan' },
+ ],
+ type: 'string',
+ 'x-component': 'Select',
+ title: '类型',
+ },
+ },
+ {
+ key: 'zhvtzwta7bz',
+ name: 'f_yc8jbfiqfvh',
+ type: 'string',
+ interface: 'radioGroup',
+ collectionName: 't_j6omof6tza8',
+ parentKey: null,
+ reverseKey: null,
+ uiSchemaUid: 'jijqs397op9',
+ uiSchema: {
+ 'x-uid': 'jijqs397op9',
+ name: '99v9hnujiuh',
+ enum: [
+ { value: 's7hocw6twfk', label: '高', color: 'red' },
+ { value: 'v25527dxseu', label: '中', color: 'blue' },
+ { value: 'c4hobfb5k07', label: '低', color: 'lime' },
+ ],
+ type: 'string',
+ 'x-component': 'Radio.Group',
+ title: '优先级',
+ },
+ },
+ {
+ key: 'nobsmkp6ypg',
+ name: 'f_ksgzy9vmgce',
+ type: 'belongsToMany',
+ interface: 'linkTo',
+ collectionName: 't_j6omof6tza8',
+ parentKey: null,
+ reverseKey: 'z70jtfabrm7',
+ uiSchemaUid: '3gbviwfd0cv',
+ uiSchema: {
+ 'x-uid': '3gbviwfd0cv',
+ name: '6sabm3win42',
+ 'x-component': 'RecordPicker',
+ 'x-component-props': { multiple: false, fieldNames: { label: 'id', value: 'id' } },
+ title: '迭代',
+ enum: [],
+ },
+ target: 't_94rsj6kbzvn',
+ targetKey: 'id',
+ sourceKey: 'id',
+ through: 't_ewdf2dsjlu3',
+ foreignKey: 'f_72tnpdz6kfg',
+ otherKey: 'f_8do8o1wsavk',
+ },
+ {
+ key: 'rwtqeecnbst',
+ name: 'f_zek99qhv0vc',
+ type: 'date',
+ interface: 'updatedAt',
+ collectionName: 't_j6omof6tza8',
+ parentKey: null,
+ reverseKey: null,
+ uiSchemaUid: 'df0kmwka9ra',
+ uiSchema: {
+ 'x-uid': 'df0kmwka9ra',
+ name: 'e3hmz8upyle',
+ 'x-component-props': { dateFormat: 'YYYY-MM-DD', showTime: true, timeFormat: 'HH:mm:ss' },
+ type: 'datetime',
+ title: '更新时间',
+ 'x-component': 'DatePicker',
+ 'x-read-pretty': true,
+ },
+ field: 'updatedAt',
+ },
+ {
+ key: 'tm66hbii6zz',
+ name: 'f_cht6rsiiiko',
+ type: 'string',
+ interface: 'select',
+ collectionName: 't_j6omof6tza8',
+ parentKey: null,
+ reverseKey: null,
+ uiSchemaUid: 'tkfjlde4ppl',
+ uiSchema: {
+ 'x-uid': 'tkfjlde4ppl',
+ name: 'p0rccyurzle',
+ enum: [
+ { value: 'uduxfqpr5t2', label: '菜单' },
+ { value: 'dnphxkwat35', label: '权限' },
+ { value: '5cso9lpizog', label: '数据表' },
+ { value: 'wlg329nsu3a', label: '字段' },
+ { value: 'm8z7r06vk9z', label: '表格' },
+ { value: 'ftfccbha25a', label: '表单' },
+ { value: 'o33ljfkje2h', label: '详情' },
+ { value: 'ym9wwk3aqbu', label: '日历' },
+ { value: 'wge57qtiz14', label: '看板' },
+ { value: 'm4xoc8rbafg', label: '操作' },
+ { value: 'r6l2wg1snxc', label: '配置' },
+ { value: 'j1cv1vf16fm', label: '附件' },
+ { value: 'l464jdsy7vr', label: '容器' },
+ ],
+ type: 'string',
+ 'x-component': 'Select',
+ title: '分类',
+ },
+ },
+ {
+ key: 'c9hzonl1hzq',
+ name: 'f_47f2d9wgofm',
+ type: 'belongsToMany',
+ interface: 'linkTo',
+ collectionName: 't_j6omof6tza8',
+ parentKey: null,
+ reverseKey: '7mufig9muag',
+ uiSchemaUid: 'rbl27ngs563',
+ uiSchema: {
+ 'x-uid': 'rbl27ngs563',
+ name: 'xrvwttiv064',
+ 'x-component': 'RecordPicker',
+ 'x-component-props': { multiple: true, fieldNames: { label: 'id', value: 'id' } },
+ title: '相关任务',
+ },
+ target: 't_j6omof6tza8',
+ targetKey: 'id',
+ sourceKey: 'id',
+ through: 't_qhgbwomd2t7',
+ foreignKey: 'f_jes256zqwr1',
+ otherKey: 'f_gtgjj059mye',
+ },
+ {
+ key: '7mufig9muag',
+ name: 'f_qmlomqm7lvb',
+ type: 'belongsToMany',
+ interface: 'linkTo',
+ collectionName: 't_j6omof6tza8',
+ parentKey: null,
+ reverseKey: 'c9hzonl1hzq',
+ uiSchemaUid: 'vgy9cgq9v6b',
+ uiSchema: {
+ 'x-uid': 'vgy9cgq9v6b',
+ name: 'sf99arn65jj',
+ 'x-component': 'RecordPicker',
+ 'x-component-props': { multiple: true, fieldNames: { label: 'id', value: 'id' } },
+ title: '任务',
+ },
+ target: 't_j6omof6tza8',
+ through: 't_qhgbwomd2t7',
+ sourceKey: 'id',
+ foreignKey: 'f_gtgjj059mye',
+ targetKey: 'id',
+ otherKey: 'f_jes256zqwr1',
+ },
+ {
+ key: 'hv3kx2z8edn',
+ name: 'f_hpmvdltzs6m_sort',
+ type: 'sort',
+ interface: null,
+ collectionName: 't_j6omof6tza8',
+ parentKey: null,
+ reverseKey: null,
+ uiSchemaUid: null,
+ uiSchema: {},
+ hidden: true,
+ scopeKey: 'f_hpmvdltzs6m',
+ },
+ {
+ key: 'q6wy6jcpdfr',
+ name: 'f_a4z4h45vi5b',
+ type: 'string',
+ interface: 'select',
+ collectionName: 't_j6omof6tza8',
+ parentKey: null,
+ reverseKey: null,
+ uiSchemaUid: 'lx4k023vm7k',
+ uiSchema: {
+ 'x-uid': 'lx4k023vm7k',
+ name: 'e9u95lm0c2q',
+ enum: [
+ { value: 'hgobsqbfnd4', label: 'SchemaInitializer', color: 'red' },
+ { value: 't8prrpfk7kl', label: 'SchemaSettings', color: 'magenta' },
+ { value: 'r3fxlrunwd8', label: 'SchemaComponent', color: 'lime' },
+ { value: 'bidogljo0l3', label: 'CollectionField', color: 'blue' },
+ { value: 'ygy1zfdr1z6', label: 'ACL', color: 'purple' },
+ { value: 'ef9qf0wzpt5', label: 'DndContext', color: 'cyan' },
+ { value: '45sfwd7ie30', label: 'useRequest', color: 'volcano' },
+ ],
+ type: 'string',
+ 'x-component': 'Select',
+ title: '开发分类',
+ },
+ },
+ {
+ key: '936iis76zdx',
+ name: 'f_a4z4h45vi5b_sort',
+ type: 'sort',
+ interface: null,
+ collectionName: 't_j6omof6tza8',
+ parentKey: null,
+ reverseKey: null,
+ uiSchemaUid: null,
+ uiSchema: {},
+ hidden: true,
+ scopeKey: 'f_a4z4h45vi5b',
+ },
+ {
+ key: 'zre21o0a5a9',
+ name: 'f_d93g4r08krl',
+ type: 'string',
+ interface: 'input',
+ collectionName: 't_j6omof6tza8',
+ parentKey: null,
+ reverseKey: null,
+ uiSchemaUid: '96geljdh9j6',
+ uiSchema: {
+ 'x-uid': '96geljdh9j6',
+ name: 'zplxa37mb0y',
+ type: 'string',
+ 'x-component': 'Input',
+ title: '评估',
+ enum: [],
+ },
+ },
+ {
+ key: 'jasdf0kcqbs',
+ name: 'f_1ckuegfab9s',
+ type: 'text',
+ interface: 'textarea',
+ collectionName: 't_j6omof6tza8',
+ parentKey: null,
+ reverseKey: null,
+ uiSchemaUid: '61h9pserdqr',
+ uiSchema: {
+ 'x-uid': '61h9pserdqr',
+ name: 'uepfpdv289y',
+ type: 'string',
+ 'x-component': 'Input.TextArea',
+ title: '评估备注',
+ },
+ },
+ ],
+ createdBy: true,
+ updatedBy: true,
+ sortable: true,
+ },
+ {
+ key: 'nlxapox70a3',
+ name: 't_ab12qiwruwk',
+ title: null,
+ inherit: true,
+ fields: [
+ {
+ key: 'q9xx9ixxxyb',
+ name: 'f_m7ibo1vrvnm',
+ type: 'string',
+ interface: 'input',
+ collectionName: 't_ab12qiwruwk',
+ parentKey: '9x87w5z0ea6',
+ reverseKey: null,
+ uiSchemaUid: 'ucn6wm03zrq',
+ uiSchema: {
+ 'x-uid': 'ucn6wm03zrq',
+ name: 'k07s9nhhj6c',
+ type: 'string',
+ 'x-component': 'Input',
+ title: '名称',
+ },
+ },
+ {
+ key: 'y91qig4eyzm',
+ name: 'f_kukaw9kddyj',
+ type: 'belongsToMany',
+ interface: 'linkTo',
+ collectionName: 't_ab12qiwruwk',
+ parentKey: '9x87w5z0ea6',
+ reverseKey: 'tvwwt24vukv',
+ uiSchemaUid: '7c9ch1fknbr',
+ uiSchema: {
+ 'x-uid': '7c9ch1fknbr',
+ name: 'gv1k2nhucte',
+ 'x-component': 'RecordPicker',
+ 'x-component-props': { multiple: false, fieldNames: { label: 'id', value: 'id' } },
+ title: '负责人',
+ },
+ target: 'users',
+ targetKey: 'id',
+ sourceKey: 'id',
+ through: 't_gsv43y8ebt8',
+ foreignKey: 'f_hbbvlsbe9gs',
+ otherKey: 'f_bhj1xy7uyc8',
+ },
+ {
+ key: 'gg0xx1vln6w',
+ name: 'f_4mpiovytw4d',
+ type: 'text',
+ interface: 'textarea',
+ collectionName: 't_ab12qiwruwk',
+ parentKey: '9x87w5z0ea6',
+ reverseKey: null,
+ uiSchemaUid: '3xgmf0ee5fn',
+ uiSchema: {
+ 'x-uid': '3xgmf0ee5fn',
+ name: '28sf67whpfs',
+ type: 'string',
+ 'x-component': 'Input.TextArea',
+ title: '描述',
+ },
+ },
+ {
+ key: 'zpz6o74z9u4',
+ name: 'f_lxsum89wkzd',
+ type: 'belongsToMany',
+ interface: 'attachment',
+ collectionName: 't_ab12qiwruwk',
+ parentKey: '9x87w5z0ea6',
+ reverseKey: null,
+ uiSchemaUid: 'wosew16td91',
+ uiSchema: {
+ 'x-uid': 'wosew16td91',
+ name: 'p82ihvtkxtf',
+ 'x-component-props': { multiple: true, action: 'attachments:upload' },
+ type: 'array',
+ 'x-component': 'Upload.Attachment',
+ title: '附件',
+ },
+ target: 'attachments',
+ targetKey: 'id',
+ sourceKey: 'id',
+ through: 't_cn6cweinuw7',
+ foreignKey: 'f_r824sp05l43',
+ otherKey: 'f_8k3e4i0d04y',
+ },
+ ],
+ createdBy: true,
+ updatedBy: true,
+ sortable: true,
+ },
+ {
+ key: 'oftbzt8nm2o',
+ name: 'users',
+ title: '{{t("Users")}}',
+ inherit: false,
+ fields: [
+ {
+ key: 'khov1egnsur',
+ name: 'nickname',
+ type: 'string',
+ interface: 'input',
+ collectionName: 'users',
+ parentKey: null,
+ reverseKey: null,
+ uiSchemaUid: '3ohfk46bf7o',
+ uiSchema: {
+ 'x-uid': '3ohfk46bf7o',
+ name: 'prmmf3kg8rx',
+ type: 'string',
+ title: '{{t("Nickname")}}',
+ 'x-component': 'Input',
+ },
+ },
+ {
+ key: 'u6cvmn45o6a',
+ name: 'email',
+ type: 'string',
+ interface: 'email',
+ collectionName: 'users',
+ parentKey: null,
+ reverseKey: null,
+ uiSchemaUid: '69ywpwux1zh',
+ uiSchema: {
+ 'x-uid': '69ywpwux1zh',
+ name: 'u8djuprwttj',
+ type: 'string',
+ title: '{{t("Email")}}',
+ 'x-component': 'Input',
+ require: true,
+ },
+ unique: true,
+ },
+ {
+ key: 'jwaky9982f2',
+ name: 'password',
+ type: 'password',
+ interface: 'password',
+ collectionName: 'users',
+ parentKey: null,
+ reverseKey: null,
+ uiSchemaUid: 'qs374baqkh0',
+ uiSchema: {
+ 'x-uid': 'qs374baqkh0',
+ name: 'tf5dolxh2i4',
+ type: 'string',
+ title: '{{t("Password")}}',
+ 'x-component': 'Password',
+ },
+ hidden: true,
+ },
+ {
+ key: '4g5nics20go',
+ name: 'roles',
+ type: 'belongsToMany',
+ interface: 'linkTo',
+ collectionName: 'users',
+ parentKey: null,
+ reverseKey: null,
+ uiSchemaUid: 'lpkacakd4my',
+ uiSchema: {
+ 'x-uid': 'lpkacakd4my',
+ name: '3oylvillmwu',
+ type: 'array',
+ title: '{{t("Roles")}}',
+ 'x-component': 'RecordPicker',
+ 'x-component-props': { multiple: true, fieldNames: { label: 'title', value: 'name' } },
+ },
+ target: 'roles',
+ foreignKey: 'userId',
+ otherKey: 'roleName',
+ sourceKey: 'id',
+ targetKey: 'name',
+ through: 'rolesUsers',
+ },
+ {
+ key: 'cqqhyq32tvf',
+ name: 'appLang',
+ type: 'string',
+ interface: null,
+ collectionName: 'users',
+ parentKey: null,
+ reverseKey: null,
+ uiSchemaUid: null,
+ uiSchema: {},
+ },
+ {
+ key: 't7t97s1rwid',
+ name: 'token',
+ type: 'string',
+ interface: null,
+ collectionName: 'users',
+ parentKey: null,
+ reverseKey: null,
+ uiSchemaUid: null,
+ uiSchema: {},
+ unique: true,
+ hidden: true,
+ },
+ {
+ key: '5myliungho1',
+ name: 'resetToken',
+ type: 'string',
+ interface: null,
+ collectionName: 'users',
+ parentKey: null,
+ reverseKey: null,
+ uiSchemaUid: null,
+ uiSchema: {},
+ unique: true,
+ hidden: true,
+ },
+ {
+ key: 'q2eg83quxls',
+ name: 'sort',
+ type: 'sort',
+ interface: null,
+ collectionName: 'users',
+ parentKey: null,
+ reverseKey: null,
+ uiSchemaUid: null,
+ uiSchema: {},
+ hidden: true,
+ },
+ {
+ key: 's5w3l6pl1hn',
+ name: 'f_xd4v2uljqcu',
+ type: 'belongsToMany',
+ interface: 'linkTo',
+ collectionName: 'users',
+ parentKey: null,
+ reverseKey: 'kwdiruosgsb',
+ uiSchemaUid: 'zv4gvihowwm',
+ uiSchema: {
+ 'x-uid': 'zv4gvihowwm',
+ name: 'mjjpnphkkhf',
+ 'x-component': 'RecordPicker',
+ 'x-component-props': { multiple: true, fieldNames: { label: 'id', value: 'id' } },
+ title: '负责人',
+ },
+ target: 't_j6omof6tza8',
+ through: 't_7476m1wwsfe',
+ sourceKey: 'id',
+ foreignKey: 'f_q84aphtx7eb',
+ targetKey: 'id',
+ otherKey: 'f_9zwkbo7dd18',
+ },
+ {
+ key: 'tvwwt24vukv',
+ name: 'f_qpsqy5rtc8t',
+ type: 'belongsToMany',
+ interface: 'linkTo',
+ collectionName: 'users',
+ parentKey: null,
+ reverseKey: 'y91qig4eyzm',
+ uiSchemaUid: '5ksz03zw47c',
+ uiSchema: {
+ 'x-uid': '5ksz03zw47c',
+ name: '8ukjiawzduz',
+ 'x-component': 'RecordPicker',
+ 'x-component-props': { multiple: true, fieldNames: { label: 'id', value: 'id' } },
+ title: '负责人',
+ },
+ target: 't_ab12qiwruwk',
+ through: 't_gsv43y8ebt8',
+ sourceKey: 'id',
+ foreignKey: 'f_bhj1xy7uyc8',
+ targetKey: 'id',
+ otherKey: 'f_hbbvlsbe9gs',
+ },
+ ],
+ sortable: 'sort',
+ },
+ {
+ key: 'tyztw0ulriv',
+ name: 't_94rsj6kbzvn',
+ title: '迭代',
+ inherit: false,
+ fields: [
+ {
+ key: 'nq4mp00jwip',
+ name: 'f_zio9ewkxss7',
+ type: 'string',
+ interface: 'input',
+ collectionName: 't_94rsj6kbzvn',
+ parentKey: null,
+ reverseKey: null,
+ uiSchemaUid: 'q7vois7ptlt',
+ uiSchema: {
+ 'x-uid': 'q7vois7ptlt',
+ name: 'o9ue1s4tu8k',
+ type: 'string',
+ 'x-component': 'Input',
+ title: '名称',
+ enum: [],
+ },
+ },
+ {
+ key: 'ec2833af7n1',
+ name: 'f_ojboh2wxpju',
+ type: 'text',
+ interface: 'textarea',
+ collectionName: 't_94rsj6kbzvn',
+ parentKey: null,
+ reverseKey: null,
+ uiSchemaUid: 'pco1f8sq2jq',
+ uiSchema: {
+ 'x-uid': 'pco1f8sq2jq',
+ name: 'lb2hdydextn',
+ type: 'string',
+ 'x-component': 'Input.TextArea',
+ title: '描述',
+ enum: [],
+ },
+ },
+ {
+ key: '82uqmdjr3v4',
+ name: 'f_nunmzapigvk',
+ type: 'date',
+ interface: 'datetime',
+ collectionName: 't_94rsj6kbzvn',
+ parentKey: null,
+ reverseKey: null,
+ uiSchemaUid: '2gawk8arctt',
+ uiSchema: {
+ 'x-uid': '2gawk8arctt',
+ name: 'j9j7c2wyev2',
+ 'x-component-props': { dateFormat: 'YYYY-MM-DD', showTime: false },
+ type: 'datetime',
+ 'x-component': 'DatePicker',
+ title: '开始日期',
+ enum: [],
+ },
+ },
+ {
+ key: 'mdx2eqz9ei8',
+ name: 'f_rberbnphu9u',
+ type: 'date',
+ interface: 'datetime',
+ collectionName: 't_94rsj6kbzvn',
+ parentKey: null,
+ reverseKey: null,
+ uiSchemaUid: 'pj0znpff63y',
+ uiSchema: {
+ 'x-uid': 'pj0znpff63y',
+ name: 'c5h7agr2qy4',
+ 'x-component-props': { dateFormat: 'YYYY-MM-DD', showTime: false },
+ type: 'datetime',
+ 'x-component': 'DatePicker',
+ title: '结束日期',
+ enum: [],
+ },
+ },
+ {
+ key: 'z70jtfabrm7',
+ name: 'f_rabhmdetc3p',
+ type: 'belongsToMany',
+ interface: 'linkTo',
+ collectionName: 't_94rsj6kbzvn',
+ parentKey: null,
+ reverseKey: null,
+ uiSchemaUid: 'aun2xsfua8d',
+ uiSchema: {
+ 'x-uid': 'aun2xsfua8d',
+ name: 'i508c1outz2',
+ 'x-component': 'RecordPicker',
+ 'x-component-props': { multiple: true, fieldNames: { label: 'id', value: 'id' } },
+ title: '任务',
+ enum: [],
+ },
+ target: 't_j6omof6tza8',
+ through: 't_ewdf2dsjlu3',
+ sourceKey: 'id',
+ foreignKey: 'f_8do8o1wsavk',
+ targetKey: 'id',
+ otherKey: 'f_72tnpdz6kfg',
+ },
+ ],
+ createdBy: true,
+ updatedBy: true,
+ sortable: true,
+ },
+ {
+ key: 'vv8umfa9592',
+ name: 'test_sheet',
+ title: '测试表',
+ inherit: false,
+ fields: [
+ {
+ key: 'q1xye1ooeol',
+ name: 'f_7mh6k6re0ey',
+ type: 'string',
+ interface: 'phone',
+ collectionName: 'test_sheet',
+ parentKey: null,
+ reverseKey: null,
+ uiSchemaUid: 't9ddc5n5pv8',
+ uiSchema: {
+ 'x-uid': 't9ddc5n5pv8',
+ name: 'd3hwsg020u8',
+ type: 'string',
+ 'x-component': 'Input',
+ 'x-validator': 'phone',
+ title: '手机',
+ },
+ },
+ {
+ key: '25icwn35oin',
+ name: 'f_turkjlzi52z',
+ type: 'string',
+ interface: 'email',
+ collectionName: 'test_sheet',
+ parentKey: null,
+ reverseKey: null,
+ uiSchemaUid: '3bkgd97f63v',
+ uiSchema: {
+ 'x-uid': '3bkgd97f63v',
+ name: 'zkvhu5cm4no',
+ type: 'string',
+ 'x-component': 'Input',
+ 'x-validator': 'email',
+ title: '邮箱',
+ },
+ },
+ {
+ key: '3cqe0mpcrmg',
+ name: 'f_c14whhblhtd',
+ type: 'float',
+ interface: 'number',
+ collectionName: 'test_sheet',
+ parentKey: null,
+ reverseKey: null,
+ uiSchemaUid: '4bajzziz060',
+ uiSchema: {
+ 'x-uid': '4bajzziz060',
+ name: '366t9rvl34b',
+ 'x-component-props': { step: '0.01', stringMode: true },
+ type: 'number',
+ 'x-component': 'InputNumber',
+ title: '数字',
+ },
+ },
+ {
+ key: 'y39vi1z69pw',
+ name: 'f_z1b674d907e',
+ type: 'float',
+ interface: 'percent',
+ collectionName: 'test_sheet',
+ parentKey: null,
+ reverseKey: null,
+ uiSchemaUid: 'vbnoib14uzl',
+ uiSchema: {
+ 'x-uid': 'vbnoib14uzl',
+ name: 'rumohzj4lh6',
+ 'x-component-props': { step: '0.1', stringMode: true, addonAfter: '%' },
+ type: 'string',
+ 'x-component': 'InputNumber',
+ title: '百分比',
+ },
+ },
+ {
+ key: 'eouuwoag6ka',
+ name: 'f_4tzp4nhmvt2',
+ type: 'string',
+ interface: 'icon',
+ collectionName: 'test_sheet',
+ parentKey: null,
+ reverseKey: null,
+ uiSchemaUid: 'qmmw3zc8l60',
+ uiSchema: {
+ 'x-uid': 'qmmw3zc8l60',
+ name: 'aivg5c51gc9',
+ type: 'string',
+ 'x-component': 'IconPicker',
+ title: '图标',
+ },
+ },
+ {
+ key: 'qurfmfe2hfi',
+ name: 'f_z0s4m3hgadd',
+ type: 'password',
+ interface: 'password',
+ collectionName: 'test_sheet',
+ parentKey: null,
+ reverseKey: null,
+ uiSchemaUid: 'tkp46kn682m',
+ uiSchema: {
+ 'x-uid': 'tkp46kn682m',
+ name: '7q7yeobot0j',
+ type: 'string',
+ 'x-component': 'Password',
+ title: '密码',
+ },
+ },
+ {
+ key: 'ka4k5tfh3k5',
+ name: 'f_q5qnozezx9s',
+ type: 'boolean',
+ interface: 'checkbox',
+ collectionName: 'test_sheet',
+ parentKey: null,
+ reverseKey: null,
+ uiSchemaUid: 'wgyf4ozsvdi',
+ uiSchema: {
+ 'x-uid': 'wgyf4ozsvdi',
+ name: 'x6agce8s18w',
+ type: 'boolean',
+ 'x-component': 'Checkbox',
+ title: '勾选',
+ },
+ },
+ {
+ key: '3dhczyw3ye2',
+ name: 'f_jbzwvldpyaa',
+ type: 'array',
+ interface: 'multipleSelect',
+ collectionName: 'test_sheet',
+ parentKey: null,
+ reverseKey: null,
+ uiSchemaUid: 'hlmfjs2pu9t',
+ uiSchema: {
+ 'x-uid': 'hlmfjs2pu9t',
+ name: 'ao52nx5pune',
+ enum: [
+ { value: '6mbqdwuzfca', label: '选项1', color: 'red' },
+ { value: '9zi6s2fbj0q', label: '选项2', color: 'geekblue' },
+ ],
+ type: 'array',
+ 'x-component': 'Select',
+ 'x-component-props': { mode: 'multiple' },
+ title: '多选',
+ },
+ defaultValue: [],
+ },
+ {
+ key: 'erdrp1gi587',
+ name: 'f_04krym08wxq',
+ type: 'array',
+ interface: 'checkboxGroup',
+ collectionName: 'test_sheet',
+ parentKey: null,
+ reverseKey: null,
+ uiSchemaUid: '6has8wyzohq',
+ uiSchema: {
+ 'x-uid': '6has8wyzohq',
+ name: 'fvwsjt58xx4',
+ enum: [
+ { value: '4lsgdvfl7xq', label: '复选2', color: 'cyan' },
+ { value: 'qzuki8bmilg', label: '复选1', color: 'purple' },
+ ],
+ type: 'string',
+ 'x-component': 'Checkbox.Group',
+ title: '复选',
+ },
+ defaultValue: [],
+ },
+ {
+ key: 'rykbub69xu7',
+ name: 'f_xclbl3htmxs',
+ type: 'belongsToMany',
+ interface: 'chinaRegion',
+ collectionName: 'test_sheet',
+ parentKey: null,
+ reverseKey: null,
+ uiSchemaUid: 'yml5yqzgvn8',
+ uiSchema: {
+ 'x-uid': 'yml5yqzgvn8',
+ name: '2ye7y09agup',
+ 'x-component-props': {
+ maxLevel: 5,
+ useDataSource: '{{ useChinaRegionDataSource }}',
+ useLoadData: '{{ useChinaRegionLoadData }}',
+ changeOnSelectLast: true,
+ labelInValue: true,
+ fieldNames: { label: 'name', value: 'code', children: 'children' },
+ },
+ type: 'array',
+ 'x-component': 'Cascader',
+ title: '中国地区',
+ },
+ target: 'chinaRegions',
+ targetKey: 'code',
+ sortBy: 'level',
+ sourceKey: 'id',
+ through: 't_o9ry49u6xez',
+ foreignKey: 'f_44r8nsvdvz7',
+ otherKey: 'f_l0tzoymin3i',
+ },
+ {
+ key: 'acm789iz922',
+ name: 'f_v4cgx7l5hwm',
+ type: 'text',
+ interface: 'markdown',
+ collectionName: 'test_sheet',
+ parentKey: null,
+ reverseKey: null,
+ uiSchemaUid: '8a2erdhwcvb',
+ uiSchema: {
+ 'x-uid': '8a2erdhwcvb',
+ name: 'rqeiz26vuns',
+ type: 'string',
+ 'x-component': 'Markdown',
+ title: 'Markdown',
+ },
+ },
+ {
+ key: 'zmip3swglzv',
+ name: 'f_ztaop411cdw',
+ type: 'time',
+ interface: 'time',
+ collectionName: 'test_sheet',
+ parentKey: null,
+ reverseKey: null,
+ uiSchemaUid: 'r6s6rrjzfml',
+ uiSchema: {
+ 'x-uid': 'r6s6rrjzfml',
+ name: 'ct893ay5apv',
+ 'x-component-props': { format: 'HH:mm:ss' },
+ type: 'string',
+ 'x-component': 'TimePicker',
+ title: '时间',
+ },
+ },
+ {
+ key: 'nx1p69b6g96',
+ name: 'f_mf8ccm0pjmh',
+ type: 'date',
+ interface: 'datetime',
+ collectionName: 'test_sheet',
+ parentKey: null,
+ reverseKey: null,
+ uiSchemaUid: '08p9oztyjf2',
+ uiSchema: {
+ 'x-uid': '08p9oztyjf2',
+ name: 'bkdnndmfpeo',
+ 'x-component-props': { dateFormat: 'DD/MM/YYYY', showTime: true, timeFormat: 'HH:mm:ss' },
+ type: 'datetime',
+ 'x-component': 'DatePicker',
+ title: '日期',
+ enum: [],
+ },
+ },
+ {
+ key: 'ge57kma4gue',
+ name: 'f_dqq0xjkdqbo',
+ type: 'belongsTo',
+ interface: 'createdBy',
+ collectionName: 'test_sheet',
+ parentKey: null,
+ reverseKey: null,
+ uiSchemaUid: 'zzmst1r01gc',
+ uiSchema: {
+ 'x-uid': 'zzmst1r01gc',
+ name: 'def1ngennlx',
+ type: 'object',
+ title: '创建人',
+ 'x-component': 'RecordPicker',
+ 'x-component-props': { fieldNames: { value: 'id', label: 'nickname' } },
+ 'x-read-pretty': true,
+ },
+ target: 'users',
+ foreignKey: 'createdById',
+ targetKey: 'id',
+ },
+ {
+ key: 'yqye22n1f1c',
+ name: 'f_7ukoar9jevs',
+ type: 'date',
+ interface: 'createdAt',
+ collectionName: 'test_sheet',
+ parentKey: null,
+ reverseKey: null,
+ uiSchemaUid: 'lgnqb28lb91',
+ uiSchema: {
+ 'x-uid': 'lgnqb28lb91',
+ name: 'jdjz5cngm3j',
+ 'x-component-props': { dateFormat: 'YYYY-MM-DD', showTime: true, timeFormat: 'HH:mm:ss' },
+ type: 'datetime',
+ title: '创建日期',
+ 'x-component': 'DatePicker',
+ 'x-read-pretty': true,
+ },
+ field: 'createdAt',
+ },
+ {
+ key: 'n4ap9l1l4wd',
+ name: 'f_81orjgxlbuo',
+ type: 'date',
+ interface: 'updatedAt',
+ collectionName: 'test_sheet',
+ parentKey: null,
+ reverseKey: null,
+ uiSchemaUid: 'z19egimonh9',
+ uiSchema: {
+ 'x-uid': 'z19egimonh9',
+ name: 'xtzu6cpnsm4',
+ 'x-component-props': { dateFormat: 'YYYY-MM-DD', showTime: true },
+ type: 'datetime',
+ title: '修改日期',
+ 'x-component': 'DatePicker',
+ 'x-read-pretty': true,
+ },
+ field: 'updatedAt',
+ },
+ {
+ key: 'oqy67fm6af8',
+ name: 'f_e31b9oqe9ej',
+ type: 'belongsTo',
+ interface: 'updatedBy',
+ collectionName: 'test_sheet',
+ parentKey: null,
+ reverseKey: null,
+ uiSchemaUid: 'tm4f7z0nyso',
+ uiSchema: {
+ 'x-uid': 'tm4f7z0nyso',
+ name: '19srjtwyce1',
+ type: 'object',
+ title: '修改人',
+ 'x-component': 'RecordPicker',
+ 'x-component-props': { fieldNames: { value: 'id', label: 'nickname' } },
+ 'x-read-pretty': true,
+ },
+ target: 'users',
+ foreignKey: 'updatedById',
+ targetKey: 'id',
+ },
+ ],
+ createdBy: true,
+ updatedBy: true,
+ sortable: true,
+ },
+ ],
+};
diff --git a/packages/client/src/schema-component/antd/calendar-v2/demos/data.ts b/packages/client/src/schema-component/antd/calendar-v2/demos/data.ts
new file mode 100644
index 000000000..51a0deb0a
--- /dev/null
+++ b/packages/client/src/schema-component/antd/calendar-v2/demos/data.ts
@@ -0,0 +1,131 @@
+export default {
+ data: [
+ {
+ id: 4,
+ createdAt: '2022-03-06T07:22:21.163Z',
+ updatedAt: '2022-03-12T04:34:19.147Z',
+ createdById: 1,
+ updatedById: 1,
+ f_1ckuegfab9s: null,
+ f_a4z4h45vi5b: 'hgobsqbfnd4',
+ f_cht6rsiiiko: 'ftfccbha25a',
+ f_d93g4r08krl: '1',
+ f_f7txg1oc3nt: '2022-03-07T07:22:21.163Z',
+ f_g8j5jvalqh0: '子表格字段缺少标题',
+ f_hpmvdltzs6m: null,
+ f_tegyd222bcc: '见附件,在此位置显示标题,包括表单和详情',
+ f_yc8jbfiqfvh: 'v25527dxseu',
+ f_z27302tl2bf: '9yyv2tawmry',
+ f_zek99qhv0vc: '2022-03-12T04:34:19.147Z',
+ },
+ {
+ id: 5,
+ createdAt: '2022-03-07T11:10:50.869Z',
+ updatedAt: '2022-03-12T04:34:37.792Z',
+ createdById: 1,
+ updatedById: 1,
+ f_1ckuegfab9s: null,
+ f_a4z4h45vi5b: 'r3fxlrunwd8',
+ f_cht6rsiiiko: 'ftfccbha25a',
+ f_d93g4r08krl: '2',
+ f_f7txg1oc3nt: '2022-03-07T11:10:50.869Z',
+ f_g8j5jvalqh0: '抽屉关闭时,如果内容已更改未保存,弹出提示',
+ f_hpmvdltzs6m: '67snt3e6yld',
+ f_tegyd222bcc: null,
+ f_yc8jbfiqfvh: 'c4hobfb5k07',
+ f_z27302tl2bf: 'mkvgt7ndsqd',
+ f_zek99qhv0vc: '2022-03-12T04:34:37.792Z',
+ },
+ {
+ id: 6,
+ createdAt: '2022-03-07T11:12:49.853Z',
+ updatedAt: '2022-03-11T15:37:00.663Z',
+ createdById: 1,
+ updatedById: 1,
+ f_1ckuegfab9s: null,
+ f_a4z4h45vi5b: 't8prrpfk7kl',
+ f_cht6rsiiiko: 'ftfccbha25a',
+ f_d93g4r08krl: null,
+ f_f7txg1oc3nt: '2022-03-07T11:12:49.853Z',
+ f_g8j5jvalqh0: '字段设置必填后,未验证必填',
+ f_hpmvdltzs6m: 'irepvdzp1ac',
+ f_tegyd222bcc: null,
+ f_yc8jbfiqfvh: 'v25527dxseu',
+ f_z27302tl2bf: 'mkvgt7ndsqd',
+ f_zek99qhv0vc: '2022-03-11T15:37:00.663Z',
+ },
+ {
+ id: 7,
+ createdAt: '2022-03-07T11:13:45.123Z',
+ updatedAt: '2022-03-10T16:02:25.805Z',
+ createdById: 1,
+ updatedById: 1,
+ f_1ckuegfab9s: null,
+ f_a4z4h45vi5b: null,
+ f_cht6rsiiiko: null,
+ f_d93g4r08krl: null,
+ f_f7txg1oc3nt: '2022-03-07T11:13:45.123Z',
+ f_g8j5jvalqh0: '菜单项移动功能',
+ f_hpmvdltzs6m: 'irepvdzp1ac',
+ f_tegyd222bcc: '同上一个版本',
+ f_yc8jbfiqfvh: 's7hocw6twfk',
+ f_z27302tl2bf: 'mkvgt7ndsqd',
+ f_zek99qhv0vc: '2022-03-10T16:02:25.805Z',
+ },
+ {
+ id: 8,
+ createdAt: '2022-03-07T11:16:15.876Z',
+ updatedAt: '2022-03-10T16:02:25.805Z',
+ createdById: 1,
+ updatedById: 1,
+ f_1ckuegfab9s: null,
+ f_a4z4h45vi5b: null,
+ f_cht6rsiiiko: null,
+ f_d93g4r08krl: null,
+ f_f7txg1oc3nt: '2022-03-07T11:16:15.876Z',
+ f_g8j5jvalqh0: '附件样式',
+ f_hpmvdltzs6m: 'irepvdzp1ac',
+ f_tegyd222bcc:
+ '修改后,有附件的行跟没附件的行的高度还是不一样\n\n1. 表格里显示更小的缩略图,同之前版本\n2. 还有子表格里\n3. 鼠标移上去之后的图标位置\n\n备注:行高不一的未解决,以后统一解决,需要给每个 td 加上字段类型的 className 方便调整不同字段的样式问题',
+ f_yc8jbfiqfvh: 's7hocw6twfk',
+ f_z27302tl2bf: '9yyv2tawmry',
+ f_zek99qhv0vc: '2022-03-10T16:02:25.805Z',
+ },
+ {
+ id: 9,
+ createdAt: '2022-03-07T11:17:11.423Z',
+ updatedAt: '2022-03-12T06:11:09.330Z',
+ createdById: 1,
+ updatedById: 1,
+ f_1ckuegfab9s: null,
+ f_a4z4h45vi5b: null,
+ f_cht6rsiiiko: null,
+ f_d93g4r08krl: null,
+ f_f7txg1oc3nt: '2022-03-07T11:17:11.423Z',
+ f_g8j5jvalqh0: '日志',
+ f_hpmvdltzs6m: 'ht963n365al',
+ f_tegyd222bcc: '同原来版本的日志功能。\n- ActionLogBlockInitializer;\n- ActionLog.Designer;',
+ f_yc8jbfiqfvh: 'v25527dxseu',
+ f_z27302tl2bf: 'mkvgt7ndsqd',
+ f_zek99qhv0vc: '2022-03-12T06:11:09.330Z',
+ },
+ {
+ id: 10,
+ createdAt: '2022-03-07T11:18:40.719Z',
+ updatedAt: '2022-03-12T04:24:31.402Z',
+ createdById: 1,
+ updatedById: 1,
+ f_1ckuegfab9s: null,
+ f_a4z4h45vi5b: null,
+ f_cht6rsiiiko: 'r6l2wg1snxc',
+ f_d93g4r08krl: null,
+ f_f7txg1oc3nt: '2022-03-07T11:18:40.719Z',
+ f_g8j5jvalqh0: '配置图标没有居中',
+ f_hpmvdltzs6m: 'irepvdzp1ac',
+ f_tegyd222bcc: null,
+ f_yc8jbfiqfvh: 'v25527dxseu',
+ f_z27302tl2bf: '9yyv2tawmry',
+ f_zek99qhv0vc: '2022-03-12T04:24:31.402Z',
+ },
+ ],
+};
diff --git a/packages/client/src/schema-component/antd/calendar-v2/demos/defaultValues.ts b/packages/client/src/schema-component/antd/calendar-v2/demos/defaultValues.ts
new file mode 100644
index 000000000..ab3a2f736
--- /dev/null
+++ b/packages/client/src/schema-component/antd/calendar-v2/demos/defaultValues.ts
@@ -0,0 +1,180 @@
+const now = new Date();
+
+export default [
+ {
+ id: 0,
+ title: 'All Day Event very long title',
+ allDay: true,
+ start: new Date(2015, 3, 0),
+ end: new Date(2015, 3, 1),
+ },
+ {
+ id: 1,
+ title: 'Long Event',
+ start: new Date(2015, 3, 7),
+ end: new Date(2015, 3, 10),
+ },
+
+ {
+ id: 2,
+ title: 'DTS STARTS',
+ start: new Date(2016, 2, 13, 0, 0, 0),
+ end: new Date(2016, 2, 20, 0, 0, 0),
+ },
+
+ {
+ id: 3,
+ title: 'DTS ENDS',
+ start: new Date(2016, 10, 6, 0, 0, 0),
+ end: new Date(2016, 10, 13, 0, 0, 0),
+ },
+
+ {
+ id: 4,
+ title: 'Some Event',
+ start: new Date(2015, 3, 9, 0, 0, 0),
+ end: new Date(2015, 3, 10, 0, 0, 0),
+ },
+ {
+ id: 5,
+ title: 'Conference',
+ start: new Date(2015, 3, 11),
+ end: new Date(2015, 3, 13),
+ desc: 'Big conference for important people',
+ },
+ {
+ id: 6,
+ title: 'Meeting',
+ start: new Date(2015, 3, 12, 10, 30, 0, 0),
+ end: new Date(2015, 3, 12, 12, 30, 0, 0),
+ desc: 'Pre-meeting meeting, to prepare for the meeting',
+ },
+ {
+ id: 7,
+ title: 'Lunch',
+ start: new Date(2015, 3, 12, 12, 0, 0, 0),
+ end: new Date(2015, 3, 12, 13, 0, 0, 0),
+ desc: 'Power lunch',
+ },
+ {
+ id: 8,
+ title: 'Meeting',
+ start: new Date(2015, 3, 12, 14, 0, 0, 0),
+ end: new Date(2015, 3, 12, 15, 0, 0, 0),
+ },
+ {
+ id: 9,
+ title: 'Happy Hour',
+ start: new Date(2015, 3, 12, 17, 0, 0, 0),
+ end: new Date(2015, 3, 12, 17, 30, 0, 0),
+ desc: 'Most important meal of the day',
+ },
+ {
+ id: 10,
+ title: 'Dinner',
+ start: new Date(2015, 3, 12, 20, 0, 0, 0),
+ end: new Date(2015, 3, 12, 21, 0, 0, 0),
+ },
+ {
+ id: 11,
+ title: 'Planning Meeting with Paige',
+ start: new Date(2015, 3, 13, 8, 0, 0),
+ end: new Date(2015, 3, 13, 10, 30, 0),
+ },
+ {
+ id: 11.1,
+ title: 'Inconvenient Conference Call',
+ start: new Date(2015, 3, 13, 9, 30, 0),
+ end: new Date(2015, 3, 13, 12, 0, 0),
+ },
+ {
+ id: 11.2,
+ title: "Project Kickoff - Lou's Shoes",
+ start: new Date(2015, 3, 13, 11, 30, 0),
+ end: new Date(2015, 3, 13, 14, 0, 0),
+ },
+ {
+ id: 11.3,
+ title: 'Quote Follow-up - Tea by Tina',
+ start: new Date(2015, 3, 13, 15, 30, 0),
+ end: new Date(2015, 3, 13, 16, 0, 0),
+ },
+ {
+ id: 12,
+ title: 'Late Night Event',
+ start: new Date(2015, 3, 17, 19, 30, 0),
+ end: new Date(2015, 3, 18, 2, 0, 0),
+ },
+ {
+ id: 12.5,
+ title: 'Late Same Night Event',
+ start: new Date(2015, 3, 17, 19, 30, 0),
+ end: new Date(2015, 3, 17, 23, 30, 0),
+ },
+ {
+ id: 13,
+ title: 'Multi-day Event',
+ start: new Date(2015, 3, 20, 19, 30, 0),
+ end: new Date(2015, 3, 22, 2, 0, 0),
+ },
+ {
+ id: 14,
+ title: 'Today',
+ start: new Date(new Date().setHours(new Date().getHours() - 3)),
+ end: new Date(new Date().setHours(new Date().getHours() + 3)),
+ },
+ {
+ id: 15,
+ title: 'Point in Time Event',
+ start: now,
+ end: now,
+ },
+ {
+ id: 16,
+ title: 'Video Record',
+ start: new Date(2015, 3, 14, 15, 30, 0),
+ end: new Date(2015, 3, 14, 19, 0, 0),
+ },
+ {
+ id: 17,
+ title: 'Dutch Song Producing',
+ start: new Date(2015, 3, 14, 16, 30, 0),
+ end: new Date(2015, 3, 14, 20, 0, 0),
+ },
+ {
+ id: 18,
+ title: 'Itaewon Halloween Meeting',
+ start: new Date(2015, 3, 14, 16, 30, 0),
+ end: new Date(2015, 3, 14, 17, 30, 0),
+ },
+ {
+ id: 19,
+ title: 'Online Coding Test',
+ start: new Date(2015, 3, 14, 17, 30, 0),
+ end: new Date(2015, 3, 14, 20, 30, 0),
+ },
+ {
+ id: 20,
+ title: 'An overlapped Event',
+ start: new Date(2015, 3, 14, 17, 0, 0),
+ end: new Date(2015, 3, 14, 18, 30, 0),
+ },
+ {
+ id: 21,
+ title: 'Phone Interview',
+ start: new Date(2015, 3, 14, 17, 0, 0),
+ end: new Date(2015, 3, 14, 18, 30, 0),
+ },
+ {
+ id: 22,
+ title: 'Cooking Class',
+ start: new Date(2015, 3, 14, 17, 30, 0),
+ end: new Date(2015, 3, 14, 19, 0, 0),
+ },
+ {
+ id: 23,
+ title: 'Go to the gym',
+ start: new Date(2015, 3, 14, 18, 30, 0),
+ end: new Date(2015, 3, 14, 20, 0, 0),
+ },
+];
diff --git a/packages/client/src/schema-component/antd/calendar-v2/demos/demo1.tsx b/packages/client/src/schema-component/antd/calendar-v2/demos/demo1.tsx
new file mode 100644
index 000000000..dd55494fd
--- /dev/null
+++ b/packages/client/src/schema-component/antd/calendar-v2/demos/demo1.tsx
@@ -0,0 +1,67 @@
+/**
+ * title: Calendar
+ */
+import {
+ AntdSchemaComponentProvider,
+ SchemaComponent,
+ SchemaComponentProvider,
+ SchemaInitializerProvider
+} from '@nocobase/client';
+import React from 'react';
+import defaultValues from './defaultValues';
+
+const schema = {
+ type: 'array',
+ name: 'calendar1',
+ 'x-component': 'CalendarV2',
+ 'x-component-props': {},
+ default: defaultValues,
+ properties: {
+ toolBar: {
+ type: 'void',
+ 'x-component': 'CalendarV2.ActionBar',
+ properties: {
+ today: {
+ type: 'void',
+ title: '今天',
+ 'x-component': 'CalendarV2.Today',
+ 'x-action': 'calendar:today',
+ 'x-align': 'left',
+ },
+ nav: {
+ type: 'void',
+ title: '翻页',
+ 'x-component': 'CalendarV2.Nav',
+ 'x-action': 'calendar:nav',
+ 'x-align': 'left',
+ },
+ title: {
+ type: 'void',
+ title: '标题',
+ 'x-component': 'CalendarV2.Title',
+ 'x-action': 'calendar:title',
+ 'x-align': 'left',
+ },
+ viewSelect: {
+ type: 'void',
+ title: '视图切换',
+ 'x-component': 'CalendarV2.ViewSelect',
+ 'x-action': 'calendar:viewSelect',
+ 'x-align': 'right',
+ },
+ },
+ },
+ },
+};
+
+export default () => {
+ return (
+
+
+
+
+
+
+
+ );
+};
diff --git a/packages/client/src/schema-component/antd/calendar-v2/demos/demo2.tsx b/packages/client/src/schema-component/antd/calendar-v2/demos/demo2.tsx
new file mode 100644
index 000000000..3f8acbc67
--- /dev/null
+++ b/packages/client/src/schema-component/antd/calendar-v2/demos/demo2.tsx
@@ -0,0 +1,117 @@
+import { ISchema } from '@formily/react';
+import {
+ AntdSchemaComponentProvider,
+ APIClient,
+ APIClientProvider,
+ BlockSchemaComponentProvider,
+ CollectionManagerProvider,
+ SchemaComponent,
+ SchemaComponentProvider
+} from '@nocobase/client';
+import MockAdapter from 'axios-mock-adapter';
+import React from 'react';
+import collections from './collections';
+import data from './data';
+
+const schema: ISchema = {
+ type: 'object',
+ properties: {
+ block: {
+ type: 'void',
+ 'x-decorator': 'CalendarBlockProvider',
+ 'x-decorator-props': {
+ collection: 't_j6omof6tza8',
+ resource: 't_j6omof6tza8',
+ action: 'list',
+ fieldNames: {
+ id: 'id',
+ start: 'createdAt',
+ end: 'createdAt',
+ title: 'f_g8j5jvalqh0',
+ },
+ params: {
+ paginate: false,
+ },
+ },
+ properties: {
+ calendar: {
+ type: 'array',
+ name: 'calendar1',
+ 'x-component': 'CalendarV2',
+ 'x-component-props': {
+ useProps: '{{ useCalendarBlockProps }}',
+ },
+ properties: {
+ toolBar: {
+ type: 'void',
+ 'x-component': 'CalendarV2.ActionBar',
+ properties: {
+ today: {
+ type: 'void',
+ title: '今天',
+ 'x-component': 'CalendarV2.Today',
+ 'x-action': 'calendar:today',
+ 'x-align': 'left',
+ },
+ nav: {
+ type: 'void',
+ title: '翻页',
+ 'x-component': 'CalendarV2.Nav',
+ 'x-action': 'calendar:nav',
+ 'x-align': 'left',
+ },
+ title: {
+ type: 'void',
+ title: '标题',
+ 'x-component': 'CalendarV2.Title',
+ 'x-action': 'calendar:title',
+ 'x-align': 'left',
+ },
+ viewSelect: {
+ type: 'void',
+ title: '视图切换',
+ 'x-component': 'CalendarV2.ViewSelect',
+ 'x-action': 'calendar:viewSelect',
+ 'x-align': 'right',
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+};
+
+const apiClient = new APIClient({
+ baseURL: 'http://localhost:3000/api',
+});
+
+const sleep = (value: number) => new Promise((resolve) => setTimeout(resolve, value));
+
+const mock = (api: APIClient) => {
+ const mock = new MockAdapter(api.axios);
+
+ mock.onGet('/t_j6omof6tza8:list').reply(async (config) => {
+ await sleep(2000);
+ return [200, data];
+ });
+};
+
+mock(apiClient);
+
+export default () => {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
diff --git a/packages/client/src/schema-component/antd/calendar-v2/index.md b/packages/client/src/schema-component/antd/calendar-v2/index.md
new file mode 100644
index 000000000..38268c37b
--- /dev/null
+++ b/packages/client/src/schema-component/antd/calendar-v2/index.md
@@ -0,0 +1,18 @@
+---
+nav:
+ path: /client
+group:
+ path: /schema-components
+---
+
+# CalendarV2
+
+## Examples
+
+#### Basic
+
+
+
+### CalendarBlockProvider
+
+
diff --git a/packages/client/src/schema-component/antd/calendar-v2/index.ts b/packages/client/src/schema-component/antd/calendar-v2/index.ts
new file mode 100644
index 000000000..298ac64dc
--- /dev/null
+++ b/packages/client/src/schema-component/antd/calendar-v2/index.ts
@@ -0,0 +1,19 @@
+import { ActionBar } from '../action';
+import { Calendar as CalendarV2 } from './Calendar';
+import { CalendarDesigner } from './Calendar.Designer';
+import { Event } from './Event';
+import { Nav } from './Nav';
+import './style.less';
+import { Title } from './Title';
+import { Today } from './Today';
+import { ViewSelect } from './ViewSelect';
+
+CalendarV2.ActionBar = ActionBar;
+CalendarV2.Event = Event;
+CalendarV2.Title = Title;
+CalendarV2.Today = Today;
+CalendarV2.Nav = Nav;
+CalendarV2.ViewSelect = ViewSelect;
+CalendarV2.Designer = CalendarDesigner;
+
+export { CalendarV2 };
diff --git a/packages/client/src/schema-component/antd/calendar-v2/style.less b/packages/client/src/schema-component/antd/calendar-v2/style.less
new file mode 100644
index 000000000..aefe3d99d
--- /dev/null
+++ b/packages/client/src/schema-component/antd/calendar-v2/style.less
@@ -0,0 +1,767 @@
+@charset "UTF-8";
+.rbc-btn {
+ color: inherit;
+ font: inherit;
+ margin: 0;
+}
+
+button.rbc-btn {
+ overflow: visible;
+ text-transform: none;
+ -webkit-appearance: button;
+ cursor: pointer;
+}
+
+button[disabled].rbc-btn {
+ cursor: not-allowed;
+}
+
+button.rbc-input::-moz-focus-inner {
+ border: 0;
+ padding: 0;
+}
+
+.rbc-calendar {
+ box-sizing: border-box;
+ display: flex;
+ flex-direction: column;
+ align-items: stretch;
+ height: 100%;
+}
+
+.rbc-calendar *,
+.rbc-calendar *:before,
+.rbc-calendar *:after {
+ box-sizing: inherit;
+}
+
+.rbc-abs-full,
+.rbc-row-bg {
+ overflow: hidden;
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+}
+
+.rbc-ellipsis,
+.rbc-event-label,
+.rbc-row-segment .rbc-event-content,
+.rbc-show-more {
+ display: block;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.rbc-rtl {
+ direction: rtl;
+}
+
+.rbc-off-range {
+ color: rgba(0, 0, 0, 0.25);
+}
+
+.rbc-off-range-bg {
+ // background: #e6e6e6;
+}
+
+.rbc-header {
+ // font-size: 16px;
+ overflow: hidden;
+ flex: 1 0 0%;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ padding: 4px 12px;
+ text-align: right;
+ vertical-align: middle;
+ // font-weight: 500;
+ min-height: 32px;
+ color: rgba(0, 0, 0, 0.85);
+ margin: 0 4px;
+ border-bottom: 2px solid #f0f0f0;
+}
+.rbc-header + .rbc-header {
+ // border-left: 1px solid #f0f0f0;
+}
+.rbc-rtl .rbc-header + .rbc-header {
+ border-left-width: 0;
+ border-right: 1px solid #f0f0f0;
+}
+.rbc-header > a,
+.rbc-header > a:active,
+.rbc-header > a:visited {
+ color: inherit;
+ text-decoration: none;
+}
+
+.rbc-row-content {
+ position: relative;
+ user-select: none;
+ -webkit-user-select: none;
+ z-index: 4;
+}
+
+.rbc-row-content-scrollable {
+ display: flex;
+ flex-direction: column;
+ height: 100%;
+}
+.rbc-row-content-scrollable .rbc-row-content-scroll-container {
+ height: 100%;
+ overflow-y: scroll;
+ /* Hide scrollbar for Chrome, Safari and Opera */
+ -ms-overflow-style: none;
+ /* IE and Edge */
+ scrollbar-width: none;
+ /* Firefox */
+}
+.rbc-row-content-scrollable .rbc-row-content-scroll-container::-webkit-scrollbar {
+ display: none;
+}
+
+.rbc-toolbar {
+ display: flex;
+ flex-wrap: wrap;
+ justify-content: center;
+ align-items: center;
+ margin-bottom: 10px;
+ font-size: 16px;
+}
+.rbc-toolbar .rbc-toolbar-label {
+ flex-grow: 1;
+ padding: 0 10px;
+ text-align: center;
+}
+.rbc-toolbar button {
+ outline: none;
+ font-size: 14px;
+ line-height: 1.5715;
+ height: 32px;
+ color: #373a3c;
+ display: inline-block;
+ margin: 0;
+ position: relative;
+ z-index: 1;
+ text-align: center;
+ vertical-align: middle;
+ background: none;
+ background-image: none;
+ border: 1px solid #d9d9d9;
+ padding: 4px 15px;
+ border-radius: 2px;
+ line-height: normal;
+ white-space: nowrap;
+}
+.rbc-toolbar button:active,
+.rbc-toolbar button.rbc-active {
+ // background-image: none;
+ // box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
+ // background-color: #e6e6e6;
+ z-index: 2;
+ color: #1890ff;
+ border-color: #1890ff;
+}
+.rbc-toolbar button:active:hover,
+.rbc-toolbar button:active:focus,
+.rbc-toolbar button.rbc-active:hover,
+.rbc-toolbar button.rbc-active:focus {
+ // color: #373a3c;
+ // background-color: #d4d4d4;
+ // border-color: #8c8c8c;
+ z-index: 2;
+ color: #40a9ff;
+ border-color: #40a9ff;
+}
+// .rbc-toolbar button:focus {
+// color: #373a3c;
+// background-color: #e6e6e6;
+// border-color: #adadad; }
+.rbc-toolbar button:hover {
+ // color: #373a3c;
+ // background-color: #e6e6e6;
+ z-index: 2;
+ color: #40a9ff;
+ border-color: #40a9ff;
+}
+
+.rbc-btn-group {
+ display: inline-block;
+ white-space: nowrap;
+}
+.rbc-btn-group > button:first-child:not(:last-child) {
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 0;
+}
+.rbc-btn-group > button:last-child:not(:first-child) {
+ border-top-left-radius: 0;
+ border-bottom-left-radius: 0;
+}
+.rbc-rtl .rbc-btn-group > button:first-child:not(:last-child) {
+ border-radius: 4px;
+ border-top-left-radius: 0;
+ border-bottom-left-radius: 0;
+}
+.rbc-rtl .rbc-btn-group > button:last-child:not(:first-child) {
+ border-radius: 4px;
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 0;
+}
+.rbc-btn-group > button:not(:first-child):not(:last-child) {
+ border-radius: 0;
+}
+.rbc-btn-group button + button {
+ margin-left: -1px;
+}
+.rbc-rtl .rbc-btn-group button + button {
+ margin-left: 0;
+ margin-right: -1px;
+}
+.rbc-btn-group + .rbc-btn-group,
+.rbc-btn-group + button {
+ margin-left: 10px;
+}
+
+.rbc-event {
+ border: none;
+ box-sizing: border-box;
+ box-shadow: none;
+ margin: 0;
+ padding: 2px 5px;
+ background-color: rgba(240, 240, 240, 0.65);
+ border-radius: 2px;
+ // color: #1890ff;
+ cursor: pointer;
+ font-size: 12px;
+ width: 100%;
+ text-align: left;
+ &:hover {
+ background-color: #e6f7ff;
+ color: #1890ff;
+ }
+}
+.rbc-slot-selecting .rbc-event {
+ cursor: inherit;
+ pointer-events: none;
+}
+.rbc-event.rbc-selected {
+ background-color: #e6f7ff;
+ color: #1890ff;
+}
+.rbc-event:focus {
+ // outline: 5px auto #3b99fc;
+}
+
+.rbc-event-label {
+ font-size: 80%;
+}
+
+.rbc-event-overlaps {
+ box-shadow: -1px 1px 5px 0px rgba(51, 51, 51, 0.5);
+}
+
+.rbc-event-continues-prior {
+ border-top-left-radius: 0;
+ border-bottom-left-radius: 0;
+}
+
+.rbc-event-continues-after {
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 0;
+}
+
+.rbc-event-continues-earlier {
+ border-top-left-radius: 0;
+ border-top-right-radius: 0;
+}
+
+.rbc-event-continues-later {
+ border-bottom-left-radius: 0;
+ border-bottom-right-radius: 0;
+}
+
+.rbc-row {
+ display: flex;
+ flex-direction: row;
+}
+
+.rbc-row-segment {
+ padding: 0 4px 1px 4px;
+}
+
+.rbc-selected-cell {
+ background-color: rgba(0, 0, 0, 0.1);
+}
+
+.rbc-show-more {
+ z-index: 4;
+ font-weight: bold;
+ font-size: 85%;
+ height: auto;
+ line-height: normal;
+ color: inherit;
+ padding: 2px 5px;
+}
+
+.rbc-month-view {
+ position: relative;
+ // border: 1px solid #f0f0f0;
+ display: flex;
+ flex-direction: column;
+ flex: 1 0 0;
+ width: 100%;
+ user-select: none;
+ -webkit-user-select: none;
+ height: 68vh;
+}
+
+.rbc-month-header {
+ display: flex;
+ flex-direction: row;
+}
+
+.rbc-month-row {
+ display: flex;
+ position: relative;
+ flex-direction: column;
+ flex: 1 0 0;
+ flex-basis: 0px;
+ overflow: hidden;
+ height: 100%;
+}
+.rbc-month-row + .rbc-month-row {
+ // border-top: 1px solid #f0f0f0;
+}
+
+.rbc-date-cell {
+ // cursor: pointer;
+ flex: 1 1 0;
+ min-width: 0;
+ padding-right: 12px;
+ padding-top: 4px;
+ text-align: right;
+}
+.rbc-date-cell.rbc-now a {
+ // font-weight: bold;
+ color: #1890ff;
+}
+.rbc-date-cell > a,
+.rbc-date-cell > a:active,
+.rbc-date-cell > a:visited {
+ color: inherit;
+ text-decoration: none;
+}
+.rbc-date-cell > a:hover {
+ color: #1890ff;
+}
+
+.rbc-row-bg {
+ display: flex;
+ flex-direction: row;
+ flex: 1 0 0;
+ overflow: hidden;
+}
+
+.rbc-day-bg {
+ flex: 1 0 0%;
+ // border-top: 2px solid #f0f0f0;
+ margin: 0 4px;
+ &:hover {
+ background: #f5f5f5;
+ }
+ // cursor: pointer;
+}
+
+.rbc-month-view {
+ .rbc-day-bg {
+ border-top: 2px solid #f0f0f0;
+ }
+ .rbc-today {
+ border-color: #1890ff !important;
+ background-color: #e6f7ff !important;
+ }
+ .rbc-header {
+ border-bottom: 0 !important;
+ }
+}
+
+.rbc-day-bg + .rbc-day-bg {
+ // margin-left: 8px;
+ // border-left: 1px solid #f0f0f0;
+}
+.rbc-rtl .rbc-day-bg + .rbc-day-bg {
+ // border-left-width: 0;
+ // border-right: 1px solid #f0f0f0;
+}
+
+.rbc-overlay {
+ position: absolute;
+ z-index: 5;
+ margin-top: 5px;
+ border-radius: 2px;
+ // border: 1px solid #e5e5e5;
+ background-color: #fff;
+ box-shadow: 0 3px 6px -4px rgba(0, 0, 0, 0.12), 0 6px 16px 0 rgba(0, 0, 0, 0.08), 0 9px 28px 8px rgba(0, 0, 0, 0.05);
+ padding: 12px 16px;
+}
+.rbc-overlay > * + * {
+ margin-top: 1px;
+}
+
+.rbc-overlay-header {
+ font-weight: 500;
+ min-height: 32px;
+ border-bottom: 1px solid #f0f0f0;
+ margin: -12px -16px 12px -16px;
+ padding: 5px 16px 4px;
+}
+
+.rbc-agenda-view {
+ display: flex;
+ flex-direction: column;
+ flex: 1 0 0;
+ overflow: auto;
+}
+.rbc-agenda-view table.rbc-agenda-table {
+ width: 100%;
+ border: 1px solid #f0f0f0;
+ border-spacing: 0;
+ border-collapse: collapse;
+}
+.rbc-agenda-view table.rbc-agenda-table tbody > tr > td {
+ padding: 5px 10px;
+ vertical-align: top;
+}
+.rbc-agenda-view table.rbc-agenda-table .rbc-agenda-time-cell {
+ padding-left: 15px;
+ padding-right: 15px;
+ text-transform: lowercase;
+}
+.rbc-agenda-view table.rbc-agenda-table tbody > tr > td + td {
+ border-left: 1px solid #f0f0f0;
+}
+.rbc-rtl .rbc-agenda-view table.rbc-agenda-table tbody > tr > td + td {
+ border-left-width: 0;
+ border-right: 1px solid #f0f0f0;
+}
+.rbc-agenda-view table.rbc-agenda-table tbody > tr + tr {
+ border-top: 1px solid #f0f0f0;
+}
+.rbc-agenda-view table.rbc-agenda-table thead > tr > th {
+ padding: 3px 5px;
+ text-align: left;
+ border-bottom: 1px solid #f0f0f0;
+}
+.rbc-rtl .rbc-agenda-view table.rbc-agenda-table thead > tr > th {
+ text-align: right;
+}
+
+.rbc-agenda-time-cell {
+ text-transform: lowercase;
+}
+.rbc-agenda-time-cell .rbc-continues-after:after {
+ content: ' »';
+}
+.rbc-agenda-time-cell .rbc-continues-prior:before {
+ content: '« ';
+}
+
+.rbc-agenda-date-cell,
+.rbc-agenda-time-cell {
+ white-space: nowrap;
+}
+
+.rbc-agenda-event-cell {
+ width: 100%;
+}
+
+.rbc-time-column {
+ display: flex;
+ flex-direction: column;
+ min-height: 100%;
+}
+.rbc-time-column .rbc-timeslot-group {
+ flex: 1;
+}
+
+.rbc-timeslot-group {
+ border-bottom: 1px solid #f0f0f0;
+ min-height: 40px;
+ line-height: 39px;
+ display: flex;
+ flex-flow: column nowrap;
+ // cursor: pointer;
+ &:hover {
+ background: #f0f0f0;
+ }
+}
+
+.rbc-time-gutter,
+.rbc-header-gutter {
+ flex: none;
+}
+
+.rbc-label {
+ padding: 0 5px;
+}
+
+.rbc-day-slot {
+ position: relative;
+}
+.rbc-day-slot .rbc-events-container {
+ bottom: 0;
+ left: 0;
+ position: absolute;
+ right: 0;
+ margin-right: 10px;
+ top: 0;
+}
+.rbc-day-slot .rbc-events-container.rbc-rtl {
+ left: 10px;
+ right: 0;
+}
+.rbc-day-slot .rbc-event {
+ border: 1px solid #265985;
+ display: flex;
+ max-height: 100%;
+ min-height: 20px;
+ flex-flow: column wrap;
+ align-items: flex-start;
+ overflow: hidden;
+ position: absolute;
+}
+.rbc-day-slot .rbc-event-label {
+ flex: none;
+ padding-right: 5px;
+ width: auto;
+}
+.rbc-day-slot .rbc-event-content {
+ width: 100%;
+ flex: 1 1 0;
+ word-wrap: break-word;
+ line-height: 1;
+ height: 100%;
+ min-height: 1em;
+}
+.rbc-day-slot .rbc-time-slot {
+ // border-top: 1px solid #f7f7f7;
+}
+.rbc-time-header-gutter {
+ line-height: 40px;
+}
+.rbc-time-header-cell {
+ min-height: 32px !important;
+}
+.rbc-calendar.view-week {
+ .rbc-time-header-cell {
+ margin-top: -32px;
+ margin-bottom: 4px;
+ }
+ .rbc-time-view {
+ padding-top: 32px;
+ border-top: 0;
+ }
+ .rbc-header {
+ padding: 4px 8px;
+ }
+ .rbc-time-header-content {
+ padding: 4px 0;
+ }
+ .rbc-time-header-gutter {
+ padding: 0;
+ padding-top: 2px;
+ > div {
+ border-top: 2px solid #f0f0f0;
+ padding: 0 5px;
+ }
+ }
+}
+.rbc-time-view-resources .rbc-time-gutter,
+.rbc-time-view-resources .rbc-time-header-gutter {
+ position: sticky;
+ left: 0;
+ background-color: white;
+ border-right: 1px solid #f0f0f0;
+ z-index: 10;
+ margin-right: -1px;
+}
+
+.rbc-time-view-resources .rbc-time-header {
+ overflow: hidden;
+}
+
+.rbc-time-view-resources .rbc-time-header-content {
+ min-width: auto;
+ flex: 1 0 0;
+ flex-basis: 0px;
+}
+
+.rbc-time-view-resources .rbc-time-header-cell-single-day {
+ display: none;
+}
+
+.rbc-time-view-resources .rbc-day-slot {
+ min-width: 140px;
+}
+
+.rbc-time-view-resources .rbc-header,
+.rbc-time-view-resources .rbc-day-bg {
+ width: 140px;
+ flex: 1 1 0;
+ flex-basis: 0 px;
+}
+
+.rbc-time-header-content + .rbc-time-header-content {
+ margin-left: -1px;
+}
+
+.rbc-time-slot {
+ flex: 1 0 0;
+}
+.rbc-time-slot.rbc-now {
+ font-weight: bold;
+}
+
+.rbc-day-header {
+ text-align: center;
+}
+
+.rbc-slot-selection {
+ z-index: 10;
+ position: absolute;
+ background-color: rgba(0, 0, 0, 0.5);
+ color: white;
+ font-size: 75%;
+ width: 100%;
+ padding: 3px;
+}
+
+.rbc-slot-selecting {
+ cursor: move;
+}
+
+.rbc-time-view {
+ display: flex;
+ flex-direction: column;
+ flex: 1;
+ width: 100%;
+ border-top: 1px solid #f0f0f0;
+ min-height: 0;
+}
+.rbc-time-view .rbc-time-gutter {
+ white-space: nowrap;
+}
+.rbc-time-view .rbc-allday-cell {
+ box-sizing: content-box;
+ width: 100%;
+ height: 100%;
+ position: relative;
+}
+.rbc-time-view .rbc-allday-cell + .rbc-allday-cell {
+ border-left: 1px solid #f0f0f0;
+}
+.rbc-time-view .rbc-allday-events {
+ position: relative;
+ z-index: 4;
+}
+.rbc-time-view .rbc-row {
+ box-sizing: border-box;
+ min-height: 20px;
+}
+
+.rbc-time-header {
+ display: flex;
+ flex: 0 0 auto;
+ flex-direction: row;
+}
+.rbc-time-header.rbc-overflowing {
+ border-right: 1px solid #f0f0f0;
+}
+.rbc-rtl .rbc-time-header.rbc-overflowing {
+ border-right-width: 0;
+ border-left: 1px solid #f0f0f0;
+}
+.rbc-time-header > .rbc-row:first-child {
+ border-bottom: 1px solid #f0f0f0;
+}
+.rbc-time-header > .rbc-row.rbc-row-resource {
+ border-bottom: 1px solid #f0f0f0;
+}
+
+.rbc-time-header-cell-single-day {
+ display: none;
+}
+
+.rbc-time-header-content {
+ flex: 1;
+ display: flex;
+ min-width: 0;
+ flex-direction: column;
+ // border-left: 1px solid #f0f0f0;
+}
+.rbc-rtl .rbc-time-header-content {
+ border-left-width: 0;
+ border-right: 1px solid #f0f0f0;
+}
+.rbc-time-header-content > .rbc-row.rbc-row-resource {
+ border-bottom: 1px solid #f0f0f0;
+ flex-shrink: 0;
+}
+
+.rbc-time-content {
+ display: flex;
+ flex: 1 0 0%;
+ align-items: flex-start;
+ width: 100%;
+ border-top: 1px solid #f0f0f0;
+ overflow-y: auto;
+ position: relative;
+}
+.rbc-time-content > .rbc-time-gutter {
+ flex: none;
+}
+.rbc-time-content > * + * > * {
+ // border-left: 1px solid #f0f0f0;
+}
+.rbc-rtl .rbc-time-content > * + * > * {
+ border-left-width: 0;
+ border-right: 1px solid #f0f0f0;
+}
+.rbc-time-content > .rbc-day-slot {
+ width: 100%;
+ user-select: none;
+ -webkit-user-select: none;
+}
+
+.rbc-current-time-indicator {
+ position: absolute;
+ z-index: 3;
+ left: 0;
+ right: 0;
+ height: 1px;
+ background-color: #74ad31;
+ pointer-events: none;
+}
+
+@media only screen and (max-width: 800px) {
+ .view-type-calendar .action-buttons {
+ position: relative !important;
+ left: 0 !important;
+ .filter-action-button {
+ left: 0 !important;
+ }
+ }
+ .rbc-toolbar .rbc-toolbar-label {
+ position: absolute;
+ top: 28px;
+ left: 50%;
+ transform: translateX(-50%);
+ }
+ .rbc-toolbar {
+ justify-content: space-between !important;
+ }
+ .rbc-header {
+ padding: 4px !important;
+ }
+}
diff --git a/packages/client/src/schema-component/antd/calendar-v2/types.d.ts b/packages/client/src/schema-component/antd/calendar-v2/types.d.ts
new file mode 100644
index 000000000..d3f3470d3
--- /dev/null
+++ b/packages/client/src/schema-component/antd/calendar-v2/types.d.ts
@@ -0,0 +1,8 @@
+export interface ToolbarProps {
+ localizer?: any;
+ label?: any;
+ view?: any;
+ views?: any;
+ onNavigate?: (action: string) => void;
+ onView?: (view: string) => void;
+}
diff --git a/packages/client/src/schema-component/antd/calendar-v2/utils.ts b/packages/client/src/schema-component/antd/calendar-v2/utils.ts
new file mode 100644
index 000000000..f72d5efba
--- /dev/null
+++ b/packages/client/src/schema-component/antd/calendar-v2/utils.ts
@@ -0,0 +1,13 @@
+import { get } from 'lodash';
+import { i18n } from '../../../i18n';
+
+export const toEvents = (data: any[], fieldNames: any) => {
+ return data?.map((item) => {
+ return {
+ id: get(item, fieldNames.id || 'id'),
+ title: get(item, fieldNames.title) || i18n.t('Untitle'),
+ start: new Date(get(item, fieldNames.start)),
+ end: new Date(get(item, fieldNames.end || fieldNames.start)),
+ };
+ });
+};
diff --git a/packages/client/src/schema-component/antd/filter/DynamicComponent.tsx b/packages/client/src/schema-component/antd/filter/DynamicComponent.tsx
index 0c4beb5a3..61e00673b 100644
--- a/packages/client/src/schema-component/antd/filter/DynamicComponent.tsx
+++ b/packages/client/src/schema-component/antd/filter/DynamicComponent.tsx
@@ -1,42 +1,43 @@
import { createForm, onFieldValueChange } from '@formily/core';
+import { FieldContext, FormContext } from '@formily/react';
import React, { useContext, useMemo } from 'react';
-import { FormProvider, SchemaComponent } from '../../../schema-component';
+import { SchemaComponent } from '../../core';
import { useComponent } from '../../hooks';
import { FilterContext } from './context';
export const DynamicComponent = (props) => {
const { dynamicComponent } = useContext(FilterContext);
const component = useComponent(dynamicComponent);
- const form = useMemo(
- () =>
- createForm({
- values: {
- value: props.value,
- },
- effects() {
- onFieldValueChange('value', (field) => {
- props?.onChange?.(field.value);
- });
- },
- }),
- [JSON.stringify(props.schema), JSON.stringify(props.value)],
- );
+ const form = useMemo(() => {
+ return createForm({
+ values: {
+ value: props.value,
+ },
+ effects() {
+ onFieldValueChange('value', (field) => {
+ props?.onChange?.(field.value);
+ });
+ },
+ });
+ }, [JSON.stringify(props.schema), JSON.stringify(props.value)]);
const renderSchemaComponent = () => {
return (
-
+
+
+
);
};
return (
-
+
{component
? React.createElement(component, {
value: props.value,
@@ -44,6 +45,6 @@ export const DynamicComponent = (props) => {
renderSchemaComponent,
})
: renderSchemaComponent()}
-
+
);
};
diff --git a/packages/client/src/schema-component/antd/filter/Filter.Action.Designer.tsx b/packages/client/src/schema-component/antd/filter/Filter.Action.Designer.tsx
new file mode 100644
index 000000000..bfe4b4d08
--- /dev/null
+++ b/packages/client/src/schema-component/antd/filter/Filter.Action.Designer.tsx
@@ -0,0 +1,90 @@
+import { ISchema, useField, useFieldSchema } from '@formily/react';
+import React from 'react';
+import { useDesignable } from '../..';
+import { GeneralSchemaDesigner, SchemaSettings } from '../../../schema-settings';
+
+export const FilterActionDesigner = (props) => {
+ const initialValue = {};
+ const field = useField();
+ const fieldSchema = useFieldSchema();
+ const { dn } = useDesignable();
+ const isPopupAction = ['create', 'update', 'view'].includes(fieldSchema['x-action'] || '');
+ return (
+
+ {
+ if (title) {
+ fieldSchema.title = title;
+ field.title = title;
+ field.componentProps.icon = icon;
+ fieldSchema['x-component-props'] = fieldSchema['x-component-props'] || {};
+ fieldSchema['x-component-props'].icon = icon;
+ dn.emit('patch', {
+ schema: {
+ ['x-uid']: fieldSchema['x-uid'],
+ title,
+ 'x-component-props': {
+ ...fieldSchema['x-component-props'],
+ },
+ },
+ });
+ dn.refresh();
+ }
+ }}
+ />
+ {isPopupAction && (
+ {
+ field.componentProps.openMode = value;
+ fieldSchema['x-component-props']['openMode'] = value;
+ dn.emit('patch', {
+ schema: {
+ 'x-uid': fieldSchema['x-uid'],
+ 'x-component-props': fieldSchema['x-component-props'],
+ },
+ });
+ dn.refresh();
+ }}
+ />
+ )}
+
+ {
+ return s['x-component'] === 'Space' || s['x-component'] === 'ActionBar';
+ }}
+ />
+
+ );
+};
diff --git a/packages/client/src/schema-component/antd/filter/Filter.tsx b/packages/client/src/schema-component/antd/filter/Filter.tsx
index 33e04725e..0bc1ea2da 100644
--- a/packages/client/src/schema-component/antd/filter/Filter.tsx
+++ b/packages/client/src/schema-component/antd/filter/Filter.tsx
@@ -3,6 +3,8 @@ import { observer, useField, useFieldSchema } from '@formily/react';
import React from 'react';
import { useRequest } from '../../../api-client';
import { FilterContext } from './context';
+import { FilterActionDesigner } from './Filter.Action.Designer';
+import { FilterAction } from './FilterAction';
import { FilterGroup } from './FilterGroup';
import { SaveDefaultValue } from './SaveDefaultValue';
@@ -17,7 +19,6 @@ export const Filter: any = observer((props: any) => {
const fieldSchema = useFieldSchema();
useDataSource({
onSuccess(data) {
- console.log('onSuccess', data?.data);
field.dataSource = data?.data || [];
},
});
@@ -26,9 +27,11 @@ export const Filter: any = observer((props: any) => {
- {/* {JSON.stringify(field.value, null, 2)}
*/}
);
});
Filter.SaveDefaultValue = SaveDefaultValue;
+
+Filter.Action = FilterAction;
+Filter.Action.Designer = FilterActionDesigner;
diff --git a/packages/client/src/schema-component/antd/filter/FilterAction.tsx b/packages/client/src/schema-component/antd/filter/FilterAction.tsx
new file mode 100644
index 000000000..c56e4f820
--- /dev/null
+++ b/packages/client/src/schema-component/antd/filter/FilterAction.tsx
@@ -0,0 +1,124 @@
+import { css } from '@emotion/css';
+import { createForm, Field, Form } from '@formily/core';
+import { observer, useField, useFieldSchema, useForm } from '@formily/react';
+import { Button, Popover, Space } from 'antd';
+import React, { createContext, useContext, useMemo, useState } from 'react';
+import { useTranslation } from 'react-i18next';
+import { FormProvider, SchemaComponent } from '../../core';
+import { useDesignable } from '../../hooks';
+import { useProps } from '../../hooks/useProps';
+import { Action } from '../action';
+
+export const FilterActionContext = createContext(null);
+
+export const FilterAction = observer((props: any) => {
+ const { t } = useTranslation();
+ const field = useField();
+ const [visible, setVisible] = useState(false);
+ const { designable, dn } = useDesignable();
+ const fieldSchema = useFieldSchema();
+ const form = useMemo