Files
td_official/src/views/gisHome/component/leftMain.vue
2025-05-13 18:29:12 +08:00

694 lines
20 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="situation">
<div class="main">
<div class="title">
<div class="flex items-center">
<img src="@/assets/images/ligner.png" alt="" />
<div class="w100% flex justify-between items-center">
<div class="subTitle flex items-center">
<img src="@/assets/images/jigong.png" alt="" />
<span>人员情况</span>
</div>
<div class="hint">PERSONNEL SITUATION</div>
</div>
<div class="mark"></div>
</div>
<div class="ligner"></div>
</div>
<div class="cardList">
<div class="card">
<div class="iconImg">
<img src="@/assets/images/totalnumber.png" alt="" />
</div>
<p>总人数</p>
<div class="peopleNum">
<span>{{ constructionUserData?.peopleCount + ' ' }} </span>
</div>
</div>
<div class="card">
<div class="iconImg">
<img src="@/assets/images/attendanceperson.png" alt="" />
</div>
<p>出勤人</p>
<div class="peopleNum">
<span>{{ constructionUserData?.attendanceCount + ' ' }} </span>
</div>
</div>
<div class="card">
<div class="iconImg">
<img src="@/assets/images/Attendancerate.png" alt="" />
</div>
<p>出勤率</p>
<div class="peopleNum">
<span>{{ constructionUserData?.attendanceRate + ' ' }} </span>%
</div>
</div>
</div>
<div class="title">
<div class="flex items-center">
<img src="@/assets/images/ligner.png" alt="" />
<div class="flex justify-between w100% items-center">
<div class="subTitle flex items-center">
<img src="@/assets/images/Machinery.png" alt="" />
<span>机械情况</span>
</div>
<div class="hint">MECHANICAL CONDITION</div>
</div>
<div class="mark"></div>
</div>
<div class="ligner"></div>
</div>
<div class="machinery" id="machineryMain"></div>
<div class="title">
<div class="flex items-center">
<img src="@/assets/images/ligner.png" alt="" />
<div class="flex justify-between w100% items-center">
<div class="subTitle flex items-center">
<img src="@/assets/images/order.png" alt="" />
<span>材料情况</span>
</div>
<div class="hint">MATERIAL STATUS</div>
</div>
<div class="mark"></div>
</div>
<div class="ligner"></div>
</div>
<div class="order" id="orderMain"></div>
</div>
<div class="topleft"></div>
<div class="bottomright"></div>
</div>
</template>
<script lang="ts" setup>
import { getConstructionUserList, getMachineryrList, getMaterialsList } from '@/api/gis';
import { ConstructionUserVO, MachineryrVO, MaterialsVO } from '@/api/gis/type';
import * as echarts from 'echarts';
import { useUserStoreHook } from '@/store/modules/user';
import { enableEchartsAutoScroll } from '@/plugins/scrollEcharts';
const userStore = useUserStoreHook();
//echarts节点
const myMachineryChart = ref(null);
const myOrderChart = ref(null);
type EChartsOption = echarts.EChartsOption;
const constructionUserData = ref<ConstructionUserVO>(null);
const machineryOption = ref<MachineryrVO[]>([]); //机械
const orderOption = ref<MaterialsVO[]>([]); //材料
const stopMachineryScroll = ref(null);
const stopOrderScroll = ref(null);
const machineryDataAxis = computed(() => machineryOption.value.map((item) => item.machineryName)); //x轴数据
const machineryData = computed(() => machineryOption.value.map((item) => item.machineryCount)); //柱状图数据
const orderDataAxis = computed(() => orderOption.value.map((item) => item.materialsName)); //材料x轴数据
const orderPutData = computed(() => orderOption.value.map((item) => item.putCount)); //柱状图领用量数据
const orderOutData = computed(() => orderOption.value.map((item) => item.outCount)); //柱状图出库量数据
const orderRankingData = computed(() => orderOption.value.map((item) => item.value)); //柱状图库存数据
// 从 store 中获取项目列表和当前选中的项目
const currentProject = computed(() => userStore.selectedProject);
//获取施工人员信息
const getConstructionUserData = async () => {
const res = await getConstructionUserList({ projectId: currentProject.value.id });
if (res.code !== 200) return;
constructionUserData.value = res.data;
};
//查询大屏机械列表
const getMachineryData = async () => {
const res = await getMachineryrList({ projectId: currentProject.value.id });
if (res.code !== 200) return;
machineryOption.value = res.data;
initMachinerycharts();
};
//查询大屏材料信息
const getOrderData = async () => {
const res = await getMaterialsList({ projectId: currentProject.value.id });
if (res.code !== 200) return;
orderOption.value = res.data;
initOrderChart();
console.log(orderDataAxis);
};
const initMachinerycharts = () => {
let chartDom = document.getElementById('machineryMain');
myMachineryChart.value = markRaw(echarts.init(chartDom));
let option: EChartsOption;
option = {
title: {
subtext: '单位:台数'
},
grid: {
// 让图表占满容器
top: '45vh',
bottom: '50vh'
},
xAxis: {
data: machineryDataAxis.value,
axisLabel: {
// inside: true,
color: 'rgba(202, 218, 226, 1)',
fontSize: 10,
interval: 0
},
axisTick: {
show: false
},
axisLine: {
show: false
},
z: 10
},
yAxis: {
axisLine: {
show: false
},
axisTick: {
show: false
},
axisLabel: {
color: '#999',
fontSize: 12
},
interval: 1,
splitNumber: 5,
splitLine: {
show: true,
lineStyle: {
color: 'rgba(108, 128, 151, 0.3)',
width: 1,
type: 'dashed'
}
}
},
dataZoom: [
{
show: true,
type: 'slider',
xAxisIndex: 0,
// 滚动条背景颜色
backgroundColor: 'rgba(8, 14, 15, .3)',
// 选中区域的背景颜色
fillerColor: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{
offset: 0,
color: 'rgba(67, 226, 203, 1)' // 0% 处的颜色
},
{
offset: 1,
color: 'rgba(21, 181, 230, 1)' // 100% 处的颜色
}
],
global: false // 缺省为 false
},
showDataShadow: false,
// 手柄大小
showDetail: false, //即拖拽时候是否显示详细数值信息 默认true
moveHandleSize: 0, //移动手柄的大小
// 滚动条高度
height: 12,
borderRadius: 8,
borderColor: 'rgba(8, 14, 15, .3)',
// 滚动条与图表的距离
bottom: 5,
handleSize: 5,
// dataZoom-slider组件离容器下侧的距离
// bottom: 0,
// 两侧缩放手柄的 icon 形状
handleIcon:
'path://M30.9,53.2C16.8,53.2,5.3,41.7,5.3,27.6S16.8,2,30.9,2C45,2,56.4,13.5,56.4,27.6S45,53.2,30.9,53.2z M30.9,3.5M36.9,35.8h-1.3z M27.8,35.8 h-1.3H27L27.8,35.8L27.8,35.8z', // 画一个圆形
handleStyle: {
borderWidth: 5,
borderCap: 'round',
borderRadius: 20, // 设置滑块的圆角大小
color: 'rgba(27,90,169,1)', // 设置滑块的颜色
shadowBlur: 3, // 设置滑块阴影的模糊大小
shadowColor: 'rgba(0, 0, 0, 0.6)', // 设置滑块阴影的颜色
shadowOffsetX: 1, // 设置滑块阴影的水平偏移
shadowOffsetY: 2 // 设置滑块阴影的垂直偏移
},
start: 0,
// 计算初始结束百分比
end: (6 / machineryData.value.length) * 100
}
],
series: [
{
type: 'bar',
showBackground: true,
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: 'rgba(25, 181, 251, 1)' },
{ offset: 1, color: 'rgba(67, 158, 255, 0)' }
])
},
barWidth: '13vh',
data: machineryData.value
}
]
};
option && myMachineryChart.value.setOption(option);
stopMachineryScroll.value = enableEchartsAutoScroll(myMachineryChart.value, machineryDataAxis.value.length, 6, 2000);
};
const initOrderChart = () => {
let chartDom = document.getElementById('orderMain');
myOrderChart.value = markRaw(echarts.init(chartDom));
let option: EChartsOption;
option = {
tooltip: {
trigger: 'axis',
axisPointer: {
// Use axis to trigger tooltip
type: 'shadow' // 'shadow' as default; can also be 'line' or 'shadow'
}
},
legend: {
left: 'right', //位置
icon: 'roundRect', //形状 类型包括 circlerect,lineroundRecttrianglediamondpinarrownone
itemWidth: 15, // 设置宽度
itemHeight: 4, // 设置高度
itemGap: 27, // 设置间距
top: 10, // 设置圆角
data: ['入库量', '领用量'],
textStyle: {
//文字样式
color: '#B4CEFF',
fontSize: '12'
}
},
grid: {
left: '5%', //距离dom间距
right: '4%',
top: '20%',
bottom: '1%',
height: '80%'
},
xAxis: {
type: 'value',
show: false
},
yAxis: {
type: 'category',
data: orderDataAxis.value,
offset: 0,
axisLine: {
show: false
},
axisTick: {
show: false
},
inverse: true,
axisLabel: {
formatter: function (value) {
let bgType = '';
let index = orderDataAxis.value.indexOf(value);
switch (index) {
case 0:
bgType = 'a';
break;
case 1:
bgType = 'b';
break;
case 2:
bgType = 'c';
break;
default:
return `No.${index + 1} {value|${value}}`;
}
return `{${bgType}|No.${index + 1}} {value|${value}}`;
},
align: 'left',
verticalAlign: 'bottom',
yAxisIndex: 0,
// 横坐标 分割线等取消显示
padding: [0, 10, 10, 6],
axisTick: {
show: false
},
axisLine: {
show: false
},
color: 'rgba(230, 247, 255, 1)',
rich: {
a: {
color: 'rgba(230, 247, 255, 1)',
fontSize: 12,
align: 'center',
width: 31,
height: 23,
backgroundColor: new echarts.graphic.LinearGradient(0, 0, 1, 0, [
{ offset: 0, color: 'rgba(255, 208, 59, 1)' },
{ offset: 1, color: 'rgba(255, 208, 59, 0)' }
])
},
b: {
color: 'rgba(230, 247, 255, 1)',
fontSize: 12,
align: 'left',
width: 31,
height: 23,
backgroundColor: new echarts.graphic.LinearGradient(0, 0, 1, 0, [
{ offset: 0, color: 'rgba(31, 189, 237, 1)' },
{ offset: 1, color: 'rgba(31, 189, 237, 0)' }
])
},
c: {
color: 'rgba(230, 247, 255, 1)',
fontSize: 12,
align: 'left',
width: 31,
height: 23,
backgroundColor: new echarts.graphic.LinearGradient(0, 0, 1, 0, [
{ offset: 0, color: 'rgba(67, 226, 203, 1)' },
{ offset: 1, color: 'rgba(67, 226, 203, 0)' }
])
}
}
}
},
dataZoom: [
{
// 设置滚动条的隐藏或显示
show: true,
// 设置类型
type: 'slider',
// 设置背景颜色
backgroundColor: 'rgba(8, 14, 15, .3)',
// 设置选中范围的填充颜色
fillerColor: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{
offset: 0,
color: 'rgba(67, 226, 203, .5)' // 0% 处的颜色
},
{
offset: 1,
color: 'rgba(21, 181, 230, .5)' // 100% 处的颜色
}
],
global: false // 缺省为 false
},
borderColor: 'rgba(8, 14, 15, .3)',
borderCap: 'round',
// 是否显示detail即拖拽时候显示详细数值信息
showDetail: false,
// handleSize: 0,
moveHandleSize: 0, //移动手柄的大小
borderRadius: 20, //滚动条圆角
// 数据窗口范围的起始数值
startValue: 0,
// 数据窗口范围的结束数值(一页显示多少条数据)
endValue: 4,
handleSize: 5,
// dataZoom-slider组件离容器下侧的距离
// bottom: 0,
// 两侧缩放手柄的 icon 形状
handleIcon:
'path://M30.9,53.2C16.8,53.2,5.3,41.7,5.3,27.6S16.8,2,30.9,2C45,2,56.4,13.5,56.4,27.6S45,53.2,30.9,53.2z M30.9,3.5M36.9,35.8h-1.3z M27.8,35.8 h-1.3H27L27.8,35.8L27.8,35.8z', // 画一个圆形
handleStyle: {
borderWidth: 5,
borderCap: 'round',
borderRadius: 20, // 设置滑块的圆角大小
color: 'rgba(27,90,169,1)', // 设置滑块的颜色
shadowBlur: 3, // 设置滑块阴影的模糊大小
shadowColor: 'rgba(0, 0, 0, 0.6)', // 设置滑块阴影的颜色
shadowOffsetX: 1, // 设置滑块阴影的水平偏移
shadowOffsetY: 2 // 设置滑块阴影的垂直偏移
},
// 控制哪个轴如果是number表示控制一个轴
// 如果是Array表示控制多个轴。此处控制第二根轴
yAxisIndex: [0, 1],
// empty当前数据窗口外的数据被设置为空。
// 即不会影响其他轴的数据范围
filterMode: 'empty',
// 滚动条高度
width: 12,
// 滚动条显示位置
height: '70%',
// 距离右边
right: 5,
showDataShadow: false, //是否显示数据阴影 默认auto
// 组件离容器上侧的距离
// 如果top的值为'top', 'middle', 'bottom',组件会根据相应的位置自动对齐
top: 'middle'
},
{
// 没有下面这块的话,只能拖动滚动条,
// 鼠标滚轮在区域内不能控制外部滚动条
type: 'inside',
// 控制哪个轴如果是number表示控制一个轴
// 如果是Array表示控制多个轴。此处控制第二根轴
yAxisIndex: [0, 1],
width: 20,
// 滚轮是否触发缩放
zoomOnMouseWheel: false,
// 鼠标移动能否触发平移
moveOnMouseMove: true,
// 鼠标滚轮能否触发平移
moveOnMouseWheel: true
}
],
series: [
{
name: '入库量',
type: 'bar',
stack: 'total',
itemStyle: {
color: 'rgba(67, 226, 203, 1)'
},
emphasis: {
focus: 'series'
},
data: orderPutData.value,
barWidth: 3
},
{
name: '领用量',
type: 'bar',
stack: 'total',
itemStyle: {
color: 'rgba(31, 189, 237, 1)'
},
emphasis: {
focus: 'series'
},
data: orderOutData.value,
showBackground: true,
barWidth: 3,
backgroundStyle: {
color: 'rgba(217, 231, 255, 0.1)'
}
}
]
};
option && myOrderChart.value.setOption(option);
stopOrderScroll.value = enableEchartsAutoScroll(myOrderChart.value, orderDataAxis.value.length, 5, 2000);
};
// 防抖函数
const debounce = <T,>(func: (this: T, ...args: any[]) => void, delay: number) => {
let timer: ReturnType<typeof setTimeout> | null = null;
return function (this: T, ...args: any[]) {
const context = this;
if (timer) clearTimeout(timer);
timer = setTimeout(() => {
func.apply(context, args);
}, delay);
};
};
// 窗口大小变化时触发的函数
const handleResize = () => {
myMachineryChart.value && myMachineryChart.value.dispose();
myOrderChart.value && myOrderChart.value.dispose();
initMachinerycharts();
initOrderChart();
};
const debouncedHandleResize = debounce(handleResize, 300);
onMounted(() => {
getOrderData();
getConstructionUserData();
getMachineryData();
window.addEventListener('resize', debouncedHandleResize); //监听窗口变化重新生成echarts
});
onUnmounted(() => {
window.removeEventListener('resize', debouncedHandleResize);
stopMachineryScroll.value();
stopOrderScroll.value();
});
</script>
<style lang="scss" scoped>
@import '../css/gis.scss';
#machineryMain {
width: vw(421);
height: vh(222);
margin-left: vw(14);
margin-bottom: vh(30);
}
#orderMain {
width: 100%;
padding-right: vw(14);
height: vh(335);
}
.title {
> div > img {
width: vw(14);
height: vh(35);
}
.subTitle {
img {
width: vw(18.8);
height: vh(20);
margin-right: vw(10);
}
span {
text-shadow: 0 0 vw(8) rgba(2, 3, 7, 0.35);
font-size: vw(16);
letter-spacing: 0;
color: rgba(255, 255, 255, 1);
font-family: 'DOUYUFont';
padding-top: vh(5);
}
}
.hint {
font-size: vw(14);
font-weight: 400;
color: rgba(204, 204, 204, 0.5);
margin-right: vw(20);
font-family: 'Roboto';
}
.mark {
width: vw(2);
height: vh(14);
background: rgba(67, 226, 203);
margin-right: vw(10);
}
.ligner {
height: vh(1);
background: linear-gradient(to right, transparent 0%, #43e2cb 90%);
margin-left: vw(14);
}
}
.situation {
width: vw(449);
height: vh(927);
background: rgb(1, 26, 33, 0.4);
backdrop-filter: blur(vw(8));
position: absolute;
top: vh(122);
left: vw(21);
z-index: 2;
.main {
padding-top: vh(30);
.cardList {
padding: 0 vw(20);
margin-top: vh(20);
margin-bottom: vh(30);
display: flex;
justify-content: space-between;
.card {
border: vw(1) dashed rgba(67, 226, 203, 0.3);
width: vw(112);
height: vh(155);
padding-top: vh(15);
text-align: center;
color: rgba(255, 255, 255, 1);
img {
width: vw(44);
margin-bottom: vh(10);
}
> p {
font-size: vw(14);
margin-top: 0;
margin-bottom: vh(10);
font-family: '思源黑体';
}
.peopleNum {
font-weight: 400;
font-size: vw(14);
text-shadow: 0 vw(1.24) vw(6.21) rgba(0, 190, 247, 1);
color: rgba(230, 247, 255, 1);
font-family: 'Roboto';
span {
font-size: vw(24);
font-weight: 700;
}
}
}
}
}
&::before {
content: '';
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
padding: vw(2);
background: linear-gradient(to bottom right, #43e2cb 0%, transparent 50%);
-webkit-mask:
linear-gradient(#fff 0 0) content-box,
linear-gradient(#fff 0 0);
mask-composite: exclude;
opacity: 0.4;
z-index: -1;
}
.topleft {
width: vw(7);
height: vw(7);
position: absolute;
top: vh(1);
left: vw(1);
background-image: url('@/assets/images/topleft.png');
}
.bottomright {
width: vw(7);
height: vw(7);
position: absolute;
bottom: 0;
right: 0;
background-image: url('@/assets/images/bottomright.png');
}
}
</style>