feat: 添加人员总览弹窗以及AI预警页面修改

This commit is contained in:
kun 2024-07-05 19:10:28 +08:00
parent e8b9db8c66
commit d6350419f2
7 changed files with 1431 additions and 78 deletions

View File

@ -26,7 +26,7 @@ export const staticRouter: RouteRecordRaw[] = [
name: "大屏",
// component: () => import("@/views/sevenLargeScreen/indexL.vue"), //七参数标准版
component: () => import("@/views/commandScreen/indexCommand.vue"), //指挥部大屏
component: () => import("@/views/agjtLiveScreen/indexLive.vue"), //鞍钢现场大屏
// component: () => import("@/views/agjtLiveScreen/indexLive.vue"), //鞍钢现场大屏
// component: () => import("@/views/overviewScreen/indexCommand.vue"), //总览大屏
// component: () => import("@/views/agjtProjectKanban/indexL.vue"), //鞍钢集团项目看板大屏
// component: () => import("@/views/sevenLargeScreen/indexL_syhy.vue"), // 只有一级路由(盘锦、嘉兴、鄱湖美湾医疗项目需切换至该首页)

View File

@ -80,7 +80,7 @@
</div>
</div>
<div class="classify-div">
<div class="classify-div-item" @click="openDialogData({ index: 98, title: '人员总览' })">
<div class="classify-div-item" @click="openDialogData({ index: 12, title: '人员总览', type: 2 })">
<span style="font-size: 12px">人员总览</span>
</div>
<div class="classify-div-item" style="width: 250px" @click="openDialogData({ index: 1, title: '' })">
@ -93,19 +93,13 @@
<span style="font-size: 12px">影响进度计划因素&nbsp;{{ statsDirectorateBigScreen.taskProgressContentNum }}</span>
</div>
<div class="classify-div-item" @click="openDialogData({ index: 4, title: '风险统计' })">
<span style="font-size: 12px"
>风险统计&nbsp;{{ statsDirectorateBigScreen.securityQualityInspectionRecordNum }}</span
>
<span style="font-size: 12px">风险统计&nbsp;{{ statsDirectorateBigScreen.securityQualityInspectionRecordNum }}</span>
</div>
<div class="classify-div-item" @click="openDialogData({ index: 5, title: '安全隐患' })">
<span style="font-size: 12px"
>安全隐患&nbsp;{{ statsDirectorateBigScreen.securityQualityInspectionRecordNum }}</span
>
<span style="font-size: 12px">安全隐患&nbsp;{{ statsDirectorateBigScreen.securityQualityInspectionRecordNum }}</span>
</div>
<div class="classify-div-item" @click="openDialogData({ index: 99, title: '质量管理' })">
<span style="font-size: 12px"
>质量管理&nbsp;{{ statsDirectorateBigScreen.securityQualityInspectionRecordNum }}</span
>
<span style="font-size: 12px">质量管理&nbsp;{{ statsDirectorateBigScreen.securityQualityInspectionRecordNum }}</span>
</div>
<div class="classify-div-item" @click="openDialogData({ index: 6, title: 'AI警报' })">
<span style="font-size: 12px">AI警报&nbsp;{{ statsDirectorateBigScreen.aiAnalyseHardWareAlarmRecordNum }}</span>

View File

@ -327,6 +327,7 @@ const handleTab = (val: number) => {
activeIfo.activeIndex = val;
queryAttendanceOfEachCompany(false);
} else if (activeIfo.activeIndex == 0) {
activeIfo.activeIndex = val;
getWorkerRiskByProject(false, 1);
}
};
@ -546,7 +547,7 @@ const getWorkerRiskByProject = async (showLoading: boolean, val: number) => {
];
randerInfo.titleInfo.percentage = res.result.area;
}
activeIfo.activeIndex = val;
// activeIfo.activeIndex = val;
};
const setIntervalFn = (showLoading: boolean) => {

View File

@ -3,22 +3,35 @@
<div class="content">
<!-- v-show="topDangerList.length > 0" -->
<div class="top-statistics" v-show="topDangerList.length > 0">
<el-scrollbar class="statistics-listBox" ref="refAlarmScrollbar">
<div
class="statistics-item"
<div class="top-statistics-left">
<el-scrollbar class="statistics-listBox" ref="refAlarmScrollbar">
<div
class="statistics-item"
:style="{
background: `url(${bgImgSet(index)}) no-repeat`,
backgroundSize: '100% 100%'
}"
v-for="(item, index) in topDangerList"
:key="index"
>
<span class="title">{{ item.alarmTypeName }}</span>
<span class="statistics-item-content">今日报警次数{{ item.alarmNumToday }}</span>
<span class="statistics-item-content">本月报警次数{{ item.alarmNumMonth }}</span>
<span class="statistics-item-content">历史报警次数{{ item.alarmNum }}</span>
</div>
</el-scrollbar>
</div>
<!-- <div class="top-statistics-right">
<div>sss</div>
播放海康
<div ref="playWndBox" style="width: 96%; height: 100%;">
<div id="playWnd" class="playWnd" style="width: 100%; height: 100%"></div>
:style="{
background: `url(${bgImgSet(index)}) no-repeat`,
backgroundSize: '100% 100%'
}"
v-for="(item, index) in topDangerList"
:key="index"
>
<span class="title">{{ item.alarmTypeName }}</span>
<span class="statistics-item-content">今日报警次数{{ item.alarmNumToday }}</span>
<span class="statistics-item-content">本月报警次数{{ item.alarmNumMonth }}</span>
<span class="statistics-item-content">历史报警次数{{ item.alarmNum }}</span>
height: playWndHeight + 'px',
width: playWndWidth + 'px'
}"
</div>
</el-scrollbar>
</div> -->
</div>
<div style="position: relative; height: 180px" v-if="topDangerList.length == 0">
<div class="notoDta">
@ -26,7 +39,31 @@
<p>暂无数据</p>
</div>
</div>
<div class="top-search">
<div class="search-item">
<span>设备名称</span>
<el-input style="width: 150px;" size="small" v-model="searchForm.hardwareName" placeholder="请输入内容" @input="conditionSearch"></el-input>
</div>
<div class="search-item">
<span>不安全行为类型</span>
<el-input style="width: 150px;" size="small" v-model="searchForm.alarmTypeName" placeholder="请输入内容" @input="conditionSearch"></el-input>
</div>
<div class="search-item">
<span>警告时间</span>
<el-date-picker
style="width: 240px;"
size="small"
v-model="searchForm.dateRange"
type="daterange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
value-format="YYYY-MM-DD"
@change="conditionSearch"
/>
</div>
<!-- <el-button @click="getMemberCountList('search')">查询</el-button> -->
</div>
<div class="table-one">
<div class="tabList">
<div>序号</div>
@ -99,13 +136,14 @@
</template>
<script lang="ts" setup>
import { ref, onMounted } from "vue";
import { ref, onMounted, onBeforeMount, onBeforeUnmount, getCurrentInstance } from "vue";
import { GlobalStore } from "@/stores";
import { getCompanyDataList } from "@/api/modules/labor";
// import type { TabsPaneContext } from "element-plus";
import { getAlarmTypeOption } from "@/api/modules/aIEarlyWarn";
import noDataImage from "@/assets/images/vehicleManagement/car.png";
import { getAlarmRecordApi, getAlarmTypeCountPageApi } from "@/api/modules/agjtCommandApi";
import { ElMessage } from "element-plus";
import bgImg1 from "@/assets/images/commandScreen/bg1.png";
import bgImg2 from "@/assets/images/commandScreen/bg2.png";
import bgImg3 from "@/assets/images/commandScreen/bg3.png";
@ -118,6 +156,34 @@ const bgImgList = ref([bgImg1, bgImg2, bgImg3, bgImg4, bgImg5, bgImg6, bgImg7, b
const store = GlobalStore();
// eslint-disable-next-line vue/require-prop-types, @typescript-eslint/no-unused-vars
const props = defineProps(["tip"]);
const searchForm = ref({
hardwareName: '',
alarmTypeName: '',
dateRange: [],
});
const playWndBox = ref(null);
let playWndHeight = ref("");
let playWndWidth = ref("");
let oWebControl = ref(null);
let pubKey = ref("");
let initCount = ref(0);
// const objData = ref({
// appkey: "23071374", //appkey
// ip: "42.180.188.17", //ip
// secret: "skaSIHSYPRIynnxz7o57", //secret
// port: 8443,
// playMode: 0, // 0 1
// layout: "1x1" //16
// });
let objData = ref({
appkey: "23071374", //anjtappkey
ip: "42.180.188.17", //anjtip
secret: "skaSIHSYPRIynnxz7o57", //anjtsecret
port: 8443,//anjt
playMode: 0, // 0 1
layout: "1x1" //16
});
let cameraIndexCode = ref<string>("");
const BASEURL = import.meta.env.VITE_API_URL;
let showDialog = ref(false as any);
const enterpriseListData = ref([] as any);
@ -133,6 +199,23 @@ const refScrollbar = ref(null as any); // 绑定到滚动的盒子上
const detailData = ref({} as any);
const partyMemberList = ref({} as any);
let aiAlarmTypeEnum = ref([] as any);
//
const conditionSearch = async () => {
getMemberCountList('search')
}
//
const getVideoList = async () => {
// objData.value.appkey = "23071374";
// objData.value.ip = "42.180.188.17";
// objData.value.secret = "skaSIHSYPRIynnxz7o57";
// objData.value.port = 8443;
// cameraIndexCode.value = "210d4649ec0d4e6899b0394e5d8eeec2";
// objData.value.appkey = "24017757";
// objData.value.ip = "182.101.141.23";
// objData.value.secret = "VJz0FbzmE6drPQ7egsBi";
// objData.value.port = 18443;
// cameraIndexCode.value = "fa34ed90c1564841b14fed388741bbe2";
};
//
const bgImgSet = (index: any) => {
return bgImgList.value[index % bgImgList.value.length];
@ -204,10 +287,16 @@ const getCompanyList = async () => {
const getMemberCountList = async (tip: any) => {
let requestData: any = {
projectSn: store.sn,
hardwareName: searchForm.value.hardwareName,
alarmTypeName: searchForm.value.alarmTypeName,
pageNo: tip == "search" ? 1 : pageNo.value,
pageSize: 100,
isPushed: 1
};
if(searchForm.value.dateRange && searchForm.value.dateRange.length > 0){
requestData.startTime = searchForm.value.dateRange[0]
requestData.endTime = searchForm.value.dateRange[1]
}
const res: any = await getAlarmRecordApi(requestData);
if (tip == "more") {
partyMemberList.value = partyMemberList.value.concat(res.result.records);
@ -224,7 +313,9 @@ const getMemberCountList = async (tip: any) => {
pageNo.value = pageNo.value + 1;
}
};
// onBeforeMount(async () => {
// getVideoList();
// });
onMounted(async () => {
await getCompanyList();
await getMemberCountList("search");
@ -252,7 +343,226 @@ onMounted(async () => {
}
}
});
//
// setTimeout(() => {
// }, 2500);
await initPlugin();
// ee
const pageInstance = getCurrentInstance();
// dom
const tagDomObj = pageInstance?.refs.playWndBox;
playWndHeight.value = tagDomObj?.clientHeight;
playWndWidth.value = tagDomObj?.clientWidth;
// scroll使DIV
window.addEventListener("scroll", () => {
if (oWebControl.value == undefined) {
setTimeout(function () {
oWebControl.JS_Resize(playWndWidth.value * store.globalScale, playWndHeight.value * store.globalScale);
}, 200);
}
});
// resize使DIV
window.addEventListener("resize", e => {
if (oWebControl.value == undefined) {
setTimeout(function () {
oWebControl.JS_Resize(playWndWidth.value * store.globalScale, playWndHeight.value * store.globalScale);
}, 200);
}
// if (oWebControl.value == undefined) {
// // console.log("wwwww", e);
// oWebControl.JS_Resize(tagDomObj?.clientWidth, tagDomObj?.clientHeight);
// // oWebControl.JS_Resize(playWndHeight.value, playWndWidth.value);
// // setWndCover();
// }
});
// previewVideo(cameraIndexCode.value)
});
onBeforeUnmount(() => {
//
oWebControl.JS_HideWnd();
//
oWebControl.JS_RequestInterface({ funcName: "destroyWnd" });
//
oWebControl.JS_Disconnect();
});
const initPlugin = () => {
oWebControl = new WebControl({
szPluginContainer: "playWnd", // id
iServicePortStart: 15900, // 使
iServicePortEnd: 15900,
szClassId: "23BF3B0A-2C56-4D97-9C03-0CB103AA8F11", // IE10使ActiveXclsid
cbConnectSuccess: () => {
// WebControl
oWebControl
.JS_StartService("window", {
// WebControl
// "./VideoPluginConnect.dll"
dllPath: "./VideoPluginConnect.dll"
})
.then(
function () {
//
oWebControl.JS_SetWindowControlCallback({
// cbIntegrationCallBack: cbIntegrationCallBack,
});
//JS_CreateWnd
oWebControl
.JS_CreateWnd("playWnd", 1000, 600, { bEmbed: true }) //bEmbed: true
.then(function () {
//
init();
});
},
function () {
//
}
);
},
// WebControl
cbConnectError: function () {
// WebControl,
// console.log(0);
// oWebControl.value = null;
console.log(oWebControl.value);
// errorwakeup
window.WebControl.JS_WakeUp("VideoWebPlugin://");
initCount.value++;
if (initCount.value < 3) {
setTimeout(function () {
initPlugin();
}, 3000);
}
// else {
// setTimeout(function () {
// setTimeout(function () {
// router.push("/home");
// }, 4000);
// }, 4000);
// }
if (initCount.value < 2) {
oWebControl.value = null;
ElMessage.warning("插件未启动,正在尝试启动,请稍候...");
// errorwakeup
window.WebControl.JS_WakeUp("VideoWebPlugin://");
setTimeout(() => {
initPlugin();
}, 3000);
initCount.value++;
}
// else {
// window.location.href = this.videoWebPluginUrl;
// }
// console.log(oWebControl.value);
},
cbConnectClose: () => {
// bNormalClose = false
// JS_DisconnectbNormalClose = true
// console.log("cbConnectClose");
oWebControl.value = null;
}
});
// oWebControl.JS_CuttingPartWindow(500, 500, 500, 500);
};
//
const init = (callback: (() => void) | undefined) => {
console.log(objData.value, "我的视频服务器");
getPubKey(() => {
let appkey = objData.value.appkey; //appkey
let secret = setEncrypt(objData.value.secret); //secret
let ip = objData.value.ip; //IP
let playMode = objData.value.playMode; //0-1-
let port = objData.value.port; //HTTPS443
let snapDir = "D:\\SnapDir"; //
let videoDir = "D:\\VideoDir"; //
let layout = objData.value.layout; //playMode
let enableHTTPS = 1; //HTTPS1
let encryptedFields = "secret"; //secret
let showToolbar = 1; //0-0-
let showSmart = 0; //线0-0-
let buttonIDs = "0,16,256,257,258,259,260,512,513,514,515,516,517,768,769"; //
// var toolBarButtonIDs = "2049,2304" //
oWebControl
?.JS_RequestInterface({
funcName: "init",
argument: JSON.stringify({
appkey: appkey, //APIappkey
secret: secret, //APIsecret
// ip: ip + ":" + port, //APIIP
ip: ip, //APIIP
playMode: playMode, //
port: port, //
snapDir: snapDir, //
videoDir: videoDir, //
layout: layout, //
enableHTTPS: enableHTTPS, //HTTPS
encryptedFields: encryptedFields, //
showToolbar: showToolbar, //
showSmart: showSmart, //
buttonIDs //
})
})
.then(function (oData: any) {
oWebControl.JS_Resize(playWndWidth.value * store.globalScale, playWndHeight.value * store.globalScale); // resizefirefoxDIV
if (callback) {
callback();
}
//
// oWebControl.JS_HideWnd()
});
});
};
// RSA
let setEncrypt = (value: string) => {
let encrypt = new window.JSEncrypt();
encrypt.setPublicKey(pubKey);
return encrypt.encrypt(value);
};
//
const getPubKey = (callback: { (): void; (): void }) => {
oWebControl
.JS_RequestInterface({
funcName: "getRSAPubKey",
argument: JSON.stringify({
keyLength: 1024
})
})
.then(function (oData: { responseMsg: { data: Ref<string> } }) {
if (oData.responseMsg.data) {
pubKey = oData.responseMsg.data;
callback();
}
});
};
//
//
const previewVideo = (data: string | null) => {
let cameraIndexCode = data; //
let streamMode = 0; // 0-1-
let transMode = 1; // 0-UDP1-TCP
let gpuMode = 0; // GPU0-1-
let wndId = -1; // 2x2
oWebControl
.JS_RequestInterface({
funcName: "startPreview",
argument: JSON.stringify({
cameraIndexCode: cameraIndexCode, //
streamMode: streamMode, //
transMode: transMode, //
gpuMode: gpuMode, // GPU
wndId: wndId //
})
})
.then(function () {
oWebControl.JS_SetWindowControlCallback({});
});
};
</script>
<style lang="scss" scoped>
@ -376,49 +686,61 @@ onMounted(async () => {
height: 35%;
color: white;
margin: 0 auto;
.statistics-listBox {
display: flex;
justify-content: space-between;
&-left {
width: 100%;
height: 100%;
:deep() {
.el-scrollbar__view {
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-gap: 20px;
.statistics-listBox {
height: 100%;
:deep() {
.el-scrollbar__view {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-gap: 20px;
// grid-row-gap: 20px;
// grid-column-gap: 60px;
}
}
}
.statistics-item {
width: 200px;
// height: 95px;
display: flex;
flex-direction: column;
position: relative;
background-size: 100% 100%;
.title {
position: absolute;
top: 4%;
left: 4%;
}
&-content {
display: inline-block;
width: 100%;
height: auto;
text-indent: 1.5em;
}
&-content:nth-child(2) {
margin-top: 15%;
}
&-content:nth-child(3) {
margin-top: 2%;
}
&-content:nth-child(4) {
margin-top: 2%;
}
&-content:last-child {
margin-bottom: 4%;
}
// &-content:nth-child(3){
// margin-top: 8%;
// }
}
}
.statistics-item {
width: 200px;
// height: 95px;
display: flex;
flex-direction: column;
position: relative;
background-size: 100% 100%;
.title {
position: absolute;
top: 4%;
left: 4%;
}
&-content {
display: inline-block;
width: 100%;
height: auto;
text-indent: 1.5em;
}
&-content:nth-child(2) {
margin-top: 15%;
}
&-content:nth-child(3) {
margin-top: 2%;
}
&-content:nth-child(4) {
margin-top: 2%;
}
&-content:last-child {
margin-bottom: 4%;
}
// &-content:nth-child(3){
// margin-top: 8%;
// }
&-right {
width: 390px;
height: 100%;
}
// .statistics-item:nth-child(1){
// background: url("@/assets/images/commandScreen/bg6.png") no-repeat;
@ -455,8 +777,8 @@ onMounted(async () => {
}
.top-search {
@include flex;
justify-content: center;
margin-bottom: 15px;
width: 90%;
margin: 0 auto;
margin-top: 20px;
.search-item {
@include flex;
@ -489,7 +811,7 @@ onMounted(async () => {
}
}
.listBox {
height: 90%;
height: 82%;
.listStyle {
display: flex;
align-items: center;

View File

@ -1,6 +1,6 @@
<template>
<div class="list-detail" v-if="showDialog && mapsDetail.length == 0">
<div :class="postData.type == 1 ? 'dialog-content' : postData.type == 2 ? 'dialog-content-show' : 'dialog-content-more'"
<div :class="postData.type == 1 ? 'dialog-content' : postData.type == 2 ? 'dialog-content-show' : postData.type == 3 ? 'dialog-content-three' : 'dialog-content-more'"
:style="postData.index == 98 ? {'left':'5%','width':'90%','top':'5%','height':'90%'} : {}">
<div class="dialog-title">
<!-- <div class="title-img"><img src="@/assets/images/titleIcon.png" alt="" /></div> -->
@ -62,11 +62,17 @@
<div class="political-outlook" v-if="showIndex == 11">
<radarMapList ref="historyAlarmList" :randerDetail="postData" :tip="postData.title"></radarMapList>
</div>
<div class="political-outlook" v-if="showIndex == 12">
<memberAllShow ref="historyAlarmList" :tip="postData.title"></memberAllShow>
</div>
<div class="political-outlook" v-if="showIndex == 13">
<memberMoreList ref="historyAlarmList"></memberMoreList>
</div>
</div>
</div>
<div v-else-if="showIndex == 10">
<div class="list-detail" v-for="ele in mapsDetail.filter((option: any) => option.show)" :key="ele.id">
<div :class="postData.type == 1 ? 'dialog-content' : postData.type == 2 ? 'dialog-content-show' : 'dialog-content-more'">
<div :class="postData.type == 1 ? 'dialog-content' : postData.type == 2 ? 'dialog-content-show' : postData.type == 3 ? 'dialog-content-three' : 'dialog-content-more'">
<div class="warning-bg"></div>
<div class="dialog-title">
<!-- <div class="title-img"><img src="@/assets/images/titleIcon.png" alt="" /></div> -->
@ -105,7 +111,8 @@ import majorHidder from "./major-hidder.vue";
import monthSafeScore from "./month-safe-score.vue";
import myMap from "./my-map.vue";
import radarMapList from "./radar-map-list.vue";
import { log } from "console";
import memberAllShow from "./member-all-show.vue";
import memberMoreList from "./member-more-list.vue";
// import { GlobalStore } from "@/stores";
let showDialog = ref(false as any);
@ -268,15 +275,26 @@ onMounted(async () => {
position: absolute;
box-sizing: border-box;
padding: 1%;
left: 15%;
left: 12%;
top: 1%;
width: 70%;
width: 80%;
height: 98%;
background: url("@/assets/images/commandScreen/dialog-bg.png") no-repeat;
background-size: 100% 100%;
z-index: 21;
}
.dialog-content-three {
position: absolute;
box-sizing: border-box;
padding: 1%;
left: 5%;
top: 21%;
width: 90%;
height: 60%;
background: url("@/assets/images/commandScreen/dialog-bg.png") no-repeat;
background-size: 100% 100%;
z-index: 21;
}
.dialog-content-more {
position: absolute;
box-sizing: border-box;
@ -292,7 +310,8 @@ onMounted(async () => {
.dialog-content,
.dialog-content-show,
.dialog-content-more {
.dialog-content-more,
.dialog-content-three {
padding-top: 50px;
position: relative;
@ -347,6 +366,10 @@ onMounted(async () => {
}
}
}
.dialog-content-show {
padding-top: 70px;
position: relative;
}
}
.notoDta {

View File

@ -0,0 +1,661 @@
<template>
<div class="political-outlook">
<div class="content">
<div class="dialog-title">
<div class="title-img"><img src="@/assets/images/titleIcon.png" alt="" /></div>
<div class="title-text">
<i>人员类型统计</i>
</div>
</div>
<div class="PersonnelType_wrap">
<div class="title_wrap" style="margin-bottom: 18px">
<div>
<div class="headcount">工地总人数{{ memberCountData.totalPersonNum }}</div>
<div class="headcount">今日出勤人数{{ memberCountData.attendancePersonNum }}</div>
<div class="headcount">今日在场人数{{ memberCountData.presencePersonNum }}</div>
</div>
</div>
<div class="echarts" ref="personnelTypeRef"></div>
</div>
</div>
<div class="content">
<div class="dialog-title">
<div class="title-img"><img src="@/assets/images/titleIcon.png" alt="" /></div>
<div class="title-text">
<i>企业出勤排名</i>
</div>
</div>
<div class="manager_wrap">
<div class="echarts" ref="ageChartRef" style="width: 100%; height: 100%"></div>
</div>
</div>
<div class="list-content">
<div class="dialog-title">
<div class="title-img"><img src="@/assets/images/titleIcon.png" alt="" /></div>
<div class="title-text">
<i>人员实时动态</i>
</div>
</div>
<div class="show-more" @click="openPeopleCountDialog({ index: 13, type: 3, title: '人员实时动态' })">
<span>更多</span>
</div>
<div class="tabList">
<div>类型</div>
<div>姓名</div>
<div>班组</div>
<div>图片</div>
<div style="width: 25%">通行时间</div>
</div>
<el-scrollbar class="listBox" ref="refScrollbar">
<div v-for="(item, index) in crewRealTimeList" class="listStyle" :key="item.id">
<div>{{ item.passType == 1 ? "进" : "出" }}</div>
<div>{{ item.workerName }}</div>
<div>{{ item.teamName ? item.teamName : item.departmentName }}</div>
<div class="list-img">
<el-image v-if="item.imageUrl" fit="contain" class="el-img" :src="item.imageUrl" :preview-src-list="[item.imageUrl]">
<template #error>
<el-image :src="noDataImage" :preview-src-list="[noDataImage]" fit="contain" class="el-no-img" alt="" />
</template>
</el-image>
<el-image v-else :src="noDataImage" :preview-src-list="[noDataImage]" fit="contain" class="el-no-img" alt="" />
</div>
<div style="width: 25%">{{ item.createTime }}</div>
</div>
<div class="notoDta" v-if="crewRealTimeList.length == 0">
<img src="@/assets/images/noData.png" alt="" />
<p>暂无数据</p>
</div>
</el-scrollbar>
</div>
<div class="list-content">
<div class="dialog-title">
<div class="title-img"><img src="@/assets/images/titleIcon.png" alt="" /></div>
<div class="title-text">
<i>班组人员统计</i>
</div>
</div>
<div class="tabList">
<div>序号</div>
<div>班组名称</div>
<div>在册人数</div>
<div>出场人数</div>
<div>在场人数</div>
</div>
<el-scrollbar class="listBox" ref="refScrollbar">
<div v-for="(item, index) in teamList" class="listStyle" :key="item.id">
<div>{{ index + 1 }}</div>
<div>{{ item.teamName }}</div>
<div>{{ item.totalPerson }}</div>
<div>{{ item.attendancePersonTotal }}</div>
<div>{{ item.presencePersonTotal }}</div>
</div>
<div class="notoDta" v-if="teamList.length == 0">
<img src="@/assets/images/noData.png" alt="" />
<p>暂无数据</p>
</div>
</el-scrollbar>
</div>
<dataDialog ref="partyBuildRefMore"></dataDialog>
</div>
</template>
<script lang="ts" setup>
import { ref, onMounted } from "vue";
import { GlobalStore } from "@/stores";
import {
getPersonTypeAndEduStatisticsApi,
getComapnyWorkTotalListApi,
getRealTimeDataApi,
getWorkerInfoApi
} from "@/api/modules/labor";
import * as echarts from "echarts";
import dataDialog from "./data-dialog.vue";
import noDataImage from "@/assets/images/vehicleManagement/car.png";
const store = GlobalStore();
const props = defineProps(["tip"]);
const partyBuildRefMore = ref();
const teamList = ref([] as any);
const BASEURL = import.meta.env.VITE_API_URL;
const crewRealTimeList = ref([] as any);
const ageChartRef = ref();
const memberCountData = ref({
totalPersonNum: 0,
totalEducationPerson: 0,
attendancePersonNum: 0,
presencePersonNum: 0
});
const personnelTypeRef = ref();
//
const openPeopleCountDialog = (obj: any) => {
partyBuildRefMore.value.openDialog(obj);
// console.log(partyBuildRef.value);
};
//
const selectWorkerTeamStatistics = async () => {
const res: any = await getWorkerInfoApi({
projectSn: store.sn,
userEnterpriseId: store.userInfo?.userEnterpriseId
});
teamList.value = res.result;
};
//
const getCrewRealTimeData = async () => {
let data = {
projectSn: store.sn,
userEnterpriseId: store.userInfo?.userEnterpriseId
};
const res: any = await getRealTimeDataApi(data);
if (res.code == 200) {
crewRealTimeList.value = res.result;
crewRealTimeList.value.map((item: any) => {
if (item.imageUrl) {
item.imageUrl = BASEURL + "/image/" + item.imageUrl;
} else if (item.fieldAcquisitionUrl) {
item.fieldAcquisitionUrl = BASEURL + "/image/" + item.fieldAcquisitionUrl;
}
});
}
};
//
const getCompanyAttendData = async () => {
let data = {
projectSn: store.sn
};
const res: any = await getComapnyWorkTotalListApi(data);
if (res.code == 200) {
var Data = res.result;
var xData: any = [],
yData1: any = [],
yData2: any = [],
yData3: any = [];
Data.forEach((element: any) => {
xData.push(element.enterpriseName);
yData1.push(element.attendancePersonNum);
yData2.push(element.totalPersonNum);
yData3.push(element.presencePersonNum);
});
createdBarCharts(xData, yData1, yData2, yData3, ageChartRef.value, ["出勤人数", "在册人数", "在场人数"]);
}
};
//---
const createdBarCharts = (xData: any, yData1: any, yData2: any, yData3: any, el: any, legendData: any) => {
let that = this;
let ageChart = echarts.init(el);
ageChart.clear();
let option = {
grid: {
width: "92%",
height: "82%",
left: 20,
containLabel: true
},
color: ["#5CE2F6", "#5181F6", "#8EBDD2"],
tooltip: {
trigger: "axis",
axisPointer: {
type: "shadow"
}
},
legend: {
show: true,
// align: "left",
top: 0,
// left: 0,
itemWidth: 13,
itemHeight: 5,
textStyle: {
color: "#9fa2ad"
}
},
xAxis: [
{
type: "category",
data: xData,
boundaryGap: true,
axisTick: {
show: false
},
axisLine: {
show: false
},
axisLabel: {
color: "#9fa2ad",
fontSize: 12,
interval: 0, //
}
}
],
dataZoom: [
{
type: "slider", // 使
start: 0, //
end: 50, //
height: 10, //
bottom: 0, //
showDetail: false, //
showDataShadow: false, //
fillerColor: "#2758C0", //
borderColor: "transparent", //
zoomLock: true, //
},
],
yAxis: {
type: "value",
axisTick: {
show: false
},
axisLine: {
show: false
},
splitLine: {
lineStyle: {
type: "dashed",
color: "rgba(231, 233, 243, 1)"
}
},
axisLabel: {
color: "#9fa2ad"
}
},
// dataZoom: [
// {
// show: true,
// start: 50,
// end: 100,
// height: 8,
// borderColor: "transparent",
// backgroundColor: "#fff",
// filterColor: "#999999",
// bottom: 0,
// },
// ],
series: [
{
name: legendData[1],
type: "bar",
data: yData2,
// stack: 'product',
barWidth: 35,
barGap: 0.1
},
{
name: legendData[0],
type: "bar",
// stack: 'product',
data: yData1,
barMaxWidth: 35
},
{
name: legendData[2],
type: "bar",
data: yData3,
// stack: 'product',
barMaxWidth: 35,
barGap: 0.1
}
]
};
ageChart.setOption(option);
};
//
const createdEcharts = (yAxisData1: any, yAxisData2: any, yAxisData3: any) => {
// let yAxisData1 = [1, 2, 3, 4, 5, 6, 7];
// let yAxisData2 = [2, 3, 7, 4, 5, 6, 7];
let personnelType = echarts.init(personnelTypeRef.value);
let option = {
color: ["#8FC8FF", "rgba(126,150,232,1)", "#61D2B9"],
tooltip: {
show: true,
trigger: "axis",
formatter: "{b} <br /> {a0}: {c0}<br /> {a1}: {c1} <br /> {a2}: {c2}",
axisPointer: {
lineStyle: {
color: "transparent"
}
},
textStyle: {
color: "#000",
fontSize: 12
},
backgroundColor: "rgba(236,236,236,0.75)"
},
grid: {
left: 30,
right: 30,
bottom: 15,
top: 20,
containLabel: true
},
xAxis: {
type: "category",
boundaryGap: false,
axisLine: {
show: false
},
axisTick: { show: false },
data: ["项目管理人员", "劳务工", "临时工"],
axisLabel: {
// rotate: 30,
textStyle: {
color: "#9fa2ad",
fontSize: 10
}
}
},
yAxis: {
show: true,
// type: "time",
axisTick: { show: false },
axisLine: {
show: false
},
splitLine: {
lineStyle: {
type: "dashed",
color: "rgba(231, 233, 243, 0.5)"
}
},
axisLabel: {
// show: false,
textStyle: {
color: "#9fa2ad",
fontSize: 10
}
}
},
series: [
{
name: "在册人数",
data: yAxisData2,
type: "line",
symbolSize: 10,
// symbol: "circle",
lineStyle: {
width: 1
},
areaStyle: {
normal: {
// opacity: 0.2
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{
offset: 0,
color: "rgba(126,150,232,0.56)"
},
{
offset: 1,
color: "rgba(30, 72, 223, 0)"
}
])
}
}
},
{
name: "出勤人数",
data: yAxisData3,
type: "line",
symbolSize: 10,
// symbol: "circle",
lineStyle: {
width: 1
},
areaStyle: {
normal: {
// opacity: 0.2
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{
offset: 0,
color: "rgba(126,150,232,0.56)"
},
{
offset: 1,
color: "rgba(30, 72, 223, 0)"
}
])
}
}
},
{
name: "在场人数",
data: yAxisData1,
type: "line",
symbolSize: 10,
// symbol: "circle",
lineStyle: {
width: 1
},
areaStyle: {
normal: {
// opacity: 0.2
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{
offset: 0,
color: "rgba(37,82,228,0.56)"
},
{
offset: 1,
color: "rgba(30, 72, 223, 0)"
}
])
}
}
}
]
};
personnelType.setOption(option);
};
//
const selectPersonTypeAndEduStatistics = async () => {
const res: any = await getPersonTypeAndEduStatisticsApi({
projectSn: store.sn,
userEnterpriseId: store.userInfo?.userEnterpriseId
});
console.log(res);
memberCountData.value.totalPersonNum = res.result.personType.toaltPerson.totalPerson; //
memberCountData.value.totalEducationPerson = res.result.totalEducationPerson;
memberCountData.value.attendancePersonNum = res.result.personType.attendancePerson.totalPerson; //
memberCountData.value.presencePersonNum = res.result.personType.presencePerson.totalPerson; //
var personTypeData = res.result.personType;
var arr1 = [
personTypeData.presencePerson.glPersonTotal,
personTypeData.presencePerson.lwPersonTotal,
personTypeData.presencePerson.lsPersonTotal
];
var arr2 = [
personTypeData.toaltPerson.glPersonTotal,
personTypeData.toaltPerson.lwPersonTotal,
personTypeData.toaltPerson.lsPersonTotal
];
var attendance = [
//
personTypeData.attendancePerson.glPersonTotal,
personTypeData.attendancePerson.lwPersonTotal,
personTypeData.attendancePerson.lsPersonTotal
];
createdEcharts(arr1, arr2, attendance);
// createdEcharts3()
};
onMounted(async () => {
await selectPersonTypeAndEduStatistics();
await getCompanyAttendData();
await getCrewRealTimeData();
await selectWorkerTeamStatistics();
});
</script>
<style lang="scss" scoped>
@mixin flex {
display: flex;
align-items: center;
}
.political-outlook {
width: 100%;
height: 97% !important;
@include flex;
flex-wrap: wrap;
justify-content: space-around;
margin-top: 1%;
.content {
height: 45%;
width: 45%;
margin-top: 10px;
// background: url("@/assets/images/cardImg.png") no-repeat;
background-size: 100% 100%;
.dialog-title {
color: #ffffff;
font-weight: bold;
font-size: 18px;
font-family: "OPPOSans-Bold";
@include flex;
.title-img {
width: 30px;
height: 30px;
img {
width: 100%;
height: 100%;
}
}
.title-text {
margin-bottom: 0.3%;
margin-left: 0.2%;
}
}
.PersonnelType_wrap {
width: 100%;
height: 100%;
.title_wrap {
> div {
@include flex;
justify-content: flex-end;
.headcount {
color: white;
font-size: 14px;
padding: 3px 10px;
// border-radius: 30px;
background: #2758c0;
margin-right: 10px;
}
}
margin-top: 12px;
}
}
.manager_wrap {
width: 100%;
height: 100%;
}
.echarts {
width: 100%;
height: calc(100% - 50px);
}
}
.list-content {
height: 45%;
width: 45%;
margin-top: 10px;
// background: url("@/assets/images/cardImg.png") no-repeat;
background-size: 100% 100%;
.dialog-title {
color: #ffffff;
font-weight: bold;
font-size: 18px;
font-family: "OPPOSans-Bold";
@include flex;
margin-bottom: 20px;
.title-img {
width: 30px;
height: 30px;
img {
width: 100%;
height: 100%;
}
}
.title-text {
margin-bottom: 0.3%;
margin-left: 0.2%;
}
}
.show-more {
cursor: pointer;
font-size: 12px;
font-family: Source Han Sans CN-Regular, Source Han Sans CN;
color: #4ac0f3;
@include flex;
justify-content: flex-end;
margin-bottom: 10px;
}
.tabList {
display: flex;
width: 100%;
height: 5%;
background: url("@/assets/images/vehicleManagement/ListTitleImg.png") no-repeat;
background-size: 100% 100%;
// position: absolute;
left: 75.5%;
top: 75%;
color: #ccc;
font-size: calc(100vw * 14 / 1920);
line-height: 30px;
align-items: center;
div {
text-align: center;
width: 20%;
}
}
.listBox {
height: 78%;
.listStyle {
display: flex;
align-items: center;
text-align: center;
color: #fff;
font-size: 12px;
margin-bottom: 5px;
.list-img {
.el-img {
width: 30px;
height: 30px;
img {
display: flex;
align-items: center;
width: 100%;
height: 100%;
}
}
.el-no-img {
width: 30px;
height: 30px;
}
}
div {
width: 20%;
white-space: nowrap; //
overflow: hidden;
text-overflow: ellipsis;
}
}
.listStyle:hover {
background: #091f3f;
}
}
}
}
.notoDta {
top: 28%;
width: 50%;
left: 22%;
position: absolute;
text-align: center;
img {
width: 40%;
margin: 5% 30%;
}
p {
color: #fff;
font-size: calc(100vw * 14 / 1920);
margin: -6% 37%;
}
}
</style>

View File

@ -0,0 +1,352 @@
<template>
<div class="political-outlook">
<div class="content">
<div class="top-search">
<div class="search-item">
<span>姓名</span>
<el-input placeholder="请输入" size="small" v-model="searchForm.name" :clearable="true" style="width: 100px" />
</div>
<div class="search-item">
<span>通行时间</span>
<el-date-picker
style="width: 300px"
v-model="searchForm.rangeTime"
type="datetimerange"
size="small"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
format="YYYY-MM-DD HH:mm:ss"
value-format="YYYY-MM-DD HH:mm:ss"
:clearable="true"
/>
</div>
<div class="search-item">
<span>所属企业</span>
<el-select
class="m-2"
placeholder="请选择"
size="small"
v-model="searchForm.belongCompany"
:clearable="true"
@change="companySelect"
style="width: 100px"
>
<el-option v-for="(item, index) in enterpriseListData" :key="index" :label="item.enterpriseName" :value="item.id" />
</el-select>
</div>
<div class="search-item">
<span>所属班组</span>
<el-select
class="m-2"
placeholder="请选择"
size="small"
v-model="searchForm.belongTeam"
:clearable="true"
style="width: 100px"
>
<el-option v-for="(item, index) in teamListData" :key="index" :label="item.teamName" :value="item.id" />
</el-select>
</div>
<div class="search-item">
<span>所属部门</span>
<el-select
class="m-2"
placeholder="请选择"
size="small"
v-model="searchForm.belongDepart"
:clearable="true"
style="width: 100px"
>
<el-option v-for="(item, index) in departListData" :key="index" :label="item.departmentName" :value="item.id" />
</el-select>
</div>
<el-button @click="getMemberCountList('search')">查询</el-button>
</div>
<div class="tabList">
<div>类型</div>
<div>姓名</div>
<div>班组</div>
<div>通行时间</div>
<div>闸机名称</div>
<div>图片</div>
</div>
<el-scrollbar class="listBox" ref="refScrollbar">
<div v-for="(item, index) in crewRealTimeList" class="listStyle" :key="item.id">
<div>{{ item.passType == 1 ? "进" : "出" }}</div>
<div>{{ item.workerName }}</div>
<div>{{ item.teamName ? item.teamName : item.departmentName }}</div>
<div>{{ item.createTime }}</div>
<div>{{ item.passagewayName }}</div>
<div class="list-img">
<el-image v-if="item.imageUrl" fit="contain" class="el-img" :src="item.imageUrl" :preview-src-list="[item.imageUrl]">
<template #error>
<el-image :src="noDataImage" :preview-src-list="[noDataImage]" fit="contain" class="el-no-img" alt="" />
</template>
</el-image>
<el-image v-else :src="noDataImage" :preview-src-list="[noDataImage]" fit="contain" class="el-no-img" alt="" />
</div>
</div>
<div class="notoDta" v-if="partyMemberList.length == 0">
<img src="@/assets/images/noData.png" alt="" />
<p>暂无数据</p>
</div>
</el-scrollbar>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref, onMounted } from "vue";
import { GlobalStore } from "@/stores";
import {
getCompanyDataList,
getMemberInfoList,
getTeamDataList,
getDepartDataList,
getRealTimeMoreDataApi
} from "@/api/modules/labor";
import noDataImage from "@/assets/images/vehicleManagement/car.png";
const store = GlobalStore();
const props = defineProps(["tip"]);
const crewRealTimeList = ref([] as any);
const BASEURL = import.meta.env.VITE_API_URL;
const departListData = ref([] as any);
const teamListData = ref([] as any);
const enterpriseListData = ref([] as any)
let pageNo = ref(1 as any);
let moreScroll = ref(true as any);
const refScrollbar = ref(null as any); //
const searchForm = ref({
name: "",
rangeTime: "",
belongCompany: "",
belongTeam: "",
belongDepart: ""
});
const partyMemberList = ref({} as any);
//
const companySelect = async () => {
await getTeamList();
await getDepartmentList();
};
//
const getDepartmentList = async () => {
let data = {
enterpriseId: searchForm.value.belongCompany,
projectSn: store.sn
};
const res: any = await getDepartDataList(data);
departListData.value = res.result.list;
};
// -
const getTeamList = async () => {
let data = {
enterpriseId: searchForm.value.belongCompany,
projectSn: store.sn
};
const res: any = await getTeamDataList(data);
teamListData.value = res.result.list;
};
//
const getCompanyList = async () => {
let data = {
projectSn: store.sn,
enterpriseName: "",
userEnterpriseId: store.userInfo?.userEnterpriseId
};
const res: any = await getCompanyDataList(data);
if (res.code == 200) {
enterpriseListData.value = res.result;
}
};
//
const getMemberCountList = async (tip: any) => {
let data:any = {
projectSn: store.sn,
userEnterpriseId: store.userInfo?.userEnterpriseId,
workerName: searchForm.value.name,
enterpriseId: searchForm.value.belongCompany,
teamId: searchForm.value.belongTeam,
departmentId: searchForm.value.belongDepart,
pageNo: tip == 'search'?1:pageNo.value,
pageSize: 20
};
if(searchForm.value.rangeTime){
data.startTime = searchForm.value.rangeTime[0]
data.endTime = searchForm.value.rangeTime[1]
}
const res: any = await getRealTimeMoreDataApi(data);
if (tip == "more") {
crewRealTimeList.value = crewRealTimeList.value.concat(res.result.records);
} else {
crewRealTimeList.value = res.result.records;
}
crewRealTimeList.value.map((item: any) => {
if (item.imageUrl) {
if(item.imageUrl.indexOf("http") == -1 && item.imageUrl.indexOf("https") == -1){
item.imageUrl = BASEURL + "/image/" + item.imageUrl;
}
} else if (item.fieldAcquisitionUrl) {
if(item.fieldAcquisitionUrl.indexOf("http") == -1 && item.fieldAcquisitionUrl.indexOf("https") == -1){
item.fieldAcquisitionUrl = BASEURL + "/image/" + item.fieldAcquisitionUrl;
}
}
});
console.log(crewRealTimeList.value,777888)
if (res.result.pages == pageNo.value) {
moreScroll.value = false;
} else {
pageNo.value = pageNo.value + 1;
}
};
onMounted(async () => {
await getCompanyList();
await getMemberCountList("search");
refScrollbar.value.wrapRef.addEventListener("scroll", (e: any) => {
// console.log("", e);
const scrollTop = e.target.scrollTop;
const scrollHeight = e.target.scrollHeight;
const clientHeight = e.target.clientHeight;
// console.log("", scrollTop, scrollHeight, clientHeight);
//
if (scrollTop >= scrollHeight - clientHeight - 1) {
// console.log("");
if (moreScroll.value) {
getMemberCountList("more");
}
}
});
});
</script>
<style lang="scss" scoped>
@mixin flex {
display: flex;
align-items: center;
}
.political-outlook {
width: 100%;
height: 97%;
.content {
height: 95%;
width: 100%;
margin-top: 10px;
// background: url("@/assets/images/cardImg.png") no-repeat;
background-size: 100% 100%;
.top-search {
width: 95%;
margin: 0 auto;
@include flex;
justify-content: flex-end;
margin-bottom: 15px;
margin-top: 30px;
.search-item {
@include flex;
margin-right: 20px;
span {
color: white;
margin-right: 10px;
}
}
}
.tabList {
display: flex;
width: 95%;
height: 5%;
margin: 0 auto;
background: url("@/assets/images/vehicleManagement/ListTitleImg.png") no-repeat;
background-size: 100% 100%;
// position: absolute;
left: 75.5%;
top: 75%;
color: #ccc;
font-size: calc(100vw * 14 / 1920);
line-height: 30px;
align-items: center;
div {
text-align: center;
width: 17%;
}
}
.listBox {
width: 95%;
margin: 0 auto;
height: 80%;
.listStyle {
display: flex;
align-items: center;
text-align: center;
color: #fff;
font-size: 12px;
margin-bottom: 5px;
.list-img {
.el-img {
width: 30px;
height: 30px;
img {
display: flex;
align-items: center;
width: 100%;
height: 100%;
}
}
.el-no-img {
width: 30px;
height: 30px;
}
}
div {
width: 17%;
white-space: nowrap; //
overflow: hidden;
text-overflow: ellipsis;
}
}
.listStyle:hover {
background: #091f3f;
}
}
}
}
.notoDta {
top: 35%;
width: 20%;
left: 40%;
position: absolute;
text-align: center;
img {
width: 40%;
margin: 5% 30%;
}
p {
color: #fff;
font-size: calc(100vw * 14 / 1920);
margin: -6% 37%;
}
}
// element
:deep() {
.el-date-editor .el-range-input,
.el-range-separator {
color: #fff;
}
.el-input__wrapper {
background: #112d59;
}
.el-input__inner {
color: #fff;
}
.el-button {
background-color: #2758c0;
color: white;
border-color: transparent;
}
}
// ::v-deep .el-select .el-input .el-select__caret {
// color: #fff;
// }
</style>