From 9d94bacb1e7decb49a4a6d3de171355867f7fde7 Mon Sep 17 00:00:00 2001
From: buhuazhen <hua100783@gmail.com>
Date: 星期一, 01 六月 2026 14:15:23 +0800
Subject: [PATCH] fix:质检合格率 添加同比环比

---
 src/views/reportAnalysis/reportManagement/index.vue |  465 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/api/reportAnalysis/qualityReport.js             |    9 +
 2 files changed, 467 insertions(+), 7 deletions(-)

diff --git a/src/api/reportAnalysis/qualityReport.js b/src/api/reportAnalysis/qualityReport.js
index 66a7540..214425d 100644
--- a/src/api/reportAnalysis/qualityReport.js
+++ b/src/api/reportAnalysis/qualityReport.js
@@ -50,3 +50,12 @@
     params: { modelType }
   })
 }
+
+// 鑾峰彇鏈堝害鍚堟牸鐜囩幆姣旂粺璁℃暟鎹�
+export function getMonthlyPassRateWithComparison(year, month) {
+  return request({
+    url: '/qualityReport/getMonthlyPassRateWithComparison',
+    method: 'post',
+    params: { year, month }
+  })
+}
diff --git a/src/views/reportAnalysis/reportManagement/index.vue b/src/views/reportAnalysis/reportManagement/index.vue
index dc9d486..6bcc5cd 100644
--- a/src/views/reportAnalysis/reportManagement/index.vue
+++ b/src/views/reportAnalysis/reportManagement/index.vue
@@ -15,7 +15,7 @@
             <div class="top-container">
               <div class="typeNum">
                 <div class="typeNum-left">
-                  <img src="~@/assets/images/chartCard.svg" alt="鍥捐〃"
+                  <img :src="chartCardIcon" alt="鍥捐〃"
                     style="width: 40px; height: 40px; object-fit: contain;">
                   <div class="typeNum-left-text">鍘熸潗鏂�</div>
                 </div>
@@ -39,7 +39,7 @@
               </div>
               <div class="typeNum">
                 <div class="typeNum-left">
-                  <img src="~@/assets/images/chartCard2.svg" alt="鍥捐〃"
+                  <img :src="chartCard2Icon" alt="鍥捐〃"
                     style="width: 40px; height: 40px; object-fit: contain;">
                   <div class="typeNum-left-text" style="color: #5EB334;">鍗婃垚鍝�</div>
                 </div>
@@ -63,7 +63,7 @@
               </div>
               <div class="typeNum">
                 <div class="typeNum-left">
-                  <img src="~@/assets/images/chartCard3.svg" alt="鍥捐〃"
+                  <img :src="chartCard3Icon" alt="鍥捐〃"
                     style="width: 40px; height: 40px; object-fit: contain;">
                   <div class="typeNum-left-text" style="color: #8000FF;">鎴愬搧</div>
                 </div>
@@ -100,7 +100,7 @@
             <div class="top-container flex-center">
               <div class="quality-card blue-card">
                 <div class="quality-card-title">
-                  <img src="~@/assets/images/chartCard.svg" alt="鍘熸潗鏂�"
+                  <img :src="chartCardIcon" alt="鍘熸潗鏂�"
                     style="width: 24px; height: 24px; margin-right: 8px;">
                   鍘熸潗鏂欏悎鏍肩巼
                 </div>
@@ -125,7 +125,7 @@
               </div>
               <div class="quality-card green-card">
                 <div class="quality-card-title">
-                  <img src="~@/assets/images/chartCard2.svg" alt="鍗婃垚鍝�"
+                  <img :src="chartCard2Icon" alt="鍗婃垚鍝�"
                     style="width: 24px; height: 24px; margin-right: 8px;">
                   鍗婃垚鍝佸悎鏍肩巼
                 </div>
@@ -150,7 +150,7 @@
               </div>
               <div class="quality-card purple-card">
                 <div class="quality-card-title">
-                  <img src="~@/assets/images/chartCard3.svg" alt="鎴愬搧"
+                  <img :src="chartCard3Icon" alt="鎴愬搧"
                     style="width: 24px; height: 24px; margin-right: 8px;">
                   鎴愬搧鍚堟牸鐜�
                 </div>
@@ -189,6 +189,120 @@
                 <span>璐ㄦ鍚堟牸鐜�</span>
               </div>
             </template>
+            <!-- 鐜瘮鎸囨爣鍗$墖 -->
+            <div class="mom-cards">
+              <!-- 鏈堜唤閫夋嫨鍣� -->
+              <div class="mom-month-selector">
+                <span class="mom-month-label">閫夋嫨鏈堜唤锛�</span>
+                <el-date-picker
+                  v-model="momSelectedMonth"
+                  type="month"
+                  placeholder="閫夋嫨鏈堜唤"
+                  format="YYYY骞碝M鏈�"
+                  value-format="YYYY-MM"
+                  :clearable="false"
+                  :disabled-date="disabledMonthDate"
+                  style="width: 150px;"
+                />
+              </div>
+
+              <!-- 涓籘ab锛氱被鍒垏鎹� -->
+              <div class="mom-main-tabs">
+                <div class="mom-tab-item" :class="{ active: momMainTab === 'raw' }" @click="momMainTab = 'raw'">
+                  <img :src="chartCardIcon" alt="" style="width: 16px; height: 16px; margin-right: 6px;">
+                  鍘熸潗鏂�
+                </div>
+                <div class="mom-tab-item" :class="{ active: momMainTab === 'semi' }" @click="momMainTab = 'semi'">
+                  <img :src="chartCard2Icon" alt="" style="width: 16px; height: 16px; margin-right: 6px;">
+                  鍗婃垚鍝�
+                </div>
+                <div class="mom-tab-item" :class="{ active: momMainTab === 'final' }" @click="momMainTab = 'final'">
+                  <img :src="chartCard3Icon" alt="" style="width: 16px; height: 16px; margin-right: 6px;">
+                  鎴愬搧
+                </div>
+              </div>
+
+              <!-- 瀛怲ab锛氭寚鏍囧垏鎹� -->
+              <div class="mom-sub-tabs">
+                <div class="mom-sub-tab-item" :class="{ active: momSubTab === 'current' }" @click="momSubTab = 'current'">
+                  褰撴湀鍚堟牸鐜�
+                </div>
+                <div class="mom-sub-tab-item" :class="{ active: momSubTab === 'mom' }" @click="momSubTab = 'mom'">
+                  鐜瘮鍙樺寲
+                </div>
+                <div class="mom-sub-tab-item" :class="{ active: momSubTab === 'yoy' }" @click="momSubTab = 'yoy'">
+                  鍚屾瘮鍙樺寲
+                </div>
+              </div>
+
+              <!-- 鏁版嵁灞曠ず鍖哄煙 -->
+              <div class="mom-data-panel">
+                <!-- 褰撴湀鍚堟牸鐜� -->
+                <div v-if="momSubTab === 'current'" class="mom-data-content">
+                  <div class="mom-data-value">{{ getCurrentMomData().passRate }}%</div>
+                  <div class="mom-data-detail">
+                    <div class="mom-detail-item">
+                      <span class="mom-detail-label">鎬绘暟閲�</span>
+                      <span class="mom-detail-value">{{ getCurrentMomData().totalCount }}</span>
+                    </div>
+                    <div class="mom-detail-item">
+                      <span class="mom-detail-label">宸插畬鎴�</span>
+                      <span class="mom-detail-value">{{ getCurrentMomData().completedCount }}</span>
+                    </div>
+                    <div class="mom-detail-item">
+                      <span class="mom-detail-label">鍚堟牸鏁�</span>
+                      <span class="mom-detail-value">{{ getCurrentMomData().qualifiedCount }}</span>
+                    </div>
+                  </div>
+                </div>
+
+                <!-- 鐜瘮鍙樺寲 -->
+                <div v-if="momSubTab === 'mom'" class="mom-data-content">
+                  <div class="mom-data-header">
+                    <div class="mom-compare-item">
+                      <span class="mom-compare-label">褰撴湀鍚堟牸鐜�</span>
+                      <span class="mom-compare-value primary">{{ getCurrentMomData().passRate }}%</span>
+                    </div>
+                    <div class="mom-compare-arrow" :class="getTrendClass(getCurrentMomData().momTrend)">
+                      {{ getTrendIcon(getCurrentMomData().momTrend) }}
+                    </div>
+                    <div class="mom-compare-item">
+                      <span class="mom-compare-label">涓婃湀鍚堟牸鐜�</span>
+                      <span class="mom-compare-value">{{ getCurrentMomData().lastMonthPassRate || 0 }}%</span>
+                    </div>
+                  </div>
+                  <div class="mom-data-change" :class="getTrendClass(getCurrentMomData().momTrend)">
+                    鐜瘮鍙樺寲: {{ formatChange(getCurrentMomData().momChange) }}
+                  </div>
+                  <div class="mom-data-desc">
+                    涓庝笂鏈堢浉姣旓紝鍚堟牸鐜噞{ getCurrentMomData().momTrend > 0 ? '涓婂崌' : getCurrentMomData().momTrend < 0 ? '涓嬮檷' : '鎸佸钩' }}
+                  </div>
+                </div>
+
+                <!-- 鍚屾瘮鍙樺寲 -->
+                <div v-if="momSubTab === 'yoy'" class="mom-data-content">
+                  <div class="mom-data-header">
+                    <div class="mom-compare-item">
+                      <span class="mom-compare-label">褰撴湀鍚堟牸鐜�</span>
+                      <span class="mom-compare-value primary">{{ getCurrentMomData().passRate }}%</span>
+                    </div>
+                    <div class="mom-compare-arrow" :class="getTrendClass(getCurrentMomData().yoyTrend)">
+                      {{ getTrendIcon(getCurrentMomData().yoyTrend) }}
+                    </div>
+                    <div class="mom-compare-item">
+                      <span class="mom-compare-label">鍘诲勾鍚屾湀</span>
+                      <span class="mom-compare-value">{{ getCurrentMomData().lastYearPassRate || 0 }}%</span>
+                    </div>
+                  </div>
+                  <div class="mom-data-change" :class="getTrendClass(getCurrentMomData().yoyTrend)">
+                    鍚屾瘮鍙樺寲: {{ formatChange(getCurrentMomData().yoyChange) }}
+                  </div>
+                  <div class="mom-data-desc">
+                    涓庡幓骞村悓鏈堢浉姣旓紝鍚堟牸鐜噞{ getCurrentMomData().yoyTrend > 0 ? '涓婂崌' : getCurrentMomData().yoyTrend < 0 ? '涓嬮檷' : '鎸佸钩' }}
+                  </div>
+                </div>
+              </div>
+            </div>
             <div class="chart-container-line">
               <div class="container-line-left">
                 <div style="height: 100%; width: 100%;" ref="usageChartRef">
@@ -280,13 +394,99 @@
 import { ref, reactive, onMounted, nextTick } from "vue";
 import { ElMessage } from "element-plus";
 import * as echarts from "echarts";
-import { getInspectStatistics, getPassRateStatistics, getMonthlyPassRateStatistics, getYearlyPassRateStatistics, getMonthlyCompletionDetails, getTopParameters } from "@/api/reportAnalysis/qualityReport";
+import { getInspectStatistics, getPassRateStatistics, getMonthlyPassRateStatistics, getYearlyPassRateStatistics, getMonthlyCompletionDetails, getTopParameters, getMonthlyPassRateWithComparison } from "@/api/reportAnalysis/qualityReport";
+
+// 瀵煎叆鍥炬爣
+import chartCardIcon from '@/assets/images/chartCard.svg';
+import chartCard2Icon from '@/assets/images/chartCard2.svg';
+import chartCard3Icon from '@/assets/images/chartCard3.svg';
 
 // 鍝嶅簲寮忔暟鎹�
 const filterForm = reactive({
   dateRange: [],
   reportType: "sample",
 });
+
+// 鐜瘮鏁版嵁
+const momData = ref([
+  { label: '鍘熸潗鏂�', passRate: 0, momChange: 0, yoyChange: 0, momTrend: 0, yoyTrend: 0, totalCount: 0, completedCount: 0, qualifiedCount: 0, lastMonthPassRate: 0, lastYearPassRate: 0 },
+  { label: '鍗婃垚鍝�', passRate: 0, momChange: 0, yoyChange: 0, momTrend: 0, yoyTrend: 0, totalCount: 0, completedCount: 0, qualifiedCount: 0, lastMonthPassRate: 0, lastYearPassRate: 0 },
+  { label: '鎴愬搧', passRate: 0, momChange: 0, yoyChange: 0, momTrend: 0, yoyTrend: 0, totalCount: 0, completedCount: 0, qualifiedCount: 0, lastMonthPassRate: 0, lastYearPassRate: 0 }
+]);
+
+// 鐜瘮Tab鐘舵��
+const momMainTab = ref('raw'); // 涓籘ab: raw, semi, final
+const momSubTab = ref('current'); // 瀛怲ab: current, mom, yoy
+
+// 鏈堜唤閫夋嫨鍣� - 榛樿褰撳墠鏈堜唤
+const momSelectedMonth = ref(new Date().toISOString().slice(0, 7)); // 鏍煎紡: YYYY-MM
+
+// 闄愬埗鏈堜唤閫夋嫨锛屼笉鍏佽閫夋嫨鏈潵鏈堜唤
+const disabledMonthDate = (time) => {
+  return time.getTime() > new Date().getTime();
+};
+
+// 鑾峰彇褰撳墠閫変腑绫诲埆鐨勬暟鎹�
+const getCurrentMomData = () => {
+  const index = momMainTab.value === 'raw' ? 0 : momMainTab.value === 'semi' ? 1 : 2;
+  return momData.value[index] || {};
+};
+
+// 鐜瘮鐩稿叧鏂规硶
+const getMomIcon = (index) => {
+  const icons = [chartCardIcon, chartCard2Icon, chartCard3Icon];
+  return icons[index];
+};
+
+const getMomIconColor = (index) => {
+  const colors = ['#165DFF', '#5EB334', '#722ED1'];
+  return colors[index];
+};
+
+const getTrendClass = (trend) => {
+  if (trend > 0) return 'trend-up';
+  if (trend < 0) return 'trend-down';
+  return 'trend-flat';
+};
+
+const getTrendIcon = (trend) => {
+  if (trend > 0) return '鈫�';
+  if (trend < 0) return '鈫�';
+  return '-';
+};
+
+const formatChange = (change) => {
+  if (!change) return '0%';
+  const value = Number(change).toFixed(1);
+  return value > 0 ? `+${value}%` : `${value}%`;
+};
+
+const fetchMomData = async () => {
+  try {
+    // 浠庨�変腑鐨勬湀浠戒腑瑙f瀽骞翠唤鍜屾湀浠�
+    const [year, monthStr] = momSelectedMonth.value.split('-');
+    const month = parseInt(monthStr, 10);
+    const res = await getMonthlyPassRateWithComparison(year, month);
+    if (res.code === 200) {
+      const data = res.data || [];
+      momData.value = data.map(item => ({
+        label: item.modelType === 0 ? '鍘熸潗鏂�' : item.modelType === 1 ? '鍗婃垚鍝�' : '鎴愬搧',
+        passRate: item.passRate ? Number(item.passRate).toFixed(1) : 0,
+        momChange: item.momChange || 0,
+        yoyChange: item.yoyChange || 0,
+        momTrend: item.momTrend || 0,
+        yoyTrend: item.yoyTrend || 0,
+        totalCount: item.totalCount || 0,
+        completedCount: item.completedCount || 0,
+        qualifiedCount: item.qualifiedCount || 0,
+        lastMonthPassRate: item.lastMonthPassRate ? Number(item.lastMonthPassRate).toFixed(1) : 0,
+        lastYearPassRate: item.lastYearPassRate ? Number(item.lastYearPassRate).toFixed(1) : 0
+      }));
+    }
+  } catch (error) {
+    console.error("Failed to fetch mom data:", error);
+  }
+};
 
 const inspectStatisticsData = ref([]);
 const passRateStatisticsData = ref([]);
@@ -422,10 +622,16 @@
   fetchMonthlyPassRateData();
   fetchYearlyPassRateData();
   fetchMonthlyCompletionDetailsData();
+  fetchMomData();
 });
 
 watch(activeTab, () => {
   fetchTopParametersData();
+});
+
+// 鐩戝惉鏈堜唤閫夋嫨鍣ㄥ彉鍖�
+watch(momSelectedMonth, () => {
+  fetchMomData();
 });
 
 
@@ -884,6 +1090,7 @@
   fetchYearlyPassRateData();
   fetchMonthlyCompletionDetailsData();
   fetchTopParametersData();
+  fetchMomData();
   nextTick(() => {
     initSampleChart();
     initEquipmentChart();
@@ -1468,4 +1675,248 @@
 :deep(.el-input__prefix) {
   display: none !important;
 }
+
+/* 鐜瘮鎸囨爣鍗$墖鏍峰紡 */
+.mom-cards {
+  margin-bottom: 20px;
+  padding: 0 10px;
+}
+
+/* 鏈堜唤閫夋嫨鍣ㄦ牱寮� */
+.mom-month-selector {
+  display: flex;
+  align-items: center;
+  margin-bottom: 16px;
+  padding: 12px 16px;
+  background: #f5f7fa;
+  border-radius: 8px;
+}
+
+.mom-month-label {
+  font-size: 14px;
+  color: #606266;
+  margin-right: 12px;
+  font-weight: 500;
+}
+
+/* 涓籘ab鏍峰紡 */
+.mom-main-tabs {
+  display: flex;
+  gap: 12px;
+  margin-bottom: 16px;
+}
+
+.mom-tab-item {
+  display: flex;
+  align-items: center;
+  padding: 10px 20px;
+  background: #f5f7fa;
+  border-radius: 8px;
+  cursor: pointer;
+  font-size: 14px;
+  color: #606266;
+  transition: all 0.3s;
+}
+
+.mom-tab-item:hover {
+  background: #e6f0ff;
+}
+
+.mom-tab-item.active {
+  background: linear-gradient(135deg, #165DFF 0%, #409EFF 100%);
+  color: #fff;
+}
+
+/* 瀛怲ab鏍峰紡 */
+.mom-sub-tabs {
+  display: flex;
+  gap: 8px;
+  margin-bottom: 20px;
+  border-bottom: 1px solid #e8e8e8;
+  padding-bottom: 12px;
+}
+
+.mom-sub-tab-item {
+  padding: 8px 16px;
+  font-size: 13px;
+  color: #909399;
+  cursor: pointer;
+  border-radius: 4px;
+  transition: all 0.3s;
+}
+
+.mom-sub-tab-item:hover {
+  color: #165DFF;
+  background: #f0f5ff;
+}
+
+.mom-sub-tab-item.active {
+  color: #165DFF;
+  font-weight: 500;
+  background: #e6f0ff;
+}
+
+/* 鏁版嵁闈㈡澘鏍峰紡 */
+.mom-data-panel {
+  background: #fafbfc;
+  border-radius: 12px;
+  padding: 24px;
+  min-height: 120px;
+}
+
+.mom-data-content {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+}
+
+.mom-data-value {
+  font-size: 48px;
+  font-weight: 600;
+  color: #165DFF;
+  margin-bottom: 20px;
+}
+
+.mom-data-detail {
+  display: flex;
+  gap: 40px;
+}
+
+.mom-detail-item {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+}
+
+.mom-detail-label {
+  font-size: 12px;
+  color: #909399;
+  margin-bottom: 4px;
+}
+
+.mom-detail-value {
+  font-size: 18px;
+  font-weight: 500;
+  color: #303133;
+}
+
+/* 瀵规瘮鏁版嵁鏍峰紡 */
+.mom-data-header {
+  display: flex;
+  align-items: center;
+  gap: 30px;
+  margin-bottom: 20px;
+}
+
+.mom-compare-item {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+}
+
+.mom-compare-label {
+  font-size: 12px;
+  color: #909399;
+  margin-bottom: 8px;
+}
+
+.mom-compare-value {
+  font-size: 28px;
+  font-weight: 500;
+  color: #606266;
+}
+
+.mom-compare-value.primary {
+  color: #165DFF;
+}
+
+.mom-compare-arrow {
+  font-size: 32px;
+  font-weight: 600;
+}
+
+.mom-data-change {
+  font-size: 24px;
+  font-weight: 500;
+  margin-bottom: 12px;
+}
+
+.mom-data-desc {
+  font-size: 14px;
+  color: #909399;
+}
+
+.mom-card {
+  background: #fff;
+  border-radius: 8px;
+  padding: 16px;
+  border: 1px solid #e8e8e8;
+}
+
+.mom-card-header {
+  display: flex;
+  align-items: center;
+  margin-bottom: 12px;
+}
+
+.mom-card-icon {
+  width: 32px;
+  height: 32px;
+  border-radius: 6px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  margin-right: 10px;
+}
+
+.mom-card-icon img {
+  width: 20px;
+  height: 20px;
+}
+
+.mom-card-title {
+  font-size: 14px;
+  color: #333;
+  font-weight: 500;
+}
+
+.mom-card-rate {
+  font-size: 32px;
+  font-weight: 600;
+  color: #165DFF;
+  margin-bottom: 12px;
+}
+
+.mom-card-changes {
+  display: flex;
+  gap: 20px;
+}
+
+.mom-change-item {
+  display: flex;
+  align-items: center;
+  gap: 6px;
+}
+
+.mom-change-label {
+  font-size: 12px;
+  color: #909399;
+}
+
+.mom-change-value {
+  font-size: 13px;
+  font-weight: 500;
+}
+
+.trend-up {
+  color: #52c41a;
+}
+
+.trend-down {
+  color: #f5222d;
+}
+
+.trend-flat {
+  color: #909399;
+}
 </style>

--
Gitblit v1.9.3