mirror of
https://github.com/ThearchyHelios/AgentLab.git
synced 2026-11-15 10:41:59 +00:00
数据目录 F2b / B9:在目录页改过目录(编辑保存、确认、驳回、填码值)之后,表头也说「已保存。N 个已发布模板引用这张表,下次正式运行时会提示数据目录有变化」,和助手卡片保存后同一句;带「查看模板」跳到影响面一栏,可收起,没有模板引用时不说;补检查脚本的断言
This commit is contained in:
@@ -18,24 +18,28 @@ import { Skeleton } from './ui'
|
||||
|
||||
const SHOWN = 5
|
||||
|
||||
export function CatalogImpactList({ sourceId, table, refreshKey, compact = false }: {
|
||||
export function CatalogImpactList({ sourceId, table, refreshKey, compact = false, onLoad }: {
|
||||
sourceId: string
|
||||
table: string
|
||||
/** 变了就重新统计(表详情传目录版本:保存之后刷新) */
|
||||
refreshKey?: number | string
|
||||
/** 卡片里的紧凑版:只列前几个模板,节点收成一行 */
|
||||
compact?: boolean
|
||||
/** 统计好了:数据目录页据此在保存之后说「N 个已发布模板引用这张表」 */
|
||||
onLoad?: (d: CatalogImpact) => void
|
||||
}) {
|
||||
const [data, setData] = useState<CatalogImpact | null>(null)
|
||||
const [error, setError] = useState<unknown>(null)
|
||||
const [all, setAll] = useState(false)
|
||||
const seq = useRef(0)
|
||||
const onLoadRef = useRef(onLoad)
|
||||
onLoadRef.current = onLoad
|
||||
|
||||
const load = useCallback(() => {
|
||||
const mine = ++seq.current
|
||||
setError(null)
|
||||
api.dataCatalog.impact(sourceId, table)
|
||||
.then((d) => { if (mine === seq.current) setData(d) })
|
||||
.then((d) => { if (mine === seq.current) { setData(d); onLoadRef.current?.(d) } })
|
||||
.catch((e) => { if (mine === seq.current) setError(e) })
|
||||
}, [sourceId, table])
|
||||
|
||||
|
||||
@@ -2525,6 +2525,10 @@ export const CATALOG_UI_TEXT = {
|
||||
codesPendingAfter: '确认后这些码值仍显示「含义待填写」,之后可以再填写',
|
||||
confirmColumnTitle: (col: string, n: number) => `确认列 ${col} 的 ${formatNumber(n)} 项推断?`,
|
||||
confirmCodesTitle: (where: string) => `确认${where}?`,
|
||||
// ---- 在目录页改过之后:和助手卡片保存后同一句,说有几个已发布模板引用这张表
|
||||
savedImpact: (n: number) => `已保存。${formatNumber(n)} 个已发布模板引用这张表,下次正式运行时会提示数据目录有变化`,
|
||||
savedImpactShow: '查看模板',
|
||||
savedImpactDismiss: '收起这条提示',
|
||||
}
|
||||
|
||||
// ===========================================================================
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import type { ReactNode } from 'react'
|
||||
import { AlertTriangle, ArrowLeft, ChevronLeft, ChevronRight, ListChecks, Lock, Pencil, Plus, RotateCw, Search } from 'lucide-react'
|
||||
import { AlertTriangle, ArrowLeft, ChevronLeft, ChevronRight, GitBranch, ListChecks, Lock, Pencil, Plus, RotateCw, Search, X } from 'lucide-react'
|
||||
import clsx from 'clsx'
|
||||
import { ApiError, api } from '../../api/client'
|
||||
import type {
|
||||
@@ -95,6 +95,12 @@ export function TableDetail({ sourceId, table, rows, drafted, fillCodes, onFillD
|
||||
setCodesFor(null)
|
||||
if (seq != null && back) onFillDoneRef.current?.(seq, pending)
|
||||
}, [])
|
||||
/**
|
||||
* 在这一页改过目录(保存、确认、驳回、填码值):改了就升版本,从已发布版本发起的正式运行会提醒「数据目录有变化」。
|
||||
* 改完在表头说一句有几个已发布模板引用这张表(和助手的「建议更新数据目录」卡片保存后同一句),版本号对上才说
|
||||
*/
|
||||
const [savedAt, setSavedAt] = useState<number | null>(null)
|
||||
const [impact, setImpact] = useState<{ version: number; n: number } | null>(null)
|
||||
const loadSeq = useRef(0)
|
||||
const onDetailRef = useRef(onDetail)
|
||||
onDetailRef.current = onDetail
|
||||
@@ -125,6 +131,8 @@ export function TableDetail({ sourceId, table, rows, drafted, fillCodes, onFillD
|
||||
setLive('')
|
||||
setCodesFor(null)
|
||||
fillOpen.current = null
|
||||
setSavedAt(null)
|
||||
setImpact(null)
|
||||
void load()
|
||||
}, [load])
|
||||
|
||||
@@ -172,6 +180,7 @@ export function TableDetail({ sourceId, table, rows, drafted, fillCodes, onFillD
|
||||
setDetail(d)
|
||||
setConflict(null)
|
||||
onDetailRef.current(d)
|
||||
setSavedAt(d.version)
|
||||
return d
|
||||
} catch (e) {
|
||||
if (e instanceof ApiError && e.status === 409) setConflict(editingRef.current ? 'edit' : 'review')
|
||||
@@ -236,6 +245,7 @@ export function TableDetail({ sourceId, table, rows, drafted, fillCodes, onFillD
|
||||
setDetail(next)
|
||||
setConflict(null)
|
||||
onDetailRef.current(next)
|
||||
setSavedAt(next.version)
|
||||
closeCodes(pendingCodes(value))
|
||||
const filled = Math.max(0, pendingCodes(item.value) - pendingCodes(value))
|
||||
toast.ok(KT.saved(filled))
|
||||
@@ -342,6 +352,13 @@ export function TableDetail({ sourceId, table, rows, drafted, fillCodes, onFillD
|
||||
await load()
|
||||
}
|
||||
|
||||
/** 表头那句影响面提示的「查看」:滚到「引用这张表的模板」一栏,焦点给它的标题 */
|
||||
const showImpact = () => {
|
||||
const el = document.getElementById('catalog-sec-impact')
|
||||
el?.scrollIntoView({ block: 'start', behavior: 'smooth' })
|
||||
el?.focus({ preventScroll: true })
|
||||
}
|
||||
|
||||
const targetOf = (name: string) => {
|
||||
const lower = name.toLowerCase()
|
||||
return rows.find((r) => r.table_name === name || r.qualified === name)
|
||||
@@ -385,6 +402,22 @@ export function TableDetail({ sourceId, table, rows, drafted, fillCodes, onFillD
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{!edit && savedAt === detail.version && impact?.version === detail.version && impact.n > 0 && (
|
||||
<div className="flex items-start gap-1.5 rounded-md border px-2 py-1.5 text-2xs leading-relaxed text-dim" role="status"
|
||||
style={{ borderColor: 'color-mix(in srgb, var(--accent) 35%, var(--border))', background: 'color-mix(in srgb, var(--accent) 6%, transparent)' }}
|
||||
data-catalog-saved-impact={impact.n}>
|
||||
<GitBranch size={11} className="mt-0.5 shrink-0" style={{ color: 'var(--accent)' }} aria-hidden />
|
||||
<span className="min-w-0 flex-1">
|
||||
{UT.savedImpact(impact.n)}
|
||||
<button type="button" className="ml-1.5 rounded px-0.5 text-[var(--accent)] underline-offset-2 outline-none hover:underline focus-visible:ring-2 focus-visible:ring-[var(--accent)]"
|
||||
onClick={showImpact} data-catalog-saved-impact-show="">{UT.savedImpactShow}</button>
|
||||
</span>
|
||||
<button type="button" className="shrink-0 rounded p-0.5 text-faint outline-none hover:bg-hover hover:text-fg focus-visible:ring-2 focus-visible:ring-[var(--accent)]"
|
||||
onClick={() => setSavedAt(null)} aria-label={UT.savedImpactDismiss} title={UT.savedImpactDismiss} data-catalog-saved-impact-dismiss="">
|
||||
<X size={10} aria-hidden />
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
{!edit && (
|
||||
<div className="flex flex-wrap items-center gap-1.5">
|
||||
{counts.proposed > 0 && (
|
||||
@@ -482,10 +515,11 @@ export function TableDetail({ sourceId, table, rows, drafted, fillCodes, onFillD
|
||||
|
||||
<section aria-labelledby="catalog-sec-impact" data-catalog-section="impact">
|
||||
<div className="mb-2 flex flex-wrap items-baseline gap-2">
|
||||
<h3 id="catalog-sec-impact" className="text-xs font-semibold">{CATALOG_IMPACT_TEXT.title}</h3>
|
||||
<h3 id="catalog-sec-impact" className="text-xs font-semibold outline-none" tabIndex={-1}>{CATALOG_IMPACT_TEXT.title}</h3>
|
||||
<span className="text-2xs text-faint">{CATALOG_IMPACT_TEXT.hint}</span>
|
||||
</div>
|
||||
<CatalogImpactList sourceId={sourceId} table={detail.table_name} refreshKey={detail.version} />
|
||||
<CatalogImpactList sourceId={sourceId} table={detail.table_name} refreshKey={detail.version}
|
||||
onLoad={(d) => setImpact({ version: detail.version, n: d.templates.length })} />
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -925,8 +925,27 @@ await section('数据目录 · 引用这张表的模板(影响面)', async (
|
||||
// 开发模式下 StrictMode 会把挂载时的取数做两遍,只查有没有取、没有写
|
||||
check('统计影响面只读', writes(sent, /impact/).length === 0 && count(sent, /\/visits\/impact$/) >= 1,
|
||||
`${count(sent, /\/visits\/impact$/)} 次`)
|
||||
// 在目录页改过(这里是编辑保存):表头说有几个已发布模板引用这张表,和助手卡片保存后同一句
|
||||
const savedHint = page.locator('[data-catalog-saved-impact]')
|
||||
check('没改过:表头不说影响面', await savedHint.count() === 0)
|
||||
await page.locator('[data-catalog-edit]').click()
|
||||
await page.locator('[data-input="grain"]').fill('一张门票的一次入园(检查)')
|
||||
await page.locator('[data-catalog-save]').click()
|
||||
check('在目录页编辑保存后:表头说「2 个已发布模板引用这张表,下次正式运行时会提示数据目录有变化」',
|
||||
await until(async () => (await savedHint.count()) === 1)
|
||||
&& (await savedHint.innerText()).includes('2 个已发布模板引用这张表,下次正式运行时会提示数据目录有变化'),
|
||||
await savedHint.innerText().catch(() => ''))
|
||||
await savedHint.locator('[data-catalog-saved-impact-show]').click()
|
||||
check('……点「查看模板」:焦点落到「引用这张表的模板」一栏', await until(async () => page.evaluate(() => document.activeElement?.id === 'catalog-sec-impact'), 2000))
|
||||
await savedHint.locator('[data-catalog-saved-impact-dismiss]').click()
|
||||
check('……可以收起', await savedHint.count() === 0)
|
||||
// 单项确认也升版本:同样说
|
||||
await openMark(page, 'description')
|
||||
await page.locator('[data-item-panel] [data-review-action="confirm"]').click()
|
||||
check('单项确认之后同样说', await until(async () => (await savedHint.count()) === 1))
|
||||
// 没有模板引用的表:直说没有
|
||||
await openDetail(page, S1, 'orders')
|
||||
check('换一张表:上一张的提示不带过来', await savedHint.count() === 0)
|
||||
check('没有模板引用:直说没有', (await page.locator('[data-catalog-section="impact"] [data-catalog-impact="0"]').innerText())
|
||||
.includes('没有已发布或受管的模板引用这张表'))
|
||||
// 表详情保存之后(版本变了)重新统计
|
||||
@@ -936,6 +955,8 @@ await section('数据目录 · 引用这张表的模板(影响面)', async (
|
||||
await page.locator('[data-catalog-save]').click()
|
||||
await until(async () => count(sent, /\/orders\/impact$/) > before, 3000)
|
||||
check('保存之后重新统计影响面', count(sent, /\/orders\/impact$/) > before, `${before} → ${count(sent, /\/orders\/impact$/)}`)
|
||||
await sleep(300)
|
||||
check('……没有模板引用的表保存后不说影响面', await savedHint.count() === 0)
|
||||
check('没有运行时报错', errors.length === 0, errors.join(' | '))
|
||||
await close()
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user