diff --git a/bun.lockb b/bun.lockb
index 9041504..3438f77 100755
Binary files a/bun.lockb and b/bun.lockb differ
diff --git a/package.json b/package.json
index 9d60199..32508b2 100644
--- a/package.json
+++ b/package.json
@@ -34,6 +34,7 @@
"clsx": "^2.1.1",
"country-flag-icons": "^1.5.13",
"d3-geo": "^3.1.1",
+ "dayjs": "^1.11.13",
"framer-motion": "^12.0.0-alpha.2",
"i18next": "^24.1.0",
"lucide-react": "^0.460.0",
diff --git a/src/components/PlanInfo.tsx b/src/components/PlanInfo.tsx
new file mode 100644
index 0000000..5d5b7c3
--- /dev/null
+++ b/src/components/PlanInfo.tsx
@@ -0,0 +1,69 @@
+import { PublicNoteData, cn } from "@/lib/utils"
+
+export default function PlanInfo({ parsedData }: { parsedData: PublicNoteData }) {
+ if (!parsedData || !parsedData.planDataMod) {
+ return null
+ }
+ return (
+
+ {parsedData.planDataMod.bandwidth}
+
+ {parsedData.planDataMod.trafficVol}
+
+ IPv4
+
+ IPv6
+
+ {parsedData.planDataMod.networkRoute.split(",").map((route) => {
+ return route
+ })}
+
+ {parsedData.planDataMod.extra.split(",").map((extra) => {
+ return extra
+ })}
+
- 剩余时间: {isNeverExpire ? "永久" : daysLeft + "天"} -
- {parsedData.billingDataMod.amount && - parsedData.billingDataMod.amount !== "0" && - parsedData.billingDataMod.amount !== "-1" ? ( -- 价格: {parsedData.billingDataMod.amount}/{parsedData.billingDataMod.cycle} -
- ) : parsedData.billingDataMod.amount === "0" ? ( -免费
- ) : parsedData.billingDataMod.amount === "-1" ? ( -按量收费
- ) : null} - > - ) : ( - <> -- 已过期: {daysLeft * -1} 天 -
- {parsedData.billingDataMod.amount && - parsedData.billingDataMod.amount !== "0" && - parsedData.billingDataMod.amount !== "-1" ? ( -- 价格: {parsedData.billingDataMod.amount}/{parsedData.billingDataMod.cycle} -
- ) : parsedData.billingDataMod.amount === "0" ? ( -免费
- ) : parsedData.billingDataMod.amount === "-1" ? ( -按量收费
- ) : null} - > - ))} - {parsedData?.planDataMod && ( -- {parsedData.planDataMod.bandwidth} -
- )} - {parsedData.planDataMod.trafficVol !== "" && ( -- {parsedData.planDataMod.trafficVol} -
- )} -+
{name}
- {parsedData?.billingDataMod && - (daysLeft >= 0 ? ( - <> -- 剩余时间: {isNeverExpire ? "永久" : daysLeft + "天"} -
- {parsedData.billingDataMod.amount && - parsedData.billingDataMod.amount !== "0" && - parsedData.billingDataMod.amount !== "-1" ? ( -- 价格: {parsedData.billingDataMod.amount}/{parsedData.billingDataMod.cycle} -
- ) : parsedData.billingDataMod.amount === "0" ? ( -免费
- ) : parsedData.billingDataMod.amount === "-1" ? ( -按量收费
- ) : null} - > - ) : ( - <> -- 已过期: {daysLeft * -1} 天 -
- {parsedData.billingDataMod.amount && - parsedData.billingDataMod.amount !== "0" && - parsedData.billingDataMod.amount !== "-1" ? ( -- 价格: {parsedData.billingDataMod.amount}/{parsedData.billingDataMod.cycle} -
- ) : parsedData.billingDataMod.amount === "0" ? ( -免费
- ) : parsedData.billingDataMod.amount === "-1" ? ( -按量收费
- ) : null} - > - ))} - {parsedData?.planDataMod && ( -- {parsedData.planDataMod.bandwidth} -
- )} - {parsedData.planDataMod.trafficVol !== "" && ( -- {parsedData.planDataMod.trafficVol} -
- )} -- 剩余时间: {isNeverExpire ? "永久" : daysLeft + "天"} -
- {parsedData.billingDataMod.amount && - parsedData.billingDataMod.amount !== "0" && - parsedData.billingDataMod.amount !== "-1" ? ( -- 价格: {parsedData.billingDataMod.amount}/{parsedData.billingDataMod.cycle} -
- ) : parsedData.billingDataMod.amount === "0" ? ( -免费
- ) : parsedData.billingDataMod.amount === "-1" ? ( -按量收费
- ) : null} - > - ) : ( - <> -- 已过期: {daysLeft * -1} 天 -
- {parsedData.billingDataMod.amount && - parsedData.billingDataMod.amount !== "0" && - parsedData.billingDataMod.amount !== "-1" ? ( -- 价格: {parsedData.billingDataMod.amount}/{parsedData.billingDataMod.cycle} -
- ) : parsedData.billingDataMod.amount === "0" ? ( -免费
- ) : parsedData.billingDataMod.amount === "-1" ? ( -按量收费
- ) : null} - > - ))} - {parsedData?.planDataMod && ( -- {parsedData.planDataMod.bandwidth} -
- )} - {parsedData.planDataMod.trafficVol !== "" && ( -- {parsedData.planDataMod.trafficVol} -
- )} -- 剩余时间: {isNeverExpire ? "永久" : daysLeft + "天"} -
- {parsedData.billingDataMod.amount && - parsedData.billingDataMod.amount !== "0" && - parsedData.billingDataMod.amount !== "-1" ? ( -- 价格: {parsedData.billingDataMod.amount}/{parsedData.billingDataMod.cycle} -
- ) : parsedData.billingDataMod.amount === "0" ? ( -免费
- ) : parsedData.billingDataMod.amount === "-1" ? ( -按量收费
- ) : null} - > - ) : ( - <> -- 已过期: {daysLeft * -1} 天 -
- {parsedData.billingDataMod.amount && - parsedData.billingDataMod.amount !== "0" && - parsedData.billingDataMod.amount !== "-1" ? ( -- 价格: {parsedData.billingDataMod.amount}/{parsedData.billingDataMod.cycle} -
- ) : parsedData.billingDataMod.amount === "0" ? ( -免费
- ) : parsedData.billingDataMod.amount === "-1" ? ( -按量收费
- ) : null} - > - ))} - {parsedData?.planDataMod && ( -- {parsedData.planDataMod.bandwidth} -
- )} - {parsedData.planDataMod.trafficVol !== "" && ( -- {parsedData.planDataMod.trafficVol} -
- )} -+ 价格: {parsedData.billingDataMod.amount}/{parsedData.billingDataMod.cycle} +
+ ) : parsedData.billingDataMod.amount === "0" ? ( +免费
+ ) : parsedData.billingDataMod.amount === "-1" ? ( +按量收费
+ ) : null} ++ 已过期: {daysLeftObject.days * -1} 天 +
+ {parsedData.billingDataMod.amount && + parsedData.billingDataMod.amount !== "0" && + parsedData.billingDataMod.amount !== "-1" ? ( ++ 价格: {parsedData.billingDataMod.amount}/{parsedData.billingDataMod.cycle} +
+ ) : parsedData.billingDataMod.amount === "0" ? ( +免费
+ ) : parsedData.billingDataMod.amount === "-1" ? ( +按量收费
+ ) : null} + > + ) +} diff --git a/src/lib/utils.ts b/src/lib/utils.ts index d04c9be..3777f5b 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -1,5 +1,6 @@ import { NezhaServer } from "@/types/nezha-api" import { type ClassValue, clsx } from "clsx" +import dayjs from "dayjs" import { twMerge } from "tailwind-merge" export function cn(...inputs: ClassValue[]) { @@ -46,6 +47,112 @@ export function formatNezhaInfo(now: number, serverInfo: NezhaServer) { } } +export function getDaysBetweenDatesWithAutoRenewal({ + autoRenewal, + cycle, + startDate, + endDate, +}: BillingData): { days: number; cycleLabel: string; remainingPercentage: number } { + let months = 1 + // 套餐资费 + let cycleLabel = cycle + + switch (cycle.toLowerCase()) { + case "月": + case "m": + case "mo": + case "month": + case "monthly": + cycleLabel = "月" + months = 1 + break + case "年": + case "y": + case "yr": + case "year": + case "annual": + cycleLabel = "年" + months = 12 + break + case "季": + case "quarterly": + cycleLabel = "季" + months = 3 + break + case "半": + case "半年": + case "h": + case "half": + case "semi-annually": + cycleLabel = "半年" + months = 6 + break + default: + cycleLabel = cycle + break + } + + const nowTime = new Date().getTime() + const endTime = dayjs(endDate).valueOf() + + if (autoRenewal !== "1") { + return { + days: getDaysBetweenDates(endDate, new Date(nowTime).toISOString()), + cycleLabel: cycleLabel, + remainingPercentage: + getDaysBetweenDates(endDate, new Date(nowTime).toISOString()) / + dayjs(endDate).diff(startDate, "day") > + 1 + ? 1 + : getDaysBetweenDates(endDate, new Date(nowTime).toISOString()) / + dayjs(endDate).diff(startDate, "day"), + } + } + + const nextTime = getNextCycleTime(endTime, months, nowTime) + const diff = dayjs(nextTime).diff(dayjs(), "day") + 1 + const remainingPercentage = diff / (30 * months) > 1 ? 1 : diff / (30 * months) + + console.log( + "nextTime", + nextTime, + "diff", + diff, + "month", + months, + "remainingPercentage", + remainingPercentage, + ) + + return { + days: diff, + cycleLabel: cycleLabel, + remainingPercentage: remainingPercentage, + } +} + +// Thanks to hi2shark for the code +// https://github.com/hi2shark/nazhua/blob/main/src/utils/date.js#L86 +export function getNextCycleTime(startDate: number, months: number, specifiedDate: number): number { + const start = dayjs(startDate) + const checkDate = dayjs(specifiedDate) + + if (!start.isValid() || months <= 0) { + throw new Error("参数无效:请检查起始日期、周期月份数和指定日期。") + } + + let nextDate = start + + // 循环增加周期直到大于当前日期 + let whileStatus = true + while (whileStatus) { + nextDate = nextDate.add(months, "month") + whileStatus = nextDate.valueOf() <= checkDate.valueOf() + } + + return nextDate.valueOf() // 返回时间毫秒数 +} + export function getDaysBetweenDates(date1: string, date2: string): number { const oneDay = 24 * 60 * 60 * 1000 // 一天的毫秒数 const firstDate = new Date(date1) @@ -137,7 +244,7 @@ interface PlanData { extra: string } -interface PublicNoteData { +export interface PublicNoteData { billingDataMod?: BillingData planDataMod?: PlanData }