<template> <div class="app-container home"> <div class="top"> <div class="topLeft"> <div class="tittle flex"> <img src="../assets/projectCont.png" alt="" /> <div class="conttittle">项目统计</div> <div class="top_time"> <el-radio-group v-model="classify"> <el-radio-button label="currentYear">当年</el-radio-button> <el-radio-button label="accumulativeTotal">累计</el-radio-button> </el-radio-group> </div> </div> <div class="flex cont"> <div v-for="i in projectList"> <div class="box">{{ statisticsData[classify][i.value] || 0 }}<span>个</span></div> <div class="label" :style="{ color: i.label == '延期' ? '#FF4F4FFF' : '' }" > {{ i.label }} </div> </div> </div> </div> <div class="topRight"> <div class="tittle flex"> <img src="../assets/projectMongy.png" alt="" /> <div class="conttittle">金额统计</div> </div> <div class="flex cont"> <div> <div class="box">{{ statisticsData.homePageMoney.totalMoney || 0 }} <span>万元</span></div> <div class="label">总金额</div> </div> <div> <div class="box">{{ statisticsData.homePageMoney.spongeTotalMoney || 0 }} <span>万元</span></div> <div class="label">海绵相关投资</div> </div> <div> <div class="box">{{ statisticsData.homePageMoney.currentYearTotalMoney || 0 }} <span>万元</span></div> <div class="label">当年完成项目总金额</div> </div> <div> <div class="box">{{ statisticsData.homePageMoney.currentYearSpongeMoney || 0 }} <span>万元</span></div> <div class="label">当年完成海绵相关投资</div> </div> </div> </div> </div> <div class="center flex"> <div class="centerLeft"> <div class="ectittle conttittle">项目类型统计</div> <homeEcharts v-if="isChart" :data="data1"></homeEcharts> </div> <div class="center_div"> <div class="ectittle conttittle">项目建设状态统计</div> <homeEcharts v-if="isChart1" :data="data2"></homeEcharts> </div> <div class="centerright"> <div class="ectittle conttittle">项目质量统计</div> <queryChat v-if="isChart2 && data3.xData.length > 0" :data="data3" ></queryChat> <div style="text-align: center; margin-top: 30%; color: #ddd" v-else> 暂无数据 </div> </div> </div> <div class="footer"> <MapBox id="map" ref="mapRef" :isShowTool="false" @clickMap="clickMap"/> <div class="tabs"> <div class="tab_it" v-for="tab in tabs" @click="tabClick(tab)" :class="{ active: curTab === tab }" > <span>{{ tab.name }}</span> </div> </div> <div class="legendList"> <div class="legend_item" v-for="(item, i) in legendList"> <span class="icon" :style="`background-color: ${colorList[i]}`"></span> <span>{{ item.name }}</span> </div> </div> </div> </div> </template> <script setup name="Index"> import { getCurrentInstance } from "vue"; import homeEcharts from "./homeEcharts/index.vue"; import queryChat from "./homeEcharts/queryChat.vue"; import MapBox from '@/components/Map' import { selectProjectQualityHomePage, getHomeStatistics, getProjectYearByType, getHomePageInfoByType, getDicts } from "@/api/home" import { projectTypeList } from '@/api/projectInformationNew' const { proxy } = getCurrentInstance() const { build_status } = proxy.useDict('build_status') const colorList = [ 'rgba(84,112,198,1)', 'rgba(145,204,117,1)', 'rgba(250,200,88,1)', 'rgba(238,102,102,1)', 'rgba(115,192,222,1)', 'rgba(59,162,114,1)', 'rgba(252,132,82,1)', 'rgba(154,96,180,1)', 'rgba(234,124,204,1)' ] const isChart = ref(false); const isChart1 = ref(false); const isChart2 = ref(false); const data1 = ref({ xData: [], yData: [], tittle: "项目类型统计", type: "pie", center: ["40%", "50%"], padding: [0, 50, 0, 0], x:'35%', y:'40%', // legend: ["",], itemStyle: { normal: { color: function (colors) { var colorList = [ "#fc8251", "#5470c6", "#9A60B4", "#ef6567", "#f9c956", "#3BA272", ]; return colorList[colors.dataIndex]; }, }, }, }); const data2 = ref({ xData: [], yData: [], type: "pie", center: ["40%", "50%"], x:'35%', y:'40%', // legend: [""], padding: [0, 0, 0, 0], itemStyle: { normal: { color: function (colors) { var colorList = [ "#9A60B4", "#f9c956", "#3BA272", "#fc8251", "#5470c6", "#ef6567", ]; return colorList[colors.dataIndex]; }, }, }, }); const data3 = ref({ xData: [], yData: [], tittle: "", type: "bar", color: "#0F69FF", title: "统计数据", interval: 5, legend: ["待整改数", "总数"], }); const projectList = ref([ { label: "总数", value: "projectCount" }, { label: "在建", value: "startCount" }, { label: "已完工", value: "endCount" }, { label: "延期", value: "postponeCount" }, ]); const classify = ref('currentYear') const tabs = ref([ { id: 1, name: '项目类型' }, { id: 2, name: '开工年份' }, { id: 3, name: '竣工年份' }, { id: 4, name: '任务类型' }, { id: 5, name: '库内库外项目' }, ]) const statisticsData = reactive({ accumulativeTotal: {}, currentYear: {}, homePageMoney: {}, buildTypeStatistics: [], projectTypeStatistics: [] }) // const projectTypes = ref([]) const legendList = ref([]) let projectListMap = {} const curTab = ref(tabs.value[0]) const tabClick = async (tab) => { curTab.value = tab switch (curTab.value.id) { case 1: getProjectTypeList(tab.id) break; case 2: getProjectYearByTypes(tab.id) break; case 3: getProjectYearByTypes(tab.id) break; case 4: getDictMaps('task_type', tab.id) break; case 5: getDictMaps('project_library_type', tab.id) break; default: break; } } const getProjectTypeList = async (id) => { const res = await projectTypeList({ status: '0' }) if(res?.code !== 200) return legendList.value = res?.data || [] legendList.value = legendList.value.map(item => ({ id: item.id, name: item.projectTypeName })) getHomePageInfoByTypes(id) } const getProjectYearByTypes = async (id) => { const res = await getProjectYearByType(id) if(res?.code !== 200) return legendList.value = res?.data || [] legendList.value = legendList.value.map(item => ({ id: item, name: item })) getHomePageInfoByTypes(id) } const getDictMaps = async (dictType, id) => { const res = await getDicts(dictType) if(res?.code !== 200) return legendList.value = res?.data || [] legendList.value = legendList.value.map(item => ({ id: item.dictValue, name: item.dictLabel })) getHomePageInfoByTypes(id) } const getHomePageInfoByTypes = async (id) => { const res = await getHomePageInfoByType(id) if(res?.code !== 200) return projectListMap = res?.data || {} console.log('接口数据', projectListMap) const mapData = [] for (const key in projectListMap) { const index = legendList.value.findIndex(ele => ele.id === key) const color = colorList[index] || 'rgba(128, 128, 128, 1)' if (Object.hasOwnProperty.call(projectListMap, key)) { for (let i = 0; i < projectListMap[key].length; i++) { const it = projectListMap[key][i]; if(!it.projectLocation) continue const options = getOptions(it, color) mapData.push(options) } } } // 地图渲染数据 console.log('地图数据', mapData) if(!mapData.length) return proxy.$refs.mapRef.clear() setTimeout(() => { renderMapData(mapData) }, 100) } const renderMapData = (mapData) => { let geojson = NewFiberMap.Data.ToGeoJSON.beansWktToGeoJson(mapData); toCenterByGeoJson(geojson); newfiberMap.geojsonToMap(geojson); } const toCenterByGeoJson = (geojson) => { let coords = turf.getCoords(geojson.features[0].geometry).flat(); let flag = geojson.features.length == 1 && coords.length == 2; if (!!geojson.features.length && !flag) { newfiberMap.getMap().camera.flyTo({destination: new Cesium.Rectangle.fromDegrees(...turf.bbox(turf.transformScale(turf.bboxPolygon(turf.bbox(geojson)), 2)))}); } else { newfiberMap.setCenter({ "roll": 0.01658908985506884, "pitch": -87.24924906709752, "heading": 5.026928271138224, "lng": coords[0], "lat": coords[1], "height": 943.5996932813425 }) } } const clickMap = (feature) => { console.log(feature) } const getOptions = (it, color) => { let options = {} if (it.projectLocation.includes('POINT')) { options = { // type: NewFiberMap.Enum.VectorType.ICON, type: NewFiberMap.Enum.VectorType.SPECIAL_CIRCLE, id: 'point', data: it, name:(it.projectName || "").substr(0,10) + (it.projectName.length > 10 ? ".....":''), style: { radius: 10, clampToGround: false, material: new NewFiberMap.Material.CircleDiffuseMaterialProperty({ color: Cesium.Color.fromCssColorString(color), speed: 20, }), }, labelOptions: { font: "17px PingFang SC", style: Cesium.LabelStyle.FILL_AND_OUTLINE, outlineColor: 'rgba(20,83,154,1)', outline: true, outlineWidth: 3, color: "#ffffff", pixelOffset: [0, -6], distanceDisplayCondition: [0, 2100000], }, geometrys: it.projectLocation, } } else if (it.projectLocation.includes('LINE')) { options = { type: NewFiberMap.Enum.VectorType.POLYLINE, id: 'line', data: it, name:(it.projectName || "").substr(0,10) + (it.projectName.length > 10 ? ".....":''), geometrys: it.projectLocation, style: { width: 5, material: color, color: color, clampToGround: true }, labelOptions: { font: "17px PingFang SC", style: Cesium.LabelStyle.FILL_AND_OUTLINE, outlineColor: 'rgba(20,83,154,1)', outline: true, outlineWidth: 3, color: "#ffffff", pixelOffset: [0, -6], distanceDisplayCondition: [0, 2100000], }, } } else if (it.projectLocation.includes('POLYGON')) { options = { type: NewFiberMap.Enum.VectorType.POLYGON, style: { material: color, color: color }, geometrys: it.projectLocation, id: 'area', data: it, name:(it.projectName || "").substr(0,10) + (it.projectName.length > 10 ? ".....":''), labelOptions: { font: "17px PingFang SC", style: Cesium.LabelStyle.FILL_AND_OUTLINE, outlineColor: 'rgba(20,83,154,1)', outline: true, outlineWidth: 3, color: "#ffffff", pixelOffset: [0, -6], distanceDisplayCondition: [0, 2100000], }, } } return options } const getData = async () => { const res = await getHomeStatistics() if(res?.code !== 200) return const { currentYear, accumulativeTotal, projectTypeStatistics, buildTypeStatistics, homePageMoney } = res.data statisticsData.currentYear = currentYear statisticsData.accumulativeTotal = accumulativeTotal statisticsData.homePageMoney = homePageMoney statisticsData.buildTypeStatistics = buildTypeStatistics.map(item => ({ typeCount: item.typeCount, typeName: build_status.value.find(it => it.value === item.typeName)?.label || '' })) statisticsData.projectTypeStatistics = projectTypeStatistics renderPie(statisticsData.projectTypeStatistics, 1) renderPie(statisticsData.buildTypeStatistics, 2) renderBar() } const renderPie = (data, type) => { let array = []; data.forEach((i) => { array.push({ name: i.typeName, value: i.typeCount }); }); switch (type) { case 1: data1.value.xData = array; isChart.value = true; break; case 2: data2.value.xData = array; isChart1.value = true; break; default: break; } } const renderBar = async () => { let { data } = await selectProjectQualityHomePage(); data3.value.xData = []; data3.value.yData = []; let arr1 = []; let arr2 = []; data.forEach((i) => { data3.value.xData.push(i.name || "无标题"); arr1.push(i.count); arr2.push(i.noRectifiedCount); data3.value.yData = { contractMoney: arr1, paymentMoney: arr2, }; isChart2.value = true; }); } onMounted(() => { getData() tabClick(curTab.value) }); </script> <style scoped lang="scss"> .home { height: 91vh; width: 100%; overflow: hidden; .top { height: 13vh; display: flex; justify-content: space-between; flex-shrink: 0; .topLeft, .topRight { height: 100%; box-shadow: 0px 2px 6px 0px rgba(28, 52, 92, 0.1); border-radius: 6px; margin: 5px 7px; flex: 1; .tittle { font-weight: bold; color: #333333; padding: 5px; position: relative; .top_time { position: absolute; right: 0.5%; } div { margin-left: 10px; } } .label { font-size: 14px; } } } .ectittle { font-size: 18px; font-weight: bold; color: #464646; position: absolute; left: 3%; top: 2%; } } .center { justify-content: space-between; margin: 10px; height: 27vh; .center_div, .centerLeft, .centerright { height: 100%; flex: 1; box-shadow: 0px 2px 6px 0px rgba(28, 52, 92, 0.1); position: relative; .center_time { position: absolute; right: 0.5%; z-index: 1; } } .center_div { margin: 10px 20px; padding: 0 10px; } } .footer { position: relative; height: 45vh; .tabs { position: absolute; bottom: 10px; left: 50%; transform: translateX(-50%); display: flex; align-items: center; color: #fff; z-index: 10; .tab_it { margin-right: 10px; width: 150px; height: 30px; line-height: 30px; background-color: rgb(240, 128, 0, 1); text-align: center; cursor: pointer; &:last-of-type { margin-right: 0; } &:hover { background-color: rgb(240, 128, 0, 0.5); } &.active { background-color: rgb(240, 128, 0, 0.5); } } } .legendList { min-width: 160px; background-color: #fff; border-radius: 6px; font-size: 16px; position: absolute; left: 10px; bottom: 10px; z-index: 10; padding: 8px 15px; box-shadow: 0px 0px 12px rgba(0, 0, 0, .12); .legend_item { line-height: 30px; display: flex; align-items: center; .icon { width: 16px; height: 16px; margin-right: 10px; } } } } .flex { display: flex; align-items: center; } .cont { justify-content: center; text-align: center; div { flex: 1; } .box { border-right: none; font-size: 22px; font-family: Source Han Sans CN; font-weight: bold; color: #333333; span { color: "#666666"; font-size: 14px; font-weight: 500; } } } ::v-deep .el-tabs__nav-wrap::after { background-color: #fff; } ::v-deep .el-tabs__item.is-active { font-size: 16px; font-weight: bold; } ::v-deep .el-tabs__active-bar { bottom: 0; left: 35px; height: 3px; width: 30px !important; } .conttittle { font-weight: 800; font-size: 18px; } </style>