feat(xg): 新增按学号重置学生密码(Web/移动端)
学工老师可按学号查询并重置学生登录密码为固定默认值,独立权限不复用系统用户重置。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
0765f00458
commit
df4c21bd0d
14 files changed
+781
No files matched your search
+24
@@ -44,6 +44,7 @@ import org.dromara.common.core.utils.SpringUtils;
|
||||
import org.dromara.common.core.utils.MapstructUtils;
|
||||
import org.dromara.common.satoken.utils.LoginHelper;
|
||||
import org.dromara.xg.domain.vo.XgStudentMsgVo;
|
||||
import org.dromara.xg.domain.vo.StudentPwdResetVo;
|
||||
import org.dromara.xg.domain.bo.XgStudentMsgBo;
|
||||
import org.dromara.xg.domain.bo.XgStudentMsgAdvancedSearchBo;
|
||||
import org.dromara.xg.service.IXgStudentMsgService;
|
||||
@@ -660,6 +661,29 @@ public class XgStudentMsgController extends BaseController {
|
||||
return R.ok(studentInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* 按学号查询学生基本信息(重置密码页)
|
||||
* 须声明在 /{id} 之前,避免被路径变量吞掉
|
||||
*/
|
||||
@SaCheckPermission("studentMsg:studentPwdReset:query")
|
||||
@GetMapping("/queryByStudentNo/{studentNo}")
|
||||
public R<StudentPwdResetVo> queryByStudentNo(
|
||||
@NotBlank(message = "学号不能为空") @PathVariable("studentNo") String studentNo) {
|
||||
return R.ok(xgStudentMsgService.queryForPwdResetByStudentNo(studentNo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 按学号重置学生登录密码为固定默认值
|
||||
*/
|
||||
@SaCheckPermission("studentMsg:studentPwdReset:reset")
|
||||
@Log(title = "重置学生密码", businessType = BusinessType.UPDATE)
|
||||
@RepeatSubmit()
|
||||
@PutMapping("/resetStudentPassword/{studentNo}")
|
||||
public R<Void> resetStudentPassword(
|
||||
@NotBlank(message = "学号不能为空") @PathVariable("studentNo") String studentNo) {
|
||||
return toAjax(xgStudentMsgService.resetStudentPasswordByStudentNo(studentNo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取学生信息详细信息
|
||||
*
|
||||
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
package org.dromara.xg.domain.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 重置学生密码:按学号查询展示信息
|
||||
*/
|
||||
@Data
|
||||
public class StudentPwdResetVo implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** 学生ID(与 sys_user.user_id 相同) */
|
||||
private Long id;
|
||||
|
||||
/** 学号 */
|
||||
private String studentNo;
|
||||
|
||||
/** 姓名 */
|
||||
private String name;
|
||||
|
||||
/** 班级名称 */
|
||||
private String className;
|
||||
|
||||
/** 班主任姓名 */
|
||||
private String classTeacherName;
|
||||
}
|
||||
+17
@@ -3,6 +3,7 @@ package org.dromara.xg.service;
|
||||
import org.dromara.xg.domain.vo.XgStudentMsgExportVo;
|
||||
import org.dromara.xg.domain.vo.XgStudentMsgVo;
|
||||
import org.dromara.xg.domain.vo.XgStudentTreeVo;
|
||||
import org.dromara.xg.domain.vo.StudentPwdResetVo;
|
||||
import org.dromara.xg.domain.bo.XgStudentMsgBo;
|
||||
import org.dromara.xg.domain.bo.XgStudentMsgAdvancedSearchBo;
|
||||
import org.dromara.common.mybatis.core.page.TableDataInfo;
|
||||
@@ -176,4 +177,20 @@ public interface IXgStudentMsgService {
|
||||
* @return 学生信息树
|
||||
*/
|
||||
List<XgStudentTreeVo> getCurrentUserStudentTree();
|
||||
|
||||
/**
|
||||
* 按学号查询学生基本信息(重置密码页展示)
|
||||
*
|
||||
* @param studentNo 学号
|
||||
* @return 姓名/学号/班级/班主任
|
||||
*/
|
||||
StudentPwdResetVo queryForPwdResetByStudentNo(String studentNo);
|
||||
|
||||
/**
|
||||
* 按学号重置学生登录密码为固定默认值
|
||||
*
|
||||
* @param studentNo 学号
|
||||
* @return 是否成功
|
||||
*/
|
||||
Boolean resetStudentPasswordByStudentNo(String studentNo);
|
||||
}
|
||||
+77
@@ -25,6 +25,8 @@ import org.dromara.system.mapper.SysDeptMapper;
|
||||
import org.dromara.system.domain.bo.SysUserBo;
|
||||
import org.dromara.system.domain.SysDept;
|
||||
import org.dromara.system.domain.vo.SysDeptVo;
|
||||
import org.dromara.system.domain.vo.SysUserVo;
|
||||
import org.dromara.xg.domain.vo.StudentPwdResetVo;
|
||||
import cn.hutool.crypto.digest.BCrypt;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import java.util.List;
|
||||
@@ -1557,4 +1559,79 @@ public class XgStudentMsgServiceImpl implements IXgStudentMsgService {
|
||||
private Long getCurrentUserCollegeId() {
|
||||
return currentUserCollegeHelper.resolveCurrentUserCollegeId();
|
||||
}
|
||||
|
||||
/** 学生账号重置密码固定值(仅用于管理端重置学生密码) */
|
||||
private static final String STUDENT_RESET_PASSWORD = "Hzdyjsxs123456!@#";
|
||||
|
||||
@Override
|
||||
public StudentPwdResetVo queryForPwdResetByStudentNo(String studentNo) {
|
||||
if (StringUtils.isBlank(studentNo)) {
|
||||
throw new ServiceException("学号不能为空");
|
||||
}
|
||||
XgStudentMsg student = baseMapper.selectOne(
|
||||
Wrappers.<XgStudentMsg>lambdaQuery()
|
||||
.eq(XgStudentMsg::getStudentNo, studentNo.trim())
|
||||
.last("LIMIT 1")
|
||||
);
|
||||
if (student == null) {
|
||||
throw new ServiceException("未找到学号对应的学生信息");
|
||||
}
|
||||
StudentPwdResetVo vo = new StudentPwdResetVo();
|
||||
vo.setId(student.getId());
|
||||
vo.setStudentNo(student.getStudentNo());
|
||||
vo.setName(student.getName());
|
||||
vo.setClassName(student.getClassName());
|
||||
vo.setClassTeacherName(student.getClassTeacherName());
|
||||
// 学生档案班主任为空时,回退班级表
|
||||
if (StringUtils.isBlank(vo.getClassTeacherName()) && StringUtils.isNotBlank(student.getClassId())) {
|
||||
try {
|
||||
XgClassMsg classMsg = xgClassMsgMapper.selectById(Long.valueOf(student.getClassId()));
|
||||
if (classMsg != null && StringUtils.isNotBlank(classMsg.getClassTeacherName())) {
|
||||
vo.setClassTeacherName(classMsg.getClassTeacherName());
|
||||
}
|
||||
if (StringUtils.isBlank(vo.getClassName()) && classMsg != null) {
|
||||
vo.setClassName(classMsg.getClassName());
|
||||
}
|
||||
} catch (NumberFormatException ignored) {
|
||||
// classId 非数字时忽略回退
|
||||
}
|
||||
}
|
||||
return vo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean resetStudentPasswordByStudentNo(String studentNo) {
|
||||
if (StringUtils.isBlank(studentNo)) {
|
||||
throw new ServiceException("学号不能为空");
|
||||
}
|
||||
String no = studentNo.trim();
|
||||
XgStudentMsg student = baseMapper.selectOne(
|
||||
Wrappers.<XgStudentMsg>lambdaQuery()
|
||||
.eq(XgStudentMsg::getStudentNo, no)
|
||||
.last("LIMIT 1")
|
||||
);
|
||||
if (student == null) {
|
||||
throw new ServiceException("未找到学号对应的学生信息");
|
||||
}
|
||||
if (student.getId() == null) {
|
||||
throw new ServiceException("学生数据异常,缺少主键");
|
||||
}
|
||||
SysUserVo user = sysUserService.selectUserById(student.getId());
|
||||
if (user == null) {
|
||||
throw new ServiceException("该学生没有登录账号,无法重置密码");
|
||||
}
|
||||
if (!"2".equals(user.getAccountType())) {
|
||||
throw new ServiceException("该账号不是学生账号,禁止重置");
|
||||
}
|
||||
if (StringUtils.isNotBlank(user.getUserName()) && !no.equals(user.getUserName())) {
|
||||
throw new ServiceException("学号与登录账号不一致,请联系管理员");
|
||||
}
|
||||
int rows = sysUserService.resetUserPwd(student.getId(), BCrypt.hashpw(STUDENT_RESET_PASSWORD));
|
||||
if (rows <= 0) {
|
||||
throw new ServiceException("重置密码失败,请稍后重试");
|
||||
}
|
||||
log.info("学生密码已重置, studentNo={}, studentId={}, operator={}",
|
||||
no, student.getId(), LoginHelper.getUserId());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
-- =============================================================================
|
||||
-- 重置学生密码:菜单 + 按钮权限
|
||||
-- 日期:2026-10-26
|
||||
-- 父菜单:学生管理 (1959499382894141441)
|
||||
-- Web 页面:xg/xsjcxx/studentMsg/studentPwdReset/index
|
||||
-- 移动端首页入口权限:student:pwdreset:listapp
|
||||
-- 重置密码固定值(服务端写死):Hzdyjsxs123456!@#
|
||||
-- 执行:开发/生产库均可幂等执行;执行后给角色勾选菜单或跑下方角色授权段
|
||||
-- =============================================================================
|
||||
|
||||
-- 1. 菜单页(学生管理下级)
|
||||
INSERT INTO `sys_menu` (
|
||||
`menu_id`, `menu_name`, `parent_id`, `order_num`, `path`, `component`, `query_param`,
|
||||
`is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`,
|
||||
`create_dept`, `create_by`, `create_time`, `remark`
|
||||
)
|
||||
SELECT 2026102610000000101, '重置学生密码', 1959499382894141441, 16, 'studentPwdReset',
|
||||
'xg/xsjcxx/studentMsg/studentPwdReset/index', NULL,
|
||||
1, 0, 'C', '0', '0', 'studentMsg:studentPwdReset:list', 'password',
|
||||
103, 1, NOW(), '按学号查询并重置学生登录密码'
|
||||
FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `sys_menu` WHERE `menu_id` = 2026102610000000101);
|
||||
|
||||
-- 2. 按钮:查询
|
||||
INSERT INTO `sys_menu` (
|
||||
`menu_id`, `menu_name`, `parent_id`, `order_num`, `path`, `component`, `query_param`,
|
||||
`is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`,
|
||||
`create_dept`, `create_by`, `create_time`, `remark`
|
||||
)
|
||||
SELECT 2026102610000000102, '查询学生', 2026102610000000101, 1, '#', '', NULL,
|
||||
1, 0, 'F', '0', '0', 'studentMsg:studentPwdReset:query', '#',
|
||||
103, 1, NOW(), '按学号查询学生信息'
|
||||
FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `sys_menu` WHERE `menu_id` = 2026102610000000102);
|
||||
|
||||
-- 3. 按钮:重置密码
|
||||
INSERT INTO `sys_menu` (
|
||||
`menu_id`, `menu_name`, `parent_id`, `order_num`, `path`, `component`, `query_param`,
|
||||
`is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`,
|
||||
`create_dept`, `create_by`, `create_time`, `remark`
|
||||
)
|
||||
SELECT 2026102610000000103, '确认重置', 2026102610000000101, 2, '#', '', NULL,
|
||||
1, 0, 'F', '0', '0', 'studentMsg:studentPwdReset:reset', '#',
|
||||
103, 1, NOW(), '确认重置学生密码'
|
||||
FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `sys_menu` WHERE `menu_id` = 2026102610000000103);
|
||||
|
||||
-- 4. 移动端首页入口权限(挂在同一菜单页下,供 hasPermi 控制入口可见)
|
||||
INSERT INTO `sys_menu` (
|
||||
`menu_id`, `menu_name`, `parent_id`, `order_num`, `path`, `component`, `query_param`,
|
||||
`is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`,
|
||||
`create_dept`, `create_by`, `create_time`, `remark`
|
||||
)
|
||||
SELECT 2026102610000000104, '重置学生密码(移动端)', 2026102610000000101, 3, '', '', NULL,
|
||||
1, 0, 'F', '0', '0', 'student:pwdreset:listapp', '#',
|
||||
103, 1, NOW(), '移动端首页入口权限'
|
||||
FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `sys_menu` WHERE `menu_id` = 2026102610000000104);
|
||||
|
||||
-- 5. 给已有「学生信息」菜单权限的角色补授权(幂等)
|
||||
INSERT INTO `sys_role_menu` (`role_id`, `menu_id`)
|
||||
SELECT DISTINCT rm.role_id, m.menu_id
|
||||
FROM `sys_role_menu` rm
|
||||
CROSS JOIN (
|
||||
SELECT 2026102610000000101 AS menu_id UNION ALL
|
||||
SELECT 2026102610000000102 UNION ALL
|
||||
SELECT 2026102610000000103 UNION ALL
|
||||
SELECT 2026102610000000104
|
||||
) m
|
||||
WHERE rm.menu_id = 1948289114628902914
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM `sys_role_menu` x
|
||||
WHERE x.role_id = rm.role_id AND x.menu_id = m.menu_id
|
||||
);
|
||||
|
||||
-- 核对
|
||||
SELECT menu_id, menu_name, parent_id, path, component, perms, menu_type
|
||||
FROM sys_menu
|
||||
WHERE menu_id BETWEEN 2026102610000000101 AND 2026102610000000104
|
||||
ORDER BY menu_id;
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
## API模块列表
|
||||
- [学生信息管理API](./学生信息管理API.md)
|
||||
- [学生密码重置API](./学生密码重置API.md) - **新增**:按学号查询并重置学生登录密码为固定默认值(2026-10-26)
|
||||
- [学生信息收集批次管理API](./学生信息收集批次管理API.md)
|
||||
- [学生信息收集批次API](./学生信息收集批次API.md) - **重要更新**:修改列表排序方式为按创建时间倒序(2025-01-03)
|
||||
- [班级信息管理API](./班级信息管理API.md)
|
||||
|
||||
@@ -0,0 +1,141 @@
|
||||
# 学生密码重置 API 接口文档
|
||||
|
||||
## 模块概述
|
||||
|
||||
**模块名称**:Student Password Reset / 学生密码重置
|
||||
**基础路径**:`/studentMsg/studentMsg`
|
||||
**版本**:v1.0
|
||||
**负责人**:学工模块
|
||||
**更新时间**:2026-10-26
|
||||
|
||||
### 模块功能
|
||||
|
||||
学工老师按学号查询学生基本信息后,一键将对应学生登录账号密码重置为系统固定默认值。
|
||||
**不复用** `system:user:resetPwd`,避免把系统用户管理权下放给学工老师。
|
||||
|
||||
**核心约束**:
|
||||
- 仅允许重置 `sys_user.account_type = '2'`(学生账号)
|
||||
- 要求 `user_id = xg_student_msg.id`,且 `user_name` 与学号一致(若有用户名)
|
||||
- 密码由服务端写死常量,前端不可传入自定义密码
|
||||
- 有菜单/按钮权限即可操作任意学生,不做班主任数据范围限制
|
||||
|
||||
**固定默认密码**(服务端常量,勿在客户端硬编码用于改密请求):`Hzdyjsxs123456!@#`
|
||||
|
||||
### 权限说明
|
||||
|
||||
| 权限标识 | 说明 |
|
||||
| -------- | ---- |
|
||||
| `studentMsg:studentPwdReset:list` | Web 菜单页可见 |
|
||||
| `studentMsg:studentPwdReset:query` | 按学号查询 |
|
||||
| `studentMsg:studentPwdReset:reset` | 确认重置密码 |
|
||||
| `student:pwdreset:listapp` | 移动端首页入口可见 |
|
||||
|
||||
菜单脚本:`backend/script/sql/update/update_20261026_student_pwd_reset_menu.sql`
|
||||
|
||||
---
|
||||
|
||||
## 接口概览
|
||||
|
||||
| 接口名称 | HTTP 方法 | 路径 | 权限要求 | 功能描述 |
|
||||
| -------- | --------- | ---- | -------- | -------- |
|
||||
| 按学号查询学生 | GET | `/studentMsg/studentMsg/queryByStudentNo/{studentNo}` | `studentMsg:studentPwdReset:query` | 返回姓名、学号、班级、班主任 |
|
||||
| 重置学生密码 | PUT | `/studentMsg/studentMsg/resetStudentPassword/{studentNo}` | `studentMsg:studentPwdReset:reset` | 重置为固定默认密码 |
|
||||
|
||||
---
|
||||
|
||||
## 详细接口说明
|
||||
|
||||
### 1. 按学号查询学生(重置密码页)
|
||||
|
||||
- **接口路径**:`GET /studentMsg/studentMsg/queryByStudentNo/{studentNo}`
|
||||
- **权限标识**:`studentMsg:studentPwdReset:query`
|
||||
- **功能描述**:按学号查询学生档案,用于重置前确认身份;班主任姓名优先取学生档案,为空时回退班级表
|
||||
|
||||
#### 路径参数
|
||||
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
| ------ | ---- | ---- | ---- |
|
||||
| studentNo | string | 是 | 学号 |
|
||||
|
||||
#### 成功响应
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"msg": "操作成功",
|
||||
"data": {
|
||||
"id": 1234567890123456789,
|
||||
"studentNo": "202513063048",
|
||||
"name": "张三",
|
||||
"className": "计应2401",
|
||||
"classTeacherName": "李老师"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 错误场景
|
||||
|
||||
| 场景 | 提示 |
|
||||
| ---- | ---- |
|
||||
| 学号为空 | 学号不能为空 |
|
||||
| 学号不存在 | 未找到学号对应的学生信息 |
|
||||
| 无权限 | 403 |
|
||||
|
||||
---
|
||||
|
||||
### 2. 重置学生密码
|
||||
|
||||
- **接口路径**:`PUT /studentMsg/studentMsg/resetStudentPassword/{studentNo}`
|
||||
- **权限标识**:`studentMsg:studentPwdReset:reset`
|
||||
- **功能描述**:将学号对应学生登录密码重置为固定默认值;写操作日志「重置学生密码」
|
||||
- **防重复提交**:`@RepeatSubmit`
|
||||
|
||||
#### 路径参数
|
||||
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
| ------ | ---- | ---- | ---- |
|
||||
| studentNo | string | 是 | 学号 |
|
||||
|
||||
#### 请求体
|
||||
|
||||
无。密码由服务端常量生成 BCrypt 后写入,不允许客户端传密。
|
||||
|
||||
#### 成功响应
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"msg": "操作成功"
|
||||
}
|
||||
```
|
||||
|
||||
#### 错误场景
|
||||
|
||||
| 场景 | 提示 |
|
||||
| ---- | ---- |
|
||||
| 学号不存在 | 未找到学号对应的学生信息 |
|
||||
| 无登录账号 | 该学生没有登录账号,无法重置密码 |
|
||||
| 非学生账号 | 该账号不是学生账号,禁止重置 |
|
||||
| 学号与登录名不一致 | 学号与登录账号不一致,请联系管理员 |
|
||||
| 无权限 | 403 |
|
||||
|
||||
---
|
||||
|
||||
## 前端与移动端
|
||||
|
||||
| 端 | 路径 / 入口 |
|
||||
| -- | ----------- |
|
||||
| Web | `web/src/views/xg/xsjcxx/studentMsg/studentPwdReset/index.vue` |
|
||||
| Web API | `web/src/api/xg/xsjcxx/studentMsg/studentMsg/index.ts` → `queryStudentForPwdReset` / `resetStudentPassword` |
|
||||
| 移动端页面 | `mobile/src/pages/xg/xsjcxx/studentPwdReset/index.vue` |
|
||||
| 移动端 API | `mobile/src/api/studentPwdReset.js` |
|
||||
| 移动端首页 | 学工管理 →「重置学生密码」(`student:pwdreset:listapp`) |
|
||||
|
||||
---
|
||||
|
||||
## 验收要点
|
||||
|
||||
1. 有权限账号:学号查得到 → 重置成功 → 该学号可用默认密码登录
|
||||
2. 无权限账号:接口 403
|
||||
3. 不存在学号 / 无用户 / 非学生账号:友好错误,不改密
|
||||
4. Web、移动端流程一致:输入学号 → 查询 → 确认 → 重置
|
||||
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* 重置学生密码 API
|
||||
*/
|
||||
import { request } from '@/common/request.js';
|
||||
|
||||
/**
|
||||
* 按学号查询学生基本信息(重置密码页)
|
||||
* @param {string} studentNo 学号
|
||||
*/
|
||||
export const queryStudentForPwdReset = (studentNo) => {
|
||||
return request({
|
||||
url: `/studentMsg/studentMsg/queryByStudentNo/${encodeURIComponent(studentNo)}`,
|
||||
method: 'GET'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 按学号重置学生登录密码为固定默认值
|
||||
* @param {string} studentNo 学号
|
||||
*/
|
||||
export const resetStudentPassword = (studentNo) => {
|
||||
return request({
|
||||
url: `/studentMsg/studentMsg/resetStudentPassword/${encodeURIComponent(studentNo)}`,
|
||||
method: 'PUT'
|
||||
});
|
||||
};
|
||||
@@ -65,6 +65,14 @@
|
||||
"navigationBarTextStyle": "white"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/xg/xsjcxx/studentPwdReset/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "重置学生密码",
|
||||
"navigationBarBackgroundColor": "#0051d9",
|
||||
"navigationBarTextStyle": "white"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/xg/xsjcxx/xsws/index",
|
||||
"style": {
|
||||
|
||||
@@ -86,6 +86,15 @@
|
||||
/>
|
||||
</template>
|
||||
</ServiceItem>
|
||||
<ServiceItem
|
||||
v-if="hasPermi(['student:pwdreset:listapp'])"
|
||||
title="重置学生密码"
|
||||
@click="gotoStudentPwdReset"
|
||||
>
|
||||
<template #icon>
|
||||
<u-icon name="lock-open" size="24" color="#1890ff" />
|
||||
</template>
|
||||
</ServiceItem>
|
||||
<!-- 卡片结束 -->
|
||||
<!-- <ServiceItem
|
||||
v-if="hasPermi(['student:dorm:list'])"
|
||||
@@ -776,6 +785,7 @@ export default {
|
||||
hasStudentServices() {
|
||||
return (
|
||||
this.hasPermi(['student:info:listapp']) ||
|
||||
this.hasPermi(['student:pwdreset:listapp']) ||
|
||||
this.hasPermi(['student:dorm:list']) ||
|
||||
this.hasPermi(['student:leave:listapp']) ||
|
||||
this.hasPermi(['student:psychology:list']) ||
|
||||
@@ -1203,6 +1213,13 @@ export default {
|
||||
});
|
||||
},
|
||||
|
||||
// 跳转到重置学生密码页面
|
||||
gotoStudentPwdReset() {
|
||||
uni.navigateTo({
|
||||
url: '/pages/xg/xsjcxx/studentPwdReset/index',
|
||||
});
|
||||
},
|
||||
|
||||
// 跳转到假期留宿申请(学生)
|
||||
gotoHolidayStayApply() {
|
||||
uni.navigateTo({
|
||||
|
||||
@@ -0,0 +1,203 @@
|
||||
<template>
|
||||
<view class="pwd-reset-page">
|
||||
<view class="search-card">
|
||||
<view class="form-row">
|
||||
<text class="label">学号</text>
|
||||
<u-input
|
||||
v-model="studentNo"
|
||||
placeholder="请输入学生学号"
|
||||
clearable
|
||||
maxlength="32"
|
||||
border="surround"
|
||||
:custom-style="{ background: '#fff' }"
|
||||
@confirm="handleQuery"
|
||||
/>
|
||||
</view>
|
||||
<u-button
|
||||
type="primary"
|
||||
text="查询"
|
||||
:loading="queryLoading"
|
||||
:custom-style="{ marginTop: '24rpx' }"
|
||||
@click="handleQuery"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<view v-if="student" class="info-card">
|
||||
<view class="card-title">学生信息</view>
|
||||
<view class="info-item">
|
||||
<text class="info-label">姓名</text>
|
||||
<text class="info-value">{{ student.name || '-' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="info-label">学号</text>
|
||||
<text class="info-value">{{ student.studentNo || '-' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="info-label">班级</text>
|
||||
<text class="info-value">{{ student.className || '-' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="info-label">班主任</text>
|
||||
<text class="info-value">{{ student.classTeacherName || '-' }}</text>
|
||||
</view>
|
||||
<u-button
|
||||
type="error"
|
||||
text="重置密码"
|
||||
:loading="resetLoading"
|
||||
:custom-style="{ marginTop: '32rpx' }"
|
||||
@click="handleReset"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<view v-else-if="queried && !queryLoading" class="empty-tip">
|
||||
<u-empty mode="search" text="请输入学号后查询" />
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
queryStudentForPwdReset,
|
||||
resetStudentPassword
|
||||
} from '@/api/studentPwdReset.js';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
studentNo: '',
|
||||
student: null,
|
||||
queried: false,
|
||||
queryLoading: false,
|
||||
resetLoading: false
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
async handleQuery() {
|
||||
const no = (this.studentNo || '').trim();
|
||||
if (!no) {
|
||||
uni.showToast({ title: '请输入学号', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
this.queryLoading = true;
|
||||
this.queried = true;
|
||||
this.student = null;
|
||||
try {
|
||||
const res = await queryStudentForPwdReset(no);
|
||||
const payload =
|
||||
res && res.data && typeof res.data === 'object' && !Array.isArray(res.data)
|
||||
? res.data
|
||||
: res;
|
||||
if (!payload || (!payload.studentNo && !payload.name && payload.id == null)) {
|
||||
uni.showToast({
|
||||
title: (res && res.msg) || '未查询到学生信息',
|
||||
icon: 'none'
|
||||
});
|
||||
this.student = null;
|
||||
return;
|
||||
}
|
||||
this.student = payload;
|
||||
this.studentNo = no;
|
||||
} catch (e) {
|
||||
this.student = null;
|
||||
} finally {
|
||||
this.queryLoading = false;
|
||||
}
|
||||
},
|
||||
handleReset() {
|
||||
if (!this.student || !this.student.studentNo) {
|
||||
uni.showToast({ title: '请先查询学生', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
const name = this.student.name || '';
|
||||
const no = this.student.studentNo;
|
||||
uni.showModal({
|
||||
title: '重置密码确认',
|
||||
content: `确认将学号「${no}」(${name})的登录密码重置为系统默认密码?`,
|
||||
confirmText: '确定',
|
||||
cancelText: '取消',
|
||||
success: async (modalRes) => {
|
||||
if (!modalRes.confirm) {
|
||||
return;
|
||||
}
|
||||
this.resetLoading = true;
|
||||
try {
|
||||
await resetStudentPassword(no);
|
||||
uni.showToast({ title: '密码已重置为默认密码', icon: 'success' });
|
||||
} catch (e) {
|
||||
// 错误提示由 request 拦截器处理
|
||||
} finally {
|
||||
this.resetLoading = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.pwd-reset-page {
|
||||
min-height: 100vh;
|
||||
background: #f5f6f8;
|
||||
padding: 24rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.search-card,
|
||||
.info-card {
|
||||
background: #fff;
|
||||
border-radius: 16rpx;
|
||||
padding: 28rpx;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.form-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.form-row .label {
|
||||
width: 80rpx;
|
||||
flex-shrink: 0;
|
||||
font-size: 28rpx;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
.card-title {
|
||||
font-size: 30rpx;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.info-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
padding: 18rpx 0;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.info-item:last-of-type {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.info-label {
|
||||
font-size: 28rpx;
|
||||
color: #909399;
|
||||
flex-shrink: 0;
|
||||
margin-right: 24rpx;
|
||||
}
|
||||
|
||||
.info-value {
|
||||
font-size: 28rpx;
|
||||
color: #303133;
|
||||
text-align: right;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.empty-tip {
|
||||
padding-top: 80rpx;
|
||||
}
|
||||
</style>
|
||||
@@ -7,6 +7,7 @@
|
||||
const SERVICE_ROUTES = {
|
||||
// 学工管理
|
||||
'student:info': '/pages/xg/xsjcxx/xsjcxx/index',
|
||||
'student:pwdreset': '/pages/xg/xsjcxx/studentPwdReset/index',
|
||||
'student:dorm': '/pages/student/dorm/index',
|
||||
'student:leave': '/pages/xg/xsjcxx/xsqj/leave/index',
|
||||
'student:psychology': '/pages/student/psychology/index',
|
||||
@@ -48,6 +49,7 @@ const SERVICE_ROUTES = {
|
||||
// 服务名称映射
|
||||
const SERVICE_NAMES = {
|
||||
学生信息: 'student:info',
|
||||
重置学生密码: 'student:pwdreset',
|
||||
宿舍管理: 'student:dorm',
|
||||
学生请假: 'student:leave',
|
||||
心理健康: 'student:psychology',
|
||||
|
||||
@@ -117,3 +117,31 @@ export const getCollegeLeaders = (studentId: string | number): AxiosPromise<{ da
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
export interface StudentPwdResetVO {
|
||||
id?: string | number;
|
||||
studentNo?: string;
|
||||
name?: string;
|
||||
className?: string;
|
||||
classTeacherName?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 按学号查询学生(重置密码页)
|
||||
*/
|
||||
export const queryStudentForPwdReset = (studentNo: string): AxiosPromise<StudentPwdResetVO> => {
|
||||
return request({
|
||||
url: `/studentMsg/studentMsg/queryByStudentNo/${encodeURIComponent(studentNo)}`,
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 按学号重置学生密码为固定默认值
|
||||
*/
|
||||
export const resetStudentPassword = (studentNo: string) => {
|
||||
return request({
|
||||
url: `/studentMsg/studentMsg/resetStudentPassword/${encodeURIComponent(studentNo)}`,
|
||||
method: 'put'
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,130 @@
|
||||
<template>
|
||||
<div class="p-2">
|
||||
<el-card shadow="hover" class="mb-3">
|
||||
<el-form :inline="true" @submit.prevent>
|
||||
<el-form-item label="学号">
|
||||
<el-input
|
||||
v-model="studentNo"
|
||||
placeholder="请输入学生学号"
|
||||
clearable
|
||||
maxlength="32"
|
||||
style="width: 260px"
|
||||
@keyup.enter="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button
|
||||
v-hasPermi="['studentMsg:studentPwdReset:query']"
|
||||
type="primary"
|
||||
icon="Search"
|
||||
:loading="queryLoading"
|
||||
@click="handleQuery"
|
||||
>
|
||||
查询
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
|
||||
<el-card v-if="student" shadow="never" v-loading="queryLoading">
|
||||
<template #header>
|
||||
<span>学生信息</span>
|
||||
</template>
|
||||
<el-descriptions :column="2" border>
|
||||
<el-descriptions-item label="姓名">{{ student.name || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="学号">{{ student.studentNo || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="班级">{{ student.className || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="班主任">{{ student.classTeacherName || '-' }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
<div class="mt-4">
|
||||
<el-button
|
||||
v-hasPermi="['studentMsg:studentPwdReset:reset']"
|
||||
type="danger"
|
||||
icon="Key"
|
||||
:loading="resetLoading"
|
||||
@click="handleReset"
|
||||
>
|
||||
重置密码
|
||||
</el-button>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<el-empty v-else-if="queried && !queryLoading" description="请输入学号后查询" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="StudentPwdReset">
|
||||
import { ref } from 'vue';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import {
|
||||
queryStudentForPwdReset,
|
||||
resetStudentPassword,
|
||||
type StudentPwdResetVO
|
||||
} from '@/api/xg/xsjcxx/studentMsg/studentMsg';
|
||||
|
||||
const studentNo = ref('');
|
||||
const student = ref<StudentPwdResetVO | null>(null);
|
||||
const queried = ref(false);
|
||||
const queryLoading = ref(false);
|
||||
const resetLoading = ref(false);
|
||||
|
||||
const handleQuery = async () => {
|
||||
const no = studentNo.value?.trim();
|
||||
if (!no) {
|
||||
ElMessage.warning('请输入学号');
|
||||
return;
|
||||
}
|
||||
queryLoading.value = true;
|
||||
queried.value = true;
|
||||
student.value = null;
|
||||
try {
|
||||
const res: any = await queryStudentForPwdReset(no);
|
||||
// 拦截器返回 { code, msg, data },需取 data;兼容偶发直接返回业务对象
|
||||
const payload =
|
||||
res?.data && typeof res.data === 'object' && !Array.isArray(res.data) ? res.data : res;
|
||||
if (!payload?.studentNo && !payload?.name && payload?.id == null) {
|
||||
ElMessage.warning(res?.msg || '未查询到学生信息,请确认后端已部署最新接口');
|
||||
student.value = null;
|
||||
return;
|
||||
}
|
||||
student.value = payload as StudentPwdResetVO;
|
||||
studentNo.value = no;
|
||||
} catch (e) {
|
||||
student.value = null;
|
||||
} finally {
|
||||
queryLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const handleReset = async () => {
|
||||
if (!student.value?.studentNo) {
|
||||
ElMessage.warning('请先查询学生');
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await ElMessageBox.confirm(
|
||||
`确认将学号「${student.value.studentNo}」(${student.value.name || ''})的登录密码重置为系统默认密码?`,
|
||||
'重置密码确认',
|
||||
{ type: 'warning', confirmButtonText: '确定', cancelButtonText: '取消' }
|
||||
);
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
resetLoading.value = true;
|
||||
try {
|
||||
await resetStudentPassword(student.value.studentNo);
|
||||
ElMessage.success('密码已重置为默认密码');
|
||||
} finally {
|
||||
resetLoading.value = false;
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.mt-4 {
|
||||
margin-top: 16px;
|
||||
}
|
||||
.mb-3 {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
</style>
|
||||
Reference in new issue
Block a user