refactor(mp): CSS 变量主题 + 登录页改造 — UI 优化 Phase 0-2

Phase 0: 建立 design token 体系
- tokens.scss 新增 --tk-pri/--tk-pri-l/--tk-pri-d/--tk-shadow-btn/--tk-shadow-tab
- .doctor-mode 覆盖为靛蓝色系,.elder-mode 非线性放大字号
- variables.scss 新增医生端色彩 + 阴影变量

Phase 1: 组件库 + 页面全局替换
- 75 个页面 SCSS $pri → var(--tk-pri) 全量替换
- 11 个新 UI 组件(PrimaryButton/TabFilter/FormInput/ProgressRing 等)
- 8 个现有组件 SCSS 更新
- 18 个医生端页面 useElderClass → useDoctorClass
- PageHeader 匹配原型 NavBar 规格

Phase 2: 登录页重写
- Logo: 方形+ → 圆形渐变 H
- 登录方式: 纯微信 → 账号密码 + 微信一键登录
- 新增 credentialLogin API + store action
- 字号/间距严格匹配原型 mp-01-login.html
This commit is contained in:
iven
2026-05-16 21:29:13 +08:00
parent 1786f0d707
commit 95e219ad5a
124 changed files with 2306 additions and 1142 deletions

View File

@@ -5,18 +5,18 @@
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
padding: 120px 40px; padding: var(--tk-gap-2xl) var(--tk-gap-xl);
} }
.empty-state-icon-wrap { .empty-state-icon-wrap {
width: 120px; width: var(--tk-gap-2xl);
height: 120px; height: var(--tk-gap-2xl);
border-radius: 50%; border-radius: 50%;
background: $surface-alt; background: $surface-alt;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
margin-bottom: 24px; margin-bottom: var(--tk-gap-lg);
} }
.empty-state-icon-char { .empty-state-icon-char {
@@ -29,19 +29,19 @@
.empty-state-text { .empty-state-text {
font-size: var(--tk-font-num); font-size: var(--tk-font-num);
color: $tx2; color: $tx2;
margin-bottom: 8px; margin-bottom: var(--tk-gap-xs);
} }
.empty-state-hint { .empty-state-hint {
font-size: var(--tk-font-h2); font-size: var(--tk-font-h2);
color: var(--tk-text-secondary); color: var(--tk-text-secondary);
margin-bottom: 32px; margin-bottom: var(--tk-gap-xl);
} }
.empty-state-action { .empty-state-action {
background: $pri; background: var(--tk-pri);
border-radius: 40px; border-radius: 40px;
padding: 16px 48px; padding: var(--tk-gap-md) var(--tk-gap-2xl);
} }
.empty-state-action-text { .empty-state-action-text {

View File

@@ -13,7 +13,7 @@
width: 64px; width: 64px;
height: 64px; height: 64px;
border-radius: 32px; border-radius: 32px;
background: $pri-l; background: var(--tk-pri-l);
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
@@ -24,7 +24,7 @@
font-family: 'Georgia', 'Times New Roman', serif; font-family: 'Georgia', 'Times New Roman', serif;
font-size: var(--tk-font-h1); font-size: var(--tk-font-h1);
font-weight: 600; font-weight: 600;
color: $pri-d; color: var(--tk-pri-d);
} }
.error-title { .error-title {
@@ -41,7 +41,7 @@
} }
.error-retry-btn { .error-retry-btn {
background: $pri; background: var(--tk-pri);
border-radius: $r-sm; border-radius: $r-sm;
padding: 14px 48px; padding: 14px 48px;
} }

View File

@@ -5,25 +5,25 @@
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
padding: 120px 40px; padding: var(--tk-gap-2xl) var(--tk-gap-xl);
} }
.error-state-icon { .error-state-icon {
font-size: var(--tk-font-display); font-size: var(--tk-font-display);
margin-bottom: 24px; margin-bottom: var(--tk-gap-lg);
} }
.error-state-text { .error-state-text {
font-size: var(--tk-font-body-lg); font-size: var(--tk-font-body-lg);
color: $tx2; color: $tx2;
margin-bottom: 32px; margin-bottom: var(--tk-gap-xl);
text-align: center; text-align: center;
} }
.error-state-retry { .error-state-retry {
background: $pri; background: var(--tk-pri);
border-radius: 40px; border-radius: 40px;
padding: 16px 48px; padding: var(--tk-gap-md) var(--tk-gap-2xl);
} }
.error-state-retry-text { .error-state-retry-text {

View File

@@ -48,7 +48,7 @@
display: inline-block; display: inline-block;
height: 48px; height: 48px;
padding: 0 32px; padding: 0 32px;
background: $pri; background: var(--tk-pri);
border-radius: $r-pill; border-radius: $r-pill;
@include flex-center; @include flex-center;

View File

@@ -12,7 +12,7 @@
width: 48px; width: 48px;
height: 48px; height: 48px;
border: 4px solid $bd; border: 4px solid $bd;
border-top-color: $pri; border-top-color: var(--tk-pri);
border-radius: 50%; border-radius: 50%;
animation: spin 0.8s linear infinite; animation: spin 0.8s linear infinite;
margin-bottom: 20px; margin-bottom: 20px;

View File

@@ -17,7 +17,7 @@
&--active { &--active {
.seg-tab__text { .seg-tab__text {
color: $pri; color: var(--tk-pri);
font-weight: bold; font-weight: bold;
} }
@@ -28,7 +28,7 @@
left: 30%; left: 30%;
right: 30%; right: 30%;
height: 4px; height: 4px;
background: $pri; background: var(--tk-pri);
border-radius: $r-xs; border-radius: $r-xs;
} }
} }

View File

@@ -44,7 +44,7 @@
z-index: 1; z-index: 1;
&.step-current { &.step-current {
background: $pri; background: var(--tk-pri);
color: white; color: white;
} }
@@ -61,7 +61,7 @@
text-align: center; text-align: center;
&.step-current { &.step-current {
color: $pri; color: var(--tk-pri);
font-weight: bold; font-weight: bold;
} }

View File

@@ -15,7 +15,7 @@
.week-arrow { .week-arrow {
font-size: var(--tk-font-body-lg); font-size: var(--tk-font-body-lg);
color: $pri; color: var(--tk-pri);
padding: 0 16px; padding: 0 16px;
} }
@@ -52,7 +52,7 @@
} }
.cell-today { .cell-today {
color: $pri; color: var(--tk-pri);
font-weight: bold; font-weight: bold;
} }
@@ -68,7 +68,7 @@
} }
.cell-selected { .cell-selected {
background: $pri; background: var(--tk-pri);
border-radius: $r-sm; border-radius: $r-sm;
.cell-date { color: white; } .cell-date { color: white; }

View File

@@ -4,9 +4,10 @@
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
height: var(--tk-touch-min); height: 44px;
padding: 0 var(--tk-page-padding); padding: 0 var(--tk-page-padding);
background: $bg; background: $bg;
border-bottom: 1px solid $bd-l;
z-index: 10; z-index: 10;
&--sticky { &--sticky {
@@ -17,7 +18,7 @@
&__left { &__left {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 8px; gap: var(--tk-gap-xs);
min-width: 0; min-width: 0;
flex: 1; flex: 1;
} }
@@ -27,19 +28,19 @@
align-items: center; align-items: center;
justify-content: center; justify-content: center;
width: 36px; width: 36px;
height: 36px; height: 44px;
min-height: var(--tk-touch-min);
} }
&__back-icon { &__back-icon {
font-size: 24px; font-size: var(--tk-font-h2);
color: $tx; color: var(--tk-pri);
line-height: 1; line-height: 1;
} }
&__title { &__title {
font-size: var(--tk-font-h1); font-family: Georgia, 'Times New Roman', serif;
font-weight: 600; font-size: var(--tk-font-nav);
font-weight: 700;
color: $tx; color: $tx;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
@@ -49,7 +50,7 @@
&__right { &__right {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 12px; gap: var(--tk-gap-sm);
flex-shrink: 0; flex-shrink: 0;
} }
} }

View File

@@ -11,7 +11,7 @@
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
padding: 8px 20px; padding: var(--tk-gap-xs) var(--tk-section-gap);
border-radius: $r-sm; border-radius: $r-sm;
background: var(--tk-card-bg); background: var(--tk-card-bg);
border: 1px solid $bd; border: 1px solid $bd;

View File

@@ -6,16 +6,16 @@
&__input-wrap { &__input-wrap {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 8px; gap: var(--tk-gap-xs);
background: var(--tk-card-bg); background: var(--tk-card-bg);
border-radius: var(--tk-card-radius); border-radius: var(--tk-card-radius);
padding: 0 16px; padding: 0 var(--tk-gap-md);
height: var(--tk-touch-min); height: var(--tk-input-height);
box-shadow: $shadow-sm; box-shadow: $shadow-sm;
} }
&__icon { &__icon {
font-size: 16px; font-size: var(--tk-font-body-sm);
flex-shrink: 0; flex-shrink: 0;
} }

View File

@@ -0,0 +1,52 @@
@import '../../../styles/variables.scss';
.alert-card {
border-radius: $r;
padding: var(--tk-gap-lg);
margin-bottom: var(--tk-gap-md);
// 渐变型 — 智能提醒
&--gradient {
background: linear-gradient(135deg, var(--tk-pri) 0%, var(--tk-pri-d) 100%);
color: $white;
}
// 左边框型 — AI 建议
&--left-border {
background: $acc-l;
border-left: 4px solid $acc;
}
// 全边框型 — 温馨提示
&--bordered {
background: $wrn-l;
border-radius: $r-sm;
}
&__header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: var(--tk-gap-xs);
}
&__title {
font-size: var(--tk-font-body);
font-weight: 600;
}
&--left-border &__title {
color: $acc;
}
&__subtitle {
font-size: var(--tk-font-micro);
opacity: 0.7;
}
&__body {
font-size: var(--tk-font-cap);
color: $tx2;
line-height: 1.6;
}
}

View File

@@ -0,0 +1,41 @@
import React, { ReactNode } from 'react';
import { View, Text } from '@tarojs/components';
import './index.scss';
type AlertVariant = 'gradient' | 'left-border' | 'bordered';
interface AlertCardProps {
variant?: AlertVariant;
title?: string;
subtitle?: string;
children?: ReactNode;
className?: string;
}
const AlertCard: React.FC<AlertCardProps> = ({
variant = 'left-border',
title,
subtitle,
children,
className = '',
}) => {
const cls = [
'alert-card',
`alert-card--${variant}`,
className,
].filter(Boolean).join(' ');
return (
<View className={cls}>
{title && (
<View className='alert-card__header'>
<Text className='alert-card__title'>{title}</Text>
{subtitle && <Text className='alert-card__subtitle'>{subtitle}</Text>}
</View>
)}
{children ?? <Text className='alert-card__body'>{subtitle}</Text>}
</View>
);
};
export default React.memo(AlertCard);

View File

@@ -0,0 +1,37 @@
@import '../../../styles/variables.scss';
.chat-bubble-wrap {
display: flex;
flex-direction: column;
margin-bottom: var(--tk-gap-xs);
}
.chat-bubble {
max-width: 75%;
padding: var(--tk-gap-md) var(--tk-gap-lg);
font-size: var(--tk-font-body);
line-height: 1.5;
&--other {
align-self: flex-start;
background: $card;
border-radius: $r $r $r $r-xs;
}
&--mine {
align-self: flex-end;
background: var(--tk-pri-l);
border-radius: $r $r $r-xs $r;
}
&__text {
color: $tx;
}
&__time {
font-size: var(--tk-font-micro);
color: $tx3;
margin-top: 4px;
text-align: center;
}
}

View File

@@ -0,0 +1,34 @@
import React, { ReactNode } from 'react';
import { View, Text } from '@tarojs/components';
import './index.scss';
interface ChatBubbleProps {
content: string;
isMine?: boolean;
time?: string;
className?: string;
}
const ChatBubble: React.FC<ChatBubbleProps> = ({
content,
isMine = false,
time,
className = '',
}) => {
const cls = [
'chat-bubble',
isMine ? 'chat-bubble--mine' : 'chat-bubble--other',
className,
].filter(Boolean).join(' ');
return (
<View className='chat-bubble-wrap'>
<View className={cls}>
<Text className='chat-bubble__text'>{content}</Text>
</View>
{time && <Text className='chat-bubble__time'>{time}</Text>}
</View>
);
};
export default React.memo(ChatBubble);

View File

@@ -15,9 +15,9 @@ interface ContentCardProps {
const PADDING_MAP = { const PADDING_MAP = {
none: '0', none: '0',
sm: '12px', sm: 'var(--tk-card-padding-sm)',
md: 'var(--tk-card-padding)', md: 'var(--tk-card-padding)',
lg: '32px', lg: 'var(--tk-card-padding-lg)',
} as const; } as const;
const ContentCard: React.FC<ContentCardProps> = ({ const ContentCard: React.FC<ContentCardProps> = ({

View File

@@ -0,0 +1,51 @@
@import '../../../styles/variables.scss';
.form-input {
&__label {
display: block;
font-size: var(--tk-font-cap);
color: $tx3;
margin-bottom: 6px;
}
&__field {
height: var(--tk-input-height);
background: $card;
border: 1.5px solid $bd;
border-radius: $r;
padding: 0 var(--tk-gap-lg);
display: flex;
align-items: center;
transition: border-color 0.2s;
}
&__control {
width: 100%;
height: 100%;
font-size: var(--tk-font-body);
color: $tx;
}
&__placeholder {
color: $tx3;
}
&--error &__field {
border-color: $dan;
}
&--disabled &__field {
opacity: 0.5;
}
&--focus &__field {
border-color: var(--tk-pri);
}
&__error {
display: block;
font-size: var(--tk-font-cap);
color: $dan;
margin-top: 4px;
}
}

View File

@@ -0,0 +1,55 @@
import React from 'react';
import { View, Text, Input } from '@tarojs/components';
import './index.scss';
interface FormInputProps {
label?: string;
placeholder?: string;
value?: string;
onInput?: (value: string) => void;
type?: 'text' | 'number' | 'idcard' | 'digit';
maxLength?: number;
disabled?: boolean;
error?: string;
className?: string;
}
const FormInput: React.FC<FormInputProps> = ({
label,
placeholder,
value,
onInput,
type = 'text',
maxLength,
disabled = false,
error,
className = '',
}) => {
const cls = [
'form-input',
error && 'form-input--error',
disabled && 'form-input--disabled',
className,
].filter(Boolean).join(' ');
return (
<View className={cls}>
{label && <Text className='form-input__label'>{label}</Text>}
<View className='form-input__field'>
<Input
className='form-input__control'
placeholder={placeholder}
placeholderClass='form-input__placeholder'
value={value}
onInput={e => onInput?.(e.detail.value)}
type={type}
maxlength={maxLength}
disabled={disabled}
/>
</View>
{error && <Text className='form-input__error'>{error}</Text>}
</View>
);
};
export default React.memo(FormInput);

View File

@@ -0,0 +1,8 @@
@import '../../../styles/variables.scss';
.gradient-header {
background: linear-gradient(135deg, var(--tk-pri) 0%, var(--tk-pri-d) 100%);
border-radius: $r;
padding: 18px;
color: $white;
}

View File

@@ -0,0 +1,21 @@
import React, { ReactNode } from 'react';
import { View } from '@tarojs/components';
import './index.scss';
interface GradientHeaderProps {
children: ReactNode;
className?: string;
}
const GradientHeader: React.FC<GradientHeaderProps> = ({
children,
className = '',
}) => {
return (
<View className={`gradient-header ${className}`}>
{children}
</View>
);
};
export default React.memo(GradientHeader);

View File

@@ -0,0 +1,27 @@
@import '../../../styles/variables.scss';
.info-row {
display: flex;
justify-content: space-between;
align-items: center;
padding: var(--tk-gap-md) 0;
border-bottom: 1px solid $bd-l;
&--last {
border-bottom: none;
}
&__label {
font-size: var(--tk-font-body);
color: $tx2;
flex-shrink: 0;
}
&__value {
font-size: var(--tk-font-body-lg);
color: $tx;
text-align: right;
flex: 1;
margin-left: var(--tk-gap-md);
}
}

View File

@@ -0,0 +1,34 @@
import React, { ReactNode } from 'react';
import { View, Text } from '@tarojs/components';
import './index.scss';
interface InfoRowProps {
label: string;
value?: string;
valueNode?: ReactNode;
last?: boolean;
className?: string;
}
const InfoRow: React.FC<InfoRowProps> = ({
label,
value,
valueNode,
last = false,
className = '',
}) => {
const cls = [
'info-row',
last && 'info-row--last',
className,
].filter(Boolean).join(' ');
return (
<View className={cls}>
<Text className='info-row__label'>{label}</Text>
{valueNode ?? <Text className='info-row__value'>{value}</Text>}
</View>
);
};
export default React.memo(InfoRow);

View File

@@ -0,0 +1,67 @@
@import '../../../styles/variables.scss';
.list-item {
display: flex;
align-items: center;
background: $card;
border-radius: $r;
padding: var(--tk-gap-lg);
box-shadow: $shadow-sm;
gap: var(--tk-gap-md);
&--pressable {
&:active {
opacity: var(--tk-touch-feedback-opacity);
}
}
&--read {
opacity: 0.7;
}
&__icon {
flex-shrink: 0;
width: 44px;
height: 44px;
border-radius: 22px;
display: flex;
align-items: center;
justify-content: center;
}
&__body {
flex: 1;
min-width: 0;
}
&__title {
display: block;
font-size: var(--tk-font-body);
font-weight: 500;
color: $tx;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
&__subtitle {
display: block;
font-size: var(--tk-font-cap);
color: $tx3;
margin-top: 4px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
&__extra {
flex-shrink: 0;
}
&__arrow {
flex-shrink: 0;
font-size: var(--tk-font-body-lg);
color: $tx3;
margin-left: var(--tk-gap-2xs);
}
}

View File

@@ -0,0 +1,46 @@
import React, { ReactNode } from 'react';
import { View, Text } from '@tarojs/components';
import './index.scss';
interface ListItemProps {
title: string;
subtitle?: string;
extra?: ReactNode;
leftIcon?: ReactNode;
onPress?: () => void;
showArrow?: boolean;
unread?: boolean;
className?: string;
}
const ListItem: React.FC<ListItemProps> = ({
title,
subtitle,
extra,
leftIcon,
onPress,
showArrow = false,
unread = false,
className = '',
}) => {
const cls = [
'list-item',
onPress && 'list-item--pressable',
!unread && 'list-item--read',
className,
].filter(Boolean).join(' ');
return (
<View className={cls} onClick={onPress}>
{leftIcon && <View className='list-item__icon'>{leftIcon}</View>}
<View className='list-item__body'>
<Text className='list-item__title'>{title}</Text>
{subtitle && <Text className='list-item__subtitle'>{subtitle}</Text>}
</View>
{extra && <View className='list-item__extra'>{extra}</View>}
{showArrow && <Text className='list-item__arrow'></Text>}
</View>
);
};
export default React.memo(ListItem);

View File

@@ -35,7 +35,7 @@
&__row { &__row {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 12px; gap: var(--tk-gap-sm);
} }
&__circle { &__circle {
@@ -50,7 +50,7 @@
flex: 1; flex: 1;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 8px; gap: var(--tk-gap-xs);
} }
&__line { &__line {

View File

@@ -13,9 +13,9 @@ interface PageShellProps {
const PADDING_MAP = { const PADDING_MAP = {
none: '0', none: '0',
sm: '16px', sm: 'var(--tk-gap-md)',
md: 'var(--tk-page-padding)', md: 'var(--tk-page-padding)',
lg: '32px', lg: 'var(--tk-gap-xl)',
} as const; } as const;
const PageShell: React.FC<PageShellProps> = ({ const PageShell: React.FC<PageShellProps> = ({

View File

@@ -0,0 +1,54 @@
@import '../../../styles/variables.scss';
.primary-btn {
display: flex;
align-items: center;
justify-content: center;
gap: var(--tk-gap-xs);
width: 100%;
background: var(--tk-pri);
color: $white;
font-weight: 600;
border: none;
border-radius: 14px;
box-shadow: var(--tk-shadow-btn);
transition: opacity 0.15s, transform 0.15s;
&--default {
height: var(--tk-btn-primary-h);
font-size: var(--tk-font-body-lg);
}
&--large {
height: 54px;
font-size: var(--tk-font-h2);
}
&:active:not(&--disabled):not(&--loading) {
opacity: var(--tk-touch-feedback-opacity);
transform: scale(0.98);
}
&--disabled {
opacity: 0.5;
box-shadow: none;
}
&__spinner {
width: 18px;
height: 18px;
border: 2px solid rgba(255, 255, 255, 0.3);
border-top-color: $white;
border-radius: 50%;
animation: primary-btn-spin 0.6s linear infinite;
}
&__text {
color: $white;
font-weight: 600;
}
}
@keyframes primary-btn-spin {
to { transform: rotate(360deg); }
}

View File

@@ -0,0 +1,38 @@
import React from 'react';
import { View, Text } from '@tarojs/components';
import './index.scss';
interface PrimaryButtonProps {
children: React.ReactNode;
onClick?: () => void;
disabled?: boolean;
loading?: boolean;
size?: 'default' | 'large';
className?: string;
}
const PrimaryButton: React.FC<PrimaryButtonProps> = ({
children,
onClick,
disabled = false,
loading = false,
size = 'default',
className = '',
}) => {
const cls = [
'primary-btn',
`primary-btn--${size}`,
disabled && 'primary-btn--disabled',
loading && 'primary-btn--loading',
className,
].filter(Boolean).join(' ');
return (
<View className={cls} onClick={!disabled && !loading ? onClick : undefined}>
{loading && <View className='primary-btn__spinner' />}
<Text className='primary-btn__text'>{children}</Text>
</View>
);
};
export default React.memo(PrimaryButton);

View File

@@ -0,0 +1,28 @@
@import '../../../styles/variables.scss';
.progress-ring {
position: relative;
flex-shrink: 0;
&__center {
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
}
&__pct {
font-family: Georgia, 'Times New Roman', serif;
font-size: var(--tk-font-cap);
font-weight: 700;
color: var(--tk-pri);
}
&__label {
font-family: Georgia, 'Times New Roman', serif;
font-size: var(--tk-font-micro);
font-weight: 700;
color: var(--tk-pri);
}
}

View File

@@ -0,0 +1,56 @@
import React from 'react';
import { View, Text } from '@tarojs/components';
import './index.scss';
interface ProgressRingProps {
progress: number;
size?: 'sm' | 'lg';
label?: string;
className?: string;
}
const ProgressRing: React.FC<ProgressRingProps> = ({
progress,
size = 'sm',
label,
className = '',
}) => {
const px = size === 'sm' ? 64 : 80;
const r = (px / 2) - 4;
const circumference = 2 * Math.PI * r;
const offset = circumference * (1 - Math.min(progress, 1));
const cls = ['progress-ring', `progress-ring--${size}`, className].filter(Boolean).join(' ');
return (
<View className={cls} style={{ width: px, height: px }}>
<svg width={px} height={px} viewBox={`0 0 ${px} ${px}`}>
<circle
cx={px / 2} cy={px / 2} r={r}
fill='none'
stroke='var(--tk-pri-l, #E8E2DC)'
strokeWidth={4}
/>
<circle
cx={px / 2} cy={px / 2} r={r}
fill='none'
stroke='var(--tk-pri)'
strokeWidth={4}
strokeDasharray={circumference}
strokeDashoffset={offset}
strokeLinecap='round'
transform={`rotate(-90 ${px / 2} ${px / 2})`}
/>
</svg>
<View className='progress-ring__center'>
{label ? (
<Text className='progress-ring__label'>{label}</Text>
) : (
<Text className='progress-ring__pct'>{Math.round(progress * 100)}%</Text>
)}
</View>
</View>
);
};
export default React.memo(ProgressRing);

View File

@@ -0,0 +1,30 @@
@import '../../../styles/variables.scss';
.secondary-btn {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: var(--tk-btn-primary-h);
background: transparent;
color: var(--tk-pri);
font-weight: 600;
border: 2px solid var(--tk-pri);
border-radius: 14px;
transition: opacity 0.15s, transform 0.15s;
&:active:not(&--disabled) {
opacity: var(--tk-touch-feedback-opacity);
transform: scale(0.98);
}
&--disabled {
opacity: 0.5;
}
&__text {
color: var(--tk-pri);
font-size: var(--tk-font-body-lg);
font-weight: 600;
}
}

View File

@@ -0,0 +1,31 @@
import React from 'react';
import { View, Text } from '@tarojs/components';
import './index.scss';
interface SecondaryButtonProps {
children: React.ReactNode;
onClick?: () => void;
disabled?: boolean;
className?: string;
}
const SecondaryButton: React.FC<SecondaryButtonProps> = ({
children,
onClick,
disabled = false,
className = '',
}) => {
const cls = [
'secondary-btn',
disabled && 'secondary-btn--disabled',
className,
].filter(Boolean).join(' ');
return (
<View className={cls} onClick={!disabled ? onClick : undefined}>
<Text className='secondary-btn__text'>{children}</Text>
</View>
);
};
export default React.memo(SecondaryButton);

View File

@@ -9,13 +9,13 @@
&__left { &__left {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 10px; gap: var(--tk-gap-sm);
} }
&__bar { &__bar {
width: 3px; width: 3px;
height: 20px; height: 20px;
background: $pri; background: var(--tk-pri);
border-radius: 2px; border-radius: 2px;
flex-shrink: 0; flex-shrink: 0;
} }
@@ -43,7 +43,7 @@
&__action { &__action {
font-size: var(--tk-font-body-sm); font-size: var(--tk-font-body-sm);
color: $pri; color: var(--tk-pri);
min-height: var(--tk-touch-min); min-height: var(--tk-touch-min);
display: flex; display: flex;
align-items: center; align-items: center;

View File

@@ -11,7 +11,7 @@
white-space: nowrap; white-space: nowrap;
&--sm { &--sm {
padding: 2px 8px; padding: 2px var(--tk-gap-xs);
font-size: 11px; font-size: var(--tk-font-micro);
} }
} }

View File

@@ -0,0 +1,87 @@
@import '../../../styles/variables.scss';
.tab-filter {
display: flex;
gap: var(--tk-gap-xs);
// 填充型 — 体征类型切换
&--fill {
.tab-filter__item {
flex: 1;
height: 44px;
border-radius: $r-sm;
background: $surface-alt;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.2s;
&--active {
background: var(--tk-pri);
box-shadow: var(--tk-shadow-tab);
.tab-filter__text {
color: $white;
font-weight: 600;
}
}
}
}
// Pill型 — 文章分类
&--pill {
flex-wrap: wrap;
gap: var(--tk-gap-xs);
.tab-filter__item {
height: 32px;
padding: 0 var(--tk-gap-lg);
border-radius: $r-pill;
background: $surface-alt;
display: flex;
align-items: center;
justify-content: center;
&--active {
background: var(--tk-pri);
.tab-filter__text {
color: $white;
font-weight: 600;
}
}
}
}
// 段控型 — 消息页咨询/通知
&--segment {
background: $surface-alt;
border-radius: $r-xs;
padding: 3px;
.tab-filter__item {
flex: 1;
height: 40px;
border-radius: 6px;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.2s;
&--active {
background: $card;
box-shadow: $shadow-sm;
.tab-filter__text {
color: $tx;
font-weight: 600;
}
}
}
}
&__text {
font-size: var(--tk-font-cap);
color: $tx2;
}
}

View File

@@ -0,0 +1,43 @@
import React from 'react';
import { View, Text } from '@tarojs/components';
import './index.scss';
type TabVariant = 'fill' | 'pill' | 'segment';
interface TabFilterProps {
tabs: string[];
activeIndex: number;
onChange: (index: number) => void;
variant?: TabVariant;
className?: string;
}
const TabFilter: React.FC<TabFilterProps> = ({
tabs,
activeIndex,
onChange,
variant = 'fill',
className = '',
}) => {
const cls = [
'tab-filter',
`tab-filter--${variant}`,
className,
].filter(Boolean).join(' ');
return (
<View className={cls}>
{tabs.map((tab, i) => (
<View
key={i}
className={`tab-filter__item ${i === activeIndex ? 'tab-filter__item--active' : ''}`}
onClick={() => onChange(i)}
>
<Text className='tab-filter__text'>{tab}</Text>
</View>
))}
</View>
);
};
export default React.memo(TabFilter);

View File

@@ -0,0 +1,41 @@
@import '../../../styles/variables.scss';
.vital-card {
background: $card;
border-radius: $r;
padding: 14px var(--tk-gap-lg);
box-shadow: $shadow-sm;
&--pressable {
&:active {
opacity: var(--tk-touch-feedback-opacity);
}
}
&__label {
display: block;
font-size: var(--tk-font-cap);
color: $tx2;
margin-bottom: 6px;
}
&__row {
display: flex;
align-items: baseline;
margin-bottom: 6px;
}
&__value {
font-family: Georgia, 'Times New Roman', serif;
font-size: var(--tk-font-num);
font-weight: 700;
color: $tx;
line-height: 1;
}
&__unit {
font-size: var(--tk-font-micro);
color: $tx3;
margin-left: 3px;
}
}

View File

@@ -0,0 +1,41 @@
import React from 'react';
import { View, Text } from '@tarojs/components';
import StatusTag from '../StatusTag';
import './index.scss';
interface VitalCardProps {
label: string;
value: string;
unit?: string;
status?: string;
onPress?: () => void;
className?: string;
}
const VitalCard: React.FC<VitalCardProps> = ({
label,
value,
unit,
status,
onPress,
className = '',
}) => {
const cls = [
'vital-card',
onPress && 'vital-card--pressable',
className,
].filter(Boolean).join(' ');
return (
<View className={cls} onClick={onPress}>
<Text className='vital-card__label'>{label}</Text>
<View className='vital-card__row'>
<Text className='vital-card__value'>{value}</Text>
{unit && <Text className='vital-card__unit'>{unit}</Text>}
</View>
{status && <StatusTag status={status} size='sm' />}
</View>
);
};
export default React.memo(VitalCard);

View File

@@ -0,0 +1,6 @@
import { useUIStore } from '../stores/ui';
export function useDoctorClass(): string {
const mode = useUIStore((s) => s.mode);
return ['doctor-mode', mode === 'elder' ? 'elder-mode' : ''].filter(Boolean).join(' ');
}

View File

@@ -3,7 +3,7 @@
.detail-type { .detail-type {
@include section-title; @include section-title;
margin-bottom: 12px; margin-bottom: var(--tk-gap-sm);
} }
.detail-meta { .detail-meta {
@@ -21,30 +21,30 @@
h1, h2, h3 { h1, h2, h3 {
font-weight: bold; font-weight: bold;
color: $tx; color: $tx;
margin: 24px 0 12px; margin: var(--tk-gap-lg) 0 var(--tk-gap-sm);
} }
p { p {
font-size: var(--tk-font-body-lg); font-size: var(--tk-font-body-lg);
color: $tx; color: $tx;
line-height: 1.8; line-height: 1.8;
margin-bottom: 16px; margin-bottom: var(--tk-gap-md);
} }
ul { ul {
padding-left: 32px; padding-left: var(--tk-gap-xl);
margin-bottom: 16px; margin-bottom: var(--tk-gap-md);
} }
li { li {
font-size: var(--tk-font-body-lg); font-size: var(--tk-font-body-lg);
color: $tx; color: $tx;
line-height: 1.8; line-height: 1.8;
margin-bottom: 8px; margin-bottom: var(--tk-gap-xs);
} }
strong { strong {
color: $pri-d; color: var(--tk-pri-d);
} }
} }
@@ -57,32 +57,32 @@
.empty-text { .empty-text {
display: block; display: block;
text-align: center; text-align: center;
padding: 120px 0; padding: var(--tk-gap-2xl) 0;
color: var(--tk-text-secondary); color: var(--tk-text-secondary);
font-size: var(--tk-font-body-lg); font-size: var(--tk-font-body-lg);
} }
.auto-badge { .auto-badge {
margin-top: 16px; margin-top: var(--tk-gap-md);
display: inline-block; display: inline-block;
} }
.auto-badge-text { .auto-badge-text {
display: inline-block; display: inline-block;
padding: 4px 16px; padding: var(--tk-gap-2xs) var(--tk-gap-md);
border-radius: $r-xs; border-radius: $r-xs;
font-size: var(--tk-font-body); font-size: var(--tk-font-body);
font-weight: 500; font-weight: 500;
background: $pri-l; background: var(--tk-pri-l);
color: $pri; color: var(--tk-pri);
} }
.trend-tip-card { .trend-tip-card {
background: $wrn-l; background: $wrn-l;
border: 1px solid $wrn; border: 1px solid $wrn;
border-radius: $r; border-radius: $r;
padding: 20px 24px; padding: var(--tk-section-gap) var(--tk-gap-lg);
margin-bottom: 20px; margin-bottom: var(--tk-section-gap);
} }
.trend-tip-text { .trend-tip-text {

View File

@@ -14,8 +14,8 @@
.report-card { .report-card {
background: $card; background: $card;
border-radius: $r; border-radius: $r;
padding: 28px; padding: var(--tk-card-padding-lg);
margin-bottom: 20px; margin-bottom: var(--tk-section-gap);
box-shadow: $shadow-sm; box-shadow: $shadow-sm;
} }
@@ -23,7 +23,7 @@
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
margin-bottom: 12px; margin-bottom: var(--tk-gap-sm);
} }
.card-type { .card-type {
@@ -41,7 +41,7 @@
} }
.status-streaming { .status-streaming {
@include tag($pri-l, $pri); @include tag(var(--tk-pri-l), var(--tk-pri));
} }
.status-failed { .status-failed {
@@ -72,6 +72,6 @@
text-align: center; text-align: center;
font-size: var(--tk-font-h2); font-size: var(--tk-font-h2);
color: var(--tk-text-secondary); color: var(--tk-text-secondary);
padding: 24px 0; padding: var(--tk-gap-lg) 0;
display: block; display: block;
} }

View File

@@ -9,7 +9,7 @@
/* 步骤内容 */ /* 步骤内容 */
.step-content { .step-content {
padding: 32px 24px; padding: var(--tk-gap-xl) var(--tk-gap-lg);
} }
.step-title { .step-title {
@@ -20,24 +20,24 @@
.dept-grid { .dept-grid {
display: grid; display: grid;
grid-template-columns: repeat(3, 1fr); grid-template-columns: repeat(3, 1fr);
gap: 16px; gap: var(--tk-gap-md);
} }
.dept-card { .dept-card {
background: $card; background: $card;
border-radius: $r; border-radius: $r;
padding: 28px 12px; padding: var(--tk-card-padding-lg) var(--tk-gap-sm);
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
gap: 12px; gap: var(--tk-gap-sm);
border: 2px solid transparent; border: 2px solid transparent;
transition: border-color 0.2s; transition: border-color 0.2s;
box-shadow: $shadow-sm; box-shadow: $shadow-sm;
&.dept-selected { &.dept-selected {
border-color: $pri; border-color: var(--tk-pri);
background: $pri-l; background: var(--tk-pri-l);
} }
} }
@@ -45,11 +45,11 @@
width: 64px; width: 64px;
height: 64px; height: 64px;
border-radius: $r; border-radius: $r;
background: $pri-l; background: var(--tk-pri-l);
@include flex-center; @include flex-center;
.dept-selected & { .dept-selected & {
background: $pri; background: var(--tk-pri);
} }
} }
@@ -57,7 +57,7 @@
font-family: 'Georgia', 'Times New Roman', serif; font-family: 'Georgia', 'Times New Roman', serif;
font-size: var(--tk-font-num); font-size: var(--tk-font-num);
font-weight: bold; font-weight: bold;
color: $pri; color: var(--tk-pri);
.dept-selected & { .dept-selected & {
color: $white; color: $white;
@@ -72,7 +72,7 @@
/* 时段 */ /* 时段 */
.slot-section { .slot-section {
margin-top: 32px; margin-top: var(--tk-gap-xl);
} }
.slot-section-title { .slot-section-title {
@@ -80,20 +80,20 @@
font-size: var(--tk-font-body-lg); font-size: var(--tk-font-body-lg);
font-weight: bold; font-weight: bold;
color: $tx; color: $tx;
margin-bottom: 16px; margin-bottom: var(--tk-gap-md);
display: block; display: block;
} }
.slot-grid { .slot-grid {
display: grid; display: grid;
grid-template-columns: repeat(2, 1fr); grid-template-columns: repeat(2, 1fr);
gap: 12px; gap: var(--tk-gap-sm);
} }
.slot-card { .slot-card {
background: $card; background: $card;
border-radius: $r-sm; border-radius: $r-sm;
padding: 20px 24px; padding: var(--tk-section-gap) var(--tk-gap-lg);
border: 2px solid transparent; border: 2px solid transparent;
transition: all 0.2s; transition: all 0.2s;
box-shadow: $shadow-sm; box-shadow: $shadow-sm;
@@ -107,8 +107,8 @@
pointer-events: none; pointer-events: none;
} }
&.slot-selected { &.slot-selected {
border-color: $pri; border-color: var(--tk-pri);
background: $pri-l; background: var(--tk-pri-l);
} }
} }
@@ -124,7 +124,7 @@
font-size: var(--tk-font-body); font-size: var(--tk-font-body);
color: $tx3; color: $tx3;
display: block; display: block;
margin-top: 6px; margin-top: var(--tk-gap-2xs);
} }
.slot-few .slot-count { color: $wrn; } .slot-few .slot-count { color: $wrn; }
@@ -132,20 +132,20 @@
/* 确认卡片 (step 3 医生信息) */ /* 确认卡片 (step 3 医生信息) */
.confirm-card { .confirm-card {
margin-bottom: 24px; margin-bottom: var(--tk-gap-lg);
} }
.confirm-row { .confirm-row {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 16px; gap: var(--tk-gap-md);
} }
.confirm-icon-wrap { .confirm-icon-wrap {
width: 56px; width: 56px;
height: 56px; height: 56px;
border-radius: $r-sm; border-radius: $r-sm;
background: $pri-l; background: var(--tk-pri-l);
@include flex-center; @include flex-center;
flex-shrink: 0; flex-shrink: 0;
} }
@@ -154,7 +154,7 @@
font-family: 'Georgia', 'Times New Roman', serif; font-family: 'Georgia', 'Times New Roman', serif;
font-size: var(--tk-font-h2); font-size: var(--tk-font-h2);
font-weight: bold; font-weight: bold;
color: $pri; color: var(--tk-pri);
} }
.confirm-info { .confirm-info {
@@ -176,37 +176,37 @@
} }
.confirm-dept-tag { .confirm-dept-tag {
@include tag($pri-l, $pri); @include tag(var(--tk-pri-l), var(--tk-pri));
flex-shrink: 0; flex-shrink: 0;
} }
.confirm-dept-text { .confirm-dept-text {
font-size: var(--tk-font-body); font-size: var(--tk-font-body);
font-weight: 500; font-weight: 500;
color: $pri; color: var(--tk-pri);
} }
/* 医生列表 */ /* 医生列表 */
.doctor-list { .doctor-list {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 16px; gap: var(--tk-gap-md);
} }
.doctor-card { .doctor-card {
background: $card; background: $card;
border-radius: $r; border-radius: $r;
padding: 24px 28px; padding: var(--tk-gap-lg) var(--tk-card-padding-lg);
display: flex; display: flex;
align-items: center; align-items: center;
gap: 20px; gap: var(--tk-section-gap);
box-shadow: $shadow-sm; box-shadow: $shadow-sm;
border: 2px solid transparent; border: 2px solid transparent;
transition: border-color 0.2s; transition: border-color 0.2s;
&.doctor-selected { &.doctor-selected {
border-color: $pri; border-color: var(--tk-pri);
background: $pri-l; background: var(--tk-pri-l);
} }
} }
@@ -214,7 +214,7 @@
width: 80px; width: 80px;
height: 80px; height: 80px;
border-radius: $r; border-radius: $r;
background: $pri-l; background: var(--tk-pri-l);
@include flex-center; @include flex-center;
flex-shrink: 0; flex-shrink: 0;
} }
@@ -222,7 +222,7 @@
.doctor-avatar-text { .doctor-avatar-text {
font-family: 'Georgia', 'Times New Roman', serif; font-family: 'Georgia', 'Times New Roman', serif;
font-size: var(--tk-font-num); font-size: var(--tk-font-num);
color: $pri; color: var(--tk-pri);
font-weight: bold; font-weight: bold;
} }
@@ -230,7 +230,7 @@
flex: 1; flex: 1;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 4px; gap: var(--tk-gap-2xs);
} }
.doctor-name { .doctor-name {
@@ -246,14 +246,14 @@
.doctor-specialty { .doctor-specialty {
font-size: var(--tk-font-body); font-size: var(--tk-font-body);
color: $pri; color: var(--tk-pri);
} }
.doctor-check { .doctor-check {
width: 48px; width: 48px;
height: 48px; height: 48px;
border-radius: $r-pill; border-radius: $r-pill;
background: $pri; background: var(--tk-pri);
@include flex-center; @include flex-center;
flex-shrink: 0; flex-shrink: 0;
} }
@@ -266,20 +266,20 @@
/* 表单 */ /* 表单 */
.form-group { .form-group {
margin-top: 32px; margin-top: var(--tk-gap-xl);
} }
.form-label { .form-label {
font-size: var(--tk-font-h1); font-size: var(--tk-font-h1);
color: $tx2; color: $tx2;
margin-bottom: 12px; margin-bottom: var(--tk-gap-sm);
display: block; display: block;
} }
.form-input { .form-input {
background: $card; background: $card;
border-radius: $r-sm; border-radius: $r-sm;
padding: 24px 28px; padding: var(--tk-gap-lg) var(--tk-card-padding-lg);
font-size: var(--tk-font-body-lg); font-size: var(--tk-font-body-lg);
color: $tx; color: $tx;
width: 100%; width: 100%;
@@ -305,8 +305,8 @@
left: 0; left: 0;
right: 0; right: 0;
display: flex; display: flex;
gap: 16px; gap: var(--tk-gap-md);
padding: 20px 24px; padding: var(--tk-section-gap) var(--tk-gap-lg);
padding-bottom: constant(safe-area-inset-bottom); padding-bottom: constant(safe-area-inset-bottom);
padding-bottom: env(safe-area-inset-bottom); padding-bottom: env(safe-area-inset-bottom);
background: $card; background: $card;
@@ -315,7 +315,7 @@
.btn { .btn {
flex: 1; flex: 1;
padding: 24px 0; padding: var(--tk-gap-lg) 0;
border-radius: $r-sm; border-radius: $r-sm;
text-align: center; text-align: center;
transition: opacity 0.2s; transition: opacity 0.2s;
@@ -327,7 +327,7 @@
.btn-next, .btn-next,
.btn-submit { .btn-submit {
background: $pri; background: var(--tk-pri);
} }
.btn-disabled { .btn-disabled {

View File

@@ -6,14 +6,14 @@
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
gap: 12px; gap: var(--tk-gap-sm);
margin: 20px 24px 24px; margin: var(--tk-section-gap) var(--tk-gap-lg) var(--tk-gap-lg);
} }
.status-tag { .status-tag {
@include tag($bd-l, $tx3); @include tag($bd-l, $tx3);
margin-bottom: 8px; margin-bottom: var(--tk-gap-xs);
padding: 8px 32px; padding: var(--tk-gap-xs) var(--tk-gap-xl);
border-radius: $r-pill; border-radius: $r-pill;
&.tag-pending { &.tag-pending {
@@ -32,8 +32,8 @@
} }
&.tag-completed { &.tag-completed {
background: $pri-l; background: var(--tk-pri-l);
.status-tag-text { color: $pri; } .status-tag-text { color: var(--tk-pri); }
} }
} }
@@ -56,12 +56,12 @@
/* 详情信息 */ /* 详情信息 */
.info-section-wrap { .info-section-wrap {
margin: 0 24px 24px; margin: 0 var(--tk-gap-lg) var(--tk-gap-lg);
} }
.section-title { .section-title {
@include section-title; @include section-title;
margin-bottom: 24px; margin-bottom: var(--tk-gap-lg);
} }
.info-item { .info-item {
@@ -85,8 +85,8 @@
.info-icon-serif { .info-icon-serif {
font-family: 'Georgia', 'Times New Roman', serif; font-family: 'Georgia', 'Times New Roman', serif;
font-size: var(--tk-font-body); font-size: var(--tk-font-body);
color: $pri; color: var(--tk-pri);
background: $pri-l; background: var(--tk-pri-l);
width: 36px; width: 36px;
height: 36px; height: 36px;
border-radius: $r-sm; border-radius: $r-sm;
@@ -126,7 +126,7 @@
/* 温馨提示 */ /* 温馨提示 */
.tips-card-wrap { .tips-card-wrap {
margin: 0 24px 24px; margin: 0 var(--tk-gap-lg) var(--tk-gap-lg);
background: $wrn-l; background: $wrn-l;
} }
@@ -135,7 +135,7 @@
font-size: var(--tk-font-body-lg); font-size: var(--tk-font-body-lg);
font-weight: bold; font-weight: bold;
color: $wrn; color: $wrn;
margin-bottom: 12px; margin-bottom: var(--tk-gap-sm);
display: block; display: block;
} }
@@ -151,7 +151,7 @@
bottom: 0; bottom: 0;
left: 0; left: 0;
right: 0; right: 0;
padding: 20px 24px; padding: var(--tk-section-gap) var(--tk-gap-lg);
padding-bottom: constant(safe-area-inset-bottom); padding-bottom: constant(safe-area-inset-bottom);
padding-bottom: env(safe-area-inset-bottom); padding-bottom: env(safe-area-inset-bottom);
background: $card; background: $card;
@@ -161,7 +161,7 @@
.cancel-btn { .cancel-btn {
background: $dan-l; background: $dan-l;
border-radius: $r-sm; border-radius: $r-sm;
padding: 24px 0; padding: var(--tk-gap-lg) 0;
text-align: center; text-align: center;
transition: opacity 0.2s; transition: opacity 0.2s;
} }

View File

@@ -9,13 +9,13 @@
/* 页头 */ /* 页头 */
.page-header { .page-header {
background: $card; background: $card;
padding: 48px 32px 36px; padding: var(--tk-gap-2xl) var(--tk-gap-xl) 36px;
box-shadow: $shadow-sm; box-shadow: $shadow-sm;
} }
.page-title { .page-title {
@include section-title; @include section-title;
margin-bottom: 4px; margin-bottom: var(--tk-gap-2xs);
font-size: var(--tk-font-num-lg); font-size: var(--tk-font-num-lg);
} }
@@ -28,15 +28,15 @@
/* 预约列表 */ /* 预约列表 */
.appointment-list { .appointment-list {
padding: 0 24px; padding: 0 var(--tk-page-padding);
margin-top: 16px; margin-top: var(--tk-gap-md);
} }
.appointment-card { .appointment-card {
background: $card; background: $card;
border-radius: $r; border-radius: $r;
padding: 28px; padding: var(--tk-card-padding-lg);
margin-bottom: 20px; margin-bottom: var(--tk-section-gap);
box-shadow: $shadow-sm; box-shadow: $shadow-sm;
} }
@@ -44,13 +44,13 @@
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
margin-bottom: 20px; margin-bottom: var(--tk-section-gap);
} }
.doctor-section { .doctor-section {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 16px; gap: var(--tk-gap-md);
flex: 1; flex: 1;
min-width: 0; min-width: 0;
} }
@@ -59,7 +59,7 @@
width: 72px; width: 72px;
height: 72px; height: 72px;
border-radius: $r; border-radius: $r;
background: $pri-l; background: var(--tk-pri-l);
@include flex-center; @include flex-center;
flex-shrink: 0; flex-shrink: 0;
} }
@@ -68,13 +68,13 @@
font-family: 'Georgia', 'Times New Roman', serif; font-family: 'Georgia', 'Times New Roman', serif;
font-size: var(--tk-font-num); font-size: var(--tk-font-num);
font-weight: bold; font-weight: bold;
color: $pri; color: var(--tk-pri);
} }
.doctor-info { .doctor-info {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 6px; gap: var(--tk-gap-2xs);
min-width: 0; min-width: 0;
} }
@@ -88,13 +88,13 @@
} }
.dept-tag { .dept-tag {
@include tag($pri-l, $pri); @include tag(var(--tk-pri-l), var(--tk-pri));
} }
.dept-tag-text { .dept-tag-text {
font-size: var(--tk-font-body); font-size: var(--tk-font-body);
font-weight: 500; font-weight: 500;
color: $pri; color: var(--tk-pri);
} }
.status-tag { .status-tag {
@@ -117,8 +117,8 @@
} }
&.tag-completed { &.tag-completed {
background: $pri-l; background: var(--tk-pri-l);
.status-tag-text { color: $pri; } .status-tag-text { color: var(--tk-pri); }
} }
} }
@@ -130,7 +130,7 @@
.card-divider { .card-divider {
height: 1px; height: 1px;
background: $bd-l; background: $bd-l;
margin-bottom: 20px; margin-bottom: var(--tk-section-gap);
} }
.card-bottom { .card-bottom {
@@ -142,7 +142,7 @@
.info-row { .info-row {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 12px; gap: var(--tk-gap-sm);
} }
.info-icon-wrap { .info-icon-wrap {
@@ -177,8 +177,8 @@
bottom: 60px; bottom: 60px;
left: 50%; left: 50%;
transform: translateX(-50%); transform: translateX(-50%);
background: $pri; background: var(--tk-pri);
padding: 24px 72px; padding: var(--tk-gap-lg) 72px;
border-radius: $r-pill; border-radius: $r-pill;
box-shadow: 0 8px 24px rgba($pri, 0.3); box-shadow: 0 8px 24px rgba($pri, 0.3);
z-index: 100; z-index: 100;

View File

@@ -5,7 +5,7 @@
.article-header { .article-header {
background: $card; background: $card;
padding: 32px; padding: var(--tk-gap-xl);
margin-bottom: 2px; margin-bottom: 2px;
} }
@@ -15,21 +15,21 @@
color: $tx; color: $tx;
display: block; display: block;
line-height: 1.4; line-height: 1.4;
margin-bottom: 16px; margin-bottom: var(--tk-gap-md);
} }
.article-meta { .article-meta {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 16px; gap: var(--tk-gap-md);
flex-wrap: wrap; flex-wrap: wrap;
} }
.article-category { .article-category {
font-size: var(--tk-font-body); font-size: var(--tk-font-body);
color: $pri; color: var(--tk-pri);
background: $pri-l; background: var(--tk-pri-l);
padding: 4px 12px; padding: var(--tk-gap-2xs) var(--tk-gap-sm);
border-radius: $r-sm; border-radius: $r-sm;
} }
@@ -45,7 +45,7 @@
.article-summary { .article-summary {
background: $card; background: $card;
padding: 24px 32px; padding: var(--tk-gap-lg) var(--tk-gap-xl);
margin-bottom: 2px; margin-bottom: 2px;
} }
@@ -57,26 +57,26 @@
.article-content { .article-content {
background: $card; background: $card;
padding: 32px; padding: var(--tk-gap-xl);
// RichText 内部样式优化 // RichText 内部样式优化
h1, h2, h3 { h1, h2, h3 {
font-weight: bold; font-weight: bold;
color: $tx; color: $tx;
margin: 24px 0 12px; margin: var(--tk-gap-lg) 0 var(--tk-gap-sm);
} }
p { p {
font-size: var(--tk-font-body-lg); font-size: var(--tk-font-body-lg);
color: $tx; color: $tx;
line-height: 1.8; line-height: 1.8;
margin-bottom: 16px; margin-bottom: var(--tk-gap-md);
} }
img { img {
max-width: 100%; max-width: 100%;
border-radius: $r-sm; border-radius: $r-sm;
margin: 12px 0; margin: var(--tk-gap-sm) 0;
} }
} }
@@ -85,7 +85,7 @@
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
padding: 120px 0; padding: var(--tk-gap-2xl) 0;
} }
.loading-text, .loading-text,

View File

@@ -8,13 +8,13 @@
/* ─── 分类筛选滚动条(页面特有) ─── */ /* ─── 分类筛选滚动条(页面特有) ─── */
.article-categories { .article-categories {
white-space: nowrap; white-space: nowrap;
margin-bottom: 24px; margin-bottom: var(--tk-gap-lg);
} }
.article-cat-tab { .article-cat-tab {
display: inline-block; display: inline-block;
padding: 12px 28px; padding: var(--tk-gap-sm) var(--tk-card-padding-lg);
margin-right: 12px; margin-right: var(--tk-gap-sm);
font-size: var(--tk-font-h1); font-size: var(--tk-font-h1);
color: $tx2; color: $tx2;
background: $card; background: $card;
@@ -22,9 +22,9 @@
border: 2px solid transparent; border: 2px solid transparent;
&--active { &--active {
color: $pri; color: var(--tk-pri);
background: $pri-l; background: var(--tk-pri-l);
border-color: $pri; border-color: var(--tk-pri);
font-weight: bold; font-weight: bold;
} }
} }
@@ -33,7 +33,7 @@
.article-list { .article-list {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 20px; gap: var(--tk-section-gap);
} }
/* ─── 文章卡片内容ContentCard 已接管外层卡片样式) ─── */ /* ─── 文章卡片内容ContentCard 已接管外层卡片样式) ─── */
@@ -41,7 +41,7 @@
flex: 1; flex: 1;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
margin-right: 20px; margin-right: var(--tk-section-gap);
} }
.article-card-title { .article-card-title {
@@ -49,7 +49,7 @@
font-weight: bold; font-weight: bold;
color: $tx; color: $tx;
line-height: 1.4; line-height: 1.4;
margin-bottom: 8px; margin-bottom: var(--tk-gap-xs);
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
display: -webkit-box; display: -webkit-box;
@@ -62,7 +62,7 @@
color: $tx2; color: $tx2;
line-height: 1.4; line-height: 1.4;
display: block; display: block;
margin-bottom: 12px; margin-bottom: var(--tk-gap-sm);
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
@@ -71,14 +71,14 @@
.article-card-meta { .article-card-meta {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 12px; gap: var(--tk-gap-sm);
} }
.article-card-tag { .article-card-tag {
font-size: var(--tk-font-body); font-size: var(--tk-font-body);
color: $pri; color: var(--tk-pri);
background: $pri-l; background: var(--tk-pri-l);
padding: 2px 12px; padding: 2px var(--tk-gap-sm);
border-radius: $r-sm; border-radius: $r-sm;
} }

View File

@@ -13,20 +13,20 @@
font-size: var(--tk-font-cap); font-size: var(--tk-font-cap);
color: var(--tk-text-secondary); color: var(--tk-text-secondary);
display: block; display: block;
margin-bottom: 20px; margin-bottom: var(--tk-section-gap);
} }
/* ─── 发起咨询按钮(页面特有) ─── */ /* ─── 发起咨询按钮(页面特有) ─── */
.consultation-create-btn { .consultation-create-btn {
height: 48px; height: 48px;
border-radius: $r; border-radius: $r;
background: $pri; background: var(--tk-pri);
@include flex-center; @include flex-center;
box-shadow: 0 2px 8px rgba(196, 98, 58, 0.25); box-shadow: 0 2px 8px rgba($pri, 0.25);
margin-bottom: 20px; margin-bottom: var(--tk-section-gap);
&:active { &:active {
opacity: 0.85; opacity: var(--tk-touch-feedback-opacity);
} }
} }
@@ -40,7 +40,7 @@
.session-list { .session-list {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 8px; gap: var(--tk-gap-xs);
} }
/* ─── 已关闭会话半透明ContentCard 已接管卡片外层) ─── */ /* ─── 已关闭会话半透明ContentCard 已接管卡片外层) ─── */
@@ -52,14 +52,14 @@
.session-inner { .session-inner {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 12px; gap: var(--tk-gap-sm);
} }
.session-avatar { .session-avatar {
width: 36px; width: 36px;
height: 36px; height: 36px;
border-radius: $r-lg; border-radius: $r-lg;
background: $pri-l; background: var(--tk-pri-l);
@include flex-center; @include flex-center;
flex-shrink: 0; flex-shrink: 0;
} }
@@ -68,7 +68,7 @@
@include serif-number; @include serif-number;
font-size: var(--tk-font-body-sm); font-size: var(--tk-font-body-sm);
font-weight: 700; font-weight: 700;
color: $pri; color: var(--tk-pri);
} }
.session-body { .session-body {
@@ -80,7 +80,7 @@
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
margin-bottom: 6px; margin-bottom: var(--tk-gap-2xs);
} }
.session-subject { .session-subject {
@@ -91,7 +91,7 @@
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
flex: 1; flex: 1;
margin-right: 8px; margin-right: var(--tk-gap-xs);
} }
.session-time { .session-time {
@@ -103,7 +103,7 @@
.session-meta { .session-meta {
display: flex; display: flex;
align-items: center; align-items: center;
margin-bottom: 4px; margin-bottom: var(--tk-gap-2xs);
} }
.session-message-row { .session-message-row {
@@ -119,7 +119,7 @@
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
flex: 1; flex: 1;
margin-right: 8px; margin-right: var(--tk-gap-xs);
} }
/* ─── 未读角标(页面特有) ─── */ /* ─── 未读角标(页面特有) ─── */

View File

@@ -2,12 +2,12 @@
@import '../../styles/mixins.scss'; @import '../../styles/mixins.scss';
.health-page { .health-page {
padding-bottom: calc(100px + env(safe-area-inset-bottom)); padding-bottom: calc(var(--tk-tabbar-space) + env(safe-area-inset-bottom));
} }
/* ─── 页头 ─── */ /* ─── 页头 ─── */
.health-header { .health-header {
margin-bottom: 20px; margin-bottom: var(--tk-section-gap);
} }
.health-title { .health-title {
@@ -19,18 +19,18 @@
/* ─── 录入区 ─── */ /* ─── 录入区 ─── */
.input-section { .input-section {
margin-bottom: 20px; margin-bottom: var(--tk-section-gap);
} }
.input-group { .input-group {
margin-bottom: 12px; margin-bottom: var(--tk-gap-sm);
} }
.input-label { .input-label {
font-size: var(--tk-font-cap); font-size: var(--tk-font-cap);
color: var(--tk-text-secondary); color: var(--tk-text-secondary);
display: block; display: block;
margin-bottom: 4px; margin-bottom: var(--tk-gap-2xs);
} }
.input-field { .input-field {
@@ -38,7 +38,7 @@
background: $bg; background: $bg;
border: 2px solid $bd; border: 2px solid $bd;
border-radius: $r-sm; border-radius: $r-sm;
padding: 0 16px; padding: 0 var(--tk-gap-md);
font-family: 'Georgia', 'Times New Roman', serif; font-family: 'Georgia', 'Times New Roman', serif;
font-size: var(--tk-font-body-lg); font-size: var(--tk-font-body-lg);
font-weight: 600; font-weight: 600;
@@ -51,19 +51,19 @@
font-size: var(--tk-font-cap); font-size: var(--tk-font-cap);
color: var(--tk-text-secondary); color: var(--tk-text-secondary);
display: block; display: block;
margin-top: 8px; margin-top: var(--tk-gap-xs);
margin-bottom: 4px; margin-bottom: var(--tk-gap-2xs);
} }
.input-label--secondary { .input-label--secondary {
margin-top: 20px; margin-top: var(--tk-section-gap);
} }
/* ─── 血糖时段选择 ─── */ /* ─── 血糖时段选择 ─── */
.period-group { .period-group {
display: flex; display: flex;
gap: 8px; gap: var(--tk-gap-xs);
margin-top: 12px; margin-top: var(--tk-gap-sm);
} }
.period-btn { .period-btn {
@@ -74,7 +74,7 @@
@include flex-center; @include flex-center;
&.period-active { &.period-active {
background: $pri; background: var(--tk-pri);
.period-btn-text { .period-btn-text {
color: $white; color: $white;
@@ -82,7 +82,7 @@
} }
&:active { &:active {
opacity: 0.85; opacity: var(--tk-touch-feedback-opacity);
} }
} }
@@ -97,13 +97,13 @@
width: 100%; width: 100%;
height: 52px; height: 52px;
border-radius: $r-sm; border-radius: $r-sm;
background: $pri; background: var(--tk-pri);
@include flex-center; @include flex-center;
margin-top: 20px; margin-top: var(--tk-section-gap);
box-shadow: 0 2px 8px rgba(196, 98, 58, 0.25); box-shadow: 0 2px 8px rgba($pri, 0.25);
&:active { &:active {
opacity: 0.85; opacity: var(--tk-touch-feedback-opacity);
} }
} }
@@ -115,7 +115,7 @@
/* ─── 趋势图 ─── */ /* ─── 趋势图 ─── */
.trend-section { .trend-section {
margin-bottom: 12px; margin-bottom: var(--tk-gap-sm);
} }
.section-title { .section-title {
@@ -132,7 +132,7 @@
} }
.trend-chart { .trend-chart {
padding: 16px; padding: var(--tk-gap-md);
} }
.trend-bars { .trend-bars {
@@ -141,7 +141,7 @@
height: 120px; height: 120px;
background: $bg; background: $bg;
border-radius: $r-sm; border-radius: $r-sm;
padding: 12px 8px; padding: var(--tk-gap-sm) var(--tk-gap-xs);
gap: 0; gap: 0;
position: relative; position: relative;
} }
@@ -180,7 +180,7 @@
opacity: 0.8; opacity: 0.8;
&.trend-bar-normal { &.trend-bar-normal {
background: $pri; background: var(--tk-pri);
} }
&.trend-bar-warn { &.trend-bar-warn {
@@ -191,21 +191,21 @@
.trend-bar-label { .trend-bar-label {
font-size: var(--tk-font-micro); font-size: var(--tk-font-micro);
color: var(--tk-text-secondary); color: var(--tk-text-secondary);
margin-top: 6px; margin-top: var(--tk-gap-2xs);
} }
/* ─── BLE 设备卡片 ─── */ /* ─── BLE 设备卡片 ─── */
.device-section { .device-section {
margin-bottom: 12px; margin-bottom: var(--tk-gap-sm);
} }
.device-card { .device-card {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 12px; gap: var(--tk-gap-sm);
&:active { &:active {
opacity: 0.85; opacity: var(--tk-touch-feedback-opacity);
} }
} }
@@ -213,7 +213,7 @@
width: 48px; width: 48px;
height: 48px; height: 48px;
border-radius: $r-sm; border-radius: $r-sm;
background: $pri-l; background: var(--tk-pri-l);
@include flex-center; @include flex-center;
flex-shrink: 0; flex-shrink: 0;
} }
@@ -249,7 +249,7 @@
/* ─── 健康资讯入口 ─── */ /* ─── 健康资讯入口 ─── */
.article-entry { .article-entry {
&:active { &:active {
opacity: 0.85; opacity: var(--tk-touch-feedback-opacity);
} }
} }
@@ -263,8 +263,8 @@
.ai-suggestion-card { .ai-suggestion-card {
background: $acc-l; background: $acc-l;
border-radius: $r; border-radius: $r;
padding: 16px; padding: var(--tk-gap-md);
margin-bottom: 20px; margin-bottom: var(--tk-section-gap);
box-shadow: none; box-shadow: none;
border-left: 4px solid $acc; border-left: 4px solid $acc;
} }
@@ -273,7 +273,7 @@
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
margin-bottom: 8px; margin-bottom: var(--tk-gap-xs);
} }
.ai-card-title { .ai-card-title {
@@ -291,8 +291,8 @@
.ai-suggestion-item { .ai-suggestion-item {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 8px; gap: var(--tk-gap-xs);
padding: 6px 0; padding: var(--tk-gap-2xs) 0;
} }
.ai-risk-dot { .ai-risk-dot {

View File

@@ -6,7 +6,7 @@
═══════════════════════════════════════ */ ═══════════════════════════════════════ */
.home-page { .home-page {
padding-bottom: calc(100px + env(safe-area-inset-bottom)); padding-bottom: calc(var(--tk-tabbar-space) + env(safe-area-inset-bottom));
} }
/* ─── 问候区 ─── */ /* ─── 问候区 ─── */
@@ -27,7 +27,7 @@
font-weight: 700; font-weight: 700;
color: $tx; color: $tx;
display: block; display: block;
margin-bottom: 4px; margin-bottom: var(--tk-gap-2xs);
} }
.greeting-date { .greeting-date {
@@ -40,18 +40,18 @@
width: 48px; width: 48px;
height: 48px; height: 48px;
border-radius: $r-pill; border-radius: $r-pill;
background: $pri-l; background: var(--tk-pri-l);
@include flex-center; @include flex-center;
flex-shrink: 0; flex-shrink: 0;
&:active { &:active {
opacity: 0.7; opacity: var(--tk-touch-feedback-opacity);
} }
} }
.greeting-bell-icon { .greeting-bell-icon {
font-size: var(--tk-font-body-sm); font-size: var(--tk-font-body-sm);
color: $pri-d; color: var(--tk-pri-d);
} }
.greeting-bell-dot { .greeting-bell-dot {
@@ -66,13 +66,13 @@
/* ─── 今日体征进度 ─── */ /* ─── 今日体征进度 ─── */
.checkin-card { .checkin-card {
margin-bottom: 16px; margin-bottom: var(--tk-gap-md);
display: flex; display: flex;
align-items: center; align-items: center;
gap: 16px; gap: var(--tk-gap-md);
&:active { &:active {
opacity: 0.9; opacity: var(--tk-touch-feedback-opacity);
} }
} }
@@ -91,18 +91,18 @@
font-weight: 600; font-weight: 600;
color: $tx; color: $tx;
display: block; display: block;
margin-bottom: 8px; margin-bottom: var(--tk-gap-xs);
} }
.checkin-capsules { .checkin-capsules {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
gap: 6px; gap: var(--tk-gap-2xs);
} }
.capsule { .capsule {
font-size: var(--tk-font-micro); font-size: var(--tk-font-micro);
padding: 3px 8px; padding: 3px var(--tk-gap-xs);
border-radius: $r-pill; border-radius: $r-pill;
font-weight: 500; font-weight: 500;
@@ -117,9 +117,9 @@
} }
} }
/* ─── 今日体征 2x2 ─── */ /* ─── 今日体征 2x2(原型 padding:14px 16px, gap:10─── */
.vitals-section { .vitals-section {
margin-bottom: 16px; margin-bottom: var(--tk-gap-md);
} }
.section-title { .section-title {
@@ -134,7 +134,7 @@
.vital-card { .vital-card {
&:active { &:active {
opacity: 0.7; opacity: var(--tk-touch-feedback-opacity);
} }
} }
@@ -142,13 +142,13 @@
font-size: var(--tk-font-cap); font-size: var(--tk-font-cap);
color: $tx2; color: $tx2;
display: block; display: block;
margin-bottom: 6px; margin-bottom: var(--tk-gap-2xs);
} }
.vital-value-row { .vital-value-row {
display: flex; display: flex;
align-items: baseline; align-items: baseline;
margin-bottom: 6px; margin-bottom: var(--tk-gap-2xs);
} }
.vital-value { .vital-value {
@@ -173,10 +173,10 @@
.vital-tag { .vital-tag {
font-size: var(--tk-font-micro); font-size: var(--tk-font-micro);
font-weight: 500; font-weight: 500;
padding: 2px 8px; padding: 2px var(--tk-gap-xs);
border-radius: $r-pill; border-radius: $r-pill;
display: inline-block; display: inline-block;
margin-top: 4px; margin-top: var(--tk-gap-2xs);
&.tag-ok { &.tag-ok {
background: $acc-l; background: $acc-l;
@@ -196,10 +196,10 @@
/* ─── 智能提醒卡片 ─── */ /* ─── 智能提醒卡片 ─── */
.reminder-card { .reminder-card {
background: linear-gradient(135deg, $pri 0%, $pri-d 100%); background: linear-gradient(135deg, var(--tk-pri) 0%, var(--tk-pri-d) 100%);
border-radius: $r; border-radius: var(--tk-card-radius);
padding: 18px; padding: var(--tk-gap-md);
margin-bottom: 16px; margin-bottom: var(--tk-gap-md);
color: $white; color: $white;
} }
@@ -207,7 +207,7 @@
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
margin-bottom: 10px; margin-bottom: var(--tk-gap-sm);
} }
.reminder-title { .reminder-title {
@@ -225,11 +225,11 @@
.reminder-item { .reminder-item {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 8px; gap: var(--tk-gap-xs);
padding: 8px 0; padding: var(--tk-gap-xs) 0;
&:active { &:active {
opacity: 0.8; opacity: var(--tk-touch-feedback-opacity);
} }
} }
@@ -239,7 +239,7 @@
.reminder-tag { .reminder-tag {
font-size: var(--tk-font-micro); font-size: var(--tk-font-micro);
padding: 2px 6px; padding: 2px var(--tk-gap-2xs);
border-radius: $r-xs; border-radius: $r-xs;
background: rgba(255, 255, 255, 0.2); background: rgba(255, 255, 255, 0.2);
font-weight: 500; font-weight: 500;
@@ -262,34 +262,34 @@
flex-shrink: 0; flex-shrink: 0;
} }
/* ─── 快捷操作 ─── */ /* ─── 快捷操作(原型 gap:10, height:52, borderRadius:14─── */
.action-section { .action-section {
display: flex; display: flex;
gap: 10px; gap: 10px;
margin-top: 8px; margin-top: var(--tk-gap-xs);
} }
.action-btn { .action-btn {
flex: 1; flex: 1;
height: 52px; height: var(--tk-btn-primary-h);
border-radius: $r-sm; border-radius: 14px;
@include flex-center; @include flex-center;
&:active { &:active {
opacity: 0.85; opacity: var(--tk-touch-feedback-opacity);
} }
} }
.action-primary { .action-primary {
background: $pri; background: var(--tk-pri);
color: $white; color: $white;
box-shadow: 0 2px 8px rgba(196, 98, 58, 0.25); box-shadow: var(--tk-shadow-tab);
} }
.action-outline { .action-outline {
background: transparent; background: transparent;
color: $pri; color: var(--tk-pri);
border: 2px solid $pri; border: 2px solid var(--tk-pri);
} }
.action-btn-text { .action-btn-text {
@@ -302,13 +302,13 @@
═══════════════════════════════════════ */ ═══════════════════════════════════════ */
.guest-page { .guest-page {
padding-bottom: calc(120px + env(safe-area-inset-bottom)); padding-bottom: calc(var(--tk-tabbar-space) + env(safe-area-inset-bottom));
} }
/* ─── 轮播图 ─── */ /* ─── 轮播图 ─── */
.guest-swiper { .guest-swiper {
width: 100%; width: 100%;
height: 360px; height: 400px;
} }
.guest-slide { .guest-slide {
@@ -323,7 +323,7 @@
inset: 0; inset: 0;
&--1 { &--1 {
background: linear-gradient(135deg, $pri-d 0%, $pri 60%, $pri-l 100%); background: linear-gradient(135deg, var(--tk-pri-d) 0%, var(--tk-pri) 60%, var(--tk-pri-l) 100%);
} }
&--2 { &--2 {
background: linear-gradient(135deg, $acc 0%, $acc-d 60%, $acc-l 100%); background: linear-gradient(135deg, $acc 0%, $acc-d 60%, $acc-l 100%);
@@ -340,7 +340,7 @@
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: center; justify-content: center;
padding: 40px 32px; padding: var(--tk-gap-2xl) var(--tk-gap-xl);
} }
.guest-slide-title { .guest-slide-title {
@@ -349,7 +349,7 @@
font-weight: 700; font-weight: 700;
color: $white; color: $white;
display: block; display: block;
margin-bottom: 8px; margin-bottom: var(--tk-gap-xs);
} }
.guest-slide-desc { .guest-slide-desc {
@@ -360,7 +360,7 @@
/* ─── 健康资讯 ─── */ /* ─── 健康资讯 ─── */
.guest-section { .guest-section {
padding: 24px 24px 0; padding: var(--tk-gap-lg) var(--tk-gap-lg) 0;
} }
.guest-section-title { .guest-section-title {
@@ -369,13 +369,13 @@
font-weight: bold; font-weight: bold;
color: $tx; color: $tx;
display: block; display: block;
margin-bottom: 16px; margin-bottom: var(--tk-gap-md);
} }
.guest-articles { .guest-articles {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 12px; gap: var(--tk-gap-sm);
} }
.guest-article-card { .guest-article-card {
@@ -383,7 +383,7 @@
display: flex; display: flex;
&:active { &:active {
opacity: 0.85; opacity: var(--tk-touch-feedback-opacity);
} }
} }
@@ -394,7 +394,7 @@
} }
.guest-article-body { .guest-article-body {
padding: 12px 14px; padding: var(--tk-gap-sm);
flex: 1; flex: 1;
min-width: 0; min-width: 0;
} }
@@ -404,7 +404,7 @@
font-weight: 600; font-weight: 600;
color: $tx; color: $tx;
display: block; display: block;
margin-bottom: 4px; margin-bottom: var(--tk-gap-2xs);
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
@@ -420,7 +420,7 @@
} }
.guest-empty { .guest-empty {
padding: 40px 0; padding: var(--tk-gap-2xl) 0;
text-align: center; text-align: center;
} }
@@ -431,10 +431,10 @@
/* ─── 底部登录引导 ─── */ /* ─── 底部登录引导 ─── */
.guest-login-prompt { .guest-login-prompt {
margin: 24px 24px 0; margin: var(--tk-gap-lg) var(--tk-gap-lg) 0;
display: flex; display: flex;
align-items: center; align-items: center;
gap: 16px; gap: var(--tk-gap-md);
} }
.guest-login-text { .guest-login-text {
@@ -444,15 +444,15 @@
} }
.guest-login-btn { .guest-login-btn {
height: 56px; height: var(--tk-input-height);
padding: 0 28px; padding: 0 var(--tk-card-padding-lg);
background: $pri; background: var(--tk-pri);
border-radius: $r-pill; border-radius: $r-pill;
@include flex-center; @include flex-center;
flex-shrink: 0; flex-shrink: 0;
&:active { &:active {
opacity: 0.85; opacity: var(--tk-touch-feedback-opacity);
} }
} }

View File

@@ -8,7 +8,7 @@ import { usePageData } from '@/hooks/usePageData';
import { useThrottledDidShow } from '@/hooks/useThrottledDidShow'; import { useThrottledDidShow } from '@/hooks/useThrottledDidShow';
import { api } from '@/services/request'; import { api } from '@/services/request';
import type { Article } from '@/services/article'; import type { Article } from '@/services/article';
import ProgressRing from '../../components/ProgressRing'; import ProgressRing from '@/components/ui/ProgressRing';
import Loading from '../../components/Loading'; import Loading from '../../components/Loading';
import PageShell from '@/components/ui/PageShell'; import PageShell from '@/components/ui/PageShell';
import ContentCard from '@/components/ui/ContentCard'; import ContentCard from '@/components/ui/ContentCard';
@@ -202,7 +202,7 @@ function HomeDashboard({ modeClass }: { modeClass: string }) {
<ContentCard variant="elevated" onPress={() => Taro.switchTab({ url: '/pages/health/index' })}> <ContentCard variant="elevated" onPress={() => Taro.switchTab({ url: '/pages/health/index' })}>
<View className='checkin-left'> <View className='checkin-left'>
<ProgressRing percent={progressPercent} /> <ProgressRing progress={progressPercent / 100} />
</View> </View>
<View className='checkin-right'> <View className='checkin-right'>
<Text className='checkin-title'> <Text className='checkin-title'>

View File

@@ -1,12 +1,15 @@
@import '../../styles/variables.scss'; @import '../../styles/variables.scss';
@import '../../styles/mixins.scss'; @import '../../styles/mixins.scss';
// 登录页使用原型精确数值,不走 design token
// 原型参考docs/design/mp-01-login.html
.login-page { .login-page {
// PageShell 接管 min-height, background, scroll min-height: 100vh;
background: $bg;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; padding: 80px 28px 40px;
padding: 100px 40px 60px;
} }
/* ─── 品牌区 ─── */ /* ─── 品牌区 ─── */
@@ -14,70 +17,155 @@
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
margin-bottom: 48px; margin-bottom: 56px;
} }
.login-logo { .login-logo {
width: 96px; width: 80px;
height: 96px; height: 80px;
border-radius: $r-lg; border-radius: 40px;
background: $pri; background: linear-gradient(135deg, var(--tk-pri-l) 0%, var(--tk-pri) 100%);
@include flex-center; @include flex-center;
margin-bottom: 24px; margin-bottom: 20px;
box-shadow: 0 8px 24px rgba($pri, 0.3); box-shadow: 0 4px 16px rgba($pri, 0.25);
} }
.login-logo-mark { .login-logo-letter {
font-family: 'Georgia', 'Times New Roman', serif; font-family: 'Georgia', 'Times New Roman', serif;
font-size: var(--tk-font-hero); font-size: 36px;
font-weight: 700;
color: $white; color: $white;
font-weight: bold;
line-height: 1; line-height: 1;
} }
.login-title { .login-title {
font-family: 'Georgia', 'Times New Roman', serif; font-family: 'Georgia', 'Times New Roman', serif;
font-size: var(--tk-font-num); font-size: 28px;
font-weight: 700;
color: $tx; color: $tx;
font-weight: bold;
margin-bottom: 8px; margin-bottom: 8px;
} }
.login-subtitle { .login-subtitle {
font-size: var(--tk-font-body-sm); font-size: 14px;
color: $tx2; color: $tx3;
letter-spacing: 0.05em;
} }
/* ─── 装饰线 ─── */ /* ─── 输入框 ─── */
.login-divider { .login-field {
width: 48px; height: 56px;
margin-bottom: 40px; background: $card;
border: 1.5px solid $bd;
border-radius: 16px;
display: flex;
align-items: center;
padding: 0 16px;
margin-bottom: 12px;
} }
.login-divider-line { .login-input {
height: 3px; flex: 1;
background: $pri; height: 100%;
border-radius: $r-xs; font-size: 16px;
opacity: 0.4; color: $tx;
}
.login-placeholder {
color: $tx3;
font-size: 16px;
}
.login-eye {
font-size: 14px;
color: var(--tk-pri);
font-weight: 500;
padding: 6px 0;
flex-shrink: 0;
} }
/* ─── 登录按钮 ─── */ /* ─── 登录按钮 ─── */
.login-body { .login-submit {
height: 54px;
border-radius: 16px;
background: var(--tk-pri);
@include flex-center;
margin-top: 12px;
margin-bottom: 16px;
box-shadow: 0 4px 16px rgba($pri, 0.3);
&:active {
opacity: var(--tk-touch-feedback-opacity);
}
}
.login-submit-text {
font-size: 18px;
font-weight: 600;
color: $white;
}
/* ─── 分隔线 ─── */
.login-divider {
display: flex;
align-items: center;
gap: 16px;
margin-bottom: 16px;
}
.login-divider-line {
flex: 1;
height: 1px;
background: $bd-l;
}
.login-divider-text {
font-size: 12px;
color: $tx3;
}
/* ─── 微信登录 ─── */
.login-wechat-btn {
height: 54px;
border-radius: 16px;
background: $wechat;
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
margin-bottom: 24px;
&:active {
opacity: var(--tk-touch-feedback-opacity);
}
}
.login-wechat-icon {
font-size: 16px;
color: $white;
font-weight: bold;
}
.login-wechat-text {
font-size: 18px;
font-weight: 600;
color: $white;
}
/* ─── 手机号绑定(微信登录后) ─── */
.login-bind-section {
width: 100%; width: 100%;
} }
.login-btn { .login-btn-bind {
width: 100%; width: 100%;
height: $btn-primary-h; height: 54px;
background: $pri; background: var(--tk-pri);
color: $white; color: $white;
font-size: var(--tk-font-body-lg); font-size: 18px;
font-weight: 600; font-weight: 600;
border-radius: $r; border-radius: 16px;
border: none; border: none;
@include flex-center; @include flex-center;
letter-spacing: 0.04em;
box-shadow: 0 4px 16px rgba($pri, 0.25); box-shadow: 0 4px 16px rgba($pri, 0.25);
padding: 0; padding: 0;
line-height: 1; line-height: 1;
@@ -85,70 +173,62 @@
&::after { &::after {
border: none; border: none;
} }
&--dev {
margin-top: 16px;
background: $wrn;
box-shadow: 0 4px 16px rgba($wrn, 0.2);
}
&:active {
opacity: 0.85;
}
} }
/* ─── 协议 ─── */ /* ─── 协议 ─── */
.agreement-row { .agreement-row {
display: flex; display: flex;
align-items: flex-start; align-items: flex-start;
margin-top: 28px;
gap: 10px; gap: 10px;
width: 100%;
} }
.agreement-check { .agreement-check {
width: 28px; width: 24px;
height: 28px; height: 24px;
border: 2px solid $bd; border: 1.5px solid $bd;
border-radius: $r-sm; border-radius: 6px;
@include flex-center; @include flex-center;
flex-shrink: 0; flex-shrink: 0;
margin-top: 2px; margin-top: 1px;
transition: all 0.2s;
&.checked { &.checked {
background: $pri; background: var(--tk-pri);
border-color: $pri; border-color: var(--tk-pri);
} }
} }
.agreement-check-mark { .agreement-check-mark {
font-size: var(--tk-font-body-sm); font-size: 14px;
color: $white; color: $white;
font-weight: bold; font-weight: bold;
line-height: 1; line-height: 1;
} }
.agreement-text { .agreement-text {
font-size: var(--tk-font-cap); font-size: 12px;
color: $tx2; color: $tx3;
line-height: 1.7; line-height: 1.8;
} }
.agreement-link { .agreement-link {
color: $pri; color: var(--tk-pri);
font-weight: 500; font-weight: 500;
} }
/* ─── 暂不登录 ─── */ /* ─── 开发模式 ─── */
.skip-row { .login-dev-btn {
width: 100%;
text-align: center; text-align: center;
margin-top: 24px; padding: 12px;
border: 1px dashed $bd;
border-radius: 12px;
margin-top: 16px;
&:active {
opacity: var(--tk-touch-feedback-opacity);
}
} }
.skip-btn { .login-dev-btn-text {
font-size: var(--tk-font-body-sm); font-size: 13px;
color: var(--tk-text-secondary); color: $tx3;
padding: 8px 16px;
} }

View File

@@ -1,28 +1,25 @@
import { useState } from 'react'; import { useState } from 'react';
import { View, Text, Button } from '@tarojs/components'; import { View, Text, Input, Button } from '@tarojs/components';
import Taro from '@tarojs/taro'; import Taro from '@tarojs/taro';
import { useAuthStore } from '../../stores/auth'; import { useAuthStore } from '../../stores/auth';
import { useElderClass } from '../../hooks/useElderClass';
import PageShell from '@/components/ui/PageShell';
import './index.scss'; import './index.scss';
const IS_DEV = process.env.NODE_ENV !== 'production'; const IS_DEV = process.env.NODE_ENV !== 'production';
// 运行时检测是否在 DevTools 模拟器中(弥补编译时 IS_DEV 在 production 构建中为 false 的问题)
const IS_SIMULATOR = typeof __wxConfig !== 'undefined' && (__wxConfig as any).envVersion !== 'release'; const IS_SIMULATOR = typeof __wxConfig !== 'undefined' && (__wxConfig as any).envVersion !== 'release';
export default function Login() { export default function Login() {
const modeClass = useElderClass(); const [username, setUsername] = useState('');
const [needBind, setNeedBind] = useState(false); const [password, setPassword] = useState('');
const [showPassword, setShowPassword] = useState(false);
const [agreed, setAgreed] = useState(false); const [agreed, setAgreed] = useState(false);
const [needBind, setNeedBind] = useState(false);
const credentialLogin = useAuthStore((s) => s.credentialLogin);
const login = useAuthStore((s) => s.login); const login = useAuthStore((s) => s.login);
const bindPhone = useAuthStore((s) => s.bindPhone); const bindPhone = useAuthStore((s) => s.bindPhone);
const loading = useAuthStore((s) => s.loading); const loading = useAuthStore((s) => s.loading);
const isMedicalStaff = useAuthStore((s) => s.isMedicalStaff); const isMedicalStaff = useAuthStore((s) => s.isMedicalStaff);
// 登录页不应用关怀模式(正常模式尺寸已足够大)
const loginClass = '';
const navigateAfterLogin = () => { const navigateAfterLogin = () => {
if (isMedicalStaff()) { if (isMedicalStaff()) {
Taro.reLaunch({ url: '/pages/pkg-doctor-core/index' }); Taro.reLaunch({ url: '/pages/pkg-doctor-core/index' });
@@ -31,6 +28,31 @@ export default function Login() {
} }
}; };
const handleCredentialLogin = async () => {
if (!username.trim()) {
Taro.showToast({ title: '请输入账号', icon: 'none' });
return;
}
if (!password.trim()) {
Taro.showToast({ title: '请输入密码', icon: 'none' });
return;
}
if (!agreed) {
Taro.showToast({ title: '请先阅读并同意用户协议', icon: 'none' });
return;
}
try {
const success = await credentialLogin(username.trim(), password);
if (success) {
navigateAfterLogin();
} else {
Taro.showToast({ title: '账号或密码错误', icon: 'none' });
}
} catch {
Taro.showToast({ title: '登录失败,请重试', icon: 'none' });
}
};
const handleWechatLogin = async () => { const handleWechatLogin = async () => {
if (!agreed) { if (!agreed) {
Taro.showToast({ title: '请先阅读并同意用户协议', icon: 'none' }); Taro.showToast({ title: '请先阅读并同意用户协议', icon: 'none' });
@@ -51,24 +73,18 @@ export default function Login() {
} }
}; };
/** Dev 模式快速登录:跳过 getPhoneNumber用 mock 数据直接调用绑定 API */
const handleDevQuickLogin = async () => { const handleDevQuickLogin = async () => {
try { try {
const success = await bindPhone('dev_mock_encrypted', 'dev_mock_iv'); const success = await credentialLogin('admin', 'Admin@2026');
if (success) { if (success) {
navigateAfterLogin(); navigateAfterLogin();
} }
} catch (err: any) { } catch (err: any) {
Taro.showToast({ title: err?.message || '绑定失败', icon: 'none' }); Taro.showToast({ title: err?.message || '登录失败', icon: 'none' });
setNeedBind(false);
} }
}; };
const handleGetPhone = async (e: { detail: { errMsg: string; encryptedData: string; iv: string } }) => { const handleGetPhone = async (e: { detail: { errMsg: string; encryptedData: string; iv: string } }) => {
if (!agreed) {
Taro.showToast({ title: '请先阅读并同意用户协议', icon: 'none' });
return;
}
if (e.detail.errMsg !== 'getPhoneNumber:ok') { if (e.detail.errMsg !== 'getPhoneNumber:ok') {
Taro.showToast({ title: '需要授权手机号', icon: 'none' }); Taro.showToast({ title: '需要授权手机号', icon: 'none' });
return; return;
@@ -80,81 +96,115 @@ export default function Login() {
navigateAfterLogin(); navigateAfterLogin();
} }
} catch (err: any) { } catch (err: any) {
const msg = err?.message || '绑定失败';
Taro.showModal({ Taro.showModal({
title: '绑定手机号失败', title: '绑定手机号失败',
content: msg, content: err?.message || '绑定失败',
confirmText: '重新登录', confirmText: '重新登录',
cancelText: '取消', cancelText: '取消',
success: (res) => { success: (res) => { if (res.confirm) setNeedBind(false); },
if (res.confirm) {
setNeedBind(false);
}
},
}); });
} }
}; };
return ( return (
<PageShell padding="none" scroll className={`login-page ${loginClass}`}> <View className="login-page">
{/* 品牌区 */} {/* 品牌区 */}
<View className='login-brand'> <View className="login-brand">
<View className='login-logo'> <View className="login-logo">
<Text className='login-logo-mark'>+</Text> <Text className="login-logo-letter">H</Text>
</View>
<Text className="login-title">HMS </Text>
<Text className="login-subtitle"></Text>
</View>
{!needBind ? (
<>
{/* 账号输入 */}
<View className="login-field">
<Input
className="login-input"
type="text"
placeholder="请输入账号"
placeholderClass="login-placeholder"
value={username}
onInput={(e) => setUsername(e.detail.value)}
/>
</View> </View>
<Text className='login-title'></Text>
<Text className='login-subtitle'></Text>
</View>
{/* 装饰线 */} {/* 密码输入 */}
<View className='login-divider'> <View className="login-field">
<View className='login-divider-line' /> <Input
</View> className="login-input"
type="text"
{/* 登录按钮 */} password={!showPassword}
<View className='login-body'> placeholder="请输入密码"
{!needBind ? ( placeholderClass="login-placeholder"
<Button className='login-btn' onClick={handleWechatLogin} loading={loading}> value={password}
onInput={(e) => setPassword(e.detail.value)}
</Button> />
) : ( <Text
<> className="login-eye"
<Button onClick={() => setShowPassword(!showPassword)}
className='login-btn' >
openType='getPhoneNumber' {showPassword ? '隐藏' : '显示'}
onGetPhoneNumber={handleGetPhone} </Text>
loading={loading}
>
</Button>
{(IS_DEV || IS_SIMULATOR) && (
<Button className='login-btn login-btn--dev' onClick={handleDevQuickLogin} loading={loading}>
</Button>
)}
</>
)}
</View>
{/* 协议 */}
<View className='agreement-row'>
<View className={`agreement-check ${agreed ? 'checked' : ''}`} onClick={() => setAgreed(!agreed)}>
{agreed && <Text className='agreement-check-mark'>&#10003;</Text>}
</View> </View>
<Text className='agreement-text'>
<Text className='agreement-link' onClick={() => Taro.navigateTo({ url: '/pages/legal/user-agreement' })}></Text>
<Text className='agreement-link' onClick={() => Taro.navigateTo({ url: '/pages/legal/privacy-policy' })}></Text>
</Text>
</View>
{/* 暂不登录 */} {/* 登录按钮 */}
<View className='skip-row'> <View className="login-submit" onClick={handleCredentialLogin}>
<Text className='skip-btn' onClick={() => Taro.reLaunch({ url: '/pages/index/index' })}> <Text className="login-submit-text">{loading ? '登录中...' : '登录'}</Text>
</View>
</Text>
{/* 分隔线 */}
<View className="login-divider">
<View className="login-divider-line" />
<Text className="login-divider-text"></Text>
<View className="login-divider-line" />
</View>
{/* 微信一键登录 */}
<View className="login-wechat-btn" onClick={handleWechatLogin}>
<Text className="login-wechat-icon"></Text>
<Text className="login-wechat-text"></Text>
</View>
</>
) : (
<View className="login-bind-section">
<Button
className="login-btn-bind"
openType="getPhoneNumber"
onGetPhoneNumber={handleGetPhone}
loading={loading}
>
</Button>
</View> </View>
</PageShell> )}
{/* 协议 */}
<View className="agreement-row">
<View
className={`agreement-check ${agreed ? 'checked' : ''}`}
onClick={() => setAgreed(!agreed)}
>
{agreed && <Text className="agreement-check-mark">&#10003;</Text>}
</View>
<Text className="agreement-text">
<Text className="agreement-link" onClick={() => Taro.navigateTo({ url: '/pages/legal/user-agreement' })}></Text>
<Text className="agreement-link" onClick={() => Taro.navigateTo({ url: '/pages/legal/privacy-policy' })}></Text>
</Text>
</View>
<View style={{ flex: 1 }} />
{/* 开发模式 */}
{(IS_DEV || IS_SIMULATOR) && (
<View className="login-dev-btn" onClick={handleDevQuickLogin}>
<Text className="login-dev-btn-text"> </Text>
</View>
)}
</View>
); );
} }

View File

@@ -2,27 +2,27 @@
@import '../../styles/mixins.scss'; @import '../../styles/mixins.scss';
.mall-page { .mall-page {
padding-bottom: calc(120px + env(safe-area-inset-bottom)); padding-bottom: calc(var(--tk-tabbar-space) + env(safe-area-inset-bottom));
} }
/* ─── 积分余额卡片 ─── */ /* ─── 积分余额卡片 ─── */
.mall-header { .mall-header {
background: linear-gradient(135deg, $pri 0%, $pri-d 100%); background: linear-gradient(135deg, var(--tk-pri) 0%, var(--tk-pri-d) 100%);
padding: 48px 32px 36px; padding: var(--tk-gap-2xl) var(--tk-gap-xl) var(--tk-gap-xl);
} }
.points-card { .points-card {
background: rgba(255, 255, 255, 0.15); background: rgba(255, 255, 255, 0.15);
border: 1px solid rgba(255, 255, 255, 0.2); border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: $r-lg; border-radius: $r-lg;
padding: 32px; padding: var(--tk-gap-xl);
} }
.points-top { .points-top {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
margin-bottom: 16px; margin-bottom: var(--tk-gap-md);
} }
.points-label { .points-label {
@@ -33,12 +33,12 @@
.checkin-btn { .checkin-btn {
background: rgba(255, 255, 255, 0.25); background: rgba(255, 255, 255, 0.25);
border: 1px solid rgba(255, 255, 255, 0.4); border: 1px solid rgba(255, 255, 255, 0.4);
padding: 10px 28px; padding: var(--tk-gap-sm) var(--tk-card-padding-lg);
border-radius: $r-pill; border-radius: $r-pill;
transition: all 0.2s; transition: all 0.2s;
&:active { &:active {
opacity: 0.8; opacity: var(--tk-touch-feedback-opacity);
} }
&.checked { &.checked {
@@ -63,7 +63,7 @@
font-weight: bold; font-weight: bold;
color: $white; color: $white;
display: block; display: block;
margin-bottom: 8px; margin-bottom: var(--tk-gap-xs);
letter-spacing: 2px; letter-spacing: 2px;
line-height: 1; line-height: 1;
} }
@@ -77,13 +77,13 @@
/* ─── 商品类型切换 ─── */ /* ─── 商品类型切换 ─── */
.type-tabs { .type-tabs {
display: flex; display: flex;
padding: 20px 24px 0; padding: var(--tk-section-gap) var(--tk-page-padding) 0;
} }
.type-tab { .type-tab {
flex: 1; flex: 1;
text-align: center; text-align: center;
padding: 16px 0; padding: var(--tk-gap-md) 0;
position: relative; position: relative;
min-height: 48px; min-height: 48px;
@@ -95,7 +95,7 @@
transform: translateX(-50%); transform: translateX(-50%);
width: 48px; width: 48px;
height: 4px; height: 4px;
background: $pri; background: var(--tk-pri);
border-radius: $r-xs; border-radius: $r-xs;
} }
} }
@@ -105,7 +105,7 @@
color: $tx2; color: $tx2;
&.active { &.active {
color: $pri; color: var(--tk-pri);
font-weight: 600; font-weight: 600;
} }
} }
@@ -114,15 +114,15 @@
.product-grid { .product-grid {
display: grid; display: grid;
grid-template-columns: 1fr 1fr; grid-template-columns: 1fr 1fr;
gap: 16px; gap: var(--tk-gap-md);
padding: 20px 24px; padding: var(--tk-section-gap) var(--tk-page-padding);
} }
.product-card { .product-card {
overflow: hidden; overflow: hidden;
&:active { &:active {
opacity: 0.7; opacity: var(--tk-touch-feedback-opacity);
} }
} }
@@ -131,7 +131,7 @@
height: 200px; height: 200px;
@include flex-center; @include flex-center;
&.type-physical { background: $pri-l; } &.type-physical { background: var(--tk-pri-l); }
&.type-service { background: $acc-l; } &.type-service { background: $acc-l; }
&.type-privilege { background: $wrn-l; } &.type-privilege { background: $wrn-l; }
} }
@@ -140,7 +140,7 @@
font-family: 'Georgia', 'Times New Roman', serif; font-family: 'Georgia', 'Times New Roman', serif;
font-size: var(--tk-font-hero); font-size: var(--tk-font-hero);
font-weight: bold; font-weight: bold;
color: $pri; color: var(--tk-pri);
line-height: 1; line-height: 1;
.type-service & { color: $acc; } .type-service & { color: $acc; }
@@ -148,7 +148,7 @@
} }
.product-info { .product-info {
padding: 20px; padding: var(--tk-section-gap);
} }
.product-name { .product-name {
@@ -156,7 +156,7 @@
font-weight: 600; font-weight: 600;
color: $tx; color: $tx;
display: block; display: block;
margin-bottom: 12px; margin-bottom: var(--tk-gap-sm);
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
@@ -171,7 +171,7 @@
.product-points { .product-points {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 4px; gap: var(--tk-gap-2xs);
} }
.product-points-char { .product-points-char {
@@ -190,7 +190,7 @@
.product-stock { .product-stock {
font-size: var(--tk-font-body); font-size: var(--tk-font-body);
padding: 2px 10px; padding: 2px var(--tk-gap-sm);
border-radius: $r-sm; border-radius: $r-sm;
&.out { &.out {

View File

@@ -3,13 +3,13 @@
.messages-page { .messages-page {
// PageShell 接管 min-height, background // PageShell 接管 min-height, background
padding: 20px 24px 100px; padding: var(--tk-section-gap) var(--tk-page-padding) var(--tk-tabbar-space);
padding-bottom: calc(100px + env(safe-area-inset-bottom)); padding-bottom: calc(var(--tk-tabbar-space) + env(safe-area-inset-bottom));
} }
/* ─── 页头 ─── */ /* ─── 页头 ─── */
.messages-header { .messages-header {
margin-bottom: 20px; margin-bottom: var(--tk-section-gap);
} }
.messages-title { .messages-title {
@@ -26,7 +26,7 @@
background: $surface-alt; background: $surface-alt;
border-radius: $r-sm; border-radius: $r-sm;
padding: 3px; padding: 3px;
margin-bottom: 12px; margin-bottom: var(--tk-gap-sm);
} }
.msg-segment-tab { .msg-segment-tab {
@@ -37,7 +37,7 @@
position: relative; position: relative;
&:active { &:active {
opacity: 0.85; opacity: var(--tk-touch-feedback-opacity);
} }
} }
@@ -82,13 +82,13 @@
.msg-list { .msg-list {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 8px; gap: var(--tk-gap-xs);
} }
/* ─── 咨询卡片 ─── */ /* ─── 咨询卡片 ─── */
.consult-card { .consult-card {
display: flex; display: flex;
gap: 12px; gap: var(--tk-gap-sm);
align-items: center; align-items: center;
// ContentCard 接管 background, border-radius, padding, box-shadow, active feedback // ContentCard 接管 background, border-radius, padding, box-shadow, active feedback
} }
@@ -107,7 +107,7 @@
} }
.consult-avatar-active { .consult-avatar-active {
background: $pri-l; background: var(--tk-pri-l);
} }
.consult-avatar-char { .consult-avatar-char {
@@ -118,7 +118,7 @@
} }
.consult-avatar-active .consult-avatar-char { .consult-avatar-active .consult-avatar-char {
color: $pri; color: var(--tk-pri);
} }
.consult-body { .consult-body {
@@ -130,7 +130,7 @@
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
margin-bottom: 4px; margin-bottom: var(--tk-gap-2xs);
&:last-child { &:last-child {
margin-bottom: 0; margin-bottom: 0;
@@ -156,7 +156,7 @@
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
flex: 1; flex: 1;
margin-right: 8px; margin-right: var(--tk-gap-xs);
} }
.consult-badge { .consult-badge {
@@ -178,7 +178,7 @@
/* ─── 通知卡片 ─── */ /* ─── 通知卡片 ─── */
.notify-card { .notify-card {
display: flex; display: flex;
gap: 12px; gap: var(--tk-gap-sm);
align-items: flex-start; align-items: flex-start;
// ContentCard 接管 background, border-radius, padding, box-shadow // ContentCard 接管 background, border-radius, padding, box-shadow
} }
@@ -203,8 +203,8 @@
.notify-type-appointment, .notify-type-appointment,
.notify-type-points { .notify-type-points {
background: $pri-l; background: var(--tk-pri-l);
color: $pri; color: var(--tk-pri);
} }
.notify-type-alert { .notify-type-alert {
@@ -227,7 +227,7 @@
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
margin-bottom: 4px; margin-bottom: var(--tk-gap-2xs);
} }
.notify-title { .notify-title {
@@ -244,7 +244,7 @@
font-size: var(--tk-font-micro); font-size: var(--tk-font-micro);
color: var(--tk-text-secondary); color: var(--tk-text-secondary);
flex-shrink: 0; flex-shrink: 0;
margin-left: 8px; margin-left: var(--tk-gap-xs);
} }
.notify-desc { .notify-desc {
@@ -257,7 +257,7 @@
width: 8px; width: 8px;
height: 8px; height: 8px;
border-radius: $r-xs; border-radius: $r-xs;
background: $pri; background: var(--tk-pri);
flex-shrink: 0; flex-shrink: 0;
margin-top: 6px; margin-top: var(--tk-gap-2xs);
} }

View File

@@ -12,7 +12,7 @@
.chat-header { .chat-header {
display: flex; display: flex;
align-items: center; align-items: center;
padding: 12px 16px; padding: var(--tk-gap-sm) var(--tk-gap-md);
background: $card; background: $card;
border-bottom: 1px solid $bd-l; border-bottom: 1px solid $bd-l;
flex-shrink: 0; flex-shrink: 0;
@@ -25,13 +25,13 @@
z-index: 1; z-index: 1;
&:active { &:active {
opacity: 0.7; opacity: var(--tk-touch-feedback-opacity);
} }
} }
.chat-header__back-text { .chat-header__back-text {
font-size: var(--tk-font-body-sm); font-size: var(--tk-font-body-sm);
color: $pri; color: var(--tk-pri);
} }
.chat-header__center { .chat-header__center {
@@ -60,14 +60,14 @@
/* ─── 消息区 ─── */ /* ─── 消息区 ─── */
.chat-messages { .chat-messages {
flex: 1; flex: 1;
padding: 16px 16px 0; padding: var(--tk-gap-md) var(--tk-gap-md) 0;
overflow-y: auto; overflow-y: auto;
} }
.msg-row { .msg-row {
display: flex; display: flex;
margin-bottom: 16px; margin-bottom: var(--tk-gap-md);
gap: 8px; gap: var(--tk-gap-xs);
&--self { &--self {
justify-content: flex-end; justify-content: flex-end;
@@ -79,7 +79,7 @@
width: 32px; width: 32px;
height: 32px; height: 32px;
border-radius: $r; border-radius: $r;
background: $pri-l; background: var(--tk-pri-l);
@include flex-center; @include flex-center;
flex-shrink: 0; flex-shrink: 0;
} }
@@ -88,13 +88,13 @@
@include serif-number; @include serif-number;
font-size: var(--tk-font-cap); font-size: var(--tk-font-cap);
font-weight: 700; font-weight: 700;
color: $pri; color: var(--tk-pri);
} }
/* ─── 消息气泡 ─── */ /* ─── 消息气泡 ─── */
.msg-bubble { .msg-bubble {
max-width: 70%; max-width: 70%;
padding: 12px 16px; padding: var(--tk-gap-sm) var(--tk-gap-md);
box-shadow: $shadow-sm; box-shadow: $shadow-sm;
&--other { &--other {
@@ -103,7 +103,7 @@
} }
&--self { &--self {
background: $pri; background: var(--tk-pri);
border-radius: $r $r $r-xs $r; border-radius: $r $r $r-xs $r;
} }
} }
@@ -123,7 +123,7 @@
.msg-date-divider { .msg-date-divider {
display: flex; display: flex;
justify-content: center; justify-content: center;
padding: 12px 0; padding: var(--tk-gap-sm) 0;
&__text { &__text {
font-size: var(--tk-font-micro); font-size: var(--tk-font-micro);
@@ -137,7 +137,7 @@
.msg-truncated-hint { .msg-truncated-hint {
display: flex; display: flex;
justify-content: center; justify-content: center;
padding: 12px 0; padding: var(--tk-gap-sm) 0;
&__text { &__text {
font-size: var(--tk-font-micro); font-size: var(--tk-font-micro);
@@ -151,14 +151,14 @@
.msg-image { .msg-image {
width: 200px; width: 200px;
border-radius: $r-sm; border-radius: $r-sm;
margin-top: 4px; margin-top: var(--tk-gap-2xs);
} }
.msg-time { .msg-time {
font-size: var(--tk-font-micro); font-size: var(--tk-font-micro);
color: var(--tk-text-secondary); color: var(--tk-text-secondary);
display: block; display: block;
margin-top: 4px; margin-top: var(--tk-gap-2xs);
.msg-bubble--self & { .msg-bubble--self & {
color: rgba(255, 255, 255, 0.7); color: rgba(255, 255, 255, 0.7);
@@ -168,7 +168,7 @@
.chat-empty { .chat-empty {
text-align: center; text-align: center;
padding: 80px 24px; padding: 80px var(--tk-page-padding);
&__text { &__text {
font-size: var(--tk-font-cap); font-size: var(--tk-font-cap);
@@ -202,10 +202,10 @@
width: 48px; width: 48px;
height: 48px; height: 48px;
border-radius: $r-lg; border-radius: $r-lg;
background: $pri; background: var(--tk-pri);
@include flex-center; @include flex-center;
flex-shrink: 0; flex-shrink: 0;
box-shadow: 0 2px 6px rgba(196, 98, 58, 0.3); box-shadow: 0 2px 6px rgba($pri, 0.3);
&--disabled { &--disabled {
opacity: 0.5; opacity: 0.5;
@@ -219,7 +219,7 @@
} }
.chat-closed-bar { .chat-closed-bar {
padding: 16px; padding: var(--tk-gap-md);
text-align: center; text-align: center;
background: $card; background: $card;
border-top: 1px solid $bd-l; border-top: 1px solid $bd-l;

View File

@@ -2,12 +2,12 @@
@import '../../../../styles/mixins.scss'; @import '../../../../styles/mixins.scss';
.alert-detail-header { .alert-detail-header {
margin-bottom: 24px; margin-bottom: var(--tk-gap-lg);
&__tags { &__tags {
display: flex; display: flex;
gap: 12px; gap: var(--tk-gap-sm);
margin-bottom: 12px; margin-bottom: var(--tk-gap-sm);
} }
&__time { &__time {
@@ -19,7 +19,7 @@
.detail-severity { .detail-severity {
font-size: var(--tk-font-h2); font-size: var(--tk-font-h2);
font-weight: 600; font-weight: 600;
padding: 6px 16px; padding: var(--tk-gap-2xs) var(--tk-gap-md);
border-radius: $r-sm; border-radius: $r-sm;
&--info { &--info {
@@ -45,7 +45,7 @@
.detail-status { .detail-status {
font-size: var(--tk-font-h2); font-size: var(--tk-font-h2);
padding: 6px 16px; padding: var(--tk-gap-2xs) var(--tk-gap-md);
border-radius: $r-sm; border-radius: $r-sm;
&--pending { &--pending {
@@ -54,8 +54,8 @@
} }
&--acknowledged { &--acknowledged {
background: $pri-l; background: var(--tk-pri-l);
color: $pri; color: var(--tk-pri);
} }
&--resolved { &--resolved {
@@ -73,7 +73,7 @@
&__label { &__label {
font-size: var(--tk-font-h2); font-size: var(--tk-font-h2);
color: $tx2; color: $tx2;
margin-bottom: 8px; margin-bottom: var(--tk-gap-xs);
} }
&__value { &__value {
@@ -94,18 +94,18 @@
line-height: 1.6; line-height: 1.6;
white-space: pre-wrap; white-space: pre-wrap;
background: $bg; background: $bg;
padding: 16px; padding: var(--tk-gap-md);
border-radius: $r; border-radius: $r;
margin-top: 8px; margin-top: var(--tk-gap-xs);
} }
} }
} }
.alert-detail-actions { .alert-detail-actions {
display: flex; display: flex;
gap: 16px; gap: var(--tk-gap-md);
margin-top: 32px; margin-top: var(--tk-gap-xl);
padding: 0 8px; padding: 0 var(--tk-gap-xs);
} }
.alert-action-btn { .alert-action-btn {
@@ -118,7 +118,7 @@
text-align: center; text-align: center;
&--primary { &--primary {
background: $pri; background: var(--tk-pri);
color: $card; color: $card;
border: none; border: none;

View File

@@ -9,7 +9,7 @@ import {
import Loading from '@/components/Loading'; import Loading from '@/components/Loading';
import PageShell from '@/components/ui/PageShell'; import PageShell from '@/components/ui/PageShell';
import ContentCard from '@/components/ui/ContentCard'; import ContentCard from '@/components/ui/ContentCard';
import { useElderClass } from '../../../../hooks/useElderClass'; import { useDoctorClass } from '@/hooks/useDoctorClass';
import './index.scss'; import './index.scss';
const SEVERITY_MAP: Record<string, { label: string; className: string }> = { const SEVERITY_MAP: Record<string, { label: string; className: string }> = {
@@ -27,7 +27,7 @@ const STATUS_MAP: Record<string, { label: string; className: string }> = {
}; };
export default function AlertDetail() { export default function AlertDetail() {
const modeClass = useElderClass(); const modeClass = useDoctorClass();
const [alert, setAlert] = useState<Alert | null>(null); const [alert, setAlert] = useState<Alert | null>(null);
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const [actionLoading, setActionLoading] = useState(false); const [actionLoading, setActionLoading] = useState(false);

View File

@@ -10,7 +10,7 @@
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
margin-bottom: 16px; margin-bottom: var(--tk-gap-md);
} }
.alert-list-title { .alert-list-title {
@@ -51,14 +51,14 @@
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
margin-bottom: 12px; margin-bottom: var(--tk-gap-sm);
} }
.alert-card__title { .alert-card__title {
font-size: var(--tk-font-body-lg); font-size: var(--tk-font-body-lg);
font-weight: 500; font-weight: 500;
color: $tx; color: $tx;
margin-bottom: 8px; margin-bottom: var(--tk-gap-xs);
} }
.alert-card__footer { .alert-card__footer {

View File

@@ -11,7 +11,7 @@ import PaginationBar from '@/components/patterns/PaginationBar';
import SearchSection from '@/components/patterns/SearchSection'; import SearchSection from '@/components/patterns/SearchSection';
import ErrorState from '@/components/ErrorState'; import ErrorState from '@/components/ErrorState';
import EmptyState from '@/components/EmptyState'; import EmptyState from '@/components/EmptyState';
import { useElderClass } from '../../../hooks/useElderClass'; import { useDoctorClass } from '@/hooks/useDoctorClass';
import { safeNavigateTo } from '@/utils/navigate'; import { safeNavigateTo } from '@/utils/navigate';
import './index.scss'; import './index.scss';
@@ -51,7 +51,7 @@ const STATUS_FILTERS = [
]; ];
export default function AlertList() { export default function AlertList() {
const modeClass = useElderClass(); const modeClass = useDoctorClass();
const [alerts, setAlerts] = useState<Alert[]>([]); const [alerts, setAlerts] = useState<Alert[]>([]);
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const [error, setError] = useState(false); const [error, setError] = useState(false);

View File

@@ -6,7 +6,7 @@
/* ─── 表单分组间距ContentCard 外层补充) ─── */ /* ─── 表单分组间距ContentCard 外层补充) ─── */
.section { .section {
margin-bottom: 16px; margin-bottom: var(--tk-gap-md);
} }
.section-title { .section-title {
@@ -14,14 +14,14 @@
font-weight: bold; font-weight: bold;
color: $tx; color: $tx;
display: block; display: block;
margin-bottom: 16px; margin-bottom: var(--tk-gap-md);
} }
.form-row { .form-row {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
padding: 16px 0; padding: var(--tk-gap-md) 0;
border-bottom: 1px solid $bd-l; border-bottom: 1px solid $bd-l;
&:last-child { &:last-child {
@@ -59,24 +59,24 @@
.form-textarea { .form-textarea {
width: 100%; width: 100%;
margin-top: 12px; margin-top: var(--tk-gap-sm);
font-size: var(--tk-font-h1); font-size: var(--tk-font-h1);
color: $tx; color: $tx;
min-height: 120px; min-height: 120px;
background: $bg; background: $bg;
border-radius: $r-sm; border-radius: $r-sm;
padding: 16px; padding: var(--tk-gap-md);
} }
.submit-btn { .submit-btn {
background: $pri; background: var(--tk-pri);
border-radius: $r-sm; border-radius: $r-sm;
padding: 24px; padding: var(--tk-gap-lg);
text-align: center; text-align: center;
margin-top: 24px; margin-top: var(--tk-gap-lg);
&:active { &:active {
background: $pri-d; opacity: var(--tk-touch-feedback-opacity);
} }
&--disabled { &--disabled {

View File

@@ -7,7 +7,7 @@ import {
import Loading from '@/components/Loading'; import Loading from '@/components/Loading';
import PageShell from '@/components/ui/PageShell'; import PageShell from '@/components/ui/PageShell';
import ContentCard from '@/components/ui/ContentCard'; import ContentCard from '@/components/ui/ContentCard';
import { useElderClass } from '../../../../hooks/useElderClass'; import { useDoctorClass } from '@/hooks/useDoctorClass';
import { useSafeTimeout } from '@/hooks/useSafeTimeout'; import { useSafeTimeout } from '@/hooks/useSafeTimeout';
import './index.scss'; import './index.scss';
@@ -61,7 +61,7 @@ export default function DialysisCreate() {
const version = router.params.version ? Number(router.params.version) : 0; const version = router.params.version ? Number(router.params.version) : 0;
const patientIdFromRoute = router.params.patientId || ''; const patientIdFromRoute = router.params.patientId || '';
const isEdit = !!id; const isEdit = !!id;
const modeClass = useElderClass(); const modeClass = useDoctorClass();
const [form, setForm] = useState<FormState>({ ...initialForm, patient_id: patientIdFromRoute }); const [form, setForm] = useState<FormState>({ ...initialForm, patient_id: patientIdFromRoute });
const [loading, setLoading] = useState(isEdit); const [loading, setLoading] = useState(isEdit);

View File

@@ -6,14 +6,14 @@
font-weight: bold; font-weight: bold;
color: $tx; color: $tx;
display: block; display: block;
margin-bottom: 12px; margin-bottom: var(--tk-gap-sm);
} }
.record-header { .record-header {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
margin-bottom: 8px; margin-bottom: var(--tk-gap-xs);
} }
.record-header__title { .record-header__title {
@@ -25,15 +25,15 @@
.record-header__status { .record-header__status {
display: inline-block; display: inline-block;
padding: 4px 12px; padding: var(--tk-gap-2xs) var(--tk-gap-sm);
border-radius: $r-xs; border-radius: $r-xs;
font-size: var(--tk-font-body); font-size: var(--tk-font-body);
background: $bd-l; background: $bd-l;
color: $tx3; color: $tx3;
&--completed { &--completed {
background: $pri-l; background: var(--tk-pri-l);
color: $pri; color: var(--tk-pri);
} }
&--reviewed { &--reviewed {
@@ -52,14 +52,14 @@
font-size: var(--tk-font-h2); font-size: var(--tk-font-h2);
color: $tx3; color: $tx3;
display: block; display: block;
margin-top: 8px; margin-top: var(--tk-gap-xs);
font-variant-numeric: tabular-nums; font-variant-numeric: tabular-nums;
} }
.detail-row { .detail-row {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
padding: 10px 0; padding: var(--tk-gap-sm) 0;
border-bottom: 1px solid $bd-l; border-bottom: 1px solid $bd-l;
&:last-child { &:last-child {
@@ -77,13 +77,13 @@
color: $tx; color: $tx;
text-align: right; text-align: right;
flex: 1; flex: 1;
margin-left: 24px; margin-left: var(--tk-gap-lg);
font-variant-numeric: tabular-nums; font-variant-numeric: tabular-nums;
} }
.error-text { .error-text {
text-align: center; text-align: center;
padding: 120px 0; padding: var(--tk-gap-2xl) 0;
font-size: var(--tk-font-body-lg); font-size: var(--tk-font-body-lg);
color: $tx3; color: $tx3;
} }
@@ -91,24 +91,24 @@
.actions { .actions {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 12px; gap: var(--tk-gap-sm);
padding: 16px 0; padding: var(--tk-gap-md) 0;
} }
.action-btn { .action-btn {
border-radius: $r-sm; border-radius: $r-sm;
padding: 20px; padding: var(--tk-section-gap);
text-align: center; text-align: center;
&--primary { &--primary {
background: $pri; background: var(--tk-pri);
.action-btn__text { .action-btn__text {
color: $white; color: $white;
} }
&:active { &:active {
background: $pri-d; opacity: var(--tk-touch-feedback-opacity);
} }
} }
@@ -117,7 +117,7 @@
border: 1px solid $bd; border: 1px solid $bd;
.action-btn__text { .action-btn__text {
color: $pri; color: var(--tk-pri);
} }
} }

View File

@@ -10,14 +10,14 @@ import {
import Loading from '@/components/Loading'; import Loading from '@/components/Loading';
import PageShell from '@/components/ui/PageShell'; import PageShell from '@/components/ui/PageShell';
import ContentCard from '@/components/ui/ContentCard'; import ContentCard from '@/components/ui/ContentCard';
import { useElderClass } from '../../../../hooks/useElderClass'; import { useDoctorClass } from '@/hooks/useDoctorClass';
import { useSafeTimeout } from '@/hooks/useSafeTimeout'; import { useSafeTimeout } from '@/hooks/useSafeTimeout';
import './index.scss'; import './index.scss';
export default function DialysisDetail() { export default function DialysisDetail() {
const router = useRouter(); const router = useRouter();
const id = router.params.id || ''; const id = router.params.id || '';
const modeClass = useElderClass(); const modeClass = useDoctorClass();
const [record, setRecord] = useState<DialysisRecord | null>(null); const [record, setRecord] = useState<DialysisRecord | null>(null);
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const [submitting, setSubmitting] = useState(false); const [submitting, setSubmitting] = useState(false);

View File

@@ -7,7 +7,7 @@
// PaginationBar 已接管pagination 分页样式 // PaginationBar 已接管pagination 分页样式
.record-count { .record-count {
margin-bottom: 16px; margin-bottom: var(--tk-gap-md);
text { text {
font-size: var(--tk-font-h2); font-size: var(--tk-font-h2);
@@ -30,12 +30,12 @@
.type-tag { .type-tag {
display: inline-block; display: inline-block;
padding: 4px 12px; padding: var(--tk-gap-2xs) var(--tk-gap-sm);
border-radius: $r-xs; border-radius: $r-xs;
font-size: var(--tk-font-body); font-size: var(--tk-font-body);
font-weight: 600; font-weight: 600;
background: $pri-l; background: var(--tk-pri-l);
color: $pri-d; color: var(--tk-pri-d);
&--hdf { &--hdf {
background: $acc-l; background: $acc-l;
@@ -51,7 +51,7 @@
.record-card__body { .record-card__body {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
gap: 12px; gap: var(--tk-gap-sm);
} }
.record-card__date { .record-card__date {
@@ -68,12 +68,12 @@
.fab { .fab {
position: fixed; position: fixed;
right: 32px; right: var(--tk-gap-xl);
bottom: 120px; bottom: calc(var(--tk-gap-2xl) + var(--tk-gap-xl));
width: 96px; width: 96px;
height: 96px; height: 96px;
border-radius: $r-pill; border-radius: $r-pill;
background: $pri; background: var(--tk-pri);
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
@@ -81,7 +81,7 @@
z-index: 10; z-index: 10;
&:active { &:active {
background: $pri-d; opacity: var(--tk-touch-feedback-opacity);
} }
} }

View File

@@ -13,7 +13,7 @@ import PaginationBar from '@/components/patterns/PaginationBar';
import SegmentTabs from '@/components/SegmentTabs'; import SegmentTabs from '@/components/SegmentTabs';
import ErrorState from '@/components/ErrorState'; import ErrorState from '@/components/ErrorState';
import EmptyState from '@/components/EmptyState'; import EmptyState from '@/components/EmptyState';
import { useElderClass } from '../../../hooks/useElderClass'; import { useDoctorClass } from '@/hooks/useDoctorClass';
import { safeNavigateTo } from '@/utils/navigate'; import { safeNavigateTo } from '@/utils/navigate';
import './index.scss'; import './index.scss';
@@ -35,7 +35,7 @@ const STATUS_LABEL: Record<string, string> = {
export default function DialysisList() { export default function DialysisList() {
const router = useRouter(); const router = useRouter();
const patientId = router.params.patientId || ''; const patientId = router.params.patientId || '';
const modeClass = useElderClass(); const modeClass = useDoctorClass();
const [searchPatient, setSearchPatient] = useState(''); const [searchPatient, setSearchPatient] = useState('');
const [currentPatientId, setCurrentPatientId] = useState(patientId); const [currentPatientId, setCurrentPatientId] = useState(patientId);
const [activeTab, setActiveTab] = useState(''); const [activeTab, setActiveTab] = useState('');

View File

@@ -6,7 +6,7 @@
/* ─── 表单分组间距ContentCard 外层补充) ─── */ /* ─── 表单分组间距ContentCard 外层补充) ─── */
.section { .section {
margin-bottom: 16px; margin-bottom: var(--tk-gap-md);
} }
.section-title { .section-title {
@@ -14,14 +14,14 @@
font-weight: bold; font-weight: bold;
color: $tx; color: $tx;
display: block; display: block;
margin-bottom: 16px; margin-bottom: var(--tk-gap-md);
} }
.form-row { .form-row {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
padding: 16px 0; padding: var(--tk-gap-md) 0;
border-bottom: 1px solid $bd-l; border-bottom: 1px solid $bd-l;
&:last-child { &:last-child {
@@ -54,24 +54,24 @@
.form-textarea { .form-textarea {
width: 100%; width: 100%;
margin-top: 12px; margin-top: var(--tk-gap-sm);
font-size: var(--tk-font-h1); font-size: var(--tk-font-h1);
color: $tx; color: $tx;
min-height: 120px; min-height: 120px;
background: $bg; background: $bg;
border-radius: $r-sm; border-radius: $r-sm;
padding: 16px; padding: var(--tk-gap-md);
} }
.submit-btn { .submit-btn {
background: $pri; background: var(--tk-pri);
border-radius: $r-sm; border-radius: $r-sm;
padding: 24px; padding: var(--tk-gap-lg);
text-align: center; text-align: center;
margin-top: 24px; margin-top: var(--tk-gap-lg);
&:active { &:active {
background: $pri-d; opacity: var(--tk-touch-feedback-opacity);
} }
&--disabled { &--disabled {

View File

@@ -5,7 +5,7 @@ import { createDialysisPrescription } from '@/services/doctor/dialysis';
import Loading from '@/components/Loading'; import Loading from '@/components/Loading';
import PageShell from '@/components/ui/PageShell'; import PageShell from '@/components/ui/PageShell';
import ContentCard from '@/components/ui/ContentCard'; import ContentCard from '@/components/ui/ContentCard';
import { useElderClass } from '../../../../hooks/useElderClass'; import { useDoctorClass } from '@/hooks/useDoctorClass';
import { useSafeTimeout } from '@/hooks/useSafeTimeout'; import { useSafeTimeout } from '@/hooks/useSafeTimeout';
import './index.scss'; import './index.scss';
@@ -54,7 +54,7 @@ const initialForm: FormState = {
export default function PrescriptionCreate() { export default function PrescriptionCreate() {
const router = useRouter(); const router = useRouter();
const patientId = router.params.patientId || ''; const patientId = router.params.patientId || '';
const modeClass = useElderClass(); const modeClass = useDoctorClass();
const [form, setForm] = useState<FormState>(initialForm); const [form, setForm] = useState<FormState>(initialForm);
const [submitting, setSubmitting] = useState(false); const [submitting, setSubmitting] = useState(false);
const { safeSetTimeout } = useSafeTimeout(); const { safeSetTimeout } = useSafeTimeout();

View File

@@ -6,14 +6,14 @@
font-weight: bold; font-weight: bold;
color: $tx; color: $tx;
display: block; display: block;
margin-bottom: 12px; margin-bottom: var(--tk-gap-sm);
} }
.rx-header { .rx-header {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
margin-bottom: 8px; margin-bottom: var(--tk-gap-xs);
} }
.rx-header__title { .rx-header__title {
@@ -24,7 +24,7 @@
.rx-header__status { .rx-header__status {
display: inline-block; display: inline-block;
padding: 4px 12px; padding: var(--tk-gap-2xs) var(--tk-gap-sm);
border-radius: $r-xs; border-radius: $r-xs;
font-size: var(--tk-font-body); font-size: var(--tk-font-body);
background: $bd-l; background: $bd-l;
@@ -46,7 +46,7 @@
.detail-row { .detail-row {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
padding: 10px 0; padding: var(--tk-gap-sm) 0;
border-bottom: 1px solid $bd-l; border-bottom: 1px solid $bd-l;
&:last-child { &:last-child {
@@ -64,7 +64,7 @@
color: $tx; color: $tx;
text-align: right; text-align: right;
flex: 1; flex: 1;
margin-left: 24px; margin-left: var(--tk-gap-lg);
font-variant-numeric: tabular-nums; font-variant-numeric: tabular-nums;
} }
@@ -76,7 +76,7 @@
.error-text { .error-text {
text-align: center; text-align: center;
padding: 120px 0; padding: var(--tk-gap-2xl) 0;
font-size: var(--tk-font-body-lg); font-size: var(--tk-font-body-lg);
color: $tx3; color: $tx3;
} }
@@ -84,13 +84,13 @@
.actions { .actions {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 12px; gap: var(--tk-gap-sm);
padding: 16px 0; padding: var(--tk-gap-md) 0;
} }
.action-btn { .action-btn {
border-radius: $r-sm; border-radius: $r-sm;
padding: 20px; padding: var(--tk-section-gap);
text-align: center; text-align: center;
&--secondary { &--secondary {
@@ -98,7 +98,7 @@
border: 1px solid $bd; border: 1px solid $bd;
.action-btn__text { .action-btn__text {
color: $pri; color: var(--tk-pri);
} }
} }

View File

@@ -9,14 +9,14 @@ import {
import Loading from '@/components/Loading'; import Loading from '@/components/Loading';
import PageShell from '@/components/ui/PageShell'; import PageShell from '@/components/ui/PageShell';
import ContentCard from '@/components/ui/ContentCard'; import ContentCard from '@/components/ui/ContentCard';
import { useElderClass } from '../../../../hooks/useElderClass'; import { useDoctorClass } from '@/hooks/useDoctorClass';
import { useSafeTimeout } from '@/hooks/useSafeTimeout'; import { useSafeTimeout } from '@/hooks/useSafeTimeout';
import './index.scss'; import './index.scss';
export default function PrescriptionDetail() { export default function PrescriptionDetail() {
const router = useRouter(); const router = useRouter();
const id = router.params.id || ''; const id = router.params.id || '';
const modeClass = useElderClass(); const modeClass = useDoctorClass();
const [rx, setRx] = useState<DialysisPrescription | null>(null); const [rx, setRx] = useState<DialysisPrescription | null>(null);
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const [submitting, setSubmitting] = useState(false); const [submitting, setSubmitting] = useState(false);

View File

@@ -7,7 +7,7 @@
// PaginationBar 已接管pagination 分页样式 // PaginationBar 已接管pagination 分页样式
.prescription-count { .prescription-count {
margin-bottom: 16px; margin-bottom: var(--tk-gap-md);
text { text {
font-size: var(--tk-font-h2); font-size: var(--tk-font-h2);
@@ -25,7 +25,7 @@
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
margin-bottom: 12px; margin-bottom: var(--tk-gap-sm);
} }
.prescription-card__model { .prescription-card__model {
@@ -36,8 +36,8 @@
.prescription-card__body { .prescription-card__body {
display: flex; display: flex;
gap: 16px; gap: var(--tk-gap-md);
margin-bottom: 8px; margin-bottom: var(--tk-gap-xs);
} }
.prescription-card__meta { .prescription-card__meta {
@@ -55,12 +55,12 @@
.fab { .fab {
position: fixed; position: fixed;
right: 32px; right: var(--tk-gap-xl);
bottom: 120px; bottom: calc(var(--tk-gap-2xl) + var(--tk-gap-xl));
width: 96px; width: 96px;
height: 96px; height: 96px;
border-radius: $r-pill; border-radius: $r-pill;
background: $pri; background: var(--tk-pri);
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
@@ -68,7 +68,7 @@
z-index: 10; z-index: 10;
&:active { &:active {
background: $pri-d; opacity: var(--tk-touch-feedback-opacity);
} }
} }

View File

@@ -13,7 +13,7 @@ import PaginationBar from '@/components/patterns/PaginationBar';
import SegmentTabs from '@/components/SegmentTabs'; import SegmentTabs from '@/components/SegmentTabs';
import ErrorState from '@/components/ErrorState'; import ErrorState from '@/components/ErrorState';
import EmptyState from '@/components/EmptyState'; import EmptyState from '@/components/EmptyState';
import { useElderClass } from '../../../hooks/useElderClass'; import { useDoctorClass } from '@/hooks/useDoctorClass';
import { safeNavigateTo } from '@/utils/navigate'; import { safeNavigateTo } from '@/utils/navigate';
import './index.scss'; import './index.scss';
@@ -31,7 +31,7 @@ const STATUS_LABEL: Record<string, string> = {
export default function PrescriptionList() { export default function PrescriptionList() {
const router = useRouter(); const router = useRouter();
const patientId = router.params.patientId || ''; const patientId = router.params.patientId || '';
const modeClass = useElderClass(); const modeClass = useDoctorClass();
const [searchPatient, setSearchPatient] = useState(''); const [searchPatient, setSearchPatient] = useState('');
const [currentPatientId, setCurrentPatientId] = useState(patientId); const [currentPatientId, setCurrentPatientId] = useState(patientId);
const [activeTab, setActiveTab] = useState(''); const [activeTab, setActiveTab] = useState('');

View File

@@ -9,7 +9,7 @@
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
margin-bottom: 12px; margin-bottom: var(--tk-gap-sm);
&__type { &__type {
font-family: 'Georgia', 'Times New Roman', serif; font-family: 'Georgia', 'Times New Roman', serif;
@@ -20,7 +20,7 @@
&__status { &__status {
font-size: var(--tk-font-h2); font-size: var(--tk-font-h2);
padding: 6px 16px; padding: 6px var(--tk-gap-md);
border-radius: $r; border-radius: $r;
font-weight: 500; font-weight: 500;
@@ -39,7 +39,7 @@
font-size: var(--tk-font-h2); font-size: var(--tk-font-h2);
color: $acc; color: $acc;
display: block; display: block;
margin-top: 8px; margin-top: var(--tk-gap-xs);
} }
.indicator-table { .indicator-table {
@@ -48,19 +48,19 @@
.indicator-row { .indicator-row {
display: flex; display: flex;
padding: 16px 0; padding: var(--tk-gap-md) 0;
border-bottom: 1px solid $bd-l; border-bottom: 1px solid $bd-l;
align-items: center; align-items: center;
&--header { &--header {
border-bottom: 2px solid $bd; border-bottom: 2px solid $bd;
padding-bottom: 12px; padding-bottom: var(--tk-gap-sm);
} }
&--abnormal { &--abnormal {
background: $dan-l; background: $dan-l;
margin: 0 -12px; margin: 0 calc(-1 * var(--tk-gap-sm));
padding: 16px 12px; padding: var(--tk-gap-md) var(--tk-gap-sm);
border-radius: $r-sm; border-radius: $r-sm;
} }
} }
@@ -108,9 +108,9 @@
} }
.notes-display { .notes-display {
background: $pri-l; background: var(--tk-pri-l);
border-radius: $r; border-radius: $r;
padding: 20px; padding: var(--tk-section-gap);
} }
.notes-text { .notes-text {
@@ -124,18 +124,18 @@
min-height: 200px; min-height: 200px;
background: $bd-l; background: $bd-l;
border-radius: $r; border-radius: $r;
padding: 20px; padding: var(--tk-section-gap);
font-size: var(--tk-font-h1); font-size: var(--tk-font-h1);
color: $tx; color: $tx;
box-sizing: border-box; box-sizing: border-box;
line-height: 1.6; line-height: 1.6;
margin-bottom: 20px; margin-bottom: var(--tk-section-gap);
} }
.review-btn { .review-btn {
background: $pri; background: var(--tk-pri);
border-radius: $r; border-radius: $r;
padding: 20px; padding: var(--tk-section-gap);
text-align: center; text-align: center;
&--disabled { &--disabled {
@@ -151,7 +151,7 @@
.error-text { .error-text {
text-align: center; text-align: center;
padding: 80px 32px; padding: 80px var(--tk-gap-xl);
color: $tx3; color: $tx3;
font-size: var(--tk-font-body-lg); font-size: var(--tk-font-body-lg);
} }

View File

@@ -6,14 +6,14 @@ import { getLabReport, reviewLabReport, type LabReportDetail } from '@/services/
import Loading from '@/components/Loading'; import Loading from '@/components/Loading';
import PageShell from '@/components/ui/PageShell'; import PageShell from '@/components/ui/PageShell';
import ContentCard from '@/components/ui/ContentCard'; import ContentCard from '@/components/ui/ContentCard';
import { useElderClass } from '../../../../hooks/useElderClass'; import { useDoctorClass } from '@/hooks/useDoctorClass';
import './index.scss'; import './index.scss';
export default function ReportDetail() { export default function ReportDetail() {
const router = useRouter(); const router = useRouter();
const patientId = router.params.patientId || ''; const patientId = router.params.patientId || '';
const reportId = router.params.id || ''; const reportId = router.params.id || '';
const modeClass = useElderClass(); const modeClass = useDoctorClass();
const [report, setReport] = useState<LabReportDetail | null>(null); const [report, setReport] = useState<LabReportDetail | null>(null);
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const [doctorNotes, setDoctorNotes] = useState(''); const [doctorNotes, setDoctorNotes] = useState('');

View File

@@ -6,7 +6,7 @@
// StatusTag 已接管reviewed 标签样式 // StatusTag 已接管reviewed 标签样式
.report-count { .report-count {
margin-bottom: 16px; margin-bottom: var(--tk-gap-md);
text { text {
font-size: var(--tk-font-h2); font-size: var(--tk-font-h2);
@@ -24,7 +24,7 @@
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
margin-bottom: 12px; margin-bottom: var(--tk-gap-sm);
} }
.report-card__type { .report-card__type {
@@ -42,7 +42,7 @@
.report-card__indicators { .report-card__indicators {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 16px; gap: var(--tk-gap-md);
} }
.report-card__abnormal { .report-card__abnormal {

View File

@@ -11,13 +11,13 @@ import LoadingCard from '@/components/ui/LoadingCard';
import SearchSection from '@/components/patterns/SearchSection'; import SearchSection from '@/components/patterns/SearchSection';
import ErrorState from '@/components/ErrorState'; import ErrorState from '@/components/ErrorState';
import EmptyState from '@/components/EmptyState'; import EmptyState from '@/components/EmptyState';
import { useElderClass } from '../../../hooks/useElderClass'; import { useDoctorClass } from '@/hooks/useDoctorClass';
import './index.scss'; import './index.scss';
export default function ReportList() { export default function ReportList() {
const router = useRouter(); const router = useRouter();
const patientId = router.params.patientId || ''; const patientId = router.params.patientId || '';
const modeClass = useElderClass(); const modeClass = useDoctorClass();
const [searchPatient, setSearchPatient] = useState(''); const [searchPatient, setSearchPatient] = useState('');
const [currentPatientId, setCurrentPatientId] = useState(patientId); const [currentPatientId, setCurrentPatientId] = useState(patientId);
const [reports, setReports] = useState<LabReportItem[]>([]); const [reports, setReports] = useState<LabReportItem[]>([]);

View File

@@ -6,28 +6,28 @@
.inbox-list { .inbox-list {
height: calc(100vh - 50px); height: calc(100vh - 50px);
padding: 12px; padding: var(--tk-gap-sm);
} }
.inbox-card { .inbox-card {
margin-bottom: 10px; margin-bottom: var(--tk-gap-sm);
.inbox-card-header { .inbox-card-header {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 8px; gap: var(--tk-gap-xs);
margin-bottom: 6px; margin-bottom: var(--tk-gap-2xs);
} }
.inbox-type-tag { .inbox-type-tag {
color: $card; color: $card;
font-size: var(--tk-font-micro); font-size: var(--tk-font-micro);
padding: 2px 6px; padding: 2px var(--tk-gap-2xs);
border-radius: $r-xs; border-radius: $r-xs;
flex-shrink: 0; flex-shrink: 0;
&--ai { &--ai {
background: $pri; background: var(--tk-pri);
} }
&--alert { &--alert {
@@ -75,7 +75,7 @@
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
padding: 16px 20px; padding: var(--tk-gap-md) var(--tk-section-gap);
border-bottom: 1px solid $bd-l; border-bottom: 1px solid $bd-l;
.dialog-title { .dialog-title {
@@ -91,21 +91,21 @@
} }
.dialog-body { .dialog-body {
padding: 16px 20px; padding: var(--tk-gap-md) var(--tk-section-gap);
} }
.dialog-patient { .dialog-patient {
font-size: var(--tk-font-cap); font-size: var(--tk-font-cap);
color: $tx2; color: $tx2;
display: block; display: block;
margin-bottom: 12px; margin-bottom: var(--tk-gap-sm);
} }
.thread-item { .thread-item {
display: flex; display: flex;
align-items: flex-start; align-items: flex-start;
gap: 10px; gap: var(--tk-gap-sm);
padding: 6px 0; padding: var(--tk-gap-2xs) 0;
} }
.thread-dot { .thread-dot {
@@ -136,19 +136,19 @@
.dialog-actions { .dialog-actions {
display: flex; display: flex;
gap: 8px; gap: var(--tk-gap-xs);
padding: 12px 20px 20px; padding: var(--tk-gap-sm) var(--tk-section-gap) var(--tk-section-gap);
border-top: 1px solid $bd-l; border-top: 1px solid $bd-l;
.action-btn { .action-btn {
flex: 1; flex: 1;
text-align: center; text-align: center;
padding: 10px; padding: var(--tk-gap-sm);
border-radius: $r-sm; border-radius: $r-sm;
font-size: var(--tk-font-cap); font-size: var(--tk-font-cap);
font-weight: 500; font-weight: 500;
&.primary { background: $pri; color: $card; } &.primary { background: var(--tk-pri); color: $card; }
&.danger { background: $dan; color: $card; } &.danger { background: $dan; color: $card; }
&.default { background: $surface-alt; color: $tx2; } &.default { background: $surface-alt; color: $tx2; }
} }

View File

@@ -15,7 +15,7 @@ import EmptyState from '@/components/EmptyState';
import SegmentTabs from '@/components/SegmentTabs'; import SegmentTabs from '@/components/SegmentTabs';
import PageShell from '@/components/ui/PageShell'; import PageShell from '@/components/ui/PageShell';
import ContentCard from '@/components/ui/ContentCard'; import ContentCard from '@/components/ui/ContentCard';
import { useElderClass } from '../../../hooks/useElderClass'; import { useDoctorClass } from '@/hooks/useDoctorClass';
import './index.scss'; import './index.scss';
const TYPE_LABEL: Record<string, string> = { const TYPE_LABEL: Record<string, string> = {
@@ -40,7 +40,7 @@ const STATUS_TABS = [
]; ];
export default function ActionInboxPage() { export default function ActionInboxPage() {
const modeClass = useElderClass(); const modeClass = useDoctorClass();
const [items, setItems] = useState<ActionItem[]>([]); const [items, setItems] = useState<ActionItem[]>([]);
const [total, setTotal] = useState(0); const [total, setTotal] = useState(0);
const [_page, setPage] = useState(1); const [_page, setPage] = useState(1);

View File

@@ -12,7 +12,7 @@
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
padding: 24px 32px; padding: var(--tk-gap-lg) var(--tk-gap-xl);
background: $card; background: $card;
border-bottom: 1px solid $bd; border-bottom: 1px solid $bd;
@@ -26,19 +26,19 @@
&__close { &__close {
font-size: var(--tk-font-h1); font-size: var(--tk-font-h1);
color: $dan; color: $dan;
padding: 8px 16px; padding: var(--tk-gap-xs) var(--tk-gap-md);
} }
} }
.chat-messages { .chat-messages {
flex: 1; flex: 1;
padding: 24px; padding: var(--tk-gap-lg);
overflow-y: auto; overflow-y: auto;
} }
.msg-row { .msg-row {
display: flex; display: flex;
margin-bottom: 20px; margin-bottom: var(--tk-section-gap);
&--self { &--self {
justify-content: flex-end; justify-content: flex-end;
@@ -47,7 +47,7 @@
.msg-bubble { .msg-bubble {
max-width: 70%; max-width: 70%;
padding: 20px 24px; padding: var(--tk-section-gap) var(--tk-gap-lg);
border-radius: $r-lg; border-radius: $r-lg;
position: relative; position: relative;
@@ -57,7 +57,7 @@
} }
&--self { &--self {
background: $pri; background: var(--tk-pri);
border-top-right-radius: 4px; border-top-right-radius: 4px;
} }
} }
@@ -77,13 +77,13 @@
.msg-truncated-hint { .msg-truncated-hint {
display: flex; display: flex;
justify-content: center; justify-content: center;
padding: 12px 0; padding: var(--tk-gap-sm) 0;
&__text { &__text {
font-size: var(--tk-font-body); font-size: var(--tk-font-body);
color: $tx3; color: $tx3;
background: $bd-l; background: $bd-l;
padding: 4px 16px; padding: var(--tk-gap-2xs) var(--tk-gap-md);
border-radius: $r; border-radius: $r;
} }
} }
@@ -93,7 +93,7 @@
font-size: var(--tk-font-body); font-size: var(--tk-font-body);
color: $tx3; color: $tx3;
display: block; display: block;
margin-top: 8px; margin-top: var(--tk-gap-xs);
text-align: right; text-align: right;
.msg-bubble--self & { .msg-bubble--self & {
@@ -103,7 +103,7 @@
.chat-empty { .chat-empty {
text-align: center; text-align: center;
padding: 120px 32px; padding: var(--tk-gap-2xl) var(--tk-gap-xl);
&__text { &__text {
font-size: var(--tk-font-h1); font-size: var(--tk-font-h1);
@@ -114,7 +114,7 @@
.chat-input-bar { .chat-input-bar {
display: flex; display: flex;
align-items: center; align-items: center;
padding: 16px 24px; padding: var(--tk-gap-md) var(--tk-gap-lg);
background: $card; background: $card;
border-top: 1px solid $bd; border-top: 1px solid $bd;
padding-bottom: calc(16px + env(safe-area-inset-bottom)); padding-bottom: calc(16px + env(safe-area-inset-bottom));
@@ -124,15 +124,15 @@
flex: 1; flex: 1;
background: $bd-l; background: $bd-l;
border-radius: $r; border-radius: $r;
padding: 16px 20px; padding: var(--tk-gap-md) var(--tk-section-gap);
font-size: var(--tk-font-body-lg); font-size: var(--tk-font-body-lg);
margin-right: 16px; margin-right: var(--tk-gap-md);
} }
.chat-send-btn { .chat-send-btn {
background: $pri; background: var(--tk-pri);
border-radius: $r; border-radius: $r;
padding: 16px 28px; padding: var(--tk-gap-md) var(--tk-card-padding-lg);
flex-shrink: 0; flex-shrink: 0;
&--disabled { &--disabled {
@@ -147,7 +147,7 @@
} }
.chat-closed-bar { .chat-closed-bar {
padding: 24px; padding: var(--tk-gap-lg);
text-align: center; text-align: center;
background: $card; background: $card;
border-top: 1px solid $bd; border-top: 1px solid $bd;

View File

@@ -7,7 +7,7 @@ import {
type ConsultationSession, type ConsultationMessage, type ConsultationSession, type ConsultationMessage,
} from '@/services/doctor/consultation'; } from '@/services/doctor/consultation';
import Loading from '@/components/Loading'; import Loading from '@/components/Loading';
import { useElderClass } from '@/hooks/useElderClass'; import { useDoctorClass } from '@/hooks/useDoctorClass';
import { useLongPolling } from '@/hooks/useLongPolling'; import { useLongPolling } from '@/hooks/useLongPolling';
import './index.scss'; import './index.scss';
@@ -17,7 +17,7 @@ const MAX_STATE_MESSAGES = 300;
export default function ConsultationDetail() { export default function ConsultationDetail() {
const router = useRouter(); const router = useRouter();
const sessionId = router.params.id || ''; const sessionId = router.params.id || '';
const modeClass = useElderClass(); const modeClass = useDoctorClass();
const [session, setSession] = useState<ConsultationSession | null>(null); const [session, setSession] = useState<ConsultationSession | null>(null);
const [messages, setMessages] = useState<ConsultationMessage[]>([]); const [messages, setMessages] = useState<ConsultationMessage[]>([]);
const [inputText, setInputText] = useState(''); const [inputText, setInputText] = useState('');

View File

@@ -17,7 +17,7 @@
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
margin-bottom: 12px; margin-bottom: var(--tk-gap-sm);
} }
.session-card__subject { .session-card__subject {
@@ -28,18 +28,18 @@
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
margin-right: 16px; margin-right: var(--tk-gap-md);
} }
.session-card__info { .session-card__info {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 16px; gap: var(--tk-gap-md);
margin-bottom: 8px; margin-bottom: var(--tk-gap-xs);
} }
.session-card__type { .session-card__type {
@include tag($pri-l, $pri); @include tag(var(--tk-pri-l), var(--tk-pri));
} }
.session-card__time { .session-card__time {
@@ -58,8 +58,8 @@
.session-card__badge { .session-card__badge {
position: absolute; position: absolute;
top: 20px; top: var(--tk-section-gap);
right: 20px; right: var(--tk-section-gap);
min-width: 36px; min-width: 36px;
height: 36px; height: 36px;
background: $dan; background: $dan;

View File

@@ -11,7 +11,7 @@ import PaginationBar from '@/components/patterns/PaginationBar';
import SearchSection from '@/components/patterns/SearchSection'; import SearchSection from '@/components/patterns/SearchSection';
import ErrorState from '@/components/ErrorState'; import ErrorState from '@/components/ErrorState';
import EmptyState from '@/components/EmptyState'; import EmptyState from '@/components/EmptyState';
import { useElderClass } from '../../../hooks/useElderClass'; import { useDoctorClass } from '@/hooks/useDoctorClass';
import { formatDateTime } from '@/utils/date'; import { formatDateTime } from '@/utils/date';
import { safeNavigateTo } from '@/utils/navigate'; import { safeNavigateTo } from '@/utils/navigate';
import './index.scss'; import './index.scss';
@@ -30,7 +30,7 @@ const STATUS_COLOR_MAP: Record<string, 'success' | 'warning' | 'default' | 'info
}; };
export default function ConsultationList() { export default function ConsultationList() {
const modeClass = useElderClass(); const modeClass = useDoctorClass();
const [sessions, setSessions] = useState<ConsultationSession[]>([]); const [sessions, setSessions] = useState<ConsultationSession[]>([]);
const [activeTab, setActiveTab] = useState(''); const [activeTab, setActiveTab] = useState('');
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);

View File

@@ -9,7 +9,7 @@
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
margin-bottom: 20px; margin-bottom: var(--tk-section-gap);
&__title { &__title {
font-family: 'Georgia', 'Times New Roman', serif; font-family: 'Georgia', 'Times New Roman', serif;
@@ -20,12 +20,12 @@
&__status { &__status {
font-size: var(--tk-font-h2); font-size: var(--tk-font-h2);
padding: 6px 16px; padding: 6px var(--tk-gap-md);
border-radius: $r; border-radius: $r;
font-weight: 500; font-weight: 500;
&--pending { background: $wrn-l; color: $wrn; } &--pending { background: $wrn-l; color: $wrn; }
&--in_progress { background: $pri-l; color: $pri; } &--in_progress { background: var(--tk-pri-l); color: var(--tk-pri); }
&--completed { background: $acc-l; color: $acc; } &--completed { background: $acc-l; color: $acc; }
&--overdue { background: $dan-l; color: $dan; } &--overdue { background: $dan-l; color: $dan; }
&--cancelled { background: $bd-l; color: $tx3; } &--cancelled { background: $bd-l; color: $tx3; }
@@ -35,13 +35,13 @@
.info-grid { .info-grid {
display: grid; display: grid;
grid-template-columns: 1fr 1fr 1fr; grid-template-columns: 1fr 1fr 1fr;
gap: 16px; gap: var(--tk-gap-md);
} }
.info-item { .info-item {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 4px; gap: var(--tk-gap-2xs);
} }
.info-label { .info-label {
@@ -56,8 +56,8 @@
} }
.task-template { .task-template {
margin-top: 16px; margin-top: var(--tk-gap-md);
padding: 16px; padding: var(--tk-gap-md);
background: $bd-l; background: $bd-l;
border-radius: $r; border-radius: $r;
@@ -65,7 +65,7 @@
font-size: var(--tk-font-body); font-size: var(--tk-font-body);
color: $tx2; color: $tx2;
display: block; display: block;
margin-bottom: 8px; margin-bottom: var(--tk-gap-xs);
} }
&__text { &__text {
@@ -76,7 +76,7 @@
} }
.record-item { .record-item {
padding: 20px 0; padding: var(--tk-section-gap) 0;
border-bottom: 1px solid $bd-l; border-bottom: 1px solid $bd-l;
&:last-child { &:last-child {
@@ -87,31 +87,31 @@
font-size: var(--tk-font-body); font-size: var(--tk-font-body);
color: $tx3; color: $tx3;
display: block; display: block;
margin-bottom: 8px; margin-bottom: var(--tk-gap-xs);
} }
&__text { &__text {
font-size: var(--tk-font-h1); font-size: var(--tk-font-h1);
color: $tx; color: $tx;
display: block; display: block;
margin-bottom: 4px; margin-bottom: var(--tk-gap-2xs);
line-height: 1.5; line-height: 1.5;
} }
} }
.start-btn { .start-btn {
text-align: center; text-align: center;
padding: 16px; padding: var(--tk-gap-md);
background: $pri; background: var(--tk-pri);
border-radius: $r; border-radius: $r;
margin-bottom: 24px; margin-bottom: var(--tk-gap-lg);
color: $card; color: $card;
font-size: var(--tk-font-body-lg); font-size: var(--tk-font-body-lg);
font-weight: 500; font-weight: 500;
} }
.form-group { .form-group {
margin-bottom: 24px; margin-bottom: var(--tk-gap-lg);
} }
.form-label { .form-label {
@@ -119,7 +119,7 @@
color: $tx2; color: $tx2;
font-weight: 500; font-weight: 500;
display: block; display: block;
margin-bottom: 12px; margin-bottom: var(--tk-gap-sm);
} }
.form-textarea { .form-textarea {
@@ -127,7 +127,7 @@
min-height: 160px; min-height: 160px;
background: $bd-l; background: $bd-l;
border-radius: $r; border-radius: $r;
padding: 16px 20px; padding: var(--tk-gap-md) var(--tk-section-gap);
font-size: var(--tk-font-h1); font-size: var(--tk-font-h1);
color: $tx; color: $tx;
box-sizing: border-box; box-sizing: border-box;
@@ -136,7 +136,7 @@
.form-date { .form-date {
width: 100%; width: 100%;
padding: 16px 20px; padding: var(--tk-gap-md) var(--tk-section-gap);
background: $bd-l; background: $bd-l;
border-radius: $r; border-radius: $r;
font-size: var(--tk-font-h1); font-size: var(--tk-font-h1);
@@ -145,11 +145,11 @@
} }
.submit-btn { .submit-btn {
background: $pri; background: var(--tk-pri);
border-radius: $r; border-radius: $r;
padding: 20px; padding: var(--tk-section-gap);
text-align: center; text-align: center;
margin-top: 16px; margin-top: var(--tk-gap-md);
&--disabled { &--disabled {
opacity: 0.5; opacity: 0.5;
@@ -164,7 +164,7 @@
.error-text { .error-text {
text-align: center; text-align: center;
padding: 80px 32px; padding: 80px var(--tk-gap-xl);
color: $tx3; color: $tx3;
font-size: var(--tk-font-body-lg); font-size: var(--tk-font-body-lg);
} }

View File

@@ -10,7 +10,7 @@ import {
import Loading from '@/components/Loading'; import Loading from '@/components/Loading';
import PageShell from '@/components/ui/PageShell'; import PageShell from '@/components/ui/PageShell';
import ContentCard from '@/components/ui/ContentCard'; import ContentCard from '@/components/ui/ContentCard';
import { useElderClass } from '../../../../hooks/useElderClass'; import { useDoctorClass } from '@/hooks/useDoctorClass';
import './index.scss'; import './index.scss';
const STATUS_LABELS: Record<string, string> = { const STATUS_LABELS: Record<string, string> = {
@@ -24,7 +24,7 @@ const STATUS_LABELS: Record<string, string> = {
export default function FollowUpDetail() { export default function FollowUpDetail() {
const router = useRouter(); const router = useRouter();
const taskId = router.params.id || ''; const taskId = router.params.id || '';
const modeClass = useElderClass(); const modeClass = useDoctorClass();
const [task, setTask] = useState<FollowUpTask | null>(null); const [task, setTask] = useState<FollowUpTask | null>(null);
const [records, setRecords] = useState<FollowUpRecord[]>([]); const [records, setRecords] = useState<FollowUpRecord[]>([]);
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);

View File

@@ -6,7 +6,7 @@
// StatusTag 已接管:任务状态标签 // StatusTag 已接管:任务状态标签
.task-count { .task-count {
margin-bottom: 16px; margin-bottom: var(--tk-gap-md);
text { text {
font-size: var(--tk-font-h2); font-size: var(--tk-font-h2);
@@ -24,7 +24,7 @@
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
margin-bottom: 12px; margin-bottom: var(--tk-gap-sm);
} }
.task-card__type { .task-card__type {
@@ -37,7 +37,7 @@
font-size: var(--tk-font-h1); font-size: var(--tk-font-h1);
color: $tx2; color: $tx2;
display: block; display: block;
margin-bottom: 8px; margin-bottom: var(--tk-gap-xs);
} }
.task-card__footer { .task-card__footer {

View File

@@ -10,7 +10,7 @@ import LoadingCard from '@/components/ui/LoadingCard';
import SearchSection from '@/components/patterns/SearchSection'; import SearchSection from '@/components/patterns/SearchSection';
import ErrorState from '@/components/ErrorState'; import ErrorState from '@/components/ErrorState';
import EmptyState from '@/components/EmptyState'; import EmptyState from '@/components/EmptyState';
import { useElderClass } from '../../../hooks/useElderClass'; import { useDoctorClass } from '@/hooks/useDoctorClass';
import './index.scss'; import './index.scss';
const TABS = [ const TABS = [
@@ -31,7 +31,7 @@ const STATUS_COLOR_MAP: Record<string, 'warning' | 'info' | 'success' | 'error'>
export default function FollowUpList() { export default function FollowUpList() {
const router = useRouter(); const router = useRouter();
const patientId = router.params.patientId || ''; const patientId = router.params.patientId || '';
const modeClass = useElderClass(); const modeClass = useDoctorClass();
const [tasks, setTasks] = useState<FollowUpTask[]>([]); const [tasks, setTasks] = useState<FollowUpTask[]>([]);
const [activeTab, setActiveTab] = useState(''); const [activeTab, setActiveTab] = useState('');
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);

View File

@@ -6,19 +6,19 @@
.doctor-home { .doctor-home {
&__header { &__header {
margin-bottom: 40px; margin-bottom: var(--tk-gap-2xl);
} }
&__title { &__title {
@include section-title; @include section-title;
margin-bottom: 12px; margin-bottom: var(--tk-gap-sm);
} }
&__greeting { &__greeting {
font-size: var(--tk-font-h2); font-size: var(--tk-font-h2);
color: $tx2; color: $tx2;
display: block; display: block;
margin-bottom: 8px; margin-bottom: var(--tk-gap-xs);
} }
&__date { &__date {
@@ -29,8 +29,8 @@
&__alert { &__alert {
display: flex; display: flex;
align-items: center; align-items: center;
margin: 16px 24px; margin: var(--tk-gap-md) var(--tk-page-padding);
padding: 16px 20px; padding: var(--tk-gap-md) var(--tk-section-gap);
background: $dan-l; background: $dan-l;
border-radius: $r; border-radius: $r;
border-left: 4px solid $dan; border-left: 4px solid $dan;
@@ -46,7 +46,7 @@
line-height: 36px; line-height: 36px;
font-weight: bold; font-weight: bold;
font-size: var(--tk-font-body); font-size: var(--tk-font-body);
margin-right: 12px; margin-right: var(--tk-gap-sm);
flex-shrink: 0; flex-shrink: 0;
} }
@@ -63,19 +63,19 @@
} }
&__search { &__search {
margin: 0 24px 16px; margin: 0 var(--tk-page-padding) var(--tk-gap-md);
} }
&__search-input { &__search-input {
background: $surface-alt; background: $surface-alt;
border-radius: $r; border-radius: $r;
padding: 16px 20px; padding: var(--tk-gap-md) var(--tk-section-gap);
font-size: var(--tk-font-h1); font-size: var(--tk-font-h1);
color: $tx3; color: $tx3;
} }
&__section { &__section {
margin-bottom: 40px; margin-bottom: var(--tk-gap-2xl);
} }
&__section-title { &__section-title {
@@ -85,19 +85,19 @@
&__grid { &__grid {
display: grid; display: grid;
grid-template-columns: 1fr 1fr; grid-template-columns: 1fr 1fr;
gap: 20px; gap: var(--tk-section-gap);
} }
&__card { &__card {
background: $card; background: $card;
border-radius: $r-lg; border-radius: $r-lg;
padding: 28px 24px; padding: var(--tk-card-padding-lg) var(--tk-card-padding);
text-align: center; text-align: center;
box-shadow: $shadow-md; box-shadow: $shadow-md;
transition: transform 0.15s; transition: transform 0.15s;
&:active { &:active {
transform: scale(0.97); opacity: var(--tk-touch-feedback-opacity);
} }
} }
@@ -107,12 +107,12 @@
width: 56px; width: 56px;
height: 56px; height: 56px;
border-radius: $r; border-radius: $r;
background: $pri-l; background: var(--tk-pri-l);
color: $pri; color: var(--tk-pri);
font-family: 'Georgia', 'Times New Roman', serif; font-family: 'Georgia', 'Times New Roman', serif;
font-size: var(--tk-font-body-lg); font-size: var(--tk-font-body-lg);
font-weight: 700; font-weight: 700;
margin-bottom: 8px; margin-bottom: var(--tk-gap-xs);
} }
&__card-num { &__card-num {
@@ -121,7 +121,7 @@
font-weight: 700; font-weight: 700;
color: $tx; color: $tx;
display: block; display: block;
margin-bottom: 8px; margin-bottom: var(--tk-gap-xs);
} }
&__card-label { &__card-label {
@@ -132,7 +132,7 @@
&__quick-actions { &__quick-actions {
display: grid; display: grid;
grid-template-columns: repeat(4, 1fr); grid-template-columns: repeat(4, 1fr);
gap: 20px; gap: var(--tk-section-gap);
} }
&__footer { &__footer {
@@ -144,7 +144,7 @@
&__logout { &__logout {
color: $dan; color: $dan;
font-size: var(--tk-font-h2); font-size: var(--tk-font-h2);
padding: 16px 48px; padding: var(--tk-gap-md) var(--tk-gap-2xl);
display: inline-block; display: inline-block;
} }
} }
@@ -153,12 +153,12 @@
flex: 1; flex: 1;
background: $card; background: $card;
border-radius: $r-lg; border-radius: $r-lg;
padding: 28px 20px; padding: var(--tk-card-padding-lg) var(--tk-section-gap);
text-align: center; text-align: center;
box-shadow: $shadow-md; box-shadow: $shadow-md;
&:active { &:active {
opacity: 0.8; opacity: var(--tk-touch-feedback-opacity);
} }
&__initial { &__initial {
@@ -177,7 +177,7 @@
&__icon-wrap { &__icon-wrap {
position: relative; position: relative;
display: inline-flex; display: inline-flex;
margin-bottom: 8px; margin-bottom: var(--tk-gap-xs);
} }
&__badge { &__badge {

View File

@@ -2,7 +2,7 @@ import { useState, useMemo, useCallback } from 'react';
import { View, Text, Input } from '@tarojs/components'; import { View, Text, Input } from '@tarojs/components';
import Taro from '@tarojs/taro'; import Taro from '@tarojs/taro';
import { useAuthStore } from '@/stores/auth'; import { useAuthStore } from '@/stores/auth';
import { useElderClass } from '../../hooks/useElderClass'; import { useDoctorClass } from '@/hooks/useDoctorClass';
import { usePageData } from '@/hooks/usePageData'; import { usePageData } from '@/hooks/usePageData';
import { getDashboard, type DoctorDashboard } from '@/services/doctor/dashboard'; import { getDashboard, type DoctorDashboard } from '@/services/doctor/dashboard';
import Loading from '@/components/Loading'; import Loading from '@/components/Loading';
@@ -58,7 +58,7 @@ export default function DoctorHome() {
const user = useAuthStore((s) => s.user); const user = useAuthStore((s) => s.user);
const logout = useAuthStore((s) => s.logout); const logout = useAuthStore((s) => s.logout);
const roles = useAuthStore((s) => s.roles); const roles = useAuthStore((s) => s.roles);
const modeClass = useElderClass(); const modeClass = useDoctorClass();
const [dashboard, setDashboard] = useState<DoctorDashboard | null>(null); const [dashboard, setDashboard] = useState<DoctorDashboard | null>(null);
const [alertCount, setAlertCount] = useState(0); const [alertCount, setAlertCount] = useState(0);
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);

View File

@@ -8,13 +8,13 @@
.info-grid { .info-grid {
display: grid; display: grid;
grid-template-columns: 1fr 1fr; grid-template-columns: 1fr 1fr;
gap: 16px; gap: var(--tk-gap-md);
} }
.info-item { .info-item {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 4px; gap: var(--tk-gap-2xs);
} }
.info-label { .info-label {
@@ -31,8 +31,8 @@
.warning-card { .warning-card {
background: $wrn-l; background: $wrn-l;
border-radius: $r; border-radius: $r;
padding: 20px; padding: var(--tk-section-gap);
margin-bottom: 16px; margin-bottom: var(--tk-gap-md);
} }
.warning-label { .warning-label {
@@ -40,23 +40,23 @@
color: $wrn; color: $wrn;
font-weight: 600; font-weight: 600;
display: block; display: block;
margin-bottom: 8px; margin-bottom: var(--tk-gap-xs);
} }
.warning-text { .warning-text {
font-size: var(--tk-font-h1); font-size: var(--tk-font-h1);
color: $pri-d; color: var(--tk-pri-d);
} }
.info-block { .info-block {
margin-bottom: 12px; margin-bottom: var(--tk-gap-sm);
} }
.info-block-label { .info-block-label {
font-size: var(--tk-font-body); font-size: var(--tk-font-body);
color: $tx3; color: $tx3;
display: block; display: block;
margin-bottom: 8px; margin-bottom: var(--tk-gap-xs);
} }
.info-block-text { .info-block-text {
@@ -68,14 +68,14 @@
.vitals-grid { .vitals-grid {
display: grid; display: grid;
grid-template-columns: 1fr 1fr; grid-template-columns: 1fr 1fr;
gap: 16px; gap: var(--tk-gap-md);
margin-bottom: 16px; margin-bottom: var(--tk-gap-md);
} }
.vital-item { .vital-item {
background: $pri-l; background: var(--tk-pri-l);
border-radius: $r; border-radius: $r;
padding: 20px; padding: var(--tk-section-gap);
text-align: center; text-align: center;
} }
@@ -83,7 +83,7 @@
@include serif-number; @include serif-number;
font-size: var(--tk-font-num-lg); font-size: var(--tk-font-num-lg);
font-weight: 700; font-weight: 700;
color: $pri; color: var(--tk-pri);
display: block; display: block;
margin-bottom: 4px; margin-bottom: 4px;
} }
@@ -97,7 +97,7 @@
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
padding: 16px 0; padding: var(--tk-gap-md) 0;
} }
.stat-label { .stat-label {
@@ -117,7 +117,7 @@
} }
.lab-item { .lab-item {
padding: 20px 0; padding: var(--tk-section-gap) 0;
border-bottom: 1px solid $bd-l; border-bottom: 1px solid $bd-l;
&:last-child { &:last-child {
@@ -125,14 +125,14 @@
} }
&:active { &:active {
background: $bd-l; opacity: var(--tk-touch-feedback-opacity);
} }
&__header { &__header {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
margin-bottom: 8px; margin-bottom: var(--tk-gap-xs);
} }
&__type { &__type {
@@ -155,21 +155,21 @@
.action-buttons { .action-buttons {
display: flex; display: flex;
gap: 16px; gap: var(--tk-gap-md);
} }
.action-btn { .action-btn {
flex: 1; flex: 1;
text-align: center; text-align: center;
padding: 20px; padding: var(--tk-section-gap);
border-radius: $r; border-radius: $r;
background: $pri; background: var(--tk-pri);
color: $card; color: $card;
font-size: var(--tk-font-h1); font-size: var(--tk-font-h1);
font-weight: 500; font-weight: 500;
&:active { &:active {
opacity: 0.85; opacity: var(--tk-touch-feedback-opacity);
} }
text { text {
@@ -179,7 +179,7 @@
.error-text { .error-text {
text-align: center; text-align: center;
padding: 80px 32px; padding: 80px var(--tk-gap-xl);
color: $tx3; color: $tx3;
font-size: var(--tk-font-body-lg); font-size: var(--tk-font-body-lg);
} }

View File

@@ -6,13 +6,13 @@ import { getPatient, getHealthSummary, type PatientDetail, type HealthSummary }
import Loading from '@/components/Loading'; import Loading from '@/components/Loading';
import PageShell from '@/components/ui/PageShell'; import PageShell from '@/components/ui/PageShell';
import ContentCard from '@/components/ui/ContentCard'; import ContentCard from '@/components/ui/ContentCard';
import { useElderClass } from '../../../../hooks/useElderClass'; import { useDoctorClass } from '@/hooks/useDoctorClass';
import './index.scss'; import './index.scss';
export default function PatientDetail() { export default function PatientDetail() {
const router = useRouter(); const router = useRouter();
const patientId = router.params.id || ''; const patientId = router.params.id || '';
const modeClass = useElderClass(); const modeClass = useDoctorClass();
const [patient, setPatient] = useState<PatientDetail | null>(null); const [patient, setPatient] = useState<PatientDetail | null>(null);
const [summary, setSummary] = useState<HealthSummary | null>(null); const [summary, setSummary] = useState<HealthSummary | null>(null);
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);

View File

@@ -6,7 +6,7 @@
// StatusTag 已接管patient-card__status 标签样式 // StatusTag 已接管patient-card__status 标签样式
.patient-count { .patient-count {
margin-bottom: 16px; margin-bottom: var(--tk-gap-md);
text { text {
font-size: var(--tk-font-h2); font-size: var(--tk-font-h2);
@@ -23,7 +23,7 @@
.patient-card__header { .patient-card__header {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 16px; gap: var(--tk-gap-md);
} }
.patient-card__name { .patient-card__name {
@@ -41,12 +41,12 @@
.patient-card__tags { .patient-card__tags {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
gap: 8px; gap: var(--tk-gap-xs);
margin-top: 12px; margin-top: var(--tk-gap-sm);
} }
.patient-tag { .patient-tag {
padding: 4px 14px; padding: var(--tk-gap-2xs) 14px;
border-radius: $r; border-radius: $r;
background: rgba($pri, 0.1); background: rgba($pri, 0.1);
@@ -57,7 +57,7 @@
.load-more-hint-wrap { .load-more-hint-wrap {
text-align: center; text-align: center;
padding: 20px; padding: var(--tk-section-gap);
} }
.load-more-hint { .load-more-hint {

View File

@@ -10,11 +10,11 @@ import LoadingCard from '@/components/ui/LoadingCard';
import SearchSection from '@/components/patterns/SearchSection'; import SearchSection from '@/components/patterns/SearchSection';
import EmptyState from '@/components/EmptyState'; import EmptyState from '@/components/EmptyState';
import Loading from '@/components/Loading'; import Loading from '@/components/Loading';
import { useElderClass } from '../../../hooks/useElderClass'; import { useDoctorClass } from '@/hooks/useDoctorClass';
import './index.scss'; import './index.scss';
export default function PatientList() { export default function PatientList() {
const modeClass = useElderClass(); const modeClass = useDoctorClass();
const [patients, setPatients] = useState<PatientItem[]>([]); const [patients, setPatients] = useState<PatientItem[]>([]);
const [tags, setTags] = useState<PatientTag[]>([]); const [tags, setTags] = useState<PatientTag[]>([]);
const [activeTag, setActiveTag] = useState<string>(''); const [activeTag, setActiveTag] = useState<string>('');

View File

@@ -19,7 +19,7 @@
} }
.alerts-tab.active { .alerts-tab.active {
background: $pri; background: var(--tk-pri);
} }
.alerts-tab-text { .alerts-tab-text {

View File

@@ -6,7 +6,7 @@
/* ── hero ── */ /* ── hero ── */
.dm-hero { .dm-hero {
padding: 48px 32px 36px; padding: var(--tk-gap-2xl) var(--tk-gap-xl) 36px;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
@@ -17,21 +17,21 @@
width: 88px; width: 88px;
height: 88px; height: 88px;
border-radius: $r-lg; border-radius: $r-lg;
background: $pri-l; background: var(--tk-pri-l);
margin-bottom: 20px; margin-bottom: var(--tk-section-gap);
} }
.dm-hero-icon-text { .dm-hero-icon-text {
font-family: 'Georgia', 'Times New Roman', serif; font-family: 'Georgia', 'Times New Roman', serif;
font-size: var(--tk-font-hero); font-size: var(--tk-font-hero);
font-weight: bold; font-weight: bold;
color: $pri; color: var(--tk-pri);
} }
.dm-hero-title { .dm-hero-title {
@include section-title; @include section-title;
font-size: var(--tk-font-num-lg); font-size: var(--tk-font-num-lg);
margin-bottom: 8px; margin-bottom: var(--tk-gap-xs);
} }
.dm-hero-sub { .dm-hero-sub {
@@ -41,14 +41,14 @@
/* ── card (standalone, used for date picker) ── */ /* ── card (standalone, used for date picker) ── */
.dm-card { .dm-card {
margin: 0 24px 20px; margin: 0 var(--tk-gap-lg) var(--tk-section-gap);
} }
.dm-card-header { .dm-card-header {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 14px; gap: 14px;
margin-bottom: 20px; margin-bottom: var(--tk-section-gap);
} }
.dm-card-title { .dm-card-title {
@@ -71,12 +71,12 @@
align-items: center; align-items: center;
background: $bg; background: $bg;
border-radius: $r-sm; border-radius: $r-sm;
padding: 22px 24px; padding: 22px var(--tk-gap-lg);
} }
.dm-date-value { .dm-date-value {
font-size: var(--tk-font-body-lg); font-size: var(--tk-font-body-lg);
color: $pri; color: var(--tk-pri);
@include serif-number; @include serif-number;
font-weight: bold; font-weight: bold;
} }
@@ -91,7 +91,7 @@
/* ── collapsible group ── */ /* ── collapsible group ── */
.dm-group { .dm-group {
margin: 0 24px 20px; margin: 0 var(--tk-gap-lg) var(--tk-section-gap);
overflow: hidden; overflow: hidden;
} }
@@ -99,10 +99,10 @@
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
padding: 24px 28px; padding: var(--tk-gap-lg) var(--tk-card-padding-lg);
&:active { &:active {
background: $bd-l; opacity: var(--tk-touch-feedback-opacity);
} }
} }
@@ -125,7 +125,7 @@
} }
.dm-group-body { .dm-group-body {
padding: 0 28px 28px; padding: 0 var(--tk-card-padding-lg) var(--tk-card-padding-lg);
} }
.dm-group-collapsed .dm-group-body { .dm-group-collapsed .dm-group-body {
@@ -134,7 +134,7 @@
/* ── inner field spacing (within groups) ── */ /* ── inner field spacing (within groups) ── */
.dm-inner-field { .dm-inner-field {
margin-bottom: 24px; margin-bottom: var(--tk-gap-lg);
&:last-child { &:last-child {
margin-bottom: 0; margin-bottom: 0;
@@ -145,7 +145,7 @@
.dm-bp-group { .dm-bp-group {
display: flex; display: flex;
align-items: flex-end; align-items: flex-end;
gap: 12px; gap: var(--tk-gap-sm);
} }
.dm-bp-field { .dm-bp-field {
@@ -156,14 +156,14 @@
font-size: var(--tk-font-body); font-size: var(--tk-font-body);
color: $tx2; color: $tx2;
display: block; display: block;
margin-bottom: 8px; margin-bottom: var(--tk-gap-xs);
} }
.dm-bp-divider { .dm-bp-divider {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
padding-bottom: 20px; padding-bottom: var(--tk-section-gap);
gap: 6px; gap: 6px;
} }
@@ -192,7 +192,7 @@
.dm-single-row { .dm-single-row {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 16px; gap: var(--tk-gap-md);
} }
.dm-input-flex { .dm-input-flex {
@@ -210,7 +210,7 @@
.dm-input-box { .dm-input-box {
background: $bg; background: $bg;
border-radius: $r-sm; border-radius: $r-sm;
padding: 20px 24px; padding: var(--tk-section-gap) var(--tk-gap-lg);
font-size: var(--tk-font-body-lg); font-size: var(--tk-font-body-lg);
color: $tx; color: $tx;
@include serif-number; @include serif-number;
@@ -230,7 +230,7 @@
.dm-field-warning { .dm-field-warning {
font-size: var(--tk-font-body); font-size: var(--tk-font-body);
color: $wrn; color: $wrn;
margin-top: 8px; margin-top: var(--tk-gap-xs);
display: block; display: block;
} }
@@ -240,16 +240,16 @@
/* ── submit ── */ /* ── submit ── */
.dm-submit { .dm-submit {
background: $pri; background: var(--tk-pri);
border-radius: $r; border-radius: $r;
padding: 26px; padding: 26px;
text-align: center; text-align: center;
margin: 40px 24px 0; margin: 40px var(--tk-gap-lg) 0;
box-shadow: $shadow-md; box-shadow: $shadow-md;
transition: opacity 0.2s; transition: opacity 0.2s;
&:active { &:active {
opacity: 0.85; opacity: var(--tk-touch-feedback-opacity);
} }
} }
@@ -268,8 +268,8 @@
/* ── reset ── */ /* ── reset ── */
.dm-reset { .dm-reset {
text-align: center; text-align: center;
padding: 24px; padding: var(--tk-gap-lg);
margin-top: 12px; margin-top: var(--tk-gap-sm);
} }
.dm-reset-text { .dm-reset-text {

View File

@@ -5,8 +5,8 @@
// ContentCard 已接管sync-status-card/sync-result-card 背景/圆角/阴影 // ContentCard 已接管sync-status-card/sync-result-card 背景/圆角/阴影
.sync-header { .sync-header {
background: $pri; background: var(--tk-pri);
padding: 48px 32px 32px; padding: var(--tk-gap-2xl) var(--tk-gap-xl) var(--tk-gap-xl);
color: $card; color: $card;
} }
@@ -16,17 +16,17 @@
} }
.sync-section { .sync-section {
padding: 24px; padding: var(--tk-gap-lg);
} }
.sync-hero { .sync-hero {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
padding: 48px 24px; padding: var(--tk-gap-2xl) var(--tk-gap-lg);
background: $card; background: $card;
border-radius: $r; border-radius: $r;
margin-bottom: 24px; margin-bottom: var(--tk-gap-lg);
box-shadow: $shadow-sm; box-shadow: $shadow-sm;
} }
@@ -34,10 +34,10 @@
width: 80px; width: 80px;
height: 80px; height: 80px;
border-radius: 50%; border-radius: 50%;
background: $pri-l; background: var(--tk-pri-l);
@include flex-center; @include flex-center;
margin-bottom: 20px; margin-bottom: var(--tk-section-gap);
color: $pri; color: var(--tk-pri);
font-family: 'Georgia', 'Times New Roman', serif; font-family: 'Georgia', 'Times New Roman', serif;
font-size: var(--tk-font-num-lg); font-size: var(--tk-font-num-lg);
font-weight: bold; font-weight: bold;
@@ -45,7 +45,7 @@
.sync-hero-title { .sync-hero-title {
@include section-title; @include section-title;
margin-bottom: 8px; margin-bottom: var(--tk-gap-xs);
} }
.sync-hero-desc { .sync-hero-desc {
@@ -55,20 +55,20 @@
.sync-action { .sync-action {
@include flex-center; @include flex-center;
background: $pri; background: var(--tk-pri);
border-radius: $r-sm; border-radius: $r-sm;
padding: 20px 40px; padding: var(--tk-section-gap) var(--tk-gap-2xl);
margin: 12px 0; margin: var(--tk-gap-sm) 0;
&--primary { &--primary {
flex: 1; flex: 1;
background: $pri; background: var(--tk-pri);
} }
&--danger { &--danger {
flex: 1; flex: 1;
background: $dan; background: $dan;
margin-left: 16px; margin-left: var(--tk-gap-md);
} }
} }
@@ -79,7 +79,7 @@
} }
.sync-device-list { .sync-device-list {
margin-top: 16px; margin-top: var(--tk-gap-md);
} }
.sync-section-title { .sync-section-title {
@@ -87,7 +87,7 @@
font-size: var(--tk-font-body-lg); font-size: var(--tk-font-body-lg);
font-weight: bold; font-weight: bold;
color: $tx; color: $tx;
margin-bottom: 12px; margin-bottom: var(--tk-gap-sm);
display: block; display: block;
} }
@@ -97,8 +97,8 @@
align-items: center; align-items: center;
background: $card; background: $card;
border-radius: $r-sm; border-radius: $r-sm;
padding: 24px; padding: var(--tk-gap-lg);
margin-bottom: 12px; margin-bottom: var(--tk-gap-sm);
box-shadow: $shadow-sm; box-shadow: $shadow-sm;
} }
@@ -127,14 +127,14 @@
.sync-status-card { .sync-status-card {
display: flex; display: flex;
align-items: center; align-items: center;
margin-bottom: 16px; margin-bottom: var(--tk-gap-md);
} }
.sync-status-dot { .sync-status-dot {
width: 16px; width: 16px;
height: 16px; height: 16px;
border-radius: 50%; border-radius: 50%;
margin-right: 16px; margin-right: var(--tk-gap-md);
background: $tx3; background: $tx3;
&--connected { &--connected {
@@ -150,8 +150,8 @@
.sync-readings-panel { .sync-readings-panel {
background: $card; background: $card;
border-radius: $r-sm; border-radius: $r-sm;
padding: 24px; padding: var(--tk-gap-lg);
margin-bottom: 16px; margin-bottom: var(--tk-gap-md);
box-shadow: $shadow-sm; box-shadow: $shadow-sm;
} }
@@ -159,7 +159,7 @@
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
padding: 12px 0; padding: var(--tk-gap-sm) 0;
border-bottom: 1px solid $bd-l; border-bottom: 1px solid $bd-l;
&:last-child { &:last-child {
@@ -175,13 +175,13 @@
.sync-reading-value { .sync-reading-value {
font-size: var(--tk-font-body-lg); font-size: var(--tk-font-body-lg);
font-weight: bold; font-weight: bold;
color: $pri; color: var(--tk-pri);
@include serif-number; @include serif-number;
} }
.sync-readings-count { .sync-readings-count {
display: block; display: block;
margin-top: 12px; margin-top: var(--tk-gap-sm);
font-size: var(--tk-font-body); font-size: var(--tk-font-body);
color: var(--tk-text-secondary); color: var(--tk-text-secondary);
text-align: center; text-align: center;
@@ -189,12 +189,12 @@
.sync-actions-row { .sync-actions-row {
display: flex; display: flex;
gap: 12px; gap: var(--tk-gap-sm);
} }
.sync-error { .sync-error {
margin: 24px; margin: var(--tk-gap-lg);
padding: 20px 24px; padding: var(--tk-section-gap) var(--tk-gap-lg);
background: $dan-l; background: $dan-l;
border-radius: $r-sm; border-radius: $r-sm;
} }
@@ -218,7 +218,7 @@
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
margin-bottom: 24px; margin-bottom: var(--tk-gap-lg);
box-shadow: $shadow-sm; box-shadow: $shadow-sm;
} }
@@ -232,12 +232,12 @@
font-family: 'Georgia', 'Times New Roman', serif; font-family: 'Georgia', 'Times New Roman', serif;
font-size: var(--tk-font-num-lg); font-size: var(--tk-font-num-lg);
font-weight: bold; font-weight: bold;
margin-bottom: 16px; margin-bottom: var(--tk-gap-md);
} }
.sync-result-title { .sync-result-title {
@include section-title; @include section-title;
margin-bottom: 8px; margin-bottom: var(--tk-gap-xs);
} }
.sync-result-count { .sync-result-count {

View File

@@ -6,7 +6,7 @@
/* ── hero ── */ /* ── hero ── */
.input-hero { .input-hero {
padding: 48px 32px 36px; padding: var(--tk-gap-2xl) var(--tk-gap-xl) 36px;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
@@ -17,21 +17,21 @@
width: 88px; width: 88px;
height: 88px; height: 88px;
border-radius: $r-lg; border-radius: $r-lg;
background: $pri-l; background: var(--tk-pri-l);
margin-bottom: 20px; margin-bottom: var(--tk-section-gap);
} }
.input-hero-icon-text { .input-hero-icon-text {
font-family: 'Georgia', 'Times New Roman', serif; font-family: 'Georgia', 'Times New Roman', serif;
font-size: var(--tk-font-hero); font-size: var(--tk-font-hero);
font-weight: bold; font-weight: bold;
color: $pri; color: var(--tk-pri);
} }
.input-hero-title { .input-hero-title {
@include section-title; @include section-title;
font-size: var(--tk-font-num-lg); font-size: var(--tk-font-num-lg);
margin-bottom: 8px; margin-bottom: var(--tk-gap-xs);
} }
.input-hero-sub { .input-hero-sub {
@@ -44,18 +44,18 @@
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
margin: 0 24px 20px; margin: 0 var(--tk-gap-lg) var(--tk-section-gap);
border: 1px dashed $pri; border: 1px dashed var(--tk-pri);
&:active { &:active {
opacity: 0.7; opacity: var(--tk-touch-feedback-opacity);
} }
} }
.input-sync-entry-text { .input-sync-entry-text {
font-size: var(--tk-font-body-lg); font-size: var(--tk-font-body-lg);
font-weight: 600; font-weight: 600;
color: $pri; color: var(--tk-pri);
} }
.input-sync-entry-hint { .input-sync-entry-hint {
@@ -65,14 +65,14 @@
/* ── card ── */ /* ── card ── */
.input-card { .input-card {
margin: 0 24px 20px; margin: 0 var(--tk-gap-lg) var(--tk-section-gap);
} }
.input-card-header { .input-card-header {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 14px; gap: 14px;
margin-bottom: 20px; margin-bottom: var(--tk-section-gap);
} }
.input-card-indicator { .input-card-indicator {
@@ -104,7 +104,7 @@
align-items: center; align-items: center;
background: $bg; background: $bg;
border-radius: $r-sm; border-radius: $r-sm;
padding: 22px 24px; padding: 22px var(--tk-gap-lg);
} }
.input-picker-value { .input-picker-value {
@@ -127,7 +127,7 @@
font-size: var(--tk-font-body-lg); font-size: var(--tk-font-body-lg);
font-weight: bold; font-weight: bold;
color: $tx; color: $tx;
margin-bottom: 16px; margin-bottom: var(--tk-gap-md);
display: block; display: block;
} }
@@ -135,7 +135,7 @@
.input-bp-group { .input-bp-group {
display: flex; display: flex;
align-items: flex-end; align-items: flex-end;
gap: 12px; gap: var(--tk-gap-sm);
} }
.input-bp-field { .input-bp-field {
@@ -146,14 +146,14 @@
font-size: var(--tk-font-body); font-size: var(--tk-font-body);
color: $tx2; color: $tx2;
display: block; display: block;
margin-bottom: 8px; margin-bottom: var(--tk-gap-xs);
} }
.input-bp-divider { .input-bp-divider {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
padding-bottom: 20px; padding-bottom: var(--tk-section-gap);
gap: 6px; gap: 6px;
} }
@@ -174,7 +174,7 @@
.input-field-box { .input-field-box {
background: $bg; background: $bg;
border-radius: $r-sm; border-radius: $r-sm;
padding: 20px 24px; padding: var(--tk-section-gap) var(--tk-gap-lg);
font-size: var(--tk-font-body-lg); font-size: var(--tk-font-body-lg);
color: $tx; color: $tx;
@include serif-number; @include serif-number;
@@ -195,16 +195,16 @@
/* ── submit ── */ /* ── submit ── */
.input-submit { .input-submit {
background: $pri; background: var(--tk-pri);
border-radius: $r; border-radius: $r;
padding: 26px; padding: 26px;
text-align: center; text-align: center;
margin: 48px 24px 0; margin: var(--tk-gap-2xl) var(--tk-gap-lg) 0;
box-shadow: $shadow-md; box-shadow: $shadow-md;
transition: opacity 0.2s; transition: opacity 0.2s;
&:active { &:active {
opacity: 0.85; opacity: var(--tk-touch-feedback-opacity);
} }
} }

View File

@@ -21,7 +21,7 @@
width: 88px; width: 88px;
height: 88px; height: 88px;
border-radius: $r-lg; border-radius: $r-lg;
background: $pri-l; background: var(--tk-pri-l);
margin-bottom: 20px; margin-bottom: 20px;
} }
@@ -29,7 +29,7 @@
font-family: 'Georgia', 'Times New Roman', serif; font-family: 'Georgia', 'Times New Roman', serif;
font-size: var(--tk-font-hero); font-size: var(--tk-font-hero);
font-weight: bold; font-weight: bold;
color: $pri; color: var(--tk-pri);
} }
.trend-hero-title { .trend-hero-title {
@@ -131,7 +131,7 @@
.trend-item-value { .trend-item-value {
font-size: var(--tk-font-body-lg); font-size: var(--tk-font-body-lg);
color: $pri; color: var(--tk-pri);
@include serif-number; @include serif-number;
font-weight: bold; font-weight: bold;
} }

View File

@@ -6,9 +6,9 @@
/* ===== 余额卡片 ===== */ /* ===== 余额卡片 ===== */
.balance-card { .balance-card {
background: $card; background: $card;
margin: 20px 24px 16px; margin: var(--tk-section-gap) var(--tk-page-padding) var(--tk-gap-md);
border-radius: $r-lg; border-radius: $r-lg;
padding: 32px; padding: var(--tk-gap-xl);
box-shadow: $shadow-sm; box-shadow: $shadow-sm;
} }
@@ -16,16 +16,16 @@
font-size: var(--tk-font-h2); font-size: var(--tk-font-h2);
color: $tx2; color: $tx2;
display: block; display: block;
margin-bottom: 8px; margin-bottom: var(--tk-gap-xs);
} }
.balance-value { .balance-value {
@include serif-number; @include serif-number;
font-size: var(--tk-font-hero); font-size: var(--tk-font-hero);
font-weight: bold; font-weight: bold;
color: $pri; color: var(--tk-pri);
display: block; display: block;
margin-bottom: 28px; margin-bottom: var(--tk-card-padding-lg);
letter-spacing: -1px; letter-spacing: -1px;
} }
@@ -34,7 +34,7 @@
align-items: center; align-items: center;
background: $bg; background: $bg;
border-radius: $r; border-radius: $r;
padding: 20px 0; padding: var(--tk-section-gap) 0;
} }
.stat-item { .stat-item {
@@ -48,7 +48,7 @@
@include serif-number; @include serif-number;
font-size: var(--tk-font-num); font-size: var(--tk-font-num);
font-weight: bold; font-weight: bold;
margin-bottom: 4px; margin-bottom: var(--tk-gap-2xs);
&.stat-earn { &.stat-earn {
color: $acc; color: $acc;
@@ -78,14 +78,14 @@
.type-tabs { .type-tabs {
display: flex; display: flex;
gap: 0; gap: 0;
padding: 0 24px; padding: 0 var(--tk-page-padding);
margin-bottom: 16px; margin-bottom: var(--tk-gap-md);
} }
.type-tab { .type-tab {
@include flex-center; @include flex-center;
flex: 1; flex: 1;
padding: 16px 0; padding: var(--tk-gap-md) 0;
position: relative; position: relative;
&.active::after { &.active::after {
@@ -96,7 +96,7 @@
transform: translateX(-50%); transform: translateX(-50%);
width: 40px; width: 40px;
height: 4px; height: 4px;
background: $pri; background: var(--tk-pri);
border-radius: $r-xs; border-radius: $r-xs;
} }
} }
@@ -106,14 +106,14 @@
color: $tx3; color: $tx3;
.type-tab.active & { .type-tab.active & {
color: $pri; color: var(--tk-pri);
font-weight: bold; font-weight: bold;
} }
} }
/* ===== 交易列表 ===== */ /* ===== 交易列表 ===== */
.transaction-list { .transaction-list {
padding: 0 24px; padding: 0 var(--tk-page-padding);
} }
.transaction-item { .transaction-item {
@@ -121,8 +121,8 @@
align-items: center; align-items: center;
background: $card; background: $card;
border-radius: $r; border-radius: $r;
padding: 24px; padding: var(--tk-card-padding);
margin-bottom: 12px; margin-bottom: var(--tk-gap-sm);
box-shadow: $shadow-sm; box-shadow: $shadow-sm;
} }
@@ -131,7 +131,7 @@
height: 64px; height: 64px;
border-radius: $r; border-radius: $r;
@include flex-center; @include flex-center;
margin-right: 20px; margin-right: var(--tk-section-gap);
flex-shrink: 0; flex-shrink: 0;
&.tx-badge-earn { &.tx-badge-earn {
@@ -174,7 +174,7 @@
font-size: var(--tk-font-body-lg); font-size: var(--tk-font-body-lg);
color: $tx; color: $tx;
display: block; display: block;
margin-bottom: 4px; margin-bottom: var(--tk-gap-2xs);
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
@@ -190,7 +190,7 @@
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: flex-end; align-items: flex-end;
margin-left: 16px; margin-left: var(--tk-gap-md);
flex-shrink: 0; flex-shrink: 0;
} }
@@ -198,7 +198,7 @@
@include serif-number; @include serif-number;
font-size: var(--tk-font-num); font-size: var(--tk-font-num);
font-weight: bold; font-weight: bold;
margin-bottom: 4px; margin-bottom: var(--tk-gap-2xs);
&.tx-amount-positive { &.tx-amount-positive {
color: $acc; color: $acc;

View File

@@ -10,9 +10,9 @@
.product-card { .product-card {
display: flex; display: flex;
align-items: center; align-items: center;
padding: 32px 24px; padding: var(--tk-gap-xl) var(--tk-gap-lg);
background: $card; background: $card;
margin: 20px 24px 16px; margin: var(--tk-section-gap) var(--tk-gap-lg) var(--tk-gap-md);
border-radius: $r-lg; border-radius: $r-lg;
box-shadow: $shadow-sm; box-shadow: $shadow-sm;
} }
@@ -22,7 +22,7 @@
height: 128px; height: 128px;
border-radius: $r; border-radius: $r;
@include flex-center; @include flex-center;
margin-right: 24px; margin-right: var(--tk-gap-lg);
flex-shrink: 0; flex-shrink: 0;
&--physical { &--physical {
@@ -30,11 +30,11 @@
} }
&--service { &--service {
background: $pri; background: var(--tk-pri);
} }
&--privilege { &--privilege {
background: $pri-d; background: var(--tk-pri-d);
} }
} }
@@ -55,20 +55,20 @@
font-weight: bold; font-weight: bold;
color: $tx; color: $tx;
display: block; display: block;
margin-bottom: 12px; margin-bottom: var(--tk-gap-sm);
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
} }
.product-type-tag { .product-type-tag {
@include tag($pri-l, $pri-d); @include tag(var(--tk-pri-l), var(--tk-pri-d));
} }
/* ===== 兑换明细 ===== */ /* ===== 兑换明细 ===== */
.detail-section { .detail-section {
padding: 0 24px; padding: 0 var(--tk-gap-lg);
margin-bottom: 16px; margin-bottom: var(--tk-gap-md);
} }
.detail-section-title { .detail-section-title {
@@ -79,14 +79,14 @@
background: $card; background: $card;
border-radius: $r; border-radius: $r;
box-shadow: $shadow-sm; box-shadow: $shadow-sm;
padding: 0 24px; padding: 0 var(--tk-gap-lg);
} }
.detail-row { .detail-row {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
padding: 24px 0; padding: var(--tk-gap-lg) 0;
border-bottom: 1px solid $bd-l; border-bottom: 1px solid $bd-l;
&.last { &.last {
@@ -106,7 +106,7 @@
font-weight: bold; font-weight: bold;
&.detail-cost { &.detail-cost {
color: $pri; color: var(--tk-pri);
font-size: var(--tk-font-num-lg); font-size: var(--tk-font-num-lg);
} }
@@ -122,8 +122,8 @@
/* ===== 温馨提示 ===== */ /* ===== 温馨提示 ===== */
.notice-section { .notice-section {
background: $card; background: $card;
padding: 24px; padding: var(--tk-gap-lg);
margin: 0 24px; margin: 0 var(--tk-gap-lg);
border-radius: $r; border-radius: $r;
box-shadow: $shadow-sm; box-shadow: $shadow-sm;
} }
@@ -131,7 +131,7 @@
.notice-title { .notice-title {
@include section-title; @include section-title;
font-size: var(--tk-font-body-lg); font-size: var(--tk-font-body-lg);
margin-bottom: 12px; margin-bottom: var(--tk-gap-sm);
} }
.notice-text { .notice-text {
@@ -139,7 +139,7 @@
color: $tx3; color: $tx3;
display: block; display: block;
line-height: 1.7; line-height: 1.7;
margin-bottom: 4px; margin-bottom: var(--tk-gap-2xs);
} }
/* ===== 底部操作栏 ===== */ /* ===== 底部操作栏 ===== */
@@ -150,10 +150,10 @@
right: 0; right: 0;
display: flex; display: flex;
align-items: center; align-items: center;
padding: 16px 24px; padding: var(--tk-gap-md) var(--tk-gap-lg);
padding-bottom: calc(16px + env(safe-area-inset-bottom)); padding-bottom: calc(var(--tk-gap-md) + env(safe-area-inset-bottom));
background: $card; background: $card;
box-shadow: 0 -2px 12px rgba(45, 42, 38, 0.06); box-shadow: 0 -2px 12px rgba($tx, 0.06);
z-index: 10; z-index: 10;
} }
@@ -172,18 +172,18 @@
@include serif-number; @include serif-number;
font-size: var(--tk-font-num-lg); font-size: var(--tk-font-num-lg);
font-weight: bold; font-weight: bold;
color: $pri; color: var(--tk-pri);
} }
.footer-cost-unit { .footer-cost-unit {
font-size: var(--tk-font-body); font-size: var(--tk-font-body);
color: $tx2; color: $tx2;
margin-left: 4px; margin-left: var(--tk-gap-2xs);
} }
.confirm-btn { .confirm-btn {
background: $pri; background: var(--tk-pri);
padding: 20px 48px; padding: var(--tk-section-gap) var(--tk-gap-2xl);
border-radius: $r-pill; border-radius: $r-pill;
transition: opacity 0.2s; transition: opacity 0.2s;

View File

@@ -6,11 +6,11 @@
/* ===== 订单列表 ===== */ /* ===== 订单列表 ===== */
.order-list { .order-list {
padding: 0 24px; padding: 0 var(--tk-gap-lg);
} }
.order-card { .order-card {
margin-bottom: 16px; margin-bottom: var(--tk-gap-md);
overflow: hidden; overflow: hidden;
} }
@@ -18,7 +18,7 @@
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
padding: 24px 24px 16px; padding: var(--tk-gap-lg) var(--tk-gap-lg) var(--tk-gap-md);
border-bottom: 1px solid $bd-l; border-bottom: 1px solid $bd-l;
} }
@@ -34,9 +34,9 @@
} }
.order-status-tag { .order-status-tag {
padding: 4px 16px; padding: var(--tk-gap-2xs) var(--tk-gap-md);
border-radius: $r-pill; border-radius: $r-pill;
margin-left: 12px; margin-left: var(--tk-gap-sm);
flex-shrink: 0; flex-shrink: 0;
&--pending { &--pending {
@@ -62,14 +62,14 @@
} }
.order-body { .order-body {
padding: 16px 24px 20px; padding: var(--tk-gap-md) var(--tk-gap-lg) var(--tk-section-gap);
} }
.order-row { .order-row {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
padding: 8px 0; padding: var(--tk-gap-xs) 0;
} }
.order-row-label { .order-row-label {
@@ -83,7 +83,7 @@
color: $tx; color: $tx;
&.order-cost { &.order-cost {
color: $pri; color: var(--tk-pri);
font-weight: bold; font-weight: bold;
} }
} }
@@ -92,22 +92,22 @@
.order-qrcode { .order-qrcode {
display: flex; display: flex;
align-items: center; align-items: center;
padding: 16px; padding: var(--tk-gap-md);
margin-top: 12px; margin-top: var(--tk-gap-sm);
background: $pri-l; background: var(--tk-pri-l);
border-radius: $r-sm; border-radius: $r-sm;
} }
.qrcode-label { .qrcode-label {
font-size: var(--tk-font-h2); font-size: var(--tk-font-h2);
color: $tx3; color: $tx3;
margin-right: 8px; margin-right: var(--tk-gap-xs);
} }
.qrcode-value { .qrcode-value {
@include serif-number; @include serif-number;
font-size: var(--tk-font-h2); font-size: var(--tk-font-h2);
color: $pri-d; color: var(--tk-pri-d);
font-weight: bold; font-weight: bold;
flex: 1; flex: 1;
overflow: hidden; overflow: hidden;
@@ -117,7 +117,7 @@
.qrcode-tap { .qrcode-tap {
font-size: var(--tk-font-body); font-size: var(--tk-font-body);
color: $pri; color: var(--tk-pri);
margin-left: 8px; margin-left: var(--tk-gap-xs);
flex-shrink: 0; flex-shrink: 0;
} }

View File

@@ -5,19 +5,19 @@
.page-title { .page-title {
@include section-title; @include section-title;
padding-left: 4px; padding-left: var(--tk-gap-2xs);
} }
.consent-list { .consent-list {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 16px; gap: var(--tk-gap-md);
} }
.consent-card { .consent-card {
background: $card; background: $card;
border-radius: $r; border-radius: $r;
padding: 28px; padding: var(--tk-card-padding-lg);
box-shadow: $shadow-sm; box-shadow: $shadow-sm;
} }
@@ -25,7 +25,7 @@
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
margin-bottom: 12px; margin-bottom: var(--tk-gap-sm);
} }
.consent-card__type { .consent-card__type {
@@ -52,19 +52,19 @@
font-size: var(--tk-font-h2); font-size: var(--tk-font-h2);
color: $tx2; color: $tx2;
display: block; display: block;
margin-bottom: 4px; margin-bottom: var(--tk-gap-2xs);
font-variant-numeric: tabular-nums; font-variant-numeric: tabular-nums;
} }
.revoke-btn { .revoke-btn {
margin-top: 16px; margin-top: var(--tk-gap-md);
padding: 12px 0; padding: var(--tk-gap-sm) 0;
text-align: center; text-align: center;
border-radius: $r-sm; border-radius: $r-sm;
border: 1px solid $dan; border: 1px solid $dan;
&:active { &:active {
background: $dan-l; opacity: var(--tk-touch-feedback-opacity);
} }
&--disabled { &--disabled {

Some files were not shown because too many files have changed in this diff Show More