You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

726 lines
19 KiB

<template>
<!-- <div class="welcome-page">欢迎来到智慧水利水工程应用v24.3.0~</div>-->
<div class="statistics-page">
<div class="parent-page">
<div class="child-page">
<svg-icon icon-class="u10" style="padding-top: 15px"/>
<div class="font-page">
<p>全部</p>
<p>{{ total }}</p>
</div>
<div class="right-font">
<p style="padding-bottom: 5px">施工中 {{acc["noAcc"]}}</p>
<p>已完工 {{acc["acc"]}}</p>
</div>
</div>
<div class="child-page">
<svg-icon icon-class="u18" style="padding-top: 15px"/>
<div class="font-page" >
<p>重大项目</p>
<p>{{ zdTotal }}</p>
</div>
<div class="right-font">
<p v-for="([key,value]) in zdTwoKeys" >{{zdProjectTypeFormat(key)}} {{value}}个</p>
</div>
</div>
<div class="child-page">
<svg-icon icon-class="u23" style="padding-top: 15px"/>
<div class="font-page" >
<p>面上项目</p>
<p>{{ msTotal }}个</p>
</div>
<div class="right-font">
<p v-for="([key,value]) in msTwoKeys" >{{msProjectTypeFormat(key)}} {{value}}个</p>
</div>
</div>
</div>
<div class="parent-2">
<div class="box_2">
<div class="top-title">项目类型</div>
<div ref="Ele" style="width: 100%; height: 60%;min-width: 350px"></div>
</div>
<div class="box_2">
<div class="top-title">投资完成</div>
<div ref="bar" style="width: 100%; height: 70%;margin-top: 20px"></div>
</div>
<div class="box_2">
<div class="top-title">施工中项目亮灯情况</div>
<div style="padding: 50px;padding-left: 70px">
<el-row style="margin-bottom: 60px">
<el-col :span="12">
<svg-icon icon-class="redLight"/>
<div class="font-light">
<p style="color: red">{{ this.forewarningNum.warn }}</p>
<p>预警</p>
</div>
</el-col>
<el-col :span="12" style="padding-left: 15px">
<svg-icon icon-class="yellowLight"/>
<div class="font-light">
<p style="color: yellow">{{ this.forewarningNum.forewarning }}</p>
<p>提醒</p>
</div>
</el-col>
</el-row>
<el-row>
<svg-icon icon-class="greenLight"/>
<div class="font-light">
<p style="color: green">{{ this.forewarningNum.normal }}</p>
<p>正常</p>
</div>
</el-row>
</div>
</div>
</div>
<div class="parent-3" >
<el-form
:model="queryParams"
ref="queryForm"
:inline="true"
v-show="showSearch"
>
<el-form-item label="项目名称" prop="projectName">
<el-input
v-model="queryParams.data.projectName"
placeholder="请输入项目名称"
clearable
size="small"
@keyup.enter.native="handleQuery"
>
<el-button
type="primary"
slot="append"
icon="el-icon-search"
size="small"
@click="handleQuery"
></el-button>
</el-input>
</el-form-item>
<el-form-item>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery"
>重置</el-button
>
</el-form-item>
</el-form>
<el-table v-loading="loading" :data="InfoList" style="background-image: linear-gradient(rgb(199, 243, 227), rgba(199, 243, 227, 0.3))"
>
<!-- <el-table-column type="selection" width="55" align="center" fixed /> -->
<el-table-column
label="序号"
type="index"
width="50"
align="center"
fixed
/>
<el-table-column
label="行政区划"
prop="adcd"
width="120"
align="center"
:formatter="$formatAdcd"
/>
<el-table-column
label="项目名称"
prop="projectName"
min-width="120"
align="center"
/>
<el-table-column
label="项目批复时间"
prop="appTime"
min-width="120"
align="center"
:formatter="formatDate"
/>
<el-table-column
label="计划总投资(万元)"
prop="planInvestment"
min-width="120"
align="center"
/>
<el-table-column
label="累计完成总投资(万元)"
prop="actInvestment"
min-width="120"
align="center"
/>
<el-table-column
label="亮灯状态"
align="center"
class-name="small-padding fixed-width"
min-width="120"
>
<template slot-scope="scope">
<svg-icon v-if="scope.row.type === '0'" icon-class="redLight"/>
<svg-icon v-else-if="scope.row.type === '1'" icon-class="yellowLight"/>
<svg-icon v-else-if="scope.row.type === '3'" icon-class="greenLight"/>
</template>
</el-table-column>
<el-table-column
label="操作"
align="center"
class-name="small-padding fixed-width"
width="180"
fixed="right"
>
<template slot-scope="scope">
<el-button
size="mini"
type="text"
icon="el-icon-view"
@click="viewInfo(scope.row)"
>查看详情</el-button
>
</template>
</el-table-column>
</el-table>
<pagination
v-show="pageTotal > 0"
:total="pageTotal"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
@pagination="getXMInfo"
/>
</div>
</div>
</template>
<script>
import Div from '../build/div/index.vue'
import { getTypeNum,getForewarningNum } from "@/api/projectStatistics/sort";
import log from '../monitor/job/log.vue'
import { integer } from 'mockjs/src/mock/random/basic'
import index from '@vue/test-utils'
import * as echarts from 'echarts'
import { getInfo, getSourceFundsNum } from '../../api/projectStatistics/sort'
import { codeToText } from 'element-china-area-data'
export default {
components: { Div },
data(){
return{
loading:false,
InfoList:[],
zd_projectTypeOptions:[],
ms_projectTypeOptions:[],
sourceFound:{},
acc:{},
pageTotal:0,
TypeNum:{},
showSearch: true,
forewarningNum:[],
ms:{},
zd:{},
adcd:[],
year:"2024",
routeList: [
{
path: "/evaluationEarlyWarning/earlyWarningManage/warning",
routeName: "项目预警管理",
},
],
formattedZd:{},
formattedMs:{},
projectType:[],
zdTotal:0,
msTotal:0,
total:0,
queryParams: {
pageNum: 1,
pageSize: 10,
ids: null,
data: {
projectName: null,
projectType: null,
total: null,
projectPhase: null,
},
},
}
},
mounted(){
},
created() {
this.getDicts("major_project").then((response) => {
this.zd_projectTypeOptions = response.data;
});
this.getDicts("general_project").then((response) => {
this.ms_projectTypeOptions = response.data;
});
this.getXMInfo()
getSourceFundsNum(this.year).then(res=>{
this.sourceFound=res.data
this.adcd=Object.keys(this.sourceFound)
console.log(this.adcd)
this.barInit();
})
getForewarningNum().then(res=>
{
this.forewarningNum=res.data
console.log(this.forewarningNum)
})
getTypeNum(this.queryParams).then(
res=>{
this.zd=res.data.zd
this.ms=res.data.ms
this.acc=res.data.acc
Object.entries(this.zd).forEach(([key, value]) => {
this.zdTotal=this.zdTotal+Number(value)
let name=this.zdProjectTypeFormat(key)
let obj={
name:name,
value:Number(value)
}
this.projectType.push(obj)
});
Object.entries(this.ms).forEach(([key, value]) => {
this.msTotal=this.msTotal+Number(value)
let name=this.msProjectTypeFormat(key)
let obj={
name:name,
value:Number(value)
}
this.projectType.push(obj)
});
this.total=Number(this.acc["acc"])+Number(this.acc["noAcc"])
console.log(this.projectType)
this.eleInit();
}
)
},
computed: {
zdTwoKeys() {
// 将对象转换为数组,按值排序,取前两个
const sortedEntries = Object.entries(this.zd).sort(([, a], [, b]) => b - a);
return sortedEntries.slice(0, 2);
},
msTwoKeys() {
// 将对象转换为数组,按值排序,取前两个
const sortedEntries = Object.entries(this.ms).sort(([, a], [, b]) => b - a);
return sortedEntries.slice(0, 2);
},
},
methods:{
move(row){
console.log(row)
},
resetQuery() {
this.resetQueryForm();
this.getXMInfo()
},
resetQueryForm(){
this.queryParams={
pageNum: 1,
pageSize: 5,
ids: null,
data: {
projectName: null,
projectType: null,
total: null,
projectPhase: null
}
}
},
handleQuery() {
this.queryParams.pageNum = 1;
this.getXMInfo();
},
truncateName(name) {
if (name.length > 4) {
return name.substring(0, 4) + '..';
}
return name;
},
getXMInfo() {
getInfo(this.queryParams).then(
res=>
{this.InfoList=res.data.records
this.pageTotal=res.data.total
console.log(this.pageTotal)
}
)
},
formatDate(row, column, cellValue, index) {
if (!cellValue) return '';
// 解析日期
const date = new Date(cellValue);
// 格式化为 YYYY-MM-DD 格式
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
return `${year}-${month}-${day}`;
},
formatAdcd(row) {
if (row) {
return (
codeToText[row]
);
}
},
msProjectTypeFormat(key) {
return this.selectDictLabel(this.ms_projectTypeOptions, key);
},
zdProjectTypeFormat(key) {
return this.selectDictLabel(this.zd_projectTypeOptions, key);
},
viewInfo(row){
// console.log(row)
this.routeList[0].routeName = row.projectName;
this.routeList.push({
path: "/evaluationEarlyWarning/earlyWarningManage/warning/options",
routeName: row.proCode,
isEdit: true,
});
// 存储面包屑信息
this.$store.commit("setRouteList", JSON.stringify(this.routeList));
// 跳转到选项卡页面
this.$router.push({
path:
"/evaluationEarlyWarning/earlyWarningManage/warning/options?baseDataId=" +
row.id,
})
},
eleInit() {
let chartDom = this.$refs.Ele;
let myChart = echarts.init(chartDom);
let option = {
tooltip: {
trigger: 'item'
},
grid: {
top: '20%', // 控制图表上方的间距
bottom: '40%',// 控制图表下方的间距
left: '0%', // 控制图表左侧的间距
right: '70%' // 控制图表右侧的间距
},
legend: {
orient: 'vertical', // 图例竖向排列
right: 0, // 图例放置在图形右侧,右边距 10
top: 'center', // 图例垂直居中
itemWidth: 20, // 图例项的宽度
itemHeight: 14, // 图例项的高度
padding: [0, 10, 0, 10], // 图例四周的内边距,防止与图形重叠
textStyle: {
fontSize: 10, // 设置字体大小为12px
color: '#333' // 设置字体颜色,默认为#333(深灰色)
}
},
color: ['#2E8B57', '#66CDAA', '#20B2AA', '#3CB371', '#228B22'], // 设置绿色系颜色
series: [
{
type: 'pie',
radius: ['40%', '60%'],
center: ["30%", "50%"],
avoidLabelOverlap: true,
padAngle: 5,
itemStyle: {
borderRadius: 10
},
label: {
show: false,
position: 'center',
textStyle: {
fontSize: 12, // 设置字体大小为12px
color: '#333' // 设置字体颜色
}
},
emphasis: {
label: {
show: true,
fontSize: 20,
fontWeight: 'bold'
}
},
labelLine: {
show: false
},
data:this.projectType.map(item => ({
...item,
name: this.truncateName(item.name)
}))
// data:[{name:'2233',value:2},{name:'21',value:2},{name:'32',value:2},
// {name:'32',value:2},{name:'12',value:2},{name:'61',value:2},{name:'34',value:2}
// ,{name:'42',value:2},{name:'32',value:2},{name:'33',value:2},{name:'43',value:2}
// ,{name:'52',value:2},{name:'72',value:2},{name:'31',value:2},{name:'55',value:2},{name:'56',value:2}
// ,{name:'62',value:2}]
}
]
};
option && myChart.setOption(option);
window.addEventListener("resize", function () {
myChart.resize();
});
},
barInit(){
const progressPlanValues = Object.values(this.sourceFound).map(obj => obj.progressPlan);
const actualValues = Object.values(this.sourceFound).map(obj => obj.actual);
const maxProgressPlan = Math.max(...progressPlanValues);
const maxActual = Math.max(...actualValues);
// 过滤掉 undefined 的项,并获取有效数据的索引
const validIndices = this.adcd
.map((item, index) => codeToText[item] ? index : null)
.filter(index => index !== null);
// 根据有效索引过滤 xAxis 数据
const filteredData = this.adcd
.filter((item, index) => validIndices.includes(index))
.map(item => codeToText[item]);
// 根据有效索引过滤 series 数据
const filteredSourceFound = Object.values(this.sourceFound)
.filter((_, index) => validIndices.includes(index));
const indexedSourceFound = filteredSourceFound.map((obj, index) => ({ ...obj, index }));
const indexedActuals = indexedSourceFound.map(obj => obj.actual);
// 根据 actuals 从大到小排序
const sortedByActuals = indexedSourceFound
.map(obj => ({ ...obj, actual: indexedActuals[obj.index] }))
.sort((a, b) => b.actual - a.actual);
// 提取排序后的索引
const sortedIndices = sortedByActuals.map(obj => obj.index);
// 根据排序后的索引重新排列 filteredData 和 filteredSourceFound
const sortedFilteredData = sortedIndices.map(index => filteredData[index]);
const sortedFilteredSourceFound = sortedIndices.map(index => filteredSourceFound[index]);
const progressPlans = sortedFilteredSourceFound.map(obj => obj.progressPlan);
const actuals = sortedFilteredSourceFound.map(obj => obj.actual);
// 取两个最大值中的最大值作为 yAxis 的 max
const yAxisMax = Math.max(maxProgressPlan, maxActual);
let chartDom = this.$refs.bar;
let myChart = echarts.init(chartDom);
let option = {
grid: {
left: '18%',
right: '0%',
top: '15%',
bottom: '20%'
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'cross',
crossStyle: {
color: '#999'
}
}
},
legend: {
data: ['年度计划', '实际完成'],
},
xAxis: [
{
axisLine: {
show: false // 隐藏 X 轴线
},
axisLabel: { interval: 0,rotate: 45 },
type: 'category',
data:sortedFilteredData,
axisPointer: {
type: 'shadow'
},
axisTick: {
alignWithLabel: true // 使刻度线与标签对齐
},
interval: 0,
fontSize: 2, // 设置字体大小
}
],
yAxis: [
{
axisLine: {
show: false // 隐藏 X 轴线
},
type: 'value',
name: '金额/万元',
min: 0,
max: yAxisMax,
interval: yAxisMax/10,
}
],
series: [
{
name: '年度计划',
type: 'bar',
color: '#2ecc71', // 绿色
tooltip: {
valueFormatter: function (value) {
return value ;
}
},
data: progressPlans
},
{
name: '实际完成',
type: 'bar',
color: '#27ae60', // 深绿色
tooltip: {
valueFormatter: function (value) {
return value;
}
},
data: actuals
}
],
};
option && myChart.setOption(option);
window.addEventListener("resize", function () {
myChart.resize();
});
}
}
}
</script>
<style scoped lang="scss">
.welcome-page {
padding: 24px;
font-weight: 700;
font-size: 20px;
color: #333;
}
.statistics-page{
background-color: white;
margin: 5px;
}
.parent-2{
display: flex;
background-color: white;
width: 100%;
height: 300px;
.box_3{
height: 400px;
width: 350px;
.svg-icon{
font-size: 60px;
line-height: 100%;
float: left;
}
.font-light{
line-height: 100%;
margin-top: 10px;
margin-left: 70px;
font-size: 24px;
}
.top-title{
margin-left: 30px;
font-family: 'PingFangSC-Semibold', 'PingFang SC Semibold', 'PingFang SC', sans-serif;
font-weight: 650;
font-style: normal;
font-size: 24px;
}
}
.box_2{
height: 400px;
flex: 1;
.svg-icon{
font-size: 60px;
line-height: 100%;
float: left;
}
.font-light{
line-height: 100%;
margin-top: 10px;
margin-left: 70px;
font-size: 24px;
}
.top-title{
margin-left: 30px;
font-family: 'PingFangSC-Semibold', 'PingFang SC Semibold', 'PingFang SC', sans-serif;
font-weight: 650;
font-style: normal;
font-size: 24px;
}
}
}
.parent-3{
width: 97%;
margin: 30px;
}
.parent-page{
padding-top:10px;
margin: 20px;
display: flex;
width: 100%;
.child-page{
position: relative;
flex: 1;
display: flex;
width: 587px;
height: 105px;
margin-right: 25px;
background-image: linear-gradient(rgb(199, 243, 227), rgba(199, 243, 227, 0.3));
border-radius: 10px;
line-height: 105px;
.svg-icon{
flex: 1;
color: black;
font-size: 70px;
}
.font-page{
flex: 1;
padding-top: 20px;
height: 50px;
line-height: 25px;
font-family: 'PingFangSC-Regular', 'PingFang SC', sans-serif;
font-weight: 200;
font-style: normal;
font-size: 20px;
}
.right-font{
right: 0px;
flex: 2;
padding-top: 20px;
height: 50px;
line-height: 25px;
margin-left: 5px;
font-family: 'PingFangSC-Regular', 'PingFang SC', sans-serif;
font-weight: 200;
font-style: normal;
font-size: 18px;
}
}
}
</style>