+ // 窄框下没填的项不占行(编辑时照常列出来好填)
+
+ {CT.columnHead[f]}
+
{editing ? input : view}
{it && !changed && (
@@ -140,8 +157,8 @@ const ColumnRow = memo(function ColumnRow({ col, editing, draft, initial, proble
const measure = items?.measure
const codes = items?.codes
return (
-
- |
+ |
+
{name}
{structure?.pk && {CT.pk}}
@@ -193,7 +210,8 @@ const ColumnRow = memo(function ColumnRow({ col, editing, draft, initial, proble
>,
)}
-
+ |
{!editing && pending > 0 && (
} disabled={busy}
onClick={() => onConfirmColumn(name)} data-confirm-column={name} />
diff --git a/frontend/src/pages/catalog/TableDetail.tsx b/frontend/src/pages/catalog/TableDetail.tsx
index e4ac8c95..3b67a5be 100644
--- a/frontend/src/pages/catalog/TableDetail.tsx
+++ b/frontend/src/pages/catalog/TableDetail.tsx
@@ -17,7 +17,7 @@ import {
import { CatalogImpactList } from '../../components/CatalogImpact'
import { CountBadges, ItemMark, StatusChip, StatusLegend, SystemNotesNotice } from './parts'
import type { ReviewTarget } from './parts'
-import { ColumnTable, Value, pendingCodes } from './ColumnTable'
+import { ColumnTable, NARROW, Value, pendingCodes } from './ColumnTable'
import type { ColumnModel } from './ColumnTable'
import { CodesDialog } from './CodesDialog'
import { coverageText } from './profile'
@@ -710,10 +710,13 @@ function RelationList({ relations, busy, targetOf, onOpen, onReview }: {
onReview: (t: ReviewTarget, a: CatalogReviewAction) => void
}) {
if (!relations.length) return {CT.noRelations}
+ // 窄框(< 40rem)下和列表格一样排成卡片:状态在第一行,下面每项一行「字段名 值」,不用横向滚动
+ const cell = clsx('px-2 py-1.5 align-top', NARROW.cell)
+ const label = (text: string) => {text}
return (
-
-
-
+
+
+
| {CT.relationHead.from} |
{CT.relationHead.to} |
@@ -723,15 +726,20 @@ function RelationList({ relations, busy, targetOf, onOpen, onReview }: {
{CT.relationHead.source} / {CT.relationHead.status} |
-
+
{relations.map((r) => {
const target = targetOf(r.to_table)
const where = CT.whereRelation(r.to_table)
const rejected = r.status === 'rejected'
return (
-
- | {r.columns.join(', ')} |
-
+ |
+ |
+ {label(CT.relationHead.from)}
+ {r.columns.join(', ')}
+ |
+
+ {label(CT.relationHead.to)}
+
{target
? (
|
- {r.to_columns.join(', ')} |
- {r.cardinality ? CATALOG_CARDINALITY_LABEL[r.cardinality] : {CT.cardinalityNone}} |
-
+ |
+ {label(CT.relationHead.toColumns)}
+ {r.to_columns.join(', ')}
+ |
+
+ {label(CT.relationHead.cardinality)}
+ {r.cardinality ? CATALOG_CARDINALITY_LABEL[r.cardinality] : {CT.cardinalityNone}}
+ |
+
+ {label(CT.relationHead.coverage)}
{typeof r.coverage === 'number'
? {coverageText(r.coverage)}
: —}
|
-
+ |
onReview({ path: relationPath(r.id), where, source: r.source, status: r.status }, a)} />
|
diff --git a/scripts/check-catalog.mjs b/scripts/check-catalog.mjs
index d86101b3..1522b799 100644
--- a/scripts/check-catalog.mjs
+++ b/scripts/check-catalog.mjs
@@ -1777,11 +1777,40 @@ await section('数据目录 · 窄屏', async () => {
await page.locator('[data-catalog-row]').first().waitFor()
check('390 宽:只显示表清单', await page.locator('[data-catalog-list]').isVisible() && !(await page.locator('[data-catalog-overview]').isVisible()))
check('……清单没有横向滚动', await overflow() <= 1, String(await overflow()))
+ // 页头放不下说明:只留标题,不能被挤成一个字的宽度、在标题后面露出说明的头一个字
+ const headSub = await page.locator('[data-catalog-page] > header p').count()
+ check('……页头只有标题,不露说明的残字', headSub === 0 && !(await page.locator('[data-catalog-page] > header').innerText()).includes('先起草'),
+ await page.locator('[data-catalog-page] > header').innerText())
await page.locator('[data-catalog-row="visits"] [data-catalog-open-row]').click()
await until(async () => (await page.locator('[data-catalog-detail="visits"][data-version]').count()) === 1)
check('……打开一张表:只显示详情,有「返回表清单」', !(await page.locator('[data-catalog-list]').isVisible())
&& await page.locator('[data-catalog-detail-back]').isVisible())
- check('……详情没有横向滚动(列表格在自己的框里滚)', await overflow() <= 1, String(await overflow()))
+ check('……详情没有横向滚动', await overflow() <= 1, String(await overflow()))
+ // 列表格、关联关系不再是 860px / 640px 宽、要横着滚的表格:排成卡片,状态标识始终在框里看得到
+ const fits = (sel) => page.evaluate((q) => {
+ const box = document.querySelector(q)
+ if (!box) return null
+ const r = box.getBoundingClientRect()
+ const marks = [...box.querySelectorAll('[data-item-mark]')].map((m) => m.getBoundingClientRect())
+ return { scroll: box.scrollWidth - box.clientWidth, width: Math.round(r.width), marks: marks.length,
+ inside: marks.every((m) => m.width > 0 && m.left >= r.left - 1 && m.right <= r.right + 1),
+ head: getComputedStyle(box.querySelector('thead')).display }
+ }, sel)
+ const cols = await fits('[data-catalog-columns]')
+ check('……列表格排成卡片:不横向滚动,表头收起,每个状态标识都在框里', !!cols && cols.scroll <= 1 && cols.head === 'none' && cols.marks > 0 && cols.inside,
+ JSON.stringify(cols))
+ const codesBox = await page.locator('[data-column="status"] [data-cell="codes"]').boundingBox()
+ check('……码值那一项在屏幕里,前面写着字段名', !!codesBox && codesBox.x >= 0 && codesBox.x + codesBox.width <= 390
+ && (await page.locator('[data-column="status"] [data-cell="codes"]').innerText()).startsWith('码值'), JSON.stringify(codesBox))
+ await page.locator('[data-column="visitor_count"]').scrollIntoViewIfNeeded()
+ if (SHOTS) await sleep(200)
+ await shot(page, 'catalog-narrow-columns')
+ const rels = await fits('[data-catalog-relations]')
+ check('……关联关系也排成卡片:不横向滚动,三条关系的状态都看得到', !!rels && rels.scroll <= 1 && rels.head === 'none' && rels.marks === 3 && rels.inside,
+ JSON.stringify(rels))
+ await page.locator('[data-catalog-section="relations"]').scrollIntoViewIfNeeded()
+ if (SHOTS) await sleep(200)
+ await shot(page, 'catalog-narrow-relations')
await openMark(page, 'grain')
const box = await panel(page).boundingBox()
check('……状态弹层在视口里', !!box && box.x >= 0 && box.x + box.width <= 390, JSON.stringify(box))
@@ -1799,6 +1828,13 @@ await section('数据目录 · 窄屏', async () => {
const inner = await profileBox(page).evaluate((el) => el.scrollWidth - el.clientWidth)
check('……数据剖析弹窗在视口里,正文不横向滚动', !!dlg && dlg.x >= 0 && dlg.x + dlg.width <= 390 && inner <= 1, `${JSON.stringify(dlg)} ${inner}`)
await page.keyboard.press('Escape')
+ // 宽屏照旧是表格(表头在,列表格 860px 起,框里横向滚动)
+ await page.setViewportSize({ width: 1440, height: 900 })
+ await openDetail(page, S1, 'visits')
+ const wide = await page.locator('[data-catalog-columns]').evaluate((el) => ({ head: getComputedStyle(el.querySelector('thead')).display,
+ table: getComputedStyle(el.querySelector('table')).display, width: el.querySelector('table').getBoundingClientRect().width }))
+ check('宽屏照旧是表格', wide.head !== 'none' && wide.table === 'table' && wide.width >= 860, JSON.stringify(wide))
+ check('……页头写着说明', (await page.locator('[data-catalog-page] > header').innerText()).includes('先起草'))
check('没有运行时报错', errors.length === 0, errors[0] ?? '')
await close()
})
| | |