MetricsContent.tsx 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. "use client";
  2. import {
  3. TrendingUp,
  4. Download,
  5. Info,
  6. CheckCircle2,
  7. AlertTriangle,
  8. } from "lucide-react";
  9. import { metricsData } from "./metricsData";
  10. export default function MetricsContent({ metricId }: { metricId: string }) {
  11. const categoryStr = metricId.charAt(0).toUpperCase();
  12. const metricCategoryList = metricsData[categoryStr] || [];
  13. const metricObj = metricCategoryList.find(m => m.id === metricId.toUpperCase());
  14. if (!metricObj) {
  15. return (
  16. <div className="flex-grow flex items-center justify-center p-8">
  17. <h2 className="text-xl text-secondary">未找到指标 {metricId.toUpperCase()} 的详情</h2>
  18. </div>
  19. );
  20. }
  21. // Parse average score out of "92%" string into a Number
  22. const scoreNum = parseFloat(metricObj.avgScore) || 85.0;
  23. return (
  24. <main className="flex-grow p-4 md:p-8 max-w-7xl mx-auto w-full grid grid-cols-1 lg:grid-cols-12 gap-6">
  25. <aside className="lg:col-span-4 xl:col-span-3 space-y-6">
  26. <div className="bg-surface rounded-2xl p-6 shadow-elevation-1 border border-outline-variant">
  27. <div className="flex items-center gap-2 mb-4">
  28. <span className={`w-2 h-8 rounded-full bg-${metricObj.colorClass}`}></span>
  29. <h2 className="text-lg font-medium text-on-surface">
  30. {metricObj.id} - {metricObj.name}
  31. </h2>
  32. </div>
  33. <div className="flex flex-col items-center justify-center py-8 relative">
  34. {/* Ambient Background Glow */}
  35. <div
  36. className="absolute w-40 h-40 rounded-full opacity-20 blur-2xl"
  37. style={{ backgroundColor: `var(--color-${metricObj.colorClass})` }}
  38. ></div>
  39. <div className="relative w-40 h-40 flex items-center justify-center rounded-full bg-surface shadow-elevation-2 border border-outline-variant/30">
  40. <svg
  41. className="absolute w-full h-full transform -rotate-90 drop-shadow-md"
  42. viewBox="0 0 100 100"
  43. >
  44. {/* Track Circle */}
  45. <circle
  46. cx="50"
  47. cy="50"
  48. r="44"
  49. fill="transparent"
  50. className="stroke-surface-variant flex-shrink-0"
  51. strokeWidth="6"
  52. />
  53. {/* Progress Circle */}
  54. <circle
  55. cx="50"
  56. cy="50"
  57. fill="transparent"
  58. r="44"
  59. style={{ stroke: `var(--color-${metricObj.colorClass})` }}
  60. strokeDasharray={`${(scoreNum / 100) * 276.46} 276.46`}
  61. strokeDashoffset="0"
  62. strokeLinecap="round"
  63. strokeWidth="8"
  64. className="transition-all duration-1000 ease-out"
  65. ></circle>
  66. </svg>
  67. <div className="text-center z-10 flex flex-col items-center justify-center bg-surface w-28 h-28 rounded-full shadow-inner border border-outline-variant/10">
  68. <span
  69. className="text-4xl font-display font-bold tracking-tight"
  70. style={{ color: `var(--color-${metricObj.colorClass})` }}
  71. >
  72. {scoreNum}
  73. </span>
  74. <span className="block text-[0.65rem] text-on-surface-variant uppercase tracking-widest mt-1 font-medium">
  75. 得分
  76. </span>
  77. </div>
  78. </div>
  79. </div>
  80. <div className="space-y-4 mt-2">
  81. <div className="flex justify-between items-center text-sm">
  82. <span className="text-on-surface-variant">
  83. 趋势 (对比上月)
  84. </span>
  85. <span className="flex items-center text-metric-high font-medium bg-metric-high/10 px-2 py-0.5 rounded-md">
  86. <TrendingUp className="w-4 h-4 mr-1" /> +2.1%
  87. </span>
  88. </div>
  89. </div>
  90. <div className="mt-6 pt-6 border-t border-outline-variant">
  91. <button className="w-full bg-primary text-on-primary h-10 rounded-full font-medium text-sm shadow-md hover:shadow-lg transition-all flex items-center justify-center gap-2 cursor-pointer">
  92. <Download className="w-5 h-5" /> 导出报告
  93. </button>
  94. </div>
  95. </div>
  96. <div className={`${metricObj.bgClass} rounded-2xl p-6`}>
  97. <h3 className="font-medium mb-2 flex items-center gap-2">
  98. <Info className="w-5 h-5" /> 指标说明
  99. </h3>
  100. <p className="text-sm opacity-90 leading-relaxed font-light">
  101. {metricObj.description}
  102. </p>
  103. </div>
  104. </aside>
  105. <section className="lg:col-span-8 xl:col-span-9 space-y-6">
  106. <div className="flex flex-col md:flex-row md:items-center justify-between mb-2">
  107. <div>
  108. <h2 className="text-2xl font-medium text-on-background">
  109. 子指标分析
  110. </h2>
  111. <p className="text-on-surface-variant text-sm mt-1">
  112. 关于 {metricObj.id} 的详细数据维度拆解
  113. </p>
  114. </div>
  115. <div className="flex gap-2 mt-4 md:mt-0">
  116. <button className="h-10 px-4 rounded-full border border-outline text-on-surface-variant text-sm font-medium hover:bg-surface-variant hover:text-on-surface focus:ring-2 focus:ring-primary focus:outline-none transition-colors cursor-pointer">
  117. 筛选
  118. </button>
  119. <button className="h-10 px-4 rounded-full border border-outline text-on-surface-variant text-sm font-medium hover:bg-surface-variant hover:text-on-surface focus:ring-2 focus:ring-primary focus:outline-none transition-colors cursor-pointer">
  120. 日期范围
  121. </button>
  122. </div>
  123. </div>
  124. <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
  125. {metricObj.subMetrics.map((sub, i) => {
  126. // Generating some mock variation to make UI look realistic
  127. const subScore = Math.min(100, scoreNum + ((i % 3 === 0 ? 1 : -1) * (i * 2 + 3)));
  128. const isWarn = subScore < 80;
  129. return (
  130. <article key={sub.id} className="bg-surface rounded-xl p-0 shadow-elevation-1 border border-outline-variant overflow-hidden flex flex-col h-full hover:shadow-elevation-2 hover:bg-surface-container transition-all duration-300 group">
  131. <div className="p-5 border-b border-outline-variant bg-surface-variant/20">
  132. <div className="flex justify-between items-start">
  133. <div>
  134. <span className={`text-xs font-bold tracking-wider uppercase text-${metricObj.colorClass}`}>
  135. {sub.id}
  136. </span>
  137. <h3 className="text-lg font-medium text-on-surface mt-1">
  138. {sub.name}
  139. </h3>
  140. </div>
  141. <span className={`w-8 h-8 rounded-full flex items-center justify-center ${isWarn ? 'bg-metric-warn/10 text-metric-warn' : 'bg-metric-high/10 text-metric-high'}`}>
  142. {isWarn ? <AlertTriangle className="w-5 h-5" /> : <CheckCircle2 className="w-5 h-5" />}
  143. </span>
  144. </div>
  145. </div>
  146. <div className="p-5 flex-grow space-y-5">
  147. {sub.values.map((val, idx) => (
  148. <div key={idx} className="flex flex-col gap-1">
  149. <div className="flex items-center justify-between">
  150. <div className="flex items-start gap-3">
  151. <CheckCircle2 className="text-on-surface-variant w-5 h-5 mt-0.5" />
  152. <p className="text-sm font-medium text-on-surface leading-tight">
  153. {val.name}
  154. </p>
  155. </div>
  156. <div className="text-right whitespace-nowrap ml-2">
  157. <span className={`block text-xl font-bold ${isWarn ? 'text-metric-warn' : 'text-metric-high'}`}>
  158. {idx === 0 ? subScore + '%' : (isWarn ? '一般' : '优秀')}
  159. </span>
  160. </div>
  161. </div>
  162. </div>
  163. ))}
  164. <div className="w-full bg-surface-variant rounded-full h-1.5 mt-2">
  165. <div
  166. className={`h-1.5 rounded-full ${isWarn ? 'bg-metric-warn' : 'bg-metric-high'}`}
  167. style={{ width: `${subScore}%` }}
  168. ></div>
  169. </div>
  170. </div>
  171. <div className="px-5 py-3 bg-surface-variant/10 flex justify-end border-t border-outline-variant/50">
  172. <button className="text-primary text-sm font-medium hover:bg-primary/10 px-3 py-1.5 rounded-lg transition-colors cursor-pointer">
  173. 查看详情
  174. </button>
  175. </div>
  176. </article>
  177. );
  178. })}
  179. </div>
  180. </section>
  181. </main>
  182. );
  183. }