2025-05-06 23:48:21 +08:00
|
|
|
<script setup lang="ts">
|
2025-06-08 22:32:52 +08:00
|
|
|
import { MapAreaType, type MapPen, type MapPointInfo, MapPointType, type Rect } from '@api/map';
|
2025-08-20 10:54:38 +08:00
|
|
|
import type {
|
|
|
|
|
BinLocationGroup,
|
|
|
|
|
BinLocationsList,
|
|
|
|
|
BinTaskItem,
|
|
|
|
|
BinTaskOperation,
|
|
|
|
|
StorageLocationInfo,
|
|
|
|
|
} from '@api/scene';
|
2025-05-06 23:48:21 +08:00
|
|
|
import type { EditorService } from '@core/editor.service';
|
|
|
|
|
import sTheme from '@core/theme.service';
|
2025-06-08 22:32:52 +08:00
|
|
|
import { isNil } from 'lodash-es';
|
2025-05-06 23:48:21 +08:00
|
|
|
import { computed, inject, type InjectionKey, type ShallowRef } from 'vue';
|
|
|
|
|
|
|
|
|
|
type Props = {
|
|
|
|
|
token: InjectionKey<ShallowRef<EditorService>>;
|
|
|
|
|
current?: string;
|
2025-07-18 15:52:26 +08:00
|
|
|
storageLocations?: StorageLocationInfo[]; // 当前动作点绑定的库位信息
|
2025-05-06 23:48:21 +08:00
|
|
|
};
|
|
|
|
|
const props = defineProps<Props>();
|
|
|
|
|
const editor = inject(props.token)!;
|
|
|
|
|
|
|
|
|
|
const pen = computed<MapPen | undefined>(() => editor.value.getPenById(props.current));
|
|
|
|
|
const point = computed<MapPointInfo | null>(() => {
|
2025-05-09 20:15:04 +08:00
|
|
|
const v = pen.value?.point;
|
|
|
|
|
if (!v?.type) return null;
|
|
|
|
|
return v;
|
2025-05-06 23:48:21 +08:00
|
|
|
});
|
2025-06-08 22:32:52 +08:00
|
|
|
const rect = computed<Partial<Rect>>(() => {
|
|
|
|
|
if (isNil(point.value)) return {};
|
|
|
|
|
const { x, y } = editor.value.getPointRect(pen.value!) ?? {};
|
|
|
|
|
return { x, y };
|
|
|
|
|
});
|
2025-05-06 23:48:21 +08:00
|
|
|
|
|
|
|
|
const bindRobot = computed<string>(
|
|
|
|
|
() =>
|
|
|
|
|
point.value?.robots
|
|
|
|
|
?.map((v) => editor.value.getRobotById(v)?.label)
|
|
|
|
|
.filter((v) => !!v)
|
|
|
|
|
.join('、') ?? '',
|
|
|
|
|
);
|
|
|
|
|
const bindAction = computed<string>(
|
|
|
|
|
() =>
|
|
|
|
|
point.value?.actions
|
2025-05-10 00:49:45 +08:00
|
|
|
?.map((v) => editor.value.getPenById(v))
|
|
|
|
|
.filter((v) => v?.point?.type === MapPointType.动作点)
|
|
|
|
|
.map((v) => v?.label)
|
2025-05-06 23:48:21 +08:00
|
|
|
.filter((v) => !!v)
|
|
|
|
|
.join('、') ?? '',
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const mapAreas = (type: MapAreaType): string => {
|
|
|
|
|
const id = pen.value?.id;
|
|
|
|
|
if (!id) return '';
|
2025-05-08 00:42:08 +08:00
|
|
|
return editor.value
|
|
|
|
|
.getBoundAreas(id, 'point', type)
|
|
|
|
|
.map(({ label }) => label)
|
|
|
|
|
.filter((v) => !!v)
|
|
|
|
|
.join('、');
|
2025-05-06 23:48:21 +08:00
|
|
|
};
|
|
|
|
|
const coArea1 = computed<string>(() => mapAreas(MapAreaType.库区));
|
|
|
|
|
const coArea2 = computed<string>(() => mapAreas(MapAreaType.互斥区));
|
2025-07-18 15:52:26 +08:00
|
|
|
|
|
|
|
|
// 库位状态标签样式映射
|
|
|
|
|
const getStorageStatusTag = (location: StorageLocationInfo) => {
|
|
|
|
|
const tags = [];
|
|
|
|
|
|
|
|
|
|
if (location.is_occupied) {
|
|
|
|
|
tags.push({ text: '已占用', color: 'error' });
|
|
|
|
|
} else {
|
2025-07-18 16:18:29 +08:00
|
|
|
tags.push({ text: '未占用', color: 'success' });
|
2025-07-18 15:52:26 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (location.is_locked) {
|
|
|
|
|
tags.push({ text: '已锁定', color: 'warning' });
|
2025-07-18 16:18:29 +08:00
|
|
|
} else {
|
|
|
|
|
tags.push({ text: '未锁定', color: 'success' });
|
2025-07-18 15:52:26 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (location.is_disabled) {
|
|
|
|
|
tags.push({ text: '已禁用', color: 'error' });
|
2025-07-18 16:18:29 +08:00
|
|
|
} else {
|
|
|
|
|
tags.push({ text: '未禁用', color: 'success' });
|
2025-07-18 15:52:26 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (location.is_empty_tray) {
|
2025-07-18 16:18:29 +08:00
|
|
|
tags.push({ text: '空托盘', color: 'success' });
|
|
|
|
|
} else {
|
|
|
|
|
tags.push({ text: '非空托盘', color: 'warning' });
|
2025-07-18 15:52:26 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return tags;
|
|
|
|
|
};
|
2025-08-05 16:42:03 +08:00
|
|
|
|
|
|
|
|
// 解析库位任务数据
|
|
|
|
|
const parseBinTask = (binTaskString: string): BinTaskItem[] => {
|
|
|
|
|
try {
|
|
|
|
|
return JSON.parse(binTaskString.replace(/\n/g, '').trim());
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('解析库位任务数据失败:', error);
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2025-08-20 10:54:38 +08:00
|
|
|
// 获取任务中的所有操作
|
|
|
|
|
const getTaskOperations = (task: BinTaskItem) => {
|
|
|
|
|
if (!task || typeof task !== 'object') return {};
|
|
|
|
|
|
|
|
|
|
const operations: Record<string, BinTaskOperation> = {};
|
|
|
|
|
|
|
|
|
|
// 遍历任务对象的所有属性,查找操作对象
|
|
|
|
|
for (const [key, value] of Object.entries(task)) {
|
|
|
|
|
if (value && typeof value === 'object' && 'operation' in value) {
|
|
|
|
|
operations[key] = value as BinTaskOperation;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return operations;
|
|
|
|
|
};
|
|
|
|
|
|
2025-08-05 16:42:03 +08:00
|
|
|
// 获取当前动作点对应的库位任务数据
|
|
|
|
|
const binTaskData = computed(() => {
|
2025-08-06 09:14:58 +08:00
|
|
|
try {
|
|
|
|
|
const rawData = editor.value.getBinLocationsList();
|
|
|
|
|
|
|
|
|
|
if (!rawData || !point.value) return [];
|
2025-08-05 16:42:03 +08:00
|
|
|
|
2025-08-06 09:14:58 +08:00
|
|
|
const currentPointName = pen.value?.label || pen.value?.id;
|
|
|
|
|
if (!currentPointName) return [];
|
2025-08-05 16:42:03 +08:00
|
|
|
|
2025-08-06 09:14:58 +08:00
|
|
|
// 获取库位组数据
|
|
|
|
|
const binLocationGroups = Array.isArray(rawData)
|
|
|
|
|
? (rawData as BinLocationGroup[])
|
|
|
|
|
: (rawData as BinLocationsList)?.binLocationsList;
|
2025-08-05 16:42:03 +08:00
|
|
|
|
2025-08-06 09:14:58 +08:00
|
|
|
if (!binLocationGroups || !Array.isArray(binLocationGroups)) return [];
|
2025-08-05 16:42:03 +08:00
|
|
|
|
2025-08-06 09:14:58 +08:00
|
|
|
const allBinLocations = binLocationGroups
|
|
|
|
|
.filter((group) => group && Array.isArray(group.binLocationList))
|
|
|
|
|
.flatMap((group) => group.binLocationList)
|
|
|
|
|
.filter((item) => item && typeof item === 'object');
|
2025-08-05 16:42:03 +08:00
|
|
|
|
2025-08-06 09:14:58 +08:00
|
|
|
return allBinLocations
|
|
|
|
|
.filter((item) => item.pointName === currentPointName)
|
|
|
|
|
.map((item) => {
|
|
|
|
|
try {
|
|
|
|
|
if (!item.property || !Array.isArray(item.property)) {
|
|
|
|
|
return {
|
|
|
|
|
instanceName: item.instanceName || '未知库位',
|
|
|
|
|
binTasks: [],
|
|
|
|
|
};
|
|
|
|
|
}
|
2025-08-05 16:42:03 +08:00
|
|
|
|
2025-08-06 09:14:58 +08:00
|
|
|
const binTaskProperty = item.property.find((prop) => prop && prop.key === 'binTask');
|
|
|
|
|
return {
|
|
|
|
|
instanceName: item.instanceName || '未知库位',
|
|
|
|
|
binTasks: binTaskProperty && binTaskProperty.stringValue ? parseBinTask(binTaskProperty.stringValue) : [],
|
|
|
|
|
};
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('处理库位任务数据失败:', error, item);
|
|
|
|
|
return {
|
|
|
|
|
instanceName: item.instanceName || '未知库位',
|
|
|
|
|
binTasks: [],
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
.filter((item) => item.binTasks.length > 0);
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('获取库位任务数据失败:', error);
|
|
|
|
|
return [];
|
|
|
|
|
}
|
2025-08-05 16:42:03 +08:00
|
|
|
});
|
2025-05-06 23:48:21 +08:00
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<template>
|
|
|
|
|
<a-card :bordered="false">
|
|
|
|
|
<template v-if="pen && point">
|
|
|
|
|
<a-row :gutter="[8, 8]">
|
|
|
|
|
<a-col :span="24">
|
|
|
|
|
<a-flex align="center" :gap="8">
|
|
|
|
|
<i class="icon point" />
|
|
|
|
|
<a-typography-text class="card-title" style="flex: auto" :content="pen.label" ellipsis />
|
|
|
|
|
<a-tag :bordered="false">{{ $t(MapPointType[point.type]) }}</a-tag>
|
|
|
|
|
</a-flex>
|
|
|
|
|
</a-col>
|
|
|
|
|
|
|
|
|
|
<a-col :span="24">
|
|
|
|
|
<a-typography-text code>{{ pen.desc || $t('暂无描述') }}</a-typography-text>
|
|
|
|
|
</a-col>
|
|
|
|
|
</a-row>
|
|
|
|
|
|
|
|
|
|
<a-list class="block mt-16">
|
|
|
|
|
<a-list-item>
|
|
|
|
|
<a-typography-text type="secondary">{{ $t('站点坐标') }}</a-typography-text>
|
2025-06-08 22:32:52 +08:00
|
|
|
<a-typography-text>({{ rect.x?.toFixed() }},{{ rect.y?.toFixed() }})</a-typography-text>
|
2025-05-06 23:48:21 +08:00
|
|
|
</a-list-item>
|
2025-07-18 17:51:04 +08:00
|
|
|
<a-list-item v-if="point.type === MapPointType.自动门点 && point.deviceId">
|
|
|
|
|
<a-typography-text type="secondary">{{ $t('设备ID') }}</a-typography-text>
|
|
|
|
|
<a-typography-text>{{ point.deviceId }}</a-typography-text>
|
|
|
|
|
</a-list-item>
|
2025-09-01 18:09:27 +08:00
|
|
|
<a-list-item v-if="point.type === MapPointType.自动门点">
|
|
|
|
|
<a-typography-text type="secondary">{{ $t('连接状态') }}</a-typography-text>
|
|
|
|
|
<a-flex align="center" :gap="8" class="conn-status">
|
|
|
|
|
<span
|
|
|
|
|
class="status-dot"
|
|
|
|
|
:class="point.isConnected ? 'online' : 'offline'"
|
|
|
|
|
:title="point.isConnected ? $t('已连接') : $t('未连接')"
|
|
|
|
|
/>
|
|
|
|
|
<a-typography-text>
|
|
|
|
|
{{ point.isConnected ? $t('已连接') : $t('未连接') }}
|
|
|
|
|
</a-typography-text>
|
|
|
|
|
</a-flex>
|
|
|
|
|
</a-list-item>
|
2025-07-18 16:41:09 +08:00
|
|
|
<a-list-item v-if="point.extensionType">
|
2025-07-15 15:49:09 +08:00
|
|
|
<a-typography-text type="secondary">{{ $t('扩展类型') }}</a-typography-text>
|
|
|
|
|
<a-typography-text>{{ $t(MapPointType[point.extensionType]) }}</a-typography-text>
|
2025-07-18 16:41:09 +08:00
|
|
|
</a-list-item>
|
2025-05-06 23:48:21 +08:00
|
|
|
<a-list-item v-if="[MapPointType.充电点, MapPointType.停靠点].includes(point.type)">
|
|
|
|
|
<a-flex :gap="8" vertical>
|
|
|
|
|
<a-typography-text type="secondary">{{ $t('绑定机器人') }}</a-typography-text>
|
|
|
|
|
<a-typography-text>{{ bindRobot || $t('暂无') }}</a-typography-text>
|
|
|
|
|
</a-flex>
|
|
|
|
|
</a-list-item>
|
2025-07-28 18:05:25 +08:00
|
|
|
<a-list-item v-if="point.type === MapPointType.停靠点">
|
|
|
|
|
<a-typography-text type="secondary">{{ $t('启用状态') }}</a-typography-text>
|
|
|
|
|
<a-typography-text>{{ point.enabled === 1 ? $t('已启用') : $t('已禁用') }}</a-typography-text>
|
|
|
|
|
</a-list-item>
|
2025-05-06 23:48:21 +08:00
|
|
|
<a-list-item v-if="MapPointType.等待点 === point.type">
|
|
|
|
|
<a-flex :gap="8" vertical>
|
|
|
|
|
<a-typography-text type="secondary">{{ $t('绑定动作点') }}</a-typography-text>
|
|
|
|
|
<a-typography-text>{{ bindAction || $t('暂无') }}</a-typography-text>
|
|
|
|
|
</a-flex>
|
|
|
|
|
</a-list-item>
|
|
|
|
|
<a-list-item v-if="MapPointType.动作点 === point.type">
|
|
|
|
|
<a-flex :gap="8" vertical>
|
|
|
|
|
<a-typography-text type="secondary">{{ $t('关联库区') }}</a-typography-text>
|
|
|
|
|
<a-typography-text>{{ coArea1 || $t('暂无') }}</a-typography-text>
|
|
|
|
|
</a-flex>
|
|
|
|
|
</a-list-item>
|
2025-07-14 18:11:29 +08:00
|
|
|
<a-list-item v-if="MapPointType.动作点 === point.type && point.associatedStorageLocations?.length">
|
2025-07-14 16:26:45 +08:00
|
|
|
<a-flex :gap="8" vertical>
|
|
|
|
|
<a-typography-text type="secondary">{{ $t('库位') }}</a-typography-text>
|
2025-07-14 18:11:29 +08:00
|
|
|
<a-typography-text>{{ point.associatedStorageLocations.join('、') }}</a-typography-text>
|
2025-07-14 16:26:45 +08:00
|
|
|
</a-flex>
|
|
|
|
|
</a-list-item>
|
2025-05-06 23:48:21 +08:00
|
|
|
<a-list-item
|
|
|
|
|
v-if="
|
|
|
|
|
[
|
|
|
|
|
MapPointType.普通点,
|
|
|
|
|
MapPointType.电梯点,
|
|
|
|
|
MapPointType.自动门点,
|
|
|
|
|
MapPointType.等待点,
|
|
|
|
|
MapPointType.充电点,
|
|
|
|
|
MapPointType.停靠点,
|
|
|
|
|
MapPointType.动作点,
|
|
|
|
|
MapPointType.临时避让点,
|
|
|
|
|
].includes(point.type)
|
|
|
|
|
"
|
|
|
|
|
>
|
|
|
|
|
<a-flex :gap="8" vertical>
|
|
|
|
|
<a-typography-text type="secondary">{{ $t('关联互斥区') }}</a-typography-text>
|
|
|
|
|
<a-typography-text>{{ coArea2 || $t('暂无') }}</a-typography-text>
|
|
|
|
|
</a-flex>
|
|
|
|
|
</a-list-item>
|
2025-07-18 15:52:26 +08:00
|
|
|
<a-list-item v-if="MapPointType.动作点 === point.type && storageLocations?.length">
|
|
|
|
|
<a-flex :gap="8" vertical>
|
|
|
|
|
<a-typography-text type="secondary">{{ $t('库位状态') }}</a-typography-text>
|
|
|
|
|
<div class="storage-locations">
|
|
|
|
|
<div v-for="location in storageLocations" :key="location.id" class="storage-item">
|
|
|
|
|
<a-typography-text class="storage-name">{{ location.layer_name }}</a-typography-text>
|
|
|
|
|
<div class="storage-tags">
|
|
|
|
|
<div
|
|
|
|
|
v-for="tag in getStorageStatusTag(location)"
|
|
|
|
|
:key="tag.text"
|
|
|
|
|
:class="['storage-tag', `storage-tag-${tag.color}`]"
|
|
|
|
|
>
|
|
|
|
|
{{ tag.text }}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</a-flex>
|
|
|
|
|
</a-list-item>
|
2025-08-05 16:42:03 +08:00
|
|
|
<a-list-item v-if="MapPointType.动作点 === point.type && binTaskData.length">
|
|
|
|
|
<a-flex :gap="8" vertical>
|
|
|
|
|
<a-typography-text type="secondary">{{ $t('库位任务配置') }}</a-typography-text>
|
|
|
|
|
<div class="bin-task-container">
|
|
|
|
|
<div v-for="binLocation in binTaskData" :key="binLocation.instanceName" class="bin-task-group">
|
|
|
|
|
<a-typography-text class="bin-location-name">{{ binLocation.instanceName }}</a-typography-text>
|
|
|
|
|
<div class="bin-tasks">
|
|
|
|
|
<div v-for="(task, taskIndex) in binLocation.binTasks" :key="taskIndex" class="bin-task-item">
|
2025-08-20 10:54:38 +08:00
|
|
|
<div
|
|
|
|
|
v-for="(operation, operationType) in getTaskOperations(task)"
|
|
|
|
|
:key="operationType"
|
|
|
|
|
class="task-operation"
|
|
|
|
|
>
|
|
|
|
|
<a-typography-text class="operation-title">
|
2025-08-20 14:10:41 +08:00
|
|
|
{{ operationType }}
|
2025-08-20 10:54:38 +08:00
|
|
|
</a-typography-text>
|
2025-08-07 15:32:20 +08:00
|
|
|
<div class="operation-summary">
|
2025-08-20 10:54:38 +08:00
|
|
|
<span class="operation-type">{{ operation.operation || '未知操作' }}</span>
|
2025-08-07 15:32:20 +08:00
|
|
|
<div class="operation-tooltip">
|
|
|
|
|
<i class="icon detail" />
|
|
|
|
|
<div class="tooltip-content">
|
2025-08-20 10:54:38 +08:00
|
|
|
<div v-for="(value, key) in operation" :key="key" class="detail-item">
|
2025-08-07 15:32:20 +08:00
|
|
|
<span class="detail-key">{{ key }}:</span>
|
|
|
|
|
<span class="detail-value">
|
|
|
|
|
{{ typeof value === 'boolean' ? (value ? '是' : '否') : (value ?? '未知') }}
|
|
|
|
|
</span>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
2025-08-05 16:42:03 +08:00
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</a-flex>
|
|
|
|
|
</a-list-item>
|
2025-05-06 23:48:21 +08:00
|
|
|
</a-list>
|
|
|
|
|
</template>
|
|
|
|
|
<a-empty v-else :image="sTheme.empty" />
|
|
|
|
|
</a-card>
|
|
|
|
|
</template>
|
2025-07-18 15:52:26 +08:00
|
|
|
|
|
|
|
|
<style scoped lang="scss">
|
|
|
|
|
@use '/src/assets/themes/theme' as *;
|
|
|
|
|
|
|
|
|
|
@include themed {
|
2025-09-01 18:09:27 +08:00
|
|
|
.conn-status {
|
|
|
|
|
.status-dot {
|
|
|
|
|
width: 8px;
|
|
|
|
|
height: 8px;
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
display: inline-block;
|
|
|
|
|
box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.05);
|
|
|
|
|
&.online {
|
|
|
|
|
background-color: get-color(success);
|
|
|
|
|
box-shadow: 0 0 0 2px rgba(82, 196, 26, 0.15);
|
|
|
|
|
}
|
|
|
|
|
&.offline {
|
|
|
|
|
background-color: get-color(error);
|
|
|
|
|
box-shadow: 0 0 0 2px rgba(255, 77, 79, 0.15);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-18 15:52:26 +08:00
|
|
|
.storage-locations {
|
|
|
|
|
.storage-item {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
margin-bottom: 8px;
|
2025-07-18 16:18:29 +08:00
|
|
|
padding: 16px;
|
2025-07-18 15:52:26 +08:00
|
|
|
border-radius: 4px;
|
|
|
|
|
|
|
|
|
|
&:last-child {
|
|
|
|
|
margin-bottom: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.storage-name {
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
margin-right: 8px;
|
|
|
|
|
color: get-color(text1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.storage-tags {
|
|
|
|
|
flex: 1;
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
justify-content: flex-end;
|
|
|
|
|
gap: 4px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.storage-tag {
|
|
|
|
|
padding: 2px 6px;
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
border: 1px solid;
|
|
|
|
|
|
|
|
|
|
&-error {
|
|
|
|
|
color: get-color(error_text);
|
|
|
|
|
background-color: get-color(error_bg);
|
|
|
|
|
border-color: get-color(error_border);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
&-success {
|
|
|
|
|
color: get-color(success_text);
|
|
|
|
|
background-color: get-color(success_bg);
|
|
|
|
|
border-color: get-color(success_border);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
&-warning {
|
|
|
|
|
color: get-color(warning_text);
|
|
|
|
|
background-color: get-color(warning_bg);
|
|
|
|
|
border-color: get-color(warning_border);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
&-processing {
|
|
|
|
|
color: get-color(primary_text);
|
|
|
|
|
background-color: get-color(primary_bg);
|
|
|
|
|
border-color: get-color(primary_border);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
&-default {
|
|
|
|
|
color: get-color(text2);
|
|
|
|
|
background-color: get-color(fill2);
|
|
|
|
|
border-color: get-color(border1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-08-05 16:42:03 +08:00
|
|
|
|
|
|
|
|
.bin-task-container {
|
|
|
|
|
.bin-task-group {
|
|
|
|
|
margin-bottom: 16px;
|
|
|
|
|
padding: 12px;
|
|
|
|
|
border-radius: 4px;
|
2025-08-05 16:55:34 +08:00
|
|
|
background-color: get-color(fill4);
|
2025-08-05 16:42:03 +08:00
|
|
|
|
|
|
|
|
&:last-child {
|
|
|
|
|
margin-bottom: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.bin-location-name {
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
color: get-color(text1);
|
|
|
|
|
margin-bottom: 8px;
|
|
|
|
|
display: block;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.bin-tasks {
|
|
|
|
|
.bin-task-item {
|
|
|
|
|
margin-bottom: 12px;
|
|
|
|
|
|
|
|
|
|
&:last-child {
|
|
|
|
|
margin-bottom: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.task-operation {
|
|
|
|
|
margin-bottom: 8px;
|
|
|
|
|
padding: 8px;
|
|
|
|
|
border-radius: 4px;
|
2025-08-05 16:55:34 +08:00
|
|
|
background-color: get-color(fill4);
|
2025-08-05 16:42:03 +08:00
|
|
|
|
|
|
|
|
&:last-child {
|
|
|
|
|
margin-bottom: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.operation-title {
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
color: get-color(primary);
|
|
|
|
|
margin-bottom: 6px;
|
|
|
|
|
display: block;
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-07 15:32:20 +08:00
|
|
|
.operation-summary {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 8px;
|
|
|
|
|
cursor: pointer;
|
2025-08-05 16:42:03 +08:00
|
|
|
|
2025-08-07 15:32:20 +08:00
|
|
|
.operation-type {
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
color: get-color(primary);
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.operation-tooltip {
|
|
|
|
|
position: relative;
|
|
|
|
|
display: inline-block;
|
|
|
|
|
|
|
|
|
|
.icon.detail {
|
|
|
|
|
width: 14px;
|
|
|
|
|
height: 14px;
|
|
|
|
|
background-image: url('/src/assets/icons/dark/detail.png');
|
|
|
|
|
background-size: contain;
|
|
|
|
|
background-repeat: no-repeat;
|
|
|
|
|
background-position: center;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
opacity: 0.6;
|
|
|
|
|
transition: opacity 0.2s ease;
|
|
|
|
|
|
|
|
|
|
&:hover {
|
|
|
|
|
opacity: 1;
|
|
|
|
|
}
|
2025-08-05 16:42:03 +08:00
|
|
|
}
|
|
|
|
|
|
2025-08-07 15:32:20 +08:00
|
|
|
@media (prefers-color-scheme: light) {
|
|
|
|
|
.icon.detail {
|
|
|
|
|
background-image: url('/src/assets/icons/light/detail.png');
|
|
|
|
|
}
|
2025-08-05 16:42:03 +08:00
|
|
|
}
|
|
|
|
|
|
2025-08-07 15:32:20 +08:00
|
|
|
.tooltip-content {
|
|
|
|
|
position: absolute;
|
|
|
|
|
bottom: 100%;
|
|
|
|
|
left: 50%;
|
|
|
|
|
transform: translateX(-50%);
|
|
|
|
|
background-color: get-color(fill1);
|
2025-08-05 16:42:03 +08:00
|
|
|
color: get-color(text1);
|
2025-08-07 15:32:20 +08:00
|
|
|
padding: 8px;
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
|
|
|
|
z-index: 1000;
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
line-height: 1.5;
|
|
|
|
|
max-width: 200px;
|
2025-08-05 16:42:03 +08:00
|
|
|
word-break: break-all;
|
2025-08-07 15:32:20 +08:00
|
|
|
border: 1px solid get-color(border1);
|
|
|
|
|
opacity: 0;
|
|
|
|
|
visibility: hidden;
|
|
|
|
|
transition:
|
|
|
|
|
opacity 0.2s ease,
|
|
|
|
|
visibility 0.2s ease;
|
|
|
|
|
backdrop-filter: blur(8px);
|
|
|
|
|
-webkit-backdrop-filter: blur(8px);
|
|
|
|
|
background: get-color(fill1) !important;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
&:hover .tooltip-content {
|
|
|
|
|
opacity: 1;
|
|
|
|
|
visibility: visible;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.detail-item {
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
margin-bottom: 4px;
|
|
|
|
|
|
|
|
|
|
&:last-child {
|
|
|
|
|
margin-bottom: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.detail-key {
|
|
|
|
|
color: get-color(text2);
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
margin-right: 8px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.detail-value {
|
|
|
|
|
color: get-color(text1);
|
|
|
|
|
text-align: right;
|
|
|
|
|
word-break: break-all;
|
|
|
|
|
}
|
2025-08-05 16:42:03 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-07-18 15:52:26 +08:00
|
|
|
}
|
|
|
|
|
</style>
|