From b11c77e47f707d2de2a9d23215fc15103c1437b1 Mon Sep 17 00:00:00 2001 From: zhaoxing <2558691827@qq.com> Date: Fri, 13 Nov 2026 11:28:58 +0800 Subject: [PATCH] =?UTF-8?q?fix(announcements):=20=E5=85=AC=E5=91=8A?= =?UTF-8?q?=E5=88=86=E7=B1=BB=E4=B8=AD=E6=96=87=E5=8C=96=E5=B9=B6=E4=BF=AE?= =?UTF-8?q?=E6=AD=A3=E7=AD=9B=E9=80=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/features/admin/labels.ts | 14 ++---- .../announcements/AnnouncementDetailPage.tsx | 5 ++- .../announcements/AnnouncementsPage.tsx | 15 +++---- .../src/features/announcements/labels.test.ts | 43 +++++++++++++++++++ frontend/src/features/announcements/labels.ts | 25 +++++++++++ .../mobile/MobileAnnouncementDetailPage.tsx | 5 ++- 6 files changed, 85 insertions(+), 22 deletions(-) create mode 100644 frontend/src/features/announcements/labels.test.ts create mode 100644 frontend/src/features/announcements/labels.ts diff --git a/frontend/src/features/admin/labels.ts b/frontend/src/features/admin/labels.ts index a1fb1b0..f040bfb 100644 --- a/frontend/src/features/admin/labels.ts +++ b/frontend/src/features/admin/labels.ts @@ -223,21 +223,15 @@ export const announcementStatusMap: Record = { archived: { label: '已归档', tone: 'default' }, } -export const announcementCategoryMap: Record = { - notice: '通知', - tutorial: '教程', - rule: '规则', - faq: '常见问题', -} +export { + announcementCategoryMap, + announcementCategoryOptions, +} from '@/features/announcements/labels' export const announcementStatusOptions = Object.entries(announcementStatusMap).map( ([value, meta]) => ({ value, label: meta.label }) ) -export const announcementCategoryOptions = Object.entries(announcementCategoryMap).map( - ([value, label]) => ({ value, label }) -) - export const auditActorTypeMap: Record = { admin: '管理员', user: '用户', diff --git a/frontend/src/features/announcements/AnnouncementDetailPage.tsx b/frontend/src/features/announcements/AnnouncementDetailPage.tsx index b599d67..7627144 100644 --- a/frontend/src/features/announcements/AnnouncementDetailPage.tsx +++ b/frontend/src/features/announcements/AnnouncementDetailPage.tsx @@ -3,7 +3,8 @@ import { ArrowLeftOutlined } from '@ant-design/icons' import { useNavigate, useParams } from 'react-router-dom' import { useAnnouncement } from '@/features/announcements/api' -import { formatDateTime, formatText } from '@/shared/utils/format' +import { announcementCategoryLabel } from '@/features/announcements/labels' +import { formatDateTime } from '@/shared/utils/format' export function AnnouncementDetailPage() { const { id } = useParams() @@ -39,7 +40,7 @@ export function AnnouncementDetailPage() { {item.is_pinned ? 置顶 : null} {item.is_important ? 重要 : null} - {formatText(item.category)} + {announcementCategoryLabel(item.category)} {formatDateTime(item.published_at || item.created_at)} · 阅读 {item.view_count} diff --git a/frontend/src/features/announcements/AnnouncementsPage.tsx b/frontend/src/features/announcements/AnnouncementsPage.tsx index 3680763..f4d862b 100644 --- a/frontend/src/features/announcements/AnnouncementsPage.tsx +++ b/frontend/src/features/announcements/AnnouncementsPage.tsx @@ -3,15 +3,14 @@ import { Alert, Card, Pagination, Segmented, Space, Tag, Typography } from 'antd import { Link } from 'react-router-dom' import { useAnnouncements } from '@/features/announcements/api' +import { + announcementCategoryLabel, + announcementCategoryOptions, +} from '@/features/announcements/labels' import type { Announcement } from '@/features/announcements/types' -import { formatDateTime, formatText } from '@/shared/utils/format' +import { formatDateTime } from '@/shared/utils/format' -const categoryOptions = [ - { value: '', label: '全部' }, - { value: 'system', label: '系统公告' }, - { value: 'order', label: '订单规则' }, - { value: 'activity', label: '活动' }, -] +const categoryOptions = [{ value: '', label: '全部' }, ...announcementCategoryOptions] export function AnnouncementsPage() { const [category, setCategory] = useState('') @@ -62,7 +61,7 @@ export function AnnouncementsPage() { {item.is_pinned ? 置顶 : null} {item.is_important ? 重要 : null} - {formatText(item.category)} + {announcementCategoryLabel(item.category)} {item.title} diff --git a/frontend/src/features/announcements/labels.test.ts b/frontend/src/features/announcements/labels.test.ts new file mode 100644 index 0000000..e09e692 --- /dev/null +++ b/frontend/src/features/announcements/labels.test.ts @@ -0,0 +1,43 @@ +import { describe, expect, it } from 'vitest' + +import { + announcementCategoryLabel, + announcementCategoryMap, + announcementCategoryOptions, +} from './labels' + +describe('announcementCategoryLabel', () => { + it('映射四个公告分类', () => { + expect(announcementCategoryLabel('notice')).toBe('通知公告') + expect(announcementCategoryLabel('tutorial')).toBe('使用教程') + expect(announcementCategoryLabel('rule')).toBe('规则说明') + expect(announcementCategoryLabel('faq')).toBe('常见问题') + }) + + it('未知分类保留原始值', () => { + expect(announcementCategoryLabel('activity')).toBe('activity') + expect(announcementCategoryLabel(' system ')).toBe('system') + }) + + it('空值显示破折号', () => { + expect(announcementCategoryLabel('')).toBe('—') + expect(announcementCategoryLabel(' ')).toBe('—') + expect(announcementCategoryLabel(null)).toBe('—') + expect(announcementCategoryLabel(undefined)).toBe('—') + }) +}) + +describe('announcementCategoryOptions', () => { + it('按固定顺序输出四个分类选项', () => { + expect(announcementCategoryOptions).toEqual([ + { value: 'notice', label: '通知公告' }, + { value: 'tutorial', label: '使用教程' }, + { value: 'rule', label: '规则说明' }, + { value: 'faq', label: '常见问题' }, + ]) + }) + + it('映射表与选项数量一致', () => { + expect(announcementCategoryOptions).toHaveLength(Object.keys(announcementCategoryMap).length) + }) +}) diff --git a/frontend/src/features/announcements/labels.ts b/frontend/src/features/announcements/labels.ts new file mode 100644 index 0000000..742e048 --- /dev/null +++ b/frontend/src/features/announcements/labels.ts @@ -0,0 +1,25 @@ +/** + * 公告分类中文映射:与后台「公告分类」下拉保持一致。 + * 后端 category 取值固定为 notice / tutorial / rule / faq。 + */ +export const announcementCategoryMap: Record = { + notice: '通知公告', + tutorial: '使用教程', + rule: '规则说明', + faq: '常见问题', +} + +export const announcementCategoryOptions = Object.entries(announcementCategoryMap).map( + ([value, label]) => ({ value, label }) +) + +/** + * 公告分类展示文案: + * - 未知分类保留原始值,避免历史数据被吞掉 + * - 空值统一显示破折号 + */ +export function announcementCategoryLabel(code?: string | null): string { + const key = (code ?? '').trim() + if (!key) return '—' + return announcementCategoryMap[key] ?? key +} diff --git a/frontend/src/features/mobile/MobileAnnouncementDetailPage.tsx b/frontend/src/features/mobile/MobileAnnouncementDetailPage.tsx index 8cd9e22..780008a 100644 --- a/frontend/src/features/mobile/MobileAnnouncementDetailPage.tsx +++ b/frontend/src/features/mobile/MobileAnnouncementDetailPage.tsx @@ -2,7 +2,8 @@ import { Card, ErrorBlock, NavBar, Skeleton, Tag } from 'antd-mobile' import { useNavigate, useParams } from 'react-router-dom' import { useAnnouncement } from '@/features/announcements/api' -import { formatDateTime, formatText } from '@/shared/utils/format' +import { announcementCategoryLabel } from '@/features/announcements/labels' +import { formatDateTime } from '@/shared/utils/format' export function MobileAnnouncementDetailPage() { const { id } = useParams() @@ -33,7 +34,7 @@ export function MobileAnnouncementDetailPage() {
{item.is_pinned ? 置顶 : null} {item.is_important ? 重要 : null} - {formatText(item.category)} + {announcementCategoryLabel(item.category)}
{formatDateTime(item.published_at || item.created_at)} · 阅读 {item.view_count}