tMerge branch 'master' of http://47.99.164.66:3000/hzdyjs-xg/all-project
This commit is contained in:
commit
844d3ac4a8
73 files changed
+1790
-846
No files matched your search
+1
-1
@@ -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>
|
||||
|
||||
+25
-4
@@ -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));
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -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()));
|
||||
}
|
||||
|
||||
|
||||
+15
@@ -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));
|
||||
}
|
||||
|
||||
/**
|
||||
* 修复已存在社团的成员记录
|
||||
* 为缺失社长、副社长成员记录的社团补充相应记录
|
||||
|
||||
+1
-1
@@ -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));
|
||||
}
|
||||
|
||||
+10
@@ -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));
|
||||
}
|
||||
}
|
||||
+6
@@ -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;
|
||||
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
|
||||
+2
-2
@@ -53,9 +53,9 @@ public class StglMember extends TenantEntity {
|
||||
private String studentName;
|
||||
|
||||
/**
|
||||
* 角色类型(1-社长 2-副社长 3-部长 4-普通成员)
|
||||
* 角色类型(社长、副社长、部长、普通成员)
|
||||
*/
|
||||
private Integer roleType;
|
||||
private String roleType;
|
||||
|
||||
/**
|
||||
* 加入时间
|
||||
|
||||
+5
@@ -127,4 +127,9 @@ public class StglVolunteerTeam extends TenantEntity {
|
||||
*/
|
||||
private Integer status;
|
||||
|
||||
/**
|
||||
* 报名开启状态(Y-开启 N-关闭)
|
||||
*/
|
||||
private String sighOpen;
|
||||
|
||||
}
|
||||
-40
@@ -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;
|
||||
}
|
||||
+5
@@ -230,6 +230,11 @@ public class StglClubBo extends BaseEntity {
|
||||
*/
|
||||
private Integer status;
|
||||
|
||||
/**
|
||||
* 开启报名(Y是 N否)
|
||||
*/
|
||||
private String sighOpen;
|
||||
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
|
||||
+2
-4
@@ -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;
|
||||
|
||||
/**
|
||||
* 备注
|
||||
|
||||
+6
@@ -150,4 +150,10 @@ public class StglVolunteerTeamBo extends BaseEntity {
|
||||
*/
|
||||
private Integer status;
|
||||
|
||||
/**
|
||||
* 报名开启状态(Y-开启 N-关闭)
|
||||
*/
|
||||
@Pattern(regexp = "^[YN]$", message = "报名开启状态只能为Y或N")
|
||||
private String sighOpen;
|
||||
|
||||
}
|
||||
+5
@@ -238,4 +238,9 @@ public class StglClubRegisterVo implements Serializable {
|
||||
*/
|
||||
private String remark;
|
||||
|
||||
/**
|
||||
* 开启报名(Y是 N否)
|
||||
*/
|
||||
private String sighOpen;
|
||||
|
||||
}
|
||||
+6
@@ -226,6 +226,12 @@ public class StglClubVo implements Serializable {
|
||||
@ExcelProperty(value = "状态")
|
||||
private Integer status;
|
||||
|
||||
/**
|
||||
* 开启报名(Y是 N否)
|
||||
*/
|
||||
@ExcelProperty(value = "开启报名")
|
||||
private String sighOpen;
|
||||
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
|
||||
+2
-2
@@ -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;
|
||||
|
||||
/**
|
||||
* 状态名称
|
||||
|
||||
+6
@@ -149,6 +149,12 @@ public class StglVolunteerTeamVo implements Serializable {
|
||||
@ExcelProperty(value = "状态")
|
||||
private Integer status;
|
||||
|
||||
/**
|
||||
* 报名开启状态(Y-开启 N-关闭)
|
||||
*/
|
||||
@ExcelProperty(value = "报名开启状态")
|
||||
private String sighOpen;
|
||||
|
||||
/**
|
||||
* 创建者
|
||||
*/
|
||||
|
||||
+2
-2
@@ -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) {
|
||||
|
||||
+2
-2
@@ -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);
|
||||
|
||||
+9
@@ -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查询活动考勤签到详情(关联查询活动信息)
|
||||
*
|
||||
|
||||
+5
-3
@@ -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);
|
||||
|
||||
/**
|
||||
* 检查用户是否已加入某个社团/志愿者队伍
|
||||
|
||||
+10
@@ -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);
|
||||
|
||||
/**
|
||||
* 用户签到
|
||||
*/
|
||||
|
||||
+9
@@ -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);
|
||||
|
||||
/**
|
||||
* 修复所有已存在社团的成员记录
|
||||
* 为那些没有对应成员记录的社长和副社长补充成员记录
|
||||
|
||||
+9
@@ -130,4 +130,13 @@ public interface IStglVolunteerTeamService {
|
||||
*/
|
||||
Integer fixExistingTeamLeaderRecords();
|
||||
|
||||
/**
|
||||
* 修改志愿者团队报名开启状态
|
||||
*
|
||||
* @param id 团队ID
|
||||
* @param sighOpen 报名开启状态(Y/N)
|
||||
* @return 是否修改成功
|
||||
*/
|
||||
Boolean changeSighOpenStatus(Long id, String sighOpen);
|
||||
|
||||
}
|
||||
+107
-17
@@ -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;
|
||||
}
|
||||
}
|
||||
+4
-2
@@ -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("社长和副社长不能直接退出,请先转让职务");
|
||||
}
|
||||
|
||||
|
||||
+29
-13
@@ -358,6 +358,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 +388,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 +497,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 +563,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 +604,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 +658,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 +692,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 +717,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 +737,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. 处理单个副社长字段(向后兼容)
|
||||
@@ -897,7 +913,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 +952,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 +964,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);
|
||||
|
||||
+45
-13
@@ -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()
|
||||
|
||||
+23
-8
@@ -220,7 +220,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 +256,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 +267,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 +303,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 +433,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 +540,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 +657,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 +675,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 +816,19 @@ 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;
|
||||
}
|
||||
}
|
||||
@@ -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 != ''">
|
||||
|
||||
+13
-7
@@ -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"/>
|
||||
|
||||
@@ -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 '备注',
|
||||
|
||||
@@ -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';
|
||||
|
||||
/**
|
||||
* 查询活动信息列表
|
||||
|
||||
@@ -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'
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1,33 +1,16 @@
|
||||
<template>
|
||||
<div class="activity-selector">
|
||||
<el-input
|
||||
v-model="selectedActivityName"
|
||||
placeholder="请选择活动"
|
||||
readonly
|
||||
@click="openSelector"
|
||||
style="cursor: pointer"
|
||||
>
|
||||
<el-input v-model="selectedActivityName" placeholder="请选择活动" readonly @click="openSelector" style="cursor: pointer">
|
||||
<template #suffix>
|
||||
<el-icon class="el-input__icon"><Search /></el-icon>
|
||||
</template>
|
||||
</el-input>
|
||||
|
||||
<!-- 活动选择对话框 -->
|
||||
<el-dialog
|
||||
v-model="dialogVisible"
|
||||
title="选择活动"
|
||||
width="800px"
|
||||
append-to-body
|
||||
>
|
||||
<el-dialog v-model="dialogVisible" title="选择活动" width="800px" append-to-body>
|
||||
<!-- 搜索框 -->
|
||||
<div class="mb-4">
|
||||
<el-input
|
||||
v-model="searchKeyword"
|
||||
placeholder="请输入活动名称搜索"
|
||||
clearable
|
||||
@input="handleSearch"
|
||||
style="width: 300px"
|
||||
>
|
||||
<el-input v-model="searchKeyword" placeholder="请输入活动名称搜索" clearable @input="handleSearch" style="width: 300px">
|
||||
<template #prefix>
|
||||
<el-icon><Search /></el-icon>
|
||||
</template>
|
||||
@@ -202,7 +185,9 @@ const formatDate = (dateStr: string): string => {
|
||||
};
|
||||
|
||||
/** 监听外部值变化 */
|
||||
watch(() => props.modelValue, async (newVal) => {
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
async (newVal) => {
|
||||
if (newVal) {
|
||||
try {
|
||||
// 这里可以根据ID获取活动名称
|
||||
@@ -219,7 +204,9 @@ watch(() => props.modelValue, async (newVal) => {
|
||||
} else {
|
||||
selectedActivityName.value = '';
|
||||
}
|
||||
}, { immediate: true });
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@@ -93,24 +93,32 @@ const currentHash = computed(() => {
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(newValue) => {
|
||||
console.log('AdvisorManager - 外部值变化:', newValue);
|
||||
if (!updateLock.value) {
|
||||
updateLock.value = true;
|
||||
nextTick(() => {
|
||||
advisors.value = newValue.map((item) => ({ ...item }));
|
||||
// 确保深拷贝,避免引用问题
|
||||
advisors.value = newValue ? newValue.map((item) => ({ ...item })) : [];
|
||||
console.log('AdvisorManager - 更新内部值:', advisors.value);
|
||||
updateLock.value = false;
|
||||
});
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
{ immediate: true, deep: false }
|
||||
);
|
||||
|
||||
// 监听内部值变化 - 使用hash比较避免深度比较
|
||||
watch(currentHash, (newHash) => {
|
||||
watch(currentHash, (newHash, oldHash) => {
|
||||
// 避免初始化时的重复触发
|
||||
if (oldHash === undefined) return;
|
||||
|
||||
console.log('AdvisorManager - 内部值变化检测到:', newHash);
|
||||
if (!updateLock.value) {
|
||||
updateLock.value = true;
|
||||
// 延迟emit,避免在TabPane环境下的立即更新冲突
|
||||
nextTick(() => {
|
||||
const newValue = advisors.value.map((advisor) => ({ ...advisor }));
|
||||
console.log('AdvisorManager - 发送更新事件:', newValue);
|
||||
emit('update:modelValue', newValue);
|
||||
// 重新添加change事件,但添加防护机制
|
||||
emit('change', newValue);
|
||||
|
||||
@@ -14,12 +14,7 @@
|
||||
value-key="uniqueKey"
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-option
|
||||
v-for="option in options"
|
||||
:key="`${option.relatedType}-${option.relatedClubId}`"
|
||||
:label="formatOptionLabel(option)"
|
||||
:value="option"
|
||||
>
|
||||
<el-option v-for="option in options" :key="option.uniqueKey || `${option.relatedType}-${option.relatedClubId}`" :label="formatOptionLabel(option)" :value="option">
|
||||
<div class="option-content">
|
||||
<span class="option-name">{{ option.relatedClubName }}</span>
|
||||
<span class="option-type">{{ getTypeLabel(option.relatedType) }}</span>
|
||||
@@ -31,12 +26,7 @@
|
||||
<el-dialog v-model="dialogVisible" title="选择社团/志愿者队伍" width="800px" append-to-body>
|
||||
<el-tabs v-model="activeTab" type="card">
|
||||
<el-tab-pane label="社团" name="club">
|
||||
<el-table
|
||||
:data="clubOptions"
|
||||
@row-click="handleRowClick"
|
||||
highlight-current-row
|
||||
height="400"
|
||||
>
|
||||
<el-table :data="clubOptions" @row-click="handleRowClick" highlight-current-row height="400">
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column label="社团名称" prop="relatedClubName" />
|
||||
<el-table-column label="类型" width="80" align="center">
|
||||
@@ -47,12 +37,7 @@
|
||||
</el-table>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="志愿者队伍" name="volunteer">
|
||||
<el-table
|
||||
:data="volunteerOptions"
|
||||
@row-click="handleRowClick"
|
||||
highlight-current-row
|
||||
height="400"
|
||||
>
|
||||
<el-table :data="volunteerOptions" @row-click="handleRowClick" highlight-current-row height="400">
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column label="队伍名称" prop="relatedClubName" />
|
||||
<el-table-column label="类型" width="100" align="center">
|
||||
@@ -111,6 +96,12 @@ const clubOptions = ref<ClubOption[]>([]);
|
||||
const volunteerOptions = ref<ClubOption[]>([]);
|
||||
const selectedOption = ref<ClubOption | null>(null);
|
||||
|
||||
// 监听 modelValue 变化
|
||||
import { watch } from 'vue';
|
||||
watch(() => props.modelValue, (newValue) => {
|
||||
console.log('ClubSelector - modelValue changed:', newValue);
|
||||
}, { immediate: true });
|
||||
|
||||
// 格式化选项标签
|
||||
const formatOptionLabel = (option: ClubOption): string => {
|
||||
const typeText = option.relatedType === 1 ? '社团' : '志愿者队伍';
|
||||
@@ -124,6 +115,7 @@ const getTypeLabel = (type: number): string => {
|
||||
|
||||
// 处理值变化
|
||||
const handleChange = (value: ClubOption | null) => {
|
||||
console.log('ClubSelector - handleChange:', value);
|
||||
emit('update:modelValue', value);
|
||||
emit('change', value);
|
||||
};
|
||||
@@ -134,10 +126,10 @@ const remoteMethod = async (query: string) => {
|
||||
loading.value = true;
|
||||
try {
|
||||
// 根据查询条件过滤选项
|
||||
const filteredOptions = [...clubOptions.value, ...volunteerOptions.value].filter(option =>
|
||||
const filteredOptions = [...clubOptions.value, ...volunteerOptions.value].filter((option) =>
|
||||
option.relatedClubName.toLowerCase().includes(query.toLowerCase())
|
||||
);
|
||||
options.value = filteredOptions.map(item => ({
|
||||
options.value = filteredOptions.map((item) => ({
|
||||
...item,
|
||||
uniqueKey: item.uniqueKey || `${item.relatedType}-${item.relatedClubId}`
|
||||
}));
|
||||
@@ -168,21 +160,25 @@ const confirmSelection = () => {
|
||||
const loadOptions = async () => {
|
||||
loading.value = true;
|
||||
try {
|
||||
const [clubRes, volunteerRes] = await Promise.all([
|
||||
getClubOptions(),
|
||||
getVolunteerTeamOptions()
|
||||
]);
|
||||
const [clubRes, volunteerRes] = await Promise.all([getClubOptions(), getVolunteerTeamOptions()]);
|
||||
|
||||
// 为每个选项添加唯一标识
|
||||
clubOptions.value = (clubRes.data || []).map(item => ({
|
||||
clubOptions.value = (clubRes.data || []).map((item) => ({
|
||||
...item,
|
||||
uniqueKey: `${item.relatedType}-${item.relatedClubId}`
|
||||
}));
|
||||
volunteerOptions.value = (volunteerRes.data || []).map(item => ({
|
||||
volunteerOptions.value = (volunteerRes.data || []).map((item) => ({
|
||||
...item,
|
||||
uniqueKey: `${item.relatedType}-${item.relatedClubId}`
|
||||
}));
|
||||
options.value = [...clubOptions.value, ...volunteerOptions.value];
|
||||
|
||||
console.log('ClubSelector - 加载选项数据完成:', {
|
||||
clubCount: clubOptions.value.length,
|
||||
volunteerCount: volunteerOptions.value.length,
|
||||
totalCount: options.value.length,
|
||||
options: options.value
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('加载选项失败:', error);
|
||||
} finally {
|
||||
|
||||
@@ -13,11 +13,7 @@
|
||||
</div>
|
||||
|
||||
<div v-else class="organizer-list">
|
||||
<div
|
||||
v-for="(organizer, index) in coOrganizers"
|
||||
:key="index"
|
||||
class="organizer-item"
|
||||
>
|
||||
<div v-for="(organizer, index) in coOrganizers" :key="index" class="organizer-item">
|
||||
<div class="organizer-content">
|
||||
<div class="organizer-info">
|
||||
<div class="info-row">
|
||||
@@ -30,13 +26,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="organizer-actions">
|
||||
<el-button
|
||||
type="danger"
|
||||
circle
|
||||
size="small"
|
||||
@click="handleDelete(index)"
|
||||
title="删除"
|
||||
>
|
||||
<el-button type="danger" circle size="small" @click="handleDelete(index)" title="删除">
|
||||
<el-icon><Delete /></el-icon>
|
||||
</el-button>
|
||||
</div>
|
||||
@@ -45,40 +35,17 @@
|
||||
</div>
|
||||
|
||||
<!-- 添加/编辑承办方对话框 -->
|
||||
<el-dialog
|
||||
v-model="dialogVisible"
|
||||
:title="dialogTitle"
|
||||
width="500px"
|
||||
append-to-body
|
||||
>
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="currentOrganizer"
|
||||
:rules="rules"
|
||||
label-width="100px"
|
||||
>
|
||||
<el-dialog v-model="dialogVisible" :title="dialogTitle" width="500px" append-to-body>
|
||||
<el-form ref="formRef" :model="currentOrganizer" :rules="rules" label-width="100px">
|
||||
<el-form-item label="承办方名称" prop="name">
|
||||
<el-input
|
||||
v-model="currentOrganizer.name"
|
||||
placeholder="请输入承办方名称"
|
||||
maxlength="100"
|
||||
show-word-limit
|
||||
/>
|
||||
<el-input v-model="currentOrganizer.name" placeholder="请输入承办方名称" maxlength="100" show-word-limit />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="联系人" prop="contact">
|
||||
<el-input
|
||||
v-model="currentOrganizer.contact"
|
||||
placeholder="请输入联系人姓名"
|
||||
maxlength="50"
|
||||
/>
|
||||
<el-input v-model="currentOrganizer.contact" placeholder="请输入联系人姓名" maxlength="50" />
|
||||
</el-form-item>
|
||||
<el-form-item label="联系电话" prop="phone">
|
||||
<el-input
|
||||
v-model="currentOrganizer.phone"
|
||||
placeholder="请输入联系电话"
|
||||
maxlength="20"
|
||||
/>
|
||||
<el-input v-model="currentOrganizer.phone" placeholder="请输入联系电话" maxlength="20" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
@@ -138,9 +105,7 @@ const rules: FormRules = {
|
||||
{ min: 1, max: 100, message: '承办方名称长度在1到100个字符', trigger: 'blur' }
|
||||
],
|
||||
|
||||
contact: [
|
||||
{ max: 50, message: '联系人姓名长度不能超过50个字符', trigger: 'blur' }
|
||||
],
|
||||
contact: [{ max: 50, message: '联系人姓名长度不能超过50个字符', trigger: 'blur' }],
|
||||
phone: [
|
||||
{ max: 20, message: '联系电话长度不能超过20个字符', trigger: 'blur' },
|
||||
{ pattern: /^[0-9\-\s\+\(\)]*$/, message: '请输入正确的电话号码格式', trigger: 'blur' }
|
||||
@@ -177,18 +142,18 @@ const handleAdd = () => {
|
||||
dialogVisible.value = true;
|
||||
};
|
||||
|
||||
|
||||
|
||||
// 删除承办方
|
||||
const handleDelete = (index: number) => {
|
||||
ElMessageBox.confirm('确定要删除这个承办方吗?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
})
|
||||
.then(() => {
|
||||
coOrganizers.value.splice(index, 1);
|
||||
ElMessage.success('删除成功');
|
||||
}).catch(() => {
|
||||
})
|
||||
.catch(() => {
|
||||
// 用户取消删除
|
||||
});
|
||||
};
|
||||
|
||||
@@ -13,11 +13,7 @@
|
||||
</div>
|
||||
|
||||
<div v-else class="instructor-list">
|
||||
<div
|
||||
v-for="(instructor, index) in instructors"
|
||||
:key="index"
|
||||
class="instructor-item"
|
||||
>
|
||||
<div v-for="(instructor, index) in instructors" :key="index" class="instructor-item">
|
||||
<div class="instructor-content">
|
||||
<div class="instructor-info">
|
||||
<div class="info-row">
|
||||
@@ -56,55 +52,22 @@
|
||||
</div>
|
||||
|
||||
<!-- 添加/编辑指导老师对话框 -->
|
||||
<el-dialog
|
||||
v-model="dialogVisible"
|
||||
:title="dialogTitle"
|
||||
width="500px"
|
||||
append-to-body
|
||||
>
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="currentInstructor"
|
||||
:rules="rules"
|
||||
label-width="100px"
|
||||
>
|
||||
<el-dialog v-model="dialogVisible" :title="dialogTitle" width="500px" append-to-body>
|
||||
<el-form ref="formRef" :model="currentInstructor" :rules="rules" label-width="100px">
|
||||
<el-form-item label="老师姓名" prop="name">
|
||||
<el-input
|
||||
v-model="currentInstructor.name"
|
||||
placeholder="请输入老师姓名"
|
||||
maxlength="50"
|
||||
show-word-limit
|
||||
/>
|
||||
<el-input v-model="currentInstructor.name" placeholder="请输入老师姓名" maxlength="50" show-word-limit />
|
||||
</el-form-item>
|
||||
<el-form-item label="所属部门" prop="dept">
|
||||
<el-input
|
||||
v-model="currentInstructor.dept"
|
||||
placeholder="请输入所属部门"
|
||||
maxlength="100"
|
||||
/>
|
||||
<el-input v-model="currentInstructor.dept" placeholder="请输入所属部门" maxlength="100" />
|
||||
</el-form-item>
|
||||
<el-form-item label="联系电话" prop="phone">
|
||||
<el-input
|
||||
v-model="currentInstructor.phone"
|
||||
placeholder="请输入联系电话"
|
||||
maxlength="20"
|
||||
/>
|
||||
<el-input v-model="currentInstructor.phone" placeholder="请输入联系电话" maxlength="20" />
|
||||
</el-form-item>
|
||||
<el-form-item label="邮箱" prop="email">
|
||||
<el-input
|
||||
v-model="currentInstructor.email"
|
||||
placeholder="请输入邮箱地址"
|
||||
maxlength="100"
|
||||
/>
|
||||
<el-input v-model="currentInstructor.email" placeholder="请输入邮箱地址" maxlength="100" />
|
||||
</el-form-item>
|
||||
<el-form-item label="指导学时" prop="hours">
|
||||
<el-input-number
|
||||
v-model="currentInstructor.hours"
|
||||
:min="0"
|
||||
:max="9999"
|
||||
placeholder="请输入指导学时"
|
||||
style="width: 100%"
|
||||
/>
|
||||
<el-input-number v-model="currentInstructor.hours" :min="0" :max="9999" placeholder="请输入指导学时" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
@@ -165,9 +128,7 @@ const rules: FormRules = {
|
||||
{ required: true, message: '请输入老师姓名', trigger: 'blur' },
|
||||
{ min: 1, max: 50, message: '老师姓名长度在1到50个字符', trigger: 'blur' }
|
||||
],
|
||||
dept: [
|
||||
{ max: 100, message: '所属部门长度不能超过100个字符', trigger: 'blur' }
|
||||
],
|
||||
dept: [{ max: 100, message: '所属部门长度不能超过100个字符', trigger: 'blur' }],
|
||||
phone: [
|
||||
{ max: 20, message: '联系电话长度不能超过20个字符', trigger: 'blur' },
|
||||
{ pattern: /^[0-9\-\s\+\(\)]*$/, message: '请输入正确的电话号码格式', trigger: 'blur' }
|
||||
@@ -176,9 +137,7 @@ const rules: FormRules = {
|
||||
{ max: 100, message: '邮箱长度不能超过100个字符', trigger: 'blur' },
|
||||
{ type: 'email', message: '请输入正确的邮箱格式', trigger: 'blur' }
|
||||
],
|
||||
hours: [
|
||||
{ type: 'number', min: 0, max: 9999, message: '指导学时必须在0到9999之间', trigger: 'blur' }
|
||||
]
|
||||
hours: [{ type: 'number', min: 0, max: 9999, message: '指导学时必须在0到9999之间', trigger: 'blur' }]
|
||||
};
|
||||
|
||||
// 监听props变化
|
||||
@@ -226,10 +185,12 @@ const handleDelete = (index: number) => {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
})
|
||||
.then(() => {
|
||||
instructors.value.splice(index, 1);
|
||||
ElMessage.success('删除成功');
|
||||
}).catch(() => {
|
||||
})
|
||||
.catch(() => {
|
||||
// 用户取消删除
|
||||
});
|
||||
};
|
||||
|
||||
@@ -2,12 +2,7 @@
|
||||
<div class="material-type-selector">
|
||||
<div class="type-options">
|
||||
<el-checkbox-group v-model="selectedTypes" @change="handleChange">
|
||||
<el-checkbox
|
||||
v-for="option in materialTypes"
|
||||
:key="option.value"
|
||||
:label="option.value"
|
||||
class="type-checkbox"
|
||||
>
|
||||
<el-checkbox v-for="option in materialTypes" :key="option.value" :label="option.value" class="type-checkbox">
|
||||
{{ option.label }}
|
||||
</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
@@ -29,9 +24,7 @@ const props = defineProps<Props>();
|
||||
const emit = defineEmits<Emits>();
|
||||
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
const { stgl_material_type } = toRefs<any>(
|
||||
proxy?.useDict('stgl_material_type')
|
||||
);
|
||||
const { stgl_material_type } = toRefs<any>(proxy?.useDict('stgl_material_type'));
|
||||
|
||||
const selectedTypes = ref<string[]>([]);
|
||||
|
||||
@@ -48,13 +41,17 @@ const handleChange = (value: string[]) => {
|
||||
};
|
||||
|
||||
/** 监听外部值变化 */
|
||||
watch(() => props.modelValue, (newVal) => {
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(newVal) => {
|
||||
if (newVal) {
|
||||
selectedTypes.value = newVal.split(',').filter(item => item.trim());
|
||||
selectedTypes.value = newVal.split(',').filter((item) => item.trim());
|
||||
} else {
|
||||
selectedTypes.value = [];
|
||||
}
|
||||
}, { immediate: true });
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@@ -6,14 +6,7 @@
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>成员 {{ index + 1 }}</span>
|
||||
<el-button
|
||||
type="danger"
|
||||
size="small"
|
||||
icon="Delete"
|
||||
circle
|
||||
@click="removeMember(index)"
|
||||
:disabled="memberList.length <= 1"
|
||||
/>
|
||||
<el-button type="danger" size="small" icon="Delete" circle @click="removeMember(index)" :disabled="memberList.length <= 1" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -239,7 +239,7 @@
|
||||
<!-- 签到操作区域 -->
|
||||
<div v-if="form.activityId" class="sign-action-section">
|
||||
<h4 class="section-title">签到操作</h4>
|
||||
<el-row :gutter="20" style="text-align: center;">
|
||||
<el-row :gutter="20" style="text-align: center">
|
||||
<el-col :span="12">
|
||||
<el-button
|
||||
type="primary"
|
||||
@@ -331,9 +331,7 @@ import {
|
||||
import { StglActivityAttendanceVO, StglActivityAttendanceQuery, StglActivityAttendanceForm } from '@/api/stgl/activityattendance/types';
|
||||
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
const { stgl_activity_type, stgl_activity_sign_status } = toRefs<any>(
|
||||
proxy?.useDict('stgl_activity_type', 'stgl_activity_sign_status')
|
||||
);
|
||||
const { stgl_activity_type, stgl_activity_sign_status } = toRefs<any>(proxy?.useDict('stgl_activity_type', 'stgl_activity_sign_status'));
|
||||
|
||||
const attendanceList = ref<StglActivityAttendanceVO[]>([]);
|
||||
const availableActivities = ref<StglActivityAttendanceVO[]>([]);
|
||||
@@ -489,7 +487,7 @@ const handleActivityChange = async (activityId: string | number) => {
|
||||
}
|
||||
|
||||
// 设置选中的活动信息
|
||||
const activity = availableActivities.value.find(item => item.activityId === activityId);
|
||||
const activity = availableActivities.value.find((item) => item.activityId === activityId);
|
||||
if (activity) {
|
||||
selectedActivity.value = activity;
|
||||
|
||||
|
||||
@@ -272,6 +272,11 @@
|
||||
</el-row>
|
||||
</el-tab-pane>
|
||||
|
||||
<!-- 报名查看标签页 -->
|
||||
<el-tab-pane label="报名查看" name="registration-view">
|
||||
<ActivityRegistrationView :activity-id="activity.id" :is-view-mode="true" />
|
||||
</el-tab-pane>
|
||||
|
||||
<!-- 其他信息标签页(仅已办活动显示) -->
|
||||
<el-tab-pane label="其他信息" name="other" v-if="activity.isFinished === 1">
|
||||
<div class="detail-item">
|
||||
@@ -362,6 +367,7 @@
|
||||
import { computed, ref, onMounted, watch } from 'vue';
|
||||
import { StglActivityVO, CoOrganizerInfo, InstructorInfo } from '@/api/stgl/activity/types';
|
||||
import ActivityRatingView from './ActivityRatingView.vue';
|
||||
import ActivityRegistrationView from './ActivityRegistrationView.vue';
|
||||
import { listByIds } from '@/api/system/oss';
|
||||
|
||||
interface Props {
|
||||
@@ -404,7 +410,6 @@ const instructorList = computed<InstructorInfo[]>(() => {
|
||||
|
||||
// 加载附件信息
|
||||
const loadAttachments = async () => {
|
||||
|
||||
// 重置附件列表
|
||||
summaryAttachmentList.value = [];
|
||||
achievementsAttachmentList.value = [];
|
||||
@@ -423,7 +428,7 @@ const loadAttachments = async () => {
|
||||
}
|
||||
} else {
|
||||
// ossId格式:通过API获取真实附件信息
|
||||
const ossIds = attachmentStr.split(',').filter(id => id.trim());
|
||||
const ossIds = attachmentStr.split(',').filter((id) => id.trim());
|
||||
if (ossIds.length > 0) {
|
||||
try {
|
||||
const res = await listByIds(ossIds.join(','));
|
||||
@@ -454,7 +459,7 @@ const loadAttachments = async () => {
|
||||
}
|
||||
} else {
|
||||
// ossId格式:通过API获取真实附件信息
|
||||
const ossIds = attachmentStr.split(',').filter(id => id.trim());
|
||||
const ossIds = attachmentStr.split(',').filter((id) => id.trim());
|
||||
if (ossIds.length > 0) {
|
||||
try {
|
||||
const res = await listByIds(ossIds.join(','));
|
||||
@@ -480,11 +485,15 @@ const downloadAttachment = (file: any) => {
|
||||
};
|
||||
|
||||
// 监听props变化,重新加载附件信息
|
||||
watch(() => props.activity, (newActivity) => {
|
||||
watch(
|
||||
() => props.activity,
|
||||
(newActivity) => {
|
||||
if (newActivity && newActivity.id) {
|
||||
loadAttachments();
|
||||
}
|
||||
}, { immediate: true });
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
// 组件挂载时加载附件信息
|
||||
onMounted(() => {
|
||||
|
||||
@@ -59,7 +59,7 @@ const averageRating = computed(() => {
|
||||
// 计算最高评分
|
||||
const maxRating = computed(() => {
|
||||
if (ratings.value.length === 0) return 0;
|
||||
return Math.max(...ratings.value.map(rating => rating.ratingScore));
|
||||
return Math.max(...ratings.value.map((rating) => rating.ratingScore));
|
||||
});
|
||||
|
||||
// 加载评分数据
|
||||
|
||||
@@ -124,24 +124,10 @@
|
||||
></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="已报名" placement="top" v-else-if="isRegistered(scope.row)">
|
||||
<el-button
|
||||
link
|
||||
type="warning"
|
||||
icon="Check"
|
||||
size="default"
|
||||
style="font-size: 16px"
|
||||
disabled
|
||||
></el-button>
|
||||
<el-button link type="warning" icon="Check" size="default" style="font-size: 16px" disabled></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="报名已结束" placement="top" v-else>
|
||||
<el-button
|
||||
link
|
||||
type="info"
|
||||
icon="Clock"
|
||||
size="default"
|
||||
style="font-size: 16px"
|
||||
disabled
|
||||
></el-button>
|
||||
<el-button link type="info" icon="Clock" size="default" style="font-size: 16px" disabled></el-button>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
@@ -301,7 +287,7 @@
|
||||
</el-row>
|
||||
|
||||
<!-- 团队成员管理 -->
|
||||
<el-divider content-position="left" style="margin-left: 0px;">团队成员</el-divider>
|
||||
<el-divider content-position="left" style="margin-left: 0px">团队成员</el-divider>
|
||||
<VicePresidentManager
|
||||
:model-value="registerForm.teamMembersNew"
|
||||
title="团队管理"
|
||||
@@ -312,7 +298,7 @@
|
||||
/>
|
||||
</div>
|
||||
|
||||
<el-form-item label="申请理由" prop="remark" style="margin-top: 24px;">
|
||||
<el-form-item label="申请理由" prop="remark" style="margin-top: 24px">
|
||||
<el-input v-model="registerForm.remark" type="textarea" :rows="4" placeholder="请简要说明您的申请理由和个人优势(选填)" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
@@ -398,10 +384,12 @@ const data = reactive<PageData<StglActivityRegistrationForm, StglActivityQuery>>
|
||||
const { queryParams } = toRefs(data);
|
||||
|
||||
// 报名表单
|
||||
const registerForm = reactive<StglActivityRegistrationForm & {
|
||||
const registerForm = reactive<
|
||||
StglActivityRegistrationForm & {
|
||||
presidentStudent?: StudentVO | null;
|
||||
teamMembersNew?: VicePresidentVO[];
|
||||
}>({
|
||||
}
|
||||
>({
|
||||
activityId: undefined,
|
||||
registrationType: undefined,
|
||||
studentName: undefined,
|
||||
@@ -604,7 +592,7 @@ const handleTeamMembersNewChange = (members: VicePresidentVO[]) => {
|
||||
registerForm.teamMembersNew = members;
|
||||
|
||||
// 同步到原有字段,用于兼容后端
|
||||
registerForm.teamMembers = members.map(member => ({
|
||||
registerForm.teamMembers = members.map((member) => ({
|
||||
userId: member.student?.id || 0,
|
||||
studentNo: member.student?.studentNo || '',
|
||||
studentName: member.name || member.student?.name || '',
|
||||
|
||||
@@ -45,6 +45,11 @@
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="关联社团" prop="relatedClubId">
|
||||
<ClubSelector v-model="queryParams.relatedClub" @change="handleQueryRelatedClubChange" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="活动时间">
|
||||
<el-date-picker
|
||||
@@ -80,11 +85,7 @@
|
||||
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['stgl:activity:add']">新增</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5" v-if="!single && selectedActivity && !['2', '3', '4', '5'].includes(selectedActivity.activityStatus)">
|
||||
<el-button
|
||||
icon="Edit"
|
||||
@click="handleUpdate()"
|
||||
v-hasPermi="['stgl:activity:edit']"
|
||||
>修改</el-button>
|
||||
<el-button icon="Edit" @click="handleUpdate()" v-hasPermi="['stgl:activity:edit']">修改</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button icon="CopyDocument" :disabled="single" @click="handleCopy()" v-hasPermi="['stgl:activity:add']">复制</el-button>
|
||||
@@ -96,7 +97,8 @@
|
||||
@click="handleDelete()"
|
||||
v-hasPermi="['stgl:activity:remove']"
|
||||
:title="selectedActivity && selectedActivity.activityStatus !== '1' ? '只有筹备中的活动可以删除' : ''"
|
||||
>删除</el-button>
|
||||
>删除</el-button
|
||||
>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="success" icon="Download" @click="handleExport" v-hasPermi="['stgl:activity:export']">导出</el-button>
|
||||
@@ -180,7 +182,7 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="创建者" align="center" prop="createByName" width="100" />
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="280" fixed="right">
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="320" fixed="right">
|
||||
<template #default="scope">
|
||||
<el-tooltip content="查看" placement="top">
|
||||
<el-button
|
||||
@@ -226,6 +228,17 @@
|
||||
v-hasPermi="['stgl:activity:edit']"
|
||||
></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="成员审核" placement="top">
|
||||
<el-button
|
||||
link
|
||||
type="info"
|
||||
icon="User"
|
||||
size="default"
|
||||
style="font-size: 16px; margin-right: 8px"
|
||||
@click="handleMembersReview(scope.row)"
|
||||
v-hasPermi="['stgl:activity:query']"
|
||||
></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="删除" placement="top" v-if="scope.row.activityStatus === '1'">
|
||||
<el-button
|
||||
link
|
||||
@@ -356,7 +369,11 @@
|
||||
<div class="section-divider">
|
||||
<h4 class="section-title">指导老师信息</h4>
|
||||
</div>
|
||||
<AdvisorManager v-model="form.advisors" @change="handleAdvisorsChange" />
|
||||
<AdvisorManager
|
||||
:key="`advisor-manager-${form.id || 'new'}-${formKey}`"
|
||||
v-model="form.advisors"
|
||||
@change="handleAdvisorsChange"
|
||||
/>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="报名设置" name="registration">
|
||||
@@ -427,24 +444,9 @@
|
||||
</el-row>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="其他信息" name="other" v-if="form.isFinished === 1">
|
||||
<el-form-item label="活动总结报告" prop="summary">
|
||||
<el-input v-model="form.summary" type="textarea" placeholder="请输入活动总结报告" resize="none" :rows="6" />
|
||||
</el-form-item>
|
||||
<el-form-item label="总结报告附件" prop="summaryAttachments">
|
||||
<file-upload v-model="form.summaryAttachments" />
|
||||
</el-form-item>
|
||||
<el-form-item label="活动成果" prop="achievements">
|
||||
<el-input v-model="form.achievements" type="textarea" placeholder="请输入活动成果" resize="none" :rows="6" />
|
||||
</el-form-item>
|
||||
<el-form-item label="成果附件" prop="achievementsAttachments">
|
||||
<file-upload v-model="form.achievementsAttachments" />
|
||||
</el-form-item>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="活动评分" name="rating" v-if="form.id && !dialog.title.includes('新增')">
|
||||
<ActivityRatingView :activity-id="form.id" />
|
||||
</el-tab-pane>
|
||||
|
||||
|
||||
</el-tabs>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
@@ -509,11 +511,20 @@
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 成员审核弹窗 -->
|
||||
<ActivityMembersDialog
|
||||
v-model:visible="membersDialog.visible"
|
||||
:activity-id="selectedActivity?.id"
|
||||
:activity-name="selectedActivity?.activityName"
|
||||
ref="membersDialogRef"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="StglActivity" lang="ts">
|
||||
import { nextTick } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import {
|
||||
listStglActivity,
|
||||
getStglActivity,
|
||||
@@ -531,9 +542,13 @@ import InstructorManager from '@/components/InstructorManager/index.vue';
|
||||
import AdvisorManager from '@/components/AdvisorManager/index.vue';
|
||||
import ActivityRatingView from './components/ActivityRatingView.vue';
|
||||
import ActivityDetailView from './components/ActivityDetailView.vue';
|
||||
import ActivityMembersDialog from './components/ActivityMembersDialog.vue';
|
||||
import ActivityRegistrationView from './components/ActivityRegistrationView.vue'; // Added import
|
||||
import { AdvisorVO } from '@/components/AdvisorManager/index.vue';
|
||||
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const { stgl_activity_type, stgl_activity_status, stgl_registration_type, stgl_registration_status, sys_yes_no } = toRefs<any>(
|
||||
proxy?.useDict('stgl_activity_type', 'stgl_activity_status', 'stgl_registration_type', 'stgl_registration_status', 'sys_yes_no')
|
||||
);
|
||||
@@ -549,11 +564,13 @@ const total = ref(0);
|
||||
const activeTab = ref('basic');
|
||||
const dateRange = ref<[string, string]>();
|
||||
const selectedActivity = ref<StglActivityVO | null>(null);
|
||||
const formKey = ref(0);
|
||||
|
||||
const queryFormRef = ref<ElFormInstance>();
|
||||
const activityFormRef = ref<ElFormInstance>();
|
||||
const copyFormRef = ref<ElFormInstance>();
|
||||
const summaryFormRef = ref<ElFormInstance>();
|
||||
const membersDialogRef = ref();
|
||||
|
||||
const dialog = reactive<DialogOption>({
|
||||
visible: false,
|
||||
@@ -575,6 +592,11 @@ const summaryDialog = reactive<DialogOption>({
|
||||
title: ''
|
||||
});
|
||||
|
||||
const membersDialog = reactive<DialogOption>({
|
||||
visible: false,
|
||||
title: ''
|
||||
});
|
||||
|
||||
const copyLoading = ref(false);
|
||||
const summaryLoading = ref(false);
|
||||
|
||||
@@ -619,7 +641,7 @@ const initFormData: StglActivityForm = {
|
||||
remark: undefined
|
||||
};
|
||||
|
||||
const data = reactive<PageData<StglActivityForm, StglActivityQuery>>({
|
||||
const data = reactive<PageData<StglActivityForm, StglActivityQuery & { relatedClub?: ClubOption | null }>>({
|
||||
form: { ...initFormData },
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
@@ -631,6 +653,7 @@ const data = reactive<PageData<StglActivityForm, StglActivityQuery>>({
|
||||
endTime: undefined,
|
||||
location: undefined,
|
||||
relatedClubId: undefined,
|
||||
relatedClub: null,
|
||||
organizerName: undefined,
|
||||
registrationType: undefined,
|
||||
registrationStart: undefined,
|
||||
@@ -654,6 +677,45 @@ const { queryParams, form, rules } = toRefs(data);
|
||||
|
||||
const viewForm = ref<StglActivityVO>({} as StglActivityVO);
|
||||
|
||||
|
||||
|
||||
/** 初始化URL参数 */
|
||||
const initUrlParams = () => {
|
||||
const { relatedClubId, relatedClubName, relatedTeamId, relatedTeamName, from } = route.query;
|
||||
|
||||
if (relatedClubId && relatedClubName && from === 'club') {
|
||||
// 设置社团查询参数
|
||||
queryParams.value.relatedClubId = relatedClubId as string | number;
|
||||
queryParams.value.relatedClub = {
|
||||
relatedClubId: relatedClubId as string | number,
|
||||
relatedClubName: relatedClubName as string,
|
||||
relatedType: 1, // 社团类型
|
||||
uniqueKey: `1-${relatedClubId}`
|
||||
};
|
||||
|
||||
console.log('初始化URL参数 - 社团过滤:', {
|
||||
relatedClubId,
|
||||
relatedClubName,
|
||||
queryParams: queryParams.value.relatedClub
|
||||
});
|
||||
} else if (relatedTeamId && relatedTeamName && from === 'team') {
|
||||
// 设置志愿者队伍查询参数
|
||||
queryParams.value.relatedClubId = relatedTeamId as string | number;
|
||||
queryParams.value.relatedClub = {
|
||||
relatedClubId: relatedTeamId as string | number,
|
||||
relatedClubName: relatedTeamName as string,
|
||||
relatedType: 2, // 志愿者队伍类型
|
||||
uniqueKey: `2-${relatedTeamId}`
|
||||
};
|
||||
|
||||
console.log('初始化URL参数 - 志愿者队伍过滤:', {
|
||||
relatedTeamId,
|
||||
relatedTeamName,
|
||||
queryParams: queryParams.value.relatedClub
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// 复制表单
|
||||
const copyForm = reactive({
|
||||
sourceId: undefined as string | number | undefined,
|
||||
@@ -714,6 +776,14 @@ const reset = () => {
|
||||
// 使用nextTick来避免递归更新
|
||||
nextTick(() => {
|
||||
form.value = { ...initFormData };
|
||||
// 确保指导老师字段被正确初始化
|
||||
form.value.advisors = [];
|
||||
form.value.instructorList = [];
|
||||
form.value.instructors = undefined;
|
||||
|
||||
// 更新formKey以强制重新渲染组件
|
||||
formKey.value++;
|
||||
|
||||
activeTab.value = 'basic';
|
||||
nextTick(() => {
|
||||
activityFormRef.value?.resetFields();
|
||||
@@ -744,9 +814,56 @@ const handleSelectionChange = (selection: StglActivityVO[]) => {
|
||||
|
||||
/** 新增按钮操作 */
|
||||
const handleAdd = () => {
|
||||
reset();
|
||||
// 先重置表单
|
||||
form.value = { ...initFormData };
|
||||
activeTab.value = 'basic';
|
||||
|
||||
// 确保指导老师字段被正确初始化
|
||||
form.value.advisors = [];
|
||||
form.value.instructorList = [];
|
||||
form.value.instructors = undefined;
|
||||
|
||||
// 更新formKey以强制重新渲染组件
|
||||
formKey.value++;
|
||||
|
||||
console.log('新增活动 - 初始化表单数据:', {
|
||||
advisors: form.value.advisors,
|
||||
instructorList: form.value.instructorList,
|
||||
instructors: form.value.instructors,
|
||||
formKey: formKey.value
|
||||
});
|
||||
|
||||
// 如果有查询条件中的关联组织(社团或志愿者队伍),自动填充关联信息
|
||||
if (queryParams.value.relatedClub) {
|
||||
form.value.relatedClub = queryParams.value.relatedClub;
|
||||
form.value.relatedClubId = queryParams.value.relatedClub.relatedClubId;
|
||||
form.value.relatedType = queryParams.value.relatedClub.relatedType;
|
||||
|
||||
console.log('新增活动 - 自动填充关联组织信息:', {
|
||||
relatedType: queryParams.value.relatedClub.relatedType,
|
||||
relatedClubId: queryParams.value.relatedClub.relatedClubId,
|
||||
relatedClubName: queryParams.value.relatedClub.relatedClubName
|
||||
});
|
||||
|
||||
// 确保表单数据正确设置
|
||||
console.log('新增活动 - 表单数据设置后:', {
|
||||
formRelatedClub: form.value.relatedClub,
|
||||
formRelatedClubId: form.value.relatedClubId,
|
||||
formRelatedType: form.value.relatedType
|
||||
});
|
||||
}
|
||||
|
||||
dialog.visible = true;
|
||||
dialog.title = '添加活动信息';
|
||||
|
||||
// 确保对话框完全打开后再检查表单状态
|
||||
nextTick(() => {
|
||||
console.log('新增活动 - 对话框打开后的表单状态:', {
|
||||
advisors: form.value.advisors,
|
||||
instructorList: form.value.instructorList,
|
||||
instructors: form.value.instructors
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/** 修改按钮操作 */
|
||||
@@ -857,17 +974,23 @@ const submitForm = () => {
|
||||
|
||||
// 处理AdvisorManager组件的指导老师数据
|
||||
if (submitData.advisors && submitData.advisors.length > 0) {
|
||||
console.log('提交前 - 原始advisors数据:', submitData.advisors);
|
||||
|
||||
// 确保advisors数据完整
|
||||
submitData.advisors = submitData.advisors.map((advisor: any) => {
|
||||
return {
|
||||
const processedAdvisor = {
|
||||
...advisor,
|
||||
name: advisor.name || advisor.teacher?.nickName || '',
|
||||
dept: advisor.dept || advisor.teacher?.deptName || '',
|
||||
phone: advisor.phone || advisor.teacher?.phonenumber || '',
|
||||
email: advisor.email || advisor.teacher?.email || ''
|
||||
};
|
||||
console.log('处理后的指导老师数据:', processedAdvisor);
|
||||
return processedAdvisor;
|
||||
});
|
||||
|
||||
submitData.instructors = JSON.stringify(submitData.advisors);
|
||||
console.log('提交的指导老师JSON数据:', submitData.instructors);
|
||||
|
||||
// 同步第一个指导老师信息到instructorList(保持兼容性)
|
||||
if (submitData.advisors.length > 0) {
|
||||
@@ -877,7 +1000,13 @@ const submitForm = () => {
|
||||
phone: advisor.phone,
|
||||
email: advisor.email
|
||||
}));
|
||||
console.log('同步的instructorList数据:', submitData.instructorList);
|
||||
}
|
||||
} else {
|
||||
console.log('提交时 - 没有指导老师数据或数据为空');
|
||||
submitData.advisors = [];
|
||||
submitData.instructors = '';
|
||||
submitData.instructorList = [];
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -916,9 +1045,9 @@ const handleDelete = async (row?: StglActivityVO) => {
|
||||
const selectedItems = activityList.value.filter((item) => ids.value.includes(item.id));
|
||||
|
||||
// 检查选中的活动是否都是筹备中状态
|
||||
const nonDeletableItems = selectedItems.filter(item => item.activityStatus !== '1');
|
||||
const nonDeletableItems = selectedItems.filter((item) => item.activityStatus !== '1');
|
||||
if (nonDeletableItems.length > 0) {
|
||||
const names = nonDeletableItems.map(item => item.activityName).join('、');
|
||||
const names = nonDeletableItems.map((item) => item.activityName).join('、');
|
||||
proxy?.$modal.msgWarning(`活动"${names}"不是筹备中状态,只有筹备中的活动可以删除`);
|
||||
return;
|
||||
}
|
||||
@@ -1067,8 +1196,6 @@ const handleAchievementsAttachmentsChange = (attachments: string) => {
|
||||
summaryForm.achievementsAttachments = attachments;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/** 导出按钮操作 */
|
||||
const handleExport = async () => {
|
||||
try {
|
||||
@@ -1082,11 +1209,26 @@ const handleExport = async () => {
|
||||
|
||||
/** 关联社团变化处理 */
|
||||
const handleRelatedClubChange = (club: ClubOption | null) => {
|
||||
console.log('活动表单 - handleRelatedClubChange:', club);
|
||||
form.value.relatedClub = club;
|
||||
form.value.relatedClubId = club?.relatedClubId;
|
||||
form.value.relatedType = club?.relatedType; // 修复:同时设置关联类型
|
||||
|
||||
console.log('活动表单 - 更新后的表单数据:', {
|
||||
relatedClub: form.value.relatedClub,
|
||||
relatedClubId: form.value.relatedClubId,
|
||||
relatedType: form.value.relatedType
|
||||
});
|
||||
};
|
||||
|
||||
/** 查询关联社团变化处理 */
|
||||
const handleQueryRelatedClubChange = (club: ClubOption | null) => {
|
||||
queryParams.value.relatedClub = club;
|
||||
queryParams.value.relatedClubId = club?.relatedClubId;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/** 承办方变化处理 */
|
||||
const handleCoOrganizersChange = (coOrganizers: CoOrganizerInfo[]) => {
|
||||
form.value.coOrganizerList = coOrganizers;
|
||||
@@ -1099,7 +1241,9 @@ const handleInstructorsChange = (instructors: InstructorInfo[]) => {
|
||||
|
||||
/** 指导老师变化处理 */
|
||||
const handleAdvisorsChange = (advisors: AdvisorVO[]) => {
|
||||
console.log('handleAdvisorsChange - 指导老师数据变化:', advisors);
|
||||
form.value.advisors = advisors;
|
||||
console.log('handleAdvisorsChange - 更新后的form.advisors:', form.value.advisors);
|
||||
};
|
||||
|
||||
/** 报名方式变化处理 */
|
||||
@@ -1122,6 +1266,13 @@ const shouldShowSummaryButton = (row: StglActivityVO): boolean => {
|
||||
return row.activityStatus === '4' || row.activityStatus === '5';
|
||||
};
|
||||
|
||||
/** 成员审核按钮操作 */
|
||||
const handleMembersReview = (row: StglActivityVO) => {
|
||||
selectedActivity.value = row;
|
||||
membersDialog.visible = true;
|
||||
membersDialog.title = `${row.activityName}活动报名管理`;
|
||||
};
|
||||
|
||||
/** 格式化日期 */
|
||||
const formatDate = (dateStr: string): string => {
|
||||
if (!dateStr) return '';
|
||||
@@ -1153,6 +1304,7 @@ const getAdvisorNames = (activity: StglActivityVO): string[] => {
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
initUrlParams();
|
||||
getList();
|
||||
});
|
||||
</script>
|
||||
@@ -1269,4 +1421,6 @@ onMounted(() => {
|
||||
border: 1px solid #dcdfe6;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
@@ -18,18 +18,10 @@
|
||||
<el-descriptions-item label="截止时间">
|
||||
{{ formatDate(task.deadline) || '未设置' }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="文件大小限制">
|
||||
{{ task.fileSizeLimit || 0 }} MB
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="文件数量限制">
|
||||
{{ task.fileCountLimit || 0 }} 个
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="总参与人数">
|
||||
{{ task.totalParticipants || 0 }} 人
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="已提交人数">
|
||||
{{ task.submittedCount || 0 }} 人
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="文件大小限制"> {{ task.fileSizeLimit || 0 }} MB </el-descriptions-item>
|
||||
<el-descriptions-item label="文件数量限制"> {{ task.fileCountLimit || 0 }} 个 </el-descriptions-item>
|
||||
<el-descriptions-item label="总参与人数"> {{ task.totalParticipants || 0 }} 人 </el-descriptions-item>
|
||||
<el-descriptions-item label="已提交人数"> {{ task.submittedCount || 0 }} 人 </el-descriptions-item>
|
||||
<el-descriptions-item label="创建者">
|
||||
{{ task.createByName || '未知' }}
|
||||
</el-descriptions-item>
|
||||
@@ -38,13 +30,7 @@
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="材料收集类型" :span="2">
|
||||
<div class="collection-types">
|
||||
<el-tag
|
||||
v-for="type in parseCollectionTypes(task.collectionTypes)"
|
||||
:key="type"
|
||||
type="info"
|
||||
size="small"
|
||||
class="mr-2"
|
||||
>
|
||||
<el-tag v-for="type in parseCollectionTypes(task.collectionTypes)" :key="type" type="info" size="small" class="mr-2">
|
||||
{{ getMaterialTypeLabel(type) }}
|
||||
</el-tag>
|
||||
</div>
|
||||
@@ -96,13 +82,7 @@
|
||||
</el-card>
|
||||
|
||||
<!-- 提交记录列表 -->
|
||||
<el-table
|
||||
v-loading="submissionsLoading"
|
||||
:data="submissions"
|
||||
border
|
||||
style="width: 100%"
|
||||
max-height="400px"
|
||||
>
|
||||
<el-table v-loading="submissionsLoading" :data="submissions" border style="width: 100%" max-height="400px">
|
||||
<el-table-column label="学号" prop="studentNo" width="120" />
|
||||
<el-table-column label="学生姓名" prop="studentName" width="100" />
|
||||
<el-table-column label="提交类型" width="100">
|
||||
@@ -120,10 +100,7 @@
|
||||
</el-table-column>
|
||||
<el-table-column label="审核状态" width="100">
|
||||
<template #default="scope">
|
||||
<el-tag
|
||||
:type="getReviewStatusType(scope.row.reviewStatus) as any"
|
||||
size="small"
|
||||
>
|
||||
<el-tag :type="getReviewStatusType(scope.row.reviewStatus) as any" size="small">
|
||||
{{ getReviewStatusText(scope.row.reviewStatus) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
@@ -140,14 +117,7 @@
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="120" fixed="right">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
size="small"
|
||||
@click="viewSubmissionDetail(scope.row)"
|
||||
>
|
||||
查看详情
|
||||
</el-button>
|
||||
<el-button link type="primary" size="small" @click="viewSubmissionDetail(scope.row)"> 查看详情 </el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
@@ -169,12 +139,7 @@
|
||||
</el-tabs>
|
||||
|
||||
<!-- 提交详情对话框 -->
|
||||
<el-dialog
|
||||
v-model="submissionDetailVisible"
|
||||
title="提交详情"
|
||||
width="600px"
|
||||
append-to-body
|
||||
>
|
||||
<el-dialog v-model="submissionDetailVisible" title="提交详情" width="600px" append-to-body>
|
||||
<SubmissionDetailView :submission="selectedSubmission" />
|
||||
</el-dialog>
|
||||
</div>
|
||||
@@ -192,9 +157,7 @@ interface Props {
|
||||
const props = defineProps<Props>();
|
||||
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
const { stgl_material_type } = toRefs<any>(
|
||||
proxy?.useDict('stgl_material_type')
|
||||
);
|
||||
const { stgl_material_type } = toRefs<any>(proxy?.useDict('stgl_material_type'));
|
||||
|
||||
const activeTab = ref('task');
|
||||
const submissionsLoading = ref(false);
|
||||
@@ -248,7 +211,7 @@ const viewSubmissionDetail = (submission: StglMaterialSubmissionVO) => {
|
||||
/** 解析收集类型 */
|
||||
const parseCollectionTypes = (types: string): string[] => {
|
||||
if (!types) return [];
|
||||
return types.split(',').filter(type => type.trim());
|
||||
return types.split(',').filter((type) => type.trim());
|
||||
};
|
||||
|
||||
/** 获取材料类型标签 */
|
||||
@@ -299,12 +262,16 @@ watch(activeTab, (newTab) => {
|
||||
});
|
||||
|
||||
/** 监听任务变化 */
|
||||
watch(() => props.task.id, (newId) => {
|
||||
watch(
|
||||
() => props.task.id,
|
||||
(newId) => {
|
||||
if (newId && activeTab.value === 'submissions') {
|
||||
getSubmissions();
|
||||
getStatistics();
|
||||
}
|
||||
}, { immediate: true });
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@@ -9,12 +9,7 @@
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="文本内容" prop="contentText">
|
||||
<el-input
|
||||
v-model="form.contentText"
|
||||
type="textarea"
|
||||
placeholder="请输入文本内容"
|
||||
:rows="4"
|
||||
/>
|
||||
<el-input v-model="form.contentText" type="textarea" placeholder="请输入文本内容" :rows="4" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="附件上传">
|
||||
|
||||
@@ -67,36 +67,16 @@
|
||||
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['stgl:activitymaterial:add']">新增</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
icon="Edit"
|
||||
:disabled="single"
|
||||
@click="handleUpdate()"
|
||||
v-hasPermi="['stgl:activitymaterial:edit']"
|
||||
>修改</el-button>
|
||||
<el-button icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['stgl:activitymaterial:edit']">修改</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
icon="Delete"
|
||||
:disabled="multiple"
|
||||
@click="handleDelete()"
|
||||
v-hasPermi="['stgl:activitymaterial:remove']"
|
||||
>删除</el-button>
|
||||
<el-button icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['stgl:activitymaterial:remove']">删除</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
icon="Open"
|
||||
:disabled="single"
|
||||
@click="handleStatusChange(1)"
|
||||
v-hasPermi="['stgl:activitymaterial:status']"
|
||||
>开启</el-button>
|
||||
<el-button icon="Open" :disabled="single" @click="handleStatusChange(1)" v-hasPermi="['stgl:activitymaterial:status']">开启</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
icon="TurnOff"
|
||||
:disabled="single"
|
||||
@click="handleStatusChange(0)"
|
||||
v-hasPermi="['stgl:activitymaterial:status']"
|
||||
>关闭</el-button>
|
||||
<el-button icon="TurnOff" :disabled="single" @click="handleStatusChange(0)" v-hasPermi="['stgl:activitymaterial:status']">关闭</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
@@ -105,7 +85,8 @@
|
||||
:disabled="single || !canUpload"
|
||||
@click="handleUpload()"
|
||||
v-hasPermi="['stgl:activitymaterial:upload']"
|
||||
>上传</el-button>
|
||||
>上传</el-button
|
||||
>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="success" icon="Download" @click="handleExport" v-hasPermi="['stgl:activitymaterial:export']">导出</el-button>
|
||||
@@ -213,18 +194,10 @@
|
||||
<ActivitySelector v-model="form.activityId" @change="handleActivityChange" />
|
||||
</el-form-item>
|
||||
<el-form-item label="任务名称" prop="taskName">
|
||||
<el-input
|
||||
v-model="form.taskName"
|
||||
placeholder="请输入任务名称"
|
||||
/>
|
||||
<el-input v-model="form.taskName" placeholder="请输入任务名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="收集说明" prop="taskDescription">
|
||||
<el-input
|
||||
v-model="form.taskDescription"
|
||||
type="textarea"
|
||||
placeholder="请输入收集说明"
|
||||
:rows="3"
|
||||
/>
|
||||
<el-input v-model="form.taskDescription" type="textarea" placeholder="请输入收集说明" :rows="3" />
|
||||
</el-form-item>
|
||||
<el-form-item label="截止时间" prop="deadline">
|
||||
<el-date-picker
|
||||
@@ -239,30 +212,13 @@
|
||||
<MaterialTypeSelector v-model="form.collectionTypes" />
|
||||
</el-form-item>
|
||||
<el-form-item label="文件大小要求(MB)" prop="fileSizeLimit">
|
||||
<el-input-number
|
||||
v-model="form.fileSizeLimit"
|
||||
:min="1"
|
||||
:max="1024"
|
||||
style="width: 100%"
|
||||
controls-position="right"
|
||||
/>
|
||||
<el-input-number v-model="form.fileSizeLimit" :min="1" :max="1024" style="width: 100%" controls-position="right" />
|
||||
</el-form-item>
|
||||
<el-form-item label="上传数量上限" prop="fileCountLimit">
|
||||
<el-input-number
|
||||
v-model="form.fileCountLimit"
|
||||
:min="1"
|
||||
:max="100"
|
||||
style="width: 100%"
|
||||
controls-position="right"
|
||||
/>
|
||||
<el-input-number v-model="form.fileCountLimit" :min="1" :max="100" style="width: 100%" controls-position="right" />
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input
|
||||
v-model="form.remark"
|
||||
type="textarea"
|
||||
placeholder="请输入备注"
|
||||
:rows="2"
|
||||
/>
|
||||
<el-input v-model="form.remark" type="textarea" placeholder="请输入备注" :rows="2" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
@@ -412,7 +368,7 @@ const resetQuery = () => {
|
||||
|
||||
/** 多选框选中数据 */
|
||||
const handleSelectionChange = (selection: StglMaterialTaskVO[]) => {
|
||||
ids.value = selection.map(item => item.id);
|
||||
ids.value = selection.map((item) => item.id);
|
||||
single.value = selection.length !== 1;
|
||||
multiple.value = !selection.length;
|
||||
};
|
||||
@@ -420,7 +376,7 @@ const handleSelectionChange = (selection: StglMaterialTaskVO[]) => {
|
||||
/** 计算是否可以上传 */
|
||||
const canUpload = computed(() => {
|
||||
if (single.value) return false;
|
||||
const selectedTask = taskList.value.find(item => ids.value.includes(item.id));
|
||||
const selectedTask = taskList.value.find((item) => ids.value.includes(item.id));
|
||||
return selectedTask?.taskStatus === 1;
|
||||
});
|
||||
|
||||
@@ -491,9 +447,7 @@ const handleStatusChange = async (status: number, row?: StglMaterialTaskVO) => {
|
||||
loading.value = true;
|
||||
try {
|
||||
// 处理单个或多个任务状态更新
|
||||
const promises = Array.isArray(taskIds)
|
||||
? taskIds.map(id => updateTaskStatus(id, status))
|
||||
: [updateTaskStatus(taskIds, status)];
|
||||
const promises = Array.isArray(taskIds) ? taskIds.map((id) => updateTaskStatus(id, status)) : [updateTaskStatus(taskIds, status)];
|
||||
await Promise.all(promises);
|
||||
await getList();
|
||||
proxy?.$modal.msgSuccess(`${statusText}成功`);
|
||||
@@ -517,7 +471,7 @@ const handleUpload = (row?: StglMaterialTaskVO) => {
|
||||
activityId = String(row.activityId);
|
||||
} else {
|
||||
// 如果没有传row,从选中的任务中获取activityId
|
||||
const selectedTask = taskList.value.find(item => item.id === ids.value[0]);
|
||||
const selectedTask = taskList.value.find((item) => item.id === ids.value[0]);
|
||||
activityId = selectedTask ? String(selectedTask.activityId) : '';
|
||||
}
|
||||
|
||||
|
||||
@@ -53,7 +53,15 @@
|
||||
<template #header>
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button type="info" plain icon="Check" :disabled="multiple" @click="handleBatchApprove()" v-hasPermi="['stgl:activityRegister:approve']">批量审核</el-button>
|
||||
<el-button
|
||||
type="info"
|
||||
plain
|
||||
icon="Check"
|
||||
:disabled="multiple"
|
||||
@click="handleBatchApprove()"
|
||||
v-hasPermi="['stgl:activityRegister:approve']"
|
||||
>批量审核</el-button
|
||||
>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['stgl:activityRegister:export']">导出</el-button>
|
||||
@@ -138,7 +146,6 @@
|
||||
></el-button>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
@@ -162,11 +169,15 @@
|
||||
<span v-else>-</span>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="报名时间">{{ parseTime(viewForm.registrationTime, '{y}-{m}-{d} {h}:{i}') }}</el-descriptions-item>
|
||||
<el-descriptions-item label="审核时间">{{ viewForm.approvalTime ? parseTime(viewForm.approvalTime, '{y}-{m}-{d} {h}:{i}') : '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="审核时间">{{
|
||||
viewForm.approvalTime ? parseTime(viewForm.approvalTime, '{y}-{m}-{d} {h}:{i}') : '-'
|
||||
}}</el-descriptions-item>
|
||||
<el-descriptions-item label="申请理由" :span="2">{{ viewForm.remark || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="审核意见" :span="2">{{ viewForm.approvalRemark || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="获得成绩">{{ viewForm.score || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="成绩录入时间">{{ viewForm.scoreTime ? parseTime(viewForm.scoreTime, '{y}-{m}-{d} {h}:{i}') : '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="成绩录入时间">{{
|
||||
viewForm.scoreTime ? parseTime(viewForm.scoreTime, '{y}-{m}-{d} {h}:{i}') : '-'
|
||||
}}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
@@ -181,20 +192,14 @@
|
||||
<!-- 显示状态选择 -->
|
||||
<el-form-item label="审核结果" prop="approvalStatus" v-if="approvalTitle === '审核' || approvalTitle === '批量审核'">
|
||||
<el-select v-model="approvalForm.approvalStatus" placeholder="请选择审核结果" style="width: 100%">
|
||||
<el-option
|
||||
:value="1"
|
||||
label="审核通过"
|
||||
>
|
||||
<span style="display: flex; align-items: center;">
|
||||
<el-option :value="1" label="审核通过">
|
||||
<span style="display: flex; align-items: center">
|
||||
<el-icon style="color: #67c23a; margin-right: 8px"><Check /></el-icon>
|
||||
审核通过
|
||||
</span>
|
||||
</el-option>
|
||||
<el-option
|
||||
:value="2"
|
||||
label="审核不通过"
|
||||
>
|
||||
<span style="display: flex; align-items: center;">
|
||||
<el-option :value="2" label="审核不通过">
|
||||
<span style="display: flex; align-items: center">
|
||||
<el-icon style="color: #f56c6c; margin-right: 8px"><Close /></el-icon>
|
||||
审核不通过
|
||||
</span>
|
||||
@@ -364,8 +369,6 @@ function handleApprove(row) {
|
||||
approvalOpen.value = true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** 批量审核操作 */
|
||||
function handleBatchApprove() {
|
||||
if (ids.value.length === 0) {
|
||||
@@ -389,16 +392,14 @@ function submitApproval() {
|
||||
if (valid) {
|
||||
if (approvalForm.id) {
|
||||
// 单个审核(包括重新审核)
|
||||
approveActivityRegister(approvalForm.id, approvalForm.approvalStatus, approvalForm.approvalRemark)
|
||||
.then(() => {
|
||||
approveActivityRegister(approvalForm.id, approvalForm.approvalStatus, approvalForm.approvalRemark).then(() => {
|
||||
proxy.$modal.msgSuccess('审核成功');
|
||||
approvalOpen.value = false;
|
||||
getList();
|
||||
});
|
||||
} else {
|
||||
// 批量审核
|
||||
batchApproveActivityRegister(ids.value, approvalForm.approvalStatus, approvalForm.approvalRemark)
|
||||
.then(() => {
|
||||
batchApproveActivityRegister(ids.value, approvalForm.approvalStatus, approvalForm.approvalRemark).then(() => {
|
||||
proxy.$modal.msgSuccess('批量审核成功');
|
||||
approvalOpen.value = false;
|
||||
getList();
|
||||
|
||||
@@ -124,7 +124,7 @@
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<i class="el-icon-s-data"></i>
|
||||
<span>成员:{{ club.memberCount || 0 }}{{ club.memberLimit ? '/' + club.memberLimit : '' }}人</span>
|
||||
<span>成员:{{ club.currentMemberCount || 0 }}{{ club.memberLimit ? '/' + club.memberLimit : '' }}人</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<i class="el-icon-time"></i>
|
||||
@@ -310,7 +310,7 @@
|
||||
<span class="date-value">{{ selectedClub.establishDate }}</span>
|
||||
</div>
|
||||
<div class="member-info">
|
||||
<span class="member-count">成员:{{ selectedClub.memberCount || 0 }}</span>
|
||||
<span class="member-count">成员:{{ selectedClub.currentMemberCount || 0 }}</span>
|
||||
<span v-if="selectedClub.memberLimit" class="member-limit">/ {{ selectedClub.memberLimit }}人</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -365,7 +365,7 @@
|
||||
<span class="info-item"><strong>姓名:</strong>{{ vp.name }}</span>
|
||||
<span v-if="vp.dept" class="info-item"><strong>院系:</strong>{{ vp.dept }}</span>
|
||||
<span v-if="vp.major" class="info-item"><strong>专业:</strong>{{ vp.major }}</span>
|
||||
<span v-if="vp.class" class="info-item"><strong>班级:</strong>{{ vp.class }}</span>
|
||||
<span v-if="vp.className" class="info-item"><strong>班级:</strong>{{ vp.className }}</span>
|
||||
<span v-if="vp.phone" class="info-item"><strong>联系方式:</strong>{{ vp.phone }}</span>
|
||||
<span v-if="vp.studentNo" class="info-item"><strong>学号:</strong>{{ vp.studentNo }}</span>
|
||||
</div>
|
||||
@@ -497,6 +497,7 @@ const queryParams = reactive<StglClubRegisterQuery>({
|
||||
clubName: undefined,
|
||||
clubType: undefined,
|
||||
canRegister: undefined,
|
||||
sighOpen: 'Y', // 只显示报名开启的社团
|
||||
params: {}
|
||||
});
|
||||
|
||||
@@ -572,14 +573,13 @@ const handleTabChange = (tabName: string) => {
|
||||
const handleViewDetail = (club: StglClubRegisterVO) => {
|
||||
// 🔥 调试信息:检查数据结构
|
||||
console.log('handleViewDetail: 社团数据详情:', club);
|
||||
console.log('handleViewDetail: advisorsJson字段:', club.advisorsJson);
|
||||
|
||||
selectedClub.value = club;
|
||||
|
||||
// 解析副社长JSON数据
|
||||
if (club.vicePresidentsJson) {
|
||||
// 解析副社长JSON数据(如果存在)
|
||||
if ((club as any).vicePresidentsJson) {
|
||||
try {
|
||||
const vicePresidents = JSON.parse(club.vicePresidentsJson);
|
||||
const vicePresidents = JSON.parse((club as any).vicePresidentsJson);
|
||||
(club as any).vicePresidents = vicePresidents;
|
||||
console.log('handleViewDetail: 解析的副社长数据:', vicePresidents);
|
||||
} catch (error) {
|
||||
@@ -588,7 +588,7 @@ const handleViewDetail = (club: StglClubRegisterVO) => {
|
||||
}
|
||||
}
|
||||
|
||||
// 解析指导老师JSON数据(如果有的话)
|
||||
// 解析指导老师JSON数据(如果存在)
|
||||
if ((club as any).advisorsJson) {
|
||||
try {
|
||||
const advisors = JSON.parse((club as any).advisorsJson);
|
||||
@@ -598,8 +598,6 @@ const handleViewDetail = (club: StglClubRegisterVO) => {
|
||||
console.warn('解析指导老师JSON数据失败:', error);
|
||||
(club as any).advisors = [];
|
||||
}
|
||||
} else {
|
||||
console.warn('handleViewDetail: 未找到advisorsJson字段');
|
||||
}
|
||||
|
||||
detailDialog.visible = true;
|
||||
@@ -667,7 +665,10 @@ const downloadCharter = async () => {
|
||||
charterDownloading.value = true;
|
||||
try {
|
||||
// 先获取OSS文件信息
|
||||
const ossResponse = await proxy?.$api.get(`/resource/oss/${charterUrl}`);
|
||||
const ossResponse = await proxy?.$http.request({
|
||||
url: `/resource/oss/${charterUrl}`,
|
||||
method: 'get'
|
||||
});
|
||||
if (ossResponse && ossResponse.code === 200) {
|
||||
const ossInfo = ossResponse.data;
|
||||
// 使用原始文件名,如果没有则使用社团名称 + 原始文件扩展名
|
||||
@@ -745,9 +746,9 @@ const getAdvisorNames = (row: any): string[] => {
|
||||
console.log('getAdvisorNames: 处理行数据:', row);
|
||||
|
||||
// 优先从JSON字段读取指导老师数据
|
||||
if (row.advisorsJson) {
|
||||
if ((row as any).advisorsJson) {
|
||||
try {
|
||||
const advisors = JSON.parse(row.advisorsJson);
|
||||
const advisors = JSON.parse((row as any).advisorsJson);
|
||||
console.log('getAdvisorNames: 从JSON字段解析的指导老师数据:', advisors);
|
||||
if (Array.isArray(advisors)) {
|
||||
const names = advisors.map((advisor: any) => advisor.name || advisor.teacher?.nickName || '').filter((name) => name);
|
||||
|
||||
@@ -96,13 +96,26 @@
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="成员数量" align="center" width="100">
|
||||
<template #default="scope">
|
||||
<span class="member-count">{{ scope.row.memberCount || 0 }}</span>
|
||||
<span v-if="scope.row.memberLimit" class="member-limit">/ {{ scope.row.memberLimit }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" align="center" prop="status" width="80">
|
||||
|
||||
<el-table-column label="报名开启" align="center" prop="sighOpen" width="90">
|
||||
<template #default="scope">
|
||||
<el-switch
|
||||
v-model="scope.row.sighOpen"
|
||||
active-value="Y"
|
||||
inactive-value="N"
|
||||
@change="handleSighOpenStatusChange(scope.row)"
|
||||
v-hasPermi="['stgl:club:edit']"
|
||||
></el-switch>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="社团状态" align="center" prop="status" width="90">
|
||||
<template #default="scope">
|
||||
<dict-tag :options="sys_normal_disable" :value="scope.row.status" />
|
||||
</template>
|
||||
@@ -112,7 +125,7 @@
|
||||
<div class="description-cell">{{ scope.row.description }}</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="240" fixed="right">
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="280" fixed="right">
|
||||
<template #default="scope">
|
||||
<el-tooltip content="查看" placement="top">
|
||||
<el-button
|
||||
@@ -136,6 +149,28 @@
|
||||
v-hasPermi="['stgl:club:edit']"
|
||||
></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="成员管理" placement="top">
|
||||
<el-button
|
||||
link
|
||||
type="info"
|
||||
icon="User"
|
||||
size="default"
|
||||
style="font-size: 16px; margin-right: 8px"
|
||||
@click="handleMemberManagement(scope.row)"
|
||||
v-hasPermi="['stgl:clubmember:list']"
|
||||
></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="活动管理" placement="top">
|
||||
<el-button
|
||||
link
|
||||
type="success"
|
||||
icon="Calendar"
|
||||
size="default"
|
||||
style="font-size: 16px; margin-right: 8px"
|
||||
@click="handleActivityManagement(scope.row)"
|
||||
v-hasPermi="['stgl:activity:list']"
|
||||
></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="转让" placement="top">
|
||||
<el-button
|
||||
link
|
||||
@@ -175,12 +210,12 @@
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="社团名称" prop="clubName">
|
||||
<el-input v-model="form.clubName" placeholder="请输入社团名称" />
|
||||
<el-input v-model="form.clubName" placeholder="请输入社团名称" :disabled="!hasInstructorPermission" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="社团类型" prop="clubType">
|
||||
<el-select v-model="form.clubType" placeholder="请选择社团类型" style="width: 100%">
|
||||
<el-select v-model="form.clubType" placeholder="请选择社团类型" style="width: 100%" :disabled="!hasInstructorPermission">
|
||||
<el-option v-for="dict in stgl_club_type" :key="dict.value" :label="dict.label" :value="dict.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
@@ -195,12 +230,13 @@
|
||||
placeholder="请选择成立日期"
|
||||
value-format="YYYY-MM-DD"
|
||||
style="width: 100%"
|
||||
:disabled="!hasInstructorPermission"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="成员规模上限" prop="memberLimit">
|
||||
<el-input-number v-model="form.memberLimit" :min="1" :max="9999" placeholder="请输入成员规模上限" style="width: 100%" />
|
||||
<el-form-item label="成员规模上限" prop="memberLimit" required>
|
||||
<el-input-number v-model="form.memberLimit" :min="1" :max="9999" placeholder="请输入成员规模上限" style="width: 100%" :disabled="!hasInstructorPermission" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
@@ -269,16 +305,12 @@
|
||||
</el-row>
|
||||
</div>
|
||||
|
||||
<!-- 副社长信息 -->
|
||||
<div class="personnel-section">
|
||||
<h4 class="section-title">副社长信息</h4>
|
||||
<VicePresidentManager :model-value="form.vicePresidents" @change="handleVicePresidentsChange" />
|
||||
</div>
|
||||
|
||||
|
||||
<!-- 指导老师信息 -->
|
||||
<div class="personnel-section">
|
||||
<h4 class="section-title">指导老师信息</h4>
|
||||
<AdvisorManager v-model="form.advisors" @change="handleAdvisorsChange" />
|
||||
<AdvisorManager v-model="form.advisors" @change="handleAdvisorsChange" :disabled="!hasInstructorPermission" />
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
|
||||
@@ -320,11 +352,22 @@
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="其他信息" name="other">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-radio-group v-model="form.status">
|
||||
<el-radio v-for="dict in sys_normal_disable" :key="dict.value" :value="dict.value">{{ dict.label }}</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="开启报名" prop="sighOpen">
|
||||
<el-radio-group v-model="form.sighOpen">
|
||||
<el-radio v-for="dict in sys_yes_no" :key="dict.value" :value="dict.value">{{ dict.label }}</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input v-model="form.remark" type="textarea" placeholder="请输入备注" resize="none" :rows="4" />
|
||||
</el-form-item>
|
||||
@@ -394,31 +437,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 副社长信息 -->
|
||||
<div class="position-group">
|
||||
<div class="position-title">副社长:</div>
|
||||
<div class="position-content">
|
||||
<template v-if="viewForm.vicePresidents && viewForm.vicePresidents.length > 0">
|
||||
<div v-for="(vp, index) in viewForm.vicePresidents" :key="index" class="vice-president-item">
|
||||
<span class="info-item"><strong>姓名:</strong>{{ vp.name }}</span>
|
||||
<span class="info-item"><strong>院系:</strong>{{ vp.dept || '暂无' }}</span>
|
||||
<span class="info-item"><strong>专业:</strong>{{ vp.major || '暂无' }}</span>
|
||||
<span class="info-item"><strong>班级:</strong>{{ vp.class || '暂无' }}</span>
|
||||
<span class="info-item"><strong>联系方式:</strong>{{ vp.phone || '暂无' }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else-if="viewForm.vicePresidentName">
|
||||
<span class="info-item"><strong>姓名:</strong>{{ viewForm.vicePresidentName }}</span>
|
||||
<span class="info-item"><strong>院系:</strong>{{ viewForm.vicePresidentDept || '暂无' }}</span>
|
||||
<span class="info-item"><strong>专业:</strong>{{ viewForm.vicePresidentMajor || '暂无' }}</span>
|
||||
<span class="info-item"><strong>班级:</strong>{{ viewForm.vicePresidentClass || '暂无' }}</span>
|
||||
<span class="info-item"><strong>联系方式:</strong>{{ viewForm.vicePresidentPhone || '暂无' }}</span>
|
||||
</template>
|
||||
<template v-else>
|
||||
<span class="no-data">暂无副社长信息</span>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- 指导老师信息 -->
|
||||
<div class="position-group">
|
||||
@@ -585,25 +604,51 @@
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 成员管理弹窗 -->
|
||||
<MemberManagementDialog
|
||||
v-model:visible="memberDialog.visible"
|
||||
:title="memberDialog.title"
|
||||
:club-id="currentClub?.id"
|
||||
:club-name="currentClub?.clubName"
|
||||
:member-limit="currentClub?.memberLimit"
|
||||
:current-member-count="currentClub?.memberCount"
|
||||
@member-count-changed="handleMemberCountChanged"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="StglClub" lang="ts">
|
||||
import { listStglClub, getStglClub, delStglClub, addStglClub, updateStglClub, transferClub, checkPresidentRole } from '@/api/stgl/club';
|
||||
import { useRouter } from 'vue-router';
|
||||
import {
|
||||
listStglClub,
|
||||
getStglClub,
|
||||
delStglClub,
|
||||
addStglClub,
|
||||
updateStglClub,
|
||||
transferClub,
|
||||
checkPresidentRole,
|
||||
changeSighOpenStatus
|
||||
} from '@/api/stgl/club';
|
||||
import { StglClubVO, StglClubQuery, StglClubForm } from '@/api/stgl/club/types';
|
||||
import StudentSelector from '@/components/StudentSelector/index.vue';
|
||||
import VicePresidentManager from '@/components/VicePresidentManager/index.vue';
|
||||
import AdvisorManager from '@/components/AdvisorManager/index.vue';
|
||||
import MemberManagementDialog from './components/MemberManagementDialog.vue';
|
||||
import { StudentVO } from '@/api/xg/student/types';
|
||||
import { VicePresidentVO } from '@/components/VicePresidentManager/index.vue';
|
||||
import { AdvisorVO } from '@/components/AdvisorManager/index.vue';
|
||||
import { Document } from '@element-plus/icons-vue';
|
||||
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
const { stgl_club_type, sys_normal_disable, stgl_funding_source } = toRefs<any>(
|
||||
proxy?.useDict('stgl_club_type', 'sys_normal_disable', 'stgl_funding_source')
|
||||
const router = useRouter();
|
||||
const { stgl_club_type, sys_normal_disable, stgl_funding_source, sys_yes_no } = toRefs<any>(
|
||||
proxy?.useDict('stgl_club_type', 'sys_normal_disable', 'stgl_funding_source', 'sys_yes_no')
|
||||
);
|
||||
|
||||
// 检查是否拥有指导老师或管理员权限
|
||||
const hasInstructorPermission = computed(() => {
|
||||
return proxy?.$auth.hasRoleOr(['stgl_instructor', 'stgl_admin', 'superadmin']);
|
||||
});
|
||||
|
||||
const clubList = ref<StglClubVO[]>([]);
|
||||
const buttonLoading = ref(false);
|
||||
const loading = ref(true);
|
||||
@@ -640,6 +685,15 @@ const transferDialog = reactive<DialogOption>({
|
||||
const transferLoading = ref(false);
|
||||
const transferFormRef = ref<ElFormInstance>();
|
||||
|
||||
// 成员管理相关
|
||||
const memberDialog = reactive<DialogOption>({
|
||||
visible: false,
|
||||
title: ''
|
||||
});
|
||||
|
||||
const currentClub = ref<StglClubVO | null>(null);
|
||||
const memberActiveTab = ref('joined');
|
||||
|
||||
interface TransferForm {
|
||||
clubId?: string | number;
|
||||
oldPresidentName?: string;
|
||||
@@ -710,6 +764,7 @@ const viewForm = reactive<StglClubForm>({
|
||||
fundingSources: [],
|
||||
charterUrl: undefined,
|
||||
status: '0',
|
||||
sighOpen: 'Y',
|
||||
remark: undefined
|
||||
});
|
||||
|
||||
@@ -729,9 +784,7 @@ const initFormData: StglClubForm = {
|
||||
presidentMajor: undefined,
|
||||
presidentClass: undefined,
|
||||
presidentPhone: undefined,
|
||||
vicePresidents: [],
|
||||
vicePresidentName: undefined,
|
||||
vicePresidentStudentNo: undefined,
|
||||
|
||||
advisors: [],
|
||||
advisorName: undefined,
|
||||
advisorDept: undefined,
|
||||
@@ -743,6 +796,7 @@ const initFormData: StglClubForm = {
|
||||
fundingSources: [],
|
||||
charterUrl: undefined,
|
||||
status: '0',
|
||||
sighOpen: 'N',
|
||||
remark: undefined
|
||||
};
|
||||
|
||||
@@ -763,6 +817,7 @@ const data = reactive<PageData<StglClubForm, StglClubQuery>>({
|
||||
presidentName: [{ required: true, message: '社长姓名不能为空', trigger: 'blur' }],
|
||||
presidentStudentNo: [{ required: true, message: '社长学号不能为空', trigger: 'blur' }],
|
||||
advisorName: [{ required: true, message: '指导老师姓名不能为空', trigger: 'blur' }],
|
||||
memberLimit: [{ required: true, message: '成员规模上限不能为空', trigger: 'blur' }],
|
||||
status: [{ required: true, message: '状态不能为空', trigger: 'change' }]
|
||||
}
|
||||
});
|
||||
@@ -791,6 +846,9 @@ const getList = async () => {
|
||||
if (item.status !== undefined && item.status !== null) {
|
||||
item.status = String(item.status);
|
||||
}
|
||||
if (item.sighOpen !== undefined && item.sighOpen !== null) {
|
||||
item.sighOpen = String(item.sighOpen);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -870,6 +928,9 @@ const handleUpdate = async (row?: StglClubVO) => {
|
||||
if (res.data.status !== undefined && res.data.status !== null) {
|
||||
(res.data as any).status = String(res.data.status);
|
||||
}
|
||||
if (res.data.sighOpen !== undefined && res.data.sighOpen !== null) {
|
||||
(res.data as any).sighOpen = String(res.data.sighOpen);
|
||||
}
|
||||
|
||||
// 处理社长信息:将简单字段转换为复杂对象结构
|
||||
if (res.data.presidentName) {
|
||||
@@ -896,39 +957,7 @@ const handleUpdate = async (row?: StglClubVO) => {
|
||||
vicePresidentPhone: res.data.vicePresidentPhone
|
||||
});
|
||||
|
||||
// 🔥 优先从JSON字段读取副社长数据
|
||||
if (res.data.vicePresidentsJson) {
|
||||
try {
|
||||
(res.data as any).vicePresidents = JSON.parse(res.data.vicePresidentsJson);
|
||||
console.log('handleUpdate: 从JSON字段读取的副社长数据:', (res.data as any).vicePresidents);
|
||||
} catch (error) {
|
||||
console.warn('解析副社长JSON数据失败:', error);
|
||||
(res.data as any).vicePresidents = [];
|
||||
}
|
||||
} else if (res.data.vicePresidentName) {
|
||||
// 回退到单一字段
|
||||
(res.data as any).vicePresidents = [
|
||||
{
|
||||
name: res.data.vicePresidentName,
|
||||
dept: res.data.vicePresidentDept,
|
||||
major: res.data.vicePresidentMajor,
|
||||
class: res.data.vicePresidentClass,
|
||||
phone: res.data.vicePresidentPhone,
|
||||
student: {
|
||||
studentNo: res.data.vicePresidentStudentNo,
|
||||
name: res.data.vicePresidentName,
|
||||
collegeName: res.data.vicePresidentDept,
|
||||
majorName: res.data.vicePresidentMajor,
|
||||
className: res.data.vicePresidentClass,
|
||||
phone: res.data.vicePresidentPhone
|
||||
}
|
||||
}
|
||||
];
|
||||
} else {
|
||||
(res.data as any).vicePresidents = [];
|
||||
}
|
||||
|
||||
console.log('handleUpdate: 处理后的副社长数据:', (res.data as any).vicePresidents);
|
||||
|
||||
// 处理指导老师信息:优先从JSON字段读取,如果没有则使用单一字段
|
||||
// 🔥 优先从JSON字段读取指导老师数据
|
||||
@@ -994,45 +1023,15 @@ const submitForm = () => {
|
||||
});
|
||||
}
|
||||
|
||||
// 确保副社长数据完整
|
||||
if (submitData.vicePresidents && Array.isArray(submitData.vicePresidents)) {
|
||||
submitData.vicePresidents = submitData.vicePresidents.map((vp: any) => {
|
||||
return {
|
||||
...vp,
|
||||
// 确保name等字段有值
|
||||
name: vp.name || vp.student?.name || '',
|
||||
dept: vp.dept || vp.student?.collegeName || '',
|
||||
major: vp.major || vp.student?.majorName || '',
|
||||
class: vp.class || vp.student?.className || '',
|
||||
phone: vp.phone || vp.student?.phone || ''
|
||||
};
|
||||
});
|
||||
|
||||
// 同步第一个副社长信息到旧字段(为了兼容后端)
|
||||
if (submitData.vicePresidents.length > 0) {
|
||||
const firstVP = submitData.vicePresidents[0];
|
||||
submitData.vicePresidentName = firstVP.name;
|
||||
submitData.vicePresidentStudentNo = firstVP.student?.studentNo;
|
||||
submitData.vicePresidentDept = firstVP.dept;
|
||||
submitData.vicePresidentMajor = firstVP.major;
|
||||
submitData.vicePresidentClass = firstVP.class;
|
||||
submitData.vicePresidentPhone = firstVP.phone;
|
||||
}
|
||||
}
|
||||
|
||||
// 🔥 新增:同时保存JSON字段和兼容性字段
|
||||
submitData.vicePresidentsJson = JSON.stringify(submitData.vicePresidents || []);
|
||||
submitData.advisorsJson = JSON.stringify(submitData.advisors || []);
|
||||
|
||||
// 调试日志:检查指导老师数据
|
||||
console.log('提交数据 - advisors:', submitData.advisors);
|
||||
console.log('提交数据 - advisorName:', submitData.advisorName);
|
||||
console.log('提交数据 - advisorDept:', submitData.advisorDept);
|
||||
// 调试日志:检查副社长数据
|
||||
console.log('提交数据 - vicePresidents:', submitData.vicePresidents);
|
||||
console.log('提交数据 - vicePresidentName:', submitData.vicePresidentName);
|
||||
console.log('提交数据 - vicePresidentStudentNo:', submitData.vicePresidentStudentNo);
|
||||
console.log('提交数据 - vicePresidentDept:', submitData.vicePresidentDept);
|
||||
console.log('完整提交数据:', submitData);
|
||||
|
||||
if (form.value.id) {
|
||||
@@ -1089,43 +1088,11 @@ const handleView = async (row: StglClubVO) => {
|
||||
if (viewData.status !== undefined && viewData.status !== null) {
|
||||
(viewData as any).status = String(viewData.status);
|
||||
}
|
||||
if (viewData.sighOpen !== undefined && viewData.sighOpen !== null) {
|
||||
(viewData as any).sighOpen = String(viewData.sighOpen);
|
||||
}
|
||||
|
||||
// 🔥 处理副社长信息:优先从JSON字段读取,如果没有则使用单一字段
|
||||
console.log('handleView: 副社长原始数据:', {
|
||||
vicePresidentsJson: viewData.vicePresidentsJson,
|
||||
vicePresidentName: viewData.vicePresidentName
|
||||
});
|
||||
|
||||
if (viewData.vicePresidentsJson) {
|
||||
try {
|
||||
(viewData as any).vicePresidents = JSON.parse(viewData.vicePresidentsJson);
|
||||
console.log('handleView: 从JSON字段读取的副社长数据:', (viewData as any).vicePresidents);
|
||||
} catch (error) {
|
||||
console.warn('解析副社长JSON数据失败:', error);
|
||||
(viewData as any).vicePresidents = [];
|
||||
}
|
||||
} else if (viewData.vicePresidentName) {
|
||||
// 回退到单一字段
|
||||
(viewData as any).vicePresidents = [
|
||||
{
|
||||
name: viewData.vicePresidentName,
|
||||
dept: viewData.vicePresidentDept,
|
||||
major: viewData.vicePresidentMajor,
|
||||
class: viewData.vicePresidentClass,
|
||||
phone: viewData.vicePresidentPhone,
|
||||
student: {
|
||||
studentNo: viewData.vicePresidentStudentNo,
|
||||
name: viewData.vicePresidentName,
|
||||
collegeName: viewData.vicePresidentDept,
|
||||
majorName: viewData.vicePresidentMajor,
|
||||
className: viewData.vicePresidentClass,
|
||||
phone: viewData.vicePresidentPhone
|
||||
}
|
||||
}
|
||||
];
|
||||
} else {
|
||||
(viewData as any).vicePresidents = [];
|
||||
}
|
||||
|
||||
// 🔥 处理指导老师信息:优先从JSON字段读取,如果没有则使用单一字段
|
||||
console.log('handleView: 指导老师原始数据:', {
|
||||
@@ -1162,7 +1129,6 @@ const handleView = async (row: StglClubVO) => {
|
||||
}
|
||||
|
||||
console.log('handleView: 处理后的数据:', {
|
||||
vicePresidents: (viewData as any).vicePresidents,
|
||||
advisors: (viewData as any).advisors
|
||||
});
|
||||
|
||||
@@ -1184,50 +1150,6 @@ const downloadCharter = async () => {
|
||||
}
|
||||
|
||||
charterDownloading.value = true;
|
||||
try {
|
||||
// 先获取OSS文件信息
|
||||
const ossResponse = await proxy?.$api.get(`/resource/oss/${viewForm.charterUrl}`);
|
||||
if (ossResponse && ossResponse.code === 200) {
|
||||
const ossInfo = ossResponse.data;
|
||||
// 使用原始文件名,如果没有则使用社团名称 + 原始文件扩展名
|
||||
let fileName = ossInfo.originalName;
|
||||
if (!fileName) {
|
||||
// 如果没有原始文件名,使用文件后缀和社团名称构造
|
||||
const fileSuffix = ossInfo.fileSuffix || '';
|
||||
fileName = `${viewForm.clubName || '社团'}_章程文档${fileSuffix}`;
|
||||
}
|
||||
|
||||
// 使用secureOss方法下载,传递正确的文件名
|
||||
proxy?.$download.secureOss(viewForm.charterUrl, fileName);
|
||||
} else {
|
||||
// 如果获取文件信息失败,尝试从URL中提取文件扩展名
|
||||
let fileExtension = '';
|
||||
try {
|
||||
// 从URL中提取文件扩展名
|
||||
const urlParts = viewForm.charterUrl.split('.');
|
||||
if (urlParts.length > 1) {
|
||||
const lastPart = urlParts[urlParts.length - 1];
|
||||
// 检查是否为常见的文件扩展名(去除可能的查询参数)
|
||||
const ext = lastPart.split('?')[0].toLowerCase();
|
||||
if (['doc', 'docx', 'pdf', 'txt', 'xls', 'xlsx', 'ppt', 'pptx'].includes(ext)) {
|
||||
fileExtension = '.' + ext;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('无法从URL提取扩展名:', e);
|
||||
}
|
||||
|
||||
// 如果没有提取到扩展名,默认使用.docx(因为大部分章程文档是Word格式)
|
||||
if (!fileExtension) {
|
||||
fileExtension = '.docx';
|
||||
}
|
||||
|
||||
const fileName = `${viewForm.clubName || '社团'}_章程文档${fileExtension}`;
|
||||
proxy?.$download.secureOss(viewForm.charterUrl, fileName);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('下载章程文档失败:', error);
|
||||
// 如果获取文件信息失败,尝试使用默认文件名下载
|
||||
try {
|
||||
// 尝试从URL中提取文件扩展名
|
||||
let fileExtension = '';
|
||||
@@ -1251,9 +1173,9 @@ const downloadCharter = async () => {
|
||||
|
||||
const fileName = `${viewForm.clubName || '社团'}_章程文档${fileExtension}`;
|
||||
proxy?.$download.secureOss(viewForm.charterUrl, fileName);
|
||||
} catch (downloadError) {
|
||||
} catch (error) {
|
||||
console.error('下载章程文档失败:', error);
|
||||
proxy?.$modal.msgError('下载章程文档失败,请稍后重试');
|
||||
}
|
||||
} finally {
|
||||
charterDownloading.value = false;
|
||||
}
|
||||
@@ -1324,36 +1246,7 @@ const handlePresidentChange = (student: StudentVO | null) => {
|
||||
});
|
||||
};
|
||||
|
||||
/** 副社长列表变化处理 */
|
||||
const handleVicePresidentsChange = (vicePresidents: VicePresidentVO[]) => {
|
||||
// 添加防护锁,避免递归更新
|
||||
if (syncLock.value) return;
|
||||
|
||||
syncLock.value = true;
|
||||
nextTick(() => {
|
||||
// 同步副社长数据到表单
|
||||
form.value.vicePresidents = vicePresidents;
|
||||
|
||||
// 为了兼容旧版本,同步第一个副社长的信息到旧字段
|
||||
if (vicePresidents.length > 0) {
|
||||
const firstVP = vicePresidents[0];
|
||||
form.value.vicePresidentName = firstVP.name;
|
||||
form.value.vicePresidentStudentNo = firstVP.student?.studentNo;
|
||||
form.value.vicePresidentDept = firstVP.student?.collegeName;
|
||||
form.value.vicePresidentMajor = firstVP.student?.majorName;
|
||||
form.value.vicePresidentClass = firstVP.student?.className;
|
||||
form.value.vicePresidentPhone = firstVP.student?.phone;
|
||||
} else {
|
||||
form.value.vicePresidentName = undefined;
|
||||
form.value.vicePresidentStudentNo = undefined;
|
||||
form.value.vicePresidentDept = undefined;
|
||||
form.value.vicePresidentMajor = undefined;
|
||||
form.value.vicePresidentClass = undefined;
|
||||
form.value.vicePresidentPhone = undefined;
|
||||
}
|
||||
syncLock.value = false;
|
||||
});
|
||||
};
|
||||
|
||||
// 防止递归更新的锁
|
||||
const syncLock = ref(false);
|
||||
@@ -1534,6 +1427,58 @@ const handleNewPresidentSelected = (student: StudentVO | null) => {
|
||||
}
|
||||
};
|
||||
|
||||
/** 报名开启状态修改 */
|
||||
const handleSighOpenStatusChange = async (row: StglClubVO) => {
|
||||
const text = row.sighOpen === 'Y' ? '开启' : '关闭';
|
||||
await proxy?.$modal
|
||||
.confirm('确认要"' + text + '""' + row.clubName + '"社团报名吗?')
|
||||
.then(async () => {
|
||||
await changeSighOpenStatus(row.id!, row.sighOpen!).then(() => {
|
||||
proxy?.$modal.msgSuccess(text + '成功');
|
||||
});
|
||||
})
|
||||
.catch(() => {
|
||||
row.sighOpen = row.sighOpen === 'Y' ? 'N' : 'Y';
|
||||
});
|
||||
};
|
||||
|
||||
/** 活动管理按钮操作 */
|
||||
const handleActivityManagement = (row: StglClubVO) => {
|
||||
router.push({
|
||||
path: '/stgl/activity/info',
|
||||
query: {
|
||||
relatedClubId: row.id,
|
||||
relatedClubName: row.clubName,
|
||||
from: 'club'
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/** 成员管理按钮操作 */
|
||||
const handleMemberManagement = (row: StglClubVO) => {
|
||||
console.log('handleMemberManagement: 打开成员管理弹窗, row:', row);
|
||||
currentClub.value = row;
|
||||
memberDialog.visible = true;
|
||||
memberDialog.title = `${row.clubName} - 成员管理`;
|
||||
memberActiveTab.value = 'joined';
|
||||
};
|
||||
|
||||
/** 处理成员数量变化 */
|
||||
const handleMemberCountChanged = (newCount: number) => {
|
||||
console.log('handleMemberCountChanged: 成员数量变化:', newCount, '当前社团:', currentClub.value);
|
||||
if (currentClub.value) {
|
||||
// 更新当前社团的成员数量
|
||||
currentClub.value.memberCount = newCount;
|
||||
|
||||
// 同时更新列表中的对应社团数据
|
||||
const clubIndex = clubList.value.findIndex(club => club.id === currentClub.value?.id);
|
||||
if (clubIndex !== -1) {
|
||||
clubList.value[clubIndex].memberCount = newCount;
|
||||
console.log('handleMemberCountChanged: 已更新列表中的成员数量:', clubList.value[clubIndex]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getList();
|
||||
});
|
||||
@@ -1590,6 +1535,8 @@ onMounted(() => {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.member-count {
|
||||
font-weight: bold;
|
||||
color: #409eff;
|
||||
@@ -1662,7 +1609,6 @@ onMounted(() => {
|
||||
}
|
||||
}
|
||||
|
||||
.vice-president-item,
|
||||
.advisor-item {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
||||
@@ -121,7 +121,7 @@
|
||||
<span v-else class="text-gray-400">暂无评分</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="180" fixed="right">
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="240" fixed="right">
|
||||
<template #default="scope">
|
||||
<el-tooltip content="查看详情" placement="top">
|
||||
<el-button
|
||||
@@ -133,6 +133,16 @@
|
||||
@click="handleView(scope.row)"
|
||||
></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="签到/签退" placement="top">
|
||||
<el-button
|
||||
link
|
||||
type="success"
|
||||
icon="Check"
|
||||
size="default"
|
||||
style="font-size: 16px; margin-right: 8px; color: green"
|
||||
@click="handleAttendance(scope.row)"
|
||||
></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="活动评分" placement="top">
|
||||
<el-button
|
||||
link
|
||||
@@ -267,14 +277,95 @@
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 签到对话框 -->
|
||||
<el-dialog title="活动签到" v-model="attendanceOpen" width="560px" append-to-body>
|
||||
<div class="attendance-content">
|
||||
<div class="attendance-actions">
|
||||
<el-button
|
||||
type="primary"
|
||||
size="large"
|
||||
:loading="signInLoading"
|
||||
:disabled="!canSignIn"
|
||||
@click="handleSignIn"
|
||||
class="action-btn"
|
||||
>
|
||||
{{ signInButtonText }}
|
||||
</el-button>
|
||||
|
||||
<el-button
|
||||
type="warning"
|
||||
size="large"
|
||||
:loading="signOutLoading"
|
||||
:disabled="!canSignOut"
|
||||
@click="handleSignOut"
|
||||
class="action-btn"
|
||||
>
|
||||
签 退
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<!-- 签到记录 -->
|
||||
<div class="attendance-records">
|
||||
<div class="records-header">
|
||||
<span class="records-title">签到记录</span>
|
||||
<el-button
|
||||
type="text"
|
||||
size="small"
|
||||
@click="refreshRecords"
|
||||
:loading="recordsLoading"
|
||||
>
|
||||
刷新
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<el-table
|
||||
:data="attendanceRecords"
|
||||
stripe
|
||||
size="small"
|
||||
class="records-table"
|
||||
v-loading="recordsLoading"
|
||||
>
|
||||
<el-table-column prop="signInTime" label="签到时间" width="200">
|
||||
<template #default="{ row }">
|
||||
{{ formatDateTime(row.signInTime) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="signOutTime" label="签退时间" width="200">
|
||||
<template #default="{ row }">
|
||||
{{ row.signOutTime ? formatDateTime(row.signOutTime) : '-' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column prop="status" label="状态" width="100">
|
||||
<template #default="{ row }">
|
||||
<el-tag
|
||||
:type="row.signOutTime ? 'success' : 'warning'"
|
||||
size="small"
|
||||
>
|
||||
{{ row.signOutTime ? '已完成' : '进行中' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<div v-if="attendanceRecords.length === 0" class="no-records">
|
||||
<el-empty description="暂无签到记录" :image-size="60" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="MyActivity" lang="ts">
|
||||
import { ref, reactive, toRefs, getCurrentInstance, ComponentInternalInstance, computed, nextTick } from 'vue';
|
||||
import { getMyActivities, rateActivity } from '@/api/stgl/my';
|
||||
import type { MyActivityVO, ActivityRatingForm, MyActivityQuery } from '@/api/stgl/my/types';
|
||||
import { Calendar } from '@element-plus/icons-vue';
|
||||
import { Calendar, Location, Clock, Check, Close } from '@element-plus/icons-vue';
|
||||
import { getInfo } from '@/api/login'; // 添加获取用户详细信息的API
|
||||
import request from '@/utils/request';
|
||||
import type { FormInstance } from 'element-plus';
|
||||
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
const { stgl_activity_type, stgl_activity_status, stgl_registration_status, stgl_my_registration_status } = toRefs<any>(
|
||||
@@ -294,7 +385,7 @@ const detailOpen = ref(false);
|
||||
const detailForm = ref<MyActivityVO>({} as MyActivityVO);
|
||||
const ratingOpen = ref(false);
|
||||
const ratingLoading = ref(false);
|
||||
const ratingFormRef = ref<ElFormInstance>();
|
||||
const ratingFormRef = ref<FormInstance>();
|
||||
|
||||
const ratingForm = ref<ActivityRatingForm & { activityName?: string; studentName?: string; studentNo?: string }>({
|
||||
activityId: '',
|
||||
@@ -429,6 +520,359 @@ const formatDate = (dateStr: string): string => {
|
||||
});
|
||||
};
|
||||
|
||||
// 签到相关API调用
|
||||
const createSignInRecord = async (signInData: any) => {
|
||||
try {
|
||||
const response = await request({
|
||||
url: '/stgl/activityAttendance/signIn',
|
||||
method: 'post',
|
||||
data: signInData
|
||||
});
|
||||
return response;
|
||||
} catch (error) {
|
||||
console.error('签到失败:', error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const updateSignOutRecord = async (signOutData: any) => {
|
||||
try {
|
||||
const response = await request({
|
||||
url: '/stgl/activityAttendance/signOut',
|
||||
method: 'post',
|
||||
data: signOutData
|
||||
});
|
||||
return response;
|
||||
} catch (error) {
|
||||
console.error('签退失败:', error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const getAllAttendanceRecords = async (activityId: number, studentNo: string) => {
|
||||
try {
|
||||
const response = await request({
|
||||
url: `/stgl/activityAttendance/getAllByActivityAndUser?activityId=${activityId}&studentNo=${studentNo}`,
|
||||
method: 'get'
|
||||
});
|
||||
return response;
|
||||
} catch (error) {
|
||||
console.error('获取所有签到记录失败:', error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
// 签到相关变量
|
||||
const attendanceOpen = ref(false);
|
||||
const attendanceForm = ref<any>({
|
||||
activityId: '',
|
||||
activityName: '',
|
||||
activityType: '',
|
||||
location: '',
|
||||
startTime: '',
|
||||
endTime: '',
|
||||
studentName: '',
|
||||
studentNo: '',
|
||||
department: '',
|
||||
major: '',
|
||||
className: '',
|
||||
phone: '',
|
||||
email: '',
|
||||
signInStatus: 0,
|
||||
signOutStatus: 0,
|
||||
signInTime: '',
|
||||
signOutTime: ''
|
||||
});
|
||||
|
||||
const signInLoading = ref(false);
|
||||
const signOutLoading = ref(false);
|
||||
const attendanceFormRef = ref<FormInstance>();
|
||||
|
||||
// 签到表单验证规则
|
||||
const attendanceRules = reactive({
|
||||
// 可以添加验证规则
|
||||
});
|
||||
|
||||
// 新增签到相关变量
|
||||
const attendanceHistory = ref<any[]>([]);
|
||||
const currentAttendance = ref<any>(null);
|
||||
|
||||
// 签到记录相关数据
|
||||
const attendanceRecords = ref<any[]>([])
|
||||
const recordsLoading = ref(false)
|
||||
|
||||
// 计算属性 - 简化状态判断
|
||||
const canSignIn = computed(() => {
|
||||
// 如果没有记录,或者最新记录已签到已签退,则可以签到
|
||||
if (!currentAttendance.value) {
|
||||
return true; // 没有记录时可以签到
|
||||
}
|
||||
|
||||
// 如果最新记录已签到已签退,则可以重新签到
|
||||
if (currentAttendance.value.signInStatus === 1 && currentAttendance.value.signOutStatus === 1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 如果最新记录已签到未签退,则不能签到
|
||||
if (currentAttendance.value.signInStatus === 1 && currentAttendance.value.signOutStatus === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 其他情况允许签到
|
||||
return true;
|
||||
});
|
||||
|
||||
const canSignOut = computed(() => {
|
||||
// 如果有记录且已签到未签退,则可以签退
|
||||
return currentAttendance.value &&
|
||||
currentAttendance.value.signInStatus === 1 &&
|
||||
currentAttendance.value.signOutStatus === 0;
|
||||
});
|
||||
|
||||
const signInButtonText = computed(() => {
|
||||
if (!currentAttendance.value) {
|
||||
return '签到';
|
||||
}
|
||||
|
||||
if (currentAttendance.value.signInStatus === 1 && currentAttendance.value.signOutStatus === 0) {
|
||||
return '已签到';
|
||||
}
|
||||
|
||||
return '签到';
|
||||
});
|
||||
|
||||
/** 打开签到对话框 */
|
||||
const handleAttendance = async (row: MyActivityVO) => {
|
||||
try {
|
||||
const userInfo = await getCurrentUserDetailInfo();
|
||||
|
||||
// 填充活动信息
|
||||
attendanceForm.value = {
|
||||
activityId: row.id,
|
||||
activityName: row.activityName,
|
||||
activityType: row.activityType,
|
||||
location: row.location,
|
||||
startTime: row.startTime,
|
||||
endTime: row.endTime,
|
||||
studentName: userInfo?.nickName || userInfo?.userName || '',
|
||||
studentNo: userInfo?.userName || '',
|
||||
department: userInfo?.deptName || '',
|
||||
major: '',
|
||||
className: '',
|
||||
phone: userInfo?.phonenumber || '',
|
||||
email: userInfo?.email || '',
|
||||
signInStatus: 0,
|
||||
signOutStatus: 0,
|
||||
signInTime: '',
|
||||
signOutTime: ''
|
||||
};
|
||||
|
||||
// 加载签到历史记录
|
||||
await refreshAttendanceData();
|
||||
|
||||
attendanceOpen.value = true;
|
||||
} catch (error) {
|
||||
console.error('获取用户信息失败:', error);
|
||||
proxy?.$modal.msgError('获取用户信息失败,请稍后重试');
|
||||
}
|
||||
};
|
||||
|
||||
/** 签到操作 */
|
||||
const handleSignIn = async () => {
|
||||
if (!attendanceForm.value.activityId) {
|
||||
proxy?.$modal.msgError('请先选择活动');
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查是否可以签到
|
||||
if (!canSignIn.value) {
|
||||
proxy?.$modal.msgError('当前状态不允许签到');
|
||||
return;
|
||||
}
|
||||
|
||||
signInLoading.value = true;
|
||||
try {
|
||||
const signInData = {
|
||||
activityId: attendanceForm.value.activityId,
|
||||
studentName: attendanceForm.value.studentName,
|
||||
studentNo: attendanceForm.value.studentNo,
|
||||
department: attendanceForm.value.department,
|
||||
major: attendanceForm.value.major,
|
||||
className: attendanceForm.value.className,
|
||||
phone: attendanceForm.value.phone,
|
||||
email: attendanceForm.value.email,
|
||||
signInLocation: '前端签到',
|
||||
signInDevice: navigator.userAgent.substring(0, 50)
|
||||
};
|
||||
|
||||
await createSignInRecord(signInData);
|
||||
|
||||
// 签到成功消息
|
||||
proxy?.$modal.msgSuccess('签到成功');
|
||||
|
||||
// 刷新签到状态和历史记录
|
||||
await refreshAttendanceData();
|
||||
|
||||
// 强制更新计算属性
|
||||
nextTick(() => {
|
||||
console.log('签到后状态更新:', {
|
||||
currentAttendance: currentAttendance.value,
|
||||
canSignIn: canSignIn.value,
|
||||
canSignOut: canSignOut.value,
|
||||
signInButtonText: signInButtonText.value
|
||||
});
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('签到失败:', error);
|
||||
proxy?.$modal.msgError('签到失败');
|
||||
} finally {
|
||||
signInLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
/** 签退操作 */
|
||||
const handleSignOut = async () => {
|
||||
if (!attendanceForm.value.activityId) {
|
||||
proxy?.$modal.msgError('请先选择活动');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!canSignOut.value) {
|
||||
proxy?.$modal.msgError('当前状态不允许签退');
|
||||
return;
|
||||
}
|
||||
|
||||
signOutLoading.value = true;
|
||||
try {
|
||||
const signOutData = {
|
||||
id: currentAttendance.value.id, // 使用当前记录的ID进行更新
|
||||
activityId: attendanceForm.value.activityId,
|
||||
studentNo: attendanceForm.value.studentNo,
|
||||
signOutLocation: '前端签退',
|
||||
signOutDevice: navigator.userAgent.substring(0, 50)
|
||||
};
|
||||
|
||||
await updateSignOutRecord(signOutData);
|
||||
proxy?.$modal.msgSuccess('签退成功');
|
||||
|
||||
// 刷新签到状态和历史记录
|
||||
await refreshAttendanceData();
|
||||
|
||||
// 强制更新计算属性
|
||||
nextTick(() => {
|
||||
console.log('签退后状态更新:', {
|
||||
currentAttendance: currentAttendance.value,
|
||||
canSignIn: canSignIn.value,
|
||||
canSignOut: canSignOut.value,
|
||||
signInButtonText: signInButtonText.value
|
||||
});
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('签退失败:', error);
|
||||
proxy?.$modal.msgError('签退失败');
|
||||
} finally {
|
||||
signOutLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
/** 刷新签到数据 */
|
||||
const refreshAttendanceData = async () => {
|
||||
try {
|
||||
// 获取最新的签到记录
|
||||
const recordRes = await getAllAttendanceRecords(attendanceForm.value.activityId, attendanceForm.value.studentNo);
|
||||
|
||||
if (recordRes.data && recordRes.data.length > 0) {
|
||||
// 按创建时间排序,取最新的一条作为当前记录
|
||||
const sortedRecords = recordRes.data.sort((a: any, b: any) =>
|
||||
new Date(b.createTime).getTime() - new Date(a.createTime).getTime()
|
||||
);
|
||||
|
||||
currentAttendance.value = sortedRecords[0];
|
||||
attendanceHistory.value = sortedRecords;
|
||||
|
||||
// 同时更新签到记录表格数据
|
||||
attendanceRecords.value = sortedRecords;
|
||||
|
||||
console.log('获取到签到记录:', {
|
||||
total: sortedRecords.length,
|
||||
current: currentAttendance.value,
|
||||
history: attendanceHistory.value
|
||||
});
|
||||
} else {
|
||||
currentAttendance.value = null;
|
||||
attendanceHistory.value = [];
|
||||
attendanceRecords.value = [];
|
||||
console.log('没有找到签到记录');
|
||||
}
|
||||
|
||||
// 强制触发计算属性重新计算
|
||||
await nextTick();
|
||||
|
||||
console.log('刷新签到数据完成:', {
|
||||
history: attendanceHistory.value,
|
||||
current: currentAttendance.value,
|
||||
canSignIn: canSignIn.value,
|
||||
canSignOut: canSignOut.value,
|
||||
signInButtonText: signInButtonText.value
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('刷新签到数据失败:', error);
|
||||
// 如果获取失败,重置状态
|
||||
currentAttendance.value = null;
|
||||
attendanceHistory.value = [];
|
||||
attendanceRecords.value = [];
|
||||
}
|
||||
};
|
||||
|
||||
/** 刷新签到记录 */
|
||||
const refreshRecords = async () => {
|
||||
if (!attendanceForm.value.activityId || !attendanceForm.value.studentNo) return;
|
||||
|
||||
recordsLoading.value = true;
|
||||
try {
|
||||
await refreshAttendanceData();
|
||||
proxy?.$modal.msgSuccess('签到记录已刷新');
|
||||
} catch (error) {
|
||||
console.error('刷新签到记录失败:', error);
|
||||
proxy?.$modal.msgError('刷新签到记录失败');
|
||||
} finally {
|
||||
recordsLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
/** 格式化日期时间 */
|
||||
const formatDateTime = (dateStr: string): string => {
|
||||
if (!dateStr) return '';
|
||||
const date = new Date(dateStr);
|
||||
return date.toLocaleString('zh-CN', {
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
second: '2-digit'
|
||||
});
|
||||
};
|
||||
|
||||
/** 计算参与时长 */
|
||||
const calculateDuration = (signInTime: string, signOutTime: string): string => {
|
||||
if (!signInTime) return '-';
|
||||
if (!signOutTime) return '进行中';
|
||||
|
||||
const start = new Date(signInTime);
|
||||
const end = new Date(signOutTime);
|
||||
const diffMs = end.getTime() - start.getTime();
|
||||
|
||||
const hours = Math.floor(diffMs / (1000 * 60 * 60));
|
||||
const minutes = Math.floor((diffMs % (1000 * 60 * 60)) / (1000 * 60));
|
||||
|
||||
if (hours > 0) {
|
||||
return `${hours}小时${minutes}分钟`;
|
||||
} else {
|
||||
return `${minutes}分钟`;
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getList();
|
||||
});
|
||||
@@ -521,4 +965,52 @@ onMounted(() => {
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
/* 签到弹窗样式 */
|
||||
.attendance-content {
|
||||
padding: 20px 0;
|
||||
}
|
||||
|
||||
.attendance-actions {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
min-width: 120px;
|
||||
height: 44px;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* 签到记录样式 */
|
||||
.attendance-records {
|
||||
margin-top: 24px;
|
||||
border-top: 1px solid #e4e7ed;
|
||||
padding-top: 20px;
|
||||
}
|
||||
|
||||
.records-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.records-title {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
.records-table {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.no-records {
|
||||
text-align: center;
|
||||
padding: 40px 0;
|
||||
color: #909399;
|
||||
}
|
||||
</style>
|
||||
@@ -12,18 +12,8 @@
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="社团类型" prop="clubType">
|
||||
<el-select
|
||||
v-model="queryParams.clubType"
|
||||
placeholder="请选择社团类型"
|
||||
clearable
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-option
|
||||
v-for="dict in stgl_club_type"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
<el-select v-model="queryParams.clubType" placeholder="请选择社团类型" clearable style="width: 100%">
|
||||
<el-option v-for="dict in stgl_club_type" :key="dict.value" :label="dict.label" :value="dict.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
@@ -7,12 +7,7 @@
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="6">
|
||||
<el-form-item label="报名类型" prop="registrationType">
|
||||
<el-select
|
||||
v-model="queryParams.registrationType"
|
||||
placeholder="请选择报名类型"
|
||||
clearable
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-select v-model="queryParams.registrationType" placeholder="请选择报名类型" clearable style="width: 100%">
|
||||
<el-option label="全部" value="" />
|
||||
<el-option label="社团" value="1" />
|
||||
<el-option label="志愿团队" value="2" />
|
||||
@@ -22,12 +17,7 @@
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="审核状态" prop="approvalStatus">
|
||||
<el-select
|
||||
v-model="queryParams.approvalStatus"
|
||||
placeholder="请选择审核状态"
|
||||
clearable
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-select v-model="queryParams.approvalStatus" placeholder="请选择审核状态" clearable style="width: 100%">
|
||||
<el-option label="全部" value="" />
|
||||
<el-option label="待审核" value="待审核" />
|
||||
<el-option label="已通过" value="已通过" />
|
||||
@@ -65,10 +55,7 @@
|
||||
<el-table-column label="序号" type="index" align="center" width="60" />
|
||||
<el-table-column label="报名类型" align="center" prop="registrationType" width="100">
|
||||
<template #default="scope">
|
||||
<el-tag
|
||||
:type="getRegistrationTypeTag(scope.row.registrationType)"
|
||||
size="small"
|
||||
>
|
||||
<el-tag :type="getRegistrationTypeTag(scope.row.registrationType)" size="small">
|
||||
{{ getRegistrationTypeText(scope.row.registrationType) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
@@ -84,20 +71,14 @@
|
||||
</el-table-column>
|
||||
<el-table-column label="审核状态" align="center" prop="approvalStatus" width="100">
|
||||
<template #default="scope">
|
||||
<el-tag
|
||||
:type="getApprovalStatusTag(scope.row.approvalStatus)"
|
||||
size="small"
|
||||
>
|
||||
<el-tag :type="getApprovalStatusTag(scope.row.approvalStatus)" size="small">
|
||||
{{ scope.row.approvalStatus }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="当前状态" align="center" prop="currentStatus" width="100">
|
||||
<template #default="scope">
|
||||
<el-tag
|
||||
:type="getCurrentStatusTag(scope.row.currentStatus)"
|
||||
size="small"
|
||||
>
|
||||
<el-tag :type="getCurrentStatusTag(scope.row.currentStatus)" size="small">
|
||||
{{ scope.row.currentStatus }}
|
||||
</el-tag>
|
||||
</template>
|
||||
@@ -132,23 +113,14 @@
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination
|
||||
v-show="total > 0"
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNum"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
|
||||
</el-card>
|
||||
|
||||
<!-- 查看详情对话框 -->
|
||||
<el-dialog title="报名记录详情" v-model="detailOpen" width="800px" append-to-body>
|
||||
<el-descriptions :column="2" border>
|
||||
<el-descriptions-item label="报名类型">
|
||||
<el-tag
|
||||
:type="getRegistrationTypeTag(detailForm.registrationType)"
|
||||
size="small"
|
||||
>
|
||||
<el-tag :type="getRegistrationTypeTag(detailForm.registrationType)" size="small">
|
||||
{{ getRegistrationTypeText(detailForm.registrationType) }}
|
||||
</el-tag>
|
||||
</el-descriptions-item>
|
||||
@@ -158,18 +130,12 @@
|
||||
<el-descriptions-item label="组织者">{{ detailForm.organizer || '暂无' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="报名时间">{{ parseTime(detailForm.registrationTime, '{y}-{m}-{d} {h}:{i}') }}</el-descriptions-item>
|
||||
<el-descriptions-item label="审核状态">
|
||||
<el-tag
|
||||
:type="getApprovalStatusTag(detailForm.approvalStatus)"
|
||||
size="small"
|
||||
>
|
||||
<el-tag :type="getApprovalStatusTag(detailForm.approvalStatus)" size="small">
|
||||
{{ detailForm.approvalStatus }}
|
||||
</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="当前状态">
|
||||
<el-tag
|
||||
:type="getCurrentStatusTag(detailForm.currentStatus)"
|
||||
size="small"
|
||||
>
|
||||
<el-tag :type="getCurrentStatusTag(detailForm.currentStatus)" size="small">
|
||||
{{ detailForm.currentStatus }}
|
||||
</el-tag>
|
||||
</el-descriptions-item>
|
||||
@@ -267,7 +233,7 @@ const getRegistrationTypeTag = (type: string) => {
|
||||
'2': 'success' as const,
|
||||
'3': 'warning' as const
|
||||
};
|
||||
return tagMap[type as keyof typeof tagMap] || 'info' as const;
|
||||
return tagMap[type as keyof typeof tagMap] || ('info' as const);
|
||||
};
|
||||
|
||||
/** 获取审核状态标签样式 */
|
||||
@@ -277,7 +243,7 @@ const getApprovalStatusTag = (status: string) => {
|
||||
'已拒绝': 'danger' as const,
|
||||
'待审核': 'warning' as const
|
||||
};
|
||||
return tagMap[status as keyof typeof tagMap] || 'info' as const;
|
||||
return tagMap[status as keyof typeof tagMap] || ('info' as const);
|
||||
};
|
||||
|
||||
/** 获取当前状态标签样式 */
|
||||
@@ -287,7 +253,7 @@ const getCurrentStatusTag = (status: string) => {
|
||||
'已报名': 'primary' as const,
|
||||
'待审核': 'warning' as const
|
||||
};
|
||||
return tagMap[status as keyof typeof tagMap] || 'info' as const;
|
||||
return tagMap[status as keyof typeof tagMap] || ('info' as const);
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
<el-option label="已满员" value="full" />
|
||||
<el-option label="已申请" value="applied" />
|
||||
<el-option label="已加入" value="joined" />
|
||||
<el-option label="报名关闭" value="closed" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
@@ -114,7 +115,10 @@
|
||||
<!-- 3. 已满员 - 红色 -->
|
||||
<el-tag v-else-if="calculateRegisterStatus(scope.row) === 'full'" type="danger" size="small">已满员</el-tag>
|
||||
|
||||
<!-- 4. 可报名 - 绿色(默认状态) -->
|
||||
<!-- 4. 报名关闭 - 灰色 -->
|
||||
<el-tag v-else-if="calculateRegisterStatus(scope.row) === 'closed'" type="info" size="small">报名关闭</el-tag>
|
||||
|
||||
<!-- 5. 可报名 - 绿色(默认状态) -->
|
||||
<el-tag v-else type="success" size="small">可报名</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
@@ -141,6 +145,11 @@
|
||||
<el-button link type="danger" icon="Close" disabled />
|
||||
</el-tooltip>
|
||||
|
||||
<!-- 报名关闭状态 - 灰色禁用按钮 -->
|
||||
<el-tooltip content="报名已关闭" placement="top" v-else-if="calculateRegisterStatus(scope.row) === 'closed'">
|
||||
<el-button link type="info" icon="Lock" disabled />
|
||||
</el-tooltip>
|
||||
|
||||
<!-- 查看详情按钮 - 始终显示 -->
|
||||
<el-tooltip content="查看详情" placement="top">
|
||||
<el-button link type="primary" icon="View" @click="handleViewDetail(scope.row)" />
|
||||
@@ -171,6 +180,7 @@
|
||||
<el-tag v-if="calculateRegisterStatus(detailForm) === 'joined'" type="primary">已加入队伍</el-tag>
|
||||
<el-tag v-else-if="calculateRegisterStatus(detailForm) === 'applied'" type="warning">申请审核中</el-tag>
|
||||
<el-tag v-else-if="calculateRegisterStatus(detailForm) === 'full'" type="danger">队伍已满员</el-tag>
|
||||
<el-tag v-else-if="calculateRegisterStatus(detailForm) === 'closed'" type="info">报名已关闭</el-tag>
|
||||
<el-tag v-else type="success">可申请报名</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="剩余名额">
|
||||
@@ -362,6 +372,7 @@ interface TeamInfo {
|
||||
advisorEmail?: string;
|
||||
email?: string;
|
||||
status?: number | string; // 队伍状态(sys_normal_disable字典:0-正常 1-停用)
|
||||
sighOpen?: string; // 报名开启状态(Y-开启 N-关闭)
|
||||
|
||||
// 用户申请状态字段(后端已支持)
|
||||
hasApplied?: boolean; // 是否已申请
|
||||
@@ -461,6 +472,11 @@ function calculateRegisterStatus(row: TeamInfo): string {
|
||||
return 'full'; // 已满员
|
||||
}
|
||||
|
||||
// 优先级3:报名开关检查
|
||||
if (row.sighOpen === 'N') {
|
||||
return 'closed'; // 报名已关闭
|
||||
}
|
||||
|
||||
return 'available'; // 可报名
|
||||
}
|
||||
|
||||
@@ -584,11 +600,13 @@ function submitApply() {
|
||||
(proxy!.$refs['applyRef'] as any).validate((valid: boolean) => {
|
||||
if (valid) {
|
||||
applyForm.value.teamId = selectedTeam.value.id;
|
||||
applyJoinVolunteerTeam(applyForm.value).then((response) => {
|
||||
applyJoinVolunteerTeam(applyForm.value)
|
||||
.then((response) => {
|
||||
proxy!.$modal.msgSuccess('申请提交成功,请等待审核');
|
||||
applyOpen.value = false;
|
||||
getList(); // 刷新列表
|
||||
}).catch((error) => {
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('申请提交失败:', error);
|
||||
// 显示具体的错误信息
|
||||
const errorMsg = error?.response?.data?.msg || error?.message || '申请提交失败,请稍后重试';
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
type="warning"
|
||||
icon="Switch"
|
||||
:disabled="single || isTransferDisabled(teamList.find((t) => ids.includes(t.id)))"
|
||||
@click="handleTransfer()"
|
||||
@click="handleTransfer"
|
||||
v-hasPermi="['stgl:volunteerTeam:transfer']"
|
||||
>转让</el-button
|
||||
>
|
||||
@@ -78,6 +78,17 @@
|
||||
<span v-if="scope.row.memberLimit" class="member-limit">/ {{ scope.row.memberLimit }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="报名开启" align="center" prop="sighOpen" width="90">
|
||||
<template #default="scope">
|
||||
<el-switch
|
||||
v-model="scope.row.sighOpen"
|
||||
active-value="Y"
|
||||
inactive-value="N"
|
||||
@change="handleSighOpenStatusChange(scope.row)"
|
||||
v-hasPermi="['stgl:volunteerTeam:edit']"
|
||||
></el-switch>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" align="center" prop="status" width="80">
|
||||
<template #default="scope">
|
||||
<dict-tag :options="sys_normal_disable" :value="scope.row.status" />
|
||||
@@ -91,7 +102,7 @@
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="240" fixed="right">
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="380" fixed="right">
|
||||
<template #default="scope">
|
||||
<el-tooltip content="查看" placement="top">
|
||||
<el-button
|
||||
@@ -115,6 +126,28 @@
|
||||
v-hasPermi="['stgl:volunteerTeam:edit']"
|
||||
></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="成员管理" placement="top">
|
||||
<el-button
|
||||
link
|
||||
type="info"
|
||||
icon="User"
|
||||
size="default"
|
||||
style="font-size: 16px; margin-right: 8px"
|
||||
@click="handleMemberManagement(scope.row)"
|
||||
v-hasPermi="['stgl:volunteermember:list']"
|
||||
></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="活动管理" placement="top">
|
||||
<el-button
|
||||
link
|
||||
type="success"
|
||||
icon="Calendar"
|
||||
size="default"
|
||||
style="font-size: 16px; margin-right: 8px"
|
||||
@click="handleActivityManagement(scope.row)"
|
||||
v-hasPermi="['stgl:activity:list']"
|
||||
></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="转让" placement="top">
|
||||
<el-button
|
||||
link
|
||||
@@ -197,7 +230,7 @@
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="24">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-radio-group v-model="form.status">
|
||||
<el-radio v-for="dict in sys_normal_disable" :key="dict.value" :label="dict.value">
|
||||
@@ -206,6 +239,15 @@
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="开启报名" prop="sighOpen">
|
||||
<el-radio-group v-model="form.sighOpen">
|
||||
<el-radio v-for="dict in sys_yes_no" :key="dict.value" :label="dict.value">
|
||||
{{ dict.label }}
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</el-tab-pane>
|
||||
@@ -296,6 +338,9 @@
|
||||
<el-descriptions-item label="状态">
|
||||
<dict-tag :options="sys_normal_disable" :value="detailForm.status" />
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="报名开启">
|
||||
<dict-tag :options="sys_yes_no" :value="detailForm.sighOpen" />
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="联系电话">{{ detailForm.phone }}</el-descriptions-item>
|
||||
<el-descriptions-item label="邮箱">{{ detailForm.email }}</el-descriptions-item>
|
||||
<el-descriptions-item label="队伍简介" :span="2">{{ detailForm.description }}</el-descriptions-item>
|
||||
@@ -413,6 +458,16 @@
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 成员管理弹窗 -->
|
||||
<MemberManagementDialog
|
||||
v-model:visible="memberDialog.visible"
|
||||
:title="memberDialog.title"
|
||||
:team-id="currentTeam?.id"
|
||||
:team-name="currentTeam?.teamName"
|
||||
:member-limit="currentTeam?.memberLimit"
|
||||
:current-member-count="currentTeam?.currentMemberCount"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -429,14 +484,18 @@ import {
|
||||
searchTeacherByName,
|
||||
getTeacherById,
|
||||
checkTeamLeader,
|
||||
transferVolunteerTeam
|
||||
transferVolunteerTeam,
|
||||
changeSighOpenStatus
|
||||
} from '@/api/stgl/volunteerteam';
|
||||
import StudentSelector from '@/components/StudentSelector/index.vue';
|
||||
import TeacherSelector from '@/components/TeacherSelector/index.vue';
|
||||
import { onMounted, getCurrentInstance, ref, reactive, watch, toRefs } from 'vue';
|
||||
import MemberManagementDialog from './components/MemberManagementDialog.vue';
|
||||
import { onMounted, getCurrentInstance, ref, reactive, watch, toRefs, nextTick } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
const { sys_normal_disable } = toRefs(proxy.useDict('sys_normal_disable'));
|
||||
const { sys_normal_disable, sys_yes_no } = toRefs(proxy.useDict('sys_normal_disable', 'sys_yes_no'));
|
||||
const router = useRouter();
|
||||
|
||||
const teamList = ref([]);
|
||||
const open = ref(false);
|
||||
@@ -480,6 +539,14 @@ const transferForm = reactive({
|
||||
newLeaderPhone: ''
|
||||
});
|
||||
|
||||
// 成员管理相关
|
||||
const memberDialog = reactive({
|
||||
visible: false,
|
||||
title: ''
|
||||
});
|
||||
|
||||
const currentTeam = ref(null);
|
||||
|
||||
const transferRules = {
|
||||
newLeaderStudent: [{ required: true, message: '请选择新队长', trigger: 'change' }]
|
||||
};
|
||||
@@ -520,7 +587,7 @@ const checkIsTeamLeader = async (teamId) => {
|
||||
const result = response.data === true;
|
||||
console.log(`✅ 队长检查结果: ${result} (response.data: ${response.data}, type: ${typeof response.data})`);
|
||||
return result;
|
||||
} catch (error) {
|
||||
} catch (error: any) {
|
||||
console.error('❌ 检查队长权限失败:', error);
|
||||
console.error('❌ 错误详情:', {
|
||||
message: error.message,
|
||||
@@ -703,6 +770,7 @@ const data = reactive({
|
||||
advisorEmail: null,
|
||||
advisorUserId: null,
|
||||
status: '0',
|
||||
sighOpen: 'Y',
|
||||
remark: null
|
||||
} as any,
|
||||
detailForm: {} as any,
|
||||
@@ -747,6 +815,9 @@ async function getList() {
|
||||
if (item.status !== undefined && item.status !== null) {
|
||||
item.status = String(item.status);
|
||||
}
|
||||
if (item.sighOpen !== undefined && item.sighOpen !== null) {
|
||||
item.sighOpen = String(item.sighOpen);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -793,6 +864,7 @@ function reset() {
|
||||
advisorEmail: null,
|
||||
advisorUserId: null,
|
||||
status: '0',
|
||||
sighOpen: 'Y',
|
||||
remark: null
|
||||
};
|
||||
|
||||
@@ -861,7 +933,9 @@ function handleUpdate(row) {
|
||||
// 指导老师信息字段映射
|
||||
advisorDepartment: data.advisorDept, // advisorDept -> advisorDepartment
|
||||
// 状态字段类型转换:确保与字典组件匹配(Integer -> String)
|
||||
status: data.status !== undefined && data.status !== null ? String(data.status) : '0'
|
||||
status: data.status !== undefined && data.status !== null ? String(data.status) : '0',
|
||||
// 报名开关字段类型转换:确保与字典组件匹配
|
||||
sighOpen: data.sighOpen !== undefined && data.sighOpen !== null ? String(data.sighOpen) : 'Y'
|
||||
};
|
||||
|
||||
// 设置队长选择器的值 - 先尝试用延迟设置的方式
|
||||
@@ -1109,6 +1183,48 @@ const getAdvisorNames = (row: any): string[] => {
|
||||
return [];
|
||||
};
|
||||
|
||||
/** 成员管理按钮操作 */
|
||||
const handleMemberManagement = (row) => {
|
||||
console.log('handleMemberManagement: 打开成员管理弹窗, row:', row);
|
||||
currentTeam.value = row;
|
||||
memberDialog.visible = true;
|
||||
memberDialog.title = `${row.teamName} - 成员管理`;
|
||||
};
|
||||
|
||||
/** 活动管理按钮操作 */
|
||||
const handleActivityManagement = (row) => {
|
||||
console.log('handleActivityManagement: 跳转到活动管理页面, row:', row);
|
||||
console.log('handleActivityManagement: 传递参数:', {
|
||||
relatedTeamId: row.id,
|
||||
relatedTeamName: row.teamName,
|
||||
from: 'team'
|
||||
});
|
||||
|
||||
router.push({
|
||||
path: '/stgl/activity/info',
|
||||
query: {
|
||||
relatedTeamId: row.id,
|
||||
relatedTeamName: row.teamName,
|
||||
from: 'team'
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/** 报名开启状态修改 */
|
||||
const handleSighOpenStatusChange = async (row) => {
|
||||
const text = row.sighOpen === 'Y' ? '开启' : '关闭';
|
||||
await proxy?.$modal
|
||||
.confirm('确认要"' + text + '""' + row.teamName + '"志愿者团队报名吗?')
|
||||
.then(async () => {
|
||||
await changeSighOpenStatus(row.id, row.sighOpen).then(() => {
|
||||
proxy?.$modal.msgSuccess(text + '成功');
|
||||
});
|
||||
})
|
||||
.catch(() => {
|
||||
row.sighOpen = row.sighOpen === 'Y' ? 'N' : 'Y';
|
||||
});
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getList();
|
||||
});
|
||||
@@ -1172,4 +1288,11 @@ onMounted(() => {
|
||||
min-width: 200px !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* 操作列自适应样式 - 只确保不换行 */
|
||||
:deep(.el-table) {
|
||||
.el-table__body-wrapper .el-table__body td:has(.el-button) {
|
||||
white-space: nowrap !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -180,7 +180,7 @@ const { evaluation_status, sys_user_sex } = toRefs<any>(proxy?.useDict('evaluati
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const evaluationId = ref(route.params.id as string);
|
||||
const evaluationId = ref(route.query.id as string);
|
||||
|
||||
const loading = ref(false);
|
||||
const statisticsLoading = ref(false);
|
||||
@@ -219,7 +219,7 @@ let pieChart: echarts.ECharts | null = null;
|
||||
const queryParams = ref<XljkEvaluationDetailQuery>({
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
evaluationId: Number(evaluationId.value),
|
||||
evaluationId: Number(evaluationId.value) || 0,
|
||||
mentalStatus: '',
|
||||
studentName: '',
|
||||
studentNo: '',
|
||||
@@ -260,7 +260,15 @@ const getStatistics = async () => {
|
||||
const getDetailList = async () => {
|
||||
try {
|
||||
loading.value = true;
|
||||
|
||||
// 确保有有效的evaluationId
|
||||
if (!evaluationId.value) {
|
||||
console.error('evaluationId为空,无法查询详情');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('查询参数:', {
|
||||
evaluationId: evaluationId.value,
|
||||
filterStatus: filterStatus.value,
|
||||
searchKeyword: searchKeyword.value,
|
||||
studentNo: queryParams.value.studentNo,
|
||||
@@ -441,6 +449,19 @@ const resetFilter = () => {
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
console.log('详情页面路由参数:', {
|
||||
params: route.params,
|
||||
query: route.query,
|
||||
evaluationId: evaluationId.value
|
||||
});
|
||||
|
||||
// 验证ID是否存在
|
||||
if (!evaluationId.value) {
|
||||
proxy?.$modal.msgError('缺少测评ID参数');
|
||||
router.push('/xljk/evaluation');
|
||||
return;
|
||||
}
|
||||
|
||||
getEvaluationInfo();
|
||||
getStatistics();
|
||||
getDetailList();
|
||||
|
||||
@@ -134,13 +134,15 @@ import {
|
||||
import { XljkEvaluationForm, StudentInfo, XljkEvaluationDetailForm } from '@/api/xljk/evaluation/types';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import { useTagsViewStore } from '@/store/modules/tagsView';
|
||||
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
const { sys_user_sex } = toRefs<any>(proxy?.useDict('sys_user_sex'));
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const evaluationId = ref(route.params.id as string);
|
||||
const tagsViewStore = useTagsViewStore();
|
||||
const evaluationId = ref(route.query.id as string);
|
||||
const formRef = ref<ElFormInstance>();
|
||||
const submitLoading = ref(false);
|
||||
const studentsLoading = ref(false);
|
||||
@@ -166,6 +168,13 @@ const rules = {
|
||||
evaluationName: [{ required: true, message: '测评名称不能为空', trigger: 'blur' }]
|
||||
};
|
||||
|
||||
// 心理状态字典映射
|
||||
const mentalStatusMap: Record<string, string> = {
|
||||
'1': '健康',
|
||||
'2': '良好',
|
||||
'3': '重点关注'
|
||||
};
|
||||
|
||||
// 学生列表
|
||||
const studentList = ref<StudentInfo[]>([]);
|
||||
// 测评明细列表
|
||||
@@ -273,7 +282,7 @@ const applyBatchMentalStatus = async () => {
|
||||
} as XljkEvaluationDetailForm;
|
||||
});
|
||||
await batchUpdateXljkMentalStatus(detailsToUpdate);
|
||||
ElMessage.success(`已批量设置 ${selectedStudents.value.length} 名学生的心理状态为 ${status}`);
|
||||
ElMessage.success(`已批量设置 ${selectedStudents.value.length} 名学生的心理状态为 ${mentalStatusMap[status]}`);
|
||||
// 刷新学生列表以更新状态
|
||||
await loadStudentList();
|
||||
await getEvaluationDetails();
|
||||
@@ -297,7 +306,7 @@ const applyBatchMentalStatusToAll = async () => {
|
||||
} as XljkEvaluationDetailForm;
|
||||
});
|
||||
await batchUpdateXljkMentalStatus(detailsToUpdate);
|
||||
ElMessage.success(`已批量设置 ${studentList.value.length} 名学生的心理状态为 ${status}`);
|
||||
ElMessage.success(`已批量设置 ${studentList.value.length} 名学生的心理状态为 ${mentalStatusMap[status]}`);
|
||||
// 刷新学生列表以更新状态
|
||||
await loadStudentList();
|
||||
await getEvaluationDetails();
|
||||
@@ -366,7 +375,11 @@ const submitForm = () => {
|
||||
await batchUpdateXljkMentalStatus(detailsToUpdate);
|
||||
|
||||
ElMessage.success('修改成功');
|
||||
router.replace('/xljk/evaluation');
|
||||
// 关闭当前标签页并跳转到列表页面
|
||||
setTimeout(async () => {
|
||||
await tagsViewStore.delView(route);
|
||||
router.push('/xljk/evaluation');
|
||||
}, 1000);
|
||||
} catch (error) {
|
||||
console.error('修改测评失败', error);
|
||||
} finally {
|
||||
@@ -377,11 +390,21 @@ const submitForm = () => {
|
||||
};
|
||||
|
||||
/** 取消按钮 */
|
||||
const cancel = () => {
|
||||
router.replace('/xljk/evaluation');
|
||||
const cancel = async () => {
|
||||
await tagsViewStore.delView(route);
|
||||
router.push('/xljk/evaluation');
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
console.log('Edit页面路由参数:', route.query);
|
||||
console.log('获取到的测评ID:', evaluationId.value);
|
||||
|
||||
if (!evaluationId.value) {
|
||||
proxy?.$modal.msgError('缺少测评ID参数');
|
||||
router.push('/xljk/evaluation');
|
||||
return;
|
||||
}
|
||||
|
||||
getEvaluationInfo();
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -343,18 +343,35 @@ const handleSelectionChange = (selection: XljkEvaluationVO[]) => {
|
||||
|
||||
/** 新增按钮操作 */
|
||||
const handleAdd = async () => {
|
||||
proxy?.$router.push({ path: '/xljk/evaluation/add' });
|
||||
proxy.$router.push({
|
||||
path: '/xljk/evaluation/add',
|
||||
query: {
|
||||
type: 'add'
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/** 查看按钮操作 */
|
||||
const handleView = (row: XljkEvaluationVO) => {
|
||||
proxy?.$router.push({ path: `/xljk/evaluation/detail/${row.id}` });
|
||||
proxy.$router.push({
|
||||
path: `/xljk/evaluation/detail`,
|
||||
query: {
|
||||
id: row.id,
|
||||
type: 'detail'
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/** 修改按钮操作 */
|
||||
const handleUpdate = (row?: XljkEvaluationVO) => {
|
||||
const id = row?.id || ids.value[0];
|
||||
proxy?.$router.push({ path: `/xljk/evaluation/edit/${id}` });
|
||||
proxy.$router.push({
|
||||
path: `/xljk/evaluation/edit`,
|
||||
query: {
|
||||
id: row.id,
|
||||
type: 'edit'
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/** 删除按钮操作 */
|
||||
|
||||
Reference in new issue
Block a user