S.规则设置逻辑和拆分更新,boxApi方法调用
This commit is contained in:
parent
1c0a51ed5e
commit
11ea95b902
|
@ -0,0 +1,365 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<!-- 打开弹窗按钮 -->
|
||||||
|
<!-- <el-button type="primary" @click="openDialog">打开设置弹窗</el-button> -->
|
||||||
|
|
||||||
|
<!-- 弹窗 -->
|
||||||
|
<!-- <el-dialog title="设置规则" v-model="dialogVisible" width="50%" @close="resetDialog"> -->
|
||||||
|
<el-dialog v-model="localVisible" width="50%" :modal="false" @close="handleClose">
|
||||||
|
<!-- 显示摄像头基本信息 -->
|
||||||
|
<div v-if="CameraDialog.id" class="camera-title">
|
||||||
|
<p><strong>摄像头名称:</strong>{{ CameraDialog.name }} <strong> 通道号: </strong>{{ CameraDialog.id }}
|
||||||
|
</p>
|
||||||
|
<!-- <p><strong>摄像头状态:</strong>{{ CameraDialog.status }}</p> -->
|
||||||
|
<!-- <img :src="CameraDialog.snapshot" alt="Snapshot" style="max-width: 100%; margin-bottom: 20px" /> -->
|
||||||
|
</div>
|
||||||
|
<el-radio-group v-model="CameraDialog.mode" @change="handleGlobalModeChange" style="margin-bottom: 20px">
|
||||||
|
<el-radio label="on">ON</el-radio>
|
||||||
|
<el-radio label="off">OFF</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
|
||||||
|
|
||||||
|
<div v-for="(rule, index) in CameraDialog.rules" :key="rule.id" class="rule-block">
|
||||||
|
<h3>{{ rule.name }} (模式: {{ rule.mode }})</h3>
|
||||||
|
<el-radio-group v-model="rule.mode" :disabled="CameraDialog.mode === 'off'" @change="handleRuleModeChange(rule)">
|
||||||
|
<el-radio label="on">ON</el-radio>
|
||||||
|
<el-radio label="off">OFF</el-radio>
|
||||||
|
<el-radio label="schedule">时间段</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
|
||||||
|
<div v-if="rule.mode === 'schedule'">
|
||||||
|
|
||||||
|
<el-select v-model="rule.schedule.type" placeholder="请选择" style="margin: 10px 0">
|
||||||
|
<el-option label="每日" value="daily"></el-option>
|
||||||
|
</el-select>
|
||||||
|
|
||||||
|
|
||||||
|
<div v-for="(time, timeIndex) in rule.schedule.time_slots" :key="timeIndex" class="time-period">
|
||||||
|
<el-time-picker v-model="rule.schedule.time_slots[timeIndex][0]" format="HH:mm" value-format="HH:mm"
|
||||||
|
placeholder="Start Time" @change="validateTime(rule.schedule.time_slots, timeIndex)" />
|
||||||
|
<el-time-picker v-model="rule.schedule.time_slots[timeIndex][1]" format="HH:mm" value-format="HH:mm"
|
||||||
|
placeholder="End Time" @change="validateTime(rule.schedule.time_slots, timeIndex)" />
|
||||||
|
<el-button type="danger" @click="removeTimeSlot(rule.schedule.time_slots, timeIndex)">删除</el-button>
|
||||||
|
|
||||||
|
|
||||||
|
<p v-if="!rule.schedule.time_slots[timeIndex][0] || !rule.schedule.time_slots[timeIndex][1]"
|
||||||
|
style="color: red; margin-top: 5px">
|
||||||
|
*
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-button type="primary" @click="addTimeSlot(rule.schedule.time_slots)">新增时间段</el-button>
|
||||||
|
<p v-if="!rule.schedule.time_slots.length" style="color: red; margin-top: 10px">
|
||||||
|
请至少添加一个时间段!
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 弹窗底部操作 -->
|
||||||
|
<span slot="footer" class="dialog-footer">
|
||||||
|
<el-button @click="handleClose">取消</el-button>
|
||||||
|
<el-button type="success" @click="saveRules">保存</el-button>
|
||||||
|
</span>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, watch, reactive } from "vue";
|
||||||
|
import { ElMessage } from "element-plus";
|
||||||
|
import { BoxApi } from "@/utils/boxApi.ts";
|
||||||
|
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
cameraData: Object, // 父组件传递过来的摄像头数据
|
||||||
|
visible: Boolean, // 父组件传递的弹窗状态
|
||||||
|
});
|
||||||
|
|
||||||
|
const emit = defineEmits(["update:visible", "save-result"]);
|
||||||
|
|
||||||
|
// API 实例
|
||||||
|
const apiInstance = new BoxApi();
|
||||||
|
|
||||||
|
const cameraDataZ = ref({});
|
||||||
|
const localVisible = ref(props.visible);
|
||||||
|
|
||||||
|
const cameraData = ref({}); // 原始摄像头数据
|
||||||
|
const CameraDialog = ref({}); // 镜像对象,用于格式化后的数据
|
||||||
|
|
||||||
|
// 弹窗控制
|
||||||
|
// const dialogVisible = ref(false);
|
||||||
|
|
||||||
|
// 打开弹窗
|
||||||
|
// const openDialog = async () => {
|
||||||
|
// try {
|
||||||
|
// const token = localStorage.getItem("alertToken");
|
||||||
|
// const cameraId = 1;
|
||||||
|
// const camera = await apiInstance.getCameraById(token, cameraId);
|
||||||
|
// cameraData.value = camera;
|
||||||
|
|
||||||
|
// console.log("摄像头数据:", cameraData.value);
|
||||||
|
|
||||||
|
// formatCameraData();
|
||||||
|
// dialogVisible.value = true;
|
||||||
|
// } catch (error) {
|
||||||
|
// console.error("获取摄像头规则失败:", error);
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.visible,
|
||||||
|
(newVal) => {
|
||||||
|
localVisible.value = newVal;
|
||||||
|
// console.log("newVal:", newVal);
|
||||||
|
if (newVal) {
|
||||||
|
// cameraDataZ.value = JSON.parse(JSON.stringify(newVal));
|
||||||
|
Object.assign(cameraDataZ.value, JSON.parse(JSON.stringify(props.cameraData)));
|
||||||
|
console.log("cameraDataZ:", cameraDataZ.value);
|
||||||
|
formatCameraData(cameraDataZ.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// watch(
|
||||||
|
// () => props.cameraData,
|
||||||
|
// (newVal) => {
|
||||||
|
// console.log("父组件传递的 cameraData:", newVal);
|
||||||
|
// },
|
||||||
|
// { immediate: true } // 立即触发以检查初始值
|
||||||
|
// );
|
||||||
|
|
||||||
|
// watch(
|
||||||
|
// () => props.visible,
|
||||||
|
// (newVal) => {
|
||||||
|
// console.log("父组件传递的 visible 状态:", newVal);
|
||||||
|
// },
|
||||||
|
// { immediate: true }
|
||||||
|
// );
|
||||||
|
|
||||||
|
const handleClose = () => {
|
||||||
|
emit("update:visible", false);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// 格式化 cameraData 到 CameraDialog
|
||||||
|
const formatCameraData = () => {
|
||||||
|
CameraDialog.value = {
|
||||||
|
id: cameraDataZ.value.id,
|
||||||
|
name: cameraDataZ.value.name,
|
||||||
|
status: cameraDataZ.value.status,
|
||||||
|
mode: cameraDataZ.value.mode,
|
||||||
|
// snapshot: cameraData.value.snapshot,
|
||||||
|
rules: cameraDataZ.value.rules.map((rule) => ({
|
||||||
|
id: rule.id,
|
||||||
|
name: rule.name,
|
||||||
|
mode: rule.mode,
|
||||||
|
schedule: {
|
||||||
|
type: rule.schedule?.type || "",
|
||||||
|
time_slots: rule.schedule?.time_slots
|
||||||
|
? rule.schedule.time_slots.map(([start, end]) => [
|
||||||
|
start !== undefined ? convertMinutesToTime(start) : "00:00",
|
||||||
|
end !== undefined ? convertMinutesToTime(end) : "00:00",
|
||||||
|
])
|
||||||
|
: [],
|
||||||
|
},
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleGlobalModeChange = () => {
|
||||||
|
if (CameraDialog.value.mode === "off") {
|
||||||
|
// 全局模式为 off,所有规则的 mode 变为 off
|
||||||
|
CameraDialog.value.rules.forEach((rule) => {
|
||||||
|
rule.mode = "off";
|
||||||
|
});
|
||||||
|
} else if (CameraDialog.value.mode === "on") {
|
||||||
|
// 全局模式为 on,规则保持原有状态
|
||||||
|
CameraDialog.value.rules.forEach((rule) => {
|
||||||
|
if (rule.mode === "schedule") {
|
||||||
|
rule.schedule.type = "daily"; // 自动设置为 daily
|
||||||
|
rule.schedule.time_slots = []; // 清空时间段
|
||||||
|
} else if (rule.mode === "on" || rule.mode === "off") {
|
||||||
|
rule.schedule.type = ""; // 清空 type
|
||||||
|
rule.schedule.time_slots = []; // 清空时间段
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleRuleModeChange = (rule) => {
|
||||||
|
if (rule.mode === "schedule") {
|
||||||
|
rule.schedule.type = "daily"; // 自动选择 daily
|
||||||
|
rule.schedule.time_slots = []; // 清空时间段
|
||||||
|
} else {
|
||||||
|
// 非 schedule 模式,清空 schedule 配置
|
||||||
|
rule.schedule.type = ""; // 清空 type
|
||||||
|
rule.schedule.time_slots = []; // 清空时间段
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const handleModeChange = (rule) => {
|
||||||
|
if (rule.mode === "schedule") {
|
||||||
|
rule.schedule.type = "daily";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// 验证时间段
|
||||||
|
const validateTime = (timeSlots, index) => {
|
||||||
|
const [startTime, endTime] = timeSlots[index];
|
||||||
|
|
||||||
|
if (!startTime || !endTime) {
|
||||||
|
ElMessage.error("请填写完整时间段");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (endTime <= startTime) {
|
||||||
|
ElMessage.error("开始时间必须小于结束时间");
|
||||||
|
timeSlots[index][1] = "00:00"; // 恢复为默认值
|
||||||
|
}
|
||||||
|
|
||||||
|
if (endTime && startTime >= endTime) {
|
||||||
|
ElMessage.error("开始时间和结束时间不合理");
|
||||||
|
timeSlots[index][1] = "";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < timeSlots.length; i++) {
|
||||||
|
if (
|
||||||
|
i !== index &&
|
||||||
|
timeSlots[i][0] &&
|
||||||
|
timeSlots[i][1] &&
|
||||||
|
startTime &&
|
||||||
|
endTime &&
|
||||||
|
!(
|
||||||
|
endTime <= timeSlots[i][0] ||
|
||||||
|
startTime >= timeSlots[i][1]
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
ElMessage.error("时间段重叠,请重新选择");
|
||||||
|
timeSlots[index][0] = "";
|
||||||
|
timeSlots[index][1] = "";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 添加时间段
|
||||||
|
const addTimeSlot = (timeSlots) => {
|
||||||
|
timeSlots.push([]);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 删除时间段
|
||||||
|
const removeTimeSlot = (timeSlots, index) => {
|
||||||
|
timeSlots.splice(index, 1);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 转换分钟数为时间字符串
|
||||||
|
const convertMinutesToTime = (minutes) => {
|
||||||
|
const hours = Math.floor(minutes / 60);
|
||||||
|
const mins = minutes % 60;
|
||||||
|
return `${String(hours).padStart(2, "0")}:${String(mins).padStart(2, "0")}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 转换时间为分钟数
|
||||||
|
const convertTimeToMinutes = (timeString) => {
|
||||||
|
const [hours, minutes] = timeString.split(":").map(Number);
|
||||||
|
return hours * 60 + minutes;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 保存规则并反向格式化
|
||||||
|
const saveRules = async() => {
|
||||||
|
let invalidSchedule = false;
|
||||||
|
|
||||||
|
// 遍历所有规则,校验 schedule 模式的完整性
|
||||||
|
CameraDialog.value.rules.forEach((rule) => {
|
||||||
|
if (rule.mode === "schedule") {
|
||||||
|
if (rule.schedule.type && rule.schedule.time_slots.length === 0) {
|
||||||
|
// 提示用户添加时间段
|
||||||
|
ElMessage.error(`规则 "${rule.name}" 的时间段不能为空,请添加至少一个时间段`);
|
||||||
|
invalidSchedule = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 如果校验失败,阻止保存
|
||||||
|
if (invalidSchedule) return;
|
||||||
|
|
||||||
|
// 构造 cameraUpdate 数据
|
||||||
|
const cameraUpdate = {
|
||||||
|
id: CameraDialog.value.id,
|
||||||
|
name: CameraDialog.value.name,
|
||||||
|
mode: CameraDialog.value.mode,
|
||||||
|
// rules: CameraDialog.value.rules.map((rule) => ({
|
||||||
|
// id: rule.id,
|
||||||
|
// name: rule.name,
|
||||||
|
// mode: rule.mode,
|
||||||
|
// })),
|
||||||
|
};
|
||||||
|
console.log("页面的cameraUpdate》》》》》》》》》》》:", cameraUpdate);
|
||||||
|
|
||||||
|
// 构造 ruleUpdate 数据
|
||||||
|
const ruleUpdate = CameraDialog.value.rules.map((rule) => ({
|
||||||
|
id: rule.id,
|
||||||
|
name: rule.name,
|
||||||
|
mode: CameraDialog.value.mode === "off" ? "off" : rule.mode,
|
||||||
|
schedule: rule.mode === "schedule" ? {
|
||||||
|
type: rule.schedule.type,
|
||||||
|
time_slots: rule.schedule.time_slots.map(([start, end]) => [
|
||||||
|
convertTimeToMinutes(start),
|
||||||
|
convertTimeToMinutes(end),
|
||||||
|
]),
|
||||||
|
} : {}, // 非 schedule 模式,schedule 为空对象
|
||||||
|
}));
|
||||||
|
|
||||||
|
try {
|
||||||
|
const token = localStorage.getItem('alertToken');
|
||||||
|
// 假设 API 分别处理 camera 和 rules 更新
|
||||||
|
await apiInstance.updateCamera(token, cameraUpdate.id, cameraUpdate); // 更新摄像头信息
|
||||||
|
await apiInstance.updateRule(token, ruleUpdate); // 更新规则信息
|
||||||
|
// ElMessage.success("规则保存成功");
|
||||||
|
emit("save-result", { success: true, message: "规则保存成功" });
|
||||||
|
emit("update:visible", false);
|
||||||
|
// dialogVisible.value = false;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("保存失败:", error);
|
||||||
|
// ElMessage.error("保存失败,请重试");
|
||||||
|
emit("save-result", { success: false, message: "规则保存失败,请重试" });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const closeDialog = () => {
|
||||||
|
dialogVisible.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.time-period {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rule-block {
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
padding: 20px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-footer {
|
||||||
|
/* text-align: right; */
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.camera-title{
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
Loading…
Reference in New Issue