From 502f985147db1fa65508a946c1fd8eca339dcbbb Mon Sep 17 00:00:00 2001 From: skycurtain Date: Fri, 9 Oct 2026 23:20:05 +0800 Subject: [PATCH] =?UTF-8?q?feat(web):=20=E6=88=90=E5=91=98=E7=AB=AF?= =?UTF-8?q?=E4=BB=85=E6=80=BB=E8=A7=88=E8=BD=AE=E8=AF=A2=EF=BC=8C=E7=9B=B8?= =?UTF-8?q?=E5=AF=B9=E6=97=B6=E9=97=B4=E7=AA=97=E5=AE=9A=E6=A0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/web/src/queries/member.ts | 12 +++++++++-- apps/web/src/stores/member-range.ts | 22 +++++++++------------ apps/web/src/views/member/overview-view.vue | 5 ++++- docs/design/交互设计-成员视图.md | 2 ++ 4 files changed, 25 insertions(+), 16 deletions(-) diff --git a/apps/web/src/queries/member.ts b/apps/web/src/queries/member.ts index 8228555..80fe276 100644 --- a/apps/web/src/queries/member.ts +++ b/apps/web/src/queries/member.ts @@ -15,10 +15,19 @@ export function useMemberMe() { }); } -export function useMemberOverview(range: () => TimeRange) { +/** + * 总览聚合。仅「总览」传 `refetchInterval`(看板心智);日志/支付不轮询。 + */ +export function useMemberOverview( + range: () => TimeRange, + opts?: { refetchInterval?: number }, +) { return useQuery({ queryKey: () => qk.member.overview(range()), queryFn: () => fetchOverview(range()), + ...(opts?.refetchInterval != null + ? { refetchInterval: opts.refetchInterval } + : {}), }); } @@ -26,6 +35,5 @@ export function useMemberLogs(params: () => MemberLogQuery) { return useQuery({ queryKey: () => qk.member.logs(params()), queryFn: () => fetchLogs(params()), - staleTime: 0, }); } diff --git a/apps/web/src/stores/member-range.ts b/apps/web/src/stores/member-range.ts index 3429f58..75ba8b5 100644 --- a/apps/web/src/stores/member-range.ts +++ b/apps/web/src/stores/member-range.ts @@ -6,28 +6,24 @@ export type RangeValue = { from: string; to: string }; export type RangeKind = "recent-7" | "recent-30" | "fixed"; /** - * 成员端全局时间范围——总览 / 调用日志 / 支付参数共用(对账窗口不因切页打断)。 + * 成员端全局时间范围——总览 / 调用日志 / 支付参数共用。 + * 相对窗口在 setRange 时定格,避免每分钟改 key 打穿缓存; + * 「看板自动刷新」只属于总览页(refetchInterval),其余页按需请求。 */ export const useMemberRangeStore = defineStore("member-range", () => { const kind = ref("recent-30"); + const windowRange = ref(recentDaysRange(30)); const fixed = ref(recentDaysRange(30)); - const nowTick = ref(Date.now()); - setInterval(() => { - nowTick.value = Date.now(); - }, 60_000); - - const range = computed(() => { - void nowTick.value; - if (kind.value === "recent-7") return recentDaysRange(7); - if (kind.value === "recent-30") return recentDaysRange(30); - return fixed.value; - }); + const range = computed(() => + kind.value === "fixed" ? fixed.value : windowRange.value, + ); function setRange(value: RangeValue, nextKind: RangeKind) { kind.value = nextKind; if (nextKind === "fixed") fixed.value = value; + else windowRange.value = value; } - return { kind, fixed, range, setRange }; + return { kind, range, setRange }; }); diff --git a/apps/web/src/views/member/overview-view.vue b/apps/web/src/views/member/overview-view.vue index 87909bf..31f3d27 100644 --- a/apps/web/src/views/member/overview-view.vue +++ b/apps/web/src/views/member/overview-view.vue @@ -15,7 +15,10 @@ import { formatInt } from "@/utils/format.ts"; const router = useRouter(); const { range, onRangeChange } = useMemberRange(); -const overviewQuery = useMemberOverview(() => range.value); +/** 看板心智:仅总览自动刷新;其它成员页不轮询 */ +const overviewQuery = useMemberOverview(() => range.value, { + refetchInterval: 60_000, +}); const usage = computed(() => overviewQuery.data.value?.usage); const bySource = computed(() => overviewQuery.data.value?.by_source ?? []); diff --git a/docs/design/交互设计-成员视图.md b/docs/design/交互设计-成员视图.md index a841625..aca5591 100644 --- a/docs/design/交互设计-成员视图.md +++ b/docs/design/交互设计-成员视图.md @@ -38,6 +38,8 @@ **时间范围全局共享**:三页共用同一对账窗口——切页保留当前范围(菜单带 `from/to`),任一页改范围即全局生效;URL 可深链。支付参数页的「该时段涉及」:订阅看期间重叠 ∪ 进行中,充值看 `paid_at`。 +**数据刷新心智**:**仅总览**作看板自动刷新(约 60s);调用日志 / 支付参数为查询台,**不主动轮询**(改条件或手动重试才拉);相对时间窗在选定后定格,避免每分钟打穿缓存。 + 无账号设置、无审计、无健康带(成员世界定位边界,见《API契约-成员视图》导语)。 ## 3. 总览页