mirror of
https://gitee.com/nocobase/nocobase.git
synced 2025-05-05 13:39:24 +08:00
* feat: init * fix: mobile layout * feat: more code * feat: improve navigate bar * fix: mobile title * feat: improve code * fix: add settings and initailzer * fix: settings * fix: tabbar items settings * feat: tabbar initializer * fix: api * fix: styles * feat: navbar * feat: navigate bar tabs initializer * feat: navigate bar tab settings * feat: navigation bar actions * fix: bug * fix: bug * fix: bug * fix: tabbar active * fix: bug * fix: mobile login and layout * fix: update version * fix: build error * feat: plugin settings support link * fix: add mobile meta * fix: desktop mode * fix: remove old code and change collection name and mobile path * fix: tabbar and tabs initialer layout * fix: initializer style * fix: adjust schema position * fix: mobile style * fix: delete relation resource and home page bug * fix: support multi app * fix: not found page * fix: js bridge * fix: bug * fix: navigation bar schema flat * fix: navigation bar action style * fix: change version * fix: mobile meta and real mobile test * refactor: folder and name * fix: navigation bar sticky and zIndex * fix: full mobile schema * fix: mobile readme and package.json * fix: e2e bug * fix: bug * fix: tabbar style on productino * fix: bug * fix: rename MobileTabBar.Page * fix: support tabbar sort * fix: support page tabs sort * fix: i18n * fix: settings utils import bug * docs: api doc * fix: qrcode refresh * test: unit tests * fix: bug * fix: unit test * fix: build bug * fix: e2e test * fix: overflow scroll * fix: bug * fix: scroll and overflow * fix: bug * fix: e2e expect await * fix: e2e bug * fix: bug * fix: change name * fix: add more e2e * fix: page header * fix: tab support icon * fix: bug * fix: bug * fix: docs * fix(T-4811): scroll bar too long * fix(T-4810): desktop mode * fix: e2e * fix(T-4812): title empty * fix: unit test * feat: hide Open mode option in mobile mode * feat: change default value of Open mode on mobile * feat: add OpenModeProvider * feat: support page mode * fix: fix build * test: update unit tests * chore: remove pro-plugins * fix: bug * fix(T-4812): title is required * fix: bug * fix: bug * fix: bug * fix: bug * refactor: remove z-index * refactor: make better for subpages * fix: drag bug * fix: bug * fix: theme bug * fix(T-4859): create tab bar title empty * fix(T-4857): action too long * fix: e2e bug * fix: remove comment * fix: bug * fix: theme bug * fix: should provider modal component * fix: bug --------- Co-authored-by: chenos <chenlinxh@gmail.com> Co-authored-by: Zeke Zhang <958414905@qq.com>
181 lines
5.0 KiB
TypeScript
181 lines
5.0 KiB
TypeScript
/**
|
|
* This file is part of the NocoBase (R) project.
|
|
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
* Authors: NocoBase Team.
|
|
*
|
|
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
*/
|
|
|
|
import { set } from 'lodash';
|
|
import React, { createElement } from 'react';
|
|
import { Outlet } from 'react-router-dom';
|
|
|
|
import { Icon } from '../icon';
|
|
import type { Application } from './Application';
|
|
import type { RouteType } from './RouterManager';
|
|
|
|
export const ADMIN_SETTINGS_KEY = 'admin.settings.';
|
|
export const ADMIN_SETTINGS_PATH = '/admin/settings/';
|
|
export const SNIPPET_PREFIX = 'pm.';
|
|
|
|
export interface PluginSettingOptions {
|
|
title: any;
|
|
/**
|
|
* @default Outlet
|
|
*/
|
|
Component?: RouteType['Component'];
|
|
icon?: string;
|
|
/**
|
|
* sort, the smaller the number, the higher the priority
|
|
* @default 0
|
|
*/
|
|
sort?: number;
|
|
aclSnippet?: string;
|
|
link?: string;
|
|
[index: string]: any;
|
|
}
|
|
|
|
export interface PluginSettingsPageType {
|
|
label?: string | React.ReactElement;
|
|
title: string | React.ReactElement;
|
|
link?: string;
|
|
key: string;
|
|
icon: any;
|
|
path: string;
|
|
sort?: number;
|
|
name?: string;
|
|
isAllow?: boolean;
|
|
topLevelName?: string;
|
|
aclSnippet: string;
|
|
children?: PluginSettingsPageType[];
|
|
[index: string]: any;
|
|
}
|
|
|
|
export class PluginSettingsManager {
|
|
protected settings: Record<string, PluginSettingOptions> = {};
|
|
protected aclSnippets: string[] = [];
|
|
public app: Application;
|
|
private cachedList = {};
|
|
|
|
constructor(_pluginSettings: Record<string, PluginSettingOptions>, app: Application) {
|
|
this.app = app;
|
|
Object.entries(_pluginSettings || {}).forEach(([name, pluginSettingOptions]) => {
|
|
this.add(name, pluginSettingOptions);
|
|
});
|
|
}
|
|
|
|
clearCache() {
|
|
this.cachedList = {};
|
|
}
|
|
|
|
setAclSnippets(aclSnippets: string[]) {
|
|
this.aclSnippets = aclSnippets;
|
|
}
|
|
|
|
getAclSnippet(name: string) {
|
|
const setting = this.settings[name];
|
|
if (setting?.skipAclConfigure) {
|
|
return null;
|
|
}
|
|
return setting?.aclSnippet ? setting.aclSnippet : `${SNIPPET_PREFIX}${name}`;
|
|
}
|
|
|
|
getRouteName(name: string) {
|
|
return `${ADMIN_SETTINGS_KEY}${name}`;
|
|
}
|
|
|
|
getRoutePath(name: string) {
|
|
return `${ADMIN_SETTINGS_PATH}${name.replaceAll('.', '/')}`;
|
|
}
|
|
|
|
add(name: string, options: PluginSettingOptions) {
|
|
const nameArr = name.split('.');
|
|
const topLevelName = nameArr[0];
|
|
this.settings[name] = {
|
|
...this.settings[name],
|
|
Component: Outlet,
|
|
...options,
|
|
name,
|
|
topLevelName: options.topLevelName || topLevelName,
|
|
};
|
|
// add children
|
|
if (nameArr.length > 1) {
|
|
set(this.settings, nameArr.join('.children.'), this.settings[name]);
|
|
}
|
|
|
|
// add route
|
|
this.app.router.add(this.getRouteName(name), {
|
|
path: this.getRoutePath(name),
|
|
Component: this.settings[name].Component,
|
|
});
|
|
}
|
|
|
|
remove(name: string) {
|
|
// delete self and children
|
|
Object.keys(this.settings).forEach((key) => {
|
|
if (key.startsWith(name)) {
|
|
delete this.settings[key];
|
|
this.app.router.remove(`${ADMIN_SETTINGS_KEY}${key}`);
|
|
}
|
|
});
|
|
}
|
|
|
|
hasAuth(name: string) {
|
|
if (this.aclSnippets.includes(`!${this.getAclSnippet('*')}`)) return false;
|
|
return this.aclSnippets.includes(`!${this.getAclSnippet(name)}`) === false;
|
|
}
|
|
|
|
getSetting(name: string) {
|
|
return this.settings[name];
|
|
}
|
|
|
|
has(name: string) {
|
|
const hasAuth = this.hasAuth(name);
|
|
if (!hasAuth) return false;
|
|
return !!this.getSetting(name);
|
|
}
|
|
|
|
get(name: string, filterAuth = true): PluginSettingsPageType {
|
|
const isAllow = this.hasAuth(name);
|
|
const pluginSetting = this.getSetting(name);
|
|
if ((filterAuth && !isAllow) || !pluginSetting) return null;
|
|
const children = Object.keys(pluginSetting.children || {})
|
|
.sort((a, b) => a.localeCompare(b)) // sort by name
|
|
.map((key) => this.get(pluginSetting.children[key].name, filterAuth))
|
|
.filter(Boolean)
|
|
.sort((a, b) => (a.sort || 0) - (b.sort || 0));
|
|
const { title, icon, aclSnippet, ...others } = pluginSetting;
|
|
return {
|
|
...others,
|
|
aclSnippet: this.getAclSnippet(name),
|
|
title,
|
|
isAllow,
|
|
label: title,
|
|
icon: typeof icon === 'string' ? createElement(Icon, { type: icon }) : icon,
|
|
path: this.getRoutePath(name),
|
|
key: name,
|
|
children: children.length ? children : undefined,
|
|
};
|
|
}
|
|
|
|
getList(filterAuth = true): PluginSettingsPageType[] {
|
|
const cacheKey = JSON.stringify(filterAuth);
|
|
if (this.cachedList[cacheKey]) return this.cachedList[cacheKey];
|
|
|
|
return (this.cachedList[cacheKey] = Array.from(
|
|
new Set(Object.values(this.settings).map((item) => item.topLevelName)),
|
|
)
|
|
.sort((a, b) => a.localeCompare(b)) // sort by name
|
|
.map((name) => this.get(name, filterAuth))
|
|
.filter(Boolean)
|
|
.sort((a, b) => (a.sort || 0) - (b.sort || 0)));
|
|
}
|
|
|
|
getAclSnippets() {
|
|
return Object.keys(this.settings)
|
|
.map((name) => this.getAclSnippet(name))
|
|
.filter(Boolean);
|
|
}
|
|
}
|