This commit is contained in:
胡圣锋 2025-11-21 18:27:17 +08:00
commit e3cda00ff2
19 changed files with 772 additions and 357 deletions

View File

@ -83,6 +83,9 @@ public class PsyAssessmentReportController extends BaseController
@Autowired
private ISasReportService sasReportService;
@Autowired
private com.ddnai.system.service.psychology.IPsyUserProfileService profileService;
/**
* 获取报告列表包含测评报告和问卷报告
*/
@ -125,6 +128,17 @@ public class PsyAssessmentReportController extends BaseController
vo.setIsGenerated(ar.getIsGenerated());
vo.setGenerateTime(ar.getGenerateTime());
vo.setCreateTime(ar.getCreateTime());
// 获取用户信息
PsyAssessment assessment = assessmentService.selectAssessmentById(ar.getAssessmentId());
if (assessment != null) {
vo.setUserId(assessment.getUserId());
// 获取用户档案信息编号
com.ddnai.system.domain.psychology.PsyUserProfile profile = profileService.selectProfileByUserId(assessment.getUserId());
if (profile != null) {
vo.setInfoNumber(profile.getInfoNumber());
}
}
allReports.add(vo);
}
@ -141,6 +155,17 @@ public class PsyAssessmentReportController extends BaseController
vo.setIsGenerated(qr.getIsGenerated());
vo.setGenerateTime(qr.getGenerateTime());
vo.setCreateTime(qr.getCreateTime());
// 获取用户信息
com.ddnai.system.domain.psychology.PsyQuestionnaireAnswer answer = questionnaireAnswerService.selectAnswerById(qr.getAnswerId());
if (answer != null) {
vo.setUserId(answer.getUserId());
// 获取用户档案信息编号
com.ddnai.system.domain.psychology.PsyUserProfile profile = profileService.selectProfileByUserId(answer.getUserId());
if (profile != null) {
vo.setInfoNumber(profile.getInfoNumber());
}
}
allReports.add(vo);
System.out.println("添加问卷报告: reportId=" + qr.getReportId() + ", answerId=" + qr.getAnswerId() + ", title=" + qr.getReportTitle());
}
@ -184,6 +209,8 @@ public class PsyAssessmentReportController extends BaseController
private Long reportId;
private String sourceType; // "assessment" "questionnaire"
private Long sourceId; // assessmentId answerId
private Long userId; // 用户ID
private String infoNumber; // 信息编号
private String reportTitle;
private String reportType;
private String reportContent;
@ -199,6 +226,10 @@ public class PsyAssessmentReportController extends BaseController
public void setSourceType(String sourceType) { this.sourceType = sourceType; }
public Long getSourceId() { return sourceId; }
public void setSourceId(Long sourceId) { this.sourceId = sourceId; }
public Long getUserId() { return userId; }
public void setUserId(Long userId) { this.userId = userId; }
public String getInfoNumber() { return infoNumber; }
public void setInfoNumber(String infoNumber) { this.infoNumber = infoNumber; }
public String getReportTitle() { return reportTitle; }
public void setReportTitle(String reportTitle) { this.reportTitle = reportTitle; }
public String getReportType() { return reportType; }

View File

@ -61,7 +61,6 @@ public class PsyQuestionnaireAnswerController extends BaseController
/**
* 获取问卷答题列表
*/
@PreAuthorize("@ss.hasPermi('psychology:questionnaire:list')")
@GetMapping("/list")
public TableDataInfo list(PsyQuestionnaireAnswer answer)
{
@ -88,9 +87,12 @@ public class PsyQuestionnaireAnswerController extends BaseController
Long questionnaireId = Long.valueOf(params.get("questionnaireId").toString());
String respondentName = params.get("respondentName") != null ? params.get("respondentName").toString() : null;
// 获取被测试用户ID如果前端传递了userId管理员代测则使用传递的userId否则使用当前登录用户ID
Long userId = params.get("userId") != null ? Long.valueOf(params.get("userId").toString()) : SecurityUtils.getUserId();
PsyQuestionnaireAnswer answer = new PsyQuestionnaireAnswer();
answer.setQuestionnaireId(questionnaireId);
answer.setUserId(SecurityUtils.getUserId());
answer.setUserId(userId); // 使用被测试用户ID
answer.setRespondentName(respondentName);
answer.setStatus("0"); // 进行中
answer.setStartTime(new Date());
@ -164,28 +166,39 @@ public class PsyQuestionnaireAnswerController extends BaseController
{
try
{
System.out.println("开始提交问卷answerId: " + answerId + ", 用户: " + SecurityUtils.getUsername());
PsyQuestionnaireAnswer answer = answerService.selectAnswerById(answerId);
if (answer == null)
{
System.err.println("答题记录不存在answerId: " + answerId);
return error("答题记录不存在");
}
if (!"0".equals(answer.getStatus()))
{
System.err.println("问卷状态异常answerId: " + answerId + ", status: " + answer.getStatus());
return error("问卷已完成或已作废");
}
// 自动评分并提交
long startTime = System.currentTimeMillis();
int result = answerService.submitAnswer(answerId);
long endTime = System.currentTimeMillis();
System.out.println("问卷提交完成answerId: " + answerId + ", 耗时: " + (endTime - startTime) + "ms, 结果: " + result);
if (result > 0)
{
return success("提交成功");
}
System.err.println("提交失败answerId: " + answerId + ", result: " + result);
return error("提交失败");
}
catch (Exception e)
{
System.err.println("提交问卷异常answerId: " + answerId + ", 错误: " + e.getMessage());
e.printStackTrace();
return error("提交失败:" + e.getMessage());
}
}
@ -290,9 +303,8 @@ public class PsyQuestionnaireAnswerController extends BaseController
{
Long detailId = Long.valueOf(params.get("detailId").toString());
java.math.BigDecimal score = new java.math.BigDecimal(params.get("score").toString());
String comment = params.get("comment") != null ? params.get("comment").toString() : null;
int result = answerService.submitScoring(detailId, score, comment);
int result = answerService.submitScoring(detailId, score, null);
if (result > 0)
{
return success("评分成功");
@ -317,9 +329,8 @@ public class PsyQuestionnaireAnswerController extends BaseController
{
Long detailId = Long.valueOf(params.get("detailId").toString());
java.math.BigDecimal score = new java.math.BigDecimal(params.get("score").toString());
String comment = params.get("comment") != null ? params.get("comment").toString() : null;
int result = answerService.submitScoring(detailId, score, comment);
int result = answerService.submitScoring(detailId, score, null);
if (result > 0)
{
successCount++;

View File

@ -34,9 +34,8 @@ public class PsyQuestionnaireController extends BaseController
private IPsyQuestionnaireService questionnaireService;
/**
* 获取问卷列表
* 获取问卷列表答题用户可访问
*/
@PreAuthorize("@ss.hasPermi('psychology:questionnaire:list')")
@GetMapping("/list")
public TableDataInfo list(PsyQuestionnaire questionnaire)
{
@ -46,9 +45,8 @@ public class PsyQuestionnaireController extends BaseController
}
/**
* 根据问卷ID获取详细信息
* 根据问卷ID获取详细信息答题用户可访问
*/
@PreAuthorize("@ss.hasPermi('psychology:questionnaire:query')")
@GetMapping(value = "/{questionnaireId}")
public AjaxResult getInfo(@PathVariable Long questionnaireId)
{

View File

@ -17,17 +17,17 @@ spring:
username:
password:
# 初始连接数
initialSize: 5
initialSize: 10
# 最小连接池数量
minIdle: 10
minIdle: 20
# 最大连接池数量
maxActive: 20
# 配置获取连接等待超时的时间
maxWait: 60000
maxActive: 100
# 配置获取连接等待超时的时间减少到20秒快速失败
maxWait: 20000
# 配置连接超时时间
connectTimeout: 30000
connectTimeout: 10000
# 配置网络超时时间
socketTimeout: 60000
socketTimeout: 30000
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
timeBetweenEvictionRunsMillis: 60000
# 配置一个连接在池中最小生存的时间,单位是毫秒

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

View File

@ -48,28 +48,48 @@ public class PsyUserProfile extends BaseEntity
@Excel(name = "生日", width = 30, dateFormat = "yyyy-MM-dd")
private java.util.Date birthday;
/** 学历 */
@Excel(name = "学历")
private String education;
/** 监狱 */
@Excel(name = "监狱")
private String prison;
/** 职业 */
@Excel(name = "职业")
private String occupation;
/** 监区 */
@Excel(name = "监区")
private String prisonArea;
/** 地址 */
@Excel(name = "地址")
private String address;
/** 性别 */
@Excel(name = "性别", readConverterExp = "0=男,1=女,2=未知")
private String gender;
/** 紧急联系人 */
@Excel(name = "紧急联系人")
private String emergencyContact;
/** 民族 */
@Excel(name = "民族")
private String nation;
/** 紧急联系电话 */
@Excel(name = "紧急联系电话")
private String emergencyPhone;
/** 文化程度 */
@Excel(name = "文化程度")
private String educationLevel;
/** 病史 */
private String medicalHistory;
/** 罪名 */
@Excel(name = "罪名")
private String crimeName;
/** 刑期 */
@Excel(name = "刑期")
private String sentenceTerm;
/** 刑期起日 */
@JsonFormat(pattern = "yyyy-MM-dd")
@Excel(name = "刑期起日", width = 30, dateFormat = "yyyy-MM-dd")
private java.util.Date sentenceStartDate;
/** 刑期止日 */
@JsonFormat(pattern = "yyyy-MM-dd")
@Excel(name = "刑期止日", width = 30, dateFormat = "yyyy-MM-dd")
private java.util.Date sentenceEndDate;
/** 入监时间 */
@JsonFormat(pattern = "yyyy-MM-dd")
@Excel(name = "入监时间", width = 30, dateFormat = "yyyy-MM-dd")
private java.util.Date entryDate;
/** 用户状态0正常 1停用 */
private String status;
@ -154,64 +174,104 @@ public class PsyUserProfile extends BaseEntity
this.birthday = birthday;
}
public String getEducation()
public String getPrison()
{
return education;
return prison;
}
public void setEducation(String education)
public void setPrison(String prison)
{
this.education = education;
this.prison = prison;
}
public String getOccupation()
public String getPrisonArea()
{
return occupation;
return prisonArea;
}
public void setOccupation(String occupation)
public void setPrisonArea(String prisonArea)
{
this.occupation = occupation;
this.prisonArea = prisonArea;
}
public String getAddress()
public String getGender()
{
return address;
return gender;
}
public void setAddress(String address)
public void setGender(String gender)
{
this.address = address;
this.gender = gender;
}
public String getEmergencyContact()
public String getNation()
{
return emergencyContact;
return nation;
}
public void setEmergencyContact(String emergencyContact)
public void setNation(String nation)
{
this.emergencyContact = emergencyContact;
this.nation = nation;
}
public String getEmergencyPhone()
public String getEducationLevel()
{
return emergencyPhone;
return educationLevel;
}
public void setEmergencyPhone(String emergencyPhone)
public void setEducationLevel(String educationLevel)
{
this.emergencyPhone = emergencyPhone;
this.educationLevel = educationLevel;
}
public String getMedicalHistory()
public String getCrimeName()
{
return medicalHistory;
return crimeName;
}
public void setMedicalHistory(String medicalHistory)
public void setCrimeName(String crimeName)
{
this.medicalHistory = medicalHistory;
this.crimeName = crimeName;
}
public String getSentenceTerm()
{
return sentenceTerm;
}
public void setSentenceTerm(String sentenceTerm)
{
this.sentenceTerm = sentenceTerm;
}
public java.util.Date getSentenceStartDate()
{
return sentenceStartDate;
}
public void setSentenceStartDate(java.util.Date sentenceStartDate)
{
this.sentenceStartDate = sentenceStartDate;
}
public java.util.Date getSentenceEndDate()
{
return sentenceEndDate;
}
public void setSentenceEndDate(java.util.Date sentenceEndDate)
{
this.sentenceEndDate = sentenceEndDate;
}
public java.util.Date getEntryDate()
{
return entryDate;
}
public void setEntryDate(java.util.Date entryDate)
{
this.entryDate = entryDate;
}
public String getUserName()
@ -274,74 +334,6 @@ public class PsyUserProfile extends BaseEntity
this.infoNumber = infoNumber;
}
// 以下方法用于兼容现有代码这些字段可能存储在 profileData JSON
// 如果需要可以从 profileData 中解析这些字段
public String getPrisonerName()
{
// 可以从 profileData JSON 中解析暂时返回 null
return null;
}
public String getPrisonName()
{
// 可以从 profileData JSON 中解析暂时返回 null
return null;
}
public String getPrisonAreaName()
{
// 可以从 profileData JSON 中解析暂时返回 null
return null;
}
public String getCustodyStatus()
{
// 可以从 profileData JSON 中解析暂时返回 null
return null;
}
public String getNation()
{
// 可以从 profileData JSON 中解析暂时返回 null
return null;
}
public String getEducationLevel()
{
// 可以从 profileData JSON 中解析暂时返回 null
return null;
}
public String getCrimeName()
{
// 可以从 profileData JSON 中解析暂时返回 null
return null;
}
public String getSentenceTerm()
{
// 可以从 profileData JSON 中解析暂时返回 null
return null;
}
public String getSentenceStartDate()
{
// 可以从 profileData JSON 中解析暂时返回 null
return null;
}
public String getSentenceEndDate()
{
// 可以从 profileData JSON 中解析暂时返回 null
return null;
}
public String getEntryDate()
{
// 可以从 profileData JSON 中解析暂时返回 null
return null;
}
@Override
public String toString() {
@ -354,11 +346,16 @@ public class PsyUserProfile extends BaseEntity
.append("userName", getUserName())
.append("phone", getPhone())
.append("birthday", getBirthday())
.append("education", getEducation())
.append("occupation", getOccupation())
.append("address", getAddress())
.append("emergencyContact", getEmergencyContact())
.append("emergencyPhone", getEmergencyPhone())
.append("prison", getPrison())
.append("prisonArea", getPrisonArea())
.append("gender", getGender())
.append("nation", getNation())
.append("educationLevel", getEducationLevel())
.append("crimeName", getCrimeName())
.append("sentenceTerm", getSentenceTerm())
.append("sentenceStartDate", getSentenceStartDate())
.append("sentenceEndDate", getSentenceEndDate())
.append("entryDate", getEntryDate())
.append("infoNumber", getInfoNumber())
.append("createBy", getCreateBy())
.append("createTime", getCreateTime())

View File

@ -156,22 +156,30 @@ public class PsyAssessmentServiceImpl implements IPsyAssessmentService
PsyUserProfile profile = userProfileService.selectProfileByUserId(userId);
if (profile != null)
{
if (StringUtils.isNotEmpty(profile.getPrisonerName()))
if (StringUtils.isNotEmpty(profile.getUserName()))
{
summaryVO.setUserName(profile.getPrisonerName());
summaryVO.setNickName(profile.getPrisonerName());
summaryVO.setUserName(profile.getUserName());
summaryVO.setNickName(profile.getUserName());
}
summaryVO.setInfoNumber(profile.getInfoNumber());
summaryVO.setPrisonName(profile.getPrisonName());
summaryVO.setPrisonAreaName(profile.getPrisonAreaName());
summaryVO.setCustodyStatus(profile.getCustodyStatus());
summaryVO.setPrisonName(profile.getPrison());
summaryVO.setPrisonAreaName(profile.getPrisonArea());
// 监管状态字段已删除可以根据需要设置默认值或删除此行
// summaryVO.setCustodyStatus(profile.getCustodyStatus());
summaryVO.setNation(profile.getNation());
summaryVO.setEducationLevel(profile.getEducationLevel());
summaryVO.setCrimeName(profile.getCrimeName());
summaryVO.setSentenceTerm(profile.getSentenceTerm());
summaryVO.setSentenceStartDate(profile.getSentenceStartDate());
summaryVO.setSentenceEndDate(profile.getSentenceEndDate());
summaryVO.setEntryDate(profile.getEntryDate());
// 日期字段需要转换为字符串格式
if (profile.getSentenceStartDate() != null) {
summaryVO.setSentenceStartDate(new java.text.SimpleDateFormat("yyyy-MM-dd").format(profile.getSentenceStartDate()));
}
if (profile.getSentenceEndDate() != null) {
summaryVO.setSentenceEndDate(new java.text.SimpleDateFormat("yyyy-MM-dd").format(profile.getSentenceEndDate()));
}
if (profile.getEntryDate() != null) {
summaryVO.setEntryDate(new java.text.SimpleDateFormat("yyyy-MM-dd").format(profile.getEntryDate()));
}
}
List<PsyAssessment> assessments = assessmentMapper.selectAssessmentListByUserId(userId);

View File

@ -3,6 +3,7 @@ package com.ddnai.system.service.impl.psychology;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@ -217,25 +218,37 @@ public class PsyQuestionnaireAnswerServiceImpl implements IPsyQuestionnaireAnswe
// 判断是否及格需要从问卷表获取及格分数
// 这里暂时不判断后续可以完善
// 更新排名需要重新计算所有答题记录的排名
updateRank(answer.getQuestionnaireId());
int result = answerMapper.updateAnswer(answer);
// 生成问卷报告
if (result > 0) {
// 异步更新排名避免阻塞提交流程
final Long questionnaireIdForRank = answer.getQuestionnaireId();
CompletableFuture.runAsync(() -> {
try {
generateQuestionnaireReport(answerId);
System.out.println("问卷报告生成完成answerId: " + answerId);
// 延迟50ms确保事务提交完成
Thread.sleep(50);
updateRank(questionnaireIdForRank);
System.out.println("异步排名更新完成questionnaireId: " + questionnaireIdForRank);
} catch (Exception e) {
// 报告生成失败不影响提交但记录详细错误
System.err.println("生成问卷报告失败answerId: " + answerId);
System.err.println("异步更新排名失败questionnaireId: " + questionnaireIdForRank);
e.printStackTrace();
}
});
// 异步生成问卷报告避免阻塞提交流程
if (result > 0) {
// 使用异步任务生成报告不阻塞当前事务
CompletableFuture.runAsync(() -> {
try {
// 延迟100ms确保事务提交完成
Thread.sleep(100);
generateQuestionnaireReport(answerId);
System.out.println("异步问卷报告生成完成answerId: " + answerId);
} catch (Exception e) {
System.err.println("异步生成问卷报告失败answerId: " + answerId);
System.err.println("错误信息: " + e.getMessage());
e.printStackTrace();
// 不抛出异常让提交流程继续
}
} else {
System.err.println("更新答题记录失败无法生成报告answerId: " + answerId);
});
}
return result;
@ -379,10 +392,35 @@ public class PsyQuestionnaireAnswerServiceImpl implements IPsyQuestionnaireAnswe
reportContent.append("</tbody></table>");
reportContent.append("</div>");
// 检查是否有未评分的主观题
boolean hasUnscoredSubjective = false;
int unscoredCount = 0;
for (PsyQuestionnaireAnswerDetail detail : details) {
PsyQuestionnaireItem item = itemMapper.selectItemById(detail.getItemId());
if (item != null) {
boolean isSubjective = isSubjectiveType(item.getItemType());
String isScored = detail.getIsScored();
// 如果是主观题且未评分
if (isSubjective && !"1".equals(isScored)) {
hasUnscoredSubjective = true;
unscoredCount++;
}
}
}
System.out.println("评分检查结果 - 总题目数: " + details.size() +
", 未评分主观题数: " + unscoredCount +
", 是否有未评分主观题: " + hasUnscoredSubjective);
// 生成报告摘要
String summary = String.format("本次答题共完成%d道题目总得分%.2f分",
String summary = String.format("本次答题共完成%d道题目总得分%.2f分%s",
details.size(),
answer.getTotalScore() != null ? answer.getTotalScore().doubleValue() : 0.0);
answer.getTotalScore() != null ? answer.getTotalScore().doubleValue() : 0.0,
hasUnscoredSubjective ? "(有" + unscoredCount + "道主观题待评分)" : "");
// 根据是否有未评分的主观题决定报告状态
String isGenerated = hasUnscoredSubjective ? "0" : "1";
// 保存报告到数据库
PsyQuestionnaireReport existingReport = reportMapper.selectReportByAnswerId(answerId);
@ -394,13 +432,13 @@ public class PsyQuestionnaireAnswerServiceImpl implements IPsyQuestionnaireAnswe
if (existingReport != null) {
// 更新现有报告
System.out.println("更新已存在的报告reportId: " + existingReport.getReportId());
System.out.println("更新已存在的报告reportId: " + existingReport.getReportId() + ", 报告状态: " + isGenerated);
report = existingReport;
report.setReportType("standard");
report.setReportTitle(reportTitle);
report.setReportContent(reportContent.toString());
report.setSummary(summary);
report.setIsGenerated("1");
report.setIsGenerated(isGenerated); // 根据评分状态设置
report.setGenerateTime(DateUtils.getNowDate());
report.setUpdateBy(SecurityUtils.getUsername());
report.setUpdateTime(DateUtils.getNowDate());
@ -414,12 +452,12 @@ public class PsyQuestionnaireAnswerServiceImpl implements IPsyQuestionnaireAnswe
report.setReportTitle(reportTitle);
report.setReportContent(reportContent.toString());
report.setSummary(summary);
report.setIsGenerated("1");
report.setIsGenerated(isGenerated); // 根据评分状态设置
report.setGenerateTime(DateUtils.getNowDate());
report.setCreateBy(SecurityUtils.getUsername());
report.setCreateTime(DateUtils.getNowDate());
int insertResult = reportMapper.insertReport(report);
System.out.println("创建新报告结果: " + insertResult + ", reportId: " + report.getReportId());
System.out.println("创建新报告结果: " + insertResult + ", reportId: " + report.getReportId() + ", 报告状态: " + isGenerated);
if (insertResult <= 0) {
System.err.println("警告报告插入失败insertResult: " + insertResult);
}
@ -1008,8 +1046,18 @@ public class PsyQuestionnaireAnswerServiceImpl implements IPsyQuestionnaireAnswe
answer.setUpdateBy(SecurityUtils.getUsername());
answerMapper.updateAnswer(answer);
// 重新计算排名
updateRank(answer.getQuestionnaireId());
// 异步重新计算排名
final Long questionnaireIdForRank = answer.getQuestionnaireId();
CompletableFuture.runAsync(() -> {
try {
Thread.sleep(50);
updateRank(questionnaireIdForRank);
System.out.println("评分后异步排名更新完成questionnaireId: " + questionnaireIdForRank);
} catch (Exception e) {
System.err.println("评分后异步更新排名失败questionnaireId: " + questionnaireIdForRank);
e.printStackTrace();
}
});
// 重新生成报告以反映最新的评分
// 注意由于在同一个事务中MyBatis一级缓存可能返回旧数据

View File

@ -92,7 +92,8 @@ public class SasReportServiceImpl implements ISasReportService
info.setUnitName(firstNonBlank(summary.getPrisonName(), summary.getDeptName(), resolveDefaultUnitName()));
info.setPrisonAreaName(summary.getPrisonAreaName());
info.setDeptName(summary.getDeptName());
info.setCustodyStatus(summary.getCustodyStatus());
// 监管状态字段已删除可以根据需要设置默认值或删除此行
// info.setCustodyStatus(summary.getCustodyStatus());
}
else
{
@ -181,10 +182,11 @@ public class SasReportServiceImpl implements ISasReportService
}
if (data.getUserInfo() != null)
{
if (StringUtils.isNotBlank(data.getUserInfo().getCustodyStatus()))
{
keywords.add(data.getUserInfo().getCustodyStatus());
}
// 监管状态字段已删除
// if (StringUtils.isNotBlank(data.getUserInfo().getCustodyStatus()))
// {
// keywords.add(data.getUserInfo().getCustodyStatus());
// }
if (StringUtils.isNotBlank(data.getUserInfo().getPrisonAreaName()))
{
keywords.add(data.getUserInfo().getPrisonAreaName());

View File

@ -25,31 +25,35 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</resultMap>
<sql id="selectQuestionnaireVo">
select questionnaire_id, questionnaire_code, questionnaire_name, questionnaire_type, paper_type,
item_count, total_score, pass_score, estimated_time, description, status, sort_order,
create_by, create_time, update_by, update_time, remark
from psy_questionnaire
select q.questionnaire_id, q.questionnaire_code, q.questionnaire_name, q.questionnaire_type, q.paper_type,
COALESCE(COUNT(i.item_id), 0) as item_count, q.total_score, q.pass_score, q.estimated_time, q.description, q.status, q.sort_order,
q.create_by, q.create_time, q.update_by, q.update_time, q.remark
from psy_questionnaire q
left join psy_questionnaire_item i on q.questionnaire_id = i.questionnaire_id
</sql>
<select id="selectQuestionnaireById" parameterType="Long" resultMap="PsyQuestionnaireResult">
<include refid="selectQuestionnaireVo"/>
where questionnaire_id = #{questionnaireId}
where q.questionnaire_id = #{questionnaireId}
group by q.questionnaire_id
</select>
<select id="selectQuestionnaireByCode" parameterType="String" resultMap="PsyQuestionnaireResult">
<include refid="selectQuestionnaireVo"/>
where questionnaire_code = #{questionnaireCode}
where q.questionnaire_code = #{questionnaireCode}
group by q.questionnaire_id
</select>
<select id="selectQuestionnaireList" parameterType="com.ddnai.system.domain.psychology.PsyQuestionnaire" resultMap="PsyQuestionnaireResult">
<include refid="selectQuestionnaireVo"/>
<where>
<if test="questionnaireCode != null and questionnaireCode != ''"> and questionnaire_code = #{questionnaireCode}</if>
<if test="questionnaireName != null and questionnaireName != ''"> and questionnaire_name like concat('%', #{questionnaireName}, '%')</if>
<if test="questionnaireType != null and questionnaireType != ''"> and questionnaire_type = #{questionnaireType}</if>
<if test="status != null and status != ''"> and status = #{status}</if>
<if test="questionnaireCode != null and questionnaireCode != ''"> and q.questionnaire_code = #{questionnaireCode}</if>
<if test="questionnaireName != null and questionnaireName != ''"> and q.questionnaire_name like concat('%', #{questionnaireName}, '%')</if>
<if test="questionnaireType != null and questionnaireType != ''"> and q.questionnaire_type = #{questionnaireType}</if>
<if test="status != null and status != ''"> and q.status = #{status}</if>
</where>
order by sort_order, create_time desc
group by q.questionnaire_id
order by q.sort_order, q.create_time desc
</select>
<insert id="insertQuestionnaire" parameterType="com.ddnai.system.domain.psychology.PsyQuestionnaire" useGeneratedKeys="true" keyProperty="questionnaireId">

View File

@ -14,12 +14,16 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<result property="userName" column="user_name" />
<result property="phone" column="phone" />
<result property="birthday" column="birthday" />
<result property="education" column="education" />
<result property="occupation" column="occupation" />
<result property="address" column="address" />
<result property="emergencyContact" column="emergency_contact" />
<result property="emergencyPhone" column="emergency_phone" />
<result property="medicalHistory" column="medical_history" />
<result property="prison" column="prison" />
<result property="prisonArea" column="prison_area" />
<result property="gender" column="gender" />
<result property="nation" column="nation" />
<result property="educationLevel" column="education_level" />
<result property="crimeName" column="crime_name" />
<result property="sentenceTerm" column="sentence_term" />
<result property="sentenceStartDate" column="sentence_start_date" />
<result property="sentenceEndDate" column="sentence_end_date" />
<result property="entryDate" column="entry_date" />
<result property="status" column="status" />
<result property="deptId" column="dept_id" />
<result property="deptName" column="dept_name" />
@ -33,8 +37,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<sql id="selectProfileVo">
select p.profile_id, p.user_id, p.profile_type, p.profile_data, p.avatar, p.id_card, p.birthday,
p.education, p.occupation, p.address, p.emergency_contact, p.emergency_phone,
p.medical_history, p.info_number, p.create_by, p.create_time, p.update_by, p.update_time, p.remark,
p.prison, p.prison_area, p.gender, p.nation, p.education_level, p.crime_name,
p.sentence_term, p.sentence_start_date, p.sentence_end_date, p.entry_date,
p.info_number, p.create_by, p.create_time, p.update_by, p.update_time, p.remark,
u.user_name, u.phonenumber as phone, u.nick_name, u.email, u.sex, u.status, u.dept_id, u.create_time as user_create_time
</sql>
@ -64,8 +69,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
select u.user_id, u.dept_id, u.nick_name, u.user_name, u.email, u.avatar as user_avatar, u.phonenumber as phone, u.sex, u.status, u.del_flag, u.login_ip, u.login_date, u.create_by as user_create_by, u.create_time as user_create_time, u.remark as user_remark,
d.dept_name, d.leader,
p.profile_id, p.profile_type, p.profile_data, p.avatar, p.id_card, p.birthday,
p.education, p.occupation, p.address, p.emergency_contact, p.emergency_phone,
p.medical_history, p.info_number, p.create_by, p.create_time, p.update_by, p.update_time, p.remark
p.prison, p.prison_area, p.gender, p.nation, p.education_level, p.crime_name,
p.sentence_term, p.sentence_start_date, p.sentence_end_date, p.entry_date,
p.info_number, p.create_by, p.create_time, p.update_by, p.update_time, p.remark
from sys_user u
left join sys_dept d on u.dept_id = d.dept_id
left join psy_user_profile p on u.user_id = p.user_id
@ -113,12 +119,16 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="avatar != null">avatar, </if>
<if test="idCard != null">id_card, </if>
<if test="birthday != null">birthday, </if>
<if test="education != null">education, </if>
<if test="occupation != null">occupation, </if>
<if test="address != null">address, </if>
<if test="emergencyContact != null">emergency_contact, </if>
<if test="emergencyPhone != null">emergency_phone, </if>
<if test="medicalHistory != null">medical_history, </if>
<if test="prison != null">prison, </if>
<if test="prisonArea != null">prison_area, </if>
<if test="gender != null">gender, </if>
<if test="nation != null">nation, </if>
<if test="educationLevel != null">education_level, </if>
<if test="crimeName != null">crime_name, </if>
<if test="sentenceTerm != null">sentence_term, </if>
<if test="sentenceStartDate != null">sentence_start_date, </if>
<if test="sentenceEndDate != null">sentence_end_date, </if>
<if test="entryDate != null">entry_date, </if>
<if test="infoNumber != null">info_number, </if>
<if test="createBy != null">create_by, </if>
<if test="remark != null">remark, </if>
@ -130,12 +140,16 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="avatar != null">#{avatar}, </if>
<if test="idCard != null">#{idCard}, </if>
<if test="birthday != null">#{birthday}, </if>
<if test="education != null">#{education}, </if>
<if test="occupation != null">#{occupation}, </if>
<if test="address != null">#{address}, </if>
<if test="emergencyContact != null">#{emergencyContact}, </if>
<if test="emergencyPhone != null">#{emergencyPhone}, </if>
<if test="medicalHistory != null">#{medicalHistory}, </if>
<if test="prison != null">#{prison}, </if>
<if test="prisonArea != null">#{prisonArea}, </if>
<if test="gender != null">#{gender}, </if>
<if test="nation != null">#{nation}, </if>
<if test="educationLevel != null">#{educationLevel}, </if>
<if test="crimeName != null">#{crimeName}, </if>
<if test="sentenceTerm != null">#{sentenceTerm}, </if>
<if test="sentenceStartDate != null">#{sentenceStartDate}, </if>
<if test="sentenceEndDate != null">#{sentenceEndDate}, </if>
<if test="entryDate != null">#{entryDate}, </if>
<if test="infoNumber != null">#{infoNumber}, </if>
<if test="createBy != null">#{createBy}, </if>
<if test="remark != null">#{remark}, </if>
@ -151,12 +165,16 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="avatar != null">avatar = #{avatar}, </if>
<if test="idCard != null">id_card = #{idCard}, </if>
<if test="birthday != null">birthday = #{birthday}, </if>
<if test="education != null">education = #{education}, </if>
<if test="occupation != null">occupation = #{occupation}, </if>
<if test="address != null">address = #{address}, </if>
<if test="emergencyContact != null">emergency_contact = #{emergencyContact}, </if>
<if test="emergencyPhone != null">emergency_phone = #{emergencyPhone}, </if>
<if test="medicalHistory != null">medical_history = #{medicalHistory}, </if>
<if test="prison != null">prison = #{prison}, </if>
<if test="prisonArea != null">prison_area = #{prisonArea}, </if>
<if test="gender != null">gender = #{gender}, </if>
<if test="nation != null">nation = #{nation}, </if>
<if test="educationLevel != null">education_level = #{educationLevel}, </if>
<if test="crimeName != null">crime_name = #{crimeName}, </if>
<if test="sentenceTerm != null">sentence_term = #{sentenceTerm}, </if>
<if test="sentenceStartDate != null">sentence_start_date = #{sentenceStartDate}, </if>
<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="updateBy != null">update_by = #{updateBy}, </if>
<if test="remark != null">remark = #{remark}, </if>

View File

@ -45,39 +45,97 @@ function buildKeywordAnalysis(reportData, severityMeta) {
}
export function buildChartOption(reportData) {
try {
const chartData = reportData.chartData || {}
const score = safeScore(reportData.calculatedResults?.rawScore)
const xAxis = chartData.xAxis || [reportData.testRecord?.projectDate || '当前']
const seriesData = chartData.series || [{ name: '总分', value: reportData.calculatedResults?.rawScore || 0 }]
const rawScoreValue = reportData.calculatedResults?.rawScore || 0
// 构建X轴数据
let xAxis = []
if (chartData.xAxis && Array.isArray(chartData.xAxis) && chartData.xAxis.length > 0) {
xAxis = chartData.xAxis
} else if (reportData.testRecord?.projectDate) {
xAxis = [formatDateLabel(reportData.testRecord.projectDate)]
} else {
xAxis = ['当前']
}
// 构建系列数据
let seriesDataValues = []
if (chartData.series && Array.isArray(chartData.series) && chartData.series.length > 0) {
seriesDataValues = chartData.series.map(item => {
const val = item.value !== undefined ? item.value : rawScoreValue
return typeof val === 'number' ? val : parseFloat(val) || 0
})
} else {
seriesDataValues = [typeof rawScoreValue === 'number' ? rawScoreValue : parseFloat(rawScoreValue) || 0]
}
return {
title: {
text: chartData.title || 'SAS总分趋势',
left: 'center'
text: chartData.title || '测评分数趋势',
left: 'center',
top: 10,
textStyle: {
fontSize: 16,
fontWeight: 'normal'
}
},
tooltip: {
trigger: 'axis',
formatter: '{b}: {c}分'
},
grid: {
left: '10%',
right: '10%',
bottom: '15%',
top: '20%',
containLabel: true
},
tooltip: { trigger: 'axis' },
xAxis: {
type: 'category',
data: xAxis
data: xAxis,
axisLine: {
lineStyle: { color: '#999' }
}
},
yAxis: {
type: 'value',
name: '分数',
axisLine: {
lineStyle: { color: '#999' }
}
},
yAxis: { type: 'value' },
series: [
{
name: '总分',
type: 'line',
smooth: true,
areaStyle: { opacity: 0.15 },
data: seriesData.map(item => item.value)
}
],
graphic: {
type: 'text',
left: 'center',
top: '10%',
style: {
text: `当前得分:${score}`,
fontSize: 14
symbol: 'circle',
symbolSize: 8,
itemStyle: {
color: '#409EFF'
},
lineStyle: {
width: 2,
color: '#409EFF'
},
areaStyle: {
opacity: 0.2,
color: '#409EFF'
},
data: seriesDataValues,
label: {
show: true,
position: 'top',
formatter: '{c}分'
}
}
]
}
} catch (error) {
console.error('构建图表配置失败:', error)
throw new Error('图表配置构建失败: ' + error.message)
}
}

View File

@ -3,10 +3,8 @@ import { buildChartOption, formatDateLabel } from './DynamicContentService'
class SASReportGenerator {
async buildHtml(reportData, includeChart = true) {
// 不再使用图表功能
let chartImage = ''
if (includeChart) {
chartImage = await this.renderChartImage(buildChartOption(reportData))
}
const recommendations = (reportData.calculatedResults.recommendations || []).map(item => `<li>${item}</li>`).join('')
const keywords = (reportData.keywordAnalysis || reportData.keywords || []).map(item => `<span class="tag">${item}</span>`).join('')
const printScopes = (reportData.printScopes || []).map(scope => `
@ -43,7 +41,7 @@ class SASReportGenerator {
<div class="section-title">基本信息</div>
<table>
<tr><th>编码</th><td>${reportData.userInfo.infoNumber || reportData.testRecord.assessmentId || '-'}</td>
<th>样本</th><td>${reportData.userInfo.name || '-'}</td></tr>
<th>姓名</th><td>${reportData.userInfo.name || '-'}</td></tr>
<tr><th>测试单位</th><td>${reportData.userInfo.unitName || '-'}</td>
<th>性别</th><td>${reportData.userInfo.gender || '-'}</td></tr>
<tr><th>所属科室/监区</th><td>${reportData.userInfo.prisonAreaName || reportData.userInfo.deptName || '-'}</td>
@ -58,7 +56,6 @@ class SASReportGenerator {
<tr><th>原始分数</th><td class="highlight">${reportData.calculatedResults.rawScore}</td></tr>
<tr><th>功能评级</th><td>${reportData.calculatedResults.functionalRating}</td></tr>
</table>
${chartImage ? `<div class="chart"><img src="${chartImage}" alt="chart" style="max-width: 600px" /></div>` : ''}
</div>
<div class="section">
@ -106,18 +103,58 @@ class SASReportGenerator {
}
async renderChartImage(option) {
const container = document.createElement('div')
let container = null
let chart = null
try {
console.log('开始渲染图表,配置:', option)
// 创建容器
container = document.createElement('div')
container.style.width = '640px'
container.style.height = '320px'
container.style.position = 'fixed'
container.style.left = '-9999px'
container.style.top = '0'
document.body.appendChild(container)
const chart = echarts.init(container)
// 初始化echarts
chart = echarts.init(container)
if (!chart) {
throw new Error('echarts初始化失败')
}
// 设置图表选项
chart.setOption(option)
const dataUrl = chart.getDataURL({ pixelRatio: 2, backgroundColor: '#fff' })
chart.dispose()
document.body.removeChild(container)
// 等待渲染完成
await new Promise(resolve => setTimeout(resolve, 100))
// 获取图表图像
const dataUrl = chart.getDataURL({
type: 'png',
pixelRatio: 2,
backgroundColor: '#fff'
})
console.log('图表渲染成功,图像长度:', dataUrl?.length || 0)
return dataUrl
} catch (error) {
console.error('图表渲染失败:', error)
throw error
} finally {
// 清理资源
try {
if (chart) {
chart.dispose()
}
if (container && container.parentNode) {
document.body.removeChild(container)
}
} catch (cleanupError) {
console.warn('清理图表资源时出错:', cleanupError)
}
}
}
resolveReportTitle(reportData) {

View File

@ -16,8 +16,8 @@ axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8'
const service = axios.create({
// axios中请求配置有baseURL选项表示请求URL公共部分
baseURL: process.env.VUE_APP_BASE_API,
// 超时
timeout: 10000
// 超时增加到30秒以支持连续测评操作
timeout: 30000
})
// request拦截器

View File

@ -95,6 +95,7 @@ export default {
scaleList: [],
profileList: [],
pausedList: [],
targetUserId: null, // IDURL
form: {
scaleId: undefined,
profileId: undefined
@ -110,11 +111,23 @@ export default {
};
},
created() {
// URLscaleId
// URLscaleIdprofileId/userId
const scaleId = this.$route.query.scaleId;
const profileId = this.$route.query.profileId;
const userId = this.$route.query.userId;
if (scaleId) {
this.form.scaleId = parseInt(scaleId);
}
if (profileId) {
this.form.profileId = parseInt(profileId);
}
// userId
if (userId) {
this.targetUserId = parseInt(userId);
}
this.loadScales();
this.loadProfiles();
this.loadPaused();
@ -140,9 +153,17 @@ export default {
if (selectedScale && selectedScale.sourceType === 'questionnaire') {
//
const questionnaireId = selectedScale.originalId || Math.abs(selectedScale.scaleId);
const queryParams = { questionnaireId: questionnaireId };
// URLuserId
const urlUserId = this.$route.query.userId;
if (urlUserId) {
queryParams.userId = urlUserId;
}
this.$router.replace({
path: '/psychology/questionnaire/start',
query: { questionnaireId: questionnaireId }
query: queryParams
});
}
}
@ -164,9 +185,17 @@ export default {
if (selectedScale && selectedScale.sourceType === 'questionnaire') {
//
const questionnaireId = selectedScale.originalId || Math.abs(selectedScale.scaleId);
const queryParams = { questionnaireId: questionnaireId };
// URLuserId
const urlUserId = this.$route.query.userId;
if (urlUserId) {
queryParams.userId = urlUserId;
}
this.$router.replace({
path: '/psychology/questionnaire/start',
query: { questionnaireId: questionnaireId }
query: queryParams
});
}
}
@ -197,9 +226,17 @@ export default {
if (selectedScale && selectedScale.sourceType === 'questionnaire') {
//
const questionnaireId = selectedScale.originalId || Math.abs(selectedScale.scaleId);
const queryParams = { questionnaireId: questionnaireId };
// URLuserId
const urlUserId = this.$route.query.userId;
if (urlUserId) {
queryParams.userId = urlUserId;
}
this.$router.replace({
path: '/psychology/questionnaire/start',
query: { questionnaireId: questionnaireId }
query: queryParams
});
}
}
@ -269,13 +306,29 @@ export default {
return;
}
//
//
if (selectedScale.sourceType === 'questionnaire') {
const questionnaireId = selectedScale.originalId || Math.abs(selectedScale.scaleId);
//
//
const queryParams = { questionnaireId: questionnaireId };
// 使targetUserIdURL使
if (this.targetUserId) {
queryParams.userId = this.targetUserId;
console.log('使用URL参数的userId:', this.targetUserId);
} else if (this.form.profileId) {
const selectedProfile = this.profileList.find(p => p.profileId === this.form.profileId);
if (selectedProfile && selectedProfile.userId) {
queryParams.userId = selectedProfile.userId;
console.log('管理员代测传递userId:', selectedProfile.userId);
}
}
//
this.$router.push({
path: '/psychology/questionnaire/start',
query: { questionnaireId: questionnaireId }
query: queryParams
});
return;
}

View File

@ -122,10 +122,25 @@
<span>{{ parseTime(scope.row.birthday, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<el-table-column label="学历" align="center" prop="education" width="100" />
<el-table-column label="职业" align="center" prop="occupation" width="120" />
<el-table-column label="紧急联系人" align="center" prop="emergencyContact" width="120" />
<el-table-column label="紧急电话" align="center" prop="emergencyPhone" width="130" />
<el-table-column label="监狱" align="center" prop="prison" width="120" />
<el-table-column label="监区" align="center" prop="prisonArea" width="100" />
<el-table-column label="性别" align="center" prop="gender" width="80">
<template slot-scope="scope">
<span v-if="scope.row.gender === '0'"></span>
<span v-else-if="scope.row.gender === '1'"></span>
<span v-else-if="scope.row.gender === '2'">未知</span>
<span v-else>{{ scope.row.gender }}</span>
</template>
</el-table-column>
<el-table-column label="民族" align="center" prop="nation" width="80" />
<el-table-column label="文化程度" align="center" prop="educationLevel" width="100" />
<el-table-column label="罪名" align="center" prop="crimeName" width="120" />
<el-table-column label="刑期" align="center" prop="sentenceTerm" width="100" />
<el-table-column label="入监时间" align="center" prop="entryDate" width="120">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.entryDate, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
@ -226,34 +241,99 @@
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="学历" prop="education">
<el-input v-model="form.education" placeholder="请输入学历" />
<el-form-item label="监狱" prop="prison">
<el-input v-model="form.prison" placeholder="请输入监狱名称" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="职业" prop="occupation">
<el-input v-model="form.occupation" placeholder="请输入职业" />
<el-form-item label="监区" prop="prisonArea">
<el-input v-model="form.prisonArea" placeholder="请输入监区" />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="性别" prop="gender">
<el-select v-model="form.gender" placeholder="请选择性别">
<el-option label="男" value="0" />
<el-option label="女" value="1" />
<el-option label="未知" value="2" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="民族" prop="nation">
<el-input v-model="form.nation" placeholder="请输入民族" />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="文化程度" prop="educationLevel">
<el-select v-model="form.educationLevel" placeholder="请选择文化程度">
<el-option label="小学" value="小学" />
<el-option label="初中" value="初中" />
<el-option label="高中" value="高中" />
<el-option label="中专" value="中专" />
<el-option label="大专" value="大专" />
<el-option label="本科" value="本科" />
<el-option label="硕士" value="硕士" />
<el-option label="博士" value="博士" />
<el-option label="其他" value="其他" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="罪名" prop="crimeName">
<el-input v-model="form.crimeName" placeholder="请输入罪名" />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="刑期" prop="sentenceTerm">
<el-input v-model="form.sentenceTerm" placeholder="请输入刑期3年6个月" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="刑期起日" prop="sentenceStartDate">
<el-date-picker
v-model="form.sentenceStartDate"
type="date"
placeholder="选择刑期起日"
value-format="yyyy-MM-dd"
style="width: 100%"
/>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="刑期止日" prop="sentenceEndDate">
<el-date-picker
v-model="form.sentenceEndDate"
type="date"
placeholder="选择刑期止日"
value-format="yyyy-MM-dd"
style="width: 100%"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="入监时间" prop="entryDate">
<el-date-picker
v-model="form.entryDate"
type="date"
placeholder="选择入监时间"
value-format="yyyy-MM-dd"
style="width: 100%"
/>
</el-form-item>
</el-col>
</el-row>
<el-form-item label="头像">
<el-input v-model="form.avatar" placeholder="请输入头像URL" />
</el-form-item>
<el-form-item label="地址" prop="address">
<el-input v-model="form.address" type="textarea" :rows="2" placeholder="请输入地址" />
</el-form-item>
<el-row>
<el-col :span="12">
<el-form-item label="紧急联系人" prop="emergencyContact">
<el-input v-model="form.emergencyContact" placeholder="请输入紧急联系人" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="紧急联系电话" prop="emergencyPhone">
<el-input v-model="form.emergencyPhone" placeholder="请输入紧急联系电话" maxlength="11" />
</el-form-item>
</el-col>
</el-row>
<el-form-item label="备注">
<el-input v-model="form.remark" type="textarea" :rows="2" placeholder="请输入备注" />
</el-form-item>
@ -542,11 +622,16 @@ export default {
userName: undefined,
phone: undefined,
birthday: undefined,
education: undefined,
occupation: undefined,
address: undefined,
emergencyContact: undefined,
emergencyPhone: undefined,
prison: undefined,
prisonArea: undefined,
gender: undefined,
nation: undefined,
educationLevel: undefined,
crimeName: undefined,
sentenceTerm: undefined,
sentenceStartDate: undefined,
sentenceEndDate: undefined,
entryDate: undefined,
remark: undefined
}
this.resetForm("form")

View File

@ -128,14 +128,6 @@
满分{{ scoreForm.itemScore || 0 }}
</div>
</el-form-item>
<el-form-item label="评语">
<el-input
v-model="scoreForm.comment"
type="textarea"
:rows="4"
placeholder="请输入评语(可选)"
/>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitScoreForm"> </el-button>
@ -164,17 +156,6 @@
/>
</template>
</el-table-column>
<el-table-column label="评语" min-width="200">
<template slot-scope="scope">
<el-input
v-model="scope.row.comment"
type="textarea"
:rows="2"
placeholder="评语(可选)"
size="small"
/>
</template>
</el-table-column>
</el-table>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitBatchScore"> </el-button>
@ -273,8 +254,7 @@ export default {
itemContent: row.itemContent,
itemScore: row.itemScore || 0,
answerText: row.answerText || '',
score: row.answerScore || 0,
comment: ''
score: row.answerScore || 0
};
this.scoreOpen = true;
},
@ -284,8 +264,7 @@ export default {
if (valid) {
const data = {
detailId: this.scoreForm.detailId,
score: this.scoreForm.score,
comment: this.scoreForm.comment
score: this.scoreForm.score
};
submitScoring(data).then(response => {
@ -315,8 +294,7 @@ export default {
itemContent: '',
itemScore: 0,
answerText: '',
score: 0,
comment: ''
score: 0
};
if (this.$refs["scoreForm"]) {
this.$refs["scoreForm"].resetFields();
@ -329,11 +307,10 @@ export default {
return;
}
// scorecomment
// score
this.selectedRows = this.selectedRows.map(row => ({
...row,
score: row.answerScore || 0,
comment: ''
score: row.answerScore || 0
}));
this.batchScoreOpen = true;
@ -342,8 +319,7 @@ export default {
submitBatchScore() {
const scoringList = this.selectedRows.map(row => ({
detailId: row.detailId,
score: row.score || 0,
comment: row.comment || ''
score: row.score || 0
}));
batchSubmitScoring(scoringList).then(response => {

View File

@ -43,6 +43,7 @@ export default {
return {
loading: false,
questionnaireList: [],
selectedUserId: null, // ID
form: {
questionnaireId: undefined,
respondentName: undefined
@ -55,10 +56,16 @@ export default {
};
},
created() {
// URLquestionnaireId
// URLquestionnaireIduserId
const questionnaireId = this.$route.query.questionnaireId;
const userId = this.$route.query.userId;
if (questionnaireId) {
this.form.questionnaireId = parseInt(questionnaireId);
// userId
if (userId) {
this.selectedUserId = parseInt(userId);
}
//
this.$nextTick(() => {
this.startAnswerDirectly();
@ -87,6 +94,11 @@ export default {
respondentName: this.form.respondentName || null
};
// userId
if (this.selectedUserId) {
data.userId = this.selectedUserId;
}
startQuestionnaireAnswer(data).then(response => {
if (response.code === 200) {
this.$modal.msgSuccess("答题已开始");
@ -119,6 +131,11 @@ export default {
respondentName: null //
};
// userId
if (this.selectedUserId) {
data.userId = this.selectedUserId;
}
startQuestionnaireAnswer(data).then(response => {
if (response.code === 200) {
this.$modal.msgSuccess("答题已开始");

View File

@ -44,8 +44,8 @@
plain
icon="el-icon-printer"
size="mini"
:disabled="!isAssessmentSelection"
@click="openSasDialog"
:disabled="multiple"
@click="openExportDialog"
v-hasPermi="['psychology:report:export']"
>导出报告</el-button>
</el-col>
@ -72,7 +72,11 @@
<el-tag v-else type="primary">量表</el-tag>
</template>
</el-table-column>
<el-table-column label="来源ID" align="center" prop="sourceId" width="100" />
<el-table-column label="信息编号" align="center" prop="infoNumber" width="120">
<template slot-scope="scope">
<span>{{ scope.row.infoNumber || '-' }}</span>
</template>
</el-table-column>
<el-table-column label="报告标题" align="center" prop="reportTitle" :show-overflow-tooltip="true" />
<el-table-column label="报告类型" align="center" prop="reportType" width="120">
<template slot-scope="scope">
@ -160,7 +164,7 @@
</el-dialog>
<!-- SAS 报告导出设置 -->
<el-dialog title="SAS报告导出" :visible.sync="sasExportDialog" width="420px" append-to-body @close="resetSasDialog">
<el-dialog title="报告导出" :visible.sync="sasExportDialog" width="420px" append-to-body @close="resetSasDialog">
<el-form :model="sasExportForm" label-width="90px">
<el-form-item label="导出格式">
<el-radio-group v-model="sasExportForm.format">
@ -168,9 +172,6 @@
<el-radio label="print">打印/PDF</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="包含图表">
<el-switch v-model="sasExportForm.includeChart"></el-switch>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="sasExportDialog = false"> </el-button>
@ -229,20 +230,13 @@ export default {
},
sasExportDialog: false,
sasExportForm: {
format: "word",
includeChart: true
format: "word"
},
sasExportLoading: false,
sasTarget: null
};
},
computed: {
isAssessmentSelection() {
const row = this.getActiveRow();
if (!row) return false;
if (!row.sourceType) return true;
return row.sourceType === 'assessment';
}
},
created() {
this.getList();
@ -403,20 +397,36 @@ export default {
}
return null;
},
async openSasDialog(row) {
const target = row || this.getActiveRow();
async openExportDialog() {
//
const target = this.getActiveRow();
if (!target) {
this.$message.warning("请先选择一条量表报告");
this.$message.warning("请先选择一条报告");
return;
}
if (target.sourceType && target.sourceType !== 'assessment') {
this.$message.warning("仅支持量表测评报告生成模板");
console.log('选中的报告对象:', target);
console.log('报告类型:', target.sourceType);
// sourceType
if (!target.sourceType) {
this.$modal.msgError("报告缺少类型信息,请刷新页面后重试");
return;
}
//
if (target.sourceType === 'questionnaire') {
this.sasTarget = { ...target, sourceType: 'questionnaire' };
this.sasExportForm = { format: "word" };
this.sasExportDialog = true;
return;
}
// 使SAS
try {
const resolved = await this.ensureAssessmentInfo(target);
this.sasTarget = resolved;
this.sasExportForm = { format: "word", includeChart: true };
this.sasTarget = { ...resolved, sourceType: 'assessment' };
this.sasExportForm = { format: "word" };
this.sasExportDialog = true;
} catch (error) {
this.$modal.msgError(error.message || "无法定位测评ID");
@ -445,35 +455,97 @@ export default {
this.sasExportDialog = false;
this.sasExportLoading = false;
this.sasTarget = null;
this.sasExportForm = { format: "word", includeChart: true };
this.sasExportForm = { format: "word" };
},
async confirmSasExport() {
if (!this.sasTarget) {
this.$message.warning("未选择报告");
return;
}
const assessmentId = this.sasTarget.sourceId || this.sasTarget.assessmentId;
if (!assessmentId) {
this.$modal.msgError("无法定位测评ID");
return;
}
this.sasExportLoading = true;
try {
const reportData = await loadSasReportData(assessmentId);
if (this.sasExportForm.format === 'word') {
await SASReportGenerator.exportWord(reportData, this.sasExportForm.includeChart);
//
if (this.sasTarget.sourceType === 'questionnaire') {
//
await this.exportQuestionnaireReport();
} else {
await SASReportGenerator.print(reportData, this.sasExportForm.includeChart);
//
await this.exportAssessmentReport();
}
this.$modal.msgSuccess("SAS报告已生成");
this.$modal.msgSuccess("报告已生成");
this.resetSasDialog();
} catch (error) {
console.error("生成SAS报告失败:", error);
console.error("生成报告失败:", error);
this.$modal.msgError("生成失败:" + (error.message || "未知错误"));
} finally {
this.sasExportLoading = false;
}
},
async exportAssessmentReport() {
const assessmentId = this.sasTarget.sourceId || this.sasTarget.assessmentId;
if (!assessmentId) {
throw new Error("无法定位测评ID");
}
const reportData = await loadSasReportData(assessmentId);
if (this.sasExportForm.format === 'word') {
await SASReportGenerator.exportWord(reportData, false);
} else {
await SASReportGenerator.print(reportData, false);
}
},
async exportQuestionnaireReport() {
// HTML
const response = await getReport(this.sasTarget.reportId, 'questionnaire');
if (!response || !response.data) {
throw new Error("获取报告内容失败");
}
const report = response.data;
console.log('问卷报告数据:', report);
// 使
const reportHtml = `
<html>
<head>
<meta charset="UTF-8" />
<title>${report.reportTitle || '问卷报告'}</title>
<style>
body { font-family: 'Microsoft Yahei', sans-serif; padding: 32px; color: #303133; }
h1, h2 { text-align: center; margin: 16px 0; }
.section { margin-top: 24px; }
.report-info { margin: 5px 0; color: #606266; }
table.score-table { width: 100%; border-collapse: collapse; margin: 20px 0; }
table.score-table td, table.score-table th { border: 1px solid #ddd; padding: 10px; font-size: 14px; }
table.score-table th { background-color: #f5f7fa; font-weight: bold; }
.content { margin-top: 24px; line-height: 1.8; }
</style>
</head>
<body>
${report.reportContent || '暂无报告内容'}
</body>
</html>
`;
if (this.sasExportForm.format === 'word') {
// Word
const blob = new Blob(['\ufeff', reportHtml], { type: 'application/msword' });
const filename = `${report.reportTitle || '问卷报告'}_${Date.now()}.doc`;
const link = document.createElement('a');
link.href = window.URL.createObjectURL(blob);
link.download = filename;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
} else {
//
const printWindow = window.open('', '_blank');
printWindow.document.write(reportHtml);
printWindow.document.close();
printWindow.focus();
printWindow.print();
}
},
/** 删除按钮操作 */
handleDelete(row) {
let reportIds;