|
@@ -1,23 +1,444 @@
|
|
|
<template>
|
|
|
- <div class="">
|
|
|
-
|
|
|
+ <div class="dashboard-container">
|
|
|
+ <!-- 左上:耕地质量评估 -->
|
|
|
+ <div class="section chart-section">
|
|
|
+ <h2>耕地质量评估</h2>
|
|
|
+ <div class="chart-container">
|
|
|
+ <canvas ref="chartCanvas"></canvas>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 右上:风险阈值评估 -->
|
|
|
+ <div class="section risk-section">
|
|
|
+ <h2>作物安全生产风险阈值评估</h2>
|
|
|
+ <div class="table-container">
|
|
|
+ <table class="styled-table">
|
|
|
+ <thead>
|
|
|
+ <tr>
|
|
|
+ <th>地区</th>
|
|
|
+ <th>风险评估</th>
|
|
|
+ <th>污染等级</th>
|
|
|
+ <th>建议措施</th>
|
|
|
+ </tr>
|
|
|
+ </thead>
|
|
|
+ <tbody>
|
|
|
+ <tr v-for="(item, index) in riskData" :key="index">
|
|
|
+ <td>{{ item.area }}</td>
|
|
|
+ <td :class="['risk-level', 'risk-' + item.risk.toLowerCase()]">{{ item.risk }}</td>
|
|
|
+ <td :class="['pollution-level', 'level-' + item.level]">{{ item.level }}</td>
|
|
|
+ <td>{{ item.action }}</td>
|
|
|
+ </tr>
|
|
|
+ </tbody>
|
|
|
+ </table>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 左下:情景模拟 -->
|
|
|
+ <div class="section simulation-section">
|
|
|
+ <h2>情景模拟</h2>
|
|
|
+ <div class="simulation-grid">
|
|
|
+ <div v-for="item in simulationData" :key="item.name" class="simulation-item">
|
|
|
+ <div class="label-container">
|
|
|
+ <span class="label">{{ item.name }}</span>
|
|
|
+ <span class="value">{{ item.value }}%</span>
|
|
|
+ </div>
|
|
|
+ <div class="progress-container">
|
|
|
+ <div
|
|
|
+ class="progress-bar"
|
|
|
+ :style="{ width: item.value + '%', backgroundColor: item.color }"
|
|
|
+ ></div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 右下:数据报表 -->
|
|
|
+ <div class="section report-section">
|
|
|
+ <h2>数据统计报表</h2>
|
|
|
+ <div class="table-container">
|
|
|
+ <table class="styled-table">
|
|
|
+ <thead>
|
|
|
+ <tr>
|
|
|
+ <th>时间</th>
|
|
|
+ <th>地理位置</th>
|
|
|
+ <th>文件</th>
|
|
|
+ <th>操作</th>
|
|
|
+ </tr>
|
|
|
+ </thead>
|
|
|
+ <tbody>
|
|
|
+ <tr v-for="(item, index) in reportData" :key="index">
|
|
|
+ <td>{{ item.time }}</td>
|
|
|
+ <td>{{ item.location }}</td>
|
|
|
+ <td>{{ item.file }}</td>
|
|
|
+ <td><button class="download-btn">{{ item.operation }}</button></td>
|
|
|
+ </tr>
|
|
|
+ </tbody>
|
|
|
+ </table>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
<script>
|
|
|
+import { ref, onMounted, watch } from 'vue'
|
|
|
+import { Chart, DoughnutController, ArcElement, Tooltip, Legend } from 'chart.js'
|
|
|
+
|
|
|
+Chart.register(DoughnutController, ArcElement, Tooltip, Legend)
|
|
|
+
|
|
|
export default {
|
|
|
- name: '',
|
|
|
- data() {
|
|
|
- return {
|
|
|
+ setup() {
|
|
|
+ const chartCanvas = ref(null)
|
|
|
+ let chartInstance = null
|
|
|
+
|
|
|
+ // 初始化图表
|
|
|
+ const initChart = () => {
|
|
|
+ if (chartInstance) chartInstance.destroy()
|
|
|
|
|
|
- };
|
|
|
- },
|
|
|
- methods: {
|
|
|
-
|
|
|
+ const ctx = chartCanvas.value?.getContext('2d')
|
|
|
+ if (!ctx) return
|
|
|
+
|
|
|
+ // 图表配置
|
|
|
+ const chartConfig = {
|
|
|
+ labels: ['优先保护区', '安全封闭区', '严格管控区'],
|
|
|
+ data: [40, 35, 25], // 调整数据比例使图表更协调
|
|
|
+ colors: [
|
|
|
+ 'rgba(67, 160, 71, 0.8)', // 绿色 - 优先保护区
|
|
|
+ 'rgba(255, 193, 7, 0.8)', // 黄色 - 安全封闭区
|
|
|
+ 'rgba(229, 57, 53, 0.8)' // 红色 - 严格管控区
|
|
|
+ ],
|
|
|
+ borderColor: 'white',
|
|
|
+ borderWidth: 1,
|
|
|
+ cutout: '65%' // 设置圆环中间空洞大小,使图表更美观
|
|
|
+ }
|
|
|
+
|
|
|
+ chartInstance = new Chart(ctx, {
|
|
|
+ type: 'doughnut',
|
|
|
+ data: {
|
|
|
+ labels: chartConfig.labels,
|
|
|
+ datasets: [{
|
|
|
+ data: chartConfig.data,
|
|
|
+ backgroundColor: chartConfig.colors,
|
|
|
+ borderColor: chartConfig.borderColor,
|
|
|
+ borderWidth: chartConfig.borderWidth,
|
|
|
+ hoverOffset: 5
|
|
|
+ }]
|
|
|
+ },
|
|
|
+ options: {
|
|
|
+ responsive: true,
|
|
|
+ maintainAspectRatio: false,
|
|
|
+ plugins: {
|
|
|
+ legend: {
|
|
|
+ position: 'bottom',
|
|
|
+ labels: {
|
|
|
+ padding: 15,
|
|
|
+ font: { size: 13 },
|
|
|
+ usePointStyle: true,
|
|
|
+ pointStyle: 'circle'
|
|
|
+ }
|
|
|
+ },
|
|
|
+ tooltip: {
|
|
|
+ backgroundColor: 'rgba(0,0,0,0.7)',
|
|
|
+ bodyFont: { size: 14 },
|
|
|
+ padding: 10,
|
|
|
+ callbacks: {
|
|
|
+ label: ctx => `${ctx.label}: ${ctx.parsed}%`
|
|
|
+ }
|
|
|
+ },
|
|
|
+ title: {
|
|
|
+ display: true,
|
|
|
+ text: '耕地质量等级分布',
|
|
|
+ font: {
|
|
|
+ size: 16,
|
|
|
+ weight: 'bold'
|
|
|
+ },
|
|
|
+ padding: {
|
|
|
+ bottom: 15
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ animation: {
|
|
|
+ animateScale: true,
|
|
|
+ animateRotate: true,
|
|
|
+ duration: 1000, // 动画持续时间
|
|
|
+ easing: 'easeOutQuart' // 动画效果
|
|
|
+ },
|
|
|
+ elements: {
|
|
|
+ arc: {
|
|
|
+ borderWidth: 1 // 确保扇形边框宽度一致
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ // 风险评估数据
|
|
|
+ const riskData = ref([
|
|
|
+ { area: 'a县', risk: 'A', level: '低', action: '常规监测' },
|
|
|
+ { area: 'b县', risk: 'C', level: '中', action: '土壤修复' },
|
|
|
+ { area: 'c县', risk: 'D', level: '高', action: '严格管控' },
|
|
|
+ { area: 'd县', risk: 'A', level: '低', action: '常规监测' },
|
|
|
+ { area: 'e县', risk: 'B', level: '中', action: '优化管理' }
|
|
|
+ ])
|
|
|
+
|
|
|
+ // 情景模拟数据
|
|
|
+ const simulationData = ref([
|
|
|
+ { name: '养分含量', value: 65, color: '#43A047' },
|
|
|
+ { name: '重金属污染情况', value: 32, color: '#E53935' },
|
|
|
+ { name: '酸碱度(PH值)', value: 78, color: '#1E88E5' },
|
|
|
+ { name: '产量及品质', value: 82, color: '#8E24AA' },
|
|
|
+ { name: '病虫害率', value: 25, color: '#FB8C00' }
|
|
|
+ ])
|
|
|
+
|
|
|
+ // 数据报表数据
|
|
|
+ const reportData = ref([
|
|
|
+ { time: '2025-05', location: '广东省韶关市', file: '202505GZ0001', operation: '下载' },
|
|
|
+ { time: '2025-04', location: '广东省韶关市', file: '202504GZ0021', operation: '下载' },
|
|
|
+ { time: '2025-03', location: '广东省韶关市', file: '202503GZ0231', operation: '下载' },
|
|
|
+ { time: '2025-02', location: '广东省韶关市', file: '202502GZ1231', operation: '下载' }
|
|
|
+ ])
|
|
|
+
|
|
|
+ // 监听数据变化
|
|
|
+ watch([riskData, simulationData, reportData], () => {
|
|
|
+ initChart()
|
|
|
+ })
|
|
|
+
|
|
|
+ onMounted(() => {
|
|
|
+ initChart()
|
|
|
+
|
|
|
+ // 添加防抖处理,避免频繁触发图表重绘
|
|
|
+ let resizeTimeout
|
|
|
+ window.addEventListener('resize', () => {
|
|
|
+ clearTimeout(resizeTimeout)
|
|
|
+ resizeTimeout = setTimeout(initChart, 300)
|
|
|
+ })
|
|
|
+ })
|
|
|
+
|
|
|
+ return {
|
|
|
+ chartCanvas,
|
|
|
+ riskData,
|
|
|
+ simulationData,
|
|
|
+ reportData
|
|
|
+ }
|
|
|
}
|
|
|
-};
|
|
|
+}
|
|
|
</script>
|
|
|
|
|
|
<style scoped>
|
|
|
-
|
|
|
+.dashboard-container {
|
|
|
+ display: grid;
|
|
|
+ grid-template:
|
|
|
+ "chart risk" minmax(280px, 1fr)
|
|
|
+ "simulation report" minmax(280px, 1fr)
|
|
|
+ / 1fr 1fr;
|
|
|
+ gap: 18px;
|
|
|
+ padding: 20px;
|
|
|
+ max-width: 1600px;
|
|
|
+ margin: 0 auto;
|
|
|
+ min-height: 100vh;
|
|
|
+ background: #f5f7fa;
|
|
|
+}
|
|
|
+
|
|
|
+.section {
|
|
|
+ background: white;
|
|
|
+ border-radius: 15px;
|
|
|
+ padding: 18px;
|
|
|
+ box-shadow: 0 4px 15px rgba(0, 0, 0, 0.05);
|
|
|
+ transition: transform 0.3s ease, box-shadow 0.3s ease;
|
|
|
+}
|
|
|
+
|
|
|
+.section:hover {
|
|
|
+ transform: translateY(-2px);
|
|
|
+ box-shadow: 0 6px 20px rgba(0, 0, 0, 0.08);
|
|
|
+}
|
|
|
+
|
|
|
+.chart-section { grid-area: chart; }
|
|
|
+.risk-section { grid-area: risk; }
|
|
|
+.simulation-section { grid-area: simulation; }
|
|
|
+.report-section { grid-area: report; }
|
|
|
+
|
|
|
+h2 {
|
|
|
+ color: #2c3e50;
|
|
|
+ margin: 0 0 15px 0;
|
|
|
+ padding-bottom: 10px;
|
|
|
+ border-bottom: 2px solid #eee;
|
|
|
+ font-size: 1.3em;
|
|
|
+ font-weight: 600;
|
|
|
+}
|
|
|
+
|
|
|
+/* 图表容器 */
|
|
|
+.chart-container {
|
|
|
+ position: relative;
|
|
|
+ height: 220px;
|
|
|
+}
|
|
|
+
|
|
|
+/* 表格样式 */
|
|
|
+.styled-table {
|
|
|
+ width: 100%;
|
|
|
+ border-collapse: collapse;
|
|
|
+ font-size: 0.9em;
|
|
|
+ margin-top: 10px;
|
|
|
+}
|
|
|
+
|
|
|
+.styled-table td:last-child {
|
|
|
+ color: #666; /* 灰色,与白色背景形成对比 */
|
|
|
+}
|
|
|
+
|
|
|
+.styled-table th,
|
|
|
+.styled-table td {
|
|
|
+ padding: 10px 12px;
|
|
|
+ border: 1px solid #e0e0e0;
|
|
|
+ text-align: left;
|
|
|
+}
|
|
|
+
|
|
|
+.styled-table th {
|
|
|
+ background-color: #f8f9fa;
|
|
|
+ font-weight: 600;
|
|
|
+ color: #2c3e50;
|
|
|
+ font-size: 0.95em;
|
|
|
+}
|
|
|
+
|
|
|
+.styled-table tr:nth-child(even) {
|
|
|
+ background-color: #fafafa;
|
|
|
+}
|
|
|
+
|
|
|
+.styled-table tr:hover {
|
|
|
+ background-color: #f5f5f5;
|
|
|
+ transition: background-color 0.2s ease;
|
|
|
+}
|
|
|
+
|
|
|
+/* 风险等级颜色 */
|
|
|
+.risk-level {
|
|
|
+ font-weight: 600;
|
|
|
+ padding: 2px 6px;
|
|
|
+ border-radius: 4px;
|
|
|
+ display: inline-block;
|
|
|
+ font-size: 0.9em;
|
|
|
+}
|
|
|
+.risk-a { background-color: #e8f5e9; color: #2e7d32; }
|
|
|
+.risk-b { background-color: #f1f8e9; color: #558b2f; }
|
|
|
+.risk-c { background-color: #fff8e1; color: #f57c00; }
|
|
|
+.risk-d { background-color: #ffebee; color: #c62828; }
|
|
|
+
|
|
|
+.pollution-level {
|
|
|
+ font-weight: 500;
|
|
|
+ padding: 2px 6px;
|
|
|
+ border-radius: 4px;
|
|
|
+ display: inline-block;
|
|
|
+ font-size: 0.9em;
|
|
|
+}
|
|
|
+.level-低 { background-color: #e8f5e9; color: #2e7d32; }
|
|
|
+.level-中 { background-color: #fff8e1; color: #f57c00; }
|
|
|
+.level-高 { background-color: #ffebee; color: #c62828; }
|
|
|
+
|
|
|
+/* 情景模拟 */
|
|
|
+.simulation-grid {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ gap: 12px;
|
|
|
+ margin-top: 12px;
|
|
|
+}
|
|
|
+
|
|
|
+.simulation-item {
|
|
|
+ background: #f8f9fa;
|
|
|
+ border-radius: 8px;
|
|
|
+ padding: 10px;
|
|
|
+ transition: transform 0.2s ease;
|
|
|
+}
|
|
|
+
|
|
|
+.simulation-item:hover {
|
|
|
+ transform: translateX(2px);
|
|
|
+}
|
|
|
+
|
|
|
+.label-container {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ margin-bottom: 8px;
|
|
|
+ font-size: 0.95em;
|
|
|
+}
|
|
|
+
|
|
|
+.label {
|
|
|
+ color: #2c3e50;
|
|
|
+ font-weight: 500;
|
|
|
+}
|
|
|
+
|
|
|
+.value {
|
|
|
+ color: #666;
|
|
|
+ font-weight: 500;
|
|
|
+}
|
|
|
+
|
|
|
+.progress-container {
|
|
|
+ height: 18px;
|
|
|
+ background: #e9ecef;
|
|
|
+ border-radius: 9px;
|
|
|
+ overflow: hidden;
|
|
|
+ box-shadow: inset 0 1px 2px rgba(0,0,0,0.1);
|
|
|
+}
|
|
|
+
|
|
|
+.progress-bar {
|
|
|
+ height: 100%;
|
|
|
+ transition: width 0.8s ease-out;
|
|
|
+ box-shadow: inset 0 -1px 0 rgba(0,0,0,0.15);
|
|
|
+}
|
|
|
+
|
|
|
+/* 下载按钮 */
|
|
|
+.download-btn {
|
|
|
+ background: #43A047;
|
|
|
+ color: white;
|
|
|
+ border: none;
|
|
|
+ padding: 5px 10px;
|
|
|
+ border-radius: 4px;
|
|
|
+ cursor: pointer;
|
|
|
+ transition: all 0.3s;
|
|
|
+ font-size: 0.9em;
|
|
|
+}
|
|
|
+
|
|
|
+.download-btn:hover {
|
|
|
+ background: #2e7d32;
|
|
|
+ transform: translateY(-1px);
|
|
|
+ box-shadow: 0 2px 5px rgba(0,0,0,0.1);
|
|
|
+}
|
|
|
+
|
|
|
+/* 确保建议措施列的文本可见 */
|
|
|
+.styled-table td:last-child {
|
|
|
+ color: #333; /* 设置明确的文本颜色 */
|
|
|
+ word-break: break-word; /* 允许长文本换行 */
|
|
|
+ white-space: normal; /* 取消强制单行显示 */
|
|
|
+}
|
|
|
+
|
|
|
+/* 响应式布局 */
|
|
|
+@media (max-width: 1200px) {
|
|
|
+ .dashboard-container {
|
|
|
+ grid-template:
|
|
|
+ "chart"
|
|
|
+ "risk"
|
|
|
+ "simulation"
|
|
|
+ "report";
|
|
|
+ grid-template-columns: 1fr;
|
|
|
+ padding: 15px;
|
|
|
+ gap: 15px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .section {
|
|
|
+ min-height: auto;
|
|
|
+ }
|
|
|
+
|
|
|
+ .chart-container {
|
|
|
+ min-height: 300px;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+@media (max-width: 768px) {
|
|
|
+ h2 {
|
|
|
+ font-size: 1.1em;
|
|
|
+ }
|
|
|
+
|
|
|
+ .styled-table {
|
|
|
+ font-size: 0.8em;
|
|
|
+ }
|
|
|
+
|
|
|
+ .styled-table th,
|
|
|
+ .styled-table td {
|
|
|
+ padding: 8px 10px;
|
|
|
+ }
|
|
|
+}
|
|
|
</style>
|