feat(miniprogram): Token 常量生成脚本 + useCanvasTokens hook (E2-1 Phase 2)
- 新增 scripts/generate-tokens.ts 从 SCSS 解析 CSS 变量生成 token-values.ts - 新增 useCanvasTokens hook 供 Canvas 组件适老化/医生端切换 - vitest include 扩展覆盖 scripts/__tests__/ - 10 单元测试覆盖 SCSS 解析和变量替换
This commit is contained in:
89
apps/miniprogram/src/hooks/useCanvasTokens.ts
Normal file
89
apps/miniprogram/src/hooks/useCanvasTokens.ts
Normal file
@@ -0,0 +1,89 @@
|
||||
import { useMemo } from 'react';
|
||||
import {
|
||||
TOKEN_VALUES,
|
||||
ELDER_TOKEN_OVERRIDES,
|
||||
DOCTOR_TOKEN_OVERRIDES,
|
||||
CANVAS_FONT_NORMAL,
|
||||
CANVAS_FONT_ELDER,
|
||||
} from '@/styles/token-values';
|
||||
import { useUIStore } from '@/stores/ui';
|
||||
import { useAuthStore } from '@/stores/auth';
|
||||
|
||||
/** Canvas 绘制用的合并 Token */
|
||||
export interface CanvasTokens {
|
||||
pri: string;
|
||||
tx: string;
|
||||
tx2: string;
|
||||
gridColor: string;
|
||||
fontH1: number;
|
||||
fontBody: number;
|
||||
fontBodySm: number;
|
||||
fontCap: number;
|
||||
yLabelFontSize: number;
|
||||
xLabelFontSize: number;
|
||||
tooltipFontSize: number;
|
||||
pointNormalRadius: number;
|
||||
pointAbnormalRadius: number;
|
||||
referenceBandColor: string;
|
||||
areaGradientStart: string;
|
||||
areaGradientEnd: string;
|
||||
lineColor: string;
|
||||
abnormalColor: string;
|
||||
}
|
||||
|
||||
function pxToInt(val: string | undefined, fallback: number): number {
|
||||
if (!val) return fallback;
|
||||
return parseInt(val, 10) || fallback;
|
||||
}
|
||||
|
||||
/** 为 Canvas 组件提供适老化/医生端的 Token 值 */
|
||||
export function useCanvasTokens(): CanvasTokens {
|
||||
const mode = useUIStore((s) => s.mode);
|
||||
const isDoctor = useAuthStore((s) => s.isDoctor());
|
||||
|
||||
return useMemo(() => {
|
||||
const tokens = TOKEN_VALUES as Record<string, string>;
|
||||
const docTokens = DOCTOR_TOKEN_OVERRIDES as Record<string, string>;
|
||||
const elderTokens = ELDER_TOKEN_OVERRIDES as Record<string, string>;
|
||||
|
||||
let pri = tokens['pri'] || '#C4623A';
|
||||
const tx = '#2D2A26';
|
||||
|
||||
if (isDoctor) {
|
||||
pri = docTokens['pri'] || pri;
|
||||
}
|
||||
|
||||
const isElder = mode === 'elder';
|
||||
const fontSet = isElder ? CANVAS_FONT_ELDER : CANVAS_FONT_NORMAL;
|
||||
|
||||
return {
|
||||
pri,
|
||||
tx,
|
||||
tx2: isElder ? '#5A554F' : (tokens['text-secondary'] || '#78716C'),
|
||||
gridColor: isElder ? '#E0DBD5' : '#F3F4F6',
|
||||
fontH1: pxToInt(tokens['font-h1'], 28),
|
||||
fontBody: pxToInt(
|
||||
isElder ? elderTokens['font-body'] : tokens['font-body'],
|
||||
isElder ? 22 : 16,
|
||||
),
|
||||
fontBodySm: pxToInt(
|
||||
isElder ? elderTokens['font-body-sm'] : tokens['font-body-sm'],
|
||||
isElder ? 19 : 14,
|
||||
),
|
||||
fontCap: pxToInt(
|
||||
isElder ? elderTokens['font-cap'] : tokens['font-cap'],
|
||||
isElder ? 18 : 13,
|
||||
),
|
||||
yLabelFontSize: fontSet.yLabel,
|
||||
xLabelFontSize: fontSet.xLabel,
|
||||
tooltipFontSize: fontSet.tooltip,
|
||||
pointNormalRadius: fontSet.pointNormal,
|
||||
pointAbnormalRadius: fontSet.pointAbnormal,
|
||||
referenceBandColor: isElder ? 'rgba(5,150,105,0.15)' : 'rgba(5,150,105,0.08)',
|
||||
areaGradientStart: isElder ? 'rgba(8,145,178,0.20)' : 'rgba(8,145,178,0.15)',
|
||||
areaGradientEnd: 'rgba(8,145,178,0.01)',
|
||||
lineColor: '#0891B2',
|
||||
abnormalColor: '#DC2626',
|
||||
};
|
||||
}, [mode, isDoctor]);
|
||||
}
|
||||
109
apps/miniprogram/src/styles/token-values.ts
Normal file
109
apps/miniprogram/src/styles/token-values.ts
Normal file
@@ -0,0 +1,109 @@
|
||||
// Auto-generated by scripts/generate-tokens.ts — DO NOT EDIT
|
||||
// Generated at: 2026-05-22T00:11:18.815Z
|
||||
|
||||
export const TOKEN_VALUES = {
|
||||
"pri": "#C4623A",
|
||||
"pri-l": "#F0DDD4",
|
||||
"pri-d": "#8B3E1F",
|
||||
"shadow-btn": "0 4px 16px rgba(196, 98, 58, 0.3)",
|
||||
"shadow-tab": "0 2px 8px rgba(196, 98, 58, 0.25)",
|
||||
"font-display": "72px",
|
||||
"font-hero": "48px",
|
||||
"font-h1": "28px",
|
||||
"font-h2": "22px",
|
||||
"font-body-lg": "18px",
|
||||
"font-body": "16px",
|
||||
"font-body-sm": "14px",
|
||||
"font-num": "30px",
|
||||
"font-num-lg": "34px",
|
||||
"font-cap": "13px",
|
||||
"font-nav": "18px",
|
||||
"font-micro": "11px",
|
||||
"line-height": "1.5",
|
||||
"touch-min": "48px",
|
||||
"btn-primary-h": "52px",
|
||||
"text-secondary": "#78716C",
|
||||
"card-bg": "#FFFFFF",
|
||||
"card-padding": "20px",
|
||||
"card-padding-sm": "16px",
|
||||
"card-padding-lg": "28px",
|
||||
"card-radius": "16px",
|
||||
"gap-2xs": "4px",
|
||||
"gap-xs": "8px",
|
||||
"gap-sm": "12px",
|
||||
"gap-md": "16px",
|
||||
"section-gap": "20px",
|
||||
"gap-lg": "24px",
|
||||
"gap-xl": "32px",
|
||||
"gap-2xl": "48px",
|
||||
"page-padding": "20px",
|
||||
"input-height": "56px",
|
||||
"tabbar-space": "100px",
|
||||
"touch-feedback-opacity": "0.85",
|
||||
"tag-font-size": "11px",
|
||||
"tag-padding-v": "3px",
|
||||
"tag-padding-h": "8px"
|
||||
} as const;
|
||||
|
||||
export const ELDER_TOKEN_OVERRIDES = {
|
||||
"font-display": "80px",
|
||||
"font-hero": "56px",
|
||||
"font-h1": "32px",
|
||||
"font-h2": "25px",
|
||||
"font-body-lg": "22px",
|
||||
"font-body": "22px",
|
||||
"font-body-sm": "19px",
|
||||
"font-num": "34px",
|
||||
"font-num-lg": "40px",
|
||||
"font-cap": "18px",
|
||||
"font-nav": "22px",
|
||||
"font-micro": "17px",
|
||||
"line-height": "1.7",
|
||||
"touch-min": "56px",
|
||||
"btn-primary-h": "60px",
|
||||
"text-secondary": "#5A554F",
|
||||
"card-padding": "28px",
|
||||
"card-padding-sm": "20px",
|
||||
"card-padding-lg": "36px",
|
||||
"card-radius": "20px",
|
||||
"gap-2xs": "6px",
|
||||
"gap-xs": "12px",
|
||||
"gap-sm": "16px",
|
||||
"gap-md": "20px",
|
||||
"section-gap": "28px",
|
||||
"gap-lg": "32px",
|
||||
"gap-xl": "40px",
|
||||
"gap-2xl": "56px",
|
||||
"page-padding": "28px",
|
||||
"input-height": "64px",
|
||||
"tabbar-space": "120px",
|
||||
"touch-feedback-opacity": "0.8",
|
||||
"tag-font-size": "13px",
|
||||
"tag-padding-v": "5px",
|
||||
"tag-padding-h": "12px"
|
||||
} as const;
|
||||
|
||||
export const DOCTOR_TOKEN_OVERRIDES = {
|
||||
"pri": "#3A6B8C",
|
||||
"pri-l": "#D4E5F0",
|
||||
"pri-d": "#2A4F6A",
|
||||
"shadow-btn": "0 4px 16px rgba(58, 107, 140, 0.3)",
|
||||
"shadow-tab": "0 2px 8px rgba(58, 107, 140, 0.25)"
|
||||
} as const;
|
||||
|
||||
// Canvas 专用:字号(数字,单位 px)
|
||||
export const CANVAS_FONT_NORMAL = {
|
||||
yLabel: 10,
|
||||
xLabel: 10,
|
||||
tooltip: 12,
|
||||
pointNormal: 3,
|
||||
pointAbnormal: 5,
|
||||
} as const;
|
||||
|
||||
export const CANVAS_FONT_ELDER = {
|
||||
yLabel: 14,
|
||||
xLabel: 14,
|
||||
tooltip: 16,
|
||||
pointNormal: 5,
|
||||
pointAbnormal: 8,
|
||||
} as const;
|
||||
Reference in New Issue
Block a user