年审
This commit is contained in:
1 parent
bb2a04ffb9
commit
de3ed43b6d
63 files changed
+8724
-328
No files matched your search
+147
@@ -0,0 +1,147 @@
|
||||
package org.dromara.stgl.controller;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.validation.constraints.*;
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.dromara.common.idempotent.annotation.RepeatSubmit;
|
||||
import org.dromara.common.log.annotation.Log;
|
||||
import org.dromara.common.web.core.BaseController;
|
||||
import org.dromara.common.mybatis.core.page.PageQuery;
|
||||
import org.dromara.common.core.domain.R;
|
||||
import org.dromara.common.core.validate.AddGroup;
|
||||
import org.dromara.common.core.validate.EditGroup;
|
||||
import org.dromara.common.log.enums.BusinessType;
|
||||
import org.dromara.common.excel.utils.ExcelUtil;
|
||||
import org.dromara.stgl.domain.vo.StglClubAdvisorHistoryVo;
|
||||
import org.dromara.stgl.domain.bo.StglClubAdvisorHistoryBo;
|
||||
import org.dromara.stgl.service.IStglClubAdvisorHistoryService;
|
||||
import org.dromara.common.mybatis.core.page.TableDataInfo;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.Parameters;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
|
||||
/**
|
||||
* 社团指导老师任职历史管理
|
||||
*
|
||||
* @author Cursor AI
|
||||
* @date 2025-01-03
|
||||
*/
|
||||
@Validated
|
||||
@RequiredArgsConstructor
|
||||
@RestController
|
||||
@RequestMapping("/stgl/club/advisor/history")
|
||||
@Tag(name = "社团指导老师任职历史管理", description = "社团指导老师任职历史管理")
|
||||
public class StglClubAdvisorHistoryController extends BaseController {
|
||||
|
||||
private final IStglClubAdvisorHistoryService stglClubAdvisorHistoryService;
|
||||
|
||||
/**
|
||||
* 查询社团指导老师任职历史列表
|
||||
*/
|
||||
@Operation(summary = "查询社团指导老师任职历史列表")
|
||||
@SaCheckPermission("stgl:club:query")
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo<StglClubAdvisorHistoryVo> list(StglClubAdvisorHistoryBo bo, PageQuery pageQuery) {
|
||||
return stglClubAdvisorHistoryService.queryPageList(bo, pageQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出社团指导老师任职历史列表
|
||||
*/
|
||||
@Operation(summary = "导出社团指导老师任职历史列表")
|
||||
@SaCheckPermission("stgl:club:export")
|
||||
@Log(title = "社团指导老师任职历史", businessType = BusinessType.EXPORT)
|
||||
@PostMapping("/export")
|
||||
public void export(StglClubAdvisorHistoryBo bo, HttpServletResponse response) {
|
||||
List<StglClubAdvisorHistoryVo> list = stglClubAdvisorHistoryService.queryList(bo);
|
||||
ExcelUtil.exportExcel(list, "社团指导老师任职历史", StglClubAdvisorHistoryVo.class, response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取社团指导老师任职历史详细信息
|
||||
*/
|
||||
@Operation(summary = "获取社团指导老师任职历史详细信息")
|
||||
@SaCheckPermission("stgl:club:query")
|
||||
@GetMapping("/{id}")
|
||||
public R<StglClubAdvisorHistoryVo> getInfo(@NotNull(message = "主键不能为空") @PathVariable Long id) {
|
||||
return R.ok(stglClubAdvisorHistoryService.queryById(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询指定社团的当前在任指导老师
|
||||
*/
|
||||
@Operation(summary = "查询指定社团的当前在任指导老师")
|
||||
@SaCheckPermission("stgl:club:query")
|
||||
@GetMapping("/current/{clubId}")
|
||||
@Parameter(name = "clubId", description = "社团ID", required = true)
|
||||
public R<List<StglClubAdvisorHistoryVo>> getCurrentAdvisors(@PathVariable Long clubId) {
|
||||
List<StglClubAdvisorHistoryVo> list = stglClubAdvisorHistoryService.getCurrentAdvisorsByClubId(clubId);
|
||||
return R.ok(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询指定社团的历史指导老师(已离任)
|
||||
*/
|
||||
@Operation(summary = "查询指定社团的历史指导老师")
|
||||
@SaCheckPermission("stgl:club:query")
|
||||
@GetMapping("/history/{clubId}")
|
||||
@Parameter(name = "clubId", description = "社团ID", required = true)
|
||||
public R<List<StglClubAdvisorHistoryVo>> getHistoryAdvisors(@PathVariable Long clubId) {
|
||||
List<StglClubAdvisorHistoryVo> list = stglClubAdvisorHistoryService.getHistoryAdvisorsByClubId(clubId);
|
||||
return R.ok(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询指定社团的所有指导老师(包含当前和历史)
|
||||
*/
|
||||
@Operation(summary = "查询指定社团的所有指导老师")
|
||||
@SaCheckPermission("stgl:club:query")
|
||||
@GetMapping("/all/{clubId}")
|
||||
@Parameter(name = "clubId", description = "社团ID", required = true)
|
||||
public R<List<StglClubAdvisorHistoryVo>> getAllAdvisors(@PathVariable Long clubId) {
|
||||
List<StglClubAdvisorHistoryVo> list = stglClubAdvisorHistoryService.getAllAdvisorsByClubId(clubId);
|
||||
return R.ok(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增社团指导老师任职历史
|
||||
*/
|
||||
@Operation(summary = "新增社团指导老师任职历史")
|
||||
@SaCheckPermission("stgl:club:add")
|
||||
@Log(title = "社团指导老师任职历史", businessType = BusinessType.INSERT)
|
||||
@RepeatSubmit()
|
||||
@PostMapping()
|
||||
public R<Void> add(@Validated(AddGroup.class) @RequestBody StglClubAdvisorHistoryBo bo) {
|
||||
return toAjax(stglClubAdvisorHistoryService.insertByBo(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改社团指导老师任职历史
|
||||
*/
|
||||
@Operation(summary = "修改社团指导老师任职历史")
|
||||
@SaCheckPermission("stgl:club:edit")
|
||||
@Log(title = "社团指导老师任职历史", businessType = BusinessType.UPDATE)
|
||||
@RepeatSubmit()
|
||||
@PutMapping()
|
||||
public R<Void> edit(@Validated(EditGroup.class) @RequestBody StglClubAdvisorHistoryBo bo) {
|
||||
return toAjax(stglClubAdvisorHistoryService.updateByBo(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除社团指导老师任职历史
|
||||
*/
|
||||
@Operation(summary = "删除社团指导老师任职历史")
|
||||
@SaCheckPermission("stgl:club:remove")
|
||||
@Log(title = "社团指导老师任职历史", businessType = BusinessType.DELETE)
|
||||
@DeleteMapping("/{ids}")
|
||||
public R<Void> remove(@NotEmpty(message = "主键不能为空") @PathVariable Long[] ids) {
|
||||
return toAjax(stglClubAdvisorHistoryService.deleteWithValidByIds(List.of(ids), true));
|
||||
}
|
||||
|
||||
}
|
||||
+213
@@ -0,0 +1,213 @@
|
||||
package org.dromara.stgl.controller;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.validation.constraints.*;
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.dromara.common.idempotent.annotation.RepeatSubmit;
|
||||
import org.dromara.common.log.annotation.Log;
|
||||
import org.dromara.common.web.core.BaseController;
|
||||
import org.dromara.common.mybatis.core.page.PageQuery;
|
||||
import org.dromara.common.core.domain.R;
|
||||
import org.dromara.common.core.validate.AddGroup;
|
||||
import org.dromara.common.core.validate.EditGroup;
|
||||
import org.dromara.common.log.enums.BusinessType;
|
||||
import org.dromara.common.excel.utils.ExcelUtil;
|
||||
import org.dromara.stgl.domain.vo.StglClubAnnualReviewVo;
|
||||
import org.dromara.stgl.domain.bo.StglClubAnnualReviewBo;
|
||||
import org.dromara.stgl.service.IStglClubAnnualReviewService;
|
||||
import org.dromara.common.mybatis.core.page.TableDataInfo;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.Parameters;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
|
||||
/**
|
||||
* 社团年审记录管理
|
||||
*
|
||||
* @author Cursor AI
|
||||
* @date 2025-01-15
|
||||
*/
|
||||
@Validated
|
||||
@RequiredArgsConstructor
|
||||
@RestController
|
||||
@RequestMapping("/stgl/club/annualReview")
|
||||
@Tag(name = "社团年审记录管理", description = "社团年审记录管理")
|
||||
public class StglClubAnnualReviewController extends BaseController {
|
||||
|
||||
private final IStglClubAnnualReviewService stglClubAnnualReviewService;
|
||||
|
||||
/**
|
||||
* 查询社团年审记录列表
|
||||
*/
|
||||
@Operation(summary = "查询社团年审记录列表")
|
||||
@SaCheckPermission("stgl:clubAnnualReview:list")
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo<StglClubAnnualReviewVo> list(StglClubAnnualReviewBo bo, PageQuery pageQuery) {
|
||||
return stglClubAnnualReviewService.queryPageList(bo, pageQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询我的社团年审记录列表
|
||||
*/
|
||||
@Operation(summary = "查询我的社团年审记录列表")
|
||||
@SaCheckPermission("stgl:clubAnnualReview:myList")
|
||||
@GetMapping("/myList")
|
||||
public TableDataInfo<StglClubAnnualReviewVo> myList(StglClubAnnualReviewBo bo, PageQuery pageQuery) {
|
||||
return stglClubAnnualReviewService.queryMyPageList(bo, pageQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出社团年审记录列表
|
||||
*/
|
||||
@Operation(summary = "导出社团年审记录列表")
|
||||
@SaCheckPermission("stgl:clubAnnualReview:export")
|
||||
@Log(title = "社团年审记录", businessType = BusinessType.EXPORT)
|
||||
@PostMapping("/export")
|
||||
public void export(StglClubAnnualReviewBo bo, HttpServletResponse response) {
|
||||
List<StglClubAnnualReviewVo> list = stglClubAnnualReviewService.queryList(bo);
|
||||
ExcelUtil.exportExcel(list, "社团年审记录", StglClubAnnualReviewVo.class, response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取社团年审记录详细信息
|
||||
*/
|
||||
@Operation(summary = "获取社团年审记录详细信息")
|
||||
@SaCheckPermission("stgl:clubAnnualReview:query")
|
||||
@GetMapping("/{id}")
|
||||
public R<StglClubAnnualReviewVo> getInfo(@NotNull(message = "主键不能为空")
|
||||
@PathVariable Long id) {
|
||||
return R.ok(stglClubAnnualReviewService.queryById(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增社团年审记录
|
||||
*/
|
||||
@Operation(summary = "新增社团年审记录")
|
||||
@SaCheckPermission("stgl:clubAnnualReview:add")
|
||||
@Log(title = "社团年审记录", businessType = BusinessType.INSERT)
|
||||
@RepeatSubmit()
|
||||
@PostMapping()
|
||||
public R<Void> add(@Validated(AddGroup.class) @RequestBody StglClubAnnualReviewBo bo) {
|
||||
return toAjax(stglClubAnnualReviewService.insertByBo(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改社团年审记录
|
||||
*/
|
||||
@Operation(summary = "修改社团年审记录")
|
||||
@SaCheckPermission("stgl:clubAnnualReview:edit")
|
||||
@Log(title = "社团年审记录", businessType = BusinessType.UPDATE)
|
||||
@RepeatSubmit()
|
||||
@PutMapping()
|
||||
public R<Void> edit(@Validated(EditGroup.class) @RequestBody StglClubAnnualReviewBo bo) {
|
||||
return toAjax(stglClubAnnualReviewService.updateByBo(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除社团年审记录
|
||||
*/
|
||||
@Operation(summary = "删除社团年审记录")
|
||||
@SaCheckPermission("stgl:clubAnnualReview:remove")
|
||||
@Log(title = "社团年审记录", businessType = BusinessType.DELETE)
|
||||
@DeleteMapping("/{ids}")
|
||||
public R<Void> remove(@NotEmpty(message = "主键不能为空")
|
||||
@PathVariable Long[] ids) {
|
||||
return toAjax(stglClubAnnualReviewService.deleteWithValidByIds(List.of(ids), true));
|
||||
}
|
||||
|
||||
/**
|
||||
* 提交年审记录
|
||||
*/
|
||||
@Operation(summary = "提交年审记录")
|
||||
@SaCheckPermission("stgl:clubAnnualReview:submit")
|
||||
@Log(title = "社团年审记录", businessType = BusinessType.UPDATE)
|
||||
@RepeatSubmit()
|
||||
@PostMapping("/submit/{id}")
|
||||
public R<Void> submit(@NotNull(message = "主键不能为空")
|
||||
@PathVariable Long id) {
|
||||
return toAjax(stglClubAnnualReviewService.submitReview(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* 撤回年审记录
|
||||
*/
|
||||
@Operation(summary = "撤回年审记录")
|
||||
@SaCheckPermission("stgl:clubAnnualReview:withdraw")
|
||||
@Log(title = "社团年审记录", businessType = BusinessType.UPDATE)
|
||||
@RepeatSubmit()
|
||||
@PostMapping("/withdraw/{id}")
|
||||
public R<Void> withdraw(@NotNull(message = "主键不能为空")
|
||||
@PathVariable Long id) {
|
||||
return toAjax(stglClubAnnualReviewService.withdrawReview(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* 审核年审记录
|
||||
*/
|
||||
@Operation(summary = "审核年审记录")
|
||||
@SaCheckPermission("stgl:clubAnnualReview:audit")
|
||||
@Log(title = "社团年审记录", businessType = BusinessType.UPDATE)
|
||||
@RepeatSubmit()
|
||||
@PostMapping("/audit")
|
||||
public R<Void> audit(@RequestParam @NotNull(message = "年审记录ID不能为空") Long id,
|
||||
@RequestParam @NotBlank(message = "审核结果不能为空") String auditResult,
|
||||
@RequestParam String auditOpinion,
|
||||
@RequestParam @NotBlank(message = "审核类型不能为空") String auditType) {
|
||||
return toAjax(stglClubAnnualReviewService.auditReview(id, auditResult, auditOpinion, auditType));
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否可以创建年审记录
|
||||
*/
|
||||
@Operation(summary = "检查是否可以创建年审记录")
|
||||
@SaCheckPermission("stgl:clubAnnualReview:check")
|
||||
@GetMapping("/check")
|
||||
public R<Boolean> checkCanCreate(@RequestParam @NotNull(message = "社团ID不能为空") Long clubId,
|
||||
@RequestParam @NotNull(message = "年审年度不能为空") Integer reviewYear) {
|
||||
return R.ok(stglClubAnnualReviewService.checkCanCreateReview(clubId, reviewYear));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取年审状态选项
|
||||
*/
|
||||
@Operation(summary = "获取年审状态选项")
|
||||
@GetMapping("/statusOptions")
|
||||
public R<List<String>> getStatusOptions() {
|
||||
List<String> options = List.of("0", "1", "2", "3", "4");
|
||||
return R.ok(options);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据社团ID和年审年度获取该年度的活动数据
|
||||
*/
|
||||
@Operation(summary = "根据社团ID和年审年度获取该年度的活动数据")
|
||||
@Parameters({
|
||||
@Parameter(name = "clubId", description = "社团ID", required = true),
|
||||
@Parameter(name = "reviewYear", description = "年审年度", required = true)
|
||||
})
|
||||
@SaCheckPermission("stgl:clubAnnualReview:query")
|
||||
@GetMapping("/clubActivities")
|
||||
public R<List<org.dromara.stgl.domain.vo.StglActivityVo>> getClubActivities(
|
||||
@RequestParam @NotNull(message = "社团ID不能为空") Long clubId,
|
||||
@RequestParam @NotNull(message = "年审年度不能为空") Integer reviewYear) {
|
||||
return R.ok(stglClubAnnualReviewService.getClubActivitiesByReviewYear(clubId, reviewYear));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据年审记录ID获取对应的活动数据
|
||||
*/
|
||||
@Operation(summary = "根据年审记录ID获取对应的活动数据")
|
||||
@Parameter(name = "id", description = "年审记录ID", required = true)
|
||||
@SaCheckPermission("stgl:clubAnnualReview:query")
|
||||
@GetMapping("/{id}/activities")
|
||||
public R<List<org.dromara.stgl.domain.vo.StglClubAnnualReviewActivityVo>> getAnnualReviewActivities(
|
||||
@NotNull(message = "年审记录ID不能为空") @PathVariable Long id) {
|
||||
return R.ok(stglClubAnnualReviewService.getAnnualReviewActivities(id));
|
||||
}
|
||||
|
||||
}
|
||||
+200
@@ -0,0 +1,200 @@
|
||||
package org.dromara.stgl.controller;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.common.core.domain.R;
|
||||
import org.dromara.common.log.annotation.Log;
|
||||
import org.dromara.common.log.enums.BusinessType;
|
||||
import org.dromara.common.web.core.BaseController;
|
||||
import org.dromara.stgl.domain.vo.ClubActivityPermissionResult;
|
||||
import org.dromara.stgl.service.IStglClubPermissionService;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 社团权限管理Controller
|
||||
*
|
||||
* @author Cursor AI
|
||||
* @date 2025-01-15
|
||||
*/
|
||||
@Slf4j
|
||||
@Validated
|
||||
@RequiredArgsConstructor
|
||||
@RestController
|
||||
@RequestMapping("/stgl/club/permission")
|
||||
public class StglClubPermissionController extends BaseController {
|
||||
|
||||
private final IStglClubPermissionService clubPermissionService;
|
||||
|
||||
/**
|
||||
* 检查社团活动权限
|
||||
* 前端每次进入活动管理页面都会调用这个接口
|
||||
*
|
||||
* @param clubId 社团ID
|
||||
* @return 权限检查结果
|
||||
*/
|
||||
@SaCheckPermission("stgl:club:list")
|
||||
@GetMapping("/checkActivityPermission/{clubId}")
|
||||
@Log(title = "检查社团活动权限", businessType = BusinessType.OTHER)
|
||||
public R<ClubActivityPermissionResult> checkActivityPermission(@PathVariable Long clubId) {
|
||||
log.info("检查社团活动权限请求,clubId: {}", clubId);
|
||||
|
||||
if (clubId == null || clubId <= 0) {
|
||||
return R.fail("社团ID不能为空");
|
||||
}
|
||||
|
||||
try {
|
||||
ClubActivityPermissionResult result = clubPermissionService.checkClubActivityPermission(clubId);
|
||||
log.info("社团ID: {} 权限检查结果: {}", clubId, result.getStatus());
|
||||
return R.ok(result);
|
||||
} catch (Exception e) {
|
||||
log.error("检查社团活动权限失败,clubId: {}", clubId, e);
|
||||
return R.fail("检查权限失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量检查多个社团的权限状态
|
||||
* 用于列表页面显示多个社团的权限状态
|
||||
*
|
||||
* @param clubIds 社团ID列表
|
||||
* @return 权限检查结果映射
|
||||
*/
|
||||
@SaCheckPermission("stgl:club:list")
|
||||
@PostMapping("/batchCheckPermission")
|
||||
@Log(title = "批量检查社团权限", businessType = BusinessType.OTHER)
|
||||
public R<Map<Long, ClubActivityPermissionResult>> batchCheckPermission(@RequestBody List<Long> clubIds) {
|
||||
log.info("批量检查社团权限请求,社团数量: {}", clubIds != null ? clubIds.size() : 0);
|
||||
|
||||
if (clubIds == null || clubIds.isEmpty()) {
|
||||
return R.fail("社团ID列表不能为空");
|
||||
}
|
||||
|
||||
if (clubIds.size() > 100) {
|
||||
return R.fail("批量检查数量不能超过100个");
|
||||
}
|
||||
|
||||
try {
|
||||
Map<Long, ClubActivityPermissionResult> results =
|
||||
clubPermissionService.batchCheckClubActivityPermission(clubIds);
|
||||
log.info("批量权限检查完成,处理社团数量: {}", results.size());
|
||||
return R.ok(results);
|
||||
} catch (Exception e) {
|
||||
log.error("批量检查社团权限失败,clubIds: {}", clubIds, e);
|
||||
return R.fail("批量检查权限失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查社团是否在年审窗口期内
|
||||
*
|
||||
* @param clubId 社团ID
|
||||
* @return 是否在年审窗口期
|
||||
*/
|
||||
@SaCheckPermission("stgl:club:list")
|
||||
@GetMapping("/isInAnnualReviewWindow/{clubId}")
|
||||
public R<Boolean> isInAnnualReviewWindow(@PathVariable Long clubId) {
|
||||
log.debug("检查社团是否在年审窗口期,clubId: {}", clubId);
|
||||
|
||||
if (clubId == null || clubId <= 0) {
|
||||
return R.fail("社团ID不能为空");
|
||||
}
|
||||
|
||||
try {
|
||||
boolean inWindow = clubPermissionService.isInAnnualReviewWindow(clubId);
|
||||
return R.ok(inWindow);
|
||||
} catch (Exception e) {
|
||||
log.error("检查年审窗口期失败,clubId: {}", clubId, e);
|
||||
return R.fail("检查失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取社团距离下次年审的天数
|
||||
*
|
||||
* @param clubId 社团ID
|
||||
* @return 距离年审的天数
|
||||
*/
|
||||
@SaCheckPermission("stgl:club:list")
|
||||
@GetMapping("/getDaysUntilNextAnnualReview/{clubId}")
|
||||
public R<Long> getDaysUntilNextAnnualReview(@PathVariable Long clubId) {
|
||||
log.debug("获取社团距离年审天数,clubId: {}", clubId);
|
||||
|
||||
if (clubId == null || clubId <= 0) {
|
||||
return R.fail("社团ID不能为空");
|
||||
}
|
||||
|
||||
try {
|
||||
long days = clubPermissionService.getDaysUntilNextAnnualReview(clubId);
|
||||
return R.ok(days);
|
||||
} catch (Exception e) {
|
||||
log.error("获取距离年审天数失败,clubId: {}", clubId, e);
|
||||
return R.fail("获取失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查社团是否已在当前年审期内提交年审
|
||||
*
|
||||
* @param clubId 社团ID
|
||||
* @return 是否已提交年审
|
||||
*/
|
||||
@SaCheckPermission("stgl:club:list")
|
||||
@GetMapping("/hasSubmittedCurrentYearAnnualReview/{clubId}")
|
||||
public R<Boolean> hasSubmittedCurrentYearAnnualReview(@PathVariable Long clubId) {
|
||||
log.debug("检查社团是否已提交年审,clubId: {}", clubId);
|
||||
|
||||
if (clubId == null || clubId <= 0) {
|
||||
return R.fail("社团ID不能为空");
|
||||
}
|
||||
|
||||
try {
|
||||
boolean hasSubmitted = clubPermissionService.hasSubmittedCurrentYearAnnualReview(clubId);
|
||||
return R.ok(hasSubmitted);
|
||||
} catch (Exception e) {
|
||||
log.error("检查年审提交状态失败,clubId: {}", clubId, e);
|
||||
return R.fail("检查失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取社团权限详细信息(包含所有相关信息的综合接口)
|
||||
*
|
||||
* @param clubId 社团ID
|
||||
* @return 权限详细信息
|
||||
*/
|
||||
@SaCheckPermission("stgl:club:list")
|
||||
@GetMapping("/getPermissionDetails/{clubId}")
|
||||
@Log(title = "获取社团权限详情", businessType = BusinessType.OTHER)
|
||||
public R<Map<String, Object>> getPermissionDetails(@PathVariable Long clubId) {
|
||||
log.info("获取社团权限详情,clubId: {}", clubId);
|
||||
|
||||
if (clubId == null || clubId <= 0) {
|
||||
return R.fail("社团ID不能为空");
|
||||
}
|
||||
|
||||
try {
|
||||
ClubActivityPermissionResult permissionResult =
|
||||
clubPermissionService.checkClubActivityPermission(clubId);
|
||||
boolean inWindow = clubPermissionService.isInAnnualReviewWindow(clubId);
|
||||
long daysUntilNext = clubPermissionService.getDaysUntilNextAnnualReview(clubId);
|
||||
boolean hasSubmitted = clubPermissionService.hasSubmittedCurrentYearAnnualReview(clubId);
|
||||
|
||||
Map<String, Object> details = Map.of(
|
||||
"permissionResult", permissionResult,
|
||||
"isInAnnualReviewWindow", inWindow,
|
||||
"daysUntilNextAnnualReview", daysUntilNext,
|
||||
"hasSubmittedCurrentYearAnnualReview", hasSubmitted
|
||||
);
|
||||
|
||||
return R.ok(details);
|
||||
} catch (Exception e) {
|
||||
log.error("获取社团权限详情失败,clubId: {}", clubId, e);
|
||||
return R.fail("获取详情失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
+24
@@ -82,6 +82,30 @@ public class StglActivity extends TenantEntity {
|
||||
@TableField("poster_url")
|
||||
private String posterUrl;
|
||||
|
||||
/**
|
||||
* 关联部门ID
|
||||
*/
|
||||
@TableField("related_dept_id")
|
||||
private Long relatedDeptId;
|
||||
|
||||
/**
|
||||
* 关联部门名称
|
||||
*/
|
||||
@TableField("related_dept_name")
|
||||
private String relatedDeptName;
|
||||
|
||||
/**
|
||||
* 关联部门ID列表(JSON格式)
|
||||
*/
|
||||
@TableField("related_dept_ids")
|
||||
private String relatedDeptIds;
|
||||
|
||||
/**
|
||||
* 关联部门名称列表(逗号分隔)
|
||||
*/
|
||||
@TableField("related_dept_names")
|
||||
private String relatedDeptNames;
|
||||
|
||||
/**
|
||||
* 关联社团ID
|
||||
*/
|
||||
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
package org.dromara.stgl.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import org.dromara.common.tenant.core.TenantEntity;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 社团指导老师任职历史对象 stgl_club_advisor_history
|
||||
*
|
||||
* @author Cursor AI
|
||||
* @date 2025-01-03
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("stgl_club_advisor_history")
|
||||
public class StglClubAdvisorHistory extends TenantEntity {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
@TableId(value = "id")
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 社团ID
|
||||
*/
|
||||
private Long clubId;
|
||||
|
||||
/**
|
||||
* 指导老师姓名
|
||||
*/
|
||||
private String advisorName;
|
||||
|
||||
/**
|
||||
* 指导老师部门
|
||||
*/
|
||||
private String advisorDept;
|
||||
|
||||
/**
|
||||
* 指导老师电话
|
||||
*/
|
||||
private String advisorPhone;
|
||||
|
||||
/**
|
||||
* 指导老师邮箱
|
||||
*/
|
||||
private String advisorEmail;
|
||||
|
||||
/**
|
||||
* 指导老师用户ID
|
||||
*/
|
||||
private Long advisorUserId;
|
||||
|
||||
/**
|
||||
* 在任状态:1-当前在任,0-历史(已离任)
|
||||
*/
|
||||
private Integer status;
|
||||
|
||||
/**
|
||||
* 上任时间
|
||||
*/
|
||||
private Date startTime;
|
||||
|
||||
/**
|
||||
* 离任时间
|
||||
*/
|
||||
private Date endTime;
|
||||
|
||||
}
|
||||
+130
@@ -0,0 +1,130 @@
|
||||
package org.dromara.stgl.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import org.dromara.common.tenant.core.TenantEntity;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 社团年审记录对象 stgl_club_annual_review
|
||||
*
|
||||
* @author Cursor AI
|
||||
* @date 2025-01-15
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("stgl_club_annual_review")
|
||||
public class StglClubAnnualReview extends TenantEntity {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
@TableId(value = "id")
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 社团ID
|
||||
*/
|
||||
private Long clubId;
|
||||
|
||||
/**
|
||||
* 年审年度
|
||||
*/
|
||||
private Integer reviewYear;
|
||||
|
||||
/**
|
||||
* 年审开始日期
|
||||
*/
|
||||
private Date reviewStartDate;
|
||||
|
||||
/**
|
||||
* 年审结束日期
|
||||
*/
|
||||
private Date reviewEndDate;
|
||||
|
||||
/**
|
||||
* 年审时间
|
||||
*/
|
||||
private Date reviewTime;
|
||||
|
||||
/**
|
||||
* 社团名称
|
||||
*/
|
||||
private String clubName;
|
||||
|
||||
/**
|
||||
* 成立日期
|
||||
*/
|
||||
private Date establishDate;
|
||||
|
||||
/**
|
||||
* 社长姓名
|
||||
*/
|
||||
private String presidentName;
|
||||
|
||||
/**
|
||||
* 社团类型
|
||||
*/
|
||||
private String clubType;
|
||||
|
||||
/**
|
||||
* 指导老师姓名
|
||||
*/
|
||||
private String advisorName;
|
||||
|
||||
/**
|
||||
* 指导老师部门
|
||||
*/
|
||||
private String advisorDept;
|
||||
|
||||
/**
|
||||
* 是否有章程
|
||||
*/
|
||||
private String hasCharter;
|
||||
|
||||
/**
|
||||
* 章程文件URL
|
||||
*/
|
||||
private String charterUrl;
|
||||
|
||||
/**
|
||||
* 是否有资金
|
||||
*/
|
||||
private String hasFunds;
|
||||
|
||||
/**
|
||||
* 资金数额
|
||||
*/
|
||||
private BigDecimal fundAmount;
|
||||
|
||||
/**
|
||||
* 成员数量
|
||||
*/
|
||||
private Integer memberCount;
|
||||
|
||||
/**
|
||||
* 状态(0-草稿 1-已提交 2-审核中 3-审核通过 4-审核不通过)
|
||||
*/
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 指导老师意见
|
||||
*/
|
||||
private String advisorOpinion;
|
||||
|
||||
/**
|
||||
* 学院意见
|
||||
*/
|
||||
private String collegeOpinion;
|
||||
|
||||
/**
|
||||
* 团委意见
|
||||
*/
|
||||
private String youthLeagueOpinion;
|
||||
}
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
package org.dromara.stgl.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.dromara.common.mybatis.core.domain.BaseEntity;
|
||||
import org.dromara.common.tenant.core.TenantEntity;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 社团年审记录活动对象 stgl_club_annual_review_activity
|
||||
*
|
||||
* @author Lion Li
|
||||
* @date 2024-12-31
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("stgl_club_annual_review_activity")
|
||||
@Schema(description = "社团年审记录活动")
|
||||
public class StglClubAnnualReviewActivity extends TenantEntity implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
@TableId(value = "id")
|
||||
@Schema(description = "主键ID")
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 年审记录ID
|
||||
*/
|
||||
@Schema(description = "年审记录ID")
|
||||
private Long annualReviewId;
|
||||
|
||||
/**
|
||||
* 活动名称
|
||||
*/
|
||||
@Schema(description = "活动名称")
|
||||
private String activityName;
|
||||
|
||||
/**
|
||||
* 活动时间
|
||||
*/
|
||||
@Schema(description = "活动时间")
|
||||
private Date activityTime;
|
||||
|
||||
/**
|
||||
* 活动开始时间
|
||||
*/
|
||||
@Schema(description = "活动开始时间")
|
||||
private Date activityStartTime;
|
||||
|
||||
/**
|
||||
* 活动结束时间
|
||||
*/
|
||||
@Schema(description = "活动结束时间")
|
||||
private Date activityEndTime;
|
||||
|
||||
/**
|
||||
* 活动地点
|
||||
*/
|
||||
@Schema(description = "活动地点")
|
||||
private String activityLocation;
|
||||
|
||||
/**
|
||||
* 活动类型
|
||||
*/
|
||||
@Schema(description = "活动类型")
|
||||
private String activityType;
|
||||
|
||||
/**
|
||||
* 参与人数
|
||||
*/
|
||||
@Schema(description = "参与人数")
|
||||
private Integer participantCount;
|
||||
|
||||
/**
|
||||
* 活动描述
|
||||
*/
|
||||
@Schema(description = "活动描述")
|
||||
private String activityDescription;
|
||||
|
||||
/**
|
||||
* 删除标志(0代表存在 1代表删除)
|
||||
*/
|
||||
@TableLogic(value = "0", delval = "1")
|
||||
private Integer deleted;
|
||||
|
||||
}
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
package org.dromara.stgl.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.dromara.common.mybatis.core.domain.BaseEntity;
|
||||
import org.dromara.common.tenant.core.TenantEntity;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 社团年审记录成员对象 stgl_club_annual_review_member
|
||||
*
|
||||
* @author Lion Li
|
||||
* @date 2024-12-31
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("stgl_club_annual_review_member")
|
||||
@Schema(description = "社团年审记录成员")
|
||||
public class StglClubAnnualReviewMember extends TenantEntity implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
@TableId(value = "id")
|
||||
@Schema(description = "主键ID")
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 年审记录ID
|
||||
*/
|
||||
@Schema(description = "年审记录ID")
|
||||
private Long annualReviewId;
|
||||
|
||||
/**
|
||||
* 成员姓名
|
||||
*/
|
||||
@Schema(description = "成员姓名")
|
||||
private String memberName;
|
||||
|
||||
/**
|
||||
* 班级
|
||||
*/
|
||||
@Schema(description = "班级")
|
||||
private String className;
|
||||
|
||||
/**
|
||||
* 职务
|
||||
*/
|
||||
@Schema(description = "职务")
|
||||
private String role;
|
||||
|
||||
/**
|
||||
* 联系方式
|
||||
*/
|
||||
@Schema(description = "联系方式")
|
||||
private String contactInfo;
|
||||
|
||||
/**
|
||||
* 删除标志(0代表存在 1代表删除)
|
||||
*/
|
||||
@TableLogic(value = "0", delval = "1")
|
||||
private Integer deleted;
|
||||
|
||||
}
|
||||
+20
-1
@@ -76,6 +76,26 @@ public class StglActivityBo extends BaseEntity {
|
||||
*/
|
||||
private String posterUrl;
|
||||
|
||||
/**
|
||||
* 关联部门ID
|
||||
*/
|
||||
private Long relatedDeptId;
|
||||
|
||||
/**
|
||||
* 关联部门名称
|
||||
*/
|
||||
private String relatedDeptName;
|
||||
|
||||
/**
|
||||
* 关联部门ID列表(JSON格式)
|
||||
*/
|
||||
private String relatedDeptIds;
|
||||
|
||||
/**
|
||||
* 关联部门名称列表(逗号分隔)
|
||||
*/
|
||||
private String relatedDeptNames;
|
||||
|
||||
/**
|
||||
* 关联社团ID
|
||||
*/
|
||||
@@ -89,7 +109,6 @@ public class StglActivityBo extends BaseEntity {
|
||||
/**
|
||||
* 主办方名称
|
||||
*/
|
||||
@NotBlank(message = "主办方名称不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
private String organizerName;
|
||||
|
||||
/**
|
||||
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
package org.dromara.stgl.domain.bo;
|
||||
|
||||
import org.dromara.common.mybatis.core.domain.BaseEntity;
|
||||
import org.dromara.common.core.validate.AddGroup;
|
||||
import org.dromara.common.core.validate.EditGroup;
|
||||
import io.github.linpeilie.annotations.AutoMapper;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import jakarta.validation.constraints.*;
|
||||
import org.dromara.stgl.domain.StglClubAdvisorHistory;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 社团指导老师任职历史业务对象 stgl_club_advisor_history
|
||||
*
|
||||
* @author Cursor AI
|
||||
* @date 2025-01-03
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@AutoMapper(target = StglClubAdvisorHistory.class, reverseConvertGenerate = false)
|
||||
public class StglClubAdvisorHistoryBo extends BaseEntity {
|
||||
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
@NotNull(message = "主键ID不能为空", groups = { EditGroup.class })
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 社团ID
|
||||
*/
|
||||
@NotNull(message = "社团ID不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
private Long clubId;
|
||||
|
||||
/**
|
||||
* 指导老师姓名
|
||||
*/
|
||||
@NotBlank(message = "指导老师姓名不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
@Size(max = 100, message = "指导老师姓名长度不能超过100个字符")
|
||||
private String advisorName;
|
||||
|
||||
/**
|
||||
* 指导老师部门
|
||||
*/
|
||||
@Size(max = 200, message = "指导老师部门长度不能超过200个字符")
|
||||
private String advisorDept;
|
||||
|
||||
/**
|
||||
* 指导老师电话
|
||||
*/
|
||||
@Size(max = 20, message = "指导老师电话长度不能超过20个字符")
|
||||
private String advisorPhone;
|
||||
|
||||
/**
|
||||
* 指导老师邮箱
|
||||
*/
|
||||
@Size(max = 100, message = "指导老师邮箱长度不能超过100个字符")
|
||||
@Email(message = "指导老师邮箱格式不正确")
|
||||
private String advisorEmail;
|
||||
|
||||
/**
|
||||
* 指导老师用户ID
|
||||
*/
|
||||
private Long advisorUserId;
|
||||
|
||||
/**
|
||||
* 在任状态:1-当前在任,0-历史(已离任)
|
||||
*/
|
||||
@NotNull(message = "在任状态不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
private Integer status;
|
||||
|
||||
/**
|
||||
* 上任时间
|
||||
*/
|
||||
@NotNull(message = "上任时间不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
private Date startTime;
|
||||
|
||||
/**
|
||||
* 离任时间
|
||||
*/
|
||||
private Date endTime;
|
||||
|
||||
}
|
||||
+163
@@ -0,0 +1,163 @@
|
||||
package org.dromara.stgl.domain.bo;
|
||||
|
||||
import org.dromara.stgl.domain.StglClubAnnualReview;
|
||||
import org.dromara.common.mybatis.core.domain.BaseEntity;
|
||||
import org.dromara.common.core.validate.AddGroup;
|
||||
import org.dromara.common.core.validate.EditGroup;
|
||||
import io.github.linpeilie.annotations.AutoMapper;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import jakarta.validation.constraints.*;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 社团年审记录业务对象 StglClubAnnualReviewBo
|
||||
*
|
||||
* @author Cursor AI
|
||||
* @date 2025-01-15
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@AutoMapper(target = StglClubAnnualReview.class, reverseConvertGenerate = false)
|
||||
public class StglClubAnnualReviewBo extends BaseEntity {
|
||||
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
@NotNull(message = "主键ID不能为空", groups = { EditGroup.class })
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 社团ID
|
||||
*/
|
||||
@NotNull(message = "社团ID不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
private Long clubId;
|
||||
|
||||
/**
|
||||
* 年审年度
|
||||
*/
|
||||
@NotNull(message = "年审年度不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
@Min(value = 2000, message = "年审年度不能小于2000年")
|
||||
@Max(value = 9999, message = "年审年度不能大于9999年")
|
||||
private Integer reviewYear;
|
||||
|
||||
/**
|
||||
* 年审开始日期
|
||||
*/
|
||||
@NotNull(message = "年审开始日期不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
private Date reviewStartDate;
|
||||
|
||||
/**
|
||||
* 年审结束日期
|
||||
*/
|
||||
@NotNull(message = "年审结束日期不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
private Date reviewEndDate;
|
||||
|
||||
/**
|
||||
* 年审时间
|
||||
*/
|
||||
@NotNull(message = "年审时间不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
private Date reviewTime;
|
||||
|
||||
/**
|
||||
* 社团名称
|
||||
*/
|
||||
@NotBlank(message = "社团名称不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
@Size(max = 100, message = "社团名称长度不能超过100个字符")
|
||||
private String clubName;
|
||||
|
||||
/**
|
||||
* 成立日期
|
||||
*/
|
||||
private Date establishDate;
|
||||
|
||||
/**
|
||||
* 社长姓名
|
||||
*/
|
||||
@Size(max = 50, message = "社长姓名长度不能超过50个字符")
|
||||
private String presidentName;
|
||||
|
||||
/**
|
||||
* 社团类型
|
||||
*/
|
||||
@Size(max = 20, message = "社团类型长度不能超过20个字符")
|
||||
private String clubType;
|
||||
|
||||
/**
|
||||
* 指导老师姓名
|
||||
*/
|
||||
@Size(max = 50, message = "指导老师姓名长度不能超过50个字符")
|
||||
private String advisorName;
|
||||
|
||||
/**
|
||||
* 指导老师部门
|
||||
*/
|
||||
@Size(max = 100, message = "指导老师部门长度不能超过100个字符")
|
||||
private String advisorDept;
|
||||
|
||||
/**
|
||||
* 是否有章程
|
||||
*/
|
||||
@Pattern(regexp = "^[YN]$", message = "是否有章程必须是Y或N")
|
||||
private String hasCharter;
|
||||
|
||||
/**
|
||||
* 章程文件URL
|
||||
*/
|
||||
@Size(max = 500, message = "章程文件URL长度不能超过500个字符")
|
||||
private String charterUrl;
|
||||
|
||||
/**
|
||||
* 是否有资金
|
||||
*/
|
||||
@Pattern(regexp = "^[YN]$", message = "是否有资金必须是Y或N")
|
||||
private String hasFunds;
|
||||
|
||||
/**
|
||||
* 资金数额
|
||||
*/
|
||||
@DecimalMin(value = "0.00", message = "资金数额不能小于0")
|
||||
@DecimalMax(value = "99999999.99", message = "资金数额不能超过99999999.99")
|
||||
private BigDecimal fundAmount;
|
||||
|
||||
/**
|
||||
* 成员数量
|
||||
*/
|
||||
@Min(value = 0, message = "成员数量不能小于0")
|
||||
@Max(value = 9999, message = "成员数量不能超过9999")
|
||||
private Integer memberCount;
|
||||
|
||||
/**
|
||||
* 状态(0-草稿 1-已提交 2-审核中 3-审核通过 4-审核不通过)
|
||||
*/
|
||||
@Pattern(regexp = "^[0-4]$", message = "状态值必须是0-4之间的数字")
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 指导老师意见
|
||||
*/
|
||||
private String advisorOpinion;
|
||||
|
||||
/**
|
||||
* 学院意见
|
||||
*/
|
||||
private String collegeOpinion;
|
||||
|
||||
/**
|
||||
* 团委意见
|
||||
*/
|
||||
private String youthLeagueOpinion;
|
||||
|
||||
/**
|
||||
* 成员信息JSON(前端传入)
|
||||
*/
|
||||
private String membersJson;
|
||||
|
||||
/**
|
||||
* 活动信息JSON(前端传入)
|
||||
*/
|
||||
private String activitiesJson;
|
||||
|
||||
}
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
package org.dromara.stgl.domain.vo;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 社团活动权限检查结果
|
||||
*
|
||||
* @author Cursor AI
|
||||
* @date 2025-01-15
|
||||
*/
|
||||
@Data
|
||||
public class ClubActivityPermissionResult {
|
||||
|
||||
/**
|
||||
* 是否可以发起活动
|
||||
*/
|
||||
private Boolean canCreateActivity;
|
||||
|
||||
/**
|
||||
* 权限状态
|
||||
* NORMAL - 正常状态
|
||||
* TEMPORARILY_LIMITED - 临时限制(15天内可恢复)
|
||||
* PERMANENTLY_LIMITED - 永久限制(超过15天)
|
||||
*/
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 状态描述信息
|
||||
*/
|
||||
private String message;
|
||||
|
||||
/**
|
||||
* 年审截止时间(仅临时限制时有值)
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date deadlineTime;
|
||||
|
||||
/**
|
||||
* 剩余小时数(仅临时限制时有值)
|
||||
*/
|
||||
private Long remainingHours;
|
||||
|
||||
/**
|
||||
* 剩余天数(仅临时限制时有值)
|
||||
*/
|
||||
private Long remainingDays;
|
||||
|
||||
/**
|
||||
* 年审开始时间
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date annualReviewStartTime;
|
||||
|
||||
/**
|
||||
* 创建正常状态结果
|
||||
*/
|
||||
public static ClubActivityPermissionResult allowed() {
|
||||
ClubActivityPermissionResult result = new ClubActivityPermissionResult();
|
||||
result.setCanCreateActivity(true);
|
||||
result.setStatus("NORMAL");
|
||||
result.setMessage("权限正常,可以发起活动");
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建临时限制状态结果
|
||||
*/
|
||||
public static ClubActivityPermissionResult temporarilyDenied(String message, Date deadlineTime,
|
||||
Date annualReviewStartTime,
|
||||
Long remainingHours, Long remainingDays) {
|
||||
ClubActivityPermissionResult result = new ClubActivityPermissionResult();
|
||||
result.setCanCreateActivity(false);
|
||||
result.setStatus("TEMPORARILY_LIMITED");
|
||||
result.setMessage(message);
|
||||
result.setDeadlineTime(deadlineTime);
|
||||
result.setAnnualReviewStartTime(annualReviewStartTime);
|
||||
result.setRemainingHours(remainingHours);
|
||||
result.setRemainingDays(remainingDays);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建永久限制状态结果
|
||||
*/
|
||||
public static ClubActivityPermissionResult permanentlyDenied(String message) {
|
||||
ClubActivityPermissionResult result = new ClubActivityPermissionResult();
|
||||
result.setCanCreateActivity(false);
|
||||
result.setStatus("PERMANENTLY_LIMITED");
|
||||
result.setMessage(message);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+10
@@ -65,6 +65,16 @@ public class MyActivityVo implements Serializable {
|
||||
*/
|
||||
private String organizerName;
|
||||
|
||||
/**
|
||||
* 关联部门名称
|
||||
*/
|
||||
private String relatedDeptName;
|
||||
|
||||
/**
|
||||
* 关联部门名称列表
|
||||
*/
|
||||
private String relatedDeptNames;
|
||||
|
||||
/**
|
||||
* 关联类型(1-社团 2-志愿者队伍)
|
||||
*/
|
||||
|
||||
+21
@@ -88,6 +88,27 @@ public class StglActivityVo implements Serializable {
|
||||
*/
|
||||
private String posterUrl;
|
||||
|
||||
/**
|
||||
* 关联部门ID
|
||||
*/
|
||||
private Long relatedDeptId;
|
||||
|
||||
/**
|
||||
* 关联部门名称
|
||||
*/
|
||||
@ExcelProperty(value = "关联部门")
|
||||
private String relatedDeptName;
|
||||
|
||||
/**
|
||||
* 关联部门ID列表(JSON格式)
|
||||
*/
|
||||
private String relatedDeptIds;
|
||||
|
||||
/**
|
||||
* 关联部门名称列表(逗号分隔)
|
||||
*/
|
||||
private String relatedDeptNames;
|
||||
|
||||
/**
|
||||
* 关联社团ID
|
||||
*/
|
||||
|
||||
+15
@@ -65,6 +65,21 @@ public class StglClassVo implements Serializable {
|
||||
*/
|
||||
private Integer activityCount;
|
||||
|
||||
/**
|
||||
* 今日活动名称(审核通过且时间为今天的活动)
|
||||
*/
|
||||
private String todayActivityName;
|
||||
|
||||
/**
|
||||
* 今日活动开始时间
|
||||
*/
|
||||
private String todayActivityStartTime;
|
||||
|
||||
/**
|
||||
* 今日活动结束时间
|
||||
*/
|
||||
private String todayActivityEndTime;
|
||||
|
||||
/**
|
||||
* 创建者
|
||||
*/
|
||||
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
package org.dromara.stgl.domain.vo;
|
||||
|
||||
import org.dromara.stgl.domain.StglClubAdvisorHistory;
|
||||
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
|
||||
import cn.idev.excel.annotation.ExcelProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import io.github.linpeilie.annotations.AutoMapper;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 社团指导老师任职历史视图对象 stgl_club_advisor_history
|
||||
*
|
||||
* @author Cursor AI
|
||||
* @date 2025-01-03
|
||||
*/
|
||||
@Data
|
||||
@ExcelIgnoreUnannotated
|
||||
@AutoMapper(target = StglClubAdvisorHistory.class)
|
||||
public class StglClubAdvisorHistoryVo implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
@ExcelProperty(value = "主键ID")
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 社团ID
|
||||
*/
|
||||
@ExcelProperty(value = "社团ID")
|
||||
private Long clubId;
|
||||
|
||||
/**
|
||||
* 社团名称
|
||||
*/
|
||||
@ExcelProperty(value = "社团名称")
|
||||
private String clubName;
|
||||
|
||||
/**
|
||||
* 指导老师姓名
|
||||
*/
|
||||
@ExcelProperty(value = "指导老师姓名")
|
||||
private String advisorName;
|
||||
|
||||
/**
|
||||
* 指导老师部门
|
||||
*/
|
||||
@ExcelProperty(value = "指导老师部门")
|
||||
private String advisorDept;
|
||||
|
||||
/**
|
||||
* 指导老师电话
|
||||
*/
|
||||
@ExcelProperty(value = "指导老师电话")
|
||||
private String advisorPhone;
|
||||
|
||||
/**
|
||||
* 指导老师邮箱
|
||||
*/
|
||||
@ExcelProperty(value = "指导老师邮箱")
|
||||
private String advisorEmail;
|
||||
|
||||
/**
|
||||
* 指导老师用户ID
|
||||
*/
|
||||
@ExcelProperty(value = "指导老师用户ID")
|
||||
private Long advisorUserId;
|
||||
|
||||
/**
|
||||
* 在任状态:1-当前在任,0-历史(已离任)
|
||||
*/
|
||||
@ExcelProperty(value = "在任状态", converter = org.dromara.common.excel.convert.ExcelDictConvert.class)
|
||||
@org.dromara.common.excel.annotation.ExcelDictFormat(dictType = "sys_normal_disable")
|
||||
private Integer status;
|
||||
|
||||
/**
|
||||
* 上任时间
|
||||
*/
|
||||
@ExcelProperty(value = "上任时间")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date startTime;
|
||||
|
||||
/**
|
||||
* 离任时间
|
||||
*/
|
||||
@ExcelProperty(value = "离任时间")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date endTime;
|
||||
|
||||
/**
|
||||
* 创建者
|
||||
*/
|
||||
private Long createBy;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date createTime;
|
||||
|
||||
/**
|
||||
* 更新者
|
||||
*/
|
||||
private Long updateBy;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date updateTime;
|
||||
|
||||
}
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
package org.dromara.stgl.domain.vo;
|
||||
|
||||
import org.dromara.stgl.domain.StglClubAnnualReviewActivity;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import io.github.linpeilie.annotations.AutoMapper;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 社团年审记录活动视图对象 StglClubAnnualReviewActivityVo
|
||||
*
|
||||
* @author Cursor AI
|
||||
* @date 2025-01-15
|
||||
*/
|
||||
@Data
|
||||
@AutoMapper(target = StglClubAnnualReviewActivity.class)
|
||||
public class StglClubAnnualReviewActivityVo implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 年审记录ID
|
||||
*/
|
||||
private Long annualReviewId;
|
||||
|
||||
/**
|
||||
* 活动名称
|
||||
*/
|
||||
private String activityName;
|
||||
|
||||
/**
|
||||
* 活动时间
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date activityTime;
|
||||
|
||||
/**
|
||||
* 活动开始时间
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date activityStartTime;
|
||||
|
||||
/**
|
||||
* 活动结束时间
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date activityEndTime;
|
||||
|
||||
/**
|
||||
* 活动地点
|
||||
*/
|
||||
private String activityLocation;
|
||||
|
||||
/**
|
||||
* 活动类型
|
||||
*/
|
||||
private String activityType;
|
||||
|
||||
/**
|
||||
* 参与人数
|
||||
*/
|
||||
private Integer participantCount;
|
||||
|
||||
/**
|
||||
* 活动描述
|
||||
*/
|
||||
private String activityDescription;
|
||||
|
||||
/**
|
||||
* 创建者
|
||||
*/
|
||||
private Long createBy;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date createTime;
|
||||
|
||||
/**
|
||||
* 更新者
|
||||
*/
|
||||
private Long updateBy;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date updateTime;
|
||||
|
||||
// 为了兼容前端,添加一些别名字段
|
||||
/**
|
||||
* 地点(别名)
|
||||
*/
|
||||
public String getLocation() {
|
||||
return this.activityLocation;
|
||||
}
|
||||
|
||||
public void setLocation(String location) {
|
||||
this.activityLocation = location;
|
||||
}
|
||||
}
|
||||
+156
@@ -0,0 +1,156 @@
|
||||
package org.dromara.stgl.domain.vo;
|
||||
|
||||
import org.dromara.stgl.domain.StglClubAnnualReview;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import io.github.linpeilie.annotations.AutoMapper;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 社团年审记录视图对象 StglClubAnnualReviewVo
|
||||
*
|
||||
* @author Cursor AI
|
||||
* @date 2025-01-15
|
||||
*/
|
||||
@Data
|
||||
@AutoMapper(target = StglClubAnnualReview.class)
|
||||
public class StglClubAnnualReviewVo implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 社团ID
|
||||
*/
|
||||
private Long clubId;
|
||||
|
||||
/**
|
||||
* 年审年度
|
||||
*/
|
||||
private Integer reviewYear;
|
||||
|
||||
/**
|
||||
* 年审开始日期
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
private Date reviewStartDate;
|
||||
|
||||
/**
|
||||
* 年审结束日期
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
private Date reviewEndDate;
|
||||
|
||||
/**
|
||||
* 年审时间
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
private Date reviewTime;
|
||||
|
||||
/**
|
||||
* 社团名称
|
||||
*/
|
||||
private String clubName;
|
||||
|
||||
/**
|
||||
* 成立日期
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
private Date establishDate;
|
||||
|
||||
/**
|
||||
* 社长姓名
|
||||
*/
|
||||
private String presidentName;
|
||||
|
||||
/**
|
||||
* 社团类型
|
||||
*/
|
||||
private String clubType;
|
||||
|
||||
/**
|
||||
* 指导老师姓名
|
||||
*/
|
||||
private String advisorName;
|
||||
|
||||
/**
|
||||
* 指导老师部门
|
||||
*/
|
||||
private String advisorDept;
|
||||
|
||||
/**
|
||||
* 是否有章程
|
||||
*/
|
||||
private String hasCharter;
|
||||
|
||||
/**
|
||||
* 章程文件URL
|
||||
*/
|
||||
private String charterUrl;
|
||||
|
||||
/**
|
||||
* 是否有资金
|
||||
*/
|
||||
private String hasFunds;
|
||||
|
||||
/**
|
||||
* 资金数额
|
||||
*/
|
||||
private BigDecimal fundAmount;
|
||||
|
||||
/**
|
||||
* 成员数量
|
||||
*/
|
||||
private Integer memberCount;
|
||||
|
||||
/**
|
||||
* 状态(0-草稿 1-已提交 2-审核中 3-审核通过 4-审核不通过)
|
||||
*/
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 指导老师意见
|
||||
*/
|
||||
private String advisorOpinion;
|
||||
|
||||
/**
|
||||
* 学院意见
|
||||
*/
|
||||
private String collegeOpinion;
|
||||
|
||||
/**
|
||||
* 团委意见
|
||||
*/
|
||||
private String youthLeagueOpinion;
|
||||
|
||||
/**
|
||||
* 成员信息JSON(用于前端显示)
|
||||
*/
|
||||
private String membersJson;
|
||||
|
||||
/**
|
||||
* 活动信息JSON(用于前端显示)
|
||||
*/
|
||||
private String activitiesJson;
|
||||
|
||||
// 继承的基础字段
|
||||
private Long createBy;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date createTime;
|
||||
|
||||
private Long updateBy;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date updateTime;
|
||||
|
||||
}
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
package org.dromara.stgl.mapper;
|
||||
|
||||
import org.dromara.stgl.domain.StglClubAdvisorHistory;
|
||||
import org.dromara.stgl.domain.vo.StglClubAdvisorHistoryVo;
|
||||
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 社团指导老师任职历史Mapper接口
|
||||
*
|
||||
* @author Cursor AI
|
||||
* @date 2025-01-03
|
||||
*/
|
||||
public interface StglClubAdvisorHistoryMapper extends BaseMapperPlus<StglClubAdvisorHistory, StglClubAdvisorHistoryVo> {
|
||||
|
||||
/**
|
||||
* 查询指定社团的当前在任指导老师
|
||||
*
|
||||
* @param clubId 社团ID
|
||||
* @return 当前在任指导老师列表
|
||||
*/
|
||||
List<StglClubAdvisorHistoryVo> selectCurrentAdvisorsByClubId(@Param("clubId") Long clubId);
|
||||
|
||||
/**
|
||||
* 查询指定社团的历史指导老师(已离任)
|
||||
*
|
||||
* @param clubId 社团ID
|
||||
* @return 历史指导老师列表
|
||||
*/
|
||||
List<StglClubAdvisorHistoryVo> selectHistoryAdvisorsByClubId(@Param("clubId") Long clubId);
|
||||
|
||||
/**
|
||||
* 查询指定社团的所有指导老师(包含当前和历史)
|
||||
*
|
||||
* @param clubId 社团ID
|
||||
* @return 所有指导老师列表
|
||||
*/
|
||||
List<StglClubAdvisorHistoryVo> selectAllAdvisorsByClubId(@Param("clubId") Long clubId);
|
||||
|
||||
/**
|
||||
* 批量设置指导老师为历史状态(离任)
|
||||
*
|
||||
* @param clubId 社团ID
|
||||
* @param advisorNames 指导老师姓名列表
|
||||
* @param endTime 离任时间
|
||||
* @return 更新记录数
|
||||
*/
|
||||
int batchSetHistoryStatus(@Param("clubId") Long clubId,
|
||||
@Param("advisorNames") List<String> advisorNames,
|
||||
@Param("endTime") java.util.Date endTime);
|
||||
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
package org.dromara.stgl.mapper;
|
||||
|
||||
import org.dromara.stgl.domain.StglClubAnnualReviewActivity;
|
||||
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 社团年审记录活动Mapper接口
|
||||
*
|
||||
* @author Lion Li
|
||||
* @date 2024-12-31
|
||||
*/
|
||||
public interface StglClubAnnualReviewActivityMapper extends BaseMapperPlus<StglClubAnnualReviewActivity, StglClubAnnualReviewActivity> {
|
||||
|
||||
/**
|
||||
* 根据年审记录ID查询活动列表
|
||||
*/
|
||||
List<StglClubAnnualReviewActivity> selectActivitiesByAnnualReviewId(@Param("annualReviewId") Long annualReviewId);
|
||||
|
||||
/**
|
||||
* 根据年审记录ID删除活动
|
||||
*/
|
||||
int deleteActivitiesByAnnualReviewId(@Param("annualReviewId") Long annualReviewId);
|
||||
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
package org.dromara.stgl.mapper;
|
||||
|
||||
import org.dromara.stgl.domain.StglClubAnnualReview;
|
||||
import org.dromara.stgl.domain.vo.StglClubAnnualReviewVo;
|
||||
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
|
||||
|
||||
/**
|
||||
* 社团年审记录Mapper接口
|
||||
*
|
||||
* @author Cursor AI
|
||||
* @date 2025-01-15
|
||||
*/
|
||||
public interface StglClubAnnualReviewMapper extends BaseMapperPlus<StglClubAnnualReview, StglClubAnnualReviewVo> {
|
||||
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
package org.dromara.stgl.mapper;
|
||||
|
||||
import org.dromara.stgl.domain.StglClubAnnualReviewMember;
|
||||
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 社团年审记录成员Mapper接口
|
||||
*
|
||||
* @author Lion Li
|
||||
* @date 2024-12-31
|
||||
*/
|
||||
public interface StglClubAnnualReviewMemberMapper extends BaseMapperPlus<StglClubAnnualReviewMember, StglClubAnnualReviewMember> {
|
||||
|
||||
/**
|
||||
* 根据年审记录ID查询成员列表
|
||||
*/
|
||||
List<StglClubAnnualReviewMember> selectMembersByAnnualReviewId(@Param("annualReviewId") Long annualReviewId);
|
||||
|
||||
/**
|
||||
* 根据年审记录ID删除成员
|
||||
*/
|
||||
int deleteMembersByAnnualReviewId(@Param("annualReviewId") Long annualReviewId);
|
||||
|
||||
}
|
||||
+130
@@ -0,0 +1,130 @@
|
||||
package org.dromara.stgl.service;
|
||||
|
||||
import org.dromara.stgl.domain.vo.StglClubAdvisorHistoryVo;
|
||||
import org.dromara.stgl.domain.bo.StglClubAdvisorHistoryBo;
|
||||
import org.dromara.common.mybatis.core.page.TableDataInfo;
|
||||
import org.dromara.common.mybatis.core.page.PageQuery;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 社团指导老师任职历史Service接口
|
||||
*
|
||||
* @author Cursor AI
|
||||
* @date 2025-01-03
|
||||
*/
|
||||
public interface IStglClubAdvisorHistoryService {
|
||||
|
||||
/**
|
||||
* 查询社团指导老师任职历史
|
||||
*/
|
||||
StglClubAdvisorHistoryVo queryById(Long id);
|
||||
|
||||
/**
|
||||
* 查询社团指导老师任职历史列表
|
||||
*/
|
||||
TableDataInfo<StglClubAdvisorHistoryVo> queryPageList(StglClubAdvisorHistoryBo bo, PageQuery pageQuery);
|
||||
|
||||
/**
|
||||
* 查询社团指导老师任职历史列表
|
||||
*/
|
||||
List<StglClubAdvisorHistoryVo> queryList(StglClubAdvisorHistoryBo bo);
|
||||
|
||||
/**
|
||||
* 新增社团指导老师任职历史
|
||||
*/
|
||||
Boolean insertByBo(StglClubAdvisorHistoryBo bo);
|
||||
|
||||
/**
|
||||
* 修改社团指导老师任职历史
|
||||
*/
|
||||
Boolean updateByBo(StglClubAdvisorHistoryBo bo);
|
||||
|
||||
/**
|
||||
* 校验并批量删除社团指导老师任职历史信息
|
||||
*/
|
||||
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
|
||||
|
||||
/**
|
||||
* 查询指定社团的当前在任指导老师
|
||||
*
|
||||
* @param clubId 社团ID
|
||||
* @return 当前在任指导老师列表
|
||||
*/
|
||||
List<StglClubAdvisorHistoryVo> getCurrentAdvisorsByClubId(Long clubId);
|
||||
|
||||
/**
|
||||
* 查询指定社团的历史指导老师(已离任)
|
||||
*
|
||||
* @param clubId 社团ID
|
||||
* @return 历史指导老师列表
|
||||
*/
|
||||
List<StglClubAdvisorHistoryVo> getHistoryAdvisorsByClubId(Long clubId);
|
||||
|
||||
/**
|
||||
* 查询指定社团的所有指导老师(包含当前和历史)
|
||||
*
|
||||
* @param clubId 社团ID
|
||||
* @return 所有指导老师列表
|
||||
*/
|
||||
List<StglClubAdvisorHistoryVo> getAllAdvisorsByClubId(Long clubId);
|
||||
|
||||
/**
|
||||
* 更新社团指导老师历史记录
|
||||
* 对比新旧指导老师列表,处理新增和离任
|
||||
*
|
||||
* @param clubId 社团ID
|
||||
* @param newAdvisors 新的指导老师列表
|
||||
*/
|
||||
void updateClubAdvisors(Long clubId, List<AdvisorInfo> newAdvisors);
|
||||
|
||||
/**
|
||||
* 更新社团指导老师历史记录(指定开始时间)
|
||||
* 对比新旧指导老师列表,处理新增和离任
|
||||
*
|
||||
* @param clubId 社团ID
|
||||
* @param newAdvisors 新的指导老师列表
|
||||
* @param startTime 新指导老师的上任时间(如社团成立日期)
|
||||
*/
|
||||
void updateClubAdvisors(Long clubId, List<AdvisorInfo> newAdvisors, java.util.Date startTime);
|
||||
|
||||
/**
|
||||
* 指导老师信息
|
||||
*/
|
||||
class AdvisorInfo {
|
||||
private String name;
|
||||
private String dept;
|
||||
private String phone;
|
||||
private String email;
|
||||
private Long userId;
|
||||
|
||||
// 构造方法
|
||||
public AdvisorInfo() {}
|
||||
|
||||
public AdvisorInfo(String name, String dept, String phone, String email, Long userId) {
|
||||
this.name = name;
|
||||
this.dept = dept;
|
||||
this.phone = phone;
|
||||
this.email = email;
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
// getter和setter方法
|
||||
public String getName() { return name; }
|
||||
public void setName(String name) { this.name = name; }
|
||||
|
||||
public String getDept() { return dept; }
|
||||
public void setDept(String dept) { this.dept = dept; }
|
||||
|
||||
public String getPhone() { return phone; }
|
||||
public void setPhone(String phone) { this.phone = phone; }
|
||||
|
||||
public String getEmail() { return email; }
|
||||
public void setEmail(String email) { this.email = email; }
|
||||
|
||||
public Long getUserId() { return userId; }
|
||||
public void setUserId(Long userId) { this.userId = userId; }
|
||||
}
|
||||
|
||||
}
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
package org.dromara.stgl.service;
|
||||
|
||||
import org.dromara.stgl.domain.vo.StglClubAnnualReviewVo;
|
||||
import org.dromara.stgl.domain.bo.StglClubAnnualReviewBo;
|
||||
import org.dromara.common.mybatis.core.page.TableDataInfo;
|
||||
import org.dromara.common.mybatis.core.page.PageQuery;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 社团年审记录Service接口
|
||||
*
|
||||
* @author Cursor AI
|
||||
* @date 2025-01-15
|
||||
*/
|
||||
public interface IStglClubAnnualReviewService {
|
||||
|
||||
/**
|
||||
* 查询社团年审记录
|
||||
*/
|
||||
StglClubAnnualReviewVo queryById(Long id);
|
||||
|
||||
/**
|
||||
* 查询社团年审记录列表
|
||||
*/
|
||||
TableDataInfo<StglClubAnnualReviewVo> queryPageList(StglClubAnnualReviewBo bo, PageQuery pageQuery);
|
||||
|
||||
/**
|
||||
* 查询社团年审记录列表
|
||||
*/
|
||||
List<StglClubAnnualReviewVo> queryList(StglClubAnnualReviewBo bo);
|
||||
|
||||
/**
|
||||
* 新增社团年审记录
|
||||
*/
|
||||
Boolean insertByBo(StglClubAnnualReviewBo bo);
|
||||
|
||||
/**
|
||||
* 修改社团年审记录
|
||||
*/
|
||||
Boolean updateByBo(StglClubAnnualReviewBo bo);
|
||||
|
||||
/**
|
||||
* 校验并批量删除社团年审记录信息
|
||||
*/
|
||||
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
|
||||
|
||||
/**
|
||||
* 提交年审记录
|
||||
*/
|
||||
Boolean submitReview(Long id);
|
||||
|
||||
/**
|
||||
* 撤回年审记录
|
||||
*/
|
||||
Boolean withdrawReview(Long id);
|
||||
|
||||
/**
|
||||
* 审核年审记录
|
||||
*/
|
||||
Boolean auditReview(Long id, String auditResult, String auditOpinion, String auditType);
|
||||
|
||||
/**
|
||||
* 获取当前用户的年审记录列表
|
||||
*/
|
||||
TableDataInfo<StglClubAnnualReviewVo> queryMyPageList(StglClubAnnualReviewBo bo, PageQuery pageQuery);
|
||||
|
||||
/**
|
||||
* 检查是否可以创建年审记录
|
||||
*/
|
||||
Boolean checkCanCreateReview(Long clubId, Integer reviewYear);
|
||||
|
||||
/**
|
||||
* 根据社团ID和成立日期获取一年内的活动数据
|
||||
* @param clubId 社团ID
|
||||
* @param establishDate 成立日期字符串 (yyyy-MM-dd格式)
|
||||
* @return 活动列表
|
||||
*/
|
||||
List<org.dromara.stgl.domain.vo.StglActivityVo> getClubActivitiesByReviewYear(Long clubId, Integer reviewYear);
|
||||
|
||||
/**
|
||||
* 根据年审记录ID获取对应的活动数据
|
||||
* @param annualReviewId 年审记录ID
|
||||
* @return 年审活动列表
|
||||
*/
|
||||
List<org.dromara.stgl.domain.vo.StglClubAnnualReviewActivityVo> getAnnualReviewActivities(Long annualReviewId);
|
||||
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
package org.dromara.stgl.service;
|
||||
|
||||
import org.dromara.stgl.domain.vo.ClubActivityPermissionResult;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 社团权限管理Service接口
|
||||
*
|
||||
* @author Cursor AI
|
||||
* @date 2025-01-15
|
||||
*/
|
||||
public interface IStglClubPermissionService {
|
||||
|
||||
/**
|
||||
* 检查社团活动权限(实时计算)
|
||||
*
|
||||
* @param clubId 社团ID
|
||||
* @return 权限检查结果
|
||||
*/
|
||||
ClubActivityPermissionResult checkClubActivityPermission(Long clubId);
|
||||
|
||||
/**
|
||||
* 批量检查社团活动权限
|
||||
*
|
||||
* @param clubIds 社团ID列表
|
||||
* @return 权限检查结果映射(key: clubId, value: 权限结果)
|
||||
*/
|
||||
Map<Long, ClubActivityPermissionResult> batchCheckClubActivityPermission(List<Long> clubIds);
|
||||
|
||||
/**
|
||||
* 检查社团是否在年审窗口期内
|
||||
*
|
||||
* @param clubId 社团ID
|
||||
* @return true-在窗口期内,false-不在窗口期内
|
||||
*/
|
||||
boolean isInAnnualReviewWindow(Long clubId);
|
||||
|
||||
/**
|
||||
* 获取社团距离下次年审的天数
|
||||
*
|
||||
* @param clubId 社团ID
|
||||
* @return 距离年审的天数,负数表示已过期
|
||||
*/
|
||||
long getDaysUntilNextAnnualReview(Long clubId);
|
||||
|
||||
/**
|
||||
* 检查社团是否已在当前年审期内提交年审
|
||||
*
|
||||
* @param clubId 社团ID
|
||||
* @return true-已提交,false-未提交
|
||||
*/
|
||||
boolean hasSubmittedCurrentYearAnnualReview(Long clubId);
|
||||
}
|
||||
+52
-1
@@ -129,7 +129,7 @@ public class StglActivityServiceImpl extends ServiceImpl<StglActivityMapper, Stg
|
||||
StglActivity activity = BeanUtil.toBean(bo, StglActivity.class);
|
||||
|
||||
// 设置查询条件:只查询报名状态为0(未开始)和1(报名中)的活动
|
||||
// 通过设置params参数来传递IN查询条件
|
||||
// 注意:现在草稿、待审批、已驳回状态的活动的registration_status为NULL,不会被包含在查询结果中
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
params.put("registrationStatusList", Arrays.asList("0", "1"));
|
||||
|
||||
@@ -258,6 +258,9 @@ public class StglActivityServiceImpl extends ServiceImpl<StglActivityMapper, Stg
|
||||
StglActivity add = MapstructUtils.convert(bo, StglActivity.class);
|
||||
validEntityBeforeSave(add);
|
||||
|
||||
// 处理多选部门数据
|
||||
handleMultiDeptData(bo, add);
|
||||
|
||||
// 设置默认值
|
||||
if (add.getIsFinished() == null) {
|
||||
add.setIsFinished(0);
|
||||
@@ -287,6 +290,9 @@ public class StglActivityServiceImpl extends ServiceImpl<StglActivityMapper, Stg
|
||||
StglActivity update = MapstructUtils.convert(bo, StglActivity.class);
|
||||
validEntityBeforeSave(update);
|
||||
|
||||
// 处理多选部门数据
|
||||
handleMultiDeptData(bo, update);
|
||||
|
||||
// 获取更新前的活动信息,用于比较指导老师变化
|
||||
StglActivity oldActivity = null;
|
||||
if (update.getId() != null) {
|
||||
@@ -857,4 +863,49 @@ public class StglActivityServiceImpl extends ServiceImpl<StglActivityMapper, Stg
|
||||
// 使用自定义SQL查询评分详情,关联用户表获取完整信息
|
||||
return stglActivityRatingMapper.selectActivityRatingsWithUserInfo(activityId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理多选部门数据
|
||||
* 将前端传递的多选部门数据转换为后端存储格式
|
||||
*/
|
||||
private void handleMultiDeptData(StglActivityBo bo, StglActivity entity) {
|
||||
// 如果有多选部门ID数据(JSON格式字符串)
|
||||
if (StringUtils.isNotBlank(bo.getRelatedDeptIds())) {
|
||||
try {
|
||||
// 解析JSON格式的部门ID列表
|
||||
List<Long> deptIds = JsonUtils.parseArray(bo.getRelatedDeptIds(), Long.class);
|
||||
|
||||
if (deptIds != null && !deptIds.isEmpty()) {
|
||||
// 设置多选部门ID列表(JSON格式)
|
||||
entity.setRelatedDeptIds(JsonUtils.toJsonString(deptIds));
|
||||
|
||||
// 设置第一个部门作为主要关联部门(保持兼容性)
|
||||
entity.setRelatedDeptId(deptIds.get(0));
|
||||
|
||||
// 如果有多选部门名称,直接使用
|
||||
if (StringUtils.isNotBlank(bo.getRelatedDeptNames())) {
|
||||
entity.setRelatedDeptNames(bo.getRelatedDeptNames());
|
||||
|
||||
// 设置第一个部门名称作为主要关联部门名称
|
||||
String[] deptNames = bo.getRelatedDeptNames().split(",");
|
||||
if (deptNames.length > 0) {
|
||||
entity.setRelatedDeptName(deptNames[0].trim());
|
||||
}
|
||||
}
|
||||
|
||||
log.info("处理多选部门数据 - 活动ID: {}, 部门ID列表: {}, 部门名称列表: {}",
|
||||
entity.getId(), entity.getRelatedDeptIds(), entity.getRelatedDeptNames());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("解析多选部门数据失败: {}", bo.getRelatedDeptIds(), e);
|
||||
// 解析失败时清空多选字段
|
||||
entity.setRelatedDeptIds(null);
|
||||
entity.setRelatedDeptNames(null);
|
||||
}
|
||||
} else {
|
||||
// 没有多选部门数据,清空相关字段
|
||||
entity.setRelatedDeptIds(null);
|
||||
entity.setRelatedDeptNames(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
+205
@@ -0,0 +1,205 @@
|
||||
package org.dromara.stgl.service.impl;
|
||||
|
||||
import org.dromara.common.core.utils.MapstructUtils;
|
||||
import org.dromara.common.core.utils.StringUtils;
|
||||
import org.dromara.common.mybatis.core.page.TableDataInfo;
|
||||
import org.dromara.common.mybatis.core.page.PageQuery;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.dromara.stgl.domain.bo.StglClubAdvisorHistoryBo;
|
||||
import org.dromara.stgl.domain.vo.StglClubAdvisorHistoryVo;
|
||||
import org.dromara.stgl.domain.StglClubAdvisorHistory;
|
||||
import org.dromara.stgl.mapper.StglClubAdvisorHistoryMapper;
|
||||
import org.dromara.stgl.service.IStglClubAdvisorHistoryService;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 社团指导老师任职历史Service业务层处理
|
||||
*
|
||||
* @author Cursor AI
|
||||
* @date 2025-01-03
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
@Service
|
||||
public class StglClubAdvisorHistoryServiceImpl implements IStglClubAdvisorHistoryService {
|
||||
|
||||
private final StglClubAdvisorHistoryMapper baseMapper;
|
||||
|
||||
/**
|
||||
* 查询社团指导老师任职历史
|
||||
*/
|
||||
@Override
|
||||
public StglClubAdvisorHistoryVo queryById(Long id){
|
||||
return baseMapper.selectVoById(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询社团指导老师任职历史列表
|
||||
*/
|
||||
@Override
|
||||
public TableDataInfo<StglClubAdvisorHistoryVo> queryPageList(StglClubAdvisorHistoryBo bo, PageQuery pageQuery) {
|
||||
LambdaQueryWrapper<StglClubAdvisorHistory> lqw = buildQueryWrapper(bo);
|
||||
Page<StglClubAdvisorHistoryVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
|
||||
return TableDataInfo.build(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询社团指导老师任职历史列表
|
||||
*/
|
||||
@Override
|
||||
public List<StglClubAdvisorHistoryVo> queryList(StglClubAdvisorHistoryBo bo) {
|
||||
LambdaQueryWrapper<StglClubAdvisorHistory> lqw = buildQueryWrapper(bo);
|
||||
return baseMapper.selectVoList(lqw);
|
||||
}
|
||||
|
||||
private LambdaQueryWrapper<StglClubAdvisorHistory> buildQueryWrapper(StglClubAdvisorHistoryBo bo) {
|
||||
Map<String, Object> params = bo.getParams();
|
||||
LambdaQueryWrapper<StglClubAdvisorHistory> lqw = Wrappers.lambdaQuery();
|
||||
lqw.eq(bo.getClubId() != null, StglClubAdvisorHistory::getClubId, bo.getClubId());
|
||||
lqw.like(StringUtils.isNotBlank(bo.getAdvisorName()), StglClubAdvisorHistory::getAdvisorName, bo.getAdvisorName());
|
||||
lqw.like(StringUtils.isNotBlank(bo.getAdvisorDept()), StglClubAdvisorHistory::getAdvisorDept, bo.getAdvisorDept());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getAdvisorPhone()), StglClubAdvisorHistory::getAdvisorPhone, bo.getAdvisorPhone());
|
||||
lqw.like(StringUtils.isNotBlank(bo.getAdvisorEmail()), StglClubAdvisorHistory::getAdvisorEmail, bo.getAdvisorEmail());
|
||||
lqw.eq(bo.getAdvisorUserId() != null, StglClubAdvisorHistory::getAdvisorUserId, bo.getAdvisorUserId());
|
||||
lqw.eq(bo.getStatus() != null, StglClubAdvisorHistory::getStatus, bo.getStatus());
|
||||
lqw.eq(bo.getStartTime() != null, StglClubAdvisorHistory::getStartTime, bo.getStartTime());
|
||||
lqw.eq(bo.getEndTime() != null, StglClubAdvisorHistory::getEndTime, bo.getEndTime());
|
||||
return lqw;
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增社团指导老师任职历史
|
||||
*/
|
||||
@Override
|
||||
public Boolean insertByBo(StglClubAdvisorHistoryBo bo) {
|
||||
StglClubAdvisorHistory add = MapstructUtils.convert(bo, StglClubAdvisorHistory.class);
|
||||
validEntityBeforeSave(add);
|
||||
boolean flag = baseMapper.insert(add) > 0;
|
||||
if (flag) {
|
||||
bo.setId(add.getId());
|
||||
}
|
||||
return flag;
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改社团指导老师任职历史
|
||||
*/
|
||||
@Override
|
||||
public Boolean updateByBo(StglClubAdvisorHistoryBo bo) {
|
||||
StglClubAdvisorHistory update = MapstructUtils.convert(bo, StglClubAdvisorHistory.class);
|
||||
validEntityBeforeSave(update);
|
||||
return baseMapper.updateById(update) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存前的数据校验
|
||||
*/
|
||||
private void validEntityBeforeSave(StglClubAdvisorHistory entity){
|
||||
// TODO 做一些数据校验,如唯一约束
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量删除社团指导老师任职历史
|
||||
*/
|
||||
@Override
|
||||
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
|
||||
if(isValid){
|
||||
// TODO 做一些业务上的校验,判断是否需要校验
|
||||
}
|
||||
return baseMapper.deleteBatchIds(ids) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询指定社团的当前在任指导老师
|
||||
*/
|
||||
@Override
|
||||
public List<StglClubAdvisorHistoryVo> getCurrentAdvisorsByClubId(Long clubId) {
|
||||
return baseMapper.selectCurrentAdvisorsByClubId(clubId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询指定社团的历史指导老师(已离任)
|
||||
*/
|
||||
@Override
|
||||
public List<StglClubAdvisorHistoryVo> getHistoryAdvisorsByClubId(Long clubId) {
|
||||
return baseMapper.selectHistoryAdvisorsByClubId(clubId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询指定社团的所有指导老师(包含当前和历史)
|
||||
*/
|
||||
@Override
|
||||
public List<StglClubAdvisorHistoryVo> getAllAdvisorsByClubId(Long clubId) {
|
||||
return baseMapper.selectAllAdvisorsByClubId(clubId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新社团指导老师历史记录
|
||||
* 对比新旧指导老师列表,处理新增和离任
|
||||
*/
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void updateClubAdvisors(Long clubId, List<AdvisorInfo> newAdvisors) {
|
||||
updateClubAdvisors(clubId, newAdvisors, new Date());
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新社团指导老师历史记录(指定开始时间)
|
||||
* 对比新旧指导老师列表,处理新增和离任
|
||||
*/
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void updateClubAdvisors(Long clubId, List<AdvisorInfo> newAdvisors, Date startTime) {
|
||||
Date currentDate = new Date();
|
||||
|
||||
// 1. 获取当前指导老师列表
|
||||
List<StglClubAdvisorHistoryVo> currentRecords = getCurrentAdvisorsByClubId(clubId);
|
||||
|
||||
// 2. 建立姓名映射
|
||||
Set<String> currentNames = currentRecords.stream()
|
||||
.map(StglClubAdvisorHistoryVo::getAdvisorName)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
Set<String> newNames = newAdvisors.stream()
|
||||
.map(AdvisorInfo::getName)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
// 3. 处理被移除的指导老师
|
||||
List<String> removedNames = currentNames.stream()
|
||||
.filter(name -> !newNames.contains(name))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
if (!removedNames.isEmpty()) {
|
||||
baseMapper.batchSetHistoryStatus(clubId, removedNames, currentDate);
|
||||
}
|
||||
|
||||
// 4. 处理新增的指导老师
|
||||
for (AdvisorInfo newAdvisor : newAdvisors) {
|
||||
if (StringUtils.isNotBlank(newAdvisor.getName()) &&
|
||||
!currentNames.contains(newAdvisor.getName())) {
|
||||
|
||||
StglClubAdvisorHistory newRecord = new StglClubAdvisorHistory();
|
||||
newRecord.setClubId(clubId);
|
||||
newRecord.setAdvisorName(newAdvisor.getName());
|
||||
newRecord.setAdvisorDept(newAdvisor.getDept());
|
||||
newRecord.setAdvisorPhone(newAdvisor.getPhone());
|
||||
newRecord.setAdvisorEmail(newAdvisor.getEmail());
|
||||
newRecord.setAdvisorUserId(newAdvisor.getUserId());
|
||||
newRecord.setStatus(1);
|
||||
// 使用传入的开始时间,而不是当前时间
|
||||
newRecord.setStartTime(startTime != null ? startTime : currentDate);
|
||||
newRecord.setEndTime(null);
|
||||
|
||||
baseMapper.insert(newRecord);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+664
@@ -0,0 +1,664 @@
|
||||
package org.dromara.stgl.service.impl;
|
||||
|
||||
import org.dromara.common.core.utils.MapstructUtils;
|
||||
import org.dromara.common.core.utils.StringUtils;
|
||||
import org.dromara.common.mybatis.core.page.TableDataInfo;
|
||||
import org.dromara.common.mybatis.core.page.PageQuery;
|
||||
import org.dromara.common.satoken.utils.LoginHelper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.dromara.stgl.domain.bo.StglClubAnnualReviewBo;
|
||||
import org.dromara.stgl.domain.vo.StglClubAnnualReviewVo;
|
||||
import org.dromara.stgl.domain.StglClubAnnualReview;
|
||||
import org.dromara.stgl.domain.StglClubAnnualReviewMember;
|
||||
import org.dromara.stgl.domain.StglClubAnnualReviewActivity;
|
||||
import org.dromara.stgl.mapper.StglClubAnnualReviewMapper;
|
||||
import org.dromara.stgl.mapper.StglClubAnnualReviewMemberMapper;
|
||||
import org.dromara.stgl.mapper.StglClubAnnualReviewActivityMapper;
|
||||
import org.dromara.stgl.service.IStglClubAnnualReviewService;
|
||||
import org.dromara.stgl.service.IStglActivityService;
|
||||
import org.dromara.stgl.service.IStglClubService;
|
||||
import org.dromara.stgl.domain.bo.StglActivityBo;
|
||||
import org.dromara.stgl.domain.vo.StglClubVo;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
import java.util.HashSet;
|
||||
import java.util.ArrayList;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.Date;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.text.ParseException;
|
||||
import java.time.LocalDate;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* 社团年审记录Service业务层处理
|
||||
*
|
||||
* @author Cursor AI
|
||||
* @date 2025-01-15
|
||||
*/
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
@Service
|
||||
public class StglClubAnnualReviewServiceImpl implements IStglClubAnnualReviewService {
|
||||
|
||||
private final StglClubAnnualReviewMapper baseMapper;
|
||||
private final StglClubAnnualReviewMemberMapper memberMapper;
|
||||
private final StglClubAnnualReviewActivityMapper activityMapper;
|
||||
private final IStglActivityService stglActivityService;
|
||||
private final IStglClubService stglClubService;
|
||||
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
/**
|
||||
* 查询社团年审记录
|
||||
*/
|
||||
@Override
|
||||
public StglClubAnnualReviewVo queryById(Long id){
|
||||
StglClubAnnualReviewVo vo = baseMapper.selectVoById(id);
|
||||
if (vo == null) {
|
||||
log.warn("未找到年审记录,ID: {}", id);
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
// 查询关联的成员数据
|
||||
List<StglClubAnnualReviewMember> members = memberMapper.selectMembersByAnnualReviewId(id);
|
||||
|
||||
// 查询关联的活动数据
|
||||
List<StglClubAnnualReviewActivity> activities = activityMapper.selectActivitiesByAnnualReviewId(id);
|
||||
|
||||
// 将成员和活动数据转为JSON格式返回给前端
|
||||
if (!members.isEmpty()) {
|
||||
// 转换成员数据格式,匹配前端期望的字段名
|
||||
List<Map<String, Object>> membersList = new ArrayList<>();
|
||||
for (StglClubAnnualReviewMember member : members) {
|
||||
Map<String, Object> memberMap = new HashMap<>();
|
||||
memberMap.put("name", member.getMemberName());
|
||||
memberMap.put("className", member.getClassName());
|
||||
memberMap.put("role", member.getRole());
|
||||
memberMap.put("phone", member.getContactInfo());
|
||||
membersList.add(memberMap);
|
||||
}
|
||||
vo.setMembersJson(objectMapper.writeValueAsString(membersList));
|
||||
} else {
|
||||
vo.setMembersJson("[]"); // 设置空数组而不是null
|
||||
}
|
||||
|
||||
if (!activities.isEmpty()) {
|
||||
// 转换活动数据格式,匹配前端期望的字段名
|
||||
List<Map<String, Object>> activitiesList = new ArrayList<>();
|
||||
for (StglClubAnnualReviewActivity activity : activities) {
|
||||
Map<String, Object> activityMap = new HashMap<>();
|
||||
activityMap.put("activityName", activity.getActivityName());
|
||||
activityMap.put("location", activity.getActivityLocation());
|
||||
activityMap.put("participantCount", activity.getParticipantCount());
|
||||
|
||||
// 格式化活动时间显示 - 优先使用新的开始/结束时间字段
|
||||
if (activity.getActivityStartTime() != null) {
|
||||
activityMap.put("activityStartTime", activity.getActivityStartTime().toString());
|
||||
}
|
||||
if (activity.getActivityEndTime() != null) {
|
||||
activityMap.put("activityEndTime", activity.getActivityEndTime().toString());
|
||||
}
|
||||
// 保持向后兼容
|
||||
if (activity.getActivityTime() != null) {
|
||||
activityMap.put("activityDate", activity.getActivityTime().toString());
|
||||
} else {
|
||||
activityMap.put("activityDate", "");
|
||||
}
|
||||
|
||||
activitiesList.add(activityMap);
|
||||
}
|
||||
vo.setActivitiesJson(objectMapper.writeValueAsString(activitiesList));
|
||||
} else {
|
||||
vo.setActivitiesJson("[]"); // 设置空数组而不是null
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("处理年审记录关联数据失败,ID: {}", id, e);
|
||||
// 确保JSON字段不为null
|
||||
if (vo.getMembersJson() == null) {
|
||||
vo.setMembersJson("[]");
|
||||
}
|
||||
if (vo.getActivitiesJson() == null) {
|
||||
vo.setActivitiesJson("[]");
|
||||
}
|
||||
}
|
||||
|
||||
return vo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询社团年审记录列表
|
||||
*/
|
||||
@Override
|
||||
public TableDataInfo<StglClubAnnualReviewVo> queryPageList(StglClubAnnualReviewBo bo, PageQuery pageQuery) {
|
||||
LambdaQueryWrapper<StglClubAnnualReview> lqw = buildQueryWrapper(bo);
|
||||
Page<StglClubAnnualReviewVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
|
||||
return TableDataInfo.build(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询社团年审记录列表
|
||||
*/
|
||||
@Override
|
||||
public List<StglClubAnnualReviewVo> queryList(StglClubAnnualReviewBo bo) {
|
||||
LambdaQueryWrapper<StglClubAnnualReview> lqw = buildQueryWrapper(bo);
|
||||
return baseMapper.selectVoList(lqw);
|
||||
}
|
||||
|
||||
private LambdaQueryWrapper<StglClubAnnualReview> buildQueryWrapper(StglClubAnnualReviewBo bo) {
|
||||
Map<String, Object> params = bo.getParams();
|
||||
LambdaQueryWrapper<StglClubAnnualReview> lqw = Wrappers.lambdaQuery();
|
||||
lqw.eq(bo.getClubId() != null, StglClubAnnualReview::getClubId, bo.getClubId());
|
||||
lqw.eq(bo.getReviewYear() != null, StglClubAnnualReview::getReviewYear, bo.getReviewYear());
|
||||
lqw.between(params.get("beginReviewStartDate") != null && params.get("endReviewStartDate") != null,
|
||||
StglClubAnnualReview::getReviewStartDate, params.get("beginReviewStartDate"), params.get("endReviewStartDate"));
|
||||
lqw.between(params.get("beginReviewEndDate") != null && params.get("endReviewEndDate") != null,
|
||||
StglClubAnnualReview::getReviewEndDate, params.get("beginReviewEndDate"), params.get("endReviewEndDate"));
|
||||
lqw.like(StringUtils.isNotBlank(bo.getClubName()), StglClubAnnualReview::getClubName, bo.getClubName());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getClubType()), StglClubAnnualReview::getClubType, bo.getClubType());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getStatus()), StglClubAnnualReview::getStatus, bo.getStatus());
|
||||
lqw.orderByDesc(StglClubAnnualReview::getCreateTime);
|
||||
return lqw;
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增社团年审记录
|
||||
*/
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Boolean insertByBo(StglClubAnnualReviewBo bo) {
|
||||
// 检查是否可以创建年审记录
|
||||
if (!checkCanCreateReview(bo.getClubId(), bo.getReviewYear())) {
|
||||
throw new RuntimeException("该社团在该年度已存在年审记录");
|
||||
}
|
||||
|
||||
StglClubAnnualReview add = MapstructUtils.convert(bo, StglClubAnnualReview.class);
|
||||
// 设置默认状态为草稿
|
||||
if (StringUtils.isBlank(add.getStatus())) {
|
||||
add.setStatus("0");
|
||||
}
|
||||
validEntityBeforeSave(add);
|
||||
boolean flag = baseMapper.insert(add) > 0;
|
||||
if (flag) {
|
||||
bo.setId(add.getId());
|
||||
|
||||
// 保存成员数据
|
||||
saveMembers(add.getId(), bo.getMembersJson());
|
||||
|
||||
// 保存活动数据
|
||||
saveActivities(add.getId(), bo.getActivitiesJson());
|
||||
}
|
||||
return flag;
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改社团年审记录
|
||||
*/
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Boolean updateByBo(StglClubAnnualReviewBo bo) {
|
||||
StglClubAnnualReview update = MapstructUtils.convert(bo, StglClubAnnualReview.class);
|
||||
validEntityBeforeSave(update);
|
||||
boolean flag = baseMapper.updateById(update) > 0;
|
||||
|
||||
if (flag) {
|
||||
// 保存成员数据
|
||||
saveMembers(bo.getId(), bo.getMembersJson());
|
||||
|
||||
// 保存活动数据
|
||||
saveActivities(bo.getId(), bo.getActivitiesJson());
|
||||
}
|
||||
|
||||
return flag;
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存前的数据校验
|
||||
*/
|
||||
private void validEntityBeforeSave(StglClubAnnualReview entity){
|
||||
// 可以在此处进行额外的业务校验
|
||||
if (entity.getReviewStartDate() != null && entity.getReviewEndDate() != null) {
|
||||
if (entity.getReviewStartDate().after(entity.getReviewEndDate())) {
|
||||
throw new RuntimeException("年审开始日期不能晚于结束日期");
|
||||
}
|
||||
}
|
||||
|
||||
// 自动填充社长信息(如果社长姓名为空且有社团ID)
|
||||
if (entity.getClubId() != null && StringUtils.isBlank(entity.getPresidentName())) {
|
||||
StglClubVo clubVo = stglClubService.queryById(entity.getClubId());
|
||||
if (clubVo != null) {
|
||||
entity.setPresidentName(clubVo.getPresidentName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存成员数据 - 优化版本:精确删除而非全量重建
|
||||
*/
|
||||
private void saveMembers(Long annualReviewId, String membersJson) {
|
||||
if (StringUtils.isBlank(membersJson)) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
// 1. 获取现有成员数据
|
||||
List<StglClubAnnualReviewMember> existingMembers =
|
||||
memberMapper.selectMembersByAnnualReviewId(annualReviewId);
|
||||
|
||||
// 2. 解析前端提交的数据
|
||||
List<Map<String, Object>> newMembersList = objectMapper.readValue(membersJson,
|
||||
new TypeReference<List<Map<String, Object>>>() {});
|
||||
|
||||
// 3. 构建要保留的成员标识集合
|
||||
Set<String> keepMemberKeys = newMembersList.stream()
|
||||
.map(this::generateMemberKey)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
// 4. 只逻辑删除不在保留列表中的成员
|
||||
for (StglClubAnnualReviewMember existingMember : existingMembers) {
|
||||
String existingKey = generateMemberKey(existingMember);
|
||||
if (!keepMemberKeys.contains(existingKey)) {
|
||||
// 逻辑删除该成员
|
||||
memberMapper.deleteById(existingMember.getId());
|
||||
log.info("删除年审成员记录: annualReviewId={}, memberName={}, className={}",
|
||||
annualReviewId, existingMember.getMemberName(), existingMember.getClassName());
|
||||
}
|
||||
}
|
||||
|
||||
// 5. 新增不存在的成员(如果有的话 - 虽然当前页面只支持删除)
|
||||
Set<String> existingMemberKeys = existingMembers.stream()
|
||||
.map(this::generateMemberKey)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
for (Map<String, Object> memberData : newMembersList) {
|
||||
String memberKey = generateMemberKey(memberData);
|
||||
if (!existingMemberKeys.contains(memberKey)) {
|
||||
// 新增成员
|
||||
StglClubAnnualReviewMember member = new StglClubAnnualReviewMember();
|
||||
member.setAnnualReviewId(annualReviewId);
|
||||
member.setMemberName((String) memberData.get("name"));
|
||||
member.setClassName((String) memberData.get("className"));
|
||||
member.setRole((String) memberData.get("role"));
|
||||
member.setContactInfo((String) memberData.get("phone"));
|
||||
|
||||
// 注意:userId字段已从数据库表中删除,不再设置
|
||||
|
||||
memberMapper.insert(member);
|
||||
log.info("新增年审成员记录: annualReviewId={}, memberName={}, className={}",
|
||||
annualReviewId, member.getMemberName(), member.getClassName());
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("保存成员数据失败: annualReviewId={}", annualReviewId, e);
|
||||
throw new RuntimeException("保存成员数据失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成成员唯一标识 - 优先使用userId,其次使用姓名+班级组合
|
||||
*/
|
||||
private String generateMemberKey(Map<String, Object> memberData) {
|
||||
// 如果有user_id,优先使用
|
||||
Object userId = memberData.get("userId");
|
||||
if (userId != null && StringUtils.isNotBlank(userId.toString())) {
|
||||
return "user_" + userId.toString();
|
||||
}
|
||||
// 否则使用姓名+班级的组合
|
||||
String name = (String) memberData.get("name");
|
||||
String className = (String) memberData.get("className");
|
||||
return (name != null ? name : "") + "_" + (className != null ? className : "");
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成成员唯一标识 - 实体对象版本
|
||||
*/
|
||||
private String generateMemberKey(StglClubAnnualReviewMember member) {
|
||||
// userId字段已删除,直接使用姓名+班级的组合
|
||||
String name = member.getMemberName();
|
||||
String className = member.getClassName();
|
||||
return (name != null ? name : "") + "_" + (className != null ? className : "");
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存活动数据 - 基于ID的精确匹配版本
|
||||
*/
|
||||
private void saveActivities(Long annualReviewId, String activitiesJson) {
|
||||
if (StringUtils.isBlank(activitiesJson)) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
// 1. 获取现有活动数据
|
||||
List<StglClubAnnualReviewActivity> existingActivities =
|
||||
activityMapper.selectActivitiesByAnnualReviewId(annualReviewId);
|
||||
|
||||
// 2. 解析前端提交的数据
|
||||
List<Map<String, Object>> newActivitiesList = objectMapper.readValue(activitiesJson,
|
||||
new TypeReference<List<Map<String, Object>>>() {});
|
||||
|
||||
// 3. 构建要保留的活动ID集合(只包含有ID的活动,即数据库中已存在的)
|
||||
Set<Long> keepActivityIds = new HashSet<>();
|
||||
List<Map<String, Object>> newActivities = new ArrayList<>();
|
||||
|
||||
for (Map<String, Object> activity : newActivitiesList) {
|
||||
Object idObj = activity.get("id");
|
||||
if (idObj != null) {
|
||||
// 现有活动,添加到保留ID集合
|
||||
Long activityId = Long.valueOf(idObj.toString());
|
||||
keepActivityIds.add(activityId);
|
||||
} else {
|
||||
// 新活动,添加到新增列表
|
||||
newActivities.add(activity);
|
||||
}
|
||||
}
|
||||
|
||||
// 4. 删除不在保留列表中的活动
|
||||
for (StglClubAnnualReviewActivity existingActivity : existingActivities) {
|
||||
if (!keepActivityIds.contains(existingActivity.getId())) {
|
||||
// 逻辑删除该活动
|
||||
activityMapper.deleteById(existingActivity.getId());
|
||||
}
|
||||
}
|
||||
|
||||
// 5. 新增活动
|
||||
for (Map<String, Object> activityData : newActivities) {
|
||||
StglClubAnnualReviewActivity activity = new StglClubAnnualReviewActivity();
|
||||
activity.setAnnualReviewId(annualReviewId);
|
||||
activity.setActivityName((String) activityData.get("activityName"));
|
||||
activity.setActivityLocation((String) activityData.get("location"));
|
||||
activity.setParticipantCount(activityData.get("participantCount") != null ?
|
||||
Integer.parseInt(activityData.get("participantCount").toString()) : 0);
|
||||
|
||||
// 处理时间字段
|
||||
parseAndSetActivityTimes(activity, activityData);
|
||||
|
||||
activityMapper.insert(activity);
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("保存活动数据失败: annualReviewId={}", annualReviewId, e);
|
||||
throw new RuntimeException("保存活动数据失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析并设置活动时间
|
||||
*/
|
||||
private void parseAndSetActivityTimes(StglClubAnnualReviewActivity activity, Map<String, Object> activityData) {
|
||||
// 处理活动日期 (保持兼容性)
|
||||
if (activityData.get("activityDate") != null) {
|
||||
String dateStr = activityData.get("activityDate").toString();
|
||||
if (StringUtils.isNotBlank(dateStr)) {
|
||||
try {
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
|
||||
Date activityDate = sdf.parse(dateStr);
|
||||
activity.setActivityTime(activityDate);
|
||||
} catch (ParseException e) {
|
||||
log.warn("解析活动时间失败: {}", dateStr, e);
|
||||
activity.setActivityTime(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 处理活动开始时间
|
||||
if (activityData.get("activityStartTime") != null) {
|
||||
String startTimeStr = activityData.get("activityStartTime").toString();
|
||||
if (StringUtils.isNotBlank(startTimeStr)) {
|
||||
try {
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
Date startTime = sdf.parse(startTimeStr);
|
||||
activity.setActivityStartTime(startTime);
|
||||
} catch (ParseException e) {
|
||||
try {
|
||||
SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy-MM-dd HH:mm");
|
||||
Date startTime = sdf2.parse(startTimeStr);
|
||||
activity.setActivityStartTime(startTime);
|
||||
} catch (ParseException e2) {
|
||||
log.warn("解析活动开始时间失败: {}", startTimeStr, e2);
|
||||
activity.setActivityStartTime(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 处理活动结束时间
|
||||
if (activityData.get("activityEndTime") != null) {
|
||||
String endTimeStr = activityData.get("activityEndTime").toString();
|
||||
if (StringUtils.isNotBlank(endTimeStr)) {
|
||||
try {
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
Date endTime = sdf.parse(endTimeStr);
|
||||
activity.setActivityEndTime(endTime);
|
||||
} catch (ParseException e) {
|
||||
try {
|
||||
SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy-MM-dd HH:mm");
|
||||
Date endTime = sdf2.parse(endTimeStr);
|
||||
activity.setActivityEndTime(endTime);
|
||||
} catch (ParseException e2) {
|
||||
log.warn("解析活动结束时间失败: {}", endTimeStr, e2);
|
||||
activity.setActivityEndTime(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成活动唯一标识 - 优先使用activityId,其次使用活动名称+时间+地点组合
|
||||
*/
|
||||
private String generateActivityKey(Map<String, Object> activityData) {
|
||||
// 如果有activity_id,优先使用
|
||||
Object activityId = activityData.get("activityId");
|
||||
if (activityId != null && StringUtils.isNotBlank(activityId.toString())) {
|
||||
return "activity_" + activityId.toString();
|
||||
}
|
||||
// 否则使用活动名称+时间+地点的组合
|
||||
String name = (String) activityData.get("activityName");
|
||||
String location = (String) activityData.get("location");
|
||||
String time = "";
|
||||
|
||||
// 优先使用开始时间,其次使用活动日期
|
||||
if (activityData.get("activityStartTime") != null) {
|
||||
time = activityData.get("activityStartTime").toString();
|
||||
} else if (activityData.get("activityDate") != null) {
|
||||
time = activityData.get("activityDate").toString();
|
||||
}
|
||||
|
||||
return (name != null ? name : "") + "_" + time + "_" + (location != null ? location : "");
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成活动唯一标识 - 实体对象版本
|
||||
*/
|
||||
private String generateActivityKey(StglClubAnnualReviewActivity activity) {
|
||||
// activityId字段已删除,直接使用活动名称+时间+地点的组合
|
||||
String name = activity.getActivityName();
|
||||
String location = activity.getActivityLocation();
|
||||
String time = "";
|
||||
|
||||
// 优先使用开始时间,其次使用活动时间
|
||||
if (activity.getActivityStartTime() != null) {
|
||||
time = activity.getActivityStartTime().toString();
|
||||
} else if (activity.getActivityTime() != null) {
|
||||
time = activity.getActivityTime().toString();
|
||||
}
|
||||
|
||||
return (name != null ? name : "") + "_" + time + "_" + (location != null ? location : "");
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量删除社团年审记录
|
||||
*/
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
|
||||
if(isValid){
|
||||
// 做一些业务上的校验,判断是否需要校验
|
||||
for (Long id : ids) {
|
||||
StglClubAnnualReviewVo vo = queryById(id);
|
||||
if (vo != null && !"0".equals(vo.getStatus())) {
|
||||
throw new RuntimeException("只能删除草稿状态的年审记录");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 删除关联的成员和活动数据
|
||||
for (Long id : ids) {
|
||||
memberMapper.deleteMembersByAnnualReviewId(id);
|
||||
activityMapper.deleteActivitiesByAnnualReviewId(id);
|
||||
}
|
||||
|
||||
return baseMapper.deleteBatchIds(ids) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 提交年审记录
|
||||
*/
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Boolean submitReview(Long id) {
|
||||
StglClubAnnualReview entity = baseMapper.selectById(id);
|
||||
if (entity == null) {
|
||||
throw new RuntimeException("年审记录不存在");
|
||||
}
|
||||
if (!"0".equals(entity.getStatus())) {
|
||||
throw new RuntimeException("只能提交草稿状态的年审记录");
|
||||
}
|
||||
|
||||
entity.setStatus("1");
|
||||
entity.setReviewTime(new Date());
|
||||
return baseMapper.updateById(entity) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 撤回年审记录
|
||||
*/
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Boolean withdrawReview(Long id) {
|
||||
StglClubAnnualReview entity = baseMapper.selectById(id);
|
||||
if (entity == null) {
|
||||
throw new RuntimeException("年审记录不存在");
|
||||
}
|
||||
if (!"1".equals(entity.getStatus())) {
|
||||
throw new RuntimeException("只能撤回已提交状态的年审记录");
|
||||
}
|
||||
|
||||
entity.setStatus("0");
|
||||
return baseMapper.updateById(entity) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 审核年审记录
|
||||
*/
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Boolean auditReview(Long id, String auditResult, String auditOpinion, String auditType) {
|
||||
StglClubAnnualReview entity = baseMapper.selectById(id);
|
||||
if (entity == null) {
|
||||
throw new RuntimeException("年审记录不存在");
|
||||
}
|
||||
|
||||
// 根据审核类型设置意见
|
||||
switch (auditType) {
|
||||
case "advisor":
|
||||
entity.setAdvisorOpinion(auditOpinion);
|
||||
break;
|
||||
case "college":
|
||||
entity.setCollegeOpinion(auditOpinion);
|
||||
break;
|
||||
case "youth_league":
|
||||
entity.setYouthLeagueOpinion(auditOpinion);
|
||||
break;
|
||||
default:
|
||||
throw new RuntimeException("无效的审核类型");
|
||||
}
|
||||
|
||||
// 设置审核结果状态
|
||||
entity.setStatus("3".equals(auditResult) ? "3" : "4");
|
||||
|
||||
return baseMapper.updateById(entity) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前用户的年审记录列表
|
||||
*/
|
||||
@Override
|
||||
public TableDataInfo<StglClubAnnualReviewVo> queryMyPageList(StglClubAnnualReviewBo bo, PageQuery pageQuery) {
|
||||
// 这里需要根据实际业务逻辑,通过用户ID查询其所属社团的年审记录
|
||||
// 暂时使用相同的查询逻辑,实际应该加上用户权限过滤
|
||||
return queryPageList(bo, pageQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否可以创建年审记录
|
||||
*/
|
||||
@Override
|
||||
public Boolean checkCanCreateReview(Long clubId, Integer reviewYear) {
|
||||
LambdaQueryWrapper<StglClubAnnualReview> lqw = Wrappers.lambdaQuery();
|
||||
lqw.eq(StglClubAnnualReview::getClubId, clubId);
|
||||
lqw.eq(StglClubAnnualReview::getReviewYear, reviewYear);
|
||||
return baseMapper.selectCount(lqw) == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据社团ID和年审年度获取该年度的活动数据
|
||||
* @param clubId 社团ID
|
||||
* @param reviewYear 年审年度 (例如:2024)
|
||||
* @return 活动列表 - 只包含已通过、进行中、已结束的活动
|
||||
*/
|
||||
@Override
|
||||
public List<org.dromara.stgl.domain.vo.StglActivityVo> getClubActivitiesByReviewYear(Long clubId, Integer reviewYear) {
|
||||
// 计算年审年度的时间范围:年审年度的1月1日到12月31日
|
||||
LocalDate startDate = LocalDate.of(reviewYear, 1, 1);
|
||||
LocalDate endDate = LocalDate.of(reviewYear, 12, 31);
|
||||
|
||||
// 构建查询条件
|
||||
StglActivityBo bo = new StglActivityBo();
|
||||
bo.setRelatedClubId(clubId); // 设置社团ID
|
||||
bo.setRelatedType(1); // 关联类型为社团(1表示社团,2表示志愿团队)
|
||||
|
||||
// 设置时间范围查询条件(查询活动开始时间在该年度内的活动)
|
||||
bo.setStartTime(java.sql.Date.valueOf(startDate));
|
||||
bo.setEndTime(java.sql.Date.valueOf(endDate));
|
||||
|
||||
// 查询活动列表
|
||||
List<org.dromara.stgl.domain.vo.StglActivityVo> allActivities = stglActivityService.queryList(bo);
|
||||
|
||||
// 过滤活动状态:只保留已通过(3)、进行中(4)、已结束(5)的活动
|
||||
return allActivities.stream()
|
||||
.filter(activity -> {
|
||||
String status = activity.getActivityStatus();
|
||||
return "3".equals(status) || "4".equals(status) || "5".equals(status);
|
||||
})
|
||||
.collect(java.util.stream.Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据年审记录ID获取对应的活动数据
|
||||
*/
|
||||
@Override
|
||||
public List<org.dromara.stgl.domain.vo.StglClubAnnualReviewActivityVo> getAnnualReviewActivities(Long annualReviewId) {
|
||||
LambdaQueryWrapper<StglClubAnnualReviewActivity> lqw = new LambdaQueryWrapper<>();
|
||||
lqw.eq(StglClubAnnualReviewActivity::getAnnualReviewId, annualReviewId);
|
||||
lqw.orderByAsc(StglClubAnnualReviewActivity::getActivityStartTime);
|
||||
|
||||
List<StglClubAnnualReviewActivity> activities = activityMapper.selectList(lqw);
|
||||
return BeanUtil.copyToList(activities, org.dromara.stgl.domain.vo.StglClubAnnualReviewActivityVo.class);
|
||||
}
|
||||
|
||||
}
|
||||
+263
@@ -0,0 +1,263 @@
|
||||
package org.dromara.stgl.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.stgl.domain.StglClub;
|
||||
import org.dromara.stgl.domain.vo.ClubActivityPermissionResult;
|
||||
import org.dromara.stgl.mapper.StglClubMapper;
|
||||
import org.dromara.stgl.service.IStglClubPermissionService;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* 社团通知定时任务
|
||||
* 仅用于发送年审提醒通知,不管理权限状态
|
||||
*
|
||||
* @author Cursor AI
|
||||
* @date 2025-01-15
|
||||
*/
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
@Component
|
||||
@ConditionalOnProperty(name = "stgl.club.notification.enabled", havingValue = "true", matchIfMissing = true)
|
||||
public class StglClubNotificationScheduler {
|
||||
|
||||
private final StglClubMapper clubMapper;
|
||||
private final IStglClubPermissionService clubPermissionService;
|
||||
|
||||
private final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
/**
|
||||
* 每天早上8点发送年审相关通知
|
||||
* 通过实时权限检查确定通知类型,不存储状态
|
||||
*/
|
||||
@Scheduled(cron = "0 0 8 * * ?")
|
||||
public void sendDailyAnnualReviewReminders() {
|
||||
log.info("开始执行社团年审通知任务");
|
||||
|
||||
try {
|
||||
Date today = new Date();
|
||||
|
||||
// 获取所有正常状态的社团
|
||||
List<StglClub> allClubs = getAllActiveClubs();
|
||||
log.info("获取到活跃社团数量: {}", allClubs.size());
|
||||
|
||||
int todayLimitedCount = 0;
|
||||
int urgentReminderCount = 0;
|
||||
int permanentlyLimitedCount = 0;
|
||||
|
||||
for (StglClub club : allClubs) {
|
||||
try {
|
||||
// 实时检查权限状态
|
||||
ClubActivityPermissionResult permission =
|
||||
clubPermissionService.checkClubActivityPermission(club.getId());
|
||||
|
||||
switch (permission.getStatus()) {
|
||||
case "TEMPORARILY_LIMITED":
|
||||
handleTemporarilyLimitedClub(club, permission);
|
||||
if (isFirstDayOfRestriction(club, today)) {
|
||||
todayLimitedCount++;
|
||||
}
|
||||
if (isUrgentReminder(permission)) {
|
||||
urgentReminderCount++;
|
||||
}
|
||||
break;
|
||||
|
||||
case "PERMANENTLY_LIMITED":
|
||||
if (isFirstDayOfPermanentRestriction(club, today)) {
|
||||
handlePermanentlyLimitedClub(club);
|
||||
permanentlyLimitedCount++;
|
||||
}
|
||||
break;
|
||||
|
||||
case "NORMAL":
|
||||
// 正常状态无需通知
|
||||
break;
|
||||
|
||||
default:
|
||||
log.warn("未知的权限状态: {}, 社团ID: {}", permission.getStatus(), club.getId());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("处理社团通知失败,社团ID: {}", club.getId(), e);
|
||||
}
|
||||
}
|
||||
|
||||
log.info("社团年审通知任务完成 - 今日开始限制: {}, 紧急提醒: {}, 永久限制: {}",
|
||||
todayLimitedCount, urgentReminderCount, permanentlyLimitedCount);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("执行社团年审通知任务失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理临时限制状态的社团
|
||||
*/
|
||||
private void handleTemporarilyLimitedClub(StglClub club, ClubActivityPermissionResult permission) {
|
||||
if (permission.getRemainingDays() != null) {
|
||||
if (permission.getRemainingDays() <= 3) {
|
||||
// 剩余3天内,发送紧急提醒
|
||||
sendUrgentAnnualReviewReminder(club, permission);
|
||||
} else if (permission.getRemainingDays() >= 12) {
|
||||
// 刚开始限制(剩余12天以上),发送开始通知
|
||||
sendAnnualReviewStartNotification(club, permission);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理永久限制状态的社团
|
||||
*/
|
||||
private void handlePermanentlyLimitedClub(StglClub club) {
|
||||
sendPermanentRestrictionNotification(club);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送年审开始通知
|
||||
*/
|
||||
private void sendAnnualReviewStartNotification(StglClub club, ClubActivityPermissionResult permission) {
|
||||
String message = String.format(
|
||||
"【年审提醒】尊敬的%s社团,您的社团从今天开始进入年审期,活动发起功能已限制。" +
|
||||
"请在%d天内完成年审提交,截止时间:%s。" +
|
||||
"提醒:超过期限将永久限制活动发起功能。",
|
||||
club.getClubName(),
|
||||
permission.getRemainingDays(),
|
||||
dateFormat.format(permission.getDeadlineTime())
|
||||
);
|
||||
|
||||
log.info("发送年审开始通知 - 社团: {}, 剩余天数: {}", club.getClubName(), permission.getRemainingDays());
|
||||
sendNotificationToClub(club, "年审开始通知", message);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送紧急年审提醒
|
||||
*/
|
||||
private void sendUrgentAnnualReviewReminder(StglClub club, ClubActivityPermissionResult permission) {
|
||||
String message = String.format(
|
||||
"【紧急提醒】%s社团,您的年审即将到期!" +
|
||||
"仅剩%d天%d小时,截止时间:%s。" +
|
||||
"请立即提交年审,否则将永久限制活动发起功能!",
|
||||
club.getClubName(),
|
||||
permission.getRemainingDays(),
|
||||
permission.getRemainingHours() % 24,
|
||||
dateFormat.format(permission.getDeadlineTime())
|
||||
);
|
||||
|
||||
log.warn("发送紧急年审提醒 - 社团: {}, 剩余: {}天{}小时",
|
||||
club.getClubName(), permission.getRemainingDays(), permission.getRemainingHours() % 24);
|
||||
sendNotificationToClub(club, "紧急年审提醒", message);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送永久限制通知
|
||||
*/
|
||||
private void sendPermanentRestrictionNotification(StglClub club) {
|
||||
String message = String.format(
|
||||
"【权限限制】%s社团,很遗憾通知您,由于超过年审提交期限," +
|
||||
"您的社团活动发起功能已被永久限制。即使现在补交年审也无法恢复权限。" +
|
||||
"如有疑问,请联系管理员。",
|
||||
club.getClubName()
|
||||
);
|
||||
|
||||
log.warn("发送永久限制通知 - 社团: {}", club.getClubName());
|
||||
sendNotificationToClub(club, "活动权限永久限制", message);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送通知到社团(实际发送逻辑)
|
||||
*/
|
||||
private void sendNotificationToClub(StglClub club, String title, String message) {
|
||||
try {
|
||||
// TODO: 集成实际的通知系统
|
||||
// 可以是站内信、邮件、短信等
|
||||
log.info("通知发送 - 社团: {}, 标题: {}, 内容: {}", club.getClubName(), title, message);
|
||||
|
||||
// 示例:记录通知日志
|
||||
recordNotificationLog(club.getId(), title, message);
|
||||
} catch (Exception e) {
|
||||
log.error("发送通知失败 - 社团: {}, 标题: {}", club.getClubName(), title, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录通知日志
|
||||
*/
|
||||
private void recordNotificationLog(Long clubId, String title, String message) {
|
||||
// TODO: 将通知记录保存到数据库,用于审计和跟踪
|
||||
log.debug("记录通知日志 - 社团ID: {}, 标题: {}", clubId, title);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有活跃的社团
|
||||
*/
|
||||
private List<StglClub> getAllActiveClubs() {
|
||||
LambdaQueryWrapper<StglClub> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(StglClub::getStatus, 1) // 1-正常状态
|
||||
.isNotNull(StglClub::getEstablishDate); // 必须有成立日期
|
||||
return clubMapper.selectList(wrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否是限制的第一天
|
||||
*/
|
||||
private boolean isFirstDayOfRestriction(StglClub club, Date today) {
|
||||
try {
|
||||
ClubActivityPermissionResult permission =
|
||||
clubPermissionService.checkClubActivityPermission(club.getId());
|
||||
|
||||
if (!"TEMPORARILY_LIMITED".equals(permission.getStatus())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 如果剩余天数接近15天,说明是刚开始限制
|
||||
return permission.getRemainingDays() != null && permission.getRemainingDays() >= 14;
|
||||
} catch (Exception e) {
|
||||
log.error("判断限制第一天失败,社团ID: {}", club.getId(), e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否需要紧急提醒
|
||||
*/
|
||||
private boolean isUrgentReminder(ClubActivityPermissionResult permission) {
|
||||
return permission.getRemainingDays() != null && permission.getRemainingDays() <= 3;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否是永久限制的第一天
|
||||
*/
|
||||
private boolean isFirstDayOfPermanentRestriction(StglClub club, Date today) {
|
||||
try {
|
||||
// 检查昨天的状态是否还是临时限制
|
||||
Date yesterday = new Date(today.getTime() - TimeUnit.DAYS.toMillis(1));
|
||||
|
||||
// 通过计算年审截止时间来判断
|
||||
// 如果今天刚好是截止后的第一天,就发送通知
|
||||
Date establishDate = club.getEstablishDate();
|
||||
if (establishDate == null) return false;
|
||||
|
||||
Calendar cal = Calendar.getInstance();
|
||||
cal.setTime(establishDate);
|
||||
cal.set(Calendar.YEAR, Calendar.getInstance().get(Calendar.YEAR));
|
||||
cal.add(Calendar.DAY_OF_MONTH, 15);
|
||||
|
||||
Date deadlineDate = cal.getTime();
|
||||
|
||||
// 如果今天是截止日期后的1-2天内,认为是刚刚永久限制
|
||||
long daysSinceDeadline = TimeUnit.MILLISECONDS.toDays(
|
||||
today.getTime() - deadlineDate.getTime());
|
||||
|
||||
return daysSinceDeadline >= 0 && daysSinceDeadline <= 1;
|
||||
} catch (Exception e) {
|
||||
log.error("判断永久限制第一天失败,社团ID: {}", club.getId(), e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
+240
@@ -0,0 +1,240 @@
|
||||
package org.dromara.stgl.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.common.core.exception.ServiceException;
|
||||
import org.dromara.stgl.domain.StglClub;
|
||||
import org.dromara.stgl.domain.StglClubAnnualReview;
|
||||
import org.dromara.stgl.domain.vo.ClubActivityPermissionResult;
|
||||
import org.dromara.stgl.mapper.StglClubMapper;
|
||||
import org.dromara.stgl.mapper.StglClubAnnualReviewMapper;
|
||||
import org.dromara.stgl.service.IStglClubPermissionService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 社团权限管理Service业务层处理
|
||||
*
|
||||
* @author Cursor AI
|
||||
* @date 2025-01-15
|
||||
*/
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
@Service
|
||||
public class StglClubPermissionServiceImpl implements IStglClubPermissionService {
|
||||
|
||||
private final StglClubMapper clubMapper;
|
||||
private final StglClubAnnualReviewMapper annualReviewMapper;
|
||||
|
||||
/**
|
||||
* 年审窗口期天数(15天)
|
||||
*/
|
||||
private static final int ANNUAL_REVIEW_WINDOW_DAYS = 15;
|
||||
|
||||
/**
|
||||
* 检查社团活动权限(实时计算,精确到秒级)
|
||||
*/
|
||||
@Override
|
||||
public ClubActivityPermissionResult checkClubActivityPermission(Long clubId) {
|
||||
log.debug("开始检查社团权限,clubId: {}", clubId);
|
||||
|
||||
// 1. 获取社团基础信息
|
||||
StglClub clubInfo = clubMapper.selectById(clubId);
|
||||
if (clubInfo == null) {
|
||||
throw new ServiceException("社团不存在,ID: " + clubId);
|
||||
}
|
||||
|
||||
Date establishDate = clubInfo.getEstablishDate();
|
||||
if (establishDate == null) {
|
||||
throw new ServiceException("社团成立日期不能为空");
|
||||
}
|
||||
|
||||
Date currentTime = new Date(); // 当前精确时间(到毫秒)
|
||||
|
||||
// 2. 计算关键时间点(精确到秒)
|
||||
Date annualReviewStartTime = calculateAnnualReviewStartTime(establishDate, currentTime);
|
||||
Date annualReviewDeadlineTime = addDays(annualReviewStartTime, ANNUAL_REVIEW_WINDOW_DAYS);
|
||||
|
||||
log.debug("社团ID: {}, 成立时间: {}, 年审开始时间: {}, 年审截止时间: {}, 当前时间: {}",
|
||||
clubId, establishDate, annualReviewStartTime, annualReviewDeadlineTime, currentTime);
|
||||
|
||||
// 3. 时间段判断(精确比较)
|
||||
if (currentTime.before(annualReviewStartTime)) {
|
||||
// 未到年审时间,正常状态
|
||||
log.debug("社团ID: {} 未到年审时间,状态正常", clubId);
|
||||
return ClubActivityPermissionResult.allowed();
|
||||
}
|
||||
|
||||
if (currentTime.after(annualReviewDeadlineTime)) {
|
||||
// 超过15天期限,永久限制
|
||||
log.debug("社团ID: {} 已超过年审期限,永久限制", clubId);
|
||||
return ClubActivityPermissionResult.permanentlyDenied(
|
||||
"已超过年审提交期限(15天),无法发起活动");
|
||||
}
|
||||
|
||||
// 4. 在15天窗口期内,检查年审提交情况(精确查询)
|
||||
boolean hasValidSubmission = checkAnnualReviewSubmissionWithPrecision(
|
||||
clubId, annualReviewStartTime, currentTime);
|
||||
|
||||
if (hasValidSubmission) {
|
||||
log.debug("社团ID: {} 已在窗口期内提交年审,权限恢复", clubId);
|
||||
return ClubActivityPermissionResult.allowed();
|
||||
} else {
|
||||
// 计算剩余时间
|
||||
long remainingMillis = annualReviewDeadlineTime.getTime() - currentTime.getTime();
|
||||
long remainingHours = TimeUnit.MILLISECONDS.toHours(remainingMillis);
|
||||
long remainingDays = TimeUnit.MILLISECONDS.toDays(remainingMillis);
|
||||
|
||||
String message = String.format("需要先提交年审,剩余时间:%d天%d小时",
|
||||
remainingDays, remainingHours % 24);
|
||||
|
||||
log.debug("社团ID: {} 在年审窗口期内但未提交,剩余{}天{}小时",
|
||||
clubId, remainingDays, remainingHours % 24);
|
||||
|
||||
return ClubActivityPermissionResult.temporarilyDenied(
|
||||
message, annualReviewDeadlineTime, annualReviewStartTime,
|
||||
remainingHours, remainingDays);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量检查社团活动权限
|
||||
*/
|
||||
@Override
|
||||
public Map<Long, ClubActivityPermissionResult> batchCheckClubActivityPermission(List<Long> clubIds) {
|
||||
if (clubIds == null || clubIds.isEmpty()) {
|
||||
return new HashMap<>();
|
||||
}
|
||||
|
||||
return clubIds.stream()
|
||||
.collect(Collectors.toMap(
|
||||
clubId -> clubId,
|
||||
this::checkClubActivityPermission,
|
||||
(existing, replacement) -> existing
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查社团是否在年审窗口期内
|
||||
*/
|
||||
@Override
|
||||
public boolean isInAnnualReviewWindow(Long clubId) {
|
||||
ClubActivityPermissionResult result = checkClubActivityPermission(clubId);
|
||||
return "TEMPORARILY_LIMITED".equals(result.getStatus());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取社团距离下次年审的天数
|
||||
*/
|
||||
@Override
|
||||
public long getDaysUntilNextAnnualReview(Long clubId) {
|
||||
StglClub clubInfo = clubMapper.selectById(clubId);
|
||||
if (clubInfo == null || clubInfo.getEstablishDate() == null) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
Date currentTime = new Date();
|
||||
Date nextAnnualReviewTime = calculateAnnualReviewStartTime(clubInfo.getEstablishDate(), currentTime);
|
||||
|
||||
// 如果已经过了今年的年审时间,计算明年的
|
||||
if (currentTime.after(nextAnnualReviewTime)) {
|
||||
Calendar cal = Calendar.getInstance();
|
||||
cal.setTime(nextAnnualReviewTime);
|
||||
cal.add(Calendar.YEAR, 1);
|
||||
nextAnnualReviewTime = cal.getTime();
|
||||
}
|
||||
|
||||
long diffMillis = nextAnnualReviewTime.getTime() - currentTime.getTime();
|
||||
return TimeUnit.MILLISECONDS.toDays(diffMillis);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查社团是否已在当前年审期内提交年审
|
||||
*/
|
||||
@Override
|
||||
public boolean hasSubmittedCurrentYearAnnualReview(Long clubId) {
|
||||
StglClub clubInfo = clubMapper.selectById(clubId);
|
||||
if (clubInfo == null || clubInfo.getEstablishDate() == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Date currentTime = new Date();
|
||||
Date annualReviewStartTime = calculateAnnualReviewStartTime(clubInfo.getEstablishDate(), currentTime);
|
||||
Date annualReviewDeadlineTime = addDays(annualReviewStartTime, ANNUAL_REVIEW_WINDOW_DAYS);
|
||||
|
||||
// 如果当前不在年审窗口期,返回false
|
||||
if (currentTime.before(annualReviewStartTime) || currentTime.after(annualReviewDeadlineTime)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return checkAnnualReviewSubmissionWithPrecision(clubId, annualReviewStartTime, currentTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算年审开始时间(精确到具体时刻)
|
||||
*
|
||||
* @param establishDate 社团成立时间
|
||||
* @param currentDate 当前时间
|
||||
* @return 今年的年审开始时间
|
||||
*/
|
||||
private Date calculateAnnualReviewStartTime(Date establishDate, Date currentDate) {
|
||||
Calendar establishCal = Calendar.getInstance();
|
||||
establishCal.setTime(establishDate);
|
||||
|
||||
Calendar currentCal = Calendar.getInstance();
|
||||
currentCal.setTime(currentDate);
|
||||
|
||||
// 构造今年同月同日的时间,保持原有的时分秒毫秒
|
||||
Calendar annualReviewCal = Calendar.getInstance();
|
||||
annualReviewCal.set(Calendar.YEAR, currentCal.get(Calendar.YEAR));
|
||||
annualReviewCal.set(Calendar.MONTH, establishCal.get(Calendar.MONTH));
|
||||
annualReviewCal.set(Calendar.DAY_OF_MONTH, establishCal.get(Calendar.DAY_OF_MONTH));
|
||||
annualReviewCal.set(Calendar.HOUR_OF_DAY, establishCal.get(Calendar.HOUR_OF_DAY));
|
||||
annualReviewCal.set(Calendar.MINUTE, establishCal.get(Calendar.MINUTE));
|
||||
annualReviewCal.set(Calendar.SECOND, establishCal.get(Calendar.SECOND));
|
||||
annualReviewCal.set(Calendar.MILLISECOND, establishCal.get(Calendar.MILLISECOND));
|
||||
|
||||
return annualReviewCal.getTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* 精确检查年审提交情况(关键改进 - 支持秒级精度)
|
||||
*
|
||||
* @param clubId 社团ID
|
||||
* @param windowStartTime 窗口开始时间
|
||||
* @param queryTime 查询时间点
|
||||
* @return 是否已提交年审
|
||||
*/
|
||||
private boolean checkAnnualReviewSubmissionWithPrecision(Long clubId, Date windowStartTime, Date queryTime) {
|
||||
LambdaQueryWrapper<StglClubAnnualReview> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(StglClubAnnualReview::getClubId, clubId)
|
||||
.ge(StglClubAnnualReview::getCreateTime, windowStartTime) // 大于等于窗口开始时间
|
||||
.le(StglClubAnnualReview::getCreateTime, queryTime) // 小于等于当前查询时间
|
||||
.ne(StglClubAnnualReview::getStatus, "0"); // 非草稿状态
|
||||
|
||||
long count = annualReviewMapper.selectCount(wrapper);
|
||||
|
||||
log.debug("年审提交检查 - 社团ID: {}, 窗口开始: {}, 查询时间: {}, 提交记录数: {}",
|
||||
clubId, windowStartTime, queryTime, count);
|
||||
|
||||
return count > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 日期加天数工具方法
|
||||
*
|
||||
* @param date 原始日期
|
||||
* @param days 要添加的天数
|
||||
* @return 新日期
|
||||
*/
|
||||
private Date addDays(Date date, int days) {
|
||||
Calendar cal = Calendar.getInstance();
|
||||
cal.setTime(date);
|
||||
cal.add(Calendar.DAY_OF_MONTH, days);
|
||||
return cal.getTime();
|
||||
}
|
||||
}
|
||||
+107
@@ -17,6 +17,7 @@ import org.dromara.stgl.domain.StglMember;
|
||||
import org.dromara.stgl.mapper.StglClubMapper;
|
||||
import org.dromara.stgl.mapper.StglMemberMapper;
|
||||
import org.dromara.stgl.service.IStglClubService;
|
||||
import org.dromara.stgl.service.IStglClubAdvisorHistoryService;
|
||||
import org.dromara.stgl.utils.AdvisorRoleAssigner;
|
||||
import org.dromara.common.satoken.utils.LoginHelper;
|
||||
import org.dromara.common.json.utils.JsonUtils;
|
||||
@@ -71,6 +72,8 @@ public class StglClubServiceImpl implements IStglClubService {
|
||||
private final AdvisorRoleAssigner advisorRoleAssigner;
|
||||
// 添加用户角色Mapper
|
||||
private final SysUserRoleMapper sysUserRoleMapper;
|
||||
// 添加指导老师历史记录Service
|
||||
private final IStglClubAdvisorHistoryService advisorHistoryService;
|
||||
|
||||
/**
|
||||
* 查询社团信息
|
||||
@@ -342,6 +345,8 @@ public class StglClubServiceImpl implements IStglClubService {
|
||||
assignAdvisorRoles(add);
|
||||
// 为社长分配角色
|
||||
assignPresidentRole(add);
|
||||
// 创建指导老师历史记录
|
||||
createAdvisorHistoryRecords(add);
|
||||
}
|
||||
return flag;
|
||||
}
|
||||
@@ -372,6 +377,8 @@ public class StglClubServiceImpl implements IStglClubService {
|
||||
syncVicePresidentsInMembers(update);
|
||||
// 处理指导老师角色分配
|
||||
handleAdvisorRoleAssignment(oldClub, update);
|
||||
// 更新指导老师历史记录
|
||||
updateAdvisorHistoryRecords(oldClub, update);
|
||||
}
|
||||
|
||||
return updateResult;
|
||||
@@ -1347,4 +1354,104 @@ public class StglClubServiceImpl implements IStglClubService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建指导老师历史记录(新增社团时)
|
||||
*
|
||||
* @param club 社团信息
|
||||
*/
|
||||
private void createAdvisorHistoryRecords(StglClub club) {
|
||||
try {
|
||||
List<IStglClubAdvisorHistoryService.AdvisorInfo> advisors = parseAdvisorsFromClub(club);
|
||||
if (!advisors.isEmpty()) {
|
||||
// 新建社团时,指导老师的上任时间应该是社团成立日期,而不是当前时间
|
||||
Date startTime = club.getEstablishDate() != null ? club.getEstablishDate() : new Date();
|
||||
advisorHistoryService.updateClubAdvisors(club.getId(), advisors, startTime);
|
||||
log.info("为社团 {} 创建指导老师历史记录成功,指导老师数量: {},上任时间: {}",
|
||||
club.getClubName(), advisors.size(), startTime);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("为社团 {} 创建指导老师历史记录失败", club.getClubName(), e);
|
||||
// 不抛出异常,避免影响主流程
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新指导老师历史记录(更新社团时)
|
||||
*
|
||||
* @param oldClub 旧的社团信息
|
||||
* @param newClub 新的社团信息
|
||||
*/
|
||||
private void updateAdvisorHistoryRecords(StglClub oldClub, StglClub newClub) {
|
||||
try {
|
||||
List<IStglClubAdvisorHistoryService.AdvisorInfo> advisors = parseAdvisorsFromClub(newClub);
|
||||
advisorHistoryService.updateClubAdvisors(newClub.getId(), advisors);
|
||||
log.info("为社团 {} 更新指导老师历史记录成功,指导老师数量: {}", newClub.getClubName(), advisors.size());
|
||||
} catch (Exception e) {
|
||||
log.error("为社团 {} 更新指导老师历史记录失败", newClub.getClubName(), e);
|
||||
// 不抛出异常,避免影响主流程
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从社团信息中解析指导老师列表
|
||||
*
|
||||
* @param club 社团信息
|
||||
* @return 指导老师列表
|
||||
*/
|
||||
private List<IStglClubAdvisorHistoryService.AdvisorInfo> parseAdvisorsFromClub(StglClub club) {
|
||||
List<IStglClubAdvisorHistoryService.AdvisorInfo> advisors = new ArrayList<>();
|
||||
|
||||
// 优先从JSON字段解析
|
||||
if (StringUtils.isNotBlank(club.getAdvisorsJson())) {
|
||||
try {
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
List<Map<String, Object>> advisorMaps = mapper.readValue(
|
||||
club.getAdvisorsJson(),
|
||||
new TypeReference<List<Map<String, Object>>>() {}
|
||||
);
|
||||
|
||||
for (Map<String, Object> advisorMap : advisorMaps) {
|
||||
String name = (String) advisorMap.get("name");
|
||||
String dept = (String) advisorMap.get("dept");
|
||||
String phone = (String) advisorMap.get("phone");
|
||||
String email = (String) advisorMap.get("email");
|
||||
Long userId = null;
|
||||
|
||||
// 尝试获取用户ID
|
||||
Object userIdObj = advisorMap.get("userId");
|
||||
if (userIdObj != null) {
|
||||
if (userIdObj instanceof Number) {
|
||||
userId = ((Number) userIdObj).longValue();
|
||||
} else if (userIdObj instanceof String) {
|
||||
try {
|
||||
userId = Long.parseLong((String) userIdObj);
|
||||
} catch (NumberFormatException e) {
|
||||
// 忽略解析错误
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (StringUtils.isNotBlank(name)) {
|
||||
advisors.add(new IStglClubAdvisorHistoryService.AdvisorInfo(name, dept, phone, email, userId));
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("解析指导老师JSON数据失败: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// 如果JSON解析失败或为空,回退到单一字段
|
||||
if (advisors.isEmpty() && StringUtils.isNotBlank(club.getAdvisorName())) {
|
||||
advisors.add(new IStglClubAdvisorHistoryService.AdvisorInfo(
|
||||
club.getAdvisorName(),
|
||||
club.getAdvisorDept(),
|
||||
club.getAdvisorPhone(),
|
||||
club.getAdvisorEmail(),
|
||||
null
|
||||
));
|
||||
}
|
||||
|
||||
return advisors;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -159,6 +159,8 @@
|
||||
a.end_time,
|
||||
a.location,
|
||||
a.organizer_name,
|
||||
a.related_dept_name,
|
||||
a.related_dept_names,
|
||||
CAST(a.related_type AS CHAR) as related_type,
|
||||
CASE
|
||||
WHEN a.related_type = 1 THEN c.club_name
|
||||
|
||||
+66
-42
@@ -14,6 +14,10 @@
|
||||
<result property="location" column="location"/>
|
||||
<result property="description" column="description"/>
|
||||
<result property="posterUrl" column="poster_url"/>
|
||||
<result property="relatedDeptId" column="related_dept_id"/>
|
||||
<result property="relatedDeptName" column="related_dept_name"/>
|
||||
<result property="relatedDeptIds" column="related_dept_ids"/>
|
||||
<result property="relatedDeptNames" column="related_dept_names"/>
|
||||
<result property="relatedClubId" column="related_club_id"/>
|
||||
<result property="relatedClubName" column="related_club_name"/>
|
||||
<result property="relatedType" column="related_type"/>
|
||||
@@ -55,22 +59,25 @@
|
||||
SELECT
|
||||
sa.id, sa.activity_name, sa.activity_type, sa.activity_theme,
|
||||
sa.start_time, sa.end_time, sa.location, sa.description,
|
||||
sa.poster_url, sa.related_club_id, sa.related_type, sa.organizer_name, sa.organizer_type,
|
||||
sa.poster_url, sa.related_dept_id, sa.related_dept_name, sa.related_dept_ids, sa.related_dept_names,
|
||||
sa.related_club_id, sa.related_type, sa.organizer_name, sa.organizer_type,
|
||||
sa.organizer_contact, sa.organizer_phone, sa.co_organizers, sa.instructors,
|
||||
sa.registration_type, sa.registration_start, sa.registration_end,
|
||||
-- 动态计算报名状态:优先根据活动状态,再根据报名时间范围
|
||||
-- 动态计算报名状态:只有审批通过的活动才根据时间计算报名状态
|
||||
CASE
|
||||
-- 首先根据活动状态强制设置(优先级最高)
|
||||
WHEN sa.activity_status = '0' THEN '0' -- 草稿 → 未开始(固定)
|
||||
WHEN sa.activity_status = '1' THEN '0' -- 未开始 → 未开始(固定)
|
||||
WHEN sa.activity_status = '2' THEN '0' -- 待审批 → 未开始(固定)
|
||||
WHEN sa.activity_status = '5' THEN '2' -- 已结束 → 报名结束(固定)
|
||||
WHEN sa.activity_status = '6' THEN '3' -- 已取消 → 已关闭(固定)
|
||||
WHEN sa.activity_status = '7' THEN '0' -- 已驳回 → 未开始(固定)
|
||||
-- 草稿、待审批、已驳回状态不映射报名状态,保持为空
|
||||
WHEN sa.activity_status IN ('0', '2', '7') THEN NULL
|
||||
|
||||
-- 然后根据活动状态和报名时间判断
|
||||
WHEN sa.activity_status = '3' OR sa.activity_status = '4' THEN
|
||||
-- 已取消的活动设置为已关闭
|
||||
WHEN sa.activity_status = '6' THEN '3' -- 已取消 → 已关闭(固定)
|
||||
|
||||
-- 只有审批通过的活动(已通过、进行中、已结束)才根据时间计算报名状态
|
||||
WHEN sa.activity_status IN ('3', '4', '5') THEN
|
||||
CASE
|
||||
-- 已结束的活动强制设置报名结束
|
||||
WHEN sa.activity_status = '5' THEN '2' -- 已结束 → 报名结束(固定)
|
||||
|
||||
-- 已通过和进行中的活动根据报名时间判断
|
||||
WHEN sa.registration_start IS NULL OR sa.registration_end IS NULL THEN sa.registration_status
|
||||
<![CDATA[WHEN NOW() < sa.registration_start THEN '0']]>
|
||||
<![CDATA[WHEN NOW() >= sa.registration_start AND NOW() <= sa.registration_end THEN '1']]>
|
||||
@@ -119,9 +126,9 @@
|
||||
END
|
||||
ELSE NULL
|
||||
END AS related_club_name,
|
||||
-- 统计报名人数(子查询)
|
||||
-- 统计报名人数(子查询)- 只统计审核通过的报名记录
|
||||
(SELECT COUNT(*) FROM stgl_activity_registration sar
|
||||
WHERE sar.activity_id = sa.id AND sar.deleted = 0) AS registration_count,
|
||||
WHERE sar.activity_id = sa.id AND sar.deleted = 0 AND sar.approval_status = 1) AS registration_count,
|
||||
-- 统计平均评分(子查询)
|
||||
(SELECT ROUND(AVG(rating_score), 1) FROM stgl_activity_rating sar2
|
||||
WHERE sar2.activity_id = sa.id AND sar2.deleted = 0) AS average_rating
|
||||
@@ -156,17 +163,19 @@
|
||||
<if test="registrationStatus != null">
|
||||
AND (
|
||||
CASE
|
||||
-- 首先根据活动状态强制设置(优先级最高)
|
||||
WHEN sa.activity_status = '0' THEN '0' -- 草稿 → 未开始(固定)
|
||||
WHEN sa.activity_status = '1' THEN '0' -- 未开始 → 未开始(固定)
|
||||
WHEN sa.activity_status = '2' THEN '0' -- 待审批 → 未开始(固定)
|
||||
WHEN sa.activity_status = '5' THEN '2' -- 已结束 → 报名结束(固定)
|
||||
WHEN sa.activity_status = '6' THEN '3' -- 已取消 → 已关闭(固定)
|
||||
WHEN sa.activity_status = '7' THEN '0' -- 已驳回 → 未开始(固定)
|
||||
-- 草稿、待审批、已驳回状态不映射报名状态,保持为空
|
||||
WHEN sa.activity_status IN ('0', '2', '7') THEN NULL
|
||||
|
||||
-- 然后根据活动状态和报名时间判断
|
||||
WHEN sa.activity_status = '3' OR sa.activity_status = '4' THEN
|
||||
-- 已取消的活动设置为已关闭
|
||||
WHEN sa.activity_status = '6' THEN '3' -- 已取消 → 已关闭(固定)
|
||||
|
||||
-- 只有审批通过的活动(已通过、进行中、已结束)才根据时间计算报名状态
|
||||
WHEN sa.activity_status IN ('3', '4', '5') THEN
|
||||
CASE
|
||||
-- 已结束的活动强制设置报名结束
|
||||
WHEN sa.activity_status = '5' THEN '2' -- 已结束 → 报名结束(固定)
|
||||
|
||||
-- 已通过和进行中的活动根据报名时间判断
|
||||
WHEN sa.registration_start IS NULL OR sa.registration_end IS NULL THEN sa.registration_status
|
||||
<![CDATA[WHEN NOW() < sa.registration_start THEN '0']]>
|
||||
<![CDATA[WHEN NOW() >= sa.registration_start AND NOW() <= sa.registration_end THEN '1']]>
|
||||
@@ -191,6 +200,9 @@
|
||||
<if test="registrationEnd != null">
|
||||
AND sa.registration_end <= #{registrationEnd}
|
||||
</if>
|
||||
<if test="relatedDeptId != null">
|
||||
AND sa.related_dept_id = #{relatedDeptId}
|
||||
</if>
|
||||
<if test="relatedClubId != null">
|
||||
AND sa.related_club_id = #{relatedClubId}
|
||||
</if>
|
||||
@@ -258,17 +270,19 @@
|
||||
<if test="activity.registrationStatus != null and activity.registrationStatus != ''">
|
||||
AND (
|
||||
CASE
|
||||
-- 首先根据活动状态强制设置(优先级最高)
|
||||
WHEN sa.activity_status = '0' THEN '0' -- 草稿 → 未开始(固定)
|
||||
WHEN sa.activity_status = '1' THEN '0' -- 未开始 → 未开始(固定)
|
||||
WHEN sa.activity_status = '2' THEN '0' -- 待审批 → 未开始(固定)
|
||||
WHEN sa.activity_status = '5' THEN '2' -- 已结束 → 报名结束(固定)
|
||||
WHEN sa.activity_status = '6' THEN '3' -- 已取消 → 已关闭(固定)
|
||||
WHEN sa.activity_status = '7' THEN '0' -- 已驳回 → 未开始(固定)
|
||||
-- 草稿、待审批、已驳回状态不映射报名状态,保持为空
|
||||
WHEN sa.activity_status IN ('0', '2', '7') THEN NULL
|
||||
|
||||
-- 然后根据活动状态和报名时间判断
|
||||
WHEN sa.activity_status = '3' OR sa.activity_status = '4' THEN
|
||||
-- 已取消的活动设置为已关闭
|
||||
WHEN sa.activity_status = '6' THEN '3' -- 已取消 → 已关闭(固定)
|
||||
|
||||
-- 只有审批通过的活动(已通过、进行中、已结束)才根据时间计算报名状态
|
||||
WHEN sa.activity_status IN ('3', '4', '5') THEN
|
||||
CASE
|
||||
-- 已结束的活动强制设置报名结束
|
||||
WHEN sa.activity_status = '5' THEN '2' -- 已结束 → 报名结束(固定)
|
||||
|
||||
-- 已通过和进行中的活动根据报名时间判断
|
||||
WHEN sa.registration_start IS NULL OR sa.registration_end IS NULL THEN sa.registration_status
|
||||
<![CDATA[WHEN NOW() < sa.registration_start THEN '0']]>
|
||||
<![CDATA[WHEN NOW() >= sa.registration_start AND NOW() <= sa.registration_end THEN '1']]>
|
||||
@@ -285,17 +299,19 @@
|
||||
<if test="activity.params != null and activity.params.registrationStatusList != null and activity.params.registrationStatusList.size() > 0">
|
||||
AND (
|
||||
CASE
|
||||
-- 首先根据活动状态强制设置(优先级最高)
|
||||
WHEN sa.activity_status = '0' THEN '0' -- 草稿 → 未开始(固定)
|
||||
WHEN sa.activity_status = '1' THEN '0' -- 未开始 → 未开始(固定)
|
||||
WHEN sa.activity_status = '2' THEN '0' -- 待审批 → 未开始(固定)
|
||||
WHEN sa.activity_status = '5' THEN '2' -- 已结束 → 报名结束(固定)
|
||||
WHEN sa.activity_status = '6' THEN '3' -- 已取消 → 已关闭(固定)
|
||||
WHEN sa.activity_status = '7' THEN '0' -- 已驳回 → 未开始(固定)
|
||||
-- 草稿、待审批、已驳回状态不映射报名状态,保持为空
|
||||
WHEN sa.activity_status IN ('0', '2', '7') THEN NULL
|
||||
|
||||
-- 然后根据活动状态和报名时间判断
|
||||
WHEN sa.activity_status = '3' OR sa.activity_status = '4' THEN
|
||||
-- 已取消的活动设置为已关闭
|
||||
WHEN sa.activity_status = '6' THEN '3' -- 已取消 → 已关闭(固定)
|
||||
|
||||
-- 只有审批通过的活动(已通过、进行中、已结束)才根据时间计算报名状态
|
||||
WHEN sa.activity_status IN ('3', '4', '5') THEN
|
||||
CASE
|
||||
-- 已结束的活动强制设置报名结束
|
||||
WHEN sa.activity_status = '5' THEN '2' -- 已结束 → 报名结束(固定)
|
||||
|
||||
-- 已通过和进行中的活动根据报名时间判断
|
||||
WHEN sa.registration_start IS NULL OR sa.registration_end IS NULL THEN sa.registration_status
|
||||
<![CDATA[WHEN NOW() < sa.registration_start THEN '0']]>
|
||||
<![CDATA[WHEN NOW() >= sa.registration_start AND NOW() <= sa.registration_end THEN '1']]>
|
||||
@@ -323,6 +339,12 @@
|
||||
<if test="activity.tenantId != null and activity.tenantId != ''">
|
||||
AND sa.tenant_id = #{activity.tenantId}
|
||||
</if>
|
||||
<if test="activity.relatedDeptId != null">
|
||||
AND sa.related_dept_id = #{activity.relatedDeptId}
|
||||
</if>
|
||||
<if test="activity.relatedClubId != null">
|
||||
AND sa.related_club_id = #{activity.relatedClubId}
|
||||
</if>
|
||||
<!-- 活动面向群体过滤:当activity_range=1时,只有组织成员才能看到 -->
|
||||
<if test="activity.params != null and activity.params.userOrganizations != null">
|
||||
AND (
|
||||
@@ -383,7 +405,8 @@
|
||||
INSERT INTO stgl_activity (
|
||||
id, tenant_id, create_dept, create_by, create_time, update_by, update_time,
|
||||
activity_name, activity_type, activity_theme, start_time, end_time, location,
|
||||
description, poster_url, related_club_id, organizer_name, organizer_type,
|
||||
description, poster_url, related_dept_id, related_dept_name, related_dept_ids, related_dept_names,
|
||||
related_club_id, organizer_name, organizer_type,
|
||||
organizer_contact, organizer_phone, co_organizers, instructors,
|
||||
registration_type, registration_start, registration_end, registration_status,
|
||||
max_participants, max_teams, team_size_min, team_size_max, need_approval,
|
||||
@@ -394,7 +417,8 @@
|
||||
#{newActivity.createBy}, #{newActivity.createTime}, #{newActivity.updateBy}, #{newActivity.updateTime},
|
||||
#{newActivity.activityName}, activity_type, activity_theme,
|
||||
#{newActivity.startTime}, #{newActivity.endTime}, location,
|
||||
description, poster_url, related_club_id, organizer_name, organizer_type,
|
||||
description, poster_url, related_dept_id, related_dept_name, related_dept_ids, related_dept_names,
|
||||
related_club_id, organizer_name, organizer_type,
|
||||
organizer_contact, organizer_phone, co_organizers, instructors,
|
||||
registration_type, #{newActivity.registrationStart}, #{newActivity.registrationEnd},
|
||||
#{newActivity.registrationStatus}, max_participants, max_teams, team_size_min, team_size_max, need_approval,
|
||||
|
||||
@@ -14,6 +14,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<result property="clubCount" column="club_count"/>
|
||||
<result property="volunteerTeamCount" column="volunteer_team_count"/>
|
||||
<result property="activityCount" column="activity_count"/>
|
||||
<result property="todayActivityName" column="today_activity_name"/>
|
||||
<result property="todayActivityStartTime" column="today_activity_start_time"/>
|
||||
<result property="todayActivityEndTime" column="today_activity_end_time"/>
|
||||
<result property="createBy" column="create_by"/>
|
||||
<result property="createTime" column="create_time"/>
|
||||
<result property="updateBy" column="update_by"/>
|
||||
@@ -52,6 +55,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
COALESCE(club_stats.club_count, 0) as club_count,
|
||||
COALESCE(volunteer_stats.volunteer_team_count, 0) as volunteer_team_count,
|
||||
COALESCE(activity_stats.activity_count, 0) as activity_count,
|
||||
today_activity_stats.activity_name as today_activity_name,
|
||||
today_activity_stats.start_time as today_activity_start_time,
|
||||
today_activity_stats.end_time as today_activity_end_time,
|
||||
s.create_by,
|
||||
s.create_time,
|
||||
s.update_by,
|
||||
@@ -60,6 +66,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
s.deleted
|
||||
FROM xg_student_msg s
|
||||
LEFT JOIN xg_class_msg c ON s.class_id = c.id
|
||||
AND c.tenant_id = '000000'
|
||||
LEFT JOIN (
|
||||
SELECT
|
||||
m.user_id,
|
||||
@@ -68,6 +75,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
WHERE m.member_type = 1
|
||||
AND m.deleted = 0
|
||||
AND m.status = 2
|
||||
AND m.tenant_id = '000000'
|
||||
GROUP BY m.user_id
|
||||
) club_stats ON s.id = club_stats.user_id
|
||||
LEFT JOIN (
|
||||
@@ -78,6 +86,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
WHERE m.member_type = 2
|
||||
AND m.deleted = 0
|
||||
AND m.status = 2
|
||||
AND m.tenant_id = '000000'
|
||||
GROUP BY m.user_id
|
||||
) volunteer_stats ON s.id = volunteer_stats.user_id
|
||||
LEFT JOIN (
|
||||
@@ -125,9 +134,132 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
AND user_id != ''
|
||||
GROUP BY user_id
|
||||
) activity_stats ON s.id = activity_stats.user_id
|
||||
LEFT JOIN (
|
||||
-- 查询学生今日活动信息(审核通过且时间为今天的活动,只取最早的一个)
|
||||
-- 使用兼容MySQL 5.7的方式,通过MIN函数和相关子查询实现
|
||||
SELECT
|
||||
ta.user_id,
|
||||
ta.activity_name,
|
||||
DATE_FORMAT(ta.start_time, '%H:%i') as start_time,
|
||||
DATE_FORMAT(ta.end_time, '%H:%i') as end_time
|
||||
FROM (
|
||||
-- 个人报名的今日活动
|
||||
SELECT
|
||||
ar.user_id,
|
||||
a.activity_name,
|
||||
a.start_time,
|
||||
a.end_time
|
||||
FROM stgl_activity_registration ar
|
||||
INNER JOIN stgl_activity a ON ar.activity_id = a.id
|
||||
AND ar.tenant_id = '000000'
|
||||
AND a.tenant_id = '000000'
|
||||
WHERE ar.deleted = 0
|
||||
AND ar.approval_status = 1
|
||||
AND ar.tenant_id = '000000'
|
||||
AND a.deleted = 0
|
||||
AND DATE(a.start_time) = CURDATE() -- 今日活动
|
||||
AND a.tenant_id = '000000'
|
||||
|
||||
UNION
|
||||
|
||||
-- 团队报名的今日活动(通过团队成员信息)
|
||||
SELECT
|
||||
JSON_UNQUOTE(JSON_EXTRACT(ati.team_members, CONCAT('$[', numbers.n, '].userId'))) as user_id,
|
||||
a.activity_name,
|
||||
a.start_time,
|
||||
a.end_time
|
||||
FROM stgl_activity_team_info ati
|
||||
CROSS JOIN (
|
||||
SELECT 0 as n UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION
|
||||
SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9 UNION
|
||||
SELECT 10 UNION SELECT 11 UNION SELECT 12 UNION SELECT 13 UNION SELECT 14 UNION
|
||||
SELECT 15 UNION SELECT 16 UNION SELECT 17 UNION SELECT 18 UNION SELECT 19
|
||||
) numbers
|
||||
INNER JOIN stgl_activity a ON ati.activity_id = a.id
|
||||
AND ati.tenant_id = '000000'
|
||||
AND a.tenant_id = '000000'
|
||||
WHERE ati.deleted = 0
|
||||
AND ati.tenant_id = '000000'
|
||||
AND a.deleted = 0
|
||||
AND DATE(a.start_time) = CURDATE() -- 今日活动
|
||||
AND a.tenant_id = '000000'
|
||||
AND JSON_UNQUOTE(JSON_EXTRACT(ati.team_members, CONCAT('$[', numbers.n, '].userId'))) IS NOT NULL
|
||||
AND JSON_UNQUOTE(JSON_EXTRACT(ati.team_members, CONCAT('$[', numbers.n, '].userId'))) != ''
|
||||
AND EXISTS (
|
||||
SELECT 1 FROM stgl_activity_registration team_ar
|
||||
WHERE team_ar.team_id = ati.id
|
||||
AND team_ar.deleted = 0
|
||||
AND team_ar.approval_status = 1
|
||||
AND team_ar.registration_type = 2
|
||||
AND team_ar.tenant_id = '000000'
|
||||
AND team_ar.tenant_id = '000000'
|
||||
)
|
||||
AND ati.tenant_id = '000000'
|
||||
) ta
|
||||
WHERE ta.user_id IS NOT NULL
|
||||
AND ta.user_id != ''
|
||||
-- 只取每个用户最早的活动(通过相关子查询实现,兼容MySQL 5.7)
|
||||
AND ta.start_time = (
|
||||
SELECT MIN(ta2.start_time)
|
||||
FROM (
|
||||
-- 个人报名的今日活动
|
||||
SELECT
|
||||
ar2.user_id,
|
||||
a2.start_time
|
||||
FROM stgl_activity_registration ar2
|
||||
INNER JOIN stgl_activity a2 ON ar2.activity_id = a2.id
|
||||
AND ar2.tenant_id = '000000'
|
||||
AND a2.tenant_id = '000000'
|
||||
WHERE ar2.deleted = 0
|
||||
AND ar2.approval_status = 1
|
||||
AND ar2.tenant_id = '000000'
|
||||
AND a2.deleted = 0
|
||||
AND DATE(a2.start_time) = CURDATE()
|
||||
AND a2.tenant_id = '000000'
|
||||
|
||||
UNION
|
||||
|
||||
-- 团队报名的今日活动
|
||||
SELECT
|
||||
JSON_UNQUOTE(JSON_EXTRACT(ati2.team_members, CONCAT('$[', numbers2.n, '].userId'))) as user_id,
|
||||
a2.start_time
|
||||
FROM stgl_activity_team_info ati2
|
||||
CROSS JOIN (
|
||||
SELECT 0 as n UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION
|
||||
SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9 UNION
|
||||
SELECT 10 UNION SELECT 11 UNION SELECT 12 UNION SELECT 13 UNION SELECT 14 UNION
|
||||
SELECT 15 UNION SELECT 16 UNION SELECT 17 UNION SELECT 18 UNION SELECT 19
|
||||
) numbers2
|
||||
INNER JOIN stgl_activity a2 ON ati2.activity_id = a2.id
|
||||
AND ati2.tenant_id = '000000'
|
||||
AND a2.tenant_id = '000000'
|
||||
WHERE ati2.deleted = 0
|
||||
AND ati2.tenant_id = '000000'
|
||||
AND a2.deleted = 0
|
||||
AND DATE(a2.start_time) = CURDATE()
|
||||
AND a2.tenant_id = '000000'
|
||||
AND JSON_UNQUOTE(JSON_EXTRACT(ati2.team_members, CONCAT('$[', numbers2.n, '].userId'))) IS NOT NULL
|
||||
AND JSON_UNQUOTE(JSON_EXTRACT(ati2.team_members, CONCAT('$[', numbers2.n, '].userId'))) != ''
|
||||
AND EXISTS (
|
||||
SELECT 1 FROM stgl_activity_registration team_ar2
|
||||
WHERE team_ar2.team_id = ati2.id
|
||||
AND team_ar2.deleted = 0
|
||||
AND team_ar2.approval_status = 1
|
||||
AND team_ar2.registration_type = 2
|
||||
AND team_ar2.tenant_id = '000000'
|
||||
AND team_ar2.tenant_id = '000000'
|
||||
)
|
||||
AND ati2.tenant_id = '000000'
|
||||
) ta2
|
||||
WHERE ta2.user_id = ta.user_id
|
||||
AND ta2.user_id IS NOT NULL
|
||||
AND ta2.user_id != ''
|
||||
)
|
||||
) today_activity_stats ON s.id = today_activity_stats.user_id
|
||||
<where>
|
||||
s.deleted = 0
|
||||
AND s.student_status = '1'
|
||||
AND s.tenant_id = '000000'
|
||||
<if test="classId != null and classId != ''">
|
||||
AND s.class_id = #{classId}
|
||||
</if>
|
||||
@@ -151,6 +283,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<where>
|
||||
s.deleted = 0
|
||||
AND s.student_status = '1'
|
||||
AND s.tenant_id = '000000'
|
||||
<if test="classId != null and classId != ''">
|
||||
AND s.class_id = #{classId}
|
||||
</if>
|
||||
@@ -185,7 +318,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
AND m.member_type = 1
|
||||
AND m.deleted = 0
|
||||
AND m.status = 2
|
||||
AND m.tenant_id = '000000'
|
||||
AND c.deleted = 0
|
||||
AND c.tenant_id = '000000'
|
||||
ORDER BY c.create_time DESC
|
||||
</select>
|
||||
|
||||
@@ -209,7 +344,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
AND m.member_type = 2
|
||||
AND m.deleted = 0
|
||||
AND m.status = 2
|
||||
AND m.tenant_id = '000000'
|
||||
AND vt.deleted = 0
|
||||
AND vt.tenant_id = '000000'
|
||||
ORDER BY vt.create_time DESC
|
||||
</select>
|
||||
|
||||
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper
|
||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="org.dromara.stgl.mapper.StglClubAdvisorHistoryMapper">
|
||||
|
||||
<resultMap type="org.dromara.stgl.domain.vo.StglClubAdvisorHistoryVo" id="StglClubAdvisorHistoryResult">
|
||||
<result property="id" column="id"/>
|
||||
<result property="clubId" column="club_id"/>
|
||||
<result property="clubName" column="club_name"/>
|
||||
<result property="advisorName" column="advisor_name"/>
|
||||
<result property="advisorDept" column="advisor_dept"/>
|
||||
<result property="advisorPhone" column="advisor_phone"/>
|
||||
<result property="advisorEmail" column="advisor_email"/>
|
||||
<result property="advisorUserId" column="advisor_user_id"/>
|
||||
<result property="status" column="status"/>
|
||||
<result property="startTime" column="start_time"/>
|
||||
<result property="endTime" column="end_time"/>
|
||||
<result property="createBy" column="create_by"/>
|
||||
<result property="createTime" column="create_time"/>
|
||||
<result property="updateBy" column="update_by"/>
|
||||
<result property="updateTime" column="update_time"/>
|
||||
</resultMap>
|
||||
|
||||
<sql id="selectStglClubAdvisorHistoryVo">
|
||||
select h.id, h.club_id, h.advisor_name, h.advisor_dept, h.advisor_phone, h.advisor_email,
|
||||
h.advisor_user_id, h.status, h.start_time, h.end_time,
|
||||
h.create_by, h.create_time, h.update_by, h.update_time,
|
||||
c.club_name
|
||||
from stgl_club_advisor_history h
|
||||
left join stgl_club c on h.club_id = c.id
|
||||
</sql>
|
||||
|
||||
<!-- 查询指定社团的当前在任指导老师 -->
|
||||
<select id="selectCurrentAdvisorsByClubId" parameterType="Long" resultMap="StglClubAdvisorHistoryResult">
|
||||
<include refid="selectStglClubAdvisorHistoryVo"/>
|
||||
where h.club_id = #{clubId} and h.status = 1 and h.deleted = 0
|
||||
order by h.start_time asc
|
||||
</select>
|
||||
|
||||
<!-- 查询指定社团的历史指导老师(已离任) -->
|
||||
<select id="selectHistoryAdvisorsByClubId" parameterType="Long" resultMap="StglClubAdvisorHistoryResult">
|
||||
<include refid="selectStglClubAdvisorHistoryVo"/>
|
||||
where h.club_id = #{clubId} and h.status = 0 and h.deleted = 0
|
||||
order by h.end_time desc
|
||||
</select>
|
||||
|
||||
<!-- 查询指定社团的所有指导老师(包含当前和历史) -->
|
||||
<select id="selectAllAdvisorsByClubId" parameterType="Long" resultMap="StglClubAdvisorHistoryResult">
|
||||
<include refid="selectStglClubAdvisorHistoryVo"/>
|
||||
where h.club_id = #{clubId} and h.deleted = 0
|
||||
order by h.status desc, h.start_time desc
|
||||
</select>
|
||||
|
||||
<!-- 批量设置指导老师为历史状态(离任) -->
|
||||
<update id="batchSetHistoryStatus">
|
||||
update stgl_club_advisor_history
|
||||
set status = 0, end_time = #{endTime}, update_time = now()
|
||||
where club_id = #{clubId} and status = 1 and deleted = 0
|
||||
<if test="advisorNames != null and advisorNames.size() > 0">
|
||||
and advisor_name in
|
||||
<foreach collection="advisorNames" item="name" open="(" close=")" separator=",">
|
||||
#{name}
|
||||
</foreach>
|
||||
</if>
|
||||
</update>
|
||||
|
||||
</mapper>
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper
|
||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="org.dromara.stgl.mapper.StglClubAnnualReviewActivityMapper">
|
||||
|
||||
<resultMap type="org.dromara.stgl.domain.StglClubAnnualReviewActivity" id="StglClubAnnualReviewActivityResult">
|
||||
<result property="id" column="id" />
|
||||
<result property="tenantId" column="tenant_id" />
|
||||
<result property="createDept" column="create_dept" />
|
||||
<result property="createBy" column="create_by" />
|
||||
<result property="createTime" column="create_time" />
|
||||
<result property="updateBy" column="update_by" />
|
||||
<result property="updateTime" column="update_time" />
|
||||
<result property="deleted" column="deleted" />
|
||||
<result property="annualReviewId" column="annual_review_id" />
|
||||
<result property="activityName" column="activity_name" />
|
||||
<result property="activityTime" column="activity_time" />
|
||||
<result property="activityLocation" column="activity_location" />
|
||||
<result property="activityType" column="activity_type" />
|
||||
<result property="participantCount" column="participant_count" />
|
||||
<result property="activityDescription" column="activity_description" />
|
||||
<result property="activityStartTime" column="activity_start_time" />
|
||||
<result property="activityEndTime" column="activity_end_time" />
|
||||
</resultMap>
|
||||
|
||||
<sql id="selectStglClubAnnualReviewActivityVo">
|
||||
select id, tenant_id, create_dept, create_by, create_time, update_by, update_time, deleted, annual_review_id, activity_name, activity_time, activity_start_time, activity_end_time, activity_location, activity_type, participant_count, activity_description from stgl_club_annual_review_activity
|
||||
</sql>
|
||||
|
||||
<!-- 根据年审记录ID查询活动列表 -->
|
||||
<select id="selectActivitiesByAnnualReviewId" parameterType="Long" resultMap="StglClubAnnualReviewActivityResult">
|
||||
<include refid="selectStglClubAnnualReviewActivityVo"/>
|
||||
where annual_review_id = #{annualReviewId} and deleted = 0
|
||||
order by activity_time desc, create_time asc
|
||||
</select>
|
||||
|
||||
<!-- 根据年审记录ID删除活动 -->
|
||||
<update id="deleteActivitiesByAnnualReviewId" parameterType="Long">
|
||||
update stgl_club_annual_review_activity set deleted = 1
|
||||
where annual_review_id = #{annualReviewId} and deleted = 0
|
||||
</update>
|
||||
|
||||
</mapper>
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper
|
||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="org.dromara.stgl.mapper.StglClubAnnualReviewMapper">
|
||||
|
||||
<resultMap type="org.dromara.stgl.domain.vo.StglClubAnnualReviewVo" id="StglClubAnnualReviewResult">
|
||||
<result property="id" column="id" />
|
||||
<result property="clubId" column="club_id" />
|
||||
<result property="reviewYear" column="review_year" />
|
||||
<result property="reviewStartDate" column="review_start_date" />
|
||||
<result property="reviewEndDate" column="review_end_date" />
|
||||
<result property="reviewTime" column="review_time" />
|
||||
<result property="clubName" column="club_name" />
|
||||
<result property="establishDate" column="establish_date" />
|
||||
<result property="presidentName" column="president_name" />
|
||||
<result property="clubType" column="club_type" />
|
||||
<result property="advisorName" column="advisor_name" />
|
||||
<result property="advisorDept" column="advisor_dept" />
|
||||
<result property="hasCharter" column="has_charter" />
|
||||
<result property="charterUrl" column="charter_url" />
|
||||
<result property="hasFunds" column="has_funds" />
|
||||
<result property="fundAmount" column="fund_amount" />
|
||||
<result property="memberCount" column="member_count" />
|
||||
<result property="status" column="status" />
|
||||
<result property="advisorOpinion" column="advisor_opinion" />
|
||||
<result property="collegeOpinion" column="college_opinion" />
|
||||
<result property="youthLeagueOpinion" column="youth_league_opinion" />
|
||||
<result property="membersJson" column="members_json" />
|
||||
<result property="activitiesJson" column="activities_json" />
|
||||
<result property="createBy" column="create_by" />
|
||||
<result property="createTime" column="create_time" />
|
||||
<result property="updateBy" column="update_by" />
|
||||
<result property="updateTime" column="update_time" />
|
||||
<result property="tenantId" column="tenant_id" />
|
||||
</resultMap>
|
||||
|
||||
<sql id="selectStglClubAnnualReviewVo">
|
||||
select
|
||||
ar.id, ar.club_id, ar.review_year, ar.review_start_date, ar.review_end_date, ar.review_time,
|
||||
ar.club_name, ar.establish_date, ar.club_type, ar.advisor_name, ar.advisor_dept,
|
||||
ar.has_charter, ar.charter_url, ar.has_funds, ar.fund_amount, ar.member_count,
|
||||
ar.status, ar.advisor_opinion, ar.college_opinion, ar.youth_league_opinion,
|
||||
ar.members_json, ar.activities_json,
|
||||
ar.create_by, ar.create_time, ar.update_by, ar.update_time, ar.tenant_id,
|
||||
c.president_name
|
||||
from stgl_club_annual_review ar
|
||||
left join stgl_club c on ar.club_id = c.id
|
||||
</sql>
|
||||
|
||||
<select id="selectVoList" parameterType="com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper" resultMap="StglClubAnnualReviewResult">
|
||||
<include refid="selectStglClubAnnualReviewVo"/>
|
||||
${ew.customSqlSegment}
|
||||
</select>
|
||||
|
||||
<select id="selectVoPage" resultMap="StglClubAnnualReviewResult">
|
||||
<include refid="selectStglClubAnnualReviewVo"/>
|
||||
${ew.customSqlSegment}
|
||||
</select>
|
||||
|
||||
<select id="selectVoById" parameterType="Long" resultMap="StglClubAnnualReviewResult">
|
||||
<include refid="selectStglClubAnnualReviewVo"/>
|
||||
where ar.id = #{id}
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper
|
||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="org.dromara.stgl.mapper.StglClubAnnualReviewMemberMapper">
|
||||
|
||||
<resultMap type="org.dromara.stgl.domain.StglClubAnnualReviewMember" id="StglClubAnnualReviewMemberResult">
|
||||
<result property="id" column="id" />
|
||||
<result property="tenantId" column="tenant_id" />
|
||||
<result property="createDept" column="create_dept" />
|
||||
<result property="createBy" column="create_by" />
|
||||
<result property="createTime" column="create_time" />
|
||||
<result property="updateBy" column="update_by" />
|
||||
<result property="updateTime" column="update_time" />
|
||||
<result property="deleted" column="deleted" />
|
||||
<result property="annualReviewId" column="annual_review_id" />
|
||||
<result property="memberName" column="member_name" />
|
||||
<result property="className" column="class_name" />
|
||||
<result property="role" column="role" />
|
||||
<result property="contactInfo" column="contact_info" />
|
||||
</resultMap>
|
||||
|
||||
<sql id="selectStglClubAnnualReviewMemberVo">
|
||||
select id, tenant_id, create_dept, create_by, create_time, update_by, update_time, deleted, annual_review_id, member_name, class_name, role, contact_info from stgl_club_annual_review_member
|
||||
</sql>
|
||||
|
||||
<!-- 根据年审记录ID查询成员列表 -->
|
||||
<select id="selectMembersByAnnualReviewId" parameterType="Long" resultMap="StglClubAnnualReviewMemberResult">
|
||||
<include refid="selectStglClubAnnualReviewMemberVo"/>
|
||||
where annual_review_id = #{annualReviewId} and deleted = 0
|
||||
order by create_time asc
|
||||
</select>
|
||||
|
||||
<!-- 根据年审记录ID删除成员 -->
|
||||
<update id="deleteMembersByAnnualReviewId" parameterType="Long">
|
||||
update stgl_club_annual_review_member set deleted = 1
|
||||
where annual_review_id = #{annualReviewId} and deleted = 0
|
||||
</update>
|
||||
|
||||
</mapper>
|
||||
@@ -190,6 +190,8 @@ CREATE TABLE `stgl_activity` (
|
||||
`location` varchar(200) COMMENT '活动地点',
|
||||
`description` text COMMENT '活动简介(1000字以内)',
|
||||
`poster_url` varchar(500) COMMENT '活动海报路径',
|
||||
`related_dept_id` bigint(20) COMMENT '关联部门ID',
|
||||
`related_dept_name` varchar(100) COMMENT '关联部门名称',
|
||||
`related_club_id` bigint(20) COMMENT '关联社团ID',
|
||||
|
||||
-- 主办承办方信息
|
||||
@@ -225,6 +227,7 @@ CREATE TABLE `stgl_activity` (
|
||||
KEY `idx_end_time` (`end_time`),
|
||||
KEY `idx_activity_status` (`activity_status`),
|
||||
KEY `idx_registration_status` (`registration_status`),
|
||||
KEY `idx_related_dept_id` (`related_dept_id`),
|
||||
KEY `idx_related_club_id` (`related_club_id`),
|
||||
KEY `idx_create_time` (`create_time`),
|
||||
KEY `idx_tenant_deleted` (`tenant_id`, `deleted`)
|
||||
|
||||
@@ -44,6 +44,26 @@ export interface StglActivityVO {
|
||||
*/
|
||||
posterUrl?: string;
|
||||
|
||||
/**
|
||||
* 关联部门ID(多个部门时用逗号分隔)
|
||||
*/
|
||||
relatedDeptId?: string | number;
|
||||
|
||||
/**
|
||||
* 关联部门ID列表
|
||||
*/
|
||||
relatedDeptIds?: (string | number)[];
|
||||
|
||||
/**
|
||||
* 关联部门名称(多个部门时用逗号分隔)
|
||||
*/
|
||||
relatedDeptName?: string;
|
||||
|
||||
/**
|
||||
* 关联部门名称列表
|
||||
*/
|
||||
relatedDeptNames?: string[];
|
||||
|
||||
/**
|
||||
* 关联社团ID
|
||||
*/
|
||||
@@ -272,6 +292,26 @@ export interface StglActivityForm {
|
||||
*/
|
||||
posterUrl?: string;
|
||||
|
||||
/**
|
||||
* 关联部门ID(多个部门时用逗号分隔)
|
||||
*/
|
||||
relatedDeptId?: string | number;
|
||||
|
||||
/**
|
||||
* 关联部门ID列表
|
||||
*/
|
||||
relatedDeptIds?: (string | number)[];
|
||||
|
||||
/**
|
||||
* 关联部门对象(用于选择器绑定)
|
||||
*/
|
||||
relatedDept?: DeptOption | null;
|
||||
|
||||
/**
|
||||
* 关联部门对象列表(多选时使用)
|
||||
*/
|
||||
relatedDepts?: DeptOption[] | null;
|
||||
|
||||
/**
|
||||
* 关联社团ID
|
||||
*/
|
||||
@@ -454,6 +494,11 @@ export interface StglActivityQuery extends PageQuery {
|
||||
*/
|
||||
location?: string;
|
||||
|
||||
/**
|
||||
* 关联部门ID
|
||||
*/
|
||||
relatedDeptId?: string | number;
|
||||
|
||||
/**
|
||||
* 关联社团ID
|
||||
*/
|
||||
@@ -505,6 +550,21 @@ export interface StglActivityQuery extends PageQuery {
|
||||
params?: any;
|
||||
}
|
||||
|
||||
/**
|
||||
* 部门选择器选项
|
||||
*/
|
||||
export interface DeptOption {
|
||||
/**
|
||||
* 部门ID
|
||||
*/
|
||||
deptId: string | number;
|
||||
|
||||
/**
|
||||
* 部门名称
|
||||
*/
|
||||
deptName: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 社团/志愿者队伍选择器选项
|
||||
*/
|
||||
|
||||
@@ -45,6 +45,26 @@ export interface StglActivityVo {
|
||||
*/
|
||||
posterUrl: string;
|
||||
|
||||
/**
|
||||
* 关联部门ID(多个部门时用逗号分隔)
|
||||
*/
|
||||
relatedDeptId?: string | number;
|
||||
|
||||
/**
|
||||
* 关联部门ID列表
|
||||
*/
|
||||
relatedDeptIds?: (string | number)[];
|
||||
|
||||
/**
|
||||
* 关联部门名称(多个部门时用逗号分隔)
|
||||
*/
|
||||
relatedDeptName?: string;
|
||||
|
||||
/**
|
||||
* 关联部门名称列表
|
||||
*/
|
||||
relatedDeptNames?: string[];
|
||||
|
||||
/**
|
||||
* 关联社团ID
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,276 @@
|
||||
import request from '@/utils/request';
|
||||
import { AxiosPromise } from 'axios';
|
||||
|
||||
export interface StglClubAdvisorHistoryVO {
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
id?: string | number;
|
||||
|
||||
/**
|
||||
* 社团ID
|
||||
*/
|
||||
clubId?: string | number;
|
||||
|
||||
/**
|
||||
* 社团名称
|
||||
*/
|
||||
clubName?: string;
|
||||
|
||||
/**
|
||||
* 指导老师姓名
|
||||
*/
|
||||
advisorName?: string;
|
||||
|
||||
/**
|
||||
* 指导老师部门
|
||||
*/
|
||||
advisorDept?: string;
|
||||
|
||||
/**
|
||||
* 指导老师电话
|
||||
*/
|
||||
advisorPhone?: string;
|
||||
|
||||
/**
|
||||
* 指导老师邮箱
|
||||
*/
|
||||
advisorEmail?: string;
|
||||
|
||||
/**
|
||||
* 指导老师用户ID
|
||||
*/
|
||||
advisorUserId?: string | number;
|
||||
|
||||
/**
|
||||
* 在任状态:1-当前在任,0-历史(已离任)
|
||||
*/
|
||||
status?: number;
|
||||
|
||||
/**
|
||||
* 上任时间
|
||||
*/
|
||||
startTime?: string;
|
||||
|
||||
/**
|
||||
* 离任时间
|
||||
*/
|
||||
endTime?: string;
|
||||
|
||||
/**
|
||||
* 创建者
|
||||
*/
|
||||
createBy?: string | number;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
createTime?: string;
|
||||
|
||||
/**
|
||||
* 更新者
|
||||
*/
|
||||
updateBy?: string | number;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
updateTime?: string;
|
||||
}
|
||||
|
||||
export interface StglClubAdvisorHistoryForm extends BaseEntity {
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
id?: string | number;
|
||||
|
||||
/**
|
||||
* 社团ID
|
||||
*/
|
||||
clubId?: string | number;
|
||||
|
||||
/**
|
||||
* 指导老师姓名
|
||||
*/
|
||||
advisorName?: string;
|
||||
|
||||
/**
|
||||
* 指导老师部门
|
||||
*/
|
||||
advisorDept?: string;
|
||||
|
||||
/**
|
||||
* 指导老师电话
|
||||
*/
|
||||
advisorPhone?: string;
|
||||
|
||||
/**
|
||||
* 指导老师邮箱
|
||||
*/
|
||||
advisorEmail?: string;
|
||||
|
||||
/**
|
||||
* 指导老师用户ID
|
||||
*/
|
||||
advisorUserId?: string | number;
|
||||
|
||||
/**
|
||||
* 在任状态:1-当前在任,0-历史(已离任)
|
||||
*/
|
||||
status?: number;
|
||||
|
||||
/**
|
||||
* 上任时间
|
||||
*/
|
||||
startTime?: string;
|
||||
|
||||
/**
|
||||
* 离任时间
|
||||
*/
|
||||
endTime?: string;
|
||||
}
|
||||
|
||||
export interface StglClubAdvisorHistoryQuery extends PageQuery {
|
||||
/**
|
||||
* 社团ID
|
||||
*/
|
||||
clubId?: string | number;
|
||||
|
||||
/**
|
||||
* 指导老师姓名
|
||||
*/
|
||||
advisorName?: string;
|
||||
|
||||
/**
|
||||
* 指导老师部门
|
||||
*/
|
||||
advisorDept?: string;
|
||||
|
||||
/**
|
||||
* 指导老师电话
|
||||
*/
|
||||
advisorPhone?: string;
|
||||
|
||||
/**
|
||||
* 指导老师邮箱
|
||||
*/
|
||||
advisorEmail?: string;
|
||||
|
||||
/**
|
||||
* 指导老师用户ID
|
||||
*/
|
||||
advisorUserId?: string | number;
|
||||
|
||||
/**
|
||||
* 在任状态:1-当前在任,0-历史(已离任)
|
||||
*/
|
||||
status?: number;
|
||||
|
||||
/**
|
||||
* 上任时间
|
||||
*/
|
||||
startTime?: string;
|
||||
|
||||
/**
|
||||
* 离任时间
|
||||
*/
|
||||
endTime?: string;
|
||||
|
||||
/**
|
||||
* 日期范围参数
|
||||
*/
|
||||
params?: any;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询社团指导老师任职历史列表
|
||||
* @param query
|
||||
* @returns {*}
|
||||
*/
|
||||
|
||||
export const listStglClubAdvisorHistory = (query?: StglClubAdvisorHistoryQuery): AxiosPromise<StglClubAdvisorHistoryVO[]> => {
|
||||
return request({
|
||||
url: '/stgl/club/advisor/history/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 查询社团指导老师任职历史详细
|
||||
* @param id
|
||||
*/
|
||||
export const getStglClubAdvisorHistory = (id: string | number): AxiosPromise<StglClubAdvisorHistoryVO> => {
|
||||
return request({
|
||||
url: '/stgl/club/advisor/history/' + id,
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 查询指定社团的当前在任指导老师
|
||||
* @param clubId
|
||||
*/
|
||||
export const getCurrentAdvisors = (clubId: string | number): AxiosPromise<StglClubAdvisorHistoryVO[]> => {
|
||||
return request({
|
||||
url: '/stgl/club/advisor/history/current/' + clubId,
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 查询指定社团的历史指导老师(已离任)
|
||||
* @param clubId
|
||||
*/
|
||||
export const getHistoryAdvisors = (clubId: string | number): AxiosPromise<StglClubAdvisorHistoryVO[]> => {
|
||||
return request({
|
||||
url: '/stgl/club/advisor/history/history/' + clubId,
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 查询指定社团的所有指导老师(包含当前和历史)
|
||||
* @param clubId
|
||||
*/
|
||||
export const getAllAdvisors = (clubId: string | number): AxiosPromise<StglClubAdvisorHistoryVO[]> => {
|
||||
return request({
|
||||
url: '/stgl/club/advisor/history/all/' + clubId,
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 新增社团指导老师任职历史
|
||||
* @param data
|
||||
*/
|
||||
export const addStglClubAdvisorHistory = (data: StglClubAdvisorHistoryForm) => {
|
||||
return request({
|
||||
url: '/stgl/club/advisor/history',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 修改社团指导老师任职历史
|
||||
* @param data
|
||||
*/
|
||||
export const updateStglClubAdvisorHistory = (data: StglClubAdvisorHistoryForm) => {
|
||||
return request({
|
||||
url: '/stgl/club/advisor/history',
|
||||
method: 'put',
|
||||
data: data
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 删除社团指导老师任职历史
|
||||
* @param id
|
||||
*/
|
||||
export const delStglClubAdvisorHistory = (id: string | number | Array<string | number>) => {
|
||||
return request({
|
||||
url: '/stgl/club/advisor/history/' + id,
|
||||
method: 'delete'
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,222 @@
|
||||
import request from '@/utils/request';
|
||||
import { AxiosPromise } from 'axios';
|
||||
import { StglClubAnnualReviewVO, StglClubAnnualReviewForm, StglClubAnnualReviewQuery } from './type';
|
||||
|
||||
/**
|
||||
* 查询社团年审记录列表
|
||||
* @param query
|
||||
* @returns {*}
|
||||
*/
|
||||
export const listStglClubAnnualReview = (query?: StglClubAnnualReviewQuery): AxiosPromise<StglClubAnnualReviewVO[]> => {
|
||||
return request({
|
||||
url: '/stgl/club/annualReview/list',
|
||||
method: 'get',
|
||||
params: query,
|
||||
headers: {
|
||||
isEncrypt: 'true'
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 查询我的社团年审记录列表
|
||||
* @param query
|
||||
* @returns {*}
|
||||
*/
|
||||
export const listMyStglClubAnnualReview = (query?: StglClubAnnualReviewQuery): AxiosPromise<StglClubAnnualReviewVO[]> => {
|
||||
return request({
|
||||
url: '/stgl/club/annualReview/myList',
|
||||
method: 'get',
|
||||
params: query,
|
||||
headers: {
|
||||
isEncrypt: 'true'
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 查询社团年审记录详细
|
||||
* @param id
|
||||
*/
|
||||
export const getStglClubAnnualReview = (id: string | number): AxiosPromise<StglClubAnnualReviewVO> => {
|
||||
return request({
|
||||
url: '/stgl/club/annualReview/' + id,
|
||||
method: 'get',
|
||||
headers: {
|
||||
isEncrypt: 'true'
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 新增社团年审记录
|
||||
* @param data
|
||||
*/
|
||||
export const addStglClubAnnualReview = (data: StglClubAnnualReviewForm) => {
|
||||
return request({
|
||||
url: '/stgl/club/annualReview',
|
||||
method: 'post',
|
||||
data: data,
|
||||
headers: {
|
||||
isEncrypt: 'true'
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 修改社团年审记录
|
||||
* @param data
|
||||
*/
|
||||
export const updateStglClubAnnualReview = (data: StglClubAnnualReviewForm) => {
|
||||
return request({
|
||||
url: '/stgl/club/annualReview',
|
||||
method: 'put',
|
||||
data: data,
|
||||
headers: {
|
||||
isEncrypt: 'true'
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 删除社团年审记录
|
||||
* @param id
|
||||
*/
|
||||
export const delStglClubAnnualReview = (id: string | number | Array<string | number>) => {
|
||||
return request({
|
||||
url: '/stgl/club/annualReview/' + id,
|
||||
method: 'delete',
|
||||
headers: {
|
||||
isEncrypt: 'true'
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 提交年审记录
|
||||
* @param id
|
||||
*/
|
||||
export const submitStglClubAnnualReview = (id: string | number) => {
|
||||
return request({
|
||||
url: '/stgl/club/annualReview/submit/' + id,
|
||||
method: 'post',
|
||||
headers: {
|
||||
isEncrypt: 'true'
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 撤回年审记录
|
||||
* @param id
|
||||
*/
|
||||
export const withdrawStglClubAnnualReview = (id: string | number) => {
|
||||
return request({
|
||||
url: '/stgl/club/annualReview/withdraw/' + id,
|
||||
method: 'post',
|
||||
headers: {
|
||||
isEncrypt: 'true'
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 审核年审记录
|
||||
* @param params
|
||||
*/
|
||||
export const auditStglClubAnnualReview = (params: {
|
||||
id: string | number;
|
||||
auditResult: string;
|
||||
auditOpinion?: string;
|
||||
auditType: string;
|
||||
}) => {
|
||||
return request({
|
||||
url: '/stgl/club/annualReview/audit',
|
||||
method: 'post',
|
||||
params: params,
|
||||
headers: {
|
||||
isEncrypt: 'true'
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 检查是否可以创建年审记录
|
||||
* @param clubId
|
||||
* @param reviewYear
|
||||
*/
|
||||
export const checkCanCreateStglClubAnnualReview = (clubId: string | number, reviewYear: number): AxiosPromise<boolean> => {
|
||||
return request({
|
||||
url: '/stgl/club/annualReview/check',
|
||||
method: 'get',
|
||||
params: {
|
||||
clubId,
|
||||
reviewYear
|
||||
},
|
||||
headers: {
|
||||
isEncrypt: 'true'
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取年审状态选项
|
||||
*/
|
||||
export const getAnnualReviewStatusOptions = (): AxiosPromise<string[]> => {
|
||||
return request({
|
||||
url: '/stgl/club/annualReview/statusOptions',
|
||||
method: 'get',
|
||||
headers: {
|
||||
isEncrypt: 'true'
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 导出社团年审记录
|
||||
* @param query
|
||||
*/
|
||||
export const exportStglClubAnnualReview = (query?: StglClubAnnualReviewQuery) => {
|
||||
return request({
|
||||
url: '/stgl/club/annualReview/export',
|
||||
method: 'post',
|
||||
data: query,
|
||||
responseType: 'blob',
|
||||
headers: {
|
||||
isEncrypt: 'true'
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 根据社团ID和年审年度获取该年度的活动数据
|
||||
* @param clubId 社团ID
|
||||
* @param reviewYear 年审年度 (例如:2024)
|
||||
*/
|
||||
export const getClubActivitiesByReviewYear = (clubId: string | number, reviewYear: number) => {
|
||||
return request({
|
||||
url: '/stgl/club/annualReview/clubActivities',
|
||||
method: 'get',
|
||||
params: {
|
||||
clubId,
|
||||
reviewYear
|
||||
},
|
||||
headers: {
|
||||
isEncrypt: 'true'
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取年审记录的活动数据(从stgl_club_annual_review_activity表中获取)
|
||||
* @param annualReviewId 年审记录ID
|
||||
*/
|
||||
export const getAnnualReviewActivities = (annualReviewId: string | number) => {
|
||||
return request({
|
||||
url: '/stgl/club/annualReview/' + annualReviewId + '/activities',
|
||||
method: 'get',
|
||||
headers: {
|
||||
isEncrypt: 'true'
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,323 @@
|
||||
export interface StglClubAnnualReviewVO {
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
id: string | number;
|
||||
|
||||
/**
|
||||
* 社团ID
|
||||
*/
|
||||
clubId: string | number;
|
||||
|
||||
/**
|
||||
* 年审年度
|
||||
*/
|
||||
reviewYear: number;
|
||||
|
||||
/**
|
||||
* 年审开始日期
|
||||
*/
|
||||
reviewStartDate: string;
|
||||
|
||||
/**
|
||||
* 年审结束日期
|
||||
*/
|
||||
reviewEndDate: string;
|
||||
|
||||
/**
|
||||
* 年审时间
|
||||
*/
|
||||
reviewTime: string;
|
||||
|
||||
/**
|
||||
* 社团名称
|
||||
*/
|
||||
clubName: string;
|
||||
|
||||
/**
|
||||
* 成立日期
|
||||
*/
|
||||
establishDate: string;
|
||||
|
||||
/**
|
||||
* 社长姓名
|
||||
*/
|
||||
presidentName: string;
|
||||
|
||||
/**
|
||||
* 社团类型
|
||||
*/
|
||||
clubType: string;
|
||||
|
||||
/**
|
||||
* 指导老师姓名
|
||||
*/
|
||||
advisorName: string;
|
||||
|
||||
/**
|
||||
* 指导老师部门
|
||||
*/
|
||||
advisorDept: string;
|
||||
|
||||
/**
|
||||
* 是否有章程
|
||||
*/
|
||||
hasCharter: string;
|
||||
|
||||
/**
|
||||
* 章程文件URL
|
||||
*/
|
||||
charterUrl: string;
|
||||
|
||||
/**
|
||||
* 是否有资金
|
||||
*/
|
||||
hasFunds: string;
|
||||
|
||||
/**
|
||||
* 资金数额
|
||||
*/
|
||||
fundAmount: number;
|
||||
|
||||
/**
|
||||
* 成员数量
|
||||
*/
|
||||
memberCount: number;
|
||||
|
||||
/**
|
||||
* 状态(0-草稿 1-已提交 2-审核中 3-审核通过 4-审核不通过)
|
||||
*/
|
||||
status: string;
|
||||
|
||||
/**
|
||||
* 指导老师意见
|
||||
*/
|
||||
advisorOpinion: string;
|
||||
|
||||
/**
|
||||
* 学院意见
|
||||
*/
|
||||
collegeOpinion: string;
|
||||
|
||||
/**
|
||||
* 团委意见
|
||||
*/
|
||||
youthLeagueOpinion: string;
|
||||
|
||||
/**
|
||||
* 创建者
|
||||
*/
|
||||
createBy: string | number;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
createTime: string;
|
||||
|
||||
/**
|
||||
* 更新者
|
||||
*/
|
||||
updateBy: string | number;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
updateTime: string;
|
||||
}
|
||||
|
||||
export interface StglClubAnnualReviewForm {
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
id?: string | number;
|
||||
|
||||
/**
|
||||
* 社团ID
|
||||
*/
|
||||
clubId: string | number;
|
||||
|
||||
/**
|
||||
* 年审年度
|
||||
*/
|
||||
reviewYear: number;
|
||||
|
||||
/**
|
||||
* 年审开始日期
|
||||
*/
|
||||
reviewStartDate: string;
|
||||
|
||||
/**
|
||||
* 年审结束日期
|
||||
*/
|
||||
reviewEndDate: string;
|
||||
|
||||
/**
|
||||
* 年审时间
|
||||
*/
|
||||
reviewTime: string;
|
||||
|
||||
/**
|
||||
* 社团名称
|
||||
*/
|
||||
clubName: string;
|
||||
|
||||
/**
|
||||
* 成立日期
|
||||
*/
|
||||
establishDate?: string;
|
||||
|
||||
/**
|
||||
* 社团类型
|
||||
*/
|
||||
clubType?: string;
|
||||
|
||||
/**
|
||||
* 指导老师姓名
|
||||
*/
|
||||
advisorName?: string;
|
||||
|
||||
/**
|
||||
* 指导老师部门
|
||||
*/
|
||||
advisorDept?: string;
|
||||
|
||||
/**
|
||||
* 是否有章程
|
||||
*/
|
||||
hasCharter?: string;
|
||||
|
||||
/**
|
||||
* 章程文件URL
|
||||
*/
|
||||
charterUrl?: string;
|
||||
|
||||
/**
|
||||
* 是否有资金
|
||||
*/
|
||||
hasFunds?: string;
|
||||
|
||||
/**
|
||||
* 资金数额
|
||||
*/
|
||||
fundAmount?: number;
|
||||
|
||||
/**
|
||||
* 成员数量
|
||||
*/
|
||||
memberCount?: number;
|
||||
|
||||
/**
|
||||
* 状态(0-草稿 1-已提交 2-审核中 3-审核通过 4-审核不通过)
|
||||
*/
|
||||
status?: string;
|
||||
|
||||
/**
|
||||
* 指导老师意见
|
||||
*/
|
||||
advisorOpinion?: string;
|
||||
|
||||
/**
|
||||
* 学院意见
|
||||
*/
|
||||
collegeOpinion?: string;
|
||||
|
||||
/**
|
||||
* 团委意见
|
||||
*/
|
||||
youthLeagueOpinion?: string;
|
||||
|
||||
/**
|
||||
* 成员信息JSON
|
||||
*/
|
||||
membersJson?: string;
|
||||
|
||||
/**
|
||||
* 活动信息JSON
|
||||
*/
|
||||
activitiesJson?: string;
|
||||
}
|
||||
|
||||
export interface StglClubAnnualReviewQuery extends PageQuery {
|
||||
/**
|
||||
* 社团ID
|
||||
*/
|
||||
clubId?: string | number;
|
||||
|
||||
/**
|
||||
* 年审年度
|
||||
*/
|
||||
reviewYear?: number;
|
||||
|
||||
/**
|
||||
* 年审开始日期范围
|
||||
*/
|
||||
beginReviewStartDate?: string;
|
||||
endReviewStartDate?: string;
|
||||
|
||||
/**
|
||||
* 年审结束日期范围
|
||||
*/
|
||||
beginReviewEndDate?: string;
|
||||
endReviewEndDate?: string;
|
||||
|
||||
/**
|
||||
* 社团名称
|
||||
*/
|
||||
clubName?: string;
|
||||
|
||||
/**
|
||||
* 社团类型
|
||||
*/
|
||||
clubType?: string;
|
||||
|
||||
/**
|
||||
* 状态(0-草稿 1-已提交 2-审核中 3-审核通过 4-审核不通过)
|
||||
*/
|
||||
status?: string;
|
||||
|
||||
/**
|
||||
* 其他查询参数
|
||||
*/
|
||||
params?: { [key: string]: any };
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询接口
|
||||
*/
|
||||
export interface PageQuery {
|
||||
/**
|
||||
* 分页大小
|
||||
*/
|
||||
pageSize?: number;
|
||||
/**
|
||||
* 当前页数
|
||||
*/
|
||||
pageNum?: number;
|
||||
/**
|
||||
* 排序列
|
||||
*/
|
||||
orderByColumn?: string;
|
||||
/**
|
||||
* 排序的方向desc或者asc
|
||||
*/
|
||||
isAsc?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 年审状态选项
|
||||
*/
|
||||
export const ANNUAL_REVIEW_STATUS_OPTIONS = [
|
||||
{ label: '草稿', value: '0' },
|
||||
{ label: '已提交', value: '1' },
|
||||
{ label: '审核中', value: '2' },
|
||||
{ label: '审核通过', value: '3' },
|
||||
{ label: '审核不通过', value: '4' }
|
||||
];
|
||||
|
||||
/**
|
||||
* 是否选项
|
||||
*/
|
||||
export const YES_NO_OPTIONS = [
|
||||
{ label: '是', value: 'Y' },
|
||||
{ label: '否', value: 'N' }
|
||||
];
|
||||
@@ -0,0 +1,93 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 社团活动权限检查结果类型定义
|
||||
export interface ClubActivityPermissionResult {
|
||||
canCreateActivity: boolean
|
||||
status: 'NORMAL' | 'TEMPORARILY_LIMITED' | 'PERMANENTLY_LIMITED'
|
||||
message: string
|
||||
deadlineTime?: string
|
||||
remainingHours?: number
|
||||
remainingDays?: number
|
||||
annualReviewStartTime?: string
|
||||
}
|
||||
|
||||
// 权限详情类型定义
|
||||
export interface ClubPermissionDetails {
|
||||
permissionResult: ClubActivityPermissionResult
|
||||
isInAnnualReviewWindow: boolean
|
||||
daysUntilNextAnnualReview: number
|
||||
hasSubmittedCurrentYearAnnualReview: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查社团活动权限
|
||||
* @param clubId 社团ID (支持字符串和数字类型,避免大整数精度丢失)
|
||||
* @returns 权限检查结果
|
||||
*/
|
||||
export function checkClubActivityPermission(clubId: number | string) {
|
||||
return request<ClubActivityPermissionResult>({
|
||||
url: `/stgl/club/permission/checkActivityPermission/${clubId}`,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量检查多个社团的权限状态
|
||||
* @param clubIds 社团ID列表
|
||||
* @returns 权限检查结果映射
|
||||
*/
|
||||
export function batchCheckClubPermission(clubIds: number[]) {
|
||||
return request<Record<number, ClubActivityPermissionResult>>({
|
||||
url: '/stgl/club/permission/batchCheckPermission',
|
||||
method: 'post',
|
||||
data: clubIds
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查社团是否在年审窗口期内
|
||||
* @param clubId 社团ID
|
||||
* @returns 是否在年审窗口期
|
||||
*/
|
||||
export function isInAnnualReviewWindow(clubId: number) {
|
||||
return request<boolean>({
|
||||
url: `/stgl/club/permission/isInAnnualReviewWindow/${clubId}`,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取社团距离下次年审的天数
|
||||
* @param clubId 社团ID
|
||||
* @returns 距离年审的天数
|
||||
*/
|
||||
export function getDaysUntilNextAnnualReview(clubId: number) {
|
||||
return request<number>({
|
||||
url: `/stgl/club/permission/getDaysUntilNextAnnualReview/${clubId}`,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查社团是否已在当前年审期内提交年审
|
||||
* @param clubId 社团ID
|
||||
* @returns 是否已提交年审
|
||||
*/
|
||||
export function hasSubmittedCurrentYearAnnualReview(clubId: number) {
|
||||
return request<boolean>({
|
||||
url: `/stgl/club/permission/hasSubmittedCurrentYearAnnualReview/${clubId}`,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取社团权限详细信息(综合接口)
|
||||
* @param clubId 社团ID
|
||||
* @returns 权限详细信息
|
||||
*/
|
||||
export function getClubPermissionDetails(clubId: number) {
|
||||
return request<ClubPermissionDetails>({
|
||||
url: `/stgl/club/permission/getPermissionDetails/${clubId}`,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
@@ -97,6 +97,10 @@ export interface MyActivityVO {
|
||||
undertakerName?: string;
|
||||
/** 关联类型(1-社团 2-志愿者队伍) */
|
||||
relatedType?: number;
|
||||
/** 关联部门名称 */
|
||||
relatedDeptName?: string;
|
||||
/** 关联部门名称列表 */
|
||||
relatedDeptNames?: string;
|
||||
/** 关联组织名称(社团名称或志愿者团队名称) */
|
||||
organizationName?: string;
|
||||
/** 指导老师 */
|
||||
|
||||
@@ -74,6 +74,21 @@ export interface StglClassVO {
|
||||
* 参与活动数量
|
||||
*/
|
||||
activityCount: number;
|
||||
|
||||
/**
|
||||
* 今日活动名称(审核通过且时间为今天的活动)
|
||||
*/
|
||||
todayActivityName?: string;
|
||||
|
||||
/**
|
||||
* 今日活动开始时间
|
||||
*/
|
||||
todayActivityStartTime?: string;
|
||||
|
||||
/**
|
||||
* 今日活动结束时间
|
||||
*/
|
||||
todayActivityEndTime?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,399 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
:model-value="visible"
|
||||
:title="title"
|
||||
width="1000px"
|
||||
append-to-body
|
||||
@update:model-value="handleClose"
|
||||
>
|
||||
<div class="advisor-history-container">
|
||||
<!-- 当前指导老师 -->
|
||||
<div class="current-advisors-section">
|
||||
<h4 class="section-title">
|
||||
<el-icon><User /></el-icon>
|
||||
当前指导老师
|
||||
</h4>
|
||||
<div v-if="currentAdvisors.length === 0" class="no-data">
|
||||
<el-empty description="暂无当前指导老师" :image-size="60" />
|
||||
</div>
|
||||
<div v-else class="advisor-cards">
|
||||
<div
|
||||
v-for="advisor in currentAdvisors"
|
||||
:key="advisor.id"
|
||||
class="advisor-card current"
|
||||
>
|
||||
<div class="advisor-header">
|
||||
<span class="advisor-name">{{ advisor.advisorName }}</span>
|
||||
<el-tag type="success" size="small">在任</el-tag>
|
||||
</div>
|
||||
<div class="advisor-info">
|
||||
<div class="info-item">
|
||||
<el-icon><OfficeBuilding /></el-icon>
|
||||
<span>{{ advisor.advisorDept || '暂无' }}</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<el-icon><Phone /></el-icon>
|
||||
<span>{{ advisor.advisorPhone || '暂无' }}</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<el-icon><Message /></el-icon>
|
||||
<span>{{ advisor.advisorEmail || '暂无' }}</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<el-icon><Calendar /></el-icon>
|
||||
<span>上任时间:{{ formatDate(advisor.startTime) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 历史记录 -->
|
||||
<div class="history-section">
|
||||
<h4 class="section-title">
|
||||
<el-icon><Clock /></el-icon>
|
||||
历史记录
|
||||
<span class="record-count">({{ historyAdvisors.length }}条)</span>
|
||||
</h4>
|
||||
<div v-if="historyAdvisors.length === 0" class="no-data">
|
||||
<el-empty description="暂无历史记录" :image-size="60" />
|
||||
</div>
|
||||
<div v-else>
|
||||
<el-table
|
||||
:data="historyAdvisors"
|
||||
style="width: 100%"
|
||||
max-height="300"
|
||||
stripe
|
||||
>
|
||||
<el-table-column label="姓名" prop="advisorName" width="180" />
|
||||
<el-table-column label="部门" prop="advisorDept" min-width="120" show-overflow-tooltip />
|
||||
<el-table-column label="联系方式" min-width="120">
|
||||
<template #default="scope">
|
||||
<div class="contact-info">
|
||||
<div v-if="scope.row.advisorPhone" class="contact-item">
|
||||
<el-icon><Phone /></el-icon>
|
||||
{{ scope.row.advisorPhone }}
|
||||
</div>
|
||||
<div v-if="scope.row.advisorEmail" class="contact-item email">
|
||||
<el-icon><Message /></el-icon>
|
||||
{{ scope.row.advisorEmail }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="上任时间" width="100">
|
||||
<template #default="scope">
|
||||
{{ formatDate(scope.row.startTime) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="离任时间" width="100">
|
||||
<template #default="scope">
|
||||
{{ formatDate(scope.row.endTime) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="任期" width="130">
|
||||
<template #default="scope">
|
||||
{{ calculateDuration(scope.row.startTime, scope.row.endTime) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="handleClose">关 闭</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, watch, computed } from 'vue';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import { User, OfficeBuilding, Phone, Message, Calendar, Clock } from '@element-plus/icons-vue';
|
||||
import { getCurrentAdvisors, getHistoryAdvisors } from '@/api/stgl/club/advisor-history';
|
||||
import type { StglClubAdvisorHistoryVO } from '@/api/stgl/club/advisor-history';
|
||||
|
||||
interface Props {
|
||||
visible: boolean;
|
||||
title: string;
|
||||
clubId?: string | number;
|
||||
clubName?: string;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
visible: false,
|
||||
title: '指导老师历史记录',
|
||||
clubId: undefined,
|
||||
clubName: ''
|
||||
});
|
||||
|
||||
const emit = defineEmits<{
|
||||
'update:visible': [value: boolean];
|
||||
}>();
|
||||
|
||||
// 响应式数据
|
||||
const currentAdvisors = ref<StglClubAdvisorHistoryVO[]>([]);
|
||||
const historyAdvisors = ref<StglClubAdvisorHistoryVO[]>([]);
|
||||
const loading = ref(false);
|
||||
|
||||
// 计算属性
|
||||
const dialogTitle = computed(() => {
|
||||
return props.clubName ? `${props.clubName} - ${props.title}` : props.title;
|
||||
});
|
||||
|
||||
// 监听弹窗显示状态
|
||||
watch(() => props.visible, (newVal) => {
|
||||
if (newVal && props.clubId) {
|
||||
loadAdvisorData();
|
||||
}
|
||||
});
|
||||
|
||||
// 监听社团ID变化
|
||||
watch(() => props.clubId, (newVal) => {
|
||||
if (newVal && props.visible) {
|
||||
loadAdvisorData();
|
||||
}
|
||||
});
|
||||
|
||||
// 加载指导老师数据
|
||||
const loadAdvisorData = async () => {
|
||||
if (!props.clubId) {
|
||||
ElMessage.warning('社团ID不能为空');
|
||||
return;
|
||||
}
|
||||
|
||||
loading.value = true;
|
||||
try {
|
||||
// 并行加载当前和历史指导老师数据
|
||||
const [currentRes, historyRes] = await Promise.all([
|
||||
getCurrentAdvisors(props.clubId),
|
||||
getHistoryAdvisors(props.clubId)
|
||||
]);
|
||||
|
||||
currentAdvisors.value = currentRes.data || [];
|
||||
historyAdvisors.value = historyRes.data || [];
|
||||
|
||||
console.log('指导老师数据加载成功:', {
|
||||
current: currentAdvisors.value.length,
|
||||
history: historyAdvisors.value.length
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('加载指导老师数据失败:', error);
|
||||
ElMessage.error('加载指导老师数据失败');
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 关闭弹窗
|
||||
const handleClose = () => {
|
||||
emit('update:visible', false);
|
||||
};
|
||||
|
||||
// 格式化日期
|
||||
const formatDate = (dateStr?: string) => {
|
||||
if (!dateStr) return '暂无';
|
||||
const date = new Date(dateStr);
|
||||
return date.toLocaleDateString('zh-CN', {
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
day: '2-digit'
|
||||
});
|
||||
};
|
||||
|
||||
// 计算任期
|
||||
const calculateDuration = (startTime?: string, endTime?: string) => {
|
||||
if (!startTime || !endTime) return '暂无';
|
||||
|
||||
const start = new Date(startTime);
|
||||
const end = new Date(endTime);
|
||||
const diffTime = Math.abs(end.getTime() - start.getTime());
|
||||
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
|
||||
|
||||
if (diffDays < 30) {
|
||||
return `${diffDays}天`;
|
||||
} else if (diffDays < 365) {
|
||||
const months = Math.floor(diffDays / 30);
|
||||
const days = diffDays % 30;
|
||||
return days > 0 ? `${months}个月${days}天` : `${months}个月`;
|
||||
} else {
|
||||
const years = Math.floor(diffDays / 365);
|
||||
const months = Math.floor((diffDays % 365) / 30);
|
||||
const days = (diffDays % 365) % 30;
|
||||
|
||||
let result = `${years}年`;
|
||||
if (months > 0) result += `${months}个月`;
|
||||
if (days > 0) result += `${days}天`;
|
||||
return result;
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.advisor-history-container {
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin: 0 0 16px 0;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
border-bottom: 2px solid #409eff;
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
|
||||
.record-count {
|
||||
font-size: 14px;
|
||||
font-weight: normal;
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
.current-advisors-section {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.history-section {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.no-data {
|
||||
text-align: center;
|
||||
padding: 20px 0;
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
/* 当前指导老师卡片样式 */
|
||||
.advisor-cards {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.advisor-card {
|
||||
flex: 1;
|
||||
min-width: 300px;
|
||||
padding: 16px;
|
||||
border-radius: 8px;
|
||||
border: 1px solid #e4e7ed;
|
||||
background: #fff;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.advisor-card.current {
|
||||
border-color: #67c23a;
|
||||
background: linear-gradient(135deg, #f0f9ff 0%, #f8fafc 100%);
|
||||
}
|
||||
|
||||
.advisor-card:hover {
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.advisor-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.advisor-name {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
.advisor-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.info-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-size: 14px;
|
||||
color: #606266;
|
||||
}
|
||||
|
||||
.info-item .el-icon {
|
||||
color: #909399;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
/* 历史记录表格样式 */
|
||||
.contact-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.contact-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
font-size: 12px;
|
||||
color: #606266;
|
||||
}
|
||||
|
||||
.contact-item.email {
|
||||
color: #409eff;
|
||||
}
|
||||
|
||||
.contact-item .el-icon {
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
/* 表格样式优化 */
|
||||
:deep(.el-table) {
|
||||
border-radius: 6px;
|
||||
overflow: hidden;
|
||||
width: 100% !important;
|
||||
table-layout: auto;
|
||||
}
|
||||
|
||||
:deep(.el-table th) {
|
||||
background-color: #fafafa;
|
||||
color: #606266;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
:deep(.el-table td) {
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
:deep(.el-table__body tr:hover > td) {
|
||||
background-color: #f5f7fa;
|
||||
}
|
||||
|
||||
/* 确保表格充满容器 */
|
||||
:deep(.el-table__header-wrapper),
|
||||
:deep(.el-table__body-wrapper) {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
:deep(.el-table__header),
|
||||
:deep(.el-table__body) {
|
||||
width: 100% !important;
|
||||
table-layout: auto;
|
||||
}
|
||||
|
||||
/* 响应式设计 */
|
||||
@media (max-width: 768px) {
|
||||
.advisor-cards {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.advisor-card {
|
||||
min-width: auto;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -64,10 +64,14 @@ export interface AdvisorVO {
|
||||
dept: string;
|
||||
email: string;
|
||||
userId?: string;
|
||||
startTime?: string; // 上任时间
|
||||
endTime?: string; // 离任时间
|
||||
}
|
||||
|
||||
interface Props {
|
||||
modelValue: AdvisorVO[];
|
||||
establishDate?: string; // 社团成立日期
|
||||
isNewClub?: boolean; // 是否为新建社团
|
||||
}
|
||||
|
||||
interface Emits {
|
||||
@@ -78,42 +82,74 @@ interface Emits {
|
||||
const props = defineProps<Props>();
|
||||
const emit = defineEmits<Emits>();
|
||||
|
||||
const advisors = ref<AdvisorVO[]>([...props.modelValue]);
|
||||
const advisors = ref<AdvisorVO[]>([]);
|
||||
|
||||
// 监听props变化
|
||||
// 使用computed来避免双重监听导致的递归更新
|
||||
const advisorsData = computed({
|
||||
get: () => props.modelValue || [],
|
||||
set: (newValue: AdvisorVO[]) => {
|
||||
emit('update:modelValue', newValue);
|
||||
emit('change', newValue);
|
||||
}
|
||||
});
|
||||
|
||||
// 初始化数据
|
||||
advisors.value = advisorsData.value.map((item) => ({ ...item }));
|
||||
|
||||
// 只监听props变化来同步内部数据
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(newValue) => {
|
||||
advisors.value = newValue ? newValue.map((item) => ({ ...item })) : [];
|
||||
console.log('AdvisorManager: props.modelValue 变化:', newValue);
|
||||
if (newValue) {
|
||||
advisors.value = newValue.map((item) => ({ ...item }));
|
||||
console.log('AdvisorManager: 设置后的 advisors.value:', advisors.value);
|
||||
} else {
|
||||
advisors.value = [];
|
||||
}
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
// 监听内部数据变化,同步到父组件
|
||||
watch(
|
||||
advisors,
|
||||
(newValue) => {
|
||||
emit('update:modelValue', newValue);
|
||||
emit('change', newValue);
|
||||
},
|
||||
{ deep: true }
|
||||
{ deep: true, immediate: true }
|
||||
);
|
||||
|
||||
// 添加指导老师
|
||||
const addAdvisor = () => {
|
||||
advisors.value.push({
|
||||
// 智能设置上任时间:
|
||||
// 1. 如果是新建社团的首任指导老师且有社团成立日期,使用成立日期
|
||||
// 2. 否则使用当前日期
|
||||
let startTime = '';
|
||||
const isFirstAdvisor = advisors.value.length === 0;
|
||||
const isNewClub = props.isNewClub !== false; // 默认认为是新建社团,除非明确传入false
|
||||
|
||||
if (isFirstAdvisor && isNewClub && props.establishDate) {
|
||||
startTime = props.establishDate;
|
||||
console.log('AdvisorManager: 新建社团的首任指导老师,上任时间设置为社团成立日期:', startTime);
|
||||
} else {
|
||||
const today = new Date();
|
||||
startTime = today.getFullYear() + '-' +
|
||||
String(today.getMonth() + 1).padStart(2, '0') + '-' +
|
||||
String(today.getDate()).padStart(2, '0');
|
||||
console.log('AdvisorManager: 后续新增指导老师,上任时间设置为当前日期:', startTime);
|
||||
}
|
||||
|
||||
const newAdvisors = [...advisors.value, {
|
||||
teacher: null,
|
||||
name: '',
|
||||
phone: '',
|
||||
dept: '',
|
||||
email: '',
|
||||
userId: ''
|
||||
});
|
||||
userId: '',
|
||||
startTime: startTime,
|
||||
endTime: undefined
|
||||
}];
|
||||
advisors.value = newAdvisors;
|
||||
advisorsData.value = newAdvisors;
|
||||
};
|
||||
|
||||
// 删除指导老师
|
||||
const removeAdvisor = (index: number) => {
|
||||
advisors.value.splice(index, 1);
|
||||
const newAdvisors = advisors.value.filter((_, i) => i !== index);
|
||||
advisors.value = newAdvisors;
|
||||
advisorsData.value = newAdvisors;
|
||||
};
|
||||
|
||||
// 监听teacher选择变化,自动填充相关信息
|
||||
@@ -121,28 +157,46 @@ const handleTeacherChange = (advisor: AdvisorVO, teacher: TeacherVO | null) => {
|
||||
console.log('handleTeacherChange called with teacher:', teacher);
|
||||
|
||||
if (teacher) {
|
||||
// 自动填充老师信息
|
||||
// 自动填充老师信息,保留时间字段
|
||||
const originalStartTime = advisor.startTime;
|
||||
const originalEndTime = advisor.endTime;
|
||||
|
||||
advisor.teacher = teacher;
|
||||
advisor.name = teacher.nickName || '';
|
||||
advisor.phone = teacher.phonenumber || '暂无电话';
|
||||
advisor.dept = teacher.deptName || '';
|
||||
advisor.email = teacher.email || '暂无邮箱';
|
||||
advisor.userId = teacher.userId?.toString() || '';
|
||||
advisor.startTime = originalStartTime; // 保留原有的上任时间
|
||||
advisor.endTime = originalEndTime; // 保留原有的离任时间
|
||||
|
||||
console.log('Updated advisor:', {
|
||||
name: advisor.name,
|
||||
phone: advisor.phone,
|
||||
dept: advisor.dept,
|
||||
email: advisor.email,
|
||||
userId: advisor.userId
|
||||
userId: advisor.userId,
|
||||
startTime: advisor.startTime,
|
||||
endTime: advisor.endTime
|
||||
});
|
||||
} else {
|
||||
// 清空选择时重置所有信息
|
||||
// 清空选择时重置所有信息,但保留时间字段
|
||||
const originalStartTime = advisor.startTime;
|
||||
const originalEndTime = advisor.endTime;
|
||||
|
||||
advisor.teacher = null;
|
||||
advisor.name = '';
|
||||
advisor.phone = '';
|
||||
advisor.dept = '';
|
||||
advisor.email = '';
|
||||
advisor.userId = '';
|
||||
advisor.startTime = originalStartTime; // 保留原有的上任时间
|
||||
advisor.endTime = originalEndTime; // 保留原有的离任时间
|
||||
}
|
||||
|
||||
// 触发更新
|
||||
const newAdvisors = [...advisors.value];
|
||||
advisorsData.value = newAdvisors;
|
||||
};
|
||||
|
||||
// 移除有问题的深度监听,改为在TeacherSelector的change事件中处理
|
||||
|
||||
@@ -0,0 +1,136 @@
|
||||
<template>
|
||||
<el-select
|
||||
v-model="selectedDepts"
|
||||
placeholder="请选择关联部门"
|
||||
clearable
|
||||
filterable
|
||||
multiple
|
||||
style="width: 100%; height: 32px;"
|
||||
size="default"
|
||||
@change="handleChange"
|
||||
@clear="handleClear"
|
||||
>
|
||||
<el-option
|
||||
v-for="dept in deptOptions"
|
||||
:key="dept.deptId"
|
||||
:label="dept.deptName"
|
||||
:value="dept.deptId"
|
||||
/>
|
||||
</el-select>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, watch } from 'vue';
|
||||
import { listDept } from '@/api/system/dept';
|
||||
import { DeptVO } from '@/api/system/dept/types';
|
||||
|
||||
interface DeptOption {
|
||||
deptId: string | number;
|
||||
deptName: string;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
modelValue?: DeptOption[] | null;
|
||||
}
|
||||
|
||||
interface Emits {
|
||||
(e: 'update:modelValue', value: DeptOption[] | null): void;
|
||||
(e: 'change', value: DeptOption[] | null): void;
|
||||
}
|
||||
|
||||
const props = defineProps<Props>();
|
||||
const emit = defineEmits<Emits>();
|
||||
|
||||
const selectedDepts = ref<(string | number)[]>([]);
|
||||
const deptOptions = ref<DeptOption[]>([]);
|
||||
|
||||
// 获取部门列表数据
|
||||
const loadDeptOptions = async () => {
|
||||
try {
|
||||
const response = await listDept({ parentId: 101 } as any);
|
||||
if (response.data) {
|
||||
// 过滤出parent_id=101的部门
|
||||
const filteredDepts = response.data.filter((dept: DeptVO) => dept.parentId === 101);
|
||||
deptOptions.value = filteredDepts.map((dept: DeptVO) => ({
|
||||
deptId: dept.deptId,
|
||||
deptName: dept.deptName
|
||||
}));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取部门列表失败:', error);
|
||||
deptOptions.value = [];
|
||||
}
|
||||
};
|
||||
|
||||
// 处理选择变化
|
||||
const handleChange = (values: (string | number)[]) => {
|
||||
if (values && values.length > 0) {
|
||||
const selectedOptions = values
|
||||
.map(value => deptOptions.value.find(dept => dept.deptId === value))
|
||||
.filter(Boolean) as DeptOption[];
|
||||
|
||||
emit('update:modelValue', selectedOptions);
|
||||
emit('change', selectedOptions);
|
||||
} else {
|
||||
emit('update:modelValue', null);
|
||||
emit('change', null);
|
||||
}
|
||||
};
|
||||
|
||||
// 处理清空
|
||||
const handleClear = () => {
|
||||
emit('update:modelValue', null);
|
||||
emit('change', null);
|
||||
};
|
||||
|
||||
// 监听外部传入的值变化
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(newValue) => {
|
||||
if (newValue && Array.isArray(newValue)) {
|
||||
selectedDepts.value = newValue.map(dept => dept.deptId);
|
||||
} else {
|
||||
selectedDepts.value = [];
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
onMounted(() => {
|
||||
loadDeptOptions();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 多选部门选择器样式 - 参考系统统一高度设置 */
|
||||
:deep(.el-select) {
|
||||
height: 32px !important;
|
||||
}
|
||||
|
||||
:deep(.el-select .el-input__wrapper) {
|
||||
min-height: 32px !important;
|
||||
height: 32px !important;
|
||||
}
|
||||
|
||||
/* 多选标签容器 - 参考系统设置 */
|
||||
:deep(.el-select__tags) {
|
||||
max-height: 28px !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
|
||||
/* 标签样式 - 显示完整部门名称 */
|
||||
:deep(.el-tag) {
|
||||
height: 20px !important;
|
||||
line-height: 18px !important;
|
||||
margin: 4px 6px 4px 0 !important;
|
||||
font-size: 12px !important;
|
||||
white-space: nowrap !important;
|
||||
display: inline-flex !important;
|
||||
align-items: center !important;
|
||||
}
|
||||
|
||||
/* 标签关闭按钮 */
|
||||
:deep(.el-tag .el-tag__close) {
|
||||
margin-left: 4px !important;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,100 @@
|
||||
<template>
|
||||
<el-select
|
||||
v-model="selectedDept"
|
||||
placeholder="请选择关联部门"
|
||||
clearable
|
||||
filterable
|
||||
style="width: 100%"
|
||||
@change="handleChange"
|
||||
@clear="handleClear"
|
||||
>
|
||||
<el-option
|
||||
v-for="dept in deptOptions"
|
||||
:key="dept.deptId"
|
||||
:label="dept.deptName"
|
||||
:value="dept.deptId"
|
||||
/>
|
||||
</el-select>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, watch } from 'vue';
|
||||
import { listDept } from '@/api/system/dept';
|
||||
import { DeptVO } from '@/api/system/dept/types';
|
||||
|
||||
interface DeptOption {
|
||||
deptId: string | number;
|
||||
deptName: string;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
modelValue?: DeptOption | null;
|
||||
}
|
||||
|
||||
interface Emits {
|
||||
(e: 'update:modelValue', value: DeptOption | null): void;
|
||||
(e: 'change', value: DeptOption | null): void;
|
||||
}
|
||||
|
||||
const props = defineProps<Props>();
|
||||
const emit = defineEmits<Emits>();
|
||||
|
||||
const selectedDept = ref<string | number>();
|
||||
const deptOptions = ref<DeptOption[]>([]);
|
||||
|
||||
// 获取部门列表数据
|
||||
const loadDeptOptions = async () => {
|
||||
try {
|
||||
const response = await listDept({ parentId: 101 } as any);
|
||||
if (response.data) {
|
||||
// 过滤出parent_id=101的部门
|
||||
const filteredDepts = response.data.filter((dept: DeptVO) => dept.parentId === 101);
|
||||
deptOptions.value = filteredDepts.map((dept: DeptVO) => ({
|
||||
deptId: dept.deptId,
|
||||
deptName: dept.deptName
|
||||
}));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取部门列表失败:', error);
|
||||
deptOptions.value = [];
|
||||
}
|
||||
};
|
||||
|
||||
// 处理选择变化
|
||||
const handleChange = (value: string | number) => {
|
||||
if (value) {
|
||||
const selectedOption = deptOptions.value.find(dept => dept.deptId === value);
|
||||
if (selectedOption) {
|
||||
emit('update:modelValue', selectedOption);
|
||||
emit('change', selectedOption);
|
||||
}
|
||||
} else {
|
||||
emit('update:modelValue', null);
|
||||
emit('change', null);
|
||||
}
|
||||
};
|
||||
|
||||
// 处理清空
|
||||
const handleClear = () => {
|
||||
selectedDept.value = undefined;
|
||||
emit('update:modelValue', null);
|
||||
emit('change', null);
|
||||
};
|
||||
|
||||
// 监听外部传入的值变化
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(newValue) => {
|
||||
if (newValue) {
|
||||
selectedDept.value = newValue.deptId;
|
||||
} else {
|
||||
selectedDept.value = undefined;
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
onMounted(() => {
|
||||
loadDeptOptions();
|
||||
});
|
||||
</script>
|
||||
@@ -38,7 +38,7 @@
|
||||
<!-- </el-link> -->
|
||||
<div class="ele-upload-list__item-content-action">
|
||||
<el-button type="danger" v-if="!disabled" link @click="handleDelete(index)">删除</el-button>
|
||||
<el-button type="danger" v-if="!disabled" link @click="handleDownloadAttachment(file)">下载</el-button>
|
||||
<el-button type="primary" link @click="handleDownloadAttachment(file)">下载</el-button>
|
||||
</div>
|
||||
</li>
|
||||
</transition-group>
|
||||
@@ -155,7 +155,7 @@ const handleDownloadAttachment = async (file: any) => {
|
||||
});
|
||||
// 由于axios响应拦截器对blob类型响应直接返回blob数据
|
||||
// 这里response直接就是blob对象,而不是包含data属性的响应对象
|
||||
const blob = response;
|
||||
const blob = response as unknown as Blob;
|
||||
|
||||
// 验证响应数据
|
||||
if (!blob) {
|
||||
@@ -306,4 +306,19 @@ const listToString = (list: any[], separator?: string) => {
|
||||
.ele-upload-list__item-content-action .el-link {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.ele-upload-list__item-content-action {
|
||||
margin-left: 15px;
|
||||
}
|
||||
|
||||
.ele-upload-list__item-content-action .el-button--primary.is-link {
|
||||
border: none !important;
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
.ele-upload-list__item-content-action .el-button--danger.is-link {
|
||||
border: none !important;
|
||||
padding: 0 !important;
|
||||
margin-right: 10px;
|
||||
}
|
||||
</style>
|
||||
@@ -157,10 +157,13 @@ const treeProps = {
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(newValue) => {
|
||||
console.log('TeacherSelector: props.modelValue 变化:', newValue);
|
||||
if (newValue) {
|
||||
displayValue.value = newValue.nickName;
|
||||
console.log('TeacherSelector: 设置 displayValue 为:', displayValue.value);
|
||||
} else {
|
||||
displayValue.value = '';
|
||||
console.log('TeacherSelector: 清空 displayValue');
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
|
||||
@@ -14,10 +14,24 @@
|
||||
<el-descriptions-item label="活动地点">{{ activity.location || '暂无' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="开始时间">{{ activity.startTime || '暂无' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="结束时间">{{ activity.endTime || '暂无' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="关联部门">
|
||||
<div v-if="getRelatedDeptNames(activity).length > 0" class="dept-list">
|
||||
<el-tag
|
||||
v-for="(deptName, index) in getRelatedDeptNames(activity)"
|
||||
:key="index"
|
||||
type="info"
|
||||
size="small"
|
||||
class="dept-tag"
|
||||
>
|
||||
{{ deptName }}
|
||||
</el-tag>
|
||||
</div>
|
||||
<span v-else>暂无</span>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="关联社团/队伍">{{ activity.relatedClubName || '暂无' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="活动状态">
|
||||
<dict-tag :options="stgl_activity_status" :value="activity.activityStatus" />
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="关联社团/队伍">{{ activity.relatedClubName || '暂无' }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
|
||||
<!-- 活动海报区域 -->
|
||||
@@ -36,14 +50,17 @@
|
||||
</div>
|
||||
|
||||
<!-- 主办方信息区域 -->
|
||||
<!--
|
||||
<el-divider content-position="left">主办方信息</el-divider>
|
||||
<el-descriptions :column="3" border class="organizer-info-section custom-width-organizer">
|
||||
<el-descriptions-item label="主办方名称" :label-style="{ width: '13%' }" :content-style="{ width: '35%' }">{{ activity.organizerName || '暂无' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="联系人" :label-style="{ width: '10%' }" :content-style="{ width: '15%' }">{{ activity.organizerContact || '暂无' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="联系电话" :label-style="{ width: '10%' }" :content-style="{ width: '17%' }">{{ activity.organizerPhone || '暂无' }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
-->
|
||||
|
||||
<!-- 承办方信息区域 -->
|
||||
<!--
|
||||
<el-divider content-position="left">承办方信息</el-divider>
|
||||
<div v-if="coOrganizerList.length > 0">
|
||||
<div v-for="(organizer, index) in coOrganizerList" :key="index" class="co-organizer-item custom-width-co-organizer">
|
||||
@@ -55,8 +72,10 @@
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="no-data">暂无承办方信息</div>
|
||||
-->
|
||||
|
||||
<!-- 指导老师信息区域 -->
|
||||
<!--
|
||||
<el-divider content-position="left">指导老师信息</el-divider>
|
||||
<div v-if="instructorList.length > 0">
|
||||
<div v-for="(instructor, index) in instructorList" :key="index" class="instructor-item custom-width-instructor">
|
||||
@@ -69,6 +88,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="no-data">暂无指导老师信息</div>
|
||||
-->
|
||||
</el-tab-pane>
|
||||
|
||||
<!-- 报名设置标签页 -->
|
||||
@@ -235,6 +255,37 @@ const instructorList = computed<InstructorInfo[]>(() => {
|
||||
}
|
||||
});
|
||||
|
||||
// 获取关联部门名称列表
|
||||
const getRelatedDeptNames = (activity: StglActivityVO | StglActivityVo): string[] => {
|
||||
// 优先检查relatedDeptNames字段(多部门支持)
|
||||
if ('relatedDeptNames' in activity && activity.relatedDeptNames) {
|
||||
// 如果是数组类型,直接使用
|
||||
if (Array.isArray(activity.relatedDeptNames)) {
|
||||
return activity.relatedDeptNames.filter(name => name);
|
||||
}
|
||||
// 如果是字符串类型(逗号分隔),进行分割
|
||||
const deptNamesStr = activity.relatedDeptNames as unknown as string;
|
||||
if (typeof deptNamesStr === 'string' && deptNamesStr.includes(',')) {
|
||||
return deptNamesStr.split(',').map(name => name.trim()).filter(name => name);
|
||||
}
|
||||
// 如果是单个字符串
|
||||
if (typeof deptNamesStr === 'string' && deptNamesStr.trim()) {
|
||||
return [deptNamesStr.trim()];
|
||||
}
|
||||
}
|
||||
|
||||
// 兼容旧的单部门字段
|
||||
if (activity.relatedDeptName) {
|
||||
if (activity.relatedDeptName.includes(',')) {
|
||||
return activity.relatedDeptName.split(',').map(name => name.trim()).filter(name => name);
|
||||
} else {
|
||||
return [activity.relatedDeptName];
|
||||
}
|
||||
}
|
||||
|
||||
return [];
|
||||
};
|
||||
|
||||
// 加载附件信息
|
||||
const loadAttachments = async () => {
|
||||
// 重置附件列表
|
||||
@@ -543,6 +594,20 @@ onMounted(() => {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
/* 部门列表显示样式 */
|
||||
.dept-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 6px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.dept-tag {
|
||||
margin: 0;
|
||||
font-size: 12px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
/* 附件显示区域样式 */
|
||||
.attachment-list {
|
||||
display: flex;
|
||||
@@ -641,3 +706,4 @@ onMounted(() => {
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -18,8 +18,8 @@
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="主办方" prop="organizerName">
|
||||
<el-input v-model="queryParams.organizerName" placeholder="请输入主办方名称" clearable @keyup.enter="handleQuery" />
|
||||
<el-form-item label="关联部门" prop="relatedDeptId">
|
||||
<DeptSelector v-model="queryParams.relatedDept" @change="handleQueryRelatedDeptChange" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
@@ -61,7 +61,23 @@
|
||||
<dict-tag :options="stgl_activity_type" :value="scope.row.activityType" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="主办方" align="center" prop="organizerName" min-width="150" show-overflow-tooltip />
|
||||
<el-table-column label="关联部门" align="center" width="150" show-overflow-tooltip>
|
||||
<template #default="scope">
|
||||
<div v-if="getTableRelatedDeptNames(scope.row).length > 0" class="dept-list-table">
|
||||
<el-tag
|
||||
v-for="(deptName, index) in getTableRelatedDeptNames(scope.row)"
|
||||
:key="index"
|
||||
type="info"
|
||||
size="small"
|
||||
class="dept-tag-table"
|
||||
:title="deptName"
|
||||
>
|
||||
{{ deptName }}
|
||||
</el-tag>
|
||||
</div>
|
||||
<span v-else class="text-gray-400">未关联</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="关联社团/队伍" align="center" min-width="150" show-overflow-tooltip>
|
||||
<template #default="scope">
|
||||
<span v-if="scope.row.relatedClubName">{{ scope.row.relatedClubName }}</span>
|
||||
@@ -355,6 +371,7 @@ import type { StudentMsgForm } from '@/api/xg/xsjcxx/studentMsg/studentMsg/types
|
||||
import { StudentVO } from '@/api/xg/student/types';
|
||||
import { VicePresidentVO } from '@/components/VicePresidentManager/index.vue';
|
||||
import { getUser } from '@/api/system/user';
|
||||
import DeptSelector from '@/components/DeptSelector/index.vue';
|
||||
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
const { stgl_activity_type, stgl_registration_type, stgl_registration_status } = toRefs<any>(
|
||||
@@ -387,7 +404,7 @@ const viewForm = ref<StglActivityVo>({} as StglActivityVo);
|
||||
// 用户报名状态缓存
|
||||
const userRegistrationStatus = ref<Record<number, boolean>>({});
|
||||
|
||||
const data = reactive<PageData<StglActivityRegistrationForm, StglActivityQuery>>({
|
||||
const data = reactive<PageData<StglActivityRegistrationForm, StglActivityQuery & { relatedDept?: any | null }>>({
|
||||
form: {
|
||||
activityId: undefined,
|
||||
registrationType: undefined,
|
||||
@@ -399,7 +416,8 @@ const data = reactive<PageData<StglActivityRegistrationForm, StglActivityQuery>>
|
||||
pageSize: 10,
|
||||
activityName: undefined,
|
||||
activityType: undefined,
|
||||
organizerName: undefined,
|
||||
relatedDeptId: undefined,
|
||||
relatedDept: null,
|
||||
registrationType: undefined,
|
||||
params: {}
|
||||
},
|
||||
@@ -506,10 +524,20 @@ const handleQuery = () => {
|
||||
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
// 重置关联部门对象
|
||||
queryParams.value.relatedDept = null;
|
||||
queryFormRef.value?.resetFields();
|
||||
handleQuery();
|
||||
};
|
||||
|
||||
/** 关联部门搜索条件变化 */
|
||||
const handleQueryRelatedDeptChange = (dept: any | null) => {
|
||||
queryParams.value.relatedDept = dept;
|
||||
queryParams.value.relatedDeptId = dept?.deptId;
|
||||
queryParams.value.pageNum = 1;
|
||||
getList();
|
||||
};
|
||||
|
||||
/** 查看按钮操作 */
|
||||
const handleView = async (row: StglActivityVo) => {
|
||||
const res = await getActivityDetail(row.id);
|
||||
@@ -799,6 +827,37 @@ const formatDate = (dateStr: string): string => {
|
||||
});
|
||||
};
|
||||
|
||||
/** 获取活动关联部门名称列表 */
|
||||
const getTableRelatedDeptNames = (activity: StglActivityVo): string[] => {
|
||||
// 优先检查relatedDeptNames字段(多部门支持)
|
||||
if (activity.relatedDeptNames) {
|
||||
// 如果是数组类型,直接使用
|
||||
if (Array.isArray(activity.relatedDeptNames)) {
|
||||
return activity.relatedDeptNames.filter(name => name);
|
||||
}
|
||||
// 如果是字符串类型(逗号分隔),进行分割
|
||||
const deptNamesStr = activity.relatedDeptNames as unknown as string;
|
||||
if (typeof deptNamesStr === 'string' && deptNamesStr.includes(',')) {
|
||||
return deptNamesStr.split(',').map(name => name.trim()).filter(name => name);
|
||||
}
|
||||
// 如果是单个字符串
|
||||
if (typeof deptNamesStr === 'string' && deptNamesStr.trim()) {
|
||||
return [deptNamesStr.trim()];
|
||||
}
|
||||
}
|
||||
|
||||
// 兼容旧的单部门字段
|
||||
if (activity.relatedDeptName) {
|
||||
if (activity.relatedDeptName.includes(',')) {
|
||||
return activity.relatedDeptName.split(',').map(name => name.trim()).filter(name => name);
|
||||
} else {
|
||||
return [activity.relatedDeptName];
|
||||
}
|
||||
}
|
||||
|
||||
return [];
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getList();
|
||||
});
|
||||
@@ -856,4 +915,27 @@ onMounted(() => {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* 部门标签样式 */
|
||||
.dept-list-table {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 4px 6px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.dept-tag-table {
|
||||
margin: 0;
|
||||
font-size: 12px;
|
||||
line-height: 1.3;
|
||||
padding: 2px 6px;
|
||||
border-radius: 2px;
|
||||
white-space: nowrap;
|
||||
overflow: visible;
|
||||
text-overflow: unset;
|
||||
flex-shrink: 0;
|
||||
max-width: none;
|
||||
}
|
||||
</style>
|
||||
@@ -33,18 +33,23 @@
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="6">
|
||||
<el-col :span="6" v-if="false">
|
||||
<el-form-item label="主办方" prop="organizerName">
|
||||
<el-input v-model="queryParams.organizerName" placeholder="请输入主办方名称" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-col :span="6" v-if="false">
|
||||
<el-form-item label="是否已办" prop="isFinished">
|
||||
<el-select v-model="queryParams.isFinished" placeholder="请选择是否已办" clearable style="width: 100%">
|
||||
<el-option v-for="dict in sys_yes_no" :key="dict.value" :label="dict.label" :value="dict.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="关联部门" prop="relatedDeptId">
|
||||
<DeptSelector v-model="queryParams.relatedDept" @change="handleQueryRelatedDeptChange" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="关联社团/队伍" prop="relatedClubId">
|
||||
<ClubSelector v-model="queryParams.relatedClub" @change="handleQueryRelatedClubChange" />
|
||||
@@ -78,11 +83,22 @@
|
||||
</div>
|
||||
</transition>
|
||||
|
||||
|
||||
|
||||
<el-card shadow="never" class="button-card table-card">
|
||||
<template #header>
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['stgl:activity:add']">新增</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
icon="Plus"
|
||||
@click="handleAdd"
|
||||
:disabled="clubPermissionInfo && !clubPermissionInfo.canCreateActivity"
|
||||
:loading="isLoadingPermission"
|
||||
v-hasPermi="['stgl:activity:add']">
|
||||
新增
|
||||
</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5" v-if="!single && selectedActivity && ['0', '7', '6'].includes(getOriginalActivityStatus(selectedActivity)) && hasActivityOperationPermission(selectedActivity)">
|
||||
<el-button icon="Edit" @click="handleUpdate()" v-hasPermi="['stgl:activity:edit']">修改</el-button>
|
||||
@@ -124,14 +140,31 @@
|
||||
<dict-tag :options="stgl_activity_type" :value="scope.row.activityType" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="主办方" align="center" prop="organizerName" width="120" show-overflow-tooltip />
|
||||
<el-table-column label="关联社团/队伍" align="center" width="120" show-overflow-tooltip>
|
||||
<el-table-column label="主办方" align="center" prop="organizerName" width="120" show-overflow-tooltip v-if="false" />
|
||||
<el-table-column label="关联部门" align="center" width="150" show-overflow-tooltip>
|
||||
<template #default="scope">
|
||||
<div v-if="getTableRelatedDeptNames(scope.row).length > 0" class="dept-list-table">
|
||||
<el-tag
|
||||
v-for="(deptName, index) in getTableRelatedDeptNames(scope.row)"
|
||||
:key="index"
|
||||
type="info"
|
||||
size="small"
|
||||
class="dept-tag-table"
|
||||
:title="deptName"
|
||||
>
|
||||
{{ deptName }}
|
||||
</el-tag>
|
||||
</div>
|
||||
<span v-else class="text-gray-400">未关联</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="关联社团/队伍" align="center" width="180" show-overflow-tooltip>
|
||||
<template #default="scope">
|
||||
<span v-if="scope.row.relatedClubName">{{ scope.row.relatedClubName }}</span>
|
||||
<span v-else class="text-gray-400">未关联</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="指导老师" align="center" width="180" :show-overflow-tooltip="false">
|
||||
<el-table-column label="指导老师" align="center" width="180" :show-overflow-tooltip="false" v-if="false">
|
||||
<template #default="scope">
|
||||
<div class="advisor-cell">
|
||||
<span v-if="getAdvisorNames(scope.row).length === 0" class="no-advisor">暂无</span>
|
||||
@@ -143,7 +176,7 @@
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="活动时间" align="center" width="160">
|
||||
<el-table-column label="活动时间" align="center" width="140">
|
||||
<template #default="scope">
|
||||
<div v-if="scope.row.startTime && scope.row.endTime">
|
||||
<div>{{ formatDate(scope.row.startTime) }}</div>
|
||||
@@ -152,7 +185,7 @@
|
||||
<span v-else class="text-gray-400">未设置</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="报名时间" align="center" width="160">
|
||||
<el-table-column label="报名时间" align="center" width="140">
|
||||
<template #default="scope">
|
||||
<div v-if="scope.row.registrationStart && scope.row.registrationEnd">
|
||||
<div>{{ formatDate(scope.row.registrationStart) }}</div>
|
||||
@@ -362,6 +395,15 @@
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="关联部门" prop="relatedDeptIds" required>
|
||||
<MultiDeptSelector
|
||||
v-model="form.relatedDepts"
|
||||
@change="handleRelatedDeptsChange"
|
||||
class="consistent-height-selector"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="关联社团/队伍" prop="relatedClubId">
|
||||
<ClubSelector v-model="form.relatedClub" @change="handleRelatedClubChange" :disabled="isRelatedClubDisabled" />
|
||||
@@ -376,6 +418,7 @@
|
||||
</el-form-item>
|
||||
|
||||
<!-- 主办方信息 -->
|
||||
<!--
|
||||
<div class="section-divider">
|
||||
<h4 class="section-title">主办方信息</h4>
|
||||
</div>
|
||||
@@ -398,14 +441,18 @@
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
-->
|
||||
|
||||
<!-- 承办方信息 -->
|
||||
<!--
|
||||
<div class="section-divider">
|
||||
<h4 class="section-title">承办方信息</h4>
|
||||
</div>
|
||||
<CoOrganizerManager v-model="form.coOrganizerList" @change="handleCoOrganizersChange" />
|
||||
-->
|
||||
|
||||
<!-- 指导老师信息 -->
|
||||
<!--
|
||||
<div class="section-divider">
|
||||
<h4 class="section-title">指导老师信息</h4>
|
||||
</div>
|
||||
@@ -415,6 +462,7 @@
|
||||
@change="handleAdvisorsChange"
|
||||
:initial-advisors="form.advisors"
|
||||
/>
|
||||
-->
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="报名设置" name="registration">
|
||||
@@ -606,7 +654,7 @@
|
||||
</template>
|
||||
|
||||
<script setup name="StglActivity" lang="ts">
|
||||
import { nextTick } from 'vue';
|
||||
import { nextTick, watch } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import { ElNotification } from 'element-plus';
|
||||
import {
|
||||
@@ -619,8 +667,10 @@ import {
|
||||
copyStglActivity,
|
||||
summarizeActivity
|
||||
} from '@/api/stgl/activity';
|
||||
import { StglActivityVO, StglActivityQuery, StglActivityForm, ClubOption, CoOrganizerInfo, InstructorInfo } from '@/api/stgl/activity/types';
|
||||
import { StglActivityVO, StglActivityQuery, StglActivityForm, ClubOption, DeptOption, CoOrganizerInfo, InstructorInfo } from '@/api/stgl/activity/types';
|
||||
import ClubSelector from '@/components/ClubSelector/index.vue';
|
||||
import DeptSelector from '@/components/DeptSelector/index.vue';
|
||||
import MultiDeptSelector from '@/components/DeptSelector/MultiDeptSelector.vue';
|
||||
import CoOrganizerManager from '@/components/CoOrganizerManager/index.vue';
|
||||
import InstructorManager from '@/components/InstructorManager/index.vue';
|
||||
import AdvisorManager from '@/components/AdvisorManager/index.vue';
|
||||
@@ -630,6 +680,7 @@ import ActivityMembersDialog from './components/ActivityMembersDialog.vue';
|
||||
import ActivityRegistrationView from './components/ActivityRegistrationView.vue'; // Added import
|
||||
import { AdvisorVO } from '@/components/AdvisorManager/index.vue';
|
||||
import { useUserStore } from '@/store/modules/user';
|
||||
import { checkClubActivityPermission, type ClubActivityPermissionResult } from '@/api/stgl/clubPermission';
|
||||
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
const route = useRoute();
|
||||
@@ -652,6 +703,10 @@ const dateRange = ref<[string, string]>();
|
||||
const selectedActivity = ref<StglActivityVO | null>(null);
|
||||
const formKey = ref(0);
|
||||
|
||||
// 社团权限相关
|
||||
const clubPermissionInfo = ref<ClubActivityPermissionResult | null>(null);
|
||||
const isLoadingPermission = ref(false);
|
||||
|
||||
const queryFormRef = ref<ElFormInstance>();
|
||||
const activityFormRef = ref<ElFormInstance>();
|
||||
const copyFormRef = ref<ElFormInstance>();
|
||||
@@ -715,6 +770,10 @@ const initFormData: StglActivityForm = {
|
||||
location: undefined,
|
||||
description: undefined,
|
||||
posterUrl: undefined,
|
||||
relatedDeptId: undefined,
|
||||
relatedDeptIds: [],
|
||||
relatedDept: null,
|
||||
relatedDepts: null,
|
||||
relatedClubId: undefined,
|
||||
relatedType: undefined,
|
||||
relatedClub: null,
|
||||
@@ -727,7 +786,7 @@ const initFormData: StglActivityForm = {
|
||||
instructorList: [],
|
||||
instructors: '[]',
|
||||
advisors: [],
|
||||
registrationType: undefined,
|
||||
registrationType: '1', // 默认为个人报名
|
||||
registrationStart: undefined,
|
||||
registrationEnd: undefined,
|
||||
registrationStatus: undefined,
|
||||
@@ -736,7 +795,7 @@ const initFormData: StglActivityForm = {
|
||||
teamSizeMin: undefined,
|
||||
teamSizeMax: undefined,
|
||||
needApproval: 1,
|
||||
activityRange: 1, // 默认为1-本组织成员
|
||||
activityRange: 0, // 默认为无限制
|
||||
activityStatus: '0',
|
||||
isFinished: 0,
|
||||
summary: undefined,
|
||||
@@ -747,7 +806,7 @@ const initFormData: StglActivityForm = {
|
||||
remark: undefined
|
||||
};
|
||||
|
||||
const data = reactive<PageData<StglActivityForm, StglActivityQuery & { relatedClub?: ClubOption | null }>>({
|
||||
const data = reactive<PageData<StglActivityForm, StglActivityQuery & { relatedClub?: ClubOption | null; relatedDept?: DeptOption | null }>>({
|
||||
form: { ...initFormData },
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
@@ -758,6 +817,8 @@ const data = reactive<PageData<StglActivityForm, StglActivityQuery & { relatedCl
|
||||
startTime: undefined,
|
||||
endTime: undefined,
|
||||
location: undefined,
|
||||
relatedDeptId: undefined,
|
||||
relatedDept: null,
|
||||
relatedClubId: undefined,
|
||||
relatedClub: null,
|
||||
organizerName: undefined,
|
||||
@@ -777,10 +838,23 @@ const data = reactive<PageData<StglActivityForm, StglActivityQuery & { relatedCl
|
||||
activityType: [{ required: true, message: '请选择活动类型', trigger: 'change' }],
|
||||
startTime: [{ required: true, message: '请选择活动开始时间', trigger: 'change' }],
|
||||
endTime: [{ required: true, message: '请选择活动结束时间', trigger: 'change' }],
|
||||
organizerName: [
|
||||
{ required: true, message: '主办方名称不能为空', trigger: 'blur' },
|
||||
{ min: 2, max: 50, message: '主办方名称长度在2到50个字符', trigger: 'blur' }
|
||||
relatedDeptIds: [
|
||||
{
|
||||
required: true,
|
||||
validator: (rule: any, value: any, callback: any) => {
|
||||
if (!value || !Array.isArray(value) || value.length === 0) {
|
||||
callback(new Error('请选择关联部门'));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
},
|
||||
trigger: 'change'
|
||||
}
|
||||
],
|
||||
// organizerName: [
|
||||
// { required: true, message: '主办方名称不能为空', trigger: 'blur' },
|
||||
// { min: 2, max: 50, message: '主办方名称长度在2到50个字符', trigger: 'blur' }
|
||||
// ],
|
||||
registrationType: [{ required: true, message: '请选择报名方式', trigger: 'change' }],
|
||||
activityRange: [{ required: true, message: '请选择报名范围', trigger: 'change' }]
|
||||
}
|
||||
@@ -806,11 +880,7 @@ const initUrlParams = () => {
|
||||
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;
|
||||
@@ -821,11 +891,7 @@ const initUrlParams = () => {
|
||||
uniqueKey: `2-${relatedTeamId}`
|
||||
};
|
||||
|
||||
console.log('初始化URL参数 - 志愿者队伍过滤:', {
|
||||
relatedTeamId,
|
||||
relatedTeamName,
|
||||
queryParams: queryParams.value.relatedClub
|
||||
});
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
@@ -897,6 +963,11 @@ const reset = () => {
|
||||
form.value.advisors = [];
|
||||
form.value.instructorList = [];
|
||||
form.value.instructors = '[]';
|
||||
// 确保关联部门字段被正确初始化
|
||||
form.value.relatedDept = null;
|
||||
form.value.relatedDepts = null;
|
||||
form.value.relatedDeptId = undefined;
|
||||
form.value.relatedDeptIds = [];
|
||||
|
||||
// 重置关联社团禁用状态
|
||||
isRelatedClubDisabled.value = false;
|
||||
@@ -912,11 +983,7 @@ const reset = () => {
|
||||
|
||||
activeTab.value = 'basic';
|
||||
|
||||
console.log('表单重置后的状态:', {
|
||||
advisors: form.value.advisors,
|
||||
instructorList: form.value.instructorList,
|
||||
instructors: form.value.instructors
|
||||
});
|
||||
|
||||
|
||||
// 延迟重置表单字段,避免递归更新
|
||||
setTimeout(() => {
|
||||
@@ -933,6 +1000,9 @@ const handleQuery = () => {
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
dateRange.value = undefined;
|
||||
// 重置关联部门和关联社团对象
|
||||
queryParams.value.relatedDept = null;
|
||||
queryParams.value.relatedClub = null;
|
||||
queryFormRef.value?.resetFields();
|
||||
handleQuery();
|
||||
};
|
||||
@@ -945,8 +1015,57 @@ const handleSelectionChange = (selection: StglActivityVO[]) => {
|
||||
selectedActivity.value = selection.length === 1 ? selection[0] : null;
|
||||
};
|
||||
|
||||
/** 检查社团活动权限 */
|
||||
const checkClubPermission = async (clubId: number | string) => {
|
||||
if (!clubId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
isLoadingPermission.value = true;
|
||||
const response = await checkClubActivityPermission(clubId);
|
||||
clubPermissionInfo.value = response.data;
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error('检查社团权限失败:', error);
|
||||
proxy?.$modal.msgError('检查社团权限失败');
|
||||
return null;
|
||||
} finally {
|
||||
isLoadingPermission.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
/** 跳转到年审页面 */
|
||||
const goToAnnualReview = () => {
|
||||
if (queryParams.value.relatedClub?.relatedClubId) {
|
||||
router.push({
|
||||
path: '/stgl/club/info/annualReview',
|
||||
query: { clubId: queryParams.value.relatedClub.relatedClubId }
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/** 新增按钮操作 */
|
||||
const handleAdd = () => {
|
||||
const handleAdd = async () => {
|
||||
// 如果有关联社团,先检查权限
|
||||
if (queryParams.value.relatedClub?.relatedClubId && queryParams.value.relatedClub?.relatedType === 1) {
|
||||
// 避免使用Number()导致的精度丢失,直接使用字符串形式的ID
|
||||
const clubId = queryParams.value.relatedClub.relatedClubId;
|
||||
|
||||
const permission = await checkClubPermission(clubId);
|
||||
|
||||
if (permission && !permission.canCreateActivity) {
|
||||
// 权限不足,显示提示信息
|
||||
if (permission.status === 'TEMPORARILY_LIMITED') {
|
||||
proxy?.$modal.msgError(`您的社团当前无法发起活动:${permission.message}`);
|
||||
} else if (permission.status === 'PERMANENTLY_LIMITED') {
|
||||
proxy?.$modal.msgError(`您的社团已被永久限制发起活动:${permission.message}`);
|
||||
}
|
||||
return; // 停止新增操作
|
||||
}
|
||||
}
|
||||
|
||||
// 权限检查通过,继续原有的新增逻辑
|
||||
// 先重置表单
|
||||
form.value = { ...initFormData };
|
||||
activeTab.value = 'basic';
|
||||
@@ -956,16 +1075,13 @@ const handleAdd = () => {
|
||||
form.value.instructorList = [];
|
||||
form.value.instructors = '[]';
|
||||
|
||||
// 自动填充报名开始时间为当前时间
|
||||
const now = new Date();
|
||||
form.value.registrationStart = now.toISOString().slice(0, 19).replace('T', ' ');
|
||||
|
||||
// 更新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;
|
||||
@@ -974,19 +1090,6 @@ const handleAdd = () => {
|
||||
|
||||
// 设置关联社团字段为禁用状态(不允许修改)
|
||||
isRelatedClubDisabled.value = true;
|
||||
|
||||
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
|
||||
});
|
||||
} else {
|
||||
// 没有关联组织时,允许编辑关联社团字段
|
||||
isRelatedClubDisabled.value = false;
|
||||
@@ -995,14 +1098,7 @@ const handleAdd = () => {
|
||||
dialog.visible = true;
|
||||
dialog.title = '添加活动信息';
|
||||
|
||||
// 确保对话框完全打开后再检查表单状态
|
||||
nextTick(() => {
|
||||
console.log('新增活动 - 对话框打开后的表单状态:', {
|
||||
advisors: form.value.advisors,
|
||||
instructorList: form.value.instructorList,
|
||||
instructors: form.value.instructors
|
||||
});
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
/** 修改按钮操作 */
|
||||
@@ -1022,11 +1118,51 @@ const handleUpdate = async (row?: StglActivityVO) => {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('handleUpdate - 获取的活动数据:', res.data);
|
||||
console.log('handleUpdate - 附件字段:', {
|
||||
summaryAttachments: res.data.summaryAttachments,
|
||||
achievementsAttachments: res.data.achievementsAttachments
|
||||
});
|
||||
|
||||
|
||||
// 处理关联部门数据
|
||||
if (res.data.relatedDeptIds && res.data.relatedDeptNames) {
|
||||
// 优先使用新的多选部门字段
|
||||
try {
|
||||
const deptIds: number[] = JSON.parse(res.data.relatedDeptIds as unknown as string);
|
||||
const deptNames: string[] = (res.data.relatedDeptNames as unknown as string).split(',').map(name => name.trim());
|
||||
|
||||
(res.data as any).relatedDepts = deptIds.map((deptId: number, index: number) => ({
|
||||
deptId: deptId,
|
||||
deptName: deptNames[index] || `部门${index + 1}`
|
||||
}));
|
||||
(res.data as any).relatedDeptIds = deptIds;
|
||||
|
||||
|
||||
} catch (error) {
|
||||
console.error('解析多选部门数据失败:', error);
|
||||
// 降级到单选模式
|
||||
if (res.data.relatedDeptId && res.data.relatedDeptName) {
|
||||
(res.data as any).relatedDepts = [{
|
||||
deptId: res.data.relatedDeptId,
|
||||
deptName: res.data.relatedDeptName
|
||||
}];
|
||||
(res.data as any).relatedDeptIds = [res.data.relatedDeptId];
|
||||
}
|
||||
}
|
||||
} else if (res.data.relatedDeptId && res.data.relatedDeptName) {
|
||||
// 兼容旧的单选部门数据
|
||||
(res.data as any).relatedDepts = [{
|
||||
deptId: res.data.relatedDeptId,
|
||||
deptName: res.data.relatedDeptName
|
||||
}];
|
||||
(res.data as any).relatedDeptIds = [res.data.relatedDeptId];
|
||||
|
||||
|
||||
}
|
||||
|
||||
// 保持向后兼容
|
||||
if (res.data.relatedDeptId && res.data.relatedDeptName) {
|
||||
(res.data as any).relatedDept = {
|
||||
deptId: res.data.relatedDeptId,
|
||||
deptName: res.data.relatedDeptName
|
||||
};
|
||||
}
|
||||
|
||||
// 处理关联社团数据
|
||||
if (res.data.relatedClubId && res.data.relatedClubName) {
|
||||
@@ -1073,7 +1209,6 @@ const handleUpdate = async (row?: StglActivityVO) => {
|
||||
if (res.data.instructors) {
|
||||
try {
|
||||
const instructorsData = JSON.parse(res.data.instructors);
|
||||
console.log('解析的指导老师数据:', instructorsData);
|
||||
|
||||
// 确保数据格式正确
|
||||
if (Array.isArray(instructorsData)) {
|
||||
@@ -1104,13 +1239,7 @@ const handleUpdate = async (row?: StglActivityVO) => {
|
||||
form.value.registrationType = String(form.value.registrationType);
|
||||
}
|
||||
|
||||
// 确保指导老师数据正确设置
|
||||
console.log('修改活动 - 设置表单数据后的状态:', {
|
||||
advisors: form.value.advisors,
|
||||
instructorList: form.value.instructorList,
|
||||
instructors: form.value.instructors,
|
||||
registrationType: form.value.registrationType
|
||||
});
|
||||
|
||||
|
||||
// 更新formKey以强制重新渲染组件
|
||||
formKey.value++;
|
||||
@@ -1201,18 +1330,44 @@ const submitForm = () => {
|
||||
|
||||
// 准备提交的数据
|
||||
const submitData = { ...form.value } as any;
|
||||
|
||||
console.log('提交前 - 原始表单数据:', {
|
||||
advisors: submitData.advisors,
|
||||
instructorList: submitData.instructorList,
|
||||
instructors: submitData.instructors
|
||||
});
|
||||
|
||||
// 新增时自动设置活动状态为0(草稿)
|
||||
if (!submitData.id) {
|
||||
submitData.activityStatus = '0';
|
||||
}
|
||||
|
||||
// 处理关联部门数据
|
||||
if (submitData.relatedDepts && submitData.relatedDepts.length > 0) {
|
||||
// 多选部门:将ID列表转换为JSON格式字符串传递给后端
|
||||
const deptIds = submitData.relatedDepts.map(dept => dept.deptId);
|
||||
submitData.relatedDeptIds = JSON.stringify(deptIds);
|
||||
|
||||
// 将部门名称用逗号连接
|
||||
submitData.relatedDeptNames = submitData.relatedDepts.map(dept => dept.deptName).join(',');
|
||||
|
||||
// 设置第一个部门作为主要关联部门(保持兼容性)
|
||||
submitData.relatedDeptId = deptIds[0];
|
||||
submitData.relatedDeptName = submitData.relatedDepts[0].deptName;
|
||||
submitData.relatedDept = submitData.relatedDepts[0];
|
||||
|
||||
|
||||
} else if (submitData.relatedDept) {
|
||||
// 单选部门的情况(向后兼容)
|
||||
submitData.relatedDeptId = submitData.relatedDept.deptId;
|
||||
submitData.relatedDeptName = submitData.relatedDept.deptName;
|
||||
// 单选时也设置多选字段
|
||||
submitData.relatedDeptIds = JSON.stringify([submitData.relatedDept.deptId]);
|
||||
submitData.relatedDeptNames = submitData.relatedDept.deptName;
|
||||
|
||||
|
||||
} else {
|
||||
// 清空关联部门数据
|
||||
submitData.relatedDeptId = null;
|
||||
submitData.relatedDeptName = null;
|
||||
submitData.relatedDeptIds = null;
|
||||
submitData.relatedDeptNames = null;
|
||||
}
|
||||
|
||||
// 处理关联社团数据
|
||||
if (submitData.relatedClub) {
|
||||
submitData.relatedClubId = submitData.relatedClub.relatedClubId;
|
||||
@@ -1223,12 +1378,7 @@ const submitForm = () => {
|
||||
submitData.relatedType = null;
|
||||
}
|
||||
|
||||
// 调试信息
|
||||
console.log('提交的关联社团数据:', {
|
||||
relatedClub: submitData.relatedClub,
|
||||
relatedClubId: submitData.relatedClubId,
|
||||
relatedType: submitData.relatedType
|
||||
});
|
||||
|
||||
|
||||
// 处理承办方数据
|
||||
if (submitData.coOrganizerList && submitData.coOrganizerList.length > 0) {
|
||||
@@ -1237,6 +1387,18 @@ const submitForm = () => {
|
||||
submitData.coOrganizers = '[]'; // 修复:使用空JSON数组而不是undefined
|
||||
}
|
||||
|
||||
// 处理主办方数据(由于前端已屏蔽主办方字段显示,设置默认值)
|
||||
if (!submitData.organizerName) {
|
||||
// 如果有关联部门,使用第一个部门名称作为主办方
|
||||
if (submitData.relatedDepts && submitData.relatedDepts.length > 0) {
|
||||
submitData.organizerName = submitData.relatedDepts[0].deptName;
|
||||
} else if (submitData.relatedClub && submitData.relatedClub.relatedClubName) {
|
||||
submitData.organizerName = submitData.relatedClub.relatedClubName;
|
||||
} else {
|
||||
submitData.organizerName = '学校'; // 默认主办方
|
||||
}
|
||||
}
|
||||
|
||||
// 处理指导老师数据
|
||||
if (submitData.advisors && submitData.advisors.length > 0) {
|
||||
submitData.instructors = JSON.stringify(submitData.advisors);
|
||||
@@ -1313,11 +1475,6 @@ const handleDelete = async (row?: StglActivityVO) => {
|
||||
/** 查看按钮操作 */
|
||||
const handleView = async (row: StglActivityVO) => {
|
||||
const res = await getStglActivity(row.id);
|
||||
console.log('handleView - 获取的活动数据:', res.data);
|
||||
console.log('handleView - 附件字段:', {
|
||||
summaryAttachments: res.data.summaryAttachments,
|
||||
achievementsAttachments: res.data.achievementsAttachments
|
||||
});
|
||||
viewForm.value = res.data;
|
||||
viewDialog.visible = true;
|
||||
viewDialog.title = '查看活动信息';
|
||||
@@ -1372,12 +1529,6 @@ const handleSummary = async (row?: StglActivityVO) => {
|
||||
const res = await getStglActivity(activity.id);
|
||||
const fullActivity = res.data;
|
||||
|
||||
console.log('handleSummary - 完整活动数据:', fullActivity);
|
||||
console.log('handleSummary - 附件字段:', {
|
||||
summaryAttachments: fullActivity.summaryAttachments,
|
||||
achievementsAttachments: fullActivity.achievementsAttachments
|
||||
});
|
||||
|
||||
summaryForm.id = fullActivity.id;
|
||||
summaryForm.activityName = fullActivity.activityName || '';
|
||||
summaryForm.summary = fullActivity.summary || '';
|
||||
@@ -1385,11 +1536,6 @@ const handleSummary = async (row?: StglActivityVO) => {
|
||||
summaryForm.summaryAttachments = fullActivity.summaryAttachments || '';
|
||||
summaryForm.achievementsAttachments = fullActivity.achievementsAttachments || '';
|
||||
|
||||
console.log('handleSummary - 设置到表单的数据:', {
|
||||
summaryAttachments: summaryForm.summaryAttachments,
|
||||
achievementsAttachments: summaryForm.achievementsAttachments
|
||||
});
|
||||
|
||||
summaryDialog.visible = true;
|
||||
};
|
||||
|
||||
@@ -1407,14 +1553,6 @@ const resetSummaryForm = () => {
|
||||
const confirmSummary = async () => {
|
||||
summaryLoading.value = true;
|
||||
try {
|
||||
console.log('confirmSummary - 提交的数据:', {
|
||||
id: summaryForm.id,
|
||||
summary: summaryForm.summary,
|
||||
achievements: summaryForm.achievements,
|
||||
summaryAttachments: summaryForm.summaryAttachments,
|
||||
achievementsAttachments: summaryForm.achievementsAttachments
|
||||
});
|
||||
|
||||
await summarizeActivity({
|
||||
id: summaryForm.id!,
|
||||
summary: summaryForm.summary,
|
||||
@@ -1453,18 +1591,39 @@ const handleExport = async () => {
|
||||
}
|
||||
};
|
||||
|
||||
/** 关联部门变化处理 */
|
||||
const handleRelatedDeptChange = (dept: DeptOption | null) => {
|
||||
form.value.relatedDept = dept;
|
||||
form.value.relatedDeptId = dept?.deptId;
|
||||
};
|
||||
|
||||
/** 关联部门多选变化处理 */
|
||||
const handleRelatedDeptsChange = (depts: DeptOption[] | null) => {
|
||||
form.value.relatedDepts = depts;
|
||||
|
||||
if (depts && depts.length > 0) {
|
||||
form.value.relatedDeptIds = depts.map(dept => dept.deptId);
|
||||
// 将第一个部门设置为主要关联部门(兼容现有逻辑)
|
||||
form.value.relatedDeptId = depts[0].deptId;
|
||||
form.value.relatedDept = depts[0];
|
||||
} else {
|
||||
form.value.relatedDeptIds = [];
|
||||
form.value.relatedDeptId = undefined;
|
||||
form.value.relatedDept = null;
|
||||
}
|
||||
};
|
||||
|
||||
/** 关联社团变化处理 */
|
||||
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 handleQueryRelatedDeptChange = (dept: DeptOption | null) => {
|
||||
queryParams.value.relatedDept = dept;
|
||||
queryParams.value.relatedDeptId = dept?.deptId;
|
||||
};
|
||||
|
||||
/** 查询关联社团变化处理 */
|
||||
@@ -1651,6 +1810,37 @@ const formatDate = (dateStr: string): string => {
|
||||
});
|
||||
};
|
||||
|
||||
/** 获取表格中关联部门名称列表 */
|
||||
const getTableRelatedDeptNames = (activity: StglActivityVO): string[] => {
|
||||
// 优先检查relatedDeptNames字段(多部门支持)
|
||||
if (activity.relatedDeptNames) {
|
||||
// 如果是数组类型,直接使用
|
||||
if (Array.isArray(activity.relatedDeptNames)) {
|
||||
return activity.relatedDeptNames.filter(name => name);
|
||||
}
|
||||
// 如果是字符串类型(逗号分隔),进行分割
|
||||
const deptNamesStr = activity.relatedDeptNames as unknown as string;
|
||||
if (typeof deptNamesStr === 'string' && deptNamesStr.includes(',')) {
|
||||
return deptNamesStr.split(',').map(name => name.trim()).filter(name => name);
|
||||
}
|
||||
// 如果是单个字符串
|
||||
if (typeof deptNamesStr === 'string' && deptNamesStr.trim()) {
|
||||
return [deptNamesStr.trim()];
|
||||
}
|
||||
}
|
||||
|
||||
// 兼容旧的单部门字段
|
||||
if (activity.relatedDeptName) {
|
||||
if (activity.relatedDeptName.includes(',')) {
|
||||
return activity.relatedDeptName.split(',').map(name => name.trim()).filter(name => name);
|
||||
} else {
|
||||
return [activity.relatedDeptName];
|
||||
}
|
||||
}
|
||||
|
||||
return [];
|
||||
};
|
||||
|
||||
/** 获取指导老师姓名列表 */
|
||||
const getAdvisorNames = (activity: StglActivityVO): string[] => {
|
||||
// 优先从JSON字段读取指导老师数据
|
||||
@@ -1665,7 +1855,6 @@ const getAdvisorNames = (activity: StglActivityVO): string[] => {
|
||||
}
|
||||
}
|
||||
|
||||
console.log('getAdvisorNames: 没有找到指导老师数据,activity:', activity);
|
||||
return [];
|
||||
};
|
||||
|
||||
@@ -1726,10 +1915,54 @@ const getOriginalActivityStatus = (activity: StglActivityVO | null): string => {
|
||||
return activity.originalActivityStatus || activity.activityStatus || '';
|
||||
};
|
||||
|
||||
// 监听活动开始时间变化,自动更新报名结束时间
|
||||
watch(
|
||||
() => form.value.startTime,
|
||||
(newStartTime) => {
|
||||
// 只有在新增或修改模式下,且有开始时间时才自动填充
|
||||
if (newStartTime && dialog.visible) {
|
||||
// 使用nextTick确保异步更新
|
||||
nextTick(() => {
|
||||
// 确保时间格式正确,保留完整的日期和时间信息
|
||||
if (typeof newStartTime === 'string' && newStartTime.includes(':')) {
|
||||
// 字符串格式且包含时间部分,直接赋值
|
||||
form.value.registrationEnd = newStartTime;
|
||||
} else if (typeof newStartTime === 'string') {
|
||||
// 字符串格式但可能只有日期部分,添加默认时间
|
||||
form.value.registrationEnd = newStartTime.includes(' ') ? newStartTime : `${newStartTime} 00:00:00`;
|
||||
} else {
|
||||
// 如果是Date对象或其他格式,转换为YYYY-MM-DD HH:mm:ss格式
|
||||
const date = new Date(newStartTime);
|
||||
if (!isNaN(date.getTime())) {
|
||||
form.value.registrationEnd = date.toISOString().slice(0, 19).replace('T', ' ');
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
onMounted(() => {
|
||||
initUrlParams();
|
||||
getList();
|
||||
// 如果有关联社团,检查权限
|
||||
if (queryParams.value.relatedClub?.relatedClubId && queryParams.value.relatedClub?.relatedType === 1) {
|
||||
checkClubPermission(Number(queryParams.value.relatedClub.relatedClubId));
|
||||
}
|
||||
});
|
||||
|
||||
// 监听路由查询参数变化,重新检查权限
|
||||
watch(
|
||||
() => queryParams.value.relatedClub,
|
||||
(newRelatedClub) => {
|
||||
if (newRelatedClub?.relatedClubId && newRelatedClub.relatedType === 1) {
|
||||
checkClubPermission(Number(newRelatedClub.relatedClubId));
|
||||
} else {
|
||||
clubPermissionInfo.value = null;
|
||||
}
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@@ -1808,6 +2041,29 @@ onMounted(() => {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* 部门列表表格样式 */
|
||||
.dept-list-table {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 4px 6px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.dept-tag-table {
|
||||
margin: 0;
|
||||
font-size: 12px;
|
||||
line-height: 1.3;
|
||||
padding: 2px 6px;
|
||||
border-radius: 2px;
|
||||
white-space: nowrap;
|
||||
overflow: visible;
|
||||
text-overflow: unset;
|
||||
flex-shrink: 0;
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
/* 指导老师单元格样式 */
|
||||
.advisor-cell {
|
||||
padding: 4px 8px;
|
||||
@@ -1845,5 +2101,20 @@ onMounted(() => {
|
||||
}
|
||||
}
|
||||
|
||||
/* 确保关联部门选择器与其他字段高度一致 */
|
||||
.consistent-height-selector :deep(.el-select) {
|
||||
height: 32px !important;
|
||||
}
|
||||
|
||||
.consistent-height-selector :deep(.el-select .el-input__wrapper) {
|
||||
min-height: 32px !important;
|
||||
height: 32px !important;
|
||||
}
|
||||
|
||||
.consistent-height-selector :deep(.el-select__tags) {
|
||||
max-height: 28px !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,527 @@
|
||||
<template>
|
||||
<div class="annual-review-detail">
|
||||
<div v-loading="loading">
|
||||
<div v-if="annualReview">
|
||||
<el-form ref="formRef" :model="annualReview" label-width="120px">
|
||||
<!-- 年审表头信息 -->
|
||||
<div class="form-header">
|
||||
<h3 class="form-title">杭州第一技师学院社团资格年审表</h3>
|
||||
<div class="year-range">{{ yearRange }} 年</div>
|
||||
<div class="review-date">年审时间:{{ reviewDate }}</div>
|
||||
</div>
|
||||
|
||||
<!-- 基本信息区域 -->
|
||||
<div class="form-section">
|
||||
<h4 class="section-title">基本信息</h4>
|
||||
<div class="basic-info-table">
|
||||
<!-- 第一行:社团名称 | 成立时间 -->
|
||||
<div class="info-row">
|
||||
<div class="info-item">
|
||||
<div class="info-label">社团名称</div>
|
||||
<div class="info-value">{{ annualReview.clubName }}</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="info-label">成立时间</div>
|
||||
<div class="info-value">{{ parseTime(annualReview.establishDate, '{y}-{m}-{d}') }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 第二行:指导部门 | 社团类别 -->
|
||||
<div class="info-row">
|
||||
<div class="info-item">
|
||||
<div class="info-label">指导部门</div>
|
||||
<div class="info-value">{{ annualReview.advisorDept }}</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="info-label">社团类别</div>
|
||||
<div class="info-value">
|
||||
<dict-tag :options="stgl_club_type" :value="annualReview.clubType" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 第三行:指导老师 | 成员 -->
|
||||
<div class="info-row">
|
||||
<div class="info-item">
|
||||
<div class="info-label">指导老师</div>
|
||||
<div class="info-value">{{ annualReview.advisorName }}</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="info-label">成员</div>
|
||||
<div class="info-value">{{ memberCountText }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 第四行:有无章程 | 社团金额 -->
|
||||
<div class="info-row">
|
||||
<div class="info-item">
|
||||
<div class="info-label">有无章程</div>
|
||||
<div class="info-value">
|
||||
<span v-if="annualReview.hasCharter === 'Y'" class="charter-status">
|
||||
有
|
||||
<div class="charter-info">
|
||||
<file-upload
|
||||
v-model="annualReview.charterUrl"
|
||||
:disabled="true"
|
||||
:is-show-tip="false"
|
||||
:limit="1"
|
||||
:file-type="['pdf', 'doc', 'docx']"
|
||||
:file-size="10"
|
||||
/>
|
||||
</div>
|
||||
</span>
|
||||
<span v-else class="charter-status">无</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="info-label">社团金额</div>
|
||||
<div class="info-value">
|
||||
<span v-if="annualReview.hasFunds === 'Y'">
|
||||
有
|
||||
<span v-if="annualReview.fundAmount" style="margin-left: 10px;">
|
||||
{{ annualReview.fundAmount }}元
|
||||
</span>
|
||||
</span>
|
||||
<span v-else>无</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 社团主要成员 -->
|
||||
<div class="form-section">
|
||||
<h4 class="section-title">社团主要成员</h4>
|
||||
<el-table :data="memberList" border style="width: 100%" max-height="300" class="member-table">
|
||||
<el-table-column label="姓名" prop="name" width="100" align="center" header-align="center" />
|
||||
<el-table-column label="班级" prop="className" min-width="140" align="center" header-align="center" />
|
||||
<el-table-column label="社团职务" prop="role" width="100" align="center" header-align="center" />
|
||||
<el-table-column label="联系方式" prop="phone" min-width="130" align="center" header-align="center" />
|
||||
</el-table>
|
||||
</div>
|
||||
|
||||
<!-- 年度活动开展情况 -->
|
||||
<div class="form-section">
|
||||
<h4 class="section-title">年度活动开展情况</h4>
|
||||
<el-table :data="activityList" border style="width: 100%" max-height="300" class="activity-table">
|
||||
<el-table-column label="时间" width="200" align="center" header-align="center">
|
||||
<template #default="{ row }">
|
||||
<div class="activity-time-display">
|
||||
{{ row.activityTimeDisplay }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="地点" prop="location" width="150" align="center" header-align="center" />
|
||||
<el-table-column label="活动名称" prop="activityName" min-width="200" align="center" header-align="center" />
|
||||
<el-table-column label="参加人数" prop="participantCount" width="100" align="center" header-align="center" />
|
||||
</el-table>
|
||||
</div>
|
||||
|
||||
<!-- 审批意见 -->
|
||||
<div class="form-section">
|
||||
<h4 class="section-title">审批意见</h4>
|
||||
|
||||
<!-- 指导老师意见 -->
|
||||
<div class="opinion-section">
|
||||
<div class="opinion-title">指导老师意见</div>
|
||||
<div class="opinion-content">
|
||||
{{ annualReview.advisorOpinion || '暂无意见' }}
|
||||
</div>
|
||||
<div class="opinion-signature">签名(公章)</div>
|
||||
</div>
|
||||
|
||||
<!-- 二级学院意见 -->
|
||||
<div class="opinion-section">
|
||||
<div class="opinion-title">二级学院意见</div>
|
||||
<div class="opinion-content">
|
||||
{{ annualReview.collegeOpinion || '暂无意见' }}
|
||||
</div>
|
||||
<div class="opinion-signature">签名(公章)</div>
|
||||
</div>
|
||||
|
||||
<!-- 校团委意见 -->
|
||||
<div class="opinion-section">
|
||||
<div class="opinion-title">校团委意见</div>
|
||||
<div class="opinion-content">
|
||||
{{ annualReview.youthLeagueOpinion || '暂无意见' }}
|
||||
</div>
|
||||
<div class="opinion-signature">签名(公章)</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<el-empty v-else description="暂无数据" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { getStglClubAnnualReview, getAnnualReviewActivities } from '@/api/stgl/club/annualReview';
|
||||
import { StglClubAnnualReviewVO } from '@/api/stgl/club/annualReview/type';
|
||||
|
||||
// 扩展的年审VO接口(包含JSON字段)
|
||||
interface ExtendedAnnualReviewVO extends StglClubAnnualReviewVO {
|
||||
membersJson?: string;
|
||||
activitiesJson?: string;
|
||||
}
|
||||
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
const { stgl_club_type } = toRefs(proxy?.useDict('stgl_club_type') as any);
|
||||
|
||||
const props = defineProps<{
|
||||
annualReviewId: string | number;
|
||||
}>();
|
||||
|
||||
const loading = ref(false);
|
||||
const annualReview = ref<ExtendedAnnualReviewVO>();
|
||||
|
||||
// 成员列表
|
||||
const memberList = ref<any[]>([]);
|
||||
// 活动列表
|
||||
const activityList = ref<any[]>([]);
|
||||
|
||||
// 年份范围计算
|
||||
const yearRange = computed(() => {
|
||||
if (annualReview.value?.reviewYear) {
|
||||
const currentYear = annualReview.value.reviewYear;
|
||||
const lastYear = currentYear - 1;
|
||||
return `${lastYear}-${currentYear}`;
|
||||
}
|
||||
return '';
|
||||
});
|
||||
|
||||
// 年审日期
|
||||
const reviewDate = computed(() => {
|
||||
if (annualReview.value?.reviewTime) {
|
||||
const date = new Date(annualReview.value.reviewTime);
|
||||
const year = date.getFullYear();
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||||
const day = String(date.getDate()).padStart(2, '0');
|
||||
return `${year}年${month}月${day}日`;
|
||||
}
|
||||
return '';
|
||||
});
|
||||
|
||||
// 成员数量文本
|
||||
const memberCountText = computed(() => {
|
||||
return `${annualReview.value?.memberCount || 0}人`;
|
||||
});
|
||||
|
||||
/** 格式化活动时间显示 */
|
||||
const formatActivityTime = (startTime: string, endTime?: string): string => {
|
||||
if (!startTime) return '';
|
||||
|
||||
try {
|
||||
// 处理标准格式 (如: "2024-09-05 14:30:00" 或 "2024-09-05")
|
||||
const parseTimeString = (timeStr: string): Date | null => {
|
||||
if (!timeStr) return null;
|
||||
|
||||
if (timeStr.match(/^\d{4}-\d{2}-\d{2}/)) {
|
||||
const date = new Date(timeStr);
|
||||
return isNaN(date.getTime()) ? null : date;
|
||||
}
|
||||
|
||||
const date = new Date(timeStr);
|
||||
return isNaN(date.getTime()) ? null : date;
|
||||
};
|
||||
|
||||
const startDate = parseTimeString(startTime);
|
||||
const endDate = endTime ? parseTimeString(endTime) : null;
|
||||
|
||||
if (!startDate) {
|
||||
return startTime;
|
||||
}
|
||||
|
||||
// 格式化为完整的日期时间(保留00:00:00)
|
||||
const formatDateTime = (date: Date): string => {
|
||||
const year = date.getFullYear();
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||||
const day = String(date.getDate()).padStart(2, '0');
|
||||
const hours = String(date.getHours()).padStart(2, '0');
|
||||
const minutes = String(date.getMinutes()).padStart(2, '0');
|
||||
const seconds = String(date.getSeconds()).padStart(2, '0');
|
||||
|
||||
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
|
||||
};
|
||||
|
||||
const formattedStartTime = formatDateTime(startDate);
|
||||
|
||||
if (!endDate || startDate.getTime() === endDate.getTime()) {
|
||||
return formattedStartTime;
|
||||
}
|
||||
|
||||
const formattedEndTime = formatDateTime(endDate);
|
||||
|
||||
// 检查是否是同一天
|
||||
const isSameDay = startDate.toDateString() === endDate.toDateString();
|
||||
|
||||
if (isSameDay) {
|
||||
// 同一天的情况,分行显示:日期在上,时间范围在下
|
||||
const dateStr = formattedStartTime.split(' ')[0];
|
||||
const startTimeStr = formattedStartTime.split(' ')[1];
|
||||
const endTimeStr = formattedEndTime.split(' ')[1];
|
||||
return `${dateStr}\n${startTimeStr}-${endTimeStr}`;
|
||||
}
|
||||
|
||||
// 不同天的情况,分行显示完整时间
|
||||
return `${formattedStartTime}\n${formattedEndTime}`;
|
||||
|
||||
} catch (error) {
|
||||
console.warn('格式化活动时间失败:', error, '原始数据:', startTime);
|
||||
return startTime;
|
||||
}
|
||||
};
|
||||
|
||||
/** 获取年审记录详情 */
|
||||
const getDetail = async () => {
|
||||
if (!props.annualReviewId) return;
|
||||
|
||||
loading.value = true;
|
||||
try {
|
||||
const res = await getStglClubAnnualReview(props.annualReviewId);
|
||||
annualReview.value = res.data;
|
||||
|
||||
// 检查数据是否存在
|
||||
if (!annualReview.value) {
|
||||
console.warn('年审记录数据为空');
|
||||
memberList.value = [];
|
||||
activityList.value = [];
|
||||
return;
|
||||
}
|
||||
|
||||
// 解析成员JSON数据
|
||||
if (annualReview.value.membersJson) {
|
||||
try {
|
||||
memberList.value = JSON.parse(annualReview.value.membersJson);
|
||||
} catch (error) {
|
||||
console.warn('解析成员JSON数据失败:', error);
|
||||
memberList.value = [];
|
||||
}
|
||||
} else {
|
||||
memberList.value = [];
|
||||
}
|
||||
|
||||
// 从stgl_club_annual_review_activity表中获取年审记录对应的活动数据
|
||||
try {
|
||||
const activitiesRes = await getAnnualReviewActivities(props.annualReviewId);
|
||||
|
||||
// 检查活动数据是否存在
|
||||
if (activitiesRes && activitiesRes.data && Array.isArray(activitiesRes.data)) {
|
||||
// 处理活动数据
|
||||
activityList.value = activitiesRes.data.map((activity: any) => {
|
||||
// 使用后端 VO 中的标准字段名
|
||||
const activityStartTime = activity.activityStartTime || activity.startTime;
|
||||
const activityEndTime = activity.activityEndTime || activity.endTime;
|
||||
|
||||
// 生成显示用的时间格式
|
||||
let timeDisplay = '';
|
||||
if (activityStartTime || activityEndTime) {
|
||||
timeDisplay = formatActivityTime(activityStartTime, activityEndTime);
|
||||
}
|
||||
|
||||
return {
|
||||
activityStartTime: activityStartTime,
|
||||
activityEndTime: activityEndTime,
|
||||
activityTimeDisplay: timeDisplay || '时间未知',
|
||||
location: activity.location || activity.activityLocation || '',
|
||||
activityName: activity.activityName || '',
|
||||
participantCount: activity.participantCount || 0
|
||||
};
|
||||
});
|
||||
} else {
|
||||
console.warn('活动数据为空或格式不正确:', activitiesRes);
|
||||
activityList.value = [];
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('获取年审活动数据失败:', error);
|
||||
activityList.value = [];
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('获取详情失败:', error);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.annualReviewId,
|
||||
() => {
|
||||
getDetail();
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.annual-review-detail {
|
||||
padding: 20px;
|
||||
max-height: 80vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.form-header {
|
||||
text-align: center;
|
||||
margin-bottom: 30px;
|
||||
padding: 20px;
|
||||
border: 2px solid #409eff;
|
||||
border-radius: 8px;
|
||||
background: #fafafa;
|
||||
}
|
||||
|
||||
.form-title {
|
||||
margin: 0 0 15px 0;
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
.year-range {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 10px;
|
||||
color: #606266;
|
||||
}
|
||||
|
||||
.review-date {
|
||||
font-size: 14px;
|
||||
color: #909399;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.form-section {
|
||||
margin-bottom: 30px;
|
||||
padding: 20px;
|
||||
border: 1px solid #e4e7ed;
|
||||
border-radius: 8px;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
margin: 0 0 20px 0;
|
||||
padding-bottom: 10px;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
color: #303133;
|
||||
border-bottom: 2px solid #409eff;
|
||||
}
|
||||
|
||||
.basic-info-table {
|
||||
border: 1px solid #e4e7ed;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.info-row {
|
||||
display: flex;
|
||||
border-bottom: 1px solid #e4e7ed;
|
||||
}
|
||||
|
||||
.info-row:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.info-item {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
border-right: 1px solid #e4e7ed;
|
||||
}
|
||||
|
||||
.info-item:last-child {
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
.info-label {
|
||||
width: 100px;
|
||||
min-width: 100px;
|
||||
padding: 12px 15px;
|
||||
background-color: #f5f7fa;
|
||||
border-right: 1px solid #e4e7ed;
|
||||
font-weight: 500;
|
||||
color: #303133;
|
||||
font-size: 14px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.info-value {
|
||||
flex: 1;
|
||||
padding: 12px 15px;
|
||||
background-color: #fff;
|
||||
color: #606266;
|
||||
font-size: 14px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
min-height: 44px;
|
||||
}
|
||||
|
||||
.charter-status {
|
||||
font-size: 14px;
|
||||
color: #606266;
|
||||
font-weight: 400;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.charter-info {
|
||||
margin-top: 5px;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.opinion-section {
|
||||
margin-bottom: 20px;
|
||||
padding: 15px;
|
||||
border: 1px solid #e4e7ed;
|
||||
border-radius: 4px;
|
||||
background: #fafafa;
|
||||
}
|
||||
|
||||
.opinion-title {
|
||||
font-weight: bold;
|
||||
margin-bottom: 10px;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
.opinion-content {
|
||||
min-height: 60px;
|
||||
padding: 10px;
|
||||
border: 1px solid #e4e7ed;
|
||||
border-radius: 4px;
|
||||
background: #fff;
|
||||
margin-bottom: 10px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.opinion-signature {
|
||||
text-align: right;
|
||||
color: #606266;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
:deep(.el-table) {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
:deep(.el-table th) {
|
||||
background-color: #f5f7fa;
|
||||
color: #303133;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
:deep(.el-descriptions__label) {
|
||||
font-weight: bold;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
:deep(.el-descriptions__content) {
|
||||
color: #606266;
|
||||
}
|
||||
|
||||
.activity-time-display {
|
||||
white-space: pre-line;
|
||||
line-height: 1.4;
|
||||
font-size: 13px;
|
||||
}
|
||||
</style>
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,386 @@
|
||||
<template>
|
||||
<div class="p-2 page-container">
|
||||
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
|
||||
<div v-show="showSearch" class="mb-[10px]">
|
||||
<el-card shadow="hover">
|
||||
<el-form ref="queryFormRef" :model="queryParams" label-width="100px">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="6">
|
||||
<el-form-item label="社团名称" prop="clubName">
|
||||
<el-input v-model="queryParams.clubName" placeholder="请输入社团名称" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="年审年度" prop="reviewYear">
|
||||
<el-select v-model="queryParams.reviewYear" placeholder="请选择年审年度" clearable style="width: 100%">
|
||||
<el-option v-for="year in yearOptions" :key="year" :label="year + '年'" :value="year" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-select v-model="queryParams.status" placeholder="请选择状态" clearable style="width: 100%">
|
||||
<el-option v-for="option in statusOptions" :key="option.value" :label="option.label" :value="option.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="24" class="search-btn-container">
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</el-card>
|
||||
</div>
|
||||
</transition>
|
||||
|
||||
<el-card shadow="never" class="button-card table-card">
|
||||
<template #header>
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['stgl:clubAnnualReview:add']">新增</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['stgl:clubAnnualReview:edit']">修改</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['stgl:clubAnnualReview:remove']">删除</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="warning" icon="Download" @click="handleExport" v-hasPermi="['stgl:clubAnnualReview:export']">导出</el-button>
|
||||
</el-col>
|
||||
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
<el-table v-loading="loading" border :data="annualReviewList" @selection-change="handleSelectionChange" @row-dblclick="handleView">
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column label="社团名称" align="center" prop="clubName" min-width="150" />
|
||||
<el-table-column label="成立日期" align="center" prop="establishDate" width="120">
|
||||
<template #default="scope">
|
||||
<span>{{ parseTime(scope.row.establishDate, '{y}-{m}-{d}') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="社长" align="center" prop="presidentName" width="100" />
|
||||
<el-table-column label="年审年度" align="center" prop="reviewYear" width="100" />
|
||||
<el-table-column label="社团类型" align="center" prop="clubType" width="100">
|
||||
<template #default="scope">
|
||||
<dict-tag :options="stgl_club_type" :value="scope.row.clubType" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="指导老师" align="center" prop="advisorName" width="100" />
|
||||
<el-table-column label="成员数量" align="center" prop="memberCount" width="100" />
|
||||
<el-table-column label="资金数额" align="center" prop="fundAmount" width="120">
|
||||
<template #default="scope">
|
||||
<span v-if="scope.row.fundAmount">{{ scope.row.fundAmount }}元</span>
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" align="center" prop="status" width="100">
|
||||
<template #default="scope">
|
||||
<el-tag :type="getStatusTagType(scope.row.status) as any">{{ getStatusText(scope.row.status) }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="年审时间" align="center" prop="createTime" width="180">
|
||||
<template #default="scope">
|
||||
<span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="200" fixed="right">
|
||||
<template #default="scope">
|
||||
<el-tooltip content="查看" placement="top">
|
||||
<el-button link type="primary" icon="View" @click="handleView(scope.row)" v-hasPermi="['stgl:clubAnnualReview:query']"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="编辑" placement="top" v-if="scope.row.status === '0'">
|
||||
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['stgl:clubAnnualReview:edit']"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="提交" placement="top" v-if="scope.row.status === '0'">
|
||||
<el-button link type="success" icon="Check" @click="handleSubmit(scope.row)" v-hasPermi="['stgl:clubAnnualReview:submit']"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="撤回" placement="top" v-if="scope.row.status === '1'">
|
||||
<el-button link type="warning" icon="RefreshLeft" @click="handleWithdraw(scope.row)" v-hasPermi="['stgl:clubAnnualReview:withdraw']"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="删除" placement="top" v-if="scope.row.status === '0'">
|
||||
<el-button link type="danger" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['stgl:clubAnnualReview:remove']"></el-button>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination
|
||||
v-show="total > 0"
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNum"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</el-card>
|
||||
|
||||
<!-- 添加或修改社团年审记录对话框 -->
|
||||
<el-dialog :title="dialog.title" v-model="dialog.visible" width="1000px" append-to-body>
|
||||
<annual-review-form ref="annualReviewFormRef" @success="handleFormSuccess" />
|
||||
</el-dialog>
|
||||
|
||||
<!-- 查看社团年审记录对话框 -->
|
||||
<el-dialog title="年审记录详情" v-model="detailDialog.visible" width="1000px" append-to-body>
|
||||
<annual-review-detail :annual-review-id="detailDialog.annualReviewId" />
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="StglClubAnnualReview" lang="ts">
|
||||
import { useRoute } from 'vue-router';
|
||||
import {
|
||||
listStglClubAnnualReview,
|
||||
getStglClubAnnualReview,
|
||||
delStglClubAnnualReview,
|
||||
submitStglClubAnnualReview,
|
||||
withdrawStglClubAnnualReview,
|
||||
exportStglClubAnnualReview
|
||||
} from '@/api/stgl/club/annualReview';
|
||||
import { StglClubAnnualReviewVO, StglClubAnnualReviewQuery, ANNUAL_REVIEW_STATUS_OPTIONS } from '@/api/stgl/club/annualReview/type';
|
||||
import AnnualReviewForm from './form.vue';
|
||||
import AnnualReviewDetail from './detail.vue';
|
||||
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
const route = useRoute();
|
||||
const { stgl_club_type } = toRefs(proxy?.useDict('stgl_club_type') as any);
|
||||
|
||||
const annualReviewList = ref<StglClubAnnualReviewVO[]>([]);
|
||||
const loading = ref(true);
|
||||
const showSearch = ref(true);
|
||||
const ids = ref<Array<string | number>>([]);
|
||||
const single = ref(true);
|
||||
const multiple = ref(true);
|
||||
const total = ref(0);
|
||||
|
||||
const queryFormRef = ref<ElFormInstance>();
|
||||
const annualReviewFormRef = ref<InstanceType<typeof AnnualReviewForm>>();
|
||||
|
||||
// 状态选项
|
||||
const statusOptions = ref(ANNUAL_REVIEW_STATUS_OPTIONS);
|
||||
|
||||
// 年份选项(最近10年)
|
||||
const yearOptions = ref<number[]>([]);
|
||||
const initYearOptions = () => {
|
||||
const currentYear = new Date().getFullYear();
|
||||
for (let i = 0; i < 10; i++) {
|
||||
yearOptions.value.push(currentYear - i);
|
||||
}
|
||||
};
|
||||
|
||||
const data = reactive<{
|
||||
queryParams: StglClubAnnualReviewQuery;
|
||||
dialog: DialogOption;
|
||||
detailDialog: {
|
||||
visible: boolean;
|
||||
annualReviewId: string | number;
|
||||
};
|
||||
}>({
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
clubName: undefined,
|
||||
reviewYear: undefined,
|
||||
status: undefined
|
||||
},
|
||||
dialog: {
|
||||
visible: false,
|
||||
title: ''
|
||||
},
|
||||
detailDialog: {
|
||||
visible: false,
|
||||
annualReviewId: ''
|
||||
}
|
||||
});
|
||||
|
||||
// 初始化查询参数(处理从社团管理页面传递的参数)
|
||||
const initQueryParams = () => {
|
||||
if (route.query.clubName) {
|
||||
queryParams.value.clubName = route.query.clubName as string;
|
||||
}
|
||||
if (route.query.clubId) {
|
||||
// 如果有具体的社团ID,我们可以根据需要进行筛选
|
||||
// 这里暂时使用社团名称进行筛选
|
||||
}
|
||||
};
|
||||
|
||||
const { queryParams, dialog, detailDialog } = toRefs(data);
|
||||
|
||||
/** 查询社团年审记录列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true;
|
||||
const res = await listStglClubAnnualReview(queryParams.value);
|
||||
annualReviewList.value = res.rows;
|
||||
total.value = res.total;
|
||||
loading.value = false;
|
||||
};
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.value.pageNum = 1;
|
||||
getList();
|
||||
};
|
||||
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
queryFormRef.value?.resetFields();
|
||||
handleQuery();
|
||||
};
|
||||
|
||||
/** 多选框选中数据 */
|
||||
const handleSelectionChange = (selection: StglClubAnnualReviewVO[]) => {
|
||||
ids.value = selection.map((item) => item.id);
|
||||
single.value = selection.length !== 1;
|
||||
multiple.value = !selection.length;
|
||||
};
|
||||
|
||||
/** 新增按钮操作 */
|
||||
const handleAdd = () => {
|
||||
dialog.value = {
|
||||
visible: true,
|
||||
title: '添加社团年审记录'
|
||||
};
|
||||
nextTick(async () => {
|
||||
await annualReviewFormRef.value?.resetForm();
|
||||
// 如果从社团管理页面跳转过来,自动设置社团信息
|
||||
if (route.query.clubId) {
|
||||
annualReviewFormRef.value?.setClubId(route.query.clubId as string);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/** 修改按钮操作 */
|
||||
const handleUpdate = async (row?: StglClubAnnualReviewVO) => {
|
||||
const annualReviewId = row?.id || ids.value[0];
|
||||
const res = await getStglClubAnnualReview(annualReviewId);
|
||||
dialog.value = {
|
||||
visible: true,
|
||||
title: '修改社团年审记录'
|
||||
};
|
||||
nextTick(async () => {
|
||||
await annualReviewFormRef.value?.resetForm(res.data);
|
||||
});
|
||||
};
|
||||
|
||||
/** 查看按钮操作 */
|
||||
const handleView = (row: StglClubAnnualReviewVO) => {
|
||||
detailDialog.value = {
|
||||
visible: true,
|
||||
annualReviewId: row.id
|
||||
};
|
||||
};
|
||||
|
||||
/** 提交按钮操作 */
|
||||
const handleSubmit = async (row: StglClubAnnualReviewVO) => {
|
||||
try {
|
||||
await proxy?.$modal.confirm(`是否确认提交年审记录"${row.clubName}"?`);
|
||||
await submitStglClubAnnualReview(row.id);
|
||||
await getList();
|
||||
proxy?.$modal.msgSuccess('提交成功');
|
||||
} catch {}
|
||||
};
|
||||
|
||||
/** 撤回按钮操作 */
|
||||
const handleWithdraw = async (row: StglClubAnnualReviewVO) => {
|
||||
try {
|
||||
await proxy?.$modal.confirm(`是否确认撤回年审记录"${row.clubName}"?`);
|
||||
await withdrawStglClubAnnualReview(row.id);
|
||||
await getList();
|
||||
proxy?.$modal.msgSuccess('撤回成功');
|
||||
} catch {}
|
||||
};
|
||||
|
||||
/** 删除按钮操作 */
|
||||
const handleDelete = async (row?: StglClubAnnualReviewVO) => {
|
||||
const annualReviewIds = row?.id || ids.value;
|
||||
try {
|
||||
await proxy?.$modal.confirm('是否确认删除所选的社团年审记录?');
|
||||
await delStglClubAnnualReview(annualReviewIds);
|
||||
await getList();
|
||||
proxy?.$modal.msgSuccess('删除成功');
|
||||
} catch {}
|
||||
};
|
||||
|
||||
/** 导出按钮操作 */
|
||||
const handleExport = async () => {
|
||||
try {
|
||||
const res = await exportStglClubAnnualReview(queryParams.value);
|
||||
proxy?.$download.excel(res, `annual_review_${new Date().getTime()}.xlsx`);
|
||||
} catch (error) {
|
||||
proxy?.$modal.msgError('导出失败');
|
||||
}
|
||||
};
|
||||
|
||||
/** 表单提交成功回调 */
|
||||
const handleFormSuccess = () => {
|
||||
dialog.value.visible = false;
|
||||
getList();
|
||||
};
|
||||
|
||||
/** 获取状态标签类型 */
|
||||
const getStatusTagType = (status: string) => {
|
||||
const tagTypes: { [key: string]: string } = {
|
||||
'0': 'info',
|
||||
'1': 'warning',
|
||||
'2': '',
|
||||
'3': 'success',
|
||||
'4': 'danger'
|
||||
};
|
||||
return tagTypes[status] || '';
|
||||
};
|
||||
|
||||
/** 获取状态文本 */
|
||||
const getStatusText = (status: string) => {
|
||||
const option = statusOptions.value.find(item => item.value === status);
|
||||
return option?.label || status;
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
initYearOptions();
|
||||
initQueryParams();
|
||||
getList();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 页面容器样式 */
|
||||
.page-container {
|
||||
padding: 8px;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
/* 表格卡片布局 */
|
||||
.table-card {
|
||||
margin-top: 0;
|
||||
border-radius: 12px;
|
||||
border: 1px solid #e4e7ed;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
.table-card :deep(.el-card__body) {
|
||||
padding: 0px 20px 20px 20px !important;
|
||||
}
|
||||
|
||||
/* 移除按钮栏底部边框 */
|
||||
.button-card :deep(.el-card__header) {
|
||||
border-bottom: none !important;
|
||||
background: linear-gradient(135deg, #fafbff 0%, #f8fafc 100%);
|
||||
padding: 16px 20px;
|
||||
}
|
||||
|
||||
/* 搜索按钮容器样式 */
|
||||
.search-btn-container {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin-top: 10px;
|
||||
|
||||
.el-form-item {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -101,7 +101,7 @@
|
||||
<el-table-column label="班级" align="center" prop="className" width="120" />
|
||||
<el-table-column label="成员角色" align="center" prop="roleType" width="100">
|
||||
<template #default="scope">
|
||||
<span v-if="scope.row.roleType">{{ scope.row.roleType }}</span>
|
||||
<dict-tag :options="stgl_role_type" :value="scope.row.roleType" v-if="scope.row.roleType" />
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
@@ -200,7 +200,9 @@
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="成员角色" prop="roleType">
|
||||
<el-input v-model="form.roleType" placeholder="请输入成员角色" />
|
||||
<el-select v-model="form.roleType" placeholder="请选择成员角色">
|
||||
<el-option v-for="dict in stgl_role_type" :key="dict.value" :label="dict.label" :value="dict.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
@@ -237,7 +239,8 @@
|
||||
<el-descriptions-item label="联系方式">{{ viewForm.phone }}</el-descriptions-item>
|
||||
<el-descriptions-item label="邮箱">{{ viewForm.email }}</el-descriptions-item>
|
||||
<el-descriptions-item label="成员角色">
|
||||
{{ viewForm.roleType || '-' }}
|
||||
<dict-tag :options="stgl_role_type" :value="viewForm.roleType" v-if="viewForm.roleType" />
|
||||
<span v-else>-</span>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="状态">
|
||||
<dict-tag :options="stgl_member_status" :value="viewForm.status" />
|
||||
@@ -322,7 +325,7 @@ interface Props {
|
||||
const props = defineProps<Props>();
|
||||
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
const { sys_user_sex, stgl_member_status } = toRefs<any>(proxy?.useDict('sys_user_sex', 'stgl_member_status'));
|
||||
const { sys_user_sex, stgl_member_status, stgl_role_type } = toRefs<any>(proxy?.useDict('sys_user_sex', 'stgl_member_status', 'stgl_role_type'));
|
||||
|
||||
const memberList = ref<StglMemberVO[]>([]);
|
||||
const buttonLoading = ref(false);
|
||||
|
||||
@@ -60,6 +60,26 @@
|
||||
>转让</el-button
|
||||
>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="info"
|
||||
icon="UserFilled"
|
||||
:disabled="single"
|
||||
@click="handleAdvisorHistoryFromToolbar()"
|
||||
v-hasPermi="['stgl:club:query']"
|
||||
>指导老师</el-button
|
||||
>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="success"
|
||||
icon="Document"
|
||||
:disabled="single"
|
||||
@click="handleAnnualReview()"
|
||||
v-hasPermi="['stgl:clubAnnualReview:list']"
|
||||
>年审管理</el-button
|
||||
>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['stgl:club:remove']">删除</el-button>
|
||||
</el-col>
|
||||
@@ -127,7 +147,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="280" 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
|
||||
@@ -162,17 +182,18 @@
|
||||
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
|
||||
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
|
||||
@@ -312,7 +333,12 @@
|
||||
<!-- 指导老师信息 -->
|
||||
<div class="personnel-section">
|
||||
<h4 class="section-title">指导老师信息</h4>
|
||||
<AdvisorManager v-model="form.advisors" @change="handleAdvisorsChange" :disabled="!hasInstructorPermission" />
|
||||
<AdvisorManager
|
||||
v-model="form.advisors"
|
||||
:establish-date="form.establishDate"
|
||||
:is-new-club="!form.id"
|
||||
:disabled="!hasInstructorPermission"
|
||||
/>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
|
||||
@@ -617,6 +643,14 @@
|
||||
:current-member-count="currentClub?.memberCount"
|
||||
@member-count-changed="handleMemberCountChanged"
|
||||
/>
|
||||
|
||||
<!-- 指导老师历史记录弹窗 -->
|
||||
<AdvisorHistoryDialog
|
||||
v-model:visible="advisorHistoryDialog.visible"
|
||||
:title="advisorHistoryDialog.title"
|
||||
:club-id="currentAdvisorClub?.id"
|
||||
:club-name="currentAdvisorClub?.clubName"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -648,6 +682,7 @@ import { StglClubVO, StglClubQuery, StglClubForm } from '@/api/stgl/club/types';
|
||||
import StudentSelector from '@/components/StudentSelector/index.vue';
|
||||
import AdvisorManager from '@/components/AdvisorManager/index.vue';
|
||||
import MemberManagementDialog from './components/MemberManagementDialog.vue';
|
||||
import AdvisorHistoryDialog from '@/components/AdvisorHistoryDialog/index.vue';
|
||||
import { StudentVO } from '@/api/xg/student/types';
|
||||
import { AdvisorVO } from '@/components/AdvisorManager/index.vue';
|
||||
import { Document } from '@element-plus/icons-vue';
|
||||
@@ -711,6 +746,14 @@ const memberDialog = reactive<DialogOption>({
|
||||
const currentClub = ref<StglClubVO | null>(null);
|
||||
const memberActiveTab = ref('joined');
|
||||
|
||||
// 指导老师历史记录相关
|
||||
const advisorHistoryDialog = reactive<DialogOption>({
|
||||
visible: false,
|
||||
title: ''
|
||||
});
|
||||
|
||||
const currentAdvisorClub = ref<StglClubVO | null>(null);
|
||||
|
||||
interface TransferForm {
|
||||
clubId?: string | number;
|
||||
oldPresidentName?: string;
|
||||
@@ -850,12 +893,7 @@ const getList = async () => {
|
||||
|
||||
const res = await listStglClub(queryParams.value);
|
||||
|
||||
// 🔥 调试信息:检查返回的数据结构
|
||||
console.log('getList: 获取到的社团列表数据:', res);
|
||||
if (res.rows && res.rows.length > 0) {
|
||||
console.log('getList: 第一条社团数据详情:', res.rows[0]);
|
||||
console.log('getList: 第一条社团的advisorsJson字段:', res.rows[0].advisorsJson);
|
||||
}
|
||||
|
||||
|
||||
// 处理列表数据:将状态字段从数字类型转换为字符串类型,确保字典组件正常显示
|
||||
if (res.rows && Array.isArray(res.rows)) {
|
||||
@@ -927,10 +965,8 @@ const handleAdd = () => {
|
||||
const handleUpdate = async (row?: StglClubVO) => {
|
||||
reset();
|
||||
const _id = row?.id || ids.value[0];
|
||||
console.log('handleUpdate: 开始获取社团数据,ID:', _id);
|
||||
|
||||
const res = await getStglClub(_id);
|
||||
console.log('handleUpdate: API响应数据:', res.data);
|
||||
|
||||
// 处理经费来源数据:如果是字符串则分割为数组
|
||||
if (res.data.fundingSources) {
|
||||
@@ -964,37 +1000,30 @@ const handleUpdate = async (row?: StglClubVO) => {
|
||||
}
|
||||
|
||||
// 处理副社长信息:优先从JSON字段读取,如果没有则使用单一字段
|
||||
console.log('handleUpdate: 副社长原始数据:', {
|
||||
vicePresidentsJson: res.data.vicePresidentsJson,
|
||||
vicePresidentName: res.data.vicePresidentName,
|
||||
vicePresidentStudentNo: res.data.vicePresidentStudentNo,
|
||||
vicePresidentDept: res.data.vicePresidentDept,
|
||||
vicePresidentMajor: res.data.vicePresidentMajor,
|
||||
vicePresidentClass: res.data.vicePresidentClass,
|
||||
vicePresidentPhone: res.data.vicePresidentPhone
|
||||
});
|
||||
|
||||
|
||||
|
||||
// 处理指导老师信息:优先从JSON字段读取,如果没有则使用单一字段
|
||||
// 🔥 优先从JSON字段读取指导老师数据
|
||||
let advisorsData = [];
|
||||
if (res.data.advisorsJson) {
|
||||
try {
|
||||
(res.data as any).advisors = JSON.parse(res.data.advisorsJson);
|
||||
console.log('handleUpdate: 从JSON字段读取的指导老师数据:', (res.data as any).advisors);
|
||||
advisorsData = JSON.parse(res.data.advisorsJson);
|
||||
} catch (error) {
|
||||
console.warn('解析指导老师JSON数据失败:', error);
|
||||
(res.data as any).advisors = [];
|
||||
advisorsData = [];
|
||||
}
|
||||
} else if (res.data.advisorName) {
|
||||
// 回退到单一字段
|
||||
(res.data as any).advisors = [
|
||||
// 回退到单一字段,构建完整的teacher对象
|
||||
advisorsData = [
|
||||
{
|
||||
name: res.data.advisorName,
|
||||
dept: res.data.advisorDept,
|
||||
phone: res.data.advisorPhone || '',
|
||||
email: res.data.advisorEmail || '',
|
||||
userId: '',
|
||||
teacher: {
|
||||
userId: null,
|
||||
userName: '',
|
||||
nickName: res.data.advisorName,
|
||||
deptName: res.data.advisorDept,
|
||||
phonenumber: res.data.advisorPhone || '',
|
||||
@@ -1002,13 +1031,15 @@ const handleUpdate = async (row?: StglClubVO) => {
|
||||
}
|
||||
}
|
||||
];
|
||||
} else {
|
||||
(res.data as any).advisors = [];
|
||||
}
|
||||
|
||||
// 先设置其他数据
|
||||
Object.assign(form.value, res.data);
|
||||
console.log('handleUpdate: 表单数据设置后:', form.value);
|
||||
console.log('handleUpdate: 表单中的副社长数据:', form.value.vicePresidents);
|
||||
|
||||
// 然后单独设置指导老师数据,避免触发 watch 时的冲突
|
||||
nextTick(() => {
|
||||
form.value.advisors = advisorsData;
|
||||
});
|
||||
|
||||
dialog.visible = true;
|
||||
dialog.title = '修改社团信息';
|
||||
@@ -1042,15 +1073,9 @@ const submitForm = () => {
|
||||
|
||||
|
||||
|
||||
// 🔥 新增:同时保存JSON字段和兼容性字段
|
||||
// 同时保存JSON字段和兼容性字段
|
||||
submitData.advisorsJson = JSON.stringify(submitData.advisors || []);
|
||||
|
||||
// 调试日志:检查指导老师数据
|
||||
console.log('提交数据 - advisors:', submitData.advisors);
|
||||
console.log('提交数据 - advisorName:', submitData.advisorName);
|
||||
console.log('提交数据 - advisorDept:', submitData.advisorDept);
|
||||
console.log('完整提交数据:', submitData);
|
||||
|
||||
if (form.value.id) {
|
||||
await updateStglClub(submitData).finally(() => (buttonLoading.value = false));
|
||||
} else {
|
||||
@@ -1111,16 +1136,10 @@ const handleView = async (row: StglClubVO) => {
|
||||
|
||||
|
||||
|
||||
// 🔥 处理指导老师信息:优先从JSON字段读取,如果没有则使用单一字段
|
||||
console.log('handleView: 指导老师原始数据:', {
|
||||
advisorsJson: viewData.advisorsJson,
|
||||
advisorName: viewData.advisorName
|
||||
});
|
||||
|
||||
// 处理指导老师信息:优先从JSON字段读取,如果没有则使用单一字段
|
||||
if (viewData.advisorsJson) {
|
||||
try {
|
||||
(viewData as any).advisors = JSON.parse(viewData.advisorsJson);
|
||||
console.log('handleView: 从JSON字段读取的指导老师数据:', (viewData as any).advisors);
|
||||
} catch (error) {
|
||||
console.warn('解析指导老师JSON数据失败:', error);
|
||||
(viewData as any).advisors = [];
|
||||
@@ -1145,10 +1164,6 @@ const handleView = async (row: StglClubVO) => {
|
||||
(viewData as any).advisors = [];
|
||||
}
|
||||
|
||||
console.log('handleView: 处理后的数据:', {
|
||||
advisors: (viewData as any).advisors
|
||||
});
|
||||
|
||||
Object.assign(viewForm, viewData);
|
||||
viewDialog.visible = true;
|
||||
viewDialog.title = '查看社团信息';
|
||||
@@ -1212,16 +1227,12 @@ const getSourceTagType = (value: string) => {
|
||||
|
||||
/** 获取指导老师姓名列表 */
|
||||
const getAdvisorNames = (row: any): string[] => {
|
||||
console.log('getAdvisorNames: 处理行数据:', row);
|
||||
|
||||
// 优先从JSON字段读取指导老师数据
|
||||
if (row.advisorsJson) {
|
||||
try {
|
||||
const advisors = JSON.parse(row.advisorsJson);
|
||||
console.log('getAdvisorNames: 从JSON字段解析的指导老师数据:', advisors);
|
||||
if (Array.isArray(advisors)) {
|
||||
const names = advisors.map((advisor: any) => advisor.name || advisor.teacher?.nickName || '').filter((name) => name);
|
||||
console.log('getAdvisorNames: 从JSON提取的姓名列表:', names);
|
||||
return names;
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -1231,11 +1242,9 @@ const getAdvisorNames = (row: any): string[] => {
|
||||
|
||||
// 回退到单一字段
|
||||
if (row.advisorName) {
|
||||
console.log('getAdvisorNames: 从单一字段获取指导老师:', row.advisorName);
|
||||
return [row.advisorName];
|
||||
}
|
||||
|
||||
console.log('getAdvisorNames: 没有找到指导老师数据');
|
||||
return [];
|
||||
};
|
||||
|
||||
@@ -1265,107 +1274,91 @@ const handlePresidentChange = (student: StudentVO | null) => {
|
||||
|
||||
|
||||
|
||||
// 防止递归更新的锁
|
||||
const syncLock = ref(false);
|
||||
// 防止递归更新的标志
|
||||
const syncingAdvisors = ref(false);
|
||||
|
||||
/** 指导老师列表变化处理 */
|
||||
const handleAdvisorsChange = (advisors: AdvisorVO[]) => {
|
||||
// 添加调试日志
|
||||
console.log('handleAdvisorsChange 被调用,advisors:', advisors);
|
||||
|
||||
// 添加防护锁,避免递归更新
|
||||
if (syncLock.value) return;
|
||||
|
||||
syncLock.value = true;
|
||||
nextTick(() => {
|
||||
// 同步指导老师数据到表单
|
||||
form.value.advisors = advisors;
|
||||
|
||||
// 为了兼容旧版本,同步第一个指导老师的信息到旧字段
|
||||
if (advisors.length > 0) {
|
||||
const firstAdvisor = advisors[0];
|
||||
form.value.advisorName = firstAdvisor.name || firstAdvisor.teacher?.nickName;
|
||||
form.value.advisorDept = firstAdvisor.dept || firstAdvisor.teacher?.deptName;
|
||||
form.value.advisorPhone = firstAdvisor.phone || firstAdvisor.teacher?.phonenumber;
|
||||
form.value.advisorEmail = firstAdvisor.email || firstAdvisor.teacher?.email;
|
||||
} else {
|
||||
form.value.advisorName = undefined;
|
||||
form.value.advisorDept = undefined;
|
||||
form.value.advisorPhone = undefined;
|
||||
form.value.advisorEmail = undefined;
|
||||
}
|
||||
syncLock.value = false;
|
||||
});
|
||||
};
|
||||
/** 监听指导老师数据变化,同步兼容性字段 */
|
||||
watch(
|
||||
() => form.value.advisors,
|
||||
(advisors: AdvisorVO[]) => {
|
||||
// 防止递归更新
|
||||
if (syncingAdvisors.value) return;
|
||||
|
||||
syncingAdvisors.value = true;
|
||||
nextTick(() => {
|
||||
try {
|
||||
// 为了兼容旧版本,同步第一个指导老师的信息到旧字段
|
||||
if (advisors && advisors.length > 0) {
|
||||
const firstAdvisor = advisors[0];
|
||||
form.value.advisorName = firstAdvisor.name || firstAdvisor.teacher?.nickName;
|
||||
form.value.advisorDept = firstAdvisor.dept || firstAdvisor.teacher?.deptName;
|
||||
form.value.advisorPhone = firstAdvisor.phone || firstAdvisor.teacher?.phonenumber;
|
||||
form.value.advisorEmail = firstAdvisor.email || firstAdvisor.teacher?.email;
|
||||
} else {
|
||||
form.value.advisorName = undefined;
|
||||
form.value.advisorDept = undefined;
|
||||
form.value.advisorPhone = undefined;
|
||||
form.value.advisorEmail = undefined;
|
||||
}
|
||||
} finally {
|
||||
syncingAdvisors.value = false;
|
||||
}
|
||||
});
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
/** 检查用户是否为指定社团的社长(无论是否为创建者) */
|
||||
const checkIsPresident = async (clubId: string | number): Promise<boolean> => {
|
||||
try {
|
||||
console.log('🔍 checkIsPresident: 开始检查社长权限, clubId:', clubId);
|
||||
const res = await checkPresidentRole(clubId);
|
||||
console.log('🔍 checkIsPresident: API返回结果:', res);
|
||||
const isPresident = res.data === true;
|
||||
console.log('🔍 checkIsPresident: 最终结果:', isPresident);
|
||||
return isPresident;
|
||||
} catch (error) {
|
||||
console.error('❌ checkIsPresident: 检查社长权限失败:', error);
|
||||
console.error('checkIsPresident: 检查社长权限失败:', error);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
/** 批量检查社长权限(检查用户是否为各社团的社长,无论是否为创建者) */
|
||||
const checkBatchPresidentRoles = async (clubs: StglClubVO[]) => {
|
||||
console.log('🔍 checkBatchPresidentRoles: 开始批量检查权限, clubs数量:', clubs.length);
|
||||
const checks = clubs.map(async (club) => {
|
||||
const isPresident = await checkIsPresident(club.id!);
|
||||
presidentRoles.value[club.id!] = isPresident;
|
||||
console.log(`🔍 checkBatchPresidentRoles: 社团${club.id}(${club.clubName}) 权限检查结果:`, isPresident);
|
||||
return { clubId: club.id!, isPresident };
|
||||
});
|
||||
|
||||
await Promise.all(checks);
|
||||
console.log('🔍 checkBatchPresidentRoles: 批量权限检查完成, presidentRoles:', presidentRoles.value);
|
||||
};
|
||||
|
||||
/** 判断是否显示转让按钮 */
|
||||
const shouldShowTransferButton = (club: StglClubVO): boolean => {
|
||||
console.log('🔍 shouldShowTransferButton: 开始判断转让按钮显示, club:', club);
|
||||
|
||||
// 1. 检查基础权限
|
||||
const hasEditPermission = proxy?.$auth.hasPermi('stgl:club:edit');
|
||||
console.log('🔍 shouldShowTransferButton: 基础edit权限检查:', hasEditPermission);
|
||||
if (!hasEditPermission) {
|
||||
console.log('❌ shouldShowTransferButton: 无基础edit权限,返回false');
|
||||
return false;
|
||||
}
|
||||
|
||||
// 2. 检查角色权限 - 如果有stgl_president角色,则需要进一步检查是否为该社团社长
|
||||
const hasPresidentRole = proxy?.$auth.hasRole('stgl_president');
|
||||
console.log('🔍 shouldShowTransferButton: stgl_president角色检查:', hasPresidentRole);
|
||||
|
||||
if (hasPresidentRole) {
|
||||
// 如果权限检查还未完成,返回false(避免按钮闪现)
|
||||
const isPresident = presidentRoles.value[club.id!];
|
||||
console.log('🔍 shouldShowTransferButton: 社团社长权限检查结果:', isPresident, 'presidentRoles:', presidentRoles.value);
|
||||
if (isPresident === undefined) {
|
||||
console.log('⏳ shouldShowTransferButton: 权限检查未完成,返回false避免闪现');
|
||||
return false;
|
||||
}
|
||||
// 社长可以看到转让按钮(无论是否为创建者)
|
||||
const result = isPresident === true;
|
||||
console.log('🔍 shouldShowTransferButton: stgl_president角色最终结果:', result);
|
||||
return result;
|
||||
}
|
||||
|
||||
// 3. 如果是管理员角色,显示转让按钮(但转让操作仍需社长权限验证)
|
||||
const hasAdminRole = proxy?.$auth.hasRoleOr(['admin', 'common']);
|
||||
console.log('🔍 shouldShowTransferButton: 管理员角色检查:', hasAdminRole);
|
||||
if (hasAdminRole) {
|
||||
console.log('✅ shouldShowTransferButton: 管理员角色,返回true');
|
||||
return true;
|
||||
}
|
||||
|
||||
console.log('❌ shouldShowTransferButton: 无任何有效角色,返回false');
|
||||
return false;
|
||||
};
|
||||
|
||||
@@ -1473,7 +1466,6 @@ const handleActivityManagement = (row: StglClubVO) => {
|
||||
|
||||
/** 成员管理按钮操作 */
|
||||
const handleMemberManagement = (row: StglClubVO) => {
|
||||
console.log('handleMemberManagement: 打开成员管理弹窗, row:', row);
|
||||
currentClub.value = row;
|
||||
memberDialog.visible = true;
|
||||
memberDialog.title = `${row.clubName} - 成员管理`;
|
||||
@@ -1482,7 +1474,6 @@ const handleMemberManagement = (row: StglClubVO) => {
|
||||
|
||||
/** 处理成员数量变化 */
|
||||
const handleMemberCountChanged = (newCount: number) => {
|
||||
console.log('handleMemberCountChanged: 成员数量变化:', newCount, '当前社团:', currentClub.value);
|
||||
if (currentClub.value) {
|
||||
// 更新当前社团的成员数量
|
||||
currentClub.value.memberCount = newCount;
|
||||
@@ -1491,11 +1482,40 @@ const handleMemberCountChanged = (newCount: number) => {
|
||||
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]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/** 指导老师历史记录按钮操作 */
|
||||
const handleAdvisorHistory = (row: StglClubVO) => {
|
||||
currentAdvisorClub.value = row;
|
||||
advisorHistoryDialog.visible = true;
|
||||
advisorHistoryDialog.title = `${row.clubName} - 指导老师历史记录`;
|
||||
};
|
||||
|
||||
/** 工具栏指导老师按钮操作 */
|
||||
const handleAdvisorHistoryFromToolbar = () => {
|
||||
const selectedClub = clubList.value.find((c) => ids.value.includes(c.id!));
|
||||
if (selectedClub) {
|
||||
handleAdvisorHistory(selectedClub);
|
||||
}
|
||||
};
|
||||
|
||||
/** 年审管理按钮操作 */
|
||||
const handleAnnualReview = () => {
|
||||
const selectedClub = clubList.value.find((c) => ids.value.includes(c.id!));
|
||||
if (selectedClub) {
|
||||
router.push({
|
||||
path: '/stgl/club/info/annualReview/index',
|
||||
query: {
|
||||
clubId: selectedClub.id,
|
||||
clubName: selectedClub.clubName,
|
||||
from: 'club'
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getList();
|
||||
});
|
||||
|
||||
@@ -77,8 +77,26 @@
|
||||
<dict-tag :options="stgl_activity_type" :value="scope.row.activityType" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="主办方" align="center" prop="organizerName" width="120" show-overflow-tooltip />
|
||||
<el-table-column label="关联组织" align="center" width="120" show-overflow-tooltip>
|
||||
<!-- 隐藏主办方字段 -->
|
||||
<!-- <el-table-column label="主办方" align="center" prop="organizerName" width="120" show-overflow-tooltip /> -->
|
||||
<el-table-column label="关联部门" align="center" width="150" show-overflow-tooltip>
|
||||
<template #default="scope">
|
||||
<div v-if="getTableRelatedDeptNames(scope.row).length > 0" class="dept-list-table">
|
||||
<el-tag
|
||||
v-for="(deptName, index) in getTableRelatedDeptNames(scope.row)"
|
||||
:key="index"
|
||||
type="info"
|
||||
size="small"
|
||||
class="dept-tag-table"
|
||||
:title="deptName"
|
||||
>
|
||||
{{ deptName }}
|
||||
</el-tag>
|
||||
</div>
|
||||
<span v-else class="text-gray-400">未关联</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="关联社团" align="center" width="120" show-overflow-tooltip>
|
||||
<template #default="scope">
|
||||
<span v-if="scope.row.organizationName">{{ scope.row.organizationName }}</span>
|
||||
<span v-else class="text-gray-400">未关联</span>
|
||||
@@ -96,7 +114,7 @@
|
||||
<el-table-column label="活动地点" align="center" prop="location" width="120" show-overflow-tooltip />
|
||||
<el-table-column label="活动状态" align="center" prop="activityStatus" width="100">
|
||||
<template #default="scope">
|
||||
<dict-tag :options="stgl_activity_status" :value="scope.row.activityStatus" />
|
||||
<dict-tag :options="stgl_activity_status" :value="calculateActivityStatus(scope.row)" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="报名时间" align="center" width="180">
|
||||
@@ -175,15 +193,28 @@
|
||||
<div class="detail-content">
|
||||
<!-- 基本信息 -->
|
||||
<div class="detail-section">
|
||||
<el-descriptions :column="2" border>
|
||||
<el-descriptions :column="2" border class="activity-detail-descriptions">
|
||||
<el-descriptions-item label="活动名称">{{ detailForm.activityName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="活动类型">
|
||||
<dict-tag :options="stgl_activity_type" :value="detailForm.activityType" />
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="活动主题">{{ detailForm.activityTheme || '暂无' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="主办方">{{ detailForm.organizerName || '暂无' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="承办方">{{ detailForm.undertakerName || '暂无' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="关联组织">
|
||||
<el-descriptions-item label="活动地点">{{ detailForm.location || '暂无' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="关联部门">
|
||||
<div v-if="getTableRelatedDeptNames(detailForm).length > 0" class="dept-list-detail-optimized">
|
||||
<el-tag
|
||||
v-for="(deptName, index) in getTableRelatedDeptNames(detailForm)"
|
||||
:key="index"
|
||||
type="info"
|
||||
size="small"
|
||||
class="dept-tag-detail-optimized"
|
||||
>
|
||||
{{ deptName }}
|
||||
</el-tag>
|
||||
</div>
|
||||
<span v-else class="text-gray-400">未关联</span>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="关联社团">
|
||||
<span v-if="detailForm.organizationName">
|
||||
{{ detailForm.organizationName }}
|
||||
<el-tag size="small" type="info" class="ml-1">
|
||||
@@ -192,17 +223,10 @@
|
||||
</span>
|
||||
<span v-else>未关联</span>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="指导老师">
|
||||
<span v-if="detailForm.instructors && detailForm.instructors.length > 0">
|
||||
{{ getInstructorNames(detailForm.instructors) }}
|
||||
</span>
|
||||
<span v-else>暂无</span>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="活动地点">{{ detailForm.location || '暂无' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="开始时间">{{ parseTime(detailForm.startTime, '{y}-{m}-{d} {h}:{i}') }}</el-descriptions-item>
|
||||
<el-descriptions-item label="结束时间">{{ parseTime(detailForm.endTime, '{y}-{m}-{d} {h}:{i}') }}</el-descriptions-item>
|
||||
<el-descriptions-item label="活动状态">
|
||||
<dict-tag :options="stgl_activity_status" :value="detailForm.activityStatus" />
|
||||
<dict-tag :options="stgl_activity_status" :value="calculateActivityStatus(detailForm)" />
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="报名状态">
|
||||
<dict-tag :options="stgl_registration_status" :value="detailForm.registrationStatus" />
|
||||
@@ -1136,6 +1160,69 @@ const getInstructorNames = (instructors: string | any[]): string => {
|
||||
.join('、');
|
||||
};
|
||||
|
||||
/** 获取活动关联部门名称列表 */
|
||||
const getTableRelatedDeptNames = (activity: MyActivityVO): string[] => {
|
||||
// 优先检查relatedDeptNames字段(多部门支持)
|
||||
if (activity.relatedDeptNames) {
|
||||
// 如果是数组类型,直接使用
|
||||
if (Array.isArray(activity.relatedDeptNames)) {
|
||||
return activity.relatedDeptNames.filter(name => name);
|
||||
}
|
||||
// 如果是字符串类型(逗号分隔),进行分割
|
||||
const deptNamesStr = activity.relatedDeptNames as unknown as string;
|
||||
if (typeof deptNamesStr === 'string' && deptNamesStr.includes(',')) {
|
||||
return deptNamesStr.split(',').map(name => name.trim()).filter(name => name);
|
||||
}
|
||||
// 如果是单个字符串
|
||||
if (typeof deptNamesStr === 'string' && deptNamesStr.trim()) {
|
||||
return [deptNamesStr.trim()];
|
||||
}
|
||||
}
|
||||
|
||||
// 兼容旧的单部门字段
|
||||
if (activity.relatedDeptName) {
|
||||
if (activity.relatedDeptName.includes(',')) {
|
||||
return activity.relatedDeptName.split(',').map(name => name.trim()).filter(name => name);
|
||||
} else {
|
||||
return [activity.relatedDeptName];
|
||||
}
|
||||
}
|
||||
|
||||
return [];
|
||||
};
|
||||
|
||||
/** 计算活动的实际状态(基于时间比较) */
|
||||
const calculateActivityStatus = (activity: MyActivityVO): string => {
|
||||
// 如果活动状态是草稿(0)、提交审核(1)、待审批(2)、已驳回(7)、已取消(6),直接返回原状态
|
||||
const originalStatus = activity.activityStatus;
|
||||
if (['0', '1', '2', '6', '7'].includes(originalStatus)) {
|
||||
return originalStatus;
|
||||
}
|
||||
|
||||
// 只有已通过(3)、进行中(4)、已结束(5)状态的活动需要根据时间动态计算
|
||||
if (!['3', '4', '5'].includes(originalStatus)) {
|
||||
return originalStatus;
|
||||
}
|
||||
|
||||
// 如果没有开始时间和结束时间,返回原状态
|
||||
if (!activity.startTime || !activity.endTime) {
|
||||
return originalStatus;
|
||||
}
|
||||
|
||||
const now = new Date();
|
||||
const startTime = new Date(activity.startTime);
|
||||
const endTime = new Date(activity.endTime);
|
||||
|
||||
// 根据时间比较确定实际状态
|
||||
if (now < startTime) {
|
||||
return '3'; // 已通过(活动还未开始)
|
||||
} else if (now >= startTime && now <= endTime) {
|
||||
return '4'; // 进行中(活动正在进行)
|
||||
} else {
|
||||
return '5'; // 已结束(活动已结束)
|
||||
}
|
||||
};
|
||||
|
||||
/** 对话框关闭时重置队伍信息 */
|
||||
const handleDetailClose = () => {
|
||||
teamInfo.value = null;
|
||||
@@ -1423,4 +1510,120 @@ onMounted(() => {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
/* 部门标签样式 */
|
||||
.dept-list-table {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 4px 6px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.dept-tag-table {
|
||||
margin: 0;
|
||||
font-size: 12px;
|
||||
line-height: 1.3;
|
||||
padding: 2px 6px;
|
||||
border-radius: 2px;
|
||||
white-space: nowrap;
|
||||
overflow: visible;
|
||||
text-overflow: unset;
|
||||
flex-shrink: 0;
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
/* 详情页面部门标签样式 */
|
||||
.dept-list-detail {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 6px 8px;
|
||||
align-items: center;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.dept-tag-detail {
|
||||
margin: 0;
|
||||
font-size: 13px;
|
||||
line-height: 1.4;
|
||||
padding: 4px 8px;
|
||||
border-radius: 4px;
|
||||
white-space: nowrap;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* 活动详情描述列表优化 */
|
||||
.activity-detail-descriptions {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.activity-detail-descriptions :deep(.el-descriptions__table) {
|
||||
table-layout: fixed;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.activity-detail-descriptions :deep(.el-descriptions__label) {
|
||||
width: 120px !important;
|
||||
min-width: 120px;
|
||||
padding: 12px 16px;
|
||||
background-color: #fafafa;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
.activity-detail-descriptions :deep(.el-descriptions__content) {
|
||||
width: auto !important;
|
||||
padding: 12px 16px;
|
||||
word-break: break-word;
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
||||
/* 针对两列布局的优化 */
|
||||
.activity-detail-descriptions :deep(.el-descriptions__table tr) {
|
||||
display: table-row;
|
||||
}
|
||||
|
||||
.activity-detail-descriptions :deep(.el-descriptions__table td) {
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
/* 第一列(左侧)宽度控制 */
|
||||
.activity-detail-descriptions :deep(.el-descriptions__table td:nth-child(1)) {
|
||||
width: 120px;
|
||||
}
|
||||
|
||||
.activity-detail-descriptions :deep(.el-descriptions__table td:nth-child(2)) {
|
||||
width: calc(50% - 60px);
|
||||
}
|
||||
|
||||
/* 第二列(右侧)宽度控制 */
|
||||
.activity-detail-descriptions :deep(.el-descriptions__table td:nth-child(3)) {
|
||||
width: 120px;
|
||||
}
|
||||
|
||||
.activity-detail-descriptions :deep(.el-descriptions__table td:nth-child(4)) {
|
||||
width: calc(50% - 60px);
|
||||
}
|
||||
|
||||
/* 优化后的部门标签样式 */
|
||||
.dept-list-detail-optimized {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 4px 6px;
|
||||
align-items: center;
|
||||
margin-top: 4px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.dept-tag-detail-optimized {
|
||||
margin: 0;
|
||||
font-size: 13px;
|
||||
line-height: 1.4;
|
||||
padding: 3px 8px;
|
||||
border-radius: 4px;
|
||||
white-space: nowrap;
|
||||
flex-shrink: 0;
|
||||
/* 移除max-width限制,让标签自适应内容宽度 */
|
||||
}
|
||||
</style>
|
||||
@@ -61,7 +61,7 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="联系电话" align="center" prop="phone" min-width="55" />
|
||||
<el-table-column label="加入社团" align="center" width="160">
|
||||
<el-table-column label="加入社团" align="center" width="120">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
v-if="scope.row.clubCount > 0"
|
||||
@@ -75,7 +75,7 @@
|
||||
<span v-else class="no-data">0</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="加入志愿者队伍" align="center" width="170">
|
||||
<el-table-column label="加入志愿者队伍" align="center" width="130">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
v-if="scope.row.volunteerTeamCount > 0"
|
||||
@@ -89,7 +89,7 @@
|
||||
<span v-else class="no-data">0</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="参与活动" align="center" width="160">
|
||||
<el-table-column label="参与活动" align="center" width="120">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
v-if="scope.row.activityCount > 0"
|
||||
@@ -103,6 +103,24 @@
|
||||
<span v-else class="no-data">0</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="今日活动" align="center" width="240">
|
||||
<template #default="scope">
|
||||
<span v-if="scope.row.todayActivityName" class="activity-name">
|
||||
{{ scope.row.todayActivityName }}
|
||||
</span>
|
||||
<span v-else class="no-data">暂无今日活动</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="活动时间" align="center" width="160">
|
||||
<template #default="scope">
|
||||
<div v-if="scope.row.todayActivityStartTime && scope.row.todayActivityEndTime" class="activity-time">
|
||||
<div class="time-start">{{ scope.row.todayActivityStartTime }}</div>
|
||||
<div class="time-separator">~</div>
|
||||
<div class="time-end">{{ scope.row.todayActivityEndTime }}</div>
|
||||
</div>
|
||||
<span v-else class="no-data">-</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<div class="pagination-container">
|
||||
@@ -452,4 +470,33 @@ onMounted(() => {
|
||||
.activity-item:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
/* 今日活动和活动时间样式 */
|
||||
.activity-name {
|
||||
color: #409eff;
|
||||
font-weight: 500;
|
||||
line-height: 1.4;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.activity-time {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.time-start,
|
||||
.time-end {
|
||||
color: #606266;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.time-separator {
|
||||
color: #909399;
|
||||
font-size: 12px;
|
||||
margin: 0 4px;
|
||||
}
|
||||
</style>
|
||||
Reference in new issue
Block a user