后端数据库接口统一使用数据库配置文件来传输配置信息,给后端代码加上注释,前端正在实现动态加载主题和插件
This commit is contained in:
parent
c2bb2d21d9
commit
ddbb770923
@ -1,22 +0,0 @@
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct Config {
|
||||
pub info: Info,
|
||||
pub database: Database,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct Info {
|
||||
pub install: bool,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct Database {
|
||||
pub db_type : String,
|
||||
pub address : String,
|
||||
pub prot : u32,
|
||||
pub user : String,
|
||||
pub password : String,
|
||||
pub db_name : String,
|
||||
}
|
@ -1,3 +1,6 @@
|
||||
# config/config.toml
|
||||
# 配置文件
|
||||
|
||||
[info]
|
||||
install = false
|
||||
|
34
backend/src/config/mod.rs
Normal file
34
backend/src/config/mod.rs
Normal file
@ -0,0 +1,34 @@
|
||||
// config/mod.rs
|
||||
/*
|
||||
配置文件结构和操作
|
||||
*/
|
||||
use std::fs;
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct Config {
|
||||
pub info: Info,
|
||||
pub database: Database,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct Info {
|
||||
pub install: bool,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct Database {
|
||||
pub db_type: String,
|
||||
pub address: String,
|
||||
pub prot: u32,
|
||||
pub user: String,
|
||||
pub password: String,
|
||||
pub db_name: String,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
/// 读取配置文件
|
||||
pub fn read(path: &str) -> Result<Self, Box<dyn std::error::Error>> {
|
||||
Ok(toml::from_str(&fs::read_to_string(path)?)?)
|
||||
}
|
||||
}
|
@ -1,68 +1,66 @@
|
||||
// main.rs
|
||||
mod sql;
|
||||
mod config;
|
||||
use rocket::{ get, launch, routes};
|
||||
use rocket::serde::json::Json; // Added import for Json
|
||||
use once_cell::sync::Lazy;
|
||||
use rocket::http::Status;
|
||||
use rocket::response::status;
|
||||
use std::sync::Arc; // Added import for Arc and Mutex
|
||||
use tokio::sync::Mutex;
|
||||
mod sql;
|
||||
use crate::sql::Database;
|
||||
use once_cell::sync::Lazy;
|
||||
use rocket::{get, http::Status, launch, response::status, routes, serde::json::Json};
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
// 修改全局变量的类型定义
|
||||
static GLOBAL_SQL: Lazy<Arc<Mutex<Option<Database>>>> = Lazy::new(|| {
|
||||
Arc::new(Mutex::new(None))
|
||||
});
|
||||
/* 修改全局变量的类型定义 */
|
||||
static DB: Lazy<Arc<Mutex<Option<Database>>>> = Lazy::new(|| Arc::new(Mutex::new(None)));
|
||||
|
||||
// 修改数据库连接函数
|
||||
async fn connect_database() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let database = sql::Database::init().await?;
|
||||
let mut lock = GLOBAL_SQL.lock().await;
|
||||
*lock = Some(database);
|
||||
/* 数据库连接函数 */
|
||||
async fn init_db(database: config::Database) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let database = Database::init(database).await?;
|
||||
*DB.lock().await = Some(database);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/* 获取数据库的引用 */
|
||||
async fn get_db() -> Result<Database, Box<dyn std::error::Error>> {
|
||||
let lock = GLOBAL_SQL.lock().await;
|
||||
match &*lock {
|
||||
Some(db) => Ok(db.clone()),
|
||||
None => Err("Database not initialized".into())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[get("/sql")]
|
||||
async fn ssql() -> Result<Json<Vec<std::collections::HashMap<String, String>>>, status::Custom<String>> {
|
||||
let db = get_db().await.map_err(|e| {
|
||||
eprintln!("Database error: {}", e);
|
||||
status::Custom(Status::InternalServerError, format!("Database error: {}", e))
|
||||
})?;
|
||||
|
||||
let query_result = db.get_db()
|
||||
.query("SELECT * FROM info".to_string()) // 确保这里是正确的表名
|
||||
DB.lock()
|
||||
.await
|
||||
.map_err(|e| {
|
||||
eprintln!("Query error: {}", e);
|
||||
status::Custom(Status::InternalServerError, format!("Query error: {}", e))
|
||||
.clone()
|
||||
.ok_or_else(|| "Database not initialized".into())
|
||||
}
|
||||
/* 用于测试数据库 */
|
||||
#[get("/sql")]
|
||||
async fn ssql(
|
||||
) -> Result<Json<Vec<std::collections::HashMap<String, String>>>, status::Custom<String>> {
|
||||
let db = get_db().await.map_err(|e| {
|
||||
status::Custom(
|
||||
Status::InternalServerError,
|
||||
format!("Database error: {}", e),
|
||||
)
|
||||
})?;
|
||||
|
||||
let query_result = db
|
||||
.get_db()
|
||||
.query("SELECT * FROM info".to_string())
|
||||
.await
|
||||
.map_err(|e| status::Custom(Status::InternalServerError, format!("Query error: {}", e)))?;
|
||||
|
||||
Ok(Json(query_result))
|
||||
}
|
||||
|
||||
|
||||
/* 安装接口 */
|
||||
#[get("/install")]
|
||||
async fn install() -> status::Custom<String> {
|
||||
match connect_database().await {
|
||||
Ok(_) => status::Custom(Status::Ok, "Database connected successfully".to_string()),
|
||||
Err(e) => status::Custom(Status::InternalServerError, format!("Failed to connect: {}", e))
|
||||
}
|
||||
get_db()
|
||||
.await
|
||||
.map(|_| status::Custom(Status::Ok, "Database connected successfully".into()))
|
||||
.unwrap_or_else(|e| {
|
||||
status::Custom(
|
||||
Status::InternalServerError,
|
||||
format!("Failed to connect: {}", e),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
/* 启动函数 */
|
||||
#[launch]
|
||||
async fn rocket() -> _ {
|
||||
connect_database().await.expect("Failed to connect to database");
|
||||
rocket::build()
|
||||
.mount("/api", routes![install,ssql])
|
||||
let config = config::Config::read("./src/config/config.toml").expect("Failed to read config");
|
||||
init_db(config.database)
|
||||
.await
|
||||
.expect("Failed to connect to database");
|
||||
rocket::build().mount("/api", routes![install, ssql])
|
||||
}
|
@ -1,57 +1,42 @@
|
||||
// mod.rs
|
||||
// sql/mod.rs
|
||||
/*
|
||||
定义了数据库具有的特征和方法
|
||||
*/
|
||||
mod postgresql;
|
||||
use std::{collections::HashMap, fs};
|
||||
use toml;
|
||||
use crate::config::Config;
|
||||
use std::collections::HashMap;
|
||||
use super::config;
|
||||
use async_trait::async_trait;
|
||||
use std::error::Error;
|
||||
use std::sync::Arc;
|
||||
|
||||
|
||||
#[async_trait]
|
||||
pub trait Databasetrait: Send + Sync {
|
||||
async fn connect(
|
||||
address: String,
|
||||
port: u32,
|
||||
user: String,
|
||||
password: String,
|
||||
dbname: String,
|
||||
) -> Result<Self, Box<dyn Error>> where Self: Sized;
|
||||
pub trait DatabaseTrait: Send + Sync {
|
||||
// 连接数据库
|
||||
async fn connect(database: config::Database) -> Result<Self, Box<dyn Error>> where Self: Sized;
|
||||
// 执行查询
|
||||
async fn query<'a>(&'a self, query: String) -> Result<Vec<HashMap<String, String>>, Box<dyn Error + 'a>>;
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Database {
|
||||
pub db: Arc<Box<dyn Databasetrait>>,
|
||||
// 数据库实例
|
||||
pub db: Arc<Box<dyn DatabaseTrait>>,
|
||||
}
|
||||
|
||||
impl Database {
|
||||
pub fn get_db(&self) -> &Box<dyn Databasetrait> {
|
||||
// 获取当前数据库实例
|
||||
pub fn get_db(&self) -> &Box<dyn DatabaseTrait> {
|
||||
&self.db
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化数据库
|
||||
pub async fn init(database: config::Database) -> Result<Self, Box<dyn Error>> {
|
||||
let db = match database.db_type.as_str() {
|
||||
"postgresql" => postgresql::Postgresql::connect(database).await?,
|
||||
_ => return Err("unknown database type".into()),
|
||||
};
|
||||
|
||||
|
||||
impl Database {
|
||||
pub async fn init() -> Result<Database, Box<dyn Error>> {
|
||||
let config_string = fs::read_to_string("./src/config.toml")
|
||||
.map_err(|e| Box::new(e) as Box<dyn Error>)?;
|
||||
let config: Config = toml::from_str(&config_string)
|
||||
.map_err(|e| Box::new(e) as Box<dyn Error>)?;
|
||||
|
||||
match config.database.db_type.as_str() {
|
||||
"postgresql" => {
|
||||
let db = postgresql::Postgresql::connect(
|
||||
config.database.address,
|
||||
config.database.prot,
|
||||
config.database.user,
|
||||
config.database.password,
|
||||
config.database.db_name,
|
||||
).await?;
|
||||
Ok(Database {
|
||||
db: Arc::new(Box::new(db))
|
||||
})
|
||||
}
|
||||
_ => Err(anyhow::anyhow!("unknown database type").into()),
|
||||
}
|
||||
Ok(Self { db: Arc::new(Box::new(db)) })
|
||||
}
|
||||
}
|
@ -1,27 +1,28 @@
|
||||
// sql/psotgresql.rs
|
||||
/*
|
||||
为postgresql数据库实现具体的方法
|
||||
*/
|
||||
use super::DatabaseTrait;
|
||||
use crate::config;
|
||||
use async_trait::async_trait;
|
||||
use sqlx::{PgPool, Row,Column};
|
||||
use sqlx::{Column, PgPool, Row};
|
||||
use std::{collections::HashMap, error::Error};
|
||||
use super::Databasetrait;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Postgresql {
|
||||
pool: PgPool,
|
||||
}
|
||||
|
||||
|
||||
|
||||
#[async_trait]
|
||||
impl Databasetrait for Postgresql {
|
||||
async fn connect(
|
||||
address: String,
|
||||
port: u32,
|
||||
user: String,
|
||||
password: String,
|
||||
dbname: String,
|
||||
) -> Result<Self, Box<dyn Error>> {
|
||||
impl DatabaseTrait for Postgresql {
|
||||
async fn connect(database: config::Database) -> Result<Self, Box<dyn Error>> {
|
||||
let connection_str = format!(
|
||||
"postgres://{}:{}@{}:{}/{}",
|
||||
user, password, address, port, dbname
|
||||
database.user,
|
||||
database.password,
|
||||
database.address,
|
||||
database.prot,
|
||||
database.db_name
|
||||
);
|
||||
|
||||
let pool = PgPool::connect(&connection_str)
|
||||
@ -30,7 +31,10 @@ impl Databasetrait for Postgresql {
|
||||
|
||||
Ok(Postgresql { pool })
|
||||
}
|
||||
async fn query<'a>(&'a self, query: String) -> Result<Vec<HashMap<String, String>>, Box<dyn Error + 'a>> {
|
||||
async fn query<'a>(
|
||||
&'a self,
|
||||
query: String,
|
||||
) -> Result<Vec<HashMap<String, String>>, Box<dyn Error + 'a>> {
|
||||
let rows = sqlx::query(&query)
|
||||
.fetch_all(&self.pool)
|
||||
.await
|
||||
|
61
frontend/hooks/plugin/usePluginLoader.ts
Normal file
61
frontend/hooks/plugin/usePluginLoader.ts
Normal file
@ -0,0 +1,61 @@
|
||||
// hooks/usePluginLoader.ts
|
||||
import { useState, useEffect } from 'react';
|
||||
import { DependencyChecker } from '../../services/dependency-checker';
|
||||
import { PerformanceMonitor } from '../../services/performance-monitor';
|
||||
import { CacheManager } from '../../services/cache-manager';
|
||||
|
||||
/**
|
||||
* 插件加载Hook
|
||||
* @param pluginId 插件ID
|
||||
* @returns 加载状态和错误信息
|
||||
*/
|
||||
export function usePluginLoader(pluginId: string) {
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const loadPlugin = async () => {
|
||||
try {
|
||||
// 检查缓存
|
||||
const cached = CacheManager.get(`plugin:${pluginId}`);
|
||||
if (cached) {
|
||||
return cached;
|
||||
}
|
||||
|
||||
// 开始性能监控
|
||||
const monitor = PerformanceMonitor.startMonitoring(pluginId);
|
||||
|
||||
// 获取插件配置
|
||||
const plugin = await fetch(`/api/admin/plugins/${pluginId}`).then(r => r.json());
|
||||
|
||||
// 检查依赖
|
||||
if (plugin.dependencies) {
|
||||
const dependenciesOk = await DependencyChecker.checkDependencies(plugin.dependencies);
|
||||
if (!dependenciesOk) {
|
||||
throw new Error('依赖检查失败');
|
||||
}
|
||||
}
|
||||
|
||||
// 加载插件
|
||||
const component = await import(/* @vite-ignore */plugin.entry);
|
||||
|
||||
// 缓存结果
|
||||
CacheManager.set(`plugin:${pluginId}`, component);
|
||||
|
||||
// 结束监控
|
||||
monitor.end();
|
||||
|
||||
return component;
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : '插件加载失败');
|
||||
throw err;
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
loadPlugin();
|
||||
}, [pluginId]);
|
||||
|
||||
return { loading, error };
|
||||
}
|
33
frontend/hooks/theme/themeManager.tsx
Normal file
33
frontend/hooks/theme/themeManager.tsx
Normal file
@ -0,0 +1,33 @@
|
||||
// components/theme/themeManager.tsx
|
||||
import React, { useEffect } from 'react';
|
||||
import { useThemeLoader } from './useThemeLoader';
|
||||
|
||||
interface ThemeManagerProps {
|
||||
themeName: string;
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
export const ThemeManager: React.FC<ThemeManagerProps> = ({
|
||||
themeName,
|
||||
children
|
||||
}) => {
|
||||
const { theme, loading, error } = useThemeLoader(themeName);
|
||||
|
||||
if (loading) {
|
||||
return <div>加载主题中...</div>;
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return <div>主题加载失败: {error}</div>;
|
||||
}
|
||||
|
||||
if (!theme) {
|
||||
return <div>主题未找到</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={`theme-${themeName}`}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
};
|
27
frontend/hooks/theme/themeSwitch.tsx
Normal file
27
frontend/hooks/theme/themeSwitch.tsx
Normal file
@ -0,0 +1,27 @@
|
||||
// components/theme/themeSwitch.tsx
|
||||
import React from 'react';
|
||||
|
||||
interface ThemeSwitchProps {
|
||||
currentTheme: string;
|
||||
availableThemes: string[];
|
||||
onThemeChange: (themeName: string) => void;
|
||||
}
|
||||
|
||||
export const ThemeSwitch: React.FC<ThemeSwitchProps> = ({
|
||||
currentTheme,
|
||||
availableThemes,
|
||||
onThemeChange
|
||||
}) => {
|
||||
return (
|
||||
<select
|
||||
value={currentTheme}
|
||||
onChange={(e) => onThemeChange(e.target.value)}
|
||||
>
|
||||
{availableThemes.map(theme => (
|
||||
<option key={theme} value={theme}>
|
||||
{theme}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
);
|
||||
};
|
66
frontend/hooks/theme/useThemeLoader.ts
Normal file
66
frontend/hooks/theme/useThemeLoader.ts
Normal file
@ -0,0 +1,66 @@
|
||||
// hooks/theme/useThemeLoader.ts
|
||||
import { useState, useEffect } from 'react';
|
||||
import { ThemeConfig } from '../../types/theme';
|
||||
import { DependencyChecker } from '../../services/dependency-checker';
|
||||
import { PerformanceMonitor } from '../../services/performance-monitor';
|
||||
import { CacheManager } from '../../services/cache-manager';
|
||||
|
||||
/**
|
||||
* 主题加载Hook
|
||||
*/
|
||||
export function useThemeLoader(themeName: string) {
|
||||
const [theme, setTheme] = useState<ThemeConfig | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const loadTheme = async () => {
|
||||
const monitor = PerformanceMonitor.startMonitoring(`theme:${themeName}`);
|
||||
|
||||
try {
|
||||
// 1. 检查缓存
|
||||
const cached = CacheManager.get(`theme:${themeName}`);
|
||||
if (cached) {
|
||||
setTheme(cached);
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. 获取主题配置
|
||||
const response = await fetch(`/api/themes/${themeName}`);
|
||||
const themeConfig: ThemeConfig = await response.json();
|
||||
|
||||
// 3. 检查依赖
|
||||
if (themeConfig.dependencies) {
|
||||
const dependenciesOk = await DependencyChecker.checkDependencies(
|
||||
themeConfig.dependencies
|
||||
);
|
||||
if (!dependenciesOk) {
|
||||
throw new Error('主题依赖检查失败');
|
||||
}
|
||||
}
|
||||
|
||||
// 4. 加载主题样式
|
||||
if (themeConfig.globalStyles) {
|
||||
const styleElement = document.createElement('style');
|
||||
styleElement.innerHTML = themeConfig.globalStyles;
|
||||
document.head.appendChild(styleElement);
|
||||
}
|
||||
|
||||
// 5. 缓存主题配置
|
||||
CacheManager.set(`theme:${themeName}`, themeConfig, 3600000); // 1小时缓存
|
||||
|
||||
setTheme(themeConfig);
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : '加载主题失败');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
monitor.end();
|
||||
}
|
||||
};
|
||||
|
||||
loadTheme();
|
||||
}, [themeName]);
|
||||
|
||||
return { theme, loading, error };
|
||||
}
|
41
frontend/services/cache-manager.ts
Normal file
41
frontend/services/cache-manager.ts
Normal file
@ -0,0 +1,41 @@
|
||||
// services/cache-manager.ts
|
||||
/**
|
||||
* 缓存管理服务
|
||||
*/
|
||||
export class CacheManager {
|
||||
private static cache = new Map<string, any>();
|
||||
private static ttl = new Map<string, number>();
|
||||
|
||||
/**
|
||||
* 设置缓存
|
||||
* @param key 缓存键
|
||||
* @param value 缓存值
|
||||
* @param expiresIn 过期时间(毫秒)
|
||||
*/
|
||||
static set(key: string, value: any, expiresIn: number = 3600000) {
|
||||
this.cache.set(key, value);
|
||||
this.ttl.set(key, Date.now() + expiresIn);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取缓存
|
||||
* @param key 缓存键
|
||||
* @returns 缓存值或null(如果已过期或不存在)
|
||||
*/
|
||||
static get(key: string): any {
|
||||
if (this.ttl.has(key) && Date.now() > (this.ttl.get(key) || 0)) {
|
||||
this.cache.delete(key);
|
||||
this.ttl.delete(key);
|
||||
return null;
|
||||
}
|
||||
return this.cache.get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除所有缓存
|
||||
*/
|
||||
static clear() {
|
||||
this.cache.clear();
|
||||
this.ttl.clear();
|
||||
}
|
||||
}
|
29
frontend/services/dependency-checker.ts
Normal file
29
frontend/services/dependency-checker.ts
Normal file
@ -0,0 +1,29 @@
|
||||
// services/dependency-checker.ts
|
||||
|
||||
import { Dependency } from "../types/common";
|
||||
|
||||
/**
|
||||
* 依赖检查器服务
|
||||
*/
|
||||
export class DependencyChecker {
|
||||
/**
|
||||
* 检查依赖项是否满足要求
|
||||
* @param dependencies 需要检查的依赖项列表
|
||||
* @returns 是否所有依赖都满足要求
|
||||
*/
|
||||
static async checkDependencies(dependencies: Dependency[]): Promise<boolean> {
|
||||
for (const dep of dependencies) {
|
||||
const response = await fetch(`/api/dependencies/check`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(dep)
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
console.error(`依赖检查失败: ${dep.id}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
54
frontend/services/performance-monitor.ts
Normal file
54
frontend/services/performance-monitor.ts
Normal file
@ -0,0 +1,54 @@
|
||||
// services/performance-monitor.ts
|
||||
/**
|
||||
* 性能指标接口
|
||||
*/
|
||||
interface PerformanceMetrics {
|
||||
loadTime: number; // 加载时间(毫秒)
|
||||
memoryUsage: number; // 内存使用量(bytes)
|
||||
errors: string[]; // 错误信息列表
|
||||
}
|
||||
|
||||
/**
|
||||
* 性能监控服务
|
||||
*/
|
||||
export class PerformanceMonitor {
|
||||
private static metrics: Map<string, PerformanceMetrics> = new Map();
|
||||
|
||||
/**
|
||||
* 开始监控性能
|
||||
* @param id 监控对象的唯一标识符
|
||||
*/
|
||||
static startMonitoring(id: string) {
|
||||
const startTime = performance.now();
|
||||
const startMemory = (performance as any).memory?.usedJSHeapSize || 0;
|
||||
|
||||
return {
|
||||
// 结束监控并记录指标
|
||||
end: () => {
|
||||
const loadTime = performance.now() - startTime;
|
||||
const memoryUsage = ((performance as any).memory?.usedJSHeapSize || 0) - startMemory;
|
||||
|
||||
this.metrics.set(id, {
|
||||
loadTime,
|
||||
memoryUsage,
|
||||
errors: []
|
||||
});
|
||||
},
|
||||
|
||||
// 记录错误信息
|
||||
error: (err: string) => {
|
||||
const metrics = this.metrics.get(id) || { loadTime: 0, memoryUsage: 0, errors: [] };
|
||||
metrics.errors.push(err);
|
||||
this.metrics.set(id, metrics);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取性能指标
|
||||
* @param id 监控对象的唯一标识符
|
||||
*/
|
||||
static getMetrics(id: string): PerformanceMetrics | undefined {
|
||||
return this.metrics.get(id);
|
||||
}
|
||||
}
|
39
frontend/services/plugin-communication.ts
Normal file
39
frontend/services/plugin-communication.ts
Normal file
@ -0,0 +1,39 @@
|
||||
// services/plugin-communication.ts
|
||||
/**
|
||||
* 消息处理器类型定义
|
||||
*/
|
||||
type MessageHandler = (data: any) => void;
|
||||
|
||||
/**
|
||||
* 插件通信服务
|
||||
*/
|
||||
export class PluginMessenger {
|
||||
// 存储所有消息处理器
|
||||
private static handlers: Map<string, Set<MessageHandler>> = new Map();
|
||||
|
||||
/**
|
||||
* 订阅消息通道
|
||||
* @param channel 通道名称
|
||||
* @param handler 消息处理函数
|
||||
* @returns 取消订阅函数
|
||||
*/
|
||||
static subscribe(channel: string, handler: MessageHandler) {
|
||||
if (!this.handlers.has(channel)) {
|
||||
this.handlers.set(channel, new Set());
|
||||
}
|
||||
this.handlers.get(channel)?.add(handler);
|
||||
|
||||
return () => {
|
||||
this.handlers.get(channel)?.delete(handler);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 发布消息到指定通道
|
||||
* @param channel 通道名称
|
||||
* @param data 消息数据
|
||||
*/
|
||||
static publish(channel: string, data: any) {
|
||||
this.handlers.get(channel)?.forEach(handler => handler(data));
|
||||
}
|
||||
}
|
@ -1,3 +1,4 @@
|
||||
//main.tsx
|
||||
import * as React from "react";
|
||||
import "./main.css"
|
||||
import DynamicPage from "./page/page.tsx"
|
||||
@ -7,7 +8,7 @@ import {createContext} from "react";
|
||||
export const serverAddressContext=createContext("localhost:8080")
|
||||
// 动态路由
|
||||
const RouterListener: React.FC = () => {
|
||||
let pathname = location.pathname.split("/");
|
||||
const pathname = location.pathname.split("/");
|
||||
console.log(pathname)
|
||||
return (
|
||||
<serverAddressContext.Provider value={"localhost:8080"}>
|
||||
@ -17,7 +18,7 @@ const RouterListener: React.FC = () => {
|
||||
}
|
||||
|
||||
|
||||
ReactDOM.createRoot(document.getElementById("root")).render(
|
||||
ReactDOM.createRoot(document.getElementById("root")!).render(
|
||||
<React.StrictMode>
|
||||
<RouterListener/>
|
||||
</React.StrictMode>
|
||||
|
@ -1,3 +1,4 @@
|
||||
// page/page.tsx
|
||||
import React from "react";
|
||||
const THEMEPATH= "../../themes"
|
||||
import {serverAddressContext} from "../main.tsx";
|
||||
|
18
frontend/types/common.ts
Normal file
18
frontend/types/common.ts
Normal file
@ -0,0 +1,18 @@
|
||||
// types/common.ts
|
||||
/**
|
||||
* 版本信息接口
|
||||
*/
|
||||
export interface VersionInfo {
|
||||
major: number; // 主版本号 - 不兼容的API修改
|
||||
minor: number; // 次版本号 - 向下兼容的功能性新增
|
||||
patch: number; // 修订号 - 向下兼容的问题修正
|
||||
}
|
||||
|
||||
/**
|
||||
* 依赖项配置接口
|
||||
*/
|
||||
export interface Dependency {
|
||||
id: string; // 依赖项的唯一标识符
|
||||
version: string; // 依赖项的版本要求
|
||||
type: 'plugin' | 'theme'; // 依赖项类型:插件或主题
|
||||
}
|
23
frontend/types/plugin.ts
Normal file
23
frontend/types/plugin.ts
Normal file
@ -0,0 +1,23 @@
|
||||
// types/plugin.ts
|
||||
import {Dependency} from "./common";
|
||||
|
||||
/**
|
||||
* 插件配置接口
|
||||
*/
|
||||
export interface PluginConfig {
|
||||
id: string; // 插件唯一标识符
|
||||
name: string; // 插件名称
|
||||
version: string; // 插件版本
|
||||
displayName: string; // 插件显示名称
|
||||
description?: string; // 插件描述
|
||||
author?: string; // 插件作者
|
||||
enabled: boolean; // 插件是否启用
|
||||
icon?: string; // 插件图标URL
|
||||
adminPath?: string; // 插件管理页面路径
|
||||
entry: string; // 插件入口组件路径
|
||||
dependencies?: Dependency[]; // 插件依赖项
|
||||
performance?: {
|
||||
maxLoadTime?: number; // 最大加载时间(毫秒)
|
||||
maxMemoryUsage?: number; // 最大内存使用量(bytes)
|
||||
};
|
||||
}
|
20
frontend/types/theme.ts
Normal file
20
frontend/types/theme.ts
Normal file
@ -0,0 +1,20 @@
|
||||
// types/theme.ts
|
||||
import {Dependency} from "./common.ts";
|
||||
|
||||
/**
|
||||
* 主题配置接口
|
||||
*/
|
||||
export interface ThemeConfig {
|
||||
name: string; // 主题的唯一名称标识符
|
||||
version: string; // 主题版本
|
||||
displayName: string; // 主题显示名称
|
||||
description?: string; // 主题描述
|
||||
author?: string; // 主题作者
|
||||
preview?: string; // 主题预览图URL
|
||||
globalStyles?: string; // 主题全局样式
|
||||
dependencies?: Dependency[]; // 主题依赖项
|
||||
performance?: {
|
||||
maxLoadTime?: number; // 最大加载时间(毫秒)
|
||||
maxMemoryUsage?: number; // 最大内存使用量(bytes)
|
||||
};
|
||||
}
|
Loading…
Reference in New Issue
Block a user