fix:活动详情优化

This commit is contained in:
zhonghua 2026-04-03 15:28:15 +08:00
parent 4a70bc7d43
commit b3954ffcf3
4 changed files with 164 additions and 31 deletions

View File

@ -17,6 +17,7 @@ import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
@Tag(name = "赛事公告")
@ -27,6 +28,34 @@ public class ContestNoticeController {
private final IContestNoticeService noticeService;
/**
* 解析日期时间字符串兼容 ISO 格式带毫秒和 Z 时区标记
*/
private LocalDateTime parseDateTime(String dateTime) {
if (!StringUtils.hasText(dateTime)) {
return null;
}
// 尝试 ISO 格式yyyy-MM-dd'T'HH:mm:ss.SSSZ yyyy-MM-dd'T'HH:mm:ss'Z'
try {
// 处理带 Z ISO 格式
if (dateTime.endsWith("Z")) {
return LocalDateTime.parse(dateTime.substring(0, dateTime.length() - 1));
}
// 处理带毫秒的格式
if (dateTime.contains(".") && dateTime.indexOf(".") + 4 == dateTime.length()) {
return LocalDateTime.parse(dateTime.substring(0, dateTime.indexOf(".")));
}
return LocalDateTime.parse(dateTime);
} catch (Exception e) {
// 尝试空格分隔格式
try {
return LocalDateTime.parse(dateTime, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
} catch (Exception ex) {
return null;
}
}
}
@PostMapping
@RequirePermission("notice:create")
@Operation(summary = "创建公告")
@ -38,7 +67,7 @@ public class ContestNoticeController {
notice.setNoticeType(dto.getNoticeType());
notice.setPriority(dto.getPriority());
if (StringUtils.hasText(dto.getPublishTime())) {
notice.setPublishTime(LocalDateTime.parse(dto.getPublishTime()));
notice.setPublishTime(parseDateTime(dto.getPublishTime()));
}
// 设置当前租户 ID租户隔离
notice.setTenantId(SecurityUtil.getCurrentTenantId());
@ -102,7 +131,7 @@ public class ContestNoticeController {
notice.setNoticeType(dto.getNoticeType());
notice.setPriority(dto.getPriority());
if (StringUtils.hasText(dto.getPublishTime())) {
notice.setPublishTime(LocalDateTime.parse(dto.getPublishTime()));
notice.setPublishTime(parseDateTime(dto.getPublishTime()));
}
noticeService.updateById(notice);
return Result.success();

View File

@ -18,6 +18,8 @@ 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.service.IContestService;
import com.competition.modules.sys.entity.SysTenant;
import com.competition.modules.sys.mapper.SysTenantMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@ -37,6 +39,7 @@ public class ContestServiceImpl extends ServiceImpl<ContestMapper, BizContest> i
private final ContestAttachmentMapper contestAttachmentMapper;
private final ContestRegistrationMapper contestRegistrationMapper;
private final ContestWorkMapper contestWorkMapper;
private final SysTenantMapper sysTenantMapper;
// 支持两种日期格式ISO 格式 (T 分隔) 和空格分隔格式
private static final DateTimeFormatter ISO_FORMATTER = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
@ -204,6 +207,24 @@ public class ContestServiceImpl extends ServiceImpl<ContestMapper, BizContest> i
List<BizContestAttachment> attachments = contestAttachmentMapper.selectList(attWrapper);
result.put("attachments", attachments);
// 查询授权租户的详细信息
List<Integer> tenantIds = contest.getContestTenants();
if (tenantIds != null && !tenantIds.isEmpty()) {
List<Map<String, Object>> tenantInfoList = new ArrayList<>();
for (Integer tenantId : tenantIds) {
SysTenant tenant = sysTenantMapper.selectById(tenantId.longValue());
if (tenant != null) {
Map<String, Object> tenantInfo = new LinkedHashMap<>();
tenantInfo.put("id", tenant.getId());
tenantInfo.put("name", tenant.getName());
tenantInfo.put("code", tenant.getCode());
tenantInfo.put("tenantType", tenant.getTenantType());
tenantInfoList.add(tenantInfo);
}
}
result.put("contestTenantInfos", tenantInfoList);
}
return result;
}

View File

@ -12,7 +12,14 @@ export interface Contest {
endTime: string;
address?: string;
content?: string;
visibility?: "public" | "designated" | "internal"; // 可见范围
contestTenants?: number[];
contestTenantInfos?: Array<{
id: number;
name: string;
code: string;
tenantType?: string;
}>;
coverUrl?: string;
posterUrl?: string;
contactName?: string;

View File

@ -250,6 +250,60 @@
<!-- 右侧组织信息 -->
<div class="content-right">
<div class="sidebar-card">
<div class="sidebar-header">
<EyeOutlined />
<span>可见范围</span>
</div>
<div class="sidebar-body">
<div class="sidebar-item">
<div class="item-label">
<GlobalOutlined />
可见范围
</div>
<div class="item-value">
{{ getVisibilityText(contest?.visibility) }}
</div>
</div>
<div
v-if="contest?.visibility === 'designated' && contest?.contestTenantInfos?.length"
class="sidebar-item"
>
<div class="item-label">
<TeamOutlined />
开放机构
</div>
<div class="item-value">
<div
v-for="tenant in contest.contestTenantInfos"
:key="tenant.id"
class="org-name"
>
{{ tenant.name }}{{ tenant.code }}
</div>
</div>
</div>
</div>
</div>
<div class="sidebar-card">
<div class="sidebar-header">
<InfoCircleOutlined />
<span>基本信息</span>
</div>
<div class="sidebar-body">
<div class="sidebar-item">
<div class="item-label">
<ClockCircleOutlined />
创建时间
</div>
<div class="item-value">
{{ formatDateTime(contest?.createTime) }}
</div>
</div>
</div>
</div>
<div class="sidebar-card">
<div class="sidebar-header">
<TeamOutlined />
@ -262,15 +316,18 @@
主办单位
</div>
<div class="item-value">
<template
v-if="contest.organizers && contest.organizers.length"
>
<div
v-for="org in contest.organizers"
:key="org"
class="org-name"
>
{{ org }}
<template v-if="contest.organizers">
<template v-if="Array.isArray(contest.organizers)">
<div
v-for="org in contest.organizers"
:key="org"
class="org-name"
>
{{ org }}
</div>
</template>
<div v-else-if="contest.organizers" class="org-name">
{{ contest.organizers }}
</div>
</template>
<span v-else class="empty-text">暂无</span>
@ -283,17 +340,18 @@
协办单位
</div>
<div class="item-value">
<template
v-if="
contest.coOrganizers && contest.coOrganizers.length
"
>
<div
v-for="org in contest.coOrganizers"
:key="org"
class="org-name"
>
{{ org }}
<template v-if="contest.coOrganizers">
<template v-if="Array.isArray(contest.coOrganizers)">
<div
v-for="org in contest.coOrganizers"
:key="org"
class="org-name"
>
{{ org }}
</div>
</template>
<div v-else-if="contest.coOrganizers" class="org-name">
{{ contest.coOrganizers }}
</div>
</template>
<span v-else class="empty-text">暂无</span>
@ -306,15 +364,18 @@
赞助单位
</div>
<div class="item-value">
<template
v-if="contest.sponsors && contest.sponsors.length"
>
<div
v-for="sp in contest.sponsors"
:key="sp"
class="org-name"
>
{{ sp }}
<template v-if="contest.sponsors">
<template v-if="Array.isArray(contest.sponsors)">
<div
v-for="sp in contest.sponsors"
:key="sp"
class="org-name"
>
{{ sp }}
</div>
</template>
<div v-else-if="contest.sponsors" class="org-name">
{{ contest.sponsors }}
</div>
</template>
<span v-else class="empty-text">暂无</span>
@ -382,6 +443,8 @@ import {
GiftOutlined,
PhoneOutlined,
UserOutlined,
GlobalOutlined,
InfoCircleOutlined,
} from "@ant-design/icons-vue"
import dayjs from "dayjs"
import { useAuthStore } from "@/stores/auth"
@ -513,6 +576,19 @@ const getNoticeTypeText = (type?: string) => {
}
}
const getVisibilityText = (visibility?: string) => {
switch (visibility) {
case "public":
return "公开(所有公众用户可见)"
case "designated":
return "指定机构"
case "internal":
return "仅内部"
default:
return "未知"
}
}
const getRankClass = (rank?: number) => {
if (rank === 1) return "rank-1"
if (rank === 2) return "rank-2"