Parcourir la source

满意度调查评分显示优化:将平均分显示改为百分比形式

zlwu il y a 1 an
Parent
commit
e7e79aa23a

+ 28 - 0
docs/变更管理/code-adjust-record-2025-07-18-wuzilong.md

@@ -0,0 +1,28 @@
+# 代码变更记录 - 2025-07-18
+
+## 变更时间
+2025-07-18 15:30:25
+
+## 变更人员
+wuzilong
+
+## 变更内容
+1. 修改了满意度调查页面 `web/src/views/hq/satSurvey/index.vue` 中的评分显示方式
+2. 将原来的平均分显示改为比例显示(百分比格式)
+3. 计算公式:平均分除以总分,保留一位小数,以百分比形式显示
+
+## 变更原因
+优化满意度显示方式,通过百分比形式更直观地展示满意度水平,符合用户预期的显示方式。
+
+## 相关文件
+- web/src/views/hq/satSurvey/index.vue
+
+## 变更详情
+- 将两处 `satisfactionData.overallScore.toFixed(1)` 修改为 `((satisfactionData.overallScore / currentResultSurvey.score) * 100).toFixed(1) + '%'`
+- 保持满意度等级计算规则不变
+
+## 测试情况
+页面显示正常,百分比计算准确。
+
+## 备注
+该变更不影响后端逻辑,仅为前端展示优化。 

+ 135 - 45
web/src/views/hq/satSurvey/index.vue

@@ -205,7 +205,7 @@
                 <el-tooltip content="查看题目" placement="top">
                   <el-button link type="info" icon="View" @click="handleQuestionView(scope.row)" v-hasPermi="['hq:satSurvey:question']"></el-button>
                 </el-tooltip>
-                <!-- <el-tooltip content="调查结论" placement="top">
+                <el-tooltip content="调查结论" placement="top">
                   <el-button
                     link
                     type="primary"
@@ -213,7 +213,7 @@
                     @click="handleSurveyResult(scope.row)"
                     v-hasPermi="['hq:satSurvey:result']"
                   ></el-button>
-                </el-tooltip> -->
+                </el-tooltip>
                 <el-tooltip content="撤销发布" placement="top">
                   <el-button
                     link
@@ -230,7 +230,7 @@
                 <el-tooltip content="查看题目" placement="top">
                   <el-button link type="info" icon="View" @click="handleQuestionView(scope.row)" v-hasPermi="['hq:satSurvey:question']"></el-button>
                 </el-tooltip>
-                <!-- <el-tooltip content="调查结论" placement="top">
+                <el-tooltip content="调查结论" placement="top">
                   <el-button
                     link
                     type="primary"
@@ -238,7 +238,7 @@
                     @click="handleSurveyResult(scope.row)"
                     v-hasPermi="['hq:satSurvey:result']"
                   ></el-button>
-                </el-tooltip> -->
+                </el-tooltip>
               </template>
 
               <!-- 其他状态的默认操作 -->
@@ -546,7 +546,7 @@
           <el-tabs v-model="activeResultTab" type="border-card" class="dialog-tabs">
             <el-tab-pane label="概览统计" name="overview"></el-tab-pane>
             <el-tab-pane label="题目分析" name="questions"></el-tab-pane>
-            <el-tab-pane label="满意度分析" name="satisfaction"></el-tab-pane>
+            <!-- <el-tab-pane label="满意度分析" name="satisfaction"></el-tab-pane> -->
             <el-tab-pane label="答题详情" name="records"></el-tab-pane>
           </el-tabs>
         </div>
@@ -560,6 +560,7 @@
                 <span><strong>问卷编号:</strong>{{ currentResultSurvey.surveyCode }}</span>
                 <span><strong>目标群体:</strong>{{ getTargetGroupName(currentResultSurvey.targetGroup) }}</span>
                 <span><strong>调查时间:</strong>{{ currentResultSurvey.startTime }} ~ {{ currentResultSurvey.endTime }}</span>
+                <span><strong>总分:</strong>{{ currentResultSurvey.score }}</span>
               </div>
             </div>
           </el-card>
@@ -598,7 +599,7 @@
                   <div class="stat-label">回答完成率</div>
                 </div>
               </div>
-              <div class="stat-card info-card">
+              <!-- <div class="stat-card info-card">
                 <div class="stat-icon">
                   <el-icon><Timer /></el-icon>
                 </div>
@@ -606,13 +607,13 @@
                   <div class="stat-value">{{ formatDuration(surveyStatistics.avgDuration) }}</div>
                   <div class="stat-label">平均用时</div>
                 </div>
-              </div>
+              </div> -->
             </div>
 
             <!-- 得分分布 -->
             <el-row :gutter="20" v-if="surveyStatistics">
               <el-col :span="12">
-                <el-card shadow="hover" class="score-card">
+                <el-card shadow="hover" class="score-card" style="height: 280px">
                   <template #header>
                     <h4>
                       <el-icon><DataAnalysis /></el-icon> 得分统计
@@ -635,7 +636,7 @@
                 </el-card>
               </el-col>
               <el-col :span="12">
-                <el-card shadow="hover" class="satisfaction-card">
+                <el-card shadow="hover" class="satisfaction-card" style="height: 280px">
                   <template #header>
                     <h4>
                       <el-icon><Star /></el-icon> 满意度评价
@@ -643,9 +644,19 @@
                   </template>
                   <div class="satisfaction-display" v-if="satisfactionData">
                     <div class="satisfaction-score">
-                      <div class="score-number">{{ satisfactionData.overallScore.toFixed(1) }}</div>
+                      <div class="score-number">{{ ((satisfactionData.overallScore / currentResultSurvey.score) * 100).toFixed(1) }}%</div>
                       <div class="score-level">{{ satisfactionData.satisfactionLevel }}</div>
                     </div>
+                    <!-- 计算规则放在右侧 -->
+                    <div class="satisfaction-rule-card">
+                      <div class="rule-title">满意度等级计算规则:</div>
+                      <ol>
+                        <li>如果平均分占总分比例大于 <b>80%</b>,则满意度等级为 <b>非常满意</b></li>
+                        <li>如果平均分占总分比例大于 <b>70%</b>,则满意度等级为 <b>满意</b></li>
+                        <li>如果平均分占总分比例大于 <b>60%</b>,则满意度等级为 <b>一般</b></li>
+                        <li>否则满意度等级为 <b>不满意</b></li>
+                      </ol>
+                    </div>
                   </div>
                 </el-card>
               </el-col>
@@ -679,9 +690,9 @@
                     >
                   </div>
                   <el-table :data="question.optionStats" border size="small" class="option-table">
-                    <el-table-column prop="optionValue" label="选项" width="80" align="center" />
-                    <el-table-column prop="optionText" label="选项内容" min-width="200" show-overflow-tooltip />
-                    <el-table-column prop="selectCount" label="选择人数" width="100" align="center" />
+                    <el-table-column prop="option_content" label="选项内容" min-width="200" show-overflow-tooltip />
+                    <el-table-column prop="option_score" label="选项分值" width="80" align="center" />
+                    <el-table-column prop="select_count" label="选择人数" width="100" align="center" />
                     <el-table-column prop="selectRate" label="选择率" width="100" align="center">
                       <template #default="scope">
                         <span>{{ (scope.row.selectRate * 100).toFixed(1) }}%</span>
@@ -712,12 +723,12 @@
                   <div class="typical-answers" v-if="question.textAnswers && question.textAnswers.length > 0">
                     <h5>典型回答</h5>
                     <div class="answer-list">
-                      <div v-for="(answer, idx) in question.textAnswers.slice(0, 5)" :key="idx" class="answer-item">
+                      <div v-for="(answer, idx) in question.textAnswers.slice(0, 5)" :key="answer.id || idx" class="answer-item">
                         <div class="answer-meta">
-                          <span class="answer-time">{{ answer.answerTime }}</span>
-                          <span class="answer-length">{{ answer.textLength }}字</span>
+                          <span class="answer-time">{{ answer.answer_time }}</span>
+                          <span class="answer-length">{{ answer.text_length }}字</span>
                         </div>
-                        <div class="answer-content">{{ answer.answerText }}</div>
+                        <div class="answer-content">{{ answer.answer_text }}</div>
                       </div>
                     </div>
                   </div>
@@ -744,7 +755,7 @@
                     </template>
                     <div class="satisfaction-score-display">
                       <div class="score-circle">
-                        <div class="score-number">{{ satisfactionData.overallScore.toFixed(1) }}</div>
+                        <div class="score-number">{{ ((satisfactionData.overallScore / currentResultSurvey.score) * 100).toFixed(1) }}%</div>
                         <div class="score-label">{{ satisfactionData.satisfactionLevel }}</div>
                       </div>
                     </div>
@@ -868,7 +879,7 @@
                 <el-form-item label="答题人">
                   <el-input v-model="recordQuery.userName" placeholder="请输入答题人姓名" clearable />
                 </el-form-item>
-                <el-form-item label="答题时间">
+                <!-- <el-form-item label="答题时间">
                   <el-date-picker
                     v-model="recordQuery.dateRange"
                     type="datetimerange"
@@ -878,7 +889,7 @@
                     format="YYYY-MM-DD HH:mm:ss"
                     value-format="YYYY-MM-DD HH:mm:ss"
                   />
-                </el-form-item>
+                </el-form-item> -->
                 <el-form-item>
                   <el-button type="primary" @click="loadAnswerRecords">查询</el-button>
                   <el-button @click="resetRecordQuery">重置</el-button>
@@ -888,27 +899,29 @@
 
             <!-- 答题记录表格 -->
             <el-table :data="answerRecords" border v-loading="recordLoading" class="records-table">
-              <el-table-column prop="userName" label="答题人" width="120" />
-              <el-table-column prop="deptName" label="部门" width="150" show-overflow-tooltip />
-              <el-table-column prop="startTime" label="开始时间" width="160" />
-              <el-table-column prop="endTime" label="结束时间" width="160" />
-              <el-table-column prop="duration" label="用时" width="100">
+              <el-table-column prop="userName" label="答题人" width="320" />
+              <el-table-column prop="deptName" label="部门" width="300" show-overflow-tooltip />
+              <!-- <el-table-column prop="startTime" label="开始时间" width="200" />
+              <el-table-column prop="endTime" label="结束时间" width="200" /> -->
+              <!-- <el-table-column prop="duration" label="用时" width="100">
                 <template #default="scope">
                   <span>{{ formatDuration(scope.row.duration) }}</span>
                 </template>
-              </el-table-column>
-              <el-table-column prop="totalScore" label="得分" width="80" align="center" />
-              <el-table-column prop="status" label="状态" width="80" align="center">
+              </el-table-column> -->
+              <el-table-column prop="total_score" label="得分" width="180" align="center" />
+              <el-table-column prop="status" label="状态" width="180" align="center">
                 <template #default="scope">
                   <el-tag :type="scope.row.status === '1' ? 'success' : 'warning'" size="small">
                     {{ scope.row.status === '1' ? '已完成' : '草稿' }}
                   </el-tag>
                 </template>
               </el-table-column>
-              <el-table-column prop="submitTime" label="提交时间" width="160" />
-              <el-table-column label="操作" width="120" fixed="right">
+              <el-table-column prop="submit_time" label="提交时间" width="300" />
+              <el-table-column label="操作" width="215" fixed="right">
                 <template #default="scope">
-                  <el-button link type="primary" @click="viewAnswerDetail(scope.row)" size="small"> 查看详情 </el-button>
+                  <el-button v-if="scope.row.status === '1'" link type="primary" @click="viewAnswerDetail(scope.row)" size="small">
+                    查看详情
+                  </el-button>
                 </template>
               </el-table-column>
             </el-table>
@@ -948,10 +961,10 @@
                 <span class="info-label">答题时间:</span>
                 <span class="info-value">{{ currentAnswerRecord.startTime }} ~ {{ currentAnswerRecord.endTime }}</span>
               </div>
-              <div class="info-item">
+              <!-- <div class="info-item">
                 <span class="info-label">用时:</span>
                 <span class="info-value">{{ formatDuration(currentAnswerRecord.duration) }}</span>
-              </div>
+              </div> -->
               <div class="info-item">
                 <span class="info-label">得分:</span>
                 <span class="info-value score">{{ currentAnswerRecord.totalScore }}</span>
@@ -985,7 +998,7 @@
                         type="primary"
                         class="selected-option"
                       >
-                        {{ option.optionValue }}. {{ option.optionText }}
+                        {{ option.optionValue }}({{ option.optionScore }}分)
                       </el-tag>
                     </div>
                     <!-- 问答题显示文本 -->
@@ -1966,7 +1979,8 @@ const loadSurveyResultData = async (surveyId: string | number) => {
     // 从API获取真实数据
     const [statisticsRes, recordsRes] = await Promise.all([
       getSurveyStatistics(surveyId),
-      getSurveyAnswerRecords(surveyId, { status: '1', pageNum: 1, pageSize: 10 })
+      // getSurveyAnswerRecords(surveyId, { status: '1', pageNum: 1, pageSize: 10 })
+      getSurveyAnswerRecords(surveyId, { pageNum: 1, pageSize: 10 })
     ]);
 
     console.log('统计数据:', statisticsRes.data);
@@ -2008,20 +2022,23 @@ const calculateSatisfactionData = () => {
   if (!surveyStatistics.value) return;
 
   const stats = surveyStatistics.value;
-
-  // 计算满意度等级
-  const getSatisfactionLevel = (score: number) => {
-    if (score >= 90) return '非常满意';
-    if (score >= 80) return '满意';
-    if (score >= 70) return '一般';
-    if (score >= 60) return '不满意';
-    return '非常不满意';
+  // 获取总分,优先用currentResultSurvey.value.score,否则用stats.maxScore
+  const totalScore = currentResultSurvey.value?.score || stats.maxScore || 0;
+
+  // 计算满意度等级(按平均分占总分比例)
+  const getSatisfactionLevel = (score: number, total: number) => {
+    if (!total || total === 0) return '无数据';
+    const percent = (score / total) * 100;
+    if (percent > 80) return '非常满意';
+    if (percent > 70) return '满意';
+    if (percent > 60) return '一般';
+    return '不满意';
   };
 
   // 从真实数据中计算满意度分布
   satisfactionData.value = {
     overallScore: stats.avgScore,
-    satisfactionLevel: getSatisfactionLevel(stats.avgScore),
+    satisfactionLevel: getSatisfactionLevel(stats.avgScore, totalScore),
     distribution: stats.satisfactionDistribution || {
       excellent: 0,
       good: 0,
@@ -2794,7 +2811,7 @@ onMounted(() => {
 .overview-content {
   .stat-cards {
     display: grid;
-    grid-template-columns: repeat(4, 1fr);
+    grid-template-columns: repeat(3, 1fr);
     gap: 20px;
     margin-bottom: 24px;
 
@@ -3540,4 +3557,77 @@ onMounted(() => {
     margin: 5px 0;
   }
 }
+
+.satisfaction-rule-card {
+  background: #f8f9fa;
+  border: 1px solid #e4e7ed;
+  border-radius: 8px;
+  padding: 12px 20px;
+  margin-top: 8px;
+  font-size: 14px;
+  color: #606266;
+}
+.satisfaction-rule-card .rule-title {
+  font-weight: bold;
+  margin-bottom: 6px;
+}
+.satisfaction-rule-card ol {
+  margin: 0;
+  padding-left: 20px;
+}
+.satisfaction-rule-card li {
+  margin-bottom: 4px;
+}
+
+/* 满意度展示区样式 */
+.satisfaction-display {
+  display: flex;
+  justify-content: space-between;
+  align-items: flex-start;
+  gap: 20px;
+
+  .satisfaction-score {
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+
+    .score-number {
+      font-size: 36px;
+      font-weight: bold;
+      color: #409eff;
+    }
+
+    .score-level {
+      font-size: 18px;
+      color: #303133;
+      margin-top: 5px;
+    }
+  }
+
+  .satisfaction-rule-card {
+    flex: 1;
+    background-color: #f8f9fa;
+    border-radius: 6px;
+    padding: 12px;
+    border: 1px solid #e4e7ed;
+
+    .rule-title {
+      font-size: 14px;
+      font-weight: 500;
+      color: #606266;
+      margin-bottom: 10px;
+    }
+
+    ol {
+      padding-left: 20px;
+      margin: 0;
+
+      li {
+        font-size: 13px;
+        line-height: 1.6;
+        color: #606266;
+      }
+    }
+  }
+}
 </style>