refactor: 使用更简洁的方式进行认证

This commit is contained in:
2025-08-16 14:53:28 +08:00
parent c974de593a
commit e61cf96c07
24 changed files with 2118 additions and 2089 deletions

View File

@@ -1,17 +1,30 @@
// filepath: c:\_Project\FPGA_WebLab\FPGA_WebLab\src\components\equipments\Switch.vue
<template>
<svg
xmlns="http://www.w3.org/2000/svg"
:width="width"
:height="height"
<svg
xmlns="http://www.w3.org/2000/svg"
:width="width"
:height="height"
:viewBox="`4 6 ${props.switchCount + 2} 4`"
class="dip-switch"
>
<defs>
<filter id="glow" x="-50%" y="-50%" width="200%" height="200%">
<feFlood result="flood" flood-color="#f08a5d" flood-opacity="1"></feFlood>
<feComposite in="flood" result="mask" in2="SourceGraphic" operator="in"></feComposite>
<feMorphology in="mask" result="dilated" operator="dilate" radius="0.02"></feMorphology>
<feFlood
result="flood"
flood-color="#f08a5d"
flood-opacity="1"
></feFlood>
<feComposite
in="flood"
result="mask"
in2="SourceGraphic"
operator="in"
></feComposite>
<feMorphology
in="mask"
result="dilated"
operator="dilate"
radius="0.02"
></feMorphology>
<feGaussianBlur in="dilated" stdDeviation="0.05" result="blur1" />
<feGaussianBlur in="dilated" stdDeviation="0.1" result="blur2" />
<feGaussianBlur in="dilated" stdDeviation="0.2" result="blur3" />
@@ -23,29 +36,41 @@
</feMerge>
</filter>
</defs>
<g>
<!-- 红色背景随开关数量变化宽度 -->
<rect :width="props.switchCount + 2" height="4" x="4" y="6" fill="#c01401" rx="0.1" />
<text v-if="props.showLabels" fill="white" font-size="0.7" x="4.25" y="6.75">ON</text>
<rect
:width="props.switchCount + 2"
height="4"
x="4"
y="6"
fill="#c01401"
rx="0.1"
/>
<text
v-if="props.showLabels"
fill="white"
font-size="0.7"
x="4.25"
y="6.75"
>
ON
</text>
<g>
<template v-for="(_, index) in Array(props.switchCount)" :key="index">
<rect
class="glow interactive"
@click="toggleBtnStatus(index)"
width="0.7"
height="2"
fill="#68716f"
:x="5.15 + index"
y="7"
rx="0.1"
<rect
class="glow interactive"
@click="toggleBtnStatus(index)"
width="0.7"
height="2"
fill="#68716f"
:x="5.15 + index"
y="7"
rx="0.1"
/>
<text
<text
v-if="props.showLabels"
:x="5.5 + index"
y="9.5"
font-size="0.4"
:x="5.5 + index"
y="9.5"
font-size="0.4"
text-anchor="middle"
fill="#444"
>
@@ -53,19 +78,21 @@
</text>
</template>
</g>
<g>
<template v-for="(location, index) in btnLocation" :key="`btn-${index}`">
<rect
<template
v-for="(location, index) in btnLocation"
:key="`btn-${index}`"
>
<rect
class="interactive"
@click="toggleBtnStatus(index)"
width="0.65"
height="0.65"
fill="white"
:x="5.175 + index"
:y="location"
@click="toggleBtnStatus(index)"
width="0.65"
height="0.65"
fill="white"
:x="5.175 + index"
:y="location"
rx="0.1"
opacity="1"
opacity="1"
/>
</template>
</g>
@@ -74,118 +101,99 @@
</template>
<script lang="ts" setup>
import { computed, ref, watch } from "vue";
import { ref, computed, watch, onMounted } from "vue";
interface Props {
size?: number;
switchCount?: number;
// 新增属性
initialValues?: boolean[] | string; // 开关的初始状态,可以是布尔数组或逗号分隔的字符串
showLabels?: boolean; // 是否显示标签
initialValues?: boolean[] | string;
showLabels?: boolean;
}
const props = withDefaults(defineProps<Props>(), {
size: 1,
switchCount: 6,
initialValues: () => [],
showLabels: true
showLabels: true,
});
const emit = defineEmits(["change"]);
// 计算实际宽高
const width = computed(() => {
// 每个开关占用25px宽度再加上两侧边距(20px)
return (props.switchCount * 25 + 20) * props.size;
});
const height = computed(() => 85 * props.size); // 高度保持固定比例
// 定义发出的事件
const emit = defineEmits(['change', 'switch-toggle']);
// 解析初始值,支持字符串和数组两种格式
const parseInitialValues = () => {
// 解析初始值
function parseInitialValues(): boolean[] {
if (Array.isArray(props.initialValues)) {
return [...props.initialValues].slice(0, props.switchCount);
} else if (typeof props.initialValues === 'string' && props.initialValues.trim() !== '') {
// 将逗号分隔的字符串转换为布尔数组
const values = props.initialValues.split(',')
.map(val => val.trim() === '1' || val.trim().toLowerCase() === 'true')
.slice(0, props.switchCount);
// 如果数组长度小于开关数量,用 false 填充
while (values.length < props.switchCount) {
values.push(false);
}
return values;
}
// 默认返回全部为 false 的数组
if (
typeof props.initialValues === "string" &&
props.initialValues.trim() !== ""
) {
const arr = props.initialValues
.split(",")
.map((val) => val.trim() === "1" || val.trim().toLowerCase() === "true");
while (arr.length < props.switchCount) arr.push(false);
return arr.slice(0, props.switchCount);
}
return Array(props.switchCount).fill(false);
};
}
// 初始化按钮状态
const btnStatus = ref(parseInitialValues());
// 状态唯一真相
const btnStatus = ref<boolean[]>(parseInitialValues());
// 监听 switchCount 变化,调整开关状态数组
watch(() => props.switchCount, (newCount) => {
if (newCount !== btnStatus.value.length) {
// 如果新数量大于当前数量,则扩展数组
if (newCount > btnStatus.value.length) {
btnStatus.value = [
...btnStatus.value,
...Array(newCount - btnStatus.value.length).fill(false)
];
} else {
// 如果新数量小于当前数量,则截断数组
btnStatus.value = btnStatus.value.slice(0, newCount);
}
}
}, { immediate: true });
// 计算宽高
const width = computed(() => (props.switchCount * 25 + 20) * props.size);
const height = computed(() => 85 * props.size);
// 监听 initialValues 变化,更新开关状态
watch(() => props.initialValues, () => {
btnStatus.value = parseInitialValues();
});
// 按钮位置
const btnLocation = computed(() =>
btnStatus.value.map((status) => (status ? 7.025 : 8.325)),
);
const btnLocation = computed(() => {
return btnStatus.value.map((status) => {
return status ? 7.025 : 8.325;
// 状态变更统一处理
function updateStatus(newStates: boolean[], index?: number) {
btnStatus.value = newStates.slice(0, props.switchCount);
SwitchClient.setStates(btnStatus.value); // 同步后端
emit("change", {
index,
value: index !== undefined ? btnStatus.value[index] : undefined,
states: [...btnStatus.value],
});
});
function setBtnStatus(btnNum: number, isOn: boolean): void {
if (btnNum >= 0 && btnNum < btnStatus.value.length) {
btnStatus.value[btnNum] = isOn;
emit('change', { index: btnNum, value: isOn, states: [...btnStatus.value] });
}
}
function toggleBtnStatus(btnNum: number): void {
if (btnNum >= 0 && btnNum < btnStatus.value.length) {
btnStatus.value[btnNum] = !btnStatus.value[btnNum];
emit('switch-toggle', {
index: btnNum,
value: btnStatus.value[btnNum],
states: [...btnStatus.value]
});
}
// 切换单个
function toggleBtnStatus(idx: number) {
if (idx < 0 || idx >= btnStatus.value.length) return;
const newStates = [...btnStatus.value];
newStates[idx] = !newStates[idx];
updateStatus(newStates, idx);
}
// 一次性设置所有开关状态
function setAllStates(states: boolean[]): void {
const newStates = states.slice(0, props.switchCount);
while (newStates.length < props.switchCount) {
newStates.push(false);
}
btnStatus.value = newStates;
emit('change', { states: [...btnStatus.value] });
// 一次性设置全部
function setAllStates(states: boolean[]) {
updateStatus(states);
}
// 暴露组件方法和状态
defineExpose({
setBtnStatus,
toggleBtnStatus,
setAllStates,
getBtnStatus: () => [...btnStatus.value]
// 单个设置
function setBtnStatus(idx: number, isOn: boolean) {
if (idx < 0 || idx >= btnStatus.value.length) return;
const newStates = [...btnStatus.value];
newStates[idx] = isOn;
updateStatus(newStates, idx);
}
// 监听 props 变化只同步一次
watch(
() => [props.switchCount, props.initialValues],
() => {
btnStatus.value = parseInitialValues();
SwitchClient.setStates(btnStatus.value);
},
);
// 监听后端推送
onMounted(() => {
SwitchClient.onStateChange((states: boolean[]) => {
btnStatus.value = states.slice(0, props.switchCount);
});
});
</script>
@@ -194,16 +202,14 @@ defineExpose({
display: block;
padding: 0;
margin: 0;
line-height: 0; /* 移除行高导致的额外间距 */
font-size: 0; /* 防止文本节点造成的间距 */
line-height: 0;
font-size: 0;
box-sizing: content-box;
overflow: visible;
}
rect {
transition: all 100ms ease-in-out;
}
.interactive {
cursor: pointer;
}