|
|
@@ -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>
|