Browse Source

数据看板页面完善

qw 1 week ago
parent
commit
5b260d6853
3 changed files with 452 additions and 11 deletions
  1. 19 0
      package-lock.json
  2. 1 0
      package.json
  3. 432 11
      src/views/User/shuJuKanBan/shuJuKanBan.vue

+ 19 - 0
package-lock.json

@@ -12,6 +12,7 @@
         "@wangeditor/editor": "^5.1.23",
         "@wangeditor/editor-for-vue": "^5.1.12",
         "axios": "^1.7.9",
+        "chart.js": "^4.4.9",
         "dom-to-image": "^2.6.0",
         "echarts": "^5.6.0",
         "echarts-gl": "^2.0.9",
@@ -1385,6 +1386,12 @@
         "@jridgewell/sourcemap-codec": "^1.4.14"
       }
     },
+    "node_modules/@kurkle/color": {
+      "version": "0.3.4",
+      "resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.4.tgz",
+      "integrity": "sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==",
+      "license": "MIT"
+    },
     "node_modules/@nodelib/fs.scandir": {
       "version": "2.1.5",
       "resolved": "https://registry.npmmirror.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@@ -3156,6 +3163,18 @@
         "node": ">=12"
       }
     },
+    "node_modules/chart.js": {
+      "version": "4.4.9",
+      "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.9.tgz",
+      "integrity": "sha512-EyZ9wWKgpAU0fLJ43YAEIF8sr5F2W3LqbS40ZJyHIner2lY14ufqv2VMp69MAiZ2rpwxEUxEhIH/0U3xyRynxg==",
+      "license": "MIT",
+      "dependencies": {
+        "@kurkle/color": "^0.3.0"
+      },
+      "engines": {
+        "pnpm": ">=8"
+      }
+    },
     "node_modules/check-error": {
       "version": "2.1.1",
       "resolved": "https://registry.npmmirror.com/check-error/-/check-error-2.1.1.tgz",

+ 1 - 0
package.json

@@ -16,6 +16,7 @@
     "@wangeditor/editor": "^5.1.23",
     "@wangeditor/editor-for-vue": "^5.1.12",
     "axios": "^1.7.9",
+    "chart.js": "^4.4.9",
     "dom-to-image": "^2.6.0",
     "echarts": "^5.6.0",
     "echarts-gl": "^2.0.9",

+ 432 - 11
src/views/User/shuJuKanBan/shuJuKanBan.vue

@@ -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>