修复用户和题目管理问题

This commit is contained in:
xiao12feng 2025-11-20 20:42:39 +08:00
parent d729451133
commit 23fe845e7d
16 changed files with 635 additions and 119 deletions

View File

@ -1,6 +1,7 @@
package com.ddnai.web.controller.psychology;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
@ -12,6 +13,8 @@ import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import com.ddnai.common.annotation.Anonymous;
import com.ddnai.common.annotation.Log;
import com.ddnai.common.core.controller.BaseController;
import com.ddnai.common.core.domain.AjaxResult;
@ -20,6 +23,7 @@ import com.ddnai.common.core.page.TableDataInfo;
import com.ddnai.common.enums.BusinessType;
import com.ddnai.common.utils.SecurityUtils;
import com.ddnai.common.utils.StringUtils;
import com.ddnai.common.utils.poi.ExcelUtil;
import com.ddnai.system.domain.psychology.PsyUserProfile;
import com.ddnai.system.service.ISysDeptService;
import com.ddnai.system.service.ISysPostService;
@ -235,4 +239,45 @@ public class PsyUserProfileController extends BaseController
{
return toAjax(profileService.deleteProfileByIds(profileIds));
}
// ==================== 导入导出功能 ====================
/**
* 导出用户档案列表
*/
@PreAuthorize("@ss.hasPermi('psychology:profile:export')")
@Log(title = "用户档案", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(HttpServletResponse response, PsyUserProfile profile)
{
List<PsyUserProfile> list = profileService.selectProfileList(profile);
ExcelUtil<PsyUserProfile> util = new ExcelUtil<PsyUserProfile>(PsyUserProfile.class);
util.exportExcel(response, list, "用户档案数据");
}
/**
* 下载用户档案导入模板
*/
@Anonymous
@GetMapping("/importTemplate")
public void importTemplate(HttpServletResponse response)
{
ExcelUtil<PsyUserProfile> util = new ExcelUtil<PsyUserProfile>(PsyUserProfile.class);
util.importTemplateExcel(response, "用户档案数据");
}
/**
* 导入用户档案数据
*/
@PreAuthorize("@ss.hasPermi('psychology:profile:import')")
@Log(title = "用户档案", businessType = BusinessType.IMPORT)
@PostMapping("/importData")
public AjaxResult importData(MultipartFile file, boolean updateSupport) throws Exception
{
ExcelUtil<PsyUserProfile> util = new ExcelUtil<PsyUserProfile>(PsyUserProfile.class);
List<PsyUserProfile> profileList = util.importExcel(file.getInputStream());
String operName = getUsername();
String message = profileService.importProfile(profileList, updateSupport, operName);
return success(message);
}
}

View File

@ -178,34 +178,23 @@ public class SysLoginController
String infoNumberTrimmed = infoNumber.trim();
SysUser user = null;
// 优先通过信息编号查找用户档案然后通过userId查找用户
// 严格模式必须通过信息编号在用户档案中查找
// 只有在用户档案中存在的信息编号才能登录
com.ddnai.system.domain.psychology.PsyUserProfile profile = userProfileService.selectProfileByInfoNumber(infoNumberTrimmed);
if (profile != null && profile.getUserId() != null)
if (profile == null)
{
user = userService.selectUserById(profile.getUserId());
return AjaxResult.error("信息编号不存在,请先创建用户档案");
}
// 如果通过信息编号找不到则使用原来的逻辑兼容旧版本
if (user == null)
if (profile.getUserId() == null)
{
// 支持两种方式1. 通过user_name查找 2. 如果是数字通过user_id查找
user = userService.selectUserByUserName(infoNumberTrimmed);
// 如果通过user_name找不到且输入的是数字尝试通过user_id查找
if (user == null && StringUtils.isNumeric(infoNumberTrimmed))
{
try
{
Long userId = Long.parseLong(infoNumberTrimmed);
user = userService.selectUserById(userId);
}
catch (NumberFormatException e)
{
// 数字格式错误忽略
}
}
return AjaxResult.error("用户档案异常,未关联系统用户");
}
// 通过档案中的userId查找用户
user = userService.selectUserById(profile.getUserId());
// 检查系统有效时间系统管理员不受限制
try
{

View File

@ -16,6 +16,7 @@ import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import com.ddnai.common.annotation.Anonymous;
import com.ddnai.common.annotation.Log;
import com.ddnai.common.core.controller.BaseController;
import com.ddnai.common.core.domain.AjaxResult;
@ -87,7 +88,8 @@ public class SysUserController extends BaseController
return success(message);
}
@PostMapping("/importTemplate")
@Anonymous
@GetMapping("/importTemplate")
public void importTemplate(HttpServletResponse response)
{
ExcelUtil<SysUser> util = new ExcelUtil<SysUser>(SysUser.class);

View File

@ -21,7 +21,7 @@ public class SysUser extends BaseEntity
{
private static final long serialVersionUID = 1L;
/** 用户ID */
/** 用户ID - 只在导出时显示,导入时不需要,通过信息编号自动设置 */
@Excel(name = "用户序号", type = Type.EXPORT, cellType = ColumnType.NUMERIC, prompt = "用户编号")
private Long userId;
@ -92,6 +92,44 @@ public class SysUser extends BaseEntity
/** 角色ID */
private Long roleId;
// ==================== 心理档案相关字段 ====================
/** 信息编号 */
@Excel(name = "信息编号")
private String infoNumber;
/** 档案类型 */
@Excel(name = "档案类型", readConverterExp = "standard=标准,child=儿童,adult=成人,senior=老年")
private String profileType;
/** 身份证号 */
@Excel(name = "身份证号")
private String idCard;
/** 生日 */
@Excel(name = "生日", width = 30, dateFormat = "yyyy-MM-dd")
private Date birthday;
/** 学历 */
@Excel(name = "学历")
private String education;
/** 职业 */
@Excel(name = "职业")
private String occupation;
/** 地址 */
@Excel(name = "地址")
private String address;
/** 紧急联系人 */
@Excel(name = "紧急联系人")
private String emergencyContact;
/** 紧急联系电话 */
@Excel(name = "紧急联系电话")
private String emergencyPhone;
public SysUser()
{
@ -310,6 +348,98 @@ public class SysUser extends BaseEntity
this.roleId = roleId;
}
// ==================== 心理档案字段的 Getter/Setter ====================
public String getInfoNumber()
{
return infoNumber;
}
public void setInfoNumber(String infoNumber)
{
this.infoNumber = infoNumber;
}
public String getProfileType()
{
return profileType;
}
public void setProfileType(String profileType)
{
this.profileType = profileType;
}
public String getIdCard()
{
return idCard;
}
public void setIdCard(String idCard)
{
this.idCard = idCard;
}
public Date getBirthday()
{
return birthday;
}
public void setBirthday(Date birthday)
{
this.birthday = birthday;
}
public String getEducation()
{
return education;
}
public void setEducation(String education)
{
this.education = education;
}
public String getOccupation()
{
return occupation;
}
public void setOccupation(String occupation)
{
this.occupation = occupation;
}
public String getAddress()
{
return address;
}
public void setAddress(String address)
{
this.address = address;
}
public String getEmergencyContact()
{
return emergencyContact;
}
public void setEmergencyContact(String emergencyContact)
{
this.emergencyContact = emergencyContact;
}
public String getEmergencyPhone()
{
return emergencyPhone;
}
public void setEmergencyPhone(String emergencyPhone)
{
this.emergencyPhone = emergencyPhone;
}
@Override
public String toString() {
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)

View File

@ -29,8 +29,10 @@ import com.ddnai.framework.manager.AsyncManager;
import com.ddnai.framework.manager.factory.AsyncFactory;
import com.ddnai.framework.security.context.AuthenticationContextHolder;
import com.ddnai.common.core.domain.entity.SysUser;
import com.ddnai.common.core.domain.entity.SysRole;
import com.ddnai.system.service.ISysConfigService;
import com.ddnai.system.service.ISysUserService;
import com.ddnai.system.service.psychology.IPsyUserProfileService;
/**
* 登录校验方法
@ -42,6 +44,9 @@ public class SysLoginService
{
private static final Logger log = LoggerFactory.getLogger(SysLoginService.class);
@Autowired
private IPsyUserProfileService userProfileService;
@Autowired
private TokenService tokenService;
@ -100,6 +105,36 @@ public class SysLoginService
}
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));
LoginUser loginUser = (LoginUser) authentication.getPrincipal();
// 检查学员用户是否有档案
SysUser user = loginUser.getUser();
if (user != null && user.getRoles() != null)
{
boolean isStudent = false;
for (SysRole role : user.getRoles())
{
if (role != null && ("student".equalsIgnoreCase(role.getRoleKey()) ||
(role.getRoleName() != null && role.getRoleName().contains("学员"))))
{
isStudent = true;
break;
}
}
// 如果是学员角色必须有档案才能登录
if (isStudent)
{
com.ddnai.system.domain.psychology.PsyUserProfile profile =
userProfileService.selectProfileByUserId(user.getUserId());
if (profile == null || StringUtils.isEmpty(profile.getInfoNumber()))
{
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, "学员账号必须有用户档案才能登录"));
throw new ServiceException("该账号未创建用户档案,请联系管理员创建档案后再登录");
}
log.info("学员登录验证通过,用户: {}, 信息编号: {}", username, profile.getInfoNumber());
}
}
recordLoginInfo(loginUser.getUserId());
// 生成token
return tokenService.createToken(loginUser);

View File

@ -1,5 +1,6 @@
package com.ddnai.system.domain.psychology;
import com.ddnai.common.annotation.Excel;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
@ -17,44 +18,54 @@ public class PsyUserProfile extends BaseEntity
/** 档案ID */
private Long profileId;
/** 用户ID关联sys_user */
/** 用户ID关联sys_user - 不在Excel模板中显示通过信息编号自动设置 */
private Long userId;
/** 档案类型standard标准 child儿童 adult成人 senior老年 */
@Excel(name = "档案类型", readConverterExp = "standard=标准,child=儿童,adult=成人,senior=老年")
private String profileType;
/** 头像 */
private String avatar;
/** 身份证号 */
@Excel(name = "身份证号")
private String idCard;
/** 档案数据JSON格式 */
private String profileData;
/** 姓名 */
@Excel(name = "姓名")
private String userName;
/** 电话 */
@Excel(name = "电话")
private String phone;
/** 生日 */
@JsonFormat(pattern = "yyyy-MM-dd")
@Excel(name = "生日", width = 30, dateFormat = "yyyy-MM-dd")
private java.util.Date birthday;
/** 学历 */
@Excel(name = "学历")
private String education;
/** 职业 */
@Excel(name = "职业")
private String occupation;
/** 地址 */
@Excel(name = "地址")
private String address;
/** 紧急联系人 */
@Excel(name = "紧急联系人")
private String emergencyContact;
/** 紧急联系电话 */
@Excel(name = "紧急联系电话")
private String emergencyPhone;
/** 病史 */
@ -70,6 +81,7 @@ public class PsyUserProfile extends BaseEntity
private String deptName;
/** 信息编号 */
@Excel(name = "信息编号")
private String infoNumber;
public Long getProfileId()

View File

@ -517,6 +517,20 @@ public class SysUserServiceImpl implements ISysUserService
{
try
{
// 如果有信息编号使用信息编号作为登录账号
if (StringUtils.isNotEmpty(user.getInfoNumber()))
{
user.setUserName(user.getInfoNumber()); // 登录账号 = 信息编号
}
// 如果没有用户名跳过
if (StringUtils.isEmpty(user.getUserName()))
{
failureNum++;
failureMsg.append("<br/>").append(failureNum).append("、用户名或信息编号不能为空");
continue;
}
// 验证是否存在这个用户
SysUser u = userMapper.selectUserByUserName(user.getUserName());
if (StringUtils.isNull(u))

View File

@ -314,7 +314,37 @@ public class PsyUserProfileServiceImpl implements IPsyUserProfileService
@Override
public int deleteProfileByIds(Long[] profileIds)
{
return profileMapper.deleteProfileByIds(profileIds);
// 先获取所有要删除的档案记录对应的用户ID
java.util.List<Long> userIdsToDelete = new java.util.ArrayList<>();
for (Long profileId : profileIds)
{
PsyUserProfile profile = profileMapper.selectProfileById(profileId);
if (profile != null && profile.getUserId() != null)
{
userIdsToDelete.add(profile.getUserId());
}
}
// 删除档案
int result = profileMapper.deleteProfileByIds(profileIds);
// 删除对应的用户
if (!userIdsToDelete.isEmpty())
{
Long[] userIds = userIdsToDelete.toArray(new Long[0]);
try
{
userService.deleteUserByIds(userIds);
log.info("删除档案同时删除了对应的用户用户ID: {}", userIdsToDelete);
}
catch (Exception e)
{
log.error("删除用户失败用户ID: {}", userIdsToDelete, e);
// 即使删除用户失败档案已经删除也返回成功
}
}
return result;
}
private void validateInfoNumberUnique(String infoNumber, Long profileId)
@ -336,16 +366,22 @@ public class PsyUserProfileServiceImpl implements IPsyUserProfileService
{
throw new ServiceException("信息编号不能为空");
}
SysUser user = new SysUser();
// 使用信息编号作为登录账号用户名
String loginAccount = profile.getInfoNumber();
user.setUserName(loginAccount);
// 不设置userId让数据库自动生成
// userId和信息编号不需要保持一致信息编号用于登录
user.setUserName(loginAccount); // 登录账号 = 信息编号
user.setNickName(StringUtils.isNotEmpty(profile.getUserName()) ? profile.getUserName() : loginAccount);
user.setPhonenumber(profile.getPhone());
user.setDeptId(profile.getDeptId());
user.setStatus("0");
user.setPassword(SecurityUtils.encryptPassword(resolveInitPassword()));
user.setCreateBy(SecurityUtils.getUsername());
user.setRemark("由用户档案自动创建");
user.setRemark("由用户档案自动创建(登录账号:" + loginAccount + "");
Long studentRoleId = resolveStudentRoleId();
if (studentRoleId != null)
{
@ -355,11 +391,16 @@ public class PsyUserProfileServiceImpl implements IPsyUserProfileService
{
log.warn("未找到学生角色,自动创建的用户将没有角色,请检查角色配置");
}
if (!userService.checkUserNameUnique(user))
{
throw new ServiceException("登录账号 '" + loginAccount + "' 已存在,请更换信息编号");
}
log.info("自动创建用户,登录账号: {}", loginAccount);
userService.insertUser(user);
log.info("用户创建成功用户ID: {}, 登录账号: {}", user.getUserId(), user.getUserName());
return user;
}
@ -418,5 +459,86 @@ public class PsyUserProfileServiceImpl implements IPsyUserProfileService
}
return null;
}
/**
* 导入用户档案数据
*
* @param profileList 用户档案数据列表
* @param isUpdateSupport 是否更新支持如果已存在则进行更新数据
* @param operName 操作用户
* @return 结果
*/
@Override
public String importProfile(List<PsyUserProfile> profileList, Boolean isUpdateSupport, String operName)
{
if (StringUtils.isNull(profileList) || profileList.size() == 0)
{
throw new ServiceException("导入用户档案数据不能为空!");
}
int successNum = 0;
int failureNum = 0;
StringBuilder successMsg = new StringBuilder();
StringBuilder failureMsg = new StringBuilder();
for (PsyUserProfile profile : profileList)
{
try
{
// 设置创建者
profile.setCreateBy(operName);
// 验证信息编号
if (StringUtils.isEmpty(profile.getInfoNumber()))
{
failureNum++;
failureMsg.append("<br/>").append(failureNum).append("、档案信息编号为空");
continue;
}
// 根据信息编号查询是否存在
PsyUserProfile existProfile = profileMapper.selectProfileByInfoNumber(profile.getInfoNumber());
if (StringUtils.isNull(existProfile))
{
// 新增档案
this.insertProfile(profile);
successNum++;
successMsg.append("<br/>").append(successNum).append("、信息编号 ").append(profile.getInfoNumber()).append(" 导入成功");
}
else if (isUpdateSupport)
{
// 更新档案
profile.setProfileId(existProfile.getProfileId());
profile.setUserId(existProfile.getUserId());
profile.setUpdateBy(operName);
this.updateProfile(profile);
successNum++;
successMsg.append("<br/>").append(successNum).append("、信息编号 ").append(profile.getInfoNumber()).append(" 更新成功");
}
else
{
failureNum++;
failureMsg.append("<br/>").append(failureNum).append("、信息编号 ").append(profile.getInfoNumber()).append(" 已存在");
}
}
catch (Exception e)
{
failureNum++;
String msg = "<br/>" + failureNum + "、信息编号 " + profile.getInfoNumber() + " 导入失败:";
failureMsg.append(msg).append(e.getMessage());
log.error(msg, e);
}
}
if (failureNum > 0)
{
failureMsg.insert(0, "很抱歉,导入失败!共 " + failureNum + " 条数据格式不正确,错误如下:");
throw new ServiceException(failureMsg.toString());
}
else
{
successMsg.insert(0, "恭喜您,数据已全部导入成功!共 " + successNum + " 条,数据如下:");
}
return successMsg.toString();
}
}

View File

@ -65,5 +65,15 @@ public interface IPsyUserProfileService
* @return 结果
*/
public int deleteProfileByIds(Long[] profileIds);
/**
* 导入用户档案数据
*
* @param profileList 用户档案数据列表
* @param isUpdateSupport 是否更新支持如果已存在则进行更新数据
* @param operName 操作用户
* @return 结果
*/
public String importProfile(List<PsyUserProfile> profileList, Boolean isUpdateSupport, String operName);
}

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="UTF-8" ?>
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
@ -152,7 +152,6 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<insert id="insertUser" parameterType="SysUser" useGeneratedKeys="true" keyProperty="userId">
insert into sys_user(
<if test="userId != null and userId != 0">user_id,</if>
<if test="deptId != null and deptId != 0">dept_id,</if>
<if test="userName != null and userName != ''">user_name,</if>
<if test="nickName != null and nickName != ''">nick_name,</if>
@ -167,7 +166,6 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="remark != null and remark != ''">remark,</if>
create_time
)values(
<if test="userId != null and userId != ''">#{userId},</if>
<if test="deptId != null and deptId != ''">#{deptId},</if>
<if test="userName != null and userName != ''">#{userName},</if>
<if test="nickName != null and nickName != ''">#{nickName},</if>

View File

@ -106,6 +106,7 @@ export const constantRoutes = [
{
path: '/student/tests',
component: () => import('@/views/student/tests'),
name: 'StudentTests',
hidden: true,
meta: { title: '心理测试题' }
},

View File

@ -9,14 +9,7 @@
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="用户ID" prop="userId">
<el-input
v-model="queryParams.userId"
placeholder="请输入用户ID"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<!-- 用户ID已隐藏只使用信息编号 -->
<el-form-item label="手机号码" prop="phone">
<el-input
v-model="queryParams.phone"
@ -85,13 +78,33 @@
v-hasPermi="['psychology:profile:remove']"
>删除</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="info"
plain
icon="el-icon-upload2"
size="mini"
@click="handleImport"
v-hasPermi="['psychology:profile:import']"
>导入</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="warning"
plain
icon="el-icon-download"
size="mini"
@click="handleExport"
v-hasPermi="['psychology:profile:export']"
>导出</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table ref="profileTable" v-loading="loading" :data="profileList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="序号" align="center" prop="profileId" width="80" />
<el-table-column label="用户ID" align="center" prop="userId" width="100" />
<!-- 用户ID列已隐藏只显示信息编号 -->
<el-table-column label="信息编号" align="center" prop="infoNumber" width="120" />
<el-table-column label="档案类型" align="center" prop="profileType" width="120">
<template slot-scope="scope">
@ -335,12 +348,32 @@
<el-button @click="cancelUser"> </el-button>
</div>
</el-dialog>
<!-- 用户档案导入对话框 -->
<el-dialog :title="upload.title" :visible.sync="upload.open" width="400px" append-to-body>
<el-upload ref="upload" :limit="1" accept=".xlsx, .xls" :headers="upload.headers" :action="upload.url + '?updateSupport=' + upload.updateSupport" :disabled="upload.isUploading" :on-progress="handleFileUploadProgress" :on-success="handleFileSuccess" :auto-upload="false" drag>
<i class="el-icon-upload"></i>
<div class="el-upload__text">将文件拖到此处<em>点击上传</em></div>
<div class="el-upload__tip text-center" slot="tip">
<div class="el-upload__tip" slot="tip">
<el-checkbox v-model="upload.updateSupport" />是否更新已经存在的用户档案数据
</div>
<span>仅允许导入xlsxlsx格式文件</span>
<el-link type="primary" :underline="false" style="font-size: 12px; vertical-align: baseline" @click="importTemplate">下载模板</el-link>
</div>
</el-upload>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitFileForm"> </el-button>
<el-button @click="upload.open = false"> </el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { listProfile, getProfile, getProfileByUserId, delProfile, addProfile, updateProfile, getUserInfo, addUserInProfile, getUserInfoById, updateUserInProfile, delUserInProfile } from "@/api/psychology/profile"
import { deptTreeSelect } from "@/api/system/user"
import { getToken } from "@/utils/auth"
import Treeselect from "@riophae/vue-treeselect"
import "@riophae/vue-treeselect/dist/vue-treeselect.css"
@ -445,6 +478,21 @@ export default {
trigger: "blur"
}
]
},
//
upload: {
//
open: false,
//
title: "",
//
isUploading: false,
//
updateSupport: 0,
//
headers: { Authorization: "Bearer " + getToken() },
//
url: process.env.VUE_APP_BASE_API + "/psychology/profile/importData"
}
}
},
@ -503,27 +551,11 @@ export default {
}
this.resetForm("form")
},
// userId
//
handleInfoNumberInput(value) {
//
const numericValue = value.replace(/\D/g, '')
this.form.infoNumber = numericValue
// userId
if (numericValue) {
this.form.userId = parseInt(numericValue)
} else {
this.form.userId = undefined
}
},
// userIdinfoNumber
syncUserIdWithInfoNumber() {
if (this.form.infoNumber) {
// userId
this.form.userId = parseInt(this.form.infoNumber)
} else if (this.form.userId) {
// userIduserId
this.form.infoNumber = this.form.userId.toString()
}
},
//
handleUserNameInput(value) {
@ -690,8 +722,6 @@ export default {
getProfile(row.profileId).then(response => {
if (response.data) {
this.form = response.data
// userId infoNumber
this.syncUserIdWithInfoNumber()
this.open = true
this.title = "修改用户档案"
} else {
@ -714,8 +744,6 @@ export default {
if (response.data && response.data.profileId) {
//
this.form = response.data
// userId infoNumber
this.syncUserIdWithInfoNumber()
this.open = true
this.title = "修改用户档案"
} else {
@ -834,6 +862,42 @@ export default {
this.getList()
this.$modal.msgSuccess("删除成功")
}).catch(() => {})
},
/** 导出按钮操作 */
handleExport() {
this.download('psychology/profile/export', {
...this.queryParams
}, `profile_${new Date().getTime()}.xlsx`)
},
/** 导入按钮操作 */
handleImport() {
this.upload.title = "用户档案导入"
this.upload.open = true
},
/** 下载模板操作 */
importTemplate() {
window.location.href = process.env.VUE_APP_BASE_API + '/psychology/profile/importTemplate'
},
//
handleFileUploadProgress(event, file, fileList) {
this.upload.isUploading = true
},
//
handleFileSuccess(response, file, fileList) {
this.upload.open = false
this.upload.isUploading = false
this.$refs.upload.clearFiles()
this.$alert("<div style='overflow: auto;overflow-x: hidden;max-height: 70vh;padding: 10px 20px 0;'>" + response.msg + "</div>", "导入结果", { dangerouslyUseHTMLString: true })
this.getList()
},
//
submitFileForm() {
const file = this.$refs.upload.uploadFiles
if (!file || file.length === 0 || !file[0].name.toLowerCase().endsWith('.xls') && !file[0].name.toLowerCase().endsWith('.xlsx')) {
this.$modal.msgError('请选择后缀为 "xls"或"xlsx"的文件。')
return
}
this.$refs.upload.submit()
}
}
}

View File

@ -116,23 +116,10 @@
</el-select>
</el-form-item>
<el-form-item label="报告摘要" prop="summary">
<el-input
v-model="editForm.summary"
type="textarea"
:rows="4"
placeholder="请输入报告摘要"
/>
<Editor v-model="editForm.summary" :min-height="150" />
</el-form-item>
<el-form-item label="报告内容" prop="reportContent">
<el-input
v-model="editForm.reportContent"
type="textarea"
:rows="15"
placeholder="请输入报告内容支持HTML格式"
/>
<div style="margin-top: 10px; color: #909399; font-size: 12px;">
<i class="el-icon-info"></i> 提示报告内容支持HTML格式可以包含标题段落列表等格式
</div>
<Editor v-model="editForm.reportContent" :min-height="400" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
@ -148,9 +135,11 @@ import { getReport, getReportByAssessmentId, updateReportWithType } from "@/api/
import { getQuestionnaireRankList } from "@/api/psychology/questionnaireAnswer";
import request from '@/utils/request';
import axios from 'axios';
import Editor from "@/components/Editor";
export default {
name: "ReportDetail",
components: { Editor },
data() {
return {
loading: true,

View File

@ -147,23 +147,10 @@
</el-select>
</el-form-item>
<el-form-item label="报告摘要" prop="summary">
<el-input
v-model="editForm.summary"
type="textarea"
:rows="4"
placeholder="请输入报告摘要"
/>
<Editor v-model="editForm.summary" :min-height="150" />
</el-form-item>
<el-form-item label="报告内容" prop="reportContent">
<el-input
v-model="editForm.reportContent"
type="textarea"
:rows="15"
placeholder="请输入报告内容支持HTML格式"
/>
<div style="margin-top: 10px; color: #909399; font-size: 12px;">
<i class="el-icon-info"></i> 提示报告内容支持HTML格式可以包含标题段落列表等格式
</div>
<Editor v-model="editForm.reportContent" :min-height="400" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
@ -197,9 +184,11 @@
import { listReport, getReport, delReport, exportReport, updateReportWithType } from "@/api/psychology/report";
import { loadSasReportData } from "@/services/report/ReportDataMapper";
import SASReportGenerator from "@/services/report/SASReportGenerator";
import Editor from "@/components/Editor";
export default {
name: "Report",
components: { Editor },
data() {
return {
//

View File

@ -91,10 +91,23 @@ export default {
})
}
},
watch: {
//
'$route'(to, from) {
//
if (to.path === '/student/tests') {
this.loadTestList()
}
}
},
created() {
//
this.loadTestList()
},
//
activated() {
this.loadTestList()
},
methods: {
//
loadTestList() {
@ -105,9 +118,19 @@ export default {
includeQuestionnaire: true //
}).then(response => {
//
this.testList = (response.rows || []).filter(scale => {
let filteredList = (response.rows || []).filter(scale => {
return scale.itemCount && scale.itemCount > 0
})
//
this.testList = filteredList.sort((a, b) => {
// 使 updateTime使 createTime
const timeA = a.updateTime || a.createTime || ''
const timeB = b.updateTime || b.createTime || ''
//
return timeB.localeCompare(timeA)
})
this.loading = false
}).catch(error => {
console.error("loadTestList, 加载测试题列表失败:", error)

View File

@ -96,32 +96,10 @@
</el-row>
<!-- 添加或修改用户配置对话框 -->
<el-dialog :title="title" :visible.sync="open" width="600px" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
<el-row>
<el-col :span="12">
<el-form-item label="用户昵称" prop="nickName">
<el-input v-model="form.nickName" placeholder="请输入用户昵称" maxlength="30" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="归属部门" prop="deptId">
<treeselect v-model="form.deptId" :options="enabledDeptOptions" :show-count="true" placeholder="请选择归属部门" />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12" v-if="false">
<el-form-item label="手机号码" prop="phonenumber">
<el-input v-model="form.phonenumber" placeholder="请输入手机号码" maxlength="11" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="邮箱" prop="email">
<el-input v-model="form.email" placeholder="请输入邮箱" maxlength="50" />
</el-form-item>
</el-col>
</el-row>
<el-dialog :title="title" :visible.sync="open" width="900px" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="120px">
<!-- 基本信息 -->
<el-divider content-position="left">基本信息</el-divider>
<el-row>
<el-col :span="12">
<el-form-item v-if="form.userId == undefined" label="用户名称" prop="userName">
@ -136,7 +114,31 @@
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="用户性别">
<el-form-item label="用户昵称" prop="nickName">
<el-input v-model="form.nickName" placeholder="请输入用户昵称/姓名" maxlength="30" @input="handleNickNameInput" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="信息编号" prop="infoNumber">
<el-input v-model="form.infoNumber" placeholder="请输入信息编号(仅数字)" @input="handleInfoNumberInput" maxlength="20" />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="手机号码" prop="phonenumber">
<el-input v-model="form.phonenumber" placeholder="请输入手机号码" maxlength="11" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="邮箱" prop="email">
<el-input v-model="form.email" placeholder="请输入邮箱" maxlength="50" />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="性别" prop="sex">
<el-select v-model="form.sex" placeholder="请选择性别">
<el-option v-for="dict in dict.type.sys_user_sex" :key="dict.value" :label="dict.label" :value="dict.value"></el-option>
</el-select>
@ -150,6 +152,78 @@
</el-form-item>
</el-col>
</el-row>
<!-- 心理档案信息 -->
<el-divider content-position="left">心理档案信息</el-divider>
<el-row>
<el-col :span="12">
<el-form-item label="档案类型" prop="profileType">
<el-select v-model="form.profileType" placeholder="请选择档案类型">
<el-option label="标准" value="standard" />
<el-option label="儿童" value="child" />
<el-option label="成人" value="adult" />
<el-option label="老年" value="senior" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="身份证号" prop="idCard">
<el-input v-model="form.idCard" placeholder="请输入身份证号" maxlength="18" />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="生日" prop="birthday">
<el-date-picker
v-model="form.birthday"
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="education">
<el-input v-model="form.education" placeholder="请输入学历" />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="职业" prop="occupation">
<el-input v-model="form.occupation" placeholder="请输入职业" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="归属部门" prop="deptId">
<treeselect v-model="form.deptId" :options="enabledDeptOptions" :show-count="true" placeholder="请选择归属部门" />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
<el-form-item label="地址" prop="address">
<el-input v-model="form.address" type="textarea" :rows="2" placeholder="请输入地址" />
</el-form-item>
</el-col>
</el-row>
<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-divider content-position="left">系统权限</el-divider>
<el-row>
<el-col :span="12">
<el-form-item label="岗位">
@ -401,10 +475,29 @@ export default {
status: "0",
remark: undefined,
postIds: [],
roleIds: []
roleIds: [],
//
infoNumber: undefined,
profileType: 'standard',
idCard: undefined,
birthday: undefined,
education: undefined,
occupation: undefined,
address: undefined,
emergencyContact: undefined,
emergencyPhone: undefined
}
this.resetForm("form")
},
//
handleInfoNumberInput(value) {
const numericValue = value.replace(/\D/g, '')
this.form.infoNumber = numericValue
},
//
handleNickNameInput(value) {
this.form.nickName = value.replace(/[^\u4e00-\u9fa5]/g, '')
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1