This commit is contained in:
chndfang 2025-05-03 00:28:22 +08:00
parent 0c3b2b405b
commit 07f7a378c0
10 changed files with 108 additions and 52 deletions

View File

@ -3,46 +3,44 @@
"success": true, "success": true,
"data": [ "data": [
{ {
"gid": "mock-robot-group-1",
"id": "mock-robot-1", "id": "mock-robot-1",
"label": "模拟机器人A", "label": "模拟机器人A",
"brand": "模拟品牌A", "brand": "模拟品牌A",
"type": 1, "type": 1
"ip": "127.0.1.1"
}, },
{ {
"gid": "mock-robot-group-1",
"id": "mock-robot-2", "id": "mock-robot-2",
"label": "模拟机器人B", "label": "模拟机器人B",
"brand": "模拟品牌A", "brand": "模拟品牌A",
"type": 2, "type": 2
"ip": "127.0.1.2"
}, },
{ {
"gid": "mock-robot-group-1",
"id": "mock-robot-3", "id": "mock-robot-3",
"label": "模拟机器人C", "label": "模拟机器人C",
"brand": "模拟品牌A", "brand": "模拟品牌A",
"type": 3, "type": 3
"ip": "127.0.1.3"
}, },
{ {
"gid": "mock-robot-group-2",
"id": "mock-robot-4", "id": "mock-robot-4",
"label": "模拟机器人D", "label": "模拟机器人D",
"brand": "模拟品牌B", "brand": "模拟品牌B",
"type": 1, "type": 1
"ip": "127.0.2.1"
}, },
{ {
"id": "mock-robot-5", "id": "mock-robot-5",
"label": "模拟机器人E", "label": "模拟机器人E",
"brand": "模拟品牌B", "brand": "模拟品牌B",
"type": 2, "type": 2
"ip": "127.0.2.2"
}, },
{ {
"id": "mock-robot-6", "id": "mock-robot-6",
"label": "模拟机器人F", "label": "模拟机器人F",
"brand": "模拟品牌B", "brand": "模拟品牌B",
"type": 3, "type": 3
"ip": "127.0.2.3"
} }
], ],
"message": "模拟提示" "message": "模拟提示"

View File

@ -10,25 +10,25 @@
"label": "模拟机器人组A", "label": "模拟机器人组A",
"robots": [ "robots": [
{ {
"gid": "mock-robot-group-1",
"id": "mock-robot-1", "id": "mock-robot-1",
"label": "模拟机器人A", "label": "模拟机器人A",
"brand": "模拟品牌A", "brand": "模拟品牌A",
"type": 1, "type": 1
"ip": "127.0.1.1"
}, },
{ {
"gid": "mock-robot-group-1",
"id": "mock-robot-2", "id": "mock-robot-2",
"label": "模拟机器人B", "label": "模拟机器人B",
"brand": "模拟品牌A", "brand": "模拟品牌A",
"type": 2, "type": 2
"ip": "127.0.1.2"
}, },
{ {
"gid": "mock-robot-group-1",
"id": "mock-robot-3", "id": "mock-robot-3",
"label": "模拟机器人C", "label": "模拟机器人C",
"brand": "模拟品牌A", "brand": "模拟品牌A",
"type": 3, "type": 3
"ip": "127.0.1.3"
} }
] ]
}, },
@ -37,25 +37,11 @@
"label": "模拟机器人组B", "label": "模拟机器人组B",
"robots": [ "robots": [
{ {
"gid": "mock-robot-group-2",
"id": "mock-robot-4", "id": "mock-robot-4",
"label": "模拟机器人D", "label": "模拟机器人D",
"brand": "模拟品牌B", "brand": "模拟品牌B",
"type": 1, "type": 1
"ip": "127.0.2.1"
},
{
"id": "mock-robot-5",
"label": "模拟机器人E",
"brand": "模拟品牌B",
"type": 2,
"ip": "127.0.2.2"
},
{
"id": "mock-robot-6",
"label": "模拟机器人F",
"brand": "模拟品牌B",
"type": 3,
"ip": "127.0.2.3"
} }
] ]
} }

View File

@ -35,6 +35,43 @@
border-color: get-color(warning); border-color: get-color(warning);
box-shadow: 0 2px 0 0 get-color(0000000a); box-shadow: 0 2px 0 0 get-color(0000000a);
} }
&.icon-btn {
background-color: transparent;
border: none;
box-shadow: none;
&.ant-btn-sm {
padding: 0;
}
}
}
.ant-checkbox {
&::after {
display: none;
}
& > .ant-checkbox-inner {
background-color: get-color(141414);
border-color: get-color(434343);
border-radius: 2px;
}
&.ant-checkbox-checked:not(.ant-checkbox-disabled) > .ant-checkbox-inner {
background-color: get-color(primary);
border-color: get-color(primary);
&::after {
border-color: get-color(141414);
}
}
}
.ant-checkbox-wrapper {
align-items: center;
font: 400 14px/22px Roboto;
color: get-color(ffffffa6);
} }
.ant-collapse.ant-collapse-ghost { .ant-collapse.ant-collapse-ghost {

View File

@ -2,13 +2,14 @@
@use 'asset/icons/icon' as *; @use 'asset/icons/icon' as *;
@include themed { @include themed {
i {
&.bg-1 {
background-color: get-color(ffffffd9);
}
}
.icon { .icon {
display: inline-block; @extend %icon;
width: 1em;
height: 1em;
font-size: 18px;
line-height: 1;
vertical-align: top;
@each $icon in $icons { @each $icon in $icons {
&.#{$icon} { &.#{$icon} {
@ -16,4 +17,23 @@
} }
} }
} }
.mask {
@extend %icon;
@each $icon in $icons {
&.#{$icon} {
mask: get-icon($icon);
}
}
}
}
%icon {
display: inline-block;
width: 1em;
height: 1em;
font-size: 24px;
line-height: 1;
vertical-align: top;
} }

View File

@ -7,15 +7,15 @@ export interface RobotGroup {
} }
export interface RobotInfo { export interface RobotInfo {
gid?: string; // 机器人组id
id: string; // 机器人id id: string; // 机器人id
label: string; // 机器人名称 label: string; // 机器人名称
brand: RobotBrand; // 机器人品牌 brand: RobotBrand; // 机器人品牌
type: RobotType; // 机器人类型 type: RobotType; // 机器人类型
ip: string; // 机器人ip
} }
export interface RobotDetail extends RobotInfo { export interface RobotDetail extends RobotInfo {
group?: string; // 机器人组id ip: string; // 机器人ip
isSimulative?: boolean; // 是否仿真机器人 isSimulative?: boolean; // 是否仿真机器人
battery?: number; // 机器人电量 battery?: number; // 机器人电量
minBattery?: number; // 最小电量 minBattery?: number; // 最小电量

View File

@ -1 +1 @@
$icons: (dropdown, edit, exit); $icons: (control, dropdown, edit, exit, trash_fill);

Binary file not shown.

After

Width:  |  Height:  |  Size: 971 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 767 B

View File

@ -1,7 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
import type { RobotGroup, RobotInfo } from '@api/robot'; import type { RobotGroup, RobotInfo } from '@api/robot';
import type { EditorService } from '@core/editor.service'; import type { EditorService } from '@core/editor.service';
import { chain } from 'lodash-es'; import { chain, groupBy, map } from 'lodash-es';
import { computed, inject, type InjectionKey, ref, type ShallowRef } from 'vue'; import { computed, inject, type InjectionKey, ref, type ShallowRef } from 'vue';
type Props = { type Props = {
@ -15,11 +15,21 @@ const editor = inject(props.editor)!;
const keyword = ref<string>(''); const keyword = ref<string>('');
//#region //#region
// const groups = computed<RobotGroup[]>(() => chain(props.groups).filter().value()) const robotList = computed<RobotInfo[]>(() =>
const robots = computed<RobotInfo[]>(() => chain(props.groups).map('robots').flatten().value()); chain(props.groups)
.map('robots')
.flatten()
.filter(({ label }) => label.includes(keyword.value))
.value(),
);
const robotGroupMap = computed<Record<RobotGroup['id'], RobotInfo[]>>(() => groupBy(robotList.value, 'gid'));
const selected = ref<RobotInfo['id'][]>([]); const selected = ref<RobotInfo['id'][]>([]);
const isAllSelected = computed<boolean>(() => robots.value.every(({ id }) => selected.value.includes(id))); const isAllSelected = computed<boolean>(() => robotList.value.every(({ id }) => selected.value.includes(id)));
const selectAll = (checked: boolean) => {
selected.value = checked ? map(robotList.value, 'id') : [];
};
//#endregion //#endregion
</script> </script>
@ -30,15 +40,20 @@ const isAllSelected = computed<boolean>(() => robots.value.every(({ id }) => sel
</a-input> </a-input>
<a-flex v-if="editable" class="mb-8" style="height: 32px" flex="none" justify="space-between" align="center"> <a-flex v-if="editable" class="mb-8" style="height: 32px" flex="none" justify="space-between" align="center">
<a-checkbox :checked="isAllSelected">{{ $t('全选') }}</a-checkbox> <a-checkbox :checked="isAllSelected" @change="selectAll($event.target.checked)">{{ $t('全选') }}</a-checkbox>
<a-space>
<a-button class="icon-btn" size="small" :disabled="!selected.length">
<i class="mask control bg-1" />
</a-button>
</a-space>
</a-flex> </a-flex>
<a-collapse class="scroll" expand-icon-position="end" ghost> <a-collapse class="scroll" expand-icon-position="end" ghost>
<template #expandIcon="v"> <template #expandIcon="v">
<i class="icon dropdown" :class="{ active: v?.isActive }" /> <i class="icon dropdown" :class="{ active: v?.isActive }" />
</template> </template>
<a-collapse-panel v-for="{ id, label, robots } in groups" :key="id" :header="label"> <a-collapse-panel v-for="{ id, label } in groups" :key="id" :header="label">
<a-list :data-source="robots"> <a-list :data-source="robotGroupMap[id]">
<template #renderItem="{ item }"> <template #renderItem="{ item }">
<a-list-item class="ph-16" style="height: 36px"> <a-list-item class="ph-16" style="height: 36px">
<template #actions> </template> <template #actions> </template>

View File

@ -47,11 +47,11 @@ watch(editable, (v) => {
<a-typography-text strong>{{ title }}</a-typography-text> <a-typography-text strong>{{ title }}</a-typography-text>
<a-space align="center"> <a-space align="center">
<a-button v-if="editable" class="warning" @click="editable = false"> <a-button v-if="editable" class="warning" @click="editable = false">
<i class="icon exit mr-8" /> <i class="icon exit size-18 mr-8" />
<span>{{ $t('退出编辑器') }}</span> <span>{{ $t('退出编辑器') }}</span>
</a-button> </a-button>
<a-button v-else type="primary" @click="editable = true"> <a-button v-else type="primary" @click="editable = true">
<i class="icon edit mr-8" /> <i class="icon edit size-18 mr-8" />
<span>{{ $t('启用编辑器') }}</span> <span>{{ $t('启用编辑器') }}</span>
</a-button> </a-button>
<a-button>{{ $t('推送') }}</a-button> <a-button>{{ $t('推送') }}</a-button>