ResultAnalysis.vue 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. <template>
  2. <div class="result-analysis">
  3. <el-card shadow="never">
  4. <template #header>
  5. <div class="card-header">
  6. <span class="title">AI 结果分析</span>
  7. <el-tag type="info" size="small">Kimi k3 + 多保真度校准</el-tag>
  8. </div>
  9. </template>
  10. <el-row :gutter="20">
  11. <el-col :span="12">
  12. <el-form label-width="100px" size="small">
  13. <el-form-item label="保真度等级">
  14. <el-select v-model="fidelity">
  15. <el-option label="L0 - 解析预筛选" value="L0" />
  16. <el-option label="L1 - 磁路等效电路" value="L1" />
  17. <el-option label="L2 - 2D FEA" value="L2" />
  18. <el-option label="L3 - 3D FEA (Motor-CAD)" value="L3" />
  19. <el-option label="L4 - 3D瞬态+热耦合" value="L4" />
  20. </el-select>
  21. </el-form-item>
  22. <el-form-item label="目标转矩">
  23. <el-input-number v-model="targets.tavg_nm" :min="0" />
  24. </el-form-item>
  25. <el-form-item label="目标效率">
  26. <el-input-number v-model="targets.efficiency_pct" :min="0" :max="100" />
  27. </el-form-item>
  28. </el-form>
  29. </el-col>
  30. <el-col :span="12">
  31. <div class="input-area">
  32. <div class="input-label">仿真结果数据 (JSON数组)</div>
  33. <el-input
  34. v-model="resultsJson"
  35. type="textarea"
  36. :rows="6"
  37. placeholder='[{"tavg_nm": 15.2, "efficiency_pct": 92.5, "airgap_mm": 1.0, ...}]'
  38. />
  39. <div class="actions">
  40. <el-button size="small" @click="loadExample">加载示例</el-button>
  41. <el-button type="primary" :loading="analyzing" @click="doAnalyze">开始分析</el-button>
  42. </div>
  43. </div>
  44. </el-col>
  45. </el-row>
  46. </el-card>
  47. <el-card v-if="analysis" shadow="never" style="margin-top: 20px;">
  48. <template #header>
  49. <div class="card-header">
  50. <span>分析结果</span>
  51. <ConfidenceBadge v-if="confidence" :grade="confidence.grade" :score="confidence.score" />
  52. </div>
  53. </template>
  54. <el-alert :title="analysis.summary" type="info" :closable="false" show-icon style="margin-bottom: 16px;" />
  55. <el-row :gutter="16">
  56. <el-col :span="12">
  57. <div class="section-title">关键指标统计</div>
  58. <el-table :data="metricStats" border size="small">
  59. <el-table-column prop="metric" label="指标" width="140" />
  60. <el-table-column prop="min" label="最小" />
  61. <el-table-column prop="max" label="最大" />
  62. <el-table-column prop="mean" label="均值" />
  63. </el-table>
  64. </el-col>
  65. <el-col :span="12">
  66. <div class="section-title">收敛性检查</div>
  67. <el-table :data="convChecks" border size="small">
  68. <el-table-column prop="name" label="判据" width="160" />
  69. <el-table-column label="结果" width="80">
  70. <template #default="{ row }">
  71. <el-tag :type="row.passed ? 'success' : 'danger'" size="small">{{ row.passed ? 'PASS' : 'FAIL' }}</el-tag>
  72. </template>
  73. </el-table-column>
  74. <el-table-column prop="value" label="数值" />
  75. </el-table>
  76. </el-col>
  77. </el-row>
  78. <el-divider v-if="aiAnalysis" content-position="left">AI 深度分析</el-divider>
  79. <el-descriptions v-if="aiAnalysis" :column="1" border size="small">
  80. <el-descriptions-item v-if="aiAnalysis.trend_analysis" label="趋势分析">{{ aiAnalysis.trend_analysis }}</el-descriptions-item>
  81. <el-descriptions-item v-if="aiAnalysis.recommendation" label="优化建议">{{ aiAnalysis.recommendation }}</el-descriptions-item>
  82. </el-descriptions>
  83. <div v-if="aiAnalysis?.optimization_suggestions?.length" class="suggestions">
  84. <div class="section-title">优化建议清单</div>
  85. <el-timeline>
  86. <el-timeline-item
  87. v-for="(s, idx) in aiAnalysis.optimization_suggestions"
  88. :key="idx"
  89. :type="s.priority === 'high' ? 'danger' : s.priority === 'medium' ? 'warning' : 'primary'"
  90. >
  91. <strong>{{ s.suggestion }}</strong>
  92. <div v-if="s.expected_improvement" style="color: #909399; font-size: 12px;">预期改善: {{ s.expected_improvement }}</div>
  93. </el-timeline-item>
  94. </el-timeline>
  95. </div>
  96. </el-card>
  97. </div>
  98. </template>
  99. <script setup lang="ts">
  100. import { ref, reactive, computed } from 'vue'
  101. import { ElMessage } from 'element-plus'
  102. import { analysisApi } from '@/api/ai'
  103. import ConfidenceBadge from '@/components/ai/ConfidenceBadge.vue'
  104. const fidelity = ref('L3')
  105. const targets = reactive({ tavg_nm: 10, efficiency_pct: 92 })
  106. const resultsJson = ref('')
  107. const analyzing = ref(false)
  108. const analysis = ref<any>(null)
  109. const aiAnalysis = ref<any>(null)
  110. const confidence = computed(() => analysis.value?.confidence)
  111. const metricStats = computed(() => {
  112. const stats = analysis.value?.quantitative?.statistics || {}
  113. return Object.entries(stats).map(([metric, s]: any) => ({
  114. metric, min: s.min, max: s.max, mean: s.mean,
  115. }))
  116. })
  117. const convChecks = computed(() => {
  118. const conv = analysis.value?.quantitative?.convergence || {}
  119. return Object.entries(conv)
  120. .filter(([k]) => k !== 'summary')
  121. .map(([name, v]: any) => ({ name, passed: v.passed, value: v.value ?? '-' }))
  122. })
  123. const loadExample = () => {
  124. const data = []
  125. for (let i = 0; i < 15; i++) {
  126. const airgap = 0.5 + Math.random() * 1.5
  127. const current = 10 + Math.random() * 15
  128. data.push({
  129. airgap_mm: +airgap.toFixed(2),
  130. current_a: +current.toFixed(1),
  131. tavg_nm: +(current * 2.5 / Math.sqrt(airgap) + (Math.random() - 0.5)).toFixed(2),
  132. efficiency_pct: +(88 + Math.random() * 5).toFixed(1),
  133. total_losses_w: +(40 + Math.random() * 20).toFixed(1),
  134. winding_temp_c: +(85 + Math.random() * 25).toFixed(1),
  135. feasible: Math.random() > 0.15,
  136. })
  137. }
  138. resultsJson.value = JSON.stringify(data, null, 2)
  139. }
  140. const doAnalyze = async () => {
  141. let results: any[]
  142. try {
  143. results = JSON.parse(resultsJson.value)
  144. } catch {
  145. ElMessage.error('结果数据不是有效的JSON')
  146. return
  147. }
  148. if (!Array.isArray(results) || !results.length) {
  149. ElMessage.error('请输入至少一条结果数据')
  150. return
  151. }
  152. analyzing.value = true
  153. try {
  154. const res: any = await analysisApi.analyze({
  155. results,
  156. targets: { ...targets },
  157. fidelity: fidelity.value,
  158. })
  159. analysis.value = res
  160. aiAnalysis.value = res.ai_analysis
  161. ElMessage.success('分析完成')
  162. } catch (e: any) {
  163. ElMessage.error('分析失败: ' + (e.message || e))
  164. } finally {
  165. analyzing.value = false
  166. }
  167. }
  168. </script>
  169. <style scoped>
  170. .result-analysis { padding: 20px; }
  171. .title { font-weight: 600; font-size: 16px; }
  172. .card-header { display: flex; justify-content: space-between; align-items: center; }
  173. .input-area { display: flex; flex-direction: column; gap: 8px; }
  174. .input-label { font-weight: 600; color: #606266; font-size: 13px; }
  175. .actions { display: flex; gap: 8px; justify-content: flex-end; }
  176. .section-title { font-weight: 600; margin: 12px 0 8px; color: #303133; }
  177. .suggestions { margin-top: 16px; }
  178. </style>