diff --git a/packages/core/client/src/block-provider/BlockSchemaComponentProvider.tsx b/packages/core/client/src/block-provider/BlockSchemaComponentProvider.tsx index dd96ec463..7f2d948ff 100644 --- a/packages/core/client/src/block-provider/BlockSchemaComponentProvider.tsx +++ b/packages/core/client/src/block-provider/BlockSchemaComponentProvider.tsx @@ -7,9 +7,7 @@ import { DetailsBlockProvider, useDetailsBlockProps } from './DetailsBlockProvid import { FilterFormBlockProvider } from './FilterFormBlockProvider'; import { FormBlockProvider, useFormBlockProps } from './FormBlockProvider'; import { FormFieldProvider, useFormFieldProps } from './FormFieldProvider'; -import { GanttBlockProvider, useGanttBlockProps } from './GanttBlockProvider'; 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'; @@ -19,7 +17,6 @@ export const BlockSchemaComponentProvider: React.FC = (props) => { return ( { FilterFormBlockProvider, FormFieldProvider, DetailsBlockProvider, - KanbanBlockProvider, RecordLink, }} scope={{ @@ -43,8 +39,6 @@ export const BlockSchemaComponentProvider: React.FC = (props) => { useTableFieldProps, useTableBlockProps, useTableSelectorProps, - useKanbanBlockProps, - useGanttBlockProps, }} > {props.children} @@ -60,7 +54,6 @@ export class BlockSchemaComponentPlugin extends Plugin { addComponents() { this.app.addComponents({ - GanttBlockProvider, CalendarBlockProvider, TableFieldProvider, TableBlockProvider, @@ -69,7 +62,6 @@ export class BlockSchemaComponentPlugin extends Plugin { FilterFormBlockProvider, FormFieldProvider, DetailsBlockProvider, - KanbanBlockProvider, RecordLink, }); } @@ -87,8 +79,6 @@ export class BlockSchemaComponentPlugin extends Plugin { useTableFieldProps, useTableBlockProps, useTableSelectorProps, - useKanbanBlockProps, - useGanttBlockProps, }); } } diff --git a/packages/core/client/src/block-provider/FormBlockProvider.tsx b/packages/core/client/src/block-provider/FormBlockProvider.tsx index 3e21cbc2f..52006aed1 100644 --- a/packages/core/client/src/block-provider/FormBlockProvider.tsx +++ b/packages/core/client/src/block-provider/FormBlockProvider.tsx @@ -14,6 +14,7 @@ import { TemplateBlockProvider } from './TemplateBlockProvider'; export const FormBlockContext = createContext({}); const InternalFormBlockProvider = (props) => { + const ctx = useFormBlockContext(); const { action, readPretty, params, association } = props; const field = useField(); const form = useMemo( @@ -28,6 +29,7 @@ const InternalFormBlockProvider = (props) => { const record = useRecord(); const formBlockValue = useMemo(() => { return { + ...ctx, params, action, form, diff --git a/packages/core/client/src/block-provider/index.tsx b/packages/core/client/src/block-provider/index.tsx index 5847e88cb..605a906d8 100644 --- a/packages/core/client/src/block-provider/index.tsx +++ b/packages/core/client/src/block-provider/index.tsx @@ -3,12 +3,10 @@ export * from './BlockSchemaComponentProvider'; export * from './CalendarBlockProvider'; export * from './FilterFormBlockProvider'; export * from './FormBlockProvider'; -export * from './KanbanBlockProvider'; export * from './SharedFilterProvider'; export * from './TableBlockProvider'; export * from './TableFieldProvider'; export * from './TableSelectorProvider'; export * from './FormFieldProvider'; -export * from './GanttBlockProvider'; export * from './SharedFilterProvider'; export * from './hooks'; diff --git a/packages/core/client/src/board/style.ts b/packages/core/client/src/board/style.ts deleted file mode 100644 index 60ea7b6dc..000000000 --- a/packages/core/client/src/board/style.ts +++ /dev/null @@ -1,157 +0,0 @@ -import { createStyles } from 'antd-style'; - -export const useStyles = createStyles(({ css, token }) => { - return css` - .react-kanban-board { - height: 100%; - // padding: 5px; - // margin-bottom: 24px; - } - - .react-kanban-card { - box-sizing: border-box; - max-width: 250px; - min-width: 250px; - border-radius: 3px; - background-color: #fff; - padding: 10px; - margin-bottom: 7px; - } - - .react-kanban-card-skeleton { - box-sizing: border-box; - max-width: 300px; - min-width: 300px; - height: 70vh; - overflow-x: hidden; - overflow-y: auto; - padding: 0 12px; - margin-bottom: 12px; - > div { - margin-bottom: 12px; - } - + div { - display: none !important; - } - } - - .react-kanban-card--dragging { - box-shadow: 2px 2px grey; - } - - .react-kanban-card__description { - padding-top: 10px; - } - - .react-kanban-card__title { - border-bottom: 1px solid #eee; - padding-bottom: 5px; - font-weight: bold; - display: flex; - justify-content: space-between; - } - - .react-kanban-column { - background-color: ${token.colorFillQuaternary}; - margin-right: 15px; - padding-bottom: 15px; - width: 300px; - } - - .react-kanban-column-header { - padding: 15px; - } - - .react-kanban-card-adder-form { - box-sizing: border-box; - max-width: 250px; - min-width: 250px; - border-radius: 3px; - background-color: #fff; - padding: 10px; - margin-bottom: 7px; - input { - border: 0px; - font-family: inherit; - font-size: inherit; - } - } - - .react-kanban-card-adder-button { - width: 100%; - margin-top: 5px; - background-color: transparent; - cursor: pointer; - border: 1px solid #ccc; - transition: 0.3s; - border-radius: 3px; - font-size: 20px; - margin-bottom: 10px; - font-weight: bold; - &:hover { - background-color: #ccc; - } - } - - .react-kanban-card-adder-form__title { - font-weight: bold; - border-bottom: 1px solid #eee; - padding-bottom: 5px; - font-weight: bold; - display: flex; - justify-content: space-between; - width: 100%; - padding: 0px; - &:focus { - outline: none; - } - } - - .react-kanban-card-adder-form__description { - width: 100%; - margin-top: 10px; - &:focus { - outline: none; - } - } - - .react-kanban-card-adder-form__button { - background-color: #eee; - border: none; - padding: 5px; - width: 45%; - margin-top: 5px; - border-radius: 3px; - &:hover { - transition: 0.3s; - cursor: pointer; - background-color: #ccc; - } - } - - .react-kanban-column-header { - } - - .react-kanban-column-header__button { - color: #333333; - background-color: #ffffff; - border-color: #cccccc; - - &:hover, - &:focus, - &:active { - background-color: #e6e6e6; - } - } - - .react-kanban-column-adder-button { - border: 2px dashed #eee; - height: 132px; - margin: 5px; - - &:hover { - cursor: pointer; - } - } - `; -}); diff --git a/packages/core/client/src/index.ts b/packages/core/client/src/index.ts index 7caadb7f6..b22e9c823 100644 --- a/packages/core/client/src/index.ts +++ b/packages/core/client/src/index.ts @@ -20,7 +20,6 @@ export * from './application'; export * from './async-data-provider'; export * from './auth'; export * from './block-provider'; -export * from './board'; export * from './china-region'; export * from './collection-manager'; export * from './css-variable'; @@ -47,3 +46,5 @@ export * from './style'; export * from './system-settings'; export * from './user'; export * from './variables'; +export * from './flag-provider'; +export * from './testUtils'; diff --git a/packages/core/client/src/locale/pt_BR.json b/packages/core/client/src/locale/pt_BR.json index 4fcf6ba98..c44ded9d8 100644 --- a/packages/core/client/src/locale/pt_BR.json +++ b/packages/core/client/src/locale/pt_BR.json @@ -675,7 +675,6 @@ "Changed to": "Mudou para", "Clear": "Limpar", "Add attach": "Adicionar anexo", - "Please select the records to be updated": "Por favor, selecione os registros a serem atualizados", "Selector": "Seletor", "Inner": "Interno", "Search and select collection": "Pesquisar e selecionar coleção", diff --git a/packages/core/client/src/nocobase-buildin-plugin/index.tsx b/packages/core/client/src/nocobase-buildin-plugin/index.tsx index 5442bb1ab..89b53e2ba 100644 --- a/packages/core/client/src/nocobase-buildin-plugin/index.tsx +++ b/packages/core/client/src/nocobase-buildin-plugin/index.tsx @@ -15,12 +15,7 @@ import { RemoteDocumentTitlePlugin } from '../document-title'; import { PinnedListPlugin } from '../plugin-manager'; import { PMPlugin } from '../pm'; import { AdminLayoutPlugin, AuthLayout, RouteSchemaComponent } from '../route-switch'; -import { - AntdSchemaComponentPlugin, - KanbanPlugin, - SchemaComponentPlugin, - menuItemInitializer, -} from '../schema-component'; +import { AntdSchemaComponentPlugin, SchemaComponentPlugin, menuItemInitializer } from '../schema-component'; import { ErrorFallback } from '../schema-component/antd/error-fallback'; import { AssociationFilterPlugin, SchemaInitializerPlugin } from '../schema-initializer'; import { BlockTemplateDetails, BlockTemplatePage } from '../schema-templates'; @@ -317,7 +312,6 @@ export class NocoBaseBuildInPlugin extends Plugin { } async addPlugins() { await this.app.pm.add(AssociationFilterPlugin); - await this.app.pm.add(KanbanPlugin); await this.app.pm.add(LocalePlugin, { name: 'builtin-locale' }); await this.app.pm.add(AdminLayoutPlugin, { name: 'admin-layout' }); await this.app.pm.add(SystemSettingsPlugin, { name: 'system-setting' }); diff --git a/packages/core/client/src/schema-component/antd/action/Action.Designer.tsx b/packages/core/client/src/schema-component/antd/action/Action.Designer.tsx index d41dc4a88..d0256ab5b 100644 --- a/packages/core/client/src/schema-component/antd/action/Action.Designer.tsx +++ b/packages/core/client/src/schema-component/antd/action/Action.Designer.tsx @@ -1,10 +1,9 @@ import { ArrayTable } from '@formily/antd-v5'; -import { onFieldInputValueChange, onFieldValueChange } from '@formily/core'; +import { onFieldValueChange } from '@formily/core'; import { ISchema, connect, mapProps, useField, useFieldSchema, useForm, useFormEffects } from '@formily/react'; import { isValid, uid } from '@formily/shared'; import { Alert, Tree as AntdTree, ModalProps } from 'antd'; -import { cloneDeep } from 'lodash'; -import React, { useCallback, useEffect, useMemo, useState } from 'react'; +import React, { useCallback, useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { RemoteSelect, useCompile, useDesignable } from '../..'; import { useApp } from '../../../application'; @@ -13,10 +12,8 @@ import { SchemaSettingOptions, SchemaSettings } from '../../../application/schem import { useSchemaToolbar } from '../../../application/schema-toolbar'; import { CollectionOptions, useCollection, useCollectionManager } from '../../../collection-manager'; import { FlagProvider } from '../../../flag-provider'; -import { useRecord } from '../../../record-provider'; import { SchemaSettingOpenModeSchemaItems } from '../../../schema-items'; import { useCollectionState } from '../../../schema-settings/DataTemplates/hooks/useCollectionState'; -import { useSyncFromForm } from '../../../schema-settings/DataTemplates/utils'; import { GeneralSchemaDesigner } from '../../../schema-settings/GeneralSchemaDesigner'; import { SchemaSettingsActionModalItem, @@ -44,27 +41,10 @@ const Tree = connect( field.value = checkedKeys; }; field.onCheck = onCheck; - const form = useForm(); return { ...props, checkedKeys, onCheck, - treeData: props?.treeData.map((v: any) => { - if (form.values.duplicateMode === 'quickDulicate') { - const children = v?.children?.map((k) => { - return { - ...k, - disabled: false, - }; - }); - return { - ...v, - disabled: false, - children, - }; - } - return v; - }), }; }), ); @@ -269,264 +249,6 @@ const getAllkeys = (data, result) => { return result; }; -function DuplicationMode() { - const { dn } = useDesignable(); - const { t } = useTranslation(); - const field = useField(); - const fieldSchema = useFieldSchema(); - const { name } = useCollection(); - const { collectionList, getEnableFieldTree, getOnLoadData, getOnCheck } = useCollectionState(name); - const duplicateValues = cloneDeep(fieldSchema['x-component-props'].duplicateFields || []); - const record = useRecord(); - const syncCallBack = useCallback((treeData, selectFields, form) => { - form.query('duplicateFields').take((f) => { - f.componentProps.treeData = treeData; - f.componentProps.defaultCheckedKeys = selectFields; - f.setInitialValue(selectFields); - f?.onCheck(selectFields); - form.setValues({ ...form.values, treeData }); - }); - }, []); - const useSelectAllFields = (form) => { - return { - async run() { - form.query('duplicateFields').take((f) => { - const selectFields = getAllkeys(f.componentProps.treeData, []); - f.componentProps.defaultCheckedKeys = selectFields; - f.setInitialValue(selectFields); - f?.onCheck(selectFields); - }); - }, - }; - }; - const useUnSelectAllFields = (form) => { - return { - async run() { - form.query('duplicateFields').take((f) => { - f.componentProps.defaultCheckedKeys = []; - f.setInitialValue([]); - f?.onCheck([]); - }); - }, - }; - }; - return ( - 1 ? 'visible' : 'hidden', - 'x-decorator': 'FormItem', - 'x-component': 'Select', - 'x-component-props': { - options: collectionList, - }, - 'x-reactions': [ - { - dependencies: ['.duplicateMode'], - fulfill: { - state: { - disabled: `{{ $deps[0]==="quickDulicate" }}`, - value: `{{ $deps[0]==="quickDulicate"? currentCollection:collectionName }}`, - }, - }, - }, - ], - }, - syncFromForm: { - type: 'void', - title: '{{ t("Sync from form fields") }}', - 'x-component': 'Action.Link', - 'x-component-props': { - type: 'primary', - style: { float: 'right', position: 'relative', zIndex: 1200 }, - useAction: () => { - const formSchema = useMemo(() => findFormBlock(fieldSchema), [fieldSchema]); - return useSyncFromForm( - formSchema, - fieldSchema['x-component-props']?.duplicateCollection || record?.__collection || name, - syncCallBack, - ); - }, - }, - 'x-reactions': [ - { - dependencies: ['.duplicateMode'], - fulfill: { - state: { - visible: `{{ $deps[0]!=="quickDulicate" }}`, - }, - }, - }, - ], - }, - selectAll: { - type: 'void', - title: '{{ t("Select all") }}', - 'x-component': 'Action.Link', - 'x-reactions': [ - { - dependencies: ['.duplicateMode'], - fulfill: { - state: { - visible: `{{ $deps[0]==="quickDulicate"}}`, - }, - }, - }, - ], - 'x-component-props': { - type: 'primary', - style: { float: 'right', position: 'relative', zIndex: 1200 }, - useAction: () => { - const from = useForm(); - return useSelectAllFields(from); - }, - }, - }, - unselectAll: { - type: 'void', - title: '{{ t("UnSelect all") }}', - 'x-component': 'Action.Link', - 'x-reactions': [ - { - dependencies: ['.duplicateMode', '.duplicateFields'], - fulfill: { - state: { - visible: `{{ $deps[0]==="quickDulicate"&&$form.getValuesIn('duplicateFields').length>0 }}`, - }, - }, - }, - ], - 'x-component-props': { - type: 'primary', - style: { float: 'right', position: 'relative', zIndex: 1200, marginRight: '10px' }, - useAction: () => { - const from = useForm(); - return useUnSelectAllFields(from); - }, - }, - }, - duplicateFields: { - type: 'array', - title: '{{ t("Data fields") }}', - required: true, - description: t('Only the selected fields will be used as the initialization data for the form'), - 'x-decorator': 'FormItem', - 'x-component': Tree, - 'x-component-props': { - defaultCheckedKeys: duplicateValues, - treeData: [], - checkable: true, - checkStrictly: true, - selectable: false, - loadData: '{{ getOnLoadData($self) }}', - onCheck: '{{ getOnCheck($self) }}', - rootStyle: { - padding: '8px 0', - border: '1px solid #d9d9d9', - borderRadius: '2px', - maxHeight: '30vh', - overflow: 'auto', - margin: '2px 0', - }, - }, - 'x-reactions': [ - { - dependencies: ['.collection', '.duplicateMode'], - fulfill: { - state: { - disabled: '{{ !$deps[0] }}', - componentProps: { - treeData: '{{ getEnableFieldTree($deps[0], $self,treeData) }}', - }, - }, - }, - }, - ], - }, - }, - } as ISchema - } - onSubmit={({ duplicateMode, collection, duplicateFields, treeData }) => { - const fields = Array.isArray(duplicateFields) ? duplicateFields : duplicateFields.checked || []; - field.componentProps.duplicateMode = duplicateMode; - field.componentProps.duplicateFields = fields; - fieldSchema['x-component-props'] = fieldSchema['x-component-props'] || {}; - fieldSchema['x-component-props'].duplicateMode = duplicateMode; - fieldSchema['x-component-props'].duplicateFields = fields; - fieldSchema['x-component-props'].duplicateCollection = collection; - fieldSchema['x-component-props'].treeData = treeData || field.componentProps?.treeData; - dn.emit('patch', { - schema: { - ['x-uid']: fieldSchema['x-uid'], - 'x-component-props': { - ...fieldSchema['x-component-props'], - }, - }, - }); - dn.refresh(); - }} - /> - ); -} - -function UpdateMode() { - const { dn } = useDesignable(); - const { t } = useTranslation(); - const fieldSchema = useFieldSchema(); - - return ( - { - fieldSchema['x-action-settings']['updateMode'] = value; - dn.emit('patch', { - schema: { - 'x-uid': fieldSchema['x-uid'], - 'x-action-settings': fieldSchema['x-action-settings'], - }, - }); - dn.refresh(); - }} - /> - ); -} - function AssignedFieldValues() { const { dn } = useDesignable(); const { t } = useTranslation(); @@ -630,7 +352,6 @@ function SkipValidation() { /> ); } - function AfterSuccess() { const { dn } = useDesignable(); const { t } = useTranslation(); @@ -699,7 +420,6 @@ function AfterSuccess() { /> ); } - function RemoveButton( props: { onConfirmOk?: ModalProps['onOk']; @@ -955,15 +675,6 @@ export const actionSettingsItems: SchemaSettingOptions['items'] = [ }; }, }, - { - name: 'duplicationMode', - Component: DuplicationMode, - useVisible() { - const fieldSchema = useFieldSchema(); - const isDuplicateAction = fieldSchema['x-action'] === 'duplicate'; - return isDuplicateAction; - }, - }, { name: 'openMode', Component: SchemaSettingOpenModeSchemaItems, @@ -984,17 +695,6 @@ export const actionSettingsItems: SchemaSettingOptions['items'] = [ }; }, }, - { - name: 'updateMode', - Component: UpdateMode, - useVisible() { - const fieldSchema = useFieldSchema(); - const isUpdateModePopupAction = ['customize:bulkUpdate', 'customize:bulkEdit'].includes( - fieldSchema['x-action'], - ); - return isUpdateModePopupAction; - }, - }, { name: 'assignFieldValues', Component: AssignedFieldValues, diff --git a/packages/core/client/src/schema-component/antd/association-field/InternalPopoverNester.tsx b/packages/core/client/src/schema-component/antd/association-field/InternalPopoverNester.tsx index ffed2dc1b..fe7dd6b5f 100644 --- a/packages/core/client/src/schema-component/antd/association-field/InternalPopoverNester.tsx +++ b/packages/core/client/src/schema-component/antd/association-field/InternalPopoverNester.tsx @@ -1,6 +1,6 @@ import { EditOutlined } from '@ant-design/icons'; import { css } from '@emotion/css'; -import { observer } from '@formily/react'; +import { observer, useFieldSchema } from '@formily/react'; import React, { useContext, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { ActionContext, ActionContextProvider } from '../action/context'; @@ -16,6 +16,8 @@ export const InternaPopoverNester = observer( const { options } = useAssociationFieldContext(); const [visible, setVisible] = useState(false); const { t } = useTranslation(); + const schema = useFieldSchema(); + schema['x-component-props'].enableLink = false; const ref = useRef(); const nesterProps = { ...props, @@ -37,10 +39,6 @@ export const InternaPopoverNester = observer( ); - const titleProps = { - ...props, - enableLink: true, - }; const getContainer = () => ref.current; const ctx = useContext(ActionContext); const modalProps = { @@ -69,7 +67,7 @@ export const InternaPopoverNester = observer( max-width: 95%; `} > - + diff --git a/packages/core/client/src/schema-component/antd/association-field/InternalViewer.tsx b/packages/core/client/src/schema-component/antd/association-field/InternalViewer.tsx index c1304b84e..be92223bd 100644 --- a/packages/core/client/src/schema-component/antd/association-field/InternalViewer.tsx +++ b/packages/core/client/src/schema-component/antd/association-field/InternalViewer.tsx @@ -67,7 +67,7 @@ export const ReadPrettyInternalViewer: React.FC = observer( {snapshot ? ( text - ) : enableLink !== false && !props.enableLink ? ( + ) : enableLink !== false ? ( { e.stopPropagation(); diff --git a/packages/core/client/src/schema-component/antd/form-v2/Templates.tsx b/packages/core/client/src/schema-component/antd/form-v2/Templates.tsx index ce3d7de64..2380760fb 100644 --- a/packages/core/client/src/schema-component/antd/form-v2/Templates.tsx +++ b/packages/core/client/src/schema-component/antd/form-v2/Templates.tsx @@ -7,10 +7,10 @@ import { useTranslation } from 'react-i18next'; import { useAPIClient } from '../../../api-client'; import { findFormBlock } from '../../../block-provider'; import { useCollectionManager } from '../../../collection-manager'; -import { useDuplicatefieldsContext } from '../../../schema-initializer/components'; import { compatibleDataId } from '../../../schema-settings/DataTemplates/FormDataTemplates'; import { useToken } from '../__builtins__'; import { RemoteSelect } from '../remote-select'; +import { useFormBlockContext } from '../../../block-provider/FormBlockProvider'; export interface ITemplate { config?: { @@ -38,10 +38,11 @@ export interface ITemplate { const useDataTemplates = () => { const fieldSchema = useFieldSchema(); const { t } = useTranslation(); - const data = useDuplicatefieldsContext(); + const { duplicateData } = useFormBlockContext(); const { getCollectionJoinField } = useCollectionManager(); - if (data) { - return data; + console.log(useFormBlockContext()); + if (duplicateData) { + return duplicateData; } const { items = [], display = true } = findDataTemplates(fieldSchema); // 过滤掉已经被删除的字段 diff --git a/packages/core/client/src/schema-component/antd/form-v2/index.ts b/packages/core/client/src/schema-component/antd/form-v2/index.ts index 486260d2e..a7497e0cb 100644 --- a/packages/core/client/src/schema-component/antd/form-v2/index.ts +++ b/packages/core/client/src/schema-component/antd/form-v2/index.ts @@ -11,4 +11,6 @@ FormV2.ReadPrettyDesigner = ReadPrettyFormDesigner; FormV2.Templates = Templates; export * from './FormField'; +export * from './Templates'; + export { FormV2, DetailsDesigner }; diff --git a/packages/core/client/src/schema-component/antd/gantt/Gantt.Designer.tsx b/packages/core/client/src/schema-component/antd/gantt/Gantt.Designer.tsx deleted file mode 100644 index ac5f2c8d0..000000000 --- a/packages/core/client/src/schema-component/antd/gantt/Gantt.Designer.tsx +++ /dev/null @@ -1,184 +0,0 @@ -import { useField, useFieldSchema } from '@formily/react'; -import React from 'react'; -import { useTranslation } from 'react-i18next'; -import { useCompile, useDesignable } from '../..'; -import { useFormBlockContext, useGanttBlockContext } from '../../../block-provider'; -import { useCollection } from '../../../collection-manager'; -import { - GeneralSchemaDesigner, - SchemaSettingsBlockTitleItem, - SchemaSettingsDataScope, - SchemaSettingsDivider, - SchemaSettingsRemove, - SchemaSettingsSelectItem, - SchemaSettingsTemplate, -} from '../../../schema-settings'; -import { useSchemaTemplate } from '../../../schema-templates'; - -const useOptions = (type = 'string') => { - const compile = useCompile(); - const { fields } = useCollection(); - const options = fields - ?.filter((field) => field.type === type) - ?.map((field) => { - return { - value: field.name, - label: compile(field?.uiSchema?.title), - }; - }); - return options; -}; - -export const GanttDesigner = () => { - const field = useField(); - const fieldSchema = useFieldSchema(); - const { form } = useFormBlockContext(); - const { name, title } = useCollection(); - const { service } = useGanttBlockContext(); - const { dn } = useDesignable(); - const compile = useCompile(); - const { t } = useTranslation(); - const template = useSchemaTemplate(); - const fieldNames = fieldSchema?.['x-decorator-props']?.['fieldNames'] || {}; - const defaultResource = fieldSchema?.['x-decorator-props']?.resource; - return ( - - - { - const fieldNames = field.decoratorProps.fieldNames || {}; - fieldNames['title'] = title; - field.decoratorProps.params = fieldNames; - fieldSchema['x-decorator-props']['params'] = fieldNames; - // Select切换option后value未按照预期切换,固增加以下代码 - fieldSchema['x-decorator-props']['fieldNames'] = 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['range'] = range; - field.decoratorProps.params = fieldNames; - fieldSchema['x-decorator-props']['params'] = fieldNames; - // Select切换option后value未按照预期切换,固增加以下代码 - fieldSchema['x-decorator-props']['fieldNames'] = 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.fieldNames = fieldNames; - fieldSchema['x-decorator-props']['fieldNames'] = 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.fieldNames = fieldNames; - fieldSchema['x-decorator-props']['fieldNames'] = 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['progress'] = progress; - field.decoratorProps.fieldNames = fieldNames; - fieldSchema['x-decorator-props']['fieldNames'] = 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/core/client/src/schema-component/antd/gantt/__tests__/gant.test.tsx b/packages/core/client/src/schema-component/antd/gantt/__tests__/gant.test.tsx deleted file mode 100644 index c57ab7b3f..000000000 --- a/packages/core/client/src/schema-component/antd/gantt/__tests__/gant.test.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import React from 'react'; -import { render } from 'testUtils'; -import App1 from '../demos/demo1'; - -describe('gantt', () => { - it('renders without crashing', () => { - render(); - }); -}); diff --git a/packages/core/client/src/schema-component/antd/gantt/components/calendar/style.tsx b/packages/core/client/src/schema-component/antd/gantt/components/calendar/style.tsx deleted file mode 100644 index 53f053b9a..000000000 --- a/packages/core/client/src/schema-component/antd/gantt/components/calendar/style.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import { TinyColor } from '@ctrl/tinycolor'; -import { genStyleHook } from '../../../__builtins__'; - -const useStyles = genStyleHook('nb-gantt-calendar', (token) => { - const { componentCls } = token; - const colorFillAlterSolid = new TinyColor(token.colorFillAlter) - .onBackground(token.colorBgContainer) - .toHexShortString(); - - return { - [componentCls]: { - '.calendarBottomText': { - textAnchor: 'middle', - fill: token.colorText, - WebkitTouchCallout: 'none', - WebkitUserSelect: 'none', - MozUserSelect: 'none', - msUserSelect: 'none', - userSelect: 'none', - pointerEvents: 'none', - fontWeight: 500, - }, - - '.calendarTopTick': { - stroke: token.colorBorderSecondary, - strokeWidth: 0, - }, - - '.calendarTopText': { - textAnchor: 'middle', - WebkitTouchCallout: 'none', - WebkitUserSelect: 'none', - MozUserSelect: 'none', - msUserSelect: 'none', - userSelect: 'none', - pointerEvents: 'none', - fontWeight: 500, - fill: token.colorText, - }, - - '.calendarHeader': { - color: token.colorText, - fill: colorFillAlterSolid, - strokeWidth: 1.4, - background: colorFillAlterSolid, - borderBottom: `1px solid ${token.colorBorderSecondary}`, - }, - }, - }; -}); - -export default useStyles; diff --git a/packages/core/client/src/schema-component/antd/gantt/components/gantt/style.tsx b/packages/core/client/src/schema-component/antd/gantt/components/gantt/style.tsx deleted file mode 100644 index b0a15b561..000000000 --- a/packages/core/client/src/schema-component/antd/gantt/components/gantt/style.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import { genStyleHook } from '../../../__builtins__'; - -const useStyles = genStyleHook('nb-gantt', (token) => { - const { componentCls } = token; - - return { - [componentCls]: { - '.ganttVerticalContainer': { - overflow: 'hidden', - fontSize: '0', - margin: '0', - padding: '0', - width: '100%', - borderLeft: `2px solid ${token.colorBorderSecondary}`, - - '.ganttHeader': { borderBottom: `1px solid ${token.colorBorderSecondary}`, fontWeight: 700 }, - '.ganttBody': { borderBottom: `1px solid ${token.colorBorderSecondary}` }, - }, - - '.horizontalContainer': { - margin: '0', - padding: '0', - overflow: 'hidden', - }, - - '.wrapper': { - display: 'flex', - padding: '0', - margin: '0', - listStyle: 'none', - outline: 'none', - position: 'relative', - '.gantt-horizontal-scoll': { display: 'none' }, - '&:hover': { '.gantt-horizontal-scoll': { display: 'block' } }, - }, - }, - }; -}); - -export default useStyles; diff --git a/packages/core/client/src/schema-component/antd/gantt/components/grid/style.tsx b/packages/core/client/src/schema-component/antd/gantt/components/grid/style.tsx deleted file mode 100644 index ce3ccf96b..000000000 --- a/packages/core/client/src/schema-component/antd/gantt/components/grid/style.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import { genStyleHook } from '../../../__builtins__'; - -const useStyles = genStyleHook('nb-grid-body', (token) => { - const { componentCls } = token; - - return { - [componentCls]: { - '.gridRow': { - fill: token.colorBgContainer, - }, - - '.gridHeightRow': { - fill: '#e6f7ff', - borderColor: token.colorBorder, - }, - - '.gridRowLine': { - stroke: token.colorBorderSecondary, - strokeWidth: 0, - borderBottom: `1px solid ${token.colorBorderSecondary}`, - }, - - '.gridTick': { - stroke: token.colorBorderSecondary, - }, - }, - }; -}); - -export default useStyles; diff --git a/packages/core/client/src/schema-component/antd/gantt/components/other/style.tsx b/packages/core/client/src/schema-component/antd/gantt/components/other/style.tsx deleted file mode 100644 index 937126dce..000000000 --- a/packages/core/client/src/schema-component/antd/gantt/components/other/style.tsx +++ /dev/null @@ -1,91 +0,0 @@ -import { genStyleHook } from '../../../__builtins__'; - -const useStyles = genStyleHook('nb-grid-other', (token) => { - const { componentCls } = token; - - return { - [componentCls]: { - '&.scrollWrapper': { - overflow: 'auto', - position: 'absolute', - bottom: '-4px', - maxWidth: '100%', - scrollbarWidth: 'thin', - height: '1.2rem', - '&::-webkit-scrollbar': { width: 8, height: 8 }, - '&::-webkit-scrollbar-corner': { background: 'transparent' }, - '&::-webkit-scrollbar-track': { background: 'var(--colorBgScrollTrack)' }, - '&::-webkit-scrollbar-thumb': { - background: 'var(--colorBgScrollBar)', - borderRadius: 4, - }, - '&::-webkit-scrollbar-thumb:hover': { - background: 'var(--colorBgScrollBarHover)', - }, - '&::-webkit-scrollbar-thumb:active': { - background: 'var(--colorBgScrollBarActive)', - }, - }, - - '.horizontalScroll': { - height: 1, - }, - - '&.tooltipDefaultContainer': { - padding: '12px', - backgroundColor: token.colorBgElevated, - backgroundClip: 'padding-box', - borderRadius: token.borderRadius, - boxShadow: token.boxShadow, - b: { display: 'block', marginBottom: token.marginXS }, - - '.tooltipDefaultContainerParagraph': { - fontSize: token.fontSizeSM, - marginBottom: token.marginXXS + (token.marginXS - token.marginXXS), - color: token.colorText, - }, - }, - - '&.tooltipDetailsContainer': { - position: 'absolute', - display: 'flex', - flexShrink: 0, - pointerEvents: 'none', - WebkitTouchCallout: 'none', - WebkitUserSelect: 'none', - MozUserSelect: 'none', - msUserSelect: 'none', - userSelect: 'none', - }, - - '&.tooltipDetailsContainerHidden': { - visibility: 'hidden', - position: 'absolute', - display: 'flex', - pointerEvents: 'none', - }, - - '&.verticalScroll': { - overflow: 'hidden auto', - width: '1rem', - flexShrink: 0, - scrollbarWidth: 'thin', - '&::-webkit-scrollbar': { width: 8, height: 8 }, - '&::-webkit-scrollbar-corner': { background: 'transparent' }, - '&::-webkit-scrollbar-track': { background: 'var(--colorBgScrollTrack)' }, - '&::-webkit-scrollbar-thumb': { - background: 'var(--colorBgScrollBar)', - borderRadius: 4, - }, - '&::-webkit-scrollbar-thumb:hover': { - background: 'var(--colorBgScrollBarHover)', - }, - '&::-webkit-scrollbar-thumb:active': { - background: 'var(--colorBgScrollBarActive)', - }, - }, - }, - }; -}); - -export default useStyles; diff --git a/packages/core/client/src/schema-component/antd/gantt/components/task-item/style.tsx b/packages/core/client/src/schema-component/antd/gantt/components/task-item/style.tsx deleted file mode 100644 index 94cb69832..000000000 --- a/packages/core/client/src/schema-component/antd/gantt/components/task-item/style.tsx +++ /dev/null @@ -1,48 +0,0 @@ -import { genStyleHook } from '../../../__builtins__'; - -const useStyles = genStyleHook('nb-gantt-task-item', (token) => { - const { componentCls } = token; - - return { - [componentCls]: { - '.barLabel': { - fill: token.colorTextLightSolid, - textAnchor: 'middle', - fontWeight: 400, - dominantBaseline: 'central', - WebkitTouchCallout: 'none', - WebkitUserSelect: 'none', - MozUserSelect: 'none', - msUserSelect: 'none', - userSelect: 'none', - pointerEvents: 'none', - }, - - '.projectLabel': { - fill: '#130d0d', - fontWeight: 500, - fontSize: '0.9em', - dominantBaseline: 'central', - WebkitTouchCallout: 'none', - WebkitUserSelect: 'none', - MozUserSelect: 'none', - msUserSelect: 'none', - userSelect: 'none', - pointerEvents: 'none', - }, - - '.barLabelOutside': { - fill: token.colorTextLabel, - textAnchor: 'start', - WebkitTouchCallout: 'none', - WebkitUserSelect: 'none', - MozUserSelect: 'none', - msUserSelect: 'none', - userSelect: 'none', - pointerEvents: 'none', - }, - }, - }; -}); - -export default useStyles; diff --git a/packages/core/client/src/schema-component/antd/gantt/index.ts b/packages/core/client/src/schema-component/antd/gantt/index.ts deleted file mode 100644 index ef34ff792..000000000 --- a/packages/core/client/src/schema-component/antd/gantt/index.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { ActionBar } from '../action'; -import { Gantt } from './components/gantt/gantt'; -import { GanttDesigner } from './Gantt.Designer'; -import { ViewMode } from './types/public-types'; -import { Event } from './components/gantt/Event'; - -Gantt.ActionBar = ActionBar; - -Gantt.ViewMode = ViewMode; -Gantt.Designer = GanttDesigner; -Gantt.Event = Event; - -// const GanttV2 = Gantt; - -export { Gantt }; diff --git a/packages/core/client/src/schema-component/antd/index.ts b/packages/core/client/src/schema-component/antd/index.ts index a135753b3..f496c14e8 100644 --- a/packages/core/client/src/schema-component/antd/index.ts +++ b/packages/core/client/src/schema-component/antd/index.ts @@ -21,13 +21,11 @@ export * from './form-dialog'; export * from './form-item'; export * from './form-v2'; export * from './g2plot'; -export * from './gantt'; export * from './grid'; export * from './grid-card'; export * from './icon-picker'; export * from './input'; export * from './input-number'; -export * from './kanban'; export * from './list'; export * from './markdown'; export * from './menu'; diff --git a/packages/core/client/src/schema-component/antd/kanban/Kanban.Designer.tsx b/packages/core/client/src/schema-component/antd/kanban/Kanban.Designer.tsx deleted file mode 100644 index f436e2269..000000000 --- a/packages/core/client/src/schema-component/antd/kanban/Kanban.Designer.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import { useField, useFieldSchema } from '@formily/react'; -import React from 'react'; -import { useFormBlockContext, useKanbanBlockContext } from '../../../block-provider'; -import { useCollection } from '../../../collection-manager'; -import { - GeneralSchemaDesigner, - SchemaSettingsBlockTitleItem, - SchemaSettingsDataScope, - SchemaSettingsDivider, - SchemaSettingsRemove, - SchemaSettingsTemplate, -} from '../../../schema-settings'; -import { useSchemaTemplate } from '../../../schema-templates'; -import { useDesignable } from '../../hooks'; -import { removeNullCondition } from '../filter'; -import { FixedBlockDesignerItem } from '../page'; - -export const KanbanDesigner = () => { - const { name, title } = useCollection(); - const field = useField(); - const fieldSchema = useFieldSchema(); - const { form } = useFormBlockContext(); - const { service } = useKanbanBlockContext(); - const { dn } = useDesignable(); - const defaultResource = fieldSchema?.['x-decorator-props']?.resource; - const template = useSchemaTemplate(); - - return ( - - - { - filter = removeNullCondition(filter); - 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': fieldSchema['x-decorator-props'], - }, - }); - }} - /> - - - - - - - ); -}; diff --git a/packages/core/client/src/schema-component/antd/kanban/__tests__/kanban.test.tsx b/packages/core/client/src/schema-component/antd/kanban/__tests__/kanban.test.tsx deleted file mode 100644 index 6ebf2347e..000000000 --- a/packages/core/client/src/schema-component/antd/kanban/__tests__/kanban.test.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import React from 'react'; -import { render, screen, waitFor } from 'testUtils'; -import App1 from '../demos/demo1'; - -describe('Kanban', () => { - it('should render correctly', async () => { - render(); - - await waitFor(() => { - // 每列卡片的状态 - expect(screen.getByText(/未开始/i)).toBeInTheDocument(); - expect(screen.getByText(/进行中/i)).toBeInTheDocument(); - expect(screen.getByText(/测试中/i)).toBeInTheDocument(); - expect(screen.getByText(/已完成/i)).toBeInTheDocument(); - - // 第一个卡片的内容 - expect(screen.getByText(/子表格字段缺少标题/i)).toBeInTheDocument(); - expect(screen.getByText(/见附件,在此位置显示标题,包括表单和详情/i)).toBeInTheDocument(); - - // 最后一个卡片的内容 - expect(screen.getByText(/新增 uischema:clearancestor action api/i)).toBeInTheDocument(); - }); - }); -}); diff --git a/packages/core/client/src/schema-component/antd/kanban/demos/collections.ts b/packages/core/client/src/schema-component/antd/kanban/demos/collections.ts deleted file mode 100644 index 6e8bc8f8c..000000000 --- a/packages/core/client/src/schema-component/antd/kanban/demos/collections.ts +++ /dev/null @@ -1,1319 +0,0 @@ -export default [ - { - 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:create' }, - 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:create' }, - 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/core/client/src/schema-component/antd/kanban/demos/data.ts b/packages/core/client/src/schema-component/antd/kanban/demos/data.ts deleted file mode 100644 index 411876154..000000000 --- a/packages/core/client/src/schema-component/antd/kanban/demos/data.ts +++ /dev/null @@ -1,2145 +0,0 @@ -export default { - data: [ - { - id: 4, - createdAt: '2022-03-07T07: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', - }, - { - id: 11, - createdAt: '2022-03-07T11:19:15.387Z', - updatedAt: '2022-03-11T12:15:36.537Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: null, - f_a4z4h45vi5b: null, - f_cht6rsiiiko: 'dnphxkwat35', - f_d93g4r08krl: null, - f_f7txg1oc3nt: '2022-03-07T11:19:15.387Z', - f_g8j5jvalqh0: '权限配置里出现了一个没有名字的数据表', - f_hpmvdltzs6m: 'irepvdzp1ac', - f_tegyd222bcc: null, - f_yc8jbfiqfvh: 's7hocw6twfk', - f_z27302tl2bf: 'mkvgt7ndsqd', - f_zek99qhv0vc: '2022-03-11T12:15:36.537Z', - }, - { - id: 12, - createdAt: '2022-03-07T11:19:45.894Z', - updatedAt: '2022-03-12T04:34:49.058Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: null, - f_a4z4h45vi5b: null, - f_cht6rsiiiko: 'r6l2wg1snxc', - f_d93g4r08krl: '1', - f_f7txg1oc3nt: '2022-03-07T11:19:45.894Z', - f_g8j5jvalqh0: '“允许注册”的配置项无效', - f_hpmvdltzs6m: null, - f_tegyd222bcc: null, - f_yc8jbfiqfvh: 'c4hobfb5k07', - f_z27302tl2bf: 'mkvgt7ndsqd', - f_zek99qhv0vc: '2022-03-12T04:34:49.058Z', - }, - { - id: 13, - createdAt: '2022-03-07T11:20:37.591Z', - 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:20:37.591Z', - f_g8j5jvalqh0: '“必填”的选项改为之前版本的方式', - f_hpmvdltzs6m: 'irepvdzp1ac', - f_tegyd222bcc: '为什么放到了“编辑”里?操作繁琐', - f_yc8jbfiqfvh: 's7hocw6twfk', - f_z27302tl2bf: '9yyv2tawmry', - f_zek99qhv0vc: '2022-03-10T16:02:25.805Z', - }, - { - id: 14, - createdAt: '2022-03-07T11:21:22.596Z', - updatedAt: '2022-03-12T06:11:27.855Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: null, - f_a4z4h45vi5b: null, - f_cht6rsiiiko: null, - f_d93g4r08krl: null, - f_f7txg1oc3nt: '2022-03-07T11:21:22.596Z', - f_g8j5jvalqh0: '导出', - f_hpmvdltzs6m: '67snt3e6yld', - f_tegyd222bcc: - '导出不可用。\n提供 Export 的 useAction hook,如 useExportAction;\n提供 ExportActionInitializer;\n提供 Export.Designer。', - f_yc8jbfiqfvh: 'v25527dxseu', - f_z27302tl2bf: 'mkvgt7ndsqd', - f_zek99qhv0vc: '2022-03-12T06:11:27.855Z', - }, - { - id: 15, - createdAt: '2022-03-07T11:22:53.622Z', - updatedAt: '2022-03-11T10:49:14.497Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: null, - f_a4z4h45vi5b: null, - f_cht6rsiiiko: 'l464jdsy7vr', - f_d93g4r08krl: null, - f_f7txg1oc3nt: '2022-03-07T11:22:53.622Z', - f_g8j5jvalqh0: '对话框的宽度太窄', - f_hpmvdltzs6m: 'irepvdzp1ac', - f_tegyd222bcc: '尽可能宽,width 80% 然后设置一个最大宽度?', - f_yc8jbfiqfvh: 's7hocw6twfk', - f_z27302tl2bf: '9yyv2tawmry', - f_zek99qhv0vc: '2022-03-11T10:49:14.497Z', - }, - { - id: 16, - createdAt: '2022-03-07T11:23:09.113Z', - 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:23:09.113Z', - f_g8j5jvalqh0: '表格列的宽度限制', - f_hpmvdltzs6m: 'irepvdzp1ac', - f_tegyd222bcc: '限制列宽最大和最小宽度;超出宽度的长文本自动省略,鼠标移上去之后显示全文。', - f_yc8jbfiqfvh: 's7hocw6twfk', - f_z27302tl2bf: '9yyv2tawmry', - f_zek99qhv0vc: '2022-03-10T16:02:25.805Z', - }, - { - id: 17, - createdAt: '2022-03-07T11:24:33.395Z', - updatedAt: '2022-03-23T03:09:20.668Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: null, - f_a4z4h45vi5b: 'r3fxlrunwd8', - f_cht6rsiiiko: 'm4xoc8rbafg', - f_d93g4r08krl: '3', - f_f7txg1oc3nt: '2022-03-07T11:24:33.395Z', - f_g8j5jvalqh0: '有预存条件的筛选无效', - f_hpmvdltzs6m: '67snt3e6yld', - f_tegyd222bcc: '筛选条件可以预存,但是预存的筛选项是空的,实际筛选时需要过滤掉,不然可能报错或者筛选不出来结果', - f_yc8jbfiqfvh: 's7hocw6twfk', - f_z27302tl2bf: 'mkvgt7ndsqd', - f_zek99qhv0vc: '2022-03-23T03:09:20.668Z', - }, - { - id: 18, - createdAt: '2022-03-07T11:25:46.415Z', - updatedAt: '2022-03-22T07:29:32.097Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: null, - f_a4z4h45vi5b: 'r3fxlrunwd8', - f_cht6rsiiiko: 'm4xoc8rbafg', - f_d93g4r08krl: '1', - f_f7txg1oc3nt: '2022-03-07T11:25:46.415Z', - f_g8j5jvalqh0: '筛选面板,将“取消”改为“清空”', - f_hpmvdltzs6m: null, - f_tegyd222bcc: '点击后,恢复默认状态并提交', - f_yc8jbfiqfvh: 'v25527dxseu', - f_z27302tl2bf: 'vx4bhmtsuus', - f_zek99qhv0vc: '2022-03-22T07:29:32.097Z', - }, - { - id: 19, - createdAt: '2022-03-07T11:27:21.151Z', - updatedAt: '2022-03-12T04:35:39.252Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: null, - f_a4z4h45vi5b: 'hgobsqbfnd4', - f_cht6rsiiiko: 'm4xoc8rbafg', - f_d93g4r08krl: '1', - f_f7txg1oc3nt: '2022-03-07T11:27:21.151Z', - f_g8j5jvalqh0: '关联字段的筛选功能', - f_hpmvdltzs6m: null, - f_tegyd222bcc: '见原型\nhttps://modao.cc/app/86f7125c21d93cb8f191be3bd5a7360be10b09a4#screen=skw4xpk9j5dc10w', - f_yc8jbfiqfvh: 'v25527dxseu', - f_z27302tl2bf: 'vx4bhmtsuus', - f_zek99qhv0vc: '2022-03-12T04:35:39.252Z', - }, - { - id: 20, - createdAt: '2022-03-07T11:29:07.244Z', - 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:29:07.244Z', - f_g8j5jvalqh0: '表格宽度超出后撑破页面', - f_hpmvdltzs6m: 'irepvdzp1ac', - f_tegyd222bcc: null, - f_yc8jbfiqfvh: 's7hocw6twfk', - f_z27302tl2bf: '9yyv2tawmry', - f_zek99qhv0vc: '2022-03-10T16:02:25.805Z', - }, - { - id: 21, - createdAt: '2022-03-07T11:30:05.575Z', - updatedAt: '2022-03-12T04:35:47.526Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: null, - f_a4z4h45vi5b: 'r3fxlrunwd8', - f_cht6rsiiiko: 'm4xoc8rbafg', - f_d93g4r08krl: '1', - f_f7txg1oc3nt: '2022-03-07T11:30:05.575Z', - f_g8j5jvalqh0: '操作按钮可添加图标', - f_hpmvdltzs6m: null, - f_tegyd222bcc: '同原来的版本', - f_yc8jbfiqfvh: 'v25527dxseu', - f_z27302tl2bf: 'mkvgt7ndsqd', - f_zek99qhv0vc: '2022-03-12T04:35:47.526Z', - }, - { - id: 22, - createdAt: '2022-03-07T11:33:53.255Z', - updatedAt: '2022-03-12T04:36:10.507Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: null, - f_a4z4h45vi5b: 'r3fxlrunwd8', - f_cht6rsiiiko: 'm4xoc8rbafg', - f_d93g4r08krl: '2', - f_f7txg1oc3nt: '2022-03-07T11:33:53.255Z', - f_g8j5jvalqh0: '筛选面板的样式', - f_hpmvdltzs6m: null, - f_tegyd222bcc: null, - f_yc8jbfiqfvh: 'v25527dxseu', - f_z27302tl2bf: '9yyv2tawmry', - f_zek99qhv0vc: '2022-03-12T04:36:10.507Z', - }, - { - id: 23, - createdAt: '2022-03-07T11:36:16.006Z', - updatedAt: '2022-03-12T04:36:18.977Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: null, - f_a4z4h45vi5b: null, - f_cht6rsiiiko: null, - f_d93g4r08krl: '2', - f_f7txg1oc3nt: '2022-03-07T11:36:16.006Z', - f_g8j5jvalqh0: '多语言和翻译', - f_hpmvdltzs6m: null, - f_tegyd222bcc: null, - f_yc8jbfiqfvh: 'c4hobfb5k07', - f_z27302tl2bf: 'mkvgt7ndsqd', - f_zek99qhv0vc: '2022-03-12T04:36:18.977Z', - }, - { - id: 24, - createdAt: '2022-03-07T11:38:09.867Z', - updatedAt: '2022-03-22T07:34:34.329Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: null, - f_a4z4h45vi5b: null, - f_cht6rsiiiko: 'r6l2wg1snxc', - f_d93g4r08krl: '2', - f_f7txg1oc3nt: '2022-03-07T11:38:09.867Z', - f_g8j5jvalqh0: '按钮左右拖拽的区域样式有问题。参考原来的版本', - f_hpmvdltzs6m: null, - f_tegyd222bcc: null, - f_yc8jbfiqfvh: 'v25527dxseu', - f_z27302tl2bf: '9yyv2tawmry', - f_zek99qhv0vc: '2022-03-22T07:34:34.329Z', - }, - { - id: 25, - createdAt: '2022-03-07T11:39:45.109Z', - updatedAt: '2022-03-12T04:37:14.041Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: null, - f_a4z4h45vi5b: 'r3fxlrunwd8', - f_cht6rsiiiko: 'm4xoc8rbafg', - f_d93g4r08krl: '2', - f_f7txg1oc3nt: '2022-03-07T11:39:45.109Z', - f_g8j5jvalqh0: '筛选按钮,缺少“可筛选的字段”配置项', - f_hpmvdltzs6m: null, - f_tegyd222bcc: '参照原来版本', - f_yc8jbfiqfvh: 'v25527dxseu', - f_z27302tl2bf: 'mkvgt7ndsqd', - f_zek99qhv0vc: '2022-03-12T04:37:14.041Z', - }, - { - id: 26, - createdAt: '2022-03-07T11:41:28.107Z', - updatedAt: '2022-03-12T04:37:29.158Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: null, - f_a4z4h45vi5b: 't8prrpfk7kl', - f_cht6rsiiiko: 'ftfccbha25a', - f_d93g4r08krl: '2', - f_f7txg1oc3nt: '2022-03-07T11:41:28.107Z', - f_g8j5jvalqh0: '表单和详情里,缺少在字段后直接添加字段的按钮', - f_hpmvdltzs6m: null, - f_tegyd222bcc: '见原版本', - f_yc8jbfiqfvh: 'c4hobfb5k07', - f_z27302tl2bf: 'mkvgt7ndsqd', - f_zek99qhv0vc: '2022-03-12T04:37:29.158Z', - }, - { - id: 27, - createdAt: '2022-03-07T11:42:33.151Z', - updatedAt: '2022-03-12T04:37:34.991Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: null, - f_a4z4h45vi5b: null, - f_cht6rsiiiko: null, - f_d93g4r08krl: '2', - f_f7txg1oc3nt: '2022-03-07T11:42:33.151Z', - f_g8j5jvalqh0: '文案问题', - f_hpmvdltzs6m: null, - f_tegyd222bcc: null, - f_yc8jbfiqfvh: 'v25527dxseu', - f_z27302tl2bf: 'mkvgt7ndsqd', - f_zek99qhv0vc: '2022-03-12T04:37:34.991Z', - }, - { - id: 28, - createdAt: '2022-03-07T12:33:30.520Z', - 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-07T12:33:30.520Z', - f_g8j5jvalqh0: '表格缺少设置排序规则', - f_hpmvdltzs6m: 'irepvdzp1ac', - f_tegyd222bcc: '同原来版本', - f_yc8jbfiqfvh: 'v25527dxseu', - f_z27302tl2bf: 'mkvgt7ndsqd', - f_zek99qhv0vc: '2022-03-10T16:02:25.805Z', - }, - { - id: 29, - createdAt: '2022-03-07T12:45:50.353Z', - updatedAt: '2022-03-23T00:03:12.633Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: 'Schema 的刷新问题', - f_a4z4h45vi5b: 'hgobsqbfnd4', - f_cht6rsiiiko: 'ftfccbha25a', - f_d93g4r08krl: '4', - f_f7txg1oc3nt: '2022-03-07T12:45:50.353Z', - f_g8j5jvalqh0: '向表单里新增一个radio字段或者复选框,新增后不可用', - f_hpmvdltzs6m: '67snt3e6yld', - f_tegyd222bcc: 'Schema 刷新问题导致\n\n点击无效,刷新页面才有效(或者好像是退出抽屉再重新打开好像也有效)', - f_yc8jbfiqfvh: 's7hocw6twfk', - f_z27302tl2bf: 'mkvgt7ndsqd', - f_zek99qhv0vc: '2022-03-23T00:03:12.633Z', - }, - { - id: 30, - createdAt: '2022-03-07T12:56:20.577Z', - updatedAt: '2022-03-12T04:38:00.199Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: null, - f_a4z4h45vi5b: 'hgobsqbfnd4', - f_cht6rsiiiko: 'r6l2wg1snxc', - f_d93g4r08krl: '1', - f_f7txg1oc3nt: '2022-03-07T12:56:20.577Z', - f_g8j5jvalqh0: '所有配置相关的菜单缺少 icon', - f_hpmvdltzs6m: null, - f_tegyd222bcc: '参照原来版本', - f_yc8jbfiqfvh: 'v25527dxseu', - f_z27302tl2bf: '9yyv2tawmry', - f_zek99qhv0vc: '2022-03-12T04:38:00.199Z', - }, - { - id: 31, - createdAt: '2022-03-07T13:59:25.051Z', - updatedAt: '2022-03-11T13:46:28.382Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: null, - f_a4z4h45vi5b: 'ygy1zfdr1z6', - f_cht6rsiiiko: 'dnphxkwat35', - f_d93g4r08krl: null, - f_f7txg1oc3nt: '2022-03-07T13:59:25.051Z', - f_g8j5jvalqh0: '配置菜单权限的时候,二级无法选择', - f_hpmvdltzs6m: 'irepvdzp1ac', - f_tegyd222bcc: null, - f_yc8jbfiqfvh: 's7hocw6twfk', - f_z27302tl2bf: 'mkvgt7ndsqd', - f_zek99qhv0vc: '2022-03-11T13:46:28.382Z', - }, - { - id: 32, - createdAt: '2022-03-08T00:27:59.518Z', - 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-08T00:27:59.518Z', - f_g8j5jvalqh0: '滚动条样式,同原来版本', - f_hpmvdltzs6m: 'irepvdzp1ac', - f_tegyd222bcc: null, - f_yc8jbfiqfvh: 'c4hobfb5k07', - f_z27302tl2bf: '9yyv2tawmry', - f_zek99qhv0vc: '2022-03-10T16:02:25.805Z', - }, - { - id: 33, - createdAt: '2022-03-08T04:22:30.799Z', - updatedAt: '2022-03-12T04:38:10.620Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: null, - f_a4z4h45vi5b: null, - f_cht6rsiiiko: 'r6l2wg1snxc', - f_d93g4r08krl: '5', - f_f7txg1oc3nt: '2022-03-08T04:22:30.799Z', - f_g8j5jvalqh0: '区块模板', - f_hpmvdltzs6m: 'ht963n365al', - f_tegyd222bcc: '详见原型', - f_yc8jbfiqfvh: 's7hocw6twfk', - f_z27302tl2bf: 'vx4bhmtsuus', - f_zek99qhv0vc: '2022-03-12T04:38:10.620Z', - }, - { - id: 34, - createdAt: '2022-03-08T04:52:30.912Z', - 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-08T04:52:30.912Z', - f_g8j5jvalqh0: '导航高亮改回原来版本的样式', - f_hpmvdltzs6m: 'irepvdzp1ac', - f_tegyd222bcc: '现在太突兀,不如原来版本吧', - f_yc8jbfiqfvh: 'v25527dxseu', - f_z27302tl2bf: '9yyv2tawmry', - f_zek99qhv0vc: '2022-03-10T16:02:25.805Z', - }, - { - id: 35, - createdAt: '2022-03-08T06:31:51.623Z', - updatedAt: '2022-03-11T10:23:31.842Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: null, - f_a4z4h45vi5b: null, - f_cht6rsiiiko: 'dnphxkwat35', - f_d93g4r08krl: null, - f_f7txg1oc3nt: '2022-03-08T06:31:51.623Z', - f_g8j5jvalqh0: '调试并解决 ACL 插件的细节问题(acl.middleware)', - f_hpmvdltzs6m: 'irepvdzp1ac', - f_tegyd222bcc: - '前端问题(问题已记录在其他任务里了):\n1. 菜单访问权限无法选中子菜单\n2. 取消勾选角色“允许配置系统,包括界面配置、数据表配置、权限配置、系统配置等全部配置项”,对应前端依旧显示系统配置项\n3. 未根据权限配置控制界面模块显示/隐藏\n4. 前端给角色配置了单独的数据权限,保存之后再继续配置,之前的配置不显示', - f_yc8jbfiqfvh: 's7hocw6twfk', - f_z27302tl2bf: 'mkvgt7ndsqd', - f_zek99qhv0vc: '2022-03-11T10:23:31.842Z', - }, - { - id: 36, - createdAt: '2022-03-08T12:55:23.048Z', - updatedAt: '2022-03-12T04:38:23.425Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: null, - f_a4z4h45vi5b: null, - f_cht6rsiiiko: 'm8z7r06vk9z', - f_d93g4r08krl: '4', - f_f7txg1oc3nt: '2022-03-08T12:55:23.048Z', - f_g8j5jvalqh0: '表头随页面滚动', - f_hpmvdltzs6m: null, - f_tegyd222bcc: - '主要问题在于:需要固定每一列的列宽和表格宽度,但是列是变化的,列宽也是不固定的\nhttps://ant.design/components/table-cn/#components-table-demo-sticky', - f_yc8jbfiqfvh: 'v25527dxseu', - f_z27302tl2bf: '9yyv2tawmry', - f_zek99qhv0vc: '2022-03-12T04:38:23.425Z', - }, - { - id: 37, - createdAt: '2022-03-09T00:04:30.123Z', - updatedAt: '2022-03-12T05:20:43.773Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: 'Schema 的刷新问题', - f_a4z4h45vi5b: 'bidogljo0l3', - f_cht6rsiiiko: 'ftfccbha25a', - f_d93g4r08krl: '4', - f_f7txg1oc3nt: '2022-03-09T00:04:30.123Z', - f_g8j5jvalqh0: '界面配置模式下,在表单区块里新增字段,字段里的值不显示', - f_hpmvdltzs6m: null, - f_tegyd222bcc: '关闭抽屉再打开也不行,必须刷新页面才能显示出字段里的值。是不是应该关闭抽屉再打开就可以?', - f_yc8jbfiqfvh: 'v25527dxseu', - f_z27302tl2bf: 'mkvgt7ndsqd', - f_zek99qhv0vc: '2022-03-12T05:20:43.773Z', - }, - { - id: 38, - createdAt: '2022-03-09T00:07:14.708Z', - updatedAt: '2022-03-23T03:08:56.311Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: null, - f_a4z4h45vi5b: null, - f_cht6rsiiiko: 'wge57qtiz14', - f_d93g4r08krl: null, - f_f7txg1oc3nt: '2022-03-09T00:07:14.708Z', - f_g8j5jvalqh0: '看板区块', - f_hpmvdltzs6m: 'irepvdzp1ac', - f_tegyd222bcc: null, - f_yc8jbfiqfvh: 'v25527dxseu', - f_z27302tl2bf: 'vx4bhmtsuus', - f_zek99qhv0vc: '2022-03-23T03:08:56.311Z', - }, - { - id: 39, - createdAt: '2022-03-09T00:12:26.948Z', - updatedAt: '2022-03-12T04:38:54.176Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: null, - f_a4z4h45vi5b: null, - f_cht6rsiiiko: null, - f_d93g4r08krl: '1', - f_f7txg1oc3nt: '2022-03-09T00:12:26.948Z', - f_g8j5jvalqh0: 'favicon', - f_hpmvdltzs6m: null, - f_tegyd222bcc: null, - f_yc8jbfiqfvh: 'v25527dxseu', - f_z27302tl2bf: '9yyv2tawmry', - f_zek99qhv0vc: '2022-03-12T04:38:54.176Z', - }, - { - id: 40, - createdAt: '2022-03-09T00:19:13.743Z', - updatedAt: '2022-03-12T05:39:40.670Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: null, - f_a4z4h45vi5b: null, - f_cht6rsiiiko: null, - f_d93g4r08krl: '1', - f_f7txg1oc3nt: '2022-03-09T00:19:13.743Z', - f_g8j5jvalqh0: '开源协议', - f_hpmvdltzs6m: null, - f_tegyd222bcc: null, - f_yc8jbfiqfvh: 'c4hobfb5k07', - f_z27302tl2bf: 'vx4bhmtsuus', - f_zek99qhv0vc: '2022-03-12T05:39:40.670Z', - }, - { - id: 42, - createdAt: '2022-03-09T04:48:22.030Z', - updatedAt: '2022-03-12T04:39:14.663Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: null, - f_a4z4h45vi5b: 'r3fxlrunwd8', - f_cht6rsiiiko: 'm8z7r06vk9z', - f_d93g4r08krl: '1', - f_f7txg1oc3nt: '2022-03-09T04:48:22.030Z', - f_g8j5jvalqh0: '表格里数字/百分比/日期类型的列,需要可以直接点击表头排序', - f_hpmvdltzs6m: null, - f_tegyd222bcc: null, - f_yc8jbfiqfvh: 'c4hobfb5k07', - f_z27302tl2bf: 'vx4bhmtsuus', - f_zek99qhv0vc: '2022-03-12T04:39:14.663Z', - }, - { - id: 43, - createdAt: '2022-03-09T04:48:54.203Z', - updatedAt: '2022-03-12T04:39:29.617Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: null, - f_a4z4h45vi5b: 'hgobsqbfnd4', - f_cht6rsiiiko: 'm8z7r06vk9z', - f_d93g4r08krl: '1', - f_f7txg1oc3nt: '2022-03-09T04:48:54.203Z', - f_g8j5jvalqh0: '表格里数字/百分比类型的列,需要右对齐', - f_hpmvdltzs6m: null, - f_tegyd222bcc: null, - f_yc8jbfiqfvh: 'v25527dxseu', - f_z27302tl2bf: 'vx4bhmtsuus', - f_zek99qhv0vc: '2022-03-12T04:39:29.617Z', - }, - { - id: 44, - createdAt: '2022-03-09T04:53:39.795Z', - updatedAt: '2022-03-12T04:39:37.328Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: null, - f_a4z4h45vi5b: 't8prrpfk7kl', - f_cht6rsiiiko: 'ftfccbha25a', - f_d93g4r08krl: '1', - f_f7txg1oc3nt: '2022-03-09T04:53:39.795Z', - f_g8j5jvalqh0: '表单和详情字段配置的“编辑描述”配置项', - f_hpmvdltzs6m: null, - f_tegyd222bcc: null, - f_yc8jbfiqfvh: 'v25527dxseu', - f_z27302tl2bf: 'vx4bhmtsuus', - f_zek99qhv0vc: '2022-03-12T04:39:37.328Z', - }, - { - id: 45, - createdAt: '2022-03-09T04:54:02.713Z', - updatedAt: '2022-03-12T04:40:50.575Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: null, - f_a4z4h45vi5b: 't8prrpfk7kl', - f_cht6rsiiiko: 'r6l2wg1snxc', - f_d93g4r08krl: '1', - f_f7txg1oc3nt: '2022-03-09T04:54:02.713Z', - f_g8j5jvalqh0: '按钮、字段、列 的“隐藏”配置项', - f_hpmvdltzs6m: null, - f_tegyd222bcc: null, - f_yc8jbfiqfvh: 'c4hobfb5k07', - f_z27302tl2bf: 'mkvgt7ndsqd', - f_zek99qhv0vc: '2022-03-12T04:40:50.575Z', - }, - { - id: 47, - createdAt: '2022-03-09T23:59:34.629Z', - updatedAt: '2022-03-12T04:40:04.185Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: null, - f_a4z4h45vi5b: 'bidogljo0l3', - f_cht6rsiiiko: '5cso9lpizog', - f_d93g4r08krl: '1', - f_f7txg1oc3nt: '2022-03-09T23:59:34.629Z', - f_g8j5jvalqh0: '数据表和字段的表格增加排序', - f_hpmvdltzs6m: null, - f_tegyd222bcc: null, - f_yc8jbfiqfvh: 'c4hobfb5k07', - f_z27302tl2bf: 'mkvgt7ndsqd', - f_zek99qhv0vc: '2022-03-12T04:40:04.185Z', - }, - { - id: 48, - createdAt: '2022-03-10T00:10:10.810Z', - updatedAt: '2022-03-12T12:53:24.774Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: null, - f_a4z4h45vi5b: 'bidogljo0l3', - f_cht6rsiiiko: 'wlg329nsu3a', - f_d93g4r08krl: '1', - f_f7txg1oc3nt: '2022-03-10T00:10:10.810Z', - f_g8j5jvalqh0: '关联字段关联多条数据配置无效', - f_hpmvdltzs6m: null, - f_tegyd222bcc: '不勾选,仍然可以选择多条数据', - f_yc8jbfiqfvh: 'v25527dxseu', - f_z27302tl2bf: 'mkvgt7ndsqd', - f_zek99qhv0vc: '2022-03-12T12:53:24.774Z', - }, - { - id: 49, - createdAt: '2022-03-10T00:12:09.056Z', - updatedAt: '2022-03-23T00:03:32.508Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: 'Schema 的刷新问题', - f_a4z4h45vi5b: 'hgobsqbfnd4', - f_cht6rsiiiko: 'o33ljfkje2h', - f_d93g4r08krl: '4', - f_f7txg1oc3nt: '2022-03-10T00:12:09.056Z', - f_g8j5jvalqh0: '在详情区块里添加字段,显示出来的是表单', - f_hpmvdltzs6m: 'ht963n365al', - f_tegyd222bcc: 'Schema 刷新问题导致\n\n补充:在详情里拖拽字段更改位置,松手后也变成了表单', - f_yc8jbfiqfvh: 's7hocw6twfk', - f_z27302tl2bf: 'mkvgt7ndsqd', - f_zek99qhv0vc: '2022-03-23T00:03:32.508Z', - }, - { - id: 50, - createdAt: '2022-03-10T00:19:02.562Z', - updatedAt: '2022-03-21T07:48:43.481Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: 'Schema 的刷新问题', - f_a4z4h45vi5b: 't8prrpfk7kl', - f_cht6rsiiiko: 'wlg329nsu3a', - f_d93g4r08krl: '4', - f_f7txg1oc3nt: '2022-03-10T00:19:02.562Z', - f_g8j5jvalqh0: '设置关联字段显示的标题无效', - f_hpmvdltzs6m: '67snt3e6yld', - f_tegyd222bcc: - '表格里,在设置标题字段之前添加的数据,会一直显示ID,即使重新编辑也不行;在设置标题字段之后添加的数据,可以正常显示标题\n再测试,好像刷新后就变正常了\n\n看板里,配置标题的卡片会正常显示,其他的要刷新', - f_yc8jbfiqfvh: 'v25527dxseu', - f_z27302tl2bf: 'mkvgt7ndsqd', - f_zek99qhv0vc: '2022-03-21T07:48:43.481Z', - }, - { - id: 52, - createdAt: '2022-03-10T00:33:11.573Z', - updatedAt: '2022-03-10T16:02:25.805Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: null, - f_a4z4h45vi5b: null, - f_cht6rsiiiko: 'wlg329nsu3a', - f_d93g4r08krl: null, - f_f7txg1oc3nt: '2022-03-10T00:33:11.573Z', - f_g8j5jvalqh0: '关联字段这里无法保存', - f_hpmvdltzs6m: 'irepvdzp1ac', - f_tegyd222bcc: - '备注:\n后端 reverseField 需要有个判断\n字段 update 时,如果已经有 reverseField 但是 reverseField 没传 key 报错', - f_yc8jbfiqfvh: 'v25527dxseu', - f_z27302tl2bf: 'mkvgt7ndsqd', - f_zek99qhv0vc: '2022-03-10T16:02:25.805Z', - }, - { - id: 53, - createdAt: '2022-03-10T03:41:10.218Z', - updatedAt: '2022-03-22T07:26:59.318Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: null, - f_a4z4h45vi5b: 'hgobsqbfnd4', - f_cht6rsiiiko: 'o33ljfkje2h', - f_d93g4r08krl: '1', - f_f7txg1oc3nt: '2022-03-10T03:41:10.218Z', - f_g8j5jvalqh0: 'textarea 字段里的换行是否可以显示在详情里?', - f_hpmvdltzs6m: null, - f_tegyd222bcc: null, - f_yc8jbfiqfvh: 'v25527dxseu', - f_z27302tl2bf: '9yyv2tawmry', - f_zek99qhv0vc: '2022-03-22T07:26:59.318Z', - }, - { - id: 54, - createdAt: '2022-03-10T03:42:14.074Z', - updatedAt: '2022-03-12T05:23:44.006Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: '这里还有些混乱,需要整理各种不同区块都可以有哪些操作可以配置。', - f_a4z4h45vi5b: 'hgobsqbfnd4', - f_cht6rsiiiko: 'r6l2wg1snxc', - f_d93g4r08krl: '3', - f_f7txg1oc3nt: '2022-03-10T03:42:14.074Z', - f_g8j5jvalqh0: '“编辑”操作里的按钮配置', - f_hpmvdltzs6m: null, - f_tegyd222bcc: - '在“添加”操作里,按钮可以配置,但是“编辑“里不能配置。这里是不是涉及到一个问题,这个按钮到底该固定在这里,还是跟着表单走?弹窗里也有同样的问题。另外,看相关任务。\n\n还有一个问题:在页面内直接添加表单区块,这个按钮只能显示或隐藏,不能修改文案。这几处都不统一。', - f_yc8jbfiqfvh: 'c4hobfb5k07', - f_z27302tl2bf: 'mkvgt7ndsqd', - f_zek99qhv0vc: '2022-03-12T05:23:44.006Z', - }, - { - id: 55, - createdAt: '2022-03-10T03:51:02.288Z', - updatedAt: '2022-03-12T05:09:31.796Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: '', - f_a4z4h45vi5b: 'bidogljo0l3', - f_cht6rsiiiko: 'ftfccbha25a', - f_d93g4r08krl: '2', - f_f7txg1oc3nt: '2022-03-10T03:51:02.288Z', - f_g8j5jvalqh0: 'markdown 编辑器', - f_hpmvdltzs6m: null, - f_tegyd222bcc: '包含基本的有序列表、无序列表、加粗、斜体等', - f_yc8jbfiqfvh: 'c4hobfb5k07', - f_z27302tl2bf: 'vx4bhmtsuus', - f_zek99qhv0vc: '2022-03-12T05:09:31.796Z', - }, - { - id: 56, - createdAt: '2022-03-10T04:04:06.260Z', - 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-10T04:04:06.260Z', - f_g8j5jvalqh0: '删除数据表里的字段后,报错', - f_hpmvdltzs6m: 'irepvdzp1ac', - f_tegyd222bcc: '', - f_yc8jbfiqfvh: 's7hocw6twfk', - f_z27302tl2bf: 'mkvgt7ndsqd', - f_zek99qhv0vc: '2022-03-10T16:02:25.805Z', - }, - { - id: 57, - createdAt: '2022-03-10T08:30:10.345Z', - updatedAt: '2022-03-12T04:42:42.525Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: null, - f_a4z4h45vi5b: null, - f_cht6rsiiiko: '5cso9lpizog', - f_d93g4r08krl: '1', - f_f7txg1oc3nt: '2022-03-10T08:30:10.345Z', - f_g8j5jvalqh0: '添加数据表时,自动添加系统字段', - f_hpmvdltzs6m: null, - f_tegyd222bcc: null, - f_yc8jbfiqfvh: 'v25527dxseu', - f_z27302tl2bf: 'vx4bhmtsuus', - f_zek99qhv0vc: '2022-03-12T04:42:42.525Z', - }, - { - id: 58, - createdAt: '2022-03-10T08:30:51.887Z', - updatedAt: '2022-03-12T05:37:15.444Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: '要从通用性来解决,细节还不少', - f_a4z4h45vi5b: null, - f_cht6rsiiiko: 'm4xoc8rbafg', - f_d93g4r08krl: '4', - f_f7txg1oc3nt: '2022-03-10T08:30:51.887Z', - f_g8j5jvalqh0: '排序支持拼音', - f_hpmvdltzs6m: null, - f_tegyd222bcc: null, - f_yc8jbfiqfvh: 'c4hobfb5k07', - f_z27302tl2bf: 'vx4bhmtsuus', - f_zek99qhv0vc: '2022-03-12T05:37:15.444Z', - }, - { - id: 59, - createdAt: '2022-03-10T08:31:23.927Z', - updatedAt: '2022-03-11T14:09:56.923Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: null, - f_a4z4h45vi5b: 'bidogljo0l3', - f_cht6rsiiiko: 'm4xoc8rbafg', - f_d93g4r08krl: null, - f_f7txg1oc3nt: '2022-03-10T08:31:23.927Z', - f_g8j5jvalqh0: '排序支持选择类型的字段', - f_hpmvdltzs6m: null, - f_tegyd222bcc: null, - f_yc8jbfiqfvh: 'v25527dxseu', - f_z27302tl2bf: 'vx4bhmtsuus', - f_zek99qhv0vc: '2022-03-11T14:09:56.923Z', - }, - { - id: 60, - createdAt: '2022-03-10T10:05:22.458Z', - updatedAt: '2022-03-13T12:09:17.469Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: null, - f_a4z4h45vi5b: null, - f_cht6rsiiiko: 'm4xoc8rbafg', - f_d93g4r08krl: '', - f_f7txg1oc3nt: '2022-03-10T10:05:22.458Z', - f_g8j5jvalqh0: '筛选问题,”不等于“ 应该包含为空', - f_hpmvdltzs6m: 'irepvdzp1ac', - f_tegyd222bcc: '现在只能筛选出”不等于且不为空“的,应该包含为空的', - f_yc8jbfiqfvh: 'v25527dxseu', - f_z27302tl2bf: 'mkvgt7ndsqd', - f_zek99qhv0vc: '2022-03-13T12:09:17.469Z', - }, - { - id: 61, - createdAt: '2022-03-10T10:17:29.361Z', - updatedAt: '2022-03-11T14:08:37.694Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: null, - f_a4z4h45vi5b: null, - f_cht6rsiiiko: 'r6l2wg1snxc', - f_d93g4r08krl: null, - f_f7txg1oc3nt: '2022-03-10T10:17:29.361Z', - f_g8j5jvalqh0: '系统配置里,上传按钮缺少文字和图标', - f_hpmvdltzs6m: 'irepvdzp1ac', - f_tegyd222bcc: null, - f_yc8jbfiqfvh: 'c4hobfb5k07', - f_z27302tl2bf: 'mkvgt7ndsqd', - f_zek99qhv0vc: '2022-03-11T14:08:37.694Z', - }, - { - id: 62, - createdAt: '2022-03-10T10:21:26.416Z', - updatedAt: '2022-03-11T14:08:24.881Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: null, - f_a4z4h45vi5b: null, - f_cht6rsiiiko: 'uduxfqpr5t2', - f_d93g4r08krl: null, - f_f7txg1oc3nt: '2022-03-10T10:21:26.416Z', - f_g8j5jvalqh0: '此处应该必填', - f_hpmvdltzs6m: 'irepvdzp1ac', - f_tegyd222bcc: null, - f_yc8jbfiqfvh: 'v25527dxseu', - f_z27302tl2bf: 'mkvgt7ndsqd', - f_zek99qhv0vc: '2022-03-11T14:08:24.881Z', - }, - { - id: 63, - createdAt: '2022-03-10T10:21:51.243Z', - updatedAt: '2022-03-12T04:47:34.630Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: null, - f_a4z4h45vi5b: 'hgobsqbfnd4', - f_cht6rsiiiko: 'j1cv1vf16fm', - f_d93g4r08krl: null, - f_f7txg1oc3nt: '2022-03-10T10:21:51.243Z', - f_g8j5jvalqh0: '子表格里的附件应该用更小的缩略图', - f_hpmvdltzs6m: 'irepvdzp1ac', - f_tegyd222bcc: '问题出现在添加子表格数据之后;当保存并退出再进入的时候,显示的就是缩略图了', - f_yc8jbfiqfvh: 'v25527dxseu', - f_z27302tl2bf: '9yyv2tawmry', - f_zek99qhv0vc: '2022-03-12T04:47:34.630Z', - }, - { - id: 65, - createdAt: '2022-03-10T11:48:12.793Z', - updatedAt: '2022-03-12T04:47:43.299Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: null, - f_a4z4h45vi5b: null, - f_cht6rsiiiko: null, - f_d93g4r08krl: '5', - f_f7txg1oc3nt: '2022-03-10T11:48:12.793Z', - f_g8j5jvalqh0: '字段类型转换', - f_hpmvdltzs6m: null, - f_tegyd222bcc: '不同类型的字段可以转换(见语雀文档)', - f_yc8jbfiqfvh: 'c4hobfb5k07', - f_z27302tl2bf: 'vx4bhmtsuus', - f_zek99qhv0vc: '2022-03-12T04:47:43.299Z', - }, - { - id: 66, - createdAt: '2022-03-10T11:55:59.753Z', - updatedAt: '2022-03-12T04:47:56.892Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: null, - f_a4z4h45vi5b: null, - f_cht6rsiiiko: null, - f_d93g4r08krl: '4', - f_f7txg1oc3nt: '2022-03-10T11:55:59.753Z', - f_g8j5jvalqh0: '兼容移动设备', - f_hpmvdltzs6m: null, - f_tegyd222bcc: null, - f_yc8jbfiqfvh: 'c4hobfb5k07', - f_z27302tl2bf: '9yyv2tawmry', - f_zek99qhv0vc: '2022-03-12T04:47:56.892Z', - }, - { - id: 67, - createdAt: '2022-03-10T12:01:40.260Z', - updatedAt: '2022-03-12T04:49:30.039Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: '如果只是简单的字段默认值是可以做的', - f_a4z4h45vi5b: 't8prrpfk7kl', - f_cht6rsiiiko: null, - f_d93g4r08krl: '1', - f_f7txg1oc3nt: '2022-03-10T12:01:40.260Z', - f_g8j5jvalqh0: '字段的默认值', - f_hpmvdltzs6m: '67snt3e6yld', - f_tegyd222bcc: '可以给表单字段设置默认值', - f_yc8jbfiqfvh: 'c4hobfb5k07', - f_z27302tl2bf: 'vx4bhmtsuus', - f_zek99qhv0vc: '2022-03-12T04:49:30.039Z', - }, - { - id: 68, - createdAt: '2022-03-10T12:05:08.756Z', - updatedAt: '2022-03-11T14:03:52.645Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: null, - f_a4z4h45vi5b: null, - f_cht6rsiiiko: 'wlg329nsu3a', - f_d93g4r08krl: null, - f_f7txg1oc3nt: '2022-03-10T12:05:08.756Z', - f_g8j5jvalqh0: '子表格里添加字段的交互与数据表里添加字段不一致', - f_hpmvdltzs6m: 'irepvdzp1ac', - f_tegyd222bcc: null, - f_yc8jbfiqfvh: 'v25527dxseu', - f_z27302tl2bf: 'mkvgt7ndsqd', - f_zek99qhv0vc: '2022-03-11T14:03:52.645Z', - }, - { - id: 69, - createdAt: '2022-03-10T13:06:43.802Z', - updatedAt: '2022-03-12T12:52:55.490Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: null, - f_a4z4h45vi5b: 'bidogljo0l3', - f_cht6rsiiiko: 'wlg329nsu3a', - f_d93g4r08krl: '1', - f_f7txg1oc3nt: '2022-03-10T13:06:43.802Z', - f_g8j5jvalqh0: '中国行政区未显示名称', - f_hpmvdltzs6m: '67snt3e6yld', - f_tegyd222bcc: null, - f_yc8jbfiqfvh: 'v25527dxseu', - f_z27302tl2bf: 'mkvgt7ndsqd', - f_zek99qhv0vc: '2022-03-12T12:52:55.490Z', - }, - { - id: 70, - createdAt: '2022-03-10T13:08:32.621Z', - updatedAt: '2022-03-12T05:21:14.133Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: 'Schema 的刷新问题', - f_a4z4h45vi5b: 'bidogljo0l3', - f_cht6rsiiiko: 'wlg329nsu3a', - f_d93g4r08krl: '4', - f_f7txg1oc3nt: '2022-03-10T13:08:32.621Z', - f_g8j5jvalqh0: '表单里添加图标字段,不刷新的情况下点击图标报错', - f_hpmvdltzs6m: '67snt3e6yld', - f_tegyd222bcc: null, - f_yc8jbfiqfvh: 'v25527dxseu', - f_z27302tl2bf: 'mkvgt7ndsqd', - f_zek99qhv0vc: '2022-03-12T05:21:14.133Z', - }, - { - id: 71, - createdAt: '2022-03-10T13:12:58.744Z', - updatedAt: '2022-03-12T04:50:47.441Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: null, - f_a4z4h45vi5b: 'bidogljo0l3', - f_cht6rsiiiko: 'wlg329nsu3a', - f_d93g4r08krl: '2', - f_f7txg1oc3nt: '2022-03-10T13:12:58.744Z', - f_g8j5jvalqh0: '手机和邮箱字段未验证格式', - f_hpmvdltzs6m: '67snt3e6yld', - f_tegyd222bcc: null, - f_yc8jbfiqfvh: 'v25527dxseu', - f_z27302tl2bf: 'mkvgt7ndsqd', - f_zek99qhv0vc: '2022-03-12T04:50:47.441Z', - }, - { - id: 73, - createdAt: '2022-03-10T13:21:24.331Z', - updatedAt: '2022-03-11T12:40:39.697Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: null, - f_a4z4h45vi5b: null, - f_cht6rsiiiko: 'l464jdsy7vr', - f_d93g4r08krl: null, - f_f7txg1oc3nt: '2022-03-10T13:21:24.331Z', - f_g8j5jvalqh0: '弹窗标签页样式调整', - f_hpmvdltzs6m: 'irepvdzp1ac', - f_tegyd222bcc: '改成跟抽屉里一样?', - f_yc8jbfiqfvh: 'v25527dxseu', - f_z27302tl2bf: '9yyv2tawmry', - f_zek99qhv0vc: '2022-03-11T12:40:39.697Z', - }, - { - id: 74, - createdAt: '2022-03-10T13:22:47.613Z', - updatedAt: '2022-03-11T14:09:56.923Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: null, - f_a4z4h45vi5b: 'bidogljo0l3', - f_cht6rsiiiko: 'wlg329nsu3a', - f_d93g4r08krl: null, - f_f7txg1oc3nt: '2022-03-10T13:22:47.613Z', - f_g8j5jvalqh0: '选项不要分页,每页数量改为100', - f_hpmvdltzs6m: 'irepvdzp1ac', - f_tegyd222bcc: null, - f_yc8jbfiqfvh: 'c4hobfb5k07', - f_z27302tl2bf: '9yyv2tawmry', - f_zek99qhv0vc: '2022-03-11T14:09:56.923Z', - }, - { - id: 75, - createdAt: '2022-03-10T13:30:58.978Z', - updatedAt: '2022-03-23T00:02:05.255Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: 'Schema 的刷新问题', - f_a4z4h45vi5b: 'hgobsqbfnd4', - f_cht6rsiiiko: 'j1cv1vf16fm', - f_d93g4r08krl: '4', - f_f7txg1oc3nt: '2022-03-10T13:30:58.978Z', - f_g8j5jvalqh0: '附件有时不显示', - f_hpmvdltzs6m: '67snt3e6yld', - f_tegyd222bcc: - 'Schema 刷新问题导致。 我在这条任务里,新增了一条子任务(原来有2条了),然后点击查看,子表格里这里附件就是空的,3行都是空的;点击编辑,进去附件就是有的。再进查看,还是空的,关掉,再进查看,就有附件了', - f_yc8jbfiqfvh: 'v25527dxseu', - f_z27302tl2bf: 'mkvgt7ndsqd', - f_zek99qhv0vc: '2022-03-23T00:02:05.255Z', - }, - { - id: 76, - createdAt: '2022-03-10T13:36:23.503Z', - updatedAt: '2022-03-11T13:54:35.004Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: null, - f_a4z4h45vi5b: null, - f_cht6rsiiiko: 'r6l2wg1snxc', - f_d93g4r08krl: null, - f_f7txg1oc3nt: '2022-03-10T13:36:23.503Z', - f_g8j5jvalqh0: '查看对话框,不应该有这组按钮', - f_hpmvdltzs6m: 'irepvdzp1ac', - f_tegyd222bcc: '看相关任务', - f_yc8jbfiqfvh: 'v25527dxseu', - f_z27302tl2bf: 'mkvgt7ndsqd', - f_zek99qhv0vc: '2022-03-11T13:54:35.004Z', - }, - { - id: 77, - createdAt: '2022-03-10T13:41:32.143Z', - updatedAt: '2022-03-12T04:53:10.789Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: null, - f_a4z4h45vi5b: 'r3fxlrunwd8', - f_cht6rsiiiko: 'wlg329nsu3a', - f_d93g4r08krl: '1', - f_f7txg1oc3nt: '2022-03-10T13:41:32.143Z', - f_g8j5jvalqh0: '关联数据的删除按钮', - f_hpmvdltzs6m: '67snt3e6yld', - f_tegyd222bcc: '为什么有的关联数据有叉号,有的没有?有叉号的点了也没反应', - f_yc8jbfiqfvh: 'v25527dxseu', - f_z27302tl2bf: 'mkvgt7ndsqd', - f_zek99qhv0vc: '2022-03-12T04:53:10.789Z', - }, - { - id: 78, - createdAt: '2022-03-10T13:42:58.888Z', - updatedAt: '2022-03-12T04:55:14.857Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: '只有某些操作可以加,像批量删除就不能加,因为打开的是相关数据的选择面板,这时候选择是给关联用的', - f_a4z4h45vi5b: 'hgobsqbfnd4', - f_cht6rsiiiko: 'm4xoc8rbafg', - f_d93g4r08krl: '2', - f_f7txg1oc3nt: '2022-03-10T13:42:58.888Z', - f_g8j5jvalqh0: '相关数据的选择页,应该有操作配置', - f_hpmvdltzs6m: '67snt3e6yld', - f_tegyd222bcc: null, - f_yc8jbfiqfvh: 'c4hobfb5k07', - f_z27302tl2bf: 'mkvgt7ndsqd', - f_zek99qhv0vc: '2022-03-12T04:55:14.857Z', - }, - { - id: 79, - createdAt: '2022-03-10T13:53:32.869Z', - updatedAt: '2022-03-12T05:08:16.907Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: '', - f_a4z4h45vi5b: 'hgobsqbfnd4', - f_cht6rsiiiko: 'ym9wwk3aqbu', - f_d93g4r08krl: '1', - f_f7txg1oc3nt: '2022-03-10T13:53:32.869Z', - f_g8j5jvalqh0: '日历的筛选按钮缺少配置项', - f_hpmvdltzs6m: '67snt3e6yld', - f_tegyd222bcc: null, - f_yc8jbfiqfvh: 'c4hobfb5k07', - f_z27302tl2bf: 'mkvgt7ndsqd', - f_zek99qhv0vc: '2022-03-12T05:08:16.907Z', - }, - { - id: 80, - createdAt: '2022-03-10T13:54:12.385Z', - updatedAt: '2022-03-12T05:07:40.387Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: '初始化问题', - f_a4z4h45vi5b: 'hgobsqbfnd4', - f_cht6rsiiiko: 'ym9wwk3aqbu', - f_d93g4r08krl: '1', - f_f7txg1oc3nt: '2022-03-10T13:54:12.385Z', - f_g8j5jvalqh0: '日历的按钮排序有问题', - f_hpmvdltzs6m: '67snt3e6yld', - f_tegyd222bcc: null, - f_yc8jbfiqfvh: 'c4hobfb5k07', - f_z27302tl2bf: 'mkvgt7ndsqd', - f_zek99qhv0vc: '2022-03-12T05:07:40.387Z', - }, - { - id: 81, - createdAt: '2022-03-10T13:54:49.313Z', - updatedAt: '2022-03-12T05:07:23.311Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: '初始化问题', - f_a4z4h45vi5b: 'hgobsqbfnd4', - f_cht6rsiiiko: 'ym9wwk3aqbu', - f_d93g4r08krl: '1', - f_f7txg1oc3nt: '2022-03-10T13:54:49.313Z', - f_g8j5jvalqh0: '日历新增按钮设置在对话框打开无效', - f_hpmvdltzs6m: '67snt3e6yld', - f_tegyd222bcc: null, - f_yc8jbfiqfvh: 'c4hobfb5k07', - f_z27302tl2bf: 'mkvgt7ndsqd', - f_zek99qhv0vc: '2022-03-12T05:07:23.311Z', - }, - { - id: 82, - createdAt: '2022-03-10T13:57:12.172Z', - updatedAt: '2022-03-22T07:25:57.994Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: '初始化的时候需要指定参数', - f_a4z4h45vi5b: 'hgobsqbfnd4', - f_cht6rsiiiko: 'm8z7r06vk9z', - f_d93g4r08krl: '1', - f_f7txg1oc3nt: '2022-03-10T13:57:12.172Z', - f_g8j5jvalqh0: '富文本类型在表格里显示应该去掉格式', - f_hpmvdltzs6m: '67snt3e6yld', - f_tegyd222bcc: '或者其他合理的处理,不要撑开表格', - f_yc8jbfiqfvh: 'v25527dxseu', - f_z27302tl2bf: '9yyv2tawmry', - f_zek99qhv0vc: '2022-03-22T07:25:57.994Z', - }, - { - id: 83, - createdAt: '2022-03-10T14:23:07.247Z', - updatedAt: '2022-03-11T02:51:46.690Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: null, - f_a4z4h45vi5b: null, - f_cht6rsiiiko: 'l464jdsy7vr', - f_d93g4r08krl: null, - f_f7txg1oc3nt: '2022-03-10T14:23:07.247Z', - f_g8j5jvalqh0: '抽屉的关闭按钮还是放到右上角', - f_hpmvdltzs6m: 'irepvdzp1ac', - f_tegyd222bcc: null, - f_yc8jbfiqfvh: 'c4hobfb5k07', - f_z27302tl2bf: '9yyv2tawmry', - f_zek99qhv0vc: '2022-03-11T02:51:46.690Z', - }, - { - id: 84, - createdAt: '2022-03-10T16:08:37.701Z', - updatedAt: '2022-03-18T07:19:13.529Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: null, - f_a4z4h45vi5b: null, - f_cht6rsiiiko: null, - f_d93g4r08krl: '', - f_f7txg1oc3nt: '2022-03-10T16:08:37.701Z', - f_g8j5jvalqh0: 'sort 字段的初始化', - f_hpmvdltzs6m: '2s2s8wzcnm0', - f_tegyd222bcc: - '如果已有数据之后添加的 sort 字段,已有数据需要初始化排序\n——————\n目前的实现有性能问题,大数据时会卡死。', - f_yc8jbfiqfvh: 'v25527dxseu', - f_z27302tl2bf: 'mkvgt7ndsqd', - f_zek99qhv0vc: '2022-03-18T07:19:13.529Z', - }, - { - id: 85, - createdAt: '2022-03-10T23:04:45.405Z', - updatedAt: '2022-03-13T12:09:54.439Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: null, - f_a4z4h45vi5b: null, - f_cht6rsiiiko: 'wlg329nsu3a', - f_d93g4r08krl: '', - f_f7txg1oc3nt: '2022-03-10T23:04:45.405Z', - f_g8j5jvalqh0: '更新时间有问题', - f_hpmvdltzs6m: 'irepvdzp1ac', - f_tegyd222bcc: '看板拖拽(move action)拖拽会导致更新日期被批量更新(批量排序)', - f_yc8jbfiqfvh: 'v25527dxseu', - f_z27302tl2bf: 'mkvgt7ndsqd', - f_zek99qhv0vc: '2022-03-13T12:09:54.439Z', - }, - { - id: 86, - createdAt: '2022-03-10T23:10:16.017Z', - updatedAt: '2022-03-22T07:25:44.237Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: '涉及到宽度的都不太好控制', - f_a4z4h45vi5b: null, - f_cht6rsiiiko: 'm8z7r06vk9z', - f_d93g4r08krl: '3', - f_f7txg1oc3nt: '2022-03-10T23:10:16.017Z', - f_g8j5jvalqh0: '表格列最小宽度能否适应表头内容,不要让表头换行?', - f_hpmvdltzs6m: '67snt3e6yld', - f_tegyd222bcc: '如果有了拖拽调整列宽,这个就不需要了', - f_yc8jbfiqfvh: 'v25527dxseu', - f_z27302tl2bf: '9yyv2tawmry', - f_zek99qhv0vc: '2022-03-22T07:25:44.237Z', - }, - { - id: 87, - createdAt: '2022-03-10T23:42:50.529Z', - updatedAt: '2022-03-12T05:05:49.593Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: '', - f_a4z4h45vi5b: null, - f_cht6rsiiiko: 'wge57qtiz14', - f_d93g4r08krl: '2', - f_f7txg1oc3nt: '2022-03-10T23:42:50.529Z', - f_g8j5jvalqh0: '看板可以配置分组字段', - f_hpmvdltzs6m: '67snt3e6yld', - f_tegyd222bcc: null, - f_yc8jbfiqfvh: 'c4hobfb5k07', - f_z27302tl2bf: 'vx4bhmtsuus', - f_zek99qhv0vc: '2022-03-12T05:05:49.593Z', - }, - { - id: 88, - createdAt: '2022-03-10T23:43:45.838Z', - updatedAt: '2022-03-11T02:23:28.119Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: null, - f_a4z4h45vi5b: null, - f_cht6rsiiiko: 'wge57qtiz14', - f_d93g4r08krl: null, - f_f7txg1oc3nt: '2022-03-10T23:43:45.838Z', - f_g8j5jvalqh0: '看板的字段可以拖拽一行多个', - f_hpmvdltzs6m: 'irepvdzp1ac', - f_tegyd222bcc: null, - f_yc8jbfiqfvh: 'v25527dxseu', - f_z27302tl2bf: 'vx4bhmtsuus', - f_zek99qhv0vc: '2022-03-11T02:23:28.119Z', - }, - { - id: 89, - createdAt: '2022-03-11T00:38:17.857Z', - updatedAt: '2022-03-11T13:50:38.057Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: null, - f_a4z4h45vi5b: null, - f_cht6rsiiiko: 'wge57qtiz14', - f_d93g4r08krl: null, - f_f7txg1oc3nt: '2022-03-11T00:38:17.857Z', - f_g8j5jvalqh0: '看板内容超出宽度用省略号', - f_hpmvdltzs6m: 'irepvdzp1ac', - f_tegyd222bcc: null, - f_yc8jbfiqfvh: 'c4hobfb5k07', - f_z27302tl2bf: '9yyv2tawmry', - f_zek99qhv0vc: '2022-03-11T13:50:38.057Z', - }, - { - id: 90, - createdAt: '2022-03-11T01:10:32.767Z', - updatedAt: '2022-03-12T11:23:18.211Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: null, - f_a4z4h45vi5b: null, - f_cht6rsiiiko: 'uduxfqpr5t2', - f_d93g4r08krl: '3', - f_f7txg1oc3nt: '2022-03-11T01:10:32.767Z', - f_g8j5jvalqh0: '浏览器前进后退,菜单不跟着变', - f_hpmvdltzs6m: '67snt3e6yld', - f_tegyd222bcc: null, - f_yc8jbfiqfvh: 'c4hobfb5k07', - f_z27302tl2bf: 'mkvgt7ndsqd', - f_zek99qhv0vc: '2022-03-12T11:23:18.211Z', - }, - { - id: 91, - createdAt: '2022-03-11T02:23:13.107Z', - updatedAt: '2022-03-22T07:25:12.948Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: null, - f_a4z4h45vi5b: null, - f_cht6rsiiiko: null, - f_d93g4r08krl: '1', - f_f7txg1oc3nt: '2022-03-11T02:23:13.107Z', - f_g8j5jvalqh0: '将 label 颜色改为 #8c8c8c', - f_hpmvdltzs6m: 'irepvdzp1ac', - f_tegyd222bcc: - '.ant-formily-item-label 目前是 color: rgba(0, 0, 0, 0.85)\nlabel 跟内容区分度太低,改成 color: #8c8c8c', - f_yc8jbfiqfvh: 'v25527dxseu', - f_z27302tl2bf: '9yyv2tawmry', - f_zek99qhv0vc: '2022-03-22T07:25:12.948Z', - }, - { - id: 92, - createdAt: '2022-03-11T02:29:04.573Z', - updatedAt: '2022-03-22T07:25:26.741Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: '区域的问题太小会有其他问题', - f_a4z4h45vi5b: null, - f_cht6rsiiiko: 'r6l2wg1snxc', - f_d93g4r08krl: '3', - f_f7txg1oc3nt: '2022-03-11T02:29:04.573Z', - f_g8j5jvalqh0: '减小拖拽区域的尺寸', - f_hpmvdltzs6m: '67snt3e6yld', - f_tegyd222bcc: '这个尺寸太大,导致空白处太多', - f_yc8jbfiqfvh: 'v25527dxseu', - f_z27302tl2bf: '9yyv2tawmry', - f_zek99qhv0vc: '2022-03-22T07:25:26.741Z', - }, - { - id: 93, - createdAt: '2022-03-11T06:53:55.353Z', - updatedAt: '2022-03-22T07:24:48.859Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: '这种 onClick 的问题都不太好解决', - f_a4z4h45vi5b: null, - f_cht6rsiiiko: 'wge57qtiz14', - f_d93g4r08krl: '3', - f_f7txg1oc3nt: '2022-03-11T06:53:55.353Z', - f_g8j5jvalqh0: '看板字段重命名的时候,点击输入框会打开抽屉', - f_hpmvdltzs6m: '67snt3e6yld', - f_tegyd222bcc: null, - f_yc8jbfiqfvh: 'v25527dxseu', - f_z27302tl2bf: 'mkvgt7ndsqd', - f_zek99qhv0vc: '2022-03-22T07:24:48.859Z', - }, - { - id: 94, - createdAt: '2022-03-11T06:54:28.016Z', - updatedAt: '2022-03-12T05:12:20.285Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: 'Schema 刷新的问题', - f_a4z4h45vi5b: null, - f_cht6rsiiiko: 'wge57qtiz14', - f_d93g4r08krl: '4', - f_f7txg1oc3nt: '2022-03-11T06:54:28.016Z', - f_g8j5jvalqh0: '看板字段重命名后,需刷新其他卡片才生效', - f_hpmvdltzs6m: '67snt3e6yld', - f_tegyd222bcc: null, - f_yc8jbfiqfvh: 'c4hobfb5k07', - f_z27302tl2bf: 'mkvgt7ndsqd', - f_zek99qhv0vc: '2022-03-12T05:12:20.285Z', - }, - { - id: 95, - createdAt: '2022-03-11T06:57:24.873Z', - updatedAt: '2022-03-21T13:47:23.835Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: null, - f_a4z4h45vi5b: null, - f_cht6rsiiiko: 'r6l2wg1snxc', - f_d93g4r08krl: '1', - f_f7txg1oc3nt: '2022-03-11T06:57:24.873Z', - f_g8j5jvalqh0: '语言设置无效', - f_hpmvdltzs6m: 'irepvdzp1ac', - f_tegyd222bcc: '选中文依然显示英文', - f_yc8jbfiqfvh: 'v25527dxseu', - f_z27302tl2bf: 'mkvgt7ndsqd', - f_zek99qhv0vc: '2022-03-21T13:47:23.835Z', - }, - { - id: 96, - createdAt: '2022-03-11T07:02:59.188Z', - updatedAt: '2022-03-23T00:01:11.688Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: '问题待进一步排查和测试', - f_a4z4h45vi5b: null, - f_cht6rsiiiko: null, - f_d93g4r08krl: '5', - f_f7txg1oc3nt: '2022-03-11T07:02:59.188Z', - f_g8j5jvalqh0: '各处加载慢、卡顿掉帧', - f_hpmvdltzs6m: 'irepvdzp1ac', - f_tegyd222bcc: '阶段性完成', - f_yc8jbfiqfvh: 'c4hobfb5k07', - f_z27302tl2bf: 'mkvgt7ndsqd', - f_zek99qhv0vc: '2022-03-23T00:01:11.688Z', - }, - { - id: 97, - createdAt: '2022-03-11T09:07:01.959Z', - updatedAt: '2022-03-12T05:13:05.939Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: null, - f_a4z4h45vi5b: null, - f_cht6rsiiiko: 'dnphxkwat35', - f_d93g4r08krl: '3', - f_f7txg1oc3nt: '2022-03-11T09:07:01.959Z', - f_g8j5jvalqh0: '解决由前端处理的权限问题', - f_hpmvdltzs6m: '67snt3e6yld', - f_tegyd222bcc: - '后端输出当前角色的配置,由前端控制各部件组件的显示,包括:\n1. 菜单\n2. 数据表\n3. 字段\n4. 操作\n5. 右上角的配置 icons', - f_yc8jbfiqfvh: 'v25527dxseu', - f_z27302tl2bf: 'mkvgt7ndsqd', - f_zek99qhv0vc: '2022-03-12T05:13:05.939Z', - }, - { - id: 98, - createdAt: '2022-03-11T09:09:42.911Z', - updatedAt: '2022-03-12T05:13:47.228Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: null, - f_a4z4h45vi5b: null, - f_cht6rsiiiko: 'dnphxkwat35', - f_d93g4r08krl: '2', - f_f7txg1oc3nt: '2022-03-11T09:09:42.911Z', - f_g8j5jvalqh0: '角色和权限的配置', - f_hpmvdltzs6m: '67snt3e6yld', - f_tegyd222bcc: - '1. 菜单访问权限的菜单列表无法选中子菜单\n2. 前端给角色配置了单独的数据权限,保存之后再继续配置,之前的配置不显示\n3. 数据范围缺少初始化数据,所有数据和自己的数据', - f_yc8jbfiqfvh: 'v25527dxseu', - f_z27302tl2bf: 'mkvgt7ndsqd', - f_zek99qhv0vc: '2022-03-12T05:13:47.228Z', - }, - { - id: 99, - createdAt: '2022-03-11T12:09:19.676Z', - updatedAt: '2022-03-13T02:30:33.916Z', - createdById: 5, - updatedById: 1, - f_1ckuegfab9s: null, - f_a4z4h45vi5b: null, - f_cht6rsiiiko: 'dnphxkwat35', - f_d93g4r08krl: '', - f_f7txg1oc3nt: '2022-03-11T12:09:19.676Z', - f_g8j5jvalqh0: '非 root user 无法更新数据', - f_hpmvdltzs6m: 'irepvdzp1ac', - f_tegyd222bcc: null, - f_yc8jbfiqfvh: 's7hocw6twfk', - f_z27302tl2bf: 'mkvgt7ndsqd', - f_zek99qhv0vc: '2022-03-13T02:30:33.916Z', - }, - { - id: 100, - createdAt: '2022-03-11T13:58:27.991Z', - updatedAt: '2022-03-12T05:14:02.989Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: null, - f_a4z4h45vi5b: null, - f_cht6rsiiiko: 'l464jdsy7vr', - f_d93g4r08krl: '1', - f_f7txg1oc3nt: '2022-03-11T13:58:27.991Z', - f_g8j5jvalqh0: 'tab 上方间距过大', - f_hpmvdltzs6m: '67snt3e6yld', - f_tegyd222bcc: - '是 ant design 默认就这么大?感觉很浪费空间\n\n这条可以等我整体上整理一下所有应该调节间距的地方,一起处理会不会更合理?', - f_yc8jbfiqfvh: 'c4hobfb5k07', - f_z27302tl2bf: '9yyv2tawmry', - f_zek99qhv0vc: '2022-03-12T05:14:02.989Z', - }, - { - id: 101, - createdAt: '2022-03-11T14:20:14.179Z', - updatedAt: '2022-03-22T23:35:45.641Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: null, - f_a4z4h45vi5b: null, - f_cht6rsiiiko: 'uduxfqpr5t2', - f_d93g4r08krl: '1', - f_f7txg1oc3nt: '2022-03-11T14:20:14.179Z', - f_g8j5jvalqh0: '菜单文字没有垂直居中', - f_hpmvdltzs6m: 'irepvdzp1ac', - f_tegyd222bcc: null, - f_yc8jbfiqfvh: 'v25527dxseu', - f_z27302tl2bf: '9yyv2tawmry', - f_zek99qhv0vc: '2022-03-22T23:35:45.641Z', - }, - { - id: 102, - createdAt: '2022-03-11T14:37:53.642Z', - updatedAt: '2022-03-22T07:24:06.421Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: null, - f_a4z4h45vi5b: null, - f_cht6rsiiiko: 'uduxfqpr5t2', - f_d93g4r08krl: '2', - f_f7txg1oc3nt: '2022-03-11T14:37:53.642Z', - f_g8j5jvalqh0: '菜单/表格表头 拖拽排序的位置提示', - f_hpmvdltzs6m: '67snt3e6yld', - f_tegyd222bcc: - '我觉得可以跟区块/字段的拖拽效果统一起来,都用0.5菜单版本的细线? 区块和字段现在的拖拽提示也太大了,太占地方,导致不拖拽的时候空白太大', - f_yc8jbfiqfvh: 'v25527dxseu', - f_z27302tl2bf: '9yyv2tawmry', - f_zek99qhv0vc: '2022-03-22T07:24:06.421Z', - }, - { - id: 103, - createdAt: '2022-03-12T00:32:12.114Z', - updatedAt: '2022-03-12T05:15:44.760Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: null, - f_a4z4h45vi5b: null, - f_cht6rsiiiko: 'r6l2wg1snxc', - f_d93g4r08krl: '1', - f_f7txg1oc3nt: '2022-03-12T00:32:12.114Z', - f_g8j5jvalqh0: '字段列表太长时,显示不完整', - f_hpmvdltzs6m: '67snt3e6yld', - f_tegyd222bcc: null, - f_yc8jbfiqfvh: 'c4hobfb5k07', - f_z27302tl2bf: 'mkvgt7ndsqd', - f_zek99qhv0vc: '2022-03-12T05:15:44.760Z', - }, - { - id: 104, - createdAt: '2022-03-12T04:08:41.956Z', - updatedAt: '2022-03-13T12:08:39.001Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: null, - f_a4z4h45vi5b: null, - f_cht6rsiiiko: 'dnphxkwat35', - f_d93g4r08krl: '', - f_f7txg1oc3nt: '2022-03-12T04:08:41.956Z', - f_g8j5jvalqh0: 'roles:check 接口,返回当前角色的权限配置', - f_hpmvdltzs6m: 'irepvdzp1ac', - f_tegyd222bcc: '- app.acl.getRole().toJSON()\n- 除了常规的配置还有菜单和系统配置', - f_yc8jbfiqfvh: 'v25527dxseu', - f_z27302tl2bf: 'mkvgt7ndsqd', - f_zek99qhv0vc: '2022-03-13T12:08:39.001Z', - }, - { - id: 105, - createdAt: '2022-03-13T01:12:50.997Z', - updatedAt: '2022-03-22T23:52:08.262Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: null, - f_a4z4h45vi5b: null, - f_cht6rsiiiko: 'uduxfqpr5t2', - f_d93g4r08krl: null, - f_f7txg1oc3nt: '2022-03-13T01:12:50.997Z', - f_g8j5jvalqh0: '导航左侧未对齐', - f_hpmvdltzs6m: '67snt3e6yld', - f_tegyd222bcc: null, - f_yc8jbfiqfvh: 'v25527dxseu', - f_z27302tl2bf: 'mkvgt7ndsqd', - f_zek99qhv0vc: '2022-03-22T23:52:08.262Z', - }, - { - id: 106, - createdAt: '2022-03-13T12:19:20.072Z', - updatedAt: '2022-03-13T12:19:20.072Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: null, - f_a4z4h45vi5b: null, - f_cht6rsiiiko: null, - f_d93g4r08krl: null, - f_f7txg1oc3nt: '2022-03-13T12:19:20.072Z', - f_g8j5jvalqh0: 'users:check 和 roles:check 是公开的?', - f_hpmvdltzs6m: '67snt3e6yld', - f_tegyd222bcc: '如果未登录用户也可以访问后台,这两个 api 应该也是公开的?', - f_yc8jbfiqfvh: null, - f_z27302tl2bf: null, - f_zek99qhv0vc: '2022-03-13T12:19:20.072Z', - }, - { - id: 107, - createdAt: '2022-03-14T01:24:01.095Z', - updatedAt: '2022-03-14T01:24:01.095Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: null, - f_a4z4h45vi5b: null, - f_cht6rsiiiko: null, - f_d93g4r08krl: null, - f_f7txg1oc3nt: '2022-03-14T01:24:01.095Z', - f_g8j5jvalqh0: 'Plugin multi-apps', - f_hpmvdltzs6m: 'ht963n365al', - f_tegyd222bcc: '', - f_yc8jbfiqfvh: null, - f_z27302tl2bf: null, - f_zek99qhv0vc: '2022-03-14T01:24:01.095Z', - }, - { - id: 108, - createdAt: '2022-03-14T05:28:51.802Z', - updatedAt: '2022-03-14T05:28:51.802Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: null, - f_a4z4h45vi5b: null, - f_cht6rsiiiko: 'uduxfqpr5t2', - f_d93g4r08krl: null, - f_f7txg1oc3nt: '2022-03-14T05:28:51.802Z', - f_g8j5jvalqh0: 'URL 变化,菜单高亮要随着变化', - f_hpmvdltzs6m: null, - f_tegyd222bcc: null, - f_yc8jbfiqfvh: 'c4hobfb5k07', - f_z27302tl2bf: 'mkvgt7ndsqd', - f_zek99qhv0vc: '2022-03-14T05:28:51.802Z', - }, - { - id: 109, - createdAt: '2022-03-14T07:51:38.047Z', - updatedAt: '2022-03-14T07:51:38.047Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: null, - f_a4z4h45vi5b: null, - f_cht6rsiiiko: 'o33ljfkje2h', - f_d93g4r08krl: null, - f_f7txg1oc3nt: '2022-03-14T07:51:38.047Z', - f_g8j5jvalqh0: '详情里,子表格不应该有操作按钮', - f_hpmvdltzs6m: '67snt3e6yld', - f_tegyd222bcc: null, - f_yc8jbfiqfvh: 'c4hobfb5k07', - f_z27302tl2bf: 'mkvgt7ndsqd', - f_zek99qhv0vc: '2022-03-14T07:51:38.047Z', - }, - { - id: 110, - createdAt: '2022-03-15T16:16:34.954Z', - updatedAt: '2022-03-15T16:16:34.954Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: null, - f_a4z4h45vi5b: null, - f_cht6rsiiiko: 'r6l2wg1snxc', - f_d93g4r08krl: null, - f_f7txg1oc3nt: '2022-03-15T16:16:34.954Z', - f_g8j5jvalqh0: '区块模板 —— 数据表设计', - f_hpmvdltzs6m: '2s2s8wzcnm0', - f_tegyd222bcc: null, - f_yc8jbfiqfvh: 's7hocw6twfk', - f_z27302tl2bf: 'vx4bhmtsuus', - f_zek99qhv0vc: '2022-03-15T16:16:34.954Z', - }, - { - id: 111, - createdAt: '2022-03-15T16:19:28.063Z', - updatedAt: '2022-03-15T16:27:02.858Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: null, - f_a4z4h45vi5b: null, - f_cht6rsiiiko: 'r6l2wg1snxc', - f_d93g4r08krl: null, - f_f7txg1oc3nt: '2022-03-15T16:19:28.063Z', - f_g8j5jvalqh0: '区块模板的管理界面(右上角 icon 及相关页面)', - f_hpmvdltzs6m: '2s2s8wzcnm0', - f_tegyd222bcc: null, - f_yc8jbfiqfvh: 's7hocw6twfk', - f_z27302tl2bf: 'vx4bhmtsuus', - f_zek99qhv0vc: '2022-03-15T16:27:02.858Z', - }, - { - id: 112, - createdAt: '2022-03-15T16:20:53.572Z', - updatedAt: '2022-03-16T10:03:28.490Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: null, - f_a4z4h45vi5b: null, - f_cht6rsiiiko: 'r6l2wg1snxc', - f_d93g4r08krl: null, - f_f7txg1oc3nt: '2022-03-15T16:20:53.572Z', - f_g8j5jvalqh0: '区块模板 —— 改进页面的「创建区块」按钮', - f_hpmvdltzs6m: '2s2s8wzcnm0', - f_tegyd222bcc: '- 页面的「创建区块」按钮', - f_yc8jbfiqfvh: 's7hocw6twfk', - f_z27302tl2bf: 'vx4bhmtsuus', - f_zek99qhv0vc: '2022-03-16T10:03:28.490Z', - }, - { - id: 113, - createdAt: '2022-03-15T16:23:14.634Z', - updatedAt: '2022-03-16T10:05:13.763Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: null, - f_a4z4h45vi5b: null, - f_cht6rsiiiko: 'r6l2wg1snxc', - f_d93g4r08krl: null, - f_f7txg1oc3nt: '2022-03-15T16:23:14.634Z', - f_g8j5jvalqh0: '区块模板 —— 区块配置的下拉菜单的改进', - f_hpmvdltzs6m: '2s2s8wzcnm0', - f_tegyd222bcc: - '支持「保存为模板」和「引用转为复制」\n区块删除时,如果删除的是引用模板,只是从界面去掉,但不删除模板', - f_yc8jbfiqfvh: 's7hocw6twfk', - f_z27302tl2bf: 'vx4bhmtsuus', - f_zek99qhv0vc: '2022-03-16T10:05:13.763Z', - }, - { - id: 114, - createdAt: '2022-03-15T16:24:38.605Z', - updatedAt: '2022-03-15T16:26:46.418Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: null, - f_a4z4h45vi5b: null, - f_cht6rsiiiko: 'r6l2wg1snxc', - f_d93g4r08krl: null, - f_f7txg1oc3nt: '2022-03-15T16:24:38.605Z', - f_g8j5jvalqh0: '区块模板 —— 添加和编辑按钮的弹窗面板改为与查看面板一致的交互', - f_hpmvdltzs6m: 'ht963n365al', - f_tegyd222bcc: null, - f_yc8jbfiqfvh: 's7hocw6twfk', - f_z27302tl2bf: 'vx4bhmtsuus', - f_zek99qhv0vc: '2022-03-15T16:26:46.418Z', - }, - { - id: 115, - createdAt: '2022-03-16T07:15:43.439Z', - updatedAt: '2022-03-17T08:57:24.643Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: null, - f_a4z4h45vi5b: null, - f_cht6rsiiiko: null, - f_d93g4r08krl: null, - f_f7txg1oc3nt: '2022-03-16T07:15:43.439Z', - f_g8j5jvalqh0: '新增 uiSchema:clearAncestor Action API', - f_hpmvdltzs6m: 'irepvdzp1ac', - f_tegyd222bcc: null, - f_yc8jbfiqfvh: null, - f_z27302tl2bf: null, - f_zek99qhv0vc: '2022-03-17T08:57:24.643Z', - }, - { - id: 116, - createdAt: '2022-03-16T07:18:53.292Z', - updatedAt: '2022-03-16T07:18:53.292Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: null, - f_a4z4h45vi5b: null, - f_cht6rsiiiko: null, - f_d93g4r08krl: null, - f_f7txg1oc3nt: '2022-03-16T07:18:53.292Z', - f_g8j5jvalqh0: 'uiSchemas:insertAdjacent 更友好的错误提示', - f_hpmvdltzs6m: '67snt3e6yld', - f_tegyd222bcc: - 'uiSchemas:insertAdjacent 如果 uid 是根节点,不允许插入 beforeBegin 和 afterEnd 位置,报错提示的时候,message 为:“不允许插入 beforeBegin 或 afterEnd 位置”', - f_yc8jbfiqfvh: null, - f_z27302tl2bf: null, - f_zek99qhv0vc: '2022-03-16T07:18:53.292Z', - }, - { - id: 117, - createdAt: '2022-03-16T10:06:11.462Z', - updatedAt: '2022-03-16T10:06:11.462Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: null, - f_a4z4h45vi5b: null, - f_cht6rsiiiko: 'r6l2wg1snxc', - f_d93g4r08krl: null, - f_f7txg1oc3nt: '2022-03-16T10:06:11.462Z', - f_g8j5jvalqh0: '区块模板 - 某条数据的添加、编辑、查看面板的「创建区块」按钮的改进', - f_hpmvdltzs6m: 'ht963n365al', - f_tegyd222bcc: null, - f_yc8jbfiqfvh: 's7hocw6twfk', - f_z27302tl2bf: 'vx4bhmtsuus', - f_zek99qhv0vc: '2022-03-16T10:06:11.462Z', - }, - { - id: 118, - createdAt: '2022-03-16T12:22:59.873Z', - updatedAt: '2022-03-16T12:22:59.873Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: null, - f_a4z4h45vi5b: null, - f_cht6rsiiiko: null, - f_d93g4r08krl: null, - f_f7txg1oc3nt: '2022-03-16T12:22:59.873Z', - f_g8j5jvalqh0: '表单支持回车键提交', - f_hpmvdltzs6m: '67snt3e6yld', - f_tegyd222bcc: null, - f_yc8jbfiqfvh: 'c4hobfb5k07', - f_z27302tl2bf: '9yyv2tawmry', - f_zek99qhv0vc: '2022-03-16T12:22:59.873Z', - }, - { - id: 119, - createdAt: '2022-03-17T02:22:24.743Z', - updatedAt: '2022-03-22T07:22:48.852Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: null, - f_a4z4h45vi5b: null, - f_cht6rsiiiko: 'uduxfqpr5t2', - f_d93g4r08krl: null, - f_f7txg1oc3nt: '2022-03-17T02:22:24.743Z', - f_g8j5jvalqh0: '点击进入区块模板列表页面,菜单高亮没有变化、左侧菜单没有隐藏', - f_hpmvdltzs6m: null, - f_tegyd222bcc: '', - f_yc8jbfiqfvh: 'v25527dxseu', - f_z27302tl2bf: 'mkvgt7ndsqd', - f_zek99qhv0vc: '2022-03-22T07:22:48.852Z', - }, - { - id: 120, - createdAt: '2022-03-17T02:36:41.192Z', - updatedAt: '2022-03-17T02:36:41.192Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: null, - f_a4z4h45vi5b: null, - f_cht6rsiiiko: 'r6l2wg1snxc', - f_d93g4r08krl: null, - f_f7txg1oc3nt: '2022-03-17T02:36:41.192Z', - f_g8j5jvalqh0: '同一页面存在多个区块都引用了同一个模板,修改配置时所有区块实时生效(现在需要刷新页面才行)', - f_hpmvdltzs6m: null, - f_tegyd222bcc: null, - f_yc8jbfiqfvh: 'c4hobfb5k07', - f_z27302tl2bf: 'mkvgt7ndsqd', - f_zek99qhv0vc: '2022-03-17T02:36:41.192Z', - }, - { - id: 121, - createdAt: '2022-03-17T08:42:16.015Z', - updatedAt: '2022-03-17T08:58:24.157Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: null, - f_a4z4h45vi5b: null, - f_cht6rsiiiko: 'r6l2wg1snxc', - f_d93g4r08krl: null, - f_f7txg1oc3nt: '2022-03-17T08:42:16.015Z', - f_g8j5jvalqh0: '当插入大片 schema 时,性能较低', - f_hpmvdltzs6m: '2s2s8wzcnm0', - f_tegyd222bcc: '闭包的 uiSchemas 查询性能好了,但是写入的性能较差。', - f_yc8jbfiqfvh: 's7hocw6twfk', - f_z27302tl2bf: 'mkvgt7ndsqd', - f_zek99qhv0vc: '2022-03-17T08:58:24.157Z', - }, - { - id: 122, - createdAt: '2022-03-17T22:57:28.490Z', - updatedAt: '2022-03-17T22:57:28.490Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: null, - f_a4z4h45vi5b: null, - f_cht6rsiiiko: 'uduxfqpr5t2', - f_d93g4r08krl: null, - f_f7txg1oc3nt: '2022-03-17T22:57:28.490Z', - f_g8j5jvalqh0: '菜单配置这里的背景色不应该有空白', - f_hpmvdltzs6m: '67snt3e6yld', - f_tegyd222bcc: null, - f_yc8jbfiqfvh: 'c4hobfb5k07', - f_z27302tl2bf: 'mkvgt7ndsqd', - f_zek99qhv0vc: '2022-03-17T22:57:28.490Z', - }, - { - id: 123, - createdAt: '2022-03-19T09:27:52.383Z', - updatedAt: '2022-03-19T09:27:52.383Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: null, - f_a4z4h45vi5b: null, - f_cht6rsiiiko: 'wlg329nsu3a', - f_d93g4r08krl: null, - f_f7txg1oc3nt: '2022-03-19T09:27:52.383Z', - f_g8j5jvalqh0: '反向关联字段的一些问题', - f_hpmvdltzs6m: null, - f_tegyd222bcc: - '新建关联字段时,会在关联的目标表里自动生成反向关联字段,是一对关系。但是这个自动生成的反向关联字段(title 也是自动处理的),容易让人困惑,是怎么出来的。需要有相关标记,与哪个关联字段是一对。\n关联字段删除时,反向关联字段怎么处理也需要明确。', - f_yc8jbfiqfvh: 'c4hobfb5k07', - f_z27302tl2bf: 'mkvgt7ndsqd', - f_zek99qhv0vc: '2022-03-19T09:27:52.383Z', - }, - { - id: 124, - createdAt: '2022-03-20T01:01:54.276Z', - updatedAt: '2022-03-22T07:32:44.921Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: null, - f_a4z4h45vi5b: null, - f_cht6rsiiiko: 'uduxfqpr5t2', - f_d93g4r08krl: null, - f_f7txg1oc3nt: '2022-03-20T01:01:54.276Z', - f_g8j5jvalqh0: '菜单太多之后被挡住。应该是出现 more 的下拉菜单', - f_hpmvdltzs6m: 'ht963n365al', - f_tegyd222bcc: null, - f_yc8jbfiqfvh: 'v25527dxseu', - f_z27302tl2bf: 'mkvgt7ndsqd', - f_zek99qhv0vc: '2022-03-22T07:32:44.921Z', - }, - { - id: 125, - createdAt: '2022-03-22T08:13:05.157Z', - updatedAt: '2022-03-22T08:13:05.157Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: null, - f_a4z4h45vi5b: null, - f_cht6rsiiiko: 'wge57qtiz14', - f_d93g4r08krl: null, - f_f7txg1oc3nt: '2022-03-22T08:13:05.157Z', - f_g8j5jvalqh0: '看板拖拽时列滚动条', - f_hpmvdltzs6m: null, - f_tegyd222bcc: null, - f_yc8jbfiqfvh: 'c4hobfb5k07', - f_z27302tl2bf: 'mkvgt7ndsqd', - f_zek99qhv0vc: '2022-03-22T08:13:05.157Z', - }, - { - id: 126, - createdAt: '2022-03-25T00:31:40.798Z', - updatedAt: '2022-03-25T00:31:40.798Z', - createdById: 1, - updatedById: 1, - f_1ckuegfab9s: null, - f_a4z4h45vi5b: null, - f_cht6rsiiiko: null, - f_d93g4r08krl: null, - f_f7txg1oc3nt: '2022-03-25T00:31:40.798Z', - f_g8j5jvalqh0: '看板需要编辑和删除', - f_hpmvdltzs6m: '67snt3e6yld', - f_tegyd222bcc: '编辑名称即可', - f_yc8jbfiqfvh: 'v25527dxseu', - f_z27302tl2bf: 'vx4bhmtsuus', - f_zek99qhv0vc: '2022-03-25T00:31:40.798Z', - }, - ], -}; diff --git a/packages/core/client/src/schema-component/antd/kanban/demos/demo1.tsx b/packages/core/client/src/schema-component/antd/kanban/demos/demo1.tsx deleted file mode 100644 index c248a501c..000000000 --- a/packages/core/client/src/schema-component/antd/kanban/demos/demo1.tsx +++ /dev/null @@ -1,89 +0,0 @@ -import { ISchema } from '@formily/react'; -import { - AntdSchemaComponentProvider, - APIClientProvider, - BlockSchemaComponentProvider, - CollectionManagerProvider, - CurrentUserProvider, - SchemaComponent, - SchemaComponentProvider, -} from '@nocobase/client'; -import React from 'react'; -import { mockAPIClient } from '../../../../testUtils'; -import collections from './collections'; -import data from './data'; - -const { apiClient, mockRequest } = mockAPIClient(); -const sleep = (value: number) => new Promise((resolve) => setTimeout(resolve, value)); - -mockRequest.onGet('/t_j6omof6tza8:list').reply(async (config) => { - await sleep(200); - return [200, data]; -}); -mockRequest.onGet('/auth:check').reply(() => { - return [200, { data: {} }]; -}); - -const schema: ISchema = { - type: 'object', - properties: { - block: { - type: 'void', - 'x-decorator': 'KanbanBlockProvider', - 'x-decorator-props': { - collection: 't_j6omof6tza8', - resource: 't_j6omof6tza8', - action: 'list', - groupField: 'f_hpmvdltzs6m', - params: { - paginate: false, - }, - }, - properties: { - kanban: { - type: 'array', - 'x-component': 'Kanban', - 'x-component-props': { - useProps: '{{ useKanbanBlockProps }}', - }, - properties: { - card: { - type: 'void', - 'x-component': 'Kanban.Card', - properties: { - f_g8j5jvalqh0: { - 'x-decorator': 'FormItem', - 'x-component': 'CollectionField', - 'x-read-pretty': true, - }, - f_tegyd222bcc: { - 'x-decorator': 'FormItem', - 'x-component': 'CollectionField', - 'x-read-pretty': true, - }, - }, - }, - }, - }, - }, - }, - }, -}; - -export default () => { - return ( - - - - - - - - - - - - - - ); -}; diff --git a/packages/core/client/src/schema-component/antd/kanban/index.ts b/packages/core/client/src/schema-component/antd/kanban/index.ts deleted file mode 100644 index 57695fc25..000000000 --- a/packages/core/client/src/schema-component/antd/kanban/index.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { Plugin } from '../../../application/Plugin'; -import { Action } from '../action'; -import { Kanban } from './Kanban'; -import { KanbanCard } from './Kanban.Card'; -import { KanbanCardDesigner, kanbanCardInitializers } from './Kanban.Card.Designer'; -import { KanbanCardDesignerTitleSwitch } from './Kanban.Card.Designer.TitleSwitch'; -import { KanbanCardViewer } from './Kanban.CardViewer'; -import { KanbanDesigner } from './Kanban.Designer'; - -Kanban.Card = KanbanCard; -Kanban.CardAdder = Action; -Kanban.CardViewer = KanbanCardViewer; -Kanban.Card.Designer = KanbanCardDesigner; -Kanban.Card.Designer.TitleSwitch = KanbanCardDesignerTitleSwitch; -Kanban.Designer = KanbanDesigner; - -const KanbanV2 = Kanban; - -class KanbanPlugin extends Plugin { - async load() { - this.app.schemaInitializerManager.add(kanbanCardInitializers); - } -} - -export { Kanban, KanbanV2, KanbanPlugin }; diff --git a/packages/core/client/src/schema-component/antd/kanban/style.ts b/packages/core/client/src/schema-component/antd/kanban/style.ts deleted file mode 100644 index 9c441f73b..000000000 --- a/packages/core/client/src/schema-component/antd/kanban/style.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { genStyleHook } from './../__builtins__'; - -export const useStyles = genStyleHook('nb-kanban', (token) => { - const { componentCls } = token; - - return { - [componentCls]: { - overflow: 'hidden', - height: '100%', - '> .ant-spin-container': { height: '100%' }, - - '.react-kanban-board': { - '.nb-block-item': { - '.ant-formily-item-control .ant-space-item': { - whiteSpace: 'normal', - wordBreak: 'break-all', - wordWrap: 'break-word', - }, - '& .ant-formily-item-label': { color: '#8c8c8c', fontWeight: 'normal' }, - }, - '.kanban-no-label': { '.ant-formily-item-label': { display: 'none' } }, - }, - }, - }; -}); diff --git a/packages/core/client/src/schema-component/hooks/useFieldModeOptions.tsx b/packages/core/client/src/schema-component/hooks/useFieldModeOptions.tsx index 7ce4cdf60..f1410ee2d 100644 --- a/packages/core/client/src/schema-component/hooks/useFieldModeOptions.tsx +++ b/packages/core/client/src/schema-component/hooks/useFieldModeOptions.tsx @@ -9,7 +9,7 @@ export const useFieldModeOptions = (props?) => { const fieldSchema = props?.fieldSchema || currentFieldSchema; const field = useField(); const form = useForm(); - const isReadPretty = field.readPretty && form.readPretty; + const isReadPretty = field.readPretty || form.readPretty; const isSubTableField = props?.fieldSchema; const { getField } = useCollection(); const collectionField = getField(fieldSchema['name']) || getCollectionJoinField(fieldSchema['x-collection-field']); diff --git a/packages/core/client/src/schema-initializer/buttons/BlockInitializers.tsx b/packages/core/client/src/schema-initializer/buttons/BlockInitializers.tsx index 090bc73bd..6a47f9bc9 100644 --- a/packages/core/client/src/schema-initializer/buttons/BlockInitializers.tsx +++ b/packages/core/client/src/schema-initializer/buttons/BlockInitializers.tsx @@ -42,16 +42,6 @@ export const blockInitializers = new SchemaInitializer({ title: '{{t("Calendar")}}', Component: 'CalendarBlockInitializer', }, - { - name: 'kanban', - title: '{{t("Kanban")}}', - Component: 'KanbanBlockInitializer', - }, - { - name: 'gantt', - title: '{{t("Gantt")}}', - Component: 'GanttBlockInitializer', - }, ], }, { diff --git a/packages/core/client/src/schema-initializer/buttons/CalendarFormActionInitializers.tsx b/packages/core/client/src/schema-initializer/buttons/CalendarFormActionInitializers.tsx index 7428967bb..7f80524d3 100644 --- a/packages/core/client/src/schema-initializer/buttons/CalendarFormActionInitializers.tsx +++ b/packages/core/client/src/schema-initializer/buttons/CalendarFormActionInitializers.tsx @@ -57,14 +57,6 @@ export const calendarFormActionInitializers = new SchemaInitializer({ return (collection.template !== 'view' || collection?.writableView) && collection.template !== 'sql'; }, }, - { - name: 'print', - title: '{{t("Print")}}', - Component: 'PrintActionInitializer', - schema: { - 'x-component': 'Action', - }, - }, ], }, { diff --git a/packages/core/client/src/schema-initializer/buttons/DetailsActionInitializers.tsx b/packages/core/client/src/schema-initializer/buttons/DetailsActionInitializers.tsx index 7ddfc2426..c13ff6853 100644 --- a/packages/core/client/src/schema-initializer/buttons/DetailsActionInitializers.tsx +++ b/packages/core/client/src/schema-initializer/buttons/DetailsActionInitializers.tsx @@ -35,19 +35,6 @@ export const detailsActionInitializers = new SchemaInitializer({ 'x-decorator': 'ACLActionProvider', }, }, - { - name: 'duplicate', - title: '{{t("Duplicate")}}', - Component: 'DuplicateActionInitializer', - schema: { - 'x-component': 'Action', - 'x-action': 'duplicate', - 'x-decorator': 'ACLActionProvider', - 'x-component-props': { - type: 'primary', - }, - }, - }, ], }, ], diff --git a/packages/core/client/src/schema-initializer/buttons/ReadPrettyFormActionInitializers.tsx b/packages/core/client/src/schema-initializer/buttons/ReadPrettyFormActionInitializers.tsx index c73bbe4ff..feb09b07e 100644 --- a/packages/core/client/src/schema-initializer/buttons/ReadPrettyFormActionInitializers.tsx +++ b/packages/core/client/src/schema-initializer/buttons/ReadPrettyFormActionInitializers.tsx @@ -42,28 +42,6 @@ export const readPrettyFormActionInitializers = new SchemaInitializer({ }, useVisible: useVisibleCollection, }, - { - title: '{{t("Duplicate")}}', - name: 'duplicate', - Component: 'DuplicateActionInitializer', - schema: { - 'x-component': 'Action', - 'x-action': 'duplicate', - 'x-decorator': 'ACLActionProvider', - 'x-component-props': { - type: 'primary', - }, - }, - useVisible: useVisibleCollection, - }, - { - title: '{{t("Print")}}', - name: 'print', - Component: 'PrintActionInitializer', - schema: { - 'x-component': 'Action', - }, - }, ], }, { diff --git a/packages/core/client/src/schema-initializer/buttons/TableActionColumnInitializers.tsx b/packages/core/client/src/schema-initializer/buttons/TableActionColumnInitializers.tsx index fa932a8bb..c23571282 100644 --- a/packages/core/client/src/schema-initializer/buttons/TableActionColumnInitializers.tsx +++ b/packages/core/client/src/schema-initializer/buttons/TableActionColumnInitializers.tsx @@ -168,21 +168,6 @@ export const tableActionColumnInitializers = new SchemaInitializer({ return collection.tree && treeTable !== false; }, }, - { - type: 'item', - title: '{{t("Duplicate")}}', - name: 'duplicate', - Component: 'DuplicateActionInitializer', - schema: { - 'x-component': 'Action.Link', - 'x-action': 'duplicate', - 'x-decorator': 'ACLActionProvider', - }, - useVisible() { - const collection = useCollection(); - return (collection.template !== 'view' || collection?.writableView) && collection.template !== 'sql'; - }, - }, ], }, { diff --git a/packages/core/client/src/schema-initializer/buttons/TableActionInitializers.tsx b/packages/core/client/src/schema-initializer/buttons/TableActionInitializers.tsx index 5b789bd85..0fc882026 100644 --- a/packages/core/client/src/schema-initializer/buttons/TableActionInitializers.tsx +++ b/packages/core/client/src/schema-initializer/buttons/TableActionInitializers.tsx @@ -94,52 +94,6 @@ export const tableActionInitializers = new SchemaInitializer({ name: 'customize', title: '{{t("Customize")}}', children: [ - { - type: 'item', - title: '{{t("Bulk update")}}', - Component: 'CustomizeActionInitializer', - name: 'bulkUpdate', - schema: { - type: 'void', - title: '{{ t("Bulk update") }}', - 'x-component': 'Action', - 'x-align': 'right', - 'x-acl-action': 'update', - 'x-decorator': 'ACLActionProvider', - 'x-acl-action-props': { - skipScopeCheck: true, - }, - 'x-action': 'customize:bulkUpdate', - 'x-designer': 'Action.Designer', - 'x-action-settings': { - assignedValues: {}, - updateMode: 'selected', - onSuccess: { - manualClose: true, - redirecting: false, - successMessage: '{{t("Updated successfully")}}', - }, - }, - 'x-component-props': { - icon: 'EditOutlined', - useProps: '{{ useCustomizeBulkUpdateActionProps }}', - }, - }, - }, - { - type: 'item', - title: '{{t("Bulk edit")}}', - name: 'bulkEdit', - Component: 'CustomizeBulkEditActionInitializer', - schema: { - 'x-align': 'right', - 'x-decorator': 'ACLActionProvider', - 'x-acl-action': 'update', - 'x-acl-action-props': { - skipScopeCheck: true, - }, - }, - }, { type: 'item', title: '{{t("Add record")}}', diff --git a/packages/core/client/src/schema-initializer/buttons/index.ts b/packages/core/client/src/schema-initializer/buttons/index.ts index a02b9ce46..186a8aa66 100644 --- a/packages/core/client/src/schema-initializer/buttons/index.ts +++ b/packages/core/client/src/schema-initializer/buttons/index.ts @@ -1,18 +1,14 @@ export * from './BlockInitializers'; -export * from './BulkEditFormItemInitializers'; export * from './CalendarActionInitializers'; export * from './CalendarFormActionInitializers'; export * from './CreateFormBlockInitializers'; -export * from './CreateFormBulkEditBlockInitializers'; export * from './CusomeizeCreateFormBlockInitializers'; export * from './CustomFormItemInitializers'; export * from './DetailsActionInitializers'; export * from './FilterFormActionInitializers'; export * from './FormActionInitializers'; export * from './FormItemInitializers'; -export * from './GanttActionInitializers'; export * from './GridCardActionInitializers'; -export * from './KanbanActionInitializers'; export * from './ListActionInitializers'; export * from './ReadPrettyFormActionInitializers'; export * from './ReadPrettyFormItemInitializers'; diff --git a/packages/core/client/src/schema-initializer/components/index.ts b/packages/core/client/src/schema-initializer/components/index.ts index 8b40e5b5d..4073716e5 100644 --- a/packages/core/client/src/schema-initializer/components/index.ts +++ b/packages/core/client/src/schema-initializer/components/index.ts @@ -1,4 +1,3 @@ export * from './assigned-field'; export * from './BulkEditField'; export * from './CreateRecordAction'; -export * from './DuplicateAction' diff --git a/packages/core/client/src/schema-initializer/index.ts b/packages/core/client/src/schema-initializer/index.ts index f85e0538a..6df3802a6 100644 --- a/packages/core/client/src/schema-initializer/index.ts +++ b/packages/core/client/src/schema-initializer/index.ts @@ -16,26 +16,25 @@ export { useInheritsTableColumnInitializerFields, useRecordCollectionDataSourceItems, useTableColumnInitializerFields, + useRemoveGridFormItem, + useAssociatedFormItemInitializerFields, + useFormItemInitializerFields, } from './utils'; import { Plugin } from '../application/Plugin'; import { - bulkEditFormItemInitializers, calendarActionInitializers, calendarFormActionInitializers, createFormBlockInitializers, - createFormBulkEditBlockInitializers, cusomeizeCreateFormBlockInitializers, customFormItemInitializers, filterFormActionInitializers, createFormActionInitializers, updateFormActionInitializers, bulkEditFormActionInitializers, - ganttActionInitializers, filterFormItemInitializers, gridCardActionInitializers, gridCardItemActionInitializers, - kanbanActionInitializers, listActionInitializers, listItemActionInitializers, recordBlockInitializers, @@ -72,22 +71,19 @@ export class SchemaInitializerPlugin extends Plugin { this.app.schemaInitializerManager.add(detailsActionInitializers); this.app.schemaInitializerManager.add(readPrettyFormItemInitializers); this.app.schemaInitializerManager.add(readPrettyFormActionInitializers); - this.app.schemaInitializerManager.add(bulkEditFormItemInitializers); this.app.schemaInitializerManager.add(calendarActionInitializers); this.app.schemaInitializerManager.add(calendarFormActionInitializers); this.app.schemaInitializerManager.add(createFormBlockInitializers); - this.app.schemaInitializerManager.add(createFormBulkEditBlockInitializers); this.app.schemaInitializerManager.add(cusomeizeCreateFormBlockInitializers); this.app.schemaInitializerManager.add(customFormItemInitializers); this.app.schemaInitializerManager.add(filterFormActionInitializers); this.app.schemaInitializerManager.add(createFormActionInitializers); this.app.schemaInitializerManager.add(updateFormActionInitializers); this.app.schemaInitializerManager.add(bulkEditFormActionInitializers); - this.app.schemaInitializerManager.add(ganttActionInitializers); this.app.schemaInitializerManager.add(filterFormItemInitializers); this.app.schemaInitializerManager.add(gridCardActionInitializers); this.app.schemaInitializerManager.add(gridCardItemActionInitializers); - this.app.schemaInitializerManager.add(kanbanActionInitializers); + this.app.schemaInitializerManager.add(listActionInitializers); this.app.schemaInitializerManager.add(listItemActionInitializers); this.app.schemaInitializerManager.add(recordBlockInitializers); diff --git a/packages/core/client/src/schema-initializer/items/index.tsx b/packages/core/client/src/schema-initializer/items/index.tsx index 98c28c90f..4a44a2bc1 100644 --- a/packages/core/client/src/schema-initializer/items/index.tsx +++ b/packages/core/client/src/schema-initializer/items/index.tsx @@ -13,30 +13,24 @@ export * from './CreateActionInitializer'; export * from './CreateChildInitializer'; export * from './CreateFilterActionInitializer'; export * from './CreateFormBlockInitializer'; -export * from './CreateFormBulkEditBlockInitializer'; export * from './CreateResetActionInitializer'; export * from './CreateSubmitActionInitializer'; export * from './CustomizeActionInitializer'; export * from './CustomizeAddRecordActionInitializer'; -export * from './CustomizeBulkEditActionInitializer'; export * from './DataBlockInitializer'; export * from './DeleteEventActionInitializer'; export * from './DestroyActionInitializer'; export * from './DetailsBlockInitializer'; -export * from './DuplicateActionInitializer'; export * from './ExpandActionInitializer'; export * from './FilterActionInitializer'; export * from './FilterCollapseBlockInitializer'; export * from './FilterFormBlockInitializer'; export * from './FormBlockInitializer'; export * from './G2PlotInitializer'; -export * from './GanttBlockInitializer'; export * from './GridCardBlockInitializer'; export * from './InitializerWithSwitch'; -export * from './KanbanBlockInitializer'; export * from './ListBlockInitializer'; export * from './MarkdownBlockInitializer'; -export * from './PrintActionInitializer'; export * from './RecordAssociationBlockInitializer'; export * from './RecordAssociationGridCardBlockInitializer'; export * from './RecordAssociationListBlockInitializer'; diff --git a/packages/core/client/src/schema-initializer/utils.ts b/packages/core/client/src/schema-initializer/utils.ts index bde76555d..8cc013035 100644 --- a/packages/core/client/src/schema-initializer/utils.ts +++ b/packages/core/client/src/schema-initializer/utils.ts @@ -1592,242 +1592,6 @@ export const createCalendarBlockSchema = (options) => { return schema; }; -export const createGanttBlockSchema = (options) => { - const { collection, resource, fieldNames, ...others } = options; - const schema: ISchema = { - type: 'void', - 'x-acl-action': `${resource || collection}:list`, - 'x-decorator': 'GanttBlockProvider', - 'x-decorator-props': { - collection: collection, - resource: resource || collection, - action: 'list', - fieldNames: { - id: 'id', - ...fieldNames, - }, - params: { - paginate: false, - }, - ...others, - }, - 'x-designer': 'Gantt.Designer', - 'x-component': 'CardItem', - properties: { - [uid()]: { - type: 'void', - 'x-component': 'Gantt', - 'x-component-props': { - useProps: '{{ useGanttBlockProps }}', - }, - properties: { - toolBar: { - type: 'void', - 'x-component': 'ActionBar', - 'x-component-props': { - style: { - marginBottom: 24, - }, - }, - 'x-initializer': 'GanttActionInitializers', - properties: {}, - }, - table: { - type: 'array', - 'x-decorator': 'div', - 'x-decorator-props': { - style: { - float: 'left', - maxWidth: '35%', - }, - }, - - 'x-initializer': 'TableColumnInitializers', - 'x-component': 'TableV2', - 'x-component-props': { - rowKey: 'id', - rowSelection: { - type: 'checkbox', - }, - useProps: '{{ useTableBlockProps }}', - pagination: false, - }, - properties: { - actions: { - type: 'void', - title: '{{ t("Actions") }}', - 'x-action-column': 'actions', - 'x-decorator': 'TableV2.Column.ActionBar', - 'x-component': 'TableV2.Column', - 'x-designer': 'TableV2.ActionColumnDesigner', - 'x-initializer': 'TableActionColumnInitializers', - properties: { - actions: { - type: 'void', - 'x-decorator': 'DndContext', - 'x-component': 'Space', - 'x-component-props': { - split: '|', - }, - properties: {}, - }, - }, - }, - }, - }, - detail: { - type: 'void', - 'x-component': 'Gantt.Event', - properties: { - drawer: { - type: 'void', - 'x-component': 'Action.Drawer', - 'x-component-props': { - className: 'nb-action-popup', - }, - title: '{{ t("View record") }}', - 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: {}, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }; - - return schema; -}; -export const createKanbanBlockSchema = (options) => { - const { collection, resource, groupField, ...others } = options; - const schema: ISchema = { - type: 'void', - 'x-acl-action': `${resource || collection}:list`, - 'x-decorator': 'KanbanBlockProvider', - 'x-decorator-props': { - collection: collection, - resource: resource || collection, - action: 'list', - groupField, - params: { - paginate: false, - }, - ...others, - }, - 'x-designer': 'Kanban.Designer', - 'x-component': 'CardItem', - properties: { - actions: { - type: 'void', - 'x-initializer': 'KanbanActionInitializers', - 'x-component': 'ActionBar', - 'x-component-props': { - style: { - marginBottom: 'var(--nb-spacing)', - }, - }, - properties: {}, - }, - [uid()]: { - type: 'array', - 'x-component': 'Kanban', - 'x-component-props': { - useProps: '{{ useKanbanBlockProps }}', - }, - properties: { - card: { - type: 'void', - 'x-read-pretty': true, - 'x-label-disabled': true, - 'x-decorator': 'BlockItem', - 'x-component': 'Kanban.Card', - 'x-component-props': { - openMode: 'drawer', - }, - 'x-designer': 'Kanban.Card.Designer', - properties: { - grid: { - type: 'void', - 'x-component': 'Grid', - 'x-component-props': { dndContext: false }, - }, - }, - }, - cardViewer: { - type: 'void', - title: '{{ t("View") }}', - 'x-designer': 'Action.Designer', - 'x-component': 'Kanban.CardViewer', - 'x-action': 'view', - 'x-component-props': { - openMode: 'drawer', - }, - properties: { - drawer: { - type: 'void', - title: '{{ t("View record") }}', - 'x-component': 'Action.Container', - '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: {}, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }; - return schema; -}; - const getChildren = ({ collections, getCollectionFields, diff --git a/packages/core/client/src/schema-settings/DataTemplates/utils.tsx b/packages/core/client/src/schema-settings/DataTemplates/utils.tsx index 0ade5cac2..96620b959 100644 --- a/packages/core/client/src/schema-settings/DataTemplates/utils.tsx +++ b/packages/core/client/src/schema-settings/DataTemplates/utils.tsx @@ -166,7 +166,7 @@ export const useSyncFromForm = (fieldSchema, collection?, callBack?) => { const formData = new Set([]); const selectFields = new Set([]); const getAssociationAppends = (schema, str) => { - schema.reduceProperties((pre, s) => { + schema?.reduceProperties?.((pre, s) => { const prefix = pre || str; const collectionfield = s['x-collection-field'] && getCollectionJoinField(s['x-collection-field']); const isAssociationSubfield = s.name.includes('.'); diff --git a/packages/core/client/src/schema-settings/index.ts b/packages/core/client/src/schema-settings/index.ts index c40a3206c..bb12a9fa2 100644 --- a/packages/core/client/src/schema-settings/index.ts +++ b/packages/core/client/src/schema-settings/index.ts @@ -1,6 +1,9 @@ export * from './GeneralSchemaDesigner'; export * from './SchemaSettings'; export * from './hooks/useIsAllowToSetDefaultValue'; +export * from './hooks/useGetAriaLabelOfDesigner'; +export * from './DataTemplates/hooks/useCollectionState'; +export * from './DataTemplates/utils'; export { default as useParseDataScopeFilter } from './hooks/useParseDataScopeFilter'; diff --git a/packages/core/client/src/variables/index.ts b/packages/core/client/src/variables/index.ts index bf46a3896..fd180254f 100644 --- a/packages/core/client/src/variables/index.ts +++ b/packages/core/client/src/variables/index.ts @@ -3,5 +3,7 @@ export { default as useBuiltinVariables } from './hooks/useBuiltinVariables'; export { default as useContextVariable } from './hooks/useContextVariable'; export { default as useLocalVariables } from './hooks/useLocalVariables'; export { default as useVariables } from './hooks/useVariables'; +export * from './utils/isVariable'; +export * from './utils/transformVariableValue'; export * from './constants'; diff --git a/packages/plugins/@nocobase/plugin-action-bulk-edit/.npmignore b/packages/plugins/@nocobase/plugin-action-bulk-edit/.npmignore new file mode 100644 index 000000000..65f5e8779 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-action-bulk-edit/.npmignore @@ -0,0 +1,2 @@ +/node_modules +/src diff --git a/packages/plugins/@nocobase/plugin-action-bulk-edit/README.md b/packages/plugins/@nocobase/plugin-action-bulk-edit/README.md new file mode 100644 index 000000000..51e4a94b7 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-action-bulk-edit/README.md @@ -0,0 +1 @@ +# @nocobase/plugin-action-bulk-edit diff --git a/packages/plugins/@nocobase/plugin-action-bulk-edit/client.d.ts b/packages/plugins/@nocobase/plugin-action-bulk-edit/client.d.ts new file mode 100644 index 000000000..6c459cbac --- /dev/null +++ b/packages/plugins/@nocobase/plugin-action-bulk-edit/client.d.ts @@ -0,0 +1,2 @@ +export * from './dist/client'; +export { default } from './dist/client'; diff --git a/packages/plugins/@nocobase/plugin-action-bulk-edit/client.js b/packages/plugins/@nocobase/plugin-action-bulk-edit/client.js new file mode 100644 index 000000000..b6e3be70e --- /dev/null +++ b/packages/plugins/@nocobase/plugin-action-bulk-edit/client.js @@ -0,0 +1 @@ +module.exports = require('./dist/client/index.js'); diff --git a/packages/plugins/@nocobase/plugin-action-bulk-edit/package.json b/packages/plugins/@nocobase/plugin-action-bulk-edit/package.json new file mode 100644 index 000000000..ecab3a6cc --- /dev/null +++ b/packages/plugins/@nocobase/plugin-action-bulk-edit/package.json @@ -0,0 +1,16 @@ +{ + "name": "@nocobase/plugin-action-bulk-edit", + "version": "0.17.0-alpha.7", + "main": "dist/server/index.js", + "displayName": "Bulk edit action", + "displayName.zh-CN": "批量编辑操作", + "description": "Provides bulk edit action", + "description.zh-CN": "提供批量编辑操作", + "license": "AGPL-3.0", + "dependencies": {}, + "peerDependencies": { + "@nocobase/client": "0.x", + "@nocobase/server": "0.x", + "@nocobase/test": "0.x" + } +} diff --git a/packages/plugins/@nocobase/plugin-action-bulk-edit/server.d.ts b/packages/plugins/@nocobase/plugin-action-bulk-edit/server.d.ts new file mode 100644 index 000000000..c41081ddc --- /dev/null +++ b/packages/plugins/@nocobase/plugin-action-bulk-edit/server.d.ts @@ -0,0 +1,2 @@ +export * from './dist/server'; +export { default } from './dist/server'; diff --git a/packages/plugins/@nocobase/plugin-action-bulk-edit/server.js b/packages/plugins/@nocobase/plugin-action-bulk-edit/server.js new file mode 100644 index 000000000..972842039 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-action-bulk-edit/server.js @@ -0,0 +1 @@ +module.exports = require('./dist/server/index.js'); diff --git a/packages/plugins/@nocobase/plugin-action-bulk-edit/src/client/BulkEditAction.Settings.tsx b/packages/plugins/@nocobase/plugin-action-bulk-edit/src/client/BulkEditAction.Settings.tsx new file mode 100644 index 000000000..f5be103e2 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-action-bulk-edit/src/client/BulkEditAction.Settings.tsx @@ -0,0 +1,134 @@ +import React from 'react'; +import { + SchemaSettings, + useCompile, + ActionDesigner, + useSchemaToolbar, + useDesignable, + SchemaInitializerOpenModeSchemaItems, + SchemaSettingsSelectItem, + SchemaSettingsDivider, + SchemaSettingsRemove, + SchemaSettingsItemGroup, +} from '@nocobase/client'; +import { ModalProps } from 'antd'; +import { useTranslation } from 'react-i18next'; +import { useFieldSchema } from '@formily/react'; + +const MenuGroup = (props) => { + const fieldSchema = useFieldSchema(); + const { t } = useTranslation(); + const compile = useCompile(); + const actionTitle = fieldSchema.title ? compile(fieldSchema.title) : ''; + return ( + ${actionTitle}`}>{props.children} + ); +}; + +function UpdateMode() { + const { dn } = useDesignable(); + const { t } = useTranslation(); + const fieldSchema = useFieldSchema(); + + return ( + { + fieldSchema['x-action-settings']['updateMode'] = value; + dn.emit('patch', { + schema: { + 'x-uid': fieldSchema['x-uid'], + 'x-action-settings': fieldSchema['x-action-settings'], + }, + }); + dn.refresh(); + }} + /> + ); +} + +function RemoveButton( + props: { + onConfirmOk?: ModalProps['onOk']; + } = {}, +) { + const { t } = useTranslation(); + const fieldSchema = useFieldSchema(); + const isDeletable = fieldSchema?.parent['x-component'] === 'CollectionField'; + return ( + !isDeletable && ( + <> + + { + return s['x-component'] === 'Space' || s['x-component'].endsWith('ActionBar'); + }} + confirm={{ + title: t('Delete action'), + onOk: props.onConfirmOk, + }} + /> + + ) + ); +} +const bulkEditactionSettings = new SchemaSettings({ + name: 'ActionSettings:customize:bulkEdit', + items: [ + { + name: 'Customize', + Component: MenuGroup, + children: [ + { + name: 'editButton', + Component: ActionDesigner.ButtonEditor, + useComponentProps() { + const { buttonEditorProps } = useSchemaToolbar(); + return buttonEditorProps; + }, + }, + { + name: 'openMode', + Component: SchemaInitializerOpenModeSchemaItems, + useComponentProps() { + const fieldSchema = useFieldSchema(); + const isPopupAction = [ + 'create', + 'update', + 'view', + 'customize:popup', + 'duplicate', + 'customize:create', + ].includes(fieldSchema['x-action'] || ''); + + return { + openMode: isPopupAction, + openSize: isPopupAction, + }; + }, + }, + { + name: 'updateMode', + Component: UpdateMode, + }, + { + name: 'remove', + sort: 100, + Component: RemoveButton as any, + useComponentProps() { + const { removeButtonProps } = useSchemaToolbar(); + return removeButtonProps; + }, + }, + ], + }, + ], +}); + +export { bulkEditactionSettings }; diff --git a/packages/core/client/src/schema-initializer/buttons/BulkEditFormItemInitializers.tsx b/packages/plugins/@nocobase/plugin-action-bulk-edit/src/client/BulkEditFormItemInitializers.tsx similarity index 77% rename from packages/core/client/src/schema-initializer/buttons/BulkEditFormItemInitializers.tsx rename to packages/plugins/@nocobase/plugin-action-bulk-edit/src/client/BulkEditFormItemInitializers.tsx index 6a9f3a73f..4472dba4c 100644 --- a/packages/core/client/src/schema-initializer/buttons/BulkEditFormItemInitializers.tsx +++ b/packages/plugins/@nocobase/plugin-action-bulk-edit/src/client/BulkEditFormItemInitializers.tsx @@ -1,7 +1,7 @@ -import { SchemaInitializer } from '../../application/schema-initializer/SchemaInitializer'; -import { gridRowColWrap, useCustomBulkEditFormItemInitializerFields } from '../utils'; +import { SchemaInitializer, gridRowColWrap } from '@nocobase/client'; +import { useCustomBulkEditFormItemInitializerFields } from './utils'; -export const bulkEditFormItemInitializers = new SchemaInitializer({ +export const BulkEditFormItemInitializers = new SchemaInitializer({ name: 'BulkEditFormItemInitializers', wrap: gridRowColWrap, icon: 'SettingOutlined', diff --git a/packages/plugins/@nocobase/plugin-action-bulk-edit/src/client/BulkEditPluginProvider.tsx b/packages/plugins/@nocobase/plugin-action-bulk-edit/src/client/BulkEditPluginProvider.tsx new file mode 100644 index 000000000..07ecf0a36 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-action-bulk-edit/src/client/BulkEditPluginProvider.tsx @@ -0,0 +1,17 @@ +import { SchemaComponentOptions } from '@nocobase/client'; +import React from 'react'; +import { CustomizeBulkEditActionInitializer } from './CustomizeBulkEditActionInitializer'; +import { CreateFormBulkEditBlockInitializer } from './CreateFormBulkEditBlockInitializer'; + +export const BulkEditPluginProvider = (props: any) => { + return ( + + {props.children} + + ); +}; diff --git a/packages/core/client/src/schema-initializer/items/CreateFormBulkEditBlockInitializer.tsx b/packages/plugins/@nocobase/plugin-action-bulk-edit/src/client/CreateFormBulkEditBlockInitializer.tsx similarity index 80% rename from packages/core/client/src/schema-initializer/items/CreateFormBulkEditBlockInitializer.tsx rename to packages/plugins/@nocobase/plugin-action-bulk-edit/src/client/CreateFormBulkEditBlockInitializer.tsx index 73d86a2d2..70dc2dea3 100644 --- a/packages/core/client/src/schema-initializer/items/CreateFormBulkEditBlockInitializer.tsx +++ b/packages/plugins/@nocobase/plugin-action-bulk-edit/src/client/CreateFormBulkEditBlockInitializer.tsx @@ -1,10 +1,15 @@ import { FormOutlined } from '@ant-design/icons'; import React from 'react'; -import { useBlockAssociationContext } from '../../block-provider'; -import { useCollection } from '../../collection-manager'; -import { useSchemaTemplateManager } from '../../schema-templates'; -import { createFormBlockSchema, useRecordCollectionDataSourceItems } from '../utils'; -import { SchemaInitializerItem, useSchemaInitializer, useSchemaInitializerItem } from '../../application'; +import { + useBlockAssociationContext, + useCollection, + useSchemaTemplateManager, + createFormBlockSchema, + useRecordCollectionDataSourceItems, + SchemaInitializerItem, + useSchemaInitializer, + useSchemaInitializerItem, +} from '@nocobase/client'; export const CreateFormBulkEditBlockInitializer = () => { const itemConfig = useSchemaInitializerItem(); diff --git a/packages/core/client/src/schema-initializer/buttons/CreateFormBulkEditBlockInitializers.tsx b/packages/plugins/@nocobase/plugin-action-bulk-edit/src/client/CreateFormBulkEditBlockInitializers.tsx similarity index 77% rename from packages/core/client/src/schema-initializer/buttons/CreateFormBulkEditBlockInitializers.tsx rename to packages/plugins/@nocobase/plugin-action-bulk-edit/src/client/CreateFormBulkEditBlockInitializers.tsx index 3efccec67..32495a6ee 100644 --- a/packages/core/client/src/schema-initializer/buttons/CreateFormBulkEditBlockInitializers.tsx +++ b/packages/plugins/@nocobase/plugin-action-bulk-edit/src/client/CreateFormBulkEditBlockInitializers.tsx @@ -1,7 +1,6 @@ -import { SchemaInitializer } from '../../application/schema-initializer/SchemaInitializer'; -import { gridRowColWrap } from '../utils'; +import { SchemaInitializer, gridRowColWrap } from '@nocobase/client'; -export const createFormBulkEditBlockInitializers = new SchemaInitializer({ +export const CreateFormBulkEditBlockInitializers = new SchemaInitializer({ name: 'CreateFormBulkEditBlockInitializers', wrap: gridRowColWrap, title: '{{t("Add block")}}', diff --git a/packages/core/client/src/schema-initializer/items/CustomizeBulkEditActionInitializer.tsx b/packages/plugins/@nocobase/plugin-action-bulk-edit/src/client/CustomizeBulkEditActionInitializer.tsx similarity index 93% rename from packages/core/client/src/schema-initializer/items/CustomizeBulkEditActionInitializer.tsx rename to packages/plugins/@nocobase/plugin-action-bulk-edit/src/client/CustomizeBulkEditActionInitializer.tsx index 18eee9ea8..47e206b01 100644 --- a/packages/core/client/src/schema-initializer/items/CustomizeBulkEditActionInitializer.tsx +++ b/packages/plugins/@nocobase/plugin-action-bulk-edit/src/client/CustomizeBulkEditActionInitializer.tsx @@ -1,6 +1,5 @@ import React from 'react'; -import { BlockInitializer } from './BlockInitializer'; -import { useSchemaInitializerItem } from '../../application'; +import { BlockInitializer, useSchemaInitializerItem } from '@nocobase/client'; export const CustomizeBulkEditActionInitializer = () => { const schema = { diff --git a/packages/plugins/@nocobase/plugin-action-bulk-edit/src/client/__e2e__/schemaInitailizer.test.ts b/packages/plugins/@nocobase/plugin-action-bulk-edit/src/client/__e2e__/schemaInitailizer.test.ts new file mode 100644 index 000000000..6d845bec2 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-action-bulk-edit/src/client/__e2e__/schemaInitailizer.test.ts @@ -0,0 +1,22 @@ +import { expect, test, oneEmptyTableBlockWithActions } from '@nocobase/test/client'; +import { oneEmptyGantt } from './utils'; + +test.describe('TableActionInitializers & GanttActionInitializers & MapActionInitializers should add bulk edit action', () => { + test('bulk edit in TableActionInitializers', async ({ page, mockPage }) => { + await mockPage(oneEmptyTableBlockWithActions).goto(); + await page.getByLabel('schema-initializer-ActionBar-TableActionInitializers-general').hover(); + await page.getByRole('menuitem', { name: 'Customize right' }).click(); + await page.getByRole('menuitem', { name: 'Bulk edit' }).click(); + await page.mouse.move(300, 0); + await expect(await page.getByLabel('Bulk edit')).toBeVisible(); + }); + test('bulk edit in GanttActionInitializers', async ({ page, mockPage, mockRecords }) => { + const nocoPage = await mockPage(oneEmptyGantt).waitForInit(); + await mockRecords('general', 3); + await nocoPage.goto(); + await page.getByLabel('schema-initializer-ActionBar-GanttActionInitializers-general').hover(); + await page.getByRole('menuitem', { name: 'Customize right' }).click(); + await page.getByRole('menuitem', { name: 'Bulk edit' }).click(); + await expect(await page.getByLabel('Bulk edit')).toBeVisible(); + }); +}); diff --git a/packages/plugins/@nocobase/plugin-action-bulk-edit/src/client/__e2e__/schemaSettings.test.ts b/packages/plugins/@nocobase/plugin-action-bulk-edit/src/client/__e2e__/schemaSettings.test.ts new file mode 100644 index 000000000..3fd135ddc --- /dev/null +++ b/packages/plugins/@nocobase/plugin-action-bulk-edit/src/client/__e2e__/schemaSettings.test.ts @@ -0,0 +1,45 @@ +import { expect, test } from '@nocobase/test/client'; +import { oneEmptyTableBlockWithCustomizeActions } from './utils'; + +test.describe('bulk edit action setting', () => { + test('data will be updated && edit form configure', async ({ page, mockPage, mockRecords }) => { + const nocoPage = await mockPage(oneEmptyTableBlockWithCustomizeActions).waitForInit(); + await mockRecords('general', 3); + await nocoPage.goto(); + await page.getByLabel('Bulk edit').hover(); + await page.getByLabel('designer-schema-settings-Action-Action.Designer-general').hover(); + //默认是选中的数据 + await expect(await page.getByTitle('Data will be updated').getByText('Selected')).toBeVisible(); + await page.getByRole('menuitem', { name: 'Data will be updated' }).click(); + //切换为全部数据 + await page.getByRole('option', { name: 'All' }).click(); + //配置更新规则 + await page.getByLabel('Bulk edit').click(); + await page.getByLabel('schema-initializer-Grid-CreateFormBulkEditBlockInitializers-general').hover(); + await page.getByRole('menuitem', { name: 'form Form' }).click(); + await page.mouse.move(300, 0); + await page.getByLabel('schema-initializer-Grid-BulkEditFormItemInitializers-general').hover(); + await page.getByRole('menuitem', { name: 'Single line text' }).click(); + await page.getByRole('menuitem', { name: 'Start date time' }).click(); + await page.getByRole('menuitem', { name: 'Percent' }).click(); + await page.getByLabel('block-item-BulkEditField-general-form-general.singleLineText').click(); + await page.getByText('Clear').click(); + await page.getByLabel('block-item-BulkEditField-general-form-general.startDatetime').click(); + await page.getByRole('option', { name: 'Remains the same' }).locator('div').click(); + await page.getByLabel('block-item-BulkEditField-general-form-general.percent').click(); + await page.getByRole('option', { name: 'Changed to' }).locator('div').click(); + await page.getByRole('spinbutton').click(); + await page.getByRole('spinbutton').fill('0'); + await page.getByLabel('schema-initializer-ActionBar-BulkEditFormActionInitializers-general').click(); + await page.getByRole('menuitem', { name: 'Submit' }).click(); + //更新提交的数据符合预期 + const [request] = await Promise.all([ + page.waitForRequest((request) => request.url().includes('api/general:update')), + page.getByLabel('action-Action-Submit-submit-general-form').click(), + ]); + const postData = request.postDataJSON(); + //更新的数据符合预期 + expect(postData.singleLineText).toEqual(null); + expect(postData.percent).toEqual(0); + }); +}); diff --git a/packages/plugins/@nocobase/plugin-action-bulk-edit/src/client/__e2e__/utils.ts b/packages/plugins/@nocobase/plugin-action-bulk-edit/src/client/__e2e__/utils.ts new file mode 100644 index 000000000..03eba2efe --- /dev/null +++ b/packages/plugins/@nocobase/plugin-action-bulk-edit/src/client/__e2e__/utils.ts @@ -0,0 +1,534 @@ +import { CollectionSetting, PageConfig } from '@nocobase/test/client'; + +/** + * 1. 创建一个名为 general 的 collection,其包含 时间、Percent 类型的字段 + */ +export const generalWithDatetimeFields: CollectionSetting[] = [ + { + name: 'general', + title: 'General', + fields: [ + { + name: 'singleLineText', + interface: 'input', + uiSchema: { + type: 'string', + 'x-component': 'Input', + title: 'Single line text', + }, + }, + { + name: 'startDatetime', + interface: 'datetime', + uiSchema: { + type: 'string', + 'x-component': 'DatePicker', + title: 'Start date time', + required: true, + }, + }, + { + name: 'endDatetime', + interface: 'datetime', + uiSchema: { + type: 'string', + 'x-component': 'DatePicker', + title: 'End date time', + required: true, + }, + }, + { + name: 'f_t22o7loai3j', + interface: 'integer', + isForeignKey: true, + uiSchema: { + type: 'number', + title: 'f_t22o7loai3j', + 'x-component': 'InputNumber', + 'x-read-pretty': true, + }, + }, + { + name: 'manyToOne', + interface: 'm2o', + foreignKey: 'f_t22o7loai3j', + uiSchema: { + 'x-component': 'AssociationField', + 'x-component-props': { + multiple: false, + fieldNames: { + label: 'id', + value: 'id', + }, + }, + title: 'Many to one', + }, + target: 'users', + targetKey: 'id', + }, + { + name: 'percent', + type: 'float', + interface: 'percent', + uiSchema: { + 'x-component-props': { + step: '0.01', + stringMode: true, + addonAfter: '%', + }, + 'x-component': 'Percent', + title: 'Percent', + }, + }, + ], + }, +]; + +export const oneEmptyGantt: PageConfig = { + collections: generalWithDatetimeFields, + pageSchema: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Page', + properties: { + jebhzap4dzi: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid', + 'x-initializer': 'BlockInitializers', + properties: { + gl57m4hyewf: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Row', + properties: { + jm7n5dybw6t: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Col', + properties: { + nsq0rdemz4i: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-acl-action': 'general:list', + 'x-decorator': 'GanttBlockProvider', + 'x-decorator-props': { + collection: 'general', + resource: 'general', + action: 'list', + fieldNames: { + id: 'id', + start: 'startDatetime', + range: 'day', + title: 'singleLineText', + end: 'endDatetime', + }, + params: { + paginate: false, + }, + }, + 'x-designer': 'Gantt.Designer', + 'x-component': 'CardItem', + properties: { + zf07g7relim: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Gantt', + 'x-component-props': { + useProps: '{{ useGanttBlockProps }}', + }, + properties: { + toolBar: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'ActionBar', + 'x-component-props': { + style: { + marginBottom: 24, + }, + }, + 'x-initializer': 'GanttActionInitializers', + 'x-uid': 'guwovmwt4c0', + 'x-async': false, + 'x-index': 1, + }, + table: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'array', + 'x-decorator': 'div', + 'x-decorator-props': { + style: { + float: 'left', + maxWidth: '35%', + }, + }, + 'x-initializer': 'TableColumnInitializers', + 'x-component': 'TableV2', + 'x-component-props': { + rowKey: 'id', + rowSelection: { + type: 'checkbox', + }, + useProps: '{{ useTableBlockProps }}', + pagination: false, + }, + properties: { + actions: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + title: '{{ t("Actions") }}', + 'x-action-column': 'actions', + 'x-decorator': 'TableV2.Column.ActionBar', + 'x-component': 'TableV2.Column', + 'x-designer': 'TableV2.ActionColumnDesigner', + 'x-initializer': 'TableActionColumnInitializers', + properties: { + actions: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-decorator': 'DndContext', + 'x-component': 'Space', + 'x-component-props': { + split: '|', + }, + 'x-uid': '9in7s3pymsd', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'rgpbsjwvq2h', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'g7qwompxaeo', + 'x-async': false, + 'x-index': 2, + }, + detail: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Gantt.Event', + properties: { + drawer: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Action.Drawer', + 'x-component-props': { + className: 'nb-action-popup', + }, + title: '{{ t("View record") }}', + properties: { + tabs: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Tabs', + 'x-component-props': {}, + 'x-initializer': 'TabPaneInitializers', + properties: { + tab1: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + title: '{{t("Details")}}', + 'x-component': 'Tabs.TabPane', + 'x-designer': 'Tabs.Designer', + 'x-component-props': {}, + properties: { + grid: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid', + 'x-initializer': 'RecordBlockInitializers', + 'x-uid': 'gxtfjqzxbfu', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'zxwwx4358p1', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '1p0tnmzpsim', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'hcue8v3fwti', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'fg7cjxu4hxp', + 'x-async': false, + 'x-index': 3, + }, + }, + 'x-uid': 'rf47sf7k16z', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'tritqukkd86', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '3am2ctuhyka', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'p8pesbc7fiv', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'ddm9fhkvrbw', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'vpk8sp3eap1', + 'x-async': true, + 'x-index': 1, + }, +}; + +/** + * 一个空的 Table 区块,并且有这些按钮:Bulk edit + */ +export const oneEmptyTableBlockWithCustomizeActions: PageConfig = { + collections: generalWithDatetimeFields, + pageSchema: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Page', + 'x-index': 1, + properties: { + '1lqiou007g2': { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid', + 'x-initializer': 'BlockInitializers', + 'x-index': 1, + properties: { + '1m4gz110aaw': { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Row', + properties: { + '695oy51236d': { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Col', + properties: { + twtgsvrdmn1: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-decorator': 'TableBlockProvider', + 'x-acl-action': 'general:list', + 'x-decorator-props': { + collection: 'general', + resource: 'general', + action: 'list', + params: { + pageSize: 20, + }, + rowKey: 'id', + showIndex: true, + dragSort: false, + disableTemplate: false, + }, + 'x-designer': 'TableBlockDesigner', + 'x-component': 'CardItem', + 'x-filter-targets': [], + properties: { + actions: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-initializer': 'TableActionInitializers', + 'x-component': 'ActionBar', + 'x-component-props': { + style: { + marginBottom: 'var(--nb-spacing)', + }, + }, + properties: { + yt5ua92rqvb: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + title: '{{t("Bulk edit")}}', + 'x-designer': 'Action.Designer', + 'x-component': 'Action', + 'x-action': 'customize:bulkEdit', + 'x-action-settings': { + updateMode: 'selected', + }, + 'x-component-props': { + openMode: 'drawer', + icon: 'EditOutlined', + }, + 'x-align': 'right', + 'x-decorator': 'ACLActionProvider', + 'x-acl-action': 'update', + 'x-acl-action-props': { + skipScopeCheck: true, + }, + properties: { + drawer: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + title: '{{t("Bulk edit")}}', + 'x-component': 'Action.Container', + 'x-component-props': { + className: 'nb-action-popup', + }, + properties: { + tabs: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Tabs', + 'x-component-props': {}, + 'x-initializer': 'TabPaneInitializersForBulkEditFormBlock', + properties: { + tab1: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + title: '{{t("Bulk edit")}}', + 'x-component': 'Tabs.TabPane', + 'x-designer': 'Tabs.Designer', + 'x-component-props': {}, + properties: { + grid: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid', + 'x-initializer': 'CreateFormBulkEditBlockInitializers', + 'x-uid': '3gjmo60w4de', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '2q284n9l6w2', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 's07s8zva7id', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '0n86l5rtgpb', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'k8sk9wa8sv6', + 'x-async': false, + 'x-index': 6, + }, + }, + 'x-uid': 'znrsshrlsna', + 'x-async': false, + 'x-index': 1, + }, + '1xnl1d9j48o': { + _isJSONSchemaObject: true, + version: '2.0', + type: 'array', + 'x-initializer': 'TableColumnInitializers', + 'x-component': 'TableV2', + 'x-component-props': { + rowKey: 'id', + rowSelection: { + type: 'checkbox', + }, + useProps: '{{ useTableBlockProps }}', + }, + properties: { + actions: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + title: '{{ t("Actions") }}', + 'x-action-column': 'actions', + 'x-decorator': 'TableV2.Column.ActionBar', + 'x-component': 'TableV2.Column', + 'x-designer': 'TableV2.ActionColumnDesigner', + 'x-initializer': 'TableActionColumnInitializers', + properties: { + actions: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-decorator': 'DndContext', + 'x-component': 'Space', + 'x-component-props': { + split: '|', + }, + 'x-uid': 'ijgo5usyzbp', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '5tnwpodzirq', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'k8t01z9qna3', + 'x-async': false, + 'x-index': 2, + }, + }, + 'x-uid': 'c29q4s49svw', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '9pe6fpnq33f', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'j6g551r7tbp', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'mbw5vw7y3ea', + 'x-async': false, + }, + }, + 'x-uid': '4mbt7m7in1l', + 'x-async': true, + }, +}; diff --git a/packages/plugins/@nocobase/plugin-action-bulk-edit/src/client/index.tsx b/packages/plugins/@nocobase/plugin-action-bulk-edit/src/client/index.tsx new file mode 100644 index 000000000..1b6d58cda --- /dev/null +++ b/packages/plugins/@nocobase/plugin-action-bulk-edit/src/client/index.tsx @@ -0,0 +1,46 @@ +import { Plugin, useCollection } from '@nocobase/client'; +import { BulkEditPluginProvider } from './BulkEditPluginProvider'; +import { BulkEditFormItemInitializers } from './BulkEditFormItemInitializers'; +import { CreateFormBulkEditBlockInitializers } from './CreateFormBulkEditBlockInitializers'; +import { bulkEditactionSettings } from './BulkEditAction.Settings'; +export class BulkEditPlugin extends Plugin { + async load() { + this.app.use(BulkEditPluginProvider); + this.app.schemaSettingsManager.add(bulkEditactionSettings); + this.app.schemaInitializerManager.add(BulkEditFormItemInitializers); + this.app.schemaInitializerManager.add(CreateFormBulkEditBlockInitializers); + + const initializerData = { + type: 'item', + title: '{{t("Bulk edit")}}', + name: 'bulkEdit', + Component: 'CustomizeBulkEditActionInitializer', + schema: { + 'x-align': 'right', + 'x-decorator': 'ACLActionProvider', + 'x-designer': 'Action.Designer', + 'x-action': 'customize:bulkEdit', + 'x-settings': 'ActionSettings:customize:bulkEdit', + 'x-acl-action': 'update', + 'x-acl-action-props': { + skipScopeCheck: true, + }, + }, + useVisible() { + const collection = useCollection(); + return ( + (collection.template !== 'view' || collection?.writableView) && + collection.template !== 'file' && + collection.template !== 'sql' + ); + }, + }; + + const tableActionInitializers = this.app.schemaInitializerManager.get('TableActionInitializers'); + tableActionInitializers?.add('customize.bulkEdit', initializerData); + this.app.schemaInitializerManager.addItem('GanttActionInitializers', 'customize.bulkEdit', initializerData); + this.app.schemaInitializerManager.addItem('MapActionInitializers', 'customize.bulkEdit', initializerData); + } +} + +export default BulkEditPlugin; diff --git a/packages/plugins/@nocobase/plugin-action-bulk-edit/src/client/locale/index.ts b/packages/plugins/@nocobase/plugin-action-bulk-edit/src/client/locale/index.ts new file mode 100644 index 000000000..30b82f7d8 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-action-bulk-edit/src/client/locale/index.ts @@ -0,0 +1,21 @@ +import { i18n } from '@nocobase/client'; +import { useTranslation } from 'react-i18next'; + +export const NAMESPACE = 'bulk-edit'; + +// i18n.addResources('zh-CN', NAMESPACE, zhCN); +// i18n.addResources('en-US', NAMESPACE, enUS); + +export function lang(key: string) { + return i18n.t(key, { ns: NAMESPACE }); +} + +export function generateNTemplate(key: string) { + return `{{t('${key}', { ns: '${NAMESPACE}', nsMode: 'fallback' })}}`; +} + +export function useBulkEditTranslation() { + return useTranslation(NAMESPACE, { + nsMode: 'fallback', + }); +} diff --git a/packages/plugins/@nocobase/plugin-action-bulk-edit/src/client/utils.ts b/packages/plugins/@nocobase/plugin-action-bulk-edit/src/client/utils.ts new file mode 100644 index 000000000..c2bb96fe1 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-action-bulk-edit/src/client/utils.ts @@ -0,0 +1,59 @@ +import { + useCollection, + useCollectionManager, + useRemoveGridFormItem, + SchemaInitializerItemType, +} from '@nocobase/client'; +import { useForm } from '@formily/react'; +import { useMemo } from 'react'; + +export const useCustomBulkEditFormItemInitializerFields = (options?: any) => { + const { name, fields } = useCollection(); + const { getInterface, getCollection } = useCollectionManager(); + const form = useForm(); + const { readPretty = form.readPretty, block = 'Form' } = options || {}; + const remove = useRemoveGridFormItem(); + const filterFields = useMemo( + () => + fields + ?.filter((field) => { + return ( + field?.interface && + !field?.uiSchema?.['x-read-pretty'] && + field.interface !== 'snapshot' && + field.type !== 'sequence' + ); + }) + .map((field) => { + const interfaceConfig = getInterface(field.interface); + const schema = { + type: 'string', + name: field.name, + title: field?.uiSchema?.title || field.name, + 'x-designer': 'FormItem.Designer', + 'x-component': 'BulkEditField', + 'x-decorator': 'FormItem', + 'x-collection-field': `${name}.${field.name}`, + }; + return { + name: field?.uiSchema?.title || field.name, + type: 'item', + title: field?.uiSchema?.title || field.name, + Component: 'CollectionFieldInitializer', + remove: remove, + schemaInitialize: (s) => { + interfaceConfig?.schemaInitialize?.(s, { + field, + block, + readPretty, + targetCollection: getCollection(field.target), + }); + }, + schema, + } as SchemaInitializerItemType; + }), + [fields], + ); + + return filterFields; +}; diff --git a/packages/plugins/@nocobase/plugin-action-bulk-edit/src/index.ts b/packages/plugins/@nocobase/plugin-action-bulk-edit/src/index.ts new file mode 100644 index 000000000..7e74612df --- /dev/null +++ b/packages/plugins/@nocobase/plugin-action-bulk-edit/src/index.ts @@ -0,0 +1,2 @@ +export * from './server'; +export { default } from './server'; diff --git a/packages/plugins/@nocobase/plugin-action-bulk-edit/src/locale/en-US.json b/packages/plugins/@nocobase/plugin-action-bulk-edit/src/locale/en-US.json new file mode 100644 index 000000000..b117d94dd --- /dev/null +++ b/packages/plugins/@nocobase/plugin-action-bulk-edit/src/locale/en-US.json @@ -0,0 +1,4 @@ +{ + "Bulk edit": "Bulk edit", + "Data will be updated": "Data will be updated" +} diff --git a/packages/plugins/@nocobase/plugin-action-bulk-edit/src/locale/es-ES.json b/packages/plugins/@nocobase/plugin-action-bulk-edit/src/locale/es-ES.json new file mode 100644 index 000000000..1cd818880 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-action-bulk-edit/src/locale/es-ES.json @@ -0,0 +1,4 @@ +{ + "Bulk edit": "Edición en masa", + "Data will be updated": "Se actualizarán los datos" +} diff --git a/packages/plugins/@nocobase/plugin-action-bulk-edit/src/locale/pt-BR.json b/packages/plugins/@nocobase/plugin-action-bulk-edit/src/locale/pt-BR.json new file mode 100644 index 000000000..eeac1d46e --- /dev/null +++ b/packages/plugins/@nocobase/plugin-action-bulk-edit/src/locale/pt-BR.json @@ -0,0 +1,4 @@ + { + "Bulk edit": "Edição em massa", + "Data will be updated": "Os dados serão atualizados" +} diff --git a/packages/plugins/@nocobase/plugin-action-bulk-edit/src/locale/zh-CN.json b/packages/plugins/@nocobase/plugin-action-bulk-edit/src/locale/zh-CN.json new file mode 100644 index 000000000..f9e8775f3 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-action-bulk-edit/src/locale/zh-CN.json @@ -0,0 +1,4 @@ +{ + "Bulk edit": "批量编辑", + "Data will be updated": "更新的数据" +} diff --git a/packages/plugins/@nocobase/plugin-action-bulk-edit/src/server/collections/.gitkeep b/packages/plugins/@nocobase/plugin-action-bulk-edit/src/server/collections/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/packages/plugins/@nocobase/plugin-action-bulk-edit/src/server/index.ts b/packages/plugins/@nocobase/plugin-action-bulk-edit/src/server/index.ts new file mode 100644 index 000000000..b68aea57f --- /dev/null +++ b/packages/plugins/@nocobase/plugin-action-bulk-edit/src/server/index.ts @@ -0,0 +1 @@ +export { default } from './plugin'; diff --git a/packages/plugins/@nocobase/plugin-action-bulk-edit/src/server/plugin.ts b/packages/plugins/@nocobase/plugin-action-bulk-edit/src/server/plugin.ts new file mode 100644 index 000000000..3348ddd97 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-action-bulk-edit/src/server/plugin.ts @@ -0,0 +1,19 @@ +import { InstallOptions, Plugin } from '@nocobase/server'; + +export class PluginActionBulkEditServer extends Plugin { + afterAdd() {} + + beforeLoad() {} + + async load() {} + + async install(options?: InstallOptions) {} + + async afterEnable() {} + + async afterDisable() {} + + async remove() {} +} + +export default PluginActionBulkEditServer; diff --git a/packages/plugins/@nocobase/plugin-action-bulk-update/.npmignore b/packages/plugins/@nocobase/plugin-action-bulk-update/.npmignore new file mode 100644 index 000000000..65f5e8779 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-action-bulk-update/.npmignore @@ -0,0 +1,2 @@ +/node_modules +/src diff --git a/packages/plugins/@nocobase/plugin-action-bulk-update/README.md b/packages/plugins/@nocobase/plugin-action-bulk-update/README.md new file mode 100644 index 000000000..74a21eb41 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-action-bulk-update/README.md @@ -0,0 +1 @@ +# @nocobase/plugin-action-bulk-update diff --git a/packages/plugins/@nocobase/plugin-action-bulk-update/client.d.ts b/packages/plugins/@nocobase/plugin-action-bulk-update/client.d.ts new file mode 100644 index 000000000..6c459cbac --- /dev/null +++ b/packages/plugins/@nocobase/plugin-action-bulk-update/client.d.ts @@ -0,0 +1,2 @@ +export * from './dist/client'; +export { default } from './dist/client'; diff --git a/packages/plugins/@nocobase/plugin-action-bulk-update/client.js b/packages/plugins/@nocobase/plugin-action-bulk-update/client.js new file mode 100644 index 000000000..b6e3be70e --- /dev/null +++ b/packages/plugins/@nocobase/plugin-action-bulk-update/client.js @@ -0,0 +1 @@ +module.exports = require('./dist/client/index.js'); diff --git a/packages/plugins/@nocobase/plugin-action-bulk-update/package.json b/packages/plugins/@nocobase/plugin-action-bulk-update/package.json new file mode 100644 index 000000000..b00e4af83 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-action-bulk-update/package.json @@ -0,0 +1,16 @@ +{ + "name": "@nocobase/plugin-action-bulk-update", + "version":"0.17.0-alpha.7", + "main": "dist/server/index.js", + "displayName": "Bulk update action", + "displayName.zh-CN": "批量更新操作", + "description": "Provides bulk update action", + "description.zh-CN": "提供批量更新操作", + "license": "AGPL-3.0", + "dependencies": {}, + "peerDependencies": { + "@nocobase/client": "0.x", + "@nocobase/server": "0.x", + "@nocobase/test": "0.x" + } +} diff --git a/packages/plugins/@nocobase/plugin-action-bulk-update/server.d.ts b/packages/plugins/@nocobase/plugin-action-bulk-update/server.d.ts new file mode 100644 index 000000000..c41081ddc --- /dev/null +++ b/packages/plugins/@nocobase/plugin-action-bulk-update/server.d.ts @@ -0,0 +1,2 @@ +export * from './dist/server'; +export { default } from './dist/server'; diff --git a/packages/plugins/@nocobase/plugin-action-bulk-update/server.js b/packages/plugins/@nocobase/plugin-action-bulk-update/server.js new file mode 100644 index 000000000..972842039 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-action-bulk-update/server.js @@ -0,0 +1 @@ +module.exports = require('./dist/server/index.js'); diff --git a/packages/plugins/@nocobase/plugin-action-bulk-update/src/client/BulkUpdateAction.Settings.tsx b/packages/plugins/@nocobase/plugin-action-bulk-update/src/client/BulkUpdateAction.Settings.tsx new file mode 100644 index 000000000..5f984a407 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-action-bulk-update/src/client/BulkUpdateAction.Settings.tsx @@ -0,0 +1,237 @@ +import React, { useCallback, useState, useEffect } from 'react'; +import { + SchemaSettings, + ActionDesigner, + useSchemaToolbar, + useDesignable, + useCompile, + DefaultValueProvider, + SchemaSettingsItemGroup, + SchemaSettingsModalItem, + SchemaSettingsActionModalItem, + SchemaSettingsSelectItem, + FlagProvider, +} from '@nocobase/client'; +import { isValid, uid } from '@formily/shared'; +import { useTranslation } from 'react-i18next'; +import { useField, useFieldSchema, ISchema } from '@formily/react'; + +const MenuGroup = (props) => { + const fieldSchema = useFieldSchema(); + const { t } = useTranslation(); + const compile = useCompile(); + const actionTitle = fieldSchema.title ? compile(fieldSchema.title) : ''; + + return ( + ${actionTitle}`}>{props.children} + ); +}; + +function UpdateMode() { + const { dn } = useDesignable(); + const { t } = useTranslation(); + const fieldSchema = useFieldSchema(); + + return ( + { + fieldSchema['x-action-settings']['updateMode'] = value; + dn.emit('patch', { + schema: { + 'x-uid': fieldSchema['x-uid'], + 'x-action-settings': fieldSchema['x-action-settings'], + }, + }); + dn.refresh(); + }} + /> + ); +} + +function AfterSuccess() { + const { dn } = useDesignable(); + const { t } = useTranslation(); + const fieldSchema = useFieldSchema(); + return ( + { + fieldSchema['x-action-settings']['onSuccess'] = onSuccess; + dn.emit('patch', { + schema: { + ['x-uid']: fieldSchema['x-uid'], + 'x-action-settings': fieldSchema['x-action-settings'], + }, + }); + }} + /> + ); +} +function AssignedFieldValues() { + const { dn } = useDesignable(); + const { t } = useTranslation(); + const fieldSchema = useFieldSchema(); + const field = useField(); + const [initialSchema, setInitialSchema] = useState(); + useEffect(() => { + const schemaUid = uid(); + const schema: ISchema = { + type: 'void', + 'x-uid': schemaUid, + 'x-component': 'Grid', + 'x-initializer': 'CustomFormItemInitializers', + }; + setInitialSchema(schema); + }, [field.address]); + + const tips = { + 'customize:update': t( + 'After clicking the custom button, the following fields of the current record will be saved according to the following form.', + ), + 'customize:save': t( + 'After clicking the custom button, the following fields of the current record will be saved according to the following form.', + ), + }; + const actionType = fieldSchema['x-action'] ?? ''; + const onSubmit = useCallback( + (assignedValues) => { + fieldSchema['x-action-settings']['assignedValues'] = assignedValues; + dn.emit('patch', { + schema: { + ['x-uid']: fieldSchema['x-uid'], + 'x-action-settings': fieldSchema['x-action-settings'], + }, + }); + }, + [dn, fieldSchema], + ); + + return ( + + false}> + + + + ); +} + +const bulkUpdateActionSettings = new SchemaSettings({ + name: 'ActionSettings:customize:bulkUpdate', + items: [ + { + name: 'Customize', + Component: MenuGroup, + children: [ + { + name: 'editButton', + Component: ActionDesigner.ButtonEditor, + useComponentProps() { + const { buttonEditorProps } = useSchemaToolbar(); + return buttonEditorProps; + }, + }, + { + name: 'updateMode', + Component: UpdateMode, + useVisible() { + const fieldSchema = useFieldSchema(); + const isUpdateModePopupAction = ['customize:bulkUpdate', 'customize:bulkEdit'].includes( + fieldSchema['x-action'], + ); + return isUpdateModePopupAction; + }, + }, + { + name: 'assignFieldValues', + Component: AssignedFieldValues, + useVisible() { + const fieldSchema = useFieldSchema(); + return isValid(fieldSchema?.['x-action-settings']?.assignedValues); + }, + }, + { + name: 'afterSuccess', + Component: AfterSuccess, + useVisible() { + const fieldSchema = useFieldSchema(); + return isValid(fieldSchema?.['x-action-settings']?.onSuccess); + }, + }, + { + name: 'remove', + sort: 100, + Component: ActionDesigner.RemoveButton as any, + useComponentProps() { + const { removeButtonProps } = useSchemaToolbar(); + return removeButtonProps; + }, + }, + ], + }, + ], +}); + +export { bulkUpdateActionSettings }; diff --git a/packages/plugins/@nocobase/plugin-action-bulk-update/src/client/BulkUpdatePluginProvider.tsx b/packages/plugins/@nocobase/plugin-action-bulk-update/src/client/BulkUpdatePluginProvider.tsx new file mode 100644 index 000000000..b54eddcda --- /dev/null +++ b/packages/plugins/@nocobase/plugin-action-bulk-update/src/client/BulkUpdatePluginProvider.tsx @@ -0,0 +1,17 @@ +import { SchemaComponentOptions } from '@nocobase/client'; +import React from 'react'; +import { CustomizeActionInitializer } from './CustomizeActionInitializer'; +import { useCustomizeBulkUpdateActionProps } from './utils'; + +export const BulkUpdatePluginProvider = (props: any) => { + return ( + + {props.children} + + ); +}; diff --git a/packages/plugins/@nocobase/plugin-action-bulk-update/src/client/CustomizeActionInitializer.tsx b/packages/plugins/@nocobase/plugin-action-bulk-update/src/client/CustomizeActionInitializer.tsx new file mode 100644 index 000000000..bfb71dacc --- /dev/null +++ b/packages/plugins/@nocobase/plugin-action-bulk-update/src/client/CustomizeActionInitializer.tsx @@ -0,0 +1,15 @@ +import React from 'react'; +import { BlockInitializer, useCollection, useSchemaInitializerItem } from '@nocobase/client'; + +export const CustomizeActionInitializer = () => { + const collection = useCollection(); + const itemConfig = useSchemaInitializerItem(); + + const schema = {}; + if (collection && schema['x-acl-action']) { + schema['x-acl-action'] = `${collection.name}:${schema['x-acl-action']}`; + schema['x-decorator'] = 'ACLActionProvider'; + } + + return ; +}; diff --git a/packages/plugins/@nocobase/plugin-action-bulk-update/src/client/__e2e__/schemaInitailizer.test.ts b/packages/plugins/@nocobase/plugin-action-bulk-update/src/client/__e2e__/schemaInitailizer.test.ts new file mode 100644 index 000000000..77e6f440c --- /dev/null +++ b/packages/plugins/@nocobase/plugin-action-bulk-update/src/client/__e2e__/schemaInitailizer.test.ts @@ -0,0 +1,22 @@ +import { expect, test, oneEmptyTableBlockWithCustomizeActions } from '@nocobase/test/client'; +import { oneEmptyGantt } from './utils'; + +test.describe('TableActionInitializers & GanttActionInitializers & MapActionInitializers ', () => { + test('TableActionInitializers should add bulk update', async ({ page, mockPage }) => { + await mockPage(oneEmptyTableBlockWithCustomizeActions).goto(); + await page.getByLabel('schema-initializer-ActionBar-TableActionInitializers-general').hover(); + await page.getByRole('menuitem', { name: 'Customize right' }).click(); + await page.getByRole('menuitem', { name: 'Bulk update' }).click(); + await expect(page.getByLabel('action-Action-Bulk update-customize:bulkUpdate-general-table')).toBeVisible(); + }); + test('GanttActionInitializers should add bulk update', async ({ page, mockPage, mockRecords }) => { + const nocoPage = await mockPage(oneEmptyGantt).waitForInit(); + await mockRecords('general', 3); + await nocoPage.goto(); + await page.getByLabel('schema-initializer-ActionBar-GanttActionInitializers-general').hover(); + await page.getByRole('menuitem', { name: 'Customize right' }).click(); + await page.getByRole('menuitem', { name: 'Bulk update' }).click(); + await page.mouse.move(300, 0); + await expect(page.getByRole('button', { name: 'Bulk update' })).toBeVisible(); + }); +}); diff --git a/packages/plugins/@nocobase/plugin-action-bulk-update/src/client/__e2e__/schemaSettings.test.ts b/packages/plugins/@nocobase/plugin-action-bulk-update/src/client/__e2e__/schemaSettings.test.ts new file mode 100644 index 000000000..230752984 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-action-bulk-update/src/client/__e2e__/schemaSettings.test.ts @@ -0,0 +1,46 @@ +import { expect, test } from '@nocobase/test/client'; +import { oneEmptyTableBlockWithCustomizeUpdate } from './utils'; + +test.describe('data will be updated && Assign field values && after successful submission', () => { + test('data will be updated && Assign field values && after successful submission', async ({ + page, + mockPage, + mockRecords, + }) => { + await mockPage(oneEmptyTableBlockWithCustomizeUpdate).goto(); + await mockRecords('general', 2); + await page.getByLabel('action-Action-Bulk update-customize:bulkUpdate-general-table').hover(); + await page.getByLabel('designer-schema-settings-Action-Action.Designer-general').hover(); + //默认是选中的数据 + await expect(await page.getByTitle('Data will be updated').getByText('Selected')).toBeVisible(); + await page.getByRole('menuitem', { name: 'Data will be updated' }).click(); + //切换为全部数据 + await page.getByRole('option', { name: 'All' }).click(); + //字段赋值 + await page.getByRole('menuitem', { name: 'Assign field values' }).click(); + await page.getByLabel('schema-initializer-Grid-CustomFormItemInitializers-general').click(); + await page.getByRole('menuitem', { name: 'Single select' }).click(); + await page.getByTestId('select-single').click(); + await page.getByText('option3').click(); + await page.getByRole('button', { name: 'Submit' }).click(); + await page.getByLabel('action-Action-Bulk update-customize:bulkUpdate-general-table').hover(); + await page.getByLabel('designer-schema-settings-Action-Action.Designer-general').hover(); + await page.getByRole('menuitem', { name: 'After successful submission' }).click(); + await page.getByLabel('Manually close').check(); + await page.getByLabel('Redirect to').check(); + await page.locator('input[type="text"]').click(); + await page.locator('input[type="text"]').fill('/admin/pm/list/local/'); + await page.getByRole('button', { name: 'OK' }).click(); + await page.getByLabel('action-Action-Bulk update-customize:bulkUpdate-general-table').click(); + const [request] = await Promise.all([ + page.waitForRequest((request) => request.url().includes('api/general:update')), + page.getByRole('button', { name: 'OK' }).click(), + ]); + const postData = request.postDataJSON(); + //更新的数据符合预期 + expect(postData.singleSelect).toEqual('option3'); + await page.getByRole('button', { name: 'OK' }).click(); + //成功后跳转路由 + expect(page.url()).toContain('/admin/pm/list/local/'); + }); +}); diff --git a/packages/plugins/@nocobase/plugin-action-bulk-update/src/client/__e2e__/utils.ts b/packages/plugins/@nocobase/plugin-action-bulk-update/src/client/__e2e__/utils.ts new file mode 100644 index 000000000..a27303e66 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-action-bulk-update/src/client/__e2e__/utils.ts @@ -0,0 +1,529 @@ +import { CollectionSetting, PageConfig, generalWithM2oSingleSelect } from '@nocobase/test/client'; + +/** + * 1. 创建一个名为 general 的 collection,其包含 时间、Percent 类型的字段 + */ +export const generalWithDatetimeFields: CollectionSetting[] = [ + { + name: 'general', + title: 'General', + fields: [ + { + name: 'singleLineText', + interface: 'input', + uiSchema: { + type: 'string', + 'x-component': 'Input', + title: 'Single line text', + }, + }, + { + name: 'singleLineText2', + interface: 'input', + uiSchema: { + type: 'string', + 'x-component': 'Input', + title: 'Single line text2', + }, + }, + { + name: 'startDatetime', + interface: 'datetime', + uiSchema: { + type: 'string', + 'x-component': 'DatePicker', + title: 'Start date time', + required: true, + }, + }, + { + name: 'startDatetime2', + interface: 'datetime', + uiSchema: { + type: 'string', + 'x-component': 'DatePicker', + title: 'Start date time2', + required: true, + }, + }, + { + name: 'endDatetime', + interface: 'datetime', + uiSchema: { + type: 'string', + 'x-component': 'DatePicker', + title: 'End date time', + required: true, + }, + }, + { + name: 'endDatetime2', + interface: 'datetime', + uiSchema: { + type: 'string', + 'x-component': 'DatePicker', + title: 'End date time2', + required: true, + }, + }, + { + name: 'f_t22o7loai3j', + interface: 'integer', + isForeignKey: true, + uiSchema: { + type: 'number', + title: 'f_t22o7loai3j', + 'x-component': 'InputNumber', + 'x-read-pretty': true, + }, + }, + { + name: 'manyToOne', + interface: 'm2o', + foreignKey: 'f_t22o7loai3j', + uiSchema: { + 'x-component': 'AssociationField', + 'x-component-props': { + multiple: false, + fieldNames: { + label: 'id', + value: 'id', + }, + }, + title: 'Many to one', + }, + target: 'users', + targetKey: 'id', + }, + { + name: 'percent', + type: 'float', + interface: 'percent', + uiSchema: { + 'x-component-props': { + step: '0.01', + stringMode: true, + addonAfter: '%', + }, + 'x-component': 'Percent', + title: 'Percent', + }, + }, + { + name: 'percent2', + type: 'float', + interface: 'percent', + uiSchema: { + 'x-component-props': { + step: '0.01', + stringMode: true, + addonAfter: '%', + }, + 'x-component': 'Percent', + title: 'Percent2', + }, + }, + ], + }, +]; + +export const oneEmptyGantt: PageConfig = { + collections: generalWithDatetimeFields, + pageSchema: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Page', + properties: { + jebhzap4dzi: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid', + 'x-initializer': 'BlockInitializers', + properties: { + gl57m4hyewf: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Row', + properties: { + jm7n5dybw6t: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Col', + properties: { + nsq0rdemz4i: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-acl-action': 'general:list', + 'x-decorator': 'GanttBlockProvider', + 'x-decorator-props': { + collection: 'general', + resource: 'general', + action: 'list', + fieldNames: { + id: 'id', + start: 'startDatetime', + range: 'day', + title: 'singleLineText', + end: 'endDatetime', + }, + params: { + paginate: false, + }, + }, + 'x-designer': 'Gantt.Designer', + 'x-component': 'CardItem', + properties: { + zf07g7relim: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Gantt', + 'x-component-props': { + useProps: '{{ useGanttBlockProps }}', + }, + properties: { + toolBar: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'ActionBar', + 'x-component-props': { + style: { + marginBottom: 24, + }, + }, + 'x-initializer': 'GanttActionInitializers', + 'x-uid': 'guwovmwt4c0', + 'x-async': false, + 'x-index': 1, + }, + table: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'array', + 'x-decorator': 'div', + 'x-decorator-props': { + style: { + float: 'left', + maxWidth: '35%', + }, + }, + 'x-initializer': 'TableColumnInitializers', + 'x-component': 'TableV2', + 'x-component-props': { + rowKey: 'id', + rowSelection: { + type: 'checkbox', + }, + useProps: '{{ useTableBlockProps }}', + pagination: false, + }, + properties: { + actions: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + title: '{{ t("Actions") }}', + 'x-action-column': 'actions', + 'x-decorator': 'TableV2.Column.ActionBar', + 'x-component': 'TableV2.Column', + 'x-designer': 'TableV2.ActionColumnDesigner', + 'x-initializer': 'TableActionColumnInitializers', + properties: { + actions: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-decorator': 'DndContext', + 'x-component': 'Space', + 'x-component-props': { + split: '|', + }, + 'x-uid': '9in7s3pymsd', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'rgpbsjwvq2h', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'g7qwompxaeo', + 'x-async': false, + 'x-index': 2, + }, + detail: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Gantt.Event', + properties: { + drawer: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Action.Drawer', + 'x-component-props': { + className: 'nb-action-popup', + }, + title: '{{ t("View record") }}', + properties: { + tabs: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Tabs', + 'x-component-props': {}, + 'x-initializer': 'TabPaneInitializers', + properties: { + tab1: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + title: '{{t("Details")}}', + 'x-component': 'Tabs.TabPane', + 'x-designer': 'Tabs.Designer', + 'x-component-props': {}, + properties: { + grid: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid', + 'x-initializer': 'RecordBlockInitializers', + 'x-uid': 'gxtfjqzxbfu', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'zxwwx4358p1', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '1p0tnmzpsim', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'hcue8v3fwti', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'fg7cjxu4hxp', + 'x-async': false, + 'x-index': 3, + }, + }, + 'x-uid': 'rf47sf7k16z', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'tritqukkd86', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '3am2ctuhyka', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'p8pesbc7fiv', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'ddm9fhkvrbw', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'vpk8sp3eap1', + 'x-async': true, + 'x-index': 1, + }, +}; +/** + * 一个空的 Table 区块,并且有这些按钮:Bulk edit + */ +export const oneEmptyTableBlockWithCustomizeUpdate: PageConfig = { + collections: generalWithM2oSingleSelect, + pageSchema: { + type: 'void', + version: '2.0', + 'x-component': 'Page', + _isJSONSchemaObject: true, + properties: { + s409jhc9wzp: { + type: 'void', + version: '2.0', + 'x-component': 'Grid', + 'x-initializer': 'BlockInitializers', + _isJSONSchemaObject: true, + properties: { + btrf53i4lpi: { + type: 'void', + version: '2.0', + 'x-component': 'Grid.Row', + _isJSONSchemaObject: true, + properties: { + '6u5d4dju1zw': { + type: 'void', + version: '2.0', + 'x-component': 'Grid.Col', + _isJSONSchemaObject: true, + properties: { + yxyx102howk: { + type: 'void', + version: '2.0', + 'x-designer': 'TableBlockDesigner', + 'x-component': 'CardItem', + 'x-decorator': 'TableBlockProvider', + 'x-acl-action': 'general:list', + 'x-filter-targets': [], + 'x-decorator-props': { + action: 'list', + params: { + pageSize: 20, + }, + rowKey: 'id', + dragSort: false, + resource: 'general', + showIndex: true, + collection: 'general', + disableTemplate: false, + }, + _isJSONSchemaObject: true, + properties: { + actions: { + type: 'void', + version: '2.0', + 'x-component': 'ActionBar', + 'x-initializer': 'TableActionInitializers', + 'x-component-props': { + style: { + marginBottom: 'var(--nb-spacing)', + }, + }, + _isJSONSchemaObject: true, + properties: { + hsb2p0q5ftw: { + type: 'void', + title: '{{ t("Bulk update") }}', + version: '2.0', + 'x-align': 'right', + 'x-action': 'customize:bulkUpdate', + 'x-designer': 'Action.Designer', + 'x-settings': 'ActionSettings:customize:bulkUpdate', + 'x-component': 'Action', + 'x-decorator': 'ACLActionProvider', + 'x-acl-action': 'update', + 'x-action-settings': { + onSuccess: { + manualClose: true, + redirecting: false, + successMessage: '{{t("Updated successfully")}}', + }, + updateMode: 'selected', + assignedValues: {}, + }, + 'x-component-props': { + icon: 'EditOutlined', + useProps: '{{ useCustomizeBulkUpdateActionProps }}', + }, + 'x-acl-action-props': { + skipScopeCheck: true, + }, + _isJSONSchemaObject: true, + 'x-uid': '5nghpuc3f3x', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '8lq9zqwwkqd', + 'x-async': false, + 'x-index': 1, + }, + dcblkvx1jj1: { + type: 'array', + version: '2.0', + 'x-component': 'TableV2', + 'x-initializer': 'TableColumnInitializers', + 'x-component-props': { + rowKey: 'id', + useProps: '{{ useTableBlockProps }}', + rowSelection: { + type: 'checkbox', + }, + }, + _isJSONSchemaObject: true, + properties: { + actions: { + type: 'void', + title: '{{ t("Actions") }}', + version: '2.0', + 'x-designer': 'TableV2.ActionColumnDesigner', + 'x-component': 'TableV2.Column', + 'x-decorator': 'TableV2.Column.ActionBar', + 'x-initializer': 'TableActionColumnInitializers', + 'x-action-column': 'actions', + _isJSONSchemaObject: true, + properties: { + actions: { + type: 'void', + version: '2.0', + 'x-component': 'Space', + 'x-decorator': 'DndContext', + 'x-component-props': { + split: '|', + }, + _isJSONSchemaObject: true, + 'x-uid': 'dqt7mgaazmh', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'qp1nqbd1ccv', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '32v8q0beyo2', + 'x-async': false, + 'x-index': 2, + }, + }, + 'x-uid': 't785fa9uwcc', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'pebalqz6iw2', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'drll4zh4jq1', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '5fdz5g6qr6y', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'z47gi9qo5jn', + 'x-async': true, + 'x-index': 1, + }, +}; diff --git a/packages/plugins/@nocobase/plugin-action-bulk-update/src/client/index.tsx b/packages/plugins/@nocobase/plugin-action-bulk-update/src/client/index.tsx new file mode 100644 index 000000000..70b8c4e6f --- /dev/null +++ b/packages/plugins/@nocobase/plugin-action-bulk-update/src/client/index.tsx @@ -0,0 +1,57 @@ +import { Plugin, useCollection } from '@nocobase/client'; +import { BulkUpdatePluginProvider } from './BulkUpdatePluginProvider'; +import { bulkUpdateActionSettings } from './BulkUpdateAction.Settings'; +export class BulkUpdatePlugin extends Plugin { + async load() { + this.app.use(BulkUpdatePluginProvider); + this.app.schemaSettingsManager.add(bulkUpdateActionSettings); + + const initializerData = { + title: '{{t("Bulk update")}}', + Component: 'CustomizeActionInitializer', + name: 'bulkUpdate', + schema: { + type: 'void', + title: '{{ t("Bulk update") }}', + 'x-component': 'Action', + 'x-align': 'right', + 'x-acl-action': 'update', + 'x-decorator': 'ACLActionProvider', + 'x-designer': 'Action.Designer', + 'x-acl-action-props': { + skipScopeCheck: true, + }, + 'x-action': 'customize:bulkUpdate', + 'x-settings': 'ActionSettings:customize:bulkUpdate', + 'x-action-settings': { + assignedValues: {}, + updateMode: 'selected', + onSuccess: { + manualClose: true, + redirecting: false, + successMessage: '{{t("Updated successfully")}}', + }, + }, + 'x-component-props': { + icon: 'EditOutlined', + useProps: '{{ useCustomizeBulkUpdateActionProps }}', + }, + }, + useVisible() { + const collection = useCollection(); + return ( + (collection.template !== 'view' || collection?.writableView) && + collection.template !== 'file' && + collection.template !== 'sql' + ); + }, + }; + + const tableActionInitializers = this.app.schemaInitializerManager.get('TableActionInitializers'); + tableActionInitializers?.add('customize.bulkUpdate', initializerData); + this.app.schemaInitializerManager.addItem('GanttActionInitializers', 'customize.bulkUpdate', initializerData); + this.app.schemaInitializerManager.addItem('MapActionInitializers', 'customize.bulkUpdate', initializerData); + } +} + +export default BulkUpdatePlugin; diff --git a/packages/plugins/@nocobase/plugin-action-bulk-update/src/client/locale/index.ts b/packages/plugins/@nocobase/plugin-action-bulk-update/src/client/locale/index.ts new file mode 100644 index 000000000..d65c0dd93 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-action-bulk-update/src/client/locale/index.ts @@ -0,0 +1,21 @@ +import { i18n } from '@nocobase/client'; +import { useTranslation } from 'react-i18next'; + +export const NAMESPACE = 'bulk-update'; + +// i18n.addResources('zh-CN', NAMESPACE, zhCN); +// i18n.addResources('en-US', NAMESPACE, enUS); + +export function lang(key: string) { + return i18n.t(key, { ns: NAMESPACE }); +} + +export function generateNTemplate(key: string) { + return `{{t('${key}', { ns: '${NAMESPACE}', nsMode: 'fallback' })}}`; +} + +export function useBulkUpdateTranslation() { + return useTranslation(NAMESPACE, { + nsMode: 'fallback', + }); +} diff --git a/packages/plugins/@nocobase/plugin-action-bulk-update/src/client/utils.tsx b/packages/plugins/@nocobase/plugin-action-bulk-update/src/client/utils.tsx new file mode 100644 index 000000000..708364dc4 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-action-bulk-update/src/client/utils.tsx @@ -0,0 +1,135 @@ +import { + useBlockRequestContext, + useTableBlockContext, + useCompile, + useRecord, + useVariables, + useCollection, + useLocalVariables, + isVariable, + transformVariableValue, + TableFieldResource, +} from '@nocobase/client'; +import { isURL } from '@nocobase/utils/client'; +import { SchemaExpressionScopeContext, useFieldSchema, useField } from '@formily/react'; +import { useNavigate } from 'react-router-dom'; +import { App, message } from 'antd'; +import { useContext } from 'react'; +import { useBulkUpdateTranslation } from './locale'; + +export const useCustomizeBulkUpdateActionProps = () => { + const { field, resource, __parent, service } = useBlockRequestContext(); + const expressionScope = useContext(SchemaExpressionScopeContext); + const actionSchema = useFieldSchema(); + const tableBlockContext = useTableBlockContext(); + const { rowKey } = tableBlockContext; + const selectedRecordKeys = + tableBlockContext.field?.data?.selectedRowKeys ?? expressionScope?.selectedRecordKeys ?? {}; + const navigate = useNavigate(); + const compile = useCompile(); + const { t } = useBulkUpdateTranslation(); + const actionField: any = useField(); + const { modal } = App.useApp(); + const variables = useVariables(); + const record = useRecord(); + const { name, getField } = useCollection(); + const localVariables = useLocalVariables({ currentRecord: { __parent: record, __collectionName: name } }); + + return { + async onClick() { + const { + assignedValues: originalAssignedValues = {}, + onSuccess, + updateMode, + } = actionSchema?.['x-action-settings'] ?? {}; + actionField.data = field.data || {}; + actionField.data.loading = true; + + const assignedValues = {}; + const waitList = Object.keys(originalAssignedValues).map(async (key) => { + const value = originalAssignedValues[key]; + const collectionField = getField(key); + + if (process.env.NODE_ENV !== 'production') { + if (!collectionField) { + throw new Error(`useCustomizeBulkUpdateActionProps: field "${key}" not found in collection "${name}"`); + } + } + + if (isVariable(value)) { + const result = await variables?.parseVariable(value, localVariables); + if (result) { + assignedValues[key] = transformVariableValue(result, { targetCollectionField: collectionField }); + } + } else if (value != null && value !== '') { + assignedValues[key] = value; + } + }); + await Promise.all(waitList); + + modal.confirm({ + title: t('Bulk update'), + content: updateMode === 'selected' ? t('Update selected data?') : t('Update all data?'), + async onOk() { + const { filter } = service.params?.[0] ?? {}; + const updateData: { filter?: any; values: any; forceUpdate: boolean } = { + values: { ...assignedValues }, + filter, + forceUpdate: false, + }; + if (updateMode === 'selected') { + if (!selectedRecordKeys?.length) { + message.error(t('Please select the records to be updated')); + actionField.data.loading = false; + return; + } + updateData.filter = { $and: [{ [rowKey || 'id']: { $in: selectedRecordKeys } }] }; + } + if (!updateData.filter) { + updateData.forceUpdate = true; + } + try { + await resource.update(updateData); + } catch (error) { + /* empty */ + } finally { + actionField.data.loading = false; + } + service?.refresh?.(); + if (!(resource instanceof TableFieldResource)) { + __parent?.service?.refresh?.(); + } + if (!onSuccess?.successMessage) { + return; + } + if (onSuccess?.manualClose) { + modal.success({ + title: compile(onSuccess?.successMessage), + onOk: async () => { + if (onSuccess?.redirecting && onSuccess?.redirectTo) { + if (isURL(onSuccess.redirectTo)) { + window.location.href = onSuccess.redirectTo; + } else { + navigate(onSuccess.redirectTo); + } + } + }, + }); + } else { + message.success(compile(onSuccess?.successMessage)); + if (onSuccess?.redirecting && onSuccess?.redirectTo) { + if (isURL(onSuccess.redirectTo)) { + window.location.href = onSuccess.redirectTo; + } else { + navigate(onSuccess.redirectTo); + } + } + } + }, + async onCancel() { + actionField.data.loading = false; + }, + }); + }, + }; +}; diff --git a/packages/plugins/@nocobase/plugin-action-bulk-update/src/index.ts b/packages/plugins/@nocobase/plugin-action-bulk-update/src/index.ts new file mode 100644 index 000000000..7e74612df --- /dev/null +++ b/packages/plugins/@nocobase/plugin-action-bulk-update/src/index.ts @@ -0,0 +1,2 @@ +export * from './server'; +export { default } from './server'; diff --git a/packages/plugins/@nocobase/plugin-action-bulk-update/src/locale/en-US.ts b/packages/plugins/@nocobase/plugin-action-bulk-update/src/locale/en-US.ts new file mode 100644 index 000000000..a3cf6c88a --- /dev/null +++ b/packages/plugins/@nocobase/plugin-action-bulk-update/src/locale/en-US.ts @@ -0,0 +1,5 @@ +export default { + "Bulk update": "Bulk update", + "After successful bulk update": "After successful bulk update", + "Please select the records to be updated": "Please select the records to be updated", +}; diff --git a/packages/plugins/@nocobase/plugin-action-bulk-update/src/locale/es-ES.ts b/packages/plugins/@nocobase/plugin-action-bulk-update/src/locale/es-ES.ts new file mode 100644 index 000000000..38806e233 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-action-bulk-update/src/locale/es-ES.ts @@ -0,0 +1,5 @@ +export default { + "Bulk update": "Actualización en masa", + "After successful bulk update": "Tras una actualización en masa correcta", + "Please select the records to be updated": "Seleccione los registros que desea actualizar", +}; diff --git a/packages/plugins/@nocobase/plugin-action-bulk-update/src/locale/pt-BR.ts b/packages/plugins/@nocobase/plugin-action-bulk-update/src/locale/pt-BR.ts new file mode 100644 index 000000000..7b1459602 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-action-bulk-update/src/locale/pt-BR.ts @@ -0,0 +1,5 @@ +export default { + "Bulk update": "Atualização em massa", + "After successful bulk update": "Após a atualização em massa bem sucedida", + "Please select the records to be updated": "Por favor, selecione os registros a serem atualizados", +}; diff --git a/packages/plugins/@nocobase/plugin-action-bulk-update/src/locale/zh-CN.ts b/packages/plugins/@nocobase/plugin-action-bulk-update/src/locale/zh-CN.ts new file mode 100644 index 000000000..204ce95bf --- /dev/null +++ b/packages/plugins/@nocobase/plugin-action-bulk-update/src/locale/zh-CN.ts @@ -0,0 +1,5 @@ +export default { + 'Bulk update': '批量更新', + 'After successful bulk update': '批量成功更新后', + 'Please select the records to be updated': '请选择要更新的记录', +}; diff --git a/packages/plugins/@nocobase/plugin-action-bulk-update/src/server/collections/.gitkeep b/packages/plugins/@nocobase/plugin-action-bulk-update/src/server/collections/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/packages/plugins/@nocobase/plugin-action-bulk-update/src/server/index.ts b/packages/plugins/@nocobase/plugin-action-bulk-update/src/server/index.ts new file mode 100644 index 000000000..b68aea57f --- /dev/null +++ b/packages/plugins/@nocobase/plugin-action-bulk-update/src/server/index.ts @@ -0,0 +1 @@ +export { default } from './plugin'; diff --git a/packages/plugins/@nocobase/plugin-action-bulk-update/src/server/plugin.ts b/packages/plugins/@nocobase/plugin-action-bulk-update/src/server/plugin.ts new file mode 100644 index 000000000..7292c86de --- /dev/null +++ b/packages/plugins/@nocobase/plugin-action-bulk-update/src/server/plugin.ts @@ -0,0 +1,19 @@ +import { InstallOptions, Plugin } from '@nocobase/server'; + +export class PluginActionBulkUpdateServer extends Plugin { + afterAdd() {} + + beforeLoad() {} + + async load() {} + + async install(options?: InstallOptions) {} + + async afterEnable() {} + + async afterDisable() {} + + async remove() {} +} + +export default PluginActionBulkUpdateServer; diff --git a/packages/plugins/@nocobase/plugin-action-duplicate/.npmignore b/packages/plugins/@nocobase/plugin-action-duplicate/.npmignore new file mode 100644 index 000000000..65f5e8779 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-action-duplicate/.npmignore @@ -0,0 +1,2 @@ +/node_modules +/src diff --git a/packages/plugins/@nocobase/plugin-action-duplicate/README.md b/packages/plugins/@nocobase/plugin-action-duplicate/README.md new file mode 100644 index 000000000..9ef1e61ef --- /dev/null +++ b/packages/plugins/@nocobase/plugin-action-duplicate/README.md @@ -0,0 +1 @@ +# @nocobase/plugin-action-duplicate diff --git a/packages/plugins/@nocobase/plugin-action-duplicate/client.d.ts b/packages/plugins/@nocobase/plugin-action-duplicate/client.d.ts new file mode 100644 index 000000000..6c459cbac --- /dev/null +++ b/packages/plugins/@nocobase/plugin-action-duplicate/client.d.ts @@ -0,0 +1,2 @@ +export * from './dist/client'; +export { default } from './dist/client'; diff --git a/packages/plugins/@nocobase/plugin-action-duplicate/client.js b/packages/plugins/@nocobase/plugin-action-duplicate/client.js new file mode 100644 index 000000000..b6e3be70e --- /dev/null +++ b/packages/plugins/@nocobase/plugin-action-duplicate/client.js @@ -0,0 +1 @@ +module.exports = require('./dist/client/index.js'); diff --git a/packages/plugins/@nocobase/plugin-action-duplicate/package.json b/packages/plugins/@nocobase/plugin-action-duplicate/package.json new file mode 100644 index 000000000..37a60f045 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-action-duplicate/package.json @@ -0,0 +1,16 @@ +{ + "name": "@nocobase/plugin-action-duplicate", + "version": "0.17.0-alpha.7", + "main": "dist/server/index.js", + "displayName": "Data duplicate", + "displayName.zh-CN": "数据复制", + "description": "Provides deplicate action", + "description.zh-CN": "提供复制数据操作", + "license": "AGPL-3.0", + "dependencies": {}, + "peerDependencies": { + "@nocobase/client": "0.x", + "@nocobase/server": "0.x", + "@nocobase/test": "0.x" + } +} diff --git a/packages/plugins/@nocobase/plugin-action-duplicate/server.d.ts b/packages/plugins/@nocobase/plugin-action-duplicate/server.d.ts new file mode 100644 index 000000000..c41081ddc --- /dev/null +++ b/packages/plugins/@nocobase/plugin-action-duplicate/server.d.ts @@ -0,0 +1,2 @@ +export * from './dist/server'; +export { default } from './dist/server'; diff --git a/packages/plugins/@nocobase/plugin-action-duplicate/server.js b/packages/plugins/@nocobase/plugin-action-duplicate/server.js new file mode 100644 index 000000000..972842039 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-action-duplicate/server.js @@ -0,0 +1 @@ +module.exports = require('./dist/server/index.js'); diff --git a/packages/plugins/@nocobase/plugin-action-duplicate/src/client/DuplicateAction.Settings.tsx b/packages/plugins/@nocobase/plugin-action-duplicate/src/client/DuplicateAction.Settings.tsx new file mode 100644 index 000000000..75d12273a --- /dev/null +++ b/packages/plugins/@nocobase/plugin-action-duplicate/src/client/DuplicateAction.Settings.tsx @@ -0,0 +1,382 @@ +import React, { useCallback, useMemo, useState, useEffect } from 'react'; +import { + SchemaSettings, + ActionDesigner, + useSchemaToolbar, + useDesignable, + SchemaSettingOpenModeSchemaItems, + useCollection, + useRecord, + SchemaSettingsModalItem, + SchemaSettingsLinkageRules, + useCollectionState, + useSyncFromForm, +} from '@nocobase/client'; +import { onFieldInputValueChange } from '@formily/core'; +import { cloneDeep } from 'lodash'; +import { Tree as AntdTree } from 'antd'; +import { useTranslation } from 'react-i18next'; +import { useField, useFieldSchema, ISchema, useForm, connect, mapProps } from '@formily/react'; + +const Tree = connect( + AntdTree, + mapProps((props, field: any) => { + useEffect(() => { + field.value = props.defaultCheckedKeys || []; + }, []); + const [checkedKeys, setCheckedKeys] = useState(props.defaultCheckedKeys || []); + const onCheck = (checkedKeys) => { + setCheckedKeys(checkedKeys); + field.value = checkedKeys; + }; + field.onCheck = onCheck; + const form = useForm(); + return { + ...props, + checkedKeys, + onCheck, + treeData: props?.treeData.map((v: any) => { + if (form.values.duplicateMode === 'quickDulicate') { + const children = v?.children?.map((k) => { + return { + ...k, + disabled: false, + }; + }); + return { + ...v, + disabled: false, + children, + }; + } + return v; + }), + }; + }), +); + +const getAllkeys = (data, result) => { + for (let i = 0; i < data?.length; i++) { + const { children, ...rest } = data[i]; + result.push(rest.key); + if (children) { + getAllkeys(children, result); + } + } + return result; +}; +const findFormBlock = (schema) => { + const formSchema = schema.reduceProperties((_, s) => { + if (s['x-decorator'] === 'FormBlockProvider') { + return s; + } else { + return findFormBlock(s); + } + }, null); + return formSchema; +}; + +function DuplicationMode() { + const { dn } = useDesignable(); + const { t } = useTranslation(); + const field = useField(); + const fieldSchema = useFieldSchema(); + const { name } = useCollection(); + const { collectionList, getEnableFieldTree, getOnLoadData, getOnCheck } = useCollectionState(name); + const duplicateValues = cloneDeep(fieldSchema['x-component-props'].duplicateFields || []); + const record = useRecord(); + const syncCallBack = useCallback((treeData, selectFields, form) => { + form.query('duplicateFields').take((f) => { + f.componentProps.treeData = treeData; + f.componentProps.defaultCheckedKeys = selectFields; + f.setInitialValue(selectFields); + f?.onCheck(selectFields); + form.setValues({ ...form.values, treeData }); + }); + }, []); + const useSelectAllFields = (form) => { + return { + async run() { + form.query('duplicateFields').take((f) => { + const selectFields = getAllkeys(f.componentProps.treeData, []); + f.componentProps.defaultCheckedKeys = selectFields; + f.setInitialValue(selectFields); + f?.onCheck?.(selectFields); + }); + }, + }; + }; + const useUnSelectAllFields = (form) => { + return { + async run() { + form.query('duplicateFields').take((f) => { + f.componentProps.defaultCheckedKeys = []; + f.setInitialValue([]); + f?.onCheck([]); + }); + }, + }; + }; + return ( + 1 ? 'visible' : 'hidden', + 'x-decorator': 'FormItem', + 'x-component': 'Select', + 'x-component-props': { + options: collectionList, + }, + 'x-reactions': [ + { + dependencies: ['.duplicateMode'], + fulfill: { + state: { + disabled: `{{ $deps[0]==="quickDulicate" }}`, + value: `{{ $deps[0]==="quickDulicate"? currentCollection:collectionName }}`, + }, + }, + }, + ], + }, + syncFromForm: { + type: 'void', + title: '{{ t("Sync from form fields") }}', + 'x-component': 'Action.Link', + 'x-component-props': { + type: 'primary', + style: { float: 'right', position: 'relative', zIndex: 1200 }, + useAction: () => { + const formSchema = useMemo(() => findFormBlock(fieldSchema), [fieldSchema]); + return useSyncFromForm( + formSchema, + fieldSchema['x-component-props']?.duplicateCollection || record?.__collection || name, + syncCallBack, + ); + }, + }, + 'x-reactions': [ + { + dependencies: ['.duplicateMode'], + fulfill: { + state: { + visible: `{{ $deps[0]!=="quickDulicate" }}`, + }, + }, + }, + ], + }, + selectAll: { + type: 'void', + title: '{{ t("Select all") }}', + 'x-component': 'Action.Link', + 'x-reactions': [ + { + dependencies: ['.duplicateMode'], + fulfill: { + state: { + visible: `{{ $deps[0]==="quickDulicate"}}`, + }, + }, + }, + ], + 'x-component-props': { + type: 'primary', + style: { float: 'right', position: 'relative', zIndex: 1200 }, + useAction: () => { + const from = useForm(); + return useSelectAllFields(from); + }, + }, + }, + unselectAll: { + type: 'void', + title: '{{ t("UnSelect all") }}', + 'x-component': 'Action.Link', + 'x-reactions': [ + { + dependencies: ['.duplicateMode', '.duplicateFields'], + fulfill: { + state: { + visible: `{{ $deps[0]==="quickDulicate"&&$form.getValuesIn('duplicateFields').length>0 }}`, + }, + }, + }, + ], + 'x-component-props': { + type: 'primary', + style: { float: 'right', position: 'relative', zIndex: 1200, marginRight: '10px' }, + useAction: () => { + const from = useForm(); + return useUnSelectAllFields(from); + }, + }, + }, + duplicateFields: { + type: 'array', + title: '{{ t("Data fields") }}', + required: true, + description: t('Only the selected fields will be used as the initialization data for the form'), + 'x-decorator': 'FormItem', + 'x-component': Tree, + 'x-component-props': { + defaultCheckedKeys: duplicateValues, + treeData: [], + checkable: true, + checkStrictly: true, + selectable: false, + loadData: '{{ getOnLoadData($self) }}', + onCheck: '{{ getOnCheck($self) }}', + rootStyle: { + padding: '8px 0', + border: '1px solid #d9d9d9', + borderRadius: '2px', + maxHeight: '30vh', + overflow: 'auto', + margin: '2px 0', + }, + }, + 'x-reactions': [ + { + dependencies: ['.collection', '.duplicateMode'], + fulfill: { + state: { + disabled: '{{ !$deps[0] }}', + componentProps: { + treeData: '{{ getEnableFieldTree($deps[0], $self,treeData) }}', + }, + }, + }, + }, + ], + }, + }, + } as ISchema + } + onSubmit={({ duplicateMode, collection, duplicateFields, treeData }) => { + const fields = Array.isArray(duplicateFields) ? duplicateFields : duplicateFields.checked || []; + field.componentProps.duplicateMode = duplicateMode; + field.componentProps.duplicateFields = fields; + fieldSchema['x-component-props'] = fieldSchema['x-component-props'] || {}; + fieldSchema['x-component-props'].duplicateMode = duplicateMode; + fieldSchema['x-component-props'].duplicateFields = fields; + fieldSchema['x-component-props'].duplicateCollection = collection; + fieldSchema['x-component-props'].treeData = treeData || field.componentProps?.treeData; + dn.emit('patch', { + schema: { + ['x-uid']: fieldSchema['x-uid'], + 'x-component-props': { + ...fieldSchema['x-component-props'], + }, + }, + }); + dn.refresh(); + }} + /> + ); +} + +const duplicateActionSettings = new SchemaSettings({ + name: 'ActionSettings:duplicate', + items: [ + { + name: 'Customize', + Component: (props): any => { + return props.children; + }, + children: [ + { + name: 'editButton', + Component: ActionDesigner.ButtonEditor, + useComponentProps() { + const { buttonEditorProps } = useSchemaToolbar(); + return buttonEditorProps; + }, + }, + { + name: 'linkageRules', + Component: SchemaSettingsLinkageRules, + useComponentProps() { + const { name } = useCollection(); + const { linkageRulesProps } = useSchemaToolbar(); + return { + ...linkageRulesProps, + collectionName: name, + }; + }, + }, + { + name: 'duplicationMode', + Component: DuplicationMode, + useVisible() { + const fieldSchema = useFieldSchema(); + const isDuplicateAction = fieldSchema['x-action'] === 'duplicate'; + return isDuplicateAction; + }, + }, + { + name: 'openMode', + Component: SchemaSettingOpenModeSchemaItems, + useComponentProps() { + const fieldSchema = useFieldSchema(); + const isPopupAction = [ + 'create', + 'update', + 'view', + 'customize:popup', + 'duplicate', + 'customize:create', + ].includes(fieldSchema['x-action'] || ''); + + return { + openMode: isPopupAction, + openSize: isPopupAction, + }; + }, + }, + { + name: 'remove', + sort: 100, + Component: ActionDesigner.RemoveButton as any, + useComponentProps() { + const { removeButtonProps } = useSchemaToolbar(); + return removeButtonProps; + }, + }, + ], + }, + ], +}); + +export { duplicateActionSettings }; diff --git a/packages/core/client/src/schema-initializer/components/DuplicateAction.tsx b/packages/plugins/@nocobase/plugin-action-duplicate/src/client/DuplicateAction.tsx similarity index 79% rename from packages/core/client/src/schema-initializer/components/DuplicateAction.tsx rename to packages/plugins/@nocobase/plugin-action-duplicate/src/client/DuplicateAction.tsx index 8df82d579..f3db77948 100644 --- a/packages/core/client/src/schema-initializer/components/DuplicateAction.tsx +++ b/packages/plugins/@nocobase/plugin-action-duplicate/src/client/DuplicateAction.tsx @@ -1,7 +1,7 @@ import { css, cx } from '@emotion/css'; import { RecursionField, observer, useField, useFieldSchema } from '@formily/react'; import { App, Button } from 'antd'; -import React, { createContext, useContext, useState } from 'react'; +import React, { useState } from 'react'; import { useTranslation } from 'react-i18next'; import { ActionContextProvider, @@ -14,15 +14,50 @@ import { useCollectionManager, useDesignable, useRecord, -} from '../../'; -import { fetchTemplateData } from '../../schema-component/antd/form-v2/Templates'; -import { actionDesignerCss } from './CreateRecordAction'; + fetchTemplateData, + FormBlockContext, + useFormBlockContext, +} from '@nocobase/client'; -const DuplicatefieldsContext = createContext(null); - -export const useDuplicatefieldsContext = () => { - return useContext(DuplicatefieldsContext); -}; +export const actionDesignerCss = css` + position: relative; + &:hover { + .general-schema-designer { + display: block; + } + } + .general-schema-designer { + position: absolute; + z-index: 999; + top: 0; + bottom: 0; + left: 0; + right: 0; + display: none; + background: var(--colorBgSettingsHover); + border: 0; + top: 0; + bottom: 0; + left: 0; + right: 0; + pointer-events: none; + > .general-schema-designer-icons { + position: absolute; + right: 2px; + top: 2px; + line-height: 16px; + pointer-events: all; + .ant-space-item { + background-color: var(--colorSettings); + color: #fff; + line-height: 16px; + width: 16px; + padding-left: 1px; + align-self: stretch; + } + } + } +`; export const DuplicateAction = observer( (props: any) => { @@ -44,6 +79,7 @@ export const DuplicateAction = observer( const { getCollectionFields } = useCollectionManager(); const { t } = useTranslation(); const collectionFields = getCollectionFields(__collection || name); + const formctx = useFormBlockContext(); const template = { key: 'duplicate', dataId: id, @@ -103,11 +139,14 @@ export const DuplicateAction = observer( `]: isLinkBtn, })} > -
@@ -147,7 +186,7 @@ export const DuplicateAction = observer(
-
+ ); }, diff --git a/packages/core/client/src/schema-initializer/items/DuplicateActionInitializer.tsx b/packages/plugins/@nocobase/plugin-action-duplicate/src/client/DuplicateActionInitializer.tsx similarity index 96% rename from packages/core/client/src/schema-initializer/items/DuplicateActionInitializer.tsx rename to packages/plugins/@nocobase/plugin-action-duplicate/src/client/DuplicateActionInitializer.tsx index 6dcbf805c..f53176db0 100644 --- a/packages/core/client/src/schema-initializer/items/DuplicateActionInitializer.tsx +++ b/packages/plugins/@nocobase/plugin-action-duplicate/src/client/DuplicateActionInitializer.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { ActionInitializer } from './ActionInitializer'; +import { ActionInitializer } from '@nocobase/client'; export const DuplicateActionInitializer = (props) => { const schema = { diff --git a/packages/plugins/@nocobase/plugin-action-duplicate/src/client/DuplicatePluginProvider.tsx b/packages/plugins/@nocobase/plugin-action-duplicate/src/client/DuplicatePluginProvider.tsx new file mode 100644 index 000000000..1d6f717ce --- /dev/null +++ b/packages/plugins/@nocobase/plugin-action-duplicate/src/client/DuplicatePluginProvider.tsx @@ -0,0 +1,12 @@ +import { SchemaComponentOptions } from '@nocobase/client'; +import React from 'react'; +import { DuplicateActionInitializer } from './DuplicateActionInitializer'; +import { DuplicateAction } from './DuplicateAction'; + +export const DuplicatePluginProvider = (props: any) => { + return ( + + {props.children} + + ); +}; diff --git a/packages/plugins/@nocobase/plugin-action-duplicate/src/client/__e2e__/schemaInitailizer.test.ts b/packages/plugins/@nocobase/plugin-action-duplicate/src/client/__e2e__/schemaInitailizer.test.ts new file mode 100644 index 000000000..c0417f324 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-action-duplicate/src/client/__e2e__/schemaInitailizer.test.ts @@ -0,0 +1,36 @@ +import { + expect, + test, + oneEmptyDetailsBlock, + oneEmptyTableBlockWithCustomizeActions, + oneEmptyTableBlockWithActions, +} from '@nocobase/test/client'; + +test.describe('TableActionColumnInitializers & DetailsActionInitializers & ReadPrettyFormActionInitializers should add duplication action', () => { + test('duplication action in TableActionColumnInitializers', async ({ page, mockPage, mockRecords }) => { + const nocoPage = await mockPage(oneEmptyTableBlockWithCustomizeActions).waitForInit(); + await mockRecords('general', 3); + await nocoPage.goto(); + await page.getByRole('button', { name: 'Actions' }).hover(); + await page.getByLabel('designer-schema-settings-TableV2.Column-TableV2.ActionColumnDesigner-general').hover(); + await page.getByRole('menuitem', { name: 'Duplicate' }).click(); + await expect(await page.getByLabel('action-Action.Link-Duplicate-duplicate-general-table-0')).toBeVisible(); + }); + test('duplication action in DetailsActionInitializers', async ({ page, mockPage, mockCollections, mockRecords }) => { + await mockPage(oneEmptyDetailsBlock).goto(); + await page.getByLabel('schema-initializer-ActionBar-DetailsActionInitializers-general').click(); + await page.getByRole('menuitem', { name: 'Duplicate' }).click(); + await expect(page.getByLabel('action-Action-Duplicate-duplicate-general-details')).toBeVisible(); + }); + test('duplicate action in ReadPrettyFormActionInitializers', async ({ page, mockPage, mockRecord }) => { + const nocoPage = await mockPage(oneEmptyTableBlockWithActions).waitForInit(); + await mockRecord('general'); + await nocoPage.goto(); + await page.getByLabel('view').click(); + await page.getByLabel('schema-initializer-Grid-RecordBlockInitializers-general').hover(); + await page.getByRole('menuitem', { name: 'form Details' }).click(); + await page.getByLabel('schema-initializer-ActionBar-ReadPrettyFormActionInitializers-general').hover(); + await page.getByRole('menuitem', { name: 'Duplicate' }).click(); + await expect(page.getByLabel('action-Action-Duplicate-duplicate-general-form')).toBeVisible(); + }); +}); diff --git a/packages/plugins/@nocobase/plugin-action-duplicate/src/client/__e2e__/schemaSettings.test.ts b/packages/plugins/@nocobase/plugin-action-duplicate/src/client/__e2e__/schemaSettings.test.ts new file mode 100644 index 000000000..58003efa8 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-action-duplicate/src/client/__e2e__/schemaSettings.test.ts @@ -0,0 +1,92 @@ +import { expect, test } from '@nocobase/test/client'; +import { oneEmptyTableBlockWithDuplicateActions } from './utils'; + +test.describe('direct duplicate & copy into the form and continue to fill in', () => { + test('direct duplicate', async ({ page, mockPage, mockCollections, mockRecords }) => { + const nocoPage = await mockPage(oneEmptyTableBlockWithDuplicateActions).waitForInit(); + const data = await mockRecords('general', 3); + await nocoPage.goto(); + await expect(await page.getByLabel('action-Action.Link-Duplicate-duplicate-general-table-0')).toBeVisible(); + await page.getByLabel('action-Action.Link-Duplicate-duplicate-general-table-0').hover(); + await page.getByRole('button', { name: 'designer-schema-settings-Action.Link-Action.Designer-general' }).click(); + await page.getByRole('menuitem', { name: 'Duplicate mode' }).click(); + await page.getByLabel('Direct duplicate').check(); + await page.getByRole('button', { name: 'singleLineText (Duplicate)' }).click(); + await page.getByRole('button', { name: 'OK', exact: true }).click(); + const [request] = await Promise.all([ + page.waitForRequest((request) => request.url().includes('api/general:create')), + page.getByLabel('action-Action.Link-Duplicate-duplicate-general-table-0').click(), + ]); + const postData = request.postDataJSON(); + //直接复制,复制的数据符合预期 + expect(postData.singleLineText).toEqual(data[0].singleLineText); + }); + + test('copy into the form and continue to fill in', async ({ page, mockPage, mockRecord }) => { + const nocoPage = await mockPage(oneEmptyTableBlockWithDuplicateActions).waitForInit(); + const data = await mockRecord('general'); + await nocoPage.goto(); + await expect(await page.getByLabel('action-Action.Link-Duplicate-duplicate-general-table-0')).toBeVisible(); + await page.getByLabel('action-Action.Link-Duplicate-duplicate-general-table-0').hover(); + await page.getByRole('button', { name: 'designer-schema-settings-Action.Link-Action.Designer-general' }).hover(); + await page.getByRole('menuitem', { name: 'Duplicate mode' }).click(); + await page.getByLabel('Copy into the form and continue to fill in').check(); + await page.getByRole('button', { name: 'singleLineText (Duplicate)' }).click(); + await page.getByRole('button', { name: 'OK' }).click(); + await page.getByLabel('action-Action.Link-Duplicate-duplicate-general-table-0').click(); + await page.getByLabel('schema-initializer-Grid-CreateFormBlockInitializers-general').hover(); + //配置表单区块 + await page.getByRole('menuitem', { name: 'form Form' }).click(); + await page.mouse.move(300, 0); + await page.getByLabel('schema-initializer-Grid-FormItemInitializers-general').hover(); + await page.getByRole('menuitem', { name: 'singleLineText' }).click(); + await page.getByRole('menuitem', { name: 'oneToOneBelongsTo' }).click(); + await page.getByRole('menuitem', { name: 'oneToOneHasOne' }).click(); + await page.getByRole('menuitem', { name: 'oneToMany' }).click(); + await page.getByRole('menuitem', { name: 'manyToOne', exact: true }).click(); + await page.getByRole('menuitem', { name: 'manyToMany' }).click(); + await page.getByLabel('schema-initializer-ActionBar-CreateFormActionInitializers-general').click(); + await page.getByRole('menuitem', { name: 'Submit' }).click(); + await page.getByLabel('drawer-Action.Container-general-Duplicate-mask').click(); + //同步表单字段 + await page.getByLabel('action-Action.Link-Duplicate-duplicate-general-table-0').hover(); + await page.getByRole('button', { name: 'designer-schema-settings-Action.Link-Action.Designer-general' }).hover(); + await page.getByRole('menuitem', { name: 'Duplicate mode' }).click(); + await page.getByLabel('action-Action.Link-Sync from form fields-general').click(); + //表单中的字段自动选中,hasOne 和 hasMany 的关系字段只能是复制,belongsTo 和 belongsToMany 是引用 + //select关系字段组件只适用于引用关系 + const defaultCheckedNodesText = await page.evaluate(() => { + const defaultCheckedNodes = Array.from(document.querySelectorAll('.ant-tree-treenode-checkbox-checked')); + return defaultCheckedNodes.map((node) => node.textContent.trim()); + }); + const expectedArray = [ + 'manyToMany (Reference)', + 'singleLineText (Duplicate)', + 'oneToOneBelongsTo (Reference)', + 'manyToOne (Reference)', + ]; + //选中的字段符合预期 + expect(expectedArray).toEqual(expect.arrayContaining(defaultCheckedNodesText)); + await page.getByRole('button', { name: 'OK' }).click(); + + await page.getByLabel('action-Action.Link-Duplicate-duplicate-general-table-0').click(); + await expect( + await page + .getByLabel('block-item-CollectionField-general-form-general.singleLineText') + .getByRole('textbox') + .inputValue(), + ).toBe(data['singleLineText']); + const [request] = await Promise.all([ + page.waitForRequest((request) => request.url().includes('api/general:create')), + page.getByLabel('action-Action-Submit-submit-general-form').click(), + ]); + const postData = request.postDataJSON(); + const manyToMany = postData.manyToMany.map((v) => v.id); + const expectManyToMany = data.manyToMany.map((v) => v.id); + //提交的数据符合预期 + expect(postData.singleLineText).toEqual(data.singleLineText); + expect(postData.manyToOne['id']).toBe(data.manyToOne['id']); + expect(postData.oneToOneBelongsTo['id']).toBe(data.oneToOneBelongsTo['id']); + expect(manyToMany).toEqual(expect.arrayContaining(expectManyToMany)); + }); +}); diff --git a/packages/plugins/@nocobase/plugin-action-duplicate/src/client/__e2e__/utils.ts b/packages/plugins/@nocobase/plugin-action-duplicate/src/client/__e2e__/utils.ts new file mode 100644 index 000000000..3dcd71846 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-action-duplicate/src/client/__e2e__/utils.ts @@ -0,0 +1,531 @@ +import { CollectionSetting, PageConfig, general } from '@nocobase/test/client'; + +/** + * 1. 创建一个名为 general 的 collection,其包含 时间、Percent 类型的字段 + */ +export const generalWithDatetimeFields: CollectionSetting[] = [ + { + name: 'general', + title: 'General', + fields: [ + { + name: 'singleLineText', + interface: 'input', + uiSchema: { + type: 'string', + 'x-component': 'Input', + title: 'Single line text', + }, + }, + { + name: 'startDatetime', + interface: 'datetime', + uiSchema: { + type: 'string', + 'x-component': 'DatePicker', + title: 'Start date time', + required: true, + }, + }, + { + name: 'endDatetime', + interface: 'datetime', + uiSchema: { + type: 'string', + 'x-component': 'DatePicker', + title: 'End date time', + required: true, + }, + }, + { + name: 'f_t22o7loai3j', + interface: 'integer', + isForeignKey: true, + uiSchema: { + type: 'number', + title: 'f_t22o7loai3j', + 'x-component': 'InputNumber', + 'x-read-pretty': true, + }, + }, + { + name: 'manyToOne', + interface: 'm2o', + foreignKey: 'f_t22o7loai3j', + uiSchema: { + 'x-component': 'AssociationField', + 'x-component-props': { + multiple: false, + fieldNames: { + label: 'id', + value: 'id', + }, + }, + title: 'Many to one', + }, + target: 'users', + targetKey: 'id', + }, + { + name: 'percent', + type: 'float', + interface: 'percent', + uiSchema: { + 'x-component-props': { + step: '0.01', + stringMode: true, + addonAfter: '%', + }, + 'x-component': 'Percent', + title: 'Percent', + }, + }, + ], + }, +]; + +export const oneEmptyGantt: PageConfig = { + collections: generalWithDatetimeFields, + pageSchema: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Page', + properties: { + jebhzap4dzi: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid', + 'x-initializer': 'BlockInitializers', + properties: { + gl57m4hyewf: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Row', + properties: { + jm7n5dybw6t: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Col', + properties: { + nsq0rdemz4i: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-acl-action': 'general:list', + 'x-decorator': 'GanttBlockProvider', + 'x-decorator-props': { + collection: 'general', + resource: 'general', + action: 'list', + fieldNames: { + id: 'id', + start: 'startDatetime', + range: 'day', + title: 'singleLineText', + end: 'endDatetime', + }, + params: { + paginate: false, + }, + }, + 'x-designer': 'Gantt.Designer', + 'x-component': 'CardItem', + properties: { + zf07g7relim: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Gantt', + 'x-component-props': { + useProps: '{{ useGanttBlockProps }}', + }, + properties: { + toolBar: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'ActionBar', + 'x-component-props': { + style: { + marginBottom: 24, + }, + }, + 'x-initializer': 'GanttActionInitializers', + 'x-uid': 'guwovmwt4c0', + 'x-async': false, + 'x-index': 1, + }, + table: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'array', + 'x-decorator': 'div', + 'x-decorator-props': { + style: { + float: 'left', + maxWidth: '35%', + }, + }, + 'x-initializer': 'TableColumnInitializers', + 'x-component': 'TableV2', + 'x-component-props': { + rowKey: 'id', + rowSelection: { + type: 'checkbox', + }, + useProps: '{{ useTableBlockProps }}', + pagination: false, + }, + properties: { + actions: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + title: '{{ t("Actions") }}', + 'x-action-column': 'actions', + 'x-decorator': 'TableV2.Column.ActionBar', + 'x-component': 'TableV2.Column', + 'x-designer': 'TableV2.ActionColumnDesigner', + 'x-initializer': 'TableActionColumnInitializers', + properties: { + actions: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-decorator': 'DndContext', + 'x-component': 'Space', + 'x-component-props': { + split: '|', + }, + 'x-uid': '9in7s3pymsd', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'rgpbsjwvq2h', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'g7qwompxaeo', + 'x-async': false, + 'x-index': 2, + }, + detail: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Gantt.Event', + properties: { + drawer: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Action.Drawer', + 'x-component-props': { + className: 'nb-action-popup', + }, + title: '{{ t("View record") }}', + properties: { + tabs: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Tabs', + 'x-component-props': {}, + 'x-initializer': 'TabPaneInitializers', + properties: { + tab1: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + title: '{{t("Details")}}', + 'x-component': 'Tabs.TabPane', + 'x-designer': 'Tabs.Designer', + 'x-component-props': {}, + properties: { + grid: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid', + 'x-initializer': 'RecordBlockInitializers', + 'x-uid': 'gxtfjqzxbfu', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'zxwwx4358p1', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '1p0tnmzpsim', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'hcue8v3fwti', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'fg7cjxu4hxp', + 'x-async': false, + 'x-index': 3, + }, + }, + 'x-uid': 'rf47sf7k16z', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'tritqukkd86', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '3am2ctuhyka', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'p8pesbc7fiv', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'ddm9fhkvrbw', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'vpk8sp3eap1', + 'x-async': true, + 'x-index': 1, + }, +}; + +/** + * 页面中有一个空的 Table 区块,并且有这些按钮:Add new / Delete / Refresh / Add record / Filter / view / edit / delete / duplicate + */ +export const oneEmptyTableBlockWithDuplicateActions: PageConfig = { + collections: general, + pageSchema: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Page', + 'x-index': 1, + properties: { + '1lqiou007g2': { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid', + 'x-initializer': 'BlockInitializers', + 'x-index': 1, + properties: { + '1m4gz110aaw': { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Row', + properties: { + '695oy51236d': { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Col', + properties: { + twtgsvrdmn1: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-decorator': 'TableBlockProvider', + 'x-acl-action': 'general:list', + 'x-decorator-props': { + collection: 'general', + resource: 'general', + action: 'list', + params: { + pageSize: 20, + }, + rowKey: 'id', + showIndex: true, + dragSort: false, + disableTemplate: false, + }, + 'x-designer': 'TableBlockDesigner', + 'x-component': 'CardItem', + 'x-filter-targets': [], + properties: { + actions: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-initializer': 'TableActionInitializers', + 'x-component': 'ActionBar', + 'x-component-props': { + style: { + marginBottom: 'var(--nb-spacing)', + }, + }, + properties: {}, + 'x-uid': 'znrsshrlsna', + 'x-async': false, + 'x-index': 1, + }, + '1xnl1d9j48o': { + _isJSONSchemaObject: true, + version: '2.0', + type: 'array', + 'x-initializer': 'TableColumnInitializers', + 'x-component': 'TableV2', + 'x-component-props': { + rowKey: 'id', + rowSelection: { + type: 'checkbox', + }, + useProps: '{{ useTableBlockProps }}', + }, + properties: { + actions: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + title: '{{ t("Actions") }}', + 'x-action-column': 'actions', + 'x-decorator': 'TableV2.Column.ActionBar', + 'x-component': 'TableV2.Column', + 'x-designer': 'TableV2.ActionColumnDesigner', + 'x-initializer': 'TableActionColumnInitializers', + properties: { + actions: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-decorator': 'DndContext', + 'x-component': 'Space', + 'x-component-props': { + split: '|', + }, + properties: { + '659x6w2yydk': { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-action': 'duplicate', + 'x-acl-action': 'create', + title: '{{ t("Duplicate") }}', + 'x-designer': 'Action.Designer', + 'x-component': 'Action.Link', + 'x-decorator': 'ACLActionProvider', + 'x-component-props': { + openMode: 'drawer', + component: 'DuplicateAction', + }, + 'x-designer-props': { + linkageAction: true, + }, + properties: { + drawer: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + title: '{{ t("Duplicate") }}', + 'x-component': 'Action.Container', + 'x-component-props': { + className: 'nb-action-popup', + }, + properties: { + tabs: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Tabs', + 'x-component-props': {}, + 'x-initializer': 'TabPaneInitializers', + properties: { + tab1: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + title: '{{t("Duplicate")}}', + 'x-component': 'Tabs.TabPane', + 'x-designer': 'Tabs.Designer', + 'x-component-props': {}, + properties: { + grid: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid', + 'x-initializer': 'CreateFormBlockInitializers', + 'x-uid': 'vtcnkzcaeec', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'kbq4w0dmexr', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '1v4k1kjpbi5', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'ok9iw50ycdh', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'sjg3udjdnc1', + 'x-async': false, + 'x-index': 4, + }, + }, + 'x-uid': 'ijgo5usyzbp', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '5tnwpodzirq', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'k8t01z9qna3', + 'x-async': false, + 'x-index': 2, + }, + }, + 'x-uid': 'c29q4s49svw', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '9pe6fpnq33f', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'j6g551r7tbp', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'mbw5vw7y3ea', + 'x-async': false, + }, + }, + 'x-uid': '4mbt7m7in1l', + 'x-async': true, + }, +}; diff --git a/packages/plugins/@nocobase/plugin-action-duplicate/src/client/index.ts b/packages/plugins/@nocobase/plugin-action-duplicate/src/client/index.ts new file mode 100644 index 000000000..60dc283fc --- /dev/null +++ b/packages/plugins/@nocobase/plugin-action-duplicate/src/client/index.ts @@ -0,0 +1,64 @@ +import { Plugin, useCollection } from '@nocobase/client'; +import { DuplicatePluginProvider } from './DuplicatePluginProvider'; +import { duplicateActionSettings } from './DuplicateAction.Settings'; + +export class DuplicatePlugin extends Plugin { + async load() { + this.app.use(DuplicatePluginProvider); + this.app.schemaSettingsManager.add(duplicateActionSettings); + + const initializerData = { + title: '{{t("Duplicate")}}', + Component: 'DuplicateActionInitializer', + schema: { + 'x-component': 'Action', + 'x-action': 'duplicate', + 'x-decorator': 'ACLActionProvider', + 'x-component-props': { + type: 'primary', + }, + }, + useVisible() { + const collection = useCollection(); + return ( + (collection.template !== 'view' || collection?.writableView) && + collection.template !== 'file' && + collection.template !== 'sql' + ); + }, + }; + + const initializerTableData = { + title: '{{t("Duplicate")}}', + Component: 'DuplicateActionInitializer', + schema: { + 'x-component': 'Action.Link', + 'x-action': 'duplicate', + 'x-designer': 'Action.Designer', + 'x-settings': 'ActionSettings:duplicate', + 'x-decorator': 'ACLActionProvider', + 'x-component-props': { + type: 'primary', + }, + }, + useVisible() { + const collection = useCollection(); + return ( + (collection.template !== 'view' || collection?.writableView) && + collection.template !== 'file' && + collection.template !== 'sql' + ); + }, + }; + + const tableActionColumnInitializers = this.app.schemaInitializerManager.get('TableActionColumnInitializers'); + tableActionColumnInitializers?.add('actions.duplicate', initializerTableData); + const detailsActionInitializers = this.app.schemaInitializerManager.get('DetailsActionInitializers'); + detailsActionInitializers?.add('enableActions.duplicate', initializerData); + const ReadPrettyFormActionInitializers = this.app.schemaInitializerManager.get('ReadPrettyFormActionInitializers'); + ReadPrettyFormActionInitializers?.add('enableActions.duplicate', initializerData); + } +} + +export default DuplicatePlugin; +export * from './DuplicateAction'; diff --git a/packages/plugins/@nocobase/plugin-action-duplicate/src/index.ts b/packages/plugins/@nocobase/plugin-action-duplicate/src/index.ts new file mode 100644 index 000000000..7e74612df --- /dev/null +++ b/packages/plugins/@nocobase/plugin-action-duplicate/src/index.ts @@ -0,0 +1,2 @@ +export * from './server'; +export { default } from './server'; diff --git a/packages/plugins/@nocobase/plugin-action-duplicate/src/server/collections/.gitkeep b/packages/plugins/@nocobase/plugin-action-duplicate/src/server/collections/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/packages/plugins/@nocobase/plugin-action-duplicate/src/server/index.ts b/packages/plugins/@nocobase/plugin-action-duplicate/src/server/index.ts new file mode 100644 index 000000000..b68aea57f --- /dev/null +++ b/packages/plugins/@nocobase/plugin-action-duplicate/src/server/index.ts @@ -0,0 +1 @@ +export { default } from './plugin'; diff --git a/packages/plugins/@nocobase/plugin-action-duplicate/src/server/plugin.ts b/packages/plugins/@nocobase/plugin-action-duplicate/src/server/plugin.ts new file mode 100644 index 000000000..683a87443 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-action-duplicate/src/server/plugin.ts @@ -0,0 +1,19 @@ +import { InstallOptions, Plugin } from '@nocobase/server'; + +export class PluginActionDuplicateServer extends Plugin { + afterAdd() {} + + beforeLoad() {} + + async load() {} + + async install(options?: InstallOptions) {} + + async afterEnable() {} + + async afterDisable() {} + + async remove() {} +} + +export default PluginActionDuplicateServer; diff --git a/packages/plugins/@nocobase/plugin-action-print/.npmignore b/packages/plugins/@nocobase/plugin-action-print/.npmignore new file mode 100644 index 000000000..65f5e8779 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-action-print/.npmignore @@ -0,0 +1,2 @@ +/node_modules +/src diff --git a/packages/plugins/@nocobase/plugin-action-print/README.md b/packages/plugins/@nocobase/plugin-action-print/README.md new file mode 100644 index 000000000..f41b59882 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-action-print/README.md @@ -0,0 +1 @@ +# @nocobase/plugin-action-print diff --git a/packages/plugins/@nocobase/plugin-action-print/client.d.ts b/packages/plugins/@nocobase/plugin-action-print/client.d.ts new file mode 100644 index 000000000..6c459cbac --- /dev/null +++ b/packages/plugins/@nocobase/plugin-action-print/client.d.ts @@ -0,0 +1,2 @@ +export * from './dist/client'; +export { default } from './dist/client'; diff --git a/packages/plugins/@nocobase/plugin-action-print/client.js b/packages/plugins/@nocobase/plugin-action-print/client.js new file mode 100644 index 000000000..b6e3be70e --- /dev/null +++ b/packages/plugins/@nocobase/plugin-action-print/client.js @@ -0,0 +1 @@ +module.exports = require('./dist/client/index.js'); diff --git a/packages/plugins/@nocobase/plugin-action-print/package.json b/packages/plugins/@nocobase/plugin-action-print/package.json new file mode 100644 index 000000000..4eb42bb6f --- /dev/null +++ b/packages/plugins/@nocobase/plugin-action-print/package.json @@ -0,0 +1,16 @@ +{ + "name": "@nocobase/plugin-action-print", + "version": "0.17.0-alpha.7", + "main": "dist/server/index.js", + "displayName": "Print", + "displayName.zh-CN": "打印操作", + "description": "Provides print action", + "description.zh-CN": "提供打印操作", + "license": "AGPL-3.0", + "dependencies": {}, + "peerDependencies": { + "@nocobase/client": "0.x", + "@nocobase/server": "0.x", + "@nocobase/test": "0.x" + } +} diff --git a/packages/plugins/@nocobase/plugin-action-print/server.d.ts b/packages/plugins/@nocobase/plugin-action-print/server.d.ts new file mode 100644 index 000000000..c41081ddc --- /dev/null +++ b/packages/plugins/@nocobase/plugin-action-print/server.d.ts @@ -0,0 +1,2 @@ +export * from './dist/server'; +export { default } from './dist/server'; diff --git a/packages/plugins/@nocobase/plugin-action-print/server.js b/packages/plugins/@nocobase/plugin-action-print/server.js new file mode 100644 index 000000000..972842039 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-action-print/server.js @@ -0,0 +1 @@ +module.exports = require('./dist/server/index.js'); diff --git a/packages/plugins/@nocobase/plugin-action-print/src/client/PrintAction.Settings.tsx b/packages/plugins/@nocobase/plugin-action-print/src/client/PrintAction.Settings.tsx new file mode 100644 index 000000000..41eb82d82 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-action-print/src/client/PrintAction.Settings.tsx @@ -0,0 +1,54 @@ +import { + SchemaSettings, + ActionDesigner, + useSchemaToolbar, + SchemaSettingsLinkageRules, + useLinkageAction, + useCollection, +} from '@nocobase/client'; + +const printActionSettings = new SchemaSettings({ + name: 'ActionSettings:print', + items: [ + { + name: 'Customize', + Component: (props): any => { + return props.children; + }, + children: [ + { + name: 'editButton', + Component: ActionDesigner.ButtonEditor, + useComponentProps() { + const { buttonEditorProps } = useSchemaToolbar(); + return buttonEditorProps; + }, + }, + { + name: 'linkageRules', + Component: SchemaSettingsLinkageRules, + useComponentProps() { + const { name } = useCollection(); + const { linkageRulesProps } = useSchemaToolbar(); + return { + ...linkageRulesProps, + collectionName: name, + }; + }, + }, + + { + name: 'remove', + sort: 100, + Component: ActionDesigner.RemoveButton as any, + useComponentProps() { + const { removeButtonProps } = useSchemaToolbar(); + return removeButtonProps; + }, + }, + ], + }, + ], +}); + +export { printActionSettings }; diff --git a/packages/core/client/src/schema-initializer/items/PrintActionInitializer.tsx b/packages/plugins/@nocobase/plugin-action-print/src/client/PrintActionInitializer.tsx similarity index 87% rename from packages/core/client/src/schema-initializer/items/PrintActionInitializer.tsx rename to packages/plugins/@nocobase/plugin-action-print/src/client/PrintActionInitializer.tsx index 06c779c92..d50eb3fb1 100644 --- a/packages/core/client/src/schema-initializer/items/PrintActionInitializer.tsx +++ b/packages/plugins/@nocobase/plugin-action-print/src/client/PrintActionInitializer.tsx @@ -1,6 +1,6 @@ import React from 'react'; -import { ActionInitializer } from './ActionInitializer'; +import { ActionInitializer } from '@nocobase/client'; export const PrintActionInitializer = (props) => { const schema = { diff --git a/packages/plugins/@nocobase/plugin-action-print/src/client/PrintActionPluginProvider.tsx b/packages/plugins/@nocobase/plugin-action-print/src/client/PrintActionPluginProvider.tsx new file mode 100644 index 000000000..0c9b8de49 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-action-print/src/client/PrintActionPluginProvider.tsx @@ -0,0 +1,12 @@ +import { SchemaComponentOptions } from '@nocobase/client'; +import React from 'react'; +import { PrintActionInitializer } from './PrintActionInitializer'; +import { useDetailPrintActionProps } from './utils'; + +export const PrintActionPluginProvider = (props: any) => { + return ( + + {props.children} + + ); +}; diff --git a/packages/plugins/@nocobase/plugin-action-print/src/client/__e2e__/schemaInitailizer.test.ts b/packages/plugins/@nocobase/plugin-action-print/src/client/__e2e__/schemaInitailizer.test.ts new file mode 100644 index 000000000..a0a26621f --- /dev/null +++ b/packages/plugins/@nocobase/plugin-action-print/src/client/__e2e__/schemaInitailizer.test.ts @@ -0,0 +1,24 @@ +import { expect, test } from '@nocobase/test/client'; +import { oneCalenderWithViewAction, oneTableWithViewAction } from './utils'; + +test.describe('ReadPrettyFormActionInitializers & CalendarFormActionInitializers should add print action', () => { + test('print action in ReadPrettyFormActionInitializers', async ({ page, mockPage, mockRecord }) => { + const nocoPage = await mockPage(oneTableWithViewAction).waitForInit(); + await mockRecord('general'); + await nocoPage.goto(); + await page.getByLabel('block-item-CardItem-general-table').hover(); + await page.getByLabel('action-Action.Link-View-view-general').click(); + await page.getByLabel('schema-initializer-ActionBar-ReadPrettyFormActionInitializers-general').hover(); + await page.getByRole('menuitem', { name: 'Print' }).click(); + await expect(page.getByLabel('action-Action-Print-print-general-form')).toBeVisible(); + }); + test('print action in CalendarFormActionInitializers', async ({ page, mockPage, mockRecord }) => { + const nocoPage = await mockPage(oneCalenderWithViewAction).waitForInit(); + await mockRecord('general', { singleLineText: 'test' }); + await nocoPage.goto(); + await page.getByTitle('test').click(); + await page.getByLabel('schema-initializer-ActionBar-CalendarFormActionInitializers-general').hover(); + await page.getByRole('menuitem', { name: 'Print' }).click(); + await page.getByLabel('action-Action-Print-print-general-form').click(); + }); +}); diff --git a/packages/plugins/@nocobase/plugin-action-print/src/client/__e2e__/utils.ts b/packages/plugins/@nocobase/plugin-action-print/src/client/__e2e__/utils.ts new file mode 100644 index 000000000..26c5f3b89 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-action-print/src/client/__e2e__/utils.ts @@ -0,0 +1,940 @@ +import { CollectionSetting, PageConfig } from '@nocobase/test/client'; + +/** + * 1. 创建一个名为 general 的 collection,其包含 时间、Percent 类型的字段 + */ +export const generalWithDatetimeFields: CollectionSetting[] = [ + { + name: 'general', + title: 'General', + fields: [ + { + name: 'singleLineText', + interface: 'input', + uiSchema: { + type: 'string', + 'x-component': 'Input', + title: 'Single line text', + }, + }, + { + name: 'startDatetime', + interface: 'datetime', + uiSchema: { + type: 'string', + 'x-component': 'DatePicker', + title: 'Start date time', + required: true, + }, + }, + { + name: 'endDatetime', + interface: 'datetime', + uiSchema: { + type: 'string', + 'x-component': 'DatePicker', + title: 'End date time', + required: true, + }, + }, + { + name: 'f_t22o7loai3j', + interface: 'integer', + isForeignKey: true, + uiSchema: { + type: 'number', + title: 'f_t22o7loai3j', + 'x-component': 'InputNumber', + 'x-read-pretty': true, + }, + }, + { + name: 'manyToOne', + interface: 'm2o', + foreignKey: 'f_t22o7loai3j', + uiSchema: { + 'x-component': 'AssociationField', + 'x-component-props': { + multiple: false, + fieldNames: { + label: 'id', + value: 'id', + }, + }, + title: 'Many to one', + }, + target: 'users', + targetKey: 'id', + }, + { + name: 'percent', + type: 'float', + interface: 'percent', + uiSchema: { + 'x-component-props': { + step: '0.01', + stringMode: true, + addonAfter: '%', + }, + 'x-component': 'Percent', + title: 'Percent', + }, + }, + ], + }, +]; + +export const oneTableWithViewAction: PageConfig = { + collections: generalWithDatetimeFields, + pageSchema: { + type: 'void', + version: '2.0', + 'x-component': 'Page', + _isJSONSchemaObject: true, + properties: { + xieizx8p4lf: { + type: 'void', + version: '2.0', + 'x-component': 'Grid', + 'x-initializer': 'BlockInitializers', + _isJSONSchemaObject: true, + properties: { + '8xj4v8eg792': { + type: 'void', + version: '2.0', + 'x-component': 'Grid.Row', + _isJSONSchemaObject: true, + properties: { + ghuh9k5lciy: { + type: 'void', + version: '2.0', + 'x-component': 'Grid.Col', + _isJSONSchemaObject: true, + properties: { + xes7knealgh: { + type: 'void', + version: '2.0', + 'x-designer': 'TableBlockDesigner', + 'x-component': 'CardItem', + 'x-decorator': 'TableBlockProvider', + 'x-acl-action': 'general:list', + 'x-filter-targets': [], + 'x-decorator-props': { + action: 'list', + params: { + pageSize: 20, + }, + rowKey: 'id', + dragSort: false, + resource: 'general', + showIndex: true, + collection: 'general', + disableTemplate: false, + }, + _isJSONSchemaObject: true, + properties: { + actions: { + type: 'void', + version: '2.0', + 'x-component': 'ActionBar', + 'x-initializer': 'TableActionInitializers', + 'x-component-props': { + style: { + marginBottom: 'var(--nb-spacing)', + }, + }, + _isJSONSchemaObject: true, + 'x-uid': '7yd6j32gi3t', + 'x-async': false, + 'x-index': 1, + }, + ivgkbdl5qsl: { + type: 'array', + version: '2.0', + 'x-component': 'TableV2', + 'x-initializer': 'TableColumnInitializers', + 'x-component-props': { + rowKey: 'id', + useProps: '{{ useTableBlockProps }}', + rowSelection: { + type: 'checkbox', + }, + }, + _isJSONSchemaObject: true, + properties: { + actions: { + type: 'void', + title: '{{ t("Actions") }}', + version: '2.0', + 'x-designer': 'TableV2.ActionColumnDesigner', + 'x-component': 'TableV2.Column', + 'x-decorator': 'TableV2.Column.ActionBar', + 'x-initializer': 'TableActionColumnInitializers', + 'x-action-column': 'actions', + _isJSONSchemaObject: true, + properties: { + actions: { + type: 'void', + version: '2.0', + 'x-component': 'Space', + 'x-decorator': 'DndContext', + 'x-component-props': { + split: '|', + }, + _isJSONSchemaObject: true, + properties: { + yo5cicv109t: { + type: 'void', + title: '{{ t("View") }}', + version: '2.0', + 'x-action': 'view', + 'x-designer': 'Action.Designer', + 'x-component': 'Action.Link', + 'x-decorator': 'ACLActionProvider', + 'x-designer-props': { + linkageAction: true, + }, + 'x-component-props': { + openMode: 'drawer', + }, + _isJSONSchemaObject: true, + properties: { + drawer: { + type: 'void', + title: '{{ t("View record") }}', + version: '2.0', + 'x-component': 'Action.Container', + 'x-component-props': { + className: 'nb-action-popup', + }, + _isJSONSchemaObject: true, + properties: { + tabs: { + type: 'void', + version: '2.0', + 'x-component': 'Tabs', + 'x-initializer': 'TabPaneInitializers', + 'x-component-props': {}, + _isJSONSchemaObject: true, + properties: { + tab1: { + type: 'void', + title: '{{t("Details")}}', + version: '2.0', + 'x-designer': 'Tabs.Designer', + 'x-component': 'Tabs.TabPane', + 'x-component-props': {}, + _isJSONSchemaObject: true, + properties: { + grid: { + type: 'void', + version: '2.0', + 'x-component': 'Grid', + 'x-initializer': 'RecordBlockInitializers', + _isJSONSchemaObject: true, + properties: { + v9jpx7749ok: { + type: 'void', + version: '2.0', + 'x-component': 'Grid.Row', + _isJSONSchemaObject: true, + properties: { + pniqykhsw7y: { + type: 'void', + version: '2.0', + 'x-component': 'Grid.Col', + _isJSONSchemaObject: true, + properties: { + wr8q6b5nv1m: { + type: 'void', + version: '2.0', + 'x-designer': 'FormV2.ReadPrettyDesigner', + 'x-component': 'CardItem', + 'x-decorator': 'FormBlockProvider', + 'x-acl-action': 'general:get', + 'x-decorator-props': { + action: 'get', + resource: 'general', + useParams: '{{ useParamsFromRecord }}', + collection: 'general', + readPretty: true, + useSourceId: '{{ useSourceIdFromParentRecord }}', + }, + _isJSONSchemaObject: true, + properties: { + '3kr5dxwjtg6': { + type: 'void', + version: '2.0', + 'x-component': 'FormV2', + 'x-read-pretty': true, + 'x-component-props': { + useProps: '{{ useFormBlockProps }}', + }, + _isJSONSchemaObject: true, + properties: { + actions: { + type: 'void', + version: '2.0', + 'x-component': 'ActionBar', + 'x-initializer': + 'ReadPrettyFormActionInitializers', + 'x-component-props': { + style: { + marginBottom: 24, + }, + }, + _isJSONSchemaObject: true, + 'x-uid': 'xfagozpna5f', + 'x-async': false, + 'x-index': 1, + }, + grid: { + type: 'void', + version: '2.0', + 'x-component': 'Grid', + 'x-initializer': + 'ReadPrettyFormItemInitializers', + _isJSONSchemaObject: true, + 'x-uid': 'kum0xpsh9se', + 'x-async': false, + 'x-index': 2, + }, + }, + 'x-uid': 'wfh52ibwf0m', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'yucndoepwjp', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'srns2fk2bm5', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'bl004rxhzxo', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'lbkw6c33nev', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'z0cz3f3w2ls', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'h7fj3lyjgcp', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'vnb6jm1n4mr', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'ts9jr505zwp', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '0odqbqq1w1b', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'ik6vmhvlwq0', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'zi8p6p4yv7n', + 'x-async': false, + 'x-index': 2, + }, + }, + 'x-uid': 'axnj0ck85xs', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '5ryow4jpdv8', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'tutp0qeuczt', + 'x-async': false, + 'x-index': 1, + }, + }, + }, + }, + }, +}; + +export const oneCalenderWithViewAction: PageConfig = { + collections: generalWithDatetimeFields, + pageSchema: { + type: 'void', + version: '2.0', + 'x-component': 'Page', + _isJSONSchemaObject: true, + properties: { + xieizx8p4lf: { + type: 'void', + version: '2.0', + 'x-component': 'Grid', + 'x-initializer': 'BlockInitializers', + _isJSONSchemaObject: true, + properties: { + '8er4x7qwc0p': { + type: 'void', + version: '2.0', + 'x-component': 'Grid.Row', + _isJSONSchemaObject: true, + properties: { + ihsa0qvrlz0: { + type: 'void', + version: '2.0', + 'x-component': 'Grid.Col', + _isJSONSchemaObject: true, + properties: { + '2tocmd26pct': { + type: 'void', + version: '2.0', + 'x-designer': 'CalendarV2.Designer', + 'x-component': 'CardItem', + 'x-decorator': 'CalendarBlockProvider', + 'x-acl-action': 'general:list', + 'x-decorator-props': { + action: 'list', + params: { + paginate: false, + }, + resource: 'general', + collection: 'general', + fieldNames: { + id: 'id', + end: ['endDateTime'], + start: ['startDateTime'], + title: 'singleLineText', + }, + }, + _isJSONSchemaObject: true, + properties: { + ac13dxxbtdx: { + type: 'void', + version: '2.0', + 'x-component': 'CalendarV2', + 'x-component-props': { + useProps: '{{ useCalendarBlockProps }}', + }, + _isJSONSchemaObject: true, + properties: { + toolBar: { + type: 'void', + version: '2.0', + 'x-component': 'CalendarV2.ActionBar', + 'x-initializer': 'CalendarActionInitializers', + 'x-component-props': { + style: { + marginBottom: 24, + }, + }, + _isJSONSchemaObject: true, + properties: { + '6gz1f7kcy9f': { + type: 'void', + title: "{{t('Add new')}}", + version: '2.0', + 'x-align': 'right', + 'x-action': 'create', + 'x-designer': 'Action.Designer', + 'x-component': 'Action', + 'x-decorator': 'ACLActionProvider', + 'x-acl-action': 'create', + 'x-component-props': { + icon: 'PlusOutlined', + type: 'primary', + openMode: 'drawer', + component: 'CreateRecordAction', + }, + 'x-acl-action-props': { + skipScopeCheck: true, + }, + _isJSONSchemaObject: true, + properties: { + drawer: { + type: 'void', + title: '{{ t("Add record") }}', + version: '2.0', + 'x-component': 'Action.Container', + 'x-component-props': { + className: 'nb-action-popup', + }, + _isJSONSchemaObject: true, + properties: { + tabs: { + type: 'void', + version: '2.0', + 'x-component': 'Tabs', + 'x-initializer': 'TabPaneInitializersForCreateFormBlock', + 'x-component-props': {}, + _isJSONSchemaObject: true, + properties: { + tab1: { + type: 'void', + title: '{{t("Add new")}}', + version: '2.0', + 'x-designer': 'Tabs.Designer', + 'x-component': 'Tabs.TabPane', + 'x-component-props': {}, + _isJSONSchemaObject: true, + properties: { + grid: { + type: 'void', + version: '2.0', + 'x-component': 'Grid', + 'x-initializer': 'CreateFormBlockInitializers', + _isJSONSchemaObject: true, + properties: { + '7e9xtqdm96x': { + type: 'void', + version: '2.0', + 'x-component': 'Grid.Row', + _isJSONSchemaObject: true, + properties: { + '49cc4rzlix3': { + type: 'void', + version: '2.0', + 'x-component': 'Grid.Col', + _isJSONSchemaObject: true, + properties: { + aqlgjvkrcx7: { + type: 'void', + version: '2.0', + 'x-designer': 'FormV2.Designer', + 'x-component': 'CardItem', + 'x-decorator': 'FormBlockProvider', + 'x-acl-action': 'general:create', + 'x-component-props': {}, + 'x-decorator-props': { + resource: 'general', + collection: 'general', + }, + 'x-acl-action-props': { + skipScopeCheck: true, + }, + _isJSONSchemaObject: true, + properties: { + e9hvag8g8z1: { + type: 'void', + version: '2.0', + 'x-component': 'FormV2', + 'x-component-props': { + useProps: '{{ useFormBlockProps }}', + }, + _isJSONSchemaObject: true, + properties: { + grid: { + type: 'void', + version: '2.0', + 'x-component': 'Grid', + 'x-initializer': 'FormItemInitializers', + _isJSONSchemaObject: true, + properties: { + ikh538659hb: { + type: 'void', + version: '2.0', + 'x-component': 'Grid.Row', + _isJSONSchemaObject: true, + properties: { + n0gqcx2an2n: { + type: 'void', + version: '2.0', + 'x-component': 'Grid.Col', + _isJSONSchemaObject: true, + properties: { + f_gsdltgajej4: { + type: 'string', + version: '2.0', + 'x-designer': 'FormItem.Designer', + 'x-component': 'CollectionField', + 'x-decorator': 'FormItem', + 'x-component-props': {}, + 'x-collection-field': + 'general.f_gsdltgajej4', + _isJSONSchemaObject: true, + 'x-uid': 'oa2zgpva04m', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'zj8cfg6yn8l', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'pjalhrecpln', + 'x-async': false, + 'x-index': 1, + }, + axdppstkbuy: { + type: 'void', + version: '2.0', + 'x-component': 'Grid.Row', + _isJSONSchemaObject: true, + properties: { + gwjxqr45unr: { + type: 'void', + version: '2.0', + 'x-component': 'Grid.Col', + _isJSONSchemaObject: true, + properties: { + f_3sjr0e6uvdv: { + type: 'string', + version: '2.0', + 'x-designer': 'FormItem.Designer', + 'x-component': 'CollectionField', + 'x-decorator': 'FormItem', + 'x-component-props': {}, + 'x-collection-field': + 'general.f_3sjr0e6uvdv', + _isJSONSchemaObject: true, + 'x-uid': 'w32tknasgsz', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'p33boqlbg5g', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'm2uf9lc6gnk', + 'x-async': false, + 'x-index': 2, + }, + '2cv7fqwte5j': { + type: 'void', + version: '2.0', + 'x-component': 'Grid.Row', + _isJSONSchemaObject: true, + properties: { + '33bbe57si0a': { + type: 'void', + version: '2.0', + 'x-component': 'Grid.Col', + _isJSONSchemaObject: true, + properties: { + f_fxzp8hq7x3g: { + type: 'string', + version: '2.0', + 'x-designer': 'FormItem.Designer', + 'x-component': 'CollectionField', + 'x-decorator': 'FormItem', + 'x-component-props': {}, + 'x-collection-field': + 'general.f_fxzp8hq7x3g', + _isJSONSchemaObject: true, + 'x-uid': '85pbklphbhd', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'adjbay0axfl', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '8exc4ivguxv', + 'x-async': false, + 'x-index': 3, + }, + }, + 'x-uid': 'u0jmxwugzrx', + 'x-async': false, + 'x-index': 1, + }, + actions: { + type: 'void', + version: '2.0', + 'x-component': 'ActionBar', + 'x-initializer': 'CreateFormActionInitializers', + 'x-component-props': { + style: { + marginTop: 24, + }, + layout: 'one-column', + }, + _isJSONSchemaObject: true, + properties: { + qnx55tt3wat: { + type: 'void', + title: '{{ t("Submit") }}', + version: '2.0', + 'x-action': 'submit', + 'x-designer': 'Action.Designer', + 'x-component': 'Action', + 'x-action-settings': { + triggerWorkflows: [], + }, + 'x-component-props': { + type: 'primary', + htmlType: 'submit', + useProps: '{{ useCreateActionProps }}', + }, + _isJSONSchemaObject: true, + 'x-uid': '264pfxox9kd', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'r6vw3madgav', + 'x-async': false, + 'x-index': 2, + }, + }, + 'x-uid': '0zblewekh2c', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '93lpozbj1vl', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'b8jlo8hoild', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'jw54aewoz9w', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'q4cx3qzdfs1', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'gkxz1m2j6tf', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '1siw8ntx5hc', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '7v0kc5p1qgz', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'eql58c5e8gy', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '4vbgl2z1230', + 'x-async': false, + 'x-index': 1, + }, + event: { + type: 'void', + version: '2.0', + 'x-component': 'CalendarV2.Event', + _isJSONSchemaObject: true, + properties: { + drawer: { + type: 'void', + title: '{{ t("View record") }}', + version: '2.0', + 'x-component': 'Action.Drawer', + 'x-component-props': { + className: 'nb-action-popup', + }, + _isJSONSchemaObject: true, + properties: { + tabs: { + type: 'void', + version: '2.0', + 'x-component': 'Tabs', + 'x-initializer': 'TabPaneInitializers', + 'x-component-props': {}, + _isJSONSchemaObject: true, + 'x-initializer-props': { + gridInitializer: 'RecordBlockInitializers', + }, + properties: { + tab1: { + type: 'void', + title: '{{t("Details")}}', + version: '2.0', + 'x-designer': 'Tabs.Designer', + 'x-component': 'Tabs.TabPane', + 'x-component-props': {}, + _isJSONSchemaObject: true, + properties: { + grid: { + type: 'void', + version: '2.0', + 'x-component': 'Grid', + 'x-initializer': 'RecordBlockInitializers', + _isJSONSchemaObject: true, + 'x-initializer-props': { + actionInitializers: 'CalendarFormActionInitializers', + }, + properties: { + '0j7rb0bivys': { + type: 'void', + version: '2.0', + 'x-component': 'Grid.Row', + _isJSONSchemaObject: true, + properties: { + o4f9ukr5ddb: { + type: 'void', + version: '2.0', + 'x-component': 'Grid.Col', + _isJSONSchemaObject: true, + properties: { + '8ek51sk98tr': { + type: 'void', + version: '2.0', + 'x-designer': 'FormV2.ReadPrettyDesigner', + 'x-component': 'CardItem', + 'x-decorator': 'FormBlockProvider', + 'x-acl-action': 'general:get', + 'x-decorator-props': { + action: 'get', + resource: 'general', + useParams: '{{ useParamsFromRecord }}', + collection: 'general', + readPretty: true, + useSourceId: '{{ useSourceIdFromParentRecord }}', + }, + _isJSONSchemaObject: true, + properties: { + k3af8hdt47o: { + type: 'void', + version: '2.0', + 'x-component': 'FormV2', + 'x-read-pretty': true, + 'x-component-props': { + useProps: '{{ useFormBlockProps }}', + }, + _isJSONSchemaObject: true, + properties: { + actions: { + type: 'void', + version: '2.0', + 'x-component': 'ActionBar', + 'x-initializer': 'CalendarFormActionInitializers', + 'x-component-props': { + style: { + marginBottom: 24, + }, + }, + _isJSONSchemaObject: true, + 'x-uid': 'qlwu7qv0in8', + 'x-async': false, + 'x-index': 1, + }, + grid: { + type: 'void', + version: '2.0', + 'x-component': 'Grid', + 'x-initializer': 'ReadPrettyFormItemInitializers', + _isJSONSchemaObject: true, + 'x-uid': 'br7bq2ddccy', + 'x-async': false, + 'x-index': 2, + }, + }, + 'x-uid': 'el0x7lhmzhh', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'fel6h5vudp2', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'f5zkbrpb0jw', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 's5neszd0kjk', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'qzrhtapf6w8', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '7w618khbjpg', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '9xkbplyqfwg', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '7zn54xk2fim', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '8bgjrafg6be', + 'x-async': false, + 'x-index': 2, + }, + }, + 'x-uid': 'z2venf40j0t', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '6w8yaucqe97', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'zxbujzse5o0', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'd6sohpn2e5u', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'h3e5hz7qv5p', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'a0wcr2f03wj', + 'x-async': true, + 'x-index': 1, + }, +}; diff --git a/packages/plugins/@nocobase/plugin-action-print/src/client/index.tsx b/packages/plugins/@nocobase/plugin-action-print/src/client/index.tsx new file mode 100644 index 000000000..06e8fe7c0 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-action-print/src/client/index.tsx @@ -0,0 +1,28 @@ +import { Plugin } from '@nocobase/client'; +import { PrintActionPluginProvider } from './PrintActionPluginProvider'; +import { printActionSettings } from './PrintAction.Settings'; +export class PrintPlugin extends Plugin { + async load() { + this.app.use(PrintActionPluginProvider); + this.app.schemaSettingsManager.add(printActionSettings); + + const initializerData = { + title: '{{t("Print")}}', + Component: 'PrintActionInitializer', + schema: { + 'x-component': 'Action', + 'x-settings': 'ActionSettings:print', + 'x-action': 'print', + }, + }; + + this.app.schemaInitializerManager.addItem( + 'ReadPrettyFormActionInitializers', + 'enableActions.print', + initializerData, + ); + this.app.schemaInitializerManager.addItem('CalendarFormActionInitializers', 'enableActions.print', initializerData); + } +} + +export default PrintPlugin; diff --git a/packages/plugins/@nocobase/plugin-action-print/src/client/utils.ts b/packages/plugins/@nocobase/plugin-action-print/src/client/utils.ts new file mode 100644 index 000000000..23785a01e --- /dev/null +++ b/packages/plugins/@nocobase/plugin-action-print/src/client/utils.ts @@ -0,0 +1,23 @@ +import { useFormBlockContext } from '@nocobase/client'; +import { useReactToPrint } from 'react-to-print'; + +export const useDetailPrintActionProps = () => { + const { formBlockRef } = useFormBlockContext(); + + const printHandler = useReactToPrint({ + content: () => formBlockRef.current, + pageStyle: `@media print { + * { + margin: 0; + } + :not(.ant-formily-item-control-content-component) > div.ant-formily-layout>div:first-child { + overflow: hidden; height: 0; + } + }`, + }); + return { + async onClick() { + printHandler(); + }, + }; +}; diff --git a/packages/plugins/@nocobase/plugin-action-print/src/index.ts b/packages/plugins/@nocobase/plugin-action-print/src/index.ts new file mode 100644 index 000000000..7e74612df --- /dev/null +++ b/packages/plugins/@nocobase/plugin-action-print/src/index.ts @@ -0,0 +1,2 @@ +export * from './server'; +export { default } from './server'; diff --git a/packages/plugins/@nocobase/plugin-action-print/src/server/collections/.gitkeep b/packages/plugins/@nocobase/plugin-action-print/src/server/collections/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/packages/plugins/@nocobase/plugin-action-print/src/server/index.ts b/packages/plugins/@nocobase/plugin-action-print/src/server/index.ts new file mode 100644 index 000000000..b68aea57f --- /dev/null +++ b/packages/plugins/@nocobase/plugin-action-print/src/server/index.ts @@ -0,0 +1 @@ +export { default } from './plugin'; diff --git a/packages/plugins/@nocobase/plugin-action-print/src/server/plugin.ts b/packages/plugins/@nocobase/plugin-action-print/src/server/plugin.ts new file mode 100644 index 000000000..e16415b66 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-action-print/src/server/plugin.ts @@ -0,0 +1,19 @@ +import { InstallOptions, Plugin } from '@nocobase/server'; + +export class PluginActionPrintServer extends Plugin { + afterAdd() {} + + beforeLoad() {} + + async load() {} + + async install(options?: InstallOptions) {} + + async afterEnable() {} + + async afterDisable() {} + + async remove() {} +} + +export default PluginActionPrintServer; diff --git a/packages/plugins/@nocobase/plugin-collection-manager/src/client/__e2e__/schema-settings/form-item/details-single-data/relation.test.ts b/packages/plugins/@nocobase/plugin-collection-manager/src/client/__e2e__/schema-settings/form-item/details-single-data/relation.test.ts index 58464d788..9f2fb7c05 100644 --- a/packages/plugins/@nocobase/plugin-collection-manager/src/client/__e2e__/schema-settings/form-item/details-single-data/relation.test.ts +++ b/packages/plugins/@nocobase/plugin-collection-manager/src/client/__e2e__/schema-settings/form-item/details-single-data/relation.test.ts @@ -31,7 +31,7 @@ test.describe('many to many', () => { }); test('enable link', async ({ page, mockPage, mockRecords }) => { - test.fail(); + // test.fail(); const record = await gotoPage(mockPage, mockRecords); await openDialog(page); diff --git a/packages/plugins/@nocobase/plugin-export/src/client/index.ts b/packages/plugins/@nocobase/plugin-export/src/client/index.ts index c6593de89..13c5cc75f 100644 --- a/packages/plugins/@nocobase/plugin-export/src/client/index.ts +++ b/packages/plugins/@nocobase/plugin-export/src/client/index.ts @@ -23,9 +23,7 @@ export class ExportPlugin extends Plugin { const tableActionInitializers = this.app.schemaInitializerManager.get('TableActionInitializers'); tableActionInitializers?.add('enableActions.export', initializerData); - - const ganttActionInitializers = this.app.schemaInitializerManager.get('GanttActionInitializers'); - ganttActionInitializers?.add('enableActions.export', initializerData); + this.app.schemaInitializerManager.addItem('GanttActionInitializers', 'enableActions.export', initializerData); } } diff --git a/packages/plugins/@nocobase/plugin-gantt/.npmignore b/packages/plugins/@nocobase/plugin-gantt/.npmignore new file mode 100644 index 000000000..65f5e8779 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-gantt/.npmignore @@ -0,0 +1,2 @@ +/node_modules +/src diff --git a/packages/plugins/@nocobase/plugin-gantt/README.md b/packages/plugins/@nocobase/plugin-gantt/README.md new file mode 100644 index 000000000..849ea6026 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-gantt/README.md @@ -0,0 +1 @@ +# @nocobase/plugin-gantt diff --git a/packages/plugins/@nocobase/plugin-gantt/client.d.ts b/packages/plugins/@nocobase/plugin-gantt/client.d.ts new file mode 100644 index 000000000..6c459cbac --- /dev/null +++ b/packages/plugins/@nocobase/plugin-gantt/client.d.ts @@ -0,0 +1,2 @@ +export * from './dist/client'; +export { default } from './dist/client'; diff --git a/packages/plugins/@nocobase/plugin-gantt/client.js b/packages/plugins/@nocobase/plugin-gantt/client.js new file mode 100644 index 000000000..b6e3be70e --- /dev/null +++ b/packages/plugins/@nocobase/plugin-gantt/client.js @@ -0,0 +1 @@ +module.exports = require('./dist/client/index.js'); diff --git a/packages/plugins/@nocobase/plugin-gantt/package.json b/packages/plugins/@nocobase/plugin-gantt/package.json new file mode 100644 index 000000000..040f39342 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-gantt/package.json @@ -0,0 +1,18 @@ +{ + "name": "@nocobase/plugin-gantt", + "version": "0.17.0-alpha.7", + "displayName": "Gantt", + "displayName.zh-CN": "甘特图", + "description": "Provides Gantt blocks", + "description.zh-CN": "提供甘特图区块", + "license": "AGPL-3.0", + "main": "dist/server/index.js", + "dependencies": { + "antd-style": "3.4.5" + }, + "peerDependencies": { + "@nocobase/client": "0.x", + "@nocobase/server": "0.x", + "@nocobase/test": "0.x" + } +} diff --git a/packages/plugins/@nocobase/plugin-gantt/server.d.ts b/packages/plugins/@nocobase/plugin-gantt/server.d.ts new file mode 100644 index 000000000..c41081ddc --- /dev/null +++ b/packages/plugins/@nocobase/plugin-gantt/server.d.ts @@ -0,0 +1,2 @@ +export * from './dist/server'; +export { default } from './dist/server'; diff --git a/packages/plugins/@nocobase/plugin-gantt/server.js b/packages/plugins/@nocobase/plugin-gantt/server.js new file mode 100644 index 000000000..972842039 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-gantt/server.js @@ -0,0 +1 @@ +module.exports = require('./dist/server/index.js'); diff --git a/packages/plugins/@nocobase/plugin-gantt/src/client/Gantt.Designer.tsx b/packages/plugins/@nocobase/plugin-gantt/src/client/Gantt.Designer.tsx new file mode 100644 index 000000000..8f5cbb32b --- /dev/null +++ b/packages/plugins/@nocobase/plugin-gantt/src/client/Gantt.Designer.tsx @@ -0,0 +1,14 @@ +import React from 'react'; +import { useCollection, GeneralSchemaDesigner, useSchemaTemplate } from '@nocobase/client'; +export const GanttDesigner = () => { + const { name, title } = useCollection(); + const template = useSchemaTemplate(); + + return ( + + ); +}; diff --git a/packages/plugins/@nocobase/plugin-gantt/src/client/Gantt.Settings.tsx b/packages/plugins/@nocobase/plugin-gantt/src/client/Gantt.Settings.tsx new file mode 100644 index 000000000..4db7dffb7 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-gantt/src/client/Gantt.Settings.tsx @@ -0,0 +1,255 @@ +import { useField, useFieldSchema } from '@formily/react'; +import { + useFormBlockContext, + useCollection, + SchemaSettings, + useDesignable, + removeNullCondition, + useCompile, + SchemaSettingsBlockTitleItem, + SchemaSettingsSelectItem, + SchemaSettingsDataScope, + SchemaSettingsTemplate, +} from '@nocobase/client'; +import { useGanttTranslation, useOptions } from './utils'; +import { useGanttBlockContext } from './GanttBlockProvider'; + +export const ganttSettings = new SchemaSettings({ + name: 'GanttBlockSettings', + items: [ + { + name: 'title', + Component: SchemaSettingsBlockTitleItem, + }, + { + name: 'titleField', + Component: SchemaSettingsSelectItem, + useComponentProps() { + const { t } = useGanttTranslation(); + const fieldSchema = useFieldSchema(); + const fieldNames = fieldSchema?.['x-decorator-props']?.['fieldNames'] || {}; + const { service } = useGanttBlockContext(); + const field = useField(); + const { dn } = useDesignable(); + return { + title: t('Title field'), + value: fieldNames.title, + options: useOptions('string'), + onChange: (title) => { + const fieldNames = field.decoratorProps.fieldNames || {}; + fieldNames['title'] = title; + field.decoratorProps.params = fieldNames; + fieldSchema['x-decorator-props']['params'] = fieldNames; + // Select切换option后value未按照预期切换,固增加以下代码 + fieldSchema['x-decorator-props']['fieldNames'] = fieldNames; + service.refresh(); + dn.emit('patch', { + schema: { + ['x-uid']: fieldSchema['x-uid'], + 'x-decorator-props': field.decoratorProps, + }, + }); + dn.refresh(); + }, + }; + }, + }, + { + name: 'timeScale', + Component: SchemaSettingsSelectItem, + useComponentProps() { + const { t } = useGanttTranslation(); + const fieldSchema = useFieldSchema(); + const fieldNames = fieldSchema?.['x-decorator-props']?.['fieldNames'] || {}; + const field = useField(); + const { service } = useGanttBlockContext(); + const { dn } = useDesignable(); + const compile = useCompile(); + return { + title: t('Time scale'), + value: fieldNames.range || 'day', + options: [ + { label: compile('{{t("Hour")}}'), value: 'hour', color: 'orange' }, + { label: compile('{{t("Quarter of day")}}'), value: 'quarterDay', color: 'default' }, + { label: compile('{{t("Half of day")}}'), value: 'halfDay', color: 'blue' }, + { label: compile('{{t("Day")}}'), value: 'day', color: 'yellow' }, + { label: compile('{{t("Week")}}'), value: 'week', color: 'pule' }, + { label: compile('{{t("Month")}}'), value: 'month', color: 'green' }, + { label: compile('{{t("QuarterYear")}}'), value: 'quarterYear', color: 'red' }, + { label: compile('{{t("Year")}}'), value: 'year', color: 'green' }, + ], + onChange: (range) => { + const fieldNames = field.decoratorProps.fieldNames || {}; + fieldNames['range'] = range; + field.decoratorProps.params = fieldNames; + fieldSchema['x-decorator-props']['params'] = fieldNames; + // Select切换option后value未按照预期切换,固增加以下代码 + fieldSchema['x-decorator-props']['fieldNames'] = fieldNames; + service.refresh(); + dn.emit('patch', { + schema: { + ['x-uid']: fieldSchema['x-uid'], + 'x-decorator-props': field.decoratorProps, + }, + }); + dn.refresh(); + }, + }; + }, + }, + { + name: 'startDateField', + Component: SchemaSettingsSelectItem, + useComponentProps() { + const { t } = useGanttTranslation(); + const fieldSchema = useFieldSchema(); + const fieldNames = fieldSchema?.['x-decorator-props']?.['fieldNames'] || {}; + const field = useField(); + const { dn } = useDesignable(); + const { service } = useGanttBlockContext(); + return { + title: t('Start date field'), + value: fieldNames.start, + options: useOptions('date'), + onChange: (start) => { + const fieldNames = field.decoratorProps.fieldNames || {}; + fieldNames['start'] = start; + field.decoratorProps.fieldNames = fieldNames; + fieldSchema['x-decorator-props']['fieldNames'] = fieldNames; + service.refresh(); + dn.emit('patch', { + schema: { + ['x-uid']: fieldSchema['x-uid'], + 'x-decorator-props': field.decoratorProps, + }, + }); + dn.refresh(); + }, + }; + }, + }, + { + name: 'endDateField', + Component: SchemaSettingsSelectItem, + useComponentProps() { + const { t } = useGanttTranslation(); + const fieldSchema = useFieldSchema(); + const field = useField(); + const { service } = useGanttBlockContext(); + const { dn } = useDesignable(); + const fieldNames = fieldSchema?.['x-decorator-props']?.['fieldNames'] || {}; + return { + title: t('End date field'), + value: fieldNames.end, + options: useOptions('date'), + onChange: (end) => { + const fieldNames = field.decoratorProps.fieldNames || {}; + fieldNames['end'] = end; + field.decoratorProps.fieldNames = fieldNames; + fieldSchema['x-decorator-props']['fieldNames'] = fieldNames; + service.refresh(); + dn.emit('patch', { + schema: { + ['x-uid']: fieldSchema['x-uid'], + 'x-decorator-props': field.decoratorProps, + }, + }); + dn.refresh(); + }, + }; + }, + }, + { + name: 'processField', + Component: SchemaSettingsSelectItem, + useComponentProps() { + const { t } = useGanttTranslation(); + const fieldSchema = useFieldSchema(); + const fieldNames = fieldSchema?.['x-decorator-props']?.['fieldNames'] || {}; + const { service } = useGanttBlockContext(); + const { dn } = useDesignable(); + const field = useField(); + + return { + title: t('Progress field'), + value: fieldNames.progress, + options: useOptions('float'), + onChange: (progress) => { + const fieldNames = field.decoratorProps.fieldNames || {}; + fieldNames['progress'] = progress; + field.decoratorProps.fieldNames = fieldNames; + fieldSchema['x-decorator-props']['fieldNames'] = fieldNames; + service.refresh(); + dn.emit('patch', { + schema: { + ['x-uid']: fieldSchema['x-uid'], + 'x-decorator-props': field.decoratorProps, + }, + }); + dn.refresh(); + }, + }; + }, + }, + { + name: 'dataScope', + Component: SchemaSettingsDataScope, + useComponentProps() { + const { name } = useCollection(); + const fieldSchema = useFieldSchema(); + const { form } = useFormBlockContext(); + const field = useField(); + const { service } = useGanttBlockContext(); + const { dn } = useDesignable(); + return { + collectionName: name, + defaultFilter: fieldSchema?.['x-decorator-props']?.params?.filter || {}, + form, + onSubmit: ({ filter }) => { + filter = removeNullCondition(filter); + 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': fieldSchema['x-decorator-props'], + }, + }); + }, + }; + }, + }, + { + name: 'divider', + type: 'divider', + }, + { + name: 'template', + Component: SchemaSettingsTemplate, + useComponentProps() { + const { name } = useCollection(); + return { + componentName: 'Gantt', + collectionName: name, + }; + }, + }, + { + name: 'divider2', + type: 'divider', + }, + { + name: 'remove', + type: 'remove', + componentProps: { + removeParentsIfNoChildren: true, + breakRemoveOn: { + 'x-component': 'Grid', + }, + }, + }, + ], +}); diff --git a/packages/core/client/src/schema-initializer/buttons/GanttActionInitializers.tsx b/packages/plugins/@nocobase/plugin-gantt/src/client/GanttActionInitializers.tsx similarity index 65% rename from packages/core/client/src/schema-initializer/buttons/GanttActionInitializers.tsx rename to packages/plugins/@nocobase/plugin-gantt/src/client/GanttActionInitializers.tsx index 56a39610f..9eede9781 100644 --- a/packages/core/client/src/schema-initializer/buttons/GanttActionInitializers.tsx +++ b/packages/plugins/@nocobase/plugin-gantt/src/client/GanttActionInitializers.tsx @@ -1,8 +1,7 @@ import { useFieldSchema } from '@formily/react'; -import { useCollection } from '../../'; -import { SchemaInitializer } from '../../application/schema-initializer/SchemaInitializer'; +import { useCollection, SchemaInitializer } from '@nocobase/client'; -export const ganttActionInitializers = new SchemaInitializer({ +export const GanttActionInitializers = new SchemaInitializer({ name: 'GanttActionInitializers', title: "{{t('Configure actions')}}", icon: 'SettingOutlined', @@ -93,52 +92,38 @@ export const ganttActionInitializers = new SchemaInitializer({ name: 'customize', title: '{{t("Customize")}}', children: [ - { - type: 'item', - title: '{{t("Bulk update")}}', - Component: 'CustomizeActionInitializer', - name: 'bulkUpdate', - schema: { - type: 'void', - title: '{{ t("Bulk update") }}', - 'x-component': 'Action', - 'x-align': 'right', - 'x-acl-action': 'update', - 'x-decorator': 'ACLActionProvider', - 'x-acl-action-props': { - skipScopeCheck: true, - }, - 'x-action': 'customize:bulkUpdate', - 'x-designer': 'Action.Designer', - 'x-action-settings': { - assignedValues: {}, - updateMode: 'selected', - onSuccess: { - manualClose: true, - redirecting: false, - successMessage: '{{t("Updated successfully")}}', - }, - }, - 'x-component-props': { - icon: 'EditOutlined', - useProps: '{{ useCustomizeBulkUpdateActionProps }}', - }, - }, - }, - { - type: 'item', - title: '{{t("Bulk edit")}}', - name: 'bulkEdit', - Component: 'CustomizeBulkEditActionInitializer', - schema: { - 'x-align': 'right', - 'x-decorator': 'ACLActionProvider', - 'x-acl-action': 'update', - 'x-acl-action-props': { - skipScopeCheck: true, - }, - }, - }, + // { + // type: 'item', + // title: '{{t("Bulk update")}}', + // Component: 'CustomizeActionInitializer', + // name: 'bulkUpdate', + // schema: { + // type: 'void', + // title: '{{ t("Bulk update") }}', + // 'x-component': 'Action', + // 'x-align': 'right', + // 'x-acl-action': 'update', + // 'x-decorator': 'ACLActionProvider', + // 'x-acl-action-props': { + // skipScopeCheck: true, + // }, + // 'x-action': 'customize:bulkUpdate', + // 'x-designer': 'Action.Designer', + // 'x-action-settings': { + // assignedValues: {}, + // updateMode: 'selected', + // onSuccess: { + // manualClose: true, + // redirecting: false, + // successMessage: '{{t("Updated successfully")}}', + // }, + // }, + // 'x-component-props': { + // icon: 'EditOutlined', + // useProps: '{{ useCustomizeBulkUpdateActionProps }}', + // }, + // }, + // }, { type: 'item', title: '{{t("Add record")}}', diff --git a/packages/core/client/src/schema-initializer/items/GanttBlockInitializer.tsx b/packages/plugins/@nocobase/plugin-gantt/src/client/GanttBlockInitializer.tsx similarity index 91% rename from packages/core/client/src/schema-initializer/items/GanttBlockInitializer.tsx rename to packages/plugins/@nocobase/plugin-gantt/src/client/GanttBlockInitializer.tsx index eb249f50b..d28bd5173 100644 --- a/packages/core/client/src/schema-initializer/items/GanttBlockInitializer.tsx +++ b/packages/plugins/@nocobase/plugin-gantt/src/client/GanttBlockInitializer.tsx @@ -4,12 +4,17 @@ import { SchemaOptionsContext } from '@formily/react'; import React, { useContext } from 'react'; import { useTranslation } from 'react-i18next'; -import { useCollectionManager } from '../../collection-manager'; -import { useGlobalTheme } from '../../global-theme'; -import { FormDialog, SchemaComponent, SchemaComponentOptions } from '../../schema-component'; -import { createGanttBlockSchema } from '../utils'; -import { DataBlockInitializer } from './DataBlockInitializer'; -import { useSchemaInitializer, useSchemaInitializerItem } from '../../application'; +import { + useSchemaInitializer, + useSchemaInitializerItem, + useCollectionManager, + useGlobalTheme, + FormDialog, + SchemaComponent, + DataBlockInitializer, + SchemaComponentOptions, +} from '@nocobase/client'; +import { createGanttBlockSchema } from './utils'; export const GanttBlockInitializer = () => { const { insert } = useSchemaInitializer(); diff --git a/packages/core/client/src/block-provider/GanttBlockProvider.tsx b/packages/plugins/@nocobase/plugin-gantt/src/client/GanttBlockProvider.tsx similarity index 87% rename from packages/core/client/src/block-provider/GanttBlockProvider.tsx rename to packages/plugins/@nocobase/plugin-gantt/src/client/GanttBlockProvider.tsx index 201511b08..9d677b164 100644 --- a/packages/core/client/src/block-provider/GanttBlockProvider.tsx +++ b/packages/plugins/@nocobase/plugin-gantt/src/client/GanttBlockProvider.tsx @@ -1,9 +1,12 @@ import { useField } from '@formily/react'; import React, { createContext, useContext, useEffect, useState } from 'react'; -import { useACLRoleContext } from '../acl/ACLProvider'; -import { useCollection } from '../collection-manager/hooks'; -import { BlockProvider, useBlockRequestContext } from './BlockProvider'; -import { TableBlockProvider } from './TableBlockProvider'; +import { + useACLRoleContext, + useCollection, + BlockProvider, + useBlockRequestContext, + TableBlockProvider, +} from '@nocobase/client'; export const GanttBlockContext = createContext({}); @@ -17,7 +20,7 @@ const formatData = ( ) => { data.forEach((item: any) => { const disable = checkPermassion(item); - const percent = item[fieldNames.progress] * 100; + const percent = parseFloat((item[fieldNames.progress] * 100).toFixed(2)); if (item.children && item.children.length) { tasks.push({ start: new Date(item[fieldNames.start] ?? undefined), @@ -73,11 +76,13 @@ const InternalGanttBlockProvider = (props) => { export const GanttBlockProvider = (props) => { const params = { filter: props.params.filter, tree: true, paginate: false, sort: props.fieldNames.start }; return ( - - - - - +
+ + + + + +
); }; diff --git a/packages/plugins/@nocobase/plugin-gantt/src/client/__e2e__/drag.test.ts b/packages/plugins/@nocobase/plugin-gantt/src/client/__e2e__/drag.test.ts new file mode 100644 index 000000000..2211417b2 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-gantt/src/client/__e2e__/drag.test.ts @@ -0,0 +1,37 @@ +import { expect, test } from '@nocobase/test/client'; +import { oneEmptyGantt } from './utils'; + +test('drag and adjust start time, end time, and progress', async ({ page, mockPage, mockRecord }) => { + const mockData = { + singleLineText: 'within apropos leaker whoever how', + startDatetime: '2023-04-26T11:02:51.129Z', + endDatetime: '2023-06-13T22:11:11.999Z', + percent: 50, + }; + await mockPage(oneEmptyGantt).goto(); + await mockRecord('general', mockData); + await page.getByLabel('block-item-gantt').hover(); + await page.getByLabel('designer-schema-settings-CardItem-Gantt.Designer-general').hover(); + await page.getByRole('menuitem', { name: 'Time scale' }).click(); + await page.getByRole('option', { name: 'Week' }).click(); + await page.getByRole('menuitem', { name: 'Time scale' }).hover(); + await page.mouse.move(300, 0); + await page.getByRole('button', { name: 'Actions' }).click(); + await expect(await page.locator('.calendarBottomText').first().textContent()).toContain('W'); + await page.locator('.bar ').hover(); + const draggableElement = await page.getByLabel('task-bar').getByRole('button').first(); + await draggableElement.hover(); + const { x: initialX, y: initialY } = await draggableElement.boundingBox(); + // 计算目标位置的坐标 + const targetX = initialX + 100; + const targetY = initialY; + await page.mouse.move(initialX, initialY); + await page.mouse.down(); + await page.mouse.move(targetX, targetY); + const [request] = await Promise.all([ + page.waitForRequest((request) => request.url().includes('api/general:update')), + page.mouse.up(), + ]); + const postData = request.postDataJSON(); + expect(postData.startDatetime).not.toEqual(mockData.startDatetime); +}); diff --git a/packages/plugins/@nocobase/plugin-gantt/src/client/__e2e__/schemaInitailizer.test.ts b/packages/plugins/@nocobase/plugin-gantt/src/client/__e2e__/schemaInitailizer.test.ts new file mode 100644 index 000000000..b93113257 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-gantt/src/client/__e2e__/schemaInitailizer.test.ts @@ -0,0 +1,97 @@ +import { expect, test } from '@nocobase/test/client'; +import { generalWithDatetimeFields, oneEmptyGantt } from './utils'; + +test('BlockInitializers should add gantt block', async ({ page, mockPage, mockCollections }) => { + await mockCollections(generalWithDatetimeFields); + await mockPage().goto(); + await page.getByLabel('schema-initializer-Grid-BlockInitializers').click(); + await page.getByRole('menuitem', { name: 'form Gantt right' }).click(); + await page.getByRole('menuitem', { name: 'General' }).click(); + await page.getByLabel('block-item-Select-Title field').click(); + await page.getByRole('option', { name: 'Single line text2' }).click(); + await page.getByLabel('block-item-Select-Start date field').click(); + await page.getByRole('option', { name: 'Start date time2' }).click(); + await page.getByLabel('block-item-Select-End date field').click(); + await page.getByRole('option', { name: 'End date time2' }).click(); + await page.getByLabel('block-item-Select-Progress field').click(); + await page.getByRole('option', { name: 'Percent2' }).click(); + await page.getByLabel('block-item-Select-Time scale').click(); + await page.getByRole('option', { name: 'Day', exact: true }).click(); + await page.getByRole('button', { name: 'OK' }).click(); + await expect(page.getByLabel('block-item-gantt')).toBeVisible(); +}); + +//配置字段 +test.describe('configure fields', () => { + test('add field,then remove field in gantt block', async ({ page, mockPage, mockRecord }) => { + await mockPage(oneEmptyGantt).goto(); + await page.getByLabel('schema-initializer-TableV2-TableColumnInitializers-general').hover(); + await page.getByRole('menuitem', { name: 'ID' }).click(); + await page.getByRole('menuitem', { name: 'Single line text2' }).click(); + //添加字段 + await expect(page.getByRole('button', { name: 'ID', exact: true })).toBeVisible(); + await expect(page.getByRole('button', { name: 'Single line text2' })).toBeVisible(); + //激活的状态 + await expect(page.getByRole('menuitem', { name: 'ID' }).getByRole('switch')).toBeChecked(); + await expect(page.getByRole('menuitem', { name: 'Single line text2' }).getByRole('switch')).toBeChecked(); + //移除字段 + await page.getByRole('menuitem', { name: 'ID' }).click(); + await page.getByRole('menuitem', { name: 'Single line text2' }).click(); + await expect(page.getByRole('menuitem', { name: 'ID' }).getByRole('switch')).not.toBeChecked(); + await expect(page.getByRole('menuitem', { name: 'Single line text2' }).getByRole('switch')).not.toBeChecked(); + }); + test('add assciation field should appends association', async ({ page, mockPage, mockRecord }) => { + await mockPage(oneEmptyGantt).goto(); + await mockRecord('general', { singleLineText: 'singleLineText', manyToOne: { id: 1 } }); + await page.getByLabel('schema-initializer-TableV2-TableColumnInitializers-general').hover(); + + //关系字段,断言请求的appends是否符合预期 + const [request] = await Promise.all([ + page.waitForRequest((request) => request.url().includes('api/general:list')), + page.getByRole('menuitem', { name: 'Many to one', exact: true }).click(), + ]); + // 获取请求参数 + const requestUrl = request.url(); + // 解析查询参数 + const queryParams = new URLSearchParams(new URL(requestUrl).search); + const appends = queryParams.get('appends[]'); + await expect(appends).toContain('manyToOne'); + //支持修改标题字段 + await page.getByRole('button', { name: 'Many to one' }).hover(); + await page + .getByRole('button', { name: 'designer-schema-settings-TableV2.Column-TableV2.Column.Designer-general' }) + .hover(); + await page.getByRole('menuitem', { name: 'Title field' }).click(); + await page.getByText('Username').click(); + await expect( + page.getByLabel('block-item-CardItem-general-table').getByRole('button', { name: 'nocobase' }), + ).toBeVisible(); + await page.reload(); + await expect( + page.getByLabel('block-item-CardItem-general-table').getByRole('button', { name: 'nocobase' }), + ).toBeVisible(); + }); +}); + +//配置操作 +test.describe('configure actions', () => { + test('configure button in gannt block', async ({ page, mockPage }) => { + await mockPage(oneEmptyGantt).goto(); + await page.getByLabel('schema-initializer-ActionBar-GanttActionInitializers-general').hover(); + await page.getByRole('menuitem', { name: 'Filter' }).getByRole('switch').click(); + await page.getByRole('menuitem', { name: 'Add new' }).click(); + await page.getByRole('menuitem', { name: 'Delete' }).click(); + await page.getByRole('menuitem', { name: 'Refresh' }).click(); + await page.getByRole('menuitem', { name: 'Customize right' }).hover(); + await page.getByRole('menuitem', { name: 'Bulk update' }).click(); + await page.getByRole('menuitem', { name: 'Bulk edit' }).click(); + await page.getByRole('menuitem', { name: 'Add record' }).click(); + await expect(page.getByLabel('action-Filter.Action-Filter-filter-general-table')).toBeVisible(); + await expect(page.getByLabel('action-Action-Add new-create-general-table')).toBeVisible(); + await expect(page.getByLabel('action-Action-Delete-destroy-general-table')).toBeVisible(); + await expect(page.getByLabel('action-Action-Refresh-refresh-general-table')).toBeVisible(); + await expect(page.getByLabel('action-Action-Bulk update-customize:bulkUpdate-general-table')).toBeVisible(); + await expect(page.getByLabel('action-Action-Bulk edit-customize:bulkEdit-general-table')).toBeVisible(); + await expect(page.getByLabel('action-Action-Add record-customize:create-general-table')).toBeVisible(); + }); +}); diff --git a/packages/plugins/@nocobase/plugin-gantt/src/client/__e2e__/schemaSettings.test.ts b/packages/plugins/@nocobase/plugin-gantt/src/client/__e2e__/schemaSettings.test.ts new file mode 100644 index 000000000..19ae0226a --- /dev/null +++ b/packages/plugins/@nocobase/plugin-gantt/src/client/__e2e__/schemaSettings.test.ts @@ -0,0 +1,93 @@ +import { expect, test } from '@nocobase/test/client'; +import { oneEmptyGantt } from './utils'; +import { getYmd } from '../helpers/other-helper'; +const mockData = { + singleLineText: 'within apropos leaker whoever how', + singleLineText2: 'the inasmuch unwelcome gah hm cleverly muscle worriedly lazily', + startDatetime: '2023-04-26T11:02:51.129Z', + startDatetime2: '2023-04-29T03:35:05.576Z', + endDatetime: '2023-05-13T22:11:11.999Z', + endDatetime2: '2023-07-26T00:47:52.859Z', + percent: 66, + percent2: 55, +}; + +//甘特图的区块参数配置 +test.describe('configure params in gantt block', () => { + test('set data scope in gantt block', async ({ page, mockPage, mockRecords }) => { + await mockPage(oneEmptyGantt).goto(); + await mockRecords('general', 2); + await page.getByLabel('block-item-gantt').hover(); + await page.getByLabel('designer-schema-settings-CardItem-Gantt.Designer-general').hover(); + await page.getByRole('menuitem', { name: 'Set the data scope' }).click(); + await page.getByText('Add condition', { exact: true }).click(); + await page.getByTestId('select-filter-field').getByLabel('Search').click(); + await page.getByTitle('ID').getByText('ID').click(); + await page.getByRole('spinbutton').fill('1'); + const [request] = await Promise.all([ + page.waitForRequest((request) => request.url().includes('api/general:list')), + page.getByRole('button', { name: 'OK' }).click(), + ]); + const requestUrl = request.url(); + const queryParams = new URLSearchParams(new URL(requestUrl).search); + const filter = queryParams.get('filter'); + //请求参数符合预期 + expect(JSON.parse(filter)).toEqual({ $and: [{ id: { $eq: 1 } }] }); + await expect(page.getByLabel('table-index-2')).not.toBeVisible(); + }); + + test('set title field', async ({ page, mockPage, mockRecord }) => { + await mockPage(oneEmptyGantt).goto(); + await mockRecord('general', mockData); + await page.getByLabel('block-item-gantt').hover(); + await page.getByLabel('designer-schema-settings-CardItem-Gantt.Designer-general').hover(); + await page.getByRole('menuitem', { name: 'Title field' }).click(); + await page.getByRole('option', { name: 'Single line text2' }).locator('div').click(); + await page.getByRole('button', { name: 'Actions' }).click(); + await page.mouse.move(300, 0); + const barLabel = await page.getByLabel('block-item-gantt').locator('.barLabel'); + await barLabel.hover(); + expect(await barLabel.textContent()).toBe(mockData['singleLineText2']); + }); + test('set start date field ', async ({ page, mockPage, mockRecord }) => { + await mockPage(oneEmptyGantt).goto(); + await mockRecord('general', mockData); + await page.getByLabel('block-item-gantt').hover(); + await page.getByLabel('designer-schema-settings-CardItem-Gantt.Designer-general').hover(); + await page.getByRole('menuitem', { name: 'Start date field' }).click(); + await page.getByRole('option', { name: 'Start date time2' }).locator('div').click(); + await page.mouse.move(300, 0); + await page.getByRole('button', { name: 'Actions' }).click(); + await page.locator('.bar').hover(); + const tooltip2 = await page.getByLabel('nb-gantt-tooltip'); + await expect(await tooltip2.innerText()).toContain(getYmd(new Date(mockData['startDatetime2']))); + }); + test('set end date field ', async ({ page, mockPage, mockRecord }) => { + await mockPage(oneEmptyGantt).goto(); + await mockRecord('general', mockData); + await page.getByLabel('block-item-gantt').hover(); + await page.getByLabel('designer-schema-settings-CardItem-Gantt.Designer-general').hover(); + //修改结束时间字段 + await page.getByRole('menuitem', { name: 'End date field' }).click(); + await page.getByRole('option', { name: 'End date time2' }).locator('div').click(); + await page.mouse.move(300, 0); + await page.getByRole('button', { name: 'Actions' }).click(); + await page.locator('.bar').hover(); + const tooltip2 = await page.getByLabel('nb-gantt-tooltip'); + await expect(await tooltip2.innerText()).toContain(getYmd(new Date(mockData['endDatetime2']))); + }); + test('set time scale ', async ({ page, mockPage, mockRecord }) => { + await mockPage(oneEmptyGantt).goto(); + await mockRecord('general', mockData); + await page.getByLabel('block-item-gantt').hover(); + await page.getByLabel('designer-schema-settings-CardItem-Gantt.Designer-general').hover(); + //修改时间缩放等级 + await page.getByRole('menuitem', { name: 'Time scale' }).click(); + await page.getByRole('option', { name: 'Week' }).click(); + await page.getByRole('menuitem', { name: 'Time scale' }).hover(); + await page.mouse.move(300, 0); + await page.getByRole('button', { name: 'Actions' }).click(); + await page.locator('.bar').hover(); + await expect(await page.locator('.calendarBottomText').first().textContent()).toContain('W'); + }); +}); diff --git a/packages/plugins/@nocobase/plugin-gantt/src/client/__e2e__/utils.ts b/packages/plugins/@nocobase/plugin-gantt/src/client/__e2e__/utils.ts new file mode 100644 index 000000000..7c2acf79e --- /dev/null +++ b/packages/plugins/@nocobase/plugin-gantt/src/client/__e2e__/utils.ts @@ -0,0 +1,354 @@ +import { CollectionSetting, PageConfig } from '@nocobase/test/client'; + +/** + * 1. 创建一个名为 general 的 collection,其包含 时间、Percent 类型的字段 + */ +export const generalWithDatetimeFields: CollectionSetting[] = [ + { + name: 'general', + title: 'General', + fields: [ + { + name: 'singleLineText', + interface: 'input', + uiSchema: { + type: 'string', + 'x-component': 'Input', + title: 'Single line text', + }, + }, + { + name: 'singleLineText2', + interface: 'input', + uiSchema: { + type: 'string', + 'x-component': 'Input', + title: 'Single line text2', + }, + }, + { + name: 'startDatetime', + interface: 'datetime', + uiSchema: { + type: 'string', + 'x-component': 'DatePicker', + title: 'Start date time', + required: true, + }, + }, + { + name: 'startDatetime2', + interface: 'datetime', + uiSchema: { + type: 'string', + 'x-component': 'DatePicker', + title: 'Start date time2', + required: true, + }, + }, + { + name: 'endDatetime', + interface: 'datetime', + uiSchema: { + type: 'string', + 'x-component': 'DatePicker', + title: 'End date time', + required: true, + }, + }, + { + name: 'endDatetime2', + interface: 'datetime', + uiSchema: { + type: 'string', + 'x-component': 'DatePicker', + title: 'End date time2', + required: true, + }, + }, + { + name: 'f_t22o7loai3j', + interface: 'integer', + isForeignKey: true, + uiSchema: { + type: 'number', + title: 'f_t22o7loai3j', + 'x-component': 'InputNumber', + 'x-read-pretty': true, + }, + }, + { + name: 'manyToOne', + interface: 'm2o', + foreignKey: 'f_t22o7loai3j', + uiSchema: { + 'x-component': 'AssociationField', + 'x-component-props': { + multiple: false, + fieldNames: { + label: 'id', + value: 'id', + }, + }, + title: 'Many to one', + }, + target: 'users', + targetKey: 'id', + }, + { + name: 'percent', + type: 'float', + interface: 'percent', + uiSchema: { + 'x-component-props': { + step: '0.01', + stringMode: true, + addonAfter: '%', + }, + 'x-component': 'Percent', + title: 'Percent', + }, + }, + { + name: 'percent2', + type: 'float', + interface: 'percent', + uiSchema: { + 'x-component-props': { + step: '0.01', + stringMode: true, + addonAfter: '%', + }, + 'x-component': 'Percent', + title: 'Percent2', + }, + }, + ], + }, +]; + +export const oneEmptyGantt: PageConfig = { + collections: generalWithDatetimeFields, + pageSchema: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Page', + properties: { + jebhzap4dzi: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid', + 'x-initializer': 'BlockInitializers', + properties: { + gl57m4hyewf: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Row', + properties: { + jm7n5dybw6t: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Col', + properties: { + nsq0rdemz4i: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-acl-action': 'general:list', + 'x-decorator': 'GanttBlockProvider', + 'x-decorator-props': { + collection: 'general', + resource: 'general', + action: 'list', + fieldNames: { + id: 'id', + start: 'startDatetime', + range: 'day', + title: 'singleLineText', + end: 'endDatetime', + }, + params: { + paginate: false, + }, + }, + 'x-designer': 'Gantt.Designer', + 'x-component': 'CardItem', + properties: { + zf07g7relim: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Gantt', + 'x-component-props': { + useProps: '{{ useGanttBlockProps }}', + }, + properties: { + toolBar: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'ActionBar', + 'x-component-props': { + style: { + marginBottom: 24, + }, + }, + 'x-initializer': 'GanttActionInitializers', + 'x-uid': 'guwovmwt4c0', + 'x-async': false, + 'x-index': 1, + }, + table: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'array', + 'x-decorator': 'div', + 'x-decorator-props': { + style: { + float: 'left', + maxWidth: '35%', + }, + }, + 'x-initializer': 'TableColumnInitializers', + 'x-component': 'TableV2', + 'x-component-props': { + rowKey: 'id', + rowSelection: { + type: 'checkbox', + }, + useProps: '{{ useTableBlockProps }}', + pagination: false, + }, + properties: { + actions: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + title: '{{ t("Actions") }}', + 'x-action-column': 'actions', + 'x-decorator': 'TableV2.Column.ActionBar', + 'x-component': 'TableV2.Column', + 'x-designer': 'TableV2.ActionColumnDesigner', + 'x-initializer': 'TableActionColumnInitializers', + properties: { + actions: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-decorator': 'DndContext', + 'x-component': 'Space', + 'x-component-props': { + split: '|', + }, + 'x-uid': '9in7s3pymsd', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'rgpbsjwvq2h', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'g7qwompxaeo', + 'x-async': false, + 'x-index': 2, + }, + detail: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Gantt.Event', + properties: { + drawer: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Action.Drawer', + 'x-component-props': { + className: 'nb-action-popup', + }, + title: '{{ t("View record") }}', + properties: { + tabs: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Tabs', + 'x-component-props': {}, + 'x-initializer': 'TabPaneInitializers', + properties: { + tab1: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + title: '{{t("Details")}}', + 'x-component': 'Tabs.TabPane', + 'x-designer': 'Tabs.Designer', + 'x-component-props': {}, + properties: { + grid: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid', + 'x-initializer': 'RecordBlockInitializers', + 'x-uid': 'gxtfjqzxbfu', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'zxwwx4358p1', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '1p0tnmzpsim', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'hcue8v3fwti', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'fg7cjxu4hxp', + 'x-async': false, + 'x-index': 3, + }, + }, + 'x-uid': 'rf47sf7k16z', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'tritqukkd86', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '3am2ctuhyka', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'p8pesbc7fiv', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'ddm9fhkvrbw', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'vpk8sp3eap1', + 'x-async': true, + 'x-index': 1, + }, +}; diff --git a/packages/core/client/src/schema-component/antd/gantt/__tests__/date-helper.test.tsx b/packages/plugins/@nocobase/plugin-gantt/src/client/__tests__/date-helper.test.tsx similarity index 100% rename from packages/core/client/src/schema-component/antd/gantt/__tests__/date-helper.test.tsx rename to packages/plugins/@nocobase/plugin-gantt/src/client/__tests__/date-helper.test.tsx diff --git a/packages/core/client/src/schema-component/antd/gantt/components/calendar/calendar.tsx b/packages/plugins/@nocobase/plugin-gantt/src/client/components/calendar/calendar.tsx similarity index 98% rename from packages/core/client/src/schema-component/antd/gantt/components/calendar/calendar.tsx rename to packages/plugins/@nocobase/plugin-gantt/src/client/components/calendar/calendar.tsx index 1962f8700..e8f4a10e9 100644 --- a/packages/core/client/src/schema-component/antd/gantt/components/calendar/calendar.tsx +++ b/packages/plugins/@nocobase/plugin-gantt/src/client/components/calendar/calendar.tsx @@ -33,7 +33,7 @@ export const Calendar: React.FC = ({ fontFamily, fontSize, }) => { - const { wrapSSR, componentCls, hashId } = useStyles(); + const { styles } = useStyles(); const getCalendarValuesForYear = () => { const topValues: ReactChild[] = []; @@ -367,16 +367,16 @@ export const Calendar: React.FC = ({ case ViewMode.Hour: [topValues, bottomValues] = getCalendarValuesForHour(); } - return wrapSSR( - + return ( + {bottomValues} {topValues} - , + ); }; diff --git a/packages/plugins/@nocobase/plugin-gantt/src/client/components/calendar/style.tsx b/packages/plugins/@nocobase/plugin-gantt/src/client/components/calendar/style.tsx new file mode 100644 index 000000000..b69905b1c --- /dev/null +++ b/packages/plugins/@nocobase/plugin-gantt/src/client/components/calendar/style.tsx @@ -0,0 +1,48 @@ +import { TinyColor } from '@ctrl/tinycolor'; +import { createStyles } from 'antd-style'; + +const useStyles = createStyles(({ token, css }) => { + const colorFillAlterSolid = new TinyColor(token.colorFillAlter) + .onBackground(token.colorBgContainer) + .toHexShortString(); + + return { + calendarheader: css` + color: ${token.colorText}; + fill: ${colorFillAlterSolid}; + stroke-width: 1.4; + background: ${colorFillAlterSolid}; + border-bottom: 1px solid ${token.colorBorderSecondary}; + `, + nbGanttCalendar: css` + .calendarbottomtext: { + textanchor: middle; + fill: ${token.colorText}; + webkittouchcallout: none; + webkituserselect: none; + mozuserselect: none; + msuserselect: none; + userselect: none; + pointerevents: none; + font-weight: 500; + } + .calendartoptick: { + stroke: ${token.colorBorderSecondary}; + stroke-width: 0; + } + .calendartoptext: { + textanchor: middle; + webkittouchcallout: none; + webkituserselect: none; + mozuserselect: none; + msuserselect: none; + userselect: none; + pointerevents: none; + font-weight: 500; + fill: ${token.colorText}; + } + `, + }; +}); + +export default useStyles; diff --git a/packages/core/client/src/schema-component/antd/gantt/components/calendar/top-part-of-calendar.tsx b/packages/plugins/@nocobase/plugin-gantt/src/client/components/calendar/top-part-of-calendar.tsx similarity index 100% rename from packages/core/client/src/schema-component/antd/gantt/components/calendar/top-part-of-calendar.tsx rename to packages/plugins/@nocobase/plugin-gantt/src/client/components/calendar/top-part-of-calendar.tsx diff --git a/packages/core/client/src/schema-component/antd/gantt/components/gantt/Event.tsx b/packages/plugins/@nocobase/plugin-gantt/src/client/components/gantt/Event.tsx similarity index 100% rename from packages/core/client/src/schema-component/antd/gantt/components/gantt/Event.tsx rename to packages/plugins/@nocobase/plugin-gantt/src/client/components/gantt/Event.tsx diff --git a/packages/core/client/src/schema-component/antd/gantt/components/gantt/gantt.tsx b/packages/plugins/@nocobase/plugin-gantt/src/client/components/gantt/gantt.tsx similarity index 91% rename from packages/core/client/src/schema-component/antd/gantt/components/gantt/gantt.tsx rename to packages/plugins/@nocobase/plugin-gantt/src/client/components/gantt/gantt.tsx index 85f073c98..b6b2d6218 100644 --- a/packages/core/client/src/schema-component/antd/gantt/components/gantt/gantt.tsx +++ b/packages/plugins/@nocobase/plugin-gantt/src/client/components/gantt/gantt.tsx @@ -2,15 +2,19 @@ import { css, cx } from '@emotion/css'; import { createForm } from '@formily/core'; import { RecursionField, Schema, useFieldSchema } from '@formily/react'; import { message } from 'antd'; +import { debounce, throttle } from 'lodash'; import React, { SyntheticEvent, useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { useAPIClient } from '../../../../../api-client'; -import { useCurrentAppInfo } from '../../../../../appInfo'; -import { useBlockRequestContext, useGanttBlockContext, useTableBlockContext } from '../../../../../block-provider'; -import { RecordProvider } from '../../../../../record-provider'; -import { useDesignable } from '../../../../../schema-component'; -import { useToken } from '../../../__builtins__'; -import { ActionContextProvider } from '../../../action'; +import { + useAPIClient, + useCurrentAppInfo, + useTableBlockContext, + useBlockRequestContext, + RecordProvider, + useToken, + ActionContextProvider, +} from '@nocobase/client'; +import { useGanttBlockContext } from '../../GanttBlockProvider'; import { convertToBarTasks } from '../../helpers/bar-helper'; import { ganttDateRange, seedDates } from '../../helpers/date-helper'; import { removeHiddenTasks, sortTasks } from '../../helpers/other-helper'; @@ -55,10 +59,31 @@ const GanttRecordViewer = (props) => { ) ); }; +const debounceHandleTaskChange = debounce(async (task: Task, resource, fieldNames, service, t) => { + await resource.update({ + filterByTk: task.id, + values: { + [fieldNames.start]: task.start, + [fieldNames.end]: task.end, + }, + }); + message.success(t('Saved successfully')); + await service?.refresh(); +}, 300); + +const debounceHandleProcessChange = debounce(async (task: Task, resource, fieldNames, service, t) => { + await resource.update({ + filterByTk: task.id, + values: { + [fieldNames.progress]: task.progress / 100, + }, + }); + message.success(t('Saved successfully')); + await service?.refresh(); +}, 300); export const Gantt: any = (props: any) => { - const { wrapSSR, componentCls, hashId } = useStyles(); + const { styles } = useStyles(); const { token } = useToken(); - const { designable } = useDesignable(); const api = useAPIClient(); const currentTheme = api.auth.getOption('theme'); const tableRowHeight = currentTheme === 'compact' ? 45 : 55.56; @@ -233,6 +258,8 @@ export const Gantt: any = (props: any) => { const prevStateTask = barTasks.find((t) => t.id === changedTask.id); if ( prevStateTask && + prevStateTask.start && + prevStateTask.end && (prevStateTask.start.getTime() !== changedTask.start.getTime() || prevStateTask.end.getTime() !== changedTask.end.getTime() || prevStateTask.progress !== changedTask.progress) @@ -404,25 +431,10 @@ export const Gantt: any = (props: any) => { setSelectedRowKeys(keys); }; const handleProgressChange = async (task: Task) => { - await resource.update({ - filterByTk: task.id, - values: { - [fieldNames.progress]: task.progress / 100, - }, - }); - message.success(t('Saved successfully')); - await service?.refresh(); + debounceHandleProcessChange(task, resource, fieldNames, service, t); }; const handleTaskChange = async (task: Task) => { - await resource.update({ - filterByTk: task.id, - values: { - [fieldNames.start]: task.start, - [fieldNames.end]: task.end, - }, - }); - message.success(t('Saved successfully')); - await service?.refresh(); + debounceHandleTaskChange(task, resource, fieldNames, service, t); }; const handleBarClick = (data) => { const flattenTree = (treeData) => { @@ -487,25 +499,21 @@ export const Gantt: any = (props: any) => { onDelete, }; - return wrapSSR( + return (
-
+
{ onScroll={handleScrollX} />
-
, +
); }; diff --git a/packages/plugins/@nocobase/plugin-gantt/src/client/components/gantt/style.tsx b/packages/plugins/@nocobase/plugin-gantt/src/client/components/gantt/style.tsx new file mode 100644 index 000000000..3b1492b4f --- /dev/null +++ b/packages/plugins/@nocobase/plugin-gantt/src/client/components/gantt/style.tsx @@ -0,0 +1,44 @@ +import { createStyles } from '@nocobase/client'; + +const useStyles = createStyles(({ token, css }) => { + return { + ganttverticalcontainer: css` + overflow: hidden; + font-size: 0; + margin: 0; + padding: 0; + width: 100%; + border-left: 2px solid ${token.colorBorderSecondary}; + .ganttheader: { + border-bottom: 1px solid ${token.colorBorderSecondary}; + font-weight: 700; + } + .ganttbody: { + border-bottom: 1px solid ${token.colorBorderSecondary}; + } + `, + horizontalcontainer: css` + margin: 0; + padding: 0; + overflow: hidden; + `, + wrapper: css` + display: flex; + padding: 0; + margin: 0; + liststyle: none; + outline: none; + position: relative; + .gantt-horizontal-scoll: { + display: none; + } + &:hover: { + .gantt-horizontal-scoll: { + display: block; + } + } + `, + }; +}); + +export default useStyles; diff --git a/packages/core/client/src/schema-component/antd/gantt/components/gantt/task-gantt-content.tsx b/packages/plugins/@nocobase/plugin-gantt/src/client/components/gantt/task-gantt-content.tsx similarity index 100% rename from packages/core/client/src/schema-component/antd/gantt/components/gantt/task-gantt-content.tsx rename to packages/plugins/@nocobase/plugin-gantt/src/client/components/gantt/task-gantt-content.tsx diff --git a/packages/core/client/src/schema-component/antd/gantt/components/gantt/task-gantt.tsx b/packages/plugins/@nocobase/plugin-gantt/src/client/components/gantt/task-gantt.tsx similarity index 91% rename from packages/core/client/src/schema-component/antd/gantt/components/gantt/task-gantt.tsx rename to packages/plugins/@nocobase/plugin-gantt/src/client/components/gantt/task-gantt.tsx index 4bc4baf5e..d722bccb4 100644 --- a/packages/core/client/src/schema-component/antd/gantt/components/gantt/task-gantt.tsx +++ b/packages/plugins/@nocobase/plugin-gantt/src/client/components/gantt/task-gantt.tsx @@ -2,6 +2,7 @@ import React, { forwardRef, useEffect, useRef } from 'react'; import { Calendar, CalendarProps } from '../calendar/calendar'; import { Grid, GridProps } from '../grid/grid'; import { TaskGanttContent, TaskGanttContentProps } from './task-gantt-content'; +import useStyles from './style'; export type TaskGanttProps = { gridProps: GridProps; @@ -17,6 +18,7 @@ export const TaskGantt: React.FC = forwardRef( const ganttSVGRef = useRef(null); const horizontalContainerRef = useRef(null); const newBarProps = { ...barProps, svg: ganttSVGRef }; + const { styles } = useStyles(); useEffect(() => { if (horizontalContainerRef.current) { @@ -31,7 +33,7 @@ export const TaskGantt: React.FC = forwardRef( }, [scrollX]); return ( -
+
= forwardRef(
= ({ rtl, selectedRowKeys, }) => { - const { wrapSSR, componentCls, hashId } = useStyles(); + const { styles } = useStyles(); const data = tasks.length ? tasks : empty; let y = 0; @@ -42,7 +42,7 @@ export const GridBody: React.FC = ({ y={y} width={svgWidth} height={rowHeight} - className={selectedRowKeys?.includes(+task.id) ? cx('gridHeightRow') : cx('gridRow')} + className={selectedRowKeys?.includes(+task.id) ? styles.gridHeightRow : styles.gridRow} />, ); rowLines.push( @@ -81,12 +81,12 @@ export const GridBody: React.FC = ({ } tickX += columnWidth; } - return wrapSSR( - + return ( + {gridRows} {rowLines} {ticks} {today} - , + ); }; diff --git a/packages/core/client/src/schema-component/antd/gantt/components/grid/grid.tsx b/packages/plugins/@nocobase/plugin-gantt/src/client/components/grid/grid.tsx similarity index 100% rename from packages/core/client/src/schema-component/antd/gantt/components/grid/grid.tsx rename to packages/plugins/@nocobase/plugin-gantt/src/client/components/grid/grid.tsx diff --git a/packages/plugins/@nocobase/plugin-gantt/src/client/components/grid/style.tsx b/packages/plugins/@nocobase/plugin-gantt/src/client/components/grid/style.tsx new file mode 100644 index 000000000..81bf78d4f --- /dev/null +++ b/packages/plugins/@nocobase/plugin-gantt/src/client/components/grid/style.tsx @@ -0,0 +1,25 @@ +import { createStyles } from 'antd-style'; + +const useStyles = createStyles(({ token, css }) => { + return { + gridRow: css` + fill: ${token.colorBgContainer}; + `, + gridHeightRow: css` + fill: #e6f7ff; + border-color: ${token.colorBorder}; + `, + nbGridbody: css` + .gridrowline: { + stroke: ${token.colorBorderSecondary}; + stroke-width: 0; + border-bottom: 1px solid ${token.colorBorderSecondary}; + } + .gridtick: { + stroke: ${token.colorBorderSecondary}; + } + `, + }; +}); + +export default useStyles; diff --git a/packages/core/client/src/schema-component/antd/gantt/components/other/arrow.tsx b/packages/plugins/@nocobase/plugin-gantt/src/client/components/other/arrow.tsx similarity index 100% rename from packages/core/client/src/schema-component/antd/gantt/components/other/arrow.tsx rename to packages/plugins/@nocobase/plugin-gantt/src/client/components/other/arrow.tsx diff --git a/packages/core/client/src/schema-component/antd/gantt/components/other/horizontal-scroll.tsx b/packages/plugins/@nocobase/plugin-gantt/src/client/components/other/horizontal-scroll.tsx similarity index 73% rename from packages/core/client/src/schema-component/antd/gantt/components/other/horizontal-scroll.tsx rename to packages/plugins/@nocobase/plugin-gantt/src/client/components/other/horizontal-scroll.tsx index 662f1921a..00c172433 100644 --- a/packages/core/client/src/schema-component/antd/gantt/components/other/horizontal-scroll.tsx +++ b/packages/plugins/@nocobase/plugin-gantt/src/client/components/other/horizontal-scroll.tsx @@ -1,4 +1,5 @@ import React, { SyntheticEvent, useEffect, useRef } from 'react'; +import { cx } from '@emotion/css'; import useStyles from './style'; export const HorizontalScroll: React.FC<{ @@ -8,7 +9,7 @@ export const HorizontalScroll: React.FC<{ rtl: boolean; onScroll: (event: SyntheticEvent) => void; }> = ({ scroll, svgWidth, taskListWidth, rtl, onScroll }) => { - const { wrapSSR, componentCls, hashId } = useStyles(); + const { styles } = useStyles(); const scrollRef = useRef(null); useEffect(() => { @@ -17,17 +18,17 @@ export const HorizontalScroll: React.FC<{ } }, [scroll]); - return wrapSSR( + return (
-
-
, +
+
); }; diff --git a/packages/plugins/@nocobase/plugin-gantt/src/client/components/other/style.tsx b/packages/plugins/@nocobase/plugin-gantt/src/client/components/other/style.tsx new file mode 100644 index 000000000..544d78853 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-gantt/src/client/components/other/style.tsx @@ -0,0 +1,88 @@ +import { createStyles } from 'antd-style'; + +const useStyles = createStyles(({ token, css }) => { + return { + scrollWrapper: css` + overflow: auto; + position: absolute; + bottom: -4px; + max-width: 100%; + scrollbarWidth: thin; + height: 1.2rem; + &::-webkit-scrollbar: { width: 8, height: 8 }; + &::-webkit-scrollbar-corner: { background: transparent }; + &::-webkit-scrollbar-track: { background: var(--colorBgScrollTrack) }; + &::-webkit-scrollbar-thumb: { + background: var(--colorBgScrollBar); + borderRadius: 4; + }; + &::-webkit-scrollbar-thumb:hover: { + background: var(--colorBgScrollBarHover); + }; + &::-webkit-scrollbar-thumb:active: { + background: var(--colorBgScrollBarActive) + } + `, + tooltipDefaultContainer: css` + padding: 12px; + background-color: ${token.colorBgElevated}; + background-clip: padding-box; + border-radius: ${token.borderRadius}; + box-shadow: ${token.boxShadow}; + b: { + display: block; + margin-bottom: ${token.marginXS}; + } + .tooltipdefaultcontainerparagraph: { + font-size: ${token.fontSizeSM}; + margin-bottom: ${token.marginXXS} + ${token.marginXS - token.marginXXS}; + color: ${token.colorText}; + } + `, + tooltipDetailsContainer: css` + position: absolute; + display: flex; + flexshrink: 0; + pointerevents: none; + webkittouchcallout: none; + webkituserselect: none; + mozuserselect: none; + msuserselect: none; + userselect: none; + `, + tooltipDetailsContainerHidden: css` + visibility: hidden; + position: absolute; + display: flex; + pointerevents: none; + `, + nbGridOther: css` + .horizontalScroll: { + height: 1 + } + + .verticalScroll: { + overflow: hidden auto; + width: 1rem; + flexShrink: 0; + scrollbarWidth: thin; + &::-webkit-scrollbar: { width: 8, height: 8 }; + &::-webkit-scrollbar-corner: { background: transparent}; + &::-webkit-scrollbar-track: { background: var(--colorBgScrollTrack) }; + &::-webkit-scrollbar-thumb: { + background: var(--colorBgScrollBar); + borderRadius: 4; + } + &::-webkit-scrollbar-thumb:hover': { + background: var(--colorBgScrollBarHover) + } + &::-webkit-scrollbar-thumb:active': { + background: var(--colorBgScrollBarActive) + }, + } + } + `, + }; +}); + +export default useStyles; diff --git a/packages/core/client/src/schema-component/antd/gantt/components/other/tooltip.tsx b/packages/plugins/@nocobase/plugin-gantt/src/client/components/other/tooltip.tsx similarity index 86% rename from packages/core/client/src/schema-component/antd/gantt/components/other/tooltip.tsx rename to packages/plugins/@nocobase/plugin-gantt/src/client/components/other/tooltip.tsx index 7a55ed32b..2b2624b0b 100644 --- a/packages/core/client/src/schema-component/antd/gantt/components/other/tooltip.tsx +++ b/packages/plugins/@nocobase/plugin-gantt/src/client/components/other/tooltip.tsx @@ -40,7 +40,7 @@ export const Tooltip: React.FC = ({ taskListWidth, TooltipContent, }) => { - const { wrapSSR, componentCls, hashId } = useStyles(); + const { styles } = useStyles(); const tooltipRef = useRef(null); const [relatedY, setRelatedY] = useState(0); const [relatedX, setRelatedX] = useState(0); @@ -95,14 +95,17 @@ export const Tooltip: React.FC = ({ rtl, ]); - return wrapSSR( + return (
-
, +
); }; @@ -111,24 +114,24 @@ export const StandardTooltipContent: React.FC<{ fontSize: string; fontFamily: string; }> = ({ task, fontSize, fontFamily }) => { - const { wrapSSR, componentCls, hashId } = useStyles(); + const { styles } = useStyles(); const style = { fontSize, fontFamily, }; - return wrapSSR( -
+ return ( +
{task.name}: {getYmd(task.start)} ~ {getYmd(task.end)} - {task.end.getTime() - task.start.getTime() !== 0 && ( + {task.end?.getTime?.() - task.start?.getTime?.() !== 0 && (

{`Duration: ${ - Math.round(((task.end.getTime() - task.start.getTime()) / (1000 * 60 * 60 * 24)) * 10) / 10 + Math.round(((task.end?.getTime?.() - task.start?.getTime?.()) / (1000 * 60 * 60 * 24)) * 10) / 10 || '' } day(s)`}

)}

{!!task.progress && `Progress: ${task.progress}%`}

-
, +
); }; diff --git a/packages/core/client/src/schema-component/antd/gantt/components/other/vertical-scroll.tsx b/packages/plugins/@nocobase/plugin-gantt/src/client/components/other/vertical-scroll.tsx similarity index 85% rename from packages/core/client/src/schema-component/antd/gantt/components/other/vertical-scroll.tsx rename to packages/plugins/@nocobase/plugin-gantt/src/client/components/other/vertical-scroll.tsx index e7e3fe9bf..0177d7941 100644 --- a/packages/core/client/src/schema-component/antd/gantt/components/other/vertical-scroll.tsx +++ b/packages/plugins/@nocobase/plugin-gantt/src/client/components/other/vertical-scroll.tsx @@ -10,7 +10,7 @@ export const VerticalScroll: React.FC<{ rtl: boolean; onScroll: (event: SyntheticEvent) => void; }> = ({ scroll, ganttHeight, ganttFullHeight, headerHeight, rtl, onScroll }) => { - const { wrapSSR, componentCls, hashId } = useStyles(); + const { styles } = useStyles(); const scrollRef = useRef(null); useEffect(() => { @@ -19,18 +19,18 @@ export const VerticalScroll: React.FC<{ } }, [scroll]); - return wrapSSR( + return (
-
, +
); }; diff --git a/packages/core/client/src/schema-component/antd/gantt/components/task-item/bar/bar-date-handle.tsx b/packages/plugins/@nocobase/plugin-gantt/src/client/components/task-item/bar/bar-date-handle.tsx similarity index 96% rename from packages/core/client/src/schema-component/antd/gantt/components/task-item/bar/bar-date-handle.tsx rename to packages/plugins/@nocobase/plugin-gantt/src/client/components/task-item/bar/bar-date-handle.tsx index b54e37071..fef731111 100644 --- a/packages/core/client/src/schema-component/antd/gantt/components/task-item/bar/bar-date-handle.tsx +++ b/packages/plugins/@nocobase/plugin-gantt/src/client/components/task-item/bar/bar-date-handle.tsx @@ -16,6 +16,7 @@ export const BarDateHandle: React.FC = ({ x, y, width, heigh width={width} height={height} className={'barHandle'} + role="button" ry={barCornerRadius} rx={barCornerRadius} onMouseDown={onMouseDown} diff --git a/packages/core/client/src/schema-component/antd/gantt/components/task-item/bar/bar-display.tsx b/packages/plugins/@nocobase/plugin-gantt/src/client/components/task-item/bar/bar-display.tsx similarity index 100% rename from packages/core/client/src/schema-component/antd/gantt/components/task-item/bar/bar-display.tsx rename to packages/plugins/@nocobase/plugin-gantt/src/client/components/task-item/bar/bar-display.tsx diff --git a/packages/core/client/src/schema-component/antd/gantt/components/task-item/bar/bar-progress-handle.tsx b/packages/plugins/@nocobase/plugin-gantt/src/client/components/task-item/bar/bar-progress-handle.tsx similarity index 100% rename from packages/core/client/src/schema-component/antd/gantt/components/task-item/bar/bar-progress-handle.tsx rename to packages/plugins/@nocobase/plugin-gantt/src/client/components/task-item/bar/bar-progress-handle.tsx diff --git a/packages/core/client/src/schema-component/antd/gantt/components/task-item/bar/bar-small.tsx b/packages/plugins/@nocobase/plugin-gantt/src/client/components/task-item/bar/bar-small.tsx similarity index 100% rename from packages/core/client/src/schema-component/antd/gantt/components/task-item/bar/bar-small.tsx rename to packages/plugins/@nocobase/plugin-gantt/src/client/components/task-item/bar/bar-small.tsx diff --git a/packages/core/client/src/schema-component/antd/gantt/components/task-item/bar/bar.tsx b/packages/plugins/@nocobase/plugin-gantt/src/client/components/task-item/bar/bar.tsx similarity index 96% rename from packages/core/client/src/schema-component/antd/gantt/components/task-item/bar/bar.tsx rename to packages/plugins/@nocobase/plugin-gantt/src/client/components/task-item/bar/bar.tsx index b6b6b7a33..4b39fcfee 100644 --- a/packages/core/client/src/schema-component/antd/gantt/components/task-item/bar/bar.tsx +++ b/packages/plugins/@nocobase/plugin-gantt/src/client/components/task-item/bar/bar.tsx @@ -18,7 +18,7 @@ export const Bar: React.FC = ({ const progressPoint = getProgressPoint(+!rtl * task.progressWidth + task.progressX, task.y, task.height); const handleHeight = task.height - 2; return ( - + { + return { + nbganttTaskitem: css` + .barLabel: { + fill: ${token.colorTextLightSolid}, + textAnchor: middle, + font-weight: 400, + dominantBaseline: central, + WebkitTouchCallout: none, + WebkitUserSelect: none, + MozUserSelect: none, + msUserSelect: none, + userSelect: none, + pointerEvents: none, + }, + .projectLabel: { + fill: #130d0d, + fontWeight: 500, + fontSize: 0.9em, + dominantBaseline: central, + WebkitTouchCallout: none, + WebkitUserSelect: none, + MozUserSelect: none, + msUserSelect: none, + userSelect: none, + pointerEvents: none, + }, + .barLabelOutside: { + fill: ${token.colorTextLabel}, + textAnchor: start, + WebkitTouchCallout: none, + WebkitUserSelect: none, + MozUserSelect: none, + msUserSelect: none, + userSelect: none, + pointerEvents: none, + }, + } + }`, + }; +}); + +export default useStyles; diff --git a/packages/core/client/src/schema-component/antd/gantt/components/task-item/task-item.tsx b/packages/plugins/@nocobase/plugin-gantt/src/client/components/task-item/task-item.tsx similarity index 96% rename from packages/core/client/src/schema-component/antd/gantt/components/task-item/task-item.tsx rename to packages/plugins/@nocobase/plugin-gantt/src/client/components/task-item/task-item.tsx index 4942b2633..8bf555947 100644 --- a/packages/core/client/src/schema-component/antd/gantt/components/task-item/task-item.tsx +++ b/packages/plugins/@nocobase/plugin-gantt/src/client/components/task-item/task-item.tsx @@ -26,7 +26,7 @@ export type TaskItemProps = { }; export const TaskItem: React.FC = (props) => { - const { wrapSSR, componentCls, hashId } = useStyles(); + const { styles } = useStyles(); const { task, arrowIndent, isDelete, taskHeight, isSelected, rtl, onEventStart } = { ...props, }; @@ -69,9 +69,9 @@ export const TaskItem: React.FC = (props) => { return task.x1 + width + arrowIndent * +hasChild + arrowIndent * 0.2; } }; - return wrapSSR( + return ( { switch (e.key) { case 'Delete': { @@ -108,6 +108,6 @@ export const TaskItem: React.FC = (props) => { ? `${task.name}: ${getYmd(task.start)} ~ ${getYmd(task.end)}` : task.name} - , + ); }; diff --git a/packages/core/client/src/schema-component/antd/gantt/context.ts b/packages/plugins/@nocobase/plugin-gantt/src/client/context.ts similarity index 100% rename from packages/core/client/src/schema-component/antd/gantt/context.ts rename to packages/plugins/@nocobase/plugin-gantt/src/client/context.ts diff --git a/packages/core/client/src/schema-component/antd/gantt/demos/demo1.tsx b/packages/plugins/@nocobase/plugin-gantt/src/client/demos/demo1.tsx similarity index 100% rename from packages/core/client/src/schema-component/antd/gantt/demos/demo1.tsx rename to packages/plugins/@nocobase/plugin-gantt/src/client/demos/demo1.tsx diff --git a/packages/core/client/src/schema-component/antd/gantt/helpers/bar-helper.ts b/packages/plugins/@nocobase/plugin-gantt/src/client/helpers/bar-helper.ts similarity index 99% rename from packages/core/client/src/schema-component/antd/gantt/helpers/bar-helper.ts rename to packages/plugins/@nocobase/plugin-gantt/src/client/helpers/bar-helper.ts index 6ae2b24bd..91fe48f66 100644 --- a/packages/core/client/src/schema-component/antd/gantt/helpers/bar-helper.ts +++ b/packages/plugins/@nocobase/plugin-gantt/src/client/helpers/bar-helper.ts @@ -338,8 +338,8 @@ const moveByX = (x: number, xStep: number, task: BarTask) => { }; const dateByX = (x: number, taskX: number, taskDate: Date, xStep: number, timeStep: number) => { - let newDate = new Date(((x - taskX) / xStep) * timeStep + taskDate.getTime()); - newDate = new Date(newDate.getTime() + (newDate.getTimezoneOffset() - taskDate.getTimezoneOffset()) * 60000); + let newDate = new Date(((x - taskX) / xStep) * timeStep + taskDate?.getTime?.()); + newDate = new Date(newDate?.getTime?.() + (newDate?.getTimezoneOffset() - taskDate?.getTimezoneOffset()) * 60000); return newDate; }; diff --git a/packages/core/client/src/schema-component/antd/gantt/helpers/date-helper.ts b/packages/plugins/@nocobase/plugin-gantt/src/client/helpers/date-helper.ts similarity index 100% rename from packages/core/client/src/schema-component/antd/gantt/helpers/date-helper.ts rename to packages/plugins/@nocobase/plugin-gantt/src/client/helpers/date-helper.ts diff --git a/packages/core/client/src/schema-component/antd/gantt/helpers/other-helper.ts b/packages/plugins/@nocobase/plugin-gantt/src/client/helpers/other-helper.ts similarity index 97% rename from packages/core/client/src/schema-component/antd/gantt/helpers/other-helper.ts rename to packages/plugins/@nocobase/plugin-gantt/src/client/helpers/other-helper.ts index b01206c1b..f79b9612c 100644 --- a/packages/core/client/src/schema-component/antd/gantt/helpers/other-helper.ts +++ b/packages/plugins/@nocobase/plugin-gantt/src/client/helpers/other-helper.ts @@ -57,7 +57,8 @@ export const sortTasks = (taskA: Task, taskB: Task) => { }; export const getYmd = (date: Date) => { - if (!isNaN(date.getTime())) { + if (!isNaN(date?.getTime?.())) { return date.getFullYear() + '/' + `${date.getMonth() + 1}` + '/' + date.getDate(); } + return 0; }; diff --git a/packages/core/client/src/schema-component/antd/gantt/index.md b/packages/plugins/@nocobase/plugin-gantt/src/client/index.md similarity index 100% rename from packages/core/client/src/schema-component/antd/gantt/index.md rename to packages/plugins/@nocobase/plugin-gantt/src/client/index.md diff --git a/packages/plugins/@nocobase/plugin-gantt/src/client/index.tsx b/packages/plugins/@nocobase/plugin-gantt/src/client/index.tsx new file mode 100644 index 000000000..4031871cf --- /dev/null +++ b/packages/plugins/@nocobase/plugin-gantt/src/client/index.tsx @@ -0,0 +1,52 @@ +import React, { useContext } from 'react'; +import { + Plugin, + ActionBar, + CollectionManagerContext, + CurrentAppInfoProvider, + SchemaComponentOptions, +} from '@nocobase/client'; +import { Gantt } from './components/gantt/gantt'; +import { GanttDesigner } from './Gantt.Designer'; +import { ViewMode } from './types/public-types'; +import { Event } from './components/gantt/Event'; +import { GanttActionInitializers } from './GanttActionInitializers'; +import { GanttBlockInitializer } from './GanttBlockInitializer'; +import { GanttBlockProvider, useGanttBlockProps } from './GanttBlockProvider'; +import { ganttSettings } from './Gantt.Settings'; + +Gantt.ActionBar = ActionBar; +Gantt.ViewMode = ViewMode; +Gantt.Designer = GanttDesigner; +Gantt.Event = Event; +export { Gantt }; + +const GanttProvider = React.memo((props) => { + const ctx = useContext(CollectionManagerContext); + return ( + + + {props.children} + + + ); +}); + +GanttProvider.displayName = 'GanttProvider'; +export class GanttPlugin extends Plugin { + async load() { + this.app.use(GanttProvider); + this.app.schemaSettingsManager.add(ganttSettings); + this.app.schemaInitializerManager.add(GanttActionInitializers); + const blockInitializers = this.app.schemaInitializerManager.get('BlockInitializers'); + blockInitializers?.add('dataBlocks.gantt', { + title: "{{t('Gantt')}}", + Component: 'GanttBlockInitializer', + }); + } +} + +export default GanttPlugin; diff --git a/packages/core/client/src/schema-component/antd/gantt/types/bar-task.ts b/packages/plugins/@nocobase/plugin-gantt/src/client/types/bar-task.ts similarity index 100% rename from packages/core/client/src/schema-component/antd/gantt/types/bar-task.ts rename to packages/plugins/@nocobase/plugin-gantt/src/client/types/bar-task.ts diff --git a/packages/core/client/src/schema-component/antd/gantt/types/date-setup.ts b/packages/plugins/@nocobase/plugin-gantt/src/client/types/date-setup.ts similarity index 100% rename from packages/core/client/src/schema-component/antd/gantt/types/date-setup.ts rename to packages/plugins/@nocobase/plugin-gantt/src/client/types/date-setup.ts diff --git a/packages/core/client/src/schema-component/antd/gantt/types/gantt-task-actions.ts b/packages/plugins/@nocobase/plugin-gantt/src/client/types/gantt-task-actions.ts similarity index 100% rename from packages/core/client/src/schema-component/antd/gantt/types/gantt-task-actions.ts rename to packages/plugins/@nocobase/plugin-gantt/src/client/types/gantt-task-actions.ts diff --git a/packages/core/client/src/schema-component/antd/gantt/types/public-types.ts b/packages/plugins/@nocobase/plugin-gantt/src/client/types/public-types.ts similarity index 100% rename from packages/core/client/src/schema-component/antd/gantt/types/public-types.ts rename to packages/plugins/@nocobase/plugin-gantt/src/client/types/public-types.ts diff --git a/packages/plugins/@nocobase/plugin-gantt/src/client/utils.tsx b/packages/plugins/@nocobase/plugin-gantt/src/client/utils.tsx new file mode 100644 index 000000000..d9dfeed47 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-gantt/src/client/utils.tsx @@ -0,0 +1,150 @@ +import { ISchema } from '@formily/react'; +import { uid } from '@formily/shared'; +import { useCompile, useCollection } from '@nocobase/client'; +import { useTranslation } from 'react-i18next'; + +export const useGanttTranslation = () => { + return useTranslation('gantt'); +}; +export const useOptions = (type = 'string') => { + const compile = useCompile(); + const { fields } = useCollection(); + const options = fields + ?.filter((field) => field.type === type) + ?.map((field) => { + return { + value: field.name, + label: compile(field?.uiSchema?.title), + }; + }); + return options; +}; +export const createGanttBlockSchema = (options) => { + const { collection, resource, fieldNames, ...others } = options; + const schema: ISchema = { + type: 'void', + 'x-acl-action': `${resource || collection}:list`, + 'x-decorator': 'GanttBlockProvider', + 'x-decorator-props': { + collection: collection, + resource: resource || collection, + action: 'list', + fieldNames: { + id: 'id', + ...fieldNames, + }, + params: { + paginate: false, + }, + ...others, + }, + 'x-designer': 'Gantt.Designer', + 'x-component': 'CardItem', + properties: { + [uid()]: { + type: 'void', + 'x-component': 'Gantt', + 'x-component-props': { + useProps: '{{ useGanttBlockProps }}', + }, + properties: { + toolBar: { + type: 'void', + 'x-component': 'ActionBar', + 'x-component-props': { + style: { + marginBottom: 24, + }, + }, + 'x-initializer': 'GanttActionInitializers', + properties: {}, + }, + table: { + type: 'array', + 'x-decorator': 'div', + 'x-decorator-props': { + style: { + float: 'left', + maxWidth: '35%', + }, + }, + + 'x-initializer': 'TableColumnInitializers', + 'x-component': 'TableV2', + 'x-component-props': { + rowKey: 'id', + rowSelection: { + type: 'checkbox', + }, + useProps: '{{ useTableBlockProps }}', + pagination: false, + }, + properties: { + actions: { + type: 'void', + title: '{{ t("Actions") }}', + 'x-action-column': 'actions', + 'x-decorator': 'TableV2.Column.ActionBar', + 'x-component': 'TableV2.Column', + 'x-designer': 'TableV2.ActionColumnDesigner', + 'x-initializer': 'TableActionColumnInitializers', + properties: { + actions: { + type: 'void', + 'x-decorator': 'DndContext', + 'x-component': 'Space', + 'x-component-props': { + split: '|', + }, + properties: {}, + }, + }, + }, + }, + }, + detail: { + type: 'void', + 'x-component': 'Gantt.Event', + properties: { + drawer: { + type: 'void', + 'x-component': 'Action.Drawer', + 'x-component-props': { + className: 'nb-action-popup', + }, + title: '{{ t("View record") }}', + 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: {}, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }; + + return schema; +}; diff --git a/packages/plugins/@nocobase/plugin-gantt/src/index.ts b/packages/plugins/@nocobase/plugin-gantt/src/index.ts new file mode 100644 index 000000000..7e74612df --- /dev/null +++ b/packages/plugins/@nocobase/plugin-gantt/src/index.ts @@ -0,0 +1,2 @@ +export * from './server'; +export { default } from './server'; diff --git a/packages/plugins/@nocobase/plugin-gantt/src/server/collections/.gitkeep b/packages/plugins/@nocobase/plugin-gantt/src/server/collections/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/packages/plugins/@nocobase/plugin-gantt/src/server/index.ts b/packages/plugins/@nocobase/plugin-gantt/src/server/index.ts new file mode 100644 index 000000000..b68aea57f --- /dev/null +++ b/packages/plugins/@nocobase/plugin-gantt/src/server/index.ts @@ -0,0 +1 @@ +export { default } from './plugin'; diff --git a/packages/plugins/@nocobase/plugin-gantt/src/server/plugin.ts b/packages/plugins/@nocobase/plugin-gantt/src/server/plugin.ts new file mode 100644 index 000000000..57c95c577 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-gantt/src/server/plugin.ts @@ -0,0 +1,19 @@ +import { InstallOptions, Plugin } from '@nocobase/server'; + +export class PluginGanttServer extends Plugin { + afterAdd() {} + + beforeLoad() {} + + async load() {} + + async install(options?: InstallOptions) {} + + async afterEnable() {} + + async afterDisable() {} + + async remove() {} +} + +export default PluginGanttServer; diff --git a/packages/plugins/@nocobase/plugin-import/src/client/index.ts b/packages/plugins/@nocobase/plugin-import/src/client/index.ts index eea4e3131..04b2a09ac 100644 --- a/packages/plugins/@nocobase/plugin-import/src/client/index.ts +++ b/packages/plugins/@nocobase/plugin-import/src/client/index.ts @@ -36,8 +36,7 @@ export class ImportPlugin extends Plugin { const tableActionInitializers = this.app.schemaInitializerManager.get('TableActionInitializers'); tableActionInitializers?.add('enableActions.import', initializerData); - const ganttActionInitializers = this.app.schemaInitializerManager.get('GanttActionInitializers'); - ganttActionInitializers?.add('enableActions.import', initializerData); + this.app.schemaInitializerManager.addItem('GanttActionInitializers', 'enableActions.import', initializerData); } } diff --git a/packages/plugins/@nocobase/plugin-kanban/.npmignore b/packages/plugins/@nocobase/plugin-kanban/.npmignore new file mode 100644 index 000000000..65f5e8779 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-kanban/.npmignore @@ -0,0 +1,2 @@ +/node_modules +/src diff --git a/packages/plugins/@nocobase/plugin-kanban/README.md b/packages/plugins/@nocobase/plugin-kanban/README.md new file mode 100644 index 000000000..8cfc6ddef --- /dev/null +++ b/packages/plugins/@nocobase/plugin-kanban/README.md @@ -0,0 +1 @@ +# @nocobase/plugin-kanban diff --git a/packages/plugins/@nocobase/plugin-kanban/client.d.ts b/packages/plugins/@nocobase/plugin-kanban/client.d.ts new file mode 100644 index 000000000..6c459cbac --- /dev/null +++ b/packages/plugins/@nocobase/plugin-kanban/client.d.ts @@ -0,0 +1,2 @@ +export * from './dist/client'; +export { default } from './dist/client'; diff --git a/packages/plugins/@nocobase/plugin-kanban/client.js b/packages/plugins/@nocobase/plugin-kanban/client.js new file mode 100644 index 000000000..b6e3be70e --- /dev/null +++ b/packages/plugins/@nocobase/plugin-kanban/client.js @@ -0,0 +1 @@ +module.exports = require('./dist/client/index.js'); diff --git a/packages/plugins/@nocobase/plugin-kanban/package.json b/packages/plugins/@nocobase/plugin-kanban/package.json new file mode 100644 index 000000000..77d3fbf28 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-kanban/package.json @@ -0,0 +1,16 @@ +{ + "name": "@nocobase/plugin-kanban", + "version": "0.17.0-alpha.7", + "main": "dist/server/index.js", + "license": "AGPL-3.0", + "displayName": "Kanban", + "displayName.zh-CN": "看板", + "description": "Provides kanban blocks", + "description.zh-CN": "提供看板区块", + "dependencies": {}, + "peerDependencies": { + "@nocobase/client": "0.x", + "@nocobase/server": "0.x", + "@nocobase/test": "0.x" + } +} diff --git a/packages/plugins/@nocobase/plugin-kanban/server.d.ts b/packages/plugins/@nocobase/plugin-kanban/server.d.ts new file mode 100644 index 000000000..c41081ddc --- /dev/null +++ b/packages/plugins/@nocobase/plugin-kanban/server.d.ts @@ -0,0 +1,2 @@ +export * from './dist/server'; +export { default } from './dist/server'; diff --git a/packages/plugins/@nocobase/plugin-kanban/server.js b/packages/plugins/@nocobase/plugin-kanban/server.js new file mode 100644 index 000000000..972842039 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-kanban/server.js @@ -0,0 +1 @@ +module.exports = require('./dist/server/index.js'); diff --git a/packages/core/client/src/schema-component/antd/kanban/Kanban.Card.Designer.TitleSwitch.tsx b/packages/plugins/@nocobase/plugin-kanban/src/client/Kanban.Card.Designer.TitleSwitch.tsx similarity index 76% rename from packages/core/client/src/schema-component/antd/kanban/Kanban.Card.Designer.TitleSwitch.tsx rename to packages/plugins/@nocobase/plugin-kanban/src/client/Kanban.Card.Designer.TitleSwitch.tsx index 897e4cad1..27f086823 100644 --- a/packages/core/client/src/schema-component/antd/kanban/Kanban.Card.Designer.TitleSwitch.tsx +++ b/packages/plugins/@nocobase/plugin-kanban/src/client/Kanban.Card.Designer.TitleSwitch.tsx @@ -1,7 +1,6 @@ import React from 'react'; -import { useFieldSchema, Schema } from '@formily/react'; -import { useDesignable } from '../../hooks'; -import { SchemaInitializerSwitch, useSchemaInitializerItem } from '../../../application'; +import { useFieldSchema } from '@formily/react'; +import { useDesignable, SchemaInitializerSwitch, useSchemaInitializerItem } from '@nocobase/client'; export const KanbanCardDesignerTitleSwitch = () => { const itemConfig = useSchemaInitializerItem(); diff --git a/packages/core/client/src/schema-component/antd/kanban/Kanban.Card.Designer.tsx b/packages/plugins/@nocobase/plugin-kanban/src/client/Kanban.Card.Designer.tsx similarity index 82% rename from packages/core/client/src/schema-component/antd/kanban/Kanban.Card.Designer.tsx rename to packages/plugins/@nocobase/plugin-kanban/src/client/Kanban.Card.Designer.tsx index 0c454f8b3..e4c5e943b 100644 --- a/packages/core/client/src/schema-component/antd/kanban/Kanban.Card.Designer.tsx +++ b/packages/plugins/@nocobase/plugin-kanban/src/client/Kanban.Card.Designer.tsx @@ -4,16 +4,17 @@ import { uid } from '@formily/shared'; import { Space } from 'antd'; import React from 'react'; import { useTranslation } from 'react-i18next'; -import { useAPIClient } from '../../../api-client'; -import { useSchemaInitializerRender } from '../../../application'; -import { SchemaInitializer } from '../../../application/schema-initializer/SchemaInitializer'; -import { createDesignable, useDesignable } from '../../../schema-component'; import { + useAPIClient, + useSchemaInitializerRender, + SchemaInitializer, + createDesignable, + useDesignable, useAssociatedFormItemInitializerFields, useFormItemInitializerFields, -} from '../../../schema-initializer/utils'; -import { SchemaInitializerOpenModeSchemaItems } from '../../../schema-items'; -import { useGetAriaLabelOfDesigner } from '../../../schema-settings/hooks/useGetAriaLabelOfDesigner'; + SchemaInitializerOpenModeSchemaItems, + useGetAriaLabelOfDesigner, +} from '@nocobase/client'; const gridRowColWrap = (schema: ISchema) => { schema['x-read-pretty'] = true; @@ -106,13 +107,7 @@ export const kanbanCardInitializers = new SchemaInitializer({ type: 'itemGroup', title: '{{t("Display fields")}}', name: 'displayFields', - useChildren() { - const fields = useFormItemInitializerFields({ - readPretty: true, - block: 'Kanban', - }); - return fields; - }, + useChildren: useFormItemInitializerFields, }, { type: 'itemGroup', @@ -121,7 +116,10 @@ export const kanbanCardInitializers = new SchemaInitializer({ name: 'displayAssociationFields', checkChildrenLength: true, useChildren() { - const associationFields = useAssociatedFormItemInitializerFields({ readPretty: true, block: 'Kanban' }); + const associationFields = useAssociatedFormItemInitializerFields({ + readPretty: true, + block: 'Kanban', + }); return associationFields; }, }, diff --git a/packages/core/client/src/schema-component/antd/kanban/Kanban.Card.tsx b/packages/plugins/@nocobase/plugin-kanban/src/client/Kanban.Card.tsx similarity index 63% rename from packages/core/client/src/schema-component/antd/kanban/Kanban.Card.tsx rename to packages/plugins/@nocobase/plugin-kanban/src/client/Kanban.Card.tsx index a797541a0..fdfebd90c 100644 --- a/packages/core/client/src/schema-component/antd/kanban/Kanban.Card.tsx +++ b/packages/plugins/@nocobase/plugin-kanban/src/client/Kanban.Card.tsx @@ -4,10 +4,7 @@ import { observer, RecursionField, useFieldSchema } from '@formily/react'; import { Card } from 'antd'; import cls from 'classnames'; import React, { useContext, useState } from 'react'; -import { ActionContextProvider } from '..'; -import { DndContext } from '../..'; -import { RecordProvider } from '../../../record-provider'; -import { SchemaComponentOptions } from '../../core/SchemaComponentOptions'; +import { ActionContextProvider, DndContext, RecordProvider, SchemaComponentOptions } from '@nocobase/client'; import { KanbanCardContext } from './context'; export const KanbanCard: any = observer( @@ -17,7 +14,6 @@ export const KanbanCard: any = observer( const fieldSchema = useFieldSchema(); const [visible, setVisible] = useState(false); const labelDisabled = fieldSchema['x-label-disabled']; - return ( { diff --git a/packages/core/client/src/schema-component/antd/kanban/Kanban.CardViewer.tsx b/packages/plugins/@nocobase/plugin-kanban/src/client/Kanban.CardViewer.tsx similarity index 100% rename from packages/core/client/src/schema-component/antd/kanban/Kanban.CardViewer.tsx rename to packages/plugins/@nocobase/plugin-kanban/src/client/Kanban.CardViewer.tsx diff --git a/packages/plugins/@nocobase/plugin-kanban/src/client/Kanban.Designer.tsx b/packages/plugins/@nocobase/plugin-kanban/src/client/Kanban.Designer.tsx new file mode 100644 index 000000000..4268a60d1 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-kanban/src/client/Kanban.Designer.tsx @@ -0,0 +1,15 @@ +import React from 'react'; +import { useCollection, GeneralSchemaDesigner, useSchemaTemplate } from '@nocobase/client'; + +export const KanbanDesigner = () => { + const { name, title } = useCollection(); + const template = useSchemaTemplate(); + + return ( + + ); +}; diff --git a/packages/plugins/@nocobase/plugin-kanban/src/client/Kanban.Settings.tsx b/packages/plugins/@nocobase/plugin-kanban/src/client/Kanban.Settings.tsx new file mode 100644 index 000000000..20ff847ea --- /dev/null +++ b/packages/plugins/@nocobase/plugin-kanban/src/client/Kanban.Settings.tsx @@ -0,0 +1,84 @@ +import { useField, useFieldSchema } from '@formily/react'; +import { + useFormBlockContext, + SchemaSettingsDataScope, + useCollection, + useDesignable, + SchemaSettings, + FixedBlockDesignerItem, + SchemaSettingsBlockTitleItem, + removeNullCondition, + SchemaSettingsTemplate, +} from '@nocobase/client'; +import { useKanbanBlockContext } from './KanbanBlockProvider'; +export const kanbanSettings = new SchemaSettings({ + name: 'KanbanSettings', + items: [ + { + name: 'title', + Component: SchemaSettingsBlockTitleItem, + }, + { + name: 'dataScope', + Component: SchemaSettingsDataScope, + useComponentProps() { + const { name } = useCollection(); + const fieldSchema = useFieldSchema(); + const { form } = useFormBlockContext(); + const field = useField(); + const { service } = useKanbanBlockContext(); + const { dn } = useDesignable(); + return { + collectionName: name, + defaultFilter: fieldSchema?.['x-decorator-props']?.params?.filter || {}, + form, + onSubmit: ({ filter }) => { + filter = removeNullCondition(filter); + 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': fieldSchema['x-decorator-props'], + }, + }); + }, + }; + }, + }, + { + name: 'fixedBlock', + Component: FixedBlockDesignerItem, + }, + { + name: 'template', + Component: SchemaSettingsTemplate, + useComponentProps() { + const { name } = useCollection(); + return { + componentName: 'Kanban', + collectionName: name, + }; + }, + }, + + { + name: 'divider', + type: 'divider', + }, + + { + name: 'remove', + type: 'remove', + componentProps: { + removeParentsIfNoChildren: true, + breakRemoveOn: { + 'x-component': 'Grid', + }, + }, + }, + ], +}); diff --git a/packages/core/client/src/schema-component/antd/kanban/Kanban.tsx b/packages/plugins/@nocobase/plugin-kanban/src/client/Kanban.tsx similarity index 91% rename from packages/core/client/src/schema-component/antd/kanban/Kanban.tsx rename to packages/plugins/@nocobase/plugin-kanban/src/client/Kanban.tsx index f6e97fe80..fdcac8cdf 100644 --- a/packages/core/client/src/schema-component/antd/kanban/Kanban.tsx +++ b/packages/plugins/@nocobase/plugin-kanban/src/client/Kanban.tsx @@ -2,11 +2,8 @@ import { ArrayField } from '@formily/core'; import { observer, RecursionField, useField, useFieldSchema, useForm } from '@formily/react'; import { Spin, Tag } from 'antd'; import React, { useContext, useMemo, useState } from 'react'; -import { SchemaComponentOptions } from '../..'; -import { RecordProvider } from '../../../'; -import { useCreateActionProps as useCAP } from '../../../block-provider/hooks'; -import { Board } from '../../../board'; -import { useProps } from '../../hooks/useProps'; +import { SchemaComponentOptions, RecordProvider, useCreateActionProps as useCAP, useProps } from '@nocobase/client'; +import { Board } from './board'; import { KanbanCardContext, KanbanColumnContext } from './context'; import { useStyles } from './style'; @@ -55,7 +52,7 @@ export const toColumns = (groupField: any, dataSource: Array = []) => { export const Kanban: any = observer( (props: any) => { - const { wrapSSR, hashId, componentCls: className } = useStyles(); + const { styles } = useStyles(); const { groupField, onCardDragEnd, ...restProps } = useProps(props); const field = useField(); const fieldSchema = useFieldSchema(); @@ -86,9 +83,8 @@ export const Kanban: any = observer( const updatedBoard = Board.moveCard({ columns: field.value }, fromColumn, toColumn); field.value = updatedBoard.columns; }; - - return wrapSSR( - + return ( + - , + ); }, { displayName: 'Kanban' }, diff --git a/packages/core/client/src/schema-initializer/buttons/KanbanActionInitializers.tsx b/packages/plugins/@nocobase/plugin-kanban/src/client/KanbanActionInitializers.tsx similarity index 88% rename from packages/core/client/src/schema-initializer/buttons/KanbanActionInitializers.tsx rename to packages/plugins/@nocobase/plugin-kanban/src/client/KanbanActionInitializers.tsx index c67859ed7..0aad31f3e 100644 --- a/packages/core/client/src/schema-initializer/buttons/KanbanActionInitializers.tsx +++ b/packages/plugins/@nocobase/plugin-kanban/src/client/KanbanActionInitializers.tsx @@ -1,5 +1,4 @@ -import { useCollection } from '../../'; -import { SchemaInitializer } from '../../application/schema-initializer/SchemaInitializer'; +import { useCollection, SchemaInitializer } from '@nocobase/client'; export const kanbanActionInitializers = new SchemaInitializer({ name: 'KanbanActionInitializers', diff --git a/packages/core/client/src/schema-initializer/items/KanbanBlockInitializer.tsx b/packages/plugins/@nocobase/plugin-kanban/src/client/KanbanBlockInitializer.tsx similarity index 87% rename from packages/core/client/src/schema-initializer/items/KanbanBlockInitializer.tsx rename to packages/plugins/@nocobase/plugin-kanban/src/client/KanbanBlockInitializer.tsx index cd75a2252..38552ec90 100644 --- a/packages/core/client/src/schema-initializer/items/KanbanBlockInitializer.tsx +++ b/packages/plugins/@nocobase/plugin-kanban/src/client/KanbanBlockInitializer.tsx @@ -4,13 +4,18 @@ import { SchemaOptionsContext } from '@formily/react'; import React, { useContext } from 'react'; import { useTranslation } from 'react-i18next'; -import { useAPIClient } from '../../api-client'; -import { useCollectionManager } from '../../collection-manager'; -import { useGlobalTheme } from '../../global-theme'; -import { FormDialog, SchemaComponent, SchemaComponentOptions } from '../../schema-component'; -import { createKanbanBlockSchema } from '../utils'; -import { DataBlockInitializer } from './DataBlockInitializer'; -import { useSchemaInitializer, useSchemaInitializerItem } from '../../application'; +import { + useAPIClient, + useCollectionManager, + useGlobalTheme, + FormDialog, + SchemaComponent, + SchemaComponentOptions, + DataBlockInitializer, + useSchemaInitializer, + useSchemaInitializerItem, +} from '@nocobase/client'; +import { createKanbanBlockSchema } from './utils'; export const KanbanBlockInitializer = () => { const { insert } = useSchemaInitializer(); diff --git a/packages/core/client/src/block-provider/KanbanBlockProvider.tsx b/packages/plugins/@nocobase/plugin-kanban/src/client/KanbanBlockProvider.tsx similarity index 92% rename from packages/core/client/src/block-provider/KanbanBlockProvider.tsx rename to packages/plugins/@nocobase/plugin-kanban/src/client/KanbanBlockProvider.tsx index 2c8be3ea8..4df38cbac 100644 --- a/packages/core/client/src/block-provider/KanbanBlockProvider.tsx +++ b/packages/plugins/@nocobase/plugin-kanban/src/client/KanbanBlockProvider.tsx @@ -3,11 +3,15 @@ import { Schema, useField, useFieldSchema } from '@formily/react'; import { Spin } from 'antd'; import uniq from 'lodash/uniq'; import React, { createContext, useContext, useEffect } from 'react'; -import { useACLRoleContext } from '../acl'; -import { useCollection, useCollectionManager } from '../collection-manager'; -import { FixedBlockWrapper } from '../schema-component'; -import { toColumns } from '../schema-component/antd/kanban/Kanban'; -import { BlockProvider, useBlockRequestContext } from './BlockProvider'; +import { + useACLRoleContext, + useCollection, + useCollectionManager, + FixedBlockWrapper, + BlockProvider, + useBlockRequestContext, +} from '@nocobase/client'; +import { toColumns } from './Kanban'; export const KanbanBlockContext = createContext({}); @@ -133,7 +137,9 @@ export const useKanbanBlockProps = () => { const ctx = useKanbanBlockContext(); useEffect(() => { if (!ctx?.service?.loading) { - field.value = toColumns(ctx.groupField, ctx?.service?.data?.data); + setTimeout(() => { + field.value = toColumns(ctx.groupField, ctx?.service?.data?.data); + }); } // field.loading = ctx?.service?.loading; }, [ctx?.service?.loading]); diff --git a/packages/plugins/@nocobase/plugin-kanban/src/client/__e2e__/schemaInitailizer.test.ts b/packages/plugins/@nocobase/plugin-kanban/src/client/__e2e__/schemaInitailizer.test.ts new file mode 100644 index 000000000..66e98fcf7 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-kanban/src/client/__e2e__/schemaInitailizer.test.ts @@ -0,0 +1,117 @@ +import { test, expect } from '@nocobase/test/client'; +import { generalWithSingleSelect, oneEmptyKanbanBlock } from './utils'; + +//在页面中可以创建看板区块 +test.describe('blockInitializers should add kanban block', () => { + test('create kanban blocks in the page', async ({ page, mockPage, mockCollections, mockRecord }) => { + await mockCollections(generalWithSingleSelect); + await mockRecord('general'); + await mockPage().goto(); + await page.getByLabel('schema-initializer-Grid-BlockInitializers').hover(); + await expect(page.getByRole('menuitem', { name: 'form Kanban right' })).toBeVisible(); + await page.getByRole('menuitem', { name: 'form Kanban right' }).click(); + await page.getByRole('menuitem', { name: 'General' }).click(); + await page.getByLabel('block-item-Select-Grouping field').getByLabel('Search').click(); + await page.getByRole('option', { name: 'Single select' }).click(); + await page.getByRole('button', { name: 'OK' }).click(); + await expect(page.getByLabel('block-item-CardItem-general-kanban')).toBeVisible(); + }); +}); + +//配置字段 +test.describe('configure fields', () => { + test('configure normal field', async ({ page, mockPage, mockRecord }) => { + const nocoPage = await mockPage(oneEmptyKanbanBlock).waitForInit(); + await mockRecord('general', { singleLineText: 'singleLineText', manyToOne: { id: 1 } }); + await nocoPage.goto(); + //添加普通字段 + await page.getByLabel('block-item-Kanban.Card-general-kanban').hover(); + await page.getByLabel('designer-schema-initializer-Kanban.Card-Kanban.Card.Designer-general').hover(); + await page.getByRole('menuitem', { name: 'ID', exact: true }).click(); + await page.getByRole('menuitem', { name: 'Single line text' }).click(); + expect(await page.getByLabel('block-item-CollectionField-general-kanban-general.singleLineText').innerText()).toBe( + 'singleLineText', + ); + expect(await page.getByLabel('block-item-CollectionField-general-kanban-general.id').innerText()).toBe('1'); + //移除字段 + await page.getByLabel('block-item-Kanban.Card-general-kanban').hover(); + await page.getByLabel('designer-schema-initializer-Kanban.Card-Kanban.Card.Designer-general').hover(); + await page.getByRole('menuitem', { name: 'ID', exact: true }).click(); + await page.getByRole('menuitem', { name: 'Single line text' }).click(); + await expect( + await page.getByLabel('block-item-CollectionField-general-kanban-general.singleLineText'), + ).not.toBeVisible(); + await expect(await page.getByLabel('block-item-CollectionField-general-kanban-general.id')).not.toBeVisible(); + }); + test('configure association field', async ({ page, mockPage, mockRecord }) => { + const nocoPage = await mockPage(oneEmptyKanbanBlock).waitForInit(); + await mockRecord('general', { singleLineText: 'singleLineText', manyToOne: { id: 1 } }); + await nocoPage.goto(); + await page.getByLabel('block-item-Kanban.Card-general-kanban').hover(); + await page.getByLabel('designer-schema-initializer-Kanban.Card-Kanban.Card.Designer-general').hover(); + //添加关系字段,断言请求的appends是否符合预期 + const [request] = await Promise.all([ + page.waitForRequest((request) => request.url().includes('api/general:list')), + page.getByRole('menuitem', { name: 'Many to one', exact: true }).click(), + ]); + // 获取请求参数 + const requestUrl = request.url(); + // 解析查询参数 + const queryParams = new URLSearchParams(new URL(requestUrl).search); + const appends = queryParams.get('appends[]'); + await expect(appends).toContain('manyToOne'); + await expect(await page.getByLabel('block-item-CollectionField-general-kanban-general.manyToOne')).toBeVisible(); + //修改标题字段 + await page.getByLabel('schema-initializer-ActionBar-KanbanActionInitializers-general').click(); + await page.getByLabel('block-item-CollectionField-general-kanban-general.manyToOne').hover(); + await page + .getByLabel('designer-schema-settings-CollectionField-FormItem.Designer-general-general.manyToOne') + .hover(); + await page.getByText('Title field').click(); + await page.getByRole('option', { name: 'Username' }).click(); + //显示新的标题字段符合预期 + expect(await page.getByLabel('block-item-CollectionField-general-kanban-general.manyToOne').innerText()).toBe( + 'nocobase', + ); + }); +}); + +//看板的操作配置 +test.describe('configure actions', () => { + test('filter action in kanban', async ({ page, mockPage }) => { + const nocoPage = await mockPage(oneEmptyKanbanBlock).waitForInit(); + await nocoPage.goto(); + await expect(page.getByLabel('block-item-CardItem-general-kanban')).toBeVisible(); + await expect(page.getByLabel('schema-initializer-ActionBar-KanbanActionInitializers-general')).toBeVisible(); + await page.getByLabel('schema-initializer-ActionBar-KanbanActionInitializers-general').click(); + await page.getByRole('menuitem', { name: 'Filter' }).getByRole('switch').click(); + //按钮正常显示 + await expect(page.getByLabel('action-Filter.Action-Filter-filter-general-kanban')).toBeVisible(); + }); + test('add new action in kanban', async ({ page, mockPage }) => { + const nocoPage = await mockPage(oneEmptyKanbanBlock).waitForInit(); + await nocoPage.goto(); + await page.getByLabel('schema-initializer-ActionBar-KanbanActionInitializers-general').click(); + await page.getByRole('menuitem', { name: 'Add new' }).getByRole('switch').click(); + //按钮正常显示 + await expect(page.getByLabel('action-Action-Add ')).toBeVisible(); + //添加数据 + await page.getByLabel('action-Action-Add new-create-general-kanban').click(); + await page.getByLabel('schema-initializer-Grid-CreateFormBlockInitializers-general').click(); + await page.getByRole('menuitem', { name: 'form Form' }).click(); + await page.mouse.move(300, 0); + await page.getByLabel('schema-initializer-Grid-FormItemInitializers-general').click(); + await page.getByRole('menuitem', { name: 'Single Select' }).click(); + await page.getByLabel('Search').click(); + await page.getByRole('option', { name: 'option1' }).click(); + await page.getByLabel('schema-initializer-ActionBar-CreateFormActionInitializers-general').hover(); + await page.getByRole('menuitem', { name: 'Submit' }).click(); + await page.getByLabel('action-Action-Submit-submit-general-form').click(); + await page.getByLabel('block-item-Kanban.Card-general-kanban').hover(); + await page.getByLabel('designer-schema-initializer-Kanban.Card-Kanban.Card.Designer-general').hover(); + await page.getByRole('menuitem', { name: 'ID', exact: true }).click(); + await page.getByRole('menuitem', { name: 'Created at' }).getByRole('switch').click(); + await page.getByRole('menuitem', { name: 'Single Select' }).getByRole('switch').click(); + await expect(await page.getByLabel('block-item-Kanban.Card-general-kanban')).toBeVisible(); + }); +}); diff --git a/packages/plugins/@nocobase/plugin-kanban/src/client/__e2e__/schemaSettings.test.ts b/packages/plugins/@nocobase/plugin-kanban/src/client/__e2e__/schemaSettings.test.ts new file mode 100644 index 000000000..3f8d710d0 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-kanban/src/client/__e2e__/schemaSettings.test.ts @@ -0,0 +1,53 @@ +import { test, expect } from '@nocobase/test/client'; +import { oneEmptyKanbanBlock } from './utils'; + +//看板的区块参数配置 +test.describe('configure setting', () => { + test('fix block ', async ({ page, mockPage, mockRecords }) => { + const nocoPage = await mockPage(oneEmptyKanbanBlock).waitForInit(); + await mockRecords('general', 100); + await nocoPage.goto(); + await expect(page.getByLabel('block-item-CardItem-general-kanban')).toBeVisible(); + //固定区块 + await page.getByLabel('block-item-CardItem-general-kanban').hover(); + await page.getByLabel('designer-schema-settings-CardItem-Kanban.Designer-general').hover(); + await page.getByRole('menuitem', { name: 'Fix block' }).click(); + const kanbanBoard = page.getByLabel('block-item-CardItem-general-kanban'); + const kanbanBoardHeight = await kanbanBoard.evaluate((element) => { + const computedStyle = window.getComputedStyle(element); + return parseInt(computedStyle.height, 10); + }); + const windowHeight = await page.evaluate(() => { + return window.innerHeight; + }); + const expectedHeight = windowHeight - 147; + await page.waitForTimeout(1000); + // 断言高度为预期值 + expect(kanbanBoardHeight).toBe(expectedHeight); + }); + test('set the data scope', async ({ page, mockPage, mockRecords }) => { + const nocoPage = await mockPage(oneEmptyKanbanBlock).waitForInit(); + await mockRecords('general', 10); + await nocoPage.goto(); + await expect(page.getByLabel('block-item-CardItem-general-kanban')).toBeVisible(); + //设置数据范围 + await page.getByLabel('block-item-CardItem-general-kanban').hover(); + await page.getByLabel('designer-schema-settings-CardItem-Kanban.Designer-general').hover(); + await page.getByRole('menuitem', { name: 'Set the data scope' }).click(); + await page.getByText('Add condition', { exact: true }).click(); + await page.getByTestId('select-filter-field').getByLabel('Search').click(); + await page.getByTitle('ID').click(); + await page.getByRole('spinbutton').fill('1'); + const [request] = await Promise.all([ + page.waitForRequest((request) => request.url().includes('api/general:list')), + page.getByRole('button', { name: 'OK' }).click(), + ]); + const requestUrl = request.url(); + const queryParams = new URLSearchParams(new URL(requestUrl).search); + const filter = queryParams.get('filter'); + //请求参数符合预期,数据显示符合预期 + expect(JSON.parse(filter)).toEqual({ $and: [{ id: { $eq: 1 } }] }); + await expect(page.getByTestId('card-1')).toBeVisible(); + await expect(page.getByTestId('card-2')).not.toBeVisible(); + }); +}); diff --git a/packages/plugins/@nocobase/plugin-kanban/src/client/__e2e__/utils.ts b/packages/plugins/@nocobase/plugin-kanban/src/client/__e2e__/utils.ts new file mode 100644 index 000000000..0e32647e2 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-kanban/src/client/__e2e__/utils.ts @@ -0,0 +1,285 @@ +import { CollectionSetting, PageConfig } from '@nocobase/test/client'; + +/** + * 1. 创建一个名为 general 的 collection,其包含 single select 类型的字段 + */ +export const generalWithSingleSelect: CollectionSetting[] = [ + { + name: 'general', + title: 'General', + fields: [ + { + name: 'singleLineText', + interface: 'input', + uiSchema: { + type: 'string', + 'x-component': 'Input', + title: 'Single line text', + }, + }, + { + name: 'singleSelect_sort', + interface: 'sort', + scopeKey: 'singleSelect', + hidden: true, + type: 'sort', + }, + { + name: 'singleSelect', + interface: 'select', + uiSchema: { + enum: [ + { + value: 'option1', + label: 'option1', + color: 'red', + }, + { + value: 'option2', + label: 'option2', + color: 'magenta', + }, + { + value: 'option3', + label: 'option3', + color: 'volcano', + }, + ], + type: 'string', + 'x-component': 'Select', + title: 'Single select', + }, + }, + { + name: 'f_t22o7loai3j', + interface: 'integer', + isForeignKey: true, + uiSchema: { + type: 'number', + title: 'f_t22o7loai3j', + 'x-component': 'InputNumber', + 'x-read-pretty': true, + }, + }, + { + name: 'manyToOne', + interface: 'm2o', + foreignKey: 'f_t22o7loai3j', + uiSchema: { + 'x-component': 'AssociationField', + 'x-component-props': { + multiple: false, + fieldNames: { + label: 'id', + value: 'id', + }, + }, + title: 'Many to one', + }, + target: 'users', + targetKey: 'id', + }, + ], + }, +]; + +export const oneEmptyKanbanBlock: PageConfig = { + collections: generalWithSingleSelect, + pageSchema: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Page', + properties: { + sp1e0rp3272: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid', + 'x-initializer': 'BlockInitializers', + properties: { + '4io56n1mje4': { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Row', + properties: { + ltnu6re5ctl: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Col', + properties: { + v8nqwpajlqi: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-acl-action': 'general:list', + 'x-decorator': 'KanbanBlockProvider', + 'x-decorator-props': { + collection: 'general', + resource: 'general', + action: 'list', + groupField: 'singleSelect', + params: { + sort: ['singleSelect_sort'], + paginate: false, + }, + }, + 'x-designer': 'Kanban.Designer', + 'x-component': 'CardItem', + properties: { + actions: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-initializer': 'KanbanActionInitializers', + 'x-component': 'ActionBar', + 'x-component-props': { + style: { + marginBottom: 'var(--nb-spacing)', + }, + }, + 'x-uid': 'p8urndbswek', + 'x-async': false, + 'x-index': 1, + }, + '1g2kx5ld8wj': { + _isJSONSchemaObject: true, + version: '2.0', + type: 'array', + 'x-component': 'Kanban', + 'x-component-props': { + useProps: '{{ useKanbanBlockProps }}', + }, + properties: { + card: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-read-pretty': true, + 'x-label-disabled': true, + 'x-decorator': 'BlockItem', + 'x-component': 'Kanban.Card', + 'x-component-props': { + openMode: 'drawer', + }, + 'x-designer': 'Kanban.Card.Designer', + properties: { + grid: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid', + 'x-component-props': { + dndContext: false, + }, + 'x-uid': 'ab07zz24rx2', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'lx8jojqoxak', + 'x-async': false, + 'x-index': 1, + }, + cardViewer: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + title: '{{ t("View") }}', + 'x-designer': 'Action.Designer', + 'x-component': 'Kanban.CardViewer', + 'x-action': 'view', + 'x-component-props': { + openMode: 'drawer', + }, + properties: { + drawer: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + title: '{{ t("View record") }}', + 'x-component': 'Action.Container', + 'x-component-props': { + className: 'nb-action-popup', + }, + properties: { + tabs: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Tabs', + 'x-component-props': {}, + 'x-initializer': 'TabPaneInitializers', + properties: { + tab1: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + title: '{{t("Details")}}', + 'x-component': 'Tabs.TabPane', + 'x-designer': 'Tabs.Designer', + 'x-component-props': {}, + properties: { + grid: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid', + 'x-initializer': 'RecordBlockInitializers', + 'x-uid': '2hsi7zy93g6', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'i7jfppu1oy5', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'hgl0a2a3506', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'vy24wl1hda3', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '1k5u78s6hxm', + 'x-async': false, + 'x-index': 2, + }, + }, + 'x-uid': 'wila2fay8gp', + 'x-async': false, + 'x-index': 2, + }, + }, + 'x-uid': '3yci31krdmf', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '3zk2v89pmxk', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'hkvyce0eer5', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '9tev3j3rxtd', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'imkzeo7w9u6', + 'x-async': true, + 'x-index': 1, + }, +}; diff --git a/packages/core/client/src/board/Board.tsx b/packages/plugins/@nocobase/plugin-kanban/src/client/board/Board.tsx similarity index 98% rename from packages/core/client/src/board/Board.tsx rename to packages/plugins/@nocobase/plugin-kanban/src/client/board/Board.tsx index 19645d8f8..cac2a91e8 100644 --- a/packages/core/client/src/board/Board.tsx +++ b/packages/plugins/@nocobase/plugin-kanban/src/client/board/Board.tsx @@ -27,7 +27,7 @@ const Board: any = (props) => { const { styles } = useStyles(); return ( -
+
{props.initialBoard ? : }
); @@ -250,7 +250,7 @@ function BoardContainer(props) { onCardNew, allowAddCard, } = props; - + const { styles } = useStyles(); function handleOnDragEnd(event) { const coordinates = getCoordinates(event, board); if (!coordinates.source) return; @@ -263,7 +263,7 @@ function BoardContainer(props) { } return ( -
+
{board.columns?.map((column, index) => ( { + return { + nbBord: css` + .react-kanban-board { + height: 100%; + // padding: 5px; + // margin-bottom: 24px; + } + + .react-kanban-card { + box-sizing: border-box; + max-width: 250px; + min-width: 250px; + border-radius: 3px; + background-color: #fff; + padding: 10px; + margin-bottom: 7px; + } + + .react-kanban-card-skeleton { + box-sizing: border-box; + max-width: 300px; + min-width: 300px; + height: 70vh; + overflow-x: hidden; + overflow-y: auto; + padding: 0 12px; + margin-bottom: 12px; + > div { + margin-bottom: 12px; + } + + div { + display: none !important; + } + } + + .react-kanban-card--dragging { + box-shadow: 2px 2px grey; + } + + .react-kanban-card__description { + padding-top: 10px; + } + + .react-kanban-card__title { + border-bottom: 1px solid #eee; + padding-bottom: 5px; + font-weight: bold; + display: flex; + justify-content: space-between; + } + + .react-kanban-column { + background-color: ${token.colorFillQuaternary}; + margin-right: 15px; + padding-bottom: 15px; + width: 300px; + } + + .react-kanban-column-header { + padding: 15px; + } + + .react-kanban-card-adder-form { + box-sizing: border-box; + max-width: 250px; + min-width: 250px; + border-radius: 3px; + background-color: #fff; + padding: 10px; + margin-bottom: 7px; + input { + border: 0px; + font-family: inherit; + font-size: inherit; + } + } + + .react-kanban-card-adder-button { + width: 100%; + margin-top: 5px; + background-color: transparent; + cursor: pointer; + border: 1px solid #ccc; + transition: 0.3s; + border-radius: 3px; + font-size: 20px; + margin-bottom: 10px; + font-weight: bold; + &:hover { + background-color: #ccc; + } + } + + .react-kanban-card-adder-form__title { + font-weight: bold; + border-bottom: 1px solid #eee; + padding-bottom: 5px; + font-weight: bold; + display: flex; + justify-content: space-between; + width: 100%; + padding: 0px; + &:focus { + outline: none; + } + } + + .react-kanban-card-adder-form__description { + width: 100%; + margin-top: 10px; + &:focus { + outline: none; + } + } + + .react-kanban-card-adder-form__button { + background-color: #eee; + border: none; + padding: 5px; + width: 45%; + margin-top: 5px; + border-radius: 3px; + &:hover { + transition: 0.3s; + cursor: pointer; + background-color: #ccc; + } + } + + .react-kanban-column-header { + } + + .react-kanban-column-header__button { + color: #333333; + background-color: #ffffff; + border-color: #cccccc; + + &:hover, + &:focus, + &:active { + background-color: #e6e6e6; + } + } + + .react-kanban-column-adder-button { + border: 2px dashed #eee; + height: 132px; + margin: 5px; + + &:hover { + cursor: pointer; + } + } + `, + kanbanBoard: css` + height: 100%; + `, + }; +}); diff --git a/packages/core/client/src/board/utils.ts b/packages/plugins/@nocobase/plugin-kanban/src/client/board/utils.ts similarity index 100% rename from packages/core/client/src/board/utils.ts rename to packages/plugins/@nocobase/plugin-kanban/src/client/board/utils.ts diff --git a/packages/core/client/src/board/withDroppable.tsx b/packages/plugins/@nocobase/plugin-kanban/src/client/board/withDroppable.tsx similarity index 100% rename from packages/core/client/src/board/withDroppable.tsx rename to packages/plugins/@nocobase/plugin-kanban/src/client/board/withDroppable.tsx diff --git a/packages/core/client/src/schema-component/antd/kanban/context.ts b/packages/plugins/@nocobase/plugin-kanban/src/client/context.ts similarity index 100% rename from packages/core/client/src/schema-component/antd/kanban/context.ts rename to packages/plugins/@nocobase/plugin-kanban/src/client/context.ts diff --git a/packages/core/client/src/schema-component/antd/kanban/index.md b/packages/plugins/@nocobase/plugin-kanban/src/client/index.md similarity index 100% rename from packages/core/client/src/schema-component/antd/kanban/index.md rename to packages/plugins/@nocobase/plugin-kanban/src/client/index.md diff --git a/packages/plugins/@nocobase/plugin-kanban/src/client/index.tsx b/packages/plugins/@nocobase/plugin-kanban/src/client/index.tsx new file mode 100644 index 000000000..2bbb65b6a --- /dev/null +++ b/packages/plugins/@nocobase/plugin-kanban/src/client/index.tsx @@ -0,0 +1,59 @@ +import React, { useContext } from 'react'; +import { + Plugin, + Action, + CurrentAppInfoProvider, + SchemaComponentOptions, + CollectionManagerContext, +} from '@nocobase/client'; +import { Kanban } from './Kanban'; +import { KanbanCard } from './Kanban.Card'; +import { KanbanCardDesigner, kanbanCardInitializers } from './Kanban.Card.Designer'; +import { KanbanCardDesignerTitleSwitch } from './Kanban.Card.Designer.TitleSwitch'; +import { KanbanCardViewer } from './Kanban.CardViewer'; +import { KanbanDesigner } from './Kanban.Designer'; +import { kanbanActionInitializers } from './KanbanActionInitializers'; +import { KanbanBlockProvider, useKanbanBlockProps } from './KanbanBlockProvider'; +import { KanbanBlockInitializer } from './KanbanBlockInitializer'; +import { kanbanSettings } from './Kanban.Settings'; + +Kanban.Card = KanbanCard; +Kanban.CardAdder = Action; +Kanban.CardViewer = KanbanCardViewer; +Kanban.Card.Designer = KanbanCardDesigner; +Kanban.Card.Designer.TitleSwitch = KanbanCardDesignerTitleSwitch; +Kanban.Designer = KanbanDesigner; + +const KanbanV2 = Kanban; + +const KanbanPluginProvider = React.memo((props) => { + const ctx = useContext(CollectionManagerContext); + return ( + + + {props.children} + + + ); +}); +KanbanPluginProvider.displayName = 'KanbanPluginProvider'; + +class KanbanPlugin extends Plugin { + async load() { + this.app.use(KanbanPluginProvider); + this.app.schemaInitializerManager.add(kanbanCardInitializers); + this.app.schemaInitializerManager.add(kanbanActionInitializers); + this.app.schemaSettingsManager.add(kanbanSettings); + + const blockInitializers = this.app.schemaInitializerManager.get('BlockInitializers'); + blockInitializers?.add('dataBlocks.kanban', { + title: '{{t("Kanban")}}', + Component: 'KanbanBlockInitializer', + }); + } +} + +export default KanbanPlugin; diff --git a/packages/plugins/@nocobase/plugin-kanban/src/client/locale/index.ts b/packages/plugins/@nocobase/plugin-kanban/src/client/locale/index.ts new file mode 100644 index 000000000..016d63abe --- /dev/null +++ b/packages/plugins/@nocobase/plugin-kanban/src/client/locale/index.ts @@ -0,0 +1,21 @@ +import { i18n } from '@nocobase/client'; +import { useTranslation } from 'react-i18next'; + +export const NAMESPACE = 'kanban'; + +// i18n.addResources('zh-CN', NAMESPACE, zhCN); +// i18n.addResources('en-US', NAMESPACE, enUS); + +export function lang(key: string) { + return i18n.t(key, { ns: NAMESPACE }); +} + +export function generateNTemplate(key: string) { + return `{{t('${key}', { ns: '${NAMESPACE}', nsMode: 'fallback' })}}`; +} + +export function useKanbanTranslation() { + return useTranslation(NAMESPACE, { + nsMode: 'fallback', + }); +} diff --git a/packages/plugins/@nocobase/plugin-kanban/src/client/style.tsx b/packages/plugins/@nocobase/plugin-kanban/src/client/style.tsx new file mode 100644 index 000000000..e9bfbbe1a --- /dev/null +++ b/packages/plugins/@nocobase/plugin-kanban/src/client/style.tsx @@ -0,0 +1,18 @@ +import { createStyles } from '@nocobase/client'; + +export const useStyles = createStyles(({ token, css }) => { + return { + nbKanban: css` + overflow: hidden; + height: 100%; + overflow-y: auto; + .ant-spin-container: { + height: 100%; + .ant-formily-item-label: { + color: #8c8c8c; + fontweight: normal; + } + } + `, + }; +}); diff --git a/packages/plugins/@nocobase/plugin-kanban/src/client/utils.ts b/packages/plugins/@nocobase/plugin-kanban/src/client/utils.ts new file mode 100644 index 000000000..1d435d05a --- /dev/null +++ b/packages/plugins/@nocobase/plugin-kanban/src/client/utils.ts @@ -0,0 +1,109 @@ +import { ISchema } from '@formily/react'; +import { uid } from '@formily/shared'; + +export const createKanbanBlockSchema = (options) => { + const { collection, resource, groupField, ...others } = options; + const schema: ISchema = { + type: 'void', + 'x-acl-action': `${resource || collection}:list`, + 'x-decorator': 'KanbanBlockProvider', + 'x-decorator-props': { + collection: collection, + resource: resource || collection, + action: 'list', + groupField, + params: { + paginate: false, + }, + ...others, + }, + 'x-designer': 'Kanban.Designer', + 'x-component': 'CardItem', + properties: { + actions: { + type: 'void', + 'x-initializer': 'KanbanActionInitializers', + 'x-component': 'ActionBar', + 'x-component-props': { + style: { + marginBottom: 'var(--nb-spacing)', + }, + }, + properties: {}, + }, + [uid()]: { + type: 'array', + 'x-component': 'Kanban', + 'x-component-props': { + useProps: '{{ useKanbanBlockProps }}', + }, + properties: { + card: { + type: 'void', + 'x-read-pretty': true, + 'x-label-disabled': true, + 'x-decorator': 'BlockItem', + 'x-component': 'Kanban.Card', + 'x-component-props': { + openMode: 'drawer', + }, + 'x-designer': 'Kanban.Card.Designer', + properties: { + grid: { + type: 'void', + 'x-component': 'Grid', + 'x-component-props': { dndContext: false }, + }, + }, + }, + cardViewer: { + type: 'void', + title: '{{ t("View") }}', + 'x-designer': 'Action.Designer', + 'x-component': 'Kanban.CardViewer', + 'x-action': 'view', + 'x-component-props': { + openMode: 'drawer', + }, + properties: { + drawer: { + type: 'void', + title: '{{ t("View record") }}', + 'x-component': 'Action.Container', + '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: {}, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }; + return schema; +}; diff --git a/packages/plugins/@nocobase/plugin-kanban/src/index.ts b/packages/plugins/@nocobase/plugin-kanban/src/index.ts new file mode 100644 index 000000000..7e74612df --- /dev/null +++ b/packages/plugins/@nocobase/plugin-kanban/src/index.ts @@ -0,0 +1,2 @@ +export * from './server'; +export { default } from './server'; diff --git a/packages/plugins/@nocobase/plugin-kanban/src/locale/en-US.ts b/packages/plugins/@nocobase/plugin-kanban/src/locale/en-US.ts new file mode 100644 index 000000000..2d905d194 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-kanban/src/locale/en-US.ts @@ -0,0 +1,3 @@ +const locale = {}; + +export default locale; diff --git a/packages/plugins/@nocobase/plugin-kanban/src/locale/fr-FR.ts b/packages/plugins/@nocobase/plugin-kanban/src/locale/fr-FR.ts new file mode 100644 index 000000000..2d905d194 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-kanban/src/locale/fr-FR.ts @@ -0,0 +1,3 @@ +const locale = {}; + +export default locale; diff --git a/packages/plugins/@nocobase/plugin-kanban/src/locale/pt-BR.ts b/packages/plugins/@nocobase/plugin-kanban/src/locale/pt-BR.ts new file mode 100644 index 000000000..1f7d9da07 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-kanban/src/locale/pt-BR.ts @@ -0,0 +1,51 @@ +const locale = { + 'Map-based geometry': 'Geometria baseada em mapa', + 'Map type': 'Tipo de mapa', + Point: 'Ponto', + Line: 'Linha', + Circle: 'Círculo', + Polygon: 'Polígono', + 'Access key': 'Chave de acesso', + 'securityJsCode or serviceHost': 'securityJsCode ou serviceHost', + AMap: 'AMap', + 'Google Maps': 'Google Maps', + Clear: 'Limpar', + 'Click to select the starting point and double-click to end the drawing': + 'Clique para selecionar o ponto de partida e dê um duplo clique para terminar a criação', + 'Clear the canvas': 'Limpar a tela', + 'Are you sure to clear the canvas?': 'Tem certeza de que deseja limpar a tela?', + Confirm: 'Confirmar', + Cancel: 'Cancelar', + 'Enter keywords to search': 'Digite palavras-chave para buscar', + 'The AccessKey is incorrect, please check it': 'A chave de acesso está incorreta, por favor verifique', + 'Please configure the AMap securityCode or serviceHost correctly': + 'Por favor, configure o securityCode ou serviceHost do AMap corretamente', + 'Map Manager': 'Gerenciador de Mapa', + Configuration: 'Configuração', + 'Saved successfully': 'Salvo com sucesso', + 'Saved failed': 'Falha ao salvar', + Edit: 'Editar', + Save: 'Salvar', + 'Please configure the AccessKey and SecurityJsCode first': + 'Por favor, configure a AccessKey e o SecurityJsCode primeiro', + 'Go to the configuration page': 'Ir para a página de configuração', + Zoom: 'Zoom', + 'Set default zoom level': 'Definir nível de zoom padrão', + 'The default zoom level of the map': 'O nível de zoom padrão do mapa', + // Designer + 'Edit field title': 'Editar título do campo', + 'Field title': 'Título do campo', + 'Edit tooltip': 'Editar dica de ferramenta', + 'Delete field': 'Excluir campo', + Required: 'Obrigatório', + Pattern: 'Padrão', + Operator: 'Operador', + Editable: 'Editável', + Readonly: 'Somente leitura', + 'Easy-reading': 'Apenas leitura (Modo de Leitura)', + 'Edit description': 'Editar descrição', + 'Map field': 'Campo de mapa', + 'Marker field': 'Campo de marcador', +}; + +export default locale; diff --git a/packages/plugins/@nocobase/plugin-kanban/src/locale/zh-CN.ts b/packages/plugins/@nocobase/plugin-kanban/src/locale/zh-CN.ts new file mode 100644 index 000000000..8c6d1f960 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-kanban/src/locale/zh-CN.ts @@ -0,0 +1,55 @@ +const locale = { + Map: '地图', + 'Map-based geometry': '基于地图的几何图形', + 'Map type': '地图类型', + Point: '点', + Line: '线', + Circle: '圆', + Polygon: '多边形', + 'Access key': '访问密钥', + 'securityJsCode or serviceHost': 'securityJsCode 或 serviceHost', + AMap: '高德地图', + 'Google Maps': '谷歌地图', + Clear: '清空', + 'Click to select the starting point and double-click to end the drawing': '点击选择起点,双击结束绘制', + 'Clear the canvas': '清空画布', + 'Are you sure to clear the canvas?': '您确定要清空画布吗?', + Confirm: '确定', + Cancel: '取消', + 'Enter keywords to search': '输入地方名关键字搜索(必须包含省/市)', + 'The AccessKey is incorrect, please check it': '访问密钥不正确,请检查', + 'Please configure the AMap securityCode or serviceHost correctly': '请正确配置高德地图 securityCode 或 serviceHost', + 'Map Manager': '地图管理', + Configuration: '配置', + 'Saved successfully': '保存成功', + 'Saved failed': '保存失败', + Edit: '编辑', + Save: '保存', + 'Please configure the AccessKey and SecurityJsCode first': '请先配置 AccessKey 和 SecurityJsCode', + 'Go to the configuration page': '前往配置页面', + Zoom: '缩放', + 'Set default zoom level': '设置默认缩放级别', + 'The default zoom level of the map': '地图默认缩放级别', + // Designer + 'Edit field title': '编辑字段标题', + 'Field title': '字段标题', + 'Edit tooltip': '编辑提示信息', + 'Delete field': '删除字段', + Required: '必填', + Pattern: '模式', + Operator: '运算符', + Editable: '可编辑', + Readonly: '只读(禁止编辑)', + 'Easy-reading': '只读(阅读模式)', + 'Edit description': '编辑描述', + 'Map field': '地图字段', + 'Marker field': '标记字段', + 'Load google maps failed, Please check the Api key and refresh the page': + '加载谷歌地图失败,请检查 Api key 并刷新页面', + 'Create map block': '创建地图区块', + 'Start point': '起点', + 'End point': '终点', + 'Concatenation order field': '连接顺序字段', +}; + +export default locale; diff --git a/packages/plugins/@nocobase/plugin-kanban/src/server/collections/.gitkeep b/packages/plugins/@nocobase/plugin-kanban/src/server/collections/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/packages/plugins/@nocobase/plugin-kanban/src/server/index.ts b/packages/plugins/@nocobase/plugin-kanban/src/server/index.ts new file mode 100644 index 000000000..b68aea57f --- /dev/null +++ b/packages/plugins/@nocobase/plugin-kanban/src/server/index.ts @@ -0,0 +1 @@ +export { default } from './plugin'; diff --git a/packages/plugins/@nocobase/plugin-kanban/src/server/plugin.ts b/packages/plugins/@nocobase/plugin-kanban/src/server/plugin.ts new file mode 100644 index 000000000..ba3739ae8 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-kanban/src/server/plugin.ts @@ -0,0 +1,19 @@ +import { InstallOptions, Plugin } from '@nocobase/server'; + +export class PluginKanbanServer extends Plugin { + afterAdd() {} + + beforeLoad() {} + + async load() {} + + async install(options?: InstallOptions) {} + + async afterEnable() {} + + async afterDisable() {} + + async remove() {} +} + +export default PluginKanbanServer; diff --git a/packages/plugins/@nocobase/plugin-map/src/client/block/MapActionInitializers.tsx b/packages/plugins/@nocobase/plugin-map/src/client/block/MapActionInitializers.tsx index 87f40fc7a..d1bbf41fd 100644 --- a/packages/plugins/@nocobase/plugin-map/src/client/block/MapActionInitializers.tsx +++ b/packages/plugins/@nocobase/plugin-map/src/client/block/MapActionInitializers.tsx @@ -60,52 +60,7 @@ export const mapActionInitializers = new SchemaInitializer({ type: 'subMenu', title: '{{t("Customize")}}', name: 'customize', - children: [ - { - name: 'bulkUpdate', - title: '{{t("Bulk update")}}', - Component: 'CustomizeActionInitializer', - schema: { - type: 'void', - title: '{{ t("Bulk update") }}', - 'x-component': 'Action', - 'x-align': 'right', - 'x-acl-action': 'update', - 'x-decorator': 'ACLActionProvider', - 'x-acl-action-props': { - skipScopeCheck: true, - }, - 'x-action': 'customize:bulkUpdate', - 'x-designer': 'Action.Designer', - 'x-action-settings': { - assignedValues: {}, - updateMode: 'selected', - onSuccess: { - manualClose: true, - redirecting: false, - successMessage: '{{t("Updated successfully")}}', - }, - }, - 'x-component-props': { - icon: 'EditOutlined', - useProps: '{{ useCustomizeBulkUpdateActionProps }}', - }, - }, - }, - { - name: 'bulkEdit', - title: '{{t("Bulk edit")}}', - Component: 'CustomizeBulkEditActionInitializer', - schema: { - 'x-align': 'right', - 'x-decorator': 'ACLActionProvider', - 'x-acl-action': 'update', - 'x-acl-action-props': { - skipScopeCheck: true, - }, - }, - }, - ], + children: [], useVisible() { const collection = useCollection(); return collection.template !== 'sql'; diff --git a/packages/presets/nocobase/package.json b/packages/presets/nocobase/package.json index 1e3f02d51..8123af749 100644 --- a/packages/presets/nocobase/package.json +++ b/packages/presets/nocobase/package.json @@ -54,6 +54,12 @@ "@nocobase/plugin-workflow-parallel": "0.17.0-alpha.7", "@nocobase/plugin-workflow-request": "0.17.0-alpha.7", "@nocobase/plugin-workflow-sql": "0.17.0-alpha.7", + "@nocobase/plugin-kanban": "0.17.0-alpha.7", + "@nocobase/plugin-gantt": "0.17.0-alpha.7", + "@nocobase/plugin-action-bulk-update": "0.17.0-alpha.7", + "@nocobase/plugin-action-bulk-edit": "0.17.0-alpha.7", + "@nocobase/plugin-action-duplicate": "0.17.0-alpha.7", + "@nocobase/plugin-action-print": "0.17.0-alpha.7", "@nocobase/server": "0.17.0-alpha.7", "cronstrue": "^2.11.0" }, diff --git a/packages/presets/nocobase/src/server/index.ts b/packages/presets/nocobase/src/server/index.ts index ef9592b60..a7d7a2387 100644 --- a/packages/presets/nocobase/src/server/index.ts +++ b/packages/presets/nocobase/src/server/index.ts @@ -36,6 +36,12 @@ export class PresetNocoBase extends Plugin { 'auth', 'sms-auth', 'custom-request', + 'action-bulk-update', + 'action-bulk-edit', + 'gantt', + 'kanban', + 'action-duplicate', + 'action-print', ]; localPlugins = [