修改问题
This commit is contained in:
parent
12d943db6c
commit
66773d3577
|
|
@ -549,13 +549,19 @@ public class PsyUserProfileServiceImpl implements IPsyUserProfileService
|
|||
|
||||
if (failureNum > 0)
|
||||
{
|
||||
failureMsg.insert(0, "很抱歉,导入失败!共 " + failureNum + " 条数据格式不正确,错误如下:");
|
||||
StringBuilder resultMsg = new StringBuilder();
|
||||
// 如果有成功的数据,先显示成功信息
|
||||
if (successNum > 0)
|
||||
{
|
||||
failureMsg.append("<br/><br/>").append(successMsg);
|
||||
resultMsg.append(successMsg).append("<br/><br/>");
|
||||
}
|
||||
importProgressManager.finishFailure(progressKey, failureMsg.toString());
|
||||
throw new ServiceException(failureMsg.toString());
|
||||
// 再显示失败信息
|
||||
failureMsg.insert(0, "很抱歉,导入失败!共 " + failureNum + " 条数据格式不正确,错误如下:");
|
||||
resultMsg.append(failureMsg);
|
||||
|
||||
String finalMsg = resultMsg.toString();
|
||||
importProgressManager.finishFailure(progressKey, finalMsg);
|
||||
throw new ServiceException(finalMsg);
|
||||
}
|
||||
|
||||
if (successNum > 0)
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||
<result property="updateBy" column="update_by" />
|
||||
<result property="updateTime" column="update_time" />
|
||||
<result property="remark" column="remark" />
|
||||
<result property="sourceType" column="source_type" />
|
||||
</resultMap>
|
||||
|
||||
<sql id="selectScaleVo">
|
||||
|
|
@ -34,7 +35,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||
COALESCE(COUNT(i.item_id), 0) as item_count,
|
||||
s.estimated_time, s.target_population,
|
||||
s.author, s.source, s.reference, s.status, s.sort_order, s.create_by, s.create_time,
|
||||
s.update_by, s.update_time, s.remark
|
||||
s.update_by, s.update_time, s.remark,
|
||||
'scale' as source_type
|
||||
from psy_scale s
|
||||
left join psy_scale_item i on s.scale_id = i.scale_id
|
||||
</sql>
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||
<result property="updateTime" column="update_time" />
|
||||
<result property="remark" column="remark" />
|
||||
<result property="scaleName" column="scale_name" />
|
||||
<result property="sourceType" column="source_type" />
|
||||
<result property="deptName" column="dept_name" />
|
||||
<result property="roleName" column="role_name" />
|
||||
<result property="userName" column="user_name" />
|
||||
|
|
|
|||
|
|
@ -182,7 +182,6 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||
<if test="sentenceEndDate != null">sentence_end_date = #{sentenceEndDate}, </if>
|
||||
<if test="entryDate != null">entry_date = #{entryDate}, </if>
|
||||
<if test="infoNumber != null">info_number = #{infoNumber}, </if>
|
||||
<if test="status != null and status != ''">status = #{status}, </if>
|
||||
<if test="updateBy != null">update_by = #{updateBy}, </if>
|
||||
<if test="remark != null">remark = #{remark}, </if>
|
||||
update_time = sysdate()
|
||||
|
|
|
|||
|
|
@ -235,7 +235,7 @@ export const dynamicRoutes = [
|
|||
meta: {
|
||||
title: '心理测评管理',
|
||||
icon: 'chart',
|
||||
roles: ['admin']
|
||||
roles: ['admin', 'teacher']
|
||||
},
|
||||
children: [
|
||||
// 量表管理
|
||||
|
|
@ -246,7 +246,7 @@ export const dynamicRoutes = [
|
|||
meta: {
|
||||
title: '量表管理',
|
||||
icon: 'table',
|
||||
roles: ['admin']
|
||||
roles: ['admin', 'teacher']
|
||||
}
|
||||
},
|
||||
// 题目管理(隐藏菜单,通过量表管理页面进入)
|
||||
|
|
@ -257,7 +257,7 @@ export const dynamicRoutes = [
|
|||
hidden: true,
|
||||
meta: {
|
||||
title: '题目管理',
|
||||
roles: ['admin']
|
||||
roles: ['admin', 'teacher']
|
||||
}
|
||||
},
|
||||
// 因子管理(隐藏菜单,通过量表管理页面进入)
|
||||
|
|
@ -268,7 +268,7 @@ export const dynamicRoutes = [
|
|||
hidden: true,
|
||||
meta: {
|
||||
title: '因子管理',
|
||||
roles: ['admin']
|
||||
roles: ['admin', 'teacher']
|
||||
}
|
||||
},
|
||||
// 测评管理
|
||||
|
|
@ -279,7 +279,7 @@ export const dynamicRoutes = [
|
|||
meta: {
|
||||
title: '测评管理',
|
||||
icon: 'edit',
|
||||
roles: ['admin']
|
||||
roles: ['admin', 'teacher']
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -289,7 +289,7 @@ export const dynamicRoutes = [
|
|||
meta: {
|
||||
title: '全员测评分析',
|
||||
icon: 'chart',
|
||||
roles: ['admin']
|
||||
roles: ['admin', 'teacher']
|
||||
}
|
||||
},
|
||||
// 开始测评
|
||||
|
|
@ -300,7 +300,7 @@ export const dynamicRoutes = [
|
|||
hidden: true,
|
||||
meta: {
|
||||
title: '开始测评',
|
||||
roles: ['admin']
|
||||
roles: ['admin', 'teacher']
|
||||
}
|
||||
},
|
||||
// 测评进行中
|
||||
|
|
@ -311,7 +311,7 @@ export const dynamicRoutes = [
|
|||
hidden: true,
|
||||
meta: {
|
||||
title: '测评中',
|
||||
roles: ['admin']
|
||||
roles: ['admin', 'teacher']
|
||||
}
|
||||
},
|
||||
// 测评报告
|
||||
|
|
@ -322,7 +322,7 @@ export const dynamicRoutes = [
|
|||
hidden: true,
|
||||
meta: {
|
||||
title: '测评报告',
|
||||
roles: ['admin']
|
||||
roles: ['admin', 'teacher']
|
||||
}
|
||||
},
|
||||
// 报告管理
|
||||
|
|
@ -333,7 +333,7 @@ export const dynamicRoutes = [
|
|||
meta: {
|
||||
title: '报告管理',
|
||||
icon: 'document',
|
||||
roles: ['admin']
|
||||
roles: ['admin', 'teacher']
|
||||
}
|
||||
},
|
||||
// 报告详情
|
||||
|
|
@ -344,7 +344,7 @@ export const dynamicRoutes = [
|
|||
hidden: true,
|
||||
meta: {
|
||||
title: '报告详情',
|
||||
roles: ['admin']
|
||||
roles: ['admin', 'teacher']
|
||||
}
|
||||
},
|
||||
// 综合评估
|
||||
|
|
@ -355,7 +355,7 @@ export const dynamicRoutes = [
|
|||
meta: {
|
||||
title: '综合评估',
|
||||
icon: 'chart',
|
||||
roles: ['admin']
|
||||
roles: ['admin', 'teacher']
|
||||
}
|
||||
},
|
||||
// 量表权限管理
|
||||
|
|
@ -366,7 +366,7 @@ export const dynamicRoutes = [
|
|||
meta: {
|
||||
title: '量表权限管理',
|
||||
icon: 'lock',
|
||||
roles: ['admin']
|
||||
roles: ['admin', 'teacher']
|
||||
}
|
||||
},
|
||||
// 用户量表权限分配
|
||||
|
|
@ -377,7 +377,7 @@ export const dynamicRoutes = [
|
|||
hidden: true,
|
||||
meta: {
|
||||
title: '分配量表权限',
|
||||
roles: ['admin']
|
||||
roles: ['admin', 'teacher']
|
||||
}
|
||||
},
|
||||
// 解释配置
|
||||
|
|
@ -388,7 +388,7 @@ export const dynamicRoutes = [
|
|||
meta: {
|
||||
title: '解释配置',
|
||||
icon: 'config',
|
||||
roles: ['admin']
|
||||
roles: ['admin', 'teacher']
|
||||
}
|
||||
},
|
||||
// 用户档案
|
||||
|
|
@ -399,7 +399,7 @@ export const dynamicRoutes = [
|
|||
meta: {
|
||||
title: '用户档案',
|
||||
icon: 'user',
|
||||
roles: ['admin']
|
||||
roles: ['admin', 'teacher']
|
||||
}
|
||||
},
|
||||
// 自定义问卷
|
||||
|
|
@ -410,7 +410,7 @@ export const dynamicRoutes = [
|
|||
meta: {
|
||||
title: '自定义问卷',
|
||||
icon: 'edit',
|
||||
roles: ['admin']
|
||||
roles: ['admin', 'teacher']
|
||||
}
|
||||
},
|
||||
// 问卷开始答题
|
||||
|
|
@ -443,7 +443,7 @@ export const dynamicRoutes = [
|
|||
hidden: true,
|
||||
meta: {
|
||||
title: '问卷题目管理',
|
||||
roles: ['admin']
|
||||
roles: ['admin', 'teacher']
|
||||
}
|
||||
},
|
||||
// 主观题评分
|
||||
|
|
@ -454,7 +454,7 @@ export const dynamicRoutes = [
|
|||
meta: {
|
||||
title: '主观题评分',
|
||||
icon: 'edit',
|
||||
roles: ['admin']
|
||||
roles: ['admin', 'teacher']
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
|
|||
|
|
@ -100,6 +100,9 @@ service.interceptors.response.use(res => {
|
|||
})
|
||||
}
|
||||
return Promise.reject('无效的会话,或者会话已过期,请重新登录。')
|
||||
} else if (code === 403) {
|
||||
// 403权限不足错误,静默处理,不显示提示
|
||||
return Promise.reject('no_permission')
|
||||
} else if (code === 500) {
|
||||
Message({ message: msg, type: 'error' })
|
||||
return Promise.reject(new Error(msg))
|
||||
|
|
@ -116,6 +119,12 @@ service.interceptors.response.use(res => {
|
|||
async error => {
|
||||
console.log('err' + error)
|
||||
let { message } = error
|
||||
|
||||
// 403权限错误,静默处理
|
||||
if (error.response && error.response.status === 403) {
|
||||
return Promise.reject('no_permission')
|
||||
}
|
||||
|
||||
// 对于blob类型的错误响应,尝试解析错误信息
|
||||
if (error.response && error.response.config &&
|
||||
(error.response.config.responseType === 'blob' || error.response.config.responseType === 'arraybuffer') &&
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@
|
|||
icon="el-icon-plus"
|
||||
size="mini"
|
||||
@click="handleStartAssessment"
|
||||
v-hasPermi="['psychology:assessment:add']"
|
||||
v-hasPermi="['psychology:assessment:start']"
|
||||
>开始测评</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
|
|
@ -164,7 +164,6 @@
|
|||
|
||||
<el-divider content-position="left">答题详情</el-divider>
|
||||
<el-table :data="answerDetailList" border style="width: 100%">
|
||||
<el-table-column type="index" label="序号" width="60" align="center" />
|
||||
<el-table-column label="题目序号" prop="itemNumber" width="100" align="center" />
|
||||
<el-table-column label="题目内容" prop="itemContent" min-width="200" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="题目类型" prop="itemType" width="100" align="center">
|
||||
|
|
|
|||
|
|
@ -145,8 +145,9 @@ export default {
|
|||
const userId = this.$store.getters.id;
|
||||
const roles = this.$store.getters.roles || [];
|
||||
|
||||
// 判断是否是管理员:userId === 1 或者 roles 中包含 'admin'
|
||||
const isAdmin = userId === 1 || (roles && roles.includes('admin'));
|
||||
// 判断是否是管理员:只有admin角色才能看到所有量表
|
||||
// 其他角色(包括教师)需要通过权限表检查
|
||||
const isAdmin = roles && roles.includes('admin');
|
||||
|
||||
// 如果是管理员,显示所有量表和问卷;否则只显示有权限的量表
|
||||
if (isAdmin) {
|
||||
|
|
|
|||
|
|
@ -38,10 +38,11 @@
|
|||
size="small"
|
||||
@click="speakText(currentItem.itemContent)"
|
||||
:disabled="!isTtsSupported"
|
||||
class="tts-btn"
|
||||
:class="['tts-btn', isSpeaking ? 'speaking' : '']"
|
||||
title="朗读题干"
|
||||
>
|
||||
<img :src="voiceIcon" alt="朗读题干" class="tts-icon" />
|
||||
<i :class="isSpeaking ? 'el-icon-video-pause' : 'el-icon-service'"
|
||||
style="font-size: 18px; color: #409EFF;"></i>
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
|
|
@ -55,10 +56,11 @@
|
|||
size="mini"
|
||||
@click="speakText(option.optionContent)"
|
||||
:disabled="!isTtsSupported"
|
||||
class="option-tts-btn"
|
||||
:class="['option-tts-btn', isSpeaking ? 'speaking' : '']"
|
||||
title="朗读选项"
|
||||
>
|
||||
<img :src="voiceIcon" alt="朗读选项" class="tts-icon" />
|
||||
<i :class="isSpeaking ? 'el-icon-video-pause' : 'el-icon-service'"
|
||||
style="font-size: 16px; color: #409EFF;"></i>
|
||||
</el-button>
|
||||
</div>
|
||||
</el-radio-group>
|
||||
|
|
@ -74,10 +76,11 @@
|
|||
size="mini"
|
||||
@click="speakText(option.optionContent)"
|
||||
:disabled="!isTtsSupported"
|
||||
class="option-tts-btn"
|
||||
:class="['option-tts-btn', isSpeaking ? 'speaking' : '']"
|
||||
title="朗读选项"
|
||||
>
|
||||
<img :src="voiceIcon" alt="朗读选项" class="tts-icon" />
|
||||
<i :class="isSpeaking ? 'el-icon-video-pause' : 'el-icon-service'"
|
||||
style="font-size: 16px; color: #409EFF;"></i>
|
||||
</el-button>
|
||||
</div>
|
||||
</el-checkbox-group>
|
||||
|
|
@ -134,7 +137,8 @@ export default {
|
|||
isTtsSupported: false,
|
||||
synth: null,
|
||||
currentUtterance: null,
|
||||
voiceIcon
|
||||
voiceIcon,
|
||||
isSpeaking: false
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
|
|
@ -208,49 +212,62 @@ export default {
|
|||
/** 朗读文本 */
|
||||
speakText(text) {
|
||||
if (!this.isTtsSupported || !text || !text.trim()) {
|
||||
this.$message.warning('浏览器不支持语音播放功能');
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果正在播放,则停止
|
||||
if (this.isSpeaking) {
|
||||
this.stopSpeaking();
|
||||
return;
|
||||
}
|
||||
|
||||
this.stopSpeaking();
|
||||
this.currentUtterance = new SpeechSynthesisUtterance(text.trim());
|
||||
this.currentUtterance.lang = 'zh-CN';
|
||||
this.currentUtterance.volume = 1.0;
|
||||
this.currentUtterance.rate = 1.0;
|
||||
this.currentUtterance.volume = 1.0; // 最大音量
|
||||
this.currentUtterance.rate = 0.9; // 稍慢一点,更清晰
|
||||
this.currentUtterance.pitch = 1.0;
|
||||
|
||||
// 获取可用的语音列表
|
||||
const voices = this.synth.getVoices();
|
||||
const chineseVoice = voices.find(voice => voice.lang.includes('zh') || voice.lang.includes('CN'));
|
||||
// 优先选择中文语音
|
||||
const chineseVoice = voices.find(voice =>
|
||||
voice.lang.includes('zh') ||
|
||||
voice.lang.includes('CN') ||
|
||||
voice.name.includes('中文') ||
|
||||
voice.name.includes('Chinese')
|
||||
);
|
||||
if (chineseVoice) {
|
||||
this.currentUtterance.voice = chineseVoice;
|
||||
}
|
||||
|
||||
let hasStarted = false;
|
||||
this.currentUtterance.onstart = () => {
|
||||
hasStarted = true;
|
||||
this.isSpeaking = true;
|
||||
};
|
||||
|
||||
this.currentUtterance.onend = () => {
|
||||
this.isSpeaking = false;
|
||||
};
|
||||
|
||||
this.currentUtterance.onerror = (event) => {
|
||||
this.isSpeaking = false;
|
||||
const errorType = event.error || '';
|
||||
// 忽略 interrupted 和 canceled 错误(这些是正常的中断,不是真正的错误)
|
||||
// 忽略正常的中断
|
||||
const ignoredErrors = ['interrupted', 'canceled'];
|
||||
if (ignoredErrors.includes(errorType)) {
|
||||
console.log('TTS 被中断(正常情况):', errorType);
|
||||
return;
|
||||
}
|
||||
console.error('TTS 错误:', event);
|
||||
if (hasStarted) {
|
||||
return;
|
||||
}
|
||||
const seriousErrors = ['network-error', 'synthesis-failed', 'synthesis-unavailable', 'not-allowed'];
|
||||
if (seriousErrors.includes(errorType)) {
|
||||
this.$message.error('语音朗读失败:' + this.getErrorMessage(errorType));
|
||||
} else if (errorType === 'text-too-long') {
|
||||
this.$message.warning('文本过长,无法朗读');
|
||||
}
|
||||
// 不再显示错误提示,改为静默失败
|
||||
};
|
||||
|
||||
try {
|
||||
// 为了兼容移动端,需要在用户交互后立即调用
|
||||
this.synth.speak(this.currentUtterance);
|
||||
} catch (error) {
|
||||
this.isSpeaking = false;
|
||||
console.error('调用 speak 失败:', error);
|
||||
this.$message.error('语音朗读失败:无法启动语音合成');
|
||||
}
|
||||
},
|
||||
/** 停止朗读 */
|
||||
|
|
@ -259,6 +276,7 @@ export default {
|
|||
this.synth.cancel();
|
||||
}
|
||||
this.currentUtterance = null;
|
||||
this.isSpeaking = false;
|
||||
},
|
||||
/** 错误信息 */
|
||||
getErrorMessage(errorType) {
|
||||
|
|
@ -446,10 +464,26 @@ export default {
|
|||
/** 退出 */
|
||||
handleExit() {
|
||||
this.$modal.confirm('确定要退出测评吗?已答题目将会保存。').then(() => {
|
||||
this.loading = true;
|
||||
// 先等待一小段时间,确保最后的答案保存请求发出
|
||||
setTimeout(() => {
|
||||
// 退出前先暂停测评,保存进度
|
||||
pauseAssessment(this.assessmentId).then(() => {
|
||||
this.loading = false;
|
||||
this.$modal.msgSuccess("测评进度已保存");
|
||||
// 根据用户角色跳转到相应页面
|
||||
const roles = this.$store.getters.roles || [];
|
||||
const isStudent = roles.some(role => role === 'student' || role.includes('学员'));
|
||||
this.$router.push(isStudent ? '/student/tests' : '/psychology/assessment');
|
||||
}).catch(error => {
|
||||
console.error('保存测评进度失败:', error);
|
||||
this.loading = false;
|
||||
// 即使保存失败也允许退出,因为答案已经自动保存了
|
||||
const roles = this.$store.getters.roles || [];
|
||||
const isStudent = roles.some(role => role === 'student' || role.includes('学员'));
|
||||
this.$router.push(isStudent ? '/student/tests' : '/psychology/assessment');
|
||||
});
|
||||
}, 500); // 等待500ms,确保答案保存请求已发送
|
||||
});
|
||||
},
|
||||
/** 提交测评 */
|
||||
|
|
@ -794,5 +828,37 @@ export default {
|
|||
border-radius: 4px;
|
||||
}
|
||||
|
||||
/* 语音播放时的动画效果 */
|
||||
.tts-btn.speaking, .option-tts-btn.speaking {
|
||||
animation: pulse 1.5s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0% {
|
||||
transform: scale(1);
|
||||
}
|
||||
50% {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
100% {
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
.tts-btn .el-icon-video-pause,
|
||||
.option-tts-btn .el-icon-video-pause {
|
||||
color: #E6A23C !important;
|
||||
animation: rotate 2s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes rotate {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
|
|
|
|||
|
|
@ -95,9 +95,10 @@
|
|||
<span v-else style="color: #909399;">-</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="部门名称" align="center" prop="deptName" width="150" />
|
||||
<el-table-column label="角色名称" align="center" prop="roleName" width="150" />
|
||||
<el-table-column label="班级名称" align="center" prop="className" width="150" />
|
||||
<!-- 隐藏部门、角色、班级列 -->
|
||||
<!-- <el-table-column label="部门名称" align="center" prop="deptName" width="150" /> -->
|
||||
<!-- <el-table-column label="角色名称" align="center" prop="roleName" width="150" /> -->
|
||||
<!-- <el-table-column label="班级名称" align="center" prop="className" width="150" /> -->
|
||||
<el-table-column label="开始时间" align="center" prop="startTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ parseTime(scope.row.startTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
|
||||
|
|
@ -252,6 +253,8 @@
|
|||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleUserQuery">搜索</el-button>
|
||||
<el-button icon="el-icon-refresh" size="mini" @click="resetUserQuery">重置</el-button>
|
||||
<el-button type="success" icon="el-icon-check" size="mini" @click="handleSelectAllUsers">全选所有用户</el-button>
|
||||
<el-button type="warning" icon="el-icon-close" size="mini" @click="handleDeselectAllUsers">取消全选</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-table
|
||||
|
|
@ -745,6 +748,21 @@ export default {
|
|||
this.updatingSelection = false;
|
||||
});
|
||||
},
|
||||
/** 全选所有用户按钮点击事件 */
|
||||
handleSelectAllUsers() {
|
||||
this.selectAllUsersUnderCurrentFilter();
|
||||
},
|
||||
/** 取消全选按钮点击事件 */
|
||||
handleDeselectAllUsers() {
|
||||
this.selectedUserIds = [];
|
||||
this.cachedFilteredUsers = [];
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.userTable) {
|
||||
this.$refs.userTable.clearSelection();
|
||||
}
|
||||
});
|
||||
this.$message.success("已取消所有选择");
|
||||
},
|
||||
/** 搜索用户(远程搜索) */
|
||||
searchUsers(keyword) {
|
||||
if (!this.studentRoleId) {
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
<template>
|
||||
<div class="app-container">
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="80px">
|
||||
<el-form-item label="用户名称" prop="userName">
|
||||
<el-form-item label="罪犯姓名" prop="userName">
|
||||
<el-input
|
||||
v-model="queryParams.userName"
|
||||
placeholder="请输入用户名称"
|
||||
placeholder="请输入罪犯姓名"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
|
|
@ -488,6 +488,10 @@ export default {
|
|||
profileList: [],
|
||||
// 学员角色ID(缓存)
|
||||
studentRoleId: null,
|
||||
// 学员用户ID列表(缓存)
|
||||
studentUserIds: null,
|
||||
// 学员用户总数(缓存)
|
||||
studentUserTotal: 0,
|
||||
// 弹出层标题
|
||||
title: "",
|
||||
// 是否显示弹出层
|
||||
|
|
@ -600,10 +604,8 @@ export default {
|
|||
this.stopImportProgressPolling()
|
||||
},
|
||||
created() {
|
||||
this.getStudentRoleId().then(() => {
|
||||
this.getList()
|
||||
this.loadFilterOptions()
|
||||
})
|
||||
this.getDeptTree()
|
||||
this.getConfigKey("sys.user.initPassword").then(response => {
|
||||
this.initPassword = response.msg
|
||||
|
|
@ -643,17 +645,83 @@ export default {
|
|||
})
|
||||
})
|
||||
},
|
||||
/** 加载学员用户ID列表(缓存) */
|
||||
loadStudentUserIds() {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (this.studentUserIds !== null) {
|
||||
// 已缓存,直接返回
|
||||
resolve(this.studentUserIds)
|
||||
return
|
||||
}
|
||||
if (!this.studentRoleId) {
|
||||
resolve(new Set())
|
||||
return
|
||||
}
|
||||
|
||||
// 分批获取所有学员用户ID
|
||||
const studentUserIds = new Set()
|
||||
let pageNum = 1
|
||||
const pageSize = 500 // 每批获取500条
|
||||
let total = 0
|
||||
|
||||
const fetchPage = () => {
|
||||
return allocatedUserList({
|
||||
roleId: this.studentRoleId,
|
||||
status: '0',
|
||||
pageNum: pageNum,
|
||||
pageSize: pageSize
|
||||
}).then(response => {
|
||||
const rows = response.rows || []
|
||||
const responseTotal = response.total || 0
|
||||
|
||||
if (total === 0 && responseTotal > 0) {
|
||||
total = responseTotal
|
||||
}
|
||||
|
||||
rows.forEach(user => {
|
||||
if (user.userId) {
|
||||
studentUserIds.add(user.userId)
|
||||
}
|
||||
})
|
||||
|
||||
// 如果还有更多数据,继续获取
|
||||
if (rows.length === pageSize && (total === 0 || studentUserIds.size < total)) {
|
||||
pageNum++
|
||||
return fetchPage()
|
||||
} else {
|
||||
// 所有数据已获取
|
||||
this.studentUserIds = studentUserIds
|
||||
this.studentUserTotal = studentUserIds.size
|
||||
resolve(studentUserIds)
|
||||
}
|
||||
}).catch(error => {
|
||||
console.error("获取学员用户列表失败:", error)
|
||||
// 即使失败,也设置一个空Set,避免重复请求
|
||||
this.studentUserIds = new Set()
|
||||
this.studentUserTotal = 0
|
||||
reject(error)
|
||||
})
|
||||
}
|
||||
|
||||
fetchPage()
|
||||
})
|
||||
},
|
||||
/** 查询档案列表 */
|
||||
getList() {
|
||||
this.loading = true
|
||||
// 为了正确过滤学员用户,需要获取所有数据(不分页或获取足够多的数据)
|
||||
|
||||
// 使用后端分页,传递正确的分页参数
|
||||
const queryParams = {
|
||||
...this.queryParams,
|
||||
pageNum: 1,
|
||||
pageSize: 10000 // 获取足够多的数据,确保能过滤所有记录
|
||||
pageNum: this.queryParams.pageNum || 1,
|
||||
pageSize: this.queryParams.pageSize || 10
|
||||
}
|
||||
|
||||
// 直接请求数据
|
||||
listProfile(queryParams).then(response => {
|
||||
const rows = response.rows || []
|
||||
const total = response.total || 0
|
||||
|
||||
// 为状态设置默认值,确保是字符串类型
|
||||
rows.forEach(row => {
|
||||
if (row.status === null || row.status === undefined || row.status === '') {
|
||||
|
|
@ -662,16 +730,12 @@ export default {
|
|||
row.status = String(row.status) // 确保是字符串类型
|
||||
}
|
||||
})
|
||||
// 如果已获取学员角色ID,过滤掉非学员用户
|
||||
if (this.studentRoleId) {
|
||||
this.filterStudentUsers(rows).then(filteredRows => {
|
||||
// 前端分页处理
|
||||
const pageNum = this.queryParams.pageNum || 1
|
||||
const pageSize = this.queryParams.pageSize || 10
|
||||
const start = (pageNum - 1) * pageSize
|
||||
const end = start + pageSize
|
||||
this.profileList = filteredRows.slice(start, end)
|
||||
this.total = filteredRows.length
|
||||
|
||||
// 直接使用后端返回的数据,不在前端进行过滤
|
||||
// 后端应该已经处理了权限和角色过滤
|
||||
this.profileList = rows
|
||||
this.total = total
|
||||
|
||||
this.selectedRows = []
|
||||
this.ids = []
|
||||
this.single = true
|
||||
|
|
@ -682,85 +746,37 @@ export default {
|
|||
}
|
||||
})
|
||||
this.loading = false
|
||||
}).catch(error => {
|
||||
console.error("过滤学员用户失败:", error)
|
||||
// 如果过滤失败,仍然显示所有用户(但不推荐)
|
||||
const pageNum = this.queryParams.pageNum || 1
|
||||
const pageSize = this.queryParams.pageSize || 10
|
||||
const start = (pageNum - 1) * pageSize
|
||||
const end = start + pageSize
|
||||
this.profileList = rows.slice(start, end)
|
||||
this.total = rows.length
|
||||
this.selectedRows = []
|
||||
this.ids = []
|
||||
this.single = true
|
||||
this.multiple = true
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.profileTable) {
|
||||
this.$refs.profileTable.clearSelection()
|
||||
}
|
||||
})
|
||||
this.loading = false
|
||||
})
|
||||
} else {
|
||||
// 如果还没有获取到学员角色ID,先获取再过滤
|
||||
this.getStudentRoleId().then(() => {
|
||||
this.filterStudentUsers(rows).then(filteredRows => {
|
||||
// 前端分页处理
|
||||
const pageNum = this.queryParams.pageNum || 1
|
||||
const pageSize = this.queryParams.pageSize || 10
|
||||
const start = (pageNum - 1) * pageSize
|
||||
const end = start + pageSize
|
||||
this.profileList = filteredRows.slice(start, end)
|
||||
this.total = filteredRows.length
|
||||
this.selectedRows = []
|
||||
this.ids = []
|
||||
this.single = true
|
||||
this.multiple = true
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.profileTable) {
|
||||
this.$refs.profileTable.clearSelection()
|
||||
}
|
||||
})
|
||||
this.loading = false
|
||||
}).catch(error => {
|
||||
console.error("过滤学员用户失败:", error)
|
||||
const pageNum = this.queryParams.pageNum || 1
|
||||
const pageSize = this.queryParams.pageSize || 10
|
||||
const start = (pageNum - 1) * pageSize
|
||||
const end = start + pageSize
|
||||
this.profileList = rows.slice(start, end)
|
||||
this.total = rows.length
|
||||
this.selectedRows = []
|
||||
this.ids = []
|
||||
this.single = true
|
||||
this.multiple = true
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.profileTable) {
|
||||
this.$refs.profileTable.clearSelection()
|
||||
}
|
||||
})
|
||||
this.loading = false
|
||||
})
|
||||
})
|
||||
}
|
||||
}).catch(error => {
|
||||
console.error("查询档案列表失败:", error)
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
/** 加载监狱/监区筛选项 */
|
||||
/** 加载监狱/监区筛选项(优化:分批加载) */
|
||||
loadFilterOptions() {
|
||||
const params = {
|
||||
pageNum: 1,
|
||||
pageSize: 10000
|
||||
}
|
||||
listProfile(params).then(response => {
|
||||
const rows = response.rows || []
|
||||
const handleRows = filteredRows => {
|
||||
// 分批加载数据以获取监狱/监区选项
|
||||
const prisonSet = new Set()
|
||||
const prisonAreaSet = new Set()
|
||||
filteredRows.forEach(row => {
|
||||
let pageNum = 1
|
||||
const pageSize = 500 // 每批500条
|
||||
let total = 0
|
||||
let loadedCount = 0
|
||||
|
||||
const loadPage = () => {
|
||||
return listProfile({
|
||||
pageNum: pageNum,
|
||||
pageSize: pageSize
|
||||
}).then(response => {
|
||||
const rows = response.rows || []
|
||||
const responseTotal = response.total || 0
|
||||
|
||||
if (total === 0 && responseTotal > 0) {
|
||||
total = responseTotal
|
||||
}
|
||||
|
||||
loadedCount += rows.length
|
||||
|
||||
// 收集监狱和监区选项
|
||||
rows.forEach(row => {
|
||||
if (row && row.prison) {
|
||||
prisonSet.add(row.prison)
|
||||
}
|
||||
|
|
@ -768,51 +784,60 @@ export default {
|
|||
prisonAreaSet.add(row.prisonArea)
|
||||
}
|
||||
})
|
||||
this.filterPrisonOptions = Array.from(prisonSet)
|
||||
this.filterPrisonAreaOptions = Array.from(prisonAreaSet)
|
||||
}
|
||||
if (this.studentRoleId) {
|
||||
this.filterStudentUsers(rows).then(filteredRows => {
|
||||
handleRows(filteredRows)
|
||||
}).catch(() => {
|
||||
handleRows(rows)
|
||||
})
|
||||
|
||||
// 如果还有更多数据,继续加载
|
||||
if (rows.length === pageSize && (total === 0 || loadedCount < total)) {
|
||||
pageNum++
|
||||
return loadPage()
|
||||
} else {
|
||||
handleRows(rows)
|
||||
// 所有数据已加载,更新选项
|
||||
this.filterPrisonOptions = Array.from(prisonSet).sort()
|
||||
this.filterPrisonAreaOptions = Array.from(prisonAreaSet).sort()
|
||||
}
|
||||
}).catch(error => {
|
||||
console.error("加载监狱筛选项失败:", error)
|
||||
// 即使失败,也更新已收集的选项
|
||||
this.filterPrisonOptions = Array.from(prisonSet).sort()
|
||||
this.filterPrisonAreaOptions = Array.from(prisonAreaSet).sort()
|
||||
})
|
||||
}
|
||||
|
||||
// 如果已有学员用户ID列表,可以优化:只加载学员用户的数据
|
||||
// 但为了简单,还是加载所有数据
|
||||
loadPage()
|
||||
},
|
||||
/** 过滤学员用户 */
|
||||
/** 过滤学员用户(使用缓存的学员用户ID列表) */
|
||||
filterStudentUsers(rows) {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!this.studentRoleId || rows.length === 0) {
|
||||
if (rows.length === 0) {
|
||||
resolve(rows)
|
||||
return
|
||||
}
|
||||
// 获取所有学员用户ID列表
|
||||
allocatedUserList({
|
||||
roleId: this.studentRoleId,
|
||||
status: '0',
|
||||
pageNum: 1,
|
||||
pageSize: 10000
|
||||
}).then(response => {
|
||||
const studentUserIds = new Set()
|
||||
const studentUsers = response.rows || []
|
||||
studentUsers.forEach(user => {
|
||||
if (user.userId) {
|
||||
studentUserIds.add(user.userId)
|
||||
}
|
||||
})
|
||||
// 过滤出只包含学员用户的记录
|
||||
|
||||
// 如果已有缓存的学员用户ID列表,直接使用
|
||||
if (this.studentUserIds && this.studentUserIds.size > 0) {
|
||||
const filteredRows = rows.filter(row => {
|
||||
return row.userId && studentUserIds.has(row.userId)
|
||||
return row.userId && this.studentUserIds.has(row.userId)
|
||||
})
|
||||
resolve(filteredRows)
|
||||
return
|
||||
}
|
||||
|
||||
// 如果没有缓存,先加载
|
||||
if (!this.studentRoleId) {
|
||||
resolve(rows)
|
||||
return
|
||||
}
|
||||
|
||||
this.loadStudentUserIds().then(() => {
|
||||
const filteredRows = rows.filter(row => {
|
||||
return row.userId && this.studentUserIds.has(row.userId)
|
||||
})
|
||||
resolve(filteredRows)
|
||||
}).catch(error => {
|
||||
console.error("获取学员用户列表失败:", error)
|
||||
reject(error)
|
||||
console.error("过滤学员用户失败:", error)
|
||||
// 如果加载失败,返回所有数据
|
||||
resolve(rows)
|
||||
})
|
||||
})
|
||||
},
|
||||
|
|
|
|||
|
|
@ -334,6 +334,13 @@ export default {
|
|||
},
|
||||
created() {
|
||||
this.getList()
|
||||
// 检查路由参数,如果存在 action: 'edit' 和 questionnaireId,自动打开编辑对话框
|
||||
const route = this.$route
|
||||
if (route.query.action === 'edit' && route.query.questionnaireId) {
|
||||
this.$nextTick(() => {
|
||||
this.handleUpdateFromRoute(route.query.questionnaireId)
|
||||
})
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
/** 查询问卷列表 */
|
||||
|
|
@ -395,10 +402,31 @@ export default {
|
|||
handleUpdate(row) {
|
||||
this.reset()
|
||||
const questionnaireId = row.questionnaireId || this.ids[0]
|
||||
this.handleUpdateFromRoute(questionnaireId)
|
||||
},
|
||||
/** 从路由参数或传入的ID打开编辑对话框 */
|
||||
handleUpdateFromRoute(questionnaireId) {
|
||||
if (!questionnaireId) {
|
||||
return
|
||||
}
|
||||
this.reset()
|
||||
getQuestionnaire(questionnaireId).then(response => {
|
||||
this.form = response.data
|
||||
this.open = true
|
||||
this.title = "修改问卷"
|
||||
// 清除路由参数,避免刷新时重复打开
|
||||
if (this.$route.query.action === 'edit') {
|
||||
const newQuery = { ...this.$route.query }
|
||||
delete newQuery.action
|
||||
delete newQuery.questionnaireId
|
||||
this.$router.replace({
|
||||
path: this.$route.path,
|
||||
query: Object.keys(newQuery).length > 0 ? newQuery : {}
|
||||
})
|
||||
}
|
||||
}).catch(error => {
|
||||
console.error("获取问卷信息失败:", error)
|
||||
this.$modal.msgError("获取问卷信息失败")
|
||||
})
|
||||
},
|
||||
/** 提交按钮 */
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@
|
|||
|
||||
<el-card class="scale-list" shadow="never" v-loading="loading">
|
||||
<div slot="header">
|
||||
<span>量表列表(请勾选需要分析的量表)</span>
|
||||
<span>量表/问卷列表(请勾选需要分析的量表/问卷)</span>
|
||||
<div class="header-actions">
|
||||
<el-button
|
||||
type="primary"
|
||||
|
|
@ -154,6 +154,7 @@ export default {
|
|||
generating: false,
|
||||
reportDialogVisible: false,
|
||||
comprehensiveReport: '',
|
||||
// ========== 本地大模型配置 ==========
|
||||
OLLAMA_URL: 'http://192.168.0.106:11434/api/generate',
|
||||
MODEL: 'deepseek-r1:32b'
|
||||
}
|
||||
|
|
@ -176,6 +177,7 @@ export default {
|
|||
.then((res) => this.normalizeStudentOptions(res.data || []))
|
||||
.catch(() => [])
|
||||
let profilePromise = Promise.resolve([])
|
||||
// 如果是纯数字,通过信息编号搜索
|
||||
if (/^\d+$/.test(trimmed)) {
|
||||
profilePromise = listProfile({
|
||||
infoNumber: trimmed,
|
||||
|
|
@ -184,10 +186,20 @@ export default {
|
|||
})
|
||||
.then((res) => this.normalizeProfileOptions(res.rows || []))
|
||||
.catch(() => [])
|
||||
} else {
|
||||
// 如果不是纯数字(输入姓名),通过姓名搜索档案以获取编号信息
|
||||
profilePromise = listProfile({
|
||||
userName: trimmed,
|
||||
pageNum: 1,
|
||||
pageSize: 20
|
||||
})
|
||||
.then((res) => this.normalizeProfileOptions(res.rows || []))
|
||||
.catch(() => [])
|
||||
}
|
||||
return Promise.all([studentPromise, profilePromise])
|
||||
.then(([studentList, profileList]) => {
|
||||
const merged = this.mergeUserOptions([...studentList, ...profileList])
|
||||
// 合并时,优先使用档案数据(因为档案数据包含完整的infoNumber)
|
||||
const merged = this.mergeUserOptions([...profileList, ...studentList])
|
||||
this.cachedUserOptions = merged
|
||||
return merged
|
||||
})
|
||||
|
|
@ -217,16 +229,46 @@ export default {
|
|||
this.$message.warning('请输入姓名或信息编号')
|
||||
return
|
||||
}
|
||||
this.fetchUserOptions(keyword).then((list) => {
|
||||
|
||||
// 从输入框中提取纯姓名或编号(去除括号、编号等格式)
|
||||
let searchKeyword = keyword
|
||||
// 如果包含编号格式,提取编号
|
||||
const numberMatch = keyword.match(/编号[::]\s*(\d+)/)
|
||||
if (numberMatch) {
|
||||
searchKeyword = numberMatch[1]
|
||||
} else {
|
||||
// 如果包含姓名格式,提取姓名(去除括号和后面的内容)
|
||||
const nameMatch = keyword.match(/^([^((]+)/)
|
||||
if (nameMatch) {
|
||||
searchKeyword = nameMatch[1].trim()
|
||||
}
|
||||
}
|
||||
|
||||
this.fetchUserOptions(searchKeyword).then((list) => {
|
||||
if (!list.length) {
|
||||
this.$message.warning('未找到匹配的用户')
|
||||
// 不显示错误消息,让用户继续搜索或选择
|
||||
// 如果用户已经选择了用户,保持选择状态
|
||||
return
|
||||
}
|
||||
if (list.length === 1) {
|
||||
// 找到唯一结果,自动选择
|
||||
this.handleUserSelect(list[0])
|
||||
} else {
|
||||
// 如果找到多条,尝试精确匹配输入框的完整内容
|
||||
const exactMatch = list.find(opt => {
|
||||
const label = this.buildUserLabel(opt)
|
||||
return label === keyword
|
||||
})
|
||||
if (exactMatch) {
|
||||
// 精确匹配成功,自动选择
|
||||
this.handleUserSelect(exactMatch)
|
||||
} else {
|
||||
// 多条记录,提示用户从下拉列表选择
|
||||
this.$message.info('找到多条记录,请从下拉列表选择具体用户')
|
||||
}
|
||||
}
|
||||
}).catch(() => {
|
||||
// 搜索失败时,不显示错误消息,让用户继续操作
|
||||
})
|
||||
},
|
||||
normalizeStudentOptions(list) {
|
||||
|
|
@ -263,6 +305,16 @@ export default {
|
|||
}
|
||||
if (!map.has(item.userId)) {
|
||||
map.set(item.userId, item)
|
||||
} else {
|
||||
// 如果已存在该用户,优先保留有infoNumber的数据
|
||||
const existing = map.get(item.userId)
|
||||
if (!existing.infoNumber && item.infoNumber) {
|
||||
// 如果已存在的没有infoNumber,而新项有,则更新
|
||||
map.set(item.userId, item)
|
||||
} else if (existing.infoNumber && !item.infoNumber) {
|
||||
// 如果已存在的有infoNumber,而新项没有,则保留已存在的
|
||||
// 不做任何操作
|
||||
}
|
||||
}
|
||||
})
|
||||
return Array.from(map.values())
|
||||
|
|
@ -512,6 +564,7 @@ ${typeLabel}${index + 1}:${report.scaleName}
|
|||
|
||||
return `${SYSTEM_PROMPT}\n\n${userInfoText}\n\n${scaleReportsText}`
|
||||
},
|
||||
// 本地 OLLAMA API 调用方法
|
||||
async callOLLAMA(prompt) {
|
||||
try {
|
||||
const { data } = await axios.post(this.OLLAMA_URL, {
|
||||
|
|
@ -536,13 +589,13 @@ ${typeLabel}${index + 1}:${report.scaleName}
|
|||
.trim()
|
||||
|
||||
if (!response) {
|
||||
throw new Error('AI分析返回结果为空')
|
||||
throw new Error('本地AI分析返回结果为空')
|
||||
}
|
||||
|
||||
return response
|
||||
} catch (error) {
|
||||
console.error('AI分析失败:', error)
|
||||
throw new Error('AI分析失败:' + (error.message || '未知错误'))
|
||||
console.error('本地AI分析失败:', error)
|
||||
throw new Error('本地AI分析失败:' + (error.message || '未知错误'))
|
||||
}
|
||||
},
|
||||
formatReport(aiReport, userInfo, scaleReports) {
|
||||
|
|
@ -589,7 +642,7 @@ ${typeLabel}${index + 1}:${report.scaleName}
|
|||
|
||||
<div style="margin-top: 30px; padding-top: 20px; border-top: 1px solid #ddd; text-align: right; color: #909399; font-size: 12px;">
|
||||
<div>报告生成时间:${parseTime(new Date(), '{y}-{m}-{d} {h}:{i}:{s}')}</div>
|
||||
<div style="margin-top: 8px;">被评估人____________________</div>
|
||||
<div style="margin-top: 8px;">被评估人:<span style="color: #303133; font-weight: bold;">${userInfo.userName || '未知'}</span></div>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
|
|
|
|||
|
|
@ -2,20 +2,38 @@
|
|||
<div class="app-container">
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="80px">
|
||||
<el-form-item label="量表名称" prop="scaleName">
|
||||
<el-input
|
||||
<el-select
|
||||
v-model="queryParams.scaleName"
|
||||
placeholder="请输入量表名称"
|
||||
placeholder="请选择或输入量表名称"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
filterable
|
||||
allow-create
|
||||
default-first-option
|
||||
>
|
||||
<el-option
|
||||
v-for="name in uniqueScaleNames"
|
||||
:key="name"
|
||||
:label="name"
|
||||
:value="name"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="量表编码" prop="scaleCode">
|
||||
<el-input
|
||||
<el-select
|
||||
v-model="queryParams.scaleCode"
|
||||
placeholder="请输入量表编码"
|
||||
placeholder="请选择或输入量表编码"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
filterable
|
||||
allow-create
|
||||
default-first-option
|
||||
>
|
||||
<el-option
|
||||
v-for="code in uniqueScaleCodes"
|
||||
:key="code"
|
||||
:label="code"
|
||||
:value="code"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="量表类型" prop="scaleType">
|
||||
<el-select v-model="queryParams.scaleType" placeholder="量表类型" clearable>
|
||||
|
|
@ -427,6 +445,95 @@
|
|||
<p style="margin-top: 20px; color: #606266;">正在生成二维码...</p>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 添加或修改问卷对话框(复用问卷管理的对话框) -->
|
||||
<el-dialog :title="questionnaireTitle" :visible.sync="questionnaireOpen" width="900px" append-to-body>
|
||||
<el-form ref="questionnaireForm" :model="questionnaireForm" :rules="questionnaireRules" label-width="120px">
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="问卷编码" prop="questionnaireCode">
|
||||
<el-input v-model="questionnaireForm.questionnaireCode" placeholder="请输入问卷编码" :disabled="questionnaireForm.questionnaireId != undefined" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="问卷名称" prop="questionnaireName">
|
||||
<el-input v-model="questionnaireForm.questionnaireName" placeholder="请输入问卷名称" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="问卷类型" prop="questionnaireType">
|
||||
<el-select v-model="questionnaireForm.questionnaireType" placeholder="请选择问卷类型">
|
||||
<el-option label="自定义" value="custom" />
|
||||
<el-option label="考试" value="exam" />
|
||||
<el-option label="练习" value="practice" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="组卷方式" prop="paperType">
|
||||
<el-select v-model="questionnaireForm.paperType" placeholder="请选择组卷方式">
|
||||
<el-option label="手动" value="manual" />
|
||||
<el-option label="随机" value="random" />
|
||||
<el-option label="混合" value="mixed" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="题目数量" prop="itemCount">
|
||||
<el-input-number v-model="questionnaireForm.itemCount" :min="0" controls-position="right" disabled />
|
||||
<span style="color: #909399; font-size: 12px; margin-left: 10px;">(根据题目自动计算)</span>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="总分" prop="totalScore">
|
||||
<el-input-number v-model="questionnaireForm.totalScore" :min="0" :precision="2" controls-position="right" disabled />
|
||||
<span style="color: #909399; font-size: 12px; margin-left: 10px;">(根据题目分数自动计算)</span>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="及格分数" prop="passScore">
|
||||
<el-input-number v-model="questionnaireForm.passScore" :min="0" :precision="2" controls-position="right" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="预计时间(分)" prop="estimatedTime">
|
||||
<el-input-number v-model="questionnaireForm.estimatedTime" :min="1" controls-position="right" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-form-item label="问卷描述" prop="description">
|
||||
<el-input v-model="questionnaireForm.description" type="textarea" :rows="4" placeholder="请输入问卷描述" />
|
||||
</el-form-item>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="排序" prop="sortOrder">
|
||||
<el-input-number v-model="questionnaireForm.sortOrder" :min="0" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-radio-group v-model="questionnaireForm.status">
|
||||
<el-radio label="0">正常</el-radio>
|
||||
<el-radio label="1">停用</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-form-item label="备注">
|
||||
<el-input v-model="questionnaireForm.remark" type="textarea" :rows="2" placeholder="请输入备注" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button type="primary" @click="submitQuestionnaireForm">确 定</el-button>
|
||||
<el-button @click="cancelQuestionnaire">取 消</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
@ -454,6 +561,8 @@ export default {
|
|||
total: 0,
|
||||
// 量表表格数据
|
||||
scaleList: [],
|
||||
// 所有量表数据(不分页,用于下拉框)
|
||||
allScaleList: [],
|
||||
// 弹出层标题
|
||||
title: "",
|
||||
// 是否显示弹出层
|
||||
|
|
@ -474,6 +583,21 @@ export default {
|
|||
// 二维码对话框
|
||||
qrcodeOpen: false,
|
||||
qrcodeInfo: null,
|
||||
// 问卷修改对话框
|
||||
questionnaireOpen: false,
|
||||
questionnaireTitle: "修改问卷",
|
||||
questionnaireForm: {},
|
||||
questionnaireRules: {
|
||||
questionnaireCode: [
|
||||
{ required: true, message: "问卷编码不能为空", trigger: "blur" }
|
||||
],
|
||||
questionnaireName: [
|
||||
{ required: true, message: "问卷名称不能为空", trigger: "blur" }
|
||||
],
|
||||
questionnaireType: [
|
||||
{ required: true, message: "问卷类型不能为空", trigger: "change" }
|
||||
]
|
||||
},
|
||||
// 查询参数
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
|
|
@ -481,7 +605,8 @@ export default {
|
|||
scaleName: undefined,
|
||||
scaleCode: undefined,
|
||||
scaleType: undefined,
|
||||
status: undefined
|
||||
status: undefined,
|
||||
includeQuestionnaire: true // 包含问卷数据
|
||||
},
|
||||
// 表单参数
|
||||
form: {},
|
||||
|
|
@ -524,10 +649,39 @@ export default {
|
|||
{ label: "正常", value: "0" },
|
||||
{ label: "停用", value: "1" }
|
||||
];
|
||||
},
|
||||
/** 去重后的量表名称列表 */
|
||||
uniqueScaleNames() {
|
||||
// 使用所有量表列表而不是分页列表
|
||||
if (!this.allScaleList || this.allScaleList.length === 0) {
|
||||
return [];
|
||||
}
|
||||
const nameSet = new Set();
|
||||
this.allScaleList.forEach(scale => {
|
||||
if (scale.scaleName && scale.scaleName.trim()) {
|
||||
nameSet.add(scale.scaleName.trim());
|
||||
}
|
||||
});
|
||||
return Array.from(nameSet).sort();
|
||||
},
|
||||
/** 去重后的量表编码列表 */
|
||||
uniqueScaleCodes() {
|
||||
// 使用所有量表列表而不是分页列表
|
||||
if (!this.allScaleList || this.allScaleList.length === 0) {
|
||||
return [];
|
||||
}
|
||||
const codeSet = new Set();
|
||||
this.allScaleList.forEach(scale => {
|
||||
if (scale.scaleCode && scale.scaleCode.trim()) {
|
||||
codeSet.add(scale.scaleCode.trim());
|
||||
}
|
||||
});
|
||||
return Array.from(codeSet).sort();
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getList()
|
||||
this.getAllScaleList() // 先获取所有量表用于下拉框
|
||||
this.getList() // 再获取分页数据
|
||||
},
|
||||
methods: {
|
||||
/** 获取字典标签 */
|
||||
|
|
@ -536,6 +690,21 @@ export default {
|
|||
const dict = dictList.find(item => item.value === value);
|
||||
return dict ? dict.label : value;
|
||||
},
|
||||
/** 获取所有量表列表(不分页,用于下拉框) */
|
||||
getAllScaleList() {
|
||||
// 创建一个不分页的查询参数
|
||||
const allParams = {
|
||||
pageNum: 1,
|
||||
pageSize: 10000, // 设置一个足够大的数值获取所有数据
|
||||
includeQuestionnaire: true // 包含问卷数据
|
||||
}
|
||||
listScale(allParams).then(response => {
|
||||
this.allScaleList = response.rows || []
|
||||
}).catch(error => {
|
||||
console.error('获取所有量表列表失败:', error)
|
||||
this.allScaleList = []
|
||||
})
|
||||
},
|
||||
/** 查询量表列表 */
|
||||
getList() {
|
||||
this.loading = true
|
||||
|
|
@ -621,12 +790,14 @@ export default {
|
|||
updateScale(this.form).then(response => {
|
||||
this.$modal.msgSuccess("修改成功")
|
||||
this.open = false
|
||||
this.getAllScaleList() // 刷新所有量表列表
|
||||
this.getList()
|
||||
})
|
||||
} else {
|
||||
addScale(this.form).then(response => {
|
||||
this.$modal.msgSuccess("新增成功")
|
||||
this.open = false
|
||||
this.getAllScaleList() // 刷新所有量表列表
|
||||
this.getList()
|
||||
})
|
||||
}
|
||||
|
|
@ -639,6 +810,7 @@ export default {
|
|||
this.$modal.confirm('是否确认删除量表编号为"' + scaleIds + '"的数据项?').then(function() {
|
||||
return delScale(scaleIds)
|
||||
}).then(() => {
|
||||
this.getAllScaleList() // 刷新所有量表列表
|
||||
this.getList()
|
||||
this.$modal.msgSuccess("删除成功")
|
||||
}).catch(() => {})
|
||||
|
|
@ -790,6 +962,7 @@ export default {
|
|||
this.$modal.msgSuccess(response.msg || "导入成功")
|
||||
this.importOpen = false
|
||||
this.importJsonText = ""
|
||||
this.getAllScaleList() // 刷新所有量表列表
|
||||
this.getList()
|
||||
}).catch(error => {
|
||||
this.$modal.msgError(error.msg || "导入失败")
|
||||
|
|
@ -826,6 +999,7 @@ export default {
|
|||
this.upload.fileContent = null
|
||||
this.$refs.upload.clearFiles()
|
||||
this.upload.isUploading = false
|
||||
this.getAllScaleList() // 刷新所有量表列表
|
||||
this.getList()
|
||||
}).catch(error => {
|
||||
this.$modal.msgError(error.msg || "导入失败")
|
||||
|
|
@ -858,6 +1032,7 @@ export default {
|
|||
this.upload.fileContent = null
|
||||
this.$refs.upload.clearFiles()
|
||||
this.upload.isUploading = false
|
||||
this.getAllScaleList() // 刷新所有量表列表
|
||||
this.getList()
|
||||
}).catch(error => {
|
||||
this.$modal.msgError(error.msg || "导入失败")
|
||||
|
|
@ -927,8 +1102,62 @@ export default {
|
|||
/** 问卷修改按钮操作 */
|
||||
handleQuestionnaireUpdate(row) {
|
||||
const questionnaireId = row.originalId || Math.abs(row.scaleId)
|
||||
// 跳转到问卷管理页面进行编辑
|
||||
this.$router.push({ path: '/psychology/questionnaire', query: { questionnaireId: questionnaireId, action: 'edit' } })
|
||||
this.resetQuestionnaireForm()
|
||||
getQuestionnaire(questionnaireId).then(response => {
|
||||
this.questionnaireForm = response.data
|
||||
this.questionnaireOpen = true
|
||||
this.questionnaireTitle = "修改问卷"
|
||||
}).catch(error => {
|
||||
console.error("获取问卷信息失败:", error)
|
||||
this.$modal.msgError("获取问卷信息失败")
|
||||
})
|
||||
},
|
||||
/** 提交问卷表单 */
|
||||
submitQuestionnaireForm() {
|
||||
this.$refs["questionnaireForm"].validate(valid => {
|
||||
if (valid) {
|
||||
if (this.questionnaireForm.questionnaireId != undefined) {
|
||||
updateQuestionnaire(this.questionnaireForm).then(response => {
|
||||
this.$modal.msgSuccess("修改成功")
|
||||
this.questionnaireOpen = false
|
||||
this.resetQuestionnaireForm()
|
||||
// 刷新量表列表
|
||||
this.getAllScaleList() // 刷新所有量表列表
|
||||
this.getList()
|
||||
}).catch(error => {
|
||||
console.error("修改问卷失败:", error)
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
/** 取消问卷编辑 */
|
||||
cancelQuestionnaire() {
|
||||
this.questionnaireOpen = false
|
||||
this.resetQuestionnaireForm()
|
||||
},
|
||||
/** 重置问卷表单 */
|
||||
resetQuestionnaireForm() {
|
||||
this.questionnaireForm = {
|
||||
questionnaireId: undefined,
|
||||
questionnaireCode: undefined,
|
||||
questionnaireName: undefined,
|
||||
questionnaireType: "custom",
|
||||
paperType: "manual",
|
||||
itemCount: 0,
|
||||
totalScore: undefined,
|
||||
passScore: undefined,
|
||||
estimatedTime: undefined,
|
||||
description: undefined,
|
||||
status: "0",
|
||||
sortOrder: 0,
|
||||
remark: undefined
|
||||
}
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.questionnaireForm) {
|
||||
this.$refs.questionnaireForm.clearValidate()
|
||||
}
|
||||
})
|
||||
},
|
||||
/** 问卷删除按钮操作 */
|
||||
handleQuestionnaireDelete(row) {
|
||||
|
|
@ -937,6 +1166,7 @@ export default {
|
|||
this.$modal.confirm('是否确认删除问卷"' + questionnaireName + '"的数据项?').then(() => {
|
||||
return delQuestionnaire(questionnaireId)
|
||||
}).then(() => {
|
||||
this.getAllScaleList() // 刷新所有量表列表
|
||||
this.getList()
|
||||
this.$modal.msgSuccess("删除成功")
|
||||
}).catch(() => {})
|
||||
|
|
|
|||
|
|
@ -191,7 +191,23 @@ export default {
|
|||
|
||||
const pausedRecord = this.getPausedRecord(test.scaleId)
|
||||
if (pausedRecord) {
|
||||
// 确认是否继续暂停的测评
|
||||
this.$confirm('检测到您有该量表的暂停测评,是否继续?', '提示', {
|
||||
confirmButtonText: '继续测评',
|
||||
cancelButtonText: '重新开始',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
this.continueAssessment(pausedRecord)
|
||||
}).catch(() => {
|
||||
// 用户选择重新开始,先提示确认
|
||||
this.$confirm('重新开始将清空之前的答题记录,确定要重新开始吗?', '警告', {
|
||||
confirmButtonText: '确定重新开始',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
this.createNewAssessment(test)
|
||||
})
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -209,6 +225,11 @@ export default {
|
|||
this.createAssessment(test)
|
||||
},
|
||||
|
||||
// 创建新测评(用于重新开始)
|
||||
createNewAssessment(test) {
|
||||
this.createAssessment(test)
|
||||
},
|
||||
|
||||
// 创建测评
|
||||
createAssessment(test) {
|
||||
this.loading = true
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user