feat: 添加机器人图片尺寸配置功能,支持动态调整和应用,优化相关样式和逻辑
This commit is contained in:
parent
2174e8350b
commit
7708bcfaa1
@ -141,6 +141,53 @@
|
||||
@update:modelValue="(value) => updateColor('robot.line', { target: { value } })"
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- 机器人图片尺寸配置 -->
|
||||
<div class="robot-size-section">
|
||||
<div class="color-group">
|
||||
<label>图片宽度:</label>
|
||||
<input
|
||||
type="number"
|
||||
:value="tempRobotSize.imageWidth"
|
||||
@input="(e) => updateTempRobotSize('imageWidth', e)"
|
||||
min="20"
|
||||
max="120"
|
||||
step="2"
|
||||
class="size-input"
|
||||
/>
|
||||
<span class="unit">px</span>
|
||||
</div>
|
||||
<div class="color-group">
|
||||
<label>图片高度:</label>
|
||||
<input
|
||||
type="number"
|
||||
:value="tempRobotSize.imageHeight"
|
||||
@input="(e) => updateTempRobotSize('imageHeight', e)"
|
||||
min="40"
|
||||
max="150"
|
||||
step="2"
|
||||
class="size-input"
|
||||
/>
|
||||
<span class="unit">px</span>
|
||||
</div>
|
||||
<div class="size-actions">
|
||||
<button
|
||||
@click="applyRobotSize"
|
||||
class="btn-apply"
|
||||
:disabled="!hasRobotSizeChanged"
|
||||
>
|
||||
确定
|
||||
</button>
|
||||
<button
|
||||
@click="resetRobotSize"
|
||||
class="btn-reset-size"
|
||||
:disabled="!hasRobotSizeChanged"
|
||||
>
|
||||
重置
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 库位颜色配置 -->
|
||||
@ -171,7 +218,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, defineAsyncComponent } from 'vue';
|
||||
import { computed, defineAsyncComponent, ref, watch } from 'vue';
|
||||
|
||||
import colorConfig from '../services/color-config.service';
|
||||
// 动态导入颜色选择器组件
|
||||
@ -184,6 +231,27 @@ defineOptions({
|
||||
|
||||
const config = computed(() => colorConfig.currentConfig);
|
||||
|
||||
// 机器人图片尺寸的临时状态
|
||||
const tempRobotSize = ref({
|
||||
imageWidth: config.value.robot.imageWidth,
|
||||
imageHeight: config.value.robot.imageHeight
|
||||
});
|
||||
|
||||
// 监听配置变化,同步临时状态
|
||||
watch(() => config.value.robot.imageWidth, (newVal) => {
|
||||
tempRobotSize.value.imageWidth = newVal;
|
||||
});
|
||||
|
||||
watch(() => config.value.robot.imageHeight, (newVal) => {
|
||||
tempRobotSize.value.imageHeight = newVal;
|
||||
});
|
||||
|
||||
// 检查机器人尺寸是否有变化
|
||||
const hasRobotSizeChanged = computed(() => {
|
||||
return tempRobotSize.value.imageWidth !== config.value.robot.imageWidth ||
|
||||
tempRobotSize.value.imageHeight !== config.value.robot.imageHeight;
|
||||
});
|
||||
|
||||
const updateColor = (path: string, event: Event | { target: { value: string } }) => {
|
||||
const target = event.target as HTMLInputElement;
|
||||
if (target) {
|
||||
@ -191,8 +259,31 @@ const updateColor = (path: string, event: Event | { target: { value: string } })
|
||||
}
|
||||
};
|
||||
|
||||
// 更新临时机器人尺寸
|
||||
const updateTempRobotSize = (key: 'imageWidth' | 'imageHeight', event: Event) => {
|
||||
const target = event.target as HTMLInputElement;
|
||||
if (target) {
|
||||
tempRobotSize.value[key] = Number(target.value);
|
||||
}
|
||||
};
|
||||
|
||||
// 应用机器人尺寸更改
|
||||
const applyRobotSize = () => {
|
||||
colorConfig.setColor('robot.imageWidth', tempRobotSize.value.imageWidth.toString());
|
||||
colorConfig.setColor('robot.imageHeight', tempRobotSize.value.imageHeight.toString());
|
||||
};
|
||||
|
||||
// 重置机器人尺寸到当前配置值
|
||||
const resetRobotSize = () => {
|
||||
tempRobotSize.value.imageWidth = config.value.robot.imageWidth;
|
||||
tempRobotSize.value.imageHeight = config.value.robot.imageHeight;
|
||||
};
|
||||
|
||||
const resetToDefault = () => {
|
||||
colorConfig.resetToDefault();
|
||||
// 重置临时状态
|
||||
tempRobotSize.value.imageWidth = config.value.robot.imageWidth;
|
||||
tempRobotSize.value.imageHeight = config.value.robot.imageHeight;
|
||||
};
|
||||
</script>
|
||||
|
||||
@ -279,6 +370,33 @@ const resetToDefault = () => {
|
||||
box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
|
||||
}
|
||||
|
||||
.size-input {
|
||||
width: 80px;
|
||||
height: 36px;
|
||||
padding: 0 8px;
|
||||
border: 2px solid #d9d9d9;
|
||||
border-radius: 6px;
|
||||
font-size: 14px;
|
||||
text-align: center;
|
||||
transition: border-color 0.2s;
|
||||
}
|
||||
|
||||
.size-input:hover {
|
||||
border-color: #40a9ff;
|
||||
}
|
||||
|
||||
.size-input:focus {
|
||||
border-color: #1890ff;
|
||||
outline: none;
|
||||
box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
|
||||
}
|
||||
|
||||
.unit {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
margin-left: 4px;
|
||||
}
|
||||
|
||||
|
||||
.actions {
|
||||
display: flex;
|
||||
@ -314,6 +432,59 @@ const resetToDefault = () => {
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
/* 机器人尺寸配置样式 */
|
||||
.robot-size-section {
|
||||
margin-top: 16px;
|
||||
padding: 16px;
|
||||
background: #fff;
|
||||
border-radius: 6px;
|
||||
border: 1px solid #e0e0e0;
|
||||
}
|
||||
|
||||
.size-actions {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-top: 16px;
|
||||
padding-top: 12px;
|
||||
border-top: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.size-actions button {
|
||||
padding: 8px 16px;
|
||||
border: 1px solid #d9d9d9;
|
||||
border-radius: 4px;
|
||||
background: white;
|
||||
cursor: pointer;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.btn-apply {
|
||||
color: #1890ff;
|
||||
border-color: #1890ff;
|
||||
}
|
||||
|
||||
.btn-apply:hover:not(:disabled) {
|
||||
background: #e6f7ff;
|
||||
border-color: #40a9ff;
|
||||
}
|
||||
|
||||
.btn-reset-size {
|
||||
color: #666;
|
||||
border-color: #d9d9d9;
|
||||
}
|
||||
|
||||
.btn-reset-size:hover:not(:disabled) {
|
||||
background: #f5f5f5;
|
||||
border-color: #bfbfbf;
|
||||
}
|
||||
|
||||
.size-actions button:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
/* 滚动条样式 */
|
||||
.color-config-panel::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
|
@ -56,7 +56,7 @@ export interface EditorColorConfig {
|
||||
};
|
||||
};
|
||||
|
||||
// 机器人颜色
|
||||
// 机器人配置
|
||||
robot: {
|
||||
stroke: string;
|
||||
fill: string;
|
||||
@ -67,6 +67,9 @@ export interface EditorColorConfig {
|
||||
fillWarning: string;
|
||||
strokeFault: string;
|
||||
fillFault: string;
|
||||
// 机器人图片尺寸
|
||||
imageWidth: number; // 机器人图片宽度
|
||||
imageHeight: number; // 机器人图片高度
|
||||
};
|
||||
|
||||
// 库位颜色
|
||||
@ -180,7 +183,9 @@ const DEFAULT_COLORS: EditorColorConfig = {
|
||||
strokeWarning: '#FF851B99',
|
||||
fillWarning: '#FF851B33',
|
||||
strokeFault: '#FF4D4F99',
|
||||
fillFault: '#FF4D4F33'
|
||||
fillFault: '#FF4D4F33',
|
||||
imageWidth: 42, // 机器人图片宽度 - 42像素
|
||||
imageHeight: 76 // 机器人图片高度 - 76像素
|
||||
},
|
||||
storage: {
|
||||
occupied: '#ff4d4f',
|
||||
@ -258,6 +263,15 @@ class ColorConfigService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新所有机器人的图片尺寸
|
||||
*/
|
||||
private updateAllRobotImageSizes(): void {
|
||||
if (this.editorService && typeof this.editorService.updateAllRobotImageSizes === 'function') {
|
||||
this.editorService.updateAllRobotImageSizes();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从本地存储加载配置
|
||||
*/
|
||||
@ -342,6 +356,12 @@ class ColorConfigService {
|
||||
*/
|
||||
public setColor(path: string, value: string): void {
|
||||
this.setNestedValue(this.config.value, path, value);
|
||||
|
||||
// 如果是机器人图片尺寸配置更新,需要特殊处理
|
||||
if (path === 'robot.imageWidth' || path === 'robot.imageHeight') {
|
||||
this.updateAllRobotImageSizes();
|
||||
}
|
||||
|
||||
// 触发编辑器重新渲染
|
||||
this.triggerRender();
|
||||
}
|
||||
|
@ -1098,6 +1098,28 @@ export class EditorService extends Meta2d {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新所有机器人的图片尺寸
|
||||
* 当机器人图片尺寸配置发生变化时调用
|
||||
*/
|
||||
public updateAllRobotImageSizes(): void {
|
||||
this.robots.forEach(({ id, type }) => {
|
||||
const pen = this.getPenById(id);
|
||||
if (pen && pen.robot) {
|
||||
const imageConfig = this.#mapRobotImage(type, pen.robot.active);
|
||||
this.setValue(
|
||||
{
|
||||
id,
|
||||
iconWidth: imageConfig.iconWidth,
|
||||
iconHeight: imageConfig.iconHeight,
|
||||
iconTop: imageConfig.iconTop,
|
||||
},
|
||||
{ render: true, history: false, doEvent: false },
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#mapRobotImage(
|
||||
type: RobotType,
|
||||
active?: boolean,
|
||||
@ -1106,10 +1128,16 @@ export class EditorService extends Meta2d {
|
||||
const image =
|
||||
import.meta.env.BASE_URL + (active ? `/robot/${type}-active-${theme}.png` : `/robot/${type}-${theme}.png`);
|
||||
|
||||
// 使用配置的机器人图片尺寸,如果没有配置则使用默认值
|
||||
const iconWidth = colorConfig.getColor('robot.imageWidth') ? Number(colorConfig.getColor('robot.imageWidth')) : 42;
|
||||
const iconHeight = colorConfig.getColor('robot.imageHeight')
|
||||
? Number(colorConfig.getColor('robot.imageHeight'))
|
||||
: 76;
|
||||
|
||||
// 使用优化的像素对齐算法,确保小车和光圈精确重合
|
||||
const iconTop = this.#calculatePixelAlignedOffset(-16);
|
||||
|
||||
return { image, iconWidth: 42, iconHeight: 76, iconTop };
|
||||
return { image, iconWidth, iconHeight, iconTop };
|
||||
}
|
||||
//#endregion
|
||||
|
||||
@ -1967,13 +1995,23 @@ function drawRobot(ctx: CanvasRenderingContext2D, pen: MapPen): void {
|
||||
// 根据机器人状态获取颜色
|
||||
const status = getRobotStatus(isWaring, isFault);
|
||||
|
||||
// 基于机器人图片大小计算光圈半径,光圈比图片大20%,并随画布缩放
|
||||
const imageWidth = colorConfig.getColor('robot.imageWidth')
|
||||
? Number(colorConfig.getColor('robot.imageWidth')) : 60;
|
||||
const imageHeight = colorConfig.getColor('robot.imageHeight')
|
||||
? Number(colorConfig.getColor('robot.imageHeight')) : 80;
|
||||
// 取图片的较大边作为基准,计算圆形光圈的半径
|
||||
const imageSize = Math.max(imageWidth, imageHeight);
|
||||
const robotRadius = (imageSize * 1.2 * s) / 2; // 光圈半径比图片大20%,并随画布缩放
|
||||
|
||||
const ox = x + w / 2;
|
||||
const oy = y + h / 2;
|
||||
ctx.save();
|
||||
ctx.ellipse(ox, oy, w / 2, h / 2, 0, 0, Math.PI * 2);
|
||||
ctx.fillStyle = colorConfig.getColor(`robot.fill${status === 'normal' ? '' : `-${status}`}`) || (get(theme, `robot.fill-${status}`) ?? (get(theme, 'robot.fill') ?? ''));
|
||||
ctx.beginPath();
|
||||
ctx.arc(ox, oy, robotRadius, 0, Math.PI * 2);
|
||||
ctx.fillStyle = colorConfig.getColor(`robot.fill${status === 'normal' ? 'Normal' : status === 'warning' ? 'Warning' : 'Fault'}`) || (get(theme, `robot.fill-${status}`) ?? (get(theme, 'robot.fill') ?? ''));
|
||||
ctx.fill();
|
||||
ctx.strokeStyle = colorConfig.getColor(`robot.stroke${status === 'normal' ? '' : `-${status}`}`) || (get(theme, `robot.stroke-${status}`) ?? (get(theme, 'robot.stroke') ?? ''));
|
||||
ctx.strokeStyle = colorConfig.getColor(`robot.stroke${status === 'normal' ? 'Normal' : status === 'warning' ? 'Warning' : 'Fault'}`) || (get(theme, `robot.stroke-${status}`) ?? (get(theme, 'robot.stroke') ?? ''));
|
||||
ctx.stroke();
|
||||
if (path?.length) {
|
||||
ctx.strokeStyle = colorConfig.getColor('robot.line') || (get(theme, 'robot.line') ?? '');
|
||||
|
Loading…
x
Reference in New Issue
Block a user