diff --git a/api/appUser.js b/api/appUser.js index 67abe45..259d647 100644 --- a/api/appUser.js +++ b/api/appUser.js @@ -87,8 +87,10 @@ export function requestShipping(user_id, ids) { return authRequest({ url: `/api/app/users/${user_id}/inventory/request-shipping`, method: 'POST', data: { inventory_ids: ids } }) } -export function getItemCards(user_id) { - return authRequest({ url: `/api/app/users/${user_id}/item_cards`, method: 'GET' }) +export function getItemCards(user_id, status) { + const data = {} + if (status !== undefined) data.status = status + return authRequest({ url: `/api/app/users/${user_id}/item_cards`, method: 'GET', data }) } export function getUserCoupons(user_id, status, page = 1, page_size = 20) { diff --git a/pages/cabinet/index.vue b/pages/cabinet/index.vue index 9975d85..9b682fb 100644 --- a/pages/cabinet/index.vue +++ b/pages/cabinet/index.vue @@ -62,20 +62,65 @@ 加载中... - 暂无已发货记录 + 暂无发货记录 - - - - - - {{ item.name || '未命名道具' }} - x{{ item.count || 1 }} - 已申请发货 - 快递:{{ item.express_code }} {{ item.express_no }} - 发货时间:{{ formatDate(item.shipped_at) }} - 签收时间:{{ formatDate(item.received_at) }} - + + + + + + 发货单 + {{ item.batch_no }} + {{ item.count }}件商品 + + + {{ getStatusText(item.status) }} + + + + + + + + + +{{ item.product_images.length - 4 }} + + + + + + {{ name }} + + + 等{{ item.product_names.length }}件商品 + + + + + + + + 📦 + + + {{ item.express_code || '待发货' }} + {{ item.express_no }} + + 复制 + + + + + 申请时间:{{ formatDate(item.created_at) }} + 发货时间:{{ formatDate(item.shipped_at) }} + 加载更多... @@ -217,6 +262,42 @@ function formatDate(dateStr) { return `${y}-${m}-${da} ${h}:${min}` } +// 发货状态辅助函数 +function getStatusClass(status) { + const statusMap = { + 1: 'status-pending', // 待发货 + 2: 'status-shipped', // 已发货 + 3: 'status-delivered', // 已签收 + 4: 'status-cancelled' // 已取消 + } + return statusMap[status] || 'status-pending' +} + +function getStatusText(status) { + const statusMap = { + 1: '待发货', + 2: '运输中', + 3: '已签收', + 4: '已取消' + } + return statusMap[status] || '待发货' +} + +function copyExpressNo(expressNo) { + uni.setClipboardData({ + data: expressNo, + success: () => { + uni.showToast({ title: '已复制单号', icon: 'success' }) + } + }) +} + +function onThumbError(shipmentIndex, imgIndex) { + if (shippedList.value[shipmentIndex] && shippedList.value[shipmentIndex].product_images) { + shippedList.value[shipmentIndex].product_images[imgIndex] = '/static/logo.png' + } +} + async function loadShipments(uid) { if (loading.value) return loading.value = true @@ -227,21 +308,51 @@ async function loadShipments(uid) { if (res && Array.isArray(res.list)) { list = res.list; total = res.total || 0 } else if (res && Array.isArray(res.data)) { list = res.data; total = res.total || 0 } else if (Array.isArray(res)) { list = res; total = res.length } - const mapped = list.map(s => ({ - image: '/static/logo.png', - name: '发货单', - count: s.count ?? (Array.isArray(s.inventory_ids) ? s.inventory_ids.length : 0), - express_code: s.express_code || '', - express_no: s.express_no || '', - shipped_at: s.shipped_at || '', - received_at: s.received_at || '', - status: s.status - })) + + // 处理每个发货单 - 直接使用返回的 products 数组 + const mapped = list.map(s => { + const products = s.products || [] + let productImages = [] + let productNames = [] + + // 从 products 数组中提取图片和名称 + products.forEach(product => { + if (product) { + // image 字段是 JSON 数组字符串,需要解析 + const rawImg = product.image || product.images || product.main_image + const img = cleanUrl(rawImg) + productImages.push(img) + productNames.push(product.name || product.title || '商品') + } + }) + + // 如果没有获取到任何图片,使用默认图片 + if (productImages.length === 0) { + productImages = ['/static/logo.png'] + productNames = ['未知商品'] + } + + return { + batch_no: s.batch_no || '', + count: s.count ?? (Array.isArray(s.inventory_ids) ? s.inventory_ids.length : 0), + product_ids: s.product_ids || [], + product_images: productImages, + product_names: productNames, + express_code: s.express_code || '', + express_no: s.express_no || '', + created_at: s.created_at || '', + shipped_at: s.shipped_at || '', + received_at: s.received_at || '', + status: s.status || 1 + } + }) + const next = page.value === 1 ? mapped : [...shippedList.value, ...mapped] shippedList.value = next if (list.length < pageSize.value || (page.value * pageSize.value >= total && total > 0)) { hasMore.value = false } else { page.value += 1 } if (list.length === 0) { hasMore.value = false } } catch (e) { + console.error('Load shipments error:', e) uni.showToast({ title: '加载失败', icon: 'none' }) } finally { loading.value = false @@ -875,4 +986,207 @@ async function onShip() { height: calc(130rpx + constant(safe-area-inset-bottom)); height: calc(130rpx + env(safe-area-inset-bottom)); } + +/* ============================================ + 发货单卡片样式 + ============================================ */ +.shipment-list { + display: flex; + flex-direction: column; + gap: 24rpx; +} + +.shipment-card { + background: #FFFFFF; + border-radius: 24rpx; + padding: 28rpx; + box-shadow: 0 8rpx 32rpx rgba(0, 0, 0, 0.08); +} + +/* 发货单头部 */ +.shipment-header { + display: flex; + justify-content: space-between; + align-items: flex-start; + margin-bottom: 24rpx; + padding-bottom: 20rpx; + border-bottom: 1rpx solid #F3F4F6; +} + +.shipment-batch { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 12rpx; +} + +.batch-label { + font-size: 28rpx; + font-weight: 700; + color: #1F2937; +} + +.batch-no { + font-size: 22rpx; + color: #9CA3AF; + font-family: monospace; +} + +.count-badge { + background: linear-gradient(135deg, #FFF4E6, #FFEDD5); + color: #FF6B35; + font-size: 22rpx; + font-weight: 600; + padding: 6rpx 16rpx; + border-radius: 20rpx; +} + +/* 发货状态标签 */ +.shipment-status { + font-size: 24rpx; + font-weight: 600; + padding: 8rpx 20rpx; + border-radius: 16rpx; + flex-shrink: 0; +} + +.status-pending { + background: #FEF3C7; + color: #D97706; +} + +.status-shipped { + background: #DBEAFE; + color: #2563EB; +} + +.status-delivered { + background: #D1FAE5; + color: #059669; +} + +.status-cancelled { + background: #FEE2E2; + color: #DC2626; +} + +/* 商品缩略图 */ +.product-thumbnails { + margin-bottom: 20rpx; +} + +.thumb-scroll { + display: flex; + gap: 16rpx; + margin-bottom: 16rpx; +} + +.thumb-img { + width: 120rpx; + height: 120rpx; + border-radius: 16rpx; + background: linear-gradient(145deg, #FFF8F3, #FFF4E6); + flex-shrink: 0; + box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.06); +} + +.thumb-more { + width: 120rpx; + height: 120rpx; + border-radius: 16rpx; + background: linear-gradient(135deg, #F9FAFB, #F3F4F6); + display: flex; + align-items: center; + justify-content: center; + font-size: 28rpx; + font-weight: 600; + color: #6B7280; + flex-shrink: 0; +} + +/* 商品名称列表 */ +.product-names { + display: flex; + flex-wrap: wrap; + gap: 8rpx 16rpx; +} + +.product-name-item { + font-size: 24rpx; + color: #6B7280; + background: #F9FAFB; + padding: 6rpx 14rpx; + border-radius: 8rpx; + max-width: 200rpx; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.product-name-more { + font-size: 24rpx; + color: #FF6B35; + font-weight: 500; +} + +/* 物流信息 */ +.shipment-express { + display: flex; + align-items: center; + background: linear-gradient(135deg, #F0F9FF, #E0F2FE); + border-radius: 16rpx; + padding: 20rpx; + margin-bottom: 16rpx; +} + +.express-icon { + width: 48rpx; + height: 48rpx; + display: flex; + align-items: center; + justify-content: center; + font-size: 32rpx; + margin-right: 16rpx; +} + +.express-info { + flex: 1; + display: flex; + flex-direction: column; + gap: 4rpx; +} + +.express-company { + font-size: 26rpx; + font-weight: 600; + color: #1F2937; +} + +.express-no { + font-size: 24rpx; + color: #6B7280; + font-family: monospace; +} + +.express-copy { + font-size: 24rpx; + color: #2563EB; + font-weight: 500; + padding: 12rpx 20rpx; + background: #FFFFFF; + border-radius: 12rpx; + box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05); +} + +/* 时间信息 */ +.shipment-time { + display: flex; + flex-direction: column; + gap: 6rpx; +} + +.time-item { + font-size: 22rpx; + color: #9CA3AF; +} diff --git a/pages/login/index.vue b/pages/login/index.vue index d3e5117..2f5252f 100644 --- a/pages/login/index.vue +++ b/pages/login/index.vue @@ -166,6 +166,9 @@ function onGetPhoneNumber(e) { if (user_info.avatar) uni.setStorageSync('avatar', user_info.avatar) if (user_info.nickname) uni.setStorageSync('nickname', user_info.nickname) if (user_info.invite_code) uni.setStorageSync('invite_code', user_info.invite_code) + // 保存 openid 用于支付等场景 + const openid = data && (data.openid || data.open_id) + if (openid) uni.setStorageSync('openid', openid) // 绑定手机号逻辑... try { diff --git a/pages/mine/index.vue b/pages/mine/index.vue index 805c4be..c5421f8 100644 --- a/pages/mine/index.vue +++ b/pages/mine/index.vue @@ -21,7 +21,6 @@ ID: {{ userId || '-' }} 立即登录 - 立即签到 @@ -42,7 +41,7 @@ - + 好礼相送 邀请好友送好礼 @@ -60,7 +59,7 @@ 我的订单 - + 盒柜 @@ -89,9 +88,9 @@ 道具卡 - - - 赏包 + + + 任务中心 @@ -102,7 +101,7 @@ 收货地址 - + 帮助中心 @@ -141,7 +140,7 @@ - + 我的优惠券 × @@ -150,30 +149,38 @@ 未使用 已使用 - 去兑换 - - - - - - + 加载中... - 暂无优惠券 + + 🎟️ + 暂无优惠券 + - - - {{ item.name || '优惠券' }} - {{ item.rules || item.description || '' }} - 有效期至:{{ formatDate(item.valid_end || item.end_time) }} + + + + ¥ + {{ formatCouponValue(item.remaining || item.amount || 0) }} + + 满{{ formatCouponValue(item.min_amount || 0) }}可用 - - - ¥ - {{ (Number(item.remaining || 0) / 100).toFixed(2) }} - - {{ couponsTab === 0 ? '未使用' : '已使用/过期' }} + + + + + + + {{ item.name || '优惠券' }} + {{ item.rules || item.description || '全场通用' }} + 有效期至:{{ formatDate(item.valid_end || item.end_time) }} + + 去使用 + + + {{ couponsTab === 1 ? '已使用' : '已过期' }} + @@ -185,22 +192,40 @@ - + 我的道具卡 × - + + + 未使用 + 已使用 + + + 加载中... - 暂无道具卡 - - - {{ item.name || item.title || '道具卡' }} - {{ item.description || item.rules || '' }} - 有效期至:{{ formatDate(item.valid_end || item.end_time) }} - - - 数量:{{ item.remaining ?? item.count ?? 1 }} + + 🃏 + {{ itemCardsTab === 0 ? '暂无可用道具卡' : '暂无使用记录' }} + + + + + + {{ getCardIcon(item.type || item.name) }} + + + {{ item.name || item.title || '道具卡' }} + {{ item.description || item.rules || '可在抽奖时使用' }} + 使用时间:{{ formatDate(item.used_at) }} + + + ×{{ item.remaining ?? item.count ?? 1 }} + + + 已使用 + @@ -209,32 +234,76 @@ - + 任务中心 × - + + + + + {{ tasksList.filter(t => t.status === 2).length }} + 已完成 + + + + {{ tasksList.filter(t => t.status === 1).length }} + 进行中 + + + + {{ tasksList.length }} + 全部任务 + + + + 加载中... - 暂无任务 - - - {{ task.name || '任务' }} - {{ task.description || '' }} - - 奖励 - - - {{ rw.reward_type || '未知' }} - ×{{ rw.quantity || 0 }} - + + 📋 + 暂无任务 + + + + + {{ getTaskIcon(task.type || task.name) }} + + + + {{ task.name || '任务' }} + + {{ formatStatus(task.status) }} + + + {{ task.description || '完成任务获取奖励' }} + + + + + + + {{ task.current || 0 }}/{{ task.target || 1 }} + + + + + + {{ getRewardIcon(rw.reward_type) }} + +{{ rw.quantity || 0 }} - - {{ formatStatus(task.status) }} + + + 去完成 + 已完成 + 未开始 + + 加载更多... + 没有更多了 @@ -278,6 +347,7 @@ const redeemCode = ref('') const itemCardsVisible = ref(false) const itemCardsList = ref([]) const itemCardsLoading = ref(false) +const itemCardsTab = ref(0) // 0: 未使用, 1: 已使用 const tasksVisible = ref(false) const tasksList = ref([]) @@ -337,6 +407,20 @@ function toHelp() { uni.navigateTo({ url: '/pages/help/index' }) } +function handleInvite() { + // Trigger WeChat share menu for inviting friends + // #ifdef MP-WEIXIN + wx.showShareMenu({ + withShareTicket: true, + menus: ['shareAppMessage', 'shareTimeline'] + }) + uni.showToast({ title: '点击右上角分享给好友', icon: 'none', duration: 2000 }) + // #endif + // #ifndef MP-WEIXIN + uni.showToast({ title: '请点击右上角分享', icon: 'none' }) + // #endif +} + // 积分弹窗逻辑 function showPointsPopup() { pointsVisible.value = true @@ -438,15 +522,24 @@ async function handleRedeem() { // 道具卡弹窗逻辑 function showItemCardsPopup() { itemCardsVisible.value = true + itemCardsTab.value = 0 + itemCardsList.value = [] loadItemCards() } function closeItemCardsPopup() { itemCardsVisible.value = false } +function switchItemCardsTab(index) { + if (itemCardsTab.value === index) return + itemCardsTab.value = index + itemCardsList.value = [] + loadItemCards() +} async function loadItemCards() { if (itemCardsLoading.value) return itemCardsLoading.value = true const user_id = uni.getStorageSync('user_id') try { - const res = await getItemCards(user_id) + const status = itemCardsTab.value // 0: 未使用, 1: 已使用 + const res = await getItemCards(user_id, status) let list = [] if (res && Array.isArray(res.list)) list = res.list else if (res && Array.isArray(res.data)) list = res.data @@ -490,6 +583,73 @@ async function loadTasks() { } function loadMoreTasks() { if (tasksHasMore.value && !tasksLoading.value) loadTasks() } +// 辅助函数 +function formatCouponValue(value) { + const num = Number(value || 0) + return (num / 100).toFixed(0) +} + +function getCardIcon(typeOrName) { + const name = String(typeOrName || '').toLowerCase() + if (name.includes('双倍') || name.includes('double')) return '✨' + if (name.includes('保底') || name.includes('guarantee')) return '🛡️' + if (name.includes('折扣') || name.includes('discount')) return '💰' + if (name.includes('免费') || name.includes('free')) return '🎁' + if (name.includes('加成') || name.includes('boost')) return '🚀' + return '🃏' +} + +// 任务中心辅助函数 +function getTaskIcon(typeOrName) { + const name = String(typeOrName || '').toLowerCase() + if (name.includes('签到') || name.includes('checkin')) return '📅' + if (name.includes('邀请') || name.includes('invite')) return '👥' + if (name.includes('分享') || name.includes('share')) return '📤' + if (name.includes('抽奖') || name.includes('lottery') || name.includes('draw')) return '🎲' + if (name.includes('充值') || name.includes('recharge')) return '💳' + if (name.includes('购买') || name.includes('buy')) return '🛒' + if (name.includes('完善') || name.includes('profile')) return '📝' + if (name.includes('新人') || name.includes('新手')) return '🌟' + return '📋' +} + +function getTaskStatusClass(status) { + const n = Number(status) + if (n === 1) return 'status-ongoing' + if (n === 2) return 'status-done' + return 'status-waiting' +} + +function getTaskProgress(task) { + if (task.progress !== undefined) return Math.min(100, Math.max(0, task.progress)) + if (task.current !== undefined && task.target) { + return Math.min(100, Math.max(0, (task.current / task.target) * 100)) + } + return 0 +} + +function getRewardIcon(rewardType) { + const type = String(rewardType || '').toLowerCase() + if (type.includes('积分') || type.includes('point')) return '💰' + if (type.includes('优惠券') || type.includes('coupon')) return '🎟️' + if (type.includes('道具') || type.includes('card') || type.includes('prop')) return '🃏' + if (type.includes('现金') || type.includes('cash')) return '💵' + return '🎁' +} + +function goToTask(task) { + closeTasksPopup() + // 根据任务类型跳转到对应页面 + const name = String(task.name || '').toLowerCase() + if (name.includes('邀请')) { + handleInvite() + } else if (name.includes('签到')) { + uni.showToast({ title: '签到功能开发中', icon: 'none' }) + } else { + uni.switchTab({ url: '/pages/index/index' }) + } +} + function formatDate(dateStr) { if (!dateStr) return '' const date = new Date(dateStr) @@ -742,4 +902,493 @@ onShareAppMessage(() => { .reward-label { font-size: 20rpx; color: #ff6b00; margin-right: 8rpx; background: #FFF0E5; padding: 2rpx 8rpx; border-radius: 6rpx; } .reward-tags { display: flex; flex-wrap: wrap; gap: 8rpx; } .reward-item { font-size: 20rpx; color: #666; background: #f0f0f0; padding: 2rpx 8rpx; border-radius: 6rpx; } + +/* ============================================ + 优惠券弹窗样式 + ============================================ */ +.coupon-popup { width: 680rpx; } +.coupon-scroll { flex: 1; min-height: 400rpx; max-height: 60vh; } + +/* 空状态 */ +.empty-state { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 80rpx 0; +} +.empty-icon { font-size: 80rpx; margin-bottom: 24rpx; } +.empty-text { font-size: 28rpx; color: #999; } + +/* 兑换区域 */ +.redeem-section { + padding: 24rpx 0; +} +.redeem-input-wrap { + background: #F5F5F5; + border-radius: 16rpx; + padding: 4rpx; + margin-bottom: 24rpx; +} +.redeem-section .redeem-input { + width: 100%; + height: 88rpx; + background: transparent; + padding: 0 24rpx; + font-size: 30rpx; +} +.redeem-section .redeem-btn { + width: 100%; + height: 88rpx; + line-height: 88rpx; + background: linear-gradient(135deg, #FF9F43, #FF6B35); + color: #fff; + font-size: 32rpx; + font-weight: 700; + border-radius: 44rpx; + text-align: center; + border: none; + box-shadow: 0 8rpx 24rpx rgba(255, 107, 53, 0.3); +} +.redeem-tips { + display: flex; + align-items: center; + justify-content: center; + margin-top: 32rpx; + padding: 20rpx; + background: #FFF8F3; + border-radius: 12rpx; +} +.tip-icon { font-size: 32rpx; margin-right: 12rpx; } +.tip-text { font-size: 24rpx; color: #FF6B35; } + +/* 优惠券票券样式 */ +.coupon-ticket { + display: flex; + background: linear-gradient(135deg, #FFF8F3, #FFFFFF); + border-radius: 16rpx; + margin-bottom: 20rpx; + overflow: hidden; + box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.06); + position: relative; +} +.coupon-ticket.used { + background: #F5F5F5; + opacity: 0.7; +} +.coupon-ticket.used .coupon-left-section { + background: #E0E0E0; +} + +.coupon-left-section { + width: 180rpx; + background: linear-gradient(135deg, #FF9F43, #FF6B35); + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 24rpx 16rpx; +} +.coupon-value { + display: flex; + align-items: baseline; +} +.coupon-symbol { + font-size: 28rpx; + color: #fff; + font-weight: 700; +} +.coupon-amount { + font-size: 56rpx; + font-weight: 800; + color: #fff; +} +.coupon-condition { + font-size: 20rpx; + color: rgba(255,255,255,0.85); + margin-top: 8rpx; +} + +.coupon-divider { + width: 24rpx; + position: relative; + display: flex; + flex-direction: column; + align-items: center; +} +.divider-circle { + width: 24rpx; + height: 12rpx; + background: #F8F5F2; + position: absolute; +} +.divider-circle.top { top: -1rpx; border-radius: 0 0 12rpx 12rpx; } +.divider-circle.bottom { bottom: -1rpx; border-radius: 12rpx 12rpx 0 0; } +.divider-line { + width: 2rpx; + flex: 1; + background: repeating-linear-gradient(to bottom, #E5E5E5 0, #E5E5E5 8rpx, transparent 8rpx, transparent 16rpx); + margin: 16rpx 0; +} + +.coupon-right-section { + flex: 1; + padding: 20rpx 24rpx; + display: flex; + flex-direction: column; + justify-content: center; +} +.coupon-right-section .coupon-name { + font-size: 28rpx; + font-weight: 700; + color: #1F2937; + margin-bottom: 8rpx; +} +.coupon-right-section .coupon-desc { + font-size: 22rpx; + color: #6B7280; + margin-bottom: 6rpx; +} +.coupon-expire { + font-size: 20rpx; + color: #9CA3AF; + margin-bottom: 12rpx; +} +.coupon-action { + align-self: flex-end; +} +.use-btn { + display: inline-block; + background: linear-gradient(135deg, #FFD166, #FF9F43); + color: #1F2937; + font-size: 22rpx; + font-weight: 700; + padding: 10rpx 24rpx; + border-radius: 20rpx; + box-shadow: 0 4rpx 12rpx rgba(255, 159, 67, 0.3); +} +.coupon-status-badge { + align-self: flex-end; + background: #E5E5E5; + color: #999; + font-size: 20rpx; + padding: 6rpx 16rpx; + border-radius: 12rpx; +} + +/* ============================================ + 道具卡弹窗样式 + ============================================ */ +.item-cards-popup { width: 680rpx; } +.item-cards-scroll { flex: 1; min-height: 400rpx; max-height: 60vh; } + +.item-cards-grid { + display: flex; + flex-direction: column; + gap: 16rpx; +} + +.item-card { + display: flex; + align-items: center; + background: linear-gradient(135deg, #FFFFFF, #FAFAFA); + border-radius: 20rpx; + padding: 24rpx; + box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.06); + position: relative; + overflow: hidden; +} +.item-card::before { + content: ''; + position: absolute; + top: 0; + left: 0; + width: 6rpx; + height: 100%; + background: linear-gradient(180deg, #FF9F43, #FF6B35); +} + +.card-icon-wrap { + width: 80rpx; + height: 80rpx; + background: linear-gradient(135deg, #FFF4E6, #FFEDD5); + border-radius: 20rpx; + display: flex; + align-items: center; + justify-content: center; + margin-right: 20rpx; + flex-shrink: 0; +} +.card-icon { + font-size: 40rpx; +} + +.card-info { + flex: 1; + display: flex; + flex-direction: column; +} +.card-name { + font-size: 28rpx; + font-weight: 700; + color: #1F2937; + margin-bottom: 8rpx; +} +.card-desc { + font-size: 22rpx; + color: #6B7280; +} + +.card-count-badge { + background: linear-gradient(135deg, #FF9F43, #FF6B35); + padding: 8rpx 20rpx; + border-radius: 20rpx; + box-shadow: 0 4rpx 12rpx rgba(255, 107, 53, 0.3); +} +.count-num { + font-size: 26rpx; + font-weight: 700; + color: #fff; +} + +/* 已使用道具卡样式 */ +.item-card.used { + opacity: 0.7; + background: linear-gradient(135deg, #F3F4F6, #E5E7EB); +} +.item-card.used::before { + background: linear-gradient(180deg, #9CA3AF, #6B7280); +} +.item-card.used .card-icon-wrap { + background: linear-gradient(135deg, #E5E7EB, #D1D5DB); +} +.card-use-time { + font-size: 20rpx; + color: #9CA3AF; + margin-top: 6rpx; +} +.card-used-badge { + background: #E5E7EB; + padding: 8rpx 20rpx; + border-radius: 20rpx; +} +.used-text { + font-size: 24rpx; + font-weight: 600; + color: #6B7280; +} + +.loading-more, .no-more { + text-align: center; + color: #9CA3AF; + padding: 24rpx 0; + font-size: 24rpx; +} + +/* ============================================ + 任务中心弹窗样式 + ============================================ */ +.task-center-popup { width: 700rpx; } +.task-scroll { flex: 1; min-height: 400rpx; max-height: 55vh; } + +/* 任务统计栏 */ +.task-stats-bar { + display: flex; + justify-content: space-around; + align-items: center; + background: linear-gradient(135deg, #FFF8F3, #FFEDD5); + border-radius: 16rpx; + padding: 24rpx; + margin-bottom: 24rpx; +} +.stats-item { + display: flex; + flex-direction: column; + align-items: center; +} +.stats-num { + font-size: 40rpx; + font-weight: 800; + color: #FF6B35; +} +.stats-label { + font-size: 22rpx; + color: #6B7280; + margin-top: 4rpx; +} +.stats-divider { + width: 2rpx; + height: 48rpx; + background: #E5E5E5; +} + +/* 任务卡片 */ +.task-card { + display: flex; + align-items: flex-start; + background: #FFFFFF; + border-radius: 20rpx; + padding: 24rpx; + margin-bottom: 16rpx; + box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.06); + position: relative; + overflow: hidden; +} +.task-card.completed { + opacity: 0.7; + background: #FAFAFA; +} +.task-card::before { + content: ''; + position: absolute; + top: 0; + left: 0; + width: 6rpx; + height: 100%; + background: linear-gradient(180deg, #FF9F43, #FF6B35); +} +.task-card.completed::before { + background: #D1D5DB; +} + +.task-icon-wrap { + width: 72rpx; + height: 72rpx; + background: linear-gradient(135deg, #FFF4E6, #FFEDD5); + border-radius: 18rpx; + display: flex; + align-items: center; + justify-content: center; + margin-right: 20rpx; + flex-shrink: 0; +} +.task-icon { + font-size: 36rpx; +} + +.task-main { + flex: 1; + min-width: 0; +} +.task-header { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 8rpx; +} +.task-card .task-name { + font-size: 28rpx; + font-weight: 700; + color: #1F2937; + flex: 1; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} +.task-card .task-desc { + font-size: 22rpx; + color: #6B7280; + margin-bottom: 12rpx; + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; +} + +/* 任务状态标签 */ +.task-status-tag { + font-size: 20rpx; + font-weight: 600; + padding: 4rpx 12rpx; + border-radius: 8rpx; + flex-shrink: 0; + margin-left: 12rpx; +} +.status-ongoing { + background: #DBEAFE; + color: #2563EB; +} +.status-done { + background: #D1FAE5; + color: #059669; +} +.status-waiting { + background: #F3F4F6; + color: #6B7280; +} + +/* 任务进度条 */ +.task-progress { + display: flex; + align-items: center; + margin-bottom: 12rpx; +} +.task-progress .progress-bar { + flex: 1; + height: 12rpx; + background: #F3F4F6; + border-radius: 6rpx; + overflow: hidden; + margin-right: 12rpx; +} +.task-progress .progress-fill { + height: 100%; + background: linear-gradient(90deg, #FF9F43, #FF6B35); + border-radius: 6rpx; + transition: width 0.3s ease; +} +.task-progress .progress-text { + font-size: 20rpx; + color: #9CA3AF; + flex-shrink: 0; +} + +/* 任务奖励 */ +.task-rewards { + display: flex; + flex-wrap: wrap; + gap: 8rpx; +} +.reward-badge { + display: flex; + align-items: center; + background: linear-gradient(135deg, #FFF4E6, #FFEDD5); + padding: 6rpx 12rpx; + border-radius: 10rpx; +} +.reward-badge .reward-icon { + font-size: 22rpx; + margin-right: 4rpx; +} +.reward-badge .reward-value { + font-size: 22rpx; + font-weight: 600; + color: #FF6B35; +} + +/* 任务操作按钮 */ +.task-action { + display: flex; + align-items: center; + margin-left: 16rpx; +} +.task-action .action-btn { + font-size: 24rpx; + font-weight: 700; + padding: 12rpx 24rpx; + border-radius: 20rpx; + background: linear-gradient(135deg, #FF9F43, #FF6B35); + color: #fff; + box-shadow: 0 4rpx 12rpx rgba(255, 107, 53, 0.3); +} +.task-action .action-btn.done { + background: #D1FAE5; + color: #059669; + box-shadow: none; +} +.task-action .action-btn.waiting { + background: #F3F4F6; + color: #9CA3AF; + box-shadow: none; +}