From 4f42f2c342c63c4c0a238ca4ab23e524f7e359a3 Mon Sep 17 00:00:00 2001 From: wangxiaogang Date: Sat, 6 Jun 2026 22:37:46 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20UI/UX=20=E5=85=A8=E9=9D=A2=E9=87=8D?= =?UTF-8?q?=E6=9E=84=20=E2=80=94=20=E8=AE=BE=E8=AE=A1=E7=B3=BB=E7=BB=9F?= =?UTF-8?q?=E7=BB=9F=E4=B8=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 设计系统: - uni.scss 扩展:间距/圆角/字体/阴影/动画 token 体系 - 新建 styles/mixins.scss:20+ 公共 mixin 页面重构(12 个页面): - 首页/记账/统计/账单/个人中心/预算/分类管理 - 群组管理/通知中心/管理员/用户管理/编辑资料 所有硬编码替换为设计 token: - 颜色:#FFF8F0→$bg, #FFFFFF→$surface, #FF8C69→$primary 等 - 间距:40rpx→$space-xl, 24rpx→$space-md 等 - 字体:28rpx→$font-lg, 24rpx→$font-md 等 - 圆角:20rpx→$radius-lg, 40rpx→$radius-2xl 等 - 阴影:统一使用 $shadow-md/$shadow-sm 重复样式提取为 mixin: - page-base, sticky-header, nav-bar, card, modal-mask - tabs, tab, state-box, shimmer, flex-center 等 --- client/src/App.vue | 9 +- client/src/pages/add/index.vue | 93 ++++---- client/src/pages/admin/index.vue | 235 ++++++++++---------- client/src/pages/admin/users.vue | 97 ++++----- client/src/pages/bills/filter-panel.vue | 7 - client/src/pages/bills/index.vue | 107 ++++----- client/src/pages/budget/index.vue | 158 +++++++------- client/src/pages/category-manage/index.vue | 149 ++++++------- client/src/pages/group-manage/index.vue | 220 +++++++++---------- client/src/pages/index/index.vue | 193 +++++++---------- client/src/pages/notifications/index.vue | 136 ++++++------ client/src/pages/profile-edit/index.vue | 85 ++++---- client/src/pages/profile/index.vue | 220 +++++++++---------- client/src/pages/stats/index.vue | 141 +++++------- client/src/styles/mixins.scss | 238 +++++++++++++++++++++ client/src/uni.scss | 58 ++++- 16 files changed, 1116 insertions(+), 1030 deletions(-) create mode 100644 client/src/styles/mixins.scss diff --git a/client/src/App.vue b/client/src/App.vue index 0e94386..d81a597 100644 --- a/client/src/App.vue +++ b/client/src/App.vue @@ -49,10 +49,15 @@ onLaunch(() => { diff --git a/client/src/pages/add/index.vue b/client/src/pages/add/index.vue index 37aed76..846bac5 100644 --- a/client/src/pages/add/index.vue +++ b/client/src/pages/add/index.vue @@ -216,51 +216,44 @@ function goBack() { diff --git a/client/src/pages/admin/users.vue b/client/src/pages/admin/users.vue index bb0b7c1..130413a 100644 --- a/client/src/pages/admin/users.vue +++ b/client/src/pages/admin/users.vue @@ -162,86 +162,77 @@ onReachBottom(() => { diff --git a/client/src/pages/bills/filter-panel.vue b/client/src/pages/bills/filter-panel.vue index 217b386..5ae41bc 100644 --- a/client/src/pages/bills/filter-panel.vue +++ b/client/src/pages/bills/filter-panel.vue @@ -290,7 +290,6 @@ async function handleDeleteSaved(id: number) { border-radius: 40rpx 40rpx 0 0; display: flex; flex-direction: column; - box-sizing: border-box; overflow: hidden; } @@ -332,13 +331,11 @@ async function handleDeleteSaved(id: number) { padding: 24rpx 40rpx; max-height: 55vh; overflow-x: hidden; - box-sizing: border-box; } .filter-section { margin-bottom: 32rpx; width: 100%; - box-sizing: border-box; } .section-label { @@ -354,7 +351,6 @@ async function handleDeleteSaved(id: number) { flex-wrap: wrap; gap: 12rpx; width: 100%; - box-sizing: border-box; } .chip { @@ -388,7 +384,6 @@ async function handleDeleteSaved(id: number) { align-items: center; gap: 16rpx; width: 100%; - box-sizing: border-box; } .date-picker { @@ -420,7 +415,6 @@ async function handleDeleteSaved(id: number) { align-items: center; gap: 16rpx; width: 100%; - box-sizing: border-box; } .amount-input { @@ -440,7 +434,6 @@ async function handleDeleteSaved(id: number) { } .keyword-input { - width: 100%; height: 72rpx; padding: 0 20rpx; background: #fff8f0; diff --git a/client/src/pages/bills/index.vue b/client/src/pages/bills/index.vue index 9a65606..ad08f4c 100644 --- a/client/src/pages/bills/index.vue +++ b/client/src/pages/bills/index.vue @@ -245,60 +245,36 @@ onPullDownRefresh(() => { diff --git a/client/src/pages/budget/index.vue b/client/src/pages/budget/index.vue index 628e86b..50c13ee 100644 --- a/client/src/pages/budget/index.vue +++ b/client/src/pages/budget/index.vue @@ -180,46 +180,41 @@ function goBack() { diff --git a/client/src/pages/profile-edit/index.vue b/client/src/pages/profile-edit/index.vue index 53bb887..3f15d9f 100644 --- a/client/src/pages/profile-edit/index.vue +++ b/client/src/pages/profile-edit/index.vue @@ -105,43 +105,34 @@ async function saveProfile() { diff --git a/client/src/pages/profile/index.vue b/client/src/pages/profile/index.vue index b4d6fb2..4581f95 100644 --- a/client/src/pages/profile/index.vue +++ b/client/src/pages/profile/index.vue @@ -357,41 +357,38 @@ async function exportData() { diff --git a/client/src/pages/stats/index.vue b/client/src/pages/stats/index.vue index 60d86b0..13322c2 100644 --- a/client/src/pages/stats/index.vue +++ b/client/src/pages/stats/index.vue @@ -313,46 +313,36 @@ function getBarWidth(amount: number) { diff --git a/client/src/styles/mixins.scss b/client/src/styles/mixins.scss new file mode 100644 index 0000000..c8933bd --- /dev/null +++ b/client/src/styles/mixins.scss @@ -0,0 +1,238 @@ +/* ===== 公共 Mixins ===== */ + +// 页面基础样式 +@mixin page-base { + min-height: 100vh; + background: $bg; + padding-bottom: 180rpx; +} + +// 粘性头部 +@mixin sticky-header { + position: sticky; + top: 0; + z-index: 100; + background: $bg; +} + +// 状态栏 +@mixin status-bar { + background: $bg; +} + +// 卡片 +@mixin card($radius: $radius-2xl) { + background: $surface; + border-radius: $radius; + border: 2rpx solid $border; + box-shadow: $shadow-md; +} + +// 导航栏 +@mixin nav-bar { + display: flex; + align-items: center; + padding: $space-sm $space-xl $space-md; +} + +// 返回按钮 +@mixin nav-back { + width: 88rpx; + height: 88rpx; + display: flex; + align-items: center; + justify-content: center; + &:active { opacity: 0.6; } +} + +// 页面标题 +@mixin page-title { + display: block; + text-align: center; + font-size: $font-2xl; + font-weight: 600; + color: $text; + padding: $space-sm 0 $space-md; +} + +// 状态空/错误/加载 +@mixin state-box { + display: flex; + flex-direction: column; + align-items: center; + gap: $space-sm; + padding: 120rpx 0; +} + +@mixin state-text { + font-size: $font-lg; + color: $text-muted; +} + +// Tabs +@mixin tabs-wrap { + display: flex; + align-items: center; + justify-content: center; + gap: $space-sm; +} + +@mixin tabs { + display: inline-flex; + background: $surface-warm; + border-radius: $radius-lg; + padding: $space-xs; +} + +@mixin tab { + padding: $space-sm $space-xl; + border-radius: $radius-lg; + font-size: $font-lg; + color: $text-sec; + transition: all $transition-normal; + + &.active { + background: $surface; + color: $primary; + font-weight: 600; + box-shadow: $shadow-sm; + } +} + +// 弹窗 +@mixin modal-mask { + position: fixed; + top: 0; left: 0; right: 0; bottom: 0; + background: rgba(0, 0, 0, 0.4); + z-index: 200; + display: flex; + align-items: flex-end; +} + +@mixin bottom-modal { + width: 100%; + max-height: 80vh; + background: $surface; + border-radius: $radius-2xl $radius-2xl 0 0; + display: flex; + flex-direction: column; +} + +@mixin modal-header { + display: flex; + align-items: center; + justify-content: space-between; + padding: $space-lg $space-xl $space-md; + border-bottom: 2rpx solid $border; +} + +@mixin modal-title { + font-size: $font-xl; + font-weight: 600; + color: $text; +} + +@mixin modal-close { + width: 56rpx; + height: 56rpx; + display: flex; + align-items: center; + justify-content: center; + border-radius: 50%; + background: $border; + &:active { background: darken($border, 5%); } +} + +// 列表项 +@mixin list-item { + display: flex; + align-items: center; + gap: $space-md; + padding: $space-lg $space-lg; + background: $surface; + border-radius: $radius-xl; + border: 2rpx solid $border; + box-shadow: $shadow-md; + margin-bottom: $space-sm; +} + +// 输入框 +@mixin input { + width: 100%; + height: 80rpx; + padding: 0 $space-md; + background: $bg; + border-radius: $radius-lg; + border: 2rpx solid $border; + font-size: $font-lg; + color: $text; +} + +// 按钮 +@mixin btn-primary { + height: 80rpx; + padding: 0 $space-lg; + background: linear-gradient(135deg, $primary, $primary-dark); + border-radius: $radius-lg; + display: flex; + align-items: center; + justify-content: center; + &:active { opacity: 0.8; } +} + +@mixin btn-primary-text { + font-size: $font-lg; + font-weight: 600; + color: $surface; + white-space: nowrap; +} + +@mixin btn-ghost { + padding: $space-sm $space-md; + background: $surface-warm; + border-radius: $radius-md; + &:active { background: $border; } +} + +@mixin btn-ghost-text { + font-size: $font-md; + color: $primary; +} + +// Shimmer 动画 +@mixin shimmer { + background: linear-gradient(90deg, $border 25%, $surface-warm 50%, $border 75%); + background-size: 200% 100%; + animation: shimmer 1.5s infinite; +} + +@keyframes shimmer { + 0% { background-position: 200% 0; } + 100% { background-position: -200% 0; } +} + +// Flex 工具 +@mixin flex-center { + display: flex; + align-items: center; + justify-content: center; +} + +@mixin flex-between { + display: flex; + align-items: center; + justify-content: space-between; +} + +// 文本溢出 +@mixin text-ellipsis { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +// 安全区域 +@mixin safe-bottom { + padding-bottom: constant(safe-area-inset-bottom); + padding-bottom: env(safe-area-inset-bottom); +} diff --git a/client/src/uni.scss b/client/src/uni.scss index 9f887c5..8ec32f3 100644 --- a/client/src/uni.scss +++ b/client/src/uni.scss @@ -1,31 +1,69 @@ -/* Design Tokens */ +/* ===== Design Tokens ===== */ + +// 颜色系统 $primary: #FF8C69; -$primary-light: #FFB89A; +$primary-light: #FFE8E0; $primary-dark: #E67355; $secondary: #6C9BCF; $accent: #FFD166; + $bg: #FFF8F0; $surface: #FFFFFF; $surface-warm: #FFF0E6; + $text: #2D1B1B; $text-sec: #8B7E7E; $text-muted: #BFB3B3; + $success: #7BC67E; $warning: #FFB347; $danger: #FF6B6B; +$danger-light: #FFE8E8; + $border: #F0E0D6; -$shadow-clay: 4px 4px 12px rgba(45, 27, 27, 0.08), inset -2px -2px 6px rgba(45, 27, 27, 0.04); -$shadow-clay-hover: 6px 6px 16px rgba(45, 27, 27, 0.12), inset -2px -2px 6px rgba(45, 27, 27, 0.04); -$shadow-clay-pressed: inset 2px 2px 6px rgba(45, 27, 27, 0.1); +// 间距系统 +$space-xs: 8rpx; +$space-sm: 16rpx; +$space-md: 24rpx; +$space-lg: 32rpx; +$space-xl: 40rpx; +$space-2xl: 48rpx; -$radius-sm: 8rpx; -$radius-md: 12rpx; -$radius-lg: 16rpx; -$radius-xl: 20rpx; +// 圆角系统 +$radius-xs: 8rpx; +$radius-sm: 12rpx; +$radius-md: 16rpx; +$radius-lg: 20rpx; +$radius-xl: 28rpx; +$radius-2xl: 40rpx; $radius-full: 9999rpx; -/* uCharts theme */ +// 字体系统 +$font-xs: 20rpx; +$font-sm: 22rpx; +$font-md: 24rpx; +$font-base: 26rpx; +$font-lg: 28rpx; +$font-xl: 32rpx; +$font-2xl: 36rpx; +$font-3xl: 40rpx; +$font-4xl: 56rpx; + +// 阴影系统 +$shadow-sm: 2rpx 2rpx 8rpx rgba(45, 27, 27, 0.06); +$shadow-md: 4rpx 4rpx 12rpx rgba(45, 27, 27, 0.08); +$shadow-lg: 8rpx 8rpx 24rpx rgba(45, 27, 27, 0.12); +$shadow-clay: $shadow-md, inset -2rpx -2rpx 6rpx rgba(45, 27, 27, 0.04); +$shadow-clay-hover: 6rpx 6rpx 16rpx rgba(45, 27, 27, 0.12), inset -2rpx -2rpx 6rpx rgba(45, 27, 27, 0.04); +$shadow-clay-pressed: inset 2rpx 2rpx 6rpx rgba(45, 27, 27, 0.1); + +// 动画 +$transition-fast: 0.15s ease; +$transition-normal: 0.2s ease; +$transition-slow: 0.3s ease; + +/* ===== uCharts theme ===== */ page { --u-charts-color-0: #FF8C69; --u-charts-color-1: #FFD166;