dedsudiyu 1 день назад
Родитель
Сommit
4baad69b44
2 измененных файлов с 116 добавлено и 112 удалено
  1. 115 111
      src/components/LabStats/ChemicalsStats.vue
  2. 1 1
      src/components/LabStats/EquipmentStats.vue

+ 115 - 111
src/components/LabStats/ChemicalsStats.vue

@@ -2,66 +2,77 @@
   <div class="ChemicalsStats panel-box">
     <div class="corner-deco tl"></div><div class="corner-deco tr"></div>
     <div class="corner-deco bl"></div><div class="corner-deco br"></div>
-    <div class="panel-title">化学品库动态统计</div>
-    <div class="cards-grid">
-
-      <!-- 左上:存量总量 -->
-      <div class="chem-card">
-        <div class="card-icon">
-          <svg width="46" height="46" viewBox="0 0 46 46" fill="none">
-            <circle cx="23" cy="23" r="20" fill="rgba(72,215,255,0.1)" stroke="rgba(72,215,255,0.4)" stroke-width="1.5"/>
-            <!-- 倾斜试管,旋转-40度 -->
-            <g transform="rotate(-40,23,23)">
-              <rect x="19" y="9" width="8" height="18" rx="1.5" fill="rgba(72,215,255,0.15)" stroke="#48d7ff" stroke-width="1.5"/>
-              <path d="M19 27 Q19 35 23 35 Q27 35 27 27Z" fill="rgba(72,215,255,0.35)" stroke="#48d7ff" stroke-width="1.5"/>
-              <rect x="17" y="7" width="12" height="3" rx="1" fill="rgba(72,215,255,0.2)" stroke="#48d7ff" stroke-width="1.2"/>
-              <!-- 液体 -->
-              <rect x="19" y="22" width="8" height="5" fill="rgba(72,215,255,0.5)"/>
-              <path d="M19 27 Q19 35 23 35 Q27 35 27 27Z" fill="rgba(72,215,255,0.5)"/>
-            </g>
-          </svg>
+    <div class="panel-title">化学品库存动态统计</div>
+    <div class="chem-panel-body">
+      <div class="chem-stats-grid">
+
+        <!-- 左上:存量总量,全满青色圆环 + 斜杠图标 -->
+        <div class="chem-stat-item">
+          <div class="chem-ring-wrap">
+            <svg viewBox="0 0 44 44">
+              <g transform="rotate(-90 22 22)">
+                <circle class="ring-bg" cx="22" cy="22" r="17"/>
+                <circle class="ring-fg" cx="22" cy="22" r="17" stroke="#48d7ff"
+                  stroke-dasharray="106.8" stroke-dashoffset="0"/>
+              </g>
+              <text x="12" y="27" class="chem-ring-icon" fill="#48d7ff">&#x1F9EA;</text>
+            </svg>
+          </div>
+          <div class="chem-stat-info">
+            <div class="chem-stat-value" style="color:#48d7ff">{{ formatNum(stats.totalAmount) }}<span class="unit"> L</span></div>
+            <div class="chem-stat-label">存量化学品总量</div>
+          </div>
         </div>
-        <div class="card-body">
-          <div class="card-value cyan">{{ formatNum(stats.totalAmount) }}<span class="unit"> L</span></div>
-          <div class="card-label">存量化学品总量</div>
-        </div>
-      </div>
 
-      <!-- 右上:管控类 -->
-      <div class="chem-card card-red">
-        <div class="donut-pct orange">{{ controlledPct }}%</div>
-        <div class="donut-wrap" ref="chartControlled"></div>
-        <div class="card-body">
-          <div class="card-value orange">{{ formatNum(stats.controlledAmount) }}<span class="unit"> L</span></div>
-          <div class="card-label">管控类化学品</div>
+        <!-- 右上:管控类,橙红色圆环 + 百分比居中 -->
+        <div class="chem-stat-item item-red">
+          <div class="chem-ring-wrap">
+            <svg viewBox="0 0 44 44">
+              <g transform="rotate(-90 22 22)">
+                <circle class="ring-bg" cx="22" cy="22" r="17"/>
+                <circle class="ring-fg" cx="22" cy="22" r="17" stroke="#ff4d4f"
+                  stroke-dasharray="106.8" :stroke-dashoffset="calcOffset(controlledPct)"/>
+              </g>
+              <text x="22" y="25" class="ring-pct" fill="#ff4d4f">{{ controlledPct }}%</text>
+            </svg>
+          </div>
+          <div class="chem-stat-info">
+            <div class="chem-stat-value" style="color:#ff4d4f">{{ formatNum(stats.controlledAmount) }}<span class="unit"> L</span></div>
+            <div class="chem-stat-label">管控类化学品</div>
+          </div>
         </div>
-      </div>
 
-      <!-- 左下:非管控类 -->
-      <div class="chem-card card-green">
-        <div class="donut-pct green">{{ uncontrolledPct }}%</div>
-        <div class="donut-wrap" ref="chartUncontrolled"></div>
-        <div class="card-body">
-          <div class="card-value green">{{ formatNum(stats.uncontrolledAmount) }}<span class="unit"> L</span></div>
-          <div class="card-label">非管控类化学品</div>
+        <!-- 左下:非管控类,绿色圆环 + 百分比居中 -->
+        <div class="chem-stat-item item-green">
+          <div class="chem-ring-wrap">
+            <svg viewBox="0 0 44 44">
+              <g transform="rotate(-90 22 22)">
+                <circle class="ring-bg" cx="22" cy="22" r="17"/>
+                <circle class="ring-fg" cx="22" cy="22" r="17" stroke="#36d399"
+                  stroke-dasharray="106.8" :stroke-dashoffset="calcOffset(uncontrolledPct)"/>
+              </g>
+              <text x="22" y="25" class="ring-pct" fill="#36d399">{{ uncontrolledPct }}%</text>
+            </svg>
+          </div>
+          <div class="chem-stat-info">
+            <div class="chem-stat-value" style="color:#36d399">{{ formatNum(stats.uncontrolledAmount) }}<span class="unit"> L</span></div>
+            <div class="chem-stat-label">非管控类化学品</div>
+          </div>
         </div>
-      </div>
 
-      <!-- 右下:类目统计 -->
-      <div class="chem-card">
-        <div class="card-body center">
-          <div class="card-value yellow">{{ stats.totalCategories }}</div>
-          <div class="card-label">存量化学品总类目</div>
-          <div class="card-sub">管控{{ stats.controlledCategories }}类 / 非管控{{ stats.uncontrolledCategories }}类</div>
+        <!-- 右下:类目统计 -->
+        <div class="chem-stat-item item-center">
+          <div class="chem-stat-value" style="color:#ffb020;font-size:28px;">{{ stats.totalCategories }}</div>
+          <div class="chem-stat-label">存量化学品总类目</div>
+          <div class="chem-stat-sub">管控{{ stats.controlledCategories }}类 / 非管控{{ stats.uncontrolledCategories }}类</div>
         </div>
-      </div>
 
+      </div>
     </div>
   </div>
 </template>
 
 <script>
-import * as echarts from 'echarts'
 import { getChemicalStats } from '@/api'
 
 export default {
@@ -74,8 +85,6 @@ export default {
       },
       controlledPct: 0,
       uncontrolledPct: 0,
-      chartC: null,
-      chartU: null,
       timer: null
     }
   },
@@ -84,12 +93,14 @@ export default {
     this.timer = setInterval(this.loadData, 5 * 60 * 1000)
   },
   beforeDestroy() {
-    if (this.chartC) this.chartC.dispose()
-    if (this.chartU) this.chartU.dispose()
     clearInterval(this.timer)
   },
   methods: {
     formatNum(n) { return (n || 0).toLocaleString() },
+    // r=17, circumference = 2*π*17 ≈ 106.8
+    calcOffset(pct) {
+      return (106.8 * (1 - pct / 100)).toFixed(1)
+    },
     async loadData() {
       try {
         const d = await getChemicalStats()
@@ -104,35 +115,9 @@ export default {
         const total = this.stats.totalAmount || 1
         this.controlledPct = Math.round(this.stats.controlledAmount / total * 1000) / 10
         this.uncontrolledPct = Math.round(this.stats.uncontrolledAmount / total * 1000) / 10
-        this.$nextTick(() => {
-          this.renderDonut('chartControlled', this.controlledPct, '#ff6b35', '#ff8c5a', 'C')
-          this.renderDonut('chartUncontrolled', this.uncontrolledPct, '#36d399', '#5de8b5', 'U')
-        })
       } catch (e) {
         console.error('ChemicalsStats:', e)
       }
-    },
-    renderDonut(refName, pct, color, colorLight, key) {
-      const el = this.$refs[refName]
-      if (!el) return
-      const existing = key === 'C' ? this.chartC : this.chartU
-      const chart = existing || echarts.init(el)
-      if (key === 'C') this.chartC = chart
-      else this.chartU = chart
-      chart.setOption({
-        series: [{
-          type: 'pie',
-          radius: ['55%', '82%'],
-          center: ['50%', '50%'],
-          startAngle: 90,
-          silent: true,
-          label: { show: false },
-          data: [
-            { value: pct, itemStyle: { color: { type: 'linear', x: 0, y: 0, x2: 0, y2: 1, colorStops: [{ offset: 0, color: colorLight }, { offset: 1, color: color }] }, shadowBlur: 6, shadowColor: color } },
-            { value: 100 - pct, itemStyle: { color: 'rgba(255,255,255,0.05)' } }
-          ]
-        }]
-      })
     }
   }
 }
@@ -140,79 +125,98 @@ export default {
 
 <style scoped lang="scss">
 .ChemicalsStats {
-  height: 272px;
   display: flex;
   flex-direction: column;
 }
 
-.cards-grid {
+.chem-panel-body {
+  flex: 1;
+  padding: 8px 10px 10px;
+  min-height: 0;
+  display: flex;
+}
+
+.chem-stats-grid {
   flex: 1;
   display: grid;
   grid-template-columns: 1fr 1fr;
   grid-template-rows: 1fr 1fr;
   gap: 8px;
-  padding: 6px 10px 10px;
-  min-height: 0;
   z-index:3;
 }
 
-.chem-card {
-  background: rgba(0, 15, 50, 0.6);
-  border: 1px solid rgba(72, 180, 255, 0.12);
+.chem-stat-item {
+  background: rgba(2, 14, 46, 0.7);
+  border: 1px solid rgba(72, 180, 255, 0.15);
   border-radius: 6px;
   display: flex;
   align-items: center;
-  padding: 0 10px;
-  gap: 6px;
+  padding: 0 14px;
+  gap: 12px;
   overflow: hidden;
-  &.card-red   { border-color: rgba(255, 107, 53, 0.3); background: rgba(40, 8, 4, 0.6); }
-  &.card-green { border-color: rgba(54, 211, 153, 0.3); background: rgba(4, 28, 18, 0.6); }
-}
 
-.card-icon { flex-shrink: 0; }
+  &.item-red   { border-color: rgba(255, 77, 79, 0.3); background: rgba(35, 6, 6, 0.7); }
+  &.item-green { border-color: rgba(54, 211, 153, 0.3); background: rgba(4, 24, 16, 0.7); }
+  &.item-center {
+    flex-direction: column;
+    justify-content: center;
+    gap: 5px;
+  }
+}
 
-.donut-wrap {
+.chem-ring-wrap {
   flex-shrink: 0;
   width: 52px;
   height: 52px;
+
+  svg { width: 100%; height: 100%; }
 }
 
-.donut-pct {
-  flex-shrink: 0;
-  font-size: 11px;
+.ring-bg {
+  fill: none;
+  stroke: rgba(255, 255, 255, 0.08);
+  stroke-width: 3.5;
+}
+
+.ring-fg {
+  fill: none;
+  stroke-width: 3.5;
+  stroke-linecap: round;
+  transition: stroke-dashoffset 0.8s ease;
+}
+
+.ring-pct {
+  font-size: 8.5px;
   font-weight: 700;
-  line-height: 1;
-  white-space: nowrap;
-  &.orange { color: #ff6b35; }
-  &.green  { color: #36d399; }
+  text-anchor: middle;
+  dominant-baseline: middle;
 }
 
-.card-body {
+.chem-stat-info {
   display: flex;
   flex-direction: column;
-  gap: 4px;
-  min-width: 0;
-  &.center { align-items: center; width: 100%; }
+  gap: 5px;
 }
 
-.card-value {
-  font-size: 20px;
+.chem-stat-value {
+  font-size: 22px;
   font-weight: 700;
   line-height: 1;
   letter-spacing: 1px;
-  .unit { font-size: 12px; font-weight: 400; }
-  &.cyan   { color: #48d7ff; text-shadow: 0 0 8px rgba(72,215,255,0.5); }
-  &.orange { color: #ff6b35; text-shadow: 0 0 8px rgba(255,107,53,0.5); }
-  &.green  { color: #36d399; text-shadow: 0 0 8px rgba(54,211,153,0.5); }
-  &.yellow { color: #ffb020; text-shadow: 0 0 8px rgba(255,176,32,0.5); font-size: 30px; }
+
+  .unit {
+    font-size: 11px;
+    font-weight: 400;
+    color: #7eacc8;
+  }
 }
 
-.card-label {
+.chem-stat-label {
   font-size: 11px;
   color: #7eacc8;
 }
 
-.card-sub {
+.chem-stat-sub {
   font-size: 10px;
   color: rgba(126, 172, 200, 0.65);
 }

+ 1 - 1
src/components/LabStats/EquipmentStats.vue

@@ -85,7 +85,7 @@ export default {
         },
         series: [{
           type: 'pie',
-          radius: ['30%', '60%'],
+          radius: ['30%', '80%'],
           center: ['35%', '50%'],
           label: { show: false },
           emphasis: { scaleSize: 4 },