|
|
@@ -1,49 +1,162 @@
|
|
|
<template>
|
|
|
- <div class="app-container">
|
|
|
- <el-row :gutter="20">
|
|
|
- <el-col :span="24">
|
|
|
- <el-card shadow="hover" style="margin-bottom:20px">
|
|
|
- <template #header><span>岸线功能区统计</span></template>
|
|
|
- <el-table v-loading="sLoading" :data="shoreData" border stripe size="small">
|
|
|
- <el-table-column prop="TYPE" label="类别" width="120" />
|
|
|
- <el-table-column prop="BELONG" label="所属" width="150" />
|
|
|
- <el-table-column prop="LINELENG" label="岸线长度(km)" width="140" />
|
|
|
- <el-table-column prop="LINEGN" label="功能区长度(km)" width="140" />
|
|
|
- <el-table-column prop="COUNT" label="功能区数量" width="120" />
|
|
|
+ <div class="syax-page" v-loading="loading">
|
|
|
+ <!-- 标题 -->
|
|
|
+ <div class="page-header">
|
|
|
+ <h1>水域岸线功能区统计</h1>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 数据总览卡片 -->
|
|
|
+ <div class="overview-cards">
|
|
|
+ <div class="card" v-for="rv in overview" :key="rv.name">
|
|
|
+ <div class="card-name">{{ rv.name }}</div>
|
|
|
+ <div class="card-value">{{ rv.total }} <span class="card-unit">km</span></div>
|
|
|
+ <div class="card-sub">{{ rv.zones }} 个功能区</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 详细表格 + 图表区 -->
|
|
|
+ <div class="content-grid">
|
|
|
+ <!-- 表格 -->
|
|
|
+ <div class="grid-left">
|
|
|
+ <el-card shadow="never">
|
|
|
+ <template #header>岸线数据明细</template>
|
|
|
+ <el-table :data="tableData" border size="small" :span-method="spanMethod">
|
|
|
+ <el-table-column label="河(湖)名称" prop="river" width="90" />
|
|
|
+ <el-table-column label="总长度(km)" prop="lineLeng" width="100" />
|
|
|
+ <el-table-column label="功能区" prop="zone" width="100" />
|
|
|
+ <el-table-column label="功能区长度(km)" prop="zoneLeng" />
|
|
|
+ <el-table-column label="功能区数量" prop="zoneCount" width="100" />
|
|
|
</el-table>
|
|
|
</el-card>
|
|
|
- </el-col>
|
|
|
- <el-col :span="24">
|
|
|
- <el-card shadow="hover">
|
|
|
- <template #header><span>河湖长制工作制度</span></template>
|
|
|
- <el-table v-loading="wLoading" :data="wpList" border size="small">
|
|
|
- <el-table-column label="方案编号" prop="wpCode" width="160" />
|
|
|
- <el-table-column label="方案名称" prop="wpName" show-overflow-tooltip />
|
|
|
- <el-table-column label="文号" prop="fileNum" width="140" />
|
|
|
- <el-table-column label="发布日期" prop="rlsTm" width="120" />
|
|
|
- <el-table-column label="发布单位" prop="releWiunName" width="180" />
|
|
|
- </el-table>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 图表 -->
|
|
|
+ <div class="grid-right">
|
|
|
+ <el-card shadow="never">
|
|
|
+ <template #header>太湖 — 功能区分布</template>
|
|
|
+ <div class="chart-row"><div ref="thBar" class="chart-box" /><div ref="thPie" class="chart-box" /></div>
|
|
|
+ </el-card>
|
|
|
+ <el-card shadow="never">
|
|
|
+ <template #header>望虞河 — 功能区分布</template>
|
|
|
+ <div class="chart-row"><div ref="wyBar" class="chart-box" /><div ref="wyPie" class="chart-box" /></div>
|
|
|
</el-card>
|
|
|
- </el-col>
|
|
|
- </el-row>
|
|
|
+ <el-card shadow="never">
|
|
|
+ <template #header>太浦河 — 功能区分布</template>
|
|
|
+ <div class="chart-row"><div ref="tpBar" class="chart-box" /><div ref="tpPie" class="chart-box" /></div>
|
|
|
+ </el-card>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
<script setup>
|
|
|
-import { ref, onMounted } from 'vue'
|
|
|
+import { ref, onMounted, nextTick } from 'vue'
|
|
|
import { getShorelineStats } from '@/api/hzz/shoreline'
|
|
|
-import { listWorkPlan } from '@/api/hzz/workplan'
|
|
|
+import * as echarts from 'echarts'
|
|
|
+
|
|
|
+const loading = ref(false)
|
|
|
+const rawData = ref([])
|
|
|
+const thBar = ref(null), thPie = ref(null), wyBar = ref(null), wyPie = ref(null), tpBar = ref(null), tpPie = ref(null)
|
|
|
+const refMap = { thBar, thPie, wyBar, wyPie, tpBar, tpPie }
|
|
|
+
|
|
|
+const zones = ['保护区', '保留区', '控制利用区']
|
|
|
+const tableData = ref([])
|
|
|
+const overview = ref([])
|
|
|
+
|
|
|
+const spanMethod = ({ columnIndex, rowIndex }) => {
|
|
|
+ if (columnIndex <= 1) {
|
|
|
+ const rows = tableData.value
|
|
|
+ for (let i = rowIndex - 1; i >= 0; i--) { if (rows[i].river === rows[rowIndex].river) return { rowspan: 0, colspan: 1 } }
|
|
|
+ let cnt = 1; for (let i = rowIndex + 1; i < rows.length && rows[i].river === rows[rowIndex].river; i++) cnt++
|
|
|
+ return { rowspan: cnt, colspan: 1 }
|
|
|
+ }
|
|
|
+}
|
|
|
|
|
|
-const sLoading = ref(false), shoreData = ref([])
|
|
|
-const wLoading = ref(false), wpList = ref([])
|
|
|
+const rivers = [
|
|
|
+ { name: '太湖', key: 'th', indices: [0,1,2], chartRefs: { bar: 'thBar', pie: 'thPie' } },
|
|
|
+ { name: '望虞河', key: 'wy', indices: [3,4,5], chartRefs: { bar: 'wyBar', pie: 'wyPie' } },
|
|
|
+ { name: '太浦河', key: 'tp', indices: [6,8], chartRefs: { bar: 'tpBar', pie: 'tpPie' } },
|
|
|
+]
|
|
|
+
|
|
|
+const buildTable = () => {
|
|
|
+ const d = rawData.value
|
|
|
+ tableData.value = [
|
|
|
+ { river: '太湖', lineLeng: d[0]?.lineLeng || '-', zone: '保护区', zoneLeng: d[0]?.lineGn || '-', zoneCount: d[0]?.count || '-' },
|
|
|
+ { river: '太湖', lineLeng: '', zone: '保留区', zoneLeng: d[1]?.lineGn || '-', zoneCount: d[1]?.count || '-' },
|
|
|
+ { river: '太湖', lineLeng: '', zone: '控制利用区', zoneLeng: d[2]?.lineGn || '-', zoneCount: d[2]?.count || '-' },
|
|
|
+ { river: '望虞河', lineLeng: d[3]?.lineLeng || '-', zone: '保护区', zoneLeng: d[3]?.lineGn || '-', zoneCount: d[3]?.count || '-' },
|
|
|
+ { river: '望虞河', lineLeng: '', zone: '保留区', zoneLeng: d[4]?.lineGn || '-', zoneCount: d[4]?.count || '-' },
|
|
|
+ { river: '望虞河', lineLeng: '', zone: '控制利用区', zoneLeng: d[5]?.lineGn || '-', zoneCount: d[5]?.count || '-' },
|
|
|
+ { river: '太浦河', lineLeng: d[6]?.lineLeng || '-', zone: '保护区', zoneLeng: d[6]?.lineGn || '-', zoneCount: d[6]?.count || '-' },
|
|
|
+ { river: '太浦河', lineLeng: '', zone: '控制利用区', zoneLeng: d[8]?.lineGn || '-', zoneCount: d[8]?.count || '-' },
|
|
|
+ ]
|
|
|
+ overview.value = [
|
|
|
+ { name: '太湖', total: d[0]?.lineLeng || '-', zones: 3 },
|
|
|
+ { name: '望虞河', total: d[3]?.lineLeng || '-', zones: 3 },
|
|
|
+ { name: '太浦河', total: d[6]?.lineLeng || '-', zones: 2 },
|
|
|
+ ]
|
|
|
+}
|
|
|
+
|
|
|
+const renderCharts = () => {
|
|
|
+ const d = rawData.value
|
|
|
+ nextTick(() => {
|
|
|
+ rivers.forEach(rv => {
|
|
|
+ const vals = rv.indices.map(i => Number(d[i]?.lineGn || 0))
|
|
|
+ const names = rv.indices.map(i => zones[i === 8 ? 2 : i === 6 && rv.key === 'tp' ? 0 : rv.indices.indexOf(i)])
|
|
|
+ const labels = rv.key === 'tp' ? ['保护区', '控制利用区'] : zones
|
|
|
+
|
|
|
+ const makeChart = (r, type) => {
|
|
|
+ const el = refMap[rv.key + (type === 'bar' ? 'Bar' : 'Pie')].value
|
|
|
+ if (!el) return
|
|
|
+ const c = echarts.init(el)
|
|
|
+ c.setOption(type === 'bar' ? {
|
|
|
+ grid: { top: 20, bottom: 30, left: 40, right: 20 },
|
|
|
+ xAxis: { type: 'category', data: labels }, yAxis: { type: 'value', name: 'km' },
|
|
|
+ series: [{ type: 'bar', data: vals, itemStyle: { color: '#409EFF' }, barMaxWidth: 50 }]
|
|
|
+ } : {
|
|
|
+ series: [{ type: 'pie', radius: ['45%','70%'], center: ['50%','55%'], data: labels.map((n,i)=>({name:n,value:vals[i]})), label: { formatter: '{b}\n{d}%', fontSize: 12 } }]
|
|
|
+ })
|
|
|
+ window.addEventListener('resize', () => c.resize())
|
|
|
+ }
|
|
|
+ makeChart(rv, 'bar')
|
|
|
+ makeChart(rv, 'pie')
|
|
|
+ })
|
|
|
+ })
|
|
|
+}
|
|
|
|
|
|
onMounted(async () => {
|
|
|
- sLoading.value = true; wLoading.value = true
|
|
|
+ loading.value = true
|
|
|
try {
|
|
|
- const [sr, wr] = await Promise.all([getShorelineStats(), listWorkPlan('')])
|
|
|
- shoreData.value = sr.data || []
|
|
|
- wpList.value = wr.data || []
|
|
|
- } catch { /* ok */ }
|
|
|
- finally { sLoading.value = false; wLoading.value = false }
|
|
|
+ const res = await getShorelineStats()
|
|
|
+ rawData.value = res.data || []
|
|
|
+ buildTable()
|
|
|
+ renderCharts()
|
|
|
+ } finally { loading.value = false }
|
|
|
})
|
|
|
</script>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+.syax-page { padding: 20px 24px; overflow-y: auto; height: calc(100vh - 84px); background: #f0f2f5; }
|
|
|
+.page-header { margin-bottom: 20px; }
|
|
|
+.page-header h1 { font-size: 20px; color: #1a1a2e; font-weight: 600; margin: 0; }
|
|
|
+.page-header h1::before { content: ''; display: inline-block; width: 4px; height: 20px; background: #409EFF; margin-right: 10px; vertical-align: middle; border-radius: 2px; }
|
|
|
+
|
|
|
+/* 总览卡片 */
|
|
|
+.overview-cards { display: flex; gap: 16px; margin-bottom: 20px; }
|
|
|
+.card { flex: 1; background: #fff; border-radius: 8px; padding: 20px; text-align: center; box-shadow: 0 1px 4px rgba(0,0,0,.06); }
|
|
|
+.card-name { font-size: 14px; color: #666; margin-bottom: 8px; }
|
|
|
+.card-value { font-size: 28px; font-weight: 700; color: #0d4b80; }
|
|
|
+.card-unit { font-size: 13px; font-weight: 400; color: #999; }
|
|
|
+.card-sub { font-size: 12px; color: #999; margin-top: 4px; }
|
|
|
+
|
|
|
+/* 内容区 */
|
|
|
+.content-grid { display: flex; gap: 16px; align-items: flex-start; }
|
|
|
+.grid-left { width: 520px; flex-shrink: 0; }
|
|
|
+.grid-right { flex: 1; display: flex; flex-direction: column; gap: 16px; min-width: 0; }
|
|
|
+
|
|
|
+.chart-row { display: flex; gap: 12px; }
|
|
|
+.chart-box { flex: 1; height: 240px; }
|
|
|
+
|
|
|
+:deep(.el-card__header) { font-size: 14px; font-weight: 600; color: #333; padding: 12px 16px; }
|
|
|
+:deep(.el-card__body) { padding: 12px 16px; }
|
|
|
+</style>
|