commit
8e5a22212c
153 changed files with 10391 additions and 0 deletions
-
7.env.dev
-
7.env.pre
-
7.env.prod
-
7.env.test
-
27.gitignore
-
0.npmrc
-
28.postcssrc.js
-
10.prettierrc
-
23README.md
-
BINcommitlint.config.js
-
19eslint.config.js
-
43increment-version.js
-
27index.html
-
90package.json
-
BINpublic/favicon.ico
-
20server/index.js
-
33server/routes/auth.js
-
74server/routes/control.js
-
28server/utils/websocket.js
-
5src/apis/container.ts
-
34src/apis/crafts.ts
-
13src/apis/home.ts
-
4src/apis/login.ts
-
4src/apis/point.ts
-
4src/apis/self.ts
-
9src/apis/solution.ts
-
12src/apis/system.ts
-
7src/apis/user.ts
-
118src/app.vue
-
1src/assets/images/background.svg
-
1src/assets/images/empty.svg
-
1src/assets/images/error.svg
-
1src/assets/images/expand.svg
-
1src/assets/images/icon_add_s.svg
-
1src/assets/images/icon_arr_s.svg
-
1src/assets/images/icon_del_s.svg
-
1src/assets/images/ing.svg
-
1src/assets/images/jaw.svg
-
1src/assets/images/liquied/liquied_bottle.svg
-
1src/assets/images/login.svg
-
1src/assets/images/logo.svg
-
1src/assets/images/logout.svg
-
1src/assets/images/menuIcon/n_debug.svg
-
1src/assets/images/menuIcon/n_expe.svg
-
1src/assets/images/menuIcon/n_home.svg
-
1src/assets/images/menuIcon/n_liquid.svg
-
1src/assets/images/menuIcon/n_liquid_config.svg
-
1src/assets/images/menuIcon/n_log.svg
-
1src/assets/images/menuIcon/n_ore.svg
-
1src/assets/images/menuIcon/n_point.svg
-
1src/assets/images/menuIcon/n_ps.svg
-
1src/assets/images/menuIcon/n_setting.svg
-
1src/assets/images/menuIcon/n_user.svg
-
1src/assets/images/menuIcon/s_debug.svg
-
1src/assets/images/menuIcon/s_expe.svg
-
1src/assets/images/menuIcon/s_home.svg
-
1src/assets/images/menuIcon/s_liquid.svg
-
1src/assets/images/menuIcon/s_liquid_config.svg
-
1src/assets/images/menuIcon/s_log.svg
-
1src/assets/images/menuIcon/s_ore.svg
-
1src/assets/images/menuIcon/s_point.svg
-
1src/assets/images/menuIcon/s_ps.svg
-
1src/assets/images/menuIcon/s_setting.svg
-
1src/assets/images/menuIcon/s_user.svg
-
1src/assets/images/password_icon.svg
-
1src/assets/images/run.svg
-
1src/assets/images/success.svg
-
22src/assets/images/user-cancel.svg
-
1src/assets/images/user.svg
-
1src/assets/images/user_icon.svg
-
1src/assets/images/wait.svg
-
1src/assets/images/wifi-active.svg
-
1src/assets/images/wifi.svg
-
91src/assets/styles/common.scss
-
72src/assets/styles/element.scss
-
3src/assets/styles/main.scss
-
5src/assets/styles/variable.scss
-
109src/components/SavePosition/index.vue
-
158src/components/common/FTButton/index.vue
-
76src/components/common/FTDialog/index.vue
-
182src/components/common/FTStream/index.vue
-
7src/components/common/FTTable/expand.ts
-
216src/components/common/FTTable/index.vue
-
235src/components/container/Item/index.vue
-
391src/components/craft/AddCraft/index.vue
-
285src/components/craft/AddCraftDialog.vue
-
257src/components/craft/CraftStatus.vue
-
40src/components/craft/TransferLeft.vue
-
161src/components/craft/TransferRight.vue
-
202src/components/home/AddLiquid/index.vue
-
502src/components/home/CheckCraft/index.vue
-
107src/components/home/Countdown/Countdown.vue
-
190src/components/home/ExecuteCraft/index.vue
-
218src/components/home/ExtractLiquid/index.vue
-
117src/components/home/FillSolution/index.vue
-
64src/components/home/Liquid/index.vue
-
131src/components/home/SetTemperature/index.vue
-
201src/components/home/StartClean/index.vue
-
50src/components/home/StartExperiment/index.vue
-
396src/components/home/Tube/index.vue
@ -0,0 +1,7 @@ |
|||
# 开发环境 |
|||
|
|||
FT_NODE_ENV=dev |
|||
|
|||
FT_WS_URL=ws://192.168.1.199:8080/ws |
|||
FT_PROXY=http://localhost:8080 |
|||
FT_API_BASE=/api |
@ -0,0 +1,7 @@ |
|||
# 预发环境 |
|||
|
|||
FT_NODE_ENV=pre |
|||
|
|||
FT_WS_URL=ws://192.168.8.108:8080/ws |
|||
FT_PROXY=http://192.168.8.108 |
|||
FT_API_BASE=http://192.168.8.108:8080/api |
@ -0,0 +1,7 @@ |
|||
# 生产环境 |
|||
|
|||
FT_NODE_ENV=prod |
|||
|
|||
FT_WS_URL=ws://192.168.8.168:8080/ws |
|||
FT_PROXY=http://192.168.8.168:8080 |
|||
FT_API_BASE=/api |
@ -0,0 +1,7 @@ |
|||
# 测试环境 |
|||
|
|||
FT_NODE_ENV=test |
|||
|
|||
FT_WS_URL=ws://192.168.1.199:8081/ws |
|||
FT_PROXY=http://127.0.0.1:8081 |
|||
FT_API_BASE=/api |
@ -0,0 +1,27 @@ |
|||
# Logs |
|||
logs |
|||
*.log |
|||
npm-debug.log* |
|||
yarn-debug.log* |
|||
yarn-error.log* |
|||
pnpm-debug.log* |
|||
lerna-debug.log* |
|||
auto-imports.d.ts |
|||
.eslintrc-auto-import.json |
|||
pnpm-lock.yaml |
|||
|
|||
node_modules |
|||
dist |
|||
dist-* |
|||
*.local |
|||
|
|||
# Editor directories and files |
|||
.vscode/* |
|||
!.vscode/extensions.json |
|||
.idea |
|||
.DS_Store |
|||
*.suo |
|||
*.ntvs* |
|||
*.njsproj |
|||
*.sln |
|||
*.sw? |
@ -0,0 +1,28 @@ |
|||
export default { |
|||
|
|||
plugins: { |
|||
'postcss-import': {}, |
|||
'postcss-url': {}, |
|||
'postcss-aspect-ratio-mini': {}, |
|||
'postcss-write-svg': { |
|||
utf8: false, |
|||
}, |
|||
'postcss-px-to-viewport-8-plugin': { |
|||
viewportWidth: 1120, |
|||
viewportHeight: 736, |
|||
unitPrecision: 3, // 指定`px`转换为视窗单位值的小数位数(很多时候无法整除)
|
|||
viewportUnit: 'vw', // 指定需要转换成的视窗单位,建议使用vw
|
|||
selectorBlackList: ['.ignore', '.hairlines', ':after'], // 指定不转换为视窗单位的类,可以自定义,可以无限添加,建议定义一至两个通用的类名
|
|||
minPixelValue: 1, // 小于或等于`1px`不转换为视窗单位,你也可以设置为你想要的值
|
|||
mediaQuery: false, // 允许在媒体查询中转换`px`
|
|||
}, |
|||
'postcss-viewport-units': { |
|||
filterRule: rule => |
|||
!/::?(?:before|after)/i.test(rule.selector), |
|||
}, |
|||
'cssnano': { |
|||
'autoprefixer': false, |
|||
'postcss-zindex': false, |
|||
}, |
|||
}, |
|||
} |
@ -0,0 +1,10 @@ |
|||
{ |
|||
"printWidth": 120, |
|||
"tabWidth": 2, |
|||
"endOfLine": "lf", |
|||
"singleQuote": true, |
|||
"semi": true, |
|||
"trailingComma": "none", |
|||
"bracketSpacing": true, |
|||
"arrowParens": "avoid" |
|||
} |
@ -0,0 +1,23 @@ |
|||
### 前端项目模板 |
|||
##### 文件目录 |
|||
- src |
|||
- components 组件目录 |
|||
- apis 接口文件目录 |
|||
- assets 静态资源目录 |
|||
- hooks hooks目录 |
|||
- libs 公共工具类 |
|||
- router 页面路由 |
|||
- stores 项目公共状态管理 |
|||
- types 类型申明 |
|||
- views 页面目录 |
|||
##### 项目运行 |
|||
项目采用pnpm 进行依赖构建,请先安装pnpm |
|||
[pnpm - 速度快、节省磁盘空间的软件包管理器](https://pnpm.io/) |
|||
```bash |
|||
npm install pnpm -g |
|||
``` |
|||
|
|||
```bash |
|||
pnpm install // 安装依赖 |
|||
pnpm dev // 运行项目 |
|||
``` |
@ -0,0 +1,19 @@ |
|||
// import eslint_js from '@eslint/js'
|
|||
// import eslint_ts from 'typescript-eslint';
|
|||
// import eslint_vue from 'eslint-plugin-vue';
|
|||
// import vue_parser from 'vue-eslint-parser';
|
|||
|
|||
import lintConfig from '@antfu/eslint-config' |
|||
|
|||
export default lintConfig({ |
|||
vue: true, |
|||
markdown: true, |
|||
ignores: [], |
|||
rules: { |
|||
'no-console': 0, |
|||
'antfu/top-level-function': 0, |
|||
'ts/no-use-before-define': 0, |
|||
'no-alert': 0, |
|||
}, |
|||
globals: { process: 'readonly' }, |
|||
}) |
@ -0,0 +1,43 @@ |
|||
import { execSync } from 'node:child_process' // 引入 child_process 模块用于执行 Git 命令
|
|||
import fs from 'node:fs' |
|||
import path, { dirname } from 'node:path' |
|||
import { fileURLToPath } from 'node:url' |
|||
import semver from 'semver' |
|||
|
|||
const __filename = fileURLToPath(import.meta.url) |
|||
const __dirname = dirname(__filename) |
|||
|
|||
const packagePath = path.resolve(__dirname, 'package.json') |
|||
const packageJson = JSON.parse(fs.readFileSync(packagePath, 'utf-8')) |
|||
|
|||
// 读取命令行参数(默认使用 'patch')
|
|||
// eslint-disable-next-line node/prefer-global/process
|
|||
const versionType = process.argv[2] || 'patch' |
|||
|
|||
// 递增版本
|
|||
const newVersion = semver.inc(packageJson.version, versionType) |
|||
if (!newVersion) { |
|||
throw new Error(`Invalid version type: ${versionType}`) |
|||
} |
|||
|
|||
packageJson.version = newVersion |
|||
fs.writeFileSync(packagePath, JSON.stringify(packageJson, null, 2)) |
|||
console.log(`Version updated to: ${newVersion}`) |
|||
|
|||
// 新增:自动提交 package.json 到远程仓库
|
|||
try { |
|||
// 将 package.json 添加到暂存区
|
|||
execSync('git add package.json') |
|||
console.log('Added package.json to staging area.') |
|||
|
|||
// 提交更改
|
|||
execSync(`git commit -m "fix: Update version to V${newVersion}"`) |
|||
console.log(`Committed changes with message: Update version to ${newVersion}`) |
|||
|
|||
// 推送到远程仓库
|
|||
execSync('git push') |
|||
console.log('Pushed changes to remote repository.') |
|||
} |
|||
catch (error) { |
|||
console.error('Failed to commit and push changes:', error.message) |
|||
} |
@ -0,0 +1,27 @@ |
|||
<!DOCTYPE html> |
|||
<html lang="en"> |
|||
<head> |
|||
<meta charset="UTF-8" /> |
|||
<link rel="icon" href="/favicon.ico" /> |
|||
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> |
|||
<title>颜色滴定</title> |
|||
<style> |
|||
html,body{ |
|||
height:100%; |
|||
width:100%; |
|||
margin: 0; |
|||
padding: 0; |
|||
} |
|||
#app { |
|||
width: 100%; |
|||
height: 100%; |
|||
position: relative; |
|||
overflow: hidden; |
|||
} |
|||
</style> |
|||
</head> |
|||
<body> |
|||
<div id="app"></div> |
|||
<script type="module" src="/src/main.ts"></script> |
|||
</body> |
|||
</html> |
@ -0,0 +1,90 @@ |
|||
{ |
|||
"name": "color-titration-web", |
|||
"type": "module", |
|||
"version": "0.0.0", |
|||
"description": "", |
|||
"author": "", |
|||
"license": "ISC", |
|||
"keywords": [], |
|||
"main": "index.js", |
|||
"scripts": { |
|||
"dev": "vite --mode dev", |
|||
"dev:server": "concurrently -n server,client -c green,blue \"node server/index.js\" \"vite --mode dev\"", |
|||
"test": "vite --mode test", |
|||
"prod": "vite --mode prod", |
|||
"pre": "vite --mode pre", |
|||
"build": "vite build --mode dev", |
|||
"build:test": "vite build --mode test", |
|||
"build:prod:patch": "node increment-version.js patch && vite build --mode prod", |
|||
"build:prod:minor": "node increment-version.js minor && vite build --mode prod", |
|||
"build:prod:major": "node increment-version.js major && vite build --mode prod", |
|||
"build:pre": "vite build --mode pre", |
|||
"build:dev": "vite build --mode dev", |
|||
"prepare": "husky", |
|||
"lint:lint-staged": "lint-staged", |
|||
"lint": "vue-tsc --noEmit --skipLibCheck && eslint", |
|||
"eslint": "eslint --fix --ext .ts,.vue src", |
|||
"prettier": "prettier --write ." |
|||
}, |
|||
"dependencies": { |
|||
"@element-plus/icons-vue": "^2.3.1", |
|||
"autoprefixer": "^10.4.20", |
|||
"axios": "1.8.2", |
|||
"cssnano": "^7.0.6", |
|||
"element-plus": "^2.9.5", |
|||
"express": "^5.1.0", |
|||
"konva": "^9.3.18", |
|||
"lodash": "^4.17.21", |
|||
"pinia": "^3.0.1", |
|||
"pinia-plugin-persistedstate": "^4.2.0", |
|||
"postcss": "^8.5.3", |
|||
"postcss-aspect-ratio-mini": "^1.1.0", |
|||
"postcss-import": "^16.1.0", |
|||
"postcss-px-to-viewport-8-plugin": "^1.2.5", |
|||
"postcss-url": "^10.1.3", |
|||
"postcss-viewport-units": "^0.1.6", |
|||
"postcss-write-svg": "^3.0.1", |
|||
"vue": "^3.5.13", |
|||
"vue-router": "^4.5.0", |
|||
"ws": "^8.18.1" |
|||
}, |
|||
"devDependencies": { |
|||
"@antfu/eslint-config": "^4.3.0", |
|||
"@commitlint/cli": "^19.7.1", |
|||
"@commitlint/config-conventional": "^19.7.1", |
|||
"@types/node": "^22.13.5", |
|||
"@typescript-eslint/eslint-plugin": "^8.25.0", |
|||
"@typescript-eslint/parser": "^8.25.0", |
|||
"@vitejs/plugin-vue": "^5.2.1", |
|||
"@vue/eslint-config-prettier": "^10.2.0", |
|||
"concurrently": "^9.1.2", |
|||
"eslint": "^9.21.0", |
|||
"eslint-config-prettier": "^10.0.2", |
|||
"eslint-plugin-prettier": "^5.2.3", |
|||
"eslint-plugin-vue": "^9.32.0", |
|||
"husky": "^9.1.7", |
|||
"lint-staged": "^15.4.3", |
|||
"prettier": "^3.5.2", |
|||
"sass": "^1.85.1", |
|||
"semver": "^7.7.1", |
|||
"typescript": "^5.7.3", |
|||
"unplugin-auto-import": "^19.1.1", |
|||
"unplugin-vue-components": "^28.4.1", |
|||
"vite": "6.2.6", |
|||
"vite-plugin-compression": "^0.5.1", |
|||
"vite-plugin-eslint": "^1.8.1", |
|||
"vue-tsc": "^2.2.4" |
|||
}, |
|||
"husky": { |
|||
"hooks": { |
|||
"pre-commit": "lint-staged" |
|||
} |
|||
}, |
|||
"lint-staged": { |
|||
"*.{js,jsx,vue,ts,tsx}": [ |
|||
"eslint --fix", |
|||
"prettier --write", |
|||
"lint-staged" |
|||
] |
|||
} |
|||
} |
@ -0,0 +1,20 @@ |
|||
// const express = require('express')
|
|||
import express from 'express' |
|||
import { authRoutes } from './routes/auth.js' |
|||
import { debugRoutes } from './routes/control.js' |
|||
import { initWebSocketServer } from './utils/websocket.js' |
|||
|
|||
const app = express() |
|||
|
|||
const PORT = 8080 // 可根据需要更改端口号
|
|||
app.listen(PORT, () => { |
|||
console.log(`服务器已启动,正在监听端口 ${PORT}`) |
|||
}) |
|||
app.use(express.json()) |
|||
|
|||
// 初始化 WebSocket 服务
|
|||
initWebSocketServer() |
|||
|
|||
// 注册路由模块
|
|||
debugRoutes(app) |
|||
authRoutes(app) |
@ -0,0 +1,33 @@ |
|||
const baseUrl = '/api/auth' |
|||
export const authRoutes = (app) => { |
|||
app.post(`${baseUrl}/login`, (req, res) => { |
|||
// const { username, password } = req.body
|
|||
const mockResponse = { |
|||
code: '0', |
|||
msg: '成功', |
|||
data: { |
|||
id: 1, |
|||
createTime: '2025-04-27 07:44:01', |
|||
updateTime: '2025-04-27 07:44:01', |
|||
username: 'admin', |
|||
nickname: 'Admin', |
|||
password: null, |
|||
role: 'ADMIN', |
|||
deleted: 'DISABLE', |
|||
fixedUser: 'ENABLE', |
|||
}, |
|||
} |
|||
setTimeout(() => { |
|||
res.json(mockResponse) |
|||
}, 2000) |
|||
}) |
|||
app.post(`${baseUrl}/logout`, (req, res) => { |
|||
// const { username, password } = req.body
|
|||
const mockResponse = { |
|||
code: '0', |
|||
msg: '成功', |
|||
data: null, |
|||
} |
|||
res.json(mockResponse) |
|||
}) |
|||
} |
@ -0,0 +1,74 @@ |
|||
import { broadcast } from '../utils/websocket.js' |
|||
|
|||
export const debugRoutes = (app) => { |
|||
const statusList = ['info', 'warn', 'success', 'error', 'finish'] |
|||
|
|||
app.post('/api/debug/cmd', (req, res) => { |
|||
const { commandId, command, params } = req.body |
|||
console.log('收到命令:', command, '参数:', params) |
|||
|
|||
// 模拟返回数据
|
|||
const mockResponse = { |
|||
code: '0', |
|||
msg: '成功', |
|||
data: null, |
|||
} |
|||
|
|||
let messageNum = 0 |
|||
|
|||
// 异步广播消息
|
|||
setTimeout(() => { |
|||
res.json(mockResponse) |
|||
const poll = setInterval(() => { |
|||
if (messageNum === 10) { |
|||
clearInterval(poll) |
|||
} |
|||
broadcast({ |
|||
type: 'cmd_debug', |
|||
data: { |
|||
commandId, |
|||
command, |
|||
status: statusList[Math.floor(Math.random() * statusList.length)], |
|||
title: `步骤${messageNum}执行完成`, |
|||
content: `具体信息${messageNum}`, |
|||
}, |
|||
}) |
|||
messageNum++ |
|||
}, Math.floor(Math.random() * (1000 - 500 + 1)) + 500) |
|||
}, 1000) |
|||
}) |
|||
app.post('/api/cmd', (req, res) => { |
|||
const { commandId, command, params } = req.body |
|||
console.log('收到命令:', command, '参数:', params) |
|||
|
|||
// 模拟返回数据
|
|||
const mockResponse = { |
|||
code: '0', |
|||
msg: '成功', |
|||
data: null, |
|||
} |
|||
|
|||
let messageNum = 0 |
|||
|
|||
// 异步广播消息
|
|||
setTimeout(() => { |
|||
res.json(mockResponse) |
|||
const poll = setInterval(() => { |
|||
if (messageNum === 10) { |
|||
clearInterval(poll) |
|||
} |
|||
broadcast({ |
|||
type: 'cmd_debug', |
|||
data: { |
|||
commandId, |
|||
command, |
|||
status: statusList[Math.floor(Math.random() * statusList.length)], |
|||
title: `步骤${messageNum}执行完成`, |
|||
content: `具体信息${messageNum}`, |
|||
}, |
|||
}) |
|||
messageNum++ |
|||
}, Math.floor(Math.random() * (1000 - 500 + 1)) + 500) |
|||
}, 1000) |
|||
}) |
|||
} |
@ -0,0 +1,28 @@ |
|||
import { WebSocketServer } from 'ws' |
|||
|
|||
export const clients = new Set() |
|||
|
|||
export const broadcast = (message) => { |
|||
try { |
|||
const jsonMessage = JSON.stringify(message) |
|||
clients.forEach((client) => { |
|||
if (client.readyState === WebSocket.OPEN) { |
|||
client.send(jsonMessage) |
|||
} |
|||
}) |
|||
} |
|||
catch (e) { |
|||
console.log('广播发送失败:', e, message) |
|||
} |
|||
} |
|||
|
|||
export const initWebSocketServer = () => { |
|||
const wss = new WebSocketServer({ port: 9527 }) |
|||
|
|||
wss.on('connection', (ws) => { |
|||
clients.add(ws) |
|||
ws.on('close', () => { |
|||
console.log('客户端断开') |
|||
}) |
|||
}) |
|||
} |
@ -0,0 +1,5 @@ |
|||
import http from 'libs/http' |
|||
|
|||
export const getContainerList = (): Promise<Container.ContainerItem[]> => http.get(`/container/list`) |
|||
|
|||
export const updateContainer = (params: Container.ContainerItem): Promise<null> => http.put(`/container`, params) |
@ -0,0 +1,34 @@ |
|||
import http from 'libs/http' |
|||
|
|||
interface Craft { |
|||
id?: number |
|||
name?: string |
|||
steps?: string |
|||
oresId?: number |
|||
} |
|||
|
|||
export const getCraftList = (params: System.Page = { pageNum: 1, pageSize: 999 }): Promise<System.PageResponse<CraftTypes.Craft>> => http.get(`/crafts/list`, { params }) |
|||
|
|||
export const createCraft = (params: Craft): Promise<null> => http.post('/crafts', params) |
|||
|
|||
export const updateCraft = (params: Craft): Promise<null> => http.put(`/crafts`, params) |
|||
|
|||
export const delCraft = (ids: string): Promise<null> => http.delete(`/crafts/${ids}`) |
|||
|
|||
// 开始执行工艺
|
|||
export const startCraft = (params: { heatId?: string, craftId?: number, columns?: number[] }): Promise<null> => http.post(`/crafts/start`, params) |
|||
export const pauseCraft = (params: { heatId: string }): Promise<null> => http.post(`/crafts/pause`, params) |
|||
export const resumeCraft = (params: { heatId: string }): Promise<null> => http.post(`/crafts/resume`, params) |
|||
|
|||
export const stopCraft = (params: { heatId: string }): Promise<null> => http.post(`/crafts/stop`, params) |
|||
|
|||
// 加热区配置工艺
|
|||
export const setCraft = (params: { heatId?: string | number, craftId?: string | number }): Promise<null> => http.post(`/crafts/set`, params) |
|||
|
|||
export const craftstatus = (): Promise<CraftTypes.CraftState[]> => http.get(`/monitor/crafts/status`) |
|||
|
|||
export const craftstatusByHeatId = (heatId: string): Promise<CraftTypes.CraftState> => http.get(`/monitor/crafts/status/${heatId}`) |
|||
export const craftList = (): Promise<any> => http.get(`/crafts/monitor/list`) |
|||
export const craftRestart = (params: any): Promise<CraftTypes.CraftState> => http.post(`/crafts/restart`, params) |
|||
export const craftRestore = (isRestore: boolean): Promise<null> => http.get(`/crafts/restore/isRestore=${isRestore}`) |
|||
export const craftRemove = (monitorId: string): Promise<CraftTypes.CraftState> => http.delete(`/crafts/remove`, { params: { monitorId } }) |
@ -0,0 +1,13 @@ |
|||
import http from 'libs/http' |
|||
|
|||
const baseUrl = '/tasks/' |
|||
export const setTargetTemperature = (params: Home.SetTargetTemperatureParams): Promise<null> => http.post('/heat/target-temperature', params) |
|||
export const trayTube = (params: Home.TrayTubeParams): Promise<null> => http.post('/tray/tube', params) |
|||
export const addTask = (params: Task.TaskAdd): Promise<null> => http.post(baseUrl, params) |
|||
export const stopTask = (): Promise<null> => http.post(`${baseUrl}stop`) |
|||
export const getTask = (id: number): Promise<Task.Task> => http.get(`${baseUrl}${id}`) |
|||
export const taskList = (params: Task.TaskQuery): Promise<Task.Task[]> => http.get(`${baseUrl}list`, { params }) |
|||
export const getTaskIng = (): Promise<null> => http.get(`${baseUrl}getIngTask`) |
|||
export const delTask = (params: string): Promise<null> => http.delete(`${baseUrl}${params}`) |
|||
export const trayIn = (): Promise<null> => http.post('tray/in') |
|||
export const trayOut = (): Promise<null> => http.post('tray/out') |
@ -0,0 +1,4 @@ |
|||
import http from 'libs/http' |
|||
|
|||
export const login = (params: User.Login): Promise<User.User> => http.post('/auth/login', params) |
|||
export const logout = (): Promise<null> => http.post('/auth/logout') |
@ -0,0 +1,4 @@ |
|||
import http from 'libs/http' |
|||
|
|||
export const getPointList = (): Promise<Point.Point[]> => http.get('/device-point/list') |
|||
export const updatePoint = (params: Point.UpdateParams): Promise<null> => http.put('/device-point', params) |
@ -0,0 +1,4 @@ |
|||
import http from 'libs/http' |
|||
|
|||
export const getSelfStatus = (): Promise<Record<string, boolean>> => http.get('/self-test/status') |
|||
export const getSelfFinish = (params: boolean): Promise<Record<string, boolean>> => http.post('/self-test/finish', { params: { mode: params } }) |
@ -0,0 +1,9 @@ |
|||
import http from 'libs/http' |
|||
|
|||
export const getSolsList = (params: System.Page = { pageNum: 1, pageSize: 999 }): Promise<System.PageResponse<Solution.SolutionItem>> => http.get(`/sols/list`, { params }) |
|||
|
|||
export const saveSols = (params: { name: string }): Promise<null> => http.post(`/sols`, params) |
|||
|
|||
export const editSols = (params: { id: number, name: string }): Promise<null> => http.put(`/sols`, params) |
|||
|
|||
export const delSols = (ids: string): Promise<null> => http.delete(`/sols/${ids}`) |
@ -0,0 +1,12 @@ |
|||
import http from 'libs/http' |
|||
|
|||
export const debugControl = <T>(params: System.CmdControlParams<T>): Promise<null> => http.post('/debug/cmd', params) |
|||
export const control = <T>(params: System.CmdControlParams<T>): Promise<null> => http.post('/cmd', params) |
|||
export const getStatus = (): Promise<System.SystemStatus> => http.get('/sys/device-status') |
|||
export const getPoint = (motor: string): Promise<string> => http.get(`/motor/position/${motor}`) |
|||
export const requireOutTray = (): Promise<{ moduleCode: string }[]> => http.get('/self-test/require-out-tray') |
|||
export const setIgnoreItem = (params: { ignoreSelfTestType: string, ignore: boolean }): Promise<null> => http.post('/self-test/set-ignore-item', params) |
|||
export const getTime = (): Promise<number> => http.get('/sys/datetime') |
|||
export const setTime = (params: { datetime?: string }): Promise<number> => http.post('/sys/datetime', params) |
|||
export const configList = (): Promise<any[]> => http.get('/sys/config-list') |
|||
export const updateConfig = (params: System.SystemConfig): Promise<number> => http.post('/sys/update-config', params) |
@ -0,0 +1,7 @@ |
|||
import http from 'libs/http' |
|||
|
|||
export const current = (): Promise<User.User> => http.get('/auth/current') |
|||
export const addUser = (params: User.User): Promise<null> => http.post('/user', params) |
|||
export const updateUser = (params: User.User): Promise<null> => http.put('/user', params) |
|||
export const userList = (params: System.Page): Promise<System.PageResponse<User.User>> => http.get('/user/list', { params }) |
|||
export const delUser = (params: string): Promise<null> => http.delete(`/user/${params}`) |
@ -0,0 +1,118 @@ |
|||
<script setup lang="ts"> |
|||
// import { getStatus } from 'apis/system' |
|||
import { socket } from 'libs/socket' |
|||
import { useSystemStore } from 'stores/systemStore' |
|||
import { onBeforeUnmount, onMounted, ref } from 'vue' |
|||
|
|||
const systemStore = useSystemStore() |
|||
onMounted(async () => { |
|||
// const res = await getStatus() |
|||
// console.log(res) |
|||
// console.log(systemStore.systemStatus) |
|||
// systemStore.updateSystemStatus(res) |
|||
startProgress() |
|||
}) |
|||
|
|||
socket.init((data: System.SystemStatus) => { |
|||
console.log(data) |
|||
systemStore.updateSystemStatus(data) |
|||
}, 'status') |
|||
|
|||
const progress = ref(0) |
|||
let timer: any = null |
|||
const version = __APP_VERSION__ |
|||
|
|||
const startProgress = () => { |
|||
const max = Math.floor(Math.random() * (90 - 80 + 1)) + 80 |
|||
timer = setInterval(() => { |
|||
const randomStep = Math.floor(Math.random() * 9 + 1) |
|||
|
|||
if (systemStore.systemStatus.initComplete) { |
|||
// 停止计时器 |
|||
progress.value = Math.min(progress.value + randomStep, 100) |
|||
} |
|||
else { |
|||
progress.value = Math.min(progress.value + randomStep, max) |
|||
} |
|||
|
|||
if (progress.value >= 100) { |
|||
clearInterval(timer) |
|||
} |
|||
}, 100) |
|||
} |
|||
|
|||
// 组件卸载时清理定时器 |
|||
onBeforeUnmount(() => { |
|||
clearInterval(timer) |
|||
}) |
|||
</script> |
|||
|
|||
<template> |
|||
<!-- 进度条容器 --> |
|||
<div v-if="progress < 100" class="main-content"> |
|||
<div class="progress-container"> |
|||
<div class="progress-bar" :style="{ width: `${progress}%` }" /> |
|||
<div class="progress-text"> |
|||
v{{ version }}系统初始化中 {{ progress }}% |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<router-view v-else v-slot="{ Component }" class="main-content"> |
|||
<transition name="el-zoom-in-center"> |
|||
<component :is="Component" /> |
|||
</transition> |
|||
</router-view> |
|||
</template> |
|||
|
|||
<style scoped lang="scss"> |
|||
.main-content { |
|||
width: 100%; |
|||
height: 100%; |
|||
background: url("assets/images/background.svg") no-repeat center; |
|||
background-size: cover; |
|||
overflow: hidden; |
|||
} |
|||
.login-container { |
|||
display: flex; |
|||
flex-direction: column; |
|||
align-items: center; |
|||
justify-content: center; |
|||
height: 100vh; |
|||
background: #f0f2f5; |
|||
} |
|||
img { |
|||
width: 600px; |
|||
position: absolute; |
|||
top: 40%; |
|||
right: 20%; |
|||
} |
|||
|
|||
.progress-container { |
|||
width: 50%; |
|||
height: 20px; |
|||
background: #e4e7ed; |
|||
border-radius: 30px; |
|||
position: relative; |
|||
top: 90%; |
|||
margin: 0 auto; |
|||
} |
|||
|
|||
.progress-bar { |
|||
height: 100%; |
|||
background: linear-gradient(90deg, #1989FA, #096ae0); |
|||
border-radius: 30px; |
|||
transition: width 0.3s ease; |
|||
} |
|||
|
|||
.progress-text { |
|||
position: absolute; |
|||
top: 50%; |
|||
left: 50%; |
|||
transform: translate(-50%, -50%); |
|||
color: #fff; |
|||
font-size: 12px; |
|||
line-height: 12px; |
|||
font-weight: 500; |
|||
} |
|||
</style> |
1
src/assets/images/background.svg
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View 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="160" height="217" viewBox="0 0 160 217"><defs><clipPath id="master_svg0_701_8873"><rect x="0" y="0" width="160" height="217" rx="0"/></clipPath><linearGradient x1="0.8473145961761475" y1="-0.13048571348190308" x2="-0.21846741185260635" y2="0.45531219158762304" id="master_svg1_701_9703"><stop offset="0%" stop-color="#9CA0FF" stop-opacity="1"/><stop offset="100%" stop-color="#9CCFFF" stop-opacity="1"/></linearGradient><linearGradient x1="1.0152618885040283" y1="0.07957261055707932" x2="0.19026522035783114" y2="1.0885049011631125" id="master_svg2_701_9704"><stop offset="0%" stop-color="#F2F3FF" stop-opacity="1"/><stop offset="100%" stop-color="#F2F9FF" stop-opacity="1"/></linearGradient></defs><g clip-path="url(#master_svg0_701_8873)"><g><rect x="41.62646484375" y="0" width="76.74667358398438" height="42.228694915771484" rx="5" fill="url(#master_svg1_701_9703)" fill-opacity="1" style="mix-blend-mode:passthrough"/></g><g><rect x="0" y="21.1171875" width="160" height="195.88375854492188" rx="5" fill="url(#master_svg2_701_9704)" fill-opacity="1" style="mix-blend-mode:passthrough"/></g><g><path d="M108.87600390624999,94.94093875L51.01304390625,94.94093875C48.43994390625,94.94098875,46.35400390625,92.85729875,46.35400390625,90.28692875C46.35605003625,87.71803875,48.44143390625,85.6366722972,51.01304390625,85.63671875L108.87590390625,85.63671875C111.44750390625,85.6366722972,113.53290390625,87.71803875,113.53490390625,90.28692875C113.53500390625,92.85724875,111.44910390625,94.94098875,108.87600390624999,94.94093875Z" fill="#FFFFFF" fill-opacity="1" style="mix-blend-mode:passthrough"/></g><g><path d="M108.87600390624999,123.71047L51.01304390625,123.71047C48.43994390625,123.71052,46.35400390625,121.62683,46.35400390625,119.05646C46.35605003625,116.48757,48.44143390625,114.4062035472,51.01304390625,114.40625L108.87590390625,114.40625C111.44750390625,114.4062035472,113.53290390625,116.48757,113.53490390625,119.05646C113.53500390625,121.62678,111.44910390625,123.71052,108.87600390624999,123.71047Z" fill="#FFFFFF" fill-opacity="1" style="mix-blend-mode:passthrough"/></g><g><path d="M108.87600390624999,152.48000125L51.01304390625,152.48000125C48.43994390625,152.48000125,46.35400390625,150.39631125,46.35400390625,147.82599125C46.35614304625,145.25715125,48.44143390625,143.17578125,51.01304390625,143.17578125L108.87590390625,143.17578125C111.44750390625,143.17578125,113.53280390625,145.25715125,113.53490390625,147.82599125C113.53500390625,150.39631125,111.44910390625,152.48000125,108.87600390624999,152.48000125Z" fill="#FFFFFF" fill-opacity="1" style="mix-blend-mode:passthrough"/></g></g></svg> |
@ -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="80" height="80" viewBox="0 0 80 80"><defs><clipPath id="master_svg0_685_8859"><rect x="0" y="0" width="80" height="80" rx="0"/></clipPath></defs><g clip-path="url(#master_svg0_685_8859)"><g><path d="M39.9853890625,4.94140625C20.7118890625,4.94140625,4.9462890625,20.70700625,4.9462890625,39.98050625C4.9462890625,59.25390625,20.7118890625,75.01950625,39.9853890625,75.01950625C59.2587890625,75.01950625,75.0243890625,59.25390625,75.0243890625,39.98050625C75.0243890625,20.70700625,59.2587890625,4.94140625,39.9853890625,4.94140625ZM56.7274890625,52.05080625C57.8993890625,53.22260625,57.8993890625,55.36330625,56.7274890625,56.52730625C56.1415890625,57.11330625,55.3681890625,57.50390625,54.3915890625,57.50390625C53.6103890625,57.50390625,52.6415890625,57.11330625,52.0556890625,56.52730625L40.7665890625,45.23830625L29.4774890625,56.52730625C28.3056890625,57.69920625,26.1650890625,57.69920625,25.0009890625,56.52730625C23.8368890625,55.35550625,23.8290890625,53.21480625,25.0009890625,52.05080625L36.2900890625,40.76170625L25.0009890625,29.47260625C24.4150890625,28.88670625,24.0243890625,28.11330625,24.0243890625,27.13670625C24.0243890625,26.35550625,24.4150890625,25.38670625,25.0009890625,24.80080625C26.1728890625,23.62890625,28.3134890625,23.62890625,29.4774890625,24.80080625L40.7665890625,36.089806249999995L52.0556890625,24.80080625C52.6415890625,24.21480625,53.4150890625,23.82420625,54.3915890625,23.82420625C55.1728890625,23.82420625,56.1415890625,24.21480625,56.7275890625,24.80080625C57.3134890625,25.38670625,57.7040890625,26.16020625,57.7040890625,27.13670625C57.7040890625,27.91800625,57.3134890625,28.88670625,56.7275890625,29.47260625L45.4384890625,40.76170625L56.7274890625,52.05080625Z" fill="#DF1515" fill-opacity="1" style="mix-blend-mode:passthrough"/></g></g></svg> |
@ -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="30.666667938232422" height="26.83333396911621" viewBox="0 0 30.666667938232422 26.83333396911621"><g><path d="M0,0L30.6667,0L30.6667,3.83333L0,3.83333L0,0ZM0,23L30.6667,23L30.6667,26.8333L0,26.8333L0,23 ZM0,11.5L30.6667,11.5L30.6667,15.3333L0,15.3333L0,11.5Z" fill="#1989FA" fill-opacity="1" style="mix-blend-mode:passthrough"/></g></svg> |
@ -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="26.303199768066406" height="26.303173065185547" viewBox="0 0 26.303199768066406 26.303173065185547"><g><path d="M24.5875,14.8666L14.8674,14.8666L14.8674,24.5882C14.8674,25.5356,14.099,26.3032,13.1516,26.3032C12.2043,26.3032,11.4359,25.5356,11.4359,24.5882L11.4359,14.8666L1.71574,14.8666C0.767578,14.8666,0,14.099,0,13.1516C0,12.2046,0.767666,11.4366,1.71574,11.4366L11.4359,11.4366L11.4359,1.71495C11.4359,0.76793,12.2042,0,13.1516,0C14.099,0,14.8673,0.767959,14.8673,1.71495L14.8673,11.4367L24.5874,11.4367C25.5356,11.4367,26.3032,12.2046,26.3032,13.1515C26.3032,14.0989,25.5356,14.8665,24.5874,14.8665L24.5875,14.8666Z" fill="#479CF1" fill-opacity="1" style="mix-blend-mode:passthrough"/></g></svg> |
@ -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="23" viewBox="0 0 15 23"><g transform="matrix(-1,0,0,-1,30,46)"><path d="M27.436500000000002,23C27.436500000000002,23,30,25.37044,30,25.37044C30,25.37044,20.12696,34.5,20.12696,34.5C20.12696,34.5,30,43.629599999999996,30,43.629599999999996C30,43.629599999999996,27.436500000000002,46,27.436500000000002,46C27.436500000000002,46,15,34.5,15,34.5C15,34.5,27.436500000000002,23,27.436500000000002,23C27.436500000000002,23,27.436500000000002,23,27.436500000000002,23Z" fill-rule="evenodd" fill="#384D5D" fill-opacity="1"/></g></svg> |
@ -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="33" height="33" viewBox="0 0 33 33"><defs><clipPath id="master_svg0_685_8832"><rect x="0" y="0" width="33" height="33" rx="0"/></clipPath></defs><g clip-path="url(#master_svg0_685_8832)"><g><path d="M28.5707296875,29.2627375C27.6653296875,30.1682375,26.1906296875,30.1682375,25.2831296875,29.2627375L4.4580476875,8.4376975C3.5526096875,7.5301975,3.5505466875,6.0575675,4.4580476875,5.1500705C5.3614196875,4.2425705,6.8361096875000005,4.2425705,7.7477296875,5.1500705L28.5707296875,25.9730375C29.4761296875,26.8847375,29.4803296875,28.3552375,28.5707296875,29.2627375ZM28.5460296875,8.4273875L7.7229796875000005,29.2503375C6.8134196875,30.1599375,5.3407996875,30.1599375,4.4332966875,29.2503375C3.5257966875,28.3449375,3.5257966875,26.8743375,4.4353596875,25.9627375L25.2583296875,5.1418205C26.1679296875,4.2343205,27.6426296875,4.2343205,28.5460296875,5.1397585C29.4555296875,6.0472575,29.4514296875,7.519887499999999,28.5460296875,8.4273875Z" fill="#7F7F7F" fill-opacity="1" style="mix-blend-mode:passthrough"/></g></g></svg> |
1
src/assets/images/ing.svg
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1 @@ |
|||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1749632093752" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4247" xmlns:xlink="http://www.w3.org/1999/xlink" width="48" height="48"><path d="M854.33 557.59L750.14 446.03c0.69-5.85 1.05-11.8 1.05-17.84V98.63c0-18.86-15.35-34.21-34.21-34.21H304.23c-18.86 0-34.21 15.35-34.21 34.21v329.56c0 6.94 0.48 13.77 1.39 20.47L169.67 557.59c-54.93 5.75-97.9 52.33-97.9 108.75 0 52.13 36.69 95.79 85.59 106.68l64.18 177.62c1.99 5.49 7.19 9.07 12.91 9.07 0.58 0 1.17-0.03 1.76-0.11 0.29-0.04 0.52-0.19 0.8-0.25 0.41 0.08 0.75 0.37 1.18 0.37 0.45 0 0.91-0.04 1.36-0.13L317.02 944c3.37-0.68 6.29-2.67 7.98-5.47 1.58-2.63 1.98-5.69 1.11-8.62l-58.26-197.28c12.82-16.78 20.97-37.32 22.31-59.7l94.42-97.67c11.87 2.99 24.28 4.59 37.07 4.59h177.91c13.58 0 26.75-1.81 39.29-5.17l94.98 98.26c1.34 22.38 9.49 42.92 22.31 59.7l-58.26 197.28c-0.87 2.93-0.47 5.99 1.11 8.62 1.69 2.8 4.61 4.79 7.98 5.47l77.46 15.59c0.46 0.09 0.91 0.13 1.36 0.13 0.43 0 0.77-0.29 1.18-0.37 0.28 0.05 0.51 0.21 0.8 0.25 0.59 0.08 1.18 0.11 1.76 0.11 5.72 0 10.92-3.57 12.91-9.07l64.18-177.62c48.9-10.89 85.59-54.55 85.59-106.68 0.02-56.43-42.95-103.01-97.88-108.76zM425.69 168.2c15.23 0 27.57 12.34 27.57 27.57s-12.34 27.57-27.57 27.57-27.57-12.34-27.57-27.57c0-15.22 12.34-27.57 27.57-27.57z m-71.1 0c15.23 0 27.57 12.34 27.57 27.57s-12.34 27.57-27.57 27.57-27.57-12.34-27.57-27.57c0-15.22 12.35-27.57 27.57-27.57z m-57.11 140.93h426.25v58.59H297.48v-58.59z m-71.12 574L187.43 775.4c18.26-1.05 35.29-6.56 50.07-15.51l-11.14 123.24z m36.03-207.04c-0.03 0.24-0.08 0.48-0.11 0.72-0.42 3.08-0.98 6.12-1.71 9.09-2.05 8.31-5.48 16.06-9.84 23.16-0.36 0.59-0.64 1.24-1.02 1.82-2.25 3.46-4.79 6.71-7.52 9.79-0.94 1.05-2 1.98-2.99 2.99-1.93 1.96-3.9 3.88-6.01 5.64-1.27 1.05-2.61 2-3.94 2.97a82.524 82.524 0 0 1-6.34 4.26 79.83 79.83 0 0 1-4.44 2.47c-2.3 1.19-4.66 2.24-7.08 3.21-1.52 0.61-3.04 1.24-4.61 1.76-2.71 0.9-5.5 1.58-8.33 2.19-1.39 0.3-2.75 0.7-4.16 0.93-4.29 0.7-8.67 1.13-13.15 1.13-2.78 0-5.52-0.14-8.23-0.42-2.49-0.25-4.95-0.64-7.37-1.11-0.19-0.04-0.39-0.05-0.57-0.09a81.34 81.34 0 0 1-14.97-4.57c-29.75-12.28-50.75-41.57-50.75-75.71 0-39.2 27.7-72.02 64.54-80 2.74-0.59 5.53-1.05 8.36-1.37l0.2-0.03c2.89-0.32 5.82-0.49 8.79-0.49 3.6 0 7.13 0.3 10.61 0.77 0.55 0.07 1.1 0.17 1.65 0.25 3.36 0.53 6.68 1.2 9.9 2.11 28.09 7.91 50.04 30.43 57.19 58.84 0.76 3.01 1.34 6.08 1.77 9.2 0.04 0.28 0.09 0.55 0.13 0.83 0.42 3.24 0.66 6.54 0.66 9.89-0.02 3.33-0.25 6.58-0.66 9.77z m24.06-38.84c-10.67-38.57-41.93-68.62-81.2-77.5l74.05-79.29c13.69 37.17 41.54 67.55 76.99 84.55l-69.84 72.24z m451.1 0l-70.73-73.18c35.22-17.5 62.71-48.3 75.86-85.78l76.07 81.46c-39.27 8.88-70.52 38.93-81.2 77.5z m60.09 245.88L786.5 759.88c14.77 8.96 31.81 14.47 50.07 15.51l-38.93 107.74z m76.37-141.08a82.117 82.117 0 0 1-14.97 4.57c-0.19 0.04-0.38 0.05-0.57 0.09-2.42 0.47-4.87 0.86-7.37 1.11a82.9 82.9 0 0 1-8.23 0.42c-4.48 0-8.86-0.44-13.15-1.13-1.42-0.23-2.77-0.63-4.16-0.93-2.82-0.61-5.62-1.29-8.33-2.19-1.57-0.52-3.08-1.15-4.61-1.76-2.42-0.97-4.78-2.02-7.08-3.21a79.83 79.83 0 0 1-4.44-2.47c-2.19-1.31-4.29-2.75-6.34-4.26-1.33-0.97-2.68-1.92-3.94-2.97-2.12-1.76-4.08-3.68-6.01-5.64-0.99-1.01-2.05-1.93-2.99-2.99-2.73-3.07-5.27-6.33-7.52-9.79-0.38-0.58-0.65-1.23-1.02-1.82-4.37-7.1-7.79-14.85-9.84-23.16-0.73-2.97-1.3-6.01-1.71-9.09-0.03-0.24-0.08-0.48-0.11-0.72-0.4-3.2-0.64-6.44-0.64-9.75 0-3.36 0.24-6.65 0.66-9.89 0.03-0.28 0.09-0.56 0.13-0.83 0.43-3.12 1.01-6.2 1.77-9.2 7.14-28.4 29.09-50.92 57.19-58.84 3.22-0.91 6.54-1.58 9.9-2.11 0.55-0.08 1.09-0.18 1.65-0.25 3.48-0.47 7.01-0.77 10.61-0.77 2.97 0 5.9 0.17 8.79 0.49l0.2 0.03c2.83 0.32 5.62 0.78 8.36 1.37 36.84 7.99 64.54 40.8 64.54 80-0.02 34.12-21.02 63.41-50.77 75.69z" fill="#2c2c2c" p-id="4248"></path><path d="M842.86 666.34m-38.04 0a38.04 38.04 0 1 0 76.08 0 38.04 38.04 0 1 0-76.08 0Z" fill="#2c2c2c" p-id="4249"></path><path d="M181.14 666.34m-38.04 0a38.04 38.04 0 1 0 76.08 0 38.04 38.04 0 1 0-76.08 0Z" fill="#2c2c2c" p-id="4250"></path><path d="M511.3 660.66c59.33 0 91.96-44.32 94.93-57.42H416.37c8.9 31.77 51.13 57.42 94.93 57.42z" fill="#2c2c2c" p-id="4251"></path></svg> |
@ -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="162.44374084472656" height="227.99989318847656" viewBox="0 0 162.44374084472656 227.99989318847656"><g><path d="M0,54.0618L0,64.2474C0,65.1128,0.699314,65.8144,1.56196,65.8144C2.4246,65.8144,3.12392,66.516,3.12392,67.3814L3.12392,73.6495C3.12392,74.5149,2.4246,75.2165,1.56196,75.2165C0.699314,75.2165,0,75.918,0,76.7835L0,90.8866C0,91.752,0.699314,92.4536,1.56196,92.4536C2.4246,92.4536,3.12392,93.1551,3.12392,94.0206L3.12392,100.289C3.12392,101.154,2.4246,101.856,1.56196,101.856C0.699314,101.856,0,102.557,0,103.423L0,122.227C0,123.092,0.699314,123.794,1.56196,123.794C2.4246,123.794,3.12392,124.495,3.12392,125.361L3.12392,131.629C3.12392,132.494,2.4246,133.196,1.56196,133.196C0.699314,133.196,0,133.897,0,134.763L0,153.567C0,154.432,0.699314,155.134,1.56196,155.134C2.4246,155.134,3.12392,155.836,3.12392,156.701L3.12392,162.969C3.12392,163.834,2.4246,164.536,1.56196,164.536C0.699314,164.536,0,165.238,0,166.103L0,183.34C0,184.206,0.699314,184.907,1.56196,184.907C2.4246,184.907,3.12392,185.609,3.12392,186.474L3.12392,193.526C3.12392,194.391,2.4246,195.093,1.56196,195.093C0.699314,195.093,0,195.794,0,196.66L0,206.062C0,218.178,9.79038,228,21.8674,228L140.576,228C152.653,228,162.444,218.178,162.444,206.062L162.444,196.66C162.444,195.794,161.744,195.093,160.882,195.093C160.019,195.093,159.32,194.391,159.32,193.526L159.32,186.474C159.32,185.609,160.019,184.907,160.882,184.907C161.744,184.907,162.444,184.206,162.444,183.34L162.444,166.103C162.444,165.238,161.744,164.536,160.882,164.536C160.019,164.536,159.32,163.834,159.32,162.969L159.32,156.701C159.32,155.836,160.019,155.134,160.882,155.134C161.744,155.134,162.444,154.432,162.444,153.567L162.444,134.763C162.444,133.897,161.744,133.196,160.882,133.196C160.019,133.196,159.32,132.494,159.32,131.629L159.32,125.361C159.32,124.495,160.019,123.794,160.882,123.794C161.744,123.794,162.444,123.092,162.444,122.227L162.444,103.423C162.444,102.557,161.744,101.856,160.882,101.856C160.019,101.856,159.32,101.154,159.32,100.289L159.32,94.0206C159.32,93.1551,160.019,92.4536,160.882,92.4536C161.744,92.4536,162.444,91.752,162.444,90.8866L162.444,76.7835C162.444,75.918,161.744,75.2165,160.882,75.2165C160.019,75.2165,159.32,74.5149,159.32,73.6495L159.32,67.3814C159.32,66.516,160.019,65.8144,160.882,65.8144C161.744,65.8144,162.444,65.1128,162.444,64.2474L162.444,54.0618C162.444,41.9457,152.653,32.1237,140.576,32.1237L134.962,32.1237C132.711,32.1237,131.583,29.3931,133.175,27.7961C137.914,23.042,140.576,16.594,140.576,9.87062L140.576,4.70103C140.576,2.10472,138.478,0,135.89,0L26.5533,0C23.9654,0,21.8674,2.10472,21.8674,4.70103L21.8674,9.87062C21.8674,16.594,24.5297,23.042,29.2685,27.7961C30.8603,29.3931,29.7329,32.1237,27.4817,32.1237L21.8674,32.1237C9.79038,32.1237,0,41.9457,0,54.0618Z" fill="#EEEFF8" fill-opacity="1"/></g></svg> |
1
src/assets/images/login.svg
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View 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="224" height="233" viewBox="0 0 224 233"><g><g><path d="M223.99979977416993,114.45162603759766C223.99979977416993,124.83402603759765,215.86279977416993,133.25062603759767,205.82579977416992,133.25062603759767C195.7877997741699,133.25062603759767,89.86419977416992,119.90242603759765,46.42679977416992,114.45162603759766C95.25419977416992,108.22022603759766,195.7877997741699,95.65262603759766,205.82579977416992,95.65262603759766C215.86279977416993,95.65264605499766,223.99979977416993,104.06923603759766,223.99979977416993,114.45162603759766Z" fill="#FAC03D" fill-opacity="1"/></g><g><path d="M201.21678225097656,176.92624592285156C201.23438225097658,182.56134592285156,196.82298225097657,187.13904592285155,191.37518225097656,187.13904592285155C185.94078225097655,187.13904592285155,128.59708225097657,179.92384592285157,105.07118225097656,176.95904592285157C131.50778225097656,173.58095592285156,185.94078225097655,166.77894592285156,191.37518225097656,166.77894592285156C196.79818225097657,166.77890588795157,201.19928225097658,171.31673592285156,201.21678225097656,176.92624592285156Z" fill="#FAC03D" fill-opacity="1"/></g><g><path d="M215.93901684570312,149.52852961425782C215.94201684570314,157.61312961425782,209.60701684570313,164.16892961425782,201.79101684570313,164.16892961425782C193.97901684570314,164.16892961425782,114.44741684570312,153.77242961425782,80.63601684570312,149.52852961425782C118.64531684570312,144.68120961425782,193.97901684570314,134.8947296142578,201.79101684570313,134.8947296142578C209.60501684570312,134.8947296142578,215.93901684570312,141.44651961425782,215.93901684570312,149.52852961425782Z" fill="#FAC03D" fill-opacity="1"/></g><g><path d="M112.735,0.0000200174C33.3546,-0.0135318,-21.121,82.656,7.90892,159.078C36.9389,235.5,131.249,257.697,189.387,201.791C181.175,201.01,163.001,198.721,131.435,194.661L127.085,194.11C85.6178,216.585,34.3882,200.014,12.688,157.106C-9.01216,114.197,7.04277,61.2171,48.539,38.7996C90.0352,16.382,141.243,33.0249,162.887,75.9634C169.261,88.781,184.624,93.5998,196.807,86.6023C208.989,79.6049,213.157,63.5677,206.008,51.1954C185.072,19.1593,150.118,-0.0260227,112.735,0.0000200174Z" fill="#275EFB" fill-opacity="1"/></g></g></svg> |
@ -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="42" height="43" viewBox="0 0 42 43"><g><g><path d="M23.9012,26.80821171875L23.5184,26.80821171875C19.5186,26.80821171875,15.5189,26.80861171875,11.519,26.80821171875C10.926,26.80801171875,10.5396,26.52971171875,10.4443,26.03191171875C10.4255,25.93251171875,10.4273,25.82801171875,10.4273,25.72631171875C10.4266,22.08721171875,10.4265,18.44831171875,10.4268,14.80931171875C10.4269,14.11901171875,10.792,13.75181171875,11.4767,13.75181171875C15.4946,13.75161171875,19.5126,13.75161171875,23.5307,13.75161171875L23.9012,13.75161171875L23.9012,13.42041171875C23.9012,10.18168171875,23.9008,6.94273171875,23.9016,3.70375171875C23.9016,3.25804771875,24.1092,2.90257571875,24.4607,2.76641631875C24.8652,2.60961481875,25.224,2.68183999875,25.5289,3.01164571875C25.7037,3.20077071875,25.8941,3.3747387187499998,26.0736,3.55867171875C28.3386,5.87259171875,30.6032,8.18756171875,32.8676,10.50147171875C35.7762,13.47271171875,38.6848,16.44331171875,41.5933,19.41441171875C42.0776,19.90851171875,42.0802,20.42981171875,41.6017,20.91861171875C37.4394,25.17281171875,33.2774,29.42731171875,29.1138,33.67981171875C27.9297,34.88921171875,26.7392,36.09131171875,25.5561,37.30171171875C25.2516,37.61371171875,24.912,37.75041171875,24.4963,37.58871171875C24.092,37.43171171875,23.9017,37.10601171875,23.9017,36.59371171875C23.901,33.45751171875,23.9012,30.32081171875,23.9012,27.18461171875L23.9012,26.80821171875L23.9012,26.80821171875ZM16.4243,2.86945471875L16.4243,6.546731718749999L16.0943,6.546731718749999C13.049,6.546731718749999,10.0037,6.54631171875,6.95843,6.54694171875C5.02868,6.54723171875,3.60763,8.00513171875,3.60755,9.98472171875C3.60726,16.83461171875,3.61953,23.68451171875,3.59949,30.53391171875C3.59397,32.34621171875,4.94067,33.65191171875,6.18574,33.89041171875C6.46085,33.94251171875,6.74508,33.96611171875,7.02513,33.96681171875C10.0431,33.97221171875,13.0612,33.970111718750005,16.0793,33.970111718750005L16.4189,33.970111718750005L16.4189,37.62641171875C16.3759,37.63521171875,16.3258,37.65491171875,16.2756,37.65491171875C13.0666,37.65491171875,9.85733,37.68831171875,6.64918,37.64061171875C3.51369,37.59461171875,0.737073,35.15841171875,0.138685,32.005211718750004C0.0542352,31.56101171875,0.0129305,31.10101171875,0.0124397,30.64841171875C0.00466952,23.72381171875,-0.0132019,16.79961171875,0.0166111,9.87552171875C0.0290843,6.98330171875,1.43263,4.90707171875,3.88372,3.55774671875C4.74846,3.08148071875,5.70366,2.86639671875,6.68823,2.86388471875C9.8698,2.85509271875,13.0514,2.86053471875,16.2331,2.86074471875C16.2868,2.86078671875,16.3405,2.86560171875,16.4243,2.86945471875Z" fill="#1989FA" fill-opacity="1"/></g></g></svg> |
@ -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="80" height="80" viewBox="0 0 80 80"><defs><clipPath id="master_svg0_592_42038"><rect x="19" y="18.921875" width="41" height="41" rx="0"/></clipPath></defs><g><g><ellipse cx="40" cy="40" rx="40" ry="40" fill="#EEEFF8" fill-opacity="1"/></g><g clip-path="url(#master_svg0_592_42038)"><g><path d="M53.8413984375,21.65234443077L25.1580984375,21.65234443077C23.2666884375,21.652344090385,21.733398779614,23.17788375,21.733399121728,25.05973375L21.733399121728,47.20904375L57.2666984375,47.20904375L57.2666984375,25.05973375C57.2670984375,23.17798375,55.7333984375,21.652344090385,53.8413984375,21.65234443077ZM52.9319984375,35.12904375C52.9319984375,35.83334375,52.3579984375,36.405643749999996,51.6500984375,36.406943749999996L47.5684984375,36.406943749999996C47.3869984375,36.40704375,47.2220984375,36.30194375,47.1462984375,36.13784375L46.7859984375,35.36184375C46.7178984375,35.21544375,46.5724984375,35.11984375,46.410498437499996,35.11504375C46.2483984375,35.11024375,46.0974984375,35.19694375,46.0206984375,35.33904375L42.2247984375,42.38224375C42.0768984375,42.65614375,41.7907984375,42.828143749999995,41.478398437500005,42.83104375C41.165898437500005,42.83394375,40.8765984375,42.66724375,40.7235984375,42.39624375L34.6923984375,31.69304375C34.6111984375,31.54864375,34.4531984375,31.46415375,34.2872984375,31.47640375C34.1214984375,31.48864375,33.9777984375,31.59540375,33.9187984375,31.75014375L32.3505984375,35.85744375C32.224298437499996,36.18814375,31.9057984375,36.40684375,31.5502384375,36.406943749999996L27.3519484375,36.406943749999996C26.6425784375,36.406943749999996,26.067528437500002,35.83474375,26.067528437500002,35.12904375C26.067528437500002,34.42324375,26.6425784375,33.85104375,27.3519484375,33.85104375L29.7753884375,33.85104375C30.1312484375,33.85104375,30.4494884375,33.63214375,30.5757584375,33.30154375L33.1400984375,26.58196375C33.2582984375,26.27280375,33.5454984375,26.05956375,33.8769984375,26.03496375C34.2084984375,26.01035375,34.5243984375,26.17881375,34.687298437500004,26.46711375L41.053898437499996,37.75784375C41.1302984375,37.89354375,41.2749984375,37.97704375,41.431298437500004,37.97554375C41.587698437499995,37.974143749999996,41.7307984375,37.887943750000005,41.804598437500005,37.75084375L45.789198437500005,30.36372375C45.9430984375,30.079963749999997,46.244798437499995,29.906903749999998,46.568698437500004,29.916573749999998C46.8925984375,29.926243749999998,47.1832984375,30.11697375,47.3197984375,30.40940375L48.6858984375,33.35554375C48.8261984375,33.65754375,49.129098437500005,33.85044375,49.4632984375,33.85104375L51.6500984375,33.85104375C52.3592984375,33.85104375,52.9344984375,34.423443750000004,52.9344984375,35.12904375L52.9319984375,35.12904375ZM21.733399121728,48.66654375L21.733399121728,53.77824375C21.733398779614,55.66014375,23.2666884375,57.18564375,25.1580984375,57.18564375L53.8413984375,57.18564375C55.7327984375,57.18564375,57.2660984375,55.66014375,57.2660984375,53.77824375L57.2660984375,48.66654375L21.733399121728,48.66654375ZM42.338998437499995,52.74404375C42.3368984375,53.44894375,41.7630984375,54.01984375,41.054598437500005,54.02194375L27.3519484375,54.02194375C26.6425784375,54.02194375,26.067528437500002,53.449743749999996,26.067528437500002,52.74404375C26.067528437500002,52.03824375,26.6425784375,51.46604375,27.3519484375,51.46604375L41.0519984375,51.46604375C41.761498437499995,51.46674375,42.3368984375,52.03814375,42.338998437499995,52.74404375ZM52.9319984375,52.74404375C52.9291984375,53.447443750000005,52.3570984375,54.01724375,51.6500984375,54.02064375L46.510498437500004,54.02064375C45.8010984375,54.02064375,45.2260984375,53.44854375,45.2260984375,52.74274375C45.2260984375,52.03694375,45.8010984375,51.46484375,46.510498437500004,51.46484375L51.6500984375,51.46484375C52.358098437500004,51.46834375,52.930598437499995,52.03954375,52.9319984375,52.74404375Z" fill="#4F85FB" fill-opacity="1" style="mix-blend-mode:passthrough"/></g></g></g></svg> |
@ -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="80" height="80" viewBox="0 0 80 80"><g><g><ellipse cx="40" cy="40" rx="40" ry="40" fill="#EEEFF8" fill-opacity="1"/></g><g><path d="M54.037,56.006L28.80688,56.006C27.22599,56.006,25.93399,54.6781,25.93399,53.0441C25.93399,51.6375,26.888939999999998,50.4627,28.16489,50.1565L28.16489,21.0455056C25.82164,21.380591,24,23.46143,24,25.96836L24,53.0523C24,55.7785,26.15066,58,28.79886,58L54.037,58C54.5706,58,55,57.5574,55,57.0072C55.004000000000005,56.457,54.5706,56.006,54.037,56.006ZM42.742000000000004,37.799800000000005L41.847300000000004,37.799800000000005L41.847300000000004,39.1815L40.5031,39.1815L40.5031,40.1081L41.847300000000004,40.1081L41.847300000000004,41.4898L42.742000000000004,41.4898L42.742000000000004,40.1081L44.090199999999996,40.1081L44.090199999999996,39.1815L42.742000000000004,39.1815L42.742000000000004,37.799800000000005ZM44.090199999999996,29.05031L40.5031,29.05031L40.5031,33.3775L39.2994,35.6817C39.817,35.5824,40.334599999999995,35.5617,40.852199999999996,35.6155C42.2605,35.7644,42.569500000000005,36.8814,44.4393,36.8814C44.985,36.8814,45.4384,36.8193,45.807500000000005,36.6952L44.090199999999996,33.3981L44.090199999999996,29.05031ZM53.2425,21L30.09086,21L30.09086,50.1276L30.5563,50.1276C30.612470000000002,50.1358,30.67266,50.1358,30.73284,50.1358L53.2425,50.1358C54.125299999999996,50.1358,54.8475,49.3912,54.8475,48.4811L54.8475,22.65888C54.8515,21.744634,54.125299999999996,21.000000315617,53.2425,21ZM49.3224,42.424800000000005C49.1619,42.6978,48.881,42.8674,48.5721,42.875699999999995L36.0212,42.875699999999995C35.7042,42.875699999999995,35.4113,42.7061,35.2508,42.424800000000005C35.0903,42.1435,35.082300000000004,41.8001,35.2348,41.5105L39.5963,33.2161L39.5963,29.05031L38.5089,29.05031C38.2521,29.05031,38.0475,28.8352,38.0475,28.57457C38.0475,28.309820000000002,38.2561,28.09884,38.5089,28.09884L46.0643,28.09884C46.3211,28.09884,46.5257,28.31395,46.5257,28.57457C46.5257,28.83933,46.317099999999996,29.05031,46.0643,29.05031L44.9769,29.05031L44.9769,33.2203L46.561800000000005,36.231899999999996L49.3425,41.5105C49.490899999999996,41.8001,49.4869,42.1434,49.3224,42.424800000000005Z" fill="#4F85FB" fill-opacity="1" style="mix-blend-mode:passthrough"/></g></g></svg> |
@ -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="80" height="80" viewBox="0 0 80 80"><g><g><ellipse cx="40" cy="40" rx="40" ry="40" fill="#EEEFF8" fill-opacity="1"/></g><g><path d="M20.0370799,43.6256C20.866486,47.6246,22.91091,51.222300000000004,25.94731,54.0277C26.3526,54.4028,26.9106,54.5752,27.46181,54.4956C28.0134,54.4165,28.49784,54.0943,28.77694,53.6208L31.1482,49.591300000000004C31.5442,48.9178,31.451999999999998,48.070499999999996,30.92,47.4942C29.68039,46.1482,28.77662,44.537800000000004,28.28009,42.7903C28.06083,42.015,27.34183,41.4784,26.52231,41.4784L21.82395,41.4784C21.27636,41.4781,20.757754,41.7198,20.411853,42.1363C20.0647341,42.552,19.9270634,43.0991,20.0370799,43.6256ZM44.1657,51.909C43.7696,51.233000000000004,42.9726,50.8869,42.1963,51.053799999999995C40.231899999999996,51.4761,38.1899,51.3965,36.2658,50.8227C35.453900000000004,50.580600000000004,34.579,50.9191,34.1541,51.6398L31.8262,55.5982C31.5578,56.055,31.5084,56.6046,31.691000000000003,57.1004C31.872999999999998,57.5967,32.2697,57.9884,32.7742,58.1703C34.9795,58.9695,37.3089,59.375,39.695,59.375C41.6678,59.375,43.6156,59.0954,45.482600000000005,58.543C46.0156,58.3847,46.446,57.9968,46.652,57.4891C46.8572,56.9806,46.814099999999996,56.408,46.535,55.9346L44.1657,51.909ZM52.5476,24.57127C52.2201,24.3027,51.806799999999996,24.15552,51.379999999999995,24.15542C51.2742,24.15542,51.1663,24.166330000000002,51.060500000000005,24.18424C50.531,24.2771,50.0703,24.59457,49.8016,25.05176L47.4685,29.01324C47.045100000000005,29.73472,47.183499999999995,30.6473,47.802800000000005,31.2165C49.39,32.6707,50.5347,34.5286,51.1096,36.5833C51.3275,37.3604,52.0476,37.8989,52.8691,37.899100000000004L57.568,37.899100000000004C58.1145,37.8988,58.6321,37.658,58.9784,37.2432C59.3258,36.8281,59.464,36.2815,59.3546,35.7553C58.4579,31.3953,56.0407,27.42316,52.5476,24.57127ZM46.652,21.88784C46.446600000000004,21.37975,46.0159,20.99153,45.482600000000005,20.833661C43.6053,20.279568,41.6553,19.99869145,39.695,20.00000417001C37.3089,20.00000417001,34.9795,20.405219,32.7742,21.20446C31.714199999999998,21.58978,31.262,22.8179,31.8262,23.77903L34.1541,27.73687C34.579,28.457549999999998,35.453900000000004,28.79609,36.2658,28.55402C37.3775,28.22171,38.5332,28.05317,39.6953,28.05394C40.530100000000004,28.05394,41.372299999999996,28.14349,42.1963,28.32119C42.9721,28.48767,43.7685,28.14262,44.1657,27.46794L46.5353,23.44182C46.8143,22.96858,46.857299999999995,22.39608,46.652,21.88784ZM57.568,41.4781L52.8691,41.4781C52.0475,41.477599999999995,51.327,42.0163,51.1096,42.7936C50.5345,44.848299999999995,49.3898,46.7063,47.802800000000005,48.160799999999995C47.1834,48.729,47.0449,49.6411,47.4685,50.3618L49.8016,54.3249C50.07,54.7824,50.5309,55.1001,51.060500000000005,55.1927C51.1663,55.2106,51.2742,55.2196,51.379999999999995,55.2196C51.8066,55.2202,52.2199,55.0737,52.5476,54.8057C56.0407,51.9518,58.4579,47.9814,59.3546,43.622C59.4641,43.0957,59.3259,42.549099999999996,58.9784,42.134C58.6326,41.718599999999995,58.1147,41.4778,57.568,41.4781ZM20.411568,37.241C20.757316,37.6573,21.27588,37.8989,21.82367,37.8989L26.52259,37.8989C27.34233,37.8986,28.06128,37.361999999999995,28.280369999999998,36.587C28.776699999999998,34.8389,29.68048,33.2279,30.920299999999997,31.8814C31.4524,31.3057,31.544600000000003,30.4588,31.1485,29.785890000000002L28.77722,25.75641C28.49772,25.28303,28.01361,24.96039,27.46209,24.87994C27.37433,24.86765,27.28578,24.86166,27.19713,24.86203C26.73679,24.86203,26.28729,25.03413,25.9476,25.349510000000002C22.91091,28.15468,20.866486,31.7521,20.0370799,35.7513C19.9271883,36.2779,20.0647273,36.825,20.411568,37.241Z" fill="#4F85FB" fill-opacity="1"/></g></g></svg> |
@ -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="80" height="80" viewBox="0 0 80 80"><g><g><ellipse cx="40" cy="40" rx="40" ry="40" fill="#EEEFF8" fill-opacity="1"/></g><g><path d="M30.42768,34.2143L30.42768,52.673C30.42768,53.4027,31.06121,54.0357,31.77429,54.0357L48.5584,54.0357C49.2714,54.0357,49.794,53.4027,49.794,52.673L49.794,34.2143L30.42768,34.2143ZM47.8574,46.726600000000005C47.8574,47.091499999999996,47.5684,47.387299999999996,47.2118,47.387299999999996C46.8553,47.387299999999996,46.5663,47.091499999999996,46.5663,46.726600000000005L46.5663,42.762299999999996C46.5663,42.397400000000005,46.8553,42.101600000000005,47.2118,42.101600000000005C47.5684,42.101600000000005,47.8574,42.397400000000005,47.8574,42.762299999999996L47.8574,46.726600000000005ZM47.2673,40.780100000000004C46.910799999999995,40.780100000000004,46.6218,40.4844,46.6218,40.1194C46.6218,39.7545,46.910799999999995,39.4587,47.2673,39.4587C47.6239,39.4587,47.9129,39.7545,47.9129,40.1194C47.9129,40.484300000000005,47.6239,40.780100000000004,47.2673,40.780100000000004ZM54.7629,21L29.21229,21C24.827023,21,24,23.26509,24,24.28292C26.44097,24.63393,26.55442,24.67522,26.55442,27.318080000000002L26.55442,52.673C26.55442,55.5922,28.92203,58,31.77429,58L48.5383,58C51.3904,58,53.6673,55.5922,53.6673,52.673L53.6673,24.96429C53.6673,23.28368,54.6596,21.803098,54.7579,21.6456C54.856300000000005,21.488186,55,21.331926,55,21.236123C55,21.139411,54.9664,21,54.7629,21ZM51.0851,24.96429L51.0851,52.673C51.0851,54.153,50.081,55.3571,48.679500000000004,55.3571L31.77429,55.3571C30.352719999999998,55.3571,29.136589999999998,54.128,29.136589999999998,52.673L29.136589999999998,27.318080000000002C29.136589999999998,26.34254,29.22487,24.24163,28.8277,23.6877C28.90202,23.62221,29.07898,23.64286,29.19211,23.64286L51.1939,23.64286C51.1345,23.64286,51.0851,24.53474,51.0851,24.96429Z" fill="#4F85FB" fill-opacity="1" style="mix-blend-mode:passthrough"/></g></g></svg> |
@ -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="80" height="80" viewBox="0 0 80 80"><g><g><ellipse cx="40" cy="40" rx="40" ry="40" fill="#EEEFF8" fill-opacity="1"/></g><g><path d="M25.010964,27.14686L25.010964,52.3976C25.00721385,52.5305,25.0362768,52.6662,25.0915901,52.791799999999995C25.490033,53.8368,27.08099,54.6141,29.95072,55.1751C33.25546,55.7635,36.6258,56.0395,39.9999,55.9955C43.375,56.0285,46.7406,55.7525,50.0501,55.1741C52.915099999999995,54.6031,54.536100000000005,53.8083,54.9083,52.7909C54.9636,52.6662,54.992599999999996,52.5342,54.9889,52.3976L54.9889,27.14686C55.003699999999995,27.099980000000002,55.003699999999995,27.04982,54.9889,27.00294L54.9889,26.77745C54.9889,25.48132,53.3829,24.50509,50.0454,23.860689999999998C46.734700000000004,23.255981,43.3715,22.9686265,40.0037,23.00271173C36.6286,22.9697126,33.26296,23.245622,29.95353,23.824024C26.64599,24.468420000000002,25.0109623,25.44373,25.0109623,26.74078C24.999922606,26.81525,24.999922606,26.89089,25.0109623,26.96536C24.996346,27.011960000000002,24.996346,27.06176,25.0109623,27.10836L25.0109623,27.14502L25.010964,27.14686ZM46.0047,44.9994C46.0122,44.4503,46.524100000000004,44.0048,47.1578,43.9975L51.8398,43.9975L51.8398,45.9976L47.1578,45.9976C46.524100000000004,45.994,46.0122,45.548500000000004,46.0047,44.9994ZM46.0047,40.4987C46.0122,39.950500000000005,46.524100000000004,39.504999999999995,47.1578,39.497699999999995L51.7292,39.497699999999995L51.7292,41.4868L47.1578,41.4868C46.5278,41.4868,46.0122,41.047799999999995,46.0047,40.4996L46.0047,40.4987ZM46.0047,35.9888C46.0122,35.4388,46.5278,35.001599999999996,47.1578,35.001599999999996L51.8398,35.001599999999996L51.8398,36.990700000000004L47.1578,36.990700000000004C46.524100000000004,36.9824,46.0122,36.537,46.0047,35.987899999999996L46.0047,35.9888ZM29.501649999999998,45.4953C29.501649999999998,44.674,29.71822,43.8637,30.13072,43.1533C31.08773,41.4645,32.13675,39.827200000000005,33.27327,38.2483L33.27327,38.1823L33.51327,38.054L33.578900000000004,37.9981L33.96984,37.9981L33.96984,38.0531L34.035470000000004,38.1191Q34.09172,38.1191,34.09172,38.186C35.5242,39.727599999999995,36.7882,41.411100000000005,37.8633,43.209199999999996C38.841300000000004,44.8373,38.690200000000004,46.8866,37.483599999999996,48.3607C36.2789,49.8228,34.27922,50.3746,32.49232,49.730199999999996C30.70542,49.0858,29.51572,47.3928,29.52697,45.4953L29.501649999999998,45.4953ZM27.45319,27.739919999999998C28.58383,26.75362,29.8326,26.11288,31.13293,25.858060000000002C34.019059999999996,25.06777,37.0069,24.68957,40.0037,24.73517C42.9756,24.6875,45.9494,25.06699,48.8744,25.858060000000002C50.174800000000005,26.10555,51.4273,26.74628,52.5542,27.739919999999998C51.419799999999995,28.71615,50.171,29.35688,48.8744,29.62179C45.9848,30.39315,42.9988,30.76495,40.0037,30.72635C37.0084,30.76678,34.02223,30.39495,31.13293,29.62179C29.83728,29.3633,28.58383,28.72623,27.45319,27.739919999999998Z" fill="#4F85FB" fill-opacity="1" style="mix-blend-mode:passthrough"/></g></g></svg> |
@ -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="80" height="80" viewBox="0 0 80 80"><g><g><ellipse cx="40" cy="40" rx="40" ry="40" fill="#EEEFF8" fill-opacity="1"/></g><g><path d="M32,30.00793C32.8,30.00793,33.5,29.30731,33.5,28.506610000000002L33.5,22.50132C33.5,21.700617,32.8,21,32,21C31.2,21,30.5,21.700617,30.5,22.50132L30.5,28.506610000000002C30.5,29.30731,31.2,30.00793,32,30.00793ZM45,30.00793C45.8,30.00793,46.5,29.30731,46.5,28.506610000000002L46.5,22.50132C46.5,21.700617,45.8,21,45,21C44.2,21,43.5,21.700617,43.5,22.50132L43.5,28.506610000000002C43.5,29.30731,44.2,30.00793,45,30.00793ZM40,50.0255C40,45.1212,43,40.8174,47.2,39.0159L28.5,39.0159C27.7,39.0159,27,38.315200000000004,27,37.5145C27,36.7138,27.7,36.0132,28.5,36.0132L47.5,36.0132C48.3,36.0132,49,36.7138,49,37.5145C49,37.9149,48.8,38.215199999999996,48.6,38.5154C49.7,38.215199999999996,50.8,38.015,52,38.015C52.3,38.015,52.7,38.015,53,38.1151L53,29.00705C53,26.80511,51.2,25.00352,49,25.00352L49,28.506610000000002C49,31.0088,47,33.0106,44.5,33.0106C42,33.0106,40,31.0088,40,28.506610000000002L40,25.00352L36,25.00352L36,28.506610000000002C36,31.0088,34,33.0106,31.5,33.0106C29,33.0106,27,31.0088,27,28.506610000000002L27,25.00352C24.8,25.00352,23,26.80511,23,29.00705L23,55.03C23,57.2319,24.8,59.0335,27,59.0335L44.1,59.0335C41.6,56.8315,40,53.6287,40,50.0255ZM37.5,51.026399999999995L28.5,51.026399999999995C27.7,51.026399999999995,27,50.3258,27,49.525099999999995C27,48.7244,27.7,48.0238,28.5,48.0238L37.5,48.0238C38.3,48.0238,39,48.7244,39,49.525099999999995C39,50.3258,38.3,51.026399999999995,37.5,51.026399999999995ZM37.5,45.021100000000004L28.5,45.021100000000004C27.7,45.021100000000004,27,44.320499999999996,27,43.519800000000004C27,42.7191,27.7,42.0185,28.5,42.0185L37.5,42.0185C38.3,42.0185,39,42.7191,39,43.519800000000004C39,44.320499999999996,38.3,45.021100000000004,37.5,45.021100000000004ZM52,41.0176C47,41.0176,43,45.1212,43,50.0255C43,54.9299,47.1,59.0335,52,59.0335C56.9,59.0335,61,54.9299,61,50.0255C61,45.1212,57,41.0176,52,41.0176ZM55.5,53.0282L50.5,53.0282C49.7,53.0282,49,52.327600000000004,49,51.5269L49,45.5216C49,44.7209,49.7,44.0203,50.5,44.0203C51.3,44.0203,52,44.7209,52,45.5216L52,50.0255L55.5,50.0255C56.3,50.0255,57,50.7262,57,51.5269C57,52.327600000000004,56.3,53.0282,55.5,53.0282Z" fill="#4F85FB" fill-opacity="1"/></g></g></svg> |
@ -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="80" height="80" viewBox="0 0 80 80"><g><g><ellipse cx="40" cy="40" rx="40" ry="40" fill="#EEEFF8" fill-opacity="1"/></g><g><path d="M51.951499999999996,39.8059C50.843199999999996,40.323899999999995,49.9413,40.7615,48.9636,41.225899999999996C48.4242,40.9535,47.7542,40.408699999999996,47.004000000000005,40.2435C44.3744,39.6987,41.8501,42.1279,41.9554,44.9411C41.9554,45.378699999999995,41.593,46.0619,41.2348,46.1958C39.8694,46.687,39.2499,47.723,39.174099999999996,49.0894C39.0982,51.2194,38.9423,53.4297,41.5467,54.2513C41.7531,54.3049,41.829,54.7693,41.959599999999995,55.0417C41.652,55.2069,41.3149,55.5329,41.0072,55.5329C37.5305,55.5597,34.0496,55.5597,30.5729,55.5597C28.97572,55.5597,27.37854,55.5865,25.78137,55.5597C23.745912,55.5061,23,54.6577,23,52.527699999999996L23,23.88686C23,21.895306,23.720627,21.0468861,25.62966,21.020094C33.539699999999996,20.99330188,41.4497,20.99330188,49.364000000000004,21.020094C51.037,21.020094,51.9136,21.895306,51.9388,23.83327C52.0021,29.18278,51.951499999999996,34.5635,51.951499999999996,39.8059ZM37.6232,24.75761C37.6232,24.730809999999998,37.6232,24.730809999999998,37.6232,24.75761C35.2001,24.75761,32.776920000000004,24.730809999999998,30.35798,24.75761C29.17379,24.75761,28.19188,25.76678,28.21717,26.86079C28.26774,27.9816,29.144289999999998,28.79876,30.35798,28.79876C35.0737,28.79876,39.763999999999996,28.79876,44.4797,28.77197C45.950500000000005,28.77197,46.591,28.00839,46.565799999999996,26.56161C46.5405,25.44081,45.765100000000004,24.78886,44.4797,24.78886C42.183,24.730809999999998,39.915800000000004,24.75761,37.6232,24.75761ZM33.7841,36.3899C35.0189,36.3899,36.2578,36.4435,37.4673,36.3631C38.445,36.3095,39.1951,35.7335,39.2963,34.6171C39.4269,33.3624,38.6515,32.4336,37.6485,32.38C35.0737,32.2416,32.46928,32.4336,29.8902,32.5184C28.68073,32.5452,28.242449999999998,33.5544,28.26774,34.568C28.29302,35.7424,29.09372,36.3988,30.25262,36.3988L33.7841,36.3988L33.7841,36.3899ZM56.9495,50.3441C57.156,51.326499999999996,56.7177,51.9294,55.8412,52.3089C54.2693,52.9921,53.856300000000005,53.7825,54.1934,55.5016C54.3999,56.5644,53.7046,57.002,53.0851,57.4128C52.4403,57.8236,51.7956,58.3952,50.893699999999995,57.6048C49.6295,56.4572,48.727599999999995,56.6225,47.210499999999996,57.5245C46.6964,57.8236,45.8198,57.7968,45.2256,57.578C44.504999999999995,57.3057,43.7549,56.8145,43.9867,55.6669C44.3491,53.8941,43.5231,52.7465,42.081900000000005,52.143699999999995C40.9988,51.6793,41.1547,50.7237,41.18,49.9869C41.2053,49.415400000000005,41.6436,48.566900000000004,42.1071,48.3213C43.5231,47.584599999999995,44.1679,47.0085,44.0415,44.6062C43.965599999999995,43.324600000000004,44.787400000000005,43.021,45.4827,42.695C45.9463,42.4494,46.8228,42.3958,47.185199999999995,42.695C48.5001,43.815799999999996,49.7348,43.7577,50.948499999999996,42.7218C52.0821,41.7662,52.7269,42.775400000000005,53.5529,43.2398C54.3536,43.6774,54.2988,44.2757,54.0923,45.0706C53.7847,46.298500000000004,54.4547,47.6917,55.6136,48.0757C56.7177,48.4062,57.0802,49.143,56.9495,50.3441ZM45.7145,49.9333C45.6892,52.143699999999995,46.9787,53.5905,49.039500000000004,53.6173C50.7673,53.6441,52.259100000000004,52.2509,52.313900000000004,50.585300000000004C52.3645,48.2365,51.0497,46.682500000000005,48.9383,46.5977C47.210499999999996,46.5486,45.7398,48.049,45.7145,49.9333Z" fill="#4F85FB" fill-opacity="1" style="mix-blend-mode:passthrough"/></g></g></svg> |
@ -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="80" height="80" viewBox="0 0 80 80"><defs><clipPath id="master_svg0_94_4549"><rect x="18" y="18" width="44" height="44" rx="0"/></clipPath></defs><g><g><ellipse cx="40" cy="40" rx="40" ry="40" fill="#EEEFF8" fill-opacity="1"/></g><g clip-path="url(#master_svg0_94_4549)"><g><path d="M39.9964013671875,20.74951171875C31.6590513671875,20.74951171875,24.8739013671875,27.53466171875,24.8739013671875,35.87201171875C24.8739013671875,43.92101171875,38.4294013671875,58.21571171875,39.0059013671875,58.82171171875C39.2646013671875,59.09521171875,39.6268013671875,59.25041171875,40.0038013671875,59.25041171875L40.0333013671875,59.25041171875C40.4177013671875,59.25041171875,40.7872013671875,59.07311171875,41.0385013671875,58.77741171875L45.7320013671875,53.40401171875C51.9702013671875,45.76881171875,55.126201367187505,39.87061171875,55.126201367187505,35.87941171875C55.126201367187505,27.53471171875,48.3411013671875,20.74955640795,39.9964013671875,20.74951171875ZM39.9964013671875,42.745811718750005C36.1973013671875,42.745811718750005,33.1225213671875,39.67101171875,33.1225213671875,35.87201171875C33.1225213671875,32.07291171875,36.1973013671875,28.998131718750003,39.9964013671875,28.998131718750003C43.7954013671875,28.998131718750003,46.870201367187505,32.07291171875,46.870201367187505,35.87201171875C46.870201367187505,39.67101171875,43.7954013671875,42.745811718750005,39.9964013671875,42.745811718750005Z" fill="#4F85FB" fill-opacity="1" style="mix-blend-mode:passthrough"/></g></g></g></svg> |
@ -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="80" height="80" viewBox="0 0 80 80"><defs><clipPath id="master_svg0_94_4745"><rect x="21" y="59" width="38" height="38" rx="0"/></clipPath></defs><g><g><ellipse cx="40" cy="40" rx="40" ry="40" fill="#479CF1" fill-opacity="1"/></g><g transform="matrix(1,0,0,-1,0,118)" clip-path="url(#master_svg0_94_4745)"><g><path d="M56.9528,97L23.04725,97C21.916585,97,21,96.0834,21,94.9528L21,61.04725C21,59.91675,21.91675,59,23.04725,59L56.9551,59C58.0832,59,59,59.91675,59,61.04725L59,94.9551C58.9987,96.0849,58.0825,97,56.9528,97ZM50.8134,83.21549999999999L50.8134,64.88525C50.8108,64.25542,50.300799999999995,63.74548,49.671,63.74288C49.0412,63.74548,48.5312,64.25541,48.5286,64.88525L48.5286,83.2226C46.297200000000004,83.8023,44.843599999999995,85.9491,45.1338,88.2362C45.424099999999996,90.5233,47.3679,92.239,49.6734,92.24289999999999C51.9827,92.2411,53.9304,90.52250000000001,54.2198,88.2313C54.5091,85.9402,53.0497,83.7914,50.8134,83.21549999999999ZM41.142399999999995,73.5754L41.142399999999995,64.90663C41.1398,64.27679,40.6298,63.76685,40,63.76425C39.3702,63.76685,38.8602,64.27679,38.857600000000005,64.90663L38.857600000000005,73.5754C36.839,74.1033,35.4307,75.9268,35.430099999999996,78.0133C35.4296,80.0999,36.8369,81.9241,38.8553,82.4531L38.8553,91.1219C38.8553,91.74889999999999,39.370599999999996,92.2642,39.9976,92.2642C40.6275,92.2616,41.1374,91.7517,41.14,91.1219L41.14,82.4531C43.1652,81.93209999999999,44.5811,80.10640000000001,44.5816,78.0152C44.5822,75.924,43.1673,74.0975,41.142399999999995,73.5754ZM30.33138,63.73575C28.02941,63.74415,26.09074,65.45857,25.80085,67.74223C25.51095,70.02590000000001,26.95977,72.1703,29.186619999999998,72.7536L29.186619999999998,91.09100000000001C29.186619999999998,91.718,29.701999999999998,92.2334,30.329,92.2334C30.95883,92.2308,31.4688,91.7208,31.4714,91.09100000000001L31.4714,72.7536C33.7053,72.1781,35.1629,70.0312,34.873599999999996,67.74254C34.584199999999996,65.45384,32.6383,63.73726,30.33138,63.73575Z" fill="#FFFFFF" fill-opacity="1" style="mix-blend-mode:passthrough"/></g></g></g></svg> |
@ -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="80" height="80" viewBox="0 0 80 80"><g><g><ellipse cx="40" cy="40" rx="40" ry="40" fill="#EEEFF8" fill-opacity="1"/></g><g><g><path d="M57.7309,36.6424L54.4199,35.1065C52.2821,34.1115,51.0169,31.80957,51.2919,29.41513L51.7134,25.70348C49.9764,24.13487,47.9673,22.91636,45.7913,22.111649L42.809,24.29289C40.9002,25.70218,38.3223,25.67794,36.438900000000004,24.23298L33.5495,22C31.3612,22.766417,29.33189,23.9468,27.56639,25.48018L27.92691,29.18911C28.16995,31.58721,26.871940000000002,33.8705,24.71933,34.8315L21.35269,36.3074C20.9039103,38.6357,20.883231,41.0297,21.291719,43.3658L24.60269,44.9071C26.74368,45.8995,28.01241,48.202,27.73869,50.5985L27.30925,54.2911C29.0484,55.858,31.056800000000003,57.0779,33.2313,57.8884L36.1897,55.7098C38.098,54.2997,40.6759,54.3229,42.559799999999996,55.767L45.4705,58C47.6601,57.2361,49.6906,56.0565,51.456199999999995,54.5225L51.0984,50.8054C50.8651,48.409099999999995,52.158699999999996,46.1352,54.306,45.1685L57.6461,43.6953C58.0967,41.3643,58.1179,38.9679,57.7044,36.6287L57.7309,36.6424ZM39.5113,49.514700000000005C34.3981,49.514700000000005,30.25307,45.2566,30.25307,40.0041C30.25307,34.7515,34.3981,30.49349,39.5113,30.49349C44.6245,30.49349,48.7696,34.7515,48.7696,40.0041C48.7696,45.2566,44.6245,49.514700000000005,39.5113,49.514700000000005Z" fill="#4F85FB" fill-opacity="1" style="mix-blend-mode:passthrough"/></g><g><path d="M39.503226597900394,32.666666984558105C35.85293659790039,32.6648691245581,32.89285659790039,35.650166984558105,32.89285659790039,39.3333369845581C32.89285659790039,43.01646698455811,35.85293659790039,46.0017669845581,39.503226597900394,45.99996698455811C43.15095659790039,45.998166984558104,46.10715659790039,43.01396698455811,46.10715659790039,39.3333369845581C46.10715659790039,35.65270698455811,43.15095659790039,32.66846320455811,39.503226597900394,32.666666984558105ZM39.503226597900394,43.0462669845581C37.535416597900394,42.95376698455811,35.98716659790039,41.316986984558106,35.98716659790039,39.329296984558106C35.98716659790039,37.34159698455811,37.535416597900394,35.704856984558106,39.503226597900394,35.61227698455811C41.495636597900386,35.7097269845581,43.08915659790039,37.317586984558105,43.185756597900394,39.3279469845581C43.17395659790039,41.372516984558104,41.52956659790039,43.024566984558106,39.503226597900394,43.0274669845581L39.503226597900394,43.0462669845581Z" fill="#4F85FB" fill-opacity="1" style="mix-blend-mode:passthrough"/></g></g></g></svg> |
@ -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="80" height="80" viewBox="0 0 80 80"><g><g><ellipse cx="40" cy="40" rx="40" ry="40" fill="#EEEFF8" fill-opacity="1"/></g><g><path d="M29.44471,32.3043C29.44472,37.9952,34.0687,42.608599999999996,39.772800000000004,42.608599999999996C45.4768,42.608599999999996,50.100899999999996,37.9952,50.100899999999996,32.3043C50.100899999999996,26.6134,45.4768,22,39.772800000000004,22C34.0687,22,29.44472,26.6134,29.44471,32.3043ZM55.678,46.5004L51.0002,46.5004C50.2702,46.5004,49.678200000000004,45.9097,49.678200000000004,45.1814C49.678200000000004,44.4531,50.2702,43.8625,51.0002,43.8625L55.678,43.8625C56.408,43.8625,57,44.4531,57,45.1814C57,45.9097,56.408,46.5004,55.678,46.5004ZM55.678,52.2502L47.580799999999996,52.2502C46.8508,52.2502,46.2588,51.6595,46.2588,50.931200000000004C46.2588,50.2029,46.8508,49.612300000000005,47.580799999999996,49.612300000000005L55.678,49.612300000000005C56.408,49.612300000000005,57,50.2029,57,50.931200000000004C57,51.6595,56.408,52.2502,55.678,52.2502ZM55.678,58L47.580799999999996,58C46.8508,58,46.2588,57.4094,46.2588,56.681C46.2588,55.9527,46.8508,55.3621,47.580799999999996,55.3621L55.678,55.3621C56.408,55.3621,57,55.9527,57,56.681C57,57.4094,56.408,58,55.678,58ZM41.260000000000005,52.9303C41.260000000000005,48.602900000000005,43.934200000000004,44.898700000000005,47.723299999999995,43.3724C48.3662,43.113600000000005,48.4289,42.229,47.825,41.8898C45.435,40.546099999999996,42.6911,39.782,39.772800000000004,39.782C30.50933,39.782,23,47.4797,23,56.9757C23,57.0475,23.000413211,57.1188,23.00123932,57.1901C23.0066098,57.6398,23.376768,58,23.827485,58L41.2753,58C41.865700000000004,58,42.262299999999996,57.4015,42.037099999999995,56.857C41.5364,55.6469,41.260000000000005,54.3209,41.260000000000005,52.9303Z" fill="#4F85FB" fill-opacity="1" style="mix-blend-mode:passthrough"/></g></g></svg> |
@ -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="80" height="80" viewBox="0 0 80 80"><defs><clipPath id="master_svg0_592_42099"><rect x="19" y="18.921875" width="41" height="41" rx="0"/></clipPath></defs><g><g><ellipse cx="40" cy="40" rx="40" ry="40" fill="#479CF1" fill-opacity="1"/></g><g clip-path="url(#master_svg0_592_42099)"><g><path d="M53.8413984375,21.65234443077L25.1580984375,21.65234443077C23.2666884375,21.652344090385,21.733398779614,23.17788375,21.733399121728,25.05973375L21.733399121728,47.20904375L57.2666984375,47.20904375L57.2666984375,25.05973375C57.2670984375,23.17798375,55.7333984375,21.652344090385,53.8413984375,21.65234443077ZM52.9319984375,35.12904375C52.9319984375,35.83334375,52.3579984375,36.405643749999996,51.6500984375,36.406943749999996L47.5684984375,36.406943749999996C47.3869984375,36.40704375,47.2220984375,36.30194375,47.1462984375,36.13784375L46.7859984375,35.36184375C46.7178984375,35.21544375,46.5724984375,35.11984375,46.410498437499996,35.11504375C46.2483984375,35.11024375,46.0974984375,35.19694375,46.0206984375,35.33904375L42.2247984375,42.38224375C42.0768984375,42.65614375,41.7907984375,42.828143749999995,41.478398437500005,42.83104375C41.165898437500005,42.83394375,40.8765984375,42.66724375,40.7235984375,42.39624375L34.6923984375,31.69304375C34.6111984375,31.54864375,34.4531984375,31.46415375,34.2872984375,31.47640375C34.1214984375,31.48864375,33.9777984375,31.59540375,33.9187984375,31.75014375L32.3505984375,35.85744375C32.224298437499996,36.18814375,31.9057984375,36.40684375,31.5502384375,36.406943749999996L27.3519484375,36.406943749999996C26.6425784375,36.406943749999996,26.067528437500002,35.83474375,26.067528437500002,35.12904375C26.067528437500002,34.42324375,26.6425784375,33.85104375,27.3519484375,33.85104375L29.7753884375,33.85104375C30.1312484375,33.85104375,30.4494884375,33.63214375,30.5757584375,33.30154375L33.1400984375,26.58196375C33.2582984375,26.27280375,33.5454984375,26.05956375,33.8769984375,26.03496375C34.2084984375,26.01035375,34.5243984375,26.17881375,34.687298437500004,26.46711375L41.053898437499996,37.75784375C41.1302984375,37.89354375,41.2749984375,37.97704375,41.431298437500004,37.97554375C41.587698437499995,37.974143749999996,41.7307984375,37.887943750000005,41.804598437500005,37.75084375L45.789198437500005,30.36372375C45.9430984375,30.079963749999997,46.244798437499995,29.906903749999998,46.568698437500004,29.916573749999998C46.8925984375,29.926243749999998,47.1832984375,30.11697375,47.3197984375,30.40940375L48.6858984375,33.35554375C48.8261984375,33.65754375,49.129098437500005,33.85044375,49.4632984375,33.85104375L51.6500984375,33.85104375C52.3592984375,33.85104375,52.9344984375,34.423443750000004,52.9344984375,35.12904375L52.9319984375,35.12904375ZM21.733399121728,48.66654375L21.733399121728,53.77824375C21.733398779614,55.66014375,23.2666884375,57.18564375,25.1580984375,57.18564375L53.8413984375,57.18564375C55.7327984375,57.18564375,57.2660984375,55.66014375,57.2660984375,53.77824375L57.2660984375,48.66654375L21.733399121728,48.66654375ZM42.338998437499995,52.74404375C42.3368984375,53.44894375,41.7630984375,54.01984375,41.054598437500005,54.02194375L27.3519484375,54.02194375C26.6425784375,54.02194375,26.067528437500002,53.449743749999996,26.067528437500002,52.74404375C26.067528437500002,52.03824375,26.6425784375,51.46604375,27.3519484375,51.46604375L41.0519984375,51.46604375C41.761498437499995,51.46674375,42.3368984375,52.03814375,42.338998437499995,52.74404375ZM52.9319984375,52.74404375C52.9291984375,53.447443750000005,52.3570984375,54.01724375,51.6500984375,54.02064375L46.510498437500004,54.02064375C45.8010984375,54.02064375,45.2260984375,53.44854375,45.2260984375,52.74274375C45.2260984375,52.03694375,45.8010984375,51.46484375,46.510498437500004,51.46484375L51.6500984375,51.46484375C52.358098437500004,51.46834375,52.930598437499995,52.03954375,52.9319984375,52.74404375Z" fill="#FFFFFF" fill-opacity="1" style="mix-blend-mode:passthrough"/></g></g></g></svg> |
@ -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="80" height="80" viewBox="0 0 80 80"><g><g><ellipse cx="40" cy="40" rx="40" ry="40" fill="#479CF1" fill-opacity="1"/></g><g><path d="M54.037,56.006L28.80688,56.006C27.22599,56.006,25.93399,54.6781,25.93399,53.0441C25.93399,51.6375,26.888939999999998,50.4627,28.16489,50.1565L28.16489,21.0455056C25.82164,21.380591,24,23.46143,24,25.96836L24,53.0523C24,55.7785,26.15066,58,28.79886,58L54.037,58C54.5706,58,55,57.5574,55,57.0072C55.004000000000005,56.457,54.5706,56.006,54.037,56.006ZM42.742000000000004,37.799800000000005L41.847300000000004,37.799800000000005L41.847300000000004,39.1815L40.5031,39.1815L40.5031,40.1081L41.847300000000004,40.1081L41.847300000000004,41.4898L42.742000000000004,41.4898L42.742000000000004,40.1081L44.090199999999996,40.1081L44.090199999999996,39.1815L42.742000000000004,39.1815L42.742000000000004,37.799800000000005ZM44.090199999999996,29.05031L40.5031,29.05031L40.5031,33.3775L39.2994,35.6817C39.817,35.5824,40.334599999999995,35.5617,40.852199999999996,35.6155C42.2605,35.7644,42.569500000000005,36.8814,44.4393,36.8814C44.985,36.8814,45.4384,36.8193,45.807500000000005,36.6952L44.090199999999996,33.3981L44.090199999999996,29.05031ZM53.2425,21L30.09086,21L30.09086,50.1276L30.5563,50.1276C30.612470000000002,50.1358,30.67266,50.1358,30.73284,50.1358L53.2425,50.1358C54.125299999999996,50.1358,54.8475,49.3912,54.8475,48.4811L54.8475,22.65888C54.8515,21.744634,54.125299999999996,21.000000315617,53.2425,21ZM49.3224,42.424800000000005C49.1619,42.6978,48.881,42.8674,48.5721,42.875699999999995L36.0212,42.875699999999995C35.7042,42.875699999999995,35.4113,42.7061,35.2508,42.424800000000005C35.0903,42.1435,35.082300000000004,41.8001,35.2348,41.5105L39.5963,33.2161L39.5963,29.05031L38.5089,29.05031C38.2521,29.05031,38.0475,28.8352,38.0475,28.57457C38.0475,28.309820000000002,38.2561,28.09884,38.5089,28.09884L46.0643,28.09884C46.3211,28.09884,46.5257,28.31395,46.5257,28.57457C46.5257,28.83933,46.317099999999996,29.05031,46.0643,29.05031L44.9769,29.05031L44.9769,33.2203L46.561800000000005,36.231899999999996L49.3425,41.5105C49.490899999999996,41.8001,49.4869,42.1434,49.3224,42.424800000000005Z" fill="#FFFFFF" fill-opacity="1" style="mix-blend-mode:passthrough"/></g></g></svg> |
@ -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="80" height="80" viewBox="0 0 80 80"><g><g><ellipse cx="40" cy="40" rx="40" ry="40" fill="#479CF1" fill-opacity="1"/></g><g><path d="M20.0370799,43.6256C20.866486,47.6246,22.91091,51.222300000000004,25.94731,54.0277C26.3526,54.4028,26.9106,54.5752,27.46181,54.4956C28.0134,54.4165,28.49784,54.0943,28.77694,53.6208L31.1482,49.591300000000004C31.5442,48.9178,31.451999999999998,48.070499999999996,30.92,47.4942C29.68039,46.1482,28.77662,44.537800000000004,28.28009,42.7903C28.06083,42.015,27.34183,41.4784,26.52231,41.4784L21.82395,41.4784C21.27636,41.4781,20.757754,41.7198,20.411853,42.1363C20.0647341,42.552,19.9270634,43.0991,20.0370799,43.6256ZM44.1657,51.909C43.7696,51.233000000000004,42.9726,50.8869,42.1963,51.053799999999995C40.231899999999996,51.4761,38.1899,51.3965,36.2658,50.8227C35.453900000000004,50.580600000000004,34.579,50.9191,34.1541,51.6398L31.8262,55.5982C31.5578,56.055,31.5084,56.6046,31.691000000000003,57.1004C31.872999999999998,57.5967,32.2697,57.9884,32.7742,58.1703C34.9795,58.9695,37.3089,59.375,39.695,59.375C41.6678,59.375,43.6156,59.0954,45.482600000000005,58.543C46.0156,58.3847,46.446,57.9968,46.652,57.4891C46.8572,56.9806,46.814099999999996,56.408,46.535,55.9346L44.1657,51.909ZM52.5476,24.57127C52.2201,24.3027,51.806799999999996,24.15552,51.379999999999995,24.15542C51.2742,24.15542,51.1663,24.166330000000002,51.060500000000005,24.18424C50.531,24.2771,50.0703,24.59457,49.8016,25.05176L47.4685,29.01324C47.045100000000005,29.73472,47.183499999999995,30.6473,47.802800000000005,31.2165C49.39,32.6707,50.5347,34.5286,51.1096,36.5833C51.3275,37.3604,52.0476,37.8989,52.8691,37.899100000000004L57.568,37.899100000000004C58.1145,37.8988,58.6321,37.658,58.9784,37.2432C59.3258,36.8281,59.464,36.2815,59.3546,35.7553C58.4579,31.3953,56.0407,27.42316,52.5476,24.57127ZM46.652,21.88784C46.446600000000004,21.37975,46.0159,20.99153,45.482600000000005,20.833661C43.6053,20.279568,41.6553,19.99869145,39.695,20.00000417001C37.3089,20.00000417001,34.9795,20.405219,32.7742,21.20446C31.714199999999998,21.58978,31.262,22.8179,31.8262,23.77903L34.1541,27.73687C34.579,28.457549999999998,35.453900000000004,28.79609,36.2658,28.55402C37.3775,28.22171,38.5332,28.05317,39.6953,28.05394C40.530100000000004,28.05394,41.372299999999996,28.14349,42.1963,28.32119C42.9721,28.48767,43.7685,28.14262,44.1657,27.46794L46.5353,23.44182C46.8143,22.96858,46.857299999999995,22.39608,46.652,21.88784ZM57.568,41.4781L52.8691,41.4781C52.0475,41.477599999999995,51.327,42.0163,51.1096,42.7936C50.5345,44.848299999999995,49.3898,46.7063,47.802800000000005,48.160799999999995C47.1834,48.729,47.0449,49.6411,47.4685,50.3618L49.8016,54.3249C50.07,54.7824,50.5309,55.1001,51.060500000000005,55.1927C51.1663,55.2106,51.2742,55.2196,51.379999999999995,55.2196C51.8066,55.2202,52.2199,55.0737,52.5476,54.8057C56.0407,51.9518,58.4579,47.9814,59.3546,43.622C59.4641,43.0957,59.3259,42.549099999999996,58.9784,42.134C58.6326,41.718599999999995,58.1147,41.4778,57.568,41.4781ZM20.411568,37.241C20.757316,37.6573,21.27588,37.8989,21.82367,37.8989L26.52259,37.8989C27.34233,37.8986,28.06128,37.361999999999995,28.280369999999998,36.587C28.776699999999998,34.8389,29.68048,33.2279,30.920299999999997,31.8814C31.4524,31.3057,31.544600000000003,30.4588,31.1485,29.785890000000002L28.77722,25.75641C28.49772,25.28303,28.01361,24.96039,27.46209,24.87994C27.37433,24.86765,27.28578,24.86166,27.19713,24.86203C26.73679,24.86203,26.28729,25.03413,25.9476,25.349510000000002C22.91091,28.15468,20.866486,31.7521,20.0370799,35.7513C19.9271883,36.2779,20.0647273,36.825,20.411568,37.241Z" fill="#FFFFFF" fill-opacity="1"/></g></g></svg> |
@ -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="80" height="80" viewBox="0 0 80 80"><g><g><ellipse cx="40" cy="40" rx="40" ry="40" fill="#479CF1" fill-opacity="1"/></g><g><path d="M30.42768,34.2143L30.42768,52.673C30.42768,53.4027,31.06121,54.0357,31.77429,54.0357L48.5584,54.0357C49.2714,54.0357,49.794,53.4027,49.794,52.673L49.794,34.2143L30.42768,34.2143ZM47.8574,46.726600000000005C47.8574,47.091499999999996,47.5684,47.387299999999996,47.2118,47.387299999999996C46.8553,47.387299999999996,46.5663,47.091499999999996,46.5663,46.726600000000005L46.5663,42.762299999999996C46.5663,42.397400000000005,46.8553,42.101600000000005,47.2118,42.101600000000005C47.5684,42.101600000000005,47.8574,42.397400000000005,47.8574,42.762299999999996L47.8574,46.726600000000005ZM47.2673,40.780100000000004C46.910799999999995,40.780100000000004,46.6218,40.4844,46.6218,40.1194C46.6218,39.7545,46.910799999999995,39.4587,47.2673,39.4587C47.6239,39.4587,47.9129,39.7545,47.9129,40.1194C47.9129,40.484300000000005,47.6239,40.780100000000004,47.2673,40.780100000000004ZM54.7629,21L29.21229,21C24.827023,21,24,23.26509,24,24.28292C26.44097,24.63393,26.55442,24.67522,26.55442,27.318080000000002L26.55442,52.673C26.55442,55.5922,28.92203,58,31.77429,58L48.5383,58C51.3904,58,53.6673,55.5922,53.6673,52.673L53.6673,24.96429C53.6673,23.28368,54.6596,21.803098,54.7579,21.6456C54.856300000000005,21.488186,55,21.331926,55,21.236123C55,21.139411,54.9664,21,54.7629,21ZM51.0851,24.96429L51.0851,52.673C51.0851,54.153,50.081,55.3571,48.679500000000004,55.3571L31.77429,55.3571C30.352719999999998,55.3571,29.136589999999998,54.128,29.136589999999998,52.673L29.136589999999998,27.318080000000002C29.136589999999998,26.34254,29.22487,24.24163,28.8277,23.6877C28.90202,23.62221,29.07898,23.64286,29.19211,23.64286L51.1939,23.64286C51.1345,23.64286,51.0851,24.53474,51.0851,24.96429Z" fill="#FFFFFF" fill-opacity="1" style="mix-blend-mode:passthrough"/></g></g></svg> |
@ -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="80" height="80" viewBox="0 0 80 80"><g><g><ellipse cx="40" cy="40" rx="40" ry="40" fill="#479CF1" fill-opacity="1"/></g><g><path d="M25.010964,27.14686L25.010964,52.3976C25.00721385,52.5305,25.0362768,52.6662,25.0915901,52.791799999999995C25.490033,53.8368,27.08099,54.6141,29.95072,55.1751C33.25546,55.7635,36.6258,56.0395,39.9999,55.9955C43.375,56.0285,46.7406,55.7525,50.0501,55.1741C52.915099999999995,54.6031,54.536100000000005,53.8083,54.9083,52.7909C54.9636,52.6662,54.992599999999996,52.5342,54.9889,52.3976L54.9889,27.14686C55.003699999999995,27.099980000000002,55.003699999999995,27.04982,54.9889,27.00294L54.9889,26.77745C54.9889,25.48132,53.3829,24.50509,50.0454,23.860689999999998C46.734700000000004,23.255981,43.3715,22.9686265,40.0037,23.00271173C36.6286,22.9697126,33.26296,23.245622,29.95353,23.824024C26.64599,24.468420000000002,25.0109623,25.44373,25.0109623,26.74078C24.999922606,26.81525,24.999922606,26.89089,25.0109623,26.96536C24.996346,27.011960000000002,24.996346,27.06176,25.0109623,27.10836L25.0109623,27.14502L25.010964,27.14686ZM46.0047,44.9994C46.0122,44.4503,46.524100000000004,44.0048,47.1578,43.9975L51.8398,43.9975L51.8398,45.9976L47.1578,45.9976C46.524100000000004,45.994,46.0122,45.548500000000004,46.0047,44.9994ZM46.0047,40.4987C46.0122,39.950500000000005,46.524100000000004,39.504999999999995,47.1578,39.497699999999995L51.7292,39.497699999999995L51.7292,41.4868L47.1578,41.4868C46.5278,41.4868,46.0122,41.047799999999995,46.0047,40.4996L46.0047,40.4987ZM46.0047,35.9888C46.0122,35.4388,46.5278,35.001599999999996,47.1578,35.001599999999996L51.8398,35.001599999999996L51.8398,36.990700000000004L47.1578,36.990700000000004C46.524100000000004,36.9824,46.0122,36.537,46.0047,35.987899999999996L46.0047,35.9888ZM29.501649999999998,45.4953C29.501649999999998,44.674,29.71822,43.8637,30.13072,43.1533C31.08773,41.4645,32.13675,39.827200000000005,33.27327,38.2483L33.27327,38.1823L33.51327,38.054L33.578900000000004,37.9981L33.96984,37.9981L33.96984,38.0531L34.035470000000004,38.1191Q34.09172,38.1191,34.09172,38.186C35.5242,39.727599999999995,36.7882,41.411100000000005,37.8633,43.209199999999996C38.841300000000004,44.8373,38.690200000000004,46.8866,37.483599999999996,48.3607C36.2789,49.8228,34.27922,50.3746,32.49232,49.730199999999996C30.70542,49.0858,29.51572,47.3928,29.52697,45.4953L29.501649999999998,45.4953ZM27.45319,27.739919999999998C28.58383,26.75362,29.8326,26.11288,31.13293,25.858060000000002C34.019059999999996,25.06777,37.0069,24.68957,40.0037,24.73517C42.9756,24.6875,45.9494,25.06699,48.8744,25.858060000000002C50.174800000000005,26.10555,51.4273,26.74628,52.5542,27.739919999999998C51.419799999999995,28.71615,50.171,29.35688,48.8744,29.62179C45.9848,30.39315,42.9988,30.76495,40.0037,30.72635C37.0084,30.76678,34.02223,30.39495,31.13293,29.62179C29.83728,29.3633,28.58383,28.72623,27.45319,27.739919999999998Z" fill="#FFFFFF" fill-opacity="1" style="mix-blend-mode:passthrough"/></g></g></svg> |
@ -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="80" height="80" viewBox="0 0 80 80"><g><g><ellipse cx="40" cy="40" rx="40" ry="40" fill="#479CF1" fill-opacity="1"/></g><g><path d="M32,30.00793C32.8,30.00793,33.5,29.30731,33.5,28.506610000000002L33.5,22.50132C33.5,21.700617,32.8,21,32,21C31.2,21,30.5,21.700617,30.5,22.50132L30.5,28.506610000000002C30.5,29.30731,31.2,30.00793,32,30.00793ZM45,30.00793C45.8,30.00793,46.5,29.30731,46.5,28.506610000000002L46.5,22.50132C46.5,21.700617,45.8,21,45,21C44.2,21,43.5,21.700617,43.5,22.50132L43.5,28.506610000000002C43.5,29.30731,44.2,30.00793,45,30.00793ZM40,50.0255C40,45.1212,43,40.8174,47.2,39.0159L28.5,39.0159C27.7,39.0159,27,38.315200000000004,27,37.5145C27,36.7138,27.7,36.0132,28.5,36.0132L47.5,36.0132C48.3,36.0132,49,36.7138,49,37.5145C49,37.9149,48.8,38.215199999999996,48.6,38.5154C49.7,38.215199999999996,50.8,38.015,52,38.015C52.3,38.015,52.7,38.015,53,38.1151L53,29.00705C53,26.80511,51.2,25.00352,49,25.00352L49,28.506610000000002C49,31.0088,47,33.0106,44.5,33.0106C42,33.0106,40,31.0088,40,28.506610000000002L40,25.00352L36,25.00352L36,28.506610000000002C36,31.0088,34,33.0106,31.5,33.0106C29,33.0106,27,31.0088,27,28.506610000000002L27,25.00352C24.8,25.00352,23,26.80511,23,29.00705L23,55.03C23,57.2319,24.8,59.0335,27,59.0335L44.1,59.0335C41.6,56.8315,40,53.6287,40,50.0255ZM37.5,51.026399999999995L28.5,51.026399999999995C27.7,51.026399999999995,27,50.3258,27,49.525099999999995C27,48.7244,27.7,48.0238,28.5,48.0238L37.5,48.0238C38.3,48.0238,39,48.7244,39,49.525099999999995C39,50.3258,38.3,51.026399999999995,37.5,51.026399999999995ZM37.5,45.021100000000004L28.5,45.021100000000004C27.7,45.021100000000004,27,44.320499999999996,27,43.519800000000004C27,42.7191,27.7,42.0185,28.5,42.0185L37.5,42.0185C38.3,42.0185,39,42.7191,39,43.519800000000004C39,44.320499999999996,38.3,45.021100000000004,37.5,45.021100000000004ZM52,41.0176C47,41.0176,43,45.1212,43,50.0255C43,54.9299,47.1,59.0335,52,59.0335C56.9,59.0335,61,54.9299,61,50.0255C61,45.1212,57,41.0176,52,41.0176ZM55.5,53.0282L50.5,53.0282C49.7,53.0282,49,52.327600000000004,49,51.5269L49,45.5216C49,44.7209,49.7,44.0203,50.5,44.0203C51.3,44.0203,52,44.7209,52,45.5216L52,50.0255L55.5,50.0255C56.3,50.0255,57,50.7262,57,51.5269C57,52.327600000000004,56.3,53.0282,55.5,53.0282Z" fill="#FFFFFF" fill-opacity="1"/></g></g></svg> |
@ -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="80" height="80" viewBox="0 0 80 80"><g><g><ellipse cx="40" cy="40" rx="40" ry="40" fill="#479CF1" fill-opacity="1"/></g><g><path d="M51.951499999999996,39.8059C50.843199999999996,40.323899999999995,49.9413,40.7615,48.9636,41.225899999999996C48.4242,40.9535,47.7542,40.408699999999996,47.004000000000005,40.2435C44.3744,39.6987,41.8501,42.1279,41.9554,44.9411C41.9554,45.378699999999995,41.593,46.0619,41.2348,46.1958C39.8694,46.687,39.2499,47.723,39.174099999999996,49.0894C39.0982,51.2194,38.9423,53.4297,41.5467,54.2513C41.7531,54.3049,41.829,54.7693,41.959599999999995,55.0417C41.652,55.2069,41.3149,55.5329,41.0072,55.5329C37.5305,55.5597,34.0496,55.5597,30.5729,55.5597C28.97572,55.5597,27.37854,55.5865,25.78137,55.5597C23.745912,55.5061,23,54.6577,23,52.527699999999996L23,23.88686C23,21.895306,23.720627,21.0468861,25.62966,21.020094C33.539699999999996,20.99330188,41.4497,20.99330188,49.364000000000004,21.020094C51.037,21.020094,51.9136,21.895306,51.9388,23.83327C52.0021,29.18278,51.951499999999996,34.5635,51.951499999999996,39.8059ZM37.6232,24.75761C37.6232,24.730809999999998,37.6232,24.730809999999998,37.6232,24.75761C35.2001,24.75761,32.776920000000004,24.730809999999998,30.35798,24.75761C29.17379,24.75761,28.19188,25.76678,28.21717,26.86079C28.26774,27.9816,29.144289999999998,28.79876,30.35798,28.79876C35.0737,28.79876,39.763999999999996,28.79876,44.4797,28.77197C45.950500000000005,28.77197,46.591,28.00839,46.565799999999996,26.56161C46.5405,25.44081,45.765100000000004,24.78886,44.4797,24.78886C42.183,24.730809999999998,39.915800000000004,24.75761,37.6232,24.75761ZM33.7841,36.3899C35.0189,36.3899,36.2578,36.4435,37.4673,36.3631C38.445,36.3095,39.1951,35.7335,39.2963,34.6171C39.4269,33.3624,38.6515,32.4336,37.6485,32.38C35.0737,32.2416,32.46928,32.4336,29.8902,32.5184C28.68073,32.5452,28.242449999999998,33.5544,28.26774,34.568C28.29302,35.7424,29.09372,36.3988,30.25262,36.3988L33.7841,36.3988L33.7841,36.3899ZM56.9495,50.3441C57.156,51.326499999999996,56.7177,51.9294,55.8412,52.3089C54.2693,52.9921,53.856300000000005,53.7825,54.1934,55.5016C54.3999,56.5644,53.7046,57.002,53.0851,57.4128C52.4403,57.8236,51.7956,58.3952,50.893699999999995,57.6048C49.6295,56.4572,48.727599999999995,56.6225,47.210499999999996,57.5245C46.6964,57.8236,45.8198,57.7968,45.2256,57.578C44.504999999999995,57.3057,43.7549,56.8145,43.9867,55.6669C44.3491,53.8941,43.5231,52.7465,42.081900000000005,52.143699999999995C40.9988,51.6793,41.1547,50.7237,41.18,49.9869C41.2053,49.415400000000005,41.6436,48.566900000000004,42.1071,48.3213C43.5231,47.584599999999995,44.1679,47.0085,44.0415,44.6062C43.965599999999995,43.324600000000004,44.787400000000005,43.021,45.4827,42.695C45.9463,42.4494,46.8228,42.3958,47.185199999999995,42.695C48.5001,43.815799999999996,49.7348,43.7577,50.948499999999996,42.7218C52.0821,41.7662,52.7269,42.775400000000005,53.5529,43.2398C54.3536,43.6774,54.2988,44.2757,54.0923,45.0706C53.7847,46.298500000000004,54.4547,47.6917,55.6136,48.0757C56.7177,48.4062,57.0802,49.143,56.9495,50.3441ZM45.7145,49.9333C45.6892,52.143699999999995,46.9787,53.5905,49.039500000000004,53.6173C50.7673,53.6441,52.259100000000004,52.2509,52.313900000000004,50.585300000000004C52.3645,48.2365,51.0497,46.682500000000005,48.9383,46.5977C47.210499999999996,46.5486,45.7398,48.049,45.7145,49.9333Z" fill="#FFFFFF" fill-opacity="1" style="mix-blend-mode:passthrough"/></g></g></svg> |
@ -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="80" height="80" viewBox="0 0 80 80"><defs><clipPath id="master_svg0_94_4654"><rect x="18" y="18" width="44" height="44" rx="0"/></clipPath></defs><g><g><ellipse cx="40" cy="40" rx="40" ry="40" fill="#479CF1" fill-opacity="1"/></g><g clip-path="url(#master_svg0_94_4654)"><g><path d="M39.9964013671875,20.74951171875C31.6590513671875,20.74951171875,24.8739013671875,27.53466171875,24.8739013671875,35.87201171875C24.8739013671875,43.92101171875,38.4294013671875,58.21571171875,39.0059013671875,58.82171171875C39.2646013671875,59.09521171875,39.6268013671875,59.25041171875,40.0038013671875,59.25041171875L40.0333013671875,59.25041171875C40.4177013671875,59.25041171875,40.7872013671875,59.07311171875,41.0385013671875,58.77741171875L45.7320013671875,53.40401171875C51.9702013671875,45.76881171875,55.126201367187505,39.87061171875,55.126201367187505,35.87941171875C55.126201367187505,27.53471171875,48.3411013671875,20.74955640795,39.9964013671875,20.74951171875ZM39.9964013671875,42.745811718750005C36.1973013671875,42.745811718750005,33.1225213671875,39.67101171875,33.1225213671875,35.87201171875C33.1225213671875,32.07291171875,36.1973013671875,28.998131718750003,39.9964013671875,28.998131718750003C43.7954013671875,28.998131718750003,46.870201367187505,32.07291171875,46.870201367187505,35.87201171875C46.870201367187505,39.67101171875,43.7954013671875,42.745811718750005,39.9964013671875,42.745811718750005Z" fill="#FFFFFF" fill-opacity="1" style="mix-blend-mode:passthrough"/></g></g></g></svg> |
@ -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="80" height="80" viewBox="0 0 80 80"><defs><clipPath id="master_svg0_94_4553"><rect x="21" y="59" width="38" height="38" rx="0"/></clipPath></defs><g><g><ellipse cx="40" cy="40" rx="40" ry="40" fill="#EEEFF8" fill-opacity="1"/></g><g transform="matrix(1,0,0,-1,0,118)" clip-path="url(#master_svg0_94_4553)"><g><path d="M56.9528,97L23.04725,97C21.916585,97,21,96.0834,21,94.9528L21,61.04725C21,59.91675,21.91675,59,23.04725,59L56.9551,59C58.0832,59,59,59.91675,59,61.04725L59,94.9551C58.9987,96.0849,58.0825,97,56.9528,97ZM50.8134,83.21549999999999L50.8134,64.88525C50.8108,64.25542,50.300799999999995,63.74548,49.671,63.74288C49.0412,63.74548,48.5312,64.25541,48.5286,64.88525L48.5286,83.2226C46.297200000000004,83.8023,44.843599999999995,85.9491,45.1338,88.2362C45.424099999999996,90.5233,47.3679,92.239,49.6734,92.24289999999999C51.9827,92.2411,53.9304,90.52250000000001,54.2198,88.2313C54.5091,85.9402,53.0497,83.7914,50.8134,83.21549999999999ZM41.142399999999995,73.5754L41.142399999999995,64.90663C41.1398,64.27679,40.6298,63.76685,40,63.76425C39.3702,63.76685,38.8602,64.27679,38.857600000000005,64.90663L38.857600000000005,73.5754C36.839,74.1033,35.4307,75.9268,35.430099999999996,78.0133C35.4296,80.0999,36.8369,81.9241,38.8553,82.4531L38.8553,91.1219C38.8553,91.74889999999999,39.370599999999996,92.2642,39.9976,92.2642C40.6275,92.2616,41.1374,91.7517,41.14,91.1219L41.14,82.4531C43.1652,81.93209999999999,44.5811,80.10640000000001,44.5816,78.0152C44.5822,75.924,43.1673,74.0975,41.142399999999995,73.5754ZM30.33138,63.73575C28.02941,63.74415,26.09074,65.45857,25.80085,67.74223C25.51095,70.02590000000001,26.95977,72.1703,29.186619999999998,72.7536L29.186619999999998,91.09100000000001C29.186619999999998,91.718,29.701999999999998,92.2334,30.329,92.2334C30.95883,92.2308,31.4688,91.7208,31.4714,91.09100000000001L31.4714,72.7536C33.7053,72.1781,35.1629,70.0312,34.873599999999996,67.74254C34.584199999999996,65.45384,32.6383,63.73726,30.33138,63.73575Z" fill="#4F85FB" fill-opacity="1" style="mix-blend-mode:passthrough"/></g></g></g></svg> |
@ -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="80" height="80" viewBox="0 0 80 80"><g><g><ellipse cx="40" cy="40" rx="40" ry="40" fill="#479CF1" fill-opacity="1"/></g><g><g><path d="M57.7309,36.6424L54.4199,35.1065C52.2821,34.1115,51.0169,31.80957,51.2919,29.41513L51.7134,25.70348C49.9764,24.13487,47.9673,22.91636,45.7913,22.111649L42.809,24.29289C40.9002,25.70218,38.3223,25.67794,36.438900000000004,24.23298L33.5495,22C31.3612,22.766417,29.33189,23.9468,27.56639,25.48018L27.92691,29.18911C28.16995,31.58721,26.871940000000002,33.8705,24.71933,34.8315L21.35269,36.3074C20.9039103,38.6357,20.883231,41.0297,21.291719,43.3658L24.60269,44.9071C26.74368,45.8995,28.01241,48.202,27.73869,50.5985L27.30925,54.2911C29.0484,55.858,31.056800000000003,57.0779,33.2313,57.8884L36.1897,55.7098C38.098,54.2997,40.6759,54.3229,42.559799999999996,55.767L45.4705,58C47.6601,57.2361,49.6906,56.0565,51.456199999999995,54.5225L51.0984,50.8054C50.8651,48.409099999999995,52.158699999999996,46.1352,54.306,45.1685L57.6461,43.6953C58.0967,41.3643,58.1179,38.9679,57.7044,36.6287L57.7309,36.6424ZM39.5113,49.514700000000005C34.3981,49.514700000000005,30.25307,45.2566,30.25307,40.0041C30.25307,34.7515,34.3981,30.49349,39.5113,30.49349C44.6245,30.49349,48.7696,34.7515,48.7696,40.0041C48.7696,45.2566,44.6245,49.514700000000005,39.5113,49.514700000000005Z" fill="#FFFFFF" fill-opacity="1" style="mix-blend-mode:passthrough"/></g><g><path d="M39.503226597900394,32.666666984558105C35.85293659790039,32.6648691245581,32.89285659790039,35.650166984558105,32.89285659790039,39.3333369845581C32.89285659790039,43.01646698455811,35.85293659790039,46.0017669845581,39.503226597900394,45.99996698455811C43.15095659790039,45.998166984558104,46.10715659790039,43.01396698455811,46.10715659790039,39.3333369845581C46.10715659790039,35.65270698455811,43.15095659790039,32.66846320455811,39.503226597900394,32.666666984558105ZM39.503226597900394,43.0462669845581C37.535416597900394,42.95376698455811,35.98716659790039,41.316986984558106,35.98716659790039,39.329296984558106C35.98716659790039,37.34159698455811,37.535416597900394,35.704856984558106,39.503226597900394,35.61227698455811C41.495636597900386,35.7097269845581,43.08915659790039,37.317586984558105,43.185756597900394,39.3279469845581C43.17395659790039,41.372516984558104,41.52956659790039,43.024566984558106,39.503226597900394,43.0274669845581L39.503226597900394,43.0462669845581Z" fill="#FFFFFF" fill-opacity="1" style="mix-blend-mode:passthrough"/></g></g></g></svg> |
@ -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="80" height="80" viewBox="0 0 80 80"><g><g><ellipse cx="40" cy="40" rx="40" ry="40" fill="#479CF1" fill-opacity="1"/></g><g><path d="M29.44471,32.3043C29.44472,37.9952,34.0687,42.608599999999996,39.772800000000004,42.608599999999996C45.4768,42.608599999999996,50.100899999999996,37.9952,50.100899999999996,32.3043C50.100899999999996,26.6134,45.4768,22,39.772800000000004,22C34.0687,22,29.44472,26.6134,29.44471,32.3043ZM55.678,46.5004L51.0002,46.5004C50.2702,46.5004,49.678200000000004,45.9097,49.678200000000004,45.1814C49.678200000000004,44.4531,50.2702,43.8625,51.0002,43.8625L55.678,43.8625C56.408,43.8625,57,44.4531,57,45.1814C57,45.9097,56.408,46.5004,55.678,46.5004ZM55.678,52.2502L47.580799999999996,52.2502C46.8508,52.2502,46.2588,51.6595,46.2588,50.931200000000004C46.2588,50.2029,46.8508,49.612300000000005,47.580799999999996,49.612300000000005L55.678,49.612300000000005C56.408,49.612300000000005,57,50.2029,57,50.931200000000004C57,51.6595,56.408,52.2502,55.678,52.2502ZM55.678,58L47.580799999999996,58C46.8508,58,46.2588,57.4094,46.2588,56.681C46.2588,55.9527,46.8508,55.3621,47.580799999999996,55.3621L55.678,55.3621C56.408,55.3621,57,55.9527,57,56.681C57,57.4094,56.408,58,55.678,58ZM41.260000000000005,52.9303C41.260000000000005,48.602900000000005,43.934200000000004,44.898700000000005,47.723299999999995,43.3724C48.3662,43.113600000000005,48.4289,42.229,47.825,41.8898C45.435,40.546099999999996,42.6911,39.782,39.772800000000004,39.782C30.50933,39.782,23,47.4797,23,56.9757C23,57.0475,23.000413211,57.1188,23.00123932,57.1901C23.0066098,57.6398,23.376768,58,23.827485,58L41.2753,58C41.865700000000004,58,42.262299999999996,57.4015,42.037099999999995,56.857C41.5364,55.6469,41.260000000000005,54.3209,41.260000000000005,52.9303Z" fill="#FFFFFF" fill-opacity="1" style="mix-blend-mode:passthrough"/></g></g></svg> |
@ -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="32.66561508178711" height="39.13572692871094" viewBox="0 0 32.66561508178711 39.13572692871094"><g><path d="M29.768,16.829C29.4345,16.829,27.5584,16.8498,27.204,16.8498L27.204,10.9347C27.204,4.37387,22.9722,0.0208277,16.3641,0C9.75591,0,5.4408,4.58215,5.4408,10.9347L5.4408,16.7873C5.21149,16.7873,2.8142,16.8082,2.60574,16.8082C1.02145,16.8082,0,18.162,0,19.6199L0,36.2615C0,37.9069,0.87553,39.1357,2.56405,39.1357L29.8514,39.1357C31.1647,39.1357,32.6656,37.9485,32.6656,35.8033L32.6656,19.9948C32.6656,18.3703,32.1236,16.829,29.768,16.829ZM19.9704,31.7626C19.9704,33.8246,18.3444,35.4908,16.3432,35.4908C14.342,35.4908,12.716,33.8246,12.716,31.7626L12.716,31.7001C12.716,30.3255,13.4456,29.1175,14.5296,28.4718L14.5296,21.5986C14.5296,20.6197,15.3009,20.5155,16.2598,20.5155C17.2188,20.5155,18.1568,20.6197,18.1568,21.5986L18.1568,28.4718C19.2408,29.1175,19.9704,30.3255,19.9704,31.7001L19.9704,31.7626ZM23.6185,16.7873L9.08884,16.7873L9.08884,9.28927C9.08884,5.68603,13.1121,3.7282,16.1348,3.7282L16.8018,3.7282C19.8245,3.7282,23.6185,5.54024,23.6185,9.28927L23.6185,16.7873Z" fill="#1989FA" fill-opacity="1" style="mix-blend-mode:passthrough"/></g></svg> |
@ -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="36" height="36" viewBox="0 0 36 36"><g><path d="M0.0338876,21.6006C0.791888,25.2568,2.6603,28.5461,5.43529,31.1111C5.80569,31.454,6.31565,31.6116,6.8194,31.5389C7.3235,31.4665,7.76624,31.1719,8.02131,30.7391L10.1884,27.0549C10.5503,26.4391,10.4661,25.6645,9.97991,25.1375C8.84698,23.9069,8.02102,22.4346,7.56723,20.8368C7.36685,20.128,6.70975,19.6374,5.96078,19.6374L1.66693,19.6374C1.16648,19.6372,0.692517,19.8581,0.376395,20.2389C0.0591609,20.619,-0.0666573,21.1192,0.0338876,21.6006ZM22.0852,29.174C21.7232,28.5559,20.9949,28.2395,20.2854,28.3921C18.4901,28.7781,16.6239,28.7054,14.8655,28.1807C14.1235,27.9594,13.3239,28.2689,12.9355,28.9278L10.808,32.5469C10.5628,32.9646,10.5176,33.4671,10.6845,33.9204C10.8509,34.3741,11.2133,34.7322,11.6745,34.8985C13.6899,35.6293,15.8187,36,17.9994,36C19.8024,36,21.5824,35.7444,23.2887,35.2393C23.7758,35.0946,24.1692,34.7399,24.3574,34.2758C24.545,33.8109,24.5056,33.2873,24.2505,32.8545L22.0852,29.174ZM29.7455,4.17944C29.4462,3.9339,29.0685,3.79934,28.6784,3.79924C28.5817,3.79924,28.4831,3.80922,28.3864,3.82559C27.9025,3.91049,27.4815,4.20075,27.2359,4.61875L25.1037,8.24068C24.7167,8.90032,24.8432,9.73465,25.4092,10.255C26.8597,11.5847,27.9059,13.2833,28.4313,15.1619C28.6304,15.8724,29.2886,16.3647,30.0393,16.3649L34.3337,16.3649C34.8331,16.3646,35.3062,16.1445,35.6226,15.7652C35.9401,15.3857,36.0664,14.886,35.9664,14.4048C35.1469,10.4185,32.9378,6.78689,29.7455,4.17944ZM24.3574,1.72602C24.1697,1.26149,23.7761,0.906541,23.2887,0.762205C21.5731,0.255605,19.791,-0.00119639,17.9994,0.00000381258C15.8187,0.00000381258,13.6899,0.370486,11.6745,1.10122C10.7057,1.45352,10.2924,2.57637,10.808,3.45511L12.9355,7.07371C13.3239,7.73262,14.1235,8.04214,14.8655,7.82082C15.8814,7.51699,16.9376,7.3629,17.9996,7.3636C18.7626,7.3636,19.5323,7.44547,20.2854,7.60794C20.9944,7.76016,21.7222,7.44468,22.0852,6.82783L24.2508,3.1468C24.5058,2.71413,24.5451,2.1907,24.3574,1.72602ZM34.3337,19.6371L30.0393,19.6371C29.2884,19.6367,28.63,20.1292,28.4313,20.8399C27.9057,22.7185,26.8595,24.4171,25.4092,25.747C24.8431,26.2665,24.7165,27.1004,25.1037,27.7593L27.2359,31.3828C27.4812,31.8011,27.9024,32.0915,28.3864,32.1762C28.4832,32.1926,28.5817,32.2008,28.6784,32.2008C29.0683,32.2013,29.446,32.0673,29.7455,31.8223C32.9378,29.2131,35.1469,25.583,35.9665,21.5972C36.0665,21.1161,35.9402,20.6163,35.6226,20.2368C35.3066,19.857,34.8333,19.6368,34.3337,19.6371ZM0.376135,15.7632C0.692116,16.1438,1.16603,16.3647,1.66666,16.3647L5.96105,16.3647C6.71021,16.3644,7.36726,15.8738,7.5675,15.1652C8.02109,13.5669,8.84706,12.0941,9.98017,10.863C10.4664,10.3367,10.5507,9.56236,10.1887,8.9471L8.02157,5.263C7.76613,4.8302,7.3237,4.53522,6.81966,4.46166C6.73946,4.45042,6.65853,4.44495,6.57751,4.44528C6.1568,4.44528,5.746,4.60263,5.43555,4.89099C2.6603,7.45571,0.791888,10.7448,0.0338876,14.4012C-0.0665431,14.8826,0.0591547,15.3828,0.376135,15.7632Z" fill="#4F85FB" fill-opacity="1"/></g></svg> |
@ -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="70" height="70" viewBox="0 0 70 70"><defs><clipPath id="master_svg0_685_8843"><rect x="0" y="0" width="70" height="70" rx="0"/></clipPath></defs><g clip-path="url(#master_svg0_685_8843)"><g><path d="M35,70C15.6712,70,0,54.3288,0,35C0,15.6712,15.6712,0,35,0C54.3288,0,70,15.6712,70,35C70,54.3288,54.3288,70,35,70ZM27.195,48.7433C28.6189,50.1676,30.9278,50.1676,32.3517,48.7433L54.4542,26.6408C55.8781,25.2169,55.8781,22.9081,54.4542,21.4842C53.0302,20.0602,50.7215,20.0602,49.2975,21.4842L29.6158,40.8508L20.8075,32.0425C19.3838,30.6166,17.0736,30.6158,15.6488,32.0405C14.2241,33.4653,14.225,35.7755,15.6508,37.1992L27.195,48.7433Z" fill="#14A656" fill-opacity="1" style="mix-blend-mode:passthrough"/></g></g></svg> |
@ -0,0 +1,22 @@ |
|||
<svg width="108" height="108" viewBox="0 0 108 108" fill="none" xmlns="http://www.w3.org/2000/svg"> |
|||
<g filter="url(#filter0_d_447_4258)"> |
|||
<circle cx="54" cy="50" r="50" fill="url(#paint0_linear_447_4258)"/> |
|||
<path d="M54 45.1119L71.1119 28L76 32.8881L58.8881 50L76 67.1119L71.1119 72L54 54.8881L36.8881 72L32 67.1119L49.1119 50L32 32.8881L36.8881 28L54 45.1119Z" fill="white"/> |
|||
</g> |
|||
<defs> |
|||
<filter id="filter0_d_447_4258" x="0" y="0" width="108" height="108" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB"> |
|||
<feFlood flood-opacity="0" result="BackgroundImageFix"/> |
|||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/> |
|||
<feOffset dy="4"/> |
|||
<feGaussianBlur stdDeviation="2"/> |
|||
<feComposite in2="hardAlpha" operator="out"/> |
|||
<feColorMatrix type="matrix" values="0 0 0 0 0.877278 0 0 0 0 0.28742 0 0 0 0 0.421121 0 0 0 1 0"/> |
|||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_447_4258"/> |
|||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_447_4258" result="shape"/> |
|||
</filter> |
|||
<linearGradient id="paint0_linear_447_4258" x1="30.5" y1="17" x2="84.5" y2="100" gradientUnits="userSpaceOnUse"> |
|||
<stop stop-color="#F08564"/> |
|||
<stop offset="1" stop-color="#EA467D"/> |
|||
</linearGradient> |
|||
</defs> |
|||
</svg> |
@ -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="40" height="40" viewBox="0 0 40 40"><defs><clipPath id="master_svg0_609_03555/609_03497"><rect x="8" y="8" width="24" height="24" rx="0"/></clipPath></defs><g><rect x="0" y="0" width="40" height="40" rx="20" fill="#1989FA" fill-opacity="1"/><g clip-path="url(#master_svg0_609_03555/609_03497)"><g><path d="M28.120604907226564,25.896875C27.67840490722656,24.849575,27.03680490722656,23.898375,26.231504907226565,23.096075C25.428604907226564,22.291475,24.47750490722656,21.649974999999998,23.430704907226563,21.207075C23.421304907226563,21.202375,23.41200490722656,21.199975000000002,23.40260490722656,21.195275000000002C24.862704907226565,20.140625,25.812004907226562,18.422655,25.812004907226562,16.484375C25.812004907226562,13.273435,23.21040490722656,10.671875,19.99946490722656,10.671875C16.788524907226563,10.671875,14.186964907226562,13.273435,14.186964907226562,16.484375C14.186964907226562,18.422655,15.136184907226562,20.140625,16.596334907226563,21.197675C16.586964907226562,21.202375,16.577584907226562,21.204675,16.56821490722656,21.209375C15.518214907226563,21.652375,14.576024907226563,22.287475,13.767434907226562,23.098475C12.962844907226563,23.901375,12.321344907226562,24.852475,11.878369907226563,25.899175C11.443186907226563,26.924075,11.208483707226563,28.022775,11.186963515026562,29.135975C11.186337956226563,29.160975,11.190725517226563,29.185875,11.199867807226562,29.209175C11.209010107226563,29.232375,11.222722007226562,29.253675,11.240195707226562,29.271575C11.257669407226562,29.289475,11.278551207226563,29.303675,11.301610907226562,29.313375C11.324669907226562,29.323175,11.349440907226562,29.328175,11.374463907226563,29.328075C11.374463907226563,29.328075,12.780714907226562,29.328075,12.780714907226562,29.328075C12.883834907226563,29.328075,12.965864907226562,29.246075,12.968214907226562,29.145275C13.015084907226562,27.335975,13.741654907226563,25.641375,15.026024907226562,24.357075000000002C16.354934907226564,23.028174999999997,18.119774907226564,22.296875,19.99946490722656,22.296875C21.879104907226562,22.296875,23.644004907226563,23.028174999999997,24.972904907226564,24.357075000000002C26.257304907226562,25.641375,26.983804907226563,27.335975,27.030704907226564,29.145275C27.033104907226562,29.248475,27.115104907226563,29.328075,27.218204907226564,29.328075C27.218204907226564,29.328075,28.624504907226562,29.328075,28.624504907226562,29.328075C28.64950490722656,29.328175,28.674304907226563,29.323175,28.697304907226563,29.313375C28.720404907226563,29.303675,28.741304907226564,29.289475,28.758704907226562,29.271575C28.776204907226564,29.253675,28.789904907226564,29.232375,28.799104907226564,29.209175C28.808204907226564,29.185875,28.81260490722656,29.160975,28.812004907226562,29.135975C28.788504907226564,28.015675,28.55650490722656,26.925775,28.120604907226564,25.896875C28.120604907226564,25.896875,28.120604907226564,25.896875,28.120604907226564,25.896875ZM19.99946490722656,20.515625C18.923684907226562,20.515625,17.911184907226563,20.096095,17.149464907226562,19.334375C16.387744907226562,18.572655,15.968214907226564,17.560155,15.968214907226564,16.484375C15.968214907226564,15.408595,16.387744907226562,14.396094999999999,17.149464907226562,13.634375C17.911184907226563,12.872655,18.923684907226562,12.453125,19.99946490722656,12.453125C21.075244907226562,12.453125,22.087704907226563,12.872655,22.84950490722656,13.634375C23.611204907226565,14.396094999999999,24.030704907226564,15.408595,24.030704907226564,16.484375C24.030704907226564,17.560155,23.611204907226565,18.572655,22.84950490722656,19.334375C22.087704907226563,20.096095,21.075244907226562,20.515625,19.99946490722656,20.515625Z" fill="#FFFFFF" fill-opacity="1"/></g></g></g></svg> |
@ -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="33.87468338012695" height="33.84542465209961" viewBox="0 0 33.87468338012695 33.84542465209961"><g><path d="M33.8747,32.442C33.8747,25.5468,28.9971,19.6889,22.2628,17.6265C24.8808,15.9057,26.6152,12.9605,26.6152,9.61666C26.6152,4.31204,22.275,0,16.9373,0C11.5996,0,7.25944,4.31204,7.25944,9.61259C7.25944,12.9565,8.99389,15.9017,11.6118,17.6224C4.87763,19.6889,0,25.5427,0,32.442L0,33.8454L33.8747,33.8454L33.8747,32.4623L33.8747,32.442Z" fill="#1989FA" fill-opacity="1" style="mix-blend-mode:passthrough"/></g></svg> |
@ -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="80" height="80" viewBox="0 0 80 80"><defs><clipPath id="master_svg0_685_8855"><rect x="0" y="0" width="80" height="80" rx="0"/></clipPath></defs><g clip-path="url(#master_svg0_685_8855)"><g><path d="M40.00636015625,5.01953125C20.68606015625,5.01953125,5.01416254044,20.69143125,5.01416254044,40.01173125C5.01416254044,59.33203125,20.68606015625,74.99613125,40.00636015625,74.99613125C59.32666015625,74.99613125,74.99856015625,59.32423125,74.99856015625,40.00393125C74.99856015625,20.683631249999998,59.32666015625,5.01953125,40.00636015625,5.01953125ZM21.97506015625,45.97263125C18.678260156249998,45.97263125,16.00636015625,43.30073125,16.00636015625,40.00393125C16.00636015625,36.70703125,18.678260156249998,34.03513125,21.97506015625,34.03513125C25.27196015625,34.03513125,27.94386015625,36.70703125,27.94386015625,40.00393125C27.94386015625,43.30073125,25.27196015625,45.97263125,21.97506015625,45.97263125ZM39.92046015625,45.97263125C36.62356015625,45.97263125,33.95166015625,43.30073125,33.95166015625,40.00393125C33.95166015625,36.70703125,36.62356015625,34.03513125,39.92046015625,34.03513125C43.21726015625,34.03513125,45.88916015625,36.70703125,45.88916015625,40.00393125C45.88916015625,43.30073125,43.21726015625,45.97263125,39.92046015625,45.97263125ZM57.86576015625,45.97263125C54.56886015625,45.97263125,51.89696015625,43.30073125,51.89696015625,40.00393125C51.89696015625,36.70703125,54.56886015625,34.03513125,57.86576015625,34.03513125C61.16256015625,34.03513125,63.83446015625,36.70703125,63.83446015625,40.00393125C63.83446015625,43.30073125,61.16256015625,45.97263125,57.86576015625,45.97263125Z" fill="#EE8223" fill-opacity="1" style="mix-blend-mode:passthrough"/></g></g></svg> |
@ -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="42" height="34" viewBox="0 0 42 34"><g><path d="M41.5238,8.34171C30.1914,-2.78057,11.812,-2.78057,0.479575,8.34171C-0.159858,8.96929,-0.159858,9.98404,0.479575,10.6072C1.11901,11.2347,2.15289,11.2347,2.78777,10.6072C12.8459,0.735472,29.1575,0.735472,39.2156,10.6072C39.8551,11.2347,40.889,11.2347,41.5238,10.6072C42.1587,9.984,42.1587,8.96929,41.5238,8.34171ZM6.63327,15.2314C5.99383,15.859,5.99383,16.8737,6.63327,17.4968C7.2727,18.1244,8.30658,18.1244,8.94146,17.4968C15.5985,10.9632,26.3958,10.9632,33.0529,17.4968C33.6923,18.1244,34.7262,18.1244,35.3611,17.4968C36.0005,16.8692,36.0005,15.8545,35.3611,15.2314C27.4297,7.44712,14.5691,7.44712,6.63327,15.2314L6.63327,15.2314ZM29.2346,22.5395L29.2436,22.5306C24.7089,18.0799,17.358,18.0799,12.8278,22.5306C12.1883,23.1581,12.1883,24.1728,12.8278,24.7959C13.4672,25.4235,14.5011,25.4235,15.136,24.7959C18.3965,21.5959,23.6795,21.5959,26.9355,24.7959L26.9445,24.7871C26.9828,24.8337,27.0237,24.8783,27.0669,24.9206C27.7064,25.5481,28.7403,25.5481,29.3752,24.9206C30.0146,24.293,30.0146,23.2783,29.3752,22.6552C29.3253,22.6151,29.2799,22.5795,29.2346,22.5395ZM18.596,31.5966C18.596,32.924,19.6923,34,21.0448,34C22.3972,34,23.4935,32.924,23.4935,31.5966C23.4935,30.2693,22.3972,29.1932,21.0448,29.1932C19.6923,29.1932,18.596,30.2692,18.596,31.5966Z" fill="#1989FA" fill-opacity="1" style="mix-blend-mode:passthrough"/></g></svg> |
@ -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="42" height="34" viewBox="0 0 42 34"><g><path d="M41.5238,8.34171C30.1914,-2.78057,11.812,-2.78057,0.479575,8.34171C-0.159858,8.96929,-0.159858,9.98404,0.479575,10.6072C1.11901,11.2347,2.15289,11.2347,2.78777,10.6072C12.8459,0.735472,29.1575,0.735472,39.2156,10.6072C39.8551,11.2347,40.889,11.2347,41.5238,10.6072C42.1587,9.984,42.1587,8.96929,41.5238,8.34171ZM6.63327,15.2314C5.99383,15.859,5.99383,16.8737,6.63327,17.4968C7.2727,18.1244,8.30658,18.1244,8.94146,17.4968C15.5985,10.9632,26.3958,10.9632,33.0529,17.4968C33.6923,18.1244,34.7262,18.1244,35.3611,17.4968C36.0005,16.8692,36.0005,15.8545,35.3611,15.2314C27.4297,7.44712,14.5691,7.44712,6.63327,15.2314L6.63327,15.2314ZM29.2346,22.5395L29.2436,22.5306C24.7089,18.0799,17.358,18.0799,12.8278,22.5306C12.1883,23.1581,12.1883,24.1728,12.8278,24.7959C13.4672,25.4235,14.5011,25.4235,15.136,24.7959C18.3965,21.5959,23.6795,21.5959,26.9355,24.7959L26.9445,24.7871C26.9828,24.8337,27.0237,24.8783,27.0669,24.9206C27.7064,25.5481,28.7403,25.5481,29.3752,24.9206C30.0146,24.293,30.0146,23.2783,29.3752,22.6552C29.3253,22.6151,29.2799,22.5795,29.2346,22.5395ZM18.596,31.5966C18.596,32.924,19.6923,34,21.0448,34C22.3972,34,23.4935,32.924,23.4935,31.5966C23.4935,30.2693,22.3972,29.1932,21.0448,29.1932C19.6923,29.1932,18.596,30.2692,18.596,31.5966Z" fill="#DF1515" fill-opacity="1" style="mix-blend-mode:passthrough"/></g></svg> |
@ -0,0 +1,91 @@ |
|||
/* CSS Document */ |
|||
html { |
|||
font-family: 'PingFangSC-Regular', 'PingFang SC', 'Microsoft YaHei', '微软雅黑', Arial, sans-serif; |
|||
height: 100vh; |
|||
font-size: 14px; |
|||
} |
|||
html, body { |
|||
overflow: hidden; |
|||
} |
|||
html, |
|||
body, |
|||
ol, |
|||
dl, |
|||
dd, |
|||
dt, |
|||
p, |
|||
h1, |
|||
h2, |
|||
h3, |
|||
h4, |
|||
h5, |
|||
h6, |
|||
form, |
|||
fieldset, |
|||
legend, |
|||
img { |
|||
margin: 0; |
|||
padding: 0; |
|||
} |
|||
fieldset { |
|||
border: none; |
|||
} |
|||
img { |
|||
display: block; |
|||
} |
|||
address, |
|||
caption, |
|||
cite, |
|||
code, |
|||
dfn, |
|||
th, |
|||
var { |
|||
font-style: normal; |
|||
font-weight: normal; |
|||
} |
|||
ul, |
|||
ol, |
|||
li { |
|||
list-style: none; |
|||
} |
|||
a { |
|||
color: #666; |
|||
text-decoration: none; |
|||
} |
|||
* { |
|||
box-sizing: border-box !important; |
|||
} |
|||
|
|||
a { |
|||
&:visited { |
|||
color: inherit; |
|||
} |
|||
} |
|||
|
|||
input, |
|||
button, |
|||
select, |
|||
textarea { |
|||
outline: none; |
|||
} |
|||
|
|||
textarea { |
|||
resize: none; |
|||
} |
|||
|
|||
input[type='number'] { |
|||
&::-webkit-outer-spin-button, |
|||
&::-webkit-inner-spin-button { |
|||
-webkit-appearance: none; |
|||
} |
|||
-moz-appearance: textfield; |
|||
} |
|||
.clear { |
|||
clear: both; |
|||
} |
|||
|
|||
.konvajs-content{ |
|||
canvas{ |
|||
border: 1px solid #ccc !important; |
|||
} |
|||
} |
@ -0,0 +1,72 @@ |
|||
:root { |
|||
// --el-font-size-base: 50px; |
|||
// --el-button-size: 80px; |
|||
|
|||
|
|||
|
|||
--el-color-primary: #1989fa; |
|||
//--el-button-active-bg-color: linear-gradient(90deg, #0657C0 24%, #096AE0 101%); |
|||
--text-color-primary: #17213c; |
|||
//--el-color-success: rgba(88, 162, 95, 1); |
|||
//--text-color-info: #838b99; |
|||
//--el-input-border: #dae0f2; |
|||
//--el-font-weight-primary: 400; |
|||
//--color-red: #f56c6c; |
|||
//--color-green: #67c23a; |
|||
//--color-yellow: #e6a23c; |
|||
//--color-blue: --el-color-primary; |
|||
--el-font-family: 'PingFangSC-Regular', 'PingFang SC', 'Microsoft YaHei', '微软雅黑', Arial, sans-serif; |
|||
} |
|||
.el-dialog { |
|||
position: absolute; |
|||
top: 50%; |
|||
left: 50%; |
|||
transform: translate(-50%, -50%); |
|||
margin: 0; |
|||
padding: 0; |
|||
.el-dialog__header { |
|||
background: rgba(0, 0, 0, .03); |
|||
padding: 10px; |
|||
} |
|||
.el-dialog__body { |
|||
padding: 10px 20px; |
|||
} |
|||
.el-dialog__footer { |
|||
padding: 10px; |
|||
} |
|||
} |
|||
|
|||
.el-input-group__append { |
|||
padding: 0 10px; |
|||
.el-icon { |
|||
margin: 0; |
|||
} |
|||
} |
|||
|
|||
.init-message { |
|||
.el-message-box__content { |
|||
.el-message-box__container { |
|||
align-items: flex-start; |
|||
ul { |
|||
padding-left: 10px; |
|||
font-size: 15px; |
|||
li { |
|||
font-weight: 700; |
|||
color: #FE0A0A; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
} |
|||
|
|||
.button-popper { |
|||
inset: auto 242px 20px auto !important; |
|||
.el-popper__arrow { |
|||
top: 100px !important; |
|||
} |
|||
} |
|||
|
|||
.el-message__content { |
|||
font-size: 16px; |
|||
} |
@ -0,0 +1,3 @@ |
|||
@use './common.scss'; |
|||
@use './element.scss'; |
|||
|
@ -0,0 +1,5 @@ |
|||
$primary-color: #1989FA; |
|||
$success-color: #14A656; |
|||
$danger-color: #DF1515; |
|||
$warn-color: #EE8223; |
|||
$info-color: #909399; |
@ -0,0 +1,109 @@ |
|||
<script setup lang="ts"> |
|||
import { getPointList, updatePoint } from 'apis/point' |
|||
import { getPoint } from 'apis/system' |
|||
import { onMounted, ref } from 'vue' |
|||
|
|||
const emits = defineEmits(['ok', 'cancel']) |
|||
|
|||
const pointList = ref<Point.Point[]>([]) |
|||
onMounted(async () => { |
|||
pointList.value = await getPointList() |
|||
}) |
|||
|
|||
const form = ref<Point.UpdateParams>({}) |
|||
const formRef = ref() |
|||
const rules = { |
|||
id: [ |
|||
{ required: true, message: '请选择名称', trigger: 'change' }, |
|||
], |
|||
position: [ |
|||
{ required: true, message: '请输入坐标', trigger: 'blur' }, |
|||
], |
|||
} |
|||
|
|||
const okHandle = async () => { |
|||
try { |
|||
const valid = await formRef.value.validate() |
|||
if (!valid) { |
|||
return |
|||
} |
|||
await updatePoint(form.value!) |
|||
emits('ok') |
|||
} |
|||
catch (error) { |
|||
console.log(error) |
|||
} |
|||
} |
|||
const cancel = () => { |
|||
emits('cancel') |
|||
} |
|||
|
|||
const motor = [ |
|||
{ |
|||
name: '转运机械臂三维点', |
|||
code: 'Transfer', |
|||
}, |
|||
{ |
|||
name: 'X轴电机', |
|||
code: 'XSV', |
|||
}, |
|||
|
|||
{ |
|||
name: 'Z轴电机', |
|||
code: 'ZM', |
|||
}, |
|||
{ |
|||
|
|||
name: '门电机', |
|||
code: 'DoorM', |
|||
}, |
|||
{ |
|||
|
|||
name: '加液臂电机', |
|||
code: 'LiquidM', |
|||
}, |
|||
] |
|||
|
|||
const selectChange = async (val: string) => { |
|||
form.value!.position = await getPoint(val) |
|||
} |
|||
</script> |
|||
|
|||
<template> |
|||
<FtDialog visible title="保存坐标" width="40%" :ok-handle="okHandle" @cancel="cancel"> |
|||
<el-form ref="formRef" label-width="auto" :model="form" :rules="rules"> |
|||
<el-form-item label="选择电机"> |
|||
<el-select v-model="form.type" clearable placeholder="请选择电机" @change="selectChange"> |
|||
<el-option |
|||
v-for="item in motor" |
|||
:key="item.code" |
|||
:label="item.name" |
|||
:value="item.code" |
|||
/> |
|||
</el-select> |
|||
</el-form-item> |
|||
<el-form-item label="当前坐标" prop="position"> |
|||
<el-input v-model="form.position" /> |
|||
</el-form-item> |
|||
<el-form-item label="名称" prop="id"> |
|||
<el-select v-model="form.id" placeholder="请选择名称"> |
|||
<el-option |
|||
v-for="item in pointList" |
|||
:key="item.id" |
|||
:label="item.name" |
|||
:value="item.id" |
|||
/> |
|||
</el-select> |
|||
</el-form-item> |
|||
</el-form> |
|||
</FtDialog> |
|||
</template> |
|||
|
|||
<style scoped lang="scss"> |
|||
.item-box { |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: space-between; |
|||
padding: 5px; |
|||
} |
|||
</style> |
@ -0,0 +1,158 @@ |
|||
<script setup lang="ts"> |
|||
import { ref } from 'vue' |
|||
|
|||
interface FTButton { |
|||
type?: 'default' | 'primary' | 'info' | 'danger' |
|||
size?: 'small' | 'default' | 'large' |
|||
disabled?: boolean |
|||
loading?: boolean |
|||
clickHandle?: () => any |
|||
} |
|||
|
|||
const props = withDefaults(defineProps<FTButton>(), { |
|||
type: 'default', |
|||
size: 'default', |
|||
disabled: false, |
|||
loading: false, |
|||
clickHandle: () => {}, |
|||
}) |
|||
|
|||
const isLoading = ref(false) |
|||
|
|||
async function handleClick() { |
|||
if (!props.clickHandle || isLoading.value) |
|||
return |
|||
isLoading.value = true // 进入 loading |
|||
try { |
|||
await props.clickHandle() // 执行异步操作 |
|||
} |
|||
finally { |
|||
isLoading.value = false // 结束 loading |
|||
} |
|||
} |
|||
|
|||
const setLoading = (loading: boolean) => { |
|||
isLoading.value = loading |
|||
} |
|||
|
|||
defineExpose({ |
|||
setLoading, |
|||
}) |
|||
</script> |
|||
|
|||
<template> |
|||
<div class="ft-button" :class="{ 'ft-button-disabled': disabled || isLoading, [`ft-button-${size}`]: true }" @click="handleClick"> |
|||
<!-- 添加 loading 判断 --> |
|||
<div v-show="disabled || isLoading" class="my-button-shadow" /> <!-- 添加 loading 判断 --> |
|||
<div |
|||
class="my-button" :class="{ |
|||
[`my-button-${type}`]: true, |
|||
[`my-button-${size}`]: true, |
|||
'button-disabled': disabled || isLoading, // 添加 loading 判断 |
|||
}" |
|||
> |
|||
<el-icon v-if="isLoading" :color="type === 'default' ? '#26509C' : '#fff'"> |
|||
<!-- 添加 loading 判断 --> |
|||
<Loading class="rotate-loading" /> <!-- 添加旋转类 --> |
|||
</el-icon> |
|||
<slot /> |
|||
</div> |
|||
</div> |
|||
</template> |
|||
|
|||
<style scoped lang="scss"> |
|||
.ft-button { |
|||
position: relative; |
|||
display: inline-block; |
|||
margin-right: 10px; |
|||
} |
|||
.ft-button-small { |
|||
margin-right: 5px; |
|||
} |
|||
.ft-button-disabled { |
|||
pointer-events: none; |
|||
} |
|||
.my-button-shadow { |
|||
position: absolute; |
|||
width: 100%; |
|||
height: 100%; |
|||
z-index: 100; |
|||
} |
|||
.my-button { |
|||
height: 30px; |
|||
padding: 5px 20px; |
|||
border-radius: 5px; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
cursor: pointer; |
|||
font-size: 14px; |
|||
//width: fit-content; |
|||
position: relative; |
|||
.el-icon { |
|||
position: absolute; |
|||
left: 5px; |
|||
svg { |
|||
width: 35px; |
|||
} |
|||
} |
|||
} |
|||
.button-disabled { |
|||
opacity: 0.5; |
|||
} |
|||
.my-button-large { |
|||
height: 40px; |
|||
font-size: 14px; |
|||
padding: 3px 15px; |
|||
.el-icon { |
|||
position: absolute; |
|||
left: 3px; |
|||
svg { |
|||
width: 25px; |
|||
} |
|||
} |
|||
} |
|||
|
|||
.my-button-small { |
|||
height: 25px; |
|||
font-size: 12px; |
|||
padding: 3px 15px; |
|||
.el-icon { |
|||
position: absolute; |
|||
left: 3px; |
|||
svg { |
|||
width: 25px; |
|||
} |
|||
} |
|||
} |
|||
.my-button-default { |
|||
background: #fff; |
|||
color: $primary-color; |
|||
border: 1px solid $primary-color; |
|||
} |
|||
.my-button-primary { |
|||
background: $primary-color; |
|||
color: #fff; |
|||
border: 1px solid $primary-color; |
|||
} |
|||
|
|||
.my-button-info { |
|||
background: #335AA5; |
|||
color: #fff; |
|||
border: 1px solid #335AA5; |
|||
} |
|||
.my-button-danger { |
|||
background: #e74444; |
|||
color: #fff; |
|||
border: 1px solid #e74444; |
|||
} |
|||
|
|||
.rotate-loading { |
|||
animation: spin 1s linear infinite; |
|||
} |
|||
|
|||
@keyframes spin { |
|||
0% { transform: rotate(0deg); } |
|||
100% { transform: rotate(360deg); } |
|||
} |
|||
</style> |
@ -0,0 +1,76 @@ |
|||
<script setup lang="ts"> |
|||
import { ref, watch } from 'vue' |
|||
|
|||
const props = defineProps({ |
|||
title: { |
|||
type: String, |
|||
default: '', |
|||
}, |
|||
visible: { |
|||
type: Boolean, |
|||
default: false, |
|||
}, |
|||
width: { |
|||
type: String, |
|||
default: '50%', |
|||
}, |
|||
okHandle: { |
|||
type: Function, |
|||
default: () => {}, |
|||
}, |
|||
}) |
|||
const emits = defineEmits(['update:visible', 'ok', 'cancel']) |
|||
|
|||
const cancel = () => { |
|||
show.value = false |
|||
emits('cancel') |
|||
} |
|||
|
|||
const show = ref(false) |
|||
|
|||
watch( |
|||
() => props.visible, |
|||
(newVal) => { |
|||
show.value = newVal |
|||
}, |
|||
{ |
|||
// 对于对象需要深度监听 |
|||
deep: true, |
|||
immediate: true, |
|||
}, |
|||
) |
|||
</script> |
|||
|
|||
<template> |
|||
<el-dialog |
|||
v-model="show" |
|||
center |
|||
:close-on-click-modal="false" |
|||
:close-on-press-escape="false" |
|||
:show-close="false" |
|||
destroy-on-close |
|||
append-to-body |
|||
:title="title" |
|||
:width="width" |
|||
:before-close="cancel" |
|||
> |
|||
<slot /> |
|||
<template #footer> |
|||
<div v-if="$slots.footer" class="dialog-footer"> |
|||
<slot name="footer" /> |
|||
</div> |
|||
<div v-else class="dialog-footer"> |
|||
<ft-button :click-handle="cancel"> |
|||
取消 |
|||
</ft-button> |
|||
<ft-button type="primary" :click-handle="okHandle"> |
|||
确认 |
|||
</ft-button> |
|||
</div> |
|||
</template> |
|||
</el-dialog> |
|||
</template> |
|||
|
|||
<style scoped lang="scss"> |
|||
|
|||
</style> |
@ -0,0 +1,182 @@ |
|||
<script setup lang="ts"> |
|||
import { cmdNameMap } from 'libs/utils' |
|||
import { useSystemStore } from 'stores/systemStore' |
|||
import { computed, nextTick, ref, watch } from 'vue' |
|||
|
|||
defineProps({ |
|||
visible: { |
|||
type: Boolean, |
|||
default: false, |
|||
}, |
|||
}) |
|||
|
|||
const systemStore = useSystemStore() |
|||
const title = computed(() => { |
|||
const commandKey = systemStore.systemList[0]?.command.replace(/^debug_/, '') |
|||
return cmdNameMap[commandKey as keyof typeof cmdNameMap] || systemStore.systemList[0]?.command |
|||
}) |
|||
|
|||
const maskBodyRef = ref<HTMLElement | null>(null) |
|||
const maskRef = ref<HTMLElement | null>(null) |
|||
const maskHeaderRef = ref<HTMLElement | null>(null) |
|||
|
|||
const statusMap = { |
|||
error: 'danger', |
|||
fail: 'danger', |
|||
success: 'success', |
|||
finish: 'primary', |
|||
SEND: 'primary', |
|||
receive: 'primary', |
|||
start: 'primary', |
|||
result: 'primary', |
|||
} |
|||
|
|||
watch( |
|||
() => systemStore.systemList, |
|||
async () => { |
|||
await nextTick() |
|||
if (maskBodyRef.value) { |
|||
maskBodyRef.value.scrollTop = maskBodyRef.value.scrollHeight |
|||
} |
|||
}, |
|||
{ deep: true }, |
|||
) |
|||
|
|||
// 拖拽相关逻辑 |
|||
let isDragging = false |
|||
let offsetX = 0 |
|||
let offsetY = 0 |
|||
|
|||
const handleMouseDown = (event: MouseEvent | TouchEvent) => { |
|||
if (maskRef.value && maskHeaderRef.value) { |
|||
isDragging = true |
|||
const clientX = 'clientX' in event ? event.clientX : event.touches[0].clientX |
|||
const clientY = 'clientY' in event ? event.clientY : event.touches[0].clientY |
|||
offsetX = clientX - maskRef.value.offsetLeft |
|||
offsetY = clientY - maskRef.value.offsetTop |
|||
document.addEventListener('mousemove', handleMouseMove) |
|||
document.addEventListener('mouseup', handleMouseUp) |
|||
document.addEventListener('touchmove', handleMouseMove) |
|||
document.addEventListener('touchend', handleMouseUp) |
|||
} |
|||
} |
|||
|
|||
const handleMouseMove = (event: MouseEvent | TouchEvent) => { |
|||
if (maskRef.value && isDragging) { |
|||
const clientX = 'clientX' in event ? event.clientX : event.touches[0].clientX |
|||
const clientY = 'clientY' in event ? event.clientY : event.touches[0].clientY |
|||
|
|||
// 获取 body 的宽度和高度 |
|||
const bodyWidth = document.body.clientWidth |
|||
const bodyHeight = document.body.clientHeight |
|||
|
|||
// 计算新的位置,并确保不会超出 body 的范围 |
|||
const newLeft = Math.max(0, Math.min(clientX - offsetX, bodyWidth - maskRef.value.offsetWidth)) |
|||
const newTop = Math.max(0, Math.min(clientY - offsetY, bodyHeight - maskRef.value.offsetHeight)) |
|||
|
|||
maskRef.value.style.left = `${newLeft}px` |
|||
maskRef.value.style.top = `${newTop}px` |
|||
} |
|||
} |
|||
|
|||
const handleMouseUp = () => { |
|||
isDragging = false |
|||
document.removeEventListener('mousemove', handleMouseMove) |
|||
document.removeEventListener('mouseup', handleMouseUp) |
|||
document.removeEventListener('touchmove', handleMouseMove) |
|||
document.removeEventListener('touchend', handleMouseUp) |
|||
} |
|||
</script> |
|||
|
|||
<template> |
|||
<teleport to="body"> |
|||
<!-- 使用 transition 组件包裹 mask 元素 --> |
|||
<transition name="mask-fade"> |
|||
<div |
|||
v-if="visible && systemStore.isDebug" |
|||
ref="maskRef" |
|||
class="mask" |
|||
> |
|||
<div |
|||
ref="maskHeaderRef" class="mask-header" @mousedown="handleMouseDown" |
|||
@touchstart="handleMouseDown" |
|||
> |
|||
<p>{{ title }}</p> |
|||
<el-icon @click="systemStore.updateStreamVisible(false)"> |
|||
<Close /> |
|||
</el-icon> |
|||
</div> |
|||
<div ref="maskBodyRef" class="mask-body"> |
|||
<el-timeline> |
|||
<el-timeline-item |
|||
v-for="item in systemStore.systemList" :key="item" |
|||
:timestamp="JSON.stringify(item.content)" |
|||
> |
|||
<el-tag :type="statusMap[item.status as keyof typeof statusMap]" class="mask-tag"> |
|||
{{ item.title }} |
|||
</el-tag> |
|||
</el-timeline-item> |
|||
</el-timeline> |
|||
</div> |
|||
</div> |
|||
</transition> |
|||
</teleport> |
|||
</template> |
|||
|
|||
<style scoped lang="scss"> |
|||
.mask { |
|||
width: 400px; |
|||
height: 250px; |
|||
padding: 5px 10px; |
|||
background: #fff; |
|||
box-shadow: var(--el-box-shadow-light); |
|||
position: absolute; |
|||
bottom: 20px; |
|||
right: 20px; |
|||
border-radius: 8px; |
|||
font-size: 30px; |
|||
z-index: 5000; |
|||
.mask-header { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
height: 30px; |
|||
font-size: 16px; |
|||
border-bottom: 1px solid #ddd; |
|||
cursor: move; // 添加鼠标指针样式 |
|||
.el-icon svg{ |
|||
width: 18px; |
|||
cursor: pointer; |
|||
} |
|||
} |
|||
.mask-body { |
|||
padding: 5px 2px; |
|||
height: calc(100% - 31px); |
|||
overflow: auto; |
|||
} |
|||
} |
|||
|
|||
/* 定义过渡效果 */ |
|||
//.mask-fade-enter-active, .mask-fade-leave-active { |
|||
// transition: transform 0.5s ease; |
|||
//} |
|||
// |
|||
//.mask-fade-enter-from { |
|||
// transform: translateX(100%); |
|||
//} |
|||
// |
|||
//.mask-fade-leave-to { |
|||
// transform: translateX(100%); |
|||
//} |
|||
:deep(.el-timeline-item__timestamp.is-bottom) { |
|||
white-space: pre-wrap; /* 保留空格和换行符,允许自动换行 */ |
|||
word-wrap: break-word; /* 允许长单词或URL强制断行 */ |
|||
overflow-wrap: break-word; |
|||
} |
|||
.mask-tag { |
|||
max-width: 100%; |
|||
height: fit-content; |
|||
padding: 5px; |
|||
white-space: pre-wrap; |
|||
} |
|||
</style> |
@ -0,0 +1,7 @@ |
|||
export default { |
|||
props: ['row', 'render', 'index', 'column'], |
|||
inheritAttrs: false, |
|||
setup(props: any) { |
|||
return () => props.render(props.row) |
|||
}, |
|||
} |
@ -0,0 +1,216 @@ |
|||
<script setup lang="ts"> |
|||
import type { VNode } from 'vue' |
|||
import { onMounted, reactive } from 'vue' |
|||
import Expand from './expand' |
|||
|
|||
defineOptions({ |
|||
name: 'FtTable', |
|||
}) |
|||
const props = withDefaults(defineProps<TableProp>(), { |
|||
columns: () => [], |
|||
mustInit: true, |
|||
hasHeader: false, |
|||
hasPage: false, |
|||
searchList: () => [], |
|||
getDataFn: () => Promise.resolve([]), |
|||
}) |
|||
const emits = defineEmits([ |
|||
'add', |
|||
'edit', |
|||
'detail', |
|||
'copy', |
|||
'sort', |
|||
'save', |
|||
'cancel', |
|||
'del', |
|||
'import', |
|||
'export', |
|||
'clickTreeNode', |
|||
'newTreeNode', |
|||
'editTreeNode', |
|||
'delTreeNode', |
|||
'rowClick', |
|||
'rowDblclick', |
|||
]) |
|||
enum ColumnType { |
|||
index = 'index', |
|||
selection = 'selection', |
|||
expand = 'expand', |
|||
} |
|||
interface TableColumn { |
|||
title: string |
|||
key: string |
|||
type?: ColumnType |
|||
width?: number // 列宽 |
|||
fixed?: 'left' | 'right' | undefined // 是否固定列 |
|||
render?: (row: any) => VNode // 内容自定义 |
|||
selectable?: (row: any) => boolean |
|||
} |
|||
|
|||
interface Btn { |
|||
name: string |
|||
icon?: string |
|||
type?: string |
|||
serverUrl: string |
|||
serverCondition?: 0 | 1 | 2 |
|||
} |
|||
|
|||
interface Search { |
|||
name: string |
|||
icon?: string |
|||
key?: string |
|||
type?: string |
|||
serverUrl: string |
|||
} |
|||
|
|||
interface TableProp { |
|||
columns?: TableColumn[] |
|||
getDataFn?: (params: any) => Promise<any> // 表格数据的接口 |
|||
mustInit?: boolean // 是否在mounted里执行getDataFn |
|||
hasHeader?: boolean |
|||
hasPage?: boolean |
|||
btnList?: Btn[] |
|||
searchList?: Search[] |
|||
} |
|||
|
|||
async function methodParent(fn: any) { |
|||
const newFn = fn[0] === '/' ? fn.slice(1) : fn |
|||
emits(newFn as never, state.selectedRows) |
|||
} |
|||
|
|||
onMounted(() => { |
|||
if (props.mustInit) { |
|||
initData() |
|||
} |
|||
}) |
|||
|
|||
const state = reactive({ |
|||
loading: false, |
|||
dataTotal: 0, |
|||
tableData: [], |
|||
selectedRows: [], |
|||
filterObj: { pageSize: 10, pageNum: 1 }, |
|||
}) |
|||
|
|||
function initData() { |
|||
state.loading = true |
|||
props |
|||
.getDataFn(state.filterObj) |
|||
.then((data) => { |
|||
console.log(data) |
|||
state.tableData = props.hasPage ? data.list : data |
|||
state.dataTotal = data.total |
|||
}) |
|||
.finally(() => { |
|||
state.loading = false |
|||
}) |
|||
} |
|||
const handleSelectionChange = (val: any) => { |
|||
state.selectedRows = val |
|||
} |
|||
defineExpose({ |
|||
initData, |
|||
}) |
|||
</script> |
|||
|
|||
<template> |
|||
<div class="ft-table"> |
|||
<div v-if="hasHeader" class="header"> |
|||
<div v-for="search in searchList" :key="search.key" class="search"> |
|||
<el-input v-model="state.filterObj[search.key]" :placeholder="search.title" clearable> |
|||
<template #append> |
|||
<el-icon class="el-input__icon" @click="initData"> |
|||
<Search /> |
|||
</el-icon> |
|||
</template> |
|||
</el-input> |
|||
</div> |
|||
<div v-for="btn in btnList" :key="btn.serverUrl"> |
|||
<ft-button |
|||
:icon="btn.icon" |
|||
:type="btn.type" |
|||
:disabled=" |
|||
!btn.serverCondition |
|||
? false |
|||
: btn.serverCondition === 1 |
|||
? state.selectedRows.length !== 1 |
|||
: state.selectedRows.length < 1 |
|||
" |
|||
@click="methodParent(btn.serverUrl)" |
|||
> |
|||
{{ btn.name }} |
|||
</ft-button> |
|||
</div> |
|||
</div> |
|||
<div class="table-main"> |
|||
<el-table |
|||
v-loading="state.loading" |
|||
:="$attrs" |
|||
:data="state.tableData" |
|||
style="width: 100%" |
|||
height="100%" |
|||
:highlight-current-row="true" |
|||
class="container-table" |
|||
header-row-class-name="header-row-class" |
|||
v-on="$attrs" |
|||
@selection-change="handleSelectionChange" |
|||
> |
|||
<template v-for="(column, index) in columns" :key="column.key"> |
|||
<el-table-column |
|||
show-overflow-tooltip |
|||
:prop="column.key" |
|||
:label="column.title" |
|||
:width="column.width" |
|||
:type="column.type" |
|||
:fixed="column.fixed" |
|||
:selectable="column.selectable" |
|||
> |
|||
<template v-if="column.render" #default="scope"> |
|||
<Expand :column="column" :row="scope.row" :render="column.render" :index="index" /> |
|||
</template> |
|||
</el-table-column> |
|||
</template> |
|||
</el-table> |
|||
</div> |
|||
<div v-if="hasPage" class="table-page"> |
|||
<el-pagination v-model:current-page="state.filterObj.pageNum" v-model:page-size="state.filterObj.pageSize" size="small" background layout="sizes, prev, pager, next" :total="state.dataTotal" @change="initData" /> |
|||
</div> |
|||
</div> |
|||
</template> |
|||
|
|||
<style lang="scss" scoped> |
|||
.ft-table { |
|||
width: 100%; |
|||
height: 100%; |
|||
display: flex; |
|||
flex-direction: column; |
|||
} |
|||
.header { |
|||
display: flex; |
|||
justify-content: flex-start; |
|||
align-items: center; |
|||
margin-bottom: 10px; |
|||
.search { |
|||
width: 200px; |
|||
} |
|||
} |
|||
.ft-button { |
|||
margin: 0 5px; |
|||
} |
|||
.table-main { |
|||
flex: 1; |
|||
overflow: auto; |
|||
|
|||
} |
|||
.table-page { |
|||
margin-top: 10px; |
|||
display: flex; |
|||
justify-content: flex-end; |
|||
} |
|||
:deep(.header-row-class) { |
|||
th { |
|||
background-color: rgba(0,0,0,0.02 ) !important; |
|||
color: rgba(0, 0, 0, 0.85) |
|||
} |
|||
} |
|||
</style> |
@ -0,0 +1,235 @@ |
|||
<script lang="ts" setup> |
|||
import { ElMessageBox } from 'element-plus' |
|||
import { FtMessage } from 'libs/message' |
|||
import { computed, ref } from 'vue' |
|||
import { useRouter } from 'vue-router' |
|||
import { updateContainer } from '@/apis/container' |
|||
import { useSolutionStore } from '@/stores/useSolutionStore' |
|||
|
|||
const props = defineProps({ |
|||
itemIndex: { |
|||
type: Number, |
|||
required: true, |
|||
}, |
|||
solutionItem: { |
|||
type: Object, |
|||
default: () => {}, |
|||
}, |
|||
}) |
|||
|
|||
const emits = defineEmits<{ |
|||
(e: 'ok'): void |
|||
}>() |
|||
|
|||
const router = useRouter() |
|||
const visible = ref(false) |
|||
const solutionStore = useSolutionStore() |
|||
const solutionId = ref() |
|||
const selectedSolutionItem = ref() |
|||
const solutionInfo = ref(props.solutionItem) |
|||
const solutionStyle = computed(() => { |
|||
const difference = (solutionInfo.value.capacityTotal - solutionInfo.value.capacityUsed) / solutionInfo.value.capacityTotal |
|||
const process = 100 - (difference * 100) |
|||
const filter = difference > 0.4 ? 'hue-rotate(270deg) saturate(100) brightness(81%)' : difference > 0.1 ? 'hue-rotate(150deg) saturate(100)' : 'hue-rotate(120deg) saturate(100)' |
|||
return { |
|||
'filter': filter, |
|||
'-webkit-mask': `linear-gradient(to bottom, transparent ${process}%, #EEEFF8 ${process + 0.1}%)`, |
|||
} |
|||
}) |
|||
const onInputBlur = async () => { |
|||
if (solutionInfo.value.capacityUsed > solutionInfo.value.capacityTotal) { |
|||
FtMessage.error(`容器容量不能超过${solutionInfo.value.capacityTotal}mL`) |
|||
solutionInfo.value.capacityUsed = 5000 |
|||
return |
|||
} |
|||
await ElMessageBox.confirm( |
|||
'确认保存吗? ', |
|||
'提示', |
|||
{ |
|||
confirmButtonText: '确认', |
|||
showClose: false, |
|||
showCancelButton: true, |
|||
closeOnClickModal: false, |
|||
closeOnPressEscape: false, |
|||
type: 'warning', |
|||
}, |
|||
) |
|||
await saveContainer() |
|||
} |
|||
|
|||
const saveContainer = async () => { |
|||
if (!solutionInfo.value.capacityUsed) { |
|||
FtMessage.warning('请输入当前容量') |
|||
return |
|||
} |
|||
if (!solutionInfo.value.solutionId) { |
|||
FtMessage.warning('请选择酸液') |
|||
return |
|||
} |
|||
const params: Container.ContainerItem = { |
|||
id: solutionInfo.value.id, |
|||
type: 0, |
|||
solutionId: solutionInfo.value.solutionId, |
|||
pumpId: solutionInfo.value.pumpId, |
|||
capacityTotal: solutionInfo.value.capacityTotal, |
|||
capacityUsed: solutionInfo.value.capacityUsed, |
|||
filled: solutionInfo.value.filled, |
|||
} |
|||
await updateContainer(params) |
|||
FtMessage.success('保存成功') |
|||
emits('ok') |
|||
} |
|||
const onSolutionChange = (value: number) => { |
|||
if (value) { |
|||
solutionId.value = value |
|||
solutionInfo.value.solutionId = value |
|||
selectedSolutionItem.value = solutionStore.solutionList.filter(item => item.id === value)[0] |
|||
} |
|||
} |
|||
const onClose = () => { |
|||
solutionId.value = null |
|||
visible.value = false |
|||
} |
|||
const onSubmitSolution = () => { |
|||
if (!solutionStore.solutionList.length) { |
|||
// 跳转至配置酸液页面 |
|||
router.push('/solution') |
|||
return |
|||
} |
|||
solutionInfo.value.solutionName = selectedSolutionItem.value.name |
|||
solutionInfo.value.solutionId = selectedSolutionItem.value.id |
|||
saveContainer() |
|||
onClose() |
|||
} |
|||
</script> |
|||
|
|||
<template> |
|||
<div class="liquied-item"> |
|||
<div class="header"> |
|||
<div class="solution-select"> |
|||
<div class="solution-name"> |
|||
<span v-if="solutionInfo.solutionName">{{ solutionInfo.solutionName }}</span> |
|||
<span v-else style="color:#d2d2d2">选择酸液</span> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="content"> |
|||
<div class="bottle_base"> |
|||
<img class="content-img" src="@/assets/images/liquied/liquied_bottle.svg" alt="chemical-bottle"> |
|||
</div> |
|||
<div class="bottle" :style="solutionStyle"> |
|||
<img class="content-img" src="@/assets/images/liquied/liquied_bottle.svg" alt="chemical-bottle"> |
|||
</div> |
|||
</div> |
|||
<div class="footer"> |
|||
<div class="footer-content"> |
|||
<span>{{ solutionInfo.capacityTotal }}mL</span> |
|||
</div> |
|||
<div class="footer-edit"> |
|||
<span>已使用</span> |
|||
<el-input v-model="solutionInfo.capacityUsed" type="number" @blur="onInputBlur"> |
|||
<template #append> |
|||
mL |
|||
</template> |
|||
</el-input> |
|||
</div> |
|||
</div> |
|||
<FtDialog v-model="visible" title="选择酸液" :ok-handle="onSubmitSolution" @cancel="onClose"> |
|||
<div v-if="solutionStore.solutionList.length"> |
|||
<el-radio-group v-model="solutionId" size="large" class="radio-group" @change="onSolutionChange"> |
|||
<el-radio-button v-for="item in solutionStore.solutionList" :key="item.id" :label="item.name" :value="item.id" class="radio-marge" /> |
|||
</el-radio-group> |
|||
</div> |
|||
<div v-else> |
|||
<el-empty description="description"> |
|||
<template #description> |
|||
未添加酸液,请在溶液管理中配置 |
|||
</template> |
|||
</el-empty> |
|||
</div> |
|||
</FtDialog> |
|||
</div> |
|||
</template> |
|||
|
|||
<style scoped> |
|||
.liquied-item { |
|||
border: 1px solid #ccc; |
|||
border-radius: 10px; |
|||
width: 200px; |
|||
height: 300px; |
|||
text-align: center; |
|||
display: flex; |
|||
flex-direction: column; |
|||
align-items: center; |
|||
justify-content: space-between; |
|||
} |
|||
.content{ |
|||
position: relative; |
|||
display: flex; |
|||
justify-content: center; |
|||
} |
|||
.bottle_base{ |
|||
position: relative; |
|||
} |
|||
.bottle { |
|||
position: absolute; |
|||
} |
|||
.header { |
|||
display: flex; |
|||
align-items: center; |
|||
background: rgba(25, 137, 250, 0.1216); |
|||
justify-content: center; |
|||
border-radius: 10px 10px 0 0; |
|||
padding:10px; |
|||
width: 100%; |
|||
} |
|||
.content-img{ |
|||
height: 150px; |
|||
margin: 10px; |
|||
} |
|||
.footer-content{ |
|||
display: flex; |
|||
justify-content: center; |
|||
} |
|||
.footer-edit { |
|||
display: flex; |
|||
justify-content: space-around; |
|||
align-items: center; |
|||
padding: 10px 0; |
|||
.el-input { |
|||
margin-left: 10px; |
|||
width: 120px; |
|||
} |
|||
} |
|||
.checked { |
|||
text-decoration: line-through; |
|||
} |
|||
.solution-select{ |
|||
display:flex; |
|||
width: 120px; |
|||
height: 25px; |
|||
text-align: center; |
|||
margin-left: 5px; |
|||
align-items: center; |
|||
border-radius: 5px; |
|||
} |
|||
.solution-name{ |
|||
width: 100px; |
|||
height: 25px; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
} |
|||
.add-icon{ |
|||
margin-right: -4px; |
|||
} |
|||
.button-icon{ |
|||
height: 25px; |
|||
width: 25px; |
|||
margin: 3px; |
|||
} |
|||
.radio-marge{ |
|||
margin: 10px; |
|||
border: 1px solid #e0e0e0; |
|||
} |
|||
</style> |
@ -0,0 +1,391 @@ |
|||
<script setup lang="ts"> |
|||
import { getContainerList } from 'apis/container' |
|||
import { createCraft, updateCraft } from 'apis/crafts' |
|||
import { getSolsList } from 'apis/solution' |
|||
import { configList } from 'apis/system' |
|||
import emptyIcon from 'assets/images/empty.svg' |
|||
import { FtMessage } from 'libs/message' |
|||
import { allPropertiesDefined } from 'libs/utils' |
|||
import { cloneDeep } from 'lodash' |
|||
import { computed, onMounted, ref } from 'vue' |
|||
|
|||
const props = defineProps({ |
|||
sourceData: { |
|||
type: Object, |
|||
default: () => ({}), |
|||
}, |
|||
}) |
|||
const emits = defineEmits(['ok', 'cancel']) |
|||
|
|||
const containerList = ref<Container.ContainerItem[]>([]) |
|||
const solutionList = ref<Solution.SolutionItem[]>([]) |
|||
const configData = ref<any[]>([]) |
|||
|
|||
onMounted(async () => { |
|||
containerList.value = await getContainerList() |
|||
solutionList.value = (await getSolsList()).list |
|||
configData.value = await configList() |
|||
if (props.sourceData) { |
|||
form.value = { ...props.sourceData, stepList: JSON.parse(props.sourceData.steps || '[]') } |
|||
form.value.stepList.forEach((step: any) => { |
|||
if (step.params.time) { |
|||
step.params.minutes = Math.floor(step.params.time / 60) |
|||
step.params.seconds = step.params.time % 60 |
|||
} |
|||
}) |
|||
} |
|||
}) |
|||
|
|||
const heatMax = computed(() => { |
|||
return configData.value.find(item => item.code === 'heat_temperature')?.value || 0 |
|||
}) |
|||
const dryMax = computed(() => { |
|||
return configData.value.find(item => item.code === 'dry_temperature')?.value || 0 |
|||
}) |
|||
const annealMax = computed(() => { |
|||
return configData.value.find(item => item.code === 'anneal_temperature')?.value || 0 |
|||
}) |
|||
|
|||
const heightMax = computed(() => { |
|||
return configData.value.find(item => item.code === 'needle_drop_height')?.value || 0 |
|||
}) |
|||
|
|||
const form = ref({ |
|||
stepList: [], |
|||
}) |
|||
const formRef = ref() |
|||
|
|||
const rules = { |
|||
name: [{ required: true, trigger: 'blur', message: '请输入工艺名称' }], |
|||
} |
|||
|
|||
const okHandle = async () => { |
|||
try { |
|||
const valid = await formRef.value.validate() |
|||
if (!valid) { |
|||
return |
|||
} |
|||
// 找到第一个参数不完整的步骤 |
|||
const errorMsg: string[] = [] |
|||
const invalidStepIndex = form.value.stepList.findIndex((step: any, index) => { |
|||
if (['heat', 'dry', 'anneal'].includes(step.method)) { |
|||
if (step.params.minutes || step.params.seconds) { |
|||
step.params.time = (step.params.minutes || 0) * 60 + (step.params.seconds || 0) || undefined |
|||
} |
|||
else { |
|||
step.params.time = undefined |
|||
} |
|||
if (+step.params.temperature > +annealMax.value && step.method === 'anneal') { |
|||
errorMsg.push(`步骤${index + 1}: 退火温度不能超过${annealMax.value}℃`) |
|||
} |
|||
if (+step.params.temperature > +heatMax.value && step.method === 'heat') { |
|||
errorMsg.push(`步骤${index + 1}: 加热温度不能超过${heatMax.value}℃`) |
|||
} |
|||
if (+step.params.temperature > +dryMax.value && step.method === 'dry') { |
|||
errorMsg.push(`步骤${index + 1}: 烘干温度不能超过${dryMax.value}℃`) |
|||
} |
|||
} |
|||
if (['clean', 'reduceLiquid', 'addThick'].includes(step.method)) { |
|||
if (+step.params.height > +heightMax.value) { |
|||
errorMsg.push(`步骤${index + 1}: 针头高度不能超过${heightMax.value}mm`) |
|||
} |
|||
} |
|||
step.params.description = `${index + 1}.` |
|||
switch (step.method) { |
|||
case 'clean': |
|||
step.params.description += `针头高度${step.params.height}mm, 加${step.params.volume}ml水清洗${step.params.cycle}次` |
|||
break |
|||
case 'addThin': |
|||
step.params.description += `添加稀硝酸 ${step.params.volume}mL` |
|||
break |
|||
case 'addThick': |
|||
step.params.description += `添加浓硝酸 ${step.params.volume}mL` |
|||
break |
|||
case 'reduceLiquid': |
|||
step.params.description += `针头高度${step.params.height}mm抽取液体` |
|||
break |
|||
case 'preHeat': |
|||
step.params.description += `${step.params.type === 'heat' ? '加热区' : '退火区'}预热到${step.params.temperature}度` |
|||
break |
|||
case 'heat': |
|||
step.params.description += `加热: ${step.params.temperature}度, 保持${step.params.minutes || 0}分${step.params.seconds || 0}秒` |
|||
break |
|||
case 'dry': |
|||
step.params.description += `烘干: ${step.params.temperature}度, 保持${step.params.minutes || 0}分${step.params.seconds || 0}秒` |
|||
break |
|||
case 'anneal': |
|||
step.params.description += `退火: ${step.params.temperature}度, 保持${step.params.minutes || 0}分${step.params.seconds || 0}秒` |
|||
break |
|||
} |
|||
return !allPropertiesDefined(step.params, ['minutes', 'seconds', 'description']) |
|||
}) |
|||
console.log(form.value) |
|||
|
|||
if (invalidStepIndex !== -1) { |
|||
FtMessage.error(`步骤${invalidStepIndex + 1}: 请填写完整参数`) |
|||
return |
|||
} |
|||
if (errorMsg.length) { |
|||
FtMessage.error(errorMsg.join('; ')) |
|||
return |
|||
} |
|||
|
|||
form.value.steps = JSON.stringify(form.value.stepList) |
|||
if (form.value.id) { |
|||
await updateCraft(form.value) |
|||
} |
|||
else { |
|||
await createCraft(form.value) |
|||
} |
|||
FtMessage.success('保存成功') |
|||
emits('ok') |
|||
} |
|||
catch (error) { |
|||
console.log(error) |
|||
} |
|||
} |
|||
const cancel = () => { |
|||
emits('cancel') |
|||
} |
|||
|
|||
const stepMap = { |
|||
preHeat: { name: '预热', method: 'preHeat', params: { type: 'heat', temperature: undefined, description: undefined } }, |
|||
addThin: { |
|||
name: '加稀硝酸', |
|||
method: 'addThin', |
|||
params: { volume: undefined, description: undefined }, |
|||
}, |
|||
addThick: { |
|||
name: '加浓硝酸', |
|||
method: 'addThick', |
|||
params: { height: undefined, volume: undefined, description: undefined }, |
|||
}, |
|||
heat: { |
|||
name: '加热', |
|||
method: 'heat', |
|||
params: { temperature: undefined, time: undefined, description: undefined, minutes: undefined, seconds: undefined }, |
|||
}, |
|||
// reduceLiquid: { name: '抽液', method: 'reduceLiquid', params: { height: undefined, description: undefined } }, |
|||
clean: { |
|||
name: '清洗', |
|||
method: 'clean', |
|||
params: { cycle: undefined, height: undefined, volume: 2, description: undefined }, |
|||
}, |
|||
dry: { |
|||
name: '烘干', |
|||
method: 'dry', |
|||
params: { temperature: undefined, time: undefined, description: undefined, minutes: undefined, seconds: undefined }, |
|||
}, |
|||
anneal: { |
|||
name: '退火', |
|||
method: 'anneal', |
|||
params: { |
|||
temperature: undefined, |
|||
time: undefined, |
|||
description: undefined, |
|||
minutes: undefined, |
|||
seconds: undefined, |
|||
}, |
|||
}, |
|||
} |
|||
|
|||
const addStep = (data: any) => { |
|||
form.value.stepList.push(cloneDeep(data)) |
|||
} |
|||
</script> |
|||
|
|||
<template> |
|||
<FtDialog visible :title="form.id ? '编辑工艺' : '新增工艺'" width="80%" :ok-handle="okHandle" @cancel="cancel"> |
|||
<el-form ref="formRef" label-width="auto" :model="form" :rules="rules" class="form-box" label-position="left"> |
|||
<el-row :gutter="30"> |
|||
<el-col :span="10"> |
|||
<el-form-item label="工艺名称" prop="name"> |
|||
<el-input v-model="form.name" placeholder="请输入工艺名称" /> |
|||
</el-form-item> |
|||
<el-form-item label="步骤列表"> |
|||
<div class="button-content"> |
|||
<el-tag v-for="item in stepMap" :key="item" size="large" @click="() => addStep(item)"> |
|||
<div style="display: flex; align-items: center; justify-content: space-around; width: 100%"> |
|||
<el-icon><Plus /></el-icon> |
|||
<span> {{ item.name }}</span> |
|||
</div> |
|||
</el-tag> |
|||
</div> |
|||
</el-form-item> |
|||
</el-col> |
|||
<el-col :span="14"> |
|||
<div v-if="form.stepList.length" class="step-box"> |
|||
<div v-for="(item, index) in form.stepList" :key="index" class="step-item"> |
|||
<el-form-item :label="`${index + 1}: ${item.name}`"> |
|||
<template v-if="item.method === 'clean'"> |
|||
<el-input v-model="item.params.height" type="number" size="small" placeholder="请输入高度"> |
|||
<template #append> |
|||
mm |
|||
</template> |
|||
</el-input> |
|||
<el-input |
|||
v-model="item.params.volume" |
|||
style="width: 100px" |
|||
type="number" |
|||
size="small" |
|||
placeholder="请输入加水量" |
|||
> |
|||
<template #append> |
|||
mL |
|||
</template> |
|||
</el-input> |
|||
<el-input v-model="item.params.cycle" type="number" size="small" placeholder="请输入次数"> |
|||
<template #append> |
|||
次 |
|||
</template> |
|||
</el-input> |
|||
</template> |
|||
<template v-else-if="['addThin', 'addThick'].includes(item.method)"> |
|||
<el-input v-model="item.params.volume" type="number" size="small" placeholder="请输入容量"> |
|||
<template #append> |
|||
mL |
|||
</template> |
|||
</el-input> |
|||
<el-input v-if="item.method === 'addThick'" v-model="item.params.height" type="number" size="small" placeholder="请输入高度"> |
|||
<template #append> |
|||
mm |
|||
</template> |
|||
</el-input> |
|||
</template> |
|||
<template v-else-if="item.method === 'reduceLiquid'"> |
|||
<el-input v-model="item.params.height" type="number" size="small" placeholder="请输入高度"> |
|||
<template #append> |
|||
mm |
|||
</template> |
|||
</el-input> |
|||
</template> |
|||
<template v-else-if="item.method === 'preHeat'"> |
|||
<el-select v-model="item.params.type" size="small"> |
|||
<el-option label="加热区" value="heat" /> |
|||
<el-option label="退火区" value="anneal" /> |
|||
</el-select> |
|||
<el-input |
|||
v-model="item.params.temperature" |
|||
type="number" |
|||
size="small" |
|||
placeholder="请输入温度" |
|||
> |
|||
<template #append> |
|||
℃ |
|||
</template> |
|||
</el-input> |
|||
</template> |
|||
<template v-else-if="['heat', 'dry', 'anneal'].includes(item.method)"> |
|||
<el-input v-model="item.params.temperature" type="number" :max="item.method === 'anneal' ? 400 : 200" size="small" placeholder="加热温度"> |
|||
<template #append> |
|||
℃ |
|||
</template> |
|||
</el-input> |
|||
<el-select |
|||
v-model="item.params.minutes" |
|||
style="width: 70px" |
|||
clearable |
|||
size="small" |
|||
placeholder="请选择" |
|||
> |
|||
<el-option v-for="i in 60" :key="i" :label="i" :value="i" /> |
|||
</el-select> |
|||
<span class="unit-text">分</span> |
|||
<el-select |
|||
v-model="item.params.seconds" |
|||
style="width: 70px" |
|||
clearable |
|||
size="small" |
|||
placeholder="请选择" |
|||
> |
|||
<el-option v-for="i in 60" :key="i" :label="i" :value="i" /> |
|||
</el-select> |
|||
<span class="unit-text">秒</span> |
|||
</template> |
|||
<el-icon style="margin-left: auto" @click="() => form.stepList.splice(index, 1)"> |
|||
<Close /> |
|||
</el-icon> |
|||
</el-form-item> |
|||
</div> |
|||
</div> |
|||
<div v-else class="empty-box"> |
|||
<img :src="emptyIcon" alt=""> |
|||
<span>暂无步骤</span> |
|||
</div> |
|||
</el-col> |
|||
</el-row> |
|||
</el-form> |
|||
</FtDialog> |
|||
</template> |
|||
|
|||
<style scoped lang="scss"> |
|||
.form-box { |
|||
height: 50vh; |
|||
.el-row { |
|||
height: 100%; |
|||
.el-col:first-child { |
|||
border-right: 1px solid #eee; |
|||
} |
|||
.el-col { |
|||
height: 100%; |
|||
.step-box { |
|||
height: 100%; |
|||
overflow: auto; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
.button-content { |
|||
width: 100%; |
|||
display: grid; |
|||
grid-template-columns: repeat(2, 1fr); /* 创建3列等宽轨道 */ |
|||
grid-template-rows: repeat(4, auto); /* 创建2行自动高度 */ |
|||
gap: 20px; |
|||
} |
|||
:deep(.el-tag__content) { |
|||
width: 100%; |
|||
} |
|||
.empty-box { |
|||
width: 100%; |
|||
height: 100%; |
|||
display: flex; |
|||
flex-direction: column; |
|||
align-items: center; |
|||
justify-content: center; |
|||
color: #ccc; |
|||
img { |
|||
margin-bottom: 10px; |
|||
} |
|||
} |
|||
.step-item { |
|||
.el-form-item { |
|||
background: rgba(82, 148, 215, 0.06); |
|||
padding: 5px; |
|||
margin-bottom: 10px; |
|||
:deep(.el-form-item__label) { |
|||
height: 25px; |
|||
line-height: 25px; |
|||
} |
|||
:deep(.el-form-item__content) { |
|||
width: 100%; |
|||
display: flex; |
|||
align-items: center; |
|||
position: relative; |
|||
|
|||
.el-input, |
|||
.el-select { |
|||
width: 120px; |
|||
margin: 0 5px; |
|||
} |
|||
.info-box { |
|||
width: 80%; |
|||
height: 100%; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
.unit-text { |
|||
font-size: 12px; |
|||
line-height: 25px; |
|||
} |
|||
</style> |
@ -0,0 +1,285 @@ |
|||
<script lang="ts" setup> |
|||
import { ElMessage } from 'element-plus' |
|||
import { StepCmdDescMap } from 'views/craft/craft_constant' |
|||
import { ref } from 'vue' |
|||
import { useRoute } from 'vue-router' |
|||
import { createCraft, updateCraft } from '@/apis/crafts' |
|||
import FtButton from '../common/FTButton/index.vue' |
|||
import FTDialog from '../common/FTDialog/index.vue' |
|||
import TransferLeft from './TransferLeft.vue' |
|||
import TransferRight from './TransferRight.vue' |
|||
|
|||
const emit = defineEmits<{ |
|||
(e: 'ok'): void |
|||
}>() |
|||
|
|||
// const stepMap = { |
|||
// preHeat: { name: '预热', data: { method: 'preHeat', params: { temperature: undefined } } }, |
|||
// addLiquid: { name: '加液', data: { method: 'addLiquid', params: { volume: undefined, id: undefined } } }, |
|||
// reduceLiquid: { name: '抽液', data: { method: 'reduceLiquid', params: { volume: undefined, id: undefined } } }, |
|||
// heatStart: { name: '加热', data: { method: 'heatStart', params: { temperature: undefined, time: undefined } } }, |
|||
// fanStart: { name: '散热', data: { method: 'fanStart', params: { temperature: undefined } } }, |
|||
// } |
|||
|
|||
const visible = ref(false) |
|||
const stepStructs = ref<CraftTypes.StepStruct[]>([]) |
|||
const craftObj = ref<CraftTypes.Craft>({}) |
|||
const loading = ref(false) |
|||
const saveRef = ref<CraftTypes.SaveRef | null>(null) |
|||
const tempStepStructs = ref<CraftTypes.StepStruct[]>([]) |
|||
const route = useRoute() |
|||
const oresId: number = route.query.oreId as unknown as number |
|||
const stepCmds: CraftTypes.StepCmd[] = [ |
|||
// 'upTray', |
|||
// 'downTray', |
|||
'addLiquid', |
|||
// 'moveToSol', |
|||
// 'moveToHeat', |
|||
// 'shaking', |
|||
'startHeating', |
|||
// 'stopHeating', |
|||
// 'takePhoto', |
|||
// 'delay', |
|||
] |
|||
const openDialog = () => { |
|||
visible.value = true |
|||
} |
|||
|
|||
const closeDialog = () => { |
|||
stepStructs.value = [] |
|||
craftObj.value.name = '' |
|||
visible.value = false |
|||
} |
|||
|
|||
const editDialog = (craftInfo: CraftTypes.Craft) => { |
|||
craftObj.value = { ...craftInfo } |
|||
if (craftInfo && craftInfo.steps) { |
|||
const step = JSON.parse(craftInfo.steps) |
|||
console.log(craftInfo) |
|||
if (step && step.length) { |
|||
step.forEach((item: CraftTypes.StepStruct) => { |
|||
if (item.method === 'addLiquid') { |
|||
const list = item.params.tubeSolList |
|||
if (list && list.length === 16 && item.params.tubeSolList) { |
|||
item.params.tubeSolList = [{ |
|||
tubeNum: 0, |
|||
addLiquidList: item.params.tubeSolList[0].addLiquidList, |
|||
}] |
|||
} |
|||
} |
|||
}) |
|||
} |
|||
stepStructs.value = step |
|||
} |
|||
openDialog() |
|||
} |
|||
|
|||
function onConfirm() { |
|||
if (!craftObj.value.name) { |
|||
ElMessage.warning('请输入工艺名称') |
|||
return |
|||
} |
|||
const stepList = JSON.parse(JSON.stringify(stepStructs.value)) |
|||
for (const step of stepList) { |
|||
if (step.method === 'addLiquid') { |
|||
const list: CraftTypes.TubeSolStruct[] = step.params.tubeSolList |
|||
if (list && list.length) { |
|||
list.forEach((item) => { |
|||
const tubeNum = item.tubeNum |
|||
if (tubeNum === 0) { |
|||
const tubeSolList: CraftTypes.TubeSolStruct[] = [] |
|||
for (let index = 0; index < 16; index++) { |
|||
tubeSolList.push({ |
|||
tubeNum: index + 1, |
|||
addLiquidList: item.addLiquidList, |
|||
}) |
|||
} |
|||
step.params.tubeSolList = tubeSolList |
|||
} |
|||
}) |
|||
} |
|||
} |
|||
} |
|||
craftObj.value.steps = JSON.stringify(stepList) |
|||
confirmCraftEdit(craftObj.value) |
|||
} |
|||
|
|||
const confirmCraftEdit = (craft: CraftTypes.Craft) => { |
|||
let req |
|||
if (craft.id) { |
|||
req = updateCraft(craft) |
|||
} |
|||
else { |
|||
craft = { name: craft.name, steps: craft.steps, oresId } |
|||
req = createCraft(craft) |
|||
} |
|||
loading.value = true |
|||
saveRef.value && saveRef.value.setLoading(true) |
|||
req.then(() => { |
|||
saveRef.value && saveRef.value.setLoading(false) |
|||
ElMessage.success('保存成功') |
|||
emit('ok') |
|||
closeDialog() |
|||
}).catch(() => { |
|||
saveRef.value && saveRef.value.setLoading(false) |
|||
}) |
|||
} |
|||
|
|||
function addStep(step: CraftTypes.StepCmd) { |
|||
let st: CraftTypes.StepStruct |
|||
if (step === 'addLiquid') { |
|||
st = { |
|||
method: step, |
|||
params: { |
|||
tubeSolList: [{ |
|||
tubeNum: 0, |
|||
addLiquidList: [{ |
|||
solId: 1, |
|||
volume: 10, |
|||
}], |
|||
}], |
|||
}, |
|||
} |
|||
} |
|||
else if (step === 'startHeating') { |
|||
st = { |
|||
method: step, |
|||
params: { |
|||
temperature: 100, |
|||
second: 10, |
|||
}, |
|||
} |
|||
} |
|||
else if (step === 'delay') { |
|||
st = { |
|||
method: step, |
|||
params: { |
|||
second: 10, |
|||
}, |
|||
} |
|||
} |
|||
else if (step === 'shaking') { |
|||
st = { |
|||
method: step, |
|||
params: { |
|||
second: 30, |
|||
}, |
|||
} |
|||
} |
|||
else { |
|||
st = { |
|||
method: step, |
|||
} |
|||
} |
|||
stepStructs.value = [...stepStructs.value, st] |
|||
tempStepStructs.value = JSON.parse(JSON.stringify(stepStructs.value)) |
|||
} |
|||
|
|||
function onStepItemDel(order: number) { |
|||
stepStructs.value = stepStructs.value.filter((s, i) => i !== order - 1) |
|||
} |
|||
|
|||
function transferChange(stepData: CraftTypes.StepStruct, order: number) { |
|||
console.log('order === ', stepStructs.value, stepData, order) |
|||
} |
|||
|
|||
defineExpose({ |
|||
openDialog, |
|||
closeDialog, |
|||
editDialog, |
|||
}) |
|||
</script> |
|||
|
|||
<template> |
|||
<FTDialog v-model="visible" title="添加工艺" style="width:70vw; height:70vh"> |
|||
<div> |
|||
<div class="mt-5 mb-8 flex items-center"> |
|||
<label class="font-medium mr-4">工艺名称:</label> |
|||
<el-input |
|||
v-model.trim="craftObj.name" |
|||
style="width: 200px" |
|||
size="small" |
|||
type="text" |
|||
placeholder="请输入名称" |
|||
class="flex-auto bg-[#f6f6f6] h-11 leading-10 rounded-sm px-4" |
|||
/> |
|||
</div> |
|||
<div class="craft-title"> |
|||
<div> |
|||
工艺步骤 |
|||
</div> |
|||
<div class="title-right"> |
|||
选择的步骤 |
|||
</div> |
|||
</div> |
|||
<div class="step-content"> |
|||
<div class="transfer-left"> |
|||
<TransferLeft v-for="cmd in stepCmds" :key="cmd" :title="StepCmdDescMap[cmd]" @click="addStep(cmd)" /> |
|||
</div> |
|||
<div v-if="stepStructs && stepStructs.length" class="transfer-right"> |
|||
<TransferRight v-for="(step, idx) in stepStructs" :key="idx" :order="idx + 1" :step="step" type="add" @del="onStepItemDel" @transfer-change="transferChange" /> |
|||
</div> |
|||
<div v-else> |
|||
<el-empty description="description"> |
|||
<template #description> |
|||
未选择工艺步骤 |
|||
</template> |
|||
</el-empty> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<template #footer> |
|||
<div class="footer"> |
|||
<FtButton @click="closeDialog"> |
|||
取消 |
|||
</FtButton> |
|||
<FtButton ref="saveRef" type="primary" :loading="loading" :click-handle="onConfirm"> |
|||
确定 |
|||
</FtButton> |
|||
</div> |
|||
</template> |
|||
</FTDialog> |
|||
</template> |
|||
|
|||
<style lang="scss" scoped> |
|||
.transfer-left{ |
|||
display: grid; |
|||
grid-template-columns: 1fr 1fr; |
|||
gap: 10px; |
|||
width:150px; |
|||
place-items: center; |
|||
height: 3rem; |
|||
} |
|||
|
|||
.transfer-right{ |
|||
max-height: 40vh; |
|||
overflow: auto; |
|||
width:30rem; |
|||
} |
|||
|
|||
.step-content{ |
|||
margin-top:1rem; |
|||
display: grid; |
|||
grid-template-columns: 1fr 1fr; |
|||
height: 21rem; |
|||
} |
|||
.footer{ |
|||
display: flex; |
|||
height: 5rem; |
|||
justify-content: center; |
|||
align-items: center; |
|||
} |
|||
|
|||
.craft-title{ |
|||
display: grid; |
|||
grid-template-columns: 1fr 1fr; |
|||
div{ |
|||
font-size:12px; |
|||
margin-top: 5px; |
|||
} |
|||
.title-right{ |
|||
display: flex; |
|||
justify-content: center; |
|||
} |
|||
} |
|||
</style> |
@ -0,0 +1,257 @@ |
|||
<script lang="ts" setup> |
|||
import { ElMessage } from 'element-plus' |
|||
import { nextTick, ref } from 'vue' |
|||
import { craftstatus, craftstatusByHeatId, stopCraft } from '@/apis/crafts' |
|||
import TransferRight from './TransferRight.vue' |
|||
|
|||
const statusVisible = ref(false) |
|||
const byHeatIdTimes = ref() |
|||
const stateList = ref<CraftTypes.CraftState[]>([]) |
|||
const craftValue = ref() |
|||
const craftList = ref<CraftTypes.CraftState[]>([]) |
|||
const stateMap: Record<string, string> = { |
|||
RUNNING: '执行中', |
|||
FINISHED: '执行完成', |
|||
ERROR: '执行失败', |
|||
} |
|||
|
|||
const heatModuleMap: Record<string, string> = { |
|||
heat_module_01: '加热区01', |
|||
heat_module_02: '加热区02', |
|||
heat_module_03: '加热区03', |
|||
heat_module_04: '加热区04', |
|||
heat_module_05: '加热区05', |
|||
heat_module_06: '加热区06', |
|||
} |
|||
// const queryCraftStatus = () => { |
|||
// intervalTimes.value = setInterval(() => { |
|||
// craftstatus().then((res) => { |
|||
// if (res && res.length === 0) { |
|||
// clearInterval(intervalTimes.value) |
|||
// return |
|||
// } |
|||
// const craftStateList = res |
|||
// const uniqueData: CraftTypes.CraftState[] = [] |
|||
// const idMap: Record<string | number, string | boolean> = {} |
|||
// res.forEach((item) => { |
|||
// const craftsId = item.craftsId |
|||
// if (!idMap[craftsId]) { |
|||
// idMap[craftsId] = true |
|||
// uniqueData.push(item) |
|||
// } |
|||
// }) |
|||
|
|||
// craftList.value = uniqueData |
|||
// if (uniqueData.length) { |
|||
// const craftsId = uniqueData[0].craftsId |
|||
// craftValue.value = craftsId |
|||
// onSelectChange(craftsId) |
|||
// } |
|||
|
|||
// stateList.value.push(...uniqueData) |
|||
// if (craftStateList && craftStateList.length) { |
|||
// const finishedList = [] |
|||
// uniqueData.forEach((item) => { |
|||
// const state = item.state |
|||
// if (state === 'FINISHED') { |
|||
// finishedList.push(state) |
|||
// } |
|||
// }) |
|||
// if (finishedList.length === uniqueData.length) { |
|||
// clearInterval(intervalTimes.value) |
|||
// } |
|||
// } |
|||
// }) |
|||
// }, 1000) |
|||
// } |
|||
const loading = ref(false) |
|||
const queryCraftStatus = () => { |
|||
loading.value = true |
|||
craftstatus().then((res) => { |
|||
loading.value = false |
|||
if (res && res.length) { |
|||
const uniqueData: CraftTypes.CraftState[] = [] |
|||
const idMap: Record<string | number, string | boolean> = {} |
|||
res.forEach((item) => { |
|||
const craftsId = item.craftsId |
|||
if (!idMap[craftsId]) { |
|||
idMap[craftsId] = true |
|||
uniqueData.push(item) |
|||
} |
|||
}) |
|||
craftList.value = uniqueData |
|||
queryCraftStatusByHeatId(res[0].heatId) |
|||
} |
|||
}) |
|||
} |
|||
|
|||
const queryCraftStatusByHeatId = (heatId: string) => { |
|||
byHeatIdTimes.value = setInterval(() => { |
|||
craftstatusByHeatId(heatId).then((res) => { |
|||
currentSteps.value = res.steps |
|||
const craftsId = res.craftsId |
|||
craftValue.value = craftsId |
|||
stateList.value.push(res) |
|||
if (res.state === 'FINISHED' || res.state === 'ERROR') { |
|||
clearInterval(byHeatIdTimes.value) |
|||
} |
|||
onHandleSteps(res.steps) |
|||
scrollToBottom() |
|||
}) |
|||
}, 1000) |
|||
} |
|||
|
|||
const showDialog = () => { |
|||
statusVisible.value = true |
|||
queryCraftStatus() |
|||
} |
|||
|
|||
const onCloseDialog = () => { |
|||
stateList.value = [] |
|||
} |
|||
|
|||
const currentSteps = ref<CraftTypes.StepStruct[]>([]) |
|||
const currentHeatId = ref() |
|||
const onSelectChange = (value: string | number) => { |
|||
stateList.value = [] |
|||
currentHeatId.value = value |
|||
const list = craftList.value.filter(item => item.craftsId === value) |
|||
list.forEach((item) => { |
|||
const steps = item.steps |
|||
clearInterval(byHeatIdTimes.value) |
|||
queryCraftStatusByHeatId(item.heatId) |
|||
onHandleSteps(steps) |
|||
currentSteps.value = [] |
|||
nextTick(() => { |
|||
currentSteps.value = steps |
|||
}) |
|||
}) |
|||
} |
|||
const onHandleSteps = (steps: CraftTypes.StepStruct[]) => { |
|||
if (!steps) { |
|||
return |
|||
} |
|||
steps.forEach((item) => { |
|||
if (item.method === 'addLiquid') { |
|||
const list = item.params.tubeSolList |
|||
if (list && list.length === 16 && item.params.tubeSolList) { |
|||
item.params.tubeSolList = [{ |
|||
tubeNum: 0, |
|||
addLiquidList: item.params.tubeSolList[0].addLiquidList, |
|||
}] |
|||
} |
|||
} |
|||
}) |
|||
} |
|||
|
|||
const statusAreaRef = ref<HTMLElement | null>(null) |
|||
const scrollToBottom = () => { |
|||
setTimeout(() => { |
|||
if (statusAreaRef.value) { |
|||
statusAreaRef.value.scrollTop = statusAreaRef.value.scrollHeight |
|||
} |
|||
}, 100) |
|||
} |
|||
|
|||
const onStopCraft = () => { |
|||
stopCraft({ heatId: currentHeatId.value }).then(() => { |
|||
clearInterval(byHeatIdTimes.value) |
|||
ElMessage.success('工艺已停止') |
|||
}) |
|||
} |
|||
|
|||
defineExpose({ |
|||
showDialog, |
|||
}) |
|||
</script> |
|||
|
|||
<template> |
|||
<el-dialog v-model="statusVisible" style="width:80vw; height:60vh" :close-on-click-modal="false" @close="onCloseDialog"> |
|||
<div class="state-container"> |
|||
<div class="item1"> |
|||
<div class="state-left" style="width:40vw"> |
|||
<div>工艺列表</div> |
|||
<div style="margin-left:10px"> |
|||
<el-select v-model="craftValue" style="width: 100px" size="small" placeholder="请选择" @change="onSelectChange"> |
|||
<el-option v-for="item in craftList" :key="item.craftsId" :label="item.craftsName" :value="item.craftsId" /> |
|||
</el-select> |
|||
<FtButton size="small" style="margin-left:10px" @click="onStopCraft"> |
|||
停止工艺 |
|||
</FtButton> |
|||
</div> |
|||
</div> |
|||
<div v-if="currentSteps.length" style="margin-top: 10px"> |
|||
<TransferRight v-for="(step, idx) in currentSteps" :key="idx" :order="idx + 1" :step="step" type="showlog" @del="() => {}" @transfer-change="() => {}" /> |
|||
</div> |
|||
<div v-else class="state-log"> |
|||
<el-empty description="description" style="height:30vh"> |
|||
<template #description> |
|||
<span style="color:#c2c2c2">没有正在执行的工艺</span> |
|||
</template> |
|||
</el-empty> |
|||
</div> |
|||
</div> |
|||
<div v-loading="loading" class="item2"> |
|||
<div>工艺执行状态</div> |
|||
<div v-if="stateList.length" ref="statusAreaRef" class="state-log state-back"> |
|||
<div v-for="item in stateList" :key="item.state"> |
|||
<span class="state-span"><label class="state-log-label">矿石名称:</label><span class="state-log-text">{{ item.oresName }}</span></span> |
|||
<span class="state-span"><label class="state-log-label">工艺名称:</label><span class="state-log-text">{{ item.craftsName }}</span></span> |
|||
<span class="state-span"><label class="state-log-label">加热区:</label><span class="state-log-text">{{ heatModuleMap[item.heatId] }}</span></span> |
|||
<span class="state-span"><label class="state-log-label">执行状态:</label><span class="state-log-text">{{ stateMap[item.state] }}</span></span> |
|||
</div> |
|||
</div> |
|||
<div v-else class="state-log"> |
|||
<el-empty description="description" style="height:30vh"> |
|||
<template #description> |
|||
<span style="color:#c2c2c2">没有正在执行的工艺</span> |
|||
</template> |
|||
</el-empty> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</el-dialog> |
|||
</template> |
|||
|
|||
<style lang="scss" scoped> |
|||
.state-container{ |
|||
display: grid; |
|||
grid-template-columns: repeat(3, 1fr); |
|||
.state-left{ |
|||
display: flex; |
|||
justify-content: center; |
|||
align-items: center; |
|||
} |
|||
|
|||
.item1 { |
|||
grid-column: 1 / 2; |
|||
} |
|||
.item2 { |
|||
grid-column: 2 / 4; |
|||
} |
|||
} |
|||
.state-log{ |
|||
height: 50vh; |
|||
max-height: 50vh; |
|||
overflow: auto; |
|||
padding: 10px; |
|||
} |
|||
.state-back{ |
|||
background: #0d0d0d; |
|||
color: #0f0; |
|||
border-radius: 10px; |
|||
overflow-y: auto; |
|||
margin-bottom: 10px; |
|||
} |
|||
.state-span{ |
|||
padding-left: 10px; |
|||
} |
|||
.state-log-label{ |
|||
font-weight: 600; |
|||
font-size: 10px; |
|||
color: rgb(217, 180, 69); |
|||
} |
|||
.state-log-text{ |
|||
font-size: 10px; |
|||
} |
|||
</style> |
@ -0,0 +1,40 @@ |
|||
<script lang="ts" setup> |
|||
defineProps<{ title: string }>() |
|||
</script> |
|||
|
|||
<template> |
|||
<div class="transfer-left btn-light"> |
|||
{{ title }} |
|||
<img |
|||
src="@/assets/images/icon_add_s.svg" |
|||
alt="add" |
|||
class="add_icon" |
|||
> |
|||
</div> |
|||
</template> |
|||
|
|||
<style lang="scss" scoped> |
|||
.transfer-left{ |
|||
display: flex; |
|||
} |
|||
|
|||
.btn-light{ |
|||
display: flex; |
|||
justify-content: center; |
|||
align-items: center; |
|||
background-color: transparent; |
|||
border-radius: 4px; |
|||
color: '#1989fa'; |
|||
border: solid 1px '#1989fa'; |
|||
height: 11px; |
|||
} |
|||
.add_icon { |
|||
right: 1rem; |
|||
width: .875rem; |
|||
} |
|||
@media (min-width: 56.25rem) { |
|||
.add_icon { |
|||
right: 1.875rem; |
|||
} |
|||
} |
|||
</style> |
@ -0,0 +1,161 @@ |
|||
<script lang="ts" setup> |
|||
import { onMounted, ref, watch } from 'vue' |
|||
import { getContainerList } from '@/apis/container' |
|||
import { getSolsList } from '@/apis/solution' |
|||
import { useSolutionStore } from '@/stores/useSolutionStore' |
|||
import { StepCmdDescMap } from '../../views/craft/craft_constant' |
|||
|
|||
const props = defineProps<{ |
|||
order: number |
|||
step: CraftTypes.StepStruct |
|||
type: string |
|||
}>() |
|||
|
|||
const $emit = defineEmits<{ |
|||
(e: 'del', order: number): void |
|||
(e: 'transferChange', stepData: CraftTypes.StepStruct, order: number): void |
|||
}>() |
|||
|
|||
const solutionStore = useSolutionStore() |
|||
const stepInfo = ref(props.step) |
|||
onMounted(() => { |
|||
querySolutionList() |
|||
}) |
|||
|
|||
watch(stepInfo, (newVal) => { |
|||
$emit('transferChange', newVal, props.order) |
|||
}, { |
|||
deep: true, |
|||
}) |
|||
|
|||
const solutionList = ref<Solution.SolutionItem[]>([]) |
|||
const querySolutionList = () => { |
|||
getSolsList().then((res) => { |
|||
if (res && res.list) { |
|||
solutionList.value = res.list |
|||
solutionStore.updateSolution(res.list) |
|||
queryContainerList() |
|||
} |
|||
}) |
|||
} |
|||
|
|||
const containerLiquiedList = ref<Container.ContainerItem[]>([]) |
|||
const queryContainerList = () => { |
|||
getContainerList().then((res) => { |
|||
res.forEach((item) => { |
|||
if (item.solutionId) { |
|||
solutionList.value.forEach((soluItem) => { |
|||
if (item.solutionId === soluItem.id) { |
|||
containerLiquiedList.value.push({ |
|||
...item, |
|||
solutionName: soluItem.name, |
|||
}) |
|||
} |
|||
}) |
|||
} |
|||
}) |
|||
}) |
|||
} |
|||
</script> |
|||
|
|||
<template> |
|||
<div class="right-container"> |
|||
<section class="right-main"> |
|||
<span class="right-seq">{{ order }}</span> |
|||
<span class="right-base">{{ StepCmdDescMap[stepInfo.method] }}</span> |
|||
<div v-if="type !== 'showlog'" class="text-item" @click="$emit('del', order)"> |
|||
<img class="item-img" src="@/assets/images/icon_del_s.svg" alt="del"> |
|||
</div> |
|||
</section> |
|||
<template v-if="stepInfo.method !== 'takePhoto'"> |
|||
<section v-if="stepInfo.method === 'addLiquid'" class="right-liquid right-base"> |
|||
<div v-for="(tubeItem, index) in stepInfo.params.tubeSolList" :key="index" class="right-liquid"> |
|||
<!-- <el-select v-model="tubeItem.tubeNum" size="small" placeholder="请选择" style="width: 120px" class="right-base" :disabled="type === 'showlog'"> --> |
|||
<!-- <el-option v-for="item in tubeSolList" :key="item.id" :label="item.name" :value="item.id" /> --> |
|||
<!-- </el-select> --> |
|||
<div v-for="(liquidItem, liquidIndex) in tubeItem.addLiquidList" :key="liquidIndex" class="right-liquid right-base"> |
|||
<el-select v-model="liquidItem.solId" size="small" placeholder="请选择" style="width: 100px" class="right-base" :disabled="type === 'showlog'"> |
|||
<el-option v-for="item in containerLiquiedList" :key="item.id" :label="item.solutionName" :value="item.id" /> |
|||
</el-select> |
|||
<div> |
|||
<el-input v-model="liquidItem.volume" size="small" style="width: 100px" :disabled="type === 'showlog'" /> |
|||
<span class="mL-2">mL</span> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</section> |
|||
|
|||
<section v-if="stepInfo.method === 'shaking' || stepInfo.method === 'delay'" class="right-shaking"> |
|||
<div class="flex items-center right-base"> |
|||
<el-input v-model="stepInfo.params.second" style="width: 100px" size="small" class="right-base" :disabled="type === 'showlog'" /> |
|||
<span class="mL-2">秒</span> |
|||
</div> |
|||
</section> |
|||
<section v-if="stepInfo.method === 'startHeating'" class="right-shaking"> |
|||
<div class="flex items-center right-base"> |
|||
加热温度:<el-input v-model="stepInfo.params.temperature" style="width: 100px" size="small" :disabled="type === 'showlog'" /> |
|||
<span class="mL-2">°C</span> |
|||
</div> |
|||
<div class="flex items-center right-base"> |
|||
加热时间:<el-input v-model="stepInfo.params.second" style="width: 100px" size="small" :disabled="type === 'showlog'" /> |
|||
<span class="mL-2">秒</span> |
|||
</div> |
|||
</section> |
|||
</template> |
|||
</div> |
|||
<div style="height:5px;background: white;" /> |
|||
</template> |
|||
|
|||
<style lang="scss" scoped> |
|||
div{ |
|||
font-size: 12px; |
|||
} |
|||
span{ |
|||
font-size: 12px; |
|||
} |
|||
.right-container{ |
|||
background-color: rgb(82 148 215 / .06); |
|||
border-radius: 10px; |
|||
min-height: 40px; |
|||
padding-top: 5px; |
|||
} |
|||
|
|||
.right-main{ |
|||
display: flex; |
|||
align-items: center; |
|||
} |
|||
|
|||
.right-base{ |
|||
line-height: 1.5rem; |
|||
margin-left: 1.25rem; |
|||
} |
|||
.text-item{ |
|||
margin-left: auto; |
|||
width: 2.5rem; |
|||
height: 2.5rem; |
|||
display: flex; |
|||
justify-content: center; |
|||
align-items: center; |
|||
.item-img{ |
|||
width:1rem |
|||
} |
|||
} |
|||
.right-liquid{ |
|||
display: flex; |
|||
height: 3rem; |
|||
} |
|||
|
|||
.right-shaking{ |
|||
display: flex; |
|||
height: 2.5rem; |
|||
} |
|||
|
|||
.right-seq{ |
|||
padding-left: 20px; |
|||
color: #19b370; |
|||
} |
|||
.el-select-dropdown__item{ |
|||
display: flex; |
|||
align-items: center; |
|||
} |
|||
</style> |
@ -0,0 +1,202 @@ |
|||
<script setup lang="ts"> |
|||
import { getContainerList } from 'apis/container' |
|||
import { getSolsList } from 'apis/solution' |
|||
import { socket } from 'libs/socket' |
|||
import { useHomeStore } from 'stores/homeStore' |
|||
import { useSystemStore } from 'stores/systemStore' |
|||
import { onMounted, onUnmounted, ref } from 'vue' |
|||
|
|||
const emits = defineEmits(['ok', 'cancel']) |
|||
|
|||
const homeStore = useHomeStore() |
|||
const systemStore = useSystemStore() |
|||
|
|||
onMounted(async () => { |
|||
await getSols() |
|||
await queryContainerList() |
|||
socket.init(receiveMessage, 'cmd_debug') |
|||
socket.init(receiveMessage, 'cmd_response') |
|||
}) |
|||
|
|||
onUnmounted(() => { |
|||
socket.unregisterCallback(receiveMessage, 'cmd_debug') |
|||
socket.unregisterCallback(receiveMessage, 'cmd_response') |
|||
}) |
|||
|
|||
const queryContainerList = async () => { |
|||
containerList.value = await getContainerList() |
|||
} |
|||
|
|||
let currentCommandId = '' |
|||
const receiveMessage = (data: Socket.cmdData) => { |
|||
data.commandId === currentCommandId && systemStore.pushSystemList(data) |
|||
} |
|||
|
|||
const form = ref<{ |
|||
columns?: number[] |
|||
containerId?: number |
|||
volume?: number |
|||
}>({}) |
|||
const formRef = ref() |
|||
|
|||
const validateHandle = (rule: any, value: any, callback: any) => { |
|||
if (!value?.length) { |
|||
callback(new Error('请选择试管')) |
|||
} |
|||
else { |
|||
callback() |
|||
} |
|||
} |
|||
|
|||
const rules = { |
|||
columns: [ |
|||
{ required: true, message: '请选择试管', trigger: 'change', validator: validateHandle }, |
|||
], |
|||
containerId: [ |
|||
{ required: true, message: '请选择溶液', trigger: 'change' }, |
|||
], |
|||
volume: [ |
|||
{ required: true, message: '请输入容量', trigger: 'blur' }, |
|||
], |
|||
} |
|||
|
|||
const okHandle = async () => { |
|||
try { |
|||
const valid = await formRef.value.validate() |
|||
if (!valid) { |
|||
return |
|||
} |
|||
currentCommandId = Date.now().toString() |
|||
const params = { |
|||
commandId: currentCommandId, |
|||
command: 'liquid_add', |
|||
params: form.value, |
|||
} |
|||
await homeStore.sendControl(params) |
|||
emits('ok') |
|||
} |
|||
catch (error) { |
|||
console.log(error) |
|||
} |
|||
} |
|||
const cancel = () => { |
|||
emits('cancel') |
|||
} |
|||
|
|||
const solsList = ref<Solution.SolutionItem[]>([]) |
|||
const containerList = ref<Container.ContainerItem[]>([]) |
|||
|
|||
const getSols = async () => { |
|||
const res = await getSolsList() |
|||
solsList.value = res.list |
|||
} |
|||
// |
|||
// const tubes = computed(() => { |
|||
// const tray = systemStore.systemStatus.trays?.find(item => item.inSolutionPositon) |
|||
// return tray?.tubes || [] |
|||
// }) |
|||
|
|||
const selectedColumns = ref(Array.from({ length: 5 }).fill(false)) |
|||
|
|||
const mousedownHandle = async (index: number) => { |
|||
// if (!tubes.value.find(item => item.columnNum === index)?.exists) { |
|||
// FtMessage.error('该列没有试管') |
|||
// return |
|||
// } |
|||
selectedColumns.value[index - 1] = !selectedColumns.value[index - 1] |
|||
form.value.columns = selectedColumns.value.map((item, index) => { |
|||
return item ? index + 1 : false |
|||
}).filter(item => item !== false) |
|||
formRef.value.validateField('columns') |
|||
} |
|||
</script> |
|||
|
|||
<template> |
|||
<FtDialog visible title="添加溶液" width="40%" :ok-handle="okHandle" @cancel="cancel"> |
|||
<el-form ref="formRef" :model="form" :rules="rules" label-width="auto"> |
|||
<el-form-item label="选择试管" prop="columns"> |
|||
<div class="tube-item"> |
|||
<div |
|||
v-for="item in 5" |
|||
:key="item" |
|||
class="tube-line" |
|||
:class="{ 'tube-line-active': selectedColumns[item - 1] }" |
|||
|
|||
@click.prevent="() => mousedownHandle(item)" |
|||
@touch.prevent="() => mousedownHandle(item)" |
|||
> |
|||
<span v-for="i in 8" :key="i" class="tube-line-inner" /> |
|||
</div> |
|||
</div> |
|||
</el-form-item> |
|||
<el-form-item label="选择溶液" prop="solutionId"> |
|||
<el-select v-model="form.containerId" placeholder="请选择溶液"> |
|||
<el-option v-for="item in containerList.filter(i => i.type === 0)" :key="item.id" :label="solsList.find(s => s.id === item.solutionId)?.name" :value="item.id" /> |
|||
</el-select> |
|||
</el-form-item> |
|||
<el-form-item label="容量" prop="volume"> |
|||
<el-input v-model="form.volume" type="number" placeholder="请输入容量"> |
|||
<template #append> |
|||
mL |
|||
</template> |
|||
</el-input> |
|||
</el-form-item> |
|||
</el-form> |
|||
</FtDialog> |
|||
</template> |
|||
|
|||
<style scoped lang="scss"> |
|||
.el-tag { |
|||
margin-right: 5px; |
|||
} |
|||
.el-row { |
|||
height: 450px; |
|||
.el-col { |
|||
height: 100%; |
|||
overflow: auto; |
|||
:deep(.el-tag) { |
|||
width: 100%; |
|||
margin-bottom: 5px; |
|||
.el-tag__content { |
|||
display: flex; |
|||
width: 100%; |
|||
justify-content: space-between; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
.tube-item { |
|||
padding: 5px; |
|||
background: #384D5D; |
|||
border-radius: 10px; |
|||
display: grid; |
|||
grid-template-columns: repeat(5, 1fr); |
|||
grid-template-rows: repeat(1, 1fr); |
|||
grid-gap: 5px; |
|||
position: relative; |
|||
.tube-line { |
|||
display: flex; |
|||
flex-direction: column; |
|||
.tube-line-inner { |
|||
display: inline-block; |
|||
width: 25px; |
|||
height: 25px; |
|||
border-radius: 50%; |
|||
background: #fff; |
|||
margin: 2px; |
|||
transition: background 0.5s; |
|||
} |
|||
} |
|||
.tube-line-disable { |
|||
.tube-line-inner { |
|||
background: #C6C6C6; |
|||
} |
|||
} |
|||
.tube-line-active { |
|||
.tube-line-inner { |
|||
background: #26D574; |
|||
} |
|||
} |
|||
|
|||
} |
|||
</style> |
@ -0,0 +1,502 @@ |
|||
<script setup lang="ts"> |
|||
import { craftRemove, craftRestart } from 'apis/crafts' |
|||
import { ElMessageBox } from 'element-plus' |
|||
import { FtMessage } from 'libs/message' |
|||
import { allPropertiesDefined } from 'libs/utils' |
|||
import { cloneDeep } from 'lodash' |
|||
import { useHomeStore } from 'stores/homeStore' |
|||
import { computed, onMounted, ref } from 'vue' |
|||
|
|||
const props = defineProps({ |
|||
sourceData: { |
|||
type: Object, |
|||
default: () => ({ |
|||
monitorId: 61, // 异常工艺id |
|||
heatId: 'heat_module_02', // 加热区id |
|||
craftsId: 3, // 工艺id |
|||
craftsName: '仅加热', // 工艺名称 |
|||
currentStepIndex: 2, // 当前步骤 |
|||
currentStepResult: '已经加热分钟已经加热分钟已经加热分钟已经加热分钟已经加热分钟已经加热分钟:2', // 当前步骤结果 |
|||
steps: [ |
|||
{ |
|||
name: '预热', |
|||
method: 'preHeat', |
|||
params: { |
|||
temperature: 12, |
|||
description: '1.预热到12度', |
|||
}, |
|||
}, |
|||
{ |
|||
name: '加稀硝酸', |
|||
method: 'addThin', |
|||
params: { |
|||
volume: 3, |
|||
description: '2.添加稀硝酸 3ml', |
|||
}, |
|||
}, |
|||
{ |
|||
name: '加热', |
|||
method: 'heat', |
|||
params: { |
|||
temperature: 4, |
|||
time: 60, |
|||
description: '3.加热: 4度, 保持1分0秒', |
|||
minutes: 1, |
|||
}, |
|||
}, |
|||
{ |
|||
name: '加浓硝酸', |
|||
method: 'addThick', |
|||
params: { |
|||
height: 6, |
|||
volume: 5, |
|||
description: '4.添加浓硝酸 5ml', |
|||
}, |
|||
}, |
|||
{ |
|||
name: '清洗', |
|||
method: 'clean', |
|||
params: { |
|||
cycle: 8, |
|||
height: 7, |
|||
volume: 2, |
|||
description: '5.针头高度7mm, 加2ml水清洗8次', |
|||
}, |
|||
}, |
|||
{ |
|||
name: '烘干', |
|||
method: 'dry', |
|||
params: { |
|||
temperature: 9, |
|||
time: 60, |
|||
description: '6.烘干: 9度, 保持1分0秒', |
|||
minutes: 1, |
|||
}, |
|||
}, |
|||
{ |
|||
name: '退火', |
|||
method: 'anneal', |
|||
params: { |
|||
temperature: 11, |
|||
time: 420, |
|||
description: '7.退火: 11度, 保持7分0秒', |
|||
minutes: 7, |
|||
}, |
|||
}, |
|||
], |
|||
columns: [// 即工艺最原始选择的试管列 |
|||
1, |
|||
5, |
|||
], |
|||
}), |
|||
}, |
|||
}) |
|||
const emits = defineEmits(['ok', 'close']) |
|||
|
|||
const homeStore = useHomeStore() |
|||
|
|||
onMounted(async () => { |
|||
form.value = { |
|||
craftId: props.sourceData.craftsId, |
|||
heatId: props.sourceData.heatId, |
|||
craftMonitorId: props.sourceData.monitorId, |
|||
modifyParam: { |
|||
}, |
|||
columns: cloneDeep(props.sourceData.columns), |
|||
steps: cloneDeep(props.sourceData.steps), |
|||
} |
|||
selectedColumns.value = selectedColumns.value.map((item, index) => { |
|||
return props.sourceData.columns.includes(index + 1) |
|||
}) |
|||
}) |
|||
|
|||
const form = ref<any>({}) |
|||
|
|||
const validateHandle = (rule: any, value: any, callback: any) => { |
|||
if (!value?.length) { |
|||
callback(new Error('请选择试管')) |
|||
} |
|||
else { |
|||
callback() |
|||
} |
|||
} |
|||
|
|||
const rules = { |
|||
columns: [ |
|||
{ required: true, message: '请选择试管', trigger: 'change', validator: validateHandle }, |
|||
], |
|||
} |
|||
|
|||
const hearInfo = computed(() => { |
|||
return homeStore.heatAreaList.find(item => item.value === props.sourceData.heatId) |
|||
}) |
|||
|
|||
const resumeCraftHandle = async () => { |
|||
try { |
|||
const valid = await formRef.value.validate() |
|||
if (!valid) { |
|||
return |
|||
} |
|||
// 找到第一个参数不完整的步骤 |
|||
const errorMsg: string[] = [] |
|||
const invalidStepIndex = form.value.steps.findIndex((step: any, index: number) => { |
|||
if (['heat', 'dry', 'anneal'].includes(step.method)) { |
|||
if (step.params.minutes || step.params.seconds) { |
|||
step.params.time = (step.params.minutes || 0) * 60 + (step.params.seconds || 0) || undefined |
|||
} |
|||
else { |
|||
step.params.time = undefined |
|||
} |
|||
if (step.params.temperature > 400 && step.method === 'anneal') { |
|||
errorMsg.push(`步骤${index + 1}: 退火温度不能超过400度`) |
|||
} |
|||
if (step.params.temperature > 200 && step.method === 'heat') { |
|||
errorMsg.push(`步骤${index + 1}: 加热温度不能超过200度`) |
|||
} |
|||
if (step.params.temperature > 200 && step.method === 'dry') { |
|||
errorMsg.push(`步骤${index + 1}: 烘干温度不能超过200度`) |
|||
} |
|||
} |
|||
step.params.description = `${index + 1}.` |
|||
switch (step.method) { |
|||
case 'clean': |
|||
step.params.description += `针头高度${step.params.height}mm, 加${step.params.volume}ml水清洗${step.params.cycle}次` |
|||
break |
|||
case 'addThin': |
|||
step.params.description += `添加稀硝酸 ${step.params.volume}mL` |
|||
break |
|||
case 'addThick': |
|||
step.params.description += `添加浓硝酸 ${step.params.volume}mL` |
|||
break |
|||
case 'reduceLiquid': |
|||
step.params.description += `针头高度${step.params.height}mm抽取液体` |
|||
break |
|||
case 'preHeat': |
|||
step.params.description += `预热到${step.params.temperature}度` |
|||
break |
|||
case 'heat': |
|||
step.params.description += `加热: ${step.params.temperature}度, 保持${step.params.minutes || 0}分${step.params.seconds || 0}秒` |
|||
break |
|||
case 'dry': |
|||
step.params.description += `烘干: ${step.params.temperature}度, 保持${step.params.minutes || 0}分${step.params.seconds || 0}秒` |
|||
break |
|||
case 'anneal': |
|||
step.params.description += `退火: ${step.params.temperature}度, 保持${step.params.minutes || 0}分${step.params.seconds || 0}秒` |
|||
break |
|||
} |
|||
return !allPropertiesDefined(step.params, ['minutes', 'seconds', 'description']) |
|||
}) |
|||
|
|||
if (invalidStepIndex !== -1) { |
|||
FtMessage.error(`步骤${invalidStepIndex + 1}: 请填写完整参数`) |
|||
return |
|||
} |
|||
if (errorMsg.length) { |
|||
FtMessage.error(errorMsg.join('; ')) |
|||
return |
|||
} |
|||
form.value.modifyParam = form.value.steps?.[props.sourceData.currentStepIndex]?.params |
|||
await craftRestart(form.value) |
|||
FtMessage.success('恢复成功') |
|||
emits('ok') |
|||
} |
|||
catch (e) { |
|||
console.log(e) |
|||
} |
|||
} |
|||
|
|||
const stopCraftHandle = async () => { |
|||
await ElMessageBox.confirm('确定删除当前工艺?', '消息', { |
|||
confirmButtonText: '确认', |
|||
cancelButtonText: '取消', |
|||
type: 'warning', |
|||
}) |
|||
await craftRemove(props.sourceData.monitorId) |
|||
FtMessage.success('删除成功') |
|||
emits('ok') |
|||
} |
|||
|
|||
const cancel = () => { |
|||
emits('close') |
|||
} |
|||
|
|||
const stepMap = { |
|||
addThin: '加稀硝酸', |
|||
addThick: '加浓硝酸', |
|||
clean: '清洗', |
|||
preHeat: '预热', |
|||
heat: '加热', |
|||
dry: '烘干', |
|||
anneal: ' 退火', |
|||
} |
|||
const formRef = ref() |
|||
const selectVisible = ref(false) |
|||
const checkChange = () => { |
|||
selectedColumns.value = Array.from({ length: 5 }).fill(selectVisible.value) |
|||
form.value.columns = selectedColumns.value.map((item, index) => index + 1).filter(item => selectedColumns.value[item - 1]) |
|||
formRef.value.validateField('columns') |
|||
} |
|||
|
|||
const selectedColumns = ref(Array.from({ length: 5 }).fill(false)) |
|||
const mousedownHandle = async (index: number) => { |
|||
selectedColumns.value[index - 1] = !selectedColumns.value[index - 1] |
|||
form.value.columns = selectedColumns.value.map((item, index) => { |
|||
return item ? index + 1 : false |
|||
}).filter(item => item !== false) |
|||
formRef.value.validateField('columns') |
|||
} |
|||
</script> |
|||
|
|||
<template> |
|||
<FtDialog visible title="工艺异常" width="80%"> |
|||
<el-form ref="formRef" :model="form" :rules="rules" label-width="auto" class="form-box" label-position="left"> |
|||
<el-row> |
|||
<el-col :span="10"> |
|||
<el-form-item label="加热区"> |
|||
<el-tag>{{ hearInfo?.label }}</el-tag> |
|||
</el-form-item> |
|||
<el-form-item label="工艺名称"> |
|||
<el-tooltip :content="sourceData.craftsName" placement="top" trigger="click"> |
|||
<el-tag>{{ sourceData.craftsName }}</el-tag> |
|||
</el-tooltip> |
|||
</el-form-item> |
|||
<el-form-item label="当前步骤"> |
|||
<el-tooltip :content="sourceData?.steps?.[sourceData?.currentStepIndex - 1]?.params?.description" placement="top" trigger="click"> |
|||
<el-tag>{{ sourceData?.steps?.[sourceData?.currentStepIndex - 1]?.params?.description }}</el-tag> |
|||
</el-tooltip> |
|||
</el-form-item> |
|||
<el-form-item label="步骤结果"> |
|||
<el-tooltip :content="sourceData?.currentStepResult" placement="top" trigger="click"> |
|||
<el-tag>{{ sourceData?.currentStepResult }}</el-tag> |
|||
</el-tooltip> |
|||
</el-form-item> |
|||
<el-form-item label="选择试管" prop="columns"> |
|||
<el-checkbox v-model="selectVisible" style="margin-right: 10px" @change="checkChange"> |
|||
全选 |
|||
</el-checkbox> |
|||
<div class="tube-item"> |
|||
<div |
|||
v-for="item in 5" |
|||
:key="item" |
|||
class="tube-line" |
|||
:class="{ 'tube-line-active': selectedColumns[item - 1] }" |
|||
|
|||
@click.prevent="() => mousedownHandle(item)" |
|||
@touch.prevent="() => mousedownHandle(item)" |
|||
> |
|||
<span v-for="i in 8" :key="i" class="tube-line-inner" /> |
|||
</div> |
|||
</div> |
|||
</el-form-item> |
|||
</el-col> |
|||
<el-col :span="14"> |
|||
<div v-if="form.steps.length" class="step-box"> |
|||
<div |
|||
v-for="(item, index) in form.steps" |
|||
:key="index" |
|||
class="step-item" |
|||
:class="{ 'step-item-success': sourceData?.currentStepIndex > index, 'step-item-ing': sourceData?.currentStepIndex === index }" |
|||
> |
|||
<el-form-item :label="sourceData?.currentStepIndex === index ? `${index + 1}: ${stepMap[item.method]}` : ''"> |
|||
<template v-if="sourceData?.currentStepIndex !== index"> |
|||
<span>{{ item.params.description }}</span> |
|||
</template> |
|||
<template v-else> |
|||
<template v-if="item.method === 'clean'"> |
|||
<el-input v-model="item.params.height" type="number" size="small" placeholder="请输入高度"> |
|||
<template #append> |
|||
mm |
|||
</template> |
|||
</el-input> |
|||
<el-input |
|||
v-model="item.params.volume" |
|||
style="width: 100px" |
|||
type="number" |
|||
size="small" |
|||
placeholder="请输入加水量" |
|||
> |
|||
<template #append> |
|||
mL |
|||
</template> |
|||
</el-input> |
|||
<el-input v-model="item.params.cycle" type="number" size="small" placeholder="请输入次数"> |
|||
<template #append> |
|||
次 |
|||
</template> |
|||
</el-input> |
|||
</template> |
|||
<template v-else-if="['addThin', 'addThick'].includes(item.method)"> |
|||
<el-input v-model="item.params.volume" type="number" size="small" placeholder="请输入容量"> |
|||
<template #append> |
|||
mL |
|||
</template> |
|||
</el-input> |
|||
<el-input v-if="item.method === 'addThick'" v-model="item.params.height" type="number" size="small" placeholder="请输入高度"> |
|||
<template #append> |
|||
mm |
|||
</template> |
|||
</el-input> |
|||
</template> |
|||
<template v-else-if="item.method === 'reduceLiquid'"> |
|||
<el-input v-model="item.params.height" type="number" size="small" placeholder="请输入高度"> |
|||
<template #append> |
|||
mm |
|||
</template> |
|||
</el-input> |
|||
</template> |
|||
<template v-else-if="item.method === 'preHeat'"> |
|||
<el-input |
|||
v-model="item.params.temperature" |
|||
type="number" |
|||
size="small" |
|||
placeholder="请输入温度" |
|||
> |
|||
<template #append> |
|||
℃ |
|||
</template> |
|||
</el-input> |
|||
</template> |
|||
<template v-else-if="['heat', 'dry', 'anneal'].includes(item.method)"> |
|||
<el-input v-model="item.params.temperature" type="number" :max="item.method === 'anneal' ? 400 : 200" size="small" placeholder="加热温度"> |
|||
<template #append> |
|||
℃ |
|||
</template> |
|||
</el-input> |
|||
<el-select |
|||
v-model="item.params.minutes" |
|||
style="width: 70px" |
|||
clearable |
|||
size="small" |
|||
placeholder="请选择" |
|||
> |
|||
<el-option v-for="i in 60" :key="i" :label="i" :value="i" /> |
|||
</el-select> |
|||
<span class="unit-text">分</span> |
|||
<el-select |
|||
v-model="item.params.seconds" |
|||
style="width: 70px" |
|||
clearable |
|||
size="small" |
|||
placeholder="请选择" |
|||
> |
|||
<el-option v-for="i in 60" :key="i" :label="i" :value="i" /> |
|||
</el-select> |
|||
<span class="unit-text">秒</span> |
|||
</template> |
|||
</template> |
|||
</el-form-item> |
|||
</div> |
|||
</div> |
|||
</el-col> |
|||
</el-row> |
|||
</el-form> |
|||
<template #footer> |
|||
<ft-button type="primary" :click-handle="resumeCraftHandle"> |
|||
恢复工艺 |
|||
</ft-button> |
|||
<ft-button type="danger" :click-handle="stopCraftHandle"> |
|||
删除工艺 |
|||
</ft-button> |
|||
<ft-button @click="cancel"> |
|||
关闭 |
|||
</ft-button> |
|||
</template> |
|||
</FtDialog> |
|||
</template> |
|||
|
|||
<style scoped lang="scss"> |
|||
.step-item-success { |
|||
.el-form-item { |
|||
background: rgba(168,225,168,0.4) !important; |
|||
} |
|||
} |
|||
.step-item-ing { |
|||
.el-form-item { |
|||
background: rgba(25,137,250,0.4) !important; |
|||
} |
|||
} |
|||
.step-item { |
|||
.el-form-item { |
|||
background: rgba(82, 148, 215, 0.06); |
|||
padding: 5px; |
|||
margin-bottom: 10px; |
|||
:deep(.el-form-item__label) { |
|||
height: 25px; |
|||
line-height: 25px; |
|||
} |
|||
:deep(.el-form-item__content) { |
|||
width: 100%; |
|||
display: flex; |
|||
align-items: center; |
|||
position: relative; |
|||
|
|||
.el-input, |
|||
.el-select { |
|||
width: 120px; |
|||
margin: 0 5px; |
|||
} |
|||
.info-box { |
|||
width: 80%; |
|||
height: 100%; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
.unit-text { |
|||
font-size: 12px; |
|||
line-height: 25px; |
|||
} |
|||
|
|||
.tube-item { |
|||
padding: 5px; |
|||
background: #384D5D; |
|||
border-radius: 10px; |
|||
display: grid; |
|||
grid-template-columns: repeat(5, 1fr); |
|||
grid-template-rows: repeat(1, 1fr); |
|||
grid-gap: 5px; |
|||
position: relative; |
|||
.tube-line { |
|||
display: flex; |
|||
flex-direction: column; |
|||
.tube-line-inner { |
|||
display: inline-block; |
|||
width: 25px; |
|||
height: 25px; |
|||
border-radius: 50%; |
|||
background: #fff; |
|||
margin: 2px; |
|||
transition: background 0.5s; |
|||
} |
|||
} |
|||
.tube-line-disable { |
|||
.tube-line-inner { |
|||
background: #C6C6C6; |
|||
} |
|||
} |
|||
.tube-line-active { |
|||
.tube-line-inner { |
|||
background: #26D574; |
|||
} |
|||
} |
|||
} |
|||
|
|||
.el-form-item { |
|||
:deep(.el-form-item__content) { |
|||
width: 100%; |
|||
display: flex; |
|||
align-items: center; |
|||
position: relative; |
|||
|
|||
.el-tag { |
|||
max-width: 90%; |
|||
.el-tag__content { |
|||
width: 100%; |
|||
white-space: nowrap; |
|||
overflow: hidden; |
|||
text-overflow: ellipsis; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
</style> |
@ -0,0 +1,107 @@ |
|||
<script setup lang="ts"> |
|||
import { useSystemStore } from 'stores/systemStore' |
|||
import { onBeforeUnmount, onMounted, ref, watch } from 'vue' |
|||
|
|||
const props = defineProps<{ |
|||
current: number |
|||
startTime: number |
|||
duration: number // 秒 |
|||
}>() |
|||
|
|||
const systemStore = useSystemStore() |
|||
|
|||
const remaining = ref(0) |
|||
let timer: number | null = null |
|||
|
|||
const calculateRemaining = () => { |
|||
// 计算结束时间 |
|||
const endTime = props.startTime + props.duration * 1000 |
|||
// 剩余时间 = 结束时间 - 当前时间 - 网络延迟 |
|||
remaining.value = Math.max(0, endTime - new Date(systemStore.currentTime).getTime()) |
|||
console.log('remaining', remaining.value) |
|||
} |
|||
|
|||
const updateCountdown = () => { |
|||
if (remaining.value <= 0) { |
|||
stopTimer() |
|||
return |
|||
} |
|||
remaining.value -= 1000 |
|||
} |
|||
|
|||
const startTimer = () => { |
|||
updateCountdown() // 立即执行一次 |
|||
timer = window.setInterval(() => { |
|||
calculateRemaining() |
|||
updateCountdown() |
|||
}, 1000) |
|||
} |
|||
|
|||
const stopTimer = () => { |
|||
if (timer) { |
|||
clearInterval(timer) |
|||
timer = null |
|||
} |
|||
} |
|||
|
|||
onMounted(() => { |
|||
startTimer() |
|||
}) |
|||
|
|||
onBeforeUnmount(() => { |
|||
stopTimer() |
|||
}) |
|||
|
|||
// 时间分割计算 |
|||
const days = ref(0) |
|||
const hours = ref('00') |
|||
const minutes = ref('00') |
|||
const seconds = ref('00') |
|||
|
|||
const visible = ref(true) |
|||
|
|||
// 监听剩余时间变化 |
|||
watch( |
|||
() => remaining.value, |
|||
(newVal) => { |
|||
if (newVal <= 0 || !newVal) { |
|||
visible.value = false |
|||
days.value = 0 |
|||
hours.value = '00' |
|||
minutes.value = '00' |
|||
seconds.value = '00' |
|||
return |
|||
} |
|||
else { |
|||
visible.value = true |
|||
} |
|||
|
|||
days.value = Math.floor(newVal / (1000 * 60 * 60 * 24)) |
|||
|
|||
const hoursNum = Math.floor((newVal % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)) |
|||
hours.value = hoursNum.toString().padStart(2, '0') |
|||
|
|||
const minutesNum = Math.floor((newVal % (1000 * 60 * 60)) / (1000 * 60)) |
|||
minutes.value = minutesNum.toString().padStart(2, '0') |
|||
|
|||
const secondsNum = Math.floor((newVal % (1000 * 60)) / 1000) |
|||
seconds.value = secondsNum.toString().padStart(2, '0') |
|||
}, |
|||
{ immediate: true }, |
|||
) |
|||
</script> |
|||
|
|||
<template> |
|||
<div v-if="visible" class="countdown"> |
|||
<span v-if="days">{{ days }}天</span> |
|||
{{ hours }}:{{ minutes }}:{{ seconds }} |
|||
</div> |
|||
</template> |
|||
|
|||
<style scoped> |
|||
.countdown { |
|||
text-align: center; |
|||
font-size: 12px; |
|||
color: #FF4500; |
|||
} |
|||
</style> |
@ -0,0 +1,190 @@ |
|||
<script setup lang="ts"> |
|||
import { getCraftList, startCraft } from 'apis/crafts' |
|||
import { FtMessage } from 'libs/message' |
|||
import { useHomeStore } from 'stores/homeStore' |
|||
import { onMounted, ref } from 'vue' |
|||
|
|||
const emits = defineEmits(['ok', 'cancel']) |
|||
|
|||
const homeStore = useHomeStore() |
|||
|
|||
onMounted(() => { |
|||
getOres() |
|||
}) |
|||
|
|||
const form = ref<{ |
|||
columns?: number[] |
|||
craftId?: number |
|||
heatId?: number |
|||
}>({}) |
|||
const formRef = ref() |
|||
|
|||
const validateHandle = (rule: any, value: any, callback: any) => { |
|||
if (!value?.length) { |
|||
callback(new Error('请选择试管')) |
|||
} |
|||
else { |
|||
callback() |
|||
} |
|||
} |
|||
|
|||
const rules = { |
|||
columns: [ |
|||
{ required: true, message: '请选择试管', trigger: 'change', validator: validateHandle }, |
|||
], |
|||
craftId: [ |
|||
{ required: true, message: '请选择工艺', trigger: 'change' }, |
|||
], |
|||
} |
|||
|
|||
const okHandle = async () => { |
|||
try { |
|||
const valid = await formRef.value.validate() |
|||
if (!valid) { |
|||
return |
|||
} |
|||
await startCraft({ |
|||
heatId: useHomeStore().heatAreaList.find(item => item.selected)?.value, |
|||
...form.value, |
|||
}) |
|||
FtMessage.success('工艺已开始') |
|||
emits('ok') |
|||
} |
|||
catch (error) { |
|||
console.log(error) |
|||
} |
|||
} |
|||
|
|||
const cancel = () => { |
|||
emits('cancel') |
|||
} |
|||
|
|||
// |
|||
// const tubes = computed(() => { |
|||
// return systemStore.systemStatus.trays?.find(item => item.heatModuleCode === homeStore.heatAreaList.find(item => item.selected)?.value)?.tubes |
|||
// }) |
|||
|
|||
const selectedColumns = ref(Array.from({ length: 5 }).fill(false)) |
|||
|
|||
const mousedownHandle = async (index: number) => { |
|||
// if (!tubes.value?.find(item => item.columnNum === index)?.exists) { |
|||
// FtMessage.error('该列没有试管') |
|||
// return |
|||
// } |
|||
selectedColumns.value[index - 1] = !selectedColumns.value[index - 1] |
|||
form.value.columns = selectedColumns.value.map((item, index) => { |
|||
return item ? index + 1 : false |
|||
}).filter(item => item !== false) |
|||
formRef.value.validateField('columns') |
|||
} |
|||
|
|||
const craftList = ref<CraftTypes.Craft[]>([]) |
|||
|
|||
const getOres = async () => { |
|||
const res = await getCraftList() |
|||
craftList.value = res.list |
|||
} |
|||
|
|||
const selectVisible = ref(false) |
|||
const checkChange = () => { |
|||
selectedColumns.value = Array.from({ length: 5 }).fill(selectVisible.value) |
|||
form.value.columns = selectedColumns.value.map((item, index) => index + 1).filter(item => selectedColumns.value[item - 1]) |
|||
formRef.value.validateField('columns') |
|||
} |
|||
</script> |
|||
|
|||
<template> |
|||
<FtDialog visible title="执行工艺" width="40%" :ok-handle="okHandle" @cancel="cancel"> |
|||
<el-form ref="formRef" :model="form" :rules="rules" label-width="auto"> |
|||
<el-form-item v-if=" homeStore.heatAreaList.filter(item => item.selected).length" label="加热区"> |
|||
<el-tag v-for="item in homeStore.heatAreaList.filter(item => item.selected)" :key="item.value" class="mask-tag"> |
|||
{{ item.label }} |
|||
</el-tag> |
|||
</el-form-item> |
|||
<el-form-item label="选择试管" prop="columns"> |
|||
<el-checkbox v-model="selectVisible" style="margin-right: 10px" @change="checkChange"> |
|||
全选 |
|||
</el-checkbox> |
|||
<div class="tube-item"> |
|||
<div |
|||
v-for="item in 5" |
|||
:key="item" |
|||
class="tube-line" |
|||
:class="{ 'tube-line-active': selectedColumns[item - 1] }" |
|||
|
|||
@click.prevent="() => mousedownHandle(item)" |
|||
@touch.prevent="() => mousedownHandle(item)" |
|||
> |
|||
<span v-for="i in 8" :key="i" class="tube-line-inner" /> |
|||
</div> |
|||
</div> |
|||
</el-form-item> |
|||
<el-form-item label="工艺" prop="craftId"> |
|||
<el-select v-model="form.craftId" placeholder="请选择工艺"> |
|||
<el-option |
|||
v-for="item in craftList" |
|||
:key="item.id" |
|||
:label="item.name" |
|||
:value="item.id" |
|||
/> |
|||
</el-select> |
|||
</el-form-item> |
|||
</el-form> |
|||
</FtDialog> |
|||
</template> |
|||
|
|||
<style scoped lang="scss"> |
|||
.el-tag { |
|||
margin-right: 5px; |
|||
} |
|||
.el-row { |
|||
height: 450px; |
|||
.el-col { |
|||
height: 100%; |
|||
overflow: auto; |
|||
:deep(.el-tag) { |
|||
width: 100%; |
|||
margin-bottom: 5px; |
|||
.el-tag__content { |
|||
display: flex; |
|||
width: 100%; |
|||
justify-content: space-between; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
.tube-item { |
|||
padding: 5px; |
|||
background: #384D5D; |
|||
border-radius: 10px; |
|||
display: grid; |
|||
grid-template-columns: repeat(5, 1fr); |
|||
grid-template-rows: repeat(1, 1fr); |
|||
grid-gap: 5px; |
|||
position: relative; |
|||
.tube-line { |
|||
display: flex; |
|||
flex-direction: column; |
|||
.tube-line-inner { |
|||
display: inline-block; |
|||
width: 25px; |
|||
height: 25px; |
|||
border-radius: 50%; |
|||
background: #fff; |
|||
margin: 2px; |
|||
transition: background 0.5s; |
|||
} |
|||
} |
|||
.tube-line-disable { |
|||
.tube-line-inner { |
|||
background: #C6C6C6; |
|||
} |
|||
} |
|||
.tube-line-active { |
|||
.tube-line-inner { |
|||
background: #26D574; |
|||
} |
|||
} |
|||
|
|||
} |
|||
</style> |
@ -0,0 +1,218 @@ |
|||
<script setup lang="ts"> |
|||
import { getSolsList } from 'apis/solution' |
|||
import { configList } from 'apis/system' |
|||
import { socket } from 'libs/socket' |
|||
import { useHomeStore } from 'stores/homeStore' |
|||
import { useSystemStore } from 'stores/systemStore' |
|||
import { computed, onMounted, onUnmounted, ref } from 'vue' |
|||
|
|||
const emits = defineEmits(['ok', 'cancel']) |
|||
|
|||
const homeStore = useHomeStore() |
|||
const systemStore = useSystemStore() |
|||
|
|||
const configData = ref<any[]>([]) |
|||
|
|||
onMounted(async () => { |
|||
await getSols() |
|||
configData.value = await configList() |
|||
socket.init(receiveMessage, 'cmd_debug') |
|||
socket.init(receiveMessage, 'cmd_response') |
|||
}) |
|||
|
|||
onUnmounted(() => { |
|||
socket.unregisterCallback(receiveMessage, 'cmd_debug') |
|||
socket.unregisterCallback(receiveMessage, 'cmd_response') |
|||
}) |
|||
|
|||
const heightMax = computed(() => { |
|||
return configData.value.find(item => item.code === 'needle_drop_height')?.value || 0 |
|||
}) |
|||
|
|||
let currentCommandId = '' |
|||
const receiveMessage = (data: Socket.cmdData) => { |
|||
data.commandId === currentCommandId && systemStore.pushSystemList(data) |
|||
} |
|||
|
|||
const form = ref<{ |
|||
columns?: number[] |
|||
height?: number |
|||
}>({}) |
|||
const formRef = ref() |
|||
|
|||
const validateHandle = (rule: any, value: any, callback: any) => { |
|||
if (!value?.length) { |
|||
callback(new Error('请选择试管')) |
|||
} |
|||
else { |
|||
callback() |
|||
} |
|||
} |
|||
|
|||
const validateHandle1 = (rule: any, value: any, callback: any) => { |
|||
if (!value) { |
|||
callback(new Error('请输入高度')) |
|||
} |
|||
else |
|||
if (value && (value < 0 || +value > +heightMax.value)) { |
|||
callback(new Error(`针头高度范围0-${heightMax.value}mm`)) |
|||
} |
|||
else { |
|||
callback() |
|||
} |
|||
} |
|||
|
|||
const rules = { |
|||
columns: [ |
|||
{ required: true, message: '请选择试管', trigger: 'change', validator: validateHandle }, |
|||
], |
|||
height: [ |
|||
{ required: true, trigger: 'blur', validator: validateHandle1 }, |
|||
], |
|||
} |
|||
|
|||
const okHandle = async () => { |
|||
try { |
|||
const valid = await formRef.value.validate() |
|||
if (!valid) { |
|||
return |
|||
} |
|||
currentCommandId = Date.now().toString() |
|||
const params = { |
|||
commandId: currentCommandId, |
|||
command: 'liquid_reduce', |
|||
params: form.value, |
|||
} |
|||
await homeStore.sendControl(params) |
|||
emits('ok') |
|||
} |
|||
catch (error) { |
|||
console.log(error) |
|||
} |
|||
} |
|||
const cancel = () => { |
|||
emits('cancel') |
|||
} |
|||
|
|||
const solsList = ref<Solution.SolutionItem[]>([]) |
|||
|
|||
const getSols = async () => { |
|||
const res = await getSolsList() |
|||
solsList.value = res.list |
|||
} |
|||
|
|||
// const tubes = computed(() => { |
|||
// const tray = systemStore.systemStatus.trays?.find(item => item.inSolutionPositon) |
|||
// return tray?.tubes || [] |
|||
// }) |
|||
|
|||
const selectedColumns = ref(Array.from({ length: 5 }).fill(false)) |
|||
|
|||
const mousedownHandle = async (index: number) => { |
|||
// if (!tubes.value.find(item => item.columnNum === index)?.exists) { |
|||
// FtMessage.error('该列没有试管') |
|||
// return |
|||
// } |
|||
selectedColumns.value[index - 1] = !selectedColumns.value[index - 1] |
|||
form.value.columns = selectedColumns.value.map((item, index) => { |
|||
return item ? index + 1 : false |
|||
}).filter(item => item !== false) |
|||
formRef.value.validateField('columns') |
|||
} |
|||
</script> |
|||
|
|||
<template> |
|||
<FtDialog visible title="抽取溶液" width="40%" :ok-handle="okHandle" @cancel="cancel"> |
|||
<el-form ref="formRef" :model="form" :rules="rules" label-width="auto"> |
|||
<el-form-item label="选择试管" prop="columns"> |
|||
<div class="tube-item"> |
|||
<!-- <div --> |
|||
<!-- v-for="item in 5" --> |
|||
<!-- :key="item" --> |
|||
<!-- class="tube-line" --> |
|||
<!-- :class="{ 'tube-line-active': selectedColumns[item - 1], 'tube-line-disable': !tubes.find(tu => tu.columnNum === item)?.exists }" --> |
|||
|
|||
<!-- @click.prevent="() => mousedownHandle(item)" --> |
|||
<!-- @touch.prevent="() => mousedownHandle(item)" --> |
|||
<!-- > --> |
|||
<!-- <span v-for="i in 8" :key="i" class="tube-line-inner" /> --> |
|||
<!-- </div> --> |
|||
<div |
|||
v-for="item in 5" |
|||
:key="item" |
|||
class="tube-line" |
|||
:class="{ 'tube-line-active': selectedColumns[item - 1] }" |
|||
|
|||
@click.prevent="() => mousedownHandle(item)" |
|||
@touch.prevent="() => mousedownHandle(item)" |
|||
> |
|||
<span v-for="i in 8" :key="i" class="tube-line-inner" /> |
|||
</div> |
|||
</div> |
|||
</el-form-item> |
|||
<el-form-item label="针头下降高度" prop="height"> |
|||
<el-input v-model="form.height" type="number" placeholder="请输入高度"> |
|||
<template #append> |
|||
mm |
|||
</template> |
|||
</el-input> |
|||
</el-form-item> |
|||
</el-form> |
|||
</FtDialog> |
|||
</template> |
|||
|
|||
<style scoped lang="scss"> |
|||
.el-tag { |
|||
margin-right: 5px; |
|||
} |
|||
.el-row { |
|||
height: 450px; |
|||
.el-col { |
|||
height: 100%; |
|||
overflow: auto; |
|||
:deep(.el-tag) { |
|||
width: 100%; |
|||
margin-bottom: 5px; |
|||
.el-tag__content { |
|||
display: flex; |
|||
width: 100%; |
|||
justify-content: space-between; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
.tube-item { |
|||
padding: 5px; |
|||
background: #384D5D; |
|||
border-radius: 10px; |
|||
display: grid; |
|||
grid-template-columns: repeat(5, 1fr); |
|||
grid-template-rows: repeat(1, 1fr); |
|||
grid-gap: 5px; |
|||
position: relative; |
|||
.tube-line { |
|||
display: flex; |
|||
flex-direction: column; |
|||
.tube-line-inner { |
|||
display: inline-block; |
|||
width: 25px; |
|||
height: 25px; |
|||
border-radius: 50%; |
|||
background: #fff; |
|||
margin: 2px; |
|||
transition: background 0.5s; |
|||
} |
|||
} |
|||
.tube-line-disable { |
|||
.tube-line-inner { |
|||
background: #C6C6C6; |
|||
} |
|||
} |
|||
.tube-line-active { |
|||
.tube-line-inner { |
|||
background: #26D574; |
|||
} |
|||
} |
|||
|
|||
} |
|||
</style> |
@ -0,0 +1,117 @@ |
|||
<script setup lang="ts"> |
|||
import { getContainerList } from 'apis/container' |
|||
import { getSolsList } from 'apis/solution' |
|||
import { FtMessage } from 'libs/message' |
|||
import { useHomeStore } from 'stores/homeStore' |
|||
import { useSolutionStore } from 'stores/useSolutionStore' |
|||
import { onMounted, ref } from 'vue' |
|||
|
|||
const emits = defineEmits(['ok', 'cancel']) |
|||
|
|||
const homeStore = useHomeStore() |
|||
let currentCommandId = '' |
|||
onMounted(() => { |
|||
queryContainerList() |
|||
querySolutionList() |
|||
}) |
|||
|
|||
const containerList = ref<Container.ContainerItem[]>([]) |
|||
|
|||
const queryContainerList = async () => { |
|||
const res = await getContainerList() |
|||
containerList.value = res.filter(item => item.type === 0) |
|||
} |
|||
|
|||
const cancel = async () => { |
|||
currentCommandId = Date.now().toString() |
|||
const params = { |
|||
commandId: currentCommandId, |
|||
command: 'liquid_motor_origin', |
|||
params: {}, |
|||
} |
|||
await homeStore.sendControl(params) |
|||
emits('cancel') |
|||
} |
|||
|
|||
const solutionList = ref<Solution.SolutionItem[]>([]) |
|||
const solutionMap = ref<Record<string | number, string>>({}) |
|||
const solutionStore = useSolutionStore() |
|||
const querySolutionList = async () => { |
|||
const res = await getSolsList() |
|||
if (res && res.list) { |
|||
solutionList.value = res.list |
|||
solutionList.value.forEach((item) => { |
|||
if (item.id) { |
|||
solutionMap.value[item.id] = item.name |
|||
} |
|||
}) |
|||
solutionStore.updateSolution(res.list) |
|||
} |
|||
} |
|||
|
|||
const filled_solution_start = async () => { |
|||
if (!checked.value) { |
|||
FtMessage.warning('请选择要预充的溶液') |
|||
return |
|||
} |
|||
currentCommandId = Date.now().toString() |
|||
const params = { |
|||
commandId: currentCommandId, |
|||
command: 'liquid_pre_fill_start', |
|||
params: { |
|||
solutionId: checked.value, |
|||
}, |
|||
} |
|||
await homeStore.sendControl(params) |
|||
} |
|||
|
|||
const filled_solution_stop = async () => { |
|||
currentCommandId = Date.now().toString() |
|||
const params = { |
|||
commandId: currentCommandId, |
|||
command: 'liquid_pre_fill_stop', |
|||
params: {}, |
|||
} |
|||
await homeStore.sendControl(params) |
|||
} |
|||
|
|||
const checked = ref<number>() |
|||
</script> |
|||
|
|||
<template> |
|||
<FtDialog visible title="预充管路" width="50%"> |
|||
<el-radio-group v-model="checked"> |
|||
<el-radio v-for="item in containerList" :key="item.id" border :label="solutionMap[item.solutionId]" :value="item.id" /> |
|||
</el-radio-group> |
|||
|
|||
<template #footer> |
|||
<ft-button type="primary" :click-handle="filled_solution_start"> |
|||
开始预充 |
|||
</ft-button> |
|||
<ft-button type="primary" :click-handle="filled_solution_stop"> |
|||
停止预充 |
|||
</ft-button> |
|||
<ft-button :click-handle="cancel"> |
|||
完成预充 |
|||
</ft-button> |
|||
</template> |
|||
</FtDialog> |
|||
</template> |
|||
|
|||
<style scoped lang="scss"> |
|||
.item-box { |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: space-between; |
|||
padding: 5px; |
|||
} |
|||
.el-checkbox { |
|||
margin: 10px 5px; |
|||
min-width: 100px; |
|||
} |
|||
.button-box { |
|||
margin-top: 10px; |
|||
display: flex; |
|||
justify-content: center; |
|||
} |
|||
</style> |
@ -0,0 +1,64 @@ |
|||
<script setup lang="ts"> |
|||
import { computed } from 'vue' |
|||
|
|||
const props = defineProps({ |
|||
data: { |
|||
type: Object, |
|||
default: () => { |
|||
return {} |
|||
}, |
|||
}, |
|||
}) |
|||
const solutionStyle = computed(() => { |
|||
const difference = (props.data.capacityTotal - props.data.capacityUsed) / props.data.capacityTotal |
|||
console.log(difference) |
|||
const process = 100 - difference * 100 |
|||
console.log(process) |
|||
const filter = difference > 0.4 ? 'hue-rotate(270deg) saturate(100) brightness(81%)' : difference > 0.1 ? 'hue-rotate(150deg) saturate(100)' : 'hue-rotate(120deg) saturate(100)' |
|||
return { |
|||
'filter': filter, |
|||
'-webkit-mask': `linear-gradient(to bottom, transparent ${process}%, #EEEFF8 ${process + 0.01}%)`, |
|||
} |
|||
}) |
|||
|
|||
const percentage = computed(() => Number(((props.data.capacityTotal - props.data.capacityUsed) / props.data.capacityTotal).toFixed(2)) * 100) |
|||
</script> |
|||
|
|||
<template> |
|||
<div class="liquid-content"> |
|||
<div class="bottle_base"> |
|||
<img class="content-img" src="@/assets/images/liquied/liquied_bottle.svg" alt="chemical-bottle"> |
|||
</div> |
|||
<div class="bottle" :style="solutionStyle"> |
|||
<img class="content-img" src="@/assets/images/liquied/liquied_bottle.svg" alt="chemical-bottle"> |
|||
</div> |
|||
<div class="num" :style="{ color: percentage > 40 ? '#fff' : percentage > 10 ? '#FF8E00' : '#FF1C00' }"> |
|||
{{ percentage }} % |
|||
</div> |
|||
</div> |
|||
</template> |
|||
|
|||
<style scoped lang="scss"> |
|||
.liquid-content{ |
|||
position: relative; |
|||
display: flex; |
|||
justify-content: center; |
|||
} |
|||
.bottle_base{ |
|||
position: relative; |
|||
} |
|||
.bottle { |
|||
position: absolute; |
|||
} |
|||
.content-img{ |
|||
height: 100px; |
|||
margin: 10px; |
|||
} |
|||
.num { |
|||
position: absolute; |
|||
top: 50%; |
|||
left: 50%; |
|||
transform: translate(-50%, -50%); |
|||
font-weight: 700; |
|||
} |
|||
</style> |
@ -0,0 +1,131 @@ |
|||
<script setup lang="ts"> |
|||
import { setTargetTemperature } from 'apis/home' |
|||
import { configList } from 'apis/system' |
|||
import { FtMessage } from 'libs/message' |
|||
import { useSystemStore } from 'stores/systemStore' |
|||
import { computed, inject, onMounted, ref } from 'vue' |
|||
|
|||
const emits = defineEmits(['ok', 'cancel']) |
|||
|
|||
const data = inject('currentTemperatureData') |
|||
const configData = ref<any[]>([]) |
|||
onMounted(async () => { |
|||
configData.value = await configList() |
|||
}) |
|||
|
|||
const heatMax = computed(() => { |
|||
return configData.value.find(item => item.code === 'heat_temperature')?.value || 0 |
|||
}) |
|||
const dryMax = computed(() => { |
|||
return configData.value.find(item => item.code === 'dry_temperature')?.value || 0 |
|||
}) |
|||
const annealMax = computed(() => { |
|||
return configData.value.find(item => item.code === 'anneal_temperature')?.value || 0 |
|||
}) |
|||
|
|||
const heatModule = computed(() => { |
|||
return useSystemStore().systemStatus.heatModule.find(item => item.moduleCode === data.value.id) |
|||
}) |
|||
|
|||
const form = ref({ |
|||
dryTemperature: heatModule.value?.dryTemperature, |
|||
annealTemperature: heatModule.value?.annealTemperature, |
|||
heatTemperature: heatModule.value?.heatTemperature, |
|||
}) |
|||
const formRef = ref() |
|||
|
|||
const validateHandle1 = (rule: any, value: any, callback: any) => { |
|||
if (value && (value < 0 || +value > +heatMax.value)) { |
|||
callback(new Error(`加热温度设置范围0℃-${heatMax.value}℃`)) |
|||
} |
|||
else { |
|||
callback() |
|||
} |
|||
} |
|||
const validateHandle2 = (rule: any, value: any, callback: any) => { |
|||
if (value && (value < 0 || +value > +dryMax.value)) { |
|||
callback(new Error(`烘干温度设置范围0℃-${dryMax.value}℃`)) |
|||
} |
|||
else { |
|||
callback() |
|||
} |
|||
} |
|||
const validateHandle3 = (rule: any, value: any, callback: any) => { |
|||
if (value && (value < 0 || +value > +annealMax.value)) { |
|||
callback(new Error(`退火温度设置范围0℃-${annealMax.value}℃`)) |
|||
} |
|||
else { |
|||
callback() |
|||
} |
|||
} |
|||
|
|||
const rules = { |
|||
heatTemperature: [ |
|||
{ required: false, trigger: 'blur', validator: validateHandle1 }, |
|||
], |
|||
dryTemperature: [ |
|||
{ required: false, trigger: 'blur', validator: validateHandle2 }, |
|||
], |
|||
annealTemperature: [ |
|||
{ required: false, trigger: 'blur', validator: validateHandle3 }, |
|||
], |
|||
} |
|||
|
|||
const okHandle = async () => { |
|||
try { |
|||
const valid = await formRef.value.validate() |
|||
if (!valid) { |
|||
return |
|||
} |
|||
await setTargetTemperature({ |
|||
...form.value, |
|||
moduleCode: data.value.id, |
|||
}) |
|||
FtMessage.success('设置成功') |
|||
emits('ok') |
|||
} |
|||
catch (error) { |
|||
console.log(error) |
|||
} |
|||
} |
|||
const cancel = () => { |
|||
emits('cancel') |
|||
} |
|||
</script> |
|||
|
|||
<template> |
|||
<FtDialog visible title="设置目标温度" width="40%" :ok-handle="okHandle" @cancel="cancel"> |
|||
<el-form ref="formRef" label-width="auto" :model="form" :rules="rules"> |
|||
<el-form-item label="加热区"> |
|||
<el-tag>{{ data?.label }}</el-tag> |
|||
</el-form-item> |
|||
<el-form-item label="加热温度" prop="heatTemperature"> |
|||
<el-input v-model="form.heatTemperature" type="number" placeholder="请输入加热温度"> |
|||
<template #append> |
|||
℃ |
|||
</template> |
|||
</el-input> |
|||
</el-form-item> |
|||
<el-form-item label="烘干温度" prop="dryTemperature"> |
|||
<el-input v-model="form.dryTemperature" type="number" placeholder="请输入烘干温度"> |
|||
<template #append> |
|||
℃ |
|||
</template> |
|||
</el-input> |
|||
</el-form-item> |
|||
<el-form-item label="退火温度" prop="annealTemperature"> |
|||
<el-input v-model="form.annealTemperature" type="number" placeholder="请输入退火温度"> |
|||
<template #append> |
|||
℃ |
|||
</template> |
|||
</el-input> |
|||
</el-form-item> |
|||
</el-form> |
|||
</FtDialog> |
|||
</template> |
|||
|
|||
<style scoped lang="scss"> |
|||
.el-tag { |
|||
margin-right: 5px; |
|||
} |
|||
</style> |
@ -0,0 +1,201 @@ |
|||
<script setup lang="ts"> |
|||
import { configList } from 'apis/system' |
|||
import { useHomeStore } from 'stores/homeStore' |
|||
import { computed, onMounted, ref } from 'vue' |
|||
|
|||
const emits = defineEmits(['ok', 'cancel']) |
|||
|
|||
const configData = ref<any[]>([]) |
|||
onMounted(async () => { |
|||
configData.value = await configList() |
|||
}) |
|||
|
|||
const heightMax = computed(() => { |
|||
return configData.value.find(item => item.code === 'needle_drop_height')?.value || 0 |
|||
}) |
|||
|
|||
const homeStore = useHomeStore() |
|||
|
|||
const form = ref({ |
|||
cycle: undefined, |
|||
columns: [], |
|||
height: undefined, |
|||
volume: undefined, |
|||
}) |
|||
const formRef = ref() |
|||
|
|||
const validateHandle = (rule: any, value: any, callback: any) => { |
|||
if (!value?.length) { |
|||
callback(new Error('请选择试管')) |
|||
} |
|||
else { |
|||
callback() |
|||
} |
|||
} |
|||
|
|||
const validateHandle1 = (rule: any, value: any, callback: any) => { |
|||
if (!value) { |
|||
callback(new Error('请输入高度')) |
|||
} |
|||
else |
|||
if (value && (value < 0 || +value > +heightMax.value)) { |
|||
callback(new Error(`针头高度范围0-${heightMax.value}mm`)) |
|||
} |
|||
else { |
|||
callback() |
|||
} |
|||
} |
|||
|
|||
const rules = { |
|||
columns: [ |
|||
{ required: true, message: '请选择试管', trigger: 'change', validator: validateHandle }, |
|||
], |
|||
cycle: [ |
|||
{ required: true, trigger: 'blur', message: '请输入清洗次数' }, |
|||
], |
|||
height: [ |
|||
{ required: true, trigger: 'blur', validator: validateHandle1 }, |
|||
], |
|||
volume: [ |
|||
{ required: true, trigger: 'blur', message: '请输入加水量' }, |
|||
], |
|||
} |
|||
|
|||
const okHandle = async () => { |
|||
try { |
|||
const valid = await formRef.value.validate() |
|||
if (!valid) { |
|||
return |
|||
} |
|||
await commandHandle('clean_start', form.value) |
|||
emits('ok') |
|||
} |
|||
catch (error) { |
|||
console.log(error) |
|||
} |
|||
} |
|||
const cancel = () => { |
|||
emits('cancel') |
|||
} |
|||
|
|||
let currentCommandId = '' |
|||
|
|||
const commandHandle = async (command: string, params?: unknown) => { |
|||
currentCommandId = Date.now().toString() |
|||
const data = { |
|||
commandId: currentCommandId, |
|||
command, |
|||
params, |
|||
} |
|||
await homeStore.sendControl(data) |
|||
} |
|||
|
|||
const selectedColumns = ref(Array.from({ length: 5 }).fill(false)) |
|||
|
|||
const mousedownHandle = async (index: number) => { |
|||
// if (!tubes.value.find(item => item.columnNum === index)?.exists) { |
|||
// FtMessage.error('该列没有试管') |
|||
// return |
|||
// } |
|||
selectedColumns.value[index - 1] = !selectedColumns.value[index - 1] |
|||
form.value.columns = selectedColumns.value.map((item, index) => { |
|||
return item ? index + 1 : false |
|||
}).filter(item => item !== false) |
|||
formRef.value.validateField('columns') |
|||
} |
|||
</script> |
|||
|
|||
<template> |
|||
<FtDialog visible title="设置清洗次数" width="40%" :ok-handle="okHandle" @cancel="cancel"> |
|||
<el-form ref="formRef" label-width="auto" :model="form" :rules="rules"> |
|||
<el-form-item label="选择试管" prop="columns"> |
|||
<div class="tube-item"> |
|||
<div |
|||
v-for="item in 5" |
|||
:key="item" |
|||
class="tube-line" |
|||
:class="{ 'tube-line-active': selectedColumns[item - 1] }" |
|||
|
|||
@click.prevent="() => mousedownHandle(item)" |
|||
@touch.prevent="() => mousedownHandle(item)" |
|||
> |
|||
<span v-for="i in 8" :key="i" class="tube-line-inner" /> |
|||
</div> |
|||
</div> |
|||
</el-form-item> |
|||
<el-form-item label="清洗次数" prop="cycle"> |
|||
<el-select |
|||
v-model="form.cycle" |
|||
filterable |
|||
allow-create |
|||
default-first-option |
|||
placeholder="请选择或输入次数" |
|||
style="width: 100%" |
|||
> |
|||
<el-option |
|||
v-for="item in 3" |
|||
:key="item" |
|||
:label="`${item}次`" |
|||
:value="item" |
|||
/> |
|||
</el-select> |
|||
</el-form-item> |
|||
|
|||
<el-form-item label="针头高度" prop="height"> |
|||
<el-input v-model="form.height" type="number" placeholder="请输入针头高度"> |
|||
<template #append> |
|||
mm |
|||
</template> |
|||
</el-input> |
|||
</el-form-item> |
|||
<el-form-item label="加水量" prop="volume"> |
|||
<el-input v-model="form.volume" type="number" placeholder="请输入加水量"> |
|||
<template #append> |
|||
mL |
|||
</template> |
|||
</el-input> |
|||
</el-form-item> |
|||
</el-form> |
|||
</FtDialog> |
|||
</template> |
|||
|
|||
<style scoped lang="scss"> |
|||
.el-tag { |
|||
margin-right: 5px; |
|||
} |
|||
|
|||
.tube-item { |
|||
padding: 5px; |
|||
background: #384D5D; |
|||
border-radius: 10px; |
|||
display: grid; |
|||
grid-template-columns: repeat(5, 1fr); |
|||
grid-template-rows: repeat(1, 1fr); |
|||
grid-gap: 5px; |
|||
position: relative; |
|||
.tube-line { |
|||
display: flex; |
|||
flex-direction: column; |
|||
.tube-line-inner { |
|||
display: inline-block; |
|||
width: 25px; |
|||
height: 25px; |
|||
border-radius: 50%; |
|||
background: #fff; |
|||
margin: 2px; |
|||
transition: background 0.5s; |
|||
} |
|||
} |
|||
.tube-line-disable { |
|||
.tube-line-inner { |
|||
background: #C6C6C6; |
|||
} |
|||
} |
|||
.tube-line-active { |
|||
.tube-line-inner { |
|||
background: #26D574; |
|||
} |
|||
} |
|||
|
|||
} |
|||
</style> |
@ -0,0 +1,50 @@ |
|||
<script setup lang="ts"> |
|||
import { addTask } from 'apis/home' |
|||
import { FtMessage } from 'libs/message' |
|||
import { ref } from 'vue' |
|||
|
|||
const emits = defineEmits(['ok', 'cancel']) |
|||
|
|||
const form = ref({ |
|||
name: '', |
|||
}) |
|||
const formRef = ref() |
|||
|
|||
const rules = { |
|||
name: [ |
|||
{ required: true, message: '请输入记录名称', trigger: 'blur' }, |
|||
], |
|||
} |
|||
|
|||
const okHandle = async () => { |
|||
try { |
|||
const valid = await formRef.value.validate() |
|||
if (!valid) { |
|||
return |
|||
} |
|||
await addTask(form.value) |
|||
FtMessage.success('已开始记录') |
|||
emits('ok') |
|||
} |
|||
catch (error) { |
|||
console.log(error) |
|||
} |
|||
} |
|||
const cancel = () => { |
|||
emits('cancel') |
|||
} |
|||
</script> |
|||
|
|||
<template> |
|||
<FtDialog visible title="开始新记录" width="30%" :ok-handle="okHandle" @cancel="cancel"> |
|||
<el-form ref="formRef" label-width="auto" :model="form" :rules="rules"> |
|||
<el-form-item label="记录名称" prop="name"> |
|||
<el-input v-model="form.name" placeholder="" /> |
|||
</el-form-item> |
|||
</el-form> |
|||
</FtDialog> |
|||
</template> |
|||
|
|||
<style scoped lang="scss"> |
|||
|
|||
</style> |
@ -0,0 +1,396 @@ |
|||
<script setup lang="ts"> |
|||
import { craftList, pauseCraft, resumeCraft, stopCraft } from 'apis/crafts' |
|||
import { trayTube } from 'apis/home' |
|||
import errorIcon from 'assets/images/error.svg' |
|||
import ingIcon from 'assets/images/ing.svg' |
|||
import jaw_icon from 'assets/images/jaw.svg' |
|||
import successIcon from 'assets/images/success.svg' |
|||
import waitIcon from 'assets/images/wait.svg' |
|||
import CHeckCraft from 'components/home/CheckCraft/index.vue' |
|||
import CountDown from 'components/home/Countdown/Countdown.vue' |
|||
import { useHomeStore } from 'stores/homeStore' |
|||
import { useSystemStore } from 'stores/systemStore' |
|||
import { computed, ref } from 'vue' |
|||
|
|||
const props = withDefaults(defineProps<{ data?: System.HeatArea }>(), { |
|||
data: () => ({ |
|||
moduleCode: 'heat_module_01', |
|||
enable: true, |
|||
trayStatus: true, |
|||
heatingType: 'stop', |
|||
fanOpen: true, |
|||
dryTemperature: 0, |
|||
annealTemperature: 0, |
|||
heatTemperature: 0, |
|||
targetTemperature: 0, |
|||
temperature: 0, |
|||
}), |
|||
}) |
|||
|
|||
const emits = defineEmits(['selectChange', 'setTemperature', 'openCraft']) |
|||
|
|||
const homeStore = useHomeStore() |
|||
const systemStore = useSystemStore() |
|||
const mousedownHandle = async (index: number) => { |
|||
await trayTube({ |
|||
trayUuid: tray.value?.uuid, |
|||
tubes: [ |
|||
{ |
|||
columnNum: index, |
|||
exists: !tray.value?.tubes.find(t => t.columnNum === index)?.exists, |
|||
}, |
|||
], |
|||
}) |
|||
} |
|||
|
|||
const activeTubeBox = ref(false) |
|||
|
|||
const tubeSelect = () => { |
|||
emits('selectChange') |
|||
} |
|||
|
|||
const hearInfo = computed(() => { |
|||
return homeStore.heatAreaList.find(item => item.value === props.data.moduleCode) |
|||
}) |
|||
console.log(hearInfo.value) |
|||
|
|||
const craft = computed(() => { |
|||
return systemStore.systemStatus.trays?.find(item => item.heatModuleCode === props.data.moduleCode)?.crafts |
|||
}) |
|||
|
|||
const tray = computed(() => { |
|||
return systemStore.systemStatus.trays?.find(item => item.heatModuleCode === props.data.moduleCode) |
|||
}) |
|||
|
|||
const craftSteps = computed(() => { |
|||
const steps = systemStore.systemStatus.trays?.find(item => item.heatModuleCode === props.data.moduleCode)?.crafts?.craft?.steps |
|||
return steps ? JSON.parse(steps) : undefined |
|||
}) |
|||
|
|||
const errCraft = computed(() => { |
|||
return systemStore.errCraftList.find(item => item.heatId === props.data.moduleCode) |
|||
}) |
|||
console.log(errCraft.value) |
|||
|
|||
const setTemperature = () => { |
|||
emits('setTemperature', props.data.moduleCode) |
|||
} |
|||
|
|||
const pauseCraftHandle = async () => { |
|||
await pauseCraft({ |
|||
heatId: props.data.moduleCode, |
|||
}) |
|||
} |
|||
|
|||
const resumeCraftHandle = async () => { |
|||
await resumeCraft({ |
|||
heatId: props.data.moduleCode, |
|||
}) |
|||
} |
|||
|
|||
const stopCraftHandle = async () => { |
|||
await stopCraft({ |
|||
heatId: props.data.moduleCode, |
|||
}) |
|||
} |
|||
|
|||
const craftVisible = ref(false) |
|||
const openCraft = () => { |
|||
craftVisible.value = true |
|||
} |
|||
|
|||
const checkCraft = async () => { |
|||
const res = await craftList() |
|||
systemStore.errorCraft = res && res.length > 0 |
|||
systemStore.errCraftList = res |
|||
} |
|||
|
|||
defineExpose({ |
|||
activeTubeBox, |
|||
}) |
|||
</script> |
|||
|
|||
<template> |
|||
<div class="tube" :class="{ 'tube-active': hearInfo?.selected, 'tube-shadow': data.trayStatus }"> |
|||
<div class="header"> |
|||
<span>{{ hearInfo?.label }}</span> |
|||
<img v-if="tray?.useArm" :src="jaw_icon" alt=""> |
|||
<el-tag v-show="!data.trayStatus" type="info"> |
|||
空置 |
|||
</el-tag> |
|||
|
|||
<el-tag v-show="data.trayStatus" type="success"> |
|||
已放置 |
|||
</el-tag> |
|||
</div> |
|||
<div |
|||
class="tube-item" :class="{ |
|||
'tube-item-anneal': ['annealing'].includes(data.heatingType), |
|||
'tube-item-dry': ['drying'].includes(data.heatingType), |
|||
'tube-item-heat': ['heating', 'constant'].includes(data.heatingType), |
|||
'tube-item-fan': data.fanOpen }" |
|||
> |
|||
<div v-if="!data.trayStatus" class="tube-disable" /> |
|||
<div |
|||
v-if="craft?.state" |
|||
class="status" :class="{ |
|||
'status-success': false, |
|||
'status-wait': craft?.state === 'READY', |
|||
'status-PAUSED': craft?.state === 'PAUSED', |
|||
'status-error': craft?.state === 'ERROR', |
|||
'status-ing': craft?.state === 'RUNNING', |
|||
}" |
|||
> |
|||
<img v-if="craft?.state === 'FINISHED'" :src="successIcon" alt=""> |
|||
<img v-if="craft?.state === 'RUNNING'" :src="ingIcon" alt=""> |
|||
<img v-if="craft?.state === 'READY'" :src="waitIcon" alt=""> |
|||
<img v-if="craft?.state === 'PAUSED'" :src="waitIcon" alt=""> |
|||
<img v-if="craft?.state === 'ERROR'" :src="errorIcon" alt=""> |
|||
|
|||
<span class="status-name">{{ craft?.craft?.name || ' ' }}</span> |
|||
<span v-if="craft?.state === 'RUNNING'" class="status-text">工艺执行中</span> |
|||
<span v-if="craft?.state === 'READY'" class="status-text">工艺等待执行</span> |
|||
<span v-if="craft?.state === 'PAUSED'" class="status-text">工艺已暂停</span> |
|||
<span v-if="craft?.state === 'ERROR'" class="status-text">工艺执行错误</span> |
|||
<span v-if="craft?.state === 'FINISHED'" class="status-text">工艺执行成功</span> |
|||
<el-tooltip v-if="craft?.state === 'RUNNING' && craftSteps && craftSteps[craft.currentIndex || 0]?.params?.description" :content="`${craftSteps[craft.currentIndex || 0].params.description}`" placement="top" trigger="click"> |
|||
<div class="status-description"> |
|||
{{ craftSteps[craft.currentIndex || 0].params.description }} |
|||
</div> |
|||
</el-tooltip> |
|||
<div class="status-operation"> |
|||
<ft-button v-if="craft?.state === 'RUNNING'" type="primary" size="small" :click-handle="pauseCraftHandle"> |
|||
暂停 |
|||
</ft-button> |
|||
<ft-button v-if="craft?.state === 'PAUSED'" type="primary" size="small" :click-handle="resumeCraftHandle"> |
|||
继续 |
|||
</ft-button> |
|||
<ft-button v-if="['RUNNING', 'PAUSED'].includes(craft?.state)" size="small" :click-handle="stopCraftHandle"> |
|||
停止 |
|||
</ft-button> |
|||
</div> |
|||
</div> |
|||
<div |
|||
v-for="item in 5" |
|||
:key="item" |
|||
class="tube-line" |
|||
:class="{ 'tube-line-active': tray?.tubes.find(tu => tu.columnNum === item)?.exists }" |
|||
|
|||
@click.prevent="() => mousedownHandle(item)" |
|||
@touch.prevent="() => mousedownHandle(item)" |
|||
> |
|||
<span v-for="i in 8" :key="i" class="tube-line-inner" /> |
|||
</div> |
|||
</div> |
|||
<div class="temperature-box"> |
|||
<span> |
|||
<span>当前温度: </span> |
|||
<span>{{ data.temperature || '--' }}</span> |
|||
<span>℃</span> |
|||
</span> |
|||
<span v-show="data.fanOpen" style="color: #6CD3FF;font-weight: bold">降温中</span> |
|||
<span v-show="data.heatingType === 'heating'" style="color: #e00d0d;font-weight: bold ">加热中</span> |
|||
<span v-show="data.heatingType === 'constant'" style="color: #FE0A0A;font-weight: bold ">恒温中</span> |
|||
<span v-show="data.heatingType === 'drying'" style="color: #F2652D;font-weight: bold ">烘干中</span> |
|||
<span v-show="data.heatingType === 'annealing'" style="color: red;font-weight: bold ">退火中</span> |
|||
<!-- <span v-show="data.heatingType === 'finish'" style="color: #FE0A0A;font-weight: bold ">加热完成</span> --> |
|||
<!-- <span v-show="data.heatingType === 'stop'" style="color: #606266;font-weight: bold ">停止加热</span> --> |
|||
</div> |
|||
<CountDown v-if="data.startHeatTime && data.targetTime" :current="new Date().getTime()" :start-time="data.startHeatTime" :duration="data.targetTime" /> |
|||
<div class="footer"> |
|||
<div class="tem-box" @click="setTemperature"> |
|||
<span :class="{ 'active-footer': hearInfo?.selected }"> {{ data.targetTemperature || '--' }}℃</span> |
|||
<el-icon><Setting /></el-icon> |
|||
</div> |
|||
<el-icon v-if="errCraft" color="#DF1515" size="30" @click="openCraft"> |
|||
<WarningFilled /> |
|||
</el-icon> |
|||
<ft-button size="small" :type="hearInfo?.selected ? 'primary' : 'default'" @click="tubeSelect"> |
|||
选择 |
|||
</ft-button> |
|||
</div> |
|||
<CHeckCraft v-if="craftVisible" :source-data="errCraft" @close="craftVisible = false" @ok="craftVisible = false;checkCraft()" /> |
|||
</div> |
|||
</template> |
|||
|
|||
<style scoped lang="scss"> |
|||
.tube-active { |
|||
border-color: #275EFB !important; |
|||
} |
|||
.tube-shadow { |
|||
box-shadow: 0 0 10px rgba(0,0,0,0.9); |
|||
} |
|||
.tube { |
|||
box-sizing: border-box; |
|||
width: 100%; |
|||
height: 95%; |
|||
background: #E9F3FF; |
|||
border-radius: 10px; |
|||
padding: 10px; |
|||
font-size: 14px; |
|||
display: flex; |
|||
flex-direction: column; |
|||
justify-content: space-between; |
|||
border: 2px solid #E9F3FF; |
|||
transition: all 0.3s; |
|||
position: relative; |
|||
overflow: hidden; |
|||
|
|||
.header { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
color: #4D6882; |
|||
img { |
|||
width: 20px; |
|||
} |
|||
} |
|||
.tube-item-heat { |
|||
background: #e00d0d !important; |
|||
} |
|||
.tube-item-fan { |
|||
background: #6CD3FF !important; |
|||
} |
|||
.tube-item-dry { |
|||
background: #F2652D !important; |
|||
} |
|||
.tube-item-anneal { |
|||
background: red !important; |
|||
} |
|||
.tube-item { |
|||
padding: 5px; |
|||
background: #384D5D; |
|||
border-radius: 10px; |
|||
display: grid; |
|||
grid-template-columns: repeat(5, 1fr); |
|||
grid-template-rows: repeat(1, 1fr); |
|||
grid-gap: 5px; |
|||
position: relative; |
|||
.tube-disable { |
|||
position: absolute; |
|||
width: 100%; |
|||
height: 100%; |
|||
top: 0; |
|||
left: 0; |
|||
background: rgba(255,255,255,0.9); |
|||
border-radius: 9px; |
|||
} |
|||
.tube-line { |
|||
display: flex; |
|||
flex-direction: column; |
|||
.tube-line-inner { |
|||
display: inline-block; |
|||
width: 25px; |
|||
height: 25px; |
|||
border-radius: 50%; |
|||
background: #fff; |
|||
margin: 2px; |
|||
transition: background 0.5s; |
|||
} |
|||
} |
|||
.tube-line-active { |
|||
.tube-line-inner { |
|||
background: #26D574; |
|||
} |
|||
} |
|||
|
|||
} |
|||
.temperature-box { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
background: #fff; |
|||
padding: 5px; |
|||
border-radius: 5px; |
|||
font-size: 12px; |
|||
} |
|||
.footer { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
font-weight: bold; |
|||
color: #4D6882; |
|||
.active-footer { |
|||
color: #1562B7; |
|||
} |
|||
.ft-button { |
|||
margin-right: 0; |
|||
} |
|||
} |
|||
|
|||
} |
|||
|
|||
.status { |
|||
position: absolute; |
|||
top: 0; |
|||
left: 0; |
|||
width: 100%; |
|||
height: 100%; |
|||
background: rgba(255,255,255,0.9); |
|||
z-index: 1; |
|||
display: flex; |
|||
flex-direction: column; |
|||
justify-content: center; |
|||
align-items: center; |
|||
border-radius: 10px; |
|||
img { |
|||
width: 30px; |
|||
margin: 10px 0; |
|||
} |
|||
.status-name { |
|||
font-size: 14px; |
|||
} |
|||
.status-text { |
|||
font-size: 16px; |
|||
font-weight: 700; |
|||
} |
|||
.status-operation { |
|||
width: 100%; |
|||
margin-top: auto; |
|||
margin-bottom: 10px; |
|||
display: flex; |
|||
justify-content: space-around; |
|||
.ft-button { |
|||
margin-right: 5px; |
|||
} |
|||
} |
|||
} |
|||
.status-wait { |
|||
background: rgba(242,235,231, 0.9); |
|||
border: 1px solid #EE8223; |
|||
color: #EE8223; |
|||
} |
|||
.status-PAUSED { |
|||
background: rgba(242,235,231, 0.9); |
|||
border: 1px solid #EE8223; |
|||
color: #EE8223; |
|||
} |
|||
.status-error { |
|||
background: rgba(232,212,222, 0.9); |
|||
border: 1px solid #DF1515; |
|||
color: #DF1515; |
|||
} |
|||
.status-ing { |
|||
background: rgba(205,223,255, 0.9); |
|||
border: 1px solid #0256FF; |
|||
color: #0256FF; |
|||
} |
|||
|
|||
.tem-box { |
|||
display: flex; |
|||
align-items: center; |
|||
.el-icon { |
|||
margin-left: 5px; |
|||
} |
|||
} |
|||
.status-description { |
|||
white-space: nowrap; |
|||
overflow: hidden; |
|||
text-overflow: ellipsis; |
|||
max-width: 95%; |
|||
padding: 0 3px; |
|||
display: inline-block; |
|||
background: #fff; |
|||
border-radius: 2px; |
|||
margin-top: auto; |
|||
} |
|||
</style> |
Some files were not shown because too many files changed in this diff
Write
Preview
Loading…
Cancel
Save
Reference in new issue