劳务管理大屏

dev_xd
haha 2025-05-21 00:47:25 +08:00
parent 5657b2d806
commit 95d37fab45
9 changed files with 395 additions and 358 deletions

View File

@ -9,7 +9,8 @@ import safety from "./safety";
import tower from "./tower";
import powerIot from "./powerIot";
import aiWarning from "./aiWarning";
import planSchedule from './planSchedule';
import planSchedule from "./planSchedule";
import labor from "./labor";
export default {
http: axios,
downFile: download,
@ -24,4 +25,5 @@ export default {
powerIot,
aiWarning,
planSchedule,
labor,
};

View File

@ -0,0 +1,74 @@
import request from "@/utils/request";
/**
* 分组统计人员年龄省份统计信息
* @param {*} type
* @param {*} prjId
* @returns
*/
const groupByWorker = (type, prjId) => {
return request({
url: `/manage/bgscreen/labor/groupByWorker/${type}/${prjId}`,
method: "get",
});
};
/**
* 劳务人员七天出勤趋势
* @param {*} type
* @param {*} prjId
* @returns
*/
const groupAttendanceLastWeek = (type, prjId) => {
return request({
url: `/manage/bgscreen/labor/groupAttendanceLastWeek/${type}/${prjId}`,
method: "get",
});
};
/**
* 劳务人员工种占比
* @param {*} type
* @param {*} prjId
* @returns
*/
const getWorkerCountGroupByType = (type, prjId) => {
return request({
url: `/manage/bgscreen/labor/getWorkerCountGroupByType/${type}/${prjId}`,
method: "get",
});
};
/**
* 实时考勤进出
* @param {*} type
* @param {*} prjId
* @returns
*/
const getRealAttendance = (type, prjId) => {
return request({
url: `/manage/bgscreen/labor/getRealAttendance/${type}/${prjId}`,
method: "get",
});
};
/**
* 考勤预警
* @param {*} type
* @param {*} prjId
* @returns
*/
const getAttendanceAlert = (type, prjId) => {
return request({
url: `/manage/bgscreen/labor/getAttendanceAlert/${type}/${prjId}`,
method: "get",
});
};
export default {
groupByWorker,
groupAttendanceLastWeek,
getWorkerCountGroupByType,
groupAttendanceLastWeek,
getRealAttendance,
getAttendanceAlert,
};

View File

@ -539,7 +539,7 @@ export default {
left: '5%',
right: '5%',
bottom: '0%',
top: '15%',
top: '5%',
containLabel: true,
},
tooltip: {

View File

@ -2,10 +2,43 @@
<div class="project-labor main-page">
<el-col :span="6" class="h100">
<module-one-1-1 label="劳务人员概况">
<img src="images/icon2001.png" class="img-openwin" />
<div class="attendance-info warning-info">
<div class="attendance-info-title warning-info-title">
<div :class="attendanceNav == 0 ? 'active' : ''" @click="doAttendanceNav(0, '在岗人员')">在岗人员</div>
<div :class="attendanceNav == 1 ? 'active' : ''" @click="doAttendanceNav(1, '今日出勤')">今日出勤</div>
</div>
<el-row>
<el-col :span="8" class="attendance-tag">
<div class="survey_content">
<div class="survey_content_img">
<img
:src="
attendanceNav == 0
? 'images/survey_icon_4.png'
: 'images/survey_icon_5.png'
"
/>
</div>
</div>
<div class="survey_content_number labour-survey_content_number">
<div class="survey_content_value">
<span>{{ attendanceTotal }}</span>
</div>
<p>{{ attendanceNav == 0 ? "在岗人员" : "今日出勤" }}</p>
</div>
</el-col>
<el-col :span="16">
<staffSurveyChart :key="chartKey" :height="$dpi() == '1K' ? '220px' : $dpi() == '2K' ? '330px' : '550px'" :data="attendanceData" :width="140"></staffSurveyChart>
</el-col>
</el-row>
</div>
</module-one-1-1>
<module-one-1-1 label="劳务人员七天出勤趋势">
<my-chart :key="chartKey" id="prj_labor_chart2" width="100%" height="100%" :render="renderChart2"></my-chart>
</module-one-1-1>
<module-one-1-1 label="劳务人员年龄分布">
<my-chart :key="chartKey" id="prj_labor_chart3" width="100%" height="100%" :render="renderChart3"></my-chart>
</module-one-1-1>
<module-one-1-1 label="劳务人员七天出勤趋势"></module-one-1-1>
<module-one-1-1 label="劳务人员年龄分布"></module-one-1-1>
</el-col>
<el-col :span="12" class="h100">
<module-one-2-2 label class="no-title no-border">
@ -15,7 +48,9 @@
</el-col>
<el-col :span="6" class="h100">
<module-one-1-1 label="工种占比分布"></module-one-1-1>
<module-one-1-1 label="人员地区分布"></module-one-1-1>
<module-one-1-1 label="人员地区分布">
<my-chart :key="chartKey" id="prj_labor_chart5" width="100%" height="100%" :render="renderChart5"></my-chart>
</module-one-1-1>
<module-one-1-1 label="人员预期"></module-one-1-1>
</el-col>
</div>
@ -23,13 +58,24 @@
<script>
import debounce from 'lodash.debounce'
import mapbg from '@/assets/images/map-bg.png'
import staffSurveyChart from '@/components/staffSurveyChart.vue'
export default {
components: { staffSurveyChart },
data() {
return {
dpi: '',
chartKey: 0,
selProject: null,
attendanceNav: 0,
attendanceTotal: 0,
attendanceData: [
{ text: '普通工种', value: 0, id: 0 },
{ text: '特殊工种', value: 0, id: 0 },
{ text: '管理人员', value: 0, id: 0 },
],
nativeList: [],
ageList: [],
weekData: [],
}
},
methods: {
@ -37,295 +83,272 @@ export default {
if (!this.selProject) {
return
}
this.loadAttendanceData()
this.getGroupByWorker()
this.groupAttendanceLastWeek()
},
groupAttendanceLastWeek() {
this.$api.labor.groupAttendanceLastWeek(this.selProject.vendorsCode, this.selProject.id).then((d) => {
this.weekData = d.data.map((it) => {
return {
name: it.dt.substring(5).replace('-', '.'),
value: it.cnt,
//value: it.cnt + parseInt(Math.random() * 100),
}
})
})
},
getGroupByWorker() {
this.$api.labor.groupByWorker(this.selProject.vendorsCode, this.selProject.id).then((d) => {
this.nativeList = (d.data.native || []).map((it) => {
it.name = it.nativePlace
it.value = it.count
return it
})
this.ageList = (d.data.age || []).map((it) => {
it.name = ['', '18~30', '30~40', '40~55', '55岁以上'][it.age]
it.value = it.count
return it
})
this.chartKey++
})
},
renderChart2(opt) {
const areaOption = {
grid: {
left: '5%',
right: '5%',
bottom: '0%',
top: '15%',
containLabel: true,
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow',
label: {
backgroundColor: '#6a7985',
},
},
},
xAxis: {
type: 'category',
data: this.weekData.map((item) => item.name),
axisLabel: {
textStyle: {
color: '#0df2c8',
},
},
},
yAxis: {
type: 'value',
minInterval: 1,
axisLabel: {
textStyle: {
color: '#0df2c8',
},
},
},
series: [
{
name: '出勤人数',
type: 'line',
areaStyle: {
color: 'rgba(13, 204, 242,0.5)', //
},
label: {
show: true,
position: 'top', // 线
textStyle: {
color: '#0df2c8', //
},
},
data: this.weekData.map((item) => item.value),
},
],
}
return areaOption
},
renderChart3(opt) {
let pieOption = {
tooltip: {
trigger: 'item',
formatter: '{b} 岁 <br/> {c} 人 ({d}%)',
},
legend: {
orient: 'vertical',
left: 'left',
top: 'center',
data: this.ageList.map((it) => it.name),
textStyle: {
color: '#19c0e6',
},
},
series: [
{
type: 'pie',
radius: '55%',
center: ['50%', '60%'],
data: this.ageList,
label: {
normal: {
formatter: '{b}\n{c}',
textStyle: {
color: '#0df2c8',
},
},
emphasis: {
formatter: '{b}\n{c}',
textStyle: {
color: '#0df2c8',
},
},
},
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
},
},
},
],
}
return pieOption
},
renderChart5(opt) {
const pieOption = {
tooltip: {
trigger: 'item',
formatter: '{b}: {c} ({d}%)',
},
series: [
{
type: 'pie',
radius: ['50%', '70%'], //
avoidLabelOverlap: false,
label: {
normal: {
formatter: '{b}\n{c}',
textStyle: {
color: '#0df2c8',
},
},
emphasis: {
formatter: '{b}\n{c}',
textStyle: {
color: '#0df2c8',
},
},
},
emphasis: {
label: {
show: true,
fontSize: '30',
fontWeight: 'bold',
},
},
data: this.nativeList,
},
],
}
return pieOption
},
doAttendanceNav(n) {
this.attendanceNav = n
this.loadAttendanceData()
},
loadJhAttendanceData() {
let data = {
id: this.attendanceNav,
projectId: this.selProject.id,
attendanceTime: this.$dt(new Date()).format('YYYY-MM-DD'),
}
let ajax = this.$api.detail.groupByComany
if (this.attendanceNav != 1) {
ajax = this.$api.detail.groupAllByComany
}
ajax(data).then((d) => {
let tmps = d.data || []
const func = (ids) => {
let sum = 0
tmps.filter((it) => ids.includes(it.companyTypeId))
.map((it) => it.id)
.forEach((it) => {
sum += it * 1
})
return sum
}
this.attendanceTotal = 0
this.attendanceData = [
{ text: '劳务人员', value: func(['0', '2', '3', '4', '5']) },
{ text: '监理人员', value: func(['8']) },
{ text: '总包人员', value: func(['1', '6']) },
]
this.attendanceData.forEach((it) => {
this.attendanceTotal += it.value
})
})
},
loadUniAttendanceData() {
let ajax = this.$api.detail.groupByCraftType
let posData = {
comId: this.selProject.comId,
projectId: this.selProject.id,
}
if (this.attendanceNav == 1) {
ajax = this.$api.detail.groupByCraftTypeByAttendance
posData.createTime = this.$dt(new Date()).format('YYYY-MM-DD')
//posData.createTime = '2024-10-13'
}
ajax(posData).then((d) => {
this.attendanceData = (d.data || []).map((it) => {
return {
text: it.createBy,
value: it.id || 0,
id: it.craftType,
}
})
let count = 0
this.attendanceData.forEach((it) => {
count += it.value
})
this.attendanceTotal = count
})
},
renderMap(opt, myChart) {
var name_title = '中国人民大学2017年各省市计划录取人数'
var subname = '数据爬取自千栀网\n\n上海、浙江无文理科录取人数'
var nameColor = ' rgb(55, 75, 113)'
var name_fontFamily = '等线'
var subname_fontSize = 15
var name_fontSize = 18
var mapName = 'china'
var data = [
{ name: '北京', value: 177 },
{ name: '天津', value: 42 },
{ name: '河北', value: 102 },
{ name: '山西', value: 81 },
{ name: '内蒙古', value: 47 },
{ name: '辽宁', value: 67 },
{ name: '吉林', value: 82 },
{ name: '黑龙江', value: 66 },
{ name: '上海', value: 24 },
{ name: '江苏', value: 92 },
{ name: '浙江', value: 114 },
{ name: '安徽', value: 109 },
{ name: '福建', value: 116 },
{ name: '江西', value: 91 },
{ name: '山东', value: 119 },
{ name: '河南', value: 137 },
{ name: '湖北', value: 0 },
{ name: '湖南', value: 114 },
{ name: '重庆', value: 91 },
{ name: '四川', value: 125 },
{ name: '贵州', value: 62 },
{ name: '云南', value: 83 },
{ name: '西藏', value: 9 },
{ name: '陕西', value: 80 },
{ name: '甘肃', value: 56 },
{ name: '青海', value: 10 },
{ name: '宁夏', value: 18 },
{ name: '新疆', value: 67 },
{ name: '广东', value: 123 },
{ name: '广西', value: 59 },
{ name: '海南', value: 14 },
]
var geoCoordMap = {}
var toolTipData = [
{
name: '北京',
value: [
{ name: '文科', value: 95 },
{ name: '理科', value: 82 },
],
},
{
name: '天津',
value: [
{ name: '文科', value: 22 },
{ name: '理科', value: 20 },
],
},
{
name: '河北',
value: [
{ name: '文科', value: 60 },
{ name: '理科', value: 42 },
],
},
{
name: '山西',
value: [
{ name: '文科', value: 40 },
{ name: '理科', value: 41 },
],
},
{
name: '内蒙古',
value: [
{ name: '文科', value: 23 },
{ name: '理科', value: 24 },
],
},
{
name: '辽宁',
value: [
{ name: '文科', value: 39 },
{ name: '理科', value: 28 },
],
},
{
name: '吉林',
value: [
{ name: '文科', value: 41 },
{ name: '理科', value: 41 },
],
},
{
name: '黑龙江',
value: [
{ name: '文科', value: 35 },
{ name: '理科', value: 31 },
],
},
{
name: '上海',
value: [
{ name: '文科', value: 12 },
{ name: '理科', value: 12 },
],
},
{
name: '江苏',
value: [
{ name: '文科', value: 47 },
{ name: '理科', value: 45 },
],
},
{
name: '浙江',
value: [
{ name: '文科', value: 57 },
{ name: '理科', value: 57 },
],
},
{
name: '安徽',
value: [
{ name: '文科', value: 57 },
{ name: '理科', value: 52 },
],
},
{
name: '福建',
value: [
{ name: '文科', value: 59 },
{ name: '理科', value: 57 },
],
},
{
name: '江西',
value: [
{ name: '文科', value: 49 },
{ name: '理科', value: 42 },
],
},
{
name: '山东',
value: [
{ name: '文科', value: 67 },
{ name: '理科', value: 52 },
],
},
{
name: '河南',
value: [
{ name: '文科', value: 69 },
{ name: '理科', value: 68 },
],
},
{
name: '湖北',
value: [
{ name: '文科', value: 60 },
{ name: '理科', value: 56 },
],
},
{
name: '湖南',
value: [
{ name: '文科', value: 62 },
{ name: '理科', value: 52 },
],
},
{
name: '重庆',
value: [
{ name: '文科', value: 47 },
{ name: '理科', value: 44 },
],
},
{
name: '四川',
value: [
{ name: '文科', value: 65 },
{ name: '理科', value: 60 },
],
},
{
name: '贵州',
value: [
{ name: '文科', value: 32 },
{ name: '理科', value: 30 },
],
},
{
name: '云南',
value: [
{ name: '文科', value: 42 },
{ name: '理科', value: 41 },
],
},
{
name: '西藏',
value: [
{ name: '文科', value: 5 },
{ name: '理科', value: 4 },
],
},
{
name: '陕西',
value: [
{ name: '文科', value: 38 },
{ name: '理科', value: 42 },
],
},
{
name: '甘肃',
value: [
{ name: '文科', value: 28 },
{ name: '理科', value: 28 },
],
},
{
name: '青海',
value: [
{ name: '文科', value: 5 },
{ name: '理科', value: 5 },
],
},
{
name: '宁夏',
value: [
{ name: '文科', value: 10 },
{ name: '理科', value: 8 },
],
},
{
name: '新疆',
value: [
{ name: '文科', value: 36 },
{ name: '理科', value: 31 },
],
},
{
name: '广东',
value: [
{ name: '文科', value: 63 },
{ name: '理科', value: 60 },
],
},
{
name: '广西',
value: [
{ name: '文科', value: 29 },
{ name: '理科', value: 30 },
],
},
{
name: '海南',
value: [
{ name: '文科', value: 8 },
{ name: '理科', value: 6 },
],
},
]
let mapName = 'china'
let data = this.nativeList
let geoCoordMap = {}
/*获取地图数据*/
var mapFeatures = echarts.getMap(mapName).geoJson.features
let mapFeatures = echarts.getMap(mapName).geoJson.features
let allData = []
mapFeatures.forEach(function (v) {
//
var name = v.properties.name
//
geoCoordMap[name] = v.properties.cp
allData.push({
name: name,
value: 0,
})
})
// console.log("============geoCoordMap===================")
// console.log(geoCoordMap)
// console.log("================data======================")
//console.log(data)
//console.log(toolTipData)
var max = 480,
min = 9 // todo
var maxSize4Pin = 100,
let max = 880,
min = 3 // todo
let maxSize4Pin = 80,
minSize4Pin = 20
var convertData = function (data, lbl) {
var res = []
for (var i = 0; i < data.length; i++) {
var geoCoord = geoCoordMap[data[i].name]
let convertData = function (data, lbl) {
let res = []
if (lbl == 1) {
data = allData
}
for (let i = 0; i < data.length; i++) {
let geoCoord = geoCoordMap[data[i].name]
if (geoCoord) {
res.push({
name: data[i].name,
@ -339,63 +362,6 @@ export default {
return res.filter((d) => d.value[2] > 0)
}
let option = {
title: {
show: false,
text: name_title,
subtext: subname,
x: 'center',
textStyle: {
color: nameColor,
fontFamily: name_fontFamily,
fontSize: name_fontSize,
},
subtextStyle: {
fontSize: subname_fontSize,
fontFamily: name_fontFamily,
},
},
tooltip: {
show: false,
trigger: 'item',
formatter: function (params) {
if (typeof params.value[2] == 'undefined') {
var toolTiphtml = ''
for (var i = 0; i < toolTipData.length; i++) {
if (params.name == toolTipData[i].name) {
toolTiphtml += toolTipData[i].name + ':<br>'
for (var j = 0; j < toolTipData[i].value.length; j++) {
toolTiphtml += toolTipData[i].value[j].name + ':' + toolTipData[i].value[j].value + '<br>'
}
}
}
console.log(toolTiphtml)
// console.log(convertData(data))
return toolTiphtml
} else {
var toolTiphtml = ''
for (var i = 0; i < toolTipData.length; i++) {
if (params.name == toolTipData[i].name) {
toolTiphtml += toolTipData[i].name + ':<br>'
for (var j = 0; j < toolTipData[i].value.length; j++) {
toolTiphtml += toolTipData[i].value[j].name + ':' + toolTipData[i].value[j].value + '<br>'
}
}
}
console.log(toolTiphtml)
// console.log(convertData(data))
return toolTiphtml
}
},
},
// legend: {
// orient: 'vertical',
// y: 'bottom',
// x: 'right',
// data: ['credit_pm2.5'],
// textStyle: {
// color: '#fff'
// }
// },
visualMap: {
show: false,
min: 0,
@ -420,20 +386,6 @@ export default {
// color: ['#00467F', '#A5CC82'] // 绿
},
},
/*工具按钮组*/
// toolbox: {
// show: true,
// orient: 'vertical',
// left: 'right',
// top: 'center',
// feature: {
// dataView: {
// readOnly: false
// },
// restore: {},
// saveAsImage: {}
// }
// },
geo: {
show: true,
map: mapName,
@ -530,8 +482,16 @@ export default {
}
return option
},
loadAttendanceData() {
if (this.selProject.vendorsCode != 'uni') {
this.loadJhAttendanceData()
} else {
this.loadUniAttendanceData()
}
},
},
mounted() {
window.plApp = this
this.$store.dispatch('ChangeNav', 102)
this.$bus.$on(
'projectChange',

View File

@ -397,10 +397,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
pi.floor_area, pi.total_output_value, pi.planned_completion_time, pi.scheduled_start_time, pi.actual_operating_time, pi.actual_completion_time,
pi.contract_amount, pi.paid_amount, pi.on_account_amount, pi.project_schedule, pi.project_summarize, pi.project_qr_code, pi.project_status,
pi.project_sort, pi.is_del, pi.create_by, pi.create_time, pi.update_by, pi.update_time, pi.remark,
ps.id ps_id,ps.org_name ps_org_name,ps.org_logo ps_org_logo,ps.org_image ps_org_image,ps.org_video ps_org_video,ps.org_plane ps_org_plane
ps.id ps_id,ps.org_name ps_org_name,ps.org_logo ps_org_logo,ps.org_image ps_org_image,ps.org_video ps_org_video,ps.org_plane ps_org_plane,cfg.vendors_code
from pro_project_info pi
left join pro_project_info_setting ps on pi.id=ps.project_id
left join sys_dept sd on pi.com_id=sd.dept_id
left join attendance_cfg cfg on cfg.project_id=pi.id
left join sys_dict_data sdd1 on sdd1.dict_value = pi.project_type and sdd1.dict_type='pro_project_type'
<if test="currentUserId != null "> left join pro_project_info_subdepts_users psu on psu.project_id = pi.id </if>
<where>

View File

@ -653,7 +653,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
from (
select DISTINCT workerId, date(ifnull(attendance_time,attendance_out_time)) dt
from sur_project_attendance_data_${year} where
is_del !=1 and state=0
is_del !=1
and projectid=#{prjId}
AND (date(attendance_time) &gt;= date(#{dateStart}) or date(attendance_out_time) &gt;= date(#{dateStart}))
AND (date(attendance_time) &lt;= date(#{dateEnd}) or date(attendance_out_time) &lt;= date(#{dateEnd}))

View File

@ -28,7 +28,7 @@ import java.util.List;
* -
*/
@RestController
@RequestMapping("/bgscreen/tower")
@RequestMapping("/bgscreen/labor")
public class LaborController extends BaseController {
@Autowired

View File

@ -1752,7 +1752,7 @@ public class ProProjectInfoSubdeptsUsersServiceImpl implements IProProjectInfoSu
JSONObject jo=new JSONObject();
jo.put("age",k);
jo.put("count",v.size());
arrNav.add(jo);
arrAge.add(jo);
});
joOut.put("age",arrAge);
return joOut;

View File

@ -353,7 +353,7 @@ public class SurProjectAttendanceUserServiceImpl implements ISurProjectAttendanc
JSONObject jo=new JSONObject();
jo.put("age",k);
jo.put("count",v.size());
arrNav.add(jo);
arrAge.add(jo);
});
joOut.put("age",arrAge);
return joOut;