first commit

This commit is contained in:
zhanghao 2026-06-29 16:33:00 +08:00
commit 751cb3cd8e
484 changed files with 58782 additions and 0 deletions

14
.env.development Normal file
View File

@ -0,0 +1,14 @@
# 页面标题
VITE_APP_TITLE = 招商车研AI测评机器人模拟测试平台
# 开发环境配置
VITE_APP_ENV = 'development'
# 招商车研物联网平台/开发环境
VITE_APP_BASE_API = '/dev-api'
VITE_API_URL = http://192.168.0.201:13080
VITE_WS_URL = ws://192.168.0.201:13080/ws
# explanation
VITE_INSPECTION_TYPE = "inspection"

20
.env.production Normal file
View File

@ -0,0 +1,20 @@
# 页面标题
VITE_APP_TITLE = 智能座舱测评AI机器人
# 生产环境配置
VITE_APP_ENV = 'production'
# 招商车研物联网平台/生产环境
VITE_APP_BASE_API = '/prod-api'
# 是否在打包时开启压缩,支持 gzip 和 brotli
VITE_BUILD_COMPRESS = gzip
#node-red 服务地址
VITE_NODE_RED_URL = 'http://10.148.108.59:13080'
VITE_API_URL = http://192.168.28.10:13080
VITE_WS_URL = ws://192.168.0.201:13080/ws
# explanation
VITE_INSPECTION_TYPE = "inspection"

13
.env.staging Normal file
View File

@ -0,0 +1,13 @@
# 页面标题
VITE_APP_TITLE = 招商车研物联网平台管理系统
# 生产环境配置
VITE_APP_ENV = 'staging'
# 招商车研物联网平台管理系统/生产环境
VITE_APP_BASE_API = '/stage-api'
# 是否在打包时开启压缩,支持 gzip 和 brotli
VITE_BUILD_COMPRESS = gzip
VITE_API_URL = http://10.148.121.100:13080
VITE_WS_URL = ws://192.168.0.201:13080/ws

26
.gitignore vendored Normal file
View File

@ -0,0 +1,26 @@
.DS_Store
node_modules/
dist/
npm-debug.log*
yarn-debug.log*
yarn-error.log*
**/*.log
tests/**/coverage/
tests/e2e/reports
selenium-debug.log
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.local
.history
package-lock.json
yarn.lock
bin/
pnpm-lock.yaml

10
Dockerfile Normal file
View File

@ -0,0 +1,10 @@
FROM node:18
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .

17
Dockerfile.frontend Normal file
View File

@ -0,0 +1,17 @@
# 使用官方的 Node.js 镜像作为基础镜像
FROM node:18
# 设置工作目录
WORKDIR /app
# 复制 package.json 和 package-lock.json如果存在到工作目录
COPY package*.json ./
# 安装项目依赖
RUN npm install
# 复制项目文件到工作目录
COPY . .
# 构建项目
RUN npm run build

20
LICENSE Normal file
View File

@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright (c) 2018 RuoYi
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

35
README.md Normal file
View File

@ -0,0 +1,35 @@
## 前端运行
```bash
# 安装依赖
yarn --registry=https://registry.npmmirror.com
# 启动服务
yarn dev
# 构建测试环境 yarn build:stage
# 构建生产环境 yarn build:prod
# 前端访问地址 http://localhost:80
```
## 内置功能
1. 用户管理:用户是系统操作者,该功能主要完成系统用户配置。
2. 部门管理:配置系统组织机构(公司、部门、小组),树结构展现支持数据权限。
3. 岗位管理:配置系统用户所属担任职务。
4. 菜单管理:配置系统菜单,操作权限,按钮权限标识等。
5. 角色管理:角色菜单权限分配、设置角色按机构进行数据范围权限划分。
6. 字典管理:对系统中经常使用的一些较为固定的数据进行维护。
7. 参数管理:对系统动态配置常用参数。
8. 通知公告:系统通知公告信息发布维护。
9. 操作日志:系统正常操作日志记录和查询;系统异常信息日志记录和查询。
10. 登录日志:系统登录日志记录查询包含登录异常。
11. 在线用户:当前系统中活跃用户状态监控。
12. 定时任务:在线(添加、修改、删除)任务调度包含执行结果日志。
13. 代码生成前后端代码的生成java、html、xml、sql支持CRUD下载 。
14. 系统接口根据业务代码自动生成相关的api接口文档。
15. 服务监控监视当前系统CPU、内存、磁盘、堆栈等相关信息。
16. 缓存监控:对系统的缓存信息查询,命令统计等。
17. 在线构建器拖动表单元素生成相应的HTML代码。
18. 连接池监视监视当前系统数据库连接池状态可进行分析SQL找出系统性能瓶颈。

46
html/ie.html Normal file

File diff suppressed because one or more lines are too long

215
index.html Normal file
View File

@ -0,0 +1,215 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="renderer" content="webkit">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<link rel="icon" href="/favicon.ico">
<title>招商车研物联网平台管理系统</title>
<!--[if lt IE 11]><script>window.location.href='/html/ie.html';</script><![endif]-->
<style>
html,
body,
#app {
height: 100%;
margin: 0px;
padding: 0px;
}
.chromeframe {
margin: 0.2em 0;
background: #ccc;
color: #000;
padding: 0.2em 0;
}
#loader-wrapper {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 999999;
}
#loader {
display: block;
position: relative;
left: 50%;
top: 50%;
width: 150px;
height: 150px;
margin: -75px 0 0 -75px;
border-radius: 50%;
border: 3px solid transparent;
border-top-color: #FFF;
-webkit-animation: spin 2s linear infinite;
-ms-animation: spin 2s linear infinite;
-moz-animation: spin 2s linear infinite;
-o-animation: spin 2s linear infinite;
animation: spin 2s linear infinite;
z-index: 1001;
}
#loader:before {
content: "";
position: absolute;
top: 5px;
left: 5px;
right: 5px;
bottom: 5px;
border-radius: 50%;
border: 3px solid transparent;
border-top-color: #FFF;
-webkit-animation: spin 3s linear infinite;
-moz-animation: spin 3s linear infinite;
-o-animation: spin 3s linear infinite;
-ms-animation: spin 3s linear infinite;
animation: spin 3s linear infinite;
}
#loader:after {
content: "";
position: absolute;
top: 15px;
left: 15px;
right: 15px;
bottom: 15px;
border-radius: 50%;
border: 3px solid transparent;
border-top-color: #FFF;
-moz-animation: spin 1.5s linear infinite;
-o-animation: spin 1.5s linear infinite;
-ms-animation: spin 1.5s linear infinite;
-webkit-animation: spin 1.5s linear infinite;
animation: spin 1.5s linear infinite;
}
@-webkit-keyframes spin {
0% {
-webkit-transform: rotate(0deg);
-ms-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(360deg);
-ms-transform: rotate(360deg);
transform: rotate(360deg);
}
}
@keyframes spin {
0% {
-webkit-transform: rotate(0deg);
-ms-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(360deg);
-ms-transform: rotate(360deg);
transform: rotate(360deg);
}
}
#loader-wrapper .loader-section {
position: fixed;
top: 0;
width: 51%;
height: 100%;
background: #7171C6;
z-index: 1000;
-webkit-transform: translateX(0);
-ms-transform: translateX(0);
transform: translateX(0);
}
#loader-wrapper .loader-section.section-left {
left: 0;
}
#loader-wrapper .loader-section.section-right {
right: 0;
}
.loaded #loader-wrapper .loader-section.section-left {
-webkit-transform: translateX(-100%);
-ms-transform: translateX(-100%);
transform: translateX(-100%);
-webkit-transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000);
transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000);
}
.loaded #loader-wrapper .loader-section.section-right {
-webkit-transform: translateX(100%);
-ms-transform: translateX(100%);
transform: translateX(100%);
-webkit-transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000);
transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000);
}
.loaded #loader {
opacity: 0;
-webkit-transition: all 0.3s ease-out;
transition: all 0.3s ease-out;
}
.loaded #loader-wrapper {
visibility: hidden;
-webkit-transform: translateY(-100%);
-ms-transform: translateY(-100%);
transform: translateY(-100%);
-webkit-transition: all 0.3s 1s ease-out;
transition: all 0.3s 1s ease-out;
}
.no-js #loader-wrapper {
display: none;
}
.no-js h1 {
color: #222222;
}
#loader-wrapper .load_title {
font-family: 'Open Sans';
color: #FFF;
font-size: 19px;
width: 100%;
text-align: center;
z-index: 9999999999999;
position: absolute;
top: 60%;
opacity: 1;
line-height: 30px;
}
#loader-wrapper .load_title span {
font-weight: normal;
font-style: italic;
font-size: 13px;
color: #FFF;
opacity: 0.5;
}
</style>
</head>
<body>
<div id="app">
<div id="loader-wrapper">
<div id="loader"></div>
<div class="loader-section section-left"></div>
<div class="loader-section section-right"></div>
<div class="load_title">正在加载系统资源,请耐心等待</div>
</div>
</div>
<script type="module" src="/src/main.js"></script>
</body>
</html>

66
package.json Normal file
View File

@ -0,0 +1,66 @@
{
"name": "CMVR-IOT-UI",
"version": "3.8.8",
"description": "CMVR-IOT平台管理系统前端UI框架",
"author": "CMVR",
"license": "MIT",
"type": "module",
"scripts": {
"dev": "vite",
"build:dev": "vite build --mode development",
"build:prod": "vite build --mode production",
"build:staging": "vite build --mode staging --verbose",
"preview": "vite preview"
},
"dependencies": {
"@element-plus/icons-vue": "2.3.1",
"@logicflow/core": "^2.0.13",
"@logicflow/extension": "^2.0.18",
"@vueup/vue-quill": "1.2.0",
"@vueuse/core": "10.11.0",
"ace-builds": "^1.43.0",
"axios": "0.28.1",
"clipboard": "2.0.11",
"dayjs": "^1.11.13",
"echarts": "5.5.1",
"element-plus": "2.9.4",
"file-saver": "2.0.5",
"fuse.js": "6.6.2",
"js-beautify": "1.14.11",
"js-cookie": "3.0.5",
"jsencrypt": "3.3.2",
"json-editor-vue": "^0.18.1",
"lodash": "^4.17.21",
"mitt": "^3.0.1",
"monaco-editor": "^0.55.1",
"nprogress": "0.2.0",
"pako": "^2.1.0",
"pinia": "2.1.7",
"pnpm": "^10.23.0",
"socket.io-client": "^4.8.1",
"sockjs-client": "^1.6.1",
"splitpanes": "3.1.5",
"three": "^0.181.2",
"urdf-loader": "^0.12.6",
"uuid": "^11.1.0",
"vue": "3.4.31",
"vue-cropper": "1.1.1",
"vue-router": "4.4.0",
"vue3-ace-editor": "^2.2.4",
"vue3-json-viewer": "2.3.0",
"vuedraggable": "4.1.0",
"xgplayer": "^3.0.22"
},
"devDependencies": {
"@vitejs/plugin-vue": "5.0.5",
"file-loader": "^6.2.0",
"sass": "1.77.5",
"unplugin-auto-import": "0.17.6",
"unplugin-vue-setup-extend-plus": "1.0.1",
"vite": "5.3.2",
"vite-plugin-compression": "0.5.1",
"vite-plugin-monaco-editor": "^1.1.0",
"vite-plugin-svg-icons": "2.0.1"
},
"packageManager": "pnpm@9.15.9+sha512.68046141893c66fad01c079231128e9afb89ef87e2691d69e4d40eee228988295fd4682181bae55b58418c3a253bde65a505ec7c5f9403ece5cc3cd37dcf2531"
}

54
public/bot.svg Normal file
View File

@ -0,0 +1,54 @@
<svg
viewBox="0 0 22 22"
xmlns="http://www.w3.org/2000/svg"
fill="none"
>
<rect width="22" height="22" x="0" y="0" />
<path
d="M11 7.33335L11 3.66669L7.33331 3.66669"
fill-rule="nonzero"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="1.833333"
/>
<rect
width="14.666667"
height="11.000000"
x="3.666687"
y="7.333344"
rx="1.833333"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="1.833333"
/>
<path
d="M1.83331 12.8333L3.66665 12.8333"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="1.833333"
/>
<path
d="M18.3333 12.8333L20.1666 12.8333"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="1.833333"
/>
<path
d="M13.75 11.9167L13.75 13.75"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="1.833333"
/>
<path
d="M8.25 11.9167L8.25 13.75"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="1.833333"
/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

View File

@ -0,0 +1,465 @@
<?xml version="1.0"?>
<robot name="elfin10" xmlns:xacro="http://wiki.ros.org/xacro">
<xacro:arg name="use_real_hardware" default="true" />
<xacro:arg name="use_fake_hardware" default="false" />
<!-- Load TRANSMISSION file: -->
<xacro:include filename="file://$(find elfin_description)/elfin10_transmission.xacro" />
<!-- Load GAZEBO/ROS2_CONTROL file: -->
<xacro:include filename="file://$(find elfin_description)/elfin10_gazebo.xacro" />
<xacro:property name="PI" value="3.1415926535897931"/>
<xacro:property name="density" value="3.09"/>
<!-- <xacro:macro name="elfin10"> -->
<!-- TRANSMISSION: -->
<xacro:elfin10_transmission />
<!-- GAZEBO - ROS2_CONTROL: -->
<xacro:elfin10_gazebo
use_real_hardware="$(arg use_real_hardware)"
use_fake_hardware="$(arg use_fake_hardware)"/>
<link name="world"/>
<joint name="world_base" type="fixed">
<origin
xyz="0 0 0"
rpy="0 0 -1.5708" />
<parent
link="world" />
<child
link="elfin_base_link" />
</joint>
<link name="elfin_base_link"/>
<joint name="elfin_base_joint" type="fixed">
<origin
xyz="0 0 0"
rpy="0 0 ${PI/2}" />
<parent
link="elfin_base_link" />
<child
link="elfin_base" />
</joint>
<link
name="elfin_base">
<inertial>
<origin
xyz="-0.00226511349377665 4.35718066723588E-06 0.0325128562537015"
rpy="0 0 0" />
<mass
value="1.66727740473724" />
<inertia
ixx="0.004269288474386"
ixy="6.02518130623876E-08"
ixz="-6.92087963593712E-05"
iyy="0.00385585336301628"
iyz="-5.51423740998761E-08"
izz="0.0068204473327682" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="file://$(find elfin_description)/meshes/elfin10/elfin_base.STL" />
</geometry>
<material
name="">
<color
rgba="0.752941176470588 0.752941176470588 0.752941176470588 1" />
</material>
</visual>
<collision>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="file://$(find elfin_description)/meshes/elfin10/elfin_base.STL" />
</geometry>
</collision>
</link>
<link
name="elfin_link1">
<inertial>
<origin
xyz="7.61162863471406E-07 -0.00300107309756385 -0.0432743915921636"
rpy="0 0 0" />
<mass
value="6.3589066662676" />
<inertia
ixx="0.0391771076676677"
ixy="5.46819690041203E-08"
ixz="4.18972540516346E-07"
iyy="0.0381555138034643"
iyz="0.00661206821297882"
izz="0.0211340101406971" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="file://$(find elfin_description)/meshes/elfin10/elfin_link1.STL" />
</geometry>
<material
name="">
<color
rgba="0.752941176470588 0.752941176470588 0.752941176470588 1" />
</material>
</visual>
<collision>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="file://$(find elfin_description)/meshes/elfin10/elfin_link1.STL" />
</geometry>
</collision>
</link>
<joint
name="elfin_joint1"
type="revolute">
<origin
xyz="0 0 0.26"
rpy="0 0 0" />
<parent
link="elfin_base" />
<child
link="elfin_link1" />
<axis
xyz="0 0 1" />
<limit
lower="-3.14"
upper="3.14"
effort="420"
velocity="1.57" />
</joint>
<link
name="elfin_link2">
<inertial>
<origin
xyz="-1.29217317440473E-07 0.204406209458582 0.145205671136993"
rpy="0 0 0" />
<mass
value="5.51072381215317" />
<inertia
ixx="0.140886684844275"
ixy="-1.98882818733046E-05"
ixz="1.28793862225363E-06"
iyy="0.00904523320332792"
iyz="0.00556623571013066"
izz="0.143882970137024" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="file://$(find elfin_description)/meshes/elfin10/elfin_link2.STL" />
</geometry>
<material
name="">
<color
rgba="0.752941176470588 0.752941176470588 0.752941176470588 1" />
</material>
</visual>
<collision>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="file://$(find elfin_description)/meshes/elfin10/elfin_link2.STL" />
</geometry>
</collision>
</link>
<joint
name="elfin_joint2"
type="revolute">
<origin
xyz="0 0 0"
rpy="1.5708 0 0" />
<parent
link="elfin_link1" />
<child
link="elfin_link2" />
<axis
xyz="0 0 1" />
<limit
lower="-2.35"
upper="2.35"
effort="420"
velocity="1.57" />
</joint>
<link
name="elfin_link3">
<inertial>
<origin
xyz="0.0357493010718184 -2.74303992683623E-06 -0.00441107040289549"
rpy="0 0 0" />
<mass
value="3.25128088148268" />
<inertia
ixx="0.00712273850158261"
ixy="4.14573354600121E-07"
ixz="-0.00240614658407673"
iyy="0.0131139013038093"
iyz="-1.53858261863385E-07"
izz="0.0123861174875263" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="file://$(find elfin_description)/meshes/elfin10/elfin_link3.STL" />
</geometry>
<material
name="">
<color
rgba="0.752941176470588 0.752941176470588 0.752941176470588 1" />
</material>
</visual>
<collision>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="file://$(find elfin_description)/meshes/elfin10/elfin_link3.STL" />
</geometry>
</collision>
</link>
<joint
name="elfin_joint3"
type="revolute">
<origin
xyz="0 0.48 0"
rpy="-3.1416 0 1.5708" />
<parent
link="elfin_link2" />
<child
link="elfin_link3" />
<axis
xyz="0 0 1" />
<limit
lower="-2.61"
upper="2.61"
effort="200"
velocity="1.57" />
</joint>
<link
name="elfin_link4">
<inertial>
<origin
xyz="3.8972274666868E-06 -0.065883038527668 0.300149807420176"
rpy="0 0 0" />
<mass
value="2.45076028364132" />
<inertia
ixx="0.0366043963749035"
ixy="2.14481800816891E-07"
ixz="1.74370308297691E-06"
iyy="0.0331005868546169"
iyz="0.00876542795872678"
izz="0.00689303506560623" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="file://$(find elfin_description)/meshes/elfin10/elfin_link4.STL" />
</geometry>
<material
name="">
<color
rgba="0.752941176470588 0.752941176470588 0.752941176470588 1" />
</material>
</visual>
<collision>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="file://$(find elfin_description)/meshes/elfin10/elfin_link4.STL" />
</geometry>
</collision>
</link>
<joint
name="elfin_joint4"
type="revolute">
<origin
xyz="0 0 0"
rpy="1.5708 0 1.5708" />
<parent
link="elfin_link3" />
<child
link="elfin_link4" />
<axis
xyz="0 0 1" />
<limit
lower="-3.14"
upper="3.14"
effort="200"
velocity="1.57" />
</joint>
<link
name="elfin_link5">
<inertial>
<origin
xyz="5.37084413168812E-06 -0.0262437975997385 -0.00353971828905618"
rpy="0 0 0" />
<mass
value="1.39234539037872" />
<inertia
ixx="0.0033625821599978"
ixy="2.21090591006111E-07"
ixz="-5.5969383984273E-07"
iyy="0.00182229285631246"
iyz="0.000639319386824375"
izz="0.00292731816323284" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="file://$(find elfin_description)/meshes/elfin10/elfin_link5.STL" />
</geometry>
<material
name="">
<color
rgba="0.752941176470588 0.752941176470588 0.752941176470588 1" />
</material>
</visual>
<collision>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="file://$(find elfin_description)/meshes/elfin10/elfin_link5.STL" />
</geometry>
</collision>
</link>
<joint
name="elfin_joint5"
type="revolute">
<origin
xyz="0 0 0.52"
rpy="-1.5708 0 0" />
<parent
link="elfin_link4" />
<child
link="elfin_link5" />
<axis
xyz="0 0 1" />
<limit
lower="-2.56"
upper="2.56"
effort="69"
velocity="1.57" />
</joint>
<link
name="elfin_link6">
<inertial>
<origin
xyz="-8.45231478862452E-06 -0.000388392932234965 0.144636177508013"
rpy="0 0 0" />
<mass
value="0.297803050744826" />
<inertia
ixx="0.000151919382929214"
ixy="-1.8598001023495E-09"
ixz="6.95653480101813E-08"
iyy="0.000155217204136347"
iyz="-6.63866631888914E-08"
izz="0.00027009779924127" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="file://$(find elfin_description)/meshes/elfin10/elfin_link6.STL" />
</geometry>
<material
name="">
<color
rgba="0.752941176470588 0.752941176470588 0.752941176470588 1" />
</material>
</visual>
<collision>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="file://$(find elfin_description)/meshes/elfin10/elfin_link6.STL" />
</geometry>
</collision>
</link>
<joint
name="elfin_joint6"
type="revolute">
<origin
xyz="0 0 0"
rpy="1.5708 0 0" />
<parent
link="elfin_link5" />
<child
link="elfin_link6" />
<axis
xyz="0 0 1" />
<limit
lower="-3.14"
upper="3.14"
effort="69"
velocity="1.57" />
</joint>
<link
name="elfin_end_link"/>
<joint
name="elfin_end_joint"
type="fixed">
<origin
xyz="0 0.0 0.1735"
rpy="0 0 0" />
<parent
link="elfin_link6" />
<child
link="elfin_end_link" />
<axis
xyz="0 0 0" />
</joint>
<link name="elfin_dummy_gripper"/>
<joint name="elfin_dummy_joint" type="fixed">
<origin
xyz="0 0.109 0"
rpy="${-PI/2} ${-PI/2} 0" />
<parent
link="elfin_link6" />
<child
link="elfin_dummy_gripper" />
</joint>
</robot>

View File

@ -0,0 +1,134 @@
<?xml version="1.0"?>
<robot xmlns:xacro="http://www.ros.org/wiki/xacro">
<xacro:macro name="elfin10_gazebo"
params="use_fake_hardware:=false
use_real_hardware:=true">
<gazebo>
<plugin filename="libgazebo_ros2_control.so" name="gazebo_ros2_control">
<robot_sim_type>gazebo_ros2_control/GazeboSystem</robot_sim_type>
<parameters>$(find elfin10_ros2_gazebo)/config/elfin_arm_controller.yaml</parameters>
</plugin>
</gazebo>
<ros2_control name="GazeboSystem" type="system">
<hardware>
<xacro:if value="${use_real_hardware}">
<plugin>elfin_hardware_interface/ElfinHWInterface</plugin>
</xacro:if>
<xacro:unless value="${use_real_hardware}">
<xacro:if value="${use_fake_hardware}">
<plugin>fake_components/GenericSystem</plugin>
</xacro:if>
<xacro:unless value="${use_fake_hardware}">
<plugin>gazebo_ros2_control/GazeboSystem</plugin>
</xacro:unless>
</xacro:unless>
</hardware>
<joint name="elfin_joint1">
<command_interface name="position">
<param name="min">-3.14</param>
<param name="max">3.14</param>
</command_interface>
<command_interface name="velocity">
<param name="min">-1.57</param>
<param name="max">1.57</param>
</command_interface>
<state_interface name="position"/>
<state_interface name="velocity"/>
</joint>
<joint name="elfin_joint2">
<command_interface name="position">
<param name="min">-2.35</param>
<param name="max">2.35</param>
</command_interface>
<command_interface name="velocity">
<param name="min">-1.57</param>
<param name="max">1.57</param>
</command_interface>
<state_interface name="position"/>
<state_interface name="velocity"/>
</joint>
<joint name="elfin_joint3">
<command_interface name="position">
<param name="min">-2.61</param>
<param name="max">2.61</param>
</command_interface>
<command_interface name="velocity">
<param name="min">-1.57</param>
<param name="max">1.57</param>
</command_interface>
<state_interface name="position"/>
<state_interface name="velocity"/>
</joint>
<joint name="elfin_joint4">
<command_interface name="position">
<param name="min">-3.14</param>
<param name="max">3.14</param>
</command_interface>
<command_interface name="velocity">
<param name="min">-1.57</param>
<param name="max">1.57</param>
</command_interface>
<state_interface name="position"/>
<state_interface name="velocity"/>
</joint>
<joint name="elfin_joint5">
<command_interface name="position">
<param name="min">-2.56</param>
<param name="max">2.56</param>
</command_interface>
<command_interface name="velocity">
<param name="min">-1.57</param>
<param name="max">1.57</param>
</command_interface>
<state_interface name="position"/>
<state_interface name="velocity"/>
</joint>
<joint name="elfin_joint6">
<command_interface name="position">
<param name="min">-3.14</param>
<param name="max">3.14</param>
</command_interface>
<command_interface name="velocity">
<param name="min">-1.57</param>
<param name="max">1.57</param>
</command_interface>
<state_interface name="position"/>
<state_interface name="velocity"/>
</joint>
</ros2_control>
<gazebo reference="elfin_link2">
<visual name='visual'>
<material>
<script>
<uri>file://media/materials/scripts/gazebo.material</uri>
<name>Gazebo/Grey</name>
</script>
</material>
</visual>
</gazebo>
<gazebo reference="elfin_link4">
<visual name='visual'>
<material>
<script>
<uri>file://media/materials/scripts/gazebo.material</uri>
<name>Gazebo/Grey</name>
</script>
</material>
</visual>
</gazebo>
</xacro:macro>
</robot>

View File

@ -0,0 +1,69 @@
<?xml version="1.0"?>
<robot xmlns:xacro="http://wiki.ros.org/xacro">
<xacro:macro name="elfin10_transmission" >
<transmission name="elfin_joint1_trans">
<type>transmission_interface/SimpleTransmission</type>
<joint name="elfin_joint1">
<hardwareInterface>hardware_interface/PositionJointInterface</hardwareInterface>
</joint>
<actuator name="elfin_joint1_motor">
<mechanicalReduction>1</mechanicalReduction>
</actuator>
</transmission>
<transmission name="elfin_joint2_trans">
<type>transmission_interface/SimpleTransmission</type>
<joint name="elfin_joint2">
<hardwareInterface>hardware_interface/PositionJointInterface</hardwareInterface>
</joint>
<actuator name="elfin_joint2_motor">
<mechanicalReduction>1</mechanicalReduction>
</actuator>
</transmission>
<transmission name="elfin_joint3_trans">
<type>transmission_interface/SimpleTransmission</type>
<joint name="elfin_joint3">
<hardwareInterface>hardware_interface/PositionJointInterface</hardwareInterface>
</joint>
<actuator name="elfin_joint3_motor">
<mechanicalReduction>1</mechanicalReduction>
</actuator>
</transmission>
<transmission name="elfin_joint4_trans">
<type>transmission_interface/SimpleTransmission</type>
<joint name="elfin_joint4">
<hardwareInterface>hardware_interface/PositionJointInterface</hardwareInterface>
</joint>
<actuator name="elfin_joint4_motor">
<mechanicalReduction>1</mechanicalReduction>
</actuator>
</transmission>
<transmission name="elfin_joint5_trans">
<type>transmission_interface/SimpleTransmission</type>
<joint name="elfin_joint5">
<hardwareInterface>hardware_interface/PositionJointInterface</hardwareInterface>
</joint>
<actuator name="elfin_joint5_motor">
<mechanicalReduction>1</mechanicalReduction>
</actuator>
</transmission>
<transmission name="elfin_joint6_trans">
<type>transmission_interface/SimpleTransmission</type>
<joint name="elfin_joint6">
<hardwareInterface>hardware_interface/PositionJointInterface</hardwareInterface>
</joint>
<actuator name="elfin_joint6_motor">
<mechanicalReduction>1</mechanicalReduction>
</actuator>
</transmission>
</xacro:macro>
</robot>

123
src/App.vue Normal file
View File

@ -0,0 +1,123 @@
<template>
<router-view/>
</template>
<script setup>
import useSettingsStore from '@/store/modules/settings'
import {handleThemeStyle} from '@/utils/theme'
onMounted(() => {
nextTick(() => {
//
handleThemeStyle(useSettingsStore().theme)
})
})
</script>
<style lang="scss">
//el-table
.el-table .el-table__header-wrapper th, .el-table .el-table__fixed-header-wrapper th {
background-color: var(--el-color-primary-light-9) !important;
}
//
.other-search-enter-from, .other-search-leave-to {
opacity: 0;
transform: translateX(20px);
}
.other-search-enter-active {
transition: all .5s;
}
.other-search-leave-active {
transition: all .6s;
}
.el-dialog {
.task-title {
height: 30px;
font-weight: 900;
}
.custom-block {
padding: 8px 16px;
background-color: rgba(var(--el-color-primary-rgb), .1);
border-radius: 4px;
border-left: 5px solid var(--el-color-primary);
margin-top: 16px;
}
.draggable-group {
height: calc(100% - 30px);
overflow-y: auto;
border: 1px solid #e7eaec;
border-radius: 4px;
.dragClass {
background-color: rgba(var(--el-color-primary-rgb), .1);
}
.list-group-item {
border-bottom: none;
cursor: pointer;
padding: 10px 20px;
font-size: 14px;
display: flex;
align-items: center;
user-select: none;
i {
margin-right: 5px;
}
&:hover {
background-color: rgba(var(--el-color-primary-rgb), .1);
}
&:last-child {
border-bottom: none;
}
&:first-child {
border-top: none;
}
}
}
&.file-upload {
.el-upload {
border: 1px dashed var(--el-border-color);
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
transition: var(--el-transition-duration-fast);
}
}
.el-dialog__body {
.file-uploader {
display: flex;
flex-direction: column;
justify-content: center;
}
}
}
.test-log {
.el-drawer__body {
padding: 0;
padding-right: 30px;
}
}
.test-config {
.el-drawer__header {
margin-bottom: 20px;
}
.el-drawer__body {
padding: 0 20px;
}
}
</style>

461
src/adapters/URDFAdapter.js Normal file
View File

@ -0,0 +1,461 @@
/**
* URDF Adapter
* Converts urdf-loaders result to unified model
*/
import * as THREE from 'three';
import { UnifiedRobotModel, Link, Joint, JointLimits, VisualGeometry, CollisionGeometry, InertialProperties } from '@/models/UnifiedRobotModel.js';
export class URDFAdapter {
/**
* Convert urdf-loaders robot object to unified model
* @param {THREE.Group} robot - Robot object returned by urdf-loaders
* @param {string} urdfXML - Original URDF XML content (optional, for extracting inertial data)
* @returns {UnifiedRobotModel}
*/
static convert(robot, urdfXML = null) {
const model = new UnifiedRobotModel();
model.name = robot.name || 'robot';
model.threeObject = robot;
// Mark model type as URDF
if (!robot.userData) robot.userData = {};
robot.userData.type = 'urdf';
if (!robot.links || !robot.joints) {
console.warn('URDF model missing links or joints information');
return model;
}
// If XML provided, parse inertial data
let inertialData = {};
if (urdfXML) {
inertialData = this.parseInertialFromXML(urdfXML);
}
// Convert links
Object.values(robot.links).forEach(urdfLink => {
const link = this.convertLink(urdfLink);
// If urdf-loader didn't parse inertial, get from XML
if (!link.inertial && inertialData[urdfLink.name]) {
link.inertial = inertialData[urdfLink.name];
}
model.addLink(link);
});
// Convert joints
Object.values(robot.joints).forEach(urdfJoint => {
const joint = this.convertJoint(urdfJoint);
model.addJoint(joint);
});
// If XML provided, supplement effort and velocity from XML (urdf-loaders may not have parsed)
if (urdfXML) {
this.supplementJointLimitsFromXML(model, urdfXML);
}
// Find root link (link that is not a child of any joint)
const allChildren = new Set(
Array.from(model.joints.values()).map(j => j.child).filter(c => c)
);
const rootLinks = Array.from(model.links.keys()).filter(
name => !allChildren.has(name)
);
if (rootLinks.length > 0) {
model.rootLink = rootLinks[0];
}
// Enhance materials for better lighting (MuJoCo style)
this.enhanceMaterials(robot);
return model;
}
/**
* Enhance materials for better lighting (MuJoCo style)
* Applies consistent shininess and specular properties to all materials
* Directly modifies materials in place to ensure changes persist
*/
static enhanceMaterials(robotObject) {
robotObject.traverse((child) => {
if (child.isMesh && child.material) {
// Handle material arrays
if (Array.isArray(child.material)) {
child.material = child.material.map(mat => {
return this.enhanceSingleMaterial(mat);
});
} else {
child.material = this.enhanceSingleMaterial(child.material);
}
}
});
}
/**
* Enhance a single material with better lighting properties
* Returns enhanced material (may be cloned or modified in place)
* Saves original properties for lighting toggle
*/
static enhanceSingleMaterial(material) {
if (material.isMeshPhongMaterial || material.isMeshStandardMaterial) {
// Save original properties if not already saved (for lighting toggle)
if (material.userData.originalShininess === undefined) {
material.userData.originalShininess = material.shininess !== undefined ? material.shininess : 30;
// IMPORTANT: Save original color to preserve URDF material colors
if (!material.userData.originalColor && material.color) {
material.userData.originalColor = material.color.clone();
}
// Save original specular - if material had no specular, save null
if (!material.specular) {
material.userData.originalSpecular = null;
} else if (material.specular.isColor) {
const spec = material.specular;
if (spec.r < 0.1 && spec.g < 0.1 && spec.b < 0.1) {
material.userData.originalSpecular = null; // Likely default
} else {
material.userData.originalSpecular = spec.clone();
}
} else if (typeof material.specular === 'number') {
if (material.specular === 0x111111 || material.specular < 0x111111) {
material.userData.originalSpecular = null;
} else {
material.userData.originalSpecular = new THREE.Color(material.specular);
}
} else {
material.userData.originalSpecular = null;
}
}
// IMPORTANT: Preserve original color from URDF material definitions
// Do not modify material.color - urdf-loaders already sets the correct color
// Apply enhanced lighting (default enabled)
// Increase shininess for better highlights
if (material.shininess === undefined || material.shininess < 50) {
material.shininess = 50;
}
// Enhance specular reflection - ensure it's a Color object with proper values
if (!material.specular) {
material.specular = new THREE.Color(0.3, 0.3, 0.3);
} else if (material.specular.isColor) {
// If it's already a Color object, update values
if (material.specular.r < 0.2 || material.specular.g < 0.2 || material.specular.b < 0.2) {
material.specular.setRGB(0.3, 0.3, 0.3);
}
} else if (typeof material.specular === 'number') {
// Convert number to Color object
if (material.specular < 0x333333) {
material.specular = new THREE.Color(0.3, 0.3, 0.3);
} else {
// Convert hex to Color
material.specular = new THREE.Color(material.specular);
if (material.specular.r < 0.2) {
material.specular.setRGB(0.3, 0.3, 0.3);
}
}
}
// Mark material as needing update
material.needsUpdate = true;
return material;
} else if (material.type === 'MeshBasicMaterial') {
// Convert MeshBasicMaterial to MeshPhongMaterial
const oldMaterial = material;
const envMap = typeof window !== 'undefined' && window.app?.sceneManager?.environmentManager?.getEnvironmentMap();
const newMaterial = new THREE.MeshPhongMaterial({
color: oldMaterial.color,
map: oldMaterial.map,
transparent: oldMaterial.transparent,
opacity: oldMaterial.opacity,
side: oldMaterial.side,
shininess: 50,
specular: new THREE.Color(0.3, 0.3, 0.3),
envMap: envMap || null,
reflectivity: envMap ? 0.3 : 0
});
// Save original properties for lighting toggle
newMaterial.userData.originalShininess = 30;
newMaterial.userData.originalSpecular = null; // MeshBasicMaterial had no specular
return newMaterial;
}
return material;
}
static convertLink(urdfLink) {
const link = new Link(urdfLink.name);
link.threeObject = urdfLink;
// Convert inertial properties
if (urdfLink.inertial) {
link.inertial = this.convertInertial(urdfLink.inertial);
}
// Note: urdf-loaders has already converted visual and collision to Three.js objects
// We mainly extract metadata, actual meshes are in threeObject
return link;
}
static convertJoint(urdfJoint) {
// URDF joint object has jointType property (not type)
const jointType = urdfJoint.jointType || urdfJoint.type || 'fixed';
const joint = new Joint(urdfJoint.name, jointType);
// Extract parent name (urdf-loader returns Three.js object reference)
joint.parent = urdfJoint.parent?.name || null;
// child may not be in urdfJoint.child, but in Three.js children array
// In Three.js scene graph, Joint is parent node, Child Link is Joint's child node
if (urdfJoint.child && urdfJoint.child.name) {
joint.child = urdfJoint.child.name;
} else if (urdfJoint.children && urdfJoint.children.length > 0) {
// Find Link object from children array
const childLink = urdfJoint.children.find(child =>
child.isURDFLink || child.type === 'URDFLink'
);
if (childLink) {
joint.child = childLink.name;
}
}
joint.threeObject = urdfJoint;
// Convert origin
if (urdfJoint.origin) {
joint.origin = {
xyz: urdfJoint.origin.xyz || [0, 0, 0],
rpy: urdfJoint.origin.rpy || [0, 0, 0]
};
}
// Convert axis
if (urdfJoint.axis) {
joint.axis = { xyz: urdfJoint.axis.xyz || [0, 0, 1] };
}
// Convert limits
if (urdfJoint.limit) {
joint.limits = this.convertLimits(urdfJoint.limit);
}
// Get current value
if (urdfJoint.angle !== undefined) {
joint.currentValue = urdfJoint.angle;
} else if (urdfJoint.jointValue !== undefined) {
joint.currentValue = urdfJoint.jointValue;
}
return joint;
}
static convertLimits(limit) {
const limits = new JointLimits();
if (limit.lower !== undefined) limits.lower = limit.lower;
if (limit.upper !== undefined) limits.upper = limit.upper;
if (limit.effort !== undefined) limits.effort = limit.effort;
if (limit.velocity !== undefined) limits.velocity = limit.velocity;
return limits;
}
/**
* Supplement joint effort and velocity information from URDF XML
* (because urdf-loaders may not have parsed these attributes)
*/
static supplementJointLimitsFromXML(model, urdfXML) {
try {
const parser = new DOMParser();
const doc = parser.parseFromString(urdfXML, 'text/xml');
const jointElements = doc.querySelectorAll('joint');
jointElements.forEach(jointEl => {
const jointName = jointEl.getAttribute('name');
const joint = model.joints.get(jointName);
if (joint) {
const limitEl = jointEl.querySelector('limit');
if (limitEl) {
if (!joint.limits) {
joint.limits = new JointLimits();
}
// Read effort and velocity
const effort = limitEl.getAttribute('effort');
const velocity = limitEl.getAttribute('velocity');
if (effort !== null) {
joint.limits.effort = parseFloat(effort);
}
if (velocity !== null) {
joint.limits.velocity = parseFloat(velocity);
}
// Also ensure lower and upper are correctly set
const lower = limitEl.getAttribute('lower');
const upper = limitEl.getAttribute('upper');
if (lower !== null) {
joint.limits.lower = parseFloat(lower);
}
if (upper !== null) {
joint.limits.upper = parseFloat(upper);
}
}
}
});
} catch (error) {
console.error('Failed to supplement joint limit information from XML:', error);
}
}
static convertInertial(inertial) {
const props = new InertialProperties();
if (inertial.mass !== undefined) props.mass = inertial.mass;
if (inertial.origin) {
props.origin = {
xyz: inertial.origin.xyz || [0, 0, 0],
rpy: inertial.origin.rpy || [0, 0, 0]
};
}
if (inertial.ixx !== undefined) props.ixx = inertial.ixx;
if (inertial.iyy !== undefined) props.iyy = inertial.iyy;
if (inertial.izz !== undefined) props.izz = inertial.izz;
if (inertial.ixy !== undefined) props.ixy = inertial.ixy;
if (inertial.ixz !== undefined) props.ixz = inertial.ixz;
if (inertial.iyz !== undefined) props.iyz = inertial.iyz;
return props;
}
/**
* Parse inertial data from URDF XML
* @param {string} xmlContent - URDF XML content
* @returns {Object} Mapping from link names to inertial data
*/
static parseInertialFromXML(xmlContent) {
const inertialData = {};
try {
const parser = new DOMParser();
const xmlDoc = parser.parseFromString(xmlContent, 'text/xml');
// Get all link elements
const links = xmlDoc.getElementsByTagName('link');
for (let i = 0; i < links.length; i++) {
const linkElement = links[i];
const linkName = linkElement.getAttribute('name');
// Find inertial element
const inertialElement = linkElement.getElementsByTagName('inertial')[0];
if (!inertialElement) continue;
const inertial = new InertialProperties();
// Parse mass
const massElement = inertialElement.getElementsByTagName('mass')[0];
if (massElement) {
inertial.mass = parseFloat(massElement.getAttribute('value'));
}
// Parse origin
const originElement = inertialElement.getElementsByTagName('origin')[0];
if (originElement) {
const xyz = originElement.getAttribute('xyz');
const rpy = originElement.getAttribute('rpy');
inertial.origin = {
xyz: xyz ? xyz.split(' ').map(parseFloat) : [0, 0, 0],
rpy: rpy ? rpy.split(' ').map(parseFloat) : [0, 0, 0]
};
} else {
inertial.origin = { xyz: [0, 0, 0], rpy: [0, 0, 0] };
}
// Parse inertia
const inertiaElement = inertialElement.getElementsByTagName('inertia')[0];
if (inertiaElement) {
inertial.ixx = parseFloat(inertiaElement.getAttribute('ixx')) || 0;
inertial.iyy = parseFloat(inertiaElement.getAttribute('iyy')) || 0;
inertial.izz = parseFloat(inertiaElement.getAttribute('izz')) || 0;
inertial.ixy = parseFloat(inertiaElement.getAttribute('ixy')) || 0;
inertial.ixz = parseFloat(inertiaElement.getAttribute('ixz')) || 0;
inertial.iyz = parseFloat(inertiaElement.getAttribute('iyz')) || 0;
}
inertialData[linkName] = inertial;
}
} catch (error) {
console.error('Failed to parse URDF XML inertial data:', error);
}
return inertialData;
}
/**
* Set joint angle (using urdf-loaders' setJointValue method)
* Reference URDFClasses.js setJointValue implementation
*/
static setJointAngle(joint, angle, ignoreLimits = false) {
joint.currentValue = angle;
// URDF format: use urdf-loader's setJointValue method
if (joint.threeObject) {
// If ignoring limits, temporarily modify URDF joint object's limit values
let originalLimits = null;
if (ignoreLimits && joint.threeObject.limit) {
originalLimits = {
lower: joint.threeObject.limit.lower,
upper: joint.threeObject.limit.upper
};
joint.threeObject.limit.lower = -Math.PI * 2;
joint.threeObject.limit.upper = Math.PI * 2;
}
// Prefer setJointValue method (urdf-loader's standard method)
if (typeof joint.threeObject.setJointValue === 'function') {
joint.threeObject.setJointValue(angle);
// Restore original limits
if (originalLimits && joint.threeObject.limit) {
joint.threeObject.limit.lower = originalLimits.lower;
joint.threeObject.limit.upper = originalLimits.upper;
}
return;
} else if (typeof joint.threeObject.setAngle === 'function') {
joint.threeObject.setAngle(angle);
// Restore original limits
if (originalLimits && joint.threeObject.limit) {
joint.threeObject.limit.lower = originalLimits.lower;
joint.threeObject.limit.upper = originalLimits.upper;
}
return;
}
// Restore original limits (if none of the above methods executed)
if (originalLimits && joint.threeObject.limit) {
joint.threeObject.limit.lower = originalLimits.lower;
joint.threeObject.limit.upper = originalLimits.upper;
}
// If none available, manually set rotation
// Note: For URDF, we usually shouldn't reach here, as urdf-loader should provide setJointValue
console.warn(`Joint ${joint.name} has no setJointValue or setAngle method, attempting manual setup`);
if (joint.type === 'revolute' || joint.type === 'continuous') {
const axis = joint.axis ? new THREE.Vector3(...joint.axis.xyz).normalize() : new THREE.Vector3(0, 0, 1);
// Note: joint.threeObject may not be a Three.js object, cannot directly set rotation
if (joint.threeObject.rotation) {
joint.threeObject.rotation.setFromAxisAngle(axis, angle);
}
} else if (joint.type === 'prismatic') {
const axis = joint.axis ? new THREE.Vector3(...joint.axis.xyz).normalize() : new THREE.Vector3(1, 0, 0);
if (joint.threeObject.position) {
joint.threeObject.position.copy(axis.multiplyScalar(angle));
}
}
} else {
console.warn(`Joint ${joint.name} has no threeObject`);
}
}
}

View File

@ -0,0 +1,624 @@
/**
* Xacro Adapter
*
* Parses ROS Xacro files and converts them to URDF format using xacro-parser.
*
* Supported Features:
* - Property definitions and substitutions (xacro:property)
* - Macro definitions with parameters (xacro:macro)
* - Conditional blocks (xacro:if, xacro:unless)
* - File inclusions (xacro:include) with package:// and relative paths
* - Python-style boolean constants (True/False)
* - Arithmetic expressions and property evaluation
*
* Compatibility:
* - ROS Jade and later (inOrder=true, requirePrefix=true, localProperties=true)
* - Automatically injects True/False constants for compatibility with ROS xacro files
*
* Usage:
* - Upload all related xacro files (main file and included files)
* - The adapter automatically resolves file inclusions from the uploaded file map
* - Mesh files are resolved using package:// paths or relative paths
*/
import { XacroParser } from 'xacro-parser';
import { URDFAdapter } from './URDFAdapter.js';
export class XacroAdapter {
/**
* Parse xacro content and convert to unified model
* @param {string} xacroContent - Xacro file content
* @param {string} fileName - Xacro file name (for working path)
* @param {Map} fileMap - File map (path -> File object)
* @param {File} file - Original file object (optional)
* @returns {Promise<UnifiedRobotModel>}
*/
static async parse(xacroContent, fileName, fileMap = null, file = null) {
try {
// Create xacro parser
const parser = new XacroParser();
// Configure parser for ROS Jade and later (default settings)
parser.inOrder = true;
parser.requirePrefix = true;
parser.localProperties = true;
// Set working path (directory where xacro file is located)
const workingPath = fileName.includes('/')
? fileName.substring(0, fileName.lastIndexOf('/') + 1)
: '';
parser.workingPath = workingPath;
// Inject Python-style boolean constants as xacro properties
// Some xacro files use True/False (capitalized) in conditions
xacroContent = this.injectBooleanConstants(xacroContent);
// Extract and set xacro arguments with their default values
// Also add common ROS arguments that might be used without definition
const xacroArgs = this.extractXacroArguments(xacroContent);
// Add common ROS arguments with sensible defaults if not already defined
if (!xacroArgs.hasOwnProperty('DEBUG')) {
xacroArgs.DEBUG = 'false';
}
if (!xacroArgs.hasOwnProperty('SELF_COLLIDE')) {
xacroArgs.SELF_COLLIDE = 'false';
}
parser.arguments = xacroArgs;
// If fileMap provided, setup custom file loader
if (fileMap) {
parser.getFileContents = async (path) => {
return await this.loadFileFromMap(path, fileMap, workingPath);
};
}
// Parse xacro to URDF XML
const urdfXML = await parser.parse(xacroContent);
// Convert XMLDocument to string
const serializer = new XMLSerializer();
let urdfString = serializer.serializeToString(urdfXML);
// Clean up the XML string - remove empty xmlns attributes that might cause issues
urdfString = urdfString.replace(/\sxmlns=""/g, '');
// Re-parse the URDF string with DOMParser to create a clean XMLDocument
// This removes any xacro-specific nodes that urdf-loader might not handle
const domParser = new DOMParser();
const cleanUrdfXML = domParser.parseFromString(urdfString, 'text/xml');
// Check for parsing errors
const parseError = cleanUrdfXML.querySelector('parsererror');
if (parseError) {
console.error('[XacroAdapter] XML parsing error:', parseError.textContent);
throw new Error('Generated URDF has invalid XML: ' + parseError.textContent);
}
// Remove problematic elements that urdf-loader might not handle well
// Remove gazebo elements (plugins, sensors, etc.)
const gazeboElements = cleanUrdfXML.querySelectorAll('gazebo');
gazeboElements.forEach(el => el.parentNode?.removeChild(el));
// Remove transmission elements (these are for Gazebo/ROS control)
const transmissionElements = cleanUrdfXML.querySelectorAll('transmission');
transmissionElements.forEach(el => el.parentNode?.removeChild(el));
// Remove visual/collision elements with empty geometry tags
this.removeEmptyGeometry(cleanUrdfXML);
// Clean up empty text nodes and comments that might cause issues
this.cleanXMLNodes(cleanUrdfXML.documentElement);
// Convert the clean XMLDocument back to string for URDFLoader
const finalUrdfString = serializer.serializeToString(cleanUrdfXML);
// Now use existing URDF loading infrastructure
// Import URDFLoader dynamically
const urdfModule = await import('urdf-loader');
const URDFLoader = urdfModule.URDFLoader || urdfModule.default || urdfModule;
return new Promise((resolve, reject) => {
const loader = new URDFLoader();
loader.parseCollision = true;
// Extract directory where URDF file is located
const urdfDir = workingPath;
// If file map provided, setup resource loader (same as URDF loading)
if (fileMap) {
// Parse URDF content, find all used package names
const packages = this.extractPackagesFromURDF(finalUrdfString);
// Build package map
const packageMap = {};
packages.forEach(pkg => {
packageMap[pkg] = pkg;
});
packageMap[''] = '';
loader.packages = packageMap;
// Set URL Modifier (same as URDF loading)
const urlModifier = (url) => {
// Handle blob URLs
if (url.startsWith('blob:')) {
const blobMatch = url.match(/^blob:https?:\/\/[^\/]+\/(.+)$/);
if (blobMatch && blobMatch[1]) {
const fileName = blobMatch[1];
if (/\.(jpg|jpeg|png|gif|bmp|tga|tiff|webp|dae|stl|obj|gltf|glb)$/i.test(fileName)) {
url = fileName;
} else {
return url;
}
} else {
return url;
}
}
const isTextureFile = /\.(jpg|jpeg|png|gif|bmp|tga|tiff|webp)$/i.test(url);
const isMeshFile = /\.(dae|stl|obj|gltf|glb)$/i.test(url);
let meshPath = url;
// Remove http:// or https:// prefix
if (meshPath.startsWith('http://') || meshPath.startsWith('https://')) {
try {
const urlObj = new URL(meshPath);
meshPath = urlObj.pathname;
if (meshPath.startsWith('/')) {
meshPath = meshPath.substring(1);
}
} catch (e) {
// Invalid URL, use as-is
}
}
// Remove package:// prefix
if (meshPath.startsWith('package://')) {
meshPath = meshPath.replace(/^package:\/\//, '');
const parts = meshPath.split('/');
if (parts.length > 1) {
meshPath = parts.slice(1).join('/');
}
}
// Remove leading ./
meshPath = meshPath.replace(/^\.\//, '');
// Handle relative paths
let normalizedPath = meshPath;
if (meshPath.includes('../')) {
const parts = meshPath.split('/');
const resolvedParts = [];
for (const part of parts) {
if (part === '..') {
resolvedParts.pop();
} else if (part !== '.' && part !== '') {
resolvedParts.push(part);
}
}
normalizedPath = resolvedParts.join('/');
}
// Build full path
const fullPath = urdfDir + normalizedPath;
const altPath = urdfDir + meshPath;
// Find file in fileMap
let matchedFile = fileMap.get(fullPath);
if (!matchedFile && altPath !== fullPath) {
matchedFile = fileMap.get(altPath);
}
if (!matchedFile) {
matchedFile = fileMap.get(normalizedPath);
}
if (!matchedFile) {
matchedFile = fileMap.get(meshPath);
}
if (!matchedFile) {
const targetFileName = normalizedPath.split('/').pop() || meshPath.split('/').pop();
for (const [key, file] of fileMap.entries()) {
const keyFileName = key.split('/').pop();
if (keyFileName === targetFileName) {
matchedFile = file;
break;
}
}
}
if (matchedFile) {
const bloburl = URL.createObjectURL(matchedFile);
return bloburl;
}
return url;
};
loader.manager.setURLModifier(urlModifier);
// Custom loadMeshCb (same as URDF loading)
const originalLoadMeshCb = loader.loadMeshCb || loader.defaultMeshLoader.bind(loader);
loader.loadMeshCb = (path, manager, done) => {
this.findFileInMapByPath(path, fileMap, urdfDir).then(file => {
if (file) {
const ext = (file.name || path).toLowerCase().split('.').pop();
this.loadMeshFileAsync(file, ext, manager).then(meshObject => {
if (meshObject) {
done(meshObject, null);
} else {
done(null, new Error(`Failed to load mesh file: ${path}`));
}
}).catch(err => {
console.error(`Failed to load mesh: ${path}`, err);
done(null, err);
});
} else {
let stlPath = path
stlPath = stlPath.replace('file:///', 'inspection/elfin10')
console.log(stlPath, manager, done)
originalLoadMeshCb(path, manager, done);
}
}).catch(error => {
console.error(`Failed to find file: ${path}`, error);
originalLoadMeshCb(path, manager, done);
});
};
}
// Create temporary URL for URDF content
const blob = new Blob([finalUrdfString], { type: 'text/xml' });
const url = URL.createObjectURL(blob);
loader.load(url, (robot) => {
URL.revokeObjectURL(url);
// Convert to unified model using URDFAdapter
try {
const model = URDFAdapter.convert(robot, finalUrdfString);
resolve(model);
} catch (error) {
console.error('[XacroAdapter] URDF conversion error:', error);
reject(new Error('URDF conversion failed: ' + error.message));
}
}, undefined, (error) => {
URL.revokeObjectURL(url);
console.error('[XacroAdapter] URDF loading error:', error);
reject(new Error('URDF loading failed: ' + (error.message || error)));
});
});
} catch (error) {
console.error('[XacroAdapter] Xacro parsing error:', error);
throw new Error('Xacro parsing failed: ' + error.message);
}
}
/**
* Load file from fileMap for xacro includes
* @param {string} path - File path
* @param {Map} fileMap - File map
* @param {string} workingPath - Working directory
* @returns {Promise<string>}
*/
static async loadFileFromMap(path, fileMap, workingPath) {
console.log('path', path)
console.log('fileMap', fileMap)
console.log('workingPath', workingPath)
// Clean path - remove leading slash if present
let cleanPath = path;
// if (cleanPath.startsWith('/')) {
// cleanPath = cleanPath.substring(1);
// }
// // Remove leading ./
// cleanPath = cleanPath.replace(/^\.\//, '');
cleanPath = cleanPath.split('///')[1];
console.log('cleanPath', cleanPath)
// Try different path combinations
const possiblePaths = [
cleanPath,
workingPath + cleanPath,
path,
workingPath + path.replace(/^\/+/, ''),
// Also try without the first directory component
cleanPath.includes('/') ? cleanPath.substring(cleanPath.indexOf('/') + 1) : cleanPath,
];
console.log('possiblePaths', possiblePaths)
// Try each path
for (const tryPath of possiblePaths) {
console.log('tryPath', tryPath)
const file = fileMap.get(tryPath);
if (file) {
console.log(12345)
const content = await file.text();
return content;
}
}
// Try filename only match
const fileName = cleanPath.split('/').pop();
for (const [key, file] of fileMap.entries()) {
const keyFileName = key.split('/').pop();
if (keyFileName === fileName) {
const content = await file.text();
return content;
}
}
console.error('[XacroAdapter] Cannot find included file:', path);
throw new Error(`Cannot find included file: ${path}`);
}
/**
* Remove visual/collision elements with empty geometry tags
* xacro-parser may not fully expand conditional geometry (xacro:if)
* We'll just remove these empty visual elements rather than try to fix them
* @param {XMLDocument} xmlDoc - XML document to clean
*/
static removeEmptyGeometry(xmlDoc) {
// Find all geometry elements
const geometryElements = xmlDoc.querySelectorAll('geometry');
geometryElements.forEach(geom => {
// Check if geometry is empty (no child elements)
if (!geom.children || geom.children.length === 0) {
// Find the parent visual or collision element
let parent = geom.parentNode;
if (parent) {
const grandParent = parent.parentNode;
if (grandParent) {
if (parent.nodeName === 'visual' || parent.nodeName === 'collision') {
// Remove empty visual/collision elements
grandParent.removeChild(parent);
}
}
}
}
});
}
/**
* Recursively clean XML nodes - remove empty text nodes and comments
* @param {Element} element - Element to clean
*/
static cleanXMLNodes(element) {
if (!element || !element.childNodes) return;
const nodesToRemove = [];
// Collect nodes to remove
for (let i = 0; i < element.childNodes.length; i++) {
const node = element.childNodes[i];
// Remove comments
if (node.nodeType === Node.COMMENT_NODE) {
nodesToRemove.push(node);
}
// Remove empty or whitespace-only text nodes
else if (node.nodeType === Node.TEXT_NODE) {
if (!node.nodeValue || node.nodeValue.trim() === '') {
nodesToRemove.push(node);
}
}
// Recursively clean element nodes
else if (node.nodeType === Node.ELEMENT_NODE) {
this.cleanXMLNodes(node);
}
}
// Remove collected nodes
nodesToRemove.forEach(node => {
element.removeChild(node);
});
}
/**
* Extract package names from URDF content
* @param {string} urdfContent - URDF content
* @returns {Set<string>}
*/
static extractPackagesFromURDF(urdfContent) {
const packages = new Set();
const packageRegex = /package:\/\/([^\/]+)/g;
let match;
while ((match = packageRegex.exec(urdfContent)) !== null) {
packages.add(match[1]);
}
return packages;
}
/**
* Find file in fileMap by path
* @param {string} path - File path
* @param {Map} fileMap - File map
* @param {string} urdfDir - URDF directory
* @returns {Promise<File|null>}
*/
static async findFileInMapByPath(path, fileMap, urdfDir) {
let meshPath = path;
// Remove blob: prefix
meshPath = meshPath.replace(/^blob:[^\/]+\//, '');
// Remove package:// prefix
if (meshPath.startsWith('package://')) {
meshPath = meshPath.replace(/^package:\/\//, '');
const parts = meshPath.split('/');
if (parts.length > 1) {
meshPath = parts.slice(1).join('/');
}
}
// Remove leading ./
meshPath = meshPath.replace(/^\.\//, '');
// Build full path
const fullPath = urdfDir + meshPath;
// Try full path
let file = fileMap.get(fullPath);
if (file) return file;
// Try path without directory
file = fileMap.get(meshPath);
if (file) return file;
// Try filename match
const targetFileName = meshPath.split('/').pop();
for (const [key, f] of fileMap.entries()) {
const keyFileName = key.split('/').pop();
if (keyFileName === targetFileName) {
return f;
}
}
return null;
}
/**
* Load mesh file asynchronously
* @param {File} file - File object
* @param {string} ext - File extension
* @param {THREE.LoadingManager} manager - Loading manager
* @returns {Promise<THREE.Object3D>}
*/
static async loadMeshFileAsync(file, ext, manager) {
const THREE = await import('three');
const blobUrl = URL.createObjectURL(file);
try {
let meshObject = null;
switch (ext) {
case 'stl': {
const { STLLoader } = await import('three/examples/jsm/loaders/STLLoader.js');
const stlLoader = new STLLoader(manager);
const stlGeometry = await new Promise((resolve, reject) => {
stlLoader.load(blobUrl, resolve, undefined, reject);
});
const stlMaterial = new THREE.MeshPhongMaterial();
meshObject = new THREE.Mesh(stlGeometry, stlMaterial);
break;
}
case 'dae': {
const { ColladaLoader } = await import('three/examples/jsm/loaders/ColladaLoader.js');
const colladaLoader = new ColladaLoader(manager);
const colladaModel = await new Promise((resolve, reject) => {
colladaLoader.load(blobUrl, resolve, undefined, reject);
});
meshObject = colladaModel.scene;
// Remove lights
if (meshObject && meshObject.traverse) {
const lightsToRemove = [];
meshObject.traverse(child => {
if (child.isLight) {
lightsToRemove.push(child);
}
});
lightsToRemove.forEach(light => {
if (light.parent) {
light.parent.remove(light);
}
});
}
break;
}
case 'obj': {
const { OBJLoader } = await import('three/examples/jsm/loaders/OBJLoader.js');
const objLoader = new OBJLoader(manager);
meshObject = await new Promise((resolve, reject) => {
objLoader.load(blobUrl, resolve, undefined, reject);
});
break;
}
case 'gltf':
case 'glb': {
const { GLTFLoader } = await import('three/examples/jsm/loaders/GLTFLoader.js');
const gltfLoader = new GLTFLoader(manager);
const gltfModel = await new Promise((resolve, reject) => {
gltfLoader.load(blobUrl, resolve, undefined, reject);
});
meshObject = gltfModel.scene;
break;
}
default:
console.warn(`Unsupported file format: ${ext}`);
URL.revokeObjectURL(blobUrl);
return null;
}
URL.revokeObjectURL(blobUrl);
return meshObject;
} catch (error) {
URL.revokeObjectURL(blobUrl);
console.error(`Failed to load mesh file: ${file.name}`, error);
throw error;
}
}
/**
* Inject Python-style boolean constants (True/False) into xacro content
* Some xacro files use capitalized True/False in conditional expressions
* The parameters are passed as strings ("True", "False") but compared to constants
* We define the constants to match the string values for comparison to work
* @param {string} xacroContent - Original xacro content
* @returns {string} - Modified xacro content with boolean constants defined
*/
static injectBooleanConstants(xacroContent) {
// Find the robot tag opening
const robotTagMatch = xacroContent.match(/(<robot[^>]*>)/);
if (!robotTagMatch) {
return xacroContent;
}
// Define True/False as string properties for comparison
// When mirror_dae="True" is passed, it becomes the string "True"
// We want ${mirror_dae == True} to work, so True must also be "True"
const booleanProperties = `
<!-- Injected by XacroAdapter: Python-style boolean constants -->
<xacro:property name="True" value="True"/>
<xacro:property name="False" value="False"/>
`;
const insertPosition = robotTagMatch.index + robotTagMatch[0].length;
return xacroContent.substring(0, insertPosition) +
booleanProperties +
xacroContent.substring(insertPosition);
}
/**
* Extract xacro arguments and their default values from xacro content
* Xacro files can define arguments using <xacro:arg name="..." default="..."/>
* These need to be provided to the parser via parser.arguments
* @param {string} xacroContent - Xacro file content
* @returns {Object} - Object with argument names as keys and default values
*/
static extractXacroArguments(xacroContent) {
const args = {};
// Match <xacro:arg name="NAME" default="VALUE"/>
// Also match <arg name="NAME" default="VALUE"/> (without xacro: prefix)
const argPattern = /<(?:xacro:)?arg\s+name=["']([^"']+)["'](?:\s+default=["']([^"']*)["'])?/g;
let match;
while ((match = argPattern.exec(xacroContent)) !== null) {
const argName = match[1];
const defaultValue = match[2] || 'false'; // Default to 'false' if no default specified
args[argName] = defaultValue;
}
return args;
}
}

18
src/api/device/arm.js Normal file
View File

@ -0,0 +1,18 @@
import request from '@/utils/request'
// 移动机械臂
export function moveJ(data) {
return request({
url: '/api/humanoid/moveJ',
method: 'post',
data: data
})
}
// 获取机械臂状态
export function status(data) {
return request({
url: '/api/humanoid/status',
method: 'post',
data: data
})
}

19
src/api/device/dexHand.js Normal file
View File

@ -0,0 +1,19 @@
import request from '@/utils/request'
// 获取灵巧手状态
export function status(query) {
return request({
url: '/api/dexHand/status',
method: 'get',
params: query
})
}
// 设置灵巧手弯曲角度
export function setDexHandAngle(query) {
return request({
url: '/api/dexHand/angle',
method: 'get',
params: query
})
}

63
src/api/device/flow.js Normal file
View File

@ -0,0 +1,63 @@
import request from '@/utils/request'
// 发布流程
export function flowDeploy(data) {
return request({
url: '/flow/publish',
method: 'post',
data: data
})
}
export function flowExecute(data) {
return request({
url: '/flow/execute',
method: 'post',
data: data
})
}
export function flowAction(data) {
return request({
url: '/flow/action',
method: 'post',
data: data
})
}
export function flowExecuteTrial(data) {
return request({
url: '/flow/executeTrial',
method: 'post',
data: data
})
}
export function flowView(data) {
return request({
url: '/flow/view',
method: 'post',
data: data
})
}
export function flowPause(instId) {
return request({
url: `/flow/pause/${instId}`,
method: 'post'
})
}
export function flowResume(instId) {
return request({
url: `/flow/resume/${instId}`,
method: 'post'
})
}
export function flowStop(instId) {
return request({
url: `/flow/stop/${instId}`,
method: 'post'
})
}

View File

@ -0,0 +1,46 @@
import request from '@/utils/request'
// 开始录音
export function startMicrophoneApi(data) {
return request({
url: '/api/Microphone/start',
method: 'post',
params: data
})
}
// 暂停录音
export function pauseMicrophoneApi(data) {
return request({
url: '/api/Microphone/pause',
method: 'post',
params: data
})
}
// 恢复录音
export function resumeMicrophoneApi(data) {
return request({
url: '/api/Microphone/resume',
method: 'post',
params: data
})
}
// 停止录音
export function stopMicrophoneApi(data) {
return request({
url: '/api/Microphone/stop',
method: 'post',
params: data
})
}
// 播放音频
export function playSpeaker(data) {
return request({
url: '/api/Speaker/play',
method: 'post',
params: data
})
}

45
src/api/device/profile.js Normal file
View File

@ -0,0 +1,45 @@
import request from '@/utils/request'
// 查询设备配置信息列表
export function listProfile(query) {
return request({
url: '/device/profile/list',
method: 'get',
params: query
})
}
// 查询设备配置信息详细
export function getProfile(query) {
return request({
url: '/device/profile/parameterInfo/',
method: 'get',
params: query
})
}
// 新增设备配置信息
export function addProfile(data) {
return request({
url: '/device/profile',
method: 'post',
data: data
})
}
// 修改设备配置信息
export function updateProfile(data) {
return request({
url: '/device/profile',
method: 'put',
data: data
})
}
// 删除设备配置信息
export function delProfile(id) {
return request({
url: '/device/profile/' + id,
method: 'delete'
})
}

146
src/api/device/register.js Normal file
View File

@ -0,0 +1,146 @@
import request from '@/utils/request'
// 查询设备注册列表
export function listRegister(query) {
return request({
url: '/device/register/list',
method: 'get',
params: query
})
}
// 查询设备注册详细
export function getRegister(id) {
return request({
url: '/device/register/' + id,
method: 'get'
})
}
// 新增设备注册
export function addRegister(data) {
return request({
url: '/device/register',
method: 'post',
data: data
})
}
// 修改设备注册
export function updateRegister(data) {
return request({
url: '/device/register',
method: 'put',
data: data
})
}
// 删除设备注册
export function delRegister(id) {
return request({
url: '/device/register/' + id,
method: 'delete'
})
}
// 设备示教
export function deviceTeach(id) {
return request({
url: '/device/profile/' + id,
method: 'get',
})
}
// 设备示教参数修改
export function deviceUpdateProfile(data) {
return request({
url: '/device/profile/updateProfile',
method: 'put',
data: data,
})
}
// 新增音色
export function createTimbre(data) {
return request({
url: '/show/tts/create',
method: 'post',
headers: {
'Content-Type': 'multipart/form-data',
Authorization: 'eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6ImFiNmEyY2Y0LWM3MjQtNDgyMy1hM2NiLTk4N2NhZWNmMzZiNCJ9.oq-JVRbQuq_ujQ2V0h2jXC3woHPdMQKhnYJ8ixRm-C6Ez4GqJWyHYgmnkw5dbDyhWGdIUH02uv6XT8UiboHD4g',
isToken: false
},
data: data,
})
}
// 获取音色
export function getTts() {
return request({
url: '/show/tts/query',
headers: {
Authorization: 'eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6ImFiNmEyY2Y0LWM3MjQtNDgyMy1hM2NiLTk4N2NhZWNmMzZiNCJ9.oq-JVRbQuq_ujQ2V0h2jXC3woHPdMQKhnYJ8ixRm-C6Ez4GqJWyHYgmnkw5dbDyhWGdIUH02uv6XT8UiboHD4g',
isToken: false
},
method: 'get',
})
}
// 生产语料
export function createCorpus(data) {
return request({
url: '/show/tts/corpus',
headers: {
Authorization: 'eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6ImFiNmEyY2Y0LWM3MjQtNDgyMy1hM2NiLTk4N2NhZWNmMzZiNCJ9.oq-JVRbQuq_ujQ2V0h2jXC3woHPdMQKhnYJ8ixRm-C6Ez4GqJWyHYgmnkw5dbDyhWGdIUH02uv6XT8UiboHD4g',
isToken: false
},
method: 'post',
data: data
})
}
export function getSynthesize(data, config = {}) {
return request({
url: '/show/tts/synthesize',
headers: {
Authorization: 'eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6ImFiNmEyY2Y0LWM3MjQtNDgyMy1hM2NiLTk4N2NhZWNmMzZiNCJ9.oq-JVRbQuq_ujQ2V0h2jXC3woHPdMQKhnYJ8ixRm-C6Ez4GqJWyHYgmnkw5dbDyhWGdIUH02uv6XT8UiboHD4g',
isToken: false
},
responseType: 'blob',
method: 'post',
data: data
}, {
...config
})
}
export function createAdvancedCorpus(data) {
return request({
url: '/show/tts/corpus/advanced',
headers: {
Authorization: 'eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6ImFiNmEyY2Y0LWM3MjQtNDgyMy1hM2NiLTk4N2NhZWNmMzZiNCJ9.oq-JVRbQuq_ujQ2V0h2jXC3woHPdMQKhnYJ8ixRm-C6Ez4GqJWyHYgmnkw5dbDyhWGdIUH02uv6XT8UiboHD4g',
isToken: false
},
method: 'post',
data: data
})
}
export function getAdvancedSynthesize(data, config = {}) {
return request({
url: '/show/tts/synthesize/advanced',
headers: {
Authorization: 'eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6ImFiNmEyY2Y0LWM3MjQtNDgyMy1hM2NiLTk4N2NhZWNmMzZiNCJ9.oq-JVRbQuq_ujQ2V0h2jXC3woHPdMQKhnYJ8ixRm-C6Ez4GqJWyHYgmnkw5dbDyhWGdIUH02uv6XT8UiboHD4g',
isToken: false
},
responseType: 'blob',
method: 'post',
data: data
}, {
...config
})
}
export const getFlowById = (id) => {
return request.get(import.meta.env.VITE_NODE_RED_URL + '/flow/' + id, {})
}

44
src/api/device/robot.js Normal file
View File

@ -0,0 +1,44 @@
import request from '@/utils/request'
// 查询机器人配置列表
export function listRobot(query) {
return request({
url: '/device/robot/list',
method: 'get',
params: query
})
}
// 查询机器人配置详细
export function getRobot(id) {
return request({
url: '/device/robot/' + id,
method: 'get'
})
}
// 新增机器人配置
export function addRobot(data) {
return request({
url: '/device/robot',
method: 'post',
data: data
})
}
// 修改机器人配置
export function updateRobot(data) {
return request({
url: '/device/robot',
method: 'put',
data: data
})
}
// 删除机器人配置
export function delRobot(id) {
return request({
url: '/device/robot/' + id,
method: 'delete'
})
}

72
src/api/device/speaker.js Normal file
View File

@ -0,0 +1,72 @@
import request from '@/utils/request'
// 暂停播放
export function pause(data) {
return request({
url: '/api/Speaker/pause',
method: 'post',
params: data
})
}
// 播放音频
export function play(data) {
return request({
url: '/api/Speaker/play',
method: 'post',
params: data
})
}
// 恢复播放
export function resume(data) {
return request({
url: '/api/Speaker/resume',
method: 'post',
params: data
})
}
// 获取扬声器状态
export function status(query) {
return request({
url: '/api/Speaker/status',
method: 'get',
params: query
})
}
// 停止播放
export function stop(data) {
return request({
url: '/api/Speaker/stop',
method: 'post',
params: data
})
}
// 获取音量
export function getVolume(query) {
return request({
url: '/api/Speaker/volume',
method: 'get',
params: query
})
}
// 设置音量
export function setVolume(data) {
return request({
url: '/api/Speaker/volume',
method: 'post',
params: data
})
}
// 设置音量
export function getAll() {
return request({
url: '/api/Speaker/getAll',
method: 'get',
})
}

View File

@ -0,0 +1,86 @@
import request from '@/utils/request'
// 查询设备终端配置列表
export function listTerminal(query) {
return request({
url: '/device/terminal/list',
method: 'get',
params: query
})
}
// 查询设备终端配置详细
export function getTerminal(id) {
return request({
url: '/device/terminal/' + id,
method: 'get'
})
}
// 新增设备终端配置
export function addTerminal(data) {
return request({
url: '/device/terminal',
method: 'post',
data: data
})
}
// 修改设备终端配置
export function updateTerminal(data) {
return request({
url: '/device/terminal',
method: 'put',
data: data
})
}
// 删除设备终端配置
export function delTerminal(id) {
return request({
url: '/device/terminal/' + id,
method: 'delete'
})
}
// 获取实例信息
export function getInstInfo(id) {
return request({
url: '/test/inst/context/' + id,
method: 'get'
})
}
// 暂停任务
export function pauseInst(id) {
return request({
url: `/test/inst/pause/${id}`,
method: 'post'
})
}
// 恢复任务
export function resumeInst(id) {
return request({
url: `/test/inst/resume/${id}`,
method: 'post'
})
}
// 恢复任务
export function stopInst(id) {
return request({
url: `/test/inst/stop/${id}`,
method: 'post'
})
}
export function getFlowParamsApi(taskId) {
return request({
url: '/test/orchestration',
method: 'get',
params: {
taskId
}
})
}

View File

@ -0,0 +1,68 @@
import request from "@/utils/request";
// 查询根据层级指标库
export function getIndicatorByParentId(parentId, type) {
return request({
url: '/evaluate/indicator/getIndicatorByParentId',
method: "get",
params: {
parentId,
type
}
});
}
/**
* 新增指标
* @param {*} data
* @returns
*/
export function addIndicator(data) {
return request({
url: `/evaluate/indicator/indicator/insert`,
method: "post",
data
});
}
/**
* 删除指标
* @param {*} id
* @returns
*/
export function deleteIndicator(id) {
return request({
url: `/evaluate/indicator/${id}`,
method: "delete",
});
}
/**
* 修改指标
* @param {*} id
* @returns
*/
export function updateIndicator(data) {
return request({
url: `/evaluate/indicator`,
method: "put",
data
});
}
/**
* 获取指定层级的指标
* @param {*} id
* @returns
*/
export function getChildrenByParentIdWithLevelLimitApi(parentId, maxLevel, type) {
return request({
url: `/evaluate/indicator/getChildrenByParentIdWithLevelLimit`,
method: "get",
params: {
parentId,
maxLevel,
type
}
});
}

33
src/api/inspection/map.js Normal file
View File

@ -0,0 +1,33 @@
import request from '@/utils/request'
export function getMapList(params) {
return request({
url: '/inspection/map/list',
method: 'get',
params: params
})
}
export function addMap(data) {
return request({
url: '/inspection/map',
method: 'post',
data: data
})
}
export function updateMap(data) {
return request({
url: '/inspection/map',
method: 'put',
data: data
})
}
export function deleteMap(id) {
return request({
url: `/inspection/map/${id}`,
method: 'delete'
})
}

View File

@ -0,0 +1,32 @@
import request from '@/utils/request'
export function getPointList(params) {
return request({
url: '/inspection/waypoint/list',
method: 'get',
params: params
})
}
export function addPoint(data) {
return request({
url: '/inspection/waypoint',
method: 'post',
data: data
})
}
export function updatePoint(data) {
return request({
url: '/inspection/waypoint',
method: 'put',
data: data
})
}
export function deletePoint(id) {
return request({
url: `/inspection/waypoint/${id}`,
method: 'delete'
})
}

View File

@ -0,0 +1,57 @@
import request from '@/utils/request'
export function getRobotList(params) {
return request({
url: '/inspection/robot/list',
method: 'get',
params: params
})
}
export function addRobot(data) {
return request({
url: '/inspection/robot',
method: 'post',
data: data
})
}
export function updateRobot(data) {
return request({
url: '/inspection/robot',
method: 'put',
data: data
})
}
export function deleteRobot(id) {
return request({
url: `/inspection/robot/${id}`,
method: 'delete'
})
}
/**
* 获取机器人地图
* @param {*} robotId
* @returns
*/
export function getMapByRobot(robotId) {
return request({
url: `/inspection/robot/${robotId}/maps`,
method: 'get'
})
}
/**
* 获取地图JSON数据
* @param {*} params
* @returns
*/
export function getMapJson(params) {
return request({
url: `/inspection/robot/${params.robotId}/download-map`,
method: 'get',
params
})
}

View File

@ -0,0 +1,75 @@
import request from '@/utils/request'
export function getRunTaskList(params) {
return request({
url: '/inspection/taskInstance/list',
method: 'get',
params: params
})
}
export function getRunTaskDetail(id) {
return request({
url: `/inspection/taskInstance/${id}`,
method: 'get'
})
}
export function addRunTask(data) {
return request({
url: '/inspection/taskInstance',
method: 'post',
data: data
})
}
export function updateRunTask(data) {
return request({
url: '/inspection/taskInstance',
method: 'put',
data: data
})
}
export function deleteRunTask(id) {
return request({
url: `/inspection/taskInstance/${id}`,
method: 'delete'
})
}
export function startRunTask(id) {
return request({
url: `/inspection/taskInstance/start/${id}`,
method: 'put'
})
}
export function stopRunTask(id) {
return request({
url: `/inspection/taskInstance/stop/${id}`,
method: 'put'
})
}
export function pauseRunTask(id) {
return request({
url: `/inspection/taskInstance/pause/${id}`,
method: 'put'
})
}
export function resumeRunTask(id) {
return request({
url: `/inspection/taskInstance/resume/${id}`,
method: 'put'
})
}
export function getTaskLogListApi(params) {
return request({
url: '/inspection/taskLog/list',
method: 'get',
params: params
})
}

View File

@ -0,0 +1,47 @@
import request from '@/utils/request'
export function getTaskList(params) {
return request({
url: '/inspection/task/list',
method: 'get',
params: params
})
}
export function addTask(data) {
return request({
url: '/inspection/task',
method: 'post',
data: data
})
}
export function updateTask(data) {
return request({
url: '/inspection/task',
method: 'put',
data: data
})
}
export function deleteTask(id) {
return request({
url: `/inspection/task/${id}`,
method: 'delete'
})
}
export function getWaypointByTaskId(taskId) {
return request({
url: `/inspection/task/${taskId}/waypoints`,
method: 'get'
})
}
export function bindWaypointsToTask(taskId, waypointIds) {
return request({
url: `/inspection/task/${taskId}/waypoints`,
method: 'post',
data: waypointIds
})
}

View File

@ -0,0 +1,32 @@
import request from '@/utils/request'
export function addVehicle(data) {
return request({
url: '/aima/vehicle',
method: 'post',
data: data
})
}
export function getVehicleList(params) {
return request({
url: '/aima/vehicle/list',
method: 'get',
params: params
})
}
export function updateVehicle(data) {
return request({
url: '/aima/vehicle',
method: 'put',
data: data
})
}
export function deleteVehicle(ids) {
return request({
url: `/aima/vehicle/${ids}`,
method: 'delete'
})
}

View File

@ -0,0 +1,32 @@
import request from '@/utils/request'
export function addPhone(data) {
return request({
url: '/aima/phone',
method: 'post',
data: data
})
}
export function getPhoneList(params) {
return request({
url: '/aima/phone/list',
method: 'get',
params: params
})
}
export function updatePhone(data) {
return request({
url: '/aima/phone',
method: 'put',
data: data
})
}
export function deletePhone(ids) {
return request({
url: `/aima/phone/${ids}`,
method: 'delete'
})
}

View File

@ -0,0 +1,69 @@
import request from '@/utils/request'
import { method } from 'lodash'
export function addTaskinstance(data) {
return request({
url: '/aima/taskinstance',
method: 'post',
data: data
})
}
export function getTaskinstanceList(params) {
return request({
url: '/aima/taskinstance/list',
method: 'get',
params: params
})
}
export function updateTaskinstance(data) {
return request({
url: '/aima/taskinstance',
method: 'put',
data: data
})
}
export function deleteTaskinstance(ids) {
return request({
url: `/aima/taskinstance/{ids}`,
method: 'delete'
})
}
export function startTaskinstance(id) {
return request({
url: `/aima/taskinstance/start/${id}`,
method: 'put'
})
}
export function pauseTaskinstance(id) {
return request({
url: `/aima/taskinstance/pause/${id}`,
method: 'put'
})
}
export function stopTaskinstance(id) {
return request({
url: `/aima/taskinstance/stop/${id}`,
method: 'put'
})
}
export function resumeTaskinstance(id) {
return request({
url: `/aima/taskinstance/resume/${id}`,
method: 'put'
})
}
export function getTaskLogListApi(params) {
return request({
url: '/aima/testlog/list',
method: 'get',
params: params
})
}

View File

@ -0,0 +1,47 @@
import request from '@/utils/request'
export function addTask(data) {
return request({
url: '/aima/task',
method: 'post',
data: data
})
}
export function getTaskList(params) {
return request({
url: '/aima/task/list',
method: 'get',
params: params
})
}
export function updateTask(data) {
return request({
url: '/aima/task',
method: 'put',
data: data
})
}
export function deleteTask(ids) {
return request({
url: `/aima/task/${ids}`,
method: 'delete'
})
}
export function getTestCaseByTaskId(taskId) {
return request({
url: `/aima/task/testCases/${taskId}`,
method: 'get',
})
}
export function bindTastCase(taskId, ids) {
return request({
url: `/aima/task/bindTestCases/${taskId}`,
method: 'post',
data: ids
})
}

View File

@ -0,0 +1,32 @@
import request from '@/utils/request'
export function addTestCase(data) {
return request({
url: '/aima/testcase',
method: 'post',
data: data
})
}
export function getTestCaseList(params) {
return request({
url: '/aima/testcase/list',
method: 'get',
params: params
})
}
export function updateTestCase(data) {
return request({
url: '/aima/testcase',
method: 'put',
data: data
})
}
export function deleteTestCase(ids) {
return request({
url: `/aima/testcase/${ids}`,
method: 'delete'
})
}

63
src/api/is/func/index.js Normal file
View File

@ -0,0 +1,63 @@
import request from '@/utils/request'
// 查询语音交互-方案列表
export function getTreeDataApi() {
return request({
url: '/ti/config/list',
method: 'get'
})
}
export function postTreeData(data) {
return request({
url: '/ti/config',
method: 'post',
data
})
}
export function updateTreeData(data) {
return request({
url: '/ti/config',
method: 'put',
data
})
}
export function deleteTreeData(ids) {
return request({
url: `/ti/config/${ids}`,
method: 'delete'
})
}
export function addFunction(data) {
return request({
url: '/ti/function',
method: 'post',
data
})
}
export function getFunction(params) {
return request({
url: '/ti/function/list',
method: 'get',
params
})
}
export function updateFunction(data) {
return request({
url: '/ti/function',
method: 'put',
data
})
}
export function deleteFunction(ids) {
return request({
url: `/ti/function/${ids}`,
method: 'delete'
})
}

32
src/api/is/im/index.js Normal file
View File

@ -0,0 +1,32 @@
import request from '@/utils/request'
export function addUi(data) {
return request({
url: '/ti/ui',
method: 'post',
data
})
}
export function getUiList(params) {
return request({
url: '/ti/ui/list',
methods: 'get',
params
})
}
export function deleteUiList(ids) {
return request({
url: `/ti/ui/${ids}`,
method: 'delete'
})
}
export function updateUI(data) {
return request({
url: '/ti/ui',
method: 'put',
data
})
}

View File

@ -0,0 +1,76 @@
import request from '@/utils/request'
export const getDictList = (params) => {
return request({
url: '/system/dict/data/list',
method: 'get',
params
})
}
export const addProject = (data) => {
return request({
url: '/ti/project',
method: 'post',
data
})
}
export const updateProject = (data) => {
return request({
url: '/ti/project',
method: 'put',
data
})
}
export const getProjectList = (params) => {
return request({
url: '/ti/project/list',
method: 'get',
params
})
}
export const deleteProject = (projectIds) => {
return request({
url: '/ti/project/' + projectIds,
method: 'delete'
})
}
export const getExecuteRunParams = (params) => {
return request({
url: '/ti/project/queryExecuteRunParams',
method: 'get',
params
})
}
export const projectExecute = (data) => {
return request({
url: '/ti/project/execute',
method: 'post',
data
})
}
export const executeProject = (data) => {
return request({
url: '/ti/project/evaluation',
method: 'post',
data
})
}
/**
* 获取评估
* @param {*} projectId
* @returns
*/
export function getValuationProject(projectId) {
return request({
url: '/ti/project/evaluation/' + projectId,
method: 'get'
})
}

60
src/api/login.js Normal file
View File

@ -0,0 +1,60 @@
import request from '@/utils/request'
// 登录方法
export function login(username, password, code, uuid) {
const data = {
username,
password,
code,
uuid
}
return request({
url: '/login',
headers: {
isToken: false,
repeatSubmit: false
},
method: 'post',
data: data
})
}
// 注册方法
export function register(data) {
return request({
url: '/register',
headers: {
isToken: false
},
method: 'post',
data: data
})
}
// 获取用户详细信息
export function getInfo() {
return request({
url: '/getInfo',
method: 'get'
})
}
// 退出方法
export function logout() {
return request({
url: '/logout',
method: 'post'
})
}
// 获取验证码
export function getCodeImg() {
return request({
url: '/captchaImage',
headers: {
isToken: false
},
method: 'get',
timeout: 20000
})
}

9
src/api/menu.js Normal file
View File

@ -0,0 +1,9 @@
import request from '@/utils/request'
// 获取路由
export const getRouters = () => {
return request({
url: '/getRouters',
method: 'get'
})
}

57
src/api/monitor/cache.js Normal file
View File

@ -0,0 +1,57 @@
import request from '@/utils/request'
// 查询缓存详细
export function getCache() {
return request({
url: '/monitor/cache',
method: 'get'
})
}
// 查询缓存名称列表
export function listCacheName() {
return request({
url: '/monitor/cache/getNames',
method: 'get'
})
}
// 查询缓存键名列表
export function listCacheKey(cacheName) {
return request({
url: '/monitor/cache/getKeys/' + cacheName,
method: 'get'
})
}
// 查询缓存内容
export function getCacheValue(cacheName, cacheKey) {
return request({
url: '/monitor/cache/getValue/' + cacheName + '/' + cacheKey,
method: 'get'
})
}
// 清理指定名称缓存
export function clearCacheName(cacheName) {
return request({
url: '/monitor/cache/clearCacheName/' + cacheName,
method: 'delete'
})
}
// 清理指定键名缓存
export function clearCacheKey(cacheKey) {
return request({
url: '/monitor/cache/clearCacheKey/' + cacheKey,
method: 'delete'
})
}
// 清理全部缓存
export function clearCacheAll() {
return request({
url: '/monitor/cache/clearCacheAll',
method: 'delete'
})
}

71
src/api/monitor/job.js Normal file
View File

@ -0,0 +1,71 @@
import request from '@/utils/request'
// 查询定时任务调度列表
export function listJob(query) {
return request({
url: '/monitor/job/list',
method: 'get',
params: query
})
}
// 查询定时任务调度详细
export function getJob(jobId) {
return request({
url: '/monitor/job/' + jobId,
method: 'get'
})
}
// 新增定时任务调度
export function addJob(data) {
return request({
url: '/monitor/job',
method: 'post',
data: data
})
}
// 修改定时任务调度
export function updateJob(data) {
return request({
url: '/monitor/job',
method: 'put',
data: data
})
}
// 删除定时任务调度
export function delJob(jobId) {
return request({
url: '/monitor/job/' + jobId,
method: 'delete'
})
}
// 任务状态修改
export function changeJobStatus(jobId, status) {
const data = {
jobId,
status
}
return request({
url: '/monitor/job/changeStatus',
method: 'put',
data: data
})
}
// 定时任务立即执行一次
export function runJob(jobId, jobGroup) {
const data = {
jobId,
jobGroup
}
return request({
url: '/monitor/job/run',
method: 'put',
data: data
})
}

26
src/api/monitor/jobLog.js Normal file
View File

@ -0,0 +1,26 @@
import request from '@/utils/request'
// 查询调度日志列表
export function listJobLog(query) {
return request({
url: '/monitor/jobLog/list',
method: 'get',
params: query
})
}
// 删除调度日志
export function delJobLog(jobLogId) {
return request({
url: '/monitor/jobLog/' + jobLogId,
method: 'delete'
})
}
// 清空调度日志
export function cleanJobLog() {
return request({
url: '/monitor/jobLog/clean',
method: 'delete'
})
}

View File

@ -0,0 +1,34 @@
import request from '@/utils/request'
// 查询登录日志列表
export function list(query) {
return request({
url: '/monitor/logininfor/list',
method: 'get',
params: query
})
}
// 删除登录日志
export function delLogininfor(infoId) {
return request({
url: '/monitor/logininfor/' + infoId,
method: 'delete'
})
}
// 解锁用户登录状态
export function unlockLogininfor(userName) {
return request({
url: '/monitor/logininfor/unlock/' + userName,
method: 'get'
})
}
// 清空登录日志
export function cleanLogininfor() {
return request({
url: '/monitor/logininfor/clean',
method: 'delete'
})
}

18
src/api/monitor/online.js Normal file
View File

@ -0,0 +1,18 @@
import request from '@/utils/request'
// 查询在线用户列表
export function list(query) {
return request({
url: '/monitor/online/list',
method: 'get',
params: query
})
}
// 强退用户
export function forceLogout(tokenId) {
return request({
url: '/monitor/online/' + tokenId,
method: 'delete'
})
}

View File

@ -0,0 +1,26 @@
import request from '@/utils/request'
// 查询操作日志列表
export function list(query) {
return request({
url: '/monitor/operlog/list',
method: 'get',
params: query
})
}
// 删除操作日志
export function delOperlog(operId) {
return request({
url: '/monitor/operlog/' + operId,
method: 'delete'
})
}
// 清空操作日志
export function cleanOperlog() {
return request({
url: '/monitor/operlog/clean',
method: 'delete'
})
}

View File

@ -0,0 +1,9 @@
import request from '@/utils/request'
// 获取服务信息
export function getServer() {
return request({
url: '/monitor/server',
method: 'get'
})
}

60
src/api/system/config.js Normal file
View File

@ -0,0 +1,60 @@
import request from '@/utils/request'
// 查询参数列表
export function listConfig(query) {
return request({
url: '/system/config/list',
method: 'get',
params: query
})
}
// 查询参数详细
export function getConfig(configId) {
return request({
url: '/system/config/' + configId,
method: 'get'
})
}
// 根据参数键名查询参数值
export function getConfigKey(configKey) {
return request({
url: '/system/config/configKey/' + configKey,
method: 'get'
})
}
// 新增参数配置
export function addConfig(data) {
return request({
url: '/system/config',
method: 'post',
data: data
})
}
// 修改参数配置
export function updateConfig(data) {
return request({
url: '/system/config',
method: 'put',
data: data
})
}
// 删除参数配置
export function delConfig(configId) {
return request({
url: '/system/config/' + configId,
method: 'delete'
})
}
// 刷新参数缓存
export function refreshCache() {
return request({
url: '/system/config/refreshCache',
method: 'delete'
})
}

52
src/api/system/dept.js Normal file
View File

@ -0,0 +1,52 @@
import request from '@/utils/request'
// 查询部门列表
export function listDept(query) {
return request({
url: '/system/dept/list',
method: 'get',
params: query
})
}
// 查询部门列表(排除节点)
export function listDeptExcludeChild(deptId) {
return request({
url: '/system/dept/list/exclude/' + deptId,
method: 'get'
})
}
// 查询部门详细
export function getDept(deptId) {
return request({
url: '/system/dept/' + deptId,
method: 'get'
})
}
// 新增部门
export function addDept(data) {
return request({
url: '/system/dept',
method: 'post',
data: data
})
}
// 修改部门
export function updateDept(data) {
return request({
url: '/system/dept',
method: 'put',
data: data
})
}
// 删除部门
export function delDept(deptId) {
return request({
url: '/system/dept/' + deptId,
method: 'delete'
})
}

View File

@ -0,0 +1,52 @@
import request from '@/utils/request'
// 查询字典数据列表
export function listData(query) {
return request({
url: '/system/dict/data/list',
method: 'get',
params: query
})
}
// 查询字典数据详细
export function getData(dictCode) {
return request({
url: '/system/dict/data/' + dictCode,
method: 'get'
})
}
// 根据字典类型查询字典数据信息
export function getDicts(dictType) {
return request({
url: '/system/dict/data/type/' + dictType,
method: 'get'
})
}
// 新增字典数据
export function addData(data) {
return request({
url: '/system/dict/data',
method: 'post',
data: data
})
}
// 修改字典数据
export function updateData(data) {
return request({
url: '/system/dict/data',
method: 'put',
data: data
})
}
// 删除字典数据
export function delData(dictCode) {
return request({
url: '/system/dict/data/' + dictCode,
method: 'delete'
})
}

View File

@ -0,0 +1,60 @@
import request from '@/utils/request'
// 查询字典类型列表
export function listType(query) {
return request({
url: '/system/dict/type/list',
method: 'get',
params: query
})
}
// 查询字典类型详细
export function getType(dictId) {
return request({
url: '/system/dict/type/' + dictId,
method: 'get'
})
}
// 新增字典类型
export function addType(data) {
return request({
url: '/system/dict/type',
method: 'post',
data: data
})
}
// 修改字典类型
export function updateType(data) {
return request({
url: '/system/dict/type',
method: 'put',
data: data
})
}
// 删除字典类型
export function delType(dictId) {
return request({
url: '/system/dict/type/' + dictId,
method: 'delete'
})
}
// 刷新字典缓存
export function refreshCache() {
return request({
url: '/system/dict/type/refreshCache',
method: 'delete'
})
}
// 获取字典选择框列表
export function optionselect() {
return request({
url: '/system/dict/type/optionselect',
method: 'get'
})
}

44
src/api/system/file.js Normal file
View File

@ -0,0 +1,44 @@
import request from '@/utils/request'
// 查询文件管理列表
export function listFile(query) {
return request({
url: '/system/file/list',
method: 'get',
params: query
})
}
// 查询文件管理详细
export function getFile(fileId) {
return request({
url: '/system/file/' + fileId,
method: 'get'
})
}
// 新增文件管理
export function addFile(data) {
return request({
url: '/system/file',
method: 'post',
data: data
})
}
// 修改文件管理
export function updateFile(data) {
return request({
url: '/system/file',
method: 'put',
data: data
})
}
// 删除文件管理
export function delFile(fileId) {
return request({
url: '/system/file/' + fileId,
method: 'delete'
})
}

44
src/api/system/group.js Normal file
View File

@ -0,0 +1,44 @@
import request from '@/utils/request'
// 查询通用分组配置列表
export function listGroup(query) {
return request({
url: '/system/group/list',
method: 'get',
params: query
})
}
// 查询通用分组配置详细
export function getGroup(groupId) {
return request({
url: '/system/group/' + groupId,
method: 'get'
})
}
// 新增通用分组配置
export function addGroup(data) {
return request({
url: '/system/group',
method: 'post',
data: data
})
}
// 修改通用分组配置
export function updateGroup(data) {
return request({
url: '/system/group',
method: 'put',
data: data
})
}
// 删除通用分组配置
export function delGroup(groupId) {
return request({
url: '/system/group/' + groupId,
method: 'delete'
})
}

60
src/api/system/menu.js Normal file
View File

@ -0,0 +1,60 @@
import request from '@/utils/request'
// 查询菜单列表
export function listMenu(query) {
return request({
url: '/system/menu/list',
method: 'get',
params: query
})
}
// 查询菜单详细
export function getMenu(menuId) {
return request({
url: '/system/menu/' + menuId,
method: 'get'
})
}
// 查询菜单下拉树结构
export function treeselect() {
return request({
url: '/system/menu/treeselect',
method: 'get'
})
}
// 根据角色ID查询菜单下拉树结构
export function roleMenuTreeselect(roleId) {
return request({
url: '/system/menu/roleMenuTreeselect/' + roleId,
method: 'get'
})
}
// 新增菜单
export function addMenu(data) {
return request({
url: '/system/menu',
method: 'post',
data: data
})
}
// 修改菜单
export function updateMenu(data) {
return request({
url: '/system/menu',
method: 'put',
data: data
})
}
// 删除菜单
export function delMenu(menuId) {
return request({
url: '/system/menu/' + menuId,
method: 'delete'
})
}

44
src/api/system/notice.js Normal file
View File

@ -0,0 +1,44 @@
import request from '@/utils/request'
// 查询公告列表
export function listNotice(query) {
return request({
url: '/system/notice/list',
method: 'get',
params: query
})
}
// 查询公告详细
export function getNotice(noticeId) {
return request({
url: '/system/notice/' + noticeId,
method: 'get'
})
}
// 新增公告
export function addNotice(data) {
return request({
url: '/system/notice',
method: 'post',
data: data
})
}
// 修改公告
export function updateNotice(data) {
return request({
url: '/system/notice',
method: 'put',
data: data
})
}
// 删除公告
export function delNotice(noticeId) {
return request({
url: '/system/notice/' + noticeId,
method: 'delete'
})
}

44
src/api/system/post.js Normal file
View File

@ -0,0 +1,44 @@
import request from '@/utils/request'
// 查询岗位列表
export function listPost(query) {
return request({
url: '/system/post/list',
method: 'get',
params: query
})
}
// 查询岗位详细
export function getPost(postId) {
return request({
url: '/system/post/' + postId,
method: 'get'
})
}
// 新增岗位
export function addPost(data) {
return request({
url: '/system/post',
method: 'post',
data: data
})
}
// 修改岗位
export function updatePost(data) {
return request({
url: '/system/post',
method: 'put',
data: data
})
}
// 删除岗位
export function delPost(postId) {
return request({
url: '/system/post/' + postId,
method: 'delete'
})
}

119
src/api/system/role.js Normal file
View File

@ -0,0 +1,119 @@
import request from '@/utils/request'
// 查询角色列表
export function listRole(query) {
return request({
url: '/system/role/list',
method: 'get',
params: query
})
}
// 查询角色详细
export function getRole(roleId) {
return request({
url: '/system/role/' + roleId,
method: 'get'
})
}
// 新增角色
export function addRole(data) {
return request({
url: '/system/role',
method: 'post',
data: data
})
}
// 修改角色
export function updateRole(data) {
return request({
url: '/system/role',
method: 'put',
data: data
})
}
// 角色数据权限
export function dataScope(data) {
return request({
url: '/system/role/dataScope',
method: 'put',
data: data
})
}
// 角色状态修改
export function changeRoleStatus(roleId, status) {
const data = {
roleId,
status
}
return request({
url: '/system/role/changeStatus',
method: 'put',
data: data
})
}
// 删除角色
export function delRole(roleId) {
return request({
url: '/system/role/' + roleId,
method: 'delete'
})
}
// 查询角色已授权用户列表
export function allocatedUserList(query) {
return request({
url: '/system/role/authUser/allocatedList',
method: 'get',
params: query
})
}
// 查询角色未授权用户列表
export function unallocatedUserList(query) {
return request({
url: '/system/role/authUser/unallocatedList',
method: 'get',
params: query
})
}
// 取消用户授权角色
export function authUserCancel(data) {
return request({
url: '/system/role/authUser/cancel',
method: 'put',
data: data
})
}
// 批量取消用户授权角色
export function authUserCancelAll(data) {
return request({
url: '/system/role/authUser/cancelAll',
method: 'put',
params: data
})
}
// 授权用户选择
export function authUserSelectAll(data) {
return request({
url: '/system/role/authUser/selectAll',
method: 'put',
params: data
})
}
// 根据角色ID查询部门树结构
export function deptTreeSelect(roleId) {
return request({
url: '/system/role/deptTree/' + roleId,
method: 'get'
})
}

136
src/api/system/user.js Normal file
View File

@ -0,0 +1,136 @@
import request from '@/utils/request'
import { parseStrEmpty } from "@/utils/ruoyi";
// 查询用户列表
export function listUser(query) {
return request({
url: '/system/user/list',
method: 'get',
params: query
})
}
// 查询用户详细
export function getUser(userId) {
return request({
url: '/system/user/' + parseStrEmpty(userId),
method: 'get'
})
}
// 新增用户
export function addUser(data) {
return request({
url: '/system/user',
method: 'post',
data: data
})
}
// 修改用户
export function updateUser(data) {
return request({
url: '/system/user',
method: 'put',
data: data
})
}
// 删除用户
export function delUser(userId) {
return request({
url: '/system/user/' + userId,
method: 'delete'
})
}
// 用户密码重置
export function resetUserPwd(userId, password) {
const data = {
userId,
password
}
return request({
url: '/system/user/resetPwd',
method: 'put',
data: data
})
}
// 用户状态修改
export function changeUserStatus(userId, status) {
const data = {
userId,
status
}
return request({
url: '/system/user/changeStatus',
method: 'put',
data: data
})
}
// 查询用户个人信息
export function getUserProfile() {
return request({
url: '/system/user/profile',
method: 'get'
})
}
// 修改用户个人信息
export function updateUserProfile(data) {
return request({
url: '/system/user/profile',
method: 'put',
data: data
})
}
// 用户密码重置
export function updateUserPwd(oldPassword, newPassword) {
const data = {
oldPassword,
newPassword
}
return request({
url: '/system/user/profile/updatePwd',
method: 'put',
params: data
})
}
// 用户头像上传
export function uploadAvatar(data) {
return request({
url: '/system/user/profile/avatar',
method: 'post',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
data: data
})
}
// 查询授权角色
export function getAuthRole(userId) {
return request({
url: '/system/user/authRole/' + userId,
method: 'get'
})
}
// 保存授权角色
export function updateAuthRole(data) {
return request({
url: '/system/user/authRole',
method: 'put',
params: data
})
}
// 查询部门下拉树结构
export function deptTreeSelect() {
return request({
url: '/system/user/deptTree',
method: 'get'
})
}

44
src/api/system/vehOper.js Normal file
View File

@ -0,0 +1,44 @@
import request from '@/utils/request'
// 查询车机操作配置列表
export function listVehOper(query) {
return request({
url: '/system/vehOper/list',
method: 'get',
params: query
})
}
// 查询车机操作配置详细
export function getVehOper(configId) {
return request({
url: '/system/vehOper/' + configId,
method: 'get'
})
}
// 新增车机操作配置
export function addVehOper(data) {
return request({
url: '/system/vehOper',
method: 'post',
data: data
})
}
// 修改车机操作配置
export function updateVehOper(data) {
return request({
url: '/system/vehOper',
method: 'put',
data: data
})
}
// 删除车机操作配置
export function delVehOper(configId) {
return request({
url: '/system/vehOper/' + configId,
method: 'delete'
})
}

78
src/api/test/config.js Normal file
View File

@ -0,0 +1,78 @@
import request from '@/utils/request'
// 查询任务流程编排列表
export function listConfig(query) {
return request({
url: '/test/config/list',
method: 'get',
params: query
})
}
// 查询任务流程编排详细
export function getConfig(id) {
return request({
url: '/test/config/' + id,
method: 'get'
})
}
// 新增任务流程编排
export function addConfig(data) {
return request({
url: '/test/config',
method: 'post',
data: data
})
}
// 修改任务流程编排
export function updateConfig(data) {
return request({
url: '/test/config',
method: 'put',
data: data
})
}
// 删除任务流程编排
export function delConfig(id) {
return request({
url: '/test/config/' + id,
method: 'delete'
})
}
// 流程编排
export function taskConfig(data) {
return request({
url: '/test/orchestration',
method: 'post',
data: data
})
}
// 流程编排查询
export function getTaskConfig(params) {
return request({
url: `/test/orchestration`,
method: 'get',
params: params
})
}
// 获取任务执行参数
export function getTaskParamsById(id) {
return request({
url: `/test/param/query/${id}`,
method: 'get',
})
}
// 任务执行
export function taskExecute(data) {
return request({
url: `/flow/execute`,
method: 'post',
data: data
})
}

44
src/api/test/detect.js Normal file
View File

@ -0,0 +1,44 @@
import request from '@/utils/request'
// 查询检测项配置列表
export function listDetect(query) {
return request({
url: '/test/detect/list',
method: 'get',
params: query
})
}
// 查询检测项配置详细
export function getDetect(id) {
return request({
url: '/test/detect/' + id,
method: 'get'
})
}
// 新增检测项配置
export function addDetect(data) {
return request({
url: '/test/detect',
method: 'post',
data: data
})
}
// 修改检测项配置
export function updateDetect(data) {
return request({
url: '/test/detect',
method: 'put',
data: data
})
}
// 删除检测项配置
export function delDetect(id) {
return request({
url: '/test/detect/' + id,
method: 'delete'
})
}

72
src/api/test/log.js Normal file
View File

@ -0,0 +1,72 @@
import request from '@/utils/request'
// 查询检测项日志列表
export function listLog(query) {
return request({
url: '/test/log/list',
method: 'get',
params: query
})
}
// 查询检测项日志详细
export function getLog(id) {
return request({
url: '/test/log/' + id,
method: 'get'
})
}
// 新增检测项日志
export function addLog(data) {
return request({
url: '/test/log',
method: 'post',
data: data
})
}
// 修改检测项日志
export function updateLog(data) {
return request({
url: '/test/log',
method: 'put',
data: data
})
}
// 删除检测项日志
export function delLog(id) {
return request({
url: '/test/log/' + id,
method: 'delete'
})
}
// 查询任务实例列表
export function listInst(query) {
return request({
url: '/test/inst/list',
method: 'get',
params: query
})
}
// 查询任务实例日志
export function getInst(query) {
return request({
url: '/test/inst/log/detail',
method: 'get',
params: query
})
}
// 查询任务实例日志
export function getLogicFlow(query) {
return request({
url: '/test/inst/log/query',
method: 'get',
params: query
})
}

60
src/api/test/params.js Normal file
View File

@ -0,0 +1,60 @@
import request from '@/utils/request'
// 查询参数配置列表
export function listParams(query) {
return request({
url: '/test/param/list',
method: 'get',
params: query
})
}
// 查询参数配置详细
export function getParams(id) {
return request({
url: '/test/param/' + id,
method: 'get'
})
}
// 新增参数配置
export function addParams(data) {
return request({
url: '/test/param',
method: 'post',
data: data
})
}
// 修改参数配置
export function updateParams(data) {
return request({
url: '/test/param',
method: 'put',
data: data
})
}
// 删除参数配置
export function delParams(id) {
return request({
url: '/test/param/' + id,
method: 'delete'
})
}
// 参数来源
export function getParamSources(id) {
return request({
url: '/test/param/sources',
method: 'get'
})
}
// 参数类型
export function getParamTypes(id) {
return request({
url: '/test/param/types',
method: 'get'
})
}

85
src/api/tool/gen.js Normal file
View File

@ -0,0 +1,85 @@
import request from '@/utils/request'
// 查询生成表数据
export function listTable(query) {
return request({
url: '/tool/gen/list',
method: 'get',
params: query
})
}
// 查询db数据库列表
export function listDbTable(query) {
return request({
url: '/tool/gen/db/list',
method: 'get',
params: query
})
}
// 查询表详细信息
export function getGenTable(tableId) {
return request({
url: '/tool/gen/' + tableId,
method: 'get'
})
}
// 修改代码生成信息
export function updateGenTable(data) {
return request({
url: '/tool/gen',
method: 'put',
data: data
})
}
// 导入表
export function importTable(data) {
return request({
url: '/tool/gen/importTable',
method: 'post',
params: data
})
}
// 创建表
export function createTable(data) {
return request({
url: '/tool/gen/createTable',
method: 'post',
params: data
})
}
// 预览生成代码
export function previewTable(tableId) {
return request({
url: '/tool/gen/preview/' + tableId,
method: 'get'
})
}
// 删除表数据
export function delTable(tableId) {
return request({
url: '/tool/gen/' + tableId,
method: 'delete'
})
}
// 生成代码(自定义路径)
export function genCode(tableName) {
return request({
url: '/tool/gen/genCode/' + tableName,
method: 'get'
})
}
// 同步数据库
export function synchDb(tableName) {
return request({
url: '/tool/gen/synchDb/' + tableName,
method: 'get'
})
}

View File

@ -0,0 +1,14 @@
import request from "@/utils/request.js";
export function coordinateMarking(data) {
return request({
url: '/show/touch/coordinates',
method: 'post',
headers: {
isToken: false,
'Content-Type': 'multipart/form-data',
'Authorization':'Bearer eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6ImIzY2QwYjFjLWNlNjgtNDEwNy1hYzA1LTdmYTBlNmQ4NzJmNiJ9.2N9E9IDrFeCe-yRIBly60N0kYYmCscdrhyd8KHmhw2dxLi7B_A1i8bcWRTtx9YryFB3AhkvZkGauMx0CdZ0TDg'
},
data: data
})
}

77
src/api/vi/corpus.js Normal file
View File

@ -0,0 +1,77 @@
import request from "@/utils/request";
import { method } from "lodash";
// 查询语料库列表
export function listCorpus(query) {
return request({
url: "/vi/corpus/single/list",
method: "get",
params: query,
});
}
// 查询语料库详细
export function getCorpus(corpusId) {
return request({
url: "/vi/corpus/" + corpusId,
method: "get",
});
}
// 新增语料库
export function addCorpus(data) {
return request({
url: "/vi/corpus",
method: "post",
data: data,
});
}
// 修改语料库
export function updateCorpus(data) {
return request({
url: "/vi/corpus",
method: "put",
data: data,
});
}
// 删除语料库
export function delCorpus(corpusId) {
return request({
url: "/vi/corpus/" + corpusId,
method: "delete",
});
}
export function batchAddCorpus(data) {
return request({
url: "/vi/corpus/continuous/insert",
method: "post",
data: data,
});
}
export function batchUpdateCorpus(data) {
return request({
url: "/vi/corpus/continuous/update",
method: "post",
data: data,
});
}
export function getBatchCorpus(query) {
return request({
url: "/vi/corpus/continuous/list",
method: "get",
params: query,
});
}
export function deleteBatchCorpus(corpusIds) {
return request({
url: `/vi/corpus/continuous`,
method: "delete",
data: corpusIds,
});
}

44
src/api/vi/plan.js Normal file
View File

@ -0,0 +1,44 @@
import request from '@/utils/request'
// 查询语音交互-方案列表
export function listScheme(query) {
return request({
url: '/vi/scheme/list',
method: 'get',
params: query
})
}
// 查询语音交互-方案详细
export function getScheme(schemeId) {
return request({
url: '/vi/scheme/' + schemeId,
method: 'get'
})
}
// 新增语音交互-方案
export function addScheme(data) {
return request({
url: '/vi/scheme',
method: 'post',
data: data
})
}
// 修改语音交互-方案
export function updateScheme(data) {
return request({
url: '/vi/scheme',
method: 'put',
data: data
})
}
// 删除语音交互-方案
export function delScheme(schemeId) {
return request({
url: '/vi/scheme/' + schemeId,
method: 'delete'
})
}

83
src/api/vi/project.js Normal file
View File

@ -0,0 +1,83 @@
import request from '@/utils/request'
// 查询语音交互-项目列表
export function listProject(query) {
return request({
url: '/vi/project/list',
method: 'get',
params: query
})
}
// 查询语音交互-项目详细
export function getProject(projectId) {
return request({
url: '/vi/project/' + projectId,
method: 'get'
})
}
// 新增语音交互-项目
export function addProject(data) {
return request({
url: '/vi/project',
method: 'post',
data: data
})
}
// 修改语音交互-项目
export function updateProject(data) {
return request({
url: '/vi/project',
method: 'put',
data: data
})
}
// 删除语音交互-项目
export function delProject(projectId) {
return request({
url: '/vi/project/' + projectId,
method: 'delete'
})
}
export function getExecuteRunParams(projectId) {
return request({
url: '/vi/project/queryExecuteRunParams',
method: 'get',
params: {
projectId
}
})
}
export function projectExecute(data) {
return request({
url: '/vi/project/execute',
method: 'post',
data
})
}
export function queryExecuteInstId(projectId) {
return request({
url: `/vi/project/queryExecuteInstId/${projectId}`,
method: 'get',
})
}
/**
* 获取评估
* @param {*} projectId
* @returns
*/
export function getValuationProject(projectId) {
return request({
url: '/vi/project/evaluation/' + projectId,
method: 'get'
})
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

View File

@ -0,0 +1 @@
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M121.718 73.272v9.953c3.957-7.584 6.199-16.05 6.199-24.995C127.917 26.079 99.273 0 63.958 0 28.644 0 0 26.079 0 58.23c0 .403.028.806.028 1.21l22.97-25.953h13.34l-19.76 27.187h6.42V53.77l13.728-19.477v49.361H22.998V73.272H2.158c5.951 20.284 23.608 36.208 45.998 41.399-1.44 3.3-5.618 11.263-12.565 12.674-8.607 1.764 23.358.428 46.163-13.178 17.519-4.611 31.938-15.849 39.77-30.513h-13.506V73.272H85.02V59.464l22.998-25.977h13.008l-19.429 27.187h6.421v-7.433l13.727-19.402v39.433h-.027zm-78.24 2.822a10.516 10.516 0 0 1-.996-4.535V44.548c0-1.613.332-3.124.996-4.535a11.66 11.66 0 0 1 2.713-3.68c1.134-1.032 2.49-1.864 4.04-2.468 1.55-.605 3.21-.908 4.982-.908h11.292c1.77 0 3.431.303 4.981.908 1.522.604 2.85 1.41 3.986 2.418l-12.26 16.303v-2.898a1.96 1.96 0 0 0-.665-1.512c-.443-.403-.996-.604-1.66-.604-.665 0-1.218.201-1.661.604a1.96 1.96 0 0 0-.664 1.512v9.071L44.364 77.606a10.556 10.556 0 0 1-.886-1.512zm35.73-4.535c0 1.613-.332 3.124-.997 4.535a11.66 11.66 0 0 1-2.712 3.68c-1.134 1.032-2.49 1.864-4.04 2.469-1.55.604-3.21.907-4.982.907H55.185c-1.77 0-3.431-.303-4.981-.907-1.55-.605-2.906-1.437-4.041-2.47a12.49 12.49 0 0 1-1.384-1.512l13.727-18.217v6.375c0 .605.222 1.109.665 1.512.442.403.996.604 1.66.604.664 0 1.218-.201 1.66-.604a1.96 1.96 0 0 0 .665-1.512V53.87L75.97 36.838c.913.932 1.66 1.99 2.214 3.175.664 1.41.996 2.922.996 4.535v27.011h.028z"/></svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -0,0 +1,54 @@
<svg
viewBox="0 0 22 22"
xmlns="http://www.w3.org/2000/svg"
fill="none"
>
<rect width="22" height="22" x="0" y="0" />
<path
d="M11 7.33335L11 3.66669L7.33331 3.66669"
fill-rule="nonzero"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="1.833333"
/>
<rect
width="14.666667"
height="11.000000"
x="3.666687"
y="7.333344"
rx="1.833333"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="1.833333"
/>
<path
d="M1.83331 12.8333L3.66665 12.8333"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="1.833333"
/>
<path
d="M18.3333 12.8333L20.1666 12.8333"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="1.833333"
/>
<path
d="M13.75 11.9167L13.75 13.75"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="1.833333"
/>
<path
d="M8.25 11.9167L8.25 13.75"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="1.833333"
/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -0,0 +1 @@
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M127.88 73.143c0 1.412-.506 2.635-1.518 3.669-1.011 1.033-2.209 1.55-3.592 1.55h-17.887c0 9.296-1.783 17.178-5.35 23.645l16.609 17.044c1.011 1.034 1.517 2.257 1.517 3.67 0 1.412-.506 2.635-1.517 3.668-.958 1.033-2.155 1.55-3.593 1.55-1.438 0-2.635-.517-3.593-1.55l-15.811-16.063a15.49 15.49 0 0 1-1.196 1.06c-.532.434-1.65 1.208-3.353 2.322a50.104 50.104 0 0 1-5.192 2.974c-1.758.87-3.94 1.658-6.546 2.364-2.607.706-5.189 1.06-7.748 1.06V47.044H58.89v73.062c-2.716 0-5.417-.367-8.106-1.102-2.688-.734-5.003-1.631-6.945-2.692a66.769 66.769 0 0 1-5.268-3.179c-1.571-1.057-2.73-1.94-3.476-2.65L33.9 109.34l-14.611 16.877c-1.066 1.14-2.344 1.711-3.833 1.711-1.277 0-2.422-.434-3.434-1.304-1.012-.978-1.557-2.187-1.635-3.627-.079-1.44.333-2.705 1.236-3.794l16.129-18.51c-3.087-6.197-4.63-13.644-4.63-22.342H5.235c-1.383 0-2.58-.517-3.592-1.55S.125 74.545.125 73.132c0-1.412.506-2.635 1.518-3.668 1.012-1.034 2.21-1.55 3.592-1.55h17.887V43.939L9.308 29.833c-1.012-1.033-1.517-2.256-1.517-3.669 0-1.412.505-2.635 1.517-3.668 1.012-1.034 2.21-1.55 3.593-1.55s2.58.516 3.593 1.55l13.813 14.106h67.396l13.814-14.106c1.012-1.034 2.21-1.55 3.592-1.55 1.384 0 2.581.516 3.593 1.55 1.012 1.033 1.518 2.256 1.518 3.668 0 1.413-.506 2.636-1.518 3.67l-13.814 14.105v23.975h17.887c1.383 0 2.58.516 3.593 1.55 1.011 1.033 1.517 2.256 1.517 3.668l-.005.01zM89.552 26.175H38.448c0-7.23 2.489-13.386 7.466-18.469C50.892 2.623 56.92.082 64 .082c7.08 0 13.108 2.541 18.086 7.624 4.977 5.083 7.466 11.24 7.466 18.469z"/></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

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="1568899741379" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2054" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M960 591.424V368.96c0-0.288 0.16-0.512 0.16-0.768S960 367.68 960 367.424V192a32 32 0 0 0-32-32H96a32 32 0 0 0-32 32v175.424c0 0.288-0.16 0.512-0.16 0.768s0.16 0.48 0.16 0.768v222.464c0 0.288-0.16 0.512-0.16 0.768s0.16 0.48 0.16 0.768V864a32 32 0 0 0 32 32h832a32 32 0 0 0 32-32v-271.04c0-0.288 0.16-0.512 0.16-0.768S960 591.68 960 591.424z m-560-31.232v-160H608v160h-208z m208 64V832h-208v-207.808H608z m-480-224h208v160H128v-160z m544 0h224v160h-224v-160zM896 224v112.192H128V224h768zM128 624.192h208V832H128v-207.808zM672 832v-207.808h224V832h-224z" p-id="2055"></path></svg>

After

Width:  |  Height:  |  Size: 954 B

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="1588670460195" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1314" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M230.4 307.712c13.824 0 25.088-11.264 25.088-25.088 0-100.352 81.92-182.272 182.272-182.272s182.272 81.408 182.272 182.272c0 13.824 11.264 25.088 25.088 25.088s25.088-11.264 24.576-25.088c0-127.488-103.936-231.936-231.936-231.936S205.824 154.624 205.824 282.624c-0.512 14.336 10.752 25.088 24.576 25.088z m564.736 234.496c-11.264 0-21.504 2.048-31.232 6.144 0-44.544-40.448-81.92-88.064-81.92-14.848 0-28.16 3.584-39.936 10.24-13.824-28.16-44.544-48.128-78.848-48.128-12.288 0-24.576 2.56-35.328 7.68V284.16c0-45.568-37.888-81.92-84.48-81.92s-84.48 36.864-84.48 81.92v348.672l-69.12-112.64c-18.432-28.16-58.368-36.864-91.136-19.968-26.624 14.336-46.592 47.104-30.208 88.064 3.072 8.192 76.8 205.312 171.52 311.296 0 0 28.16 24.576 43.008 58.88 4.096 9.728 13.312 15.36 22.528 15.36 3.072 0 6.656-0.512 9.728-2.048 12.288-5.12 18.432-19.968 12.8-32.256-19.456-44.544-53.76-74.752-53.76-74.752C281.6 768 209.408 573.44 208.384 570.88c-5.12-12.8-2.56-20.992 7.168-26.112 9.216-4.608 21.504-4.608 26.112 2.56l113.152 184.32c4.096 8.704 12.8 14.336 22.528 14.336 13.824 0 25.088-10.752 25.088-25.088V284.16c0-17.92 15.36-32.256 34.816-32.256s34.816 14.336 34.816 32.256v284.16c0 13.824 10.24 25.088 24.576 25.088 13.824 0 25.088-11.264 25.088-25.088v-57.344c0-17.92 15.36-32.768 34.816-32.768 19.968 0 37.376 15.36 37.376 32.768v95.232c0 7.168 3.072 13.312 7.68 17.92 4.608 4.608 10.752 7.168 17.92 7.168 13.824 0 24.576-11.264 24.576-25.088V547.84c0-18.432 13.824-32.256 32.256-32.256 20.48 0 38.912 15.36 38.912 32.256v95.232c0 13.824 11.264 25.088 25.088 25.088s24.576-11.264 25.088-25.088v-18.944c0-18.944 12.8-32.256 30.72-32.256 18.432 0 22.528 18.944 22.528 31.744 0 1.024-11.776 99.84-50.688 173.056-30.72 58.368-45.056 112.128-51.2 146.944-2.56 13.312 6.656 26.112 19.968 28.672 1.536 0 3.072 0.512 4.608 0.512 11.776 0 22.016-8.192 24.064-20.48 5.632-31.232 18.432-79.36 46.08-132.608 43.52-81.92 55.808-186.88 56.32-193.536-0.512-50.688-29.696-83.968-72.704-83.968z"></path></path></svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

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="1576153230908" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="971" xmlns:xlink="http://www.w3.org/1999/xlink" width="81" height="81"><defs><style type="text/css"></style></defs><path d="M772.87036133 734.06115723c-43.34106445 0-80.00793458 27.93273926-93.76831055 66.57714843H475.90991211c-56.60705567 0-102.66723633-46.06018067-102.66723633-102.66723633V600.82446289h305.859375c13.76037598 38.64440918 50.42724609 66.57714844 93.76831055 66.57714844 55.12390137 0 99.94812012-44.82421875 99.94812012-99.94812012S827.9942627 467.50537109 772.87036133 467.50537109c-43.34106445 0-80.00793458 27.93273926-93.76831055 66.57714844H373.24267578V401.01062011h321.92687989c55.12390137 0 99.94812012-44.82421875 99.94812011-99.94812011V190.07312011C795.11767578 134.94921875 750.29345703 90.125 695.16955567 90.125H251.12963867C196.0057373 90.125 151.18151855 134.94921875 151.18151855 190.07312011V301.0625c0 55.12390137 44.82421875 99.94812012 99.94812012 99.94812012h55.53588867v296.96044921c0 93.35632325 75.97045898 169.32678223 169.32678224 169.32678223h203.19213866c13.76037598 38.64440918 50.42724609 66.57714844 93.76831055 66.57714844 55.12390137 0 99.94812012-44.82421875 99.94812012-99.94812012s-44.90661622-99.86572266-100.03051758-99.86572265z m0-199.89624024c18.37463379 0 33.28857422 14.91394043 33.28857422 33.28857423s-14.91394043 33.28857422-33.28857422 33.28857421-33.28857422-14.91394043-33.28857422-33.28857421 14.91394043-33.28857422 33.28857422-33.28857422zM217.75866699 301.0625V190.07312011c0-18.37463379 14.91394043-33.28857422 33.28857423-33.28857421h444.03991698c18.37463379 0 33.28857422 14.91394043 33.28857422 33.28857422V301.0625c0 18.37463379-14.91394043 33.28857422-33.28857422 33.28857422H251.12963867c-18.37463379 0-33.37097168-14.91394043-33.37097168-33.28857422z m555.11169434 566.23535156c-18.37463379 0-33.28857422-14.91394043-33.28857422-33.28857422 0-18.37463379 14.91394043-33.28857422 33.28857422-33.28857422s33.28857422 14.91394043 33.28857422 33.28857422c0.08239747 18.29223633-14.91394043 33.28857422-33.28857422 33.28857422z" p-id="972"></path></svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -0,0 +1 @@
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M0 54.857h36.571V128H0V54.857zM91.429 27.43H128V128H91.429V27.429zM45.714 0h36.572v128H45.714V0z"/></svg>

After

Width:  |  Height:  |  Size: 179 B

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="1575982282951" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="902" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M828.40625 90.125H195.59375C137.375 90.125 90.125 137.375 90.125 195.59375v632.8125c0 58.21875 47.25 105.46875 105.46875 105.46875h632.8125c58.21875 0 105.46875-47.25 105.46875-105.46875V195.59375c0-58.21875-47.25-105.46875-105.46875-105.46875z m52.734375 738.28125c0 29.16-23.57015625 52.734375-52.734375 52.734375H195.59375c-29.109375 0-52.734375-23.574375-52.734375-52.734375V195.59375c0-29.109375 23.625-52.734375 52.734375-52.734375h632.8125c29.16 0 52.734375 23.625 52.734375 52.734375v632.8125z" p-id="903"></path><path d="M421.52890625 709.55984375a36.28125 36.28125 0 0 1-27.55265625-12.66890625L205.17453125 476.613125a36.28546875 36.28546875 0 0 1 55.10109375-47.22890625l164.986875 192.4846875 342.16171875-298.48078125a36.2896875 36.2896875 0 0 1 47.70984375 54.68765625L445.3859375 700.6203125a36.3234375 36.3234375 0 0 1-23.85703125 8.93953125z" p-id="904"></path></svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -0,0 +1,4 @@
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="24.000000" height="24.000000" fill="none" customFrame="#000000">
<rect id="chevron-down" width="24.000000" height="24.000000" x="0.000000" y="0.000000" />
<path id="矢量 82" d="M6 9L12 15L18 9" fill-rule="nonzero" stroke="rgb(0,212,255)" stroke-linecap="round" stroke-linejoin="round" stroke-width="2.000000" />
</svg>

After

Width:  |  Height:  |  Size: 433 B

View File

@ -0,0 +1,4 @@
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="24.000000" height="24.000000" fill="none" customFrame="#000000">
<rect id="chevron-left" width="24.000000" height="24.000000" x="0.000000" y="0.000000" />
<path id="矢量 80" d="M15 18L9 12L15 6" fill-rule="nonzero" stroke="rgb(0,212,255)" stroke-linecap="round" stroke-linejoin="round" stroke-width="2.000000" />
</svg>

After

Width:  |  Height:  |  Size: 434 B

View File

@ -0,0 +1,4 @@
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="24.000000" height="24.000000" fill="none" customFrame="#000000">
<rect id="chevron-right" width="24.000000" height="24.000000" x="0.000000" y="0.000000" />
<path id="矢量 81" d="M9 18L15 12L9 6" fill-rule="nonzero" stroke="rgb(0,212,255)" stroke-linecap="round" stroke-linejoin="round" stroke-width="2.000000" />
</svg>

After

Width:  |  Height:  |  Size: 434 B

View File

@ -0,0 +1,16 @@
<svg viewBox="0 0 52 52" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="52.000000" height="52.000000" fill="none" customFrame="url(#clipPath_0)">
<defs>
<clipPath id="clipPath_0">
<rect width="52.000000" height="52.000000" x="0.000000" y="0.000000" rx="8.000000" fill="rgb(255,255,255)" />
</clipPath>
<clipPath id="clipPath_1">
<rect width="22.000000" height="22.000000" x="15.000000" y="15.000000" fill="rgb(255,255,255)" />
</clipPath>
</defs>
<rect id="容器 86" width="52.000000" height="52.000000" x="0.000000" y="0.000000" rx="8.000000" fill="rgb(255,43,43)" fill-opacity="0.200000003" />
<g id="square" customFrame="url(#clipPath_1)">
<rect id="square" width="22.000000" height="22.000000" x="15.000000" y="15.000000" />
<rect id="矩形 13" width="16.500000" height="16.500000" x="17.750000" y="17.750000" rx="1.833333" stroke="rgb(255,43,43)" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.833333" />
</g>
<rect id="容器 86" width="51.000000" height="51.000000" x="0.500000" y="0.500000" rx="7.500000" stroke="rgb(255,43,43)" stroke-width="1.000000" />
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,4 @@
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="24.000000" height="24.000000" fill="none" customFrame="#000000">
<rect id="chevron-up" width="24.000000" height="24.000000" x="0.000000" y="0.000000" />
<path id="矢量 79" d="M18 15L12 9L6 15" fill-rule="nonzero" stroke="rgb(0,212,255)" stroke-linecap="round" stroke-linejoin="round" stroke-width="2.000000" />
</svg>

After

Width:  |  Height:  |  Size: 432 B

View File

@ -0,0 +1 @@
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M54.857 118.857h64V73.143H89.143c-1.902 0-3.52-.668-4.855-2.002-1.335-1.335-2.002-2.954-2.002-4.855V36.57H54.857v82.286zM73.143 16v-4.571a2.2 2.2 0 0 0-.677-1.61 2.198 2.198 0 0 0-1.609-.676H20.571c-.621 0-1.158.225-1.609.676a2.198 2.198 0 0 0-.676 1.61V16a2.2 2.2 0 0 0 .676 1.61c.451.45.988.676 1.61.676h50.285c.622 0 1.158-.226 1.61-.677.45-.45.676-.987.676-1.609zm18.286 48h21.357L91.43 42.642V64zM128 73.143v48c0 1.902-.667 3.52-2.002 4.855-1.335 1.335-2.953 2.002-4.855 2.002H52.57c-1.901 0-3.52-.667-4.854-2.002-1.335-1.335-2.003-2.953-2.003-4.855v-11.429H6.857c-1.902 0-3.52-.667-4.855-2.002C.667 106.377 0 104.759 0 102.857v-96c0-1.902.667-3.52 2.002-4.855C3.337.667 4.955 0 6.857 0h77.714c1.902 0 3.52.667 4.855 2.002 1.335 1.335 2.003 2.953 2.003 4.855V30.29c1 .622 1.856 1.29 2.569 2.003l29.147 29.147c1.335 1.335 2.478 3.145 3.429 5.43.95 2.287 1.426 4.383 1.426 6.291v-.018z"/></svg>

After

Width:  |  Height:  |  Size: 971 B

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="1546567861908" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2422" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M318.577778 819.2L17.066667 512l301.511111-307.2 45.511111 45.511111L96.711111 512l267.377778 261.688889zM705.422222 819.2l-45.511111-45.511111L927.288889 512l-267.377778-261.688889 45.511111-45.511111L1006.933333 512zM540.785778 221.866667l55.751111 11.150222L483.157333 802.133333l-55.751111-11.093333z" p-id="2423"></path></svg>

After

Width:  |  Height:  |  Size: 717 B

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="1577252187056" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2508" xmlns:xlink="http://www.w3.org/1999/xlink" width="81" height="81"><defs><style type="text/css"></style></defs><path d="M747.59340925 691.12859384c11.51396329 0.25305413 22.43746719-0.21087818 40.74171707-1.51832482 29.35428085-2.10878421 35.84933734-2.36183835 46.47761114-0.8856895 24.71495444 3.37405491 41.12129828 21.76265671 32.47528161 47.95376084-85.57447632 258.19957947-442.00123984 249.76444099-628.67084683 50.73735554-153.47733892-159.33976008-153.09775772-414.41833795 0.92786545-573.42069196 159.71934128-162.67163983 424.03439521-166.59397897 565.78689185 0.63263534 80.38686649 94.81095318 108.34934958 169.16669549 89.11723508 230.57450162-15.01454608 47.99593598-50.61082928 77.68762207-119.77896259 114.63352789-4.89237973 2.65706845-29.35428085 15.52065436-35.84933652 19.02123633-46.94154346 25.30541465-63.51659033 41.20565021-62.20914449 58.45550757 2.95229856 39.13904114 24.16667102 52.7196135 70.98168823 53.81618115z m44.41100207 50.10472101c-19.82257471 1.43397372-32.05352527 1.940082-45.63409763 1.6448519-70.34905207-1.60267593-115.98314969-30.91478165-121.38163769-101.64341492-3.45840683-46.05585397 24.7571304-73.13264758 89.24376132-107.96976837 6.7902866-3.66928501 31.37871396-16.57504688 36.06021551-19.06341229 57.69634516-30.83042972 85.15271997-53.73183005 94.76877722-84.47790866 12.77923398-40.78389304-9.10994898-98.94417051-79.24812286-181.6507002-121.17075953-142.97559219-350.14258521-139.60153647-489.2380134 2.06660824-134.49827774 138.84237405-134.79350784 362.12048163-0.42175717 501.637667 158.53842169 168.99799328 451.9968783 181.18676788 534.57688175-11.80919339-4.68150156 0.2952301-10.71262573 0.67481131-18.72600705 1.26527069z" p-id="2509"></path><path d="M346.03865637 637.18588562a78.82636652 78.82636652 0 0 0 78.32025825-79.29029883c0-43.69401562-35.005823-79.29029883-78.32025825-79.29029882a78.82636652 78.82636652 0 0 0-78.36243338 79.29029882c0 43.69401562 35.005823 79.29029883 78.36243338 79.29029883z m0-51.7495729a27.07679361 27.07679361 0 0 1-26.5706845-27.54072593c0-15.30977536 11.97789643-27.54072593 26.5706845-27.54072592 14.55061295 0 26.57068533 12.23095057 26.57068533 27.54072592a27.07679361 27.07679361 0 0 1-26.57068533 27.54072593zM475.7289063 807.11174353a78.82636652 78.82636652 0 0 0 78.3624334-79.29029882c0-43.69401562-34.96364785-79.29029883-78.32025825-79.29029883a78.82636652 78.82636652 0 0 0-78.32025742 79.29029883c0 43.69401562 34.96364785 79.29029883 78.32025742 79.29029882z m0-51.74957208a27.07679361 27.07679361 0 0 1-26.57068532-27.54072674c0-15.30977536 12.06224753-27.54072593 26.57068532-27.54072593 14.59278892 0 26.57068533 12.23095057 26.57068453 27.54072593a27.07679361 27.07679361 0 0 1-26.57068453 27.54072674zM601.24376214 377.21492718a78.82636652 78.82636652 0 0 0 78.32025742-79.29029883c0-43.69401562-34.96364785-79.29029883-78.32025742-79.29029882a78.82636652 78.82636652 0 0 0-78.32025823 79.29029883c0 43.69401562 34.96364785 79.29029883 78.32025824 79.29029883z m1e-8-51.74957208a27.07679361 27.07679361 0 0 1-26.57068534-27.54072675c0-15.30977536 11.97789643-27.54072593 26.57068534-27.54072591 14.55061295 0 26.57068533 12.23095057 26.57068451 27.54072592a27.07679361 27.07679361 0 0 1-26.57068451 27.54072674zM378.80916809 433.85687983a78.82636652 78.82636652 0 0 0 78.32025824-79.29029883c0-43.69401562-34.96364785-79.29029883-78.32025824-79.29029802a78.82636652 78.82636652 0 0 0-78.32025742 79.29029802c0 43.69401562 34.96364785 79.29029883 78.32025742 79.29029883z m0-51.74957209a27.07679361 27.07679361 0 0 1-26.57068451-27.54072674c0-15.30977536 11.97789643-27.54072593 26.57068451-27.54072593 14.55061295 0 26.57068533 12.23095057 26.57068533 27.54072593a27.07679361 27.07679361 0 0 1-26.57068533 27.54072674z" p-id="2510"></path></svg>

After

Width:  |  Height:  |  Size: 3.9 KiB

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="1575804206892" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3145" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M826.56 470.016c-32.896 0-64.384 12.288-89.984 35.52l0-104.96c0-62.208-50.496-112.832-112.64-113.088L623.936 287.04 519.552 287.104C541.824 262.72 554.56 230.72 554.56 197.12c0-73.536-59.904-133.44-133.504-133.44-73.472 0-133.376 59.904-133.376 133.44 0 32.896 12.224 64.256 35.52 89.984L175.232 287.104l0 0.576C113.728 288.704 64 338.88 64 400.576l0.32 0 0.32 116.48C60.864 544.896 70.592 577.728 100.8 588.48c12.736 4.608 37.632 7.488 60.864-25.28 12.992-18.368 34.24-29.248 56.64-29.248 38.336 0 69.504 31.104 69.504 69.312 0 38.4-31.168 69.504-69.504 69.504-22.656 0-44.032-11.264-57.344-30.4C138.688 610.112 112.576 615.36 102.464 619.136c-29.824 10.752-39.104 43.776-38.144 67.392l0 160.384L64 846.912C64 909.248 114.752 960 177.216 960l446.272 0c62.4 0 113.152-50.752 113.152-113.152l0-145.024c24.384 22.272 56.384 35.008 89.984 35.008 73.536 0 133.44-59.904 133.44-133.504C960 529.92 900.096 470.016 826.56 470.016zM826.56 672.896c-22.72 0-44.032-11.264-57.344-30.4-22.272-32.384-48.448-27.136-58.56-23.36-29.824 10.752-39.04 43.776-38.08 67.392l0 160.384c0 27.136-22.016 49.152-49.152 49.152L177.216 896.064C150.08 896 128 873.984 128 846.848l0.32 0 0-145.024c24.384 22.272 56.384 35.008 89.984 35.008 73.6 0 133.504-59.904 133.504-133.504 0-73.472-59.904-133.376-133.504-133.376-32.896 0-64.32 12.288-89.984 35.52l0-104.96L128 400.512c0-27.072 22.08-49.152 49.216-49.152L177.216 351.04 334.656 350.72c3.776 0.512 7.616 0.832 11.52 0.832 24.896 0 50.752-10.816 60.032-37.056 4.544-12.736 7.424-37.568-25.344-60.736C362.624 240.768 351.68 219.52 351.68 197.12c0-38.272 31.104-69.44 69.376-69.44 38.336 0 69.504 31.168 69.504 69.44 0 22.72-11.264 44.032-30.528 57.472C427.968 276.736 433.088 302.784 436.8 313.024c10.752 29.888 43.072 39.232 67.392 38.08l119.232 0 0 0.384c27.136 0 49.152 22.08 49.152 49.152l0.256 116.48c-3.776 27.84 6.016 60.736 36.224 71.488 12.736 4.608 37.632 7.488 60.8-25.28 13.056-18.368 34.24-29.248 56.704-29.248C864.832 534.016 896 565.12 896 603.392 896 641.728 864.832 672.896 826.56 672.896z" p-id="3146"></path></svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

Some files were not shown because too many files have changed in this diff Show More