feat: 重构颜色配置相关组件,新增颜色配置面板和带透明度的颜色选择器,优化颜色管理逻辑
This commit is contained in:
parent
6599216e38
commit
b4ac71a717
@ -1,7 +1,7 @@
|
||||
import type { RobotGroup, RobotInfo } from '@api/robot';
|
||||
import type { Meta2dData } from '@meta2d/core';
|
||||
|
||||
import type { EditorColorConfig } from '../../services/color-config.service';
|
||||
import type { EditorColorConfig } from '../../services/color/color-config.service';
|
||||
|
||||
export interface SceneInfo {
|
||||
id: string; // 场景id
|
||||
|
108
src/components/color/README.md
Normal file
108
src/components/color/README.md
Normal file
@ -0,0 +1,108 @@
|
||||
# 颜色配置组件
|
||||
|
||||
## 概述
|
||||
|
||||
这个文件夹包含了所有与颜色配置相关的组件和服务,包括颜色配置面板、颜色选择器和颜色配置服务。
|
||||
|
||||
## 文件结构
|
||||
|
||||
```
|
||||
src/components/color/
|
||||
├── color-config-panel.vue # 颜色配置面板组件
|
||||
├── color-picker-with-alpha.vue # 带透明度的颜色选择器
|
||||
└── README.md # 说明文档
|
||||
|
||||
src/services/color/
|
||||
└── color-config.service.ts # 颜色配置服务
|
||||
```
|
||||
|
||||
## 新增功能:区域边框颜色配置
|
||||
|
||||
### 功能特性
|
||||
|
||||
1. **全局边框设置**:
|
||||
- 默认边框宽度(1-10px)
|
||||
- 默认透明度(15%)
|
||||
- 边框样式固定为实线
|
||||
|
||||
2. **区域类型边框颜色**:
|
||||
- 库区(类型1)
|
||||
- 互斥区(类型11)
|
||||
- 非互斥区(类型12)
|
||||
- 约束区(类型13)
|
||||
- 描述区(类型14)
|
||||
|
||||
### 使用方法
|
||||
|
||||
#### 1. 在颜色配置面板中设置
|
||||
|
||||
打开颜色配置面板,在"区域颜色"部分找到"边框配置":
|
||||
|
||||
- 调整全局边框设置(宽度、透明度)
|
||||
- 为每种区域类型设置独立的边框颜色
|
||||
|
||||
#### 2. 通过代码设置
|
||||
|
||||
```typescript
|
||||
import colorConfig from '@/services/color/color-config.service';
|
||||
|
||||
// 设置区域边框颜色
|
||||
colorConfig.setAreaBorderColor(1, '#FF0000'); // 库区红色边框
|
||||
|
||||
// 设置边框宽度
|
||||
colorConfig.setAreaBorderWidth(1, 3); // 3px宽度
|
||||
|
||||
// 设置边框透明度
|
||||
colorConfig.setAreaBorderOpacity(1, 0.15); // 15%透明度
|
||||
|
||||
// 批量设置
|
||||
colorConfig.setAreaBorderConfig(1, {
|
||||
color: '#FF0000',
|
||||
width: 3,
|
||||
opacity: 0.15,
|
||||
});
|
||||
```
|
||||
|
||||
#### 3. 获取当前配置
|
||||
|
||||
```typescript
|
||||
// 获取边框颜色
|
||||
const borderColor = colorConfig.getAreaBorderColor(1);
|
||||
|
||||
// 获取边框宽度
|
||||
const borderWidth = colorConfig.getAreaBorderWidth(1);
|
||||
|
||||
// 获取边框透明度
|
||||
const borderOpacity = colorConfig.getAreaBorderOpacity(1);
|
||||
```
|
||||
|
||||
### 测试功能
|
||||
|
||||
在颜色配置面板中点击"测试边框颜色"按钮,系统会:
|
||||
|
||||
1. 在控制台输出当前所有区域类型的边框配置
|
||||
2. 自动设置一些测试颜色(库区红色、互斥区绿色、非互斥区蓝色)
|
||||
3. 触发编辑器重新渲染以显示新的边框颜色
|
||||
|
||||
### 技术实现
|
||||
|
||||
- **实时渲染**:配置更改后立即触发编辑器重新渲染
|
||||
- **本地存储**:所有配置自动保存到localStorage
|
||||
- **类型安全**:完整的TypeScript类型定义
|
||||
- **向后兼容**:保持现有API不变
|
||||
|
||||
### 注意事项
|
||||
|
||||
1. 边框颜色优先级:`area.border.colors[type]` > `area.types[type].borderColor` > 默认值
|
||||
2. 边框样式固定为实线
|
||||
3. 透明度范围:0-1(0%到100%)
|
||||
4. 边框宽度范围:1-10像素
|
||||
|
||||
### 故障排除
|
||||
|
||||
如果边框颜色不生效,请检查:
|
||||
|
||||
1. 确保区域类型正确(1, 11, 12, 13, 14)
|
||||
2. 检查控制台是否有调试信息输出
|
||||
3. 确认颜色值格式正确(如:#FF0000)
|
||||
4. 使用"测试边框颜色"功能验证配置是否正确
|
@ -47,40 +47,119 @@
|
||||
<!-- 区域颜色配置 -->
|
||||
<div class="color-section">
|
||||
<h4>区域颜色</h4>
|
||||
<div class="color-group">
|
||||
<label>库区填充:</label>
|
||||
<ColorPickerWithAlpha
|
||||
:modelValue="config.area.fill[1]"
|
||||
@update:modelValue="(value) => updateColor('area.fill.1', { target: { value } })"
|
||||
/>
|
||||
|
||||
<!-- 区域填充颜色 -->
|
||||
<div class="color-subsection">
|
||||
<h5>填充颜色</h5>
|
||||
<div class="color-group">
|
||||
<label>库区填充:</label>
|
||||
<ColorPickerWithAlpha
|
||||
:modelValue="config.area.fill[1]"
|
||||
@update:modelValue="(value) => updateColor('area.fill.1', { target: { value } })"
|
||||
/>
|
||||
</div>
|
||||
<div class="color-group">
|
||||
<label>互斥区填充:</label>
|
||||
<ColorPickerWithAlpha
|
||||
:modelValue="config.area.fill[11]"
|
||||
@update:modelValue="(value) => updateColor('area.fill.11', { target: { value } })"
|
||||
/>
|
||||
</div>
|
||||
<div class="color-group">
|
||||
<label>非互斥区填充:</label>
|
||||
<ColorPickerWithAlpha
|
||||
:modelValue="config.area.fill[12]"
|
||||
@update:modelValue="(value) => updateColor('area.fill.12', { target: { value } })"
|
||||
/>
|
||||
</div>
|
||||
<div class="color-group">
|
||||
<label>约束区填充:</label>
|
||||
<ColorPickerWithAlpha
|
||||
:modelValue="config.area.fill[13]"
|
||||
@update:modelValue="(value) => updateColor('area.fill.13', { target: { value } })"
|
||||
/>
|
||||
</div>
|
||||
<div class="color-group">
|
||||
<label>描述区填充:</label>
|
||||
<ColorPickerWithAlpha
|
||||
:modelValue="config.area.fill[14]"
|
||||
@update:modelValue="(value) => updateColor('area.fill.14', { target: { value } })"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="color-group">
|
||||
<label>互斥区填充:</label>
|
||||
<ColorPickerWithAlpha
|
||||
:modelValue="config.area.fill[11]"
|
||||
@update:modelValue="(value) => updateColor('area.fill.11', { target: { value } })"
|
||||
/>
|
||||
</div>
|
||||
<div class="color-group">
|
||||
<label>非互斥区填充:</label>
|
||||
<ColorPickerWithAlpha
|
||||
:modelValue="config.area.fill[12]"
|
||||
@update:modelValue="(value) => updateColor('area.fill.12', { target: { value } })"
|
||||
/>
|
||||
</div>
|
||||
<div class="color-group">
|
||||
<label>约束区填充:</label>
|
||||
<ColorPickerWithAlpha
|
||||
:modelValue="config.area.fill[13]"
|
||||
@update:modelValue="(value) => updateColor('area.fill.13', { target: { value } })"
|
||||
/>
|
||||
</div>
|
||||
<div class="color-group">
|
||||
<label>描述区填充:</label>
|
||||
<ColorPickerWithAlpha
|
||||
:modelValue="config.area.fill[14]"
|
||||
@update:modelValue="(value) => updateColor('area.fill.14', { target: { value } })"
|
||||
/>
|
||||
|
||||
<!-- 区域边框配置 -->
|
||||
<div class="color-subsection">
|
||||
<h5>边框配置</h5>
|
||||
|
||||
<!-- 全局边框设置 -->
|
||||
<div class="border-global-settings">
|
||||
<div class="color-group">
|
||||
<label>默认边框宽度:</label>
|
||||
<input
|
||||
type="number"
|
||||
:value="config.area.border.width"
|
||||
@input="(e) => updateBorderProperty('width', e)"
|
||||
min="1"
|
||||
max="10"
|
||||
step="1"
|
||||
class="size-input"
|
||||
/>
|
||||
<span class="unit">px</span>
|
||||
</div>
|
||||
<div class="color-group">
|
||||
<label>默认透明度:</label>
|
||||
<input
|
||||
type="range"
|
||||
:value="config.area.border.opacity * 100"
|
||||
@input="(e) => updateBorderOpacity(e)"
|
||||
min="0"
|
||||
max="100"
|
||||
step="5"
|
||||
class="opacity-slider"
|
||||
/>
|
||||
<span class="opacity-value">{{ Math.round(config.area.border.opacity * 100) }}%</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 各类型区域边框颜色 -->
|
||||
<div class="border-colors">
|
||||
<div class="color-group">
|
||||
<label>库区边框:</label>
|
||||
<ColorPickerWithAlpha
|
||||
:modelValue="config.area.border.colors[1]"
|
||||
@update:modelValue="(value) => updateAreaBorderColor(1, value)"
|
||||
/>
|
||||
</div>
|
||||
<div class="color-group">
|
||||
<label>互斥区边框:</label>
|
||||
<ColorPickerWithAlpha
|
||||
:modelValue="config.area.border.colors[11]"
|
||||
@update:modelValue="(value) => updateAreaBorderColor(11, value)"
|
||||
/>
|
||||
</div>
|
||||
<div class="color-group">
|
||||
<label>非互斥区边框:</label>
|
||||
<ColorPickerWithAlpha
|
||||
:modelValue="config.area.border.colors[12]"
|
||||
@update:modelValue="(value) => updateAreaBorderColor(12, value)"
|
||||
/>
|
||||
</div>
|
||||
<div class="color-group">
|
||||
<label>约束区边框:</label>
|
||||
<ColorPickerWithAlpha
|
||||
:modelValue="config.area.border.colors[13]"
|
||||
@update:modelValue="(value) => updateAreaBorderColor(13, value)"
|
||||
/>
|
||||
</div>
|
||||
<div class="color-group">
|
||||
<label>描述区边框:</label>
|
||||
<ColorPickerWithAlpha
|
||||
:modelValue="config.area.border.colors[14]"
|
||||
@update:modelValue="(value) => updateAreaBorderColor(14, value)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -218,9 +297,9 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, defineAsyncComponent, ref, watch } from 'vue';
|
||||
import { computed, defineAsyncComponent, nextTick, ref, watch } from 'vue';
|
||||
|
||||
import colorConfig from '../services/color-config.service';
|
||||
import colorConfig from '../../services/color/color-config.service';
|
||||
// 动态导入颜色选择器组件
|
||||
const ColorPickerWithAlpha = defineAsyncComponent(() => import('./color-picker-with-alpha.vue'));
|
||||
|
||||
@ -279,11 +358,54 @@ const resetRobotSize = () => {
|
||||
tempRobotSize.value.imageHeight = config.value.robot.imageHeight;
|
||||
};
|
||||
|
||||
const resetToDefault = () => {
|
||||
// 更新边框属性
|
||||
const updateBorderProperty = (property: 'width', event: Event) => {
|
||||
const target = event.target as HTMLInputElement;
|
||||
if (target) {
|
||||
const value = Number(target.value);
|
||||
// 更新全局边框宽度
|
||||
colorConfig.setColor(`area.border.${property}`, value.toString());
|
||||
|
||||
// 同时更新所有区域类型的边框宽度
|
||||
const areaTypes = [1, 11, 12, 13, 14];
|
||||
areaTypes.forEach(type => {
|
||||
colorConfig.setAreaBorderWidth(type, value);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// 更新边框透明度
|
||||
const updateBorderOpacity = (event: Event) => {
|
||||
const target = event.target as HTMLInputElement;
|
||||
if (target) {
|
||||
const opacity = Number(target.value) / 100;
|
||||
// 更新全局边框透明度
|
||||
colorConfig.setColor('area.border.opacity', opacity.toString());
|
||||
|
||||
// 同时更新所有区域类型的边框透明度
|
||||
const areaTypes = [1, 11, 12, 13, 14];
|
||||
areaTypes.forEach(type => {
|
||||
colorConfig.setAreaBorderOpacity(type, opacity);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// 更新区域边框颜色
|
||||
const updateAreaBorderColor = (areaType: number, color: string) => {
|
||||
colorConfig.setAreaBorderColor(areaType, color);
|
||||
};
|
||||
|
||||
|
||||
const resetToDefault = async () => {
|
||||
colorConfig.resetToDefault();
|
||||
|
||||
// 等待下一个tick确保配置已更新
|
||||
await nextTick();
|
||||
|
||||
// 重置临时状态
|
||||
tempRobotSize.value.imageWidth = config.value.robot.imageWidth;
|
||||
tempRobotSize.value.imageHeight = config.value.robot.imageHeight;
|
||||
|
||||
};
|
||||
</script>
|
||||
|
||||
@ -417,6 +539,7 @@ const resetToDefault = () => {
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
|
||||
.btn-reset {
|
||||
color: #ff4d4f;
|
||||
border-color: #ff4d4f;
|
||||
@ -485,6 +608,60 @@ const resetToDefault = () => {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
/* 边框配置样式 */
|
||||
.border-global-settings {
|
||||
margin-bottom: 16px;
|
||||
padding: 12px;
|
||||
background: #f8f9fa;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #e9ecef;
|
||||
}
|
||||
|
||||
.border-colors {
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
|
||||
.opacity-slider {
|
||||
width: 120px;
|
||||
height: 6px;
|
||||
border-radius: 3px;
|
||||
background: #d9d9d9;
|
||||
outline: none;
|
||||
cursor: pointer;
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
}
|
||||
|
||||
.opacity-slider::-webkit-slider-thumb {
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
border-radius: 50%;
|
||||
background: #1890ff;
|
||||
cursor: pointer;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.opacity-slider::-moz-range-thumb {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
border-radius: 50%;
|
||||
background: #1890ff;
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.opacity-value {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
margin-left: 8px;
|
||||
min-width: 35px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
/* 滚动条样式 */
|
||||
.color-config-panel::-webkit-scrollbar {
|
||||
width: 6px;
|
@ -4,7 +4,7 @@ import type { EditorService } from '@core/editor.service';
|
||||
import { useToolbar } from '@core/useToolbar';
|
||||
import { computed, inject, type InjectionKey, onBeforeUnmount, onMounted, ref, type ShallowRef } from 'vue';
|
||||
|
||||
import ColorConfigPanel from './color-config-panel.vue';
|
||||
import ColorConfigPanel from './color/color-config-panel.vue';
|
||||
|
||||
// 通用地图工具栏(右下角),临时中文按钮
|
||||
// 功能:放大、缩小、适配视图、全屏、截图、网格(占位)
|
||||
|
@ -10,7 +10,7 @@ import { computed, onMounted, onUnmounted, provide, ref, shallowRef, watch } fro
|
||||
import { useRoute } from 'vue-router';
|
||||
|
||||
import { autoDoorSimulationService, type AutoDoorWebSocketData } from '../services/auto-door-simulation.service';
|
||||
import colorConfig from '../services/color-config.service';
|
||||
import colorConfig from '../services/color/color-config.service';
|
||||
import {
|
||||
type ContextMenuState,
|
||||
createContextMenuManager,
|
||||
|
@ -46,12 +46,22 @@ export interface EditorColorConfig {
|
||||
strokeActive: string;
|
||||
stroke: Record<number, string>; // 按区域类型索引
|
||||
fill: Record<number, string>; // 按区域类型索引
|
||||
// 边框颜色配置
|
||||
border: {
|
||||
width: number; // 边框宽度
|
||||
opacity: number; // 边框透明度 (0-1)
|
||||
// 各类型区域边框颜色
|
||||
colors: Record<number, string>; // 按区域类型索引的边框颜色
|
||||
};
|
||||
// 各类型区域专用颜色
|
||||
types: {
|
||||
[key: number]: {
|
||||
stroke: string;
|
||||
strokeActive: string;
|
||||
fill: string;
|
||||
borderColor: string; // 边框颜色
|
||||
borderWidth: number; // 边框宽度
|
||||
borderOpacity: number; // 边框透明度
|
||||
};
|
||||
};
|
||||
};
|
||||
@ -165,13 +175,60 @@ const DEFAULT_COLORS: EditorColorConfig = {
|
||||
13: '#e61e4a33',
|
||||
14: '#FFD70033'
|
||||
},
|
||||
// 边框配置
|
||||
border: {
|
||||
width: 1, // 默认边框宽度
|
||||
opacity: 0.15, // 默认边框透明度 15%
|
||||
colors: {
|
||||
1: '#1890FF', // 库区边框颜色
|
||||
11: '#FF4D4F', // 互斥区边框颜色
|
||||
12: '#52C41A', // 非互斥区边框颜色
|
||||
13: '#FA8C16', // 约束区边框颜色
|
||||
14: '#722ED1' // 描述区边框颜色
|
||||
}
|
||||
},
|
||||
types: {
|
||||
// 区域类型
|
||||
1: { stroke: '#9ACDFF99', strokeActive: '#EBB214', fill: '#9ACDFF33' }, // 库区
|
||||
11: { stroke: '#FF535399', strokeActive: '#EBB214', fill: '#FF9A9A33' }, // 互斥区
|
||||
12: { stroke: '#0DBB8A99', strokeActive: '#EBB214', fill: '#0DBB8A33' }, // 非互斥区
|
||||
13: { stroke: '#e61e4aad', strokeActive: '#EBB214', fill: '#e61e4a33' }, // 约束区
|
||||
14: { stroke: '#FFD70099', strokeActive: '#EBB214', fill: '#FFD70033' }, // 描述区
|
||||
1: {
|
||||
stroke: '#9ACDFF99',
|
||||
strokeActive: '#EBB214',
|
||||
fill: '#9ACDFF33',
|
||||
borderColor: '#1890FF',
|
||||
borderWidth: 1,
|
||||
borderOpacity: 0.15
|
||||
}, // 库区
|
||||
11: {
|
||||
stroke: '#FF535399',
|
||||
strokeActive: '#EBB214',
|
||||
fill: '#FF9A9A33',
|
||||
borderColor: '#FF4D4F',
|
||||
borderWidth: 1,
|
||||
borderOpacity: 0.15
|
||||
}, // 互斥区
|
||||
12: {
|
||||
stroke: '#0DBB8A99',
|
||||
strokeActive: '#EBB214',
|
||||
fill: '#0DBB8A33',
|
||||
borderColor: '#52C41A',
|
||||
borderWidth: 1,
|
||||
borderOpacity: 0.15
|
||||
}, // 非互斥区
|
||||
13: {
|
||||
stroke: '#e61e4aad',
|
||||
strokeActive: '#EBB214',
|
||||
fill: '#e61e4a33',
|
||||
borderColor: '#FA8C16',
|
||||
borderWidth: 1,
|
||||
borderOpacity: 0.15
|
||||
}, // 约束区
|
||||
14: {
|
||||
stroke: '#FFD70099',
|
||||
strokeActive: '#EBB214',
|
||||
fill: '#FFD70033',
|
||||
borderColor: '#722ED1',
|
||||
borderWidth: 1,
|
||||
borderOpacity: 0.15
|
||||
}, // 描述区
|
||||
}
|
||||
},
|
||||
robot: {
|
||||
@ -217,6 +274,7 @@ class ColorConfigService {
|
||||
private config = ref<EditorColorConfig>({ ...DEFAULT_COLORS });
|
||||
private editorService: any = null;
|
||||
private readonly STORAGE_KEY = 'editor-color-config';
|
||||
private isResetting = false; // 标记是否正在重置
|
||||
|
||||
constructor() {
|
||||
// 从本地存储加载配置
|
||||
@ -234,7 +292,10 @@ class ColorConfigService {
|
||||
watch(
|
||||
() => this.config.value,
|
||||
(newConfig) => {
|
||||
this.saveToLocalStorage(newConfig);
|
||||
// 如果正在重置,不保存到本地存储
|
||||
if (!this.isResetting) {
|
||||
this.saveToLocalStorage(newConfig);
|
||||
}
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
@ -276,6 +337,11 @@ class ColorConfigService {
|
||||
* 从本地存储加载配置
|
||||
*/
|
||||
private loadFromLocalStorage(): void {
|
||||
// 如果正在重置,不加载本地存储
|
||||
if (this.isResetting) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const stored = localStorage.getItem(this.STORAGE_KEY);
|
||||
if (stored) {
|
||||
@ -336,7 +402,18 @@ class ColorConfigService {
|
||||
* 重置为默认配置
|
||||
*/
|
||||
public resetToDefault(): void {
|
||||
this.config.value = { ...DEFAULT_COLORS };
|
||||
// 设置重置标记,防止自动保存
|
||||
this.isResetting = true;
|
||||
|
||||
// 清除本地存储
|
||||
localStorage.removeItem(this.STORAGE_KEY);
|
||||
|
||||
// 重置为默认配置,使用深度复制确保完全重置
|
||||
this.config.value = JSON.parse(JSON.stringify(DEFAULT_COLORS));
|
||||
|
||||
// 重置标记,允许后续自动保存
|
||||
this.isResetting = false;
|
||||
|
||||
// 触发编辑器重新渲染
|
||||
this.triggerRender();
|
||||
}
|
||||
@ -366,10 +443,94 @@ class ColorConfigService {
|
||||
this.triggerRender();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置区域边框颜色
|
||||
* @param areaType 区域类型
|
||||
* @param color 边框颜色
|
||||
*/
|
||||
public setAreaBorderColor(areaType: number, color: string): void {
|
||||
this.setColor(`area.border.colors.${areaType}`, color);
|
||||
this.setColor(`area.types.${areaType}.borderColor`, color);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置区域边框宽度
|
||||
* @param areaType 区域类型
|
||||
* @param width 边框宽度
|
||||
*/
|
||||
public setAreaBorderWidth(areaType: number, width: number): void {
|
||||
this.setNestedValue(this.config.value, `area.types.${areaType}.borderWidth`, width);
|
||||
this.triggerRender();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 设置区域边框透明度
|
||||
* @param areaType 区域类型
|
||||
* @param opacity 透明度 (0-1)
|
||||
*/
|
||||
public setAreaBorderOpacity(areaType: number, opacity: number): void {
|
||||
this.setNestedValue(this.config.value, `area.types.${areaType}.borderOpacity`, opacity);
|
||||
this.triggerRender();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取区域边框颜色
|
||||
* @param areaType 区域类型
|
||||
*/
|
||||
public getAreaBorderColor(areaType: number): string {
|
||||
return this.getNestedValue(this.config.value, `area.border.colors.${areaType}`) ||
|
||||
this.getNestedValue(this.config.value, `area.types.${areaType}.borderColor`) ||
|
||||
this.config.value.area.border.colors[areaType] || '';
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取区域边框宽度
|
||||
* @param areaType 区域类型
|
||||
*/
|
||||
public getAreaBorderWidth(areaType: number): number {
|
||||
return this.getNestedValue(this.config.value, `area.types.${areaType}.borderWidth`) || this.config.value.area.border.width;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取区域边框透明度
|
||||
* @param areaType 区域类型
|
||||
*/
|
||||
public getAreaBorderOpacity(areaType: number): number {
|
||||
return this.getNestedValue(this.config.value, `area.types.${areaType}.borderOpacity`) || this.config.value.area.border.opacity;
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量设置区域边框配置
|
||||
* @param areaType 区域类型
|
||||
* @param config 边框配置
|
||||
*/
|
||||
public setAreaBorderConfig(areaType: number, config: {
|
||||
color?: string;
|
||||
width?: number;
|
||||
opacity?: number;
|
||||
}): void {
|
||||
if (config.color !== undefined) {
|
||||
this.setAreaBorderColor(areaType, config.color);
|
||||
}
|
||||
if (config.width !== undefined) {
|
||||
this.setAreaBorderWidth(areaType, config.width);
|
||||
}
|
||||
if (config.opacity !== undefined) {
|
||||
this.setAreaBorderOpacity(areaType, config.opacity);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从主题配置加载默认颜色
|
||||
*/
|
||||
private loadConfig(): void {
|
||||
// 如果正在重置,不加载主题配置
|
||||
if (this.isResetting) {
|
||||
return;
|
||||
}
|
||||
|
||||
const theme = sTheme.editor as any;
|
||||
if (theme) {
|
||||
// 从主题配置中加载颜色,如果没有则使用默认值
|
||||
@ -419,6 +580,59 @@ class ColorConfigService {
|
||||
12: theme.area?.['fill-12'] || DEFAULT_COLORS.area.fill[12],
|
||||
13: theme.area?.['fill-13'] || DEFAULT_COLORS.area.fill[13],
|
||||
14: theme.area?.['fill-14'] || DEFAULT_COLORS.area.fill[14],
|
||||
},
|
||||
border: {
|
||||
width: theme.area?.border?.width || DEFAULT_COLORS.area.border.width,
|
||||
opacity: theme.area?.border?.opacity || DEFAULT_COLORS.area.border.opacity,
|
||||
colors: {
|
||||
1: theme.area?.border?.['color-1'] || DEFAULT_COLORS.area.border.colors[1],
|
||||
11: theme.area?.border?.['color-11'] || DEFAULT_COLORS.area.border.colors[11],
|
||||
12: theme.area?.border?.['color-12'] || DEFAULT_COLORS.area.border.colors[12],
|
||||
13: theme.area?.border?.['color-13'] || DEFAULT_COLORS.area.border.colors[13],
|
||||
14: theme.area?.border?.['color-14'] || DEFAULT_COLORS.area.border.colors[14],
|
||||
}
|
||||
},
|
||||
types: {
|
||||
1: {
|
||||
stroke: theme.area?.['stroke-1'] || DEFAULT_COLORS.area.types[1].stroke,
|
||||
strokeActive: theme.area?.strokeActive || DEFAULT_COLORS.area.types[1].strokeActive,
|
||||
fill: theme.area?.['fill-1'] || DEFAULT_COLORS.area.types[1].fill,
|
||||
borderColor: theme.area?.border?.['color-1'] || DEFAULT_COLORS.area.types[1].borderColor,
|
||||
borderWidth: theme.area?.border?.width || DEFAULT_COLORS.area.types[1].borderWidth,
|
||||
borderOpacity: theme.area?.border?.opacity || DEFAULT_COLORS.area.types[1].borderOpacity,
|
||||
},
|
||||
11: {
|
||||
stroke: theme.area?.['stroke-11'] || DEFAULT_COLORS.area.types[11].stroke,
|
||||
strokeActive: theme.area?.strokeActive || DEFAULT_COLORS.area.types[11].strokeActive,
|
||||
fill: theme.area?.['fill-11'] || DEFAULT_COLORS.area.types[11].fill,
|
||||
borderColor: theme.area?.border?.['color-11'] || DEFAULT_COLORS.area.types[11].borderColor,
|
||||
borderWidth: theme.area?.border?.width || DEFAULT_COLORS.area.types[11].borderWidth,
|
||||
borderOpacity: theme.area?.border?.opacity || DEFAULT_COLORS.area.types[11].borderOpacity,
|
||||
},
|
||||
12: {
|
||||
stroke: theme.area?.['stroke-12'] || DEFAULT_COLORS.area.types[12].stroke,
|
||||
strokeActive: theme.area?.strokeActive || DEFAULT_COLORS.area.types[12].strokeActive,
|
||||
fill: theme.area?.['fill-12'] || DEFAULT_COLORS.area.types[12].fill,
|
||||
borderColor: theme.area?.border?.['color-12'] || DEFAULT_COLORS.area.types[12].borderColor,
|
||||
borderWidth: theme.area?.border?.width || DEFAULT_COLORS.area.types[12].borderWidth,
|
||||
borderOpacity: theme.area?.border?.opacity || DEFAULT_COLORS.area.types[12].borderOpacity,
|
||||
},
|
||||
13: {
|
||||
stroke: theme.area?.['stroke-13'] || DEFAULT_COLORS.area.types[13].stroke,
|
||||
strokeActive: theme.area?.strokeActive || DEFAULT_COLORS.area.types[13].strokeActive,
|
||||
fill: theme.area?.['fill-13'] || DEFAULT_COLORS.area.types[13].fill,
|
||||
borderColor: theme.area?.border?.['color-13'] || DEFAULT_COLORS.area.types[13].borderColor,
|
||||
borderWidth: theme.area?.border?.width || DEFAULT_COLORS.area.types[13].borderWidth,
|
||||
borderOpacity: theme.area?.border?.opacity || DEFAULT_COLORS.area.types[13].borderOpacity,
|
||||
},
|
||||
14: {
|
||||
stroke: theme.area?.['stroke-14'] || DEFAULT_COLORS.area.types[14].stroke,
|
||||
strokeActive: theme.area?.strokeActive || DEFAULT_COLORS.area.types[14].strokeActive,
|
||||
fill: theme.area?.['fill-14'] || DEFAULT_COLORS.area.types[14].fill,
|
||||
borderColor: theme.area?.border?.['color-14'] || DEFAULT_COLORS.area.types[14].borderColor,
|
||||
borderWidth: theme.area?.border?.width || DEFAULT_COLORS.area.types[14].borderWidth,
|
||||
borderOpacity: theme.area?.border?.opacity || DEFAULT_COLORS.area.types[14].borderOpacity,
|
||||
}
|
||||
}
|
||||
},
|
||||
robot: {
|
@ -1,7 +1,7 @@
|
||||
import type { MapPen } from '@api/map';
|
||||
import { LockState } from '@meta2d/core';
|
||||
|
||||
import colorConfig from '../color-config.service';
|
||||
import colorConfig from '../color/color-config.service';
|
||||
|
||||
// 预加载锁定图标(仅一次)
|
||||
const lockedIcon = new Image();
|
||||
|
@ -30,7 +30,7 @@ import { BehaviorSubject, debounceTime, filter, map, Subject, switchMap } from '
|
||||
import { reactive, watch } from 'vue';
|
||||
|
||||
import { AreaOperationService } from './area-operation.service';
|
||||
import colorConfig from './color-config.service';
|
||||
import colorConfig from './color/color-config.service';
|
||||
import {
|
||||
drawStorageBackground,
|
||||
drawStorageLocation,
|
||||
@ -1895,10 +1895,32 @@ function drawArea(ctx: CanvasRenderingContext2D, pen: MapPen): void {
|
||||
ctx.fillStyle = finalFillColor;
|
||||
ctx.fill();
|
||||
|
||||
// 获取边框颜色 - 优先使用新的边框颜色配置
|
||||
const borderColor = type ? colorConfig.getAreaBorderColor(type) : '';
|
||||
const generalStrokeColor = colorConfig.getColor(active ? 'area.strokeActive' : `area.stroke.${type}`);
|
||||
const typeStrokeColor = colorConfig.getColor(`area.types.${type}.stroke`);
|
||||
const themeStrokeColor = get(theme, active ? 'area.strokeActive' : `area.stroke-${type}`);
|
||||
ctx.strokeStyle = generalStrokeColor || typeStrokeColor || themeStrokeColor || '';
|
||||
|
||||
// 获取边框宽度和透明度
|
||||
const borderWidth = type ? colorConfig.getAreaBorderWidth(type) : 1;
|
||||
const borderOpacity = type ? colorConfig.getAreaBorderOpacity(type) : 0.15;
|
||||
|
||||
|
||||
// 设置边框宽度和样式
|
||||
ctx.lineWidth = borderWidth;
|
||||
ctx.setLineDash([]); // 固定为实线
|
||||
|
||||
// 优先使用边框颜色,然后是通用颜色,最后是主题颜色
|
||||
let finalStrokeColor = borderColor || generalStrokeColor || typeStrokeColor || themeStrokeColor || '';
|
||||
|
||||
// 应用透明度
|
||||
if (borderOpacity < 1 && finalStrokeColor.startsWith('#')) {
|
||||
const alpha = Math.round(borderOpacity * 255).toString(16).padStart(2, '0');
|
||||
finalStrokeColor = finalStrokeColor + alpha;
|
||||
}
|
||||
|
||||
ctx.strokeStyle = finalStrokeColor;
|
||||
|
||||
ctx.stroke();
|
||||
|
||||
// 如果是描述区且有描述内容,渲染描述文字
|
||||
|
Loading…
x
Reference in New Issue
Block a user