feat(admin): 数据总览按客迹视觉V1重做(周期卡/趋势双线/渠道条形/最近访问)

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
浅念
2026-09-17 12:18:37 +08:00
parent 132e2feb75
commit 8f20d8d9a2
8 changed files with 955 additions and 300 deletions
+23
View File
@@ -80,6 +80,29 @@ export function channelBadge(row: ChannelBadgeRow): string {
export const formatDateTime = (iso?: string | null): string =>
iso ? iso.replace('T', ' ').slice(0, 16) : '—'
/** 上海自然日今天(YYYY-MM-DD;后端 ISO 串同为上海时间,日期部分可直接比对) */
const shanghaiDate = (): string =>
new Intl.DateTimeFormat('en-CA', { timeZone: 'Asia/Shanghai' }).format(new Date())
/** YYYY-MM-DD 平移 n 天(按 UTC 毫秒运算,不受本地时区夏令时干扰) */
const shiftDate = (date: string, days: number): string => {
const d = new Date(`${date}T00:00:00Z`)
d.setUTCDate(d.getUTCDate() + days)
return d.toISOString().slice(0, 10)
}
/** ISO 时间 → 智能短时间:今天 HH:mm / 昨天 HH:mm / 更早 MM-DD HH:mm(空值 —)
* 后端已是上海时间,日期部分与 shanghaiDate 同口径比对,不做时区换算 */
export const formatSmartTime = (iso?: string | null): string => {
if (!iso) return '—'
const date = iso.slice(0, 10)
const time = iso.slice(11, 16)
const today = shanghaiDate()
if (date === today) return `今天 ${time}`
if (date === shiftDate(today, -1)) return `昨天 ${time}`
return `${date.slice(5)} ${time}`
}
/** 毫秒 → x天x小时x分x秒(智能省略:为 0 的单位不显示;全 0 / 不足 1 秒显示 0秒) */
export const formatStay = (ms?: number | null): string => {
const totalSec = Math.max(0, Math.floor((ms ?? 0) / 1000))