feat: 作品分配仅限活动评委、评委库仅启用及 UGC 调整
- 作品管理分配评委仅使用活动显式名单,assignWork 校验 t_biz_contest_judge - 添加评委/评审进度选择评委时仅查询启用账号;接口文档与 API 注释 - UGC 作品分页与公开创作服务相关改动 Made-with: Cursor
This commit is contained in:
parent
937f0650f0
commit
b19acbd6d5
@ -30,7 +30,8 @@ public class JudgesManagementController {
|
|||||||
|
|
||||||
@GetMapping
|
@GetMapping
|
||||||
@RequirePermission("judge:read")
|
@RequirePermission("judge:read")
|
||||||
@Operation(summary = "查询评委列表")
|
@Operation(summary = "查询评委列表",
|
||||||
|
description = "status:enabled/disabled,不传则返回全部状态。向活动添加评委、评审进度等「仅用于选择评委」的场景请传 status=enabled,不包含停用账号。")
|
||||||
public Result<PageResult<Map<String, Object>>> findAll(
|
public Result<PageResult<Map<String, Object>>> findAll(
|
||||||
@RequestParam(defaultValue = "1") Long page,
|
@RequestParam(defaultValue = "1") Long page,
|
||||||
@RequestParam(defaultValue = "10") Long pageSize,
|
@RequestParam(defaultValue = "10") Long pageSize,
|
||||||
|
|||||||
@ -36,7 +36,7 @@ public class ContestJudgeController {
|
|||||||
@GetMapping("/contest/{contestId}")
|
@GetMapping("/contest/{contestId}")
|
||||||
@RequirePermission("contest:read")
|
@RequirePermission("contest:read")
|
||||||
@Operation(summary = "查询活动评委列表",
|
@Operation(summary = "查询活动评委列表",
|
||||||
description = "返回 assigned(显式关联)与 implicitPool(平台隐式池)。添加评委抽屉仅用 assigned 回显;作品分配可选池为 assigned ∪ implicitPool(前端合并)。")
|
description = "返回 assigned(t_biz_contest_judge 显式关联)与 implicitPool(平台评委租户下启用用户,未写入关联表时由业务视为可选池)。添加评委抽屉仅用 assigned 回显;作品管理「分配评委」仅应使用 assigned。")
|
||||||
public Result<ContestJudgesForContestVo> findByContest(@PathVariable Long contestId) {
|
public Result<ContestJudgesForContestVo> findByContest(@PathVariable Long contestId) {
|
||||||
return Result.success(contestJudgeService.findByContest(contestId));
|
return Result.success(contestJudgeService.findByContest(contestId));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -50,6 +50,19 @@ public class ContestReviewServiceImpl implements IContestReviewService {
|
|||||||
private final ContestReviewRuleMapper reviewRuleMapper;
|
private final ContestReviewRuleMapper reviewRuleMapper;
|
||||||
private final SysUserMapper sysUserMapper;
|
private final SysUserMapper sysUserMapper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 仅允许将「已在本活动评委表 t_biz_contest_judge 中显式配置」的评委新分配到作品。
|
||||||
|
*/
|
||||||
|
private void assertJudgeAssignedToContest(Long contestId, Long judgeId) {
|
||||||
|
LambdaQueryWrapper<BizContestJudge> w = new LambdaQueryWrapper<>();
|
||||||
|
w.eq(BizContestJudge::getContestId, contestId);
|
||||||
|
w.eq(BizContestJudge::getJudgeId, judgeId);
|
||||||
|
w.eq(BizContestJudge::getValidState, 1);
|
||||||
|
if (judgeMapper.selectCount(w) == 0) {
|
||||||
|
throw BusinessException.of(ErrorCode.BAD_REQUEST, "所选评委未加入本活动,请先在活动评委管理中配置");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ====== 作品分配 ======
|
// ====== 作品分配 ======
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -99,6 +112,8 @@ public class ContestReviewServiceImpl implements IContestReviewService {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assertJudgeAssignedToContest(contestId, judgeId);
|
||||||
|
|
||||||
BizContestWorkJudgeAssignment assignment = new BizContestWorkJudgeAssignment();
|
BizContestWorkJudgeAssignment assignment = new BizContestWorkJudgeAssignment();
|
||||||
assignment.setContestId(contestId);
|
assignment.setContestId(contestId);
|
||||||
assignment.setWorkId(workId);
|
assignment.setWorkId(workId);
|
||||||
|
|||||||
@ -24,6 +24,7 @@ public class PublicCreationService {
|
|||||||
|
|
||||||
private final UgcWorkMapper ugcWorkMapper;
|
private final UgcWorkMapper ugcWorkMapper;
|
||||||
private final UgcWorkPageMapper ugcWorkPageMapper;
|
private final UgcWorkPageMapper ugcWorkPageMapper;
|
||||||
|
private final PublicUserWorkService publicUserWorkService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 提交 AI 创作(保留但降级为辅助接口)
|
* 提交 AI 创作(保留但降级为辅助接口)
|
||||||
@ -118,6 +119,7 @@ public class PublicCreationService {
|
|||||||
.orderByDesc(UgcWork::getCreateTime);
|
.orderByDesc(UgcWork::getCreateTime);
|
||||||
|
|
||||||
IPage<UgcWork> result = ugcWorkMapper.selectPage(new Page<>(page, pageSize), wrapper);
|
IPage<UgcWork> result = ugcWorkMapper.selectPage(new Page<>(page, pageSize), wrapper);
|
||||||
|
publicUserWorkService.attachPageCounts(result.getRecords());
|
||||||
return PageResult.from(result);
|
return PageResult.from(result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,6 +11,7 @@ import com.competition.modules.ugc.entity.UgcWork;
|
|||||||
import com.competition.modules.ugc.entity.UgcWorkPage;
|
import com.competition.modules.ugc.entity.UgcWorkPage;
|
||||||
import com.competition.modules.ugc.entity.UgcWorkTag;
|
import com.competition.modules.ugc.entity.UgcWorkTag;
|
||||||
import com.competition.modules.ugc.mapper.UgcWorkMapper;
|
import com.competition.modules.ugc.mapper.UgcWorkMapper;
|
||||||
|
import com.competition.modules.ugc.mapper.UgcWorkPageCountRow;
|
||||||
import com.competition.modules.ugc.mapper.UgcWorkPageMapper;
|
import com.competition.modules.ugc.mapper.UgcWorkPageMapper;
|
||||||
import com.competition.modules.ugc.mapper.UgcWorkTagMapper;
|
import com.competition.modules.ugc.mapper.UgcWorkTagMapper;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
@ -21,6 +22,7 @@ import org.springframework.util.StringUtils;
|
|||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Service
|
@Service
|
||||||
@ -89,9 +91,36 @@ public class PublicUserWorkService {
|
|||||||
wrapper.orderByDesc(UgcWork::getCreateTime);
|
wrapper.orderByDesc(UgcWork::getCreateTime);
|
||||||
|
|
||||||
IPage<UgcWork> result = ugcWorkMapper.selectPage(new Page<>(page, pageSize), wrapper);
|
IPage<UgcWork> result = ugcWorkMapper.selectPage(new Page<>(page, pageSize), wrapper);
|
||||||
|
fillPageCounts(result.getRecords());
|
||||||
return PageResult.from(result);
|
return PageResult.from(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 为作品列表填充 _count.pages(供其它模块复用,如创作历史)
|
||||||
|
*/
|
||||||
|
public void attachPageCounts(List<UgcWork> works) {
|
||||||
|
fillPageCounts(works);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 为列表中的每条作品填充 _count.pages(来自 t_ugc_work_page 行数) */
|
||||||
|
private void fillPageCounts(List<UgcWork> works) {
|
||||||
|
if (works == null || works.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
List<Long> ids = works.stream().map(UgcWork::getId).collect(Collectors.toList());
|
||||||
|
List<UgcWorkPageCountRow> rows = ugcWorkPageMapper.countPagesGroupedByWorkIds(ids);
|
||||||
|
Map<Long, Integer> idToPages = rows.stream()
|
||||||
|
.collect(Collectors.toMap(
|
||||||
|
UgcWorkPageCountRow::getWorkId,
|
||||||
|
r -> r.getPageCount() != null ? r.getPageCount().intValue() : 0,
|
||||||
|
(a, b) -> a));
|
||||||
|
for (UgcWork work : works) {
|
||||||
|
UgcWork.WorkCountMeta meta = new UgcWork.WorkCountMeta();
|
||||||
|
meta.setPages(idToPages.getOrDefault(work.getId(), 0));
|
||||||
|
work.setCount(meta);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取作品详情
|
* 获取作品详情
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import com.baomidou.mybatisplus.annotation.TableField;
|
|||||||
import com.baomidou.mybatisplus.annotation.TableId;
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
import com.baomidou.mybatisplus.annotation.TableName;
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
|
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
@ -137,4 +138,19 @@ public class UgcWork implements Serializable {
|
|||||||
@Schema(description = "修改时间")
|
@Schema(description = "修改时间")
|
||||||
@TableField("modify_time")
|
@TableField("modify_time")
|
||||||
private LocalDateTime modifyTime;
|
private LocalDateTime modifyTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 列表接口填充:绘本页数等(非表字段,与前端 _count.pages 对齐)
|
||||||
|
*/
|
||||||
|
@Schema(description = "统计信息(列表接口填充)")
|
||||||
|
@TableField(exist = false)
|
||||||
|
@JsonProperty("_count")
|
||||||
|
private WorkCountMeta count;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Schema(description = "作品计数")
|
||||||
|
public static class WorkCountMeta implements Serializable {
|
||||||
|
@Schema(description = "绘本页数")
|
||||||
|
private Integer pages;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,12 @@
|
|||||||
|
package com.competition.modules.ugc.mapper;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 按作品分组的绘本页数统计(列表接口用)
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class UgcWorkPageCountRow {
|
||||||
|
private Long workId;
|
||||||
|
private Long pageCount;
|
||||||
|
}
|
||||||
@ -3,7 +3,21 @@ package com.competition.modules.ugc.mapper;
|
|||||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
import com.competition.modules.ugc.entity.UgcWorkPage;
|
import com.competition.modules.ugc.entity.UgcWorkPage;
|
||||||
import org.apache.ibatis.annotations.Mapper;
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
import org.apache.ibatis.annotations.Select;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
@Mapper
|
@Mapper
|
||||||
public interface UgcWorkPageMapper extends BaseMapper<UgcWorkPage> {
|
public interface UgcWorkPageMapper extends BaseMapper<UgcWorkPage> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量统计各作品的绘本页数(用于作品列表展示)
|
||||||
|
*/
|
||||||
|
@Select("<script>"
|
||||||
|
+ "SELECT work_id AS workId, COUNT(1) AS pageCount FROM t_ugc_work_page WHERE work_id IN "
|
||||||
|
+ "<foreach collection=\"workIds\" item=\"id\" open=\"(\" separator=\",\" close=\")\">#{id}</foreach> "
|
||||||
|
+ "GROUP BY work_id"
|
||||||
|
+ "</script>")
|
||||||
|
List<UgcWorkPageCountRow> countPagesGroupedByWorkIds(@Param("workIds") List<Long> workIds);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -551,6 +551,8 @@ export interface ContestJudge {
|
|||||||
validState?: number;
|
validState?: number;
|
||||||
/** 隐式平台评委(未写入关联表时由后端追加) */
|
/** 隐式平台评委(未写入关联表时由后端追加) */
|
||||||
isPlatform?: boolean;
|
isPlatform?: boolean;
|
||||||
|
/** 作品分配抽屉:历史隐式池分配回显(未在活动评委表中显式配置) */
|
||||||
|
legacyImplicit?: boolean;
|
||||||
judgeName?: string;
|
judgeName?: string;
|
||||||
judgeUsername?: string;
|
judgeUsername?: string;
|
||||||
assignedCount?: number;
|
assignedCount?: number;
|
||||||
@ -591,7 +593,10 @@ export interface ContestJudgesForContestResponse {
|
|||||||
implicitPool: ContestJudge[];
|
implicitPool: ContestJudge[];
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 作品分配等场景:合并为可选评委池(assigned ∪ implicitPool) */
|
/**
|
||||||
|
* 合并 assigned 与 implicitPool。作品管理「分配评委」应仅使用 `assigned`,勿用本函数。
|
||||||
|
* 添加评委抽屉等场景如需「全平台可选」再按需合并。
|
||||||
|
*/
|
||||||
export function flattenContestJudgePool(
|
export function flattenContestJudgePool(
|
||||||
r: ContestJudgesForContestResponse,
|
r: ContestJudgesForContestResponse,
|
||||||
): ContestJudge[] {
|
): ContestJudge[] {
|
||||||
|
|||||||
@ -74,7 +74,7 @@ export interface JudgeListResponse {
|
|||||||
pageSize: number;
|
pageSize: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取评委列表
|
// 获取评委列表(评委管理页可不传 status 以查看全部;添加评委/选择评委等场景传 status: 'enabled')
|
||||||
export async function getJudgesList(
|
export async function getJudgesList(
|
||||||
params: QueryJudgeParams
|
params: QueryJudgeParams
|
||||||
): Promise<JudgeListResponse> {
|
): Promise<JudgeListResponse> {
|
||||||
|
|||||||
@ -195,6 +195,8 @@ const loadJudges = async () => {
|
|||||||
pageSize: judgePagination.pageSize,
|
pageSize: judgePagination.pageSize,
|
||||||
nickname: searchParams.nickname || undefined,
|
nickname: searchParams.nickname || undefined,
|
||||||
organization: searchParams.organization || undefined,
|
organization: searchParams.organization || undefined,
|
||||||
|
/** 仅可选择启用中的评委,停用账号不展示 */
|
||||||
|
status: "enabled",
|
||||||
}
|
}
|
||||||
const res = await judgesManagementApi.getList(params)
|
const res = await judgesManagementApi.getList(params)
|
||||||
judgeList.value = res.list
|
judgeList.value = res.list
|
||||||
|
|||||||
@ -464,6 +464,7 @@ const fetchJudgeList = async () => {
|
|||||||
page: judgePagination.current,
|
page: judgePagination.current,
|
||||||
pageSize: judgePagination.pageSize,
|
pageSize: judgePagination.pageSize,
|
||||||
nickname: judgeSearchParams.nickname || undefined,
|
nickname: judgeSearchParams.nickname || undefined,
|
||||||
|
status: "enabled",
|
||||||
})
|
})
|
||||||
judgeList.value = response.list
|
judgeList.value = response.list
|
||||||
judgePagination.total = response.total
|
judgePagination.total = response.total
|
||||||
|
|||||||
@ -169,17 +169,28 @@
|
|||||||
</a-form>
|
</a-form>
|
||||||
</a-card>
|
</a-card>
|
||||||
|
|
||||||
<!-- 全部评委列表 -->
|
<a-alert
|
||||||
|
v-if="!judgeListLoading && judgeListSource.length === 0"
|
||||||
|
type="info"
|
||||||
|
show-icon
|
||||||
|
class="mb-4"
|
||||||
|
message="请先在活动评委管理中为本活动添加评委"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- 本活动评委列表(仅活动显式配置的评委) -->
|
||||||
<a-card class="mb-4" size="small">
|
<a-card class="mb-4" size="small">
|
||||||
<template #title>全部评委</template>
|
<template #title>本活动评委</template>
|
||||||
<a-table :columns="judgeColumns" :data-source="judgeList" :loading="judgeListLoading"
|
<a-table :columns="judgeColumns" :data-source="judgeTablePageData" :loading="judgeListLoading"
|
||||||
:pagination="judgePagination" :row-selection="{
|
:pagination="judgePaginationComputed" :row-selection="{
|
||||||
selectedRowKeys: selectedJudgeKeys,
|
selectedRowKeys: selectedJudgeKeys,
|
||||||
onChange: handleJudgeSelectionChange,
|
onChange: handleJudgeSelectionChange,
|
||||||
}" row-key="judgeId" size="small" @change="handleJudgeTableChange">
|
}" row-key="judgeId" size="small" @change="handleJudgeTableChange">
|
||||||
<template #bodyCell="{ column, record }">
|
<template #bodyCell="{ column, record }">
|
||||||
<template v-if="column.key === 'judgeName'">
|
<template v-if="column.key === 'judgeName'">
|
||||||
{{ record.judgeName || record.judgeUsername || "-" }}
|
<a-space>
|
||||||
|
<span>{{ record.judgeName || record.judgeUsername || "-" }}</span>
|
||||||
|
<a-tag v-if="record.legacyImplicit" color="orange">历史分配</a-tag>
|
||||||
|
</a-space>
|
||||||
</template>
|
</template>
|
||||||
<template v-else-if="column.key === 'phone'">
|
<template v-else-if="column.key === 'phone'">
|
||||||
{{ record.phone || "-" }}
|
{{ record.phone || "-" }}
|
||||||
@ -204,7 +215,10 @@
|
|||||||
<a-list-item>
|
<a-list-item>
|
||||||
<a-list-item-meta>
|
<a-list-item-meta>
|
||||||
<template #title>
|
<template #title>
|
||||||
{{ item.judgeName || item.judgeUsername || "-" }}
|
<a-space>
|
||||||
|
<span>{{ item.judgeName || item.judgeUsername || "-" }}</span>
|
||||||
|
<a-tag v-if="item.legacyImplicit" color="orange">历史分配</a-tag>
|
||||||
|
</a-space>
|
||||||
</template>
|
</template>
|
||||||
<template #description>
|
<template #description>
|
||||||
{{ item.organization || item.tenantName || "-" }}
|
{{ item.organization || item.tenantName || "-" }}
|
||||||
@ -259,7 +273,6 @@ import {
|
|||||||
worksApi,
|
worksApi,
|
||||||
reviewsApi,
|
reviewsApi,
|
||||||
judgesApi,
|
judgesApi,
|
||||||
flattenContestJudgePool,
|
|
||||||
type ContestWork,
|
type ContestWork,
|
||||||
type ContestJudge,
|
type ContestJudge,
|
||||||
} from "@/api/contests"
|
} from "@/api/contests"
|
||||||
@ -354,13 +367,12 @@ const assignLoading = ref(false)
|
|||||||
const currentAssignWork = ref<ContestWork | null>(null)
|
const currentAssignWork = ref<ContestWork | null>(null)
|
||||||
const isBatchAssign = ref(false)
|
const isBatchAssign = ref(false)
|
||||||
|
|
||||||
// 评委列表
|
// 评委列表(仅活动显式评委 + 单条分配时历史隐式池回显)
|
||||||
const judgeList = ref<ContestJudge[]>([])
|
const judgeListSource = ref<ContestJudge[]>([])
|
||||||
const judgeListLoading = ref(false)
|
const judgeListLoading = ref(false)
|
||||||
const judgePagination = reactive({
|
const judgePagination = reactive({
|
||||||
current: 1,
|
current: 1,
|
||||||
pageSize: 10,
|
pageSize: 10,
|
||||||
total: 0,
|
|
||||||
})
|
})
|
||||||
const judgeSearchParams = reactive({
|
const judgeSearchParams = reactive({
|
||||||
nickname: "",
|
nickname: "",
|
||||||
@ -369,23 +381,77 @@ const judgeSearchParams = reactive({
|
|||||||
const selectedJudgeKeys = ref<number[]>([])
|
const selectedJudgeKeys = ref<number[]>([])
|
||||||
const selectedJudgeRows = ref<ContestJudge[]>([])
|
const selectedJudgeRows = ref<ContestJudge[]>([])
|
||||||
|
|
||||||
|
/** 将作品上已分配但不在活动显式名单中的评委并入列表,避免误删历史分配 */
|
||||||
|
function mergeOrphanJudges(record: ContestWork, base: ContestJudge[]): ContestJudge[] {
|
||||||
|
const seen = new Set(base.map((j) => j.judgeId))
|
||||||
|
const extra: ContestJudge[] = []
|
||||||
|
for (const a of record.assignments || []) {
|
||||||
|
if (!seen.has(a.judgeId)) {
|
||||||
|
seen.add(a.judgeId)
|
||||||
|
extra.push({
|
||||||
|
contestId,
|
||||||
|
judgeId: a.judgeId,
|
||||||
|
judgeName: a.judge?.nickname || a.judge?.username,
|
||||||
|
judgeUsername: a.judge?.username,
|
||||||
|
assignedCount: 0,
|
||||||
|
legacyImplicit: true,
|
||||||
|
} as ContestJudge)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return [...base, ...extra]
|
||||||
|
}
|
||||||
|
|
||||||
|
const judgeFiltered = computed(() => {
|
||||||
|
const nick = (judgeSearchParams.nickname || "").trim().toLowerCase()
|
||||||
|
const tenant = (judgeSearchParams.tenantName || "").trim().toLowerCase()
|
||||||
|
let list = judgeListSource.value
|
||||||
|
if (nick) {
|
||||||
|
list = list.filter(
|
||||||
|
(j) =>
|
||||||
|
(j.judgeName || "").toLowerCase().includes(nick) ||
|
||||||
|
(j.judgeUsername || "").toLowerCase().includes(nick),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (tenant) {
|
||||||
|
list = list.filter(
|
||||||
|
(j) =>
|
||||||
|
(j.organization || "").toLowerCase().includes(tenant) ||
|
||||||
|
(j.tenantName || "").toLowerCase().includes(tenant),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return list
|
||||||
|
})
|
||||||
|
|
||||||
|
const judgeTablePageData = computed(() => {
|
||||||
|
const start = (judgePagination.current - 1) * judgePagination.pageSize
|
||||||
|
return judgeFiltered.value.slice(start, start + judgePagination.pageSize)
|
||||||
|
})
|
||||||
|
|
||||||
|
const judgePaginationComputed = computed(() => ({
|
||||||
|
current: judgePagination.current,
|
||||||
|
pageSize: judgePagination.pageSize,
|
||||||
|
total: judgeFiltered.value.length,
|
||||||
|
showSizeChanger: true,
|
||||||
|
showTotal: (total: number) => `共 ${total} 条`,
|
||||||
|
}))
|
||||||
|
|
||||||
// 评委选择变化(行键与提交均使用 judgeId)
|
// 评委选择变化(行键与提交均使用 judgeId)
|
||||||
const handleJudgeSelectionChange = (selectedKeys: number[]) => {
|
const handleJudgeSelectionChange = (selectedKeys: number[]) => {
|
||||||
const newSelectedIds = selectedKeys.filter(
|
const newSelectedIds = selectedKeys.filter(
|
||||||
(id) => !selectedJudgeKeys.value.includes(id)
|
(id) => !selectedJudgeKeys.value.includes(id),
|
||||||
)
|
)
|
||||||
const removedIds = selectedJudgeKeys.value.filter(
|
const removedIds = selectedJudgeKeys.value.filter(
|
||||||
(id) => !selectedKeys.includes(id)
|
(id) => !selectedKeys.includes(id),
|
||||||
)
|
)
|
||||||
|
|
||||||
selectedJudgeKeys.value = selectedKeys
|
selectedJudgeKeys.value = selectedKeys
|
||||||
|
|
||||||
selectedJudgeRows.value = selectedJudgeRows.value.filter(
|
selectedJudgeRows.value = selectedJudgeRows.value.filter(
|
||||||
(judge) => !removedIds.includes(judge.judgeId)
|
(judge) => !removedIds.includes(judge.judgeId),
|
||||||
)
|
)
|
||||||
|
|
||||||
const newSelectedJudges = judgeList.value.filter((judge) =>
|
const newSelectedJudges = judgeFiltered.value.filter((judge) =>
|
||||||
newSelectedIds.includes(judge.judgeId)
|
newSelectedIds.includes(judge.judgeId),
|
||||||
)
|
)
|
||||||
selectedJudgeRows.value = [...selectedJudgeRows.value, ...newSelectedJudges]
|
selectedJudgeRows.value = [...selectedJudgeRows.value, ...newSelectedJudges]
|
||||||
}
|
}
|
||||||
@ -462,14 +528,18 @@ const fetchList = async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取活动评委列表(显式 + 隐式平台池)
|
// 获取活动评委列表(仅活动显式配置;单条分配时合并历史隐式池分配以便回显)
|
||||||
const fetchJudgeList = async () => {
|
const fetchJudgeList = async () => {
|
||||||
judgeListLoading.value = true
|
judgeListLoading.value = true
|
||||||
try {
|
try {
|
||||||
const response = await judgesApi.getList(contestId)
|
const response = await judgesApi.getList(contestId)
|
||||||
const pool = flattenContestJudgePool(response)
|
const assigned = Array.isArray(response.assigned) ? response.assigned : []
|
||||||
judgeList.value = pool
|
let merged = assigned
|
||||||
judgePagination.total = pool.length
|
if (!isBatchAssign.value && currentAssignWork.value) {
|
||||||
|
merged = mergeOrphanJudges(currentAssignWork.value, assigned)
|
||||||
|
}
|
||||||
|
judgeListSource.value = merged
|
||||||
|
judgePagination.current = 1
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
message.error("获取评委列表失败")
|
message.error("获取评委列表失败")
|
||||||
} finally {
|
} finally {
|
||||||
@ -525,8 +595,8 @@ const handleAssignJudge = async (record: ContestWork) => {
|
|||||||
// 回显已分配的评委
|
// 回显已分配的评委
|
||||||
if (record.assignments && record.assignments.length > 0) {
|
if (record.assignments && record.assignments.length > 0) {
|
||||||
const assignedJudgeUserIds = record.assignments.map((a) => a.judgeId)
|
const assignedJudgeUserIds = record.assignments.map((a) => a.judgeId)
|
||||||
const matchedJudges = judgeList.value.filter((judge) =>
|
const matchedJudges = judgeListSource.value.filter((judge) =>
|
||||||
assignedJudgeUserIds.includes(judge.judgeId)
|
assignedJudgeUserIds.includes(judge.judgeId),
|
||||||
)
|
)
|
||||||
selectedJudgeKeys.value = matchedJudges.map((j) => j.judgeId)
|
selectedJudgeKeys.value = matchedJudges.map((j) => j.judgeId)
|
||||||
selectedJudgeRows.value = matchedJudges
|
selectedJudgeRows.value = matchedJudges
|
||||||
@ -539,6 +609,7 @@ const handleBatchAssign = () => {
|
|||||||
message.warning("请先选择作品")
|
message.warning("请先选择作品")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
currentAssignWork.value = null
|
||||||
isBatchAssign.value = true
|
isBatchAssign.value = true
|
||||||
selectedJudgeKeys.value = []
|
selectedJudgeKeys.value = []
|
||||||
selectedJudgeRows.value = []
|
selectedJudgeRows.value = []
|
||||||
@ -568,11 +639,10 @@ const handleResetJudgeSearch = () => {
|
|||||||
fetchJudgeList()
|
fetchJudgeList()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 评委表格分页变化
|
// 评委表格分页变化(客户端分页)
|
||||||
const handleJudgeTableChange = (pag: any) => {
|
const handleJudgeTableChange = (pag: any) => {
|
||||||
judgePagination.current = pag.current
|
judgePagination.current = pag.current
|
||||||
judgePagination.pageSize = pag.pageSize
|
judgePagination.pageSize = pag.pageSize
|
||||||
fetchJudgeList()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 确认分配评委(可与列表同步:移除的评委在后端删除分配记录)
|
// 确认分配评委(可与列表同步:移除的评委在后端删除分配记录)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user