320 lines
10 KiB
Vue
320 lines
10 KiB
Vue
|
<template>
|
||
|
<div class="bigBox">
|
||
|
<MOSTY.FromPage title="预制点选择" @closeDialog="black">
|
||
|
<template #content>
|
||
|
<div class="main">
|
||
|
<div class="left">
|
||
|
<video controls style="width:100%;height:100%;object-fit:fill" id='videoElement'></video>
|
||
|
</div>
|
||
|
<div class="right">
|
||
|
<MOSTY.Assort title="方向控制" style="padding: 10px" />
|
||
|
<div class="controller">
|
||
|
<div v-for="btn in directionButton" :key="btn.key"
|
||
|
:class="[btn.value !== 9 ? 'sector' : '', btn.key]" :title="btn.label"
|
||
|
@mousedown="handleRegulate(btn, 0)" @mouseup="handleRegulate(btn, 1)">
|
||
|
<el-icon>
|
||
|
<CaretLeft />
|
||
|
</el-icon>
|
||
|
</div>
|
||
|
</div>
|
||
|
<MOSTY.Assort title="预制点" style="padding: 10px" />
|
||
|
<div style="text-align: left;" class="ml10">
|
||
|
<el-button size="small" @click="createPrefabricatedPoints()">创建</el-button>
|
||
|
</div>
|
||
|
<el-table size="small" :data="pointList" style="width: 100%">
|
||
|
<el-table-column prop="presetPointName" label="名称" />
|
||
|
<el-table-column label="操作">
|
||
|
<template #default="{ row }">
|
||
|
<span class="operedit" @click="selectPoint(row)">
|
||
|
<el-icon>
|
||
|
<Position />
|
||
|
</el-icon>选择</span>
|
||
|
</template>
|
||
|
</el-table-column>
|
||
|
</el-table>
|
||
|
</div>
|
||
|
</div>
|
||
|
</template>
|
||
|
<template #footer>
|
||
|
<el-button @click="black"> <el-icon>
|
||
|
<DocumentDelete />
|
||
|
</el-icon>关闭</el-button>
|
||
|
</template>
|
||
|
</MOSTY.FromPage>
|
||
|
</div>
|
||
|
</template>
|
||
|
<script setup>
|
||
|
import { ref, onMounted, getCurrentInstance, reactive } from "vue";
|
||
|
import * as MOSTY from "@/components/MyComponents/index";
|
||
|
import { ElMessage, ElMessageBox } from "element-plus"
|
||
|
import { getApi, postApi } from "@/api/tobAcco_api.js";
|
||
|
const { proxy } = getCurrentInstance();
|
||
|
let cameraDirectionControl = reactive({ stepX: 4, stepY: 4 })
|
||
|
let listOfPreFabricatedCameraPoints = ref([])
|
||
|
let hls = null
|
||
|
let pointList = ref([])
|
||
|
const props = defineProps({
|
||
|
sbbh: {
|
||
|
type: String,
|
||
|
default: '1003105'
|
||
|
},
|
||
|
tddh: {
|
||
|
type: String,
|
||
|
default: '1003105$1$0$0'
|
||
|
}
|
||
|
});
|
||
|
const directionButton = reactive([
|
||
|
{ label: "上", key: "up", value: 1 },
|
||
|
{ label: "右上", key: "up-right", value: 7 },
|
||
|
{ label: "右", key: "right", value: 4 },
|
||
|
{ label: "右下", key: "down-right", value: 8 },
|
||
|
{ label: "下", key: "down", value: 2 },
|
||
|
{ label: "左下", key: "down-left", value: 6 },
|
||
|
{ label: "左", key: "left", value: 3 },
|
||
|
{ label: "左上", key: "up-left", value: 5 },
|
||
|
{ label: "中心", key: "center", value: 9 }]
|
||
|
)
|
||
|
const emit = defineEmits("saveSuccess");
|
||
|
onMounted(() => {
|
||
|
initVideo(props.tddh)
|
||
|
getPointList()
|
||
|
});
|
||
|
|
||
|
/**
|
||
|
* 创建预设点
|
||
|
*/
|
||
|
const createPrefabricatedPoints = () => {
|
||
|
ElMessageBox.prompt("输入预制点名称", "预制点创建", {
|
||
|
confirmButtonText: "确认",
|
||
|
cancelButtonText: "取消",
|
||
|
inputPattern: /\S/,
|
||
|
inputErrorMessage: "预制点名称不能为空"
|
||
|
}).then(({ value }) => {
|
||
|
let data = {
|
||
|
presetPointCode: generateUniqueCode(listOfPreFabricatedCameraPoints.value),
|
||
|
operateType: 3,
|
||
|
presetPointName: value
|
||
|
}
|
||
|
cameraPreFabricationPointSetting(data)
|
||
|
}).catch((err) => {
|
||
|
ElMessage({
|
||
|
type: "info",
|
||
|
message: "Input canceled"
|
||
|
})
|
||
|
})
|
||
|
}
|
||
|
/**
|
||
|
* 设置功能预制点
|
||
|
* @param data
|
||
|
*/
|
||
|
const cameraPreFabricationPointSetting = async (data) => {
|
||
|
await postApi({ ...data, channelId: props.tddh }, "/mosty-lps/daSdk/operatePresetPoint")
|
||
|
await getPointList()
|
||
|
}
|
||
|
/**
|
||
|
* 生成唯一编码
|
||
|
* @param presetArray
|
||
|
* @returns {string}
|
||
|
*/
|
||
|
function generateUniqueCode(presetArray) {
|
||
|
const existingCodes = new Set(presetArray.map(item => item.presetPointCode))
|
||
|
let code
|
||
|
do {
|
||
|
const num = Math.floor(Math.random() * 256) + 1
|
||
|
code = num.toString().padStart(3, "0")
|
||
|
if (existingCodes.size >= 256) throw new Error("所有可能的编码都已被占用")
|
||
|
} while (existingCodes.has(code))
|
||
|
return code
|
||
|
}
|
||
|
/**
|
||
|
* 保存预制点
|
||
|
* @param data
|
||
|
*/
|
||
|
const selectPoint = (data) => {
|
||
|
emit("saveSuccess", data);
|
||
|
}
|
||
|
/**
|
||
|
* 获取预置点
|
||
|
* @param id
|
||
|
*/
|
||
|
const getPointList = async () => {
|
||
|
let data = await postApi({ channelId: props.tddh }, "/mosty-lps/daSdk/getPresetPoints")
|
||
|
if (data instanceof Array) pointList.value = data
|
||
|
}
|
||
|
/**
|
||
|
* 摄像头方向控制
|
||
|
* @param btn
|
||
|
* @param type
|
||
|
*/
|
||
|
const handleRegulate = async (btn, type) => {
|
||
|
cameraDirectionControl.channelId = props.tddh
|
||
|
cameraDirectionControl.command = !type ? 1 : 0
|
||
|
cameraDirectionControl.direct = btn.value
|
||
|
await postApi(cameraDirectionControl, "/mosty-lps/daSdk/operateDirect")
|
||
|
}
|
||
|
/**
|
||
|
* 初始化摄像头设备
|
||
|
* @param channelId
|
||
|
* @param domId
|
||
|
*/
|
||
|
const initVideo = async (channelId) => {
|
||
|
if (hls !== null) hls.destroy()
|
||
|
const videoElement = document.getElementById("videoElement")
|
||
|
//调用Hls.isSupported()检查浏览器是否支持MSE
|
||
|
let data = await postApi({ channelId, "streamType": 1, type: "hls" }, "/mosty-lps/daSdk/realtime")
|
||
|
if (proxy?.$Hls.isSupported()) {
|
||
|
hls = new Hls()
|
||
|
hls.attachMedia(videoElement)
|
||
|
hls.on(Hls.Events.MEDIA_ATTACHED, async () => {
|
||
|
hls.loadSource(data)
|
||
|
hls.on(Hls.Events.MANIFEST_PARSED, () => videoElement.play())
|
||
|
})
|
||
|
return
|
||
|
} else if (videoElement.canPlayType("application/vnd.apple.mpegurl")) {
|
||
|
// 如果支持原生播放
|
||
|
videoElement.src = data
|
||
|
videoElement.addEventListenter("canplay", () => videoElement.play())
|
||
|
return
|
||
|
}
|
||
|
ElMessage.error("当前环境不支持初始化")
|
||
|
}
|
||
|
const black = () => {
|
||
|
emit("saveSuccess");
|
||
|
};
|
||
|
|
||
|
</script>
|
||
|
|
||
|
<style lang="scss" scoped>
|
||
|
.bigBox {
|
||
|
.main {
|
||
|
height: 100%;
|
||
|
display: flex;
|
||
|
|
||
|
.left {
|
||
|
width: 75%;
|
||
|
height: 100%;
|
||
|
border: 1px solid grey;
|
||
|
}
|
||
|
|
||
|
.right {
|
||
|
width: 25%;
|
||
|
height: 100%;
|
||
|
border: 1px solid grey;
|
||
|
|
||
|
.controller {
|
||
|
margin: 0 auto;
|
||
|
position: relative;
|
||
|
width: 200px;
|
||
|
height: 200px;
|
||
|
display: flex;
|
||
|
justify-content: center;
|
||
|
align-items: center;
|
||
|
transform: rotate(22deg);
|
||
|
box-shadow: var(--panel-head-box-shadow);
|
||
|
border-radius: 50%;
|
||
|
z-index: 9;
|
||
|
|
||
|
.sector {
|
||
|
width: 194px;
|
||
|
height: 194px;
|
||
|
background-color: #f6f4f4;
|
||
|
clip-path: polygon(50% 50%, 100% 50%, 100% 3%);
|
||
|
border-radius: 50%;
|
||
|
position: absolute;
|
||
|
cursor: pointer;
|
||
|
|
||
|
&:hover {
|
||
|
background-color: var(--core-hover-color);
|
||
|
}
|
||
|
|
||
|
.el-icon {
|
||
|
position: absolute;
|
||
|
right: 20px;
|
||
|
top: 63px;
|
||
|
transform: rotate(-200deg);
|
||
|
color: black;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* 各个方向的扇形按钮 */
|
||
|
.up {
|
||
|
transform: rotate(270deg);
|
||
|
}
|
||
|
|
||
|
.right {
|
||
|
transform: rotate(0deg);
|
||
|
|
||
|
.el-icon {
|
||
|
transform: rotate(-202deg);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
.down-right {
|
||
|
transform: rotate(45deg);
|
||
|
}
|
||
|
|
||
|
.down {
|
||
|
transform: rotate(90deg);
|
||
|
|
||
|
.el-icon {
|
||
|
transform: rotate(-204deg);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
.down-left {
|
||
|
transform: rotate(135deg);
|
||
|
}
|
||
|
|
||
|
.left {
|
||
|
transform: rotate(180deg);
|
||
|
|
||
|
.el-icon {
|
||
|
transform: rotate(-200deg);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
.up-left {
|
||
|
transform: rotate(225deg);
|
||
|
}
|
||
|
|
||
|
.up {
|
||
|
transform: rotate(270deg);
|
||
|
}
|
||
|
|
||
|
.up-right {
|
||
|
transform: rotate(315deg);
|
||
|
}
|
||
|
|
||
|
/* 中心的圆形空心区域 */
|
||
|
.center {
|
||
|
position: absolute;
|
||
|
transform: translate(-50%, -50%);
|
||
|
/* 将中心定位到控制器的中心 */
|
||
|
width: 65px;
|
||
|
height: 65px;
|
||
|
top: 100px;
|
||
|
left: 100px;
|
||
|
background-color: var(--core-button-normal-bg-color);
|
||
|
border: 1px solid var(--core-border-color);
|
||
|
border-radius: 50%;
|
||
|
box-shadow: 0 0 5px rgba(0, 0, 0, 0.3);
|
||
|
z-index: 10;
|
||
|
/* 确保中心区域在其他扇形之上 */
|
||
|
display: flex;
|
||
|
align-items: center;
|
||
|
justify-content: center;
|
||
|
cursor: pointer;
|
||
|
|
||
|
&:hover {
|
||
|
background-color: var(--core-hover-color);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
display: block !important;
|
||
|
padding: 0 14px;
|
||
|
box-sizing: border-box;
|
||
|
}
|
||
|
</style>
|