diff --git a/frontend/src/pages/catalog/TableDetail.tsx b/frontend/src/pages/catalog/TableDetail.tsx
index f002cb24..c0168f7e 100644
--- a/frontend/src/pages/catalog/TableDetail.tsx
+++ b/frontend/src/pages/catalog/TableDetail.tsx
@@ -9,7 +9,7 @@ import type {
} from '../../types'
import { DeleteButton, EmptyState, ErrorState, Notice, Skeleton, Spinner, confirmDialog, toast } from '../../components/ui'
import { useLeaveGuard } from '../../lib/leave'
-import { formatDateTime, formatNumber } from '../../lib/format'
+import { formatDateTime, formatNumber, formatTime } from '../../lib/format'
import {
CATALOG_CARDINALITY_LABEL, CATALOG_IMPACT_TEXT, CATALOG_KIND_HINT, CATALOG_KIND_LABEL, CATALOG_TABLE_FIELD_HINT,
CATALOG_TABLE_FIELD_LABEL, CATALOG_TEXT as CT, CODES_TEXT as KT,
@@ -326,9 +326,10 @@ export function TableDetail({ sourceId, table, rows, drafted, fillCodes, prev, n
{label &&
{st?.qualified ?? detail.table_name}}
{CT.usage(detail.usage)}
-
+ {/* 时间写全站统一的短格式(今天「10:05」、今年「9/27 15:21」),完整时间放在悬停里 */}
+
{detail.updated_at
- ? (detail.updated_by ? CT.updatedBy(detail.updated_by, formatDateTime(detail.updated_at)) : CT.updatedAt(formatDateTime(detail.updated_at)))
+ ? (detail.updated_by ? CT.updatedBy(detail.updated_by, formatTime(detail.updated_at)) : CT.updatedAt(formatTime(detail.updated_at)))
: CT.notStarted}
diff --git a/frontend/src/pages/catalog/parts.tsx b/frontend/src/pages/catalog/parts.tsx
index 5c90ee69..3e210979 100644
--- a/frontend/src/pages/catalog/parts.tsx
+++ b/frontend/src/pages/catalog/parts.tsx
@@ -6,7 +6,7 @@ import type { LucideIcon } from 'lucide-react'
import clsx from 'clsx'
import type { CatalogCounts, CatalogReviewAction, CatalogSource, CatalogStatus } from '../../types'
import { Notice } from '../../components/ui'
-import { formatDateTime, formatNumber } from '../../lib/format'
+import { formatDateTime, formatNumber, formatTime } from '../../lib/format'
import {
CATALOG_SOURCE_LABEL, CATALOG_STATUS_HINT, CATALOG_STATUS_LABEL, CATALOG_TEXT as CT,
} from '../../lib/terms'
@@ -256,7 +256,9 @@ export function ItemMark({ target, compact = false, disabled = false, disabledHi
{CATALOG_STATUS_HINT[status]}
{CT.itemSource(CATALOG_SOURCE_LABEL[source])}
- {target.updated_at && {CT.itemUpdated(formatDateTime(target.updated_at))}
}
+ {target.updated_at && (
+ {CT.itemUpdated(formatTime(target.updated_at))}
+ )}
{target.note && {target.note}
}
diff --git a/scripts/check-catalog.mjs b/scripts/check-catalog.mjs
index 5b3a7d9d..82b3569a 100644
--- a/scripts/check-catalog.mjs
+++ b/scripts/check-catalog.mjs
@@ -802,6 +802,10 @@ await section('数据目录 · 表详情与单项审阅', async () => {
await openDetail(page, S1, 'visits')
const head = await detail(page).locator('header').innerText()
check('表头:中文名、表类型、表名、使用次数、谁改过', ['入园记录', '明细表', 'visits', '使用 42 次', '审核甲'].every((s) => head.includes(s)), head.replace(/\s+/g, ' '))
+ // 时间用全站统一的短格式(formatTime:今年写「9/28 10:00」),完整时间(带秒和时区)放在悬停里
+ const updated = detail(page).locator('[data-detail-updated]')
+ check('……修改时间写全站统一的短格式,完整时间放在悬停里', /^审核甲 于 (2026\/)?9\/28 10:00 修改$/.test(await updated.innerText())
+ && !head.includes('UTC') && (await updated.getAttribute('title')) === '2026-09-28 10:00:00 (UTC+08:00)', `${await updated.innerText()} / ${await updated.getAttribute('title')}`)
const grain = page.locator('[data-field="grain"]')
check('表级项:粒度的值和「推断 · 模型起草」', (await grain.innerText()).includes('一张门票的一次入园') && (await grain.innerText()).includes('推断')
&& (await grain.innerText()).includes('模型起草'))
@@ -826,6 +830,8 @@ await section('数据目录 · 表详情与单项审阅', async () => {
await openMark(page, 'grain')
const pt = await panel(page).innerText()
check('点状态标识:弹层写位置、状态说明、来源', pt.includes('表的粒度的来源和状态') && pt.includes('只作提示,确认后才参与 SQL 检查') && pt.includes('来源:模型起草'), pt.replace(/\s+/g, ' '))
+ check('……更新时间也写短格式,完整时间放在悬停里', /更新于 (2026\/)?9\/28 10:00/.test(pt) && !pt.includes('UTC')
+ && (await panel(page).locator('[data-item-updated]').getAttribute('title')) === '2026-09-28 10:00:00 (UTC+08:00)', pt.replace(/\s+/g, ' '))
check('……焦点落在「确认」上', await page.evaluate(() => document.activeElement?.getAttribute('data-review-action')) === 'confirm')
check('……推断项已是初始状态,「恢复」不可点', await panel(page).locator('[data-review-action="reset"]').isDisabled())
await page.keyboard.press('Escape')