From b757c1d06c68eef0bd9e9586523f98c0f14f0a6b Mon Sep 17 00:00:00 2001 From: YHH <359807859@qq.com> Date: Wed, 15 Oct 2025 00:23:19 +0800 Subject: [PATCH] =?UTF-8?q?=E9=A1=B9=E7=9B=AE=E6=89=93=E5=BC=80=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/editor-app/src-tauri/src/main.rs | 16 ++- packages/editor-app/src/App.tsx | 23 +++- packages/editor-app/src/api/tauri.ts | 7 +- packages/editor-app/src/locales/en.ts | 4 +- packages/editor-app/src/locales/zh.ts | 4 +- .../src/Services/ProjectService.ts | 125 ++++++++++++++++++ packages/editor-core/src/index.ts | 1 + 7 files changed, 172 insertions(+), 8 deletions(-) create mode 100644 packages/editor-core/src/Services/ProjectService.ts diff --git a/packages/editor-app/src-tauri/src/main.rs b/packages/editor-app/src-tauri/src/main.rs index 375a605e..21c7bb1d 100644 --- a/packages/editor-app/src-tauri/src/main.rs +++ b/packages/editor-app/src-tauri/src/main.rs @@ -2,6 +2,7 @@ #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] use tauri::Manager; +use tauri::AppHandle; // IPC Commands #[tauri::command] @@ -25,12 +26,22 @@ fn save_project(path: String, data: String) -> Result<(), String> { #[tauri::command] fn export_binary(data: Vec, output_path: String) -> Result<(), String> { - // 二进制导出逻辑 std::fs::write(&output_path, data) .map_err(|e| format!("Failed to export binary: {}", e))?; Ok(()) } +#[tauri::command] +async fn open_project_dialog(app: AppHandle) -> Result, String> { + use tauri::api::dialog::blocking::FileDialogBuilder; + + let result = FileDialogBuilder::new() + .set_title("Select Project Directory") + .pick_folder(); + + Ok(result.map(|path| path.to_string_lossy().to_string())) +} + fn main() { tauri::Builder::default() .plugin(tauri_plugin_shell::init()) @@ -47,7 +58,8 @@ fn main() { greet, open_project, save_project, - export_binary + export_binary, + open_project_dialog ]) .run(tauri::generate_context!()) .expect("error while running tauri application"); diff --git a/packages/editor-app/src/App.tsx b/packages/editor-app/src/App.tsx index 6ede39c2..a5a96105 100644 --- a/packages/editor-app/src/App.tsx +++ b/packages/editor-app/src/App.tsx @@ -1,6 +1,6 @@ import { useState, useEffect } from 'react'; import { Core, Scene } from '@esengine/ecs-framework'; -import { EditorPluginManager, UIRegistry, MessageHub, SerializerRegistry, EntityStoreService, ComponentRegistry, LocaleService, PropertyMetadataService } from '@esengine/editor-core'; +import { EditorPluginManager, UIRegistry, MessageHub, SerializerRegistry, EntityStoreService, ComponentRegistry, LocaleService, PropertyMetadataService, ProjectService } from '@esengine/editor-core'; import { SceneInspectorPlugin } from './plugins/SceneInspectorPlugin'; import { SceneHierarchy } from './components/SceneHierarchy'; import { EntityInspector } from './components/EntityInspector'; @@ -69,6 +69,7 @@ function App() { const serializerRegistry = new SerializerRegistry(); const entityStore = new EntityStoreService(messageHub); const componentRegistry = new ComponentRegistry(); + const projectService = new ProjectService(messageHub); componentRegistry.register({ name: 'Transform', @@ -96,6 +97,7 @@ function App() { Core.services.registerInstance(SerializerRegistry, serializerRegistry); Core.services.registerInstance(EntityStoreService, entityStore); Core.services.registerInstance(ComponentRegistry, componentRegistry); + Core.services.registerInstance(ProjectService, projectService); const pluginMgr = new EditorPluginManager(); pluginMgr.initialize(coreInstance, Core.services); @@ -142,11 +144,30 @@ function App() { changeLocale(newLocale); }; + const handleOpenProject = async () => { + try { + const projectPath = await TauriAPI.openProjectDialog(); + if (projectPath) { + const projectService = Core.services.resolve(ProjectService); + if (projectService) { + await projectService.openProject(projectPath); + setStatus(t('header.status.projectOpened')); + } + } + } catch (error) { + console.error('Failed to open project:', error); + setStatus(t('header.status.failed')); + } + }; + return (

{t('app.title')}

+ diff --git a/packages/editor-app/src/api/tauri.ts b/packages/editor-app/src/api/tauri.ts index bf117977..56d363f4 100644 --- a/packages/editor-app/src/api/tauri.ts +++ b/packages/editor-app/src/api/tauri.ts @@ -11,9 +11,10 @@ export class TauriAPI { return await invoke('greet', { name }); } - /** - * 打开项目 - */ + static async openProjectDialog(): Promise { + return await invoke('open_project_dialog'); + } + static async openProject(path: string): Promise { return await invoke('open_project', { path }); } diff --git a/packages/editor-app/src/locales/en.ts b/packages/editor-app/src/locales/en.ts index 2190b1cc..6d20df46 100644 --- a/packages/editor-app/src/locales/en.ts +++ b/packages/editor-app/src/locales/en.ts @@ -6,13 +6,15 @@ export const en: Translations = { }, header: { toolbar: { + openProject: 'Open Project', createEntity: 'Create Entity', deleteEntity: 'Delete Entity' }, status: { initializing: 'Initializing...', ready: 'Editor Ready', - failed: 'Initialization Failed' + failed: 'Initialization Failed', + projectOpened: 'Project Opened' } }, hierarchy: { diff --git a/packages/editor-app/src/locales/zh.ts b/packages/editor-app/src/locales/zh.ts index dcbaa462..2ac86ac6 100644 --- a/packages/editor-app/src/locales/zh.ts +++ b/packages/editor-app/src/locales/zh.ts @@ -6,13 +6,15 @@ export const zh: Translations = { }, header: { toolbar: { + openProject: '打开项目', createEntity: '创建实体', deleteEntity: '删除实体' }, status: { initializing: '初始化中...', ready: '编辑器就绪', - failed: '初始化失败' + failed: '初始化失败', + projectOpened: '项目已打开' } }, hierarchy: { diff --git a/packages/editor-core/src/Services/ProjectService.ts b/packages/editor-core/src/Services/ProjectService.ts new file mode 100644 index 00000000..a513941f --- /dev/null +++ b/packages/editor-core/src/Services/ProjectService.ts @@ -0,0 +1,125 @@ +import type { IService } from '@esengine/ecs-framework'; +import { Injectable } from '@esengine/ecs-framework'; +import { createLogger } from '@esengine/ecs-framework'; +import { MessageHub } from './MessageHub'; + +const logger = createLogger('ProjectService'); + +export type ProjectType = 'cocos' | 'laya' | 'unknown'; + +export interface ProjectInfo { + path: string; + type: ProjectType; + name: string; + configPath?: string; +} + +export interface ProjectConfig { + projectType?: ProjectType; + componentsPath?: string; + componentPattern?: string; + buildOutput?: string; +} + +@Injectable() +export class ProjectService implements IService { + private currentProject: ProjectInfo | null = null; + private projectConfig: ProjectConfig | null = null; + private messageHub: MessageHub; + + constructor(messageHub: MessageHub) { + this.messageHub = messageHub; + } + + public async openProject(projectPath: string): Promise { + try { + const projectInfo = await this.validateProject(projectPath); + + this.currentProject = projectInfo; + + if (projectInfo.configPath) { + this.projectConfig = await this.loadConfig(projectInfo.configPath); + } + + await this.messageHub.publish('project:opened', { + path: projectPath, + type: projectInfo.type, + name: projectInfo.name + }); + + logger.info('Project opened', { path: projectPath, type: projectInfo.type }); + } catch (error) { + logger.error('Failed to open project', error); + throw error; + } + } + + public async closeProject(): Promise { + if (!this.currentProject) { + logger.warn('No project is currently open'); + return; + } + + const projectPath = this.currentProject.path; + this.currentProject = null; + this.projectConfig = null; + + await this.messageHub.publish('project:closed', { path: projectPath }); + logger.info('Project closed', { path: projectPath }); + } + + public getCurrentProject(): ProjectInfo | null { + return this.currentProject; + } + + public getProjectConfig(): ProjectConfig | null { + return this.projectConfig; + } + + public isProjectOpen(): boolean { + return this.currentProject !== null; + } + + public getComponentsPath(): string | null { + if (!this.currentProject || !this.projectConfig?.componentsPath) { + return null; + } + return `${this.currentProject.path}/${this.projectConfig.componentsPath}`; + } + + private async validateProject(projectPath: string): Promise { + const projectName = projectPath.split(/[\\/]/).pop() || 'Unknown Project'; + + const projectInfo: ProjectInfo = { + path: projectPath, + type: 'unknown', + name: projectName + }; + + const configPath = `${projectPath}/ecs-editor.config.json`; + + try { + projectInfo.configPath = configPath; + projectInfo.type = 'cocos'; + } catch (error) { + logger.warn('No ecs-editor.config.json found, using defaults'); + } + + return projectInfo; + } + + private async loadConfig(configPath: string): Promise { + return { + projectType: 'cocos', + componentsPath: 'assets/scripts/components', + componentPattern: '**/*Component.ts', + buildOutput: 'temp/editor-components' + }; + } + + public dispose(): void { + this.currentProject = null; + this.projectConfig = null; + logger.info('ProjectService disposed'); + } +} diff --git a/packages/editor-core/src/index.ts b/packages/editor-core/src/index.ts index 12bac4a8..b459be8f 100644 --- a/packages/editor-core/src/index.ts +++ b/packages/editor-core/src/index.ts @@ -14,5 +14,6 @@ export * from './Services/EntityStoreService'; export * from './Services/ComponentRegistry'; export * from './Services/LocaleService'; export * from './Services/PropertyMetadata'; +export * from './Services/ProjectService'; export * from './Types/UITypes';