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