473 lines
14 KiB
TypeScript
473 lines
14 KiB
TypeScript
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();
|