Merge branch 'dunhuang-dev' of http://192.168.34.160:8023/admin/zhgdyun into dunhuang-cs
This commit is contained in:
commit
678f084a34
@ -113,12 +113,13 @@ import {
|
|||||||
getHikPtzControlApi,
|
getHikPtzControlApi,
|
||||||
setHikPtzControlApi,
|
setHikPtzControlApi,
|
||||||
} from "@/assets/js/api/equipmentCenter/cameraList";
|
} from "@/assets/js/api/equipmentCenter/cameraList";
|
||||||
|
import { Message } from "element-ui";
|
||||||
export default {
|
export default {
|
||||||
name: "DraggableGauge",
|
name: 'DraggableGauge',
|
||||||
props: {
|
props: {
|
||||||
value: {
|
value: {
|
||||||
type: Number,
|
type: Number,
|
||||||
default: 50,
|
default: 10,
|
||||||
},
|
},
|
||||||
size: {
|
size: {
|
||||||
type: Number,
|
type: Number,
|
||||||
@ -160,7 +161,6 @@ export default {
|
|||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
currentValue: this.value,
|
currentValue: this.value,
|
||||||
canvas: null,
|
|
||||||
ctx: null,
|
ctx: null,
|
||||||
isDragging: false,
|
isDragging: false,
|
||||||
controlInfo: {
|
controlInfo: {
|
||||||
@ -168,55 +168,25 @@ export default {
|
|||||||
opType: "",
|
opType: "",
|
||||||
},
|
},
|
||||||
videoInfo: {},
|
videoInfo: {},
|
||||||
|
// 常量定义
|
||||||
|
DEVICE_TYPE_BALL: 2, // 球机设备类型
|
||||||
|
VIDEO_TYPE_ISC: 3, // ISC视频类型
|
||||||
|
SUCCESS_CODE: 200, // 成功状态码
|
||||||
|
GAP_ANGLE: 90, // 仪表盘缺口角度
|
||||||
|
INDICATOR_COLOR: "#2758C0", // 指示器颜色
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
mounted() {
|
|
||||||
this.initCanvas();
|
|
||||||
this.drawGauge();
|
|
||||||
this.addEventListeners();
|
|
||||||
},
|
|
||||||
beforeDestroy() {
|
|
||||||
this.removeEventListeners();
|
|
||||||
},
|
|
||||||
methods: {
|
methods: {
|
||||||
controlVideoMouse(opType, action) {
|
|
||||||
if (this.videoInfo.deviceType != 2) {
|
|
||||||
this.$message.warning("该设备不是球机,不支持此操作");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
this.controlInfo.opType = opType;
|
|
||||||
if (action == 0) {
|
|
||||||
this.controlInfo.videoFlag = true;
|
|
||||||
}
|
|
||||||
const json = {
|
|
||||||
itemId: this.videoInfo.itemId,
|
|
||||||
opType: opType,
|
|
||||||
action: action,
|
|
||||||
opSize: this.currentValue,
|
|
||||||
opCode: 1,
|
|
||||||
};
|
|
||||||
setHikPtzControlApi(json)
|
|
||||||
.then((res) => {
|
|
||||||
if (res.code == 200) {
|
|
||||||
// Message.success("控制成功");
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
if (action == 1) {
|
|
||||||
this.controlInfo.videoFlag = false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
initCanvas() {
|
initCanvas() {
|
||||||
this.canvas = this.$refs.canvas;
|
const canvas = this.$refs.canvas;
|
||||||
this.ctx = this.canvas.getContext("2d");
|
if (!canvas) return;
|
||||||
|
this.ctx = canvas.getContext("2d");
|
||||||
|
|
||||||
// 设置canvas尺寸
|
|
||||||
const dpr = window.devicePixelRatio || 1;
|
const dpr = window.devicePixelRatio || 1;
|
||||||
this.canvas.width = this.size * dpr;
|
canvas.width = this.size * dpr;
|
||||||
this.canvas.height = this.size * dpr;
|
canvas.height = this.size * dpr;
|
||||||
this.canvas.style.width = `${this.size}px`;
|
canvas.style.width = `${this.size}px`;
|
||||||
this.canvas.style.height = `${this.size}px`;
|
canvas.style.height = `${this.size}px`;
|
||||||
this.ctx.scale(dpr, dpr);
|
this.ctx.scale(dpr, dpr);
|
||||||
},
|
},
|
||||||
drawGauge() {
|
drawGauge() {
|
||||||
@ -225,15 +195,13 @@ export default {
|
|||||||
const center = this.size / 2;
|
const center = this.size / 2;
|
||||||
const radius = center - this.lineWidth / 2;
|
const radius = center - this.lineWidth / 2;
|
||||||
const startAngle = Math.PI / 2 + Math.PI / 4;
|
const startAngle = Math.PI / 2 + Math.PI / 4;
|
||||||
const endAngle =
|
const endAngle = startAngle + ((2 * Math.PI - Math.PI / 2) * this.currentValue) / 100;
|
||||||
startAngle + ((2 * Math.PI - Math.PI / 2) * this.currentValue) / 100;
|
|
||||||
|
|
||||||
// 清除画布
|
|
||||||
this.ctx.clearRect(0, 0, this.size, this.size);
|
this.ctx.clearRect(0, 0, this.size, this.size);
|
||||||
|
|
||||||
// 绘制背景圆(虚线)
|
// 绘制背景圆
|
||||||
this.ctx.beginPath();
|
this.ctx.beginPath();
|
||||||
this.ctx.setLineDash([5, 3]); // 设置虚线样式:5px实线,3px空白
|
this.ctx.setLineDash([5, 3]);
|
||||||
this.ctx.arc(
|
this.ctx.arc(
|
||||||
center,
|
center,
|
||||||
center,
|
center,
|
||||||
@ -246,25 +214,15 @@ export default {
|
|||||||
this.ctx.lineWidth = this.lineWidth;
|
this.ctx.lineWidth = this.lineWidth;
|
||||||
this.ctx.stroke();
|
this.ctx.stroke();
|
||||||
|
|
||||||
// 绘制进度圆(虚线)
|
// 绘制进度圆
|
||||||
this.ctx.beginPath();
|
this.ctx.beginPath();
|
||||||
this.ctx.setLineDash([5, 3]); // 设置虚线样式:5px实线,3px空白
|
this.ctx.setLineDash([5, 3]);
|
||||||
this.ctx.arc(center, center, radius, startAngle, endAngle);
|
this.ctx.arc(center, center, radius, startAngle, endAngle);
|
||||||
this.ctx.strokeStyle = this.progressColor;
|
this.ctx.strokeStyle = this.progressColor;
|
||||||
this.ctx.lineWidth = this.lineWidth;
|
this.ctx.lineWidth = this.lineWidth;
|
||||||
this.ctx.stroke();
|
this.ctx.stroke();
|
||||||
|
|
||||||
// // 绘制指示器
|
// 绘制指示器
|
||||||
// const indicatorRadius = this.lineWidth * 1.5;
|
|
||||||
// const indicatorX = center + radius * Math.cos(endAngle);
|
|
||||||
// const indicatorY = center + radius * Math.sin(endAngle);
|
|
||||||
|
|
||||||
// this.ctx.beginPath();
|
|
||||||
// this.ctx.setLineDash([]); // 重置为实线
|
|
||||||
// this.ctx.arc(indicatorX, indicatorY, indicatorRadius, 0, 2 * Math.PI);
|
|
||||||
// this.ctx.fillStyle = this.progressColor;
|
|
||||||
// this.ctx.fill();
|
|
||||||
// 绘制指示器(长方形)
|
|
||||||
const indicatorWidth = this.lineWidth;
|
const indicatorWidth = this.lineWidth;
|
||||||
const indicatorHeight = this.lineWidth * 3;
|
const indicatorHeight = this.lineWidth * 3;
|
||||||
const indicatorX = center + radius * Math.cos(endAngle);
|
const indicatorX = center + radius * Math.cos(endAngle);
|
||||||
@ -272,7 +230,7 @@ export default {
|
|||||||
|
|
||||||
this.ctx.save();
|
this.ctx.save();
|
||||||
this.ctx.translate(indicatorX, indicatorY);
|
this.ctx.translate(indicatorX, indicatorY);
|
||||||
this.ctx.rotate(endAngle + Math.PI / 2); // 使长方形朝向切线方向
|
this.ctx.rotate(endAngle + Math.PI / 2);
|
||||||
|
|
||||||
this.ctx.beginPath();
|
this.ctx.beginPath();
|
||||||
this.ctx.setLineDash([]);
|
this.ctx.setLineDash([]);
|
||||||
@ -282,22 +240,63 @@ export default {
|
|||||||
indicatorWidth,
|
indicatorWidth,
|
||||||
indicatorHeight
|
indicatorHeight
|
||||||
);
|
);
|
||||||
this.ctx.fillStyle = "#2758C0";
|
this.ctx.fillStyle = this.INDICATOR_COLOR;
|
||||||
this.ctx.fill();
|
this.ctx.fill();
|
||||||
|
|
||||||
this.ctx.restore();
|
this.ctx.restore();
|
||||||
},
|
},
|
||||||
addEventListeners() {
|
controlVideoMouse(opType, action) {
|
||||||
this.canvas.addEventListener("mousedown", this.startDrag);
|
if (this.videoInfo.deviceType !== this.DEVICE_TYPE_BALL) {
|
||||||
this.canvas.addEventListener("touchstart", this.startDrag);
|
Message.warning("该设备不是球机,不支持此操作");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
this.controlInfo.opType = opType;
|
||||||
|
if (action === 0) {
|
||||||
|
this.controlInfo.videoFlag = true;
|
||||||
|
}
|
||||||
|
const json = {
|
||||||
|
itemId: this.videoInfo.itemId,
|
||||||
|
opType: opType,
|
||||||
|
action: action,
|
||||||
|
opSize: this.currentValue,
|
||||||
|
opCode: 1,
|
||||||
|
};
|
||||||
|
setHikPtzControlApi(json)
|
||||||
|
.then((res) => {
|
||||||
|
if (res.code === this.SUCCESS_CODE) {
|
||||||
|
// 控制成功,静默处理
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error("视频控制失败:", error);
|
||||||
|
Message.error("视频控制失败,请稍后重试");
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
if (action === 1) {
|
||||||
|
this.controlInfo.videoFlag = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
},
|
},
|
||||||
removeEventListeners() {
|
controlVideoFn_isc(opType) {
|
||||||
this.canvas.removeEventListener("mousedown", this.startDrag);
|
const json = {
|
||||||
this.canvas.removeEventListener("touchstart", this.startDrag);
|
itemId: this.videoInfo.itemId,
|
||||||
document.removeEventListener("mousemove", this.handleDrag);
|
opType: opType,
|
||||||
document.removeEventListener("mouseup", this.stopDrag);
|
opSize: this.currentValue,
|
||||||
document.removeEventListener("touchmove", this.handleDrag);
|
opCode: 1,
|
||||||
document.removeEventListener("touchend", this.stopDrag);
|
};
|
||||||
|
getHikPtzControlApi(json)
|
||||||
|
.then((res) => {
|
||||||
|
if (res.code === this.SUCCESS_CODE) {
|
||||||
|
Message.success("控制成功");
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error("ISC视频控制失败:", error);
|
||||||
|
Message.error("视频控制失败,请稍后重试");
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.controlInfo.videoFlag = false;
|
||||||
|
});
|
||||||
},
|
},
|
||||||
startDrag(e) {
|
startDrag(e) {
|
||||||
this.isDragging = true;
|
this.isDragging = true;
|
||||||
@ -305,93 +304,103 @@ export default {
|
|||||||
document.addEventListener("mouseup", this.stopDrag);
|
document.addEventListener("mouseup", this.stopDrag);
|
||||||
document.addEventListener("touchmove", this.handleDrag);
|
document.addEventListener("touchmove", this.handleDrag);
|
||||||
document.addEventListener("touchend", this.stopDrag);
|
document.addEventListener("touchend", this.stopDrag);
|
||||||
this.handleDrag(e); // 立即更新位置
|
this.handleDrag(e);
|
||||||
},
|
},
|
||||||
handleDrag(e) {
|
handleDrag(e) {
|
||||||
if (!this.isDragging) return;
|
if (!this.isDragging || !this.$refs.canvas) return;
|
||||||
|
|
||||||
const rect = this.canvas.getBoundingClientRect();
|
const rect = this.$refs.canvas.getBoundingClientRect();
|
||||||
const clientX = e.clientX || e.touches[0].clientX;
|
// 安全地获取触摸或鼠标坐标
|
||||||
const clientY = e.clientY || e.touches[0].clientY;
|
const clientX = e.clientX !== undefined ? e.clientX : (e.touches && e.touches[0] && e.touches[0].clientX);
|
||||||
|
const clientY = e.clientY !== undefined ? e.clientY : (e.touches && e.touches[0] && e.touches[0].clientY);
|
||||||
|
|
||||||
|
if (clientX === undefined || clientY === undefined) return;
|
||||||
|
|
||||||
// 计算相对于canvas中心的坐标
|
|
||||||
const centerX = rect.left + rect.width / 2;
|
const centerX = rect.left + rect.width / 2;
|
||||||
const centerY = rect.top + rect.height / 2;
|
const centerY = rect.top + rect.height / 2;
|
||||||
const x = clientX - centerX;
|
const x = clientX - centerX;
|
||||||
const y = clientY - centerY;
|
const y = clientY - centerY;
|
||||||
|
|
||||||
// 计算角度 (0-360度),从底部开始计算
|
// 计算鼠标相对于圆心的角度(弧度)
|
||||||
let angle = Math.atan2(y, x) * (180 / Math.PI);
|
let angle = Math.atan2(y, x);
|
||||||
angle = (angle + 180) % 360; // 调整为从底部开始
|
|
||||||
|
// 仪表盘起始角度:Math.PI / 2 + Math.PI / 4 = 135° (从右上角开始)
|
||||||
|
const startAngle = Math.PI / 2 + Math.PI / 4;
|
||||||
|
// 仪表盘结束角度:135° + 270° = 405° = 45° (跨越0°)
|
||||||
|
const endAngle = startAngle + (2 * Math.PI - Math.PI / 2);
|
||||||
|
// 仪表盘有效角度范围:270° (360° - 90°缺口)
|
||||||
|
const usableAngle = 2 * Math.PI - Math.PI / 2;
|
||||||
|
|
||||||
|
// 将角度转换为 0-2π 范围
|
||||||
|
if (angle < 0) {
|
||||||
|
angle += 2 * Math.PI;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算相对于起始角度的偏移角度
|
||||||
|
let relativeAngle;
|
||||||
|
|
||||||
|
// 计算结束角度(在0-2π范围内)
|
||||||
|
const endAngleNormalized = endAngle % (2 * Math.PI); // 45° = π/4
|
||||||
|
|
||||||
|
// 判断角度是否在有效范围内
|
||||||
|
// 情况1:角度在 startAngle 到 2π 之间(135° 到 360°)
|
||||||
|
if (angle >= startAngle && angle <= 2 * Math.PI) {
|
||||||
|
relativeAngle = angle - startAngle;
|
||||||
|
// 如果超出有效范围,限制在边界
|
||||||
|
if (relativeAngle > usableAngle) {
|
||||||
|
relativeAngle = usableAngle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 情况2:角度在 0 到 endAngleNormalized 之间(0° 到 45°),即跨越0°的情况
|
||||||
|
else if (angle >= 0 && angle <= endAngleNormalized) {
|
||||||
|
// 角度跨越了0°,需要加上2π再计算
|
||||||
|
relativeAngle = (angle + 2 * Math.PI) - startAngle;
|
||||||
|
// 确保不超过有效范围
|
||||||
|
if (relativeAngle > usableAngle) {
|
||||||
|
relativeAngle = usableAngle;
|
||||||
|
}
|
||||||
|
// 确保不小于0(防止计算错误)
|
||||||
|
if (relativeAngle < 0) {
|
||||||
|
relativeAngle = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 情况3:角度在无效范围内(45° 到 135°之间,即缺口部分)
|
||||||
|
else {
|
||||||
|
// 如果角度在缺口范围内,限制到最近的边界
|
||||||
|
// 判断更接近起始还是结束
|
||||||
|
const distToStart = Math.abs(angle - startAngle);
|
||||||
|
const distToEnd = Math.min(
|
||||||
|
Math.abs(angle - endAngleNormalized),
|
||||||
|
Math.abs(angle + 2 * Math.PI - endAngleNormalized)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (distToStart < distToEnd) {
|
||||||
|
relativeAngle = 0; // 限制到起始位置(0%)
|
||||||
|
} else {
|
||||||
|
relativeAngle = usableAngle; // 限制到结束位置(100%)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将角度转换为 0-100 的值
|
||||||
|
const value = Math.round((relativeAngle / usableAngle) * 100);
|
||||||
|
this.currentValue = Math.min(this.max, Math.max(this.min, value));
|
||||||
|
|
||||||
// 转换为0-100的值,考虑90度缺口部分
|
|
||||||
const gapAngle = 90; // 缺口角度
|
|
||||||
const usableAngle = 360 - gapAngle;
|
|
||||||
let value = Math.round((angle * 100) / usableAngle);
|
|
||||||
if (angle > usableAngle) value = 100; // 超过可用角度时设为最大值
|
|
||||||
this.currentValue = Math.min(100, Math.max(0, value));
|
|
||||||
|
|
||||||
// 立即重绘指示器位置
|
|
||||||
this.drawGauge();
|
this.drawGauge();
|
||||||
|
|
||||||
this.$emit("input", this.currentValue);
|
this.$emit("input", this.currentValue);
|
||||||
this.$emit("change", this.currentValue);
|
this.$emit("change", this.currentValue);
|
||||||
|
|
||||||
if (e.cancelable && e.type.includes("touch")) {
|
if (e.cancelable && e.type && e.type.includes("touch")) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
stopDrag() {
|
stopDrag() {
|
||||||
|
if (!this.isDragging) return;
|
||||||
this.isDragging = false;
|
this.isDragging = false;
|
||||||
document.removeEventListener("mousemove", this.handleDrag);
|
document.removeEventListener("mousemove", this.handleDrag);
|
||||||
document.removeEventListener("mouseup", this.stopDrag);
|
document.removeEventListener("mouseup", this.stopDrag);
|
||||||
document.removeEventListener("touchmove", this.handleDrag);
|
document.removeEventListener("touchmove", this.handleDrag);
|
||||||
document.removeEventListener("touchend", this.stopDrag);
|
document.removeEventListener("touchend", this.stopDrag);
|
||||||
},
|
}
|
||||||
controlVideoFn(pan, tilt, zoom, opType) {
|
|
||||||
this.controlInfo.opType = opType;
|
|
||||||
if (this.controlInfo.videoFlag) {
|
|
||||||
this.$message.warning("不要重复点击");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (this.videoInfo.deviceType != 2) {
|
|
||||||
this.$message.warning("该设备不是球机,不支持此操作");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
switch (this.videoInfo.videoType) {
|
|
||||||
case 3:
|
|
||||||
this.controlInfo.videoFlag = true;
|
|
||||||
this.controlVideoFn_isc(opType);
|
|
||||||
break;
|
|
||||||
// case 4:
|
|
||||||
// this.controlInfo.videoFlag = true;
|
|
||||||
// this.controlVideoFn_dh(pan, tilt, zoom)
|
|
||||||
// break;
|
|
||||||
default:
|
|
||||||
this.$message.warning("暂不支持");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
controlVideoFn_isc(opType) {
|
|
||||||
let json = {
|
|
||||||
// cameraId: this.videoInfo.deviceSerial,
|
|
||||||
itemId: this.videoInfo.itemId,
|
|
||||||
opType: opType,
|
|
||||||
opSize: this.currentValue,
|
|
||||||
opCode: 1,
|
|
||||||
};
|
|
||||||
getHikPtzControlApi({
|
|
||||||
...json,
|
|
||||||
})
|
|
||||||
.then((res) => {
|
|
||||||
if (res.code == 200) {
|
|
||||||
this.$message.success("控制成功");
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
this.controlInfo.videoFlag = false;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
value(newVal) {
|
value(newVal) {
|
||||||
@ -403,16 +412,40 @@ export default {
|
|||||||
this.videoInfo = newVal;
|
this.videoInfo = newVal;
|
||||||
},
|
},
|
||||||
deep: true,
|
deep: true,
|
||||||
immediate: true,
|
immediate: true
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
|
mounted() {
|
||||||
|
if (!this.$refs.canvas) return;
|
||||||
|
this.initCanvas();
|
||||||
|
this.drawGauge();
|
||||||
|
this.$refs.canvas.addEventListener("mousedown", this.startDrag);
|
||||||
|
this.$refs.canvas.addEventListener("touchstart", this.startDrag);
|
||||||
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
// 确保停止拖拽状态
|
||||||
|
if (this.isDragging) {
|
||||||
|
this.stopDrag();
|
||||||
|
}
|
||||||
|
// 清理canvas事件监听器
|
||||||
|
if (this.$refs.canvas) {
|
||||||
|
this.$refs.canvas.removeEventListener("mousedown", this.startDrag);
|
||||||
|
this.$refs.canvas.removeEventListener("touchstart", this.startDrag);
|
||||||
|
}
|
||||||
|
// 清理document事件监听器(防止内存泄漏)
|
||||||
|
document.removeEventListener("mousemove", this.handleDrag);
|
||||||
|
document.removeEventListener("mouseup", this.stopDrag);
|
||||||
|
document.removeEventListener("touchmove", this.handleDrag);
|
||||||
|
document.removeEventListener("touchend", this.stopDrag);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
<style scoped lang="less">
|
<style scoped lang="less">
|
||||||
.gauge-canvas {
|
.gauge-canvas {
|
||||||
margin-bottom: 28px;
|
margin-bottom: 28px;
|
||||||
position: absolute;
|
position: absolute
|
||||||
}
|
}
|
||||||
#content {
|
#content {
|
||||||
margin: auto;
|
margin: auto;
|
||||||
|
|||||||
@ -111,7 +111,6 @@ import {
|
|||||||
setHikPtzControlApi,
|
setHikPtzControlApi,
|
||||||
} from "@/assets/js/api/equipmentCenter/cameraList";
|
} from "@/assets/js/api/equipmentCenter/cameraList";
|
||||||
import { Message } from "element-ui";
|
import { Message } from "element-ui";
|
||||||
import { active } from "sortablejs";
|
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
value: {
|
value: {
|
||||||
@ -168,9 +167,16 @@ const controlInfo = ref({
|
|||||||
});
|
});
|
||||||
const videoInfo = ref({});
|
const videoInfo = ref({});
|
||||||
|
|
||||||
|
// 常量定义
|
||||||
|
const DEVICE_TYPE_BALL = 2; // 球机设备类型
|
||||||
|
const VIDEO_TYPE_ISC = 3; // ISC视频类型
|
||||||
|
const SUCCESS_CODE = 200; // 成功状态码
|
||||||
|
const GAP_ANGLE = 90; // 仪表盘缺口角度
|
||||||
|
const INDICATOR_COLOR = "#2758C0"; // 指示器颜色
|
||||||
|
|
||||||
// 方法定义
|
// 方法定义
|
||||||
const initCanvas = () => {
|
const initCanvas = () => {
|
||||||
// canvas.value = canvas.value;
|
if (!canvas.value) return;
|
||||||
ctx.value = canvas.value.getContext("2d");
|
ctx.value = canvas.value.getContext("2d");
|
||||||
|
|
||||||
const dpr = window.devicePixelRatio || 1;
|
const dpr = window.devicePixelRatio || 1;
|
||||||
@ -233,18 +239,18 @@ const drawGauge = () => {
|
|||||||
indicatorWidth,
|
indicatorWidth,
|
||||||
indicatorHeight
|
indicatorHeight
|
||||||
);
|
);
|
||||||
ctx.value.fillStyle = "#2758C0";
|
ctx.value.fillStyle = INDICATOR_COLOR;
|
||||||
ctx.value.fill();
|
ctx.value.fill();
|
||||||
|
|
||||||
ctx.value.restore();
|
ctx.value.restore();
|
||||||
};
|
};
|
||||||
const controlVideoMouse = (opType, action) => {
|
const controlVideoMouse = (opType, action) => {
|
||||||
if (videoInfo.value.deviceType != 2) {
|
if (videoInfo.value.deviceType !== DEVICE_TYPE_BALL) {
|
||||||
Message.warning("该设备不是球机,不支持此操作");
|
Message.warning("该设备不是球机,不支持此操作");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
controlInfo.value.opType = opType;
|
controlInfo.value.opType = opType;
|
||||||
if (action == 0) {
|
if (action === 0) {
|
||||||
controlInfo.value.videoFlag = true;
|
controlInfo.value.videoFlag = true;
|
||||||
}
|
}
|
||||||
const json = {
|
const json = {
|
||||||
@ -256,36 +262,41 @@ const controlVideoMouse = (opType, action) => {
|
|||||||
};
|
};
|
||||||
setHikPtzControlApi(json)
|
setHikPtzControlApi(json)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
if (res.code == 200) {
|
if (res.code === SUCCESS_CODE) {
|
||||||
// Message.success("控制成功");
|
// 控制成功,静默处理
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error("视频控制失败:", error);
|
||||||
|
Message.error("视频控制失败,请稍后重试");
|
||||||
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
if (action == 1) {
|
if (action === 1) {
|
||||||
controlInfo.value.videoFlag = false;
|
controlInfo.value.videoFlag = false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
const controlVideoFn = (pan, tilt, zoom, opType) => {
|
// 如果将来需要使用,保留此方法
|
||||||
controlInfo.value.opType = opType;
|
// const controlVideoFn = (opType) => {
|
||||||
if (controlInfo.value.videoFlag) {
|
// controlInfo.value.opType = opType;
|
||||||
Message.warning("不要重复点击");
|
// if (controlInfo.value.videoFlag) {
|
||||||
return;
|
// Message.warning("不要重复点击");
|
||||||
}
|
// return;
|
||||||
if (videoInfo.value.deviceType != 2) {
|
// }
|
||||||
Message.warning("该设备不是球机,不支持此操作");
|
// if (videoInfo.value.deviceType !== DEVICE_TYPE_BALL) {
|
||||||
return false;
|
// Message.warning("该设备不是球机,不支持此操作");
|
||||||
}
|
// return false;
|
||||||
switch (videoInfo.value.videoType) {
|
// }
|
||||||
case 3:
|
// switch (videoInfo.value.videoType) {
|
||||||
controlInfo.value.videoFlag = true;
|
// case VIDEO_TYPE_ISC:
|
||||||
controlVideoFn_isc(opType);
|
// controlInfo.value.videoFlag = true;
|
||||||
break;
|
// controlVideoFn_isc(opType);
|
||||||
default:
|
// break;
|
||||||
Message.warning("暂不支持");
|
// default:
|
||||||
break;
|
// Message.warning("暂不支持");
|
||||||
}
|
// break;
|
||||||
};
|
// }
|
||||||
|
// };
|
||||||
|
|
||||||
const controlVideoFn_isc = (opType) => {
|
const controlVideoFn_isc = (opType) => {
|
||||||
const json = {
|
const json = {
|
||||||
@ -296,10 +307,14 @@ const controlVideoFn_isc = (opType) => {
|
|||||||
};
|
};
|
||||||
getHikPtzControlApi(json)
|
getHikPtzControlApi(json)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
if (res.code == 200) {
|
if (res.code === SUCCESS_CODE) {
|
||||||
Message.success("控制成功");
|
Message.success("控制成功");
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error("ISC视频控制失败:", error);
|
||||||
|
Message.error("视频控制失败,请稍后重试");
|
||||||
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
controlInfo.value.videoFlag = false;
|
controlInfo.value.videoFlag = false;
|
||||||
});
|
});
|
||||||
@ -316,25 +331,83 @@ const startDrag = (e) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleDrag = (e) => {
|
const handleDrag = (e) => {
|
||||||
if (!isDragging.value) return;
|
if (!isDragging.value || !canvas.value) return;
|
||||||
|
|
||||||
const rect = canvas.value.getBoundingClientRect();
|
const rect = canvas.value.getBoundingClientRect();
|
||||||
const clientX = e.clientX || e.touches[0].clientX;
|
// 安全地获取触摸或鼠标坐标
|
||||||
const clientY = e.clientY || e.touches[0].clientY;
|
const clientX = e.clientX ?? (e.touches?.[0]?.clientX);
|
||||||
|
const clientY = e.clientY ?? (e.touches?.[0]?.clientY);
|
||||||
|
|
||||||
|
if (clientX === undefined || clientY === undefined) return;
|
||||||
|
|
||||||
const centerX = rect.left + rect.width / 2;
|
const centerX = rect.left + rect.width / 2;
|
||||||
const centerY = rect.top + rect.height / 2;
|
const centerY = rect.top + rect.height / 2;
|
||||||
const x = clientX - centerX;
|
const x = clientX - centerX;
|
||||||
const y = clientY - centerY;
|
const y = clientY - centerY;
|
||||||
|
|
||||||
let angle = Math.atan2(y, x) * (180 / Math.PI);
|
// 计算鼠标相对于圆心的角度(弧度)
|
||||||
angle = (angle + 180) % 360;
|
let angle = Math.atan2(y, x);
|
||||||
|
|
||||||
const gapAngle = 90;
|
// 仪表盘起始角度:Math.PI / 2 + Math.PI / 4 = 135° (从右上角开始)
|
||||||
const usableAngle = 360 - gapAngle;
|
const startAngle = Math.PI / 2 + Math.PI / 4;
|
||||||
let value = Math.round((angle * 100) / usableAngle);
|
// 仪表盘结束角度:135° + 270° = 405° = 45° (跨越0°)
|
||||||
if (angle > usableAngle) value = 100;
|
const endAngle = startAngle + (2 * Math.PI - Math.PI / 2);
|
||||||
currentValue.value = Math.min(100, Math.max(0, value));
|
// 仪表盘有效角度范围:270° (360° - 90°缺口)
|
||||||
|
const usableAngle = 2 * Math.PI - Math.PI / 2;
|
||||||
|
|
||||||
|
// 将角度转换为 0-2π 范围
|
||||||
|
if (angle < 0) {
|
||||||
|
angle += 2 * Math.PI;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算相对于起始角度的偏移角度
|
||||||
|
let relativeAngle;
|
||||||
|
|
||||||
|
// 计算结束角度(在0-2π范围内)
|
||||||
|
const endAngleNormalized = endAngle % (2 * Math.PI); // 45° = π/4
|
||||||
|
|
||||||
|
// 判断角度是否在有效范围内
|
||||||
|
// 情况1:角度在 startAngle 到 2π 之间(135° 到 360°)
|
||||||
|
if (angle >= startAngle && angle <= 2 * Math.PI) {
|
||||||
|
relativeAngle = angle - startAngle;
|
||||||
|
// 如果超出有效范围,限制在边界
|
||||||
|
if (relativeAngle > usableAngle) {
|
||||||
|
relativeAngle = usableAngle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 情况2:角度在 0 到 endAngleNormalized 之间(0° 到 45°),即跨越0°的情况
|
||||||
|
else if (angle >= 0 && angle <= endAngleNormalized) {
|
||||||
|
// 角度跨越了0°,需要加上2π再计算
|
||||||
|
relativeAngle = (angle + 2 * Math.PI) - startAngle;
|
||||||
|
// 确保不超过有效范围
|
||||||
|
if (relativeAngle > usableAngle) {
|
||||||
|
relativeAngle = usableAngle;
|
||||||
|
}
|
||||||
|
// 确保不小于0(防止计算错误)
|
||||||
|
if (relativeAngle < 0) {
|
||||||
|
relativeAngle = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 情况3:角度在无效范围内(45° 到 135°之间,即缺口部分)
|
||||||
|
else {
|
||||||
|
// 如果角度在缺口范围内,限制到最近的边界
|
||||||
|
// 判断更接近起始还是结束
|
||||||
|
const distToStart = Math.abs(angle - startAngle);
|
||||||
|
const distToEnd = Math.min(
|
||||||
|
Math.abs(angle - endAngleNormalized),
|
||||||
|
Math.abs(angle + 2 * Math.PI - endAngleNormalized)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (distToStart < distToEnd) {
|
||||||
|
relativeAngle = 0; // 限制到起始位置(0%)
|
||||||
|
} else {
|
||||||
|
relativeAngle = usableAngle; // 限制到结束位置(100%)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将角度转换为 0-100 的值
|
||||||
|
const value = Math.round((relativeAngle / usableAngle) * 100);
|
||||||
|
currentValue.value = Math.min(props.max, Math.max(props.min, value));
|
||||||
|
|
||||||
drawGauge();
|
drawGauge();
|
||||||
emit("input", currentValue.value);
|
emit("input", currentValue.value);
|
||||||
@ -346,6 +419,7 @@ const handleDrag = (e) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const stopDrag = () => {
|
const stopDrag = () => {
|
||||||
|
if (!isDragging.value) return;
|
||||||
isDragging.value = false;
|
isDragging.value = false;
|
||||||
document.removeEventListener("mousemove", handleDrag);
|
document.removeEventListener("mousemove", handleDrag);
|
||||||
document.removeEventListener("mouseup", stopDrag);
|
document.removeEventListener("mouseup", stopDrag);
|
||||||
@ -355,6 +429,7 @@ const stopDrag = () => {
|
|||||||
|
|
||||||
// 生命周期
|
// 生命周期
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
if (!canvas.value) return;
|
||||||
initCanvas();
|
initCanvas();
|
||||||
drawGauge();
|
drawGauge();
|
||||||
canvas.value.addEventListener("mousedown", startDrag);
|
canvas.value.addEventListener("mousedown", startDrag);
|
||||||
@ -362,8 +437,16 @@ onMounted(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
canvas.value.removeEventListener("mousedown", startDrag);
|
// 确保停止拖拽状态
|
||||||
canvas.value.removeEventListener("touchstart", startDrag);
|
if (isDragging.value) {
|
||||||
|
stopDrag();
|
||||||
|
}
|
||||||
|
// 清理canvas事件监听器
|
||||||
|
if (canvas.value) {
|
||||||
|
canvas.value.removeEventListener("mousedown", startDrag);
|
||||||
|
canvas.value.removeEventListener("touchstart", startDrag);
|
||||||
|
}
|
||||||
|
// 清理document事件监听器(防止内存泄漏)
|
||||||
document.removeEventListener("mousemove", handleDrag);
|
document.removeEventListener("mousemove", handleDrag);
|
||||||
document.removeEventListener("mouseup", stopDrag);
|
document.removeEventListener("mouseup", stopDrag);
|
||||||
document.removeEventListener("touchmove", handleDrag);
|
document.removeEventListener("touchmove", handleDrag);
|
||||||
@ -392,10 +475,7 @@ watch(
|
|||||||
margin-bottom: 28px;
|
margin-bottom: 28px;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
}
|
}
|
||||||
#content {
|
|
||||||
margin: auto;
|
|
||||||
width: 200px;
|
|
||||||
}
|
|
||||||
.draggable-gauge-container {
|
.draggable-gauge-container {
|
||||||
position: relative;
|
position: relative;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
@ -408,73 +488,6 @@ watch(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Element UI 风格仪表盘样式
|
|
||||||
.gauge-wrapper {
|
|
||||||
width: 260px;
|
|
||||||
height: 260px;
|
|
||||||
position: absolute;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
margin-bottom: 30px;
|
|
||||||
z-index: 5;
|
|
||||||
}
|
|
||||||
|
|
||||||
.gauge-container {
|
|
||||||
position: relative;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
transform: rotate(90deg);
|
|
||||||
}
|
|
||||||
|
|
||||||
.gauge-svg {
|
|
||||||
transform: rotate(-135deg); // 旋转SVG使圆弧从正确位置开始
|
|
||||||
}
|
|
||||||
|
|
||||||
.gauge-background {
|
|
||||||
opacity: 0.2;
|
|
||||||
}
|
|
||||||
|
|
||||||
.gauge-progress {
|
|
||||||
stroke-linecap: round; // 圆角端点
|
|
||||||
}
|
|
||||||
|
|
||||||
.gauge-indicator {
|
|
||||||
cursor: pointer;
|
|
||||||
transition: all 0.2s ease;
|
|
||||||
|
|
||||||
// &:hover {
|
|
||||||
// transform: scale(1.1);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// &:active {
|
|
||||||
// transform: scale(0.95);
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
.gauge-value-display {
|
|
||||||
position: absolute;
|
|
||||||
top: 50%;
|
|
||||||
left: 50%;
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
text-align: center;
|
|
||||||
pointer-events: none;
|
|
||||||
z-index: 4;
|
|
||||||
|
|
||||||
.value-text {
|
|
||||||
font-weight: bold;
|
|
||||||
line-height: 1;
|
|
||||||
margin-bottom: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.title-text {
|
|
||||||
opacity: 0.8;
|
|
||||||
line-height: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.container-bottom {
|
.container-bottom {
|
||||||
margin-top: 6px;
|
margin-top: 6px;
|
||||||
width: 183px;
|
width: 183px;
|
||||||
@ -490,10 +503,7 @@ watch(
|
|||||||
background-size: 100% 100%;
|
background-size: 100% 100%;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
}
|
}
|
||||||
.video-icon2_1 {
|
// .video-icon2_1 未使用,已移除
|
||||||
height: 13px;
|
|
||||||
background-image: url("~@/assets/images/videoSurveillance/video-icon2_1.png");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.container-box {
|
.container-box {
|
||||||
width: 159px;
|
width: 159px;
|
||||||
|
|||||||
@ -984,6 +984,7 @@ export default {
|
|||||||
...row,
|
...row,
|
||||||
projectSn: this.$store.state.projectSn,
|
projectSn: this.$store.state.projectSn,
|
||||||
isEnabled: row.isEnabled == 1 ? 0 : 1,
|
isEnabled: row.isEnabled == 1 ? 0 : 1,
|
||||||
|
devSn: this.currentGroupInfo.devSn,
|
||||||
}).then((res) => {
|
}).then((res) => {
|
||||||
if (res.code == 200) {
|
if (res.code == 200) {
|
||||||
this.$message.success(this.$t("message.projectInfo.saveSuccess"));
|
this.$message.success(this.$t("message.projectInfo.saveSuccess"));
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user