feat: 作品编号 workNo 生成与回填(公开端/作业/Flyway V8)及评审与前端展示
Made-with: Cursor
This commit is contained in:
parent
430dce1f09
commit
36cd01c585
@ -11,6 +11,11 @@ import java.util.Map;
|
|||||||
|
|
||||||
public interface IContestWorkService extends IService<BizContestWork> {
|
public interface IContestWorkService extends IService<BizContestWork> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 为指定赛事生成下一个作品编号(与 {@link #submitWork} 所用规则一致:W{contestId}-{序号})。
|
||||||
|
*/
|
||||||
|
String nextContestWorkNo(Long contestId);
|
||||||
|
|
||||||
Map<String, Object> submitWork(SubmitWorkDto dto, Long tenantId, Long submitterId);
|
Map<String, Object> submitWork(SubmitWorkDto dto, Long tenantId, Long submitterId);
|
||||||
|
|
||||||
PageResult<Map<String, Object>> findAll(QueryWorkDto dto, Long tenantId, boolean isSuperTenant);
|
PageResult<Map<String, Object>> findAll(QueryWorkDto dto, Long tenantId, boolean isSuperTenant);
|
||||||
|
|||||||
@ -110,7 +110,7 @@ public class ContestWorkServiceImpl extends ServiceImpl<ContestWorkMapper, BizCo
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 生成作品编号
|
// 生成作品编号
|
||||||
String workNo = generateWorkNo(contestId);
|
String workNo = nextContestWorkNo(contestId);
|
||||||
|
|
||||||
// 创建作品
|
// 创建作品
|
||||||
BizContestWork work = new BizContestWork();
|
BizContestWork work = new BizContestWork();
|
||||||
@ -658,7 +658,8 @@ public class ContestWorkServiceImpl extends ServiceImpl<ContestWorkMapper, BizCo
|
|||||||
return isStart ? date.atStartOfDay() : date.atTime(23, 59, 59);
|
return isStart ? date.atStartOfDay() : date.atTime(23, 59, 59);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String generateWorkNo(Long contestId) {
|
@Override
|
||||||
|
public String nextContestWorkNo(Long contestId) {
|
||||||
LambdaQueryWrapper<BizContestWork> wrapper = new LambdaQueryWrapper<>();
|
LambdaQueryWrapper<BizContestWork> wrapper = new LambdaQueryWrapper<>();
|
||||||
wrapper.eq(BizContestWork::getContestId, contestId);
|
wrapper.eq(BizContestWork::getContestId, contestId);
|
||||||
long count = count(wrapper);
|
long count = count(wrapper);
|
||||||
|
|||||||
@ -55,10 +55,17 @@ public class HomeworkSubmissionServiceImpl extends ServiceImpl<HomeworkSubmissio
|
|||||||
throw BusinessException.of(ErrorCode.CONFLICT, "您已提交过该作业");
|
throw BusinessException.of(ErrorCode.CONFLICT, "您已提交过该作业");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LambdaQueryWrapper<BizHomeworkSubmission> seqWrapper = new LambdaQueryWrapper<>();
|
||||||
|
seqWrapper.eq(BizHomeworkSubmission::getHomeworkId, dto.getHomeworkId());
|
||||||
|
seqWrapper.eq(BizHomeworkSubmission::getValidState, 1);
|
||||||
|
long homeworkSubmitCount = count(seqWrapper);
|
||||||
|
String workNo = "H" + dto.getHomeworkId() + "-" + (homeworkSubmitCount + 1);
|
||||||
|
|
||||||
BizHomeworkSubmission entity = new BizHomeworkSubmission();
|
BizHomeworkSubmission entity = new BizHomeworkSubmission();
|
||||||
entity.setTenantId(tenantId);
|
entity.setTenantId(tenantId);
|
||||||
entity.setHomeworkId(dto.getHomeworkId());
|
entity.setHomeworkId(dto.getHomeworkId());
|
||||||
entity.setStudentId(studentId);
|
entity.setStudentId(studentId);
|
||||||
|
entity.setWorkNo(workNo);
|
||||||
entity.setWorkName(dto.getWorkName());
|
entity.setWorkName(dto.getWorkName());
|
||||||
entity.setWorkDescription(dto.getWorkDescription());
|
entity.setWorkDescription(dto.getWorkDescription());
|
||||||
entity.setFiles(dto.getFiles());
|
entity.setFiles(dto.getFiles());
|
||||||
|
|||||||
@ -7,8 +7,10 @@ import com.competition.common.enums.PublishStatus;
|
|||||||
import com.competition.common.exception.BusinessException;
|
import com.competition.common.exception.BusinessException;
|
||||||
import com.competition.common.result.PageResult;
|
import com.competition.common.result.PageResult;
|
||||||
import com.competition.modules.biz.contest.entity.BizContest;
|
import com.competition.modules.biz.contest.entity.BizContest;
|
||||||
|
import com.competition.modules.biz.contest.entity.BizContestRegistration;
|
||||||
import com.competition.modules.biz.contest.entity.BizContestWork;
|
import com.competition.modules.biz.contest.entity.BizContestWork;
|
||||||
import com.competition.modules.biz.contest.mapper.ContestMapper;
|
import com.competition.modules.biz.contest.mapper.ContestMapper;
|
||||||
|
import com.competition.modules.biz.contest.mapper.ContestRegistrationMapper;
|
||||||
import com.competition.modules.biz.contest.mapper.ContestWorkMapper;
|
import com.competition.modules.biz.contest.mapper.ContestWorkMapper;
|
||||||
import com.competition.modules.biz.review.dto.AutoSetAwardsDto;
|
import com.competition.modules.biz.review.dto.AutoSetAwardsDto;
|
||||||
import com.competition.modules.biz.review.dto.BatchSetAwardsDto;
|
import com.competition.modules.biz.review.dto.BatchSetAwardsDto;
|
||||||
@ -38,6 +40,7 @@ public class ContestResultServiceImpl implements IContestResultService {
|
|||||||
|
|
||||||
private final ContestWorkMapper workMapper;
|
private final ContestWorkMapper workMapper;
|
||||||
private final ContestMapper contestMapper;
|
private final ContestMapper contestMapper;
|
||||||
|
private final ContestRegistrationMapper contestRegistrationMapper;
|
||||||
private final ContestReviewRuleMapper reviewRuleMapper;
|
private final ContestReviewRuleMapper reviewRuleMapper;
|
||||||
private final ContestWorkScoreMapper scoreMapper;
|
private final ContestWorkScoreMapper scoreMapper;
|
||||||
private final ContestJudgeMapper judgeMapper;
|
private final ContestJudgeMapper judgeMapper;
|
||||||
@ -309,7 +312,19 @@ public class ContestResultServiceImpl implements IContestResultService {
|
|||||||
wrapper.like(BizContestWork::getWorkNo, workNo);
|
wrapper.like(BizContestWork::getWorkNo, workNo);
|
||||||
}
|
}
|
||||||
if (StringUtils.hasText(accountNo)) {
|
if (StringUtils.hasText(accountNo)) {
|
||||||
wrapper.like(BizContestWork::getSubmitterAccountNo, accountNo);
|
LambdaQueryWrapper<BizContestRegistration> regQw = new LambdaQueryWrapper<>();
|
||||||
|
regQw.eq(BizContestRegistration::getContestId, contestId);
|
||||||
|
regQw.eq(BizContestRegistration::getValidState, 1);
|
||||||
|
regQw.like(BizContestRegistration::getAccountNo, accountNo);
|
||||||
|
List<Long> matchRegIds = contestRegistrationMapper.selectList(regQw).stream()
|
||||||
|
.map(BizContestRegistration::getId)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
wrapper.and(q -> {
|
||||||
|
q.like(BizContestWork::getSubmitterAccountNo, accountNo);
|
||||||
|
if (!matchRegIds.isEmpty()) {
|
||||||
|
q.or().in(BizContestWork::getRegistrationId, matchRegIds);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
wrapper.orderByDesc(BizContestWork::getFinalScore);
|
wrapper.orderByDesc(BizContestWork::getFinalScore);
|
||||||
@ -317,8 +332,21 @@ public class ContestResultServiceImpl implements IContestResultService {
|
|||||||
Page<BizContestWork> pageObj = new Page<>(page, pageSize);
|
Page<BizContestWork> pageObj = new Page<>(page, pageSize);
|
||||||
Page<BizContestWork> result = workMapper.selectPage(pageObj, wrapper);
|
Page<BizContestWork> result = workMapper.selectPage(pageObj, wrapper);
|
||||||
|
|
||||||
List<Map<String, Object>> voList = result.getRecords().stream().map(w -> {
|
List<BizContestWork> records = result.getRecords();
|
||||||
|
Set<Long> registrationIds = records.stream()
|
||||||
|
.map(BizContestWork::getRegistrationId)
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
Map<Long, BizContestRegistration> registrationById = new HashMap<>();
|
||||||
|
if (!registrationIds.isEmpty()) {
|
||||||
|
for (BizContestRegistration reg : contestRegistrationMapper.selectBatchIds(registrationIds)) {
|
||||||
|
registrationById.put(reg.getId(), reg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Map<String, Object>> voList = records.stream().map(w -> {
|
||||||
Map<String, Object> map = new LinkedHashMap<>();
|
Map<String, Object> map = new LinkedHashMap<>();
|
||||||
|
map.put("id", w.getId());
|
||||||
map.put("workId", w.getId());
|
map.put("workId", w.getId());
|
||||||
map.put("workNo", w.getWorkNo());
|
map.put("workNo", w.getWorkNo());
|
||||||
map.put("title", w.getTitle());
|
map.put("title", w.getTitle());
|
||||||
@ -330,6 +358,10 @@ public class ContestResultServiceImpl implements IContestResultService {
|
|||||||
map.put("awardName", w.getAwardName());
|
map.put("awardName", w.getAwardName());
|
||||||
map.put("certificateUrl", w.getCertificateUrl());
|
map.put("certificateUrl", w.getCertificateUrl());
|
||||||
map.put("submitTime", w.getSubmitTime());
|
map.put("submitTime", w.getSubmitTime());
|
||||||
|
BizContestRegistration reg = w.getRegistrationId() != null
|
||||||
|
? registrationById.get(w.getRegistrationId())
|
||||||
|
: null;
|
||||||
|
map.put("registration", reg != null ? registrationToNestedMap(reg) : null);
|
||||||
return map;
|
return map;
|
||||||
}).collect(Collectors.toList());
|
}).collect(Collectors.toList());
|
||||||
|
|
||||||
@ -397,20 +429,63 @@ public class ContestResultServiceImpl implements IContestResultService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BizContest contest = contestMapper.selectById(contestId);
|
||||||
|
if (contest == null) {
|
||||||
|
throw BusinessException.of(ErrorCode.NOT_FOUND, "赛事不存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, Object> contestMap = new LinkedHashMap<>();
|
||||||
|
contestMap.put("id", contest.getId());
|
||||||
|
contestMap.put("contestName", contest.getContestName());
|
||||||
|
contestMap.put("resultState", contest.getResultState());
|
||||||
|
contestMap.put("resultPublishTime", contest.getResultPublishTime());
|
||||||
|
contestMap.put("contestType", contest.getContestType());
|
||||||
|
|
||||||
|
long unscoredWorks = Math.max(0, totalWorks - scoredWorks);
|
||||||
|
|
||||||
|
Map<String, Object> summaryMap = new LinkedHashMap<>();
|
||||||
|
summaryMap.put("totalWorks", totalWorks);
|
||||||
|
summaryMap.put("scoredWorks", scoredWorks);
|
||||||
|
summaryMap.put("rankedWorks", rankedWorks);
|
||||||
|
summaryMap.put("awardedWorks", awardedWorks);
|
||||||
|
summaryMap.put("unscoredWorks", unscoredWorks);
|
||||||
|
|
||||||
|
Map<String, Object> scoreStats = new LinkedHashMap<>();
|
||||||
|
scoreStats.put("avgScore", scoredList.isEmpty() ? null : avgScore.toPlainString());
|
||||||
|
scoreStats.put("maxScore", scoredList.isEmpty() ? null : maxScore.toPlainString());
|
||||||
|
scoreStats.put("minScore", scoredList.isEmpty() ? null : minScore.toPlainString());
|
||||||
|
|
||||||
Map<String, Object> result = new LinkedHashMap<>();
|
Map<String, Object> result = new LinkedHashMap<>();
|
||||||
result.put("totalWorks", totalWorks);
|
result.put("contest", contestMap);
|
||||||
result.put("scoredWorks", scoredWorks);
|
result.put("summary", summaryMap);
|
||||||
result.put("rankedWorks", rankedWorks);
|
result.put("scoreStats", scoreStats);
|
||||||
result.put("awardedWorks", awardedWorks);
|
|
||||||
result.put("avgScore", avgScore);
|
|
||||||
result.put("maxScore", maxScore);
|
|
||||||
result.put("minScore", minScore);
|
|
||||||
result.put("awardDistribution", awardDistribution);
|
result.put("awardDistribution", awardDistribution);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ====== 私有辅助方法 ======
|
// ====== 私有辅助方法 ======
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 与前端成果列表/作品管理一致:registration.user / registration.team 结构
|
||||||
|
*/
|
||||||
|
private Map<String, Object> registrationToNestedMap(BizContestRegistration reg) {
|
||||||
|
Map<String, Object> regMap = new LinkedHashMap<>();
|
||||||
|
Map<String, Object> user = new LinkedHashMap<>();
|
||||||
|
user.put("id", reg.getUserId());
|
||||||
|
user.put("username", reg.getAccountNo());
|
||||||
|
user.put("nickname", reg.getAccountName());
|
||||||
|
regMap.put("user", user);
|
||||||
|
Map<String, Object> team = null;
|
||||||
|
if ("team".equals(reg.getRegistrationType())
|
||||||
|
&& (reg.getTeamId() != null || StringUtils.hasText(reg.getTeamName()))) {
|
||||||
|
team = new LinkedHashMap<>();
|
||||||
|
team.put("id", reg.getTeamId());
|
||||||
|
team.put("teamName", reg.getTeamName());
|
||||||
|
}
|
||||||
|
regMap.put("team", team);
|
||||||
|
return regMap;
|
||||||
|
}
|
||||||
|
|
||||||
private BigDecimal doCalculate(List<BizContestWorkScore> scores, String calculationRule, Map<Long, BigDecimal> weightMap) {
|
private BigDecimal doCalculate(List<BizContestWorkScore> scores, String calculationRule, Map<Long, BigDecimal> weightMap) {
|
||||||
List<BigDecimal> scoreValues = scores.stream()
|
List<BigDecimal> scoreValues = scores.stream()
|
||||||
.map(BizContestWorkScore::getTotalScore)
|
.map(BizContestWorkScore::getTotalScore)
|
||||||
|
|||||||
@ -621,12 +621,40 @@ public class ContestReviewServiceImpl implements IContestReviewService {
|
|||||||
|
|
||||||
List<BizContestWorkScore> scores = scoreMapper.selectList(wrapper);
|
List<BizContestWorkScore> scores = scoreMapper.selectList(wrapper);
|
||||||
|
|
||||||
|
Set<Long> judgeIds = scores.stream()
|
||||||
|
.map(BizContestWorkScore::getJudgeId)
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
Map<Long, SysUser> judgeUserById = new HashMap<>();
|
||||||
|
if (!judgeIds.isEmpty()) {
|
||||||
|
for (SysUser u : sysUserMapper.selectBatchIds(judgeIds)) {
|
||||||
|
if (u != null && u.getId() != null) {
|
||||||
|
judgeUserById.put(u.getId(), u);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return scores.stream().map(s -> {
|
return scores.stream().map(s -> {
|
||||||
Map<String, Object> map = new LinkedHashMap<>();
|
Map<String, Object> map = new LinkedHashMap<>();
|
||||||
|
map.put("id", s.getId());
|
||||||
map.put("scoreId", s.getId());
|
map.put("scoreId", s.getId());
|
||||||
map.put("assignmentId", s.getAssignmentId());
|
map.put("assignmentId", s.getAssignmentId());
|
||||||
map.put("judgeId", s.getJudgeId());
|
map.put("judgeId", s.getJudgeId());
|
||||||
map.put("judgeName", s.getJudgeName());
|
SysUser judgeUser = s.getJudgeId() != null ? judgeUserById.get(s.getJudgeId()) : null;
|
||||||
|
String judgeName = s.getJudgeName();
|
||||||
|
if (!StringUtils.hasText(judgeName) && judgeUser != null) {
|
||||||
|
judgeName = StringUtils.hasText(judgeUser.getNickname())
|
||||||
|
? judgeUser.getNickname()
|
||||||
|
: judgeUser.getUsername();
|
||||||
|
}
|
||||||
|
map.put("judgeName", judgeName);
|
||||||
|
if (judgeUser != null) {
|
||||||
|
Map<String, Object> judge = new LinkedHashMap<>();
|
||||||
|
judge.put("id", judgeUser.getId());
|
||||||
|
judge.put("username", judgeUser.getUsername());
|
||||||
|
judge.put("nickname", judgeUser.getNickname());
|
||||||
|
map.put("judge", judge);
|
||||||
|
}
|
||||||
map.put("dimensionScores", s.getDimensionScores());
|
map.put("dimensionScores", s.getDimensionScores());
|
||||||
map.put("totalScore", s.getTotalScore());
|
map.put("totalScore", s.getTotalScore());
|
||||||
map.put("comments", s.getComments());
|
map.put("comments", s.getComments());
|
||||||
|
|||||||
@ -13,6 +13,7 @@ import com.competition.modules.biz.contest.entity.BizContestWork;
|
|||||||
import com.competition.modules.biz.contest.mapper.ContestMapper;
|
import com.competition.modules.biz.contest.mapper.ContestMapper;
|
||||||
import com.competition.modules.biz.contest.mapper.ContestRegistrationMapper;
|
import com.competition.modules.biz.contest.mapper.ContestRegistrationMapper;
|
||||||
import com.competition.modules.biz.contest.mapper.ContestWorkMapper;
|
import com.competition.modules.biz.contest.mapper.ContestWorkMapper;
|
||||||
|
import com.competition.modules.biz.contest.service.IContestWorkService;
|
||||||
import com.competition.modules.pub.dto.PublicRegisterActivityDto;
|
import com.competition.modules.pub.dto.PublicRegisterActivityDto;
|
||||||
import com.competition.modules.ugc.entity.UgcWork;
|
import com.competition.modules.ugc.entity.UgcWork;
|
||||||
import com.competition.modules.ugc.entity.UgcWorkPage;
|
import com.competition.modules.ugc.entity.UgcWorkPage;
|
||||||
@ -37,6 +38,7 @@ public class PublicActivityService {
|
|||||||
private final ContestMapper contestMapper;
|
private final ContestMapper contestMapper;
|
||||||
private final ContestRegistrationMapper contestRegistrationMapper;
|
private final ContestRegistrationMapper contestRegistrationMapper;
|
||||||
private final ContestWorkMapper contestWorkMapper;
|
private final ContestWorkMapper contestWorkMapper;
|
||||||
|
private final IContestWorkService contestWorkService;
|
||||||
private final UserChildMapper userChildMapper;
|
private final UserChildMapper userChildMapper;
|
||||||
private final UgcWorkMapper ugcWorkMapper;
|
private final UgcWorkMapper ugcWorkMapper;
|
||||||
private final UgcWorkPageMapper ugcWorkPageMapper;
|
private final UgcWorkPageMapper ugcWorkPageMapper;
|
||||||
@ -378,6 +380,8 @@ public class PublicActivityService {
|
|||||||
work.setSubmitTime(LocalDateTime.now());
|
work.setSubmitTime(LocalDateTime.now());
|
||||||
work.setVersion(nextVersion);
|
work.setVersion(nextVersion);
|
||||||
work.setIsLatest(true);
|
work.setIsLatest(true);
|
||||||
|
work.setWorkNo(contestWorkService.nextContestWorkNo(contestId));
|
||||||
|
work.setSubmitterAccountNo(reg.getAccountNo());
|
||||||
|
|
||||||
// 从作品库选择作品提交(快照复制)
|
// 从作品库选择作品提交(快照复制)
|
||||||
if (dto.get("userWorkId") != null) {
|
if (dto.get("userWorkId") != null) {
|
||||||
|
|||||||
@ -0,0 +1,9 @@
|
|||||||
|
-- 回填历史数据中空的作品编号(与运行时生成形态一致:W{contestId}-* / H{homeworkId}-*)
|
||||||
|
|
||||||
|
UPDATE t_biz_contest_work
|
||||||
|
SET work_no = CONCAT('W', contest_id, '-', id)
|
||||||
|
WHERE work_no IS NULL OR TRIM(work_no) = '';
|
||||||
|
|
||||||
|
UPDATE t_biz_homework_submission
|
||||||
|
SET work_no = CONCAT('H', homework_id, '-', id)
|
||||||
|
WHERE work_no IS NULL OR TRIM(work_no) = '';
|
||||||
@ -1374,6 +1374,8 @@ export interface ContestResult {
|
|||||||
id: number;
|
id: number;
|
||||||
workNo: string | null;
|
workNo: string | null;
|
||||||
title: string;
|
title: string;
|
||||||
|
/** 作品上冗余的提交账号,与报名账号一致时优先展示 */
|
||||||
|
submitterAccountNo?: string | null;
|
||||||
finalScore: number | null;
|
finalScore: number | null;
|
||||||
rank: number | null;
|
rank: number | null;
|
||||||
awardLevel: string | null;
|
awardLevel: string | null;
|
||||||
@ -1404,6 +1406,7 @@ export interface ResultsSummary {
|
|||||||
contest: {
|
contest: {
|
||||||
id: number;
|
id: number;
|
||||||
contestName: string;
|
contestName: string;
|
||||||
|
contestType?: "individual" | "team";
|
||||||
resultState: string;
|
resultState: string;
|
||||||
resultPublishTime: string | null;
|
resultPublishTime: string | null;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -84,17 +84,19 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 评审记录 -->
|
<!-- 评审记录:纵向卡片,避免多评委时 Tab 横向溢出 -->
|
||||||
<div class="section">
|
<div class="section section-review">
|
||||||
<div class="section-title">评审记录</div>
|
<div class="section-title">评审记录</div>
|
||||||
<div class="review-records">
|
<div class="review-records">
|
||||||
<template v-if="reviewRecords && reviewRecords.length > 0">
|
<template v-if="reviewRecords && reviewRecords.length > 0">
|
||||||
<a-tabs v-model:activeKey="activeReviewTab">
|
<div
|
||||||
<a-tab-pane
|
v-for="(record, index) in reviewRecords"
|
||||||
v-for="record in reviewRecords"
|
:key="record.scoreId ?? record.id ?? index"
|
||||||
:key="record.id"
|
class="review-judge-block"
|
||||||
:tab="record.judge?.nickname || record.judge?.username || '评委'"
|
|
||||||
>
|
>
|
||||||
|
<div class="review-judge-header">
|
||||||
|
<span class="review-judge-title">{{ judgeDisplayName(record, index) }}</span>
|
||||||
|
</div>
|
||||||
<div class="review-card">
|
<div class="review-card">
|
||||||
<div class="review-item">
|
<div class="review-item">
|
||||||
<span class="review-label">作品评分:</span>
|
<span class="review-label">作品评分:</span>
|
||||||
@ -105,7 +107,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="review-item">
|
<div class="review-item">
|
||||||
<span class="review-label">评委老师:</span>
|
<span class="review-label">评委老师:</span>
|
||||||
<span class="review-value">{{ record.judge?.nickname || record.judge?.username || '-' }}</span>
|
<span class="review-value">{{ judgeNameText(record) || '-' }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="review-item">
|
<div class="review-item">
|
||||||
<span class="review-label">评分时间:</span>
|
<span class="review-label">评分时间:</span>
|
||||||
@ -118,8 +120,7 @@
|
|||||||
<span class="review-value">{{ record.comments }}</span>
|
<span class="review-value">{{ record.comments }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</a-tab-pane>
|
</div>
|
||||||
</a-tabs>
|
|
||||||
</template>
|
</template>
|
||||||
<a-empty v-else description="暂无评审记录" :image="false" />
|
<a-empty v-else description="暂无评审记录" :image="false" />
|
||||||
</div>
|
</div>
|
||||||
@ -161,9 +162,29 @@ const visible = ref(false)
|
|||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const workDetail = ref<ContestWork | null>(null)
|
const workDetail = ref<ContestWork | null>(null)
|
||||||
const reviewRecords = ref<any[]>([])
|
const reviewRecords = ref<any[]>([])
|
||||||
const activeReviewTab = ref<number | string>("")
|
|
||||||
const showPreviewBtn = ref(false)
|
const showPreviewBtn = ref(false)
|
||||||
|
|
||||||
|
/** 评委展示名:接口扁平字段 judgeName,或嵌套 judge(与列表接口一致) */
|
||||||
|
const judgeNameText = (record: any) => {
|
||||||
|
const flat = record?.judgeName
|
||||||
|
if (flat != null && String(flat).trim() !== '') return String(flat).trim()
|
||||||
|
return (
|
||||||
|
record?.judge?.nickname ||
|
||||||
|
record?.judge?.username ||
|
||||||
|
''
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 纵向列表标题:有姓名用姓名,否则「评委一、评委二…」 */
|
||||||
|
const judgeDisplayName = (record: any, index: number) => {
|
||||||
|
const name = judgeNameText(record)
|
||||||
|
if (name) return name
|
||||||
|
const numerals = ['一', '二', '三', '四', '五', '六', '七', '八', '九', '十']
|
||||||
|
const n = index + 1
|
||||||
|
if (n >= 1 && n <= 10) return `评委${numerals[n - 1]}`
|
||||||
|
return `评委 ${n}`
|
||||||
|
}
|
||||||
|
|
||||||
// 抽屉标题
|
// 抽屉标题
|
||||||
const drawerTitle = computed(() => {
|
const drawerTitle = computed(() => {
|
||||||
if (workDetail.value) {
|
if (workDetail.value) {
|
||||||
@ -268,9 +289,6 @@ const fetchReviewRecords = async (workId: number) => {
|
|||||||
try {
|
try {
|
||||||
const records = await reviewsApi.getWorkScores(workId)
|
const records = await reviewsApi.getWorkScores(workId)
|
||||||
reviewRecords.value = records || []
|
reviewRecords.value = records || []
|
||||||
if (records && records.length > 0) {
|
|
||||||
activeReviewTab.value = records[0].id
|
|
||||||
}
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("获取评审记录失败", error)
|
console.error("获取评审记录失败", error)
|
||||||
reviewRecords.value = []
|
reviewRecords.value = []
|
||||||
@ -354,6 +372,9 @@ watch(
|
|||||||
.work-detail-drawer {
|
.work-detail-drawer {
|
||||||
:deep(.ant-drawer-body) {
|
:deep(.ant-drawer-body) {
|
||||||
padding: 16px 24px;
|
padding: 16px 24px;
|
||||||
|
overflow-x: hidden;
|
||||||
|
max-width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -475,9 +496,31 @@ watch(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.section-review {
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.review-records {
|
.review-records {
|
||||||
:deep(.ant-tabs-nav) {
|
min-width: 0;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.review-judge-block {
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.review-judge-header {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.review-judge-title {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #1f2937;
|
||||||
|
word-break: break-all;
|
||||||
}
|
}
|
||||||
|
|
||||||
.review-card {
|
.review-card {
|
||||||
@ -486,11 +529,15 @@ watch(
|
|||||||
padding: 16px;
|
padding: 16px;
|
||||||
border-radius: 0 8px 8px 0;
|
border-radius: 0 8px 8px 0;
|
||||||
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.08);
|
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.08);
|
||||||
|
max-width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
.review-item {
|
.review-item {
|
||||||
margin-bottom: 12px;
|
margin-bottom: 12px;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
line-height: 1.6;
|
line-height: 1.6;
|
||||||
|
word-break: break-word;
|
||||||
|
overflow-wrap: anywhere;
|
||||||
|
|
||||||
&:last-child {
|
&:last-child {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
@ -498,6 +545,7 @@ watch(
|
|||||||
|
|
||||||
.review-label {
|
.review-label {
|
||||||
color: #666;
|
color: #666;
|
||||||
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.review-value {
|
.review-value {
|
||||||
|
|||||||
@ -105,10 +105,15 @@
|
|||||||
<span v-else class="text-muted">-</span>
|
<span v-else class="text-muted">-</span>
|
||||||
</template>
|
</template>
|
||||||
<template v-else-if="column.key === 'nickname'">
|
<template v-else-if="column.key === 'nickname'">
|
||||||
|
<template v-if="contestInfo?.contestType === 'team'">
|
||||||
|
{{ record.registration?.team?.teamName || record.registration?.user?.nickname || '-' }}
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
{{ record.registration?.user?.nickname || record.registration?.team?.teamName || '-' }}
|
{{ record.registration?.user?.nickname || record.registration?.team?.teamName || '-' }}
|
||||||
</template>
|
</template>
|
||||||
|
</template>
|
||||||
<template v-else-if="column.key === 'username'">
|
<template v-else-if="column.key === 'username'">
|
||||||
{{ record.registration?.user?.username || '-' }}
|
{{ record.submitterAccountNo || record.registration?.user?.username || '-' }}
|
||||||
</template>
|
</template>
|
||||||
<template v-else-if="column.key === 'action'">
|
<template v-else-if="column.key === 'action'">
|
||||||
<a-button v-if="!isSuperAdmin && contestInfo?.resultState !== 'published'" type="link" size="small" @click="openSetAward(record)">设奖</a-button>
|
<a-button v-if="!isSuperAdmin && contestInfo?.resultState !== 'published'" type="link" size="small" @click="openSetAward(record)">设奖</a-button>
|
||||||
@ -277,7 +282,9 @@ const fetchList = async () => {
|
|||||||
workNo: searchParams.workNo || undefined,
|
workNo: searchParams.workNo || undefined,
|
||||||
accountNo: searchParams.accountNo || undefined,
|
accountNo: searchParams.accountNo || undefined,
|
||||||
})
|
})
|
||||||
|
if (response.contest) {
|
||||||
contestInfo.value = response.contest
|
contestInfo.value = response.contest
|
||||||
|
}
|
||||||
let list = response.list
|
let list = response.list
|
||||||
// 前端过滤奖项
|
// 前端过滤奖项
|
||||||
if (searchParams.awardLevel) {
|
if (searchParams.awardLevel) {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user