mirror of
https://github.com/nocobase/nocobase
synced 2024-11-15 09:09:03 +00:00
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
This commit is contained in:
parent
b1610e6994
commit
f82b4d8726
@ -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;
|
||||
}
|
||||
|
@ -19,7 +19,9 @@ export const AppComponent: FC<AppComponentProps> = 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 <AppError app={app} error={app.error} />;
|
||||
if (app.error?.code === 'LOAD_ERROR' || app.error?.code === 'APP_ERROR') {
|
||||
return <AppError app={app} error={app.error} />;
|
||||
}
|
||||
return (
|
||||
<ErrorBoundary
|
||||
FallbackComponent={(props) => <AppError app={app} error={app.error} {...props} />}
|
||||
|
@ -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';
|
||||
};
|
||||
|
@ -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',
|
||||
|
@ -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;
|
||||
});
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -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' });
|
||||
});
|
||||
});
|
@ -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 || {};
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -32,27 +32,24 @@ export class ClientPlugin extends Plugin {
|
||||
//
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
this.db.on('systemSettings.beforeCreate', async (instance, { transaction }) => {
|
||||
const uiSchemas = this.db.getRepository<any>('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<any>('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: {},
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -10,6 +10,7 @@ export default class extends Migration {
|
||||
}
|
||||
|
||||
const r = this.db.getRepository<Repository>('storages');
|
||||
await r.collection.sync();
|
||||
const items = await r.find({
|
||||
filter: {
|
||||
type: 'local',
|
||||
|
@ -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) {
|
||||
|
@ -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(
|
||||
|
@ -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) => {
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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<any>('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() {}
|
||||
|
@ -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: {
|
||||
|
Loading…
Reference in New Issue
Block a user