diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..5a5809d
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,9 @@
+[*.{js,jsx,mjs,cjs,ts,tsx,mts,cts,vue,css,scss,sass,less,styl}]
+charset = utf-8
+indent_size = 2
+indent_style = space
+insert_final_newline = true
+trim_trailing_whitespace = true
+
+end_of_line = lf
+max_line_length = 100
diff --git a/.env b/.env
new file mode 100644
index 0000000..6a61e22
--- /dev/null
+++ b/.env
@@ -0,0 +1,13 @@
+VITE_SOCKET_URL=http://172.18.32.98:8888/
+VITE_URL=http://172.18.32.98:8888/
+VITE_PREVIEW=false
+VITE_BASE_API=/api
+VITE_OPENAI_API=/openai
+VITE_VERSION='0.1.16'
+
+PASTED_TEXT_CHARACTER_LIMIT = 1000;
+
+WEBUI_VERSION = APP_VERSION;
+WEBUI_BUILD_HASH = APP_BUILD_HASH;
+REQUIRED_OLLAMA_VERSION = '0.1.16';
+
diff --git a/.env.development b/.env.development
new file mode 100644
index 0000000..4825456
--- /dev/null
+++ b/.env.development
@@ -0,0 +1,13 @@
+VITE_SOCKET_URL=http://localhost:5173
+VITE_URL=http://172.18.32.98:8888/
+VITE_PREVIEW=false
+VITE_BASE_API=/api
+VITE_OPENAI_API=/openai
+VITE_VERSION='0.1.16'
+
+PASTED_TEXT_CHARACTER_LIMIT = 1000;
+
+WEBUI_VERSION = APP_VERSION;
+WEBUI_BUILD_HASH = APP_BUILD_HASH;
+REQUIRED_OLLAMA_VERSION = '0.1.16';
+
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..6313b56
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1 @@
+* text=auto eol=lf
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..8ee54e8
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,30 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+lerna-debug.log*
+
+node_modules
+.DS_Store
+dist
+dist-ssr
+coverage
+*.local
+
+/cypress/videos/
+/cypress/screenshots/
+
+# Editor directories and files
+.vscode/*
+!.vscode/extensions.json
+.idea
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?
+
+*.tsbuildinfo
diff --git a/.vscode/extensions.json b/.vscode/extensions.json
new file mode 100644
index 0000000..a06a8c6
--- /dev/null
+++ b/.vscode/extensions.json
@@ -0,0 +1,9 @@
+{
+ "recommendations": [
+ "Vue.volar",
+ "vitest.explorer",
+ "dbaeumer.vscode-eslint",
+ "EditorConfig.EditorConfig",
+ "esbenp.prettier-vscode"
+ ]
+}
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..9e5fc8f
--- /dev/null
+++ b/index.html
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+ Vite App
+
+
+
+
+
+
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..e6e9278
--- /dev/null
+++ b/package.json
@@ -0,0 +1,60 @@
+{
+ "name": "radar-system",
+ "version": "0.0.0",
+ "private": true,
+ "type": "module",
+ "scripts": {
+ "dev": "vite --open ",
+ "build": "vite build",
+ "preview": "vite preview",
+ "test:unit": "vitest",
+ "build-only": "vite build",
+ "lint": "eslint . --fix",
+ "format": "prettier --write src/"
+ },
+ "dependencies": {
+ "socket.io-client": "^4.2.0",
+ "ant-design-vue": "4.x",
+ "echarts": "^5.6.0",
+ "lodash": "^4.17.21",
+ "pinia": "^3.0.1",
+ "vue": "^3.5.13",
+ "vue-router": "^4.5.0",
+ "screenfull": "^5.1.0"
+ },
+ "devDependencies": {
+ "@ant-design/icons-vue": "^7.0.1",
+ "@tsconfig/node22": "^22.0.1",
+ "@types/jsdom": "^21.1.7",
+ "@types/node": "^22.14.0",
+ "@vitejs/plugin-vue": "^5.2.3",
+ "@vitejs/plugin-vue-jsx": "^4.1.2",
+ "@vitest/eslint-plugin": "^1.1.39",
+ "@vue/eslint-config-prettier": "^10.2.0",
+ "@vue/eslint-config-typescript": "^14.5.0",
+ "@vue/test-utils": "^2.4.6",
+ "@vue/tsconfig": "^0.7.0",
+ "autoprefixer": "^10.4.19",
+ "dayjs": "^1.11.13",
+ "eslint": "^9.22.0",
+ "eslint-plugin-vue": "~10.0.0",
+ "jiti": "^2.4.2",
+ "jsdom": "^26.0.0",
+ "npm-run-all2": "^7.0.2",
+ "postcss-normalize-charset": "^6.0.1",
+ "postcss-preset-env": "^9.3.0",
+ "prettier": "3.5.3",
+ "sass": "^1.66.1",
+ "sass-loader": "^13.3.2",
+ "typescript": "~5.8.0",
+ "vite": "^6.2.4",
+ "vite-plugin-vue-devtools": "^7.7.2",
+ "vitest": "^3.1.1",
+ "vue-tsc": "^2.2.8"
+ },
+ "packageManager": "pnpm@6.11.0",
+ "engines": {
+ "node": ">=18.18.2 <= 22.10.0",
+ "pnpm": ">=6.11.0"
+ }
+}
diff --git a/public/css/tinymceEdit--wirte.css b/public/css/tinymceEdit--wirte.css
new file mode 100644
index 0000000..48aec65
--- /dev/null
+++ b/public/css/tinymceEdit--wirte.css
@@ -0,0 +1,18 @@
+@font-face {
+ font-family: '黑体';
+ src: url('/static/font/MyFont.ttf');
+}
+
+@font-face {
+ font-family: 'Times New Roman';
+ src: url('/static/font/Times-New-Romance-1.ttf');
+}
+
+@font-face {
+ font-family: '宋体';
+ src: url('/static/font/ZiTiGuanJiaFangSongTi-2.ttf');
+}
+
+* {
+ line-height: 1.5;
+}
diff --git a/public/favicon.ico b/public/favicon.ico
new file mode 100644
index 0000000..46c2933
Binary files /dev/null and b/public/favicon.ico differ
diff --git a/src/App.vue b/src/App.vue
new file mode 100644
index 0000000..986629a
--- /dev/null
+++ b/src/App.vue
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/apis/auth/index.ts b/src/apis/auth/index.ts
new file mode 100644
index 0000000..8945976
--- /dev/null
+++ b/src/apis/auth/index.ts
@@ -0,0 +1,10 @@
+import { request } from '@/utils/request/index.ts';
+
+// 中断任务
+// export const interruptApi = async (taskId: string) => {
+// return await request({
+// base: '/api',
+// url: `/tasks/stop/${taskId}`,
+// method: 'POST',
+// })
+// };
diff --git a/src/apis/dataManage/dataExport.ts b/src/apis/dataManage/dataExport.ts
new file mode 100644
index 0000000..1b93477
--- /dev/null
+++ b/src/apis/dataManage/dataExport.ts
@@ -0,0 +1,43 @@
+// 数据导出相关
+import { request } from '@/utils/request/index.ts';
+// 列表接口
+export const listApi = async (data?: Record) => {
+ return await request({
+ url: `/data/list_info`,
+ method: 'GET',
+ params: data
+ })
+};
+
+// 更新标注
+export const updateLabelApi = async (data: Record) => {
+ return await request({
+ url: `/data/save_label`,
+ method: 'POST',
+ data: data
+ })
+}
+
+// 获取导出数据文件在线地址
+export const getExportUrlApi = async (data: Record) => {
+ return await request({
+ url: `/data/export_data`,
+ method: 'GET',
+ params: data,
+ getRaw: true
+ })
+}
+
+// 导入标注
+export const importLabelByFileApi = async (data: Record) => {
+return await request({
+ url: `/data/batch_label`,
+ method: 'POST',
+ headers: {
+ Accept: 'application/json',
+ authorization: `Bearer ${localStorage.token}`
+ },
+ data,
+ dataRaw: true,
+ })
+}
diff --git a/src/apis/dataManage/dataImport.ts b/src/apis/dataManage/dataImport.ts
new file mode 100644
index 0000000..8dec990
--- /dev/null
+++ b/src/apis/dataManage/dataImport.ts
@@ -0,0 +1,43 @@
+// 数据导入相关
+
+import { request } from '@/utils/request/index.ts';
+// 列表接口
+export const listApi = async () => {
+ return await request({
+ url: `/data/list_data`,
+ method: 'GET',
+ })
+};
+
+export const deleteApi = async (data: Record) => {
+ return await request({
+ url: `/data/delete/${data.fileName}`,
+ method: 'DELETE',
+ })
+}
+
+export const importApi = async (data: Record) => {
+ console.log('importApi', data)
+ return await request({
+ url: `/data/import`,
+ method: 'POST',
+ headers: {
+ Accept: 'application/json',
+ authorization: `Bearer ${localStorage.token}`
+ },
+ data,
+ dataRaw: true,
+ })
+
+}
+
+
+
+ // 更新
+ export const updateApi = async (data: Record) => {
+ return await request({
+ url: `/data/data`,
+ method: 'POST',
+ data: data
+ })
+ }
diff --git a/src/apis/dataManage/statistics.ts b/src/apis/dataManage/statistics.ts
new file mode 100644
index 0000000..a490521
--- /dev/null
+++ b/src/apis/dataManage/statistics.ts
@@ -0,0 +1,22 @@
+// 统计相关
+
+import { request } from '@/utils/request/index.ts';
+
+// 统计数据
+export const statisticsApi = async () => {
+ return await request({
+ url: `/data/statistics`,
+ method: 'GET',
+ })
+};
+
+// 根据bacthid获取统计数据
+export const lineStatisticsApi = async (bacthId: string, controller?: AbortController) => {
+ return await request({
+ url: `/data/query_plot_data/${bacthId}`,
+ method: 'GET',
+ controller: controller
+ })
+}
+
+
diff --git a/src/apis/dataset/index.ts b/src/apis/dataset/index.ts
new file mode 100644
index 0000000..a816dc2
--- /dev/null
+++ b/src/apis/dataset/index.ts
@@ -0,0 +1,52 @@
+// 数据集相关
+import { request } from '@/utils/request/index.ts';
+
+// ?数据集
+export const reasonDatasetListApi = async () => {
+ return request({
+ url: '/performance/datasets',
+ method: 'GET',
+ })
+}
+
+// 测试集
+// export const testDatasetListApi = async () => {
+// return request({
+// url: '/config/test_datasets',
+// method: 'GET',
+// })
+// }
+
+// // 训练集
+// export const trainDatasetListApi = async () => {
+// return request({
+// url: '/config/train_datasets',
+// method: 'GET',
+// })
+// }
+
+// 数据集
+export const datasetListApi = async (params: Record) => {
+ return request({
+ url: '/data/exported_data',
+ method: 'GET',
+ params: params
+ })
+}
+
+// 更新数据集
+export const updateDatasetApi = async (params: Record) => {
+ return request({
+ url: '/data/exported_data',
+ method: 'PUT',
+ data: params
+ })
+}
+
+// 删除数据集
+export const deleteDatasetApi = async (id: number | string) => {
+return request({
+ url: `/data/exported_data/${id}`,
+ method: 'DELETE',
+ })
+}
diff --git a/src/apis/model/index.ts b/src/apis/model/index.ts
new file mode 100644
index 0000000..99199f7
--- /dev/null
+++ b/src/apis/model/index.ts
@@ -0,0 +1,45 @@
+import { request } from '@/utils/request/index.ts';
+
+
+// 新增
+export const addApi = async (data: Record, controller?: AbortController) => {
+ return await request({
+ url: `/train/model/import`,
+ method: 'POST',
+ headers: {
+ Accept: 'application/json',
+ authorization: `Bearer ${localStorage.token}`
+ },
+ data: data,
+ dataRaw: true,
+ })
+};
+
+// 修改
+export const updateApi = async (params: string, controller?: AbortController) => {
+ return await request({
+ url: `/train/model`,
+ method: 'PUT',
+ data: params
+ })
+};
+
+// 删除
+export const deleteApi = async (modelId: string) => {
+ return await request({
+ url: `/train/model/${modelId}`,
+ method: 'DELETE',
+ })
+}
+
+// 列表
+export const listApi = async (params?: Record) => {
+ return await request({
+ url: `/train/model/list`,
+ method: 'GET',
+ params: params
+ })
+}
+
+
+
diff --git a/src/apis/model/reasoning.ts b/src/apis/model/reasoning.ts
new file mode 100644
index 0000000..e69de29
diff --git a/src/apis/model/train.ts b/src/apis/model/train.ts
new file mode 100644
index 0000000..e69de29
diff --git a/src/apis/system/config.ts b/src/apis/system/config.ts
new file mode 100644
index 0000000..5b15288
--- /dev/null
+++ b/src/apis/system/config.ts
@@ -0,0 +1,25 @@
+// 配置相关
+import { request } from '@/utils/request/index.ts';
+
+// 获取模型配置
+export const modelConfigApi = async () => {
+ return await request({
+ url: `/config/models`,
+ method: 'GET',
+ })
+}
+
+// 获取数据集配置
+export const testDatasetConfigApi = async () => {
+ return await request({
+ url: `/config/test_datasets`,
+ method: 'GET',
+ })
+}
+
+export const getPlatformByModuleNameApi = (moduleName: string) => {
+ return request({
+ url: `/config/model/${moduleName}/platform`,
+ method: 'GET',
+ })
+}
diff --git a/src/apis/system/index.ts b/src/apis/system/index.ts
new file mode 100644
index 0000000..6af1da5
--- /dev/null
+++ b/src/apis/system/index.ts
@@ -0,0 +1,34 @@
+// 系统相关
+import { request } from '@/utils/request/index.ts';
+
+// 获取cpu使用情况
+export const cpuUsageApi = async () => {
+ return await request({
+ url: `/system/cpu/usage`,
+ method: 'GET',
+ })
+}
+
+// 获取gpu使用情况
+export const gpuUsageApi = async () => {
+ return await request({
+ url: `/system/gpu/usage`,
+ method: 'GET',
+ })
+}
+
+// 获取gpu信息
+export const gpuInfoApi = async () => {
+ return await request({
+ url: `/system/gpu/info`,
+ method: 'GET',
+ })
+}
+
+// 获取内存使用情况
+export const memoryInfoApi = async () => {
+ return await request({
+ url: `/system/memory/usage/formatted`,
+ method: 'GET',
+ })
+}
diff --git a/src/apis/system/model.ts b/src/apis/system/model.ts
new file mode 100644
index 0000000..2aca0c6
--- /dev/null
+++ b/src/apis/system/model.ts
@@ -0,0 +1 @@
+// 模型相关
diff --git a/src/apis/train/index.ts b/src/apis/train/index.ts
new file mode 100644
index 0000000..d86fd7d
--- /dev/null
+++ b/src/apis/train/index.ts
@@ -0,0 +1,67 @@
+// 训练相关
+import { request } from '@/utils/request'
+
+// 获取列表
+export const listApi = async () => {
+ return await request({
+ url: `/train/tasks`,
+ method: 'GET'
+ })
+}
+
+export const deleteApi = async (taskId: string) => {
+ return await request({
+ url: `/train/${taskId}`,
+ method: 'DELETE'
+ })
+}
+
+// 获取任务参数
+export const taskParamsApi = async (taskId: string) => {
+ return await request({
+ url: `/train/${taskId}/params`,
+ method: 'GET'
+ })
+}
+
+// 获取训练详情
+export const statusApi = async (taskId: string) => {
+ return await request({
+ url: `/train/${taskId}/status`,
+ method: 'GET'
+ })
+}
+
+// 获取训练日志
+export const logsApi = async (taskId: string) => {
+ return request({
+ url: `/train/${taskId}/logs/stream`,
+ method: 'GET',
+ getRaw: true
+ })
+}
+
+// 创建任务
+export const createTaskApi = async (data: Record) => {
+ return await request({
+ url: '/train/create',
+ method: 'POST',
+ data: data
+ })
+}
+
+// 中断
+export const onInterruptApi = async (taskId: string) => {
+ return await request({
+ url: `/train/${taskId}/stop`,
+ method: 'POST'
+ })
+}
+
+// 模型导出
+export const modelExportApi = async (taskId: string) => {
+ return await request({
+ url: `/train/${taskId}/export`,
+ method: 'GET'
+ })
+}
diff --git a/src/apis/train/inference.ts b/src/apis/train/inference.ts
new file mode 100644
index 0000000..0b8a72f
--- /dev/null
+++ b/src/apis/train/inference.ts
@@ -0,0 +1,84 @@
+// 推理任务相关
+import { request } from '@/utils/request'
+
+// 获取推理任务列表
+export const listApi = async () => {
+ return await request({
+ url: '/inference/tasks',
+ method: 'GET'
+ })
+}
+
+// 删除任务
+export const deleteApi = async (taskId: string) => {
+ return await request({
+ url: `/inference/tasks/${taskId}`,
+ method: 'DELETE',
+ })
+}
+
+// 获取推理任务结果
+export const taskResultApi = async (taskId: string, controller?: AbortController) => {
+ return await request({
+ url: `/inference/results/${taskId}`,
+ method: 'GET',
+ controller: controller,
+ })
+}
+
+// 获取推理任务状态
+export const taskStatusApi = async (taskId: string, controller?: AbortController) => {
+ return await request({
+ url: `/inference/status/${taskId}`,
+ method: 'GET',
+ controller: controller,
+ })
+}
+
+// 启动推理任务
+export const createTaskApi = async (data: Record, controller?: AbortController) => {
+ return await request({
+ url: '/inference/run',
+ method: 'POST',
+ data: data,
+ controller: controller,
+ })
+}
+
+
+// 获取推理任务模式
+export const optionsApi = async () => {
+ return await request({
+ url: '/inference/infer_types',
+ method: 'GET',
+ })
+}
+
+// 获取权重目录
+export const weightOptionsApi = async (params: Record) => {
+ return await request({
+ url: '/inference/directories',
+ method: 'GET',
+ params: params
+ })
+}
+
+// 获取推理任务日志
+export const logsApi = async (taskId: string, controller?: AbortController) => {
+ return await request({
+ url: `/inference/${taskId}/logs/stream`,
+ method: 'GET',
+ controller: controller,
+ getRaw: true
+ })
+}
+
+// 停止推理任务
+export const stopTaskApi = async (taskId: string, controller?: AbortController) => {
+ return await request({
+ url: `/inference/${taskId}/stop`,
+ method: 'POST',
+ controller: controller,
+ })
+}
+
diff --git a/src/apis/train/performance.ts b/src/apis/train/performance.ts
new file mode 100644
index 0000000..05532ef
--- /dev/null
+++ b/src/apis/train/performance.ts
@@ -0,0 +1,19 @@
+// 运行效果相关
+import { request } from '@/utils/request'
+
+// 获取在特定数据集上运行过的模型列表
+export const getModelByDatasetApi = async (datasetName: string) => {
+ return await request({
+ url: `/performance/datasets/${datasetName}/models`,
+ method: 'GET'
+ })
+}
+
+// 获取特定模型在特定数据集上的性能记录
+export const getPerformanceApi = async (data: Record) => {
+ return await request({
+ url: `/performance/datasets/${data.datasetName}/models/${data.modelName}/${data.checkpoint_dir}`,
+ method: 'GET'
+ })
+}
+
diff --git a/src/assets/files/dataExport/导入标注模板文件.csv b/src/assets/files/dataExport/导入标注模板文件.csv
new file mode 100644
index 0000000..8996a98
--- /dev/null
+++ b/src/assets/files/dataExport/导入标注模板文件.csv
@@ -0,0 +1,2 @@
+batch_id,label
+20250612_960_0,A330
diff --git a/src/assets/icons/back.svg b/src/assets/icons/back.svg
new file mode 100644
index 0000000..01871cb
--- /dev/null
+++ b/src/assets/icons/back.svg
@@ -0,0 +1,10 @@
+
diff --git a/src/assets/icons/chat.svg b/src/assets/icons/chat.svg
new file mode 100644
index 0000000..f29db5d
--- /dev/null
+++ b/src/assets/icons/chat.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/icons/close.svg b/src/assets/icons/close.svg
new file mode 100644
index 0000000..9525858
--- /dev/null
+++ b/src/assets/icons/close.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/assets/icons/correct.svg b/src/assets/icons/correct.svg
new file mode 100644
index 0000000..6295d00
--- /dev/null
+++ b/src/assets/icons/correct.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/icons/data.svg b/src/assets/icons/data.svg
new file mode 100644
index 0000000..0c3904b
--- /dev/null
+++ b/src/assets/icons/data.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/icons/delete.svg b/src/assets/icons/delete.svg
new file mode 100644
index 0000000..bec1ad7
--- /dev/null
+++ b/src/assets/icons/delete.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/assets/icons/edit.svg b/src/assets/icons/edit.svg
new file mode 100644
index 0000000..4563ba2
--- /dev/null
+++ b/src/assets/icons/edit.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/assets/icons/email.svg b/src/assets/icons/email.svg
new file mode 100644
index 0000000..4351ca4
--- /dev/null
+++ b/src/assets/icons/email.svg
@@ -0,0 +1,5 @@
+
diff --git a/src/assets/icons/export.svg b/src/assets/icons/export.svg
new file mode 100644
index 0000000..ccb2992
--- /dev/null
+++ b/src/assets/icons/export.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/icons/fileUpload.svg b/src/assets/icons/fileUpload.svg
new file mode 100644
index 0000000..def82c7
--- /dev/null
+++ b/src/assets/icons/fileUpload.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/icons/import.svg b/src/assets/icons/import.svg
new file mode 100644
index 0000000..7a95baf
--- /dev/null
+++ b/src/assets/icons/import.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/icons/interrupt.svg b/src/assets/icons/interrupt.svg
new file mode 100644
index 0000000..0c4fd14
--- /dev/null
+++ b/src/assets/icons/interrupt.svg
@@ -0,0 +1,11 @@
+
diff --git a/src/assets/icons/interruptLine.svg b/src/assets/icons/interruptLine.svg
new file mode 100644
index 0000000..50889ff
--- /dev/null
+++ b/src/assets/icons/interruptLine.svg
@@ -0,0 +1,6 @@
+
diff --git a/src/assets/icons/lock.svg b/src/assets/icons/lock.svg
new file mode 100644
index 0000000..e4f2778
--- /dev/null
+++ b/src/assets/icons/lock.svg
@@ -0,0 +1,5 @@
+
diff --git a/src/assets/icons/plus.svg b/src/assets/icons/plus.svg
new file mode 100644
index 0000000..67be93c
--- /dev/null
+++ b/src/assets/icons/plus.svg
@@ -0,0 +1,11 @@
+
diff --git a/src/assets/icons/plusFill.svg b/src/assets/icons/plusFill.svg
new file mode 100644
index 0000000..1fe152a
--- /dev/null
+++ b/src/assets/icons/plusFill.svg
@@ -0,0 +1,5 @@
+
diff --git a/src/assets/icons/refresh.svg b/src/assets/icons/refresh.svg
new file mode 100644
index 0000000..afbfc1a
--- /dev/null
+++ b/src/assets/icons/refresh.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/icons/save.svg b/src/assets/icons/save.svg
new file mode 100644
index 0000000..2a6590e
--- /dev/null
+++ b/src/assets/icons/save.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/icons/sort.svg b/src/assets/icons/sort.svg
new file mode 100644
index 0000000..0c78b98
--- /dev/null
+++ b/src/assets/icons/sort.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/icons/stop.svg b/src/assets/icons/stop.svg
new file mode 100644
index 0000000..3987afe
--- /dev/null
+++ b/src/assets/icons/stop.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/icons/zoomIn.svg b/src/assets/icons/zoomIn.svg
new file mode 100644
index 0000000..d952efe
--- /dev/null
+++ b/src/assets/icons/zoomIn.svg
@@ -0,0 +1,5 @@
+
diff --git a/src/assets/images/auth/auth-bg.jpg b/src/assets/images/auth/auth-bg.jpg
new file mode 100644
index 0000000..557e6ab
Binary files /dev/null and b/src/assets/images/auth/auth-bg.jpg differ
diff --git a/src/assets/images/index/bg.jpg b/src/assets/images/index/bg.jpg
new file mode 100644
index 0000000..442d8ce
Binary files /dev/null and b/src/assets/images/index/bg.jpg differ
diff --git a/src/assets/images/index/bg_old.jpg b/src/assets/images/index/bg_old.jpg
new file mode 100644
index 0000000..21b43ba
Binary files /dev/null and b/src/assets/images/index/bg_old.jpg differ
diff --git a/src/assets/images/index/sider-radar.png b/src/assets/images/index/sider-radar.png
new file mode 100644
index 0000000..1820bdc
Binary files /dev/null and b/src/assets/images/index/sider-radar.png differ
diff --git a/src/assets/images/logo/logo.png b/src/assets/images/logo/logo.png
new file mode 100644
index 0000000..3b1aa9f
Binary files /dev/null and b/src/assets/images/logo/logo.png differ
diff --git a/src/assets/logo.png b/src/assets/logo.png
new file mode 100644
index 0000000..46c2933
Binary files /dev/null and b/src/assets/logo.png differ
diff --git a/src/components/Base/Layout/LoginLayout/index.vue b/src/components/Base/Layout/LoginLayout/index.vue
new file mode 100644
index 0000000..c282c51
--- /dev/null
+++ b/src/components/Base/Layout/LoginLayout/index.vue
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/components/Base/Layout/SystemLayout/Content/index.vue b/src/components/Base/Layout/SystemLayout/Content/index.vue
new file mode 100644
index 0000000..21bf243
--- /dev/null
+++ b/src/components/Base/Layout/SystemLayout/Content/index.vue
@@ -0,0 +1,85 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/Base/Layout/SystemLayout/Header/index.vue b/src/components/Base/Layout/SystemLayout/Header/index.vue
new file mode 100644
index 0000000..7458f03
--- /dev/null
+++ b/src/components/Base/Layout/SystemLayout/Header/index.vue
@@ -0,0 +1,58 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/Base/Layout/SystemLayout/Sider/hooks/router.ts b/src/components/Base/Layout/SystemLayout/Sider/hooks/router.ts
new file mode 100644
index 0000000..b00b489
--- /dev/null
+++ b/src/components/Base/Layout/SystemLayout/Sider/hooks/router.ts
@@ -0,0 +1,44 @@
+import { h } from 'vue'
+import Icon from '@/components/Module/Icon/index.vue'
+
+
+export const useMain = () => {
+ // 路由过滤,只显示要显示的路由
+ const routerFilter = (menus: Array>) => {
+ return menus.filter((item: Record) => {
+ if (item.children) {
+ item.children = routerFilter(item.children)
+ }
+
+ return !item.meta.hidden
+ })
+ }
+
+ // 路由格式化
+ const routerFormat = (arr: Array>) => {
+ return arr.map((item: Record) => {
+ if (item.children && item.children.length) {
+ item.children = routerFormat(item.children)
+ }
+ return {
+ meta: item.meta,
+ icon: () =>
+ h(Icon, {
+ name: item.meta.icon,
+ }),
+ fullPath: item.fullPath,
+ pid: item.parentId,
+ key: item.name,
+ label: item.meta.title || '',
+ title: item.meta.title || '',
+ children: item.children?.length ? item.children : '',
+ type: item.type || null,
+ }
+ })
+ }
+
+ return {
+ routerFilter,
+ routerFormat
+ }
+}
diff --git a/src/components/Base/Layout/SystemLayout/Sider/index.vue b/src/components/Base/Layout/SystemLayout/Sider/index.vue
new file mode 100644
index 0000000..0cafa3f
--- /dev/null
+++ b/src/components/Base/Layout/SystemLayout/Sider/index.vue
@@ -0,0 +1,242 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/Base/Layout/SystemLayout/index.vue b/src/components/Base/Layout/SystemLayout/index.vue
new file mode 100644
index 0000000..a8acda7
--- /dev/null
+++ b/src/components/Base/Layout/SystemLayout/index.vue
@@ -0,0 +1,48 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/Base/Layout/ViewLayout/index.vue b/src/components/Base/Layout/ViewLayout/index.vue
new file mode 100644
index 0000000..3084ee8
--- /dev/null
+++ b/src/components/Base/Layout/ViewLayout/index.vue
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
diff --git a/src/components/Base/Tools/Info/index.vue b/src/components/Base/Tools/Info/index.vue
new file mode 100644
index 0000000..7bf4ae7
--- /dev/null
+++ b/src/components/Base/Tools/Info/index.vue
@@ -0,0 +1,56 @@
+
+
+
+
+
+
+
+
![]()
+
+
+ {{ info.title }}
+
+
+
+
+
diff --git a/src/components/Base/Tools/Menus/index.vue b/src/components/Base/Tools/Menus/index.vue
new file mode 100644
index 0000000..9d82de0
--- /dev/null
+++ b/src/components/Base/Tools/Menus/index.vue
@@ -0,0 +1,81 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/Base/Tools/UserBox/hooks/index.ts b/src/components/Base/Tools/UserBox/hooks/index.ts
new file mode 100644
index 0000000..de4dc6d
--- /dev/null
+++ b/src/components/Base/Tools/UserBox/hooks/index.ts
@@ -0,0 +1,36 @@
+export const useMain = () => {
+ const menus = [
+ {
+ name: '设置',
+ path:'/toolsSet',
+ key: 'toolsSet',
+ icon: 'briefCase'
+ },
+ {
+ name: '已归档对话',
+ path:'/workspace/knowledge',
+ key: 'knowledge',
+ icon: 'book'
+ },
+ {
+ name: '管理员面板',
+ path:'/fileManagementSet',
+ key: 'fileManagementSet',
+ icon: 'file'
+ },
+ ]
+
+ const operateMenus = [
+ {
+ name: '退出登录',
+ path:'/login',
+ key: 'login',
+ icon: 'briefCase'
+ }
+ ]
+
+ return {
+ menus,
+ operateMenus
+ }
+}
diff --git a/src/components/Base/Tools/UserBox/index.vue b/src/components/Base/Tools/UserBox/index.vue
new file mode 100644
index 0000000..f09d5c4
--- /dev/null
+++ b/src/components/Base/Tools/UserBox/index.vue
@@ -0,0 +1,71 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/Module/AsyncContent/ErrorLoading.vue b/src/components/Module/AsyncContent/ErrorLoading.vue
new file mode 100644
index 0000000..5bf6e0f
--- /dev/null
+++ b/src/components/Module/AsyncContent/ErrorLoading.vue
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/components/Module/AsyncContent/index.vue b/src/components/Module/AsyncContent/index.vue
new file mode 100644
index 0000000..8693f4b
--- /dev/null
+++ b/src/components/Module/AsyncContent/index.vue
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/components/Module/Icon/components/SvgIcon.vue b/src/components/Module/Icon/components/SvgIcon.vue
new file mode 100644
index 0000000..fb45c66
--- /dev/null
+++ b/src/components/Module/Icon/components/SvgIcon.vue
@@ -0,0 +1,80 @@
+
+
+
+
+
+
+
diff --git a/src/components/Module/Icon/components/icon.ts b/src/components/Module/Icon/components/icon.ts
new file mode 100644
index 0000000..db1dde4
--- /dev/null
+++ b/src/components/Module/Icon/components/icon.ts
@@ -0,0 +1,92 @@
+// Icon Store - For efficient SVG icon imports (Vue3 Composition API)
+import { ref } from 'vue';
+
+type IconMap = {
+ [key: string]: string;
+}
+
+// 响应式状态
+const availableIcons = ref({});
+const iconContents = ref({});
+const iconCache = new Map();
+
+// 使用Vite的import.meta.glob动态导入
+export const initialize = async () => {
+ const iconModules = import.meta.glob('/src/assets/icons/*.svg', {
+ eager: true,
+ as: 'raw'
+ });
+
+ Object.entries(iconModules).forEach(([path, content]: [string, string]) => {
+ const name = path?.split('/')?.pop()?.replace('.svg', '') as string;
+ iconContents.value[name] = content;
+ availableIcons.value[name] = `data:image/svg+xml;base64,${btoa(content)}`;
+ });
+
+ return Object.keys(availableIcons).length;
+};
+
+// 获取图标URL
+export const getIconUrl = (name: string) => {
+ if (iconCache.has(name)) return iconCache.get(name);
+
+ const url = availableIcons.value[name];
+ if (url) iconCache.set(name, url);
+ return url;
+};
+
+// 获取原始SVG内容
+export const getIconContent = (name: string) => {
+ return iconContents.value[name] || null;
+};
+
+initialize()
+
+// return {
+// availableIcons: ref(availableIcons),
+// iconContents: ref(iconContents),
+// initialize,
+// getIconUrl,
+// getIconContent
+// };
+
+// 初始化图标存储
+// export function useIconStore() {
+// // 使用Vite的import.meta.glob动态导入
+// const initialize = async () => {
+// const iconModules = import.meta.glob('/src/lib/assets/icons/*.svg', {
+// eager: true,
+// as: 'raw'
+// });
+
+// Object.entries(iconModules).forEach(([path, content]: [string, string]) => {
+// const name = path?.split('/')?.pop()?.replace('.svg', '') as string;
+// iconContents.value[name] = content;
+// availableIcons.value[name] = `data:image/svg+xml;base64,${btoa(content)}`;
+// });
+
+// return Object.keys(availableIcons).length;
+// };
+
+// // 获取图标URL
+// const getIconUrl = (name: string) => {
+// if (iconCache.has(name)) return iconCache.get(name);
+
+// const url = availableIcons.value[name];
+// if (url) iconCache.set(name, url);
+// return url;
+// };
+
+// // 获取原始SVG内容
+// const getIconContent = (name: string) => {
+// return iconContents.value[name] || null;
+// };
+
+// return {
+// availableIcons: ref(availableIcons),
+// iconContents: ref(iconContents),
+// initialize,
+// getIconUrl,
+// getIconContent
+// };
+// }
diff --git a/src/components/Module/Icon/index.vue b/src/components/Module/Icon/index.vue
new file mode 100644
index 0000000..397dcdf
--- /dev/null
+++ b/src/components/Module/Icon/index.vue
@@ -0,0 +1,73 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/Module/Modal/components/ModalHeader.vue b/src/components/Module/Modal/components/ModalHeader.vue
new file mode 100644
index 0000000..3813b7e
--- /dev/null
+++ b/src/components/Module/Modal/components/ModalHeader.vue
@@ -0,0 +1,171 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/components/Module/Modal/index.vue b/src/components/Module/Modal/index.vue
new file mode 100644
index 0000000..1eb79f2
--- /dev/null
+++ b/src/components/Module/Modal/index.vue
@@ -0,0 +1,187 @@
+
+
+
+
+
+
+
+
+
+
+ {{ props.title }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{{
+ confirmText
+ }}
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/__tests__/HelloWorld.spec.ts b/src/components/__tests__/HelloWorld.spec.ts
new file mode 100644
index 0000000..2533202
--- /dev/null
+++ b/src/components/__tests__/HelloWorld.spec.ts
@@ -0,0 +1,11 @@
+import { describe, it, expect } from 'vitest'
+
+import { mount } from '@vue/test-utils'
+import HelloWorld from '../HelloWorld.vue'
+
+describe('HelloWorld', () => {
+ it('renders properly', () => {
+ const wrapper = mount(HelloWorld, { props: { msg: 'Hello Vitest' } })
+ expect(wrapper.text()).toContain('Hello Vitest')
+ })
+})
diff --git a/src/config/index.ts b/src/config/index.ts
new file mode 100644
index 0000000..b53e802
--- /dev/null
+++ b/src/config/index.ts
@@ -0,0 +1,8 @@
+import logo from '@/assets/images/logo/logo.png'
+import authBg from '@/assets/images/auth/auth-bg.jpg'
+
+export const info = {
+ logo: logo,
+ title: "雷达⽬标识别迁移部署系统",
+ bgImg: authBg
+}
diff --git a/src/core/lazy_use.ts b/src/core/lazy_use.ts
new file mode 100644
index 0000000..90a4e7e
--- /dev/null
+++ b/src/core/lazy_use.ts
@@ -0,0 +1,115 @@
+import type { App } from 'vue'
+import {
+ App as antApp,
+ Button,
+ Tree,
+ ConfigProvider,
+ Layout,
+ Menu,
+ PageHeader,
+ Dropdown,
+ Drawer,
+ Space,
+ Switch,
+ Input,
+ Form,
+ Popover,
+ Row,
+ Col,
+ Select,
+ Checkbox,
+ Modal,
+ Tag,
+ AutoComplete,
+ Upload,
+ Radio,
+ Tabs,
+ Timeline,
+ Carousel,
+ Spin,
+ List,
+ Tooltip,
+ Card,
+ DatePicker,
+ Divider,
+ message,
+ InputNumber,
+ TreeSelect,
+ Steps,
+ Pagination,
+ Popconfirm,
+ Cascader,
+ Badge,
+ Empty,
+ Progress,
+ Affix,
+ Descriptions,
+ Slider,
+ Table
+} from 'ant-design-vue'
+
+import { router } from '@/router/index.ts'
+
+
+const [ messageApi, contextHolder] = message.useMessage();
+
+// 注册全局组件
+export function loadComponent(app: App) {
+ app
+ .use(ConfigProvider)
+ .use(Layout)
+ .use(Checkbox)
+ .use(Menu)
+ .use(Button)
+ .use(Tree)
+ .use(antApp)
+ .use(PageHeader)
+ .use(Dropdown)
+ .use(Drawer)
+ .use(Space)
+ .use(Switch)
+ .use(Input)
+ .use(Form)
+ .use(Popover)
+ .use(Row)
+ .use(Col)
+ .use(Select)
+ .use(Table)
+ .use(Modal)
+ .use(Tag)
+ .use(AutoComplete)
+ .use(Upload)
+ .use(Radio)
+ .use(Tabs)
+ .use(Timeline)
+ .use(Carousel)
+ .use(Spin)
+ .use(List)
+ .use(Tooltip)
+ .use(Card)
+ .use(DatePicker)
+ .use(Divider)
+ .use(List)
+ .use(InputNumber)
+ .use(TreeSelect)
+ // .use(hevueImgPreview)
+ .use(Steps)
+ .use(Pagination)
+ .use(Popconfirm)
+ .use(Cascader)
+ .use(Badge)
+ .use(Empty)
+ .use(Progress)
+ .use(Affix)
+ .use(Descriptions)
+ .use(Slider)
+
+
+}
+
+// 注册全局方法
+export function loadFunc (app: App) {
+
+
+}
+
diff --git a/src/directive/index.ts b/src/directive/index.ts
new file mode 100644
index 0000000..c3043ae
--- /dev/null
+++ b/src/directive/index.ts
@@ -0,0 +1,8 @@
+import type { App } from 'vue'
+
+
+function directive(app: App) {
+ // 注册指令
+}
+
+export default directive;
diff --git a/src/main.ts b/src/main.ts
new file mode 100644
index 0000000..6a56953
--- /dev/null
+++ b/src/main.ts
@@ -0,0 +1,36 @@
+// import './assets/main.css'
+import '@/styles/index.css'
+import 'ant-design-vue/dist/reset.css'
+
+import { createApp } from 'vue'
+import { setupStore } from '@/stores'
+import { router, setupRouter } from '@/router'
+import { setRouteGuard } from '@/router/guard/index'
+import { loadComponent, loadFunc } from '@/core/lazy_use'
+
+import * as Icons from '@ant-design/icons-vue'
+
+import App from './App.vue'
+// import router from './router'
+
+
+
+const app = createApp(App)
+
+loadComponent(app)
+
+loadFunc(app)
+
+setupStore(app)
+
+setupRouter(app)
+
+setRouteGuard(router)
+
+const icons:any = Icons
+for (const i in icons) {
+ app.component(i, icons[i])
+}
+
+
+app.mount('#app')
diff --git a/src/router/base/index.ts b/src/router/base/index.ts
new file mode 100644
index 0000000..c7193ad
--- /dev/null
+++ b/src/router/base/index.ts
@@ -0,0 +1,40 @@
+import type { AppRouter } from '@/router/config/index'
+
+export const baseRouter: AppRouter= {
+ name: 'Base',
+ path: '',
+ meta: {
+ title: '首页',
+ hidden: false,
+ hiddenInMenu: true,
+ },
+ component: 'BaseLayout',
+ children: []
+}
+
+export const globalRouter = [
+ {
+ name: 'LoginView',
+ path: '/login',
+ redirect: '/login/index',
+ icon: 'HomeOutlined',
+ hidden: false,
+ meta: {
+ icon: 'HomeOutlined',
+ title: '用户登录',
+ },
+ component: () => import('@/components/Base/Layout/LoginLayout/index.vue'),
+ children: [
+ {
+ name: 'Login',
+ path: 'index',
+ meta: {
+ hidden: false,
+ title: '用户登录',
+ },
+ component: () => import('@/views/login/index.vue'),
+
+ },
+ ]
+ },
+]
diff --git a/src/router/config/index.ts b/src/router/config/index.ts
new file mode 100644
index 0000000..b90d0ab
--- /dev/null
+++ b/src/router/config/index.ts
@@ -0,0 +1,61 @@
+import type {
+ RouteRecordRaw,
+ RouteMeta
+} from 'vue-router'
+
+import type { defineComponent } from 'vue'
+
+
+export type Component = ReturnType | (() => Promise) | (() => Promise)
+
+// @ts-expect-error
+export interface AppRouter extends Omit {
+ keepAlive?: boolean
+ visible?: boolean
+ icon?: string
+ name?: string
+ key?: string
+ sort?: number
+ parent?: AppRouter | null
+ parentId?: number | string
+ menuId?: number | string
+ meta: RouteMeta
+ component?: Component | string
+ components?: Component
+ componentName?: string
+ children?: AppRouter[]
+ props?: Recordable,
+ path?: string,
+ fullPath?: string,
+ hidden?: boolean,
+ type?: string | null,
+ typeName?: string | null,
+ menuName?: string | null
+ menuType?: string | null
+}
+
+
+// export interface AppRouter {
+// keepAlive?: boolean, // 缓存
+// component?: RawRouteComponent | string | null | undefined | any, // 组件
+// children?: AppRouter[], // 子路由
+// path?: string, // 路径
+// redirect?: string, // 重定向
+// isFrame?: number, // 外链
+// target?: string // 外链打开方式 新标签打开还是当前页面打开
+// key?: string,
+// hidden: boolean, // 是否隐藏
+// title?: string // 标题
+// // visible?: boolean //
+// // icon?: string // 图标
+// name: string // 路由name
+// sort?: number // 排序
+// parentId?: number // 父节点
+// // component?: Component | string
+// // components?: Component
+// // componentName?: string //
+// hideChildrenInMenu?: boolean, // 隐藏下级节点
+// meta?: RouteMeta, // 信息
+// // props?: Recordable
+// // fullPath?: string // 全路径
+// }
diff --git a/src/router/func/index.ts b/src/router/func/index.ts
new file mode 100644
index 0000000..c70b1f2
--- /dev/null
+++ b/src/router/func/index.ts
@@ -0,0 +1,70 @@
+import { layouts, staticRouters } from "@/router/modules/routerComponents"
+import type { AppRouter } from '@/router/config/index'
+import { validURL } from "@/utils/valid"
+import type { Component } from "vue"
+import { uuid } from '@/utils/util'
+
+
+const constantRouterComponents: Component = {
+ ...layouts,
+ ...staticRouters
+}
+
+const menuTypeFormat = (type?: string) => {
+ if (!type) return null
+ const menuTypeObj: ExtraObj = {
+ 'G': 'group', // 分组
+ 'F': 'button', // 按钮
+ 'C': 'menu', // 菜单
+ 'M': 'catalogue', // 目录
+ }
+ return menuTypeObj[type] || null
+}
+
+// 构造路由
+export const generator = async (routerMap: AppRouter[] | ExtraObj[], parent:AppRouter | null): Promise => {
+ const newRouter = await Promise.all(routerMap.map(async (item: AppRouter | ExtraObj) => {
+ const { title, hideChildren, hiddenHeaderContent, hidden, icon, isFrame, target } = item.meta || {}
+ const currentRouter: AppRouter = {
+ path: item.path,
+ key: item.key,
+ fullPath: (parent ? `${parent?.fullPath ?? ''}${item.path?.includes('/') ? item.path : '/' + item.path}` : `${item.path}`),
+ // 路由名称,建议唯一
+ name: item.menuId || item.name || item.key || uuid(),
+ // 该路由对应页面的 组件(动态加载)
+ component: constantRouterComponents[item.component] || (() => import(`@/views/${item.component}`)),
+ // hideChildrenInMenu: item.hideChildrenInMenu,
+ // meta: 页面标题, 菜单图标, 页面权限(供指令权限用,可去掉)
+ hidden: item.hidden,
+ parent: parent,
+ parentId: item.parentId ?? parent?.name,
+ icon: item?.meta?.icon || item.icon as string | undefined,
+ meta: {
+ icon: item?.meta?.icon || item.icon,
+ isFrame: isFrame,
+ title: title || item.menuName,
+ hideChildrenInMenu: hideChildren,
+ hiddenHeaderContent: hiddenHeaderContent,
+ // 目前只能通过判断path的http链接来判断是否外链
+ target: (validURL(item.path) ? '_blank' : ''),
+ permission: item.name,
+ hidden: hidden
+ },
+ type: item.type || menuTypeFormat(item.menuType),
+ typeName: item.typeName || null,
+ redirect: item.redirect
+ }
+ if (item.component && !constantRouterComponents[item.component]) {
+ currentRouter.path = `${parent?.path ?? ''}/${item.path}`
+ }
+ // 是否设置了隐藏子菜单
+ // 是否有子菜单,并递归处理,并将父path传入
+ if (item.children && item.children.length > 0) {
+ // Recursion
+ currentRouter.children = await generator(item.children, currentRouter)
+ }
+ return currentRouter
+ }))
+
+ return newRouter
+}
diff --git a/src/router/guard/before.ts b/src/router/guard/before.ts
new file mode 100644
index 0000000..3dd24e7
--- /dev/null
+++ b/src/router/guard/before.ts
@@ -0,0 +1,20 @@
+import { useSocketStoreWithOut } from '@/stores/modules/socket'
+
+const socketStore = useSocketStoreWithOut()
+
+
+
+// 初始化socket
+export const initSocket = () => {
+ if (!socketStore.socket) {
+ socketStore.initSocket()
+ }
+}
+
+
+// 初始化
+export const init = async () => {
+ return Promise.all([
+ await initSocket(),
+ ])
+}
diff --git a/src/router/guard/index.ts b/src/router/guard/index.ts
new file mode 100644
index 0000000..465f13c
--- /dev/null
+++ b/src/router/guard/index.ts
@@ -0,0 +1,90 @@
+import type { Router, RouteRecordRaw, RouteRecordName } from 'vue-router'
+import { useUserStore } from '@/stores/modules/user'
+import { useRouterStoreWithOut } from '@/stores/modules/async-router'
+import type { AppRouter } from '@/router/config/index'
+import { APP_VERSION } from '@/stores/modules/mutation-types'
+import { info } from '@/config/index.ts'
+import { init as beforeInit } from '@/router/guard/before.ts'
+
+const allowName = ['Login', 'LoginView', '404']
+const defaultRouter = '/'
+
+
+export function _window(key: string) {
+ if (window.hasOwnProperty('admin_config')) {
+ //@ts-ignore
+ return window.admin_config[key]
+ }
+
+ return null
+}
+
+// 版本校验
+const checkVersion = () => {
+
+}
+
+
+
+
+
+
+export const setRouteGuard = (router:Router) => {
+ router.beforeEach(async (to, from, next) => {
+ window.document.title = `${to.meta.title as string} | ${info.title}`
+ console.log('from', from)
+ // beforeInit()
+ // 校验版本
+ // checkVersion()
+ const userStore = useUserStore()
+ const token = userStore.getToken()
+ console.log('getToken', token)
+ // token失效走退出
+ // if (token && !userStore.validToken()) {
+ // // userStore.logout()
+ // }
+
+ // 无token
+ // if(!token || token === '') {
+ // console.log(to.name && allowName.includes(to.name as string))
+ // if (to.name && allowName.includes(to.name as string)) {
+ // next()
+ // } else {
+ // // next({ path: '/login/index', query: { redirect: to.fullPath } })
+ // next()
+ // }
+ // } else {
+ console.log('toROuter', to)
+ const routerStore = useRouterStoreWithOut()
+ const nowRouter = routerStore.ROUTERS
+ // 无角色, 重新初始化路由
+ // if (!userStore.permissions?.length || (userStore.permissions?.length && !nowRouter.length))
+ // if (!(userStore.permissions?.length && nowRouter.length)){
+ // 结构化路由
+ if (!(nowRouter.length)){
+ await routerStore.loadRouter().then(res => {
+ const addRoters:AppRouter[] = res
+ console.log('addRoters', addRoters)
+ // routerStore.setRouter(addRoters)
+ addRoters.map((route)=> {
+ router.addRoute(route as RouteRecordRaw)
+ })
+ })
+ next({ path: to.fullPath, replace: true, query: to.query })
+ } else {
+ // const userInfo = userStore.USERINFO ? JSON.parse(userStore.USERINFO) : {}
+ // console.log('userInfo', userInfo)
+ // if ((token || token !== '') && !userInfo.sysUserId) {
+ // userStore.getUserInfo()
+ // }
+ next()
+ // if (to.path == '/') {
+ // next({ path: defaultRouter, replace: true, query: to.query })
+
+ // } else {
+ // next()
+ // }
+ }
+ // }
+ })
+}
diff --git a/src/router/index.ts b/src/router/index.ts
new file mode 100644
index 0000000..4d8be1a
--- /dev/null
+++ b/src/router/index.ts
@@ -0,0 +1,17 @@
+import { createRouter, createWebHashHistory } from 'vue-router'
+import { globalRouter } from './base/index'
+import type { App } from 'vue'
+
+
+export const router = createRouter({
+ history: createWebHashHistory(),
+ routes: [
+ ...globalRouter
+ ],
+})
+
+
+export function setupRouter(app: App) {
+ app.use(router)
+}
+
diff --git a/src/router/modules/routerComponents.ts b/src/router/modules/routerComponents.ts
new file mode 100644
index 0000000..894c741
--- /dev/null
+++ b/src/router/modules/routerComponents.ts
@@ -0,0 +1,21 @@
+import { defineAsyncComponent, markRaw } from 'vue'
+
+export const layouts = {
+ BaseLayout: () => import('@/components/Base/Layout/SystemLayout/index.vue'),
+ ViewLayout: () => import('@/components/Base/Layout/ViewLayout/index.vue'),
+ LoginLayout: () => import('@/components/Base/Layout/LoginLayout/index.vue'),
+}
+
+
+
+export const staticRouters = {
+ Index: () => import('@/views/index/index.vue'),
+ Test: () => import('@/views/test/index.vue'),
+ NotFound: defineAsyncComponent(() => import('@/views/exception/404.vue')),
+ DataManage: defineAsyncComponent(() => import('@/views/dataManage/index/index.vue')),
+ ModelReasoning: defineAsyncComponent(() => import('@/views/modelReasoning/index/index.vue')),
+ ModelTraining: defineAsyncComponent(() => import('@/views/modelTraining/index/index.vue')),
+ ModelTrainingDetail: defineAsyncComponent(() => import('@/views/modelTraining/detail/index.vue')),
+ ModelManage: defineAsyncComponent(() => import('@/views/setting/model/index/index.vue')),
+}
+
diff --git a/src/router/modules/staticRouter.ts b/src/router/modules/staticRouter.ts
new file mode 100644
index 0000000..c3ba8a6
--- /dev/null
+++ b/src/router/modules/staticRouter.ts
@@ -0,0 +1,109 @@
+export const staticRouter = [
+
+ {
+ name: 'DataManage',
+ path: '/data-manage',
+ meta: {
+ icon: 'chat',
+ title: '数据管理',
+ hidden: false,
+ },
+ component: 'DataManage',
+ },
+ {
+ name: 'ModelTraining',
+ path: '/model-training',
+ redirect: '/model-training/index',
+ meta: {
+ icon: 'sort',
+ title: '模型训练',
+ hidden: false,
+ },
+ component: 'ViewLayout',
+ children: [
+ {
+ name: 'ModelTrainingIndex',
+ path: 'index',
+ hidden: false,
+ meta: {
+ icon: 'sort',
+ title: '模型训练',
+ hidden: true,
+ },
+ component: 'ModelTraining',
+ },
+ {
+ name: 'ModelTrainingDetail',
+ path: 'detail',
+ hidden: false,
+ meta: {
+ icon: 'sort',
+ title: '模型训练',
+ hidden: true,
+ },
+ component: 'ModelTrainingDetail',
+ }
+ ]
+ },
+ {
+ name: 'ModelReasoning',
+ path: '/model-reasoning',
+ meta: {
+ icon: 'data',
+ title: '模型推理',
+ hidden: false,
+ },
+ component: 'ModelReasoning',
+ },
+ {
+ name: 'ModelManage',
+ path: '/model-manage',
+ meta: {
+ icon: 'data',
+ title: '模型管理',
+ hidden: false,
+ },
+ component: 'ModelManage',
+ },
+ {
+ path: '',
+ meta: {
+ hidden: true,
+ },
+ redirect: '/data-manage'
+ },
+]
+
+export const exceptionRouter = [
+ {
+ path: '/404',
+ name: '404',
+ component: 'NotFound',
+ meta: {
+ icon: '',
+ title: '',
+ hidden: true,
+ },
+ },
+ {
+ path: '/test',
+ name: 'test',
+ component: 'Test',
+ meta: {
+ icon: '',
+ title: '',
+ hidden: true,
+ },
+ },
+ {
+ path: '/:pathMatch(.*)',
+ redirect: '/404',
+ hidden: true,
+ meta: {
+ icon: '',
+ title: '',
+ hidden: true,
+ },
+ }
+]
+
diff --git a/src/stores/counter.ts b/src/stores/counter.ts
new file mode 100644
index 0000000..b6757ba
--- /dev/null
+++ b/src/stores/counter.ts
@@ -0,0 +1,12 @@
+import { ref, computed } from 'vue'
+import { defineStore } from 'pinia'
+
+export const useCounterStore = defineStore('counter', () => {
+ const count = ref(0)
+ const doubleCount = computed(() => count.value * 2)
+ function increment() {
+ count.value++
+ }
+
+ return { count, doubleCount, increment }
+})
diff --git a/src/stores/index.ts b/src/stores/index.ts
new file mode 100644
index 0000000..cd30903
--- /dev/null
+++ b/src/stores/index.ts
@@ -0,0 +1,10 @@
+import type { App } from 'vue'
+import { createPinia } from 'pinia'
+
+const store = createPinia()
+
+export function setupStore(app: App) {
+ app.use(store)
+}
+
+export { store }
\ No newline at end of file
diff --git a/src/stores/modules/async-router.ts b/src/stores/modules/async-router.ts
new file mode 100644
index 0000000..ca10bfe
--- /dev/null
+++ b/src/stores/modules/async-router.ts
@@ -0,0 +1,54 @@
+import { defineStore } from "pinia"
+import { store } from '@/stores'
+import { staticRouter, exceptionRouter } from '@/router/modules/staticRouter'
+import type { AppRouter } from '@/router/config/index'
+import { generator } from '@/router/func/index'
+import { baseRouter } from '@/router/base/index'
+import { handleTree } from "@/utils/util"
+import { Row } from "ant-design-vue"
+
+interface router {
+ routerArr: AppRouter[]
+ asyncRouter: ExtraObj[]
+}
+
+
+export const useRouterStore = defineStore('router', {
+ state: (): router => ({
+ routerArr: [],
+ asyncRouter: []
+ }),
+ getters: {
+ ROUTERS(state):AppRouter[] {
+ return state.routerArr
+ },
+ ASYNCROUTERS(state): ExtraObj[] {
+ return state.asyncRouter
+ },
+ },
+ actions: {
+ setRouter(data: AppRouter[] ) {
+ this.routerArr = data
+ },
+ async loadRouter(): Promise {
+ let routers:AppRouter[] = []
+ return new Promise(async (resolve) => {
+
+ // 组装
+ const menuNav: ExtraObj[] = []
+ baseRouter.children = staticRouter.concat() as AppRouter[]
+ menuNav.push(baseRouter)
+ menuNav.push(...exceptionRouter)
+ // menuNav.push(...globalRouter)
+ routers = await generator(menuNav, null)
+ console.log('routers', routers)
+ this.routerArr = routers
+ resolve(routers)
+ })
+ }
+ },
+})
+
+export const useRouterStoreWithOut = () => {
+ return useRouterStore(store)
+}
diff --git a/src/stores/modules/mutation-types.ts b/src/stores/modules/mutation-types.ts
new file mode 100644
index 0000000..01daca2
--- /dev/null
+++ b/src/stores/modules/mutation-types.ts
@@ -0,0 +1,6 @@
+
+export const ACCESS_TOKEN = 'token'
+export const ACCESS_EXPIRES = 'expires'
+export const APP_VERSION = 'app_version'
+
+
diff --git a/src/stores/modules/socket.ts b/src/stores/modules/socket.ts
new file mode 100644
index 0000000..54f9e07
--- /dev/null
+++ b/src/stores/modules/socket.ts
@@ -0,0 +1,78 @@
+import { defineStore, acceptHMRUpdate } from "pinia"
+import { store } from '@/stores'
+import { socketInit } from '@/utils/socket/index.ts'
+import type { Socket } from "socket.io-client"
+
+interface UserState {
+ socket: Socket | null,
+ connected:any,
+ reconnectAttempts: number,
+}
+
+export const useSocketStore = defineStore('socket', {
+ state: (): UserState => ({
+ socket: null,
+ connected: false,
+ reconnectAttempts: 0,
+ }),
+ getters: {
+ SOCKET(state) {
+ return state.socket
+ },
+ isConnected(state) {
+ return state.connected
+ },
+ attempts(state) {
+ return state.reconnectAttempts
+ }
+ },
+ actions: {
+ // 获取token
+ initSocket() {
+ console.log('?initSocket')
+ const socket = socketInit(true)
+ this.$patch((state) => {
+ state.socket = socket
+ })
+ // ===== 事件监听 =====
+
+ socket.on('connect', () => {
+ this.connected = true
+ console.log('[Socket] 已连接,socket id:', socket.id)
+ })
+
+ socket.on('disconnect', (reason) => {
+ this.connected = false
+ console.log(`[Socket] 已断开,原因: ${reason}`)
+ })
+
+ // 重连中,每次尝试都会触发
+ socket.io.on('reconnect_attempt', (attempt: number) => {
+ this.reconnectAttempts = attempt
+ console.log(`[Socket] 重连尝试 #${attempt}`)
+ })
+
+ // 重连成功
+ socket.io.on('reconnect', (attempt: number) => {
+ console.log(`[Socket] 重连成功,尝试次数: ${attempt}`)
+ })
+
+ // 重连失败
+ socket.io.on('reconnect_failed', () => {
+ console.warn('[Socket] 重连失败,放弃继续重连')
+ })
+ },
+ // 清除仓库数据
+ cleanStore() {
+ this.$reset()
+ }
+ },
+})
+
+if (import.meta.hot) {
+ import.meta.hot.accept(acceptHMRUpdate(useSocketStore, import.meta.hot))
+}
+
+export const useSocketStoreWithOut = () => {
+ return useSocketStore(store)
+}
diff --git a/src/stores/modules/system.ts b/src/stores/modules/system.ts
new file mode 100644
index 0000000..afd8fea
--- /dev/null
+++ b/src/stores/modules/system.ts
@@ -0,0 +1,22 @@
+import { defineStore } from "pinia"
+import { store } from '@/stores'
+
+
+interface info {
+}
+
+
+export const systemStore = defineStore('system', {
+ state: (): info => ({
+ }),
+ getters: {
+
+ },
+ actions: {
+
+ },
+})
+
+export const systemStoreWithOut = () => {
+ return systemStore(store)
+}
diff --git a/src/stores/modules/user.ts b/src/stores/modules/user.ts
new file mode 100644
index 0000000..7922a1e
--- /dev/null
+++ b/src/stores/modules/user.ts
@@ -0,0 +1,103 @@
+import { defineStore, acceptHMRUpdate } from "pinia"
+import { store } from '@/stores'
+import { ACCESS_TOKEN, ACCESS_EXPIRES, USER_INFO } from '@/stores/modules/mutation-types'
+// import { loginApi } from '@/apis/auths/index'
+import { notification } from 'ant-design-vue';
+import { useRouterStoreWithOut } from './async-router'
+
+interface LoginForm {
+ email: string;
+ password: string;
+}
+
+interface UserState {
+ token: string | null
+ role: string | null
+ permissions: {
+ [key: string]: ExtraObj
+ },
+ userInfo?: ExtraObj,
+}
+
+export const useUserStore = defineStore('user', {
+ state: (): UserState => ({
+ token: '',
+ role: null,
+ permissions: {},
+ userInfo: {},
+ }),
+ getters: {
+ ROLE(state) {
+ return state.role
+ },
+ TOKEN(): string {
+ return this.getToken()
+ },
+ USERINFO(state) {
+ return state.userInfo
+ },
+
+ },
+ actions: {
+ // 获取token
+ getToken() {
+ const raw = localStorage.getItem(ACCESS_TOKEN) || ''
+ return raw
+ },
+ // 设置token
+ setToken(token: string) {
+ console.log('ACCESS_TOKEN', ACCESS_TOKEN)
+ localStorage.setItem(ACCESS_TOKEN, token || '')
+ },
+ // 设置用户数据
+ setUserInfo(data: ExtraObj | null) {
+ this.$patch((state) => {
+ if (data) {
+ state.userInfo = data
+ state.permissions = data.permissions
+ state.role = data.role
+ } else {
+ state.userInfo = {}
+ state.permissions = {}
+ state.role = null
+ state.userInfo = {}
+ }
+
+ })
+ },
+ // 登录
+ async login(data: LoginForm) {
+ // let res: ExtraObj
+ // res = await loginApi(data)
+ // .catch((err) => {
+ // notification.error({
+ // message: '登录失败',
+ // description: err.message,
+ // })
+ // return err
+ // })
+ // if (res.code == 200) {
+ // this.setToken(res.data.token)
+ // this.setUserInfo(res.data)
+ // this.getUserInfo()
+ // }
+ // return res
+ },
+ // 获取用户信息
+ async getUserInfo() {
+
+ },
+ // 退出
+ logout() {
+
+ },
+ },
+})
+
+if (import.meta.hot) {
+ import.meta.hot.accept(acceptHMRUpdate(useUserStore, import.meta.hot))
+}
+
+export const useUserStoreWithOut = () => {
+ return useUserStore(store)
+}
diff --git a/src/styles/index.css b/src/styles/index.css
new file mode 100644
index 0000000..bbc22d8
--- /dev/null
+++ b/src/styles/index.css
@@ -0,0 +1,30 @@
+@charset "UTF-8";
+#app {
+ padding: 0;
+ margin: 0;
+ max-width: 100%;
+ width: 100%;
+ height: 100vh;
+}
+
+p {
+ margin-top: 0;
+ margin-bottom: 0;
+}
+
+button {
+ margin: 0;
+ /* 清除外边距 */
+ padding: 0;
+ /* 清除内边距 */
+ border: none;
+ /* 去除边框 */
+ background: none;
+ /* 清除背景色 */
+ font: inherit;
+ /* 继承父元素字体 */
+ color: inherit;
+ /* 继承父元素文字颜色 */
+ cursor: pointer;
+ /* 保持指针样式 */
+}
diff --git a/src/styles/index.scss b/src/styles/index.scss
new file mode 100644
index 0000000..a44ae04
--- /dev/null
+++ b/src/styles/index.scss
@@ -0,0 +1,34 @@
+html,
+body,
+p {
+}
+
+#app {
+ padding: 0;
+ margin: 0;
+ max-width: 100%;
+ width: 100%;
+ height: 100vh;
+}
+
+p {
+ margin-top: 0;
+ margin-bottom: 0;
+}
+
+a,
+a:hover,
+a:visited,
+a:link,
+a:active {
+}
+
+button {
+ margin: 0; /* 清除外边距 */
+ padding: 0; /* 清除内边距 */
+ border: none; /* 去除边框 */
+ background: none; /* 清除背景色 */
+ font: inherit; /* 继承父元素字体 */
+ color: inherit; /* 继承父元素文字颜色 */
+ cursor: pointer; /* 保持指针样式 */
+}
diff --git a/src/styles/theme/default.json b/src/styles/theme/default.json
new file mode 100644
index 0000000..abf96df
--- /dev/null
+++ b/src/styles/theme/default.json
@@ -0,0 +1,30 @@
+{
+ "token": {
+ "fontSize": 16,
+ "wireframe": false,
+ "fontSizeXL": 20,
+ "fontSizeHeading1": 36,
+ "fontSizeHeading2": 20,
+ "fontSizeHeading3": 16,
+ "fontSizeHeading4": 14,
+ "fontSizeHeading5": 12,
+ "lineHeight": 1.5,
+ "sizeStep": 4,
+ "borderRadiusLG": 16
+ },
+ "components": {
+ "Dropdown": {
+ "borderRadiusLG": 6,
+ "fontSize": 14,
+ "fontSizeSM": 12,
+ "lineWidthBold": 2
+ },
+ "Menu": {
+ "zIndexPopup": 1050,
+ "dropdownWidth": 260,
+ "radiusItem": 6
+ },
+ "Pagination": {},
+ "Cascader": {}
+ }
+}
diff --git a/src/types/global.d.ts b/src/types/global.d.ts
new file mode 100644
index 0000000..688a850
--- /dev/null
+++ b/src/types/global.d.ts
@@ -0,0 +1,37 @@
+import type { DefineComponent } from 'vue'
+
+
+declare global {
+ type ExtraObj = {
+ [key?: string]: any
+ }
+ type Recordable = Record
+
+ declare module '*.csv' {
+ const value: any;
+ export default value;
+ }
+}
+
+
+declare module '*.jpg' {
+ const value: any;
+ export default value;
+}
+
+declare module '*.png' {
+ const value: any;
+ export default value;
+}
+
+
+
+declare module '*.vue' {
+ const component: DefineComponent<{}, {}, any>
+ export default component
+}
+
+declare module '@components/*';
+
+
+
diff --git a/src/types/module.d.ts b/src/types/module.d.ts
new file mode 100644
index 0000000..5468342
--- /dev/null
+++ b/src/types/module.d.ts
@@ -0,0 +1,37 @@
+declare module '*.vue' {
+ import { DefineComponent } from 'vue'
+ const Component: DefineComponent<{}, {}, any>
+ export default Component
+}
+
+// declare module 'ant-design-vue/es/locale/*' {
+// import { Locale } from 'ant-design-vue/types/locale-provider'
+// const locale: Locale & ReadonlyRecordable
+// export default locale as Locale & ReadonlyRecordable
+// }
+
+declare module 'virtual:*' {
+ const result: any
+ export default result
+}
+
+declare module 'lodash' {
+ const content: any
+ export = content
+}
+declare module '*.ts' {
+ const content: any
+ export = content
+}
+
+declare module 'hevue-img-preview' {
+ const content: any
+ export = content
+}
+
+declare module 'fabric-with-erasing' {
+ const content: any
+ export = content
+}
+
+declare module '@ant-design/icons-vue';
diff --git a/src/utils/EvenStream/index.ts b/src/utils/EvenStream/index.ts
new file mode 100644
index 0000000..e69de29
diff --git a/src/utils/download/index.ts b/src/utils/download/index.ts
new file mode 100644
index 0000000..97c2d48
--- /dev/null
+++ b/src/utils/download/index.ts
@@ -0,0 +1,187 @@
+// import { getEnv } from '@/lib/utils/env/index.ts'
+import { request } from '@/utils/request/index.ts';
+
+
+
+// 创建一个a标签,并做点击下载事件
+export function downloadFile(hrefUrl:any, fileName:any){
+ console.log('downloadFile',hrefUrl,fileName);
+ const a = document.createElement('a')
+ a.href = hrefUrl
+ a.target = '_blank' // 新窗口打开 20250320 yjc 新增
+ a.download = fileName // 下载后文件名
+ document.body.appendChild(a)
+ a.click() // 点击下载
+ document.body.removeChild(a) // 下载完成移除元素
+}
+// 封装blob对象
+function dataURLToBlob(base64Str:any, mimeTypeStr:any) {
+ const bstr = window.atob(base64Str); // 解码 base-64 编码的字符串,base-64 编码使用方法是 btoa()
+ let length = bstr.length;
+ const u8arr = new Uint8Array(length); // 创建初始化为0的,包含length个元素的无符号整型数组
+ while (length--) {
+ u8arr[length] = bstr.charCodeAt(length); // 返回在指定的位置的字符的 Unicode 编码
+ }
+ return new Blob([u8arr], { type: mimeTypeStr }); // 返回一个blob对象
+}
+
+// 后端返回base64公共导出
+export function downloadFileByBase64(base64Str:any, mimeTypeStr:any, fileName:any){
+ const myBlob = dataURLToBlob(base64Str, mimeTypeStr)
+ const myUrl = window.URL.createObjectURL(myBlob)
+ downloadFile(myUrl, fileName)
+}
+// 后端返回文件流公共导出
+export function downloadFileByFileFlow(blobData:any, mimeTypeStr:any, fileName:any) {
+ const blob = new Blob([blobData], { type: mimeTypeStr })
+ const hrefUrl = window.URL.createObjectURL(blob) // 创建下载的链接
+ downloadFile(hrefUrl, fileName);
+}
+
+//将base64转换为文件
+export function base64Tofile(dataurl:any,fileName:any){
+ console.log(dataurl,fileName);
+ // debugger;
+ let arr = dataurl.split(','),
+ mime = arr[0].match(/:(.*?);/)[1],
+ bstr = atob(arr[1]),
+ n = bstr.length,
+ u8arr = new Uint8Array(n);
+ while (n--) {
+ u8arr[n] = bstr.charCodeAt(n);
+ }
+ const blob =new Blob([u8arr], { type: mime });
+ // @ts-ignore
+ blob.lastModifiedDate = new Date();
+ // @ts-ignore
+ blob.name = fileName;
+
+ console.log(blob,'blob');
+ download(blob)
+ // window.location.replace(blobUrl)
+
+ return blob;
+}
+
+
+export function download(downfile:any, fileName?: string) {
+ const tmpLink = document.createElement("a");
+ const objectUrl = URL.createObjectURL(downfile);
+
+ tmpLink.href = objectUrl;
+ tmpLink.target = '_blank' // 新窗口打开 20250320 yjc 新增
+ tmpLink.download = fileName || downfile.name;
+ document.body.appendChild(tmpLink);
+ tmpLink.click();
+
+ document.body.removeChild(tmpLink);
+ URL.revokeObjectURL(objectUrl);
+}
+
+// 生成唯一的uuid
+export function getUUID(randomLength:any) {
+ function S4() {
+ return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
+ }
+ return ("p"+S4() + S4() + S4() + S4() + S4() + S4() + S4() + S4())
+}
+
+export function downloadWav(base64Data:any,name:any) {
+ // const tmpLink = document.createElement("a");
+ // const objectUrl = URL.createObjectURL(downfile);
+
+ // tmpLink.href = objectUrl;
+ // tmpLink.download = downfile.name;
+ // document.body.appendChild(tmpLink);
+ // tmpLink.click();
+
+ // document.body.removeChild(tmpLink);
+ // URL.revokeObjectURL(objectUrl);
+ const downloadLink = document.createElement('a');
+ downloadLink.href = base64Data; // 这里的 base64Data 是之前转换得到的 Base64 字符串
+ downloadLink.download = name; // 设置下载文件的名称
+ downloadLink.click(); // 模拟点击下载链接
+ }
+
+export function saveDatatoJson(data:any, filename:any) {
+ if (!data) {
+ console.error('Console.save: No data')
+ return;
+ }
+ if (!filename) filename = 'console.json'
+ if (typeof data === "object") {
+ data = JSON.stringify(data, undefined, 4)
+ }
+ const blob = new Blob([data], {
+ type: 'text/json'
+ }),
+ e = document.createEvent('MouseEvents'),
+ a = document.createElement('a')
+ a.download = filename
+ a.href = window.URL.createObjectURL(blob)
+ a.dataset.downloadurl = ['text/json', a.download, a.href].join(':')
+ e.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null)
+ a.dispatchEvent(e)
+}
+// file 文件转 base64
+export function fileToBase64(file:any) {
+ return new Promise((resolve, reject) => {
+ const reader = new FileReader();
+ reader.readAsDataURL(file);
+ reader.onload = () => {
+ resolve(reader.result);
+ };
+ reader.onerror = (error) => {
+ reject(error);
+ };
+ });
+}
+// file 文件转换成 字节流
+export function fileToBytes(file:any) {
+ return new Promise((resolve, reject) => {
+ const reader = new FileReader();
+ reader.readAsArrayBuffer(file);
+ reader.onload = () => {
+ resolve(reader.result);
+ };
+ reader.onerror = (error) => {
+ reject(error);
+ };
+ });
+}
+
+// base64转文件
+export function base64ToFile(base64Data: string, fileName: string, mimeType?: string): File {
+ // 从base64字符串中提取数据部分和MIME类型
+ let dataType = '';
+ let base64Content = '';
+
+ if (base64Data.includes(';base64,')) {
+ const parts = base64Data.split(';base64,');
+ dataType = parts[0].split(':')[1];
+ base64Content = parts[1];
+ } else {
+ // 如果base64字符串不包含MIME信息,则使用传入的mimeType
+ dataType = mimeType || 'application/octet-stream';
+ base64Content = base64Data;
+ }
+
+ // 解码base64
+ const byteCharacters = atob(base64Content);
+ const byteArrays = [];
+
+ // 将字符串转换为字节数组
+ for (let i = 0; i < byteCharacters.length; i++) {
+ byteArrays.push(byteCharacters.charCodeAt(i));
+ }
+
+ // 创建Uint8Array
+ const uint8Array = new Uint8Array(byteArrays);
+
+ // 创建Blob对象
+ const blob = new Blob([uint8Array], { type: dataType });
+
+ // 创建File对象
+ return new File([blob], fileName, { type: dataType, lastModified: new Date().getTime() });
+}
+
diff --git a/src/utils/request/index.ts b/src/utils/request/index.ts
new file mode 100644
index 0000000..5cb5bc5
--- /dev/null
+++ b/src/utils/request/index.ts
@@ -0,0 +1,106 @@
+import { Config, RequestHead, ResponseData, ResponseError, RequestParams } from '@/utils/request/types/index.ts'
+import { message } from "ant-design-vue";
+
+// 默认配置
+const createDefaultConfig = (): {
+ headers: RequestHead
+} => {
+ const defaultConfig = {
+ headers: {
+ Accept: 'application/json',
+ 'Content-Type': 'application/json',
+ authorization: `Bearer ${localStorage.token}`
+ },
+ }
+ return defaultConfig
+}
+
+// 构造数据
+const genData = (data: any, dataRaw: boolean) => {
+ if (dataRaw) {
+ return data instanceof FormData || data instanceof URLSearchParams
+ ? data
+ : new URLSearchParams(data);
+ }
+ return JSON.stringify(data);
+};
+
+// 构造返回
+const genResult = (result: ExtraObj, status: 'success' | 'error'): ResponseData | ResponseError => {
+ if (result.code) {
+ return {
+ ...result,
+ code: result.code,
+ data: result.data
+ }
+ } else {
+ if (status == 'success') {
+ return {
+ code: 200,
+ data: result
+ }
+ } else {
+ let error
+ if ('detail' in result) {
+ error = result.detail
+ } else {
+ error = result
+ }
+ return {
+ code: 500,
+ message: error,
+ _raw: result
+ }
+ }
+
+ }
+}
+
+// 构造请求参数
+const createRuestParams = (config: Config) => {
+ const { method, data, dataRaw, headers, controller } = config;
+ const requetParams: RequestParams = {
+ method,
+ headers: headers as HeadersInit ?? createDefaultConfig().headers,
+ body: data instanceof URLSearchParams ? data : genData(data, dataRaw || false),
+ signal: controller?.signal
+ };
+ return requetParams
+}
+
+// 请求
+export const request = async (config: Config): Promise => {
+ const { url, base, params, getRaw } = config;
+ // 路径参数格式化
+ const urlParams = params ? new URLSearchParams(params) : null
+ const extra = `${urlParams ? '?' + urlParams.toString() : ''}`
+ const requestUrl = `${base || import.meta.env.VITE_BASE_API}${url}${extra}`
+ const requestParams = createRuestParams(config)
+
+ // 请求逻辑封装
+ const res = await fetch(requestUrl, requestParams).then(async (res) => {
+ // 是否需要返回原始数据
+ const result = getRaw ? res : await res.json();
+ if (!res.ok) throw result
+ if (result?.code && result?.code !== 200) {
+ throw result
+ }
+ return getRaw ? result : genResult(result, 'success')
+ }).catch((err) => {
+ if (err.name == 'AbortError') {
+ return Promise.reject(err)
+ }
+ let error
+ if ('code' in err) {
+ error = err
+ } else {
+ error = genResult(err, 'error')
+ }
+ if (error.message) {
+ message.error(error.message)
+ }
+ return Promise.reject(error)
+ });
+
+ return res;
+}
diff --git a/src/utils/request/types/index.ts b/src/utils/request/types/index.ts
new file mode 100644
index 0000000..8ad4ad7
--- /dev/null
+++ b/src/utils/request/types/index.ts
@@ -0,0 +1,43 @@
+export interface Config {
+ base?: string, // 前缀
+ url: string // 请求地址
+ method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH'
+ data?: string | Record // 请求体
+ dataRaw?: boolean // 请求体是否为原始数据
+ params?: string | Record | URLSearchParams | string[][] // 请求参数
+ headers?: HeadersInit | undefined // 请求头
+ controller?: AbortController // 请求控制器
+ getRaw?: boolean // 是否需要返回原始数据
+}
+
+// 请求头
+export interface RequestHead {
+ [key: string]: string
+}
+
+// 标准响应
+export interface ResponseData {
+ code: number
+ data: T
+ [key: string]: any
+}
+
+// 错误响应
+export interface ResponseError {
+ code: number
+ message?: string
+ _raw: T
+}
+
+// 请求参数
+export interface RequestParams extends RequestInit {
+ signal?: AbortSignal
+ method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH'
+ headers: HeadersInit
+ body?: BodyInit | null
+ [key: string]: any
+}
+
+
+
+
diff --git a/src/utils/socket/index.ts b/src/utils/socket/index.ts
new file mode 100644
index 0000000..90b0dc0
--- /dev/null
+++ b/src/utils/socket/index.ts
@@ -0,0 +1,19 @@
+import { io } from 'socket.io-client';
+import { Socket } from 'socket.io-client/build/esm/socket'
+// 设置socket链接
+export const socketInit = (enableWebsocket: boolean) => {
+ // const socketUrl = import.meta.env.VITE_SOCKET_URL;
+
+ const _socket: Socket = io(undefined, {
+ reconnection: true,
+ reconnectionDelay: 1000,
+ reconnectionDelayMax: 5000,
+ randomizationFactor: 0.5,
+ path: '/ws/socket.io',
+ transports: enableWebsocket ? ['websocket'] : ['polling', 'websocket'],
+ auth: { token: localStorage.token }
+ });
+ console.log('_socket', _socket)
+ return _socket
+}
+
diff --git a/src/utils/text/index.ts b/src/utils/text/index.ts
new file mode 100644
index 0000000..8f42f2f
--- /dev/null
+++ b/src/utils/text/index.ts
@@ -0,0 +1,48 @@
+
+// 复制文本
+/**
+ * 复制文本到剪贴板
+ * @param text 要复制的文本
+ * @returns 是否复制成功
+ */
+export const copyToClipboard = async (text: string) => {
+ let result = false;
+ if (!navigator.clipboard) {
+ const textArea = document.createElement('textarea');
+ textArea.value = text;
+
+ // Avoid scrolling to bottom
+ textArea.style.top = '0';
+ textArea.style.left = '0';
+ textArea.style.position = 'fixed';
+
+ document.body.appendChild(textArea);
+ textArea.focus();
+ textArea.select();
+
+ try {
+ const successful = document.execCommand('copy');
+ const msg = successful ? 'successful' : 'unsuccessful';
+ console.log('Fallback: Copying text command was ' + msg);
+ result = true;
+ } catch (err) {
+ console.error('Fallback: Oops, unable to copy', err);
+ }
+
+ document.body.removeChild(textArea);
+ return result;
+ }
+
+ result = await navigator.clipboard
+ .writeText(text)
+ .then(() => {
+ console.log('Async: Copying to clipboard was successful!');
+ return true;
+ })
+ .catch((error) => {
+ console.error('Async: Could not copy text: ', error);
+ return false;
+ });
+
+ return result;
+};
diff --git a/src/utils/transition/file.ts b/src/utils/transition/file.ts
new file mode 100644
index 0000000..51effca
--- /dev/null
+++ b/src/utils/transition/file.ts
@@ -0,0 +1,21 @@
+// 文件格式转换
+export const sizeFormat = (
+ number: number | string,
+ start: number = 0,
+ max: number = 999,
+ showUnit: boolean = true,
+ decimal: number = 2
+) => {
+ const unit = ['b', 'Kb', 'Mb', 'Gb', 'Tb']
+ let limitUnitIndex = Math.min(max, unit.length - 1)
+ let unitIndex = start
+ let useNumber = Number(number)
+ console.log('useNumber', useNumber)
+ while (useNumber >= 1024 && unitIndex < limitUnitIndex) {
+ useNumber /= 1024
+ unitIndex++
+ }
+ console.log('useNumber', useNumber)
+
+ return `${Math.ceil(useNumber).toFixed(decimal)}${showUnit ? unit[unitIndex] : ''}`
+}
diff --git a/src/utils/util.ts b/src/utils/util.ts
new file mode 100644
index 0000000..19842cc
--- /dev/null
+++ b/src/utils/util.ts
@@ -0,0 +1,64 @@
+
+export function uuid() {
+ let d = Date.now()
+ if (typeof performance !== 'undefined' && typeof performance.now === 'function') {
+ d += performance.now() // use high-precision timer if available
+ }
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
+ const r = (d + Math.random() * 16) % 16 | 0
+ d = Math.floor(d / 16)
+ return (c === 'x' ? r : (r & 0x3) | 0x8).toString(16)
+ })
+}
+
+/**
+ * 构造树型结构数据
+ * @param {*} data 数据源
+ * @param {string} id id字段 默认 'id'
+ * @param {string} parentId 父节点字段 默认 'parentId'
+ * @param {*} children 孩子节点字段 默认 'children'
+ * @param {string | number} rootId 根Id 默认 0
+ */
+export function handleTree(data: ExtraObj[], id?: string, parentId?: string, children?: string, rootId?: number | string) {
+ const reNameId = id || 'id'
+ const reNameParentId = parentId || 'parentId'
+ const reNameChildren = children || 'children'
+ const reNameRootId =
+ rootId ||
+ Math.min.apply(
+ Math,
+ data.map(item => {
+ return item[reNameParentId]
+ })
+ ) ||
+ 0
+ // 对源数据深度克隆
+ const cloneData = JSON.parse(JSON.stringify(data))
+ // 循环所有项
+ const treeData = cloneData.filter((father: ExtraObj) => {
+ var branchArr = cloneData.filter((child: ExtraObj) => {
+ // 返回每一项的子级数组
+ return father[reNameId] === child[reNameParentId]
+ })
+ if (branchArr.length > 0) {
+ father[reNameChildren] = branchArr
+ } else {
+ father[reNameChildren] = ''
+ }
+ // 返回第一层
+ return father[reNameParentId] == reNameRootId
+ })
+ return treeData !== '' ? treeData : data
+}
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/utils/valid.ts b/src/utils/valid.ts
new file mode 100644
index 0000000..c30fc51
--- /dev/null
+++ b/src/utils/valid.ts
@@ -0,0 +1,23 @@
+// 网址校验
+export const httpReg = /^([hH][tT]{2}[pP]:\/\/|[hH][tT]{2}[pP][sS]:\/\/)(([A-Za-z0-9-~]+)\.)+([A-Za-z0-9-~/])+$/
+
+export function validURL (url?:string) {
+ const reg = /^([hH][tT]{2}[pP]:\/\/|[hH][tT]{2}[pP][sS]:\/\/)(([A-Za-z0-9-~]+)\.)+([A-Za-z0-9-~/])+$/
+ return reg.test(url||'')
+}
+
+// 手机校验
+export const phoneReg = /^1[3456789]\d{9}$/
+
+export const MobReg = /^((0\d{2,3})-)?(\d{7,8})$/
+
+export function validPhone (phone: string) {
+ return phoneReg.test(phone) || MobReg.test(phone)
+}
+
+// 邮箱校验
+export const mailReg = /^[a-zA-Z0-9]+([._\\-]?[a-zA-Z0-9]+)*@[a-zA-Z0-9]+([._\\-]?[a-zA-Z0-9]+)*\.[a-zA-Z]{2,}$/
+
+export function validMail (mail: string) {
+ return mailReg.test(mail)
+}
diff --git a/src/views/dataManage/index/components/Card/ModuleCard.vue b/src/views/dataManage/index/components/Card/ModuleCard.vue
new file mode 100644
index 0000000..b8d333d
--- /dev/null
+++ b/src/views/dataManage/index/components/Card/ModuleCard.vue
@@ -0,0 +1,87 @@
+
+
+
+
+
+
+
+
{{ props.title }}
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/views/dataManage/index/components/Chat/chat.vue b/src/views/dataManage/index/components/Chat/chat.vue
new file mode 100644
index 0000000..fec54d0
--- /dev/null
+++ b/src/views/dataManage/index/components/Chat/chat.vue
@@ -0,0 +1,88 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/views/dataManage/index/components/Modal/ExportModal.vue b/src/views/dataManage/index/components/Modal/ExportModal.vue
new file mode 100644
index 0000000..8712991
--- /dev/null
+++ b/src/views/dataManage/index/components/Modal/ExportModal.vue
@@ -0,0 +1,209 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/views/dataManage/index/components/Modal/ImportDataLabelModa.vue b/src/views/dataManage/index/components/Modal/ImportDataLabelModa.vue
new file mode 100644
index 0000000..1cb0142
--- /dev/null
+++ b/src/views/dataManage/index/components/Modal/ImportDataLabelModa.vue
@@ -0,0 +1,271 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/views/dataManage/index/components/Modal/ImportModal.vue b/src/views/dataManage/index/components/Modal/ImportModal.vue
new file mode 100644
index 0000000..b9a3b6c
--- /dev/null
+++ b/src/views/dataManage/index/components/Modal/ImportModal.vue
@@ -0,0 +1,310 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/views/dataManage/index/components/Module/Chat1.vue b/src/views/dataManage/index/components/Module/Chat1.vue
new file mode 100644
index 0000000..2dc4ee3
--- /dev/null
+++ b/src/views/dataManage/index/components/Module/Chat1.vue
@@ -0,0 +1,155 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/views/dataManage/index/components/Module/Chat2.vue b/src/views/dataManage/index/components/Module/Chat2.vue
new file mode 100644
index 0000000..e78886b
--- /dev/null
+++ b/src/views/dataManage/index/components/Module/Chat2.vue
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/views/dataManage/index/components/Module/DataExport.vue b/src/views/dataManage/index/components/Module/DataExport.vue
new file mode 100644
index 0000000..0904980
--- /dev/null
+++ b/src/views/dataManage/index/components/Module/DataExport.vue
@@ -0,0 +1,487 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ dayjs(text).format("YYYY-MM-DD HH:mm:ss") }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/views/dataManage/index/components/Module/DataImport.vue b/src/views/dataManage/index/components/Module/DataImport.vue
new file mode 100644
index 0000000..95985ad
--- /dev/null
+++ b/src/views/dataManage/index/components/Module/DataImport.vue
@@ -0,0 +1,608 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ dayjs(text).format("YYYY-MM-DD HH:mm:ss") }}
+
+
+
+ {{ text }} MB
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/views/dataManage/index/components/Module/RadarChat.vue b/src/views/dataManage/index/components/Module/RadarChat.vue
new file mode 100644
index 0000000..a522820
--- /dev/null
+++ b/src/views/dataManage/index/components/Module/RadarChat.vue
@@ -0,0 +1,102 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/views/dataManage/index/components/Module/testDataset.vue b/src/views/dataManage/index/components/Module/testDataset.vue
new file mode 100644
index 0000000..9e9db49
--- /dev/null
+++ b/src/views/dataManage/index/components/Module/testDataset.vue
@@ -0,0 +1,416 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{{ index + 1 }}
+
{{ item }}
+
+
+
+
+ {{ text }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ dayjs(text).format("YYYY-MM-DD HH:mm:ss") }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/views/dataManage/index/components/Module/trainDataset.vue b/src/views/dataManage/index/components/Module/trainDataset.vue
new file mode 100644
index 0000000..55ee722
--- /dev/null
+++ b/src/views/dataManage/index/components/Module/trainDataset.vue
@@ -0,0 +1,409 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{{ index + 1 }}
+
{{ item }}
+
+
+
+
+ {{ text }}
+
+
+
+
+ {{ dayjs(text).format("YYYY-MM-DD HH:mm:ss") }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/views/dataManage/index/components/Table/index.vue b/src/views/dataManage/index/components/Table/index.vue
new file mode 100644
index 0000000..fb446d2
--- /dev/null
+++ b/src/views/dataManage/index/components/Table/index.vue
@@ -0,0 +1,344 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ col.title }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ rows[col.dataIndex] }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/views/dataManage/index/config/index.ts b/src/views/dataManage/index/config/index.ts
new file mode 100644
index 0000000..e69de29
diff --git a/src/views/dataManage/index/hooks/circleChat.ts b/src/views/dataManage/index/hooks/circleChat.ts
new file mode 100644
index 0000000..cf9a8a3
--- /dev/null
+++ b/src/views/dataManage/index/hooks/circleChat.ts
@@ -0,0 +1,99 @@
+import { ref } from "vue";
+
+
+
+export const useMain = () => {
+ var colorList = ["#73DDFF", "#73ACFF", "#FDD56A", "#FDB36A", "#FD866A", "#9E87FF", "#58D5FF"];
+
+ const options = ref>({
+ title: {
+ text: `{a|总量}\n{b|0}`,
+ textStyle: {
+ rich: {
+ a: {
+ fontSize: 14,
+ color: "#4E5969",
+ lineHeight: 24,
+ },
+ b: {
+ fontSize: 16,
+ color: "#1D2129",
+ lineHeight: 28,
+ fontWeight: "bold",
+ },
+ },
+ },
+ left: "50%", // 水平居中
+ top: "50%", // 垂直居中
+ textAlign: "center",
+ textVerticalAlign: "middle",
+ },
+ legend: {
+ icon: "circle",
+ bottom: "0",
+ left: "center",
+ itemWidth: 6,
+ itemGap: 20,
+ textStyle: {
+ color: "#556677",
+ },
+ },
+ tooltip: {
+ trigger: "item",
+ },
+ series: [
+ {
+ type: "pie",
+ center: ["50%", "50%"],
+ radius: ["40%", "65%"],
+ clockwise: true,
+ avoidLabelOverlap: true,
+ hoverOffset: 15,
+ // itemStyle: {
+ // normal: {
+ // color: function (params: Record) {
+ // return colorList[params.dataIndex];
+ // },
+ // },
+ // },
+ label: {
+ show: true,
+ position: "outer",
+ formatter: "{a|{b}:}\n{value|{d}}%",
+ distanceToLabelLine: 10,
+ rich: {
+ a: {
+ fontSize: 14,
+ color: "#4E5969",
+ lineHeight: 20,
+ padding: [0, 0, 8, 0],
+ // padding: [-30, 15, -20, 15],
+ },
+ value: {
+ fontSize: 14,
+ color: "#1D2129",
+ lineHeight: 24,
+ },
+ },
+ },
+ labelLine: {
+ normal: {
+ length: 20,
+ length2: 20,
+ lineStyle: {
+ width: 1,
+ },
+ },
+ },
+ data: [],
+ },
+ ],
+ });
+
+ // const options = ref>(t);
+
+ return {
+ options,
+ colorList,
+ };
+};
diff --git a/src/views/dataManage/index/hooks/dataExport.ts b/src/views/dataManage/index/hooks/dataExport.ts
new file mode 100644
index 0000000..8ef9aac
--- /dev/null
+++ b/src/views/dataManage/index/hooks/dataExport.ts
@@ -0,0 +1,77 @@
+import { listApi, updateLabelApi, getExportUrlApi, importLabelByFileApi } from '@/apis/dataManage/dataExport.ts'
+import { ref } from 'vue'
+
+export const useMain = () => {
+ // 列表loading
+ const listLoading = ref(false)
+
+ // 获取列表
+ const getList = async (params?: Record) => {
+ if (listLoading.value) return false
+ listLoading.value = true
+ const res = await listApi(params).catch(err => {
+ return err
+ })
+ listLoading.value = false
+ return res
+ }
+
+
+ // 编辑
+ const updateLabelLoading = ref(false)
+
+ const onUpdateLabel = async (params: Record) => {
+ if (updateLabelLoading.value) return false
+ updateLabelLoading.value = true
+ const res = await updateLabelApi(params).catch(err => {
+ return err
+ })
+ updateLabelLoading.value = false
+ return res
+ }
+
+ // 获取导出地址
+ const exportUrlLoading = ref(false)
+ const getExportUrl = async (params: Record) => {
+ if (exportUrlLoading.value) return false
+ exportUrlLoading.value = true
+ const res = await getExportUrlApi(params).catch(err => {
+ return err
+ })
+ exportUrlLoading.value = false
+ return res
+ }
+
+ // 导入标注
+ const importLabelLoading = ref(false)
+ const importLabelByFile = async (params: Record) => {
+ if (importLabelLoading.value) return false
+ importLabelLoading.value = true
+ const res = await importLabelByFileApi(params).catch(err => {
+ return err
+ })
+ importLabelLoading.value = false
+ return res
+ }
+
+
+
+ return {
+ listRequest: {
+ request: getList,
+ loading: listLoading
+ },
+ updateRequest: {
+ request: onUpdateLabel,
+ loading: updateLabelLoading
+ },
+ exportUrlRequest: {
+ request: getExportUrl,
+ loading: exportUrlLoading
+ },
+ importLabelRequest: {
+ request: importLabelByFile,
+ loading: importLabelLoading
+ }
+ }
+}
diff --git a/src/views/dataManage/index/hooks/dataImport.ts b/src/views/dataManage/index/hooks/dataImport.ts
new file mode 100644
index 0000000..e8235fe
--- /dev/null
+++ b/src/views/dataManage/index/hooks/dataImport.ts
@@ -0,0 +1,78 @@
+import { listApi, deleteApi, importApi, updateApi } from '@/apis/dataManage/dataImport.ts'
+import { ref } from 'vue'
+
+export const useMain = () => {
+ // 列表loading
+ const listLoading = ref(false)
+
+ // 获取列表
+ const getList = async () => {
+ if (listLoading.value) return false
+ listLoading.value = true
+ const res = await listApi().catch(err => {
+ return err
+ })
+ listLoading.value = false
+ return res
+ }
+
+ const importLoading = ref(false)
+
+ // 导入
+ const importFile = async (data: Record) => {
+ if (importLoading.value) return false
+ importLoading.value = true
+ const res = await importApi(data).catch(err => {
+ return err
+ })
+ importLoading.value = false
+ return res
+ }
+
+
+ const deleteLoading = ref(false)
+
+ // 删除
+ const onDelete = async (data: Record) => {
+ if (deleteLoading.value) return false
+ deleteLoading.value = true
+ const res = await deleteApi(data).catch(err => {
+ return err
+ })
+ deleteLoading.value = false
+ return res
+ }
+
+
+ const updateLoading = ref(false)
+
+ // 更新
+ const onUpdate = async (data: Record) => {
+ if (updateLoading.value) return false
+ updateLoading.value = true
+ const res = await updateApi(data).catch(err => {
+ return err
+ })
+ updateLoading.value = false
+ return res
+ }
+
+ return {
+ listRequest: {
+ request: getList,
+ loading: listLoading
+ },
+ importRequest: {
+ request: importFile,
+ loading: importLoading
+ },
+ deleteRequest: {
+ request: onDelete,
+ loading: deleteLoading
+ },
+ updateRequest: {
+ request: onUpdate,
+ loading: updateLoading
+ }
+ }
+}
diff --git a/src/views/dataManage/index/hooks/dataset.ts b/src/views/dataManage/index/hooks/dataset.ts
new file mode 100644
index 0000000..edf4c85
--- /dev/null
+++ b/src/views/dataManage/index/hooks/dataset.ts
@@ -0,0 +1,87 @@
+import { datasetListApi, deleteDatasetApi, updateDatasetApi } from '@/apis/dataset/index.ts'
+
+import { ref } from 'vue'
+
+
+export const useDatasetHooks = () => {
+
+ // 获取测试集loading
+ const testDatasetListLoading = ref(false)
+
+ // 获取测试集
+ const getTestDatasetList = async (params?: Record) => {
+ if (testDatasetListLoading.value) return false
+ testDatasetListLoading.value = true
+ const res = await datasetListApi({
+ ...params || {},
+ type: 'test'
+ }).catch(err => {
+ return err
+ })
+ testDatasetListLoading.value = false
+ return res
+ }
+
+ // 获取训练集loading
+ const trainDatasetListLoading = ref(false)
+
+ // 获取训练集
+ const getTrainDatasetList = async (params?: Record) => {
+ if (trainDatasetListLoading.value) return false
+ trainDatasetListLoading.value = true
+ const res = await datasetListApi({
+ ...params || {},
+ type: 'train'
+ }).catch(err => {
+ return err
+ })
+ trainDatasetListLoading.value = false
+ return res
+ }
+
+ const deleteLoading = ref(false)
+
+ // 删除数据集
+ const deleteRequest = async (id: string) => {
+ if (deleteLoading.value) return false
+ deleteLoading.value = true
+ const res = await deleteDatasetApi(id).catch(err => {
+ return err
+ })
+ deleteLoading.value = false
+ return res
+ }
+
+ // 更新数据集
+ const updateLoading = ref(false)
+
+ const updateRequest = async (params: Record) => {
+ if (updateLoading.value) return false
+ updateLoading.value = true
+ const res = await updateDatasetApi(params).catch(err => {
+ return err
+ })
+ updateLoading.value = false
+ return res
+ }
+
+
+ return {
+ testDatasetListRequest: {
+ request: getTestDatasetList,
+ loading: testDatasetListLoading
+ },
+ trainDatasetListRequest: {
+ request: getTrainDatasetList,
+ loading: trainDatasetListLoading
+ },
+ deleteRequest: {
+ request: deleteRequest,
+ loading: deleteLoading
+ },
+ updateRequest: {
+ request: updateRequest,
+ loading: updateLoading
+ }
+ }
+}
diff --git a/src/views/dataManage/index/hooks/lineChat.ts b/src/views/dataManage/index/hooks/lineChat.ts
new file mode 100644
index 0000000..50a3159
--- /dev/null
+++ b/src/views/dataManage/index/hooks/lineChat.ts
@@ -0,0 +1,240 @@
+import { ref } from 'vue'
+
+interface SeriesOptions {
+ title: string,
+ color?: string,
+ data: number[],
+ lineColor?: string
+}
+
+
+export const useMain = () => {
+ const colorList = ["#9E87FF", "#73DDFF", "#fe9a8b", "#F56948", "#9E87FF"];
+
+ // 构建serise配置
+ const createSeriesOptions = (options: SeriesOptions) => {
+ const item = {
+ name: options.title,
+ type: "line",
+ data: options.data,
+ symbolSize: 1,
+ symbol: "circle",
+ smooth: true,
+ showSymbol: false,
+ lineStyle: {
+ width: 2,
+ // color: options.lineColor,
+ // shadowColor: "rgba(158,135,255, 0.3)",
+ // shadowBlur: 10,
+ // shadowOffsetY: 20,
+ },
+ // itemStyle: {
+ // normal: {
+ // color: options.lineColor,
+ // borderColor: options.lineColor,
+ // },
+ // },
+ }
+ return item
+ }
+
+ // tooltip格式化
+ const tooltipFormat = (params: Array>) => {
+ // 标题部分 - 黑色
+ let result = `${params[0].axisValue}
`;
+
+ // 每项数据
+ params.forEach((item: Record, index: number) => {
+ const marginBottom = index < params.length - 1 ? 'margin-bottom:4px;' : '';
+ result += `
+
+
+ ${item.marker}
+ ${item.seriesName}:
+
+
+ ${item.value}
+
+
+ `;
+ });
+ return result;
+ }
+
+ const options = ref>({
+ backgroundColor: "rgba(255, 255, 255, 0)",
+ title: {
+ text: "",
+ textStyle: {
+ fontSize: 12,
+ fontWeight: 400,
+ },
+ left: "center",
+ top: "5%",
+ },
+ legend: {
+ icon: "circle",
+ bottom: "0",
+ left: "center",
+ itemWidth: 6,
+ itemGap: 20,
+ textStyle: {
+ color: "#556677",
+ },
+ },
+
+ tooltip: {
+ trigger: 'axis',
+ axisPointer: {
+ type: 'none'
+ },
+ borderColor: 'transparent',
+ borderWidth: 0,
+ padding: [10, 15],
+ textStyle: {
+ color: '#333',
+ fontSize: 14,
+ fontWeight: 'bold'
+ },
+ extraCssText: 'border-radius: 6px; background: rgba(255, 255, 255, 0.2); backdrop-filter: blur(10px)',
+ },
+ grid: {
+ top: "15%",
+ },
+ xAxis: [
+ {
+ type: "category",
+ data: [],
+ axisLine: {
+ lineStyle: {
+ color: "#A9AEB8",
+ },
+ },
+ axisTick: {
+ show: true,
+ color: '#A9AEB8'
+ },
+ axisLabel: {
+ // interval: 0,
+ textStyle: {
+ color: "#556677",
+ },
+ // 默认x轴字体大小
+ fontSize: 12,
+ // margin:文字到x轴的距离
+ margin: 15,
+ },
+ axisPointer: {
+ label: {
+ // padding: [11, 5, 7],
+ padding: [0, 0, 10, 0],
+ margin: 15,
+ fontSize: 12,
+ backgroundColor: {
+ type: "linear",
+ x: 0,
+ y: 0,
+ x2: 0,
+ y2: 1,
+ colorStops: [
+ {
+ offset: 0,
+ color: "#fff", // 0% 处的颜色
+ },
+ {
+ offset: 0.86,
+ color: "#fff", // 0% 处的颜色
+ },
+ {
+ offset: 0.86,
+ color: "#33c0cd", // 0% 处的颜色
+ },
+ {
+ offset: 1,
+ color: "#33c0cd", // 100% 处的颜色
+ },
+ ],
+ global: false, // 缺省为 false
+ },
+ },
+ },
+ boundaryGap: false,
+ },
+ ],
+ yAxis: [
+ {
+ type: "value",
+ axisTick: {
+ show: false,
+ },
+ axisLine: {
+ show: false,
+ lineStyle: {
+ color: "#333",
+ },
+ },
+ axisLabel: {
+ textStyle: {
+ color: "#556677",
+ },
+ },
+ splitLine: {
+ show: true,
+ color: '#E5E6EB'
+ },
+ },
+ ],
+ series: [
+ // {
+ // name: "老北京布鞋",
+ // type: "line",
+ // data: [150, 120, 170, 140, 500, 160, 110],
+ // symbolSize: 1,
+ // symbol: "circle",
+ // smooth: true,
+ // showSymbol: false,
+ // lineStyle: {
+ // width: 5,
+ // color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [
+ // {
+ // offset: 0,
+ // color: "#fe9a",
+ // },
+ // {
+ // offset: 1,
+ // color: "#fe9a8b",
+ // },
+ // ]),
+ // shadowColor: "rgba(254,154,139, 0.3)",
+ // shadowBlur: 10,
+ // shadowOffsetY: 20,
+ // },
+ // itemStyle: {
+ // normal: {
+ // color: colorList[2],
+ // borderColor: colorList[2],
+ // },
+ // },
+ // },
+ ]
+ })
+
+
+ return {
+ colorList,
+ tooltipFormat,
+ createSeriesOptions,
+ options
+ }
+};
+
+
diff --git a/src/views/dataManage/index/hooks/mock.ts b/src/views/dataManage/index/hooks/mock.ts
new file mode 100644
index 0000000..3988fda
--- /dev/null
+++ b/src/views/dataManage/index/hooks/mock.ts
@@ -0,0 +1,25 @@
+export const useMain = () => {
+ const generatorList = (template: Record, number: number) => {
+ const list = []
+ for (let i = 0; i < number; i++) {
+ const item = { ...template }
+ item.id = `a${i}`
+ item.name = `name ${i}`
+ list.push(item)
+ }
+ return list
+ }
+
+ const sleep = async (time: number) => {
+ return new Promise((resolve) => {
+ setTimeout(() => {
+ resolve(true)
+ }, time)
+ })
+ }
+
+ return {
+ generatorList,
+ sleep
+ }
+}
diff --git a/src/views/dataManage/index/hooks/options.ts b/src/views/dataManage/index/hooks/options.ts
new file mode 100644
index 0000000..e69de29
diff --git a/src/views/dataManage/index/hooks/radarChat.ts b/src/views/dataManage/index/hooks/radarChat.ts
new file mode 100644
index 0000000..a9fef5e
--- /dev/null
+++ b/src/views/dataManage/index/hooks/radarChat.ts
@@ -0,0 +1,335 @@
+import { ref, Ref, onMounted, onUnmounted } from "vue";
+import * as echarts from "echarts";
+
+/**
+ * 雷达图扫描区域功能实现
+ * 提供30度扇形扫描区域,带动画旋转和渐变效果
+ */
+export const useMain = () => {
+ // 主色调定义
+ const primaryColor = "#509e38";
+ const splitLineColor = "rgba(80, 158, 56, 0.7)"; // 主色调的70%透明度
+ // 扫描动画相关变量
+ let animationTimer: number | null = null;
+ let currentAngle = 0;
+
+ // 动画配置参数
+ const animationConfig = {
+ rotationSpeed: -2, // 每次旋转的角度
+ interval: 16, // 旋转间隔时间(毫秒)
+ scanAngle: 45, // 扫描扇形角度
+ isRunning: false // 动画运行状态标记
+ };
+
+ // 自定义数据提示框格式化函数
+ const tooltipFormat = (params: Array>) => {
+ // 标题部分 - 黑色
+ let result = `${params[0].axisValue}
`;
+
+ // 每项数据
+ params.forEach((item: Record, index: number) => {
+ const { angle, dis, height } = item.data?._rawData || {};
+ if (!item.data?._rawData) return;
+ const marginBottom = index < params.length - 1 ? "margin-bottom:4px;" : "";
+ result += `
+
+
+ ${item.marker}
+ ${item.seriesName}
+
+
+ 角度: ${angle.toFixed(2)}°
+ 距离: ${(dis / 1000).toFixed(2)}km
+ 高度: ${(height / 1000).toFixed(2)}km
+
+
+ `;
+ });
+ return result;
+ };
+
+ // ECharts配置项
+ const options = ref>({
+ title: {
+ text: "",
+ },
+ legend: {
+ show: false,
+ },
+ polar: {},
+ tooltip: {
+ trigger: "axis",
+ axisPointer: {
+ type: "cross",
+ crossStyle: {
+ color: primaryColor,
+ },
+ angleAxis: {
+ type: "line",
+ },
+ radiusAxis: {
+ type: "line",
+ },
+ },
+ formatter: tooltipFormat,
+ },
+ angleAxis: {
+ type: "value",
+ min: 0,
+ max: 360,
+ interval: 20,
+ sliptNumber: 360,
+ axisTick: {
+ show: false,
+ },
+ axisLabel: {
+ show: true,
+ fontSize: 12,
+ color: "#333",
+ },
+ axisLine: {
+ show: true,
+ lineStyle: {
+ color: primaryColor,
+ width: 3,
+ },
+ },
+ splitLine: {
+ show: true,
+ lineStyle: {
+ color: splitLineColor,
+ width: 1,
+ type: "solid",
+ },
+ },
+ },
+ radiusAxis: {
+ type: "value",
+ min: 0,
+ max: 0,
+ splitNumber: 3,
+ axisLine: {
+ show: false,
+ },
+ axisTick: {
+ show: false,
+ },
+ axisLabel: {
+ show: false,
+ formatter: (value: number) => value.toFixed(2),
+ fontSize: 12,
+ color: "#333",
+ },
+ splitLine: {
+ show: true,
+ lineStyle: {
+ color: splitLineColor,
+ width: 1,
+ type: "solid",
+ },
+ },
+ },
+ series: [
+ // 主要数据点系列
+ {
+ coordinateSystem: "polar",
+ name: "",
+ animation: false,
+ zlevel: 20,
+ symbolSize: 15,
+ label: {
+ normal: {
+ show: true,
+ formatter: "{b}",
+ position: "inside",
+ textStyle: {
+ color: "#fff",
+ fontSize: 15,
+ },
+ },
+ },
+ type: "scatter",
+ data: [],
+ },
+ // 扫描区域系列 - 使用饼图实现30度扇形
+ {
+ type: "pie",
+ radius: ["0%", "80%"],
+ startAngle: 0,
+ zlevel: 10,
+ animation: false,
+ legend: {
+ show: false,
+ },
+ tooltip: {
+ show: false,
+ },
+ label: {
+ show: false,
+ },
+ labelLine: {
+ show: false,
+ },
+ data: [
+ // 30度扫描扇形
+ {
+ value: animationConfig.scanAngle, // 30度扇形区域
+ name: "扫描区",
+ itemStyle: {
+ color: {
+ type: "linear",
+ x: 0.5 - Math.cos(animationConfig.scanAngle / 2 * Math.PI / 180) * 0.5, // animationConfig.scanAngle / 2度扇形的中线角度
+ y: 0.5 - Math.sin(animationConfig.scanAngle / 2 * Math.PI / 180) * 0.5,
+ x2: 0.5 + Math.cos(animationConfig.scanAngle / 2 * Math.PI / 180) * 0.5,
+ y2: 0.5 + Math.sin(animationConfig.scanAngle / 2 * Math.PI / 180) * 0.5,
+ colorStops: [
+ { offset: 0, color: "rgba(128, 251, 128, 1)" }, // 左侧完全不透明
+ { offset: 1, color: "rgba(80, 158, 56, 0)" } // 右侧完全透明
+ ],
+ global: false
+ }
+ }
+ },
+ // 剩余330度透明区域
+ {
+ value: 360 - animationConfig.scanAngle,
+ itemStyle: {
+ color: 'transparent' // 透明,只显示30度的扫描扇形
+ }
+ }
+ ],
+ },
+ ],
+ });
+
+ /**
+ * 创建系列数据
+ * @param data 原始数据数组
+ * @returns 处理后的ECharts数据格式
+ */
+ const createSeriesData = (data: Array>) => {
+ const points = data.map((item) => {
+ const itemStyle = {
+ color: {
+ type: "radial",
+ x: 0.5,
+ y: 0.5,
+ r: 0.5,
+ colorStops: [
+ { offset: 0, color: "#80fb80" },
+ { offset: 0.5, color: "rgba(80, 158, 56, 0.7)" },
+ { offset: 1, color: "rgba(80, 158, 56, 0)" },
+ ],
+ global: false,
+ },
+ };
+ const point = {
+ value: [item.radius.toFixed(2), item.angle.toFixed(2)],
+ _rawData: item,
+ itemStyle: itemStyle,
+ };
+ return point;
+ });
+ return points;
+ };
+
+
+
+ /**
+ * 启动扫描动画
+ * 使30度扇形区域以指定速度旋转
+ */
+ const startScanAnimation = (echartsRef: Ref) => {
+ // 清除已有的定时器
+ if (animationTimer) {
+ clearInterval(animationTimer);
+ }
+
+ animationConfig.isRunning = true;
+
+ // 创建新的定时器
+ animationTimer = window.setInterval(() => {
+ if (!echartsRef.value) return;
+ // 更新当前角度,确保在0-360范围内
+ currentAngle = (currentAngle + animationConfig.rotationSpeed) % 360;
+ if (echartsRef.value) {
+ // 计算扇形中线角度,用于渐变方向
+ const gradientAngle = currentAngle + (animationConfig.scanAngle / 2);
+
+ // 局部更新扫描区域的起始角度和渐变方向
+ echartsRef.value.setOption({
+ series: [
+ {}, // 第一个系列保持不变
+ {
+ startAngle: currentAngle,
+ data: [
+ {
+ value: animationConfig.scanAngle,
+ itemStyle: {
+ color: {
+ type: "linear",
+ x: 0.5 - Math.cos(gradientAngle * Math.PI / 180) * 0.5,
+ y: 0.5 - Math.sin(gradientAngle * Math.PI / 180) * 0.5,
+ x2: 0.5 + Math.cos(gradientAngle * Math.PI / 180) * 0.5,
+ y2: 0.5 + Math.sin(gradientAngle * Math.PI / 180) * 0.5,
+ colorStops: [
+ { offset: 0, color: "rgba(128, 251, 128, 1)" },
+ { offset: 1, color: "rgba(80, 158, 56, 0)" }
+ ],
+ global: false
+ }
+ }
+ },
+ { value: 360 - animationConfig.scanAngle, itemStyle: { color: 'transparent' } }
+ ]
+ }
+ ]
+ }, false); // 第二个参数设为 false,禁止合并动画
+ }
+ }, animationConfig.interval);
+ };
+
+ /**
+ * 停止扫描动画
+ */
+ const stopScanAnimation = () => {
+ if (animationTimer) {
+ clearInterval(animationTimer);
+ animationTimer = null;
+ animationConfig.isRunning = false;
+ }
+ };
+
+ /**
+ * 设置动画速度
+ * @param speed 新的旋转速度
+ */
+ // const setAnimationSpeed = (speed: number) => {
+ // animationConfig.rotationSpeed = speed;
+ // // 如果动画正在运行,重新启动以应用新速度
+ // if (animationConfig.isRunning) {
+ // startScanAnimation();
+ // }
+ // };
+
+
+
+
+ return {
+ options,
+ createSeriesData,
+ tooltipFormat,
+ startScanAnimation,
+ stopScanAnimation,
+ // setAnimationSpeed
+ };
+};
diff --git a/src/views/dataManage/index/hooks/statistics.ts b/src/views/dataManage/index/hooks/statistics.ts
new file mode 100644
index 0000000..9fe83f8
--- /dev/null
+++ b/src/views/dataManage/index/hooks/statistics.ts
@@ -0,0 +1,52 @@
+
+import { statisticsApi, lineStatisticsApi } from '@/apis/dataManage/statistics.ts'
+import { ref } from 'vue'
+
+export const useMain = () => {
+ // 列表loading
+ const loading = ref(false)
+
+ // 获取统计
+ const getStatistics = async () => {
+ if (loading.value) return false
+ loading.value = true
+ const res = await statisticsApi().catch(err => {
+ return err
+ })
+ loading.value = false
+ return res
+ }
+
+ const lineLoading = ref(false)
+ let lineController: undefined | AbortController = undefined
+ const getLineStatistics = async (batchId: string) => {
+ if (lineLoading.value) return false
+ lineController = new AbortController()
+ lineLoading.value = true
+ const res = await lineStatisticsApi(batchId, lineController).catch(err => {
+ return err
+ }).finally(() => {
+ lineController = undefined
+ })
+ lineLoading.value = false
+ return res
+ }
+
+ const onLineStatisticsInterrupt = () => {
+ if (lineController) lineController.abort()
+ }
+
+
+
+ return {
+ statisticsRequest: {
+ request: getStatistics,
+ loading: loading
+ },
+ lineStatisticsRequest: {
+ request: getLineStatistics,
+ loading: lineLoading,
+ interrupt: onLineStatisticsInterrupt
+ }
+ }
+}
diff --git a/src/views/dataManage/index/index.vue b/src/views/dataManage/index/index.vue
new file mode 100644
index 0000000..0983f81
--- /dev/null
+++ b/src/views/dataManage/index/index.vue
@@ -0,0 +1,161 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/views/dataManage/index/services/dataExport.ts b/src/views/dataManage/index/services/dataExport.ts
new file mode 100644
index 0000000..762ff28
--- /dev/null
+++ b/src/views/dataManage/index/services/dataExport.ts
@@ -0,0 +1,91 @@
+import { useMain as useDataExportMain } from '@/views/dataManage/index/hooks/dataExport.ts'
+import TemplateFile from '@/assets/files/dataExport/导入标注模板文件.csv?url'
+import { downloadFile } from '@/utils/download/index.ts'
+import _ from "lodash";
+import { ref } from 'vue'
+
+export const useMain = () => {
+
+ const { listRequest, updateRequest, exportUrlRequest, importLabelRequest } = useDataExportMain()
+
+ const exportData = ref>([])
+ const exportListParams = ref({
+ date_str: null,
+ label_info: null,
+ track_id: null
+ })
+
+ const createListParams = () => {
+ const raw = exportListParams.value
+ const params = {} as Record
+ if (raw.date_str) params.date_str = raw.date_str
+ if (raw.label_info) params.label_info = raw.label_info
+ if (raw.track_id) params.label_info = raw.track_id
+ // if (raw)
+ return params
+ }
+
+ // 获取导出数据
+ const getExportData = async (params?: Record) => {
+ const customParams = params ?? createListParams()
+ const res = await listRequest.request(customParams)
+ if (res?.code == 200) {
+ exportData.value = res.data
+ console.log('exportData', exportData)
+ }
+ return res
+ }
+
+ const onExportDataSearch = _.debounce((params?: Record) => {
+ getExportData(params)
+ }, 1000)
+
+ const onUpdateLabel = async (params: Record) => {
+ const res = await updateRequest.request(params)
+ // if (res?.code == 200) {
+ // exportData.value = res.data
+ // console.log('exportData', exportData)
+ // }
+ return res
+ }
+
+ // 获取导出文件地址
+ const getExportFileUrl = async (params: Record) => {
+ const res = await exportUrlRequest.request(params)
+ return res
+ }
+
+ // 下载导入标注数据模板
+ const onDownloadLabelTemplate = () => {
+ downloadFile(TemplateFile, '导入标注模板文件.csv')
+ }
+
+ // 导入标注数据
+ const onImportLabel = async (params: Record) => {
+ const res = await importLabelRequest.request(params)
+ return res
+ }
+
+ const state = {
+ exportListParams: exportListParams,
+ exportData: exportData
+ }
+
+ return {
+ state,
+ api: {
+ getExportData,
+ onExportDataSearch,
+ onUpdateLabel,
+ onDownloadLabelTemplate,
+ getExportFileUrl,
+ onImportLabel
+ },
+ requestMap: {
+ listRequest,
+ updateRequest,
+ exportUrlRequest,
+ importLabelRequest
+ }
+ }
+}
diff --git a/src/views/dataManage/index/services/dataImport.ts b/src/views/dataManage/index/services/dataImport.ts
new file mode 100644
index 0000000..bdc2e63
--- /dev/null
+++ b/src/views/dataManage/index/services/dataImport.ts
@@ -0,0 +1,59 @@
+import { useMain as useDataImportMain } from '@/views/dataManage/index/hooks/dataImport.ts'
+import { ref } from 'vue'
+
+export const useMain = () => {
+
+ const { listRequest, importRequest, deleteRequest, updateRequest } = useDataImportMain()
+
+ const importData = ref([])
+
+ // 获取导入数据
+ const getImportData = async () => {
+ const res = await listRequest.request()
+ console.log('res', res)
+ if (res?.code == 200) {
+ importData.value = res.data
+ }
+ return res
+ }
+
+ // 导入数据
+ const onImportData = async (data: Record) => {
+ const res = await importRequest.request(data)
+ console.log('res', res)
+ return res
+ }
+
+ // 删除
+ const onDelete = async (data: Record) => {
+ const res = await deleteRequest.request(data)
+ console.log('res', res)
+ return res
+ }
+
+ // 更新
+ const onUpdate = async (data: Record) => {
+ const res = await updateRequest.request(data)
+ console.log('res', res)
+ return res
+ }
+
+ const state = {
+ importData: importData
+ }
+
+ return {
+ state,
+ api: {
+ getImportData,
+ onDelete,
+ onImportData,
+ onUpdate
+ },
+ requestMap: {
+ listRequest,
+ importRequest,
+ deleteRequest
+ }
+ }
+}
diff --git a/src/views/dataManage/index/services/dataset.ts b/src/views/dataManage/index/services/dataset.ts
new file mode 100644
index 0000000..961cc96
--- /dev/null
+++ b/src/views/dataManage/index/services/dataset.ts
@@ -0,0 +1,60 @@
+import { useDatasetHooks } from '@/views/dataManage/index/hooks/dataset.ts'
+import { ref } from 'vue'
+
+export const useDatasetServer = () => {
+ const { testDatasetListRequest, trainDatasetListRequest, deleteRequest, updateRequest } = useDatasetHooks()
+
+ const testDataset = ref([])
+ const getTestDataset = async () => {
+ const res = await testDatasetListRequest.request()
+ if (res?.code == 200) {
+ testDataset.value = res.data?.datasets
+ }
+ return res
+ }
+
+ const trainDataset = ref([])
+ const getTrainDataset = async () => {
+ const res = await trainDatasetListRequest.request()
+ if (res?.code == 200) {
+ trainDataset.value = res.data?.datasets
+ }
+ return res
+ }
+
+ const deleteDataset = async (id: string) => {
+ const res = await deleteRequest.request(id)
+ return res
+ }
+
+ const updateDataset = async (params: Record) => {
+ const res = await updateRequest.request(params)
+ return res
+ }
+
+
+ const state = {
+ testDataset,
+ trainDataset,
+ }
+
+ const api = {
+ getTestDataset,
+ getTrainDataset,
+ deleteDataset,
+ updateDataset
+ }
+
+ const requestMap = {
+ testDatasetListRequest,
+ trainDatasetListRequest,
+ deleteRequest,
+ updateRequest
+ }
+
+ return {
+ state,
+ api,
+ requestMap,
+ }
+}
diff --git a/src/views/dataManage/index/services/index.ts b/src/views/dataManage/index/services/index.ts
new file mode 100644
index 0000000..bad2c17
--- /dev/null
+++ b/src/views/dataManage/index/services/index.ts
@@ -0,0 +1,22 @@
+import { useMain as useDataServicesMain } from '@/views/dataManage/index/services/dataImport.ts'
+import { useMain as useStatisticsServicesMain } from '@/views/dataManage/index/services/statistics.ts'
+import { useMain as useDataExportServicesMain } from '@/views/dataManage/index/services/dataExport.ts'
+import { useDatasetServer } from '@/views/dataManage/index/services/dataset.ts'
+
+const dataImportServices = useDataServicesMain()
+const statisticsServices= useStatisticsServicesMain()
+const dataExportServices = useDataExportServicesMain()
+const datasetServers = useDatasetServer()
+
+export const useMain = () => {
+ const dataManageServices = {
+ common: {}, // 公共状态模块
+ dataImport: dataImportServices, // 数据导入模块
+ dataExport: dataExportServices, // 数据导出模块
+ statistics: statisticsServices, // 统计数据模块
+ dataset: datasetServers, // 数据集模块
+ }
+ return {
+ dataManageServices,
+ }
+}
diff --git a/src/views/dataManage/index/services/statistics.ts b/src/views/dataManage/index/services/statistics.ts
new file mode 100644
index 0000000..a00c2e8
--- /dev/null
+++ b/src/views/dataManage/index/services/statistics.ts
@@ -0,0 +1,210 @@
+import { useMain as useStatisticsMain } from "@/views/dataManage/index/hooks/statistics.ts";
+import { useMain as useCircleChatMain } from "@/views/dataManage/index/hooks/circleChat.ts";
+import { useMain as useLineChatMain } from "@/views/dataManage/index/hooks/lineChat.ts";
+import { useMain as useDataExportMain } from "@/views/dataManage/index/hooks/dataExport.ts";
+import { useMain as useRadarChatMain } from "@/views/dataManage/index/hooks/radarChat.ts";
+
+import { ref } from "vue";
+
+export const useMain = () => {
+ const { options: circleChatOptions } = useCircleChatMain();
+ const {
+ listRequest: exportDataListRequest,
+ updateRequest,
+ exportUrlRequest,
+ } = useDataExportMain();
+
+ const {
+ options: lineChatOptions,
+ createSeriesOptions: createLineSeriesOptions,
+ tooltipFormat,
+ } = useLineChatMain();
+
+ const {
+ options: radarChatOptions,
+ createSeriesData: createRadarSeriesData,
+ tooltipFormat: radarTooltipFormat,
+ startScanAnimation: radarStartScanAnimation,
+ stopScanAnimation: radarStopScanAnimation,
+ } = useRadarChatMain();
+
+ const { statisticsRequest, lineStatisticsRequest } = useStatisticsMain();
+ const { lineStatisticsRequest: radarStatisticsRequest } = useStatisticsMain();
+
+ const statisticsData = ref({});
+
+ // 获取统计数据数据
+ const getStatisticsData = async () => {
+ const res = await statisticsRequest.request();
+ console.log("getStatisticsData", res);
+ if (res?.code == 200) {
+ statisticsData.value = res.data;
+ }
+ return res;
+ };
+
+ // 更新环型图表配置
+ const updateCircleChatOptions = (chatData: Record) => {
+ const { batch_count, hrrp_by_model, model_count } = chatData;
+ circleChatOptions.value.title.text = `{a|批次}\n{b|${batch_count}}`;
+ const showData: Record[] = [];
+ for (const plane in hrrp_by_model) {
+ showData.push({
+ name: `${plane}`,
+ value: hrrp_by_model[plane],
+ });
+ }
+ circleChatOptions.value = {
+ ...circleChatOptions.value,
+ title: {
+ ...circleChatOptions.value.title,
+ text: `{a|批次}\n{b|${batch_count}}`,
+ },
+ series: [
+ {
+ ...circleChatOptions.value.series[0],
+ data: showData,
+ },
+ ],
+ };
+ };
+
+ // 根据batchId获取折线图、雷达图统计数据
+ const lineStatisticsData = ref({});
+ const getLineStaticsData = async (batchId: string) => {
+ const res = await lineStatisticsRequest.request(batchId);
+ console.log("getLineStaticsData", res);
+ if (res.code == 200) {
+ lineStatisticsData.value = res.data;
+ }
+ };
+
+ // 折线图
+ const updateLineChatOptions = (chatData: Record) => {
+ if (!chatData || !chatData.hrrp) return;
+ const { hrrp } = chatData;
+ console.log("hrrp", hrrp);
+ // 获取hrrp数组的子数组里最长的长度
+ const maxLen = Math.max(...hrrp.map((item: number[] | string[]) => item.length));
+ // 获取最值
+ const maxValue = Math.max(...hrrp.flat(Infinity));
+ const xPoint = 10;
+ const xSide = Math.ceil(maxLen / xPoint);
+ const xAxis = [];
+ for (let i = 0; i < maxLen + 1; i++) {
+ xAxis.push(i);
+ }
+ lineChatOptions.value.xAxis[0].data = xAxis;
+ const series = hrrp.map((item: number[]) => {
+ const seriesItem = createLineSeriesOptions({
+ title: "",
+ data: [...item],
+ });
+ return seriesItem;
+ });
+ lineChatOptions.value.yAxis[0].name = "幅度";
+ lineChatOptions.value.series = series;
+ lineChatOptions.value = {
+ ...lineChatOptions.value,
+ };
+ console.log("lineChatOptions", lineChatOptions.value);
+ };
+
+ const onUpdateLineChat = async (batchId: string) => {
+ await getLineStaticsData(batchId);
+ updateLineChatOptions(lineStatisticsData.value);
+ };
+
+ // 根据batchId获取雷达图统计数据
+ const radarStatisticsData = ref({});
+ const getRadatStaticsData = async (batchId: string) => {
+ const res = await radarStatisticsRequest.request(batchId);
+ if (res.code == 200) {
+ radarStatisticsData.value = res.data;
+ }
+ };
+
+ // 雷达图
+ const updateRadarChatOptions = (chatData: Record) => {
+ if (!chatData || !chatData.dis) return;
+ const { dis, height, azimuth } = chatData;
+ console.log("chatData", chatData);
+ const pointKey = "dis";
+ const pointData = chatData[pointKey] || [];
+ const maxPoint = Math.max(...pointData);
+ radarChatOptions.value.radiusAxis.max = Math.ceil(maxPoint / 10) * 10;
+ const showData = [];
+ for (let i = 0; i < azimuth.length; i++) {
+ showData.push({
+ radius: pointData[i],
+ angle: azimuth[i] * (180 / Math.PI),
+ azimuth: azimuth[i],
+ dis: dis[i],
+ height: height[i],
+ // value: [pointData[i], azimuth[i] * (180/Math.PI)],
+ // name: "",
+ });
+ }
+ const seriesData = createRadarSeriesData(showData);
+ console.log("seriesData", seriesData);
+ radarChatOptions.value.series[0].data = seriesData;
+ radarChatOptions.value = {
+ ...radarChatOptions.value,
+ };
+ };
+
+ const onUpdateRadarChat = async (batchId: string) => {
+ await getRadatStaticsData(batchId);
+ updateRadarChatOptions(radarStatisticsData.value);
+ };
+
+ // 更新图表
+ const onUpdateChat = async () => {
+ await getStatisticsData();
+ updateCircleChatOptions(statisticsData.value);
+ };
+
+ const exportData = ref>([]);
+ const exportListParams = ref({});
+
+ // 获取导出数据
+ const getExportData = async () => {
+ const res = await exportDataListRequest.request();
+ if (res?.code == 200) {
+ exportData.value = res.data;
+ }
+ return res;
+ };
+
+ const state = {
+ statisticsData: statisticsData,
+ circleChatOptions,
+ lineStatisticsData: lineStatisticsData,
+ lineChatOptions,
+ radarStatisticsData: radarStatisticsData,
+ radarChatOptions,
+ exportData: exportData,
+ };
+
+ const api = {
+ getStatisticsData,
+ updateCircleChatOptions,
+ onUpdateChat,
+ onUpdateLineChat,
+ onUpdateRadarChat,
+ radarStartScanAnimation,
+ radarStopScanAnimation,
+ getExportData,
+ };
+
+ return {
+ state,
+ api,
+ requestMap: {
+ statisticsRequest,
+ lineStatisticsRequest,
+ radarStatisticsRequest,
+ exportDataListRequest,
+ },
+ };
+};
diff --git a/src/views/exception/404.vue b/src/views/exception/404.vue
new file mode 100644
index 0000000..23c0bd8
--- /dev/null
+++ b/src/views/exception/404.vue
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
diff --git a/src/views/index/index.vue b/src/views/index/index.vue
new file mode 100644
index 0000000..37e4804
--- /dev/null
+++ b/src/views/index/index.vue
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
diff --git a/src/views/login/hooks/index.ts b/src/views/login/hooks/index.ts
new file mode 100644
index 0000000..c021b89
--- /dev/null
+++ b/src/views/login/hooks/index.ts
@@ -0,0 +1,54 @@
+import { ref } from 'vue'
+
+
+export const useMain = () => {
+ // 表单
+ const formList: Record[] = [
+ {
+ label: '邮箱',
+ key: 'email',
+ icon: 'email',
+ type: 'input',
+ placeholder: '请输入邮箱',
+ rules: [
+ { required: true, message: '请输入邮箱', trigger: 'blur' },
+ ]
+ },
+ {
+ label: '密码',
+ key: 'password',
+ icon: 'lock',
+ type: 'password',
+ placeholder: '请输入密码',
+ rules: [
+ { required: true, message: '请输入密码', trigger: 'blur' },
+ ]
+ }
+ ]
+
+ // 数据
+ const form = ref>({
+ email: '',
+ password: '',
+ })
+
+ // 构造登录参数
+ const getLoginParams = () => {
+ return form.value
+ }
+
+ // 登陆
+ const onLogin = () => {
+
+ }
+
+
+
+ return {
+ formList,
+ form,
+ getLoginParams,
+ onLogin
+ }
+
+}
diff --git a/src/views/login/index.vue b/src/views/login/index.vue
new file mode 100644
index 0000000..de3f223
--- /dev/null
+++ b/src/views/login/index.vue
@@ -0,0 +1,156 @@
+
+
+
+
+
+ {{ info.title }}
+
+
+
+
+
+
+
+
+
diff --git a/src/views/modelReasoning/index/components/Module/BarChat.vue b/src/views/modelReasoning/index/components/Module/BarChat.vue
new file mode 100644
index 0000000..68c038c
--- /dev/null
+++ b/src/views/modelReasoning/index/components/Module/BarChat.vue
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/views/modelReasoning/index/components/Module/Compare.vue b/src/views/modelReasoning/index/components/Module/Compare.vue
new file mode 100644
index 0000000..81b7986
--- /dev/null
+++ b/src/views/modelReasoning/index/components/Module/Compare.vue
@@ -0,0 +1,486 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
选择对比性能
+
批平均准确率
+
类平均准确率
+
点平均准确率
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/views/modelReasoning/index/components/Module/Reasoning.vue b/src/views/modelReasoning/index/components/Module/Reasoning.vue
new file mode 100644
index 0000000..0fc7f33
--- /dev/null
+++ b/src/views/modelReasoning/index/components/Module/Reasoning.vue
@@ -0,0 +1,932 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 推理进度
+
+ {{ showStatus }}
+
+
+
+
+
+
+
+ 推理日志
+
+
+
+
+
+
+
+
+
+
资源监控
+
+
+
+
+ {{ item.label }}
+
+
+ {{ sourceFieldFormat(item.key) }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/views/modelReasoning/index/components/Module/Result.vue b/src/views/modelReasoning/index/components/Module/Result.vue
new file mode 100644
index 0000000..b28958e
--- /dev/null
+++ b/src/views/modelReasoning/index/components/Module/Result.vue
@@ -0,0 +1,311 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ workTimeFormat(text) }}
+
+
+
+ {{ dayjs(text).format("YYYY-MM-DD HH:mm:ss") }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/views/modelReasoning/index/hooks/barChat.ts b/src/views/modelReasoning/index/hooks/barChat.ts
new file mode 100644
index 0000000..bc9dcdc
--- /dev/null
+++ b/src/views/modelReasoning/index/hooks/barChat.ts
@@ -0,0 +1,163 @@
+import { ref } from "vue";
+
+interface SeriesOptions {
+ title: string;
+ key: string
+ // color: string;
+ // data: number[];
+ // lineColor: string;
+}
+
+export const useMain = () => {
+ const colorList = ["#9E87FF", "#73DDFF", "#fe9a8b", "#F56948", "#9E87FF"];
+
+ // 构建serise配置
+ const createSeriesOptions = (options: SeriesOptions) => {
+ const item = {
+ name: options.title,
+ key: options.key,
+ type: "bar",
+ stack: options.title,
+ barWidth: "8px",
+ barGap: "30%",
+ data: [],
+ };
+ return item;
+ };
+
+ // tooltip格式化
+ const tooltipFormat = (params: Array>) => {
+ // 标题部分 - 黑色
+ let result = `${params[0].axisValue}
`;
+
+ // 每项数据
+ params.forEach((item: Record, index: number) => {
+ const marginBottom = index < params.length - 1 ? "margin-bottom:4px;" : "";
+ result += `
+
+
+ ${item.marker}
+ ${item.seriesName}:
+
+
+ ${item.value}
+
+
+ `;
+ });
+ return result;
+ };
+
+ var singleData = [224, 220, 335];
+ var multipleData = [442, 440, 220];
+ var judgeData = [110, 220, 115];
+
+ const options = ref>({
+ tooltip: {
+ trigger: 'axis',
+ axisPointer: {
+ type: 'none'
+ },
+ borderColor: 'transparent',
+ borderWidth: 0,
+ padding: [10, 15],
+ textStyle: {
+ color: '#333',
+ fontSize: 14,
+ fontWeight: 'bold'
+ },
+ extraCssText: 'border-radius: 6px; background: rgba(255, 255, 255, 0.2); backdrop-filter: blur(10px)',
+ },
+ legend: {
+ icon: "circle",
+ bottom: "0",
+ left: "center",
+ itemWidth: 6,
+ itemGap: 20,
+ textStyle: {
+ color: "#556677",
+ },
+ },
+ grid: {
+ top: "20%",
+ bottom: "15%",
+ left: "5%",
+ right: "4%",
+ containLabel: true,
+ },
+ xAxis: [
+ {
+ type: "category",
+ data: [],
+ axisLabel: {
+ // inside: true,
+ // padding: [15, 0, 0, 0],
+ textStyle: {
+ color: "#86909C",
+ fontSize: 12,
+ },
+ },
+ axisTick: {
+ inside: true,
+ length: 4,
+ lineStyle: {
+ color: "#86909C",
+ },
+ },
+ axisLine: {
+ lineStyle: {
+ color: "#E5E6EB",
+ },
+ },
+ },
+ ],
+ yAxis: [
+ {
+ type: "value",
+ axisLabel: {
+ // inside: true,
+ textStyle: {
+ color: "#4E5969",
+ fontSize: 12,
+ },
+ },
+ splitLine: {
+ lineStyle: {
+ color: "#F2F3F5",
+ type: "solid",
+ },
+ show: true,
+ },
+ },
+ ],
+ series: [
+ {
+ key: 'eva_batch_top1',
+ },
+ {
+ key: 'eva_class_top1',
+ },
+ {
+ key: 'point_acc',
+ },
+ ],
+ });
+
+
+
+ return {
+ colorList,
+ tooltipFormat,
+ createSeriesOptions,
+ options,
+ };
+};
diff --git a/src/views/modelReasoning/index/hooks/performance.ts b/src/views/modelReasoning/index/hooks/performance.ts
new file mode 100644
index 0000000..f7d17b2
--- /dev/null
+++ b/src/views/modelReasoning/index/hooks/performance.ts
@@ -0,0 +1,37 @@
+import { ref } from 'vue'
+import { getModelByDatasetApi, getPerformanceApi } from '@/apis/train/performance.ts'
+
+export const useMain = () => {
+ const getModelByDatasetLoading = ref(false)
+ const getModelByDataset = async (datasetName: string) => {
+ if (getModelByDatasetLoading.value) return false
+ getModelByDatasetLoading.value = true
+ const res = await getModelByDatasetApi(datasetName).catch(err => {
+ return err
+ })
+ getModelByDatasetLoading.value = false
+ return res
+ }
+
+ const performanceLoading = ref(false)
+ const getPerformance = async (data: Record) => {
+ if (performanceLoading.value) return false
+ performanceLoading.value = true
+ const res = await getPerformanceApi(data).catch(err => {
+ return err
+ })
+ performanceLoading.value = false
+ return res
+ }
+
+ return {
+ getModelByDatasetRequest: {
+ request: getModelByDataset,
+ loading: getModelByDatasetLoading
+ },
+ getPerformanceRequest: {
+ request: getPerformance,
+ loading: performanceLoading
+ }
+ }
+}
diff --git a/src/views/modelReasoning/index/hooks/reasoning.ts b/src/views/modelReasoning/index/hooks/reasoning.ts
new file mode 100644
index 0000000..8b3a54f
--- /dev/null
+++ b/src/views/modelReasoning/index/hooks/reasoning.ts
@@ -0,0 +1,275 @@
+import { gpuUsageApi, gpuInfoApi, memoryInfoApi, cpuUsageApi } from '@/apis/system/index.ts'
+import { reasonDatasetListApi, datasetListApi } from '@/apis/dataset/index.ts'
+import { listApi as modelListApi } from '@/apis/model/index.ts'
+
+import {
+ optionsApi as reasoningTypeOptionsApi,
+ weightOptionsApi,
+ createTaskApi,
+ taskStatusApi,
+ taskResultApi,
+ listApi as taskListApi,
+ logsApi,
+ stopTaskApi
+} from '@/apis/train/inference.ts'
+import { ref } from 'vue'
+
+export const useMain = () => {
+ // 获取gpu使用情况loading
+ const gpuUsageLoading = ref(false)
+
+ // 获取gpu使用情况
+ const getGpuUsage = async () => {
+ if (gpuUsageLoading.value) return false
+ gpuUsageLoading.value = true
+ const res = await gpuUsageApi().catch(err => {
+ return err
+ })
+ gpuUsageLoading.value = false
+ return res
+ }
+
+ // 获取gpu信息loading
+ const gpuInfoLoading = ref(false)
+
+ // 获取gpu信息
+ const getGpuInfo = async () => {
+ if (gpuInfoLoading.value) return false
+ gpuInfoLoading.value = true
+ const res = await gpuInfoApi().catch(err => {
+ return err
+ })
+ gpuInfoLoading.value = false
+ return res
+ }
+
+ // 内存loading
+ const memoryInfoLoading = ref(false)
+
+ // 获取内存信息
+ const getMemoryInfo = async () => {
+ if (memoryInfoLoading.value) return false
+ memoryInfoLoading.value = true
+ const res = await memoryInfoApi().catch(err => {
+ return err
+ })
+ memoryInfoLoading.value = false
+ return res
+ }
+
+ // cpu使用loading
+ const cpuUsageLoading = ref(false)
+
+ // 获取内存信息
+ const getCpuUsage= async () => {
+ if (cpuUsageLoading.value) return false
+ cpuUsageLoading.value = true
+ const res = await cpuUsageApi().catch(err => {
+ return err
+ })
+ cpuUsageLoading.value = false
+ return res
+ }
+
+ // 获取数据集loading
+ const datasetListLoading = ref(false)
+
+ // 获取数据集
+ const getDatasetList = async () => {
+ if (datasetListLoading.value) return false
+ datasetListLoading.value = true
+ const res = await reasonDatasetListApi().catch(err => {
+ return err
+ })
+ datasetListLoading.value = false
+ return res
+ }
+
+ // 获取测试数据集
+ const testDatasetListLoading = ref(false)
+ const getTestDataList = async () => {
+ if (testDatasetListLoading.value) return false
+ testDatasetListLoading.value = true
+ const res = await datasetListApi({
+ type: 'test'
+ }).catch(err => {
+ return err
+ })
+ testDatasetListLoading.value = false
+ return res
+ }
+
+ // 模型列表
+ const modelListLoading = ref(false)
+ const getModelList = async () => {
+ if (modelListLoading.value) return false
+ modelListLoading.value = true
+ const res = await modelListApi().catch(err => {
+ return err
+ })
+ modelListLoading.value = false
+ return res
+ }
+
+ // 推理模式
+ const reasoningTypeOptionsLoading = ref(false)
+ const getReasoningTypeOptions = async () => {
+ if (reasoningTypeOptionsLoading.value) return false
+ reasoningTypeOptionsLoading.value = true
+ const res = await reasoningTypeOptionsApi().catch(err => {
+ return err
+ })
+ reasoningTypeOptionsLoading.value = false
+ return res
+ }
+
+ // 模型权重
+ const modelWeightOptionsLoading = ref(false)
+ const getModelWeightOptions = async (modelId: string) => {
+ if (modelWeightOptionsLoading.value) return false
+ modelWeightOptionsLoading.value = true
+ const res = await weightOptionsApi({
+ model_id: modelId
+ }).catch(err => {
+ return err
+ })
+ modelWeightOptionsLoading.value = false
+ return res
+ }
+
+ // 创建推理任务
+ const createTaskLoading = ref(false)
+ const createTask = async (data: Record) => {
+ if (createTaskLoading.value) return false
+ createTaskLoading.value = true
+ const res = await createTaskApi(data).catch(err => {
+ return err
+ })
+ createTaskLoading.value = false
+ return res
+ }
+
+ // 停止推理任务
+ const stopTaskLoading = ref(false)
+ const stopTask = async (taskId: string) => {
+ if (stopTaskLoading.value) return false
+ stopTaskLoading.value = true
+ const res = await stopTaskApi(taskId).catch(err => {
+ return err
+ })
+ stopTaskLoading.value = false
+ return res
+ }
+
+ // 推理任务列表
+ const taskListLoading = ref(false)
+ const getTaskList = async () => {
+ if (taskListLoading.value) return false
+ taskListLoading.value = true
+ const res = await taskListApi().catch(err => {
+ return err
+ })
+ taskListLoading.value = false
+ return res
+ }
+
+ // 获取任务状态
+ const taskStatusLoading = ref(false)
+ const getTaskStatus = async (id: string, controller?: AbortController) => {
+ if (taskStatusLoading.value) return false
+ taskStatusLoading.value = true
+ const res = await taskStatusApi(id, controller).catch(err => {
+ return err
+ })
+ taskStatusLoading.value = false
+ return res
+ }
+
+ // 获取任务结果
+ const taskResultLoading = ref(false)
+ const getTaskResult = async (id: string, controller?: AbortController) => {
+ if (taskResultLoading.value) return false
+ taskResultLoading.value = true
+ const res = await taskResultApi(id, controller).catch(err => {
+ return err
+ })
+ taskResultLoading.value = false
+ return res
+ }
+
+ // 获取任务日志
+ const getLogsLoading = ref(false)
+ const getLogs = async (id: string, controller?: AbortController) => {
+ if (getLogsLoading.value) return false
+ getLogsLoading.value = true
+ const res = await logsApi(id, controller).catch(err => {
+ return err
+ })
+ getLogsLoading.value = false
+ return res
+ }
+
+
+ return {
+ gpuUsageRequest: {
+ request: getGpuUsage,
+ loading: gpuUsageLoading
+ },
+ gpuInfoRequest: {
+ request: getGpuInfo,
+ loading: gpuInfoLoading
+ },
+ memoryInfoRequest: {
+ request: getMemoryInfo,
+ loading: memoryInfoLoading
+ },
+ cpuUsageRequest: {
+ request: getCpuUsage,
+ loading: cpuUsageLoading
+ },
+ datasetListRequest: {
+ request: getDatasetList,
+ loading: datasetListLoading
+ },
+ testDatasetListRequest: {
+ request: getTestDataList,
+ loading: testDatasetListLoading
+ },
+ modelListRequest: {
+ request: getModelList,
+ loading: modelListLoading
+ },
+ reasoningOptionsRequest: {
+ request: getReasoningTypeOptions,
+ loading: reasoningTypeOptionsLoading
+ },
+ modelWeightOptionsRequest: {
+ request: getModelWeightOptions,
+ loading: modelWeightOptionsLoading
+ },
+ createTaskRequest: {
+ request: createTask,
+ loading: createTaskLoading
+ },
+ stopTaskRequest: {
+ request: stopTask,
+ loading: stopTaskLoading
+ },
+ taskListRequest: {
+ request: getTaskList,
+ loading: taskListLoading
+ },
+ taskStatusRequest: {
+ request: getTaskStatus,
+ loading: taskStatusLoading
+ },
+ taskResultRequest: {
+ request: getTaskResult,
+ loading: taskResultLoading
+ },
+ logsRequest: {
+ request: getLogs,
+ loading: getLogsLoading
+ }
+ }
+}
diff --git a/src/views/modelReasoning/index/index.vue b/src/views/modelReasoning/index/index.vue
new file mode 100644
index 0000000..8d15b08
--- /dev/null
+++ b/src/views/modelReasoning/index/index.vue
@@ -0,0 +1,113 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/views/modelReasoning/index/services/index.ts b/src/views/modelReasoning/index/services/index.ts
new file mode 100644
index 0000000..70cfa21
--- /dev/null
+++ b/src/views/modelReasoning/index/services/index.ts
@@ -0,0 +1,19 @@
+import { useMain as useReasoningMain } from '@/views/modelReasoning/index/services/reasoning.ts'
+import { useMain as usePerformanceMain } from '@/views/modelReasoning/index/services/performance.ts'
+
+
+
+export const useMain = () => {
+
+ const reasoningServices = useReasoningMain()
+ const performanceServices = usePerformanceMain()
+ const modelReasoningServices = {
+ common: {}, // 公共状态模块
+ reasoning: reasoningServices, // 推理
+ performance: performanceServices // 性能相关模块
+ }
+
+ return {
+ modelReasoningServices,
+ }
+}
diff --git a/src/views/modelReasoning/index/services/performance.ts b/src/views/modelReasoning/index/services/performance.ts
new file mode 100644
index 0000000..9b25e84
--- /dev/null
+++ b/src/views/modelReasoning/index/services/performance.ts
@@ -0,0 +1,144 @@
+import { useMain as usePerformanceMain } from '@/views/modelReasoning/index/hooks/performance.ts'
+import { useMain as usePerformaceChatMain } from '@/views/modelReasoning/index/hooks/barChat.ts'
+import { ref } from 'vue'
+
+export const useMain = () => {
+ const { getModelByDatasetRequest, getPerformanceRequest } = usePerformanceMain()
+
+ const {
+ createSeriesOptions,
+ options: performanceOptions
+ } = usePerformaceChatMain()
+
+ const models = ref([])
+
+ const getModelsByDataset = async (datasetId: string) => {
+ const res = await getModelByDatasetRequest.request(datasetId)
+ if (res?.code == 200) {
+ models.value = res.data
+ }
+ return res
+ }
+
+ const performanceData = ref([])
+
+ const getPerformance = async (data: Record) => {
+ const res = await getPerformanceRequest.request(data)
+ if (res?.code == 200) {
+ performanceData.value = res.data
+ }
+ return res
+ }
+
+ const onUpdateChatOptions = (data: Record[]) => {
+ const showXAxisData: string[] = []
+ const seriesList: Record = []
+ const titleMap: Record = {
+ eva_batch_top1: '批平均准确率',
+ eva_class_top1 :'类平均准确率',
+ point_acc: '点平均准确率'
+ }
+ for(const type in titleMap) {
+ const series = createSeriesOptions({
+ title: titleMap[type],
+ key: type
+ })
+ seriesList.push(series)
+ }
+ data.forEach((item: Record, index) => {
+ const { eva_batch_metrics, eva_class_metrics, point_acc } = item.metrics
+ const { eva_batch_top1 } = eva_batch_metrics
+ const { eva_class_top1 } = eva_class_metrics
+ showXAxisData.push(`第${index + 1}次`)
+ seriesList[0].data.push(eva_batch_top1)
+ seriesList[1].data.push(eva_class_top1)
+ seriesList[2].data.push(point_acc)
+ })
+ performanceOptions.value = {
+ ...performanceOptions.value,
+ series: seriesList,
+ xAxis: [
+ {
+ ...performanceOptions.value.xAxis[0],
+ data: showXAxisData
+ }
+ ]
+ }
+ console.log('performanceOptions', performanceOptions.value)
+ // performanceOptions
+ }
+
+ const createChatOptions = (groupData: Record) => {
+ const seriesList = []
+ const xAxisData = []
+ const titleMap: Record = {
+ eva_batch_top1: '批平均准确率',
+ eva_class_top1 :'类平均准确率',
+ point_acc: '点平均准确率'
+ }
+ for(const type in titleMap) {
+ const series = createSeriesOptions({
+ title: titleMap[type],
+ key: type
+ })
+ seriesList.push(series)
+ }
+ const dataName = `${groupData.modelInfo.label}_${groupData.weightInfo.label}`
+ groupData.data.records.forEach((item: Record, index) => {
+ const { eva_batch_metrics, eva_class_metrics, point_acc } = item.metrics
+ const { eva_batch_top1 } = eva_batch_metrics
+ const { eva_class_top1 } = eva_class_metrics
+ xAxisData.push(`第${index + 1}次`)
+ seriesList[0].data.push(eva_batch_top1)
+ seriesList[1].data.push(eva_class_top1)
+ seriesList[2].data.push(point_acc)
+ })
+
+ return {
+ groupName: dataName,
+ xAxis: xAxisData,
+ series: seriesList
+ }
+ // data.forEach((item: Record, index: number) => {
+ // const { eva_batch_metrics, eva_class_metrics, point_acc } = item.metrics
+ // const { eva_batch_top1 } = eva_batch_metrics
+ // const { eva_class_top1 } = eva_class_metrics
+ // showXAxisData.push(`第${index + 1}次`)
+ // seriesList[0].data.push(eva_batch_top1)
+ // seriesList[1].data.push(eva_class_top1)
+ // seriesList[2].data.push(point_acc)
+ // })
+ // performanceOptions.value = {
+ // ...performanceOptions.value,
+ // series: seriesList,
+ // xAxis: [
+ // {
+ // ...performanceOptions.value.xAxis[0],
+ // data: showXAxisData
+ // }
+ // ]
+ // }
+ // console.log('performanceOptions', performanceOptions.value)
+ }
+
+ const state = {
+ models,
+ performanceData,
+ performanceOptions
+ }
+
+ return {
+ state: state,
+ api: {
+ getModelsByDataset,
+ getPerformance,
+ onUpdateChatOptions,
+ createChatOptions
+ },
+ requestMap: {
+ getModelByDatasetRequest,
+ getPerformanceRequest
+ }
+ }
+
+}
diff --git a/src/views/modelReasoning/index/services/reasoning.ts b/src/views/modelReasoning/index/services/reasoning.ts
new file mode 100644
index 0000000..c89f9a9
--- /dev/null
+++ b/src/views/modelReasoning/index/services/reasoning.ts
@@ -0,0 +1,295 @@
+import { useMain as useReasoningMain } from '@/views/modelReasoning/index/hooks/reasoning.ts'
+import { ref } from 'vue'
+
+export const useMain = () => {
+
+ const {
+ gpuUsageRequest,
+ gpuInfoRequest,
+ memoryInfoRequest,
+ cpuUsageRequest,
+ datasetListRequest,
+ testDatasetListRequest,
+ modelListRequest,
+ reasoningOptionsRequest,
+ modelWeightOptionsRequest,
+ createTaskRequest,
+ stopTaskRequest,
+ taskStatusRequest,
+ taskResultRequest,
+ taskListRequest,
+ logsRequest
+ } = useReasoningMain()
+
+ const cpuUsage = ref('')
+ const gpuUsage = ref('')
+ const memoryUsage = ref('')
+ const gpuInfo = ref('')
+
+ // 获取cpu使用情况
+ const getCpuUsage = async () => {
+ const res = await cpuUsageRequest.request()
+ if (res?.code == 200) {
+ cpuUsage.value = res.data
+ }
+ return res
+ }
+
+ // 获取gpu使用情况
+ const getGpuUsageInfo = async () => {
+ const res = await gpuUsageRequest.request()
+ if (res?.code == 200) {
+ gpuUsage.value = res.data.gpu_usage[0]
+ }
+ return res
+ }
+
+ // 获取内存使用情况
+ const getMemoryInfo = async () => {
+ const res = await memoryInfoRequest.request()
+ if (res?.code == 200) {
+ memoryUsage.value = res.data
+ }
+ return res
+ }
+
+ // 获取gpu情况
+ const getGpuInfo = async () => {
+ const res = await gpuInfoRequest.request()
+ if (res?.code == 200) {
+ gpuInfo.value = res.data.gpus[0]
+ }
+ return res
+ }
+
+
+ // 获取系统数据
+ const getSystemInfo = async () => {
+ const res = await Promise.all([
+ getCpuUsage(),
+ getGpuUsageInfo(),
+ getMemoryInfo(),
+ getGpuInfo(),
+ ])
+ return res
+ }
+
+ const datasetList = ref([])
+
+ // 获取数据集
+ const getDataset = async () => {
+ const res = await datasetListRequest.request()
+ console.log('getDataset', res)
+ if (res?.code == 200) {
+ datasetList.value = res.data?.datasets
+ }
+ return res
+ }
+
+ const testDatasetList = ref([])
+
+ // 获取测试数据集
+ const getTestDataset = async () => {
+ const res = await testDatasetListRequest.request()
+ if (res?.code == 200) {
+ testDatasetList.value = res.data?.datasets
+ }
+ return res
+ }
+
+ const models = ref([])
+
+ // 获取模型列表
+ const getModelList = async () => {
+ const res = await modelListRequest.request()
+ if (res?.code == 200) {
+ models.value = res?.models
+ }
+ console.log('getModelList', models.value)
+ return res
+ }
+
+ // 模型权重
+ const modelWeightOptions = ref([])
+
+ const getModelWeightOptions = async (moduleName: string) => {
+ const res = await modelWeightOptionsRequest.request(moduleName)
+ if (res?.code == 200) {
+ modelWeightOptions.value = res.data
+ }
+ return res
+ }
+
+
+
+ const resoningTypes = ref([])
+
+ // 获取推理模式
+ const getReasoningOptions = async () => {
+ const res = await reasoningOptionsRequest.request()
+ if (res?.code == 200) {
+ resoningTypes.value = res.data
+ }
+ return res
+ }
+
+ const relationTaskId = ref('')
+
+ // 创建任务
+ const createTask = async (params: Record) => {
+ const res = await createTaskRequest.request(params)
+ if (res?.code == 200) {
+ relationTaskId.value = res.data.task_id
+ }
+ return res
+ }
+
+ // 停止任务
+ const onStopTask = async (taskId: string) => {
+ const res = await stopTaskRequest.request(taskId)
+ return res
+ }
+ // 清空任务id
+
+ const cleanTaskId = () => {
+ relationTaskId.value = ''
+ }
+
+ const taskStatus = ref({})
+ let taskController: null | AbortController = null
+
+ // 获取任务状态
+ const getTaskStatus = async (taskId: string) => {
+ taskController = new AbortController()
+ const res = await taskStatusRequest.request(taskId, taskController)
+ taskController = null
+ if (res?.code == 200) {
+ taskStatus.value = res.data
+ }
+ return res
+ }
+
+ const onTaskStatusInterrupt = () => {
+ console.log('taskController', taskController)
+ if (taskController) {
+ taskController.abort()
+ taskController = null
+ }
+ }
+
+ // 重置任务状态
+ const resetTaskStatus = () => {
+ taskStatus.value = {}
+ }
+
+ // 任务结果列表
+ const taskResultList = ref([])
+
+ // 获取任务结果
+ const getTaskResult = async (taskId: string) => {
+ const res = await taskResultRequest.request(taskId)
+ if (res?.code == 200) {
+ taskResultList.value = [res.data]
+ }
+ return res
+ }
+
+ // 获取任务列表
+ const taskList = ref([])
+ const getTaskList = async () => {
+ const res = await taskListRequest.request()
+ if (res?.code == 200) {
+ taskList.value = res.data.tasks
+ }
+ return res
+ }
+
+ // 获取日志
+ const logs = ref([])
+ let logController: null | AbortController = null
+
+ // 中断日志请求
+ const onLogInterrupt = async () => {
+ if (logController) {
+ logController.abort()
+ logController = null;
+ }
+ }
+
+ // 清空日志
+ const clearLogs = () => {
+ logs.value = []
+ }
+
+ const getLogs = async (id: string) => {
+ logController = new AbortController()
+ const res = await logsRequest.request(id, logController)
+ if (res?.status == 200) {
+ clearLogs()
+ }
+ return res
+ }
+
+
+ const state = {
+ cpuUsage: cpuUsage,
+ gpuUsage: gpuUsage,
+ memoryUsage: memoryUsage,
+ gpuInfo: gpuInfo,
+ models: models,
+ resoningTypes: resoningTypes,
+ datasetList: datasetList,
+ testDatasetList: testDatasetList,
+ modelWeightOptions: modelWeightOptions,
+ relationTaskId: relationTaskId,
+ taskResultList: taskResultList,
+ taskStatus: taskStatus,
+ taskList,
+ log: {
+ logs: logs,
+ logController: logController,
+ }
+ }
+
+ return {
+ state,
+ api: {
+ getSystemInfo,
+ getModelList,
+ getDataset,
+ getTestDataset,
+ getReasoningOptions,
+ getModelWeightOptions,
+ resetTaskStatus,
+ createTask,
+ onStopTask,
+ cleanTaskId,
+ getTaskResult,
+ getTaskStatus,
+ onTaskStatusInterrupt,
+ getTaskList,
+ log: {
+ getLogs,
+ clearLogs,
+ onLogInterrupt,
+ }
+ },
+ requestMap: {
+ gpuUsageRequest,
+ gpuInfoRequest,
+ memoryInfoRequest,
+ cpuUsageRequest,
+ datasetListRequest,
+ testDatasetListRequest,
+ modelListRequest,
+ reasoningOptionsRequest,
+ modelWeightOptionsRequest,
+ createTaskRequest,
+ stopTaskRequest,
+ taskStatusRequest,
+ taskResultRequest,
+ taskListRequest,
+ logsRequest
+ }
+ }
+}
diff --git a/src/views/modelTraining/detail/components/Form/Train.vue b/src/views/modelTraining/detail/components/Form/Train.vue
new file mode 100644
index 0000000..4d91c43
--- /dev/null
+++ b/src/views/modelTraining/detail/components/Form/Train.vue
@@ -0,0 +1,278 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/views/modelTraining/detail/hooks/options.ts b/src/views/modelTraining/detail/hooks/options.ts
new file mode 100644
index 0000000..4fa532a
--- /dev/null
+++ b/src/views/modelTraining/detail/hooks/options.ts
@@ -0,0 +1,79 @@
+import { getPlatformByModuleNameApi } from '@/apis/system/config.ts'
+import { testDatasetListApi, trainDatasetListApi } from '@/apis/dataset/index.ts'
+import { listApi as modelListApi } from '@/apis/model/index.ts'
+
+
+import { ref } from 'vue'
+
+
+export const useMain = () => {
+ // 模型
+ const modelListLoading = ref(false)
+ const getModelList = async () => {
+ if (modelListLoading.value) return false
+ modelListLoading.value = true
+ const res = await modelListApi().catch(err => {
+ return err
+ })
+ modelListLoading.value = false
+ return res
+ }
+
+ const platformLoading = ref(false)
+ const getPlatformList = async (moduleName: string) => {
+ if (platformLoading.value) return false
+ platformLoading.value = true
+ const res = await getPlatformByModuleNameApi(moduleName).catch(err => {
+ return err
+ })
+ platformLoading.value = false
+ return res
+ }
+
+ // 获取测试集loading
+ const testDatasetListLoading = ref(false)
+
+ // 获取测试集
+ const getTestDatasetList = async () => {
+ if (testDatasetListLoading.value) return false
+ testDatasetListLoading.value = true
+ const res = await testDatasetListApi().catch(err => {
+ return err
+ })
+ testDatasetListLoading.value = false
+ return res
+ }
+
+ // 获取训练集loading
+ const trainDatasetListLoading = ref(false)
+
+ // 获取训练集
+ const getTrainDatasetList = async () => {
+ if (trainDatasetListLoading.value) return false
+ trainDatasetListLoading.value = true
+ const res = await trainDatasetListApi().catch(err => {
+ return err
+ })
+ trainDatasetListLoading.value = false
+ return res
+ }
+
+ return {
+ modelListRequest: {
+ request: getModelList,
+ loading: modelListLoading
+ },
+ platformRequest: {
+ request: getPlatformList,
+ loading: platformLoading
+ },
+ testDatasetListRequest: {
+ request: getTestDatasetList,
+ loading: testDatasetListLoading
+ },
+ trainDatasetListRequest: {
+ request: getTrainDatasetList,
+ loading: trainDatasetListLoading
+ }
+ }
+}
diff --git a/src/views/modelTraining/detail/hooks/task.ts b/src/views/modelTraining/detail/hooks/task.ts
new file mode 100644
index 0000000..c8a917c
--- /dev/null
+++ b/src/views/modelTraining/detail/hooks/task.ts
@@ -0,0 +1,89 @@
+import { ref } from 'vue'
+import { taskParamsApi, onInterruptApi, statusApi, logsApi, modelExportApi } from '@/apis/train/index.ts'
+
+
+export const useMain = () => {
+
+ // 模型导出
+ const exportLoading = ref(false)
+ const modelExport = async (taskId: string) => {
+ if (exportLoading.value) return false
+ exportLoading.value = true
+ const res = await modelExportApi(taskId).catch(err => {
+ return err
+ })
+ exportLoading.value = false
+ return res
+ }
+
+ // 中断任务
+ const onInterruptLoading = ref(false)
+ const onInterrupt = async (taskId: string) => {
+ if (onInterruptLoading.value) return false
+ onInterruptLoading.value = true
+ const res = await onInterruptApi(taskId).catch(err => {
+ return err
+ })
+ onInterruptLoading.value = false
+ return res
+ }
+
+ // 获取任务状态
+ const statusLoading = ref(false)
+ const getStatus = async (taskId: string) => {
+ if (statusLoading.value) return false
+ statusLoading.value = true
+ const res = await statusApi(taskId).catch(err => {
+ return err
+ })
+ statusLoading.value = false
+ return res
+ }
+
+ // 获取任务日志
+ const logsLoading = ref(false)
+ const getLogs = async (taskId: string) => {
+ if (logsLoading.value) return false
+ logsLoading.value = true
+ const res = await logsApi(taskId).catch(err => {
+ return err
+ })
+ logsLoading.value = false
+ return res
+ }
+
+ // 获取详情
+ const taskParamsLoading = ref(false)
+ const getTaskParams = async (taskId: string) => {
+ if (taskParamsLoading.value) return false
+ taskParamsLoading.value = true
+ const res = await taskParamsApi(taskId).catch(err => {
+ return err
+ })
+ taskParamsLoading.value = false
+ return res
+ }
+
+ return {
+ taskParamsRequest: {
+ request: getTaskParams,
+ loading: taskParamsLoading
+ },
+ interruptRequest: {
+ request: onInterrupt,
+ loading: onInterruptLoading
+ },
+ statusRequest: {
+ request: getStatus,
+ loading: statusLoading
+ },
+ logsRequest: {
+ request: getLogs,
+ loading: logsLoading
+ },
+ modelExportRequest: {
+ request: modelExport,
+ loading: exportLoading
+ }
+ }
+}
diff --git a/src/views/modelTraining/detail/index.vue b/src/views/modelTraining/detail/index.vue
new file mode 100644
index 0000000..4874d48
--- /dev/null
+++ b/src/views/modelTraining/detail/index.vue
@@ -0,0 +1,413 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{
+ statusMap[taskStatus?.status]
+ }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/views/modelTraining/detail/services/common.ts b/src/views/modelTraining/detail/services/common.ts
new file mode 100644
index 0000000..a308b23
--- /dev/null
+++ b/src/views/modelTraining/detail/services/common.ts
@@ -0,0 +1,23 @@
+import { router } from "@/router/index.ts";
+import { ref } from 'vue'
+export const useMain = () => {
+ const taskId = ref('')
+ // 获取页面传参
+ const getQuery = () => {
+ const { id } = router.currentRoute.value.query;
+ taskId.value = ( id || '') as string
+ }
+
+ const state = {
+ taskId,
+ }
+
+ return {
+ state: state,
+ api: {
+ getQuery
+ },
+ requestMap: {
+ }
+ }
+}
diff --git a/src/views/modelTraining/detail/services/index.ts b/src/views/modelTraining/detail/services/index.ts
new file mode 100644
index 0000000..bf6e8d0
--- /dev/null
+++ b/src/views/modelTraining/detail/services/index.ts
@@ -0,0 +1,18 @@
+import { useMain as useTaskServices } from '@/views/modelTraining/detail/services/task.ts'
+import { useMain as useComminServices } from '@/views/modelTraining/detail/services/common.ts'
+import { useMain as useOptionsMain } from '@/views/modelTraining/detail/services/options.ts'
+
+export const useMain = () => {
+ const commonServices = useComminServices()
+ const taskServices = useTaskServices()
+ const optionsServices = useOptionsMain()
+ const trainDetailServices = {
+ common: commonServices, // 公共状态模块
+ task: taskServices, // 任务模块
+ options: optionsServices // 选项
+ }
+
+ return {
+ trainDetailServices
+ }
+}
diff --git a/src/views/modelTraining/detail/services/options.ts b/src/views/modelTraining/detail/services/options.ts
new file mode 100644
index 0000000..2532546
--- /dev/null
+++ b/src/views/modelTraining/detail/services/options.ts
@@ -0,0 +1,105 @@
+// 创建参数相关
+import { useMain as useOptionsHooks } from '@/views/modelTraining/index/hooks/options.ts'
+import { ref } from 'vue'
+
+export const useMain = () => {
+ const {
+ testDatasetListRequest,
+ modelListRequest,
+ platformRequest,
+ trainDatasetListRequest
+ } = useOptionsHooks()
+
+
+ const models = ref([])
+ const getModels = async () => {
+ const res = await modelListRequest.request()
+ if (res?.code == 200) {
+ models.value = res?.models
+ }
+ return res
+ }
+
+ const platform = ref({})
+ const getPlatform = async (moduleName: string) => {
+ const res = await platformRequest.request(moduleName)
+ if (res?.code == 200) {
+ platform.value = res.data
+ }
+ return res
+ }
+
+ const testDataset = ref([])
+ const getTestDataset = async () => {
+ const res = await testDatasetListRequest.request()
+ if (res?.code == 200) {
+ testDataset.value = res.data?.datasets
+ }
+ return res
+ }
+
+ const trainDataset = ref([])
+ const getTrainDataset = async () => {
+ const res = await trainDatasetListRequest.request()
+ if (res?.code == 200) {
+ trainDataset.value = res.data?.datasets
+ }
+ return res
+ }
+
+ const optionsLoading = ref(false)
+ const getOptions = async () => {
+ optionsLoading.value = true
+ const res = await Promise.all([
+ getModels(),
+ getTestDataset(),
+ getTrainDataset(),
+ ])
+ optionsLoading.value = false
+ }
+
+ // 导出选项
+ const exportOptions = [
+ {
+ label: "ONNX",
+ value: "ONNX",
+ },
+ {
+ label: "PT",
+ value: "PT",
+ },
+ {
+ label: "PTH",
+ value: "PTH",
+ },
+ ]
+
+
+
+ const state = {
+ models,
+ exportOptions,
+ platform,
+ testDataset,
+ trainDataset,
+ optionsLoading
+ }
+
+
+ return {
+ state: state,
+ api: {
+ getModels,
+ getPlatform,
+ getTestDataset,
+ getTrainDataset,
+ getOptions,
+ },
+ requestMap: {
+ testDatasetListRequest,
+ modelListRequest,
+ platformRequest,
+ trainDatasetListRequest
+ }
+ }
+}
diff --git a/src/views/modelTraining/detail/services/task.ts b/src/views/modelTraining/detail/services/task.ts
new file mode 100644
index 0000000..7b8bbbb
--- /dev/null
+++ b/src/views/modelTraining/detail/services/task.ts
@@ -0,0 +1,123 @@
+import { useMain as useTaskHooks } from '@/views/modelTraining/detail/hooks/task.ts'
+import { ref } from 'vue'
+import { message } from 'ant-design-vue'
+
+export const useMain = () => {
+ const { taskParamsRequest, interruptRequest, statusRequest, logsRequest, modelExportRequest } = useTaskHooks()
+
+ // 任务参数
+ const taskParams = ref({})
+ const getTaskParams = async (taskId: string) => {
+ const res = await taskParamsRequest.request(taskId)
+ if (res?.code == 200) {
+ taskParams.value = res.data
+ }
+ return res
+ }
+
+ // 中断
+ const onInterrupt = async (taskId: string) => {
+ const res = await interruptRequest.request(taskId)
+ if (res?.code == 200) {
+
+ }
+ return res
+ }
+
+ // 任务状态
+ const taskStatus = ref({})
+ const getStatus = async (taskId: string) => {
+ const res = await statusRequest.request(taskId)
+ if (res?.code == 200) {
+ taskStatus.value = res.data
+ }
+ return res
+ }
+
+ // 任务日志
+ const logs = ref([])
+ let logsController: null | AbortController = null
+
+ // 清空日志
+ const clearLogs = () => {
+ logs.value = []
+ }
+
+ // 获取日志
+ const getLogs = async (taskId: string) => {
+ const res = await logsRequest.request(taskId)
+ // if (res?.status == 200) {
+ // const decoder = new TextDecoder("utf-8")
+ // const reader = res?.body?.getReader()
+ // function read() {
+ // // @ts-ignore
+ // reader.read().then(function processText({ done, value }) {
+ // console.log('logsStream', done, value)
+ // if (done) {
+ // // @ts-ignore
+ // // logs.value = buffer
+ // return
+ // }
+ // const chunk = decoder.decode(value, { stream: false });
+ // const chunks = chunk.split('\n')
+ // console.log('chunks', chunk, chunks)
+ // logs.value.push(...chunks)
+ // read();
+ // });
+ // }
+ // return read();
+ // }
+
+ return res
+ }
+
+ // 中断日志
+ const interruptLogs = () => {
+ if (logsController) {
+ logsController?.abort()
+ logsController = null
+ }
+ }
+
+ // 导出模型
+ const onModelExport = async (taskId: string) => {
+ const res = await modelExportRequest.request(taskId)
+ console.log('res', res)
+ if (res?.code == 200) {
+ const { exported_model_path } = res.data
+ message.info({
+ content: `已经导出到${exported_model_path}`,
+ duration: 10,
+ });
+ // todo
+ }
+ return res
+ }
+
+ const state = {
+ taskParams,
+ taskStatus,
+ logs,
+ logsController
+ }
+
+ return {
+ state: state,
+ api: {
+ getTaskParams,
+ getStatus,
+ getLogs,
+ interruptLogs,
+ onInterrupt,
+ onModelExport,
+ clearLogs
+ },
+ requestMap: {
+ taskParamsRequest,
+ interruptRequest,
+ statusRequest,
+ logsRequest,
+ modelExportRequest
+ }
+ }
+}
diff --git a/src/views/modelTraining/index/components/Form/Train.vue b/src/views/modelTraining/index/components/Form/Train.vue
new file mode 100644
index 0000000..ab46896
--- /dev/null
+++ b/src/views/modelTraining/index/components/Form/Train.vue
@@ -0,0 +1,377 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/views/modelTraining/index/components/Modal/TaskFormModal.vue b/src/views/modelTraining/index/components/Modal/TaskFormModal.vue
new file mode 100644
index 0000000..c9764eb
--- /dev/null
+++ b/src/views/modelTraining/index/components/Modal/TaskFormModal.vue
@@ -0,0 +1,325 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/views/modelTraining/index/components/Module/TrainTableModule.vue b/src/views/modelTraining/index/components/Module/TrainTableModule.vue
new file mode 100644
index 0000000..efa21f6
--- /dev/null
+++ b/src/views/modelTraining/index/components/Module/TrainTableModule.vue
@@ -0,0 +1,390 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ dayjs(text).format("YYYY-MM-DD HH:mm:ss") }}
+
+
+
+
+ {{ text }}
+
+
+ {{ statusMap[text] }}
+
+
+
+
+
+ 查看
+
+ 删除
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/views/modelTraining/index/hooks/index.ts b/src/views/modelTraining/index/hooks/index.ts
new file mode 100644
index 0000000..d018c2a
--- /dev/null
+++ b/src/views/modelTraining/index/hooks/index.ts
@@ -0,0 +1,104 @@
+import { ref } from 'vue'
+import { listApi, deleteApi, createTaskApi, onInterruptApi, statusApi, logsApi } from '@/apis/train/index.ts'
+
+
+export const useMain = () => {
+ // 获取列表
+ const listLoading = ref(false)
+ const getList = async () => {
+ if (listLoading.value) return false
+ listLoading.value = true
+ const res = await listApi().catch(err => {
+ return err
+ })
+ listLoading.value = false
+ return res
+ }
+
+ // 删除任务
+ const deleteLoading = ref(false)
+ const deleteTask = async (taskId: string) => {
+ if (deleteLoading.value) return false
+ deleteLoading.value = true
+ const res = await deleteApi(taskId).catch(err => {
+ return err
+ })
+ deleteLoading.value = false
+ return res
+ }
+
+ // 创建任务
+ const createTaskLoading = ref(false)
+ const createTask = async (params: Record) => {
+ if (createTaskLoading.value) return false
+ createTaskLoading.value = true
+ const res = await createTaskApi(params).catch(err => {
+ return err
+ })
+ createTaskLoading.value = false
+ return res
+ }
+
+ // 中断任务
+ const onInterruptLoading = ref(false)
+ const onInterrupt = async (taskId: string) => {
+ if (onInterruptLoading.value) return false
+ onInterruptLoading.value = true
+ const res = await onInterruptApi(taskId).catch(err => {
+ return err
+ })
+ onInterruptLoading.value = false
+ return res
+ }
+
+ // 获取任务状态
+ const statusLoading = ref(false)
+ const getStatus = async (taskId: string) => {
+ if (statusLoading.value) return false
+ statusLoading.value = true
+ const res = await statusApi(taskId).catch(err => {
+ return err
+ })
+ statusLoading.value = false
+ return res
+ }
+
+ // 获取任务日志
+ const logsLoading = ref(false)
+ const getLogs = async (taskId: string) => {
+ if (logsLoading.value) return false
+ logsLoading.value = true
+ const res = await logsApi(taskId).catch(err => {
+ return err
+ })
+ logsLoading.value = false
+ return res
+ }
+
+ return {
+ listRequest: {
+ request: getList,
+ loading: listLoading
+ },
+ deleteRequest: {
+ request: deleteTask,
+ loading: deleteLoading
+ },
+ createTaskRequest: {
+ request: createTask,
+ loading: createTaskLoading
+ },
+ onInterruptRequest: {
+ request: onInterrupt,
+ loading: onInterruptLoading
+ },
+ statusRequest: {
+ request: getStatus,
+ loading: statusLoading
+ },
+ logsRequest: {
+ request: getLogs,
+ loading: logsLoading
+ }
+ }
+}
diff --git a/src/views/modelTraining/index/hooks/options.ts b/src/views/modelTraining/index/hooks/options.ts
new file mode 100644
index 0000000..0b35c12
--- /dev/null
+++ b/src/views/modelTraining/index/hooks/options.ts
@@ -0,0 +1,82 @@
+import { getPlatformByModuleNameApi } from '@/apis/system/config.ts'
+import { datasetListApi } from '@/apis/dataset/index.ts'
+import { listApi as modelListApi } from '@/apis/model/index.ts'
+
+import { ref } from 'vue'
+
+
+export const useMain = () => {
+ // 模型
+ const modelListLoading = ref(false)
+ const getModelList = async () => {
+ if (modelListLoading.value) return false
+ modelListLoading.value = true
+ const res = await modelListApi().catch(err => {
+ return err
+ })
+ modelListLoading.value = false
+ return res
+ }
+
+ const platformLoading = ref(false)
+ const getPlatformList = async (moduleName: string) => {
+ if (platformLoading.value) return false
+ platformLoading.value = true
+ const res = await getPlatformByModuleNameApi(moduleName).catch(err => {
+ return err
+ })
+ platformLoading.value = false
+ return res
+ }
+
+ // 获取测试集loading
+ const testDatasetListLoading = ref(false)
+
+ // 获取测试集
+ const getTestDatasetList = async () => {
+ if (testDatasetListLoading.value) return false
+ testDatasetListLoading.value = true
+ const res = await datasetListApi({
+ type: 'test'
+ }).catch(err => {
+ return err
+ })
+ testDatasetListLoading.value = false
+ return res
+ }
+
+ // 获取训练集loading
+ const trainDatasetListLoading = ref(false)
+
+ // 获取训练集
+ const getTrainDatasetList = async () => {
+ if (trainDatasetListLoading.value) return false
+ trainDatasetListLoading.value = true
+ const res = await datasetListApi({
+ type: 'train'
+ }).catch(err => {
+ return err
+ })
+ trainDatasetListLoading.value = false
+ return res
+ }
+
+ return {
+ modelListRequest: {
+ request: getModelList,
+ loading: modelListLoading
+ },
+ platformRequest: {
+ request: getPlatformList,
+ loading: platformLoading
+ },
+ testDatasetListRequest: {
+ request: getTestDatasetList,
+ loading: testDatasetListLoading
+ },
+ trainDatasetListRequest: {
+ request: getTrainDatasetList,
+ loading: trainDatasetListLoading
+ }
+ }
+}
diff --git a/src/views/modelTraining/index/index.vue b/src/views/modelTraining/index/index.vue
new file mode 100644
index 0000000..752be83
--- /dev/null
+++ b/src/views/modelTraining/index/index.vue
@@ -0,0 +1,113 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/views/modelTraining/index/services/index.ts b/src/views/modelTraining/index/services/index.ts
new file mode 100644
index 0000000..e1a544e
--- /dev/null
+++ b/src/views/modelTraining/index/services/index.ts
@@ -0,0 +1,16 @@
+import { useMain as useTaskMain } from '@/views/modelTraining/index/services/train.ts'
+import { useMain as useOptionsMain } from '@/views/modelTraining/index/services/options.ts'
+
+export const useMain = () => {
+ const taskServices = useTaskMain()
+ const optionsServices = useOptionsMain()
+ const trainServices = {
+ common: {}, // 公共状态模块
+ task: taskServices, // 推理
+ options: optionsServices // 选项
+ }
+
+ return {
+ trainServices,
+ }
+}
diff --git a/src/views/modelTraining/index/services/options.ts b/src/views/modelTraining/index/services/options.ts
new file mode 100644
index 0000000..1335c1b
--- /dev/null
+++ b/src/views/modelTraining/index/services/options.ts
@@ -0,0 +1,106 @@
+// 创建参数相关
+import { useMain as useOptionsHooks } from '@/views/modelTraining/index/hooks/options.ts'
+import { ref } from 'vue'
+
+export const useMain = () => {
+ const {
+ testDatasetListRequest,
+ modelListRequest,
+ platformRequest,
+ trainDatasetListRequest
+ } = useOptionsHooks()
+
+
+ const models = ref([])
+ const getModels = async () => {
+ const res = await modelListRequest.request()
+ if (res?.code == 200) {
+ models.value = res?.models || []
+ }
+ return res
+ }
+
+ const platform = ref({})
+ const getPlatform = async (moduleName: string) => {
+ const res = await platformRequest.request(moduleName)
+ if (res?.code == 200) {
+ platform.value = res.data
+ }
+ return res
+ }
+
+ const testDataset = ref([])
+ const getTestDataset = async () => {
+ const res = await testDatasetListRequest.request()
+ console.log('getTestDataset', res)
+ if (res?.code == 200) {
+ testDataset.value = res.data?.datasets
+ }
+ return res
+ }
+
+ const trainDataset = ref([])
+ const getTrainDataset = async () => {
+ const res = await trainDatasetListRequest.request()
+ if (res?.code == 200) {
+ trainDataset.value = res.data?.datasets
+ }
+ return res
+ }
+
+ const optionsLoading = ref(false)
+ const getOptions = async () => {
+ optionsLoading.value = true
+ const res = await Promise.all([
+ getModels(),
+ getTestDataset(),
+ getTrainDataset(),
+ ])
+ optionsLoading.value = false
+ }
+
+ // 导出选项
+ const exportOptions = [
+ {
+ label: "ONNX",
+ value: "ONNX",
+ },
+ {
+ label: "PT",
+ value: "PT",
+ },
+ {
+ label: "PTH",
+ value: "PTH",
+ },
+ ]
+
+
+
+ const state = {
+ models,
+ exportOptions,
+ platform,
+ testDataset,
+ trainDataset,
+ optionsLoading
+ }
+
+
+ return {
+ state: state,
+ api: {
+ getModels,
+ getPlatform,
+ getTestDataset,
+ getTrainDataset,
+ getOptions,
+ },
+ requestMap: {
+ testDatasetListRequest,
+ modelListRequest,
+ platformRequest,
+ trainDatasetListRequest
+ }
+ }
+}
diff --git a/src/views/modelTraining/index/services/train.ts b/src/views/modelTraining/index/services/train.ts
new file mode 100644
index 0000000..229dab1
--- /dev/null
+++ b/src/views/modelTraining/index/services/train.ts
@@ -0,0 +1,110 @@
+import { useMain as useTrainMain } from '@/views/modelTraining/index/hooks/index.ts'
+import { ref, h } from 'vue'
+import { Modal, message } from 'ant-design-vue';
+import type { ModalProps } from 'ant-design-vue'
+
+export const useMain = () => {
+ const { listRequest, createTaskRequest, onInterruptRequest, statusRequest, logsRequest, deleteRequest } = useTrainMain()
+
+ const listData = ref([])
+ // 获取任务结果
+ const getTaskList = async () => {
+ const res = await listRequest.request()
+ if (res?.code == 200) {
+ listData.value = res.data
+ }
+ return res
+ }
+
+ // 创建任务
+ const createTask = async (params: Record) => {
+ const res = await createTaskRequest.request(params)
+ return res
+ }
+
+ // 中断任务
+ const onInterrupt = async (id: string) => {
+ const res = await onInterruptRequest.request(id)
+ return res
+ }
+
+ // 获取任务状态
+ const getStatus = async (id: string) => {
+ const res = await statusRequest.request(id)
+ return res
+ }
+
+ // 获取任务日志
+ const getLogs = async (id: string) => {
+ const res = await logsRequest.request(id)
+ return res
+ }
+
+ // 构造删除删除弹出层
+ const createDeleteProps = () => {
+ return {
+ centered: true,
+ title: h("div", { style: "font-size: 16px" }, "删除数据"),
+ icon: h("span"),
+ content: h(
+ "div",
+ { style: "font-size: 16px; margin-bottom: 20px" },
+ "确定要删除当前数据吗?本次操作无法撤回",
+ ),
+ okText: "确定",
+ cancelText: "取消",
+ onOk: async () => {},
+ onCancel() {},
+ }
+
+
+};
+
+ // 删除任务
+ const onShowTaskDelete = async (taskId: string) => {
+ return new Promise((resolve, reject) => {
+ const modalProps = createDeleteProps()
+ modalProps.onOk = async () => {
+ const res = await onDelete(taskId)
+ if (res?.code == 200) {
+ message.success('删除成功')
+ resolve(res)
+ } else {
+ reject(res)
+ }
+ }
+ Modal.confirm(modalProps);
+ })
+ }
+
+ const onDelete = async (info: any) => {
+ const res = await deleteRequest.request(info)
+ return res
+ }
+
+
+ const state = {
+ listData: listData
+ }
+
+ return {
+ state: state,
+ api: {
+ getTaskList,
+ createTask,
+ onInterrupt,
+ getStatus,
+ getLogs,
+ onShowTaskDelete,
+ onDelete
+ },
+ requestMap: {
+ logsRequest,
+ statusRequest,
+ createTaskRequest,
+ onInterruptRequest,
+ listRequest,
+ deleteRequest
+ }
+ }
+}
diff --git a/src/views/setting/model/index/components/Form/detailForm.vue b/src/views/setting/model/index/components/Form/detailForm.vue
new file mode 100644
index 0000000..1877990
--- /dev/null
+++ b/src/views/setting/model/index/components/Form/detailForm.vue
@@ -0,0 +1,232 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/views/setting/model/index/components/Form/index.vue b/src/views/setting/model/index/components/Form/index.vue
new file mode 100644
index 0000000..c9571db
--- /dev/null
+++ b/src/views/setting/model/index/components/Form/index.vue
@@ -0,0 +1,237 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/views/setting/model/index/components/Modal/UpdateModal.vue b/src/views/setting/model/index/components/Modal/UpdateModal.vue
new file mode 100644
index 0000000..2243484
--- /dev/null
+++ b/src/views/setting/model/index/components/Modal/UpdateModal.vue
@@ -0,0 +1,244 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/views/setting/model/index/components/Module/ModelTableModule.vue b/src/views/setting/model/index/components/Module/ModelTableModule.vue
new file mode 100644
index 0000000..2bf5b41
--- /dev/null
+++ b/src/views/setting/model/index/components/Module/ModelTableModule.vue
@@ -0,0 +1,557 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ dayjs(text).format("YYYY-MM-DD HH:mm:ss") }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{{ record.name || "" }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{{ index + 1 }}
+
{{ item.description }}
+
+
+
+ {{ record.tasks.map((item: Record) => item.description).join(',') }}
+
+
+
+
+
+
+
+
+
{{ index + 1 }}
+
{{ item }}
+
+
+
+ {{ text }}
+
+
+
+
+
+
+
+
+
+
+
+
+
{{ index + 1 }}
+
{{ item }}
+
+
+
+ {{ text }}
+
+
+
+
+
+
+
+
+
+
+
+ 查看
+ 删除
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/views/setting/model/index/hooks/index.ts b/src/views/setting/model/index/hooks/index.ts
new file mode 100644
index 0000000..b34ae5b
--- /dev/null
+++ b/src/views/setting/model/index/hooks/index.ts
@@ -0,0 +1,73 @@
+import { ref } from 'vue'
+import { addApi, updateApi, deleteApi, listApi } from '@/apis/model/index.ts'
+
+export const useModelHooks = () => {
+ // 新增
+ const addLoading = ref(false)
+ const addModel = async (params: any) => {
+ if (addLoading.value) return false
+ addLoading.value = true
+ const res = await addApi(params).catch(err => {
+ return err
+ })
+ addLoading.value = false
+ return res
+ }
+
+ // 修改
+ const updateLoading = ref(false)
+ const updateModel = async (params: any) => {
+ if (updateLoading.value) return false
+ updateLoading.value = true
+ const res = await updateApi(params).catch(err => {
+ return err
+ })
+ updateLoading.value = false
+ return res
+ }
+
+ // 删除
+ const deleteLoading = ref(false)
+ const deleteModel = async (id: string) => {
+ if (deleteLoading.value) return false
+ deleteLoading.value = true
+ const res = await deleteApi(id).catch(err => {
+ return err
+ })
+ deleteLoading.value = false
+ return res
+ }
+
+ // 获取列表
+ const listLoading = ref(false)
+ const getModelList = async (params?: any) => {
+ if (listLoading.value) return false
+ listLoading.value = true
+ const res = await listApi(params).catch(err => {
+ return err
+ })
+ listLoading.value = false
+ return res
+ }
+
+
+
+ return {
+ addRequest: {
+ request: addModel,
+ loading: addLoading
+ },
+ updateRequest: {
+ request: updateModel,
+ loading: updateLoading
+ },
+ deleteRequest: {
+ request: deleteModel,
+ loading: deleteLoading
+ },
+ listRequest: {
+ request: getModelList,
+ loading: listLoading
+ }
+ }
+}
diff --git a/src/views/setting/model/index/index.vue b/src/views/setting/model/index/index.vue
new file mode 100644
index 0000000..04c3595
--- /dev/null
+++ b/src/views/setting/model/index/index.vue
@@ -0,0 +1,149 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/views/setting/model/index/services/index.ts b/src/views/setting/model/index/services/index.ts
new file mode 100644
index 0000000..48d1a75
--- /dev/null
+++ b/src/views/setting/model/index/services/index.ts
@@ -0,0 +1 @@
+export * from './main'
diff --git a/src/views/setting/model/index/services/main.ts b/src/views/setting/model/index/services/main.ts
new file mode 100644
index 0000000..0940a35
--- /dev/null
+++ b/src/views/setting/model/index/services/main.ts
@@ -0,0 +1,14 @@
+import { useModelServer } from '@/views/setting/model/index/services/model/index.ts'
+
+export const useMainServer = () => {
+
+ const modelServer = {
+ base: useModelServer()
+ }
+
+
+
+ return {
+ modelServer
+ }
+}
diff --git a/src/views/setting/model/index/services/model/index.ts b/src/views/setting/model/index/services/model/index.ts
new file mode 100644
index 0000000..8ceca44
--- /dev/null
+++ b/src/views/setting/model/index/services/model/index.ts
@@ -0,0 +1,90 @@
+import { ref, h } from 'vue'
+import { useModelHooks } from '@/views/setting/model/index/hooks/index.ts'
+import { Modal, message } from 'ant-design-vue';
+
+
+export const useModelServer = () => {
+ const { listRequest, addRequest, updateRequest, deleteRequest } = useModelHooks()
+
+ const modelList = ref([])
+ // 获取列表
+ const getList = async (params?: any) => {
+ const res = await listRequest.request(params)
+ console.log('res', res)
+ if (res?.code == 200) {
+ modelList.value = res.models || []
+ }
+ return res
+ }
+
+ // 新增
+ const addModel = async (params: any) => {
+ return await addRequest.request(params)
+ }
+
+ // 更新
+ const updateModel = async (params: any) => {
+ return await updateRequest.request(params)
+ }
+
+ // 构造删除删除弹出层
+ const createDeleteProps = () => {
+ return {
+ centered: true,
+ title: h("div", { style: "font-size: 16px" }, "删除数据"),
+ icon: h("span"),
+ content: h(
+ "div",
+ { style: "font-size: 16px; margin-bottom: 20px" },
+ "确定要删除当前数据吗?本次操作无法撤回",
+ ),
+ okText: "确定",
+ cancelText: "取消",
+ onOk: async () => {},
+ onCancel() {},
+ }
+ };
+
+ // 删除任务
+ const onShowModelDelete = async (modelId: string) => {
+ return new Promise((resolve, reject) => {
+ const modalProps = createDeleteProps()
+ modalProps.onOk = async () => {
+ const res = await deleteModel(modelId)
+ if (res?.code == 200) {
+ message.success('删除成功')
+ resolve(res)
+ } else {
+ reject(res)
+ }
+ }
+ Modal.confirm(modalProps);
+ })
+ }
+
+ // 删除
+ const deleteModel = async (params: any) => {
+ const res = await deleteRequest.request(params)
+ return res
+ }
+
+
+ return {
+ state: {
+ modelList
+ },
+ api: {
+ getList,
+ addModel,
+ updateModel,
+ deleteModel,
+ onShowModelDelete
+ },
+ requestMap: {
+ listRequest,
+ addRequest,
+ updateRequest,
+ deleteRequest
+ }
+ }
+}
diff --git a/src/views/test/components/chart/composables/useLargeDataOptimization.ts b/src/views/test/components/chart/composables/useLargeDataOptimization.ts
new file mode 100644
index 0000000..02aabd5
--- /dev/null
+++ b/src/views/test/components/chart/composables/useLargeDataOptimization.ts
@@ -0,0 +1,39 @@
+// composables/useLargeDataOptimization.ts
+export function useLargeDataOptimization() {
+ // 数据分片加载
+ const loadDataInChunks = async (totalPoints: number, chunkSize: number = 10000) => {
+ const chunks = [];
+
+ for (let i = 0; i < totalPoints; i += chunkSize) {
+ const chunk = await fetchChunk(i, Math.min(chunkSize, totalPoints - i));
+ chunks.push(chunk);
+
+ // 分批渲染,避免界面卡顿
+ if (i % (chunkSize * 5) === 0) {
+ await new Promise(resolve => setTimeout(resolve, 0));
+ }
+ }
+
+ return chunks.flat();
+ };
+
+ // 数据采样(大数据量时)
+ const sampleData = (data: ThermalPoint[], sampleRate: number = 0.1) => {
+ return data.filter(() => Math.random() < sampleRate);
+ };
+
+ // 使用 Web Worker 处理数据
+ const processDataInWorker = (data: ThermalPoint[]) => {
+ return new Promise((resolve) => {
+ const worker = new Worker('./thermalWorker.js');
+ worker.postMessage(data);
+ worker.onmessage = (e) => resolve(e.data);
+ });
+ };
+
+ return {
+ loadDataInChunks,
+ sampleData,
+ processDataInWorker
+ };
+}
diff --git a/src/views/test/components/chart/composables/useThermalData.ts b/src/views/test/components/chart/composables/useThermalData.ts
new file mode 100644
index 0000000..2babd61
--- /dev/null
+++ b/src/views/test/components/chart/composables/useThermalData.ts
@@ -0,0 +1,77 @@
+// composables/useThermalData.ts
+import { ref, computed } from 'vue';
+
+interface ThermalPoint {
+ timeIndex: number;
+ valueIndex: number;
+ intensity: number;
+}
+
+export function useThermalData() {
+ // 模拟数据生成(替换为实际API)
+ const generateSimulatedData = (): ThermalPoint[] => {
+ const data: ThermalPoint[] = [];
+ const timePoints = 100; // 时间点数量
+ const valueRange = 80; // 数值范围 -40 到 +40
+
+ for (let t = 0; t < timePoints; t++) {
+ for (let v = 0; v < valueRange; v++) {
+ // 模拟中心亮带效果
+ const centerValue = 40; // 中心位置
+ const distance = Math.abs(v - centerValue);
+ const baseIntensity = Math.exp(-distance / 10); // 高斯分布
+
+ // 添加噪声和动态变化
+ const noise = (Math.random() - 0.5) * 0.2;
+ const timeWave = Math.sin(t * 0.1) * 0.3;
+
+ const intensity = Math.max(0, baseIntensity + noise + timeWave);
+
+ if (intensity > 0.1) { // 过滤低强度点
+ data.push({
+ timeIndex: t * 20, // 转换为毫秒
+ valueIndex: v - 40, // 转换为 -40 到 +40
+ intensity
+ });
+ }
+ }
+ }
+
+ return data;
+ };
+
+ const thermalData = ref(generateSimulatedData());
+
+ // 计算范围
+ const timeRange = computed(() => {
+ const times = thermalData.value.map(d => d.timeIndex);
+ return Array.from(new Set(times)).sort((a, b) => a - b);
+ });
+
+ const valueRange = computed(() => {
+ const values = thermalData.value.map(d => d.valueIndex);
+ return Array.from(new Set(values)).sort((a, b) => a - b);
+ });
+
+ // 转换为 ECharts 需要的格式 [x, y, value]
+ const chartData = computed(() =>
+ thermalData.value.map(item => [
+ item.timeIndex,
+ item.valueIndex,
+ item.intensity * 80 - 60 // 缩放到 -60 到 +20 范围
+ ])
+ );
+
+ // 更新数据(模拟实时更新)
+ const updateData = () => {
+ const newData = generateSimulatedData();
+ thermalData.value = newData;
+ };
+
+ return {
+ thermalData: chartData,
+ timeRange,
+ valueRange,
+ updateData
+ };
+}
diff --git a/src/views/test/components/chart/index.vue b/src/views/test/components/chart/index.vue
new file mode 100644
index 0000000..269c517
--- /dev/null
+++ b/src/views/test/components/chart/index.vue
@@ -0,0 +1,205 @@
+
+
+
+
+
+
+
+
+ {{ isPlaying ? "暂停" : "播放" }}
+
+
+ 速度: {{ speed }}ms
+
+
+
+
+
+
+
+
diff --git a/src/views/test/components/chart/t.html b/src/views/test/components/chart/t.html
new file mode 100644
index 0000000..943cae4
--- /dev/null
+++ b/src/views/test/components/chart/t.html
@@ -0,0 +1,142 @@
+
+
+
+
+
+ ECharts 热图实现
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/views/test/components/chart/t.vue b/src/views/test/components/chart/t.vue
new file mode 100644
index 0000000..b8d1f53
--- /dev/null
+++ b/src/views/test/components/chart/t.vue
@@ -0,0 +1,162 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/views/test/components/fileSelect/index.vue b/src/views/test/components/fileSelect/index.vue
new file mode 100644
index 0000000..c96a615
--- /dev/null
+++ b/src/views/test/components/fileSelect/index.vue
@@ -0,0 +1,71 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/views/test/index.vue b/src/views/test/index.vue
new file mode 100644
index 0000000..576ce05
--- /dev/null
+++ b/src/views/test/index.vue
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts
new file mode 100644
index 0000000..11f02fe
--- /dev/null
+++ b/src/vite-env.d.ts
@@ -0,0 +1 @@
+///
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..0b1e6e0
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,37 @@
+{
+ "compilerOptions": {
+ "noEmitOnError": false,
+ "target": "ES2020",
+ "useDefineForClassFields": true,
+ "module": "ESNext",
+ "lib": ["ES2021", "DOM", "DOM.Iterable"],
+ "skipLibCheck": true,
+ "allowSyntheticDefaultImports": true,
+ "moduleResolution": "node",
+ "allowImportingTsExtensions": true,
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "noEmit": true,
+ "jsx": "preserve",
+ /* Linting */
+ "strict": true,
+ "noUnusedLocals": false,
+ "noUnusedParameters": false,
+ "noFallthroughCasesInSwitch": true,
+ "baseUrl": "./",
+ "paths": {
+ "@": ["src"],
+ "@/*": ["src/*"]
+ },
+ "types": ["vite/client"], // 引入 Vite 环境类型
+ "typeRoots": ["./node_modules/@types", "./src/types"] // 指定类型查找路径
+ },
+ "include": [
+ "src/**/*.ts",
+ "src/**/*.d.ts",
+ "src/**/*.vue",
+ "src/**/*.tsx",
+ "src/**/*"
+ ],
+ "references": [{ "path": "./tsconfig.node.json" }]
+}
diff --git a/tsconfig.node.json b/tsconfig.node.json
new file mode 100644
index 0000000..42872c5
--- /dev/null
+++ b/tsconfig.node.json
@@ -0,0 +1,10 @@
+{
+ "compilerOptions": {
+ "composite": true,
+ "skipLibCheck": true,
+ "module": "ESNext",
+ "moduleResolution": "bundler",
+ "allowSyntheticDefaultImports": true
+ },
+ "include": ["vite.config.ts"]
+}
diff --git a/vite.config.ts b/vite.config.ts
new file mode 100644
index 0000000..8dc94f5
--- /dev/null
+++ b/vite.config.ts
@@ -0,0 +1,103 @@
+import { fileURLToPath, URL } from 'node:url'
+import { resolve } from 'node:path'
+
+import { defineConfig } from 'vite'
+import vue from '@vitejs/plugin-vue'
+import vueJsx from '@vitejs/plugin-vue-jsx'
+import vueDevTools from 'vite-plugin-vue-devtools'
+import postcssPresetEnv from 'postcss-preset-env'
+import postcssNormalizeCharset from 'postcss-normalize-charset'
+import autoprefixer from 'autoprefixer'
+
+function pathResolve(dir: string) {
+ return resolve(__dirname, dir)
+}
+
+const URL_USE = 'http://172.18.32.98:8888'
+// const URL_USE = 'http://172.18.32.98:11439'
+// const URL_USE = 'http://192.168.0.46:11439'
+// const URL_USE = 'http://222.190.139.186:14000'
+
+// https://vite.dev/config/
+export default defineConfig({
+ server: {
+ host: true,
+ port: 3019, // 指定端口
+ strictPort: true, // 如果端口已被占用则退出
+ proxy: {
+ '/api': {
+ target: URL_USE,
+ changeOrigin: true, // 修改请求的 origin
+ rewrite: (path: string) => path, // 可选:重写路径
+ },
+ '/ollama': {
+ target: URL_USE,
+ changeOrigin: true, // 修改请求的 origin
+ rewrite: (path: string) => path, // 可选:重写路径
+ },
+ '/openai': {
+ target: URL_USE,
+ changeOrigin: true, // 修改请求的 origin
+ rewrite: (path: string) => path, // 可选:重写路径
+ },
+ '/ws': {
+ target: URL_USE,
+ changeOrigin: true, // 修改请求的 origin
+ ws: true,
+ rewrite: (path: string) => path, // 可选:重写路径
+ },
+ },
+ },
+ plugins: [
+ vue(),
+ vueJsx(),
+ vueDevTools(),
+ ],
+ resolve: {
+ alias: [
+ {
+ find: /\@\//,
+ replacement: `${pathResolve('src')}/`,
+ },
+ ]
+ },
+ css: {
+ postcss: {
+ plugins:[
+ postcssPresetEnv(),
+ postcssNormalizeCharset({
+ add: true,
+ }),
+ autoprefixer({
+ //css兼容前缀
+ overrideBrowserslist: [
+ 'Android 4.1',
+ 'ios 7.1',
+ 'Chrome >31',
+ 'not ie <=11', //不考虑IE浏览器
+ 'ff >= 30', //仅新版本用'ff >= 30
+ '>1%', //全球统计有超过1%的使用了使用'> 1%'
+ 'last 2 version', //所有主流浏览器最近2个版本
+ ],
+ grid: true, //开启grid布局的兼容(浏览器IE除外其它都能兼容grid,可以关闭开启)
+ }),
+ // px2rem({
+ // rootValue: 19.2, // UI设计稿的宽度/10
+ // // rootValue : 37.5,
+ // unitPrecision: 3, // 转rem精确到小数点多少位
+ // propList: ['*'], // 需要转换的属性 *表示所有
+ // selectorBlackList: ['ignore'], // 不进行px转换的选择器
+ // replace: true, // 是否直接更换属性值,而不添加备用属性
+ // mediaQuery: false, // 是否在媒体查询的css代码中也进行转换
+ // minPixelValue: 0, // 设置要替换的最小像素值
+ // exclude: /node_modules/i // 排除node_modules文件夹下的文件
+ // })
+ ]
+ },
+ preprocessorOptions: {
+ scss: {
+
+ },
+ },
+ }
+})