实现了对拓展的定义和拓展服务

This commit is contained in:
lsy 2024-11-14 17:43:18 +08:00
parent 2b44435c2a
commit 1385251318
13 changed files with 211 additions and 43 deletions

View File

@ -8,7 +8,10 @@
* - component: 可选的组件函数 React * - component: 可选的组件函数 React
* - text: 可选的文本生成函数 * - text: 可选的文本生成函数
*/ */
export interface ExtensionType {
export class ExtensionProps {
/** 可选的操作函数,接受任意参数并返回 void */ /** 可选的操作函数,接受任意参数并返回 void */
action?: (...args: any[]) => void; action?: (...args: any[]) => void;
@ -18,3 +21,5 @@ export interface ExtensionType {
/** 可选的文本生成函数,接受任意参数并返回一个字符串 */ /** 可选的文本生成函数,接受任意参数并返回一个字符串 */
text?: (...args: any[]) => string; text?: (...args: any[]) => string;
} }

View File

@ -0,0 +1,12 @@
// File path: /d:/data/echoes/frontend/Requirements/generalRequirement.ts
/**
*
*
* - null
* - number
* - string
* - boolean
* -
* -
*/
export type SerializeType = null | number | string | boolean | { [key: string]: SerializeType } | Array<SerializeType>;

View File

@ -1,4 +1,4 @@
// File path: types/pluginType.ts // File path: ../Requirements/pluginRequirement.ts
/** /**
* *
@ -6,6 +6,11 @@
* *
* *
*/ */
import { SerializeType } from "./generalRequirement";
import { ExtensionProps } from "types/extensionRequirement";
import { useExtention } from "hooks/extensionService";
import { ExtensionService } from "service/extensionService";
export interface PluginConfig { export interface PluginConfig {
name: string; // 插件名称 name: string; // 插件名称
version: string; // 插件版本 version: string; // 插件版本
@ -16,10 +21,6 @@ export interface PluginConfig {
icon?: string; // 插件图标URL可选 icon?: string; // 插件图标URL可选
managePath?: string; // 插件管理页面路径(可选) managePath?: string; // 插件管理页面路径(可选)
configuration?: PluginConfiguration; // 插件配置 configuration?: PluginConfiguration; // 插件配置
dependencies?: {
plugins?: string[]; // 依赖的插件列表(可选)
themes?: string[]; // 依赖的主题列表(可选)
};
hooks?: { hooks?: {
onInstall?: (context: any) => {}; // 安装时调用的钩子(可选) onInstall?: (context: any) => {}; // 安装时调用的钩子(可选)
onUninstall?: (context: any) => {}; // 卸载时调用的钩子(可选) onUninstall?: (context: any) => {}; // 卸载时调用的钩子(可选)
@ -38,11 +39,32 @@ export interface PluginConfig {
* *
*/ */
export interface PluginConfiguration { export interface PluginConfiguration {
type: string; // 配置类型 [key: string]: {
properties: Record<string, {
type: string; // 属性类型
title: string; // 属性标题 title: string; // 属性标题
description?: string; // 属性描述(可选) description?: string; // 属性描述(可选)
data: any; // 额外数据(可选) data: SerializeType; // 额外数据(可选),支持序列化
}>; };
}
export interface PluginDependencies {
plugins: string[]; // 依赖的插件列表(可选)
themes: string[]; // 依赖的主题列表(可选)
}
/**
*
*
*
*/
export class usePluginProps implements ExtensionProps {
private extensionService: ExtensionService = useExtention();
private dependencies: PluginDependencies;
constructor(dependencies: PluginDependencies) {
this.dependencies = dependencies;
}
action?: ((...args: any[]) => void); // 动作函数(可选)
component?: (...args: any[]) => React.FC; // 组件函数(可选)
text?: (...args: any[]) => string; // 文本生成函数(可选)
} }

View File

@ -5,7 +5,7 @@
* *
*/ */
import React from "react"; import React from "react";
import { ExtensionType } from "types/extensionType"; import { ExtensionProps } from "types/extensionRequirement";
export interface TemplateConfig { export interface TemplateConfig {
/** /**
@ -22,7 +22,7 @@ export interface TemplateConfig {
extensions?: Record<string, { extensions?: Record<string, {
description?: string; description?: string;
extension: ExtensionType; extension: ExtensionProps;
}>; }>;
/** /**

View File

@ -8,6 +8,7 @@
* *
* *
*/ */
import { SerializeType } from "./generalRequirement";
export interface ThemeConfig { export interface ThemeConfig {
name: string; // 主题的名称 name: string; // 主题的名称
displayName: string; // 主题的显示名称 displayName: string; // 主题的显示名称
@ -23,10 +24,9 @@ export interface ThemeConfig {
}; };
/** 主题配置文件 */ /** 主题配置文件 */
settingsSchema: Record<string, { settingsSchema: Record<string, {
type: string; // 属性的数据类型 name: string; // 属性的名称
title: string; // 属性的标题
description?: string; // 属性的描述信息 description?: string; // 属性的描述信息
data?: any; // 属性的默认数据 data: SerializeType; // 属性的默认数据
}>; }>;
/** 依赖 */ /** 依赖 */
dependencies?: { dependencies?: {

View File

@ -0,0 +1,55 @@
// File path: /hooks/createServiceContext.tsx
import { createContext, ReactNode, useContext } from "react";
/**
* 使
*
* @param name -
* @param getInstance -
* @returns Provider useService
*/
interface ServiceContextResult<T> {
Provider: React.FC<{ children: ReactNode }>; // 提供服务实例的组件
useService: () => T; // 获取服务实例的钩子
}
/**
*
*
* @param name -
* @param getInstance -
* @returns Provider useService
*/
export function createServiceContext<T>(
name: string,
getInstance: () => T
): ServiceContextResult<T> {
// 创建一个上下文,初始值为 undefined。
const ServiceContext = createContext<T | undefined>(undefined);
// Provider 组件,用于提供服务实例给子组件
const Provider = ({ children }: { children: ReactNode }) => {
const service = getInstance();
return (
<ServiceContext.Provider value={service}>
{children}
</ServiceContext.Provider>
);
};
// 获取服务实例的钩子
const useService = (): T => {
const context = useContext(ServiceContext);
if (context === undefined) {
throw new Error(`use${name} must be used within a ${name}Provider`);
}
return context;
};
return {
Provider,
useService,
};
}

View File

@ -0,0 +1,41 @@
// File path: /d:/data/echoes/frontend/hooks/extensionService.tsx
/**
* Hook
*
* 访 Hook
*/
import { createContext, useContext, ReactNode } from 'react';
import { ExtensionService } from 'service/extensionService';
// 创建扩展服务上下文
const ExtensionContext = createContext<ExtensionService | undefined>(undefined);
/**
*
*
* @param children -
*/
export function ExtensionProvider({ children }: { children: ReactNode }) {
const extensionService = ExtensionService.getInstance(); // 获取扩展服务实例
return (
<ExtensionContext.Provider value={extensionService}>
{children}
</ExtensionContext.Provider>
)
}
/**
* Hook访
*
* @returns {ExtensionService} -
* @throws {Error} - ExtensionProvider
*/
export function useExtention(): ExtensionService {
const context = useContext(ExtensionContext); // 获取扩展服务上下文
if (context === undefined) {
throw new Error('useTheme must be used within a ThemeProvider'); // 抛出错误
}
return context; // 返回扩展服务实例
}

View File

@ -1,9 +1,16 @@
// service/theme/themeContext.tsx // File path: /hooks/themeContext.tsx
import { createContext, useContext, ReactNode } from 'react'; import { createContext, useContext, ReactNode } from 'react';
import { ThemeService } from 'service/themeService'; import { ThemeService } from 'service/themeService';
const ThemeContext = createContext<ThemeService | undefined>(undefined); const ThemeContext = createContext<ThemeService | undefined>(undefined);
/**
* ThemeProvider
* 使 ThemeService
*
* @param children -
*/
export function ThemeProvider({ children }: { children: ReactNode }) { export function ThemeProvider({ children }: { children: ReactNode }) {
const themeService = ThemeService.getInstance(); const themeService = ThemeService.getInstance();
@ -14,6 +21,12 @@ export function ThemeProvider({ children }: { children: ReactNode }) {
); );
} }
/**
* useTheme 访
*
* @returns ThemeService -
* @throws Error - ThemeProvider 使
*/
export function useTheme(): ThemeService { export function useTheme(): ThemeService {
const context = useContext(ThemeContext); const context = useContext(ThemeContext);
if (context === undefined) { if (context === undefined) {

View File

@ -4,38 +4,42 @@
* ExtensionManage * ExtensionManage
* *
*/ */
import { ExtensionType } from "types/extensionType"; import { ExtensionProps } from "types/extensionRequirement";
import React from "react"; import React from "react";
import { PluginConfiguration } from "types/pluginRequirement";
class ExtensionManage { export class ExtensionService {
/** 存储扩展的映射,键为扩展名称,值为插件名称和扩展的集合 */ /** 存储扩展的映射,键为扩展名称,值为插件名称和扩展的集合 */
private extensions: Map<string, Set<{ pluginName: string; extension: ExtensionType }>> = new Map(); private extensions: Map<string, Set<{ pluginName: string; extension: ExtensionProps }>> = new Map();
private configuration: Map<string, PluginConfiguration> = new Map();
/** ExtensionManage 的唯一实例 */ /** ExtensionManage 的唯一实例 */
private static instance: ExtensionManage; private static instance: ExtensionService;
/** 私有构造函数,防止外部实例化 */ /** 私有构造函数,防止外部实例化 */
private constructor() {} private constructor() { }
/** /**
* ExtensionManage * ExtensionManage
* @returns {ExtensionManage} ExtensionManage * @returns {ExtensionManage} ExtensionManage
*/ */
public static getInstance(): ExtensionManage { public static getInstance(): ExtensionService {
if (!this.instance) { if (!this.instance) {
this.instance = new ExtensionManage(); this.instance = new ExtensionService();
} }
return this.instance; return this.instance;
} }
/** 注册扩展 */ /** 注册扩展 */
private register(extensionName: string, pluginName: string, extension: ExtensionType) { private register(extensionName: string, pluginName: string, extension: ExtensionProps, pluginConfiguration: PluginConfiguration) {
const handlers = this.extensions.get(extensionName) || new Set(); const handlers = this.extensions.get(extensionName) || new Set();
this.configuration.has(extensionName) || this.configuration.set(pluginName, pluginConfiguration);
handlers.add({ pluginName, extension }); handlers.add({ pluginName, extension });
this.extensions.set(extensionName, handlers); this.extensions.set(extensionName, handlers);
} }
/** 执行扩展方法 */ /** 执行扩展方法 */
private executeExtensionMethod<T>(extensionName: string, method: keyof ExtensionType, ...args: any[]): Set<T> { private executeExtensionMethod<T>(extensionName: string, method: keyof ExtensionProps, ...args: any[]): Set<T> {
const result = new Set<T>(); const result = new Set<T>();
const handlers = this.extensions.get(extensionName); const handlers = this.extensions.get(extensionName);
@ -80,5 +84,11 @@ class ExtensionManage {
); );
this.extensions.set(extensionName, newHandlers); this.extensions.set(extensionName, newHandlers);
}); });
this.configuration.delete(pluginName);
}
//获取指定配置文件
getConfiguration(pluginName: string): PluginConfiguration | undefined {
return this.configuration.get(pluginName);
} }
} }

View File

@ -1,24 +1,34 @@
import { PluginConfig ,PluginType ,PluginConfiguration} from "types/pluginType"; // File path: /service/pluginService.ts
/**
*
*
*/
import { PluginConfiguration } from "types/pluginRequirement";
export class PluginService { export class PluginService {
private static pluginInstance: PluginService | null = null; // 单例实例 /** 单例实例 */
private pluginComponents: Map<PluginType, Set<{ private static pluginInstance: PluginService | null = null;
name:string, /** 插件组件缓存 */
configuration?:PluginConfiguration, private pluginComponents: Map<string, Set<{
managePath?: string, name: string, // 插件名称
configuration?: PluginConfiguration, // 插件配置
managePath?: string, // 管理路径
}>> = new Map();
/**
*
*/
private constructor() {};
}>> = new Map(); // 插件组件缓存 /**
private constructor (){}; *
* @returns {PluginService}
*/
public static getInstance(): PluginService { public static getInstance(): PluginService {
if (!this.pluginInstance) { if (!this.pluginInstance) {
this.pluginInstance = new PluginService(); this.pluginInstance = new PluginService();
} }
return this.pluginInstance; return this.pluginInstance;
} }
} }

View File

@ -1,5 +1,5 @@
// service/theme/themeService.ts // service/theme/themeService.ts
import type { ThemeConfig } from 'types/themeType'; import type { ThemeConfig } from 'types/themeTypeRequirement';
export class ThemeService { export class ThemeService {
private static themeInstance: ThemeService; // 单例实例 private static themeInstance: ThemeService; // 单例实例

View File

@ -1,4 +1,4 @@
import { ThemeConfig } from "types/themeType"; import { ThemeConfig } from "types/themeTypeRequirement";
export const themeConfig: ThemeConfig = { export const themeConfig: ThemeConfig = {
name: 'default', name: 'default',

View File

@ -24,7 +24,7 @@
"baseUrl": ".", "baseUrl": ".",
"paths": { "paths": {
"theme/*":["./theme/*"], "theme/*":["./theme/*"],
"types/*":["./types/*"], "types/*":["Requirements/*"],
"service/*":["./service/*"], "service/*":["./service/*"],
"hooks/*":["./hooks/*"], "hooks/*":["./hooks/*"],
}, },