diff --git a/mocks/robot/getAll b/mocks/robot/getAll
index 6cb99cb..57067c9 100644
--- a/mocks/robot/getAll
+++ b/mocks/robot/getAll
@@ -3,44 +3,46 @@
"success": true,
"data": [
{
- "gid": "mock-robot-group-1",
"id": "mock-robot-1",
"label": "模拟机器人A",
"brand": "模拟品牌A",
- "type": 1
+ "type": 1,
+ "ip": "127.0.1.1"
},
{
- "gid": "mock-robot-group-1",
"id": "mock-robot-2",
"label": "模拟机器人B",
"brand": "模拟品牌A",
- "type": 2
+ "type": 2,
+ "ip": "127.0.1.2"
},
{
- "gid": "mock-robot-group-1",
"id": "mock-robot-3",
"label": "模拟机器人C",
"brand": "模拟品牌A",
- "type": 3
+ "type": 3,
+ "ip": "127.0.1.3"
},
{
- "gid": "mock-robot-group-2",
"id": "mock-robot-4",
"label": "模拟机器人D",
"brand": "模拟品牌B",
- "type": 1
+ "type": 1,
+ "ip": "127.0.2.1"
},
{
"id": "mock-robot-5",
"label": "模拟机器人E",
"brand": "模拟品牌B",
- "type": 2
+ "type": 2,
+ "ip": "127.0.2.2"
},
{
"id": "mock-robot-6",
"label": "模拟机器人F",
"brand": "模拟品牌B",
- "type": 3
+ "type": 3,
+ "ip": "127.0.2.3"
}
],
"message": "模拟提示"
diff --git a/mocks/robot/register b/mocks/robot/register
new file mode 100644
index 0000000..23972f9
--- /dev/null
+++ b/mocks/robot/register
@@ -0,0 +1,12 @@
+{
+ "code": 200,
+ "success": true,
+ "data": {
+ "id": "mock-robot-0",
+ "label": "模拟机器人-注册",
+ "brand": "模拟品牌A",
+ "type": 1,
+ "ip": "127.0.0.0"
+ },
+ "message": "模拟提示"
+}
diff --git a/mocks/robot/seizeByIds b/mocks/robot/seizeByIds
new file mode 100644
index 0000000..8ecc2d2
--- /dev/null
+++ b/mocks/robot/seizeByIds
@@ -0,0 +1,5 @@
+{
+ "code": 200,
+ "success": false,
+ "message": "模拟提示"
+}
diff --git a/mocks/scene/getById b/mocks/scene/getById
index c51cffc..f716eb6 100644
--- a/mocks/scene/getById
+++ b/mocks/scene/getById
@@ -4,49 +4,7 @@
"data": {
"id": "mock-scene-1",
"label": "模拟场景A",
- "robotGroups": [
- {
- "id": "mock-robot-group-1",
- "label": "模拟机器人组A",
- "robots": [
- {
- "gid": "mock-robot-group-1",
- "id": "mock-robot-1",
- "label": "模拟机器人A",
- "brand": "模拟品牌A",
- "type": 1
- },
- {
- "gid": "mock-robot-group-1",
- "id": "mock-robot-2",
- "label": "模拟机器人B",
- "brand": "模拟品牌A",
- "type": 2
- },
- {
- "gid": "mock-robot-group-1",
- "id": "mock-robot-3",
- "label": "模拟机器人C",
- "brand": "模拟品牌A",
- "type": 3
- }
- ]
- },
- {
- "id": "mock-robot-group-2",
- "label": "模拟机器人组B",
- "robots": [
- {
- "gid": "mock-robot-group-2",
- "id": "mock-robot-4",
- "label": "模拟机器人D",
- "brand": "模拟品牌B",
- "type": 1
- }
- ]
- }
- ],
- "map": ""
+ "json": "{\"x\":0,\"y\":0,\"scale\":1,\"pens\":[],\"origin\":{\"x\":0,\"y\":0},\"center\":{\"x\":0,\"y\":0},\"paths\":{},\"template\":\"4c2a10f\",\"locked\":10,\"version\":\"1.0.78\",\"dataPoints\":[],\"robotGroups\":[{\"id\":\"mock-robot-group-1\",\"label\":\"模拟机器人组A\",\"robots\":[\"mock-robot-1\",\"mock-robot-2\",\"mock-robot-3\"]},{\"sid\":\"mock-scene-1\",\"id\":\"mock-robot-group-2\",\"label\":\"模拟机器人组B\",\"robots\":[\"mock-robot-4\"]}],\"robots\":[{\"gid\":\"mock-robot-group-1\",\"id\":\"mock-robot-1\",\"label\":\"模拟机器人A\",\"brand\":\"模拟品牌A\",\"type\":1},{\"gid\":\"mock-robot-group-1\",\"id\":\"mock-robot-2\",\"label\":\"模拟机器人B\",\"brand\":\"模拟品牌A\",\"type\":2},{\"gid\":\"mock-robot-group-1\",\"id\":\"mock-robot-3\",\"label\":\"模拟机器人C\",\"brand\":\"模拟品牌A\",\"type\":3},{\"gid\":\"mock-robot-group-2\",\"id\":\"mock-robot-4\",\"label\":\"模拟机器人D\",\"brand\":\"模拟品牌B\",\"type\":1}]}"
},
"message": "模拟提示"
}
diff --git a/src/App.vue b/src/App.vue
index cb67900..eeef236 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -1,6 +1,9 @@
@@ -9,7 +12,14 @@ import sTheme from '@core/theme.service';
:theme="sTheme.ant"
:autoInsertSpaceInButton="false"
:wave="{ disabled: true }"
+ :input="{ autocomplete: 'off' }"
>
+
+
+
+ {{ $t('暂无数据') }}
+
+
diff --git a/src/_ant.scss b/src/_ant.scss
index af9a954..5756e62 100644
--- a/src/_ant.scss
+++ b/src/_ant.scss
@@ -10,36 +10,47 @@
.ant-btn {
display: inline-flex;
align-items: center;
+ justify-content: center;
font: 400 14px/22px Roboto;
+ vertical-align: top;
border-radius: 2px;
+ box-shadow: none !important;
+
+ &.ant-btn-lg {
+ padding-block: 8px;
+ border-radius: 4px;
+ }
&.ant-btn-default {
color: get-color(primary);
- background-color: get-color(141414);
+ background-color: get-color(neutral1);
border-color: get-color(primary);
- box-shadow: 0 2px 0 0 get-color(00000005);
+ }
+
+ &.ant-btn-dashed {
+ font-weight: 500;
+ color: get-color(primary_text-active);
+ background-color: get-color(info_bg);
+ border-color: get-color(primary_border);
}
&.ant-btn-primary {
font-weight: 500;
- color: get-color(141414);
+ color: get-color(text1-inverse);
background-color: get-color(primary);
border-color: get-color(primary);
- box-shadow: 0 2px 0 0 get-color(0000000a);
}
&.warning {
font-weight: 500;
- color: get-color(141414);
+ color: get-color(text1-inverse);
background-color: get-color(warning);
border-color: get-color(warning);
- box-shadow: 0 2px 0 0 get-color(0000000a);
}
&.icon-btn {
background-color: transparent;
border: none;
- box-shadow: none;
&.ant-btn-sm {
padding: 0;
@@ -53,8 +64,8 @@
}
& > .ant-checkbox-inner {
- background-color: get-color(141414);
- border-color: get-color(434343);
+ background-color: get-color(neutral1);
+ border-color: get-color(neutral5);
border-radius: 2px;
}
@@ -63,7 +74,13 @@
border-color: get-color(primary);
&::after {
- border-color: get-color(141414);
+ border-color: get-color(neutral1);
+ }
+ }
+
+ &.ant-checkbox-indeterminate:not(.ant-checkbox-disabled) > .ant-checkbox-inner {
+ &::after {
+ background-color: get-color(primary);
}
}
}
@@ -71,7 +88,7 @@
.ant-checkbox-wrapper {
align-items: center;
font: 400 14px/22px Roboto;
- color: get-color(ffffffa6);
+ color: get-color(text2);
}
.ant-collapse.ant-collapse-ghost {
@@ -79,14 +96,19 @@
& > .ant-collapse-item {
& > .ant-collapse-header {
+ gap: 8px;
align-items: center;
height: 36px;
- padding: 0 0 0 8px;
+ padding: 0;
font: 500 14px/22px Roboto;
- color: get-color(ffffffd9);
- background: get-color(ffffff14);
+ color: get-color(text1);
+ background: get-color(fill3);
border-radius: 2px;
+ & > .ant-collapse-header-text {
+ padding-inline: 8px;
+ }
+
& > .ant-collapse-expand-icon {
width: 24px;
height: 24px;
@@ -114,59 +136,355 @@
}
}
- .ant-input {
- color: get-color(ffffffd9);
- background-color: transparent;
+ .ant-dropdown-menu-root {
+ padding: 0;
+ overflow: hidden;
+ background-color: get-color(neutral9);
+ border-radius: 4px;
+ box-shadow: 0 2px 8px 0 get-color(shadow2);
- &::placeholder {
- color: get-color(ffffff40);
+ & > .ant-dropdown-menu-item {
+ padding: 8px 16px;
+ font: 400 14px/16px Roboto;
+ color: get-color(text1-inverse);
+ border-radius: 0;
+
+ &:hover {
+ background-color: get-color(neutral8);
+ }
}
}
- .ant-input-affix-wrapper {
- box-shadow: none;
+ .ant-flex {
+ & > * {
+ flex: none;
+ }
+ }
+
+ .ant-form {
+ & > .ant-form-item {
+ margin-block-end: 8px;
+
+ & > .ant-form-item-row {
+ justify-content: flex-end;
+ }
+
+ .ant-form-item-label {
+ font: 400 14px/22px Roboto;
+ color: get-color(text1);
+
+ & > .ant-form-item-required::before {
+ color: get-color(error);
+ }
+ }
+
+ .ant-form-item-control {
+ max-width: 292px;
+ }
+ }
+ }
+
+ .ant-input {
+ color: get-color(text1);
+ background-color: get-color(neutral1);
+ border-color: get-color(neutral5);
+ border-radius: 2px;
+ box-shadow: none !important;
+
+ &.ant-input-status-error {
+ border-color: get-color(error) !important;
+ outline-color: rgba($color: get-color(error), $alpha: 20%) !important;
+ }
+
+ &:not(:disabled):focus {
+ border-color: get-color(primary);
+ outline: 2px solid rgba($color: get-color(primary), $alpha: 20%);
+ transition: none;
+ }
+
+ &::placeholder {
+ color: get-color(text4);
+ }
+ }
+
+ .ant-input-affix-wrapper:not(.ant-input-affix-wrapper-borderless) {
+ border-radius: 2px;
+ box-shadow: none !important;
+
+ &.ant-input-affix-wrapper-status-error {
+ border-color: get-color(error) !important;
+ outline-color: rgba($color: get-color(error), $alpha: 20%) !important;
+ }
+
+ &:not(.ant-input-affix-wrapper-disabled).ant-input-affix-wrapper-focused {
+ border-color: get-color(primary);
+ outline: 2px solid rgba($color: get-color(primary), $alpha: 20%);
+ transition: none;
+ }
&::before {
display: none;
}
+ & > .ant-input {
+ background-color: transparent;
+ outline: none;
+ }
+
& > .ant-input-suffix {
margin-inline-start: 12px;
font-size: 16px;
- color: get-color(ffffffd9);
+ color: get-color(icon-disabled);
}
&.search {
padding-inline-start: 7px;
- background-color: transparent;
- border-color: get-color(595959);
- border-radius: 4px;
+ background-color: get-color(fill3);
+ border-color: get-color(border2);
+ }
+ }
+
+ .ant-input-number {
+ width: 100%;
+ color: get-color(text1);
+ background-color: get-color(neutral1);
+ border-color: get-color(neutral5);
+ border-radius: 2px;
+ box-shadow: none !important;
+
+ &.ant-input-number-status-error {
+ border-color: get-color(error) !important;
+ outline-color: rgba($color: get-color(error), $alpha: 20%) !important;
+ }
+
+ &:not(:disabled).ant-input-number-focused {
+ border-color: get-color(primary);
+ outline: 2px solid rgba($color: get-color(primary), $alpha: 20%);
+ transition: none;
}
}
.ant-layout {
- background-color: get-color(2a2c2c);
+ background-color: get-color(page_bg);
& > .ant-layout-header {
+ z-index: 10;
line-height: unset;
- background-color: get-color(242525);
- box-shadow: 0 2px 8px 0 get-color(00000026);
+ background-color: get-color(sider_bg);
+ box-shadow: 0 2px 8px 0 get-color(shadow2);
}
& > .ant-layout-sider {
- background-color: get-color(36393a);
+ z-index: 20;
+ background-color: get-color(sider_bg2);
border-radius: 8px;
- box-shadow: 0 2px 8px 0 get-color(ffffff14);
+ box-shadow: 0 2px 8px 0 get-color(shadow1);
}
}
.ant-list-items {
& > .ant-list-item {
padding: 0;
- border-color: get-color(f9f9f914);
+ border-color: get-color(item_bg-hover);
&:hover {
- background-color: get-color(f9f9f914);
+ background-color: get-color(item_bg-hover);
+ }
+
+ &.selected {
+ background-color: get-color(primary_bg);
+ }
+ }
+ }
+
+ .ant-modal.ant-modal-confirm .ant-modal-content {
+ padding: 32px 32px 24px;
+ background-color: get-color(popover);
+ border-radius: 2px;
+ box-shadow: none !important;
+
+ & > .ant-modal-body > .ant-modal-confirm-body-wrapper {
+ & > .ant-modal-confirm-body {
+ & > .anticon {
+ margin-inline-end: 16px;
+ color: get-color(warning);
+ }
+
+ & > .ant-modal-confirm-title {
+ font: 500 16px/24px Roboto;
+ color: get-color(text1);
+ }
+
+ & > .ant-modal-confirm-content {
+ margin: 0;
+ }
+ }
+
+ & > .ant-modal-confirm-btns {
+ margin-block-start: 24px;
+ }
+ }
+ }
+
+ .ant-modal:not(.ant-modal-confirm) .ant-modal-content {
+ padding: 0;
+ background-color: get-color(popover);
+ border-radius: 2px;
+ box-shadow: none !important;
+
+ & > .ant-modal-close {
+ inset: 17px 16px 17px auto;
+ width: 1em;
+ height: 1em;
+ font-size: 16px;
+ color: get-color(text3);
+ background-color: transparent;
+
+ & > .ant-modal-close-x {
+ width: 1em;
+ height: 1em;
+ line-height: 1;
+ }
+ }
+
+ & > .ant-modal-header {
+ padding: 14px 16px;
+ margin: 0;
+ background-color: get-color(bg_layout);
+ border-radius: 2px 2px 0 0;
+
+ & > .ant-modal-title {
+ font: 500 16px/22px Roboto;
+ color: get-color(text1);
+ }
+ }
+
+ & > .ant-modal-body {
+ padding: 24px 16px;
+ }
+
+ & > .ant-modal-footer {
+ padding: 16px;
+ margin: 0;
+ border-block-start: 1px solid get-color(border2);
+ }
+ }
+
+ .ant-modal-mask {
+ background-color: get-color(mask);
+ }
+
+ .ant-select:not(.ant-select-borderless) {
+ & > .ant-select-selector {
+ color: get-color(text1);
+ background-color: get-color(neutral1);
+ border-color: get-color(neutral5);
+ border-radius: 2px;
+ box-shadow: none !important;
+
+ & > .ant-select-selection-item {
+ color: inherit;
+ }
+
+ & > .ant-select-selection-placeholder {
+ color: get-color(text4);
+ }
+ }
+
+ &.ant-select-disabled > .ant-select-selector {
+ color: get-color(text4);
+ background-color: get-color(fill3);
+ }
+
+ &.ant-select-status-error > .ant-select-selector {
+ border-color: get-color(error) !important;
+ outline-color: rgba($color: get-color(error), $alpha: 20%) !important;
+ }
+
+ &:not(.ant-select-disabled):hover > .ant-select-selector {
+ border-color: get-color(neutral5);
+ }
+
+ &:not(.ant-select-disabled).ant-select-focused > .ant-select-selector {
+ border-color: get-color(primary);
+ outline: 2px solid rgba($color: get-color(primary), $alpha: 20%);
+ transition: none;
+ }
+
+ & > .ant-select-arrow {
+ color: get-color(icon-disabled);
+ }
+ }
+
+ .ant-select-dropdown {
+ padding: 4px 0;
+ background-color: get-color(popover);
+ border-radius: 2px;
+ box-shadow: 0 0 4px 0 get-color(shadow1);
+
+ .ant-select-item {
+ font: 400 14px/22px Roboto;
+ color: get-color(text1);
+ background-color: transparent;
+ border-radius: 0;
+
+ &.ant-select-item-option-selected {
+ background-color: get-color(fill1);
+ }
+
+ &:not(.ant-select-item-option-disabled):hover {
+ background-color: get-color(item_bg-hover);
+ }
+ }
+ }
+
+ .ant-table.ant-table-bordered {
+ background-color: transparent;
+ border-radius: 0;
+
+ & > .ant-table-container {
+ border-color: get-color(divider);
+ border-radius: 0;
+
+ & > .ant-table-header {
+ border-radius: 0;
+
+ & > table {
+ border-color: get-color(divider);
+ border-radius: 0;
+
+ & > .ant-table-thead > tr > .ant-table-cell {
+ padding: 9px 16px 8px;
+ font: 500 14px/22px Roboto;
+ color: get-color(text3);
+ background-color: get-color(neutral3);
+ border-color: get-color(divider);
+ border-radius: 0;
+ }
+ }
+ }
+
+ & > .ant-table-body {
+ & > table {
+ & > .ant-table-tbody > .ant-table-row > .ant-table-cell {
+ padding: 9px 16px 8px;
+ font: 400 14px/22px Roboto;
+ color: get-color(text1);
+ background-color: transparent;
+ border-color: get-color(divider);
+ }
+ }
+ }
+
+ & > .ant-table-header > table > .ant-table-thead > tr > .ant-table-cell.ant-table-selection-column,
+ & > .ant-table-body > table > .ant-table-tbody > .ant-table-row > .ant-table-cell.ant-table-selection-column {
+ padding: 0;
+ }
+
+ & > .ant-table-body > table > .ant-table-tbody > .ant-table-placeholder > .ant-table-cell {
+ padding: 8px 0 0;
+ background-color: transparent;
+ border-color: get-color(divider);
}
}
}
@@ -176,14 +494,14 @@
& > .ant-tabs-nav {
margin: 0;
- background-color: get-color(ffffff2e);
+ background-color: get-color(fill1);
& > .ant-tabs-nav-wrap > .ant-tabs-nav-list {
& > .ant-tabs-tab {
padding: 14px 32px;
margin: 0;
font: 400 16px/20px Roboto;
- color: get-color(ffffff73);
+ color: get-color(text3);
background-color: transparent;
border: none;
border-radius: 0;
@@ -196,7 +514,7 @@
&.ant-tabs-tab-active {
font-weight: 600;
color: get-color(primary);
- background-color: get-color(36393a);
+ background-color: get-color(sider_bg2);
}
}
@@ -215,11 +533,15 @@
.ant-typography {
font: 400 14px/22px Roboto;
- color: get-color(ffffffa6);
+ color: get-color(text2);
+
+ &.ant-typography-disabled {
+ color: get-color(text4);
+ }
& > strong {
font: 500 16px/22px SourceHanSansSC;
- color: get-color(ffffffd9);
+ color: get-color(text1);
}
}
}
diff --git a/src/_icon.scss b/src/_icon.scss
index d40d68d..1d46a9f 100644
--- a/src/_icon.scss
+++ b/src/_icon.scss
@@ -2,9 +2,13 @@
@use 'asset/icons/icon' as *;
@include themed {
- i {
- &.bg-1 {
- background-color: get-color(ffffffd9);
+ .icon-btn {
+ &.panel-btn {
+ color: get-color(ffffffd9);
+
+ &:disabled {
+ color: get-color(ffffff2e);
+ }
}
}
@@ -21,6 +25,13 @@
.mask {
@extend %icon;
+ color: inherit;
+ background-color: currentcolor;
+
+ &.primary {
+ color: get-color(icon-brand);
+ }
+
@each $icon in $icons {
&.#{$icon} {
mask: get-icon($icon);
diff --git a/src/apis/map/type.ts b/src/apis/map/type.ts
index bbaa0f9..46ea61b 100644
--- a/src/apis/map/type.ts
+++ b/src/apis/map/type.ts
@@ -12,13 +12,13 @@ export interface MapPen extends Pen {
area?: MapAreaInfo; // 区域信息
attrs?: Record; // 额外属性
- activeAttrs?: string[]; // 已激活的额外属性
+ activeAttrs?: Array; // 已激活的额外属性
}
//#region 点位
export interface MapPointInfo {
type: MapPointType; // 点位类型
- robots?: RobotInfo['id'][]; // 绑定机器人id集合
+ robots?: Array; // 绑定机器人id集合
}
//#endregion
@@ -32,7 +32,7 @@ export interface MapRouteInfo {
//#region 区域
export interface MapAreaInfo {
type: MapAreaType; // 区域类型
- points?: string[]; // 绑定点位id集合
- routes?: string[]; // 绑定线路id集合
+ points?: Array; // 绑定点位id集合
+ routes?: Array; // 绑定线路id集合
}
//#endregion
diff --git a/src/apis/robot/api.ts b/src/apis/robot/api.ts
index ac27a42..229b5b8 100644
--- a/src/apis/robot/api.ts
+++ b/src/apis/robot/api.ts
@@ -1,13 +1,39 @@
import http from '@core/http';
-import type { RobotInfo } from './type';
+import type { RobotDetail, RobotInfo } from './type';
const enum API {
获取所有机器人 = '/robot/getAll',
+
+ 注册机器人 = '/robot/register',
+
+ 批量抢占控制权 = '/robot/seizeByIds',
}
-export async function getAllRobot(): Promise {
+export async function getAllRobots(): Promise> {
type D = RobotInfo[];
- const data = await http.get(API.获取所有机器人);
+ const data = await http.post(API.获取所有机器人);
return data ?? [];
}
+
+export async function registerRobot(robot: Omit): Promise {
+ type B = Omit;
+ type D = RobotInfo;
+ const body = robot;
+ const data = await http.post(API.注册机器人, body);
+ return data ?? null;
+}
+
+export async function seizeRobotByIds(ids: Array): Promise {
+ if (!ids.length) return false;
+ type B = { ids: string[] };
+ type D = void;
+ try {
+ const body = { ids };
+ await http.post(API.批量抢占控制权, body);
+ return true;
+ } catch (error) {
+ console.debug(error);
+ return false;
+ }
+}
diff --git a/src/apis/robot/constant.ts b/src/apis/robot/constant.ts
index 8b29311..19ed990 100644
--- a/src/apis/robot/constant.ts
+++ b/src/apis/robot/constant.ts
@@ -1,12 +1,18 @@
+import { isNumber } from 'lodash-es';
+
export enum RobotBrand {
'先工' = 1,
}
+export const ROBOT_BRAND_OPTIONS = >(
+ Object.entries(RobotBrand).filter(([, v]) => isNumber(v))
+);
export enum RobotType {
叉车机器人 = 1,
AMR机器人,
料箱机器人,
}
+export const ROBOT_TYPE_OPTIONS = >Object.entries(RobotType).filter(([, v]) => isNumber(v));
export enum RobotState {
'任务执行中' = 1,
diff --git a/src/apis/robot/type.ts b/src/apis/robot/type.ts
index 36defc8..96997cf 100644
--- a/src/apis/robot/type.ts
+++ b/src/apis/robot/type.ts
@@ -3,7 +3,7 @@ import type { RobotBrand, RobotState, RobotType } from './constant';
export interface RobotGroup {
id: string; // 机器人组id
label: string; // 机器人组名称
- robots: RobotInfo[]; // 机器人列表
+ robots?: Array; // 机器人列表
}
export interface RobotInfo {
@@ -12,19 +12,20 @@ export interface RobotInfo {
label: string; // 机器人名称
brand: RobotBrand; // 机器人品牌
type: RobotType; // 机器人类型
-}
-
-export interface RobotDetail extends RobotInfo {
ip: string; // 机器人ip
- isSimulative?: boolean; // 是否仿真机器人
battery?: number; // 机器人电量
- minBattery?: number; // 最小电量
- maxBattery?: number; // 最大电量
- chargeBattery?: number; // 充电电量
- swapBattery?: number; // 交换电量
isConnected?: boolean; // 机器人连接状态
state?: RobotState; // 机器人状态
canOrder?: boolean; // 接单状态
canStop?: boolean; // 急停状态
canControl?: boolean; // 控制状态
}
+
+export interface RobotDetail extends RobotInfo {
+ isSimulative?: 0 | 1; // 是否仿真机器人
+ minBattery?: number; // 最小电量
+ maxBattery?: number; // 最大电量
+ chargeBattery?: number; // 充电电量
+ taskBattery?: number; // 任务电量
+ swapBattery?: number; // 交换电量
+}
diff --git a/src/apis/scene/api.ts b/src/apis/scene/api.ts
index 1fe4446..790cb23 100644
--- a/src/apis/scene/api.ts
+++ b/src/apis/scene/api.ts
@@ -10,7 +10,12 @@ export async function getSceneById(id: SceneInfo['id']): Promise(API.获取场景, body);
- return data ?? null;
+ try {
+ const body = { id };
+ const data = await http.post(API.获取场景, body);
+ return data ?? null;
+ } catch (error) {
+ console.debug(error);
+ return null;
+ }
}
diff --git a/src/apis/scene/type.ts b/src/apis/scene/type.ts
index 84c52f3..b5b790f 100644
--- a/src/apis/scene/type.ts
+++ b/src/apis/scene/type.ts
@@ -1,10 +1,15 @@
-import type { RobotGroup } from '@api/robot';
+import type { RobotGroup, RobotInfo } from '@api/robot';
+import type { Meta2dData } from '@meta2d/core';
export interface SceneInfo {
id: string; // 场景id
label: string; // 场景名称
}
export interface SceneDetail extends SceneInfo {
- robotGroups?: RobotGroup[]; // 机器人组列表
- map?: string; // 地图JSON
+ json?: string; // 场景JSON
+}
+
+export interface SceneData extends Meta2dData {
+ robots?: Array;
+ robotGroups?: Array;
}
diff --git a/src/assets/fonts/Roboto-Bold.ttf b/src/assets/fonts/Roboto-Bold.ttf
deleted file mode 100644
index 91ec212..0000000
Binary files a/src/assets/fonts/Roboto-Bold.ttf and /dev/null differ
diff --git a/src/assets/fonts/Roboto-SemiBold.ttf b/src/assets/fonts/Roboto-SemiBold.ttf
new file mode 100644
index 0000000..3f34834
Binary files /dev/null and b/src/assets/fonts/Roboto-SemiBold.ttf differ
diff --git a/src/assets/fonts/_font.scss b/src/assets/fonts/_font.scss
index 7229722..f8c6ac8 100644
--- a/src/assets/fonts/_font.scss
+++ b/src/assets/fonts/_font.scss
@@ -2,19 +2,19 @@
@font-face {
font-family: Roboto;
font-weight: 400;
- src: url(./Roboto-Regular.otf) format('truetype');
+ src: url(./Roboto-Regular.ttf) format('truetype');
}
@font-face {
font-family: Roboto;
font-weight: 500;
- src: url(./Roboto-Medium.otf) format('truetype');
+ src: url(./Roboto-Medium.ttf) format('truetype');
}
@font-face {
font-family: Roboto;
font-weight: 600;
- src: url(./Roboto-Bold.ttf) format('truetype');
+ src: url(./Roboto-SemiBold.ttf) format('truetype');
}
// 思源黑体
diff --git a/src/assets/icons/_icon.scss b/src/assets/icons/_icon.scss
index f7c5150..51a6034 100644
--- a/src/assets/icons/_icon.scss
+++ b/src/assets/icons/_icon.scss
@@ -1 +1 @@
-$icons: (control, dropdown, edit, exit, trash_fill);
+$icons: (control, dot, dropdown, edit, exit, pen, plus, register, trash_fill, trash);
diff --git a/src/assets/icons/dark/dot.png b/src/assets/icons/dark/dot.png
new file mode 100644
index 0000000..9eecd73
Binary files /dev/null and b/src/assets/icons/dark/dot.png differ
diff --git a/src/assets/icons/dark/pen.png b/src/assets/icons/dark/pen.png
new file mode 100644
index 0000000..c0dcf78
Binary files /dev/null and b/src/assets/icons/dark/pen.png differ
diff --git a/src/assets/icons/dark/plus.png b/src/assets/icons/dark/plus.png
new file mode 100644
index 0000000..d1f6144
Binary files /dev/null and b/src/assets/icons/dark/plus.png differ
diff --git a/src/assets/icons/dark/register.png b/src/assets/icons/dark/register.png
new file mode 100644
index 0000000..d28a7c7
Binary files /dev/null and b/src/assets/icons/dark/register.png differ
diff --git a/src/assets/icons/dark/trash.png b/src/assets/icons/dark/trash.png
new file mode 100644
index 0000000..1458edb
Binary files /dev/null and b/src/assets/icons/dark/trash.png differ
diff --git a/src/assets/images/empty-dark.png b/src/assets/images/empty-dark.png
new file mode 100644
index 0000000..94b6cdf
Binary files /dev/null and b/src/assets/images/empty-dark.png differ
diff --git a/src/assets/themes/_dark.scss b/src/assets/themes/_dark.scss
index 8c84f93..ac3bc7b 100644
--- a/src/assets/themes/_dark.scss
+++ b/src/assets/themes/_dark.scss
@@ -1,29 +1,130 @@
$colors: (
+ // 品牌色
primary: #0dbb8a,
+ primary-hover: #2bb589,
+ primary-active: #0f8161,
+ primary_bg: #11221e,
+ primary_bg-hover: #10342b,
+ primary_border: #124637,
+ primary_border-hover: #115f49,
+ primary_text: #0ea278,
+ primary_text-hover: #2bb589,
+ primary_text-active: #0f8161,
+
+ // 成功色
+ success: #49aa19,
+ success-hover: #306317,
+ success-active: #3c8618,
+ success_bg: #162312,
+ success_bg-hover: #1d3712,
+ success_border: #274916,
+ success_border-hover: #306317,
+ success_text: #49aa19,
+ success_text-hover: #6abe39,
+ success_text-active: #3c8618,
+
+ // 警戒色
warning: #d89614,
- 111e1b: #111e1b,
+ warning-hover: #7c5914,
+ warning-active: #aa7714,
+ warning_bg: #2b2111,
+ warning_bg-hover: #443111,
+ warning_border: #594214,
+ warning_border-hover: #7c5914,
+ warning_text: #d89614,
+ warning_text-hover: #e8b339,
+ warning_text-active: #aa7714,
- 141414: #141414,
- 595959: #595959,
- 00000005: #00000005,
- 0000000a: #0000000a,
- 00000026: #00000026,
- f9f9f914: #f9f9f914,
+ // 错误色
+ error: #dc4446,
+ error-hover: #e86e6b,
+ error-active: #ad393a,
+ error_bg: #2c1618,
+ error_bg-hover: #451d1f,
+ error_border: #5b2526,
+ error_border-hover: #7e2e2f,
+ error_text: #dc4446,
+ error_text-hover: #e86e6b,
+ error_text-active: #ad393a,
- 000000: #000,
- 2a2c2c: #2a2c2c,
- 242525: #242525,
- 36393a: #36393a,
+ // 信息色
+ info: #0ea278,
+ info-hover: #115441,
+ info-active: #107055,
+ info_bg: #111e1b,
+ info_bg-hover: #102e27,
+ info_border: #123f32,
+ info_border-hover: #115441,
+ info_text: #0f8d69,
+ info_text-hover: #2aa07b,
+ info_text-active: #107055,
- 424242: #424242,
- 434343: #434343,
+ // 中性色-文本
+ text1: #ffffffd9,
+ text1-inverse: #141414,
+ text2: #ffffffa6,
+ text3: #ffffff73,
+ text4: #ffffff40,
- ffffff2e: #ffffff2e,
- ffffff14: #ffffff14,
- ffffff0a: #ffffff0a,
+ // 中性色-描边
+ border1: #424242,
+ border2: #303030,
- ffffffd9: #ffffffd9,
- ffffffa6: #ffffffa6,
- ffffff73: #ffffff73,
- ffffff40: #ffffff40,
+ // 中性色-填充
+ fill1: #ffffff2e,
+ fill2: #ffffff1f,
+ fill3: #ffffff14,
+ fill4: #ffffff0a,
+
+ // 中性色-背景
+ bg_component: #141414,
+ bg_modal: #1f1f1f,
+ bg_layout: #000,
+ bg_attention: #424242,
+ bg_mask: #00000073,
+ bg_title: #525f74,
+
+ // 中性色-阴影
+ shadow1: #212121,
+ shadow2: #141414,
+ shadow3: #0dbb8a,
+
+ // 图标
+ icon: #fff,
+ icon-inverse: #141414,
+ icon-hover: #434343,
+ icon-disabled: #595959,
+ icon-selected: #0dbb8a,
+ icon-brand: #0dbb8a,
+
+ // Neutral
+ neutral1: #141414,
+ neutral2: #212121,
+ neutral3: #1f1f1f,
+ neutral4: #262626,
+ neutral5: #434343,
+ neutral6: #595959,
+ neutral7: #8c8c8c,
+ neutral8: #bfbfbf,
+ neutral9: #f0f0f0,
+ neutral10: #f5f5f5,
+ neutral11: #fafafa,
+ neutral12: #fff,
+ neutral13: #fff,
+
+ // Conditional
+ divider: #303030,
+ header_bg: #0f1c29,
+ item_bg-hover: #f9f9f914,
+ mask: #000c,
+ menu_item_bg-selected: #177ddc,
+ menu_item_text-selected: #fff,
+ side_icon: #a0a9c0,
+ sider_bg: #242525,
+ sider_bg2: #36393a,
+ table_column: #ffffff05,
+ table_filter_trigger-hover: #0000000a,
+ page_bg: #2a2c2c,
+ popover: #232525,
+ popover_header_bg: #454647
);
diff --git a/src/assets/themes/_light.scss b/src/assets/themes/_light.scss
index 7f6e0e3..16bd3ff 100644
--- a/src/assets/themes/_light.scss
+++ b/src/assets/themes/_light.scss
@@ -1,3 +1,130 @@
$colors: (
- text-1: #000,
+ // 品牌色
+ primary: #0dbb8a,
+ primary-hover: #2ec796,
+ primary-active: #039470,
+ primary_bg: #e1faef,
+ primary_bg-hover: #a8edd1,
+ primary_border: #7be0ba,
+ primary_border-hover: #53d4a6,
+ primary_text: #0dbb8a,
+ primary_text-hover: #2ec796,
+ primary_text-active: #039470,
+
+ // 成功色
+ success: #52c41a,
+ success-hover: #95de64,
+ success-active: #389e0d,
+ success_bg: #f6ffed,
+ success_bg-hover: #d9f7be,
+ success_border: #b7eb8f,
+ success_border-hover: #95de64,
+ success_text: #52c41a,
+ success_text-hover: #73d13d,
+ success_text-active: #389e0d,
+
+ // 警戒色
+ warning: #faad14,
+ warning-hover: #ffd666,
+ warning-active: #d48806,
+ warning_bg: #fffbe6,
+ warning_bg-hover: #fff1b8,
+ warning_border: #ffe58f,
+ warning_border-hover: #ffd666,
+ warning_text: #faad14,
+ warning_text-hover: #ffc53d,
+ warning_text-active: #d48806,
+
+ // 错误色
+ error: #ff4d4f,
+ error-hover: #ff7875,
+ error-active: #d9363e,
+ error_bg: #fff2f0,
+ error_bg-hover: #fff1f0,
+ error_border: #ffccc7,
+ error_border-hover: #ffa39e,
+ error_text: #ff4d4f,
+ error_text-hover: #ff7875,
+ error_text-active: #d9363e,
+
+ // 信息色
+ info: #0dbb8a,
+ info-hover: #53d4a6,
+ info-active: #039470,
+ info_bg: #e1faef,
+ info_bg-hover: #a8edd1,
+ info_border: #7be0ba,
+ info_border-hover: #53d4a6,
+ info_text: #0dbb8a,
+ info_text-hover: #2ec796,
+ info_text-active: #039470,
+
+ // 中性色-文本
+ text1: #000000e0,
+ text1-inverse: #fff,
+ text2: #000000a6,
+ text3: #00000073,
+ text4: #00000040,
+
+ // 中性色-描边
+ border1: #d9d9d9,
+ border2: #f0f0f0,
+
+ // 中性色-填充
+ fill1: #00000026,
+ fill2: #0000000f,
+ fill3: #0000000a,
+ fill4: #00000005,
+
+ // 中性色-背景
+ bg_component: #fff,
+ bg_modal: #fff,
+ bg_layout: #f5f5f5,
+ bg_attention: #000000d9,
+ bg_mask: #00000073,
+ bg_title: #c0d4da,
+
+ // 中性色-阴影
+ shadow1: #0000000a,
+ shadow2: #00000026,
+ shadow3: #0dbb8a,
+
+ // 图标
+ icon: #000,
+ icon-inverse: #fff,
+ icon-hover: #d9d9d9,
+ icon-disabled: #bfbfbf,
+ icon-selected: #0dbb8a,
+ icon-brand: #0dbb8a,
+
+ // Neutral
+ neutral1: #fff,
+ neutral2: #fafafa,
+ neutral3: #f5f5f5,
+ neutral4: #f0f0f0,
+ neutral5: #d9d9d9,
+ neutral6: #bfbfbf,
+ neutral7: #8c8c8c,
+ neutral8: #595959,
+ neutral9: #434343,
+ neutral10: #262626,
+ neutral11: #1f1f1f,
+ neutral12: #141414,
+ neutral13: #000,
+
+ // Conditional
+ divider: #0000000f,
+ header_bg: #fff,
+ item_bg-hover: #29386014,
+ mask: #0000004d,
+ menu_item_bg-selected: #e6f7ff,
+ menu_item_text-selected: #1890ff,
+ side_icon: #798582,
+ sider_bg: #fff,
+ sider_bg2: #fcfcfc,
+ table_column: #fafafa,
+ table_filter_trigger-hover: #0000000a,
+ page_bg: #f0f2f5,
+ popover: #fff,
+ popover_header_bg: #ebebeb
);
diff --git a/src/components/modal/robot-add-modal.vue b/src/components/modal/robot-add-modal.vue
new file mode 100644
index 0000000..6a8a33a
--- /dev/null
+++ b/src/components/modal/robot-add-modal.vue
@@ -0,0 +1,84 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t(RobotType[text]) }}
+
+
+
+
+
diff --git a/src/components/modal/robot-group-rename-modal.vue b/src/components/modal/robot-group-rename-modal.vue
new file mode 100644
index 0000000..f484872
--- /dev/null
+++ b/src/components/modal/robot-group-rename-modal.vue
@@ -0,0 +1,56 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/modal/robot-register-modal.vue b/src/components/modal/robot-register-modal.vue
new file mode 100644
index 0000000..9a11220
--- /dev/null
+++ b/src/components/modal/robot-register-modal.vue
@@ -0,0 +1,140 @@
+
+
+
+
+
+
+
+
+
+
+ {{ $t('是') }}
+ {{ $t('否') }}
+
+
+
+
+ {{ $t(l) }}
+
+
+
+
+ {{ $t(l) }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ label }}
+
+
+
+
+
diff --git a/src/components/robot-group.vue b/src/components/robot-group.vue
deleted file mode 100644
index 5bbeefa..0000000
--- a/src/components/robot-group.vue
+++ /dev/null
@@ -1,69 +0,0 @@
-
-
-
-
-
-
-
-
-
- {{ $t('全选') }}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{ item.label }}
-
-
-
-
-
-
-
-
-
diff --git a/src/components/robot-groups.vue b/src/components/robot-groups.vue
new file mode 100644
index 0000000..e15c3de
--- /dev/null
+++ b/src/components/robot-groups.vue
@@ -0,0 +1,154 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t('全选') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t('添加机器人') }}
+
+
+
+
+
+ {{ $t('注册机器人') }}
+
+
+
+
+
+ {{ $t('修改组名称') }}
+
+
+
+
+
+ {{ $t('删除组') }}
+
+
+
+
+
+
+
+
+
+
+ {{ item.label }}
+
+
+
+
+
+
+
+
+ {{ $t('添加机器人组') }}
+
+
+
+
+
diff --git a/src/pages/scene-editor.vue b/src/pages/scene-editor.vue
index 94d10b6..234cc65 100644
--- a/src/pages/scene-editor.vue
+++ b/src/pages/scene-editor.vue
@@ -1,5 +1,4 @@
@@ -65,7 +65,13 @@ watch(editable, (v) => {
-
+
Content of Tab Pane 2
Content of Tab Pane 3
diff --git a/src/services/editor.service.ts b/src/services/editor.service.ts
index f1e9ee4..e25fd3f 100644
--- a/src/services/editor.service.ts
+++ b/src/services/editor.service.ts
@@ -1,9 +1,11 @@
import { EDITOR_CONFIG, MapAreaType, type MapPen, MapPointType } from '@api/map';
+import type { RobotGroup, RobotInfo } from '@api/robot';
+import type { SceneData } from '@api/scene';
import sTheme from '@core/theme.service';
-import { CanvasLayer, EditType, LockState, Meta2d } from '@meta2d/core';
+import { CanvasLayer, EditType, LockState, Meta2d, s8 } from '@meta2d/core';
import { useObservable } from '@vueuse/rxjs';
-import { cloneDeep, get, pick } from 'lodash-es';
-import { debounceTime, filter, map, Subject, switchMap } from 'rxjs';
+import { clone, cloneDeep, get, isNil, pick, remove, some } from 'lodash-es';
+import { BehaviorSubject, debounceTime, filter, map, Subject, switchMap } from 'rxjs';
import { watch } from 'vue';
export type Point = Record<'x' | 'y', number>;
@@ -44,6 +46,9 @@ export class EditorService extends Meta2d {
),
);
+ public override data(): SceneData {
+ return super.data();
+ }
public override find(target: string): MapPen[] {
return super.find(target);
}
@@ -52,15 +57,73 @@ export class EditorService extends Meta2d {
this.lock(readonly ? LockState.Disable : LockState.None);
}
+ //#region 机器人
+ readonly #robotMap = new Map();
+ public get robots(): RobotInfo[] {
+ return Array.from(this.#robotMap.values());
+ }
+
+ public getRobotById(id: RobotInfo['id']): RobotInfo | undefined {
+ return this.#robotMap.get(id);
+ }
+
+ public addRobots(gid: RobotInfo['gid'], robots: RobotInfo[]): void {
+ const groups = clone(this.#robotGroups$$.value);
+ const group = groups.find((v) => v.id === gid);
+ if (isNil(group)) throw Error('未找到目标机器人组');
+ group.robots ??= [];
+ robots.forEach((v) => {
+ if (this.#robotMap.has(v.id)) return;
+ this.#robotMap.set(v.id, { ...v, gid });
+ group.robots?.push(v.id);
+ });
+ this.#robotGroups$$.next(groups);
+ (this.store.data).robots = [...this.#robotMap.values()];
+ (this.store.data).robotGroups = this.#robotGroups$$.value;
+ }
+
+ readonly #robotGroups$$ = new BehaviorSubject([]);
+ public readonly robotGroups = useObservable(this.#robotGroups$$.pipe(debounceTime(300)));
+
+ public createRobotGroup(): void {
+ const id = s8();
+ const label = `RG-${id}`;
+ const groups = clone(this.#robotGroups$$.value);
+ groups.push({ id, label });
+ this.#robotGroups$$.next(groups);
+ (this.store.data).robotGroups = this.#robotGroups$$.value;
+ }
+ public deleteRobotGroup(id: RobotGroup['id']): void {
+ const groups = clone(this.#robotGroups$$.value);
+ const group = groups.find((v) => v.id === id);
+ group?.robots?.forEach((v) => this.#robotMap.delete(v));
+ remove(groups, group);
+ this.#robotGroups$$.next(groups);
+ (this.store.data).robots = [...this.#robotMap.values()];
+ (this.store.data).robotGroups = this.#robotGroups$$.value;
+ }
+ public updateRobotGroupLabel(id: RobotGroup['id'], label: RobotGroup['label']): void {
+ const groups = this.#robotGroups$$.value;
+ const group = groups.find((v) => v.id === id);
+ if (isNil(group)) throw Error('未找到目标机器人组');
+ if (some(groups, ['label', label])) throw Error('机器人组名称已经存在');
+ group.label = label;
+ this.#robotGroups$$.next([...groups]);
+ (this.store.data).robotGroups = this.#robotGroups$$.value;
+ }
+ //#endregion
+
//#region 点位
public async addPoint(p: Point, type = MapPointType.普通点): Promise {
+ const id = s8();
const pen: MapPen = {
...p,
...this.#mapPoint(type),
...this.#mapPointImage(type),
+ id,
name: 'point',
tags: ['point', `point-${type}`],
- label: 'POINT',
+ label: `P-${id}`,
point: { type },
};
const { x, y, width, height } = this.getPenRect(pen);
@@ -128,6 +191,11 @@ export class EditorService extends Meta2d {
this.setTheme(theme);
}
+ const { robots, robotGroups } = this.data();
+ this.#robotMap.clear();
+ robots?.forEach((r) => this.#robotMap.set(r.id, r));
+ this.#robotGroups$$.next(robotGroups ?? []);
+
this.find('point').forEach((pen) => {
if (!pen.point?.type) return;
if (pen.point.type < 10) return;
diff --git a/src/services/theme.service.ts b/src/services/theme.service.ts
index 557125b..ba98262 100644
--- a/src/services/theme.service.ts
+++ b/src/services/theme.service.ts
@@ -29,10 +29,11 @@ class ThemeService {
public get ant(): AntdTheme {
switch (this.#theme.value) {
case Theme.Dark:
- return THEME_MAP[`antd-${this.#theme.value}`] ?? {};
+ return { algorithm: theme.darkAlgorithm };
case Theme.Light:
- default:
return { algorithm: theme.defaultAlgorithm };
+ default:
+ return {};
}
}
diff --git a/src/style.scss b/src/style.scss
index 7fc8567..95792c8 100644
--- a/src/style.scss
+++ b/src/style.scss
@@ -21,14 +21,10 @@ body {
.full {
width: 100%;
height: 100%;
- overflow: hidden;
+ overflow: visible;
}
-.scroll {
- overflow-y: auto;
-}
-
-@for $i from 0 through 20 {
+@for $i from 0 through 50 {
.size-#{$i * 2} {
font-size: $i * 2px !important;
}