用水统计
This commit is contained in:
@@ -12,3 +12,39 @@ export const lookDetails = (roomNo: string) => {
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 水电统计
|
||||
* @param params 统计参数
|
||||
*/
|
||||
export const getStats = (params: any) => {
|
||||
return request({
|
||||
url: '/stuwork/watermonthreport/stats',
|
||||
method: 'get',
|
||||
params
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 水电趋势分析
|
||||
* @param params 查询参数
|
||||
*/
|
||||
export const getTrend = (params: any) => {
|
||||
return request({
|
||||
url: '/stuwork/watermonthreport/trend',
|
||||
method: 'get',
|
||||
params
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 按楼层统计水电
|
||||
* @param params 查询参数
|
||||
*/
|
||||
export const getFloorStats = (params: any) => {
|
||||
return request({
|
||||
url: '/stuwork/watermonthreport/floorStats',
|
||||
method: 'get',
|
||||
params
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -69,23 +69,30 @@
|
||||
水费明细列表
|
||||
</span>
|
||||
<div class="header-actions">
|
||||
<el-button
|
||||
icon="FolderAdd"
|
||||
type="primary"
|
||||
<el-button
|
||||
icon="FolderAdd"
|
||||
type="primary"
|
||||
@click="formDialogRef.openDialog()">
|
||||
新增明细
|
||||
</el-button>
|
||||
<el-button
|
||||
icon="Download"
|
||||
type="success"
|
||||
class="ml10"
|
||||
<el-button
|
||||
icon="DataAnalysis"
|
||||
type="warning"
|
||||
class="ml10"
|
||||
@click="handleStats">
|
||||
统计分析
|
||||
</el-button>
|
||||
<el-button
|
||||
icon="Download"
|
||||
type="success"
|
||||
class="ml10"
|
||||
@click="handleExport">
|
||||
导出数据
|
||||
</el-button>
|
||||
<el-button
|
||||
icon="Setting"
|
||||
type="warning"
|
||||
class="ml10"
|
||||
<el-button
|
||||
icon="Setting"
|
||||
type="info"
|
||||
class="ml10"
|
||||
@click="handleInitWaterOrder">
|
||||
批量初始化订单
|
||||
</el-button>
|
||||
@@ -235,6 +242,9 @@
|
||||
<!-- 详情对话框 -->
|
||||
<DetailDialog ref="detailDialogRef" />
|
||||
|
||||
<!-- 统计分析对话框 -->
|
||||
<StatsDialog ref="statsDialogRef" />
|
||||
|
||||
<!-- 批量初始化对话框-->
|
||||
<el-dialog v-model="initDialogVisible" title="批量初始化订单" :width="500" :close-on-click-modal="false" draggable>
|
||||
<el-form ref="initFormRef" :model="initForm" :rules="initRules" label-width="120px">
|
||||
@@ -259,7 +269,7 @@
|
||||
|
||||
<script setup lang="ts" name="WaterDetail">
|
||||
import { reactive, ref, onMounted, computed, nextTick } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { BasicTableProps, useTable } from "/@/hooks/table";
|
||||
import { fetchList, delObjs, initWaterOrder } from "/@/api/stuwork/waterdetail";
|
||||
import { getBuildingList } from "/@/api/stuwork/dormbuilding";
|
||||
@@ -267,16 +277,19 @@ import { useMessage, useMessageBox } from "/@/hooks/message";
|
||||
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
||||
import FormDialog from './form.vue';
|
||||
import DetailDialog from './detail.vue';
|
||||
import { List, OfficeBuilding, House, UserFilled, Money, Setting, Menu, Search, Document } from '@element-plus/icons-vue'
|
||||
import StatsDialog from './stats.vue';
|
||||
import { List, OfficeBuilding, House, UserFilled, Money, Setting, Menu, Search, Document, DataAnalysis } from '@element-plus/icons-vue'
|
||||
import { useTableColumnControl } from '/@/hooks/tableColumn'
|
||||
|
||||
|
||||
// 定义变量
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const searchFormRef = ref()
|
||||
const columnControlRef = ref<any>()
|
||||
const formDialogRef = ref()
|
||||
const detailDialogRef = ref()
|
||||
const statsDialogRef = ref()
|
||||
const initFormRef = ref()
|
||||
const showSearch = ref(true)
|
||||
const buildingList = ref<any[]>([])
|
||||
@@ -402,6 +415,11 @@ const handleExport = () => {
|
||||
useMessage().warning('功能开发中')
|
||||
}
|
||||
|
||||
// 统计分析
|
||||
const handleStats = () => {
|
||||
statsDialogRef.value.openDialog()
|
||||
}
|
||||
|
||||
// 批量初始化订单
|
||||
const handleInitWaterOrder = () => {
|
||||
initDialogVisible.value = true
|
||||
|
||||
495
src/views/stuwork/waterdetail/stats.vue
Normal file
495
src/views/stuwork/waterdetail/stats.vue
Normal file
@@ -0,0 +1,495 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
v-model="visible"
|
||||
title="水电统计分析"
|
||||
width="90%"
|
||||
:close-on-click-modal="false"
|
||||
draggable
|
||||
destroy-on-close
|
||||
@open="handleOpen"
|
||||
@closed="handleClosed">
|
||||
|
||||
<!-- 查询条件 -->
|
||||
<div class="query-bar">
|
||||
<el-form :inline="true" class="query-form">
|
||||
<el-form-item label="年份">
|
||||
<el-date-picker
|
||||
v-model="queryParams.year"
|
||||
type="year"
|
||||
placeholder="选择年份"
|
||||
format="YYYY"
|
||||
value-format="YYYY"
|
||||
style="width: 120px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="月份">
|
||||
<el-select v-model="queryParams.month" placeholder="选择月份" clearable style="width: 100px">
|
||||
<el-option v-for="m in 12" :key="m" :label="m + '月'" :value="m" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">查询</el-button>
|
||||
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<!-- 统计卡片 -->
|
||||
<el-row :gutter="16" class="stats-row">
|
||||
<el-col :span="6">
|
||||
<div class="stats-card water">
|
||||
<el-icon class="stats-icon"><Odometer /></el-icon>
|
||||
<div class="stats-info">
|
||||
<div class="stats-label">总用水量</div>
|
||||
<div class="stats-value">{{ statsData.totalWaterUsage || 0 }} <span class="unit">m³</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<div class="stats-card water-cost">
|
||||
<el-icon class="stats-icon"><Money /></el-icon>
|
||||
<div class="stats-info">
|
||||
<div class="stats-label">用水费用</div>
|
||||
<div class="stats-value">¥{{ formatMoney(statsData.totalWaterCost) }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<div class="stats-card electricity">
|
||||
<el-icon class="stats-icon"><Odometer /></el-icon>
|
||||
<div class="stats-info">
|
||||
<div class="stats-label">总用电量</div>
|
||||
<div class="stats-value">{{ statsData.totalElectricityUsage || 0 }} <span class="unit">度</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<div class="stats-card electricity-cost">
|
||||
<el-icon class="stats-icon"><Money /></el-icon>
|
||||
<div class="stats-info">
|
||||
<div class="stats-label">用电费用</div>
|
||||
<div class="stats-value">¥{{ formatMoney(statsData.totalElectricityCost) }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 图表区域 -->
|
||||
<el-row :gutter="16" class="chart-row">
|
||||
<el-col :span="12">
|
||||
<el-card shadow="never" class="chart-card">
|
||||
<template #header>
|
||||
<span class="chart-title">水电用量趋势</span>
|
||||
</template>
|
||||
<div ref="trendChartRef" class="chart-container"></div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-card shadow="never" class="chart-card">
|
||||
<template #header>
|
||||
<span class="chart-title">楼层用水用电分布</span>
|
||||
</template>
|
||||
<div ref="floorChartRef" class="chart-container"></div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 楼层统计表格 -->
|
||||
<el-card shadow="never" class="table-card">
|
||||
<template #header>
|
||||
<span class="chart-title">楼层水电统计明细</span>
|
||||
</template>
|
||||
<el-table :data="floorStatsData" v-loading="floorLoading" stripe border size="small" max-height="250">
|
||||
<el-table-column type="index" label="序号" width="60" align="center" />
|
||||
<el-table-column prop="floorName" label="楼层" align="center" width="80" />
|
||||
<el-table-column prop="waterUsage" label="用水量(m³)" align="center">
|
||||
<template #default="scope">
|
||||
<span class="water-text">{{ formatNumber(scope.row.waterUsage) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="waterCost" label="用水费用(元)" align="center">
|
||||
<template #default="scope">
|
||||
<span class="water-text">¥{{ formatMoney(scope.row.waterCost) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="electricityUsage" label="用电量(度)" align="center">
|
||||
<template #default="scope">
|
||||
<span class="electricity-text">{{ formatNumber(scope.row.electricityUsage) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="electricityCost" label="用电费用(元)" align="center">
|
||||
<template #default="scope">
|
||||
<span class="electricity-text">¥{{ formatMoney(scope.row.electricityCost) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
|
||||
<template #footer>
|
||||
<el-button @click="visible = false">关闭</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, nextTick } from 'vue'
|
||||
import { getStats, getTrend, getFloorStats } from '/@/api/stuwork/watermonthreport'
|
||||
import { useMessage } from '/@/hooks/message'
|
||||
import { Odometer, Money } from '@element-plus/icons-vue'
|
||||
import * as echarts from 'echarts'
|
||||
|
||||
const visible = ref(false)
|
||||
|
||||
// 查询参数
|
||||
const queryParams = reactive({
|
||||
year: new Date().getFullYear().toString(),
|
||||
month: new Date().getMonth() + 1
|
||||
})
|
||||
|
||||
// 统计数据
|
||||
const statsData = ref<any>({})
|
||||
const trendData = ref<any[]>([])
|
||||
const floorStatsData = ref<any[]>([])
|
||||
const floorLoading = ref(false)
|
||||
|
||||
// 图表引用
|
||||
const trendChartRef = ref<HTMLElement>()
|
||||
const floorChartRef = ref<HTMLElement>()
|
||||
let trendChart: echarts.ECharts | null = null
|
||||
let floorChart: echarts.ECharts | null = null
|
||||
|
||||
// 打开弹窗
|
||||
const openDialog = () => {
|
||||
visible.value = true
|
||||
}
|
||||
|
||||
// 格式化金额
|
||||
const formatMoney = (value: any) => {
|
||||
if (!value) return '0.00'
|
||||
return Number(value).toFixed(2)
|
||||
}
|
||||
|
||||
// 格式化数字
|
||||
const formatNumber = (value: any) => {
|
||||
if (!value) return '0'
|
||||
return Number(value).toFixed(2)
|
||||
}
|
||||
|
||||
// 弹窗打开时
|
||||
const handleOpen = () => {
|
||||
resetQuery()
|
||||
nextTick(() => {
|
||||
handleQuery()
|
||||
})
|
||||
}
|
||||
|
||||
// 弹窗关闭时
|
||||
const handleClosed = () => {
|
||||
trendChart?.dispose()
|
||||
floorChart?.dispose()
|
||||
trendChart = null
|
||||
floorChart = null
|
||||
}
|
||||
|
||||
// 重置查询参数
|
||||
const resetQuery = () => {
|
||||
queryParams.year = new Date().getFullYear().toString()
|
||||
queryParams.month = new Date().getMonth() + 1
|
||||
}
|
||||
|
||||
// 查询统计数据
|
||||
const handleQuery = async () => {
|
||||
await Promise.all([
|
||||
getStatsData(),
|
||||
getTrendData(),
|
||||
getFloorData()
|
||||
])
|
||||
}
|
||||
|
||||
// 重置查询
|
||||
const handleReset = () => {
|
||||
resetQuery()
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
// 获取统计数据
|
||||
const getStatsData = async () => {
|
||||
try {
|
||||
const res = await getStats({
|
||||
year: queryParams.year,
|
||||
month: queryParams.month,
|
||||
statsType: 'school'
|
||||
})
|
||||
if (res.data) {
|
||||
statsData.value = res.data
|
||||
}
|
||||
} catch (err: any) {
|
||||
useMessage().error(err.msg || '获取统计数据失败')
|
||||
}
|
||||
}
|
||||
|
||||
// 获取趋势数据
|
||||
const getTrendData = async () => {
|
||||
try {
|
||||
const res = await getTrend({
|
||||
year: queryParams.year
|
||||
})
|
||||
if (res.data && res.data.trendList) {
|
||||
trendData.value = res.data.trendList
|
||||
nextTick(() => renderTrendChart())
|
||||
}
|
||||
} catch (err: any) {
|
||||
useMessage().error(err.msg || '获取趋势数据失败')
|
||||
}
|
||||
}
|
||||
|
||||
// 获取楼层数据
|
||||
const getFloorData = async () => {
|
||||
floorLoading.value = true
|
||||
try {
|
||||
const res = await getFloorStats({
|
||||
year: queryParams.year,
|
||||
month: queryParams.month
|
||||
})
|
||||
if (res.data) {
|
||||
floorStatsData.value = res.data
|
||||
nextTick(() => renderFloorChart())
|
||||
}
|
||||
} catch (err: any) {
|
||||
useMessage().error(err.msg || '获取楼层数据失败')
|
||||
} finally {
|
||||
floorLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 渲染趋势图表
|
||||
const renderTrendChart = () => {
|
||||
if (!trendChartRef.value) return
|
||||
|
||||
if (trendChart) {
|
||||
trendChart.dispose()
|
||||
}
|
||||
trendChart = echarts.init(trendChartRef.value)
|
||||
|
||||
const option = {
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: { type: 'cross' }
|
||||
},
|
||||
legend: {
|
||||
data: ['用水量', '用电量'],
|
||||
top: 0
|
||||
},
|
||||
grid: {
|
||||
left: '3%',
|
||||
right: '4%',
|
||||
bottom: '3%',
|
||||
top: 40,
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: trendData.value.map(d => d.timeLabel)
|
||||
},
|
||||
yAxis: [
|
||||
{
|
||||
type: 'value',
|
||||
name: '用水量(m³)',
|
||||
position: 'left'
|
||||
},
|
||||
{
|
||||
type: 'value',
|
||||
name: '用电量(度)',
|
||||
position: 'right'
|
||||
}
|
||||
],
|
||||
series: [
|
||||
{
|
||||
name: '用水量',
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
itemStyle: { color: '#409eff' },
|
||||
areaStyle: { color: 'rgba(64, 158, 255, 0.2)' },
|
||||
data: trendData.value.map(d => d.waterUsage)
|
||||
},
|
||||
{
|
||||
name: '用电量',
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
yAxisIndex: 1,
|
||||
itemStyle: { color: '#f56c6c' },
|
||||
areaStyle: { color: 'rgba(245, 108, 108, 0.2)' },
|
||||
data: trendData.value.map(d => d.electricityUsage)
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
trendChart.setOption(option)
|
||||
}
|
||||
|
||||
// 渲染楼层图表
|
||||
const renderFloorChart = () => {
|
||||
if (!floorChartRef.value) return
|
||||
|
||||
if (floorChart) {
|
||||
floorChart.dispose()
|
||||
}
|
||||
floorChart = echarts.init(floorChartRef.value)
|
||||
|
||||
const option = {
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: { type: 'shadow' }
|
||||
},
|
||||
legend: {
|
||||
data: ['用水量', '用电量'],
|
||||
top: 0
|
||||
},
|
||||
grid: {
|
||||
left: '3%',
|
||||
right: '4%',
|
||||
bottom: '3%',
|
||||
top: 40,
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: floorStatsData.value.map(d => d.floorName)
|
||||
},
|
||||
yAxis: [
|
||||
{
|
||||
type: 'value',
|
||||
name: '用水量(m³)'
|
||||
},
|
||||
{
|
||||
type: 'value',
|
||||
name: '用电量(度)',
|
||||
position: 'right'
|
||||
}
|
||||
],
|
||||
series: [
|
||||
{
|
||||
name: '用水量',
|
||||
type: 'bar',
|
||||
itemStyle: { color: '#409eff' },
|
||||
data: floorStatsData.value.map(d => d.waterUsage)
|
||||
},
|
||||
{
|
||||
name: '用电量',
|
||||
type: 'bar',
|
||||
yAxisIndex: 1,
|
||||
itemStyle: { color: '#f56c6c' },
|
||||
data: floorStatsData.value.map(d => d.electricityUsage)
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
floorChart.setOption(option)
|
||||
}
|
||||
|
||||
// 暴露方法
|
||||
defineExpose({
|
||||
openDialog
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.query-bar {
|
||||
margin-bottom: 16px;
|
||||
padding: 12px 16px;
|
||||
background: #f5f7fa;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.stats-row {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.stats-card {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 16px;
|
||||
border-radius: 8px;
|
||||
background: linear-gradient(135deg, #f5f7fa 0%, #e4e7ed 100%);
|
||||
|
||||
.stats-icon {
|
||||
font-size: 36px;
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
.stats-info {
|
||||
flex: 1;
|
||||
|
||||
.stats-label {
|
||||
font-size: 13px;
|
||||
color: #909399;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.stats-value {
|
||||
font-size: 22px;
|
||||
font-weight: bold;
|
||||
|
||||
.unit {
|
||||
font-size: 12px;
|
||||
font-weight: normal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.water {
|
||||
background: linear-gradient(135deg, #ecf5ff 0%, #d9ecff 100%);
|
||||
.stats-icon { color: #409eff; }
|
||||
.stats-value { color: #409eff; }
|
||||
}
|
||||
|
||||
&.water-cost {
|
||||
background: linear-gradient(135deg, #f0f9eb 0%, #e1f3d8 100%);
|
||||
.stats-icon { color: #67c23a; }
|
||||
.stats-value { color: #67c23a; }
|
||||
}
|
||||
|
||||
&.electricity {
|
||||
background: linear-gradient(135deg, #fdf6ec 0%, #faecd8 100%);
|
||||
.stats-icon { color: #e6a23c; }
|
||||
.stats-value { color: #e6a23c; }
|
||||
}
|
||||
|
||||
&.electricity-cost {
|
||||
background: linear-gradient(135deg, #fef0f0 0%, #fde2e2 100%);
|
||||
.stats-icon { color: #f56c6c; }
|
||||
.stats-value { color: #f56c6c; }
|
||||
}
|
||||
}
|
||||
|
||||
.chart-row {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.chart-card {
|
||||
.chart-title {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.chart-container {
|
||||
height: 280px;
|
||||
}
|
||||
}
|
||||
|
||||
.table-card {
|
||||
.chart-title {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
.water-text {
|
||||
color: #409eff;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.electricity-text {
|
||||
color: #f56c6c;
|
||||
font-weight: bold;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user