mirror of
https://github.com/ThearchyHelios/AgentLab.git
synced 2026-11-15 06:02:01 +00:00
数据目录起草(前端):调用接连没写出来时,进行中醒目显示服务端给的提醒(job.notice)——提醒的样式、不是错误,写在进度条下面;关掉对话框时目录页上方的进度也带着这一行(全文在悬停里);好转后服务端清掉就收起,任务结束后不再显示。用量那一行接着写没写出来、超时的次数(没写出来的次数里含超时,写成「没写出来 4 次(其中超时 3 次)」,不读成两个数相加),有超时时写明「超时的调用不计用量,实际用量可能更多」;额度用完、超时多了服务端把之后的段缩小时写明「之后的段每段最多 20 列」。类型加 CatalogDraftJob.part_columns、notice,usage 加 timeouts、failed_calls(老服务端没有这几个键,按 0 和 null 处理)。check-catalog 的假服务端跟着给这几项,新加一节覆盖提醒的出现与消失、超时和没写出来的次数、段缩小的说明、任务结束后提醒收起,窄屏的起草弹窗和上方进度带着提醒一并量;宽表没写出来的原因照新写法写明列的范围(第 3 段(promo_attr_19 到 promo_attr_43,共 25 列))
This commit is contained in:
@@ -2390,8 +2390,11 @@ export const CATALOG_TEXT = {
|
||||
// - 「起草要求」:使用者补充给模型的业务背景和用词约定,存在数据源上,以后每次用模型起草都带上;
|
||||
// 「本次补充说明」只用于这一次(重试时沿用)。两者都只交给模型,不改起草规则:冲突时以规则为准。
|
||||
// - 「起草模型」:设置里单独的一项(偏好设置 → 起草模型),留空跟随助手的模型。
|
||||
// - 宽表的「段」:超过 40 列的表拆成表级一段和若干列分段,逐段起草;写的是「已完成 3 / 9 段」。
|
||||
// - 宽表的「段」:超过 40 列的表拆成表级一段和若干列分段,逐段起草;写的是「已完成 3 / 9 段」。模型多次用完额度或
|
||||
// 超时后,之后的段缩小(每段最多 20 列、10 列)。
|
||||
// - 「只重试失败的表」:重新起草失败、部分完成和已停止的表;已经写进目录的内容不回滚、不受影响。
|
||||
// - 「没写出来」:一次调用没有写出内容(额度用完、只给空壳、超时、出错)。接连没写出来时服务端给一句「提醒」,
|
||||
// 好转后收起;提醒是提醒的样式,不是错误。超时的调用不计用量。
|
||||
// ===========================================================================
|
||||
|
||||
/** 起草要求、本次补充说明的字数上限(服务端同一个数:超长 422) */
|
||||
@@ -2441,6 +2444,19 @@ export const CATALOG_DRAFT_TEXT = {
|
||||
modelUsed: (model: string) => `模型:${model}`,
|
||||
usage: (calls: number, input: number, output: number) =>
|
||||
`调用模型 ${formatNumber(calls)} 次,输入 ${formatTokens(input, { compact: true })}、输出 ${formatTokens(output, { compact: true })}`,
|
||||
/**
|
||||
* 用量那一行接着写的:没写出来、超时的调用各几次(没有的不写)。没写出来的次数里含超时的,写成「其中」,免得
|
||||
* 读成两个数相加
|
||||
*/
|
||||
usageFailures: (timeouts: number, failed: number) => {
|
||||
if (failed && timeouts && timeouts <= failed) return `没写出来 ${formatNumber(failed)} 次(其中超时 ${formatNumber(timeouts)} 次)`
|
||||
return [failed ? `没写出来 ${formatNumber(failed)} 次` : '', timeouts ? `超时 ${formatNumber(timeouts)} 次` : ''].filter(Boolean).join('、')
|
||||
},
|
||||
usageFailuresHint: '没写出来:调用没有写出内容,包括额度用完、只给空壳、超时和出错',
|
||||
// 超时的调用服务端照实记 0 个 token,服务方却可能已经生成了内容
|
||||
timeoutNote: '超时的调用不计用量,实际用量可能更多',
|
||||
/** 额度用完、超时多了,服务端把之后的段缩小了 */
|
||||
partColumns: (n: number) => `模型多次用完额度或超时,之后的段每段最多 ${formatNumber(n)} 列`,
|
||||
// ---- 结束
|
||||
title: {
|
||||
queued: '排队中,等待开始…',
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import type { ReactNode } from 'react'
|
||||
import { useNavigate, useParams, useSearchParams } from 'react-router-dom'
|
||||
import { ArrowLeft, BookMarked, Database, ListFilter as ListFilterIcon, ScanSearch, Sparkles } from 'lucide-react'
|
||||
import { ArrowLeft, BookMarked, Database, ListFilter as ListFilterIcon, ScanSearch, Sparkles, TriangleAlert } from 'lucide-react'
|
||||
import clsx from 'clsx'
|
||||
import { ApiError, api } from '../../api/client'
|
||||
import type { CatalogDetail, CatalogList, CatalogTableRow } from '../../types'
|
||||
@@ -203,6 +203,8 @@ export function CatalogPage() {
|
||||
const stripJob: { done: number; total: number; status: string } | null = draftJob && (draftActive || draftJob.id === listActive?.id)
|
||||
? (draftActive ? draftJob : null)
|
||||
: listActive
|
||||
// 调用接连没写出来的提醒:关着对话框时也要看得到(上方的进度带着它)
|
||||
const stripNotice = draftActive ? draftJob?.notice?.trim() || null : null
|
||||
|
||||
const options = savedOptions?.sourceId === sourceId ? savedOptions.options : source?.options
|
||||
const profileSettings = useMemo(() => profileSettingsOf(options), [options])
|
||||
@@ -430,7 +432,7 @@ export function CatalogPage() {
|
||||
onGoSource={() => navigate(back)} />
|
||||
)}
|
||||
{hasTables && usage.used > 0 && !empty && <UsageSummary {...usage} active={usedOnly ? filter : null} onFocus={focusUsed} />}
|
||||
{hasTables && stripJob && !draftOpen && <DraftStrip job={stripJob} onOpen={openDraft} />}
|
||||
{hasTables && stripJob && !draftOpen && <DraftStrip job={stripJob} notice={stripNotice} onOpen={openDraft} />}
|
||||
{body}
|
||||
{draftOpen && data && (
|
||||
<DraftDialog
|
||||
@@ -481,9 +483,14 @@ export function CatalogPage() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 起草任务在进行:目录页上方一条进度(关掉对话框任务照常进行,每张表起草完就写进目录)。点「查看进度」回到对话框
|
||||
* 起草任务在进行:目录页上方一条进度(关掉对话框任务照常进行,每张表起草完就写进目录)。点「查看进度」回到对话框。
|
||||
* notice:调用接连没写出来时服务端给的提醒,跟着写一行(全文在悬停里,对话框里也有)
|
||||
*/
|
||||
function DraftStrip({ job, onOpen }: { job: { done: number; total: number; status: string }; onOpen: () => void }) {
|
||||
function DraftStrip({ job, notice, onOpen }: {
|
||||
job: { done: number; total: number; status: string }
|
||||
notice: string | null
|
||||
onOpen: () => void
|
||||
}) {
|
||||
const text = job.status === 'queued' ? DT.stripQueued(job.total) : DT.strip(job.done, job.total)
|
||||
return (
|
||||
<div className="flex shrink-0 items-center gap-2 border-b px-4 py-2 text-xs"
|
||||
@@ -492,6 +499,13 @@ function DraftStrip({ job, onOpen }: { job: { done: number; total: number; statu
|
||||
<Spinner size={12} />
|
||||
<div className="min-w-0 flex-1">
|
||||
<p className="truncate font-medium" title={text}>{text}</p>
|
||||
{notice && (
|
||||
<p className="mt-0.5 flex min-w-0 items-center gap-1 text-2xs text-[var(--warn)]" role="status" title={notice}
|
||||
data-catalog-draft-notice="">
|
||||
<TriangleAlert size={10} className="shrink-0" aria-hidden />
|
||||
<span className="min-w-0 truncate">{notice}</span>
|
||||
</p>
|
||||
)}
|
||||
<div className="mt-1 h-1 max-w-xs overflow-hidden rounded-full bg-hover" role="progressbar" aria-label={text}
|
||||
aria-valuemin={0} aria-valuemax={job.total} aria-valuenow={job.done}>
|
||||
<div className="h-full rounded-full bg-[var(--accent)] transition-[width] duration-300"
|
||||
|
||||
@@ -9,7 +9,8 @@ import { errorMessage } from '../../lib/errors'
|
||||
import { formatTime } from '../../lib/format'
|
||||
import { CATALOG_DRAFT_TEXT as DT, CATALOG_TEXT as CT, DRAFT_GUIDANCE_MAX, DRAFT_NOTE_MAX } from '../../lib/terms'
|
||||
import {
|
||||
DRAFT_MAX_TABLES, DRAFT_MODEL_UNAVAILABLE, DRAFT_TOP_N, RETRYABLE, isJobActive, nothingWritten, retryableCount, taskReason,
|
||||
DRAFT_MAX_TABLES, DRAFT_MODEL_UNAVAILABLE, DRAFT_PART_COLUMNS, DRAFT_TOP_N, RETRYABLE, isJobActive, nothingWritten, retryableCount,
|
||||
taskReason,
|
||||
} from './draftJob'
|
||||
import type { DraftStartBody } from './draftJob'
|
||||
|
||||
@@ -17,8 +18,9 @@ import type { DraftStartBody } from './draftJob'
|
||||
// 起草数据目录。起草是服务端的后台任务:这里选范围、写起草要求和本次补充说明,建任务;之后显示页面跟着的那个任务
|
||||
// (进行中的进度、结束后的结果),任务本身的状态在页面上(draftJob.ts),关掉对话框照常进行。
|
||||
//
|
||||
// 三段:设置(范围、用不用模型;用模型时写起草要求、补充说明,写明用哪个起草模型)→ 进行中(总进度、合计、逐表的
|
||||
// 状态和宽表的段进度、没写全的原因;可以停止、在后台继续)→ 结束(汇总、没写全的表和原因、只重试失败的表)。
|
||||
// 三段:设置(范围、用不用模型;用模型时写起草要求、补充说明,写明用哪个起草模型)→ 进行中(总进度、合计、调用接连
|
||||
// 没写出来时的提醒、逐表的状态和宽表的段进度、没写全的原因;可以停止、在后台继续)→ 结束(汇总、没写全的表和原因、
|
||||
// 只重试失败的表)。
|
||||
// 已经写进目录的内容不回滚——起草只增改推断项,人工确认、驳回过的项服务端不会动。
|
||||
// ===========================================================================
|
||||
|
||||
@@ -317,7 +319,7 @@ function Counter({ n, max }: { n: number; max: number }) {
|
||||
)
|
||||
}
|
||||
|
||||
/** 任务的进度或结果:标题、总进度、合计、模型和用量、没写全的表、逐表列表 */
|
||||
/** 任务的进度或结果:标题、总进度、提醒(进行中)、合计、模型和用量(超时、没写出来的次数)、没写全的表、逐表列表 */
|
||||
function JobView({ job, running, stopping, busy }: { job: CatalogDraftJob; running: boolean; stopping: boolean; busy: boolean }) {
|
||||
const problems = job.tasks.filter((t) => RETRYABLE.has(t.status))
|
||||
const [showAll, setShowAll] = useState(false)
|
||||
@@ -342,6 +344,13 @@ function JobView({ job, running, stopping, busy }: { job: CatalogDraftJob; runni
|
||||
const progress = running ? DT.progress(job.done, job.total) : DT.progressDone(ok, job.total)
|
||||
const tone = job.status === 'failed' ? 'var(--err)' : running ? 'var(--accent)' : problems.length ? 'var(--warn)' : 'var(--st-done)'
|
||||
const pct = job.total ? (value / job.total) * 100 : 0
|
||||
const { usage } = job
|
||||
const timeouts = usage.timeouts ?? 0
|
||||
const failures = DT.usageFailures(timeouts, usage.failed_calls ?? 0)
|
||||
// 调用接连没写出来时服务端给的提醒:进行中写在进度条下面(提醒,不是错误);好转后服务端清掉
|
||||
const notice = running ? job.notice?.trim() ?? '' : ''
|
||||
// 额度用完、超时多了,之后的段缩小了:进行中写一句,段数为什么变多看得明白
|
||||
const partColumns = running && job.use_model && job.part_columns != null && job.part_columns < DRAFT_PART_COLUMNS ? job.part_columns : null
|
||||
return (
|
||||
<div className="min-w-0 space-y-3">
|
||||
{busy && <Notice tone="info" attr={{ 'data-draft-busy': '' }}>{DT.busy}</Notice>}
|
||||
@@ -355,14 +364,22 @@ function JobView({ job, running, stopping, busy }: { job: CatalogDraftJob; runni
|
||||
aria-valuemin={0} aria-valuemax={job.total} aria-valuenow={value}>
|
||||
<div className="h-full rounded-full transition-[width] duration-300" style={{ width: `${pct}%`, background: tone }} />
|
||||
</div>
|
||||
{notice && <Notice tone="warn" className="break-words" attr={{ 'data-draft-notice': '' }}>{notice}</Notice>}
|
||||
<p className="tnum text-sm" data-draft-summary="">{summary}</p>
|
||||
{(job.model || job.usage.calls > 0) && (
|
||||
<p className="flex flex-wrap gap-x-3 gap-y-0.5 text-2xs text-faint">
|
||||
{job.model && <span className="break-all" data-draft-model-used="">{DT.modelUsed(job.model)}</span>}
|
||||
{job.usage.calls > 0 && (
|
||||
<span className="tnum" data-draft-usage="">{DT.usage(job.usage.calls, job.usage.input_tokens, job.usage.output_tokens)}</span>
|
||||
)}
|
||||
</p>
|
||||
{(job.model || usage.calls > 0 || failures) && (
|
||||
<div className="space-y-0.5 text-2xs leading-relaxed text-faint">
|
||||
<p className="flex flex-wrap gap-x-3 gap-y-0.5" data-draft-usage-line="">
|
||||
{job.model && <span className="break-all" data-draft-model-used="">{DT.modelUsed(job.model)}</span>}
|
||||
{usage.calls > 0 && (
|
||||
<span className="tnum" data-draft-usage="">{DT.usage(usage.calls, usage.input_tokens, usage.output_tokens)}</span>
|
||||
)}
|
||||
{failures && (
|
||||
<span className="tnum text-[var(--warn)]" title={DT.usageFailuresHint} data-draft-usage-failures="">{failures}</span>
|
||||
)}
|
||||
</p>
|
||||
{timeouts > 0 && <p data-draft-timeout-note="">{DT.timeoutNote}</p>}
|
||||
{partColumns != null && <p data-draft-part-columns={partColumns}>{DT.partColumns(partColumns)}</p>}
|
||||
</div>
|
||||
)}
|
||||
{running && <p className="text-2xs leading-relaxed text-faint">{DT.hideHint}</p>}
|
||||
{job.status === 'failed' && job.error && <Notice tone="err" attr={{ 'data-draft-job-error': '' }}>{job.error}</Notice>}
|
||||
|
||||
@@ -36,6 +36,9 @@ export const retryableCount = (j: CatalogDraftJob): number => j.tasks.filter((t)
|
||||
/** 一张表这次为什么没写全:整张表没起草的原因在前,模型那部分的原因在后 */
|
||||
export const taskReason = (t: { error: string | null; model_error: string | null }): string => t.error ?? t.model_error ?? ''
|
||||
|
||||
/** 每段最多几列(服务端起草的起始上限)。任务里的当前上限比它小:额度用完、超时多了,之后的段缩小了 */
|
||||
export const DRAFT_PART_COLUMNS = 40
|
||||
|
||||
/**
|
||||
* 一项都没写进去的任务该怎么说(只对写完了的任务:完成、部分完成)。null:照常写合计。
|
||||
* 用模型、却没有一张表拿到模型的可用内容:目录没有更新、模型没给内容;有表没写全:目录没有更新、原因见下方;
|
||||
|
||||
+24
-1
@@ -2901,6 +2901,19 @@ export interface CatalogDraftTask {
|
||||
finished_at: string | null
|
||||
}
|
||||
|
||||
/** 起草任务的模型用量 */
|
||||
export interface CatalogDraftUsage {
|
||||
/** 调用次数(没写出来的、超时的也算) */
|
||||
calls: number
|
||||
/** 超时的调用不计 token(照实记 0:服务方可能已经生成了内容) */
|
||||
input_tokens: number
|
||||
output_tokens: number
|
||||
/** 超时的调用次数。老服务端没有这个键 */
|
||||
timeouts?: number
|
||||
/** 没写出来的调用次数:额度用完、只给空壳、超时、出错。老服务端没有这个键 */
|
||||
failed_calls?: number
|
||||
}
|
||||
|
||||
/** 一次起草任务 */
|
||||
export interface CatalogDraftJob {
|
||||
id: string
|
||||
@@ -2929,7 +2942,17 @@ export interface CatalogDraftJob {
|
||||
updated: number
|
||||
removed: number
|
||||
/** 模型调用的用量;拿不到用量时照实是 0 */
|
||||
usage: { calls: number; input_tokens: number; output_tokens: number }
|
||||
usage: CatalogDraftUsage
|
||||
/**
|
||||
* 当前每段最多几列:一次起草里额度用完或超时累计 2 次后,之后新开始的段减半(40 → 20 → 10,最低 10)。
|
||||
* 不用模型时可能为 null;老服务端没有这个键
|
||||
*/
|
||||
part_columns?: number | null
|
||||
/**
|
||||
* 调用接连没写出来时的提醒(一句中文:最近几次里几次没写出来、原因,建议换起草模型、缩小范围或停止);
|
||||
* 好转后服务端清掉,为 null。老服务端没有这个键
|
||||
*/
|
||||
notice?: string | null
|
||||
/** 任务整体出错的原因 */
|
||||
error: string | null
|
||||
tasks: CatalogDraftTask[]
|
||||
|
||||
@@ -425,7 +425,12 @@ const JOB_ERR = '起草任务出错:database is locked'
|
||||
/** 起草时每段最多几列(服务端的常量):超过的宽表拆成「表级一段 + 列分段」 */
|
||||
const PART_COLUMNS = 40
|
||||
|
||||
/** 建一个排队的任务:表结构里没有的表照样建进任务、记 skipped。重试建的记着原任务(retry_of,界面不用) */
|
||||
/**
|
||||
* 建一个排队的任务:表结构里没有的表照样建进任务、记 skipped。重试建的记着原任务(retry_of,界面不用)。
|
||||
* 超过 40 列的宽表均匀分段:列分段数 = ceil(列数 / 40),段进度是「表级一段 + 列分段」。
|
||||
* 用模型的任务记着当前每段最多几列(part_columns,额度用完、超时多了服务端会减半)、调用接连没写出来时的提醒(notice),
|
||||
* 用量里另记超时和没写出来的调用次数
|
||||
*/
|
||||
function newJob(state, src, { tables, use_model, note = null, retry_of = null }) {
|
||||
const id = `check-job-${++state.jobSeq}`
|
||||
const tasks = tables.map((name) => {
|
||||
@@ -442,7 +447,8 @@ function newJob(state, src, { tables, use_model, note = null, retry_of = null })
|
||||
const job = {
|
||||
id, source_id: src, status: 'queued', use_model: !!use_model, model: use_model ? 'check-draft-model' : null,
|
||||
guidance: state.guidance[src] ?? null, note, created_at: isoNow(), started_at: null, finished_at: null, created_by: ACTOR,
|
||||
retry_of, usage: { calls: 0, input_tokens: 0, output_tokens: 0 }, error: null, tasks,
|
||||
retry_of, part_columns: use_model ? PART_COLUMNS : null, notice: null,
|
||||
usage: { calls: 0, input_tokens: 0, output_tokens: 0, timeouts: 0, failed_calls: 0 }, error: null, tasks,
|
||||
}
|
||||
state.jobs.push(job)
|
||||
return job
|
||||
@@ -677,8 +683,11 @@ async function startDraft(page, { select = null, model = false, note = '' } = {}
|
||||
/** 起草用的几句话(虚构) */
|
||||
const GUIDANCE2 = '景区票务库:「核销」指闸机检票入园;金额单位是元。会员相关的表统一写「会员」,不写「用户」'
|
||||
const DRAFT_NOTE = '这一批是闸机和会员相关的表'
|
||||
const WIDE_ERR = '这张表 50 列,分 3 段起草,第 2 段没写出来(共 10 列):模型的思考和输出用完了额度(32768 个 token),已拆小重试仍没写出。可以只重试这张表'
|
||||
// 宽表没写出来的那一段写明列的范围(列名照表结构原样);只有真拆过才写「已拆小重试仍没写出」
|
||||
const WIDE_ERR = '这张表 50 列,分 3 段起草,第 3 段(promo_attr_19 到 promo_attr_43,共 25 列)没写出来:模型的思考和输出用完了额度(32768 个 token),已拆小重试仍没写出。可以只重试这张表'
|
||||
const CALL_ERR = '调用模型出错:服务暂时不可用(已重试 2 次)'
|
||||
/** 调用接连没写出来时服务端给的提醒(虚构的一句,照约定写明次数、原因和办法) */
|
||||
const DRAFT_NOTICE = '最近 6 次调用有 4 次没写出来(3 次超时、1 次额度用完)。建议在「设置 → 偏好设置」的「起草模型」换一个模型、缩小起草范围,或者停止这次起草'
|
||||
const detail = (page) => page.locator('[data-catalog-detail]')
|
||||
const version = async (page) => Number(await detail(page).getAttribute('data-version'))
|
||||
const panel = (page) => page.locator('[data-item-panel]')
|
||||
@@ -1021,7 +1030,7 @@ await section('数据目录 · 起草:进度、结果与重试', async () => {
|
||||
check('……写明哪些没写全:失败 1 张、部分完成 1 张', problems.includes('失败 1 张') && problems.includes('部分完成 1 张'), problems)
|
||||
const shown = await draftBox(page).locator('[data-draft-task]').evaluateAll((els) => els.map((e) => e.getAttribute('data-draft-task')))
|
||||
check('……逐表只列没写全的 2 张,各自写原因', shown.join(',') === 'store_sales,members'
|
||||
&& (await draftText(page, '[data-draft-task="store_sales"] [data-draft-task-reason]')).includes('第 2 段没写出来')
|
||||
&& (await draftText(page, '[data-draft-task="store_sales"] [data-draft-task-reason]')).includes('第 3 段(promo_attr_19 到 promo_attr_43,共 25 列)没写出来')
|
||||
&& (await draftText(page, '[data-draft-task="members"] [data-draft-task-reason]')).includes('已重试 2 次'), shown.join(','))
|
||||
await draftBox(page).locator('[data-draft-show-all]').click()
|
||||
check('……点「显示全部」列出 3 张', (await draftBox(page).locator('[data-draft-task]').count()) === 3)
|
||||
@@ -1305,6 +1314,71 @@ await section('数据目录 · 起草:清单上的失败标记与单表重起
|
||||
await close()
|
||||
})
|
||||
|
||||
await section('数据目录 · 起草:接连没写出来的提醒、超时与段缩小', async () => {
|
||||
const state = freshState()
|
||||
const { page, errors, natives, close } = await open(`/data/catalog/${S1}`, { handlers: catalogHandlers(state) })
|
||||
await page.locator('[data-catalog-row]').first().waitFor()
|
||||
check('开始起草(闸机到会员 3 张,用模型)', await startDraft(page, { select: ['gates', 'members'], model: true }))
|
||||
const job = state.jobs.at(-1)
|
||||
const usageLine = () => draftText(page, '[data-draft-usage-line]')
|
||||
settle(state, job, 'store_sales', { status: 'running', parts_done: 1 })
|
||||
check('还没有超时、没写出来的调用:用量只写调用次数和 token,没有提醒、超时的说明和段大小的说明',
|
||||
await until(async () => (await usageLine()).includes('调用模型 1 次')) && !(await usageLine()).includes('超时')
|
||||
&& !(await usageLine()).includes('没写出来')
|
||||
&& await draftBox(page).locator('[data-draft-notice], [data-draft-timeout-note], [data-draft-part-columns]').count() === 0, await usageLine())
|
||||
|
||||
// 宽表的段接连超时、用完额度:服务端记下超时和没写出来的次数,之后的段缩小到每段 20 列,并给出一句提醒
|
||||
Object.assign(job.usage, { calls: 7, timeouts: 3, failed_calls: 4 })
|
||||
Object.assign(job, { part_columns: 20, notice: DRAFT_NOTICE })
|
||||
const notice = draftBox(page).locator('[data-draft-notice]')
|
||||
check('接连没写出来:进行中醒目显示服务端的提醒,是提醒的样式(不是错误)',
|
||||
await until(async () => (await draftText(page, '[data-draft-notice]')) === DRAFT_NOTICE)
|
||||
&& (await notice.getAttribute('data-notice')) === 'warn', await draftText(page, '[data-draft-notice]'))
|
||||
const above = await page.evaluate(() => {
|
||||
const n = document.querySelector('[data-draft-notice]')?.getBoundingClientRect()
|
||||
const list = document.querySelector('[data-draft-tasks]')?.getBoundingClientRect()
|
||||
return !!n && !!list && n.bottom <= list.top
|
||||
})
|
||||
check('……提醒在逐表进度上面,不用往下翻', above)
|
||||
// 没写出来的次数里含超时的:写成「其中」,免得读成两个数相加
|
||||
check('……用量写「没写出来 4 次(其中超时 3 次)」', (await usageLine()).includes('没写出来 4 次(其中超时 3 次)'), await usageLine())
|
||||
check('……写明超时的调用不计用量', (await draftText(page, '[data-draft-timeout-note]')).includes('超时的调用不计用量'),
|
||||
await draftText(page, '[data-draft-timeout-note]'))
|
||||
check('……写明之后的段每段最多 20 列', (await draftText(page, '[data-draft-part-columns]')).includes('每段最多 20 列'),
|
||||
await draftText(page, '[data-draft-part-columns]'))
|
||||
await shot(page, 'catalog-draft-notice')
|
||||
|
||||
// 关掉对话框:上方的进度也带着提醒;好转后服务端清掉提醒,两处都收起
|
||||
await page.locator('[data-draft-hide]').click()
|
||||
const strip = page.locator('[data-catalog-draft-progress]')
|
||||
const stripNotice = strip.locator('[data-catalog-draft-notice]')
|
||||
check('在后台继续:上方的进度也带着提醒(全文在悬停里)', await until(async () => (await stripNotice.count()) === 1)
|
||||
&& (await stripNotice.getAttribute('title')) === DRAFT_NOTICE && (await stripNotice.innerText()).length > 0,
|
||||
await stripNotice.getAttribute('title', { timeout: 500 }).catch(() => ''))
|
||||
await shot(page, 'catalog-draft-strip-notice')
|
||||
job.notice = null
|
||||
check('……好转后服务端清掉提醒:上方的进度不再带着', await until(async () => (await stripNotice.count()) === 0))
|
||||
await strip.locator('[data-catalog-draft-open]').click()
|
||||
check('……点开对话框:提醒也不再显示,超时和没写出来的次数照旧', await until(async () => (await draftPhase(page)) === 'running')
|
||||
&& await notice.count() === 0 && (await usageLine()).includes('其中超时 3 次'), await usageLine())
|
||||
|
||||
// 又接连没写出来:提醒再出现;任务结束以后不再显示(停止、缩小范围的建议只对进行中的任务有用),用量里的次数和
|
||||
// 超时不计用量的说明照旧,段缩小的说明收起
|
||||
Object.assign(job.usage, { timeouts: 5, failed_calls: 6 })
|
||||
Object.assign(job, { part_columns: 10, notice: DRAFT_NOTICE })
|
||||
check('又接连没写出来:提醒再出现,段缩小到每段 10 列', await until(async () => (await draftText(page, '[data-draft-notice]')) === DRAFT_NOTICE)
|
||||
&& (await draftText(page, '[data-draft-part-columns]')).includes('每段最多 10 列'), await draftText(page, '[data-draft-part-columns]'))
|
||||
for (const name of ['gates', 'store_sales', 'members']) settle(state, job, name, { added: 1, model_items: 1 })
|
||||
finishJob(job)
|
||||
check('结束:不再显示提醒和段缩小的说明,用量里的次数和超时不计用量的说明照旧', await until(async () => (await draftPhase(page)) === 'done')
|
||||
&& await notice.count() === 0 && await draftBox(page).locator('[data-draft-part-columns]').count() === 0
|
||||
&& (await usageLine()).includes('没写出来 6 次(其中超时 5 次)') && (await draftText(page, '[data-draft-timeout-note]')).includes('超时的调用不计用量'),
|
||||
await usageLine())
|
||||
check('没有原生对话框', natives.length === 0, natives.join(' | '))
|
||||
check('没有运行时报错', errors.length === 0, errors[0] ?? '')
|
||||
await close()
|
||||
})
|
||||
|
||||
await section('数据目录 · 表详情与单项审阅', async () => {
|
||||
const state = freshState()
|
||||
const { page, sent, errors, natives, close } = await open(`/data/catalog/${S1}`, { handlers: catalogHandlers(state) })
|
||||
@@ -2396,14 +2470,19 @@ await section('数据目录 · 窄屏', async () => {
|
||||
const narrowJob = state.jobs.at(-1)
|
||||
settle(state, narrowJob, 'visits', { added: 2 })
|
||||
settle(state, narrowJob, 'store_sales', { status: 'partial', parts_done: 2, added: 4, model_error: WIDE_ERR })
|
||||
await until(async () => (await draftText(page, '[data-draft-task="store_sales"] [data-draft-task-reason]')).includes('第 2 段'))
|
||||
// 接连没写出来的提醒、超时和没写出来的次数、段缩小的说明也都在
|
||||
Object.assign(narrowJob.usage, { timeouts: 3, failed_calls: 4 })
|
||||
Object.assign(narrowJob, { part_columns: 20, notice: DRAFT_NOTICE })
|
||||
await until(async () => (await draftText(page, '[data-draft-task="store_sales"] [data-draft-task-reason]')).includes('第 3 段')
|
||||
&& (await draftText(page, '[data-draft-notice]')) === DRAFT_NOTICE)
|
||||
const runFit = await draftFits()
|
||||
check('……起草进行中:段进度和长原因折行,弹窗不横向滚动', runFit.ok, JSON.stringify(runFit))
|
||||
check('……起草进行中:段进度、长原因、提醒都折行,弹窗不横向滚动', runFit.ok, JSON.stringify(runFit))
|
||||
await shot(page, 'catalog-narrow-draft')
|
||||
await page.locator('[data-draft-hide]').click()
|
||||
await until(async () => (await page.locator('[data-catalog-draft-progress]').count()) === 1)
|
||||
check('……上方的起草进度不撑宽页面', await overflow() <= 1 && !!(await page.locator('[data-catalog-draft-progress]').boundingBox())
|
||||
&& (await page.locator('[data-catalog-draft-progress]').boundingBox()).width <= 390, String(await overflow()))
|
||||
check('……上方的起草进度(带着提醒)不撑宽页面', await overflow() <= 1 && !!(await page.locator('[data-catalog-draft-progress]').boundingBox())
|
||||
&& (await page.locator('[data-catalog-draft-progress]').boundingBox()).width <= 390
|
||||
&& await page.locator('[data-catalog-draft-notice]').count() === 1, String(await overflow()))
|
||||
stopJob(narrowJob)
|
||||
await until(async () => (await page.locator('[data-catalog-draft-progress]').count()) === 0)
|
||||
// 宽屏照旧是表格(表头在,列表格 860px 起,框里横向滚动)
|
||||
|
||||
Reference in New Issue
Block a user