Files
NextEdu/src/shared/lib/utils.ts
SpecialX 5ff7ab9e72 fix(teacher): 统一详情页返回路径与中英文文案 (P1-3+P2-1)
P1-3: empty-state 默认按钮 variant 改为 outline 并新增 variant prop;button.tsx 导出 ButtonProps;统一 5 个详情页返回路径为 ghost+ArrowLeft+文字标签;course-plan-detail raw a 改为 Link。P2-1: formatLongDate 默认 locale 改为 zh-CN,weekday 改为 short;返回按钮文案中文化;course-plan-detail 全量中文化;grades/analytics 标题中文化。验证:tsc 0 错误,lint 0 错误,架构图 004/005 已同步。
2026-06-22 13:52:26 +08:00

94 lines
2.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { type ClassValue, clsx } from "clsx";
import { twMerge } from "tailwind-merge";
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
export function formatDate(date: string | Date, locale: string = "zh-CN") {
return new Intl.DateTimeFormat(locale, {
year: "numeric",
month: "short",
day: "numeric",
}).format(new Date(date))
}
/**
* 格式化日期+时间(含小时、分钟)。
* 用于替代各处重复的 `new Date(x).toLocaleString("zh-CN", {...})` 调用。
*/
export function formatDateTime(date: string | Date, locale: string = "zh-CN") {
return new Intl.DateTimeFormat(locale, {
year: "numeric",
month: "2-digit",
day: "2-digit",
hour: "2-digit",
minute: "2-digit",
}).format(new Date(date))
}
/**
* 格式化为长日期(含星期、完整月份名)。
* 用于替代 `new Date(x).toLocaleDateString("en-US", { weekday: "long", year: "numeric", month: "long", day: "numeric" })`。
* 默认使用中文 locale输出形如「2026年6月20日 周一」。
*/
export function formatLongDate(date: string | Date, locale: string = "zh-CN") {
return new Intl.DateTimeFormat(locale, {
weekday: "short",
year: "numeric",
month: "long",
day: "numeric",
}).format(new Date(date))
}
/** Next.js App Router 搜索参数类型 */
export type SearchParams = { [key: string]: string | string[] | undefined }
/** 从 SearchParams 中安全提取单个字符串值 */
export function getSearchParam(params: SearchParams, key: string): string | undefined {
const v = params[key]
if (typeof v === "string") return v
if (Array.isArray(v)) return v[0]
return undefined
}
/** 格式化数字null/undefined/非有限数返回 "-" */
export function formatNumber(v: number | null | undefined, digits = 1): string {
if (typeof v !== "number" || !Number.isFinite(v)) return "-"
return v.toFixed(digits)
}
/**
* 从姓名生成头像占位用的首字母(最多 2 个字符)。
* 用于 AvatarFallback 组件。
* - 含空格的姓名:取各单词首字母拼接(如 "John Doe" -> "JD"
* - 无空格的姓名:取前 2 个字符(如 "张三" -> "张三"
* - 空值:返回 "U"User 通用占位)
*/
export function getInitials(name: string | null | undefined): string {
if (!name) return "U"
const trimmed = name.trim()
if (!trimmed) return "U"
if (trimmed.includes(" ")) {
return trimmed
.split(/\s+/)
.map((n) => n[0])
.join("")
.toUpperCase()
.slice(0, 2)
}
return trimmed.slice(0, 2).toUpperCase()
}
/**
* 格式化日期为文件名安全的 YYYY-MM-DD 格式。
* 用于导出文件名(如 `grades_export_2026-06-20.xlsx`)。
* @param d 日期对象,默认为当前时间
*/
export function formatDateForFile(d: Date = new Date()): string {
const y = d.getFullYear()
const m = String(d.getMonth() + 1).padStart(2, "0")
const day = String(d.getDate()).padStart(2, "0")
return `${y}-${m}-${day}`
}