Compare commits
No commits in common. "master" and "lm/fix/stlye" have entirely different histories.
master
...
lm/fix/stl
1
.gitignore
vendored
1
.gitignore
vendored
@ -7,4 +7,3 @@ pnpm-debug
|
|||||||
auto-*.d.ts
|
auto-*.d.ts
|
||||||
.idea
|
.idea
|
||||||
.history
|
.history
|
||||||
.omc/
|
|
||||||
|
|||||||
@ -34,8 +34,6 @@ const include = [
|
|||||||
'markmap-toolbar',
|
'markmap-toolbar',
|
||||||
'highlight.js',
|
'highlight.js',
|
||||||
'element-plus',
|
'element-plus',
|
||||||
'html-docx-js-typescript',
|
|
||||||
'file-saver',
|
|
||||||
'element-plus/es',
|
'element-plus/es',
|
||||||
'element-plus/es/locale/lang/zh-cn',
|
'element-plus/es/locale/lang/zh-cn',
|
||||||
'element-plus/es/locale/lang/en',
|
'element-plus/es/locale/lang/en',
|
||||||
|
|||||||
@ -50,8 +50,10 @@
|
|||||||
"echarts-wordcloud": "^2.1.0",
|
"echarts-wordcloud": "^2.1.0",
|
||||||
"element-plus": "2.11.1",
|
"element-plus": "2.11.1",
|
||||||
"fast-xml-parser": "^4.3.2",
|
"fast-xml-parser": "^4.3.2",
|
||||||
|
"file-saver": "^2.0.5",
|
||||||
"highlight.js": "^11.9.0",
|
"highlight.js": "^11.9.0",
|
||||||
"html-docx-js": "^0.3.1",
|
"html-docx-js": "^0.3.1",
|
||||||
|
"html-docx-js-typescript": "^0.1.5",
|
||||||
"jsencrypt": "^3.3.2",
|
"jsencrypt": "^3.3.2",
|
||||||
"jsoneditor": "^10.1.3",
|
"jsoneditor": "^10.1.3",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
@ -113,8 +115,6 @@
|
|||||||
"eslint-plugin-import": "^2.29.1",
|
"eslint-plugin-import": "^2.29.1",
|
||||||
"eslint-plugin-prettier": "^5.1.3",
|
"eslint-plugin-prettier": "^5.1.3",
|
||||||
"eslint-plugin-vue": "^9.22.0",
|
"eslint-plugin-vue": "^9.22.0",
|
||||||
"file-saver": "^2.0.5",
|
|
||||||
"html-docx-js-typescript": "^0.1.5",
|
|
||||||
"lint-staged": "^15.2.2",
|
"lint-staged": "^15.2.2",
|
||||||
"postcss": "^8.4.35",
|
"postcss": "^8.4.35",
|
||||||
"postcss-html": "^1.6.0",
|
"postcss-html": "^1.6.0",
|
||||||
|
|||||||
12
pnpm-lock.yaml
generated
12
pnpm-lock.yaml
generated
@ -83,12 +83,18 @@ importers:
|
|||||||
fast-xml-parser:
|
fast-xml-parser:
|
||||||
specifier: ^4.3.2
|
specifier: ^4.3.2
|
||||||
version: 4.5.0
|
version: 4.5.0
|
||||||
|
file-saver:
|
||||||
|
specifier: ^2.0.5
|
||||||
|
version: 2.0.5
|
||||||
highlight.js:
|
highlight.js:
|
||||||
specifier: ^11.9.0
|
specifier: ^11.9.0
|
||||||
version: 11.10.0
|
version: 11.10.0
|
||||||
html-docx-js:
|
html-docx-js:
|
||||||
specifier: ^0.3.1
|
specifier: ^0.3.1
|
||||||
version: 0.3.1
|
version: 0.3.1
|
||||||
|
html-docx-js-typescript:
|
||||||
|
specifier: ^0.1.5
|
||||||
|
version: 0.1.5
|
||||||
jsencrypt:
|
jsencrypt:
|
||||||
specifier: ^3.3.2
|
specifier: ^3.3.2
|
||||||
version: 3.3.2
|
version: 3.3.2
|
||||||
@ -267,12 +273,6 @@ importers:
|
|||||||
eslint-plugin-vue:
|
eslint-plugin-vue:
|
||||||
specifier: ^9.22.0
|
specifier: ^9.22.0
|
||||||
version: 9.31.0(eslint@8.57.1)
|
version: 9.31.0(eslint@8.57.1)
|
||||||
file-saver:
|
|
||||||
specifier: ^2.0.5
|
|
||||||
version: 2.0.5
|
|
||||||
html-docx-js-typescript:
|
|
||||||
specifier: ^0.1.5
|
|
||||||
version: 0.1.5
|
|
||||||
lint-staged:
|
lint-staged:
|
||||||
specifier: ^15.2.2
|
specifier: ^15.2.2
|
||||||
version: 15.2.10
|
version: 15.2.10
|
||||||
|
|||||||
@ -108,7 +108,7 @@ export const AreaApi = {
|
|||||||
|
|
||||||
/** 批量删除监区信息 */
|
/** 批量删除监区信息 */
|
||||||
deleteAreaList: async (ids: number[]) => {
|
deleteAreaList: async (ids: number[]) => {
|
||||||
return await request.post({ url: `/prison/area/delete-list`, data: ids })
|
return await request.delete({ url: `/prison/area/delete-list?ids=${ids.join(',')}` })
|
||||||
},
|
},
|
||||||
|
|
||||||
// 导出监区信息 Excel
|
// 导出监区信息 Excel
|
||||||
|
|||||||
@ -74,7 +74,7 @@ export const CellApi = {
|
|||||||
|
|
||||||
// 批量删除监室信息
|
// 批量删除监室信息
|
||||||
deleteCellList: async (ids: number[]) => {
|
deleteCellList: async (ids: number[]) => {
|
||||||
return await request.post({ url: '/prison/cell/delete-list', data: ids })
|
return await request.delete({ url: '/prison/cell/delete-list', params: { ids: ids.join(',') } })
|
||||||
},
|
},
|
||||||
|
|
||||||
// 导出监室信息 Excel
|
// 导出监室信息 Excel
|
||||||
|
|||||||
@ -79,7 +79,7 @@ export const ConsumptionApi = {
|
|||||||
|
|
||||||
/** 批量删除消费订单 */
|
/** 批量删除消费订单 */
|
||||||
deleteConsumptionList: async (ids: number[]) => {
|
deleteConsumptionList: async (ids: number[]) => {
|
||||||
return await request.post({ url: `/prison/consumption/delete-list`, data: ids })
|
return await request.delete({ url: `/prison/consumption/delete-list?ids=${ids.join(',')}` })
|
||||||
},
|
},
|
||||||
|
|
||||||
// 查询消费明细列表
|
// 查询消费明细列表
|
||||||
|
|||||||
@ -182,7 +182,7 @@ export const TemplateApi = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
deleteTemplateList: async (ids: number[]) => {
|
deleteTemplateList: async (ids: number[]) => {
|
||||||
return await request.post({ url: '/prison/evaluation-report/template/delete-list', data: ids })
|
return await request.delete({ url: '/prison/evaluation-report/template/delete-list', params: { ids: ids.join(',') } })
|
||||||
},
|
},
|
||||||
|
|
||||||
getEnabledTemplateList: async () => {
|
getEnabledTemplateList: async () => {
|
||||||
@ -220,7 +220,7 @@ export const DimensionApi = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
deleteDimensionList: async (ids: number[]) => {
|
deleteDimensionList: async (ids: number[]) => {
|
||||||
return await request.post({ url: '/prison/evaluation-report/dimension/delete-list', data: ids })
|
return await request.delete({ url: '/prison/evaluation-report/dimension/delete-list', params: { ids: ids.join(',') } })
|
||||||
},
|
},
|
||||||
|
|
||||||
getDimensionsByTemplateId: async (templateId: number) => {
|
getDimensionsByTemplateId: async (templateId: number) => {
|
||||||
@ -262,7 +262,7 @@ export const ReportApi = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
deleteReportList: async (ids: number[]) => {
|
deleteReportList: async (ids: number[]) => {
|
||||||
return await request.post({ url: '/prison/evaluation-report/report/delete-list', data: ids })
|
return await request.delete({ url: '/prison/evaluation-report/report/delete-list', params: { ids: ids.join(',') } })
|
||||||
},
|
},
|
||||||
|
|
||||||
submitReport: async (id: number) => {
|
submitReport: async (id: number) => {
|
||||||
@ -383,7 +383,7 @@ export const CommentApi = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
deleteCommentList: async (ids: number[]) => {
|
deleteCommentList: async (ids: number[]) => {
|
||||||
return await request.post({ url: '/prison/evaluation-report/comment/delete-list', data: ids })
|
return await request.delete({ url: '/prison/evaluation-report/comment/delete-list', params: { ids: ids.join(',') } })
|
||||||
},
|
},
|
||||||
|
|
||||||
getCommentsByTypeAndLevel: async (commentType: number, level?: number) => {
|
getCommentsByTypeAndLevel: async (commentType: number, level?: number) => {
|
||||||
|
|||||||
@ -162,7 +162,7 @@ export const EvaluationTemplateApi = {
|
|||||||
|
|
||||||
// 批量删除模板
|
// 批量删除模板
|
||||||
deleteTemplateList: async (ids: number[]) => {
|
deleteTemplateList: async (ids: number[]) => {
|
||||||
return await request.post({ url: '/prison/evaluation-report/template/delete-list', data: ids })
|
return await request.delete({ url: '/prison/evaluation-report/template/delete-list?ids=' + ids.join(',') })
|
||||||
},
|
},
|
||||||
|
|
||||||
// 获取启用的模板列表
|
// 获取启用的模板列表
|
||||||
@ -216,7 +216,7 @@ export const EvaluationDimensionApi = {
|
|||||||
|
|
||||||
// 批量删除维度
|
// 批量删除维度
|
||||||
deleteDimensionList: async (ids: number[]) => {
|
deleteDimensionList: async (ids: number[]) => {
|
||||||
return await request.post({ url: '/prison/evaluation-report/dimension/delete-list', data: ids })
|
return await request.delete({ url: '/prison/evaluation-report/dimension/delete-list?ids=' + ids.join(',') })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -260,7 +260,7 @@ export const EvaluationReportApi = {
|
|||||||
|
|
||||||
// 批量删除报告
|
// 批量删除报告
|
||||||
deleteReportList: async (ids: number[]) => {
|
deleteReportList: async (ids: number[]) => {
|
||||||
return await request.post({ url: '/prison/evaluation-report/report/delete-list', data: ids })
|
return await request.delete({ url: '/prison/evaluation-report/report/delete-list?ids=' + ids.join(',') })
|
||||||
},
|
},
|
||||||
|
|
||||||
// 提交审核
|
// 提交审核
|
||||||
@ -324,7 +324,7 @@ export const ReportCommentApi = {
|
|||||||
|
|
||||||
// 批量删除评语
|
// 批量删除评语
|
||||||
deleteCommentList: async (ids: number[]) => {
|
deleteCommentList: async (ids: number[]) => {
|
||||||
return await request.post({ url: '/prison/evaluation-report/comment/delete-list', data: ids })
|
return await request.delete({ url: '/prison/evaluation-report/comment/delete-list?ids=' + ids.join(',') })
|
||||||
},
|
},
|
||||||
|
|
||||||
// 获取评语详情
|
// 获取评语详情
|
||||||
|
|||||||
@ -115,7 +115,7 @@ export const PrisonerApi = {
|
|||||||
},
|
},
|
||||||
// 批量删除
|
// 批量删除
|
||||||
deleteList: (ids: number[]) => {
|
deleteList: (ids: number[]) => {
|
||||||
return request.post({ url: '/prison/prisoner/delete-list', data: ids })
|
return request.delete({ url: '/prison/prisoner/delete-list', params: { ids: ids.join(',') } })
|
||||||
},
|
},
|
||||||
// 导出
|
// 导出
|
||||||
export: (params: PageParam) => {
|
export: (params: PageParam) => {
|
||||||
|
|||||||
@ -73,7 +73,7 @@ export const QuestionApi = {
|
|||||||
|
|
||||||
/** 批量删除问卷问题 */
|
/** 批量删除问卷问题 */
|
||||||
deleteQuestionList: async (ids: number[]) => {
|
deleteQuestionList: async (ids: number[]) => {
|
||||||
return await request.post<boolean>({ url: `/prison/question/delete-list`, data: ids })
|
return await request.delete<boolean>({ url: `/prison/question/delete-list`, params: { ids: ids.join(',') } })
|
||||||
},
|
},
|
||||||
|
|
||||||
// 批量更新问卷问题(使用POST方法,与后端一致)
|
// 批量更新问卷问题(使用POST方法,与后端一致)
|
||||||
|
|||||||
@ -108,8 +108,6 @@ export interface TaskAreaStatistics {
|
|||||||
|
|
||||||
/** 人员填写进度 */
|
/** 人员填写进度 */
|
||||||
export interface PrisonerProgress {
|
export interface PrisonerProgress {
|
||||||
questionnaireId: number
|
|
||||||
questionnaireName?: string
|
|
||||||
id: number
|
id: number
|
||||||
prisonerId: number
|
prisonerId: number
|
||||||
prisonerNo: string
|
prisonerNo: string
|
||||||
@ -164,7 +162,7 @@ export const QuestionnaireTaskApi = {
|
|||||||
|
|
||||||
// 批量删除问卷任务
|
// 批量删除问卷任务
|
||||||
deleteQuestionnaireTaskList: async (ids: number[]) => {
|
deleteQuestionnaireTaskList: async (ids: number[]) => {
|
||||||
return await request.post({ url: `/prison/questionnaire-task/delete-list`, data: ids })
|
return await request.delete({ url: `/prison/questionnaire-task/delete-list`, params: { ids: ids.join(',') } })
|
||||||
},
|
},
|
||||||
|
|
||||||
// 导出问卷任务 Excel
|
// 导出问卷任务 Excel
|
||||||
|
|||||||
@ -65,7 +65,7 @@ export const QuestionnaireApi = {
|
|||||||
|
|
||||||
/** 批量删除问卷模板 */
|
/** 批量删除问卷模板 */
|
||||||
deleteQuestionnaireList: async (ids: number[]) => {
|
deleteQuestionnaireList: async (ids: number[]) => {
|
||||||
return await request.post<boolean>({ url: `/prison/questionnaire/delete-list`, data: ids })
|
return await request.delete<boolean>({ url: `/prison/questionnaire/delete-list`, params: { ids: ids.join(',') } })
|
||||||
},
|
},
|
||||||
|
|
||||||
// 导出问卷模板 Excel
|
// 导出问卷模板 Excel
|
||||||
|
|||||||
@ -92,7 +92,7 @@ export const QuickCommentApi = {
|
|||||||
|
|
||||||
// 批量删除评语
|
// 批量删除评语
|
||||||
deleteList: async (ids: number[]) => {
|
deleteList: async (ids: number[]) => {
|
||||||
return await request.post({ url: '/prison/quick-comment/delete-list', data: ids })
|
return await request.delete({ url: '/prison/quick-comment/delete-list?ids=' + ids.join(',') })
|
||||||
},
|
},
|
||||||
|
|
||||||
// 导入评语
|
// 导入评语
|
||||||
|
|||||||
@ -67,7 +67,7 @@ export const ReleaseApi = {
|
|||||||
return request.delete({ url: `/prison/release/delete?id=${id}` })
|
return request.delete({ url: `/prison/release/delete?id=${id}` })
|
||||||
},
|
},
|
||||||
deleteReleaseList: (ids: number[]) => {
|
deleteReleaseList: (ids: number[]) => {
|
||||||
return request.post({ url: '/prison/release/delete-list', data: ids })
|
return request.delete({ url: `/prison/release/delete-list?ids=${ids.join(',')}` })
|
||||||
},
|
},
|
||||||
doRelease: (id: number) => {
|
doRelease: (id: number) => {
|
||||||
return request.post({ url: `/prison/release/do-release?id=${id}` })
|
return request.post({ url: `/prison/release/do-release?id=${id}` })
|
||||||
|
|||||||
@ -113,7 +113,7 @@ export const RiskApi = {
|
|||||||
|
|
||||||
/** 批量删除风险评估 */
|
/** 批量删除风险评估 */
|
||||||
deleteRiskList: async (ids: number[]) => {
|
deleteRiskList: async (ids: number[]) => {
|
||||||
return await request.post({ url: `/prison/risk/delete-list`, data: ids })
|
return await request.delete({ url: `/prison/risk/delete-list?ids=${ids.join(',')}` })
|
||||||
},
|
},
|
||||||
|
|
||||||
// 导出风险评估 Excel
|
// 导出风险评估 Excel
|
||||||
|
|||||||
@ -66,7 +66,7 @@ export const RiskAssessmentApi = {
|
|||||||
|
|
||||||
/** 批量删除危险评估 */
|
/** 批量删除危险评估 */
|
||||||
deleteRiskAssessmentList: async (ids: number[]) => {
|
deleteRiskAssessmentList: async (ids: number[]) => {
|
||||||
return await request.post({ url: `/prison/risk-assessment/delete-list`, data: ids })
|
return await request.delete({ url: `/prison/risk-assessment/delete-list?ids=${ids.join(',')}` })
|
||||||
},
|
},
|
||||||
|
|
||||||
// 导出危险评估 Excel
|
// 导出危险评估 Excel
|
||||||
|
|||||||
@ -58,7 +58,7 @@ export const ScoreDetailApi = {
|
|||||||
return request.delete({ url: `/prison/score-detail/delete?id=${id}` })
|
return request.delete({ url: `/prison/score-detail/delete?id=${id}` })
|
||||||
},
|
},
|
||||||
deleteList: (ids: number[]) => {
|
deleteList: (ids: number[]) => {
|
||||||
return request.post({ url: '/prison/score-detail/delete-list', data: ids })
|
return request.delete({ url: `/prison/score-detail/delete-list?ids=${ids.join(',')}` })
|
||||||
},
|
},
|
||||||
export: (params: ScoreDetailPageReqVO) => {
|
export: (params: ScoreDetailPageReqVO) => {
|
||||||
return request.download({ url: '/prison/score-detail/export-excel', params })
|
return request.download({ url: '/prison/score-detail/export-excel', params })
|
||||||
|
|||||||
@ -56,7 +56,7 @@ export const ScoreRuleApi = {
|
|||||||
return request.delete({ url: `/prison/score-rule/delete?id=${id}` })
|
return request.delete({ url: `/prison/score-rule/delete?id=${id}` })
|
||||||
},
|
},
|
||||||
deleteRuleList: (ids: number[]) => {
|
deleteRuleList: (ids: number[]) => {
|
||||||
return request.post({ url: '/prison/score-rule/delete-list', data: ids })
|
return request.delete({ url: `/prison/score-rule/delete-list?ids=${ids.join(',')}` })
|
||||||
},
|
},
|
||||||
getRuleByCategory: (category: number) => {
|
getRuleByCategory: (category: number) => {
|
||||||
return request.get({ url: `/prison/score-rule/list-by-category?category=${category}` })
|
return request.get({ url: `/prison/score-rule/list-by-category?category=${category}` })
|
||||||
|
|||||||
@ -68,7 +68,7 @@ export const ScoreApi = {
|
|||||||
|
|
||||||
/** 批量删除计分考核 */
|
/** 批量删除计分考核 */
|
||||||
deleteScoreList: async (ids: number[]) => {
|
deleteScoreList: async (ids: number[]) => {
|
||||||
return await request.post({ url: `/prison/score/delete-list`, data: ids })
|
return await request.delete({ url: `/prison/score/delete-list?ids=${ids.join(',')}` })
|
||||||
},
|
},
|
||||||
|
|
||||||
// 导出计分考核 Excel
|
// 导出计分考核 Excel
|
||||||
|
|||||||
@ -106,7 +106,7 @@ export const SituationApi = {
|
|||||||
|
|
||||||
/** 批量删除狱情收集 */
|
/** 批量删除狱情收集 */
|
||||||
deleteSituationList: async (ids: number[]) => {
|
deleteSituationList: async (ids: number[]) => {
|
||||||
return await request.post({ url: `/prison/situation/delete-list`, data: ids })
|
return await request.delete({ url: `/prison/situation/delete-list?ids=${ids.join(',')}` })
|
||||||
},
|
},
|
||||||
|
|
||||||
// 导出狱情收集 Excel
|
// 导出狱情收集 Excel
|
||||||
|
|||||||
@ -161,7 +161,7 @@ export const WarningApi = {
|
|||||||
|
|
||||||
/** 批量删除预警 */
|
/** 批量删除预警 */
|
||||||
deleteWarningList: async (ids: number[]) => {
|
deleteWarningList: async (ids: number[]) => {
|
||||||
return await request.post({ url: `/prison/warning/delete-list`, data: ids })
|
return await request.delete({ url: `/prison/warning/delete-list?ids=${ids.join(',')}` })
|
||||||
},
|
},
|
||||||
|
|
||||||
// 导出预警 Excel
|
// 导出预警 Excel
|
||||||
|
|||||||
@ -92,10 +92,8 @@ const open = async (id: number, prisonerId?: number) => {
|
|||||||
reportId.value = id
|
reportId.value = id
|
||||||
dialogVisible.value = true
|
dialogVisible.value = true
|
||||||
await loadReportDetail(id)
|
await loadReportDetail(id)
|
||||||
// 等待视图渲染完成,避免导出时 DOM 仍为空
|
|
||||||
await nextTick()
|
|
||||||
try {
|
try {
|
||||||
await exportToWord()
|
exportToWord()
|
||||||
} catch {
|
} catch {
|
||||||
} finally {
|
} finally {
|
||||||
handleClose()
|
handleClose()
|
||||||
|
|||||||
@ -32,21 +32,13 @@
|
|||||||
<div class="options-container">
|
<div class="options-container">
|
||||||
<el-radio-group v-model="answers[question.id]" :disabled="disabled">
|
<el-radio-group v-model="answers[question.id]" :disabled="disabled">
|
||||||
<el-radio
|
<el-radio
|
||||||
v-for="(opt, optIdx) in parseOptions(question.options)"
|
v-for="(opt, index) in parseOptions(question.options)"
|
||||||
:key="optIdx"
|
:key="index"
|
||||||
:value="optIdx"
|
:value="index"
|
||||||
>
|
>
|
||||||
{{ opt.label }}
|
{{ opt.label }}
|
||||||
</el-radio>
|
</el-radio>
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
<!-- 其他选项输入框 -->
|
|
||||||
<div v-if="getOtherOptionIndex(question) !== undefined && answers[question.id] === getOtherOptionIndex(question)" class="other-input">
|
|
||||||
<el-input
|
|
||||||
v-model="otherAnswers[question.id]"
|
|
||||||
placeholder="请输入其他选项内容"
|
|
||||||
:disabled="disabled"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -55,25 +47,14 @@
|
|||||||
<div class="options-container">
|
<div class="options-container">
|
||||||
<el-checkbox-group v-model="multiAnswers[question.id]">
|
<el-checkbox-group v-model="multiAnswers[question.id]">
|
||||||
<el-checkbox
|
<el-checkbox
|
||||||
v-for="(opt, optIdx) in parseOptions(question.options)"
|
v-for="(opt, index) in parseOptions(question.options)"
|
||||||
:key="optIdx"
|
:key="index"
|
||||||
:value="optIdx"
|
:value="index"
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
>
|
>
|
||||||
{{ opt.label }}
|
{{ opt.label }}
|
||||||
</el-checkbox>
|
</el-checkbox>
|
||||||
</el-checkbox-group>
|
</el-checkbox-group>
|
||||||
<!-- 其他选项输入框 -->
|
|
||||||
<div
|
|
||||||
v-if="isOtherOptionSelected(question, multiAnswers[question.id])"
|
|
||||||
class="other-input"
|
|
||||||
>
|
|
||||||
<el-input
|
|
||||||
v-model="otherAnswers[question.id]"
|
|
||||||
placeholder="请输入其他选项内容"
|
|
||||||
:disabled="disabled"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -126,6 +107,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, reactive, computed } from 'vue'
|
import { ref, reactive, computed } from 'vue'
|
||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
|
import { QuestionnaireApi } from '@/api/prison/questionnaire'
|
||||||
import { QuestionApi } from '@/api/prison/question'
|
import { QuestionApi } from '@/api/prison/question'
|
||||||
import { QuestionnaireRecordApi } from '@/api/prison/questionnairerecord'
|
import { QuestionnaireRecordApi } from '@/api/prison/questionnairerecord'
|
||||||
|
|
||||||
@ -146,7 +128,6 @@ const questions = ref<any[]>([])
|
|||||||
// 答案
|
// 答案
|
||||||
const answers = reactive<Record<number, any>>({})
|
const answers = reactive<Record<number, any>>({})
|
||||||
const multiAnswers = reactive<Record<number, number[]>>({})
|
const multiAnswers = reactive<Record<number, number[]>>({})
|
||||||
const otherAnswers = reactive<Record<number, string>>({}) // 其他选项输入框的答案
|
|
||||||
|
|
||||||
// 是否禁用
|
// 是否禁用
|
||||||
const disabled = computed(() => submitLoading.value)
|
const disabled = computed(() => submitLoading.value)
|
||||||
@ -161,51 +142,24 @@ const parseOptions = (optionsStr: string) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 获取其他选项的索引 */
|
|
||||||
const getOtherOptionIndex = (question: any): number | undefined => {
|
|
||||||
const options = parseOptions(question.options)
|
|
||||||
return options.findIndex((opt: any) => opt.isOther === true)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 检查多选题的其他选项是否被选中 */
|
|
||||||
const isOtherOptionSelected = (question: any, selectedIndices: number[] | undefined): boolean => {
|
|
||||||
const otherIdx = getOtherOptionIndex(question)
|
|
||||||
if (otherIdx === undefined || otherIdx === -1) return false
|
|
||||||
return selectedIndices?.includes(otherIdx) ?? false
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 打开弹窗 */
|
/** 打开弹窗 */
|
||||||
const open = async (record: any) => {
|
const open = async (record: any) => {
|
||||||
console.log('=== 代填弹窗打开 ===')
|
|
||||||
console.log('传入的record:', record)
|
|
||||||
console.log('record.questionnaireId:', record.questionnaireId)
|
|
||||||
|
|
||||||
dialogVisible.value = true
|
dialogVisible.value = true
|
||||||
recordId.value = record.id
|
recordId.value = record.id
|
||||||
prisonerInfo.value = record
|
prisonerInfo.value = record
|
||||||
questionnaireId.value = record.questionnaireId
|
questionnaireId.value = record.questionnaireId
|
||||||
|
|
||||||
console.log('设置的questionnaireId.value:', questionnaireId.value)
|
|
||||||
|
|
||||||
// 重置答案
|
// 重置答案
|
||||||
Object.keys(answers).forEach(key => delete answers[key])
|
Object.keys(answers).forEach(key => delete answers[key])
|
||||||
Object.keys(multiAnswers).forEach(key => delete multiAnswers[key])
|
Object.keys(multiAnswers).forEach(key => delete multiAnswers[key])
|
||||||
Object.keys(otherAnswers).forEach(key => delete otherAnswers[key])
|
|
||||||
|
|
||||||
loading.value = true
|
loading.value = true
|
||||||
try {
|
try {
|
||||||
// 从后端重新获取最新的记录信息,确保状态是最新的
|
|
||||||
const latestRecord = await QuestionnaireRecordApi.getQuestionnaireRecord(record.id)
|
|
||||||
console.log('获取的最新记录状态:', latestRecord.status)
|
|
||||||
prisonerInfo.value = { ...record, ...latestRecord }
|
|
||||||
|
|
||||||
// 先获取问卷题目总数
|
// 先获取问卷题目总数
|
||||||
const countData = await QuestionApi.getQuestionnaireQuestionList(
|
const countData = await QuestionApi.getQuestionnaireQuestionList(
|
||||||
{ questionnaireId: questionnaireId.value, pageNo: 1, pageSize: 1 }
|
{ questionnaireId: questionnaireId.value, pageNo: 1, pageSize: 1 }
|
||||||
)
|
)
|
||||||
console.log('获取的countData:', countData)
|
|
||||||
const totalCount = countData.total || 0
|
const totalCount = countData.total || 0
|
||||||
console.log('题目总数:', totalCount)
|
|
||||||
|
|
||||||
// 根据总数获取所有题目(后端限制每页最大200条)
|
// 根据总数获取所有题目(后端限制每页最大200条)
|
||||||
const maxPageSize = 200
|
const maxPageSize = 200
|
||||||
@ -214,8 +168,6 @@ const open = async (record: any) => {
|
|||||||
const questionData = await QuestionApi.getQuestionnaireQuestionList(
|
const questionData = await QuestionApi.getQuestionnaireQuestionList(
|
||||||
{ questionnaireId: questionnaireId.value, pageNo: 1, pageSize: pageSize }
|
{ questionnaireId: questionnaireId.value, pageNo: 1, pageSize: pageSize }
|
||||||
)
|
)
|
||||||
console.log('获取的questionData:', questionData)
|
|
||||||
console.log('实际获取到的题目数量:', questionData.list?.length || 0)
|
|
||||||
questions.value = questionData.list || []
|
questions.value = questionData.list || []
|
||||||
|
|
||||||
// 初始化答案结构
|
// 初始化答案结构
|
||||||
@ -256,68 +208,11 @@ const handleSubmit = async () => {
|
|||||||
questionId: q.id
|
questionId: q.id
|
||||||
}
|
}
|
||||||
|
|
||||||
const options = parseOptions(q.options)
|
|
||||||
const otherIndex = getOtherOptionIndex(q)
|
|
||||||
const hasOtherSelected = otherIndex !== undefined && (
|
|
||||||
(q.type === 1 && answers[q.id] === otherIndex) ||
|
|
||||||
(q.type === 2 && multiAnswers[q.id]?.includes(otherIndex))
|
|
||||||
)
|
|
||||||
|
|
||||||
if (q.type === 2) {
|
if (q.type === 2) {
|
||||||
// 多选题:直接使用选项索引作为 optionIds(纯数字)
|
// 多选题
|
||||||
answerItem.optionIds = multiAnswers[q.id] || []
|
answerItem.optionIds = multiAnswers[q.id] || []
|
||||||
// 拼接所有选中的选项文字到 answer 字段
|
} else {
|
||||||
const selectedIndices = multiAnswers[q.id] || []
|
|
||||||
const selectedLabels = selectedIndices.map((idx: number) => {
|
|
||||||
const opt = options[idx]
|
|
||||||
return opt ? (opt.value !== undefined ? opt.value : opt.label) : String(idx)
|
|
||||||
})
|
|
||||||
if (selectedLabels.length > 0) {
|
|
||||||
answerItem.answer = selectedLabels.join('、')
|
|
||||||
}
|
|
||||||
} else if (q.type === 1) {
|
|
||||||
// 单选题:检查是否选择了"其他"选项
|
|
||||||
const selectedIndex = answers[q.id]
|
|
||||||
const otherIndex = getOtherOptionIndex(q)
|
|
||||||
const isOtherSelected = otherIndex !== undefined && selectedIndex === otherIndex
|
|
||||||
|
|
||||||
if (isOtherSelected) {
|
|
||||||
// 选择了"其他"选项,需要发送 optionIds
|
|
||||||
answerItem.optionIds = [selectedIndex]
|
|
||||||
}
|
|
||||||
// 普通单选题不发送 optionIds
|
|
||||||
}
|
|
||||||
// 填空题、评分题、日期题、数字题不发送 optionIds
|
|
||||||
|
|
||||||
// 如果选中了其他选项,将其他输入框的内容通过 answerText 传递
|
|
||||||
if (hasOtherSelected && otherAnswers[q.id]) {
|
|
||||||
const otherOptValue = options[otherIndex] ? (options[otherIndex].value !== undefined ? options[otherIndex].value : options[otherIndex].label) : '其他'
|
|
||||||
const otherText = `${otherOptValue}:${otherAnswers[q.id]}`
|
|
||||||
if (q.type === 2) {
|
|
||||||
// 多选题:追加其他选项的文字(前面已经有选中选项的文字了)
|
|
||||||
answerItem.answer = answerItem.answer
|
|
||||||
? `${answerItem.answer}、${otherText}`
|
|
||||||
: otherText
|
|
||||||
} else {
|
|
||||||
// 单选题:直接设置其他选项的文字
|
|
||||||
answerItem.answer = otherText
|
|
||||||
}
|
|
||||||
} else if (q.type === 2 && answerItem.answer) {
|
|
||||||
// 多选题选中了"其他"选项但没有输入文字,也要显示"其他"选项的文字
|
|
||||||
if (hasOtherSelected) {
|
|
||||||
const otherOptValue = options[otherIndex] ? (options[otherIndex].value !== undefined ? options[otherIndex].value : options[otherIndex].label) : '其他'
|
|
||||||
answerItem.answer = `${answerItem.answer}、${otherOptValue}`
|
|
||||||
}
|
|
||||||
} else if (q.type === 3 || q.type === 4 || q.type === 5 || q.type === 6) {
|
|
||||||
// 填空题、评分题、日期题、数字题直接使用 answerText
|
|
||||||
answerItem.answer = answers[q.id] !== undefined ? String(answers[q.id]) : ''
|
answerItem.answer = answers[q.id] !== undefined ? String(answers[q.id]) : ''
|
||||||
} else if (q.type === 1 && !hasOtherSelected) {
|
|
||||||
// 单选题没有选择其他选项时,将选项值设置为 answerText
|
|
||||||
const selectedIndex = answers[q.id]
|
|
||||||
if (selectedIndex !== undefined && selectedIndex !== null) {
|
|
||||||
const opt = options[selectedIndex]
|
|
||||||
answerItem.answer = opt ? (opt.value !== undefined ? opt.value : opt.label) : String(selectedIndex)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return answerItem
|
return answerItem
|
||||||
@ -432,11 +327,5 @@ defineExpose({ open })
|
|||||||
line-height: 1.6;
|
line-height: 1.6;
|
||||||
word-break: break-word;
|
word-break: break-word;
|
||||||
}
|
}
|
||||||
|
|
||||||
.other-input {
|
|
||||||
margin-top: 12px;
|
|
||||||
margin-left: 24px;
|
|
||||||
width: 300px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -493,9 +493,6 @@ const open = async (id: number) => {
|
|||||||
activeTab.value = 'overview'
|
activeTab.value = 'overview'
|
||||||
loading.value = true
|
loading.value = true
|
||||||
|
|
||||||
// 清空之前的任务数据
|
|
||||||
prisonerProgressList.value = []
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 并行加载数据
|
// 并行加载数据
|
||||||
const [progressData, areaData] = await Promise.all([
|
const [progressData, areaData] = await Promise.all([
|
||||||
|
|||||||
@ -1,118 +1,210 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-dialog
|
<Dialog v-model="dialogVisible" :title="title" width="1000px" :fullscreen="false">
|
||||||
v-model="dialogVisible"
|
|
||||||
:title="`答题详情 - ${recordInfo.prisonerName || ''}`"
|
|
||||||
width="800px"
|
|
||||||
:close-on-click-modal="false"
|
|
||||||
destroy-on-close
|
|
||||||
>
|
|
||||||
<div v-loading="loading" class="answer-detail-dialog">
|
<div v-loading="loading" class="answer-detail-dialog">
|
||||||
<!-- 记录基本信息 -->
|
<!-- 记录基本信息 -->
|
||||||
<el-descriptions :column="3" border class="mb-20px">
|
<div class="record-header">
|
||||||
<el-descriptions-item label="问卷名称">{{ recordInfo.questionnaireName }}</el-descriptions-item>
|
<div class="header-info">
|
||||||
<el-descriptions-item label="罪犯编号">{{ recordInfo.prisonerNo }}</el-descriptions-item>
|
<h3 class="title">{{ recordInfo.questionnaireName }}</h3>
|
||||||
<el-descriptions-item label="完成时间">{{ formatDateTime(recordInfo.createTime) }}</el-descriptions-item>
|
<div class="meta-info">
|
||||||
<el-descriptions-item label="客观分">{{ recordInfo.objectiveScore || 0 }}</el-descriptions-item>
|
<span class="info-item">
|
||||||
<el-descriptions-item label="主观分">{{ recordInfo.subjectiveScore || 0 }}</el-descriptions-item>
|
<Icon icon="ep:user" />
|
||||||
<el-descriptions-item label="总分">{{ recordInfo.totalScore || 0 }}</el-descriptions-item>
|
{{ recordInfo.prisonerName || recordInfo.prisonerNo }}
|
||||||
<el-descriptions-item label="答题用时">{{ formatDuration(recordInfo.duration) }}</el-descriptions-item>
|
</span>
|
||||||
<el-descriptions-item label="及格状态">
|
<span class="info-item">
|
||||||
<el-tag :type="getPassStatusTag(recordInfo.passStatus)" size="small">
|
<Icon icon="ep:clock" />
|
||||||
{{ getPassStatusText(recordInfo.passStatus) }}
|
{{ formatDateTime(recordInfo.createTime) }}
|
||||||
</el-tag>
|
</span>
|
||||||
</el-descriptions-item>
|
<span class="info-item" v-if="recordInfo.duration">
|
||||||
</el-descriptions>
|
<Icon icon="ep:timer" />
|
||||||
|
用时:{{ formatDuration(recordInfo.duration) }}
|
||||||
<!-- 问卷题目 -->
|
</span>
|
||||||
<div v-if="questions.length > 0" class="questionnaire-content">
|
|
||||||
<div
|
|
||||||
v-for="(item, index) in questions"
|
|
||||||
:key="item.id"
|
|
||||||
class="question-item"
|
|
||||||
>
|
|
||||||
<div class="question-header">
|
|
||||||
<span class="question-index">{{ index + 1 }}</span>
|
|
||||||
<span class="question-title">{{ item.title }}</span>
|
|
||||||
<el-tag v-if="item.isRequired" type="danger" size="small" class="required-tag">必填</el-tag>
|
|
||||||
<el-tag v-if="item.score" type="info" size="small">{{ item.score }}分</el-tag>
|
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<!-- 答案展示 -->
|
<div class="score-info">
|
||||||
<div class="answer-area">
|
<div class="score-item">
|
||||||
<!-- 单选题 -->
|
<span class="label">客观分</span>
|
||||||
<div v-if="item.type === 1" class="options-container">
|
<span class="value objective">{{ recordInfo.objectiveScore || 0 }}</span>
|
||||||
<span v-if="getAnswerText(item.id)" class="answer-text">
|
|
||||||
{{ getAnswerText(item.id) }}
|
|
||||||
</span>
|
|
||||||
<span v-else class="empty-answer">未作答</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 多选题 -->
|
|
||||||
<div v-else-if="item.type === 2" class="options-container">
|
|
||||||
<span v-if="getMultiAnswerText(item.id)" class="answer-text">
|
|
||||||
{{ getMultiAnswerText(item.id) }}
|
|
||||||
</span>
|
|
||||||
<span v-else class="empty-answer">未作答</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 填空题 -->
|
|
||||||
<div v-else-if="item.type === 3" class="options-container">
|
|
||||||
<span v-if="getAnswerText(item.id)" class="answer-text">
|
|
||||||
{{ getAnswerText(item.id) }}
|
|
||||||
</span>
|
|
||||||
<span v-else class="empty-answer">未作答</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 评分题 -->
|
|
||||||
<div v-else-if="item.type === 4" class="options-container">
|
|
||||||
<span v-if="getAnswerText(item.id)" class="answer-text">
|
|
||||||
{{ getAnswerText(item.id) }} 分
|
|
||||||
</span>
|
|
||||||
<span v-else class="empty-answer">未作答</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 日期题 -->
|
|
||||||
<div v-else-if="item.type === 5" class="options-container">
|
|
||||||
<span v-if="getAnswerText(item.id)" class="answer-text">
|
|
||||||
{{ getAnswerText(item.id) }}
|
|
||||||
</span>
|
|
||||||
<span v-else class="empty-answer">未作答</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 数字题 -->
|
|
||||||
<div v-else-if="item.type === 6" class="options-container">
|
|
||||||
<span v-if="getAnswerText(item.id)" class="answer-text">
|
|
||||||
{{ getAnswerText(item.id) }}
|
|
||||||
</span>
|
|
||||||
<span v-else class="empty-answer">未作答</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class="score-item">
|
||||||
|
<span class="label">主观分</span>
|
||||||
|
<span class="value subjective">{{ recordInfo.subjectiveScore || 0 }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="score-item total">
|
||||||
|
<span class="label">总分</span>
|
||||||
|
<span class="value">{{ recordInfo.totalScore || 0 }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="score-item" :class="getPassStatusClass(recordInfo.passStatus)">
|
||||||
|
<span class="label">状态</span>
|
||||||
|
<span class="value">{{ getPassStatusText(recordInfo.passStatus) }}</span>
|
||||||
|
</div>
|
||||||
|
<el-button type="primary" link @click="handlePreviewQuestionnaire">
|
||||||
|
<Icon icon="ep:view" class="mr-3px" />查看问卷
|
||||||
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<el-empty v-else description="暂无答题记录" />
|
<!-- 问题列表(按分区显示) -->
|
||||||
|
<div class="questions-container">
|
||||||
|
<template v-for="partition in partitions" :key="partition.name || 'default'">
|
||||||
|
<!-- 分区标题 -->
|
||||||
|
<div v-if="partition.name" class="partition-title">
|
||||||
|
<Icon icon="ep:folder" />
|
||||||
|
{{ partition.name }}
|
||||||
|
<span class="question-count">({{ partition.questions.length }} 道题)</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 问题列表 -->
|
||||||
|
<div class="questions-list">
|
||||||
|
<div
|
||||||
|
v-for="item in partition.questions"
|
||||||
|
:key="item.question.id"
|
||||||
|
class="question-item"
|
||||||
|
>
|
||||||
|
<!-- 问题标题 -->
|
||||||
|
<div class="question-header">
|
||||||
|
<span class="question-index">{{ item.index }}.</span>
|
||||||
|
<span class="question-title">
|
||||||
|
{{ item.question.title }}
|
||||||
|
<el-tag v-if="item.question.isRequired" type="danger" size="small" class="required-tag">
|
||||||
|
必填
|
||||||
|
</el-tag>
|
||||||
|
<el-tag v-if="item.question.score" type="info" size="small">
|
||||||
|
{{ item.question.score }}分
|
||||||
|
</el-tag>
|
||||||
|
<el-tag
|
||||||
|
v-if="item.answer"
|
||||||
|
:type="item.answer.isCorrect ? 'success' : 'danger'"
|
||||||
|
size="small"
|
||||||
|
>
|
||||||
|
{{ item.answer.isCorrect ? '正确' : '错误' }}
|
||||||
|
</el-tag>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 用户答案 -->
|
||||||
|
<div class="answer-section">
|
||||||
|
<div class="answer-label">
|
||||||
|
<Icon icon="ep:edit" />
|
||||||
|
用户答案:
|
||||||
|
</div>
|
||||||
|
<div class="answer-content">
|
||||||
|
<!-- 单选/多选题 -->
|
||||||
|
<template v-if="item.question.type === 1 || item.question.type === 2">
|
||||||
|
<div v-if="item.answer?.answerText" class="selected-options">
|
||||||
|
<el-tag
|
||||||
|
v-for="(opt, idx) in parseAnswerOptions(item.answer.answerText)"
|
||||||
|
:key="idx"
|
||||||
|
type="primary"
|
||||||
|
size="small"
|
||||||
|
class="option-tag"
|
||||||
|
>
|
||||||
|
{{ opt }}
|
||||||
|
</el-tag>
|
||||||
|
</div>
|
||||||
|
<span v-else class="empty-answer">未作答</span>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 填空题 -->
|
||||||
|
<template v-else-if="item.question.type === 3">
|
||||||
|
<span v-if="item.answer?.answerText" class="text-answer">{{ item.answer.answerText }}</span>
|
||||||
|
<span v-else class="empty-answer">未作答</span>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 评分题 -->
|
||||||
|
<template v-else-if="item.question.type === 4">
|
||||||
|
<span v-if="item.answer?.answerText" class="score-answer">
|
||||||
|
<el-tag type="warning">{{ item.answer.answerText }} 分</el-tag>
|
||||||
|
</span>
|
||||||
|
<span v-else class="empty-answer">未作答</span>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 日期题 -->
|
||||||
|
<template v-else-if="item.question.type === 5">
|
||||||
|
<span v-if="item.answer?.answerText" class="date-answer">{{ item.answer.answerText }}</span>
|
||||||
|
<span v-else class="empty-answer">未作答</span>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 数字题 -->
|
||||||
|
<template v-else-if="item.question.type === 6">
|
||||||
|
<span v-if="item.answer?.answerText" class="number-answer">{{ item.answer.answerText }}</span>
|
||||||
|
<span v-else class="empty-answer">未作答</span>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 得分信息 -->
|
||||||
|
<div v-if="item.answer?.score !== undefined" class="score-section">
|
||||||
|
<span class="score-label">
|
||||||
|
<Icon icon="ep:coin" />
|
||||||
|
得分:
|
||||||
|
</span>
|
||||||
|
<span class="score-value" :class="{ 'full-score': item.answer.score === item.question.score }">
|
||||||
|
{{ item.answer.score }} / {{ item.question.score || 0 }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 正确答案(客观题) -->
|
||||||
|
<div v-if="showCorrectAnswer(item.question, item.answer)" class="correct-answer-section">
|
||||||
|
<span class="correct-label">
|
||||||
|
<Icon icon="ep:check" />
|
||||||
|
正确答案:
|
||||||
|
</span>
|
||||||
|
<span class="correct-content">{{ getCorrectAnswer(item.question) }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 答题用时 -->
|
||||||
|
<div v-if="item.answer?.duration" class="duration-section">
|
||||||
|
<Icon icon="ep:timer" />
|
||||||
|
答题用时:{{ formatDuration(item.answer.duration) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 空状态 -->
|
||||||
|
<el-empty v-if="partitions.length === 0 && !loading" description="暂无答题记录" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 问卷预览弹窗 -->
|
||||||
|
<el-dialog
|
||||||
|
v-model="previewVisible"
|
||||||
|
title="问卷预览"
|
||||||
|
width="800px"
|
||||||
|
destroy-on-close
|
||||||
|
>
|
||||||
|
<QuestionnairePreview :id="previewQuestionnaireId" />
|
||||||
|
<template #footer>
|
||||||
|
<el-button @click="previewVisible = false">关闭</el-button>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<el-button @click="dialogVisible = false">关闭</el-button>
|
<el-button @click="dialogVisible = false">关 闭</el-button>
|
||||||
</template>
|
</template>
|
||||||
</el-dialog>
|
</Dialog>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from 'vue'
|
import { ref, computed } from 'vue'
|
||||||
import { DICT_TYPE } from '@/utils/dict'
|
import { DICT_TYPE } from '@/utils/dict'
|
||||||
import { formatDateTime } from '@/utils/formatTime'
|
import { formatDateTime } from '@/utils/formatTime'
|
||||||
import { QuestionnaireRecordApi, type QuestionnaireRecord } from '@/api/prison/questionnairerecord'
|
import { QuestionnaireRecordApi, type QuestionnaireRecord } from '@/api/prison/questionnairerecord'
|
||||||
import { QuestionApi, type Question } from '@/api/prison/question'
|
import { QuestionApi, type Question } from '@/api/prison/question'
|
||||||
import { AnswerApi, type Answer } from '@/api/prison/answer'
|
import { AnswerApi, type Answer } from '@/api/prison/answer'
|
||||||
|
import QuestionnairePreview from '@/views/prison/questionnaire/components/QuestionnairePreview.vue'
|
||||||
import { getIntDictOptions } from '@/utils/dict'
|
import { getIntDictOptions } from '@/utils/dict'
|
||||||
|
|
||||||
defineOptions({ name: 'AnswerDetailDialog' })
|
defineOptions({ name: 'AnswerDetailDialog' })
|
||||||
|
|
||||||
const dialogVisible = ref(false)
|
const dialogVisible = ref(false)
|
||||||
|
const title = ref('答题详情')
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
|
|
||||||
|
// 预览相关
|
||||||
|
const previewVisible = ref(false)
|
||||||
|
const previewQuestionnaireId = ref<number>()
|
||||||
|
|
||||||
// 记录信息
|
// 记录信息
|
||||||
const recordInfo = ref<QuestionnaireRecord>({
|
const recordInfo = ref<QuestionnaireRecord>({
|
||||||
id: undefined,
|
id: undefined,
|
||||||
@ -133,19 +225,69 @@ const questions = ref<Question[]>([])
|
|||||||
// 答案列表
|
// 答案列表
|
||||||
const answers = ref<Answer[]>([])
|
const answers = ref<Answer[]>([])
|
||||||
|
|
||||||
/** 格式化时长 */
|
// 分区列表(带答案)
|
||||||
const formatDuration = (seconds: number | undefined): string => {
|
interface QuestionWithAnswer {
|
||||||
if (!seconds) return '-'
|
question: Question
|
||||||
const hours = Math.floor(seconds / 3600)
|
answer?: Answer
|
||||||
const minutes = Math.floor((seconds % 3600) / 60)
|
index: number
|
||||||
const secs = seconds % 60
|
}
|
||||||
if (hours > 0) {
|
|
||||||
return `${hours}时${minutes}分${secs}秒`
|
const partitions = computed(() => {
|
||||||
} else if (minutes > 0) {
|
const partMap = new Map<string, QuestionWithAnswer[]>()
|
||||||
return `${minutes}分${secs}秒`
|
|
||||||
} else {
|
questions.value.forEach((q, index) => {
|
||||||
return `${secs}秒`
|
const partName = q.partName || ''
|
||||||
|
const answer = answers.value.find(a => a.questionId === q.id)
|
||||||
|
|
||||||
|
if (!partMap.has(partName)) {
|
||||||
|
partMap.set(partName, [])
|
||||||
|
}
|
||||||
|
partMap.get(partName)!.push({
|
||||||
|
question: q,
|
||||||
|
answer,
|
||||||
|
index: index + 1
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// 按分区排序
|
||||||
|
const sortedParts = Array.from(partMap.entries())
|
||||||
|
.sort((a, b) => {
|
||||||
|
const sortA = a[1][0]?.question.partSort ?? 0
|
||||||
|
const sortB = b[1][0]?.question.partSort ?? 0
|
||||||
|
return sortA - sortB
|
||||||
|
})
|
||||||
|
|
||||||
|
// 构建分区列表
|
||||||
|
const result: Array<{ name: string; questions: QuestionWithAnswer[] }> = []
|
||||||
|
|
||||||
|
// 添加默认分区
|
||||||
|
const defaultQuestions = sortedParts.find(([name]) => !name)
|
||||||
|
if (defaultQuestions) {
|
||||||
|
result.push({
|
||||||
|
name: '',
|
||||||
|
questions: defaultQuestions[1]
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 添加其他分区
|
||||||
|
sortedParts
|
||||||
|
.filter(([name]) => name)
|
||||||
|
.forEach(([name, qs]) => {
|
||||||
|
result.push({
|
||||||
|
name,
|
||||||
|
questions: qs
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
return result
|
||||||
|
})
|
||||||
|
|
||||||
|
/** 格式化时长 */
|
||||||
|
const formatDuration = (seconds: number): string => {
|
||||||
|
if (!seconds) return '-'
|
||||||
|
const minutes = Math.floor(seconds / 60)
|
||||||
|
const secs = seconds % 60
|
||||||
|
return minutes > 0 ? `${minutes}分${secs}秒` : `${secs}秒`
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 获取及格状态文本 */
|
/** 获取及格状态文本 */
|
||||||
@ -154,55 +296,52 @@ const getPassStatusText = (status: number | undefined) => {
|
|||||||
return options.find(o => o.value === status)?.label || '-'
|
return options.find(o => o.value === status)?.label || '-'
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 获取及格状态标签类型 */
|
/** 获取及格状态样式 */
|
||||||
const getPassStatusTag = (status: number | undefined): 'success' | 'danger' | 'warning' | 'info' => {
|
const getPassStatusClass = (status: number | undefined) => {
|
||||||
const tagMap: Record<number, 'success' | 'danger' | 'warning' | 'info'> = {
|
const classMap: Record<number, string> = {
|
||||||
1: 'success',
|
1: 'pass',
|
||||||
2: 'danger',
|
2: 'fail',
|
||||||
3: 'warning'
|
3: 'pending'
|
||||||
}
|
}
|
||||||
return tagMap[status || 0] || 'info'
|
return classMap[status || 0] || ''
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 获取单选/填空/评分/日期/数字题的答案文本 */
|
/** 解析答案选项(单选/多选) */
|
||||||
const getAnswerText = (questionId: number | undefined): string | null => {
|
const parseAnswerOptions = (answerText: string): string[] => {
|
||||||
if (questionId === undefined) return null
|
if (!answerText) return []
|
||||||
const answer = answers.value.find(a => a.questionId === questionId)
|
// 假设答案用逗号分隔(多选)或单个值(单选)
|
||||||
return answer?.answerText || null
|
return answerText.split(',').filter(s => s.trim())
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 获取多选题的答案文本 */
|
/** 判断是否显示正确答案 */
|
||||||
const getMultiAnswerText = (questionId: number | undefined): string | null => {
|
const showCorrectAnswer = (question: Question, answer?: Answer): boolean => {
|
||||||
if (questionId === undefined) return null
|
// 仅客观题显示正确答案
|
||||||
const answer = answers.value.find(a => a.questionId === questionId)
|
if (question.type !== 1 && question.type !== 2) return false
|
||||||
if (!answer?.optionIds || !Array.isArray(answer.optionIds) || answer.optionIds.length === 0) {
|
// 必须有答案记录
|
||||||
return null
|
if (!answer) return false
|
||||||
}
|
return true
|
||||||
|
}
|
||||||
// 找到对应的问题
|
|
||||||
const question = questions.value.find(q => q.id === questionId)
|
|
||||||
if (!question?.options) return null
|
|
||||||
|
|
||||||
|
/** 获取正确答案 */
|
||||||
|
const getCorrectAnswer = (question: Question): string => {
|
||||||
|
if (!question.options) return '-'
|
||||||
try {
|
try {
|
||||||
// 解析选项
|
const options = JSON.parse(question.options)
|
||||||
const options = JSON.parse(question.options) as Array<{ label: string; value?: string; isOther?: boolean }>
|
// 找到分值最高的选项作为正确答案
|
||||||
|
const correctOptions = options
|
||||||
|
.filter((o: any) => o.score > 0)
|
||||||
|
.map((o: any) => o.label)
|
||||||
|
|
||||||
// 根据 optionIds(索引数组)获取对应的选项文字
|
return correctOptions.length > 0 ? correctOptions.join('、') : '-'
|
||||||
const selectedLabels = answer.optionIds
|
} catch {
|
||||||
.map((idx: number) => options[idx])
|
return '-'
|
||||||
.filter((opt): opt is { label: string } => !!opt)
|
|
||||||
.map(opt => opt.label)
|
|
||||||
|
|
||||||
return selectedLabels.length > 0 ? selectedLabels.join('、') : null
|
|
||||||
} catch (e) {
|
|
||||||
console.error('解析选项失败:', e)
|
|
||||||
return null
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 打开弹窗 */
|
/** 打开弹窗 */
|
||||||
const open = async (recordId: number) => {
|
const open = async (recordId: number) => {
|
||||||
dialogVisible.value = true
|
dialogVisible.value = true
|
||||||
|
title.value = '答题详情'
|
||||||
loading.value = true
|
loading.value = true
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -231,89 +370,296 @@ const open = async (recordId: number) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 预览问卷 */
|
||||||
|
const handlePreviewQuestionnaire = () => {
|
||||||
|
if (!recordInfo.value.questionnaireId) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
previewQuestionnaireId.value = recordInfo.value.questionnaireId
|
||||||
|
previewVisible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
defineExpose({ open })
|
defineExpose({ open })
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style scoped lang="scss">
|
||||||
.answer-detail-dialog {
|
.answer-detail-dialog {
|
||||||
|
max-height: 70vh;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
max-height: 55vh;
|
padding: 20px;
|
||||||
padding-right: 8px;
|
background: #f5f7fa;
|
||||||
|
}
|
||||||
|
|
||||||
&::-webkit-scrollbar {
|
.record-header {
|
||||||
width: 8px;
|
background: #fff;
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 8px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
|
||||||
|
|
||||||
|
.header-info {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
|
||||||
|
.title {
|
||||||
|
margin: 0 0 12px 0;
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #303133;
|
||||||
|
}
|
||||||
|
|
||||||
|
.meta-info {
|
||||||
|
display: flex;
|
||||||
|
gap: 16px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
|
||||||
|
.info-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
color: #606266;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&::-webkit-scrollbar-thumb {
|
.score-info {
|
||||||
background-color: #e4e7ed;
|
display: flex;
|
||||||
border-radius: 4px;
|
gap: 16px;
|
||||||
|
padding-top: 16px;
|
||||||
|
border-top: 1px solid #ebeef5;
|
||||||
|
|
||||||
|
.score-item {
|
||||||
|
flex: 1;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
.label {
|
||||||
|
display: block;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #909399;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.value {
|
||||||
|
display: block;
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #303133;
|
||||||
|
|
||||||
|
&.objective {
|
||||||
|
color: #67c23a;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.subjective {
|
||||||
|
color: #e6a23c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.total {
|
||||||
|
.value {
|
||||||
|
color: #409eff;
|
||||||
|
font-size: 24px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.pass .value {
|
||||||
|
color: #67c23a;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.fail .value {
|
||||||
|
color: #f56c6c;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.pending .value {
|
||||||
|
color: #e6a23c;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.questionnaire-content {
|
.questions-container {
|
||||||
overflow-y: auto;
|
.partition-title {
|
||||||
max-height: 50vh;
|
background: linear-gradient(135deg, #e8f4fd 0%, #f0f7ff 100%);
|
||||||
padding-right: 8px;
|
padding: 12px 20px;
|
||||||
|
border-radius: 8px;
|
||||||
&::-webkit-scrollbar {
|
margin-bottom: 16px;
|
||||||
width: 8px;
|
margin-top: 24px;
|
||||||
}
|
font-weight: 600;
|
||||||
|
color: #1a5cb8;
|
||||||
&::-webkit-scrollbar-thumb {
|
font-size: 16px;
|
||||||
background-color: #e4e7ed;
|
|
||||||
border-radius: 4px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.question-item {
|
|
||||||
margin-bottom: 24px;
|
|
||||||
|
|
||||||
.question-header {
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
margin-bottom: 12px;
|
|
||||||
|
|
||||||
.question-index {
|
&:first-child {
|
||||||
flex-shrink: 0;
|
margin-top: 0;
|
||||||
width: 24px;
|
|
||||||
height: 24px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
background-color: #409eff;
|
|
||||||
color: #fff;
|
|
||||||
border-radius: 50%;
|
|
||||||
font-size: 13px;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.question-title {
|
.question-count {
|
||||||
font-size: 15px;
|
font-size: 12px;
|
||||||
font-weight: 500;
|
color: #909399;
|
||||||
color: #303133;
|
font-weight: normal;
|
||||||
|
margin-left: 4px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.answer-area {
|
.questions-list {
|
||||||
padding-left: 32px;
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
.options-container {
|
.question-item {
|
||||||
|
background: #fff;
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
|
||||||
|
|
||||||
|
.question-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
align-items: flex-start;
|
||||||
|
gap: 8px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
|
||||||
|
.question-index {
|
||||||
|
flex-shrink: 0;
|
||||||
|
width: 28px;
|
||||||
|
height: 28px;
|
||||||
|
background: #409eff;
|
||||||
|
color: #fff;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.question-title {
|
||||||
|
flex: 1;
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #303133;
|
||||||
|
line-height: 1.6;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.answer-section {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
|
padding: 12px 16px;
|
||||||
|
background: #f4f4f5;
|
||||||
|
border-radius: 6px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
|
||||||
|
.answer-label {
|
||||||
|
flex-shrink: 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
font-size: 13px;
|
||||||
|
color: #606266;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.answer-content {
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
|
.selected-options {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
.option-tag {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-answer,
|
||||||
|
.date-answer,
|
||||||
|
.number-answer {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #303133;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.score-answer {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-answer {
|
||||||
|
color: #c0c4cc;
|
||||||
|
font-size: 14px;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.answer-text {
|
.score-section {
|
||||||
font-size: 14px;
|
display: flex;
|
||||||
color: #303133;
|
align-items: center;
|
||||||
line-height: 1.6;
|
gap: 8px;
|
||||||
|
padding: 8px 16px;
|
||||||
|
background: #ecf5ff;
|
||||||
|
border-radius: 6px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
|
||||||
|
.score-label {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #409eff;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.score-value {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #409eff;
|
||||||
|
|
||||||
|
&.full-score {
|
||||||
|
color: #67c23a;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.empty-answer {
|
.correct-answer-section {
|
||||||
color: #c0c4cc;
|
display: flex;
|
||||||
font-size: 14px;
|
align-items: center;
|
||||||
font-style: italic;
|
gap: 8px;
|
||||||
|
padding: 8px 16px;
|
||||||
|
background: #f0f9eb;
|
||||||
|
border-radius: 6px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
|
||||||
|
.correct-label {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #67c23a;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.correct-content {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #67c23a;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.duration-section {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #909399;
|
||||||
|
padding: 4px 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -283,60 +283,12 @@ const handleSubmit = async () => {
|
|||||||
// 构建答案列表
|
// 构建答案列表
|
||||||
const answerList = questions.value.map(q => {
|
const answerList = questions.value.map(q => {
|
||||||
const answer = answers.value[q.id!]
|
const answer = answers.value[q.id!]
|
||||||
|
// 多选时用逗号分隔
|
||||||
// 处理多选题(type === 2)
|
const answerStr = Array.isArray(answer) ? answer.join(',') : answer
|
||||||
if (q.type === 2 && Array.isArray(answer)) {
|
|
||||||
// 解析选项获取文字
|
|
||||||
let optionLabels: string[] = []
|
|
||||||
try {
|
|
||||||
const options = JSON.parse(q.options || '[]') as Array<{ label: string }>
|
|
||||||
optionLabels = answer
|
|
||||||
.map((idx: string) => options[parseInt(idx)]?.label)
|
|
||||||
.filter((label): label is string => !!label)
|
|
||||||
} catch (e) {
|
|
||||||
console.error('解析选项失败:', e)
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
questionId: q.id!,
|
|
||||||
answer: optionLabels.join('、') || '',
|
|
||||||
optionIds: answer.map((idx: string) => parseInt(idx, 10))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 单选题(type === 1):如果选择了"其他"选项,需要发送 optionIds
|
|
||||||
if (q.type === 1 && answer) {
|
|
||||||
const answerIndex = parseInt(answer as string, 10)
|
|
||||||
let isOtherOption = false
|
|
||||||
|
|
||||||
try {
|
|
||||||
const options = JSON.parse(q.options || '[]') as Array<{ isOther?: boolean }>
|
|
||||||
isOtherOption = options[answerIndex]?.isOther === true
|
|
||||||
} catch (e) {
|
|
||||||
console.error('解析选项失败:', e)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 解析选项获取文字
|
|
||||||
let answerText = answer as string
|
|
||||||
try {
|
|
||||||
const options = JSON.parse(q.options || '[]') as Array<{ label: string }>
|
|
||||||
answerText = options[answerIndex]?.label || answer as string
|
|
||||||
} catch (e) {
|
|
||||||
console.error('解析选项失败:', e)
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
questionId: q.id!,
|
|
||||||
answer: answerText,
|
|
||||||
optionIds: isOtherOption ? [answerIndex] : undefined
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 填空题、评分题、日期题、数字题(type 3/4/5/6)
|
|
||||||
return {
|
return {
|
||||||
questionId: q.id!,
|
questionId: q.id!,
|
||||||
answer: String(answer || ''),
|
answer: answerStr || '',
|
||||||
optionIds: undefined
|
optionIds: undefined as number[] | undefined
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user