Browse Source

大屏

master
yxq 3 months ago
parent
commit
c20177e7bc
42 changed files with 2645 additions and 1 deletions
  1. BIN
      SafeCampus.WEB/public/static/screen/img/bottomleft.png
  2. BIN
      SafeCampus.WEB/public/static/screen/img/bottomleft1.png
  3. BIN
      SafeCampus.WEB/public/static/screen/img/bottomright.png
  4. BIN
      SafeCampus.WEB/public/static/screen/img/bottomright1.png
  5. BIN
      SafeCampus.WEB/public/static/screen/img/circle.png
  6. BIN
      SafeCampus.WEB/public/static/screen/img/greenLight.png
  7. BIN
      SafeCampus.WEB/public/static/screen/img/greenbg.png
  8. BIN
      SafeCampus.WEB/public/static/screen/img/header-aside-left.png
  9. BIN
      SafeCampus.WEB/public/static/screen/img/header-aside-right.png
  10. BIN
      SafeCampus.WEB/public/static/screen/img/numaside.png
  11. BIN
      SafeCampus.WEB/public/static/screen/img/numbg.png
  12. BIN
      SafeCampus.WEB/public/static/screen/img/numborder.png
  13. BIN
      SafeCampus.WEB/public/static/screen/img/numborder1.png
  14. BIN
      SafeCampus.WEB/public/static/screen/img/numborder2.png
  15. BIN
      SafeCampus.WEB/public/static/screen/img/redLight.png
  16. BIN
      SafeCampus.WEB/public/static/screen/img/redbg.png
  17. BIN
      SafeCampus.WEB/public/static/screen/img/setting.png
  18. BIN
      SafeCampus.WEB/public/static/screen/img/title-bg.png
  19. BIN
      SafeCampus.WEB/public/static/screen/img/title-bg_aside.png
  20. BIN
      SafeCampus.WEB/public/static/screen/img/titleIcon.png
  21. BIN
      SafeCampus.WEB/public/static/screen/img/topleft.png
  22. BIN
      SafeCampus.WEB/public/static/screen/img/topleft1.png
  23. BIN
      SafeCampus.WEB/public/static/screen/img/topright.png
  24. BIN
      SafeCampus.WEB/public/static/screen/img/topright1.png
  25. BIN
      SafeCampus.WEB/public/static/screen/img/yellowLight.png
  26. BIN
      SafeCampus.WEB/public/static/screen/img/yellowbg.png
  27. BIN
      SafeCampus.WEB/public/static/screen/testimg/7.png
  28. +1
    -0
      SafeCampus.WEB/src/api/modules/index.ts
  29. +54
    -0
      SafeCampus.WEB/src/api/modules/screen/index.ts
  30. +26
    -1
      SafeCampus.WEB/src/routers/modules/staticRouter.ts
  31. +230
    -0
      SafeCampus.WEB/src/views/screen/classroom.vue
  32. +108
    -0
      SafeCampus.WEB/src/views/screen/component/classroom/classAnalysis.vue
  33. +92
    -0
      SafeCampus.WEB/src/views/screen/component/classroom/classNotice.vue
  34. +104
    -0
      SafeCampus.WEB/src/views/screen/component/classroom/classStatistics.vue
  35. +130
    -0
      SafeCampus.WEB/src/views/screen/component/classroom/stuEnterList.vue
  36. +377
    -0
      SafeCampus.WEB/src/views/screen/component/header.vue
  37. +383
    -0
      SafeCampus.WEB/src/views/screen/component/index/peopeleNum.vue
  38. +234
    -0
      SafeCampus.WEB/src/views/screen/component/index/todayInfo.vue
  39. +230
    -0
      SafeCampus.WEB/src/views/screen/component/index/todayNotice.vue
  40. +226
    -0
      SafeCampus.WEB/src/views/screen/component/index/video.vue
  41. +95
    -0
      SafeCampus.WEB/src/views/screen/index.vue
  42. +355
    -0
      SafeCampus.WEB/src/views/screen/stureturn.vue

BIN
SafeCampus.WEB/public/static/screen/img/bottomleft.png View File

Before After
Width: 39  |  Height: 40  |  Size: 233 B

BIN
SafeCampus.WEB/public/static/screen/img/bottomleft1.png View File

Before After
Width: 26  |  Height: 26  |  Size: 239 B

BIN
SafeCampus.WEB/public/static/screen/img/bottomright.png View File

Before After
Width: 40  |  Height: 40  |  Size: 236 B

BIN
SafeCampus.WEB/public/static/screen/img/bottomright1.png View File

Before After
Width: 26  |  Height: 26  |  Size: 263 B

BIN
SafeCampus.WEB/public/static/screen/img/circle.png View File

Before After
Width: 345  |  Height: 351  |  Size: 11 KiB

BIN
SafeCampus.WEB/public/static/screen/img/greenLight.png View File

Before After
Width: 38  |  Height: 49  |  Size: 1.5 KiB

BIN
SafeCampus.WEB/public/static/screen/img/greenbg.png View File

Before After
Width: 305  |  Height: 131  |  Size: 2.4 KiB

BIN
SafeCampus.WEB/public/static/screen/img/header-aside-left.png View File

Before After
Width: 1690  |  Height: 11  |  Size: 610 B

BIN
SafeCampus.WEB/public/static/screen/img/header-aside-right.png View File

Before After
Width: 1672  |  Height: 9  |  Size: 517 B

BIN
SafeCampus.WEB/public/static/screen/img/numaside.png View File

Before After
Width: 12  |  Height: 158  |  Size: 365 B

BIN
SafeCampus.WEB/public/static/screen/img/numbg.png View File

Before After
Width: 110  |  Height: 164  |  Size: 1.2 KiB

BIN
SafeCampus.WEB/public/static/screen/img/numborder.png View File

Before After
Width: 34  |  Height: 42  |  Size: 1.9 KiB

BIN
SafeCampus.WEB/public/static/screen/img/numborder1.png View File

Before After
Width: 34  |  Height: 42  |  Size: 1.8 KiB

BIN
SafeCampus.WEB/public/static/screen/img/numborder2.png View File

Before After
Width: 34  |  Height: 42  |  Size: 1.9 KiB

BIN
SafeCampus.WEB/public/static/screen/img/redLight.png View File

Before After
Width: 58  |  Height: 72  |  Size: 2.1 KiB

BIN
SafeCampus.WEB/public/static/screen/img/redbg.png View File

Before After
Width: 305  |  Height: 131  |  Size: 2.4 KiB

BIN
SafeCampus.WEB/public/static/screen/img/setting.png View File

Before After
Width: 50  |  Height: 50  |  Size: 1.1 KiB

BIN
SafeCampus.WEB/public/static/screen/img/title-bg.png View File

Before After
Width: 998  |  Height: 108  |  Size: 85 KiB

BIN
SafeCampus.WEB/public/static/screen/img/title-bg_aside.png View File

Before After
Width: 158  |  Height: 4  |  Size: 174 B

BIN
SafeCampus.WEB/public/static/screen/img/titleIcon.png View File

Before After
Width: 344  |  Height: 55  |  Size: 3.9 KiB

BIN
SafeCampus.WEB/public/static/screen/img/topleft.png View File

Before After
Width: 39  |  Height: 40  |  Size: 229 B

BIN
SafeCampus.WEB/public/static/screen/img/topleft1.png View File

Before After
Width: 26  |  Height: 26  |  Size: 235 B

BIN
SafeCampus.WEB/public/static/screen/img/topright.png View File

Before After
Width: 40  |  Height: 40  |  Size: 209 B

BIN
SafeCampus.WEB/public/static/screen/img/topright1.png View File

Before After
Width: 26  |  Height: 26  |  Size: 240 B

BIN
SafeCampus.WEB/public/static/screen/img/yellowLight.png View File

Before After
Width: 58  |  Height: 72  |  Size: 2.5 KiB

BIN
SafeCampus.WEB/public/static/screen/img/yellowbg.png View File

Before After
Width: 305  |  Height: 131  |  Size: 2.3 KiB

BIN
SafeCampus.WEB/public/static/screen/testimg/7.png View File

Before After
Width: 174  |  Height: 174  |  Size: 59 KiB

+ 1
- 0
SafeCampus.WEB/src/api/modules/index.ts View File

@@ -22,3 +22,4 @@ export * from "./statistion";
export * from "./usermanage";
export * from "./attendance";
export * from "./violation";
export * from "./screen"

+ 54
- 0
SafeCampus.WEB/src/api/modules/screen/index.ts View File

@@ -0,0 +1,54 @@
/**
* @description
* @license Apache License Version 2.0
* @Copyright (c) 2022-Now 少林寺驻北固山办事处大神父王喇嘛
* @remarks
* SimpleAdmin 基于 Apache License Version 2.0 协议发布,可用于商业项目,但必须遵守以下补充条款:
* 1.请不要删除和修改根目录下的LICENSE文件。
* 2.请不要删除和修改SimpleAdmin源码头部的版权声明。
* 3.分发源码时候,请注明软件出处 https://gitee.com/dotnetmoyu/SimpleAdmin
* 4.基于本软件的作品,只能使用 SimpleAdmin 作为后台服务,除外情况不可商用且不允许二次分发或开源。
* 5.请不得将本软件应用于危害国家安全、荣誉和利益的行为,不能以任何形式用于非法为目的的行为不要删除和修改作者声明。
* 6.任何基于本软件而产生的一切法律纠纷和责任,均于我司无关
* @see https://gitee.com/dotnetmoyu/SimpleAdmin
*/
import { moduleRequest } from "@/api/request";
const http = moduleRequest("/large/screen/");

/**
* @Description: 单页管理
* @Author: yxq
* @Date: 2023-12-15 15:34:54
*/
const screenApi = {
/** 获取大屏首页数据 */
getHomeData(params: any={}) {
return http.get("getHomeData", params);
},
/** 获取大屏学生归寝数据 */
getStudentReturnBed(params: any={}) {
return http.get("getStudentReturnBed", params);
},
/** 获取大屏智慧课堂数据 */
getSmartClassroom(params: any={}) {
return http.get("getSmartClassroom", params);
},
/** 获取宿舍楼 */
getNoPageList(params: any={}) {
return http.get("getNoPageList", params);
},
/** 获取班级列表 */
getPersonSetNoPageList(params: any={}) {
return http.get("getPersonSetNoPageList", params);
},
/** 获取单页详情 */
detail(params: any) {
return http.get("getStartVideoLive", params);
},
/** 获取单页详情 */
stopUrl(params: any) {
return http.get("getStopVideoLive", params);
},
};

export { screenApi };

+ 26
- 1
SafeCampus.WEB/src/routers/modules/staticRouter.ts View File

@@ -55,7 +55,32 @@ export const staticRouter: RouteRecordRaw[] = [
component: () => import("@/views/violation/portrait/detail.vue")
}
]
}
},
// AI智能预警分析平台
{
path: "/screen",
name: "AI智能预警分析平台",
component: () => import("@/views/screen/index.vue"),
meta: {
title: "AI智能预警分析平台"
},
},
{
name: "AI智能预警分析平台-智慧课堂",
meta: {
title: "AI智能预警分析平台-智慧课堂"
},
path: "/screen/classroom",
component: () => import("@/views/screen/classroom.vue")
},
{
name: "AI智能预警分析平台-学生归寝",
meta: {
title: "AI智能预警分析平台-学生归寝"
},
path: "/screen/stureturn",
component: () => import("@/views/screen/stureturn.vue")
},
];

/**


+ 230
- 0
SafeCampus.WEB/src/views/screen/classroom.vue View File

@@ -0,0 +1,230 @@
<template>
<div class="fullscreen-container">
<myHeader place="1" ref="myHeaderRef" @getClassRoomInfo="getClassRoomInfo"></myHeader>
<div style="position: absolute; width: 100%; display: flex; justify-content: space-between; top: 102px">
<img style="width: 846px" src="/static/screen/img/header-aside-left.png" alt="" />
<img style="width: 845px" src="/static/screen/img/header-aside-right.png" alt="" />
</div>
<main v-if="ready">
<div class="main-top">
<div class="tearchBox myborder">
<div class="headimgBox">
<div class="imgBox">
<img src="/static/screen/testimg/7.png" alt="" />
</div>
<div class="title">{{ classRoom.teacher }}老师</div>
<div class="status" :style="{ backgroundColor: classRoom.isClassing ? '#11ca2e' : '#b1b3b8' }">
{{ classRoom.isClassing ? "正在上课" : "未上课" }}
</div>
<!-- <img class="setting" src="/static/screen/img/setting.png" alt="" /> -->
</div>
<div class="classTime">
<div>
<div>上课时间</div>
<div class="time">{{ classRoom.classTime }}</div>
</div>
<div>
<div>下课时间</div>
<div class="time">{{ classRoom.classBreakTime }}</div>
</div>
</div>
</div>
<div class="video">
<!-- <img style="width: 100%; height: 100%" src="/static/screen/testimg/3.png" alt="" /> -->
<iframe :src="rtsUrl" frameborder="0" style="width: 100%; height: 100%"></iframe>
</div>
<div class="stuEnterList myborder">
<stuEnterList :screenData="screenData"></stuEnterList>
</div>
</div>
<div class="main-bottom">
<div class="classStatistics myborder">
<classStatistics :screenData="screenData"></classStatistics>
</div>
<div class="classNotice myborder">
<classNotice :screenData="screenData"></classNotice>
</div>
<div class="classAnalysis myborder">
<classAnalysis :screenData="screenData" />
</div>
</div>
</main>
</div>
</template>
<script setup>
import myHeader from "./component/header.vue";
import stuEnterList from "./component/classroom/stuEnterList.vue";
import classNotice from "./component/classroom/classNotice.vue";
import classAnalysis from "./component/classroom/classAnalysis.vue";
import classStatistics from "./component/classroom/classStatistics.vue";
import { screenApi } from "@/api";
const myHeaderRef = ref(null);
const screenData = ref({});
const classInfo = ref({});
const ready = ref(false);
const classRoom = ref({});
const alarmList = ref([]);
const rollCall = ref([]);
const statisti = ref({
labale: [],
value: []
});
const getClassRoomInfo = obj => {
classInfo.value = obj;
// 归寝人数信息
ready.value = false;
screenApi.getSmartClassroom({ personSetId: obj.value }).then(res => {
if (res.code == 200) {
screenData.value = res.data;
classRoom.value = screenData.value.classRoom;
let classTime = new Date(classRoom.value.classTime).valueOf();
let classBreakTime = new Date(classRoom.value.classBreakTime).valueOf();
let currTime = new Date().valueOf();
if (currTime > classTime && currTime < classBreakTime) {
classRoom.value.isClassing = true;
}
showRts(classRoom.value.cameraId);
classInfo.value = screenData.value.classInfo;
alarmList.value = screenData.value.alarmList;
rollCall.value = screenData.value.rollCall;
statisti.value = screenData.value.statisti;
console.log(res.data);
ready.value = true;
nextTick(() => {
myHeaderRef.value.setBoder();
});
}
});
};
// 监控
const streamId = ref("");
const videoToken = ref("");
const rtsUrl = ref("");
const sensorId = ref("");
const showRts = sensorId_ => {
if (sensorId_) {
if (sensorId.value == sensorId_) return;
if (streamId.value) stopUrl();
let rtsUrl_ = "/static/rtsPlayer.html?height=556px&rtsUrl=";
sensorId.value = sensorId_;
screenApi.detail({ sensorId: sensorId_ }).then(res => {
if (res.code == 200) {
if (res.data.rtsPullStreamUrls[0]) {
rtsUrl.value = rtsUrl_ + res.data.rtsPullStreamUrls[0].url;
}
streamId.value = res.data.streamId;
videoToken.value = res.data.videoToken;
}
});
}
};
const stopUrl = () => {
if (!streamId.value) return;
screenApi.stopUrl({
sensorId: sensorId.value,
streamId: streamId.value,
videoToken: videoToken.value
});
};
onUnmounted(() => {
stopUrl();
});
</script>
<style lang="scss" scoped>
main {
padding: 41px 53px 0px 58px;
.main-top {
height: 556px;
display: flex;
justify-content: space-between;
> div {
height: 100%;
box-sizing: border-box;
}
.tearchBox {
width: 208px;
.headimgBox {
box-sizing: border-box;
border-bottom: 1px solid #2e84e5;
height: 304px;
text-align: center;
position: relative;
.setting {
position: absolute;
top: 37px;
right: 24px;
width: 25px;
}
.imgBox {
margin-top: 83px;
width: 87px;
height: 87px;
border-radius: 50%;
overflow: hidden;
display: inline-block;
img {
width: 100%;
height: 100%;
}
}
.title {
color: #fff;
font-size: 18px;
margin-top: 16px;
}
.status {
margin-top: 16px;
display: inline-block;
border-radius: 4px;
width: 67px;
height: 26px;
color: #fff;
font-size: 12px;
line-height: 26px;
}
}
.classTime {
color: #78dfff;
text-align: center;
font-size: 14px;
> div {
margin-top: 50px;
.time {
margin-top: 11px;
}
}
}
}
.video {
width: 872px;
}
.stuEnterList {
width: 1338px;
padding: 35px 40px;
}
}
.main-bottom {
height: 652px;
margin-top: 36px;
display: flex;
justify-content: space-between;
> div {
height: 100%;
box-sizing: border-box;
}
.classStatistics {
width: 579px;
padding: 35px 37px;
}
.classNotice {
width: 1095px;
padding: 30px 40px;
}
.classAnalysis {
width: 742px;
padding: 30px 45px;
}
}
}
</style>

+ 108
- 0
SafeCampus.WEB/src/views/screen/component/classroom/classAnalysis.vue View File

@@ -0,0 +1,108 @@
<template>
<div style="width: 100%; height: 100%">
<div class="commontitle">
<div class="left">
<span>课堂行为人数分析</span>
<img src="/static/screen/img/titleIcon.png" alt="" />
</div>
<div class="right"></div>
</div>
<div style="width: 100%; height: 450px; margin-top: 40px">
<div ref="chart1" style="width: 100%; height: 100%"></div>
</div>
</div>
</template>
<script setup>
import * as echarts from "echarts";
let chart1 = ref(null);
const props = defineProps({
screenData: {}
});
const statisti = ref({});
onMounted(() => {
statisti.value = props.screenData.statisti;
getCharts1();
});
const getCharts1 = () => {
const chart = echarts.init(chart1.value);
let data = statisti.value.labale.map((e, i) => {
return {
name: e,
value: statisti.value.value[i]
};
});
data.push({
name: "正常听讲",
value: props.screenData.normalClass
});
let option = {
color: ["#14C9C9", "#165DFF", "#CBE0FF", "#9FDB1D", "#C42ED1", "#F7BA1E"],
legend: {
show: true,
itemGap: 20,
textStyle: {
color: "#2E84E5",
fontSize: 12
}
},
tooltip: {
trigger: "item"
},
animation: true,
title: {
text: props.screenData.studentList.length,
subtext: "班级总人数",
left: "center",
top: "52%",

textStyle: {
color: "#78DFFF",
fontSize: 24,
fontWeight: "600"
},
subtextStyle: {
color: "#2E84E5",
fontSize: 14
}
},

series: [
{
tooltip: { show: false },
type: "pie",
radius: ["0", "38%"],
padAngle: 0,
color: ["#182665"],
data: [
{
name: "",
value: 1
}
],
top: 80,
label: {
show: false
}
},
{
type: "pie",
radius: ["70%", "90%"],
padAngle: 5,
data,
top: 80,
label: {
color: "#FFF",
formatter: "{b0}: {c}({d}%)"
}
}
]
};

chart.setOption(option);
window.addEventListener("resize", function () {
chart.resize();
});
};
</script>
<style lang="scss" scoped>
</style>

+ 92
- 0
SafeCampus.WEB/src/views/screen/component/classroom/classNotice.vue View File

@@ -0,0 +1,92 @@
<template>
<div style="width: 100%; height: 100%">
<div class="commontitle">
<div class="left">
<span>学员课堂告警情况</span>
<img src="/static/screen/img/titleIcon.png" alt="" />
</div>
<div class="right">
<img src="/static/screen/img/redLight.png" alt="" />
<span>告警数量({{ alarmList.length }})</span>
</div>
</div>
<div style="height: 568px; overflow: auto; margin-top: 4px">
<ul>
<li v-for="item in alarmList" :key="item.id">
<div class="name">{{ item.personName || "未知" }}</div>
<div class="imgbox">
<el-image :src="item.snapshotUrl" fit="cover" :preview-src-list="[item.snapshotUrl]"></el-image>
</div>
<div class="title">{{ item.alarmTypeDesc }}</div>
</li>
</ul>
<div v-if="alarmList.length == 0" style="text-align: center; margin-top: 160px; color: #78dfff; font-size: 14px">暂无告警</div>
</div>
</div>
</template>
<script setup>
const props = defineProps({
screenData: {}
});
const alarmList = ref([]);
onMounted(() => {
alarmList.value = props.screenData.alarmList || [];
});
</script>
<style scoped lang="scss">
.commontitle {
.right {
color: #ffa1a1;
font-size: 14px;
img {
width: 18px;
position: relative;
top: 4px;
right: 6px;
}
}
}
ul {
display: flex;
flex-wrap: wrap;
li {
width: 129.366px;
height: 173.506px;
background: #182665;
padding: 0 10px;
box-sizing: border-box;
padding-top: 6px;
margin-right: 16px;
margin-bottom: 7.8px;
margin-top: 7.8px;
&:nth-child(7n) {
margin-right: 0px;
}
.name {
color: #fff;
text-align: center;
font-size: 14px;
}
.imgbox {
margin-top: 5px;
width: 109.702px;
height: 112.44px;
object-fit: cover;
text-align: center;
.el-image {
height: 100%;
width: 100%;
}
}
.title {
background: #0c4dcf;
width: 109.702px;
height: 23px;
color: #fff;
text-align: center;
font-size: 14px;
line-height: 23px;
}
}
}
</style>

+ 104
- 0
SafeCampus.WEB/src/views/screen/component/classroom/classStatistics.vue View File

@@ -0,0 +1,104 @@
<template>
<div style="width: 100%; height: 100%">
<div class="commontitle">
<div class="left">
<span>课堂行为次数统计</span>
<img src="/static/screen/img/titleIcon.png" alt="" />
</div>
<div class="right"></div>
</div>
<div style="width: 100%; height: 500px; margin-top: 40px">
<div ref="chart1" style="width: 540px; height: 500px"></div>
</div>
</div>
</template>
<script setup>
import * as echarts from "echarts";
const props = defineProps({
screenData: {}
});
const statisti = ref({});
let chart1 = ref(null);
onMounted(() => {
statisti.value = props.screenData.statisti;
getCharts1();
});
const getCharts1 = () => {
const chart = echarts.init(chart1.value);
let data = statisti.value.labale.map((e, i) => {
return {
name: e,
value: statisti.value.value[i]
};
});
let data_ = data.map(e => e.name);
let color = ["#14C9C9", "#165DFF", "#CBE0FF", "#9FDB1D", "#C42ED1", "#F7BA1E"];
data = data.map((e, i) => {
e.itemStyle = {
color: color[i % color.length]
};
return e;
});
let option = {
textStyle: {
color: "#fff",
fontSize: 12
},
grid: {
show: false,
left: "12%"
},
tooltip: {
show: true,
trigger: "item" //触发类型
},
animation: true,
xAxis: {
type: "value", //坐标轴类型
position: "top", //x 轴的位置 bottom top
minInterval: 1,
axisLine: {
show: true
},
splitLine: {
show: true,
lineStyle: {
color: "#666",
type: "dashed"
}
},
axisTick: {
show: true,
inside: false
}
},
yAxis: {
type: "category",
name: "行为",
nameLocation: "start",
position: "left",
axisTick: false,
axisLabel: {
show: true,
width: 100,
overflow: "break"
},
data: data_
},
series: [
{
type: "bar",
barWidth: 7,
data
}
]
};

chart.setOption(option);
window.addEventListener("resize", function () {
chart.resize();
});
};
</script>
<style lang="scss" scoped>
</style>

+ 130
- 0
SafeCampus.WEB/src/views/screen/component/classroom/stuEnterList.vue View File

@@ -0,0 +1,130 @@
<template>
<div style="width: 100%; height: 100%">
<div class="commontitle">
<div class="left">
<span>学员课堂出勤</span>
<img src="/static/screen/img/titleIcon.png" alt="" />
</div>
<div class="right"></div>
</div>
<div style="margin-top: 12px; overflow: auto; height: 448px">
<ul>
<li v-for="(item, index) in studentList" :key="index">
<div class="left">
<el-image :src="item.faceUrl" fit="cover" :preview-src-list="[item.faceUrl]"></el-image>
<!-- <img :src="item.faceUrl" alt="" /> -->
</div>
<div class="right">
<div class="name">{{ item.name }}</div>
<div class="stucode">学号:{{ item.personId }}</div>
<div class="enterTime">进班时间:{{ item.insTime }}</div>
<div class="statusBox">
<div class="status" :style="{ backgroundColor: item.state != '正常' ? '#9a112c' : '#123CA0' }">
<div class="circle"></div>
<div>{{ item.state }}</div>
</div>
<img v-if="item.state != '正常'" src="/static/screen/img/redLight.png" alt="" />
</div>
</div>
</li>
</ul>
</div>
</div>
</template>
<script setup>
const props = defineProps({
screenData: {}
});
const studentList = ref([]);
onMounted(() => {
studentList.value = props.screenData.studentList;
studentList.value = studentList.value.map(e => {
e.faceUrl = e.faces.length ? e.faces[0].faceUrl : "";
return e;
});
});
</script>
<style scoped lang="scss">
ul {
display: flex;
flex-wrap: wrap;
li {
display: flex;
width: 398.372px;
height: 121px;
background: #182665;
margin-right: 26px;
margin-bottom: 28px;
margin-top: 14px;
margin-bottom: 14px;
&:nth-child(3n) {
margin-right: 0px;
}
.left {
width: 131px;
height: 100%;
box-sizing: border-box;
position: relative;
padding-top: 14px;
text-align: center;
&::after {
content: "";
display: block;
position: absolute;
width: 1px;
height: 93px;
right: 0;
top: 12px;
background: #1848bc;
}
.el-image {
height: 93px;
width: 73px;
}
}
.right {
flex: 1;
padding-left: 29px;
padding-top: 15px;
padding-right: 16px;
.name {
color: #fff;
font-size: 14px;
line-height: 1.5;
}
.stucode {
color: #78dfff;
font-size: 12px;
line-height: 1.5;
margin-top: 2px;
}
.enterTime {
color: #78dfff;
font-size: 12px;
line-height: 1.5;
}
.statusBox {
display: flex;
justify-content: space-between;
margin-top: 10px;
.status {
width: 82px;
height: 22px;
color: #fff;
font-size: 12px;
display: flex;
justify-content: space-between;
line-height: 20px;
align-content: center;
padding-left: 9px;
padding-right: 16px;
box-sizing: border-box;
}
img {
width: 15px;
}
}
}
}
}
</style>

+ 377
- 0
SafeCampus.WEB/src/views/screen/component/header.vue View File

@@ -0,0 +1,377 @@
<template>
<header>
<div class="left">
<div>
<el-select style="width: 321px" class="header-select" v-model="place" :teleported="false" placeholder="场景选择" @change="placeChange">
<el-option v-for="item in placeOptions" :value="item.value" :label="item.label" :key="item.value"></el-option>
</el-select>
<el-select
v-if="place == 1"
style="width: 186px; margin-left: 14px"
class="header-select"
v-model="classValue"
:teleported="false"
placeholder="班级选择"
@change="classChange"
>
<el-option v-for="item in classoptions" :value="item.value" :label="item.label" :key="item.value"></el-option>
</el-select>
<el-select
v-if="place == 2"
style="width: 186px; margin-left: 14px"
class="header-select"
v-model="buildId"
:teleported="false"
placeholder="宿舍楼选择"
@change="buildIdChange"
>
<el-option v-for="item in dormitoryOptions" :value="item.value" :label="item.label" :key="item.value"></el-option>
</el-select>
</div>
</div>
<div class="middle">
<img style="width: 79px; height: 2px; position: relative; top: 98px; left: 46px" src="/static/screen/img/title-bg_aside.png" alt="" />
<img src="/static/screen/img/title-bg.png" alt="" />
<img style="width: 79px; height: 2px; position: relative; top: 98px; right: 42px" src="/static/screen/img/title-bg_aside.png" alt="" />
<h2>AI智能预警分析平台</h2>
</div>
<div class="right">
<div class="time">{{ currentDate }}</div>
<img src="/static/screen/img/greenLight.png" alt="" />
</div>
</header>
</template>
<script setup>
import router from "@/routers";
import { screenApi } from "@/api";
const emit = defineEmits(["getStuReturnInfo", "getClassRoomInfo"]);
const props = defineProps({
place: "1"
});
// 场景选择
const placeOptions = ref([
{ value: "0", label: "场景选择", url: "/screen" },
{ value: "1", label: "智慧课堂", url: "/screen/classroom" },
{ value: "2", label: "学生归寝", url: "/screen/stureturn" }
]);
const place = ref("");
if (props.place) place.value = props.place;
const placeChange = value => {
let obj = placeOptions.value.find(e => e.value == value);
router.push(obj.url);
};
// 班级选择
const classValue = ref("");
const classoptions = ref([]);
screenApi.getPersonSetNoPageList().then(res => {
if (res.code == 200) {
classoptions.value = res.data.map(e => {
e.label = e.personSetName;
e.value = e.personSetId;
return e;
});
classValue.value = classoptions.value[0].value;
emit("getClassRoomInfo", classoptions.value[0]);
}
});
const classChange = () => {
let obj = classoptions.value.find(e => e.value == classValue.value);
emit("getClassRoomInfo", obj);
};
// 宿舍楼选择
const buildId = ref("");
const dormitoryOptions = ref([]);
screenApi.getNoPageList().then(res => {
if (res.code == 200) {
dormitoryOptions.value = res.data.map(e => {
e.label = e.name;
e.value = e.id;
return e;
});
buildId.value = dormitoryOptions.value[0].value;
emit("getStuReturnInfo", dormitoryOptions.value[0]);
}
});
const buildIdChange = () => {
let obj = dormitoryOptions.value.find(e => e.value == buildId.value);
emit("getStuReturnInfo", obj);
};
// 当前时间
const currentDate = ref("");
onMounted(() => {
// 时间
setInterval(() => {
const now = new Date();
let hours = now.getHours();
let minutes = now.getMinutes();
let seconds = now.getSeconds();

// 添加零以确保时、分、秒是两位数
let hour = hours < 10 ? "0" + hours : hours;
let minute = minutes < 10 ? "0" + minutes : minutes;
let second = seconds < 10 ? "0" + seconds : seconds;

currentDate.value = `${now.getFullYear()}-${now.getMonth() + 1}-${now.getDate()} ${hour}:${minute}:${second}`;
}, 1000);
// 边框
setBoder();
setBoder1();
});
onBeforeMount(() => {
handleResize();
window.addEventListener("resize", handleResize);
});
onBeforeUnmount(() => {
window.removeEventListener("resize", handleResize);
});
// 边框添加
const setBoder = () => {
let borderEle = document.querySelectorAll(".myborder");
borderEle.forEach(e => {
e.style.cssText = "position:relative;border: 1px solid #2e84e5;background: linear-gradient(179deg, #081340 -54.28%, #111e55 124.4%);";
let borderImgs = document.createElement("div");
borderImgs.style.cssText = `position: absolute;top: -3px;left: -3px;width: calc(100% + 6px);;height: calc(100% + 6px);;display: flex;justify-content: space-between;flex-flow: column;pointer-events:none`;
borderImgs.innerHTML = `<div style="width: 100%;display: flex;justify-content: space-between;">
<img src="/static/screen/img/topleft.png" alt="" />
<img src="/static/screen/img/topright.png" alt="" /></div>
<div style="width: 100%;display: flex;justify-content: space-between;"><img src="/static/screen/img/bottomleft.png" alt="" />
<img src="/static/screen/img/bottomright.png" alt="" /></div>`;
e.append(borderImgs);
});
};
// 边框添加1
const setBoder1 = () => {
let borderEle = document.querySelectorAll(".myborder1");
borderEle.forEach(e => {
if (e.classList.contains("hasSetBoder")) return;
e.classList.add("hasSetBoder");
e.style.cssText = "position:relative;border: 1px solid #2e84e5;background: linear-gradient(179deg, #081340 -54.28%, #111e55 124.4%);";
let borderImgs = document.createElement("div");
borderImgs.style.cssText = `position: absolute;top: -2px;left: -2px;width: calc(100% + 4px);;height: calc(100% + 4px);;display: flex;justify-content: space-between;flex-flow: column;pointer-events:none`;
borderImgs.innerHTML = `<div style="width: 100%;display: flex;justify-content: space-between;">
<img src="/static/screen/img/topleft1.png" alt="" style="width:12.7px;height:12.7px"/>
<img src="/static/screen/img/topright1.png" alt="" style="width:12.7px;height:12.7px"/></div>
<div style="width: 100%;display: flex;justify-content: space-between;"><img src="/static/screen/img/bottomleft1.png" alt="" style="width:12.7px;height:12.7px"/>
<img src="/static/screen/img/bottomright1.png" alt="" style="width:12.7px;height:12.7px"/></div>`;
e.append(borderImgs);
});
};
// 屏幕宽度响应
const handleResize = () => {
// 设计稿: 1920 * 1080
// 目标适配: 1920 * 1080 3840 * 2160 ( 2 * 2 ) ; 7680 * 2160( 4 * 2)

// 1.设计稿的尺寸
let targetX = 2560;
let targetY = 1440;
// let targetX = 1920;
// let targetY = 1080;
let targetRatio = 16 / 9; // 宽高比率

// 2.拿到当前设备(浏览器)的宽度
let currentX = document.documentElement.clientWidth || document.body.clientWidth;
let currentY = document.documentElement.clientHeight || document.body.clientHeight;
// 1920 * 1080 -> 3840 * 2160

// 3.计算缩放比例
let scaleRatio = currentX / targetX; // 参照宽度进行缩放 ( 默认情况 )
let currentRatio = currentX / currentY; // 宽高比率

// 超宽屏
if (currentRatio > targetRatio) {
// 如果当前设备的宽高比率大于设计稿的宽高比率,那么就以高度为参照进行缩放,并且居中显示
scaleRatio = currentY / targetY;
document.body.style = `width:${targetX}px; height:${targetY}px;position:fixed;transform: scale(${scaleRatio}) translateX(-50%);left:50%;transform-origin: left top;`;
} else {
// 4.开始缩放网页
document.body.style = `width:${targetX}px; height:${targetY}px; transform: scale(${scaleRatio});transform-origin: left top;`;
}
};
defineExpose({
setBoder,
setBoder1,
classValue,
buildId
});
</script>
<style lang="scss" scoped>
.header-select {
:deep(.el-select__wrapper) {
border: 1px solid #35c9f0;
background: #0c4dcf;
width: 100%;
height: 55px;
outline: none;
}
:deep(.el-popper) {
position: absolute !important;
top: 58px !important;
left: 0px !important;
}
:deep(.el-select-dropdown__item) {
color: #0c4dcf;
font-size: 20px;
height: 55px;
line-height: 55px;
}
:deep(.el-select__placeholder) {
background: linear-gradient(9deg, #10cefe 7.44%, #fff 79.62%);
background-clip: text;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
font-size: 20px;
}
}
header {
position: relative;
display: flex;
justify-content: space-between;
box-sizing: border-box;
padding-left: 58px;
padding-right: 53px;
> * {
flex: 1;
}
.left {
padding-top: 25px;
}
.middle {
display: flex;
justify-content: center;
position: relative;
img {
width: 997.5px;
height: 108px;
}
h2 {
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
margin: 0 auto;
text-align: center;
line-height: 108px;
background: linear-gradient(90deg, #10cefe 0.82%, #10cefe 13.05%, #81e1ff 50.75%, #10cefe 98.64%);
background-clip: text;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
font-family: "Microsoft YaHei UI";
font-size: 48px;
font-style: normal;
font-weight: 700;
width: 444px;
border-bottom: 3px solid #10cefe;
}
}
.right {
display: flex;
justify-content: right;
img {
width: 38px;
height: 48.384px;
margin-top: 29px;
margin-right: 30px;
}
.time {
color: #cce3fe;
margin-top: 44px;
margin-right: 80px;
}
}
}
</style>
<style lang="scss">
ul:not(.el-scrollbar__view) {
list-style-type: none;
margin: 0;
padding: 0;
li {
margin: 0;
padding: 0;
}
}
.circle {
width: 5px;
height: 5px;
background: #fff;
border-radius: 50%;
position: relative;
top: 8px;
}
.commontitle {
width: 100%;
display: flex;
justify-content: space-between;
> .left {
display: flex;
padding-top: 6px;
font-weight: 700;
img {
width: 171.859px;
height: 27.139px;
}
span {
height: 35px;
color: #78dfff;
font-family: "Microsoft YaHei UI";
font-size: 24px;
line-height: 1;
margin-right: 16px;
}
}
}
.commonTitle1 {
width: 100%;
display: flex;
justify-content: space-between;
.left {
color: #fff;
font-size: 18px;
font-weight: 700;
}
.right {
position: relative;
bottom: 3px;
color: #78dfff;
font-size: 14px;
span {
display: inline-block;
color: #78dfff;
width: 40px;
font-size: 20px;
position: relative;
top: 2px;
text-align: right;
}
}
}
.fullscreen-container {
font-size: 20px;
height: 100%;
background: #08123e;
}
:deep(.el-select) {
border: none;
}
.borderImgs {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
justify-content: space-between;
flex-flow: column;
> div {
width: 100%;
display: flex;
justify-content: space-between;
}
}
::-webkit-scrollbar-thumb {
background-color: #3877f2; /* 滑块的背景色 */
border-radius: 10px; /* 滑块的圆角 */
// border: 2px solid #ffffff; /* 滑块边框 */
}
</style>

+ 383
- 0
SafeCampus.WEB/src/views/screen/component/index/peopeleNum.vue View File

@@ -0,0 +1,383 @@
<template>
<div style="width: 100%; height: 100%">
<ul>
<li>
<span class="title">学生总人数:</span>
<div class="numbox">
<div v-for="(item, index) in studentPersonNum.totalNum" :key="index">
<span class="num">{{ item }}</span>
</div>
</div>
<img src="/static/screen/img/numaside.png" alt="" />
</li>
<li>
<span class="title">男生人数:</span>
<div class="numbox">
<div v-for="(item, index) in studentPersonNum.maleNum" :key="index">
<span class="num">{{ item }}</span>
</div>
</div>
<img src="/static/screen/img/numaside.png" alt="" />
</li>
<li>
<span class="title">女生人数:</span>
<div class="numbox">
<div v-for="(item, index) in studentPersonNum.femaleNum" :key="index">
<span class="num">{{ item }}</span>
</div>
</div>
</li>
</ul>
<div style="display: flex; justify-content: space-around; padding: 25px 0">
<div style="width: 175px; height: 175px; position: relative; display: flex; justify-content: center; align-items: center">
<div ref="chart1" style="width: 160px; height: 160px"></div>
<img src="/static/screen/img/circle.png" style="position: absolute; top: 0; left: 0; width: 175px; height: 175px" alt="" />
</div>
<div style="width: 175px; height: 175px; position: relative; display: flex; justify-content: center; align-items: center">
<div ref="chart2" style="width: 160px; height: 160px"></div>
<img src="/static/screen/img/circle.png" style="position: absolute; top: 0; left: 0; width: 175px; height: 175px" alt="" />
</div>
</div>
<div class="commontitle">
<div class="left">
<span>设备统计</span>
<img src="/static/screen/img/titleIcon.png" alt="" />
</div>
<div class="right">
设备总数:<span>{{ count }}</span>
</div>
</div>

<div style="width: 100%; height: 200px">
<div ref="chart3" style="width: 100%; height: 100%"></div>
</div>
</div>
</template>
<script setup>
const props = defineProps({
screenData: {}
});
import * as echarts from "echarts";
let chart1 = ref(null);
let chart2 = ref(null);
let chart3 = ref(null);
onMounted(() => {
getstudentPersonNum();
getCharts1();
getCharts2();
getCharts3();
});
// 学生人数
const studentPersonNumKeyValue = { femaleNum: "女生人数", maleNum: "男生人数", totalNum: "学生总人数" };
const studentPersonNum_ = reactive({
femaleNum: 0,
maleNum: 0,
totalNum: 0
});
const studentPersonNum = reactive({
femaleNum: [],
maleNum: [],
totalNum: []
});
const getstudentPersonNum = () => {
studentPersonNum_.value = props.screenData.studentPersonNum;
for (const key in studentPersonNum_.value) {
let item = studentPersonNum_.value[key];
let arr = item.toString().split("");
function initArr(arr) {
if (arr.length < 4) {
arr.unshift("0");
}
if (arr.length < 4) {
return initArr(arr);
}
return arr;
}
arr = initArr(arr);
studentPersonNum[key] = arr;
}
};
const getCharts1 = () => {
const chart = echarts.init(chart1.value);
let arr = [];
for (const key in studentPersonNum_.value) {
if (key == "femaleNum") {
arr[0] = {
name: studentPersonNumKeyValue[key],
value: studentPersonNum_.value[key]
};
}
if (key == "maleNum") {
arr[1] = {
name: studentPersonNumKeyValue[key],
value: studentPersonNum_.value[key]
};
}
}
var title = ((studentPersonNum_.value.maleNum * 100) / studentPersonNum_.value.totalNum).toFixed(0) + "%";
let option = {
backgroundColor: "#021841",
animation: true,
title: {
text: title,
subtext: "男",
x: "center",
y: "35%",
textStyle: {
color: "#D5C54A",
fontSize: 25,
fontWeight: "600",
align: "center"
// "width": "200px"
},
subtextStyle: {
color: "#fff",
fontSize: 20,

fontWeight: "normal",
align: "center"
}
},

series: [
{
type: "pie",
center: ["50%", "50%"],
radius: ["80%", "90%"],
color: ["#154076", "#18EAFE"],
labelLine: {
normal: {
length: 25
}
},
label: {
normal: {
show: false
}
},
data: arr
}
]
};

chart.setOption(option);
window.addEventListener("resize", function () {
chart.resize();
});
};
const getCharts2 = data => {
const chart = echarts.init(chart2.value);
let arr = [];
for (const key in studentPersonNum_.value) {
if (key == "maleNum") {
arr[0] = {
name: studentPersonNumKeyValue[key],
value: studentPersonNum_.value[key]
};
}
if (key == "femaleNum") {
arr[1] = {
name: studentPersonNumKeyValue[key],
value: studentPersonNum_.value[key]
};
}
}
var title = ((studentPersonNum_.value.femaleNum * 100) / studentPersonNum_.value.totalNum).toFixed(0) + "%";
let option = {
backgroundColor: "#021841",
animation: true,
title: {
text: title,
subtext: "女",
x: "center",
y: "35%",
textStyle: {
color: "#D5C54A",
fontSize: 25,
fontWeight: "600",
align: "center"
},
subtextStyle: {
color: "#fff",
fontSize: 20,

fontWeight: "normal",
align: "center"
}
},

series: [
{
type: "pie",
center: ["50%", "50%"],
radius: ["80%", "90%"],
color: ["#154076", "#E74B3C"],
startAngle: 270,
labelLine: {
normal: {
length: 25
}
},
label: {
normal: {
show: false
}
},
data: arr
}
]
};

chart.setOption(option);
window.addEventListener("resize", function () {
chart.resize();
});
};
// 设备统计
const count = ref(0);
const getCharts3 = () => {
const chart = echarts.init(chart3.value);
let data = props.screenData.camera;
let xArr = data.map(e => e.cameraInfos.length);
xArr.forEach(element => {
count.value += element;
});
let yArr = data.map(e => {
return {
value: e.name
};
});
let option = {
tooltip: {
show: true,
trigger: "item" //触发类型
},
textStyle: {
color: "rgba(255, 255, 255, 0.64)"
},
grid: {
left: "3%",
right: "6%",
bottom: "3%",
top: "10%",
containLabel: true
},
xAxis: {
type: "value",
name: "个",
axisLabel: {
show: true,
fontSize: 12
},
splitLine: {
show: true,
lineStyle: {
color: "rgba(255, 255, 255, 0.15)"
}
}
},
yAxis: {
type: "category",
data: yArr,
axisTick: false,
axisLabel: {
show: true,
color: "rgba(255, 255, 255, 0.64)",
fontSize: 12,
overflow: "break",
width: 100
}
},
series: [
{
name: "设备统计",
type: "bar",
barWidth: "11px",
itemStyle: {
color: {
type: "linear",
colorStops: [
{ offset: 0, color: "rgba(27,99,204,0.9)" },
{ offset: 1, color: "rgba(3,223,252,0.9)" }
]
}
},
data: xArr
}
]
};

chart.setOption(option);
window.addEventListener("resize", function () {
chart.resize();
});
};
</script>
<style scoped lang="scss">
ul {
display: flex;
justify-content: space-between;
li {
position: relative;
.title {
color: #fff;
font-family: "Microsoft YaHei UI";
font-size: 20px;
}
.numbox {
margin-top: 18px;
display: flex;
color: #fff;
text-align: center;
font-family: "Microsoft YaHei UI";
font-size: 28px;
> div {
background: url("/static/screen/img/numborder.png") 100% 100% no-repeat;
width: 34px;
height: 42px;
line-height: 42px;
margin-right: 5px;
}
> div:last-child {
margin-right: 0px;
}
}
img {
width: 6px;
height: 79px;
position: absolute;
right: -36px;
top: 6px;
}
}
li:nth-child(2) {
.title {
color: #00fff4;
}
.numbox {
> div {
background: url("/static/screen/img/numborder1.png") 100% 100% no-repeat;
}
}
}
li:nth-child(3) {
.title {
color: #fde0b9;
}
.numbox {
> div {
background: url("/static/screen/img/numborder2.png") 100% 100% no-repeat;
}
}
}
}
.commontitle .right {
color: #79bde5;
font-size: 16px;
padding-top: 8px;
span {
font-size: 18px;
font-weight: 700;
}
}
</style>

+ 234
- 0
SafeCampus.WEB/src/views/screen/component/index/todayInfo.vue View File

@@ -0,0 +1,234 @@
<template>
<div style="width: 100%; height: 100%">
<div class="commontitle">
<div class="left">
<span>今日告警信息</span>
<img src="/static/screen/img/titleIcon.png" alt="" />
</div>
<div class="right">
<el-select
style="width: 160px"
class="header-select nobg"
v-model="place1"
:teleported="false"
@change="placeChange"
placeholder="场景选择"
clearable
>
<el-option v-for="item in placeOptions" :value="item.value" :label="item.label" :key="item.value"></el-option>
</el-select>
</div>
</div>
<div class="todayBoticeTotal myborder1">
<div
style="
width: 40px;
height: 40px;
border: 1px solid #ab2626;
border-radius: 50%;
text-align: center;
position: relative;
bottom: 3px;
margin-right: 14px;
"
>
<img src="/static/screen/img/redLight.png" alt="" style="width: 22px; margin-top: 5px" />
</div>
<div>
今日<span>7</span>个场景预警总计:<span class="total">{{ count }}</span> 起
</div>
</div>
<div style="width: 100%; height: calc(100% - 136px); overflow: auto; margin-top: 29px">
<ul>
<template v-for="item in warnList" :key="item.id">
<li>
<div class="left">
<el-image :src="item.snapshotUrl" fit="cover" :preview-src-list="[item.snapshotUrl]"></el-image>
<!-- <img :src="item.snapshotUrl" alt="" /> -->
</div>
<div class="right">
<div class="time">{{ item.tick }}</div>
<div v-if="item.warnHand == 0" class="status" style="background-color: #9a112c">
<div class="circle"></div>
<div>未处理</div>
</div>
<div class="title">{{ item.alarmTypeDesc }}</div>
<div class="des">
<div>所属位置:{{ item.cameraName }}</div>
<div>所属场景:{{ item.cameraGroup }}</div>
</div>
</div>
</li>
</template>
<template v-if="!warnList.length">
<div style="color: #10cefe; text-align: center; margin-top: 18px; font-size: 14px">暂无数据</div>
</template>
</ul>
</div>
</div>
</template>
<script setup>
const props = defineProps({
screenData: {}
});
// 场景选择
const placeOptions = ref([]);
placeOptions.value = props.screenData.camera.map(e => {
return {
value: e.id,
label: e.name
};
});
const place1 = ref("");
const count = ref(0);
// 列表
const warnList = ref([]);
const warnList_copy = ref([]);
const getwarnList = () => {
warnList.value = props.screenData.alarmStatisti.warnList;
warnList_copy.value = JSON.parse(JSON.stringify(warnList.value));
count.value = warnList.value.length;
};
const placeChange = () => {
let obj = placeOptions.value.find(e => e.value == place1.value);
if (obj) {
warnList.value = warnList_copy.value.filter(e => e.cameraGroup == obj.label);
} else {
warnList.value = warnList_copy.value;
}
};
onMounted(() => {
getwarnList();
});
</script>

<style scoped lang="scss">
.todayBoticeTotal {
width: 588px;
height: 59px;
background: #0f1e5e;
color: #78dfff;
font-family: "Microsoft YaHei UI";
font-size: 16px;
margin-top: 22px;
padding-left: 25px;
padding-top: 13px;
box-sizing: border-box;
display: flex;
align-content: center;
span.total {
color: #ff483a;
font-family: "Microsoft YaHei UI";
font-size: 22px;
position: relative;
top: 3px;
}
}
ul:not(.el-scrollbar__view) {
li:first-child {
margin-top: 0px;
}
li {
display: flex;
margin-top: 18px;
background: #182665;
padding-right: 20px;
box-sizing: border-box;
.left {
width: 195px;
height: 114px;
object-fit: cover;
.el-image {
width: 100%;
height: 100%;
}
}
.right {
flex: 1;
padding-left: 20px;
position: relative;
.status {
width: 82px;
height: 22px;
color: #fff;
font-size: 12px;
display: flex;
justify-content: space-between;
line-height: 20px;
align-content: center;
padding-left: 9px;
padding-right: 16px;
box-sizing: border-box;
position: absolute;
top: 16px;
right: 0px;
}
.time {
color: #78dfff;
font-size: 14px;
margin-top: 13px;
}
.title {
color: #fff;
font-size: 16px;
margin-top: 13px;
}
.des {
display: flex;
justify-content: space-between;
color: #78dfff;
font-size: 12px;
margin-top: 16px;
}
}
}
}
.header-select {
:deep(.el-select__wrapper) {
border: 1px solid #35c9f0;
background: unset;
width: 100%;
height: 40px;
outline: none;
}
// :deep(.el-popper) {
// position: absolute !important;
// top: 58px !important;
// left: 0px !important;
// }
:deep(.el-select-dropdown__item) {
color: #0c4dcf;
font-size: 20px;
height: 55px;
line-height: 55px;
}
:deep(.el-select__placeholder) {
background: linear-gradient(9deg, #10cefe 7.44%, #fff 79.62%);
background-clip: text;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
font-size: 16px;
}
}
// .header-select.nobg {
// :deep(.el-select__wrapper) {
// background-color: unset;
// font-size: 16px;
// width: 100%;
// height: 40px;
// }
// :deep(.el-select-dropdown__item) {
// color: #0c4dcf;
// max-width: 220px;
// font-size: 16px;
// line-height: 40px;
// }
// :deep(.el-select__placeholder) {
// background: linear-gradient(9deg, #10cefe 7.44%, #fff 79.62%);
// background-clip: text;
// -webkit-background-clip: text;
// -webkit-text-fill-color: transparent;
// font-size: 16px;
// }
// }
</style>

+ 230
- 0
SafeCampus.WEB/src/views/screen/component/index/todayNotice.vue View File

@@ -0,0 +1,230 @@
<template>
<div style="width: 100%; height: 100%">
<div class="commontitle">
<div class="left">
<span>今日场景告警次数</span>
<img src="/static/screen/img/titleIcon.png" alt="" />
</div>
<div class="right"></div>
</div>
<div style="display: flex">
<div class="safe myborder1">
<div class="commonTitle1">
<span class="left">校园安全</span>
<span class="right"
>告警次数:<span class="num">{{ typeStatistiCount }}</span></span
>
</div>
<div style="height: 410px; overflow-y: auto; margin-top: 18px">
<ul>
<template v-for="(item, index) in typeStatisti" :key="index">
<li>
<div class="title">
<span class="top">Top{{ index + 1 }}</span>
<span class="title_">{{ item.name }}</span>
</div>
<div class="progressBox">
<el-progress
:percentage="item.value"
:show-text="false"
:stroke-width="8"
style="width: 100%"
:color="
index == 0
? 'linear-gradient(to right,rgba(226,74,59,0.01),rgba(226,74,59,1))'
: index == 1
? 'linear-gradient(to right,rgba(246,152,14,0.01),rgba(246,152,14,1))'
: 'linear-gradient(to right,rgba(55,116,237,0.01),rgba(55,116,237,1))'
"
/>
</div>
<div class="num">{{ item.value }}次</div>
</li>
</template>
</ul>
</div>
</div>
<div class="cate">
<ul>
<template v-for="(item, index) in groupStatisti" :key="index">
<li class="myborder1">
<div class="commonTitle1">
<span class="left">{{ item.name }}</span>
<span class="right"
>告警次数:<span class="num">{{ item.value }}</span></span
>
</div>
<div style="height: 146px; overflow: auto; margin-top: 24px">
<ul>
<template v-for="(item1, index1) in item.subset" :key="index1">
<li>
<div>{{ item1.name }}:</div>
<div>{{ item1.value }}次</div>
</li>
</template>
</ul>
</div>
</li>
</template>
</ul>
</div>
</div>
</div>
</template>
<script setup>
const props = defineProps({
screenData: {}
});
// 校园安全
const typeStatisti = ref([]);
const typeStatistiCount = ref(0);
const getData = () => {
typeStatisti.value = props.screenData.alarmStatisti.typeStatisti.sort((a, b) => {
return b.value - a.value;
});
typeStatisti.value.forEach(e => {
typeStatistiCount.value += e.value;
});
};
onMounted(() => {
getData();
getRightData();
});
// 右边模块
const groupStatisti = ref([]);
const getRightData = () => {
groupStatisti.value = props.screenData.alarmStatisti.groupStatisti;
nextTick(() => {
setBoder1();
});
};
// 边框添加1
const setBoder1 = () => {
let borderEle = document.querySelectorAll(".myborder1");
borderEle.forEach(e => {
if (e.classList.contains("hasSetBoder")) return;
e.classList.add("hasSetBoder");
e.style.cssText = "position:relative;border: 1px solid #2e84e5;background: linear-gradient(179deg, #081340 -54.28%, #111e55 124.4%);";
let borderImgs = document.createElement("div");
borderImgs.style.cssText = `position: absolute;top: -2px;left: -2px;width: calc(100% + 4px);;height: calc(100% + 4px);;display: flex;justify-content: space-between;flex-flow: column;pointer-events:none`;
borderImgs.innerHTML = `<div style="width: 100%;display: flex;justify-content: space-between;">
<img src="/static/screen/img/topleft1.png" alt="" style="width:12.7px;height:12.7px"/>
<img src="/static/screen/img/topright1.png" alt="" style="width:12.7px;height:12.7px"/></div>
<div style="width: 100%;display: flex;justify-content: space-between;"><img src="/static/screen/img/bottomleft1.png" alt="" style="width:12.7px;height:12.7px"/>
<img src="/static/screen/img/bottomright1.png" alt="" style="width:12.7px;height:12.7px"/></div>`;
e.append(borderImgs);
});
};
</script>
<style scoped lang="scss">
.commontitle {
margin-bottom: 22px;
}
.safe {
padding: 24px 29px 0px 29px;
box-sizing: border-box;
width: 456px;
height: 494px;
ul {
padding-right: 10px;
li {
display: flex;
margin-top: 18.6px;
&:first-child {
margin-top: 0px;
}
.title {
display: flex;
color: #e3e9f3;
font-size: 12px;
.top {
display: inline-block;
width: 60px;
height: 20px;
border-radius: 10px;
background: linear-gradient(to left, rgba(55, 116, 237, 0.01), rgba(55, 116, 237, 1));
box-sizing: border-box;
padding-left: 10px;
}
.title_ {
display: block;
width: 76px;
position: relative;
right: 10px;
}
}
.progressBox {
width: 190px;
padding-top: 6px;
}
.num {
color: #fff;
text-align: right;
font-size: 14px;
display: block;
width: 42px;
}
&:nth-child(1) {
.title {
.top {
background: linear-gradient(to right, rgba(226, 74, 59, 1), rgba(226, 74, 59, 0.01));
}
}
}
&:nth-child(2) {
.title {
.top {
background: linear-gradient(to left, rgba(246, 152, 14, 0.01), rgba(246, 152, 14, 1));
}
}
}
}
}
}
.cate {
width: calc(100% - 200px);
margin-left: 22px;
box-sizing: border-box;
height: 494px;
overflow-y: auto;
> ul {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
padding-right: 10px;
> li {
margin-right: 16px;
width: 383.228px;
height: 235.65px;
margin-bottom: 22px;
padding: 22px;
box-sizing: border-box;
}
> li:nth-child(3n) {
margin-right: 0px;
}
ul {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
li {
display: flex;
justify-content: space-between;
color: #fff;
font-size: 12px;
width: 48%;
height: 40.92px;
box-shadow: 0px 0px 10px 0px #3877f2 inset;
background: #0e298b;
box-sizing: border-box;
padding: 0 8px;
line-height: 40.92px;
margin-bottom: 7.7px;
}
}
}
}
:deep(.el-progress-bar__outer) {
background: #404c80;
}
</style>

+ 226
- 0
SafeCampus.WEB/src/views/screen/component/index/video.vue View File

@@ -0,0 +1,226 @@
<template>
<div class="myvideo" style="width: 100%; height: 100%">
<div class="left">
<div class="commontitle">
<div class="left">
<span>实时监控</span>
<img src="/static/screen/img/titleIcon.png" alt="" />
</div>
<div class="right">
<el-input v-model="searchText" @keyup.enter="searchHandle" placeholder="搜索区域/设备">
<template #suffix>
<el-icon style="cursor: pointer" class="el-input__icon" @click="searchHandle" color="#2E84E5"><search /></el-icon>
</template>
</el-input>
</div>
</div>
<!-- -->
<div style="width: 794px; height: 487px; padding-top: 26px">
<!-- <img src="/static/screen/testimg/2.png" alt="" style="width: 100%; height: 100%" /> -->
<iframe id="rtsIframe" style="width: 100%; height: 100%" :src="rtsUrl" frameborder="0"></iframe>
</div>
</div>
<div class="right">
<div class="cateBox">
<div @click="cate = 1" :class="{ active: cate == 1 }">
<div class="title">区域选择</div>
<CaretBottom v-if="cate == 1" color="#fff" width="11px" />
</div>
<div @click="cate = 2" :class="{ active: cate == 2 }">
<div class="title">设备选择</div>
<CaretBottom v-if="cate == 2" color="#fff" width="11px" />
</div>
</div>
<ul v-if="cate == 1">
<li
v-for="(item, index) in areaList"
:class="{ active: item.id == areaId }"
:key="index"
class="wrap1"
@click="areaItemClick(item)"
:title="item.name"
>
{{ item.name }}
</li>
<template v-if="!areaList.length">
<li class="wrap1" style="border: none">暂无区域</li>
</template>
</ul>
<ul v-if="cate == 2">
<li
v-for="(item, index) in deviceList"
:class="{ active: item.sensorId == sensorId }"
:key="index"
class="wrap1"
@click="itemClick(item)"
:title="item.sensorName"
>
{{ item.sensorName }}
</li>
<template v-if="!deviceList.length">
<li class="wrap1" style="border: none">暂无设备</li>
</template>
</ul>
</div>
</div>
</template>
<script setup>
import { screenApi } from "@/api";
const props = defineProps({
screenData: {}
});
const searchText = ref("");
const cate = ref(1);
const resInfo = ref([]);
const allDevice = ref([]);
const areaList = ref([]);
const deviceList = ref([]);
const deviceList_copy = ref([]);
const areaId = ref("");
const sensorId = ref("");
// 获取区域设备信息
const getInfo = () => {
resInfo.value = props.screenData.camera;
areaList.value = JSON.parse(JSON.stringify(resInfo.value));
props.screenData.camera.forEach(e => {
allDevice.value = allDevice.value.concat(e.cameraInfos);
});
let obj = areaList.value.find(e => e.cameraInfos.length);
areaId.value = obj.id;
deviceList.value = obj.cameraInfos;
deviceList_copy.value = JSON.parse(JSON.stringify(obj.cameraInfos));
cate.value = 2;
itemClick(deviceList.value[0]);
};
// 搜索
const searchHandle = () => {
if (cate.value == 1) {
areaList.value = resInfo.value.filter(e => {
return e.name.includes(searchText.value);
});
}
if (cate.value == 2) {
deviceList.value = deviceList_copy.value.filter(e => {
return e.sensorName.includes(searchText.value);
});
}
};
// 点击某区域
const areaItemClick = item => {
areaId.value = item.id;
deviceList.value = item.cameraInfos;
deviceList_copy.value = JSON.parse(JSON.stringify(item.cameraInfos));
};
// 点击某设备
const itemClick = item => {
if (sensorId.value == item.sensorId) return;
if (streamId.value) stopUrl();
let rtsUrl_ = "/static/rtsPlayer.html?height=487px&rtsUrl=";
sensorId.value = item.sensorId;
screenApi.detail({ sensorId: item.sensorId }).then(res => {
if (res.code == 200) {
if (res.data.rtsPullStreamUrls[0]) {
rtsUrl.value = rtsUrl_ + res.data.rtsPullStreamUrls[0].url;
}
streamId.value = res.data.streamId;
videoToken.value = res.data.videoToken;
}
});
};
// 展示视频
const streamId = ref("");
const videoToken = ref("");
const rtsUrl = ref("");
const stopUrl = () => {
if (!streamId.value) return;
screenApi.stopUrl({
sensorId: sensorId.value,
streamId: streamId.value,
videoToken: videoToken.value
});
};
onMounted(() => {
getInfo();
});
onUnmounted(() => {
stopUrl();
});
</script>
<style lang="scss" scoped>
.myvideo {
display: flex;
.left {
flex: 1;
}
.right {
width: 186px;
padding-left: 14px;
.cateBox {
display: flex;
color: #79bde5;
font-size: 16px;
height: 50px;
padding-top: 8px;
> div {
color: #79bde5;
font-size: 16px;
flex: 1;
text-align: center;
cursor: pointer;
&.active {
background: linear-gradient(9deg, #10cefe 7.44%, #fff 79.62%);
background-clip: text;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
}
& > div:first-child > .title {
display: block;
border-right: 1px solid #2e84e5;
}
}
ul {
li {
height: 47px;
border: 1px solid #35c9f0;
background: #0c1749;
margin-top: 8px;
box-sizing: border-box;
color: #10cefe;
font-size: 14px;
line-height: 47px;
text-align: center;
cursor: pointer;
padding: 0 12px;
box-sizing: border-box;
&.active {
background: #0c4dcf;
}
}
}
}
}
:deep(.el-input__wrapper) {
border-radius: 17.5px;
border: 1px solid #2e84e5;
background: #112060;
width: 260px;
height: 35px;
}
:deep(.el-input__inner) {
color: #fff; /* 将文字颜色改为红色 */
font-size: 12px;
}
:deep(input::-webkit-input-placeholder) {
-webkit-text-fill-color: #2e84e5;
font-size: 12px;
}

/* 文字溢出 */
.wrap1 {
display: block;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
</style>

+ 95
- 0
SafeCampus.WEB/src/views/screen/index.vue View File

@@ -0,0 +1,95 @@
<template>
<div class="fullscreen-container">
<myHeader ref="myHeaderRef" place="0"></myHeader>
<div style="position: absolute; width: 100%; display: flex; justify-content: space-between; top: 102px">
<img style="width: 846px" src="/static/screen/img/header-aside-left.png" alt="" />
<img style="width: 845px" src="/static/screen/img/header-aside-right.png" alt="" />
</div>
<main v-if="ready">
<!-- 页面左边模块 -->
<div>
<div class="main-left">
<div style="display: flex">
<div class="peapleNum myborder">
<peopeleNum :screenData="screenData"></peopeleNum>
</div>
<div class="videoBox myborder">
<myVideo :screenData="screenData"></myVideo>
</div>
</div>
<div class="todayNotice myborder">
<todayNotice :screenData="screenData"></todayNotice>
</div>
</div>
</div>
<!-- 页面右边模块 -->
<div>
<div class="main-right myborder">
<todayInfo :screenData="screenData"></todayInfo>
</div>
</div>
</main>
</div>
</template>
<script setup>
import myHeader from "./component/header.vue";
import peopeleNum from "./component/index/peopeleNum.vue";
import myVideo from "./component/index/video.vue";
import todayNotice from "./component/index/todayNotice.vue";
import todayInfo from "./component/index/todayInfo.vue";
import { screenApi } from "@/api";
const screenData = ref({});
const ready = ref(false);
const myHeaderRef = ref(null);
onMounted(() => {
ready.value = false;
screenApi.getHomeData().then(res => {
if (res.code == 200) {
screenData.value = res.data;
console.log(res.data);
ready.value = true;
nextTick(() => {
myHeaderRef.value.setBoder();
});
}
});
});
</script>
<style lang="scss" scoped>
main {
padding: 41px 53px 0px 58px;
display: flex;
justify-content: space-between;
.main-left {
.peapleNum {
padding: 28px 36px;
box-sizing: border-box;
display: inline-block;
width: 661px;
height: 608.896px;
margin-right: 26px;
}
.videoBox {
padding: 32px 36px;
box-sizing: border-box;
display: inline-block;
width: 1070px;
height: 609px;
}
.todayNotice {
padding: 32px 45px;
box-sizing: border-box;
display: inline-block;
width: 1760px;
height: 617px;
margin-top: 24px;
}
}
.main-right {
width: 661px;
height: 1249px;
padding: 36px;
box-sizing: border-box;
}
}
</style>

+ 355
- 0
SafeCampus.WEB/src/views/screen/stureturn.vue View File

@@ -0,0 +1,355 @@
<template>
<div class="fullscreen-container">
<myHeader place="2" @getStuReturnInfo="getStuReturnInfo"></myHeader>
<div v-if="ready" style="position: absolute; width: 100%; display: flex; justify-content: space-between; top: 102px">
<img style="width: 846px" src="/static/screen/img/header-aside-left.png" alt="" />
<img style="width: 845px" src="/static/screen/img/header-aside-right.png" alt="" />
</div>
<main>
<div class="numsBox">
<ul>
<li>
<div class="title">寝室总人数</div>
<div class="numList">
<div v-for="(item, index) in studentPersonNum.totalNum" :key="index">{{ item }}</div>
</div>
</li>
<li>
<div class="title">在寝人数</div>
<div class="numList">
<div v-for="(item, index) in studentPersonNum.inNum" :key="index">{{ item }}</div>
</div>
</li>
<li>
<div class="title">不在寝人数</div>
<div class="numList">
<div v-for="(item, index) in studentPersonNum.noInNum" :key="index">{{ item }}</div>
</div>
</li>
</ul>
</div>
<div class="main">
<div class="left myborder1">
<div class="timeslot">
当前考勤时段:{{ screenData.returnTime }}
(入寝)
</div>
<div style="height: 880px; overflow: auto; margin-top: 39px">
<ul>
<li v-for="(item, index) in chamberList" :key="index" :data-status="item.status">
<div class="title">{{ item.name }}</div>
<div class="des">
<template v-if="item.personCount == 0"> 未安排入住 </template>
<template v-else> {{ item.dormitoryCount }}/{{ item.personCount }} </template>
</div>
<div class="sex">{{ item.gender ? "男生" : "女生" }}宿舍</div>
</li>
</ul>
</div>
</div>
<div class="right myborder1">
<div class="imgBox">
<!-- <img src="/static/screen/testimg/8.png" alt="" /> -->
<iframe :src="rtsUrl" frameborder="0" style="width: 100%; height: 100%"></iframe>
</div>
<el-table
:data="tableData"
style="width: 100%"
:row-class-name="
() => {
return 'tableRowClassName';
}
"
height="590px"
>
<el-table-column type="index" width="50" />
<el-table-column prop="personName" label="姓名" align="center"> </el-table-column>
<el-table-column prop="dormitName" label="所在宿舍" align="center"> </el-table-column>
<el-table-column prop="gender" label="宿舍性别" align="center" :formatter="genderFormatter"> </el-table-column>
<el-table-column prop="$status" label="归寝状态" align="center"> </el-table-column>
</el-table>
</div>
</div>
</main>
</div>
</template>
<script setup>
import myHeader from "./component/header.vue";
import { screenApi } from "@/api";
const screenData = ref({});
const ready = ref(false);
const chamberList = ref([]);
const tableData = ref([]);
const buildInfo = ref({});
const getStuReturnInfo = obj => {
buildInfo.value = obj;
// 归寝人数信息
ready.value = false;
screenApi.getStudentReturnBed({ buildId: obj.value }).then(res => {
if (res.code == 200) {
res.data.attendList = res.data.attendList.map(e => {
e.$status = e.cameraId == buildInfo.value.outCameraId ? "未归寝" : e.cameraId == buildInfo.value.insCameraId ? "归寝" : "";
return e;
});
screenData.value = res.data;
tableData.value = screenData.value.attendList;
console.log(res.data);
ready.value = true;
getstudentPersonNum();
}
});
// 监控
showRts(buildInfo.value.insCameraId);
};
// 性别
const genderOptions = ref([
{
label: "未知",
value: "GENDER_UNKNOWN"
},
{
label: "男",
value: "GENDER_MALE"
},
{
label: "女",
value: "GENDER_FEMALE"
}
]);
const genderFormatter = row => {
let obj = genderOptions.value.find(e => e.value == row.gender);
return obj ? obj.label : "";
};
// 监控
const streamId = ref("");
const videoToken = ref("");
const rtsUrl = ref("");
const sensorId = ref("");
const showRts = sensorId_ => {
if (sensorId_) {
if (sensorId.value == sensorId_) return;
if (streamId.value) stopUrl();
let rtsUrl_ = "/static/rtsPlayer.html?height=446px&rtsUrl=";
sensorId.value = sensorId_;
screenApi.detail({ sensorId: sensorId_ }).then(res => {
if (res.code == 200) {
if (res.data.rtsPullStreamUrls[0]) {
rtsUrl.value = rtsUrl_ + res.data.rtsPullStreamUrls[0].url;
}
streamId.value = res.data.streamId;
videoToken.value = res.data.videoToken;
}
});
}
};
const stopUrl = () => {
if (!streamId.value) return;
screenApi.stopUrl({
sensorId: sensorId.value,
streamId: streamId.value,
videoToken: videoToken.value
});
};
onUnmounted(() => {
stopUrl();
});
// 归寝信息
const studentPersonNum_ = reactive({
inNum: 0,
noInNum: 0,
totalNum: 0
});
const studentPersonNum = reactive({
inNum: [],
noInNum: [],
totalNum: []
});
// 0全部归寝 1部分归寝 2全未归寝 3未安排入住
const getstudentPersonNum = () => {
studentPersonNum_.value = screenData.value.building;
chamberList.value = screenData.value.dormitoryList.map((e, index) => {
e.status = e.personCount == 0 ? 3 : e.dormitoryCount == 0 ? 2 : e.dormitoryCount < e.personCount ? 1 : 0;
return e;
});
for (const key in studentPersonNum_.value) {
let item = studentPersonNum_.value[key];
let arr = item.toString().split("");
function initArr(arr) {
if (arr.length < 4) {
arr.unshift("0");
}
if (arr.length < 4) {
return initArr(arr);
}
return arr;
}
arr = initArr(arr);
studentPersonNum[key] = arr;
}
};
onMounted(() => {});
</script>
<style lang="scss" scoped>
main {
padding: 50px 67px 0px 63px;
.numsBox {
ul {
display: flex;
justify-content: space-around;
padding: 0px 400px 57px 400px;
li {
.title {
color: #08edf3;
text-align: center;
font-size: 22px;
margin-bottom: 21px;
}
.numList {
display: flex;
height: 82px;
div {
background: url("/static/screen/img/numbg.png") no-repeat;
background-size: cover;
width: 55px;
height: 82px;
color: #09fbff;
text-shadow: 0px 0px 4px rgba(168, 233, 255, 0.64);
font-size: 48px;
text-align: center;
line-height: 82px;
margin-right: 9px;
&:last-child {
margin-right: 0;
}
}
}
}
}
}
.main {
display: flex;
.left {
width: 1630px;
height: 1036.977px;
margin-right: 24px;
padding: 48px 42px;
box-sizing: border-box;
.timeslot {
color: #fff;
font-size: 20px;
}
ul {
display: flex;
flex-wrap: wrap;
li {
width: 152.372px;
height: 153.691px;
border-radius: 18px;
margin-right: 18.6px;
margin-bottom: 24px;
&:nth-child(9n) {
margin-right: 0px;
}
.title {
text-align: center;
font-size: 30px;
font-weight: 700;
color: #fff;
width: 152.372px;
height: 65.401px;
box-sizing: border-box;
padding-top: 20px;
}
.des {
color: #fff;
text-align: center;
font-size: 20px;
margin-top: 13px;
}
.sex {
color: #040404;
text-align: center;
font-size: 14px;
margin-top: 13px;
}
}
li[data-status="0"] {
box-shadow: 0 0 0 0px rgb(255, 255, 255, 0.7), inset 0 0 36px rgb(255, 255, 255, 0.4);
background: #0c4dcf;
.title {
color: #59edfd;
}
.des {
color: #fff;
}
.sex {
color: #09fbff;
}
}
li[data-status="1"] {
background: #fff8e5;
.title {
background: url("/static/screen/img/yellowbg.png");
background-size: cover;
}
.des {
color: #ee8f00;
}
}
li[data-status="2"] {
background: #ffe7e5;
.title {
background: url("/static/screen/img/redbg.png");
background-size: cover;
}
.des {
color: #de3d32;
}
}
li[data-status="3"] {
background: #c6f2e1;
.title {
background: url("/static/screen/img/greenbg.png");
background-size: cover;
}
.des {
color: #26ab43;
}
}
}
}
.right {
flex: 1;
.imgBox {
width: 776px;
height: 446px;
img {
width: 100%;
height: 100%;
}
}
}
}
}
:deep(.el-table) {
--el-table-border-color: #2e84e5;
--el-table-header-bg-color: #0037a4;
--el-table-text-color: #fff;
--el-table-header-text-color: #fff;
}
:deep(.el-table .tableRowClassName:nth-child(2n)) {
--el-table-tr-bg-color: #021940;
--el-table-row-hover-bg-color: #021940;
}
:deep(.el-table .tableRowClassName:nth-child(2n-1)) {
--el-table-tr-bg-color: #082253;
--el-table-row-hover-bg-color: #082253;
}
:deep(.el-table__empty-block) {
background: linear-gradient(179deg, rgb(8, 19, 64) -54.28%, rgb(17, 30, 85) 124.4%);
}
:deep(.el-table__body-wrapper) {
background: linear-gradient(179deg, rgb(8, 19, 64) -54.28%, rgb(17, 30, 85) 124.4%);
}
</style>

Loading…
Cancel
Save