<template>
<div>
<div class="access">
<div class="access-system">
<TitleName text="平台接入系统" width="166px"></TitleName>
<div class="access-left flex-right">
<div class="access-left-item" v-for="(v, i) in access" :key="i">
<div class="access-left-text" @click="accessSystem(v.text)">{{ v.data }}</div>
<div class="access-left-item-text">{{ v.text }}</div>
</div>
</div>
</div>
<div class="access-statistics">
<TitleName text="平台接入统计" width="182px"></TitleName>
<div class="access-right flex-left">
<div class="access-left-item" v-for="(v2, i2) in total" :key="i2">
<div :class="v2.text == '单位总计' ? 'access-left-text' : 'access-left-text2'" @click="todevice(v2.text, '')">
{{ v2.data }}
</div>
<div class="access-left-item-text">{{ v2.text }}</div>
</div>
</div>
</div>
</div>
<!-- 添加CustomDialog组件 -->
<CustomDialog v-model:visible="elevatorVisible" :title="elevatorTitle">
<!-- 电梯监控列表 -->
<el-button type="primary" @click="openAddDialog" style="margin-top: -20px">新增</el-button>
<el-table :data="elevatorData" v-loading="elevatorloading">
<el-table-column property="name" show-overflow-tooltip label="名称" />
<el-table-column property="deviceCode" show-overflow-tooltip label="设备编号" />
<el-table-column property="status" label="状态" width="60">
<template #default="{ row }">
<!-- <el-icon :color="row.status === 1 ? 'green' : 'red'">
<SuccessFilled v-if="row.status === 1" />
<CircleCloseFilled v-else />
</el-icon> -->
{{ row.status === 1 ? '在线' : '离线' }}
</template>
</el-table-column>
<el-table-column property="createTime" :formatter="formatDateTime" label="创建时间" />
<el-table-column property="updateTime" :formatter="formatDateTime" label="修改时间" />
<el-table-column label="操作" width="200px">
<template #default="{ row }">
<el-button style="margin-left: 5px" size="small" @click="openPlay(row)">播放</el-button>
<el-button style="margin-left: 5px" size="small" @click="openEitd(row)">修改</el-button>
<el-button style="margin-left: 5px" size="small" @click="openwarning(row)">预警</el-button>
<!-- <el-button style="margin-left: 5px" @click="openwarning(row)" size="small">预警</el-button> -->
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<div class="pagination-container">
<el-pagination
v-model:current-page="pagination.currentPage"
v-model:page-size="pagination.pageSize"
:page-sizes="[5, 10, 20, 30]"
:total="pagination.total"
layout="total, sizes, prev, pager, next, jumper"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
</CustomDialog>
<!-- 新增 -->
<CustomDialog v-model:visible="addVisible" :title="addTitle" typeEit="" style="height: 350px; width: 650px">
<el-form label-width="120px" style="max-width: 600px; margin-top: 10px" :model="ruleForm" status-icon class="demo-ruleForm">
<el-form-item label="身份证" prop="id">
<el-input v-model="ruleForm.id" placeholder="请输入身份证号" />
</el-form-item>
<el-form-item label="名字" prop="name">
<el-input v-model="ruleForm.name" placeholder="名字" />
</el-form-item>
<el-form-item label="设备代码" prop="deviceCode">
<el-input v-model="ruleForm.deviceCode" placeholder="请输入设备代码" />
</el-form-item>
<el-form-item label="创建时间" prop="createTime">
<el-date-picker style="width: 480px" v-model="ruleForm.createTime" type="datetime" placeholder="请选择" />
</el-form-item>
<el-form-item label="更新时间" prop="updateTime">
<el-date-picker style="width: 480px" v-model="ruleForm.updateTime" type="datetime" placeholder="请选择" />
</el-form-item>
</el-form>
<div style="width: 95%; display: flex; justify-content: flex-end">
<el-button type="primary" style="background-color: #153b63" @click="submitForm">确定</el-button>
</div>
</CustomDialog>
<!-- 预警图片展示 -->
<CustomDialog v-model:visible="warningVisible" :title="warningTitle" :mountShow="mountShow">
<el-table :data="warningData" v-loading="warningLoading" height="600px">
<el-table-column property="deviceCode" show-overflow-tooltip label="设备编号" />
<el-table-column property="status" label="状态" width="60">
<template #default="{ row }">
{{ row.status === 1 ? '在线' : '离线' }}
</template>
</el-table-column>
<el-table-column prop="img" label="图片">
<template #default="{ row }">
<el-image
v-for="(item, idx) in row.img.split(',')"
:key="idx"
:src="`/img/${item}`"
:zoom-rate="1.2"
:max-scale="7"
:min-scale="0.2"
preview-teleported="true"
show-progress
:preview-src-list="row.img.split(',').map((i: any) => `/img/${i}`)"
fit="cover"
style="width: 50px; height: 50px"
/>
</template>
</el-table-column>
<el-table-column property="createTime" :formatter="formatDateTime" label="创建时间" />
</el-table>
<div class="pagination-container">
<el-pagination
v-model:current-page="warningPage.currentPage"
v-model:page-size="warningPage.pageSize"
:page-sizes="[5, 10, 20]"
:total="warningPage.total"
layout="total, sizes, prev, pager, next, jumper"
@size-change="warningSizeChange"
@current-change="warningCurrentChange"
/>
</div>
</CustomDialog>
<div class="center-video" v-if="open">
<vue3videoPlay
width="1200px"
height="700"
v-if="open"
style="object-fit: cover"
:autoPlay="options.autoPlay"
:control="options.control"
:loop="options.loop"
:muted="options.muted"
:src="`http://36.138.229.106:9092/hls/${videoUrl}.m3u8`"
type="m3u8"
/>
<el-button class="close-btn" @click="closePlay">关闭 X</el-button>
</div>
</div>
<!-- @click="todevice('', v2.text)" -->
</template>
<script lang="ts">
import { defineComponent, ref, defineEmits, onMounted, reactive, nextTick } from 'vue';
import TitleName from './TitleName.vue';
import { ElMessage, ElPopover, ElTooltip } from 'element-plus';
import { statisticsPlatform, statisticsPlatformResDataCurrent, statisticsSystemResDataCurrent } from '@/request/statisticsPlatform';
import { statisticsSystem } from '@/request/statisticsSystem';
import router from '@/router';
import CustomDialog from '@/components/CustomDialog.vue';
import vue3videoPlay from 'vue3-video-play'; // 引入npm i vue3-video-play --save
import 'vue3-video-play/dist/style.css'; // 引入css样式
import WarningView from '@/UnitHome/WarningView.vue';
import dayjs from 'dayjs';
export default defineComponent({
components: {
TitleName,
CustomDialog,
vue3videoPlay,
},
setup(props, context) {
const visible = ref(false);
const total = ref<any[]>([]);
const access = ref<any[]>([]);
const elevatorVisible = ref(false); // 控制弹窗显示
const elevatorTitle = ref('电梯视频监控'); // 弹窗标题
const elevatorData = ref(); //电梯监控表格
const elevatorloading = ref(true); //
const addVisible = ref(false); // 控制弹窗显示
const open = ref(false); // 电梯监控视频播放弹窗显示
const addTitle = ref<'新增电梯视频监控' | '修改电梯视频监控'>('新增电梯视频监控'); // 弹窗标题
const dialogType = ref<'add' | 'edit'>('add'); // 默认新增
const videoUrl = ref(''); //视频地址
const warningVisible = ref(false); // 预警弹窗
const warningTitle = ref('预警图片展示'); // 预警弹窗标题
const warningData = ref(); //预警列表
const warningLoading = ref(false); //预警loding
const mountShow = ref(false);
const videoPlayer = ref<HTMLVideoElement>();
interface VideoOptions {
autoPlay: boolean;
loop: boolean;
control: boolean;
muted: boolean;
type: string;
}
const options = ref<VideoOptions>({
type: 'm3u8', //视频类型
autoPlay: true, //是否自动播放
loop: false, //是否循环播放
control: true, //是否显示控制条
muted: true, //静音
});
// 表单字段信息
const ruleForm = ref({
id: '',
name: '',
deviceCode: '',
createTime: '',
updateTime: '',
});
// 分页参数
const pagination = ref({
currentPage: 1,
pageSize: 10,
total: 0,
});
// 分页参数
const warningPage = ref({
currentPage: 1,
pageSize: 10,
total: 0,
});
const resData = ref<statisticsSystemResDataCurrent>({
electronic: 0,
water: 0,
wireless: 0,
surveillance: 0,
fire: 0,
elevator: 0,
});
const resData2 = ref<statisticsPlatformResDataCurrent>({
unit: 0,
sensor: 0,
building: 0,
surveillance: 0,
equipment: 0,
camera: 0,
});
const iconClick = (v: any) => {
console.log(321);
};
const popoverData = ref({});
// 初始化数据
const fetchData = async () => {
statisticsPlatform().then((res: any) => {
resData2.value = res;
total.value = [
{
text: '单位总计',
icon: require('../res/17.png'),
data: resData2.value.unit,
},
{
text: '传感器总计',
icon: require('../res/16.png'),
data: resData2.value.sensor,
},
// {
// text: '建筑总计',
// icon: require('../res/15.png'),
// data: resData2.value.building,
// },
{
text: '监控点总计',
icon: require('../res/14.png'),
data: resData2.value.camera,
},
{
text: '消防设备总计',
icon: require('../res/13.png'),
data: resData2.value.equipment,
},
{
text: '视频设备总计',
icon: require('../res/12.png'),
data: resData2.value.surveillance,
},
];
});
statisticsSystem().then((res: any) => {
resData.value = res;
access.value = [
{
text: '电气火灾系统',
icon: require('../res/22.png'),
data: resData.value.electronic,
},
{
text: '消防水源系统',
icon: require('../res/21.png'),
data: resData.value.water,
},
{
text: '无线报警系统',
icon: require('../res/20.png'),
data: resData.value.wireless,
},
{
text: '视频监控系统',
icon: require('../res/19.png'),
data: resData.value.surveillance,
},
{
text: '火灾报警系统',
icon: require('../res/18.png'),
data: resData.value.fire,
},
{
text: '电梯视频监控',
icon: require('../res/18.png'),
data: resData.value.fire,
},
];
});
};
const todevice = (v: string, v2: string) => {
if (v.indexOf('总计') != -1 && v != '单位总计') {
return;
}
if (v == '单位总计') {
v = '';
}
sessionStorage.Record2Id = JSON.stringify({
id: '',
type: '',
deviceType: v,
page: 1,
limit: 20,
});
sessionStorage.deviceTitle = v2;
router.push('/unitdevice');
};
// 获取电梯数据(带分页)
const fetchElevatorData = async () => {
elevatorloading.value = true;
try {
const response = await fetch(
`http://36.138.229.106:9091/yuShiDevice/getList?pageSize=${pagination.value.pageSize}&pageNo=${pagination.value.currentPage}`
);
const res = await response.json();
elevatorData.value = res.data?.records || [];
pagination.value.total = res.data?.total || 0;
} catch (error) {
console.error('获取电梯数据失败:', error);
ElMessage.error('获取电梯数据失败');
} finally {
elevatorloading.value = false;
}
};
const handleSizeChange = (val: number) => {
pagination.value.pageSize = val;
fetchElevatorData();
};
// 当前页改变
const handleCurrentChange = (val: number) => {
pagination.value.currentPage = val;
fetchElevatorData();
};
// 电梯视频监控数据列表
const accessSystem = (v: string) => {
// 如果是电梯视频建监控,显示弹窗而不是跳转
if (v == '电梯视频监控') {
elevatorVisible.value = true;
// 重置分页参数
pagination.value.currentPage = 1;
pagination.value.pageSize = 10;
fetchElevatorData();
return;
}
};
// 事件格式转换
const formatDateTime = (row: any, column: any, cellValue: any) => {
if (!cellValue) return '-';
return dayjs(cellValue).format('YYYY-MM-DD HH:mm:ss');
};
// 新增设备弹窗打开
const openAddDialog = () => {
dialogType.value = 'add';
ruleForm.value = {
// 重置表单
id: '',
name: '',
deviceCode: '',
createTime: '',
updateTime: '',
};
addVisible.value = true;
return;
};
// 新增表单
const submitForm = async () => {
const data = ruleForm.value;
const opt = {
id: Number(data?.id),
name: data?.name,
deviceCode: data?.deviceCode,
// createTime: dayjs(data?.createTime).format('YYYY-MM-DD HH:mm:ss'),
// updateTime: dayjs(data?.updateTime).format('YYYY-MM-DD HH:mm:ss'),
};
if (ruleForm.value && dialogType.value == 'add') {
console.log('表单数据:', ruleForm.value);
// 新增保存接口
fetch('http://36.138.229.106:9091/yuShiDevice/deviceInset', {
method: 'POST', // 指定请求方法
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
...opt,
}),
})
.then((res: any) => {
addVisible.value = false;
ElMessage.success('新增成功');
fetchElevatorData();
})
.catch((error) => console.error('Error:', error));
} else {
// 修改接口
fetch('http://36.138.229.106:9091/yuShiDevice/deviceUpdate', {
method: 'POST', // 指定请求方法
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
...opt,
}),
})
.then((res: any) => {
addVisible.value = false;
ElMessage.success('修改成功');
fetchElevatorData();
})
.catch((error) => console.error('Error:', error));
}
};
// 修改设备
const openEitd = (data: any) => {
dialogType.value = 'edit';
addTitle.value = '修改电梯视频监控';
addVisible.value = true;
ruleForm.value = { ...data };
};
// 视频播放
const openPlay = (row: any) => {
fetch(`http://36.138.229.106:9091/yuShiDevice/yuShi?deviceId=${row.deviceCode}&type=${1}`)
.then((res: any) => {
videoUrl.value = row.deviceCode;
open.value = true;
})
.catch((error) => {
console.error('获取视频数据失败:', error);
ElMessage.error('获取视频数据失败');
}); // 错误处理
};
// 删除播放视频
const closePlay = async () => {
if (videoUrl.value) {
console.log(videoUrl.value, 'deviceCode.value');
open.value = false;
try {
const response = await fetch(`http://36.138.229.106:9091/yuShiDevice/yuShi?deviceId=${videoUrl.value}&type=${2}`);
console.log(response, 'response');
} catch (error) {
console.error('删除失败:', error);
ElMessage.error('删除视频数据失败');
}
}
};
const deviceCode = ref(); //
// 当前页改变
const warningCurrentChange = (val: number) => {
warningPage.value.currentPage = val;
fetcWarningData();
};
const warningSizeChange = (val: number) => {
warningPage.value.pageSize = val;
fetcWarningData();
};
// 获取预警数据
const fetcWarningData = async () => {
warningLoading.value = true;
try {
const response = await fetch(
`http://36.138.229.106:9091/yuShiDevice/alarmList?deviceId=${deviceCode.value}&pageSize=${warningPage.value.pageSize}&pageNo=${warningPage.value.currentPage}`
);
const res = await response.json();
warningLoading.value = false;
warningData.value = res.data?.records || [];
warningPage.value.total = res.data?.total || 0;
} catch (error) {
console.error('获取视频数据失败:', error);
ElMessage.error('获取视频数据失败');
}
};
// 预警操作
const openwarning = async (row: any) => {
warningVisible.value = true;
mountShow.value = true;
deviceCode.value = row.deviceCode;
// 重置分页参数
warningPage.value.currentPage = 1;
warningPage.value.pageSize = 10;
fetcWarningData();
};
// 组件挂载时获取数据
onMounted(fetchData);
return {
access,
total,
visible,
todevice,
accessSystem,
elevatorVisible,
elevatorTitle,
elevatorData,
elevatorloading,
formatDateTime,
pagination,
handleSizeChange,
handleCurrentChange,
openAddDialog,
addVisible,
addTitle,
ruleForm,
submitForm,
openEitd,
dialogType,
openPlay,
videoPlayer,
videoUrl,
open,
options,
closePlay,
warningVisible,
warningTitle,
openwarning,
warningData,
warningLoading,
warningSizeChange,
warningCurrentChange,
warningPage,
mountShow,
};
},
});
</script>
<style lang="scss" scoped>
.access {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 25px;
margin-bottom: 10px;
width: 356px;
box-sizing: border-box;
}
.access-system,
.access-statistics {
display: flex;
flex-direction: column;
// padding-left: 8px;
}
.access-system {
.access-left {
display: flex;
justify-content: space-around;
align-items: center;
flex-wrap: wrap;
}
}
.access-left-item {
white-space: nowrap;
height: 55px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
.access-left-text {
cursor: pointer;
color: #00ffff;
font-size: 18px;
border-bottom: 2px solid #00ffff;
}
.access-left-text2 {
cursor: pointer;
color: #00ffff;
font-size: 18px;
}
}
.access-left-item-box {
width: 37px;
height: 37px;
border-radius: 50%;
display: flex;
cursor: pointer;
justify-content: center;
align-items: center;
.access-left-item-icon {
width: 37px;
height: 37px;
border-radius: 50%;
}
}
.access-left-item-text {
width: 72px;
height: 14px;
font-size: 10px;
font-family: AlibabaPuHuiTi_2_55_Regular-, AlibabaPuHuiTi_2_55_Regular;
font-weight: normal;
color: #0283c2;
text-align: center;
margin-top: 8px;
line-height: 12px;
}
.access-left,
.access-right {
width: 165px;
height: 240px;
display: flex;
justify-content: space-around;
align-items: center;
flex-wrap: wrap;
background: linear-gradient(180deg, rgba(1, 76, 131, 0.145) 0%, rgba(1, 76, 131, 0.11) 100%);
border-radius: 3px 3px 3px 3px;
}
.flex-left {
align-self: flex-start;
}
.flex-right {
align-self: flex-end;
}
.popover {
width: 100px;
min-width: 0px !important;
position: relative;
height: 100px;
font-size: 12px;
background-color: #081343;
color: #ffffff;
text-align: center;
box-shadow: 1px 1px 8px 2px #00ffff;
border-radius: 3px;
left: -12px;
top: -13px;
.popover-hr {
height: 1px;
width: 100%;
background: linear-gradient(
90deg,
rgba(1, 76, 131, 0.6) 0%,
rgba(1, 76, 131, 0) 0%,
rgba(0, 255, 255, 1) 52%,
rgba(1, 76, 131, 0) 95%,
rgba(1, 76, 131, 0.5) 100%
);
}
.popover-hr2 {
position: absolute;
left: 187px;
top: 0;
height: 190px;
width: 1px !important;
background: linear-gradient(
180deg,
rgba(1, 76, 131, 0.6) 0%,
rgba(1, 76, 131, 0) 0%,
rgba(0, 255, 255, 1) 52%,
rgba(1, 76, 131, 0) 95%,
rgba(1, 76, 131, 0.5) 100%
);
}
.popover-hr3 {
position: absolute;
right: 187px;
top: 0px;
height: 190px;
width: 1px !important;
background: linear-gradient(
180deg,
rgba(1, 76, 131, 0.6) 0%,
rgba(1, 76, 131, 0) 0%,
rgba(0, 255, 255, 1) 52%,
rgba(1, 76, 131, 0) 95%,
rgba(1, 76, 131, 0.5) 100%
);
}
> div {
display: flex;
width: 100px;
height: 100px;
> div {
flex: 1;
padding-top: 26px;
}
}
.num {
cursor: pointer;
font-size: 17px;
color: #00ffff;
}
}
</style>
<style lang="scss" scoped>
/* 确保弹窗的 z-index 足够高 */
:deep(.el-dialog) {
z-index: 2000 !important;
}
/* 确保图片预览的 z-index 高于弹窗 */
:deep(.el-image-viewer__wrapper) {
z-index: 3000 !important;
}
// 视频样式
.center-video {
position: fixed;
top: 20%;
left: 17%;
z-index: 999999999;
padding: 30px;
background: rgba(0, 0, 0, 0.7); /* 可选:添加半透明背景 */
border-radius: 8px;
.close-btn {
position: absolute;
top: 10px; /* 调整与顶部的距离 */
right: 10px; /* 调整与右侧的距离 */
z-index: 99999999999;
background: rgba(255, 255, 255, 0.3);
border: none;
color: white;
padding: 5px 10px;
border-radius: 4px;
cursor: pointer;
}
.close-btn:hover {
background: rgba(255, 255, 255, 0.5);
}
}
/* 添加分页样式 */
.pagination-container {
margin-top: 12px;
display: flex;
justify-content: flex-end;
:deep(.el-pager li) {
background-color: #081242;
color: #014c83;
}
:deep(.el-pager li.is-active) {
color: #00ffff;
border: 1px solid;
}
:deep(.el-pagination button) {
background-color: #014c83;
color: #279ee0;
}
:deep(.el-pagination__goto) {
// background-color: #014c83;
font-size: 12px;
color: #279ee0;
}
:deep(.el-pagination__classifier) {
background-color: #014c83;
color: #279ee0;
}
:deep(.el-input__wrapper) {
background-color: #014c83;
color: #00ffff;
}
:deep(.el-input__inner) {
color: #00ffff;
}
:deep(.el-input) {
--el-input-border-color: #014c83;
}
}
</style>
上面代码vue3+ts优化vue3videoPlay 插件打开链接时候服务器上传还没成功有延迟 这种情况怎么解决