feat: 同步代码与文档,完善满意度调查相关功能及文档记录(含前后端、API文档、变更日志等)

This commit is contained in:
zlwu committed 2025-07-15 19:52:54 +08:00
1 parent eeb79de050
commit 7a10da0ffc
15 files changed
+460 -177

No files matched your search

+13
View File
@@ -16,6 +16,19 @@ export const listSatCategory = (query?: SatCategoryQuery): AxiosPromise<SatCateg
});
};
/**
* 查询所有题目分类列表(包括已逻辑删除的)
* @param query
* @returns {*}
*/
export const listAllSatCategory = (query?: SatCategoryQuery): AxiosPromise<SatCategoryVO[]> => {
return request({
url: '/hq/satCategory/listAllIncludeDeleted',
method: 'get',
params: query
});
};
/**
* 查询题目分类详细
* @param id
+2
View File
@@ -72,6 +72,8 @@ export default {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
// 允许在内容中使用 HTML
dangerouslyUseHTMLString: true,
// 使用自定义样式类确保按钮样式正确应用
confirmButtonClass: 'el-button--primary',
cancelButtonClass: 'el-button--default'
+18 -2
View File
@@ -29,7 +29,23 @@ export const useDict = (...args: string[]) => {
console.log(`字典 ${dictType} API响应:`, resp);
if (resp && resp.data && Array.isArray(resp.data) && resp.data.length > 0) {
const dictData = resp.data.map(
// 保留原始数据用于排序
const dictDataRaw = resp.data;
// 按dictSort升序,dictSort相同再按dictValue排序
const sortedDictDataRaw = dictDataRaw.sort((a, b) => {
if (a.dictSort !== b.dictSort) {
return a.dictSort - b.dictSort;
}
// dictSort相同,按dictValue数值排序
const valueA = Number(a.dictValue);
const valueB = Number(b.dictValue);
if (!isNaN(valueA) && !isNaN(valueB)) {
return valueA - valueB;
}
return String(a.dictValue).localeCompare(String(b.dictValue));
});
// 转换为DictDataOption
const dictData = sortedDictDataRaw.map(
(p): DictDataOption => ({
label: p.dictLabel,
value: p.dictValue,
@@ -39,7 +55,7 @@ export const useDict = (...args: string[]) => {
);
res[dictType] = dictData;
useDictStore().setDict(dictType, dictData);
console.log(`字典 ${dictType} 加载成功,数据:`, dictData);
console.log(`字典 ${dictType} 加载成功并按dictSort排序,数据:`, dictData);
} else {
console.warn(`字典类型 ${dictType} 没有返回有效数据,响应:`, resp);
res[dictType] = [];
+250 -78
View File
@@ -245,12 +245,17 @@
<!-- 题目答案展示区域 -->
<div class="answer-display-area">
<!-- 单选题 -->
<el-radio-group v-if="question.questionType === '1' || question.questionType === 1" :model-value="question.userAnswer" disabled>
<el-radio-group
v-if="question.questionType === '1' || question.questionType === 1"
:model-value="question.userAnswer"
disabled
class="answer-options-horizontal"
>
<el-radio
v-for="(option, index) in question.options || []"
:key="option.id"
:label="String.fromCharCode(65 + index)"
class="answer-option"
class="answer-option-horizontal"
>
{{ String.fromCharCode(65 + index) }}. {{ option.optionText }}
</el-radio>
@@ -261,12 +266,13 @@
v-else-if="question.questionType === '2' || question.questionType === 2"
:model-value="question.userAnswer || []"
disabled
class="answer-options-horizontal"
>
<el-checkbox
v-for="(option, index) in question.options || []"
:key="option.id"
:label="String.fromCharCode(65 + index)"
class="answer-option"
class="answer-option-horizontal"
>
{{ String.fromCharCode(65 + index) }}. {{ option.optionText }}
</el-checkbox>
@@ -402,12 +408,14 @@
<!-- 问卷信息头部 -->
<div class="survey-header" v-if="currentAnswerSurvey">
<div class="survey-info">
<h3 class="survey-title">{{ currentAnswerSurvey.surveyTitle }}</h3>
<div class="survey-meta">
<h3 class="survey-title">
{{ currentAnswerSurvey.surveyTitle }}
<el-tag size="small" type="primary">{{ answerQuestions.length }} 题</el-tag>
<el-tag size="small" :type="currentAnswerSurvey.isAnonymous === 1 ? 'success' : 'info'">
</h3>
<div class="survey-meta">
<!-- <el-tag size="small" :type="currentAnswerSurvey.isAnonymous === 1 ? 'success' : 'info'">
{{ currentAnswerSurvey.isAnonymous === 1 ? '匿名问卷' : '实名问卷' }}
</el-tag>
</el-tag> -->
</div>
<div class="survey-description" v-if="currentAnswerSurvey.surveyDesc">
{{ currentAnswerSurvey.surveyDesc }}
@@ -483,10 +491,10 @@
<template #footer>
<div class="dialog-footer answer-dialog-footer">
<el-button @click="closeAnswerDialog">取消</el-button>
<el-button type="primary" :loading="answerSubmitting" @click="submitAnswer" :disabled="answerQuestions.length === 0">
{{ answerSubmitting ? '提交中...' : '提交答卷' }}
</el-button>
<el-button @click="closeAnswerDialog">取消</el-button>
</div>
</template>
</el-dialog>
@@ -950,7 +958,8 @@ const handleStartSurvey = async (row: SatSurveyVO) => {
console.log(`问卷包含 ${questions.length} 个题目,可以开始答题`);
// 3. 显示确认对话框
const confirmMessage = `确定要开始答题吗?\n\n问卷标题:${row.surveyTitle}\n题目数量:${questions.length} 题\n${row.isAnonymous === 1 ? '匿名问卷' : '实名问卷'}`;
// const confirmMessage = `确定要开始答题吗?\n\n问卷标题:${row.surveyTitle}\n题目数量:${questions.length} 题\n${row.isAnonymous === 1 ? '匿名问卷' : '实名问卷'}`;
const confirmMessage = `确定要开始答题吗?<br/>问卷标题:${row.surveyTitle}<br/>题目数量:${questions.length} 题`;
await proxy?.$modal.confirm(confirmMessage);
@@ -1125,8 +1134,20 @@ const loadPreviousAnswers = async (surveyId: string | number, formData: Record<s
console.error('=== 加载历史答案发生错误 ===');
console.error('错误详情:', error);
// 如果是404错误,说明用户没有答过这个问卷,这是正常情况
if ((error as any)?.response?.status === 404) {
// // 如果是404错误,说明用户没有答过这个问卷,这是正常情况
// if ((error as any)?.response?.status === 404) {
// console.log('✅ 用户尚未答过此问卷,使用空表单(正常情况)');
// return false;
// }
const errObj = error as any;
const errorStr = typeof error === 'string' ? error : error?.message || error?.msg || '';
if (
errObj?.response?.status === 404 ||
errObj?.code === 404 ||
(typeof errObj?.msg === 'string' && errObj.msg.includes('未找到有效的答题记录')) ||
(typeof errObj?.message === 'string' && errObj.message.includes('未找到有效的答题记录')) ||
(typeof errorStr === 'string' && (errorStr.includes('404') || errorStr.includes('未找到有效的答题记录')))
) {
console.log('✅ 用户尚未答过此问卷,使用空表单(正常情况)');
return false;
}
@@ -2057,7 +2078,7 @@ onMounted(() => {
.question-number {
font-weight: 600;
color: #409eff;
color: var(--el-color-primary);
font-size: 14px;
flex-shrink: 0;
}
@@ -2099,6 +2120,7 @@ onMounted(() => {
margin-top: 10px;
}
/* 旧版单选/多选选项样式 - 仅保留兼容 */
.answer-option {
display: block !important;
margin-bottom: 12px;
@@ -2120,12 +2142,12 @@ onMounted(() => {
.answer-display-area .el-checkbox {
display: inline-flex !important;
align-items: center;
margin-right: 0 !important;
margin-right: 56px !important;
margin-bottom: 0 !important;
padding: 6px 12px;
border: 1px solid #e4e7ed;
/* border: 1px solid #e4e7ed; */
border-radius: 6px;
background: #fafbfc;
/* background: #fafbfc; */
transition: all 0.3s ease;
white-space: nowrap;
flex-shrink: 0;
@@ -2133,9 +2155,9 @@ onMounted(() => {
.answer-display-area .el-radio.is-checked,
.answer-display-area .el-checkbox.is-checked {
background: rgba(93, 135, 255, 0.1);
border-color: rgba(93, 135, 255, 0.3);
color: rgb(93, 135, 255);
/* background: rgba(0, 58, 255, 0.1); */
border-color: rgba(0, 58, 255, 0.3);
color: rgb(0, 58, 255);
font-weight: 500;
}
@@ -2428,13 +2450,13 @@ onMounted(() => {
resize: both;
overflow: auto;
border-radius: 16px;
box-shadow: 0 20px 60px rgba(93, 135, 255, 0.15);
border: 1px solid rgba(93, 135, 255, 0.1);
box-shadow: 0 20px 60px rgba(0, 58, 255, 0.15);
border: 1px solid rgba(0, 58, 255, 0.1);
}
.answer-dialog :deep(.el-dialog__header) {
padding: 24px 32px 16px;
border-bottom: 1px solid rgba(93, 135, 255, 0.1);
border-bottom: 1px solid rgba(0, 58, 255, 0.1);
background: linear-gradient(135deg, #fafbff 0%, #f8fafc 100%);
border-radius: 16px 16px 0 0;
}
@@ -2467,8 +2489,8 @@ onMounted(() => {
}
.answer-dialog :deep(.el-dialog__body) {
max-height: 75vh;
overflow-y: auto;
max-height: 65vh; /* 设置最大高度 */
overflow-y: auto; /* 启用垂直滚动 */
padding: 20px 24px;
background: #ffffff;
}
@@ -2480,7 +2502,7 @@ onMounted(() => {
}
.answer-container {
min-height: 400px;
/* min-height: 400px; */
}
/* 问卷信息头部 */
@@ -2489,12 +2511,12 @@ onMounted(() => {
padding: 18px;
background: linear-gradient(135deg, #f0f9ff 0%, #f8fafc 100%);
border-radius: 12px;
border: 1px solid rgba(93, 135, 255, 0.1);
box-shadow: 0 2px 8px rgba(93, 135, 255, 0.08);
border: 1px solid rgba(0, 58, 255, 0.1);
box-shadow: 0 2px 8px rgba(0, 58, 255, 0.08);
}
.survey-title {
margin: 0 0 12px 0;
margin: 0 0 3px 0;
font-size: 22px;
font-weight: 700;
color: #1e293b;
@@ -2512,8 +2534,8 @@ onMounted(() => {
border-radius: 6px;
font-weight: 500;
border: none;
background: rgba(93, 135, 255, 0.1);
color: rgb(93, 135, 255);
background: rgba(0, 58, 255, 0.1);
color: rgb(0, 58, 255);
}
.survey-meta .el-tag.el-tag--success {
@@ -2522,7 +2544,7 @@ onMounted(() => {
}
.survey-description {
color: #475569;
color: #000000;
font-size: 15px;
line-height: 1.5;
margin-bottom: 12px;
@@ -2541,8 +2563,8 @@ onMounted(() => {
/* 答题表单 */
.answer-form {
max-height: 60vh;
overflow-y: auto;
/* max-height: 65vh; */ /* 移除最大高度限制 */
overflow-y: visible; /* 移除垂直滚动 */
padding-right: 12px;
}
@@ -2550,7 +2572,7 @@ onMounted(() => {
margin-bottom: 16px;
padding: 16px;
background: linear-gradient(135deg, #ffffff 0%, #fafbff 100%);
border: 1px solid rgba(93, 135, 255, 0.08);
border: 1px solid rgba(0, 58, 255, 0.08);
border-radius: 12px;
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
position: relative;
@@ -2564,14 +2586,14 @@ onMounted(() => {
left: 0;
right: 0;
height: 3px;
background: linear-gradient(90deg, rgba(93, 135, 255, 0.6) 0%, rgba(73, 190, 255, 0.6) 100%);
background: linear-gradient(90deg, rgba(0, 58, 255, 0.6) 0%, rgba(73, 190, 255, 0.6) 100%);
transform: scaleX(0);
transition: transform 0.3s ease;
}
.question-item:hover {
border-color: rgba(93, 135, 255, 0.2);
box-shadow: 0 8px 24px rgba(93, 135, 255, 0.12);
border-color: rgba(0, 58, 255, 0.2);
box-shadow: 0 8px 24px rgba(0, 58, 255, 0.12);
transform: translateY(-2px);
}
@@ -2583,7 +2605,7 @@ onMounted(() => {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px 14px;
padding: 4px 14px;
background: #f8f9fa;
border-bottom: 1px solid #e4e7ed;
border-radius: 8px 8px 0 0;
@@ -2591,12 +2613,9 @@ onMounted(() => {
.question-number {
font-weight: 700;
color: rgb(93, 135, 255);
/* color: rgb(0, 58, 255); */
font-size: 14px;
padding: 4px 12px;
background: rgba(93, 135, 255, 0.1);
border-radius: 20px;
border: 1px solid rgba(93, 135, 255, 0.2);
}
.question-title {
@@ -2640,9 +2659,9 @@ onMounted(() => {
white-space: nowrap;
margin-bottom: 0 !important;
padding: 8px 16px;
border: 2px solid rgba(93, 135, 255, 0.15);
/* border: 2px solid rgba(0, 58, 255, 0.15); */
border-radius: 20px;
background: rgba(93, 135, 255, 0.03);
/* background: rgba(0, 58, 255, 0.03); */
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
cursor: pointer;
font-weight: 500;
@@ -2658,16 +2677,16 @@ onMounted(() => {
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(93, 135, 255, 0.1), transparent);
background: linear-gradient(90deg, transparent, rgba(0, 58, 255, 0.1), transparent);
transition: left 0.5s ease;
}
.answer-option-horizontal:hover {
background: rgba(93, 135, 255, 0.08);
border-color: rgba(93, 135, 255, 0.3);
color: rgb(93, 135, 255);
background: rgba(0, 58, 255, 0.08);
border-color: rgba(0, 58, 255, 0.3);
color: rgb(0, 58, 255);
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(93, 135, 255, 0.15);
box-shadow: 0 4px 12px rgba(0, 58, 255, 0.15);
}
.answer-option-horizontal:hover::before {
@@ -2675,11 +2694,11 @@ onMounted(() => {
}
.answer-option-horizontal.is-checked {
background: rgba(93, 135, 255, 0.1);
border-color: rgb(93, 135, 255);
color: rgb(93, 135, 255);
background: rgba(0, 58, 255, 0.1);
border-color: rgb(0, 58, 255);
color: rgb(0, 58, 255);
font-weight: 600;
box-shadow: 0 4px 16px rgba(93, 135, 255, 0.2);
box-shadow: 0 4px 16px rgba(0, 58, 255, 0.2);
}
.answer-option-horizontal :deep(.el-radio__label),
@@ -2691,19 +2710,19 @@ onMounted(() => {
.answer-option-horizontal :deep(.el-radio__input.is-checked .el-radio__inner),
.answer-option-horizontal :deep(.el-checkbox__input.is-checked .el-checkbox__inner) {
border-color: rgb(93, 135, 255);
background-color: rgb(93, 135, 255);
border-color: rgb(0, 58, 255);
background-color: rgb(0, 58, 255);
}
.answer-option-horizontal :deep(.el-radio__inner),
.answer-option-horizontal :deep(.el-checkbox__inner) {
border-color: rgba(93, 135, 255, 0.3);
border-color: rgba(0, 58, 255, 0.3);
transition: all 0.3s ease;
}
.answer-option-horizontal:hover :deep(.el-radio__inner),
.answer-option-horizontal:hover :deep(.el-checkbox__inner) {
border-color: rgb(93, 135, 255);
border-color: rgb(0, 58, 255);
}
.answer-textarea {
@@ -2713,8 +2732,8 @@ onMounted(() => {
.answer-textarea :deep(.el-textarea__inner) {
border-radius: 12px;
border: 2px solid rgba(93, 135, 255, 0.15);
background: rgba(93, 135, 255, 0.02);
/* border: 2px solid rgba(0, 58, 255, 0.15); */
background: rgba(0, 58, 255, 0.02);
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
font-size: 15px;
line-height: 1.5;
@@ -2723,15 +2742,15 @@ onMounted(() => {
}
.answer-textarea :deep(.el-textarea__inner):focus {
border-color: rgb(93, 135, 255);
background: rgba(93, 135, 255, 0.05);
box-shadow: 0 0 0 4px rgba(93, 135, 255, 0.1);
border-color: rgb(0, 58, 255);
background: rgba(0, 58, 255, 0.05);
box-shadow: 0 0 0 4px rgba(0, 58, 255, 0.1);
outline: none;
}
.answer-textarea :deep(.el-textarea__inner):hover {
border-color: rgba(93, 135, 255, 0.3);
background: rgba(93, 135, 255, 0.03);
border-color: rgba(0, 58, 255, 0.3);
background: rgba(0, 58, 255, 0.03);
}
.answer-textarea :deep(.el-input__count) {
@@ -2766,7 +2785,7 @@ onMounted(() => {
justify-content: flex-end !important;
gap: 16px;
padding: 20px 0 0 0;
border-top: 1px solid rgba(93, 135, 255, 0.1);
border-top: 1px solid rgba(0, 58, 255, 0.1);
margin-top: 20px;
}
@@ -2799,28 +2818,28 @@ onMounted(() => {
/* 主要按钮样式 */
.answer-dialog-footer .el-button--primary {
background: rgba(93, 135, 255, 0.05);
border: 2px solid rgb(93, 135, 255);
color: rgb(93, 135, 255);
background: rgba(0, 58, 255, 0.05);
border: 2px solid rgb(0, 58, 255);
color: rgb(0, 58, 255);
}
.answer-dialog-footer .el-button--primary:hover:not(.is-loading) {
background: rgba(93, 135, 255, 0.1);
border-color: rgba(93, 135, 255, 0.8);
color: rgb(93, 135, 255);
background: rgba(0, 58, 255, 0.1);
border-color: rgba(0, 58, 255, 0.8);
color: rgb(0, 58, 255);
transform: translateY(-1px);
box-shadow: 0 6px 20px rgba(93, 135, 255, 0.25);
box-shadow: 0 6px 20px rgba(0, 58, 255, 0.25);
}
.answer-dialog-footer .el-button--primary:active {
transform: scale(0.98) translateY(-1px);
background: rgba(93, 135, 255, 0.15);
background: rgba(0, 58, 255, 0.15);
}
.answer-dialog-footer .el-button--primary.is-loading {
background: rgba(93, 135, 255, 0.08);
border-color: rgba(93, 135, 255, 0.6);
color: rgba(93, 135, 255, 0.8);
background: rgba(0, 58, 255, 0.08);
border-color: rgba(0, 58, 255, 0.6);
color: rgba(0, 58, 255, 0.8);
}
/* 禁用状态 */
@@ -2855,13 +2874,13 @@ onMounted(() => {
resize: both;
overflow: auto;
border-radius: 16px;
box-shadow: 0 20px 60px rgba(93, 135, 255, 0.15);
border: 1px solid rgba(93, 135, 255, 0.1);
box-shadow: 0 20px 60px rgba(0, 58, 255, 0.15);
border: 1px solid rgba(0, 58, 255, 0.1);
}
.question-view-dialog :deep(.el-dialog__header) {
padding: 20px 24px 16px;
border-bottom: 1px solid rgba(93, 135, 255, 0.1);
border-bottom: 1px solid rgba(0, 58, 255, 0.1);
background: linear-gradient(135deg, #fafbff 0%, #f8fafc 100%);
border-radius: 16px 16px 0 0;
}
@@ -2944,4 +2963,157 @@ onMounted(() => {
margin-bottom: 12px;
padding: 10px 14px;
}
/* 查看题目对话框中单选和多选选项样式继承答题界面样式 */
.question-view-dialog .answer-display-area .answer-options-horizontal {
display: flex;
flex-wrap: wrap;
gap: 12px;
padding: 8px 0;
}
/* 移除Element Plus默认的禁用状态灰色背景和文本颜色 */
.question-view-dialog :deep(.el-checkbox.is-disabled),
.question-view-dialog :deep(.el-radio.is-disabled) {
background-color: transparent !important;
color: #475569 !important;
filter: none !important;
}
.question-view-dialog .answer-option-horizontal.is-checked {
background: rgba(0, 58, 255, 0.1);
border-color: rgb(0, 58, 255);
color: rgb(0, 58, 255);
font-weight: 600;
}
/* 禁用状态下保持正常文本颜色 */
.question-view-dialog .answer-option-horizontal :deep(.el-radio__label),
.question-view-dialog .answer-option-horizontal :deep(.el-checkbox__label) {
color: #475569 !important;
opacity: 1 !important;
}
/* 确保禁用状态下不会变暗 */
.question-view-dialog .answer-option-horizontal :deep(.el-radio__input.is-disabled),
.question-view-dialog .answer-option-horizontal :deep(.el-checkbox__input.is-disabled) {
opacity: 0.9;
}
.question-view-dialog .answer-option-horizontal :deep(.el-radio.is-disabled),
.question-view-dialog .answer-option-horizontal :deep(.el-checkbox.is-disabled) {
cursor: default;
opacity: 1;
}
/* 禁用状态下保持选中颜色 */
.question-view-dialog .answer-option-horizontal.is-checked :deep(.el-radio__input.is-disabled .el-radio__inner),
.question-view-dialog .answer-option-horizontal.is-checked :deep(.el-checkbox__input.is-disabled .el-checkbox__inner) {
background-color: rgb(0, 58, 255) !important;
border-color: rgb(0, 58, 255) !important;
}
/* 移除默认的禁用样式效果 */
.question-view-dialog :deep(.el-radio__input.is-disabled + span.el-radio__label),
.question-view-dialog :deep(.el-checkbox__input.is-disabled + span.el-checkbox__label) {
color: #475569 !important;
}
/* 覆盖全局禁用样式 */
:deep(.el-radio__input.is-disabled + span.el-radio__label) {
color: #303133 !important;
}
:deep(.el-checkbox__input.is-disabled + span.el-checkbox__label) {
color: #303133 !important;
}
/* 最高优先级覆盖多选框发黑问题 */
.question-view-dialog :deep(.answer-option-horizontal .el-checkbox.is-disabled) {
background-color: transparent !important;
filter: none !important;
}
.question-view-dialog :deep(.answer-option-horizontal .el-checkbox.is-disabled .el-checkbox__label) {
color: #475569 !important;
opacity: 1 !important;
}
/* 确保多选框背景色不受禁用影响 */
.question-view-dialog :deep(.answer-option-horizontal) {
background-color: transparent !important;
filter: none !important;
}
/* 查看题目对话框中的答案选项美化 */
.question-card .answer-option-horizontal {
display: inline-flex !important;
align-items: center;
white-space: nowrap;
margin-bottom: 8px !important;
padding: 6px 14px;
border-radius: 20px;
transition: all 0.3s ease;
font-weight: 500;
color: #475569;
position: relative;
background-color: transparent !important;
border: 1px solid transparent;
}
/* 选中状态特殊样式 */
.question-card .answer-option-horizontal.is-checked {
background: rgba(0, 58, 255, 0.1);
color: rgb(0, 58, 255);
font-weight: 600;
box-shadow: 0 2px 8px rgba(0, 58, 255, 0.15);
}
/* 确保禁用状态下单选/多选按钮的颜色正确 */
.question-card .answer-option-horizontal.is-checked :deep(.el-radio__input.is-disabled .el-radio__inner::after) {
background-color: white !important;
}
/* 强化多选框选中状态的显示效果 */
.question-card .answer-option-horizontal.is-checked :deep(.el-checkbox__input.is-disabled.is-checked .el-checkbox__inner) {
background-color: rgb(0, 58, 255) !important;
border-color: rgb(0, 58, 255) !important;
}
.question-card .answer-option-horizontal.is-checked :deep(.el-checkbox__input.is-disabled.is-checked .el-checkbox__inner::after) {
border-color: white !important;
transform: rotate(45deg) scaleY(1) !important;
opacity: 1 !important;
}
/* 未选中状态下的单选/多选按钮样式调整 */
.question-card .answer-option-horizontal :deep(.el-radio__input.is-disabled .el-radio__inner) {
background-color: white !important;
border-color: #c0c4cc !important;
opacity: 0.8;
}
.question-card .answer-option-horizontal :deep(.el-checkbox__input.is-disabled .el-checkbox__inner) {
background-color: white !important;
border-color: #c0c4cc !important;
opacity: 0.8;
}
/* 专门针对多选框的禁用样式增强 */
.question-card .answer-option-horizontal :deep(.el-checkbox.is-disabled) {
opacity: 1 !important;
filter: none !important;
}
.question-card .answer-option-horizontal :deep(.el-checkbox.is-disabled .el-checkbox__input) {
filter: none !important;
}
.question-card .answer-option-horizontal :deep(.el-checkbox.is-disabled .el-checkbox__label) {
color: #475569 !important;
}
.question-card .answer-display-area .el-radio,
.question-card .answer-display-area .el-checkbox {
margin-right: 20px !important;
}
</style>
+13 -2
View File
@@ -104,7 +104,7 @@
<!-- <el-table-column label="题目内容" align="center" prop="questionContent" /> -->
<el-table-column label="所属分类" align="center" prop="categoryId">
<template #default="scope">
{{ satCategoryList.find((cat) => cat.id == scope.row.categoryId)?.categoryName || '--' }}
{{ satCategoryAllList.find((cat) => cat.id == scope.row.categoryId)?.categoryName || '--' }}
</template>
</el-table-column>
<el-table-column label="题目类型" align="center" prop="questionType">
@@ -210,7 +210,7 @@
<script setup name="SatQuestion" lang="ts">
import { listSatQuestion, getSatQuestion, delSatQuestion, addSatQuestion, updateSatQuestion } from '@/api/hq/satQuestion';
import { SatQuestionVO, SatQuestionQuery, SatQuestionForm } from '@/api/hq/satQuestion/types';
import { listSatCategory } from '@/api/hq/satCategory';
import { listSatCategory, listAllSatCategory } from '@/api/hq/satCategory';
import { SatCategoryVO } from '@/api/hq/satCategory/types';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
@@ -220,6 +220,7 @@ const { sys_question_category, sys_item_enable, sys_item_required, sys_item_dele
const satQuestionList = ref<SatQuestionVO[]>([]);
const satCategoryList = ref<SatCategoryVO[]>([]);
const satCategoryAllList = ref<SatCategoryVO[]>([]);
const buttonLoading = ref(false);
const loading = ref(true);
const showSearch = ref(true);
@@ -291,6 +292,15 @@ const getCategoryList = async () => {
satCategoryList.value = res.rows.sort((a, b) => a.sortOrder - b.sortOrder); // 按sort_order排序
};
/** 获取全部分类列表(包括已删除的) */
const getAllCategoryList = async () => {
const res = await listAllSatCategory({
pageNum: 1,
pageSize: 1000
}); // 获取所有分类,包括已删除的
satCategoryAllList.value = res.data.sort((a, b) => a.sortOrder - b.sortOrder); // 按sort_order排序
};
/** 取消按钮 */
const cancel = () => {
reset();
@@ -392,6 +402,7 @@ const searchRef = ref<HTMLElement>();
onMounted(() => {
getList();
getCategoryList();
getAllCategoryList();
});
</script>
<style scoped>
+41 -21
View File
@@ -59,16 +59,16 @@
/>
</el-form-item>
</el-col>
<el-col :span="6">
<!-- <el-form-item label="最大提交次数" prop="maxSubmitTimes">
<!-- <el-col :span="6"> -->
<!-- <el-form-item label="最大提交次数" prop="maxSubmitTimes">
<el-input v-model="queryParams.maxSubmitTimes" placeholder="请输入最大提交次数" clearable @keyup.enter="handleQuery" />
</el-form-item> -->
<el-form-item label="是否匿名" prop="isAnonymous">
<!-- <el-form-item label="是否匿名" prop="isAnonymous">
<el-select v-model="queryParams.isAnonymous" placeholder="请选择是否匿名" clearable>
<el-option v-for="dict in sys_yesno" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select>
</el-form-item>
</el-col>
</el-col> -->
<el-col :span="6">
<el-form-item label="是否发布" prop="isPublished">
<el-select v-model="queryParams.isPublished" placeholder="请选择是否发布" clearable>
@@ -137,8 +137,6 @@
</el-table-column> -->
<el-table-column label="问卷编号" align="center" prop="surveyCode" width="140" show-overflow-tooltip />
<el-table-column label="问卷标题" align="center" prop="surveyTitle" min-width="200" show-overflow-tooltip />
<el-table-column label="问卷描述" align="center" prop="surveyDesc" min-width="250" show-overflow-tooltip />
<el-table-column label="填写说明" align="center" prop="surveyInstruction" min-width="250" show-overflow-tooltip />
<el-table-column label="目标群体" align="center" prop="targetGroup" width="120">
<template #default="scope">
<dict-tag :options="sys_survey_people" :value="scope.row.targetGroup" />
@@ -146,20 +144,20 @@
</el-table-column>
<el-table-column label="开始时间" align="center" prop="startTime" width="180">
<template #default="scope">
<span>{{ parseTime(scope.row.startTime, '{y}-{m}-{d}') }}</span>
<span>{{ parseTime(scope.row.startTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
</template>
</el-table-column>
<el-table-column label="结束时间" align="center" prop="endTime" width="180">
<template #default="scope">
<span>{{ parseTime(scope.row.endTime, '{y}-{m}-{d}') }}</span>
<span>{{ parseTime(scope.row.endTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
</template>
</el-table-column>
<!-- <el-table-column label="最大提交次数" align="center" prop="maxSubmitTimes" /> -->
<el-table-column label="是否匿名" align="center" prop="isAnonymous" width="100">
<!-- <el-table-column label="是否匿名" align="center" prop="isAnonymous" width="100">
<template #default="scope">
<dict-tag :options="sys_yesno" :value="scope.row.isAnonymous" />
</template>
</el-table-column>
</el-table-column> -->
<el-table-column label="是否发布" align="center" prop="isPublished" width="100">
<template #default="scope">
<dict-tag :options="sys_yesno" :value="scope.row.isPublished" />
@@ -170,6 +168,8 @@
<dict-tag :options="sys_survey_status" :value="scope.row.status" />
</template>
</el-table-column>
<el-table-column label="问卷描述" align="center" prop="surveyDesc" min-width="250" show-overflow-tooltip />
<el-table-column label="填写说明" align="center" prop="surveyInstruction" min-width="250" show-overflow-tooltip />
<el-table-column label="备注" align="center" prop="remark" min-width="200" show-overflow-tooltip />
<el-table-column label="操作" align="center" width="280" fixed="right" class-name="small-padding fixed-width">
<template #default="scope">
@@ -339,6 +339,10 @@
<div v-for="(question, index) in selectedQuestions" :key="question.id || question.questionId" class="question-card">
<div class="question-header">
<div class="question-number">第 {{ index + 1 }} 题</div>
<div class="question-meta-info">
<el-tag size="small" type="primary">{{ getQuestionType(question.questionType) }}</el-tag>
<el-tag v-if="question.isRequired === 1 || question.isRequired === '1'" size="small" type="danger">必填</el-tag>
</div>
<div class="question-actions" v-if="!readonly">
<el-button type="text" size="small" @click="removeQuestion(index)">
<el-icon><Delete /></el-icon>
@@ -348,10 +352,6 @@
<div class="question-content-display">
<div class="question-title-display">{{ question.questionTitle }}</div>
<div class="question-meta-info">
<el-tag size="small" type="primary">{{ getQuestionType(question.questionType) }}</el-tag>
<el-tag v-if="question.isRequired === 1 || question.isRequired === '1'" size="small" type="danger">必填</el-tag>
</div>
<!-- 题目答案展示区域 -->
<div class="answer-display-area">
@@ -445,7 +445,7 @@
class="selector-question-item"
@click="toggleQuestionSelection(question.id)"
>
<div class="selector-question-checkbox">
<div class="selector-question-checkbox" @click.stop>
<el-checkbox :model-value="selectedQuestionIds.includes(question.id)" @change="toggleQuestionSelection(question.id)" />
</div>
<div class="selector-question-content">
@@ -991,6 +991,7 @@ import { listSatQuestion, getQuestionOptions } from '@/api/hq/satQuestion';
import { SatQuestionVO } from '@/api/hq/satQuestion/types';
import { listSatCategory } from '@/api/hq/satCategory';
import { SatCategoryVO } from '@/api/hq/satCategory/types';
import { createUniqueString } from '@/utils/index';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { sys_survey_people, sys_item_delete, sys_survey_status, sys_yesno } = toRefs<any>(
@@ -1156,6 +1157,13 @@ const handleSelectionChange = (selection: SatSurveyVO[]) => {
/** 新增按钮操作 */
const handleAdd = () => {
reset();
// 自动生成唯一问卷编号,格式可自定义(如 SAT-20240715-xxxxxx)
const now = new Date();
const yyyy = now.getFullYear();
const mm = String(now.getMonth() + 1).padStart(2, '0');
const dd = String(now.getDate()).padStart(2, '0');
const prefix = `SAT-${yyyy}${mm}${dd}`;
form.value.surveyCode = `${prefix}-${createUniqueString()}`;
dialog.visible = true;
dialog.title = '添加问卷';
};
@@ -1556,6 +1564,7 @@ const openQuestionSelector = () => {
/** 切换题目选择状态 */
const toggleQuestionSelection = (questionId: string | number) => {
// 处理点击行时的选择/取消选择
const index = selectedQuestionIds.value.indexOf(questionId);
if (index > -1) {
selectedQuestionIds.value.splice(index, 1);
@@ -1566,11 +1575,19 @@ const toggleQuestionSelection = (questionId: string | number) => {
/** 全选/取消全选 */
const toggleSelectAll = (checked: boolean) => {
// 强制刷新所有复选框状态
if (checked) {
selectedQuestionIds.value = filteredAvailableQuestions.value.map((q) => q.id);
// 全选 - 使用所有过滤后的题目ID
selectedQuestionIds.value = [...filteredAvailableQuestions.value.map((q) => q.id)];
} else {
// 清空选择
selectedQuestionIds.value = [];
}
// 强制下一个事件循环中重新渲染
nextTick(() => {
console.log(`选择状态已更新: ${selectedQuestionIds.value.length} 个题目被选中`);
});
};
/** 确认添加选中的题目 */
@@ -2272,9 +2289,9 @@ onMounted(() => {
.question-header {
display: flex;
justify-content: space-between;
justify-content: flex-start;
align-items: center;
padding: 12px 16px;
padding: 4px 16px;
background: #f8f9fa;
border-bottom: 1px solid #e4e7ed;
border-radius: 8px 8px 0 0;
@@ -2282,13 +2299,14 @@ onMounted(() => {
.question-number {
font-weight: 600;
color: #409eff;
color: var(--el-color-primary);
font-size: 14px;
}
.question-actions {
display: flex;
gap: 8px;
margin-left: auto;
}
.question-content-display {
@@ -2306,7 +2324,8 @@ onMounted(() => {
.question-meta-info {
display: flex;
gap: 8px;
margin-bottom: 16px;
margin: 0 16px;
align-items: center;
}
.answer-display-area {
@@ -2314,7 +2333,8 @@ onMounted(() => {
}
.answer-option {
display: block !important;
display: inline-flex !important;
margin-right: 56px;
margin-bottom: 8px;
white-space: normal;
word-break: break-all;