Merge branch 'master' of http://47.99.164.66:3000/hzdyjs-xg/all-project
No files matched your search
@@ -325,3 +325,4 @@ docs/变更管理/
|
||||
.playwright-mcp/
|
||||
查找界面中涉及到的权限字符.md
|
||||
docs/部署文档/nginx配置/hzdyjsweb.camasort.com.conf
|
||||
首页完善的提示词.md
|
||||
@@ -31,7 +31,7 @@
|
||||
<lock4j.version>2.2.7</lock4j.version>
|
||||
<dynamic-ds.version>4.3.1</dynamic-ds.version>
|
||||
<snailjob.version>1.5.0</snailjob.version>
|
||||
<mapstruct-plus.version>1.4.8</mapstruct-plus.version>
|
||||
<mapstruct-plus.version>1.5.0</mapstruct-plus.version>
|
||||
<mapstruct-plus.lombok.version>0.2.0</mapstruct-plus.lombok.version>
|
||||
<lombok.version>1.18.36</lombok.version>
|
||||
<bouncycastle.version>1.80</bouncycastle.version>
|
||||
|
||||
@@ -157,11 +157,11 @@
|
||||
</dependency>
|
||||
|
||||
<!-- 社团管理模块 -->
|
||||
<!-- <dependency>
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-stgl</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency> -->
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>de.codecentric</groupId>
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
<module>ruoyi-jcbt</module>
|
||||
<module>ruoyi-bzrkh</module>
|
||||
<module>ruoyi-xskh</module>
|
||||
<!-- <module>ruoyi-stgl</module> -->
|
||||
<module>ruoyi-stgl</module>
|
||||
</modules>
|
||||
|
||||
<artifactId>ruoyi-modules</artifactId>
|
||||
|
||||
@@ -22,6 +22,7 @@ import org.dromara.stgl.domain.bo.StglActivityAttendanceBo;
|
||||
import org.dromara.stgl.service.IStglActivityAttendanceService;
|
||||
import org.dromara.common.mybatis.core.page.TableDataInfo;
|
||||
import org.dromara.common.satoken.utils.LoginHelper;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
/**
|
||||
* 活动考勤签到
|
||||
@@ -120,6 +121,28 @@ public class StglActivityAttendanceController extends BaseController {
|
||||
return R.ok(stglActivityAttendanceService.queryByActivityAndUser(activityId, userId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据活动ID和用户ID查询所有签到记录(支持多次签到)
|
||||
*/
|
||||
@SaCheckPermission("stgl:activityAttendance:query")
|
||||
@GetMapping("/getAllByActivityAndUser")
|
||||
public R<List<StglActivityAttendanceVo>> getAllByActivityAndUser(
|
||||
@RequestParam("activityId") Long activityId,
|
||||
@RequestParam(value = "userId", required = false) Long userId,
|
||||
@RequestParam(value = "studentNo", required = false) String studentNo) {
|
||||
|
||||
List<StglActivityAttendanceVo> list;
|
||||
if (userId != null) {
|
||||
list = stglActivityAttendanceService.queryAllByActivityAndUser(activityId, userId);
|
||||
} else if (StringUtils.isNotBlank(studentNo)) {
|
||||
list = stglActivityAttendanceService.queryAllByActivityAndStudentNo(activityId, studentNo);
|
||||
} else {
|
||||
return R.fail("请提供用户ID或学号");
|
||||
}
|
||||
|
||||
return R.ok(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户签到
|
||||
*/
|
||||
@@ -128,8 +151,7 @@ public class StglActivityAttendanceController extends BaseController {
|
||||
@RepeatSubmit()
|
||||
@PostMapping("/signIn")
|
||||
public R<Void> signIn(@Validated @RequestBody StglActivityAttendanceBo bo) {
|
||||
// 设置当前登录用户ID
|
||||
bo.setUserId(LoginHelper.getUserId());
|
||||
// 移除重复设置userId,让Service层通过学号查找用户ID
|
||||
return toAjax(stglActivityAttendanceService.signIn(bo));
|
||||
}
|
||||
|
||||
@@ -141,8 +163,7 @@ public class StglActivityAttendanceController extends BaseController {
|
||||
@RepeatSubmit()
|
||||
@PostMapping("/signOut")
|
||||
public R<Void> signOut(@Validated @RequestBody StglActivityAttendanceBo bo) {
|
||||
// 设置当前登录用户ID
|
||||
bo.setUserId(LoginHelper.getUserId());
|
||||
// 移除重复设置userId,让Service层通过学号查找用户ID
|
||||
return toAjax(stglActivityAttendanceService.signOut(bo));
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,7 @@ import org.dromara.common.mybatis.core.page.PageQuery;
|
||||
import org.dromara.common.mybatis.core.page.TableDataInfo;
|
||||
import org.dromara.common.satoken.utils.LoginHelper;
|
||||
import org.dromara.common.web.core.BaseController;
|
||||
import org.dromara.stgl.domain.bo.BatchApprovalBo;
|
||||
import org.dromara.stgl.domain.bo.StglBatchApprovalBo;
|
||||
import org.dromara.stgl.domain.bo.StglActivityRegistrationBo;
|
||||
import org.dromara.stgl.domain.vo.StglActivityRegistrationVo;
|
||||
import org.dromara.stgl.service.IStglActivityRegistrationService;
|
||||
@@ -120,7 +120,7 @@ public class StglActivityRegisterController extends BaseController {
|
||||
@SaCheckPermission("stgl:activityRegister:approve")
|
||||
@Log(title = "活动报名批量审核", businessType = BusinessType.UPDATE)
|
||||
@PostMapping("/batchApprove")
|
||||
public R<Void> batchApprove(@Validated @RequestBody BatchApprovalBo batchApprovalBo) {
|
||||
public R<Void> batchApprove(@Validated @RequestBody StglBatchApprovalBo batchApprovalBo) {
|
||||
return toAjax(stglActivityRegistrationService.batchApprove(batchApprovalBo.getIds(), batchApprovalBo.getApprovalStatus(), batchApprovalBo.getApprovalRemark(), LoginHelper.getUserId()));
|
||||
}
|
||||
|
||||
|
||||
@@ -228,6 +228,21 @@ public class StglClubController extends BaseController {
|
||||
return toAjax(stglClubService.changeStatus(id, status));
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改社团报名开启状态
|
||||
*/
|
||||
@Operation(summary = "修改社团报名开启状态")
|
||||
@SaCheckPermission("stgl:club:edit")
|
||||
@Log(title = "社团信息", businessType = BusinessType.UPDATE)
|
||||
@PutMapping("/changeSighOpenStatus")
|
||||
@Parameters({
|
||||
@Parameter(name = "id", description = "社团ID", required = true),
|
||||
@Parameter(name = "sighOpen", description = "报名开启状态(Y/N)", required = true)
|
||||
})
|
||||
public R<Void> changeSighOpenStatus(@RequestParam Long id, @RequestParam String sighOpen) {
|
||||
return toAjax(stglClubService.changeSighOpenStatus(id, sighOpen));
|
||||
}
|
||||
|
||||
/**
|
||||
* 修复已存在社团的成员记录
|
||||
* 为缺失社长、副社长成员记录的社团补充相应记录
|
||||
|
||||
@@ -92,7 +92,7 @@ public class StglVolunteerMemberController extends BaseController {
|
||||
bo.setMemberType(2); // 志愿者成员
|
||||
// 如果前端没有传递roleType,设置默认值为1(队员)
|
||||
if (bo.getRoleType() == null) {
|
||||
bo.setRoleType(1);
|
||||
bo.setRoleType("1");
|
||||
}
|
||||
return toAjax(stglMemberService.insertByBo(bo));
|
||||
}
|
||||
|
||||
@@ -302,4 +302,14 @@ public class StglVolunteerTeamController extends BaseController {
|
||||
|
||||
return toAjax(stglVolunteerTeamService.transferVolunteerTeam(id, newLeaderUserId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改志愿者团队报名开启状态
|
||||
*/
|
||||
@SaCheckPermission("stgl:volunteerTeam:edit")
|
||||
@Log(title = "志愿者团队报名状态变更", businessType = BusinessType.UPDATE)
|
||||
@PutMapping("/changeSighOpenStatus")
|
||||
public R<Void> changeSighOpenStatus(@RequestParam Long id, @RequestParam String sighOpen) {
|
||||
return toAjax(stglVolunteerTeamService.changeSighOpenStatus(id, sighOpen));
|
||||
}
|
||||
}
|
||||
@@ -126,4 +126,10 @@ public class StglActivityAttendance extends TenantEntity {
|
||||
* 签到状态(0-未签到 1-已签到 2-已签退)
|
||||
*/
|
||||
private Integer overallStatus;
|
||||
|
||||
/**
|
||||
* 删除标志(0代表存在 1代表删除)
|
||||
*/
|
||||
@TableLogic(value = "0", delval = "1")
|
||||
private Integer deleted;
|
||||
}
|
||||
@@ -192,6 +192,11 @@ public class StglClub extends TenantEntity {
|
||||
*/
|
||||
private Integer status;
|
||||
|
||||
/**
|
||||
* 开启报名(Y是 N否)
|
||||
*/
|
||||
private String sighOpen;
|
||||
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
|
||||
@@ -53,9 +53,9 @@ public class StglMember extends TenantEntity {
|
||||
private String studentName;
|
||||
|
||||
/**
|
||||
* 角色类型(1-社长 2-副社长 3-部长 4-普通成员)
|
||||
* 角色类型(社长、副社长、部长、普通成员)
|
||||
*/
|
||||
private Integer roleType;
|
||||
private String roleType;
|
||||
|
||||
/**
|
||||
* 加入时间
|
||||
|
||||
@@ -127,4 +127,9 @@ public class StglVolunteerTeam extends TenantEntity {
|
||||
*/
|
||||
private Integer status;
|
||||
|
||||
/**
|
||||
* 报名开启状态(Y-开启 N-关闭)
|
||||
*/
|
||||
private String sighOpen;
|
||||
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
package org.dromara.stgl.domain.bo;
|
||||
|
||||
import lombok.Data;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Min;
|
||||
import jakarta.validation.constraints.Max;
|
||||
import jakarta.validation.constraints.Size;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 批量审核请求对象
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2025-01-31
|
||||
*/
|
||||
@Data
|
||||
public class BatchApprovalBo {
|
||||
|
||||
/**
|
||||
* 报名ID数组
|
||||
*/
|
||||
@NotEmpty(message = "审核ID列表不能为空")
|
||||
private List<Long> ids;
|
||||
|
||||
/**
|
||||
* 审核状态 1-通过 2-驳回
|
||||
*/
|
||||
@NotNull(message = "审核状态不能为空")
|
||||
@Min(value = 1, message = "审核状态值无效")
|
||||
@Max(value = 2, message = "审核状态值无效")
|
||||
private Integer approvalStatus;
|
||||
|
||||
/**
|
||||
* 审核意见
|
||||
*/
|
||||
@Size(max = 500, message = "审核意见不能超过500个字符")
|
||||
private String approvalRemark;
|
||||
}
|
||||
@@ -230,6 +230,11 @@ public class StglClubBo extends BaseEntity {
|
||||
*/
|
||||
private Integer status;
|
||||
|
||||
/**
|
||||
* 开启报名(Y是 N否)
|
||||
*/
|
||||
private String sighOpen;
|
||||
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
|
||||
@@ -64,9 +64,7 @@ public class StglMemberBo extends BaseEntity {
|
||||
/**
|
||||
* 角色类型(社团:1-社长 2-副社长 3-部长 4-普通成员;志愿者:0-队长 1-队员)
|
||||
*/
|
||||
@Min(value = 0, message = "角色类型值无效")
|
||||
@Max(value = 4, message = "角色类型值无效")
|
||||
private Integer roleType;
|
||||
private String roleType;
|
||||
|
||||
/**
|
||||
* 加入时间
|
||||
@@ -76,7 +74,7 @@ public class StglMemberBo extends BaseEntity {
|
||||
/**
|
||||
* 状态(0-正常 1-停用)
|
||||
*/
|
||||
private Integer status;
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 备注
|
||||
|
||||
@@ -150,4 +150,10 @@ public class StglVolunteerTeamBo extends BaseEntity {
|
||||
*/
|
||||
private Integer status;
|
||||
|
||||
/**
|
||||
* 报名开启状态(Y-开启 N-关闭)
|
||||
*/
|
||||
@Pattern(regexp = "^[YN]$", message = "报名开启状态只能为Y或N")
|
||||
private String sighOpen;
|
||||
|
||||
}
|
||||
@@ -238,4 +238,9 @@ public class StglClubRegisterVo implements Serializable {
|
||||
*/
|
||||
private String remark;
|
||||
|
||||
/**
|
||||
* 开启报名(Y是 N否)
|
||||
*/
|
||||
private String sighOpen;
|
||||
|
||||
}
|
||||
@@ -226,6 +226,12 @@ public class StglClubVo implements Serializable {
|
||||
@ExcelProperty(value = "状态")
|
||||
private Integer status;
|
||||
|
||||
/**
|
||||
* 开启报名(Y是 N否)
|
||||
*/
|
||||
@ExcelProperty(value = "开启报名")
|
||||
private String sighOpen;
|
||||
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
|
||||
@@ -112,7 +112,7 @@ public class StglMemberVo implements Serializable {
|
||||
/**
|
||||
* 角色类型(1-社长 2-副社长 3-成员)
|
||||
*/
|
||||
private Integer roleType;
|
||||
private String roleType;
|
||||
|
||||
/**
|
||||
* 角色类型名称
|
||||
@@ -130,7 +130,7 @@ public class StglMemberVo implements Serializable {
|
||||
/**
|
||||
* 状态(0-正常 1-停用)
|
||||
*/
|
||||
private Integer status;
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 状态名称
|
||||
|
||||
@@ -149,6 +149,12 @@ public class StglVolunteerTeamVo implements Serializable {
|
||||
@ExcelProperty(value = "状态")
|
||||
private Integer status;
|
||||
|
||||
/**
|
||||
* 报名开启状态(Y-开启 N-关闭)
|
||||
*/
|
||||
@ExcelProperty(value = "报名开启状态")
|
||||
private String sighOpen;
|
||||
|
||||
/**
|
||||
* 创建者
|
||||
*/
|
||||
|
||||
@@ -273,10 +273,10 @@ public class StglMemberImportListener extends AnalysisEventListener<StglMemberIm
|
||||
member.setMemberType(1); // 社团成员
|
||||
}
|
||||
if (member.getRoleType() == null) {
|
||||
member.setRoleType(4); // 普通成员
|
||||
member.setRoleType("4"); // 普通成员
|
||||
}
|
||||
if (member.getStatus() == null) {
|
||||
member.setStatus(2); // 已加入
|
||||
member.setStatus("2"); // 已加入
|
||||
}
|
||||
// 🔑 关键验证:确保userId不为空
|
||||
if (member.getUserId() == null) {
|
||||
|
||||
@@ -238,8 +238,8 @@ public class StglVolunteerImportListener extends AnalysisEventListener<StglVolun
|
||||
newMember.setStudentName(volunteerVo.getStudentName());
|
||||
// 🔑 关键修复:设置用户ID(学生ID就是用户ID)
|
||||
newMember.setUserId(student.getId());
|
||||
newMember.setRoleType(1); // 志愿者角色 stgl_volunteer_role=1 队员
|
||||
newMember.setStatus(2); // 成员状态 stgl_member_status=2 已加入
|
||||
newMember.setRoleType("1"); // 志愿者角色 stgl_volunteer_role=1 队员
|
||||
newMember.setStatus("2"); // 成员状态 stgl_member_status=2 已加入
|
||||
newMember.setJoinTime(volunteerVo.getJoinTime() != null ? volunteerVo.getJoinTime() : new Date());
|
||||
|
||||
newMember.setCreateBy(operUserId);
|
||||
|
||||
@@ -39,6 +39,15 @@ public interface StglActivityAttendanceMapper extends BaseMapperPlus<StglActivit
|
||||
*/
|
||||
StglActivityAttendanceVo selectByActivityAndUser(@Param("activityId") Long activityId, @Param("userId") Long userId);
|
||||
|
||||
/**
|
||||
* 根据活动ID和用户ID查询所有签到记录(支持多次签到)
|
||||
*
|
||||
* @param activityId 活动ID
|
||||
* @param userId 用户ID
|
||||
* @return 签到记录列表
|
||||
*/
|
||||
List<StglActivityAttendanceVo> selectAllByActivityAndUser(@Param("activityId") Long activityId, @Param("userId") Long userId);
|
||||
|
||||
/**
|
||||
* 根据ID查询活动考勤签到详情(关联查询活动信息)
|
||||
*
|
||||
|
||||
@@ -36,17 +36,19 @@ public interface StglMemberMapper extends BaseMapperPlus<StglMember, StglMemberV
|
||||
* @param clubName 社团名称(用于社团成员查询)
|
||||
* @param teamName 队伍名称(用于志愿者查询)
|
||||
* @param userId 用户ID(用于权限过滤)
|
||||
* @param params 其他查询参数(包含statusList等)
|
||||
* @return 成员列表
|
||||
*/
|
||||
List<StglMemberVo> selectMemberWithUserInfo(@Param("memberType") Integer memberType,
|
||||
@Param("relationId") Long relationId,
|
||||
@Param("studentNo") String studentNo,
|
||||
@Param("studentName") String studentName,
|
||||
@Param("roleType") Integer roleType,
|
||||
@Param("status") Integer status,
|
||||
@Param("roleType") String roleType,
|
||||
@Param("status") String status,
|
||||
@Param("clubName") String clubName,
|
||||
@Param("teamName") String teamName,
|
||||
@Param("userId") Long userId);
|
||||
@Param("userId") Long userId,
|
||||
@Param("params") java.util.Map<String, Object> params);
|
||||
|
||||
/**
|
||||
* 检查用户是否已加入某个社团/志愿者队伍
|
||||
|
||||
@@ -56,6 +56,16 @@ public interface IStglActivityAttendanceService {
|
||||
*/
|
||||
StglActivityAttendanceVo queryByActivityAndUser(Long activityId, Long userId);
|
||||
|
||||
/**
|
||||
* 根据活动ID和用户ID查询所有签到记录(支持多次签到)
|
||||
*/
|
||||
List<StglActivityAttendanceVo> queryAllByActivityAndUser(Long activityId, Long userId);
|
||||
|
||||
/**
|
||||
* 根据活动ID和学号查询所有签到记录(支持多次签到)
|
||||
*/
|
||||
List<StglActivityAttendanceVo> queryAllByActivityAndStudentNo(Long activityId, String studentNo);
|
||||
|
||||
/**
|
||||
* 用户签到
|
||||
*/
|
||||
|
||||
@@ -106,6 +106,15 @@ public interface IStglClubService {
|
||||
*/
|
||||
Boolean changeStatus(Long id, Integer status);
|
||||
|
||||
/**
|
||||
* 修改社团报名开启状态
|
||||
*
|
||||
* @param id 社团ID
|
||||
* @param sighOpen 报名开启状态(Y/N)
|
||||
* @return 是否操作成功
|
||||
*/
|
||||
Boolean changeSighOpenStatus(Long id, String sighOpen);
|
||||
|
||||
/**
|
||||
* 修复所有已存在社团的成员记录
|
||||
* 为那些没有对应成员记录的社长和副社长补充成员记录
|
||||
|
||||
@@ -130,4 +130,13 @@ public interface IStglVolunteerTeamService {
|
||||
*/
|
||||
Integer fixExistingTeamLeaderRecords();
|
||||
|
||||
/**
|
||||
* 修改志愿者团队报名开启状态
|
||||
*
|
||||
* @param id 团队ID
|
||||
* @param sighOpen 报名开启状态(Y/N)
|
||||
* @return 是否修改成功
|
||||
*/
|
||||
Boolean changeSighOpenStatus(Long id, String sighOpen);
|
||||
|
||||
}
|
||||
@@ -15,11 +15,14 @@ import org.dromara.stgl.domain.vo.StglActivityAttendanceVo;
|
||||
import org.dromara.stgl.domain.StglActivityAttendance;
|
||||
import org.dromara.stgl.mapper.StglActivityAttendanceMapper;
|
||||
import org.dromara.stgl.service.IStglActivityAttendanceService;
|
||||
import org.dromara.system.domain.SysUser;
|
||||
import org.dromara.system.mapper.SysUserMapper;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* 活动考勤签到Service业务层处理
|
||||
@@ -32,6 +35,7 @@ import java.util.Date;
|
||||
public class StglActivityAttendanceServiceImpl implements IStglActivityAttendanceService {
|
||||
|
||||
private final StglActivityAttendanceMapper baseMapper;
|
||||
private final SysUserMapper sysUserMapper;
|
||||
|
||||
/**
|
||||
* 查询活动考勤签到
|
||||
@@ -141,48 +145,134 @@ public class StglActivityAttendanceServiceImpl implements IStglActivityAttendanc
|
||||
return baseMapper.selectByActivityAndUser(activityId, userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据活动ID和用户ID查询所有签到记录(支持多次签到)
|
||||
*/
|
||||
@Override
|
||||
public List<StglActivityAttendanceVo> queryAllByActivityAndUser(Long activityId, Long userId) {
|
||||
return baseMapper.selectAllByActivityAndUser(activityId, userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据活动ID和学号查询所有签到记录(支持多次签到)
|
||||
*/
|
||||
@Override
|
||||
public List<StglActivityAttendanceVo> queryAllByActivityAndStudentNo(Long activityId, String studentNo) {
|
||||
// 先通过学号获取用户ID
|
||||
Long userId = getUserIdByStudentNo(studentNo);
|
||||
if (userId == null) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
return baseMapper.selectAllByActivityAndUser(activityId, userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户签到
|
||||
*/
|
||||
@Override
|
||||
public Boolean signIn(StglActivityAttendanceBo bo) {
|
||||
// 检查是否已存在签到记录
|
||||
StglActivityAttendanceVo existingRecord = queryByActivityAndUser(bo.getActivityId(), bo.getUserId());
|
||||
// 通过学号查找用户ID
|
||||
Long userId = getUserIdByStudentNo(bo.getStudentNo());
|
||||
if (userId == null) {
|
||||
throw new RuntimeException("未找到对应的用户信息");
|
||||
}
|
||||
|
||||
if (existingRecord != null) {
|
||||
// 如果已存在记录,更新签到信息
|
||||
bo.setId(existingRecord.getId());
|
||||
bo.setSignInTime(new Date());
|
||||
bo.setSignInStatus(1);
|
||||
bo.setOverallStatus(1); // 已签到
|
||||
return updateByBo(bo);
|
||||
} else {
|
||||
// 如果不存在记录,新增签到记录
|
||||
// 移除重复签到检查,允许每次签到都生成新记录
|
||||
// 无论之前是否有签到记录,都创建新的签到记录
|
||||
bo.setUserId(userId);
|
||||
bo.setSignInTime(new Date());
|
||||
bo.setSignInStatus(1);
|
||||
bo.setSignOutStatus(0);
|
||||
bo.setOverallStatus(1); // 已签到
|
||||
|
||||
// 设置基础字段
|
||||
bo.setCreateTime(new Date());
|
||||
bo.setUpdateTime(new Date());
|
||||
|
||||
return insertByBo(bo);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户签退
|
||||
*/
|
||||
@Override
|
||||
public Boolean signOut(StglActivityAttendanceBo bo) {
|
||||
// 必须先签到才能签退
|
||||
StglActivityAttendanceVo existingRecord = queryByActivityAndUser(bo.getActivityId(), bo.getUserId());
|
||||
// 通过学号查找用户ID
|
||||
Long userId = getUserIdByStudentNo(bo.getStudentNo());
|
||||
if (userId == null) {
|
||||
throw new RuntimeException("未找到对应的用户信息");
|
||||
}
|
||||
|
||||
if (existingRecord == null || existingRecord.getSignInStatus() != 1) {
|
||||
throw new RuntimeException("请先完成签到");
|
||||
// 如果提供了记录ID,直接更新该记录
|
||||
if (bo.getId() != null) {
|
||||
StglActivityAttendance record = baseMapper.selectById(bo.getId());
|
||||
if (record == null) {
|
||||
throw new RuntimeException("没有找到指定的签到记录");
|
||||
}
|
||||
|
||||
// 检查是否可以签退
|
||||
if (record.getSignInStatus() != 1) {
|
||||
throw new RuntimeException("您还未完成签到,不能签退");
|
||||
}
|
||||
|
||||
if (record.getSignOutStatus() == 1) {
|
||||
throw new RuntimeException("您已经完成签退,无需重复签退");
|
||||
}
|
||||
|
||||
// 更新签退信息
|
||||
bo.setId(existingRecord.getId());
|
||||
bo.setUserId(userId);
|
||||
bo.setSignOutTime(new Date());
|
||||
bo.setSignOutStatus(1);
|
||||
bo.setOverallStatus(2); // 已签退
|
||||
return updateByBo(bo);
|
||||
} else {
|
||||
// 如果没有提供记录ID,查找最新的未完成签到记录
|
||||
LambdaQueryWrapper<StglActivityAttendance> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(StglActivityAttendance::getActivityId, bo.getActivityId());
|
||||
queryWrapper.eq(StglActivityAttendance::getUserId, userId);
|
||||
queryWrapper.orderByDesc(StglActivityAttendance::getCreateTime);
|
||||
queryWrapper.last("LIMIT 1");
|
||||
|
||||
StglActivityAttendance record = baseMapper.selectOne(queryWrapper);
|
||||
|
||||
if (record == null) {
|
||||
throw new RuntimeException("没有找到签到记录,请先完成签到");
|
||||
}
|
||||
|
||||
// 检查是否可以签退
|
||||
if (record.getSignInStatus() != 1) {
|
||||
throw new RuntimeException("您还未完成签到,不能签退");
|
||||
}
|
||||
|
||||
if (record.getSignOutStatus() == 1) {
|
||||
throw new RuntimeException("您已经完成签退,无需重复签退");
|
||||
}
|
||||
|
||||
// 更新签退信息
|
||||
bo.setId(record.getId());
|
||||
bo.setUserId(userId);
|
||||
bo.setSignOutTime(new Date());
|
||||
bo.setSignOutStatus(1);
|
||||
bo.setOverallStatus(2); // 已签退
|
||||
return updateByBo(bo);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据学号获取用户ID
|
||||
*/
|
||||
private Long getUserIdByStudentNo(String studentNo) {
|
||||
if (StringUtils.isBlank(studentNo)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 这里需要根据实际的用户表结构来查询
|
||||
// 假设用户表是sys_user,学号字段是user_name
|
||||
LambdaQueryWrapper<SysUser> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(SysUser::getUserName, studentNo);
|
||||
wrapper.eq(SysUser::getDelFlag, "0");
|
||||
|
||||
SysUser user = sysUserMapper.selectOne(wrapper);
|
||||
return user != null ? user.getUserId() : null;
|
||||
}
|
||||
}
|
||||
@@ -21,16 +21,27 @@ import org.dromara.stgl.domain.bo.StglActivityBo;
|
||||
import org.dromara.stgl.domain.vo.StglActivityVo;
|
||||
import org.dromara.stgl.mapper.StglActivityMapper;
|
||||
import org.dromara.stgl.service.IStglActivityService;
|
||||
import org.dromara.stgl.utils.AdvisorRoleAssigner;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.stream.Collectors;
|
||||
import org.dromara.common.json.utils.JsonUtils;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import org.dromara.stgl.domain.StglClub;
|
||||
import org.dromara.stgl.domain.StglVolunteerTeam;
|
||||
import org.dromara.stgl.mapper.StglClubMapper;
|
||||
import org.dromara.stgl.mapper.StglVolunteerTeamMapper;
|
||||
|
||||
/**
|
||||
* 活动信息Service业务层处理
|
||||
@@ -38,11 +49,15 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||
* @author ruoyi
|
||||
* @date 2025-01-07
|
||||
*/
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
@Service
|
||||
public class StglActivityServiceImpl extends ServiceImpl<StglActivityMapper, StglActivity> implements IStglActivityService {
|
||||
|
||||
private final StglActivityMapper baseMapper;
|
||||
private final AdvisorRoleAssigner advisorRoleAssigner;
|
||||
private final StglClubMapper stglClubMapper;
|
||||
private final StglVolunteerTeamMapper stglVolunteerTeamMapper;
|
||||
|
||||
/**
|
||||
* 查询活动信息
|
||||
@@ -57,6 +72,9 @@ public class StglActivityServiceImpl extends ServiceImpl<StglActivityMapper, Stg
|
||||
*/
|
||||
@Override
|
||||
public TableDataInfo<StglActivityVo> queryPageList(StglActivityBo bo, PageQuery pageQuery) {
|
||||
// 应用数据权限控制
|
||||
applyDataScope(bo);
|
||||
|
||||
// 转换为实体对象,使用自定义SQL查询(包含关联信息)
|
||||
StglActivity activity = BeanUtil.toBean(bo, StglActivity.class);
|
||||
Page<StglActivityVo> result = baseMapper.selectVoPage(pageQuery.build(), activity);
|
||||
@@ -68,6 +86,9 @@ public class StglActivityServiceImpl extends ServiceImpl<StglActivityMapper, Stg
|
||||
*/
|
||||
@Override
|
||||
public List<StglActivityVo> queryList(StglActivityBo bo) {
|
||||
// 应用数据权限控制
|
||||
applyDataScope(bo);
|
||||
|
||||
// 转换为实体对象,使用自定义SQL查询(包含关联信息)
|
||||
StglActivity activity = BeanUtil.toBean(bo, StglActivity.class);
|
||||
List<StglActivityVo> list = baseMapper.selectVoList(activity);
|
||||
@@ -215,6 +236,8 @@ public class StglActivityServiceImpl extends ServiceImpl<StglActivityMapper, Stg
|
||||
boolean flag = baseMapper.insert(add) > 0;
|
||||
if (flag) {
|
||||
bo.setId(add.getId());
|
||||
// 为指导老师分配角色
|
||||
assignAdvisorRoles(add);
|
||||
}
|
||||
return flag;
|
||||
}
|
||||
@@ -227,7 +250,21 @@ public class StglActivityServiceImpl extends ServiceImpl<StglActivityMapper, Stg
|
||||
public Boolean updateByBo(StglActivityBo bo) {
|
||||
StglActivity update = MapstructUtils.convert(bo, StglActivity.class);
|
||||
validEntityBeforeSave(update);
|
||||
return baseMapper.updateById(update) > 0;
|
||||
|
||||
// 获取更新前的活动信息,用于比较指导老师变化
|
||||
StglActivity oldActivity = null;
|
||||
if (update.getId() != null) {
|
||||
oldActivity = baseMapper.selectById(update.getId());
|
||||
}
|
||||
|
||||
boolean updateResult = baseMapper.updateById(update) > 0;
|
||||
|
||||
if (updateResult) {
|
||||
// 处理指导老师角色分配
|
||||
handleAdvisorRoleAssignment(oldActivity, update);
|
||||
}
|
||||
|
||||
return updateResult;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -382,4 +419,219 @@ public class StglActivityServiceImpl extends ServiceImpl<StglActivityMapper, Stg
|
||||
return baseMapper.update(null, wrapper) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 为指导老师分配角色
|
||||
*
|
||||
* @param activity 活动信息
|
||||
*/
|
||||
private void assignAdvisorRoles(StglActivity activity) {
|
||||
try {
|
||||
List<Long> advisorUserIds = extractAdvisorUserIds(activity);
|
||||
if (!advisorUserIds.isEmpty()) {
|
||||
advisorRoleAssigner.assignAdvisorRole(advisorUserIds);
|
||||
log.info("成功为活动 {} 的指导老师分配角色,指导老师数量: {}", activity.getActivityName(), advisorUserIds.size());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("为活动 {} 的指导老师分配角色时发生异常: {}", activity.getActivityName(), e.getMessage());
|
||||
// 不抛出异常,避免影响主业务流程
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理指导老师角色分配(更新时使用)
|
||||
*
|
||||
* @param oldActivity 更新前的活动信息
|
||||
* @param newActivity 更新后的活动信息
|
||||
*/
|
||||
private void handleAdvisorRoleAssignment(StglActivity oldActivity, StglActivity newActivity) {
|
||||
try {
|
||||
// 获取更新前后的指导老师用户ID列表
|
||||
List<Long> oldAdvisorUserIds = oldActivity != null ? extractAdvisorUserIds(oldActivity) : new ArrayList<>();
|
||||
List<Long> newAdvisorUserIds = extractAdvisorUserIds(newActivity);
|
||||
|
||||
// 找出新增的指导老师
|
||||
List<Long> addedAdvisorUserIds = newAdvisorUserIds.stream()
|
||||
.filter(id -> !oldAdvisorUserIds.contains(id))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// 找出移除的指导老师
|
||||
List<Long> removedAdvisorUserIds = oldAdvisorUserIds.stream()
|
||||
.filter(id -> !newAdvisorUserIds.contains(id))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// 为新指导老师分配角色
|
||||
if (!addedAdvisorUserIds.isEmpty()) {
|
||||
advisorRoleAssigner.assignAdvisorRole(addedAdvisorUserIds);
|
||||
log.info("活动 {} 新增指导老师,成功分配角色,数量: {}", newActivity.getActivityName(), addedAdvisorUserIds.size());
|
||||
}
|
||||
|
||||
// 移除旧指导老师的角色
|
||||
if (!removedAdvisorUserIds.isEmpty()) {
|
||||
advisorRoleAssigner.removeAdvisorRoles(removedAdvisorUserIds);
|
||||
log.info("活动 {} 移除指导老师,成功移除角色,数量: {}", newActivity.getActivityName(), removedAdvisorUserIds.size());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("处理活动 {} 的指导老师角色分配时发生异常: {}", newActivity.getActivityName(), e.getMessage());
|
||||
// 不抛出异常,避免影响主业务流程
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从活动信息中提取指导老师用户ID列表
|
||||
*
|
||||
* @param activity 活动信息
|
||||
* @return 指导老师用户ID列表
|
||||
*/
|
||||
private List<Long> extractAdvisorUserIds(StglActivity activity) {
|
||||
List<Long> advisorUserIds = new ArrayList<>();
|
||||
|
||||
try {
|
||||
// 从JSON字段读取指导老师数据
|
||||
if (StringUtils.isNotBlank(activity.getInstructors())) {
|
||||
List<Map<String, Object>> instructors = JsonUtils.parseObject(activity.getInstructors(), new TypeReference<List<Map<String, Object>>>() {});
|
||||
for (Map<String, Object> instructor : instructors) {
|
||||
// 修复:支持嵌套的JSON结构,从teacher子对象中提取userId
|
||||
Object userIdObj = null;
|
||||
|
||||
// 尝试直接从instructor对象获取userId(兼容旧格式)
|
||||
if (instructor.containsKey("userId")) {
|
||||
userIdObj = instructor.get("userId");
|
||||
}
|
||||
// 尝试从teacher子对象获取userId(新格式)
|
||||
else if (instructor.containsKey("teacher")) {
|
||||
Object teacherObj = instructor.get("teacher");
|
||||
if (teacherObj instanceof Map) {
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> teacher = (Map<String, Object>) teacherObj;
|
||||
userIdObj = teacher.get("userId");
|
||||
}
|
||||
}
|
||||
|
||||
if (userIdObj != null) {
|
||||
Long userId = Long.valueOf(userIdObj.toString());
|
||||
if (userId != null) {
|
||||
advisorUserIds.add(userId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("解析活动 {} 的指导老师数据时发生异常: {}", activity.getActivityName(), e.getMessage());
|
||||
}
|
||||
|
||||
return advisorUserIds;
|
||||
}
|
||||
|
||||
/**
|
||||
* 应用数据权限控制
|
||||
* 如果是stgl_president角色,只能查看自己创建的活动
|
||||
* 如果是stgl_instructor角色,只能查看自己作为指导老师的社团的活动
|
||||
*
|
||||
* @param bo 查询条件
|
||||
*/
|
||||
private void applyDataScope(StglActivityBo bo) {
|
||||
try {
|
||||
// 获取当前登录用户信息
|
||||
Long currentUserId = LoginHelper.getUserId();
|
||||
if (currentUserId == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Set<String> roleKeys = LoginHelper.getLoginUser().getRolePermission();
|
||||
|
||||
// 如果是stgl_president角色且不是管理员,只能查看自己创建的活动
|
||||
if (roleKeys.contains("stgl_president") &&
|
||||
!roleKeys.contains("admin") &&
|
||||
!roleKeys.contains("common")) {
|
||||
|
||||
// 设置数据权限过滤条件 - 使用params.dataScope方式,与XML中的${activity.params.dataScope}对应
|
||||
if (bo.getParams() == null) {
|
||||
bo.setParams(new HashMap<>());
|
||||
}
|
||||
bo.getParams().put("dataScope", "AND sa.create_by = " + currentUserId);
|
||||
log.info("应用数据权限:用户 {} 为社长角色,只能查看自己创建的活动", currentUserId);
|
||||
}
|
||||
|
||||
// 🔥 新增:如果是stgl_instructor角色且不是管理员,只能查看自己作为指导老师的社团的活动
|
||||
if (roleKeys.contains("stgl_instructor") &&
|
||||
!roleKeys.contains("admin") &&
|
||||
!roleKeys.contains("common")) {
|
||||
|
||||
// 获取用户作为指导老师的社团ID列表
|
||||
List<Long> userAdvisorClubIds = getUserAdvisorClubIds(currentUserId);
|
||||
|
||||
if (userAdvisorClubIds.isEmpty()) {
|
||||
// 如果不是任何社团的指导老师,设置一个不存在的ID,返回空结果
|
||||
if (bo.getParams() == null) {
|
||||
bo.setParams(new HashMap<>());
|
||||
}
|
||||
bo.getParams().put("dataScope", "AND sa.related_club_id = -1");
|
||||
log.info("应用数据权限:用户 {} 为指导老师角色,但不是任何社团的指导老师,返回空结果", currentUserId);
|
||||
} else {
|
||||
// 设置数据权限过滤条件 - 只能查看自己作为指导老师的社团的活动
|
||||
if (bo.getParams() == null) {
|
||||
bo.setParams(new HashMap<>());
|
||||
}
|
||||
String clubIdsStr = userAdvisorClubIds.stream()
|
||||
.map(String::valueOf)
|
||||
.collect(Collectors.joining(","));
|
||||
bo.getParams().put("dataScope", "AND sa.related_club_id IN (" + clubIdsStr + ")");
|
||||
log.info("应用数据权限:用户 {} 为指导老师角色,只能查看社团ID为 {} 的活动", currentUserId, clubIdsStr);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("应用数据权限控制失败:{}", e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户作为指导老师的社团ID列表
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @return 社团ID列表
|
||||
*/
|
||||
private List<Long> getUserAdvisorClubIds(Long userId) {
|
||||
List<Long> clubIds = new ArrayList<>();
|
||||
|
||||
try {
|
||||
// 从stgl_club表的advisors_json字段查询
|
||||
List<Long> jsonClubIds = getJsonAdvisorClubIds(userId);
|
||||
clubIds.addAll(jsonClubIds);
|
||||
|
||||
log.debug("用户 {} 作为指导老师的社团ID列表: {}", userId, clubIds);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("获取用户 {} 作为指导老师的社团ID列表时发生异常: {}", userId, e.getMessage(), e);
|
||||
}
|
||||
|
||||
return clubIds;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从社团表的advisors_json字段获取用户作为指导老师的社团ID列表
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @return 社团ID列表
|
||||
*/
|
||||
private List<Long> getJsonAdvisorClubIds(Long userId) {
|
||||
List<Long> clubIds = new ArrayList<>();
|
||||
|
||||
try {
|
||||
// 查询社团表中advisors_json字段包含该用户的记录
|
||||
LambdaQueryWrapper<StglClub> lqw = new LambdaQueryWrapper<>();
|
||||
lqw.select(StglClub::getId);
|
||||
lqw.like(StglClub::getAdvisorsJson, userId.toString());
|
||||
|
||||
List<StglClub> clubs = stglClubMapper.selectList(lqw);
|
||||
clubIds = clubs.stream()
|
||||
.map(StglClub::getId)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("从社团表获取用户 {} 作为指导老师的社团ID列表时发生异常: {}", userId, e.getMessage(), e);
|
||||
}
|
||||
|
||||
return clubIds;
|
||||
}
|
||||
}
|
||||
@@ -79,6 +79,7 @@ public class StglClubRegisterServiceImpl implements IStglClubRegisterService {
|
||||
private LambdaQueryWrapper<StglClub> buildQueryWrapper(String clubType, String clubName) {
|
||||
LambdaQueryWrapper<StglClub> lqw = Wrappers.lambdaQuery();
|
||||
lqw.eq(StglClub::getStatus, 0); // 只查询正常状态的社团(0-正常,1-停用)
|
||||
lqw.eq(StglClub::getSighOpen, "Y"); // 只查询报名开启的社团
|
||||
lqw.eq(StringUtils.isNotBlank(clubType), StglClub::getClubType, clubType);
|
||||
lqw.like(StringUtils.isNotBlank(clubName), StglClub::getClubName, clubName);
|
||||
lqw.orderByDesc(StglClub::getCreateTime);
|
||||
@@ -137,6 +138,7 @@ public class StglClubRegisterServiceImpl implements IStglClubRegisterService {
|
||||
// ========== 新增:章程文件和备注信息 ==========
|
||||
vo.setCharterUrl(club.getCharterUrl());
|
||||
vo.setRemark(club.getRemark());
|
||||
vo.setSighOpen(club.getSighOpen()); // 设置报名开启状态
|
||||
|
||||
// 查询社团成员数量(所有已加入的成员)
|
||||
LambdaQueryWrapper<StglMember> memberCountLqw = Wrappers.lambdaQuery();
|
||||
@@ -243,7 +245,7 @@ public class StglClubRegisterServiceImpl implements IStglClubRegisterService {
|
||||
member.setUserId(userId);
|
||||
member.setStudentNo(studentNo);
|
||||
member.setStudentName(studentName != null ? studentName : user.getNickName()); // 如果没有学生信息,使用用户昵称
|
||||
member.setRoleType(4); // 普通成员
|
||||
member.setRoleType("4"); // 普通成员
|
||||
member.setJoinTime(new Date());
|
||||
member.setStatus(0); // 待审核状态(stgl_member_status字典,新报名需审核)
|
||||
member.setRemark(reason); // 将申请理由存储到备注字段
|
||||
@@ -272,7 +274,7 @@ public class StglClubRegisterServiceImpl implements IStglClubRegisterService {
|
||||
}
|
||||
|
||||
// 检查是否是社长或副社长
|
||||
if (member.getRoleType() != null && member.getRoleType() <= 2) {
|
||||
if (member.getRoleType() != null && Integer.parseInt(member.getRoleType()) <= 2) {
|
||||
throw new RuntimeException("社长和副社长不能直接退出,请先转让职务");
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ import org.dromara.stgl.domain.StglMember;
|
||||
import org.dromara.stgl.mapper.StglClubMapper;
|
||||
import org.dromara.stgl.mapper.StglMemberMapper;
|
||||
import org.dromara.stgl.service.IStglClubService;
|
||||
import org.dromara.stgl.utils.AdvisorRoleAssigner;
|
||||
import org.dromara.common.satoken.utils.LoginHelper;
|
||||
import org.dromara.common.json.utils.JsonUtils;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
@@ -35,10 +36,12 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import java.util.Set;
|
||||
|
||||
@@ -58,6 +61,8 @@ public class StglClubServiceImpl implements IStglClubService {
|
||||
private final StglMemberMapper memberMapper;
|
||||
// 添加学生信息Mapper
|
||||
private final XgStudentMsgMapper studentMsgMapper;
|
||||
// 添加指导老师角色分配工具
|
||||
private final AdvisorRoleAssigner advisorRoleAssigner;
|
||||
|
||||
/**
|
||||
* 查询社团信息
|
||||
@@ -143,6 +148,31 @@ public class StglClubServiceImpl implements IStglClubService {
|
||||
bo.setClubIds(userClubIds);
|
||||
}
|
||||
}
|
||||
|
||||
// 🔥 新增:如果是stgl_instructor角色且不是管理员,只能查看自己作为指导老师的社团
|
||||
if (roleKeys.contains("stgl_instructor") &&
|
||||
!roleKeys.contains("admin") &&
|
||||
!roleKeys.contains("common")) {
|
||||
|
||||
// 获取用户作为指导老师的社团ID列表
|
||||
List<Long> userAdvisorClubIds = getUserAdvisorClubIds(currentUserId);
|
||||
|
||||
if (userAdvisorClubIds.isEmpty()) {
|
||||
// 如果不是任何社团的指导老师,设置一个不存在的ID,返回空结果
|
||||
bo.setClubIds(Collections.singletonList(-1L));
|
||||
} else {
|
||||
// 如果之前已经设置了clubIds(比如stgl_president角色),则取交集
|
||||
if (CollectionUtils.isNotEmpty(bo.getClubIds())) {
|
||||
List<Long> intersection = bo.getClubIds().stream()
|
||||
.filter(userAdvisorClubIds::contains)
|
||||
.collect(Collectors.toList());
|
||||
bo.setClubIds(intersection.isEmpty() ? Collections.singletonList(-1L) : intersection);
|
||||
} else {
|
||||
// 设置数据权限过滤条件
|
||||
bo.setClubIds(userAdvisorClubIds);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// 如果获取权限信息失败,不影响正常查询
|
||||
log.warn("获取用户权限信息失败: {}", e.getMessage());
|
||||
@@ -164,7 +194,7 @@ public class StglClubServiceImpl implements IStglClubService {
|
||||
LambdaQueryWrapper<StglMember> lqw = Wrappers.lambdaQuery();
|
||||
lqw.eq(StglMember::getMemberType, 1) // 社团成员
|
||||
.eq(StglMember::getUserId, userId) // 当前用户
|
||||
.eq(StglMember::getRoleType, 1) // 社长角色
|
||||
.eq(StglMember::getRoleType, "社长") // 社长角色 - 修复:使用字符串而不是数值
|
||||
.eq(StglMember::getStatus, 2); // 已加入状态
|
||||
|
||||
List<StglMember> members = memberMapper.selectList(lqw);
|
||||
@@ -178,6 +208,91 @@ public class StglClubServiceImpl implements IStglClubService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户作为指导老师的社团ID列表
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @return 社团ID列表
|
||||
*/
|
||||
private List<Long> getUserAdvisorClubIds(Long userId) {
|
||||
if (userId == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
try {
|
||||
// 方法1:从stgl_member表查询(如果指导老师作为成员记录)
|
||||
LambdaQueryWrapper<StglMember> memberLqw = Wrappers.lambdaQuery();
|
||||
memberLqw.eq(StglMember::getMemberType, 1) // 社团成员
|
||||
.eq(StglMember::getUserId, userId) // 当前用户
|
||||
.eq(StglMember::getRoleType, "指导老师") // 指导老师角色
|
||||
.eq(StglMember::getStatus, 2); // 已加入状态
|
||||
|
||||
List<StglMember> memberAdvisors = memberMapper.selectList(memberLqw);
|
||||
List<Long> memberClubIds = memberAdvisors.stream()
|
||||
.map(StglMember::getRelationId)
|
||||
.distinct()
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// 方法2:从社团表的advisors_json字段查询(主要方法)
|
||||
LambdaQueryWrapper<StglClub> clubLqw = Wrappers.lambdaQuery();
|
||||
clubLqw.select(StglClub::getId, StglClub::getAdvisorsJson);
|
||||
clubLqw.isNotNull(StglClub::getAdvisorsJson);
|
||||
clubLqw.ne(StglClub::getAdvisorsJson, "");
|
||||
|
||||
List<StglClub> clubs = baseMapper.selectList(clubLqw);
|
||||
List<Long> jsonClubIds = new ArrayList<>();
|
||||
|
||||
for (StglClub club : clubs) {
|
||||
try {
|
||||
if (StringUtils.isNotBlank(club.getAdvisorsJson())) {
|
||||
List<Map<String, Object>> advisors = JsonUtils.parseObject(
|
||||
club.getAdvisorsJson(),
|
||||
new TypeReference<List<Map<String, Object>>>() {}
|
||||
);
|
||||
|
||||
for (Map<String, Object> advisor : advisors) {
|
||||
Object userIdObj = null;
|
||||
|
||||
// 尝试直接从advisor对象获取userId
|
||||
if (advisor.containsKey("userId")) {
|
||||
userIdObj = advisor.get("userId");
|
||||
}
|
||||
// 尝试从teacher子对象获取userId
|
||||
else if (advisor.containsKey("teacher")) {
|
||||
Object teacherObj = advisor.get("teacher");
|
||||
if (teacherObj instanceof Map) {
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> teacher = (Map<String, Object>) teacherObj;
|
||||
userIdObj = teacher.get("userId");
|
||||
}
|
||||
}
|
||||
|
||||
if (userIdObj != null && userId.toString().equals(userIdObj.toString())) {
|
||||
jsonClubIds.add(club.getId());
|
||||
break; // 找到该用户,继续下一个社团
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("解析社团 {} 的指导老师JSON数据失败: {}", club.getId(), e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// 合并两种方法的结果
|
||||
Set<Long> allClubIds = new HashSet<>();
|
||||
allClubIds.addAll(memberClubIds);
|
||||
allClubIds.addAll(jsonClubIds);
|
||||
|
||||
log.info("用户 {} 作为指导老师的社团ID列表: memberClubIds={}, jsonClubIds={}, 最终结果={}",
|
||||
userId, memberClubIds, jsonClubIds, allClubIds);
|
||||
|
||||
return new ArrayList<>(allClubIds);
|
||||
} catch (Exception e) {
|
||||
log.error("查询用户作为指导老师的社团失败: {}", e.getMessage(), e);
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
private LambdaQueryWrapper<StglClub> buildQueryWrapper(StglClubBo bo) {
|
||||
Map<String, Object> params = bo.getParams();
|
||||
LambdaQueryWrapper<StglClub> lqw = Wrappers.lambdaQuery();
|
||||
@@ -215,6 +330,8 @@ public class StglClubServiceImpl implements IStglClubService {
|
||||
bo.setId(add.getId());
|
||||
// 自动创建社长和副社长的成员记录
|
||||
createLeaderMemberRecords(add);
|
||||
// 为指导老师分配角色
|
||||
assignAdvisorRoles(add);
|
||||
}
|
||||
return flag;
|
||||
}
|
||||
@@ -231,12 +348,20 @@ public class StglClubServiceImpl implements IStglClubService {
|
||||
StglClub update = MapstructUtils.convert(bo, StglClub.class);
|
||||
validEntityBeforeSave(update);
|
||||
|
||||
// 获取更新前的社团信息,用于比较指导老师变化
|
||||
StglClub oldClub = null;
|
||||
if (update.getId() != null) {
|
||||
oldClub = baseMapper.selectById(update.getId());
|
||||
}
|
||||
|
||||
// 更新社团基本信息
|
||||
boolean updateResult = baseMapper.updateById(update) > 0;
|
||||
|
||||
if (updateResult) {
|
||||
// 同步更新副社长的成员记录
|
||||
syncVicePresidentsInMembers(update);
|
||||
// 处理指导老师角色分配
|
||||
handleAdvisorRoleAssignment(oldClub, update);
|
||||
}
|
||||
|
||||
return updateResult;
|
||||
@@ -358,6 +483,21 @@ public class StglClubServiceImpl implements IStglClubService {
|
||||
return baseMapper.updateById(club) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改社团报名开启状态
|
||||
*
|
||||
* @param id 社团ID
|
||||
* @param sighOpen 报名开启状态(Y/N)
|
||||
* @return 是否操作成功
|
||||
*/
|
||||
@Override
|
||||
public Boolean changeSighOpenStatus(Long id, String sighOpen) {
|
||||
StglClub club = new StglClub();
|
||||
club.setId(id);
|
||||
club.setSighOpen(sighOpen);
|
||||
return baseMapper.updateById(club) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 自动创建社长和副社长的成员记录
|
||||
*
|
||||
@@ -373,7 +513,7 @@ public class StglClubServiceImpl implements IStglClubService {
|
||||
president.setRelationId(club.getId());
|
||||
president.setStudentNo(club.getPresidentStudentNo());
|
||||
president.setStudentName(club.getPresidentName());
|
||||
president.setRoleType(1); // 社长
|
||||
president.setRoleType("社长"); // 社长
|
||||
president.setJoinTime(now);
|
||||
president.setStatus(2); // 已加入状态(stgl_member_status字典)
|
||||
president.setRemark("社团创建时自动添加");
|
||||
@@ -482,7 +622,7 @@ public class StglClubServiceImpl implements IStglClubService {
|
||||
vicePresident.setRelationId(clubId);
|
||||
vicePresident.setStudentNo(studentNo);
|
||||
vicePresident.setStudentName(name);
|
||||
vicePresident.setRoleType(2); // 副社长
|
||||
vicePresident.setRoleType("副社长"); // 副社长
|
||||
vicePresident.setJoinTime(joinTime);
|
||||
vicePresident.setStatus(2); // 已加入状态(stgl_member_status字典)
|
||||
vicePresident.setRemark("社团创建时自动添加");
|
||||
@@ -548,14 +688,14 @@ public class StglClubServiceImpl implements IStglClubService {
|
||||
|
||||
// 检查并补充社长成员记录
|
||||
if (StringUtils.isNotBlank(club.getPresidentStudentNo())) {
|
||||
boolean presidentExists = checkMemberExists(club.getId(), club.getPresidentStudentNo(), 1);
|
||||
boolean presidentExists = checkMemberExists(club.getId(), club.getPresidentStudentNo(), "社长");
|
||||
if (!presidentExists) {
|
||||
StglMember president = new StglMember();
|
||||
president.setMemberType(1); // 社团成员
|
||||
president.setRelationId(club.getId());
|
||||
president.setStudentNo(club.getPresidentStudentNo());
|
||||
president.setStudentName(club.getPresidentName());
|
||||
president.setRoleType(1); // 社长
|
||||
president.setRoleType("社长"); // 社长
|
||||
president.setJoinTime(club.getCreateTime() != null ? club.getCreateTime() : now);
|
||||
president.setStatus(2); // 已加入状态(stgl_member_status字典)
|
||||
president.setRemark("补充缺失的社长成员记录");
|
||||
@@ -589,7 +729,7 @@ public class StglClubServiceImpl implements IStglClubService {
|
||||
|
||||
// 1. 处理单个副社长字段(向后兼容)
|
||||
if (StringUtils.isNotBlank(club.getVicePresidentStudentNo())) {
|
||||
boolean vicePresidentExists = checkMemberExists(club.getId(), club.getVicePresidentStudentNo(), 2);
|
||||
boolean vicePresidentExists = checkMemberExists(club.getId(), club.getVicePresidentStudentNo(), "副社长");
|
||||
if (!vicePresidentExists) {
|
||||
fixedCount += createFixedVicePresidentRecord(
|
||||
club.getId(),
|
||||
@@ -643,7 +783,7 @@ public class StglClubServiceImpl implements IStglClubService {
|
||||
if (StringUtils.isBlank(club.getVicePresidentStudentNo()) ||
|
||||
!studentNo.equals(club.getVicePresidentStudentNo())) {
|
||||
|
||||
boolean exists = checkMemberExists(club.getId(), studentNo, 2);
|
||||
boolean exists = checkMemberExists(club.getId(), studentNo, "副社长");
|
||||
if (!exists) {
|
||||
System.out.println("修复副社长成员记录: " + name + "(" + studentNo + ")");
|
||||
fixedCount += createFixedVicePresidentRecord(club.getId(), studentNo, name, joinTime) ? 1 : 0;
|
||||
@@ -677,7 +817,7 @@ public class StglClubServiceImpl implements IStglClubService {
|
||||
vicePresident.setRelationId(clubId);
|
||||
vicePresident.setStudentNo(studentNo);
|
||||
vicePresident.setStudentName(name);
|
||||
vicePresident.setRoleType(2); // 副社长
|
||||
vicePresident.setRoleType("副社长"); // 副社长
|
||||
vicePresident.setJoinTime(joinTime);
|
||||
vicePresident.setStatus(2); // 已加入状态(stgl_member_status字典)
|
||||
vicePresident.setRemark("补充缺失的副社长成员记录");
|
||||
@@ -702,10 +842,11 @@ public class StglClubServiceImpl implements IStglClubService {
|
||||
* @param roleType 角色类型
|
||||
* @return 是否存在
|
||||
*/
|
||||
private boolean checkMemberExists(Long clubId, String studentNo, Integer roleType) {
|
||||
private boolean checkMemberExists(Long clubId, String studentNo, String roleType) {
|
||||
LambdaQueryWrapper<StglMember> query = Wrappers.lambdaQuery();
|
||||
query.eq(StglMember::getMemberType, 1);
|
||||
query.eq(StglMember::getRelationId, clubId);
|
||||
query.eq(StglMember::getMemberType, 1);
|
||||
query.eq(StglMember::getStudentNo, studentNo);
|
||||
query.eq(StglMember::getRoleType, roleType);
|
||||
query.eq(StglMember::getStatus, 1);
|
||||
@@ -721,11 +862,11 @@ public class StglClubServiceImpl implements IStglClubService {
|
||||
*/
|
||||
private void syncVicePresidentsInMembers(StglClub club) {
|
||||
try {
|
||||
// 1. 删除现有的所有副社长记录(roleType = 2)
|
||||
// 1. 删除现有的所有副社长记录(roleType = "副社长")
|
||||
LambdaQueryWrapper<StglMember> deleteWrapper = Wrappers.lambdaQuery();
|
||||
deleteWrapper.eq(StglMember::getRelationId, club.getId())
|
||||
.eq(StglMember::getMemberType, 1) // 社团成员
|
||||
.eq(StglMember::getRoleType, 2); // 副社长角色
|
||||
.eq(StglMember::getRoleType, "副社长"); // 副社长角色
|
||||
memberMapper.delete(deleteWrapper);
|
||||
|
||||
// 2. 处理单个副社长字段(向后兼容)
|
||||
@@ -851,7 +992,7 @@ public class StglClubServiceImpl implements IStglClubService {
|
||||
lqw.eq(StglMember::getMemberType, 1); // 社团成员
|
||||
lqw.eq(StglMember::getRelationId, clubId); // 该社团
|
||||
lqw.eq(StglMember::getUserId, userId); // 指定用户
|
||||
lqw.eq(StglMember::getRoleType, 1); // 社长角色 (1-社长)
|
||||
lqw.eq(StglMember::getRoleType, "社长"); // 社长角色 - 修复:使用字符串而不是数值
|
||||
lqw.eq(StglMember::getStatus, 2); // 已加入状态 (2-已加入)
|
||||
|
||||
StglMember member = memberMapper.selectOne(lqw);
|
||||
@@ -897,7 +1038,7 @@ public class StglClubServiceImpl implements IStglClubService {
|
||||
LambdaQueryWrapper<StglMember> oldPresidentLqw = Wrappers.lambdaQuery();
|
||||
oldPresidentLqw.eq(StglMember::getMemberType, 1); // 社团成员
|
||||
oldPresidentLqw.eq(StglMember::getRelationId, clubId);
|
||||
oldPresidentLqw.eq(StglMember::getRoleType, 1); // 社长
|
||||
oldPresidentLqw.eq(StglMember::getRoleType, "社长"); // 社长
|
||||
oldPresidentLqw.eq(StglMember::getStatus, 2); // 已加入状态
|
||||
StglMember oldPresidentMember = memberMapper.selectOne(oldPresidentLqw);
|
||||
|
||||
@@ -936,7 +1077,7 @@ public class StglClubServiceImpl implements IStglClubService {
|
||||
// 如果新社长已经是成员,更新角色和状态
|
||||
StglMember updateNewPresident = new StglMember();
|
||||
updateNewPresident.setId(newPresidentMember.getId());
|
||||
updateNewPresident.setRoleType(1); // 社长
|
||||
updateNewPresident.setRoleType("社长"); // 社长
|
||||
updateNewPresident.setStatus(2); // 已加入状态
|
||||
updateNewPresident.setJoinTime(new Date());
|
||||
memberMapper.updateById(updateNewPresident);
|
||||
@@ -948,7 +1089,7 @@ public class StglClubServiceImpl implements IStglClubService {
|
||||
newMember.setUserId(newPresidentUserId);
|
||||
newMember.setStudentNo(newPresidentStudent.getStudentNo());
|
||||
newMember.setStudentName(newPresidentStudent.getName());
|
||||
newMember.setRoleType(1); // 社长
|
||||
newMember.setRoleType("社长"); // 社长
|
||||
newMember.setJoinTime(new Date());
|
||||
newMember.setStatus(2); // 已加入状态
|
||||
memberMapper.insert(newMember);
|
||||
@@ -957,4 +1098,114 @@ public class StglClubServiceImpl implements IStglClubService {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 为指导老师分配角色
|
||||
*
|
||||
* @param club 社团信息
|
||||
*/
|
||||
private void assignAdvisorRoles(StglClub club) {
|
||||
try {
|
||||
List<Long> advisorUserIds = extractAdvisorUserIds(club);
|
||||
if (!advisorUserIds.isEmpty()) {
|
||||
advisorRoleAssigner.assignAdvisorRole(advisorUserIds);
|
||||
log.info("成功为社团 {} 的指导老师分配角色,指导老师数量: {}", club.getClubName(), advisorUserIds.size());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("为社团 {} 的指导老师分配角色时发生异常", club.getClubName(), e);
|
||||
// 不抛出异常,避免影响主业务流程
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理指导老师角色分配(更新时使用)
|
||||
*
|
||||
* @param oldClub 更新前的社团信息
|
||||
* @param newClub 更新后的社团信息
|
||||
*/
|
||||
private void handleAdvisorRoleAssignment(StglClub oldClub, StglClub newClub) {
|
||||
try {
|
||||
// 获取更新前后的指导老师用户ID列表
|
||||
List<Long> oldAdvisorUserIds = oldClub != null ? extractAdvisorUserIds(oldClub) : new ArrayList<>();
|
||||
List<Long> newAdvisorUserIds = extractAdvisorUserIds(newClub);
|
||||
|
||||
// 找出新增的指导老师
|
||||
List<Long> addedAdvisorUserIds = newAdvisorUserIds.stream()
|
||||
.filter(id -> !oldAdvisorUserIds.contains(id))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// 找出移除的指导老师
|
||||
List<Long> removedAdvisorUserIds = oldAdvisorUserIds.stream()
|
||||
.filter(id -> !newAdvisorUserIds.contains(id))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// 为新指导老师分配角色
|
||||
if (!addedAdvisorUserIds.isEmpty()) {
|
||||
advisorRoleAssigner.assignAdvisorRole(addedAdvisorUserIds);
|
||||
log.info("社团 {} 新增指导老师,成功分配角色,数量: {}", newClub.getClubName(), addedAdvisorUserIds.size());
|
||||
}
|
||||
|
||||
// 移除旧指导老师的角色
|
||||
if (!removedAdvisorUserIds.isEmpty()) {
|
||||
advisorRoleAssigner.removeAdvisorRoles(removedAdvisorUserIds);
|
||||
log.info("社团 {} 移除指导老师,成功移除角色,数量: {}", newClub.getClubName(), removedAdvisorUserIds.size());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("处理社团 {} 的指导老师角色分配时发生异常", newClub.getClubName(), e);
|
||||
// 不抛出异常,避免影响主业务流程
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从社团信息中提取指导老师用户ID列表
|
||||
*
|
||||
* @param club 社团信息
|
||||
* @return 指导老师用户ID列表
|
||||
*/
|
||||
private List<Long> extractAdvisorUserIds(StglClub club) {
|
||||
List<Long> advisorUserIds = new ArrayList<>();
|
||||
|
||||
try {
|
||||
// 优先从JSON字段读取指导老师数据
|
||||
if (StringUtils.isNotBlank(club.getAdvisorsJson())) {
|
||||
List<Map<String, Object>> advisors = JsonUtils.parseObject(club.getAdvisorsJson(), new TypeReference<List<Map<String, Object>>>() {});
|
||||
for (Map<String, Object> advisor : advisors) {
|
||||
// 修复:支持嵌套的JSON结构,从teacher子对象中提取userId
|
||||
Object userIdObj = null;
|
||||
|
||||
// 尝试直接从advisor对象获取userId(兼容旧格式)
|
||||
if (advisor.containsKey("userId")) {
|
||||
userIdObj = advisor.get("userId");
|
||||
}
|
||||
// 尝试从teacher子对象获取userId(新格式)
|
||||
else if (advisor.containsKey("teacher")) {
|
||||
Object teacherObj = advisor.get("teacher");
|
||||
if (teacherObj instanceof Map) {
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> teacher = (Map<String, Object>) teacherObj;
|
||||
userIdObj = teacher.get("userId");
|
||||
}
|
||||
}
|
||||
|
||||
if (userIdObj != null) {
|
||||
Long userId = Long.valueOf(userIdObj.toString());
|
||||
if (userId != null) {
|
||||
advisorUserIds.add(userId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 如果没有JSON数据,尝试从单一字段获取
|
||||
if (advisorUserIds.isEmpty() && StringUtils.isNotBlank(club.getAdvisorName())) {
|
||||
// 这里需要根据指导老师姓名查找用户ID,暂时跳过
|
||||
// 因为单一字段模式下没有用户ID信息
|
||||
log.debug("社团 {} 使用单一指导老师字段,无法获取用户ID", club.getClubName());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("解析社团 {} 的指导老师数据时发生异常", club.getClubName(), e);
|
||||
}
|
||||
|
||||
return advisorUserIds;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -85,7 +85,8 @@ public class StglMemberServiceImpl implements IStglMemberService {
|
||||
bo.getStatus(),
|
||||
bo.getClubName(),
|
||||
bo.getTeamName(),
|
||||
bo.getUserId()
|
||||
bo.getUserId(),
|
||||
bo.getParams()
|
||||
);
|
||||
|
||||
log.info("查询结果: total={}", allMembers.size());
|
||||
@@ -131,7 +132,8 @@ public class StglMemberServiceImpl implements IStglMemberService {
|
||||
bo.getStatus(),
|
||||
bo.getClubName(),
|
||||
bo.getTeamName(),
|
||||
bo.getUserId()
|
||||
bo.getUserId(),
|
||||
bo.getParams()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -144,7 +146,35 @@ public class StglMemberServiceImpl implements IStglMemberService {
|
||||
lqw.like(StringUtils.isNotBlank(bo.getStudentNo()), StglMember::getStudentNo, bo.getStudentNo());
|
||||
lqw.like(StringUtils.isNotBlank(bo.getStudentName()), StglMember::getStudentName, bo.getStudentName());
|
||||
lqw.eq(bo.getRoleType() != null, StglMember::getRoleType, bo.getRoleType());
|
||||
lqw.eq(bo.getStatus() != null, StglMember::getStatus, bo.getStatus());
|
||||
|
||||
// 处理状态查询:优先使用statusList多状态查询,如果没有则使用单个status
|
||||
if (params != null && params.get("statusList") != null) {
|
||||
Object statusList = params.get("statusList");
|
||||
if (statusList instanceof List) {
|
||||
List<?> statuses = (List<?>) statusList;
|
||||
if (!statuses.isEmpty()) {
|
||||
lqw.in(StglMember::getStatus, statuses);
|
||||
}
|
||||
} else if (statusList instanceof String) {
|
||||
String statusStr = (String) statusList;
|
||||
if (statusStr.contains(",")) {
|
||||
// 处理 "0,3" 格式的字符串
|
||||
String[] statuses = statusStr.split(",");
|
||||
List<String> statusList2 = new ArrayList<>();
|
||||
for (String status : statuses) {
|
||||
statusList2.add(status.trim());
|
||||
}
|
||||
lqw.in(StglMember::getStatus, statusList2);
|
||||
} else {
|
||||
// 单个状态值
|
||||
lqw.eq(StglMember::getStatus, statusStr);
|
||||
}
|
||||
}
|
||||
} else if (bo.getStatus() != null) {
|
||||
// 如果没有statusList,则使用单个status字段
|
||||
lqw.eq(StglMember::getStatus, bo.getStatus());
|
||||
}
|
||||
|
||||
lqw.between(params.get("beginJoinTime") != null && params.get("endJoinTime") != null,
|
||||
StglMember::getJoinTime, params.get("beginJoinTime"), params.get("endJoinTime"));
|
||||
lqw.orderByDesc(StglMember::getJoinTime);
|
||||
@@ -208,7 +238,7 @@ public class StglMemberServiceImpl implements IStglMemberService {
|
||||
entity.setStatus(2); // 默认为已加入状态(stgl_member_status字典)
|
||||
}
|
||||
if (entity.getRoleType() == null) {
|
||||
entity.setRoleType(3); // 默认为普通成员
|
||||
entity.setRoleType("3"); // 默认为普通成员
|
||||
}
|
||||
}
|
||||
|
||||
@@ -289,7 +319,7 @@ public class StglMemberServiceImpl implements IStglMemberService {
|
||||
member.setMemberType(memberType);
|
||||
member.setRelationId(relationId);
|
||||
member.setUserId(userId);
|
||||
member.setRoleType(roleType != null ? roleType : 3);
|
||||
member.setRoleType(roleType != null ? roleType.toString() : "3");
|
||||
member.setJoinTime(now);
|
||||
member.setStatus(2); // 已加入状态(stgl_member_status字典)
|
||||
return member;
|
||||
@@ -309,7 +339,7 @@ public class StglMemberServiceImpl implements IStglMemberService {
|
||||
public Boolean updateMemberRole(Long id, Integer roleType) {
|
||||
StglMember member = new StglMember();
|
||||
member.setId(id);
|
||||
member.setRoleType(roleType);
|
||||
member.setRoleType(roleType.toString());
|
||||
return baseMapper.updateById(member) > 0;
|
||||
}
|
||||
|
||||
@@ -395,8 +425,8 @@ public class StglMemberServiceImpl implements IStglMemberService {
|
||||
memberBo.setStudentNo(memberVo.getStudentNo());
|
||||
memberBo.setStudentName(memberVo.getStudentName());
|
||||
memberBo.setRelationId(memberVo.getRelationId());
|
||||
memberBo.setRoleType(memberVo.getRoleType() != null ? memberVo.getRoleType() : 1); // 默认普通成员
|
||||
memberBo.setStatus(memberVo.getStatus() != null ? memberVo.getStatus() : 1); // 默认正常状态
|
||||
memberBo.setRoleType(memberVo.getRoleType() != null ? memberVo.getRoleType() : "1"); // 默认普通成员
|
||||
memberBo.setStatus(memberVo.getStatus() != null ? memberVo.getStatus() : "1"); // 默认正常状态
|
||||
memberBo.setJoinTime(memberVo.getJoinTime() != null ? memberVo.getJoinTime() : new Date());
|
||||
|
||||
// 检查是否已存在
|
||||
@@ -469,16 +499,17 @@ public class StglMemberServiceImpl implements IStglMemberService {
|
||||
null, // status
|
||||
null, // clubName
|
||||
null, // teamName
|
||||
bo.getUserId()
|
||||
bo.getUserId(),
|
||||
null // params
|
||||
);
|
||||
|
||||
// 基于权限过滤后的数据进行统计
|
||||
long totalCount = allMembers.size();
|
||||
long activeCount = allMembers.stream()
|
||||
.mapToLong(member -> member.getStatus() != null && member.getStatus() == 2 ? 1 : 0)
|
||||
.mapToLong(member -> member.getStatus() != null && "2".equals(member.getStatus()) ? 1 : 0)
|
||||
.sum();
|
||||
long pendingCount = allMembers.stream()
|
||||
.mapToLong(member -> member.getStatus() != null && member.getStatus() == 1 ? 1 : 0)
|
||||
.mapToLong(member -> member.getStatus() != null && "1".equals(member.getStatus()) ? 1 : 0)
|
||||
.sum();
|
||||
|
||||
Map<String, Object> stats = new HashMap<>();
|
||||
@@ -597,7 +628,7 @@ public class StglMemberServiceImpl implements IStglMemberService {
|
||||
// 查询用户作为队长的志愿者队伍
|
||||
StglMemberBo captainBo = new StglMemberBo();
|
||||
captainBo.setMemberType(2); // 志愿者成员
|
||||
captainBo.setRoleType(0); // 队长角色
|
||||
captainBo.setRoleType("0"); // 队长角色
|
||||
|
||||
List<StglMemberVo> captainRecords = baseMapper.selectMemberWithUserInfo(
|
||||
captainBo.getMemberType(),
|
||||
@@ -608,7 +639,8 @@ public class StglMemberServiceImpl implements IStglMemberService {
|
||||
null, // status
|
||||
null, // clubName
|
||||
null, // teamName
|
||||
userId // userId 权限过滤
|
||||
userId, // userId 权限过滤
|
||||
null // params
|
||||
);
|
||||
|
||||
return captainRecords.stream()
|
||||
|
||||
@@ -25,13 +25,16 @@ import org.dromara.system.mapper.SysUserMapper;
|
||||
import org.dromara.system.domain.vo.SysUserVo;
|
||||
import org.dromara.system.domain.SysUser;
|
||||
import org.dromara.common.satoken.utils.LoginHelper;
|
||||
import org.dromara.stgl.utils.AdvisorRoleAssigner;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 志愿者队伍Service业务层处理
|
||||
@@ -39,6 +42,7 @@ import java.util.List;
|
||||
* @author Cursor AI
|
||||
* @date 2025-01-07
|
||||
*/
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
@Service
|
||||
public class StglVolunteerTeamServiceImpl implements IStglVolunteerTeamService {
|
||||
@@ -47,6 +51,7 @@ public class StglVolunteerTeamServiceImpl implements IStglVolunteerTeamService {
|
||||
private final StglMemberMapper memberMapper;
|
||||
private final XgStudentMsgMapper studentMsgMapper;
|
||||
private final SysUserMapper userMapper;
|
||||
private final AdvisorRoleAssigner advisorRoleAssigner;
|
||||
|
||||
/**
|
||||
* 查询志愿者队伍
|
||||
@@ -148,6 +153,8 @@ public class StglVolunteerTeamServiceImpl implements IStglVolunteerTeamService {
|
||||
bo.setId(add.getId());
|
||||
// 自动创建队长的成员记录
|
||||
createLeaderMemberRecord(add);
|
||||
// 为指导老师分配角色
|
||||
assignAdvisorRole(add);
|
||||
}
|
||||
return flag;
|
||||
}
|
||||
@@ -161,12 +168,20 @@ public class StglVolunteerTeamServiceImpl implements IStglVolunteerTeamService {
|
||||
StglVolunteerTeam update = MapstructUtils.convert(bo, StglVolunteerTeam.class);
|
||||
validEntityBeforeSave(update);
|
||||
|
||||
// 获取更新前的队伍信息,用于比较指导老师变化
|
||||
StglVolunteerTeam oldTeam = null;
|
||||
if (update.getId() != null) {
|
||||
oldTeam = baseMapper.selectById(update.getId());
|
||||
}
|
||||
|
||||
// 更新队伍基本信息
|
||||
boolean updateResult = baseMapper.updateById(update) > 0;
|
||||
|
||||
if (updateResult) {
|
||||
// 同步更新队长的成员记录
|
||||
syncLeaderInMembers(update);
|
||||
// 处理指导老师角色分配
|
||||
handleAdvisorRoleAssignment(oldTeam, update);
|
||||
}
|
||||
|
||||
return updateResult;
|
||||
@@ -220,7 +235,7 @@ public class StglVolunteerTeamServiceImpl implements IStglVolunteerTeamService {
|
||||
leader.setUserId(team.getLeaderUserId());
|
||||
leader.setStudentNo(team.getLeaderStudentNo());
|
||||
leader.setStudentName(team.getLeaderName());
|
||||
leader.setRoleType(0); // 队长 (stgl_volunteer_role=0)
|
||||
leader.setRoleType("队长"); // 队长角色
|
||||
leader.setJoinTime(new Date());
|
||||
leader.setStatus(2); // 已加入状态
|
||||
leader.setRemark("志愿者队伍创建时自动添加");
|
||||
@@ -256,7 +271,7 @@ public class StglVolunteerTeamServiceImpl implements IStglVolunteerTeamService {
|
||||
LambdaQueryWrapper<StglMember> deleteWrapper = Wrappers.lambdaQuery();
|
||||
deleteWrapper.eq(StglMember::getRelationId, team.getId())
|
||||
.eq(StglMember::getMemberType, 2) // 志愿者成员
|
||||
.eq(StglMember::getRoleType, 0); // 队长角色
|
||||
.eq(StglMember::getRoleType, "队长"); // 队长角色
|
||||
memberMapper.delete(deleteWrapper);
|
||||
|
||||
// 2. 创建新的队长成员记录
|
||||
@@ -267,7 +282,7 @@ public class StglVolunteerTeamServiceImpl implements IStglVolunteerTeamService {
|
||||
leader.setUserId(team.getLeaderUserId());
|
||||
leader.setStudentNo(team.getLeaderStudentNo());
|
||||
leader.setStudentName(team.getLeaderName());
|
||||
leader.setRoleType(0); // 队长 (stgl_volunteer_role=0)
|
||||
leader.setRoleType("队长"); // 队长角色
|
||||
leader.setJoinTime(new Date());
|
||||
leader.setStatus(2); // 已加入状态
|
||||
leader.setRemark("志愿者队伍修改时同步更新");
|
||||
@@ -303,7 +318,7 @@ public class StglVolunteerTeamServiceImpl implements IStglVolunteerTeamService {
|
||||
query.eq(StglMember::getMemberType, 2); // 志愿者成员
|
||||
query.eq(StglMember::getRelationId, teamId); // 指定队伍
|
||||
query.eq(StglMember::getStudentNo, studentNo); // 指定学号
|
||||
query.eq(StglMember::getRoleType, 0); // 队长角色
|
||||
query.eq(StglMember::getRoleType, "0"); // 队长角色
|
||||
query.eq(StglMember::getStatus, 2); // 已加入状态
|
||||
|
||||
return memberMapper.selectCount(query) > 0;
|
||||
@@ -433,7 +448,7 @@ public class StglVolunteerTeamServiceImpl implements IStglVolunteerTeamService {
|
||||
member.setUserId(LoginHelper.getUserId()); // 设置当前登录用户ID
|
||||
member.setStudentName(applicationBo.getStudentName());
|
||||
member.setStudentNo(applicationBo.getStudentNo());
|
||||
member.setRoleType(1); // 1-队员(志愿者角色)
|
||||
member.setRoleType("1"); // 1-队员(志愿者角色)
|
||||
member.setStatus(0); // 0-待审核
|
||||
member.setJoinTime(new Date());
|
||||
|
||||
@@ -540,7 +555,7 @@ public class StglVolunteerTeamServiceImpl implements IStglVolunteerTeamService {
|
||||
leader.setUserId(team.getLeaderUserId());
|
||||
leader.setStudentNo(team.getLeaderStudentNo());
|
||||
leader.setStudentName(team.getLeaderName());
|
||||
leader.setRoleType(0); // 队长 (stgl_volunteer_role=0)
|
||||
leader.setRoleType("0"); // 队长 (stgl_volunteer_role=0)
|
||||
leader.setJoinTime(team.getCreateTime() != null ? team.getCreateTime() : new Date());
|
||||
leader.setStatus(2); // 已加入状态
|
||||
leader.setRemark("补充缺失的队长成员记录");
|
||||
@@ -657,7 +672,7 @@ public class StglVolunteerTeamServiceImpl implements IStglVolunteerTeamService {
|
||||
// 如果新队长已经是成员,更新角色和状态
|
||||
StglMember updateNewLeader = new StglMember();
|
||||
updateNewLeader.setId(newLeaderMember.getId());
|
||||
updateNewLeader.setRoleType(0); // 队长角色
|
||||
updateNewLeader.setRoleType("0"); // 队长角色
|
||||
updateNewLeader.setStatus(2); // 已加入状态
|
||||
updateNewLeader.setJoinTime(new Date());
|
||||
updateNewLeader.setUpdateBy(LoginHelper.getUserId());
|
||||
@@ -675,7 +690,7 @@ public class StglVolunteerTeamServiceImpl implements IStglVolunteerTeamService {
|
||||
newMember.setUserId(newLeaderUserId);
|
||||
newMember.setStudentNo(newLeaderStudent.getStudentNo());
|
||||
newMember.setStudentName(newLeaderStudent.getName());
|
||||
newMember.setRoleType(0); // 队长角色
|
||||
newMember.setRoleType("0"); // 队长角色
|
||||
newMember.setJoinTime(new Date());
|
||||
newMember.setStatus(2); // 已加入状态
|
||||
newMember.setCreateBy(LoginHelper.getUserId());
|
||||
@@ -816,4 +831,92 @@ public class StglVolunteerTeamServiceImpl implements IStglVolunteerTeamService {
|
||||
List<StglMember> members = memberMapper.selectList(wrapper);
|
||||
return members.isEmpty() ? null : members.get(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改志愿者团队报名开启状态
|
||||
*
|
||||
* @param id 团队ID
|
||||
* @param sighOpen 报名开启状态(Y/N)
|
||||
* @return 是否操作成功
|
||||
*/
|
||||
@Override
|
||||
public Boolean changeSighOpenStatus(Long id, String sighOpen) {
|
||||
StglVolunteerTeam team = new StglVolunteerTeam();
|
||||
team.setId(id);
|
||||
team.setSighOpen(sighOpen);
|
||||
return baseMapper.updateById(team) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 为指导老师分配角色
|
||||
*
|
||||
* @param team 志愿者队伍信息
|
||||
*/
|
||||
private void assignAdvisorRole(StglVolunteerTeam team) {
|
||||
try {
|
||||
Long advisorUserId = extractAdvisorUserId(team);
|
||||
if (advisorUserId != null) {
|
||||
advisorRoleAssigner.assignAdvisorRole(advisorUserId);
|
||||
log.info("成功为志愿者队伍 {} 的指导老师分配角色,指导老师用户ID: {}", team.getTeamName(), advisorUserId);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("为志愿者队伍 {} 的指导老师分配角色时发生异常", team.getTeamName(), e);
|
||||
// 不抛出异常,避免影响主业务流程
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理指导老师角色分配(更新时使用)
|
||||
*
|
||||
* @param oldTeam 更新前的队伍信息
|
||||
* @param newTeam 更新后的队伍信息
|
||||
*/
|
||||
private void handleAdvisorRoleAssignment(StglVolunteerTeam oldTeam, StglVolunteerTeam newTeam) {
|
||||
try {
|
||||
// 获取更新前后的指导老师用户ID
|
||||
Long oldAdvisorUserId = oldTeam != null ? extractAdvisorUserId(oldTeam) : null;
|
||||
Long newAdvisorUserId = extractAdvisorUserId(newTeam);
|
||||
|
||||
// 如果指导老师发生变化
|
||||
if (!Objects.equals(oldAdvisorUserId, newAdvisorUserId)) {
|
||||
// 移除旧指导老师的角色
|
||||
if (oldAdvisorUserId != null) {
|
||||
advisorRoleAssigner.removeAdvisorRole(oldAdvisorUserId);
|
||||
log.info("志愿者队伍 {} 移除指导老师,成功移除角色,用户ID: {}", newTeam.getTeamName(), oldAdvisorUserId);
|
||||
}
|
||||
|
||||
// 为新指导老师分配角色
|
||||
if (newAdvisorUserId != null) {
|
||||
advisorRoleAssigner.assignAdvisorRole(newAdvisorUserId);
|
||||
log.info("志愿者队伍 {} 新增指导老师,成功分配角色,用户ID: {}", newTeam.getTeamName(), newAdvisorUserId);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("处理志愿者队伍 {} 的指导老师角色分配时发生异常", newTeam.getTeamName(), e);
|
||||
// 不抛出异常,避免影响主业务流程
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从志愿者队伍信息中提取指导老师用户ID
|
||||
*
|
||||
* @param team 志愿者队伍信息
|
||||
* @return 指导老师用户ID
|
||||
*/
|
||||
private Long extractAdvisorUserId(StglVolunteerTeam team) {
|
||||
// 志愿者队伍目前使用单一指导老师字段,需要根据指导老师姓名查找用户ID
|
||||
if (StringUtils.isNotBlank(team.getAdvisorName())) {
|
||||
try {
|
||||
// 这里需要根据指导老师姓名查找用户ID
|
||||
// 由于志愿者队伍表没有直接存储指导老师用户ID,需要额外的查询逻辑
|
||||
// 暂时返回null,后续可以根据需要实现
|
||||
log.debug("志愿者队伍 {} 的指导老师 {} 需要根据姓名查找用户ID", team.getTeamName(), team.getAdvisorName());
|
||||
return null;
|
||||
} catch (Exception e) {
|
||||
log.error("查找志愿者队伍 {} 指导老师用户ID时发生异常", team.getTeamName(), e);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -39,6 +39,7 @@
|
||||
<result property="fundingSources" column="funding_sources"/>
|
||||
<result property="charterUrl" column="charter_url"/>
|
||||
<result property="status" column="status"/>
|
||||
<result property="sighOpen" column="sigh_open"/>
|
||||
<result property="remark" column="remark"/>
|
||||
<result property="createBy" column="create_by"/>
|
||||
<result property="createTime" column="create_time"/>
|
||||
@@ -56,7 +57,7 @@
|
||||
c.advisor_name, c.advisor_dept, c.advisor_phone, c.advisor_email,
|
||||
c.advisors_json,
|
||||
c.member_limit, c.departments, c.recruit_cycle, c.recruit_time, c.activity_frequency, c.funding_sources,
|
||||
c.charter_url, c.status, c.remark, c.create_by, c.create_time, c.update_by, c.update_time, c.tenant_id, c.deleted,
|
||||
c.charter_url, c.status, c.sigh_open, c.remark, c.create_by, c.create_time, c.update_by, c.update_time, c.tenant_id, c.deleted,
|
||||
COALESCE(m.member_count, 0) as memberCount
|
||||
from stgl_club c
|
||||
LEFT JOIN (
|
||||
@@ -76,7 +77,7 @@
|
||||
c.vice_president_name, c.vice_president_student_no, c.vice_president_dept, c.vice_president_major, c.vice_president_class, c.vice_president_phone,
|
||||
c.advisor_name, c.advisor_dept, c.advisor_phone, c.advisor_email,
|
||||
c.member_limit, c.departments, c.recruit_cycle, c.recruit_time,
|
||||
c.activity_frequency, c.funding_sources, c.charter_url, c.status, c.remark,
|
||||
c.activity_frequency, c.funding_sources, c.charter_url, c.status, c.sigh_open, c.remark,
|
||||
c.create_by, c.create_time, c.update_by, c.update_time,
|
||||
COALESCE(m.member_count, 0) as memberCount
|
||||
FROM stgl_club c
|
||||
@@ -98,7 +99,7 @@
|
||||
c.vice_president_name, c.vice_president_student_no, c.vice_president_dept, c.vice_president_major, c.vice_president_class, c.vice_president_phone,
|
||||
c.advisor_name, c.advisor_dept, c.advisor_phone, c.advisor_email,
|
||||
c.member_limit, c.departments, c.recruit_cycle, c.recruit_time,
|
||||
c.activity_frequency, c.funding_sources, c.charter_url, c.status, c.remark,
|
||||
c.activity_frequency, c.funding_sources, c.charter_url, c.status, c.sigh_open, c.remark,
|
||||
c.create_by, c.create_time, c.update_by, c.update_time,
|
||||
COALESCE(m.member_count, 0) as memberCount
|
||||
FROM stgl_club c
|
||||
@@ -129,7 +130,7 @@
|
||||
c.advisor_name, c.advisor_dept, c.advisor_phone, c.advisor_email,
|
||||
c.advisors_json,
|
||||
c.member_limit, c.departments, c.recruit_cycle, c.recruit_time,
|
||||
c.activity_frequency, c.funding_sources, c.charter_url, c.status, c.remark,
|
||||
c.activity_frequency, c.funding_sources, c.charter_url, c.status, c.sigh_open, c.remark,
|
||||
c.create_by, c.create_time, c.update_by, c.update_time,
|
||||
COALESCE(m.member_count, 0) as memberCount
|
||||
FROM stgl_club c
|
||||
|
||||
@@ -160,7 +160,35 @@
|
||||
<if test="roleType != null">
|
||||
AND m.role_type = #{roleType}
|
||||
</if>
|
||||
<if test="status != null">
|
||||
<!-- 状态过滤:优先使用statusList多状态查询,如果没有则使用单个status -->
|
||||
<if test="params != null and params.statusList != null">
|
||||
<choose>
|
||||
<when test="params.statusList instanceof java.util.List">
|
||||
<if test="params.statusList.size() > 0">
|
||||
AND m.status IN
|
||||
<foreach collection="params.statusList" item="status" open="(" separator="," close=")">
|
||||
#{status}
|
||||
</foreach>
|
||||
</if>
|
||||
</when>
|
||||
<otherwise>
|
||||
<if test="params.statusList != null and params.statusList != ''">
|
||||
<choose>
|
||||
<when test="params.statusList.indexOf(',') > -1">
|
||||
AND m.status IN
|
||||
<foreach collection="params.statusList.split(',')" item="status" open="(" separator="," close=")">
|
||||
#{status}
|
||||
</foreach>
|
||||
</when>
|
||||
<otherwise>
|
||||
AND m.status = #{params.statusList}
|
||||
</otherwise>
|
||||
</choose>
|
||||
</if>
|
||||
</otherwise>
|
||||
</choose>
|
||||
</if>
|
||||
<if test="status != null and (params == null or params.statusList == null)">
|
||||
AND m.status = #{status}
|
||||
</if>
|
||||
<if test="clubName != null and clubName != ''">
|
||||
|
||||
@@ -99,13 +99,28 @@
|
||||
END as organization_name,
|
||||
a.instructors,
|
||||
CAST(a.activity_status AS CHAR) as activity_status,
|
||||
-- 动态计算报名状态:基于当前时间和报名时间范围
|
||||
-- 动态计算报名状态:优先根据活动状态,再根据报名时间范围
|
||||
CAST(
|
||||
CASE
|
||||
-- 首先根据活动状态强制设置(优先级最高)
|
||||
WHEN a.activity_status = '0' THEN '0' -- 草稿 → 未开始(固定)
|
||||
WHEN a.activity_status = '1' THEN '0' -- 未开始 → 未开始(固定)
|
||||
WHEN a.activity_status = '2' THEN '0' -- 待审批 → 未开始(固定)
|
||||
WHEN a.activity_status = '5' THEN '2' -- 已结束 → 报名结束(固定)
|
||||
WHEN a.activity_status = '6' THEN '3' -- 已取消 → 已关闭(固定)
|
||||
WHEN a.activity_status = '7' THEN '0' -- 已驳回 → 未开始(固定)
|
||||
|
||||
-- 然后根据活动状态和报名时间判断
|
||||
WHEN a.activity_status = '3' OR a.activity_status = '4' THEN
|
||||
CASE
|
||||
WHEN a.registration_start IS NULL OR a.registration_end IS NULL THEN a.registration_status
|
||||
WHEN NOW() < a.registration_start THEN '0'
|
||||
WHEN NOW() >= a.registration_start AND NOW() <= a.registration_end THEN '1'
|
||||
WHEN NOW() > a.registration_end THEN '2'
|
||||
<![CDATA[WHEN NOW() < a.registration_start THEN '0']]>
|
||||
<![CDATA[WHEN NOW() >= a.registration_start AND NOW() <= a.registration_end THEN '1']]>
|
||||
<![CDATA[WHEN NOW() > a.registration_end THEN '2']]>
|
||||
ELSE a.registration_status
|
||||
END
|
||||
|
||||
-- 其他情况保持原状态
|
||||
ELSE a.registration_status
|
||||
END AS CHAR
|
||||
) as registration_status,
|
||||
|
||||
@@ -130,13 +130,7 @@
|
||||
AND sar.user_id = #{userId}
|
||||
AND sar.approval_status = 1 -- 报名成功(审核通过)
|
||||
AND sa.activity_status = '4' -- 进行中
|
||||
-- 排除已有签到记录的活动
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM stgl_activity_sign sas2
|
||||
WHERE sas2.activity_id = sa.id
|
||||
AND sas2.user_id = #{userId}
|
||||
AND sas2.deleted = 0
|
||||
)
|
||||
-- 移除NOT EXISTS子查询,允许用户重复签到
|
||||
</where>
|
||||
ORDER BY sa.start_time ASC
|
||||
</select>
|
||||
@@ -152,6 +146,18 @@
|
||||
</where>
|
||||
</select>
|
||||
|
||||
<!-- 根据活动ID和用户ID查询所有签到记录(支持多次签到) -->
|
||||
<select id="selectAllByActivityAndUser" resultMap="StglActivityAttendanceResult">
|
||||
<include refid="selectStglActivityAttendanceVo"/>
|
||||
<where>
|
||||
sas.deleted = 0
|
||||
AND sa.deleted = 0
|
||||
AND sas.activity_id = #{activityId}
|
||||
AND sas.user_id = #{userId}
|
||||
</where>
|
||||
ORDER BY sas.create_time DESC
|
||||
</select>
|
||||
|
||||
<!-- 根据ID查询活动考勤签到详情(关联查询活动信息) -->
|
||||
<select id="selectVoByIdWithActivity" parameterType="Long" resultMap="StglActivityAttendanceResult">
|
||||
<include refid="selectStglActivityAttendanceVo"/>
|
||||
|
||||
@@ -56,12 +56,27 @@
|
||||
sa.poster_url, sa.related_club_id, sa.related_type, sa.organizer_name, sa.organizer_type,
|
||||
sa.organizer_contact, sa.organizer_phone, sa.co_organizers, sa.instructors,
|
||||
sa.registration_type, sa.registration_start, sa.registration_end,
|
||||
-- 动态计算报名状态:基于当前时间和报名时间范围
|
||||
-- 动态计算报名状态:优先根据活动状态,再根据报名时间范围
|
||||
CASE
|
||||
-- 首先根据活动状态强制设置(优先级最高)
|
||||
WHEN sa.activity_status = '0' THEN '0' -- 草稿 → 未开始(固定)
|
||||
WHEN sa.activity_status = '1' THEN '0' -- 未开始 → 未开始(固定)
|
||||
WHEN sa.activity_status = '2' THEN '0' -- 待审批 → 未开始(固定)
|
||||
WHEN sa.activity_status = '5' THEN '2' -- 已结束 → 报名结束(固定)
|
||||
WHEN sa.activity_status = '6' THEN '3' -- 已取消 → 已关闭(固定)
|
||||
WHEN sa.activity_status = '7' THEN '0' -- 已驳回 → 未开始(固定)
|
||||
|
||||
-- 然后根据活动状态和报名时间判断
|
||||
WHEN sa.activity_status = '3' OR sa.activity_status = '4' THEN
|
||||
CASE
|
||||
WHEN sa.registration_start IS NULL OR sa.registration_end IS NULL THEN sa.registration_status
|
||||
WHEN NOW() < sa.registration_start THEN '0'
|
||||
WHEN NOW() >= sa.registration_start AND NOW() <= sa.registration_end THEN '1'
|
||||
WHEN NOW() > sa.registration_end THEN '2'
|
||||
<![CDATA[WHEN NOW() < sa.registration_start THEN '0']]>
|
||||
<![CDATA[WHEN NOW() >= sa.registration_start AND NOW() <= sa.registration_end THEN '1']]>
|
||||
<![CDATA[WHEN NOW() > sa.registration_end THEN '2']]>
|
||||
ELSE sa.registration_status
|
||||
END
|
||||
|
||||
-- 其他情况保持原状态
|
||||
ELSE sa.registration_status
|
||||
END AS registration_status,
|
||||
sa.max_participants, sa.max_teams, sa.team_size_min, sa.team_size_max, sa.need_approval,
|
||||
@@ -119,11 +134,26 @@
|
||||
</if>
|
||||
<if test="registrationStatus != null">
|
||||
AND (
|
||||
CASE
|
||||
-- 首先根据活动状态强制设置(优先级最高)
|
||||
WHEN sa.activity_status = '0' THEN '0' -- 草稿 → 未开始(固定)
|
||||
WHEN sa.activity_status = '1' THEN '0' -- 未开始 → 未开始(固定)
|
||||
WHEN sa.activity_status = '2' THEN '0' -- 待审批 → 未开始(固定)
|
||||
WHEN sa.activity_status = '5' THEN '2' -- 已结束 → 报名结束(固定)
|
||||
WHEN sa.activity_status = '6' THEN '3' -- 已取消 → 已关闭(固定)
|
||||
WHEN sa.activity_status = '7' THEN '0' -- 已驳回 → 未开始(固定)
|
||||
|
||||
-- 然后根据活动状态和报名时间判断
|
||||
WHEN sa.activity_status = '3' OR sa.activity_status = '4' THEN
|
||||
CASE
|
||||
WHEN sa.registration_start IS NULL OR sa.registration_end IS NULL THEN sa.registration_status
|
||||
WHEN NOW() < sa.registration_start THEN '0'
|
||||
WHEN NOW() >= sa.registration_start AND NOW() <= sa.registration_end THEN '1'
|
||||
WHEN NOW() > sa.registration_end THEN '2'
|
||||
<![CDATA[WHEN NOW() < sa.registration_start THEN '0']]>
|
||||
<![CDATA[WHEN NOW() >= sa.registration_start AND NOW() <= sa.registration_end THEN '1']]>
|
||||
<![CDATA[WHEN NOW() > sa.registration_end THEN '2']]>
|
||||
ELSE sa.registration_status
|
||||
END
|
||||
|
||||
-- 其他情况保持原状态
|
||||
ELSE sa.registration_status
|
||||
END
|
||||
) = #{registrationStatus}
|
||||
@@ -186,11 +216,26 @@
|
||||
</if>
|
||||
<if test="activity.registrationStatus != null and activity.registrationStatus != ''">
|
||||
AND (
|
||||
CASE
|
||||
-- 首先根据活动状态强制设置(优先级最高)
|
||||
WHEN sa.activity_status = '0' THEN '0' -- 草稿 → 未开始(固定)
|
||||
WHEN sa.activity_status = '1' THEN '0' -- 未开始 → 未开始(固定)
|
||||
WHEN sa.activity_status = '2' THEN '0' -- 待审批 → 未开始(固定)
|
||||
WHEN sa.activity_status = '5' THEN '2' -- 已结束 → 报名结束(固定)
|
||||
WHEN sa.activity_status = '6' THEN '3' -- 已取消 → 已关闭(固定)
|
||||
WHEN sa.activity_status = '7' THEN '0' -- 已驳回 → 未开始(固定)
|
||||
|
||||
-- 然后根据活动状态和报名时间判断
|
||||
WHEN sa.activity_status = '3' OR sa.activity_status = '4' THEN
|
||||
CASE
|
||||
WHEN sa.registration_start IS NULL OR sa.registration_end IS NULL THEN sa.registration_status
|
||||
WHEN NOW() < sa.registration_start THEN '0'
|
||||
WHEN NOW() >= sa.registration_start AND NOW() <= sa.registration_end THEN '1'
|
||||
WHEN NOW() > sa.registration_end THEN '2'
|
||||
<![CDATA[WHEN NOW() < sa.registration_start THEN '0']]>
|
||||
<![CDATA[WHEN NOW() >= sa.registration_start AND NOW() <= sa.registration_end THEN '1']]>
|
||||
<![CDATA[WHEN NOW() > sa.registration_end THEN '2']]>
|
||||
ELSE sa.registration_status
|
||||
END
|
||||
|
||||
-- 其他情况保持原状态
|
||||
ELSE sa.registration_status
|
||||
END
|
||||
) = #{activity.registrationStatus}
|
||||
@@ -198,11 +243,26 @@
|
||||
<!-- 支持报名状态IN查询(用于活动报名大厅) -->
|
||||
<if test="activity.params != null and activity.params.registrationStatusList != null and activity.params.registrationStatusList.size() > 0">
|
||||
AND (
|
||||
CASE
|
||||
-- 首先根据活动状态强制设置(优先级最高)
|
||||
WHEN sa.activity_status = '0' THEN '0' -- 草稿 → 未开始(固定)
|
||||
WHEN sa.activity_status = '1' THEN '0' -- 未开始 → 未开始(固定)
|
||||
WHEN sa.activity_status = '2' THEN '0' -- 待审批 → 未开始(固定)
|
||||
WHEN sa.activity_status = '5' THEN '2' -- 已结束 → 报名结束(固定)
|
||||
WHEN sa.activity_status = '6' THEN '3' -- 已取消 → 已关闭(固定)
|
||||
WHEN sa.activity_status = '7' THEN '0' -- 已驳回 → 未开始(固定)
|
||||
|
||||
-- 然后根据活动状态和报名时间判断
|
||||
WHEN sa.activity_status = '3' OR sa.activity_status = '4' THEN
|
||||
CASE
|
||||
WHEN sa.registration_start IS NULL OR sa.registration_end IS NULL THEN sa.registration_status
|
||||
WHEN NOW() < sa.registration_start THEN '0'
|
||||
WHEN NOW() >= sa.registration_start AND NOW() <= sa.registration_end THEN '1'
|
||||
WHEN NOW() > sa.registration_end THEN '2'
|
||||
<![CDATA[WHEN NOW() < sa.registration_start THEN '0']]>
|
||||
<![CDATA[WHEN NOW() >= sa.registration_start AND NOW() <= sa.registration_end THEN '1']]>
|
||||
<![CDATA[WHEN NOW() > sa.registration_end THEN '2']]>
|
||||
ELSE sa.registration_status
|
||||
END
|
||||
|
||||
-- 其他情况保持原状态
|
||||
ELSE sa.registration_status
|
||||
END
|
||||
) IN
|
||||
|
||||
@@ -97,6 +97,7 @@
|
||||
|
||||
| 日期 | 版本 | 更新内容 | 更新人 |
|
||||
|------|------|---------|-------|
|
||||
| 2025-01-03 | 1.1 | 社团管理API更新:删除副社长信息卡片,role_type字段改为字符串类型 | 系统管理员 |
|
||||
| 2025-07-01 | 1.0 | 初始版本 | 系统管理员 |
|
||||
| 2025-07-15 | 1.1 | 添加心理健康测评API | 张工 |
|
||||
| 2025-07-30 | 1.2 | 添加预约限制功能API | Cursor |
|
||||
|
||||
@@ -107,7 +107,7 @@ CREATE TABLE `stgl_member` (
|
||||
`user_id` bigint(20) NOT NULL COMMENT '用户ID',
|
||||
`student_no` varchar(20) COMMENT '学号',
|
||||
`student_name` varchar(50) COMMENT '学生姓名',
|
||||
`role_type` tinyint COMMENT '角色类型(1-社长 2-副社长 3-成员)',
|
||||
`role_type` varchar(20) COMMENT '角色类型(社长、副社长、部长、普通成员)',
|
||||
`join_time` datetime COMMENT '加入时间',
|
||||
`status` tinyint DEFAULT 1 COMMENT '状态(1-正常 0-退出)',
|
||||
`remark` varchar(500) DEFAULT NULL COMMENT '备注',
|
||||
|
||||
@@ -395,6 +395,31 @@ export const api = {
|
||||
url: '/auth/logout',
|
||||
method: 'POST',
|
||||
}),
|
||||
|
||||
// 修改密码
|
||||
resetPassword: (data) =>
|
||||
request({
|
||||
url: '/system/user/profile/updatePwd',
|
||||
method: 'PUT',
|
||||
header: {
|
||||
isEncrypt: 'true', // 修复:使用字符串 'true' 而不是布尔值 true
|
||||
repeatSubmit: false,
|
||||
},
|
||||
data: {
|
||||
oldPassword: data.oldPassword,
|
||||
newPassword: data.newPassword,
|
||||
},
|
||||
}),
|
||||
|
||||
// 更新用户头像
|
||||
// updateUserAvatar: (data) =>
|
||||
// request({
|
||||
// url: '/system/user/profile/updateAvatar',
|
||||
// method: 'PUT',
|
||||
// data: {
|
||||
// avatar: data.avatar,
|
||||
// },
|
||||
// }),
|
||||
};
|
||||
|
||||
// 便捷请求方法封装
|
||||
|
||||
@@ -294,6 +294,14 @@
|
||||
"navigationBarBackgroundColor": "#1890ff",
|
||||
"navigationBarTextStyle": "white"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/user/resetPasswd",
|
||||
"style": {
|
||||
"navigationBarTitleText": "修改密码",
|
||||
"navigationBarBackgroundColor": "#0051d9",
|
||||
"navigationBarTextStyle": "white"
|
||||
}
|
||||
}
|
||||
],
|
||||
"globalStyle": {
|
||||
|
||||
@@ -460,7 +460,7 @@
|
||||
</Card> -->
|
||||
|
||||
<!-- 系统管理 -->
|
||||
<!-- <Card v-if="hasSystemServices">
|
||||
<Card>
|
||||
<template #header>
|
||||
<view class="section-header">
|
||||
<u-icon name="setting" size="18" color="#f5222d" />
|
||||
@@ -468,7 +468,27 @@
|
||||
</view>
|
||||
</template>
|
||||
<GridLayout :columns="3" gap="16px">
|
||||
<ServiceItem
|
||||
<ServiceItem title="修改密码" @click="gotoResetPassword">
|
||||
<template #icon>
|
||||
<image
|
||||
src="/static/00118.png"
|
||||
mode="aspectFit"
|
||||
style="width: 24px; height: 24px"
|
||||
/>
|
||||
</template>
|
||||
</ServiceItem>
|
||||
|
||||
<!-- <ServiceItem title="修改头像" @click="gotoChangeAvatar">
|
||||
<template #icon>
|
||||
<image
|
||||
src="/static/password.png"
|
||||
mode="aspectFit"
|
||||
style="width: 24px; height: 24px"
|
||||
/>
|
||||
</template>
|
||||
</ServiceItem> -->
|
||||
|
||||
<!-- <ServiceItem
|
||||
v-if="hasPermi(['system:mobileuser:list'])"
|
||||
title="用户管理"
|
||||
@click="handleServiceClick"
|
||||
@@ -530,9 +550,9 @@
|
||||
<template #icon>
|
||||
<u-icon name="wifi" size="24" />
|
||||
</template>
|
||||
</ServiceItem>
|
||||
</ServiceItem> -->
|
||||
</GridLayout>
|
||||
</Card> -->
|
||||
</Card>
|
||||
|
||||
<!-- 工作流 -->
|
||||
<Card v-if="hasWorkflowServices">
|
||||
@@ -946,6 +966,20 @@ export default {
|
||||
});
|
||||
},
|
||||
|
||||
// 跳转到修改密码页面
|
||||
gotoResetPassword() {
|
||||
uni.navigateTo({
|
||||
url: '/pages/user/resetPasswd',
|
||||
});
|
||||
},
|
||||
|
||||
// 跳转到修改头像页面
|
||||
gotoChangeAvatar() {
|
||||
uni.navigateTo({
|
||||
url: '/pages/user/changeaAvatar',
|
||||
});
|
||||
},
|
||||
|
||||
// 图片加载成功处理
|
||||
handleImageLoad(event) {
|
||||
console.log('✅ 轮播图片加载成功:', event.detail);
|
||||
|
||||
@@ -11,7 +11,9 @@
|
||||
></u-avatar>
|
||||
</view>
|
||||
<view class="info-section">
|
||||
<view class="user-name">{{ userInfo.nickName || userInfo.userName || '学生' }}</view>
|
||||
<view class="user-name">
|
||||
{{ userInfo.nickName || userInfo.userName || '学生' }}
|
||||
</view>
|
||||
<view class="user-detail">学号:{{ userInfo.userName || '暂无' }}</view>
|
||||
<view class="user-detail">部门:{{ userInfo.deptName || '暂无' }}</view>
|
||||
</view>
|
||||
@@ -35,11 +37,22 @@
|
||||
</view>
|
||||
<view class="info-row">
|
||||
<text class="label">实习期间:</text>
|
||||
<text class="value">{{ formatDateRange(internshipInfo.internshipStartDate, internshipInfo.internshipEndDate) }}</text>
|
||||
<text class="value">
|
||||
{{
|
||||
formatDateRange(
|
||||
internshipInfo.internshipStartDate,
|
||||
internshipInfo.internshipEndDate,
|
||||
)
|
||||
}}
|
||||
</text>
|
||||
</view>
|
||||
<view class="info-row">
|
||||
<text class="label">状态:</text>
|
||||
<u-tag :text="getStatusText(internshipInfo.internshipStatus)" :type="getStatusType(internshipInfo.internshipStatus)" size="mini"></u-tag>
|
||||
<u-tag
|
||||
:text="getStatusText(internshipInfo.internshipStatus)"
|
||||
:type="getStatusType(internshipInfo.internshipStatus)"
|
||||
size="mini"
|
||||
></u-tag>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -78,7 +91,7 @@
|
||||
@click="handleCheckin"
|
||||
:custom-style="checkinButtonStyle"
|
||||
>
|
||||
<u-icon name="map" size="20" color="#fff" style="margin-right: 8px;"></u-icon>
|
||||
<u-icon name="map" size="20" color="#fff" style="margin-right: 8px"></u-icon>
|
||||
{{ checkinLoading ? '签到中...' : '立即签到' }}
|
||||
</u-button>
|
||||
|
||||
@@ -93,7 +106,6 @@
|
||||
<view class="location-card">
|
||||
<view class="card-header">
|
||||
<view class="header-left">
|
||||
|
||||
<text class="card-title">签到位置</text>
|
||||
</view>
|
||||
</view>
|
||||
@@ -120,15 +132,24 @@
|
||||
<!-- 坐标信息 -->
|
||||
<view class="location-coords-container">
|
||||
<text class="location-coords">
|
||||
经纬度:{{ currentLocation.latitude.toFixed(6) }},{{ currentLocation.longitude.toFixed(6) }}
|
||||
经纬度:{{ currentLocation.latitude.toFixed(6) }},{{
|
||||
currentLocation.longitude.toFixed(6)
|
||||
}}
|
||||
</text>
|
||||
</view>
|
||||
|
||||
<!-- 精度信息 -->
|
||||
<view class="accuracy-section" v-if="currentLocation.accuracy">
|
||||
<view class="accuracy-info">
|
||||
<u-icon name="target" size="14" color="#67c23a" class="accuracy-icon"></u-icon>
|
||||
<text class="accuracy-text">定位精度 {{ currentLocation.accuracy.toFixed(0) }}米</text>
|
||||
<u-icon
|
||||
name="target"
|
||||
size="14"
|
||||
color="#67c23a"
|
||||
class="accuracy-icon"
|
||||
></u-icon>
|
||||
<text class="accuracy-text">
|
||||
定位精度 {{ currentLocation.accuracy.toFixed(0) }}米
|
||||
</text>
|
||||
<u-tag
|
||||
:text="getAccuracyStatus(currentLocation.accuracy).text"
|
||||
:type="getAccuracyStatus(currentLocation.accuracy).type"
|
||||
@@ -173,7 +194,12 @@
|
||||
|
||||
<view class="error-content">
|
||||
<view class="error-header">
|
||||
<u-icon name="warning-circle" size="28" color="#e6a23c" class="error-icon"></u-icon>
|
||||
<u-icon
|
||||
name="warning-circle"
|
||||
size="28"
|
||||
color="#e6a23c"
|
||||
class="error-icon"
|
||||
></u-icon>
|
||||
<text class="error-title">无法获取位置信息</text>
|
||||
</view>
|
||||
|
||||
@@ -183,15 +209,30 @@
|
||||
<text class="tips-title">签到需要位置信息,请确保:</text>
|
||||
<view class="tips-list">
|
||||
<view class="tip-item">
|
||||
<u-icon name="checkmark-circle" size="14" color="#67c23a" class="tip-icon"></u-icon>
|
||||
<u-icon
|
||||
name="checkmark-circle"
|
||||
size="14"
|
||||
color="#67c23a"
|
||||
class="tip-icon"
|
||||
></u-icon>
|
||||
<text class="tip-text">已开启位置权限</text>
|
||||
</view>
|
||||
<view class="tip-item">
|
||||
<u-icon name="checkmark-circle" size="14" color="#67c23a" class="tip-icon"></u-icon>
|
||||
<u-icon
|
||||
name="checkmark-circle"
|
||||
size="14"
|
||||
color="#67c23a"
|
||||
class="tip-icon"
|
||||
></u-icon>
|
||||
<text class="tip-text">GPS/网络定位已开启</text>
|
||||
</view>
|
||||
<view class="tip-item">
|
||||
<u-icon name="checkmark-circle" size="14" color="#67c23a" class="tip-icon"></u-icon>
|
||||
<u-icon
|
||||
name="checkmark-circle"
|
||||
size="14"
|
||||
color="#67c23a"
|
||||
class="tip-icon"
|
||||
></u-icon>
|
||||
<text class="tip-text">网络连接正常</text>
|
||||
</view>
|
||||
</view>
|
||||
@@ -226,7 +267,7 @@
|
||||
</view>
|
||||
|
||||
<!-- 最近签到记录 -->
|
||||
<view class="records-card" style="display: none;">
|
||||
<view class="records-card" style="display: none">
|
||||
<view class="card-header">
|
||||
<u-icon name="list" size="18" color="#909399"></u-icon>
|
||||
<text class="card-title">最近签到</text>
|
||||
@@ -259,8 +300,18 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getInternshipInfo, getStudentInternshipInfo, checkIn, getDailyList, getStatistics } from '@/api/internship.js';
|
||||
import { getCurrentLocation, checkLocationPermission, openLocationSettings } from '@/utils/location.js';
|
||||
import {
|
||||
getInternshipInfo,
|
||||
getStudentInternshipInfo,
|
||||
checkIn,
|
||||
getDailyList,
|
||||
getStatistics,
|
||||
} from '@/api/internship.js';
|
||||
import {
|
||||
getCurrentLocation,
|
||||
checkLocationPermission,
|
||||
openLocationSettings,
|
||||
} from '@/utils/location.js';
|
||||
import { userManager } from '@/utils/user.js';
|
||||
import weChatSDK from '@/utils/wechat.js';
|
||||
import { request } from '@/common/request.js';
|
||||
@@ -295,7 +346,7 @@ export default {
|
||||
totalDays: 0,
|
||||
checkedDays: 0,
|
||||
missedDays: 0,
|
||||
exceptionDays: 0
|
||||
exceptionDays: 0,
|
||||
},
|
||||
|
||||
// 签到记录
|
||||
@@ -312,8 +363,8 @@ export default {
|
||||
background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
|
||||
border: 'none',
|
||||
fontSize: '16px',
|
||||
fontWeight: 'bold'
|
||||
}
|
||||
fontWeight: 'bold',
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
@@ -386,7 +437,7 @@ export default {
|
||||
if (!this.userInfo.userId) {
|
||||
uni.showToast({
|
||||
title: '请先登录',
|
||||
icon: 'none'
|
||||
icon: 'none',
|
||||
});
|
||||
return;
|
||||
}
|
||||
@@ -404,19 +455,18 @@ export default {
|
||||
this.loadInternshipInfo(),
|
||||
this.loadTodayRecord(),
|
||||
this.loadRecentRecords(),
|
||||
this.loadStatistics()
|
||||
this.loadStatistics(),
|
||||
]);
|
||||
|
||||
// 初始化位置信息(不阻塞页面加载)
|
||||
this.initLocation().catch(error => {
|
||||
this.initLocation().catch((error) => {
|
||||
console.error('位置信息初始化失败:', error);
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('初始化页面失败:', error);
|
||||
uni.showToast({
|
||||
title: '数据加载失败',
|
||||
icon: 'none'
|
||||
icon: 'none',
|
||||
});
|
||||
}
|
||||
},
|
||||
@@ -468,7 +518,10 @@ export default {
|
||||
|
||||
if (response.code === 200) {
|
||||
this.internshipInfo = response.data;
|
||||
console.log('✅ 实习信息获取成功:', JSON.stringify(this.internshipInfo, null, 2));
|
||||
console.log(
|
||||
'✅ 实习信息获取成功:',
|
||||
JSON.stringify(this.internshipInfo, null, 2),
|
||||
);
|
||||
|
||||
// 详细字段检查
|
||||
if (this.internshipInfo) {
|
||||
@@ -510,7 +563,7 @@ export default {
|
||||
uni.showToast({
|
||||
title: message,
|
||||
icon: 'none',
|
||||
duration: 3000
|
||||
duration: 3000,
|
||||
});
|
||||
},
|
||||
|
||||
@@ -524,7 +577,7 @@ export default {
|
||||
studentId: this.userInfo.userId,
|
||||
checkDate: this.formatDate(today),
|
||||
pageNum: 1,
|
||||
pageSize: 1
|
||||
pageSize: 1,
|
||||
};
|
||||
|
||||
const response = await getDailyList(params);
|
||||
@@ -551,7 +604,7 @@ export default {
|
||||
const params = {
|
||||
studentId: this.userInfo.userId,
|
||||
pageNum: 1,
|
||||
pageSize: 5
|
||||
pageSize: 5,
|
||||
};
|
||||
|
||||
const response = await getDailyList(params);
|
||||
@@ -572,7 +625,7 @@ export default {
|
||||
const params = {
|
||||
studentId: this.userInfo.userId,
|
||||
year: now.getFullYear(),
|
||||
month: now.getMonth() + 1
|
||||
month: now.getMonth() + 1,
|
||||
};
|
||||
|
||||
const response = await getStatistics(params);
|
||||
@@ -684,7 +737,6 @@ export default {
|
||||
|
||||
// 优先级4:uni-app通用定位(最后的回退方案)
|
||||
await this.handleFallbackLocation();
|
||||
|
||||
} catch (error) {
|
||||
console.error('智能定位流程出错:', error);
|
||||
this.updateLocationError(error.message || '定位失败');
|
||||
@@ -701,7 +753,7 @@ export default {
|
||||
// 用户选择重试
|
||||
this.handleGetLocation();
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
} finally {
|
||||
this.locationLoading = false;
|
||||
@@ -730,7 +782,9 @@ export default {
|
||||
latitude: parseFloat(result.latitude),
|
||||
longitude: parseFloat(result.longitude),
|
||||
accuracy: result.accuracy || 50,
|
||||
address: `${parseFloat(result.latitude).toFixed(6)}, ${parseFloat(result.longitude).toFixed(6)}`
|
||||
address: `${parseFloat(result.latitude).toFixed(6)}, ${parseFloat(
|
||||
result.longitude,
|
||||
).toFixed(6)}`,
|
||||
};
|
||||
|
||||
this.updateLocationSuccess();
|
||||
@@ -793,7 +847,7 @@ export default {
|
||||
latitude: latitude,
|
||||
longitude: longitude,
|
||||
accuracy: accuracy || 100,
|
||||
address: `${latitude.toFixed(6)}, ${longitude.toFixed(6)}`
|
||||
address: `${latitude.toFixed(6)}, ${longitude.toFixed(6)}`,
|
||||
};
|
||||
|
||||
this.updateLocationSuccess();
|
||||
@@ -865,7 +919,9 @@ export default {
|
||||
latitude: res.latitude,
|
||||
longitude: res.longitude,
|
||||
accuracy: res.accuracy || 100,
|
||||
address: res.address || `${res.latitude.toFixed(6)}, ${res.longitude.toFixed(6)}`
|
||||
address:
|
||||
res.address ||
|
||||
`${res.latitude.toFixed(6)}, ${res.longitude.toFixed(6)}`,
|
||||
};
|
||||
|
||||
this.updateLocationSuccess();
|
||||
@@ -873,7 +929,7 @@ export default {
|
||||
uni.showToast({
|
||||
title: '定位成功',
|
||||
icon: 'success',
|
||||
duration: 2000
|
||||
duration: 2000,
|
||||
});
|
||||
resolve(true);
|
||||
},
|
||||
@@ -893,7 +949,7 @@ export default {
|
||||
uni.showToast({
|
||||
title: errorMsg,
|
||||
icon: 'none',
|
||||
duration: 3000
|
||||
duration: 3000,
|
||||
});
|
||||
resolve(false);
|
||||
},
|
||||
@@ -901,10 +957,6 @@ export default {
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 更新定位成功状态
|
||||
*/
|
||||
@@ -935,8 +987,6 @@ export default {
|
||||
this.handleGetLocation();
|
||||
},
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 打开位置信息在地图中查看(参考请假页面实现)
|
||||
*/
|
||||
@@ -1029,7 +1079,7 @@ export default {
|
||||
// 首先尝试使用uni.openLocation(原生地图)
|
||||
if (typeof uni.openLocation === 'function') {
|
||||
uni.showLoading({
|
||||
title: '打开地图中...'
|
||||
title: '打开地图中...',
|
||||
});
|
||||
|
||||
uni.openLocation({
|
||||
@@ -1046,7 +1096,7 @@ export default {
|
||||
console.error('uni.openLocation打开地图失败:', error);
|
||||
uni.hideLoading();
|
||||
this.showCopyCoordinatesOption(latitude, longitude);
|
||||
}
|
||||
},
|
||||
});
|
||||
} else {
|
||||
// 如果uni.openLocation不可用,显示复制坐标选项
|
||||
@@ -1058,7 +1108,8 @@ export default {
|
||||
showCopyCoordinatesOption(latitude, longitude) {
|
||||
uni.showModal({
|
||||
title: '无法打开地图',
|
||||
content: '当前环境不支持地图功能,是否复制坐标到剪贴板?\n您可以在其他地图应用中粘贴坐标查看位置。',
|
||||
content:
|
||||
'当前环境不支持地图功能,是否复制坐标到剪贴板?\n您可以在其他地图应用中粘贴坐标查看位置。',
|
||||
showCancel: true,
|
||||
cancelText: '取消',
|
||||
confirmText: '复制坐标',
|
||||
@@ -1069,12 +1120,12 @@ export default {
|
||||
success: () => {
|
||||
uni.showToast({
|
||||
title: '坐标已复制',
|
||||
icon: 'success'
|
||||
icon: 'success',
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
@@ -1090,7 +1141,7 @@ export default {
|
||||
if (!this.internshipInfo) {
|
||||
uni.showToast({
|
||||
title: '暂无实习信息',
|
||||
icon: 'none'
|
||||
icon: 'none',
|
||||
});
|
||||
return;
|
||||
}
|
||||
@@ -1105,7 +1156,7 @@ export default {
|
||||
if (res.confirm) {
|
||||
this.handleGetLocation();
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
return;
|
||||
}
|
||||
@@ -1115,11 +1166,13 @@ export default {
|
||||
const confirmResult = await new Promise((resolve) => {
|
||||
uni.showModal({
|
||||
title: '定位精度提醒',
|
||||
content: `当前定位精度较低(${this.currentLocation.accuracy.toFixed(0)}米),可能影响签到准确性,是否继续签到?`,
|
||||
content: `当前定位精度较低(${this.currentLocation.accuracy.toFixed(
|
||||
0,
|
||||
)}米),可能影响签到准确性,是否继续签到?`,
|
||||
confirmText: '继续签到',
|
||||
cancelText: '重新定位',
|
||||
success: (res) => resolve(res.confirm),
|
||||
fail: () => resolve(false)
|
||||
fail: () => resolve(false),
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1147,14 +1200,14 @@ export default {
|
||||
dailySummary: '移动端签到打卡', // 后端映射为 workContent
|
||||
|
||||
status: '1', // 正常状态
|
||||
isException: '0' // 正常,无异常
|
||||
isException: '0', // 正常,无异常
|
||||
};
|
||||
|
||||
console.log('🔍 签到前检查:', {
|
||||
internshipInfo: this.internshipInfo,
|
||||
userInfo: this.userInfo,
|
||||
currentLocation: this.currentLocation,
|
||||
canCheckin: this.canCheckin
|
||||
canCheckin: this.canCheckin,
|
||||
});
|
||||
console.log('📤 发送签到数据:', JSON.stringify(checkinData, null, 2));
|
||||
|
||||
@@ -1163,27 +1216,25 @@ export default {
|
||||
if (response.code === 200) {
|
||||
uni.showToast({
|
||||
title: '签到成功',
|
||||
icon: 'success'
|
||||
icon: 'success',
|
||||
});
|
||||
|
||||
// 刷新今日记录
|
||||
await this.loadTodayRecord();
|
||||
await this.loadRecentRecords();
|
||||
await this.loadStatistics();
|
||||
|
||||
} else {
|
||||
throw new Error(response.msg || '签到失败');
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 签到详细错误:', {
|
||||
message: error.message,
|
||||
response: error.response,
|
||||
stack: error.stack
|
||||
stack: error.stack,
|
||||
});
|
||||
uni.showToast({
|
||||
title: error.message || '签到失败',
|
||||
icon: 'none'
|
||||
icon: 'none',
|
||||
});
|
||||
} finally {
|
||||
this.checkinLoading = false;
|
||||
@@ -1197,7 +1248,7 @@ export default {
|
||||
// 可以跳转到详细记录页面
|
||||
uni.showToast({
|
||||
title: '功能开发中',
|
||||
icon: 'none'
|
||||
icon: 'none',
|
||||
});
|
||||
},
|
||||
|
||||
@@ -1226,10 +1277,10 @@ export default {
|
||||
*/
|
||||
getStatusText(status) {
|
||||
const statusMap = {
|
||||
'1': '进行中',
|
||||
'2': '已完成',
|
||||
'3': '已暂停',
|
||||
'4': '已终止'
|
||||
1: '进行中',
|
||||
2: '已完成',
|
||||
3: '已暂停',
|
||||
4: '已终止',
|
||||
};
|
||||
return statusMap[status] || '未实习';
|
||||
},
|
||||
@@ -1239,10 +1290,10 @@ export default {
|
||||
*/
|
||||
getStatusType(status) {
|
||||
const typeMap = {
|
||||
'1': 'success', // 进行中
|
||||
'2': 'info', // 已完成
|
||||
'3': 'warning', // 已暂停
|
||||
'4': 'error' // 已终止
|
||||
1: 'success', // 进行中
|
||||
2: 'info', // 已完成
|
||||
3: 'warning', // 已暂停
|
||||
4: 'error', // 已终止
|
||||
};
|
||||
return typeMap[status] || 'default';
|
||||
},
|
||||
@@ -1265,15 +1316,14 @@ export default {
|
||||
*/
|
||||
async loadUserInfo() {
|
||||
try {
|
||||
const userInfo = userManager.getUserInfo();
|
||||
const userInfo = userManager.getCurrentUser();
|
||||
if (userInfo) {
|
||||
this.userInfo = userInfo;
|
||||
console.log('用户信息加载成功:', userInfo);
|
||||
} else {
|
||||
console.warn('未获取到用户信息');
|
||||
// 尝试重新获取
|
||||
await userManager.refreshUserInfo();
|
||||
this.userInfo = userManager.getUserInfo() || {};
|
||||
// 用户信息为空,使用默认值
|
||||
this.userInfo = {};
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载用户信息失败:', error);
|
||||
@@ -1327,11 +1377,10 @@ export default {
|
||||
|
||||
// 检查今日签到状态
|
||||
await this.checkTodayCheckInStatus();
|
||||
|
||||
} catch (error) {
|
||||
console.error('数据刷新失败:', error);
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -1361,25 +1410,25 @@ export default {
|
||||
// this.checkIsWechatBrowser() 在需要时调用
|
||||
|
||||
// 获取用户信息(不阻塞)
|
||||
this.loadUserInfo().catch(error => {
|
||||
this.loadUserInfo().catch((error) => {
|
||||
console.error('用户信息加载失败:', error);
|
||||
});
|
||||
|
||||
// 获取实习信息(不阻塞)
|
||||
this.loadInternshipInfo().catch(error => {
|
||||
this.loadInternshipInfo().catch((error) => {
|
||||
console.error('实习信息加载失败:', error);
|
||||
});
|
||||
|
||||
// 初始化位置权限检查(不阻塞页面显示)
|
||||
this.initLocation().catch(error => {
|
||||
this.initLocation().catch((error) => {
|
||||
console.error('位置信息初始化失败:', error);
|
||||
});
|
||||
|
||||
// 检查今日签到状态(不阻塞)
|
||||
this.checkTodayCheckInStatus().catch(error => {
|
||||
this.checkTodayCheckInStatus().catch((error) => {
|
||||
console.error('签到状态检查失败:', error);
|
||||
});
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
|
||||
@@ -0,0 +1,932 @@
|
||||
<template>
|
||||
<view class="page-container">
|
||||
<!-- 页面头部 -->
|
||||
<u-navbar :title="pageTitle" :border-bottom="false" bg-color="#3c9cff" title-color="#fff">
|
||||
<view slot="left" @click="goBack">
|
||||
<u-icon name="arrow-left" color="#fff" size="22"></u-icon>
|
||||
</view>
|
||||
</u-navbar>
|
||||
|
||||
<!-- 加载状态 -->
|
||||
<u-loading-page :loading="loading" loading-text="加载中..."></u-loading-page>
|
||||
|
||||
<!-- 主要内容 -->
|
||||
<view v-if="!loading" class="main-content">
|
||||
<view class="avatar-container">
|
||||
<!-- 当前头像预览 -->
|
||||
<view class="current-avatar-section">
|
||||
<text class="section-title">当前头像</text>
|
||||
<view class="avatar-preview">
|
||||
<image
|
||||
:src="currentAvatar"
|
||||
mode="aspectFill"
|
||||
class="avatar-image"
|
||||
@error="handleAvatarError"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 新头像预览区域 -->
|
||||
<view class="new-avatar-section">
|
||||
<text class="section-title">新头像预览</text>
|
||||
|
||||
<!-- 加载状态 -->
|
||||
<view v-if="avatarLoading || wechatImageLoading" class="avatar-loading">
|
||||
<u-loading-icon mode="circle" size="32"></u-loading-icon>
|
||||
<text class="loading-text">
|
||||
{{ wechatImageLoading ? '正在处理图片...' : '加载中...' }}
|
||||
</text>
|
||||
<text v-if="uploadProgress > 0" class="progress-text">
|
||||
上传进度: {{ uploadProgress }}%
|
||||
</text>
|
||||
</view>
|
||||
|
||||
<!-- 已有新头像 - 点击可预览或重新选择 -->
|
||||
<view v-else-if="newAvatar" class="avatar-wrapper" @click="handleAvatarClick">
|
||||
<image
|
||||
:src="newAvatar"
|
||||
mode="aspectFill"
|
||||
class="avatar-image"
|
||||
@error="handleAvatarError"
|
||||
/>
|
||||
|
||||
<!-- 头像操作遮罩 -->
|
||||
<view class="avatar-overlay">
|
||||
<view class="overlay-content">
|
||||
<u-icon
|
||||
name="camera-fill"
|
||||
color="rgba(255,255,255,0.9)"
|
||||
size="24"
|
||||
></u-icon>
|
||||
<text class="overlay-text">点击更换头像</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 头像角标 -->
|
||||
<view class="avatar-badge">
|
||||
<u-icon name="checkmark-circle-fill" color="#52c41a" size="20"></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 空状态 - 点击选择头像 -->
|
||||
<view v-else class="avatar-placeholder" @click="handlePhotoUpload">
|
||||
<text class="placeholder-text">选择新头像</text>
|
||||
<text class="placeholder-note">点击选择图片</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 快捷操作按钮 -->
|
||||
<view class="avatar-quick-actions">
|
||||
<view class="placeholder-tips">
|
||||
<text class="tip-item">
|
||||
• 建议尺寸:200x200像素 • 支持格式:JPG、PNG • 文件大小:不超过10MB
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 提交按钮 -->
|
||||
<view v-if="newAvatar" class="submit-section">
|
||||
<button
|
||||
class="submit-btn"
|
||||
:class="{ 'btn-loading': isSubmitting }"
|
||||
:disabled="isSubmitting"
|
||||
@click="handleSubmit"
|
||||
>
|
||||
<u-icon v-if="isSubmitting" name="loading" size="16" color="#ffffff" />
|
||||
<text class="btn-text">{{ isSubmitting ? '上传中...' : '确认修改' }}</text>
|
||||
</button>
|
||||
</view>
|
||||
|
||||
<!-- 微信环境提示 -->
|
||||
<view v-if="!isWechatBrowser" class="wechat-tip">
|
||||
<u-icon name="info-circle" size="16" color="#fa8c16" />
|
||||
<text class="tip-text">建议在微信中使用以获得最佳体验</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { api, request } from '@/common/request';
|
||||
import { userManager } from '@/utils/user';
|
||||
import { REQUEST_CONFIG } from '@/common/config';
|
||||
import weChatSDK from '@/utils/wechat.js';
|
||||
|
||||
export default {
|
||||
name: 'ChangeAvatar',
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
avatarLoading: false,
|
||||
currentAvatar: '',
|
||||
newAvatar: '',
|
||||
isSubmitting: false,
|
||||
pageTitle: '修改头像',
|
||||
|
||||
// 微信SDK相关
|
||||
wechatSDK: weChatSDK,
|
||||
isWechatBrowser: false,
|
||||
wechatInitializing: false,
|
||||
wechatImageLoading: false,
|
||||
uploadProgress: 0,
|
||||
};
|
||||
},
|
||||
onLoad() {
|
||||
this.loadCurrentAvatar();
|
||||
this.checkWechatEnvironment();
|
||||
},
|
||||
methods: {
|
||||
// 返回上一页
|
||||
goBack() {
|
||||
uni.navigateBack();
|
||||
},
|
||||
|
||||
// 加载当前头像
|
||||
loadCurrentAvatar() {
|
||||
const userInfo = userManager.getCurrentUser();
|
||||
this.currentAvatar = userInfo?.avatar || '/static/111.png';
|
||||
},
|
||||
|
||||
// 检查微信环境并自动初始化SDK
|
||||
async checkWechatEnvironment() {
|
||||
this.isWechatBrowser = this.wechatSDK.isWeChatBrowser();
|
||||
|
||||
if (this.isWechatBrowser) {
|
||||
console.log('检测到微信环境,自动初始化微信SDK...');
|
||||
await this.initWechatSDK();
|
||||
} else {
|
||||
console.log('非微信环境,使用标准图片选择功能');
|
||||
}
|
||||
},
|
||||
|
||||
// 初始化微信SDK
|
||||
async initWechatSDK() {
|
||||
if (this.wechatInitializing) return;
|
||||
|
||||
this.wechatInitializing = true;
|
||||
console.log('开始初始化微信SDK...');
|
||||
|
||||
try {
|
||||
// 检查微信环境
|
||||
if (!this.isWechatBrowser) {
|
||||
throw new Error('当前不在微信浏览器环境中');
|
||||
}
|
||||
|
||||
// 使用微信SDK工具类初始化
|
||||
const configData = await this.wechatSDK.init({
|
||||
debug: false,
|
||||
});
|
||||
|
||||
console.log('✅ 微信SDK初始化成功!', configData);
|
||||
|
||||
// 验证图片相关API可用性
|
||||
try {
|
||||
const checkResult = await this.wechatSDK.checkJsApi([
|
||||
'chooseImage',
|
||||
'previewImage',
|
||||
'uploadImage',
|
||||
'downloadImage',
|
||||
]);
|
||||
console.log('图片API可用性检查完成:', checkResult);
|
||||
} catch (checkError) {
|
||||
console.warn('API可用性检查失败:', checkError.message);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('微信SDK初始化失败:', error);
|
||||
uni.showToast({
|
||||
title: '微信SDK初始化失败',
|
||||
icon: 'none',
|
||||
duration: 2000,
|
||||
});
|
||||
} finally {
|
||||
this.wechatInitializing = false;
|
||||
}
|
||||
},
|
||||
|
||||
// === 头像处理相关方法 ===
|
||||
|
||||
// 头像上传处理(主要入口)
|
||||
handlePhotoUpload() {
|
||||
if (this.isWechatBrowser) {
|
||||
if (this.wechatSDK.isInitialized) {
|
||||
this.chooseWechatImage();
|
||||
} else {
|
||||
this.showWechatInitDialog();
|
||||
}
|
||||
} else {
|
||||
this.chooseImage();
|
||||
}
|
||||
},
|
||||
|
||||
// 头像点击处理(已有新头像时)
|
||||
handleAvatarClick() {
|
||||
uni.showActionSheet({
|
||||
itemList: ['预览头像', '更换头像', '删除头像'],
|
||||
success: (res) => {
|
||||
switch (res.tapIndex) {
|
||||
case 0:
|
||||
this.handleAvatarPreview();
|
||||
break;
|
||||
case 1:
|
||||
this.handleAvatarReselect();
|
||||
break;
|
||||
case 2:
|
||||
this.handleAvatarDelete();
|
||||
break;
|
||||
}
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
// 预览头像
|
||||
handleAvatarPreview() {
|
||||
if (this.newAvatar) {
|
||||
uni.previewImage({
|
||||
urls: [this.newAvatar],
|
||||
current: this.newAvatar,
|
||||
success: () => {
|
||||
console.log('头像预览打开成功');
|
||||
},
|
||||
fail: (error) => {
|
||||
console.error('预览头像失败:', error);
|
||||
uni.showToast({
|
||||
title: '预览失败',
|
||||
icon: 'none',
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
// 重新选择头像
|
||||
handleAvatarReselect() {
|
||||
uni.showModal({
|
||||
title: '确认操作',
|
||||
content: '确定要重新选择头像吗?当前头像将被替换。',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
this.handlePhotoUpload();
|
||||
}
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
// 删除头像
|
||||
handleAvatarDelete() {
|
||||
uni.showModal({
|
||||
title: '确认删除',
|
||||
content: '确定要删除当前头像吗?删除后无法恢复。',
|
||||
confirmColor: '#ff5722',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
this.clearAvatarData();
|
||||
uni.showToast({
|
||||
title: '头像已删除',
|
||||
icon: 'success',
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
// 清空头像数据
|
||||
clearAvatarData() {
|
||||
this.newAvatar = '';
|
||||
this.uploadProgress = 0;
|
||||
},
|
||||
|
||||
// 显示微信初始化对话框
|
||||
showWechatInitDialog() {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '微信SDK未初始化,是否立即初始化?',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
this.initWechatSDK();
|
||||
}
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
// 显示非微信环境提示
|
||||
showNonWechatTip() {
|
||||
uni.showModal({
|
||||
title: '环境提示',
|
||||
content:
|
||||
'建议在微信中使用此功能以获得最佳体验。\n\n在微信中打开此页面可以:\n• 直接选择相册图片\n• 拍照上传\n• 图片预览和编辑',
|
||||
showCancel: false,
|
||||
confirmText: '我知道了',
|
||||
});
|
||||
},
|
||||
|
||||
// 选择微信图片
|
||||
async chooseWechatImage() {
|
||||
if (!this.wechatSDK.isInitialized) {
|
||||
console.log('请先初始化微信SDK');
|
||||
await this.initWechatSDK();
|
||||
return;
|
||||
}
|
||||
|
||||
this.wechatImageLoading = true;
|
||||
this.uploadProgress = 0;
|
||||
console.log('正在打开微信图片选择器...');
|
||||
|
||||
try {
|
||||
const res = await this.wechatSDK.chooseImage({
|
||||
count: 1, // 头像只选择一张
|
||||
sizeType: ['original', 'compressed'],
|
||||
sourceType: ['album', 'camera'],
|
||||
});
|
||||
|
||||
console.log('✅ 微信图片选择成功,共', res.localIds.length, '张');
|
||||
|
||||
// 将选择的图片设置为头像预览
|
||||
if (res.localIds.length > 0) {
|
||||
this.newAvatar = res.localIds[0];
|
||||
console.log('📷 已设置头像预览');
|
||||
|
||||
// 显示上传提示
|
||||
uni.showToast({
|
||||
title: '正在上传头像...',
|
||||
icon: 'loading',
|
||||
duration: 2000,
|
||||
});
|
||||
|
||||
// 自动上传图片到微信服务器并保存到OSS
|
||||
await this.uploadWechatImageToOSS(res.localIds[0]);
|
||||
|
||||
// 上传成功提示
|
||||
uni.showToast({
|
||||
title: '头像上传成功!',
|
||||
icon: 'success',
|
||||
duration: 1500,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('选择微信图片失败:', error);
|
||||
|
||||
// 显示错误提示
|
||||
uni.showModal({
|
||||
title: '选择图片失败',
|
||||
content: error.message || '请检查微信环境或网络连接',
|
||||
showCancel: false,
|
||||
confirmText: '我知道了',
|
||||
});
|
||||
|
||||
// 重置头像预览
|
||||
this.newAvatar = '';
|
||||
} finally {
|
||||
this.wechatImageLoading = false;
|
||||
this.uploadProgress = 0;
|
||||
}
|
||||
},
|
||||
|
||||
// 上传微信图片到OSS
|
||||
async uploadWechatImageToOSS(localId) {
|
||||
try {
|
||||
console.log('📤 开始上传微信图片,localId:', localId);
|
||||
|
||||
// 步骤1:上传到微信服务器(20%进度)
|
||||
this.uploadProgress = 10;
|
||||
console.log('📤 正在上传到微信服务器...');
|
||||
|
||||
const wxUploadRes = await new Promise((resolve, reject) => {
|
||||
wx.uploadImage({
|
||||
localId: localId,
|
||||
isShowProgressTips: 0, // 关闭微信自带的进度提示
|
||||
success: resolve,
|
||||
fail: reject,
|
||||
});
|
||||
});
|
||||
|
||||
const mediaId = wxUploadRes.serverId;
|
||||
this.uploadProgress = 30;
|
||||
console.log('✅ 微信服务器上传成功,mediaId:', mediaId);
|
||||
|
||||
// 步骤2:保存到OSS(80%进度)
|
||||
this.uploadProgress = 50;
|
||||
console.log('💾 正在保存到云端存储...');
|
||||
|
||||
const ossResponse = await request({
|
||||
url: '/system/wechat/downloadToOss',
|
||||
method: 'GET',
|
||||
params: {
|
||||
mediaId: mediaId,
|
||||
},
|
||||
timeout: 30000, // 30秒超时
|
||||
});
|
||||
|
||||
if (ossResponse.code === 200) {
|
||||
const ossResult = ossResponse.data;
|
||||
this.uploadProgress = 90;
|
||||
|
||||
console.log('🎉 头像保存成功!', ossResult);
|
||||
|
||||
// 更新新头像URL
|
||||
this.newAvatar = ossResult.url;
|
||||
|
||||
// 上传进度完成
|
||||
this.uploadProgress = 100;
|
||||
|
||||
return ossResult;
|
||||
} else {
|
||||
throw new Error(ossResponse.msg || '保存头像到云端存储失败');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('上传微信图片失败:', error);
|
||||
|
||||
// 上传失败时重置进度
|
||||
this.uploadProgress = 0;
|
||||
|
||||
// 显示详细错误信息
|
||||
let errorMsg = '头像上传失败,请重试';
|
||||
if (error.message.includes('timeout')) {
|
||||
errorMsg = '上传超时,请检查网络连接后重试';
|
||||
} else if (error.message.includes('mediaId')) {
|
||||
errorMsg = '微信服务器上传失败,请重试';
|
||||
} else if (error.message.includes('OSS')) {
|
||||
errorMsg = '保存到云端存储失败,请重试';
|
||||
}
|
||||
|
||||
uni.showToast({
|
||||
title: errorMsg,
|
||||
icon: 'none',
|
||||
duration: 2000,
|
||||
});
|
||||
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
// 选择图片(非微信环境)
|
||||
chooseImage() {
|
||||
uni.chooseImage({
|
||||
count: 1,
|
||||
sizeType: ['compressed'],
|
||||
sourceType: ['album'],
|
||||
success: (res) => {
|
||||
this.handleImageSelected(res.tempFilePaths[0]);
|
||||
},
|
||||
fail: (error) => {
|
||||
console.error('选择图片失败:', error);
|
||||
uni.showToast({
|
||||
title: '选择图片失败',
|
||||
icon: 'none',
|
||||
duration: 2000,
|
||||
});
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
// 拍照
|
||||
takePhoto() {
|
||||
uni.chooseImage({
|
||||
count: 1,
|
||||
sizeType: ['compressed'],
|
||||
sourceType: ['camera'],
|
||||
success: (res) => {
|
||||
this.handleImageSelected(res.tempFilePaths[0]);
|
||||
},
|
||||
fail: (error) => {
|
||||
console.error('拍照失败:', error);
|
||||
uni.showToast({
|
||||
title: '拍照失败',
|
||||
icon: 'none',
|
||||
duration: 2000,
|
||||
});
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
// 处理选中的图片
|
||||
handleImageSelected(filePath) {
|
||||
// 检查文件大小
|
||||
uni.getFileInfo({
|
||||
filePath: filePath,
|
||||
success: (res) => {
|
||||
const fileSizeInMB = res.size / (1024 * 1024);
|
||||
if (fileSizeInMB > 10) {
|
||||
uni.showToast({
|
||||
title: '图片大小不能超过10MB',
|
||||
icon: 'none',
|
||||
duration: 2000,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// 压缩图片
|
||||
this.compressImage(filePath);
|
||||
},
|
||||
fail: (error) => {
|
||||
console.error('获取文件信息失败:', error);
|
||||
// 直接使用原图
|
||||
this.newAvatar = filePath;
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
// 压缩图片
|
||||
compressImage(filePath) {
|
||||
// 检查是否支持压缩图片API
|
||||
if (typeof uni.compressImage === 'function' && uni.compressImage) {
|
||||
try {
|
||||
uni.compressImage({
|
||||
src: filePath,
|
||||
quality: 80,
|
||||
success: (res) => {
|
||||
console.log('图片压缩成功:', res.tempFilePath);
|
||||
this.newAvatar = res.tempFilePath;
|
||||
},
|
||||
fail: (error) => {
|
||||
console.warn('压缩图片失败,使用原图:', error);
|
||||
// 使用原图
|
||||
this.newAvatar = filePath;
|
||||
},
|
||||
complete: () => {
|
||||
// 确保有值
|
||||
if (!this.newAvatar) {
|
||||
this.newAvatar = filePath;
|
||||
}
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
console.warn('compressImage API调用异常,使用原图:', error);
|
||||
this.newAvatar = filePath;
|
||||
}
|
||||
} else {
|
||||
console.log('当前环境不支持图片压缩API,使用原图');
|
||||
// 不支持压缩时直接使用原图
|
||||
this.newAvatar = filePath;
|
||||
}
|
||||
},
|
||||
|
||||
// 处理头像加载错误
|
||||
handleAvatarError() {
|
||||
this.currentAvatar = '/static/111.png';
|
||||
},
|
||||
|
||||
// 提交头像
|
||||
async handleSubmit() {
|
||||
if (!this.newAvatar) {
|
||||
uni.showToast({
|
||||
title: '请先选择头像',
|
||||
icon: 'none',
|
||||
duration: 2000,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
this.isSubmitting = true;
|
||||
|
||||
try {
|
||||
let avatarUrl = this.newAvatar;
|
||||
|
||||
// 如果不是微信上传的图片,需要先上传到服务器
|
||||
if (!this.newAvatar.startsWith('http')) {
|
||||
const uploadResult = await this.uploadAvatar(this.newAvatar);
|
||||
avatarUrl = uploadResult.url;
|
||||
}
|
||||
|
||||
// 更新用户信息
|
||||
await api.updateUserAvatar({
|
||||
avatar: avatarUrl,
|
||||
});
|
||||
|
||||
// 更新本地用户信息
|
||||
userManager.updateUserInfo({
|
||||
avatar: avatarUrl,
|
||||
});
|
||||
|
||||
uni.showToast({
|
||||
title: '头像修改成功',
|
||||
icon: 'success',
|
||||
duration: 2000,
|
||||
});
|
||||
|
||||
// 延迟返回上一页
|
||||
setTimeout(() => {
|
||||
uni.navigateBack();
|
||||
}, 2000);
|
||||
} catch (error) {
|
||||
console.error('修改头像失败:', error);
|
||||
uni.showToast({
|
||||
title: error.message || '修改头像失败',
|
||||
icon: 'none',
|
||||
duration: 2000,
|
||||
});
|
||||
} finally {
|
||||
this.isSubmitting = false;
|
||||
}
|
||||
},
|
||||
|
||||
// 上传头像到服务器(非微信环境使用)
|
||||
uploadAvatar(filePath) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const token = uni.getStorageSync('access_token');
|
||||
const clientid = uni.getStorageSync('clientid') || 'mobile';
|
||||
|
||||
// 构建完整URL
|
||||
let fullUrl;
|
||||
if (REQUEST_CONFIG.baseURL.startsWith('/')) {
|
||||
// 开发环境,baseURL是'/api',需要使用完整的服务器URL
|
||||
fullUrl = `http://localhost:8080${REQUEST_CONFIG.baseURL}/system/user/profile/avatar`;
|
||||
} else {
|
||||
// 生产环境,直接拼接
|
||||
fullUrl = REQUEST_CONFIG.baseURL + '/system/user/profile/avatar';
|
||||
}
|
||||
|
||||
uni.uploadFile({
|
||||
url: fullUrl,
|
||||
filePath: filePath,
|
||||
name: 'avatarfile',
|
||||
header: {
|
||||
authorization: token ? `Bearer ${token}` : '',
|
||||
clientid: clientid,
|
||||
},
|
||||
success: (res) => {
|
||||
try {
|
||||
const data = JSON.parse(res.data);
|
||||
if (data.code === 200) {
|
||||
resolve(data.data);
|
||||
} else {
|
||||
reject(new Error(data.msg || '上传失败'));
|
||||
}
|
||||
} catch (error) {
|
||||
reject(new Error('解析响应失败'));
|
||||
}
|
||||
},
|
||||
fail: (error) => {
|
||||
reject(new Error('上传失败'));
|
||||
},
|
||||
});
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.page-container {
|
||||
min-height: 100vh;
|
||||
background-color: #f5f6fa;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
flex: 1;
|
||||
padding: 20rpx;
|
||||
padding-bottom: 40rpx;
|
||||
}
|
||||
|
||||
.avatar-container {
|
||||
background-color: #ffffff;
|
||||
border-radius: 16rpx;
|
||||
margin-bottom: 20rpx;
|
||||
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.08);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.current-avatar-section,
|
||||
.new-avatar-section {
|
||||
padding: 30rpx;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 30rpx;
|
||||
font-weight: bold;
|
||||
color: #303133;
|
||||
margin-bottom: 20rpx;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.avatar-loading {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 200rpx;
|
||||
height: 200rpx;
|
||||
border: 2rpx dashed #d9d9d9;
|
||||
border-radius: 50%;
|
||||
background-color: #fafafa;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.loading-text {
|
||||
font-size: 24rpx;
|
||||
margin-top: 16rpx;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.progress-text {
|
||||
font-size: 20rpx;
|
||||
color: #1890ff;
|
||||
margin-top: 12rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.avatar-wrapper {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
margin: 0 auto;
|
||||
display: block;
|
||||
|
||||
&:hover .avatar-overlay {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.avatar-image {
|
||||
width: 200rpx;
|
||||
height: 200rpx;
|
||||
border-radius: 50%;
|
||||
border: 3rpx solid #ffffff;
|
||||
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.1);
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.avatar-overlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 200rpx;
|
||||
height: 200rpx;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
opacity: 0;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.overlay-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.overlay-text {
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
font-size: 20rpx;
|
||||
margin-top: 8rpx;
|
||||
}
|
||||
|
||||
.avatar-badge {
|
||||
position: absolute;
|
||||
top: -6rpx;
|
||||
right: calc(50% - 100rpx + 6rpx);
|
||||
width: 32rpx;
|
||||
height: 32rpx;
|
||||
background: #fff;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.avatar-placeholder {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 200rpx;
|
||||
height: 200rpx;
|
||||
border: 2rpx dashed #d9d9d9;
|
||||
border-radius: 50%;
|
||||
background-color: #fafafa;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.placeholder-text {
|
||||
font-size: 24rpx;
|
||||
color: #8c939d;
|
||||
margin-top: 16rpx;
|
||||
}
|
||||
|
||||
.placeholder-note {
|
||||
font-size: 20rpx;
|
||||
color: #c0c4cc;
|
||||
margin-top: 8rpx;
|
||||
}
|
||||
|
||||
.placeholder-tips {
|
||||
padding: 16rpx 20rpx;
|
||||
background: rgba(24, 144, 255, 0.05);
|
||||
border-radius: 8rpx;
|
||||
border: 1px solid rgba(24, 144, 255, 0.1);
|
||||
}
|
||||
|
||||
.tip-item {
|
||||
display: block;
|
||||
font-size: 20rpx;
|
||||
color: #666;
|
||||
margin-bottom: 6rpx;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.avatar-quick-actions {
|
||||
padding: 20rpx 30rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20rpx;
|
||||
border-top: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.submit-section {
|
||||
padding: 30rpx;
|
||||
border-top: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.submit-btn {
|
||||
width: 100%;
|
||||
height: 88rpx;
|
||||
background: linear-gradient(135deg, #52c41a, #389e0d);
|
||||
border: none;
|
||||
border-radius: 12rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 16rpx;
|
||||
transition: all 0.3s ease;
|
||||
|
||||
&:active {
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
background: #cccccc;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
&.btn-loading {
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-text {
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.wechat-tip {
|
||||
margin: 20rpx 30rpx;
|
||||
padding: 16rpx 20rpx;
|
||||
background-color: #fffbe6;
|
||||
border: 1px solid #ffe58f;
|
||||
border-radius: 8rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16rpx;
|
||||
font-size: 28rpx;
|
||||
color: #faad14;
|
||||
}
|
||||
|
||||
.tip-text {
|
||||
font-size: 28rpx;
|
||||
color: #faad14;
|
||||
}
|
||||
|
||||
/* 响应式设计 */
|
||||
@media (max-width: 375px) {
|
||||
.main-content {
|
||||
padding: 16rpx 12rpx;
|
||||
}
|
||||
|
||||
.avatar-container {
|
||||
padding: 20rpx 16rpx;
|
||||
}
|
||||
|
||||
.avatar-image {
|
||||
width: 160rpx;
|
||||
height: 160rpx;
|
||||
}
|
||||
|
||||
.avatar-wrapper {
|
||||
width: 160rpx;
|
||||
height: 160rpx;
|
||||
}
|
||||
|
||||
.avatar-overlay {
|
||||
width: 160rpx;
|
||||
height: 160rpx;
|
||||
}
|
||||
|
||||
.avatar-placeholder {
|
||||
width: 160rpx;
|
||||
height: 160rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,418 @@
|
||||
<template>
|
||||
<view class="page-container">
|
||||
<!-- 页面头部 -->
|
||||
<!-- <view class="page-header">
|
||||
<view class="header-content">
|
||||
<view class="back-btn" @click="goBack">
|
||||
<u-icon name="arrow-left" size="20" color="#ffffff" />
|
||||
</view>
|
||||
<text class="page-title">修改密码</text>
|
||||
<view class="placeholder"></view>
|
||||
</view>
|
||||
</view> -->
|
||||
|
||||
<!-- 主要内容 -->
|
||||
<view class="main-content">
|
||||
<view class="form-container">
|
||||
<!-- 旧密码 -->
|
||||
<view class="form-item">
|
||||
<view class="input-label">
|
||||
<u-icon name="lock" size="16" color="#666666" />
|
||||
<text class="label-text">旧密码</text>
|
||||
</view>
|
||||
<view class="input-wrapper">
|
||||
<input
|
||||
v-model="formData.oldPassword"
|
||||
type="password"
|
||||
placeholder="请输入旧密码"
|
||||
class="input-field"
|
||||
:class="{ 'input-error': errors.oldPassword }"
|
||||
@blur="validateOldPassword"
|
||||
/>
|
||||
<view v-if="errors.oldPassword" class="error-message">
|
||||
{{ errors.oldPassword }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 新密码 -->
|
||||
<view class="form-item">
|
||||
<view class="input-label">
|
||||
<u-icon name="lock-open" size="16" color="#666666" />
|
||||
<text class="label-text">新密码</text>
|
||||
</view>
|
||||
<view class="input-wrapper">
|
||||
<input
|
||||
v-model="formData.newPassword"
|
||||
type="password"
|
||||
placeholder="请输入新密码"
|
||||
class="input-field"
|
||||
:class="{ 'input-error': errors.newPassword }"
|
||||
@blur="validateNewPassword"
|
||||
/>
|
||||
<view v-if="errors.newPassword" class="error-message">
|
||||
{{ errors.newPassword }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 确认新密码 -->
|
||||
<view class="form-item">
|
||||
<view class="input-label">
|
||||
<u-icon name="checkmark-circle" size="16" color="#666666" />
|
||||
<text class="label-text">确认新密码</text>
|
||||
</view>
|
||||
<view class="input-wrapper">
|
||||
<input
|
||||
v-model="formData.confirmPassword"
|
||||
type="password"
|
||||
placeholder="请再次输入新密码"
|
||||
class="input-field"
|
||||
:class="{ 'input-error': errors.confirmPassword }"
|
||||
@blur="validateConfirmPassword"
|
||||
/>
|
||||
<view v-if="errors.confirmPassword" class="error-message">
|
||||
{{ errors.confirmPassword }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 提交按钮 -->
|
||||
<view class="submit-section">
|
||||
<button
|
||||
class="submit-btn"
|
||||
:class="{ 'btn-loading': isSubmitting }"
|
||||
:disabled="isSubmitting || !isFormValid"
|
||||
@click="handleSubmit"
|
||||
>
|
||||
<u-icon v-if="isSubmitting" name="loading" size="16" color="#ffffff" />
|
||||
<text class="btn-text">{{ isSubmitting ? '提交中...' : '确认修改' }}</text>
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { api } from '@/common/request';
|
||||
|
||||
export default {
|
||||
name: 'ResetPassword',
|
||||
data() {
|
||||
return {
|
||||
formData: {
|
||||
oldPassword: '',
|
||||
newPassword: '',
|
||||
confirmPassword: '',
|
||||
},
|
||||
errors: {
|
||||
oldPassword: '',
|
||||
newPassword: '',
|
||||
confirmPassword: '',
|
||||
},
|
||||
isSubmitting: false,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
isFormValid() {
|
||||
return (
|
||||
this.formData.oldPassword.trim() !== '' &&
|
||||
this.formData.newPassword.trim() !== '' &&
|
||||
this.formData.confirmPassword.trim() !== '' &&
|
||||
!this.errors.oldPassword &&
|
||||
!this.errors.newPassword &&
|
||||
!this.errors.confirmPassword
|
||||
);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
// 返回上一页
|
||||
goBack() {
|
||||
uni.navigateBack();
|
||||
},
|
||||
|
||||
// 验证旧密码
|
||||
validateOldPassword() {
|
||||
if (!this.formData.oldPassword.trim()) {
|
||||
this.errors.oldPassword = '请输入旧密码';
|
||||
return false;
|
||||
}
|
||||
this.errors.oldPassword = '';
|
||||
return true;
|
||||
},
|
||||
|
||||
// 验证新密码
|
||||
validateNewPassword() {
|
||||
const password = this.formData.newPassword.trim();
|
||||
|
||||
if (!password) {
|
||||
this.errors.newPassword = '请输入新密码';
|
||||
return false;
|
||||
}
|
||||
|
||||
if (password.length < 6) {
|
||||
this.errors.newPassword = '密码长度不能少于6位';
|
||||
return false;
|
||||
}
|
||||
|
||||
if (password.length > 20) {
|
||||
this.errors.newPassword = '密码长度不能超过20位';
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查是否包含数字和字母
|
||||
const hasNumber = /\d/.test(password);
|
||||
const hasLetter = /[a-zA-Z]/.test(password);
|
||||
|
||||
if (!hasNumber || !hasLetter) {
|
||||
this.errors.newPassword = '密码必须包含数字和字母';
|
||||
return false;
|
||||
}
|
||||
|
||||
this.errors.newPassword = '';
|
||||
return true;
|
||||
},
|
||||
|
||||
// 验证确认密码
|
||||
validateConfirmPassword() {
|
||||
if (!this.formData.confirmPassword.trim()) {
|
||||
this.errors.confirmPassword = '请确认新密码';
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.formData.newPassword !== this.formData.confirmPassword) {
|
||||
this.errors.confirmPassword = '两次输入的密码不一致';
|
||||
return false;
|
||||
}
|
||||
|
||||
this.errors.confirmPassword = '';
|
||||
return true;
|
||||
},
|
||||
|
||||
// 提交表单
|
||||
async handleSubmit() {
|
||||
// 验证所有字段
|
||||
const isOldValid = this.validateOldPassword();
|
||||
const isNewValid = this.validateNewPassword();
|
||||
const isConfirmValid = this.validateConfirmPassword();
|
||||
|
||||
if (!isOldValid || !isNewValid || !isConfirmValid) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.isFormValid) {
|
||||
uni.showToast({
|
||||
title: '请完善表单信息',
|
||||
icon: 'none',
|
||||
duration: 2000,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
this.isSubmitting = true;
|
||||
|
||||
try {
|
||||
// 调用修改密码API
|
||||
await api.resetPassword({
|
||||
oldPassword: this.formData.oldPassword,
|
||||
newPassword: this.formData.newPassword,
|
||||
});
|
||||
|
||||
uni.showToast({
|
||||
title: '密码修改成功',
|
||||
icon: 'success',
|
||||
duration: 2000,
|
||||
});
|
||||
|
||||
// 延迟返回上一页
|
||||
setTimeout(() => {
|
||||
uni.navigateBack();
|
||||
}, 2000);
|
||||
} catch (error) {
|
||||
console.error('修改密码失败:', error);
|
||||
uni.showToast({
|
||||
title: error.message || '修改密码失败',
|
||||
icon: 'none',
|
||||
duration: 2000,
|
||||
});
|
||||
} finally {
|
||||
this.isSubmitting = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.page-container {
|
||||
min-height: 100vh;
|
||||
background-color: #f5f5f5;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* 页面头部 */
|
||||
.page-header {
|
||||
background: linear-gradient(135deg, #007aff, #0051d9);
|
||||
padding: calc(var(--status-bar-height) + 12px) 16px 16px;
|
||||
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.header-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.back-btn {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
transition: all 0.3s ease;
|
||||
|
||||
&:active {
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
transform: scale(0.95);
|
||||
}
|
||||
}
|
||||
|
||||
.page-title {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: #ffffff;
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.placeholder {
|
||||
width: 40px;
|
||||
}
|
||||
|
||||
/* 主要内容 */
|
||||
.main-content {
|
||||
flex: 1;
|
||||
padding: 20px 16px;
|
||||
}
|
||||
|
||||
.form-container {
|
||||
background-color: #ffffff;
|
||||
border-radius: 12px;
|
||||
padding: 24px 20px;
|
||||
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
.form-item {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.input-label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.label-text {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.input-wrapper {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.input-field {
|
||||
width: 100%;
|
||||
height: 48px;
|
||||
padding: 0 16px;
|
||||
border: 1px solid #e0e0e0;
|
||||
border-radius: 8px;
|
||||
font-size: 16px;
|
||||
background-color: #ffffff;
|
||||
transition: all 0.3s ease;
|
||||
box-sizing: border-box;
|
||||
|
||||
&:focus {
|
||||
border-color: #007aff;
|
||||
box-shadow: 0 0 0 2px rgba(0, 122, 255, 0.1);
|
||||
}
|
||||
|
||||
&.input-error {
|
||||
border-color: #ff4d4f;
|
||||
box-shadow: 0 0 0 2px rgba(255, 77, 79, 0.1);
|
||||
}
|
||||
|
||||
&::placeholder {
|
||||
color: #999999;
|
||||
}
|
||||
}
|
||||
|
||||
.error-message {
|
||||
font-size: 14px;
|
||||
color: #ff4d4f;
|
||||
margin-top: 4px;
|
||||
padding-left: 4px;
|
||||
}
|
||||
|
||||
.submit-section {
|
||||
margin-top: 32px;
|
||||
}
|
||||
|
||||
.submit-btn {
|
||||
width: 100%;
|
||||
height: 48px;
|
||||
background: linear-gradient(135deg, #007aff, #0051d9);
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
transition: all 0.3s ease;
|
||||
|
||||
&:active {
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
background: #cccccc;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
&.btn-loading {
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-text {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
/* 响应式设计 */
|
||||
@media (max-width: 375px) {
|
||||
.main-content {
|
||||
padding: 16px 12px;
|
||||
}
|
||||
|
||||
.form-container {
|
||||
padding: 20px 16px;
|
||||
}
|
||||
|
||||
.input-field {
|
||||
height: 44px;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.submit-btn {
|
||||
height: 44px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -581,8 +581,30 @@
|
||||
</view>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="label">宿舍房间号</text>
|
||||
<text class="value">{{ form.dormitoryRoom || '-' }}</text>
|
||||
<text class="label">宿舍房间</text>
|
||||
<view class="value">
|
||||
<view v-if="roomDisplayName" class="room-info">
|
||||
<u-tag
|
||||
:text="roomDisplayName"
|
||||
:type="getRoomTagType(form.dormitoryRoom)"
|
||||
size="mini"
|
||||
></u-tag>
|
||||
</view>
|
||||
<text v-else>-</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="label">床位</text>
|
||||
<view class="value">
|
||||
<view v-if="bedDisplayName" class="bed-info">
|
||||
<u-tag
|
||||
:text="bedDisplayName"
|
||||
:type="getBedTagType(form.extField1)"
|
||||
size="mini"
|
||||
></u-tag>
|
||||
</view>
|
||||
<text v-else>-</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -724,6 +746,7 @@ export default {
|
||||
classTeacherName: '',
|
||||
dormitoryBuilding: '',
|
||||
dormitoryRoom: '',
|
||||
extField1: '', // 新增床位信息字段
|
||||
|
||||
// 银行卡信息字段
|
||||
bankCardNo: '',
|
||||
@@ -751,6 +774,14 @@ export default {
|
||||
// 行政区划和宿舍楼栋数据
|
||||
regionOptions: [],
|
||||
dormBuildingOptions: [],
|
||||
|
||||
// 房间和床位数据
|
||||
dormRoomOptions: [],
|
||||
dormBedOptions: [],
|
||||
|
||||
// 显示名称(异步加载)
|
||||
roomDisplayName: '',
|
||||
bedDisplayName: '',
|
||||
};
|
||||
},
|
||||
|
||||
@@ -770,6 +801,13 @@ export default {
|
||||
console.error('区域和宿舍数据加载出错:', error);
|
||||
}
|
||||
|
||||
try {
|
||||
await this.loadDormRoomAndBedData();
|
||||
console.log('✓ 房间和床位数据加载完成');
|
||||
} catch (error) {
|
||||
console.error('房间和床位数据加载出错:', error);
|
||||
}
|
||||
|
||||
try {
|
||||
await this.getPersonalInfoData();
|
||||
console.log('✓ 个人信息加载完成');
|
||||
@@ -1224,6 +1262,33 @@ export default {
|
||||
});
|
||||
},
|
||||
|
||||
// 加载房间和床位数据(改为按需加载)
|
||||
async loadDormRoomAndBedData() {
|
||||
console.log('房间和床位数据改为按需加载模式,不再预加载所有数据');
|
||||
// 初始化空数组,数据将在需要时按需查询
|
||||
this.dormRoomOptions = [];
|
||||
this.dormBedOptions = [];
|
||||
},
|
||||
|
||||
// 异步加载房间和床位显示名称
|
||||
async loadRoomAndBedDisplayNames() {
|
||||
try {
|
||||
// 加载房间名称
|
||||
if (this.form.dormitoryRoom) {
|
||||
this.roomDisplayName = await this.getRoomName(this.form.dormitoryRoom);
|
||||
console.log('房间名称加载完成:', this.roomDisplayName);
|
||||
}
|
||||
|
||||
// 加载床位名称
|
||||
if (this.form.extField1) {
|
||||
this.bedDisplayName = await this.getBedName(this.form.extField1);
|
||||
console.log('床位名称加载完成:', this.bedDisplayName);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载房间和床位显示名称失败:', error);
|
||||
}
|
||||
},
|
||||
|
||||
// 获取学生个人信息
|
||||
async getPersonalInfoData() {
|
||||
try {
|
||||
@@ -1257,6 +1322,17 @@ export default {
|
||||
|
||||
// 将API返回的数据映射到表单
|
||||
Object.assign(this.form, data);
|
||||
|
||||
// 调试日志:检查宿舍相关字段
|
||||
console.log('宿舍信息字段检查:', {
|
||||
dormitoryBuilding: this.form.dormitoryBuilding,
|
||||
dormitoryRoom: this.form.dormitoryRoom,
|
||||
extField1: this.form.extField1,
|
||||
extField1Type: typeof this.form.extField1,
|
||||
});
|
||||
|
||||
// 异步加载房间和床位信息
|
||||
await this.loadRoomAndBedDisplayNames();
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: response.msg || '获取个人信息失败',
|
||||
@@ -1468,6 +1544,178 @@ export default {
|
||||
|
||||
return findBuilding(this.dormBuildingOptions, buildingId);
|
||||
},
|
||||
|
||||
// 根据房间ID获取房间名称(含性别标识)
|
||||
async getRoomName(roomId) {
|
||||
// 防护性检查
|
||||
if (!roomId) {
|
||||
return '';
|
||||
}
|
||||
|
||||
// 如果本地缓存中没有数据,则按需查询
|
||||
if (!this.dormRoomOptions || this.dormRoomOptions.length === 0) {
|
||||
try {
|
||||
const response = await request({
|
||||
url: `/xgss/dormRoom/${roomId}`,
|
||||
method: 'GET',
|
||||
});
|
||||
|
||||
if (response.code === 200 && response.data) {
|
||||
const roomData = response.data;
|
||||
// 缓存房间数据
|
||||
if (!this.dormRoomOptions) {
|
||||
this.dormRoomOptions = [];
|
||||
}
|
||||
this.dormRoomOptions.push(roomData);
|
||||
|
||||
// 返回房间名称
|
||||
return roomData.roomName || roomData.name || `房间${roomId}`;
|
||||
} else {
|
||||
console.warn(`查询房间信息失败,房间ID: ${roomId}`);
|
||||
return `房间${roomId}`;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`查询房间信息异常,房间ID: ${roomId}`, error);
|
||||
return `房间${roomId}`;
|
||||
}
|
||||
}
|
||||
|
||||
// 从本地缓存中查找
|
||||
const room = this.dormRoomOptions.find((item) => String(item.id) === String(roomId));
|
||||
if (room) {
|
||||
return room.roomName || room.name || `房间${roomId}`;
|
||||
}
|
||||
|
||||
// 如果本地缓存中没有找到,则按需查询
|
||||
try {
|
||||
const response = await request({
|
||||
url: `/xgss/dormRoom/${roomId}`,
|
||||
method: 'GET',
|
||||
});
|
||||
|
||||
if (response.code === 200 && response.data) {
|
||||
const roomData = response.data;
|
||||
// 缓存房间数据
|
||||
this.dormRoomOptions.push(roomData);
|
||||
|
||||
// 返回房间名称
|
||||
return roomData.roomName || roomData.name || `房间${roomId}`;
|
||||
} else {
|
||||
console.warn(`查询房间信息失败,房间ID: ${roomId}`);
|
||||
return `房间${roomId}`;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`查询房间信息异常,房间ID: ${roomId}`, error);
|
||||
return `房间${roomId}`;
|
||||
}
|
||||
},
|
||||
|
||||
// 根据房间ID获取房间标签类型
|
||||
getRoomTagType(roomId) {
|
||||
if (!roomId) {
|
||||
return 'info'; // 默认类型
|
||||
}
|
||||
|
||||
// 从本地缓存中查找
|
||||
const room = this.dormRoomOptions.find((item) => String(item.id) === String(roomId));
|
||||
if (room) {
|
||||
// 根据房间类型设置标签类型
|
||||
if (room.roomType === '1') {
|
||||
// 男生房间
|
||||
return 'warning';
|
||||
} else if (room.roomType === '2') {
|
||||
// 女生房间
|
||||
return 'error';
|
||||
}
|
||||
}
|
||||
return 'info'; // 默认类型
|
||||
},
|
||||
|
||||
// 根据床位ID获取床位名称(含性别标识)
|
||||
async getBedName(bedId) {
|
||||
// 防护性检查
|
||||
if (!bedId) {
|
||||
return '';
|
||||
}
|
||||
|
||||
// 如果本地缓存中没有数据,则按需查询
|
||||
if (!this.dormBedOptions || this.dormBedOptions.length === 0) {
|
||||
try {
|
||||
const response = await request({
|
||||
url: `/dormbed/dormBed/${bedId}`,
|
||||
method: 'GET',
|
||||
});
|
||||
|
||||
if (response.code === 200 && response.data) {
|
||||
const bedData = response.data;
|
||||
// 缓存床位数据
|
||||
if (!this.dormBedOptions) {
|
||||
this.dormBedOptions = [];
|
||||
}
|
||||
this.dormBedOptions.push(bedData);
|
||||
|
||||
// 返回床位名称
|
||||
return bedData.bedName || bedData.name || `床位${bedId}`;
|
||||
} else {
|
||||
console.warn(`查询床位信息失败,床位ID: ${bedId}`);
|
||||
return `床位${bedId}`;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`查询床位信息异常,床位ID: ${bedId}`, error);
|
||||
return `床位${bedId}`;
|
||||
}
|
||||
}
|
||||
|
||||
// 从本地缓存中查找
|
||||
const bed = this.dormBedOptions.find((item) => String(item.id) === String(bedId));
|
||||
if (bed) {
|
||||
return bed.bedName || bed.name || `床位${bedId}`;
|
||||
}
|
||||
|
||||
// 如果本地缓存中没有找到,则按需查询
|
||||
try {
|
||||
const response = await request({
|
||||
url: `/dormbed/dormBed/${bedId}`,
|
||||
method: 'GET',
|
||||
});
|
||||
|
||||
if (response.code === 200 && response.data) {
|
||||
const bedData = response.data;
|
||||
// 缓存床位数据
|
||||
this.dormBedOptions.push(bedData);
|
||||
|
||||
// 返回床位名称
|
||||
return bedData.bedName || bedData.name || `床位${bedId}`;
|
||||
} else {
|
||||
console.warn(`查询床位信息失败,床位ID: ${bedId}`);
|
||||
return `床位${bedId}`;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`查询床位信息异常,床位ID: ${bedId}`, error);
|
||||
return `床位${bedId}`;
|
||||
}
|
||||
},
|
||||
|
||||
// 根据床位ID获取床位标签类型
|
||||
getBedTagType(bedId) {
|
||||
if (!bedId) {
|
||||
return 'info'; // 默认类型
|
||||
}
|
||||
|
||||
// 从本地缓存中查找
|
||||
const bed = this.dormBedOptions.find((item) => String(item.id) === String(bedId));
|
||||
if (bed) {
|
||||
// 根据床位类型设置标签类型
|
||||
if (bed.bedType === '1') {
|
||||
// 男生床位
|
||||
return 'warning';
|
||||
} else if (bed.bedType === '2') {
|
||||
// 女生床位
|
||||
return 'error';
|
||||
}
|
||||
}
|
||||
return 'info'; // 默认类型
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -1655,6 +1903,18 @@ export default {
|
||||
gap: 8rpx;
|
||||
}
|
||||
|
||||
.room-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8rpx;
|
||||
}
|
||||
|
||||
.bed-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8rpx;
|
||||
}
|
||||
|
||||
// uview组件样式调整
|
||||
::v-deep .u-tag {
|
||||
margin: 0;
|
||||
|
||||
@@ -747,9 +747,15 @@
|
||||
</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="label">宿舍房间号:</text>
|
||||
<text class="label">宿舍房间:</text>
|
||||
<text class="value">
|
||||
{{ studentInfo.dormitoryRoom || '-' }}
|
||||
{{ dormRoomName || '-' }}
|
||||
</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="label">床位:</text>
|
||||
<text class="value">
|
||||
{{ dormBedName || '-' }}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
@@ -881,6 +887,10 @@ export default {
|
||||
// 新增:行政区划和宿舍楼栋数据
|
||||
regionOptions: [], // 行政区划选项
|
||||
dormBuildingOptions: [], // 宿舍楼栋选项
|
||||
|
||||
// 宿舍房间和床位显示名称
|
||||
dormRoomName: '',
|
||||
dormBedName: '',
|
||||
};
|
||||
},
|
||||
|
||||
@@ -1293,6 +1303,42 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
// 获取宿舍房间名称
|
||||
async getDormRoomName(roomId) {
|
||||
if (!roomId) return '-';
|
||||
try {
|
||||
const response = await request({
|
||||
url: `/xgss/dormRoom/${roomId}`,
|
||||
method: 'GET',
|
||||
});
|
||||
if (response.code === 200 && response.data) {
|
||||
return response.data.roomName || '-';
|
||||
}
|
||||
return roomId; // 如果没有找到,返回原始ID
|
||||
} catch (error) {
|
||||
console.error('获取宿舍房间名称失败:', error);
|
||||
return roomId;
|
||||
}
|
||||
},
|
||||
|
||||
// 获取床位名称
|
||||
async getDormBedName(bedId) {
|
||||
if (!bedId) return '-';
|
||||
try {
|
||||
const response = await request({
|
||||
url: `/dormbed/dormBed/${bedId}`,
|
||||
method: 'GET',
|
||||
});
|
||||
if (response.code === 200 && response.data) {
|
||||
return response.data.bedName || '-';
|
||||
}
|
||||
return bedId; // 如果没有找到,返回原始ID
|
||||
} catch (error) {
|
||||
console.error('获取床位名称失败:', error);
|
||||
return bedId;
|
||||
}
|
||||
},
|
||||
|
||||
// 根据行政区划代码获取完整路径(省/市/区)
|
||||
getRegionFullPath(code) {
|
||||
if (!code || !this.regionOptions) return code || '';
|
||||
@@ -1336,6 +1382,21 @@ export default {
|
||||
return findBuilding(this.dormBuildingOptions, buildingId) || buildingId;
|
||||
},
|
||||
|
||||
// 设置宿舍房间和床位显示名称
|
||||
async setDormDisplayNames() {
|
||||
if (this.studentInfo.dormitoryRoom) {
|
||||
this.dormRoomName = await this.getDormRoomName(this.studentInfo.dormitoryRoom);
|
||||
} else {
|
||||
this.dormRoomName = '-';
|
||||
}
|
||||
|
||||
if (this.studentInfo.extField1) {
|
||||
this.dormBedName = await this.getDormBedName(this.studentInfo.extField1);
|
||||
} else {
|
||||
this.dormBedName = '-';
|
||||
}
|
||||
},
|
||||
|
||||
// 加载数据
|
||||
async loadData() {
|
||||
if (!this.batchInfo || !this.batchInfo.batchId) {
|
||||
@@ -1407,6 +1468,9 @@ export default {
|
||||
if (this.studentInfo.photoPath) {
|
||||
this.loadPhoto();
|
||||
}
|
||||
|
||||
// 设置宿舍房间和床位显示名称
|
||||
await this.setDormDisplayNames();
|
||||
} else {
|
||||
console.log('未获取到学生信息或数据为空');
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 快捷操作按钮 v-if="isWechatBrowser"-->
|
||||
<!-- 快捷操作按钮 -->
|
||||
<view class="photo-quick-actions">
|
||||
<view class="placeholder-tips">
|
||||
<text class="tip-item">• 建议尺寸:2寸证件照 • 支持格式:JPG、PNG • 文件大小:不超过10MB</text>
|
||||
@@ -1086,20 +1086,21 @@
|
||||
<u-picker
|
||||
:columns="[trainingModeOptions]"
|
||||
keyName="label"
|
||||
:disabled="true"
|
||||
:defaultIndex="trainingModeIndex"
|
||||
:show="showTrainingModePicker"
|
||||
:closeOnClickOverlay="true"
|
||||
@confirm="onTrainingModeConfirm"
|
||||
@cancel="onTrainingModeClose"
|
||||
></u-picker>
|
||||
<view class="picker-input" @click="showTrainingModePicker = true">
|
||||
<view class="picker-input disabled">
|
||||
<text class="picker-text">
|
||||
{{
|
||||
getDictLabel(trainingModeOptions, form.trainingType) ||
|
||||
'请选择培养方式'
|
||||
}}
|
||||
</text>
|
||||
<u-icon name="arrow-down" size="16" color="#c0c4cc"></u-icon>
|
||||
<u-icon name="arrow-down" size="16" color="#e4e7ed"></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -1109,13 +1110,14 @@
|
||||
<u-picker
|
||||
:columns="[admissionCategoryOptions]"
|
||||
keyName="label"
|
||||
:disabled="true"
|
||||
:defaultIndex="admissionCategoryIndex"
|
||||
:show="showAdmissionCategoryPicker"
|
||||
:closeOnClickOverlay="true"
|
||||
@confirm="onAdmissionCategoryConfirm"
|
||||
@cancel="onAdmissionCategoryClose"
|
||||
></u-picker>
|
||||
<view class="picker-input" @click="showAdmissionCategoryPicker = true">
|
||||
<view class="picker-input disabled">
|
||||
<text class="picker-text">
|
||||
{{
|
||||
getDictLabel(
|
||||
@@ -1124,7 +1126,7 @@
|
||||
) || '请选择录取类别'
|
||||
}}
|
||||
</text>
|
||||
<u-icon name="arrow-down" size="16" color="#c0c4cc"></u-icon>
|
||||
<u-icon name="arrow-down" size="16" color="#e4e7ed"></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
<view class="form-item-half">
|
||||
@@ -1132,24 +1134,26 @@
|
||||
<u-picker
|
||||
:columns="[studentStatusOptions]"
|
||||
keyName="label"
|
||||
:disabled="true"
|
||||
:defaultIndex="studentStatusIndex"
|
||||
:show="showStudentStatusPicker"
|
||||
:closeOnClickOverlay="true"
|
||||
@confirm="onStudentStatusConfirm"
|
||||
@cancel="onStudentStatusClose"
|
||||
></u-picker>
|
||||
<view class="picker-input" @click="showStudentStatusPicker = true">
|
||||
<view class="picker-input disabled">
|
||||
<text class="picker-text">
|
||||
{{
|
||||
getDictLabel(studentStatusOptions, form.studentStatus) ||
|
||||
'请选择学籍状态'
|
||||
}}
|
||||
</text>
|
||||
<u-icon name="arrow-down" size="16" color="#c0c4cc"></u-icon>
|
||||
<u-icon name="arrow-down" size="16" color="#e4e7ed"></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="form-item">
|
||||
<view class="form-row">
|
||||
<view class="form-item-half">
|
||||
<text class="form-label">是否住校</text>
|
||||
<u-radio-group v-model="form.isBoarder">
|
||||
<u-radio
|
||||
@@ -1162,7 +1166,6 @@
|
||||
</u-radio>
|
||||
</u-radio-group>
|
||||
</view>
|
||||
<view class="form-row">
|
||||
<view class="form-item-half">
|
||||
<text class="form-label">宿舍楼栋</text>
|
||||
<u-picker
|
||||
@@ -1185,11 +1188,22 @@
|
||||
<u-icon name="arrow-down" size="16" color="#e4e7ed"></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="form-row">
|
||||
<view class="form-item-half">
|
||||
<text class="form-label">宿舍房间号</text>
|
||||
<text class="form-label">宿舍房间</text>
|
||||
<u-input
|
||||
v-model="form.dormitoryRoom"
|
||||
placeholder="请输入宿舍房间号"
|
||||
:value="dormRoomName"
|
||||
placeholder="宿舍房间"
|
||||
:disabled="true"
|
||||
border-bottom
|
||||
></u-input>
|
||||
</view>
|
||||
<view class="form-item-half">
|
||||
<text class="form-label">床位</text>
|
||||
<u-input
|
||||
:value="dormBedName"
|
||||
placeholder="床位"
|
||||
:disabled="true"
|
||||
border-bottom
|
||||
></u-input>
|
||||
@@ -1387,6 +1401,7 @@ export default {
|
||||
classTeacherName: '',
|
||||
dormitoryBuilding: '',
|
||||
dormitoryRoom: '',
|
||||
extField1: '',
|
||||
|
||||
// 银行卡信息字段
|
||||
bankCardNo: '',
|
||||
@@ -1453,6 +1468,14 @@ export default {
|
||||
showStudentStatusPicker: false,
|
||||
showDormBuildingPicker: false,
|
||||
showSourceRegionPicker: false,
|
||||
|
||||
// 缓存
|
||||
roomNameCache: {},
|
||||
bedNameCache: {},
|
||||
|
||||
// 宿舍房间和床位显示名称
|
||||
dormRoomName: '',
|
||||
dormBedName: '',
|
||||
};
|
||||
},
|
||||
|
||||
@@ -2106,6 +2129,9 @@ export default {
|
||||
|
||||
// 同步picker索引
|
||||
this.syncPickerIndexes();
|
||||
|
||||
// 设置宿舍房间和床位显示名称
|
||||
await this.setDormDisplayNames();
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: response.msg || '获取个人信息失败',
|
||||
@@ -3127,6 +3153,77 @@ export default {
|
||||
this.debugMode = !this.debugMode;
|
||||
this.addWechatLog(`调试模式已${this.debugMode ? '开启' : '关闭'}`, 'info');
|
||||
},
|
||||
|
||||
// 获取宿舍房间名称
|
||||
async getDormRoomName(roomId) {
|
||||
if (!roomId) return '-';
|
||||
|
||||
// 检查缓存
|
||||
if (this.roomNameCache && this.roomNameCache[roomId]) {
|
||||
return this.roomNameCache[roomId];
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await request({
|
||||
url: `/xgss/dormRoom/${roomId}`,
|
||||
method: 'GET',
|
||||
});
|
||||
if (response.code === 200 && response.data) {
|
||||
const roomName = response.data.roomName || response.data.roomCode || String(roomId);
|
||||
// 缓存结果
|
||||
if (!this.roomNameCache) this.roomNameCache = {};
|
||||
this.roomNameCache[roomId] = roomName;
|
||||
return roomName;
|
||||
}
|
||||
return String(roomId); // 如果没有找到,返回原始ID
|
||||
} catch (error) {
|
||||
console.error('获取宿舍房间名称失败:', error);
|
||||
return String(roomId);
|
||||
}
|
||||
},
|
||||
|
||||
// 获取床位名称
|
||||
async getDormBedName(bedId) {
|
||||
if (!bedId) return '-';
|
||||
|
||||
// 检查缓存
|
||||
if (this.bedNameCache && this.bedNameCache[bedId]) {
|
||||
return this.bedNameCache[bedId];
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await request({
|
||||
url: `/dormbed/dormBed/${bedId}`,
|
||||
method: 'GET',
|
||||
});
|
||||
if (response.code === 200 && response.data) {
|
||||
const bedName = response.data.bedName || response.data.bedCode || String(bedId);
|
||||
// 缓存结果
|
||||
if (!this.bedNameCache) this.bedNameCache = {};
|
||||
this.bedNameCache[bedId] = bedName;
|
||||
return bedName;
|
||||
}
|
||||
return String(bedId); // 如果没有找到,返回原始ID
|
||||
} catch (error) {
|
||||
console.error('获取床位名称失败:', error);
|
||||
return String(bedId);
|
||||
}
|
||||
},
|
||||
|
||||
// 设置宿舍房间和床位显示名称
|
||||
async setDormDisplayNames() {
|
||||
if (this.form.dormitoryRoom) {
|
||||
this.dormRoomName = await this.getDormRoomName(this.form.dormitoryRoom);
|
||||
} else {
|
||||
this.dormRoomName = '-';
|
||||
}
|
||||
|
||||
if (this.form.extField1) {
|
||||
this.dormBedName = await this.getDormBedName(this.form.extField1);
|
||||
} else {
|
||||
this.dormBedName = '-';
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="body_1" width="3182" height="3197">
|
||||
|
||||
<g transform="matrix(1.3333334 0 0 1.3333334 0 0)">
|
||||
<image x="0" y="0" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAADG4AAAx9CAYAAACOo659AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAP+lSURBVHhe7L0HgGRFufbfk2d2lyw5Y7zfTd93w/9eA+xO6O7pPGl3Qa6RqCISJUqSpICgCCiSFAQURQwYAEEkqAQlSZacd2HjzHR+/+/z1qnuMz09y+Aq6T4/eLbq1KlTp845dapqz75vV0RedypSqZTkwgvOk+223Vq2324bufeeuywN+wghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQ8r+XaqBpVMvy46uulB222VLeue1W8tvf/lYqFeeHUK02PeJNAR03CCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBDypoGOG2sNHTcIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEENIcOm6sNXTcIIQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEJIc+i4sdbQcYMQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIc2h48ZaQ8cNQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQ0h44baw0dNwghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQ0hw6bqw1dNwghBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQkhz6Lix1tBxgxBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghzaHjxlpDxw1CCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhDSHjhtrDR03CCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBDSHDpurDV03CCEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCSHPouLHW0HGDEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCHNoePGWkPHDUIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGENIeOG2sNHTcIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEENIcOm6sNXTcIIQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEJIc+i4sdbQcYMQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIc2h48ZaQ8cNQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQ0h44baw0dNwghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQ0hw6bqw1dNwghBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQkhz6Lix1tBxgxBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghzaHjxlpDxw1CCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhDSHjhtrDR03CCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBDSHDpurDV03CCEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCSHPouLHW0HGDEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCHNoePGWkPHDUIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGENIeOG2sNHTcIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEENIcOm6sNXTcIIQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEJIc+i4sdbQcYMQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIc2h48ZaQ8cNQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQ0h44baw0dNwghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQ0hw6bqw1dNwghBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQkhz6Lix1tBxgxBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghzaHjxlpDxw1CCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhDSHjhtrDR03CCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBDSHDpurDV03CCEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCSHPouLHW0HGDEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCHNoePGWkPHDUIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGENIeOG2sNHTcIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEENIcOm6sNXTcIIQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEJIc+i4sdbQcYMQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIc2h48ZaQ8cNQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQ0h44baw0dNwghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQ0hw6bqw1dNwghBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQkhz6Lix1tBxgxBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghzaHjxlpDxw1CCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhDSHjhtrDR03CCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBDSHDpurDV03CCEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCSHPeho4bqOTfT1W9MZDfrpSLdrMu/Na58q5ttpF37rCd3H3nHZZWrZSm5KUoiqIoiqIoiqIoiqIoiqL+lloD/ut3WJWq+2br5cG+AMumf06RJuKnekrBtosD1MHFXHmN9Zsq/70YpbhtLRiFAwReAeEkyH4uSCO+Lv5jvftzzSCPOzPq79R4PPbNVBbSG/f5+1GRciC/XU/z39T9tdfzBvdLC0U9anUJ4uG0aQTJ/jr886hvV6VYrUzZdqrnK+DcuJmBXLvQnfXHVRfSw/LPLAD/eOL/AcVTLuP6Gph6mDH1eoNEBeVVy1pmqI5WT00rFYpuWysXvreNarbPnk6QjhJMOLeGniCXpeFe+XuJtHIQR5rHl2H1C66lEaRYmbrPl1+7puBYTVyjrFjE/SUpSPIgHt7253HP3l9PvQ34/f64sGr4BK0fqog6TL3GUAVfi/T+l+w9CNfDxWrvxquo/nw9zfL5q8W/ZTVKj7WL0jBQvcxmZVEURVFro3r/ql2ujiONc4XGuQQhhBBCCCGEEELefuDLsv+6PIUqHTfWqPCH61IxL+d94xzZfsst5T07bC/33fUnTeaHbYqiKIqiKIqiKIqiKIqiqL+r/BfuQN6gulHOKBwZ6obJpqmHT5GZUGtkmjQdptAI8adX+Jvx7BQCRTUByV7An9cbndu1NWZaA96A3ZfxGg51NByAcmDA7xV2yoCm3GsV0srVUmh//cyNdbBLW4PwLOpG+CKFJioG8vF6fnf9WgmHRq1AHwYKO05Y3O9HGOCj/lrM4SKIN4LUpnv8juBctXOUtE0h1HNXCkVXLraDPP6eYgVwCPc2rMb77e65b6f100Ief3+R1ug3gnvnQ+y3y4SAD2cgnBXgWlAVL7ue0LZLm1l27hAznR7paKdhhd+DsBrrCKyegWo3IpQRR9Yq9Rrlnkm9PuF99ec0s/x71WyfU73F4w2A88YUNR6PuNeUciiKoqi1Vbi/tTElwMcRYg5B5w1CCCGEEEIIIeTtTejz8lSqb1HHjXKpIGuj0qsonKdQmDSnjaKGl1x0oWy3lXPcuOuO20V3SH71KluRI3wsRVEURVEURVEURVEURVEU9TdSsVRTNRSHSqVGFaRYrqtUnhQpqvLjIoWJpqoWxqfJ8k8iPmHljFdLMlkpSaFUtHCirCG+D5dLut+psS6oH9JRZ0srFE1Nr3FGTdo1hFUuTaxRuN6qqoLrw7UXCjVVi3Wh/GbftevbOJcry8uX7ctvJtwzU5CnPLnahLSy3lcI+8JxyxfE/TZCKeXtWDyLSh6hK8fL6hPa9mle7jyT9p0fwnd+yH/zh+yag22E/p5gtW2k5fMTMjGx2vYhr6VpfSC/HS4PwjkR4px5rcOE3kevcYTlvIVQvloUPaO2q4KsLk7IpO7z2+O6Xda2hn+DKOL8oX+LsOcUpCOO0MetPoHcsy/W20DoXlX0nuMeh9O8rP3rdVe1TVWKeq0FfY5BvLbdRCjTP9uivku4HlwLhOv28UblQ/JpePfQBsPPMyzfRqHG98TL7fdt+tXl7631H/re4v3VF9/Cxv4H8uleyBvOD60pf1ha4WlptX5D42sqxz/fsMLve/i9n+maZ1KzYyiKoqiZhb6zoP2wjSOBswbijbyZDTIIIYQQQgghhBDy1/O2c9zYY/dPyuuh3VV77rGbabdPflxS8Zg5bkC7Ll4ku3/8Y7LP3ns1PZaiKIqiKIqiKIqiKIqiKIpae+212+5TtEdIu+/u5b7n7raHk48j3Hu3j8mnP+n18VB8Zu29m8uHY3fbY3f5hCocTpc73yf2RFhPR35cg6/fTApf79S0j2uZTrvv8XEtsy5sh4W8EOqMcC+9BlyHhZ/U+6Daa7e6Gs/nzxkuD+Heu3/iNcvOqeGn9J74sBZHHt2P8NOatpfeZ8jqingQ7qna4xMflc/suZvlw/EILR4c6+N+2+J7On1GnwWO2XOPT8gee+wme+2p7Ue19157TIn7fwPANkJ/X1y6lrW3pu/l7sseWu7ee+1m2kvL9/LbCJHXpyO+296flE9+yoUW3+sTtfjun9rNtu35ahxCG/qYnvcTWtbHNdxtN5W2W/+sLNQ0X09fbx+iznYdQTzclj+lz/QzeiyEd8Hin/i47PPJUBjsx/ZntZ3geHuuPlyD/HPfW+8VzoXzf1KvA9eC0Me9Pq7X6oXrDgtpeKd8m95r93q79sJ2o1w+lxey9hzcO3//wgrvC8v3Line truncated
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.7 MiB |
|
After Width: | Height: | Size: 2.4 KiB |
@@ -107,7 +107,7 @@ export const userManager = {
|
||||
|
||||
return {
|
||||
name: this.getDisplayName(),
|
||||
avatar: user.avatar || '/static/logo-2.png',
|
||||
avatar: user.avatar || '/static/logo.svg',
|
||||
deptName: user.deptName || '未设置部门',
|
||||
status: user.status,
|
||||
loginDate: user.loginDate,
|
||||
|
||||
@@ -1,6 +1,14 @@
|
||||
import request from '@/utils/request';
|
||||
import { AxiosPromise } from 'axios';
|
||||
import { StglActivityVO, StglActivityForm, StglActivityQuery, ClubOption, ActivityCopyParams, ActivitySummaryParams, ActivityRatingVO } from './types';
|
||||
import {
|
||||
StglActivityVO,
|
||||
StglActivityForm,
|
||||
StglActivityQuery,
|
||||
ClubOption,
|
||||
ActivityCopyParams,
|
||||
ActivitySummaryParams,
|
||||
ActivityRatingVO
|
||||
} from './types';
|
||||
|
||||
/**
|
||||
* 查询活动信息列表
|
||||
|
||||
@@ -478,6 +478,11 @@ export interface StglActivityQuery extends PageQuery {
|
||||
*/
|
||||
isFinished?: number;
|
||||
|
||||
/**
|
||||
* 创建者ID
|
||||
*/
|
||||
createBy?: string | number;
|
||||
|
||||
/**
|
||||
* 日期范围参数
|
||||
*/
|
||||
|
||||
@@ -12,7 +12,7 @@ export const listActivityRegister = (query?: any): AxiosPromise<StglActivityRegi
|
||||
return request({
|
||||
url: '/stgl/activity/register/list',
|
||||
method: 'get',
|
||||
params: query,
|
||||
params: query
|
||||
});
|
||||
};
|
||||
|
||||
@@ -23,7 +23,7 @@ export const listActivityRegister = (query?: any): AxiosPromise<StglActivityRegi
|
||||
export const getActivityRegister = (id: string | number): AxiosPromise<StglActivityRegistrationVo> => {
|
||||
return request({
|
||||
url: '/stgl/activity/register/' + id,
|
||||
method: 'get',
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
@@ -35,7 +35,7 @@ export const addActivityRegister = (data: StglActivityRegistrationForm) => {
|
||||
return request({
|
||||
url: '/stgl/activity/register',
|
||||
method: 'post',
|
||||
data: data,
|
||||
data: data
|
||||
});
|
||||
};
|
||||
|
||||
@@ -47,7 +47,7 @@ export const updateActivityRegister = (data: StglActivityRegistrationForm) => {
|
||||
return request({
|
||||
url: '/stgl/activity/register',
|
||||
method: 'put',
|
||||
data: data,
|
||||
data: data
|
||||
});
|
||||
};
|
||||
|
||||
@@ -58,7 +58,7 @@ export const updateActivityRegister = (data: StglActivityRegistrationForm) => {
|
||||
export const delActivityRegister = (id: string | number | Array<string | number>) => {
|
||||
return request({
|
||||
url: '/stgl/activity/register/' + id,
|
||||
method: 'delete',
|
||||
method: 'delete'
|
||||
});
|
||||
};
|
||||
|
||||
@@ -74,8 +74,8 @@ export const approveActivityRegister = (id: string | number, approvalStatus: num
|
||||
method: 'post',
|
||||
params: {
|
||||
approvalStatus,
|
||||
approvalRemark,
|
||||
},
|
||||
approvalRemark
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@@ -92,8 +92,8 @@ export const batchApproveActivityRegister = (ids: Array<string | number>, approv
|
||||
data: {
|
||||
ids,
|
||||
approvalStatus,
|
||||
approvalRemark,
|
||||
},
|
||||
approvalRemark
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@@ -121,7 +121,7 @@ export const recordActivityScore = (id: string | number, score: string) => {
|
||||
url: `/stgl/activity/register/score/${id}`,
|
||||
method: 'post',
|
||||
params: {
|
||||
score,
|
||||
},
|
||||
score
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -11,7 +11,7 @@ export const listRegistrableActivities = (query?: any): AxiosPromise<StglActivit
|
||||
return request({
|
||||
url: '/stgl/activity/registerhall/list',
|
||||
method: 'get',
|
||||
params: query,
|
||||
params: query
|
||||
});
|
||||
};
|
||||
|
||||
@@ -22,7 +22,7 @@ export const listRegistrableActivities = (query?: any): AxiosPromise<StglActivit
|
||||
export const getActivityDetail = (id: string | number): AxiosPromise<StglActivityVo> => {
|
||||
return request({
|
||||
url: '/stgl/activity/registerhall/' + id,
|
||||
method: 'get',
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
@@ -34,7 +34,7 @@ export const registerActivity = (data: StglActivityRegistrationForm) => {
|
||||
return request({
|
||||
url: '/stgl/activity/registerhall/register',
|
||||
method: 'post',
|
||||
data: data,
|
||||
data: data
|
||||
});
|
||||
};
|
||||
|
||||
@@ -45,7 +45,7 @@ export const registerActivity = (data: StglActivityRegistrationForm) => {
|
||||
export const checkRegistration = (activityId: string | number): AxiosPromise<boolean> => {
|
||||
return request({
|
||||
url: `/stgl/activity/registerhall/checkRegistration/${activityId}`,
|
||||
method: 'get',
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
@@ -57,6 +57,6 @@ export const getMyRegistrations = (query?: any): AxiosPromise<StglActivityRegist
|
||||
return request({
|
||||
url: '/stgl/activity/registerhall/myRegistrations',
|
||||
method: 'get',
|
||||
params: query,
|
||||
params: query
|
||||
});
|
||||
};
|
||||
@@ -120,3 +120,22 @@ export const checkPresidentRole = (id: string | number) => {
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 修改社团报名开启状态
|
||||
* @param id 社团ID
|
||||
* @param sighOpen 报名开启状态(Y/N)
|
||||
*/
|
||||
export const changeSighOpenStatus = (id: string | number, sighOpen: string) => {
|
||||
return request({
|
||||
url: '/stgl/club/changeSighOpenStatus',
|
||||
method: 'put',
|
||||
params: {
|
||||
id,
|
||||
sighOpen
|
||||
},
|
||||
headers: {
|
||||
isEncrypt: 'true'
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -189,6 +189,11 @@ export interface StglClubVO {
|
||||
*/
|
||||
status: string;
|
||||
|
||||
/**
|
||||
* 是否开启报名
|
||||
*/
|
||||
sighOpen: string;
|
||||
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
@@ -381,6 +386,11 @@ export interface StglClubForm {
|
||||
*/
|
||||
status?: string;
|
||||
|
||||
/**
|
||||
* 是否开启报名
|
||||
*/
|
||||
sighOpen?: string;
|
||||
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
|
||||
@@ -67,9 +67,9 @@ export interface StglMemberVO {
|
||||
email: string;
|
||||
|
||||
/**
|
||||
* 角色类型
|
||||
* 成员角色
|
||||
*/
|
||||
roleType: number;
|
||||
roleType: string;
|
||||
|
||||
/**
|
||||
* 角色名称
|
||||
@@ -164,9 +164,9 @@ export interface StglMemberForm {
|
||||
email?: string;
|
||||
|
||||
/**
|
||||
* 角色类型
|
||||
* 成员角色
|
||||
*/
|
||||
roleType?: number;
|
||||
roleType?: string;
|
||||
|
||||
/**
|
||||
* 角色名称
|
||||
@@ -216,9 +216,9 @@ export interface StglMemberQuery extends PageQuery {
|
||||
studentName?: string;
|
||||
|
||||
/**
|
||||
* 角色类型
|
||||
* 成员角色
|
||||
*/
|
||||
roleType?: number;
|
||||
roleType?: string;
|
||||
|
||||
/**
|
||||
* 状态
|
||||
|
||||
@@ -1,6 +1,16 @@
|
||||
import request from '@/utils/request';
|
||||
import { AxiosPromise } from 'axios';
|
||||
import { MyClubVO, MyVolunteerTeamVO, MyActivityVO, MyRegistrationVO, ActivityRatingForm, MyRegistrationQuery, MyClubQuery, MyVolunteerQuery, MyActivityQuery } from './types';
|
||||
import {
|
||||
MyClubVO,
|
||||
MyVolunteerTeamVO,
|
||||
MyActivityVO,
|
||||
MyRegistrationVO,
|
||||
ActivityRatingForm,
|
||||
MyRegistrationQuery,
|
||||
MyClubQuery,
|
||||
MyVolunteerQuery,
|
||||
MyActivityQuery
|
||||
} from './types';
|
||||
|
||||
/**
|
||||
* 查询我参与的社团列表
|
||||
|
||||
@@ -49,6 +49,11 @@ export interface StglClubRegisterVO {
|
||||
*/
|
||||
canRegister: boolean;
|
||||
|
||||
/**
|
||||
* 报名开启状态
|
||||
*/
|
||||
sighOpen?: string;
|
||||
|
||||
/**
|
||||
* 招新周期
|
||||
*/
|
||||
@@ -242,6 +247,11 @@ export interface StglClubRegisterQuery extends PageQuery {
|
||||
*/
|
||||
canRegister?: boolean;
|
||||
|
||||
/**
|
||||
* 报名开启状态
|
||||
*/
|
||||
sighOpen?: string;
|
||||
|
||||
/**
|
||||
* 日期范围参数
|
||||
*/
|
||||
|
||||
@@ -4,6 +4,16 @@ export interface VolunteerMemberVO {
|
||||
*/
|
||||
id: string | number;
|
||||
|
||||
/**
|
||||
* 用户ID
|
||||
*/
|
||||
userId?: string | number;
|
||||
|
||||
/**
|
||||
* 成员类型(1-社团成员,2-志愿者成员)
|
||||
*/
|
||||
memberType?: number;
|
||||
|
||||
/**
|
||||
* 学号
|
||||
*/
|
||||
@@ -65,7 +75,7 @@ export interface VolunteerMemberVO {
|
||||
joinTime: string;
|
||||
|
||||
/**
|
||||
* 状态(0正常 1停用)
|
||||
* 状态(0待审核 1审核中 2已加入 3已驳回 4已退出)
|
||||
*/
|
||||
status: string;
|
||||
|
||||
@@ -101,6 +111,16 @@ export interface VolunteerMemberForm {
|
||||
*/
|
||||
id?: string | number;
|
||||
|
||||
/**
|
||||
* 用户ID
|
||||
*/
|
||||
userId?: string | number;
|
||||
|
||||
/**
|
||||
* 成员类型(1-社团成员,2-志愿者成员)
|
||||
*/
|
||||
memberType?: number;
|
||||
|
||||
/**
|
||||
* 学号
|
||||
*/
|
||||
@@ -157,7 +177,7 @@ export interface VolunteerMemberForm {
|
||||
joinTime?: string;
|
||||
|
||||
/**
|
||||
* 状态(0正常 1停用)
|
||||
* 状态(0待审核 1审核中 2已加入 3已驳回 4已退出)
|
||||
*/
|
||||
status?: string;
|
||||
|
||||
@@ -209,7 +229,7 @@ export interface VolunteerMemberQuery {
|
||||
relationId?: string | number;
|
||||
|
||||
/**
|
||||
* 状态(0正常 1停用)
|
||||
* 状态(0待审核 1审核中 2已加入 3已驳回 4已退出)
|
||||
*/
|
||||
status?: string;
|
||||
|
||||
|
||||
@@ -212,3 +212,22 @@ export function listVolunteerTeamForRegistration(query) {
|
||||
params: query
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改志愿者团队报名开启状态
|
||||
* @param id 团队ID
|
||||
* @param sighOpen 报名开启状态(Y/N)
|
||||
*/
|
||||
export function changeSighOpenStatus(id, sighOpen) {
|
||||
return request({
|
||||
url: '/stgl/volunteerTeam/changeSighOpenStatus',
|
||||
method: 'put',
|
||||
params: {
|
||||
id,
|
||||
sighOpen
|
||||
},
|
||||
headers: {
|
||||
isEncrypt: 'true'
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -11,6 +11,17 @@ export const listMenu = (query?: MenuQuery): AxiosPromise<MenuVO[]> => {
|
||||
});
|
||||
};
|
||||
|
||||
// 获取首页菜单数据(无权限限制,用于首页展示)
|
||||
export const getHomeMenus = (): AxiosPromise<MenuVO[]> => {
|
||||
return request({
|
||||
url: '/system/menu/list',
|
||||
method: 'get',
|
||||
params: {
|
||||
status: '0' // 只获取正常状态的菜单
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// 查询菜单详细
|
||||
export const getMenu = (menuId: string | number): AxiosPromise<MenuVO> => {
|
||||
return request({
|
||||
|
||||
@@ -52,10 +52,34 @@ export function getStudentPersonalInfo(userId?: number): AxiosPromise<any> {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取宿舍房间信息
|
||||
* @param roomId 房间ID
|
||||
*/
|
||||
export function getDormRoomInfo(roomId: number): AxiosPromise<R<any>> {
|
||||
return request({
|
||||
url: `/xgss/dormRoom/${roomId}`,
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取宿舍床位信息
|
||||
* @param bedId 床位ID
|
||||
*/
|
||||
export function getDormBedInfo(bedId: number): AxiosPromise<R<any>> {
|
||||
return request({
|
||||
url: `/dormbed/dormBed/${bedId}`,
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
|
||||
export default {
|
||||
XgDeptTreeSelect,
|
||||
deptTreeSelect,
|
||||
listStudentRoster,
|
||||
exportStudentRoster,
|
||||
getStudentPersonalInfo
|
||||
getStudentPersonalInfo,
|
||||
getDormRoomInfo,
|
||||
getDormBedInfo
|
||||
};
|
||||
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 2.5 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 2.5 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1756114168869" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="37747" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M768 0H256C114.6112 0 0 114.6112 0 256v512c0 141.3376 114.6112 256 256 256h512c141.3376 0 256-114.6624 256-256V256c0-141.3888-114.6624-256-256-256zM447.6416 230.4h123.4688c15.1552 0 27.2384 8.9088 27.2384 25.5488 0.0256 16.6912-12.1088 25.6512-26.496 25.6512h-124.2624c-15.104 0-27.2384-8.96-27.2384-25.6512 0.0256-16.64 12.16-25.5488 27.2896-25.5488zM768 725.0688C768 774.3232 749.9008 793.6 701.3888 793.6H316.5952C268.1344 793.6 256 768.9984 256 725.0432v-405.248c0-40.9344 6.8096-63.7952 56.832-63.7952h62.2592c-0.6144 0-0.9984 2.0736-1.024 5.4272 0 25.3696 18.432 45.7728 41.4976 45.7728h188.8768c21.8624 0 40.2432-20.3776 40.2432-45.7728 0-3.3792-0.384-5.4272-0.9984-5.4272h61.4912c46.2336 0 62.8224 22.8864 62.8224 63.7952v405.2736z" fill="#6EE9DF" p-id="37748"></path><path d="M695.4752 665.6H328.5248a21.3248 21.3248 0 0 0-21.3248 21.3248v8.5504c0 11.776 9.5488 21.3248 21.3248 21.3248h366.9504c11.776 0 21.3248-9.5488 21.3248-21.3248v-8.5504a21.3248 21.3248 0 0 0-21.3248-21.3248zM672.8192 354.8928l-182.0672 224.6656-103.5264-93.6192a31.4368 31.4368 0 0 0-44.2368 2.0992 30.9248 30.9248 0 0 0 2.1248 43.904l118.7072 107.2896c8.0128 7.1936 18.4576 11.2384 29.1584 11.2384 1.0752 0 2.1504-0.1024 3.2512-0.2048 11.776-0.896 22.8608-6.656 30.464-15.9488l194.8928-240.4352a30.9504 30.9504 0 0 0-4.7872-43.776 31.488 31.488 0 0 0-43.9808 4.7872z" fill="#6EE9DF" p-id="37749"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 2.5 KiB |
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1756113838264" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1618" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M720.96 240.746667A85.333333 85.333333 0 0 1 725.333333 267.733333v482.197334a85.333333 85.333333 0 0 1-116.266666 79.530666l-186.133334-72.384a127.786667 127.786667 0 0 1-51.733333-37.12A85.013333 85.013333 0 0 1 341.333333 725.333333h-128a128 128 0 0 1-128-128v-192a128 128 0 0 1 128-128h128c11.669333 0 22.805333 2.346667 32.938667 6.592a127.445333 127.445333 0 0 1 54.592-35.776l184.149333-61.376a85.333333 85.333333 0 0 1 107.946667 53.973334z" fill="#FAB007" p-id="1619"></path><path d="M768 469.333333m42.666667 0l85.333333 0q42.666667 0 42.666667 42.666667l0 0q0 42.666667-42.666667 42.666667l-85.333333 0q-42.666667 0-42.666667-42.666667l0 0q0-42.666667 42.666667-42.666667Z" fill="#FEE789" p-id="1620"></path><path d="M758.099166 325.716249m36.950417-21.333333l73.900834-42.666667q36.950417-21.333333 58.283751 15.617084l0 0q21.333333 36.950417-15.617084 58.283751l-73.900835 42.666667q-36.950417 21.333333-58.28375-15.617084l0 0q-21.333333-36.950417 15.617084-58.283751Z" fill="#FEE789" p-id="1621"></path><path d="M758.098241 698.2872m36.950418 21.333333l73.900834 42.666667q36.950417 21.333333 58.283751-15.617084l0 0q21.333333-36.950417-15.617084-58.283751l-73.900835-42.666666q-36.950417-21.333333-58.28375 15.617083l0 0q-21.333333 36.950417 15.617084 58.283751Z" fill="#FEE789" p-id="1622"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1756113949131" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1213" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M516.8128 427.4688a46.08 46.08 0 1 0-46.08-46.08 46.3872 46.3872 0 0 0 46.08 46.08z" fill="#FFA937" p-id="1214"></path><path d="M938.8544 830.2592h-58.5216V381.1328a60.8768 60.8768 0 0 0-60.8768-60.8768h-111.7696V272.384a36.9152 36.9152 0 0 0-12.032-27.2896l-152.5248-139.1616a37.0176 37.0176 0 0 0-49.5616 0L338.176 245.0944a36.864 36.864 0 0 0-12.288 27.4944v47.6672H214.1184a60.8768 60.8768 0 0 0-60.8768 60.8768v449.1264H92.16a30.1056 30.1056 0 0 0 0 60.16h846.6944a30.1056 30.1056 0 1 0 0-60.16z m-422.0416-555.52a106.4448 106.4448 0 1 1-106.4448 106.3936 106.496 106.496 0 0 1 106.4448-106.3936z m80.8448 525.4656v-142.2336a19.3536 19.3536 0 0 0-19.3536-19.3536h-122.88a19.4048 19.4048 0 0 0-19.3536 19.4048v142.1824a30.0544 30.0544 0 0 1-30.0544 30.0544 30.0544 30.0544 0 0 1-30.1056-30.0544v-142.2336A79.4624 79.4624 0 0 1 455.2704 578.56h122.88a79.4624 79.4624 0 0 1 79.5136 79.4624v142.2336a30.0544 30.0544 0 0 1-30.1056 30.0544 30.0544 30.0544 0 0 1-29.9008-30.1056z" fill="#FFA937" p-id="1215"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1756096800268" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6149" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M958.83 512c0 321.16-126.32 447.5-447.48 447.5S63.83 833.15 63.83 512 190.18 64.51 511.36 64.51 958.81 190.83 958.81 512z" fill="#03A9F5" p-id="6150"></path><path d="M391.16 697.63h240.47a16.39 16.39 0 1 1 0 32.79H391.16A17.63 17.63 0 0 1 374.84 714a15.74 15.74 0 0 1 16.32-16.39z m275.67-404.05H355.75A63.54 63.54 0 0 0 293 356.46v229.25a63.54 63.54 0 0 0 62.81 62.81h311a64.13 64.13 0 0 0 62.81-65.58V356.46a63.54 63.54 0 0 0-62.81-62.89zM615 577.76a20.33 20.33 0 0 1-24.56 0l-38.24-38.18a98.22 98.22 0 0 1-54.58 16.32 95.6 95.6 0 1 1 95.53-95.68 98.37 98.37 0 0 1-16.39 54.58L615 553a20.33 20.33 0 0 1 0 24.56z m0 0" fill="#FFFFFF" p-id="6151"></path><path d="M500.39 400.18a60 60 0 1 0 60 60 60 60 0 0 0-60-60z m0 0" fill="#FFFFFF" p-id="6152"></path><path d="M560.44 460.23a60 60 0 1 1-60-60 60 60 0 0 1 60 60z m0 0" fill="#FFFFFF" p-id="6153"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1756113963939" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1717" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M154.7264 150.528m89.2928 0l148.224 0q89.2928 0 89.2928 89.2928l0 148.224q0 89.2928-89.2928 89.2928l-148.224 0q-89.2928 0-89.2928-89.2928l0-148.224q0-89.2928 89.2928-89.2928Z" fill="#5097FF" p-id="1718"></path><path d="M154.7264 541.44m89.2928 0l148.224 0q89.2928 0 89.2928 89.2928l0 148.224q0 89.2928-89.2928 89.2928l-148.224 0q-89.2928 0-89.2928-89.2928l0-148.224q0-89.2928 89.2928-89.2928Z" fill="#5097FF" p-id="1719"></path><path d="M547.4816 541.44m89.2928 0l148.224 0q89.2928 0 89.2928 89.2928l0 148.224q0 89.2928-89.2928 89.2928l-148.224 0q-89.2928 0-89.2928-89.2928l0-148.224q0-89.2928 89.2928-89.2928Z" fill="#5097FF" p-id="1720"></path><path d="M710.906623 97.648519m63.139544 63.139545l90.002814 90.002814q63.139544 63.139544 0 126.279088l-90.002814 90.002814q-63.139544 63.139544-126.279089 0l-90.002814-90.002814q-63.139544-63.139544 0-126.279088l90.002814-90.002814q63.139544-63.139544 126.279089 0Z" fill="#5097FF" p-id="1721"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1756096945880" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2454" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M711.104 0c47.104 0 85.312 38.208 85.312 85.312v853.376c0 47.104-38.208 85.312-85.312 85.312H312.96a85.312 85.312 0 0 1-85.312-85.312V85.312C227.584 38.208 265.792 0 312.896 0h398.208zM113.792 142.208c31.36 0 56.896 25.472 56.896 56.96v682.624c0 31.36-25.472 56.896-56.96 56.896H0V142.208h113.792z m910.208 0v796.48h-113.792a56.896 56.896 0 0 1-56.896-56.96V199.168c0-31.36 25.472-56.896 56.96-56.896H1024z" fill="#25DCA2" p-id="2455"></path></svg>
|
||||
|
After Width: | Height: | Size: 781 B |
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1756096967537" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3118" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M1011.512149 85.354138V1023.999905H180.011738C87.414674 1023.999905 12.487851 947.199912 12.487851 852.292604V171.707301C12.487851 76.799993 87.414674 0 180.011738 0h664.975548v682.64579H181.073202c-46.829264 0-85.354138 39.46146-85.354139 87.477065v81.108285c0 47.953166 38.524874 87.414626 85.354139 87.414626h747.207735V85.354138H1011.512149zM844.987286 767.999929H179.012714v85.354138h665.974572V767.999929z" fill="#FE9C27" p-id="3119"></path></svg>
|
||||
|
After Width: | Height: | Size: 786 B |
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1756096958321" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2784" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M864.097437 0H159.841877A127.67992 127.67992 0 0 0 31.969957 127.42392v769.08752C31.969957 966.911396 89.249922 1023.99936 159.841877 1023.99936h704.25556a127.61592 127.61592 0 0 0 127.80792-127.35992V127.35992C991.969357 57.023964 934.689393 0 864.097437 0z m-563.199648 277.439827h39.807975c30.591981 0 55.359965 19.135988 55.359966 42.687973 0 23.679985-24.767985 42.751973-55.359966 42.751973H300.769789c-30.591981 0-55.359965-19.135988-55.359965-42.751973 0-23.551985 24.767985-42.687973 55.359965-42.687973z m422.143736 490.623693H300.833789c-30.591981 0-55.359965-19.135988-55.359965-42.687973 0-23.679985 24.767985-42.751973 55.359965-42.751974h422.207736c30.655981 0 55.423965 19.135988 55.423966 42.751974 0 23.551985-24.767985 42.687973-55.423966 42.687973z m0-192.31988H300.833789c-30.591981 0-55.359965-19.007988-55.359965-42.431973 0-23.487985 24.767985-42.495973 55.359965-42.495974h422.207736c30.655981 0 55.423965 19.007988 55.423966 42.495974 0 23.423985-24.767985 42.431973-55.423966 42.431973z m0-212.863867H552.033632c-30.591981 0-55.423965-19.135988-55.423965-42.687973 0-23.679985 24.831984-42.751973 55.423965-42.751973h171.007893c30.655981 0 55.423965 19.135988 55.423966 42.751973 0 23.551985-24.767985 42.687973-55.423966 42.687973z" fill="#FF7400" p-id="2785"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 3.0 KiB |
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1756113811295" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="943" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M648.746667 241.130667l-90.176-90.816a65.706667 65.706667 0 0 0-93.397334 0l-90.197333 90.816a65.728 65.728 0 0 1-65.344 16.789333l-113.066667-33.536c-42.304-12.522667-84.693333 19.413333-84.693333 63.808v489.152c0 36.736 29.568 66.496 66.048 66.496h667.904c36.48 0 66.048-29.76 66.048-66.496V288.192c0-44.394667-42.389333-76.352-84.693333-63.786667l-113.066667 33.493334a65.728 65.728 0 0 1-65.344-16.768z" fill="#FE7840" p-id="944"></path><path d="M585.301333 373.909333h-146.858666c-16.746667 0-32.832 7.082667-44.672 19.669334l-101.162667 107.562666a26.794667 26.794667 0 0 0 0 36.245334l196.693333 209.109333a30.613333 30.613333 0 0 0 45.141334 0l196.693333-209.109333c9.408-10.026667 9.408-26.24 0-36.266667L629.973333 393.6a61.333333 61.333333 0 0 0-44.672-19.669333z" fill="#FFFFFF" opacity=".56" p-id="945"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1756113951989" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1382" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M931.1232 465.3056l-94.72-87.6544V216.064a50.0224 50.0224 0 1 0-99.9424 0v69.0176l-175.9744-162.9184C532.992 98.8672 481.792 104.6528 457.728 128L98.9184 465.0496a49.9712 49.9712 0 1 0 68.4544 72.8064L467.2 256A66.56 66.56 0 0 1 558.08 256l305.3568 282.7776a50.0224 50.0224 0 0 0 67.9424-73.3696z" fill="#FF5A3E" p-id="1383"></path><path d="M546.816 301.4144a50.2272 50.2272 0 0 0-66.56 0L189.44 579.072v289.536a47.616 47.616 0 0 0 48.9472 46.08H788.48a47.616 47.616 0 0 0 48.9472-46.08v-290.816z" fill="#FF5A3E" p-id="1384"></path></svg>
|
||||
|
After Width: | Height: | Size: 871 B |
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1756096961327" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2952" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M929.6 134.336c-20.16-20.8-43.52-31.168-69.952-31.168H652.16A159.68 159.68 0 0 0 598.4 28.8C573.44 9.6 544.704 0 512.064 0c-32.704 0-61.44 9.536-86.4 28.736-24.832 19.2-42.752 43.968-53.632 74.368H164.352c-26.432 0-49.792 10.432-70.016 31.168-20.224 20.864-30.336 44.8-30.336 72v714.56c0 27.136 10.176 51.136 30.336 71.936 20.224 20.864 43.584 31.232 70.016 31.232h695.296c26.432 0 49.792-10.432 69.952-31.168 20.288-20.8 30.4-44.736 30.4-71.936V206.272c0-27.136-10.176-51.136-30.4-71.936z m-452.672-16.832A47.104 47.104 0 0 1 512 103.104c14.08 0 25.664 4.8 35.072 14.4 9.344 9.6 13.952 21.568 13.952 35.968a53.824 53.824 0 0 1-13.952 37.184 45.184 45.184 0 0 1-35.072 15.616 45.312 45.312 0 0 1-35.072-15.552 53.568 53.568 0 0 1-13.952-37.184c0-14.4 4.608-26.368 13.952-36.032z m135.424 702.656H262.336v-103.168h349.952v103.168zM761.6 613.888H262.336V513.216h499.328v100.672z m0-203.84H262.336V306.944h499.328v103.104z" fill="#5290FB" p-id="2953"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1756096928165" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1282" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M716.270137 257.088L473.710137 27.328a99.328 99.328 0 0 0-136.832 0.064L95.214137 257.152A100.736 100.736 0 0 0 63.982137 329.92v593.664C63.982137 979.072 108.590137 1024 163.694137 1024h484.16c55.104 0 99.712-44.864 99.712-100.288V330.112c0-27.648-11.264-54.08-31.36-72.96zM408.238137 178.624c31.616 0 57.152 25.792 57.152 57.6 0 31.744-25.6 57.472-57.152 57.472a57.344 57.344 0 0 1-57.216-57.536c0-31.744 25.6-57.536 57.216-57.536z m-100.608 650.88l63.104-183.552H254.318137l196.352-225.28-12.608 167.424 104.32 5.248-234.752 236.16z m498.176-358.912l146.432 290.88c17.344 34.56 4.608 76.8-28.992 95.68l-119.68 67.328 2.24-453.888z" fill="#FF8E12" p-id="1283"></path></svg>
|
||||
|
After Width: | Height: | Size: 1008 B |
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1756096832904" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7620" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M959.6 512.3c0 321.16-126.32 447.5-447.48 447.5S64.6 833.46 64.6 512.3 190.94 64.82 512.12 64.82s447.45 126.32 447.45 447.5z m0 0" fill="#F76969" p-id="7621"></path><path d="M721.15 452.57c-232.91 104.54-418.14 0-418.14 0v65.63c200.61 121.12 418.14 0 418.14 0z" fill="#FFFFFF" p-id="7622"></path><path d="M303 607.85c200.61 121.13 418.14 0 418.14 0v-65.68c-232.91 104.55-418.14 0-418.14 0zM512.1 273.35c-115.48 0-209.1 46.82-209.1 104.54 0 37.34 39.8 71.87 104.55 90.54s144.38 18.67 209.09 0 104.55-53.17 104.55-90.54c0-57.72-93.6-104.54-209.09-104.54z" fill="#FFFFFF" p-id="7623"></path><path d="M303 697.46c200.61 121.09 418.14 0 418.14 0v-65.67c-232.91 104.54-418.14 0-418.14 0z" fill="#FFFFFF" p-id="7624"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 1.8 KiB |