|
|
|
|
@ -264,16 +264,25 @@
|
|
|
|
|
</div>
|
|
|
|
|
<div class="answer-content">
|
|
|
|
|
<div v-if="getAnswerForQuestion(q.id) !== null && getAnswerForQuestion(q.id) !== undefined">
|
|
|
|
|
<!-- 单选题 -->
|
|
|
|
|
<div v-if="q.edit_input === 'radio'">
|
|
|
|
|
<span class="answer-value">{{ getAnswerForQuestion(q.id) || '未填写' }}</span>
|
|
|
|
|
<!-- 单选题:列出所有选项,标记选中的 -->
|
|
|
|
|
<div v-if="q.edit_input === 'radio'" class="options-list">
|
|
|
|
|
<div
|
|
|
|
|
v-for="opt in (q.select_item || [])"
|
|
|
|
|
:key="opt.value || opt.key"
|
|
|
|
|
:class="['option-item', { 'option-selected': isOptionSelected(q.id, opt, 'radio') }]">
|
|
|
|
|
<span class="option-label">{{ opt.value || opt.key }}</span>
|
|
|
|
|
<span v-if="isOptionSelected(q.id, opt, 'radio')" class="option-check"><i class="el-icon-check"></i> 已选</span>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<!-- 多选题 -->
|
|
|
|
|
<div v-else-if="q.edit_input === 'checkbox'">
|
|
|
|
|
<span v-if="Array.isArray(getAnswerForQuestion(q.id))" class="answer-value">
|
|
|
|
|
{{ getAnswerForQuestion(q.id).join('、') || '未填写' }}
|
|
|
|
|
</span>
|
|
|
|
|
<span v-else class="answer-value">{{ getAnswerForQuestion(q.id) || '未填写' }}</span>
|
|
|
|
|
<!-- 多选题:列出所有选项,标记选中的 -->
|
|
|
|
|
<div v-else-if="q.edit_input === 'checkbox'" class="options-list">
|
|
|
|
|
<div
|
|
|
|
|
v-for="opt in (q.select_item || [])"
|
|
|
|
|
:key="opt.value || opt.key"
|
|
|
|
|
:class="['option-item', { 'option-selected': isOptionSelected(q.id, opt, 'checkbox') }]">
|
|
|
|
|
<span class="option-label">{{ opt.value || opt.key }}</span>
|
|
|
|
|
<span v-if="isOptionSelected(q.id, opt, 'checkbox')" class="option-check"><i class="el-icon-check"></i> 已选</span>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<!-- 多维度题 -->
|
|
|
|
|
<div v-else-if="q.edit_input === 'multi_dimension'">
|
|
|
|
|
@ -406,16 +415,17 @@ export default {
|
|
|
|
|
async viewResponseDetail(response) {
|
|
|
|
|
this.responsesLoading = true
|
|
|
|
|
try {
|
|
|
|
|
// 获取详情
|
|
|
|
|
const res = await formResponseShow({
|
|
|
|
|
id: response.id,
|
|
|
|
|
show_relation: ['user']
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
this.currentResponse = res || response
|
|
|
|
|
// 解析答案数据
|
|
|
|
|
}, false)
|
|
|
|
|
// 表单记录含 user,答案为 data。若后端返回 { data: record } 则取 res.data
|
|
|
|
|
const record = (res && res.user) ? res : ((res && res.data && res.data.user) ? res.data : (res || response))
|
|
|
|
|
this.currentResponse = record
|
|
|
|
|
this.parseResponseAnswers(this.currentResponse)
|
|
|
|
|
this.detailDialogVisible = true
|
|
|
|
|
this.$nextTick(() => {
|
|
|
|
|
this.detailDialogVisible = true
|
|
|
|
|
})
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('获取回复详情失败:', error)
|
|
|
|
|
this.$message.error('获取回复详情失败,请重试')
|
|
|
|
|
@ -433,7 +443,6 @@ export default {
|
|
|
|
|
|
|
|
|
|
let answerData = null
|
|
|
|
|
try {
|
|
|
|
|
// 尝试解析 JSON 字符串
|
|
|
|
|
if (typeof response.data === 'string') {
|
|
|
|
|
answerData = JSON.parse(response.data)
|
|
|
|
|
} else {
|
|
|
|
|
@ -448,22 +457,44 @@ export default {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 将答案数据转换为以题目ID为键的对象
|
|
|
|
|
// 若 API 返回的是题目数组,转为 id -> 答案 的映射
|
|
|
|
|
let dataByQId = {}
|
|
|
|
|
if (Array.isArray(answerData)) {
|
|
|
|
|
answerData.forEach(item => {
|
|
|
|
|
if (item && item.id != null) {
|
|
|
|
|
dataByQId[item.id] = item
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
} else {
|
|
|
|
|
dataByQId = answerData
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 提取答案值:API 可能返回完整题目对象 { id, name, value, select_item... },需取 value
|
|
|
|
|
const extractAnswer = (raw, q) => {
|
|
|
|
|
if (raw === undefined || raw === null) return null
|
|
|
|
|
// 若为完整题目对象(含 value 且含 edit_input/select_item),取 value
|
|
|
|
|
if (typeof raw === 'object' && raw.value !== undefined && (raw.edit_input || raw.select_item)) {
|
|
|
|
|
return raw.value
|
|
|
|
|
}
|
|
|
|
|
return raw
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this.questions.forEach(q => {
|
|
|
|
|
if (answerData[q.id] !== undefined && answerData[q.id] !== null) {
|
|
|
|
|
// 如果是多维度题目,需要特殊处理
|
|
|
|
|
const raw = dataByQId[q.id]
|
|
|
|
|
if (raw !== undefined && raw !== null) {
|
|
|
|
|
if (q.edit_input === 'multi_dimension') {
|
|
|
|
|
let dimensionValue = answerData[q.id]
|
|
|
|
|
let dimensionValue = raw
|
|
|
|
|
if (typeof raw === 'object' && raw.value !== undefined && (raw.edit_input || raw.select_item)) {
|
|
|
|
|
dimensionValue = raw.value
|
|
|
|
|
}
|
|
|
|
|
if (typeof dimensionValue === 'string') {
|
|
|
|
|
try {
|
|
|
|
|
dimensionValue = JSON.parse(dimensionValue)
|
|
|
|
|
} catch (e) {
|
|
|
|
|
console.error('解析多维度答案失败:', e)
|
|
|
|
|
}
|
|
|
|
|
} catch (e) {}
|
|
|
|
|
}
|
|
|
|
|
this.$set(this.responseAnswers, q.id, dimensionValue || {})
|
|
|
|
|
this.$set(this.responseAnswers, q.id, dimensionValue && typeof dimensionValue === 'object' ? dimensionValue : {})
|
|
|
|
|
} else {
|
|
|
|
|
this.$set(this.responseAnswers, q.id, answerData[q.id])
|
|
|
|
|
this.$set(this.responseAnswers, q.id, extractAnswer(raw, q))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
@ -471,6 +502,19 @@ export default {
|
|
|
|
|
getAnswerForQuestion(questionId) {
|
|
|
|
|
return this.responseAnswers[questionId] !== undefined ? this.responseAnswers[questionId] : null
|
|
|
|
|
},
|
|
|
|
|
isOptionSelected(questionId, opt, type) {
|
|
|
|
|
const ans = this.getAnswerForQuestion(questionId)
|
|
|
|
|
if (ans == null) return false
|
|
|
|
|
const optVal = opt.value || opt.key || ''
|
|
|
|
|
if (type === 'radio') {
|
|
|
|
|
return String(ans).trim() === String(optVal).trim()
|
|
|
|
|
}
|
|
|
|
|
if (type === 'checkbox') {
|
|
|
|
|
const selected = Array.isArray(ans) ? ans : (typeof ans === 'string' ? ans.split(/[,,]/).map(s => s.trim()) : [])
|
|
|
|
|
return selected.some(v => String(v).trim() === String(optVal).trim())
|
|
|
|
|
}
|
|
|
|
|
return false
|
|
|
|
|
},
|
|
|
|
|
getQuestionTypeText(type) {
|
|
|
|
|
const typeMap = {
|
|
|
|
|
'radio': '单选题',
|
|
|
|
|
@ -794,6 +838,36 @@ export default {
|
|
|
|
|
color: #303133;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.options-list {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 8px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.option-item {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
padding: 8px 12px;
|
|
|
|
|
border-radius: 6px;
|
|
|
|
|
background: #f5f7fa;
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.option-item.option-selected {
|
|
|
|
|
background: #ecf5ff;
|
|
|
|
|
border-left: 3px solid #409eff;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.option-label {
|
|
|
|
|
color: #303133;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.option-check {
|
|
|
|
|
color: #409eff;
|
|
|
|
|
font-size: 13px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.answer-empty {
|
|
|
|
|
color: #c0c4cc;
|
|
|
|
|
font-style: italic;
|
|
|
|
|
|