diff --git a/apps/miniprogram/src/components/EmptyState/index.scss b/apps/miniprogram/src/components/EmptyState/index.scss index a588b45..166774a 100644 --- a/apps/miniprogram/src/components/EmptyState/index.scss +++ b/apps/miniprogram/src/components/EmptyState/index.scss @@ -5,18 +5,18 @@ flex-direction: column; align-items: center; justify-content: center; - padding: 120px 40px; + padding: var(--tk-gap-2xl) var(--tk-gap-xl); } .empty-state-icon-wrap { - width: 120px; - height: 120px; + width: var(--tk-gap-2xl); + height: var(--tk-gap-2xl); border-radius: 50%; background: $surface-alt; display: flex; align-items: center; justify-content: center; - margin-bottom: 24px; + margin-bottom: var(--tk-gap-lg); } .empty-state-icon-char { @@ -29,19 +29,19 @@ .empty-state-text { font-size: var(--tk-font-num); color: $tx2; - margin-bottom: 8px; + margin-bottom: var(--tk-gap-xs); } .empty-state-hint { font-size: var(--tk-font-h2); color: var(--tk-text-secondary); - margin-bottom: 32px; + margin-bottom: var(--tk-gap-xl); } .empty-state-action { - background: $pri; + background: var(--tk-pri); border-radius: 40px; - padding: 16px 48px; + padding: var(--tk-gap-md) var(--tk-gap-2xl); } .empty-state-action-text { diff --git a/apps/miniprogram/src/components/ErrorBoundary/index.scss b/apps/miniprogram/src/components/ErrorBoundary/index.scss index 1445790..494b1b1 100644 --- a/apps/miniprogram/src/components/ErrorBoundary/index.scss +++ b/apps/miniprogram/src/components/ErrorBoundary/index.scss @@ -13,7 +13,7 @@ width: 64px; height: 64px; border-radius: 32px; - background: $pri-l; + background: var(--tk-pri-l); display: flex; align-items: center; justify-content: center; @@ -24,7 +24,7 @@ font-family: 'Georgia', 'Times New Roman', serif; font-size: var(--tk-font-h1); font-weight: 600; - color: $pri-d; + color: var(--tk-pri-d); } .error-title { @@ -41,7 +41,7 @@ } .error-retry-btn { - background: $pri; + background: var(--tk-pri); border-radius: $r-sm; padding: 14px 48px; } diff --git a/apps/miniprogram/src/components/ErrorState/index.scss b/apps/miniprogram/src/components/ErrorState/index.scss index b4db008..43af1a1 100644 --- a/apps/miniprogram/src/components/ErrorState/index.scss +++ b/apps/miniprogram/src/components/ErrorState/index.scss @@ -5,25 +5,25 @@ flex-direction: column; align-items: center; justify-content: center; - padding: 120px 40px; + padding: var(--tk-gap-2xl) var(--tk-gap-xl); } .error-state-icon { font-size: var(--tk-font-display); - margin-bottom: 24px; + margin-bottom: var(--tk-gap-lg); } .error-state-text { font-size: var(--tk-font-body-lg); color: $tx2; - margin-bottom: 32px; + margin-bottom: var(--tk-gap-xl); text-align: center; } .error-state-retry { - background: $pri; + background: var(--tk-pri); border-radius: 40px; - padding: 16px 48px; + padding: var(--tk-gap-md) var(--tk-gap-2xl); } .error-state-retry-text { diff --git a/apps/miniprogram/src/components/GuestGuard/index.scss b/apps/miniprogram/src/components/GuestGuard/index.scss index 208e350..f86bf85 100644 --- a/apps/miniprogram/src/components/GuestGuard/index.scss +++ b/apps/miniprogram/src/components/GuestGuard/index.scss @@ -48,7 +48,7 @@ display: inline-block; height: 48px; padding: 0 32px; - background: $pri; + background: var(--tk-pri); border-radius: $r-pill; @include flex-center; diff --git a/apps/miniprogram/src/components/Loading/index.scss b/apps/miniprogram/src/components/Loading/index.scss index c4f4a8a..e8a3e11 100644 --- a/apps/miniprogram/src/components/Loading/index.scss +++ b/apps/miniprogram/src/components/Loading/index.scss @@ -12,7 +12,7 @@ width: 48px; height: 48px; border: 4px solid $bd; - border-top-color: $pri; + border-top-color: var(--tk-pri); border-radius: 50%; animation: spin 0.8s linear infinite; margin-bottom: 20px; diff --git a/apps/miniprogram/src/components/SegmentTabs/index.scss b/apps/miniprogram/src/components/SegmentTabs/index.scss index 882a2bc..4a1e01a 100644 --- a/apps/miniprogram/src/components/SegmentTabs/index.scss +++ b/apps/miniprogram/src/components/SegmentTabs/index.scss @@ -17,7 +17,7 @@ &--active { .seg-tab__text { - color: $pri; + color: var(--tk-pri); font-weight: bold; } @@ -28,7 +28,7 @@ left: 30%; right: 30%; height: 4px; - background: $pri; + background: var(--tk-pri); border-radius: $r-xs; } } diff --git a/apps/miniprogram/src/components/StepIndicator/index.scss b/apps/miniprogram/src/components/StepIndicator/index.scss index 41c4379..06eb7f9 100644 --- a/apps/miniprogram/src/components/StepIndicator/index.scss +++ b/apps/miniprogram/src/components/StepIndicator/index.scss @@ -44,7 +44,7 @@ z-index: 1; &.step-current { - background: $pri; + background: var(--tk-pri); color: white; } @@ -61,7 +61,7 @@ text-align: center; &.step-current { - color: $pri; + color: var(--tk-pri); font-weight: bold; } diff --git a/apps/miniprogram/src/components/WeekCalendar/index.scss b/apps/miniprogram/src/components/WeekCalendar/index.scss index 25a3a9a..5e27b3c 100644 --- a/apps/miniprogram/src/components/WeekCalendar/index.scss +++ b/apps/miniprogram/src/components/WeekCalendar/index.scss @@ -15,7 +15,7 @@ .week-arrow { font-size: var(--tk-font-body-lg); - color: $pri; + color: var(--tk-pri); padding: 0 16px; } @@ -52,7 +52,7 @@ } .cell-today { - color: $pri; + color: var(--tk-pri); font-weight: bold; } @@ -68,7 +68,7 @@ } .cell-selected { - background: $pri; + background: var(--tk-pri); border-radius: $r-sm; .cell-date { color: white; } diff --git a/apps/miniprogram/src/components/patterns/PageHeader/index.scss b/apps/miniprogram/src/components/patterns/PageHeader/index.scss index fc3c482..b409519 100644 --- a/apps/miniprogram/src/components/patterns/PageHeader/index.scss +++ b/apps/miniprogram/src/components/patterns/PageHeader/index.scss @@ -4,9 +4,10 @@ display: flex; align-items: center; justify-content: space-between; - height: var(--tk-touch-min); + height: 44px; padding: 0 var(--tk-page-padding); background: $bg; + border-bottom: 1px solid $bd-l; z-index: 10; &--sticky { @@ -17,7 +18,7 @@ &__left { display: flex; align-items: center; - gap: 8px; + gap: var(--tk-gap-xs); min-width: 0; flex: 1; } @@ -27,19 +28,19 @@ align-items: center; justify-content: center; width: 36px; - height: 36px; - min-height: var(--tk-touch-min); + height: 44px; } &__back-icon { - font-size: 24px; - color: $tx; + font-size: var(--tk-font-h2); + color: var(--tk-pri); line-height: 1; } &__title { - font-size: var(--tk-font-h1); - font-weight: 600; + font-family: Georgia, 'Times New Roman', serif; + font-size: var(--tk-font-nav); + font-weight: 700; color: $tx; overflow: hidden; text-overflow: ellipsis; @@ -49,7 +50,7 @@ &__right { display: flex; align-items: center; - gap: 12px; + gap: var(--tk-gap-sm); flex-shrink: 0; } } diff --git a/apps/miniprogram/src/components/patterns/PaginationBar/index.scss b/apps/miniprogram/src/components/patterns/PaginationBar/index.scss index 0dba797..205ebc7 100644 --- a/apps/miniprogram/src/components/patterns/PaginationBar/index.scss +++ b/apps/miniprogram/src/components/patterns/PaginationBar/index.scss @@ -11,7 +11,7 @@ display: flex; align-items: center; justify-content: center; - padding: 8px 20px; + padding: var(--tk-gap-xs) var(--tk-section-gap); border-radius: $r-sm; background: var(--tk-card-bg); border: 1px solid $bd; diff --git a/apps/miniprogram/src/components/patterns/SearchSection/index.scss b/apps/miniprogram/src/components/patterns/SearchSection/index.scss index afb9843..e2c213e 100644 --- a/apps/miniprogram/src/components/patterns/SearchSection/index.scss +++ b/apps/miniprogram/src/components/patterns/SearchSection/index.scss @@ -6,16 +6,16 @@ &__input-wrap { display: flex; align-items: center; - gap: 8px; + gap: var(--tk-gap-xs); background: var(--tk-card-bg); border-radius: var(--tk-card-radius); - padding: 0 16px; - height: var(--tk-touch-min); + padding: 0 var(--tk-gap-md); + height: var(--tk-input-height); box-shadow: $shadow-sm; } &__icon { - font-size: 16px; + font-size: var(--tk-font-body-sm); flex-shrink: 0; } diff --git a/apps/miniprogram/src/components/ui/AlertCard/index.scss b/apps/miniprogram/src/components/ui/AlertCard/index.scss new file mode 100644 index 0000000..ea3a115 --- /dev/null +++ b/apps/miniprogram/src/components/ui/AlertCard/index.scss @@ -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; + } +} diff --git a/apps/miniprogram/src/components/ui/AlertCard/index.tsx b/apps/miniprogram/src/components/ui/AlertCard/index.tsx new file mode 100644 index 0000000..3487bf4 --- /dev/null +++ b/apps/miniprogram/src/components/ui/AlertCard/index.tsx @@ -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 = ({ + variant = 'left-border', + title, + subtitle, + children, + className = '', +}) => { + const cls = [ + 'alert-card', + `alert-card--${variant}`, + className, + ].filter(Boolean).join(' '); + + return ( + + {title && ( + + {title} + {subtitle && {subtitle}} + + )} + {children ?? {subtitle}} + + ); +}; + +export default React.memo(AlertCard); diff --git a/apps/miniprogram/src/components/ui/ChatBubble/index.scss b/apps/miniprogram/src/components/ui/ChatBubble/index.scss new file mode 100644 index 0000000..400204a --- /dev/null +++ b/apps/miniprogram/src/components/ui/ChatBubble/index.scss @@ -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; + } +} diff --git a/apps/miniprogram/src/components/ui/ChatBubble/index.tsx b/apps/miniprogram/src/components/ui/ChatBubble/index.tsx new file mode 100644 index 0000000..0f77278 --- /dev/null +++ b/apps/miniprogram/src/components/ui/ChatBubble/index.tsx @@ -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 = ({ + content, + isMine = false, + time, + className = '', +}) => { + const cls = [ + 'chat-bubble', + isMine ? 'chat-bubble--mine' : 'chat-bubble--other', + className, + ].filter(Boolean).join(' '); + + return ( + + + {content} + + {time && {time}} + + ); +}; + +export default React.memo(ChatBubble); diff --git a/apps/miniprogram/src/components/ui/ContentCard/index.tsx b/apps/miniprogram/src/components/ui/ContentCard/index.tsx index f29a847..c3c4a38 100644 --- a/apps/miniprogram/src/components/ui/ContentCard/index.tsx +++ b/apps/miniprogram/src/components/ui/ContentCard/index.tsx @@ -15,9 +15,9 @@ interface ContentCardProps { const PADDING_MAP = { none: '0', - sm: '12px', + sm: 'var(--tk-card-padding-sm)', md: 'var(--tk-card-padding)', - lg: '32px', + lg: 'var(--tk-card-padding-lg)', } as const; const ContentCard: React.FC = ({ diff --git a/apps/miniprogram/src/components/ui/FormInput/index.scss b/apps/miniprogram/src/components/ui/FormInput/index.scss new file mode 100644 index 0000000..f08e4a7 --- /dev/null +++ b/apps/miniprogram/src/components/ui/FormInput/index.scss @@ -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; + } +} diff --git a/apps/miniprogram/src/components/ui/FormInput/index.tsx b/apps/miniprogram/src/components/ui/FormInput/index.tsx new file mode 100644 index 0000000..b4c337a --- /dev/null +++ b/apps/miniprogram/src/components/ui/FormInput/index.tsx @@ -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 = ({ + 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 ( + + {label && {label}} + + onInput?.(e.detail.value)} + type={type} + maxlength={maxLength} + disabled={disabled} + /> + + {error && {error}} + + ); +}; + +export default React.memo(FormInput); diff --git a/apps/miniprogram/src/components/ui/GradientHeader/index.scss b/apps/miniprogram/src/components/ui/GradientHeader/index.scss new file mode 100644 index 0000000..f5bdb39 --- /dev/null +++ b/apps/miniprogram/src/components/ui/GradientHeader/index.scss @@ -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; +} diff --git a/apps/miniprogram/src/components/ui/GradientHeader/index.tsx b/apps/miniprogram/src/components/ui/GradientHeader/index.tsx new file mode 100644 index 0000000..dc7cf0c --- /dev/null +++ b/apps/miniprogram/src/components/ui/GradientHeader/index.tsx @@ -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 = ({ + children, + className = '', +}) => { + return ( + + {children} + + ); +}; + +export default React.memo(GradientHeader); diff --git a/apps/miniprogram/src/components/ui/InfoRow/index.scss b/apps/miniprogram/src/components/ui/InfoRow/index.scss new file mode 100644 index 0000000..85c6416 --- /dev/null +++ b/apps/miniprogram/src/components/ui/InfoRow/index.scss @@ -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); + } +} diff --git a/apps/miniprogram/src/components/ui/InfoRow/index.tsx b/apps/miniprogram/src/components/ui/InfoRow/index.tsx new file mode 100644 index 0000000..55e9bfa --- /dev/null +++ b/apps/miniprogram/src/components/ui/InfoRow/index.tsx @@ -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 = ({ + label, + value, + valueNode, + last = false, + className = '', +}) => { + const cls = [ + 'info-row', + last && 'info-row--last', + className, + ].filter(Boolean).join(' '); + + return ( + + {label} + {valueNode ?? {value}} + + ); +}; + +export default React.memo(InfoRow); diff --git a/apps/miniprogram/src/components/ui/ListItem/index.scss b/apps/miniprogram/src/components/ui/ListItem/index.scss new file mode 100644 index 0000000..4cdc135 --- /dev/null +++ b/apps/miniprogram/src/components/ui/ListItem/index.scss @@ -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); + } +} diff --git a/apps/miniprogram/src/components/ui/ListItem/index.tsx b/apps/miniprogram/src/components/ui/ListItem/index.tsx new file mode 100644 index 0000000..2a4e8c6 --- /dev/null +++ b/apps/miniprogram/src/components/ui/ListItem/index.tsx @@ -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 = ({ + 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 ( + + {leftIcon && {leftIcon}} + + {title} + {subtitle && {subtitle}} + + {extra && {extra}} + {showArrow && } + + ); +}; + +export default React.memo(ListItem); diff --git a/apps/miniprogram/src/components/ui/LoadingCard/index.scss b/apps/miniprogram/src/components/ui/LoadingCard/index.scss index 944cfd8..3726531 100644 --- a/apps/miniprogram/src/components/ui/LoadingCard/index.scss +++ b/apps/miniprogram/src/components/ui/LoadingCard/index.scss @@ -35,7 +35,7 @@ &__row { display: flex; align-items: center; - gap: 12px; + gap: var(--tk-gap-sm); } &__circle { @@ -50,7 +50,7 @@ flex: 1; display: flex; flex-direction: column; - gap: 8px; + gap: var(--tk-gap-xs); } &__line { diff --git a/apps/miniprogram/src/components/ui/PageShell/index.tsx b/apps/miniprogram/src/components/ui/PageShell/index.tsx index 0e63a08..07829ed 100644 --- a/apps/miniprogram/src/components/ui/PageShell/index.tsx +++ b/apps/miniprogram/src/components/ui/PageShell/index.tsx @@ -13,9 +13,9 @@ interface PageShellProps { const PADDING_MAP = { none: '0', - sm: '16px', + sm: 'var(--tk-gap-md)', md: 'var(--tk-page-padding)', - lg: '32px', + lg: 'var(--tk-gap-xl)', } as const; const PageShell: React.FC = ({ diff --git a/apps/miniprogram/src/components/ui/PrimaryButton/index.scss b/apps/miniprogram/src/components/ui/PrimaryButton/index.scss new file mode 100644 index 0000000..66d9933 --- /dev/null +++ b/apps/miniprogram/src/components/ui/PrimaryButton/index.scss @@ -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); } +} diff --git a/apps/miniprogram/src/components/ui/PrimaryButton/index.tsx b/apps/miniprogram/src/components/ui/PrimaryButton/index.tsx new file mode 100644 index 0000000..8ba21a9 --- /dev/null +++ b/apps/miniprogram/src/components/ui/PrimaryButton/index.tsx @@ -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 = ({ + 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 ( + + {loading && } + {children} + + ); +}; + +export default React.memo(PrimaryButton); diff --git a/apps/miniprogram/src/components/ui/ProgressRing/index.scss b/apps/miniprogram/src/components/ui/ProgressRing/index.scss new file mode 100644 index 0000000..62b4f08 --- /dev/null +++ b/apps/miniprogram/src/components/ui/ProgressRing/index.scss @@ -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); + } +} diff --git a/apps/miniprogram/src/components/ui/ProgressRing/index.tsx b/apps/miniprogram/src/components/ui/ProgressRing/index.tsx new file mode 100644 index 0000000..736836b --- /dev/null +++ b/apps/miniprogram/src/components/ui/ProgressRing/index.tsx @@ -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 = ({ + 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 ( + + + + + + + {label ? ( + {label} + ) : ( + {Math.round(progress * 100)}% + )} + + + ); +}; + +export default React.memo(ProgressRing); diff --git a/apps/miniprogram/src/components/ui/SecondaryButton/index.scss b/apps/miniprogram/src/components/ui/SecondaryButton/index.scss new file mode 100644 index 0000000..fd13b6a --- /dev/null +++ b/apps/miniprogram/src/components/ui/SecondaryButton/index.scss @@ -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; + } +} diff --git a/apps/miniprogram/src/components/ui/SecondaryButton/index.tsx b/apps/miniprogram/src/components/ui/SecondaryButton/index.tsx new file mode 100644 index 0000000..14d6ede --- /dev/null +++ b/apps/miniprogram/src/components/ui/SecondaryButton/index.tsx @@ -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 = ({ + children, + onClick, + disabled = false, + className = '', +}) => { + const cls = [ + 'secondary-btn', + disabled && 'secondary-btn--disabled', + className, + ].filter(Boolean).join(' '); + + return ( + + {children} + + ); +}; + +export default React.memo(SecondaryButton); diff --git a/apps/miniprogram/src/components/ui/SectionTitle/index.scss b/apps/miniprogram/src/components/ui/SectionTitle/index.scss index 38e04cf..b8ea551 100644 --- a/apps/miniprogram/src/components/ui/SectionTitle/index.scss +++ b/apps/miniprogram/src/components/ui/SectionTitle/index.scss @@ -9,13 +9,13 @@ &__left { display: flex; align-items: center; - gap: 10px; + gap: var(--tk-gap-sm); } &__bar { width: 3px; height: 20px; - background: $pri; + background: var(--tk-pri); border-radius: 2px; flex-shrink: 0; } @@ -43,7 +43,7 @@ &__action { font-size: var(--tk-font-body-sm); - color: $pri; + color: var(--tk-pri); min-height: var(--tk-touch-min); display: flex; align-items: center; diff --git a/apps/miniprogram/src/components/ui/StatusTag/index.scss b/apps/miniprogram/src/components/ui/StatusTag/index.scss index 063f421..1ff3962 100644 --- a/apps/miniprogram/src/components/ui/StatusTag/index.scss +++ b/apps/miniprogram/src/components/ui/StatusTag/index.scss @@ -11,7 +11,7 @@ white-space: nowrap; &--sm { - padding: 2px 8px; - font-size: 11px; + padding: 2px var(--tk-gap-xs); + font-size: var(--tk-font-micro); } } diff --git a/apps/miniprogram/src/components/ui/TabFilter/index.scss b/apps/miniprogram/src/components/ui/TabFilter/index.scss new file mode 100644 index 0000000..d09ea79 --- /dev/null +++ b/apps/miniprogram/src/components/ui/TabFilter/index.scss @@ -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; + } +} diff --git a/apps/miniprogram/src/components/ui/TabFilter/index.tsx b/apps/miniprogram/src/components/ui/TabFilter/index.tsx new file mode 100644 index 0000000..ac652c1 --- /dev/null +++ b/apps/miniprogram/src/components/ui/TabFilter/index.tsx @@ -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 = ({ + tabs, + activeIndex, + onChange, + variant = 'fill', + className = '', +}) => { + const cls = [ + 'tab-filter', + `tab-filter--${variant}`, + className, + ].filter(Boolean).join(' '); + + return ( + + {tabs.map((tab, i) => ( + onChange(i)} + > + {tab} + + ))} + + ); +}; + +export default React.memo(TabFilter); diff --git a/apps/miniprogram/src/components/ui/VitalCard/index.scss b/apps/miniprogram/src/components/ui/VitalCard/index.scss new file mode 100644 index 0000000..edfc347 --- /dev/null +++ b/apps/miniprogram/src/components/ui/VitalCard/index.scss @@ -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; + } +} diff --git a/apps/miniprogram/src/components/ui/VitalCard/index.tsx b/apps/miniprogram/src/components/ui/VitalCard/index.tsx new file mode 100644 index 0000000..1c7e77d --- /dev/null +++ b/apps/miniprogram/src/components/ui/VitalCard/index.tsx @@ -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 = ({ + label, + value, + unit, + status, + onPress, + className = '', +}) => { + const cls = [ + 'vital-card', + onPress && 'vital-card--pressable', + className, + ].filter(Boolean).join(' '); + + return ( + + {label} + + {value} + {unit && {unit}} + + {status && } + + ); +}; + +export default React.memo(VitalCard); diff --git a/apps/miniprogram/src/hooks/useDoctorClass.ts b/apps/miniprogram/src/hooks/useDoctorClass.ts new file mode 100644 index 0000000..c412412 --- /dev/null +++ b/apps/miniprogram/src/hooks/useDoctorClass.ts @@ -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(' '); +} diff --git a/apps/miniprogram/src/pages/ai-report/detail/index.scss b/apps/miniprogram/src/pages/ai-report/detail/index.scss index 52cfd91..005c8d5 100644 --- a/apps/miniprogram/src/pages/ai-report/detail/index.scss +++ b/apps/miniprogram/src/pages/ai-report/detail/index.scss @@ -3,7 +3,7 @@ .detail-type { @include section-title; - margin-bottom: 12px; + margin-bottom: var(--tk-gap-sm); } .detail-meta { @@ -21,30 +21,30 @@ h1, h2, h3 { font-weight: bold; color: $tx; - margin: 24px 0 12px; + margin: var(--tk-gap-lg) 0 var(--tk-gap-sm); } p { font-size: var(--tk-font-body-lg); color: $tx; line-height: 1.8; - margin-bottom: 16px; + margin-bottom: var(--tk-gap-md); } ul { - padding-left: 32px; - margin-bottom: 16px; + padding-left: var(--tk-gap-xl); + margin-bottom: var(--tk-gap-md); } li { font-size: var(--tk-font-body-lg); color: $tx; line-height: 1.8; - margin-bottom: 8px; + margin-bottom: var(--tk-gap-xs); } strong { - color: $pri-d; + color: var(--tk-pri-d); } } @@ -57,32 +57,32 @@ .empty-text { display: block; text-align: center; - padding: 120px 0; + padding: var(--tk-gap-2xl) 0; color: var(--tk-text-secondary); font-size: var(--tk-font-body-lg); } .auto-badge { - margin-top: 16px; + margin-top: var(--tk-gap-md); display: inline-block; } .auto-badge-text { display: inline-block; - padding: 4px 16px; + padding: var(--tk-gap-2xs) var(--tk-gap-md); border-radius: $r-xs; font-size: var(--tk-font-body); font-weight: 500; - background: $pri-l; - color: $pri; + background: var(--tk-pri-l); + color: var(--tk-pri); } .trend-tip-card { background: $wrn-l; border: 1px solid $wrn; border-radius: $r; - padding: 20px 24px; - margin-bottom: 20px; + padding: var(--tk-section-gap) var(--tk-gap-lg); + margin-bottom: var(--tk-section-gap); } .trend-tip-text { diff --git a/apps/miniprogram/src/pages/ai-report/list/index.scss b/apps/miniprogram/src/pages/ai-report/list/index.scss index cb815b5..99b9700 100644 --- a/apps/miniprogram/src/pages/ai-report/list/index.scss +++ b/apps/miniprogram/src/pages/ai-report/list/index.scss @@ -14,8 +14,8 @@ .report-card { background: $card; border-radius: $r; - padding: 28px; - margin-bottom: 20px; + padding: var(--tk-card-padding-lg); + margin-bottom: var(--tk-section-gap); box-shadow: $shadow-sm; } @@ -23,7 +23,7 @@ display: flex; justify-content: space-between; align-items: center; - margin-bottom: 12px; + margin-bottom: var(--tk-gap-sm); } .card-type { @@ -41,7 +41,7 @@ } .status-streaming { - @include tag($pri-l, $pri); + @include tag(var(--tk-pri-l), var(--tk-pri)); } .status-failed { @@ -72,6 +72,6 @@ text-align: center; font-size: var(--tk-font-h2); color: var(--tk-text-secondary); - padding: 24px 0; + padding: var(--tk-gap-lg) 0; display: block; } diff --git a/apps/miniprogram/src/pages/appointment/create/index.scss b/apps/miniprogram/src/pages/appointment/create/index.scss index 55242e9..0fca6cf 100644 --- a/apps/miniprogram/src/pages/appointment/create/index.scss +++ b/apps/miniprogram/src/pages/appointment/create/index.scss @@ -9,7 +9,7 @@ /* 步骤内容 */ .step-content { - padding: 32px 24px; + padding: var(--tk-gap-xl) var(--tk-gap-lg); } .step-title { @@ -20,24 +20,24 @@ .dept-grid { display: grid; grid-template-columns: repeat(3, 1fr); - gap: 16px; + gap: var(--tk-gap-md); } .dept-card { background: $card; border-radius: $r; - padding: 28px 12px; + padding: var(--tk-card-padding-lg) var(--tk-gap-sm); display: flex; flex-direction: column; align-items: center; - gap: 12px; + gap: var(--tk-gap-sm); border: 2px solid transparent; transition: border-color 0.2s; box-shadow: $shadow-sm; &.dept-selected { - border-color: $pri; - background: $pri-l; + border-color: var(--tk-pri); + background: var(--tk-pri-l); } } @@ -45,11 +45,11 @@ width: 64px; height: 64px; border-radius: $r; - background: $pri-l; + background: var(--tk-pri-l); @include flex-center; .dept-selected & { - background: $pri; + background: var(--tk-pri); } } @@ -57,7 +57,7 @@ font-family: 'Georgia', 'Times New Roman', serif; font-size: var(--tk-font-num); font-weight: bold; - color: $pri; + color: var(--tk-pri); .dept-selected & { color: $white; @@ -72,7 +72,7 @@ /* 时段 */ .slot-section { - margin-top: 32px; + margin-top: var(--tk-gap-xl); } .slot-section-title { @@ -80,20 +80,20 @@ font-size: var(--tk-font-body-lg); font-weight: bold; color: $tx; - margin-bottom: 16px; + margin-bottom: var(--tk-gap-md); display: block; } .slot-grid { display: grid; grid-template-columns: repeat(2, 1fr); - gap: 12px; + gap: var(--tk-gap-sm); } .slot-card { background: $card; border-radius: $r-sm; - padding: 20px 24px; + padding: var(--tk-section-gap) var(--tk-gap-lg); border: 2px solid transparent; transition: all 0.2s; box-shadow: $shadow-sm; @@ -107,8 +107,8 @@ pointer-events: none; } &.slot-selected { - border-color: $pri; - background: $pri-l; + border-color: var(--tk-pri); + background: var(--tk-pri-l); } } @@ -124,7 +124,7 @@ font-size: var(--tk-font-body); color: $tx3; display: block; - margin-top: 6px; + margin-top: var(--tk-gap-2xs); } .slot-few .slot-count { color: $wrn; } @@ -132,20 +132,20 @@ /* 确认卡片 (step 3 医生信息) */ .confirm-card { - margin-bottom: 24px; + margin-bottom: var(--tk-gap-lg); } .confirm-row { display: flex; align-items: center; - gap: 16px; + gap: var(--tk-gap-md); } .confirm-icon-wrap { width: 56px; height: 56px; border-radius: $r-sm; - background: $pri-l; + background: var(--tk-pri-l); @include flex-center; flex-shrink: 0; } @@ -154,7 +154,7 @@ font-family: 'Georgia', 'Times New Roman', serif; font-size: var(--tk-font-h2); font-weight: bold; - color: $pri; + color: var(--tk-pri); } .confirm-info { @@ -176,37 +176,37 @@ } .confirm-dept-tag { - @include tag($pri-l, $pri); + @include tag(var(--tk-pri-l), var(--tk-pri)); flex-shrink: 0; } .confirm-dept-text { font-size: var(--tk-font-body); font-weight: 500; - color: $pri; + color: var(--tk-pri); } /* 医生列表 */ .doctor-list { display: flex; flex-direction: column; - gap: 16px; + gap: var(--tk-gap-md); } .doctor-card { background: $card; border-radius: $r; - padding: 24px 28px; + padding: var(--tk-gap-lg) var(--tk-card-padding-lg); display: flex; align-items: center; - gap: 20px; + gap: var(--tk-section-gap); box-shadow: $shadow-sm; border: 2px solid transparent; transition: border-color 0.2s; &.doctor-selected { - border-color: $pri; - background: $pri-l; + border-color: var(--tk-pri); + background: var(--tk-pri-l); } } @@ -214,7 +214,7 @@ width: 80px; height: 80px; border-radius: $r; - background: $pri-l; + background: var(--tk-pri-l); @include flex-center; flex-shrink: 0; } @@ -222,7 +222,7 @@ .doctor-avatar-text { font-family: 'Georgia', 'Times New Roman', serif; font-size: var(--tk-font-num); - color: $pri; + color: var(--tk-pri); font-weight: bold; } @@ -230,7 +230,7 @@ flex: 1; display: flex; flex-direction: column; - gap: 4px; + gap: var(--tk-gap-2xs); } .doctor-name { @@ -246,14 +246,14 @@ .doctor-specialty { font-size: var(--tk-font-body); - color: $pri; + color: var(--tk-pri); } .doctor-check { width: 48px; height: 48px; border-radius: $r-pill; - background: $pri; + background: var(--tk-pri); @include flex-center; flex-shrink: 0; } @@ -266,20 +266,20 @@ /* 表单 */ .form-group { - margin-top: 32px; + margin-top: var(--tk-gap-xl); } .form-label { font-size: var(--tk-font-h1); color: $tx2; - margin-bottom: 12px; + margin-bottom: var(--tk-gap-sm); display: block; } .form-input { background: $card; border-radius: $r-sm; - padding: 24px 28px; + padding: var(--tk-gap-lg) var(--tk-card-padding-lg); font-size: var(--tk-font-body-lg); color: $tx; width: 100%; @@ -305,8 +305,8 @@ left: 0; right: 0; display: flex; - gap: 16px; - padding: 20px 24px; + gap: var(--tk-gap-md); + padding: var(--tk-section-gap) var(--tk-gap-lg); padding-bottom: constant(safe-area-inset-bottom); padding-bottom: env(safe-area-inset-bottom); background: $card; @@ -315,7 +315,7 @@ .btn { flex: 1; - padding: 24px 0; + padding: var(--tk-gap-lg) 0; border-radius: $r-sm; text-align: center; transition: opacity 0.2s; @@ -327,7 +327,7 @@ .btn-next, .btn-submit { - background: $pri; + background: var(--tk-pri); } .btn-disabled { diff --git a/apps/miniprogram/src/pages/appointment/detail/index.scss b/apps/miniprogram/src/pages/appointment/detail/index.scss index d8cc7f2..2ef62a6 100644 --- a/apps/miniprogram/src/pages/appointment/detail/index.scss +++ b/apps/miniprogram/src/pages/appointment/detail/index.scss @@ -6,14 +6,14 @@ display: flex; flex-direction: column; align-items: center; - gap: 12px; - margin: 20px 24px 24px; + gap: var(--tk-gap-sm); + margin: var(--tk-section-gap) var(--tk-gap-lg) var(--tk-gap-lg); } .status-tag { @include tag($bd-l, $tx3); - margin-bottom: 8px; - padding: 8px 32px; + margin-bottom: var(--tk-gap-xs); + padding: var(--tk-gap-xs) var(--tk-gap-xl); border-radius: $r-pill; &.tag-pending { @@ -32,8 +32,8 @@ } &.tag-completed { - background: $pri-l; - .status-tag-text { color: $pri; } + background: var(--tk-pri-l); + .status-tag-text { color: var(--tk-pri); } } } @@ -56,12 +56,12 @@ /* 详情信息 */ .info-section-wrap { - margin: 0 24px 24px; + margin: 0 var(--tk-gap-lg) var(--tk-gap-lg); } .section-title { @include section-title; - margin-bottom: 24px; + margin-bottom: var(--tk-gap-lg); } .info-item { @@ -85,8 +85,8 @@ .info-icon-serif { font-family: 'Georgia', 'Times New Roman', serif; font-size: var(--tk-font-body); - color: $pri; - background: $pri-l; + color: var(--tk-pri); + background: var(--tk-pri-l); width: 36px; height: 36px; border-radius: $r-sm; @@ -126,7 +126,7 @@ /* 温馨提示 */ .tips-card-wrap { - margin: 0 24px 24px; + margin: 0 var(--tk-gap-lg) var(--tk-gap-lg); background: $wrn-l; } @@ -135,7 +135,7 @@ font-size: var(--tk-font-body-lg); font-weight: bold; color: $wrn; - margin-bottom: 12px; + margin-bottom: var(--tk-gap-sm); display: block; } @@ -151,7 +151,7 @@ bottom: 0; left: 0; right: 0; - padding: 20px 24px; + padding: var(--tk-section-gap) var(--tk-gap-lg); padding-bottom: constant(safe-area-inset-bottom); padding-bottom: env(safe-area-inset-bottom); background: $card; @@ -161,7 +161,7 @@ .cancel-btn { background: $dan-l; border-radius: $r-sm; - padding: 24px 0; + padding: var(--tk-gap-lg) 0; text-align: center; transition: opacity 0.2s; } diff --git a/apps/miniprogram/src/pages/appointment/index.scss b/apps/miniprogram/src/pages/appointment/index.scss index ba89678..8f7186e 100644 --- a/apps/miniprogram/src/pages/appointment/index.scss +++ b/apps/miniprogram/src/pages/appointment/index.scss @@ -9,13 +9,13 @@ /* 页头 */ .page-header { background: $card; - padding: 48px 32px 36px; + padding: var(--tk-gap-2xl) var(--tk-gap-xl) 36px; box-shadow: $shadow-sm; } .page-title { @include section-title; - margin-bottom: 4px; + margin-bottom: var(--tk-gap-2xs); font-size: var(--tk-font-num-lg); } @@ -28,15 +28,15 @@ /* 预约列表 */ .appointment-list { - padding: 0 24px; - margin-top: 16px; + padding: 0 var(--tk-page-padding); + margin-top: var(--tk-gap-md); } .appointment-card { background: $card; border-radius: $r; - padding: 28px; - margin-bottom: 20px; + padding: var(--tk-card-padding-lg); + margin-bottom: var(--tk-section-gap); box-shadow: $shadow-sm; } @@ -44,13 +44,13 @@ display: flex; justify-content: space-between; align-items: center; - margin-bottom: 20px; + margin-bottom: var(--tk-section-gap); } .doctor-section { display: flex; align-items: center; - gap: 16px; + gap: var(--tk-gap-md); flex: 1; min-width: 0; } @@ -59,7 +59,7 @@ width: 72px; height: 72px; border-radius: $r; - background: $pri-l; + background: var(--tk-pri-l); @include flex-center; flex-shrink: 0; } @@ -68,13 +68,13 @@ font-family: 'Georgia', 'Times New Roman', serif; font-size: var(--tk-font-num); font-weight: bold; - color: $pri; + color: var(--tk-pri); } .doctor-info { display: flex; flex-direction: column; - gap: 6px; + gap: var(--tk-gap-2xs); min-width: 0; } @@ -88,13 +88,13 @@ } .dept-tag { - @include tag($pri-l, $pri); + @include tag(var(--tk-pri-l), var(--tk-pri)); } .dept-tag-text { font-size: var(--tk-font-body); font-weight: 500; - color: $pri; + color: var(--tk-pri); } .status-tag { @@ -117,8 +117,8 @@ } &.tag-completed { - background: $pri-l; - .status-tag-text { color: $pri; } + background: var(--tk-pri-l); + .status-tag-text { color: var(--tk-pri); } } } @@ -130,7 +130,7 @@ .card-divider { height: 1px; background: $bd-l; - margin-bottom: 20px; + margin-bottom: var(--tk-section-gap); } .card-bottom { @@ -142,7 +142,7 @@ .info-row { display: flex; align-items: center; - gap: 12px; + gap: var(--tk-gap-sm); } .info-icon-wrap { @@ -177,8 +177,8 @@ bottom: 60px; left: 50%; transform: translateX(-50%); - background: $pri; - padding: 24px 72px; + background: var(--tk-pri); + padding: var(--tk-gap-lg) 72px; border-radius: $r-pill; box-shadow: 0 8px 24px rgba($pri, 0.3); z-index: 100; diff --git a/apps/miniprogram/src/pages/article/detail/index.scss b/apps/miniprogram/src/pages/article/detail/index.scss index 88f7ac2..fe7eed5 100644 --- a/apps/miniprogram/src/pages/article/detail/index.scss +++ b/apps/miniprogram/src/pages/article/detail/index.scss @@ -5,7 +5,7 @@ .article-header { background: $card; - padding: 32px; + padding: var(--tk-gap-xl); margin-bottom: 2px; } @@ -15,21 +15,21 @@ color: $tx; display: block; line-height: 1.4; - margin-bottom: 16px; + margin-bottom: var(--tk-gap-md); } .article-meta { display: flex; align-items: center; - gap: 16px; + gap: var(--tk-gap-md); flex-wrap: wrap; } .article-category { font-size: var(--tk-font-body); - color: $pri; - background: $pri-l; - padding: 4px 12px; + color: var(--tk-pri); + background: var(--tk-pri-l); + padding: var(--tk-gap-2xs) var(--tk-gap-sm); border-radius: $r-sm; } @@ -45,7 +45,7 @@ .article-summary { background: $card; - padding: 24px 32px; + padding: var(--tk-gap-lg) var(--tk-gap-xl); margin-bottom: 2px; } @@ -57,26 +57,26 @@ .article-content { background: $card; - padding: 32px; + padding: var(--tk-gap-xl); // RichText 内部样式优化 h1, h2, h3 { font-weight: bold; color: $tx; - margin: 24px 0 12px; + margin: var(--tk-gap-lg) 0 var(--tk-gap-sm); } p { font-size: var(--tk-font-body-lg); color: $tx; line-height: 1.8; - margin-bottom: 16px; + margin-bottom: var(--tk-gap-md); } img { max-width: 100%; border-radius: $r-sm; - margin: 12px 0; + margin: var(--tk-gap-sm) 0; } } @@ -85,7 +85,7 @@ display: flex; justify-content: center; align-items: center; - padding: 120px 0; + padding: var(--tk-gap-2xl) 0; } .loading-text, diff --git a/apps/miniprogram/src/pages/article/index.scss b/apps/miniprogram/src/pages/article/index.scss index 43d171f..55f65d5 100644 --- a/apps/miniprogram/src/pages/article/index.scss +++ b/apps/miniprogram/src/pages/article/index.scss @@ -8,13 +8,13 @@ /* ─── 分类筛选滚动条(页面特有) ─── */ .article-categories { white-space: nowrap; - margin-bottom: 24px; + margin-bottom: var(--tk-gap-lg); } .article-cat-tab { display: inline-block; - padding: 12px 28px; - margin-right: 12px; + padding: var(--tk-gap-sm) var(--tk-card-padding-lg); + margin-right: var(--tk-gap-sm); font-size: var(--tk-font-h1); color: $tx2; background: $card; @@ -22,9 +22,9 @@ border: 2px solid transparent; &--active { - color: $pri; - background: $pri-l; - border-color: $pri; + color: var(--tk-pri); + background: var(--tk-pri-l); + border-color: var(--tk-pri); font-weight: bold; } } @@ -33,7 +33,7 @@ .article-list { display: flex; flex-direction: column; - gap: 20px; + gap: var(--tk-section-gap); } /* ─── 文章卡片内容(ContentCard 已接管外层卡片样式) ─── */ @@ -41,7 +41,7 @@ flex: 1; display: flex; flex-direction: column; - margin-right: 20px; + margin-right: var(--tk-section-gap); } .article-card-title { @@ -49,7 +49,7 @@ font-weight: bold; color: $tx; line-height: 1.4; - margin-bottom: 8px; + margin-bottom: var(--tk-gap-xs); overflow: hidden; text-overflow: ellipsis; display: -webkit-box; @@ -62,7 +62,7 @@ color: $tx2; line-height: 1.4; display: block; - margin-bottom: 12px; + margin-bottom: var(--tk-gap-sm); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; @@ -71,14 +71,14 @@ .article-card-meta { display: flex; align-items: center; - gap: 12px; + gap: var(--tk-gap-sm); } .article-card-tag { font-size: var(--tk-font-body); - color: $pri; - background: $pri-l; - padding: 2px 12px; + color: var(--tk-pri); + background: var(--tk-pri-l); + padding: 2px var(--tk-gap-sm); border-radius: $r-sm; } diff --git a/apps/miniprogram/src/pages/consultation/index.scss b/apps/miniprogram/src/pages/consultation/index.scss index c803e22..31a01e7 100644 --- a/apps/miniprogram/src/pages/consultation/index.scss +++ b/apps/miniprogram/src/pages/consultation/index.scss @@ -13,20 +13,20 @@ font-size: var(--tk-font-cap); color: var(--tk-text-secondary); display: block; - margin-bottom: 20px; + margin-bottom: var(--tk-section-gap); } /* ─── 发起咨询按钮(页面特有) ─── */ .consultation-create-btn { height: 48px; border-radius: $r; - background: $pri; + background: var(--tk-pri); @include flex-center; - box-shadow: 0 2px 8px rgba(196, 98, 58, 0.25); - margin-bottom: 20px; + box-shadow: 0 2px 8px rgba($pri, 0.25); + margin-bottom: var(--tk-section-gap); &:active { - opacity: 0.85; + opacity: var(--tk-touch-feedback-opacity); } } @@ -40,7 +40,7 @@ .session-list { display: flex; flex-direction: column; - gap: 8px; + gap: var(--tk-gap-xs); } /* ─── 已关闭会话半透明(ContentCard 已接管卡片外层) ─── */ @@ -52,14 +52,14 @@ .session-inner { display: flex; align-items: center; - gap: 12px; + gap: var(--tk-gap-sm); } .session-avatar { width: 36px; height: 36px; border-radius: $r-lg; - background: $pri-l; + background: var(--tk-pri-l); @include flex-center; flex-shrink: 0; } @@ -68,7 +68,7 @@ @include serif-number; font-size: var(--tk-font-body-sm); font-weight: 700; - color: $pri; + color: var(--tk-pri); } .session-body { @@ -80,7 +80,7 @@ display: flex; align-items: center; justify-content: space-between; - margin-bottom: 6px; + margin-bottom: var(--tk-gap-2xs); } .session-subject { @@ -91,7 +91,7 @@ text-overflow: ellipsis; white-space: nowrap; flex: 1; - margin-right: 8px; + margin-right: var(--tk-gap-xs); } .session-time { @@ -103,7 +103,7 @@ .session-meta { display: flex; align-items: center; - margin-bottom: 4px; + margin-bottom: var(--tk-gap-2xs); } .session-message-row { @@ -119,7 +119,7 @@ text-overflow: ellipsis; white-space: nowrap; flex: 1; - margin-right: 8px; + margin-right: var(--tk-gap-xs); } /* ─── 未读角标(页面特有) ─── */ diff --git a/apps/miniprogram/src/pages/health/index.scss b/apps/miniprogram/src/pages/health/index.scss index 52992da..0c81d8b 100644 --- a/apps/miniprogram/src/pages/health/index.scss +++ b/apps/miniprogram/src/pages/health/index.scss @@ -2,12 +2,12 @@ @import '../../styles/mixins.scss'; .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 { - margin-bottom: 20px; + margin-bottom: var(--tk-section-gap); } .health-title { @@ -19,18 +19,18 @@ /* ─── 录入区 ─── */ .input-section { - margin-bottom: 20px; + margin-bottom: var(--tk-section-gap); } .input-group { - margin-bottom: 12px; + margin-bottom: var(--tk-gap-sm); } .input-label { font-size: var(--tk-font-cap); color: var(--tk-text-secondary); display: block; - margin-bottom: 4px; + margin-bottom: var(--tk-gap-2xs); } .input-field { @@ -38,7 +38,7 @@ background: $bg; border: 2px solid $bd; border-radius: $r-sm; - padding: 0 16px; + padding: 0 var(--tk-gap-md); font-family: 'Georgia', 'Times New Roman', serif; font-size: var(--tk-font-body-lg); font-weight: 600; @@ -51,19 +51,19 @@ font-size: var(--tk-font-cap); color: var(--tk-text-secondary); display: block; - margin-top: 8px; - margin-bottom: 4px; + margin-top: var(--tk-gap-xs); + margin-bottom: var(--tk-gap-2xs); } .input-label--secondary { - margin-top: 20px; + margin-top: var(--tk-section-gap); } /* ─── 血糖时段选择 ─── */ .period-group { display: flex; - gap: 8px; - margin-top: 12px; + gap: var(--tk-gap-xs); + margin-top: var(--tk-gap-sm); } .period-btn { @@ -74,7 +74,7 @@ @include flex-center; &.period-active { - background: $pri; + background: var(--tk-pri); .period-btn-text { color: $white; @@ -82,7 +82,7 @@ } &:active { - opacity: 0.85; + opacity: var(--tk-touch-feedback-opacity); } } @@ -97,13 +97,13 @@ width: 100%; height: 52px; border-radius: $r-sm; - background: $pri; + background: var(--tk-pri); @include flex-center; - margin-top: 20px; - box-shadow: 0 2px 8px rgba(196, 98, 58, 0.25); + margin-top: var(--tk-section-gap); + box-shadow: 0 2px 8px rgba($pri, 0.25); &:active { - opacity: 0.85; + opacity: var(--tk-touch-feedback-opacity); } } @@ -115,7 +115,7 @@ /* ─── 趋势图 ─── */ .trend-section { - margin-bottom: 12px; + margin-bottom: var(--tk-gap-sm); } .section-title { @@ -132,7 +132,7 @@ } .trend-chart { - padding: 16px; + padding: var(--tk-gap-md); } .trend-bars { @@ -141,7 +141,7 @@ height: 120px; background: $bg; border-radius: $r-sm; - padding: 12px 8px; + padding: var(--tk-gap-sm) var(--tk-gap-xs); gap: 0; position: relative; } @@ -180,7 +180,7 @@ opacity: 0.8; &.trend-bar-normal { - background: $pri; + background: var(--tk-pri); } &.trend-bar-warn { @@ -191,21 +191,21 @@ .trend-bar-label { font-size: var(--tk-font-micro); color: var(--tk-text-secondary); - margin-top: 6px; + margin-top: var(--tk-gap-2xs); } /* ─── BLE 设备卡片 ─── */ .device-section { - margin-bottom: 12px; + margin-bottom: var(--tk-gap-sm); } .device-card { display: flex; align-items: center; - gap: 12px; + gap: var(--tk-gap-sm); &:active { - opacity: 0.85; + opacity: var(--tk-touch-feedback-opacity); } } @@ -213,7 +213,7 @@ width: 48px; height: 48px; border-radius: $r-sm; - background: $pri-l; + background: var(--tk-pri-l); @include flex-center; flex-shrink: 0; } @@ -249,7 +249,7 @@ /* ─── 健康资讯入口 ─── */ .article-entry { &:active { - opacity: 0.85; + opacity: var(--tk-touch-feedback-opacity); } } @@ -263,8 +263,8 @@ .ai-suggestion-card { background: $acc-l; border-radius: $r; - padding: 16px; - margin-bottom: 20px; + padding: var(--tk-gap-md); + margin-bottom: var(--tk-section-gap); box-shadow: none; border-left: 4px solid $acc; } @@ -273,7 +273,7 @@ display: flex; justify-content: space-between; align-items: center; - margin-bottom: 8px; + margin-bottom: var(--tk-gap-xs); } .ai-card-title { @@ -291,8 +291,8 @@ .ai-suggestion-item { display: flex; align-items: center; - gap: 8px; - padding: 6px 0; + gap: var(--tk-gap-xs); + padding: var(--tk-gap-2xs) 0; } .ai-risk-dot { diff --git a/apps/miniprogram/src/pages/index/index.scss b/apps/miniprogram/src/pages/index/index.scss index c7f1d32..ace2c8a 100644 --- a/apps/miniprogram/src/pages/index/index.scss +++ b/apps/miniprogram/src/pages/index/index.scss @@ -6,7 +6,7 @@ ═══════════════════════════════════════ */ .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; color: $tx; display: block; - margin-bottom: 4px; + margin-bottom: var(--tk-gap-2xs); } .greeting-date { @@ -40,18 +40,18 @@ width: 48px; height: 48px; border-radius: $r-pill; - background: $pri-l; + background: var(--tk-pri-l); @include flex-center; flex-shrink: 0; &:active { - opacity: 0.7; + opacity: var(--tk-touch-feedback-opacity); } } .greeting-bell-icon { font-size: var(--tk-font-body-sm); - color: $pri-d; + color: var(--tk-pri-d); } .greeting-bell-dot { @@ -66,13 +66,13 @@ /* ─── 今日体征进度 ─── */ .checkin-card { - margin-bottom: 16px; + margin-bottom: var(--tk-gap-md); display: flex; align-items: center; - gap: 16px; + gap: var(--tk-gap-md); &:active { - opacity: 0.9; + opacity: var(--tk-touch-feedback-opacity); } } @@ -91,18 +91,18 @@ font-weight: 600; color: $tx; display: block; - margin-bottom: 8px; + margin-bottom: var(--tk-gap-xs); } .checkin-capsules { display: flex; flex-wrap: wrap; - gap: 6px; + gap: var(--tk-gap-2xs); } .capsule { font-size: var(--tk-font-micro); - padding: 3px 8px; + padding: 3px var(--tk-gap-xs); border-radius: $r-pill; font-weight: 500; @@ -117,9 +117,9 @@ } } -/* ─── 今日体征 2x2 ─── */ +/* ─── 今日体征 2x2(原型 padding:14px 16px, gap:10)─── */ .vitals-section { - margin-bottom: 16px; + margin-bottom: var(--tk-gap-md); } .section-title { @@ -134,7 +134,7 @@ .vital-card { &:active { - opacity: 0.7; + opacity: var(--tk-touch-feedback-opacity); } } @@ -142,13 +142,13 @@ font-size: var(--tk-font-cap); color: $tx2; display: block; - margin-bottom: 6px; + margin-bottom: var(--tk-gap-2xs); } .vital-value-row { display: flex; align-items: baseline; - margin-bottom: 6px; + margin-bottom: var(--tk-gap-2xs); } .vital-value { @@ -173,10 +173,10 @@ .vital-tag { font-size: var(--tk-font-micro); font-weight: 500; - padding: 2px 8px; + padding: 2px var(--tk-gap-xs); border-radius: $r-pill; display: inline-block; - margin-top: 4px; + margin-top: var(--tk-gap-2xs); &.tag-ok { background: $acc-l; @@ -196,10 +196,10 @@ /* ─── 智能提醒卡片 ─── */ .reminder-card { - background: linear-gradient(135deg, $pri 0%, $pri-d 100%); - border-radius: $r; - padding: 18px; - margin-bottom: 16px; + background: linear-gradient(135deg, var(--tk-pri) 0%, var(--tk-pri-d) 100%); + border-radius: var(--tk-card-radius); + padding: var(--tk-gap-md); + margin-bottom: var(--tk-gap-md); color: $white; } @@ -207,7 +207,7 @@ display: flex; justify-content: space-between; align-items: center; - margin-bottom: 10px; + margin-bottom: var(--tk-gap-sm); } .reminder-title { @@ -225,11 +225,11 @@ .reminder-item { display: flex; align-items: center; - gap: 8px; - padding: 8px 0; + gap: var(--tk-gap-xs); + padding: var(--tk-gap-xs) 0; &:active { - opacity: 0.8; + opacity: var(--tk-touch-feedback-opacity); } } @@ -239,7 +239,7 @@ .reminder-tag { font-size: var(--tk-font-micro); - padding: 2px 6px; + padding: 2px var(--tk-gap-2xs); border-radius: $r-xs; background: rgba(255, 255, 255, 0.2); font-weight: 500; @@ -262,34 +262,34 @@ flex-shrink: 0; } -/* ─── 快捷操作 ─── */ +/* ─── 快捷操作(原型 gap:10, height:52, borderRadius:14)─── */ .action-section { display: flex; gap: 10px; - margin-top: 8px; + margin-top: var(--tk-gap-xs); } .action-btn { flex: 1; - height: 52px; - border-radius: $r-sm; + height: var(--tk-btn-primary-h); + border-radius: 14px; @include flex-center; &:active { - opacity: 0.85; + opacity: var(--tk-touch-feedback-opacity); } } .action-primary { - background: $pri; + background: var(--tk-pri); color: $white; - box-shadow: 0 2px 8px rgba(196, 98, 58, 0.25); + box-shadow: var(--tk-shadow-tab); } .action-outline { background: transparent; - color: $pri; - border: 2px solid $pri; + color: var(--tk-pri); + border: 2px solid var(--tk-pri); } .action-btn-text { @@ -302,13 +302,13 @@ ═══════════════════════════════════════ */ .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 { width: 100%; - height: 360px; + height: 400px; } .guest-slide { @@ -323,7 +323,7 @@ inset: 0; &--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 { background: linear-gradient(135deg, $acc 0%, $acc-d 60%, $acc-l 100%); @@ -340,7 +340,7 @@ display: flex; flex-direction: column; justify-content: center; - padding: 40px 32px; + padding: var(--tk-gap-2xl) var(--tk-gap-xl); } .guest-slide-title { @@ -349,7 +349,7 @@ font-weight: 700; color: $white; display: block; - margin-bottom: 8px; + margin-bottom: var(--tk-gap-xs); } .guest-slide-desc { @@ -360,7 +360,7 @@ /* ─── 健康资讯 ─── */ .guest-section { - padding: 24px 24px 0; + padding: var(--tk-gap-lg) var(--tk-gap-lg) 0; } .guest-section-title { @@ -369,13 +369,13 @@ font-weight: bold; color: $tx; display: block; - margin-bottom: 16px; + margin-bottom: var(--tk-gap-md); } .guest-articles { display: flex; flex-direction: column; - gap: 12px; + gap: var(--tk-gap-sm); } .guest-article-card { @@ -383,7 +383,7 @@ display: flex; &:active { - opacity: 0.85; + opacity: var(--tk-touch-feedback-opacity); } } @@ -394,7 +394,7 @@ } .guest-article-body { - padding: 12px 14px; + padding: var(--tk-gap-sm); flex: 1; min-width: 0; } @@ -404,7 +404,7 @@ font-weight: 600; color: $tx; display: block; - margin-bottom: 4px; + margin-bottom: var(--tk-gap-2xs); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; @@ -420,7 +420,7 @@ } .guest-empty { - padding: 40px 0; + padding: var(--tk-gap-2xl) 0; text-align: center; } @@ -431,10 +431,10 @@ /* ─── 底部登录引导 ─── */ .guest-login-prompt { - margin: 24px 24px 0; + margin: var(--tk-gap-lg) var(--tk-gap-lg) 0; display: flex; align-items: center; - gap: 16px; + gap: var(--tk-gap-md); } .guest-login-text { @@ -444,15 +444,15 @@ } .guest-login-btn { - height: 56px; - padding: 0 28px; - background: $pri; + height: var(--tk-input-height); + padding: 0 var(--tk-card-padding-lg); + background: var(--tk-pri); border-radius: $r-pill; @include flex-center; flex-shrink: 0; &:active { - opacity: 0.85; + opacity: var(--tk-touch-feedback-opacity); } } diff --git a/apps/miniprogram/src/pages/index/index.tsx b/apps/miniprogram/src/pages/index/index.tsx index cd4b836..4d5bcdf 100644 --- a/apps/miniprogram/src/pages/index/index.tsx +++ b/apps/miniprogram/src/pages/index/index.tsx @@ -8,7 +8,7 @@ import { usePageData } from '@/hooks/usePageData'; import { useThrottledDidShow } from '@/hooks/useThrottledDidShow'; import { api } from '@/services/request'; import type { Article } from '@/services/article'; -import ProgressRing from '../../components/ProgressRing'; +import ProgressRing from '@/components/ui/ProgressRing'; import Loading from '../../components/Loading'; import PageShell from '@/components/ui/PageShell'; import ContentCard from '@/components/ui/ContentCard'; @@ -202,7 +202,7 @@ function HomeDashboard({ modeClass }: { modeClass: string }) { Taro.switchTab({ url: '/pages/health/index' })}> - + diff --git a/apps/miniprogram/src/pages/login/index.scss b/apps/miniprogram/src/pages/login/index.scss index 127a80a..6a01e33 100644 --- a/apps/miniprogram/src/pages/login/index.scss +++ b/apps/miniprogram/src/pages/login/index.scss @@ -1,12 +1,15 @@ @import '../../styles/variables.scss'; @import '../../styles/mixins.scss'; +// 登录页使用原型精确数值,不走 design token +// 原型参考:docs/design/mp-01-login.html + .login-page { - // PageShell 接管 min-height, background, scroll + min-height: 100vh; + background: $bg; display: flex; flex-direction: column; - align-items: center; - padding: 100px 40px 60px; + padding: 80px 28px 40px; } /* ─── 品牌区 ─── */ @@ -14,70 +17,155 @@ display: flex; flex-direction: column; align-items: center; - margin-bottom: 48px; + margin-bottom: 56px; } .login-logo { - width: 96px; - height: 96px; - border-radius: $r-lg; - background: $pri; + width: 80px; + height: 80px; + border-radius: 40px; + background: linear-gradient(135deg, var(--tk-pri-l) 0%, var(--tk-pri) 100%); @include flex-center; - margin-bottom: 24px; - box-shadow: 0 8px 24px rgba($pri, 0.3); + margin-bottom: 20px; + box-shadow: 0 4px 16px rgba($pri, 0.25); } -.login-logo-mark { +.login-logo-letter { font-family: 'Georgia', 'Times New Roman', serif; - font-size: var(--tk-font-hero); + font-size: 36px; + font-weight: 700; color: $white; - font-weight: bold; line-height: 1; } .login-title { font-family: 'Georgia', 'Times New Roman', serif; - font-size: var(--tk-font-num); + font-size: 28px; + font-weight: 700; color: $tx; - font-weight: bold; margin-bottom: 8px; } .login-subtitle { - font-size: var(--tk-font-body-sm); - color: $tx2; - letter-spacing: 0.05em; + font-size: 14px; + color: $tx3; } -/* ─── 装饰线 ─── */ -.login-divider { - width: 48px; - margin-bottom: 40px; +/* ─── 输入框 ─── */ +.login-field { + height: 56px; + background: $card; + border: 1.5px solid $bd; + border-radius: 16px; + display: flex; + align-items: center; + padding: 0 16px; + margin-bottom: 12px; } -.login-divider-line { - height: 3px; - background: $pri; - border-radius: $r-xs; - opacity: 0.4; +.login-input { + flex: 1; + height: 100%; + font-size: 16px; + 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%; } -.login-btn { +.login-btn-bind { width: 100%; - height: $btn-primary-h; - background: $pri; + height: 54px; + background: var(--tk-pri); color: $white; - font-size: var(--tk-font-body-lg); + font-size: 18px; font-weight: 600; - border-radius: $r; + border-radius: 16px; border: none; @include flex-center; - letter-spacing: 0.04em; box-shadow: 0 4px 16px rgba($pri, 0.25); padding: 0; line-height: 1; @@ -85,70 +173,62 @@ &::after { border: none; } - - &--dev { - margin-top: 16px; - background: $wrn; - box-shadow: 0 4px 16px rgba($wrn, 0.2); - } - - &:active { - opacity: 0.85; - } } /* ─── 协议 ─── */ .agreement-row { display: flex; align-items: flex-start; - margin-top: 28px; gap: 10px; - width: 100%; } .agreement-check { - width: 28px; - height: 28px; - border: 2px solid $bd; - border-radius: $r-sm; + width: 24px; + height: 24px; + border: 1.5px solid $bd; + border-radius: 6px; @include flex-center; flex-shrink: 0; - margin-top: 2px; - transition: all 0.2s; + margin-top: 1px; &.checked { - background: $pri; - border-color: $pri; + background: var(--tk-pri); + border-color: var(--tk-pri); } } .agreement-check-mark { - font-size: var(--tk-font-body-sm); + font-size: 14px; color: $white; font-weight: bold; line-height: 1; } .agreement-text { - font-size: var(--tk-font-cap); - color: $tx2; - line-height: 1.7; + font-size: 12px; + color: $tx3; + line-height: 1.8; } .agreement-link { - color: $pri; + color: var(--tk-pri); font-weight: 500; } -/* ─── 暂不登录 ─── */ -.skip-row { - width: 100%; +/* ─── 开发模式 ─── */ +.login-dev-btn { 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 { - font-size: var(--tk-font-body-sm); - color: var(--tk-text-secondary); - padding: 8px 16px; +.login-dev-btn-text { + font-size: 13px; + color: $tx3; } diff --git a/apps/miniprogram/src/pages/login/index.tsx b/apps/miniprogram/src/pages/login/index.tsx index 8b1e3a7..9c1f215 100644 --- a/apps/miniprogram/src/pages/login/index.tsx +++ b/apps/miniprogram/src/pages/login/index.tsx @@ -1,28 +1,25 @@ 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 { useAuthStore } from '../../stores/auth'; -import { useElderClass } from '../../hooks/useElderClass'; -import PageShell from '@/components/ui/PageShell'; import './index.scss'; const IS_DEV = process.env.NODE_ENV !== 'production'; - -// 运行时检测是否在 DevTools 模拟器中(弥补编译时 IS_DEV 在 production 构建中为 false 的问题) const IS_SIMULATOR = typeof __wxConfig !== 'undefined' && (__wxConfig as any).envVersion !== 'release'; export default function Login() { - const modeClass = useElderClass(); - const [needBind, setNeedBind] = useState(false); + const [username, setUsername] = useState(''); + const [password, setPassword] = useState(''); + const [showPassword, setShowPassword] = 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 bindPhone = useAuthStore((s) => s.bindPhone); const loading = useAuthStore((s) => s.loading); const isMedicalStaff = useAuthStore((s) => s.isMedicalStaff); - // 登录页不应用关怀模式(正常模式尺寸已足够大) - const loginClass = ''; - const navigateAfterLogin = () => { if (isMedicalStaff()) { 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 () => { if (!agreed) { Taro.showToast({ title: '请先阅读并同意用户协议', icon: 'none' }); @@ -51,24 +73,18 @@ export default function Login() { } }; - /** Dev 模式快速登录:跳过 getPhoneNumber,用 mock 数据直接调用绑定 API */ const handleDevQuickLogin = async () => { try { - const success = await bindPhone('dev_mock_encrypted', 'dev_mock_iv'); + const success = await credentialLogin('admin', 'Admin@2026'); if (success) { navigateAfterLogin(); } } catch (err: any) { - Taro.showToast({ title: err?.message || '绑定失败', icon: 'none' }); - setNeedBind(false); + Taro.showToast({ title: err?.message || '登录失败', icon: 'none' }); } }; 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') { Taro.showToast({ title: '需要授权手机号', icon: 'none' }); return; @@ -80,81 +96,115 @@ export default function Login() { navigateAfterLogin(); } } catch (err: any) { - const msg = err?.message || '绑定失败'; Taro.showModal({ title: '绑定手机号失败', - content: msg, + content: err?.message || '绑定失败', confirmText: '重新登录', cancelText: '取消', - success: (res) => { - if (res.confirm) { - setNeedBind(false); - } - }, + success: (res) => { if (res.confirm) setNeedBind(false); }, }); } }; return ( - - {/* 品牌区 */} - - - + + + {/* 品牌区 */} + + + H + + HMS 健康 + 您的专属健康管家 + + + {!needBind ? ( + <> + {/* 账号输入 */} + + setUsername(e.detail.value)} + /> - 健康管理 - 您的专属健康管家 - - {/* 装饰线 */} - - - - - {/* 登录按钮 */} - - {!needBind ? ( - - ) : ( - <> - - {(IS_DEV || IS_SIMULATOR) && ( - - )} - - )} - - - {/* 协议 */} - - setAgreed(!agreed)}> - {agreed && } + {/* 密码输入 */} + + setPassword(e.detail.value)} + /> + setShowPassword(!showPassword)} + > + {showPassword ? '隐藏' : '显示'} + - - 我已阅读并同意 - Taro.navigateTo({ url: '/pages/legal/user-agreement' })}>《用户服务协议》 - 和 - Taro.navigateTo({ url: '/pages/legal/privacy-policy' })}>《隐私政策》 - - - {/* 暂不登录 */} - - Taro.reLaunch({ url: '/pages/index/index' })}> - 暂不登录,先看看 - + {/* 登录按钮 */} + + {loading ? '登录中...' : '登录'} + + + {/* 分隔线 */} + + + + + + + {/* 微信一键登录 */} + + + 微信一键登录 + + + ) : ( + + - + )} + + {/* 协议 */} + + setAgreed(!agreed)} + > + {agreed && } + + + 登录即同意 + Taro.navigateTo({ url: '/pages/legal/user-agreement' })}>《用户协议》 + 和 + Taro.navigateTo({ url: '/pages/legal/privacy-policy' })}>《隐私政策》 + + + + + + {/* 开发模式 */} + {(IS_DEV || IS_SIMULATOR) && ( + + 开发模式快速登录 › + + )} + ); } diff --git a/apps/miniprogram/src/pages/mall/index.scss b/apps/miniprogram/src/pages/mall/index.scss index 3658a84..b40599e 100644 --- a/apps/miniprogram/src/pages/mall/index.scss +++ b/apps/miniprogram/src/pages/mall/index.scss @@ -2,27 +2,27 @@ @import '../../styles/mixins.scss'; .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 { - background: linear-gradient(135deg, $pri 0%, $pri-d 100%); - padding: 48px 32px 36px; + background: linear-gradient(135deg, var(--tk-pri) 0%, var(--tk-pri-d) 100%); + padding: var(--tk-gap-2xl) var(--tk-gap-xl) var(--tk-gap-xl); } .points-card { background: rgba(255, 255, 255, 0.15); border: 1px solid rgba(255, 255, 255, 0.2); border-radius: $r-lg; - padding: 32px; + padding: var(--tk-gap-xl); } .points-top { display: flex; justify-content: space-between; align-items: center; - margin-bottom: 16px; + margin-bottom: var(--tk-gap-md); } .points-label { @@ -33,12 +33,12 @@ .checkin-btn { background: rgba(255, 255, 255, 0.25); 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; transition: all 0.2s; &:active { - opacity: 0.8; + opacity: var(--tk-touch-feedback-opacity); } &.checked { @@ -63,7 +63,7 @@ font-weight: bold; color: $white; display: block; - margin-bottom: 8px; + margin-bottom: var(--tk-gap-xs); letter-spacing: 2px; line-height: 1; } @@ -77,13 +77,13 @@ /* ─── 商品类型切换 ─── */ .type-tabs { display: flex; - padding: 20px 24px 0; + padding: var(--tk-section-gap) var(--tk-page-padding) 0; } .type-tab { flex: 1; text-align: center; - padding: 16px 0; + padding: var(--tk-gap-md) 0; position: relative; min-height: 48px; @@ -95,7 +95,7 @@ transform: translateX(-50%); width: 48px; height: 4px; - background: $pri; + background: var(--tk-pri); border-radius: $r-xs; } } @@ -105,7 +105,7 @@ color: $tx2; &.active { - color: $pri; + color: var(--tk-pri); font-weight: 600; } } @@ -114,15 +114,15 @@ .product-grid { display: grid; grid-template-columns: 1fr 1fr; - gap: 16px; - padding: 20px 24px; + gap: var(--tk-gap-md); + padding: var(--tk-section-gap) var(--tk-page-padding); } .product-card { overflow: hidden; &:active { - opacity: 0.7; + opacity: var(--tk-touch-feedback-opacity); } } @@ -131,7 +131,7 @@ height: 200px; @include flex-center; - &.type-physical { background: $pri-l; } + &.type-physical { background: var(--tk-pri-l); } &.type-service { background: $acc-l; } &.type-privilege { background: $wrn-l; } } @@ -140,7 +140,7 @@ font-family: 'Georgia', 'Times New Roman', serif; font-size: var(--tk-font-hero); font-weight: bold; - color: $pri; + color: var(--tk-pri); line-height: 1; .type-service & { color: $acc; } @@ -148,7 +148,7 @@ } .product-info { - padding: 20px; + padding: var(--tk-section-gap); } .product-name { @@ -156,7 +156,7 @@ font-weight: 600; color: $tx; display: block; - margin-bottom: 12px; + margin-bottom: var(--tk-gap-sm); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; @@ -171,7 +171,7 @@ .product-points { display: flex; align-items: center; - gap: 4px; + gap: var(--tk-gap-2xs); } .product-points-char { @@ -190,7 +190,7 @@ .product-stock { font-size: var(--tk-font-body); - padding: 2px 10px; + padding: 2px var(--tk-gap-sm); border-radius: $r-sm; &.out { diff --git a/apps/miniprogram/src/pages/messages/index.scss b/apps/miniprogram/src/pages/messages/index.scss index e901355..4f73c1e 100644 --- a/apps/miniprogram/src/pages/messages/index.scss +++ b/apps/miniprogram/src/pages/messages/index.scss @@ -3,13 +3,13 @@ .messages-page { // PageShell 接管 min-height, background - padding: 20px 24px 100px; - padding-bottom: calc(100px + env(safe-area-inset-bottom)); + padding: var(--tk-section-gap) var(--tk-page-padding) var(--tk-tabbar-space); + padding-bottom: calc(var(--tk-tabbar-space) + env(safe-area-inset-bottom)); } /* ─── 页头 ─── */ .messages-header { - margin-bottom: 20px; + margin-bottom: var(--tk-section-gap); } .messages-title { @@ -26,7 +26,7 @@ background: $surface-alt; border-radius: $r-sm; padding: 3px; - margin-bottom: 12px; + margin-bottom: var(--tk-gap-sm); } .msg-segment-tab { @@ -37,7 +37,7 @@ position: relative; &:active { - opacity: 0.85; + opacity: var(--tk-touch-feedback-opacity); } } @@ -82,13 +82,13 @@ .msg-list { display: flex; flex-direction: column; - gap: 8px; + gap: var(--tk-gap-xs); } /* ─── 咨询卡片 ─── */ .consult-card { display: flex; - gap: 12px; + gap: var(--tk-gap-sm); align-items: center; // ContentCard 接管 background, border-radius, padding, box-shadow, active feedback } @@ -107,7 +107,7 @@ } .consult-avatar-active { - background: $pri-l; + background: var(--tk-pri-l); } .consult-avatar-char { @@ -118,7 +118,7 @@ } .consult-avatar-active .consult-avatar-char { - color: $pri; + color: var(--tk-pri); } .consult-body { @@ -130,7 +130,7 @@ display: flex; justify-content: space-between; align-items: center; - margin-bottom: 4px; + margin-bottom: var(--tk-gap-2xs); &:last-child { margin-bottom: 0; @@ -156,7 +156,7 @@ text-overflow: ellipsis; white-space: nowrap; flex: 1; - margin-right: 8px; + margin-right: var(--tk-gap-xs); } .consult-badge { @@ -178,7 +178,7 @@ /* ─── 通知卡片 ─── */ .notify-card { display: flex; - gap: 12px; + gap: var(--tk-gap-sm); align-items: flex-start; // ContentCard 接管 background, border-radius, padding, box-shadow } @@ -203,8 +203,8 @@ .notify-type-appointment, .notify-type-points { - background: $pri-l; - color: $pri; + background: var(--tk-pri-l); + color: var(--tk-pri); } .notify-type-alert { @@ -227,7 +227,7 @@ display: flex; justify-content: space-between; align-items: center; - margin-bottom: 4px; + margin-bottom: var(--tk-gap-2xs); } .notify-title { @@ -244,7 +244,7 @@ font-size: var(--tk-font-micro); color: var(--tk-text-secondary); flex-shrink: 0; - margin-left: 8px; + margin-left: var(--tk-gap-xs); } .notify-desc { @@ -257,7 +257,7 @@ width: 8px; height: 8px; border-radius: $r-xs; - background: $pri; + background: var(--tk-pri); flex-shrink: 0; - margin-top: 6px; + margin-top: var(--tk-gap-2xs); } diff --git a/apps/miniprogram/src/pages/pkg-consultation/detail/index.scss b/apps/miniprogram/src/pages/pkg-consultation/detail/index.scss index a5cbea6..d5c8d30 100644 --- a/apps/miniprogram/src/pages/pkg-consultation/detail/index.scss +++ b/apps/miniprogram/src/pages/pkg-consultation/detail/index.scss @@ -12,7 +12,7 @@ .chat-header { display: flex; align-items: center; - padding: 12px 16px; + padding: var(--tk-gap-sm) var(--tk-gap-md); background: $card; border-bottom: 1px solid $bd-l; flex-shrink: 0; @@ -25,13 +25,13 @@ z-index: 1; &:active { - opacity: 0.7; + opacity: var(--tk-touch-feedback-opacity); } } .chat-header__back-text { font-size: var(--tk-font-body-sm); - color: $pri; + color: var(--tk-pri); } .chat-header__center { @@ -60,14 +60,14 @@ /* ─── 消息区 ─── */ .chat-messages { flex: 1; - padding: 16px 16px 0; + padding: var(--tk-gap-md) var(--tk-gap-md) 0; overflow-y: auto; } .msg-row { display: flex; - margin-bottom: 16px; - gap: 8px; + margin-bottom: var(--tk-gap-md); + gap: var(--tk-gap-xs); &--self { justify-content: flex-end; @@ -79,7 +79,7 @@ width: 32px; height: 32px; border-radius: $r; - background: $pri-l; + background: var(--tk-pri-l); @include flex-center; flex-shrink: 0; } @@ -88,13 +88,13 @@ @include serif-number; font-size: var(--tk-font-cap); font-weight: 700; - color: $pri; + color: var(--tk-pri); } /* ─── 消息气泡 ─── */ .msg-bubble { max-width: 70%; - padding: 12px 16px; + padding: var(--tk-gap-sm) var(--tk-gap-md); box-shadow: $shadow-sm; &--other { @@ -103,7 +103,7 @@ } &--self { - background: $pri; + background: var(--tk-pri); border-radius: $r $r $r-xs $r; } } @@ -123,7 +123,7 @@ .msg-date-divider { display: flex; justify-content: center; - padding: 12px 0; + padding: var(--tk-gap-sm) 0; &__text { font-size: var(--tk-font-micro); @@ -137,7 +137,7 @@ .msg-truncated-hint { display: flex; justify-content: center; - padding: 12px 0; + padding: var(--tk-gap-sm) 0; &__text { font-size: var(--tk-font-micro); @@ -151,14 +151,14 @@ .msg-image { width: 200px; border-radius: $r-sm; - margin-top: 4px; + margin-top: var(--tk-gap-2xs); } .msg-time { font-size: var(--tk-font-micro); color: var(--tk-text-secondary); display: block; - margin-top: 4px; + margin-top: var(--tk-gap-2xs); .msg-bubble--self & { color: rgba(255, 255, 255, 0.7); @@ -168,7 +168,7 @@ .chat-empty { text-align: center; - padding: 80px 24px; + padding: 80px var(--tk-page-padding); &__text { font-size: var(--tk-font-cap); @@ -202,10 +202,10 @@ width: 48px; height: 48px; border-radius: $r-lg; - background: $pri; + background: var(--tk-pri); @include flex-center; flex-shrink: 0; - box-shadow: 0 2px 6px rgba(196, 98, 58, 0.3); + box-shadow: 0 2px 6px rgba($pri, 0.3); &--disabled { opacity: 0.5; @@ -219,7 +219,7 @@ } .chat-closed-bar { - padding: 16px; + padding: var(--tk-gap-md); text-align: center; background: $card; border-top: 1px solid $bd-l; diff --git a/apps/miniprogram/src/pages/pkg-doctor-clinical/alerts/detail/index.scss b/apps/miniprogram/src/pages/pkg-doctor-clinical/alerts/detail/index.scss index c99dc0c..f52c510 100644 --- a/apps/miniprogram/src/pages/pkg-doctor-clinical/alerts/detail/index.scss +++ b/apps/miniprogram/src/pages/pkg-doctor-clinical/alerts/detail/index.scss @@ -2,12 +2,12 @@ @import '../../../../styles/mixins.scss'; .alert-detail-header { - margin-bottom: 24px; + margin-bottom: var(--tk-gap-lg); &__tags { display: flex; - gap: 12px; - margin-bottom: 12px; + gap: var(--tk-gap-sm); + margin-bottom: var(--tk-gap-sm); } &__time { @@ -19,7 +19,7 @@ .detail-severity { font-size: var(--tk-font-h2); font-weight: 600; - padding: 6px 16px; + padding: var(--tk-gap-2xs) var(--tk-gap-md); border-radius: $r-sm; &--info { @@ -45,7 +45,7 @@ .detail-status { font-size: var(--tk-font-h2); - padding: 6px 16px; + padding: var(--tk-gap-2xs) var(--tk-gap-md); border-radius: $r-sm; &--pending { @@ -54,8 +54,8 @@ } &--acknowledged { - background: $pri-l; - color: $pri; + background: var(--tk-pri-l); + color: var(--tk-pri); } &--resolved { @@ -73,7 +73,7 @@ &__label { font-size: var(--tk-font-h2); color: $tx2; - margin-bottom: 8px; + margin-bottom: var(--tk-gap-xs); } &__value { @@ -94,18 +94,18 @@ line-height: 1.6; white-space: pre-wrap; background: $bg; - padding: 16px; + padding: var(--tk-gap-md); border-radius: $r; - margin-top: 8px; + margin-top: var(--tk-gap-xs); } } } .alert-detail-actions { display: flex; - gap: 16px; - margin-top: 32px; - padding: 0 8px; + gap: var(--tk-gap-md); + margin-top: var(--tk-gap-xl); + padding: 0 var(--tk-gap-xs); } .alert-action-btn { @@ -118,7 +118,7 @@ text-align: center; &--primary { - background: $pri; + background: var(--tk-pri); color: $card; border: none; diff --git a/apps/miniprogram/src/pages/pkg-doctor-clinical/alerts/detail/index.tsx b/apps/miniprogram/src/pages/pkg-doctor-clinical/alerts/detail/index.tsx index d0c4b2c..7e03921 100644 --- a/apps/miniprogram/src/pages/pkg-doctor-clinical/alerts/detail/index.tsx +++ b/apps/miniprogram/src/pages/pkg-doctor-clinical/alerts/detail/index.tsx @@ -9,7 +9,7 @@ import { import Loading from '@/components/Loading'; import PageShell from '@/components/ui/PageShell'; import ContentCard from '@/components/ui/ContentCard'; -import { useElderClass } from '../../../../hooks/useElderClass'; +import { useDoctorClass } from '@/hooks/useDoctorClass'; import './index.scss'; const SEVERITY_MAP: Record = { @@ -27,7 +27,7 @@ const STATUS_MAP: Record = { }; export default function AlertDetail() { - const modeClass = useElderClass(); + const modeClass = useDoctorClass(); const [alert, setAlert] = useState(null); const [loading, setLoading] = useState(true); const [actionLoading, setActionLoading] = useState(false); diff --git a/apps/miniprogram/src/pages/pkg-doctor-clinical/alerts/index.scss b/apps/miniprogram/src/pages/pkg-doctor-clinical/alerts/index.scss index c0b7939..ebc06ad 100644 --- a/apps/miniprogram/src/pages/pkg-doctor-clinical/alerts/index.scss +++ b/apps/miniprogram/src/pages/pkg-doctor-clinical/alerts/index.scss @@ -10,7 +10,7 @@ display: flex; justify-content: space-between; align-items: center; - margin-bottom: 16px; + margin-bottom: var(--tk-gap-md); } .alert-list-title { @@ -51,14 +51,14 @@ display: flex; justify-content: space-between; align-items: center; - margin-bottom: 12px; + margin-bottom: var(--tk-gap-sm); } .alert-card__title { font-size: var(--tk-font-body-lg); font-weight: 500; color: $tx; - margin-bottom: 8px; + margin-bottom: var(--tk-gap-xs); } .alert-card__footer { diff --git a/apps/miniprogram/src/pages/pkg-doctor-clinical/alerts/index.tsx b/apps/miniprogram/src/pages/pkg-doctor-clinical/alerts/index.tsx index b3d0dfb..594740c 100644 --- a/apps/miniprogram/src/pages/pkg-doctor-clinical/alerts/index.tsx +++ b/apps/miniprogram/src/pages/pkg-doctor-clinical/alerts/index.tsx @@ -11,7 +11,7 @@ import PaginationBar from '@/components/patterns/PaginationBar'; import SearchSection from '@/components/patterns/SearchSection'; import ErrorState from '@/components/ErrorState'; import EmptyState from '@/components/EmptyState'; -import { useElderClass } from '../../../hooks/useElderClass'; +import { useDoctorClass } from '@/hooks/useDoctorClass'; import { safeNavigateTo } from '@/utils/navigate'; import './index.scss'; @@ -51,7 +51,7 @@ const STATUS_FILTERS = [ ]; export default function AlertList() { - const modeClass = useElderClass(); + const modeClass = useDoctorClass(); const [alerts, setAlerts] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(false); diff --git a/apps/miniprogram/src/pages/pkg-doctor-clinical/dialysis/create/index.scss b/apps/miniprogram/src/pages/pkg-doctor-clinical/dialysis/create/index.scss index ecdfe18..f3373ea 100644 --- a/apps/miniprogram/src/pages/pkg-doctor-clinical/dialysis/create/index.scss +++ b/apps/miniprogram/src/pages/pkg-doctor-clinical/dialysis/create/index.scss @@ -6,7 +6,7 @@ /* ─── 表单分组间距(ContentCard 外层补充) ─── */ .section { - margin-bottom: 16px; + margin-bottom: var(--tk-gap-md); } .section-title { @@ -14,14 +14,14 @@ font-weight: bold; color: $tx; display: block; - margin-bottom: 16px; + margin-bottom: var(--tk-gap-md); } .form-row { display: flex; align-items: center; justify-content: space-between; - padding: 16px 0; + padding: var(--tk-gap-md) 0; border-bottom: 1px solid $bd-l; &:last-child { @@ -59,24 +59,24 @@ .form-textarea { width: 100%; - margin-top: 12px; + margin-top: var(--tk-gap-sm); font-size: var(--tk-font-h1); color: $tx; min-height: 120px; background: $bg; border-radius: $r-sm; - padding: 16px; + padding: var(--tk-gap-md); } .submit-btn { - background: $pri; + background: var(--tk-pri); border-radius: $r-sm; - padding: 24px; + padding: var(--tk-gap-lg); text-align: center; - margin-top: 24px; + margin-top: var(--tk-gap-lg); &:active { - background: $pri-d; + opacity: var(--tk-touch-feedback-opacity); } &--disabled { diff --git a/apps/miniprogram/src/pages/pkg-doctor-clinical/dialysis/create/index.tsx b/apps/miniprogram/src/pages/pkg-doctor-clinical/dialysis/create/index.tsx index cc8228b..ea03133 100644 --- a/apps/miniprogram/src/pages/pkg-doctor-clinical/dialysis/create/index.tsx +++ b/apps/miniprogram/src/pages/pkg-doctor-clinical/dialysis/create/index.tsx @@ -7,7 +7,7 @@ import { import Loading from '@/components/Loading'; import PageShell from '@/components/ui/PageShell'; import ContentCard from '@/components/ui/ContentCard'; -import { useElderClass } from '../../../../hooks/useElderClass'; +import { useDoctorClass } from '@/hooks/useDoctorClass'; import { useSafeTimeout } from '@/hooks/useSafeTimeout'; import './index.scss'; @@ -61,7 +61,7 @@ export default function DialysisCreate() { const version = router.params.version ? Number(router.params.version) : 0; const patientIdFromRoute = router.params.patientId || ''; const isEdit = !!id; - const modeClass = useElderClass(); + const modeClass = useDoctorClass(); const [form, setForm] = useState({ ...initialForm, patient_id: patientIdFromRoute }); const [loading, setLoading] = useState(isEdit); diff --git a/apps/miniprogram/src/pages/pkg-doctor-clinical/dialysis/detail/index.scss b/apps/miniprogram/src/pages/pkg-doctor-clinical/dialysis/detail/index.scss index aa7c852..4e36b64 100644 --- a/apps/miniprogram/src/pages/pkg-doctor-clinical/dialysis/detail/index.scss +++ b/apps/miniprogram/src/pages/pkg-doctor-clinical/dialysis/detail/index.scss @@ -6,14 +6,14 @@ font-weight: bold; color: $tx; display: block; - margin-bottom: 12px; + margin-bottom: var(--tk-gap-sm); } .record-header { display: flex; justify-content: space-between; align-items: center; - margin-bottom: 8px; + margin-bottom: var(--tk-gap-xs); } .record-header__title { @@ -25,15 +25,15 @@ .record-header__status { display: inline-block; - padding: 4px 12px; + padding: var(--tk-gap-2xs) var(--tk-gap-sm); border-radius: $r-xs; font-size: var(--tk-font-body); background: $bd-l; color: $tx3; &--completed { - background: $pri-l; - color: $pri; + background: var(--tk-pri-l); + color: var(--tk-pri); } &--reviewed { @@ -52,14 +52,14 @@ font-size: var(--tk-font-h2); color: $tx3; display: block; - margin-top: 8px; + margin-top: var(--tk-gap-xs); font-variant-numeric: tabular-nums; } .detail-row { display: flex; justify-content: space-between; - padding: 10px 0; + padding: var(--tk-gap-sm) 0; border-bottom: 1px solid $bd-l; &:last-child { @@ -77,13 +77,13 @@ color: $tx; text-align: right; flex: 1; - margin-left: 24px; + margin-left: var(--tk-gap-lg); font-variant-numeric: tabular-nums; } .error-text { text-align: center; - padding: 120px 0; + padding: var(--tk-gap-2xl) 0; font-size: var(--tk-font-body-lg); color: $tx3; } @@ -91,24 +91,24 @@ .actions { display: flex; flex-direction: column; - gap: 12px; - padding: 16px 0; + gap: var(--tk-gap-sm); + padding: var(--tk-gap-md) 0; } .action-btn { border-radius: $r-sm; - padding: 20px; + padding: var(--tk-section-gap); text-align: center; &--primary { - background: $pri; + background: var(--tk-pri); .action-btn__text { color: $white; } &:active { - background: $pri-d; + opacity: var(--tk-touch-feedback-opacity); } } @@ -117,7 +117,7 @@ border: 1px solid $bd; .action-btn__text { - color: $pri; + color: var(--tk-pri); } } diff --git a/apps/miniprogram/src/pages/pkg-doctor-clinical/dialysis/detail/index.tsx b/apps/miniprogram/src/pages/pkg-doctor-clinical/dialysis/detail/index.tsx index 568fdd9..0666123 100644 --- a/apps/miniprogram/src/pages/pkg-doctor-clinical/dialysis/detail/index.tsx +++ b/apps/miniprogram/src/pages/pkg-doctor-clinical/dialysis/detail/index.tsx @@ -10,14 +10,14 @@ import { import Loading from '@/components/Loading'; import PageShell from '@/components/ui/PageShell'; import ContentCard from '@/components/ui/ContentCard'; -import { useElderClass } from '../../../../hooks/useElderClass'; +import { useDoctorClass } from '@/hooks/useDoctorClass'; import { useSafeTimeout } from '@/hooks/useSafeTimeout'; import './index.scss'; export default function DialysisDetail() { const router = useRouter(); const id = router.params.id || ''; - const modeClass = useElderClass(); + const modeClass = useDoctorClass(); const [record, setRecord] = useState(null); const [loading, setLoading] = useState(true); const [submitting, setSubmitting] = useState(false); diff --git a/apps/miniprogram/src/pages/pkg-doctor-clinical/dialysis/index.scss b/apps/miniprogram/src/pages/pkg-doctor-clinical/dialysis/index.scss index a0f7ebc..4d0011c 100644 --- a/apps/miniprogram/src/pages/pkg-doctor-clinical/dialysis/index.scss +++ b/apps/miniprogram/src/pages/pkg-doctor-clinical/dialysis/index.scss @@ -7,7 +7,7 @@ // PaginationBar 已接管:pagination 分页样式 .record-count { - margin-bottom: 16px; + margin-bottom: var(--tk-gap-md); text { font-size: var(--tk-font-h2); @@ -30,12 +30,12 @@ .type-tag { display: inline-block; - padding: 4px 12px; + padding: var(--tk-gap-2xs) var(--tk-gap-sm); border-radius: $r-xs; font-size: var(--tk-font-body); font-weight: 600; - background: $pri-l; - color: $pri-d; + background: var(--tk-pri-l); + color: var(--tk-pri-d); &--hdf { background: $acc-l; @@ -51,7 +51,7 @@ .record-card__body { display: flex; flex-wrap: wrap; - gap: 12px; + gap: var(--tk-gap-sm); } .record-card__date { @@ -68,12 +68,12 @@ .fab { position: fixed; - right: 32px; - bottom: 120px; + right: var(--tk-gap-xl); + bottom: calc(var(--tk-gap-2xl) + var(--tk-gap-xl)); width: 96px; height: 96px; border-radius: $r-pill; - background: $pri; + background: var(--tk-pri); display: flex; align-items: center; justify-content: center; @@ -81,7 +81,7 @@ z-index: 10; &:active { - background: $pri-d; + opacity: var(--tk-touch-feedback-opacity); } } diff --git a/apps/miniprogram/src/pages/pkg-doctor-clinical/dialysis/index.tsx b/apps/miniprogram/src/pages/pkg-doctor-clinical/dialysis/index.tsx index 570a6c1..97a2b0a 100644 --- a/apps/miniprogram/src/pages/pkg-doctor-clinical/dialysis/index.tsx +++ b/apps/miniprogram/src/pages/pkg-doctor-clinical/dialysis/index.tsx @@ -13,7 +13,7 @@ import PaginationBar from '@/components/patterns/PaginationBar'; import SegmentTabs from '@/components/SegmentTabs'; import ErrorState from '@/components/ErrorState'; import EmptyState from '@/components/EmptyState'; -import { useElderClass } from '../../../hooks/useElderClass'; +import { useDoctorClass } from '@/hooks/useDoctorClass'; import { safeNavigateTo } from '@/utils/navigate'; import './index.scss'; @@ -35,7 +35,7 @@ const STATUS_LABEL: Record = { export default function DialysisList() { const router = useRouter(); const patientId = router.params.patientId || ''; - const modeClass = useElderClass(); + const modeClass = useDoctorClass(); const [searchPatient, setSearchPatient] = useState(''); const [currentPatientId, setCurrentPatientId] = useState(patientId); const [activeTab, setActiveTab] = useState(''); diff --git a/apps/miniprogram/src/pages/pkg-doctor-clinical/prescription/create/index.scss b/apps/miniprogram/src/pages/pkg-doctor-clinical/prescription/create/index.scss index c3b0105..e9d4fbf 100644 --- a/apps/miniprogram/src/pages/pkg-doctor-clinical/prescription/create/index.scss +++ b/apps/miniprogram/src/pages/pkg-doctor-clinical/prescription/create/index.scss @@ -6,7 +6,7 @@ /* ─── 表单分组间距(ContentCard 外层补充) ─── */ .section { - margin-bottom: 16px; + margin-bottom: var(--tk-gap-md); } .section-title { @@ -14,14 +14,14 @@ font-weight: bold; color: $tx; display: block; - margin-bottom: 16px; + margin-bottom: var(--tk-gap-md); } .form-row { display: flex; align-items: center; justify-content: space-between; - padding: 16px 0; + padding: var(--tk-gap-md) 0; border-bottom: 1px solid $bd-l; &:last-child { @@ -54,24 +54,24 @@ .form-textarea { width: 100%; - margin-top: 12px; + margin-top: var(--tk-gap-sm); font-size: var(--tk-font-h1); color: $tx; min-height: 120px; background: $bg; border-radius: $r-sm; - padding: 16px; + padding: var(--tk-gap-md); } .submit-btn { - background: $pri; + background: var(--tk-pri); border-radius: $r-sm; - padding: 24px; + padding: var(--tk-gap-lg); text-align: center; - margin-top: 24px; + margin-top: var(--tk-gap-lg); &:active { - background: $pri-d; + opacity: var(--tk-touch-feedback-opacity); } &--disabled { diff --git a/apps/miniprogram/src/pages/pkg-doctor-clinical/prescription/create/index.tsx b/apps/miniprogram/src/pages/pkg-doctor-clinical/prescription/create/index.tsx index 5e0ad18..63cbc71 100644 --- a/apps/miniprogram/src/pages/pkg-doctor-clinical/prescription/create/index.tsx +++ b/apps/miniprogram/src/pages/pkg-doctor-clinical/prescription/create/index.tsx @@ -5,7 +5,7 @@ import { createDialysisPrescription } from '@/services/doctor/dialysis'; import Loading from '@/components/Loading'; import PageShell from '@/components/ui/PageShell'; import ContentCard from '@/components/ui/ContentCard'; -import { useElderClass } from '../../../../hooks/useElderClass'; +import { useDoctorClass } from '@/hooks/useDoctorClass'; import { useSafeTimeout } from '@/hooks/useSafeTimeout'; import './index.scss'; @@ -54,7 +54,7 @@ const initialForm: FormState = { export default function PrescriptionCreate() { const router = useRouter(); const patientId = router.params.patientId || ''; - const modeClass = useElderClass(); + const modeClass = useDoctorClass(); const [form, setForm] = useState(initialForm); const [submitting, setSubmitting] = useState(false); const { safeSetTimeout } = useSafeTimeout(); diff --git a/apps/miniprogram/src/pages/pkg-doctor-clinical/prescription/detail/index.scss b/apps/miniprogram/src/pages/pkg-doctor-clinical/prescription/detail/index.scss index 46da703..eb2daec 100644 --- a/apps/miniprogram/src/pages/pkg-doctor-clinical/prescription/detail/index.scss +++ b/apps/miniprogram/src/pages/pkg-doctor-clinical/prescription/detail/index.scss @@ -6,14 +6,14 @@ font-weight: bold; color: $tx; display: block; - margin-bottom: 12px; + margin-bottom: var(--tk-gap-sm); } .rx-header { display: flex; justify-content: space-between; align-items: center; - margin-bottom: 8px; + margin-bottom: var(--tk-gap-xs); } .rx-header__title { @@ -24,7 +24,7 @@ .rx-header__status { display: inline-block; - padding: 4px 12px; + padding: var(--tk-gap-2xs) var(--tk-gap-sm); border-radius: $r-xs; font-size: var(--tk-font-body); background: $bd-l; @@ -46,7 +46,7 @@ .detail-row { display: flex; justify-content: space-between; - padding: 10px 0; + padding: var(--tk-gap-sm) 0; border-bottom: 1px solid $bd-l; &:last-child { @@ -64,7 +64,7 @@ color: $tx; text-align: right; flex: 1; - margin-left: 24px; + margin-left: var(--tk-gap-lg); font-variant-numeric: tabular-nums; } @@ -76,7 +76,7 @@ .error-text { text-align: center; - padding: 120px 0; + padding: var(--tk-gap-2xl) 0; font-size: var(--tk-font-body-lg); color: $tx3; } @@ -84,13 +84,13 @@ .actions { display: flex; flex-direction: column; - gap: 12px; - padding: 16px 0; + gap: var(--tk-gap-sm); + padding: var(--tk-gap-md) 0; } .action-btn { border-radius: $r-sm; - padding: 20px; + padding: var(--tk-section-gap); text-align: center; &--secondary { @@ -98,7 +98,7 @@ border: 1px solid $bd; .action-btn__text { - color: $pri; + color: var(--tk-pri); } } diff --git a/apps/miniprogram/src/pages/pkg-doctor-clinical/prescription/detail/index.tsx b/apps/miniprogram/src/pages/pkg-doctor-clinical/prescription/detail/index.tsx index df4077d..25c0491 100644 --- a/apps/miniprogram/src/pages/pkg-doctor-clinical/prescription/detail/index.tsx +++ b/apps/miniprogram/src/pages/pkg-doctor-clinical/prescription/detail/index.tsx @@ -9,14 +9,14 @@ import { import Loading from '@/components/Loading'; import PageShell from '@/components/ui/PageShell'; import ContentCard from '@/components/ui/ContentCard'; -import { useElderClass } from '../../../../hooks/useElderClass'; +import { useDoctorClass } from '@/hooks/useDoctorClass'; import { useSafeTimeout } from '@/hooks/useSafeTimeout'; import './index.scss'; export default function PrescriptionDetail() { const router = useRouter(); const id = router.params.id || ''; - const modeClass = useElderClass(); + const modeClass = useDoctorClass(); const [rx, setRx] = useState(null); const [loading, setLoading] = useState(true); const [submitting, setSubmitting] = useState(false); diff --git a/apps/miniprogram/src/pages/pkg-doctor-clinical/prescription/index.scss b/apps/miniprogram/src/pages/pkg-doctor-clinical/prescription/index.scss index 8ccb9cc..49568a6 100644 --- a/apps/miniprogram/src/pages/pkg-doctor-clinical/prescription/index.scss +++ b/apps/miniprogram/src/pages/pkg-doctor-clinical/prescription/index.scss @@ -7,7 +7,7 @@ // PaginationBar 已接管:pagination 分页样式 .prescription-count { - margin-bottom: 16px; + margin-bottom: var(--tk-gap-md); text { font-size: var(--tk-font-h2); @@ -25,7 +25,7 @@ display: flex; justify-content: space-between; align-items: center; - margin-bottom: 12px; + margin-bottom: var(--tk-gap-sm); } .prescription-card__model { @@ -36,8 +36,8 @@ .prescription-card__body { display: flex; - gap: 16px; - margin-bottom: 8px; + gap: var(--tk-gap-md); + margin-bottom: var(--tk-gap-xs); } .prescription-card__meta { @@ -55,12 +55,12 @@ .fab { position: fixed; - right: 32px; - bottom: 120px; + right: var(--tk-gap-xl); + bottom: calc(var(--tk-gap-2xl) + var(--tk-gap-xl)); width: 96px; height: 96px; border-radius: $r-pill; - background: $pri; + background: var(--tk-pri); display: flex; align-items: center; justify-content: center; @@ -68,7 +68,7 @@ z-index: 10; &:active { - background: $pri-d; + opacity: var(--tk-touch-feedback-opacity); } } diff --git a/apps/miniprogram/src/pages/pkg-doctor-clinical/prescription/index.tsx b/apps/miniprogram/src/pages/pkg-doctor-clinical/prescription/index.tsx index 3a7a883..6e83942 100644 --- a/apps/miniprogram/src/pages/pkg-doctor-clinical/prescription/index.tsx +++ b/apps/miniprogram/src/pages/pkg-doctor-clinical/prescription/index.tsx @@ -13,7 +13,7 @@ import PaginationBar from '@/components/patterns/PaginationBar'; import SegmentTabs from '@/components/SegmentTabs'; import ErrorState from '@/components/ErrorState'; import EmptyState from '@/components/EmptyState'; -import { useElderClass } from '../../../hooks/useElderClass'; +import { useDoctorClass } from '@/hooks/useDoctorClass'; import { safeNavigateTo } from '@/utils/navigate'; import './index.scss'; @@ -31,7 +31,7 @@ const STATUS_LABEL: Record = { export default function PrescriptionList() { const router = useRouter(); const patientId = router.params.patientId || ''; - const modeClass = useElderClass(); + const modeClass = useDoctorClass(); const [searchPatient, setSearchPatient] = useState(''); const [currentPatientId, setCurrentPatientId] = useState(patientId); const [activeTab, setActiveTab] = useState(''); diff --git a/apps/miniprogram/src/pages/pkg-doctor-clinical/report/detail/index.scss b/apps/miniprogram/src/pages/pkg-doctor-clinical/report/detail/index.scss index 31a34d0..b80cd22 100644 --- a/apps/miniprogram/src/pages/pkg-doctor-clinical/report/detail/index.scss +++ b/apps/miniprogram/src/pages/pkg-doctor-clinical/report/detail/index.scss @@ -9,7 +9,7 @@ display: flex; justify-content: space-between; align-items: center; - margin-bottom: 12px; + margin-bottom: var(--tk-gap-sm); &__type { font-family: 'Georgia', 'Times New Roman', serif; @@ -20,7 +20,7 @@ &__status { font-size: var(--tk-font-h2); - padding: 6px 16px; + padding: 6px var(--tk-gap-md); border-radius: $r; font-weight: 500; @@ -39,7 +39,7 @@ font-size: var(--tk-font-h2); color: $acc; display: block; - margin-top: 8px; + margin-top: var(--tk-gap-xs); } .indicator-table { @@ -48,19 +48,19 @@ .indicator-row { display: flex; - padding: 16px 0; + padding: var(--tk-gap-md) 0; border-bottom: 1px solid $bd-l; align-items: center; &--header { border-bottom: 2px solid $bd; - padding-bottom: 12px; + padding-bottom: var(--tk-gap-sm); } &--abnormal { background: $dan-l; - margin: 0 -12px; - padding: 16px 12px; + margin: 0 calc(-1 * var(--tk-gap-sm)); + padding: var(--tk-gap-md) var(--tk-gap-sm); border-radius: $r-sm; } } @@ -108,9 +108,9 @@ } .notes-display { - background: $pri-l; + background: var(--tk-pri-l); border-radius: $r; - padding: 20px; + padding: var(--tk-section-gap); } .notes-text { @@ -124,18 +124,18 @@ min-height: 200px; background: $bd-l; border-radius: $r; - padding: 20px; + padding: var(--tk-section-gap); font-size: var(--tk-font-h1); color: $tx; box-sizing: border-box; line-height: 1.6; - margin-bottom: 20px; + margin-bottom: var(--tk-section-gap); } .review-btn { - background: $pri; + background: var(--tk-pri); border-radius: $r; - padding: 20px; + padding: var(--tk-section-gap); text-align: center; &--disabled { @@ -151,7 +151,7 @@ .error-text { text-align: center; - padding: 80px 32px; + padding: 80px var(--tk-gap-xl); color: $tx3; font-size: var(--tk-font-body-lg); } diff --git a/apps/miniprogram/src/pages/pkg-doctor-clinical/report/detail/index.tsx b/apps/miniprogram/src/pages/pkg-doctor-clinical/report/detail/index.tsx index 9015936..a6fa309 100644 --- a/apps/miniprogram/src/pages/pkg-doctor-clinical/report/detail/index.tsx +++ b/apps/miniprogram/src/pages/pkg-doctor-clinical/report/detail/index.tsx @@ -6,14 +6,14 @@ import { getLabReport, reviewLabReport, type LabReportDetail } from '@/services/ import Loading from '@/components/Loading'; import PageShell from '@/components/ui/PageShell'; import ContentCard from '@/components/ui/ContentCard'; -import { useElderClass } from '../../../../hooks/useElderClass'; +import { useDoctorClass } from '@/hooks/useDoctorClass'; import './index.scss'; export default function ReportDetail() { const router = useRouter(); const patientId = router.params.patientId || ''; const reportId = router.params.id || ''; - const modeClass = useElderClass(); + const modeClass = useDoctorClass(); const [report, setReport] = useState(null); const [loading, setLoading] = useState(true); const [doctorNotes, setDoctorNotes] = useState(''); diff --git a/apps/miniprogram/src/pages/pkg-doctor-clinical/report/index.scss b/apps/miniprogram/src/pages/pkg-doctor-clinical/report/index.scss index 7d46586..3a95b63 100644 --- a/apps/miniprogram/src/pages/pkg-doctor-clinical/report/index.scss +++ b/apps/miniprogram/src/pages/pkg-doctor-clinical/report/index.scss @@ -6,7 +6,7 @@ // StatusTag 已接管:reviewed 标签样式 .report-count { - margin-bottom: 16px; + margin-bottom: var(--tk-gap-md); text { font-size: var(--tk-font-h2); @@ -24,7 +24,7 @@ display: flex; justify-content: space-between; align-items: center; - margin-bottom: 12px; + margin-bottom: var(--tk-gap-sm); } .report-card__type { @@ -42,7 +42,7 @@ .report-card__indicators { display: flex; align-items: center; - gap: 16px; + gap: var(--tk-gap-md); } .report-card__abnormal { diff --git a/apps/miniprogram/src/pages/pkg-doctor-clinical/report/index.tsx b/apps/miniprogram/src/pages/pkg-doctor-clinical/report/index.tsx index f899c3e..106831b 100644 --- a/apps/miniprogram/src/pages/pkg-doctor-clinical/report/index.tsx +++ b/apps/miniprogram/src/pages/pkg-doctor-clinical/report/index.tsx @@ -11,13 +11,13 @@ import LoadingCard from '@/components/ui/LoadingCard'; import SearchSection from '@/components/patterns/SearchSection'; import ErrorState from '@/components/ErrorState'; import EmptyState from '@/components/EmptyState'; -import { useElderClass } from '../../../hooks/useElderClass'; +import { useDoctorClass } from '@/hooks/useDoctorClass'; import './index.scss'; export default function ReportList() { const router = useRouter(); const patientId = router.params.patientId || ''; - const modeClass = useElderClass(); + const modeClass = useDoctorClass(); const [searchPatient, setSearchPatient] = useState(''); const [currentPatientId, setCurrentPatientId] = useState(patientId); const [reports, setReports] = useState([]); diff --git a/apps/miniprogram/src/pages/pkg-doctor-core/action-inbox/index.scss b/apps/miniprogram/src/pages/pkg-doctor-core/action-inbox/index.scss index 668fe34..870e121 100644 --- a/apps/miniprogram/src/pages/pkg-doctor-core/action-inbox/index.scss +++ b/apps/miniprogram/src/pages/pkg-doctor-core/action-inbox/index.scss @@ -6,28 +6,28 @@ .inbox-list { height: calc(100vh - 50px); - padding: 12px; + padding: var(--tk-gap-sm); } .inbox-card { - margin-bottom: 10px; + margin-bottom: var(--tk-gap-sm); .inbox-card-header { display: flex; align-items: center; - gap: 8px; - margin-bottom: 6px; + gap: var(--tk-gap-xs); + margin-bottom: var(--tk-gap-2xs); } .inbox-type-tag { color: $card; font-size: var(--tk-font-micro); - padding: 2px 6px; + padding: 2px var(--tk-gap-2xs); border-radius: $r-xs; flex-shrink: 0; &--ai { - background: $pri; + background: var(--tk-pri); } &--alert { @@ -75,7 +75,7 @@ display: flex; justify-content: space-between; align-items: center; - padding: 16px 20px; + padding: var(--tk-gap-md) var(--tk-section-gap); border-bottom: 1px solid $bd-l; .dialog-title { @@ -91,21 +91,21 @@ } .dialog-body { - padding: 16px 20px; + padding: var(--tk-gap-md) var(--tk-section-gap); } .dialog-patient { font-size: var(--tk-font-cap); color: $tx2; display: block; - margin-bottom: 12px; + margin-bottom: var(--tk-gap-sm); } .thread-item { display: flex; align-items: flex-start; - gap: 10px; - padding: 6px 0; + gap: var(--tk-gap-sm); + padding: var(--tk-gap-2xs) 0; } .thread-dot { @@ -136,19 +136,19 @@ .dialog-actions { display: flex; - gap: 8px; - padding: 12px 20px 20px; + gap: var(--tk-gap-xs); + padding: var(--tk-gap-sm) var(--tk-section-gap) var(--tk-section-gap); border-top: 1px solid $bd-l; .action-btn { flex: 1; text-align: center; - padding: 10px; + padding: var(--tk-gap-sm); border-radius: $r-sm; font-size: var(--tk-font-cap); font-weight: 500; - &.primary { background: $pri; color: $card; } + &.primary { background: var(--tk-pri); color: $card; } &.danger { background: $dan; color: $card; } &.default { background: $surface-alt; color: $tx2; } } diff --git a/apps/miniprogram/src/pages/pkg-doctor-core/action-inbox/index.tsx b/apps/miniprogram/src/pages/pkg-doctor-core/action-inbox/index.tsx index 049f165..849a67a 100644 --- a/apps/miniprogram/src/pages/pkg-doctor-core/action-inbox/index.tsx +++ b/apps/miniprogram/src/pages/pkg-doctor-core/action-inbox/index.tsx @@ -15,7 +15,7 @@ import EmptyState from '@/components/EmptyState'; import SegmentTabs from '@/components/SegmentTabs'; import PageShell from '@/components/ui/PageShell'; import ContentCard from '@/components/ui/ContentCard'; -import { useElderClass } from '../../../hooks/useElderClass'; +import { useDoctorClass } from '@/hooks/useDoctorClass'; import './index.scss'; const TYPE_LABEL: Record = { @@ -40,7 +40,7 @@ const STATUS_TABS = [ ]; export default function ActionInboxPage() { - const modeClass = useElderClass(); + const modeClass = useDoctorClass(); const [items, setItems] = useState([]); const [total, setTotal] = useState(0); const [_page, setPage] = useState(1); diff --git a/apps/miniprogram/src/pages/pkg-doctor-core/consultation/detail/index.scss b/apps/miniprogram/src/pages/pkg-doctor-core/consultation/detail/index.scss index 31915e5..9d8df0c 100644 --- a/apps/miniprogram/src/pages/pkg-doctor-core/consultation/detail/index.scss +++ b/apps/miniprogram/src/pages/pkg-doctor-core/consultation/detail/index.scss @@ -12,7 +12,7 @@ display: flex; justify-content: space-between; align-items: center; - padding: 24px 32px; + padding: var(--tk-gap-lg) var(--tk-gap-xl); background: $card; border-bottom: 1px solid $bd; @@ -26,19 +26,19 @@ &__close { font-size: var(--tk-font-h1); color: $dan; - padding: 8px 16px; + padding: var(--tk-gap-xs) var(--tk-gap-md); } } .chat-messages { flex: 1; - padding: 24px; + padding: var(--tk-gap-lg); overflow-y: auto; } .msg-row { display: flex; - margin-bottom: 20px; + margin-bottom: var(--tk-section-gap); &--self { justify-content: flex-end; @@ -47,7 +47,7 @@ .msg-bubble { max-width: 70%; - padding: 20px 24px; + padding: var(--tk-section-gap) var(--tk-gap-lg); border-radius: $r-lg; position: relative; @@ -57,7 +57,7 @@ } &--self { - background: $pri; + background: var(--tk-pri); border-top-right-radius: 4px; } } @@ -77,13 +77,13 @@ .msg-truncated-hint { display: flex; justify-content: center; - padding: 12px 0; + padding: var(--tk-gap-sm) 0; &__text { font-size: var(--tk-font-body); color: $tx3; background: $bd-l; - padding: 4px 16px; + padding: var(--tk-gap-2xs) var(--tk-gap-md); border-radius: $r; } } @@ -93,7 +93,7 @@ font-size: var(--tk-font-body); color: $tx3; display: block; - margin-top: 8px; + margin-top: var(--tk-gap-xs); text-align: right; .msg-bubble--self & { @@ -103,7 +103,7 @@ .chat-empty { text-align: center; - padding: 120px 32px; + padding: var(--tk-gap-2xl) var(--tk-gap-xl); &__text { font-size: var(--tk-font-h1); @@ -114,7 +114,7 @@ .chat-input-bar { display: flex; align-items: center; - padding: 16px 24px; + padding: var(--tk-gap-md) var(--tk-gap-lg); background: $card; border-top: 1px solid $bd; padding-bottom: calc(16px + env(safe-area-inset-bottom)); @@ -124,15 +124,15 @@ flex: 1; background: $bd-l; border-radius: $r; - padding: 16px 20px; + padding: var(--tk-gap-md) var(--tk-section-gap); font-size: var(--tk-font-body-lg); - margin-right: 16px; + margin-right: var(--tk-gap-md); } .chat-send-btn { - background: $pri; + background: var(--tk-pri); border-radius: $r; - padding: 16px 28px; + padding: var(--tk-gap-md) var(--tk-card-padding-lg); flex-shrink: 0; &--disabled { @@ -147,7 +147,7 @@ } .chat-closed-bar { - padding: 24px; + padding: var(--tk-gap-lg); text-align: center; background: $card; border-top: 1px solid $bd; diff --git a/apps/miniprogram/src/pages/pkg-doctor-core/consultation/detail/index.tsx b/apps/miniprogram/src/pages/pkg-doctor-core/consultation/detail/index.tsx index 24fd976..1f35790 100644 --- a/apps/miniprogram/src/pages/pkg-doctor-core/consultation/detail/index.tsx +++ b/apps/miniprogram/src/pages/pkg-doctor-core/consultation/detail/index.tsx @@ -7,7 +7,7 @@ import { type ConsultationSession, type ConsultationMessage, } from '@/services/doctor/consultation'; import Loading from '@/components/Loading'; -import { useElderClass } from '@/hooks/useElderClass'; +import { useDoctorClass } from '@/hooks/useDoctorClass'; import { useLongPolling } from '@/hooks/useLongPolling'; import './index.scss'; @@ -17,7 +17,7 @@ const MAX_STATE_MESSAGES = 300; export default function ConsultationDetail() { const router = useRouter(); const sessionId = router.params.id || ''; - const modeClass = useElderClass(); + const modeClass = useDoctorClass(); const [session, setSession] = useState(null); const [messages, setMessages] = useState([]); const [inputText, setInputText] = useState(''); diff --git a/apps/miniprogram/src/pages/pkg-doctor-core/consultation/index.scss b/apps/miniprogram/src/pages/pkg-doctor-core/consultation/index.scss index b6dc175..8343834 100644 --- a/apps/miniprogram/src/pages/pkg-doctor-core/consultation/index.scss +++ b/apps/miniprogram/src/pages/pkg-doctor-core/consultation/index.scss @@ -17,7 +17,7 @@ display: flex; justify-content: space-between; align-items: center; - margin-bottom: 12px; + margin-bottom: var(--tk-gap-sm); } .session-card__subject { @@ -28,18 +28,18 @@ overflow: hidden; text-overflow: ellipsis; white-space: nowrap; - margin-right: 16px; + margin-right: var(--tk-gap-md); } .session-card__info { display: flex; align-items: center; - gap: 16px; - margin-bottom: 8px; + gap: var(--tk-gap-md); + margin-bottom: var(--tk-gap-xs); } .session-card__type { - @include tag($pri-l, $pri); + @include tag(var(--tk-pri-l), var(--tk-pri)); } .session-card__time { @@ -58,8 +58,8 @@ .session-card__badge { position: absolute; - top: 20px; - right: 20px; + top: var(--tk-section-gap); + right: var(--tk-section-gap); min-width: 36px; height: 36px; background: $dan; diff --git a/apps/miniprogram/src/pages/pkg-doctor-core/consultation/index.tsx b/apps/miniprogram/src/pages/pkg-doctor-core/consultation/index.tsx index 1d6eecf..0904411 100644 --- a/apps/miniprogram/src/pages/pkg-doctor-core/consultation/index.tsx +++ b/apps/miniprogram/src/pages/pkg-doctor-core/consultation/index.tsx @@ -11,7 +11,7 @@ import PaginationBar from '@/components/patterns/PaginationBar'; import SearchSection from '@/components/patterns/SearchSection'; import ErrorState from '@/components/ErrorState'; import EmptyState from '@/components/EmptyState'; -import { useElderClass } from '../../../hooks/useElderClass'; +import { useDoctorClass } from '@/hooks/useDoctorClass'; import { formatDateTime } from '@/utils/date'; import { safeNavigateTo } from '@/utils/navigate'; import './index.scss'; @@ -30,7 +30,7 @@ const STATUS_COLOR_MAP: Record([]); const [activeTab, setActiveTab] = useState(''); const [loading, setLoading] = useState(true); diff --git a/apps/miniprogram/src/pages/pkg-doctor-core/followup/detail/index.scss b/apps/miniprogram/src/pages/pkg-doctor-core/followup/detail/index.scss index 32a3c90..90d7fc2 100644 --- a/apps/miniprogram/src/pages/pkg-doctor-core/followup/detail/index.scss +++ b/apps/miniprogram/src/pages/pkg-doctor-core/followup/detail/index.scss @@ -9,7 +9,7 @@ display: flex; justify-content: space-between; align-items: center; - margin-bottom: 20px; + margin-bottom: var(--tk-section-gap); &__title { font-family: 'Georgia', 'Times New Roman', serif; @@ -20,12 +20,12 @@ &__status { font-size: var(--tk-font-h2); - padding: 6px 16px; + padding: 6px var(--tk-gap-md); border-radius: $r; font-weight: 500; &--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; } &--overdue { background: $dan-l; color: $dan; } &--cancelled { background: $bd-l; color: $tx3; } @@ -35,13 +35,13 @@ .info-grid { display: grid; grid-template-columns: 1fr 1fr 1fr; - gap: 16px; + gap: var(--tk-gap-md); } .info-item { display: flex; flex-direction: column; - gap: 4px; + gap: var(--tk-gap-2xs); } .info-label { @@ -56,8 +56,8 @@ } .task-template { - margin-top: 16px; - padding: 16px; + margin-top: var(--tk-gap-md); + padding: var(--tk-gap-md); background: $bd-l; border-radius: $r; @@ -65,7 +65,7 @@ font-size: var(--tk-font-body); color: $tx2; display: block; - margin-bottom: 8px; + margin-bottom: var(--tk-gap-xs); } &__text { @@ -76,7 +76,7 @@ } .record-item { - padding: 20px 0; + padding: var(--tk-section-gap) 0; border-bottom: 1px solid $bd-l; &:last-child { @@ -87,31 +87,31 @@ font-size: var(--tk-font-body); color: $tx3; display: block; - margin-bottom: 8px; + margin-bottom: var(--tk-gap-xs); } &__text { font-size: var(--tk-font-h1); color: $tx; display: block; - margin-bottom: 4px; + margin-bottom: var(--tk-gap-2xs); line-height: 1.5; } } .start-btn { text-align: center; - padding: 16px; - background: $pri; + padding: var(--tk-gap-md); + background: var(--tk-pri); border-radius: $r; - margin-bottom: 24px; + margin-bottom: var(--tk-gap-lg); color: $card; font-size: var(--tk-font-body-lg); font-weight: 500; } .form-group { - margin-bottom: 24px; + margin-bottom: var(--tk-gap-lg); } .form-label { @@ -119,7 +119,7 @@ color: $tx2; font-weight: 500; display: block; - margin-bottom: 12px; + margin-bottom: var(--tk-gap-sm); } .form-textarea { @@ -127,7 +127,7 @@ min-height: 160px; background: $bd-l; border-radius: $r; - padding: 16px 20px; + padding: var(--tk-gap-md) var(--tk-section-gap); font-size: var(--tk-font-h1); color: $tx; box-sizing: border-box; @@ -136,7 +136,7 @@ .form-date { width: 100%; - padding: 16px 20px; + padding: var(--tk-gap-md) var(--tk-section-gap); background: $bd-l; border-radius: $r; font-size: var(--tk-font-h1); @@ -145,11 +145,11 @@ } .submit-btn { - background: $pri; + background: var(--tk-pri); border-radius: $r; - padding: 20px; + padding: var(--tk-section-gap); text-align: center; - margin-top: 16px; + margin-top: var(--tk-gap-md); &--disabled { opacity: 0.5; @@ -164,7 +164,7 @@ .error-text { text-align: center; - padding: 80px 32px; + padding: 80px var(--tk-gap-xl); color: $tx3; font-size: var(--tk-font-body-lg); } diff --git a/apps/miniprogram/src/pages/pkg-doctor-core/followup/detail/index.tsx b/apps/miniprogram/src/pages/pkg-doctor-core/followup/detail/index.tsx index 758ebb3..65a857e 100644 --- a/apps/miniprogram/src/pages/pkg-doctor-core/followup/detail/index.tsx +++ b/apps/miniprogram/src/pages/pkg-doctor-core/followup/detail/index.tsx @@ -10,7 +10,7 @@ import { import Loading from '@/components/Loading'; import PageShell from '@/components/ui/PageShell'; import ContentCard from '@/components/ui/ContentCard'; -import { useElderClass } from '../../../../hooks/useElderClass'; +import { useDoctorClass } from '@/hooks/useDoctorClass'; import './index.scss'; const STATUS_LABELS: Record = { @@ -24,7 +24,7 @@ const STATUS_LABELS: Record = { export default function FollowUpDetail() { const router = useRouter(); const taskId = router.params.id || ''; - const modeClass = useElderClass(); + const modeClass = useDoctorClass(); const [task, setTask] = useState(null); const [records, setRecords] = useState([]); const [loading, setLoading] = useState(true); diff --git a/apps/miniprogram/src/pages/pkg-doctor-core/followup/index.scss b/apps/miniprogram/src/pages/pkg-doctor-core/followup/index.scss index 91bc019..6f8f764 100644 --- a/apps/miniprogram/src/pages/pkg-doctor-core/followup/index.scss +++ b/apps/miniprogram/src/pages/pkg-doctor-core/followup/index.scss @@ -6,7 +6,7 @@ // StatusTag 已接管:任务状态标签 .task-count { - margin-bottom: 16px; + margin-bottom: var(--tk-gap-md); text { font-size: var(--tk-font-h2); @@ -24,7 +24,7 @@ display: flex; justify-content: space-between; align-items: center; - margin-bottom: 12px; + margin-bottom: var(--tk-gap-sm); } .task-card__type { @@ -37,7 +37,7 @@ font-size: var(--tk-font-h1); color: $tx2; display: block; - margin-bottom: 8px; + margin-bottom: var(--tk-gap-xs); } .task-card__footer { diff --git a/apps/miniprogram/src/pages/pkg-doctor-core/followup/index.tsx b/apps/miniprogram/src/pages/pkg-doctor-core/followup/index.tsx index 87fbdcd..b1136c5 100644 --- a/apps/miniprogram/src/pages/pkg-doctor-core/followup/index.tsx +++ b/apps/miniprogram/src/pages/pkg-doctor-core/followup/index.tsx @@ -10,7 +10,7 @@ import LoadingCard from '@/components/ui/LoadingCard'; import SearchSection from '@/components/patterns/SearchSection'; import ErrorState from '@/components/ErrorState'; import EmptyState from '@/components/EmptyState'; -import { useElderClass } from '../../../hooks/useElderClass'; +import { useDoctorClass } from '@/hooks/useDoctorClass'; import './index.scss'; const TABS = [ @@ -31,7 +31,7 @@ const STATUS_COLOR_MAP: Record export default function FollowUpList() { const router = useRouter(); const patientId = router.params.patientId || ''; - const modeClass = useElderClass(); + const modeClass = useDoctorClass(); const [tasks, setTasks] = useState([]); const [activeTab, setActiveTab] = useState(''); const [loading, setLoading] = useState(true); diff --git a/apps/miniprogram/src/pages/pkg-doctor-core/index.scss b/apps/miniprogram/src/pages/pkg-doctor-core/index.scss index 3a15391..fd21781 100644 --- a/apps/miniprogram/src/pages/pkg-doctor-core/index.scss +++ b/apps/miniprogram/src/pages/pkg-doctor-core/index.scss @@ -6,19 +6,19 @@ .doctor-home { &__header { - margin-bottom: 40px; + margin-bottom: var(--tk-gap-2xl); } &__title { @include section-title; - margin-bottom: 12px; + margin-bottom: var(--tk-gap-sm); } &__greeting { font-size: var(--tk-font-h2); color: $tx2; display: block; - margin-bottom: 8px; + margin-bottom: var(--tk-gap-xs); } &__date { @@ -29,8 +29,8 @@ &__alert { display: flex; align-items: center; - margin: 16px 24px; - padding: 16px 20px; + margin: var(--tk-gap-md) var(--tk-page-padding); + padding: var(--tk-gap-md) var(--tk-section-gap); background: $dan-l; border-radius: $r; border-left: 4px solid $dan; @@ -46,7 +46,7 @@ line-height: 36px; font-weight: bold; font-size: var(--tk-font-body); - margin-right: 12px; + margin-right: var(--tk-gap-sm); flex-shrink: 0; } @@ -63,19 +63,19 @@ } &__search { - margin: 0 24px 16px; + margin: 0 var(--tk-page-padding) var(--tk-gap-md); } &__search-input { background: $surface-alt; border-radius: $r; - padding: 16px 20px; + padding: var(--tk-gap-md) var(--tk-section-gap); font-size: var(--tk-font-h1); color: $tx3; } &__section { - margin-bottom: 40px; + margin-bottom: var(--tk-gap-2xl); } &__section-title { @@ -85,19 +85,19 @@ &__grid { display: grid; grid-template-columns: 1fr 1fr; - gap: 20px; + gap: var(--tk-section-gap); } &__card { background: $card; border-radius: $r-lg; - padding: 28px 24px; + padding: var(--tk-card-padding-lg) var(--tk-card-padding); text-align: center; box-shadow: $shadow-md; transition: transform 0.15s; &:active { - transform: scale(0.97); + opacity: var(--tk-touch-feedback-opacity); } } @@ -107,12 +107,12 @@ width: 56px; height: 56px; border-radius: $r; - background: $pri-l; - color: $pri; + background: var(--tk-pri-l); + color: var(--tk-pri); font-family: 'Georgia', 'Times New Roman', serif; font-size: var(--tk-font-body-lg); font-weight: 700; - margin-bottom: 8px; + margin-bottom: var(--tk-gap-xs); } &__card-num { @@ -121,7 +121,7 @@ font-weight: 700; color: $tx; display: block; - margin-bottom: 8px; + margin-bottom: var(--tk-gap-xs); } &__card-label { @@ -132,7 +132,7 @@ &__quick-actions { display: grid; grid-template-columns: repeat(4, 1fr); - gap: 20px; + gap: var(--tk-section-gap); } &__footer { @@ -144,7 +144,7 @@ &__logout { color: $dan; font-size: var(--tk-font-h2); - padding: 16px 48px; + padding: var(--tk-gap-md) var(--tk-gap-2xl); display: inline-block; } } @@ -153,12 +153,12 @@ flex: 1; background: $card; border-radius: $r-lg; - padding: 28px 20px; + padding: var(--tk-card-padding-lg) var(--tk-section-gap); text-align: center; box-shadow: $shadow-md; &:active { - opacity: 0.8; + opacity: var(--tk-touch-feedback-opacity); } &__initial { @@ -177,7 +177,7 @@ &__icon-wrap { position: relative; display: inline-flex; - margin-bottom: 8px; + margin-bottom: var(--tk-gap-xs); } &__badge { diff --git a/apps/miniprogram/src/pages/pkg-doctor-core/index.tsx b/apps/miniprogram/src/pages/pkg-doctor-core/index.tsx index 0ace41b..e13615c 100644 --- a/apps/miniprogram/src/pages/pkg-doctor-core/index.tsx +++ b/apps/miniprogram/src/pages/pkg-doctor-core/index.tsx @@ -2,7 +2,7 @@ import { useState, useMemo, useCallback } from 'react'; import { View, Text, Input } from '@tarojs/components'; import Taro from '@tarojs/taro'; import { useAuthStore } from '@/stores/auth'; -import { useElderClass } from '../../hooks/useElderClass'; +import { useDoctorClass } from '@/hooks/useDoctorClass'; import { usePageData } from '@/hooks/usePageData'; import { getDashboard, type DoctorDashboard } from '@/services/doctor/dashboard'; import Loading from '@/components/Loading'; @@ -58,7 +58,7 @@ export default function DoctorHome() { const user = useAuthStore((s) => s.user); const logout = useAuthStore((s) => s.logout); const roles = useAuthStore((s) => s.roles); - const modeClass = useElderClass(); + const modeClass = useDoctorClass(); const [dashboard, setDashboard] = useState(null); const [alertCount, setAlertCount] = useState(0); const [loading, setLoading] = useState(true); diff --git a/apps/miniprogram/src/pages/pkg-doctor-core/patients/detail/index.scss b/apps/miniprogram/src/pages/pkg-doctor-core/patients/detail/index.scss index b95ad60..0b2a52e 100644 --- a/apps/miniprogram/src/pages/pkg-doctor-core/patients/detail/index.scss +++ b/apps/miniprogram/src/pages/pkg-doctor-core/patients/detail/index.scss @@ -8,13 +8,13 @@ .info-grid { display: grid; grid-template-columns: 1fr 1fr; - gap: 16px; + gap: var(--tk-gap-md); } .info-item { display: flex; flex-direction: column; - gap: 4px; + gap: var(--tk-gap-2xs); } .info-label { @@ -31,8 +31,8 @@ .warning-card { background: $wrn-l; border-radius: $r; - padding: 20px; - margin-bottom: 16px; + padding: var(--tk-section-gap); + margin-bottom: var(--tk-gap-md); } .warning-label { @@ -40,23 +40,23 @@ color: $wrn; font-weight: 600; display: block; - margin-bottom: 8px; + margin-bottom: var(--tk-gap-xs); } .warning-text { font-size: var(--tk-font-h1); - color: $pri-d; + color: var(--tk-pri-d); } .info-block { - margin-bottom: 12px; + margin-bottom: var(--tk-gap-sm); } .info-block-label { font-size: var(--tk-font-body); color: $tx3; display: block; - margin-bottom: 8px; + margin-bottom: var(--tk-gap-xs); } .info-block-text { @@ -68,14 +68,14 @@ .vitals-grid { display: grid; grid-template-columns: 1fr 1fr; - gap: 16px; - margin-bottom: 16px; + gap: var(--tk-gap-md); + margin-bottom: var(--tk-gap-md); } .vital-item { - background: $pri-l; + background: var(--tk-pri-l); border-radius: $r; - padding: 20px; + padding: var(--tk-section-gap); text-align: center; } @@ -83,7 +83,7 @@ @include serif-number; font-size: var(--tk-font-num-lg); font-weight: 700; - color: $pri; + color: var(--tk-pri); display: block; margin-bottom: 4px; } @@ -97,7 +97,7 @@ display: flex; justify-content: space-between; align-items: center; - padding: 16px 0; + padding: var(--tk-gap-md) 0; } .stat-label { @@ -117,7 +117,7 @@ } .lab-item { - padding: 20px 0; + padding: var(--tk-section-gap) 0; border-bottom: 1px solid $bd-l; &:last-child { @@ -125,14 +125,14 @@ } &:active { - background: $bd-l; + opacity: var(--tk-touch-feedback-opacity); } &__header { display: flex; justify-content: space-between; align-items: center; - margin-bottom: 8px; + margin-bottom: var(--tk-gap-xs); } &__type { @@ -155,21 +155,21 @@ .action-buttons { display: flex; - gap: 16px; + gap: var(--tk-gap-md); } .action-btn { flex: 1; text-align: center; - padding: 20px; + padding: var(--tk-section-gap); border-radius: $r; - background: $pri; + background: var(--tk-pri); color: $card; font-size: var(--tk-font-h1); font-weight: 500; &:active { - opacity: 0.85; + opacity: var(--tk-touch-feedback-opacity); } text { @@ -179,7 +179,7 @@ .error-text { text-align: center; - padding: 80px 32px; + padding: 80px var(--tk-gap-xl); color: $tx3; font-size: var(--tk-font-body-lg); } diff --git a/apps/miniprogram/src/pages/pkg-doctor-core/patients/detail/index.tsx b/apps/miniprogram/src/pages/pkg-doctor-core/patients/detail/index.tsx index 5267aad..6ed96cc 100644 --- a/apps/miniprogram/src/pages/pkg-doctor-core/patients/detail/index.tsx +++ b/apps/miniprogram/src/pages/pkg-doctor-core/patients/detail/index.tsx @@ -6,13 +6,13 @@ import { getPatient, getHealthSummary, type PatientDetail, type HealthSummary } import Loading from '@/components/Loading'; import PageShell from '@/components/ui/PageShell'; import ContentCard from '@/components/ui/ContentCard'; -import { useElderClass } from '../../../../hooks/useElderClass'; +import { useDoctorClass } from '@/hooks/useDoctorClass'; import './index.scss'; export default function PatientDetail() { const router = useRouter(); const patientId = router.params.id || ''; - const modeClass = useElderClass(); + const modeClass = useDoctorClass(); const [patient, setPatient] = useState(null); const [summary, setSummary] = useState(null); const [loading, setLoading] = useState(true); diff --git a/apps/miniprogram/src/pages/pkg-doctor-core/patients/index.scss b/apps/miniprogram/src/pages/pkg-doctor-core/patients/index.scss index a2dc5a9..4acf542 100644 --- a/apps/miniprogram/src/pages/pkg-doctor-core/patients/index.scss +++ b/apps/miniprogram/src/pages/pkg-doctor-core/patients/index.scss @@ -6,7 +6,7 @@ // StatusTag 已接管:patient-card__status 标签样式 .patient-count { - margin-bottom: 16px; + margin-bottom: var(--tk-gap-md); text { font-size: var(--tk-font-h2); @@ -23,7 +23,7 @@ .patient-card__header { display: flex; align-items: center; - gap: 16px; + gap: var(--tk-gap-md); } .patient-card__name { @@ -41,12 +41,12 @@ .patient-card__tags { display: flex; flex-wrap: wrap; - gap: 8px; - margin-top: 12px; + gap: var(--tk-gap-xs); + margin-top: var(--tk-gap-sm); } .patient-tag { - padding: 4px 14px; + padding: var(--tk-gap-2xs) 14px; border-radius: $r; background: rgba($pri, 0.1); @@ -57,7 +57,7 @@ .load-more-hint-wrap { text-align: center; - padding: 20px; + padding: var(--tk-section-gap); } .load-more-hint { diff --git a/apps/miniprogram/src/pages/pkg-doctor-core/patients/index.tsx b/apps/miniprogram/src/pages/pkg-doctor-core/patients/index.tsx index e09e462..c3a8d7f 100644 --- a/apps/miniprogram/src/pages/pkg-doctor-core/patients/index.tsx +++ b/apps/miniprogram/src/pages/pkg-doctor-core/patients/index.tsx @@ -10,11 +10,11 @@ import LoadingCard from '@/components/ui/LoadingCard'; import SearchSection from '@/components/patterns/SearchSection'; import EmptyState from '@/components/EmptyState'; import Loading from '@/components/Loading'; -import { useElderClass } from '../../../hooks/useElderClass'; +import { useDoctorClass } from '@/hooks/useDoctorClass'; import './index.scss'; export default function PatientList() { - const modeClass = useElderClass(); + const modeClass = useDoctorClass(); const [patients, setPatients] = useState([]); const [tags, setTags] = useState([]); const [activeTag, setActiveTag] = useState(''); diff --git a/apps/miniprogram/src/pages/pkg-health/alerts/index.scss b/apps/miniprogram/src/pages/pkg-health/alerts/index.scss index 0945578..4dc697a 100644 --- a/apps/miniprogram/src/pages/pkg-health/alerts/index.scss +++ b/apps/miniprogram/src/pages/pkg-health/alerts/index.scss @@ -19,7 +19,7 @@ } .alerts-tab.active { - background: $pri; + background: var(--tk-pri); } .alerts-tab-text { diff --git a/apps/miniprogram/src/pages/pkg-health/daily-monitoring/index.scss b/apps/miniprogram/src/pages/pkg-health/daily-monitoring/index.scss index 4f2a458..70e393c 100644 --- a/apps/miniprogram/src/pages/pkg-health/daily-monitoring/index.scss +++ b/apps/miniprogram/src/pages/pkg-health/daily-monitoring/index.scss @@ -6,7 +6,7 @@ /* ── hero ── */ .dm-hero { - padding: 48px 32px 36px; + padding: var(--tk-gap-2xl) var(--tk-gap-xl) 36px; display: flex; flex-direction: column; align-items: center; @@ -17,21 +17,21 @@ width: 88px; height: 88px; border-radius: $r-lg; - background: $pri-l; - margin-bottom: 20px; + background: var(--tk-pri-l); + margin-bottom: var(--tk-section-gap); } .dm-hero-icon-text { font-family: 'Georgia', 'Times New Roman', serif; font-size: var(--tk-font-hero); font-weight: bold; - color: $pri; + color: var(--tk-pri); } .dm-hero-title { @include section-title; font-size: var(--tk-font-num-lg); - margin-bottom: 8px; + margin-bottom: var(--tk-gap-xs); } .dm-hero-sub { @@ -41,14 +41,14 @@ /* ── card (standalone, used for date picker) ── */ .dm-card { - margin: 0 24px 20px; + margin: 0 var(--tk-gap-lg) var(--tk-section-gap); } .dm-card-header { display: flex; align-items: center; gap: 14px; - margin-bottom: 20px; + margin-bottom: var(--tk-section-gap); } .dm-card-title { @@ -71,12 +71,12 @@ align-items: center; background: $bg; border-radius: $r-sm; - padding: 22px 24px; + padding: 22px var(--tk-gap-lg); } .dm-date-value { font-size: var(--tk-font-body-lg); - color: $pri; + color: var(--tk-pri); @include serif-number; font-weight: bold; } @@ -91,7 +91,7 @@ /* ── collapsible group ── */ .dm-group { - margin: 0 24px 20px; + margin: 0 var(--tk-gap-lg) var(--tk-section-gap); overflow: hidden; } @@ -99,10 +99,10 @@ display: flex; align-items: center; justify-content: space-between; - padding: 24px 28px; + padding: var(--tk-gap-lg) var(--tk-card-padding-lg); &:active { - background: $bd-l; + opacity: var(--tk-touch-feedback-opacity); } } @@ -125,7 +125,7 @@ } .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 { @@ -134,7 +134,7 @@ /* ── inner field spacing (within groups) ── */ .dm-inner-field { - margin-bottom: 24px; + margin-bottom: var(--tk-gap-lg); &:last-child { margin-bottom: 0; @@ -145,7 +145,7 @@ .dm-bp-group { display: flex; align-items: flex-end; - gap: 12px; + gap: var(--tk-gap-sm); } .dm-bp-field { @@ -156,14 +156,14 @@ font-size: var(--tk-font-body); color: $tx2; display: block; - margin-bottom: 8px; + margin-bottom: var(--tk-gap-xs); } .dm-bp-divider { display: flex; flex-direction: column; align-items: center; - padding-bottom: 20px; + padding-bottom: var(--tk-section-gap); gap: 6px; } @@ -192,7 +192,7 @@ .dm-single-row { display: flex; align-items: center; - gap: 16px; + gap: var(--tk-gap-md); } .dm-input-flex { @@ -210,7 +210,7 @@ .dm-input-box { background: $bg; border-radius: $r-sm; - padding: 20px 24px; + padding: var(--tk-section-gap) var(--tk-gap-lg); font-size: var(--tk-font-body-lg); color: $tx; @include serif-number; @@ -230,7 +230,7 @@ .dm-field-warning { font-size: var(--tk-font-body); color: $wrn; - margin-top: 8px; + margin-top: var(--tk-gap-xs); display: block; } @@ -240,16 +240,16 @@ /* ── submit ── */ .dm-submit { - background: $pri; + background: var(--tk-pri); border-radius: $r; padding: 26px; text-align: center; - margin: 40px 24px 0; + margin: 40px var(--tk-gap-lg) 0; box-shadow: $shadow-md; transition: opacity 0.2s; &:active { - opacity: 0.85; + opacity: var(--tk-touch-feedback-opacity); } } @@ -268,8 +268,8 @@ /* ── reset ── */ .dm-reset { text-align: center; - padding: 24px; - margin-top: 12px; + padding: var(--tk-gap-lg); + margin-top: var(--tk-gap-sm); } .dm-reset-text { diff --git a/apps/miniprogram/src/pages/pkg-health/device-sync/index.scss b/apps/miniprogram/src/pages/pkg-health/device-sync/index.scss index db05bda..d6f7669 100644 --- a/apps/miniprogram/src/pages/pkg-health/device-sync/index.scss +++ b/apps/miniprogram/src/pages/pkg-health/device-sync/index.scss @@ -5,8 +5,8 @@ // ContentCard 已接管:sync-status-card/sync-result-card 背景/圆角/阴影 .sync-header { - background: $pri; - padding: 48px 32px 32px; + background: var(--tk-pri); + padding: var(--tk-gap-2xl) var(--tk-gap-xl) var(--tk-gap-xl); color: $card; } @@ -16,17 +16,17 @@ } .sync-section { - padding: 24px; + padding: var(--tk-gap-lg); } .sync-hero { display: flex; flex-direction: column; align-items: center; - padding: 48px 24px; + padding: var(--tk-gap-2xl) var(--tk-gap-lg); background: $card; border-radius: $r; - margin-bottom: 24px; + margin-bottom: var(--tk-gap-lg); box-shadow: $shadow-sm; } @@ -34,10 +34,10 @@ width: 80px; height: 80px; border-radius: 50%; - background: $pri-l; + background: var(--tk-pri-l); @include flex-center; - margin-bottom: 20px; - color: $pri; + margin-bottom: var(--tk-section-gap); + color: var(--tk-pri); font-family: 'Georgia', 'Times New Roman', serif; font-size: var(--tk-font-num-lg); font-weight: bold; @@ -45,7 +45,7 @@ .sync-hero-title { @include section-title; - margin-bottom: 8px; + margin-bottom: var(--tk-gap-xs); } .sync-hero-desc { @@ -55,20 +55,20 @@ .sync-action { @include flex-center; - background: $pri; + background: var(--tk-pri); border-radius: $r-sm; - padding: 20px 40px; - margin: 12px 0; + padding: var(--tk-section-gap) var(--tk-gap-2xl); + margin: var(--tk-gap-sm) 0; &--primary { flex: 1; - background: $pri; + background: var(--tk-pri); } &--danger { flex: 1; background: $dan; - margin-left: 16px; + margin-left: var(--tk-gap-md); } } @@ -79,7 +79,7 @@ } .sync-device-list { - margin-top: 16px; + margin-top: var(--tk-gap-md); } .sync-section-title { @@ -87,7 +87,7 @@ font-size: var(--tk-font-body-lg); font-weight: bold; color: $tx; - margin-bottom: 12px; + margin-bottom: var(--tk-gap-sm); display: block; } @@ -97,8 +97,8 @@ align-items: center; background: $card; border-radius: $r-sm; - padding: 24px; - margin-bottom: 12px; + padding: var(--tk-gap-lg); + margin-bottom: var(--tk-gap-sm); box-shadow: $shadow-sm; } @@ -127,14 +127,14 @@ .sync-status-card { display: flex; align-items: center; - margin-bottom: 16px; + margin-bottom: var(--tk-gap-md); } .sync-status-dot { width: 16px; height: 16px; border-radius: 50%; - margin-right: 16px; + margin-right: var(--tk-gap-md); background: $tx3; &--connected { @@ -150,8 +150,8 @@ .sync-readings-panel { background: $card; border-radius: $r-sm; - padding: 24px; - margin-bottom: 16px; + padding: var(--tk-gap-lg); + margin-bottom: var(--tk-gap-md); box-shadow: $shadow-sm; } @@ -159,7 +159,7 @@ display: flex; justify-content: space-between; align-items: center; - padding: 12px 0; + padding: var(--tk-gap-sm) 0; border-bottom: 1px solid $bd-l; &:last-child { @@ -175,13 +175,13 @@ .sync-reading-value { font-size: var(--tk-font-body-lg); font-weight: bold; - color: $pri; + color: var(--tk-pri); @include serif-number; } .sync-readings-count { display: block; - margin-top: 12px; + margin-top: var(--tk-gap-sm); font-size: var(--tk-font-body); color: var(--tk-text-secondary); text-align: center; @@ -189,12 +189,12 @@ .sync-actions-row { display: flex; - gap: 12px; + gap: var(--tk-gap-sm); } .sync-error { - margin: 24px; - padding: 20px 24px; + margin: var(--tk-gap-lg); + padding: var(--tk-section-gap) var(--tk-gap-lg); background: $dan-l; border-radius: $r-sm; } @@ -218,7 +218,7 @@ display: flex; flex-direction: column; align-items: center; - margin-bottom: 24px; + margin-bottom: var(--tk-gap-lg); box-shadow: $shadow-sm; } @@ -232,12 +232,12 @@ font-family: 'Georgia', 'Times New Roman', serif; font-size: var(--tk-font-num-lg); font-weight: bold; - margin-bottom: 16px; + margin-bottom: var(--tk-gap-md); } .sync-result-title { @include section-title; - margin-bottom: 8px; + margin-bottom: var(--tk-gap-xs); } .sync-result-count { diff --git a/apps/miniprogram/src/pages/pkg-health/input/index.scss b/apps/miniprogram/src/pages/pkg-health/input/index.scss index a83cfef..61259e5 100644 --- a/apps/miniprogram/src/pages/pkg-health/input/index.scss +++ b/apps/miniprogram/src/pages/pkg-health/input/index.scss @@ -6,7 +6,7 @@ /* ── hero ── */ .input-hero { - padding: 48px 32px 36px; + padding: var(--tk-gap-2xl) var(--tk-gap-xl) 36px; display: flex; flex-direction: column; align-items: center; @@ -17,21 +17,21 @@ width: 88px; height: 88px; border-radius: $r-lg; - background: $pri-l; - margin-bottom: 20px; + background: var(--tk-pri-l); + margin-bottom: var(--tk-section-gap); } .input-hero-icon-text { font-family: 'Georgia', 'Times New Roman', serif; font-size: var(--tk-font-hero); font-weight: bold; - color: $pri; + color: var(--tk-pri); } .input-hero-title { @include section-title; font-size: var(--tk-font-num-lg); - margin-bottom: 8px; + margin-bottom: var(--tk-gap-xs); } .input-hero-sub { @@ -44,18 +44,18 @@ display: flex; align-items: center; justify-content: space-between; - margin: 0 24px 20px; - border: 1px dashed $pri; + margin: 0 var(--tk-gap-lg) var(--tk-section-gap); + border: 1px dashed var(--tk-pri); &:active { - opacity: 0.7; + opacity: var(--tk-touch-feedback-opacity); } } .input-sync-entry-text { font-size: var(--tk-font-body-lg); font-weight: 600; - color: $pri; + color: var(--tk-pri); } .input-sync-entry-hint { @@ -65,14 +65,14 @@ /* ── card ── */ .input-card { - margin: 0 24px 20px; + margin: 0 var(--tk-gap-lg) var(--tk-section-gap); } .input-card-header { display: flex; align-items: center; gap: 14px; - margin-bottom: 20px; + margin-bottom: var(--tk-section-gap); } .input-card-indicator { @@ -104,7 +104,7 @@ align-items: center; background: $bg; border-radius: $r-sm; - padding: 22px 24px; + padding: 22px var(--tk-gap-lg); } .input-picker-value { @@ -127,7 +127,7 @@ font-size: var(--tk-font-body-lg); font-weight: bold; color: $tx; - margin-bottom: 16px; + margin-bottom: var(--tk-gap-md); display: block; } @@ -135,7 +135,7 @@ .input-bp-group { display: flex; align-items: flex-end; - gap: 12px; + gap: var(--tk-gap-sm); } .input-bp-field { @@ -146,14 +146,14 @@ font-size: var(--tk-font-body); color: $tx2; display: block; - margin-bottom: 8px; + margin-bottom: var(--tk-gap-xs); } .input-bp-divider { display: flex; flex-direction: column; align-items: center; - padding-bottom: 20px; + padding-bottom: var(--tk-section-gap); gap: 6px; } @@ -174,7 +174,7 @@ .input-field-box { background: $bg; border-radius: $r-sm; - padding: 20px 24px; + padding: var(--tk-section-gap) var(--tk-gap-lg); font-size: var(--tk-font-body-lg); color: $tx; @include serif-number; @@ -195,16 +195,16 @@ /* ── submit ── */ .input-submit { - background: $pri; + background: var(--tk-pri); border-radius: $r; padding: 26px; text-align: center; - margin: 48px 24px 0; + margin: var(--tk-gap-2xl) var(--tk-gap-lg) 0; box-shadow: $shadow-md; transition: opacity 0.2s; &:active { - opacity: 0.85; + opacity: var(--tk-touch-feedback-opacity); } } diff --git a/apps/miniprogram/src/pages/pkg-health/trend/index.scss b/apps/miniprogram/src/pages/pkg-health/trend/index.scss index 036f33a..d0b27bc 100644 --- a/apps/miniprogram/src/pages/pkg-health/trend/index.scss +++ b/apps/miniprogram/src/pages/pkg-health/trend/index.scss @@ -21,7 +21,7 @@ width: 88px; height: 88px; border-radius: $r-lg; - background: $pri-l; + background: var(--tk-pri-l); margin-bottom: 20px; } @@ -29,7 +29,7 @@ font-family: 'Georgia', 'Times New Roman', serif; font-size: var(--tk-font-hero); font-weight: bold; - color: $pri; + color: var(--tk-pri); } .trend-hero-title { @@ -131,7 +131,7 @@ .trend-item-value { font-size: var(--tk-font-body-lg); - color: $pri; + color: var(--tk-pri); @include serif-number; font-weight: bold; } diff --git a/apps/miniprogram/src/pages/pkg-mall/detail/index.scss b/apps/miniprogram/src/pages/pkg-mall/detail/index.scss index 73446e9..4597ea2 100644 --- a/apps/miniprogram/src/pages/pkg-mall/detail/index.scss +++ b/apps/miniprogram/src/pages/pkg-mall/detail/index.scss @@ -6,9 +6,9 @@ /* ===== 余额卡片 ===== */ .balance-card { background: $card; - margin: 20px 24px 16px; + margin: var(--tk-section-gap) var(--tk-page-padding) var(--tk-gap-md); border-radius: $r-lg; - padding: 32px; + padding: var(--tk-gap-xl); box-shadow: $shadow-sm; } @@ -16,16 +16,16 @@ font-size: var(--tk-font-h2); color: $tx2; display: block; - margin-bottom: 8px; + margin-bottom: var(--tk-gap-xs); } .balance-value { @include serif-number; font-size: var(--tk-font-hero); font-weight: bold; - color: $pri; + color: var(--tk-pri); display: block; - margin-bottom: 28px; + margin-bottom: var(--tk-card-padding-lg); letter-spacing: -1px; } @@ -34,7 +34,7 @@ align-items: center; background: $bg; border-radius: $r; - padding: 20px 0; + padding: var(--tk-section-gap) 0; } .stat-item { @@ -48,7 +48,7 @@ @include serif-number; font-size: var(--tk-font-num); font-weight: bold; - margin-bottom: 4px; + margin-bottom: var(--tk-gap-2xs); &.stat-earn { color: $acc; @@ -78,14 +78,14 @@ .type-tabs { display: flex; gap: 0; - padding: 0 24px; - margin-bottom: 16px; + padding: 0 var(--tk-page-padding); + margin-bottom: var(--tk-gap-md); } .type-tab { @include flex-center; flex: 1; - padding: 16px 0; + padding: var(--tk-gap-md) 0; position: relative; &.active::after { @@ -96,7 +96,7 @@ transform: translateX(-50%); width: 40px; height: 4px; - background: $pri; + background: var(--tk-pri); border-radius: $r-xs; } } @@ -106,14 +106,14 @@ color: $tx3; .type-tab.active & { - color: $pri; + color: var(--tk-pri); font-weight: bold; } } /* ===== 交易列表 ===== */ .transaction-list { - padding: 0 24px; + padding: 0 var(--tk-page-padding); } .transaction-item { @@ -121,8 +121,8 @@ align-items: center; background: $card; border-radius: $r; - padding: 24px; - margin-bottom: 12px; + padding: var(--tk-card-padding); + margin-bottom: var(--tk-gap-sm); box-shadow: $shadow-sm; } @@ -131,7 +131,7 @@ height: 64px; border-radius: $r; @include flex-center; - margin-right: 20px; + margin-right: var(--tk-section-gap); flex-shrink: 0; &.tx-badge-earn { @@ -174,7 +174,7 @@ font-size: var(--tk-font-body-lg); color: $tx; display: block; - margin-bottom: 4px; + margin-bottom: var(--tk-gap-2xs); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; @@ -190,7 +190,7 @@ display: flex; flex-direction: column; align-items: flex-end; - margin-left: 16px; + margin-left: var(--tk-gap-md); flex-shrink: 0; } @@ -198,7 +198,7 @@ @include serif-number; font-size: var(--tk-font-num); font-weight: bold; - margin-bottom: 4px; + margin-bottom: var(--tk-gap-2xs); &.tx-amount-positive { color: $acc; diff --git a/apps/miniprogram/src/pages/pkg-mall/exchange/index.scss b/apps/miniprogram/src/pages/pkg-mall/exchange/index.scss index 285e0e3..dfd5406 100644 --- a/apps/miniprogram/src/pages/pkg-mall/exchange/index.scss +++ b/apps/miniprogram/src/pages/pkg-mall/exchange/index.scss @@ -10,9 +10,9 @@ .product-card { display: flex; align-items: center; - padding: 32px 24px; + padding: var(--tk-gap-xl) var(--tk-gap-lg); background: $card; - margin: 20px 24px 16px; + margin: var(--tk-section-gap) var(--tk-gap-lg) var(--tk-gap-md); border-radius: $r-lg; box-shadow: $shadow-sm; } @@ -22,7 +22,7 @@ height: 128px; border-radius: $r; @include flex-center; - margin-right: 24px; + margin-right: var(--tk-gap-lg); flex-shrink: 0; &--physical { @@ -30,11 +30,11 @@ } &--service { - background: $pri; + background: var(--tk-pri); } &--privilege { - background: $pri-d; + background: var(--tk-pri-d); } } @@ -55,20 +55,20 @@ font-weight: bold; color: $tx; display: block; - margin-bottom: 12px; + margin-bottom: var(--tk-gap-sm); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .product-type-tag { - @include tag($pri-l, $pri-d); + @include tag(var(--tk-pri-l), var(--tk-pri-d)); } /* ===== 兑换明细 ===== */ .detail-section { - padding: 0 24px; - margin-bottom: 16px; + padding: 0 var(--tk-gap-lg); + margin-bottom: var(--tk-gap-md); } .detail-section-title { @@ -79,14 +79,14 @@ background: $card; border-radius: $r; box-shadow: $shadow-sm; - padding: 0 24px; + padding: 0 var(--tk-gap-lg); } .detail-row { display: flex; justify-content: space-between; align-items: center; - padding: 24px 0; + padding: var(--tk-gap-lg) 0; border-bottom: 1px solid $bd-l; &.last { @@ -106,7 +106,7 @@ font-weight: bold; &.detail-cost { - color: $pri; + color: var(--tk-pri); font-size: var(--tk-font-num-lg); } @@ -122,8 +122,8 @@ /* ===== 温馨提示 ===== */ .notice-section { background: $card; - padding: 24px; - margin: 0 24px; + padding: var(--tk-gap-lg); + margin: 0 var(--tk-gap-lg); border-radius: $r; box-shadow: $shadow-sm; } @@ -131,7 +131,7 @@ .notice-title { @include section-title; font-size: var(--tk-font-body-lg); - margin-bottom: 12px; + margin-bottom: var(--tk-gap-sm); } .notice-text { @@ -139,7 +139,7 @@ color: $tx3; display: block; line-height: 1.7; - margin-bottom: 4px; + margin-bottom: var(--tk-gap-2xs); } /* ===== 底部操作栏 ===== */ @@ -150,10 +150,10 @@ right: 0; display: flex; align-items: center; - padding: 16px 24px; - padding-bottom: calc(16px + env(safe-area-inset-bottom)); + padding: var(--tk-gap-md) var(--tk-gap-lg); + padding-bottom: calc(var(--tk-gap-md) + env(safe-area-inset-bottom)); 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; } @@ -172,18 +172,18 @@ @include serif-number; font-size: var(--tk-font-num-lg); font-weight: bold; - color: $pri; + color: var(--tk-pri); } .footer-cost-unit { font-size: var(--tk-font-body); color: $tx2; - margin-left: 4px; + margin-left: var(--tk-gap-2xs); } .confirm-btn { - background: $pri; - padding: 20px 48px; + background: var(--tk-pri); + padding: var(--tk-section-gap) var(--tk-gap-2xl); border-radius: $r-pill; transition: opacity 0.2s; diff --git a/apps/miniprogram/src/pages/pkg-mall/orders/index.scss b/apps/miniprogram/src/pages/pkg-mall/orders/index.scss index 6010e8a..0747fa2 100644 --- a/apps/miniprogram/src/pages/pkg-mall/orders/index.scss +++ b/apps/miniprogram/src/pages/pkg-mall/orders/index.scss @@ -6,11 +6,11 @@ /* ===== 订单列表 ===== */ .order-list { - padding: 0 24px; + padding: 0 var(--tk-gap-lg); } .order-card { - margin-bottom: 16px; + margin-bottom: var(--tk-gap-md); overflow: hidden; } @@ -18,7 +18,7 @@ display: flex; justify-content: space-between; 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; } @@ -34,9 +34,9 @@ } .order-status-tag { - padding: 4px 16px; + padding: var(--tk-gap-2xs) var(--tk-gap-md); border-radius: $r-pill; - margin-left: 12px; + margin-left: var(--tk-gap-sm); flex-shrink: 0; &--pending { @@ -62,14 +62,14 @@ } .order-body { - padding: 16px 24px 20px; + padding: var(--tk-gap-md) var(--tk-gap-lg) var(--tk-section-gap); } .order-row { display: flex; justify-content: space-between; align-items: center; - padding: 8px 0; + padding: var(--tk-gap-xs) 0; } .order-row-label { @@ -83,7 +83,7 @@ color: $tx; &.order-cost { - color: $pri; + color: var(--tk-pri); font-weight: bold; } } @@ -92,22 +92,22 @@ .order-qrcode { display: flex; align-items: center; - padding: 16px; - margin-top: 12px; - background: $pri-l; + padding: var(--tk-gap-md); + margin-top: var(--tk-gap-sm); + background: var(--tk-pri-l); border-radius: $r-sm; } .qrcode-label { font-size: var(--tk-font-h2); color: $tx3; - margin-right: 8px; + margin-right: var(--tk-gap-xs); } .qrcode-value { @include serif-number; font-size: var(--tk-font-h2); - color: $pri-d; + color: var(--tk-pri-d); font-weight: bold; flex: 1; overflow: hidden; @@ -117,7 +117,7 @@ .qrcode-tap { font-size: var(--tk-font-body); - color: $pri; - margin-left: 8px; + color: var(--tk-pri); + margin-left: var(--tk-gap-xs); flex-shrink: 0; } diff --git a/apps/miniprogram/src/pages/pkg-profile/consents/index.scss b/apps/miniprogram/src/pages/pkg-profile/consents/index.scss index 7b2b904..f657482 100644 --- a/apps/miniprogram/src/pages/pkg-profile/consents/index.scss +++ b/apps/miniprogram/src/pages/pkg-profile/consents/index.scss @@ -5,19 +5,19 @@ .page-title { @include section-title; - padding-left: 4px; + padding-left: var(--tk-gap-2xs); } .consent-list { display: flex; flex-direction: column; - gap: 16px; + gap: var(--tk-gap-md); } .consent-card { background: $card; border-radius: $r; - padding: 28px; + padding: var(--tk-card-padding-lg); box-shadow: $shadow-sm; } @@ -25,7 +25,7 @@ display: flex; justify-content: space-between; align-items: center; - margin-bottom: 12px; + margin-bottom: var(--tk-gap-sm); } .consent-card__type { @@ -52,19 +52,19 @@ font-size: var(--tk-font-h2); color: $tx2; display: block; - margin-bottom: 4px; + margin-bottom: var(--tk-gap-2xs); font-variant-numeric: tabular-nums; } .revoke-btn { - margin-top: 16px; - padding: 12px 0; + margin-top: var(--tk-gap-md); + padding: var(--tk-gap-sm) 0; text-align: center; border-radius: $r-sm; border: 1px solid $dan; &:active { - background: $dan-l; + opacity: var(--tk-touch-feedback-opacity); } &--disabled { diff --git a/apps/miniprogram/src/pages/pkg-profile/diagnoses/index.scss b/apps/miniprogram/src/pages/pkg-profile/diagnoses/index.scss index ccb25fa..5b79ef6 100644 --- a/apps/miniprogram/src/pages/pkg-profile/diagnoses/index.scss +++ b/apps/miniprogram/src/pages/pkg-profile/diagnoses/index.scss @@ -8,21 +8,21 @@ font-size: var(--tk-font-num); font-weight: bold; color: $tx; - margin-bottom: 20px; + margin-bottom: var(--tk-section-gap); display: block; - padding-left: 4px; + padding-left: var(--tk-gap-2xs); } .diagnosis-list { display: flex; flex-direction: column; - gap: 16px; + gap: var(--tk-gap-md); } .diagnosis-card { background: $card; border-radius: $r; - padding: 28px; + padding: var(--tk-card-padding-lg); box-shadow: $shadow-sm; } @@ -30,7 +30,7 @@ display: flex; justify-content: space-between; align-items: center; - margin-bottom: 12px; + margin-bottom: var(--tk-gap-sm); } .diagnosis-card__name { @@ -38,7 +38,7 @@ font-weight: bold; color: $tx; flex: 1; - margin-right: 12px; + margin-right: var(--tk-gap-sm); } .diagnosis-card__status { @@ -60,12 +60,12 @@ .diagnosis-card__meta { display: flex; align-items: center; - gap: 12px; - margin-bottom: 8px; + gap: var(--tk-gap-sm); + margin-bottom: var(--tk-gap-xs); } .diagnosis-card__type { - @include tag($pri-l, $pri-d); + @include tag(var(--tk-pri-l), var(--tk-pri-d)); &.secondary { @include tag($bd-l, $tx2); @@ -92,5 +92,5 @@ font-size: var(--tk-font-body); color: $tx2; display: block; - margin-top: 8px; + margin-top: var(--tk-gap-xs); } diff --git a/apps/miniprogram/src/pages/pkg-profile/dialysis-prescriptions/detail/index.scss b/apps/miniprogram/src/pages/pkg-profile/dialysis-prescriptions/detail/index.scss index a2a439e..92a8841 100644 --- a/apps/miniprogram/src/pages/pkg-profile/dialysis-prescriptions/detail/index.scss +++ b/apps/miniprogram/src/pages/pkg-profile/dialysis-prescriptions/detail/index.scss @@ -5,7 +5,7 @@ display: flex; justify-content: space-between; align-items: center; - margin-bottom: 8px; + margin-bottom: var(--tk-gap-xs); } .detail-title { @@ -17,7 +17,7 @@ .status-tag { display: inline-block; - padding: 4px 12px; + padding: var(--tk-gap-2xs) var(--tk-gap-sm); border-radius: $r-xs; font-size: var(--tk-font-body); font-weight: 500; @@ -48,14 +48,14 @@ font-weight: bold; color: $tx; display: block; - margin-bottom: 16px; + margin-bottom: var(--tk-gap-md); } .detail-row { display: flex; justify-content: space-between; align-items: flex-start; - padding: 12px 0; + padding: var(--tk-gap-sm) 0; border-bottom: 1px solid $bd-l; &:last-child { @@ -74,7 +74,7 @@ color: $tx; text-align: right; flex: 1; - margin-left: 24px; + margin-left: var(--tk-gap-lg); @include serif-number; } @@ -88,7 +88,7 @@ display: flex; justify-content: center; align-items: center; - padding: 120px 0; + padding: var(--tk-gap-2xl) 0; } .empty-text { diff --git a/apps/miniprogram/src/pages/pkg-profile/dialysis-prescriptions/index.scss b/apps/miniprogram/src/pages/pkg-profile/dialysis-prescriptions/index.scss index b81ef49..b15fb10 100644 --- a/apps/miniprogram/src/pages/pkg-profile/dialysis-prescriptions/index.scss +++ b/apps/miniprogram/src/pages/pkg-profile/dialysis-prescriptions/index.scss @@ -5,23 +5,23 @@ .page-title { @include section-title; - padding-left: 4px; + padding-left: var(--tk-gap-2xs); } .prescription-list { display: flex; flex-direction: column; - gap: 16px; + gap: var(--tk-gap-md); } .prescription-card { background: $card; border-radius: $r; - padding: 28px; + padding: var(--tk-card-padding-lg); box-shadow: $shadow-sm; &:active { - box-shadow: $shadow-md; + opacity: var(--tk-touch-feedback-opacity); } } @@ -29,7 +29,7 @@ display: flex; justify-content: space-between; align-items: center; - margin-bottom: 12px; + margin-bottom: var(--tk-gap-sm); } .prescription-model { @@ -52,8 +52,8 @@ .prescription-meta { display: flex; - gap: 24px; - margin-bottom: 8px; + gap: var(--tk-gap-lg); + margin-bottom: var(--tk-gap-xs); } .meta-item { diff --git a/apps/miniprogram/src/pages/pkg-profile/dialysis-records/detail/index.scss b/apps/miniprogram/src/pages/pkg-profile/dialysis-records/detail/index.scss index 999cd03..5c06ccc 100644 --- a/apps/miniprogram/src/pages/pkg-profile/dialysis-records/detail/index.scss +++ b/apps/miniprogram/src/pages/pkg-profile/dialysis-records/detail/index.scss @@ -5,7 +5,7 @@ display: flex; justify-content: space-between; align-items: center; - margin-bottom: 8px; + margin-bottom: var(--tk-gap-xs); } .detail-title { @@ -17,7 +17,7 @@ .status-tag { display: inline-block; - padding: 4px 12px; + padding: var(--tk-gap-2xs) var(--tk-gap-sm); border-radius: $r-xs; font-size: var(--tk-font-body); font-weight: 500; @@ -25,8 +25,8 @@ color: $tx3; &.completed { - background: $pri-l; - color: $pri; + background: var(--tk-pri-l); + color: var(--tk-pri); } &.reviewed { @@ -45,7 +45,7 @@ font-size: var(--tk-font-h2); color: var(--tk-text-secondary); display: block; - margin-top: 8px; + margin-top: var(--tk-gap-xs); @include serif-number; } @@ -55,14 +55,14 @@ font-weight: bold; color: $tx; display: block; - margin-bottom: 16px; + margin-bottom: var(--tk-gap-md); } .detail-row { display: flex; justify-content: space-between; align-items: flex-start; - padding: 12px 0; + padding: var(--tk-gap-sm) 0; border-bottom: 1px solid $bd-l; &:last-child { @@ -81,7 +81,7 @@ color: $tx; text-align: right; flex: 1; - margin-left: 24px; + margin-left: var(--tk-gap-lg); @include serif-number; } @@ -89,7 +89,7 @@ display: flex; justify-content: center; align-items: center; - padding: 120px 0; + padding: var(--tk-gap-2xl) 0; } .empty-text { diff --git a/apps/miniprogram/src/pages/pkg-profile/dialysis-records/index.scss b/apps/miniprogram/src/pages/pkg-profile/dialysis-records/index.scss index 29c140f..ddbc601 100644 --- a/apps/miniprogram/src/pages/pkg-profile/dialysis-records/index.scss +++ b/apps/miniprogram/src/pages/pkg-profile/dialysis-records/index.scss @@ -5,23 +5,23 @@ .page-title { @include section-title; - padding-left: 4px; + padding-left: var(--tk-gap-2xs); } .record-list { display: flex; flex-direction: column; - gap: 16px; + gap: var(--tk-gap-md); } .record-card { background: $card; border-radius: $r; - padding: 28px; + padding: var(--tk-card-padding-lg); box-shadow: $shadow-sm; &:active { - box-shadow: $shadow-md; + opacity: var(--tk-touch-feedback-opacity); } } @@ -29,11 +29,11 @@ display: flex; justify-content: space-between; align-items: center; - margin-bottom: 12px; + margin-bottom: var(--tk-gap-sm); } .type-tag { - @include tag($pri-l, $pri-d); + @include tag(var(--tk-pri-l), var(--tk-pri-d)); &.hdf { @include tag($acc-l, $acc); @@ -48,7 +48,7 @@ @include tag($bd-l, $tx3); &.completed { - @include tag($pri-l, $pri); + @include tag(var(--tk-pri-l), var(--tk-pri)); } &.reviewed { @@ -61,13 +61,13 @@ font-size: var(--tk-font-h1); color: $tx2; display: block; - margin-bottom: 8px; + margin-bottom: var(--tk-gap-xs); } .weight-row { display: flex; - gap: 24px; - margin-bottom: 4px; + gap: var(--tk-gap-lg); + margin-bottom: var(--tk-gap-2xs); } .weight-item { diff --git a/apps/miniprogram/src/pages/pkg-profile/elder-mode/index.scss b/apps/miniprogram/src/pages/pkg-profile/elder-mode/index.scss index e103d21..30ba7e3 100644 --- a/apps/miniprogram/src/pages/pkg-profile/elder-mode/index.scss +++ b/apps/miniprogram/src/pages/pkg-profile/elder-mode/index.scss @@ -12,8 +12,8 @@ .elder-mode-header { display: flex; align-items: center; - gap: 16px; - margin-bottom: 20px; + gap: var(--tk-gap-md); + margin-bottom: var(--tk-section-gap); } .elder-mode-icon { @@ -37,7 +37,7 @@ font-weight: 700; color: $tx; display: block; - margin-bottom: 4px; + margin-bottom: var(--tk-gap-2xs); } .elder-mode-desc { @@ -49,7 +49,7 @@ display: flex; align-items: center; justify-content: space-between; - padding: 16px 0 0; + padding: var(--tk-gap-md) 0 0; border-top: 1px solid $bd-l; } @@ -89,7 +89,7 @@ /* --- 效果预览 --- */ .elder-mode-preview { - margin-top: 4px; + margin-top: var(--tk-gap-2xs); } .elder-mode-preview-title { @@ -97,8 +97,8 @@ font-weight: 600; color: $tx2; display: block; - margin-bottom: 10px; - padding-left: 4px; + margin-bottom: var(--tk-gap-sm); + padding-left: var(--tk-gap-2xs); } .elder-mode-preview-card { @@ -109,7 +109,7 @@ font-size: var(--tk-font-body-sm); color: $tx; display: block; - margin-bottom: 8px; + margin-bottom: var(--tk-gap-xs); transition: font-size 0.25s; &--large { diff --git a/apps/miniprogram/src/pages/pkg-profile/events/index.scss b/apps/miniprogram/src/pages/pkg-profile/events/index.scss index 84048ab..194b338 100644 --- a/apps/miniprogram/src/pages/pkg-profile/events/index.scss +++ b/apps/miniprogram/src/pages/pkg-profile/events/index.scss @@ -3,12 +3,12 @@ // PageShell 已接管:min-height, background, padding .events-page { - padding-bottom: 120px; + padding-bottom: var(--tk-tabbar-space); } .events-header { - background: $pri; - padding: 48px 32px 32px; + background: var(--tk-pri); + padding: var(--tk-gap-2xl) var(--tk-gap-xl) var(--tk-gap-xl); color: $card; &__title { @@ -16,7 +16,7 @@ font-size: var(--tk-font-h1); font-weight: bold; display: block; - margin-bottom: 8px; + margin-bottom: var(--tk-gap-xs); } &__subtitle { @@ -26,23 +26,23 @@ } .event-list { - padding: 24px; + padding: var(--tk-page-padding); display: flex; flex-direction: column; - gap: 20px; + gap: var(--tk-section-gap); } .event-card { background: $card; border-radius: $r; - padding: 28px; + padding: var(--tk-card-padding-lg); box-shadow: $shadow-sm; &__header { display: flex; justify-content: space-between; align-items: center; - margin-bottom: 16px; + margin-bottom: var(--tk-gap-md); } &__status { @@ -51,7 +51,7 @@ } &__status--published { - @include tag($pri-l, $pri); + @include tag(var(--tk-pri-l), var(--tk-pri)); } &__status--ongoing { @@ -79,22 +79,22 @@ font-weight: bold; color: $tx; display: block; - margin-bottom: 12px; + margin-bottom: var(--tk-gap-sm); } &__desc { font-size: var(--tk-font-h1); color: $tx2; display: block; - margin-bottom: 16px; + margin-bottom: var(--tk-gap-md); line-height: 1.5; } &__info { display: flex; flex-direction: column; - gap: 8px; - margin-bottom: 16px; + gap: var(--tk-gap-xs); + margin-bottom: var(--tk-gap-md); } &__date { @@ -111,7 +111,7 @@ display: flex; justify-content: space-between; align-items: center; - padding-top: 16px; + padding-top: var(--tk-gap-md); border-top: 1px solid $bd-l; } @@ -122,9 +122,9 @@ } &__btn { - background: $pri; + background: var(--tk-pri); border-radius: $r-sm; - padding: 16px 32px; + padding: var(--tk-gap-md) var(--tk-gap-xl); &--disabled { background: $bd; diff --git a/apps/miniprogram/src/pages/pkg-profile/family-add/index.scss b/apps/miniprogram/src/pages/pkg-profile/family-add/index.scss index 58531c7..3e5eea6 100644 --- a/apps/miniprogram/src/pages/pkg-profile/family-add/index.scss +++ b/apps/miniprogram/src/pages/pkg-profile/family-add/index.scss @@ -8,13 +8,13 @@ .page-title { @include section-title; - padding-left: 4px; + padding-left: var(--tk-gap-2xs); } .form-card { background: $card; border-radius: $r; - padding: 4px 28px; + padding: var(--tk-gap-2xs) var(--tk-card-padding-lg); box-shadow: $shadow-sm; } @@ -22,7 +22,7 @@ display: flex; align-items: center; justify-content: space-between; - padding: 28px 0; + padding: var(--tk-card-padding-lg) 0; border-bottom: 1px solid $bd-l; &:last-child { @@ -63,7 +63,7 @@ .form-picker-text { font-size: var(--tk-font-body-lg); color: $tx; - margin-right: 10px; + margin-right: var(--tk-gap-sm); &.placeholder { color: $tx3; @@ -81,10 +81,10 @@ bottom: 0; left: 0; right: 0; - background: $pri; - padding: 28px; + background: var(--tk-pri); + padding: var(--tk-card-padding-lg); text-align: center; - box-shadow: 0 -2px 12px rgba(196, 98, 58, 0.15); + box-shadow: 0 -2px 12px rgba($pri, 0.15); &.disabled { opacity: 0.5; diff --git a/apps/miniprogram/src/pages/pkg-profile/family/index.scss b/apps/miniprogram/src/pages/pkg-profile/family/index.scss index 4a5464f..55099d2 100644 --- a/apps/miniprogram/src/pages/pkg-profile/family/index.scss +++ b/apps/miniprogram/src/pages/pkg-profile/family/index.scss @@ -8,13 +8,13 @@ .family-page-title { @include section-title; - padding-left: 4px; + padding-left: var(--tk-gap-2xs); } .family-list { display: flex; flex-direction: column; - gap: 16px; + gap: var(--tk-gap-md); } .family-item { @@ -22,12 +22,12 @@ align-items: center; background: $card; border-radius: $r; - padding: 24px; + padding: var(--tk-card-padding); box-shadow: $shadow-sm; transition: box-shadow 0.2s; &:active { - box-shadow: $shadow-md; + opacity: var(--tk-touch-feedback-opacity); } &.active { @@ -40,16 +40,16 @@ width: 80px; height: 80px; border-radius: $r; - background: $pri-l; + background: var(--tk-pri-l); flex-shrink: 0; - margin-right: 20px; + margin-right: var(--tk-section-gap); } .family-avatar-text { font-family: 'Georgia', 'Times New Roman', serif; font-size: var(--tk-font-num-lg); font-weight: bold; - color: $pri-d; + color: var(--tk-pri-d); } .family-info { @@ -62,8 +62,8 @@ .family-name-row { display: flex; align-items: center; - gap: 10px; - margin-bottom: 8px; + gap: var(--tk-gap-sm); + margin-bottom: var(--tk-gap-xs); } .family-name { @@ -73,7 +73,7 @@ } .family-current-tag { - @include tag($pri, $white); + @include tag(var(--tk-pri), $white); font-size: var(--tk-font-body-sm); padding: 2px 10px; } @@ -81,11 +81,11 @@ .family-meta { display: flex; align-items: center; - gap: 12px; + gap: var(--tk-gap-sm); } .family-relation-tag { - @include tag($pri-l, $pri-d); + @include tag(var(--tk-pri-l), var(--tk-pri-d)); font-size: var(--tk-font-body); padding: 2px 12px; } @@ -97,15 +97,15 @@ .family-edit { flex-shrink: 0; - margin-left: 16px; - padding: 14px 24px; + margin-left: var(--tk-gap-md); + padding: var(--tk-gap-md) var(--tk-gap-lg); border: 1px solid $bd; border-radius: $r-pill; min-height: 48px; @include flex-center; &:active { - background: $bd-l; + opacity: var(--tk-touch-feedback-opacity); } } @@ -119,10 +119,10 @@ bottom: 0; left: 0; right: 0; - background: $pri; - padding: 28px; + background: var(--tk-pri); + padding: var(--tk-card-padding-lg); text-align: center; - box-shadow: 0 -2px 12px rgba(196, 98, 58, 0.15); + box-shadow: 0 -2px 12px rgba($pri, 0.15); } .family-add-text { diff --git a/apps/miniprogram/src/pages/pkg-profile/followups/detail/index.scss b/apps/miniprogram/src/pages/pkg-profile/followups/detail/index.scss index cb31462..cb67e16 100644 --- a/apps/miniprogram/src/pages/pkg-profile/followups/detail/index.scss +++ b/apps/miniprogram/src/pages/pkg-profile/followups/detail/index.scss @@ -4,13 +4,13 @@ .detail-title { @include section-title; font-size: var(--tk-font-num-lg); - margin-bottom: 20px; + margin-bottom: var(--tk-section-gap); } .detail-row { display: flex; justify-content: space-between; - padding: 12px 0; + padding: var(--tk-gap-sm) 0; border-bottom: 1px solid $bd-l; &:last-child { @@ -33,8 +33,8 @@ } .countdown { - margin-top: 12px; - padding: 12px 16px; + margin-top: var(--tk-gap-sm); + padding: var(--tk-gap-sm) var(--tk-gap-md); background: $wrn-l; border-radius: $r-sm; } @@ -52,8 +52,8 @@ } .detail-desc { - margin-top: 16px; - padding: 20px; + margin-top: var(--tk-gap-md); + padding: var(--tk-section-gap); background: $bd-l; border-radius: $r-sm; } @@ -66,7 +66,7 @@ .section-title { @include section-title; - margin-bottom: 16px; + margin-bottom: var(--tk-gap-md); } .submit-textarea { @@ -76,21 +76,21 @@ color: $tx; background: $bg; border-radius: $r-sm; - padding: 20px; + padding: var(--tk-section-gap); box-sizing: border-box; border: none; outline: none; - margin-bottom: 20px; + margin-bottom: var(--tk-section-gap); } .submit-btn { - background: $pri; + background: var(--tk-pri); border-radius: $r; - padding: 24px; + padding: var(--tk-gap-lg); text-align: center; &:active { - opacity: 0.85; + opacity: var(--tk-touch-feedback-opacity); } &.disabled { @@ -107,7 +107,7 @@ .loading-state, .empty-state { @include flex-center; - padding: 120px 0; + padding: var(--tk-tabbar-space) 0; } .loading-text, diff --git a/apps/miniprogram/src/pages/pkg-profile/followups/index.scss b/apps/miniprogram/src/pages/pkg-profile/followups/index.scss index 4cdc77d..7c7a027 100644 --- a/apps/miniprogram/src/pages/pkg-profile/followups/index.scss +++ b/apps/miniprogram/src/pages/pkg-profile/followups/index.scss @@ -16,17 +16,17 @@ flex-direction: column; justify-content: center; align-items: center; - padding: 24px 0 20px; + padding: var(--tk-gap-lg) 0 var(--tk-section-gap); position: relative; } .tab-text { font-size: var(--tk-font-body-lg); color: $tx2; - margin-bottom: 8px; + margin-bottom: var(--tk-gap-xs); .tab-item.active & { - color: $pri; + color: var(--tk-pri); font-weight: bold; } } @@ -34,25 +34,25 @@ .tab-indicator { width: 32px; height: 4px; - background: $pri; + background: var(--tk-pri); border-radius: $r-xs; } .task-list { - padding: 24px; + padding: var(--tk-gap-lg); display: flex; flex-direction: column; - gap: 16px; + gap: var(--tk-gap-md); } .task-card { background: $card; border-radius: $r; - padding: 28px; + padding: var(--tk-card-padding-lg); box-shadow: $shadow-sm; &:active { - box-shadow: $shadow-md; + opacity: var(--tk-touch-feedback-opacity); } } @@ -60,7 +60,7 @@ display: flex; justify-content: space-between; align-items: center; - margin-bottom: 12px; + margin-bottom: var(--tk-gap-sm); } .task-name { @@ -90,7 +90,7 @@ font-size: var(--tk-font-h1); color: $tx2; display: block; - margin-bottom: 8px; + margin-bottom: var(--tk-gap-xs); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; diff --git a/apps/miniprogram/src/pages/pkg-profile/health-records/index.scss b/apps/miniprogram/src/pages/pkg-profile/health-records/index.scss index ac71eb5..9fb1a64 100644 --- a/apps/miniprogram/src/pages/pkg-profile/health-records/index.scss +++ b/apps/miniprogram/src/pages/pkg-profile/health-records/index.scss @@ -8,21 +8,21 @@ font-size: var(--tk-font-num); font-weight: bold; color: $tx; - margin-bottom: 20px; + margin-bottom: var(--tk-section-gap); display: block; - padding-left: 4px; + padding-left: var(--tk-gap-2xs); } .record-list { display: flex; flex-direction: column; - gap: 16px; + gap: var(--tk-gap-md); } .record-card { background: $card; border-radius: $r; - padding: 28px; + padding: var(--tk-card-padding-lg); box-shadow: $shadow-sm; } @@ -30,7 +30,7 @@ display: flex; justify-content: space-between; align-items: center; - margin-bottom: 8px; + margin-bottom: var(--tk-gap-xs); } .record-card__type { @@ -49,19 +49,19 @@ font-size: var(--tk-font-h2); color: $tx; display: block; - margin-bottom: 4px; + margin-bottom: var(--tk-gap-2xs); } .record-card__source { font-size: var(--tk-font-body); color: var(--tk-text-secondary); display: block; - margin-bottom: 4px; + margin-bottom: var(--tk-gap-2xs); } .record-card__notes { font-size: var(--tk-font-body); color: $tx2; display: block; - margin-top: 8px; + margin-top: var(--tk-gap-xs); } diff --git a/apps/miniprogram/src/pages/pkg-profile/medication/index.scss b/apps/miniprogram/src/pages/pkg-profile/medication/index.scss index f9cafb8..72286a2 100644 --- a/apps/miniprogram/src/pages/pkg-profile/medication/index.scss +++ b/apps/miniprogram/src/pages/pkg-profile/medication/index.scss @@ -7,7 +7,7 @@ } .medication-loading { - padding: 40px 0; + padding: var(--tk-gap-2xl) 0; text-align: center; } @@ -18,13 +18,13 @@ .page-title { @include section-title; - padding-left: 4px; + padding-left: var(--tk-gap-2xs); } .reminder-list { display: flex; flex-direction: column; - gap: 16px; + gap: var(--tk-gap-md); } .reminder-card { @@ -32,7 +32,7 @@ align-items: center; background: $card; border-radius: $r; - padding: 24px; + padding: var(--tk-gap-lg); box-shadow: $shadow-sm; &.disabled { @@ -47,7 +47,7 @@ border-radius: $r; background: $acc-l; flex-shrink: 0; - margin-right: 20px; + margin-right: var(--tk-section-gap); } .reminder-avatar-text { @@ -69,7 +69,7 @@ font-size: var(--tk-font-num); font-weight: bold; color: $tx; - margin-bottom: 4px; + margin-bottom: var(--tk-gap-2xs); } .reminder-dosage { @@ -81,9 +81,9 @@ .reminder-actions { display: flex; align-items: center; - gap: 16px; + gap: var(--tk-gap-md); flex-shrink: 0; - margin-left: 12px; + margin-left: var(--tk-gap-sm); } .toggle { @@ -95,7 +95,7 @@ transition: background 0.3s; &.on { - background: $pri; + background: var(--tk-pri); } &.off { @@ -132,8 +132,8 @@ .form-card { background: $card; border-radius: $r; - padding: 28px; - margin-top: 24px; + padding: var(--tk-card-padding-lg); + margin-top: var(--tk-gap-lg); box-shadow: $shadow-sm; } @@ -142,7 +142,7 @@ font-size: var(--tk-font-body-lg); font-weight: bold; color: $tx; - margin-bottom: 20px; + margin-bottom: var(--tk-section-gap); display: block; } @@ -150,7 +150,7 @@ display: flex; align-items: center; justify-content: space-between; - padding: 24px 0; + padding: var(--tk-gap-lg) 0; border-bottom: 1px solid $bd-l; &:last-of-type { @@ -184,7 +184,7 @@ display: flex; align-items: center; justify-content: flex-end; - gap: 12px; + gap: var(--tk-gap-sm); } .time-value { @@ -195,20 +195,20 @@ .time-modify { font-size: var(--tk-font-h2); - color: $pri; + color: var(--tk-pri); } .form-actions { display: flex; - gap: 16px; - margin-top: 24px; + gap: var(--tk-gap-md); + margin-top: var(--tk-gap-lg); } .form-cancel { flex: 1; background: $bd-l; border-radius: $r-sm; - padding: 20px; + padding: var(--tk-section-gap); text-align: center; } @@ -219,9 +219,9 @@ .form-confirm { flex: 1; - background: $pri; + background: var(--tk-pri); border-radius: $r-sm; - padding: 20px; + padding: var(--tk-section-gap); text-align: center; } @@ -236,10 +236,10 @@ bottom: 0; left: 0; right: 0; - background: $pri; - padding: 28px; + background: var(--tk-pri); + padding: var(--tk-card-padding-lg); text-align: center; - box-shadow: 0 -2px 12px rgba(196, 98, 58, 0.15); + box-shadow: 0 -2px 12px rgba($pri, 0.15); } .add-text { diff --git a/apps/miniprogram/src/pages/pkg-profile/reports/detail/index.scss b/apps/miniprogram/src/pages/pkg-profile/reports/detail/index.scss index a3c2de3..8d9124b 100644 --- a/apps/miniprogram/src/pages/pkg-profile/reports/detail/index.scss +++ b/apps/miniprogram/src/pages/pkg-profile/reports/detail/index.scss @@ -7,14 +7,14 @@ font-weight: bold; color: $tx; display: block; - margin-bottom: 20px; + margin-bottom: var(--tk-section-gap); } .detail-row { display: flex; justify-content: space-between; align-items: flex-start; - padding: 12px 0; + padding: var(--tk-gap-sm) 0; border-bottom: 1px solid $bd-l; &:last-child { @@ -33,7 +33,7 @@ color: $tx; text-align: right; flex: 1; - margin-left: 24px; + margin-left: var(--tk-gap-lg); } .section-title { @@ -42,14 +42,14 @@ font-weight: bold; color: $tx; display: block; - margin-bottom: 16px; + margin-bottom: var(--tk-gap-md); } .indicator-item { display: flex; justify-content: space-between; align-items: center; - padding: 20px 0; + padding: var(--tk-section-gap) 0; border-bottom: 1px solid $bd-l; &:last-child { @@ -65,7 +65,7 @@ .indicator-name { font-size: var(--tk-font-h1); color: $tx2; - margin-bottom: 4px; + margin-bottom: var(--tk-gap-2xs); } .indicator-value { @@ -84,14 +84,14 @@ .indicator-ref { font-size: var(--tk-font-body); color: var(--tk-text-secondary); - margin-bottom: 4px; + margin-bottom: var(--tk-gap-2xs); @include serif-number; } .indicator-status { font-size: var(--tk-font-h2); font-weight: bold; - padding: 4px 12px; + padding: var(--tk-gap-2xs) var(--tk-gap-sm); border-radius: $r; &.normal { @@ -115,7 +115,7 @@ display: flex; justify-content: center; align-items: center; - padding: 120px 0; + padding: var(--tk-gap-2xl) 0; } .loading-text, diff --git a/apps/miniprogram/src/pages/pkg-profile/reports/index.scss b/apps/miniprogram/src/pages/pkg-profile/reports/index.scss index 329070a..edd3d26 100644 --- a/apps/miniprogram/src/pages/pkg-profile/reports/index.scss +++ b/apps/miniprogram/src/pages/pkg-profile/reports/index.scss @@ -5,23 +5,23 @@ .page-title { @include section-title; - padding-left: 4px; + padding-left: var(--tk-gap-2xs); } .report-list { display: flex; flex-direction: column; - gap: 16px; + gap: var(--tk-gap-md); } .report-card { background: $card; border-radius: $r; - padding: 28px; + padding: var(--tk-card-padding-lg); box-shadow: $shadow-sm; &:active { - box-shadow: $shadow-md; + opacity: var(--tk-touch-feedback-opacity); } } @@ -29,7 +29,7 @@ display: flex; justify-content: space-between; align-items: center; - margin-bottom: 16px; + margin-bottom: var(--tk-gap-md); } .report-type-row { @@ -42,8 +42,8 @@ width: 56px; height: 56px; border-radius: $r-sm; - background: $pri-l; - margin-right: 16px; + background: var(--tk-pri-l); + margin-right: var(--tk-gap-md); flex-shrink: 0; } @@ -51,7 +51,7 @@ font-family: 'Georgia', 'Times New Roman', serif; font-size: var(--tk-font-body-lg); font-weight: bold; - color: $pri-d; + color: var(--tk-pri-d); } .report-type { diff --git a/apps/miniprogram/src/pages/pkg-profile/settings/index.scss b/apps/miniprogram/src/pages/pkg-profile/settings/index.scss index 764aa20..b443151 100644 --- a/apps/miniprogram/src/pages/pkg-profile/settings/index.scss +++ b/apps/miniprogram/src/pages/pkg-profile/settings/index.scss @@ -5,21 +5,21 @@ .page-title { @include section-title; - padding-left: 4px; + padding-left: var(--tk-gap-2xs); } .settings-group { background: $card; border-radius: $r; overflow: hidden; - margin-bottom: 24px; + margin-bottom: var(--tk-gap-lg); box-shadow: $shadow-sm; } .settings-item { display: flex; align-items: center; - padding: 28px 24px; + padding: var(--tk-card-padding-lg) var(--tk-gap-lg); border-bottom: 1px solid $bd-l; &:last-child { @@ -36,8 +36,8 @@ width: 48px; height: 48px; border-radius: $r-sm; - background: $pri-l; - margin-right: 16px; + background: var(--tk-pri-l); + margin-right: var(--tk-gap-md); flex-shrink: 0; } @@ -45,7 +45,7 @@ font-family: 'Georgia', 'Times New Roman', serif; font-size: var(--tk-font-h2); font-weight: bold; - color: $pri-d; + color: var(--tk-pri-d); } .settings-label { diff --git a/apps/miniprogram/src/pages/profile/index.scss b/apps/miniprogram/src/pages/profile/index.scss index d87a6d3..5d28cf0 100644 --- a/apps/miniprogram/src/pages/profile/index.scss +++ b/apps/miniprogram/src/pages/profile/index.scss @@ -2,22 +2,22 @@ @import '../../styles/mixins.scss'; .profile-page { - padding-bottom: calc(100px + env(safe-area-inset-bottom)); + padding-bottom: calc(var(--tk-tabbar-space) + env(safe-area-inset-bottom)); } /* ─── 用户信息卡片 ─── */ .profile-user-card { display: flex; align-items: center; - gap: 16px; - margin-bottom: 16px; + gap: var(--tk-gap-md); + margin-bottom: var(--tk-gap-md); } .profile-avatar { width: 60px; height: 60px; border-radius: $r-pill; - background: linear-gradient(135deg, $pri-l 0%, $pri 100%); + background: linear-gradient(135deg, var(--tk-pri-l) 0%, var(--tk-pri) 100%); @include flex-center; flex-shrink: 0; } @@ -57,8 +57,8 @@ /* ─── 积分 + 打卡 ─── */ .profile-stats-row { display: flex; - gap: 10px; - margin-bottom: 24px; + gap: var(--tk-gap-sm); + margin-bottom: var(--tk-gap-lg); } .stat-card { @@ -74,7 +74,7 @@ margin-bottom: 2px; &.stat-pri { - color: $pri; + color: var(--tk-pri); } &.stat-acc { @@ -94,7 +94,7 @@ /* ─── 分组菜单 ─── */ .menu-group { - margin-bottom: 14px; + margin-bottom: var(--tk-gap-sm); } .menu-group-title { @@ -102,8 +102,8 @@ font-weight: 600; color: $tx2; display: block; - margin-bottom: 8px; - padding-left: 4px; + margin-bottom: var(--tk-gap-xs); + padding-left: var(--tk-gap-2xs); } .menu-group-card { @@ -113,13 +113,13 @@ .menu-item { display: flex; align-items: center; - gap: 14px; - padding: 16px; + gap: var(--tk-gap-sm); + padding: var(--tk-gap-md); min-height: 48px; position: relative; &:active { - background: $bd-l; + opacity: var(--tk-touch-feedback-opacity); } } @@ -140,7 +140,7 @@ flex-shrink: 0; &.menu-icon--pri-l { - background: $pri-l; + background: var(--tk-pri-l); } &.menu-icon--acc-l { @@ -158,7 +158,7 @@ font-weight: 700; &.menu-icon-text--pri { - color: $pri; + color: var(--tk-pri); } &.menu-icon-text--acc { @@ -184,9 +184,9 @@ /* ─── 退出登录 ─── */ .profile-logout { - margin-top: 16px; + margin-top: var(--tk-gap-md); text-align: center; - padding: 16px 0; + padding: var(--tk-gap-md) 0; } .logout-text { diff --git a/apps/miniprogram/src/services/auth.ts b/apps/miniprogram/src/services/auth.ts index 2b19af8..2b0e9d0 100644 --- a/apps/miniprogram/src/services/auth.ts +++ b/apps/miniprogram/src/services/auth.ts @@ -27,6 +27,14 @@ export interface PatientInfo { relation: string; } +export async function credentialLogin(username: string, password: string, tenantId: string) { + return api.post<{ access_token: string; refresh_token: string; expires_in?: number; user: { id: string; username: string; display_name?: string; phone?: string; tenant_id?: string } }>('/auth/login', { + username, + password, + tenant_id: tenantId, + }); +} + export async function wechatLogin(code: string): Promise { return api.post('/auth/wechat/login', { code }); } diff --git a/apps/miniprogram/src/stores/auth.ts b/apps/miniprogram/src/stores/auth.ts index e77fb59..c7ca5f9 100644 --- a/apps/miniprogram/src/stores/auth.ts +++ b/apps/miniprogram/src/stores/auth.ts @@ -28,6 +28,7 @@ interface AuthState { loading: boolean; login: (code: string) => Promise; + credentialLogin: (username: string, password: string) => Promise; bindPhone: (encryptedData: string, iv: string) => Promise; setCurrentPatient: (patient: authApi.PatientInfo) => void; loadPatients: () => Promise; @@ -154,6 +155,33 @@ export const useAuthStore = create((set, get) => ({ } }, + credentialLogin: async (username: string, password: string) => { + if (get().loading) return false; + set({ loading: true }); + try { + const tenantId = Taro.getStorageSync('tenant_id') || process.env.TARO_APP_DEFAULT_TENANT_ID || ''; + const resp = await authApi.credentialLogin(username, password, tenantId); + const roles = (resp as Record).roles instanceof Array + ? ((resp as Record).roles as Array>).map((r) => r.code || r.name || String(r)) + : []; + secureSet('access_token', resp.access_token); + secureSet('refresh_token', resp.refresh_token); + if (resp.expires_in) { + secureSet('token_expires_at', String(Date.now() + resp.expires_in * 1000)); + } + const user = resp.user; + secureSet('user_data', JSON.stringify(user)); + secureSet('user_roles', JSON.stringify(roles)); + secureSet('tenant_id', user.tenant_id || tenantId); + set({ user, roles, loading: false }); + clearLoggingOut(); + return true; + } catch { + set({ loading: false }); + return false; + } + }, + bindPhone: async (encryptedData: string, iv: string) => { if (get().loading) return false; set({ loading: true }); diff --git a/apps/miniprogram/src/styles/elder-mode.scss b/apps/miniprogram/src/styles/elder-mode.scss index 9f9ef3e..45f1152 100644 --- a/apps/miniprogram/src/styles/elder-mode.scss +++ b/apps/miniprogram/src/styles/elder-mode.scss @@ -1,5 +1,5 @@ // 关怀模式 CSS 覆写(仅保留结构性调整) -// 字号 / 颜色由 tokens.scss 的 .elder-mode 块自动级联处理 +// 字号 / 颜色 / 间距由 tokens.scss 的 .elder-mode 块自动级联处理 // 此文件仅保留无法通过 CSS 变量表达的布局覆写 .elder-mode { @@ -31,89 +31,86 @@ // ─── 体征网格:2 列 → 1 列(解决溢出核心改动)─── .vitals-grid { grid-template-columns: 1fr; + gap: var(--tk-gap-md); } - // ─── 间距放大(增加呼吸空间)─── - .vitals-grid { - gap: 14px; - } - - .checkin-card { - padding: 28px; - } - - .reminder-card { - padding: 24px; - } - + // ─── 页面间距放大 ─── .home-page, .guest-page, .health-page, .messages-page, .consultation-body { - padding: 28px 32px 120px; + padding: var(--tk-card-padding-lg) var(--tk-gap-xl) var(--tk-tabbar-space); } .profile-page { - padding: 28px 32px 120px; + padding: var(--tk-card-padding-lg) var(--tk-gap-xl) var(--tk-tabbar-space); } .menu-item { - padding: 18px 22px; + padding: var(--tk-card-padding) var(--tk-gap-xl); } .session-list, .msg-list { - gap: 12px; + gap: var(--tk-gap-sm); + } + + .checkin-card { + padding: var(--tk-card-padding-lg); + } + + .reminder-card { + padding: var(--tk-card-padding); } .session-card, .consult-card, .notify-card { - padding: 20px; + padding: var(--tk-section-gap); } .vital-tabs, .period-group { - gap: 10px; + gap: var(--tk-gap-xs); } // ─── 组件布局调整 ─── // EmptyState .empty-state-action { - padding: 20px 56px; + padding: var(--tk-section-gap) 56px; } // ErrorState .error-state-retry { - padding: 20px 56px; + padding: var(--tk-section-gap) 56px; } // Loading .loading-spinner { - width: 56px; - height: 56px; + width: var(--tk-btn-primary-h); + height: var(--tk-btn-primary-h); } // StepIndicator .step-dot { - width: 56px; - height: 56px; + width: var(--tk-btn-primary-h); + height: var(--tk-btn-primary-h); } .step-line { - height: 4px; - top: 28px; + height: var(--tk-gap-2xs); + top: calc(var(--tk-btn-primary-h) / 2); } // ─── 登录页布局调整 ─── .login-page { - padding: 80px 48px 60px; + padding: calc(var(--tk-gap-2xl) * 2) var(--tk-gap-2xl) var(--tk-gap-2xl); } .login-brand { - margin-bottom: 56px; + margin-bottom: var(--tk-gap-2xl); } .login-logo { @@ -122,6 +119,6 @@ } .guest-login-btn { - height: 64px; + height: var(--tk-input-height); } } diff --git a/apps/miniprogram/src/styles/mixins.scss b/apps/miniprogram/src/styles/mixins.scss index 17a21fa..bc337b5 100644 --- a/apps/miniprogram/src/styles/mixins.scss +++ b/apps/miniprogram/src/styles/mixins.scss @@ -2,10 +2,10 @@ @mixin card { background: $card; - border-radius: $r; + border-radius: var(--tk-card-radius); box-shadow: $shadow-md; - padding: 28px; - margin: 0 24px 24px; + padding: var(--tk-card-padding); + margin: 0 var(--tk-page-padding) var(--tk-gap-md); } @mixin flex-center { @@ -29,13 +29,13 @@ font-size: var(--tk-font-h1); font-weight: bold; color: $tx; - margin-bottom: 20px; + margin-bottom: var(--tk-section-gap); display: block; } @mixin tag($bg, $color) { display: inline-block; - padding: 4px 12px; + padding: var(--tk-tag-padding-v) var(--tk-tag-padding-h); border-radius: $r-sm; font-size: var(--tk-font-body); font-weight: 500; @@ -52,33 +52,40 @@ } @mixin btn-primary { - height: $btn-primary-h; - border-radius: $r; - background: $pri; + height: var(--tk-btn-primary-h); + border-radius: var(--tk-card-radius); + background: var(--tk-pri); color: $white; font-size: var(--tk-font-body-lg); font-weight: 600; border: none; width: 100%; + box-shadow: var(--tk-shadow-btn); @include touch-target; &:active { - opacity: 0.85; + opacity: var(--tk-touch-feedback-opacity); } } @mixin btn-outline { - height: $btn-primary-h; - border-radius: $r; + height: var(--tk-btn-primary-h); + border-radius: var(--tk-card-radius); background: transparent; - color: $pri; + color: var(--tk-pri); font-size: var(--tk-font-body-lg); font-weight: 600; - border: 2px solid $pri; + border: 2px solid var(--tk-pri); width: 100%; @include touch-target; &:active { - background: $pri-l; + opacity: var(--tk-touch-feedback-opacity); + } +} + +@mixin touch-feedback { + &:active { + opacity: var(--tk-touch-feedback-opacity); } } diff --git a/apps/miniprogram/src/styles/tokens.scss b/apps/miniprogram/src/styles/tokens.scss index fcbc80d..367e9d7 100644 --- a/apps/miniprogram/src/styles/tokens.scss +++ b/apps/miniprogram/src/styles/tokens.scss @@ -1,43 +1,60 @@ -// Design Token — CSS 自定义属性(校准后) -// 基于全量审计:600+ font-size 声明的频率分布 +// Design Token — CSS 自定义属性 +// 基准参考:docs/design/mp-redesign-home.html 原型设计 // 页面样式应引用 var(--tk-*) 而非硬编码 px 值 // 关怀模式通过 .elder-mode 覆盖 token 值自动生效 // ═══════════════════════════════════════ -// 正常模式 Token +// 正常模式 Token(对齐原型 mp-redesign-home.html) // ═══════════════════════════════════════ page { - // ─── 字号(11 级,覆盖 92.5% 场景)─── - --tk-font-display: 72px; // 大型装饰数值(积分余额、空状态图标) - --tk-font-hero: 48px; // 装饰图标、空状态字符 - --tk-font-h1: 26px; // 页面/区块标题 - --tk-font-h2: 24px; // 副标题、日期、菜单组 - --tk-font-body-lg: 28px; // 大正文、按钮(页面默认字号) - --tk-font-body: 22px; // 正文、标签 - --tk-font-body-sm: 16px; // 中等正文、列表项 - --tk-font-num: 30px; // 数值 - --tk-font-num-lg: 34px; // 大数值、统计 - --tk-font-cap: 13px; // 说明文字、时间戳 - --tk-font-micro: 11px; // 角标、标签 + // ─── 色彩 Token(CSS 变量级联,医生端可覆盖)─── + --tk-pri: #{$pri}; + --tk-pri-l: #{$pri-l}; + --tk-pri-d: #{$pri-d}; + --tk-shadow-btn: #{$shadow-btn}; + --tk-shadow-tab: #{$shadow-tab}; + + // ─── 字号(11 级)─── + --tk-font-display: 72px; + --tk-font-hero: 48px; + --tk-font-h1: 26px; // 页面标题(原型 fontSize:26 serif bold) + --tk-font-h2: 24px; // 副标题 + --tk-font-body-lg: 28px; // 按钮大字、页面默认 + --tk-font-body: 22px; // 正文 + --tk-font-body-sm: 16px; // 列表项、副文本(原型 15-16px) + --tk-font-num: 30px; // 数值(原型 fontSize:30 serif bold) + --tk-font-num-lg: 34px; // 大数值 + --tk-font-cap: 13px; // 说明文字(原型 fontSize:13) + --tk-font-nav: 18px; // 导航栏标题(原型 fontSize:18 serif bold) + --tk-font-micro: 11px; // 角标(原型 fontSize:11) // ─── 结构 ─── --tk-line-height: 1.5; --tk-touch-min: 48px; - --tk-btn-primary-h: 56px; - --tk-text-secondary: #78716C; // $tx3 — 关怀模式提升对比度 + --tk-btn-primary-h: 52px; // 按钮高度(原型 height:52) + --tk-text-secondary: #78716C; // ─── 统一组件库结构化 Token ─── --tk-card-bg: #FFFFFF; - --tk-card-padding: 24px; - --tk-card-radius: 16px; - --tk-gap-sm: 12px; - --tk-gap-md: 16px; + --tk-card-padding: 20px; // 卡片内边距(原型 padding:16-20) + --tk-card-padding-sm: 16px; // 紧凑卡片 + --tk-card-padding-lg: 28px; // 大卡片 + --tk-card-radius: 16px; // 卡片圆角(原型 borderRadius:16) + --tk-gap-2xs: 4px; + --tk-gap-xs: 8px; + --tk-gap-sm: 12px; // 网格/列表项间距(原型 gap:10) + --tk-gap-md: 16px; // 区块间距(原型 marginBottom:16) + --tk-section-gap: 20px; // 大区块间距 --tk-gap-lg: 24px; - --tk-page-padding: 24px; + --tk-gap-xl: 32px; + --tk-gap-2xl: 48px; + --tk-page-padding: 20px; // 页面边距(原型 padding:20) + --tk-input-height: 56px; + --tk-tabbar-space: 100px; --tk-touch-feedback-opacity: 0.85; - --tk-tag-font-size: 12px; - --tk-tag-padding-v: 4px; - --tk-tag-padding-h: 12px; + --tk-tag-font-size: 11px; // 标签字号(原型 fontSize:11) + --tk-tag-padding-v: 3px; // 标签纵向(原型 padding:3px) + --tk-tag-padding-h: 8px; // 标签横向(原型 padding:8px) } // ═══════════════════════════════════════ @@ -47,30 +64,52 @@ page { .elder-mode { --tk-font-display: 80px; --tk-font-hero: 56px; - --tk-font-h1: 30px; - --tk-font-h2: 28px; + --tk-font-h1: 30px; // 26×1.15 + --tk-font-h2: 28px; // 24×1.15 --tk-font-body-lg: 34px; - --tk-font-body: 30px; - --tk-font-body-sm: 22px; + --tk-font-body: 30px; // 22×1.35 + --tk-font-body-sm: 22px; // 16×1.35 --tk-font-num: 34px; --tk-font-num-lg: 40px; - --tk-font-cap: 18px; - --tk-font-micro: 17px; + --tk-font-cap: 18px; // 13×1.4 + --tk-font-nav: 22px; // 18×1.2(标题轻度放大) + --tk-font-micro: 17px; // 11×1.55 --tk-line-height: 1.7; --tk-touch-min: 56px; - --tk-btn-primary-h: 64px; - --tk-text-secondary: #5A554F; // $tx2 — 对比度提升 + --tk-btn-primary-h: 60px; + --tk-text-secondary: #5A554F; - // ─── 统一组件库 — 关怀覆写 ─── - --tk-card-padding: 32px; + --tk-card-padding: 28px; + --tk-card-padding-sm: 20px; + --tk-card-padding-lg: 36px; --tk-card-radius: 20px; + --tk-gap-2xs: 6px; + --tk-gap-xs: 12px; --tk-gap-sm: 16px; --tk-gap-md: 20px; + --tk-section-gap: 28px; --tk-gap-lg: 32px; - --tk-page-padding: 32px; + --tk-gap-xl: 40px; + --tk-gap-2xl: 56px; + --tk-page-padding: 28px; + --tk-input-height: 64px; + --tk-tabbar-space: 120px; --tk-touch-feedback-opacity: 0.8; --tk-tag-font-size: 13px; - --tk-tag-padding-v: 6px; - --tk-tag-padding-h: 16px; + --tk-tag-padding-v: 5px; + --tk-tag-padding-h: 12px; +} + +// ═══════════════════════════════════════ +// 医生端 Token 覆盖(靛蓝色系) +// 在医生端页面根容器添加 class="doctor-mode" +// 所有引用 var(--tk-pri) 的组件自动变色 +// ═══════════════════════════════════════ +.doctor-mode { + --tk-pri: #{$doc-pri}; + --tk-pri-l: #{$doc-pri-l}; + --tk-pri-d: #{$doc-pri-d}; + --tk-shadow-btn: #{$shadow-btn-doc}; + --tk-shadow-tab: #{$shadow-tab-doc}; } diff --git a/apps/miniprogram/src/styles/variables.scss b/apps/miniprogram/src/styles/variables.scss index 0afc52e..b797fba 100644 --- a/apps/miniprogram/src/styles/variables.scss +++ b/apps/miniprogram/src/styles/variables.scss @@ -24,7 +24,15 @@ $wrn: #C4873A; // 警告 (warm amber) $wrn-l: #FFF3E0; // 警告浅 $wrn-d: #8B6F4E; // 警告深(渐变中间色) -// ─── 圆角 ─── +// ─── 医生端色彩(靛蓝系)─── +$doc-pri: #3A6B8C; // 医生端主色 +$doc-pri-l: #D4E5F0; // 医生端浅色 +$doc-pri-d: #2A4F6A; // 医生端深色 + +// ─── 功能色 ─── +$wechat: #07C160; // 微信绿 + +// ─── 圆角(对齐原型 T.r:16, T.rSm:12, T.rXs:8)─── $r: 16px; $r-sm: 12px; $r-xs: 8px; @@ -32,13 +40,31 @@ $r-lg: 20px; $r-pill: 999px; // ─── 老年友好触控参数 ─── -$touch-min: 48px; // 最小触控区域 -$btn-primary-h: 56px; // 主按钮高度 -$menu-item-h: 64px; // 菜单项高度 -$tab-h: 56px; // Tab 切换高度 -$font-min: 22px; // 最小字号 +$touch-min: 48px; +$btn-primary-h: 52px; // 对齐原型 height:52 +$menu-item-h: 64px; +$tab-h: 56px; +$font-min: 22px; +$input-h: 56px; -// ─── 阴影 ─── -$shadow-sm: 0 1px 4px rgba(45, 42, 38, 0.04); -$shadow-md: 0 2px 12px rgba(45, 42, 38, 0.08); -$shadow-lg: 0 8px 32px rgba(45, 42, 38, 0.12); +// ─── 间距阶梯(SCSS 编译时变量,与 CSS Token 对应)─── +$sp-2xs: 4px; +$sp-xs: 8px; +$sp-sm: 12px; +$sp-md: 16px; +$sp-section: 20px; +$sp-lg: 24px; +$sp-xl: 32px; +$sp-2xl: 48px; + +// ─── TabBar 底部安全区 ─── +$tabbar-space: 100px; + +// ─── 阴影(原型 boxShadow 参数,略增可见度)─── +$shadow-sm: 0 1px 4px rgba(45, 42, 38, 0.06); +$shadow-md: 0 2px 12px rgba(45, 42, 38, 0.10); +$shadow-lg: 0 8px 32px rgba(45, 42, 38, 0.15); +$shadow-btn: 0 4px 16px rgba(196, 98, 58, 0.3); // 主按钮阴影 +$shadow-tab: 0 2px 8px rgba(196, 98, 58, 0.25); // 选中Tab阴影 +$shadow-btn-doc: 0 4px 16px rgba(58, 107, 140, 0.3); // 医生端按钮阴影 +$shadow-tab-doc: 0 2px 8px rgba(58, 107, 140, 0.25); // 医生端Tab阴影 diff --git a/wiki/index.md b/wiki/index.md index 046e94c..d768aff 100644 --- a/wiki/index.md +++ b/wiki/index.md @@ -4,7 +4,7 @@ ## 关键数字 -> 最后更新: 2026-05-16 | 数据截止: feat/media-library-banner 分支 +> 最后更新: 2026-05-16 | 数据截止: feat/media-library-banner 分支(UI 优化 Phase 0-2 完成) | 指标 | 值 | |------|-----| @@ -17,7 +17,7 @@ | erp-health 实体 | **57 个** Entity(31 handler / 36 service / 21 DTO,189 文件) | | erp-ai 实体 | 9 个 Entity(45 文件,4 AI Provider) | | Web 前端 | 332 个 TS/TSX 文件(29 活跃路由 + 6 冻结路由,52 API 模块) | -| 微信小程序 | Taro 4.2 + React 18,163 个 TS/TSX 文件 / 66 页面 / 4 TabBar + 医生端分包,统一组件库(PageShell/ContentCard/StatusTag/SectionTitle/LoadingCard + SearchSection/CardList/PaginationBar + useListPage Hook) | +| 微信小程序 | Taro 4.2 + React 18,163 个 TS/TSX 文件 / 66 页面 / 4 TabBar + 医生端分包,统一组件库 + CSS 变量主题(75 页面 SCSS `$pri` → `var(--tk-pri)`,医生端 `.doctor-mode` 靛蓝覆盖,登录页改为账号密码+微信登录) | | 前端单元测试 | 88 个测试文件(472 Web 断言 + 39 MP 断言)+ 13 E2E spec(124 断言) | | 后端测试 | **943 个函数**(762 同步 + 181 异步),79 个文件含内联测试 | | 事件系统 | 31 事件类型(health 模块内)/ 23 幂等消费者 / Outbox + LISTEN/NOTIFY | @@ -31,10 +31,10 @@ | 系统分析评分 | **6.9/10 (B)**(六维度全面均衡分析,2026-05-11) | | 审计状态 | V1: 83% → V2: 85%,P0 安全修复已完成,V2 CRITICAL 全清零 | | 角色测试 | R01-R05 全角色验证完成,86.5% 通过率,5 个 BUG 已修复;小程序 MP 多角色 96.2% 通过率 | -| Design Token | 11 级字号(含 `--tk-font-display`)+ 12 结构 token(card/gap/page/tag/touch),68 SCSS 文件全面接入,关怀模式 CSS 变量级联自动生效 | +| Design Token | 11 级字号 + 12 结构 token,75 SCSS 页面全量接入 `var(--tk-pri)`,`.doctor-mode` / `.elder-mode` CSS 变量级联覆盖 | | 长者模式 | 58/58 页面 100% 覆盖 | -| UI 合规审计 | T40: 60 页面全覆盖(PASS 24 / PASS_WITH_ISSUES 36 / NEEDS_WORK 0),HIGH×2 + MEDIUM×6 + LOW×67 全部修复,评分 95/100,66 页面全部迁移到统一组件库 | -| 项目阶段 | **功能完善**(媒体库+轮播图+文章编辑器上线,6 模块冻结待解冻) | +| UI 合规审计 | T40: 60 页面全覆盖(PASS 24 / PASS_WITH_ISSUES 36 / NEEDS_WORK 0),HIGH×2 + MEDIUM×6 + LOW×67 全部修复,评分 95/100 | +| 项目阶段 | **UI 优化实施**(CSS 变量主题 Phase 0-2 完成,按原型逐页验证中) | ## 症状导航