<template> <!-- 水质监测站详情弹窗 --> <div class="shuizhiModals"> <n-tabs type="line" @update:value="changeTab" animated> <n-tab-pane name="0" tab="监测详情"> <div class="szjcz"> <div class="contLeft"> <div class="title">站点基本信息</div> <div class="part"> <div class="detail" v-for="(val, keys, index) in detailName" :key="index"> <p>{{ val }}</p> <p v-if="keys != 'status'">{{ detailVal[keys] }}</p> <p v-if="keys == 'status'">{{ detailVal[keys] == 0 ? '在线' : '离线' }}</p> </div> </div> </div> <div class="contRight"> <div class="search"> <n-date-picker v-model:value="dateRange" @update:value="getLineTableData" type="daterange" /> <n-select :options="szOptions" v-model:value="yinziVal" placeholder="选择因子" @update:value="getLineTableData" /> </div> <div id="echartYz"></div> <div class="table"> <n-data-table :columns="columns" :data="tableData" :loading="tableLoading" :remote="true" :pagination="pagination" :bordered="false" /> </div> </div> </div> </n-tab-pane> <n-tab-pane name="1" v-if="detailVal.videoPathList.length > 0" tab="视频监控"> <div class="cameraBtn"> <n-button type="primary" @click="changeTab(2, item.key)" v-for="(item, index) in detailVal.videoPathList" :key="'a' + index"> {{ item.label }} </n-button> </div> <!-- 视频盒子 --> <div id="fxPlay" v-if="ifShowVideo" class="fxPlayBox"></div> </n-tab-pane> </n-tabs> </div> </template> <script> import { ref, toRefs, onMounted, reactive, onBeforeUnmount, nextTick } from 'vue'; import * as echarts from 'echarts'; import { lyInfoStationDetail, lyInfoStationLine, lyInfoStationTable } from '@/services'; import { formatDate } from '@/utils/util'; export default { name: 'shuizhiModals', props: { stationCode: String, }, setup(props, context) { console.log(props.stationCode); const allData = reactive({ ifShowVideo: false, tableLoading: false, tableData: [], columns: [ { key: 'ttTime', title: '时间' }, { key: 'value', title: '观测值' }, { key: 'waterLevelDesc', title: '水质评价' }, { key: 'assessmentScope', title: '水质评价范围' }, { key: 'alarmValue', title: '触发报警值' }, { key: 'warningValue', title: '触发预警值' }, ], dateRange: [Date.now() - 1 * 24 * 60 * 60 * 1000, Date.now()], yinziVal: 'codmn', szOptions: [ { value: 'codmn', label: 'COD(mg/L)' }, { value: 'ph', label: 'PH' }, { value: 'do', label: 'DO(mg/L)' }, { value: 'nh4n', label: 'NH3-N(mg/L)' }, { value: 'tp', label: 'TP(mg/L)' }, ], detailName: { stName: '站点名称', stCode: '站点编码', stationType: '站点类型', area: '河道名称', createTime: '安装时间', lon: '经度', lat: '纬度', platformName: '项目名称', person: '负责人', address: '安装地址', status: '站点状态', }, detailVal: { videoPathList: [], }, oWebControl: null, plugKey: '', Count: 0, width: 900, height: 480, showVideos: { cameraIndexCode: '4097ade6e64142d7ac7adff80b4617f0', // 监控点编号4097ade6e64142d7ac7adff80b4617f0 streamMode: 0, // 主子码流标识:0-主码流,1-子码流 transMode: 1, // 传输协议:0-UDP,1-TCP gpuMode: 0, // 是否启用GPU硬解,0-不启用,1-启用 wndId: 1, // 播放窗口序号 }, szOption: { legend: { show: false, }, grid: { left: '2%', right: '4%', bottom: '0%', top: '16%', containLabel: true, }, tooltip: { trigger: 'axis', }, xAxis: { type: 'category', data: ['06-01', '06-02', '06-03'], axisLabel: { textStyle: { color: '#B4C9E0', }, }, axisLine: { show: false, }, splitLine: { show: false, }, axisTick: { show: false, }, }, yAxis: { type: 'value', splitLine: { show: false, }, }, series: [ { name: '水质值', areaStyle: { normal: {} }, type: 'line', smooth: true, color: '#C72B31', data: [5, 7, 3], }, ], }, }); //分页 const paginationReactive = reactive({ page: 1, pageSize: 4, showSizePicker: true, pageSizes: [4, 20, 50], showQuickJumper: true, pageCount: 0, itemCount: 0, pageSlot: 5, prefix: () => { return '共 ' + paginationReactive.itemCount + ' 项'; }, onChange: (page) => { paginationReactive.page = page; getTableData(); }, onPageSizeChange: (pageSize) => { paginationReactive.pageSize = pageSize; paginationReactive.page = 1; getTableData(); }, }); // tab切换 const changeTab = (val, code) => { if (val == 0) { nextTick(() => { getLineTableData(); }); allData.ifShowVideo = false; if (allData.oWebControl) { allData.oWebControl.JS_HideWnd(); allData.oWebControl.JS_DestroyWnd(); allData.oWebControl = null; allData.ifShowVideo = false; } } else { allData.ifShowVideo = true; let video = { cameraIndexCode: code ? code : allData.detailVal.videoPathList[0].key, // 监控点编号4097ade6e64142d7ac7adff80b4617f0 streamMode: 0, // 主子码流标识:0-主码流,1-子码流 transMode: 1, // 传输协议:0-UDP,1-TCP gpuMode: 0, // 是否启用GPU硬解,0-不启用,1-启用 wndId: -1, // 播放窗口序号 }; allData.showVideos = video; if (allData.oWebControl == null) { initPlugin(); } else { allData.oWebControl.JS_HideWnd(); allData.oWebControl.JS_DestroyWnd(); allData.oWebControl = null; initPlugin(); } } }; /* 创建插件实例 */ function initPlugin() { const dll = { dllPath: './VideoPluginConnect.dll' }; allData.oWebControl = new WebControl({ szPluginContainer: 'fxPlay', // 指定容器id iServicePortStart: 15900, iServicePortEnd: 15909, szClassId: '23BF3B0A-2C56-4D97-9C03-0CB103AA8F11', // 用于IE10使用ActiveX的clsid cbConnectSuccess: () => { allData.oWebControl.JS_StartService('window', dll).then(() => { // 设置视频 web 插件消息回调 allData.oWebControl.JS_SetWindowControlCallback({ // cbIntegrationCallBack: msg => { } }); // 启动插件服务成功 allData.oWebControl.JS_CreateWnd('fxPlay', allData.width, allData.height).then(() => { // JS_CreateWnd创建视频播放窗口,宽高可设定 initVideo(allData.oWebControl); // 创建播放实例成功后初始化 console.log('启动插件成功!'); }); }); allData.oWebControl.oDocOffset.top = 60; }, // 插件服务启动失败,弹框提示 cbConnectError: () => { allData.oWebControl = null; allData.Count++; if (allData.Count < 2) { WebControl.JS_WakeUp('VideoWebPlugin://'); setTimeout(() => { initPlugin(); }, 8000); } else { } console.log('创建插件失败'); }, }); } /* 获取公钥 */ function initVideo(oWebControl) { const params = { funcName: 'getRSAPubKey', argument: JSON.stringify({ keyLength: 1024 }), }; oWebControl.JS_RequestInterface(params).then((res) => { if (res.responseMsg.data) { allData.plugKey = res.responseMsg.data; getVideoConfig(oWebControl); } }); } /* 视频插件配置 插件初始化 */ function getVideoConfig(oWebControl) { const configObj = { funcName: 'init', argument: JSON.stringify({ appkey: '26265267', // API网关提供的appkey secret: setEncrypt('CHtVgUwdLsJjE6CWYLMy'), // API网关提供的secret ip: '192.168.10.49', // API网关IP地址z playMode: 0, // 播放模式 0-预览,1-回放 port: 443, // 端口 snapDir: 'D:\\SnapDir', // 抓图存储路径 videoDir: 'D:\\VideoDir', // 紧急录像或录像剪辑存储路径 layout: '1x1', // 布局 enableHTTPS: 1, // 是否启用HTTPS协议 0https 1http encryptedFields: 'secret', // 加密字段 showToolbar: 1, // 是否显示工具栏 showSmart: 1, // 是否显示智能信息 showToolbar: 0, //是否显示工具栏 0不显示 1显示 // buttonIDs: "0,16,256,257,258,259,260,512,513,514,515,516,517,768,769", // 自定义工具条按钮 新的 }), }; oWebControl.JS_RequestInterface(configObj).then(() => { oWebControl.JS_Resize(allData.width, allData.height); getClickAction(); }); } /* 视频流RSA加密 */ function setEncrypt(value) { const encrypt = new JSEncrypt(); encrypt.setPublicKey(allData.plugKey); return encrypt.encrypt(value); } /* 视频预览 */ function getClickAction() { console.log(allData.showVideos, 'showVideos'); allData.oWebControl.JS_RequestInterface({ funcName: 'startPreview', argument: JSON.stringify(allData.showVideos), }); } // 折线图加载 let echartShuizhijc = null; const shuizhiEcharts = () => { echartShuizhijc = echarts.init(document.getElementById('echartYz')); echartShuizhijc.clear(); echartShuizhijc.setOption(allData.szOption); }; // 折线图表格数据加载 async function getLineTableData() { let params = { stCode: props.stationCode, property: allData.yinziVal, startTime: formatDate(allData.dateRange[0]), endTime: formatDate(allData.dateRange[1]), }; let res = await lyInfoStationLine(params); if (res && res.code == 1) { allData.szOption.xAxis.data = res.data.tt; allData.szOption.series[0].data = res.data.property; shuizhiEcharts(); } getTableData(); } async function getTableData() { let params = { current: paginationReactive.page, size: paginationReactive.pageSize, data: { stCode: props.stationCode, property: allData.yinziVal, startTime: formatDate(allData.dateRange[0]), endTime: formatDate(allData.dateRange[1]), }, }; let res = await lyInfoStationTable(params); if (res && res.code == 1) { allData.tableData = res.data.list; paginationReactive.pageCount = res.data.pages; paginationReactive.itemCount = res.data.total; } } // 加载基本信息数据 const getBaseData = async () => { let params = { stCode: props.stationCode, }; let res = await lyInfoStationDetail(params); if (res && res.code == 1) { let datas = res.data; allData.detailVal = datas; } }; onMounted(() => { getLineTableData(); getBaseData(); }); onBeforeUnmount(() => { if (allData.oWebControl != null) { allData.ifShowVideo = false; allData.oWebControl.JS_HideWnd(); allData.oWebControl.JS_DestroyWnd(); allData.oWebControl = null; } if (!!echartShuizhijc) echartShuizhijc.dispose(); }); return { ...toRefs(allData), pagination: paginationReactive, changeTab, getLineTableData, }; }, }; </script> <style lang="less"> .shuizhiModals { color: #b4c9e0; height: 575px; .title { font-size: 18px; font-weight: bold; } .szjcz { display: flex; margin: 10px 0px; .contLeft { width: 300px; .part { margin-top: 10px; .detail { width: 100%; display: flex; font-size: 14px; background: rgba(1, 27, 46, 0.6); p { height: 34px; line-height: 34px; width: 100%; } p:nth-of-type(1) { width: 35%; text-align: center; } p:nth-of-type(2) { flex: 1; text-align: left; } } .detail:nth-of-type(odd) { background: rgba(1, 27, 46, 0.3); } } } .contRight { flex: 1; padding-left: 20px; margin-left: 20px; border-left: 2px solid rgba(1, 27, 46, 0.5); .search { display: flex; .n-date-picker { width: 260px; margin: 0px 20px; } .n-select { width: 150px; } } #echartYz { margin-top: 10px; width: 580px; height: 200px; } .table { width: 100%; margin-top: 20px; max-height: 240px; overflow: auto; .n-data-table-td, .n-data-table-th { padding: 6px !important; text-align: center; } } } } .fxPlayBox { width: 900px; height: 480px; margin-top: 10px; } .cameraBtn { overflow: auto; height: 34px; .n-button { margin-right: 15px; } } .n-tabs-tab { font-size: 15px; } } </style>