数字化大屏
This commit is contained in:
@ -5,8 +5,8 @@ VITE_APP_TITLE = 煤科建管平台
|
|||||||
VITE_APP_ENV = 'development'
|
VITE_APP_ENV = 'development'
|
||||||
|
|
||||||
# 开发环境
|
# 开发环境
|
||||||
VITE_APP_BASE_API = 'http://192.168.110.149:8899'
|
|
||||||
# VITE_APP_BASE_API = 'http://192.168.110.209:8899'
|
VITE_APP_BASE_API = 'http://192.168.110.209:8899'
|
||||||
# 李陈杰 209
|
# 李陈杰 209
|
||||||
# VITE_APP_BASE_API = 'http://192.168.110.209:8899'
|
# VITE_APP_BASE_API = 'http://192.168.110.209:8899'
|
||||||
# 曾涛
|
# 曾涛
|
||||||
@ -14,7 +14,7 @@ VITE_APP_BASE_API = 'http://192.168.110.149:8899'
|
|||||||
# 罗成
|
# 罗成
|
||||||
# VITE_APP_BASE_API = 'http://192.168.110.188:8899'
|
# VITE_APP_BASE_API = 'http://192.168.110.188:8899'
|
||||||
# 朱银
|
# 朱银
|
||||||
VITE_APP_BASE_API = 'http://192.168.110.149:8899'
|
# VITE_APP_BASE_API = 'http://192.168.110.149:8899'
|
||||||
#曾涛
|
#曾涛
|
||||||
# VITE_APP_BASE_API = 'http://192.168.110.171:8899'
|
# VITE_APP_BASE_API = 'http://192.168.110.171:8899'
|
||||||
|
|
||||||
|
@ -72,9 +72,15 @@ export const constantRoutes: RouteRecordRaw[] = [
|
|||||||
component: () => import('@/views/error/401.vue'),
|
component: () => import('@/views/error/401.vue'),
|
||||||
hidden: true
|
hidden: true
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
path: '/ProjectScreen',
|
path: '/ProjectScreen',
|
||||||
component: () => import('@/views/ProjectScreen/index.vue'),
|
component: () => import('@/views/projectLarge/ProjectScreen/index.vue'),
|
||||||
|
hidden: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/digitalizationScreen',
|
||||||
|
component: () => import('@/views/projectLarge/digitalizationScreen/index.vue'),
|
||||||
hidden: true
|
hidden: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -22,11 +22,10 @@ import leftPage from './components/leftPage.vue';
|
|||||||
import centerPage from './components/centerPage.vue';
|
import centerPage from './components/centerPage.vue';
|
||||||
import rightPage from './components/rightPage.vue';
|
import rightPage from './components/rightPage.vue';
|
||||||
import { useUserStoreHook } from '@/store/modules/user';
|
import { useUserStoreHook } from '@/store/modules/user';
|
||||||
|
|
||||||
const userStore = useUserStoreHook();
|
const userStore = useUserStoreHook();
|
||||||
const projectId = computed(() => userStore.selectedProject.id);
|
const projectId = computed(() => userStore.selectedProject.id);
|
||||||
const isFull = ref(false)
|
const isFull = ref(false);
|
||||||
const isHideOther = ref(false)
|
const isHideOther = ref(false);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 切换中心页面全屏
|
* 切换中心页面全屏
|
||||||
@ -41,7 +40,7 @@ const handleChangePage = () => {
|
|||||||
isFull.value = true;
|
isFull.value = true;
|
||||||
isHideOther.value = true;
|
isHideOther.value = true;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
@ -0,0 +1,275 @@
|
|||||||
|
<template>
|
||||||
|
<div class="centerPage">
|
||||||
|
<div class="topPage">
|
||||||
|
<div id="earth" style="width: 100%;height: 100%;"></div>
|
||||||
|
</div>
|
||||||
|
<div class="endPage" :class="{ 'slide-out-down': isHide }">
|
||||||
|
<Title title="AI安全巡检">
|
||||||
|
<img src="@/assets/projectLarge/robot.svg" alt="" height="20px" width="20px">
|
||||||
|
</Title>
|
||||||
|
<div class="swiper" v-if="inspectionList.length">
|
||||||
|
<div class="arrow" :class="{ 'canUse': canLeft }" @click="swiperClick('left')">
|
||||||
|
<el-icon size="16" :color="canLeft ? 'rgba(29, 214, 255, 1)' : 'rgba(29, 214, 255, 0.3)'">
|
||||||
|
<ArrowLeft />
|
||||||
|
</el-icon>
|
||||||
|
</div>
|
||||||
|
<div class="swiper_content" ref="swiperContent">
|
||||||
|
<div class="swiper_item" v-for="(item, i) in inspectionList" :key="i">
|
||||||
|
<img :src="item.picture" alt="安全巡检" class="swiper_img">
|
||||||
|
<div class="swiper_date">{{ item.createTime.slice(5) }}</div>
|
||||||
|
<div class="swiper_tip">{{ item.label }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="arrow" :class="{ 'canUse': canRight }" @click="swiperClick('right')">
|
||||||
|
<el-icon size="16" :color="canRight ? 'rgba(29, 214, 255, 1)' : 'rgba(29, 214, 255, 0.3)'">
|
||||||
|
<ArrowRight />
|
||||||
|
</el-icon>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, onMounted, toRefs, getCurrentInstance } from "vue"
|
||||||
|
import Title from './title.vue'
|
||||||
|
import { ArrowLeft, ArrowRight } from '@element-plus/icons-vue'
|
||||||
|
import { getScreenSafetyInspection } from '@/api/projectScreen'
|
||||||
|
|
||||||
|
const { proxy } = getCurrentInstance();
|
||||||
|
const { violation_level_type } = toRefs(proxy?.useDict('violation_level_type'));
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
isHide: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
projectId: {
|
||||||
|
type: String,
|
||||||
|
default: ""
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const inspectionList = ref([{
|
||||||
|
id: "",
|
||||||
|
label: "",
|
||||||
|
picture: "",
|
||||||
|
createTime: ""
|
||||||
|
}])
|
||||||
|
const swiperContent = ref()
|
||||||
|
const swiperItemWidth = ref(100)
|
||||||
|
const canLeft = ref(false)
|
||||||
|
const canRight = ref(true)
|
||||||
|
|
||||||
|
const swiperClick = (direction) => {
|
||||||
|
if (!swiperContent.value) return
|
||||||
|
|
||||||
|
if (direction === 'right') {
|
||||||
|
if (swiperContent.value.scrollLeft >= swiperContent.value.scrollWidth - swiperContent.value.clientWidth) {
|
||||||
|
canRight.value = false
|
||||||
|
canLeft.value = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
swiperContent.value.scrollLeft += swiperItemWidth.value
|
||||||
|
} else {
|
||||||
|
if (swiperContent.value.scrollLeft <= 0) {
|
||||||
|
canLeft.value = false
|
||||||
|
canRight.value = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
swiperContent.value.scrollLeft -= swiperItemWidth.value
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新箭头状态
|
||||||
|
canLeft.value = swiperContent.value.scrollLeft > 0
|
||||||
|
canRight.value = swiperContent.value.scrollLeft < swiperContent.value.scrollWidth - swiperContent.value.clientWidth
|
||||||
|
}
|
||||||
|
|
||||||
|
const getInspectionList = async () => {
|
||||||
|
const res = await getScreenSafetyInspection(props.projectId)
|
||||||
|
const { code, data } = res
|
||||||
|
if (code === 200) {
|
||||||
|
data.map(item => {
|
||||||
|
item.label = violation_level_type.value.find((i) => i.value === item.violationType)?.label
|
||||||
|
})
|
||||||
|
inspectionList.value = data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 创建地球
|
||||||
|
const createEarth = () => {
|
||||||
|
window.YJ.on({
|
||||||
|
ws: true,
|
||||||
|
// host: getIP(), //资源所在服务器地址
|
||||||
|
// username: this.loginForm.username, //用户名 可以不登录(不填写用户名),不登录时无法加载服务端的数据
|
||||||
|
// password: md5pass, //密码 生成方式:md5(用户名_密码)
|
||||||
|
}).then((res) => {
|
||||||
|
let earth = new YJ.YJEarth("earth");
|
||||||
|
window.Earth1 = earth;
|
||||||
|
YJ.Global.openRightClick(window.Earth1);
|
||||||
|
YJ.Global.openLeftClick(window.Earth1);
|
||||||
|
let view = {
|
||||||
|
"position": {
|
||||||
|
"lng": 102.03643298211526,
|
||||||
|
"lat": 34.393586474501,
|
||||||
|
"alt": 11298179.51993155
|
||||||
|
},
|
||||||
|
"orientation": {
|
||||||
|
"heading": 360,
|
||||||
|
"pitch": -89.94481747201486,
|
||||||
|
"roll": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
loadBaseMap(earth.viewer)
|
||||||
|
YJ.Global.CesiumContainer(window.Earth1, {
|
||||||
|
compass: false, //罗盘
|
||||||
|
});
|
||||||
|
// YJ.Global.flyTo(earth, view);
|
||||||
|
// YJ.Global.setDefaultView(earth.viewer, view)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 加载底图
|
||||||
|
const loadBaseMap = (viewer) => {
|
||||||
|
// 创建瓦片提供器
|
||||||
|
const imageryProvider = new Cesium.UrlTemplateImageryProvider({
|
||||||
|
url: 'https://webst01.is.autonavi.com/appmaptile?style=6&x={x}&y={y}&z={z}',
|
||||||
|
// 可选:设置瓦片的格式
|
||||||
|
fileExtension: 'png',
|
||||||
|
// 可选:设置瓦片的范围和级别
|
||||||
|
minimumLevel: 0,
|
||||||
|
maximumLevel: 18,
|
||||||
|
// 可选:设置瓦片的投影(默认为Web Mercator)
|
||||||
|
projection: Cesium.WebMercatorProjection,
|
||||||
|
// 可选:如果瓦片服务需要跨域请求,设置请求头部
|
||||||
|
credit: new Cesium.Credit('卫星图数据来源')
|
||||||
|
});
|
||||||
|
|
||||||
|
// 添加图层到视图
|
||||||
|
const layer = viewer.imageryLayers.addImageryProvider(imageryProvider);
|
||||||
|
}
|
||||||
|
onMounted(() => {
|
||||||
|
getInspectionList()
|
||||||
|
createEarth()
|
||||||
|
if (swiperContent.value && swiperContent.value.children.length > 0) {
|
||||||
|
swiperItemWidth.value = swiperContent.value.children[0].clientWidth + 20
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.centerPage {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.topPage,
|
||||||
|
.endPage {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 100%;
|
||||||
|
padding: 15px 0;
|
||||||
|
border: 1px solid rgba(230, 247, 255, 0.1);
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.topPage {
|
||||||
|
flex: 1;
|
||||||
|
margin-bottom: 23px;
|
||||||
|
transition: flex 0.5s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.endPage {
|
||||||
|
max-height: 300px;
|
||||||
|
opacity: 1;
|
||||||
|
transition: all 0.5s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 向下滑出动画 */
|
||||||
|
.slide-out-down {
|
||||||
|
transform: translateY(100%);
|
||||||
|
opacity: 0;
|
||||||
|
max-height: 0;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.swiper {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 20px;
|
||||||
|
padding: 20px 20px 10px 20px;
|
||||||
|
|
||||||
|
.swiper_content {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
gap: 20px;
|
||||||
|
transition: all 0.3s ease-in-out;
|
||||||
|
overflow-x: auto;
|
||||||
|
|
||||||
|
&::-webkit-scrollbar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.swiper_item {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
width: 133px;
|
||||||
|
height: 84px;
|
||||||
|
|
||||||
|
.swiper_img {
|
||||||
|
width: 133px;
|
||||||
|
height: 84px;
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
.swiper_date {
|
||||||
|
position: absolute;
|
||||||
|
top: 4px;
|
||||||
|
right: 4px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 400;
|
||||||
|
color: rgba(230, 247, 255, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.swiper_tip {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
width: 100%;
|
||||||
|
padding: 5px 0;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 400;
|
||||||
|
color: rgba(230, 247, 255, 1);
|
||||||
|
background-color: rgba(0, 0, 0, 0.5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.arrow {
|
||||||
|
display: grid;
|
||||||
|
place-items: center;
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
border-radius: 50%;
|
||||||
|
border: 1px solid rgba(29, 214, 255, 0.3);
|
||||||
|
color: skyblue;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
|
||||||
|
&.canUse {
|
||||||
|
border: 1px solid rgba(29, 214, 255, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover:not(.canUse) {
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@ -0,0 +1,327 @@
|
|||||||
|
<template>
|
||||||
|
<div class="header">
|
||||||
|
<div class="header_left">
|
||||||
|
<div class="header_left_img">
|
||||||
|
<img src="@/assets/large/secure.png" style="width: 100%; height: 100%" />
|
||||||
|
</div>
|
||||||
|
<div style="font-size: 12px; padding-left: 10px">安全生产天数:</div>
|
||||||
|
<div class="header_left_text">
|
||||||
|
{{ safetyDay }}
|
||||||
|
<span style="font-size: 12px">天</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="title">
|
||||||
|
<div>煤科建管-新能源项目数智化管理平台</div>
|
||||||
|
<div>Coal Science Construction Management - New Energy Project Digital Intelligent Management Platform</div>
|
||||||
|
</div>
|
||||||
|
<div class="header_right">
|
||||||
|
<div class="top-bar">
|
||||||
|
<!-- 左侧:天气图标 + 日期文字 -->
|
||||||
|
<div class="left-section">
|
||||||
|
<div class="weather-list" @mouseenter="requestPause" @mouseleave="resumeScroll">
|
||||||
|
<div
|
||||||
|
v-for="(item, i) in weatherList"
|
||||||
|
:key="i"
|
||||||
|
class="weather-item"
|
||||||
|
:style="{ transform: `translateY(-${offsetY}px)`, transition: transition }"
|
||||||
|
>
|
||||||
|
<img :src="`../../../src/assets/images/${item.icon}.png`" alt="" />
|
||||||
|
<div>{{ item.weather }}{{ item.tempMin }}°/{{ item.tempMax }}°</div>
|
||||||
|
<div>{{ item.week }}({{ item.date }})</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- 分割线 -->
|
||||||
|
<div class="divider">
|
||||||
|
<div class="top-block"></div>
|
||||||
|
<div class="bottom-block"></div>
|
||||||
|
</div>
|
||||||
|
<!-- 右侧:管理系统图标 + 文字 -->
|
||||||
|
<div class="right-section">
|
||||||
|
<img src="@/assets/large/setting.png" alt="设置图标" />
|
||||||
|
<span>管理系统</span>
|
||||||
|
</div>
|
||||||
|
<!-- 分割线 -->
|
||||||
|
<div class="divider">
|
||||||
|
<div class="top-block"></div>
|
||||||
|
<div class="bottom-block"></div>
|
||||||
|
</div>
|
||||||
|
<!-- -->
|
||||||
|
<div class="change" @click="emit('changePage')">
|
||||||
|
<el-icon size="20" v-if="!isFull">
|
||||||
|
<Expand />
|
||||||
|
</el-icon>
|
||||||
|
<el-icon size="20" v-else>
|
||||||
|
<Fold />
|
||||||
|
</el-icon>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, onMounted, onUnmounted } from 'vue';
|
||||||
|
import { getScreenSafetyDay, getScreenWeather } from '@/api/projectScreen';
|
||||||
|
|
||||||
|
interface Weather {
|
||||||
|
week: string;
|
||||||
|
date: string;
|
||||||
|
icon: string;
|
||||||
|
weather: string;
|
||||||
|
tempMax: string;
|
||||||
|
tempMin: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
projectId: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
isFull: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const emit = defineEmits(['changePage']);
|
||||||
|
|
||||||
|
const safetyDay = ref<number>(0);
|
||||||
|
const weatherList = ref<Weather[]>([]);
|
||||||
|
const timer = ref<number | null>(0);
|
||||||
|
const offsetY = ref<number>(0);
|
||||||
|
const curIndex = ref(0);
|
||||||
|
const transition = ref('transform 0.5s ease');
|
||||||
|
const pendingPause = ref(false);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断当前时间是白天/夜晚
|
||||||
|
*/
|
||||||
|
function judgeDayOrNight(sunRise: string, sunSet: string) {
|
||||||
|
// 将 "HH:MM" 格式转为分钟数(便于计算)
|
||||||
|
const timeToMinutes = (timeStr: any) => {
|
||||||
|
const [hours, minutes] = timeStr.split(':').map(Number);
|
||||||
|
return isNaN(hours) || isNaN(minutes) ? 0 : hours * 60 + minutes;
|
||||||
|
};
|
||||||
|
// 转换日出、日落时间为分钟数
|
||||||
|
const sunRiseMinutes = timeToMinutes(sunRise);
|
||||||
|
const sunSetMinutes = timeToMinutes(sunSet);
|
||||||
|
// 获取当前时间并转为分钟数
|
||||||
|
const now = new Date();
|
||||||
|
const currentMinutes = now.getHours() * 60 + now.getMinutes();
|
||||||
|
// true 白天 false 夜晚
|
||||||
|
return currentMinutes >= sunRiseMinutes && currentMinutes <= sunSetMinutes ? true : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置天气周期滑动
|
||||||
|
*/
|
||||||
|
const setWeatherScroll = () => {
|
||||||
|
curIndex.value += 1;
|
||||||
|
transition.value = 'transform 0.3s ease';
|
||||||
|
offsetY.value = curIndex.value * 60;
|
||||||
|
|
||||||
|
if (curIndex.value === weatherList.value.length - 1) {
|
||||||
|
setTimeout(() => {
|
||||||
|
transition.value = 'none';
|
||||||
|
curIndex.value = 0;
|
||||||
|
offsetY.value = 0;
|
||||||
|
}, 350);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function startScroll() {
|
||||||
|
if (timer.value) clearInterval(timer.value);
|
||||||
|
timer.value = window.setInterval(setWeatherScroll, 5000);
|
||||||
|
}
|
||||||
|
|
||||||
|
function requestPause() {
|
||||||
|
if (timer.value) {
|
||||||
|
clearInterval(timer.value);
|
||||||
|
timer.value = null;
|
||||||
|
}
|
||||||
|
pendingPause.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function resumeScroll() {
|
||||||
|
console.log('resumeScroll');
|
||||||
|
pendingPause.value = false;
|
||||||
|
startScroll();
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
/**
|
||||||
|
* 获取安全生产天数
|
||||||
|
*/
|
||||||
|
getScreenSafetyDay(props.projectId).then((res) => {
|
||||||
|
const { data, code } = res;
|
||||||
|
if (code === 200) {
|
||||||
|
safetyDay.value = data.safetyDay;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
/**
|
||||||
|
* 获取近三天天气
|
||||||
|
*/
|
||||||
|
getScreenWeather(props.projectId).then((res) => {
|
||||||
|
const { data, code } = res;
|
||||||
|
if (code === 200) {
|
||||||
|
data.forEach((item) => {
|
||||||
|
if (judgeDayOrNight(item.sunRise, item.sunSet)) {
|
||||||
|
item.weather = item.dayStatus;
|
||||||
|
item.icon = item.dayIcon;
|
||||||
|
} else {
|
||||||
|
item.weather = item.nightStatus;
|
||||||
|
item.icon = item.nightIcon;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
weatherList.value = data;
|
||||||
|
// 多添加第一项 实现无缝衔接
|
||||||
|
weatherList.value = [...weatherList.value, weatherList.value[0]];
|
||||||
|
startScroll();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
if (timer.value) {
|
||||||
|
clearInterval(timer.value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.header {
|
||||||
|
width: 100%;
|
||||||
|
height: 80px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 10px;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 2fr 1fr;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header_left {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.header_left_img {
|
||||||
|
width: 48px;
|
||||||
|
height: 48px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
// padding-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header_left_text {
|
||||||
|
font-weight: 500;
|
||||||
|
text-shadow: 0px 1.24px 6.21px rgba(25, 179, 250, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.header_right {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
color: #fff;
|
||||||
|
font-family: 'AlimamaShuHeiTi', sans-serif;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title > div:first-child {
|
||||||
|
/* 第一个子元素的样式 */
|
||||||
|
font-size: 38px;
|
||||||
|
letter-spacing: 0.1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title > div:last-child {
|
||||||
|
/* 最后一个子元素的样式 */
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 顶部栏容器:Flex 水平布局 + 垂直居中 */
|
||||||
|
.top-bar {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-end;
|
||||||
|
color: #fff;
|
||||||
|
padding: 8px 16px;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 左侧区域(天气 + 日期):自身也用 Flex 水平排列,确保元素在一行 */
|
||||||
|
.left-section {
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.weather-list {
|
||||||
|
height: 60px;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
.weather-item {
|
||||||
|
height: 60px;
|
||||||
|
line-height: 60px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
& > div:last-child {
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
width: 50px;
|
||||||
|
height: 50px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 分割线(视觉分隔,可根据需求调整样式) */
|
||||||
|
.divider {
|
||||||
|
display: grid;
|
||||||
|
grid-template-rows: 1fr 1fr;
|
||||||
|
gap: 2px;
|
||||||
|
padding: 14px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.divider .top-block {
|
||||||
|
width: 2px;
|
||||||
|
height: 7px;
|
||||||
|
background: #19b5fb;
|
||||||
|
align-self: start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.divider .bottom-block {
|
||||||
|
width: 2px;
|
||||||
|
height: 7px;
|
||||||
|
background: #19b5fb;
|
||||||
|
align-self: end;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 右侧区域(管理系统):图标 + 文字水平排列 */
|
||||||
|
.right-section {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
font-family: 'AlimamaShuHeiTi', sans-serif;
|
||||||
|
font-size: 20px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.right-section img {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
margin-right: 6px;
|
||||||
|
/* 图标与文字间距 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.change {
|
||||||
|
display: grid;
|
||||||
|
place-items: center;
|
||||||
|
margin-right: 10px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
</style>
|
@ -0,0 +1,326 @@
|
|||||||
|
<template>
|
||||||
|
<div class="leftPage">
|
||||||
|
<div class="topPage">
|
||||||
|
<Title title="项目公告" />
|
||||||
|
<div class="content">
|
||||||
|
<div class="content_item" v-for="item in news" :key="item.id">
|
||||||
|
<img src="@/assets/projectLarge/round.svg" alt="" />
|
||||||
|
<div class="ellipsis">
|
||||||
|
{{ item.title }}
|
||||||
|
<span @click="showNewsDetail(item)" style="color: rgba(138, 149, 165, 1)">{{ item.id === newId ? '关闭' : '查看' }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="detailBox" :class="{ 'show': newId }">
|
||||||
|
<!-- <div class="detail_title">{{ newDetail.title }}</div> -->
|
||||||
|
<div class="detail_content" v-html="newDetail.content"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="endPage">
|
||||||
|
<Title title="人员情况" />
|
||||||
|
<div class="map">
|
||||||
|
<img src="@/assets/projectLarge/map.svg" alt="" />
|
||||||
|
<!-- <div ref="mapChartRef"></div> -->
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="attendance_tag">
|
||||||
|
<div class="tag_item">
|
||||||
|
<img src="@/assets/projectLarge/people.svg" alt="" />
|
||||||
|
<div class="tag_title">出勤人</div>
|
||||||
|
<div class="tag_info">
|
||||||
|
{{ attendanceCount }}
|
||||||
|
<span style="font-size: 14px">人</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="tag_item">
|
||||||
|
<img src="@/assets/projectLarge/people.svg" alt="" />
|
||||||
|
<div class="tag_title">在岗人</div>
|
||||||
|
<div class="tag_info">
|
||||||
|
{{ peopleCount }}
|
||||||
|
<span style="font-size: 14px">人</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="tag_item">
|
||||||
|
<img src="@/assets/projectLarge/people.svg" alt="" />
|
||||||
|
<div class="tag_title">出勤率</div>
|
||||||
|
<div class="tag_info">
|
||||||
|
{{ attendanceRate }}
|
||||||
|
<span style="font-size: 14px">%</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="attendance_list">
|
||||||
|
<div class="attendance_item subfont">
|
||||||
|
<div class="attendance_item_title"></div>
|
||||||
|
<div class="attendance_item_title">在岗人数</div>
|
||||||
|
<div class="attendance_item_title">出勤率</div>
|
||||||
|
<div class="attendance_item_title">出勤时间</div>
|
||||||
|
</div>
|
||||||
|
<div v-for="item in teamAttendanceList" :key="item.id" class="attendance_item">
|
||||||
|
<div class="attendance_item_title">{{ item.teamName }}</div>
|
||||||
|
<div class="attendance_item_number">
|
||||||
|
{{ item.attendanceNumber }} <span class="subfont">人/{{ item.allNumber }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="attendance_item_rate">{{ item.attendanceRate }} %</div>
|
||||||
|
<div class="attendance_item_date subfont">{{ item.attendanceTime }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import Title from './title.vue';
|
||||||
|
import { getScreenNews, getScreenPeople } from '@/api/projectScreen';
|
||||||
|
import { mapOption } from './optionList';
|
||||||
|
import * as echarts from 'echarts';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
projectId: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let mapChart = null;
|
||||||
|
const mapChartRef = ref<HTMLDivElement | null>(null);
|
||||||
|
const news = ref([]);
|
||||||
|
const newDetail = ref({
|
||||||
|
title: '',
|
||||||
|
content: ''
|
||||||
|
});
|
||||||
|
const newId = ref('');
|
||||||
|
const attendanceCount = ref(0);
|
||||||
|
const attendanceRate = ref(0);
|
||||||
|
const peopleCount = ref(0);
|
||||||
|
const teamAttendanceList = ref([{ id: '', teamName: '', attendanceNumber: 0, allNumber: 0, attendanceRate: 0, attendanceTime: '' }]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 显示新闻详情
|
||||||
|
*/
|
||||||
|
const showNewsDetail = (item: any) => {
|
||||||
|
if (newId.value === item.id) {
|
||||||
|
newId.value = '';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
newDetail.value = item;
|
||||||
|
newId.value = item.id;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取项目人员出勤数据
|
||||||
|
*/
|
||||||
|
const getPeopleData = async () => {
|
||||||
|
const res = await getScreenPeople(props.projectId);
|
||||||
|
const { data, code } = res;
|
||||||
|
if (code === 200) {
|
||||||
|
attendanceCount.value = data.attendanceCount;
|
||||||
|
attendanceRate.value = data.attendanceRate;
|
||||||
|
peopleCount.value = data.peopleCount;
|
||||||
|
teamAttendanceList.value = data.teamAttendanceList;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取项目新闻数据
|
||||||
|
*/
|
||||||
|
const getNewsData = async () => {
|
||||||
|
const res = await getScreenNews(props.projectId);
|
||||||
|
const { data, code } = res;
|
||||||
|
if (code === 200) {
|
||||||
|
news.value = data;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化地图
|
||||||
|
*/
|
||||||
|
const initMapChart = () => {
|
||||||
|
if (!mapChartRef.value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mapChart = echarts.init(mapChartRef.value);
|
||||||
|
mapChart.setOption(mapOption);
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
// nextTick(() => {
|
||||||
|
// initMapChart();
|
||||||
|
// });
|
||||||
|
getPeopleData();
|
||||||
|
getNewsData();
|
||||||
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
// if (mapChart) {
|
||||||
|
// mapChart.dispose();
|
||||||
|
// mapChart = null;
|
||||||
|
// }
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.leftPage {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
.topPage,
|
||||||
|
.endPage {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
padding: 15px 0;
|
||||||
|
border: 1px solid rgba(29, 214, 255, 0.1);
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.endPage {
|
||||||
|
flex: 1;
|
||||||
|
margin-top: 23px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
max-height: 100px;
|
||||||
|
margin: 0 15px;
|
||||||
|
padding: 0 10px;
|
||||||
|
margin-top: 15px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
overflow-y: auto;
|
||||||
|
|
||||||
|
&::-webkit-scrollbar-track {
|
||||||
|
background: rgba(204, 204, 204, 0.1);
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::-webkit-scrollbar-thumb {
|
||||||
|
background: rgba(29, 214, 255, 0.78);
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content_item {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 10px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 400;
|
||||||
|
color: rgba(230, 247, 255, 1);
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
.ellipsis {
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
-webkit-line-clamp: 2;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
margin-top: 3px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.map {
|
||||||
|
margin-top: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.attendance_tag {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 0 30px;
|
||||||
|
margin-top: 15px;
|
||||||
|
|
||||||
|
.tag_item {
|
||||||
|
width: 28%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
border: 1px dashed rgba(29, 214, 255, 0.3);
|
||||||
|
padding: 10px;
|
||||||
|
|
||||||
|
.tag_info {
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: rgba(230, 247, 255, 1);
|
||||||
|
text-shadow: 0px 1.24px 6.21px rgba(0, 190, 247, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tag_title {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 400;
|
||||||
|
color: rgba(230, 247, 255, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.attendance_list {
|
||||||
|
padding: 0px 30px;
|
||||||
|
font-size: 14px;
|
||||||
|
|
||||||
|
.attendance_item {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 3fr 2fr 2fr 3fr;
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.subfont {
|
||||||
|
color: rgba(138, 149, 165, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.detailBox {
|
||||||
|
position: absolute;
|
||||||
|
left: 20vw;
|
||||||
|
top: 0;
|
||||||
|
width: 300px;
|
||||||
|
height: 300px;
|
||||||
|
max-height: 500px;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: 0 15px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
background: rgba(255, 255, 255, 0.2);
|
||||||
|
border-radius: 4px;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
opacity: 0;
|
||||||
|
z-index: -1;
|
||||||
|
|
||||||
|
&.show {
|
||||||
|
left: 25vw;
|
||||||
|
opacity: 1;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.detailBox::before {
|
||||||
|
content: '';
|
||||||
|
/* 绝对定位相对于父元素 */
|
||||||
|
position: absolute;
|
||||||
|
/* 定位到左侧中间位置 */
|
||||||
|
left: -10px;
|
||||||
|
top: 50%;
|
||||||
|
/* 垂直居中 */
|
||||||
|
transform: translateY(-50%);
|
||||||
|
/* 利用边框创建三角形 */
|
||||||
|
border-width: 10px 10px 10px 0;
|
||||||
|
border-style: solid;
|
||||||
|
/* 三角形颜色与背景匹配,左侧边框透明 */
|
||||||
|
border-color: transparent rgba(255, 255, 255, 0.2) transparent transparent;
|
||||||
|
/* 确保三角形在内容下方 */
|
||||||
|
z-index: -1;
|
||||||
|
}
|
||||||
|
</style>
|
@ -0,0 +1,153 @@
|
|||||||
|
export let pieOption = {
|
||||||
|
// 定义中心文字
|
||||||
|
graphic: [
|
||||||
|
{
|
||||||
|
type: 'text',
|
||||||
|
left: 'center',
|
||||||
|
top: '40%',
|
||||||
|
style: {
|
||||||
|
// 需要从接口替换
|
||||||
|
text: '70%',
|
||||||
|
fontSize: 24,
|
||||||
|
fontWeight: 'bold',
|
||||||
|
fill: '#fff'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'text',
|
||||||
|
left: 'center',
|
||||||
|
top: '50%',
|
||||||
|
style: {
|
||||||
|
text: '总进度',
|
||||||
|
fontSize: 14,
|
||||||
|
fill: '#fff'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
],
|
||||||
|
legend: {
|
||||||
|
show: true,
|
||||||
|
type: 'plain',
|
||||||
|
bottom: 20,
|
||||||
|
itemWidth: 12,
|
||||||
|
itemHeight: 12,
|
||||||
|
textStyle: {
|
||||||
|
color: '#fff'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
series: {
|
||||||
|
type: 'pie',
|
||||||
|
data: [],
|
||||||
|
radius: [50, 80],
|
||||||
|
center: ['50%', '45%'],
|
||||||
|
itemStyle: {
|
||||||
|
borderColor: '#fff',
|
||||||
|
borderWidth: 1
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
alignTo: 'edge',
|
||||||
|
formatter: '{name|{b}}\n{percent|{c} %}',
|
||||||
|
minMargin: 10,
|
||||||
|
edgeDistance: 20,
|
||||||
|
lineHeight: 15,
|
||||||
|
rich: {
|
||||||
|
name: {
|
||||||
|
fontSize: 12,
|
||||||
|
color: '#fff'
|
||||||
|
},
|
||||||
|
percent: {
|
||||||
|
fontSize: 12,
|
||||||
|
color: '#fff'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
top: 'bottom'
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export let barOption = {
|
||||||
|
legend: {
|
||||||
|
icon: 'rect',
|
||||||
|
itemWidth: 12,
|
||||||
|
itemHeight: 12,
|
||||||
|
// 调整文字与图标间距
|
||||||
|
data: ['计划流转面积', '已流转面积'],
|
||||||
|
top: 0,
|
||||||
|
right: 20,
|
||||||
|
textStyle: {
|
||||||
|
color: '#fff',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
type: 'category',
|
||||||
|
data: ['地块1', '地块2', '地块3', '地块4', '地块5', '地块6'],
|
||||||
|
axisLabel: {
|
||||||
|
color: '#fff'
|
||||||
|
},
|
||||||
|
axisLine: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
splitLine: {
|
||||||
|
show: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
name: '单位:m²',
|
||||||
|
type: 'value',
|
||||||
|
axisLabel: {
|
||||||
|
formatter: '{value}'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
bottom: 0, // 距离容器底部的距离
|
||||||
|
containLabel: true // 确保坐标轴标签不被裁剪
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: '计划流转面积',
|
||||||
|
type: 'bar',
|
||||||
|
data: [],
|
||||||
|
barWidth: '20%',
|
||||||
|
itemStyle: {
|
||||||
|
color: 'rgb(29, 253, 253)'
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '已流转面积',
|
||||||
|
type: 'bar',
|
||||||
|
data: [],
|
||||||
|
barWidth: '20%',
|
||||||
|
itemStyle: {
|
||||||
|
color: 'rgb(25, 181, 251)'
|
||||||
|
},
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
export let mapOption = {
|
||||||
|
geo: {
|
||||||
|
map: 'ch',
|
||||||
|
roam: true,
|
||||||
|
aspectScale: Math.cos((47 * Math.PI) / 180),
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
type: 'graph',
|
||||||
|
coordinateSystem: 'geo',
|
||||||
|
data: [
|
||||||
|
{ name: 'a', value: [7.667821250000001, 46.791734269956265] },
|
||||||
|
{ name: 'b', value: [7.404848750000001, 46.516308805996054] },
|
||||||
|
{ name: 'c', value: [7.376673125000001, 46.24728858538375] },
|
||||||
|
{ name: 'd', value: [8.015320625000001, 46.39460918238572] },
|
||||||
|
{ name: 'e', value: [8.616400625, 46.7020608630855] },
|
||||||
|
{ name: 'f', value: [8.869981250000002, 46.37539345234199] },
|
||||||
|
{ name: 'g', value: [9.546196250000001, 46.58676648282309] },
|
||||||
|
{ name: 'h', value: [9.311399375, 47.182454114178896] },
|
||||||
|
{ name: 'i', value: [9.085994375000002, 47.55395822835779] },
|
||||||
|
{ name: 'j', value: [8.653968125000002, 47.47709530818285] },
|
||||||
|
{ name: 'k', value: [8.203158125000002, 47.44506909144329] }
|
||||||
|
],
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
@ -0,0 +1,221 @@
|
|||||||
|
<template>
|
||||||
|
<div class="leftPage">
|
||||||
|
<div class="topPage">
|
||||||
|
<Title title="项目概况" />
|
||||||
|
<div class="content" v-html="generalize"></div>
|
||||||
|
</div>
|
||||||
|
<div class="endPage">
|
||||||
|
<Title title="形象进度" />
|
||||||
|
<div class="chart_container">
|
||||||
|
<div ref="pieChartRef" class="echart" />
|
||||||
|
<div ref="lineChartRef" class="echart" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, onMounted, onUnmounted, nextTick } from "vue"
|
||||||
|
import Title from './title.vue'
|
||||||
|
import * as echarts from 'echarts';
|
||||||
|
import { pieOption, barOption } from './optionList';
|
||||||
|
import { getScreenLand, getScreenImgProcess, getScreenGeneralize } from '@/api/projectScreen';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
projectId: {
|
||||||
|
type: String,
|
||||||
|
default: 0
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const generalize = ref()
|
||||||
|
// 饼图相关
|
||||||
|
const pieChartRef = ref<HTMLDivElement | null>(null);
|
||||||
|
let pieChart: any = null;
|
||||||
|
const totalPercent = ref(0)
|
||||||
|
// 折线图相关
|
||||||
|
const lineChartRef = ref<HTMLDivElement | null>(null);
|
||||||
|
let lineChart: any = null;
|
||||||
|
// 土地数据 折线图
|
||||||
|
const designAreaData = ref([])
|
||||||
|
const transferAreaData = ref([])
|
||||||
|
// 饼图数据
|
||||||
|
const pieData = [
|
||||||
|
{ label: 'areaPercentage', name: '厂区', value: 0 },
|
||||||
|
{ label: 'roadPercentage', name: '道路', value: 0 },
|
||||||
|
{ label: 'collectorLinePercentage', name: '集电线路', value: 0 },
|
||||||
|
{ label: 'exportLinePercentage', name: '送出线路', value: 0 },
|
||||||
|
{ label: 'substationPercentage', name: '升压站', value: 0 },
|
||||||
|
{ label: 'boxTransformerPercentage', name: '箱变', value: 0 },
|
||||||
|
]
|
||||||
|
|
||||||
|
// 初始化饼图
|
||||||
|
const initPieChart = () => {
|
||||||
|
if (!pieChartRef.value) {
|
||||||
|
console.error('未找到饼图容器元素');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
pieOption.series.data = pieData
|
||||||
|
pieOption.graphic[0].style.text = totalPercent.value + '%'
|
||||||
|
pieChart = echarts.init(pieChartRef.value, null, {
|
||||||
|
renderer: 'canvas',
|
||||||
|
useDirtyRect: false
|
||||||
|
});
|
||||||
|
pieChart.setOption(pieOption);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化折线图
|
||||||
|
const initLineChart = () => {
|
||||||
|
if (!lineChartRef.value) {
|
||||||
|
console.error('未找到折线图容器元素');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
barOption.series[0].data = designAreaData.value
|
||||||
|
barOption.series[1].data = transferAreaData.value
|
||||||
|
lineChart = echarts.init(lineChartRef.value, null, {
|
||||||
|
renderer: 'canvas',
|
||||||
|
useDirtyRect: false
|
||||||
|
});
|
||||||
|
lineChart.setOption(barOption);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 响应窗口大小变化
|
||||||
|
const handleResize = () => {
|
||||||
|
if (pieChart) pieChart.resize();
|
||||||
|
if (lineChart) lineChart.resize();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取项目土地统计数据
|
||||||
|
*/
|
||||||
|
const getScreenLandData = async () => {
|
||||||
|
const res = await getScreenLand(props.projectId);
|
||||||
|
const { data, code } = res
|
||||||
|
if (code === 200) {
|
||||||
|
designAreaData.value = data.map((item: any) => Number(item.designArea))
|
||||||
|
transferAreaData.value = data.map((item: any) => Number(item.transferArea))
|
||||||
|
initLineChart();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取项目形象进度数据
|
||||||
|
*/
|
||||||
|
const getScreenImgProcessData = async () => {
|
||||||
|
const res = await getScreenImgProcess(props.projectId);
|
||||||
|
const { data, code } = res
|
||||||
|
if (code === 200) {
|
||||||
|
totalPercent.value = data.totalPercentage
|
||||||
|
pieData.forEach((item: any) => {
|
||||||
|
item.value = data[item.label]
|
||||||
|
})
|
||||||
|
initPieChart()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取项目概况数据
|
||||||
|
*/
|
||||||
|
const getScreenGeneralizeData = async () => {
|
||||||
|
const res = await getScreenGeneralize(props.projectId);
|
||||||
|
const { data, code } = res
|
||||||
|
if (code === 200) {
|
||||||
|
generalize.value = data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 组件挂载时初始化图表
|
||||||
|
onMounted(() => {
|
||||||
|
getScreenLandData()
|
||||||
|
getScreenImgProcessData()
|
||||||
|
getScreenGeneralizeData()
|
||||||
|
nextTick(() => {
|
||||||
|
initPieChart();
|
||||||
|
window.addEventListener('resize', handleResize);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// 组件卸载时清理
|
||||||
|
onUnmounted(() => {
|
||||||
|
window.removeEventListener('resize', handleResize);
|
||||||
|
if (pieChart) {
|
||||||
|
pieChart.dispose();
|
||||||
|
pieChart = null;
|
||||||
|
}
|
||||||
|
if (lineChart) {
|
||||||
|
lineChart.dispose();
|
||||||
|
lineChart = null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.leftPage {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
.topPage,
|
||||||
|
.endPage {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
padding: 15px 0;
|
||||||
|
border: 1px solid rgba(29, 214, 255, 0.1);
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.endPage {
|
||||||
|
flex: 1;
|
||||||
|
margin-top: 23px;
|
||||||
|
|
||||||
|
.chart_container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 5px;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.echart {
|
||||||
|
height: 48%;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
height: 100px;
|
||||||
|
margin: 0 15px;
|
||||||
|
padding: 0 10px;
|
||||||
|
margin-top: 15px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
overflow-y: auto;
|
||||||
|
|
||||||
|
&::-webkit-scrollbar-track {
|
||||||
|
background: rgba(204, 204, 204, 0.1);
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::-webkit-scrollbar-thumb {
|
||||||
|
background: rgba(29, 214, 255, 0.78);
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content_item {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 400;
|
||||||
|
color: rgba(230, 247, 255, 1);
|
||||||
|
margin-bottom: 10px;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.subfont {
|
||||||
|
color: rgba(138, 149, 165, 1);
|
||||||
|
}
|
||||||
|
</style>
|
@ -0,0 +1,45 @@
|
|||||||
|
<template>
|
||||||
|
<div class="title">
|
||||||
|
<div class="title_icon">
|
||||||
|
<img src="@/assets/projectLarge/section.svg" alt="">
|
||||||
|
<img src="@/assets/projectLarge/border.svg" alt="">
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<slot></slot>
|
||||||
|
</div>
|
||||||
|
<div>{{ title }}</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
defineProps({
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
default: '标题'
|
||||||
|
},
|
||||||
|
prefix: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.title {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 3px;
|
||||||
|
font-family: 'AlimamaShuHeiTi', sans-serif;
|
||||||
|
|
||||||
|
.title_icon {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&>img:last-child {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 4px;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
107
src/views/projectLarge/digitalizationScreen/index.vue
Normal file
107
src/views/projectLarge/digitalizationScreen/index.vue
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
<template>
|
||||||
|
<div class="large_screen">
|
||||||
|
<Header :projectId="projectId" :isFull="isFull" @changePage="handleChangePage" />
|
||||||
|
<div class="nav">
|
||||||
|
<div class="nav_left" :style="{ left: isHideOther ? '-25vw' : '0' }">
|
||||||
|
<leftPage :projectId="projectId" />
|
||||||
|
</div>
|
||||||
|
<div class="nav_center" :style="{ width: isFull ? '100%' : 'calc(50vw - 30px)' }">
|
||||||
|
<centerPage :projectId="projectId" :isHide="isFull" />
|
||||||
|
</div>
|
||||||
|
<div class="nav_right" :style="{ right: isHideOther ? '-25vw' : '0' }">
|
||||||
|
<rightPage :projectId="projectId" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import Header from './components/header.vue';
|
||||||
|
import leftPage from './components/leftPage.vue';
|
||||||
|
import centerPage from './components/centerPage.vue';
|
||||||
|
import rightPage from './components/rightPage.vue';
|
||||||
|
import { useUserStoreHook } from '@/store/modules/user';
|
||||||
|
const userStore = useUserStoreHook();
|
||||||
|
const projectId = computed(() => userStore.selectedProject.id);
|
||||||
|
const isFull = ref(false);
|
||||||
|
const isHideOther = ref(false);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 切换中心页面全屏
|
||||||
|
*/
|
||||||
|
const handleChangePage = () => {
|
||||||
|
if (isFull.value) {
|
||||||
|
isFull.value = false;
|
||||||
|
setTimeout(() => {
|
||||||
|
isHideOther.value = false;
|
||||||
|
}, 500);
|
||||||
|
} else {
|
||||||
|
isFull.value = true;
|
||||||
|
isHideOther.value = true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.large_screen {
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
background: url('@/assets/large/bg.png') no-repeat;
|
||||||
|
background-size: 100% 100%;
|
||||||
|
background-attachment: fixed;
|
||||||
|
background-color: rgba(4, 7, 17, 1);
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav {
|
||||||
|
position: relative;
|
||||||
|
display: grid;
|
||||||
|
place-items: center;
|
||||||
|
width: calc(100vw - 30px);
|
||||||
|
height: calc(100vh - 90px);
|
||||||
|
margin: 0 auto;
|
||||||
|
box-sizing: border-box;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav_left,
|
||||||
|
.nav_right {
|
||||||
|
position: absolute;
|
||||||
|
width: calc(25vw - 15px);
|
||||||
|
height: 100%;
|
||||||
|
transition: all 0.5s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav_left {
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav_right {
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav_center {
|
||||||
|
height: 100%;
|
||||||
|
transition: all 0.5s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 中间面板全屏动画 */
|
||||||
|
.full-width {
|
||||||
|
/* 取消flex增长,使用固定宽度 */
|
||||||
|
width: calc(100vw - 30px);
|
||||||
|
flex: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slide_left {
|
||||||
|
left: -25vw;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slide_right {
|
||||||
|
right: -25vw;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
</style>
|
Reference in New Issue
Block a user