Merge branch 'lm/fix/stlye'
This commit is contained in:
commit
e8aefff16b
@ -1,20 +1,41 @@
|
||||
<template>
|
||||
<Dialog style="display: none;" :title="'评估报告'" v-model="dialogVisible" width="900px">
|
||||
<Dialog style="display: none" :title="'评估报告'" v-model="dialogVisible" width="900px">
|
||||
<div v-loading="loading" class="report-edit-container" ref="previewRef">
|
||||
<template v-if="selectedReport">
|
||||
<div class="basic-info-title">{{ selectedReport.templateName }}</div>
|
||||
<!-- 基本信息区 -->
|
||||
<div class="basic-info-section">
|
||||
<div class="basic-info-item">服刑人员:{{ selectedReport.prisonerName }} ({{ selectedReport.prisonerNo }})</div>
|
||||
<div class="basic-info-item"
|
||||
>服刑人员:{{ selectedReport.prisonerName }} ({{ selectedReport.prisonerNo }})</div
|
||||
>
|
||||
<div class="basic-info-item">监区:{{ selectedReport.areaName || '-' }}</div>
|
||||
<div class="basic-info-item">评估日期:{{ formatDateTime(selectedReport.evaluationDate, 'YYYY-MM-DD') }}</div>
|
||||
<div class="basic-info-item">风险等级:{{ getDictLabel(DICT_TYPE.PRISON_RISK_LEVEL, selectedReport.riskLevel) }}</div>
|
||||
<div class="basic-info-item">状态:{{ getDictLabel(DICT_TYPE.PRISON_REPORT_STATUS, selectedReport.status) }}</div>
|
||||
<div class="basic-info-item"
|
||||
>评估日期:{{ formatDateTime(selectedReport.evaluationDate, 'YYYY-MM-DD') }}</div
|
||||
>
|
||||
<div class="basic-info-item"
|
||||
>风险等级:{{
|
||||
getDictLabel(DICT_TYPE.PRISON_RISK_LEVEL, selectedReport.riskLevel)
|
||||
}}</div
|
||||
>
|
||||
<div class="basic-info-item"
|
||||
>状态:{{ getDictLabel(DICT_TYPE.PRISON_REPORT_STATUS, selectedReport.status) }}</div
|
||||
>
|
||||
</div>
|
||||
|
||||
<div v-for="item in dimensionAnalysisPanelRef" :key="item.id" class="dimension-item">
|
||||
<div class="dimension-item-title">{{ item.name }}</div>
|
||||
<div style="white-space: pre-line; line-height: 1.5;">{{ item.aiAnalysis?.replace(/## 综合分析建议\n\n/g, '') }}</div>
|
||||
<div
|
||||
v-for="(item, index) in dimensionAnalysisPanelRef"
|
||||
:key="item.id"
|
||||
class="dimension-item"
|
||||
>
|
||||
<div class="dimension-item-title"
|
||||
>{{ ['一', '二', '三', '四', '五', '六', '七', '八', '九', '十'][index] }}、{{
|
||||
item.name
|
||||
}}</div
|
||||
>
|
||||
<div style="white-space: pre-line; line-height: 1.5">{{
|
||||
// 移除所有以 '##' 开头、并以两个连续换行结尾的标题块,兼容 CRLF/LF
|
||||
item.aiAnalysis?.replace(/(^|\r?\n)##.*?\r?\n\r?\n/gm, '$1')
|
||||
}}</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -32,7 +53,14 @@
|
||||
import { DICT_TYPE, getDictLabel } from '@/utils/dict'
|
||||
import { formatDateTime } from '@/utils/formatTime'
|
||||
import download from '@/utils/download'
|
||||
import { ReportApi, ReportVO, DimensionDataApi, DimensionDataVO, DimensionApi, DimensionVO } from '@/api/prison/evaluation-report'
|
||||
import {
|
||||
ReportApi,
|
||||
ReportVO,
|
||||
DimensionDataApi,
|
||||
DimensionDataVO,
|
||||
DimensionApi,
|
||||
DimensionVO
|
||||
} from '@/api/prison/evaluation-report'
|
||||
import { PrisonerApi } from '@/api/prison/prisoner'
|
||||
import { asBlob } from 'html-docx-js-typescript'
|
||||
import { saveAs } from 'file-saver'
|
||||
@ -78,7 +106,8 @@ const loadReportDetail = async (id: number) => {
|
||||
try {
|
||||
selectedReport.value = await ReportApi.getReport(id)
|
||||
dimensionDataList.value = await DimensionDataApi.getDimensionDataListByReportId(id)
|
||||
drawerTitle.value = selectedReport.value?.title || `${selectedReport.value?.prisonerName} - 评估报告`
|
||||
drawerTitle.value =
|
||||
selectedReport.value?.title || `${selectedReport.value?.prisonerName} - 评估报告`
|
||||
|
||||
// 监区信息兜底(报告未返回监区时,从罪犯档案补齐)
|
||||
if (selectedReport.value?.prisonerId && !selectedReport.value.areaName) {
|
||||
@ -92,15 +121,16 @@ const loadReportDetail = async (id: number) => {
|
||||
// 加载维度配置
|
||||
if (selectedReport.value?.templateId) {
|
||||
try {
|
||||
const dimensionList = await DimensionApi.getDimensionsByTemplateId(selectedReport.value.templateId)
|
||||
const dimensionList = await DimensionApi.getDimensionsByTemplateId(
|
||||
selectedReport.value.templateId
|
||||
)
|
||||
if (dimensionList && dimensionList.length > 0) {
|
||||
console.log(dimensionList);
|
||||
console.log(dimensionList)
|
||||
dimensions.value = dimensionList
|
||||
} else {
|
||||
// 使用默认维度配置
|
||||
dimensions.value = getDefaultDimensions(selectedReport.value.templateId)
|
||||
}
|
||||
|
||||
} catch {
|
||||
dimensions.value = getDefaultDimensions(selectedReport.value.templateId)
|
||||
}
|
||||
@ -108,11 +138,11 @@ const loadReportDetail = async (id: number) => {
|
||||
|
||||
if (selectedReport.value?.id && dimensions.value.length > 0) {
|
||||
const list = await DimensionDataApi.getDimensionDataListByReportId(selectedReport.value.id)
|
||||
console.log(list, dimensions.value);
|
||||
dimensionAnalysisPanelRef.value = dimensions.value.map(item => {
|
||||
console.log(list, dimensions.value)
|
||||
dimensionAnalysisPanelRef.value = dimensions.value.map((item) => {
|
||||
return {
|
||||
...item,
|
||||
aiAnalysis: list.find(analys => analys.dimensionId === item.id)?.aiAnalysis
|
||||
aiAnalysis: list.find((analys) => analys.dimensionId === item.id)?.aiAnalysis
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -127,11 +157,51 @@ const loadReportDetail = async (id: number) => {
|
||||
/** 获取默认维度配置 */
|
||||
const getDefaultDimensions = (templateId: number): DimensionVO[] => {
|
||||
return [
|
||||
{ id: 1, templateId, name: '基本信息', dimensionType: 1, aiEnabled: 0, status: 0, dataSources: ['prisoner'] },
|
||||
{ id: 2, templateId, name: '犯罪情况分析', dimensionType: 1, aiEnabled: 1, status: 0, dataSources: ['prisoner', 'risk'] },
|
||||
{ id: 3, templateId, name: '服刑表现评估', dimensionType: 1, aiEnabled: 1, status: 0, dataSources: ['score', 'violation', 'reward'] },
|
||||
{ id: 4, templateId, name: '消费行为分析', dimensionType: 1, aiEnabled: 1, status: 0, dataSources: ['consumption'] },
|
||||
{ id: 5, templateId, name: '综合评估结论', dimensionType: 1, aiEnabled: 1, status: 0, dataSources: ['prisoner', 'psychology'] }
|
||||
{
|
||||
id: 1,
|
||||
templateId,
|
||||
name: '基本信息',
|
||||
dimensionType: 1,
|
||||
aiEnabled: 0,
|
||||
status: 0,
|
||||
dataSources: ['prisoner']
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
templateId,
|
||||
name: '犯罪情况分析',
|
||||
dimensionType: 1,
|
||||
aiEnabled: 1,
|
||||
status: 0,
|
||||
dataSources: ['prisoner', 'risk']
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
templateId,
|
||||
name: '服刑表现评估',
|
||||
dimensionType: 1,
|
||||
aiEnabled: 1,
|
||||
status: 0,
|
||||
dataSources: ['score', 'violation', 'reward']
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
templateId,
|
||||
name: '消费行为分析',
|
||||
dimensionType: 1,
|
||||
aiEnabled: 1,
|
||||
status: 0,
|
||||
dataSources: ['consumption']
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
templateId,
|
||||
name: '综合评估结论',
|
||||
dimensionType: 1,
|
||||
aiEnabled: 1,
|
||||
status: 0,
|
||||
dataSources: ['prisoner', 'psychology']
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@ -189,40 +259,36 @@ const exportToWord = async () => {
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<style>
|
||||
/* 使用 pt 单位以避免 Word/WPS 在 px->pt 换算时产生差异 */
|
||||
body {
|
||||
font-family: 'Microsoft YaHei', '微软雅黑', SimSun, Arial, sans-serif;
|
||||
font-size: 14px;
|
||||
line-height: 1.6;
|
||||
margin: 20px;
|
||||
padding: 20px;
|
||||
font-size: 11pt;
|
||||
line-height: 1.5;
|
||||
}
|
||||
.basic-info-title{
|
||||
font-family: '黑体', 'Microsoft YaHei', SimSun, Arial, sans-serif;
|
||||
font-size: 21px;
|
||||
font-size: 18pt;
|
||||
font-weight: 700;
|
||||
color: black;
|
||||
text-align: center;
|
||||
margin-bottom: 20px;
|
||||
margin-bottom: 15pt;
|
||||
}
|
||||
.basic-info-section {
|
||||
padding: 15px 20px;
|
||||
color: black;
|
||||
font-size: 12px;
|
||||
font-size: 11pt;
|
||||
}
|
||||
.basic-info-item{
|
||||
margin-right: 30px;
|
||||
margin-right: 25pt;
|
||||
}
|
||||
|
||||
.dimension-item {
|
||||
padding: 0 40px;
|
||||
font-size: 12px;
|
||||
font-size: 11pt;
|
||||
color: black;
|
||||
}
|
||||
.dimension-item-title {
|
||||
font-size: 21px;
|
||||
padding: 15px 0;
|
||||
font-size: 15pt;
|
||||
font-weight: 500;
|
||||
color: black;
|
||||
margin-top: 15pt;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
@ -269,7 +335,7 @@ defineExpose({ open })
|
||||
color: black;
|
||||
font-size: 14px;
|
||||
}
|
||||
.basic-info-item{
|
||||
.basic-info-item {
|
||||
margin-right: 30px;
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,11 @@
|
||||
<template>
|
||||
<Dialog style="display: none;" title="问卷预览" v-model="dialogVisible" width="900px" :fullscreen="false">
|
||||
<Dialog
|
||||
style="display: none"
|
||||
title="问卷预览"
|
||||
v-model="dialogVisible"
|
||||
width="900px"
|
||||
:fullscreen="false"
|
||||
>
|
||||
<div ref="previewRef" class="questionnaire-preview" v-loading="loading">
|
||||
<!-- 问卷头部信息 -->
|
||||
<h1 class="preview-header">{{ recordInfo?.questionnaireName }}</h1>
|
||||
@ -18,22 +24,23 @@
|
||||
|
||||
<!-- 问题列表(按分区显示) -->
|
||||
<div class="preview-questions">
|
||||
<template v-for="partition in partitions" :key="partition.name || 'default'">
|
||||
<template v-for="(partition, index) in partitions" :key="partition.name || 'default'">
|
||||
<!-- 分区标题 -->
|
||||
<div v-if="partition.name" class="partition-title">
|
||||
{{ ['一', '二', '三', '四', '五', '六', '七', '八', '九', '十'][index] }}、
|
||||
{{ partition.name }}
|
||||
<span class="question-count">({{ partition.questions.length }} 道题)</span>
|
||||
</div>
|
||||
|
||||
<!-- 问题列表 -->
|
||||
<div class="question-items">
|
||||
<!-- 问题列表 -->
|
||||
<div class="question-items">
|
||||
<div
|
||||
v-for="(questionWithAnswer, index) in partition.questions"
|
||||
:key="questionWithAnswer.question.id"
|
||||
class="question-item"
|
||||
>
|
||||
<span class="question-index">{{ index + 1 }}.</span>
|
||||
<span class="question-title">{{ questionWithAnswer.question.title }}</span>
|
||||
<span class="question-title">{{ questionWithAnswer.question.title }}:</span>
|
||||
|
||||
<!-- 帮助说明 -->
|
||||
<span v-if="questionWithAnswer.question.helpText" class="question-help-inline">
|
||||
@ -41,25 +48,50 @@
|
||||
</span>
|
||||
|
||||
<!-- 单选/多选题 -->
|
||||
<span v-if="questionWithAnswer.question.type === 1 || questionWithAnswer.question.type === 2" class="question-options-inline">
|
||||
<span
|
||||
v-if="
|
||||
questionWithAnswer.question.type === 1 || questionWithAnswer.question.type === 2
|
||||
"
|
||||
class="question-options-inline"
|
||||
>
|
||||
<span
|
||||
v-for="option in getQuestionOptions(questionWithAnswer.question)"
|
||||
:key="option.label"
|
||||
class="option-item"
|
||||
>
|
||||
<span v-if="questionWithAnswer.question.type === 1 && questionWithAnswer.answer?.answerText?.trim() === option.label">
|
||||
<span
|
||||
v-if="
|
||||
questionWithAnswer.question.type === 1 &&
|
||||
questionWithAnswer.answer?.answerText?.trim() === option.label
|
||||
"
|
||||
>
|
||||
☑
|
||||
{{ option.label }}
|
||||
</span>
|
||||
<span v-if="questionWithAnswer.question.type === 1 && questionWithAnswer.answer?.answerText?.trim() !== option.label">
|
||||
<span
|
||||
v-if="
|
||||
questionWithAnswer.question.type === 1 &&
|
||||
questionWithAnswer.answer?.answerText?.trim() !== option.label
|
||||
"
|
||||
>
|
||||
☐
|
||||
{{ option.label }}
|
||||
</span>
|
||||
<span v-if="questionWithAnswer.question.type === 2 && getSelectedLabels(questionWithAnswer.answer).includes(option.label)">
|
||||
<span
|
||||
v-if="
|
||||
questionWithAnswer.question.type === 2 &&
|
||||
getSelectedLabels(questionWithAnswer.answer).includes(option.label)
|
||||
"
|
||||
>
|
||||
☑
|
||||
{{ option.label }}
|
||||
</span>
|
||||
<span v-if="questionWithAnswer.question.type === 2 && !getSelectedLabels(questionWithAnswer.answer).includes(option.label)">
|
||||
<span
|
||||
v-if="
|
||||
questionWithAnswer.question.type === 2 &&
|
||||
!getSelectedLabels(questionWithAnswer.answer).includes(option.label)
|
||||
"
|
||||
>
|
||||
☐
|
||||
{{ option.label }}
|
||||
</span>
|
||||
@ -67,26 +99,48 @@
|
||||
</span>
|
||||
|
||||
<!-- 填空题 -->
|
||||
<span v-else-if="questionWithAnswer.question.type === 3" class="question-input-inline">
|
||||
<span
|
||||
v-else-if="questionWithAnswer.question.type === 3"
|
||||
class="question-input-inline"
|
||||
>
|
||||
{{ getAnswerDisplayValue(questionWithAnswer.answer) }}
|
||||
</span>
|
||||
|
||||
<!-- 评分题 -->
|
||||
<span v-else-if="questionWithAnswer.question.type === 4" class="question-rating-inline">
|
||||
<span class="question-input-inline">{{ getAnswerDisplayValue(questionWithAnswer.answer) }}</span>
|
||||
<span
|
||||
v-else-if="questionWithAnswer.question.type === 4"
|
||||
class="question-rating-inline"
|
||||
>
|
||||
<span class="question-input-inline">{{
|
||||
getAnswerDisplayValue(questionWithAnswer.answer)
|
||||
}}</span>
|
||||
</span>
|
||||
|
||||
<!-- 日期题 -->
|
||||
<span v-else-if="questionWithAnswer.question.type === 5" class="question-date-inline">
|
||||
<span class="date-info" v-if="getRangeValue(questionWithAnswer.question, 'min') || getRangeValue(questionWithAnswer.question, 'max')">
|
||||
日期范围:{{ getRangeValue(questionWithAnswer.question, 'min') || '无限制' }} ~ {{ getRangeValue(questionWithAnswer.question, 'max') || '无限制' }}
|
||||
<span
|
||||
class="date-info"
|
||||
v-if="
|
||||
getRangeValue(questionWithAnswer.question, 'min') ||
|
||||
getRangeValue(questionWithAnswer.question, 'max')
|
||||
"
|
||||
>
|
||||
日期范围:{{ getRangeValue(questionWithAnswer.question, 'min') || '无限制' }} ~
|
||||
{{ getRangeValue(questionWithAnswer.question, 'max') || '无限制' }}
|
||||
</span>
|
||||
<span class="question-input-inline">{{ getAnswerDisplayValue(questionWithAnswer.answer) }}</span>
|
||||
<span class="question-input-inline">{{
|
||||
getAnswerDisplayValue(questionWithAnswer.answer)
|
||||
}}</span>
|
||||
</span>
|
||||
|
||||
<!-- 数字题 -->
|
||||
<span v-else-if="questionWithAnswer.question.type === 6" class="question-number-inline">
|
||||
<span class="question-input-inline">{{ getAnswerDisplayValue(questionWithAnswer.answer) }}</span>
|
||||
<span
|
||||
v-else-if="questionWithAnswer.question.type === 6"
|
||||
class="question-number-inline"
|
||||
>
|
||||
<span class="question-input-inline">{{
|
||||
getAnswerDisplayValue(questionWithAnswer.answer)
|
||||
}}</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@ -137,7 +191,7 @@ const partitions = computed(() => {
|
||||
|
||||
questions.value.forEach((q, index) => {
|
||||
const partName = q.partName || ''
|
||||
const answer = answers.value.find(a => a.questionId === q.id)
|
||||
const answer = answers.value.find((a) => a.questionId === q.id)
|
||||
|
||||
if (!partMap.has(partName)) {
|
||||
partMap.set(partName, [])
|
||||
@ -150,12 +204,11 @@ const partitions = computed(() => {
|
||||
})
|
||||
|
||||
// 按分区排序
|
||||
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 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[] }> = []
|
||||
@ -184,20 +237,21 @@ const partitions = computed(() => {
|
||||
|
||||
/** 根据问题ID获取答案 */
|
||||
const getAnswerByQuestionId = (questionId: number): Answer | undefined => {
|
||||
return answers.value.find(a => a.questionId === questionId)
|
||||
return answers.value.find((a) => a.questionId === questionId)
|
||||
}
|
||||
|
||||
/** 获取答案显示值(兼容不同字段名) */
|
||||
const getAnswerDisplayValue = (answer?: Answer): string => {
|
||||
if (!answer) return ''
|
||||
// 优先使用 answerText,其次使用 optionIds
|
||||
return answer.answerText || answer.optionIds || '-'
|
||||
// 优先使用 answerText,其次使用 optionIds,并去除前后空格和特殊字符
|
||||
const value = answer.answerText || answer.optionIds || ''
|
||||
return String(value).trim().replace(/^["']|["']$/g, '')
|
||||
}
|
||||
|
||||
/** 问卷类型标签 */
|
||||
const getTypeLabel = (type: number) => {
|
||||
const options = getIntDictOptions(DICT_TYPE.PRISON_QUESTIONNAIRE_TYPE)
|
||||
return options.find(o => o.value === type)?.label || '未知'
|
||||
return options.find((o) => o.value === type)?.label || '未知'
|
||||
}
|
||||
|
||||
/** 获取问题选项 */
|
||||
@ -233,11 +287,16 @@ const getRangeValue = (question: Question, key: 'min' | 'max') => {
|
||||
/** 安全获取多选答案的标签数组 */
|
||||
const getSelectedLabels = (answer?: Answer): string[] => {
|
||||
if (!answer?.answerText) return []
|
||||
return answer.answerText.split(',').map(s => s.trim())
|
||||
return answer.answerText.split(',').map((s) => s.trim())
|
||||
}
|
||||
|
||||
/** 判断选项是否被选中(支持单选和多选)- 已废弃,改用模板直接绑定 */
|
||||
const isOptionSelected = (answer?: Answer, optionValue?: string, optionLabel?: string, questionType?: number): boolean => {
|
||||
const isOptionSelected = (
|
||||
answer?: Answer,
|
||||
optionValue?: string,
|
||||
optionLabel?: string,
|
||||
questionType?: number
|
||||
): boolean => {
|
||||
if (!answer || !optionValue || !optionLabel) {
|
||||
return false
|
||||
}
|
||||
@ -245,7 +304,7 @@ const isOptionSelected = (answer?: Answer, optionValue?: string, optionLabel?: s
|
||||
// 多选(type === 2):answerText 是逗号分隔的标签列表
|
||||
if (questionType === 2 && answer.answerText && answer.answerText.includes(',')) {
|
||||
const answerText = answer.answerText
|
||||
const selectedLabels = answerText.split(',').map(s => s.trim())
|
||||
const selectedLabels = answerText.split(',').map((s) => s.trim())
|
||||
return selectedLabels.includes(optionLabel)
|
||||
}
|
||||
|
||||
@ -257,7 +316,7 @@ const isOptionSelected = (answer?: Answer, optionValue?: string, optionLabel?: s
|
||||
// 兼容:没有 questionType 时,根据是否有逗号判断
|
||||
if (answer.answerText && answer.answerText.includes(',')) {
|
||||
const answerText = answer.answerText
|
||||
const selectedLabels = answerText.split(',').map(s => s.trim())
|
||||
const selectedLabels = answerText.split(',').map((s) => s.trim())
|
||||
return selectedLabels.includes(optionLabel)
|
||||
}
|
||||
|
||||
@ -266,7 +325,6 @@ const isOptionSelected = (answer?: Answer, optionValue?: string, optionLabel?: s
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
/** 打开弹窗 */
|
||||
const open = async (recordId: number) => {
|
||||
dialogVisible.value = true
|
||||
@ -296,7 +354,9 @@ const open = async (recordId: number) => {
|
||||
if (recordData.questionnaireId) {
|
||||
try {
|
||||
const { QuestionnaireApi } = await import('@/api/prison/questionnaire')
|
||||
questionnaireInfo.value = await QuestionnaireApi.getQuestionnaire(recordData.questionnaireId)
|
||||
questionnaireInfo.value = await QuestionnaireApi.getQuestionnaire(
|
||||
recordData.questionnaireId
|
||||
)
|
||||
} catch (error) {
|
||||
console.warn('加载问卷详细信息失败:', error)
|
||||
}
|
||||
@ -327,7 +387,11 @@ const exportToWord = async () => {
|
||||
}
|
||||
|
||||
// 获取预览容器的HTML内容
|
||||
const previewHTML = previewRef.value.innerHTML
|
||||
let previewHTML = previewRef.value.innerHTML
|
||||
|
||||
// 清理可能导致WPS显示引号的特殊字符
|
||||
previewHTML = previewHTML.replace(/[\u201C\u201D\u2018\u2019]/g, '') // 移除中文引号
|
||||
previewHTML = previewHTML.replace(/^["']|["']$/gm, '') // 移除行首尾的引号
|
||||
|
||||
// 构建完整的HTML文档,使用与预览页面一致的样式
|
||||
const fullHTML = `
|
||||
@ -336,108 +400,110 @@ const exportToWord = async () => {
|
||||
<meta charset="utf-8">
|
||||
<title>${recordInfo.value?.questionnaireName || '问卷'}</title>
|
||||
<style>
|
||||
/* 使用 pt 单位以避免 Word/WPS 在 px->pt 换算时产生差异 */
|
||||
body {
|
||||
font-family: 'Microsoft YaHei', '微软雅黑', SimSun, Arial, sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 1.6;
|
||||
margin: 20px;
|
||||
padding: 20px;
|
||||
font-size: 11pt;
|
||||
margin: 15pt;
|
||||
padding: 15pt;
|
||||
}
|
||||
h1 {
|
||||
font-family: 'Microsoft YaHei', '微软雅黑', SimSun, Arial, sans-serif;
|
||||
font-size: 18pt;
|
||||
font-weight: 500;
|
||||
color: #000;
|
||||
text-align: center;
|
||||
}
|
||||
.section-title {
|
||||
margin: 15px 0;
|
||||
font-size: 18px;
|
||||
font-weight: 500;
|
||||
font-family: 'Microsoft YaHei', '微软雅黑', SimSun, Arial, sans-serif;
|
||||
margin-bottom: 15pt;
|
||||
font-size: 18pt;
|
||||
font-weight: 700;
|
||||
color: #000;
|
||||
}
|
||||
.description-content {
|
||||
color: #000;
|
||||
line-height: 1.8;
|
||||
font-size: 16px;
|
||||
font-size: 11pt;
|
||||
margin-bottom: 6pt;
|
||||
line-height: 1.5;
|
||||
}
|
||||
.instruction-content {
|
||||
color: #000;
|
||||
font-size: 18px;
|
||||
font-size: 11pt;
|
||||
font-weight: 500;
|
||||
line-height: 1.8;
|
||||
margin-bottom: 6pt;
|
||||
line-height: 1.5;
|
||||
}
|
||||
.partition-title {
|
||||
margin-bottom: 16px;
|
||||
margin-top: 24px;
|
||||
margin-top: 15pt;
|
||||
color: #000;
|
||||
font-size: 17px;
|
||||
font-size: 15pt;
|
||||
font-weight: 500;
|
||||
}
|
||||
.partition-title:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
.question-count {
|
||||
font-size: 14px;
|
||||
font-size: 11pt;
|
||||
color: #909399;
|
||||
font-weight: normal;
|
||||
margin-left: 4px;
|
||||
margin-left: 2pt;
|
||||
}
|
||||
.question-item {
|
||||
font-size: 16px;
|
||||
font-size: 11pt;
|
||||
color: #000;
|
||||
line-height: 1.6;
|
||||
margin-bottom: 16px;
|
||||
margin-bottom: 8pt;
|
||||
line-height: 1.5;
|
||||
page-break-inside: avoid;
|
||||
}
|
||||
.question-index {
|
||||
margin-right: 4px;
|
||||
font-weight: bold;
|
||||
margin-right: 2pt;
|
||||
}
|
||||
.question-title {
|
||||
margin-right: 8px;
|
||||
margin-right: 4pt;
|
||||
}
|
||||
.question-help-inline {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
gap: 3pt;
|
||||
color: #909399;
|
||||
font-size: 15px;
|
||||
padding: 4px 8px;
|
||||
font-size: 11pt;
|
||||
background: #f4f4f5;
|
||||
border-radius: 4px;
|
||||
margin-right: 8px;
|
||||
border-radius: 2pt;
|
||||
margin-right: 6pt;
|
||||
}
|
||||
.question-options-inline {
|
||||
display: inline-flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 12px;
|
||||
gap: 8pt;
|
||||
align-items: center;
|
||||
}
|
||||
.option-item {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
gap: 3pt;
|
||||
}
|
||||
.question-rating-inline,
|
||||
.question-date-inline,
|
||||
.question-number-inline {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
gap: 8pt;
|
||||
flex-wrap: wrap;
|
||||
margin-left: 8px;
|
||||
margin-left: 3pt;
|
||||
}
|
||||
.rating-info,
|
||||
.date-info,
|
||||
.number-info {
|
||||
display: inline-flex;
|
||||
gap: 16px;
|
||||
gap: 11pt;
|
||||
color: #909399;
|
||||
font-size: 13px;
|
||||
font-size: 11pt;
|
||||
}
|
||||
.question-input-inline {
|
||||
color: #000;
|
||||
font-size: 16px;
|
||||
line-height: 2.2;
|
||||
font-size: 11pt;
|
||||
line-height: 1.5;
|
||||
text-decoration: underline;
|
||||
}
|
||||
</style>
|
||||
@ -475,100 +541,99 @@ defineExpose({ open, exportToWord })
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-family: 'Microsoft YaHei', '微软雅黑', SimSun, Arial, sans-serif;
|
||||
font-size: 18pt;
|
||||
font-weight: 500;
|
||||
color: #000;
|
||||
text-align: center;
|
||||
}
|
||||
.section-title {
|
||||
margin: 15px 0;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
font-family: 'Microsoft YaHei', '微软雅黑', SimSun, Arial, sans-serif;
|
||||
margin-bottom: 15pt;
|
||||
font-size: 18pt;
|
||||
font-weight: 700;
|
||||
color: #000;
|
||||
}
|
||||
.description-content {
|
||||
color: #000;
|
||||
line-height: 1.8;
|
||||
font-size: 14px;
|
||||
line-height: 3;
|
||||
font-size: 11pt;
|
||||
}
|
||||
.instruction-content {
|
||||
color: #000;
|
||||
font-size: 16px;
|
||||
font-size: 11pt;
|
||||
font-weight: 500;
|
||||
line-height: 1.8;
|
||||
line-height: 3;
|
||||
}
|
||||
.partition-title {
|
||||
margin-bottom: 16px;
|
||||
margin-top: 24px;
|
||||
margin-top: 15pt;
|
||||
color: #000;
|
||||
font-size: 15px;
|
||||
font-size: 15pt;
|
||||
font-weight: 500;
|
||||
}
|
||||
.partition-title:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
.question-count {
|
||||
font-size: 12px;
|
||||
font-size: 11pt;
|
||||
color: #909399;
|
||||
font-weight: normal;
|
||||
margin-left: 4px;
|
||||
margin-left: 2pt;
|
||||
}
|
||||
.question-item {
|
||||
font-size: 14px;
|
||||
font-size: 11pt;
|
||||
color: #000;
|
||||
line-height: 1.6;
|
||||
margin-bottom: 16px;
|
||||
line-height: 3;
|
||||
page-break-inside: avoid;
|
||||
}
|
||||
.question-index {
|
||||
margin-right: 4px;
|
||||
font-weight: bold;
|
||||
margin-right: 2pt;
|
||||
}
|
||||
.question-title {
|
||||
margin-right: 8px;
|
||||
margin-right: 4pt;
|
||||
}
|
||||
.question-help-inline {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
gap: 3pt;
|
||||
color: #909399;
|
||||
font-size: 13px;
|
||||
padding: 4px 8px;
|
||||
font-size: 11pt;
|
||||
background: #f4f4f5;
|
||||
border-radius: 4px;
|
||||
margin-right: 8px;
|
||||
border-radius: 2pt;
|
||||
margin-right: 6pt;
|
||||
}
|
||||
.question-options-inline {
|
||||
display: inline-flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 12px;
|
||||
gap: 8pt;
|
||||
align-items: center;
|
||||
}
|
||||
.option-item {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
gap: 3pt;
|
||||
}
|
||||
.question-rating-inline,
|
||||
.question-date-inline,
|
||||
.question-number-inline {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
gap: 8pt;
|
||||
flex-wrap: wrap;
|
||||
margin-left: 8px;
|
||||
margin-left: 3pt;
|
||||
}
|
||||
.rating-info,
|
||||
.date-info,
|
||||
.number-info {
|
||||
display: inline-flex;
|
||||
gap: 16px;
|
||||
gap: 11pt;
|
||||
color: #909399;
|
||||
font-size: 13px;
|
||||
font-size: 11pt;
|
||||
}
|
||||
.question-input-inline {
|
||||
color: #000;
|
||||
font-size: 14px;
|
||||
line-height: 2.2;
|
||||
font-size: 11pt;
|
||||
line-height: 3;
|
||||
text-decoration: underline;
|
||||
}
|
||||
</style>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user