feat(web): 时间选择器增加「今天」快捷项
北京日界左闭右开;相对窗口可跨日自动重算。
This commit is contained in:
@@ -4,6 +4,7 @@ import dayjs from "dayjs";
|
|||||||
import { computed } from "vue";
|
import { computed } from "vue";
|
||||||
import {
|
import {
|
||||||
recentDaysRange,
|
recentDaysRange,
|
||||||
|
todayRange,
|
||||||
wallAsBeijingIso,
|
wallAsBeijingIso,
|
||||||
type RangeValue,
|
type RangeValue,
|
||||||
} from "@/utils/time.ts";
|
} from "@/utils/time.ts";
|
||||||
@@ -28,6 +29,10 @@ const pickerValue = computed<[Dayjs, Dayjs] | null>(() => {
|
|||||||
|
|
||||||
const presets = computed(() => {
|
const presets = computed(() => {
|
||||||
const builtIn = [
|
const builtIn = [
|
||||||
|
{
|
||||||
|
label: "今天",
|
||||||
|
value: rangeToDayjs(todayRange()),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: "最近 7 天",
|
label: "最近 7 天",
|
||||||
value: rangeToDayjs(recentDaysRange(7)),
|
value: rangeToDayjs(recentDaysRange(7)),
|
||||||
@@ -51,6 +56,13 @@ function rangeToDayjs(range: RangeValue): [Dayjs, Dayjs] {
|
|||||||
function detectKind(value: RangeValue): RangeKind {
|
function detectKind(value: RangeValue): RangeKind {
|
||||||
const fromD = dayjs(value.from).format("YYYY-MM-DD");
|
const fromD = dayjs(value.from).format("YYYY-MM-DD");
|
||||||
const toD = dayjs(value.to).format("YYYY-MM-DD");
|
const toD = dayjs(value.to).format("YYYY-MM-DD");
|
||||||
|
const today = todayRange();
|
||||||
|
if (
|
||||||
|
dayjs(today.from).format("YYYY-MM-DD") === fromD &&
|
||||||
|
dayjs(today.to).format("YYYY-MM-DD") === toD
|
||||||
|
) {
|
||||||
|
return "today";
|
||||||
|
}
|
||||||
for (const days of [7, 30] as const) {
|
for (const days of [7, 30] as const) {
|
||||||
const r = recentDaysRange(days);
|
const r = recentDaysRange(days);
|
||||||
if (
|
if (
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import { computed, onUnmounted, ref } from "vue";
|
import { computed, onUnmounted, ref } from "vue";
|
||||||
import { recentDaysRange } from "@/utils/time.ts";
|
import { recentDaysRange, todayRange } from "@/utils/time.ts";
|
||||||
|
|
||||||
export type RangeValue = { from: string; to: string };
|
export type RangeValue = { from: string; to: string };
|
||||||
export type RangeKind = "recent-7" | "recent-30" | "fixed";
|
export type RangeKind = "today" | "recent-7" | "recent-30" | "fixed";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 可跟随「今天」推进的时间范围:
|
* 可跟随「今天」推进的时间范围:
|
||||||
* - 最近 7/30 天:定时重算窗口(页面挂过夜也不会陈旧)
|
* - 今天 / 最近 7/30 天:定时重算窗口(页面挂过夜也不会陈旧)
|
||||||
* - fixed:自定义/订阅期间等绝对区间,窗口不挪
|
* - fixed:自定义/订阅期间等绝对区间,窗口不挪
|
||||||
*/
|
*/
|
||||||
export function useLiveTimeRange(initial: Exclude<RangeKind, "fixed"> = "recent-30") {
|
export function useLiveTimeRange(initial: Exclude<RangeKind, "fixed"> = "recent-30") {
|
||||||
@@ -21,6 +21,7 @@ export function useLiveTimeRange(initial: Exclude<RangeKind, "fixed"> = "recent-
|
|||||||
|
|
||||||
const range = computed<RangeValue>(() => {
|
const range = computed<RangeValue>(() => {
|
||||||
void nowTick.value;
|
void nowTick.value;
|
||||||
|
if (kind.value === "today") return todayRange();
|
||||||
if (kind.value === "recent-7") return recentDaysRange(7);
|
if (kind.value === "recent-7") return recentDaysRange(7);
|
||||||
if (kind.value === "recent-30") return recentDaysRange(30);
|
if (kind.value === "recent-30") return recentDaysRange(30);
|
||||||
return fixed.value;
|
return fixed.value;
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { computed, ref } from "vue";
|
|||||||
import { recentDaysRange } from "@/utils/time.ts";
|
import { recentDaysRange } from "@/utils/time.ts";
|
||||||
|
|
||||||
export type RangeValue = { from: string; to: string };
|
export type RangeValue = { from: string; to: string };
|
||||||
export type RangeKind = "recent-7" | "recent-30" | "fixed";
|
export type RangeKind = "today" | "recent-7" | "recent-30" | "fixed";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 成员端全局时间范围——总览 / 调用日志 / 支付参数共用。
|
* 成员端全局时间范围——总览 / 调用日志 / 支付参数共用。
|
||||||
|
|||||||
@@ -83,6 +83,14 @@ export function recentDaysRange(
|
|||||||
return { from: shanghaiIso(from), to: shanghaiIso(to) };
|
return { from: shanghaiIso(from), to: shanghaiIso(to) };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 快捷项:今天(北京时间日界,左闭右开) */
|
||||||
|
export function todayRange(nowMs: number = Date.now()): {
|
||||||
|
from: string;
|
||||||
|
to: string;
|
||||||
|
} {
|
||||||
|
return recentDaysRange(1, nowMs);
|
||||||
|
}
|
||||||
|
|
||||||
/** 统计口径标注:「2026-09-02 ~ 10-01」(北京时间日) */
|
/** 统计口径标注:「2026-09-02 ~ 10-01」(北京时间日) */
|
||||||
export function formatRangeCaption(fromIso: string, toIso: string): string {
|
export function formatRangeCaption(fromIso: string, toIso: string): string {
|
||||||
if (!fromIso || !toIso) return "—";
|
if (!fromIso || !toIso) return "—";
|
||||||
|
|||||||
@@ -9,7 +9,10 @@ import PageHeader from "@/components/common/page-header.vue";
|
|||||||
import TableRefresh from "@/components/common/table-refresh.vue";
|
import TableRefresh from "@/components/common/table-refresh.vue";
|
||||||
import TimeRangePicker from "@/components/common/time-range-picker.vue";
|
import TimeRangePicker from "@/components/common/time-range-picker.vue";
|
||||||
import QuotaText from "@/components/common/quota-text.vue";
|
import QuotaText from "@/components/common/quota-text.vue";
|
||||||
import { useLiveTimeRange } from "@/composables/use-live-time-range.ts";
|
import {
|
||||||
|
useLiveTimeRange,
|
||||||
|
type RangeKind,
|
||||||
|
} from "@/composables/use-live-time-range.ts";
|
||||||
import { useAdminLogs, useKeys, useMembers } from "@/queries/admin.ts";
|
import { useAdminLogs, useKeys, useMembers } from "@/queries/admin.ts";
|
||||||
import { formatDateTime } from "@/utils/time.ts";
|
import { formatDateTime } from "@/utils/time.ts";
|
||||||
import { formatInt } from "@/utils/format.ts";
|
import { formatInt } from "@/utils/format.ts";
|
||||||
@@ -177,7 +180,7 @@ let ro: ResizeObserver | null = null;
|
|||||||
|
|
||||||
function onRangeChange(
|
function onRangeChange(
|
||||||
v: { from: string; to: string },
|
v: { from: string; to: string },
|
||||||
k: "recent-7" | "recent-30" | "fixed",
|
k: RangeKind,
|
||||||
) {
|
) {
|
||||||
setRange(v, k);
|
setRange(v, k);
|
||||||
page.value = 1;
|
page.value = 1;
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import TableRefresh from "@/components/common/table-refresh.vue";
|
|||||||
import TimeRangePicker from "@/components/common/time-range-picker.vue";
|
import TimeRangePicker from "@/components/common/time-range-picker.vue";
|
||||||
import QuotaText from "@/components/common/quota-text.vue";
|
import QuotaText from "@/components/common/quota-text.vue";
|
||||||
import { useMemberRange } from "@/composables/use-member-range.ts";
|
import { useMemberRange } from "@/composables/use-member-range.ts";
|
||||||
|
import type { RangeKind } from "@/stores/member-range.ts";
|
||||||
import { useMemberLogs, useMemberOverview } from "@/queries/member.ts";
|
import { useMemberLogs, useMemberOverview } from "@/queries/member.ts";
|
||||||
import { formatDateTime } from "@/utils/time.ts";
|
import { formatDateTime } from "@/utils/time.ts";
|
||||||
import { formatInt, formatPaidAmount } from "@/utils/format.ts";
|
import { formatInt, formatPaidAmount } from "@/utils/format.ts";
|
||||||
@@ -177,7 +178,7 @@ watch([apiKeyId, billingSource, subscriptionId, typeFilter], () => {
|
|||||||
});
|
});
|
||||||
watch(page, () => pushQuery());
|
watch(page, () => pushQuery());
|
||||||
|
|
||||||
function onRangeChange(v: { from: string; to: string }, k: "recent-7" | "recent-30" | "fixed") {
|
function onRangeChange(v: { from: string; to: string }, k: RangeKind) {
|
||||||
setRange(v, k);
|
setRange(v, k);
|
||||||
page.value = 1;
|
page.value = 1;
|
||||||
pushQuery();
|
pushQuery();
|
||||||
|
|||||||
@@ -45,7 +45,7 @@
|
|||||||
### 3.1 时间范围选择器(核心组件)
|
### 3.1 时间范围选择器(核心组件)
|
||||||
|
|
||||||
- 一切统计的公共维度:**任意时段** + 快捷项
|
- 一切统计的公共维度:**任意时段** + 快捷项
|
||||||
- 快捷项:最近 7 天 / 最近 30 天(默认)/ **本订阅期间**(订阅期间作为预置选项——R10 裁撤后的「周期」形态;依赖支付记录,无登记的部署形态自动隐藏该项,见 §3.8)/ 自定义起止
|
- 快捷项:**今天** / 最近 7 天 / 最近 30 天(默认)/ **本订阅期间**(订阅期间作为预置选项——R10 裁撤后的「周期」形态;依赖支付记录,无登记的部署形态自动隐藏该项,见 §3.8)/ 自定义起止
|
||||||
- 所有统计数字标注其口径(「2026-09-02 ~ 10-01」),杜绝无口径数字
|
- 所有统计数字标注其口径(「2026-09-02 ~ 10-01」),杜绝无口径数字
|
||||||
|
|
||||||
### 3.2 数值呈现三约定
|
### 3.2 数值呈现三约定
|
||||||
|
|||||||
Reference in New Issue
Block a user