From f82b4d872606159d62180e9727440d8178b6176c Mon Sep 17 00:00:00 2001 From: chenos Date: Sun, 17 Dec 2023 11:16:30 +0800 Subject: [PATCH] feat(ui-schema): nocobase-admin-menu & nocobase-mobile-container (#3213) * feat(ui-schema): nocobase-admin-menu & nocobase-mobile-container * fix: db.sync * fix: error * fix: error * fix: error * fix: add test case * fix: migration error * fix: test error --- .../client/src/application/Application.tsx | 4 +- .../application/components/AppComponent.tsx | 4 +- .../client/src/hooks/useAdminSchemaUid.ts | 5 +- .../src/nocobase-buildin-plugin/index.tsx | 2 +- packages/core/database/src/migration.ts | 6 +- .../20231215215245-admin-menu-uid.test.ts | 61 +++++++++++++++++++ .../20230620203216-admin-ui-schema-uid.ts | 6 +- .../20231215215247-admin-menu-uid.ts | 53 ++++++++++++++++ .../plugin-client/src/server/server.ts | 37 ++++++----- .../migrations/20231120142523-fix-storage.ts | 1 + .../src/server/actions/localization.ts | 23 ++++--- .../migrations/20231206161851-fix-module.ts | 9 +-- .../src/client/router/Application.tsx | 13 +--- .../20230620203218-mobile-ui-schema-uid.ts | 4 ++ .../20231215215233-mobile-container-uid.ts | 53 ++++++++++++++++ .../plugin-mobile-client/src/server/plugin.ts | 13 +--- .../20230612021134-manual-collection-block.ts | 1 + 17 files changed, 229 insertions(+), 66 deletions(-) create mode 100644 packages/plugins/@nocobase/plugin-client/src/server/__tests__/20231215215245-admin-menu-uid.test.ts create mode 100644 packages/plugins/@nocobase/plugin-client/src/server/migrations/20231215215247-admin-menu-uid.ts create mode 100644 packages/plugins/@nocobase/plugin-mobile-client/src/server/migrations/20231215215233-mobile-container-uid.ts diff --git a/packages/core/client/src/application/Application.tsx b/packages/core/client/src/application/Application.tsx index afa3abf60..a78600290 100644 --- a/packages/core/client/src/application/Application.tsx +++ b/packages/core/client/src/application/Application.tsx @@ -218,11 +218,11 @@ export class Application { } loadFailed = true; this.error = { - ...error, code: 'LOAD_ERROR', message: error.message, + ...error, }; - console.error(error); + console.error(this.error); } this.loading = false; } diff --git a/packages/core/client/src/application/components/AppComponent.tsx b/packages/core/client/src/application/components/AppComponent.tsx index 0c0382b9e..c549df8bb 100644 --- a/packages/core/client/src/application/components/AppComponent.tsx +++ b/packages/core/client/src/application/components/AppComponent.tsx @@ -19,7 +19,9 @@ export const AppComponent: FC = observer((props) => { const AppError = app.getComponent('AppError'); if (app.loading) return app.renderComponent('AppSpin', { app }); if (!app.maintained && app.maintaining) return app.renderComponent('AppMaintaining', { app }); - if (app.error?.code === 'LOAD_ERROR') return ; + if (app.error?.code === 'LOAD_ERROR' || app.error?.code === 'APP_ERROR') { + return ; + } return ( } diff --git a/packages/core/client/src/hooks/useAdminSchemaUid.ts b/packages/core/client/src/hooks/useAdminSchemaUid.ts index c5904b60e..b5fb7ad3e 100644 --- a/packages/core/client/src/hooks/useAdminSchemaUid.ts +++ b/packages/core/client/src/hooks/useAdminSchemaUid.ts @@ -1,6 +1,3 @@ -import { useSystemSettings } from '../system-settings'; - export const useAdminSchemaUid = () => { - const ctx = useSystemSettings(); - return ctx?.data?.data?.options?.adminSchemaUid; + return 'nocobase-admin-menu'; }; diff --git a/packages/core/client/src/nocobase-buildin-plugin/index.tsx b/packages/core/client/src/nocobase-buildin-plugin/index.tsx index 89b53e2ba..d6b41e820 100644 --- a/packages/core/client/src/nocobase-buildin-plugin/index.tsx +++ b/packages/core/client/src/nocobase-buildin-plugin/index.tsx @@ -128,7 +128,7 @@ const getProps = (app: Application) => { }; } - if (app.error.code === 'APP_ERROR') { + if (app.error.code === 'APP_ERROR' || app.error.code === 'LOAD_ERROR') { return { status: 'error', title: 'App error', diff --git a/packages/core/database/src/migration.ts b/packages/core/database/src/migration.ts index 46c3af474..687576ba5 100644 --- a/packages/core/database/src/migration.ts +++ b/packages/core/database/src/migration.ts @@ -1,3 +1,4 @@ +import _ from 'lodash'; import { QueryInterface, Sequelize } from 'sequelize'; import Database from './database'; @@ -70,7 +71,10 @@ export class Migrations { callback() { return async (ctx) => { - return this.items; + return _.sortBy(this.items, (item) => { + const keys = item.name.split('/'); + return keys.pop() || item.name; + }); }; } } diff --git a/packages/plugins/@nocobase/plugin-client/src/server/__tests__/20231215215245-admin-menu-uid.test.ts b/packages/plugins/@nocobase/plugin-client/src/server/__tests__/20231215215245-admin-menu-uid.test.ts new file mode 100644 index 000000000..1ffa0d6e9 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-client/src/server/__tests__/20231215215245-admin-menu-uid.test.ts @@ -0,0 +1,61 @@ +import { MockServer, mockServer } from '@nocobase/test'; +import Migration from '../migrations/20231215215247-admin-menu-uid'; + +// 每个插件的 app 最小化安装的插件都不一样,需要插件根据自己的情况添加必备插件 +async function createApp(options: any = {}) { + const app = mockServer({ + ...options, + plugins: ['client', 'ui-schema-storage', 'system-settings'].concat(options.plugins || []), + }); + // 这里可以补充一些需要特殊处理的逻辑,比如导入测试需要的数据表 + return app; +} + +// 大部分的测试都需要启动应用,所以也可以提供一个通用的启动方法 +async function startApp() { + const app = await createApp(); + await app.quickstart({ + // 运行测试前,清空数据库 + clean: true, + }); + return app; +} + +describe('nocobase-admin-menu', () => { + let app: MockServer; + + beforeEach(async () => { + app = await startApp(); + }); + + afterEach(async () => { + // 运行测试后,清空数据库 + await app.destroy(); + // 只停止不清空数据库 + // await app.stop(); + }); + + test('migration', async () => { + const uiSchemas = app.db.getModel('uiSchemas'); + const systemSettings = app.db.getRepository('systemSettings'); + await uiSchemas.truncate(); + await app.db.getModel('uiSchemaTreePath').truncate(); + await uiSchemas.create({ + 'x-uid': 'abc', + name: 'abc', + }); + const instance = await systemSettings.findOne(); + instance.set('options', { + adminSchemaUid: 'abc', + }); + await instance.save(); + const migration = new Migration({ + db: app.db, + // @ts-ignore + app: app, + }); + await migration.up(); + const schema = await uiSchemas.findOne(); + expect(schema.toJSON()).toMatchObject({ 'x-uid': 'nocobase-admin-menu', name: 'abc' }); + }); +}); diff --git a/packages/plugins/@nocobase/plugin-client/src/server/migrations/20230620203216-admin-ui-schema-uid.ts b/packages/plugins/@nocobase/plugin-client/src/server/migrations/20230620203216-admin-ui-schema-uid.ts index be557df05..21806215f 100644 --- a/packages/plugins/@nocobase/plugin-client/src/server/migrations/20230620203216-admin-ui-schema-uid.ts +++ b/packages/plugins/@nocobase/plugin-client/src/server/migrations/20230620203216-admin-ui-schema-uid.ts @@ -3,6 +3,10 @@ import { Migration } from '@nocobase/server'; export default class extends Migration { async up() { + const result = await this.app.version.satisfies('<0.14.0-alpha.1'); + if (!result) { + return; + } await this.db.getCollection('systemSettings').sync({ force: false, alter: { @@ -15,10 +19,10 @@ export default class extends Migration { return; } const uiRoutes = this.db.getRepository('uiRoutes'); - const routes = await uiRoutes.find(); if (!uiRoutes) { return; } + const routes = await uiRoutes.find(); for (const route of routes) { if (route.uiSchemaUid && route?.options?.component === 'AdminLayout') { const options = instance.options || {}; diff --git a/packages/plugins/@nocobase/plugin-client/src/server/migrations/20231215215247-admin-menu-uid.ts b/packages/plugins/@nocobase/plugin-client/src/server/migrations/20231215215247-admin-menu-uid.ts new file mode 100644 index 000000000..63c2283e0 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-client/src/server/migrations/20231215215247-admin-menu-uid.ts @@ -0,0 +1,53 @@ +import { Model } from '@nocobase/database'; +import { Migration } from '@nocobase/server'; + +export default class extends Migration { + async up() { + const result = await this.app.version.satisfies('<0.17.0-alpha.8'); + if (!result) { + return; + } + const systemSettings = this.db.getRepository('systemSettings'); + const instance: Model = await systemSettings.findOne(); + if (!instance?.options?.adminSchemaUid) { + return; + } + const UiSchemas = this.db.getModel('uiSchemas'); + await this.db.sequelize.transaction(async (transaction) => { + await UiSchemas.update( + { + 'x-uid': 'nocobase-admin-menu', + }, + { + transaction, + where: { + 'x-uid': instance?.options?.adminSchemaUid, + }, + }, + ); + await this.db.getModel('uiSchemaTreePath').update( + { + descendant: 'nocobase-admin-menu', + }, + { + transaction, + where: { + descendant: instance?.options?.adminSchemaUid, + }, + }, + ); + await this.db.getModel('uiSchemaTreePath').update( + { + ancestor: 'nocobase-admin-menu', + }, + { + transaction, + where: { + ancestor: instance?.options?.adminSchemaUid, + }, + }, + ); + }); + console.log(instance?.options?.adminSchemaUid); + } +} diff --git a/packages/plugins/@nocobase/plugin-client/src/server/server.ts b/packages/plugins/@nocobase/plugin-client/src/server/server.ts index 6bbcabbff..bd30734a2 100644 --- a/packages/plugins/@nocobase/plugin-client/src/server/server.ts +++ b/packages/plugins/@nocobase/plugin-client/src/server/server.ts @@ -32,27 +32,24 @@ export class ClientPlugin extends Plugin { // } }); + } - this.db.on('systemSettings.beforeCreate', async (instance, { transaction }) => { - const uiSchemas = this.db.getRepository('uiSchemas'); - const schema = await uiSchemas.insert( - { - type: 'void', - 'x-component': 'Menu', - 'x-designer': 'Menu.Designer', - 'x-initializer': 'MenuItemInitializers', - 'x-component-props': { - mode: 'mix', - theme: 'dark', - // defaultSelectedUid: 'u8', - onSelect: '{{ onSelect }}', - sideMenuRefScopeKey: 'sideMenuRef', - }, - properties: {}, - }, - { transaction }, - ); - instance.set('options.adminSchemaUid', schema['x-uid']); + async install() { + const uiSchemas = this.db.getRepository('uiSchemas'); + await uiSchemas.insert({ + type: 'void', + 'x-uid': 'nocobase-admin-menu', + 'x-component': 'Menu', + 'x-designer': 'Menu.Designer', + 'x-initializer': 'MenuItemInitializers', + 'x-component-props': { + mode: 'mix', + theme: 'dark', + // defaultSelectedUid: 'u8', + onSelect: '{{ onSelect }}', + sideMenuRefScopeKey: 'sideMenuRef', + }, + properties: {}, }); } diff --git a/packages/plugins/@nocobase/plugin-file-manager/src/server/migrations/20231120142523-fix-storage.ts b/packages/plugins/@nocobase/plugin-file-manager/src/server/migrations/20231120142523-fix-storage.ts index 0d497da8c..59850f19f 100644 --- a/packages/plugins/@nocobase/plugin-file-manager/src/server/migrations/20231120142523-fix-storage.ts +++ b/packages/plugins/@nocobase/plugin-file-manager/src/server/migrations/20231120142523-fix-storage.ts @@ -10,6 +10,7 @@ export default class extends Migration { } const r = this.db.getRepository('storages'); + await r.collection.sync(); const items = await r.find({ filter: { type: 'local', diff --git a/packages/plugins/@nocobase/plugin-localization-management/src/server/actions/localization.ts b/packages/plugins/@nocobase/plugin-localization-management/src/server/actions/localization.ts index 933b6fbe7..3873d3917 100644 --- a/packages/plugins/@nocobase/plugin-localization-management/src/server/actions/localization.ts +++ b/packages/plugins/@nocobase/plugin-localization-management/src/server/actions/localization.ts @@ -1,9 +1,9 @@ import { Context, Next } from '@nocobase/actions'; import { Database, Model, Op } from '@nocobase/database'; import { UiSchemaRepository } from '@nocobase/plugin-ui-schema-storage'; +import { NAMESPACE_COLLECTIONS, NAMESPACE_MENUS } from '../constans'; import LocalizationManagementPlugin from '../plugin'; import { getTextsFromDBRecord, getTextsFromUISchema } from '../utils'; -import { NAMESPACE_COLLECTIONS, NAMESPACE_MENUS } from '../constans'; const getResourcesInstance = async (ctx: Context) => { const plugin = ctx.app.getPlugin('localization-management') as LocalizationManagementPlugin; @@ -88,21 +88,24 @@ export const getTextsFromDB = async (db: Database) => { return result; }; -const getSchemaUid = async (db: Database) => { - const systemSettings = await db.getRepository('systemSettings').findOne(); - const options = systemSettings?.options || {}; - const { adminSchemaUid, mobileSchemaUid } = options; - return { adminSchemaUid, mobileSchemaUid }; +const getSchemaUid = async (db: Database, migrate = false) => { + if (migrate) { + const systemSettings = await db.getRepository('systemSettings').findOne(); + const options = systemSettings?.options || {}; + const { adminSchemaUid, mobileSchemaUid } = options; + return { adminSchemaUid, mobileSchemaUid }; + } + return { adminSchemaUid: 'nocobase-admin-menu', mobileSchemaUid: 'nocobase-mobile-container' }; }; -export const getTextsFromMenu = async (db: Database) => { +export const getTextsFromMenu = async (db: Database, migrate = false) => { const result = {}; - const { adminSchemaUid, mobileSchemaUid } = await getSchemaUid(db); + const { adminSchemaUid, mobileSchemaUid } = await getSchemaUid(db, migrate); const repo = db.getRepository('uiSchemas') as UiSchemaRepository; if (adminSchemaUid) { const schema = await repo.getProperties(adminSchemaUid); const extractTitle = (schema: any) => { - if (schema.properties) { + if (schema?.properties) { Object.values(schema.properties).forEach((item: any) => { if (item.title) { result[item.title] = ''; @@ -115,7 +118,7 @@ export const getTextsFromMenu = async (db: Database) => { } if (mobileSchemaUid) { const schema = await repo.getProperties(mobileSchemaUid); - if (schema['properties']?.tabBar?.properties) { + if (schema?.['properties']?.tabBar?.properties) { Object.values(schema['properties']?.tabBar?.properties).forEach((item: any) => { const title = item['x-component-props']?.title; if (title) { diff --git a/packages/plugins/@nocobase/plugin-localization-management/src/server/migrations/20231206161851-fix-module.ts b/packages/plugins/@nocobase/plugin-localization-management/src/server/migrations/20231206161851-fix-module.ts index 1eea7db98..e3db1b728 100644 --- a/packages/plugins/@nocobase/plugin-localization-management/src/server/migrations/20231206161851-fix-module.ts +++ b/packages/plugins/@nocobase/plugin-localization-management/src/server/migrations/20231206161851-fix-module.ts @@ -1,7 +1,7 @@ -import { Migration } from '@nocobase/server'; -import { getTextsFromMenu, getTextsFromDB } from '../actions/localization'; -import { NAMESPACE_COLLECTIONS, NAMESPACE_MENUS } from '../constans'; import { Op } from '@nocobase/database'; +import { Migration } from '@nocobase/server'; +import { getTextsFromDB, getTextsFromMenu } from '../actions/localization'; +import { NAMESPACE_COLLECTIONS, NAMESPACE_MENUS } from '../constans'; export default class FixModuleMigration extends Migration { async up() { @@ -12,10 +12,11 @@ export default class FixModuleMigration extends Migration { } const resources = await this.app.localeManager.getCacheResources('zh-CN'); - const menus = await getTextsFromMenu(this.context.db); + const menus = await getTextsFromMenu(this.context.db, true); const collections = await getTextsFromDB(this.context.db); const db = this.context.db; + await db.getCollection('localizationTexts').sync(); await db.sequelize.transaction(async (t) => { const menuTexts = Object.keys(menus); await db.getModel('localizationTexts').update( diff --git a/packages/plugins/@nocobase/plugin-mobile-client/src/client/router/Application.tsx b/packages/plugins/@nocobase/plugin-mobile-client/src/client/router/Application.tsx index 8382e341b..08817e66c 100644 --- a/packages/plugins/@nocobase/plugin-mobile-client/src/client/router/Application.tsx +++ b/packages/plugins/@nocobase/plugin-mobile-client/src/client/router/Application.tsx @@ -1,12 +1,4 @@ -import { - ActionContextProvider, - AdminProvider, - css, - cx, - RemoteSchemaComponent, - useSystemSettings, - useViewport, -} from '@nocobase/client'; +import { ActionContextProvider, AdminProvider, css, cx, RemoteSchemaComponent, useViewport } from '@nocobase/client'; import { DrawerProps, ModalProps } from 'antd'; import React, { useMemo } from 'react'; import { Outlet, useParams } from 'react-router-dom'; @@ -76,8 +68,7 @@ const modalProps = { }; const useMobileSchemaUid = () => { - const ctx = useSystemSettings(); - return ctx?.data?.data?.options?.mobileSchemaUid; + return 'nocobase-mobile-container'; }; const MApplication: React.FC = (props) => { diff --git a/packages/plugins/@nocobase/plugin-mobile-client/src/server/migrations/20230620203218-mobile-ui-schema-uid.ts b/packages/plugins/@nocobase/plugin-mobile-client/src/server/migrations/20230620203218-mobile-ui-schema-uid.ts index c5be094ad..b031ad916 100644 --- a/packages/plugins/@nocobase/plugin-mobile-client/src/server/migrations/20230620203218-mobile-ui-schema-uid.ts +++ b/packages/plugins/@nocobase/plugin-mobile-client/src/server/migrations/20230620203218-mobile-ui-schema-uid.ts @@ -3,6 +3,10 @@ import { Migration } from '@nocobase/server'; export default class extends Migration { async up() { + const result = await this.app.version.satisfies('<0.14.0-alpha.1'); + if (!result) { + return; + } const systemSettings = this.db.getRepository('systemSettings'); let instance: Model = await systemSettings.findOne(); if (instance?.options?.mobileSchemaUid) { diff --git a/packages/plugins/@nocobase/plugin-mobile-client/src/server/migrations/20231215215233-mobile-container-uid.ts b/packages/plugins/@nocobase/plugin-mobile-client/src/server/migrations/20231215215233-mobile-container-uid.ts new file mode 100644 index 000000000..a9101b462 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-mobile-client/src/server/migrations/20231215215233-mobile-container-uid.ts @@ -0,0 +1,53 @@ +import { Model } from '@nocobase/database'; +import { Migration } from '@nocobase/server'; + +export default class extends Migration { + async up() { + const result = await this.app.version.satisfies('<0.17.0-alpha.8'); + if (!result) { + return; + } + const systemSettings = this.db.getRepository('systemSettings'); + const instance: Model = await systemSettings.findOne(); + if (!instance?.options?.mobileSchemaUid) { + return; + } + const UiSchemas = this.db.getModel('uiSchemas'); + await this.db.sequelize.transaction(async (transaction) => { + await UiSchemas.update( + { + 'x-uid': 'nocobase-mobile-container', + }, + { + transaction, + where: { + 'x-uid': instance?.options?.mobileSchemaUid, + }, + }, + ); + await this.db.getModel('uiSchemaTreePath').update( + { + descendant: 'nocobase-mobile-container', + }, + { + transaction, + where: { + descendant: instance?.options?.mobileSchemaUid, + }, + }, + ); + await this.db.getModel('uiSchemaTreePath').update( + { + ancestor: 'nocobase-mobile-container', + }, + { + transaction, + where: { + ancestor: instance?.options?.mobileSchemaUid, + }, + }, + ); + }); + console.log(instance?.options?.mobileSchemaUid); + } +} diff --git a/packages/plugins/@nocobase/plugin-mobile-client/src/server/plugin.ts b/packages/plugins/@nocobase/plugin-mobile-client/src/server/plugin.ts index a989d9824..02aa05d55 100644 --- a/packages/plugins/@nocobase/plugin-mobile-client/src/server/plugin.ts +++ b/packages/plugins/@nocobase/plugin-mobile-client/src/server/plugin.ts @@ -15,16 +15,10 @@ export class MobileClientPlugin extends Plugin { } async install() { - // const repository = this.app.db.getRepository('uiRoutes'); - // for (const values of routes) { - // await repository.create({ - // values, - // }); - // } const uiSchemas = this.db.getRepository('uiSchemas'); - const systemSettings = this.db.getRepository('systemSettings'); - const schema = await uiSchemas.insert({ + await uiSchemas.insert({ type: 'void', + 'x-uid': 'nocobase-mobile-container', 'x-component': 'MContainer', 'x-designer': 'MContainer.Designer', 'x-component-props': {}, @@ -47,9 +41,6 @@ export class MobileClientPlugin extends Plugin { }, }, }); - const instance = await systemSettings.findOne(); - instance.set('options.mobileSchemaUid', schema['x-uid']); - await instance.save(); } async afterEnable() {} diff --git a/packages/plugins/@nocobase/plugin-workflow/src/server/migrations/20230612021134-manual-collection-block.ts b/packages/plugins/@nocobase/plugin-workflow/src/server/migrations/20230612021134-manual-collection-block.ts index 14945a73e..ec350d80d 100644 --- a/packages/plugins/@nocobase/plugin-workflow/src/server/migrations/20230612021134-manual-collection-block.ts +++ b/packages/plugins/@nocobase/plugin-workflow/src/server/migrations/20230612021134-manual-collection-block.ts @@ -106,6 +106,7 @@ export default class extends Migration { } const { db } = this.context; const NodeRepo = db.getRepository('flow_nodes'); + await NodeRepo.collection.sync(); await db.sequelize.transaction(async (transaction) => { const nodes = await NodeRepo.find({ filter: {