flx:优化云台流程功能 监控展现形式

This commit is contained in:
Rain_ 2025-11-17 14:23:35 +08:00
parent 4bc6693ad4
commit b4feeafb38
7 changed files with 814 additions and 293 deletions

View File

@ -28,3 +28,6 @@ export const selectLiveVideoListApi = (params: {}, showLoading: boolean) => {
export const getHikPtzControlApi = (params: {}) => {
return http.post(BASEURL + `/xmgl/videoItem/getHikPtzControl`, params, { headers: { noLoading: true }});
};
export const setHikPtzControlApi = (params: {}) => {
return http.post(BASEURL + `/xmgl/videoItem/setHikPtzControl`, params, { headers: { noLoading: true }});
};

View File

@ -29,4 +29,8 @@ export const getVideoItemInfoPoliceCameraItemApi = (params: {}) => {
// 分页列表查询执法记录仪设备列表信息
export const getPoliceCameraItemPageApi = (params: {}) => {
return http.get(BASEURL + `/xmgl/policeCameraItem/page`, params);
};
// 查询所有项目信息列表信息
export const selectAllProjectInfoList = (params: {}) => {
return http.post(BASEURL + `/xmgl/project/selectAllProjectInfoList`, params);
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 714 B

View File

@ -9,7 +9,8 @@
:class="{
'container-top_active container_active': controlInfo.videoFlag && controlInfo.opType === 'UP'
}"
@click="controlVideoFn(0, 0.2, 0, 'UP')"
@mousedown="controlVideoMouse('UP', 0)"
@mouseup="controlVideoMouse('UP', 1)"
>
<div class="icon-up"></div>
</div>
@ -18,7 +19,8 @@
:class="{
'container-right_active container_active': controlInfo.videoFlag && controlInfo.opType === 'RIGHT'
}"
@click="controlVideoFn(0.2, 0, 0, 'RIGHT')"
@mousedown="controlVideoMouse('RIGHT', 0)"
@mouseup="controlVideoMouse('RIGHT', 1)"
>
<div class="icon-up"></div>
</div>
@ -27,7 +29,8 @@
:class="{
'container-bottom_active container_active': controlInfo.videoFlag && controlInfo.opType === 'DOWN'
}"
@click="controlVideoFn(0, -0.2, 0, 'DOWN')"
@mousedown="controlVideoMouse('DOWN', 0)"
@mouseup="controlVideoMouse('DOWN', 1)"
>
<div class="icon-up"></div>
</div>
@ -36,16 +39,33 @@
:class="{
'container-left_active container_active': controlInfo.videoFlag && controlInfo.opType === 'LEFT'
}"
@click="controlVideoFn(-0.2, 0, 0, 'LEFT')"
@mousedown="controlVideoMouse('LEFT', 0)"
@mouseup="controlVideoMouse('LEFT', 1)"
>
<div class="icon-up"></div>
</div>
<div class="container-interior">
<div class="interior-main">
<div class="interior-item interior-icon1" @click="controlVideoFn(-0.2, 0, 0, 'LEFT_UP')"></div>
<div class="interior-item interior-icon2" @click="controlVideoFn(-0.2, 0, 0, 'RIGHT_UP')"></div>
<div class="interior-item interior-icon3" @click="controlVideoFn(-0.2, 0, 0, 'RIGHT_DOWN')"></div>
<div class="interior-item interior-icon4" @click="controlVideoFn(-0.2, 0, 0, 'LEFT_DOWN')"></div>
<div
class="interior-item interior-icon1"
@mousedown="controlVideoMouse('LEFT_UP', 0)"
@mouseup="controlVideoMouse('LEFT_UP', 1)"
></div>
<div
class="interior-item interior-icon2"
@mousedown="controlVideoMouse('RIGHT_UP', 0)"
@mouseup="controlVideoMouse('RIGHT_UP', 1)"
></div>
<div
class="interior-item interior-icon3"
@mousedown="controlVideoMouse('RIGHT_DOWN', 0)"
@mouseup="controlVideoMouse('RIGHT_DOWN', 1)"
></div>
<div
class="interior-item interior-icon4"
@mousedown="controlVideoMouse('LEFT_DOWN', 0)"
@mouseup="controlVideoMouse('LEFT_DOWN', 1)"
></div>
<div class="interior-refresh">
<!-- <div class="icon-refresh"></div> -->
</div>
@ -59,13 +79,13 @@
</div>
</div>
<div class="container-btn">
<div @click="controlVideoFn(0, 0, -0.2, 'ZOOM_OUT')">
<div @mousedown="controlVideoMouse('ZOOM_OUT', 0)" @mouseup="controlVideoMouse('ZOOM_OUT', 1)">
<div class="btn-round">
<div class="icon-minus"></div>
</div>
<div>倍率缩小</div>
</div>
<div @click="controlVideoFn(0, 0, 0.2, 'ZOOM_IN')">
<div @mousedown="controlVideoMouse('ZOOM_IN', 0)" @mouseup="controlVideoMouse('ZOOM_IN', 1)">
<div>倍率放大</div>
<div class="btn-round">
<div class="icon-plus"></div>
@ -76,50 +96,50 @@
</template>
<script setup>
import { ref, watch, onMounted, onBeforeUnmount } from "vue";
import { getHikPtzControlApi } from "@/api/modules/video";
import { getHikPtzControlApi, setHikPtzControlApi } from "@/api/modules/video";
import { ElMessage } from "element-plus";
const props = defineProps({
value: {
type: Number,
default: 10
},
size: {
type: Number,
default: 200
},
min: {
type: Number,
default: 0
},
max: {
type: Number,
default: 100
},
title: {
type: String,
default: ""
},
progressColor: {
type: String,
default: "#00C9FF"
},
backgroundColor: {
type: String,
default: "#BAF0FF"
},
themeColor: {
type: String,
default: "#fff"
},
lineWidth: {
type: Number,
default: 5
},
newVideoInfo: {
type: Object,
default: () => ({})
}
value: {
type: Number,
default: 10,
},
size: {
type: Number,
default: 200,
},
min: {
type: Number,
default: 0,
},
max: {
type: Number,
default: 100,
},
title: {
type: String,
default: "",
},
progressColor: {
type: String,
default: "#00C9FF",
},
backgroundColor: {
type: String,
default: "#BAF0FF",
},
themeColor: {
type: String,
default: "#fff",
},
lineWidth: {
type: Number,
default: 5,
},
newVideoInfo: {
type: Object,
default: () => ({}),
},
});
const emit = defineEmits(["input", "change"]);
@ -129,190 +149,315 @@ const canvas = ref(null);
const ctx = ref(null);
const isDragging = ref(false);
const controlInfo = ref({
videoFlag: false,
opType: ""
videoFlag: false,
opType: "",
});
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 = () => {
// canvas.value = canvas.value;
ctx.value = canvas.value.getContext("2d");
if (!canvas.value) return;
ctx.value = canvas.value.getContext("2d");
const dpr = window.devicePixelRatio || 1;
canvas.value.width = props.size * dpr;
canvas.value.height = props.size * dpr;
canvas.value.style.width = `${props.size}px`;
canvas.value.style.height = `${props.size}px`;
ctx.value.scale(dpr, dpr);
const dpr = window.devicePixelRatio || 1;
canvas.value.width = props.size * dpr;
canvas.value.height = props.size * dpr;
canvas.value.style.width = `${props.size}px`;
canvas.value.style.height = `${props.size}px`;
ctx.value.scale(dpr, dpr);
};
const drawGauge = () => {
if (!ctx.value) return;
if (!ctx.value) return;
const center = props.size / 2;
const radius = center - props.lineWidth / 2;
const startAngle = Math.PI / 2 + Math.PI / 4;
const endAngle = startAngle + ((2 * Math.PI - Math.PI / 2) * currentValue.value) / 100;
const center = props.size / 2;
const radius = center - props.lineWidth / 2;
const startAngle = Math.PI / 2 + Math.PI / 4;
const endAngle =
startAngle + ((2 * Math.PI - Math.PI / 2) * currentValue.value) / 100;
ctx.value.clearRect(0, 0, props.size, props.size);
ctx.value.clearRect(0, 0, props.size, props.size);
//
ctx.value.beginPath();
ctx.value.setLineDash([5, 3]);
ctx.value.arc(center, center, radius, startAngle, startAngle + (2 * Math.PI - Math.PI / 2), false);
ctx.value.strokeStyle = props.backgroundColor;
ctx.value.lineWidth = props.lineWidth;
ctx.value.stroke();
//
ctx.value.beginPath();
ctx.value.setLineDash([5, 3]);
ctx.value.arc(
center,
center,
radius,
startAngle,
startAngle + (2 * Math.PI - Math.PI / 2),
false
);
ctx.value.strokeStyle = props.backgroundColor;
ctx.value.lineWidth = props.lineWidth;
ctx.value.stroke();
//
ctx.value.beginPath();
ctx.value.setLineDash([5, 3]);
ctx.value.arc(center, center, radius, startAngle, endAngle);
ctx.value.strokeStyle = props.progressColor;
ctx.value.lineWidth = props.lineWidth;
ctx.value.stroke();
//
ctx.value.beginPath();
ctx.value.setLineDash([5, 3]);
ctx.value.arc(center, center, radius, startAngle, endAngle);
ctx.value.strokeStyle = props.progressColor;
ctx.value.lineWidth = props.lineWidth;
ctx.value.stroke();
//
const indicatorWidth = props.lineWidth;
const indicatorHeight = props.lineWidth * 3;
const indicatorX = center + radius * Math.cos(endAngle);
const indicatorY = center + radius * Math.sin(endAngle);
//
const indicatorWidth = props.lineWidth;
const indicatorHeight = props.lineWidth * 3;
const indicatorX = center + radius * Math.cos(endAngle);
const indicatorY = center + radius * Math.sin(endAngle);
ctx.value.save();
ctx.value.translate(indicatorX, indicatorY);
ctx.value.rotate(endAngle + Math.PI / 2);
ctx.value.save();
ctx.value.translate(indicatorX, indicatorY);
ctx.value.rotate(endAngle + Math.PI / 2);
ctx.value.beginPath();
ctx.value.setLineDash([]);
ctx.value.rect(-indicatorWidth / 2, -indicatorHeight / 2, indicatorWidth, indicatorHeight);
ctx.value.fillStyle = "#2758C0";
ctx.value.fill();
ctx.value.beginPath();
ctx.value.setLineDash([]);
ctx.value.rect(
-indicatorWidth / 2,
-indicatorHeight / 2,
indicatorWidth,
indicatorHeight
);
ctx.value.fillStyle = INDICATOR_COLOR;
ctx.value.fill();
ctx.value.restore();
ctx.value.restore();
};
const controlVideoFn = (pan, tilt, zoom, opType) => {
controlInfo.value.opType = opType;
if (controlInfo.value.videoFlag) {
ElMessage.warning("不要重复点击");
return;
}
if (videoInfo.value.deviceType != 2) {
ElMessage.warning("该设备不是球机,不支持此操作");
return false;
}
switch (videoInfo.value.videoType) {
case 3:
controlInfo.value.videoFlag = true;
controlVideoFn_isc(opType);
break;
default:
ElMessage.warning("暂不支持");
break;
}
const controlVideoMouse = (opType, action) => {
if (videoInfo.value.deviceType !== DEVICE_TYPE_BALL) {
ElMessage.warning("该设备不是球机,不支持此操作");
return false;
}
controlInfo.value.opType = opType;
if (action === 0) {
controlInfo.value.videoFlag = true;
}
const json = {
itemId: videoInfo.value.itemId,
opType: opType,
action: action,
opSize: currentValue.value,
opCode: 1,
};
setHikPtzControlApi(json)
.then((res) => {
if (res.code === SUCCESS_CODE) {
//
}
})
.catch((error) => {
console.error("视频控制失败:", error);
ElMessage.error("视频控制失败,请稍后重试");
})
.finally(() => {
if (action === 1) {
controlInfo.value.videoFlag = false;
}
});
};
// 使
// const controlVideoFn = (opType) => {
// controlInfo.value.opType = opType;
// if (controlInfo.value.videoFlag) {
// ElMessage.warning("");
// return;
// }
// if (videoInfo.value.deviceType !== DEVICE_TYPE_BALL) {
// ElMessage.warning("");
// return false;
// }
// switch (videoInfo.value.videoType) {
// case VIDEO_TYPE_ISC:
// controlInfo.value.videoFlag = true;
// controlVideoFn_isc(opType);
// break;
// default:
// ElMessage.warning("");
// break;
// }
// };
const controlVideoFn_isc = opType => {
const json = {
itemId: videoInfo.value.itemId,
opType: opType,
opSize: currentValue.value,
opCode: 1
};
getHikPtzControlApi(json)
.then(res => {
if (res.code == 200) {
ElMessage.success("控制成功");
}
})
.finally(() => {
controlInfo.value.videoFlag = false;
});
const controlVideoFn_isc = (opType) => {
const json = {
itemId: videoInfo.value.itemId,
opType: opType,
opSize: currentValue.value,
opCode: 1,
};
getHikPtzControlApi(json)
.then((res) => {
if (res.code === SUCCESS_CODE) {
ElMessage.success("控制成功");
}
})
.catch((error) => {
console.error("ISC视频控制失败:", error);
ElMessage.error("视频控制失败,请稍后重试");
})
.finally(() => {
controlInfo.value.videoFlag = false;
});
};
//
const startDrag = e => {
isDragging.value = true;
document.addEventListener("mousemove", handleDrag);
document.addEventListener("mouseup", stopDrag);
document.addEventListener("touchmove", handleDrag);
document.addEventListener("touchend", stopDrag);
handleDrag(e);
const startDrag = (e) => {
isDragging.value = true;
document.addEventListener("mousemove", handleDrag);
document.addEventListener("mouseup", stopDrag);
document.addEventListener("touchmove", handleDrag);
document.addEventListener("touchend", stopDrag);
handleDrag(e);
};
const handleDrag = e => {
if (!isDragging.value) return;
const handleDrag = (e) => {
if (!isDragging.value || !canvas.value) return;
const rect = canvas.value.getBoundingClientRect();
const clientX = e.clientX || e.touches[0].clientX;
const clientY = e.clientY || e.touches[0].clientY;
const rect = canvas.value.getBoundingClientRect();
//
const clientX = e.clientX ?? (e.touches?.[0]?.clientX);
const clientY = e.clientY ?? (e.touches?.[0]?.clientY);
const centerX = rect.left + rect.width / 2;
const centerY = rect.top + rect.height / 2;
const x = clientX - centerX;
const y = clientY - centerY;
if (clientX === undefined || clientY === undefined) return;
let angle = Math.atan2(y, x) * (180 / Math.PI);
angle = (angle + 180) % 360;
const centerX = rect.left + rect.width / 2;
const centerY = rect.top + rect.height / 2;
const x = clientX - centerX;
const y = clientY - centerY;
const gapAngle = 90;
const usableAngle = 360 - gapAngle;
let value = Math.round((angle * 100) / usableAngle);
if (angle > usableAngle) value = 100;
currentValue.value = Math.min(100, Math.max(0, value));
//
let angle = Math.atan2(y, x);
// 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;
}
}
// 345° 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();
emit("input", currentValue.value);
emit("change", currentValue.value);
drawGauge();
emit("input", currentValue.value);
emit("change", currentValue.value);
if (e.cancelable && e.type.includes("touch")) {
e.preventDefault();
}
if (e.cancelable && e.type.includes("touch")) {
e.preventDefault();
}
};
const stopDrag = () => {
isDragging.value = false;
document.removeEventListener("mousemove", handleDrag);
document.removeEventListener("mouseup", stopDrag);
document.removeEventListener("touchmove", handleDrag);
document.removeEventListener("touchend", stopDrag);
if (!isDragging.value) return;
isDragging.value = false;
document.removeEventListener("mousemove", handleDrag);
document.removeEventListener("mouseup", stopDrag);
document.removeEventListener("touchmove", handleDrag);
document.removeEventListener("touchend", stopDrag);
};
//
onMounted(() => {
initCanvas();
drawGauge();
canvas.value.addEventListener("mousedown", startDrag);
canvas.value.addEventListener("touchstart", startDrag);
if (!canvas.value) return;
initCanvas();
drawGauge();
canvas.value.addEventListener("mousedown", startDrag);
canvas.value.addEventListener("touchstart", startDrag);
});
onBeforeUnmount(() => {
canvas.value.removeEventListener("mousedown", startDrag);
canvas.value.removeEventListener("touchstart", startDrag);
document.removeEventListener("mousemove", handleDrag);
document.removeEventListener("mouseup", stopDrag);
document.removeEventListener("touchmove", handleDrag);
document.removeEventListener("touchend", stopDrag);
//
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("mouseup", stopDrag);
document.removeEventListener("touchmove", handleDrag);
document.removeEventListener("touchend", stopDrag);
});
//
watch(
() => props.value,
newVal => {
currentValue.value = newVal;
drawGauge();
}
() => props.value,
(newVal) => {
currentValue.value = newVal;
drawGauge();
}
);
watch(
() => props.newVideoInfo,
newVal => {
videoInfo.value = newVal;
},
{ deep: true, immediate: true }
() => props.newVideoInfo,
(newVal) => {
videoInfo.value = newVal;
},
{ deep: true, immediate: true }
);
</script>
<style scoped lang="scss">
.gauge-canvas {
margin-bottom: 28px;
@ -423,7 +568,7 @@ watch(
}
.container-box {
width: 159px;
height: 163px;
height: 163px;
padding: 8px;
background-image: url("@/assets/images/videoImage/bg-index1.png");
background-size: 100% 100%;
@ -570,9 +715,9 @@ watch(
.container-btn {
width: 270px;
height: 50px;
background: rgba(39,88,192,0.2);
background: rgba(39, 88, 192, 0.2);
border-radius: 237px;
border: 1px solid rgba(39,88,192,0.6);;
border: 1px solid rgba(39, 88, 192, 0.6);
margin: 0 auto;
margin-top: 15px;
display: flex;
@ -587,7 +732,7 @@ watch(
align-items: center;
// justify-content: center;
font-size: 15px;
color: #FFFFFF;
color: #ffffff;
cursor: pointer;
.btn-round {
width: 34px;

View File

@ -65,6 +65,7 @@ import { reactive, watch, onMounted, computed, nextTick, onBeforeUnmount } from
import { ElMessage } from "element-plus";
import loadingGif from "@/assets/images/iscImage/loading.gif";
import errorPng from "@/assets/images/iscImage/text-to-image.png";
import reloadImg from "@/assets/images/iscImage/reload.png";
const props = defineProps({
devList: {
type: Array,
@ -415,8 +416,9 @@ const play = (row, index) => {
//
const id = `#player-${props.playerId} #player-container-${index}`;
var d1 = document.querySelector(id); //div
// console.log(88555, d1.childNodes);
d1.childNodes.forEach(item => {
if (item.nodeName == "" || item.nodeName == "IMG") {
if (item.nodeName == "" || item.nodeName == "IMG" || item.className == "classImg") {
d1.removeChild(item);
}
});
@ -445,9 +447,9 @@ const play = (row, index) => {
if (!index) {
index = 0;
}
const findIndex = Array.from(d1.childNodes).findIndex(item => item.localName == "img");
videoInfo.player.JS_Play(preUrl, param, index).then(
() => {
const findIndex = Array.from(d1.childNodes).findIndex(item => item.localName == "img");
if (findIndex !== -1) {
d1.removeChild(d1.childNodes[findIndex]);
console.log("播放成功", findIndex);
@ -458,15 +460,44 @@ const play = (row, index) => {
},
err => {
console.log("播放失败");
im.src = errorPng;
const imgHeightNum = 20;
const imgWidthNum = 150;
im.style.width = imgWidthNum + "px";
im.style.height = imgHeightNum + "px";
const positionNum = imgHeightNum / 2 - imgHeightNum;
const positionWidthNum = imgWidthNum / 2 - imgWidthNum;
im.style.marginLeft = positionWidthNum + "px";
im.style.marginTop = positionNum + "px";
// im.src = errorPng;
// const imgHeightNum = 20;
// const imgWidthNum = 150;
// im.style.width = imgWidthNum + "px";
// im.style.height = imgHeightNum + "px";
// const positionNum = imgHeightNum / 2 - imgHeightNum;
// const positionWidthNum = imgWidthNum / 2 - imgWidthNum;
// im.style.marginLeft = positionWidthNum + "px";
// im.style.marginTop = positionNum + "px";
if (findIndex !== -1) {
d1.removeChild(d1.childNodes[findIndex]);
let ndiv = document.createElement("div"); //
const reloadBtn = document.createElement("div");
//
const reloadIcon = document.createElement("img");
reloadIcon.src = reloadImg; //
reloadBtn.textContent = "重新加载";
reloadBtn.addEventListener("click", (e) => {
e.stopPropagation();
onReload(row.id);
});
//
reloadBtn.appendChild(reloadIcon);
ndiv.innerHTML = `
${row.status == 2 ? "工单任务未关闭或现场网络信号弱" : "预览失败,请检查设备网络"}
`;
ndiv.style.position = "absolute";
ndiv.style.top = "50%";
ndiv.style.left = "50%";
ndiv.style.transform = "translate(-50%, -50%)";
ndiv.className = "classImg";
ndiv.appendChild(reloadBtn);
d1.appendChild(ndiv); //div
}
}
);
videoInfo.player.JS_SetConnectTimeOut(index, 60).then(
@ -481,9 +512,14 @@ const play = (row, index) => {
);
});
};
/**
* 分屏这里我太懒了就循环了一个视频流
*/
const onReload = (id) => {
console.log("重新加载", id);
//
const findItem = videoInfo.devH5List.find(item => item.id === id);
if (findItem) {
play(findItem, videoInfo.iWndIndex);
}
};
const onTwoSubmit = num => {
// 24
videoInfo.numCount = num;
@ -563,7 +599,7 @@ watch(
const videoNameUp = computed(() => {
return index => {
const find = videoInfo.devH5List.find(item => item.eIndex === index);
console.log(8844555, videoInfo.devH5List);
// console.log(8844555, videoInfo.devH5List);
return find ? find.videoName : "";
};
});
@ -592,6 +628,28 @@ onBeforeUnmount(() => {
</script>
<style lang="scss" scoped>
:deep(.classImg) {
color: rgba(255, 255, 255, 0.5);
font-size: 16px;
width: calc(100% - 24px);
text-align: center;
padding: 0 12px;
z-index: 101;
> div {
color: rgb(255, 255, 255);
font-size: 12px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
margin-top: 6px;
> img {
width: 16px;
height: 16px;
margin-left: 8px;
}
}
}
:deep(.sub-wnd) {
height: 100% !important;
}

View File

@ -130,6 +130,17 @@
<div>工作票类型</div>
<div>{{ workTicketDetail.typeName }}</div>
</div>
<!-- <div class="box1">
<div>风险状态</div>
<div
class="state-box"
:class="{
gfx_active: workTicketDetail.riskType == 2
}"
>
{{ workTicketDetail.riskType == 2 ? "高风险" : "一般风险" }}
</div>
</div> -->
<div class="box1">
<div>工作票编号</div>
<div>{{ workTicketDetail.workTicketNumber }}</div>
@ -227,8 +238,18 @@
:autoplay="true"
:controls="true"
/> -->
<!-- <IscPlayer :devList="[item]" :key="'player-' + item.itemId" :playerId="'player-' + item.itemId" /> -->
<div class="videoOverview" :id="`videoOverview${item.itemId}`">
<IscPlayer
v-if="videoConfig.enableNotPlugin == 1"
:devList="[
{
...item,
status: workTicketDetail.status
}
]"
:key="'player-' + item.itemId"
:playerId="'player-' + item.itemId"
/>
<div v-else class="videoOverview" :id="`videoOverview${item.itemId}`">
<IscPlugin
:devList="[item]"
:itemId="item.itemId"
@ -381,43 +402,129 @@
:modal="false"
:modal-append-to-body="false"
v-model="equipmentDialog"
width="448px"
width="941px"
class="equipment-dialog"
>
<div class="dialog_content">
<div>
<div>设备名称</div>
<div>{{ equipmentDetail.devName ? equipmentDetail.devName : "--" }}</div>
</div>
<div>
<div>设备编号</div>
<div>{{ equipmentDetail.devSn ? equipmentDetail.devSn : "--" }}</div>
</div>
<div>
<div>设备状态</div>
<div class="state-box" :class="{ 'state-box_offline': equipmentDetail.deviceState != 1 }">
{{ equipmentDetail.deviceState == 1 ? "在线" : "离线" }}
<div class="content-left">
<div>
<div>设备名称</div>
<div>
{{ equipmentDetail.devName ? equipmentDetail.devName : "--" }}
</div>
</div>
<div>
<div>设备编号</div>
<div>{{ equipmentDetail.devSn ? equipmentDetail.devSn : "--" }}</div>
</div>
<div>
<div>所属项目</div>
<div>{{ projectSnName }}</div>
</div>
<div>
<div>设备状态</div>
<div class="state-box" :class="{ 'state-box_offline': equipmentDetail.deviceState != 1 }">
{{ equipmentDetail.deviceState == 1 ? "在线" : "离线" }}
</div>
</div>
<!-- <div>
<div>风险状态</div>
<div
class="state-box"
:class="{
gfx_active: workTicketDetail.riskType == 2
}"
>
{{ workTicketDetail.riskType == 2 ? "高风险" : "一般风险" }}
</div>
</div> -->
<div>
<div>工作票类型</div>
<div>
{{ workTicketDetail.typeName ? workTicketDetail.typeName : "--" }}
</div>
</div>
<div>
<div>工作票编号</div>
<div>
{{ workTicketDetail.workTicketNumber ? workTicketDetail.workTicketNumber : "--" }}
</div>
</div>
<div>
<div>施工区域</div>
<div>
{{ workTicketDetail.constructionAreaNames ? workTicketDetail.constructionAreaNames : "--" }}
</div>
</div>
<div>
<div>申请单位</div>
<div>
{{ workTicketDetail.applicantNames ? workTicketDetail.applicantNames : "--" }}
</div>
</div>
<div>
<div>作业人员</div>
<div>
{{ workTicketDetail.operator ? workTicketDetail.operator : "--" }}
</div>
</div>
<div>
<div>施工时间</div>
<div>
{{ workTicketDetail.constructionTimeBegin }} -
{{ workTicketDetail.constructionTimeEnd }}
</div>
</div>
<div>
<div>申请时间</div>
<div>
{{ workTicketDetail.applicationTime ? workTicketDetail.applicationTime : "--" }}
</div>
</div>
<div>
<div>安全措施</div>
<div>
{{ workTicketDetail.safetyMeasure ? workTicketDetail.safetyMeasure : "--" }}
</div>
</div>
<div>
<div>作业内容</div>
<div>
{{ workTicketDetail.workContent ? workTicketDetail.workContent : "--" }}
</div>
</div>
</div>
<div>
<div>SD卡容量</div>
<div>{{ equipmentDetail.sdCardCapacity ? equipmentDetail.sdCardCapacity : "--" }}</div>
</div>
<div>
<div>已用容量</div>
<div>{{ equipmentDetail.usedCapacity ? equipmentDetail.usedCapacity : "--" }}</div>
</div>
<div>
<div>剩余容量</div>
<div>{{ equipmentDetail.usedCapacity ? equipmentDetail.sdCardCapacity - equipmentDetail.usedCapacity : "--" }}</div>
</div>
<div>
<div>录像状态</div>
<div class="state-box">{{ equipmentDetail.recordingStatus ? equipmentDetail.recordingStatus : "--" }}</div>
</div>
<div>
<div>电量</div>
<div>{{ equipmentDetail.batteryLevel ? equipmentDetail.batteryLevel : "--" }}</div>
<div class="content-right">
<div class="content-right_header">附件内容</div>
<div class="box1">
<div>工作票附件</div>
<div class="content-img">
<template v-if="workTicketDetail.workTicketAttachment && workTicketDetail.workTicketAttachment.length > 0">
<el-image
v-for="item in workTicketDetail.workTicketAttachment"
:key="item.url"
:src="BASEURL + '/image/' + item.url"
:preview-src-list="[BASEURL + '/image/' + item.url]"
>
</el-image>
</template>
</div>
</div>
<div class="box1">
<div>其他附件</div>
<div class="content-file_list">
<div v-for="item in workTicketDetail.otherAttachment" :key="item.url">
<div>
<div></div>
<div>{{ item.name }}</div>
</div>
<div @click="downloadFileBtn(BASEURL + '/image/' + item.url, item.name)">
<div></div>
<div>下载</div>
</div>
</div>
</div>
</div>
</div>
</div>
</el-dialog>
@ -439,8 +546,10 @@ import {
getWorkTicketQueryByIdApi,
getWorkTicketHistoryListApi,
getVideoItemInfoPoliceCameraItemApi,
getPoliceCameraItemPageApi
getPoliceCameraItemPageApi,
selectAllProjectInfoList
} from "@/api/modules/workTicket";
import { getUseProjectVideoConfigApi } from "@/api/modules/tower";
import dayjs from "dayjs";
import duration from "dayjs/plugin/duration";
dayjs.extend(duration);
@ -513,7 +622,7 @@ const getWorkTicketCountWorkTicket = showLoading => {
});
};
const workTicketInfo = reactive({
status: "",
status: 2,
typeId: "",
numberOrContent: "",
constructionTime: [],
@ -577,6 +686,8 @@ onMounted(() => {
// // getWorkTicketCountWorkTicket(true);
// }, 30000);
getWorkTicketTypeTreePage();
getProjectVideoConfigList();
getSelectAllProjectInfoList();
});
const viewAllShow = ref(false);
const onViewAllClick = () => {
@ -785,7 +896,23 @@ const getVideoItemInfoPoliceCameraItem = async row => {
if (res.code != 200) return null;
return res.result.videoInfo;
};
const videoConfig = ref({
enableNotPlugin: 1
});
//
const getProjectVideoConfigList = () => {
getUseProjectVideoConfigApi({
projectSn: store.sn
}).then(res => {
if (res.code == 200) {
if (res.result) {
videoConfig.value = res.result;
} else {
videoConfig.enableNotPlugin = 1;
}
}
});
};
const onRefresh = () => {
if (!viewAllShow.value) {
onWorkTicketQuery();
@ -820,8 +947,8 @@ const updateStatus = computed(() => {
};
});
const itemListDevNameUp = computed(() => {
return workTicketDetail.value.itemList ? workTicketDetail.value.itemList.map(item => item.devName).join('、') : ""
})
return workTicketDetail.value.itemList ? workTicketDetail.value.itemList.map(item => item.devName).join("、") : "";
});
const workTicketTypeTreeList = ref([]);
//
const getWorkTicketTypeTreePage = () => {
@ -882,6 +1009,26 @@ const downloadFileBtn = (url, name) => {
console.error(error);
});
};
const projectList = ref([]);
const getSelectAllProjectInfoList = () => {
selectAllProjectInfoList({
sn: store.sn
}).then(res => {
if (res.code == 200) {
projectList.value = [
{
projectName: "全部项目",
projectSn: store.sn
},
...res.result
];
}
});
};
const projectSnName = computed(() => {
const find = projectList.value.find(item => item.projectSn == workTicketDetail.value.projectSn);
return find ? find.projectName : "";
});
</script>
<style lang="scss" scoped>
@ -1325,6 +1472,19 @@ const downloadFileBtn = (url, name) => {
border-radius: 4px;
}
}
.state-box {
padding: 4px 8px;
min-width: 40px;
color: white;
background-color: #88cf65;
border-radius: 4px;
text-align: center;
flex: initial !important;
}
.gfx_active {
background-color: #ff0000;
color: #ffffff;
}
}
.box1-header {
display: flex;
@ -1634,37 +1794,131 @@ const downloadFileBtn = (url, name) => {
.dialog_content {
padding: 0 20px 5px;
display: flex;
flex-wrap: wrap;
> div {
width: 50%;
display: flex;
align-items: flex-start;
font-size: 14px;
padding: 10px 0;
> div:last-child {
justify-content: space-between;
// flex-wrap: wrap;
.content-right {
width: 48%;
.content-right_header {
font-weight: 800;
font-size: 16px;
color: #ffffff;
margin-left: 10px;
flex: 1;
word-break: break-all;
}
.box1 {
display: flex;
align-items: flex-start;
padding: 10px 0;
font-size: 14px;
> div:first-child {
width: 70px;
color: #a2a4af;
> div:last-child {
color: #ffffff;
margin-left: 10px;
flex: 1;
word-break: break-all;
}
> div:first-child {
width: 70px;
color: #a2a4af;
}
.content-file_list {
display: flex;
flex-direction: column;
> div {
display: flex;
align-items: center;
justify-content: space-between;
height: 30px;
padding: 0 16px 0 20px;
background: rgba(39, 88, 192, 0.2);
> div:first-child {
font-size: 14px;
color: rgba(255, 255, 255, 0.6);
> div:last-child {
width: 170px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
> div:first-child {
width: 16px;
height: 16px;
background: url("~@/assets/images/companyBigScreen/terminalOperation/index-icon9.png") no-repeat;
background-size: 100% 100%;
}
}
> div:last-child {
font-size: 14px;
color: #5181f6;
cursor: pointer;
> div:first-child {
width: 16px;
height: 16px;
background: url("~@/assets/images/companyBigScreen/terminalOperation/index-icon10.png") no-repeat;
background-size: 100% 100%;
}
}
> div {
display: flex;
align-items: center;
> div:not(:first-child) {
margin-left: 6px;
}
}
}
}
.content-img {
display: flex;
flex-wrap: wrap;
> .el-image:nth-child(n + 4) {
margin-top: 20px;
}
> .el-image:not(:nth-child(3n + 1)) {
margin-left: 20px;
}
> .el-image {
width: 80px;
height: 80px;
border-radius: 4px;
}
}
}
.state-box {
padding: 4px 8px;
min-width: 40px;
color: white;
background-color: #88cf65;
border-radius: 4px;
text-align: center;
flex: 0 !important;
}
.state-box_offline {
background-color: #f7f7f7;
color: #272d45 !important;
}
.content-left {
width: 49%;
> div {
display: flex;
align-items: flex-start;
font-size: 14px;
padding: 10px 0;
> div:last-child {
color: #ffffff;
margin-left: 10px;
flex: 1;
word-break: break-all;
}
> div:first-child {
width: 70px;
color: #a2a4af;
}
.state-box {
padding: 4px 8px;
min-width: 40px;
color: white;
background-color: #88cf65;
border-radius: 4px;
text-align: center;
flex: initial !important;
}
.state-box_offline {
background-color: #f7f7f7;
color: #272d45 !important;
}
.gfx_active {
background-color: #ff0000;
color: #ffffff;
}
}
}
}

View File

@ -68,6 +68,7 @@ import { reactive, watch, onMounted, computed, nextTick } from "vue";
import { ElMessage } from "element-plus";
import loadingGif from "@/assets/images/iscImage/loading.gif";
import errorPng from "@/assets/images/iscImage/text-to-image.png";
import reloadImg from "@/assets/images/iscImage/reload.png";
const props = defineProps({
devList: {
type: Array,
@ -110,6 +111,10 @@ const stopAllPlay = () => {
if (controls) {
controls.classList.remove("video-controls_flex");
}
const findIndex = Array.from(wnd.childNodes).findIndex(item => item.localName == "img" || item.className == "classImg");
if (findIndex !== -1) {
wnd.removeChild(wnd.childNodes[findIndex]);
}
}
});
videoInfo.devH5List = [];
@ -131,7 +136,7 @@ const stopPlay = (currentWindowIndex, type) => {
if (controls) {
controls.classList.remove("video-controls_flex");
}
const findIndex = Array.from(wnd.childNodes).findIndex(item => item.localName == "img");
const findIndex = Array.from(wnd.childNodes).findIndex(item => item.localName == "img" || item.className == "classImg");
if (findIndex !== -1) {
wnd.removeChild(wnd.childNodes[findIndex]);
}
@ -225,7 +230,8 @@ const talkbackStart = currentWindowIndex => {
console.log(res2.data.url);
const url = res2.data.url.replace("hrdl.zhgdyun.com", "sp.szjxj.com");
player.JS_StartTalk(url, {
player
.JS_StartTalk(url, {
// irecordType: 1,
// cbStreamCB: this.streamcb,
})
@ -376,15 +382,15 @@ const initPlayer = () => {
if (controls) {
// if (iWndIndex > videoInfo.devH5List.length - 1) return;
// controls.classList.add("video-controls_flex");
const someFlag = Array.from(wnd.childNodes).some(item => {
return item.src;
});
console.log(11222233, someFlag);
const player_playVideo = wnd.querySelector(`#player_playVideo${iWndIndex}`);
const findFlag = Array.from(wnd.childNodes).find(item => {
return item.id && item.id.includes("player_playVideo");
});
if (player_playVideo.src || someFlag || findFlag.duration == "Infinity") {
const findImg = Array.from(wnd.childNodes).some(item => {
return item.className == "classImg";
});
// console.log(8848, findFlag, findImg, player_playVideo);
if (player_playVideo.src || findImg || findFlag.duration == "Infinity") {
controls.classList.add("video-controls_flex");
}
}
@ -507,28 +513,52 @@ const play = (row, index) => {
if (!index) {
index = 0;
}
const findIndex = Array.from(d1.childNodes).findIndex(item => item.localName == "img");
videoInfo.player.JS_Play(preUrl, param, index).then(
() => {
const findIndex = Array.from(d1.childNodes).findIndex(item => item.localName == "img");
if (findIndex !== -1) {
d1.removeChild(d1.childNodes[findIndex]);
console.log("播放成功", findIndex);
}
//
// d1.removeChild(d1.childNodes[d1.childNodes.length - 1]);
// console.log("");
},
err => {
console.log("播放失败");
im.src = errorPng;
const imgHeightNum = 20;
const imgWidthNum = 150;
im.style.width = imgWidthNum + "px";
im.style.height = imgHeightNum + "px";
const positionNum = imgHeightNum / 2 - imgHeightNum;
const positionWidthNum = imgWidthNum / 2 - imgWidthNum;
im.style.marginLeft = positionWidthNum + "px";
im.style.marginTop = positionNum + "px";
// im.src = errorPng;
// const imgHeightNum = 20;
// const imgWidthNum = 150;
// im.style.width = imgWidthNum + "px";
// im.style.height = imgHeightNum + "px";
// const positionNum = imgHeightNum / 2 - imgHeightNum;
// const positionWidthNum = imgWidthNum / 2 - imgWidthNum;
// im.style.marginLeft = positionWidthNum + "px";
// im.style.marginTop = positionNum + "px";
if (findIndex !== -1) {
d1.removeChild(d1.childNodes[findIndex]);
let ndiv = document.createElement("div"); //
const reloadBtn = document.createElement("div");
//
const reloadIcon = document.createElement("img");
reloadIcon.src = reloadImg; //
reloadBtn.textContent = "重新加载";
reloadBtn.addEventListener("click", (e) => {
e.stopPropagation();
onReload(row.id);
});
//
reloadBtn.appendChild(reloadIcon);
ndiv.innerHTML = `预览失败,请检查设备网络`;
ndiv.style.position = "absolute";
ndiv.style.top = "50%";
ndiv.style.left = "50%";
ndiv.style.transform = "translate(-50%, -50%)";
ndiv.className = "classImg";
ndiv.appendChild(reloadBtn);
d1.appendChild(ndiv); //div
}
}
);
videoInfo.iWndIndex =
@ -555,9 +585,14 @@ const play = (row, index) => {
);
});
};
/**
* 分屏这里我太懒了就循环了一个视频流
*/
const onReload = (id) => {
console.log("重新加载", id);
//
const findItem = videoInfo.devH5List.find(item => item.id === id);
if (findItem) {
play(findItem, videoInfo.iWndIndex);
}
};
const onTwoSubmit = num => {
// 24
videoInfo.numCount = num;
@ -660,6 +695,28 @@ onMounted(() => {
</script>
<style lang="scss" scoped>
:deep(.classImg) {
color: rgba(255, 255, 255, 0.5);
font-size: 16px;
width: calc(100% - 24px);
text-align: center;
padding: 0 12px;
z-index: 101;
> div {
color: rgb(255, 255, 255);
font-size: 12px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
margin-top: 6px;
> img {
width: 16px;
height: 16px;
margin-left: 8px;
}
}
}
.bgImage {
width: 18px;
height: 18px;