Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/components/NodeDetail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { Skeleton } from "@/components/ui/skeleton"
import { Country, Status } from "@/components/NodeCard"
import { api, type Node } from "@/lib/api"
import {
axisBytes, axisTop, bytes, clockFor, despike, quarters, cpuName, CYCLES, FOREVER, money, osName, rate,
axisBytes, axisTop, bytes, clockFor, cycle, despike, quarters, cpuName, FOREVER, money, osName, rate,
timeTicks,
} from "@/lib/format"

Expand Down Expand Up @@ -365,7 +365,7 @@ export function NodeDetail({ node }: { node: Node }) {
label="续费"
value={[
node.price > 0
? `${money(node.price, node.currency)} / ${CYCLES[node.billing_cycle] ?? node.billing_cycle}`
? `${money(node.price, node.currency)} / ${cycle(node.billing_cycle)}`
: "免费",
node.expires_at ? `${node.expires_at} 到期` : FOREVER,
].join(" · ")}
Expand Down
5 changes: 4 additions & 1 deletion src/lib/format.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
//
// Nothing imports it, so the bundle never includes it.
import {
axisBytes, axisTop, bytes, cpuName, daysUntil, despike, osName, pair, quarters, timeTicks, uptime,
axisBytes, axisTop, bytes, cpuName, cycle, daysUntil, despike, osName, pair, quarters, timeTicks, uptime,
} from "./format.ts"

let failed = 0
Expand Down Expand Up @@ -117,6 +117,9 @@ eq(uptime(2 * 86400 + 5 * 3600), "2 天 5 小时", "超过一天不再写分钟"
eq(despike([180, 181, 180, 180, 181, 180, 180]), [180, 181, 180, 180, 181, 180, 180], "1 ms 抖动原样保留")
}

// 旧 hub 存名称,新 hub 把其余长度存成 `<n>m`;两种写法同一个长度读法一致。
eq(["yearly", "12m", "60m", "18m", "once", "weekly"].map(cycle), ["年付", "年付", "5 年付", "18 个月付", "一次性", "weekly"], "付款周期")

eq(osName("Debian GNU/Linux 12 (bookworm)"), "Debian 12", "发行版名去掉代号")
eq(cpuName("Intel(R) Xeon(R) CPU E5-2680 8-Core Processor"), "Intel Xeon E5-2680", "CPU 名去掉商标和核数")

Expand Down
19 changes: 11 additions & 8 deletions src/lib/format.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,14 +84,17 @@ export function money(amount: number, currency: string): string {
return `${SYMBOLS[currency] ?? ""}${amount.toFixed(2)}${SYMBOLS[currency] ? "" : ` ${currency}`}`
}

export const CYCLES: Record<string, string> = {
monthly: "月付",
quarterly: "季付",
semiannual: "半年付",
yearly: "年付",
biennial: "两年付",
triennial: "三年付",
once: "一次性",
// Hub 1.3.0 and earlier store only these names; later hubs store any other
// length as `<n>m`.
const NAMED_CYCLES: Record<string, number> = { monthly: 1, quarterly: 3, semiannual: 6, yearly: 12, biennial: 24, triennial: 36 }
const CYCLE_WORDS: Record<number, string> = { 1: "月付", 3: "季付", 6: "半年付", 12: "年付" }

/** How a billing cycle reads: 月付, 5 年付, 18 个月付, 一次性. */
export function cycle(billing: string): string {
if (billing === "once") return "一次性"
const months = NAMED_CYCLES[billing] ?? Number(/^(\d+)m$/.exec(billing)?.[1])
if (!months) return billing
return CYCLE_WORDS[months] ?? (months % 12 ? `${months} 个月付` : `${months / 12} 年付`)
}

// Hoisted out of `clock`: recharts calls a tickFormatter for every sample when
Expand Down
Loading