web-map/src/services/color-config.service.ts

473 lines
14 KiB
TypeScript
Raw Normal View History

import sTheme from '@core/theme.service';
import { ref, watch } from 'vue';
/**
*
*/
export interface EditorColorConfig {
// 点位颜色
point: {
small: {
stroke: string;
strokeActive: string;
fill: Record<number, string>; // 按点位类型索引
};
large: {
stroke: string;
strokeActive: string;
strokeOccupied: string;
strokeUnoccupied: string;
strokeEmpty: string;
strokeDisabled: string;
strokeEnabled: string;
strokeLocked: string;
strokeUnlocked: string;
};
// 各类型点位专用颜色
types: {
[key: number]: {
stroke: string;
strokeActive: string;
fill: string;
};
};
};
// 路线颜色
route: {
strokeActive: string;
strokeEmpty: string; // 空载路线颜色
strokeLoaded: string; // 载货路线颜色
strokeForbidden: string; // 禁行路线颜色
};
// 区域颜色
area: {
strokeActive: string;
stroke: Record<number, string>; // 按区域类型索引
fill: Record<number, string>; // 按区域类型索引
// 各类型区域专用颜色
types: {
[key: number]: {
stroke: string;
strokeActive: string;
fill: string;
};
};
};
// 机器人颜色
robot: {
stroke: string;
fill: string;
line: string;
strokeNormal: string;
fillNormal: string;
strokeWarning: string;
fillWarning: string;
strokeFault: string;
fillFault: string;
};
// 库位颜色
storage: {
occupied: string;
available: string;
default: string;
locked: string;
moreButton: {
background: string;
border: string;
text: string;
};
};
// 自动门颜色
autoDoor: {
strokeClosed: string;
fillClosed: string;
strokeOpen: string;
fillOpen: string;
};
// 通用颜色
common: {
color: string;
background: string;
};
}
/**
*
*/
const DEFAULT_COLORS: EditorColorConfig = {
point: {
small: {
stroke: '#8C8C8C',
strokeActive: '#EBB214',
fill: {
1: '#14D1A5',
2: '#69C6F5',
3: '#E48B1D',
4: '#E48B1D',
5: '#a72b69'
}
},
large: {
stroke: '#595959',
strokeActive: '#EBB214',
strokeOccupied: '#ff4d4f',
strokeUnoccupied: '#52c41a',
strokeEmpty: '#1890ff',
strokeDisabled: '#bfbfbf',
strokeEnabled: '#52c41a',
strokeLocked: '#faad14',
strokeUnlocked: '#52c41a'
},
types: {
// 小点位类型 (1-9)
1: { stroke: '#8C8C8C', strokeActive: '#EBB214', fill: '#14D1A5' }, // 普通点
2: { stroke: '#8C8C8C', strokeActive: '#EBB214', fill: '#69C6F5' }, // 等待点
3: { stroke: '#8C8C8C', strokeActive: '#EBB214', fill: '#E48B1D' }, // 避让点
4: { stroke: '#8C8C8C', strokeActive: '#EBB214', fill: '#E48B1D' }, // 临时避让点
5: { stroke: '#8C8C8C', strokeActive: '#EBB214', fill: '#a72b69' }, // 库区点
// 大点位类型 (11+)
11: { stroke: '#595959', strokeActive: '#EBB214', fill: '#1890ff' }, // 电梯点
12: { stroke: '#595959', strokeActive: '#EBB214', fill: '#52c41a' }, // 自动门点
13: { stroke: '#595959', strokeActive: '#EBB214', fill: '#faad14' }, // 充电点
14: { stroke: '#595959', strokeActive: '#EBB214', fill: '#722ed1' }, // 停靠点
15: { stroke: '#595959', strokeActive: '#EBB214', fill: '#13c2c2' }, // 动作点
16: { stroke: '#595959', strokeActive: '#EBB214', fill: '#ff4d4f' }, // 禁行点
}
},
route: {
strokeActive: '#EBB214',
strokeEmpty: '#52C41A', // 空载路线 - 绿色
strokeLoaded: '#1982F3', // 载货路线 - 蓝色
strokeForbidden: '#E63A3A' // 禁行路线 - 红色
},
area: {
strokeActive: '#EBB214',
stroke: {
1: '#9ACDFF99',
11: '#FF535399',
12: '#0DBB8A99',
13: '#e61e4aad',
14: '#FFD70099'
},
fill: {
1: '#9ACDFF33',
11: '#FF9A9A33',
12: '#0DBB8A33',
13: '#e61e4a33',
14: '#FFD70033'
},
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' }, // 描述区
}
},
robot: {
stroke: '#01FDAF99',
fill: '#01FAAD33',
line: '#01fdaf',
strokeNormal: '#01FDAF99',
fillNormal: '#01FAAD33',
strokeWarning: '#FF851B99',
fillWarning: '#FF851B33',
strokeFault: '#FF4D4F99',
fillFault: '#FF4D4F33'
},
storage: {
occupied: '#ff4d4f',
available: '#52c41a',
default: '#f5f5f5',
locked: '#faad14',
moreButton: {
background: '#e6f4ff',
border: '#1677ff',
text: '#1677ff'
}
},
autoDoor: {
strokeClosed: '#FF4D4F99',
fillClosed: '#FF4D4F33',
strokeOpen: '#1890FF99',
fillOpen: '#1890FF33'
},
common: {
color: '#595959',
background: '#F0F2F5'
}
};
/**
*
*/
class ColorConfigService {
private config = ref<EditorColorConfig>({ ...DEFAULT_COLORS });
private editorService: any = null;
private readonly STORAGE_KEY = 'editor-color-config';
constructor() {
// 从本地存储加载配置
this.loadFromLocalStorage();
// 监听主题变化,重新加载配置
watch(
() => sTheme.theme,
() => {
this.loadConfig();
}
);
// 监听配置变化,自动保存到本地存储
watch(
() => this.config.value,
(newConfig) => {
this.saveToLocalStorage(newConfig);
},
{ deep: true }
);
}
/**
*
*/
public get currentConfig(): EditorColorConfig {
return this.config.value;
}
/**
*
*/
public setEditorService(editor: any): void {
this.editorService = editor;
}
/**
*
*/
private triggerRender(): void {
if (this.editorService && typeof this.editorService.render === 'function') {
this.editorService.render();
}
}
/**
*
*/
private loadFromLocalStorage(): void {
try {
const stored = localStorage.getItem(this.STORAGE_KEY);
if (stored) {
const parsedConfig = JSON.parse(stored);
this.config.value = this.mergeConfig(DEFAULT_COLORS, parsedConfig);
} else {
this.config.value = { ...DEFAULT_COLORS };
}
} catch (error) {
console.warn('Failed to load color config from localStorage:', error);
this.config.value = { ...DEFAULT_COLORS };
}
}
/**
*
*/
private saveToLocalStorage(config: EditorColorConfig): void {
try {
localStorage.setItem(this.STORAGE_KEY, JSON.stringify(config));
} catch (error) {
console.warn('Failed to save color config to localStorage:', error);
}
}
/**
* 使
* @deprecated 使
*/
public loadFromScene(): void {
// 不再从场景数据加载,保持向后兼容
console.warn('loadFromScene is deprecated, color config now uses localStorage');
}
/**
* 使
* @deprecated 使
*/
public getConfigForSave(): EditorColorConfig {
console.warn('getConfigForSave is deprecated, color config now uses localStorage');
return { ...this.config.value };
}
/**
*
* @param updates
*/
public updateConfig(updates: Partial<EditorColorConfig>): void {
this.config.value = {
...this.config.value,
...updates
};
// 触发编辑器重新渲染
this.triggerRender();
}
/**
*
*/
public resetToDefault(): void {
this.config.value = { ...DEFAULT_COLORS };
// 触发编辑器重新渲染
this.triggerRender();
}
/**
*
* @param path 'point.small.stroke'
*/
public getColor(path: string): string {
return this.getNestedValue(this.config.value, path) || '';
}
/**
*
* @param path
* @param value
*/
public setColor(path: string, value: string): void {
this.setNestedValue(this.config.value, path, value);
// 触发编辑器重新渲染
this.triggerRender();
}
/**
*
*/
private loadConfig(): void {
const theme = sTheme.editor as any;
if (theme) {
// 从主题配置中加载颜色,如果没有则使用默认值
this.config.value = this.mergeConfig(DEFAULT_COLORS, {
point: {
small: {
stroke: theme['point-s']?.stroke || DEFAULT_COLORS.point.small.stroke,
strokeActive: theme['point-s']?.strokeActive || DEFAULT_COLORS.point.small.strokeActive,
fill: {
1: theme['point-s']?.['fill-1'] || DEFAULT_COLORS.point.small.fill[1],
2: theme['point-s']?.['fill-2'] || DEFAULT_COLORS.point.small.fill[2],
3: theme['point-s']?.['fill-3'] || DEFAULT_COLORS.point.small.fill[3],
4: theme['point-s']?.['fill-4'] || DEFAULT_COLORS.point.small.fill[4],
5: theme['point-s']?.['fill-5'] || DEFAULT_COLORS.point.small.fill[5],
}
},
large: {
stroke: theme['point-l']?.stroke || DEFAULT_COLORS.point.large.stroke,
strokeActive: theme['point-l']?.strokeActive || DEFAULT_COLORS.point.large.strokeActive,
strokeOccupied: theme['point-l']?.['stroke-occupied'] || DEFAULT_COLORS.point.large.strokeOccupied,
strokeUnoccupied: theme['point-l']?.['stroke-unoccupied'] || DEFAULT_COLORS.point.large.strokeUnoccupied,
strokeEmpty: theme['point-l']?.['stroke-empty'] || DEFAULT_COLORS.point.large.strokeEmpty,
strokeDisabled: theme['point-l']?.['stroke-disabled'] || DEFAULT_COLORS.point.large.strokeDisabled,
strokeEnabled: theme['point-l']?.['stroke-enabled'] || DEFAULT_COLORS.point.large.strokeEnabled,
strokeLocked: theme['point-l']?.['stroke-locked'] || DEFAULT_COLORS.point.large.strokeLocked,
strokeUnlocked: theme['point-l']?.['stroke-unlocked'] || DEFAULT_COLORS.point.large.strokeUnlocked,
}
},
route: {
strokeActive: theme.route?.strokeActive || DEFAULT_COLORS.route.strokeActive,
strokeEmpty: theme.route?.['stroke-empty'] || DEFAULT_COLORS.route.strokeEmpty,
strokeLoaded: theme.route?.['stroke-loaded'] || DEFAULT_COLORS.route.strokeLoaded,
strokeForbidden: theme.route?.['stroke-forbidden'] || DEFAULT_COLORS.route.strokeForbidden,
},
area: {
strokeActive: theme.area?.strokeActive || DEFAULT_COLORS.area.strokeActive,
stroke: {
1: theme.area?.['stroke-1'] || DEFAULT_COLORS.area.stroke[1],
11: theme.area?.['stroke-11'] || DEFAULT_COLORS.area.stroke[11],
12: theme.area?.['stroke-12'] || DEFAULT_COLORS.area.stroke[12],
13: theme.area?.['stroke-13'] || DEFAULT_COLORS.area.stroke[13],
14: theme.area?.['stroke-14'] || DEFAULT_COLORS.area.stroke[14],
},
fill: {
1: theme.area?.['fill-1'] || DEFAULT_COLORS.area.fill[1],
11: theme.area?.['fill-11'] || DEFAULT_COLORS.area.fill[11],
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],
}
},
robot: {
stroke: theme.robot?.stroke || DEFAULT_COLORS.robot.stroke,
fill: theme.robot?.fill || DEFAULT_COLORS.robot.fill,
line: theme.robot?.line || DEFAULT_COLORS.robot.line,
strokeNormal: theme.robot?.['stroke-normal'] || DEFAULT_COLORS.robot.strokeNormal,
fillNormal: theme.robot?.['fill-normal'] || DEFAULT_COLORS.robot.fillNormal,
strokeWarning: theme.robot?.['stroke-warning'] || DEFAULT_COLORS.robot.strokeWarning,
fillWarning: theme.robot?.['fill-warning'] || DEFAULT_COLORS.robot.fillWarning,
strokeFault: theme.robot?.['stroke-fault'] || DEFAULT_COLORS.robot.strokeFault,
fillFault: theme.robot?.['fill-fault'] || DEFAULT_COLORS.robot.fillFault,
},
autoDoor: {
strokeClosed: theme.autoDoor?.['stroke-closed'] || DEFAULT_COLORS.autoDoor.strokeClosed,
fillClosed: theme.autoDoor?.['fill-closed'] || DEFAULT_COLORS.autoDoor.fillClosed,
strokeOpen: theme.autoDoor?.['stroke-open'] || DEFAULT_COLORS.autoDoor.strokeOpen,
fillOpen: theme.autoDoor?.['fill-open'] || DEFAULT_COLORS.autoDoor.fillOpen,
},
common: {
color: theme.color || DEFAULT_COLORS.common.color,
background: theme.background || DEFAULT_COLORS.common.background,
}
});
}
}
/**
*
*/
private mergeConfig(target: any, source: any): any {
const result = { ...target };
for (const key in source) {
if (source[key] && typeof source[key] === 'object' && !Array.isArray(source[key])) {
result[key] = this.mergeConfig(target[key] || {}, source[key]);
} else {
result[key] = source[key];
}
}
return result;
}
/**
*
*/
private getNestedValue(obj: any, path: string): any {
return path.split('.').reduce((current, key) => current?.[key], obj);
}
/**
*
*/
private setNestedValue(obj: any, path: string, value: any): void {
const keys = path.split('.');
const lastKey = keys.pop()!;
const target = keys.reduce((current, key) => {
if (!current[key]) {
// 如果是数字键,创建数组或对象
if (!isNaN(Number(key))) {
current[key] = {};
} else {
current[key] = {};
}
}
return current[key];
}, obj);
target[lastKey] = value;
}
}
export default new ColorConfigService();