From dd12d8e500c50baa4a02ace9e8ee5c3ccab014d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=82=B9=E6=96=B9=E6=88=90?= Date: Wed, 17 Dec 2025 01:55:23 +0800 Subject: [PATCH] feat: add app sharing and enhance UI with animations and revamped coupon display. --- api/appUser.js | 21 +- pages/activity/list/index.vue | 97 +++ pages/cabinet/index.vue | 142 ++++ pages/index/index.vue | 196 +++++ pages/mine/index.vue | 1462 +++++++++++++++++++++++++++++++-- pages/shop/index.vue | 126 +-- 6 files changed, 1856 insertions(+), 188 deletions(-) diff --git a/api/appUser.js b/api/appUser.js index 259d647..724bc6f 100644 --- a/api/appUser.js +++ b/api/appUser.js @@ -5,8 +5,8 @@ export function wechatLogin(code, invite_code) { return request({ url: '/api/app/users/weixin/login', method: 'POST', data }) } -export function getInventory(user_id, page = 1, page_size = 20){ - return authRequest({ url: `/api/app/users/${user_id}/inventory`, method: 'GET', data: { page, page_size } }) +export function getInventory(user_id, page = 1, page_size = 20) { + return authRequest({ url: `/api/app/users/${user_id}/inventory`, method: 'GET', data: { page, page_size } }) } export function bindPhone(user_id, code, extraHeader = {}) { @@ -99,6 +99,18 @@ export function getUserCoupons(user_id, status, page = 1, page_size = 20) { return authRequest({ url: `/api/app/users/${user_id}/coupons`, method: 'GET', data }) } +export function getCouponStats(user_id) { + return authRequest({ url: `/api/app/users/${user_id}/coupons/stats`, method: 'GET' }) +} + +export function getCouponUsage(user_id, user_coupon_id, page = 1, page_size = 20) { + return authRequest({ url: `/api/app/users/${user_id}/coupons/${user_coupon_id}/usage`, method: 'GET', data: { page, page_size } }) +} + +export function redeemCouponByPoints(user_id, coupon_id) { + return authRequest({ url: `/api/app/users/${user_id}/points/redeem-coupon`, method: 'POST', data: { coupon_id } }) +} + export function redeemCoupon(user_id, code) { return authRequest({ url: `/api/app/users/${user_id}/coupons/redeem`, method: 'POST', data: { code } }) } @@ -130,3 +142,8 @@ export function getTasks(page = 1, page_size = 20) { export function getShipments(user_id, page = 1, page_size = 20) { return authRequest({ url: `/api/app/users/${user_id}/shipments`, method: 'GET', data: { page, page_size } }) } + +// 获取用户邀请记录 +export function getUserInvites(user_id, page = 1, page_size = 20) { + return authRequest({ url: `/api/app/users/${user_id}/invites`, method: 'GET', data: { page, page_size } }) +} diff --git a/pages/activity/list/index.vue b/pages/activity/list/index.vue index 67cfed8..4775eb0 100644 --- a/pages/activity/list/index.vue +++ b/pages/activity/list/index.vue @@ -116,6 +116,27 @@ onLoad((opts) => { } loadData() }) + +// 分享功能 +import { onShareAppMessage, onShareTimeline } from '@dcloudio/uni-app' + +onShareAppMessage(() => { + const inviteCode = uni.getStorageSync('invite_code') || (uni.getStorageSync('user_info') || {}).invite_code || '' + return { + title: `${title.value || '精彩活动'} - 奇盒潮玩`, + path: `/pages/index/index?invite_code=${inviteCode}`, + imageUrl: '/static/logo.png' + } +}) + +onShareTimeline(() => { + const inviteCode = uni.getStorageSync('invite_code') || (uni.getStorageSync('user_info') || {}).invite_code || '' + return { + title: `${title.value || '精彩活动'} - 奇盒潮玩`, + query: `invite_code=${inviteCode}`, + imageUrl: '/static/logo.png' + } +}) diff --git a/pages/cabinet/index.vue b/pages/cabinet/index.vue index 9b682fb..abc09cb 100644 --- a/pages/cabinet/index.vue +++ b/pages/cabinet/index.vue @@ -1189,4 +1189,146 @@ async function onShip() { font-size: 22rpx; color: #9CA3AF; } + +/* ============================================ + 盒柜 UX/UI 增强 + ============================================ */ + +/* 入场动画 */ +@keyframes fadeInUp { + from { opacity: 0; transform: translateY(30rpx); } + to { opacity: 1; transform: translateY(0); } +} + +@keyframes scaleIn { + from { opacity: 0; transform: scale(0.95); } + to { opacity: 1; transform: scale(1); } +} + +@keyframes slideUp { + from { opacity: 0; transform: translateY(100%); } + to { opacity: 1; transform: translateY(0); } +} + +@keyframes bounceIn { + 0% { transform: scale(0); } + 50% { transform: scale(1.2); } + 100% { transform: scale(1); } +} + +@keyframes pulse { + 0%, 100% { transform: scale(1); } + 50% { transform: scale(1.05); } +} + +/* Tab 切换动画 */ +.tabs { + animation: fadeInUp 0.3s ease-out; +} +.tab-item { + transition: all 0.25s ease; +} +.tab-item:active { + transform: scale(0.96); +} + +/* 库存卡片交错入场 */ +.inventory-item:nth-child(1) { animation: fadeInUp 0.4s ease-out 0.1s both; } +.inventory-item:nth-child(2) { animation: fadeInUp 0.4s ease-out 0.15s both; } +.inventory-item:nth-child(3) { animation: fadeInUp 0.4s ease-out 0.2s both; } +.inventory-item:nth-child(4) { animation: fadeInUp 0.4s ease-out 0.25s both; } +.inventory-item:nth-child(n+5) { animation: fadeInUp 0.4s ease-out 0.3s both; } + +/* 发货单卡片交错入场 */ +.shipment-card:nth-child(1) { animation: fadeInUp 0.4s ease-out 0.1s both; } +.shipment-card:nth-child(2) { animation: fadeInUp 0.4s ease-out 0.15s both; } +.shipment-card:nth-child(3) { animation: fadeInUp 0.4s ease-out 0.2s both; } +.shipment-card:nth-child(n+4) { animation: fadeInUp 0.4s ease-out 0.25s both; } + +/* 卡片点击效果 */ +.inventory-item { + transition: all 0.25s cubic-bezier(0.25, 0.46, 0.45, 0.94); +} +.inventory-item:active { + transform: scale(0.98); + box-shadow: 0 4rpx 16rpx rgba(0,0,0,0.08); +} + +/* 选中时的高亮效果 */ +.inventory-item:has(.checkbox.checked) { + box-shadow: 0 8rpx 32rpx rgba(255, 107, 53, 0.15); + border: 2rpx solid rgba(255, 159, 67, 0.3); +} + +/* 复选框动画 */ +.checkbox { + transition: all 0.2s ease; +} +.checkbox.checked { + animation: bounceIn 0.3s ease-out; +} + +/* 底部操作栏增强 */ +.bottom-bar { + animation: slideUp 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94); + backdrop-filter: blur(20rpx); + -webkit-backdrop-filter: blur(20rpx); + background: rgba(255, 255, 255, 0.95); +} + +/* 操作按钮动画 */ +.action-btn { + transition: all 0.2s ease; +} +.action-btn:active { + transform: scale(0.95); +} +.btn-redeem { + animation: pulse 2s infinite; +} +.btn-redeem:active { + animation: none; +} + +/* 步进器按钮效果 */ +.step-btn { + transition: all 0.15s ease; +} +.step-btn:active { + background: #FF9F43 !important; + color: #fff !important; + transform: scale(0.9); +} + +/* 全选栏入场 */ +.action-bar { + animation: fadeInUp 0.3s ease-out; +} + +/* 发货单卡片点击效果 */ +.shipment-card { + transition: all 0.25s ease; +} +.shipment-card:active { + transform: scale(0.98); + box-shadow: 0 4rpx 20rpx rgba(0,0,0,0.1); +} + +/* 复制按钮效果 */ +.express-copy { + transition: all 0.2s ease; +} +.express-copy:active { + background: #2563EB; + color: #fff; + transform: scale(0.95); +} + +/* 缩略图图片悬浮效果 */ +.thumb-img { + transition: all 0.2s ease; +} +.thumb-img:active { + transform: scale(1.05); +} diff --git a/pages/index/index.vue b/pages/index/index.vue index 17c7fc9..0fb8652 100644 --- a/pages/index/index.vue +++ b/pages/index/index.vue @@ -314,6 +314,24 @@ export default { confirmText: '知道了' }) } + }, + // 分享给好友 + onShareAppMessage() { + const inviteCode = uni.getStorageSync('invite_code') || (uni.getStorageSync('user_info') || {}).invite_code || '' + return { + title: '奇盒潮玩 - 开箱惊喜等你来', + path: `/pages/index/index?invite_code=${inviteCode}`, + imageUrl: '/static/logo.png' + } + }, + // 分享到朋友圈 + onShareTimeline() { + const inviteCode = uni.getStorageSync('invite_code') || (uni.getStorageSync('user_info') || {}).invite_code || '' + return { + title: '奇盒潮玩 - 开箱惊喜等你来', + query: `invite_code=${inviteCode}`, + imageUrl: '/static/logo.png' + } } } @@ -682,4 +700,182 @@ export default { color: #9CA3AF; font-size: 28rpx; } + +/* ============================================ + UX/UI 增强 - 动画与交互 + ============================================ */ + +/* 入场动画 */ +@keyframes fadeInUp { + from { + opacity: 0; + transform: translateY(30rpx); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +@keyframes scaleIn { + from { + opacity: 0; + transform: scale(0.95); + } + to { + opacity: 1; + transform: scale(1); + } +} + +@keyframes shimmer { + 0% { background-position: -200% 0; } + 100% { background-position: 200% 0; } +} + +@keyframes pulse { + 0%, 100% { transform: scale(1); } + 50% { transform: scale(1.05); } +} + +@keyframes float { + 0%, 100% { transform: translateY(0); } + 50% { transform: translateY(-8rpx); } +} + +/* Banner 入场动画 */ +.banner-box { + animation: scaleIn 0.5s ease-out; +} + +/* 通知栏滑入 */ +.notice-bar { + animation: fadeInUp 0.4s ease-out 0.1s both; +} + +/* 玩法卡片交错入场 */ +.game-card-large:first-child { + animation: fadeInUp 0.4s ease-out 0.2s both; +} +.game-card-large:last-child { + animation: fadeInUp 0.4s ease-out 0.3s both; +} +.game-card-small:nth-child(1) { + animation: fadeInUp 0.4s ease-out 0.35s both; +} +.game-card-small:nth-child(2) { + animation: fadeInUp 0.4s ease-out 0.4s both; +} +.game-card-small:nth-child(3) { + animation: fadeInUp 0.4s ease-out 0.45s both; +} + +/* 活动卡片交错入场 */ +.activity-item:nth-child(1) { animation: fadeInUp 0.4s ease-out 0.2s both; } +.activity-item:nth-child(2) { animation: fadeInUp 0.4s ease-out 0.25s both; } +.activity-item:nth-child(3) { animation: fadeInUp 0.4s ease-out 0.3s both; } +.activity-item:nth-child(4) { animation: fadeInUp 0.4s ease-out 0.35s both; } +.activity-item:nth-child(n+5) { animation: fadeInUp 0.4s ease-out 0.4s both; } + +/* 玻璃态搜索栏 */ +.search-bar { + background: rgba(255, 255, 255, 0.85); + backdrop-filter: blur(20rpx); + -webkit-backdrop-filter: blur(20rpx); + border: 1rpx solid rgba(255, 255, 255, 0.6); + box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.04); + transition: all 0.3s ease; +} +.search-bar:active { + transform: scale(0.98); + box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.08); +} + +/* 卡片点击效果 */ +.game-card-large, +.game-card-small { + transition: all 0.25s cubic-bezier(0.25, 0.46, 0.45, 0.94); + cursor: pointer; +} +.game-card-large:active { + transform: scale(0.97); + box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.1); +} +.game-card-small:active { + transform: scale(0.95); + box-shadow: 0 2rpx 6rpx rgba(0,0,0,0.08); +} + +/* 活动卡片悬浮效果 */ +.activity-item { + transition: all 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94); +} +.activity-item:active { + transform: scale(0.97) translateY(4rpx); + box-shadow: 0 4rpx 12rpx rgba(0,0,0,0.08); +} + +/* GO 按钮脉冲动画 */ +.activity-btn-go { + animation: pulse 2s infinite; + transition: all 0.2s ease; +} +.activity-btn-go:active { + animation: none; + transform: scale(0.9); +} + +/* 图片加载动画 */ +.activity-thumb { + background: linear-gradient(90deg, #f0f0f0 25%, #e8e8e8 50%, #f0f0f0 75%); + background-size: 200% 100%; + animation: shimmer 1.5s infinite; +} + +/* 热门标签浮动效果 */ +.activity-tag-hot { + animation: float 3s ease-in-out infinite; +} + +/* Banner 图片悬浮效果 */ +.banner-image { + transition: transform 0.4s ease; +} +.banner-box:active .banner-image { + transform: scale(1.02); +} + +/* 通知栏点击效果 */ +.notice-bar { + transition: all 0.2s ease; +} +.notice-bar:active { + background: #F0F0F0; + transform: scale(0.99); +} + +/* 品牌标志动画 */ +.brand-star { + animation: float 2s ease-in-out infinite; +} + +/* Section 标题增强 */ +.section-header { + animation: fadeInUp 0.4s ease-out 0.15s both; +} + +/* 增强阴影效果 */ +.banner-box { + box-shadow: 0 12rpx 40rpx rgba(0,0,0,0.08); +} +.activity-item { + box-shadow: 0 8rpx 32rpx rgba(0,0,0,0.06); +} + +/* 热门标签增强 */ +.activity-tag-hot { + background: linear-gradient(135deg, rgba(255,77,79,0.9), rgba(255,107,53,0.9)); + color: #fff; + text-shadow: 0 1rpx 2rpx rgba(0,0,0,0.2); +} diff --git a/pages/mine/index.vue b/pages/mine/index.vue index c5421f8..cd7e38b 100644 --- a/pages/mine/index.vue +++ b/pages/mine/index.vue @@ -92,8 +92,8 @@ 任务中心 - - + + 邀请记录 @@ -146,41 +146,67 @@ × - - 未使用 - 已使用 + + 未使用 + 已使用 + 已过期 加载中... 🎟️ - 暂无优惠券 + {{ getCouponEmptyText() }} - - - + + + + ¥ - {{ formatCouponValue(item.remaining || item.amount || 0) }} + {{ formatCouponValue(item.remaining ?? item.amount ?? 0) }} - 满{{ formatCouponValue(item.min_amount || 0) }}可用 + {{ couponsTab === 1 ? '可用' : (couponsTab === 2 ? '已用' : '过期') }} - - - - + + + + + + - - {{ item.name || '优惠券' }} - {{ item.rules || item.description || '全场通用' }} - 有效期至:{{ formatDate(item.valid_end || item.end_time) }} - - 去使用 + + + + + {{ item.name || '优惠券' }} + + 原值 ¥{{ formatCouponValue(item.amount) }} + - - {{ couponsTab === 1 ? '已使用' : '已过期' }} + + {{ item.rules || '全场通用' }} + + + + + + + 已用 {{ formatCouponValue(item.amount - item.remaining) }} ({{ getCouponUsedPercent(item) }}%) + + + {{ formatCouponExpiry(item) }} + + 去使用 + + + {{ couponsTab === 2 ? '已使用' : '已过期' }} + + + + + 使用时间:{{ formatDateTime(item.used_at) }} @@ -240,21 +266,25 @@ × - - - - {{ tasksList.filter(t => t.status === 2).length }} - 已完成 + + + + {{ getOverallProgress() }}% + 总完成率 - - - {{ tasksList.filter(t => t.status === 1).length }} - 进行中 - - - - {{ tasksList.length }} - 全部任务 + + + + 已完成 {{ tasksList.filter(t => t.status === 2).length }} 个 + + + + 进行中 {{ tasksList.filter(t => t.status === 1).length }} 个 + + + + 未开始 {{ tasksList.filter(t => t.status !== 1 && t.status !== 2).length }} 个 + @@ -265,40 +295,42 @@ 暂无任务 - - - {{ getTaskIcon(task.type || task.name) }} + + + + {{ getStatusIcon(task.status) }} - - - {{ task.name || '任务' }} - - {{ formatStatus(task.status) }} - + + + + {{ task.name || '任务' }} - {{ task.description || '完成任务获取奖励' }} + {{ task.description || '完成任务获取奖励' }} - - - - + + + + - {{ task.current || 0 }}/{{ task.target || 1 }} + {{ getTaskProgressV2(task) }}% - - - - {{ getRewardIcon(rw.reward_type) }} - +{{ rw.quantity || 0 }} + + + 奖励: + + + {{ getRewardIcon(rw.reward_type) }} {{ rw.quantity || 0 }} + - - 去完成 - 已完成 - 未开始 + + + @@ -308,13 +340,105 @@ + + + + + ✨ 我的邀请团 ✨ + + + + + + + + + + + + + + 我的邀请码 + + {{ inviteCode || '加载中...' }} + + 复制 + + + + + + + + + {{ invitesTotal }} + 累计邀请 (人) + + + + Lv.1 + 邀请等级 + + + + + + + + 好友列表 + 邀请好友,一起欧气满满 + + + + + + 正在加载好友... + + + + + 暂无邀请记录 + 每邀请一位好友,双方都能获得奖励哦 + + + + + + + + + {{ item.nickname || '神秘好友' }} + 新晋 + + ID: {{ item.id }} · 邀请码: {{ item.invite_code || '-' }} + + + + 已激活 + + + + 加载更多... + - 到底啦 - + + + + + + + + +