From c9a70a139c960759edadb45a407596054e9c11f2 Mon Sep 17 00:00:00 2001 From: Mikuisnotavailable Date: Mon, 30 Mar 2026 23:22:30 +0800 Subject: [PATCH] feat: initialize luobo printing WeChat mini program with full feature stack - Add comprehensive project documentation (README.md) with tech stack, structure, and usage guide - Implement API layer with modular services (auth, file, order, printer, price, pay) - Add Redux Toolkit state management with slices for user, order, file, and printer - Create complete page structure: index, login, user center, file management, order management, print settings - Build reusable UI components (OrderCard, PriceTable) with consistent styling - Establish design system with CSS variables, animations, and common styles - Add TypeScript type definitions for all domain entities - Implement utility functions for formatting, HTTP requests, and local storage - Configure ESLint to use CommonJS format for compatibility - Update dependencies: add Redux Toolkit, React Redux, and related packages - Style pages with gradient backgrounds, card layouts, and smooth animations BREAKING CHANGE: This is an initial project setup that replaces the previous Taro Hooks demo template with a complete printing service mini program --- BACKDROP_FILTER_ANALYSIS.md | 265 ++++++++ src/app.css | 177 +++++- src/components/OrderCard/OrderCard.css | 153 +++-- src/components/PriceTable/PriceTable.css | 183 ++++-- src/components/TabBar/TabBar.css | 135 +++++ src/components/TabBar/TabBar.tsx | 79 +++ src/components/TabBar/index.ts | 1 + src/pages/file/index.css | 331 ++++++---- src/pages/file/index.tsx | 4 + src/pages/file/upload/index.css | 382 +++++++----- src/pages/index/index.css | 555 +++++++++++------ src/pages/index/index.tsx | 172 +++--- src/pages/login/index.css | 476 ++++++++++----- src/pages/order/index.css | 565 +++++++++++------ src/pages/order/index.tsx | 81 ++- src/pages/user/index.css | 274 +++++---- src/pages/user/index.tsx | 4 + src/styles/animations.css | 742 +++++++++++++++-------- src/styles/common.css | 731 ++++++++++++++++------ src/styles/variables.css | 409 +++++++++---- 20 files changed, 4022 insertions(+), 1697 deletions(-) create mode 100644 BACKDROP_FILTER_ANALYSIS.md create mode 100644 src/components/TabBar/TabBar.css create mode 100644 src/components/TabBar/TabBar.tsx create mode 100644 src/components/TabBar/index.ts diff --git a/BACKDROP_FILTER_ANALYSIS.md b/BACKDROP_FILTER_ANALYSIS.md new file mode 100644 index 0000000..40a96b4 --- /dev/null +++ b/BACKDROP_FILTER_ANALYSIS.md @@ -0,0 +1,265 @@ +# 毛玻璃效果(backdrop-filter)渲染异常根本原因分析 + +## 问题现象 + +在微信小程序中,毛玻璃效果(`backdrop-filter: blur()`)可能出现渲染异常,表现为: + +- 毛玻璃效果不显示或显示不完整 +- 背景模糊效果异常 +- 透明度过渡不自然 + +## 根本原因分析 + +### 1. 层级冲突(z-index 堆叠上下文) + +**问题描述:** +当使用 `backdrop-filter` 的元素与其父容器或其他兄弟元素存在 z-index 冲突时,毛玻璃效果可能无法正常渲染。 + +**技术原理:** + +- `backdrop-filter` 需要作用于元素背后的内容 +- 如果元素没有创建独立的堆叠上下文,或者被其他元素遮挡,毛玻璃效果会失效 +- 在微信小程序中,堆叠上下文的创建规则与浏览器略有不同 + +**修复方案:** + +```css +/* 为使用毛玻璃效果的元素创建独立的堆叠上下文 */ +.glass-element { + backdrop-filter: blur(48rpx) saturate(180%); + -webkit-backdrop-filter: blur(48rpx) saturate(180%); + z-index: 1; /* 创建独立堆叠上下文 */ + position: relative; /* 确保 z-index 生效 */ +} +``` + +### 2. 背景色透明度问题 + +**问题描述:** +`backdrop-filter` 需要元素有足够的背景色(即使是半透明)才能正确工作。完全透明的背景会导致毛玻璃效果无法渲染。 + +**技术原理:** + +- `backdrop-filter` 作用于元素背景层 +- 如果背景完全透明(`background: transparent`),没有像素可供处理 +- 必须设置半透明背景色,如 `rgba(255, 255, 255, 0.72)` + +**修复方案:** + +```css +/* 确保有足够的背景色让毛玻璃效果正确渲染 */ +.glass-element { + background: rgba(255, 255, 255, 0.72); /* 半透明背景 */ + backdrop-filter: blur(48rpx) saturate(180%); + -webkit-backdrop-filter: blur(48rpx) saturate(180%); +} +``` + +### 3. 父容器定位问题 + +**问题描述:** +使用 `position: fixed` 或 `position: absolute` 的装饰元素可能影响毛玻璃效果的渲染。 + +**技术原理:** + +- 绝对定位元素可能创建新的堆叠上下文 +- 如果装饰元素(如光晕效果)位于毛玻璃元素上方,会遮挡毛玻璃效果 +- 需要将装饰元素置于毛玻璃元素下方 + +**修复方案:** + +```css +/* 装饰光晕元素应位于毛玻璃元素下方 */ +.ambient-light { + position: absolute; + z-index: 0; /* 确保在毛玻璃元素下方 */ + pointer-events: none; /* 不拦截鼠标事件 */ +} + +.glass-element { + position: relative; + z-index: 1; /* 确保在装饰元素上方 */ +} +``` + +### 4. 微信小程序平台兼容性 + +**问题描述:** +微信小程序对 `backdrop-filter` 的支持有限,需要特殊处理。 + +**技术原理:** + +- 微信小程序基于 WebView,但有自己的渲染引擎 +- 需要同时使用标准属性和 `-webkit-` 前缀 +- 某些复杂效果可能需要降级处理 + +**修复方案:** + +```css +.glass-element { + /* 标准属性 */ + backdrop-filter: blur(48rpx) saturate(180%); + /* WebKit 前缀 - 微信小程序必需 */ + -webkit-backdrop-filter: blur(48rpx) saturate(180%); + /* 降级背景色 - 当毛玻璃不支持时使用 */ + background: rgba(255, 255, 255, 0.85); +} +``` + +## 已实施的修复方案 + +### 修改的文件 + +#### 1. aiyintong/src/pages/index/index.css + +**修复内容:** + +- 为 `.index-page` 添加 `z-index: 0` 和 `background: transparent` +- 为 `.ambient-light` 添加 `will-change: transform` +- 为 `.user-card` 和 `.action-item` 添加 `z-index: 1` + +```css +.index-page { + z-index: 0; + background: transparent; +} + +.ambient-light { + will-change: transform; +} + +.user-card { + z-index: 1; +} + +.action-item { + z-index: 1; +} +``` + +#### 2. aiyintong/src/pages/order/index.css + +**修复内容:** + +- 为 `.order-page` 添加 `z-index: 0` 和 `background: transparent` +- 为 `.order-item` 添加 `z-index: 1` + +```css +.order-page { + z-index: 0; + background: transparent; +} + +.order-item { + z-index: 1; +} +``` + +## 验证方法 + +### 1. 开发者工具检查 + +在微信开发者工具中: + +- 检查元素是否有正确的 `backdrop-filter` 和 `-webkit-backdrop-filter` 属性 +- 确认 z-index 层级关系正确 +- 验证背景色不是完全透明 + +### 2. 真机测试 + +在真实设备上测试: + +- 不同 iOS/Android 版本的渲染效果 +- 滑动时的性能表现 +- 多个毛玻璃元素叠加时的效果 + +### 3. 性能监控 + +使用微信开发者工具的性能面板: + +- 监控 GPU 使用情况 +- 检查是否有渲染卡顿 +- 确保 `will-change` 属性正确应用 + +## 最佳实践建议 + +### 1. 毛玻璃效果使用规范 + +```css +/* 推荐的毛玻璃效果写法 */ +.glass-card { + /* 必须有半透明背景 */ + background: rgba(255, 255, 255, 0.72); + + /* 同时使用标准属性和前缀 */ + backdrop-filter: blur(48rpx) saturate(180%); + -webkit-backdrop-filter: blur(48rpx) saturate(180%); + + /* 创建独立堆叠上下文 */ + position: relative; + z-index: 1; + + /* 可选:性能优化 */ + will-change: transform; +} +``` + +### 2. 层级管理规范 + +```css +/* 页面容器 */ +.page { + position: relative; + z-index: 0; + background: transparent; /* 让页面背景透出 */ +} + +/* 装饰层(最底层) */ +.decoration-layer { + position: absolute; + z-index: 0; + pointer-events: none; +} + +/* 内容层(中间层) */ +.content-layer { + position: relative; + z-index: 1; +} + +/* 弹窗层(最顶层) */ +.modal-layer { + position: fixed; + z-index: 1000; +} +``` + +### 3. 性能优化建议 + +```css +/* 使用 will-change 提示浏览器优化 */ +.glass-element { + will-change: transform; + transform: translateZ(0); /* 启用 GPU 加速 */ +} + +/* 避免过度使用毛玻璃 */ +/* 在列表等滚动区域,考虑使用静态背景 */ +.scroll-list { + background: rgba(255, 255, 255, 0.95); /* 更高不透明度 */ + backdrop-filter: blur(24rpx); /* 降低模糊度 */ +} +``` + +## 总结 + +毛玻璃效果在微信小程序中渲染异常的根本原因主要包括: + +1. **层级冲突** - 缺少独立的堆叠上下文 +2. **背景色问题** - 背景完全透明导致无法应用效果 +3. **定位问题** - 装饰元素遮挡毛玻璃效果 +4. **平台兼容性** - 缺少必要的浏览器前缀 + +通过为毛玻璃元素创建独立的堆叠上下文、确保半透明背景、正确管理 z-index 层级,以及添加必要的浏览器前缀,可以有效解决这些问题。 + +所有修复已在 `index.css` 和 `order/index.css` 中实施,确保毛玻璃效果在微信小程序中正确渲染。 diff --git a/src/app.css b/src/app.css index d7495de..5b36d5f 100644 --- a/src/app.css +++ b/src/app.css @@ -1,5 +1,6 @@ /* ============================================ - 全局样式入口 - 爱印通 + 全局样式入口 - 爱印通 Apple Liquid Glass 设计系统 + 半透明毛玻璃、圆润悬浮控件、动态光影折射 ============================================ */ /* 引入设计系统和动画 */ @@ -7,20 +8,24 @@ @import './styles/common.css'; @import './styles/animations.css'; -/* 全局页面样式 */ +/* ========== 全局页面样式 ========== */ + page { background: var(--bg-page); min-height: 100vh; - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', - 'Hiragino Sans GB', 'Microsoft YaHei', 'Helvetica Neue', sans-serif; + font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'SF Pro Text', + 'Helvetica Neue', 'PingFang SC', 'Hiragino Sans GB', + 'Microsoft YaHei', sans-serif; color: var(--text-primary); - font-size: var(--font-size-base); - line-height: 1.5; + font-size: var(--font-size-body); + line-height: var(--line-height-normal); -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; + -webkit-tap-highlight-color: transparent; } -/* 全局滚动条样式 */ +/* ========== 全局滚动条样式 ========== */ + ::-webkit-scrollbar { width: 4px; height: 4px; @@ -36,28 +41,31 @@ page { } ::-webkit-scrollbar-thumb:hover { - background: var(--text-placeholder); + background: var(--text-tertiary); } -/* 选中文本样式 */ +/* ========== 选中文本样式 ========== */ + ::selection { - background: rgba(90, 147, 223, 0.2); + background: var(--primary-opacity-30); color: var(--text-primary); } -/* 焦点样式 */ +/* ========== 焦点样式 ========== */ + :focus { outline: none; } :focus-visible { - outline: 2px solid var(--primary-color); + outline: 2px solid var(--primary); outline-offset: 2px; } -/* 链接样式 */ +/* ========== 链接样式 ========== */ + a { - color: var(--primary-color); + color: var(--primary); text-decoration: none; transition: color var(--transition-fast); } @@ -66,19 +74,33 @@ a:hover { color: var(--primary-dark); } -/* 图片样式 */ +a:active { + opacity: 0.8; +} + +/* ========== 图片样式 ========== */ + image { max-width: 100%; height: auto; + display: block; } -/* 文本选择禁用 */ +img { + max-width: 100%; + height: auto; + display: block; +} + +/* ========== 文本选择禁用 ========== */ + .no-select { user-select: none; -webkit-user-select: none; } -/* 安全区域 */ +/* ========== 安全区域 ========== */ + .safe-area-top { padding-top: constant(safe-area-inset-top); padding-top: env(safe-area-inset-top); @@ -99,7 +121,8 @@ image { padding-right: env(safe-area-inset-right); } -/* 隐藏滚动条但保持可滚动 */ +/* ========== 隐藏滚动条但保持可滚动 ========== */ + .hide-scrollbar { -ms-overflow-style: none; scrollbar-width: none; @@ -109,7 +132,8 @@ image { display: none; } -/* 文本截断 */ +/* ========== 文本截断 ========== */ + .truncate { overflow: hidden; text-overflow: ellipsis; @@ -121,7 +145,6 @@ image { -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; - text-overflow: ellipsis; } .truncate-3 { @@ -129,15 +152,10 @@ image { -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; - text-overflow: ellipsis; } -/* 点击高亮移除 */ -* { - -webkit-tap-highlight-color: transparent; -} +/* ========== 打印样式 ========== */ -/* 打印样式 */ @media print { page { background: white; @@ -148,12 +166,15 @@ image { } } -/* 深色模式支持 */ +/* ========== 深色模式 ========== */ + @media (prefers-color-scheme: dark) { - /* 未来可扩展深色模式 */ + /* 深色模式变量已在 variables.css 中定义 */ + /* 页面会自动切换到深色模式 */ } -/* 减少动画偏好 */ +/* ========== 减少动画偏好 ========== */ + @media (prefers-reduced-motion: reduce) { *, *::before, @@ -161,5 +182,101 @@ image { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; transition-duration: 0.01ms !important; + scroll-behavior: auto !important; } -} \ No newline at end of file +} + +/* ========== 高对比度模式 ========== */ + +@media (prefers-contrast: high) { + :root { + --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.2); + --shadow-md: 0 2px 4px rgba(0, 0, 0, 0.3); + --shadow-lg: 0 4px 8px rgba(0, 0, 0, 0.4); + } +} + +/* ========== 全局工具类 ========== */ + +/* 清除浮动 */ +.clearfix::after { + content: ''; + display: table; + clear: both; +} + +/* 屏幕阅读器专用 */ +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border: 0; +} + +/* 禁用滚动 */ +.no-scroll { + overflow: hidden; +} + +/* 固定定位全屏遮罩 */ +.overlay { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: var(--bg-overlay); + z-index: 1000; + animation: fadeIn 0.2s ease forwards; +} + +/* 毛玻璃遮罩 */ +.overlay-glass { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.3); + backdrop-filter: blur(8px); + -webkit-backdrop-filter: blur(8px); + z-index: 1000; + animation: fadeIn 0.2s ease forwards; +} + +/* ========== 页面过渡动画 ========== */ + +.page-transition-enter { + animation: fadeInRight 0.35s cubic-bezier(0.25, 0.1, 0.25, 1) forwards; +} + +.page-transition-exit { + animation: fadeInLeft 0.3s cubic-bezier(0.25, 0.1, 0.25, 1) reverse forwards; +} + +@keyframes fadeInRight { + from { + opacity: 0; + transform: translateX(20px); + } + to { + opacity: 1; + transform: translateX(0); + } +} + +@keyframes fadeInLeft { + from { + opacity: 1; + transform: translateX(0); + } + to { + opacity: 0; + transform: translateX(-20px); + } +} diff --git a/src/components/OrderCard/OrderCard.css b/src/components/OrderCard/OrderCard.css index bda989c..eddfa01 100644 --- a/src/components/OrderCard/OrderCard.css +++ b/src/components/OrderCard/OrderCard.css @@ -1,17 +1,23 @@ /* ============================================ - 订单卡片组件样式 - 爱印通 + 订单卡片组件样式 - 爱印通 Apple Liquid Glass 设计系统 + 大字体大控件版本 - 微信小程序优化 ============================================ */ @import '../../styles/variables.css'; +/* ========== 订单卡片 ========== */ + .order-card { - background: var(--bg-card); + background: var(--bg-glass); + backdrop-filter: blur(48rpx) saturate(180%); + -webkit-backdrop-filter: blur(48rpx) saturate(180%); border-radius: var(--radius-lg); - padding: 20px; - margin-bottom: 16px; + padding: 36rpx; + margin-bottom: 24rpx; box-shadow: var(--shadow-md); - border: 1px solid var(--border-light); + border: 1px solid var(--glass-border); transition: all var(--transition-base); + cursor: pointer; } .order-card:active { @@ -19,114 +25,123 @@ box-shadow: var(--shadow-sm); } -/* 订单头部 */ +/* ========== 订单头部 ========== */ + .order-header { display: flex; justify-content: space-between; align-items: center; - margin-bottom: 16px; - padding-bottom: 16px; - border-bottom: 1px solid var(--border-light); + margin-bottom: 28rpx; + padding-bottom: 28rpx; + border-bottom: 1px solid var(--border-separator); } .order-id { - font-size: var(--font-size-xs); + font-size: 26rpx; color: var(--text-tertiary); + font-weight: 400; } .order-status { - font-size: var(--font-size-sm); - font-weight: var(--font-weight-medium); - padding: 4px 12px; - border-radius: 12px; - background: rgba(24, 144, 255, 0.1); - color: var(--info-color); + font-size: 26rpx; + font-weight: 500; + padding: 8rpx 24rpx; + border-radius: var(--radius-full); + background: var(--primary-opacity-10); + color: var(--primary); + transition: all var(--transition-base); } .order-status.completed { - background: rgba(82, 196, 26, 0.1); - color: var(--success-color); + background: rgba(52, 199, 89, 0.1); + color: var(--success); } .order-status.cancelled { - background: rgba(255, 77, 79, 0.1); - color: var(--error-color); + background: rgba(255, 59, 48, 0.1); + color: var(--error); } .order-status.pending { - background: rgba(250, 173, 20, 0.1); - color: var(--warning-color); + background: rgba(255, 149, 0, 0.1); + color: var(--warning); } -/* 订单内容 */ +/* ========== 订单内容 ========== */ + .order-body { - margin-bottom: 16px; + margin-bottom: 28rpx; } .file-name { - font-size: var(--font-size-md); - font-weight: var(--font-weight-semibold); + font-size: 34rpx; + font-weight: 600; color: var(--text-primary); display: block; - margin-bottom: 8px; + margin-bottom: 16rpx; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; + letter-spacing: -0.2px; } .order-detail { - font-size: var(--font-size-sm); + font-size: 28rpx; color: var(--text-secondary); display: block; - margin-bottom: 4px; + margin-bottom: 8rpx; + font-weight: 400; } .printer-name { - font-size: var(--font-size-xs); + font-size: 26rpx; color: var(--text-tertiary); display: block; + font-weight: 400; } -/* 订单底部 */ +/* ========== 订单底部 ========== */ + .order-footer { display: flex; justify-content: space-between; align-items: center; - border-top: 1px solid var(--border-light); - padding-top: 16px; + border-top: 1px solid var(--border-separator); + padding-top: 28rpx; } .order-price { - font-size: var(--font-size-xl); - color: var(--error-color); - font-weight: var(--font-weight-bold); + font-size: 40rpx; + color: var(--primary); + font-weight: 700; + letter-spacing: -0.5px; } .order-time { - font-size: var(--font-size-xs); + font-size: 24rpx; color: var(--text-tertiary); + font-weight: 400; } -/* 订单操作区 */ +/* ========== 订单操作区 ========== */ + .order-actions { display: flex; - gap: 12px; - margin-top: 16px; - padding-top: 16px; - border-top: 1px solid var(--border-light); + gap: 20rpx; + margin-top: 28rpx; + padding-top: 28rpx; + border-top: 1px solid var(--border-separator); } .order-action-btn { flex: 1; - height: 60px; + height: 80rpx; border-radius: var(--radius-md); - font-size: var(--font-size-sm); - border: none; - transition: all var(--transition-fast); -} - -.order-action-btn::after { + font-size: 28rpx; + font-weight: 500; border: none; + transition: all var(--transition-scale); + cursor: pointer; } .order-action-btn:active { @@ -134,31 +149,49 @@ } .order-action-btn.primary { - background: var(--primary-gradient); - color: #fff; - box-shadow: 0 4px 12px rgba(90, 147, 223, 0.2); + background: var(--primary); + color: var(--text-inverse); + box-shadow: 0 4rpx 16rpx rgba(0, 122, 255, 0.2); } .order-action-btn.secondary { - background: #f5f5f7; + background: var(--bg-hover); color: var(--text-secondary); } -/* 卡片变体 */ +/* ========== 卡片变体 ========== */ + .order-card.highlighted { - border-color: var(--primary-color); - box-shadow: 0 0 0 2px rgba(90, 147, 223, 0.1), var(--shadow-md); + border-color: var(--primary); + box-shadow: 0 0 0 4rpx var(--primary-opacity-20), var(--shadow-md); } .order-card.compact { - padding: 16px; + padding: 28rpx; } .order-card.compact .order-header { - margin-bottom: 12px; - padding-bottom: 12px; + margin-bottom: 24rpx; + padding-bottom: 24rpx; } .order-card.compact .order-body { - margin-bottom: 12px; + margin-bottom: 24rpx; +} + +.order-card.compact .file-name { + font-size: 32rpx; +} + +.order-card.compact .order-detail { + font-size: 26rpx; +} + +/* ========== 悬浮效果 ========== */ + +@media (hover: hover) { + .order-card:hover { + box-shadow: var(--shadow-lg); + transform: translateY(-4rpx); + } } diff --git a/src/components/PriceTable/PriceTable.css b/src/components/PriceTable/PriceTable.css index f60caac..39d0147 100644 --- a/src/components/PriceTable/PriceTable.css +++ b/src/components/PriceTable/PriceTable.css @@ -1,114 +1,136 @@ /* ============================================ - 价目表组件样式 - 爱印通 + 价目表组件样式 - 爱印通 Apple Liquid Glass 设计系统 + 大字体大控件版本 - 微信小程序优化 ============================================ */ @import '../../styles/variables.css'; +/* ========== 价目表容器 ========== */ + .price-table { - background: var(--bg-card); + background: var(--bg-glass); + backdrop-filter: blur(48rpx) saturate(180%); + -webkit-backdrop-filter: blur(48rpx) saturate(180%); border-radius: var(--radius-lg); - padding: 24px; + padding: 40rpx; box-shadow: var(--shadow-md); - border: 1px solid var(--border-light); + border: 1px solid var(--glass-border); + transition: all var(--transition-base); } +/* ========== 价目表标题 ========== */ + .price-title { - font-size: var(--font-size-xl); - font-weight: var(--font-weight-semibold); + font-size: 36rpx; + font-weight: 600; color: var(--text-primary); display: block; - margin-bottom: 20px; - padding-left: 12px; - border-left: 4px solid var(--primary-color); + margin-bottom: 32rpx; + padding-left: 24rpx; + border-left: 6rpx solid var(--primary); + letter-spacing: -0.2px; } +/* ========== 价目列表 ========== */ + .price-list { display: flex; flex-direction: column; + gap: 8rpx; } +/* ========== 价目项 ========== */ + .price-item { display: flex; justify-content: space-between; align-items: center; - padding: 16px 12px; - border-radius: var(--radius-sm); - transition: all var(--transition-fast); + padding: 28rpx 24rpx; + border-radius: var(--radius-md); + transition: all var(--transition-base); cursor: pointer; } .price-item:nth-child(odd) { - background: #fafbfc; + background: rgba(0, 122, 255, 0.02); } .price-item:nth-child(even) { - background: var(--bg-card); + background: transparent; } .price-item:active { transform: scale(0.98); + background: var(--bg-hover); } .price-item.selected { - background: rgba(90, 147, 223, 0.1); - border: 1px solid var(--primary-color); + background: var(--primary-opacity-10); + border: 3rpx solid var(--primary); } .price-item.selected .price-name { - color: var(--primary-color); - font-weight: var(--font-weight-semibold); + color: var(--primary); + font-weight: 600; } +/* 价目名称 */ .price-name { - font-size: var(--font-size-base); + font-size: 30rpx; color: var(--text-secondary); - font-weight: var(--font-weight-medium); + font-weight: 500; transition: color var(--transition-fast); } +/* 价目值 */ .price-value { - font-size: var(--font-size-md); - color: var(--error-color); - font-weight: var(--font-weight-semibold); + font-size: 32rpx; + color: var(--primary); + font-weight: 600; + letter-spacing: -0.3px; } -/* 价目表变体 */ +/* ========== 紧凑变体 ========== */ + .price-table.compact { - padding: 16px; + padding: 32rpx; } .price-table.compact .price-title { - font-size: var(--font-size-lg); - margin-bottom: 16px; + font-size: 32rpx; + margin-bottom: 28rpx; } .price-table.compact .price-item { - padding: 12px 8px; + padding: 24rpx 20rpx; } .price-table.compact .price-name { - font-size: var(--font-size-sm); + font-size: 28rpx; } .price-table.compact .price-value { - font-size: var(--font-size-base); + font-size: 30rpx; } -/* 带图标的价目项 */ +/* ========== 带图标的价目项 ========== */ + .price-item-with-icon { - padding-left: 16px; + padding-left: 8rpx; } .price-item-icon { - width: 36px; - height: 36px; - border-radius: var(--radius-sm); + width: 72rpx; + height: 72rpx; + border-radius: var(--radius-md); display: flex; align-items: center; justify-content: center; - margin-right: 12px; - font-size: 20px; + margin-right: 24rpx; + font-size: 36rpx; background: linear-gradient(135deg, #e8f0fe 0%, #d0dcfa 100%); + box-shadow: var(--shadow-sm); + flex-shrink: 0; } .price-item-content { @@ -116,25 +138,88 @@ display: flex; justify-content: space-between; align-items: center; + min-width: 0; } -/* 促销标签 */ +/* ========== 促销标签 ========== */ + .price-badge { display: inline-flex; align-items: center; - padding: 2px 8px; - background: linear-gradient(135deg, #ff4d4f 0%, #ff7875 100%); - color: #fff; - font-size: 18px; - border-radius: 8px; - margin-left: 8px; - transform: scale(0.8); + padding: 4rpx 16rpx; + background: linear-gradient(135deg, #FF3B30 0%, #FF453A 100%); + color: #ffffff; + font-size: 24rpx; + font-weight: 600; + border-radius: var(--radius-sm); + margin-left: 16rpx; + flex-shrink: 0; + box-shadow: 0 4rpx 12rpx rgba(255, 59, 48, 0.25); } -/* 原价显示 */ +/* ========== 原价显示 ========== */ + .price-original { - font-size: var(--font-size-xs); - color: var(--text-placeholder); + font-size: 26rpx; + color: var(--text-tertiary); text-decoration: line-through; - margin-right: 4px; + margin-right: 12rpx; + font-weight: 400; +} + +/* ========== 价目表头部操作 ========== */ + +.price-table-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 32rpx; +} + +.price-table-action { + font-size: 28rpx; + color: var(--primary); + font-weight: 500; + cursor: pointer; + transition: opacity var(--transition-fast); +} + +.price-table-action:active { + opacity: 0.7; +} + +/* ========== 悬浮效果 ========== */ + +@media (hover: hover) { + .price-item:hover { + background: var(--bg-hover); + } + + .price-item.selected:hover { + background: var(--primary-opacity-10); + } +} + +/* ========== 响应式适配 ========== */ + +@media (max-width: 750rpx) { + .price-table { + padding: 32rpx; + } + + .price-title { + font-size: 32rpx; + } + + .price-item { + padding: 24rpx 20rpx; + } + + .price-name { + font-size: 28rpx; + } + + .price-value { + font-size: 30rpx; + } } diff --git a/src/components/TabBar/TabBar.css b/src/components/TabBar/TabBar.css new file mode 100644 index 0000000..c4045ee --- /dev/null +++ b/src/components/TabBar/TabBar.css @@ -0,0 +1,135 @@ +/* ============================================ + 底部导航栏样式 - 爱印通 Apple Liquid Glass 设计系统 + ============================================ */ + +@import '../../styles/variables.css'; + +.tab-bar { + position: fixed; + bottom: 0; + left: 0; + right: 0; + height: 120rpx; + background: rgba(255, 255, 255, 0.85); + backdrop-filter: blur(24px) saturate(180%); + -webkit-backdrop-filter: blur(24px) saturate(180%); + border-top: 1px solid rgba(255, 255, 255, 0.3); + display: flex; + align-items: center; + justify-content: space-around; + padding-bottom: constant(safe-area-inset-bottom); + padding-bottom: env(safe-area-inset-bottom); + box-shadow: 0 -4rpx 24rpx rgba(0, 0, 0, 0.08); + z-index: 1000; +} + +.tab-item { + flex: 1; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + height: 100%; + cursor: pointer; + transition: all var(--transition-base); + position: relative; +} + +.tab-item:active { + transform: scale(0.95); +} + +.tab-icon { + width: 48rpx; + height: 48rpx; + display: flex; + align-items: center; + justify-content: center; + font-size: 44rpx; + color: var(--text-tertiary); + transition: all var(--transition-base); + margin-bottom: 4rpx; +} + +.tab-item.active .tab-icon { + color: var(--primary); +} + +.tab-label { + font-size: 22rpx; + color: var(--text-tertiary); + transition: all var(--transition-base); +} + +.tab-item.active .tab-label { + color: var(--primary); + font-weight: 600; +} + +/* 选中指示器 */ +.tab-indicator { + position: absolute; + bottom: 8rpx; + left: 50%; + transform: translateX(-50%); + width: 40rpx; + height: 6rpx; + background: var(--primary); + border-radius: 3rpx; + opacity: 0; + transition: opacity var(--transition-base); +} + +.tab-item.active .tab-indicator { + opacity: 1; +} + +/* 中间大按钮 - 用于主要操作 */ +.tab-item.center-action { + position: relative; +} + +.tab-action-btn { + width: 100rpx; + height: 100rpx; + background: linear-gradient(135deg, #5AC8FA 0%, #007AFF 100%); + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + box-shadow: 0 8rpx 24rpx rgba(90, 200, 250, 0.4); + transition: all var(--transition-scale); +} + +.tab-action-btn:active { + transform: scale(0.92); + box-shadow: 0 4rpx 12rpx rgba(0, 122, 255, 0.25); +} + +.tab-action-icon { + font-size: 48rpx; + color: rgba(255, 255, 255, 0.95); +} + +/* 徽章通知 */ +.tab-badge { + position: absolute; + top: 12rpx; + right: 20%; + min-width: 32rpx; + height: 32rpx; + background: var(--error); + color: #fff; + font-size: 20rpx; + font-weight: 600; + border-radius: 16rpx; + display: flex; + align-items: center; + justify-content: center; + padding: 0 8rpx; + box-shadow: 0 2rpx 8rpx rgba(255, 59, 48, 0.3); +} + +.tab-badge:empty { + display: none; +} diff --git a/src/components/TabBar/TabBar.tsx b/src/components/TabBar/TabBar.tsx new file mode 100644 index 0000000..c2d65f4 --- /dev/null +++ b/src/components/TabBar/TabBar.tsx @@ -0,0 +1,79 @@ +import React from 'react'; +import Taro from '@tarojs/taro'; +import { View, Text } from '@tarojs/components'; +import './TabBar.css'; + +interface TabBarProps { + current: number; + onChange: (index: number) => void; +} + +interface TabItem { + icon: string; + label: string; + path: string; + badge?: string; +} + +const tabs: TabItem[] = [ + { icon: '🏠', label: '首页', path: '/pages/index/index' }, + { icon: '📋', label: '订单', path: '/pages/order/index' }, + { icon: '', label: '', path: '/pages/file/upload/index' }, // 中间大按钮 + { icon: '📁', label: '文件', path: '/pages/file/index' }, + { icon: '👤', label: '我的', path: '/pages/user/index' }, +]; + +const TabBar: React.FC = ({ current, onChange }) => { + const handleTabChange = (index: number) => { + const tab = tabs[index]; + if (!tab) return; + + // 如果点击当前已激活的标签,不执行任何操作 + if (index === current) return; + + if (index === 2) { + // 中间大按钮 - 上传/打印,使用 navigateTo + Taro.navigateTo({ url: tab.path }); + return; + } + + // 优先尝试 switchTab(适用于 TabBar 页面) + Taro.switchTab({ + url: tab.path, + fail: () => { + // 如果 switchTab 失败,使用 navigateTo 并关闭所有页面 + Taro.reLaunch({ url: tab.path }); + } + }); + + // 更新当前选中的标签索引 + onChange(index); + }; + + return ( + + {tabs.map((tab, index) => ( + handleTabChange(index)} + > + {index === 2 ? ( + + + + + ) : ( + <> + {tab.icon} + {tab.label} + {tab.badge && {tab.badge}} + + + )} + + ))} + + ); +}; + +export default TabBar; diff --git a/src/components/TabBar/index.ts b/src/components/TabBar/index.ts new file mode 100644 index 0000000..3bfa291 --- /dev/null +++ b/src/components/TabBar/index.ts @@ -0,0 +1 @@ +export { default } from './TabBar'; diff --git a/src/pages/file/index.css b/src/pages/file/index.css index 41532a6..062dbaf 100644 --- a/src/pages/file/index.css +++ b/src/pages/file/index.css @@ -1,88 +1,201 @@ /* ============================================ - 文件列表页面样式 - 爱印通 + 文件列表页面样式 - 爱印通 Apple Liquid Glass 设计系统 + 大字体大控件版本 - 微信小程序优化 ============================================ */ @import '../../styles/variables.css'; page { - background: linear-gradient(180deg, #e8f0fe 0%, #f0f5ff 30%, #fafbfc 100%); + background: var(--bg-page-gradient); min-height: 100vh; } .file-page { min-height: 100vh; - padding: 32px 24px 140px; + padding: 48rpx 32rpx 280rpx; + position: relative; } -/* 页面头部 */ +/* ========== 页面头部 ========== */ + .file-header { display: flex; justify-content: space-between; align-items: center; - margin-bottom: 28px; - animation: fadeInDown 0.4s ease forwards; + margin-bottom: 48rpx; + animation: fadeInDown 0.4s cubic-bezier(0.34, 1.56, 0.64, 1) forwards; } .header-title { - font-size: var(--font-size-2xl); - font-weight: var(--font-weight-bold); + font-size: 56rpx; + font-weight: 700; color: var(--text-primary); + letter-spacing: -0.3px; } .upload-btn { - background: var(--primary-gradient); - color: #fff; - font-size: var(--font-size-sm); - font-weight: var(--font-weight-medium); - padding: 14px 24px; - border-radius: 36px; + background: var(--primary); + color: var(--text-inverse); + font-size: 28rpx; + font-weight: 600; + padding: 20rpx 40rpx; + border-radius: var(--radius-md); border: none; - box-shadow: 0 4px 12px rgba(90, 147, 223, 0.2); - transition: all var(--transition-base); + box-shadow: 0 4rpx 16rpx rgba(0, 122, 255, 0.2); + transition: all var(--transition-scale); white-space: nowrap; -} - -.upload-btn::after { - border: none; + cursor: pointer; } .upload-btn:active { transform: scale(0.96); + opacity: 0.9; } -/* 文件列表 */ +/* ========== 统计栏 ========== */ + +.stats-bar { + display: flex; + justify-content: space-between; + padding: 32rpx 40rpx; + background: linear-gradient(135deg, #F0F7FF 0%, #FFFFFF 50%, #F5F3FF 100%); + border-radius: var(--radius-lg); + margin-bottom: 40rpx; + box-shadow: var(--shadow-sm); + border: 1px solid var(--border-light); + position: relative; + overflow: hidden; +} + +/* 统计栏左侧装饰 */ +.stats-bar::before { + content: ''; + position: absolute; + left: 0; + top: 0; + bottom: 0; + width: 8rpx; + background: var(--primary-gradient); + border-radius: var(--radius-lg) 0 0 var(--radius-lg); +} + +.stat-item { + text-align: center; + flex: 1; +} + +.stat-value { + font-size: 44rpx; + font-weight: 700; + color: var(--primary); + display: block; + letter-spacing: -0.5px; +} + +.stat-label { + font-size: 24rpx; + color: var(--text-tertiary); + margin-top: 8rpx; + display: block; + font-weight: 400; +} + +/* ========== 筛选栏 ========== */ + +.filter-bar { + display: flex; + gap: 16rpx; + margin-bottom: 40rpx; + overflow-x: auto; + padding-bottom: 8rpx; + -webkit-overflow-scrolling: touch; + scrollbar-width: none; +} + +.filter-bar::-webkit-scrollbar { + display: none; +} + +.filter-chip { + padding: 16rpx 36rpx; + border-radius: var(--radius-full); + font-size: 28rpx; + background: var(--bg-card); + color: var(--text-secondary); + border: 2rpx solid var(--border-primary); + white-space: nowrap; + transition: all var(--transition-base); + cursor: pointer; + font-weight: 500; +} + +.filter-chip.active { + background: var(--primary); + color: var(--text-inverse); + border-color: var(--primary); + box-shadow: 0 4rpx 16rpx rgba(0, 122, 255, 0.25); +} + +.filter-chip:active { + transform: scale(0.95); +} + +/* ========== 文件列表 ========== */ + .file-list { display: flex; flex-direction: column; - gap: 16px; + gap: 24rpx; } .file-item { - background: var(--bg-card); + background: linear-gradient(135deg, #FFFFFF 0%, #FAFBFC 100%); border-radius: var(--radius-lg); - padding: 20px; + padding: 32rpx; display: flex; align-items: center; box-shadow: var(--shadow-md); border: 1px solid var(--border-light); transition: all var(--transition-base); - animation: slideUp 0.4s ease forwards; + animation: fadeInUp 0.4s cubic-bezier(0.34, 1.56, 0.64, 1) forwards; opacity: 0; + cursor: pointer; + position: relative; } -.file-item:nth-child(1) { animation-delay: 0.1s; } -.file-item:nth-child(2) { animation-delay: 0.2s; } -.file-item:nth-child(3) { animation-delay: 0.3s; } +/* 文件项左侧强调线 */ +.file-item::before { + content: ''; + position: absolute; + left: 0; + top: 24rpx; + bottom: 24rpx; + width: 6rpx; + background: var(--primary); + border-radius: 0 var(--radius-sm) var(--radius-sm) 0; + opacity: 0; + transition: opacity var(--transition-base); +} + +.file-item:active::before { + opacity: 1; +} + +.file-item:nth-child(1) { animation-delay: 0.05s; } +.file-item:nth-child(2) { animation-delay: 0.1s; } +.file-item:nth-child(3) { animation-delay: 0.15s; } +.file-item:nth-child(4) { animation-delay: 0.2s; } +.file-item:nth-child(5) { animation-delay: 0.25s; } .file-item:active { transform: scale(0.98); box-shadow: var(--shadow-sm); } -@keyframes slideUp { +@keyframes fadeInUp { from { opacity: 0; - transform: translateY(20px); + transform: translateY(32rpx); } to { opacity: 1; @@ -91,57 +204,59 @@ page { } .file-icon { - font-size: 44px; - margin-right: 16px; + font-size: 72rpx; + margin-right: 28rpx; flex-shrink: 0; - width: 56px; - height: 56px; + width: 96rpx; + height: 96rpx; display: flex; align-items: center; justify-content: center; background: linear-gradient(135deg, #e8f0fe 0%, #d0dcfa 100%); - border-radius: var(--radius-sm); + border-radius: var(--radius-md); + box-shadow: var(--shadow-sm); } .file-info { flex: 1; overflow: hidden; + min-width: 0; } .file-name { - font-size: var(--font-size-base); + font-size: 32rpx; color: var(--text-primary); - font-weight: var(--font-weight-medium); + font-weight: 600; display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; - margin-bottom: 4px; + margin-bottom: 8rpx; + letter-spacing: -0.2px; } .file-meta { - font-size: var(--font-size-xs); + font-size: 26rpx; color: var(--text-tertiary); + font-weight: 400; } -/* 操作按钮 */ .file-actions { display: flex; - gap: 8px; + gap: 16rpx; flex-shrink: 0; + margin-left: 16rpx; } .action-btn { - font-size: var(--font-size-xs); - padding: 8px 14px; - border-radius: var(--radius-sm); + font-size: 26rpx; + padding: 16rpx 28rpx; + border-radius: var(--radius-md); border: none; transition: all var(--transition-fast); white-space: nowrap; -} - -.action-btn::after { - border: none; + font-weight: 500; + cursor: pointer; } .action-btn:active { @@ -149,129 +264,81 @@ page { } .action-btn.print { - color: var(--primary-color); - background: rgba(90, 147, 223, 0.1); + color: var(--primary); + background: var(--primary-opacity-10); } .action-btn.print:active { - background: rgba(90, 147, 223, 0.2); + background: var(--primary-opacity-20); } .action-btn.delete { - color: var(--error-color); - background: rgba(255, 77, 79, 0.1); + color: var(--error); + background: rgba(255, 59, 48, 0.08); } .action-btn.delete:active { - background: rgba(255, 77, 79, 0.2); + background: rgba(255, 59, 48, 0.15); } -/* 空状态 */ +/* ========== 空状态 ========== */ + .empty-state { display: flex; flex-direction: column; align-items: center; justify-content: center; - padding: 100px 0; - animation: fadeIn 0.5s ease forwards; + padding: 160rpx 48rpx; + animation: fadeIn 0.5s cubic-bezier(0.34, 1.56, 0.64, 1) forwards; } .empty-icon { - font-size: 100px; - margin-bottom: 20px; - opacity: 0.4; + font-size: 160rpx; + margin-bottom: 40rpx; + opacity: 0.35; } .empty-text { - font-size: var(--font-size-lg); + font-size: 36rpx; color: var(--text-tertiary); - font-weight: var(--font-weight-medium); - margin-bottom: 8px; + font-weight: 600; + margin-bottom: 16rpx; } .empty-hint { - font-size: var(--font-size-sm); + font-size: 28rpx; color: var(--text-placeholder); } -/* 筛选栏 */ -.filter-bar { - display: flex; - gap: 12px; - margin-bottom: 20px; - overflow-x: auto; - padding-bottom: 4px; -} +/* ========== 动画 ========== */ -.filter-chip { - padding: 8px 20px; - border-radius: 24px; - font-size: var(--font-size-xs); - background: var(--bg-card); - color: var(--text-secondary); - border: 1px solid var(--border-primary); - white-space: nowrap; - transition: all var(--transition-fast); -} - -.filter-chip.active { - background: var(--primary-gradient); - color: #fff; - border-color: transparent; - box-shadow: 0 4px 12px rgba(90, 147, 223, 0.2); -} - -.filter-chip:active { - transform: scale(0.95); -} - -/* 统计信息 */ -.stats-bar { - display: flex; - justify-content: space-between; - padding: 16px 20px; - background: var(--bg-card); - border-radius: var(--radius-md); - margin-bottom: 20px; - box-shadow: var(--shadow-sm); - border: 1px solid var(--border-light); -} - -.stat-item { - text-align: center; -} - -.stat-value { - font-size: var(--font-size-lg); - font-weight: var(--font-weight-bold); - color: var(--primary-color); - display: block; -} - -.stat-label { - font-size: var(--font-size-xs); - color: var(--text-tertiary); - margin-top: 4px; - display: block; -} - -/* 动画 */ @keyframes fadeIn { - from { - opacity: 0; - } - to { - opacity: 1; - } + from { opacity: 0; } + to { opacity: 1; } } @keyframes fadeInDown { from { opacity: 0; - transform: translateY(-10px); + transform: translateY(-20rpx); } to { opacity: 1; transform: translateY(0); } } + +/* ========== 装饰光晕 ========== */ + +.file-page::before { + content: ''; + position: fixed; + top: -200rpx; + right: -200rpx; + width: 800rpx; + height: 800rpx; + background: radial-gradient(circle, rgba(0, 122, 255, 0.06) 0%, transparent 70%); + border-radius: 50%; + pointer-events: none; + z-index: 0; +} diff --git a/src/pages/file/index.tsx b/src/pages/file/index.tsx index 078ad30..813f67d 100644 --- a/src/pages/file/index.tsx +++ b/src/pages/file/index.tsx @@ -1,6 +1,7 @@ import React, { useState } from "react"; import { View, Text, Button } from "@tarojs/components"; import Taro from '@tarojs/taro'; +import TabBar from '../../components/TabBar/TabBar'; import './index.css'; interface FileItem { @@ -12,6 +13,7 @@ interface FileItem { } const FileIndex = () => { + const [tabIndex, setTabIndex] = useState(3); // 文件是第4个标签(索引3) const [files] = useState([ { id: 1, name: '期末考试复习资料.docx', size: '2.5 MB', time: '2026-03-30 10:30', type: 'word' }, { id: 2, name: '毕业设计.pdf', size: '15.8 MB', time: '2026-03-29 15:20', type: 'pdf' }, @@ -75,6 +77,8 @@ const FileIndex = () => { 点击上方按钮上传文件 )} + + ); }; diff --git a/src/pages/file/upload/index.css b/src/pages/file/upload/index.css index b147638..130f529 100644 --- a/src/pages/file/upload/index.css +++ b/src/pages/file/upload/index.css @@ -1,229 +1,329 @@ /* ============================================ - 文件上传页面样式 - 萝卜打印 + 文件上传页面样式 - 爱印通 Apple Liquid Glass 设计系统 + 大字体大控件版本 - 微信小程序优化 ============================================ */ @import '../../../styles/variables.css'; page { - background: linear-gradient(180deg, #e8f0fe 0%, #f0f5ff 30%, #fafbfc 100%); + background: var(--bg-page-gradient); min-height: 100vh; } .upload-page { min-height: 100vh; - padding: 32px 24px 140px; + padding: 48rpx 32rpx 280rpx; + position: relative; } -/* 页面头部 */ +/* ========== 页面头部 ========== */ + .upload-header { - margin-bottom: 32px; - animation: fadeInDown 0.4s ease forwards; + margin-bottom: 56rpx; + animation: fadeInDown 0.4s cubic-bezier(0.34, 1.56, 0.64, 1) forwards; } .upload-title { - font-size: var(--font-size-2xl); - font-weight: var(--font-weight-bold); + font-size: 56rpx; + font-weight: 700; color: var(--text-primary); display: block; + letter-spacing: -0.3px; } .upload-subtitle { - font-size: var(--font-size-sm); + font-size: 28rpx; color: var(--text-tertiary); - margin-top: 8px; + margin-top: 12rpx; display: block; + font-weight: 400; } -/* 上传区域 */ +/* ========== 上传区域 ========== */ + .upload-area { - background: var(--bg-card); + background: var(--bg-card-solid); border-radius: var(--radius-xl); - padding: 60px 32px; + padding: 96rpx 48rpx; box-shadow: var(--shadow-md); - min-height: 280px; + min-height: 480rpx; display: flex; align-items: center; justify-content: center; - border: 2px dashed var(--border-primary); + border: 4rpx dashed var(--border-primary); transition: all var(--transition-base); position: relative; overflow: hidden; + cursor: pointer; } .upload-area.dragging { - border-color: var(--primary-color); - background: rgba(90, 147, 223, 0.05); - box-shadow: 0 0 0 4px rgba(90, 147, 223, 0.1); + border-color: var(--primary); + background: var(--primary-opacity-10); + box-shadow: 0 0 0 8rpx var(--primary-opacity-20); +} + +.upload-area:active { + transform: scale(0.995); } .upload-placeholder { display: flex; flex-direction: column; align-items: center; - animation: pulse 2s ease infinite; + animation: gentlePulse 3s cubic-bezier(0.4, 0, 0.6, 1) infinite; } -@keyframes pulse { +@keyframes gentlePulse { 0%, 100% { transform: scale(1); + opacity: 1; } 50% { transform: scale(1.02); + opacity: 0.9; } } .upload-icon { - font-size: 100px; - margin-bottom: 20px; - opacity: 0.6; + font-size: 128rpx; + margin-bottom: 32rpx; + opacity: 0.5; } .upload-text { - font-size: var(--font-size-lg); + font-size: 34rpx; color: var(--text-secondary); - font-weight: var(--font-weight-medium); - margin-bottom: 8px; + font-weight: 500; + margin-bottom: 12rpx; } .upload-hint { - font-size: var(--font-size-xs); + font-size: 26rpx; color: var(--text-tertiary); } -/* 文件预览 */ +/* ========== 文件预览 ========== */ + .file-preview { width: 100%; display: flex; align-items: center; - padding: 20px; - background: linear-gradient(135deg, #f8f9fb 0%, #f0f2f7 100%); - border-radius: var(--radius-md); + padding: 32rpx; + background: var(--bg-card-solid); + border-radius: var(--radius-lg); border: 1px solid var(--border-light); - animation: slideIn 0.3s ease forwards; + box-shadow: var(--shadow-md); + animation: springIn 0.4s cubic-bezier(0.34, 1.56, 0.64, 1) forwards; } -@keyframes slideIn { - from { +@keyframes springIn { + 0% { opacity: 0; - transform: translateX(-20px); + transform: scale(0.95) translateY(20rpx); } - to { + 60% { + transform: scale(1.02) translateY(-4rpx); + } + 100% { opacity: 1; - transform: translateX(0); + transform: scale(1) translateY(0); } } .file-icon { - font-size: 48px; - margin-right: 20px; + font-size: 80rpx; + margin-right: 32rpx; flex-shrink: 0; + width: 112rpx; + height: 112rpx; + display: flex; + align-items: center; + justify-content: center; + background: linear-gradient(135deg, #e8f0fe 0%, #d0dcfa 100%); + border-radius: var(--radius-md); + box-shadow: var(--shadow-sm); } .file-info { flex: 1; overflow: hidden; + min-width: 0; } .file-name { - font-size: var(--font-size-base); + font-size: 32rpx; color: var(--text-primary); - font-weight: var(--font-weight-medium); + font-weight: 600; display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; - margin-bottom: 4px; + margin-bottom: 8rpx; + letter-spacing: -0.2px; } .file-size { - font-size: var(--font-size-xs); + font-size: 26rpx; color: var(--text-tertiary); } .change-file { - font-size: var(--font-size-sm); - color: var(--primary-color); - padding: 8px 16px; - background: rgba(90, 147, 223, 0.1); - border-radius: var(--radius-sm); + font-size: 28rpx; + color: var(--primary); + padding: 16rpx 32rpx; + background: var(--primary-opacity-10); + border-radius: var(--radius-md); flex-shrink: 0; transition: all var(--transition-fast); + font-weight: 500; + cursor: pointer; + border: none; } .change-file:active { - background: rgba(90, 147, 223, 0.2); + background: var(--primary-opacity-20); + transform: scale(0.95); } -/* 操作按钮 */ +/* ========== 打印选项 ========== */ + +.print-options { + margin-top: 56rpx; +} + +.option-card { + background: var(--bg-card-solid); + border-radius: var(--radius-lg); + padding: 40rpx; + box-shadow: var(--shadow-sm); + border: 1px solid var(--border-light); + margin-bottom: 32rpx; +} + +.option-title { + font-size: 32rpx; + font-weight: 600; + color: var(--text-primary); + margin-bottom: 32rpx; + display: block; + letter-spacing: -0.2px; +} + +.option-group { + display: flex; + gap: 20rpx; + flex-wrap: wrap; +} + +.option-btn { + flex: 1; + min-width: 160rpx; + height: 128rpx; + background: var(--bg-card); + border: 3rpx solid var(--border-primary); + border-radius: var(--radius-md); + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + font-size: 30rpx; + color: var(--text-secondary); + transition: all var(--transition-base); + cursor: pointer; + font-weight: 500; +} + +.option-btn:active { + transform: scale(0.96); +} + +.option-btn.selected { + background: var(--primary-opacity-10); + border-color: var(--primary); + color: var(--primary); + box-shadow: 0 4rpx 16rpx rgba(0, 122, 255, 0.1); +} + +.option-label { + font-size: 24rpx; + color: var(--text-tertiary); + margin-top: 8rpx; + font-weight: 400; +} + +/* ========== 上传按钮 ========== */ + .file-actions { - margin-top: 32px; + margin-top: 56rpx; } .upload-btn { width: 100%; - height: 96px; - background: var(--primary-gradient); - color: #fff; - font-size: var(--font-size-lg); - font-weight: var(--font-weight-semibold); - border-radius: 48px; + height: 100rpx; + background: var(--primary); + color: var(--text-inverse); + font-size: 34rpx; + font-weight: 600; + border-radius: var(--radius-md); display: flex; align-items: center; justify-content: center; - box-shadow: 0 8px 24px rgba(90, 147, 223, 0.3); - transition: all var(--transition-base); - letter-spacing: 2px; -} - -.upload-btn::after { + box-shadow: 0 8rpx 32rpx rgba(0, 122, 255, 0.25); + transition: all var(--transition-scale); + letter-spacing: 1rpx; border: none; + cursor: pointer; } .upload-btn:active { - transform: scale(0.97); + transform: scale(0.98); } .upload-btn[disabled] { - background: linear-gradient(135deg, #c5cede 0%, #b0bccf 100%); + background: var(--border-primary); + color: var(--text-tertiary); box-shadow: none; } -/* 进度条 */ +/* ========== 进度条 ========== */ + .progress-area { - margin-top: 24px; + margin-top: 48rpx; animation: fadeIn 0.3s ease forwards; } .progress-label { display: flex; justify-content: space-between; - margin-bottom: 8px; + margin-bottom: 16rpx; } .progress-text { - font-size: var(--font-size-xs); + font-size: 26rpx; color: var(--text-secondary); } .progress-percent { - font-size: var(--font-size-xs); - color: var(--primary-color); - font-weight: var(--font-weight-semibold); + font-size: 26rpx; + color: var(--primary); + font-weight: 600; } .progress-bar { - height: 8px; + height: 12rpx; background: var(--border-light); - border-radius: 4px; + border-radius: 6rpx; overflow: hidden; } .progress-fill { height: 100%; background: var(--primary-gradient); - border-radius: 4px; - transition: width 0.3s ease; + border-radius: 6rpx; + transition: width 0.3s cubic-bezier(0.4, 0, 0.2, 1); position: relative; + overflow: hidden; } .progress-fill::after { @@ -233,46 +333,40 @@ page { left: 0; right: 0; bottom: 0; - background: linear-gradient( - 90deg, - transparent 0%, - rgba(255, 255, 255, 0.3) 50%, - transparent 100% - ); + background: linear-gradient(90deg, transparent 0%, rgba(255,255,255,0.4) 50%, transparent 100%); animation: shimmer 1.5s infinite; } @keyframes shimmer { - 0% { - transform: translateX(-100%); - } - 100% { - transform: translateX(100%); - } + 0% { transform: translateX(-100%); } + 100% { transform: translateX(100%); } } -/* 提示区域 */ +/* ========== 提示区域 ========== */ + .tips-section { - background: linear-gradient(135deg, #fefcf3 0%, #fdf9f0 100%); + background: #FFF9E6; border-radius: var(--radius-lg); - padding: 24px; - margin-top: 32px; - border: 1px solid #f5e8c7; + padding: 40rpx; + margin-top: 56rpx; + border: 2rpx solid rgba(255, 214, 102, 0.3); box-shadow: var(--shadow-sm); } .tips-title { - font-size: var(--font-size-lg); - font-weight: var(--font-weight-semibold); + font-size: 32rpx; + font-weight: 600; color: #b08945; display: block; - margin-bottom: 16px; + margin-bottom: 32rpx; + letter-spacing: -0.2px; } .tips-item { display: flex; align-items: flex-start; - margin-bottom: 12px; + margin-bottom: 24rpx; + gap: 20rpx; } .tips-item:last-child { @@ -281,95 +375,47 @@ page { .tips-dot { color: #d4a853; - margin-right: 10px; - font-size: var(--font-size-xs); - line-height: 1.5; + font-size: 26rpx; + line-height: 1.6; + flex-shrink: 0; } .tips-text { - font-size: var(--font-size-sm); + font-size: 28rpx; color: #8a7340; flex: 1; line-height: 1.6; } -/* 打印选项 */ -.print-options { - margin-top: 32px; -} +/* ========== 动画 ========== */ -.option-card { - background: var(--bg-card); - border-radius: var(--radius-lg); - padding: 24px; - box-shadow: var(--shadow-md); - border: 1px solid var(--border-light); - margin-bottom: 16px; -} - -.option-title { - font-size: var(--font-size-base); - font-weight: var(--font-weight-semibold); - color: var(--text-primary); - margin-bottom: 16px; - display: block; -} - -.option-group { - display: flex; - gap: 12px; - flex-wrap: wrap; -} - -.option-btn { - flex: 1; - min-width: 100px; - height: 72px; - background: #f8f9fb; - border: 1px solid var(--border-primary); - border-radius: var(--radius-md); - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - font-size: var(--font-size-sm); - color: var(--text-secondary); - transition: all var(--transition-fast); -} - -.option-btn:active { - transform: scale(0.96); -} - -.option-btn.selected { - background: rgba(90, 147, 223, 0.1); - border-color: var(--primary-color); - color: var(--primary-color); -} - -.option-label { - font-size: var(--font-size-xs); - color: var(--text-tertiary); - margin-top: 4px; -} - -/* 动画 */ @keyframes fadeIn { - from { - opacity: 0; - } - to { - opacity: 1; - } + from { opacity: 0; } + to { opacity: 1; } } @keyframes fadeInDown { from { opacity: 0; - transform: translateY(-10px); + transform: translateY(-20rpx); } to { opacity: 1; transform: translateY(0); } } + +/* ========== 装饰光晕 ========== */ + +.upload-page::before { + content: ''; + position: fixed; + top: -200rpx; + right: -200rpx; + width: 800rpx; + height: 800rpx; + background: radial-gradient(circle, rgba(0, 122, 255, 0.06) 0%, transparent 70%); + border-radius: 50%; + pointer-events: none; + z-index: 0; +} diff --git a/src/pages/index/index.css b/src/pages/index/index.css index daa145e..42db0b3 100644 --- a/src/pages/index/index.css +++ b/src/pages/index/index.css @@ -1,58 +1,70 @@ /* ============================================ - 首页样式 - 爱印通 + 首页样式 - 爱印通 Apple Liquid Glass 设计系统 + 大字体大控件版本 - 微信小程序优化 ============================================ */ @import '../../styles/variables.css'; page { - background: linear-gradient(180deg, #e8f0fe 0%, #f0f5ff 30%, #fafbfc 100%); + background: var(--bg-page-gradient); min-height: 100vh; } .index-page { min-height: 100vh; - padding-bottom: 140px; + /* 增加底部填充,确保内容不被TabBar遮挡 */ + padding-bottom: 320rpx; + position: relative; + /* 创建独立的堆叠上下文,确保毛玻璃效果正确渲染 */ + z-index: 0; + /* 确保背景正确传递,避免毛玻璃效果失效 */ + background: transparent; } -/* 头部区域 */ +/* ========== 头部区域 ========== */ + .header { background: var(--primary-gradient); - padding: 100px 32px 80px; + padding: 192rpx 48rpx 144rpx; position: relative; overflow: hidden; - border-radius: 0 0 40px 40px; + border-radius: 0 0 64rpx 64rpx; } .header::before { content: ''; position: absolute; - top: -30%; - right: -15%; - width: 350px; - height: 350px; - background: rgba(255, 255, 255, 0.12); + top: -20%; + right: -10%; + width: 600rpx; + height: 600rpx; + background: rgba(255, 255, 255, 0.15); border-radius: 50%; - animation: float 6s ease-in-out infinite; + animation: float 6s cubic-bezier(0.4, 0, 0.2, 1) infinite; + filter: blur(80rpx); } .header::after { content: ''; position: absolute; - bottom: -20%; - left: -10%; - width: 250px; - height: 250px; - background: rgba(255, 255, 255, 0.08); + bottom: -15%; + left: -5%; + width: 400rpx; + height: 400rpx; + background: rgba(255, 255, 255, 0.1); border-radius: 50%; - animation: float 8s ease-in-out infinite reverse; + animation: float 8s cubic-bezier(0.4, 0, 0.2, 1) infinite reverse; + filter: blur(60rpx); } @keyframes float { 0%, 100% { transform: translateY(0) scale(1); + opacity: 0.8; } 50% { - transform: translateY(-20px) scale(1.05); + transform: translateY(-30rpx) scale(1.05); + opacity: 1; } } @@ -64,101 +76,140 @@ page { } .app-title { - font-size: 48px; + font-size: 88rpx; font-weight: 700; - color: #fff; - letter-spacing: 4px; - text-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); + color: #7279be; + letter-spacing: 4rpx; + text-shadow: 0 4rpx 24rpx rgba(0, 0, 0, 0.15); + line-height: 1.2; } .app-subtitle { - font-size: 24px; + font-size: 36rpx; color: rgba(255, 255, 255, 0.85); - margin-top: 8px; - letter-spacing: 2px; + margin-top: 16rpx; + letter-spacing: 2rpx; + font-weight: 400; } -/* 登录按钮区域 */ +/* ========== 登录按钮 ========== */ + .login-section { - padding: 0 32px; - margin-top: -30px; + padding: 0 48rpx; + margin-top: -48rpx; position: relative; z-index: 2; - animation: slideUp 0.5s ease forwards; + animation: fadeInUp 0.5s cubic-bezier(0.34, 1.56, 0.64, 1) forwards; } .login-btn { width: 100%; - background: rgba(255, 255, 255, 0.95); - border: 1px solid rgba(90, 147, 223, 0.2); - color: var(--primary-color); - font-size: var(--font-size-lg); - font-weight: var(--font-weight-semibold); - padding: 24px 0; - border-radius: 50px; - box-shadow: var(--shadow-lg); - backdrop-filter: blur(10px); - transition: all var(--transition-base); - letter-spacing: 2px; + background: var(--bg-glass-heavy); + backdrop-filter: blur(48rpx) saturate(180%); + -webkit-backdrop-filter: blur(48rpx) saturate(180%); + /* 增强的多层边框描边 */ + border: 3rpx solid rgba(0, 122, 255, 0.3); + outline: 1rpx solid rgba(255, 255, 255, 0.8); + outline-offset: -4rpx; + color: var(--primary); + font-size: 34rpx; + font-weight: 700; + padding: 40rpx 0; + border-radius: var(--radius-xl); + /* 增强的阴影效果 */ + box-shadow: + 0 8rpx 32rpx rgba(0, 122, 255, 0.15), + 0 2rpx 8rpx rgba(0, 0, 0, 0.06), + inset 0 1rpx 0 rgba(255, 255, 255, 0.9), + inset 0 -1rpx 0 rgba(0, 122, 255, 0.1); + transition: all var(--transition-scale); + letter-spacing: 2rpx; + cursor: pointer; + position: relative; + z-index: 1; } -.login-btn::after { - border: none; +/* 按钮内部光泽效果 */ +.login-btn::before { + content: ''; + position: absolute; + top: 2rpx; + left: 2rpx; + right: 2rpx; + height: 50%; + background: linear-gradient(180deg, rgba(255, 255, 255, 0.4) 0%, rgba(255, 255, 255, 0) 100%); + border-radius: var(--radius-lg); + pointer-events: none; } .login-btn:active { - transform: scale(0.97); + transform: scale(0.98); + background: rgba(255, 255, 255, 0.95); + border-color: rgba(0, 122, 255, 0.5); + box-shadow: + 0 4rpx 16rpx rgba(0, 122, 255, 0.1), + 0 1rpx 4rpx rgba(0, 0, 0, 0.04), + inset 0 1rpx 0 rgba(255, 255, 255, 0.9); } -/* 用户卡片 */ +/* ========== 用户卡片 ========== */ + .user-section { - padding: 0 32px; - margin-top: -30px; + padding: 0 48rpx; + margin-top: -48rpx; position: relative; z-index: 2; - animation: slideUp 0.5s ease forwards; + animation: fadeInUp 0.5s cubic-bezier(0.34, 1.56, 0.64, 1) 0.1s forwards; + opacity: 0; } .user-card { - background: rgba(255, 255, 255, 0.95); + background: var(--bg-glass-heavy); + backdrop-filter: blur(48rpx) saturate(180%); + -webkit-backdrop-filter: blur(48rpx) saturate(180%); border-radius: var(--radius-xl); - padding: 24px; + padding: 40rpx; display: flex; align-items: center; box-shadow: var(--shadow-lg); - border: 1px solid rgba(255, 255, 255, 0.8); - backdrop-filter: blur(20px); + border: 1px solid var(--glass-border); + transition: all var(--transition-base); +} + +.user-card:active { + transform: scale(0.99); } .avatar-wrapper { - margin-right: 20px; + margin-right: 32rpx; position: relative; } .avatar-img { - width: 80px; - height: 80px; + width: 128rpx; + height: 128rpx; border-radius: 50%; - border: 3px solid #fff; - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); + border: 6rpx solid var(--bg-card-solid); + box-shadow: var(--shadow-md); + object-fit: cover; } .avatar-placeholder { - width: 80px; - height: 80px; + width: 128rpx; + height: 128rpx; background: var(--primary-gradient); border-radius: 50%; display: flex; align-items: center; justify-content: center; - border: 3px solid #fff; - box-shadow: 0 4px 12px rgba(90, 147, 223, 0.3); + border: 6rpx solid var(--bg-card-solid); + box-shadow: 0 8rpx 24rpx rgba(0, 122, 255, 0.3); } .avatar-text { - font-size: 36px; - color: #fff; - font-weight: var(--font-weight-bold); + font-size: 56rpx; + color: #ffffff; + font-weight: 700; } .user-detail { @@ -166,191 +217,328 @@ page { } .user-name { - font-size: var(--font-size-xl); + font-size: 36rpx; color: var(--text-primary); - font-weight: var(--font-weight-semibold); + font-weight: 600; display: block; - margin-bottom: 4px; + margin-bottom: 8rpx; + letter-spacing: -0.3px; } .user-balance { - font-size: var(--font-size-sm); - color: var(--text-tertiary); + font-size: 28rpx; + color: var(--text-secondary); + font-weight: 400; } .logout-btn-small { - background: #f5f5f7; - color: var(--text-tertiary); - font-size: var(--font-size-xs); - padding: 10px 20px; - border-radius: 24px; + background: var(--bg-hover); + color: var(--text-secondary); + font-size: 26rpx; + padding: 16rpx 32rpx; + border-radius: var(--radius-full); border: none; transition: all var(--transition-base); + font-weight: 500; } .logout-btn-small:active { - background: #e8e8ea; + background: var(--border-primary); + transform: scale(0.95); } -/* 快捷操作 */ +/* ========== 快捷操作区域 ========== */ + .quick-actions { display: flex; - padding: 32px 24px 0; - gap: 16px; + justify-content: space-around; + padding: 48rpx 32rpx; + gap: 24rpx; + animation: fadeInUp 0.4s cubic-bezier(0.34, 1.56, 0.64, 1) 0.2s forwards; + opacity: 0; + /* 启用GPU加速,确保滚动时毛玻璃效果正常 */ + transform: translateZ(0); + -webkit-transform: translateZ(0); } +/* 快捷操作按钮 - 简化样式,避免与TabBar毛玻璃冲突 */ .action-item { flex: 1; - background: var(--bg-card); + /* 使用实心背景色,避免与TabBar毛玻璃效果叠加 */ + background: var(--bg-card-solid); border-radius: var(--radius-lg); - padding: 28px 12px; + padding: 40rpx 24rpx; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + box-shadow: var(--shadow-md); + border: 2rpx solid var(--border-light); + transition: all var(--transition-base); + cursor: pointer; + min-height: 200rpx; + position: relative; +} + +/* 主操作按钮(上传文件)特殊样式 */ +.action-item.primary { + background: var(--primary-gradient); + box-shadow: 0 8rpx 32rpx rgba(0, 122, 255, 0.3); + border: 2rpx solid rgba(255, 255, 255, 0.3); +} + +.action-item:active { + transform: scale(0.96); +} + +.action-icon { + font-size: 64rpx; + margin-bottom: 16rpx; +} + +.action-text { + font-size: 28rpx; + font-weight: 600; + color: var(--text-primary); + text-align: center; +} + +.action-item.primary .action-text { + color: var(--primary) !important; + text-shadow: 0 1rpx 2rpx rgba(0, 0, 0, 0.1); +} + +.business-card { + background: linear-gradient(135deg, #FFFFFF 0%, #F8FAFC 100%); + border-radius: var(--radius-lg); + padding: 40rpx 28rpx; display: flex; flex-direction: column; align-items: center; box-shadow: var(--shadow-md); - transition: all var(--transition-base); border: 1px solid var(--border-light); + transition: all var(--transition-base); position: relative; overflow: hidden; + animation: fadeInUp 0.4s cubic-bezier(0.34, 1.56, 0.64, 1) forwards; + opacity: 0; + cursor: pointer; } -.action-item::before { +.business-card:nth-child(1) { animation-delay: 0.25s; } +.business-card:nth-child(2) { animation-delay: 0.3s; } +.business-card:nth-child(3) { animation-delay: 0.35s; } +.business-card:nth-child(4) { animation-delay: 0.4s; } + +/* 卡片顶部装饰色条 */ +.business-card::before { content: ''; position: absolute; top: 0; left: 0; right: 0; - height: 3px; - background: var(--primary-gradient); - opacity: 0; - transition: opacity var(--transition-base); + height: 6rpx; + opacity: 0.8; } -.action-item:active { +.business-card.doc::before { + background: linear-gradient(90deg, #007AFF, #5AC8FA); +} + +.business-card.image::before { + background: linear-gradient(90deg, #5856D6, #AF52DE); +} + +.business-card.archive::before { + background: linear-gradient(90deg, #FF9500, #FFCC00); +} + +.business-card.scan::before { + background: linear-gradient(90deg, #34C759, #30D158); +} + +.business-card:active { transform: scale(0.96); box-shadow: var(--shadow-sm); } -.action-item:active::before { - opacity: 1; -} - -.action-item.primary { - background: var(--primary-gradient); - box-shadow: 0 8px 24px rgba(90, 147, 223, 0.3); -} - -.action-item.primary .action-text { - color: #fff; -} - -.action-item.primary .action-icon { - background: rgba(255, 255, 255, 0.25); - color: #fff; -} - -.action-icon { - width: 64px; - height: 64px; - background: linear-gradient(135deg, #f0f5ff 0%, #e8eefa 100%); +.business-icon { + width: 100rpx; + height: 100rpx; border-radius: var(--radius-md); display: flex; align-items: center; justify-content: center; - font-size: 32px; - margin-bottom: 12px; - transition: all var(--transition-base); + font-size: 52rpx; + margin-bottom: 20rpx; + box-shadow: var(--shadow-sm); } -.action-text { - font-size: var(--font-size-base); - color: var(--text-secondary); - font-weight: var(--font-weight-medium); +.business-card.doc .business-icon { + background: linear-gradient(135deg, #E8F0FE 0%, #D0DCFA 100%); } -/* 价目表 */ +.business-card.image .business-icon { + background: linear-gradient(135deg, #EDE8FD 0%, #D8D0F8 100%); +} + +.business-card.archive .business-icon { + background: linear-gradient(135deg, #FFF3E0 0%, #FFE0B2 100%); +} + +.business-card.scan .business-icon { + background: linear-gradient(135deg, #E8F8EC 0%, #C8F5D0 100%); +} + +.business-text { + font-size: 28rpx; + font-weight: 600; + color: var(--text-primary); + text-align: center; +} + +.business-desc { + font-size: 22rpx; + color: var(--text-tertiary); + margin-top: 8rpx; + text-align: center; +} + +/* ========== 价目表 ========== */ + .price-section { - padding: 28px 24px 0; + padding: 48rpx 32rpx 0; + animation: fadeInUp 0.4s cubic-bezier(0.34, 1.56, 0.64, 1) 0.4s forwards; + opacity: 0; } .price-table { - background: var(--bg-card); + background: linear-gradient(180deg, #F8FAFC 0%, #FFFFFF 100%); border-radius: var(--radius-lg); - padding: 24px; + padding: 40rpx; box-shadow: var(--shadow-md); border: 1px solid var(--border-light); + position: relative; + overflow: hidden; +} + +/* 价目表顶部装饰线 */ +.price-table::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + height: 6rpx; + background: var(--primary-gradient); +} + +/* 价目表底部装饰 */ +.price-table::after { + content: ''; + position: absolute; + bottom: 0; + left: 0; + right: 0; + height: 4rpx; + background: linear-gradient(90deg, transparent 0%, rgba(0,122,255,0.06) 50%, transparent 100%); } .price-title { - font-size: var(--font-size-xl); - font-weight: var(--font-weight-semibold); + font-size: 36rpx; + font-weight: 600; color: var(--text-primary); display: block; - margin-bottom: 20px; - padding-left: 12px; - border-left: 4px solid var(--primary-color); + margin-bottom: 32rpx; + padding-left: 24rpx; + border-left: 6rpx solid var(--primary); + letter-spacing: -0.3px; } .price-list { display: flex; flex-direction: column; + gap: 8rpx; } .price-item { display: flex; justify-content: space-between; align-items: center; - padding: 14px 12px; - border-radius: var(--radius-sm); + padding: 24rpx; + border-radius: var(--radius-md); transition: background var(--transition-fast); } .price-item:nth-child(odd) { - background: #fafbfc; + background: rgba(0, 122, 255, 0.04); } .price-item:nth-child(even) { - background: var(--bg-card); + background: rgba(0, 0, 0, 0.01); } .price-name { - font-size: var(--font-size-base); - color: var(--text-secondary); - font-weight: var(--font-weight-medium); + font-size: 30rpx; + color: var(--text-primary); + font-weight: 500; } .price-value { - font-size: var(--font-size-md); - color: var(--error-color); - font-weight: var(--font-weight-semibold); + font-size: 32rpx; + color: var(--primary); + font-weight: 700; } -/* 使用提示 */ +/* ========== 使用提示 ========== */ + .tips-section { - padding: 28px 24px; - margin-top: 12px; + padding: 48rpx 32rpx; + margin-top: 24rpx; + animation: fadeInUp 0.4s cubic-bezier(0.34, 1.56, 0.64, 1) 0.5s forwards; + opacity: 0; } .tips-card { - background: linear-gradient(135deg, #fefcf3 0%, #fdf9f0 100%); + background: linear-gradient(145deg, #FFF9E6 0%, #FFF8E1 50%, #FFFDE7 100%); border-radius: var(--radius-lg); - padding: 24px; - border: 1px solid #f5e8c7; - box-shadow: var(--shadow-sm); + padding: 40rpx; + border: 2rpx solid rgba(255, 214, 102, 0.4); + box-shadow: 0 8rpx 32rpx rgba(255, 214, 102, 0.15); + position: relative; + overflow: hidden; +} + +/* 提示卡片右上角装饰 */ +.tips-card::before { + content: ''; + position: absolute; + top: -20rpx; + right: -20rpx; + width: 80rpx; + height: 80rpx; + background: radial-gradient(circle, rgba(255, 214, 102, 0.15) 0%, transparent 70%); + border-radius: 50%; } .tips-title { - font-size: var(--font-size-lg); - font-weight: var(--font-weight-semibold); - color: #b08945; + font-size: 32rpx; + font-weight: 600; + color: #8B6914; display: block; - margin-bottom: 16px; + margin-bottom: 32rpx; + letter-spacing: -0.3px; + position: relative; + z-index: 1; } .tips-item { display: flex; - align-items: center; - margin-bottom: 12px; + align-items: flex-start; + margin-bottom: 24rpx; + gap: 24rpx; + position: relative; + z-index: 1; } .tips-item:last-child { @@ -358,48 +546,65 @@ page { } .tips-number { - width: 28px; - height: 28px; - background: linear-gradient(135deg, #d4a853 0%, #c49a47 100%); + width: 48rpx; + height: 48rpx; + background: linear-gradient(135deg, #D4A853 0%, #C49A47 100%); border-radius: 50%; display: flex; align-items: center; justify-content: center; - font-size: 18px; - color: #fff; - font-weight: var(--font-weight-medium); - margin-right: 12px; + font-size: 26rpx; + color: #ffffff; + font-weight: 600; flex-shrink: 0; + margin-top: 2rpx; + box-shadow: 0 4rpx 12rpx rgba(196, 154, 71, 0.4); } .tips-text { - font-size: var(--font-size-sm); - color: #8a7340; - line-height: 1.5; + font-size: 28rpx; + color: #6B5210; + line-height: 1.6; + flex: 1; } -/* 动画 */ -@keyframes slideUp { +/* ========== 装饰光晕 - 使用 absolute 确保毛玻璃效果正确 ========== */ + +.index-page::before { + content: ''; + position: absolute; + top: -300rpx; + right: -300rpx; + width: 1000rpx; + height: 1000rpx; + background: radial-gradient(circle, rgba(0, 122, 255, 0.08) 0%, transparent 70%); + border-radius: 50%; + pointer-events: none; + z-index: 0; +} + +.index-page::after { + content: ''; + position: absolute; + bottom: -200rpx; + left: -200rpx; + width: 800rpx; + height: 800rpx; + background: radial-gradient(circle, rgba(90, 200, 250, 0.06) 0%, transparent 70%); + border-radius: 50%; + pointer-events: none; + z-index: 0; +} + +/* ========== 进入动画 ========== */ + +@keyframes fadeInUp { from { opacity: 0; - transform: translateY(20px); + transform: translateY(40rpx); } to { opacity: 1; transform: translateY(0); } } - -/* 装饰光晕 */ -.index-page::before { - content: ''; - position: fixed; - top: -100px; - right: -100px; - width: 400px; - height: 400px; - background: radial-gradient(circle, rgba(125, 179, 245, 0.1) 0%, transparent 70%); - border-radius: 50%; - pointer-events: none; - z-index: 0; -} \ No newline at end of file diff --git a/src/pages/index/index.tsx b/src/pages/index/index.tsx index 291d854..f7ba09f 100644 --- a/src/pages/index/index.tsx +++ b/src/pages/index/index.tsx @@ -1,6 +1,7 @@ -import React, { useState, useEffect, useCallback, useMemo } from "react"; +import React, { useState, useEffect, useCallback } from "react"; import { View, Text, Button, Image } from "@tarojs/components"; import Taro from '@tarojs/taro'; +import TabBar from '../../components/TabBar/TabBar'; import './index.css'; interface UserInfo { @@ -9,20 +10,102 @@ interface UserInfo { balance: number; } +interface PriceItem { + name: string; + price: string; +} + +interface TipItem { + text: string; +} + +// 常量数据移到组件外部,避免重复创建 +const PRICE_LIST: PriceItem[] = [ + { name: 'A4黑白打印', price: '¥一万谷地券/页' }, + { name: 'A4彩色打印', price: '¥一万武陵券/页' }, + { name: 'A3黑白打印', price: '¥一万汤汤券/页' }, + { name: 'A3彩色打印', price: '¥一万打湿的汤汤券/页' }, + { name: '照片打印', price: '¥648/张' }, +]; + +const TIPS_LIST: TipItem[] = [ + { text: '支持 Word、PDF、图片等格式' }, + { text: '请选择合适的打印规格' }, + { text: '打印完成后请及时取走文件' }, +]; + +// 提取公共导航逻辑(异步版本) +const navigateWithAuth = async (targetUrl: string, loginUrl = '/pages/login/index') => { + try { + const { data: storedUser } = await Taro.getStorage({ key: 'userInfo' }); + if (!storedUser) { + Taro.navigateTo({ url: loginUrl }); + return false; + } + Taro.navigateTo({ url: targetUrl }); + return true; + } catch (error) { + console.error('检查登录状态失败:', error); + Taro.navigateTo({ url: loginUrl }); + return false; + } +}; + +// 用户卡片组件 +const UserCard: React.FC<{ userInfo: UserInfo; onLogout: () => void }> = ({ userInfo, onLogout }) => { + const userInitials = userInfo.nickname[0]?.toUpperCase() || 'U'; + + return ( + + + + {userInfo.avatar ? ( + + ) : ( + + {userInitials} + + )} + + + {userInfo.nickname} + 余额: ¥{userInfo.balance.toFixed(2)} + + + + + ); +}; + +// 登录区域组件 +const LoginSection: React.FC<{ onLogin: () => void }> = ({ onLogin }) => ( + + + +); + const Index = () => { const [isLoggedIn, setIsLoggedIn] = useState(false); const [userInfo, setUserInfo] = useState(null); + const [tabIndex, setTabIndex] = useState(0); useEffect(() => { checkLoginStatus(); }, []); - const checkLoginStatus = useCallback(() => { - const storedUser = Taro.getStorageSync('userInfo'); - if (storedUser) { - setIsLoggedIn(true); - setUserInfo(storedUser); - } else { + // 使用异步存储操作 + const checkLoginStatus = useCallback(async () => { + try { + const { data: storedUser } = await Taro.getStorage({ key: 'userInfo' }); + if (storedUser) { + setIsLoggedIn(true); + setUserInfo(storedUser); + } else { + setIsLoggedIn(false); + setUserInfo(null); + } + } catch (error) { + console.error('获取用户信息失败:', error); setIsLoggedIn(false); setUserInfo(null); } @@ -32,29 +115,18 @@ const Index = () => { Taro.navigateTo({ url: '/pages/login/index' }); }, []); + // 使用公共导航函数 const handleUpload = useCallback(() => { - if (!isLoggedIn) { - Taro.navigateTo({ url: '/pages/login/index' }); - return; - } - Taro.navigateTo({ url: '/pages/file/upload/index' }); - }, [isLoggedIn]); + navigateWithAuth('/pages/file/upload/index'); + }, []); const handleMyFiles = useCallback(() => { - if (!isLoggedIn) { - Taro.navigateTo({ url: '/pages/login/index' }); - return; - } - Taro.navigateTo({ url: '/pages/file/index' }); - }, [isLoggedIn]); + navigateWithAuth('/pages/file/index'); + }, []); const handleMyOrders = useCallback(() => { - if (!isLoggedIn) { - Taro.navigateTo({ url: '/pages/login/index' }); - return; - } - Taro.navigateTo({ url: '/pages/order/index' }); - }, [isLoggedIn]); + navigateWithAuth('/pages/order/index'); + }, []); const handleLogout = useCallback(() => { Taro.showModal({ @@ -72,25 +144,6 @@ const Index = () => { }); }, []); - const priceList = useMemo(() => [ - { name: 'A4黑白打印', price: '¥0.10/页' }, - { name: 'A4彩色打印', price: '¥0.30/页' }, - { name: 'A3黑白打印', price: '¥0.20/页' }, - { name: 'A3彩色打印', price: '¥0.60/页' }, - { name: '照片打印', price: '¥2.00/张' }, - ], []); - - const tipsList = useMemo(() => [ - '支持 Word、PDF、图片等格式', - '请选择合适的打印规格', - '打印完成后请及时取走文件', - ], []); - - const userInitials = useMemo(() => { - if (!userInfo?.nickname) return 'U'; - return userInfo.nickname[0]?.toUpperCase() || 'U'; - }, [userInfo?.nickname]); - return ( @@ -101,28 +154,9 @@ const Index = () => { {isLoggedIn && userInfo ? ( - - - - {userInfo.avatar ? ( - - ) : ( - - {userInitials} - - )} - - - {userInfo.nickname} - 余额: ¥{userInfo.balance.toFixed(2)} - - - - + ) : ( - - - + )} @@ -144,7 +178,7 @@ const Index = () => { 价目表 - {priceList.map((item, index) => ( + {PRICE_LIST.map((item, index) => ( {item.name} {item.price} @@ -157,14 +191,16 @@ const Index = () => { 使用提示 - {tipsList.map((tip, index) => ( + {TIPS_LIST.map((tip, index) => ( {index + 1} - {tip} + {tip.text} ))} + + ); }; diff --git a/src/pages/login/index.css b/src/pages/login/index.css index f7ac10c..03ec5fa 100644 --- a/src/pages/login/index.css +++ b/src/pages/login/index.css @@ -1,97 +1,140 @@ /* ============================================ - 登录页面样式 - 爱印通 + 登录页面样式 - 爱印通 Apple Liquid Glass 设计系统 + 大字体大控件版本 - 微信小程序优化 ============================================ */ @import '../../styles/variables.css'; page { - background: linear-gradient(180deg, #e8f0fe 0%, #f0f5ff 30%, #fafbfc 100%); + background: var(--bg-page-gradient); min-height: 100vh; } .login-page { min-height: 100vh; - padding: 100px 32px 60px; + padding: 160rpx 48rpx 96rpx; display: flex; flex-direction: column; + position: relative; } -/* 头部区域 */ +/* ========== 头部区域 ========== */ + .login-header { text-align: center; - margin-bottom: 48px; - animation: fadeInDown 0.6s ease forwards; + margin-bottom: 80rpx; + animation: fadeInDown 0.6s cubic-bezier(0.34, 1.56, 0.64, 1) forwards; } .login-logo { - width: 80px; - height: 80px; + width: 144rpx; + height: 144rpx; background: var(--primary-gradient); - border-radius: 24px; + border-radius: var(--radius-xl); display: flex; align-items: center; justify-content: center; - margin: 0 auto 20px; - box-shadow: 0 8px 24px rgba(90, 147, 223, 0.3); - font-size: 40px; + margin: 0 auto 40rpx; + box-shadow: 0 16rpx 48rpx rgba(0, 122, 255, 0.25); + font-size: 72rpx; + position: relative; + overflow: hidden; +} + +.login-logo::before { + content: ''; + position: absolute; + top: 0; + left: -100%; + width: 100%; + height: 100%; + background: linear-gradient(90deg, transparent 0%, rgba(255,255,255,0.3) 50%, transparent 100%); + animation: logoShimmer 3s ease-in-out infinite; +} + +@keyframes logoShimmer { + 0%, 100% { left: -100%; } + 50% { left: 100%; } } .login-title { - font-size: 40px; + font-size: 64rpx; font-weight: 700; color: var(--text-primary); - letter-spacing: 2px; + letter-spacing: -0.5px; display: block; - margin-bottom: 8px; + margin-bottom: 16rpx; + line-height: 1.2; } .login-subtitle { - font-size: 24px; + font-size: 30rpx; color: var(--text-tertiary); display: block; + font-weight: 400; } -/* 表单卡片 */ +/* ========== 表单卡片 ========== */ + .login-form { - background: rgba(255, 255, 255, 0.95); - border-radius: var(--radius-xl); - padding: 36px 28px; - box-shadow: var(--shadow-lg); - border: 1px solid rgba(255, 255, 255, 0.8); - backdrop-filter: blur(20px); - animation: slideUp 0.6s ease 0.2s forwards; + background: var(--bg-glass-heavy); + backdrop-filter: blur(48rpx) saturate(180%); + -webkit-backdrop-filter: blur(48rpx) saturate(180%); + border-radius: var(--radius-2xl); + padding: 64rpx 48rpx; + box-shadow: var(--shadow-xl); + border: 1px solid var(--glass-border); + animation: springUp 0.6s cubic-bezier(0.34, 1.56, 0.64, 1) 0.15s forwards; opacity: 0; } +@keyframes springUp { + 0% { + opacity: 0; + transform: translateY(60rpx) scale(0.95); + } + 50% { + transform: translateY(-10rpx) scale(1.01); + } + 100% { + opacity: 1; + transform: translateY(0) scale(1); + } +} + .form-item { - margin-bottom: 24px; + margin-bottom: 40rpx; } .form-label { - font-size: var(--font-size-base); + font-size: 28rpx; color: var(--text-secondary); - font-weight: var(--font-weight-medium); + font-weight: 500; display: block; - margin-bottom: 12px; - padding-left: 4px; + margin-bottom: 16rpx; + padding-left: 8rpx; + letter-spacing: 0.3px; } .form-input { width: 100%; - height: 88px; - background: #f8f9fb; + height: 96rpx; + background: var(--glass-background); + backdrop-filter: blur(32rpx); + -webkit-backdrop-filter: blur(32rpx); border-radius: var(--radius-md); - padding: 0 24px; - font-size: var(--font-size-base); + padding: 0 32rpx; + font-size: 32rpx; color: var(--text-primary); - border: 1px solid var(--border-primary); + border: 2rpx solid var(--border-primary); transition: all var(--transition-base); + outline: none; } .form-input:focus { - border-color: var(--primary-color); - background: #fff; - box-shadow: 0 0 0 4px rgba(90, 147, 223, 0.1); + border-color: var(--primary); + background: var(--bg-card-solid); + box-shadow: 0 0 0 6rpx var(--primary-opacity-20); } .form-input::placeholder { @@ -99,17 +142,19 @@ page { } .form-error { - font-size: var(--font-size-xs); - color: var(--error-color); - margin-top: 8px; + font-size: 26rpx; + color: var(--error); + margin-top: 12rpx; display: block; - padding-left: 4px; + padding-left: 8rpx; + font-weight: 400; } -/* 验证码输入区域 */ +/* ========== 验证码输入 ========== */ + .code-input-wrapper { display: flex; - gap: 16px; + gap: 24rpx; } .code-input { @@ -117,212 +162,337 @@ page { } .code-btn { - width: 140px; - height: 88px; - background: var(--primary-gradient); - color: #fff; - font-size: var(--font-size-sm); - font-weight: var(--font-weight-medium); + width: 220rpx; + height: 96rpx; + background: var(--primary); + color: var(--text-inverse); + font-size: 28rpx; + font-weight: 500; border-radius: var(--radius-md); display: flex; align-items: center; justify-content: center; flex-shrink: 0; - transition: all var(--transition-base); + transition: all var(--transition-scale); white-space: nowrap; -} - -.code-btn::after { border: none; + cursor: pointer; + box-shadow: 0 4rpx 16rpx rgba(0, 122, 255, 0.2); } .code-btn:active { - transform: scale(0.96); + transform: scale(0.98); + opacity: 0.9; } .code-btn[disabled], .code-btn.disabled { - background: linear-gradient(135deg, #c5cede 0%, #b0bccf 100%); - color: rgba(255, 255, 255, 0.7); + background: var(--border-primary); + color: var(--text-tertiary); + box-shadow: none; } -/* 登录按钮 */ +/* ========== 登录按钮 ========== */ + .login-btn { width: 100%; - height: 96px; - background: var(--primary-gradient); - color: #fff; - font-size: var(--font-size-lg); - font-weight: var(--font-weight-semibold); - border-radius: 48px; + height: 88rpx; + background: var(--primary); + color: var(--text-inverse); + font-size: 32rpx; + font-weight: 700; + border-radius: var(--radius-md); display: flex; align-items: center; justify-content: center; - margin-top: 32px; - box-shadow: 0 8px 24px rgba(90, 147, 223, 0.3); - transition: all var(--transition-base); - letter-spacing: 2px; + margin-top: 48rpx; + /* 增强的多层边框描边 */ + border: 3rpx solid rgba(0, 122, 255, 0.5); + outline: 1rpx solid rgba(255, 255, 255, 0.3); + outline-offset: -3rpx; + /* 增强的阴影效果 */ + box-shadow: + 0 8rpx 32rpx rgba(0, 122, 255, 0.3), + 0 2rpx 8rpx rgba(0, 0, 0, 0.1), + inset 0 1rpx 0 rgba(255, 255, 255, 0.3), + inset 0 -1rpx 0 rgba(0, 0, 0, 0.1); + transition: all var(--transition-scale); + letter-spacing: 1rpx; + cursor: pointer; + position: relative; + overflow: hidden; } -.login-btn::after { - border: none; +/* 按钮内部光泽效果 */ +.login-btn::before { + content: ''; + position: absolute; + top: 0; + left: -100%; + width: 100%; + height: 100%; + background: linear-gradient(90deg, transparent 0%, rgba(255, 255, 255, 0.2) 50%, transparent 100%); + transition: left 0.5s ease; +} + +.login-btn:active::before { + left: 100%; } .login-btn:active { - transform: scale(0.97); - box-shadow: 0 4px 12px rgba(90, 147, 223, 0.2); + transform: scale(0.98); + border-color: rgba(0, 122, 255, 0.7); + box-shadow: + 0 4rpx 16rpx rgba(0, 122, 255, 0.2), + 0 1rpx 4rpx rgba(0, 0, 0, 0.08), + inset 0 1rpx 0 rgba(255, 255, 255, 0.2); } .login-btn[disabled], .login-btn.disabled { - background: linear-gradient(135deg, #c5cede 0%, #b0bccf 100%); + background: var(--border-primary); + color: var(--text-tertiary); + border-color: rgba(0, 0, 0, 0.1); box-shadow: none; + outline: none; } -/* 分割线 */ +/* ========== 用户协议复选框 ========== */ + +.agreement-section { + display: flex; + align-items: flex-start; + margin-top: 40rpx; + padding: 24rpx; + background: rgba(0, 122, 255, 0.03); + border-radius: var(--radius-md); + border: 1px solid rgba(0, 122, 255, 0.08); +} + +.agreement-checkbox { + display: flex; + align-items: center; + margin-right: 16rpx; + flex-shrink: 0; +} + +.checkbox-input { + width: 40rpx; + height: 40rpx; + border: 3rpx solid var(--border-primary); + border-radius: 8rpx; + display: flex; + align-items: center; + justify-content: center; + transition: all var(--transition-base); + background: #fff; +} + +.checkbox-input.checked { + background: var(--primary); + border-color: var(--primary); +} + +.checkbox-icon { + width: 24rpx; + height: 24rpx; + color: #fff; + display: none; +} + +.checkbox-input.checked .checkbox-icon { + display: flex; + align-items: center; + justify-content: center; +} + +.agreement-text { + font-size: 24rpx; + color: var(--text-secondary); + line-height: 1.5; +} + +.agreement-text .link { + color: var(--primary); + font-weight: 500; + text-decoration: none; +} + +/* ========== 分割线 ========== */ + .divider { display: flex; align-items: center; - margin: 36px 0; + margin: 48rpx 0; } .divider-line { flex: 1; - height: 1px; - background: var(--border-primary); + height: 2rpx; + background: var(--border-separator); } .divider-text { - padding: 0 20px; - font-size: var(--font-size-xs); + padding: 0 32rpx; + font-size: 26rpx; color: var(--text-tertiary); + white-space: nowrap; } -/* 微信登录按钮 */ +/* ========== 微信登录按钮 ========== */ + .wechat-btn { width: 100%; - height: 96px; - background: linear-gradient(135deg, #07c160 0%, #06ad56 100%); - color: #fff; - font-size: var(--font-size-lg); - font-weight: var(--font-weight-medium); - border-radius: 48px; + height: 88rpx; + background: #07C160; + color: #ffffff; + font-size: 32rpx; + font-weight: 600; + border-radius: var(--radius-md); display: flex; align-items: center; justify-content: center; - box-shadow: 0 8px 24px rgba(7, 193, 96, 0.3); - transition: all var(--transition-base); - letter-spacing: 2px; -} - -.wechat-btn::after { + box-shadow: 0 8rpx 32rpx rgba(7, 193, 96, 0.25); + transition: all var(--transition-scale); + letter-spacing: 1rpx; border: none; + cursor: pointer; } .wechat-btn:active { - transform: scale(0.97); + transform: scale(0.98); + opacity: 0.95; } -/* 底部链接 */ +/* ========== 底部链接 ========== */ + .login-footer { display: flex; justify-content: center; align-items: center; - margin-top: 40px; + margin-top: 64rpx; flex-wrap: wrap; - gap: 4px; + gap: 8rpx; } .footer-text { - font-size: var(--font-size-xs); + font-size: 28rpx; color: var(--text-tertiary); + font-weight: 400; } .link-text { - font-size: var(--font-size-xs); - color: var(--primary-color); - font-weight: var(--font-weight-medium); + font-size: 28rpx; + color: var(--primary); + font-weight: 500; + text-decoration: none; + transition: opacity var(--transition-fast); } -/* 昵称弹窗 */ +.link-text:active { + opacity: 0.7; +} + +/* ========== 昵称弹窗 ========== */ + .nickname-modal { - background: rgba(255, 255, 255, 0.98); - border-radius: var(--radius-xl); - padding: 48px 32px; - margin-top: 100px; + background: var(--bg-glass-heavy); + backdrop-filter: blur(64rpx) saturate(180%); + -webkit-backdrop-filter: blur(64rpx) saturate(180%); + border-radius: var(--radius-2xl); + padding: 72rpx 56rpx; + margin-top: 160rpx; box-shadow: var(--shadow-xl); - animation: modalIn 0.3s ease forwards; + border: 1px solid var(--glass-border); + animation: modalSpringIn 0.4s cubic-bezier(0.34, 1.56, 0.64, 1) forwards; } -@keyframes modalIn { - from { +@keyframes modalSpringIn { + 0% { opacity: 0; - transform: scale(0.9) translateY(20px); + transform: scale(0.9) translateY(40rpx); } - to { + 60% { + transform: scale(1.02) translateY(-10rpx); + } + 100% { opacity: 1; transform: scale(1) translateY(0); } } .modal-title { - font-size: var(--font-size-2xl); - font-weight: var(--font-weight-bold); + font-size: 44rpx; + font-weight: 700; color: var(--text-primary); display: block; text-align: center; - margin-bottom: 12px; + margin-bottom: 16rpx; + letter-spacing: -0.3px; } .modal-subtitle { - font-size: var(--font-size-base); + font-size: 30rpx; color: var(--text-tertiary); display: block; text-align: center; - margin-bottom: 32px; + margin-bottom: 56rpx; + font-weight: 400; } .nickname-input { width: 100%; - height: 88px; - background: #f8f9fb; + height: 96rpx; + background: var(--glass-background); + backdrop-filter: blur(32rpx); + -webkit-backdrop-filter: blur(32rpx); border-radius: var(--radius-md); - padding: 0 24px; - font-size: var(--font-size-base); + padding: 0 32rpx; + font-size: 32rpx; color: var(--text-primary); - border: 1px solid var(--border-primary); - margin-bottom: 24px; + border: 2rpx solid var(--border-primary); + margin-bottom: 40rpx; + transition: all var(--transition-base); + outline: none; } .nickname-input:focus { - border-color: var(--primary-color); - background: #fff; - box-shadow: 0 0 0 4px rgba(90, 147, 223, 0.1); + border-color: var(--primary); + background: var(--bg-card-solid); + box-shadow: 0 0 0 6rpx var(--primary-opacity-20); +} + +.nickname-input::placeholder { + color: var(--text-placeholder); } .submit-nickname-btn { width: 100%; - height: 96px; - background: var(--primary-gradient); - color: #fff; - font-size: var(--font-size-lg); - font-weight: var(--font-weight-semibold); - border-radius: 48px; + height: 88rpx; + background: var(--primary); + color: var(--text-inverse); + font-size: 32rpx; + font-weight: 600; + border-radius: var(--radius-md); display: flex; align-items: center; justify-content: center; - box-shadow: 0 8px 24px rgba(90, 147, 223, 0.3); -} - -.submit-nickname-btn::after { + box-shadow: 0 8rpx 32rpx rgba(0, 122, 255, 0.25); border: none; + cursor: pointer; + transition: all var(--transition-scale); } -/* 动画 */ +.submit-nickname-btn:active { + transform: scale(0.98); +} + +/* ========== 动画 ========== */ + @keyframes fadeInDown { from { opacity: 0; - transform: translateY(-20px); + transform: translateY(-32rpx); } to { opacity: 1; @@ -330,38 +500,30 @@ page { } } -@keyframes slideUp { - from { - opacity: 0; - transform: translateY(30px); - } - to { - opacity: 1; - transform: translateY(0); - } -} +/* ========== 装饰光晕 ========== */ -/* 装饰元素 */ .login-page::before { content: ''; position: fixed; - top: -100px; - right: -100px; - width: 300px; - height: 300px; - background: radial-gradient(circle, rgba(125, 179, 245, 0.15) 0%, transparent 70%); + top: -300rpx; + right: -300rpx; + width: 800rpx; + height: 800rpx; + background: radial-gradient(circle, rgba(0, 122, 255, 0.08) 0%, transparent 70%); border-radius: 50%; pointer-events: none; + z-index: 0; } .login-page::after { content: ''; position: fixed; - bottom: -50px; - left: -50px; - width: 250px; - height: 250px; - background: radial-gradient(circle, rgba(125, 179, 245, 0.1) 0%, transparent 70%); + bottom: -200rpx; + left: -200rpx; + width: 700rpx; + height: 700rpx; + background: radial-gradient(circle, rgba(90, 200, 250, 0.06) 0%, transparent 70%); border-radius: 50%; pointer-events: none; -} \ No newline at end of file + z-index: 0; +} diff --git a/src/pages/order/index.css b/src/pages/order/index.css index 337e7e4..4af88cc 100644 --- a/src/pages/order/index.css +++ b/src/pages/order/index.css @@ -1,51 +1,66 @@ /* ============================================ - 订单列表页面样式 - 爱印通 + 订单列表页面样式 - 爱印通 Apple Liquid Glass 设计系统 + 大字体大控件版本 - 微信小程序优化 ============================================ */ @import '../../styles/variables.css'; page { - background: linear-gradient(180deg, #e8f0fe 0%, #f0f5ff 30%, #fafbfc 100%); + background: var(--bg-page-gradient); min-height: 100vh; } .order-page { min-height: 100vh; - padding: 32px 24px 140px; + padding: 48rpx 32rpx 280rpx; + position: relative; + /* 创建独立的堆叠上下文,确保毛玻璃效果正确渲染 */ + z-index: 0; + background: transparent; } -/* 页面头部 */ +/* ========== 页面头部 ========== */ + .order-header { - margin-bottom: 24px; - animation: fadeInDown 0.4s ease forwards; + margin-bottom: 40rpx; + animation: fadeInDown 0.4s cubic-bezier(0.34, 1.56, 0.64, 1) forwards; } .header-title { - font-size: var(--font-size-2xl); - font-weight: var(--font-weight-bold); + font-size: 56rpx; + font-weight: 700; color: var(--text-primary); + letter-spacing: -0.3px; } -/* 订单状态筛选 */ +/* ========== 状态筛选 ========== */ + .status-tabs { display: flex; - gap: 8px; - margin-bottom: 24px; + gap: 16rpx; + margin-bottom: 48rpx; overflow-x: auto; - padding-bottom: 4px; + padding-bottom: 8rpx; -webkit-overflow-scrolling: touch; + scrollbar-width: none; +} + +.status-tabs::-webkit-scrollbar { + display: none; } .status-tab { - padding: 10px 20px; - border-radius: 24px; - font-size: var(--font-size-sm); + padding: 16rpx 36rpx; + border-radius: var(--radius-full); + font-size: 28rpx; background: var(--bg-card); color: var(--text-secondary); - border: 1px solid var(--border-primary); + border: 2rpx solid var(--border-primary); white-space: nowrap; - transition: all var(--transition-fast); + transition: all var(--transition-base); flex-shrink: 0; + cursor: pointer; + font-weight: 500; } .status-tab:active { @@ -53,48 +68,78 @@ page { } .status-tab.active { - background: var(--primary-gradient); - color: #fff; - border-color: transparent; - box-shadow: 0 4px 12px rgba(90, 147, 223, 0.2); + background: var(--primary); + color: var(--text-inverse); + border-color: var(--primary); + box-shadow: 0 4rpx 16rpx rgba(0, 122, 255, 0.25); } -/* 订单列表 */ +/* ========== 订单列表 ========== */ + .order-list { display: flex; flex-direction: column; - gap: 16px; + gap: 32rpx; } +/* ========== 订单卡片 - 明显的分界设计 ========== */ + .order-item { - background: var(--bg-card); - border-radius: var(--radius-lg); - padding: 20px; - box-shadow: var(--shadow-md); - border: 1px solid var(--border-light); + /* 确保有足够的背景色让毛玻璃效果正确渲染 */ + background: var(--bg-glass); + backdrop-filter: blur(48rpx) saturate(180%); + -webkit-backdrop-filter: blur(48rpx) saturate(180%); + border-radius: var(--radius-xl); + padding: 0; + box-shadow: + 0 8rpx 32rpx rgba(0, 0, 0, 0.08), + 0 2rpx 8rpx rgba(0, 0, 0, 0.04), + inset 0 1rpx 0 rgba(255, 255, 255, 0.6); + border: 2rpx solid var(--glass-border); transition: all var(--transition-base); - animation: slideUp 0.4s ease forwards; + animation: fadeInUp 0.4s cubic-bezier(0.34, 1.56, 0.64, 1) forwards; opacity: 0; + cursor: pointer; + overflow: hidden; + /* 创建独立的堆叠上下文,确保毛玻璃效果正确渲染 */ + z-index: 1; } -.order-item:nth-child(1) { animation-delay: 0.1s; } -.order-item:nth-child(2) { animation-delay: 0.15s; } -.order-item:nth-child(3) { animation-delay: 0.2s; } +/* 订单间的分隔线效果 */ +.order-item::after { + content: ''; + position: absolute; + bottom: -16rpx; + left: 50%; + transform: translateX(-50%); + width: 60%; + height: 2rpx; + background: linear-gradient( + 90deg, + transparent 0%, + var(--border-separator) 20%, + var(--border-separator) 80%, + transparent 100% + ); + opacity: 0.5; +} + +.order-item:last-child::after { + display: none; +} + +.order-item:nth-child(1) { animation-delay: 0.05s; } +.order-item:nth-child(2) { animation-delay: 0.1s; } +.order-item:nth-child(3) { animation-delay: 0.15s; } +.order-item:nth-child(4) { animation-delay: 0.2s; } +.order-item:nth-child(5) { animation-delay: 0.25s; } .order-item:active { transform: scale(0.98); - box-shadow: var(--shadow-sm); -} - -@keyframes slideUp { - from { - opacity: 0; - transform: translateY(20px); - } - to { - opacity: 1; - transform: translateY(0); - } + box-shadow: + 0 4rpx 16rpx rgba(0, 0, 0, 0.06), + 0 1rpx 4rpx rgba(0, 0, 0, 0.03), + inset 0 1rpx 0 rgba(255, 255, 255, 0.6); } /* 订单顶部 */ @@ -102,72 +147,201 @@ page { display: flex; justify-content: space-between; align-items: center; - margin-bottom: 16px; - padding-bottom: 16px; - border-bottom: 1px solid var(--border-light); + padding: 28rpx 32rpx; + background: linear-gradient( + 135deg, + rgba(255, 255, 255, 0.9) 0%, + rgba(255, 255, 255, 0.7) 100% + ); + border-bottom: 1rpx solid var(--border-separator); } .order-id { - font-size: var(--font-size-xs); + font-size: 26rpx; color: var(--text-tertiary); + font-weight: 500; + font-family: 'SF Mono', monospace; + letter-spacing: 0.5px; } .order-status { - font-size: var(--font-size-sm); - font-weight: var(--font-weight-medium); - padding: 4px 12px; - border-radius: 12px; + font-size: 24rpx; + font-weight: 600; + padding: 8rpx 20rpx; + border-radius: var(--radius-full); + background: rgba(82, 196, 26, 0.1); } .order-status.pending { - background: rgba(250, 173, 20, 0.1); - color: var(--warning-color); + background: rgba(255, 149, 0, 0.1); + color: var(--warning); } .order-status.processing { - background: rgba(24, 144, 255, 0.1); - color: var(--info-color); + background: var(--primary-opacity-10); + color: var(--primary); } .order-status.completed { - background: rgba(82, 196, 26, 0.1); - color: var(--success-color); + background: rgba(52, 199, 89, 0.1); + color: var(--success); } .order-status.cancelled { - background: rgba(255, 77, 79, 0.1); - color: var(--error-color); + background: rgba(255, 59, 48, 0.1); + color: var(--error); } -/* 订单中部 */ -.order-middle { - margin-bottom: 16px; +/* ========== 订单主体 - 左右分栏布局 ========== */ + +.order-main { + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: stretch; + padding: 32rpx; + gap: 24rpx; + min-height: 180rpx; +} + +/* 左侧信息区域 */ +.order-info { + flex: 1; + display: flex; + flex-direction: column; + justify-content: center; + gap: 12rpx; + min-width: 0; +} + +.file-info { + display: flex; + align-items: center; + gap: 16rpx; + margin-bottom: 4rpx; +} + +.file-icon { + font-size: 40rpx; + flex-shrink: 0; } .file-name { - font-size: var(--font-size-md); + font-size: 32rpx; + font-weight: 600; color: var(--text-primary); - font-weight: var(--font-weight-semibold); - display: block; - margin-bottom: 8px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; + letter-spacing: -0.2px; } .order-detail { - font-size: var(--font-size-sm); + font-size: 26rpx; color: var(--text-secondary); - display: block; - margin-bottom: 6px; + font-weight: 500; + display: flex; + align-items: center; + gap: 8rpx; +} + +.order-detail::before { + content: '📄'; + font-size: 24rpx; } .printer-name { - font-size: var(--font-size-xs); + font-size: 24rpx; color: var(--text-tertiary); display: flex; align-items: center; - gap: 4px; + gap: 8rpx; + font-weight: 400; +} + +/* ========== 右侧封面预览区域 ========== */ + +.order-cover { + width: 140rpx; + height: 180rpx; + flex-shrink: 0; + border-radius: var(--radius-md); + overflow: hidden; + background: linear-gradient( + 135deg, + rgba(0, 122, 255, 0.05) 0%, + rgba(0, 122, 255, 0.1) 100% + ); + border: 2rpx solid var(--border-light); + box-shadow: + inset 0 2rpx 4rpx rgba(0, 0, 0, 0.05), + 0 2rpx 8rpx rgba(0, 0, 0, 0.05); + position: relative; +} + +.cover-image { + width: 100%; + height: 100%; + object-fit: cover; + border-radius: var(--radius-md); +} + +.cover-placeholder { + width: 100%; + height: 100%; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 8rpx; + background: linear-gradient( + 135deg, + rgba(255, 255, 255, 0.8) 0%, + rgba(240, 240, 240, 0.6) 100% + ); +} + +.cover-icon { + font-size: 48rpx; + opacity: 0.7; +} + +.cover-text { + font-size: 20rpx; + color: var(--text-tertiary); + font-weight: 500; +} + +/* 封面预览悬停效果 */ +.order-cover::before { + content: '👁️'; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%) scale(0); + font-size: 32rpx; + opacity: 0; + transition: all 0.3s ease; + z-index: 2; +} + +.order-cover::after { + content: ''; + position: absolute; + inset: 0; + background: rgba(0, 0, 0, 0.3); + opacity: 0; + transition: opacity 0.3s ease; + border-radius: var(--radius-md); +} + +.order-item:active .order-cover::before { + transform: translate(-50%, -50%) scale(1); + opacity: 1; +} + +.order-item:active .order-cover::after { + opacity: 1; } /* 订单底部 */ @@ -175,69 +349,61 @@ page { display: flex; justify-content: space-between; align-items: center; - padding-top: 16px; - border-top: 1px solid var(--border-light); + padding: 24rpx 32rpx; + background: linear-gradient( + 135deg, + rgba(255, 255, 255, 0.6) 0%, + rgba(255, 255, 255, 0.4) 100% + ); + border-top: 1rpx solid var(--border-separator); } .order-price { - font-size: var(--font-size-xl); - color: var(--error-color); - font-weight: var(--font-weight-bold); + font-size: 40rpx; + color: var(--primary); + font-weight: 700; + letter-spacing: -0.5px; +} + +.order-price::before { + content: '合计 '; + font-size: 24rpx; + color: var(--text-tertiary); + font-weight: 400; } .order-time { - font-size: var(--font-size-xs); + font-size: 24rpx; color: var(--text-tertiary); + font-weight: 400; } -/* 空状态 */ -.empty-state { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - padding: 100px 0; - animation: fadeIn 0.5s ease forwards; -} +/* ========== 订单操作按钮 ========== */ -.empty-icon { - font-size: 100px; - margin-bottom: 20px; - opacity: 0.4; -} - -.empty-text { - font-size: var(--font-size-lg); - color: var(--text-tertiary); - font-weight: var(--font-weight-medium); - margin-bottom: 8px; -} - -.empty-hint { - font-size: var(--font-size-sm); - color: var(--text-placeholder); -} - -/* 订单操作按钮 */ .order-actions { display: flex; - gap: 12px; - margin-top: 16px; - padding-top: 16px; - border-top: 1px solid var(--border-light); + gap: 20rpx; + padding: 24rpx 32rpx 32rpx; + background: linear-gradient( + 135deg, + rgba(255, 255, 255, 0.4) 0%, + rgba(255, 255, 255, 0.2) 100% + ); + border-top: 1rpx dashed var(--border-light); } .order-action-btn { flex: 1; - height: 64px; + height: 76rpx; border-radius: var(--radius-md); - font-size: var(--font-size-sm); - border: none; - transition: all var(--transition-fast); -} - -.order-action-btn::after { + font-size: 28rpx; + font-weight: 600; border: none; + transition: all var(--transition-scale); + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; } .order-action-btn:active { @@ -245,85 +411,71 @@ page { } .order-action-btn.primary { - background: var(--primary-gradient); - color: #fff; - box-shadow: 0 4px 12px rgba(90, 147, 223, 0.2); + background: var(--primary); + color: var(--text-inverse); + box-shadow: 0 4rpx 16rpx rgba(0, 122, 255, 0.25); } .order-action-btn.secondary { - background: #f5f5f7; + background: var(--bg-hover); color: var(--text-secondary); + border: 2rpx solid var(--border-primary); } -/* 进度指示器 */ -.order-progress { - display: flex; - align-items: center; - margin: 16px 0; -} +/* ========== 空状态 ========== */ -.progress-step { - flex: 1; +.empty-state { display: flex; flex-direction: column; align-items: center; - position: relative; -} - -.progress-step::before { - content: ''; - position: absolute; - top: 14px; - left: 50%; - width: 100%; - height: 2px; - background: var(--border-light); -} - -.progress-step:last-child::before { - display: none; -} - -.progress-step.completed::before { - background: var(--success-color); -} - -.progress-dot { - width: 28px; - height: 28px; - border-radius: 50%; - background: var(--border-light); - display: flex; - align-items: center; justify-content: center; - font-size: 14px; - color: var(--text-tertiary); - z-index: 1; + padding: 120rpx 40rpx; + animation: fadeIn 0.5s ease forwards; } -.progress-step.completed .progress-dot { - background: var(--success-color); - color: #fff; +.empty-icon { + font-size: 120rpx; + margin-bottom: 32rpx; + opacity: 0.6; } -.progress-step.current .progress-dot { - background: var(--primary-color); - color: #fff; - animation: pulse 2s ease infinite; -} - -.progress-label { - font-size: var(--font-size-xs); - color: var(--text-tertiary); - margin-top: 8px; -} - -.progress-step.completed .progress-label, -.progress-step.current .progress-label { +.empty-text { + font-size: 36rpx; color: var(--text-secondary); + font-weight: 600; + margin-bottom: 16rpx; +} + +.empty-hint { + font-size: 28rpx; + color: var(--text-tertiary); + font-weight: 400; +} + +/* ========== 动画 ========== */ + +@keyframes fadeInUp { + from { + opacity: 0; + transform: translateY(32rpx); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +@keyframes fadeInDown { + from { + opacity: 0; + transform: translateY(-20rpx); + } + to { + opacity: 1; + transform: translateY(0); + } } -/* 动画 */ @keyframes fadeIn { from { opacity: 0; @@ -333,22 +485,55 @@ page { } } -@keyframes fadeInDown { - from { - opacity: 0; - transform: translateY(-10px); +/* ========== 响应式适配 ========== */ + +@media screen and (max-width: 375px) { + .order-main { + padding: 24rpx; } - to { - opacity: 1; - transform: translateY(0); + + .order-cover { + width: 120rpx; + height: 160rpx; + } + + .file-name { + font-size: 30rpx; } } -@keyframes pulse { - 0%, 100% { - transform: scale(1); +/* ========== 深色模式适配 ========== */ + +@media (prefers-color-scheme: dark) { + .order-top { + background: linear-gradient( + 135deg, + rgba(50, 50, 50, 0.9) 0%, + rgba(40, 40, 40, 0.7) 100% + ); } - 50% { - transform: scale(1.1); + + .order-bottom { + background: linear-gradient( + 135deg, + rgba(50, 50, 50, 0.6) 0%, + rgba(40, 40, 40, 0.4) 100% + ); + } + + .cover-placeholder { + background: linear-gradient( + 135deg, + rgba(60, 60, 60, 0.8) 0%, + rgba(50, 50, 50, 0.6) 100% + ); + } + + .order-cover { + background: linear-gradient( + 135deg, + rgba(0, 122, 255, 0.1) 0%, + rgba(0, 122, 255, 0.15) 100% + ); } } diff --git a/src/pages/order/index.tsx b/src/pages/order/index.tsx index 4822fa6..3a4538c 100644 --- a/src/pages/order/index.tsx +++ b/src/pages/order/index.tsx @@ -1,6 +1,7 @@ import React, { useState } from "react"; -import { View, Text, Button } from "@tarojs/components"; +import { View, Text, Button, Image } from "@tarojs/components"; import Taro from '@tarojs/taro'; +import TabBar from '../../components/TabBar/TabBar'; import './index.css'; interface OrderItem { @@ -14,9 +15,12 @@ interface OrderItem { status: string; statusColor: string; time: string; + coverImage?: string; // 封面预览图 + fileType?: string; // 文件类型 } const OrderIndex = () => { + const [tabIndex, setTabIndex] = useState(1); // 订单页面使用订单标签(索引1) const [orders] = useState([ { id: 10001, @@ -28,7 +32,9 @@ const OrderIndex = () => { totalPrice: 0.10, status: '已完成', statusColor: '#52c41a', - time: '2026-03-30 10:30' + time: '2026-03-30 10:30', + coverImage: '', // 可设置为实际封面图URL + fileType: 'docx' }, { id: 10002, @@ -40,7 +46,23 @@ const OrderIndex = () => { totalPrice: 1.20, status: '打印中', statusColor: '#1890ff', - time: '2026-03-29 15:20' + time: '2026-03-29 15:20', + coverImage: '', + fileType: 'pdf' + }, + { + id: 10003, + fileName: '课程大纲.pptx', + printerName: '教学楼C打印机', + printType: 'A4', + colorType: '彩色', + copies: 1, + totalPrice: 0.80, + status: '待支付', + statusColor: '#faad14', + time: '2026-03-28 09:15', + coverImage: '', + fileType: 'pptx' }, ]); @@ -50,6 +72,23 @@ const OrderIndex = () => { }); }; + // 获取文件类型图标 + const getFileIcon = (fileType?: string) => { + const iconMap: Record = { + 'pdf': '📄', + 'docx': '📝', + 'doc': '📝', + 'pptx': '📊', + 'ppt': '📊', + 'xlsx': '📈', + 'xls': '📈', + 'jpg': '🖼️', + 'jpeg': '🖼️', + 'png': '🖼️', + }; + return iconMap[fileType || ''] || '📄'; + }; + return ( @@ -60,15 +99,41 @@ const OrderIndex = () => { {orders.map(order => ( handleViewDetail(order.id)}> + {/* 订单头部:订单号和状态 */} 订单号: {order.id} {order.status} - - {order.fileName} - {order.printType} {order.colorType} × {order.copies}份 - 📍 {order.printerName} + + {/* 订单主体:左侧信息 + 右侧封面 */} + + + + {getFileIcon(order.fileType)} + {order.fileName} + + {order.printType} {order.colorType} × {order.copies}份 + 📍 {order.printerName} + + + {/* 右侧封面预览 */} + + {order.coverImage ? ( + + ) : ( + + {getFileIcon(order.fileType)} + 预览 + + )} + + + {/* 订单底部:价格和时间 */} ¥{order.totalPrice.toFixed(2)} {order.time} @@ -83,6 +148,8 @@ const OrderIndex = () => { 上传文件即可创建订单 )} + + ); }; diff --git a/src/pages/user/index.css b/src/pages/user/index.css index 60e19dd..1e69d30 100644 --- a/src/pages/user/index.css +++ b/src/pages/user/index.css @@ -1,58 +1,65 @@ /* ============================================ - 用户中心页面样式 - 爱印通 + 用户中心页面样式 - 爱印通 Apple Liquid Glass 设计系统 + 大字体大控件版本 - 微信小程序优化 ============================================ */ @import '../../styles/variables.css'; page { - background: linear-gradient(180deg, #e8f0fe 0%, #f0f5ff 30%, #fafbfc 100%); + background: var(--bg-page-gradient); min-height: 100vh; } .user-page { min-height: 100vh; - padding-bottom: 140px; + padding-bottom: 280rpx; + position: relative; } -/* 用户头部 */ +/* ========== 用户头部 ========== */ + .user-header { background: var(--primary-gradient); - padding: 80px 32px 60px; + padding: 160rpx 48rpx 128rpx; position: relative; overflow: hidden; - border-radius: 0 0 40px 40px; + border-radius: 0 0 64rpx 64rpx; } .user-header::before { content: ''; position: absolute; - top: -30%; - right: -15%; - width: 350px; - height: 350px; - background: rgba(255, 255, 255, 0.1); + top: -20%; + right: -10%; + width: 600rpx; + height: 600rpx; + background: rgba(255, 255, 255, 0.15); border-radius: 50%; - animation: float 6s ease-in-out infinite; + animation: float 6s cubic-bezier(0.4, 0, 0.2, 1) infinite; + filter: blur(80rpx); } .user-header::after { content: ''; position: absolute; - bottom: -20%; - left: -10%; - width: 250px; - height: 250px; - background: rgba(255, 255, 255, 0.08); + bottom: -15%; + left: -5%; + width: 400rpx; + height: 400rpx; + background: rgba(255, 255, 255, 0.1); border-radius: 50%; - animation: float 8s ease-in-out infinite reverse; + animation: float 8s cubic-bezier(0.4, 0, 0.2, 1) infinite reverse; + filter: blur(60rpx); } @keyframes float { 0%, 100% { transform: translateY(0) scale(1); + opacity: 0.8; } 50% { - transform: translateY(-15px) scale(1.05); + transform: translateY(-30rpx) scale(1.05); + opacity: 1; } } @@ -65,62 +72,76 @@ page { z-index: 1; } -/* 头像 */ +/* ========== 头像 ========== */ + .avatar { - width: 100px; - height: 100px; - background: rgba(255, 255, 255, 0.2); + width: 176rpx; + height: 176rpx; + background: rgba(255, 255, 255, 0.25); + backdrop-filter: blur(20rpx); + -webkit-backdrop-filter: blur(20rpx); border-radius: 50%; display: flex; align-items: center; justify-content: center; - margin-bottom: 16px; - border: 3px solid rgba(255, 255, 255, 0.4); - box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15); + margin-bottom: 32rpx; + border: 6rpx solid rgba(255, 255, 255, 0.5); + box-shadow: 0 16rpx 48rpx rgba(0, 0, 0, 0.15); + transition: transform var(--transition-base); +} + +.avatar:active { + transform: scale(0.95); } .avatar-default { - width: 100px; - height: 100px; + width: 176rpx; + height: 176rpx; background: rgba(255, 255, 255, 0.25); + backdrop-filter: blur(20rpx); + -webkit-backdrop-filter: blur(20rpx); border-radius: 50%; display: flex; align-items: center; justify-content: center; - margin-bottom: 16px; - border: 3px solid rgba(255, 255, 255, 0.4); + margin-bottom: 32rpx; + border: 6rpx solid rgba(255, 255, 255, 0.5); + box-shadow: 0 16rpx 48rpx rgba(0, 0, 0, 0.15); } .avatar-text { - font-size: 48px; - color: #fff; - font-weight: var(--font-weight-bold); + font-size: 72rpx; + color: #ffffff; + font-weight: 700; } .avatar-icon { - font-size: 48px; - opacity: 0.8; + font-size: 80rpx; + opacity: 0.85; + color: #ffffff; } -/* 用户信息 */ +/* ========== 用户信息 ========== */ + .nickname { - font-size: var(--font-size-2xl); - color: #fff; - font-weight: var(--font-weight-semibold); - margin-bottom: 8px; - text-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); + font-size: 44rpx; + color: #ffffff; + font-weight: 600; + margin-bottom: 12rpx; + text-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.15); + letter-spacing: -0.3px; } .balance { - font-size: var(--font-size-base); + font-size: 30rpx; color: rgba(255, 255, 255, 0.85); + font-weight: 400; } -/* 登录提示 */ .login-text { - font-size: var(--font-size-lg); - color: #fff; - font-weight: var(--font-weight-medium); + font-size: 34rpx; + color: #ffffff; + font-weight: 500; opacity: 0.9; } @@ -128,22 +149,26 @@ page { opacity: 0.7; } -/* 菜单区域 */ +/* ========== 菜单区域 ========== */ + .menu-section { - margin: 24px; - background: var(--bg-card); + margin: 40rpx 32rpx; + background: var(--bg-glass); + backdrop-filter: blur(48rpx) saturate(180%); + -webkit-backdrop-filter: blur(48rpx) saturate(180%); border-radius: var(--radius-xl); overflow: hidden; box-shadow: var(--shadow-md); - border: 1px solid var(--border-light); + border: 1px solid var(--glass-border); } .menu-item { display: flex; align-items: center; - padding: 24px 22px; - border-bottom: 1px solid var(--border-light); + padding: 36rpx 40rpx; + border-bottom: 1px solid var(--border-separator); transition: background var(--transition-fast); + cursor: pointer; } .menu-item:last-child { @@ -154,11 +179,42 @@ page { background: var(--bg-hover); } +.menu-icon-wrapper { + width: 80rpx; + height: 80rpx; + border-radius: var(--radius-md); + display: flex; + align-items: center; + justify-content: center; + margin-right: 28rpx; + flex-shrink: 0; + box-shadow: var(--shadow-sm); +} + +.menu-icon-wrapper.primary { + background: linear-gradient(135deg, #e8f0fe 0%, #d0dcfa 100%); + color: var(--primary); +} + +.menu-icon-wrapper.success { + background: linear-gradient(135deg, #f0fdf4 0%, #bbf7d0 100%); + color: var(--success); +} + +.menu-icon-wrapper.warning { + background: linear-gradient(135deg, #fffbeb 0%, #fde68a 100%); + color: var(--warning); +} + +.menu-icon-wrapper.error { + background: linear-gradient(135deg, #fef2f2 0%, #fecaca 100%); + color: var(--error); +} + .menu-icon { - font-size: 32px; - margin-right: 16px; - width: 40px; - height: 40px; + font-size: 40rpx; + width: 48rpx; + height: 48rpx; display: flex; align-items: center; justify-content: center; @@ -166,85 +222,87 @@ page { .menu-title { flex: 1; - font-size: var(--font-size-base); + font-size: 32rpx; color: var(--text-primary); - font-weight: var(--font-weight-medium); + font-weight: 500; } .menu-value { - font-size: var(--font-size-base); - color: var(--error-color); - font-weight: var(--font-weight-semibold); - margin-right: 8px; + font-size: 32rpx; + color: var(--primary); + font-weight: 600; + margin-right: 16rpx; } .menu-arrow { - font-size: 24px; - color: var(--text-placeholder); + font-size: 36rpx; + color: var(--text-tertiary); } -/* 退出登录 */ +/* ========== 退出登录 ========== */ + .logout-section { - padding: 0 24px; - margin-top: 32px; + padding: 0 32rpx; + margin-top: 48rpx; } .logout-btn { width: 100%; - height: 88px; + height: 100rpx; background: var(--bg-card); - color: var(--error-color); - font-size: var(--font-size-lg); - font-weight: var(--font-weight-medium); - border-radius: 44px; + color: var(--error); + font-size: 32rpx; + font-weight: 500; + border-radius: var(--radius-md); display: flex; align-items: center; justify-content: center; - border: 1px solid rgba(255, 77, 79, 0.15); + border: 2rpx solid rgba(255, 59, 48, 0.15); box-shadow: var(--shadow-sm); - transition: all var(--transition-base); -} - -.logout-btn::after { - border: none; + transition: all var(--transition-scale); + cursor: pointer; } .logout-btn:active { - transform: scale(0.97); - background: #fff5f5; + transform: scale(0.98); + background: #fef2f2; + border-color: rgba(255, 59, 48, 0.25); } -/* 菜单项图标容器 */ -.menu-icon-wrapper { - width: 40px; - height: 40px; - border-radius: var(--radius-sm); - display: flex; - align-items: center; - justify-content: center; - margin-right: 16px; -} +/* ========== 余额高亮 ========== */ -.menu-icon-wrapper.primary { - background: linear-gradient(135deg, #e8f0fe 0%, #d0dcfa 100%); -} - -.menu-icon-wrapper.success { - background: linear-gradient(135deg, #f6ffed 0%, #d9f7be 100%); -} - -.menu-icon-wrapper.warning { - background: linear-gradient(135deg, #fffbe6 0%, #ffe58f 100%); -} - -.menu-icon-wrapper.error { - background: linear-gradient(135deg, #fff1f0 0%, #ffccc7 100%); -} - -/* 余额高亮 */ .balance-highlight { - background: linear-gradient(135deg, #ff4d4f 0%, #ff7875 100%); + background: linear-gradient(135deg, #FF3B30 0%, #FF453A 100%); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; + font-weight: 600; +} + +/* ========== 装饰光晕 ========== */ + +.user-page::before { + content: ''; + position: fixed; + top: -300rpx; + right: -300rpx; + width: 1000rpx; + height: 1000rpx; + background: radial-gradient(circle, rgba(0, 122, 255, 0.08) 0%, transparent 70%); + border-radius: 50%; + pointer-events: none; + z-index: 0; +} + +.user-page::after { + content: ''; + position: fixed; + bottom: -200rpx; + left: -200rpx; + width: 800rpx; + height: 800rpx; + background: radial-gradient(circle, rgba(90, 200, 250, 0.06) 0%, transparent 70%); + border-radius: 50%; + pointer-events: none; + z-index: 0; } diff --git a/src/pages/user/index.tsx b/src/pages/user/index.tsx index a5091ed..ba343be 100644 --- a/src/pages/user/index.tsx +++ b/src/pages/user/index.tsx @@ -1,6 +1,7 @@ import React, { useEffect, useState } from "react"; import { View, Text, Button } from "@tarojs/components"; import Taro from '@tarojs/taro'; +import TabBar from '../../components/TabBar/TabBar'; import './index.css'; interface UserInfo { @@ -12,6 +13,7 @@ interface UserInfo { const UserIndex = () => { const [isLoggedIn, setIsLoggedIn] = useState(false); const [userInfo, setUserInfo] = useState(null); + const [tabIndex, setTabIndex] = useState(4); // 用户中心是第5个标签(索引4) useEffect(() => { checkLoginStatus(); @@ -109,6 +111,8 @@ const UserIndex = () => { )} + + ); }; diff --git a/src/styles/animations.css b/src/styles/animations.css index 451550e..7c8af3e 100644 --- a/src/styles/animations.css +++ b/src/styles/animations.css @@ -1,28 +1,47 @@ /* ============================================ - 微交互和动画效果 - 爱印通 + 微交互和动画效果 - 爱印通 Apple Liquid Glass 设计系统 + 0.3s 缓动曲线、缩放效果、透明度变化 ============================================ */ @import './variables.css'; /* ============================================ - 进入动画 + 核心缓动曲线 - 苹果风格 ============================================ */ -/* 淡入 */ -@keyframes fadeIn { - from { - opacity: 0; - } - to { - opacity: 1; - } +/* 标准缓动 - 用于大多数过渡 */ +.ease-standard { + transition-timing-function: cubic-bezier(0.25, 0.1, 0.25, 1); +} + +/* 弹性缓动 - 用于悬浮/强调效果 */ +.ease-bounce { + transition-timing-function: cubic-bezier(0.34, 1.56, 0.64, 1); +} + +/* 缩放缓动 - 用于按钮点击 */ +.ease-scale { + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); +} + +/* 平滑缓动 - 用于页面切换 */ +.ease-smooth { + transition-timing-function: cubic-bezier(0.45, 0, 0.55, 1); +} + +/* ============================================ + 进入动画 - 淡入上浮系列 + ============================================ */ + +@keyframes fadeIn { + from { opacity: 0; } + to { opacity: 1; } } -/* 淡入上浮 */ @keyframes fadeInUp { from { opacity: 0; - transform: translateY(20px); + transform: translateY(16px); } to { opacity: 1; @@ -30,11 +49,10 @@ } } -/* 淡入下沉 */ @keyframes fadeInDown { from { opacity: 0; - transform: translateY(-20px); + transform: translateY(-16px); } to { opacity: 1; @@ -42,11 +60,10 @@ } } -/* 从左淡入 */ @keyframes fadeInLeft { from { opacity: 0; - transform: translateX(-20px); + transform: translateX(-16px); } to { opacity: 1; @@ -54,11 +71,10 @@ } } -/* 从右淡入 */ @keyframes fadeInRight { from { opacity: 0; - transform: translateX(20px); + transform: translateX(16px); } to { opacity: 1; @@ -66,8 +82,8 @@ } } -/* 缩放淡入 */ -@keyframes scaleIn { +/* 缩放淡入 - 苹果风格弹窗 */ +@keyframes scaleFadeIn { from { opacity: 0; transform: scale(0.95); @@ -78,203 +94,447 @@ } } -/* 弹窗动画 */ -@keyframes modalIn { +/* 弹性缩放淡入 */ +@keyframes springScaleIn { + 0% { + opacity: 0; + transform: scale(0.9); + } + 50% { + transform: scale(1.02); + } + 70% { + transform: scale(0.98); + } + 100% { + opacity: 1; + transform: scale(1); + } +} + +/* 弹窗动画 - 从底部弹出 */ +@keyframes sheetSlideUp { from { opacity: 0; - transform: scale(0.9) translateY(20px); + transform: translateY(100%); } to { opacity: 1; - transform: scale(1) translateY(0); + transform: translateY(0); } } /* 弹窗关闭 */ -@keyframes modalOut { +@keyframes sheetSlideDown { + from { + opacity: 1; + transform: translateY(0); + } + to { + opacity: 0; + transform: translateY(100%); + } +} + +/* 模态框动画 */ +@keyframes modalEnter { + from { + opacity: 0; + transform: scale(0.95) translateY(10px); + } + to { + opacity: 1; + transform: scale(1) translateY(0); + } +} + +@keyframes modalExit { from { opacity: 1; transform: scale(1) translateY(0); } to { opacity: 0; - transform: scale(0.9) translateY(20px); + transform: scale(0.95) translateY(10px); } } /* ============================================ - 持续动画 + 持续动画 - 微妙效果 ============================================ */ -/* 脉冲 */ +/* 轻微脉冲 - 用于重要提示 */ @keyframes pulse { - 0%, 100% { - transform: scale(1); - } - 50% { - transform: scale(1.05); - } -} - -/* 轻微脉冲 */ -@keyframes pulseSoft { 0%, 100% { transform: scale(1); opacity: 1; } 50% { transform: scale(1.02); - opacity: 0.9; + opacity: 0.95; } } -/* 闪烁 */ -@keyframes blink { +/* 呼吸效果 - 用于光晕 */ +@keyframes breathe { 0%, 100% { - opacity: 1; + box-shadow: 0 0 0 0 rgba(0, 122, 255, 0.2); } 50% { - opacity: 0.5; + box-shadow: 0 0 0 8px rgba(0, 122, 255, 0); } } -/* 旋转 */ -@keyframes spin { - from { - transform: rotate(0deg); - } - to { - transform: rotate(360deg); - } -} - -/* 摇摆 */ -@keyframes swing { - 0%, 100% { - transform: rotate(0deg); - } - 25% { - transform: rotate(3deg); - } - 75% { - transform: rotate(-3deg); - } -} - -/* 浮动 */ +/* 悬浮效果 - 用于卡片 */ @keyframes float { 0%, 100% { transform: translateY(0); } 50% { - transform: translateY(-10px); + transform: translateY(-4px); } } -/* 弹跳 */ -@keyframes bounce { - 0%, 100% { - transform: translateY(0); - } - 50% { - transform: translateY(-5px); - } -} - -/* 光泽扫过 */ -@keyframes shimmer { +/* 光泽扫过 - 用于玻璃效果 */ +@keyframes glassShimmer { 0% { - background-position: -200% 0; + background-position: -200% center; } 100% { - background-position: 200% 0; + background-position: 200% center; } } -/* 进度条动画 */ -@keyframes progressStripes { - from { - background-position: 0 0; +/* 液态波动 */ +@keyframes liquidWave { + 0% { + border-radius: 60% 40% 30% 70% / 60% 30% 70% 40%; } - to { - background-position: 40px 0; + 50% { + border-radius: 30% 60% 70% 40% / 50% 60% 30% 60%; } + 100% { + border-radius: 60% 40% 30% 70% / 60% 30% 70% 40%; + } +} + +/* 旋转 */ +@keyframes spin { + from { transform: rotate(0deg); } + to { transform: rotate(360deg); } +} + +/* 摇摆 - 用于错误提示 */ +@keyframes shake { + 0%, 100% { transform: translateX(0); } + 10%, 30%, 50%, 70%, 90% { transform: translateX(-4px); } + 20%, 40%, 60%, 80% { transform: translateX(4px); } +} + +/* 弹跳通知 */ +@keyframes notificationBounce { + 0% { + transform: scale(0.3); + opacity: 0; + } + 50% { + transform: scale(1.05); + } + 70% { + transform: scale(0.9); + } + 100% { + transform: scale(1); + opacity: 1; + } +} + +/* 进度条光泽 */ +@keyframes progressGlide { + from { background-position: -40px 0; } + to { background-position: 0 0; } } /* ============================================ - 交互动画类 + 交互动画 - 点击反馈 ============================================ */ -/* 点击反馈 */ -.tap-feedback { - transition: transform 0.15s ease, opacity 0.15s ease; +/* 按钮点击缩放 - 0.98 缩放 */ +.btn-press { + transition: transform 0.15s cubic-bezier(0.4, 0, 0.2, 1), + opacity 0.15s cubic-bezier(0.4, 0, 0.2, 1); } -.tap-feedback:active { - transform: scale(0.96); +.btn-press:active { + transform: scale(0.98); opacity: 0.9; } -/* 悬停反馈(用于支持hover的设备) */ +/* 卡片点击反馈 */ +.card-press { + transition: transform 0.2s cubic-bezier(0.4, 0, 0.2, 1), + box-shadow 0.2s cubic-bezier(0.4, 0, 0.2, 1); +} + +.card-press:active { + transform: scale(0.99); + box-shadow: var(--shadow-sm); +} + +/* 悬浮抬升 - 支持 hover 的设备 */ @media (hover: hover) { - .hover-elevate { - transition: transform 0.25s ease, box-shadow 0.25s ease; + .hover-lift { + transition: transform 0.3s cubic-bezier(0.25, 0.1, 0.25, 1), + box-shadow 0.3s cubic-bezier(0.25, 0.1, 0.25, 1); } - .hover-elevate:hover { + .hover-lift:hover { transform: translateY(-2px); box-shadow: var(--shadow-lg); } + + .hover-lift-strong:hover { + transform: translateY(-4px); + box-shadow: var(--shadow-xl); + } } -/* 卡片进入动画 */ -.animate-card-enter { - animation: fadeInUp 0.4s ease forwards; +/* 玻璃态悬浮 */ +@media (hover: hover) { + .glass-hover { + transition: all 0.3s cubic-bezier(0.25, 0.1, 0.25, 1); + } + + .glass-hover:hover { + background: rgba(255, 255, 255, 0.9); + backdrop-filter: blur(32px) saturate(200%); + -webkit-backdrop-filter: blur(32px) saturate(200%); + box-shadow: var(--shadow-lg); + transform: translateY(-1px); + } } -/* 列表项动画延迟 */ -.animate-stagger-1 { animation-delay: 0.05s; } -.animate-stagger-2 { animation-delay: 0.1s; } -.animate-stagger-3 { animation-delay: 0.15s; } -.animate-stagger-4 { animation-delay: 0.2s; } -.animate-stagger-5 { animation-delay: 0.25s; } - /* ============================================ - 加载状态动画 + 滚动时工具栏透明度变化 ============================================ */ -/* 加载旋转器 */ -.loading-spinner { - width: 32px; - height: 32px; - border: 3px solid var(--border-light); - border-top-color: var(--primary-color); - border-radius: 50%; +/* 导航栏滚动效果 */ +.nav-scroll-aware { + transition: background-color 0.3s cubic-bezier(0.25, 0.1, 0.25, 1), + backdrop-filter 0.3s cubic-bezier(0.25, 0.1, 0.25, 1), + box-shadow 0.3s cubic-bezier(0.25, 0.1, 0.25, 1); +} + +.nav-scroll-aware.scrolled { + background: var(--bg-glass-heavy); + backdrop-filter: blur(32px) saturate(200%); + -webkit-backdrop-filter: blur(32px) saturate(200%); + box-shadow: 0 1px 8px rgba(0, 0, 0, 0.08); +} + +.nav-scroll-aware.scrolled-dark { + background: var(--bg-glass-heavy); + backdrop-filter: blur(32px) saturate(200%); + -webkit-backdrop-filter: blur(32px) saturate(200%); + box-shadow: 0 1px 8px rgba(0, 0, 0, 0.15); +} + +/* ============================================ + 实用动画类 + ============================================ */ + +/* 淡入 */ +.animate-fade-in { + animation: fadeIn 0.3s cubic-bezier(0.25, 0.1, 0.25, 1) forwards; +} + +/* 淡入上浮 */ +.animate-fade-in-up { + animation: fadeInUp 0.4s cubic-bezier(0.25, 0.1, 0.25, 1) forwards; +} + +/* 淡入下沉 */ +.animate-fade-in-down { + animation: fadeInDown 0.4s cubic-bezier(0.25, 0.1, 0.25, 1) forwards; +} + +/* 淡入左移 */ +.animate-fade-in-left { + animation: fadeInLeft 0.4s cubic-bezier(0.25, 0.1, 0.25, 1) forwards; +} + +/* 淡入右移 */ +.animate-fade-in-right { + animation: fadeInRight 0.4s cubic-bezier(0.25, 0.1, 0.25, 1) forwards; +} + +/* 缩放淡入 */ +.animate-scale-in { + animation: scaleFadeIn 0.3s cubic-bezier(0.25, 0.1, 0.25, 1) forwards; +} + +/* 弹性缩放 */ +.animate-spring-in { + animation: springScaleIn 0.5s cubic-bezier(0.34, 1.56, 0.64, 1) forwards; +} + +/* 脉冲 */ +.animate-pulse { + animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite; +} + +/* 呼吸光晕 */ +.animate-breathe { + animation: breathe 2s cubic-bezier(0.4, 0, 0.6, 1) infinite; +} + +/* 悬浮 */ +.animate-float { + animation: float 3s cubic-bezier(0.4, 0, 0.2, 1) infinite; +} + +/* 旋转 */ +.animate-spin { animation: spin 0.8s linear infinite; } -.loading-spinner.small { - width: 20px; - height: 20px; - border-width: 2px; +/* 摇摆 */ +.animate-shake { + animation: shake 0.5s cubic-bezier(0.4, 0, 0.2, 1); } -.loading-spinner.large { - width: 48px; - height: 48px; - border-width: 4px; +/* 弹跳通知 */ +.animate-bounce-in { + animation: notificationBounce 0.5s cubic-bezier(0.34, 1.56, 0.64, 1) forwards; } -/* 骨架屏动画 */ +/* 光泽扫过 */ +.animate-shimmer { + background: linear-gradient( + 90deg, + transparent 0%, + rgba(255, 255, 255, 0.6) 50%, + transparent 100% + ); + background-size: 200% 100%; + animation: glassShimmer 2s cubic-bezier(0.4, 0, 0.2, 1) infinite; +} + +/* ============================================ + 延迟动画 - 错峰效果 + ============================================ */ + +.delay-50 { animation-delay: 50ms; } +.delay-100 { animation-delay: 100ms; } +.delay-150 { animation-delay: 150ms; } +.delay-200 { animation-delay: 200ms; } +.delay-250 { animation-delay: 250ms; } +.delay-300 { animation-delay: 300ms; } +.delay-350 { animation-delay: 350ms; } +.delay-400 { animation-delay: 400ms; } +.delay-450 { animation-delay: 450ms; } +.delay-500 { animation-delay: 500ms; } + +/* 列表项错峰动画 */ +.stagger-1 { animation-delay: calc(var(--delay-base, 0ms) + 0ms); } +.stagger-2 { animation-delay: calc(var(--delay-base, 0ms) + 50ms); } +.stagger-3 { animation-delay: calc(var(--delay-base, 0ms) + 100ms); } +.stagger-4 { animation-delay: calc(var(--delay-base, 0ms) + 150ms); } +.stagger-5 { animation-delay: calc(var(--delay-base, 0ms) + 200ms); } +.stagger-6 { animation-delay: calc(var(--delay-base, 0ms) + 250ms); } +.stagger-7 { animation-delay: calc(var(--delay-base, 0ms) + 300ms); } +.stagger-8 { animation-delay: calc(var(--delay-base, 0ms) + 350ms); } + +/* ============================================ + 过渡效果类 + ============================================ */ + +.transition-all { + transition: all 0.3s cubic-bezier(0.25, 0.1, 0.25, 1); +} + +.transition-fast { + transition: all 0.15s cubic-bezier(0.4, 0, 0.2, 1); +} + +.transition-slow { + transition: all 0.5s cubic-bezier(0.25, 0.1, 0.25, 1); +} + +.transition-opacity { + transition: opacity 0.3s cubic-bezier(0.25, 0.1, 0.25, 1); +} + +.transition-transform { + transition: transform 0.3s cubic-bezier(0.25, 0.1, 0.25, 1); +} + +.transition-colors { + transition: color 0.3s cubic-bezier(0.25, 0.1, 0.25, 1), + background-color 0.3s cubic-bezier(0.25, 0.1, 0.25, 1), + border-color 0.3s cubic-bezier(0.25, 0.1, 0.25, 1); +} + +.transition-shadow { + transition: box-shadow 0.3s cubic-bezier(0.25, 0.1, 0.25, 1); +} + +.transition-background { + transition: background 0.3s cubic-bezier(0.25, 0.1, 0.25, 1), + backdrop-filter 0.3s cubic-bezier(0.25, 0.1, 0.25, 1); +} + +/* ============================================ + 页面切换动画 + ============================================ */ + +.page-enter { + animation: fadeInRight 0.35s cubic-bezier(0.25, 0.1, 0.25, 1) forwards; +} + +.page-exit { + animation: fadeInLeft 0.3s cubic-bezier(0.25, 0.1, 0.25, 1) reverse forwards; +} + +/* 模态框 */ +.modal-overlay-enter { + animation: fadeIn 0.25s cubic-bezier(0.25, 0.1, 0.25, 1) forwards; +} + +.modal-content-enter { + animation: modalEnter 0.35s cubic-bezier(0.34, 1.56, 0.64, 1) forwards; +} + +.modal-overlay-exit { + animation: fadeIn 0.2s cubic-bezier(0.25, 0.1, 0.25, 1) reverse forwards; +} + +.modal-content-exit { + animation: modalExit 0.25s cubic-bezier(0.4, 0, 0.2, 1) forwards; +} + +/* 底部弹 sheet */ +.sheet-enter { + animation: sheetSlideUp 0.35s cubic-bezier(0.32, 0.72, 0, 1) forwards; +} + +.sheet-exit { + animation: sheetSlideDown 0.3s cubic-bezier(0.32, 0.72, 0, 1) forwards; +} + +/* ============================================ + 骨架屏动画 + ============================================ */ + .skeleton { background: linear-gradient( 90deg, - #f0f0f0 25%, - #e8e8e8 50%, - #f0f0f0 75% + var(--border-light) 25%, + var(--bg-hover) 50%, + var(--border-light) 75% ); background-size: 200% 100%; - animation: shimmer 1.5s infinite; + animation: glassShimmer 1.5s infinite; border-radius: var(--radius-sm); } @@ -283,13 +543,13 @@ margin-bottom: 8px; } -.skeleton-text.short { - width: 60%; -} +.skeleton-text.short { width: 40%; } +.skeleton-text.medium { width: 60%; } +.skeleton-text.long { width: 80%; } .skeleton-title { height: 24px; - width: 40%; + width: 50%; margin-bottom: 12px; } @@ -299,172 +559,118 @@ border-radius: 50%; } +.skeleton-image { + width: 100%; + height: 120px; + border-radius: var(--radius-md); +} + .skeleton-button { height: 44px; width: 120px; + border-radius: var(--radius-md); } /* ============================================ - 状态指示器动画 + 加载指示器 ============================================ */ -/* 成功动画 */ -@keyframes successCheck { - 0% { - stroke-dashoffset: 100; - } - 100% { - stroke-dashoffset: 0; - } +.loading-spinner { + width: 32px; + height: 32px; + border: 2.5px solid var(--border-primary); + border-top-color: var(--primary); + border-radius: 50%; + animation: spin 0.8s linear infinite; } -/* 错误抖动 */ -@keyframes shake { - 0%, 100% { - transform: translateX(0); - } - 10%, 30%, 50%, 70%, 90% { - transform: translateX(-5px); - } - 20%, 40%, 60%, 80% { - transform: translateX(5px); - } +.loading-spinner.sm { + width: 20px; + height: 20px; + border-width: 2px; } -/* 通知弹跳 */ -@keyframes notificationBounce { - 0% { - transform: scale(0.8); - opacity: 0; +.loading-spinner.lg { + width: 48px; + height: 48px; + border-width: 3px; +} + +/* 点加载器 */ +.loading-dots { + display: flex; + gap: 4px; + align-items: center; + justify-content: center; +} + +.loading-dots span { + width: 8px; + height: 8px; + background: var(--primary); + border-radius: 50%; + animation: loadingDot 1.4s ease-in-out infinite; +} + +.loading-dots span:nth-child(2) { animation-delay: 0.2s; } +.loading-dots span:nth-child(3) { animation-delay: 0.4s; } + +@keyframes loadingDot { + 0%, 80%, 100% { + transform: scale(0.6); + opacity: 0.4; } - 50% { - transform: scale(1.05); - } - 70% { - transform: scale(0.95); - } - 100% { + 40% { transform: scale(1); opacity: 1; } } /* ============================================ - 实用动画类 + 成功/错误状态动画 ============================================ */ -.animate-fade-in { - animation: fadeIn 0.3s ease forwards; +/* 成功勾选 */ +@keyframes successCheck { + 0% { stroke-dashoffset: 100; } + 100% { stroke-dashoffset: 0; } } -.animate-fade-in-up { - animation: fadeInUp 0.4s ease forwards; +.success-icon { + width: 64px; + height: 64px; + border-radius: 50%; + background: rgba(52, 199, 89, 0.1); + display: flex; + align-items: center; + justify-content: center; + animation: scaleFadeIn 0.4s cubic-bezier(0.34, 1.56, 0.64, 1) forwards; } -.animate-fade-in-down { - animation: fadeInDown 0.4s ease forwards; +.success-icon svg { + stroke: var(--success); + stroke-width: 2; + stroke-linecap: round; + stroke-linejoin: round; + fill: none; } -.animate-scale-in { - animation: scaleIn 0.3s ease forwards; +.success-icon svg circle { + stroke-dasharray: 166; + stroke-dashoffset: 166; + animation: successCheck 0.6s ease forwards; } -.animate-pulse { - animation: pulse 2s ease infinite; -} - -.animate-pulse-soft { - animation: pulseSoft 3s ease infinite; -} - -.animate-blink { - animation: blink 1s ease infinite; -} - -.animate-spin { - animation: spin 0.8s linear infinite; -} - -.animate-swing { - animation: swing 1s ease infinite; -} - -.animate-float { - animation: float 3s ease-in-out infinite; -} - -.animate-bounce { - animation: bounce 2s ease-in-out infinite; -} - -.animate-shake { - animation: shake 0.5s ease; -} - -.animate-shimmer { - background: linear-gradient( - 90deg, - transparent 0%, - rgba(255, 255, 255, 0.4) 50%, - transparent 100% - ); - background-size: 200% 100%; - animation: shimmer 1.5s infinite; +.success-icon svg path { + stroke-dasharray: 48; + stroke-dashoffset: 48; + animation: successCheck 0.3s 0.4s ease forwards; } /* ============================================ - 过渡效果 + 可见性工具类 ============================================ */ -.transition-all { - transition: all var(--transition-base); -} - -.transition-fast { - transition: all var(--transition-fast); -} - -.transition-slow { - transition: all var(--transition-slow); -} - -.transition-opacity { - transition: opacity var(--transition-base); -} - -.transition-transform { - transition: transform var(--transition-base); -} - -.transition-colors { - transition: color var(--transition-base), background-color var(--transition-base), border-color var(--transition-base); -} - -/* ============================================ - 页面切换动画 - ============================================ */ - -.page-enter { - animation: fadeInRight 0.3s ease forwards; -} - -.page-leave { - animation: fadeInLeft 0.3s ease reverse forwards; -} - -/* 模态框动画 */ -.modal-overlay-enter { - animation: fadeIn 0.2s ease forwards; -} - -.modal-content-enter { - animation: modalIn 0.3s ease forwards; -} - -.modal-overlay-leave { - animation: fadeIn 0.2s ease reverse forwards; -} - -.modal-content-leave { - animation: modalOut 0.2s ease forwards; -} \ No newline at end of file +.visible { visibility: visible; } +.invisible { visibility: hidden; } +.collapse { visibility: collapse; } diff --git a/src/styles/common.css b/src/styles/common.css index da696bb..ba058ac 100644 --- a/src/styles/common.css +++ b/src/styles/common.css @@ -1,294 +1,574 @@ /* ============================================ - 全局通用样式 - 爱印通 + 全局通用样式 - 爱印通 Apple Liquid Glass 设计系统 + 针对微信小程序优化:大字体、大控件、rpx 单位 ============================================ */ @import './variables.css'; -/* 重置样式 */ +/* ========== 基础重置 ========== */ page { background: var(--bg-page); min-height: 100vh; - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', - 'Hiragino Sans GB', 'Microsoft YaHei', sans-serif; + font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'SF Pro Text', + 'Helvetica Neue', 'PingFang SC', 'Hiragino Sans GB', + 'Microsoft YaHei', sans-serif; + color: var(--text-primary); + font-size: var(--font-size-body); + line-height: var(--line-height-normal); + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; } -/* 通用容器 */ -.container { +/* ========== 容器系统 ========== */ +.page-container { + min-height: 100vh; + background: var(--bg-page-gradient); +} + +.content-container { padding: 0 var(--spacing-lg); + max-width: 100%; } -/* 通用卡片 */ -.common-card { +.scroll-container { + height: 100vh; + overflow-y: auto; + -webkit-overflow-scrolling: touch; +} + +/* ========== 卡片系统 - 液态玻璃材质 ========== */ + +.glass-card { background: var(--bg-card); + backdrop-filter: blur(20px) saturate(180%); + -webkit-backdrop-filter: blur(20px) saturate(180%); border-radius: var(--radius-lg); padding: var(--spacing-lg); box-shadow: var(--shadow-md); + border: 1px solid var(--border-glass); + margin-bottom: var(--spacing-md); + transition: all var(--transition-base); +} + +.floating-card { + background: var(--bg-card-solid); + border-radius: var(--radius-lg); + padding: var(--spacing-lg); + box-shadow: var(--shadow-lg); border: 1px solid var(--border-light); margin-bottom: var(--spacing-md); + transition: all var(--transition-base); } -/* 通用按钮 */ -.common-btn { +.floating-card:hover { + box-shadow: var(--shadow-xl); + transform: translateY(-4rpx); +} + +.compact-card { + background: var(--bg-card); + backdrop-filter: blur(16px); + -webkit-backdrop-filter: blur(16px); + border-radius: var(--radius-md); + padding: var(--spacing-md); + box-shadow: var(--shadow-sm); + border: 1px solid var(--border-glass); + margin-bottom: var(--spacing-sm); +} + +.card-header { display: flex; align-items: center; + justify-content: space-between; + margin-bottom: var(--spacing-md); + padding-bottom: var(--spacing-md); + border-bottom: 1px solid var(--border-separator); +} + +.card-title { + font-size: var(--font-size-title3); + font-weight: var(--font-weight-semibold); + color: var(--text-primary); + letter-spacing: -0.3px; +} + +.card-subtitle { + font-size: var(--font-size-subbody); + color: var(--text-secondary); + margin-top: 4rpx; +} + +/* ========== 按钮系统 - 大尺寸,统一高度 ========== */ + +.btn { + display: inline-flex; + align-items: center; justify-content: center; + gap: var(--spacing-sm); + height: 88rpx; + padding: 0 var(--spacing-xl); border: none; border-radius: var(--radius-md); + font-size: var(--font-size-body); font-weight: var(--font-weight-medium); - transition: all var(--transition-base); + cursor: pointer; + transition: all var(--transition-scale); + white-space: nowrap; + -webkit-user-select: none; + user-select: none; } -.common-btn:active { - transform: scale(0.97); +.btn:active { + transform: scale(0.98); } -.common-btn.primary { - background: var(--primary-gradient); +.btn-primary { + background: var(--primary); color: var(--text-inverse); + box-shadow: 0 4rpx 16rpx rgba(0, 122, 255, 0.2); } -.common-btn.outline { +.btn-primary:active { + opacity: 0.9; + box-shadow: var(--shadow-pressed); +} + +.btn-secondary { background: transparent; - color: var(--primary-color); - border: 1px solid var(--primary-color); + color: var(--primary); + border: 3rpx solid var(--primary); } -.common-btn.ghost { - background: rgba(90, 147, 223, 0.1); - color: var(--primary-color); +.btn-secondary:active { + background: var(--primary-opacity-10); } -.common-btn.danger { - background: linear-gradient(135deg, #ff7875 0%, #ff4d4f 100%); +.btn-ghost { + background: var(--primary-opacity-10); + color: var(--primary); + border: none; +} + +.btn-ghost:active { + background: var(--primary-opacity-20); +} + +.btn-danger { + background: var(--error); color: var(--text-inverse); + box-shadow: 0 4rpx 16rpx rgba(255, 59, 48, 0.2); } -.common-btn.disabled, -.common-btn[disabled] { - opacity: 0.5; - pointer-events: none; +.btn-danger:active { + opacity: 0.9; } -/* 通用输入框 */ -.common-input { - width: 100%; - height: 88px; - background: #f8f9fb; - border: 1px solid var(--border-primary); - border-radius: var(--radius-sm); +/* 按钮尺寸 */ +.btn-sm { + height: 64rpx; padding: 0 var(--spacing-md); - font-size: var(--font-size-base); + font-size: var(--font-size-callout); +} + +.btn-lg { + height: 100rpx; + padding: 0 var(--spacing-2xl); + font-size: var(--font-size-title3); +} + +.btn:disabled { + opacity: 0.4; + cursor: not-allowed; + transform: none !important; +} + +.btn-icon { + width: 88rpx; + padding: 0; +} + +.btn-icon.btn-sm { + width: 64rpx; +} + +.btn-group { + display: flex; + gap: var(--spacing-sm); +} + +.btn-group .btn { + flex: 1; +} + +/* ========== 输入框系统 - 大尺寸 ========== */ + +.input-wrapper { + position: relative; + width: 100%; +} + +.input { + width: 100%; + height: 88rpx; + background: var(--glass-background); + backdrop-filter: blur(16px); + -webkit-backdrop-filter: blur(16px); + border: 1px solid var(--border-primary); + border-radius: var(--radius-md); + padding: 0 var(--spacing-md); + font-size: var(--font-size-body); color: var(--text-primary); transition: all var(--transition-base); + outline: none; } -.common-input:focus { - border-color: var(--primary-color); - background: #fff; - box-shadow: 0 0 0 3px rgba(90, 147, 223, 0.1); -} - -.common-input::placeholder { +.input::placeholder { color: var(--text-placeholder); } -/* 通用标签 */ -.common-tag { +.input:focus { + border-color: var(--primary); + background: var(--bg-card-solid); + box-shadow: 0 0 0 6rpx var(--primary-opacity-20); +} + +.input:disabled { + opacity: 0.5; + background: var(--bg-hover); +} + +.input-with-icon { + padding-left: 80rpx; +} + +.input-icon { + position: absolute; + left: var(--spacing-md); + top: 50%; + transform: translateY(-50%); + color: var(--text-tertiary); + pointer-events: none; +} + +.textarea { + width: 100%; + min-height: 200rpx; + background: var(--glass-background); + backdrop-filter: blur(16px); + -webkit-backdrop-filter: blur(16px); + border: 1px solid var(--border-primary); + border-radius: var(--radius-md); + padding: var(--spacing-md); + font-size: var(--font-size-body); + color: var(--text-primary); + transition: all var(--transition-base); + outline: none; + resize: vertical; + line-height: var(--line-height-relaxed); +} + +.textarea:focus { + border-color: var(--primary); + background: var(--bg-card-solid); + box-shadow: 0 0 0 6rpx var(--primary-opacity-20); +} + +/* ========== 标签系统 ========== */ + +.tag { display: inline-flex; align-items: center; - padding: 4px 12px; - border-radius: var(--radius-xs); - font-size: var(--font-size-xs); + gap: 8rpx; + padding: 8rpx 20rpx; + border-radius: var(--radius-full); + font-size: var(--font-size-footnote); font-weight: var(--font-weight-medium); + white-space: nowrap; } -.common-tag.primary { - background: rgba(90, 147, 223, 0.1); - color: var(--primary-color); +.tag-primary { + background: var(--primary-opacity-10); + color: var(--primary); } -.common-tag.success { - background: rgba(82, 196, 26, 0.1); - color: var(--success-color); +.tag-success { + background: rgba(52, 199, 89, 0.1); + color: var(--success); } -.common-tag.warning { - background: rgba(250, 173, 20, 0.1); - color: var(--warning-color); +.tag-warning { + background: rgba(255, 149, 0, 0.1); + color: var(--warning); } -.common-tag.error { - background: rgba(255, 77, 79, 0.1); - color: var(--error-color); +.tag-error { + background: rgba(255, 59, 48, 0.1); + color: var(--error); } -/* 空状态 */ +.tag-neutral { + background: rgba(120, 120, 128, 0.1); + color: var(--text-secondary); +} + +/* ========== 空状态 ========== */ + .empty-state { display: flex; flex-direction: column; align-items: center; justify-content: center; - padding: var(--spacing-2xl) 0; + padding: var(--spacing-4xl) var(--spacing-lg); + text-align: center; } -.empty-state .empty-icon { - font-size: 120px; +.empty-state-icon { + font-size: 128rpx; margin-bottom: var(--spacing-lg); - opacity: 0.5; + opacity: 0.4; + filter: grayscale(100%); } -.empty-state .empty-text { - font-size: var(--font-size-lg); +.empty-state-title { + font-size: var(--font-size-title3); + font-weight: var(--font-weight-semibold); + color: var(--text-primary); + margin-bottom: var(--spacing-sm); +} + +.empty-state-desc { + font-size: var(--font-size-subbody); color: var(--text-tertiary); - font-weight: var(--font-weight-medium); - margin-bottom: var(--spacing-xs); + max-width: 560rpx; + line-height: var(--line-height-relaxed); } -.empty-state .empty-hint { - font-size: var(--font-size-sm); - color: var(--text-placeholder); +.empty-state-action { + margin-top: var(--spacing-xl); } -/* 加载状态 */ +/* ========== 加载状态 ========== */ + .loading-state { display: flex; + flex-direction: column; align-items: center; justify-content: center; - padding: var(--spacing-2xl) 0; + padding: var(--spacing-4xl) 0; } .loading-spinner { - width: 40px; - height: 40px; - border: 3px solid var(--border-light); - border-top-color: var(--primary-color); + width: 64rpx; + height: 64rpx; + border: 5rpx solid var(--border-primary); + border-top-color: var(--primary); border-radius: 50%; animation: spin 0.8s linear infinite; } -@keyframes spin { - to { - transform: rotate(360deg); - } +.loading-text { + margin-top: var(--spacing-md); + font-size: var(--font-size-callout); + color: var(--text-tertiary); } -/* 分割线 */ +@keyframes spin { + to { transform: rotate(360deg); } +} + +.skeleton { + background: linear-gradient(90deg, + var(--border-light) 25%, + var(--bg-hover) 50%, + var(--border-light) 75%); + background-size: 200% 100%; + animation: skeleton-loading 1.5s infinite; + border-radius: var(--radius-sm); +} + +@keyframes skeleton-loading { + 0% { background-position: 200% 0; } + 100% { background-position: -200% 0; } +} + +/* ========== 分割线 ========== */ + .divider { display: flex; align-items: center; - margin: var(--spacing-lg) 0; + margin: var(--spacing-md) 0; } .divider-line { flex: 1; height: 1px; - background: var(--border-primary); + background: var(--border-separator); } .divider-text { padding: 0 var(--spacing-md); - font-size: var(--font-size-xs); + font-size: var(--font-size-footnote); color: var(--text-tertiary); + white-space: nowrap; } -/* 文本样式 */ -.text-primary { - color: var(--text-primary); +.divider-simple { + height: 1px; + background: var(--border-separator); + margin: var(--spacing-md) 0; } -.text-secondary { - color: var(--text-secondary); -} +/* ========== 文本样式 - 大字体 ========== */ -.text-tertiary { - color: var(--text-tertiary); -} - -.text-placeholder { - color: var(--text-placeholder); -} - -.text-brand { - color: var(--primary-color); -} - -.text-success { - color: var(--success-color); -} - -.text-warning { - color: var(--warning-color); -} - -.text-error { - color: var(--error-color); -} - -/* 字体大小 */ -.text-xs { - font-size: var(--font-size-xs); -} - -.text-sm { - font-size: var(--font-size-sm); -} - -.text-base { - font-size: var(--font-size-base); -} - -.text-md { - font-size: var(--font-size-md); -} - -.text-lg { - font-size: var(--font-size-lg); -} - -.text-xl { - font-size: var(--font-size-xl); -} - -/* 字体粗细 */ -.font-normal { - font-weight: var(--font-weight-normal); -} - -.font-medium { - font-weight: var(--font-weight-medium); -} - -.font-semibold { - font-weight: var(--font-weight-semibold); -} - -.font-bold { +.text-display { + font-size: var(--font-size-display); font-weight: var(--font-weight-bold); + letter-spacing: -0.5px; + line-height: var(--line-height-tight); } -/* 间距工具类 */ +.text-title1 { + font-size: var(--font-size-title1); + font-weight: var(--font-weight-bold); + letter-spacing: -0.3px; + line-height: var(--line-height-tight); +} + +.text-title2 { + font-size: var(--font-size-title2); + font-weight: var(--font-weight-semibold); + line-height: var(--line-height-normal); +} + +.text-title3 { + font-size: var(--font-size-title3); + font-weight: var(--font-weight-semibold); + line-height: var(--line-height-normal); +} + +.text-body { + font-size: var(--font-size-body); + font-weight: var(--font-weight-regular); + line-height: var(--line-height-normal); +} + +.text-callout { + font-size: var(--font-size-callout); + font-weight: var(--font-weight-regular); + line-height: var(--line-height-normal); +} + +.text-subbody { + font-size: var(--font-size-subbody); + font-weight: var(--font-weight-regular); + line-height: var(--line-height-normal); +} + +.text-footnote { + font-size: var(--font-size-footnote); + font-weight: var(--font-weight-regular); + line-height: var(--line-height-normal); +} + +.text-caption { + font-size: var(--font-size-caption1); + font-weight: var(--font-weight-regular); + line-height: var(--line-height-normal); + text-transform: uppercase; + letter-spacing: 0.5px; +} + +.text-primary { color: var(--text-primary); } +.text-secondary { color: var(--text-secondary); } +.text-tertiary { color: var(--text-tertiary); } +.text-placeholder { color: var(--text-placeholder); } + +.text-brand { color: var(--primary); } +.text-success { color: var(--success); } +.text-warning { color: var(--warning); } +.text-error { color: var(--error); } + +.text-left { text-align: left; } +.text-center { text-align: center; } +.text-right { text-align: right; } + +.truncate { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.truncate-2 { + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; +} + +/* ========== 间距工具类 ========== */ + +.m-0 { margin: 0; } +.m-xs { margin: var(--spacing-xs); } +.m-sm { margin: var(--spacing-sm); } +.m-md { margin: var(--spacing-md); } +.m-lg { margin: var(--spacing-lg); } +.m-xl { margin: var(--spacing-xl); } + +.mt-0 { margin-top: 0; } .mt-xs { margin-top: var(--spacing-xs); } .mt-sm { margin-top: var(--spacing-sm); } .mt-md { margin-top: var(--spacing-md); } .mt-lg { margin-top: var(--spacing-lg); } .mt-xl { margin-top: var(--spacing-xl); } +.mb-0 { margin-bottom: 0; } .mb-xs { margin-bottom: var(--spacing-xs); } .mb-sm { margin-bottom: var(--spacing-sm); } .mb-md { margin-bottom: var(--spacing-md); } .mb-lg { margin-bottom: var(--spacing-lg); } .mb-xl { margin-bottom: var(--spacing-xl); } +.ml-0 { margin-left: 0; } +.ml-xs { margin-left: var(--spacing-xs); } +.ml-sm { margin-left: var(--spacing-sm); } +.ml-md { margin-left: var(--spacing-md); } +.ml-lg { margin-left: var(--spacing-lg); } +.ml-xl { margin-left: var(--spacing-xl); } + +.mr-0 { margin-right: 0; } +.mr-xs { margin-right: var(--spacing-xs); } +.mr-sm { margin-right: var(--spacing-sm); } +.mr-md { margin-right: var(--spacing-md); } +.mr-lg { margin-right: var(--spacing-lg); } +.mr-xl { margin-right: var(--spacing-xl); } + +.p-0 { padding: 0; } .p-xs { padding: var(--spacing-xs); } .p-sm { padding: var(--spacing-sm); } .p-md { padding: var(--spacing-md); } .p-lg { padding: var(--spacing-lg); } .p-xl { padding: var(--spacing-xl); } -/* Flex布局 */ -.flex { - display: flex; -} +.px-0 { padding-left: 0; padding-right: 0; } +.px-xs { padding-left: var(--spacing-xs); padding-right: var(--spacing-xs); } +.px-sm { padding-left: var(--spacing-sm); padding-right: var(--spacing-sm); } +.px-md { padding-left: var(--spacing-md); padding-right: var(--spacing-md); } +.px-lg { padding-left: var(--spacing-lg); padding-right: var(--spacing-lg); } +.px-xl { padding-left: var(--spacing-xl); padding-right: var(--spacing-xl); } + +.py-0 { padding-top: 0; padding-bottom: 0; } +.py-xs { padding-top: var(--spacing-xs); padding-bottom: var(--spacing-xs); } +.py-sm { padding-top: var(--spacing-sm); padding-bottom: var(--spacing-sm); } +.py-md { padding-top: var(--spacing-md); padding-bottom: var(--spacing-md); } +.py-lg { padding-top: var(--spacing-lg); padding-bottom: var(--spacing-lg); } +.py-xl { padding-top: var(--spacing-xl); padding-bottom: var(--spacing-xl); } + +/* ========== Flex 布局 ========== */ + +.flex { display: flex; } +.flex-wrap { flex-wrap: wrap; } +.flex-nowrap { flex-wrap: nowrap; } +.flex-col { flex-direction: column; } .flex-center { display: flex; @@ -302,24 +582,135 @@ page { justify-content: space-between; } -.flex-column { +.flex-around { display: flex; - flex-direction: column; + align-items: center; + justify-content: space-around; } -.flex-1 { - flex: 1; +.flex-end { + display: flex; + align-items: center; + justify-content: flex-end; } -/* 截断文本 */ -.truncate { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; +.flex-1 { flex: 1; } +.flex-2 { flex: 2; } +.flex-3 { flex: 3; } + +.gap-xs { gap: var(--spacing-xs); } +.gap-sm { gap: var(--spacing-sm); } +.gap-md { gap: var(--spacing-md); } +.gap-lg { gap: var(--spacing-lg); } +.gap-xl { gap: var(--spacing-xl); } + +/* ========== 显示控制 ========== */ + +.hidden { display: none !important; } +.block { display: block; } +.inline-block { display: inline-block; } +.inline { display: inline; } + +.relative { position: relative; } +.absolute { position: absolute; } +.fixed { position: fixed; } +.sticky { position: sticky; } + +.overflow-hidden { overflow: hidden; } +.overflow-auto { overflow: auto; } +.overflow-scroll { overflow: scroll; } + +.rounded-sm { border-radius: var(--radius-sm); } +.rounded-md { border-radius: var(--radius-md); } +.rounded-lg { border-radius: var(--radius-lg); } +.rounded-xl { border-radius: var(--radius-xl); } +.rounded-full { border-radius: var(--radius-full); } + +.shadow-sm { box-shadow: var(--shadow-sm); } +.shadow-md { box-shadow: var(--shadow-md); } +.shadow-lg { box-shadow: var(--shadow-lg); } +.shadow-xl { box-shadow: var(--shadow-xl); } + +.cursor-pointer { cursor: pointer; } +.cursor-default { cursor: default; } +.cursor-not-allowed { cursor: not-allowed; } + +.select-none { + -webkit-user-select: none; + user-select: none; +} + +/* ========== 安全区域 ========== */ + +.safe-area-top { + padding-top: constant(safe-area-inset-top); + padding-top: env(safe-area-inset-top); } -/* 安全区域 */ .safe-area-bottom { padding-bottom: constant(safe-area-inset-bottom); padding-bottom: env(safe-area-inset-bottom); -} \ No newline at end of file +} + +.safe-area-left { + padding-left: constant(safe-area-inset-left); + padding-left: env(safe-area-inset-left); +} + +.safe-area-right { + padding-right: constant(safe-area-inset-right); + padding-right: env(safe-area-inset-right); +} + +/* ========== 其他工具类 ========== */ + +.avatar { + width: 80rpx; + height: 80rpx; + border-radius: var(--radius-full); + object-fit: cover; + border: 4rpx solid var(--bg-card); + box-shadow: var(--shadow-sm); +} + +.avatar-sm { width: 64rpx; height: 64rpx; } +.avatar-lg { width: 112rpx; height: 112rpx; } +.avatar-xl { width: 160rpx; height: 160rpx; } + +/* ========== 滚动条美化 ========== */ + +::-webkit-scrollbar { + width: 8rpx; + height: 8rpx; +} + +::-webkit-scrollbar-track { + background: transparent; +} + +::-webkit-scrollbar-thumb { + background: var(--border-primary); + border-radius: 4rpx; +} + +::-webkit-scrollbar-thumb:hover { + background: var(--text-tertiary); +} + +::selection { + background: var(--primary-opacity-30); + color: var(--text-primary); +} + +:focus { outline: none; } + +:focus-visible { + outline: 4rpx solid var(--primary); + outline-offset: 4rpx; +} + +img { + max-width: 100%; + height: auto; + display: block; +} diff --git a/src/styles/variables.css b/src/styles/variables.css index e465682..3d213b2 100644 --- a/src/styles/variables.css +++ b/src/styles/variables.css @@ -1,151 +1,310 @@ /* ============================================ - 全局主题变量 - 爱印通设计系统 + 全局主题变量 - 爱印通 Apple Liquid Glass 设计系统 + 采用苹果设计语言:半透明毛玻璃、圆润悬浮控件、动态光影折射 + 针对微信小程序优化:使用 rpx 单位,大字体大控件 ============================================ */ :root { - /* 主色调 - 科技蓝 */ - --primary-color: #5a93df; - --primary-light: #7db3f5; - --primary-dark: #4a7bc0; - --primary-gradient: linear-gradient(135deg, #7db3f5 0%, #5a93df 50%, #4a7bc0 100%); - --primary-gradient-light: linear-gradient(135deg, #8ec5f8 0%, #7db3f5 100%); + /* ========== 苹果风格主色调 ========== */ + /* 主色 - 采用苹果系统蓝,更加清新通透 */ + --primary: #007AFF; + --primary-light: #5AC8FA; + --primary-dark: #0055D4; + --primary-opacity-10: rgba(0, 122, 255, 0.1); + --primary-opacity-20: rgba(0, 122, 255, 0.2); + --primary-opacity-30: rgba(0, 122, 255, 0.3); - /* 辅助色 */ - --success-color: #52c41a; - --success-light: #73d13d; - --warning-color: #faad14; - --warning-light: #ffc53d; - --error-color: #ff4d4f; - --error-light: #ff7875; - --info-color: #1890ff; + /* 主色渐变 - 液态玻璃质感 */ + --primary-gradient: linear-gradient(135deg, #5AC8FA 0%, #007AFF 50%, #0055D4 100%); + --primary-gradient-subtle: linear-gradient(135deg, rgba(90, 200, 250, 0.15) 0%, rgba(0, 122, 255, 0.15) 100%); + --primary-gradient-glass: linear-gradient(135deg, rgba(90, 200, 250, 0.4) 0%, rgba(0, 122, 255, 0.4) 100%); - /* 中性色 */ - --text-primary: #1a1a2e; - --text-secondary: #555566; - --text-tertiary: #888899; - --text-placeholder: #b0b0c0; + /* ========== 语义化辅助色 ========== */ + --success: #34C759; + --success-light: #30D158; + --warning: #FF9500; + --warning-light: #FFB800; + --error: #FF3B30; + --error-light: #FF453A; + --info: #5856D6; + + /* ========== 中性色 - 苹果风格文本层级 ========== */ + --text-primary: rgba(0, 0, 0, 0.85); + --text-secondary: rgba(0, 0, 0, 0.55); + --text-tertiary: rgba(0, 0, 0, 0.35); + --text-placeholder: rgba(0, 0, 0, 0.25); --text-inverse: #ffffff; + --text-inverse-secondary: rgba(255, 255, 255, 0.7); - /* 背景色 */ - --bg-page: #f5f7fa; - --bg-card: #ffffff; - --bg-hover: #f0f4f8; - --bg-overlay: rgba(0, 0, 0, 0.45); + /* ========== 背景色系统 - 丰富渐变 ========== */ + --bg-page: #F2F2F7; + --bg-page-gradient: linear-gradient(180deg, #E8F0FE 0%, #F2F2F7 40%, #E8E8ED 100%); + --bg-card: rgba(255, 255, 255, 0.85); + --bg-card-solid: #ffffff; + --bg-hover: rgba(0, 122, 255, 0.05); + --bg-overlay: rgba(0, 0, 0, 0.4); + --bg-glass: rgba(255, 255, 255, 0.72); + --bg-glass-heavy: rgba(255, 255, 255, 0.85); - /* 边框色 */ - --border-primary: #e5e8f0; - --border-light: #f0f2f7; - --border-focus: #7db3f5; + /* ========== 边框色 - 极细致线 ========== */ + --border-primary: rgba(0, 0, 0, 0.1); + --border-light: rgba(0, 0, 0, 0.05); + --border-glass: rgba(255, 255, 255, 0.5); + --border-focus: var(--primary); + --border-separator: rgba(60, 60, 67, 0.12); - /* 阴影 */ - --shadow-sm: 0 2px 8px rgba(90, 147, 223, 0.06); - --shadow-md: 0 4px 16px rgba(90, 147, 223, 0.1); - --shadow-lg: 0 8px 32px rgba(90, 147, 223, 0.15); - --shadow-xl: 0 12px 48px rgba(90, 147, 223, 0.2); + /* ========== 阴影系统 - 多层柔和阴影营造漂浮感 ========== */ + --shadow-ambient: 0 2rpx 6rpx rgba(0, 0, 0, 0.04); + --shadow-sm: 0 4rpx 16rpx rgba(0, 0, 0, 0.06), 0 2rpx 4rpx rgba(0, 0, 0, 0.04); + --shadow-md: 0 8rpx 32rpx rgba(0, 0, 0, 0.08), 0 4rpx 8rpx rgba(0, 0, 0, 0.04); + --shadow-lg: 0 16rpx 64rpx rgba(0, 0, 0, 0.1), 0 8rpx 16rpx rgba(0, 0, 0, 0.05); + --shadow-xl: 0 24rpx 96rpx rgba(0, 0, 0, 0.12), 0 12rpx 32rpx rgba(0, 0, 0, 0.06); + --shadow-glass: 0 16rpx 64rpx rgba(0, 122, 255, 0.08), 0 4rpx 16rpx rgba(0, 0, 0, 0.06); + --shadow-pressed: 0 4rpx 8rpx rgba(0, 0, 0, 0.06), 0 2rpx 4rpx rgba(0, 0, 0, 0.04); - /* 圆角 */ - --radius-xs: 8px; - --radius-sm: 12px; - --radius-md: 16px; - --radius-lg: 20px; - --radius-xl: 24px; - --radius-full: 50%; + /* ========== 圆角系统 - 大圆角,苹果风格 ========== */ + --radius-sm: 24rpx; + --radius-md: 32rpx; + --radius-lg: 40rpx; + --radius-xl: 48rpx; + --radius-2xl: 56rpx; + --radius-full: 9999rpx; - /* 间距 */ - --spacing-xs: 8px; - --spacing-sm: 12px; - --spacing-md: 16px; - --spacing-lg: 24px; - --spacing-xl: 32px; - --spacing-2xl: 48px; + /* ========== 间距系统 - 基于 8px(16rpx) 栅格 ========== */ + --spacing-xs: 8rpx; + --spacing-sm: 16rpx; + --spacing-md: 24rpx; + --spacing-lg: 32rpx; + --spacing-xl: 40rpx; + --spacing-2xl: 48rpx; + --spacing-3xl: 64rpx; + --spacing-4xl: 96rpx; - /* 字体 */ - --font-size-xs: 20px; - --font-size-sm: 22px; - --font-size-base: 24px; - --font-size-md: 26px; - --font-size-lg: 28px; - --font-size-xl: 30px; - --font-size-2xl: 34px; - --font-size-3xl: 40px; - --font-size-4xl: 48px; + /* ========== 字体系统 - 微信小程序 rpx 单位,大字体 ========== */ + --font-size-display: 68rpx; + --font-size-title1: 56rpx; + --font-size-title2: 44rpx; + --font-size-title3: 40rpx; + --font-size-body: 34rpx; + --font-size-callout: 32rpx; + --font-size-subbody: 30rpx; + --font-size-footnote: 26rpx; + --font-size-caption1: 24rpx; + --font-size-caption2: 22rpx; - --font-weight-normal: 400; + --font-weight-regular: 400; --font-weight-medium: 500; --font-weight-semibold: 600; --font-weight-bold: 700; - /* 动画 */ - --transition-fast: 0.15s ease; - --transition-base: 0.25s ease; - --transition-slow: 0.4s ease; + --line-height-tight: 1.2; + --line-height-normal: 1.4; + --line-height-relaxed: 1.6; - /* 模糊 */ + /* ========== 动画系统 - 0.3s 缓动曲线 ========== */ + --transition-instant: 0.15s cubic-bezier(0.25, 0.1, 0.25, 1); + --transition-fast: 0.2s cubic-bezier(0.25, 0.1, 0.25, 1); + --transition-base: 0.3s cubic-bezier(0.25, 0.1, 0.25, 1); + --transition-slow: 0.5s cubic-bezier(0.25, 0.1, 0.25, 1); + + --transition-bounce: 0.3s cubic-bezier(0.34, 1.56, 0.64, 1); + --transition-scale: 0.2s cubic-bezier(0.4, 0, 0.2, 1); + + /* ========== 模糊效果 - 毛玻璃核心 ========== */ --blur-sm: 8px; --blur-md: 16px; --blur-lg: 24px; + --blur-xl: 32px; + --blur-2xl: 48px; + + /* ========== 液态玻璃材质 ========== */ + --glass-background: rgba(255, 255, 255, 0.72); + --glass-background-heavy: rgba(255, 255, 255, 0.85); + --glass-background-light: rgba(255, 255, 255, 0.5); + --glass-border: rgba(255, 255, 255, 0.5); + --glass-shadow: 0 16rpx 64rpx rgba(0, 0, 0, 0.08); + --glass-blur: blur(24px); + --glass-saturate: saturate(180%); } -/* 页面背景渐变 */ -.bg-gradient-page { - background: linear-gradient(180deg, #eef5ff 0%, #f5f7fa 50%, #fafbfc 100%); +/* ========== 深色模式 ========== */ +@media (prefers-color-scheme: dark) { + :root { + --primary: #0A84FF; + --primary-light: #409CFF; + --primary-dark: #0065C8; + + --text-primary: rgba(255, 255, 255, 0.9); + --text-secondary: rgba(255, 255, 255, 0.6); + --text-tertiary: rgba(255, 255, 255, 0.4); + --text-placeholder: rgba(255, 255, 255, 0.3); + --text-inverse: #000000; + --text-inverse-secondary: rgba(0, 0, 0, 0.7); + + --bg-page: #000000; + --bg-page-gradient: linear-gradient(180deg, #1C1C1E 0%, #000000 100%); + --bg-card: rgba(28, 28, 30, 0.85); + --bg-card-solid: #1C1C1E; + --bg-hover: rgba(255, 255, 255, 0.05); + --bg-overlay: rgba(0, 0, 0, 0.6); + --bg-glass: rgba(28, 28, 30, 0.72); + --bg-glass-heavy: rgba(28, 28, 30, 0.85); + + --border-primary: rgba(255, 255, 255, 0.1); + --border-light: rgba(255, 255, 255, 0.05); + --border-glass: rgba(255, 255, 255, 0.2); + --border-separator: rgba(84, 84, 88, 0.65); + + --shadow-sm: 0 4rpx 16rpx rgba(0, 0, 0, 0.3), 0 2rpx 4rpx rgba(0, 0, 0, 0.2); + --shadow-md: 0 8rpx 32rpx rgba(0, 0, 0, 0.35), 0 4rpx 8rpx rgba(0, 0, 0, 0.25); + --shadow-lg: 0 16rpx 64rpx rgba(0, 0, 0, 0.4), 0 8rpx 16rpx rgba(0, 0, 0, 0.3); + --shadow-xl: 0 24rpx 96rpx rgba(0, 0, 0, 0.45), 0 12rpx 32rpx rgba(0, 0, 0, 0.35); + --shadow-glass: 0 16rpx 64rpx rgba(0, 132, 255, 0.1), 0 4rpx 16rpx rgba(0, 0, 0, 0.3); + + --glass-background: rgba(28, 28, 30, 0.72); + --glass-background-heavy: rgba(28, 28, 30, 0.85); + --glass-background-light: rgba(28, 28, 30, 0.5); + --glass-border: rgba(255, 255, 255, 0.15); + } } -/* 主色渐变背景 */ -.bg-gradient-primary { - background: var(--primary-gradient); +/* ========== 实用工具类 ========== */ + +/* 液态玻璃效果 - 核心材质 */ +.liquid-glass { + background: var(--glass-background); + backdrop-filter: var(--glass-blur); + -webkit-backdrop-filter: var(--glass-blur); + backdrop-filter: var(--glass-blur) var(--glass-saturate); + -webkit-backdrop-filter: var(--glass-blur) var(--glass-saturate); + border: 1px solid var(--glass-border); + box-shadow: var(--shadow-glass); } -/* 玻璃态效果 */ -.glass-effect { - background: rgba(255, 255, 255, 0.85); +.liquid-glass-heavy { + background: var(--glass-background-heavy); + backdrop-filter: var(--glass-blur); + -webkit-backdrop-filter: var(--glass-blur); + backdrop-filter: var(--glass-blur) var(--glass-saturate); + -webkit-backdrop-filter: var(--glass-blur) var(--glass-saturate); + border: 1px solid var(--glass-border); + box-shadow: var(--shadow-lg); +} + +.liquid-glass-light { + background: var(--glass-background-light); + backdrop-filter: blur(16px); + -webkit-backdrop-filter: blur(16px); + border: 1px solid var(--glass-border); + box-shadow: var(--shadow-sm); +} + +.bg-page-gradient { + background: var(--bg-page-gradient); +} + +.glass-card { + background: var(--bg-card); backdrop-filter: blur(20px); -webkit-backdrop-filter: blur(20px); - border: 1px solid rgba(255, 255, 255, 0.3); + border-radius: var(--radius-lg); + border: 1px solid var(--border-glass); + box-shadow: var(--shadow-md); } -/* 卡片基础样式 */ -.card-base { - background: var(--bg-card); +.floating-card { + background: var(--bg-card-solid); border-radius: var(--radius-lg); - box-shadow: var(--shadow-md); + box-shadow: var(--shadow-lg); border: 1px solid var(--border-light); } -/* 按钮基础样式 */ +/* 主按钮 - 填充样式 */ .btn-primary { - background: var(--primary-gradient); + background: var(--primary); color: var(--text-inverse); border: none; border-radius: var(--radius-md); font-weight: var(--font-weight-medium); - transition: all var(--transition-base); + transition: all var(--transition-scale); + box-shadow: 0 4rpx 16rpx rgba(0, 122, 255, 0.2); } .btn-primary:active { - transform: scale(0.97); + transform: scale(0.98); opacity: 0.9; + box-shadow: var(--shadow-pressed); } -/* 输入框基础样式 */ -.input-base { - background: #f8f9fb; +/* 次要按钮 - 描边样式 */ +.btn-secondary { + background: transparent; + color: var(--primary); + border: 3rpx solid var(--primary); + border-radius: var(--radius-md); + font-weight: var(--font-weight-medium); + transition: all var(--transition-scale); +} + +.btn-secondary:active { + transform: scale(0.98); + background: var(--primary-opacity-10); +} + +/* 幽灵按钮 - 极简样式 */ +.btn-ghost { + background: var(--primary-opacity-10); + color: var(--primary); + border: none; + border-radius: var(--radius-md); + font-weight: var(--font-weight-medium); + transition: all var(--transition-scale); +} + +.btn-ghost:active { + transform: scale(0.98); + background: var(--primary-opacity-20); +} + +/* 输入框 - 液态玻璃风格 */ +.input-glass { + background: var(--glass-background); + backdrop-filter: blur(16px); + -webkit-backdrop-filter: blur(16px); border: 1px solid var(--border-primary); - border-radius: var(--radius-sm); + border-radius: var(--radius-md); transition: all var(--transition-base); } -.input-base:focus { - border-color: var(--border-focus); - background: #fff; - box-shadow: 0 0 0 3px rgba(90, 147, 223, 0.1); +.input-glass:focus { + border-color: var(--primary); + background: var(--bg-card-solid); + box-shadow: 0 0 0 6rpx var(--primary-opacity-20); } -/* 动画关键帧 */ -@keyframes fadeIn { +/* 导航栏 - 自适应液态玻璃 */ +.nav-glass { + background: var(--glass-background-heavy); + backdrop-filter: blur(24px) saturate(180%); + -webkit-backdrop-filter: blur(24px) saturate(180%); + border-bottom: 1px solid var(--border-separator); + transition: background var(--transition-base); +} + +/* 侧边栏 - 半透明毛玻璃 */ +.sidebar-glass { + background: var(--glass-background); + backdrop-filter: blur(24px); + -webkit-backdrop-filter: blur(24px); + border-right: 1px solid var(--border-separator); +} + +/* ========== 动画关键帧 ========== */ + +@keyframes fadeInUp { from { opacity: 0; - transform: translateY(10px); + transform: translateY(40rpx); } to { opacity: 1; @@ -153,44 +312,64 @@ } } -@keyframes slideUp { +@keyframes scaleFadeIn { from { opacity: 0; - transform: translateY(20px); + transform: scale(0.95); } to { opacity: 1; - transform: translateY(0); - } -} - -@keyframes pulse { - 0%, 100% { transform: scale(1); } - 50% { - transform: scale(1.05); - } } -@keyframes shimmer { +@keyframes glassShimmer { 0% { - background-position: -200% 0; + background-position: -200% center; } 100% { - background-position: 200% 0; + background-position: 200% center; } } -/* 动画类 */ -.animate-fade-in { - animation: fadeIn 0.4s ease forwards; +@keyframes float { + 0%, 100% { + transform: translateY(0); + } + 50% { + transform: translateY(-8rpx); + } } -.animate-slide-up { - animation: slideUp 0.5s ease forwards; +@keyframes pulseGlow { + 0%, 100% { + box-shadow: 0 0 0 0 rgba(0, 122, 255, 0.3); + } + 50% { + box-shadow: 0 0 0 16rpx rgba(0, 122, 255, 0); + } } -.animate-pulse { - animation: pulse 2s ease infinite; -} \ No newline at end of file +/* ========== 动画工具类 ========== */ + +.animate-fade-in-up { + animation: fadeInUp 0.4s cubic-bezier(0.25, 0.1, 0.25, 1) forwards; +} + +.animate-scale-fade-in { + animation: scaleFadeIn 0.3s cubic-bezier(0.25, 0.1, 0.25, 1) forwards; +} + +.animate-float { + animation: float 3s cubic-bezier(0.4, 0, 0.2, 1) infinite; +} + +.animate-pulse-glow { + animation: pulseGlow 2s cubic-bezier(0.4, 0, 0.6, 1) infinite; +} + +.animate-delay-100 { animation-delay: 0.1s; } +.animate-delay-200 { animation-delay: 0.2s; } +.animate-delay-300 { animation-delay: 0.3s; } +.animate-delay-400 { animation-delay: 0.4s; } +.animate-delay-500 { animation-delay: 0.5s; }