ChemicalsStats.vue 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. <template>
  2. <div class="ChemicalsStats panel-box">
  3. <div class="corner-deco tl"></div><div class="corner-deco tr"></div>
  4. <div class="corner-deco bl"></div><div class="corner-deco br"></div>
  5. <div class="panel-title">化学品库存动态统计</div>
  6. <div class="chem-panel-body">
  7. <div class="chem-stats-grid">
  8. <!-- 左上:存量总量,全满青色圆环 + 斜杠图标 -->
  9. <div class="chem-stat-item">
  10. <div class="chem-ring-wrap">
  11. <svg viewBox="0 0 44 44">
  12. <g transform="rotate(-90 22 22)">
  13. <circle class="ring-bg" cx="22" cy="22" r="17"/>
  14. <circle class="ring-fg" cx="22" cy="22" r="17" stroke="#48d7ff"
  15. stroke-dasharray="106.8" stroke-dashoffset="0"/>
  16. </g>
  17. <text x="12" y="27" class="chem-ring-icon" fill="#48d7ff">&#x1F9EA;</text>
  18. </svg>
  19. </div>
  20. <div class="chem-stat-info">
  21. <div class="chem-stat-value" style="color:#48d7ff">{{ formatNum(stats.totalAmount) }}<span class="unit"> {{ stats.unit }}</span></div>
  22. <div class="chem-stat-label">存量化学品总量</div>
  23. </div>
  24. </div>
  25. <!-- 右上:管控类,橙红色圆环 + 百分比居中 -->
  26. <div class="chem-stat-item item-red">
  27. <div class="chem-ring-wrap">
  28. <svg viewBox="0 0 44 44">
  29. <g transform="rotate(-90 22 22)">
  30. <circle class="ring-bg" cx="22" cy="22" r="17"/>
  31. <circle class="ring-fg" cx="22" cy="22" r="17" stroke="#ff4d4f"
  32. stroke-dasharray="106.8" :stroke-dashoffset="calcOffset(controlledPct)"/>
  33. </g>
  34. <text x="22" y="25" class="ring-pct" fill="#ff4d4f">{{ controlledPct }}%</text>
  35. </svg>
  36. </div>
  37. <div class="chem-stat-info">
  38. <div class="chem-stat-value" style="color:#ff4d4f">{{ formatNum(stats.controlledAmount) }}<span class="unit"> L</span></div>
  39. <div class="chem-stat-label">管控类化学品</div>
  40. </div>
  41. </div>
  42. <!-- 左下:非管控类,绿色圆环 + 百分比居中 -->
  43. <div class="chem-stat-item item-green">
  44. <div class="chem-ring-wrap">
  45. <svg viewBox="0 0 44 44">
  46. <g transform="rotate(-90 22 22)">
  47. <circle class="ring-bg" cx="22" cy="22" r="17"/>
  48. <circle class="ring-fg" cx="22" cy="22" r="17" stroke="#36d399"
  49. stroke-dasharray="106.8" :stroke-dashoffset="calcOffset(uncontrolledPct)"/>
  50. </g>
  51. <text x="22" y="25" class="ring-pct" fill="#36d399">{{ uncontrolledPct }}%</text>
  52. </svg>
  53. </div>
  54. <div class="chem-stat-info">
  55. <div class="chem-stat-value" style="color:#36d399">{{ formatNum(stats.uncontrolledAmount) }}<span class="unit"> L</span></div>
  56. <div class="chem-stat-label">非管控类化学品</div>
  57. </div>
  58. </div>
  59. <!-- 右下:类目统计 -->
  60. <div class="chem-stat-item item-center">
  61. <div class="chem-stat-value" style="color:#ffb020;font-size:22px;">{{ stats.totalCategories }}</div>
  62. <div class="chem-stat-label">存量化学品总类目</div>
  63. <div class="chem-stat-sub">管控{{ stats.controlledCategories }}类 / 非管控{{ stats.uncontrolledCategories }}类</div>
  64. </div>
  65. </div>
  66. </div>
  67. </div>
  68. </template>
  69. <script>
  70. import { getChemicalStats } from '@/api'
  71. export default {
  72. name: 'ChemicalsStats',
  73. data() {
  74. return {
  75. stats: {
  76. totalAmount: 0, unit: 'L', controlledAmount: 0, uncontrolledAmount: 0,
  77. totalCategories: 0, controlledCategories: 0, uncontrolledCategories: 0
  78. },
  79. controlledPct: 0,
  80. uncontrolledPct: 0,
  81. timer: null
  82. }
  83. },
  84. async mounted() {
  85. await this.loadData()
  86. this.timer = setInterval(this.loadData, 5 * 60 * 1000)
  87. },
  88. beforeDestroy() {
  89. clearInterval(this.timer)
  90. },
  91. methods: {
  92. formatNum(n) { return (n || 0).toLocaleString() },
  93. // r=17, circumference = 2*π*17 ≈ 106.8
  94. calcOffset(pct) {
  95. return (106.8 * (1 - pct / 100)).toFixed(1)
  96. },
  97. async loadData() {
  98. try {
  99. const d = await getChemicalStats()
  100. this.stats = {
  101. totalAmount: d.totalSurplus || 0,
  102. unit: d.totalUnit || 'L',
  103. controlledAmount: d.controlledSurplus || 0,
  104. uncontrolledAmount: d.uncontrolledSurplus || 0,
  105. totalCategories: d.totalCategoryCount || 0,
  106. controlledCategories: d.controlledCategoryCount || 0,
  107. uncontrolledCategories: d.uncontrolledCategoryCount || 0
  108. }
  109. this.controlledPct = d.controlledPercent || 0
  110. this.uncontrolledPct = d.uncontrolledPercent || 0
  111. } catch (e) {
  112. console.error('ChemicalsStats:', e)
  113. }
  114. }
  115. }
  116. }
  117. </script>
  118. <style scoped lang="scss">
  119. .ChemicalsStats {
  120. display: flex;
  121. flex-direction: column;
  122. }
  123. .chem-panel-body {
  124. flex: 1;
  125. padding: 8px 10px 10px;
  126. min-height: 0;
  127. display: flex;
  128. }
  129. .chem-stats-grid {
  130. flex: 1;
  131. display: grid;
  132. grid-template-columns: 1fr 1fr;
  133. grid-template-rows: 1fr 1fr;
  134. gap: 8px;
  135. z-index:3;
  136. }
  137. .chem-stat-item {
  138. background: rgba(2, 14, 46, 0.7);
  139. border: 1px solid rgba(72, 180, 255, 0.15);
  140. border-radius: 6px;
  141. display: flex;
  142. align-items: center;
  143. gap: 12px;
  144. overflow: hidden;
  145. &.item-red { border-color: rgba(255, 77, 79, 0.3); background: rgba(35, 6, 6, 0.7); }
  146. &.item-green { border-color: rgba(54, 211, 153, 0.3); background: rgba(4, 24, 16, 0.7); }
  147. &.item-center {
  148. flex-direction: column;
  149. justify-content: center;
  150. gap: 5px;
  151. }
  152. }
  153. .chem-ring-wrap {
  154. flex-shrink: 0;
  155. width: 52px;
  156. height: 52px;
  157. margin-left:14px;
  158. svg { width: 100%; height: 100%; }
  159. }
  160. .ring-bg {
  161. fill: none;
  162. stroke: rgba(255, 255, 255, 0.08);
  163. stroke-width: 3.5;
  164. }
  165. .ring-fg {
  166. fill: none;
  167. stroke-width: 3.5;
  168. stroke-linecap: round;
  169. transition: stroke-dashoffset 0.8s ease;
  170. }
  171. .ring-pct {
  172. font-size: 8.5px;
  173. font-weight: 700;
  174. text-anchor: middle;
  175. dominant-baseline: middle;
  176. }
  177. .chem-stat-info {
  178. display: flex;
  179. flex-direction: column;
  180. gap: 5px;
  181. }
  182. .chem-stat-value {
  183. font-size: 16px;
  184. font-weight: 700;
  185. line-height: 1;
  186. letter-spacing: 1px;
  187. .unit {
  188. font-size: 12px;
  189. font-weight: 400;
  190. color: #7eacc8;
  191. }
  192. }
  193. .chem-stat-label {
  194. font-size: 12px;
  195. color: #7eacc8;
  196. }
  197. .chem-stat-sub {
  198. font-size: 14px;
  199. color: rgba(126, 172, 200, 0.65);
  200. }
  201. </style>