From 0e70e3848a4abc79225d7ea0fc918df53e79f72c Mon Sep 17 00:00:00 2001 From: chenos Date: Wed, 2 Nov 2022 22:13:25 +0800 Subject: [PATCH] feat: improve collection manager (#1013) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 图形化管理数据表 * feat: 图形化管理数据表 * feat: 图形化管理数据表 * feat: 图形化管理数据表 * feat: 完善图形化管理数据表 * feat: 完善图形化管理数据表 * feat: 完善图形化管理数据表 * feat: 完善图形化管理数据表 * feat: 完善图形化管理数据表 * feat: 完善图形化管理数据表 * feat: 完善图形化管理数据表 * feat: 完善图形化管理数据表 * feat: 完善图形化管理数据表 * feat: 完善图形化管理数据表 * feat: 完善图形化管理数据表 * feat: 完善图形化管理数据表 * feat: 完善图形化管理数据表 * feat: 完善图形化管理数据表 * feat: 完善图形化管理数据表 * feat: 完善图形化管理数据表 * feat: 完善图形化管理数据表 * feat: 样式优化图形化管理数据表 * feat: 样式优化图形化管理数据表 * feat: 样式优化图形化管理数据表 * feat: 样式优化图形化管理数据表 * feat: 样式优化图形化管理数据表 * feat: 样式优化图形化管理数据表 * feat: 样式优化图形化管理数据表 * feat: 样式优化图形化管理数据表 * feat: 样式优化图形化管理数据表 * feat: 样式优化图形化管理数据表 * feat: 样式优化图形化管理数据表 * feat: 样式优化图形化管理数据表 * feat: 样式优化图形化管理数据表 * feat: 样式优化图形化管理数据表 * feat: 样式优化图形化管理数据表 * feat: 样式优化图形化管理数据表 * feat: 样式优化图形化管理数据表 * feat: 样式优化图形化管理数据表 * feat: 样式优化图形化管理数据表 * feat: 样式优化图形化管理数据表 * feat: 样式优化图形化管理数据表 * feat(collection-manager): add foreignKey Field and support relate field record foreignKey info through collection record into collections and foreignKey field record info fields * fix(collection-manager): if has through collection then don't create through collections record * fix(client/route-switch): skip sub routes * feat: 添加graphpostion * feat: 图形化collection新增表时刷新数据 * fix(collection-manager): refactor afterCreateForRelateField * feat: 图形化collection存储位置 * feat: 图形化collection存储位置 * feat: 图形化collection存储位置 * feat: 图形化collection存储位置 * feat: 图形化collection存储位置 * feat: 图形化collection存储位置 * feat: 图形化collection存储位置 * feat: 图形化collection存储位置 * feat: 图形化collection存储位置 * feat: 图形化collection存储位置 * feat: 图形化样式优化 * feat: styling * feat: 图形化样式优化 * feat: 图形化样式优化 * feat: 图形化数据表多语言完善 * feat: 图形化数据表多语言完善 * feat: improve code * feat: 图形化数据表连线样式修改 * feat: 图形化数据表样式修改 * feat: 图形化数据表样式修改 * feat: 图形化数据表样式修改 * feat: 图形化数据表样式修改 * fix(collection-manager): fix afterCreateForRelateField * feat: 样式优化 * feat: 样式优化 * feat: afterCreateForForeignKeyField * fix: timestamps: false * feat: 连线锚点优化 * fix(collection-manager): when del foreign key field, relate fields will be del too * fix: update package.json * fix: update package.json * feat: 文件名大小写 * feat: 连线锚点优化 * feat: 连线锚点通过计算得到样式优化 * feat: 连线锚点通过计算得到样式优化 * fix: fk * fix: remove index * feat: 连线hover时高亮 * fix: test error * feat: 初始化计算位置 * feat: 初始化时计算坐标位置 * feat: 初始化时计算坐标位置 * feat: improve code (#933) * fix: built in * feat: 没有关系字段时也要连线 * feat: 自关联也要连线 * fix: styling * feat: 滚动条问题 * feat: 拖拽优化 * feat: 画布paddig优化 * feat: 编辑时支持反向关联字段配置 * feat: 画布拖拽滚动优化 * feat: 画布拖拽滚动优化 * fix: reload * feat: 修复数据表新建重叠 * fix: refreshCM & refreshGM * feat: 修复表达式输入框显示异常 * feat: 渲染性能优化(增量渲染) * feat: 渲染性能优化(增量渲染) * feat: 渲染性能优化(增量渲染) * fix: 消除代码警告 * fix: 消除代码警告 * feat: 渲染性能优化(增量渲染) * feat: 渲染性能优化(增量渲染) * feat: 渲染性能优化(增量渲染) * feat: 渲染性能优化(增量渲染) * feat: 渲染性能优化(增量渲染) * feat: 渲染性能优化(增量渲染) * feat: 渲染性能优化(增量渲染) * feat: 渲染性能优化 * feat: 渲染性能优化 * feat: 外键生成在位置在前面 * feat: 限制表最多显示10个字段其余滚动 * feat: 移动表位置的连线重新计算最优位置 * fix: error * feat: 布局自动换行 * fix: test error * fix: xpipe.eq * fix: upgrade error * fix: upgrade error * feat: 选中表时只显示和目标表关联的表和连线 * feat: 连线优化 * fix: maxListenersExceededWarning * feat: 连线优化 * feat: powerby样式优化 * feat: 表筛选优化 * feat: 新建字段优化 * feat: 点击线高亮主外键和关联字段 * feat: 点击线高亮主外键和关联字段 * feat: 鼠标hover连线高亮主外键和关联字段 * fix(collection-manager): foreign key sorting should follow ID * fix(client/config-relation-field): set Relation field's ReverseField default value is false * feat: 卡片默认显示主外键和关联字段其余通过折叠展示且分组区分显示 * fix(client/collection-manager): don't display auto create through collections and foreign key only display in graph menu * feat: 样式优化 * feat: 添加字段时默认展开折叠 * feat: 样式优化 * feat: foreign field migration (#1001) * feat: 补充多语言 * feat: settings center tabs * feat: 主键判断primaryKey * fix(collection-manager): foreign key sorting should follow primaryKey * fix(client/block-select-collection): filter auto create through collections * fix(client/block-config-fields): filter isForeignKey fields * fix(client/configuration-table): relation fileds select collection filter auto create through * feat: 多对多连线高亮时全亮 * feat: 选中多对多中的一张表另一张表也显示 * feat: 连线mouseleave事件 * feat: 多语言更新 * feat: 计算新建表位置优化 * feat: 添加自动布局 * feat(client/configure-fields): categorize fields * fix(client/configure-fields): display foreign key fields * fix(client): package reference * fix: remove graph * fix: remove Co-authored-by: 唐小爱 Co-authored-by: lyf-coder Co-authored-by: katherinehhh Co-authored-by: ChengLei Shao Co-authored-by: mytharcher --- .vscode/launch.json | 32 +-- .../Configuration/AddFieldAction.tsx | 3 +- .../Configuration/CollectionFieldsTable.tsx | 66 ++++++ .../CollectionFieldsTableArray.tsx | 220 ++++++++++++++++++ .../Configuration/ConfigurationTable.tsx | 12 +- .../Configuration/index.tsx | 1 + .../Configuration/schemas/collectionFields.ts | 4 +- .../Configuration/schemas/collections.ts | 5 + .../interfaces/properties/index.ts | 2 +- packages/core/client/src/locale/en_US.ts | 4 + packages/core/client/src/locale/zh_CN.ts | 4 + packages/core/client/src/pm/index.tsx | 2 +- .../route-switch/antd/admin-layout/index.tsx | 2 +- .../client/src/schema-initializer/utils.ts | 12 +- .../hooks/afterCreateForForeignKeyField.ts | 35 ++- .../20221101111110-update-foreign-fields.ts | 48 ++++ .../plugins/collection-manager/src/server.ts | 1 + 17 files changed, 406 insertions(+), 47 deletions(-) create mode 100644 packages/core/client/src/collection-manager/Configuration/CollectionFieldsTable.tsx create mode 100644 packages/core/client/src/collection-manager/Configuration/CollectionFieldsTableArray.tsx create mode 100644 packages/plugins/collection-manager/src/migrations/20221101111110-update-foreign-fields.ts diff --git a/.vscode/launch.json b/.vscode/launch.json index 20cb42464..88c889d10 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -4,41 +4,13 @@ // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ - { - "type": "node", - "request": "launch", - "name": "Debug Server", - "cmd": "${workspaceRoot}", - "runtimeArgs": [ - "-r", "dotenv/config", - "-r", "tsconfig-paths/register", - "-r", "ts-node/register" - ], - "args": ["${workspaceRoot}/packages/app/server/src/index.ts", "start"], - "port": 9229, - "skipFiles": [ - "/**" - ], - "console": "integratedTerminal", - "internalConsoleOptions": "neverOpen" - }, { "type": "node", "request": "launch", "name": "Debug Jest Tests", "runtimeExecutable": "yarn", - "runtimeArgs": [ - "run", - "--inspect-brk", - "test", - "--runInBand", - // could be any single file path to debug - "${workspaceFolder}/packages/plugins/workflow/src/__tests__/instructions/parallel.test.ts" - ], - "port": 9229, - "skipFiles": [ - "/**" - ], + "runtimeArgs": ["run", "--inspect-brk", "test", "--runInBand", "${fileBasenameNoExtension}"], + "skipFiles": ["/**"], "console": "integratedTerminal", "internalConsoleOptions": "neverOpen" } diff --git a/packages/core/client/src/collection-manager/Configuration/AddFieldAction.tsx b/packages/core/client/src/collection-manager/Configuration/AddFieldAction.tsx index 85bd6b1c3..829261746 100644 --- a/packages/core/client/src/collection-manager/Configuration/AddFieldAction.tsx +++ b/packages/core/client/src/collection-manager/Configuration/AddFieldAction.tsx @@ -143,7 +143,7 @@ export const AddCollectionField = (props) => { }; export const AddFieldAction = (props) => { - const { scope, getContainer, item: record, children } = props; + const { scope, getContainer, item: record, children,trigger } = props; const { getInterface } = useCollectionManager(); const [visible, setVisible] = useState(false); const [schema, setSchema] = useState({}); @@ -154,6 +154,7 @@ export const AddFieldAction = (props) => { & { + request?: any; + useSelectedRowKeys?: any; + useDataSource?: ( + options?: Options & { uid?: string }, + props?: any, + ) => Result & { state?: any; setState?: any }; +}; + +const useDefSelectedRowKeys = () => { + const result = useAsyncData(); + return [result?.state?.selectedRowKeys, (selectedRowKeys) => result?.setState?.({ selectedRowKeys })]; +}; +const useDef = (options, props) => { + const { request, dataSource } = props; + if (request) { + return useRequest(request(props), options); + } else { + return Promise.resolve({ + data: dataSource, + }); + } +}; + +export const CollectionFieldsTable: React.FC = observer((props) => { + const { rowKey = 'id', useDataSource = useDef, useSelectedRowKeys = useDefSelectedRowKeys } = props; + const field = useField(); + const fieldSchema = useFieldSchema(); + const form = useMemo(() => createForm(), []); + const f = useAttach(form.createArrayField({ ...field.props, basePath: '' })); + const result = useDataSource( + { + uid: fieldSchema['x-uid'], + onSuccess(data) { + form.setValues({ + [fieldSchema.name]: data?.data, + }); + }, + }, + props, + ); + return ( + + + + + + + + ); +}); diff --git a/packages/core/client/src/collection-manager/Configuration/CollectionFieldsTableArray.tsx b/packages/core/client/src/collection-manager/Configuration/CollectionFieldsTableArray.tsx new file mode 100644 index 000000000..fe8cb176a --- /dev/null +++ b/packages/core/client/src/collection-manager/Configuration/CollectionFieldsTableArray.tsx @@ -0,0 +1,220 @@ +import { css } from '@emotion/css'; +import { ArrayField, Field } from '@formily/core'; +import { observer, RecursionField, Schema, useField, useFieldSchema } from '@formily/react'; +import { Table, TableColumnProps } from 'antd'; +import { default as classNames } from 'classnames'; +import React, { useState } from 'react'; +import { RecordIndexProvider, RecordProvider, useCollectionManager, useRequest, useSchemaInitializer } from '../..'; +import { useTranslation } from 'react-i18next'; + +const isColumnComponent = (schema: Schema) => { + return schema['x-component']?.endsWith('.Column') > -1; +}; + +const useTableColumns = () => { + const field = useField(); + const schema = useFieldSchema(); + const { exists, render } = useSchemaInitializer(schema['x-initializer']); + const columns = schema + .reduceProperties((buf, s) => { + if (isColumnComponent(s)) { + return buf.concat([s]); + } + }, []) + .map((s: Schema) => { + return { + title: , + dataIndex: s.name, + key: s.name, + render: (v, record) => { + const index = field.value?.indexOf(record); + // console.log((Date.now() - start) / 1000); + return ( + + + + + + ); + }, + } as TableColumnProps; + }); + if (!exists) { + return columns; + } + return columns.concat({ + title: render(), + dataIndex: 'TABLE_COLUMN_INITIALIZER', + key: 'TABLE_COLUMN_INITIALIZER', + }); +}; + +export const components = { + body: { + row: (props) => { + return ; + }, + cell: (props) => ( + + ), + }, +}; + +const useDef = () => { + const [selectedRowKeys, setSelectedRowKeys] = useState([]); + return [selectedRowKeys, setSelectedRowKeys]; +}; + +const useDefDataSource = (options, props) => { + const field = useField(); + return useRequest(() => { + return Promise.resolve({ + data: field.value, + }); + }, options); +}; + +const groupColumns = [ + { + dataIndex: 'name', + key: 'name', + }, +]; + +type CategorizeKey = 'primaryAndForeignKey' | 'relation' | 'systemInfo' | 'basic'; +const sortKeyArr: Array = ['primaryAndForeignKey', 'relation', 'basic', 'systemInfo']; +const CategorizeKeyNameMap = new Map([ + ['primaryAndForeignKey', 'Primary key & Foreign key fields'], + ['relation', 'Relation fields'], + ['systemInfo', 'System fields'], + ['basic', 'General fields'], +]); + +interface CategorizeDataItem { + key: CategorizeKey; + name: string; + data: Array; +} + +export const CollectionFieldsTableArray: React.FC = observer((props) => { + const field = useField(); + const columns = useTableColumns(); + const { t } = useTranslation(); + const { getInterface } = useCollectionManager(); + const { + showIndex = true, + useSelectedRowKeys = useDef, + useDataSource = useDefDataSource, + onChange, + ...others + } = props; + const [selectedRowKeys, setSelectedRowKeys] = useSelectedRowKeys(); + + const [categorizeData, setCategorizeData] = useState>([]); + useDataSource({ + onSuccess(data) { + field.value = data?.data || []; + // categorize field + const categorizeMap = new Map(); + const addCategorizeVal = (categorizeKey: CategorizeKey, val) => { + let fieldArr = categorizeMap.get(categorizeKey); + if (!fieldArr) { + fieldArr = []; + } + fieldArr.push(val); + categorizeMap.set(categorizeKey, fieldArr); + }; + field.value.forEach((item) => { + const itemInterface = getInterface(item?.interface); + if (item?.primaryKey || item.isForeignKey) { + addCategorizeVal('primaryAndForeignKey', item); + return; + } + const group = itemInterface?.group as CategorizeKey; + switch (group) { + case 'systemInfo': + case 'relation': + addCategorizeVal(group, item); + break; + default: + addCategorizeVal('basic', item); + } + }); + const tmpData: Array = []; + sortKeyArr.forEach((key) => { + if (categorizeMap.get(key)?.length > 0) { + tmpData.push({ + key, + name: t(CategorizeKeyNameMap.get(key)), + data: categorizeMap.get(key), + }); + } + }); + setCategorizeData(tmpData); + }, + }); + const restProps = { + rowSelection: props.rowSelection + ? { + type: 'checkbox', + selectedRowKeys, + onChange(selectedRowKeys: any[]) { + setSelectedRowKeys(selectedRowKeys); + }, + ...props.rowSelection, + } + : undefined, + }; + + const defaultRowKey = (record: any) => { + return field.value?.indexOf?.(record); + }; + + const expandedRowRender = (record: CategorizeDataItem, index, indent, expanded) => { + debugger; + return ( + + ); + }; + return ( +
+
+ + ); +}); diff --git a/packages/core/client/src/collection-manager/Configuration/ConfigurationTable.tsx b/packages/core/client/src/collection-manager/Configuration/ConfigurationTable.tsx index 69d1d72c2..8310cbbf3 100644 --- a/packages/core/client/src/collection-manager/Configuration/ConfigurationTable.tsx +++ b/packages/core/client/src/collection-manager/Configuration/ConfigurationTable.tsx @@ -11,6 +11,7 @@ import { AddSubFieldAction } from './AddSubFieldAction'; import { FieldSummary } from './components/FieldSummary'; import { EditSubFieldAction } from './EditSubFieldAction'; import { collectionSchema } from './schemas/collections'; +import { CollectionFieldsTable } from "."; const useAsyncDataSource = (service: any) => (field: any) => { field.loading = true; @@ -174,10 +175,12 @@ export const ConfigurationTable = () => { const { collections = [] } = useCollectionManager(); const compile = useCompile(); const loadCollections = async (field: any) => { - return collections?.map((item: any) => ({ - label: compile(item.title), - value: item.name, - })); + return collections + ?.filter((item) => !(item.autoCreate && item.isThrough)) + .map((item: any) => ({ + label: compile(item.title), + value: item.name, + })); }; const ctx = useContext(SchemaComponentContext); return ( @@ -189,6 +192,7 @@ export const ConfigurationTable = () => { AddSubFieldAction, EditSubFieldAction, FieldSummary, + CollectionFieldsTable }} scope={{ useDestroySubField, diff --git a/packages/core/client/src/collection-manager/Configuration/index.tsx b/packages/core/client/src/collection-manager/Configuration/index.tsx index 00e42cb03..f7ead8c55 100644 --- a/packages/core/client/src/collection-manager/Configuration/index.tsx +++ b/packages/core/client/src/collection-manager/Configuration/index.tsx @@ -6,6 +6,7 @@ export * from './ConfigurationTable'; export * from './EditFieldAction'; export * from './interfaces'; export * from './components'; +export * from './CollectionFieldsTable'; registerValidateFormats({ uid: /^[A-Za-z0-9][A-Za-z0-9_-]*$/, diff --git a/packages/core/client/src/collection-manager/Configuration/schemas/collectionFields.ts b/packages/core/client/src/collection-manager/Configuration/schemas/collectionFields.ts index c68abc9e7..eff4c464e 100644 --- a/packages/core/client/src/collection-manager/Configuration/schemas/collectionFields.ts +++ b/packages/core/client/src/collection-manager/Configuration/schemas/collectionFields.ts @@ -71,7 +71,7 @@ export const collectionFieldSchema: ISchema = { resource: 'collections.fields', action: 'list', params: { - pageSize: 50, + paginate: false, filter: { 'interface.$not': null, }, @@ -123,7 +123,7 @@ export const collectionFieldSchema: ISchema = { table: { type: 'void', 'x-uid': 'input', - 'x-component': 'Table.Void', + 'x-component': 'CollectionFieldsTable', 'x-component-props': { rowKey: 'name', rowSelection: { diff --git a/packages/core/client/src/collection-manager/Configuration/schemas/collections.ts b/packages/core/client/src/collection-manager/Configuration/schemas/collections.ts index f80ed6380..a3d942816 100644 --- a/packages/core/client/src/collection-manager/Configuration/schemas/collections.ts +++ b/packages/core/client/src/collection-manager/Configuration/schemas/collections.ts @@ -63,6 +63,11 @@ export const collectionSchema: ISchema = { pageSize: 50, filter: { inherit: false, + options: { + // filter auto create through collections + autoCreate: { $not: true }, + isThrough: { $not: true }, + }, }, sort: ['sort'], appends: [], diff --git a/packages/core/client/src/collection-manager/interfaces/properties/index.ts b/packages/core/client/src/collection-manager/interfaces/properties/index.ts index 4013acc77..dd522a6ac 100644 --- a/packages/core/client/src/collection-manager/interfaces/properties/index.ts +++ b/packages/core/client/src/collection-manager/interfaces/properties/index.ts @@ -78,7 +78,7 @@ export const reverseFieldProperties: Record = { properties: { autoCreateReverseField: { type: 'boolean', - default: true, + default: false, 'x-decorator': 'FormItem', 'x-component': 'Checkbox', 'x-content': '{{t("Create inverse field in the target collection")}}', diff --git a/packages/core/client/src/locale/en_US.ts b/packages/core/client/src/locale/en_US.ts index 66d71eb3b..6127b53c3 100644 --- a/packages/core/client/src/locale/en_US.ts +++ b/packages/core/client/src/locale/en_US.ts @@ -121,6 +121,10 @@ export default { "Configure columns": "Configure columns", "Edit field": "Edit field", "Configure fields of {{title}}": "Configure fields of {{title}}", + "Primary key & Foreign key fields": "Primary key & Foreign key fields", + "Relation fields": "Relation fields", + "System fields": "System fields", + "General fields": "General fields", "Basic": "Basic", "Single line text": "Single line text", "Long text": "Long text", diff --git a/packages/core/client/src/locale/zh_CN.ts b/packages/core/client/src/locale/zh_CN.ts index 9dd0db23c..6c46e32b1 100644 --- a/packages/core/client/src/locale/zh_CN.ts +++ b/packages/core/client/src/locale/zh_CN.ts @@ -125,6 +125,10 @@ export default { "Configure columns": "配置字段", "Edit field": "编辑字段", "Configure fields of {{title}}": "「{{title}}」的字段配置", + "Primary key & Foreign key fields": "主外键字段", + "Relation fields": "关系字段", + "System fields": "系统字段", + "General fields": "普通字段", "Basic": "基本类型", "Single line text": "单行文本", "Long text": "多行文本", diff --git a/packages/core/client/src/pm/index.tsx b/packages/core/client/src/pm/index.tsx index e36a4950a..97dcddd7c 100644 --- a/packages/core/client/src/pm/index.tsx +++ b/packages/core/client/src/pm/index.tsx @@ -14,7 +14,7 @@ import { useCompile } from '../schema-component'; import { BlockTemplatesPane } from '../schema-templates'; import { SystemSettingsPane } from '../system-settings'; -const SettingsCenterContext = createContext({}); +export const SettingsCenterContext = createContext({}); const PluginCard = (props) => { const history = useHistory(); diff --git a/packages/core/client/src/route-switch/antd/admin-layout/index.tsx b/packages/core/client/src/route-switch/antd/admin-layout/index.tsx index 2d810eb70..818ae28d8 100644 --- a/packages/core/client/src/route-switch/antd/admin-layout/index.tsx +++ b/packages/core/client/src/route-switch/antd/admin-layout/index.tsx @@ -190,7 +190,7 @@ const InternalAdminLayout = (props: any) => { text-align: center; width: 100%; z-index: 0; - padding: 10px 50px; + padding: 0px 50px; } `} > diff --git a/packages/core/client/src/schema-initializer/utils.ts b/packages/core/client/src/schema-initializer/utils.ts index 3a35091f5..69a2873fd 100644 --- a/packages/core/client/src/schema-initializer/utils.ts +++ b/packages/core/client/src/schema-initializer/utils.ts @@ -74,7 +74,7 @@ export const useTableColumnInitializerFields = () => { const { name, fields = [] } = useCollection(); const { getInterface } = useCollectionManager(); return fields - .filter((field) => field?.interface && field?.interface !== 'subTable') + .filter((field) => field?.interface && field?.interface !== 'subTable' && !field?.isForeignKey) .map((field) => { const interfaceConfig = getInterface(field.interface); const schema = { @@ -157,7 +157,7 @@ export const useFormItemInitializerFields = (options?: any) => { const { readPretty = form.readPretty, block = 'Form' } = options || {}; return fields - ?.filter((field) => field?.interface) + ?.filter((field) => field?.interface && !field?.isForeignKey) ?.map((field) => { const interfaceConfig = getInterface(field.interface); @@ -433,7 +433,13 @@ export const useCollectionDataSourceItems = (componentName) => { type: 'itemGroup', title: t('Select collection'), children: collections - ?.filter((item) => !item.inherit) + ?.filter((item) => { + if(item.inherit){ + return false + }else{ + return !(item?.isThrough && item?.autoCreate); + } + }) ?.map((item, index) => { const templates = getTemplatesByCollection(item.name).filter((template) => { return ( diff --git a/packages/plugins/collection-manager/src/hooks/afterCreateForForeignKeyField.ts b/packages/plugins/collection-manager/src/hooks/afterCreateForForeignKeyField.ts index 0f983983a..81b6a171e 100644 --- a/packages/plugins/collection-manager/src/hooks/afterCreateForForeignKeyField.ts +++ b/packages/plugins/collection-manager/src/hooks/afterCreateForForeignKeyField.ts @@ -2,7 +2,14 @@ import Database from '@nocobase/database'; export function afterCreateForForeignKeyField(db: Database) { function generateFkOptions(collectionName: string, foreignKey: string) { - const M = db.getModel(collectionName); + const collection = db.getCollection(collectionName); + + if (!collection) { + throw new Error('collection not found'); + } + + const M = collection.model; + const attr = M.rawAttributes[foreignKey]; if (!attr) { throw new Error(`${collectionName}.${foreignKey} does not exists`); @@ -50,23 +57,39 @@ export function afterCreateForForeignKeyField(db: Database) { }, transaction, }); + if (instance) { if (instance.type !== values.type) { throw new Error(`fk type invalid`); } - instance.set('sort',1); + instance.set('sort', 1); instance.set('isForeignKey', true); await instance.save({ transaction }); } else { - await r.create({ + const creatInstance = await r.create({ values: { isForeignKey: true, - sort:1, ...values, }, transaction, }); + // SortField#setSortValue instance._previousDataValues[scopeKey] judgment cause create set sort:1 invalid, need update + creatInstance.set('sort', 1); + await creatInstance.save({ transaction }); } + // update ID sort:0 + await r.update({ + filter: { + collectionName, + options:{ + primaryKey: true + } + }, + values: { + sort: 0, + }, + transaction, + }); } return async (model, { transaction, context }) => { @@ -74,7 +97,9 @@ export function afterCreateForForeignKeyField(db: Database) { if (!context) { return; } + const { type, interface: interfaceType, collectionName, target, through, foreignKey, otherKey } = model.get(); + // foreign key in target collection if (['oho', 'o2m'].includes(interfaceType)) { const values = generateFkOptions(target, foreignKey); @@ -86,6 +111,7 @@ export function afterCreateForForeignKeyField(db: Database) { transaction, }); } + // foreign key in source collection else if (['obo', 'm2o'].includes(interfaceType)) { const values = generateFkOptions(collectionName, foreignKey); @@ -94,6 +120,7 @@ export function afterCreateForForeignKeyField(db: Database) { transaction, }); } + // foreign key in through collection else if (['linkTo', 'm2m'].includes(interfaceType)) { if (type !== 'belongsToMany') { diff --git a/packages/plugins/collection-manager/src/migrations/20221101111110-update-foreign-fields.ts b/packages/plugins/collection-manager/src/migrations/20221101111110-update-foreign-fields.ts new file mode 100644 index 000000000..0bc430b7b --- /dev/null +++ b/packages/plugins/collection-manager/src/migrations/20221101111110-update-foreign-fields.ts @@ -0,0 +1,48 @@ +import { Migration } from '@nocobase/server'; +import { afterCreateForForeignKeyField } from '../hooks/afterCreateForForeignKeyField'; + +export default class DropForeignKeysMigration extends Migration { + async up() { + const result = await this.app.version.satisfies('<0.8.0'); + + if (!result) { + return; + } + + const transaction = await this.app.db.sequelize.transaction(); + const callback = afterCreateForForeignKeyField(this.app.db); + + try { + const fields = await this.app.db.getCollection('fields').repository.find({ + filter: { + interface: { + $in: ['oho', 'o2m', 'obo', 'm2o', 'linkTo', 'm2m'], + }, + collectionName: { + $not: null, + }, + }, + }); + + for (const field of fields) { + try { + await callback(field, { + transaction, + context: {}, + }); + } catch (error) { + if (error.message.includes('collection not found')) { + continue; + } + + throw error; + } + } + + await transaction.commit(); + } catch (error) { + console.log(error); + await transaction.rollback(); + } + } +} diff --git a/packages/plugins/collection-manager/src/server.ts b/packages/plugins/collection-manager/src/server.ts index 04376c8c6..275c4f020 100644 --- a/packages/plugins/collection-manager/src/server.ts +++ b/packages/plugins/collection-manager/src/server.ts @@ -83,6 +83,7 @@ export class CollectionManagerPlugin extends Plugin { }); } }); + // after migrate this.app.db.on('fields.afterCreate', afterCreateForForeignKeyField(this.app.db));