wuzhen更新社团管理新需求
This commit is contained in:
1 parent
52d892337e
commit
0745d287f5
32 files changed
+1634
-72
No files matched your search
+9
@@ -98,4 +98,13 @@ public class StglClassController extends BaseController {
|
||||
List<StglClassVo> list = stglClassService.queryList(bo);
|
||||
ExcelUtil.exportExcel(list, "班级学生参与情况", StglClassVo.class, response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询班主任所辖班级中当前正在参加活动的学生列表
|
||||
*/
|
||||
//@SaCheckPermission("stgl:stclass:list")
|
||||
@GetMapping("/current-activities")
|
||||
public TableDataInfo<Map<String, Object>> currentActivities(StglClassBo bo, PageQuery pageQuery) {
|
||||
return stglClassService.getCurrentActiveStudents(bo, pageQuery);
|
||||
}
|
||||
}
|
||||
+44
@@ -29,6 +29,9 @@ import org.dromara.stgl.domain.vo.StglMemberVo;
|
||||
import org.dromara.stgl.domain.vo.StglMemberImportVo;
|
||||
import org.dromara.stgl.domain.bo.StglMemberBo;
|
||||
import org.dromara.stgl.service.IStglMemberService;
|
||||
import org.dromara.stgl.service.IStglMemberApprovalService;
|
||||
import org.dromara.stgl.domain.bo.StglMemberApprovalBo;
|
||||
import org.dromara.stgl.domain.vo.StglMemberApprovalVo;
|
||||
import org.dromara.stgl.listener.StglMemberImportListener;
|
||||
import org.dromara.common.mybatis.core.page.TableDataInfo;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
@@ -51,6 +54,7 @@ import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
public class StglMemberController extends BaseController {
|
||||
|
||||
private final IStglMemberService stglMemberService;
|
||||
private final IStglMemberApprovalService memberApprovalService;
|
||||
|
||||
/**
|
||||
* 查询社团成员列表
|
||||
@@ -232,4 +236,44 @@ public class StglMemberController extends BaseController {
|
||||
ExcelUtil.exportExcel(templateData, "社团成员导入模板", StglMemberImportVo.class, response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 社长审批成员申请
|
||||
*/
|
||||
@Operation(summary = "社长审批成员申请")
|
||||
@Log(title = "社长审批", businessType = BusinessType.UPDATE)
|
||||
@RepeatSubmit()
|
||||
@PutMapping("/presidentApprove")
|
||||
public R<Void> presidentApprove(@RequestBody StglMemberApprovalBo bo) {
|
||||
return toAjax(memberApprovalService.presidentApprove(bo.getMemberId(), bo.getApproved(), bo.getOpinion()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 指导老师审批成员申请
|
||||
*/
|
||||
@Operation(summary = "指导老师审批成员申请")
|
||||
@Log(title = "指导老师审批", businessType = BusinessType.UPDATE)
|
||||
@RepeatSubmit()
|
||||
@PutMapping("/advisorApprove")
|
||||
public R<Void> advisorApprove(@RequestBody StglMemberApprovalBo bo) {
|
||||
return toAjax(memberApprovalService.advisorApprove(bo.getMemberId(), bo.getApproved(), bo.getOpinion()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取成员申请的审批流程
|
||||
*/
|
||||
@Operation(summary = "获取成员申请的审批流程")
|
||||
@GetMapping("/approvalFlow/{memberId}")
|
||||
public R<List<StglMemberApprovalVo>> getApprovalFlow(@PathVariable Long memberId) {
|
||||
return R.ok(memberApprovalService.getApprovalFlow(memberId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取待当前用户审批的成员列表
|
||||
*/
|
||||
@Operation(summary = "获取待当前用户审批的成员列表")
|
||||
@GetMapping("/pendingApprovals")
|
||||
public R<List<StglMemberApprovalVo>> getPendingApprovals(@RequestParam(required = false) Long clubId) {
|
||||
return R.ok(memberApprovalService.getPendingApprovals(clubId));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -152,6 +152,11 @@ public class StglClub extends TenantEntity {
|
||||
*/
|
||||
private String advisorsJson;
|
||||
|
||||
/**
|
||||
* 归属(部门ID)
|
||||
*/
|
||||
private Long affiliation;
|
||||
|
||||
/**
|
||||
* 成员规模上限
|
||||
*/
|
||||
@@ -197,6 +202,21 @@ public class StglClub extends TenantEntity {
|
||||
*/
|
||||
private String sighOpen;
|
||||
|
||||
/**
|
||||
* 社长用户ID
|
||||
*/
|
||||
private Long presidentUserId;
|
||||
|
||||
/**
|
||||
* 物资保管员用户ID
|
||||
*/
|
||||
private Long materialKeeperUserId;
|
||||
|
||||
/**
|
||||
* 物资保管员姓名
|
||||
*/
|
||||
private String materialKeeperName;
|
||||
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
package org.dromara.stgl.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import org.dromara.common.tenant.core.TenantEntity;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 成员申请审批流程对象 stgl_member_approval
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("stgl_member_approval")
|
||||
public class StglMemberApproval extends TenantEntity {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@TableId(value = "id")
|
||||
private Long id;
|
||||
|
||||
/** 成员记录ID(关联stgl_member) */
|
||||
private Long memberId;
|
||||
|
||||
/** 社团ID */
|
||||
private Long clubId;
|
||||
|
||||
/** 节点代码(president_approve/advisor_approve) */
|
||||
private String nodeCode;
|
||||
|
||||
/** 节点名称(社长审批/指导老师审批) */
|
||||
private String nodeName;
|
||||
|
||||
/** 节点顺序(1.0, 2.0) */
|
||||
private BigDecimal nodeOrder;
|
||||
|
||||
/** 审批人ID */
|
||||
private Long approverId;
|
||||
|
||||
/** 审批人姓名 */
|
||||
private String approverName;
|
||||
|
||||
/** 审批结果(通过/不通过) */
|
||||
private String approveResult;
|
||||
|
||||
/** 审批状态(pending/approved/rejected) */
|
||||
private String auditStatus;
|
||||
|
||||
/** 审批意见 */
|
||||
private String auditOpinion;
|
||||
|
||||
/** 审批时间 */
|
||||
private Date auditTime;
|
||||
}
|
||||
+5
@@ -12,6 +12,7 @@ import org.dromara.common.mybatis.core.domain.BaseEntity;
|
||||
import org.dromara.stgl.domain.StglActivity;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 活动信息业务对象 stgl_activity
|
||||
@@ -229,4 +230,8 @@ public class StglActivityBo extends BaseEntity {
|
||||
*/
|
||||
private String remark;
|
||||
|
||||
/**
|
||||
* 邀请成员ID列表(非数据库字段,用于创建报名记录)
|
||||
*/
|
||||
private List<Long> invitedMemberIds;
|
||||
}
|
||||
+21
@@ -184,6 +184,11 @@ public class StglClubBo extends BaseEntity {
|
||||
*/
|
||||
private String advisorsJson;
|
||||
|
||||
/**
|
||||
* 归属(部门ID)
|
||||
*/
|
||||
private Long affiliation;
|
||||
|
||||
/**
|
||||
* 成员规模上限
|
||||
*/
|
||||
@@ -240,6 +245,22 @@ public class StglClubBo extends BaseEntity {
|
||||
*/
|
||||
private String remark;
|
||||
|
||||
/**
|
||||
* 社长用户ID(创建时选择学生)
|
||||
*/
|
||||
private Long presidentUserId;
|
||||
|
||||
/**
|
||||
* 物资保管员用户ID(创建时选择学生)
|
||||
*/
|
||||
private Long materialKeeperUserId;
|
||||
|
||||
/**
|
||||
* 物资保管员姓名
|
||||
*/
|
||||
@Size(max = 50, message = "物资保管员姓名长度不能超过50个字符")
|
||||
private String materialKeeperName;
|
||||
|
||||
/**
|
||||
* 社团ID列表(用于数据权限控制)
|
||||
*/
|
||||
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package org.dromara.stgl.domain.bo;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 成员审批请求对象
|
||||
*/
|
||||
@Data
|
||||
public class StglMemberApprovalBo {
|
||||
|
||||
/** 成员记录ID */
|
||||
@NotNull(message = "成员记录ID不能为空")
|
||||
private Long memberId;
|
||||
|
||||
/** 是否通过 */
|
||||
@NotNull(message = "审批结果不能为空")
|
||||
private Boolean approved;
|
||||
|
||||
/** 审批意见 */
|
||||
private String opinion;
|
||||
}
|
||||
+21
@@ -178,6 +178,11 @@ public class StglClubVo implements Serializable {
|
||||
*/
|
||||
private String advisorsJson;
|
||||
|
||||
/**
|
||||
* 归属(部门ID)
|
||||
*/
|
||||
private Long affiliation;
|
||||
|
||||
/**
|
||||
* 成员规模上限
|
||||
*/
|
||||
@@ -232,6 +237,22 @@ public class StglClubVo implements Serializable {
|
||||
@ExcelProperty(value = "开启报名")
|
||||
private String sighOpen;
|
||||
|
||||
/**
|
||||
* 社长用户ID
|
||||
*/
|
||||
private Long presidentUserId;
|
||||
|
||||
/**
|
||||
* 物资保管员用户ID
|
||||
*/
|
||||
private Long materialKeeperUserId;
|
||||
|
||||
/**
|
||||
* 物资保管员姓名
|
||||
*/
|
||||
@ExcelProperty(value = "物资保管员")
|
||||
private String materialKeeperName;
|
||||
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
package org.dromara.stgl.domain.vo;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 成员申请审批流程视图对象
|
||||
*/
|
||||
@Data
|
||||
public class StglMemberApprovalVo implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private Long id;
|
||||
private Long memberId;
|
||||
private Long clubId;
|
||||
private String nodeCode;
|
||||
private String nodeName;
|
||||
private BigDecimal nodeOrder;
|
||||
private Long approverId;
|
||||
private String approverName;
|
||||
private String approveResult;
|
||||
private String auditStatus;
|
||||
private String auditOpinion;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date auditTime;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date createTime;
|
||||
}
|
||||
+16
@@ -62,4 +62,20 @@ public interface StglClassMapper extends BaseMapperPlus<StglClass, StglClassVo>
|
||||
* @return 活动详情列表
|
||||
*/
|
||||
List<Map<String, Object>> selectStudentActivities(Long studentId);
|
||||
|
||||
/**
|
||||
* 查询指定班级中当前正在参加活动的学生列表
|
||||
*
|
||||
* @param params 查询参数(classId, studentName, studentNo, offset, pageSize)
|
||||
* @return 正在参加活动的学生列表
|
||||
*/
|
||||
List<Map<String, Object>> selectCurrentActiveStudents(Map<String, Object> params);
|
||||
|
||||
/**
|
||||
* 查询指定班级中当前正在参加活动的学生总数
|
||||
*
|
||||
* @param params 查询参数(classId, studentName, studentNo)
|
||||
* @return 学生总数
|
||||
*/
|
||||
Integer selectCurrentActiveStudentsCount(Map<String, Object> params);
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
package org.dromara.stgl.mapper;
|
||||
|
||||
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
|
||||
import org.dromara.stgl.domain.StglMemberApproval;
|
||||
import org.dromara.stgl.domain.vo.StglMemberApprovalVo;
|
||||
|
||||
/**
|
||||
* 成员申请审批流程Mapper接口
|
||||
*/
|
||||
public interface StglMemberApprovalMapper extends BaseMapperPlus<StglMemberApproval, StglMemberApprovalVo> {
|
||||
}
|
||||
+9
@@ -65,4 +65,13 @@ public interface IStglClassService {
|
||||
* @return 活动详情列表
|
||||
*/
|
||||
List<Map<String, Object>> getStudentActivities(Long studentId);
|
||||
|
||||
/**
|
||||
* 查询班主任所辖班级中当前正在参加活动的学生分页列表
|
||||
*
|
||||
* @param bo 查询条件
|
||||
* @param pageQuery 分页参数
|
||||
* @return 分页结果
|
||||
*/
|
||||
TableDataInfo<Map<String, Object>> getCurrentActiveStudents(StglClassBo bo, PageQuery pageQuery);
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
package org.dromara.stgl.service;
|
||||
|
||||
import org.dromara.stgl.domain.vo.StglMemberApprovalVo;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 成员申请审批流程Service接口
|
||||
*/
|
||||
public interface IStglMemberApprovalService {
|
||||
|
||||
/**
|
||||
* 为成员申请创建审批流程(社长审批 → 指导老师审批)
|
||||
*
|
||||
* @param memberId 成员记录ID
|
||||
* @param clubId 社团ID
|
||||
* @return 是否创建成功
|
||||
*/
|
||||
Boolean createApprovalFlow(Long memberId, Long clubId);
|
||||
|
||||
/**
|
||||
* 获取成员申请的审批流程列表
|
||||
*
|
||||
* @param memberId 成员记录ID
|
||||
* @return 审批节点列表
|
||||
*/
|
||||
List<StglMemberApprovalVo> getApprovalFlow(Long memberId);
|
||||
|
||||
/**
|
||||
* 获取待当前用户审批的列表
|
||||
*
|
||||
* @param clubId 社团ID(可选)
|
||||
* @return 待审批的成员列表
|
||||
*/
|
||||
List<StglMemberApprovalVo> getPendingApprovals(Long clubId);
|
||||
|
||||
/**
|
||||
* 社长审批
|
||||
*
|
||||
* @param memberId 成员记录ID
|
||||
* @param approved 是否通过
|
||||
* @param opinion 审批意见
|
||||
* @return 是否审批成功
|
||||
*/
|
||||
Boolean presidentApprove(Long memberId, Boolean approved, String opinion);
|
||||
|
||||
/**
|
||||
* 指导老师审批
|
||||
*
|
||||
* @param memberId 成员记录ID
|
||||
* @param approved 是否通过
|
||||
* @param opinion 审批意见
|
||||
* @return 是否审批成功
|
||||
*/
|
||||
Boolean advisorApprove(Long memberId, Boolean approved, String opinion);
|
||||
}
|
||||
+39
-2
@@ -47,10 +47,13 @@ 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.domain.StglActivityRegistration;
|
||||
import org.dromara.stgl.domain.StglMember;
|
||||
import org.dromara.stgl.domain.vo.StglClubVo;
|
||||
import org.dromara.stgl.domain.vo.StglVolunteerTeamVo;
|
||||
import org.dromara.stgl.mapper.StglClubMapper;
|
||||
import org.dromara.stgl.mapper.StglVolunteerTeamMapper;
|
||||
import org.dromara.stgl.mapper.StglActivityRegistrationMapper;
|
||||
import org.dromara.system.domain.SysUser;
|
||||
import org.dromara.system.domain.vo.SysUserVo;
|
||||
import org.dromara.system.service.ISysUserService;
|
||||
@@ -76,6 +79,7 @@ public class StglActivityServiceImpl extends ServiceImpl<StglActivityMapper, Stg
|
||||
private final IStglActivityAuditService activityAuditService;
|
||||
private final StglActivityAuditMapper activityAuditMapper;
|
||||
private final IStglClubService clubService;
|
||||
private final StglActivityRegistrationMapper registrationMapper;
|
||||
|
||||
/**
|
||||
* 查询活动信息
|
||||
@@ -283,8 +287,8 @@ public class StglActivityServiceImpl extends ServiceImpl<StglActivityMapper, Stg
|
||||
boolean flag = baseMapper.insert(add) > 0;
|
||||
if (flag) {
|
||||
bo.setId(add.getId());
|
||||
// 为指导老师分配角色
|
||||
assignAdvisorRoles(add);
|
||||
createInvitedRegistrations(add.getId(), bo.getInvitedMemberIds());
|
||||
}
|
||||
return flag;
|
||||
}
|
||||
@@ -311,8 +315,8 @@ public class StglActivityServiceImpl extends ServiceImpl<StglActivityMapper, Stg
|
||||
boolean updateResult = baseMapper.updateById(update) > 0;
|
||||
|
||||
if (updateResult) {
|
||||
// 处理指导老师角色分配
|
||||
handleAdvisorRoleAssignment(oldActivity, update);
|
||||
createInvitedRegistrations(update.getId(), bo.getInvitedMemberIds());
|
||||
|
||||
// 监听状态变化:如果状态从0(草稿)变为8(已提交),自动创建审批记录
|
||||
if (oldActivity != null &&
|
||||
@@ -1018,4 +1022,37 @@ public class StglActivityServiceImpl extends ServiceImpl<StglActivityMapper, Stg
|
||||
|
||||
// 注意:submitActivityForApproval 和 withdrawActivityApproval 方法已废弃
|
||||
// 功能已整合到 updateByBo 方法中,通过监听状态变化自动处理审批记录
|
||||
|
||||
private void createInvitedRegistrations(Long activityId, List<Long> invitedMemberIds) {
|
||||
if (invitedMemberIds == null || invitedMemberIds.isEmpty() || activityId == null) {
|
||||
return;
|
||||
}
|
||||
Date now = new Date();
|
||||
for (Long userId : invitedMemberIds) {
|
||||
LambdaQueryWrapper<StglActivityRegistration> existLqw = Wrappers.lambdaQuery();
|
||||
existLqw.eq(StglActivityRegistration::getActivityId, activityId)
|
||||
.eq(StglActivityRegistration::getUserId, userId);
|
||||
if (registrationMapper.selectCount(existLqw) > 0) {
|
||||
continue;
|
||||
}
|
||||
StglMember member = stglMemberMapper.selectOne(
|
||||
Wrappers.<StglMember>lambdaQuery()
|
||||
.eq(StglMember::getUserId, userId)
|
||||
.eq(StglMember::getStatus, 2)
|
||||
.last("LIMIT 1")
|
||||
);
|
||||
StglActivityRegistration reg = new StglActivityRegistration();
|
||||
reg.setActivityId(activityId);
|
||||
reg.setUserId(userId);
|
||||
reg.setStudentNo(member != null ? member.getStudentNo() : "");
|
||||
reg.setStudentName(member != null ? member.getStudentName() : "");
|
||||
reg.setRegistrationType("1");
|
||||
reg.setRegistrationTime(now);
|
||||
reg.setApprovalStatus("1");
|
||||
reg.setApprovalTime(now);
|
||||
reg.setRemark("活动邀请自动报名");
|
||||
registrationMapper.insert(reg);
|
||||
}
|
||||
log.info("活动邀请报名完成: activityId={}, 邀请人数={}", activityId, invitedMemberIds.size());
|
||||
}
|
||||
}
|
||||
+17
@@ -115,6 +115,23 @@ public class StglClassServiceImpl implements IStglClassService {
|
||||
return baseMapper.selectStudentActivities(studentId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TableDataInfo<Map<String, Object>> getCurrentActiveStudents(StglClassBo bo, PageQuery pageQuery) {
|
||||
Map<String, Object> params = buildQueryParams(bo);
|
||||
params.put("offset", (pageQuery.getPageNum() - 1) * pageQuery.getPageSize());
|
||||
params.put("pageSize", pageQuery.getPageSize());
|
||||
|
||||
List<Map<String, Object>> list = baseMapper.selectCurrentActiveStudents(params);
|
||||
|
||||
Map<String, Object> countParams = buildQueryParams(bo);
|
||||
Integer total = baseMapper.selectCurrentActiveStudentsCount(countParams);
|
||||
|
||||
TableDataInfo<Map<String, Object>> result = new TableDataInfo<>();
|
||||
result.setRows(list);
|
||||
result.setTotal(total != null ? total : 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建查询参数
|
||||
*
|
||||
|
||||
+10
-3
@@ -15,6 +15,7 @@ import org.dromara.stgl.domain.StglMember;
|
||||
import org.dromara.stgl.mapper.StglClubMapper;
|
||||
import org.dromara.stgl.mapper.StglMemberMapper;
|
||||
import org.dromara.stgl.service.IStglClubRegisterService;
|
||||
import org.dromara.stgl.service.IStglMemberApprovalService;
|
||||
import org.dromara.common.satoken.utils.LoginHelper;
|
||||
import org.dromara.stgl.domain.vo.StglClubVo;
|
||||
import org.dromara.system.domain.SysUser;
|
||||
@@ -40,6 +41,7 @@ public class StglClubRegisterServiceImpl implements IStglClubRegisterService {
|
||||
private final StglMemberMapper memberMapper;
|
||||
private final SysUserMapper sysUserMapper;
|
||||
private final XgStudentMsgMapper xgStudentMsgMapper;
|
||||
private final IStglMemberApprovalService memberApprovalService;
|
||||
|
||||
/**
|
||||
* 分页查询社团报名大厅列表
|
||||
@@ -247,10 +249,15 @@ public class StglClubRegisterServiceImpl implements IStglClubRegisterService {
|
||||
member.setStudentName(studentName != null ? studentName : user.getNickName()); // 如果没有学生信息,使用用户昵称
|
||||
// 申请时不设置roleType,等待审核通过后再设置
|
||||
member.setJoinTime(new Date());
|
||||
member.setStatus(0); // 待审核状态(stgl_member_status字典,新报名需审核)
|
||||
member.setRemark(reason); // 将申请理由存储到备注字段
|
||||
member.setStatus(0); // 待审核状态
|
||||
member.setRemark(reason);
|
||||
|
||||
return memberMapper.insert(member) > 0;
|
||||
boolean inserted = memberMapper.insert(member) > 0;
|
||||
if (inserted) {
|
||||
// 创建两级审批流程:社长审批 → 指导老师审批
|
||||
memberApprovalService.createApprovalFlow(member.getId(), clubId);
|
||||
}
|
||||
return inserted;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+163
-12
@@ -334,23 +334,162 @@ public class StglClubServiceImpl implements IStglClubService {
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Boolean insertByBo(StglClubBo bo) {
|
||||
Long currentUserId = LoginHelper.getUserId();
|
||||
if (currentUserId == null) {
|
||||
throw new RuntimeException("用户未登录");
|
||||
}
|
||||
|
||||
StglClub add = MapstructUtils.convert(bo, StglClub.class);
|
||||
|
||||
// 根据社长用户ID从学生表查询并填充冗余字段
|
||||
if (add.getPresidentUserId() != null) {
|
||||
fillPresidentInfoFromStudent(add);
|
||||
}
|
||||
|
||||
// 创建者自动设为首席指导老师
|
||||
add.setCreateBy(currentUserId);
|
||||
if (StringUtils.isBlank(add.getAdvisorName())) {
|
||||
// 从用户表获取创建者(老师)信息填充指导老师字段
|
||||
fillCreatorAsAdvisor(add, currentUserId);
|
||||
}
|
||||
|
||||
validEntityBeforeSave(add);
|
||||
boolean flag = baseMapper.insert(add) > 0;
|
||||
if (flag) {
|
||||
bo.setId(add.getId());
|
||||
// 自动创建社长和副社长的成员记录
|
||||
createLeaderMemberRecords(add);
|
||||
// 为指导老师分配角色
|
||||
createMaterialKeeperMemberRecord(add);
|
||||
assignAdvisorRoles(add);
|
||||
// 为社长分配角色
|
||||
assignPresidentRole(add);
|
||||
// 创建指导老师历史记录
|
||||
createAdvisorHistoryRecords(add);
|
||||
}
|
||||
return flag;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据社长用户ID从学生表查询信息,自动填充冗余字段
|
||||
*/
|
||||
private void fillPresidentInfoFromStudent(StglClub club) {
|
||||
try {
|
||||
LambdaQueryWrapper<XgStudentMsg> wrapper = Wrappers.lambdaQuery();
|
||||
wrapper.eq(XgStudentMsg::getId, club.getPresidentUserId())
|
||||
.eq(XgStudentMsg::getDeleted, 0)
|
||||
.last("LIMIT 1");
|
||||
XgStudentMsg student = studentMsgMapper.selectOne(wrapper);
|
||||
if (student != null) {
|
||||
club.setPresidentName(student.getName());
|
||||
club.setPresidentStudentNo(student.getStudentNo());
|
||||
club.setPresidentDept(student.getCollegeName());
|
||||
club.setPresidentMajor(student.getMajorName());
|
||||
club.setPresidentClass(student.getClassName());
|
||||
club.setPresidentPhone(student.getPhone());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("根据用户ID查询社长学生信息失败: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将创建者设为首席指导老师
|
||||
*/
|
||||
private void fillCreatorAsAdvisor(StglClub club, Long userId) {
|
||||
try {
|
||||
String nickName = LoginHelper.getLoginUser().getNickname();
|
||||
club.setAdvisorName(nickName);
|
||||
} catch (Exception e) {
|
||||
log.warn("获取创建者老师信息失败: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建物资保管员成员记录
|
||||
*/
|
||||
private void createMaterialKeeperMemberRecord(StglClub club) {
|
||||
if (club.getMaterialKeeperUserId() == null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
LambdaQueryWrapper<XgStudentMsg> wrapper = Wrappers.lambdaQuery();
|
||||
wrapper.eq(XgStudentMsg::getId, club.getMaterialKeeperUserId())
|
||||
.eq(XgStudentMsg::getDeleted, 0)
|
||||
.last("LIMIT 1");
|
||||
XgStudentMsg student = studentMsgMapper.selectOne(wrapper);
|
||||
if (student == null) {
|
||||
log.warn("物资保管员学生信息不存在, userId={}", club.getMaterialKeeperUserId());
|
||||
return;
|
||||
}
|
||||
|
||||
StglMember keeper = new StglMember();
|
||||
keeper.setMemberType(1);
|
||||
keeper.setRelationId(club.getId());
|
||||
keeper.setUserId(club.getMaterialKeeperUserId());
|
||||
keeper.setStudentNo(student.getStudentNo());
|
||||
keeper.setStudentName(student.getName());
|
||||
keeper.setRoleType("物资保管员");
|
||||
keeper.setJoinTime(new Date());
|
||||
keeper.setStatus(2);
|
||||
keeper.setRemark("社团创建时自动添加");
|
||||
memberMapper.insert(keeper);
|
||||
|
||||
// 回填物资保管员姓名到社团表
|
||||
if (StringUtils.isBlank(club.getMaterialKeeperName())) {
|
||||
StglClub updateClub = new StglClub();
|
||||
updateClub.setId(club.getId());
|
||||
updateClub.setMaterialKeeperName(student.getName());
|
||||
baseMapper.updateById(updateClub);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("创建物资保管员成员记录失败: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void syncMaterialKeeperMemberRecord(StglClub newClub, StglClub oldClub) {
|
||||
Long newKeeperId = newClub.getMaterialKeeperUserId();
|
||||
Long oldKeeperId = oldClub != null ? oldClub.getMaterialKeeperUserId() : null;
|
||||
|
||||
if (java.util.Objects.equals(newKeeperId, oldKeeperId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 删除旧的物资保管员成员记录
|
||||
if (oldKeeperId != null) {
|
||||
LambdaQueryWrapper<StglMember> delWrapper = Wrappers.lambdaQuery();
|
||||
delWrapper.eq(StglMember::getRelationId, newClub.getId())
|
||||
.eq(StglMember::getMemberType, 1)
|
||||
.eq(StglMember::getRoleType, "物资保管员");
|
||||
memberMapper.delete(delWrapper);
|
||||
}
|
||||
|
||||
// 创建新的物资保管员成员记录
|
||||
if (newKeeperId != null) {
|
||||
LambdaQueryWrapper<XgStudentMsg> wrapper = Wrappers.lambdaQuery();
|
||||
wrapper.eq(XgStudentMsg::getId, newKeeperId)
|
||||
.eq(XgStudentMsg::getDeleted, 0)
|
||||
.last("LIMIT 1");
|
||||
XgStudentMsg student = studentMsgMapper.selectOne(wrapper);
|
||||
if (student != null) {
|
||||
StglMember keeper = new StglMember();
|
||||
keeper.setMemberType(1);
|
||||
keeper.setRelationId(newClub.getId());
|
||||
keeper.setUserId(newKeeperId);
|
||||
keeper.setStudentNo(student.getStudentNo());
|
||||
keeper.setStudentName(student.getName());
|
||||
keeper.setRoleType("物资保管员");
|
||||
keeper.setJoinTime(new Date());
|
||||
keeper.setStatus(2);
|
||||
keeper.setRemark("修改社团信息时更新");
|
||||
memberMapper.insert(keeper);
|
||||
|
||||
if (StringUtils.isBlank(newClub.getMaterialKeeperName())) {
|
||||
StglClub updateName = new StglClub();
|
||||
updateName.setId(newClub.getId());
|
||||
updateName.setMaterialKeeperName(student.getName());
|
||||
baseMapper.updateById(updateName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改社团信息
|
||||
*
|
||||
@@ -375,6 +514,8 @@ public class StglClubServiceImpl implements IStglClubService {
|
||||
if (updateResult) {
|
||||
// 同步更新副社长的成员记录
|
||||
syncVicePresidentsInMembers(update);
|
||||
// 同步物资保管员成员记录
|
||||
syncMaterialKeeperMemberRecord(update, oldClub);
|
||||
// 处理指导老师角色分配
|
||||
handleAdvisorRoleAssignment(oldClub, update);
|
||||
// 更新指导老师历史记录
|
||||
@@ -523,24 +664,34 @@ public class StglClubServiceImpl implements IStglClubService {
|
||||
private void createLeaderMemberRecords(StglClub club) {
|
||||
Date now = new Date();
|
||||
|
||||
// 1. 创建社长成员记录
|
||||
if (StringUtils.isNotBlank(club.getPresidentStudentNo())) {
|
||||
// 1. 创建社长成员记录(优先用 presidentUserId)
|
||||
if (club.getPresidentUserId() != null) {
|
||||
StglMember president = new StglMember();
|
||||
president.setMemberType(1); // 社团成员
|
||||
president.setMemberType(1);
|
||||
president.setRelationId(club.getId());
|
||||
president.setUserId(club.getPresidentUserId());
|
||||
president.setStudentNo(club.getPresidentStudentNo());
|
||||
president.setStudentName(club.getPresidentName());
|
||||
president.setRoleType("社长");
|
||||
president.setJoinTime(now);
|
||||
president.setStatus(2);
|
||||
president.setRemark("社团创建时自动添加");
|
||||
memberMapper.insert(president);
|
||||
} else if (StringUtils.isNotBlank(club.getPresidentStudentNo())) {
|
||||
// 兼容旧逻辑:通过学号查找
|
||||
StglMember president = new StglMember();
|
||||
president.setMemberType(1);
|
||||
president.setRelationId(club.getId());
|
||||
president.setStudentNo(club.getPresidentStudentNo());
|
||||
president.setStudentName(club.getPresidentName());
|
||||
president.setRoleType("社长"); // 社长
|
||||
president.setRoleType("社长");
|
||||
president.setJoinTime(now);
|
||||
president.setStatus(2); // 已加入状态(stgl_member_status字典)
|
||||
president.setStatus(2);
|
||||
president.setRemark("社团创建时自动添加");
|
||||
|
||||
// 根据学号查找用户ID
|
||||
Long presidentUserId = findUserIdByStudentNo(club.getPresidentStudentNo());
|
||||
if (presidentUserId != null) {
|
||||
president.setUserId(presidentUserId);
|
||||
}
|
||||
|
||||
memberMapper.insert(president);
|
||||
}
|
||||
|
||||
|
||||
+262
@@ -0,0 +1,262 @@
|
||||
package org.dromara.stgl.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.common.core.utils.StringUtils;
|
||||
import org.dromara.common.json.utils.JsonUtils;
|
||||
import org.dromara.common.satoken.utils.LoginHelper;
|
||||
import org.dromara.stgl.domain.StglClub;
|
||||
import org.dromara.stgl.domain.StglMember;
|
||||
import org.dromara.stgl.domain.StglMemberApproval;
|
||||
import org.dromara.stgl.domain.vo.StglMemberApprovalVo;
|
||||
import org.dromara.stgl.mapper.StglClubMapper;
|
||||
import org.dromara.stgl.mapper.StglMemberApprovalMapper;
|
||||
import org.dromara.stgl.mapper.StglMemberMapper;
|
||||
import org.dromara.stgl.service.IStglMemberApprovalService;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* 成员申请审批流程Service业务层处理
|
||||
*/
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
@Service
|
||||
public class StglMemberApprovalServiceImpl implements IStglMemberApprovalService {
|
||||
|
||||
private final StglMemberApprovalMapper approvalMapper;
|
||||
private final StglMemberMapper memberMapper;
|
||||
private final StglClubMapper clubMapper;
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Boolean createApprovalFlow(Long memberId, Long clubId) {
|
||||
StglClub club = clubMapper.selectById(clubId);
|
||||
if (club == null) {
|
||||
throw new RuntimeException("社团不存在");
|
||||
}
|
||||
|
||||
// 获取社长信息(审批节点1)
|
||||
Long presidentUserId = club.getPresidentUserId();
|
||||
String presidentName = club.getPresidentName();
|
||||
if (presidentUserId == null) {
|
||||
// 兼容旧数据:从 stgl_member 表查找社长
|
||||
LambdaQueryWrapper<StglMember> lqw = Wrappers.lambdaQuery();
|
||||
lqw.eq(StglMember::getMemberType, 1)
|
||||
.eq(StglMember::getRelationId, clubId)
|
||||
.eq(StglMember::getRoleType, "社长")
|
||||
.eq(StglMember::getStatus, 2)
|
||||
.last("LIMIT 1");
|
||||
StglMember president = memberMapper.selectOne(lqw);
|
||||
if (president != null) {
|
||||
presidentUserId = president.getUserId();
|
||||
presidentName = president.getStudentName();
|
||||
}
|
||||
}
|
||||
if (presidentUserId == null) {
|
||||
throw new RuntimeException("社团暂无社长,无法提交申请");
|
||||
}
|
||||
|
||||
// 获取指导老师信息(审批节点2)- 从 advisors_json 中获取首席指导老师
|
||||
Long advisorUserId = null;
|
||||
String advisorName = club.getAdvisorName();
|
||||
String advisorsJson = club.getAdvisorsJson();
|
||||
if (StringUtils.isNotBlank(advisorsJson)) {
|
||||
try {
|
||||
List<Map<String, Object>> advisors = JsonUtils.parseObject(advisorsJson, new TypeReference<List<Map<String, Object>>>() {});
|
||||
if (advisors != null && !advisors.isEmpty()) {
|
||||
Map<String, Object> first = advisors.get(0);
|
||||
Object uid = first.get("userId");
|
||||
if (uid != null) {
|
||||
advisorUserId = Long.parseLong(uid.toString());
|
||||
}
|
||||
if (first.get("teacher") instanceof Map) {
|
||||
Map<?, ?> teacher = (Map<?, ?>) first.get("teacher");
|
||||
if (teacher.get("nickName") != null) {
|
||||
advisorName = teacher.get("nickName").toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("解析advisors_json失败: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
if (advisorUserId == null) {
|
||||
advisorUserId = club.getCreateBy();
|
||||
}
|
||||
if (advisorUserId == null) {
|
||||
throw new RuntimeException("社团暂无指导老师,无法提交申请");
|
||||
}
|
||||
|
||||
Date now = new Date();
|
||||
Long currentUserId = LoginHelper.getUserId();
|
||||
|
||||
// 节点1:社长审批
|
||||
StglMemberApproval presidentNode = new StglMemberApproval();
|
||||
presidentNode.setMemberId(memberId);
|
||||
presidentNode.setClubId(clubId);
|
||||
presidentNode.setNodeCode("president_approve");
|
||||
presidentNode.setNodeName("社长审批");
|
||||
presidentNode.setNodeOrder(new BigDecimal("1.0"));
|
||||
presidentNode.setApproverId(presidentUserId);
|
||||
presidentNode.setApproverName(presidentName != null ? presidentName : "");
|
||||
presidentNode.setAuditStatus("pending");
|
||||
approvalMapper.insert(presidentNode);
|
||||
|
||||
// 节点2:指导老师审批
|
||||
StglMemberApproval advisorNode = new StglMemberApproval();
|
||||
advisorNode.setMemberId(memberId);
|
||||
advisorNode.setClubId(clubId);
|
||||
advisorNode.setNodeCode("advisor_approve");
|
||||
advisorNode.setNodeName("指导老师审批");
|
||||
advisorNode.setNodeOrder(new BigDecimal("2.0"));
|
||||
advisorNode.setApproverId(advisorUserId);
|
||||
advisorNode.setApproverName(advisorName != null ? advisorName : "");
|
||||
advisorNode.setAuditStatus("pending");
|
||||
approvalMapper.insert(advisorNode);
|
||||
|
||||
log.info("创建成员审批流程: memberId={}, clubId={}, 社长={}, 指导老师={}",
|
||||
memberId, clubId, presidentUserId, advisorUserId);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<StglMemberApprovalVo> getApprovalFlow(Long memberId) {
|
||||
LambdaQueryWrapper<StglMemberApproval> lqw = Wrappers.lambdaQuery();
|
||||
lqw.eq(StglMemberApproval::getMemberId, memberId)
|
||||
.orderByAsc(StglMemberApproval::getNodeOrder);
|
||||
return approvalMapper.selectVoList(lqw);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<StglMemberApprovalVo> getPendingApprovals(Long clubId) {
|
||||
Long currentUserId = LoginHelper.getUserId();
|
||||
LambdaQueryWrapper<StglMemberApproval> lqw = Wrappers.lambdaQuery();
|
||||
lqw.eq(StglMemberApproval::getApproverId, currentUserId)
|
||||
.eq(StglMemberApproval::getAuditStatus, "pending");
|
||||
if (clubId != null) {
|
||||
lqw.eq(StglMemberApproval::getClubId, clubId);
|
||||
}
|
||||
lqw.orderByAsc(StglMemberApproval::getNodeOrder);
|
||||
return approvalMapper.selectVoList(lqw);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Boolean presidentApprove(Long memberId, Boolean approved, String opinion) {
|
||||
Boolean result = doApprove(memberId, "president_approve", approved, opinion);
|
||||
|
||||
if (approved) {
|
||||
StglMember member = memberMapper.selectById(memberId);
|
||||
if (member != null) {
|
||||
StglMember updateMember = new StglMember();
|
||||
updateMember.setId(memberId);
|
||||
updateMember.setStatus(1); // 审核中,等待指导老师审批
|
||||
memberMapper.updateById(updateMember);
|
||||
log.info("社长审批通过,进入指导老师审批阶段: memberId={}", memberId);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Boolean advisorApprove(Long memberId, Boolean approved, String opinion) {
|
||||
Boolean result = doApprove(memberId, "advisor_approve", approved, opinion);
|
||||
|
||||
// 指导老师审批是最后一个节点,如果通过则更新成员状态为已加入
|
||||
if (approved) {
|
||||
StglMember member = memberMapper.selectById(memberId);
|
||||
if (member != null) {
|
||||
StglMember updateMember = new StglMember();
|
||||
updateMember.setId(memberId);
|
||||
updateMember.setStatus(2); // 已加入
|
||||
updateMember.setRoleType("普通成员");
|
||||
memberMapper.updateById(updateMember);
|
||||
log.info("成员申请全部审批通过,已加入社团: memberId={}", memberId);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通用审批处理逻辑
|
||||
*/
|
||||
private Boolean doApprove(Long memberId, String nodeCode, Boolean approved, String opinion) {
|
||||
Long currentUserId = LoginHelper.getUserId();
|
||||
if (currentUserId == null) {
|
||||
throw new RuntimeException("用户未登录");
|
||||
}
|
||||
|
||||
// 查找待审批的节点
|
||||
LambdaQueryWrapper<StglMemberApproval> lqw = Wrappers.lambdaQuery();
|
||||
lqw.eq(StglMemberApproval::getMemberId, memberId)
|
||||
.eq(StglMemberApproval::getNodeCode, nodeCode)
|
||||
.eq(StglMemberApproval::getAuditStatus, "pending");
|
||||
StglMemberApproval approvalNode = approvalMapper.selectOne(lqw);
|
||||
|
||||
if (approvalNode == null) {
|
||||
throw new RuntimeException("未找到待审批记录或已审批过");
|
||||
}
|
||||
|
||||
// 校验审批人身份
|
||||
log.info("审批人校验: approverId={}, currentUserId={}, equals={}",
|
||||
approvalNode.getApproverId(), currentUserId,
|
||||
approvalNode.getApproverId().equals(currentUserId));
|
||||
log.info("审批人校验类型: approverId.class={}, currentUserId.class={}",
|
||||
approvalNode.getApproverId().getClass().getName(),
|
||||
currentUserId.getClass().getName());
|
||||
|
||||
if (!approvalNode.getApproverId().equals(currentUserId)) {
|
||||
throw new RuntimeException("您不是该节点的审批人(节点审批人:" + approvalNode.getApproverId() + ", 当前用户:" + currentUserId + ")");
|
||||
}
|
||||
|
||||
// 更新审批记录
|
||||
StglMemberApproval updateNode = new StglMemberApproval();
|
||||
updateNode.setId(approvalNode.getId());
|
||||
updateNode.setApproveResult(approved ? "通过" : "不通过");
|
||||
updateNode.setAuditStatus(approved ? "approved" : "rejected");
|
||||
updateNode.setAuditOpinion(opinion);
|
||||
updateNode.setAuditTime(new Date());
|
||||
approvalMapper.updateById(updateNode);
|
||||
|
||||
// 如果拒绝,更新成员状态为已拒绝,并标记后续节点也结束
|
||||
if (!approved) {
|
||||
StglMember member = memberMapper.selectById(memberId);
|
||||
if (member != null) {
|
||||
StglMember updateMember = new StglMember();
|
||||
updateMember.setId(memberId);
|
||||
updateMember.setStatus(3); // 已拒绝
|
||||
updateMember.setRemark(opinion != null ? "审批拒绝: " + opinion : "审批拒绝");
|
||||
memberMapper.updateById(updateMember);
|
||||
}
|
||||
|
||||
// 将后续待审批节点也标记为结束
|
||||
LambdaQueryWrapper<StglMemberApproval> pendingLqw = Wrappers.lambdaQuery();
|
||||
pendingLqw.eq(StglMemberApproval::getMemberId, memberId)
|
||||
.eq(StglMemberApproval::getAuditStatus, "pending")
|
||||
.ne(StglMemberApproval::getId, approvalNode.getId());
|
||||
List<StglMemberApproval> pendingNodes = approvalMapper.selectList(pendingLqw);
|
||||
for (StglMemberApproval pending : pendingNodes) {
|
||||
StglMemberApproval cancelNode = new StglMemberApproval();
|
||||
cancelNode.setId(pending.getId());
|
||||
cancelNode.setAuditStatus("rejected");
|
||||
cancelNode.setApproveResult("前置节点拒绝,自动终止");
|
||||
cancelNode.setAuditTime(new Date());
|
||||
approvalMapper.updateById(cancelNode);
|
||||
}
|
||||
|
||||
log.info("成员申请被拒绝: memberId={}, nodeCode={}, 拒绝人={}", memberId, nodeCode, currentUserId);
|
||||
} else {
|
||||
log.info("成员申请节点审批通过: memberId={}, nodeCode={}, 审批人={}", memberId, nodeCode, currentUserId);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
+16
-17
@@ -16,6 +16,7 @@ import org.dromara.stgl.domain.vo.StglMemberVo;
|
||||
import org.dromara.stgl.domain.StglMember;
|
||||
import org.dromara.stgl.mapper.StglMemberMapper;
|
||||
import org.dromara.stgl.service.IStglMemberService;
|
||||
import org.dromara.stgl.service.IStglMemberApprovalService;
|
||||
import org.dromara.system.service.ISysUserService;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import org.dromara.common.satoken.utils.LoginHelper;
|
||||
@@ -44,6 +45,7 @@ public class StglMemberServiceImpl implements IStglMemberService {
|
||||
|
||||
private final StglMemberMapper baseMapper;
|
||||
private final ISysUserService userService;
|
||||
private final IStglMemberApprovalService memberApprovalService;
|
||||
|
||||
/**
|
||||
* 查询社团成员
|
||||
@@ -382,27 +384,24 @@ public class StglMemberServiceImpl implements IStglMemberService {
|
||||
throw new RuntimeException("成员记录不存在");
|
||||
}
|
||||
|
||||
if (member.getStatus() != 0) {
|
||||
if (member.getStatus() != 0 && member.getStatus() != 1) {
|
||||
throw new RuntimeException("该申请已审核过,无法重复审核");
|
||||
}
|
||||
|
||||
// 更新成员状态和角色
|
||||
StglMember updateMember = new StglMember();
|
||||
updateMember.setId(id);
|
||||
if (approved) {
|
||||
updateMember.setStatus(2); // 已加入状态(stgl_member_status字典)
|
||||
// 根据成员类型设置不同的角色
|
||||
if (member.getMemberType() == 1) {
|
||||
updateMember.setRoleType("普通成员"); // 社团成员
|
||||
} else if (member.getMemberType() == 2) {
|
||||
updateMember.setRoleType("队员"); // 志愿者队伍成员
|
||||
}
|
||||
} else {
|
||||
updateMember.setStatus(3); // 已驳回状态(stgl_member_status字典)
|
||||
updateMember.setRoleType(null); // 拒绝时清空角色
|
||||
}
|
||||
Long currentUserId = LoginHelper.getUserId();
|
||||
java.util.Set<String> roleKeys = LoginHelper.getLoginUser().getRolePermission();
|
||||
|
||||
return baseMapper.updateById(updateMember) > 0;
|
||||
if (roleKeys.contains("stgl_instructor")) {
|
||||
if (member.getStatus() == 0) {
|
||||
throw new RuntimeException("该申请需等待社长先审批,请稍后再试");
|
||||
}
|
||||
return memberApprovalService.advisorApprove(id, approved, null);
|
||||
} else {
|
||||
if (member.getStatus() == 1) {
|
||||
throw new RuntimeException("社长已审批通过,等待指导老师审批中");
|
||||
}
|
||||
return memberApprovalService.presidentApprove(id, approved, null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
<result property="advisorEmail" column="advisor_email"/>
|
||||
<result property="vicePresidentsJson" column="vice_presidents_json"/>
|
||||
<result property="advisorsJson" column="advisors_json"/>
|
||||
<result property="affiliation" column="affiliation"/>
|
||||
<result property="memberLimit" column="member_limit"/>
|
||||
<result property="departments" column="departments"/>
|
||||
<result property="recruitCycle" column="recruit_cycle"/>
|
||||
@@ -56,6 +57,7 @@
|
||||
c.vice_presidents_json,
|
||||
c.advisor_name, c.advisor_dept, c.advisor_phone, c.advisor_email,
|
||||
c.advisors_json,
|
||||
c.affiliation,
|
||||
c.member_limit, c.departments, c.recruit_cycle, c.recruit_time, 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, c.tenant_id, c.deleted,
|
||||
COALESCE(m.member_count, 0) as memberCount
|
||||
@@ -76,6 +78,7 @@
|
||||
c.president_dept, c.president_major, c.president_class, c.president_phone,
|
||||
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.affiliation,
|
||||
c.member_limit, c.departments, c.recruit_cycle, c.recruit_time,
|
||||
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,
|
||||
@@ -98,6 +101,7 @@
|
||||
c.president_dept, c.president_major, c.president_class, c.president_phone,
|
||||
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.affiliation,
|
||||
c.member_limit, c.departments, c.recruit_cycle, c.recruit_time,
|
||||
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,
|
||||
@@ -129,6 +133,7 @@
|
||||
c.vice_presidents_json,
|
||||
c.advisor_name, c.advisor_dept, c.advisor_phone, c.advisor_email,
|
||||
c.advisors_json,
|
||||
c.affiliation,
|
||||
c.member_limit, c.departments, c.recruit_cycle, c.recruit_time,
|
||||
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,
|
||||
|
||||
@@ -445,4 +445,132 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
ORDER BY a.start_time DESC, a.create_time DESC
|
||||
</select>
|
||||
|
||||
<!-- 查询指定班级中当前正在参加活动的学生列表(个人报名 + 团队报名) -->
|
||||
<select id="selectCurrentActiveStudents" parameterType="java.util.Map" resultType="java.util.Map">
|
||||
SELECT
|
||||
s.id,
|
||||
s.student_no AS studentNo,
|
||||
s.name AS studentName,
|
||||
s.gender,
|
||||
s.phone,
|
||||
c.class_name AS className,
|
||||
a.id AS activityId,
|
||||
a.activity_name AS activityName,
|
||||
a.start_time AS startTime,
|
||||
a.end_time AS endTime,
|
||||
a.location,
|
||||
CASE
|
||||
WHEN a.related_type = 1 THEN club.club_name
|
||||
WHEN a.related_type = 2 THEN vt.team_name
|
||||
ELSE NULL
|
||||
END AS organizationName
|
||||
FROM xg_student_msg s
|
||||
LEFT JOIN xg_class_msg c ON s.class_id = c.id AND c.tenant_id = '000000'
|
||||
INNER JOIN (
|
||||
SELECT ar.user_id, ar.activity_id FROM stgl_activity_registration ar
|
||||
INNER JOIN stgl_activity a ON ar.activity_id = a.id AND a.deleted = 0 AND a.tenant_id = '000000'
|
||||
WHERE ar.deleted = 0 AND ar.approval_status = 1 AND ar.tenant_id = '000000'
|
||||
AND NOW() BETWEEN a.start_time AND a.end_time
|
||||
|
||||
UNION
|
||||
|
||||
SELECT
|
||||
CAST(JSON_UNQUOTE(JSON_EXTRACT(ati.team_members, CONCAT('$[', numbers.n, '].userId'))) AS UNSIGNED) AS user_id,
|
||||
ati.activity_id
|
||||
FROM stgl_activity_team_info ati
|
||||
CROSS JOIN (
|
||||
SELECT 0 AS n UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION
|
||||
SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9 UNION
|
||||
SELECT 10 UNION SELECT 11 UNION SELECT 12 UNION SELECT 13 UNION SELECT 14 UNION
|
||||
SELECT 15 UNION SELECT 16 UNION SELECT 17 UNION SELECT 18 UNION SELECT 19
|
||||
) numbers
|
||||
INNER JOIN stgl_activity a ON ati.activity_id = a.id AND a.deleted = 0 AND a.tenant_id = '000000'
|
||||
WHERE ati.deleted = 0 AND ati.tenant_id = '000000'
|
||||
AND NOW() BETWEEN a.start_time AND a.end_time
|
||||
AND JSON_UNQUOTE(JSON_EXTRACT(ati.team_members, CONCAT('$[', numbers.n, '].userId'))) IS NOT NULL
|
||||
AND JSON_UNQUOTE(JSON_EXTRACT(ati.team_members, CONCAT('$[', numbers.n, '].userId'))) != ''
|
||||
AND EXISTS (
|
||||
SELECT 1 FROM stgl_activity_registration team_ar
|
||||
WHERE team_ar.team_id = ati.id
|
||||
AND team_ar.deleted = 0
|
||||
AND team_ar.approval_status = 1
|
||||
AND team_ar.registration_type = 2
|
||||
AND team_ar.tenant_id = '000000'
|
||||
)
|
||||
) active ON s.id = active.user_id
|
||||
INNER JOIN stgl_activity a ON active.activity_id = a.id AND a.deleted = 0 AND a.tenant_id = '000000'
|
||||
LEFT JOIN stgl_club club ON a.related_type = 1 AND a.related_club_id = club.id AND club.deleted = 0 AND club.tenant_id = '000000'
|
||||
LEFT JOIN stgl_volunteer_team vt ON a.related_type = 2 AND a.related_club_id = vt.id AND vt.deleted = 0 AND vt.tenant_id = '000000'
|
||||
<where>
|
||||
s.deleted = 0
|
||||
AND s.student_status = '1'
|
||||
AND s.tenant_id = '000000'
|
||||
<if test="classId != null and classId != ''">
|
||||
AND s.class_id = #{classId}
|
||||
</if>
|
||||
<if test="studentName != null and studentName != ''">
|
||||
AND s.name LIKE CONCAT('%', #{studentName}, '%')
|
||||
</if>
|
||||
<if test="studentNo != null and studentNo != ''">
|
||||
AND s.student_no LIKE CONCAT('%', #{studentNo}, '%')
|
||||
</if>
|
||||
</where>
|
||||
ORDER BY a.start_time, s.student_no
|
||||
<if test="offset != null and pageSize != null">
|
||||
LIMIT #{pageSize} OFFSET #{offset}
|
||||
</if>
|
||||
</select>
|
||||
|
||||
<!-- 查询指定班级中当前正在参加活动的学生总数 -->
|
||||
<select id="selectCurrentActiveStudentsCount" parameterType="java.util.Map" resultType="java.lang.Integer">
|
||||
SELECT COUNT(*)
|
||||
FROM xg_student_msg s
|
||||
INNER JOIN (
|
||||
SELECT ar.user_id, ar.activity_id FROM stgl_activity_registration ar
|
||||
INNER JOIN stgl_activity a ON ar.activity_id = a.id AND a.deleted = 0 AND a.tenant_id = '000000'
|
||||
WHERE ar.deleted = 0 AND ar.approval_status = 1 AND ar.tenant_id = '000000'
|
||||
AND NOW() BETWEEN a.start_time AND a.end_time
|
||||
|
||||
UNION
|
||||
|
||||
SELECT
|
||||
CAST(JSON_UNQUOTE(JSON_EXTRACT(ati.team_members, CONCAT('$[', numbers.n, '].userId'))) AS UNSIGNED) AS user_id,
|
||||
ati.activity_id
|
||||
FROM stgl_activity_team_info ati
|
||||
CROSS JOIN (
|
||||
SELECT 0 AS n UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION
|
||||
SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9 UNION
|
||||
SELECT 10 UNION SELECT 11 UNION SELECT 12 UNION SELECT 13 UNION SELECT 14 UNION
|
||||
SELECT 15 UNION SELECT 16 UNION SELECT 17 UNION SELECT 18 UNION SELECT 19
|
||||
) numbers
|
||||
INNER JOIN stgl_activity a ON ati.activity_id = a.id AND a.deleted = 0 AND a.tenant_id = '000000'
|
||||
WHERE ati.deleted = 0 AND ati.tenant_id = '000000'
|
||||
AND NOW() BETWEEN a.start_time AND a.end_time
|
||||
AND JSON_UNQUOTE(JSON_EXTRACT(ati.team_members, CONCAT('$[', numbers.n, '].userId'))) IS NOT NULL
|
||||
AND JSON_UNQUOTE(JSON_EXTRACT(ati.team_members, CONCAT('$[', numbers.n, '].userId'))) != ''
|
||||
AND EXISTS (
|
||||
SELECT 1 FROM stgl_activity_registration team_ar
|
||||
WHERE team_ar.team_id = ati.id
|
||||
AND team_ar.deleted = 0
|
||||
AND team_ar.approval_status = 1
|
||||
AND team_ar.registration_type = 2
|
||||
AND team_ar.tenant_id = '000000'
|
||||
)
|
||||
) active ON s.id = active.user_id
|
||||
<where>
|
||||
s.deleted = 0
|
||||
AND s.student_status = '1'
|
||||
AND s.tenant_id = '000000'
|
||||
<if test="classId != null and classId != ''">
|
||||
AND s.class_id = #{classId}
|
||||
</if>
|
||||
<if test="studentName != null and studentName != ''">
|
||||
AND s.name LIKE CONCAT('%', #{studentName}, '%')
|
||||
</if>
|
||||
<if test="studentNo != null and studentNo != ''">
|
||||
AND s.student_no LIKE CONCAT('%', #{studentNo}, '%')
|
||||
</if>
|
||||
</where>
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
@@ -461,6 +461,11 @@ export interface StglActivityForm {
|
||||
* 备注
|
||||
*/
|
||||
remark?: string;
|
||||
|
||||
/**
|
||||
* 邀请成员ID列表(前端专用,提交时传给后端)
|
||||
*/
|
||||
invitedMemberIds?: (string | number)[];
|
||||
}
|
||||
|
||||
export interface StglActivityQuery extends PageQuery {
|
||||
|
||||
@@ -79,6 +79,21 @@ export interface StglClubVO {
|
||||
*/
|
||||
presidentPhone: string;
|
||||
|
||||
/**
|
||||
* 社长用户ID(关联学生用户)
|
||||
*/
|
||||
presidentUserId?: string | number;
|
||||
|
||||
/**
|
||||
* 物资保管员用户ID
|
||||
*/
|
||||
materialKeeperUserId?: string | number;
|
||||
|
||||
/**
|
||||
* 物资保管员姓名
|
||||
*/
|
||||
materialKeeperName?: string;
|
||||
|
||||
/**
|
||||
* 副社长列表
|
||||
*/
|
||||
@@ -148,10 +163,7 @@ export interface StglClubVO {
|
||||
* 指导老师列表JSON格式存储
|
||||
*/
|
||||
advisorsJson?: string;
|
||||
|
||||
/**
|
||||
* 成员规模上限
|
||||
*/
|
||||
affiliation?: string;
|
||||
memberLimit: number;
|
||||
|
||||
/**
|
||||
@@ -286,6 +298,36 @@ export interface StglClubForm {
|
||||
*/
|
||||
presidentPhone?: string;
|
||||
|
||||
/**
|
||||
* 社长用户ID(关联学生用户)
|
||||
*/
|
||||
presidentUserId?: string | number;
|
||||
|
||||
/**
|
||||
* 物资保管员用户ID
|
||||
*/
|
||||
materialKeeperUserId?: string | number;
|
||||
|
||||
/**
|
||||
* 物资保管员姓名
|
||||
*/
|
||||
materialKeeperName?: string;
|
||||
|
||||
/**
|
||||
* 物资保管员学生对象(前端选择器用)
|
||||
*/
|
||||
materialKeeperStudent?: StudentVO | null;
|
||||
|
||||
/**
|
||||
* 物资保管员学号(前端显示用)
|
||||
*/
|
||||
materialKeeperStudentNo?: string;
|
||||
|
||||
/**
|
||||
* 物资保管员联系方式(前端显示用)
|
||||
*/
|
||||
materialKeeperPhone?: string;
|
||||
|
||||
/**
|
||||
* 副社长列表
|
||||
*/
|
||||
@@ -350,6 +392,7 @@ export interface StglClubForm {
|
||||
* 成员规模上限
|
||||
*/
|
||||
memberLimit?: number;
|
||||
affiliation?: string;
|
||||
|
||||
/**
|
||||
* 组织架构
|
||||
|
||||
@@ -98,9 +98,41 @@ export const approveMember = (id: string | number, approved: boolean) => {
|
||||
return request({
|
||||
url: `/stgl/member/approve/${id}`,
|
||||
method: 'put',
|
||||
params: { approved },
|
||||
headers: {
|
||||
isEncrypt: 'true'
|
||||
}
|
||||
params: { approved }
|
||||
});
|
||||
};
|
||||
|
||||
export const presidentApprove = (memberId: number, approved: boolean, opinion?: string) => {
|
||||
return request({
|
||||
url: '/stgl/member/presidentApprove',
|
||||
method: 'put',
|
||||
data: { memberId, approved, opinion },
|
||||
headers: { isEncrypt: 'true' }
|
||||
});
|
||||
};
|
||||
|
||||
export const advisorApprove = (memberId: number, approved: boolean, opinion?: string) => {
|
||||
return request({
|
||||
url: '/stgl/member/advisorApprove',
|
||||
method: 'put',
|
||||
data: { memberId, approved, opinion },
|
||||
headers: { isEncrypt: 'true' }
|
||||
});
|
||||
};
|
||||
|
||||
export const getApprovalFlow = (memberId: number) => {
|
||||
return request({
|
||||
url: `/stgl/member/approvalFlow/${memberId}`,
|
||||
method: 'get',
|
||||
headers: { isEncrypt: 'true' }
|
||||
});
|
||||
};
|
||||
|
||||
export const getPendingApprovals = (clubId?: number) => {
|
||||
return request({
|
||||
url: '/stgl/member/pendingApprovals',
|
||||
method: 'get',
|
||||
params: clubId ? { clubId } : {},
|
||||
headers: { isEncrypt: 'true' }
|
||||
});
|
||||
};
|
||||
@@ -62,3 +62,11 @@ export function exportStglClass(query: StglClassQuery) {
|
||||
params: query
|
||||
});
|
||||
}
|
||||
|
||||
export function getCurrentActivities(query: StglClassQuery) {
|
||||
return request({
|
||||
url: '/stgl/stclass/current-activities',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
}
|
||||
@@ -290,28 +290,24 @@ export interface ActivityDetailVO {
|
||||
* 班级详情对象
|
||||
*/
|
||||
export interface StglClassDetailVO {
|
||||
/**
|
||||
* 班级ID
|
||||
*/
|
||||
id: string | number;
|
||||
|
||||
/**
|
||||
* 班级名称
|
||||
*/
|
||||
className: string;
|
||||
|
||||
/**
|
||||
* 学院名称
|
||||
*/
|
||||
collegeName: string;
|
||||
|
||||
/**
|
||||
* 专业名称
|
||||
*/
|
||||
majorName: string;
|
||||
|
||||
/**
|
||||
* 年级
|
||||
*/
|
||||
grade: string;
|
||||
}
|
||||
|
||||
export interface CurrentActivityVO {
|
||||
id: string | number;
|
||||
studentNo: string;
|
||||
studentName: string;
|
||||
gender: string;
|
||||
phone: string;
|
||||
className: string;
|
||||
activityId: string | number;
|
||||
activityName: string;
|
||||
startTime: string;
|
||||
endTime: string;
|
||||
location: string;
|
||||
organizationName: string;
|
||||
}
|
||||
@@ -118,6 +118,7 @@ watch(
|
||||
(newValue) => {
|
||||
if (updateLock.value) return;
|
||||
updateLock.value = true;
|
||||
emit('update:modelValue', newValue);
|
||||
emit('change', newValue);
|
||||
nextTick(() => {
|
||||
updateLock.value = false;
|
||||
|
||||
@@ -610,6 +610,34 @@
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-divider content-position="left">邀请成员</el-divider>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="24">
|
||||
<el-form-item label="邀请成员">
|
||||
<div style="width: 100%">
|
||||
<div style="margin-bottom: 10px; display: flex; align-items: center; gap: 12px;">
|
||||
<el-checkbox v-model="inviteCheckAll" :indeterminate="inviteIndeterminate" @change="handleInviteCheckAll">全选</el-checkbox>
|
||||
<el-input v-model="inviteSearchText" placeholder="搜索姓名/学号" clearable style="width: 200px" size="small" />
|
||||
<span style="color: #909399; font-size: 12px;">已选 {{ form.invitedMemberIds?.length || 0 }} / {{ inviteMemberList.length }} 人</span>
|
||||
<span v-if="!form.relatedClubId" style="color: #E6A23C; font-size: 12px;">请先选择关联社团</span>
|
||||
</div>
|
||||
<el-checkbox-group v-model="form.invitedMemberIds" @change="handleInviteChange">
|
||||
<div style="max-height: 200px; overflow-y: auto; border: 1px solid #dcdfe6; border-radius: 4px; padding: 8px;">
|
||||
<div v-if="inviteMemberList.length === 0" style="color: #909399; text-align: center; padding: 20px;">暂无可邀请的成员</div>
|
||||
<el-checkbox
|
||||
v-for="member in filteredInviteMembers"
|
||||
:key="member.userId"
|
||||
:label="member.userId"
|
||||
style="width: 200px; margin: 4px 0;"
|
||||
>
|
||||
{{ member.studentName }}({{ member.studentNo }})
|
||||
</el-checkbox>
|
||||
</div>
|
||||
</el-checkbox-group>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</el-form>
|
||||
@@ -690,7 +718,7 @@
|
||||
</template>
|
||||
|
||||
<script setup name="StglActivity" lang="ts">
|
||||
import { nextTick, watch } from 'vue';
|
||||
import { nextTick, watch, computed } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import { ElNotification } from 'element-plus';
|
||||
import {
|
||||
@@ -719,6 +747,7 @@ import { AdvisorVO } from '@/components/AdvisorManager/index.vue';
|
||||
import { useUserStore } from '@/store/modules/user';
|
||||
import { checkClubActivityPermission, type ClubActivityPermissionResult } from '@/api/stgl/clubPermission';
|
||||
import { getPendingActivityAudits } from '@/api/stgl/activity/audit';
|
||||
import { listStglMember } from '@/api/stgl/member';
|
||||
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
const route = useRoute();
|
||||
@@ -741,6 +770,37 @@ const loading = ref(true);
|
||||
const showSearch = ref(true);
|
||||
const ids = ref<Array<string | number>>([]);
|
||||
const single = ref(true);
|
||||
|
||||
// 邀请成员相关
|
||||
interface InviteMember { userId: string; studentName: string; studentNo: string; }
|
||||
const inviteMemberList = ref<InviteMember[]>([]);
|
||||
const inviteCheckAll = ref(false);
|
||||
const inviteIndeterminate = ref(false);
|
||||
const inviteSearchText = ref('');
|
||||
const filteredInviteMembers = computed(() => {
|
||||
if (!inviteSearchText.value) return inviteMemberList.value;
|
||||
const kw = inviteSearchText.value.toLowerCase();
|
||||
return inviteMemberList.value.filter(m => m.studentName.toLowerCase().includes(kw) || m.studentNo.includes(kw));
|
||||
});
|
||||
const handleInviteCheckAll = (val: boolean) => {
|
||||
form.value.invitedMemberIds = val ? filteredInviteMembers.value.map(m => m.userId) : [];
|
||||
inviteIndeterminate.value = false;
|
||||
};
|
||||
const handleInviteChange = (val: (string | number)[]) => {
|
||||
const total = filteredInviteMembers.value.length;
|
||||
inviteCheckAll.value = val.length === total;
|
||||
inviteIndeterminate.value = val.length > 0 && val.length < total;
|
||||
};
|
||||
const loadInviteMembers = async (clubId: string | number) => {
|
||||
try {
|
||||
const res = await listStglMember({ memberType: 1, relationId: clubId, status: '2', pageNum: 1, pageSize: 9999 } as any);
|
||||
const rows = (res as any)?.rows || [];
|
||||
inviteMemberList.value = rows.map((m: any) => ({ userId: String(m.userId), studentName: m.studentName, studentNo: m.studentNo }));
|
||||
} catch (e) {
|
||||
console.error('加载社团成员失败:', e);
|
||||
inviteMemberList.value = [];
|
||||
}
|
||||
};
|
||||
const multiple = ref(true);
|
||||
const total = ref(0);
|
||||
const activeTab = ref('basic');
|
||||
@@ -836,7 +896,8 @@ const initFormData: StglActivityForm = {
|
||||
summaryAttachments: undefined,
|
||||
achievementsAttachments: undefined,
|
||||
importSource: undefined,
|
||||
remark: undefined
|
||||
remark: undefined,
|
||||
invitedMemberIds: []
|
||||
};
|
||||
|
||||
const data = reactive<PageData<StglActivityForm, StglActivityQuery & { relatedClub?: ClubOption | null; relatedDept?: DeptOption | null }>>({
|
||||
@@ -2113,6 +2174,20 @@ watch(
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
watch(
|
||||
() => form.value.relatedClub,
|
||||
(newClub) => {
|
||||
if (newClub?.relatedClubId && newClub.relatedType === 1) {
|
||||
loadInviteMembers(newClub.relatedClubId);
|
||||
} else {
|
||||
inviteMemberList.value = [];
|
||||
form.value.invitedMemberIds = [];
|
||||
inviteCheckAll.value = false;
|
||||
}
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@@ -59,6 +59,14 @@
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="审批阶段" align="center" width="120">
|
||||
<template #default="scope">
|
||||
<el-tag v-if="scope.row.status === 0" type="warning">等待社长审批</el-tag>
|
||||
<el-tag v-else-if="scope.row.status === 1" type="info">等待指导老师审批</el-tag>
|
||||
<el-tag v-else-if="scope.row.status === 3" type="danger">已驳回</el-tag>
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="学号" align="center" prop="studentNo" width="120" />
|
||||
<el-table-column label="姓名" align="center" prop="studentName" width="80" />
|
||||
<el-table-column label="性别" align="center" prop="gender" width="60">
|
||||
@@ -105,6 +113,7 @@
|
||||
style="font-size: 16px; margin-right: 8px"
|
||||
@click="handleApprove(scope.row)"
|
||||
v-hasPermi="['stgl:clubmember:approve']"
|
||||
v-if="scope.row.status === '0' || scope.row.status === '1'"
|
||||
></el-button>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
@@ -188,6 +197,7 @@ import { Check, Close } from '@element-plus/icons-vue';
|
||||
import { listStglMember, getStglMember, approveMember } from '@/api/stgl/member';
|
||||
import { StglMemberVO, StglMemberQuery } from '@/api/stgl/member/types';
|
||||
import { parseTime } from '@/utils/ruoyi';
|
||||
import { useUserStore } from '@/store/modules/user';
|
||||
|
||||
interface Props {
|
||||
clubId: string | number;
|
||||
@@ -277,12 +287,16 @@ const getList = async () => {
|
||||
|
||||
console.log('PendingMembersTab: getList 开始查询,参数:', queryData);
|
||||
|
||||
// 待审核页面:查询待审核(status=0)和已驳回(status=3)的成员
|
||||
// 字典配置:0-待审核,1-审核中,2-已加入,3-已驳回,4-已退出
|
||||
// 根据角色动态设置查询状态
|
||||
// 社长:看到 status=0(待审核)+ status=3(已驳回)
|
||||
// 指导老师:看到 status=1(审核中,社长已通过)+ status=3(已驳回)
|
||||
const userRoles = useUserStore().roles;
|
||||
const isInstructor = userRoles.includes('stgl_instructor');
|
||||
|
||||
if (!queryData.status) {
|
||||
// 使用params传递多状态查询
|
||||
queryData.params = { ...queryData.params, statusList: ['0', '3'] } as any;
|
||||
delete queryData.status; // 删除单个status字段
|
||||
const statusList = isInstructor ? ['1', '3'] : ['0', '3'];
|
||||
queryData.params = { ...queryData.params, statusList } as any;
|
||||
delete queryData.status;
|
||||
}
|
||||
|
||||
// 确保phone参数正确传递到params中
|
||||
@@ -439,7 +453,8 @@ const submitApproval = () => {
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('审核失败:', error);
|
||||
proxy.$modal.msgError('审核失败,请检查数据完整性');
|
||||
const msg = error?.msg || error?.message || '审核失败';
|
||||
proxy.$modal.msgError(msg);
|
||||
});
|
||||
} else {
|
||||
// 批量审核 - 再次验证数据完整性
|
||||
@@ -461,7 +476,8 @@ const submitApproval = () => {
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('批量审核失败:', error);
|
||||
proxy.$modal.msgError('批量审核失败,请检查数据完整性');
|
||||
const msg = error?.msg || error?.message || '批量审核失败';
|
||||
proxy.$modal.msgError(msg);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -241,6 +241,13 @@
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="归属" prop="affiliation">
|
||||
<el-select v-model="form.affiliation" placeholder="请选择归属" style="width: 100%" :disabled="!hasInstructorPermission">
|
||||
<el-option v-for="item in affiliationOptions" :key="item.deptId" :label="item.deptName" :value="item.deptId" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
@@ -333,6 +340,17 @@
|
||||
</el-row>
|
||||
</div>
|
||||
|
||||
<!-- 副社长信息 -->
|
||||
<div class="personnel-section">
|
||||
<h4 class="section-title">副社长信息</h4>
|
||||
<VicePresidentManager
|
||||
v-model="form.vicePresidents"
|
||||
title=""
|
||||
add-button-text="添加副社长"
|
||||
empty-tip="暂未添加副社长"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 指导老师信息 -->
|
||||
<div class="personnel-section">
|
||||
<h4 class="section-title">指导老师信息</h4>
|
||||
@@ -343,6 +361,33 @@
|
||||
:disabled="!hasInstructorPermission"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 物资保管员 -->
|
||||
<div class="personnel-section">
|
||||
<h4 class="section-title">物资保管员</h4>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="选择学生" prop="materialKeeperStudent">
|
||||
<StudentSelector
|
||||
v-model="form.materialKeeperStudent"
|
||||
placeholder="请选择物资保管员"
|
||||
:showStudentNo="false"
|
||||
@change="handleMaterialKeeperChange"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="学号">
|
||||
<el-input v-model="form.materialKeeperStudentNo" placeholder="学号" readonly />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="联系方式">
|
||||
<el-input v-model="form.materialKeeperPhone" placeholder="联系方式" readonly />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="组织架构" name="organization">
|
||||
@@ -422,6 +467,7 @@
|
||||
<el-descriptions-item label="社团类型">
|
||||
<dict-tag :options="stgl_club_type" :value="viewForm.clubType" />
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="归属">{{ affiliationOptions.find(o => o.deptId === String(viewForm.affiliation))?.deptName || '暂未设置' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="成立日期">{{ viewForm.establishDate || '暂无' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="成员规模上限">{{ viewForm.memberLimit || '不限' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="邮箱">{{ viewForm.email || '暂无' }}</el-descriptions-item>
|
||||
@@ -468,6 +514,19 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 物资保管员信息 -->
|
||||
<div class="position-group">
|
||||
<div class="position-title">物资保管员:</div>
|
||||
<div class="position-content">
|
||||
<template v-if="viewForm.materialKeeperName">
|
||||
<span class="info-item"><strong>姓名:</strong>{{ viewForm.materialKeeperName }}</span>
|
||||
</template>
|
||||
<template v-else>
|
||||
<span class="no-data">暂无物资保管员信息</span>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 指导老师信息 -->
|
||||
<div class="position-group">
|
||||
<div class="position-title">指导老师:</div>
|
||||
@@ -682,11 +741,14 @@ import {
|
||||
import { StglClubVO, StglClubQuery, StglClubForm } from '@/api/stgl/club/types';
|
||||
import StudentSelector from '@/components/StudentSelector/index.vue';
|
||||
import AdvisorManager from '@/components/AdvisorManager/index.vue';
|
||||
import VicePresidentManager from '@/components/VicePresidentManager/index.vue';
|
||||
import MemberManagementDialog from './components/MemberManagementDialog.vue';
|
||||
import AdvisorHistoryDialog from '@/components/AdvisorHistoryDialog/index.vue';
|
||||
import { StudentVO } from '@/api/xg/student/types';
|
||||
import { AdvisorVO } from '@/components/AdvisorManager/index.vue';
|
||||
import { VicePresidentVO } from '@/components/VicePresidentManager/index.vue';
|
||||
import { Document } from '@element-plus/icons-vue';
|
||||
import { listCollege } from '@/api/xg/xsjcxx/collegemsg';
|
||||
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
const router = useRouter();
|
||||
@@ -699,6 +761,22 @@ const hasInstructorPermission = computed(() => {
|
||||
return proxy?.$auth.hasRoleOr(['stgl_instructor', 'stgl_stgl_admin', 'superadmin']);
|
||||
});
|
||||
|
||||
// 归属选项:二级学院 + 学生处
|
||||
interface DeptOption { deptId: string; deptName: string; }
|
||||
const affiliationOptions = ref<DeptOption[]>([]);
|
||||
const loadAffiliationOptions = async () => {
|
||||
try {
|
||||
const res = await listCollege();
|
||||
const colleges = (res as any)?.rows || (res as any)?.data || [];
|
||||
const options: DeptOption[] = colleges.map((c: any) => ({ deptId: String(c.deptId), deptName: c.deptName }));
|
||||
options.push({ deptId: '202512231307170009', deptName: '学生处' });
|
||||
affiliationOptions.value = options;
|
||||
} catch (e) {
|
||||
console.error('加载归属选项失败:', e);
|
||||
}
|
||||
};
|
||||
loadAffiliationOptions();
|
||||
|
||||
// 社长权限控制 - 检查用户是否为各社团的社长(无论是否为创建者)
|
||||
// 用于控制转让按钮显示和社长相关操作权限
|
||||
const presidentRoles = ref<Record<string | number, boolean>>({});
|
||||
@@ -811,6 +889,12 @@ const viewForm = reactive<StglClubForm>({
|
||||
presidentMajor: undefined,
|
||||
presidentClass: undefined,
|
||||
presidentPhone: undefined,
|
||||
presidentUserId: undefined,
|
||||
materialKeeperUserId: undefined,
|
||||
materialKeeperName: undefined,
|
||||
materialKeeperStudent: null,
|
||||
materialKeeperStudentNo: undefined,
|
||||
materialKeeperPhone: undefined,
|
||||
vicePresidentName: undefined,
|
||||
vicePresidentStudentNo: undefined,
|
||||
advisorName: undefined,
|
||||
@@ -843,11 +927,19 @@ const initFormData: StglClubForm = {
|
||||
presidentMajor: undefined,
|
||||
presidentClass: undefined,
|
||||
presidentPhone: undefined,
|
||||
presidentUserId: undefined,
|
||||
materialKeeperUserId: undefined,
|
||||
materialKeeperName: undefined,
|
||||
materialKeeperStudent: null,
|
||||
materialKeeperStudentNo: undefined,
|
||||
materialKeeperPhone: undefined,
|
||||
|
||||
vicePresidents: [],
|
||||
advisors: [],
|
||||
advisorName: undefined,
|
||||
advisorDept: undefined,
|
||||
memberLimit: undefined,
|
||||
affiliation: undefined,
|
||||
departments: undefined,
|
||||
recruitCycle: undefined,
|
||||
recruitTime: undefined,
|
||||
@@ -983,6 +1075,9 @@ const handleUpdate = async (row?: StglClubVO) => {
|
||||
if (res.data.sighOpen !== undefined && res.data.sighOpen !== null) {
|
||||
(res.data as any).sighOpen = String(res.data.sighOpen);
|
||||
}
|
||||
if (res.data.affiliation !== undefined && res.data.affiliation !== null) {
|
||||
(res.data as any).affiliation = String(res.data.affiliation);
|
||||
}
|
||||
|
||||
// 处理社长信息:将简单字段转换为复杂对象结构
|
||||
if (res.data.presidentName) {
|
||||
@@ -998,7 +1093,54 @@ const handleUpdate = async (row?: StglClubVO) => {
|
||||
(res.data as any).presidentStudent = null;
|
||||
}
|
||||
|
||||
// 处理物资保管员信息:如果有userId则构建选择器对象
|
||||
if (res.data.materialKeeperUserId) {
|
||||
(res.data as any).materialKeeperStudent = {
|
||||
name: res.data.materialKeeperName,
|
||||
phone: ''
|
||||
};
|
||||
} else {
|
||||
(res.data as any).materialKeeperStudent = null;
|
||||
}
|
||||
|
||||
// 处理副社长信息:优先从JSON字段读取,如果没有则使用单一字段
|
||||
let vicePresidentsData: VicePresidentVO[] = [];
|
||||
if (res.data.vicePresidentsJson) {
|
||||
try {
|
||||
const parsed = JSON.parse(res.data.vicePresidentsJson);
|
||||
vicePresidentsData = parsed.map((vp: any) => ({
|
||||
student: {
|
||||
id: vp.studentId || vp.studentNo || '',
|
||||
name: vp.name || '',
|
||||
studentNo: vp.studentNo || '',
|
||||
phone: vp.phone || '',
|
||||
collegeName: vp.dept || '',
|
||||
majorName: vp.major || '',
|
||||
className: vp.class || ''
|
||||
} as StudentVO,
|
||||
name: vp.name || '',
|
||||
dept: vp.dept || '',
|
||||
major: vp.major || '',
|
||||
class: vp.class || '',
|
||||
phone: vp.phone || ''
|
||||
}));
|
||||
} catch (error) {
|
||||
console.warn('解析副社长JSON数据失败:', error);
|
||||
vicePresidentsData = [];
|
||||
}
|
||||
} else if (res.data.vicePresidentName) {
|
||||
vicePresidentsData = [{
|
||||
student: {
|
||||
name: res.data.vicePresidentName,
|
||||
studentNo: res.data.vicePresidentStudentNo || ''
|
||||
} as StudentVO,
|
||||
name: res.data.vicePresidentName,
|
||||
dept: res.data.vicePresidentDept || '',
|
||||
major: res.data.vicePresidentMajor || '',
|
||||
class: res.data.vicePresidentClass || '',
|
||||
phone: res.data.vicePresidentPhone || ''
|
||||
}];
|
||||
}
|
||||
|
||||
// 处理指导老师信息:优先从JSON字段读取,如果没有则使用单一字段
|
||||
let advisorsData = [];
|
||||
@@ -1036,6 +1178,7 @@ const handleUpdate = async (row?: StglClubVO) => {
|
||||
// 然后单独设置指导老师数据,避免触发 watch 时的冲突
|
||||
nextTick(() => {
|
||||
form.value.advisors = advisorsData;
|
||||
form.value.vicePresidents = vicePresidentsData;
|
||||
});
|
||||
|
||||
dialog.visible = true;
|
||||
@@ -1071,6 +1214,26 @@ const submitForm = () => {
|
||||
// 🔥 新增:同时保存JSON字段和兼容性字段
|
||||
submitData.advisorsJson = JSON.stringify(submitData.advisors || []);
|
||||
|
||||
// 处理副社长数据:序列化为JSON
|
||||
if (submitData.vicePresidents && Array.isArray(submitData.vicePresidents)) {
|
||||
submitData.vicePresidentsJson = JSON.stringify(submitData.vicePresidents);
|
||||
// 兼容旧字段:取第一个副社长
|
||||
if (submitData.vicePresidents.length > 0) {
|
||||
const firstVp = submitData.vicePresidents[0];
|
||||
submitData.vicePresidentName = firstVp.name;
|
||||
submitData.vicePresidentStudentNo = firstVp.student?.studentNo || '';
|
||||
} else {
|
||||
submitData.vicePresidentName = '';
|
||||
submitData.vicePresidentStudentNo = '';
|
||||
}
|
||||
}
|
||||
|
||||
// 清理前端专用字段,不发送到后端
|
||||
delete submitData.materialKeeperStudent;
|
||||
delete submitData.materialKeeperStudentNo;
|
||||
delete submitData.materialKeeperPhone;
|
||||
delete submitData.presidentStudent;
|
||||
|
||||
if (form.value.id) {
|
||||
await updateStglClub(submitData).finally(() => (buttonLoading.value = false));
|
||||
} else {
|
||||
@@ -1255,6 +1418,7 @@ const handlePresidentChange = (student: StudentVO | null) => {
|
||||
form.value.presidentMajor = student.majorName;
|
||||
form.value.presidentClass = student.className;
|
||||
form.value.presidentPhone = student.phone;
|
||||
form.value.presidentUserId = student.id;
|
||||
} else {
|
||||
form.value.presidentName = undefined;
|
||||
form.value.presidentStudentNo = undefined;
|
||||
@@ -1262,14 +1426,29 @@ const handlePresidentChange = (student: StudentVO | null) => {
|
||||
form.value.presidentMajor = undefined;
|
||||
form.value.presidentClass = undefined;
|
||||
form.value.presidentPhone = undefined;
|
||||
form.value.presidentUserId = undefined;
|
||||
}
|
||||
|
||||
// 手动触发相关字段的验证,确保表单验证状态正确更新
|
||||
nextTick(() => {
|
||||
clubFormRef.value?.validateField(['presidentName', 'presidentStudentNo']);
|
||||
});
|
||||
};
|
||||
|
||||
/** 物资保管员选择变化处理 */
|
||||
const handleMaterialKeeperChange = (student: StudentVO | null) => {
|
||||
if (student) {
|
||||
form.value.materialKeeperName = student.name;
|
||||
form.value.materialKeeperStudentNo = student.studentNo;
|
||||
form.value.materialKeeperPhone = student.phone;
|
||||
form.value.materialKeeperUserId = student.id;
|
||||
} else {
|
||||
form.value.materialKeeperName = undefined;
|
||||
form.value.materialKeeperStudentNo = undefined;
|
||||
form.value.materialKeeperPhone = undefined;
|
||||
form.value.materialKeeperUserId = undefined;
|
||||
}
|
||||
};
|
||||
|
||||
// 防止递归更新的锁
|
||||
const syncLock = ref(false);
|
||||
|
||||
|
||||
@@ -126,7 +126,7 @@
|
||||
v-hasPermi="['stgl:clubmember:edit']"
|
||||
></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="审核" placement="top" v-if="scope.row.status === 0">
|
||||
<el-tooltip content="审核" placement="top" v-if="scope.row.status === 0 || scope.row.status === 1">
|
||||
<el-button
|
||||
link
|
||||
type="success"
|
||||
|
||||
@@ -0,0 +1,275 @@
|
||||
<template>
|
||||
<div class="p-2 page-container">
|
||||
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
|
||||
<div v-show="showSearch" class="mb-[10px]">
|
||||
<el-card shadow="hover">
|
||||
<el-form ref="queryFormRef" :model="queryParams" label-width="100px">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="6">
|
||||
<el-form-item label="班级" prop="classId">
|
||||
<el-select v-model="queryParams.classId" placeholder="请选择班级" clearable style="width: 100%" @change="handleClassChange">
|
||||
<el-option v-for="item in classList" :key="item.id" :label="item.className" :value="item.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="学生姓名" prop="studentName">
|
||||
<el-input v-model="queryParams.studentName" placeholder="请输入学生姓名" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="学号" prop="studentNo">
|
||||
<el-input v-model="queryParams.studentNo" placeholder="请输入学号" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row>
|
||||
<el-col :span="24" class="search-btn-container">
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</el-card>
|
||||
</div>
|
||||
</transition>
|
||||
|
||||
<el-card shadow="never" class="button-card table-card">
|
||||
<template #header>
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button type="primary" plain icon="RefreshRight" @click="handleQuery">刷新</el-button>
|
||||
</el-col>
|
||||
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
<!-- 统计信息 -->
|
||||
<div v-if="queryParams.classId" class="statistics-bar">
|
||||
<el-tag type="success" size="large" effect="plain">
|
||||
当前共有 <span class="statistics-count">{{ total }}</span> 名学生正在参加活动
|
||||
</el-tag>
|
||||
</div>
|
||||
|
||||
<!-- 未选择班级提示 -->
|
||||
<div v-if="!queryParams.classId" class="no-data-container">
|
||||
<el-empty description="请先选择班级" />
|
||||
</div>
|
||||
|
||||
<!-- 活动列表表格 -->
|
||||
<el-table v-else v-loading="loading" border :data="activityList" row-key="id">
|
||||
<el-table-column label="序号" type="index" width="60" align="center" />
|
||||
<el-table-column label="学生姓名" align="center" prop="studentName" min-width="40" />
|
||||
<el-table-column label="学号" align="center" prop="studentNo" min-width="45" />
|
||||
<el-table-column label="性别" align="center" prop="gender" width="70">
|
||||
<template #default="scope">
|
||||
<dict-tag :options="sys_user_sex" :value="scope.row.gender" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="活动名称" align="center" min-width="60">
|
||||
<template #default="scope">
|
||||
<span v-if="scope.row.activityName" class="activity-name">{{ scope.row.activityName }}</span>
|
||||
<span v-else class="no-data">-</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="所属社团/队伍" align="center" prop="organizationName" min-width="50">
|
||||
<template #default="scope">
|
||||
<span v-if="scope.row.organizationName">{{ scope.row.organizationName }}</span>
|
||||
<span v-else class="no-data">-</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="活动开始时间" align="center" width="160">
|
||||
<template #default="scope">
|
||||
<span v-if="scope.row.startTime">{{ formatTime(scope.row.startTime) }}</span>
|
||||
<span v-else class="no-data">-</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="活动结束时间" align="center" width="160">
|
||||
<template #default="scope">
|
||||
<span v-if="scope.row.endTime">{{ formatTime(scope.row.endTime) }}</span>
|
||||
<span v-else class="no-data">-</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="活动地址" align="center" prop="location" min-width="50">
|
||||
<template #default="scope">
|
||||
<span v-if="scope.row.location">{{ scope.row.location }}</span>
|
||||
<span v-else class="no-data">-</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<div v-if="queryParams.classId" class="pagination-container">
|
||||
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="StglClassCurrentActivity" lang="ts">
|
||||
import { getTeacherClasses, getCurrentActivities } from '@/api/stgl/stclass';
|
||||
import { StglClassQuery, CurrentActivityVO, StglClassDetailVO } from '@/api/stgl/stclass/types';
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
const { sys_user_sex } = toRefs<any>(proxy?.useDict('sys_user_sex'));
|
||||
|
||||
const loading = ref(false);
|
||||
const showSearch = ref(true);
|
||||
const total = ref(0);
|
||||
const activityList = ref<CurrentActivityVO[]>([]);
|
||||
const classList = ref<StglClassDetailVO[]>([]);
|
||||
|
||||
const queryFormRef = ref<ElFormInstance>();
|
||||
|
||||
const queryParams = ref<StglClassQuery>({
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
classId: undefined,
|
||||
studentName: undefined,
|
||||
studentNo: undefined
|
||||
});
|
||||
|
||||
/** 格式化时间 yyyy-MM-dd HH:mm */
|
||||
const formatTime = (time: string) => {
|
||||
return dayjs(time).format('YYYY-MM-DD HH:mm');
|
||||
};
|
||||
|
||||
/** 查询班主任的班级列表 */
|
||||
const getTeacherClassList = async () => {
|
||||
try {
|
||||
const res = await getTeacherClasses();
|
||||
if ((res.code === 200 || res.code === 0) && res.data) {
|
||||
classList.value = res.data;
|
||||
if (classList.value.length > 0) {
|
||||
queryParams.value.classId = classList.value[0].id;
|
||||
await getList();
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取班级列表失败:', error);
|
||||
proxy?.$modal.msgError('获取班级列表失败');
|
||||
}
|
||||
};
|
||||
|
||||
/** 查询当前活动列表 */
|
||||
const getList = async () => {
|
||||
if (!queryParams.value.classId) return;
|
||||
loading.value = true;
|
||||
try {
|
||||
const res = await getCurrentActivities(queryParams.value);
|
||||
if (res.code === 200 || res.code === 0) {
|
||||
activityList.value = res.rows;
|
||||
total.value = res.total;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('查询当前活动列表失败:', error);
|
||||
proxy?.$modal.msgError('查询当前活动列表失败');
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.value.pageNum = 1;
|
||||
getList();
|
||||
};
|
||||
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
queryFormRef.value?.resetFields();
|
||||
handleQuery();
|
||||
};
|
||||
|
||||
/** 班级切换处理 */
|
||||
const handleClassChange = () => {
|
||||
queryParams.value.pageNum = 1;
|
||||
getList();
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getTeacherClassList();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 页面容器样式 */
|
||||
.page-container {
|
||||
padding: 8px;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
/* 表格卡片布局 */
|
||||
.table-card {
|
||||
margin-top: 0;
|
||||
border-radius: 12px;
|
||||
border: 1px solid #e4e7ed;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
.table-card :deep(.el-card__body) {
|
||||
padding: 0px 20px 20px 20px !important;
|
||||
}
|
||||
|
||||
/* 移除按钮栏底部边框 */
|
||||
.button-card :deep(.el-card__header) {
|
||||
border-bottom: none !important;
|
||||
background: linear-gradient(135deg, #fafbff 0%, #f8fafc 100%);
|
||||
padding: 16px 20px;
|
||||
}
|
||||
|
||||
/* 搜索按钮容器样式 */
|
||||
.search-btn-container {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin-top: 10px;
|
||||
|
||||
.el-form-item {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* 统计信息栏 */
|
||||
.statistics-bar {
|
||||
padding: 12px 0 16px 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.statistics-count {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
color: #409eff;
|
||||
}
|
||||
|
||||
/* 活动名称样式 */
|
||||
.activity-name {
|
||||
color: #303133;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* 分页容器样式 */
|
||||
.pagination-container {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
padding: 16px 20px 0 0 !important;
|
||||
background-color: #fff;
|
||||
margin-top: 0px !important;
|
||||
}
|
||||
|
||||
/* 无数据样式 */
|
||||
.no-data {
|
||||
color: #909399;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.no-data-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 300px;
|
||||
}
|
||||
</style>
|
||||
Reference in new issue
Block a user