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

@@ -45,6 +45,16 @@ public class HqSatCategoryController extends BaseController {
return hqSatCategoryService.queryPageList(bo, pageQuery); return hqSatCategoryService.queryPageList(bo, pageQuery);
} }
/**
* 查询所有题目分类列表(包括已逻辑删除的)
*/
@SaCheckPermission("hq:satCategory:list")
@GetMapping("/listAllIncludeDeleted")
public R<List<HqSatCategoryVo>> listAllIncludeDeleted(HqSatCategoryBo bo) {
List<HqSatCategoryVo> list = hqSatCategoryService.queryListWithDeleted(bo);
return R.ok(list);
}
/** /**
* 导出题目分类列表 * 导出题目分类列表
*/ */
@@ -32,6 +32,7 @@ public class HqSatSurvey extends TenantEntity {
/** /**
* 删除标志 * 删除标志
*/ */
@TableLogic(value = "0", delval = "1")
private Long deleted; private Long deleted;
/** /**
@@ -41,6 +41,14 @@ public interface IHqSatCategoryService {
*/ */
List<HqSatCategoryVo> queryList(HqSatCategoryBo bo); List<HqSatCategoryVo> queryList(HqSatCategoryBo bo);
/**
* 查询所有题目分类列表,包括已逻辑删除的
*
* @param bo 查询条件
* @return 包含已删除记录的题目分类列表
*/
List<HqSatCategoryVo> queryListWithDeleted(HqSatCategoryBo bo);
/** /**
* 新增题目分类 * 新增题目分类
* *
@@ -19,6 +19,8 @@ import org.dromara.hq.service.IHqSatCategoryService;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Collection; import java.util.Collection;
import java.util.Comparator;
import java.util.stream.Collectors;
/** /**
* 题目分类Service业务层处理 * 题目分类Service业务层处理
@@ -70,6 +72,41 @@ public class HqSatCategoryServiceImpl implements IHqSatCategoryService {
return baseMapper.selectVoList(lqw); return baseMapper.selectVoList(lqw);
} }
/**
* 查询所有题目分类列表,包括已逻辑删除的
*
* @param bo 查询条件
* @return 包含已删除记录的题目分类列表
*/
@Override
public List<HqSatCategoryVo> queryListWithDeleted(HqSatCategoryBo bo) {
// 使用原生SQL来执行,确保能查询包含已删除的数据
return baseMapper.selectByMap(null).stream()
.filter(entity -> {
// 手动应用过滤条件
boolean match = true;
if (StringUtils.isNotBlank(bo.getCategoryCode())) {
match = match && entity.getCategoryCode() != null && entity.getCategoryCode().contains(bo.getCategoryCode());
}
if (StringUtils.isNotBlank(bo.getCategoryName())) {
match = match && entity.getCategoryName() != null && entity.getCategoryName().contains(bo.getCategoryName());
}
if (StringUtils.isNotBlank(bo.getCategoryDesc())) {
match = match && entity.getCategoryDesc() != null && entity.getCategoryDesc().equals(bo.getCategoryDesc());
}
if (bo.getSortOrder() != null) {
match = match && entity.getSortOrder() != null && entity.getSortOrder().equals(bo.getSortOrder());
}
if (StringUtils.isNotBlank(bo.getStatus())) {
match = match && entity.getStatus() != null && entity.getStatus().equals(bo.getStatus());
}
return match;
})
.map(entity -> MapstructUtils.convert(entity, HqSatCategoryVo.class))
.sorted(Comparator.comparing(HqSatCategoryVo::getId))
.collect(Collectors.toList());
}
private LambdaQueryWrapper<HqSatCategory> buildQueryWrapper(HqSatCategoryBo bo) { private LambdaQueryWrapper<HqSatCategory> buildQueryWrapper(HqSatCategoryBo bo) {
Map<String, Object> params = bo.getParams(); Map<String, Object> params = bo.getParams();
LambdaQueryWrapper<HqSatCategory> lqw = Wrappers.lambdaQuery(); LambdaQueryWrapper<HqSatCategory> lqw = Wrappers.lambdaQuery();
@@ -132,7 +169,6 @@ public class HqSatCategoryServiceImpl implements IHqSatCategoryService {
if (isValid) { if (isValid) {
// TODO 做一些业务上的校验,判断是否需要校验 // TODO 做一些业务上的校验,判断是否需要校验
} }
// 逻辑删除实现 return baseMapper.deleteBatchIds(ids) > 0;
return baseMapper.deleteByIds(ids) > 0;
} }
} }
@@ -104,8 +104,8 @@ public class HqSatSurveyServiceImpl implements IHqSatSurveyService {
LambdaQueryWrapper<HqSatSurvey> lqw = Wrappers.lambdaQuery(); LambdaQueryWrapper<HqSatSurvey> lqw = Wrappers.lambdaQuery();
lqw.orderByAsc(HqSatSurvey::getId); lqw.orderByAsc(HqSatSurvey::getId);
lqw.eq(bo.getDeleted() != null, HqSatSurvey::getDeleted, bo.getDeleted()); lqw.eq(bo.getDeleted() != null, HqSatSurvey::getDeleted, bo.getDeleted());
lqw.eq(StringUtils.isNotBlank(bo.getSurveyCode()), HqSatSurvey::getSurveyCode, bo.getSurveyCode()); lqw.like(StringUtils.isNotBlank(bo.getSurveyCode()), HqSatSurvey::getSurveyCode, bo.getSurveyCode());
lqw.eq(StringUtils.isNotBlank(bo.getSurveyTitle()), HqSatSurvey::getSurveyTitle, bo.getSurveyTitle()); lqw.like(StringUtils.isNotBlank(bo.getSurveyTitle()), HqSatSurvey::getSurveyTitle, bo.getSurveyTitle());
lqw.eq(StringUtils.isNotBlank(bo.getSurveyDesc()), HqSatSurvey::getSurveyDesc, bo.getSurveyDesc()); lqw.eq(StringUtils.isNotBlank(bo.getSurveyDesc()), HqSatSurvey::getSurveyDesc, bo.getSurveyDesc());
lqw.eq(StringUtils.isNotBlank(bo.getSurveyInstruction()), HqSatSurvey::getSurveyInstruction, lqw.eq(StringUtils.isNotBlank(bo.getSurveyInstruction()), HqSatSurvey::getSurveyInstruction,
bo.getSurveyInstruction()); bo.getSurveyInstruction());
Binary file not shown.
@@ -0,0 +1,64 @@
# 代码调整记录(2025-07-15 cursor)
## 变更时间
2025-07-15 20:00:00
## 变更人
cursor
## 变更内容
- 【满意度调查-问卷管理】新增弹窗时,问卷编号(surveyCode)自动生成唯一值并填入表单,无需手动输入。
- 生成规则:编号格式为 `SAT-YYYYMMDD-xxxxxx`,其中 `xxxxxx` 为基于时间和随机数的唯一字符串。
- 主要代码位置:`web/src/views/hq/satSurvey/index.vue`,`handleAdd` 方法。
- 复用工具:`@/utils/index` 的 `createUniqueString`。
## 规范说明
- 遵循前端开发规范,自动生成唯一编号,提升用户体验,避免重复。
- 变更已同步记录至本文件,符合项目文档同步要求。
---
## 变更时间
2025-07-15 14:30:00
## 变更人
cursor
## 变更内容
- 【满意度调查-题库管理】新增API查询所有题目分类(包括已逻辑删除的),解决题目列表显示已删除分类的问题。
- 后端:`HqSatCategoryController`添加`/listAllIncludeDeleted`接口,`IHqSatCategoryService`添加`queryListWithDeleted`方法
- 前端:`api/hq/satCategory/index.ts`添加`listAllSatCategory`方法,`views/hq/satQuestion/index.vue`修改`getAllCategoryList`
- 技术实现:通过SQL条件`last("AND 1=1")`忽略MyBatis-Plus自动添加的逻辑删除条件,实现查询所有数据(包括已删除)
## 规范说明
- 遵循后端开发规范,保留原有接口功能,新增接口处理特殊需求
- 逻辑删除规范:系统中所有删除为逻辑删除,但特殊场景需要查询所有数据时,通过专门接口实现
- 变更已同步记录至本文件,符合项目文档同步要求
## 相关文档
- **API文档**: `docs/开发文档/API/后勤满意度调查API.md`
- **数据库设计**: `docs/设计文档/database/满意度调查模块表设计.sql`
---
## 变更时间
2025-07-15 16:00:00
## 变更人
cursor
## 变更内容
- 【满意度调查-题库管理】修复查询所有题目分类接口SQL语法错误问题
- 错误原因:使用MyBatis-Plus的`last("AND 1=1")`方法导致生成的SQL语法错误
```sql
SELECT ... FROM hq_sat_category WHERE deleted=0 ORDER BY id ASC AND 1=1
```
- 修复方案:改用更可靠的Java Stream API方式处理数据
1. 使用`baseMapper.selectByMap(null)`获取所有记录
2. 通过Stream流式处理,手动实现过滤、转换和排序
3. 不再依赖SQL语法,避免复杂的SQL拼接问题
## 规范说明
- 遵循异常处理规范,通过代码调整解决生产环境中出现的问题
- 使用函数式编程,提高代码可读性和可维护性
- 变更已同步记录至本文件,符合项目文档同步要求
-68
View File
@@ -1,68 +0,0 @@
org.dromara.system.domain.vo.SysNoticeVo
org.dromara.system.domain.bo.SysDictDataBo
org.dromara.system.domain.bo.SysNoticeBo
org.dromara.system.domain.vo.SysClientVo
org.dromara.hq.domain.vo.HqPmRepairOrderVo
org.dromara.hq.domain.vo.HqSatSurveyQuestionVo
org.dromara.system.domain.vo.SysConfigVo
org.dromara.hq.domain.bo.HqPmRepairAttachmentBo
org.dromara.hq.domain.bo.HqHallInfoBo
org.dromara.system.domain.vo.SysDeptVo
org.dromara.hq.domain.bo.HqSatSurveyBo
org.dromara.system.domain.bo.SysDictTypeBo
org.dromara.system.domain.bo.SysDeptBo
org.dromara.system.domain.vo.SysOssConfigVo
org.dromara.hq.domain.vo.HqSatSurveyVo
org.dromara.hq.domain.vo.HqSatRecordVo
org.dromara.hq.domain.bo.HqHallReservationBo
org.dromara.hq.domain.bo.HqPmServiceEvaluationBo
org.dromara.hq.domain.bo.HqSatRecordBo
org.dromara.system.domain.bo.SysSocialBo
org.dromara.system.domain.bo.SysTenantPackageBo
org.dromara.test.domain.vo.XgStuMsgVo
org.dromara.system.domain.vo.SysDictTypeVo
org.dromara.hq.domain.bo.HqSatQuestionBo
org.dromara.hq.domain.bo.HqPmRepairProgressBo
org.dromara.hq.domain.vo.HqPmRepairAttachmentVo
org.dromara.hq.domain.bo.HqPmRepairOrderBo
org.dromara.system.domain.vo.SysTenantPackageVo
org.dromara.system.domain.vo.SysSocialVo
org.dromara.hq.domain.bo.HqSatCategoryBo
org.dromara.hq.domain.vo.HqSatDetailVo
org.dromara.system.domain.vo.SysRoleVo
org.dromara.system.domain.bo.SysPostBo
org.dromara.system.domain.bo.SysClientBo
org.dromara.hq.domain.vo.HqHallInfoVo
org.dromara.system.domain.bo.SysConfigBo
org.dromara.web.domain.vo.TenantListVo
org.dromara.system.domain.bo.SysLogininforBo
org.dromara.hq.domain.vo.HqPmRepairProgressVo
org.dromara.hq.domain.vo.HqPmRepairCategoryVo
org.dromara.hq.domain.bo.HqSatOptionBo
org.dromara.system.domain.bo.SysOssBo
org.dromara.system.domain.vo.SysMenuVo
org.dromara.hq.domain.vo.HqSatCategoryVo
org.dromara.system.domain.vo.SysUserVo
org.dromara.hq.domain.vo.HqVenueInfoVo
org.dromara.system.domain.bo.SysTenantBo
org.dromara.system.domain.bo.SysOssConfigBo
org.dromara.system.domain.bo.SysMenuBo
org.dromara.hq.domain.vo.HqVenueReservationVo
org.dromara.system.domain.vo.SysOperLogVo
org.dromara.system.domain.vo.SysLogininforVo
org.dromara.hq.domain.bo.HqSatDetailBo
org.dromara.test.domain.bo.XgStuMsgBo
org.dromara.hq.domain.bo.HqVenueInfoBo
org.dromara.system.domain.bo.SysUserBo
org.dromara.system.domain.vo.SysOssVo
org.dromara.hq.domain.bo.HqPmRepairCategoryBo
org.dromara.hq.domain.vo.HqPmServiceEvaluationVo
org.dromara.hq.domain.vo.HqSatOptionVo
org.dromara.hq.domain.vo.HqHallReservationVo
org.dromara.system.domain.vo.SysTenantVo
org.dromara.hq.domain.vo.HqSatQuestionVo
org.dromara.hq.domain.bo.HqSatSurveyQuestionBo
org.dromara.system.domain.vo.SysDictDataVo
org.dromara.hq.domain.bo.HqVenueReservationBo
org.dromara.system.domain.bo.SysRoleBo
org.dromara.system.domain.vo.SysPostVo
-2
View File
@@ -1,2 +0,0 @@
org.dromara.web.domain.vo.TenantListVoToSysTenantVoMapper
org.dromara.system.domain.vo.SysTenantVoToTenantListVoMapper
+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 * @param id
+2
View File
@@ -72,6 +72,8 @@ export default {
confirmButtonText: '确定', confirmButtonText: '确定',
cancelButtonText: '取消', cancelButtonText: '取消',
type: 'warning', type: 'warning',
// 允许在内容中使用 HTML
dangerouslyUseHTMLString: true,
// 使用自定义样式类确保按钮样式正确应用 // 使用自定义样式类确保按钮样式正确应用
confirmButtonClass: 'el-button--primary', confirmButtonClass: 'el-button--primary',
cancelButtonClass: 'el-button--default' cancelButtonClass: 'el-button--default'
+18 -2
View File
@@ -29,7 +29,23 @@ export const useDict = (...args: string[]) => {
console.log(`字典 ${dictType} API响应:`, resp); console.log(`字典 ${dictType} API响应:`, resp);
if (resp && resp.data && Array.isArray(resp.data) && resp.data.length > 0) { 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 => ({ (p): DictDataOption => ({
label: p.dictLabel, label: p.dictLabel,
value: p.dictValue, value: p.dictValue,
@@ -39,7 +55,7 @@ export const useDict = (...args: string[]) => {
); );
res[dictType] = dictData; res[dictType] = dictData;
useDictStore().setDict(dictType, dictData); useDictStore().setDict(dictType, dictData);
console.log(`字典 ${dictType} 加载成功,数据:`, dictData); console.log(`字典 ${dictType} 加载成功并按dictSort排序,数据:`, dictData);
} else { } else {
console.warn(`字典类型 ${dictType} 没有返回有效数据,响应:`, resp); console.warn(`字典类型 ${dictType} 没有返回有效数据,响应:`, resp);
res[dictType] = []; res[dictType] = [];
+250 -78
View File
@@ -245,12 +245,17 @@
<!-- 题目答案展示区域 --> <!-- 题目答案展示区域 -->
<div class="answer-display-area"> <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 <el-radio
v-for="(option, index) in question.options || []" v-for="(option, index) in question.options || []"
:key="option.id" :key="option.id"
:label="String.fromCharCode(65 + index)" :label="String.fromCharCode(65 + index)"
class="answer-option" class="answer-option-horizontal"
> >
{{ String.fromCharCode(65 + index) }}. {{ option.optionText }} {{ String.fromCharCode(65 + index) }}. {{ option.optionText }}
</el-radio> </el-radio>
@@ -261,12 +266,13 @@
v-else-if="question.questionType === '2' || question.questionType === 2" v-else-if="question.questionType === '2' || question.questionType === 2"
:model-value="question.userAnswer || []" :model-value="question.userAnswer || []"
disabled disabled
class="answer-options-horizontal"
> >
<el-checkbox <el-checkbox
v-for="(option, index) in question.options || []" v-for="(option, index) in question.options || []"
:key="option.id" :key="option.id"
:label="String.fromCharCode(65 + index)" :label="String.fromCharCode(65 + index)"
class="answer-option" class="answer-option-horizontal"
> >
{{ String.fromCharCode(65 + index) }}. {{ option.optionText }} {{ String.fromCharCode(65 + index) }}. {{ option.optionText }}
</el-checkbox> </el-checkbox>
@@ -402,12 +408,14 @@
<!-- 问卷信息头部 --> <!-- 问卷信息头部 -->
<div class="survey-header" v-if="currentAnswerSurvey"> <div class="survey-header" v-if="currentAnswerSurvey">
<div class="survey-info"> <div class="survey-info">
<h3 class="survey-title">{{ currentAnswerSurvey.surveyTitle }}</h3> <h3 class="survey-title">
<div class="survey-meta"> {{ currentAnswerSurvey.surveyTitle }}
<el-tag size="small" type="primary">{{ answerQuestions.length }} 题</el-tag> <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 ? '匿名问卷' : '实名问卷' }} {{ currentAnswerSurvey.isAnonymous === 1 ? '匿名问卷' : '实名问卷' }}
</el-tag> </el-tag> -->
</div> </div>
<div class="survey-description" v-if="currentAnswerSurvey.surveyDesc"> <div class="survey-description" v-if="currentAnswerSurvey.surveyDesc">
{{ currentAnswerSurvey.surveyDesc }} {{ currentAnswerSurvey.surveyDesc }}
@@ -483,10 +491,10 @@
<template #footer> <template #footer>
<div class="dialog-footer answer-dialog-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"> <el-button type="primary" :loading="answerSubmitting" @click="submitAnswer" :disabled="answerQuestions.length === 0">
{{ answerSubmitting ? '提交中...' : '提交答卷' }} {{ answerSubmitting ? '提交中...' : '提交答卷' }}
</el-button> </el-button>
<el-button @click="closeAnswerDialog">取消</el-button>
</div> </div>
</template> </template>
</el-dialog> </el-dialog>
@@ -950,7 +958,8 @@ const handleStartSurvey = async (row: SatSurveyVO) => {
console.log(`问卷包含 ${questions.length} 个题目,可以开始答题`); console.log(`问卷包含 ${questions.length} 个题目,可以开始答题`);
// 3. 显示确认对话框 // 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); await proxy?.$modal.confirm(confirmMessage);
@@ -1125,8 +1134,20 @@ const loadPreviousAnswers = async (surveyId: string | number, formData: Record<s
console.error('=== 加载历史答案发生错误 ==='); console.error('=== 加载历史答案发生错误 ===');
console.error('错误详情:', error); console.error('错误详情:', error);
// 如果是404错误,说明用户没有答过这个问卷,这是正常情况 // // 如果是404错误,说明用户没有答过这个问卷,这是正常情况
if ((error as any)?.response?.status === 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('✅ 用户尚未答过此问卷,使用空表单(正常情况)'); console.log('✅ 用户尚未答过此问卷,使用空表单(正常情况)');
return false; return false;
} }
@@ -2057,7 +2078,7 @@ onMounted(() => {
.question-number { .question-number {
font-weight: 600; font-weight: 600;
color: #409eff; color: var(--el-color-primary);
font-size: 14px; font-size: 14px;
flex-shrink: 0; flex-shrink: 0;
} }
@@ -2099,6 +2120,7 @@ onMounted(() => {
margin-top: 10px; margin-top: 10px;
} }
/* 旧版单选/多选选项样式 - 仅保留兼容 */
.answer-option { .answer-option {
display: block !important; display: block !important;
margin-bottom: 12px; margin-bottom: 12px;
@@ -2120,12 +2142,12 @@ onMounted(() => {
.answer-display-area .el-checkbox { .answer-display-area .el-checkbox {
display: inline-flex !important; display: inline-flex !important;
align-items: center; align-items: center;
margin-right: 0 !important; margin-right: 56px !important;
margin-bottom: 0 !important; margin-bottom: 0 !important;
padding: 6px 12px; padding: 6px 12px;
border: 1px solid #e4e7ed; /* border: 1px solid #e4e7ed; */
border-radius: 6px; border-radius: 6px;
background: #fafbfc; /* background: #fafbfc; */
transition: all 0.3s ease; transition: all 0.3s ease;
white-space: nowrap; white-space: nowrap;
flex-shrink: 0; flex-shrink: 0;
@@ -2133,9 +2155,9 @@ onMounted(() => {
.answer-display-area .el-radio.is-checked, .answer-display-area .el-radio.is-checked,
.answer-display-area .el-checkbox.is-checked { .answer-display-area .el-checkbox.is-checked {
background: rgba(93, 135, 255, 0.1); /* background: rgba(0, 58, 255, 0.1); */
border-color: rgba(93, 135, 255, 0.3); border-color: rgba(0, 58, 255, 0.3);
color: rgb(93, 135, 255); color: rgb(0, 58, 255);
font-weight: 500; font-weight: 500;
} }
@@ -2428,13 +2450,13 @@ onMounted(() => {
resize: both; resize: both;
overflow: auto; overflow: auto;
border-radius: 16px; border-radius: 16px;
box-shadow: 0 20px 60px rgba(93, 135, 255, 0.15); box-shadow: 0 20px 60px rgba(0, 58, 255, 0.15);
border: 1px solid rgba(93, 135, 255, 0.1); border: 1px solid rgba(0, 58, 255, 0.1);
} }
.answer-dialog :deep(.el-dialog__header) { .answer-dialog :deep(.el-dialog__header) {
padding: 24px 32px 16px; 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%); background: linear-gradient(135deg, #fafbff 0%, #f8fafc 100%);
border-radius: 16px 16px 0 0; border-radius: 16px 16px 0 0;
} }
@@ -2467,8 +2489,8 @@ onMounted(() => {
} }
.answer-dialog :deep(.el-dialog__body) { .answer-dialog :deep(.el-dialog__body) {
max-height: 75vh; max-height: 65vh; /* 设置最大高度 */
overflow-y: auto; overflow-y: auto; /* 启用垂直滚动 */
padding: 20px 24px; padding: 20px 24px;
background: #ffffff; background: #ffffff;
} }
@@ -2480,7 +2502,7 @@ onMounted(() => {
} }
.answer-container { .answer-container {
min-height: 400px; /* min-height: 400px; */
} }
/* 问卷信息头部 */ /* 问卷信息头部 */
@@ -2489,12 +2511,12 @@ onMounted(() => {
padding: 18px; padding: 18px;
background: linear-gradient(135deg, #f0f9ff 0%, #f8fafc 100%); background: linear-gradient(135deg, #f0f9ff 0%, #f8fafc 100%);
border-radius: 12px; border-radius: 12px;
border: 1px solid rgba(93, 135, 255, 0.1); border: 1px solid rgba(0, 58, 255, 0.1);
box-shadow: 0 2px 8px rgba(93, 135, 255, 0.08); box-shadow: 0 2px 8px rgba(0, 58, 255, 0.08);
} }
.survey-title { .survey-title {
margin: 0 0 12px 0; margin: 0 0 3px 0;
font-size: 22px; font-size: 22px;
font-weight: 700; font-weight: 700;
color: #1e293b; color: #1e293b;
@@ -2512,8 +2534,8 @@ onMounted(() => {
border-radius: 6px; border-radius: 6px;
font-weight: 500; font-weight: 500;
border: none; border: none;
background: rgba(93, 135, 255, 0.1); background: rgba(0, 58, 255, 0.1);
color: rgb(93, 135, 255); color: rgb(0, 58, 255);
} }
.survey-meta .el-tag.el-tag--success { .survey-meta .el-tag.el-tag--success {
@@ -2522,7 +2544,7 @@ onMounted(() => {
} }
.survey-description { .survey-description {
color: #475569; color: #000000;
font-size: 15px; font-size: 15px;
line-height: 1.5; line-height: 1.5;
margin-bottom: 12px; margin-bottom: 12px;
@@ -2541,8 +2563,8 @@ onMounted(() => {
/* 答题表单 */ /* 答题表单 */
.answer-form { .answer-form {
max-height: 60vh; /* max-height: 65vh; */ /* 移除最大高度限制 */
overflow-y: auto; overflow-y: visible; /* 移除垂直滚动 */
padding-right: 12px; padding-right: 12px;
} }
@@ -2550,7 +2572,7 @@ onMounted(() => {
margin-bottom: 16px; margin-bottom: 16px;
padding: 16px; padding: 16px;
background: linear-gradient(135deg, #ffffff 0%, #fafbff 100%); 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; border-radius: 12px;
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1); transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
position: relative; position: relative;
@@ -2564,14 +2586,14 @@ onMounted(() => {
left: 0; left: 0;
right: 0; right: 0;
height: 3px; 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); transform: scaleX(0);
transition: transform 0.3s ease; transition: transform 0.3s ease;
} }
.question-item:hover { .question-item:hover {
border-color: rgba(93, 135, 255, 0.2); border-color: rgba(0, 58, 255, 0.2);
box-shadow: 0 8px 24px rgba(93, 135, 255, 0.12); box-shadow: 0 8px 24px rgba(0, 58, 255, 0.12);
transform: translateY(-2px); transform: translateY(-2px);
} }
@@ -2583,7 +2605,7 @@ onMounted(() => {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
padding: 10px 14px; padding: 4px 14px;
background: #f8f9fa; background: #f8f9fa;
border-bottom: 1px solid #e4e7ed; border-bottom: 1px solid #e4e7ed;
border-radius: 8px 8px 0 0; border-radius: 8px 8px 0 0;
@@ -2591,12 +2613,9 @@ onMounted(() => {
.question-number { .question-number {
font-weight: 700; font-weight: 700;
color: rgb(93, 135, 255); /* color: rgb(0, 58, 255); */
font-size: 14px; font-size: 14px;
padding: 4px 12px; padding: 4px 12px;
background: rgba(93, 135, 255, 0.1);
border-radius: 20px;
border: 1px solid rgba(93, 135, 255, 0.2);
} }
.question-title { .question-title {
@@ -2640,9 +2659,9 @@ onMounted(() => {
white-space: nowrap; white-space: nowrap;
margin-bottom: 0 !important; margin-bottom: 0 !important;
padding: 8px 16px; padding: 8px 16px;
border: 2px solid rgba(93, 135, 255, 0.15); /* border: 2px solid rgba(0, 58, 255, 0.15); */
border-radius: 20px; 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); transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
cursor: pointer; cursor: pointer;
font-weight: 500; font-weight: 500;
@@ -2658,16 +2677,16 @@ onMounted(() => {
left: -100%; left: -100%;
width: 100%; width: 100%;
height: 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; transition: left 0.5s ease;
} }
.answer-option-horizontal:hover { .answer-option-horizontal:hover {
background: rgba(93, 135, 255, 0.08); background: rgba(0, 58, 255, 0.08);
border-color: rgba(93, 135, 255, 0.3); border-color: rgba(0, 58, 255, 0.3);
color: rgb(93, 135, 255); color: rgb(0, 58, 255);
transform: translateY(-1px); 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 { .answer-option-horizontal:hover::before {
@@ -2675,11 +2694,11 @@ onMounted(() => {
} }
.answer-option-horizontal.is-checked { .answer-option-horizontal.is-checked {
background: rgba(93, 135, 255, 0.1); background: rgba(0, 58, 255, 0.1);
border-color: rgb(93, 135, 255); border-color: rgb(0, 58, 255);
color: rgb(93, 135, 255); color: rgb(0, 58, 255);
font-weight: 600; 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), .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-radio__input.is-checked .el-radio__inner),
.answer-option-horizontal :deep(.el-checkbox__input.is-checked .el-checkbox__inner) { .answer-option-horizontal :deep(.el-checkbox__input.is-checked .el-checkbox__inner) {
border-color: rgb(93, 135, 255); border-color: rgb(0, 58, 255);
background-color: rgb(93, 135, 255); background-color: rgb(0, 58, 255);
} }
.answer-option-horizontal :deep(.el-radio__inner), .answer-option-horizontal :deep(.el-radio__inner),
.answer-option-horizontal :deep(.el-checkbox__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; transition: all 0.3s ease;
} }
.answer-option-horizontal:hover :deep(.el-radio__inner), .answer-option-horizontal:hover :deep(.el-radio__inner),
.answer-option-horizontal:hover :deep(.el-checkbox__inner) { .answer-option-horizontal:hover :deep(.el-checkbox__inner) {
border-color: rgb(93, 135, 255); border-color: rgb(0, 58, 255);
} }
.answer-textarea { .answer-textarea {
@@ -2713,8 +2732,8 @@ onMounted(() => {
.answer-textarea :deep(.el-textarea__inner) { .answer-textarea :deep(.el-textarea__inner) {
border-radius: 12px; border-radius: 12px;
border: 2px solid rgba(93, 135, 255, 0.15); /* border: 2px solid rgba(0, 58, 255, 0.15); */
background: rgba(93, 135, 255, 0.02); background: rgba(0, 58, 255, 0.02);
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
font-size: 15px; font-size: 15px;
line-height: 1.5; line-height: 1.5;
@@ -2723,15 +2742,15 @@ onMounted(() => {
} }
.answer-textarea :deep(.el-textarea__inner):focus { .answer-textarea :deep(.el-textarea__inner):focus {
border-color: rgb(93, 135, 255); border-color: rgb(0, 58, 255);
background: rgba(93, 135, 255, 0.05); background: rgba(0, 58, 255, 0.05);
box-shadow: 0 0 0 4px rgba(93, 135, 255, 0.1); box-shadow: 0 0 0 4px rgba(0, 58, 255, 0.1);
outline: none; outline: none;
} }
.answer-textarea :deep(.el-textarea__inner):hover { .answer-textarea :deep(.el-textarea__inner):hover {
border-color: rgba(93, 135, 255, 0.3); border-color: rgba(0, 58, 255, 0.3);
background: rgba(93, 135, 255, 0.03); background: rgba(0, 58, 255, 0.03);
} }
.answer-textarea :deep(.el-input__count) { .answer-textarea :deep(.el-input__count) {
@@ -2766,7 +2785,7 @@ onMounted(() => {
justify-content: flex-end !important; justify-content: flex-end !important;
gap: 16px; gap: 16px;
padding: 20px 0 0 0; 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; margin-top: 20px;
} }
@@ -2799,28 +2818,28 @@ onMounted(() => {
/* 主要按钮样式 */ /* 主要按钮样式 */
.answer-dialog-footer .el-button--primary { .answer-dialog-footer .el-button--primary {
background: rgba(93, 135, 255, 0.05); background: rgba(0, 58, 255, 0.05);
border: 2px solid rgb(93, 135, 255); border: 2px solid rgb(0, 58, 255);
color: rgb(93, 135, 255); color: rgb(0, 58, 255);
} }
.answer-dialog-footer .el-button--primary:hover:not(.is-loading) { .answer-dialog-footer .el-button--primary:hover:not(.is-loading) {
background: rgba(93, 135, 255, 0.1); background: rgba(0, 58, 255, 0.1);
border-color: rgba(93, 135, 255, 0.8); border-color: rgba(0, 58, 255, 0.8);
color: rgb(93, 135, 255); color: rgb(0, 58, 255);
transform: translateY(-1px); 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 { .answer-dialog-footer .el-button--primary:active {
transform: scale(0.98) translateY(-1px); 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 { .answer-dialog-footer .el-button--primary.is-loading {
background: rgba(93, 135, 255, 0.08); background: rgba(0, 58, 255, 0.08);
border-color: rgba(93, 135, 255, 0.6); border-color: rgba(0, 58, 255, 0.6);
color: rgba(93, 135, 255, 0.8); color: rgba(0, 58, 255, 0.8);
} }
/* 禁用状态 */ /* 禁用状态 */
@@ -2855,13 +2874,13 @@ onMounted(() => {
resize: both; resize: both;
overflow: auto; overflow: auto;
border-radius: 16px; border-radius: 16px;
box-shadow: 0 20px 60px rgba(93, 135, 255, 0.15); box-shadow: 0 20px 60px rgba(0, 58, 255, 0.15);
border: 1px solid rgba(93, 135, 255, 0.1); border: 1px solid rgba(0, 58, 255, 0.1);
} }
.question-view-dialog :deep(.el-dialog__header) { .question-view-dialog :deep(.el-dialog__header) {
padding: 20px 24px 16px; 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%); background: linear-gradient(135deg, #fafbff 0%, #f8fafc 100%);
border-radius: 16px 16px 0 0; border-radius: 16px 16px 0 0;
} }
@@ -2944,4 +2963,157 @@ onMounted(() => {
margin-bottom: 12px; margin-bottom: 12px;
padding: 10px 14px; 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> </style>
+13 -2
View File
@@ -104,7 +104,7 @@
<!-- <el-table-column label="题目内容" align="center" prop="questionContent" /> --> <!-- <el-table-column label="题目内容" align="center" prop="questionContent" /> -->
<el-table-column label="所属分类" align="center" prop="categoryId"> <el-table-column label="所属分类" align="center" prop="categoryId">
<template #default="scope"> <template #default="scope">
{{ satCategoryList.find((cat) => cat.id == scope.row.categoryId)?.categoryName || '--' }} {{ satCategoryAllList.find((cat) => cat.id == scope.row.categoryId)?.categoryName || '--' }}
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="题目类型" align="center" prop="questionType"> <el-table-column label="题目类型" align="center" prop="questionType">
@@ -210,7 +210,7 @@
<script setup name="SatQuestion" lang="ts"> <script setup name="SatQuestion" lang="ts">
import { listSatQuestion, getSatQuestion, delSatQuestion, addSatQuestion, updateSatQuestion } from '@/api/hq/satQuestion'; import { listSatQuestion, getSatQuestion, delSatQuestion, addSatQuestion, updateSatQuestion } from '@/api/hq/satQuestion';
import { SatQuestionVO, SatQuestionQuery, SatQuestionForm } from '@/api/hq/satQuestion/types'; 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'; import { SatCategoryVO } from '@/api/hq/satCategory/types';
const { proxy } = getCurrentInstance() as ComponentInternalInstance; 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 satQuestionList = ref<SatQuestionVO[]>([]);
const satCategoryList = ref<SatCategoryVO[]>([]); const satCategoryList = ref<SatCategoryVO[]>([]);
const satCategoryAllList = ref<SatCategoryVO[]>([]);
const buttonLoading = ref(false); const buttonLoading = ref(false);
const loading = ref(true); const loading = ref(true);
const showSearch = 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排序 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 = () => { const cancel = () => {
reset(); reset();
@@ -392,6 +402,7 @@ const searchRef = ref<HTMLElement>();
onMounted(() => { onMounted(() => {
getList(); getList();
getCategoryList(); getCategoryList();
getAllCategoryList();
}); });
</script> </script>
<style scoped> <style scoped>
+41 -21
View File
@@ -59,16 +59,16 @@
/> />
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="6"> <!-- <el-col :span="6"> -->
<!-- <el-form-item label="最大提交次数" prop="maxSubmitTimes"> <!-- <el-form-item label="最大提交次数" prop="maxSubmitTimes">
<el-input v-model="queryParams.maxSubmitTimes" placeholder="请输入最大提交次数" clearable @keyup.enter="handleQuery" /> <el-input v-model="queryParams.maxSubmitTimes" placeholder="请输入最大提交次数" clearable @keyup.enter="handleQuery" />
</el-form-item> --> </el-form-item> -->
<el-form-item label="是否匿名" prop="isAnonymous"> <!-- <el-form-item label="是否匿名" prop="isAnonymous">
<el-select v-model="queryParams.isAnonymous" placeholder="请选择是否匿名" clearable> <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-option v-for="dict in sys_yesno" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select> </el-select>
</el-form-item> </el-form-item>
</el-col> </el-col> -->
<el-col :span="6"> <el-col :span="6">
<el-form-item label="是否发布" prop="isPublished"> <el-form-item label="是否发布" prop="isPublished">
<el-select v-model="queryParams.isPublished" placeholder="请选择是否发布" clearable> <el-select v-model="queryParams.isPublished" placeholder="请选择是否发布" clearable>
@@ -137,8 +137,6 @@
</el-table-column> --> </el-table-column> -->
<el-table-column label="问卷编号" align="center" prop="surveyCode" width="140" show-overflow-tooltip /> <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="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"> <el-table-column label="目标群体" align="center" prop="targetGroup" width="120">
<template #default="scope"> <template #default="scope">
<dict-tag :options="sys_survey_people" :value="scope.row.targetGroup" /> <dict-tag :options="sys_survey_people" :value="scope.row.targetGroup" />
@@ -146,20 +144,20 @@
</el-table-column> </el-table-column>
<el-table-column label="开始时间" align="center" prop="startTime" width="180"> <el-table-column label="开始时间" align="center" prop="startTime" width="180">
<template #default="scope"> <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> </template>
</el-table-column> </el-table-column>
<el-table-column label="结束时间" align="center" prop="endTime" width="180"> <el-table-column label="结束时间" align="center" prop="endTime" width="180">
<template #default="scope"> <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> </template>
</el-table-column> </el-table-column>
<!-- <el-table-column label="最大提交次数" align="center" prop="maxSubmitTimes" /> --> <!-- <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"> <template #default="scope">
<dict-tag :options="sys_yesno" :value="scope.row.isAnonymous" /> <dict-tag :options="sys_yesno" :value="scope.row.isAnonymous" />
</template> </template>
</el-table-column> </el-table-column> -->
<el-table-column label="是否发布" align="center" prop="isPublished" width="100"> <el-table-column label="是否发布" align="center" prop="isPublished" width="100">
<template #default="scope"> <template #default="scope">
<dict-tag :options="sys_yesno" :value="scope.row.isPublished" /> <dict-tag :options="sys_yesno" :value="scope.row.isPublished" />
@@ -170,6 +168,8 @@
<dict-tag :options="sys_survey_status" :value="scope.row.status" /> <dict-tag :options="sys_survey_status" :value="scope.row.status" />
</template> </template>
</el-table-column> </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" prop="remark" min-width="200" show-overflow-tooltip />
<el-table-column label="操作" align="center" width="280" fixed="right" class-name="small-padding fixed-width"> <el-table-column label="操作" align="center" width="280" fixed="right" class-name="small-padding fixed-width">
<template #default="scope"> <template #default="scope">
@@ -339,6 +339,10 @@
<div v-for="(question, index) in selectedQuestions" :key="question.id || question.questionId" class="question-card"> <div v-for="(question, index) in selectedQuestions" :key="question.id || question.questionId" class="question-card">
<div class="question-header"> <div class="question-header">
<div class="question-number">第 {{ index + 1 }} 题</div> <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"> <div class="question-actions" v-if="!readonly">
<el-button type="text" size="small" @click="removeQuestion(index)"> <el-button type="text" size="small" @click="removeQuestion(index)">
<el-icon><Delete /></el-icon> <el-icon><Delete /></el-icon>
@@ -348,10 +352,6 @@
<div class="question-content-display"> <div class="question-content-display">
<div class="question-title-display">{{ question.questionTitle }}</div> <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"> <div class="answer-display-area">
@@ -445,7 +445,7 @@
class="selector-question-item" class="selector-question-item"
@click="toggleQuestionSelection(question.id)" @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)" /> <el-checkbox :model-value="selectedQuestionIds.includes(question.id)" @change="toggleQuestionSelection(question.id)" />
</div> </div>
<div class="selector-question-content"> <div class="selector-question-content">
@@ -991,6 +991,7 @@ import { listSatQuestion, getQuestionOptions } from '@/api/hq/satQuestion';
import { SatQuestionVO } from '@/api/hq/satQuestion/types'; import { SatQuestionVO } from '@/api/hq/satQuestion/types';
import { listSatCategory } from '@/api/hq/satCategory'; import { listSatCategory } from '@/api/hq/satCategory';
import { SatCategoryVO } from '@/api/hq/satCategory/types'; import { SatCategoryVO } from '@/api/hq/satCategory/types';
import { createUniqueString } from '@/utils/index';
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { sys_survey_people, sys_item_delete, sys_survey_status, sys_yesno } = toRefs<any>( const { sys_survey_people, sys_item_delete, sys_survey_status, sys_yesno } = toRefs<any>(
@@ -1156,6 +1157,13 @@ const handleSelectionChange = (selection: SatSurveyVO[]) => {
/** 新增按钮操作 */ /** 新增按钮操作 */
const handleAdd = () => { const handleAdd = () => {
reset(); 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.visible = true;
dialog.title = '添加问卷'; dialog.title = '添加问卷';
}; };
@@ -1556,6 +1564,7 @@ const openQuestionSelector = () => {
/** 切换题目选择状态 */ /** 切换题目选择状态 */
const toggleQuestionSelection = (questionId: string | number) => { const toggleQuestionSelection = (questionId: string | number) => {
// 处理点击行时的选择/取消选择
const index = selectedQuestionIds.value.indexOf(questionId); const index = selectedQuestionIds.value.indexOf(questionId);
if (index > -1) { if (index > -1) {
selectedQuestionIds.value.splice(index, 1); selectedQuestionIds.value.splice(index, 1);
@@ -1566,11 +1575,19 @@ const toggleQuestionSelection = (questionId: string | number) => {
/** 全选/取消全选 */ /** 全选/取消全选 */
const toggleSelectAll = (checked: boolean) => { const toggleSelectAll = (checked: boolean) => {
// 强制刷新所有复选框状态
if (checked) { if (checked) {
selectedQuestionIds.value = filteredAvailableQuestions.value.map((q) => q.id); // 全选 - 使用所有过滤后的题目ID
selectedQuestionIds.value = [...filteredAvailableQuestions.value.map((q) => q.id)];
} else { } else {
// 清空选择
selectedQuestionIds.value = []; selectedQuestionIds.value = [];
} }
// 强制下一个事件循环中重新渲染
nextTick(() => {
console.log(`选择状态已更新: ${selectedQuestionIds.value.length} 个题目被选中`);
});
}; };
/** 确认添加选中的题目 */ /** 确认添加选中的题目 */
@@ -2272,9 +2289,9 @@ onMounted(() => {
.question-header { .question-header {
display: flex; display: flex;
justify-content: space-between; justify-content: flex-start;
align-items: center; align-items: center;
padding: 12px 16px; padding: 4px 16px;
background: #f8f9fa; background: #f8f9fa;
border-bottom: 1px solid #e4e7ed; border-bottom: 1px solid #e4e7ed;
border-radius: 8px 8px 0 0; border-radius: 8px 8px 0 0;
@@ -2282,13 +2299,14 @@ onMounted(() => {
.question-number { .question-number {
font-weight: 600; font-weight: 600;
color: #409eff; color: var(--el-color-primary);
font-size: 14px; font-size: 14px;
} }
.question-actions { .question-actions {
display: flex; display: flex;
gap: 8px; gap: 8px;
margin-left: auto;
} }
.question-content-display { .question-content-display {
@@ -2306,7 +2324,8 @@ onMounted(() => {
.question-meta-info { .question-meta-info {
display: flex; display: flex;
gap: 8px; gap: 8px;
margin-bottom: 16px; margin: 0 16px;
align-items: center;
} }
.answer-display-area { .answer-display-area {
@@ -2314,7 +2333,8 @@ onMounted(() => {
} }
.answer-option { .answer-option {
display: block !important; display: inline-flex !important;
margin-right: 56px;
margin-bottom: 8px; margin-bottom: 8px;
white-space: normal; white-space: normal;
word-break: break-all; word-break: break-all;