init:first commit of plus-ui
This commit is contained in:
151
src/views/gisHome/component/autoScroller.vue
Normal file
151
src/views/gisHome/component/autoScroller.vue
Normal file
@ -0,0 +1,151 @@
|
||||
<template>
|
||||
<div class="auto-scroll-container" @mouseenter="onMouseEnter" @mouseleave="onMouseLeave" ref="container">
|
||||
<div class="auto-scroll-content" ref="content" :style="{ transform: scrollEnabled ? `translate3d(0, ${Math.round(scrollY)}px, 0)` : 'none' }">
|
||||
<div class="auto-scroll-item" :class="safety ? 'safety' : ''" v-for="(item, index) in displayList" :key="index">
|
||||
<slot :item="item" :index="index">{{ item }}</slot>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
interface Props {
|
||||
items: any[];
|
||||
speed?: number;
|
||||
height?: number;
|
||||
minItems?: number;
|
||||
autoScroll?: boolean;
|
||||
safety?: boolean;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
speed: 0.4,
|
||||
minItems: 2,
|
||||
autoScroll: true
|
||||
});
|
||||
|
||||
const container = ref<HTMLElement | null>(null);
|
||||
const content = ref<HTMLElement | null>(null);
|
||||
|
||||
let scrollY = 0;
|
||||
let animationFrameId: number | null = null;
|
||||
let manualPaused = false;
|
||||
let manualControl = false;
|
||||
|
||||
// 是否满足滚动条件
|
||||
const scrollEnabled = computed(() => props.items.length >= props.minItems);
|
||||
|
||||
// 展示数据列表(数据足够时复制一份)
|
||||
const displayList = computed(() => (scrollEnabled.value ? [...props.items, ...props.items] : props.items));
|
||||
|
||||
// 修正 scrollY 范围
|
||||
function normalizeScrollY(contentHeight: number) {
|
||||
scrollY = (((scrollY % contentHeight) + contentHeight) % contentHeight) - contentHeight;
|
||||
}
|
||||
|
||||
// 滚动逻辑
|
||||
function step() {
|
||||
if (!content.value) return;
|
||||
const contentHeight = content.value.offsetHeight / 2;
|
||||
scrollY -= props.speed;
|
||||
normalizeScrollY(contentHeight);
|
||||
content.value.style.transform = `translate3d(0, ${Math.round(scrollY)}px, 0)`;
|
||||
animationFrameId = requestAnimationFrame(step);
|
||||
}
|
||||
|
||||
function startScroll() {
|
||||
if (!animationFrameId) {
|
||||
animationFrameId = requestAnimationFrame(step);
|
||||
}
|
||||
}
|
||||
|
||||
function pauseScroll() {
|
||||
if (animationFrameId) {
|
||||
cancelAnimationFrame(animationFrameId);
|
||||
animationFrameId = null;
|
||||
}
|
||||
}
|
||||
|
||||
function onMouseEnter() {
|
||||
if (!manualControl) pauseScroll();
|
||||
}
|
||||
|
||||
function onMouseLeave() {
|
||||
if (!manualControl && props.autoScroll) startScroll();
|
||||
}
|
||||
|
||||
function onWheel(e: WheelEvent) {
|
||||
if (!content.value || !container.value) return;
|
||||
|
||||
const contentHeight = content.value.offsetHeight / (scrollEnabled.value ? 2 : 1);
|
||||
const containerHeight = container.value.offsetHeight;
|
||||
|
||||
if (contentHeight <= containerHeight) {
|
||||
e.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
manualPaused = true;
|
||||
pauseScroll();
|
||||
scrollY -= e.deltaY;
|
||||
normalizeScrollY(contentHeight);
|
||||
content.value.style.transform = `translate3d(0, ${Math.round(scrollY)}px, 0)`;
|
||||
}
|
||||
|
||||
// 生命周期
|
||||
onMounted(() => {
|
||||
if (scrollEnabled.value && props.autoScroll) {
|
||||
startScroll();
|
||||
}
|
||||
container.value?.addEventListener('wheel', onWheel, { passive: false });
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
pauseScroll();
|
||||
container.value?.removeEventListener('wheel', onWheel);
|
||||
});
|
||||
|
||||
// 监听数据是否滚动条件满足
|
||||
watch(scrollEnabled, (newVal) => {
|
||||
if (!newVal) {
|
||||
pauseScroll();
|
||||
scrollY = 0;
|
||||
if (content.value) content.value.style.transform = 'none';
|
||||
} else if (props.autoScroll && !manualPaused && !manualControl) {
|
||||
startScroll();
|
||||
}
|
||||
});
|
||||
|
||||
// 暴露控制方法
|
||||
defineExpose({
|
||||
pause: (): void => {
|
||||
manualControl = true;
|
||||
pauseScroll();
|
||||
},
|
||||
resume: (): void => {
|
||||
manualControl = false;
|
||||
if (scrollEnabled.value) startScroll();
|
||||
},
|
||||
reset: (): void => {
|
||||
scrollY = 0;
|
||||
if (content.value) {
|
||||
content.value.style.transform = 'translate3d(0, 0, 0)';
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.auto-scroll-container {
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
.auto-scroll-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
will-change: transform;
|
||||
}
|
||||
.safety:nth-child(odd) {
|
||||
background-color: rgba(67, 226, 203, 0.2);
|
||||
}
|
||||
</style>
|
148
src/views/gisHome/component/carousel.vue
Normal file
148
src/views/gisHome/component/carousel.vue
Normal file
@ -0,0 +1,148 @@
|
||||
<template>
|
||||
<div class="carousel">
|
||||
<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/robot-line.png" alt="" />
|
||||
<span>AI安全巡检</span>
|
||||
</div>
|
||||
<div class="hint">AI SAFETY INSPECTION</div>
|
||||
</div>
|
||||
<div class="mark"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="carousellist flex items-center">
|
||||
<div>
|
||||
<img src="@/assets/images/toleft.png" alt="" />
|
||||
</div>
|
||||
<div class="carouselcontainer flex justify-between">
|
||||
<div class="carouselItem flex flex-col justify-between" v-for="item in 5">
|
||||
<img src="@/assets/images/carousel.png" alt="" />
|
||||
<div class="time">03-18 15:00</div>
|
||||
<div class="remark">未佩戴安全帽</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<img src="@/assets/images/toright.png" alt="" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
console.log(window.innerWidth);
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '../css/gis.scss';
|
||||
|
||||
.carousel {
|
||||
width: vw(907);
|
||||
height: vh(167);
|
||||
background-color: rgba(8, 14, 15, 0.3);
|
||||
backdrop-filter: blur(vw(4));
|
||||
position: absolute;
|
||||
bottom: vh(31);
|
||||
left: vw(507);
|
||||
padding-top: vh(10);
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.carousellist {
|
||||
margin-top: vh(16);
|
||||
padding: 0 vw(23);
|
||||
width: 100%;
|
||||
height: vh(84);
|
||||
|
||||
> div > img {
|
||||
width: vw(20);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.carouselcontainer {
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
margin: 0 vw(13);
|
||||
|
||||
.carouselItem {
|
||||
font-size: vw(12);
|
||||
color: #fff;
|
||||
width: vw(133);
|
||||
position: relative;
|
||||
|
||||
.time {
|
||||
text-align: right;
|
||||
margin-right: vw(3);
|
||||
|
||||
font-family: 'D-Din';
|
||||
}
|
||||
|
||||
.remark {
|
||||
text-align: center;
|
||||
height: vh(21);
|
||||
line-height: vh(21);
|
||||
font-family: '思源黑体';
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
margin-right: vw(3);
|
||||
}
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
object-fit: cover;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
> div {
|
||||
> img {
|
||||
width: vw(14);
|
||||
height: vh(35);
|
||||
margin-right: vw(8);
|
||||
}
|
||||
}
|
||||
|
||||
.subTitle {
|
||||
img {
|
||||
width: vw(18.8);
|
||||
margin-right: vw(10);
|
||||
}
|
||||
|
||||
span {
|
||||
text-shadow: 0 0 vw(8) rgba(2, 3, 7, 0.35);
|
||||
font-size: vw(20);
|
||||
font-weight: 500;
|
||||
letter-spacing: 0;
|
||||
color: rgba(255, 255, 255, 1);
|
||||
}
|
||||
}
|
||||
|
||||
.hint {
|
||||
font-size: vw(14);
|
||||
font-weight: 400;
|
||||
color: rgba(204, 204, 204, 0.5);
|
||||
margin-right: vw(20);
|
||||
}
|
||||
|
||||
.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);
|
||||
}
|
||||
}
|
||||
</style>
|
693
src/views/gisHome/component/leftMain.vue
Normal file
693
src/views/gisHome/component/leftMain.vue
Normal file
@ -0,0 +1,693 @@
|
||||
<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', //形状 类型包括 circle,rect,line,roundRect,triangle,diamond,pin,arrow,none
|
||||
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>
|
43
src/views/gisHome/component/map.vue
Normal file
43
src/views/gisHome/component/map.vue
Normal file
@ -0,0 +1,43 @@
|
||||
<template>
|
||||
<div class="ol-map" id="olMap"></div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
// const sdk = ref(null);
|
||||
const createEarth = () => {
|
||||
window.sdk = new YJ.YJEarth('olMap');
|
||||
YJ.Global.setDefaultView(sdk, {
|
||||
destination: { lng: 100, lat: 30, alt: 22099000 },
|
||||
orientation: {
|
||||
heading: 0.0,
|
||||
pitch: -90.0,
|
||||
roll: 0.0
|
||||
}
|
||||
});
|
||||
let layer = new YJ.Obj.ArcgisWXImagery(sdk, {
|
||||
show: true,
|
||||
layer_index: 1
|
||||
});
|
||||
YJ.Global.CesiumContainer(sdk, {
|
||||
compass: false // 罗盘
|
||||
});
|
||||
new YJ.Tools(sdk).flyHome(0);
|
||||
};
|
||||
|
||||
onMounted(async () => {
|
||||
await YJ.on();
|
||||
createEarth();
|
||||
console.log(YJ);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '../css/gis.scss';
|
||||
.ol-map {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: calc(100vh);
|
||||
}
|
||||
</style>
|
789
src/views/gisHome/component/rightMain.vue
Normal file
789
src/views/gisHome/component/rightMain.vue
Normal file
@ -0,0 +1,789 @@
|
||||
<template>
|
||||
<div class="management">
|
||||
<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/News.png" alt="" />
|
||||
<span>项目新闻</span>
|
||||
</div>
|
||||
<div class="hint">PROJECT NEWS</div>
|
||||
</div>
|
||||
<div class="mark"></div>
|
||||
</div>
|
||||
<div class="ligner"></div>
|
||||
</div>
|
||||
<div class="events">
|
||||
<div class="content events-content event_s">
|
||||
<AutoScroller :items="projectNewsData" class="events-list" ref="newsScroller">
|
||||
<template #default="{ item, index }">
|
||||
<li>
|
||||
<span class="text detail" style="display: inline"> {{ item.title }}</span>
|
||||
<span class="more" v-if="!item.show" @click="onMore(index, true, item.id)">查看详情</span>
|
||||
<span class="more" style="color: #ffb100eb" v-else @click="onMore(index, false)">关闭详情</span>
|
||||
</li>
|
||||
</template>
|
||||
</AutoScroller>
|
||||
<div class="detail-content" v-if="newsDetailDialog" v-html="NewsDetailMain"></div>
|
||||
<!-- <span v-else style="font-size: 20px; letter-spacing: 10px">暂无数据</span> -->
|
||||
</div>
|
||||
</div>
|
||||
<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/Safety.png" alt="" />
|
||||
<span>安全管理</span>
|
||||
</div>
|
||||
<div class="hint">SAFETY MANAGEMENT</div>
|
||||
</div>
|
||||
<div class="mark"></div>
|
||||
</div>
|
||||
<div class="ligner"></div>
|
||||
</div>
|
||||
<div class="safetyData">
|
||||
<div class="head flex justify-around">
|
||||
<div class="safetyData-item flex items-center">
|
||||
<div>
|
||||
<img src="@/assets/images/danggerNum.png" alt="" />
|
||||
</div>
|
||||
<div>
|
||||
<span>站班会情况</span>
|
||||
<p>{{ safetyInspectionData.teamMeetingCount }}<span> 件</span></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="safetyData-item flex items-center">
|
||||
<div>
|
||||
<img src="@/assets/images/record.png" alt="" />
|
||||
</div>
|
||||
<div>
|
||||
<span>巡检记录</span>
|
||||
<p>{{ safetyInspectionData.safetyInspectionCount }}<span> 件</span></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="safetyData-item flex items-center">
|
||||
<div>
|
||||
<img src="@/assets/images/situation.png" alt="" />
|
||||
</div>
|
||||
<div>
|
||||
<span>整改情况</span>
|
||||
<p>{{ safetyInspectionData.correctSituationCount }}<span> 件</span></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tables">
|
||||
<div class="thead">
|
||||
<span class="teamWidth">班组</span>
|
||||
<span class="teamWidth">名称</span>
|
||||
<span class="teamWidth">时间</span>
|
||||
<span class="statusWidth">操作</span>
|
||||
</div>
|
||||
<AutoScroller :items="safetyInspectionData.teamMeetingList" class="tbody" safety>
|
||||
<template #default="{ item }">
|
||||
<div class="flex">
|
||||
<span class="teamWidth">{{ item.teamName }}</span>
|
||||
<span class="teamWidth">{{ item.name }}</span>
|
||||
<span class="teamWidth time">{{ item.meetingDate }}</span>
|
||||
<span class="statusWidth">查看</span>
|
||||
</div>
|
||||
</template>
|
||||
</AutoScroller>
|
||||
</div>
|
||||
</div>
|
||||
<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/Quality.png" alt="" />
|
||||
<span>质量管理</span>
|
||||
</div>
|
||||
<div class="hint">SAFETY MANAGEMENT</div>
|
||||
</div>
|
||||
<div class="mark"></div>
|
||||
</div>
|
||||
<div class="ligner"></div>
|
||||
</div>
|
||||
<div class="quality">
|
||||
<div class="qualitydata flex items-center">
|
||||
<div>
|
||||
<img src="@/assets/images/qualityLogo.png" alt="" />
|
||||
</div>
|
||||
<div class="qualityNum">
|
||||
<div>巡检记录 <b></b></div>
|
||||
<p>{{ qualityData?.count }}<span> 件</span></p>
|
||||
</div>
|
||||
<div class="qualityNum ml-15">
|
||||
<div>整改情况 <b></b></div>
|
||||
<p>{{ qualityData?.correctSituation }}<span> %</span></p>
|
||||
</div>
|
||||
</div>
|
||||
<AutoScroller :items="qualityData?.list" class="qualityList">
|
||||
<template #default="{ item }">
|
||||
<div class="qualityItem flex items-center">
|
||||
<div>
|
||||
<img src="@/assets/images/timeIcon.png" alt="" />
|
||||
<span class="text-white qualityTime">{{ item.createTime }}</span>
|
||||
</div>
|
||||
<div class="text-#43E2CB record">{{ item.inspectionTypeLabel }}</div>
|
||||
<div class="text-#E6F7FF text-truncate">
|
||||
<el-tooltip :content="item.inspectionHeadline" placement="top"> {{ item.inspectionHeadline }}</el-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</AutoScroller>
|
||||
</div>
|
||||
<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/usercopy.png" alt="" />
|
||||
<span>形象进度</span>
|
||||
</div>
|
||||
<div class="hint">PROGRESS MANAGEMENT</div>
|
||||
</div>
|
||||
<div class="mark"></div>
|
||||
</div>
|
||||
<div class="ligner"></div>
|
||||
</div>
|
||||
<div class="user" id="userMain"></div>
|
||||
</div>
|
||||
<div class="topleft"></div>
|
||||
<div class="bottomright"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import * as echarts from 'echarts';
|
||||
|
||||
import { projectNewsVO, QualityVO, safetyInspectionVO } from '@/api/gis/type';
|
||||
import { useUserStoreHook } from '@/store/modules/user';
|
||||
import { getQualityList, getprojectNewsDetailList, getprojectNewsList, getsafetyInspectionList } from '@/api/gis';
|
||||
import AutoScroller from './autoScroller.vue';
|
||||
const userStore = useUserStoreHook();
|
||||
type EChartsOption = echarts.EChartsOption;
|
||||
const option = ref<EChartsOption>(null);
|
||||
const myMachineryChart = ref(null);
|
||||
const projectScroll = ref(null);
|
||||
const tableScroll = ref(null);
|
||||
const qualityScroll = ref(null);
|
||||
const scrollList = reactive({
|
||||
projectRef: {
|
||||
dom: projectScroll,
|
||||
intervalId: null
|
||||
},
|
||||
tableRef: {
|
||||
dom: tableScroll,
|
||||
intervalId: null
|
||||
},
|
||||
qualityRef: {
|
||||
dom: qualityScroll,
|
||||
intervalId: null
|
||||
}
|
||||
});
|
||||
const newsScroller = ref();
|
||||
const newsDetailDialog = ref<boolean>(false);
|
||||
// 从 store 中获取项目列表和当前选中的项目
|
||||
const currentProject = computed(() => userStore.selectedProject);
|
||||
const qualityData = ref<QualityVO>({ list: [], correctSituation: null, count: null });
|
||||
const projectNewsData = ref<projectNewsVO[]>([]);
|
||||
const NewsDetailMain = ref<string>(null);
|
||||
const safetyInspectionData = ref<safetyInspectionVO>({
|
||||
teamMeetingCount: null,
|
||||
safetyInspectionCount: null,
|
||||
correctSituationCount: null,
|
||||
teamMeetingList: []
|
||||
});
|
||||
const classOptions = {
|
||||
limitMoveNum: 3,
|
||||
hoverStop: true,
|
||||
step: 1
|
||||
};
|
||||
|
||||
//获取质量信息
|
||||
const getQualityData = async () => {
|
||||
const res = await getQualityList({ projectId: currentProject.value.id });
|
||||
if (res.code !== 200) return;
|
||||
qualityData.value = res.data;
|
||||
};
|
||||
|
||||
//获取大屏项目新闻列表
|
||||
const getProjectNewsData = async () => {
|
||||
const res = await getprojectNewsList({ projectId: currentProject.value.id });
|
||||
if (res.code !== 200) return;
|
||||
projectNewsData.value = res.data;
|
||||
};
|
||||
|
||||
//查询大屏安全信息
|
||||
const getsafetyInspectionData = async () => {
|
||||
const res = await getsafetyInspectionList({ projectId: currentProject.value.id });
|
||||
if (res.code !== 200) return;
|
||||
safetyInspectionData.value = res.data;
|
||||
};
|
||||
|
||||
const safetyData = ref([]);
|
||||
new Array(10).fill(0).forEach((item) => {
|
||||
safetyData.value.push({
|
||||
id: 1,
|
||||
name: '站班会',
|
||||
teamName: '测试组',
|
||||
meetingDate: '2024-03-18'
|
||||
});
|
||||
});
|
||||
|
||||
const onMore = async (index, isShow, id?) => {
|
||||
if (isShow) {
|
||||
newsScroller.value.pause();
|
||||
projectNewsData.value[index].show = true;
|
||||
const res = await getprojectNewsDetailList(id);
|
||||
NewsDetailMain.value = res.data.content;
|
||||
newsDetailDialog.value = true;
|
||||
} else {
|
||||
newsScroller.value.resume();
|
||||
projectNewsData.value[index].show = false;
|
||||
newsDetailDialog.value = false;
|
||||
}
|
||||
};
|
||||
//初始化形象进度图表
|
||||
const initUserChart = () => {
|
||||
let chartDom = document.getElementById('userMain');
|
||||
myMachineryChart.value = echarts.init(chartDom);
|
||||
// prettier-ignore
|
||||
let dataAxis = ['桩点浇筑', '支架安装', '组件安装', '箱变安装'];
|
||||
option.value = {
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
// Use axis to trigger tooltip
|
||||
type: 'shadow' // 'shadow' as default; can also be 'line' or 'shadow'
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
left: '3%',
|
||||
right: '4%',
|
||||
bottom: '0',
|
||||
containLabel: true,
|
||||
width: '90%',
|
||||
height: '100%'
|
||||
},
|
||||
xAxis: {
|
||||
type: 'value',
|
||||
splitLine: {
|
||||
lineStyle: {
|
||||
color: 'rgba(108, 128, 151, 0.3)'
|
||||
}
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
type: 'category',
|
||||
data: dataAxis,
|
||||
offset: 0,
|
||||
axisLine: {
|
||||
show: false
|
||||
},
|
||||
axisTick: {
|
||||
show: false
|
||||
},
|
||||
inverse: true,
|
||||
axisLabel: {
|
||||
color: '#E6F7FF',
|
||||
fontSize: 12
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: 'bar',
|
||||
itemStyle: {
|
||||
color: {
|
||||
type: 'linear',
|
||||
x: 0,
|
||||
y: 0,
|
||||
x2: 1,
|
||||
y2: 0,
|
||||
colorStops: [
|
||||
{
|
||||
offset: 0,
|
||||
color: 'rgba(0, 89, 84, 0)' // 0% 处的颜色
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: 'rgba(102, 225, 223, 1)' // 100% 处的颜色
|
||||
}
|
||||
],
|
||||
global: false // 缺省为 false
|
||||
}
|
||||
},
|
||||
emphasis: {
|
||||
focus: 'series'
|
||||
},
|
||||
data: [120, 132, 101, 134],
|
||||
showBackground: true,
|
||||
barWidth: 8,
|
||||
backgroundStyle: {
|
||||
color: 'rgba(108, 128, 151, .1)'
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
option.value && myMachineryChart.value.setOption(option.value);
|
||||
};
|
||||
|
||||
// 防抖函数
|
||||
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();
|
||||
initUserChart();
|
||||
};
|
||||
|
||||
const debouncedHandleResize = debounce(handleResize, 300);
|
||||
|
||||
onMounted(() => {
|
||||
initUserChart();
|
||||
getQualityData();
|
||||
getProjectNewsData();
|
||||
getsafetyInspectionData();
|
||||
window.addEventListener('resize', debouncedHandleResize);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('resize', debouncedHandleResize);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '../css/gis.scss';
|
||||
|
||||
#userMain {
|
||||
width: 100%;
|
||||
padding-right: vw(14);
|
||||
margin-top: vh(19);
|
||||
height: vh(170);
|
||||
}
|
||||
|
||||
.management {
|
||||
width: vw(449);
|
||||
height: vh(927);
|
||||
background: rgb(1, 26, 33, 0.4);
|
||||
backdrop-filter: blur(vw(8));
|
||||
position: absolute;
|
||||
top: vh(122);
|
||||
right: vw(21);
|
||||
z-index: 2;
|
||||
.main {
|
||||
padding-top: vh(17);
|
||||
}
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
padding: vw(2);
|
||||
background: linear-gradient(to bottom left, #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);
|
||||
right: vw(1);
|
||||
background-image: url('@/assets/images/topleft.png');
|
||||
transform: rotatez(90deg);
|
||||
}
|
||||
|
||||
.bottomright {
|
||||
width: vw(7);
|
||||
height: vw(7);
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
background-image: url('@/assets/images/bottomright.png');
|
||||
transform: rotatez(90deg);
|
||||
}
|
||||
}
|
||||
|
||||
.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);
|
||||
}
|
||||
}
|
||||
|
||||
.quality {
|
||||
margin: vh(13) 0 vh(15);
|
||||
font-family: '思源黑体';
|
||||
.qualitydata {
|
||||
margin: 0 0 vh(12) vw(28);
|
||||
|
||||
img {
|
||||
width: vw(68);
|
||||
margin-right: vw(48);
|
||||
}
|
||||
|
||||
.qualityNum {
|
||||
width: vw(99);
|
||||
height: vh(53);
|
||||
text-align: center;
|
||||
div {
|
||||
/** 文本1 */
|
||||
font-size: vw(14);
|
||||
font-weight: 400;
|
||||
color: rgba(255, 255, 255, 1);
|
||||
margin-bottom: vh(5);
|
||||
position: relative;
|
||||
font-family: '思源黑体';
|
||||
b {
|
||||
position: absolute;
|
||||
top: vh(5);
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: vh(6);
|
||||
background-color: rgba(67, 226, 203, 0.1);
|
||||
z-index: -1;
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: vw(3);
|
||||
height: vh(3);
|
||||
background-color: rgba(67, 226, 203, 1);
|
||||
}
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: vw(3);
|
||||
height: vh(3);
|
||||
background-color: rgba(67, 226, 203, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
p {
|
||||
width: 100%;
|
||||
height: vh(27);
|
||||
background-color: rgba(67, 226, 203, 0.1);
|
||||
font-size: vw(24);
|
||||
color: #fff;
|
||||
text-shadow: 0 vw(1.24) vw(6.21) rgba(0, 190, 247, 1);
|
||||
line-height: vh(27);
|
||||
font-family: 'Roboto';
|
||||
span {
|
||||
font-size: vw(12);
|
||||
font-family: '思源黑体';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.qualityList {
|
||||
margin-left: vw(21);
|
||||
display: block;
|
||||
height: vh(90);
|
||||
overflow: hidden;
|
||||
margin-right: vw(10);
|
||||
font-size: vw(14);
|
||||
.qualityItem {
|
||||
margin-bottom: vh(13);
|
||||
font-family: '思源黑体';
|
||||
.qualityTime {
|
||||
font-family: 'Roboto';
|
||||
}
|
||||
img {
|
||||
width: vw(12);
|
||||
margin-right: vw(8);
|
||||
}
|
||||
.record {
|
||||
margin: 0 vw(54) 0 vw(40);
|
||||
}
|
||||
.text-truncate {
|
||||
width: vw(154);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.safetyData {
|
||||
margin: vh(20) 0 vh(15);
|
||||
|
||||
.head {
|
||||
img {
|
||||
width: vw(36);
|
||||
}
|
||||
|
||||
.safetyData-item {
|
||||
height: vh(53);
|
||||
|
||||
> div {
|
||||
> span {
|
||||
font-size: vw(14);
|
||||
font-weight: 400;
|
||||
color: rgba(204, 204, 204, 1);
|
||||
font-family: '思源黑体';
|
||||
}
|
||||
|
||||
> p {
|
||||
text-shadow: 0 vw(1.24) vw(6.21) rgba(0, 190, 247, 1);
|
||||
font-size: vw(24);
|
||||
font-weight: 700;
|
||||
color: rgba(255, 255, 255, 1);
|
||||
font-family: 'Roboto';
|
||||
> span {
|
||||
font-size: vw(12);
|
||||
font-weight: 400;
|
||||
color: rgba(255, 255, 255, 1);
|
||||
font-family: '思源黑体';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.events {
|
||||
margin: vh(20) 0 vh(15);
|
||||
width: 100%;
|
||||
font-family: '思源黑体';
|
||||
position: relative;
|
||||
height: vh(82);
|
||||
.events-list {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
padding-right: vw(10);
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
li {
|
||||
width: 100%;
|
||||
padding-left: vw(20);
|
||||
background: url('@/assets/images/li.png') no-repeat 0 20%;
|
||||
list-style-type: none;
|
||||
list-style-position: inside;
|
||||
margin-bottom: vh(8);
|
||||
position: relative;
|
||||
|
||||
.detail {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.more {
|
||||
color: rgba(67, 226, 203, 1);
|
||||
font-size: vw(14);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.text {
|
||||
font-size: vw(12);
|
||||
font-weight: 400;
|
||||
letter-spacing: 0;
|
||||
color: rgba(230, 247, 255, 1);
|
||||
text-align: justify;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 2;
|
||||
max-height: 4em;
|
||||
height: 4em;
|
||||
margin-right: vw(5);
|
||||
}
|
||||
}
|
||||
|
||||
// li:last-child {
|
||||
// margin-bottom: 0;
|
||||
// }
|
||||
|
||||
li::after {
|
||||
content: '';
|
||||
border-left: vw(1) dashed rgba(0, 190, 247, 0.3);
|
||||
position: absolute;
|
||||
top: vh(22);
|
||||
left: vw(5);
|
||||
width: vw(10);
|
||||
height: 85%;
|
||||
display: block;
|
||||
}
|
||||
|
||||
// li:last-child::after {
|
||||
// content: '';
|
||||
// border-left: none;
|
||||
// }
|
||||
}
|
||||
.detail-content {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: vw(-275);
|
||||
width: vw(250);
|
||||
height: vh(328);
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
overflow: auto;
|
||||
padding: vh(15) vw(15);
|
||||
|
||||
color: rgba(255, 255, 255, 1);
|
||||
backdrop-filter: blur(vw(8));
|
||||
border: 1px solid;
|
||||
border-image: linear-gradient(to bottom, rgba(67, 226, 203, 1), rgba(67, 226, 203, 0.5)) 1;
|
||||
::v-deep(p) {
|
||||
margin: vh(10) 0;
|
||||
img {
|
||||
width: 100%;
|
||||
border-radius: vw(8);
|
||||
}
|
||||
span {
|
||||
font-size: vw(12) !important;
|
||||
line-height: 18px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
width: 100%;
|
||||
height: vh(82);
|
||||
padding: 0 vw(10) 0 vw(20);
|
||||
}
|
||||
|
||||
.event_s {
|
||||
display: grid;
|
||||
place-items: center;
|
||||
color: #f0f8ff;
|
||||
}
|
||||
|
||||
.tables {
|
||||
padding: 0 vw(13) 0 vw(14);
|
||||
font-weight: 400;
|
||||
font-size: vw(14);
|
||||
font-family: '思源黑体';
|
||||
.tbody {
|
||||
height: vh(94);
|
||||
overflow: hidden;
|
||||
padding-right: vw(14);
|
||||
color: rgba(255, 255, 255, 1);
|
||||
font-size: 13px;
|
||||
font-family: '思源黑体';
|
||||
.flex {
|
||||
padding-left: vw(16);
|
||||
height: vh(26);
|
||||
line-height: vh(26);
|
||||
font-size: vw(13);
|
||||
margin: vh(8) 0;
|
||||
}
|
||||
.time {
|
||||
font-family: 'Roboto';
|
||||
}
|
||||
}
|
||||
|
||||
.thead {
|
||||
padding-right: vw(20);
|
||||
margin-top: vh(12);
|
||||
display: flex;
|
||||
color: #43e2cb;
|
||||
height: vh(40);
|
||||
padding-left: vw(16);
|
||||
line-height: vh(40);
|
||||
}
|
||||
.statusWidth {
|
||||
width: vw(44);
|
||||
color: rgba(67, 226, 203, 1);
|
||||
cursor: pointer;
|
||||
}
|
||||
.teamWidth {
|
||||
width: vw(114);
|
||||
}
|
||||
}
|
||||
|
||||
/* 滚动条整体样式 */
|
||||
::-webkit-scrollbar {
|
||||
width: vw(6);
|
||||
display: none;
|
||||
|
||||
/* 纵向滚动条宽度 */
|
||||
}
|
||||
|
||||
/* 滚动条轨道 */
|
||||
::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
/* 轨道背景颜色 */
|
||||
border-radius: vw(5);
|
||||
/* 轨道圆角 */
|
||||
}
|
||||
|
||||
/* 滚动条滑块 */
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: linear-gradient(180deg, rgba(67, 226, 203, 0.5) 0%, rgba(21, 181, 230, 0.5) 100%);
|
||||
border-radius: vw(5);
|
||||
/* 滑块圆角 */
|
||||
}
|
||||
|
||||
/* 隐藏滚动条的上下手柄 */
|
||||
::-webkit-scrollbar-button {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
110
src/views/gisHome/component/weatherListScroll.vue
Normal file
110
src/views/gisHome/component/weatherListScroll.vue
Normal file
@ -0,0 +1,110 @@
|
||||
<template>
|
||||
<div class="marquee-container" @mouseenter="requestPause" @mouseleave="resumeScroll">
|
||||
<ul class="marquee-list" :style="{ transform: `translateY(-${offset}px)`, transition: transition }">
|
||||
<li v-for="(item, i) in fullList" :key="i" class="marquee-item" :ref="i === 0 ? 'firstItem' : null">
|
||||
<slot :item="item" :index="i % items.length">{{ item }}</slot>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, onUnmounted, computed } from 'vue';
|
||||
|
||||
const props = defineProps<{
|
||||
items: any[];
|
||||
interval?: number;
|
||||
height?: number;
|
||||
}>();
|
||||
|
||||
const currentIndex = ref(0);
|
||||
const offset = ref(0);
|
||||
const transition = ref('transform 0.5s ease');
|
||||
const pendingPause = ref(false);
|
||||
const firstItem = ref<HTMLElement | null>(null);
|
||||
|
||||
let timer: number | null = null;
|
||||
|
||||
// 使用 vh 单位计算的高度
|
||||
const itemHeight = ref(0);
|
||||
|
||||
// 拼接列表:结尾添加一项首项实现无缝
|
||||
const fullList = computed(() => [...props.items, props.items[0]]);
|
||||
|
||||
function scrollStep() {
|
||||
currentIndex.value++;
|
||||
transition.value = 'transform 0.3s ease';
|
||||
offset.value = currentIndex.value * itemHeight.value;
|
||||
|
||||
if (currentIndex.value === props.items.length) {
|
||||
setTimeout(() => {
|
||||
transition.value = 'none';
|
||||
currentIndex.value = 0;
|
||||
offset.value = 0;
|
||||
}, 350);
|
||||
}
|
||||
|
||||
// 如果鼠标悬停请求正在等待,滚动结束后执行暂停
|
||||
if (pendingPause.value) {
|
||||
clearInterval(timer!);
|
||||
timer = null;
|
||||
pendingPause.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function startScroll() {
|
||||
if (timer) clearInterval(timer);
|
||||
timer = window.setInterval(scrollStep, props.interval ?? 2000);
|
||||
}
|
||||
|
||||
function requestPause() {
|
||||
pendingPause.value = true;
|
||||
}
|
||||
|
||||
function resumeScroll() {
|
||||
pendingPause.value = false;
|
||||
startScroll();
|
||||
}
|
||||
|
||||
function updateItemHeight() {
|
||||
if (firstItem.value) {
|
||||
itemHeight.value = firstItem.value[0].getBoundingClientRect().height;
|
||||
offset.value = currentIndex.value * itemHeight.value;
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
// 等待 DOM 加载后获取第一个项目的实际高度
|
||||
if (firstItem.value) {
|
||||
updateItemHeight();
|
||||
startScroll();
|
||||
}
|
||||
window.addEventListener('resize', updateItemHeight);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
if (timer) clearInterval(timer);
|
||||
window.removeEventListener('resize', updateItemHeight);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import '../css/gis.scss'; // 引入 vh 单位
|
||||
.marquee-container {
|
||||
height: vh(50); /* 控制容器高度为视口高度的 10% */
|
||||
min-width: vw(287);
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.marquee-list {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.marquee-item {
|
||||
height: vh(50); /* 控制每项高度为视口高度的 10% */
|
||||
line-height: vh(50);
|
||||
}
|
||||
</style>
|
Reference in New Issue
Block a user