diff --git a/furniture-3d-system/docs/架构设计/物流到货签收时长看板-订单明细快照.md b/furniture-3d-system/docs/架构设计/物流到货签收时长看板-订单明细快照.md index 67359e75..fd562f96 100644 --- a/furniture-3d-system/docs/架构设计/物流到货签收时长看板-订单明细快照.md +++ b/furniture-3d-system/docs/架构设计/物流到货签收时长看板-订单明细快照.md @@ -149,7 +149,15 @@ sudo bash scripts/install-logistics-arrival-signing-duration-snapshot-timer.sh - 解析规则由 `splitSearchKeywords()` 统一提供,服务端缓存键按关键词集合(排序后)计算,输入顺序不同不会重复缓存。 - 输入框与其它筛选控件**等高**(34px 单行高度,多行时框内滚动),"已识别 N 个关键词"显示在标签行右侧并在空间不足时省略,不再把这一格撑成两行高。 -筛选区还提供 **重置**:一键清空"签收日期(起/止)"与"关键词"两个输入(排除关键词有自己的「清空排除 / 恢复默认」,不受影响),并提示"已清空签收日期与关键词,填好后点查询"。重置只清输入不立即取数;直接点查询时,空日期按默认区间(本月 1 号 ~ 昨日)、空关键词表示不过滤,因此在跨天且当日快照尚未生成时可能落到实时源(较慢但结果正确)。 +筛选区的三个按钮各管一件事,点下去**立即按新条件查询**(互不重复): + +| 按钮 | 签收日期 | 关键词 | 排除关键词 | 结果 | +| --- | --- | --- | --- | --- | +| **重置** | 清空(输入框留空) | 清空 | 清空 | 没有任何筛选条件;空日期由服务端兜底为本月 1 号~昨日(`dateRangeCleared` 让输入框保持空白,不被兜底值回填) | +| **恢复默认** | 回到本月 1 号~昨日(显式填回输入框) | 保留 | 恢复系统默认配置(全部勾选) | 默认口径 | +| **清空排除** | 回到本月 1 号~昨日(显式填回输入框) | 保留 | 置空 | 默认区间 + 不排除任何关键词 | + +三者过去都只是"改输入、等用户点查询"(重置只清日期与关键词、另两个只改排除词),结果看起来一样容易混;现在各自的语义、界面状态与结果都区分开了,按钮 hover 有对应说明,筛选栏底部也有一行图例。空日期提交时服务端仍按默认区间查询(跨天且当日快照未生成时可能落实时源,较慢但结果正确)。 **默认签收日期区间是「本月 1 号 ~ 昨日」**(由 `normalizeLogisticsArrivalSigningDurationFilters()` 生成):当日签收数据仍在滚动,且每日快照只覆盖到昨日,用"到昨日"作默认值可以让首屏直接命中快照(整区间精确 + 秒级),不再因为区间含当天而回落到实时源。每月 1 号时会自动退化为"昨日当天"(本月 1 号还没到昨日)。 diff --git a/furniture-3d-system/src/app/supply-chain/management/logistics-arrival-signing-duration/LogisticsArrivalSigningDurationView.test.tsx b/furniture-3d-system/src/app/supply-chain/management/logistics-arrival-signing-duration/LogisticsArrivalSigningDurationView.test.tsx index 7d56f05f..b73cd1bc 100644 --- a/furniture-3d-system/src/app/supply-chain/management/logistics-arrival-signing-duration/LogisticsArrivalSigningDurationView.test.tsx +++ b/furniture-3d-system/src/app/supply-chain/management/logistics-arrival-signing-duration/LogisticsArrivalSigningDurationView.test.tsx @@ -1,6 +1,8 @@ import { fireEvent, render, screen } from '@testing-library/react' import { buildLogisticsArrivalSigningDurationDashboard, + logisticsArrivalSigningDurationDefaultRange, + serializeExcludeKeywords, type LogisticsArrivalSigningDurationExcludeKeywords, type LogisticsArrivalSigningDurationFilters, type LogisticsArrivalSigningDurationPage, @@ -163,3 +165,56 @@ it('系统管理员在选择器里新增关键词会写入默认排除关键词 global.fetch = originalFetch } }) + +it('重置 / 恢复默认 / 清空排除 三个按钮语义各不相同,并且都立即查询', async () => { + const originalFetch = global.fetch + const dashboard = buildLogisticsArrivalSigningDurationDashboard([row]) + global.fetch = jest.fn(async () => ({ ok: true, json: async () => ({ success: true, data: dashboard }) }) as Response) as typeof fetch + const requestSubmit = jest.spyOn(HTMLFormElement.prototype, 'requestSubmit').mockImplementation(() => {}) + try { + render( + , + ) + const dateFrom = document.querySelector('input[name="dateFrom"]')! + const dateTo = document.querySelector('input[name="dateTo"]')! + const keyword = screen.getByPlaceholderText(/订单号/) + const hiddenExclude = document.querySelector('input[name="excludeKeywords"]')! + const hiddenMode = document.querySelector('input[name="excludeKeywordsMode"]')! + const defaultRange = logisticsArrivalSigningDurationDefaultRange() + + // 重置 = 没有任何筛选条件:日期、关键词、排除关键词全部清空 + fireEvent.change(dateFrom, { target: { value: '2026-09-10' } }) + fireEvent.change(keyword, { target: { value: '桃子' } }) + fireEvent.click(screen.getByRole('button', { name: '重置' })) + expect(dateFrom.value).toBe('') + expect(dateTo.value).toBe('') + expect(keyword).toHaveValue('') + expect(hiddenExclude.value).toBe('') + expect(hiddenMode.value).toBe('clear') + expect(requestSubmit).toHaveBeenCalledTimes(1) + + // 恢复默认 = 本月1号~昨日 + 系统默认排除关键词 + fireEvent.click(screen.getByRole('button', { name: '恢复默认' })) + expect(dateFrom.value).toBe(defaultRange.dateFrom) + expect(dateTo.value).toBe(defaultRange.dateTo) + expect(hiddenMode.value).toBe('default') + expect(hiddenExclude.value).toBe(serializeExcludeKeywords(defaultExcludeKeywords)) + expect(requestSubmit).toHaveBeenCalledTimes(2) + + // 清空排除 = 本月1号~昨日 + 排除关键词置空(与"重置"的区别是日期区间被显式填回) + fireEvent.click(screen.getByRole('button', { name: '清空排除' })) + expect(dateFrom.value).toBe(defaultRange.dateFrom) + expect(dateTo.value).toBe(defaultRange.dateTo) + expect(hiddenExclude.value).toBe('') + expect(hiddenMode.value).toBe('clear') + expect(requestSubmit).toHaveBeenCalledTimes(3) + } finally { + requestSubmit.mockRestore() + global.fetch = originalFetch + } +}) diff --git a/furniture-3d-system/src/app/supply-chain/management/logistics-arrival-signing-duration/LogisticsArrivalSigningDurationView.tsx b/furniture-3d-system/src/app/supply-chain/management/logistics-arrival-signing-duration/LogisticsArrivalSigningDurationView.tsx index 8322c66b..9e6b4060 100644 --- a/furniture-3d-system/src/app/supply-chain/management/logistics-arrival-signing-duration/LogisticsArrivalSigningDurationView.tsx +++ b/furniture-3d-system/src/app/supply-chain/management/logistics-arrival-signing-duration/LogisticsArrivalSigningDurationView.tsx @@ -9,6 +9,7 @@ import { LOGISTICS_ARRIVAL_SIGNING_DURATION_EXCLUDE_FIELDS, LOGISTICS_ARRIVAL_SIGNING_DURATION_MAX_EXCLUDE_KEYWORDS, LOGISTICS_ARRIVAL_SIGNING_DURATION_PAGE_SIZE_OPTIONS, + logisticsArrivalSigningDurationDefaultRange, mergeExcludeKeywords, parseExcludeKeywords, serializeExcludeKeywords, @@ -49,11 +50,14 @@ export function LogisticsArrivalSigningDurationView({ defaultExcludeKeywords, canManageExcludeKeywords, canDownload, + dateRangeCleared = false, }: { initialData: LogisticsArrivalSigningDurationPage defaultExcludeKeywords: LogisticsArrivalSigningDurationExcludeKeywords canManageExcludeKeywords: boolean canDownload: boolean + /** 「重置」提交过来的 URL 日期是空的:输入框保持空白(查询仍由服务端兜底成默认区间)。 */ + dateRangeCleared?: boolean }) { const { filters } = initialData const router = useRouter() @@ -75,14 +79,13 @@ export function LogisticsArrivalSigningDurationView({ const [keywordInput, setKeywordInput] = useState(filters.keyword) const [dateFromInput, setDateFromInput] = useState(filters.dateFrom) const [dateToInput, setDateToInput] = useState(filters.dateTo) - const [resetHint, setResetHint] = useState(false) const keywordCount = splitSearchKeywords(keywordInput).length - /** 重置筛选输入:清空签收日期与关键词(排除关键词有自己的清空/恢复默认),填好后点查询生效。 */ - const resetFilterInputs = () => { + /** 「重置」后日期输入框保持空白(服务端查询仍用默认区间兜底),直到用户自己填日期。 */ + const [datesCleared, setDatesCleared] = useState(dateRangeCleared) + if (dateRangeCleared && dateFromInput && dateToInput) { + // 首次进入时把服务端兜底的日期清掉,保持"没有任何筛选条件"的观感。 setDateFromInput('') setDateToInput('') - setKeywordInput('') - setResetHint(true) } const [excludeKeywordsMode, setExcludeKeywordsMode] = useState<'default' | 'clear'>(filters.excludeKeywordsMode ?? 'default') // 系统管理员维护的默认排除关键词(服务端 system config),也是选择器的候选池。 @@ -140,12 +143,49 @@ export function LogisticsArrivalSigningDurationView({ const [syncedFiltersKey, setSyncedFiltersKey] = useState(filtersKey) if (syncedFiltersKey !== filtersKey) { setSyncedFiltersKey(filtersKey) - setDateFromInput(filters.dateFrom) - setDateToInput(filters.dateTo) + // 「重置」清空全部条件后马上查询,服务端会把空日期兜底成本月 1 号~昨日; + // 这种状态下不要把兜底值回填到输入框,否则看起来"重置没生效"(日期又被填上了)。 + if (datesCleared) { + setDateFromInput('') + setDateToInput('') + } else { + setDateFromInput(filters.dateFrom) + setDateToInput(filters.dateTo) + } setKeywordInput(filters.keyword) setExcludeInput(serializeExcludeKeywords(filters.excludeKeywords)) setExcludeKeywordsMode(filters.excludeKeywordsMode ?? 'default') - setResetHint(false) + } + + /** + * 三个按钮的语义(用户口径): + * - 重置:没有任何筛选条件 —— 日期、关键词、排除关键词全部清空并立即查询(日期交给服务端兜底); + * - 恢复默认:签收日期回到本月 1 号~昨日、排除关键词回到系统默认配置,立即查询; + * - 清空排除:签收日期回到本月 1 号~昨日、排除关键词置空,立即查询。 + * 三者过去都只是"改输入、等用户点查询",结果看起来一样;现在各自立即生效且互不重复。 + */ + const applyFilterPreset = (preset: 'reset' | 'restore-default' | 'clear-exclude') => { + if (preset === 'reset') { + setDatesCleared(true) + setDateFromInput('') + setDateToInput('') + setKeywordInput('') + setExcludeInput('') + setExcludeKeywordsMode('clear') + } else { + const { dateFrom, dateTo } = logisticsArrivalSigningDurationDefaultRange() + setDatesCleared(false) + setDateFromInput(dateFrom) + setDateToInput(dateTo) + if (preset === 'restore-default') { + setExcludeInput(serializeExcludeKeywords(defaultKeywords)) + setExcludeKeywordsMode('default') + } else { + setExcludeInput('') + setExcludeKeywordsMode('clear') + } + } + setPendingFilterSubmit(true) } /** 仅系统管理员:把新关键词并入默认排除关键词并落库(服务端同样校验管理员身份)。 */ @@ -229,9 +269,9 @@ export function LogisticsArrivalSigningDurationView({
- + 至 - +