feat&fix: 完善JPEGClient逻辑,前端新增编码器组件
This commit is contained in:
@@ -29,6 +29,7 @@ export interface TemplateConfig {
|
||||
export const previewSizes: Record<string, number> = {
|
||||
MechanicalButton: 0.4,
|
||||
Switch: 0.35,
|
||||
EC11RotaryEncoder: 0.4,
|
||||
Pin: 0.8,
|
||||
SMT_LED: 0.7,
|
||||
SevenSegmentDisplay: 0.4,
|
||||
@@ -48,6 +49,7 @@ export const previewSizes: Record<string, number> = {
|
||||
export const availableComponents: ComponentConfig[] = [
|
||||
{ type: "MechanicalButton", name: "机械按钮" },
|
||||
{ type: "Switch", name: "开关" },
|
||||
{ type: "EC11RotaryEncoder", name: "EC11旋转编码器" },
|
||||
{ type: "Pin", name: "引脚" },
|
||||
{ type: "SMT_LED", name: "贴片LED" },
|
||||
{ type: "SevenSegmentDisplay", name: "数码管" },
|
||||
|
||||
258
src/components/equipments/EC11RotaryEncoder.vue
Normal file
258
src/components/equipments/EC11RotaryEncoder.vue
Normal file
@@ -0,0 +1,258 @@
|
||||
<template>
|
||||
<div class="ec11-container" :style="{
|
||||
width: width + 'px',
|
||||
height: height + 'px',
|
||||
position: 'relative',
|
||||
}">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" :width="width" :height="height" viewBox="0 0 100 100"
|
||||
class="ec11-encoder">
|
||||
<defs>
|
||||
<!-- 发光效果滤镜 -->
|
||||
<filter id="glow" x="-50%" y="-50%" width="200%" height="200%">
|
||||
<feFlood result="flood" flood-color="#00ff88" flood-opacity="1"></feFlood>
|
||||
<feComposite in="flood" result="mask" in2="SourceGraphic" operator="in"></feComposite>
|
||||
<feMorphology in="mask" result="dilated" operator="dilate" radius="1"></feMorphology>
|
||||
<feGaussianBlur in="dilated" stdDeviation="2" result="blur1" />
|
||||
<feGaussianBlur in="dilated" stdDeviation="4" result="blur2" />
|
||||
<feGaussianBlur in="dilated" stdDeviation="8" result="blur3" />
|
||||
<feMerge>
|
||||
<feMergeNode in="blur3" />
|
||||
<feMergeNode in="blur2" />
|
||||
<feMergeNode in="blur1" />
|
||||
<feMergeNode in="SourceGraphic" />
|
||||
</feMerge>
|
||||
</filter>
|
||||
|
||||
<!-- 编码器主体渐变 -->
|
||||
<radialGradient id="encoderGradient" cx="50%" cy="30%">
|
||||
<stop offset="0%" stop-color="#666666" />
|
||||
<stop offset="70%" stop-color="#333333" />
|
||||
<stop offset="100%" stop-color="#1a1a1a" />
|
||||
</radialGradient>
|
||||
|
||||
<!-- 旋钮渐变 -->
|
||||
<radialGradient id="knobGradient" cx="30%" cy="30%">
|
||||
<stop offset="0%" stop-color="#555555" />
|
||||
<stop offset="70%" stop-color="#222222" />
|
||||
<stop offset="100%" stop-color="#111111" />
|
||||
</radialGradient>
|
||||
|
||||
<!-- 按下状态渐变 -->
|
||||
<radialGradient id="knobPressedGradient" cx="50%" cy="50%">
|
||||
<stop offset="0%" stop-color="#333333" />
|
||||
<stop offset="70%" stop-color="#555555" />
|
||||
<stop offset="100%" stop-color="#888888" />
|
||||
</radialGradient>
|
||||
</defs>
|
||||
|
||||
<!-- 编码器底座 -->
|
||||
<rect x="10" y="30" width="80" height="60" rx="8" ry="8"
|
||||
fill="#2a2a2a" stroke="#444444" stroke-width="1"/>
|
||||
|
||||
<!-- 编码器主体外壳 -->
|
||||
<circle cx="50" cy="60" r="32" fill="url(#encoderGradient)" stroke="#555555" stroke-width="1"/>
|
||||
|
||||
<!-- 编码器接线端子 -->
|
||||
<rect x="5" y="75" width="4" height="8" fill="#c9c9c9" rx="1"/>
|
||||
<rect x="15" y="85" width="4" height="8" fill="#c9c9c9" rx="1"/>
|
||||
<rect x="25" y="85" width="4" height="8" fill="#c9c9c9" rx="1"/>
|
||||
<rect x="81" y="85" width="4" height="8" fill="#c9c9c9" rx="1"/>
|
||||
<rect x="91" y="75" width="4" height="8" fill="#c9c9c9" rx="1"/>
|
||||
|
||||
<!-- 旋钮 -->
|
||||
<circle cx="50" cy="60" r="22"
|
||||
:fill="isPressed ? 'url(#knobPressedGradient)' : 'url(#knobGradient)'"
|
||||
stroke="#666666" stroke-width="1"
|
||||
:transform="`rotate(${rotation/2} 50 60)`"
|
||||
class="interactive"
|
||||
@mousedown="handleMouseDown"
|
||||
@mouseup="handlePress(false)"
|
||||
@mouseleave="handlePress(false)"/>
|
||||
|
||||
<!-- 旋钮指示器 -->
|
||||
<line x1="50" y1="42" x2="50" y2="48"
|
||||
stroke="#ffffff" stroke-width="2" stroke-linecap="round"
|
||||
:transform="`rotate(${rotation} 50 60)`"/>
|
||||
|
||||
<!-- 旋钮上的纹理刻度 -->
|
||||
<g :transform="`rotate(${rotation} 50 60)`">
|
||||
<circle cx="50" cy="60" r="18" fill="none" stroke="#777777" stroke-width="0.5"/>
|
||||
<!-- 刻度线 -->
|
||||
<g v-for="i in 16" :key="i">
|
||||
<line :x1="50 + 16 * Math.cos((i-1) * Math.PI / 8)"
|
||||
:y1="60 + 16 * Math.sin((i-1) * Math.PI / 8)"
|
||||
:x2="50 + 18 * Math.cos((i-1) * Math.PI / 8)"
|
||||
:y2="60 + 18 * Math.sin((i-1) * Math.PI / 8)"
|
||||
stroke="#999999" stroke-width="0.5"/>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
<!-- 编码器编号标签 -->
|
||||
<text x="50" y="15" text-anchor="middle" font-family="Arial" font-size="10"
|
||||
fill="#cccccc" font-weight="bold">
|
||||
EC11-{{ encoderNumber }}
|
||||
</text>
|
||||
|
||||
<!-- 状态指示器 -->
|
||||
<circle cx="85" cy="20" r="3" :fill="isPressed ? '#ff4444' : '#444444'"
|
||||
:filter="isPressed ? 'url(#glow)' : ''"
|
||||
stroke="#666666" stroke-width="0.5"/>
|
||||
</svg>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed } from 'vue';
|
||||
|
||||
interface Props {
|
||||
size?: number;
|
||||
encoderNumber?: number;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
size: 1,
|
||||
encoderNumber: 1
|
||||
});
|
||||
|
||||
// 组件状态
|
||||
const isPressed = ref(false);
|
||||
const rotation = ref(0);
|
||||
|
||||
// 拖动状态
|
||||
const isDragging = ref(false);
|
||||
const dragStartX = ref(0);
|
||||
const lastTriggerX = ref(0);
|
||||
const dragThreshold = 20; // 每20像素触发一次旋转
|
||||
const hasRotated = ref(false); // 标记是否已经发生了旋转
|
||||
|
||||
// 计算宽高
|
||||
const width = computed(() => 100 * props.size);
|
||||
const height = computed(() => 100 * props.size);
|
||||
|
||||
// 定义发出的事件
|
||||
const emit = defineEmits(['press', 'release', 'rotate-left', 'rotate-right']);
|
||||
|
||||
// 鼠标按下处理
|
||||
function handleMouseDown(event: MouseEvent) {
|
||||
isDragging.value = true;
|
||||
dragStartX.value = event.clientX;
|
||||
lastTriggerX.value = event.clientX;
|
||||
hasRotated.value = false; // 重置旋转标记
|
||||
|
||||
// 添加全局鼠标事件监听
|
||||
document.addEventListener('mousemove', handleMouseMove);
|
||||
document.addEventListener('mouseup', handleMouseUp);
|
||||
}
|
||||
|
||||
// 鼠标移动处理
|
||||
function handleMouseMove(event: MouseEvent) {
|
||||
if (!isDragging.value) return;
|
||||
|
||||
const currentX = event.clientX;
|
||||
const deltaX = currentX - lastTriggerX.value;
|
||||
|
||||
// 检查是否达到触发阈值
|
||||
if (Math.abs(deltaX) >= dragThreshold) {
|
||||
hasRotated.value = true; // 标记已经发生旋转
|
||||
|
||||
if (deltaX > 0) {
|
||||
// 右拖动 - 右旋转
|
||||
rotation.value += 15;
|
||||
emit('rotate-right', {
|
||||
encoderNumber: props.encoderNumber
|
||||
});
|
||||
} else {
|
||||
// 左拖动 - 左旋转
|
||||
rotation.value -= 15;
|
||||
emit('rotate-left', {
|
||||
encoderNumber: props.encoderNumber
|
||||
});
|
||||
}
|
||||
|
||||
// 更新最后触发位置
|
||||
lastTriggerX.value = currentX;
|
||||
|
||||
// 保持角度在0-360度范围内
|
||||
rotation.value = rotation.value % 720;
|
||||
if (rotation.value < 0) {
|
||||
rotation.value += 720;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 鼠标松开处理
|
||||
function handleMouseUp() {
|
||||
isDragging.value = false;
|
||||
|
||||
// 只有在没有发生旋转的情况下才识别为按压事件
|
||||
if (!hasRotated.value) {
|
||||
// 触发按压和释放事件(模拟快速按压)
|
||||
handlePress(true);
|
||||
// 使用setTimeout来模拟按压和释放的时序
|
||||
setTimeout(() => {
|
||||
handlePress(false);
|
||||
}, 100);
|
||||
}
|
||||
|
||||
// 移除全局事件监听
|
||||
document.removeEventListener('mousemove', handleMouseMove);
|
||||
document.removeEventListener('mouseup', handleMouseUp);
|
||||
}
|
||||
|
||||
// 按压处理
|
||||
function handlePress(pressed: boolean) {
|
||||
if (pressed !== isPressed.value) {
|
||||
isPressed.value = pressed;
|
||||
if (pressed) {
|
||||
emit('press', { encoderNumber: props.encoderNumber });
|
||||
} else {
|
||||
emit('release', { encoderNumber: props.encoderNumber });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 暴露组件方法
|
||||
defineExpose({
|
||||
press: () => handlePress(true),
|
||||
release: () => handlePress(false),
|
||||
rotateLeft: () => {
|
||||
rotation.value -= 15;
|
||||
emit('rotate-left', {
|
||||
encoderNumber: props.encoderNumber
|
||||
});
|
||||
},
|
||||
rotateRight: () => {
|
||||
rotation.value += 15;
|
||||
emit('rotate-right', {
|
||||
encoderNumber: props.encoderNumber
|
||||
});
|
||||
},
|
||||
isPressed: () => isPressed.value
|
||||
});
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
// 添加一个静态方法来获取默认props
|
||||
export function getDefaultProps() {
|
||||
return {
|
||||
size: 1,
|
||||
encoderNumber: 1
|
||||
};
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="postcss">
|
||||
.ec11-container {
|
||||
display: inline-block;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.ec11-encoder {
|
||||
display: block;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.interactive {
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user