第一次提交
9
.editorconfig
Normal file
@ -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
|
||||
13
.env
Normal file
@ -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';
|
||||
|
||||
13
.env.development
Normal file
@ -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';
|
||||
|
||||
1
.gitattributes
vendored
Normal file
@ -0,0 +1 @@
|
||||
* text=auto eol=lf
|
||||
30
.gitignore
vendored
Normal file
@ -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
|
||||
9
.vscode/extensions.json
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"Vue.volar",
|
||||
"vitest.explorer",
|
||||
"dbaeumer.vscode-eslint",
|
||||
"EditorConfig.EditorConfig",
|
||||
"esbenp.prettier-vscode"
|
||||
]
|
||||
}
|
||||
13
index.html
Normal file
@ -0,0 +1,13 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<link rel="icon" href="/favicon.ico">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Vite App</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
</html>
|
||||
60
package.json
Normal file
@ -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"
|
||||
}
|
||||
}
|
||||
18
public/css/tinymceEdit--wirte.css
Normal file
@ -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;
|
||||
}
|
||||
BIN
public/favicon.ico
Normal file
|
After Width: | Height: | Size: 11 KiB |
45
src/App.vue
Normal file
@ -0,0 +1,45 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from "vue";
|
||||
import defaultJSON from "@/styles/theme/default.json";
|
||||
import zhCN from "ant-design-vue/es/locale/zh_CN";
|
||||
import { useRoute } from "vue-router";
|
||||
|
||||
const data = {
|
||||
theme: defaultJSON,
|
||||
};
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
const reload = ref<boolean>(false);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<a-config-provider :theme="data.theme" :locale="zhCN">
|
||||
<a-app>
|
||||
<Suspense>
|
||||
<RouterView :reload="reload" />
|
||||
<template #fallback>
|
||||
<div class="loading-box">
|
||||
<a-spin />
|
||||
</div>
|
||||
</template>
|
||||
</Suspense>
|
||||
</a-app>
|
||||
</a-config-provider>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.ant-app {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.loading-box {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
</style>
|
||||
10
src/apis/auth/index.ts
Normal file
@ -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',
|
||||
// })
|
||||
// };
|
||||
43
src/apis/dataManage/dataExport.ts
Normal file
@ -0,0 +1,43 @@
|
||||
// 数据导出相关
|
||||
import { request } from '@/utils/request/index.ts';
|
||||
// 列表接口
|
||||
export const listApi = async (data?: Record<string, any>) => {
|
||||
return await request({
|
||||
url: `/data/list_info`,
|
||||
method: 'GET',
|
||||
params: data
|
||||
})
|
||||
};
|
||||
|
||||
// 更新标注
|
||||
export const updateLabelApi = async (data: Record<string, any>) => {
|
||||
return await request({
|
||||
url: `/data/save_label`,
|
||||
method: 'POST',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 获取导出数据文件在线地址
|
||||
export const getExportUrlApi = async (data: Record<string, any>) => {
|
||||
return await request({
|
||||
url: `/data/export_data`,
|
||||
method: 'GET',
|
||||
params: data,
|
||||
getRaw: true
|
||||
})
|
||||
}
|
||||
|
||||
// 导入标注
|
||||
export const importLabelByFileApi = async (data: Record<string, any>) => {
|
||||
return await request({
|
||||
url: `/data/batch_label`,
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
authorization: `Bearer ${localStorage.token}`
|
||||
},
|
||||
data,
|
||||
dataRaw: true,
|
||||
})
|
||||
}
|
||||
43
src/apis/dataManage/dataImport.ts
Normal file
@ -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<string, any>) => {
|
||||
return await request({
|
||||
url: `/data/delete/${data.fileName}`,
|
||||
method: 'DELETE',
|
||||
})
|
||||
}
|
||||
|
||||
export const importApi = async (data: Record<string, any>) => {
|
||||
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<string, any>) => {
|
||||
return await request({
|
||||
url: `/data/data`,
|
||||
method: 'POST',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
22
src/apis/dataManage/statistics.ts
Normal file
@ -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
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
52
src/apis/dataset/index.ts
Normal file
@ -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<string, any>) => {
|
||||
return request({
|
||||
url: '/data/exported_data',
|
||||
method: 'GET',
|
||||
params: params
|
||||
})
|
||||
}
|
||||
|
||||
// 更新数据集
|
||||
export const updateDatasetApi = async (params: Record<string, any>) => {
|
||||
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',
|
||||
})
|
||||
}
|
||||
45
src/apis/model/index.ts
Normal file
@ -0,0 +1,45 @@
|
||||
import { request } from '@/utils/request/index.ts';
|
||||
|
||||
|
||||
// 新增
|
||||
export const addApi = async (data: Record<string, any>, 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<string, any>) => {
|
||||
return await request({
|
||||
url: `/train/model/list`,
|
||||
method: 'GET',
|
||||
params: params
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
0
src/apis/model/reasoning.ts
Normal file
0
src/apis/model/train.ts
Normal file
25
src/apis/system/config.ts
Normal file
@ -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',
|
||||
})
|
||||
}
|
||||
34
src/apis/system/index.ts
Normal file
@ -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',
|
||||
})
|
||||
}
|
||||
1
src/apis/system/model.ts
Normal file
@ -0,0 +1 @@
|
||||
// 模型相关
|
||||
67
src/apis/train/index.ts
Normal file
@ -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<string, any>) => {
|
||||
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'
|
||||
})
|
||||
}
|
||||
84
src/apis/train/inference.ts
Normal file
@ -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<string, any>, 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<string, any>) => {
|
||||
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,
|
||||
})
|
||||
}
|
||||
|
||||
19
src/apis/train/performance.ts
Normal file
@ -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<string, any>) => {
|
||||
return await request({
|
||||
url: `/performance/datasets/${data.datasetName}/models/${data.modelName}/${data.checkpoint_dir}`,
|
||||
method: 'GET'
|
||||
})
|
||||
}
|
||||
|
||||
2
src/assets/files/dataExport/导入标注模板文件.csv
Normal file
@ -0,0 +1,2 @@
|
||||
batch_id,label
|
||||
20250612_960_0,A330
|
||||
|
10
src/assets/icons/back.svg
Normal file
@ -0,0 +1,10 @@
|
||||
<svg width="12" height="11" viewBox="0 0 12 11" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="_鍥惧眰_1" clip-path="url(#clip0_75_1658)">
|
||||
<path id="Vector" d="M7.71372 1.83341H3.42832V0L0 2.75027L3.42832 5.50039V3.6662H7.71387C9.17109 3.6662 10.2863 4.85719 10.2863 6.41647C10.2863 7.97575 9.17183 9.16659 7.71387 9.16659H3.42832V11H7.71387C10.0283 11 12 8.98284 12 6.41647C12 3.8501 10.1145 1.83341 7.71372 1.83341Z" fill="currentColor"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_75_1658">
|
||||
<rect width="12" height="11" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 610 B |
1
src/assets/icons/chat.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" version="1.1" width="18" height="18" viewBox="0 0 18 18"><g><g><g><path d="M1.875,15.375L1.875,2.625Q1.875,2.5511314,1.889411,2.478682Q1.903822,2.406233,1.93209,2.337987Q1.960359,2.269742,2.001398,2.208322Q2.042437,2.146903,2.09467,2.09467Q2.146903,2.042437,2.208322,2.001398Q2.269742,1.960359,2.337987,1.93209Q2.406233,1.903822,2.478682,1.889411Q2.5511314,1.875,2.625,1.875L15.375,1.875Q15.4489,1.875,15.5213,1.889411Q15.5938,1.903822,15.662,1.93209Q15.7303,1.960359,15.7917,2.001398Q15.8531,2.042437,15.9053,2.09467Q15.9576,2.146903,15.9986,2.208322Q16.0396,2.269742,16.0679,2.337987Q16.0962,2.406233,16.110599999999998,2.478682Q16.125,2.5511314,16.125,2.625L16.125,15.375Q16.125,15.4489,16.110599999999998,15.5213Q16.0962,15.5938,16.0679,15.662Q16.0396,15.7303,15.9986,15.7917Q15.9576,15.8531,15.9053,15.9053Q15.8531,15.9576,15.7917,15.9986Q15.7303,16.0396,15.662,16.0679Q15.5938,16.0962,15.5213,16.110599999999998Q15.4489,16.125,15.375,16.125L2.625,16.125Q2.5511314,16.125,2.478682,16.110599999999998Q2.406233,16.0962,2.337987,16.0679Q2.269742,16.0396,2.208322,15.9986Q2.146903,15.9576,2.09467,15.9053Q2.042437,15.8531,2.001398,15.7917Q1.960359,15.7303,1.93209,15.662Q1.903822,15.5938,1.889411,15.5213Q1.875,15.4489,1.875,15.375ZM3.375,3.375L3.375,14.625L14.625,14.625L14.625,3.375L3.375,3.375Z" fill="currentColor" fill-opacity="1"/></g></g><g transform="matrix(0,1,-1,0,15.75,-5.25)"><path d="M10.5,4.5C10.085786,4.5,9.75,4.835786,9.75,5.25C9.75,5.664214,10.085786,6,10.5,6C10.5,6,10.5,4.5,10.5,4.5C10.5,4.5,10.5,4.5,10.5,4.5ZM12.375,6C12.78921,6,13.125,5.664214,13.125,5.25C13.125,4.835786,12.78921,4.5,12.375,4.5C12.375,4.5,12.375,6,12.375,6C12.375,6,12.375,6,12.375,6ZM10.5,6C10.5,6,12.375,6,12.375,6C12.375,6,12.375,4.5,12.375,4.5C12.375,4.5,10.5,4.5,10.5,4.5C10.5,4.5,10.5,6,10.5,6C10.5,6,10.5,6,10.5,6Z" fill="currentColor" fill-opacity="1"/></g><g transform="matrix(0,1,-1,0,13.125,-2.625)"><path d="M7.875,4.5C7.460786,4.5,7.125,4.835786,7.125,5.25C7.125,5.664214,7.460786,6,7.875,6C7.875,6,7.875,4.5,7.875,4.5C7.875,4.5,7.875,4.5,7.875,4.5ZM13.125,6C13.53921,6,13.875,5.664214,13.875,5.25C13.875,4.835786,13.53921,4.5,13.125,4.5C13.125,4.5,13.125,6,13.125,6C13.125,6,13.125,6,13.125,6ZM7.875,6C7.875,6,13.125,6,13.125,6C13.125,6,13.125,4.5,13.125,4.5C13.125,4.5,7.875,4.5,7.875,4.5C7.875,4.5,7.875,6,7.875,6C7.875,6,7.875,6,7.875,6Z" fill="currentColor" fill-opacity="1"/></g><g transform="matrix(0,1,-1,0,10.5,0)"><path d="M5.25,4.5C4.835786,4.5,4.5,4.835786,4.5,5.25C4.5,5.664214,4.835786,6,5.25,6C5.25,6,5.25,4.5,5.25,4.5C5.25,4.5,5.25,4.5,5.25,4.5ZM9,6C9.41421,6,9.75,5.664214,9.75,5.25C9.75,4.835786,9.41421,4.5,9,4.5C9,4.5,9,6,9,6C9,6,9,6,9,6ZM5.25,6C5.25,6,9,6,9,6C9,6,9,4.5,9,4.5C9,4.5,5.25,4.5,5.25,4.5C5.25,4.5,5.25,6,5.25,6C5.25,6,5.25,6,5.25,6Z" fill="currentColor" fill-opacity="1"/></g></g></svg>
|
||||
|
After Width: | Height: | Size: 2.9 KiB |
3
src/assets/icons/close.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.771 5.17211C12.0314 4.91176 12.0314 4.48965 11.771 4.2293C11.5107 3.96895 11.0886 3.96895 10.8282 4.2293L7.99984 7.0577L5.17136 4.22922C4.91101 3.96887 4.4889 3.96887 4.22855 4.22922C3.9682 4.48956 3.9682 4.91167 4.22855 5.17202L7.05703 8.00051L4.22858 10.829C3.96823 11.0893 3.96823 11.5114 4.22858 11.7718C4.48893 12.0321 4.91104 12.0321 5.17139 11.7718L7.99984 8.94332L10.8282 11.7717C11.0886 12.032 11.5107 12.032 11.771 11.7717C12.0314 11.5113 12.0314 11.0892 11.771 10.8289L8.94265 8.00051L11.771 5.17211Z" fill="#151515"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 686 B |
1
src/assets/icons/correct.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" version="1.1" width="16" height="16" viewBox="0 0 16 16"><g><g></g><g><path d="M13.0895315625,6.013678125Q13.1801615625,5.910921125,13.2286115625,5.782757125Q13.2770515625,5.654592125,13.2770515625,5.517578125Q13.2770515625,5.443709625,13.2626415625,5.371260125Q13.2482315625,5.298811125,13.2199615625,5.230565125Q13.1916915625,5.162320125,13.1506515625,5.100900125Q13.1096115625,5.039481125,13.0573815625,4.987248125Q13.0051515625,4.935015125,12.9437315625,4.893976125Q12.8823115625,4.852937125,12.8140615625,4.824668125Q12.7458215625,4.796400125,12.6733715625,4.781989125Q12.6009215625,4.767578125,12.5270515625,4.767578125Q12.3656215625,4.767578125,12.2184915625,4.833994125Q12.0713515625,4.900409125,11.9645715625,5.021477125L11.9641615625,5.021939125L7.0038015625,10.646018125000001L4.1427655625,7.389058125L4.1425725625,7.3888381249999995Q4.0357755625,7.267258125,3.8883495625,7.200538125Q3.7409235625,7.1338181249999995,3.5791015625,7.1338181249999995Q3.5052330625,7.1338181249999995,3.4327835625,7.148228125Q3.3603345625,7.162638125,3.2920885625,7.190908125Q3.2238435625,7.2191681249999995,3.1624235625,7.260208125Q3.1010045625,7.301248125,3.0487715625,7.353488125Q2.9965385625,7.405718125,2.9554995625,7.467138125Q2.9144605625,7.528558125,2.8861915625,7.596798124999999Q2.8579235625,7.665048125,2.8435125625,7.737498125Q2.8291015625,7.809948125,2.8291015625,7.8838181249999995Q2.8291015625,8.020438125,2.8772815625,8.148288125Q2.9254605625,8.276138125,3.0156305625,8.378788125L6.4391915625,12.276118125Q6.4707315625,12.312018125,6.5065615625,12.343628125Q6.618451562500001,12.442308125,6.7595815625,12.490658125Q6.9007115625,12.539008124999999,7.0496015624999995,12.529678125Q7.1984915625,12.520338125,7.3324815625,12.454738125Q7.4664615625,12.389128125,7.5651415625,12.277248125L13.0895315625,6.013679125L13.0895315625,6.013678125Z" fill-rule="evenodd" fill="currentColor" fill-opacity="1"/></g></g></svg>
|
||||
|
After Width: | Height: | Size: 2.0 KiB |
1
src/assets/icons/data.svg
Normal file
|
After Width: | Height: | Size: 5.3 KiB |
3
src/assets/icons/delete.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M14.6655 3.31655C14.7611 3.31655 14.8567 3.36691 14.9044 3.41727C14.9522 3.46763 15 3.56835 15 3.66906V4.52518C15 4.6259 14.9522 4.72662 14.9044 4.77698C14.8567 4.82734 14.7611 4.8777 14.6655 4.8777H13.1843V13.4388C13.1843 14.295 12.4676 14.9496 11.6553 15H11.5597H4.48805C3.62799 15 2.91126 14.3957 2.86348 13.5396V13.4388V4.8777H1.38225C1.14334 4.8777 1 4.72662 1 4.52518V3.71942C1 3.61871 1.04778 3.51799 1.09556 3.46763C1.14334 3.41727 1.23891 3.36691 1.33447 3.36691H14.6655V3.31655ZM11.7031 4.8777H4.34471V13.3885C4.39249 13.3885 4.39249 13.4388 4.44027 13.4388H4.48805H11.6075C11.6553 13.4388 11.7031 13.4388 11.7509 13.3885V4.8777H11.7031ZM6.92491 6.84173C7.11604 6.84173 7.30717 6.99281 7.30717 7.2446V11.1223C7.30717 11.223 7.25939 11.3237 7.2116 11.3741C7.16382 11.4245 7.06826 11.4748 6.9727 11.4748H6.25597C6.16041 11.4748 6.06485 11.4245 6.01707 11.3741C5.96928 11.3237 5.9215 11.223 5.9215 11.1223V7.2446C5.9215 7.04317 6.06485 6.84173 6.30375 6.84173H6.92491ZM9.88737 6.84173C9.98294 6.84173 10.0785 6.89209 10.1263 6.94245C10.1741 6.99281 10.2218 7.09353 10.2218 7.19424V11.0719C10.2218 11.1727 10.1741 11.2734 10.1263 11.3237C10.0785 11.3741 9.98294 11.4245 9.88737 11.4245H9.12287C9.0273 11.4245 8.93174 11.3741 8.88396 11.3237C8.83618 11.2734 8.7884 11.1727 8.7884 11.0719V7.19424C8.7884 7.09353 8.83618 6.99281 8.88396 6.94245C8.93174 6.89209 9.0273 6.84173 9.12287 6.84173H9.88737ZM9.88737 1C9.98294 1 10.0785 1.05036 10.1263 1.10072C10.1741 1.15108 10.2218 1.2518 10.2218 1.35252V2.10791C10.2218 2.20863 10.1741 2.30935 10.1263 2.35971C10.0785 2.51079 9.98294 2.56115 9.88737 2.56115H6.20819C6.01707 2.56115 5.82594 2.41007 5.82594 2.15827V1.40288C5.82594 1.20144 5.96928 1 6.20819 1H9.88737Z" fill="currentColor"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
3
src/assets/icons/edit.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M7.32955 2C7.50622 2.00003 7.67564 2.07028 7.80055 2.1953C7.92547 2.32032 7.99564 2.48987 7.99564 2.66667C7.99564 2.84346 7.92547 3.01301 7.80055 3.13804C7.67564 3.26306 7.50622 3.33331 7.32955 3.33333H3.33239V12.6667H12.6591V8.66667L12.6638 8.58867C12.6838 8.41994 12.7675 8.26524 12.8977 8.15618C13.0279 8.04712 13.1948 7.99193 13.3643 8.00189C13.5338 8.01184 13.6931 8.0862 13.8097 8.20975C13.9263 8.33331 13.9913 8.49675 13.9915 8.66667V12.6667C13.9915 13.0203 13.8511 13.3594 13.6012 13.6095C13.3514 13.8595 13.0125 14 12.6591 14H3.33239C2.97902 14 2.64012 13.8595 2.39025 13.6095C2.14038 13.3594 2 13.0203 2 12.6667V3.33333C2 2.97971 2.14038 2.64057 2.39025 2.39052C2.64012 2.14048 2.97902 2 3.33239 2H7.32955ZM13.8049 2.19533C13.9298 2.32035 14 2.48989 14 2.66667C14 2.84344 13.9298 3.01298 13.8049 3.138L7.6813 9.26667C7.55565 9.3881 7.38737 9.4553 7.2127 9.45378C7.03802 9.45226 6.87093 9.38215 6.74742 9.25855C6.6239 9.13494 6.55384 8.96773 6.55232 8.79293C6.5508 8.61814 6.61795 8.44974 6.7393 8.324L12.863 2.196C12.9879 2.07102 13.1573 2.00081 13.3339 2.00081C13.5106 2.00081 13.68 2.07102 13.8049 2.196V2.19533Z" fill="currentColor"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
5
src/assets/icons/email.svg
Normal file
@ -0,0 +1,5 @@
|
||||
<svg width="15" height="12" viewBox="0 0 15 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="Vector">
|
||||
<path id="Vector_2" d="M12.4443 0.825817H1.46413C1.40457 0.825817 1.34645 0.833899 1.29058 0.849615L6.60885 6.66281C6.74584 6.81233 6.96765 6.81233 7.10443 6.66281L12.4443 0.825817ZM13.2023 1.08063L8.91915 5.76275L13.2393 10.4848C13.3286 10.3562 13.3816 10.1954 13.3816 10.0217V1.59229C13.3816 1.39584 13.3138 1.21691 13.2027 1.08086H13.2023V1.08063ZM12.5248 10.7881L8.42378 6.30472L7.60001 7.20522C7.40284 7.42075 7.13523 7.54198 6.85633 7.54198C6.57743 7.54198 6.30982 7.42075 6.11266 7.20522L5.2893 6.30472L1.22918 10.7439C1.3027 10.7724 1.38177 10.7879 1.46433 10.7879H12.5252H12.5248V10.7881ZM0.773852 10.1573L4.79372 5.76253L0.788845 1.38529C0.771593 1.45264 0.762967 1.52224 0.762967 1.59229V10.0219C0.762967 10.0679 0.766459 10.1135 0.773852 10.1575V10.1573ZM1.46413 0.0595703H12.6804C13.0524 0.0595703 13.4089 0.220992 13.6718 0.508587C13.9347 0.795958 14.0826 1.1857 14.0826 1.59229V10.0219C14.0826 10.4285 13.9347 10.8182 13.6718 11.1056C13.4089 11.393 13.0522 11.5546 12.6804 11.5546H1.46413C1.09219 11.5546 0.735652 11.3932 0.472768 11.1056C0.209884 10.8182 0.0620117 10.4285 0.0620117 10.0219V1.59229C0.0620117 1.1857 0.209679 0.795958 0.472768 0.508587C0.735652 0.220992 1.09219 0.0595703 1.46413 0.0595703Z" fill="currentColor"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
1
src/assets/icons/export.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" version="1.1" width="16" height="16" viewBox="0 0 16 16"><defs><clipPath id="master_svg0_39_604"><rect x="0" y="0" width="16" height="16" rx="0"/></clipPath></defs><g clip-path="url(#master_svg0_39_604)"><g><path d="M4.59375,0.59375Q4.03125,1.15625,4,2L4,14Q4.03125,14.8438,4.59375,15.4062Q5.15625,15.9688,6,16L14,16Q14.8438,15.9688,15.4062,15.4062Q15.9688,14.8438,16,14L16,5L12,5Q11.5625,5,11.28125,4.71875Q11,4.4375,11,4L11,0L6,0Q5.15625,0.03125,4.59375,0.59375ZM4,9.74951Q4,9.48389,4.21875,9.21826L6.71875,6.71826Q7.25,6.28076,7.78125,6.71826Q8.21875,7.24951,7.78125,7.78076L6.5625,8.99951L12.25,8.99951Q12.9375,9.06201,13,9.74951Q12.9375,10.437,12.25,10.4995L6.5625,10.4995L7.78125,11.7183Q8.21875,12.2495,7.78125,12.7808Q7.25,13.2183,6.71875,12.7808L4.21875,10.2808Q4,10.0151,4,9.74951ZM16,4L12,0L12,4L16,4Z" fill-rule="evenodd" fill="currentColor" fill-opacity="1" style="mix-blend-mode:passthrough"/></g></g></svg>
|
||||
|
After Width: | Height: | Size: 1016 B |
1
src/assets/icons/fileUpload.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" version="1.1" width="17" height="17" viewBox="0 0 17 17"><defs><clipPath id="master_svg0_41_5342"><rect x="0" y="0" width="17" height="17" rx="0"/></clipPath></defs><g clip-path="url(#master_svg0_41_5342)"><g><path d="M15.0908015625,14.8915984375L1.9091795624999999,14.8915984375C1.4507405625,14.8915984375,1.0791015625,14.5199984375,1.0791015625,14.0614984375L1.0791015625,2.9384764375C1.0791015625,2.4800374375,1.4507405625,2.1083984375,1.9091795624999999,2.1083984375L8.051761562500001,2.1083984375C8.5102015625,2.1083984375,8.8818315625,2.4800374375,8.8818315625,2.9384764375L8.8818315625,3.9677784375C8.8818315625,4.0594584375,8.9561615625,4.1337884375,9.0478515625,4.1337884375L13.0488015625,4.1337884375C13.5073015625,4.1337884375,13.8789015625,4.5054284375,13.8789015625,4.9638684375L13.8789015625,5.9267584375L15.0908015625,5.9267584375C15.5493015625,5.9267584375,15.9209015625,6.2983984375,15.9209015625,6.7568384375L15.9209015625,14.0614984375C15.9209015625,14.5199984375,15.5493015625,14.8915984375,15.0908015625,14.8915984375ZM8.3837915625,5.1298784375C8.108721562500001,5.1298784375,7.8857415625,4.906898437500001,7.8857415625,4.6318384375L7.8857415625,3.4365184375C7.8857415625,3.2531484375,7.7370915625,3.1044924375000003,7.5537115625,3.1044924375000003L2.4072215625,3.1044924375000003C2.2238515625,3.1044924375000003,2.0751955625000003,3.2531484375,2.0751955625000003,3.4365184375L2.0751955625000003,13.5634984375C2.0751955625000003,13.7468984375,2.2308715625,13.8954984375,2.4142315625,13.8954984375L3.4697215625,13.8954984375C3.6531015625,13.8954984375,3.8017615625,13.7468984375,3.8017615625,13.5634984375L3.8017615625,6.7568384375C3.8017615625,6.2983984375,4.1733915625,5.9267584375,4.6318315625,5.9267584375L12.8828015625,5.9267584375L12.8828015625,5.4619184375C12.8828015625,5.2785384375,12.7342015625,5.1298784375,12.5508015625,5.1298784375L8.8818315625,5.1298784375M14.9248015625,7.2548784375C14.9248015625,7.0715084375,14.7761015625,6.9228484375,14.5928015625,6.9228484375L5.1298815625,6.9228484375C4.9465115625,6.9228484375,4.7978515625,7.0715084375,4.7978515625,7.2548784375L4.7978515625,13.8954984375L14.5928015625,13.8954984375C14.7761015625,13.8954984375,14.9248015625,13.7468984375,14.9248015625,13.5634984375L14.9248015625,7.2548784375ZM10.8019515625,10.2232384375L10.3600515625,9.7827984375L10.3600515625,12.2353984375C10.3600615625,12.5103984375,10.1370715625,12.7333984375,9.8620115625,12.7333984375L9.8601315625,12.7333984375C9.5850715625,12.7333984375,9.3620815625,12.5103984375,9.3620815625,12.2353984375L9.3620815625,9.782888437499999L8.9201715625,10.2232384375C8.7257415625,10.4167784375,8.411431562499999,10.4166984375,8.217111562500001,10.2230584375C8.0227815625,10.0294184375,8.021601562499999,9.7151084375,8.214451562499999,9.5199984375L9.5081815625,8.2305684375C9.7033115625,8.036358437499999,10.0187115625,8.036358437499999,10.2138415625,8.2305684375L11.5076015625,9.5199984375C11.6971015625,9.715628437500001,11.6944015625,10.0272184375,11.5015015625,10.2194984375C11.3085015625,10.4117784375,10.9969415625,10.4134484375,10.8019515625,10.2232384375Z" fill="currentColor" fill-opacity="1"/></g></g></svg>
|
||||
|
After Width: | Height: | Size: 3.2 KiB |
1
src/assets/icons/import.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" version="1.1" width="16" height="16" viewBox="0 0 16 16"><defs><clipPath id="master_svg0_1_250"><rect x="0" y="0" width="16" height="16" rx="0"/></clipPath></defs><g clip-path="url(#master_svg0_1_250)"><g><path d="M4,2L4,9L9.4375,9L8.21875,7.78125Q7.78125,7.25,8.21875,6.71875Q8.75,6.28125,9.28125,6.71875L11.78125,9.21875Q12.21875,9.75,11.78125,10.2812L9.28125,12.7812Q8.75,13.2188,8.21875,12.7812Q7.78125,12.25,8.21875,11.7188L9.4375,10.5L4,10.5L4,14Q4.03125,14.8438,4.59375,15.4062Q5.15625,15.9688,6,16L14,16Q14.8438,15.9688,15.4062,15.4062Q15.9688,14.8438,16,14L16,5L12,5Q11.5625,5,11.28125,4.71875Q11,4.4375,11,4L11,0L6,0Q5.15625,0.03125,4.59375,0.59375Q4.03125,1.15625,4,2ZM16,4L12,0L12,4L16,4Z" fill-rule="evenodd" fill="currentColor" fill-opacity="1" style="mix-blend-mode:passthrough"/></g></g></svg>
|
||||
|
After Width: | Height: | Size: 904 B |
11
src/assets/icons/interrupt.svg
Normal file
@ -0,0 +1,11 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_4756_1572)">
|
||||
<path d="M16 8C16 12.4182 12.4182 16 8 16C3.58179 16 0 12.4182 0 8C0 3.58179 3.58179 0 8 0C12.4182 0 16 3.58179 16 8Z" fill="#333"/>
|
||||
<path d="M5.51721 5.51758H10.4827V10.4831H5.51721V5.51758Z" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_4756_1572">
|
||||
<rect width="16" height="16" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 457 B |
6
src/assets/icons/interruptLine.svg
Normal file
@ -0,0 +1,6 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="Group 114">
|
||||
<path id="Vector" d="M10.6456 10.0696C10.6456 10.3492 10.4168 10.578 10.1372 10.578H5.86385C5.58305 10.578 5.35547 10.3504 5.35547 10.0696V5.92928C5.35547 5.64848 5.58305 5.4209 5.86385 5.4209H10.1372C10.4168 5.4209 10.6456 5.64967 10.6456 5.92928V10.0696Z" fill="currentColor" />
|
||||
<path id="Vector_2" d="M16 8.00205C16.0044 5.87857 15.1627 3.84068 13.661 2.33916C12.1595 0.837257 10.1216 -0.00435197 7.99816 1.6925e-05C5.87627 -0.00335904 3.84057 0.838448 2.34085 2.33956C0.837947 3.84008 -0.00465313 5.87817 0.000112932 8.00205C-0.0112065 10.1269 0.828812 12.1682 2.3325 13.6695C3.83282 15.173 5.87309 16.0124 7.99697 15.9999C12.4026 15.9552 15.9998 12.4057 15.9998 8.00205H16ZM0.939822 8.00205C0.938829 6.12938 1.68194 4.33297 3.00571 3.0084C4.32949 1.68383 6.1255 0.939529 7.99816 0.939529C11.9218 0.939529 15.1044 4.1189 15.1044 8.00205C15.1044 11.8852 11.9218 15.0624 7.99816 15.0624C6.12331 15.0709 4.32273 14.3296 2.99717 13.0036C1.67161 11.6777 0.930885 9.8769 0.939822 8.00205Z" fill="currentColor" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
5
src/assets/icons/lock.svg
Normal file
@ -0,0 +1,5 @@
|
||||
<svg width="12" height="15" viewBox="0 0 12 15" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="Vector">
|
||||
<path id="Vector_2" d="M11.4471 14.4718H1.22356C0.697827 14.5303 0.230469 13.828 0.230469 13.477V6.86365C0.230469 6.16135 0.989884 5.69314 1.39886 5.69314H1.98315V3.9373C1.98315 1.94752 4.08626 0.425781 6.07245 0.425781C8.05864 0.425781 10.1618 1.94752 10.1618 3.9373V5.69314H10.7459C11.2132 5.69314 11.9143 6.10286 11.9143 6.86365V13.477C11.8559 13.828 11.9726 14.4718 11.4469 14.4718H11.4471ZM8.93498 3.9373C8.93498 2.59117 7.3576 1.59629 6.01407 1.59629C4.67055 1.59629 3.09317 2.59117 3.09317 3.9373V5.69314H8.93515V3.9373H8.93498ZM10.6877 6.86348H1.34049V13.3013H10.6877L10.863 13.477L10.6877 6.86365V6.86348ZM6.59819 10.1995V11.5457C6.59819 11.8967 6.36451 12.131 6.01407 12.131C5.66364 12.131 5.42996 11.8384 5.42996 11.5457V10.1995C5.07953 10.0239 4.84585 9.61438 4.84585 9.20466C4.84585 8.56084 5.37175 8.03415 6.01424 8.03415C6.65673 8.03415 7.18264 8.56084 7.18264 9.20466C7.18264 9.61438 6.94896 10.0239 6.59853 10.1995H6.59819Z" fill="currentColor"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
11
src/assets/icons/plus.svg
Normal file
@ -0,0 +1,11 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_3949_728)">
|
||||
<path d="M8 3.3335V12.6668" stroke="currentColor" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M3.33331 8H12.6666" stroke="currentColor" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_3949_728">
|
||||
<rect width="16" height="16" fill="currentColor"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 499 B |
5
src/assets/icons/plusFill.svg
Normal file
@ -0,0 +1,5 @@
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="Subtract">
|
||||
<path id="Subtract_2" d="M16 0C18.2091 0 20 1.79086 20 4V16C20 18.2091 18.2091 20 16 20H4C1.79086 20 3.22133e-08 18.2091 0 16V4C0 1.79086 1.79086 3.22128e-08 4 0H16ZM10.1113 5C9.74314 5 9.44434 5.2988 9.44434 5.66699V9.44531H5.66699C5.2988 9.44531 5 9.74411 5 10.1123C5 10.4805 5.2988 10.7793 5.66699 10.7793H9.44434V14.5557C9.44452 14.9237 9.74325 15.2217 10.1113 15.2217C10.4793 15.2215 10.7772 14.9236 10.7773 14.5557V10.7793H14.5557L14.6895 10.7656C14.9934 10.7036 15.2217 10.4346 15.2217 10.1123C15.2217 9.79004 14.9934 9.52105 14.6895 9.45898L14.5557 9.44531H10.7773V5.66699C10.7773 5.29888 10.4794 5.00013 10.1113 5Z" fill="currentColor"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 773 B |
1
src/assets/icons/refresh.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" version="1.1" width="16" height="16" viewBox="0 0 16 16"><defs><clipPath id="master_svg0_41_946"><rect x="0.5" y="0" width="15" height="16" rx="0"/></clipPath></defs><g><rect x="0" y="0" width="16" height="16" rx="0" fill="#000000" fill-opacity="0" style="mix-blend-mode:passthrough"/><g clip-path="url(#master_svg0_41_946)"><g transform="matrix(1,0,0,-1,0,30.6875)"><g><path d="M3.96875,25.87495Q3.15625,25.03125,2.78125,24Q2.625,23.625,2.28125,23.4375Q1.90625,23.28125,1.5,23.40625Q1.125,23.5625,0.9375,23.90625Q0.78125,24.28125,0.90625,24.6875Q1.40625,26.12495,2.5625,27.28125Q3.9375,28.65625,5.71875,29.12495Q7.46875,29.56255,9.25,29.12495Q11.0312,28.68755,12.4062,27.31255L13.7188,28.62495Q14.0938,28.96875,14.5312,28.78125Q14.9688,28.59375,15,28.09375L15,24.09375Q14.9375,23.40625,14.25,23.34375L14,23.34375L10.25,23.34375Q9.75,23.375,9.5625,23.8125Q9.375,24.25,9.71875,24.625L11,25.90625Q9.46875,27.34375,7.46875,27.34375Q5.5,27.34375,3.96875,25.87495ZM0,20.59375Q0.0625,21.28125,0.75,21.34375L1,21.34375L4.75,21.34375Q5.25,21.3125,5.4375,20.875Q5.625,20.4375,5.28125,20.0625L4,18.78125Q5.53125,17.34375,7.53125,17.34375Q9.5,17.34375,11.0312,18.8125Q11.8438,19.65625,12.2188,20.6875Q12.375,21.0625,12.7188,21.25Q13.0938,21.40625,13.5,21.28125Q13.875,21.125,14.0625,20.78125Q14.25,20.40625,14.0938,20Q13.5938,18.5625,12.4375,17.40625Q11.0625,16.03125,9.28125,15.5625Q7.53125,15.125,5.75,15.5625Q3.96875,16,2.59375,17.375L1.28125,16.0625Q0.90625,15.71875,0.46875,15.90625Q0.03125,16.09375,0,16.59375L0,20.34375L0,20.59375Z" fill="currentColor" fill-opacity="1" style="mix-blend-mode:passthrough"/></g></g></g></g></svg>
|
||||
|
After Width: | Height: | Size: 1.7 KiB |
1
src/assets/icons/save.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" version="1.1" width="15" height="16" viewBox="-0.984375 0 15 16"><defs><clipPath id="master_svg0_1_255"><rect x="0" y="0" width="14" height="16" rx="0"/></clipPath></defs><g clip-path="url(#master_svg0_1_255)"><g transform="matrix(1,0,0,-1,0,30)"><g><path d="M1.872765,29Q1.089285,28.9688,0.566964,28.4062Q0.0446429,27.8438,0.015625,27L0.015625,17Q0.0446429,16.15625,0.566964,15.59375Q1.089285,15.03125,1.872765,15L11.158525,15Q11.941925,15.03125,12.464325,15.59375Q12.986625,16.15625,13.015625,17L13.015625,24.59375Q13.015625,25.4062,12.464325,26L10.229925,28.4062Q9.678575,29,8.924105,29L1.872765,29ZM1.872765,26Q1.872765,26.4375,2.133925,26.7188Q2.395085,27,2.801335,27L8.372765,27Q8.779015,27,9.040175,26.7188Q9.301335,26.4375,9.301335,26L9.301335,24Q9.301335,23.5625,9.040175,23.28125Q8.779015,23,8.372765,23L2.801335,23Q2.395085,23,2.133925,23.28125Q1.872765,23.5625,1.872765,24L1.872765,26ZM6.515625,21Q7.560265,20.96875,8.111605,20Q8.633925,19,8.111605,18Q7.560265,17.03125,6.515625,17Q5.470985,17.03125,4.919645,18Q4.397325,19,4.919645,20Q5.470985,20.96875,6.515625,21Z" fill="currentColor" fill-opacity="1" style="mix-blend-mode:passthrough"/></g></g></g></svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
1
src/assets/icons/sort.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" version="1.1" width="18" height="18" viewBox="0 0 18 18"><g><rect x="0" y="0" width="18" height="18" rx="0" fill="none" fill-opacity="1"/><g><g><rect x="3" y="3" width="4.5" height="12" rx="0" fill="none" fill-opacity="1"/><path d="M2.25,15L2.25,3Q2.25,2.9261315,2.264411,2.853682Q2.278822,2.781233,2.30709,2.712987Q2.335359,2.644742,2.376398,2.583322Q2.417437,2.521903,2.46967,2.46967Q2.521903,2.417437,2.583322,2.376398Q2.644742,2.335359,2.712987,2.30709Q2.781233,2.278822,2.853682,2.264411Q2.9261315,2.25,3,2.25L7.5,2.25Q7.57387,2.25,7.64632,2.264411Q7.71877,2.278822,7.78701,2.30709Q7.85526,2.335359,7.91668,2.376398Q7.9781,2.417437,8.03033,2.46967Q8.08256,2.521903,8.1236,2.583322Q8.16464,2.644742,8.192910000000001,2.712987Q8.22118,2.781233,8.23559,2.853682Q8.25,2.9261315,8.25,3L8.25,15Q8.25,15.0739,8.23559,15.1463Q8.22118,15.2188,8.192910000000001,15.287Q8.16464,15.3553,8.1236,15.4167Q8.08256,15.4781,8.03033,15.5303Q7.9781,15.5826,7.91668,15.6236Q7.85526,15.6646,7.78701,15.6929Q7.71877,15.7212,7.64632,15.7356Q7.57387,15.75,7.5,15.75L3,15.75Q2.9261315,15.75,2.853682,15.7356Q2.781233,15.7212,2.712987,15.6929Q2.644742,15.6646,2.583322,15.6236Q2.521903,15.5826,2.46967,15.5303Q2.417437,15.4781,2.376398,15.4167Q2.335359,15.3553,2.30709,15.287Q2.278822,15.2188,2.264411,15.1463Q2.25,15.0739,2.25,15ZM3.75,3.75L3.75,14.25L6.75,14.25L6.75,3.75L3.75,3.75Z" fill="currentColor" fill-opacity="1"/></g><g><rect x="10.125" y="3" width="4.875" height="4.875" rx="0" fill="none" fill-opacity="1"/><path d="M9.375,7.875L9.375,3Q9.375,2.9261315,9.389410999999999,2.853682Q9.403822,2.781233,9.43209,2.712987Q9.460359,2.644742,9.501398,2.583322Q9.542437,2.521903,9.59467,2.46967Q9.646903,2.417437,9.708322,2.376398Q9.769742,2.335359,9.837987,2.30709Q9.906233,2.278822,9.978682,2.264411Q10.0511315,2.25,10.125,2.25L15,2.25Q15.07387,2.25,15.14632,2.264411Q15.21877,2.278822,15.28701,2.30709Q15.355260000000001,2.335359,15.41668,2.376398Q15.478100000000001,2.417437,15.53033,2.46967Q15.58256,2.521903,15.6236,2.583322Q15.66464,2.644742,15.692910000000001,2.712987Q15.72118,2.781233,15.73559,2.853682Q15.75,2.9261315,15.75,3L15.75,7.875Q15.75,7.94887,15.73559,8.02132Q15.72118,8.09377,15.692910000000001,8.16201Q15.66464,8.230260000000001,15.6236,8.29168Q15.58256,8.353100000000001,15.53033,8.40533Q15.478100000000001,8.45756,15.41668,8.4986Q15.355260000000001,8.53964,15.28701,8.567910000000001Q15.21877,8.59618,15.14632,8.61059Q15.07387,8.625,15,8.625L10.125,8.625Q10.0511315,8.625,9.978682,8.61059Q9.906233,8.59618,9.837987,8.567910000000001Q9.769742,8.53964,9.708322,8.4986Q9.646903,8.45756,9.59467,8.40533Q9.542437,8.353100000000001,9.501398,8.29168Q9.460359,8.230260000000001,9.43209,8.16201Q9.403822,8.09377,9.389410999999999,8.02132Q9.375,7.94887,9.375,7.875ZM10.875,3.75L10.875,7.125L14.25,7.125L14.25,3.75L10.875,3.75Z" fill="currentColor" fill-opacity="1"/></g><g><rect x="10.125" y="10.125" width="4.875" height="4.875" rx="0" fill="none" fill-opacity="1"/><path d="M9.375,15L9.375,10.125Q9.375,10.0511315,9.389410999999999,9.978682Q9.403822,9.906233,9.43209,9.837987Q9.460359,9.769742,9.501398,9.708322Q9.542437,9.646903,9.59467,9.59467Q9.646903,9.542437,9.708322,9.501398Q9.769742,9.460359,9.837987,9.43209Q9.906233,9.403822,9.978682,9.389410999999999Q10.0511315,9.375,10.125,9.375L15,9.375Q15.07387,9.375,15.14632,9.389410999999999Q15.21877,9.403822,15.28701,9.43209Q15.355260000000001,9.460359,15.41668,9.501398Q15.478100000000001,9.542437,15.53033,9.59467Q15.58256,9.646903,15.6236,9.708322Q15.66464,9.769742,15.692910000000001,9.837987Q15.72118,9.906233,15.73559,9.978682Q15.75,10.0511315,15.75,10.125L15.75,15Q15.75,15.07387,15.73559,15.14632Q15.72118,15.21877,15.692910000000001,15.28701Q15.66464,15.355260000000001,15.6236,15.41668Q15.58256,15.478100000000001,15.53033,15.53033Q15.478100000000001,15.58256,15.41668,15.6236Q15.355260000000001,15.66464,15.28701,15.692910000000001Q15.21877,15.72118,15.14632,15.73559Q15.07387,15.75,15,15.75L10.125,15.75Q10.0511315,15.75,9.978682,15.73559Q9.906233,15.72118,9.837987,15.692910000000001Q9.769742,15.66464,9.708322,15.6236Q9.646903,15.58256,9.59467,15.53033Q9.542437,15.478100000000001,9.501398,15.41668Q9.460359,15.355260000000001,9.43209,15.28701Q9.403822,15.21877,9.389410999999999,15.14632Q9.375,15.07387,9.375,15ZM10.875,10.875L10.875,14.25L14.25,14.25L14.25,10.875L10.875,10.875Z" fill="currentColor" fill-opacity="1"/></g></g></g></svg>
|
||||
|
After Width: | Height: | Size: 4.4 KiB |
1
src/assets/icons/stop.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" version="1.1" width="12" height="16" viewBox="0 0 12 16"><defs><clipPath id="master_svg0_30_03326"><rect x="0" y="0" width="12" height="16" rx="0"/></clipPath></defs><g clip-path="url(#master_svg0_30_03326)"><g transform="matrix(1,0,0,-1,0,30.70355224609375)"><g><path d="M2.28125,29.141176123046876Q1.53125,29.578676123046876,0.78125,29.172376123046874Q0.03125,28.734876123046874,0,27.859876123046874L0,16.859876123046874Q0.03125,15.984878123046876,0.78125,15.547378123046874Q1.53125,15.141128123046874,2.28125,15.578628123046874L11.2812,21.078626123046874Q11.9688,21.516126123046874,12,22.359876123046874Q11.9688,23.172376123046874,11.2812,23.641126123046874L2.28125,29.141176123046876Z" fill="currentColor" fill-opacity="1" style="mix-blend-mode:passthrough"/></g></g></g></svg>
|
||||
|
After Width: | Height: | Size: 876 B |
5
src/assets/icons/zoomIn.svg
Normal file
@ -0,0 +1,5 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M3.14184 2.19544C3.01669 2.07029 2.84705 2 2.67018 2C2.49331 2 2.32367 2.07029 2.19852 2.19544C2.07353 2.32074 2.00333 2.4906 2.00333 2.66769C2.00333 2.84479 2.07353 3.01464 2.19852 3.13994L3.14184 2.19544ZM5.16195 6.08037C5.2871 6.20552 5.45674 6.27581 5.63361 6.27581C5.81048 6.27581 5.98012 6.20552 6.10527 6.08037C6.23026 5.95507 6.30046 5.78522 6.30046 5.60812C6.30046 5.43103 6.23026 5.26117 6.10527 5.13587L5.16195 6.08037ZM2.19852 3.13994L5.16195 6.08037L6.10527 5.13587L3.14184 2.19544L2.19852 3.13994ZM2.19852 12.8523C2.07225 12.9775 2.00084 13.1479 2.00001 13.3258C1.99917 13.5038 2.06898 13.6748 2.19407 13.8012C2.31916 13.9277 2.48929 13.9992 2.66703 14C2.84477 14.0008 3.01556 13.9309 3.14184 13.8057L2.19852 12.8523ZM6.10527 10.8653C6.23154 10.74 6.30295 10.5697 6.30378 10.3917C6.30461 10.2137 6.23481 10.0427 6.10972 9.9163C5.98462 9.78987 5.8145 9.71837 5.63676 9.71753C5.45902 9.7167 5.28822 9.78659 5.16195 9.91184L6.10527 10.8653ZM3.14184 13.8057L6.10527 10.8653L5.16195 9.91184L2.19852 12.8523L3.14184 13.8057ZM12.8776 13.8057C13.0041 13.9237 13.1714 13.988 13.3443 13.9849C13.5172 13.9819 13.6822 13.9117 13.8045 13.7893C13.9268 13.6669 13.9968 13.5017 13.9999 13.3286C14.0029 13.1554 13.9388 12.9879 13.8209 12.8612L12.8776 13.8057ZM10.8841 9.92075C10.8228 9.85771 10.7494 9.8076 10.6684 9.77338C10.5874 9.73917 10.5004 9.72154 10.4125 9.72154C10.3246 9.72154 10.2375 9.73917 10.1565 9.77338C10.0755 9.8076 10.0022 9.85771 9.94082 9.92075C9.81583 10.0461 9.74562 10.2159 9.74562 10.393C9.74562 10.5701 9.81583 10.74 9.94082 10.8653L10.8841 9.92075ZM13.8209 12.8612L10.8841 9.92075L9.94082 10.8653L12.8776 13.8057L13.8209 12.8612ZM13.7942 3.13994C13.9192 3.01464 13.9894 2.84479 13.9894 2.66769C13.9894 2.4906 13.9192 2.32074 13.7942 2.19544C13.6664 2.07186 13.4957 2.00278 13.3181 2.00278C13.1404 2.00278 12.9697 2.07186 12.842 2.19544L13.7942 3.13994ZM9.90522 5.13587C9.78023 5.26117 9.71003 5.43103 9.71003 5.60812C9.71003 5.78522 9.78023 5.95507 9.90522 6.08037C10.033 6.20396 10.2037 6.27303 10.3813 6.27303C10.559 6.27303 10.7297 6.20396 10.8574 6.08037L9.90522 5.13587ZM12.842 2.19544L9.90522 5.13587L10.8574 6.08037L13.7942 3.13994L12.842 2.19544Z" fill="#151515"/>
|
||||
<path d="M10.7128 2.20027C10.5394 2.20027 10.373 2.26931 10.2504 2.39218C10.1278 2.51506 10.0589 2.68172 10.0589 2.8555C10.0589 3.02928 10.1278 3.19594 10.2504 3.31881C10.373 3.44169 10.5394 3.51073 10.7128 3.51073V2.20027ZM13.3286 2.8555H13.9826C13.9803 2.68243 13.9107 2.51708 13.7885 2.39469C13.6664 2.27229 13.5013 2.20254 13.3286 2.20027V2.8555ZM12.6747 5.47641C12.6747 5.65018 12.7436 5.81684 12.8662 5.93972C12.9888 6.0626 13.1552 6.13163 13.3286 6.13163C13.5021 6.13163 13.6684 6.0626 13.791 5.93972C13.9137 5.81684 13.9826 5.65018 13.9826 5.47641H12.6747ZM10.7128 3.51073H13.3286V2.20027H10.7128V3.51073ZM12.6921 2.8555V5.47641H14V2.8555H12.6921ZM14 10.7182C14 10.5444 13.9311 10.3778 13.8085 10.2549C13.6858 10.132 13.5195 10.063 13.3461 10.063C13.1726 10.063 13.0063 10.132 12.8836 10.2549C12.761 10.3778 12.6921 10.5444 12.6921 10.7182H14ZM13.3286 13.3391V13.9944C13.5013 13.9921 13.6664 13.9223 13.7885 13.7999C13.9107 13.6775 13.9803 13.5122 13.9826 13.3391H13.3286ZM10.7128 12.6839C10.5394 12.6839 10.373 12.7529 10.2504 12.8758C10.1278 12.9987 10.0589 13.1653 10.0589 13.3391C10.0589 13.5129 10.1278 13.6796 10.2504 13.8024C10.373 13.9253 10.5394 13.9944 10.7128 13.9944V12.6839ZM12.6921 10.7182V13.3391H14V10.7182H12.6921ZM13.3286 12.6839H10.7128V13.9944H13.3286V12.6839ZM5.48122 13.9944C5.65466 13.9944 5.82099 13.9253 5.94363 13.8024C6.06627 13.6796 6.13517 13.5129 6.13517 13.3391C6.13517 13.1653 6.06627 12.9987 5.94363 12.8758C5.82099 12.7529 5.65466 12.6839 5.48122 12.6839V13.9944ZM2.86542 13.3391H2.22891C2.22891 13.5129 2.29781 13.6796 2.42045 13.8024C2.54309 13.9253 2.70942 13.9944 2.88286 13.9944V13.3391H2.86542ZM3.51937 10.7182C3.51937 10.5444 3.45047 10.3778 3.32784 10.2549C3.2052 10.132 3.03886 10.063 2.86542 10.063C2.69199 10.063 2.52565 10.132 2.40301 10.2549C2.28037 10.3778 2.21147 10.5444 2.21147 10.7182H3.51937ZM5.48122 12.6839H2.86542V13.9944H5.48122V12.6839ZM3.53681 13.3391V10.7182H2.22891V13.3391H3.53681ZM2.22891 5.47641C2.22891 5.65018 2.29781 5.81684 2.42045 5.93972C2.54309 6.0626 2.70942 6.13163 2.88286 6.13163C3.0563 6.13163 3.22263 6.0626 3.34527 5.93972C3.46791 5.81684 3.53681 5.65018 3.53681 5.47641H2.22891ZM2.86542 2.8555V2.20027C2.69503 2.20483 2.53314 2.27587 2.41424 2.39825C2.29535 2.52064 2.22885 2.68471 2.22891 2.8555H2.86542ZM5.48122 3.51073C5.65466 3.51073 5.82099 3.44169 5.94363 3.31881C6.06627 3.19594 6.13517 3.02928 6.13517 2.8555C6.13517 2.68172 6.06627 2.51506 5.94363 2.39218C5.82099 2.26931 5.65466 2.20027 5.48122 2.20027V3.51073ZM3.53681 5.47641V2.8555H2.22891V5.47641H3.53681ZM2.86542 3.51073H5.48122V2.20027H2.86542V3.51073Z" fill="#151515"/>
|
||||
<path d="M10.5013 2.00586C10.3279 2.00586 10.1616 2.07489 10.0389 2.19777C9.91629 2.32065 9.84739 2.48731 9.84739 2.66108C9.84739 2.83486 9.91629 3.00152 10.0389 3.1244C10.1616 3.24728 10.3279 3.31631 10.5013 3.31631V2.00586ZM13.1171 2.66108H13.7711C13.7688 2.48801 13.6992 2.32266 13.5771 2.20027C13.4549 2.07788 13.2899 2.00812 13.1171 2.00586V2.66108ZM12.4632 5.28199C12.4632 5.45577 12.5321 5.62243 12.6547 5.74531C12.7774 5.86818 12.9437 5.93722 13.1171 5.93722C13.2906 5.93722 13.4569 5.86818 13.5796 5.74531C13.7022 5.62243 13.7711 5.45577 13.7711 5.28199H12.4632ZM10.5013 3.31631H13.1171V2.00586H10.5013V3.31631ZM12.4806 2.66108V5.28199H13.7885V2.66108H12.4806ZM13.7885 10.5238C13.7885 10.35 13.7196 10.1834 13.597 10.0605C13.4744 9.93761 13.308 9.86858 13.1346 9.86858C12.9611 9.86858 12.7948 9.93761 12.6722 10.0605C12.5495 10.1834 12.4806 10.35 12.4806 10.5238H13.7885ZM13.1171 13.1447V13.7999C13.2899 13.7977 13.4549 13.7279 13.5771 13.6055C13.6992 13.4831 13.7688 13.3178 13.7711 13.1447H13.1171ZM10.5013 12.4895C10.3279 12.4895 10.1616 12.5585 10.0389 12.6814C9.91629 12.8043 9.84739 12.9709 9.84739 13.1447C9.84739 13.3185 9.91629 13.4851 10.0389 13.608C10.1616 13.7309 10.3279 13.7999 10.5013 13.7999V12.4895ZM12.4806 10.5238V13.1447H13.7885V10.5238H12.4806ZM13.1171 12.4895H10.5013V13.7999H13.1171V12.4895ZM5.26975 13.7999C5.44319 13.7999 5.60952 13.7309 5.73216 13.608C5.8548 13.4851 5.9237 13.3185 5.9237 13.1447C5.9237 12.9709 5.8548 12.8043 5.73216 12.6814C5.60952 12.5585 5.44319 12.4895 5.26975 12.4895V13.7999ZM2.65395 13.1447H2.01744C2.01744 13.3185 2.08634 13.4851 2.20898 13.608C2.33162 13.7309 2.49795 13.7999 2.67139 13.7999V13.1447H2.65395ZM3.3079 10.5238C3.3079 10.35 3.239 10.1834 3.11636 10.0605C2.99372 9.93761 2.82739 9.86858 2.65395 9.86858C2.48051 9.86858 2.31418 9.93761 2.19154 10.0605C2.0689 10.1834 2 10.35 2 10.5238H3.3079ZM5.26975 12.4895H2.65395V13.7999H5.26975V12.4895ZM3.32534 13.1447V10.5238H2.01744V13.1447H3.32534ZM2.01744 5.28199C2.01744 5.45577 2.08634 5.62243 2.20898 5.74531C2.33162 5.86818 2.49795 5.93722 2.67139 5.93722C2.84483 5.93722 3.01116 5.86818 3.1338 5.74531C3.25644 5.62243 3.32534 5.45577 3.32534 5.28199H2.01744ZM2.65395 2.66108V2.00586C2.48355 2.01041 2.32166 2.08145 2.20277 2.20384C2.08388 2.32623 2.01738 2.49029 2.01744 2.66108H2.65395ZM5.26975 3.31631C5.44319 3.31631 5.60952 3.24728 5.73216 3.1244C5.8548 3.00152 5.9237 2.83486 5.9237 2.66108C5.9237 2.48731 5.8548 2.32065 5.73216 2.19777C5.60952 2.07489 5.44319 2.00586 5.26975 2.00586V3.31631ZM3.32534 5.28199V2.66108H2.01744V5.28199H3.32534ZM2.65395 3.31631H5.26975V2.00586H2.65395V3.31631Z" fill="#151515"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 7.4 KiB |
BIN
src/assets/images/auth/auth-bg.jpg
Normal file
|
After Width: | Height: | Size: 898 KiB |
BIN
src/assets/images/index/bg.jpg
Normal file
|
After Width: | Height: | Size: 170 KiB |
BIN
src/assets/images/index/bg_old.jpg
Normal file
|
After Width: | Height: | Size: 243 KiB |
BIN
src/assets/images/index/sider-radar.png
Normal file
|
After Width: | Height: | Size: 636 KiB |
BIN
src/assets/images/logo/logo.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
src/assets/logo.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
49
src/components/Base/Layout/LoginLayout/index.vue
Normal file
@ -0,0 +1,49 @@
|
||||
<script lang="ts">
|
||||
import { defineComponent, reactive } from "vue";
|
||||
export default defineComponent({
|
||||
name: "LoginLayout",
|
||||
});
|
||||
</script>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { info } from "@/config/index.ts";
|
||||
|
||||
const prefix = "login-layout";
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div :class="prefix" :style="`background-image: url(${info.bgImg})`">
|
||||
<div :class="`${prefix}-form`">
|
||||
<RouterView />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
$prefix: "login-layout";
|
||||
|
||||
.#{$prefix} {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
background-size: 100% auto;
|
||||
&-form {
|
||||
position: absolute;
|
||||
left: 384px;
|
||||
top: 253px;
|
||||
width: 447px;
|
||||
height: 300px;
|
||||
opacity: 1;
|
||||
background: #fff;
|
||||
box-shadow: 0px 4px 4px 0px rgba(0, 0, 0, 0.16);
|
||||
border-radius: 16px;
|
||||
// border-radius: 16px 16px 16px 16px;
|
||||
}
|
||||
}
|
||||
.global-login-page {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
}
|
||||
</style>
|
||||
85
src/components/Base/Layout/SystemLayout/Content/index.vue
Normal file
@ -0,0 +1,85 @@
|
||||
<script lang="ts">
|
||||
import { defineComponent, defineAsyncComponent } from "vue";
|
||||
export default defineComponent({
|
||||
name: "SystemLayoutContent",
|
||||
});
|
||||
</script>
|
||||
|
||||
<script setup lang="ts">
|
||||
// import LoginDrawer from '@/components/Service/Login/index/index.vue'
|
||||
import ErrorLoading from "@/components/Module/AsyncContent/ErrorLoading.vue";
|
||||
import { useRoute } from "vue-router";
|
||||
|
||||
const asyncContent = defineAsyncComponent({
|
||||
loader: () => import("@/components/Module/AsyncContent/index.vue"),
|
||||
delay: 2000,
|
||||
loadingComponent: ErrorLoading,
|
||||
errorComponent: ErrorLoading,
|
||||
timeout: 10000,
|
||||
});
|
||||
|
||||
interface propsType {
|
||||
contentLoading: boolean;
|
||||
}
|
||||
|
||||
let props = withDefaults(defineProps<propsType>(), {
|
||||
// 默认值
|
||||
contentLoading: true,
|
||||
});
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
const prefix = "_system-layout-content";
|
||||
</script>
|
||||
|
||||
<!-- bgImg
|
||||
SiderImg-->
|
||||
<template>
|
||||
<a-layout-content :class="prefix">
|
||||
<div class="router-view-box">
|
||||
<div v-if="props.contentLoading">
|
||||
<Suspense>
|
||||
<asyncContent>
|
||||
<RouterView :key="route.path" />
|
||||
</asyncContent>
|
||||
<template #fallback>
|
||||
<div class="empty-box">
|
||||
<a-spin />
|
||||
</div>
|
||||
</template>
|
||||
</Suspense>
|
||||
</div>
|
||||
</div>
|
||||
<!-- <LoginDrawer /> -->
|
||||
</a-layout-content>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
$prefix: "_system-layout-content";
|
||||
|
||||
.#{$prefix} {
|
||||
width: 100%;
|
||||
// background-color: #f4f6fc;
|
||||
width: 100%;
|
||||
height: calc(100vh - 60px);
|
||||
overflow: hidden;
|
||||
.router-view-box {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
// background-color: #f7f8fa;
|
||||
overflow: auto;
|
||||
box-sizing: border-box;
|
||||
> div {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.empty-box {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
58
src/components/Base/Layout/SystemLayout/Header/index.vue
Normal file
@ -0,0 +1,58 @@
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "vue";
|
||||
export default defineComponent({
|
||||
name: "SystemLayoutHeader",
|
||||
});
|
||||
</script>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Info from "@/components/Base/Tools/Info/index.vue";
|
||||
|
||||
const prefix = "_system-layout-header";
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<a-layout-header theme="light" :class="prefix">
|
||||
<div :class="`${prefix}-wrapper`">
|
||||
<div :class="`${prefix}-wrapper-grid`">
|
||||
<Info />
|
||||
</div>
|
||||
<div :class="`${prefix}-wrapper-grid`">
|
||||
<!-- <UserBox /> -->
|
||||
</div>
|
||||
</div>
|
||||
</a-layout-header>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
$prefix: "_system-layout-header";
|
||||
|
||||
.#{$prefix} {
|
||||
padding: 0;
|
||||
background-color: #fff;
|
||||
height: 60px;
|
||||
position: reactive;
|
||||
z-index: 20;
|
||||
box-shadow: 0px 2px 4px 0px rgba(133, 99, 244, 0.2);
|
||||
box-sizing: border-box;
|
||||
&-wrapper {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 20px;
|
||||
&-left {
|
||||
font-size: 16px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
&-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,44 @@
|
||||
import { h } from 'vue'
|
||||
import Icon from '@/components/Module/Icon/index.vue'
|
||||
|
||||
|
||||
export const useMain = () => {
|
||||
// 路由过滤,只显示要显示的路由
|
||||
const routerFilter = (menus: Array<Record<string, any>>) => {
|
||||
return menus.filter((item: Record<string, any>) => {
|
||||
if (item.children) {
|
||||
item.children = routerFilter(item.children)
|
||||
}
|
||||
|
||||
return !item.meta.hidden
|
||||
})
|
||||
}
|
||||
|
||||
// 路由格式化
|
||||
const routerFormat = (arr: Array<Record<string, any>>) => {
|
||||
return arr.map((item: Record<string, any>) => {
|
||||
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
|
||||
}
|
||||
}
|
||||
242
src/components/Base/Layout/SystemLayout/Sider/index.vue
Normal file
@ -0,0 +1,242 @@
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "vue";
|
||||
import { computed } from "vue";
|
||||
export default defineComponent({
|
||||
name: "SystemLayoutSider",
|
||||
});
|
||||
</script>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { reactive, ref, watch, h, onMounted } from "vue";
|
||||
import type { MenuProps } from "ant-design-vue";
|
||||
import { router } from "@/router/index.ts";
|
||||
import UserBox from "@/components/Base/Tools/UserBox/index.vue";
|
||||
import { useMain } from "@/components/Base/Layout/SystemLayout/Sider/hooks/router.ts";
|
||||
import { useRouterStore } from "@/stores/modules/async-router.ts";
|
||||
import type { AppRouter } from "@/router/config/index";
|
||||
import SiderImg from "@/assets/images/index/sider-radar.png";
|
||||
|
||||
const routerStore = useRouterStore();
|
||||
const routers = routerStore.ROUTERS;
|
||||
|
||||
const { routerFilter, routerFormat } = useMain();
|
||||
|
||||
// 路由表
|
||||
let menus = ref<Array<AppRouter>>([]);
|
||||
|
||||
// 路由表组装
|
||||
const createMenus = (routerList: Array<AppRouter>) => {
|
||||
const validRouter = routerFilter(routerList);
|
||||
const useRouter = routerFormat(validRouter);
|
||||
return useRouter;
|
||||
};
|
||||
|
||||
// let menuSelectKeys = computed(() => {
|
||||
// console.log("router.currentRoute", router.currentRoute.value);
|
||||
// return [router.currentRoute.value.name];
|
||||
// });
|
||||
|
||||
// 路由点击
|
||||
const menuSelectKeys = ref<string[]>([]);
|
||||
|
||||
// 点击事件
|
||||
const handleClick: MenuProps["onClick"] = (e) => {
|
||||
const { fullPath } = e.item;
|
||||
console.log("fullPath", e.item, routers[0].children);
|
||||
console.log("menuSelectKeys", menuSelectKeys);
|
||||
if (fullPath != router.currentRoute.value.fullPath) {
|
||||
router.push(e.item.fullPath as string);
|
||||
}
|
||||
};
|
||||
|
||||
// 当前展开的路由菜单
|
||||
const routerOpenKeys = ref<string[]>([]);
|
||||
|
||||
const handleOpenChange = (openKeys: string[]) => {};
|
||||
|
||||
// 路由表初始化
|
||||
const initMenus = () => {
|
||||
//@ts-ignorex
|
||||
menus.value = createMenus(routers[0].children as AppRouter[]);
|
||||
};
|
||||
|
||||
// 路由表选中初始化
|
||||
const initMenuSelectkeys = () => {
|
||||
const curRouter = router.currentRoute.value;
|
||||
console.log("curRouter", curRouter, routers[0]);
|
||||
let key = curRouter.name as string;
|
||||
const { hidden } = curRouter.meta;
|
||||
if (hidden) {
|
||||
const routerGroup = curRouter.matched[1];
|
||||
let target = findRouter([routerGroup], key);
|
||||
console.log("targetRouter", target);
|
||||
let isSelect = true;
|
||||
while (isSelect) {
|
||||
if (target.meta.hidden) {
|
||||
if (!target.parent) {
|
||||
isSelect = false;
|
||||
} else {
|
||||
target = target.parent;
|
||||
}
|
||||
} else {
|
||||
key = target.name;
|
||||
isSelect = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
menuSelectKeys.value = [key];
|
||||
};
|
||||
|
||||
// 寻找指定路由
|
||||
const findRouter = (routerList: Array<Record<string, any>>, key: string): any => {
|
||||
let router = null;
|
||||
for (let i = 0; i < routerList.length; i++) {
|
||||
const item = routerList[i];
|
||||
if (item.name == key) {
|
||||
router = item;
|
||||
break;
|
||||
}
|
||||
if (item.children) {
|
||||
router = findRouter(item.children, key);
|
||||
if (router) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return router;
|
||||
};
|
||||
|
||||
// 路由表状态初始化
|
||||
const initStatus = () => {
|
||||
const { value } = router.currentRoute;
|
||||
routerOpenKeys.value = (value.meta.parentId ? [value.meta.parentId] : []) as string[];
|
||||
};
|
||||
|
||||
const init = () => {
|
||||
initMenus();
|
||||
initStatus();
|
||||
initMenuSelectkeys();
|
||||
};
|
||||
|
||||
const collapsed = ref(false);
|
||||
|
||||
onMounted(() => {
|
||||
//@ts-ignorex
|
||||
init();
|
||||
});
|
||||
|
||||
const prefix = "_system-layout-sider";
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<a-layout-sider
|
||||
collapsible
|
||||
v-model:collapsed="collapsed"
|
||||
theme="light"
|
||||
:class="prefix"
|
||||
:trigger="null"
|
||||
width="240"
|
||||
>
|
||||
<a-menu
|
||||
v-model:openKeys="routerOpenKeys"
|
||||
v-model:selectedKeys="menuSelectKeys"
|
||||
mode="inline"
|
||||
:multiple="false"
|
||||
:inline-collapsed="collapsed"
|
||||
:items="menus"
|
||||
@click="handleClick"
|
||||
@openChange="handleOpenChange"
|
||||
>
|
||||
</a-menu>
|
||||
<img :src="SiderImg" :class="`${prefix}-marker`" />
|
||||
</a-layout-sider>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
$prefix: "_system-layout-sider";
|
||||
|
||||
.#{$prefix} {
|
||||
max-height: 100vh;
|
||||
overflow: hidden;
|
||||
// padding: 10px 10px;
|
||||
box-sizing: border-box;
|
||||
position: reactive;
|
||||
z-index: 10;
|
||||
box-shadow: 0px 2px 4px 0px rgba(133, 99, 244, 0.2);
|
||||
border-radius: 0px 0px 16px 16px;
|
||||
background: rgba(255, 255, 255, 0.6);
|
||||
border: 1px solid rgba(231, 239, 251, 0.5);
|
||||
background: rgba(255, 255, 255, 0.6);
|
||||
:deep(.ant-layout-sider-children) {
|
||||
display: flex;
|
||||
align-items: start;
|
||||
justify-content: start;
|
||||
flex-direction: column;
|
||||
.ant-menu-light {
|
||||
background-color: transparent;
|
||||
margin: 0;
|
||||
}
|
||||
.ant-menu-item-selected {
|
||||
background-color: #e2eaf2;
|
||||
}
|
||||
.ant-menu-root.ant-menu-inline {
|
||||
border-inline-end: none;
|
||||
}
|
||||
}
|
||||
&-header {
|
||||
padding-bottom: 15px;
|
||||
&__row {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 10px;
|
||||
.search-row {
|
||||
margin-top: 5px;
|
||||
padding: 5px 15px;
|
||||
border-radius: 40px;
|
||||
color: #333;
|
||||
box-sizing: border-box;
|
||||
:deep(.ant-input) {
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
}
|
||||
&__collapsed {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
background-color: #f0f0f0;
|
||||
}
|
||||
}
|
||||
}
|
||||
&-chats {
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
flex-shrink: 0;
|
||||
align-self: stretch;
|
||||
}
|
||||
&-menus {
|
||||
padding-top: 10px;
|
||||
width: 100%;
|
||||
height: 240px;
|
||||
margin-top: auto;
|
||||
}
|
||||
&-footer {
|
||||
width: 100%;
|
||||
}
|
||||
&-marker {
|
||||
width: 339px;
|
||||
height: 319px;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: -121px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
48
src/components/Base/Layout/SystemLayout/index.vue
Normal file
@ -0,0 +1,48 @@
|
||||
<script setup lang="ts">
|
||||
import { reactive, computed } from "vue";
|
||||
import LayoutHeader from "./Header/index.vue";
|
||||
import LayoutSider from "./Sider/index.vue";
|
||||
import LayoutContent from "./Content/index.vue";
|
||||
import { router } from "@/router/index.ts";
|
||||
import { inject } from "vue";
|
||||
import bgImg from "@/assets/images/index/bg.jpg";
|
||||
|
||||
const data = reactive({
|
||||
loading: true,
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<a-layout class="system-layout">
|
||||
<layout-header></layout-header>
|
||||
<a-layout>
|
||||
<layout-sider v-model:contentLoading="data.loading"> </layout-sider>
|
||||
<layout-content v-model:contentLoading="data.loading"> </layout-content>
|
||||
</a-layout>
|
||||
</a-layout>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.system-layout {
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
position: relative;
|
||||
background:
|
||||
linear-gradient(rgba(255, 255, 255, 0.5), rgba(255, 255, 255, 0.5)),
|
||||
url(@/assets/images/index/bg.jpg) no-repeat center center;
|
||||
background-size: cover;
|
||||
:deep(.ant-layout) {
|
||||
background: transparent;
|
||||
}
|
||||
&_header {
|
||||
}
|
||||
&_content {
|
||||
// padding-top: 80px;
|
||||
box-sizing: border-box;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
// position: relative;
|
||||
// z-index: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
12
src/components/Base/Layout/ViewLayout/index.vue
Normal file
@ -0,0 +1,12 @@
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue'
|
||||
export default defineComponent({
|
||||
name: 'ViewLayout',
|
||||
})
|
||||
</script>
|
||||
|
||||
<script setup lang="ts"></script>
|
||||
|
||||
<template>
|
||||
<RouterView />
|
||||
</template>
|
||||
56
src/components/Base/Tools/Info/index.vue
Normal file
@ -0,0 +1,56 @@
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "vue";
|
||||
export default defineComponent({
|
||||
name: "SystemInfo",
|
||||
});
|
||||
</script>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { info } from "@/config/index.ts";
|
||||
const prefix = "_info-box";
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div :class="prefix">
|
||||
<div :class="`${prefix}-icon`">
|
||||
<img :class="`${prefix}-icon-img`" :src="info.logo" />
|
||||
</div>
|
||||
<div :class="`${prefix}-title`">
|
||||
{{ info.title }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
$prefix: "_info-box";
|
||||
|
||||
.#{$prefix} {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
&-icon {
|
||||
margin-right: 10px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
&-img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
&-title {
|
||||
font-family:
|
||||
Source Han Sans CN,
|
||||
Source Han Sans CN;
|
||||
font-weight: 500;
|
||||
font-size: 20px;
|
||||
color: #1d2129;
|
||||
line-height: 28px;
|
||||
text-align: left;
|
||||
font-style: normal;
|
||||
text-transform: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
81
src/components/Base/Tools/Menus/index.vue
Normal file
@ -0,0 +1,81 @@
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "vue";
|
||||
export default defineComponent({
|
||||
name: "SystemLayoutSider",
|
||||
});
|
||||
</script>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Icon from "@/components/Module/Icon/index.vue";
|
||||
import { ref } from "vue";
|
||||
|
||||
interface propsType {
|
||||
menus: Array<{
|
||||
name: string;
|
||||
path: string;
|
||||
key: string;
|
||||
icon: string;
|
||||
}>;
|
||||
}
|
||||
|
||||
let props = withDefaults(defineProps<propsType>(), {
|
||||
// 默认值
|
||||
menus: () => [],
|
||||
});
|
||||
|
||||
const emits = defineEmits(["click"]);
|
||||
|
||||
// 点击事件
|
||||
const onHandle = (item: { path: string; key: string }) => {
|
||||
emits("click", item);
|
||||
};
|
||||
|
||||
const prefix = "_system-menus";
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div :class="prefix">
|
||||
<button :class="`${prefix}-row`" v-for="item in props.menus" @click="onHandle(item)">
|
||||
<div :class="`${prefix}-row__icon`">
|
||||
<Icon :name="item.icon" />
|
||||
</div>
|
||||
<div :class="`${prefix}-row__label`">{{ item.name }}</div>
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
$prefix: "_system-menus";
|
||||
|
||||
.#{$prefix} {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: start;
|
||||
justify-content: start;
|
||||
flex-direction: column;
|
||||
&-row {
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
padding: 5px;
|
||||
border-radius: 12px;
|
||||
background-color: transparent;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: start;
|
||||
gap: 10px;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
background-color: #333;
|
||||
}
|
||||
&__icon {
|
||||
margin-right: 8px;
|
||||
font-size: 16px;
|
||||
}
|
||||
&__label {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
36
src/components/Base/Tools/UserBox/hooks/index.ts
Normal file
@ -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
|
||||
}
|
||||
}
|
||||
71
src/components/Base/Tools/UserBox/index.vue
Normal file
@ -0,0 +1,71 @@
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref, inject } from "vue";
|
||||
export default defineComponent({
|
||||
name: "UserCompBox",
|
||||
});
|
||||
</script>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useMain } from "@/components/Base/Tools/UserBox/hooks/index.ts";
|
||||
import Menus from "@/components/Base/Tools/Menus/index.vue";
|
||||
import Icon from "@/components/Module/Icon/index.vue";
|
||||
|
||||
const { menus, operateMenus } = useMain();
|
||||
|
||||
const prefix = "_user-box";
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<a-Dropdown>
|
||||
<template #overlay>
|
||||
<div :class="`${prefix}-drop`">
|
||||
<div :class="`${prefix}-drop-row`">
|
||||
<Menus :menus="menus" />
|
||||
</div>
|
||||
<div :class="`${prefix}-drop-row`">
|
||||
<Menus :menus="operateMenus" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<button :class="`${prefix}-avator`">
|
||||
<icon name="userLine" />
|
||||
</button>
|
||||
</a-Dropdown>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
$prefix: "_user-box";
|
||||
|
||||
.#{$prefix} {
|
||||
&-avator {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
background-color: #333;
|
||||
}
|
||||
}
|
||||
&-drop {
|
||||
width: 200px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 10px;
|
||||
border-radius: 6px;
|
||||
background-color: #fff;
|
||||
box-shadow: 0px 4px 4px 0px rgba(0, 0, 0, 0.16);
|
||||
&-row {
|
||||
margin-bottom: 10px;
|
||||
&:last-child {
|
||||
padding-top: 10px;
|
||||
margin-bottom: 0;
|
||||
border-top: 1px solid #333;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
21
src/components/Module/AsyncContent/ErrorLoading.vue
Normal file
@ -0,0 +1,21 @@
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue'
|
||||
export default defineComponent({
|
||||
name: 'asyncErrLoadingBox',
|
||||
})
|
||||
</script>
|
||||
|
||||
<script setup lang="ts"></script>
|
||||
|
||||
<template>
|
||||
<div class="global-comp-loading-box">
|
||||
<a-spin />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.global-comp-loading-box {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
14
src/components/Module/AsyncContent/index.vue
Normal file
@ -0,0 +1,14 @@
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue'
|
||||
export default defineComponent({
|
||||
name: 'asyncContentBox',
|
||||
})
|
||||
</script>
|
||||
|
||||
<script setup lang="ts"></script>
|
||||
|
||||
<template>
|
||||
<slot></slot>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
80
src/components/Module/Icon/components/SvgIcon.vue
Normal file
@ -0,0 +1,80 @@
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "vue";
|
||||
export default defineComponent({
|
||||
name: "SvgIcon",
|
||||
});
|
||||
</script>
|
||||
<script lang="ts" setup>
|
||||
import { computed } from "vue";
|
||||
import { getIconContent } from "@/components/Module/Icon/components/icon.ts";
|
||||
|
||||
const props = defineProps({
|
||||
prefix: {
|
||||
type: String,
|
||||
default: "icon",
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
color: {
|
||||
type: String,
|
||||
default: "currentColor",
|
||||
},
|
||||
className: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
stroke: {
|
||||
type: String,
|
||||
default: "currentColor",
|
||||
},
|
||||
});
|
||||
|
||||
function processSvgForInline(svg: string, fillColor: string) {
|
||||
let processed = svg.replace(/<\?xml.*?\?>/, "");
|
||||
|
||||
if (fillColor) {
|
||||
processed = processed.replace("<svg", `<svg fill="${fillColor || "currentColor"}" `);
|
||||
}
|
||||
|
||||
return processed;
|
||||
}
|
||||
|
||||
let svgContent: string | null = "";
|
||||
|
||||
const processedSvg = computed(() => {
|
||||
svgContent = getIconContent(props.name);
|
||||
return svgContent ? processSvgForInline(svgContent, props.color) : "";
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<div class="_svg-container" :style="`--color: ${color ?? 'inherit'};`">
|
||||
<template v-if="processedSvg">
|
||||
<div :class="`_svg-icon ${className || ''}`" v-html="processedSvg"></div>
|
||||
</template>
|
||||
<template v-else-if="name">
|
||||
<div :class="`_svg-icon ${className || ''}`">{{ name }}</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
._svg-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: inherit;
|
||||
}
|
||||
._svg-icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
vertical-align: -0.1em;
|
||||
fill: currentColor;
|
||||
overflow: hidden;
|
||||
font-size: inherit;
|
||||
}
|
||||
</style>
|
||||
92
src/components/Module/Icon/components/icon.ts
Normal file
@ -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<IconMap>({});
|
||||
const iconContents = ref<IconMap>({});
|
||||
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
|
||||
// };
|
||||
// }
|
||||
73
src/components/Module/Icon/index.vue
Normal file
@ -0,0 +1,73 @@
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "vue";
|
||||
export default defineComponent({
|
||||
name: "CommonIcon",
|
||||
});
|
||||
</script>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import * as Icons from "@ant-design/icons-vue";
|
||||
import SvgIcon from "./components/SvgIcon.vue";
|
||||
|
||||
// const { name, color } = defineProps(['name', 'color'])
|
||||
|
||||
interface propsType {
|
||||
name?: string;
|
||||
color?: string;
|
||||
}
|
||||
|
||||
let props = withDefaults(defineProps<propsType>(), {
|
||||
name: "",
|
||||
color: "inherit",
|
||||
});
|
||||
|
||||
const keys = Object.keys(Icons);
|
||||
|
||||
const rendIcon = (name: string) => {
|
||||
const isDefault = keys.find((item) => {
|
||||
return item == name;
|
||||
});
|
||||
return isDefault;
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- {{ props.color }} -->
|
||||
<div
|
||||
v-if="rendIcon(props.name)"
|
||||
class="_global-icon"
|
||||
:style="{
|
||||
'--color': props.color ? props.color : 'inherit',
|
||||
}"
|
||||
>
|
||||
<component :is="props.name"></component>
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
class="_global-icon"
|
||||
:style="{
|
||||
'--color': props.color ? props.color : 'inherit',
|
||||
}"
|
||||
>
|
||||
<svg-icon :name="props.name" v-if="props.name" :color="props.color" class="_global-svg-icon" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
._global-icon {
|
||||
color: var(--color);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
._global-svg-icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: inherit;
|
||||
font-size: inherit;
|
||||
use {
|
||||
fill: currentColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
171
src/components/Module/Modal/components/ModalHeader.vue
Normal file
@ -0,0 +1,171 @@
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "vue";
|
||||
export default defineComponent({
|
||||
name: "ModalHeader",
|
||||
});
|
||||
</script>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { reactive, defineEmits, withDefaults } from "vue";
|
||||
import { useRoute } from "vue-router";
|
||||
import Icon from "@/components/Module/Icon/index.vue";
|
||||
|
||||
const emits = defineEmits(["onSubmit", "onSubmitAndCreate", "onCancel"]);
|
||||
|
||||
interface propsType {
|
||||
addPermission?: string[] | undefined; // 新增按钮的权限字符
|
||||
showIcon?: boolean;
|
||||
title?: string | null | undefined;
|
||||
showSubmit?: boolean;
|
||||
showSubmitAndCreate?: boolean;
|
||||
loading?: boolean;
|
||||
// 是否显示icon
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<propsType>(), {
|
||||
// 默认值
|
||||
addPermission: undefined, // 新增权限
|
||||
showSubmit: true,
|
||||
showSubmitAndCreate: true,
|
||||
loading: false,
|
||||
});
|
||||
|
||||
// 设置表头信息
|
||||
const route = useRoute();
|
||||
const pageInfo = reactive({
|
||||
name: route.meta.title,
|
||||
icon: route.meta.icon,
|
||||
});
|
||||
|
||||
const typeObj: ExtraObj = {
|
||||
add: "新增",
|
||||
edit: "编辑",
|
||||
};
|
||||
|
||||
console.log("typeObj", typeObj, props.type);
|
||||
|
||||
const onSubmit = () => {
|
||||
emits("onSubmit");
|
||||
};
|
||||
|
||||
const onSubmitAndCreate = () => {
|
||||
emits("onSubmitAndCreate");
|
||||
};
|
||||
|
||||
const onCancel = () => {
|
||||
emits("onCancel");
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<a-page-header class="_modal-header">
|
||||
<template #title>
|
||||
<div class="page-header-title-bar">
|
||||
<slot name="title">
|
||||
<slot name="title-start-slot"></slot>
|
||||
<div class="page-header-title">
|
||||
<!-- <div class="icon" v-if="showIcon">
|
||||
<Icon :name="route.meta.icon" />
|
||||
</div> -->
|
||||
<div class="type">
|
||||
{{ typeObj[$attrs.type] || "" }}
|
||||
</div>
|
||||
<div class="title">{{ props.title || pageInfo.name }}</div>
|
||||
</div>
|
||||
<slot name="title-end-slot"></slot>
|
||||
</slot>
|
||||
</div>
|
||||
</template>
|
||||
<template #extra>
|
||||
<!-- 前插槽 -->
|
||||
<slot name="btn-start-slot"></slot>
|
||||
<a-button
|
||||
type="primary"
|
||||
class="main-color"
|
||||
@click="onSubmit"
|
||||
v-if="showSubmit"
|
||||
:loading="props.loading"
|
||||
>提交</a-button
|
||||
>
|
||||
<a-button
|
||||
type="primary"
|
||||
class="main-color"
|
||||
@click="onSubmitAndCreate"
|
||||
:loading="props.loading"
|
||||
v-if="showSubmitAndCreate"
|
||||
>提交并继续新建</a-button
|
||||
>
|
||||
<a-button @click="onCancel">{{ $attrs.cancelText || "取消" }}</a-button>
|
||||
<!-- 后插槽 -->
|
||||
<slot name="btn-end-slot"></slot>
|
||||
</template>
|
||||
</a-page-header>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
._modal-header {
|
||||
padding: 0;
|
||||
height: 54px;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
border-bottom: 1px solid rgba(229, 233, 242, 1);
|
||||
.page-header-title-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-left: 28px;
|
||||
position: relative;
|
||||
&::after {
|
||||
content: " ";
|
||||
position: absolute;
|
||||
left: 0px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 4px;
|
||||
height: 32px;
|
||||
// background: $primary-color-1;
|
||||
}
|
||||
.page-header-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.type {
|
||||
margin-right: 4px;
|
||||
@include module-title-font;
|
||||
}
|
||||
.title {
|
||||
@include module-title-font;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.ant-page-header-heading-extra) {
|
||||
padding-left: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
.ant-page-header {
|
||||
// background-color: red !important;
|
||||
// background-color: red;
|
||||
:deep(.ant-page-header-heading) {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
padding-right: 24px;
|
||||
box-sizing: border-box;
|
||||
margin: 0px;
|
||||
}
|
||||
:deep(.ant-page-header-extra) {
|
||||
margin: 0px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
:deep(.ant-btn) {
|
||||
padding: 7px 16px;
|
||||
height: 38px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
</style>
|
||||
187
src/components/Module/Modal/index.vue
Normal file
@ -0,0 +1,187 @@
|
||||
<script lang="ts">
|
||||
import { defineComponent, defineAsyncComponent } from "vue";
|
||||
export default defineComponent({
|
||||
name: "CommonModal",
|
||||
});
|
||||
</script>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, watch } from "vue";
|
||||
import Icon from "@/components/Module/Icon/index.vue";
|
||||
|
||||
const emits = defineEmits(["update:open"]);
|
||||
|
||||
const asyncContent = defineAsyncComponent({
|
||||
loader: () => import("@/components/Module/AsyncContent/index.vue"),
|
||||
delay: 2000,
|
||||
});
|
||||
|
||||
interface propsType {
|
||||
open: boolean;
|
||||
title?: string | null;
|
||||
zIndex?: number | string;
|
||||
loading?: boolean;
|
||||
containDom?: any; // 挂载dom
|
||||
showConfirm?: boolean;
|
||||
showCancel?: boolean;
|
||||
confirmText?: string;
|
||||
cancelText?: string;
|
||||
onConfirm?: null | (() => void | Promise<void>);
|
||||
onCancel?: null | (() => void | Promise<void>);
|
||||
}
|
||||
|
||||
let props = withDefaults(defineProps<propsType>(), {
|
||||
// 默认值
|
||||
open: false,
|
||||
title: null,
|
||||
zIndex: 1000,
|
||||
loading: false,
|
||||
confirmText: "确定",
|
||||
cancelText: "取消",
|
||||
onConfirm: null,
|
||||
onCancel: null,
|
||||
showConfirm: true,
|
||||
showCancel: true,
|
||||
});
|
||||
|
||||
const useOpen = ref<boolean>(props.open);
|
||||
|
||||
const onConfirmHandle = () => {
|
||||
if (props.onConfirm) {
|
||||
props.onConfirm();
|
||||
} else {
|
||||
emits("update:open", false);
|
||||
useOpen.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const onCancel = () => {
|
||||
if (props.onCancel) {
|
||||
props.onCancel();
|
||||
} else {
|
||||
emits("update:open", false);
|
||||
useOpen.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.open,
|
||||
(val) => {
|
||||
useOpen.value = val;
|
||||
},
|
||||
);
|
||||
|
||||
let globalModalRef = ref<HTMLElement>();
|
||||
|
||||
const prefix = "_global-modal";
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div ref="globalModalRef" :class="prefix">
|
||||
<a-modal
|
||||
:zIndex="props.zIndex"
|
||||
v-bind="$attrs"
|
||||
:getContainer="() => props.containDom || globalModalRef"
|
||||
:closable="false"
|
||||
v-model:open="useOpen"
|
||||
:footer="null"
|
||||
@cancel="onCancel"
|
||||
>
|
||||
<slot name="header">
|
||||
<div :class="`${prefix}-head`">
|
||||
<div :class="`${prefix}-head-label`">
|
||||
{{ props.title }}
|
||||
</div>
|
||||
<button :class="`${prefix}-head-close`" @click="onCancel">
|
||||
<Icon name="close" />
|
||||
</button>
|
||||
</div>
|
||||
</slot>
|
||||
<Suspense>
|
||||
<asyncContent>
|
||||
<a-spin :spinning="props.loading">
|
||||
<div :class="`${prefix}-container`">
|
||||
<div class="modal-content-center">
|
||||
<asyncContent>
|
||||
<slot></slot>
|
||||
</asyncContent>
|
||||
</div>
|
||||
</div>
|
||||
</a-spin>
|
||||
<slot name="footer">
|
||||
<div :class="`${prefix}-footer`">
|
||||
<div :class="`${prefix}-footer__grid`" v-if="props.showCancel">
|
||||
<a-button @click="onCancel">{{ cancelText }}</a-button>
|
||||
</div>
|
||||
<div :class="`${prefix}-footer__grid`" v-if="props.showConfirm">
|
||||
<a-button type="primary" @click="onConfirmHandle" :loading="props.loading">{{
|
||||
confirmText
|
||||
}}</a-button>
|
||||
</div>
|
||||
</div>
|
||||
</slot>
|
||||
</asyncContent>
|
||||
<template #fallback>
|
||||
<div class="modal-content load-box">
|
||||
<a-spin />
|
||||
</div>
|
||||
</template>
|
||||
</Suspense>
|
||||
</a-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
$prefix: "_global-modal";
|
||||
|
||||
.#{$prefix} {
|
||||
&-head {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 12px 16px;
|
||||
border-radius: 16px 16px 0 0;
|
||||
background-color: #d9d9d9;
|
||||
margin-bottom: 20px;
|
||||
&-close {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 20px;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
&-container {
|
||||
padding: 0 16px;
|
||||
width: 100%;
|
||||
flex: 1;
|
||||
align-self: stretch;
|
||||
}
|
||||
&-footer {
|
||||
padding: 12px 16px;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: end;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
:deep(.ant-modal-content) {
|
||||
padding: 0;
|
||||
.ant-modal-body {
|
||||
padding: 0;
|
||||
display: flex;
|
||||
align-items: start;
|
||||
justify-content: start;
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
:deep(.ant-spin-nested-loading) {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
11
src/components/__tests__/HelloWorld.spec.ts
Normal file
@ -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')
|
||||
})
|
||||
})
|
||||
8
src/config/index.ts
Normal file
@ -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
|
||||
}
|
||||
115
src/core/lazy_use.ts
Normal file
@ -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<Element>) {
|
||||
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<Element>) {
|
||||
|
||||
|
||||
}
|
||||
|
||||
8
src/directive/index.ts
Normal file
@ -0,0 +1,8 @@
|
||||
import type { App } from 'vue'
|
||||
|
||||
|
||||
function directive(app: App<Element>) {
|
||||
// 注册指令
|
||||
}
|
||||
|
||||
export default directive;
|
||||
36
src/main.ts
Normal file
@ -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')
|
||||
40
src/router/base/index.ts
Normal file
@ -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'),
|
||||
|
||||
},
|
||||
]
|
||||
},
|
||||
]
|
||||
61
src/router/config/index.ts
Normal file
@ -0,0 +1,61 @@
|
||||
import type {
|
||||
RouteRecordRaw,
|
||||
RouteMeta
|
||||
} from 'vue-router'
|
||||
|
||||
import type { defineComponent } from 'vue'
|
||||
|
||||
|
||||
export type Component<T = any> = ReturnType<typeof defineComponent> | (() => Promise<typeof import('*.vue')>) | (() => Promise<T>)
|
||||
|
||||
// @ts-expect-error
|
||||
export interface AppRouter extends Omit<RouteRecordRaw, 'meta'> {
|
||||
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 // 全路径
|
||||
// }
|
||||
70
src/router/func/index.ts
Normal file
@ -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<AppRouter[]> => {
|
||||
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
|
||||
}
|
||||
20
src/router/guard/before.ts
Normal file
@ -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(),
|
||||
])
|
||||
}
|
||||
90
src/router/guard/index.ts
Normal file
@ -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()
|
||||
// }
|
||||
}
|
||||
// }
|
||||
})
|
||||
}
|
||||
17
src/router/index.ts
Normal file
@ -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<Element>) {
|
||||
app.use(router)
|
||||
}
|
||||
|
||||
21
src/router/modules/routerComponents.ts
Normal file
@ -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')),
|
||||
}
|
||||
|
||||
109
src/router/modules/staticRouter.ts
Normal file
@ -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,
|
||||
},
|
||||
}
|
||||
]
|
||||
|
||||
12
src/stores/counter.ts
Normal file
@ -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 }
|
||||
})
|
||||
10
src/stores/index.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import type { App } from 'vue'
|
||||
import { createPinia } from 'pinia'
|
||||
|
||||
const store = createPinia()
|
||||
|
||||
export function setupStore(app: App<Element>) {
|
||||
app.use(store)
|
||||
}
|
||||
|
||||
export { store }
|
||||
54
src/stores/modules/async-router.ts
Normal file
@ -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<AppRouter[]> {
|
||||
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)
|
||||
}
|
||||
6
src/stores/modules/mutation-types.ts
Normal file
@ -0,0 +1,6 @@
|
||||
|
||||
export const ACCESS_TOKEN = 'token'
|
||||
export const ACCESS_EXPIRES = 'expires'
|
||||
export const APP_VERSION = 'app_version'
|
||||
|
||||
|
||||
78
src/stores/modules/socket.ts
Normal file
@ -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)
|
||||
}
|
||||
22
src/stores/modules/system.ts
Normal file
@ -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)
|
||||
}
|
||||
103
src/stores/modules/user.ts
Normal file
@ -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)
|
||||
}
|
||||
30
src/styles/index.css
Normal file
@ -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;
|
||||
/* 保持指针样式 */
|
||||
}
|
||||
34
src/styles/index.scss
Normal file
@ -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; /* 保持指针样式 */
|
||||
}
|
||||
30
src/styles/theme/default.json
Normal file
@ -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": {}
|
||||
}
|
||||
}
|
||||
37
src/types/global.d.ts
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
import type { DefineComponent } from 'vue'
|
||||
|
||||
|
||||
declare global {
|
||||
type ExtraObj = {
|
||||
[key?: string]: any
|
||||
}
|
||||
type Recordable<T = any> = Record<string, T>
|
||||
|
||||
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/*';
|
||||
|
||||
|
||||
|
||||
37
src/types/module.d.ts
vendored
Normal file
@ -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';
|
||||
0
src/utils/EvenStream/index.ts
Normal file
187
src/utils/download/index.ts
Normal file
@ -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() });
|
||||
}
|
||||
|
||||
106
src/utils/request/index.ts
Normal file
@ -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<ResponseData | ResponseError> => {
|
||||
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;
|
||||
}
|
||||
43
src/utils/request/types/index.ts
Normal file
@ -0,0 +1,43 @@
|
||||
export interface Config {
|
||||
base?: string, // 前缀
|
||||
url: string // 请求地址
|
||||
method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH'
|
||||
data?: string | Record<string, any> // 请求体
|
||||
dataRaw?: boolean // 请求体是否为原始数据
|
||||
params?: string | Record<string, any> | URLSearchParams | string[][] // 请求参数
|
||||
headers?: HeadersInit | undefined // 请求头
|
||||
controller?: AbortController // 请求控制器
|
||||
getRaw?: boolean // 是否需要返回原始数据
|
||||
}
|
||||
|
||||
// 请求头
|
||||
export interface RequestHead {
|
||||
[key: string]: string
|
||||
}
|
||||
|
||||
// 标准响应
|
||||
export interface ResponseData<T = any> {
|
||||
code: number
|
||||
data: T
|
||||
[key: string]: any
|
||||
}
|
||||
|
||||
// 错误响应
|
||||
export interface ResponseError<T = any> {
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||