From e39d014b67ab21c90135207b2fc5426a66fa6b86 Mon Sep 17 00:00:00 2001 From: chenos Date: Tue, 22 Oct 2024 14:24:34 +0800 Subject: [PATCH 1/6] feat: support for the USE_DB_SCHEMA_IN_SUBAPP environment variable (#5481) --- .../plugin-multi-app-manager/src/server/server.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/plugins/@nocobase/plugin-multi-app-manager/src/server/server.ts b/packages/plugins/@nocobase/plugin-multi-app-manager/src/server/server.ts index 1252cf0109..c4eba7ef12 100644 --- a/packages/plugins/@nocobase/plugin-multi-app-manager/src/server/server.ts +++ b/packages/plugins/@nocobase/plugin-multi-app-manager/src/server/server.ts @@ -61,7 +61,7 @@ const defaultSubAppUpgradeHandle: SubAppUpgradeHandler = async (mainApp: Applica const defaultDbCreator = async (app: Application) => { const databaseOptions = app.options.database as any; - const { host, port, username, password, dialect, database } = databaseOptions; + const { host, port, username, password, dialect, database, schema } = databaseOptions; if (dialect === 'mysql') { const mysql = require('mysql2/promise'); @@ -91,7 +91,11 @@ const defaultDbCreator = async (app: Application) => { await client.connect(); try { - await client.query(`CREATE DATABASE "${database}"`); + if (process.env.USE_DB_SCHEMA_IN_SUBAPP === 'true') { + await client.query(`CREATE SCHEMA IF NOT EXISTS ${schema}`); + } else { + await client.query(`CREATE DATABASE "${database}"`); + } } catch (e) { console.log(e); } @@ -109,6 +113,8 @@ const defaultAppOptionsFactory = (appName: string, mainApp: Application) => { const mainStorageDir = path.dirname(mainAppStorage); rawDatabaseOptions.storage = path.join(mainStorageDir, `${appName}.sqlite`); } + } else if (process.env.USE_DB_SCHEMA_IN_SUBAPP === 'true' && rawDatabaseOptions.dialect === 'postgres') { + rawDatabaseOptions.schema = appName; } else { rawDatabaseOptions.database = appName; } From 1da28b62e0df9dd25da5c8d7ec09b1460d6a3dce Mon Sep 17 00:00:00 2001 From: Jinx <61403813+Jokergga@users.noreply.github.com> Date: Tue, 22 Oct 2024 14:44:29 +0800 Subject: [PATCH 2/6] fix: Grid.Col displayName correct (#4958) --- packages/core/client/src/schema-component/antd/grid/Grid.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/client/src/schema-component/antd/grid/Grid.tsx b/packages/core/client/src/schema-component/antd/grid/Grid.tsx index 45f091d0ea..0979a0b175 100644 --- a/packages/core/client/src/schema-component/antd/grid/Grid.tsx +++ b/packages/core/client/src/schema-component/antd/grid/Grid.tsx @@ -549,7 +549,7 @@ Grid.Col = observer( ); }, - { displayName: 'Grid.Row' }, + { displayName: 'Grid.Col' }, ); Grid.wrap = (schema: ISchema) => { From 1ee6ca7bef2b91501aa005e2609596bbb2451e04 Mon Sep 17 00:00:00 2001 From: Chareice Date: Tue, 22 Oct 2024 15:16:39 +0800 Subject: [PATCH 3/6] chore: ignore error when write to exclude --- packages/core/cli/src/commands/postinstall.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/core/cli/src/commands/postinstall.js b/packages/core/cli/src/commands/postinstall.js index 539afdf8b7..a0fc7a3fd2 100644 --- a/packages/core/cli/src/commands/postinstall.js +++ b/packages/core/cli/src/commands/postinstall.js @@ -18,9 +18,16 @@ function writeToExclude() { const excludePath = resolve(process.cwd(), '.git', 'info', 'exclude'); const content = 'packages/pro-plugins/\n'; const dirPath = dirname(excludePath); + if (!existsSync(dirPath)) { - mkdirSync(dirPath, { recursive: true }); + try { + mkdirSync(dirPath, { recursive: true }); + } catch (e) { + console.log(`${e.message}, ignore write to git exclude`); + return; + } } + let fileContent = ''; if (existsSync(excludePath)) { fileContent = readFileSync(excludePath, 'utf-8'); From d6b2c02c32b486c97198e8bfc31bbc8cec424794 Mon Sep 17 00:00:00 2001 From: Zhichao Gu Date: Tue, 22 Oct 2024 16:59:35 +0800 Subject: [PATCH 4/6] feat: show create popup when select calendar slot (#5483) --- .../src/client/calendar/Calendar.tsx | 31 ++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/packages/plugins/@nocobase/plugin-calendar/src/client/calendar/Calendar.tsx b/packages/plugins/@nocobase/plugin-calendar/src/client/calendar/Calendar.tsx index 6845ab7137..3c83909f23 100644 --- a/packages/plugins/@nocobase/plugin-calendar/src/client/calendar/Calendar.tsx +++ b/packages/plugins/@nocobase/plugin-calendar/src/client/calendar/Calendar.tsx @@ -19,6 +19,7 @@ import { useProps, useToken, withDynamicSchemaProps, + useACLRoleContext, } from '@nocobase/client'; import { parseExpression } from 'cron-parser'; import type { Dayjs } from 'dayjs'; @@ -214,6 +215,18 @@ const useEvents = ( ]); }; +function findCreateSchema(schema): Schema { + return schema.reduceProperties((buf, current) => { + if (current['x-component'].endsWith('Action') && current['x-action'] === 'create') { + return current; + } + if (current['x-component'].endsWith('.ActionBar')) { + return findCreateSchema(current); + } + return buf; + }, null); +} + export const Calendar: any = withDynamicSchemaProps( observer( (props: any) => { @@ -233,6 +246,13 @@ export const Calendar: any = withDynamicSchemaProps( const parentRecordData = useCollectionParentRecordData(); const fieldSchema = useFieldSchema(); const { token } = useToken(); + //nint deal with slot select to show create popup + const { parseAction } = useACLRoleContext(); + const collection = useCollection(); + const canCreate = parseAction(`${collection.name}:create`); + const createActionSchema: Schema = useMemo(() => findCreateSchema(fieldSchema), [fieldSchema]); + const startFieldName = fieldNames?.start?.[0]; + const endFieldName = fieldNames?.end?.[0]; const components = useMemo(() => { return { @@ -302,7 +322,16 @@ export const Calendar: any = withDynamicSchemaProps( onNavigate={setDate} onView={setView} onSelectSlot={(slotInfo) => { - console.log('onSelectSlot', slotInfo); + //nint show create popup + if (canCreate && createActionSchema) { + const record = {}; + record[startFieldName] = slotInfo.start; + record[endFieldName] = slotInfo.end; + openPopup({ + recordData: record, + customActionSchema: createActionSchema, + }); + } }} onDoubleClickEvent={() => { console.log('onDoubleClickEvent'); From c903b43a17bb3bb81c8de8a76173337c30435e85 Mon Sep 17 00:00:00 2001 From: Katherine Date: Tue, 22 Oct 2024 17:00:11 +0800 Subject: [PATCH 5/6] feat: subTable support pagination (#5450) * refactor: subtable support pagination settings * fix: bug * fix: bug --- .../components/SchemaSettingsChildren.tsx | 2 +- .../subTablePopoverComponentFieldSettings.tsx | 32 +++++++++++++++++++ .../association-field/InternalSubTable.tsx | 15 +++++++++ .../antd/association-field/SubTable.tsx | 31 ++++++++++++++++-- 4 files changed, 77 insertions(+), 3 deletions(-) diff --git a/packages/core/client/src/application/schema-settings/components/SchemaSettingsChildren.tsx b/packages/core/client/src/application/schema-settings/components/SchemaSettingsChildren.tsx index 61d4eabd9a..26b52739bc 100644 --- a/packages/core/client/src/application/schema-settings/components/SchemaSettingsChildren.tsx +++ b/packages/core/client/src/application/schema-settings/components/SchemaSettingsChildren.tsx @@ -83,7 +83,7 @@ export const SchemaSettingsChildren: FC = (props) = // 此时如果使用 item.name 作为 key,会导致 React 认为其前后是同一个组件;因为 SchemaSettingsChild 的某些 hooks 是通过 props 传入的, // 两次渲染之间 props 可能发生变化,就可能报 hooks 调用顺序的错误。所以这里使用 fieldComponentName 和 item.name 拼成 // 一个不会重复的 key,保证每次渲染都是新的组件。 - const key = `${fieldComponentName ? fieldComponentName + '-' : ''}${item.name}`; + const key = `${fieldComponentName ? fieldComponentName + '-' : ''}${item?.name}`; return ( ({ value: v })), + onChange: (pageSize) => { + const schema = { + ['x-uid']: fieldSchema['x-uid'], + }; + field.componentProps = field.componentProps || {}; + field.componentProps.pageSize = pageSize; + fieldSchema['x-component-props'] = fieldSchema['x-component-props'] || {}; + fieldSchema['x-component-props'].pageSize = pageSize; + schema['x-component-props'] = fieldSchema['x-component-props']; + dn.emit('patch', { + schema, + }); + }, + }; + }, +}; + export const subTablePopoverComponentFieldSettings = new SchemaSettings({ name: 'fieldSettings:component:SubTable', items: [ @@ -332,5 +363,6 @@ export const subTablePopoverComponentFieldSettings = new SchemaSettings({ allowDisassociation, setDefaultSortingRules, linkageRules, + recordPerPage, ], }); diff --git a/packages/core/client/src/schema-component/antd/association-field/InternalSubTable.tsx b/packages/core/client/src/schema-component/antd/association-field/InternalSubTable.tsx index 14071320e9..936d7ffcc4 100644 --- a/packages/core/client/src/schema-component/antd/association-field/InternalSubTable.tsx +++ b/packages/core/client/src/schema-component/antd/association-field/InternalSubTable.tsx @@ -63,6 +63,21 @@ export const InternalSubTable = observer( max-height: 100% !important; min-height: 100% !important; } + + // configure columns + .ant-table-thead + button[aria-label*='schema-initializer-AssociationField.SubTable-table:configureColumns'] + > span:last-child { + display: none !important; + } + .ant-table-thead + button[aria-label*='schema-initializer-AssociationField.SubTable-table:configureColumns'] + > .ant-btn-icon { + margin: 0px; + } + .ant-table-tbody .nb-column-initializer { + min-width: 40px !important; + } `} layout={'vertical'} bordered={false} diff --git a/packages/core/client/src/schema-component/antd/association-field/SubTable.tsx b/packages/core/client/src/schema-component/antd/association-field/SubTable.tsx index f83382458c..8a09fbdbb9 100644 --- a/packages/core/client/src/schema-component/antd/association-field/SubTable.tsx +++ b/packages/core/client/src/schema-component/antd/association-field/SubTable.tsx @@ -14,7 +14,7 @@ import { observer, RecursionField, useFieldSchema } from '@formily/react'; import { action } from '@formily/reactive'; import { isArr } from '@formily/shared'; import { Button } from 'antd'; -import React, { useContext, useMemo, useState } from 'react'; +import React, { useContext, useEffect, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { FormProvider, @@ -157,6 +157,8 @@ export const SubTable: any = observer( field.initialValue = field.value; setSelectedRows([]); setVisible(false); + const totalPages = Math.ceil(field.value.length / (field.componentProps?.pageSize || 10)); + setCurrentPage(totalPages); }, }; }; @@ -166,6 +168,28 @@ export const SubTable: any = observer( const filter = list.length ? { $and: [{ [`${targetKey}.$ne`]: list }] } : {}; return filter; }; + //分页 + const [currentPage, setCurrentPage] = useState(1); + const [pageSize, setPageSize] = useState(field.componentProps?.pageSize || 10); // 每页条数 + useEffect(() => { + setPageSize(field.componentProps?.pageSize); + }, [field.componentProps?.pageSize]); + + const paginationConfig = useMemo(() => { + return { + current: currentPage, + pageSize: pageSize || 10, + total: field?.value?.length, + onChange: (page, pageSize) => { + setCurrentPage(page); + setPageSize(pageSize); + field.onInput(field.value); + }, + showSizeChanger: true, + pageSizeOptions: ['10', '20', '50', '100'], + hideOnSinglePage: false, + }; + }, [field.value?.length, pageSize, currentPage]); return (
@@ -193,7 +217,7 @@ export const SubTable: any = observer( } : false } - pagination={false} + pagination={paginationConfig} rowSelection={{ type: 'none', hideSelectAll: true }} footer={() => field.editable && ( @@ -206,6 +230,9 @@ export const SubTable: any = observer( onClick={() => { field.value = field.value || []; field.value.push(markRecordAsNew({})); + // 计算总页数,并跳转到最后一页 + const totalPages = Math.ceil(field.value.length / (field.componentProps?.pageSize || 10)); + setCurrentPage(totalPages); }} > {t('Add new')} From 749b28cef3d4c48b8baecb1698e46bf95a91a83a Mon Sep 17 00:00:00 2001 From: ChengLei Shao Date: Tue, 22 Oct 2024 17:07:57 +0800 Subject: [PATCH 6/6] chore: datasource sql logger (#5485) --- .../core/data-source-manager/src/data-source.ts | 9 +++++++++ packages/core/server/src/application.ts | 14 ++++++++++---- .../src/server/models/data-source.ts | 1 + 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/packages/core/data-source-manager/src/data-source.ts b/packages/core/data-source-manager/src/data-source.ts index 84ce8bdb29..49e0e8e1e3 100644 --- a/packages/core/data-source-manager/src/data-source.ts +++ b/packages/core/data-source-manager/src/data-source.ts @@ -22,6 +22,7 @@ export abstract class DataSource extends EventEmitter { public resourceManager: ResourceManager; public acl: ACL; logger: Logger; + _sqlLogger: Logger; constructor(protected options: DataSourceOptions) { super(); @@ -32,6 +33,14 @@ export abstract class DataSource extends EventEmitter { this.logger = logger; } + setSqlLogger(logger: Logger) { + this._sqlLogger = logger; + } + + get sqlLogger() { + return this._sqlLogger || this.logger; + } + get name() { return this.options.name; } diff --git a/packages/core/server/src/application.ts b/packages/core/server/src/application.ts index 065a440794..f29a238332 100644 --- a/packages/core/server/src/application.ts +++ b/packages/core/server/src/application.ts @@ -227,7 +227,7 @@ export class Application exten */ public syncManager: SyncManager; public requestLogger: Logger; - private sqlLogger: Logger; + private _sqlLogger: Logger; protected _logger: SystemLogger; constructor(public options: ApplicationOptions) { @@ -252,6 +252,10 @@ export class Application exten return this._logger; } + get sqlLogger() { + return this._sqlLogger; + } + get log() { return this._logger; } @@ -1083,12 +1087,14 @@ export class Application exten // Due to the use of custom log levels, // we have to use any type here until Winston updates the type definitions. }) as any; + this.requestLogger = createLogger({ dirname: getLoggerFilePath(this.name), filename: 'request', ...(options?.request || {}), }); - this.sqlLogger = this.createLogger({ + + this._sqlLogger = this.createLogger({ filename: 'sql', level: 'debug', }); @@ -1097,7 +1103,7 @@ export class Application exten protected closeLogger() { this.log?.close(); this.requestLogger?.close(); - this.sqlLogger?.close(); + this._sqlLogger?.close(); } protected init() { @@ -1209,7 +1215,7 @@ export class Application exten if (msg.includes('INSERT INTO')) { msg = msg.substring(0, 2000) + '...'; } - this.sqlLogger.debug({ message: msg, app: this.name, reqId: this.context.reqId }); + this._sqlLogger.debug({ message: msg, app: this.name, reqId: this.context.reqId }); }; const dbOptions = options.database instanceof Database ? options.database.options : options.database; const db = new Database({ diff --git a/packages/plugins/@nocobase/plugin-data-source-manager/src/server/models/data-source.ts b/packages/plugins/@nocobase/plugin-data-source-manager/src/server/models/data-source.ts index 463c87d318..f4663f2d3c 100644 --- a/packages/plugins/@nocobase/plugin-data-source-manager/src/server/models/data-source.ts +++ b/packages/plugins/@nocobase/plugin-data-source-manager/src/server/models/data-source.ts @@ -96,6 +96,7 @@ export class DataSourceModel extends Model { ...createOptions, name: dataSourceKey, logger: app.logger.child({ dataSourceKey }), + sqlLogger: app.sqlLogger.child({ dataSourceKey }), }); if (loadAtAfterStart) {