From 01085a3ba58209678a4fc8c801b009c44df9c818 Mon Sep 17 00:00:00 2001 From: Miao_Spring <539943419@qq.com> Date: Fri, 21 Mar 2025 11:55:33 +0800 Subject: [PATCH] update --- .gitignore | 6 +- README.md | 120 +--- build.auto.release.sh | 6 +- src-server/api/getHandler.go | 3 + src/class/VirtualRemoteDirectory.ts | 172 ++++-- src/plugins/browser_llm/browser_llm.vue | 89 +-- src/plugins/browser_llm/export.ts | 2 +- src/plugins/onnx_demo_emotion/export.ts | 51 ++ src/plugins/onnx_demo_emotion/onnx_demo.vue | 318 +++++++++++ src/plugins/onnx_demo_mnist/export.ts | 31 + .../onnx_demo_mnist/onnx_demo_mnist.vue | 536 ++++++++++++++++++ src/utils/buildFromTree.ts | 7 +- 12 files changed, 1133 insertions(+), 208 deletions(-) create mode 100644 src/plugins/onnx_demo_emotion/export.ts create mode 100644 src/plugins/onnx_demo_emotion/onnx_demo.vue create mode 100644 src/plugins/onnx_demo_mnist/export.ts create mode 100644 src/plugins/onnx_demo_mnist/onnx_demo_mnist.vue diff --git a/.gitignore b/.gitignore index 7ca7554..06f788a 100644 --- a/.gitignore +++ b/.gitignore @@ -35,4 +35,8 @@ auto-imports.d.ts components.d.ts -src/plugins/onnx_demo_* +# src/plugins/onnx_demo_* + +public/test + +doc \ No newline at end of file diff --git a/README.md b/README.md index c670e54..2890010 100644 --- a/README.md +++ b/README.md @@ -25,122 +25,4 @@ ## 碎碎念: -写的真潦草 - -## 让cursor生成了个架构图,可能不是很专业,不过我看了感觉没啥问题: -``` -┌────────────────────────────────────────────────────────────────────────────────────────────────────┐ -│ miaoDirectory前端架构 │ -└────────────────────────────────────────────────────────────────────────────────────────────────────┘ - ▲ - │ - │ HTTP API通信 - │ -┌────────────────────────────────────────────────────────────────────────────────────────────────────┐ -│ 后端服务 │ -└────────────────────────────────────────────────────────────────────────────────────────────────────┘ - - -┌────────────────────────────────────────────────────────────────────────────────────────────────────┐ -│ 应用入口 (App.vue) │ -│ ┌──────────────────────────┐ ┌────────────────────────────┐ ┌────────────────────────────────┐ │ -│ │ 全局状态初始化 │ │ 全局消息提供器 │ │ 全局导航菜单 │ │ -│ │ (初始化各个系统核心实例) │ │ (miaoAlertTipProvider) │ │ │ │ -│ └──────────────┬───────────┘ └─────────────┬──────────────┘ └────────────────────────────────┘ │ -│ │ │ │ -│ ▼ ▼ │ -│ ┌──────────────────────────┐ ┌────────────────────────────┐ │ -│ │ 视图控制器渲染 │ │ 视图容器渲染 │ │ -│ │ (ViewController) │ │ (ViewContainer) │ │ -│ └──────────────────────────┘ └────────────────────────────┘ │ -└────────────────────────────────────────────────────────────────────────────────────────────────────┘ - │ │ - │ │ - ▼ ▼ -┌────────────────────────────────────────────────────────────────────────────────────────────────────┐ -│ 多页面系统 │ -│ │ -│ ┌──────────────────────────────────────────┐ ┌───────────────────────────────────────────────┐ │ -│ │ VirtualPages类 │ │ VirtualPage类 │ │ -│ │ ┌────────────────────────────────────┐ │ │ ┌─────────────────────────────────────────┐ │ │ -│ │ │ _views │ │ │ │ component: 页面使用的组件 │ │ │ -│ │ │ (存储所有虚拟页面的响应式数组) │ │ │ │ name: 页面名 │ │ │ -│ │ └────────────────────────────────────┘ │ │ │ visible: 是否可见 │ │ │ -│ │ │ │ │ currentDirectories: 当前使用的目录 │ │ │ -│ │ ┌────────────────────────────────────┐ │ │ │ currentFiles: 当前使用的文件 │ │ │ -│ │ │ getIndex(): 获取页面索引 │ │ │ │ id: 唯一标识符 │ │ │ -│ │ │ getView(): 根据ID获取页面 │ │ │ │ color: 页面颜色 │ │ │ -│ │ │ push(): 添加新页面 │◄──┼────┼─┤ title: 页面标题 │ │ │ -│ │ │ deleteView(): 删除页面 │ │ │ │ exitConfirm: 关闭时是否需要确认 │ │ │ -│ │ │ find(): 查找页面 │ │ │ │ allowCopy: 是否允许复制页面 │ │ │ -│ │ └────────────────────────────────────┘ │ │ └─────────────────────────────────────────┘ │ │ -│ └──────────────────────────────────────────┘ │ │ │ -│ ▲ │ ┌─────────────────────────────────────────┐ │ │ -│ │ │ │ setTitle(): 设置标题 │ │ │ -│ │ │ │ switchShow(): 切换显示状态 │ │ │ -│ │ │ │ refresh(): 刷新页面 │ │ │ -│ │ │ └─────────────────────────────────────────┘ │ │ -│ │ └───────────────────────────────────────────────┘ │ -│ │ ▲ │ -│ │ │ │ -│ useVirtualPages() │ │ -│ (全局单例访问钩子) │ │ -│ │ │ -└────────────────────────────────────────────────────────────────────────────────────────────────────┘ - │ │ - │ │ - ▼ │ -┌────────────────────────────────────────────────────────────────────────────────────────────────────┐ -│ 插件系统 │ -│ │ -│ ┌──────────────────────────────────────────┐ ┌───────────────────────────────────────────────┐ │ -│ │ PluginCenter类 │ │ 插件接口定义 │ │ -│ │ ┌────────────────────────────────────┐ │ │ ┌─────────────────────────────────────────┐ │ │ -│ │ │ pluginsMap │ │ │ │ name: 插件名称 │ │ │ -│ │ │ (存储已注册插件的映射表) │ │ │ │ icon: 插件图标 │ │ │ -│ │ └────────────────────────────────────┘ │ │ │ disable: 是否禁用 │ │ │ -│ │ │ │ │ group: 所属插件组 │ │ │ -│ │ ┌────────────────────────────────────┐ │ │ │ priority: 优先级 │ │ │ -│ │ │ register(): 注册插件 │ │ │ │ filter(): 适用性过滤函数 │ │ │ -│ │ │ deregister(): 注销插件 │ │ │ │ func(): 插件主函数 │ │ │ -│ │ │ registerComponent(): 注册组件插件 │ │ │ └─────────────────────────────────────────┘ │ │ -│ │ │ getUsablePlugin(): 获取可用插件 │───┼────┼─► 创建虚拟页面 │ │ -│ │ │ usePlugin(): 使用插件 │ │ │ │ │ -│ │ └────────────────────────────────────┘ │ │ ┌─────────────────────────────────────────┐ │ │ -│ └──────────────────────────────────────────┘ │ │ 插件分组(PluginGroup) │ │ │ -│ ▲ │ │ │ │ │ -│ │ │ │ default: 默认组 │ │ │ -│ │ │ │ mainMenu: 主菜单组 │ │ │ -│ │ │ │ immediately: 立即执行组 │ │ │ -│ │ │ └─────────────────────────────────────────┘ │ │ -│ │ └───────────────────────────────────────────────┘ │ -│ │ │ -│ usePluginCenter() │ -│ (全局单例访问钩子) │ -│ │ -└────────────────────────────────────────────────────────────────────────────────────────────────────┘ - │ - │ - ▼ -┌────────────────────────────────────────────────────────────────────────────────────────────────────┐ -│ 虚拟文件系统 │ -│ │ -│ ┌──────────────────────────────────────────┐ ┌───────────────────────────────────────────────┐ │ -│ │ VirtualDirectory类 │ │ VirtualFile类 │ │ -│ │ ┌────────────────────────────────────┐ │ │ ┌─────────────────────────────────────────┐ │ │ -│ │ │ name: 目录名 │ │ │ │ name: 文件名 │ │ │ -│ │ │ path: 完整路径 │ │ │ │ size: 文件大小 │ │ │ -│ │ │ stats: 文件状态信息 │ │ │ │ stats: 文件状态信息 │ │ │ -│ │ │ files: 文件列表 │◄──┼────┼─► parent: 父目录 │ │ │ -│ │ │ directories: 子目录列表 │ │ │ │ id: 唯一标识符 │ │ │ -│ │ │ parent?: 父目录 │ │ │ │ path: 完整路径(计算属性) │ │ │ -│ │ └────────────────────────────────────┘ │ │ └─────────────────────────────────────────┘ │ │ -│ └──────────────────────────────────────────┘ └───────────────────────────────────────────────┘ │ -│ ▲ │ -│ │ │ -│ │ │ -│ useRootVDirectory() │ -│ (获取根目录实例钩子) │ -│ │ -└────────────────────────────────────────────────────────────────────────────────────────────────────┘ -``` \ No newline at end of file +写的真潦草 \ No newline at end of file diff --git a/build.auto.release.sh b/build.auto.release.sh index c631937..ca2e7ba 100644 --- a/build.auto.release.sh +++ b/build.auto.release.sh @@ -11,14 +11,14 @@ cd ./src-server echo "building amd64 win" go env -w GOOS=windows GOARCH=amd64 -go build -o ../miao-directory-amd64-win.exe +go build -ldflags="-s -w" -o ../miao-directory-amd64-win.exe echo "building amd64 linux" go env -w GOOS=linux GOARCH=amd64 -go build -o ../miao-directory-amd64-linux +go build -ldflags="-s -w" -o ../miao-directory-amd64-linux # go env -w GOOS=linux GOARCH=arm64 -# go build -o ../miao-directory-arm64-linux +# go build -ldflags="-s -w" -o ../miao-directory-arm64-linux echo "build finish" \ No newline at end of file diff --git a/src-server/api/getHandler.go b/src-server/api/getHandler.go index 1c5c7ce..8634de6 100644 --- a/src-server/api/getHandler.go +++ b/src-server/api/getHandler.go @@ -72,6 +72,7 @@ func GetHandler(c *gin.Context) { switch ext { case ".js": case ".cjs": + case ".mjs": c.Writer.Header().Set("Content-Type", "application/javascript") case ".css": c.Writer.Header().Set("Content-Type", "text/css") @@ -79,6 +80,8 @@ func GetHandler(c *gin.Context) { c.Writer.Header().Set("Content-Type", "text/html") case ".json": c.Writer.Header().Set("Content-Type", "application/json") + case ".wasm": + c.Writer.Header().Set("Content-Type", "application/wasm") default: c.Writer.Header().Set("Content-Type", "application/octet-stream") } diff --git a/src/class/VirtualRemoteDirectory.ts b/src/class/VirtualRemoteDirectory.ts index 61121b8..8030819 100644 --- a/src/class/VirtualRemoteDirectory.ts +++ b/src/class/VirtualRemoteDirectory.ts @@ -5,6 +5,31 @@ import config from '@/config' const miaoFetchApi = useMiaoFetchApi() +/** + * 轮询事件直到完成 + * @param eventId 事件ID + * @param maxRetries 最大重试次数 + * @param interval 轮询间隔(毫秒) + * @returns 事件结果 + */ +async function pollEventUntilComplete(eventId: number, maxRetries = 100, interval = 300) { + let _count = 0; + while (_count < maxRetries) { + try { + const { response } = miaoFetchApi.query(eventId); + const result = await response; + if (result.status === 'success' || result.status === 'failed') { + return result; + } + _count++; + await new Promise(_ => setTimeout(_, interval)); + } catch (error) { + throw error; + } + } + throw new Error('poll timeout'); +} + export class VirtualRemoteFile extends VirtualFileBase { constructor(info: file, parent: VirtualDirectoryBase) { super(info.name, info.size, info.stats, parent) @@ -20,8 +45,13 @@ export class VirtualRemoteFile extends VirtualFileBase { retry: 5 }); const id = (await response).eventId; - const { response: res } = miaoFetchApi.query(id); - const eventResult = await res; + + const eventResult = await pollEventUntilComplete(id); + + if (eventResult.status === 'success' && this.parent) { + await this.parent.update(); // 操作成功后更新父目录 + } + return eventResult.status === 'success'; } catch (error) { console.error('删除文件失败:', error); @@ -39,8 +69,13 @@ export class VirtualRemoteFile extends VirtualFileBase { retry: 5 }); const id = (await response).eventId; - const { response: res } = miaoFetchApi.query(id); - const eventResult = await res; + + const eventResult = await pollEventUntilComplete(id); + + if (eventResult.status === 'success' && this.parent) { + await this.parent.update(); // 操作成功后更新父目录 + } + return eventResult.status === 'success'; } catch (error) { console.error('重命名文件失败:', error); @@ -57,12 +92,13 @@ export class VirtualRemoteFile extends VirtualFileBase { const _from = this.parent; const { response } = miaoFetchApi.cut(this, targetDir); const id = (await response).eventId; - const { response: res } = miaoFetchApi.query(id); - const eventResult = await res; + + const eventResult = await pollEventUntilComplete(id); const success = eventResult.status === 'success'; if (success) { - _from.update(); + if (_from) await _from.update(); // 更新源目录 + await targetDir.update(); // 更新目标目录 } return success; @@ -79,40 +115,87 @@ export class VirtualRemoteDirectory extends VirtualDirectoryBase { } get url(): string { - return `${config.api.get}${this.path}/` + return `${config.api.get}${this.path}` } updateContent(content: (file | directory)[]) { this._isUpdated = true - // 创建名称到内容项的映射,O(n) - const contentMap = new Map(); - content.forEach(item => contentMap.set(item.name, item)); + const files = content.filter(item => item.type === 'file') as file[]; + const directories = content.filter(item => item.type === 'directory') as directory[]; - // 使用Set存储所有内容项的名称,用于O(1)查找,O(n) - const itemNamesSet = new Set(content.map(v => v.name)); + this.diffFiles(files); + this.diffDirectories(directories); + } - // 删除不再存在的文件和目录,O(m) - this.files = this.files.filter(file => itemNamesSet.has(file.name)); - this.directories = this.directories.filter(dir => itemNamesSet.has(dir.name)); + /** + * 文件diff算法,类似Vue的diff + * @param newFiles 新文件列表 + */ + private diffFiles(newFiles: file[]) { + if (this.files.length === 0) { + this.files = newFiles.map(file => new VirtualRemoteFile(file, this)); + return; + } - // 创建现有文件和目录的名称集合,O(m) - const existingFileNames = new Set(this.files.map(file => file.name)); - const existingDirNames = new Set(this.directories.map(dir => dir.name)); - - // 添加新文件和目录,O(n) - content.forEach(item => { - // 如果是文件且不存在于现有文件中 - if (item.type === 'file' && !existingFileNames.has(item.name)) { - const nvf = new VirtualRemoteFile(item as file, this); - this.files.push(nvf); + const oldFiles = [...this.files]; + const newFilesList: VirtualRemoteFile[] = []; + + const oldKeyMap = new Map(); + oldFiles.forEach(file => oldKeyMap.set(file.name, file as VirtualRemoteFile)); + + for (let i = 0; i < newFiles.length; i++) { + const newFile = newFiles[i]; + const key = newFile.name; + + const oldFile = oldKeyMap.get(key); + + if (oldFile) { + oldFile.size = newFile.size; + oldFile.stats = newFile.stats; + newFilesList.push(oldFile); + // 从旧映射中删除,表示已处理 + oldKeyMap.delete(key); + } else { + newFilesList.push(new VirtualRemoteFile(newFile, this)); } - // 如果是目录且不存在于现有目录中 - else if (item.type === 'directory' && !existingDirNames.has(item.name)) { - const nvd = new VirtualRemoteDirectory(item as directory, this); - this.directories.push(nvd); + } + + this.files = newFilesList; + } + + /** + * 目录diff算法,类似Vue的diff + * @param newDirs 新目录列表 + */ + private diffDirectories(newDirs: directory[]) { + if (this.directories.length === 0) { + this.directories = newDirs.map(dir => new VirtualRemoteDirectory(dir, this)); + return; + } + + const oldDirs = [...this.directories]; + const newDirsList: VirtualRemoteDirectory[] = []; + + const oldKeyMap = new Map(); + oldDirs.forEach(dir => oldKeyMap.set(dir.name, dir as VirtualRemoteDirectory)); + + for (let i = 0; i < newDirs.length; i++) { + const newDir = newDirs[i]; + const key = newDir.name; + + const oldDir = oldKeyMap.get(key); + + if (oldDir) { + oldDir.stats = newDir.stats; + newDirsList.push(oldDir); + oldKeyMap.delete(key); + } else { + newDirsList.push(new VirtualRemoteDirectory(newDir, this)); } - }); + } + + this.directories = newDirsList; } async update(): Promise { @@ -126,8 +209,13 @@ export class VirtualRemoteDirectory extends VirtualDirectoryBase { retry: 5 }); const id = (await response).eventId; - const { response: res } = miaoFetchApi.query(id); - const eventResult = await res; + + const eventResult = await pollEventUntilComplete(id); + + if (eventResult.status === 'success' && this.parent) { + await this.parent.update(); // 操作成功后更新父目录 + } + return eventResult.status === 'success'; } catch (error) { console.error('删除目录失败:', error); @@ -145,8 +233,13 @@ export class VirtualRemoteDirectory extends VirtualDirectoryBase { retry: 5 }); const id = (await response).eventId; - const { response: res } = miaoFetchApi.query(id); - const eventResult = await res; + + const eventResult = await pollEventUntilComplete(id); + + if (eventResult.status === 'success' && this.parent) { + await this.parent.update(); // 操作成功后更新父目录 + } + return eventResult.status === 'success'; } catch (error) { console.error('重命名目录失败:', error); @@ -168,12 +261,13 @@ export class VirtualRemoteDirectory extends VirtualDirectoryBase { const _from = this.parent; const { response } = miaoFetchApi.cut(this, targetDir); const id = (await response).eventId; - const { response: res } = miaoFetchApi.query(id); - const eventResult = await res; + + const eventResult = await pollEventUntilComplete(id); const success = eventResult.status === 'success'; - if (success && _from) { - _from.update(); + if (success) { + if (_from) await _from.update(); // 更新源目录 + await targetDir.update(); // 更新目标目录 } return success; diff --git a/src/plugins/browser_llm/browser_llm.vue b/src/plugins/browser_llm/browser_llm.vue index 8573671..cfcffc5 100644 --- a/src/plugins/browser_llm/browser_llm.vue +++ b/src/plugins/browser_llm/browser_llm.vue @@ -10,16 +10,16 @@
-
-
{{ message.content }}
-
-
- -
-
-
...
-
+
+
{{ message.content }}
+
+
+ +
+
+
...
+