From 3dc03f6fe59a8f820a06779d74405dd758c29ea7 Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Sat, 13 Jun 2026 18:19:46 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BA=86=E6=A0=B7=E5=BC=8F?= =?UTF-8?q?=EF=BC=8C=E6=8A=BD=E8=B1=A1=E5=87=BA=E4=BA=86=20WXSS/JS=20?= =?UTF-8?q?=E5=BA=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app.wxss | 1179 +++++++++++++++ pages/assessor/assessor.js | 381 +++-- pages/category/category.js | 652 +++++---- pages/chat/chat.wxml | 86 +- pages/chat/chat.wxss | 99 +- pages/cs-chat/cs-chat.wxml | 110 +- pages/cs-chat/cs-chat.wxss | 68 +- pages/fighter-msg/fighter-msg.wxml | 14 +- pages/fighter-msg/fighter-msg.wxss | 16 +- pages/fighter-orders/fighter-orders.js | 596 ++++---- pages/fighter-recharge/fighter-recharge.wxml | 2 +- pages/fighter/fighter.js | 196 +-- pages/fighter/fighter.wxml | 2 +- pages/fighter/fighter.wxss | 22 +- pages/group-chat/group-chat.wxml | 92 +- pages/group-chat/group-chat.wxss | 97 +- pages/index/index.js | 46 +- pages/index/index.wxml | 6 +- pages/index/index.wxss | 79 + pages/leader/leader.js | 360 ++--- pages/manager-rank/manager-rank.wxml | 364 +++-- pages/manager-rank/manager-rank.wxss | 1277 +---------------- pages/manager/manager.js | 1173 +++++++-------- pages/merchant-dispatch/merchant-dispatch.js | 824 ++++++----- pages/merchant-orders/merchant-orders.js | 634 ++++---- pages/merchant-rank/merchant-rank.wxml | 366 +++-- pages/merchant-rank/merchant-rank.wxss | 1277 +---------------- pages/merchant/merchant.js | 280 +--- pages/messages/messages.js | 797 +++++----- pages/messages/messages.wxss | 278 +++- pages/mine/mine.js | 309 ++-- pages/orders/orders.wxss | 403 +++--- .../components/fakuan-list/fakuan-list.wxml | 10 +- .../components/fakuan-list/fakuan-list.wxss | 4 +- .../components/fakuan-pay/fakuan-pay.wxml | 4 +- .../components/fakuan-pay/fakuan-pay.wxss | 4 +- .../components/jifen-list/jifen-list.wxml | 8 +- .../components/jifen-list/jifen-list.wxss | 4 +- pages/poster/poster.wxml | 42 +- pages/poster/poster.wxss | 502 +++---- pages/product-detail/product-detail.js | 7 +- pages/product-detail/product-detail.wxml | 235 +-- pages/product-detail/product-detail.wxss | 696 +++++---- pages/submit/submit.js | 879 +++++------- pages/submit/submit.wxml | 256 ++-- pages/submit/submit.wxss | 576 ++++---- pages/verify/verify.js | 328 ++--- pages/withdraw/withdraw.js | 126 +- tab-bar/index.wxss | 15 +- utils/api-helper.js | 46 + utils/base-page.js | 362 +++++ 51 files changed, 7154 insertions(+), 9035 deletions(-) create mode 100644 utils/api-helper.js create mode 100644 utils/base-page.js diff --git a/app.wxss b/app.wxss index 958f5ce..58b2d37 100644 --- a/app.wxss +++ b/app.wxss @@ -887,3 +887,1182 @@ page { font-size: 26rpx; color: #ccc; } + +/* ========== 更多动画 ========== */ +@keyframes bounceIn { + 0% { opacity: 0; transform: scale(0.3); } + 50% { opacity: 1; transform: scale(1.05); } + 70% { transform: scale(0.9); } + 100% { transform: scale(1); } +} + +@keyframes shake { + 0%, 100% { transform: translateX(0); } + 10%, 30%, 50%, 70%, 90% { transform: translateX(-6rpx); } + 20%, 40%, 60%, 80% { transform: translateX(6rpx); } +} + +@keyframes slideInLeft { + from { opacity: 0; transform: translateX(-60rpx); } + to { opacity: 1; transform: translateX(0); } +} + +@keyframes slideInRight { + from { opacity: 0; transform: translateX(60rpx); } + to { opacity: 1; transform: translateX(0); } +} + +@keyframes slideInUp { + from { opacity: 0; transform: translateY(40rpx); } + to { opacity: 1; transform: translateY(0); } +} + +@keyframes scaleIn { + from { opacity: 0; transform: scale(0.8); } + to { opacity: 1; transform: scale(1); } +} + +@keyframes shimmer { + 0% { background-position: -200% 0; } + 100% { background-position: 200% 0; } +} + +@keyframes float { + 0%, 100% { transform: translateY(0); } + 50% { transform: translateY(-16rpx); } +} + +@keyframes iconFloat { + 0%, 100% { transform: translateY(0); } + 50% { transform: translateY(-30rpx); } +} + +@keyframes glowPulse { + 0%, 100% { opacity: 0.5; } + 50% { opacity: 0.8; } +} + +@keyframes scanline { + 0% { top: 0; } + 100% { top: 100%; } +} + +@keyframes streamFlow { + 0% { top: -100rpx; opacity: 0; } + 10% { opacity: 1; } + 90% { opacity: 1; } + 100% { top: 100%; opacity: 0; } +} + +@keyframes particleFloat { + 0%, 100% { transform: translate(0, 0); } + 25% { transform: translate(100rpx, -100rpx); } + 50% { transform: translate(-50rpx, 150rpx); } + 75% { transform: translate(150rpx, 50rpx); } +} + +@keyframes crownFloat { + 0%, 100% { transform: translateY(0) rotate(5deg) scale(1); filter: drop-shadow(0 0 10rpx rgba(255, 215, 0, 0.8)); } + 50% { transform: translateY(-15rpx) rotate(10deg) scale(1.1); filter: drop-shadow(0 0 25rpx rgba(255, 215, 0, 1)); } +} + +@keyframes haloRotate { + 0% { transform: translate(-50%, -50%) rotate(0deg); } + 100% { transform: translate(-50%, -50%) rotate(360deg); } +} + +@keyframes sparkleTwinkle { + 0%, 100% { opacity: 0; transform: scale(0.5); } + 50% { opacity: 1; transform: scale(1); } +} + +@keyframes cardFlash { + 0% { filter: brightness(1) drop-shadow(0 0 0 rgba(255, 255, 255, 0)); } + 50% { filter: brightness(1.4) drop-shadow(0 0 40rpx rgba(255, 255, 255, 0.8)); } + 100% { filter: brightness(1) drop-shadow(0 0 0 rgba(255, 255, 255, 0)); } +} + +@keyframes spinOuter { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} + +@keyframes spinInner { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(-360deg); } +} + +/* ========== 动画工具类 ========== */ +.anim-bounce { animation: bounceIn 0.5s ease-out; } +.anim-shake { animation: shake 0.5s ease; } +.anim-slide-left { animation: slideInLeft 0.3s ease-out; } +.anim-slide-right { animation: slideInRight 0.3s ease-out; } +.anim-slide-up { animation: slideInUp 0.3s ease-out; } +.anim-scale { animation: scaleIn 0.3s ease-out; } +.anim-float { animation: float 3s ease-in-out infinite; } +.anim-shimmer { + background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%); + background-size: 200% 100%; + animation: shimmer 1.5s infinite; +} + +/* ========== 聊天公共样式 ========== */ +.chat-page { + width: 100%; + height: 100vh; + background: #f5f5f5; +} + +.chat-msg-list { + position: absolute; + left: 0; + right: 0; + padding: 0 20rpx; + box-sizing: border-box; + overflow-y: auto; +} + +.chat-loading-more { + text-align: center; + font-size: 24rpx; + color: #999; + padding: 20rpx 0; +} + +.chat-time-tag { + text-align: center; + margin: 28rpx 0; + font-size: 24rpx; + color: #b0b0b0; +} + +.chat-msg-wrapper { + margin-bottom: 12rpx; + animation: slideInUp 0.2s ease-out; +} + +.chat-msg-row { + display: flex; + align-items: flex-start; + margin-bottom: 4rpx; +} + +.chat-msg-row--right { + justify-content: flex-end; +} + +.chat-msg-row--left { + justify-content: flex-start; +} + +.chat-avatar { + width: 76rpx; + height: 76rpx; + border-radius: 50%; + background: #e0e0e0; + margin: 0 12rpx; + flex-shrink: 0; +} + +.chat-bubble { + max-width: 70%; + padding: 16rpx 20rpx; + border-radius: 16rpx; + position: relative; + word-break: break-all; + font-size: 30rpx; + line-height: 1.5; + animation: scaleIn 0.15s ease-out; +} + +.chat-bubble--left { + background: #fff; + border-top-left-radius: 4rpx; +} + +.chat-bubble--right { + background: #00aaff; + color: #fff; + border-top-right-radius: 4rpx; +} + +.chat-msg-text { + color: inherit; +} + +.chat-msg-image { + max-width: 240rpx; + border-radius: 10rpx; +} + +.chat-read-tag { + font-size: 20rpx; + color: #888; + text-align: right; + padding: 4rpx 12rpx 0 0; +} + +.chat-order-bubble { + background: #fff7e0; + padding: 12rpx; + border-radius: 8rpx; +} + +.chat-order-label { + font-size: 24rpx; + color: #e6a23c; + font-weight: 600; +} + +.chat-order-info { + font-size: 24rpx; + color: #333; + margin: 6rpx 0; +} + +.chat-input-area { + position: fixed; + left: 0; + right: 0; + background: #f7f7f7; + border-top: 1rpx solid #ddd; + z-index: 10; +} + +.chat-plus-panel { + position: absolute; + bottom: 100%; + left: 0; + right: 0; + background: #fff; + border-top: 1rpx solid #e0e0e0; + padding: 20rpx; + transform: translateY(100%); + opacity: 0; + transition: transform 0.3s, opacity 0.3s; + pointer-events: none; + z-index: 9; +} + +.chat-plus-panel.show { + transform: translateY(0); + opacity: 1; + pointer-events: auto; +} + +.chat-plus-grid { + display: flex; + justify-content: space-around; +} + +.chat-plus-item { + display: flex; + flex-direction: column; + align-items: center; + width: 120rpx; +} + +.chat-plus-icon { + font-size: 60rpx; +} + +.chat-plus-label { + font-size: 24rpx; + color: #666; + margin-top: 8rpx; +} + +.chat-emoji-mask { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: transparent; + z-index: 8; +} + +.chat-emoji-panel { + position: absolute; + bottom: 100%; + left: 0; + right: 0; + background: #fff; + border-top: 1rpx solid #e0e0e0; + height: 380rpx; + transform: translateY(100%); + opacity: 0; + transition: 0.3s; + pointer-events: none; + z-index: 9; +} + +.chat-emoji-panel.show { + transform: translateY(0); + opacity: 1; + pointer-events: auto; +} + +.chat-emoji-scroll { + height: 100%; +} + +.chat-emoji-grid { + display: flex; + flex-wrap: wrap; + padding: 20rpx 10rpx; +} + +.chat-emoji-item { + width: 14.28%; + text-align: center; + padding: 16rpx 0; + font-size: 44rpx; +} + +.chat-pending-image { + display: flex; + align-items: center; + padding: 10rpx 20rpx; + background: #fff; +} + +.chat-pending-thumb { + width: 80rpx; + height: 80rpx; + border-radius: 8rpx; +} + +.chat-pending-remove { + font-size: 30rpx; + color: #999; + margin-left: 10rpx; +} + +.chat-write-row { + display: flex; + padding: 0 20rpx 10rpx; + align-items: center; +} + +.chat-text-input { + flex: 1; + height: 76rpx; + background: #fff; + border-radius: 12rpx; + padding: 0 20rpx; + font-size: 30rpx; + margin-right: 12rpx; +} + +.chat-send-btn { + width: 120rpx; + height: 76rpx; + background: #00aaff; + color: #fff; + font-size: 28rpx; + font-weight: 500; + border-radius: 12rpx; + display: flex; + align-items: center; + justify-content: center; + transition: opacity 0.2s; +} + +.chat-send-btn:active { + opacity: 0.8; +} + +.chat-plus-btn { + width: 56rpx; + height: 56rpx; + display: flex; + align-items: center; + justify-content: center; + margin-left: 10rpx; +} + +.chat-plus-icon-single { + font-size: 46rpx; + color: #00aaff; +} + +.chat-detail-modal { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.7); + z-index: 999; + display: flex; + align-items: center; + justify-content: center; + animation: fadeIn 0.2s ease-out; +} + +.chat-detail-content { + width: 80%; + max-height: 80%; + background: #fff; + border-radius: 16rpx; + padding: 40rpx; + animation: scaleIn 0.2s ease-out; +} + +.chat-detail-text { + font-size: 32rpx; + line-height: 1.6; + word-break: break-all; + overflow-y: auto; +} + +.chat-copy-btn { + width: 100%; + height: 80rpx; + background: #00aaff; + color: #fff; + border-radius: 12rpx; + margin-top: 20rpx; + font-size: 30rpx; + display: flex; + align-items: center; + justify-content: center; +} + +/* ========== 排行榜公共样式 ========== */ +.rank-page { + min-height: 100vh; + background: #000; + color: #fff; + position: relative; + overflow-x: hidden; + padding: 40rpx 30rpx; + box-sizing: border-box; +} + +.rank-cyber-bg { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + z-index: 1; + pointer-events: none; +} + +.rank-cyber-grid { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-image: + linear-gradient(rgba(0, 255, 255, 0.05) 1px, transparent 1px), + linear-gradient(90deg, rgba(0, 255, 255, 0.05) 1px, transparent 1px); + background-size: 50rpx 50rpx; + opacity: 0.3; +} + +.rank-cyber-scanline { + position: absolute; + top: 0; + left: 0; + right: 0; + height: 3rpx; + background: linear-gradient(90deg, transparent 0%, #00ffff 30%, #ff00ff 50%, #00ffff 70%, transparent 100%); + animation: scanline 3s linear infinite; + filter: blur(1rpx); + box-shadow: 0 0 20rpx #00ffff; + z-index: 2; +} + +.rank-data-streams { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + overflow: hidden; +} + +.rank-stream { + position: absolute; + width: 1rpx; + height: 100rpx; + background: linear-gradient(to bottom, transparent, #00ffff, transparent); + filter: blur(0.5rpx); + animation: streamFlow 8s linear infinite; +} + +.rank-stream--1 { left: 20%; animation-delay: 0s; } +.rank-stream--2 { left: 50%; animation-delay: 2s; } +.rank-stream--3 { left: 80%; animation-delay: 4s; } + +.rank-particle { + position: absolute; + width: 6rpx; + height: 6rpx; + background: #00ffff; + border-radius: 50%; + filter: blur(1rpx); + box-shadow: 0 0 10rpx #00ffff; + animation: particleFloat 20s infinite linear; +} + +.rank-particle:nth-child(odd) { + background: #ff00ff; + box-shadow: 0 0 10rpx #ff00ff; +} + +.rank-page-header { + position: relative; + z-index: 10; + margin-bottom: 55rpx; + margin-top: -40rpx; +} + +.rank-title-container { + text-align: center; + margin-bottom: 40rpx; +} + +.rank-main-title { + display: block; + font-size: 58rpx; + font-weight: 900; + background: linear-gradient(90deg, #00ffff, #ff00ff, #ffff00); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + letter-spacing: 4rpx; + margin-bottom: 8rpx; +} + +.rank-title-line { + width: 220rpx; + height: 3rpx; + background: linear-gradient(90deg, transparent, #00ffff, #ff00ff, transparent); + margin: 12rpx auto; + position: relative; +} + +.rank-sub-title { + display: block; + font-size: 22rpx; + color: rgba(255, 255, 255, 0.6); + letter-spacing: 6rpx; + text-transform: uppercase; + margin-top: 5rpx; +} + +.rank-toggle-container { + display: flex; + background: rgba(10, 10, 30, 0.8); + border-radius: 60rpx; + padding: 6rpx; + position: relative; + border: 1rpx solid rgba(0, 255, 255, 0.2); + backdrop-filter: blur(20rpx); + max-width: 700rpx; + width: 90%; + margin: 0 auto; + box-shadow: 0 10rpx 40rpx rgba(0, 0, 0, 0.8), inset 0 0 20rpx rgba(0, 255, 255, 0.05); +} + +.rank-toggle-bg { + position: absolute; + top: 6rpx; + bottom: 6rpx; + width: 50%; + background: linear-gradient(90deg, rgba(0, 255, 255, 0.2), rgba(255, 0, 255, 0.2)); + border-radius: 54rpx; + transition: transform 0.4s cubic-bezier(0.68, -0.55, 0.265, 1.55); + z-index: 1; +} + +.rank-toggle-btn { + flex: 1; + text-align: center; + padding: 30rpx 0; + font-size: 32rpx; + font-weight: 600; + color: rgba(255, 255, 255, 0.7); + position: relative; + z-index: 2; + transition: all 0.3s ease; + border-radius: 54rpx; + min-width: 200rpx; +} + +.rank-toggle-btn.active { + color: #ffffff; + text-shadow: 0 0 15rpx rgba(255, 255, 255, 0.5); +} + +.rank-loading-state { + display: flex; + align-items: center; + justify-content: center; + height: 60vh; + position: relative; + z-index: 10; +} + +.rank-loading-spinner { + position: relative; + width: 120rpx; + height: 120rpx; + margin: 0 auto 30rpx; +} + +.rank-spinner-outer { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + border: 8rpx solid transparent; + border-top-color: #00ffff; + border-right-color: #ff00ff; + border-radius: 50%; + animation: spinOuter 2s linear infinite; +} + +.rank-spinner-inner { + position: absolute; + top: 15rpx; + left: 15rpx; + right: 15rpx; + bottom: 15rpx; + border: 6rpx solid transparent; + border-bottom-color: #ffff00; + border-left-color: #00ffff; + border-radius: 50%; + animation: spinInner 1.5s linear infinite reverse; +} + +.rank-spinner-core { + position: absolute; + top: 30rpx; + left: 30rpx; + right: 30rpx; + bottom: 30rpx; + background: radial-gradient(circle, rgba(0, 255, 255, 0.2) 0%, rgba(255, 0, 255, 0.1) 50%, transparent 100%); + border-radius: 50%; +} + +.rank-loading-text { + display: block; + font-size: 32rpx; + color: #00ffff; + margin-bottom: 10rpx; + text-shadow: 0 0 10rpx rgba(0, 255, 255, 0.5); +} + +.rank-loading-sub { + display: block; + font-size: 24rpx; + color: rgba(255, 255, 255, 0.6); +} + +.rank-top-three { + display: flex; + justify-content: center; + align-items: flex-end; + position: relative; + z-index: 10; + margin-bottom: 40rpx; + gap: 20rpx; + margin-top: 10rpx; +} + +.rank-card { + position: relative; + display: flex; + flex-direction: column; + align-items: center; + transition: all 0.3s ease; +} + +.rank-card--1 { width: 280rpx; z-index: 30; margin-bottom: 30rpx; } +.rank-card--2 { width: 220rpx; z-index: 20; margin-right: -20rpx; } +.rank-card--3 { width: 220rpx; z-index: 20; margin-left: -20rpx; } + +.rank-card.flash { animation: cardFlash 0.4s ease-out; } + +.rank-podium-base { + position: absolute; + bottom: 0; + left: 0; + right: 0; + height: 60rpx; + border-radius: 30rpx 30rpx 0 0; + display: flex; + align-items: center; + justify-content: center; + z-index: 1; +} + +.rank-card--1 .rank-podium-base { height: 80rpx; } + +.rank-podium-gold { background: linear-gradient(180deg, rgba(255, 215, 0, 0.3) 0%, rgba(255, 215, 0, 0.1) 100%); border: 1rpx solid rgba(255, 215, 0, 0.4); } +.rank-podium-silver { background: linear-gradient(180deg, rgba(192, 192, 192, 0.3) 0%, rgba(192, 192, 192, 0.1) 100%); border: 1rpx solid rgba(192, 192, 192, 0.4); } +.rank-podium-bronze { background: linear-gradient(180deg, rgba(205, 127, 50, 0.3) 0%, rgba(205, 127, 50, 0.1) 100%); border: 1rpx solid rgba(205, 127, 50, 0.4); } + +.rank-podium-number { + font-size: 32rpx; + font-weight: 900; + color: rgba(255, 255, 255, 0.9); + text-shadow: 0 0 10rpx currentColor; +} + +.rank-crown-container { + position: absolute; + top: -55rpx; + left: 55%; + transform: translateX(20%) rotate(35deg) !important; + width: 110rpx; + height: 110rpx; + z-index: 40; + display: flex; + align-items: center; + justify-content: center; +} + +.rank-crown-icon { + font-size: 85rpx; + position: relative; + z-index: 2; + filter: drop-shadow(0 0 10rpx rgba(255, 215, 0, 0.8)); + animation: crownFloat 3s ease-in-out infinite; + transform: rotate(5deg); +} + +.rank-crown-glow { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: 200rpx; + height: 200rpx; + background: radial-gradient(circle, rgba(255, 215, 0, 0.3) 0%, rgba(255, 215, 0, 0.1) 40%, transparent 70%); + z-index: 0; + animation: glowPulse 2s ease-in-out infinite; +} + +.rank-avatar-frame { + position: relative; + width: 160rpx; + height: 160rpx; + margin: 0 auto 30rpx; + display: flex; + align-items: center; + justify-content: center; +} + +.rank-card--1 .rank-avatar-frame { width: 200rpx; height: 200rpx; } + +.rank-avatar-container { + position: relative; + width: 100%; + height: 100%; + border-radius: 50%; + overflow: hidden; + background: rgba(20, 20, 40, 0.8); + display: flex; + align-items: center; + justify-content: center; + border: 4rpx solid transparent; +} + +.rank-card--1 .rank-avatar-container { border-width: 6rpx; } + +.rank-frame-gold .rank-avatar-container { border-color: rgba(255, 215, 0, 0.3); filter: drop-shadow(0 0 30rpx rgba(255, 215, 0, 0.5)); } +.rank-frame-silver .rank-avatar-container { border-color: rgba(192, 192, 192, 0.3); filter: drop-shadow(0 0 25rpx rgba(192, 192, 192, 0.5)); } +.rank-frame-bronze .rank-avatar-container { border-color: rgba(205, 127, 50, 0.3); filter: drop-shadow(0 0 25rpx rgba(205, 127, 50, 0.5)); } + +.rank-avatar-image { + width: 100%; + height: 100%; + border-radius: 50%; + object-fit: cover; + position: relative; + z-index: 2; +} + +.rank-avatar-halo { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + border-radius: 50%; + z-index: 1; + pointer-events: none; + animation: haloRotate 10s linear infinite; +} + +.rank-halo-gold { width: 220rpx; height: 220rpx; background: radial-gradient(circle, rgba(255, 215, 0, 0.4) 0%, rgba(255, 215, 0, 0.2) 30%, transparent 70%); } +.rank-card--1 .rank-halo-gold { width: 260rpx; height: 260rpx; } +.rank-halo-silver { width: 200rpx; height: 200rpx; background: radial-gradient(circle, rgba(192, 192, 192, 0.4) 0%, rgba(192, 192, 192, 0.2) 30%, transparent 70%); } +.rank-halo-bronze { width: 200rpx; height: 200rpx; background: radial-gradient(circle, rgba(205, 127, 50, 0.4) 0%, rgba(205, 127, 50, 0.2) 30%, transparent 70%); } + +.rank-sparkle { + position: absolute; + width: 8rpx; + height: 8rpx; + background: #fff; + border-radius: 50%; + filter: blur(1rpx); + opacity: 0; + animation: sparkleTwinkle 3s ease-in-out infinite; +} + +.rank-card--1 .rank-sparkle { width: 10rpx; height: 10rpx; } + +.rank-badge { + position: absolute; + bottom: -10rpx; + right: -10rpx; + width: 60rpx; + height: 60rpx; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + font-size: 28rpx; + font-weight: 900; + color: #fff; + z-index: 10; + border: 3rpx solid rgba(255, 255, 255, 0.3); + box-shadow: 0 10rpx 30rpx rgba(0, 0, 0, 0.5); +} + +.rank-card--1 .rank-badge { width: 70rpx; height: 70rpx; font-size: 32rpx; bottom: -15rpx; right: -15rpx; } + +.rank-badge-gold { background: linear-gradient(135deg, #ffd700, #ff9800); } +.rank-badge-silver { background: linear-gradient(135deg, #c0c0c0, #808080); } +.rank-badge-bronze { background: linear-gradient(135deg, #cd7f32, #a0522d); } + +.rank-user-info-card { + background: rgba(15, 15, 35, 0.95); + border-radius: 25rpx; + padding: 30rpx 25rpx; + backdrop-filter: blur(20rpx); + border: 1rpx solid rgba(255, 255, 255, 0.1); + box-shadow: 0 20rpx 60rpx rgba(0, 0, 0, 0.8), inset 0 0 30rpx rgba(255, 255, 255, 0.05); + position: relative; + overflow: hidden; +} + +.rank-card--1 .rank-user-info-card { border-color: rgba(255, 215, 0, 0.3); background: rgba(20, 15, 5, 0.95); } +.rank-card--2 .rank-user-info-card { border-color: rgba(192, 192, 192, 0.3); background: rgba(25, 25, 35, 0.95); } +.rank-card--3 .rank-user-info-card { border-color: rgba(205, 127, 50, 0.3); background: rgba(30, 20, 10, 0.95); } + +.rank-user-name { + font-size: 32rpx; + font-weight: 700; + color: #fff; + display: block; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + text-align: center; +} + +.rank-card--1 .rank-user-name { color: #ffd700; text-shadow: 0 0 15rpx rgba(255, 215, 0, 0.7); font-size: 34rpx; } +.rank-card--2 .rank-user-name { color: #c0c0c0; text-shadow: 0 0 10rpx rgba(192, 192, 192, 0.7); } +.rank-card--3 .rank-user-name { color: #cd7f32; text-shadow: 0 0 10rpx rgba(205, 127, 50, 0.7); } + +.rank-info-body { + border-top: 1rpx solid rgba(255, 255, 255, 0.1); + padding-top: 15rpx; +} + +.rank-uid-row, .rank-income-row { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 12rpx; +} + +.rank-uid-label, .rank-income-label { + font-size: 24rpx; + color: rgba(255, 255, 255, 0.6); +} + +.rank-uid-value { + font-size: 24rpx; + color: rgba(255, 255, 255, 0.9); + font-family: 'Courier New', monospace; +} + +.rank-income-value { + display: flex; + align-items: baseline; +} + +.rank-currency { + font-size: 26rpx; + color: #4ade80; + margin-right: 4rpx; +} + +.rank-card--1 .rank-currency { color: #ffd700; } +.rank-card--2 .rank-currency { color: #c0c0c0; } +.rank-card--3 .rank-currency { color: #cd7f32; } + +.rank-amount { + font-size: 32rpx; + font-weight: 700; + color: #4ade80; + text-shadow: 0 0 10rpx rgba(74, 222, 128, 0.5); +} + +.rank-card--1 .rank-amount { color: #ffd700; text-shadow: 0 0 15rpx rgba(255, 215, 0, 0.7); } +.rank-card--2 .rank-amount { color: #c0c0c0; text-shadow: 0 0 10rpx rgba(192, 192, 192, 0.7); } +.rank-card--3 .rank-amount { color: #cd7f32; text-shadow: 0 0 10rpx rgba(205, 127, 50, 0.7); } + +.rank-unit { + font-size: 24rpx; + color: rgba(255, 255, 255, 0.6); + margin-left: 4rpx; +} + +.rank-section-title { + display: flex; + align-items: center; + justify-content: center; + margin-bottom: 40rpx; +} + +.rank-section-line { + flex: 1; + height: 2rpx; + background: linear-gradient(90deg, transparent, #00ffff, #ff00ff, #00ffff, transparent); +} + +.rank-section-text { + font-size: 32rpx; + font-weight: 700; + color: #00ffff; + margin: 0 30rpx; + text-shadow: 0 0 15rpx rgba(0, 255, 255, 0.5); +} + +.rank-list-item { + display: flex; + align-items: center; + background: rgba(20, 20, 40, 0.8); + border-radius: 25rpx; + padding: 25rpx 30rpx; + margin-bottom: 20rpx; + border: 1rpx solid rgba(0, 255, 255, 0.1); + backdrop-filter: blur(20rpx); + position: relative; + overflow: hidden; + transition: all 0.3s ease; + box-shadow: 0 10rpx 40rpx rgba(0, 0, 0, 0.6), inset 0 0 20rpx rgba(0, 255, 255, 0.05); +} + +.rank-list-item.flash { animation: cardFlash 0.4s ease-out; } + +.rank-number { + width: 80rpx; + text-align: center; + position: relative; + margin-right: 30rpx; + flex-shrink: 0; +} + +.rank-number-text { + font-size: 42rpx; + font-weight: 900; + color: rgba(255, 255, 255, 0.9); + position: relative; + z-index: 1; +} + +.rank-number-glow { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: 80rpx; + height: 80rpx; + background: radial-gradient(circle, rgba(0, 255, 255, 0.2) 0%, transparent 70%); + z-index: 0; +} + +.rank-list-avatar { + position: relative; + width: 100rpx; + height: 100rpx; + margin-right: 25rpx; + flex-shrink: 0; +} + +.rank-list-avatar-img { + width: 100%; + height: 100%; + border-radius: 50%; + object-fit: cover; + position: relative; + z-index: 2; + border: 3rpx solid rgba(0, 255, 255, 0.2); +} + +.rank-list-avatar-halo { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: 120rpx; + height: 120rpx; + background: radial-gradient(circle, rgba(0, 255, 255, 0.2) 0%, rgba(0, 255, 255, 0.1) 30%, transparent 70%); + z-index: 1; + border-radius: 50%; +} + +.rank-list-info { + flex: 1; + min-width: 0; +} + +.rank-list-name { + display: block; + font-size: 32rpx; + font-weight: 600; + color: #ffffff; + margin-bottom: 8rpx; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.rank-list-uid { + display: block; + font-size: 24rpx; + color: rgba(255, 255, 255, 0.6); + font-family: 'Courier New', monospace; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.rank-list-income { + position: relative; + flex-shrink: 0; + display: flex; + align-items: baseline; + margin-left: 20rpx; +} + +.rank-income-currency { + font-size: 28rpx; + color: #00ffff; + margin-right: 4rpx; + opacity: 0.9; +} + +.rank-income-amount { + font-size: 36rpx; + font-weight: 700; + color: #4ade80; + text-shadow: 0 0 10rpx rgba(74, 222, 128, 0.5); + white-space: nowrap; + line-height: 1; +} + +.rank-income-unit { + font-size: 24rpx; + color: rgba(255, 255, 255, 0.6); + margin-left: 4rpx; +} + +.rank-income-glow { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: 140rpx; + height: 70rpx; + background: radial-gradient(ellipse, rgba(74, 222, 128, 0.2) 0%, transparent 70%); + z-index: 0; +} + +.rank-empty-state { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 120rpx 0; + text-align: center; + position: relative; + z-index: 10; +} + +.rank-empty-icon { + font-size: 120rpx; + margin-bottom: 30rpx; + opacity: 0.5; + animation: iconFloat 3s ease-in-out infinite; +} + +.rank-empty-title { + font-size: 36rpx; + color: rgba(255, 255, 255, 0.8); + margin-bottom: 15rpx; +} + +.rank-empty-desc { + font-size: 28rpx; + color: rgba(255, 255, 255, 0.5); +} + +.rank-content { + position: relative; + z-index: 5; + margin-top: 10rpx; +} + +.rank-demo-notice { + background: rgba(15, 15, 35, 0.9); + border-radius: 25rpx; + padding: 30rpx; + margin-bottom: 40rpx; + position: relative; + z-index: 10; + border: 1rpx solid rgba(255, 0, 255, 0.3); + backdrop-filter: blur(20rpx); + display: flex; + align-items: center; + overflow: hidden; + box-shadow: 0 10rpx 40rpx rgba(0, 0, 0, 0.8), 0 0 30rpx rgba(255, 0, 255, 0.2); +} + +.rank-notice-icon { + font-size: 60rpx; + margin-right: 30rpx; + background: linear-gradient(45deg, #ff00ff, #00ffff); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + flex-shrink: 0; +} + +.rank-notice-content { flex: 1; } + +.rank-notice-title { + display: block; + font-size: 32rpx; + font-weight: 700; + color: #ff00ff; + margin-bottom: 10rpx; + text-shadow: 0 0 15rpx rgba(255, 0, 255, 0.5); +} + +.rank-notice-desc { + display: block; + font-size: 26rpx; + color: rgba(255, 255, 255, 0.7); + line-height: 1.4; +} diff --git a/pages/assessor/assessor.js b/pages/assessor/assessor.js index a62d4ed..55b975b 100644 --- a/pages/assessor/assessor.js +++ b/pages/assessor/assessor.js @@ -1,218 +1,163 @@ -// pages/assessor/assessor.js -// 只新增了两个字段的接收,其余所有代码未变 -import request from '../../utils/request.js'; -import { - isRoleStatusActive, - isCenterPageActive, - syncRoleStatuses, - ensureRoleOnCenterPage, -} from '../../utils/role-tab-bar.js'; -const app = getApp(); - -Page({ - data: { - isKaoheguan: false, - isLoading: false, - inviteCode: '', - statusText: '用户尚未注册或账号已被封禁', - uid: '', - avatarUrl: '', - icons: { - refresh: '', - tixian: '', - daofen: '', - jilu: '', - zhongxin: '', - bgImage: '' - }, - kaoheData: { - shenheZongshu: 0, - tongguoZongshu: 0, - yue: '0.00', - zonge: '0.00', - shenheZhuangtai: 0, - bankuaiList: [], - isRenzheng: false, // 🆕 是否认证 - pendingShenheCount: 0 // 🆕 审核中数量 - }, - lastRefreshTime: 0, - canRefresh: true, - planetPaused: { - top: false, - bottom: false, - left: false, - right: false - } - }, - - _planetTimers: {}, - - onLoad() { - wx.redirectTo({ url: '/pages/fighter/fighter' }); - return; - const ossUrl = app.globalData.ossImageUrl || ''; - const iconBase = 'beijing/kaohe/'; - this.setData({ - 'icons.refresh': ossUrl + iconBase + 'shuaxin.png', - 'icons.tixian': ossUrl + iconBase + 'tixian.png', - 'icons.daofen': ossUrl + iconBase + 'daofen.png', - 'icons.jilu': ossUrl + iconBase + 'jilu.png', - 'icons.zhongxin': ossUrl + iconBase + 'zhongxin.png', - 'icons.bgImage': ossUrl + iconBase + 'top_bg.jpg' - }); - - const kaoheguanstatus = wx.getStorageSync('kaoheguanstatus'); - if (isRoleStatusActive(kaoheguanstatus)) { - this.loadUserInfo(); - this.setData({ isKaoheguan: true }); - this.getKaoheguanInfo(); - ensureRoleOnCenterPage(this, 'kaoheguan'); - } else { - this.setData({ isKaoheguan: false }); - } - }, - - onReady() { - if (isCenterPageActive(this, 'isKaoheguan', 'kaoheguanstatus')) { - ensureRoleOnCenterPage(this, 'kaoheguan'); - } - }, - - onShow() { - if (isCenterPageActive(this, 'isKaoheguan', 'kaoheguanstatus')) { - if (!this.data.isKaoheguan) this.setData({ isKaoheguan: true }); - ensureRoleOnCenterPage(this, 'kaoheguan'); - this.refreshKaoheInfo(); - } - }, - - loadUserInfo() { - const uid = wx.getStorageSync('uid') || ''; - const touxiang = wx.getStorageSync('touxiang') || ''; - const ossUrl = app.globalData.ossImageUrl || ''; - const defaultAvatar = ossUrl + (app.globalData.morentouxiang || 'avatar/default.jpg'); - let avatarUrl = defaultAvatar; - if (touxiang) { - avatarUrl = ossUrl + (touxiang.startsWith('/') ? touxiang : '/' + touxiang); - } - this.setData({ uid, avatarUrl }); - }, - - async getKaoheguanInfo() { - this.setData({ isLoading: true }); - try { - const res = await request({ url: '/dengji/khgsx', method: 'POST' }); - if (res && res.data.code === 200) { - const data = res.data.data; - if (data.zhuangtai === 0 || data.zhuangtai === 2) { - wx.setStorageSync('kaoheguanstatus', 0); - this.setData({ isKaoheguan: false, isLoading: false, statusText: '用户尚未注册或账号已被封禁' }); - return; - } - wx.setStorageSync('kaoheguanstatus', 1); - this.setData({ - isKaoheguan: true, - isLoading: false, - kaoheData: { - shenheZongshu: data.shenhe_zongshu || 0, - tongguoZongshu: data.tongguo_zongshu || 0, - yue: data.yue || '0.00', - zonge: data.zonge || '0.00', - shenheZhuangtai: data.shenhe_zhuangtai || 0, - bankuaiList: data.bankuai_list || [], - isRenzheng: data.is_renzheng || false, // 🆕 - pendingShenheCount: data.pending_shenhe_count || 0 // 🆕 - } - }); - ensureRoleOnCenterPage(this, 'kaoheguan'); - } else { - wx.showToast({ title: res?.data?.msg || '获取信息失败', icon: 'none' }); - this.setData({ isLoading: false }); - } - } catch (error) { - console.error('获取考核官信息失败:', error); - wx.showToast({ title: '网络错误', icon: 'none' }); - this.setData({ isLoading: false }); - } - }, - - onInviteCodeInput(e) { - this.setData({ inviteCode: e.detail.value.trim() }); - }, - - async onRegister() { - const { inviteCode } = this.data; - if (!inviteCode) { - wx.showToast({ title: '请输入邀请码', icon: 'none', duration: 2000 }); - return; - } - if (inviteCode.length > 100) { - wx.showToast({ title: '邀请码不能超过100位', icon: 'none', duration: 2000 }); - return; - } - this.setData({ isLoading: true }); - try { - const res = await request({ - url: '/dengji/khgzc', - method: 'POST', - data: { inviteCode: inviteCode } - }); - if (res && res.data.code === 200) { - const data = res.data.data || {}; - syncRoleStatuses(data); - if (!Object.prototype.hasOwnProperty.call(data, 'kaoheguanstatus')) { - wx.setStorageSync('kaoheguanstatus', 1); - } - wx.showToast({ title: '注册成功', icon: 'success', duration: 2000 }); - this.loadUserInfo(); - await this.getKaoheguanInfo(); - ensureRoleOnCenterPage(this, 'kaoheguan'); - } else { - wx.showToast({ title: res?.data?.msg || '注册失败', icon: 'none', duration: 2000 }); - this.setData({ isLoading: false }); - } - } catch (error) { - console.error('注册失败:', error); - wx.showToast({ title: '网络错误', icon: 'none' }); - this.setData({ isLoading: false }); - } - }, - - refreshKaoheInfo() { - const now = Date.now(); - const lastTime = this.data.lastRefreshTime; - const canRefresh = this.data.canRefresh; - if (!canRefresh || (now - lastTime < 3000)) return; - this.setData({ lastRefreshTime: now, canRefresh: false }); - this.getKaoheguanInfo(); - setTimeout(() => this.setData({ canRefresh: true }), 3000); - }, - - goToWithdraw() { - const yue = this.data.kaoheData.yue || 0; - wx.navigateTo({ - url: `/pages/withdraw/withdraw?amount=${yue}&from=kaoheguan` - }); - }, - goToDafen() { wx.navigateTo({ url: '/pages/assess-score/assess-score' }); }, - goToJilu() { wx.navigateTo({ url: '/pages/assess-log/assess-log' }); }, - goToZhongxin() { wx.navigateTo({ url: '/pages/assess-center/assess-center' }); }, - - onPlanetTouch(e) { - const id = e.currentTarget.dataset.id; - if (this._planetTimers[id]) { - clearTimeout(this._planetTimers[id]); - this._planetTimers[id] = null; - } - const paused = `planetPaused.${id}`; - this.setData({ [paused]: true }); - }, - - onPlanetLeave(e) { - const id = e.currentTarget.dataset.id; - this._planetTimers[id] = setTimeout(() => { - const paused = `planetPaused.${id}`; - this.setData({ [paused]: false }); - }, 5000); - } -}); \ No newline at end of file +// pages/assessor/assessor.js +import { createPage, request, isCenterPageActive, ensureRoleOnCenterPage } from '../../utils/base-page.js'; + +Page(createPage({ + data: { + isKaoheguan: false, + inviteCode: '', + statusText: '用户尚未注册或账号已被封禁', + uid: '', + avatarUrl: '', + icons: { + refresh: '', + tixian: '', + daofen: '', + jilu: '', + zhongxin: '', + bgImage: '' + }, + kaoheData: { + shenheZongshu: 0, + tongguoZongshu: 0, + yue: '0.00', + zonge: '0.00', + shenheZhuangtai: 0, + bankuaiList: [], + isRenzheng: false, + pendingShenheCount: 0 + }, + planetPaused: { + top: false, + bottom: false, + left: false, + right: false + } + }, + + _planetTimers: {}, + + onLoad() { + wx.redirectTo({ url: '/pages/fighter/fighter' }); + return; + const icons = this.initImageUrls({ + 'icons.refresh': 'beijing/kaohe/shuaxin.png', + 'icons.tixian': 'beijing/kaohe/tixian.png', + 'icons.daofen': 'beijing/kaohe/daofen.png', + 'icons.jilu': 'beijing/kaohe/jilu.png', + 'icons.zhongxin': 'beijing/kaohe/zhongxin.png', + 'icons.bgImage': 'beijing/kaohe/top_bg.jpg' + }); + this.setData(icons); + + this.checkRoleStatus(); + if (this.data.isKaoheguan) { + this.getKaoheguanInfo(); + } + this.checkColdStartPopup('kaoheguanduan'); + }, + + onShow() { + if (isCenterPageActive(this, 'isKaoheguan', 'kaoheguanstatus')) { + if (!this.data.isKaoheguan) this.setData({ isKaoheguan: true }); + ensureRoleOnCenterPage(this, 'dashou'); + this.refreshKaoheInfo(); + } + }, + + async getKaoheguanInfo() { + this.setData({ isLoading: true }); + try { + const res = await request({ url: '/dengji/khgsx', method: 'POST' }); + if (res && res.data.code === 200) { + const data = res.data.data; + if (data.zhuangtai === 0 || data.zhuangtai === 2) { + wx.setStorageSync('kaoheguanstatus', 0); + this.setData({ isKaoheguan: false, isLoading: false, statusText: '用户尚未注册或账号已被封禁' }); + return; + } + wx.setStorageSync('kaoheguanstatus', 1); + this.setData({ + isKaoheguan: true, + isLoading: false, + kaoheData: { + shenheZongshu: data.shenhe_zongshu || 0, + tongguoZongshu: data.tongguo_zongshu || 0, + yue: data.yue || '0.00', + zonge: data.zonge || '0.00', + shenheZhuangtai: data.shenhe_zhuangtai || 0, + bankuaiList: data.bankuai_list || [], + isRenzheng: data.is_renzheng || false, + pendingShenheCount: data.pending_shenhe_count || 0 + } + }); + ensureRoleOnCenterPage(this, 'dashou'); + } else { + wx.showToast({ title: res?.data?.msg || '获取信息失败', icon: 'none' }); + this.setData({ isLoading: false }); + } + } catch (error) { + console.error('获取考核官信息失败:', error); + wx.showToast({ title: '网络错误', icon: 'none' }); + this.setData({ isLoading: false }); + } + }, + + onInviteCodeInput(e) { + this.setData({ inviteCode: e.detail.value.trim() }); + }, + + async onRegister() { + this.registerWithInviteCode({ + inviteCode: this.data.inviteCode, + apiUrl: '/dengji/khgzc', + role: 'dashou', + statusKey: 'kaoheguanstatus', + successMsg: '注册成功', + onSuccess: async () => { + this.loadUserInfo(); + await this.getKaoheguanInfo(); + ensureRoleOnCenterPage(this, 'dashou'); + } + }); + }, + + refreshKaoheInfo() { + this.throttledRefresh(this.getKaoheguanInfo); + }, + + goToWithdraw() { + const yue = this.data.kaoheData.yue || 0; + wx.navigateTo({ + url: `/pages/withdraw/withdraw?amount=${yue}&from=kaoheguan` + }); + }, + goToDafen() { wx.navigateTo({ url: '/pages/assess-score/assess-score' }); }, + goToJilu() { wx.navigateTo({ url: '/pages/assess-log/assess-log' }); }, + goToZhongxin() { wx.navigateTo({ url: '/pages/assess-center/assess-center' }); }, + + onPlanetTouch(e) { + const id = e.currentTarget.dataset.id; + if (this._planetTimers[id]) { + clearTimeout(this._planetTimers[id]); + this._planetTimers[id] = null; + } + const paused = `planetPaused.${id}`; + this.setData({ [paused]: true }); + }, + + onPlanetLeave(e) { + const id = e.currentTarget.dataset.id; + this._planetTimers[id] = setTimeout(() => { + const paused = `planetPaused.${id}`; + this.setData({ [paused]: false }); + }, 5000); + } +}, { + roleConfig: { + role: 'dashou', + statusKey: 'kaoheguanstatus', + dataFlag: 'isKaoheguan', + pageId: 'kaoheguanduan', + apiUrl: '/dengji/khgsx' + } +})); diff --git a/pages/category/category.js b/pages/category/category.js index a95e8d7..abd8f65 100644 --- a/pages/category/category.js +++ b/pages/category/category.js @@ -1,331 +1,321 @@ -// pages/category/category.js -const app = getApp() - -Page({ - data: { - ossImageUrl: '', - searchText: '', - showSearchResult: false, - searchResults: [], - isSearching: false, - shangpinleixing: [], - shangpinzhuanqu: [], - shangpinliebiao: [], - selectedLeixingId: null, - selectedZhuanquId: null, - filteredZhuanquList: [], - filteredShangpinList: [], - zhuanquByLeixing: {}, - shangpinByZhuanqu: {}, - zhuanquGoodsCount: {}, - isLoading: false, - hasInitialized: false - }, - - onLoad(options) { - this.setData({ - ossImageUrl: app.globalData.ossImageUrl - }) - this.initPageData() - }, - - onShow() { - this.registerNotificationComponent(); - }, - - registerNotificationComponent() { - const app = getApp(); - const notificationComp = this.selectComponent('#global-notification'); - if (notificationComp && notificationComp.showNotification) { - app.globalData.globalNotification = { - show: (data) => notificationComp.showNotification(data), - hide: () => notificationComp.hideNotification() - }; - } - }, - - initPageData() { - const that = this - this.setData({ isLoading: true }) - if (app.globalData.shangpinleixing && app.globalData.shangpinleixing.length > 0) { - this.initFromGlobalData() - } else { - this.loadAllShangpinData() - } - }, - - initFromGlobalData() { - const leixingList = app.globalData.shangpinleixing || [] - const zhuanquList = app.globalData.shangpinzhuanqu || [] - const shangpinList = app.globalData.shangpinliebiao || [] - - const zhuanquByLeixing = {} - const shangpinByZhuanqu = {} - const zhuanquGoodsCount = {} - - zhuanquList.forEach(zhuanqu => { - const leixingId = zhuanqu.leixing_id - if (!zhuanquByLeixing[leixingId]) { - zhuanquByLeixing[leixingId] = [] - } - zhuanquByLeixing[leixingId].push(zhuanqu) - }) - - shangpinList.forEach(shangpin => { - const zhuanquId = shangpin.zhuanqu_id - if (zhuanquId) { - if (!shangpinByZhuanqu[zhuanquId]) { - shangpinByZhuanqu[zhuanquId] = [] - zhuanquGoodsCount[zhuanquId] = 0 - } - const jiage = parseFloat(shangpin.jiage) || 0 - const priceInteger = Math.floor(jiage) - let priceDecimal = '00' - const decimalPart = (jiage - priceInteger).toFixed(2) - if (decimalPart > 0) { - priceDecimal = decimalPart.toString().split('.')[1] - } - shangpinByZhuanqu[zhuanquId].push({ - id: shangpin.id, - biaoqian: shangpin.biaoqian, - jiage: jiage, - priceInteger: priceInteger, - priceDecimal: priceDecimal, - tupian_url: shangpin.tupian_url, - duiwai_xiaoliang: shangpin.duiwai_xiaoliang || 0 - }) - zhuanquGoodsCount[zhuanquId] += 1 - } - }) - - const firstLeixing = leixingList[0] - let firstZhuanquId = null - if (firstLeixing && zhuanquByLeixing[firstLeixing.id]) { - firstZhuanquId = zhuanquByLeixing[firstLeixing.id][0]?.id || null - } - - this.setData({ - shangpinleixing: leixingList, - shangpinzhuanqu: zhuanquList, - shangpinliebiao: shangpinList, - zhuanquByLeixing: zhuanquByLeixing, - shangpinByZhuanqu: shangpinByZhuanqu, - zhuanquGoodsCount: zhuanquGoodsCount, - selectedLeixingId: firstLeixing?.id || null, - selectedZhuanquId: firstZhuanquId - }, () => { - this.filterData() - this.setData({ isLoading: false, hasInitialized: true }) - }) - }, - - loadAllShangpinData() { - const that = this - wx.request({ - url: app.globalData.apiBaseUrl + '/shangpin/shangpinhuoqu/', - method: 'POST', - header: { - 'content-type': 'application/json' - }, - success(res) { - if (res.statusCode === 200 && res.data) { - const data = res.data - app.globalData.shangpinleixing = data.shangpinleixing || [] - app.globalData.shangpinzhuanqu = data.shangpinzhuanqu || [] - app.globalData.shangpinliebiao = data.shangpinliebiao || [] - that.initFromGlobalData() - } else { - that.handleLoadError() - } - }, - fail(error) { - console.error('加载商品数据失败:', error) - that.handleLoadError() - } - }) - }, - - handleLoadError() { - wx.showToast({ - title: '加载失败,请重试', - icon: 'none' - }) - this.setData({ isLoading: false }) - }, - - filterData() { - const { selectedLeixingId, selectedZhuanquId, zhuanquByLeixing, shangpinByZhuanqu } = this.data - const filteredZhuanquList = zhuanquByLeixing[selectedLeixingId] || [] - let filteredShangpinList = [] - if (selectedZhuanquId) { - filteredShangpinList = shangpinByZhuanqu[selectedZhuanquId] || [] - } else if (filteredZhuanquList.length > 0) { - const firstZhuanquId = filteredZhuanquList[0].id - filteredShangpinList = shangpinByZhuanqu[firstZhuanquId] || [] - this.setData({ selectedZhuanquId: firstZhuanquId }) - } - this.setData({ - filteredZhuanquList: filteredZhuanquList, - filteredShangpinList: filteredShangpinList - }) - }, - - selectLeixing(e) { - const leixingId = e.currentTarget.dataset.id - if (this.data.selectedLeixingId === leixingId) { - return - } - const zhuanquList = this.data.zhuanquByLeixing[leixingId] || [] - const firstZhuanquId = zhuanquList.length > 0 ? zhuanquList[0].id : null - this.setData({ - selectedLeixingId: leixingId, - selectedZhuanquId: firstZhuanquId - }, () => { - this.filterData() - }) - }, - - selectZhuanqu(e) { - const zhuanquId = e.currentTarget.dataset.id - if (this.data.selectedZhuanquId === zhuanquId) { - return - } - this.setData({ - selectedZhuanquId: zhuanquId - }, () => { - const filteredShangpinList = this.data.shangpinByZhuanqu[zhuanquId] || [] - this.setData({ - filteredShangpinList: filteredShangpinList - }) - }) - }, - - onSearchInput(e) { - this.setData({ - searchText: e.detail.value - }) - }, - - onSearchTap() { - const searchText = this.data.searchText.trim() - if (!searchText) { - wx.showToast({ - title: '请输入搜索内容', - icon: 'none' - }) - return - } - this.startSearch(searchText) - }, - - onSearchConfirm(e) { - const searchText = e.detail.value.trim() - if (!searchText) { - wx.showToast({ - title: '请输入搜索内容', - icon: 'none' - }) - return - } - this.setData({ - searchText: searchText - }) - this.startSearch(searchText) - }, - - startSearch(searchText) { - this.setData({ - isSearching: true, - showSearchResult: true - }) - const allShangpin = this.data.shangpinliebiao - const results = [] - for (const item of allShangpin) { - if (item.biaoqian && item.biaoqian.toLowerCase().includes(searchText.toLowerCase())) { - const jiage = parseFloat(item.jiage) || 0 - const priceInteger = Math.floor(jiage) - let priceDecimal = '00' - const decimalPart = (jiage - priceInteger).toFixed(2) - if (decimalPart > 0) { - priceDecimal = decimalPart.toString().split('.')[1] - } - results.push({ - id: item.id, - biaoqian: item.biaoqian, - jiage: jiage, - priceInteger: priceInteger, - priceDecimal: priceDecimal, - tupian_url: item.tupian_url, - leixing_name: this.getLeixingName(item.leixing_id), - zhuanqu_name: this.getZhuanquName(item.zhuanqu_id) - }) - } - } - this.setData({ - searchResults: results, - isSearching: false - }) - }, - - onClearSearch() { - this.setData({ - searchText: '', - showSearchResult: false, - searchResults: [] - }) - }, - - getLeixingName(leixingId) { - const leixingList = this.data.shangpinleixing - for (const item of leixingList) { - if (item.id === leixingId) { - return item.jieshao - } - } - return '' - }, - - getZhuanquName(zhuanquId) { - const zhuanquList = this.data.shangpinzhuanqu - for (const item of zhuanquList) { - if (item.id === zhuanquId) { - return item.mingzi - } - } - return '' - }, - - goToDetail(e) { - const shangpinId = e.currentTarget.dataset.id - if (!shangpinId) return - wx.navigateTo({ - url: `/pages/product-detail/product-detail?id=${shangpinId}` - }) - }, - - onRefresh() { - wx.showLoading({ - title: '刷新中...', - mask: true - }) - app.globalData.shangpinleixing = [] - app.globalData.shangpinzhuanqu = [] - app.globalData.shangpinliebiao = [] - this.loadAllShangpinData() - setTimeout(() => { - wx.hideLoading() - wx.showToast({ - title: '刷新成功', - icon: 'success', - duration: 1500 - }) - }, 1000) - }, - - onShareAppMessage() { - return { - title: '阿龙电竞 - 商品分类', - path: '/pages/category/category' - } - } -}) +// pages/category/category.js +const app = getApp() +import { createPage } from '../../utils/base-page.js'; + +Page(createPage({ + data: { + ossImageUrl: '', + searchText: '', + showSearchResult: false, + searchResults: [], + isSearching: false, + shangpinleixing: [], + shangpinzhuanqu: [], + shangpinliebiao: [], + selectedLeixingId: null, + selectedZhuanquId: null, + filteredZhuanquList: [], + filteredShangpinList: [], + zhuanquByLeixing: {}, + shangpinByZhuanqu: {}, + zhuanquGoodsCount: {}, + isLoading: false, + hasInitialized: false + }, + + onLoad(options) { + this.setData({ + ossImageUrl: app.globalData.ossImageUrl + }) + this.initPageData() + }, + + onShow() { + this.registerNotificationComponent(); + }, + + initPageData() { + const that = this + this.setData({ isLoading: true }) + if (app.globalData.shangpinleixing && app.globalData.shangpinleixing.length > 0) { + this.initFromGlobalData() + } else { + this.loadAllShangpinData() + } + }, + + initFromGlobalData() { + const leixingList = app.globalData.shangpinleixing || [] + const zhuanquList = app.globalData.shangpinzhuanqu || [] + const shangpinList = app.globalData.shangpinliebiao || [] + + const zhuanquByLeixing = {} + const shangpinByZhuanqu = {} + const zhuanquGoodsCount = {} + + zhuanquList.forEach(zhuanqu => { + const leixingId = zhuanqu.leixing_id + if (!zhuanquByLeixing[leixingId]) { + zhuanquByLeixing[leixingId] = [] + } + zhuanquByLeixing[leixingId].push(zhuanqu) + }) + + shangpinList.forEach(shangpin => { + const zhuanquId = shangpin.zhuanqu_id + if (zhuanquId) { + if (!shangpinByZhuanqu[zhuanquId]) { + shangpinByZhuanqu[zhuanquId] = [] + zhuanquGoodsCount[zhuanquId] = 0 + } + const jiage = parseFloat(shangpin.jiage) || 0 + const priceInteger = Math.floor(jiage) + let priceDecimal = '00' + const decimalPart = (jiage - priceInteger).toFixed(2) + if (decimalPart > 0) { + priceDecimal = decimalPart.toString().split('.')[1] + } + shangpinByZhuanqu[zhuanquId].push({ + id: shangpin.id, + biaoqian: shangpin.biaoqian, + jiage: jiage, + priceInteger: priceInteger, + priceDecimal: priceDecimal, + tupian_url: shangpin.tupian_url, + duiwai_xiaoliang: shangpin.duiwai_xiaoliang || 0 + }) + zhuanquGoodsCount[zhuanquId] += 1 + } + }) + + const firstLeixing = leixingList[0] + let firstZhuanquId = null + if (firstLeixing && zhuanquByLeixing[firstLeixing.id]) { + firstZhuanquId = zhuanquByLeixing[firstLeixing.id][0]?.id || null + } + + this.setData({ + shangpinleixing: leixingList, + shangpinzhuanqu: zhuanquList, + shangpinliebiao: shangpinList, + zhuanquByLeixing: zhuanquByLeixing, + shangpinByZhuanqu: shangpinByZhuanqu, + zhuanquGoodsCount: zhuanquGoodsCount, + selectedLeixingId: firstLeixing?.id || null, + selectedZhuanquId: firstZhuanquId + }, () => { + this.filterData() + this.setData({ isLoading: false, hasInitialized: true }) + }) + }, + + loadAllShangpinData() { + const that = this + wx.request({ + url: app.globalData.apiBaseUrl + '/shangpin/shangpinhuoqu/', + method: 'POST', + header: { + 'content-type': 'application/json' + }, + success(res) { + if (res.statusCode === 200 && res.data) { + const data = res.data + app.globalData.shangpinleixing = data.shangpinleixing || [] + app.globalData.shangpinzhuanqu = data.shangpinzhuanqu || [] + app.globalData.shangpinliebiao = data.shangpinliebiao || [] + that.initFromGlobalData() + } else { + that.handleLoadError() + } + }, + fail(error) { + console.error('加载商品数据失败:', error) + that.handleLoadError() + } + }) + }, + + handleLoadError() { + wx.showToast({ + title: '加载失败,请重试', + icon: 'none' + }) + this.setData({ isLoading: false }) + }, + + filterData() { + const { selectedLeixingId, selectedZhuanquId, zhuanquByLeixing, shangpinByZhuanqu } = this.data + const filteredZhuanquList = zhuanquByLeixing[selectedLeixingId] || [] + let filteredShangpinList = [] + if (selectedZhuanquId) { + filteredShangpinList = shangpinByZhuanqu[selectedZhuanquId] || [] + } else if (filteredZhuanquList.length > 0) { + const firstZhuanquId = filteredZhuanquList[0].id + filteredShangpinList = shangpinByZhuanqu[firstZhuanquId] || [] + this.setData({ selectedZhuanquId: firstZhuanquId }) + } + this.setData({ + filteredZhuanquList: filteredZhuanquList, + filteredShangpinList: filteredShangpinList + }) + }, + + selectLeixing(e) { + const leixingId = e.currentTarget.dataset.id + if (this.data.selectedLeixingId === leixingId) { + return + } + const zhuanquList = this.data.zhuanquByLeixing[leixingId] || [] + const firstZhuanquId = zhuanquList.length > 0 ? zhuanquList[0].id : null + this.setData({ + selectedLeixingId: leixingId, + selectedZhuanquId: firstZhuanquId + }, () => { + this.filterData() + }) + }, + + selectZhuanqu(e) { + const zhuanquId = e.currentTarget.dataset.id + if (this.data.selectedZhuanquId === zhuanquId) { + return + } + this.setData({ + selectedZhuanquId: zhuanquId + }, () => { + const filteredShangpinList = this.data.shangpinByZhuanqu[zhuanquId] || [] + this.setData({ + filteredShangpinList: filteredShangpinList + }) + }) + }, + + onSearchInput(e) { + this.setData({ + searchText: e.detail.value + }) + }, + + onSearchTap() { + const searchText = this.data.searchText.trim() + if (!searchText) { + wx.showToast({ + title: '请输入搜索内容', + icon: 'none' + }) + return + } + this.startSearch(searchText) + }, + + onSearchConfirm(e) { + const searchText = e.detail.value.trim() + if (!searchText) { + wx.showToast({ + title: '请输入搜索内容', + icon: 'none' + }) + return + } + this.setData({ + searchText: searchText + }) + this.startSearch(searchText) + }, + + startSearch(searchText) { + this.setData({ + isSearching: true, + showSearchResult: true + }) + const allShangpin = this.data.shangpinliebiao + const results = [] + for (const item of allShangpin) { + if (item.biaoqian && item.biaoqian.toLowerCase().includes(searchText.toLowerCase())) { + const jiage = parseFloat(item.jiage) || 0 + const priceInteger = Math.floor(jiage) + let priceDecimal = '00' + const decimalPart = (jiage - priceInteger).toFixed(2) + if (decimalPart > 0) { + priceDecimal = decimalPart.toString().split('.')[1] + } + results.push({ + id: item.id, + biaoqian: item.biaoqian, + jiage: jiage, + priceInteger: priceInteger, + priceDecimal: priceDecimal, + tupian_url: item.tupian_url, + leixing_name: this.getLeixingName(item.leixing_id), + zhuanqu_name: this.getZhuanquName(item.zhuanqu_id) + }) + } + } + this.setData({ + searchResults: results, + isSearching: false + }) + }, + + onClearSearch() { + this.setData({ + searchText: '', + showSearchResult: false, + searchResults: [] + }) + }, + + getLeixingName(leixingId) { + const leixingList = this.data.shangpinleixing + for (const item of leixingList) { + if (item.id === leixingId) { + return item.jieshao + } + } + return '' + }, + + getZhuanquName(zhuanquId) { + const zhuanquList = this.data.shangpinzhuanqu + for (const item of zhuanquList) { + if (item.id === zhuanquId) { + return item.mingzi + } + } + return '' + }, + + goToDetail(e) { + const shangpinId = e.currentTarget.dataset.id + if (!shangpinId) return + wx.navigateTo({ + url: `/pages/product-detail/product-detail?id=${shangpinId}` + }) + }, + + onRefresh() { + wx.showLoading({ + title: '刷新中...', + mask: true + }) + app.globalData.shangpinleixing = [] + app.globalData.shangpinzhuanqu = [] + app.globalData.shangpinliebiao = [] + this.loadAllShangpinData() + setTimeout(() => { + wx.hideLoading() + wx.showToast({ + title: '刷新成功', + icon: 'success', + duration: 1500 + }) + }, 1000) + }, + + onShareAppMessage() { + return { + title: '阿龙电竞 - 商品分类', + path: '/pages/category/category' + } + } +})) diff --git a/pages/chat/chat.wxml b/pages/chat/chat.wxml index 4a6b5f3..5dd1fb6 100644 --- a/pages/chat/chat.wxml +++ b/pages/chat/chat.wxml @@ -1,31 +1,31 @@ - - 加载更早消息... + 加载更早消息... - - {{item.formattedTime}} - - - + {{item.formattedTime}} + + + - {{item.payload.text}} - - - [订单] - ID: {{item.payload.orderId}} + {{item.payload.text}} + + + [订单] + ID: {{item.payload.orderId}} - + - + {{item.read ? '已读' : '未读'}} @@ -33,53 +33,53 @@ - - - - - 😀 - 表情 + + + + + 😀 + 表情 - - 📷 - 图片 + + 📷 + 图片 - - 📋 - 订单 + + 📋 + 订单 - - - - + + + + - {{item}} + {{item}} - - - + + + - - - 发送 - - + + + 发送 + + - - - {{detailText}} - + + + {{detailText}} + diff --git a/pages/chat/chat.wxss b/pages/chat/chat.wxss index 21a4b4e..1826010 100644 --- a/pages/chat/chat.wxss +++ b/pages/chat/chat.wxss @@ -1,98 +1 @@ -.chat-page { width:100%; height:100vh; background:#f5f5f5; } - -.msg-list { - position: absolute; - left:0; right:0; - padding: 0 20rpx; - box-sizing: border-box; - overflow-y: auto; -} - -.loading-more { text-align:center; font-size:24rpx; color:#999; padding:20rpx 0; } -.time-tag { text-align:center; margin:28rpx 0; font-size:24rpx; color:#b0b0b0; } -.msg-wrapper { margin-bottom:12rpx; } -.msg-row { display:flex; align-items:flex-start; margin-bottom:4rpx; } -.right { justify-content:flex-end; } -.left { justify-content:flex-start; } -.avatar { width:76rpx; height:76rpx; border-radius:50%; background:#e0e0e0; margin:0 12rpx; flex-shrink:0; } -.bubble { max-width:70%; padding:16rpx 20rpx; border-radius:16rpx; position:relative; word-break:break-all; font-size:30rpx; line-height:1.5; } -.bubble-left { background:#fff; border-top-left-radius:4rpx; } -.bubble-right { background:#00aaff; color:#fff; border-top-right-radius:4rpx; } -.msg-text { color:inherit; } -.msg-image { max-width:240rpx; border-radius:10rpx; } -.order-bubble { background:#fff7e0; padding:12rpx; border-radius:8rpx; } -.order-label { font-size:24rpx; color:#e6a23c; font-weight:600; } -.order-info { font-size:24rpx; color:#333; margin:6rpx 0; } - -.read-tag { - font-size:20rpx; - color:#888; - text-align:right; - padding:4rpx 12rpx 0 0; - margin-top: -4rpx; -} - -.input-area { - position: fixed; - left:0; right:0; - background:#f7f7f7; - border-top:1rpx solid #ddd; - z-index:10; -} - -.plus-panel { - position: absolute; - bottom:100%; left:0; right:0; - background:#fff; - border-top:1rpx solid #e0e0e0; - padding: 20rpx; - transform: translateY(100%); - opacity:0; - transition: transform 0.3s, opacity 0.3s; - pointer-events:none; - z-index:9; -} -.plus-panel.show { transform: translateY(0); opacity:1; pointer-events:auto; } -.plus-grid { display: flex; justify-content: space-around; } -.plus-item { display: flex; flex-direction: column; align-items: center; width: 120rpx; } -.plus-icon { font-size: 60rpx; } -.plus-label { font-size: 24rpx; color: #666; margin-top: 8rpx; } - -.emoji-mask { position:fixed; top:0; left:0; right:0; bottom:0; background:transparent; z-index:8; } -.emoji-panel { - position: absolute; bottom:100%; left:0; right:0; - background:#fff; border-top:1rpx solid #e0e0e0; - height:380rpx; - transform: translateY(100%); opacity:0; transition:0.3s; - pointer-events:none; z-index:9; -} -.emoji-panel.show { transform:translateY(0); opacity:1; pointer-events:auto; } -.emoji-scroll { height:100%; } -.emoji-grid { display:flex; flex-wrap:wrap; padding:20rpx 10rpx; } -.emoji-item { width:14.28%; text-align:center; padding:16rpx 0; font-size:44rpx; } - -.pending-image { display:flex; align-items:center; padding:10rpx 20rpx; background:#fff; } -.pending-thumb { width:80rpx; height:80rpx; border-radius:8rpx; } -.pending-remove { font-size:30rpx; color:#999; margin-left:10rpx; } - -.write-row { display:flex; padding:0 20rpx 10rpx; align-items:center; } -.text-input { - flex:1; height:76rpx; background:#fff; border-radius:12rpx; - padding:0 20rpx; font-size:30rpx; margin-right:12rpx; -} -.send-btn { - width:120rpx; height:76rpx; background:#00aaff; color:#fff; - font-size:28rpx; font-weight:500; border-radius:12rpx; - display:flex; align-items:center; justify-content:center; -} -.plus-btn { - width:56rpx; height:56rpx; - display:flex; align-items:center; justify-content:center; - margin-left: 10rpx; -} -.plus-icon-single { font-size:46rpx; color:#00aaff; } - -.detail-modal { position:fixed; top:0; left:0; right:0; bottom:0; background:rgba(0,0,0,0.7); z-index:999; display:flex; align-items:center; justify-content:center; } -.detail-content { width:80%; max-height:80%; background:#fff; border-radius:16rpx; padding:40rpx; } -.detail-text { font-size:32rpx; line-height:1.6; word-break:break-all; overflow-y:auto; } -.copy-btn { width:100%; height:80rpx; background:#00aaff; color:#fff; border-radius:12rpx; margin-top:20rpx; font-size:30rpx; } \ No newline at end of file +/* 使用全局 chat-* 样式 */ \ No newline at end of file diff --git a/pages/cs-chat/cs-chat.wxml b/pages/cs-chat/cs-chat.wxml index 0b64b0c..f805235 100644 --- a/pages/cs-chat/cs-chat.wxml +++ b/pages/cs-chat/cs-chat.wxml @@ -1,37 +1,37 @@ - - 加载更早消息... + 加载更早消息... - - {{item.formattedTime}} - + + {{item.formattedTime}} + - - + - {{item.payload.text}} - - - 📋 订单消息 - + {{item.payload.text}} + + + 📋 订单消息 + 订单号:{{item.payload.dingdan_id || '无'}} 服务:{{item.payload.jieshao || ''}} - ¥{{item.payload.jine}} + ¥{{item.payload.jine}} - + - {{item.payload.text || '[未知消息]'}} + {{item.payload.text || '[未知消息]'}} - + @@ -40,70 +40,70 @@ - - - - - 😀 - 表情 + + + + + 😀 + 表情 - - 📷 - 图片 + + 📷 + 图片 - - 📋 - 订单 + + 📋 + 订单 - - - - + + + + - {{item}} + {{item}} - - - + + + - - - 发送 - - + + + 发送 + + - - - {{detailText}} - + + + {{detailText}} + - - - 订单详情 - 订单号:{{detailOrder.dingdan_id}} - 服务描述:{{detailOrder.jieshao}} - 金额:¥{{detailOrder.jine}} - 类型:{{detailOrder.leixing || '无'}} - 时间:{{detailOrder.shijian || '无'}} - + + + 订单详情 + 订单号:{{detailOrder.dingdan_id}} + 服务描述:{{detailOrder.jieshao}} + 金额:¥{{detailOrder.jine}} + 类型:{{detailOrder.leixing || '无'}} + 时间:{{detailOrder.shijian || '无'}} + - \ No newline at end of file + diff --git a/pages/cs-chat/cs-chat.wxss b/pages/cs-chat/cs-chat.wxss index 00e37b0..4e66705 100644 --- a/pages/cs-chat/cs-chat.wxss +++ b/pages/cs-chat/cs-chat.wxss @@ -1,67 +1 @@ -.chat-page { width:100%; height:100vh; background:#f5f5f5; } -.msg-list { position:absolute; left:0; right:0; padding:0 20rpx; box-sizing:border-box; overflow-y:auto; } -.loading-more { text-align:center; font-size:24rpx; color:#999; padding:20rpx 0; } -.time-tag { text-align:center; margin:28rpx 0; font-size:24rpx; color:#b0b0b0; } -.msg-wrapper { margin-bottom:12rpx; } -.msg-row { display:flex; align-items:flex-start; margin-bottom:4rpx; } -.right { justify-content:flex-end; } -.left { justify-content:flex-start; } -.avatar { width:76rpx; height:76rpx; border-radius:50%; background:#e0e0e0; margin:0 12rpx; flex-shrink:0; } -.bubble { max-width:70%; padding:16rpx 20rpx; border-radius:16rpx; position:relative; word-break:break-all; font-size:30rpx; line-height:1.5; } -.bubble-left { background:#fff; border-top-left-radius:4rpx; } -.bubble-right { background:#00aaff; color:#fff; border-top-right-radius:4rpx; } -.msg-text { color:inherit; } -.msg-image { max-width:240rpx; border-radius:10rpx; } - -/* 已读/未读标签:靠右显示 */ -.read-tag { - display: flex; - justify-content: flex-end; - font-size: 20rpx; - color: #888; - padding: 4rpx 12rpx 0 0; - margin-top: 2rpx; -} - -/* 订单气泡 */ -.order-bubble { background:#fff7e0; padding:12rpx; border-radius:8rpx; } -.order-label { font-size:24rpx; color:#e6a23c; font-weight:600; margin-bottom:8rpx; } -.order-brief { font-size:24rpx; color:#333; line-height:1.6; } -.order-price { color:#e6a23c; font-weight:bold; } -.order-detail-btn { margin-top:10rpx; background:#e6a23c; color:#fff; border-radius:8rpx; } - -/* 订单详情弹窗 */ -.order-detail-mask { position:fixed; top:0; left:0; right:0; bottom:0; background:rgba(0,0,0,0.6); z-index:999; display:flex; justify-content:center; align-items:center; } -.order-detail-panel { width:80%; background:#fff; border-radius:20rpx; padding:30rpx; } -.order-detail-title { font-size:34rpx; font-weight:bold; margin-bottom:20rpx; color:#333; } -.order-detail-item { font-size:28rpx; margin:16rpx 0; color:#555; } -.order-detail-close-btn { width:100%; height:80rpx; background:#00aaff; color:#fff; border-radius:12rpx; margin-top:30rpx; font-size:30rpx; } - -/* 底部输入区域 */ -.input-area { position:fixed; left:0; right:0; background:#f7f7f7; border-top:1rpx solid #ddd; z-index:10; } -.plus-panel { position:absolute; bottom:100%; left:0; right:0; background:#fff; border-top:1rpx solid #e0e0e0; padding:20rpx; transform:translateY(100%); opacity:0; transition:0.3s; pointer-events:none; z-index:9; } -.plus-panel.show { transform:translateY(0); opacity:1; pointer-events:auto; } -.plus-grid { display:flex; justify-content:space-around; } -.plus-item { display:flex; flex-direction:column; align-items:center; width:120rpx; } -.plus-icon { font-size:60rpx; } -.plus-label { font-size:24rpx; color:#666; margin-top:8rpx; } -.emoji-mask { position:fixed; top:0; left:0; right:0; bottom:0; background:transparent; z-index:8; } -.emoji-panel { position:absolute; bottom:100%; left:0; right:0; background:#fff; border-top:1rpx solid #e0e0e0; height:380rpx; transform:translateY(100%); opacity:0; transition:0.3s; pointer-events:none; z-index:9; } -.emoji-panel.show { transform:translateY(0); opacity:1; pointer-events:auto; } -.emoji-scroll { height:100%; } -.emoji-grid { display:flex; flex-wrap:wrap; padding:20rpx 10rpx; } -.emoji-item { width:14.28%; text-align:center; padding:16rpx 0; font-size:44rpx; } -.pending-image { display:flex; align-items:center; padding:10rpx 20rpx; background:#fff; } -.pending-thumb { width:80rpx; height:80rpx; border-radius:8rpx; } -.pending-remove { font-size:30rpx; color:#999; margin-left:10rpx; } -.write-row { display:flex; padding:0 20rpx 10rpx; align-items:center; } -.text-input { flex:1; height:76rpx; background:#fff; border-radius:12rpx; padding:0 20rpx; font-size:30rpx; margin-right:12rpx; } -.send-btn { width:120rpx; height:76rpx; background:#00aaff; color:#fff; font-size:28rpx; font-weight:500; border-radius:12rpx; display:flex; align-items:center; justify-content:center; } -.plus-btn { width:56rpx; height:56rpx; display:flex; align-items:center; justify-content:center; margin-left:10rpx; } -.plus-icon-single { font-size:46rpx; color:#00aaff; } - -/* 双击放大文本弹窗 */ -.detail-modal { position:fixed; top:0; left:0; right:0; bottom:0; background:rgba(0,0,0,0.7); z-index:999; display:flex; align-items:center; justify-content:center; } -.detail-content { width:80%; max-height:80%; background:#fff; border-radius:16rpx; padding:40rpx; } -.detail-text { font-size:32rpx; line-height:1.6; word-break:break-all; overflow-y:auto; } -.copy-btn { width:100%; height:80rpx; background:#00aaff; color:#fff; border-radius:12rpx; margin-top:20rpx; font-size:30rpx; } \ No newline at end of file +/* 使用全局 chat-* 样式 */ diff --git a/pages/fighter-msg/fighter-msg.wxml b/pages/fighter-msg/fighter-msg.wxml index f77b430..f1d08e3 100644 --- a/pages/fighter-msg/fighter-msg.wxml +++ b/pages/fighter-msg/fighter-msg.wxml @@ -215,14 +215,14 @@ - + - + 联系客服 - 申诉 @@ -299,11 +299,11 @@ - - + + 取消 - + 提交申诉 diff --git a/pages/fighter-msg/fighter-msg.wxss b/pages/fighter-msg/fighter-msg.wxss index fe267b5..2c19a0c 100644 --- a/pages/fighter-msg/fighter-msg.wxss +++ b/pages/fighter-msg/fighter-msg.wxss @@ -625,12 +625,12 @@ flex-shrink: 0; } -.btn-group { +.cbtn-group { display: flex; gap: 20rpx; } -.btn { +.cbtn { flex: 1; height: 90rpx; display: flex; @@ -645,37 +645,37 @@ color: var(--cyber-text) !important; } -.btn:active { +.cbtn:active { transform: scale(0.98); } /* 按钮样式 */ -.btn-kefu { +.cbtn-kefu { background: rgba(0, 243, 255, 0.1); border-color: var(--cyber-blue); box-shadow: 0 0 12rpx rgba(0, 243, 255, 0.2); } -.btn-shensu { +.cbtn-shensu { background: rgba(255, 165, 0, 0.1); border-color: #FFA500; box-shadow: 0 0 12rpx rgba(255, 165, 0, 0.2); } -.btn-shensu-disabled { +.cbtn-shensu-disabled { background: rgba(102, 102, 102, 0.1); border-color: #666; box-shadow: 0 0 12rpx rgba(102, 102, 102, 0.2); opacity: 0.5; } -.btn-quxiao { +.cbtn-quxiao { background: rgba(255, 255, 255, 0.07); border-color: rgba(255, 255, 255, 0.2); box-shadow: 0 0 12rpx rgba(255, 255, 255, 0.1); } -.btn-queren { +.cbtn-queren { background: rgba(0, 255, 157, 0.1); border-color: var(--cyber-green); box-shadow: 0 0 12rpx rgba(0, 255, 157, 0.2); diff --git a/pages/fighter-orders/fighter-orders.js b/pages/fighter-orders/fighter-orders.js index 130c191..8323223 100644 --- a/pages/fighter-orders/fighter-orders.js +++ b/pages/fighter-orders/fighter-orders.js @@ -1,307 +1,289 @@ -const app = getApp(); -import request from '../../utils/request.js'; -import { reconnectForRole } from '../../utils/role-tab-bar.js'; - -Page({ - data: { - // 商品类型 - shangpinleixing: [], - xuanzhongLeixingId: null, - - // 订单类型: normal 普通, peihu 陪护 - orderType: 'normal', - - // 搜索关键字 - searchKeyword: '', - - // 状态列表(增加全部) - statusList: [ - { name: '全部', key: 'all', zhuangtaiList: [], color: '#333333' }, - { name: '进行中', key: 'jinxingzhong', zhuangtaiList: [2], color: '#2196F3' }, - { name: '退款中', key: 'tuikuanzhong', zhuangtaiList: [4], color: '#FF9800' }, - { name: '已退款', key: 'yituikuan', zhuangtaiList: [5], color: '#9E9E9E' }, - { name: '退款失败', key: 'tuikuanshibai', zhuangtaiList: [6], color: '#F44336' }, - { name: '已完成', key: 'yiwancheng', zhuangtaiList: [3], color: '#4CAF50' }, - { name: '结算中', key: 'jiesuanzhong', zhuangtaiList: [8], color: '#FF5722' } - ], - currentStatusKey: 'all', - - // 每个状态独立的数据集 - dsDingdanShuju: { - all: { list: [], page: 1, hasMore: true, isLoading: false }, - jinxingzhong: { list: [], page: 1, hasMore: true, isLoading: false }, - tuikuanzhong: { list: [], page: 1, hasMore: true, isLoading: false }, - yituikuan: { list: [], page: 1, hasMore: true, isLoading: false }, - tuikuanshibai: { list: [], page: 1, hasMore: true, isLoading: false }, - yiwancheng: { list: [], page: 1, hasMore: true, isLoading: false }, - jiesuanzhong: { list: [], page: 1, hasMore: true, isLoading: false } - }, - - currentList: [], - hasMore: true, - isLoading: false, - isLoadingMore: false, - scrollViewRefreshing: false, - defaultImg: '/images/default-order.png', - ossImageUrl: app.globalData.ossImageUrl || '', - }, - - onLoad() { - wx.setNavigationBarTitle({ title: '我的接单' }); - this.loadShangpinLeixing(); - this.registerNotificationComponent(); - }, - - onShow() { - this.registerNotificationComponent(); - if (wx.getStorageSync('uid')) { - reconnectForRole('dashou'); - if (app.startImWhenReady) app.startImWhenReady(); - } - if (this.data.currentStatusKey && this.data.xuanzhongLeixingId) { - this.loadCurrentStatusOrders(true); - } - }, - - registerNotificationComponent() { - const notificationComp = this.selectComponent('#global-notification'); - if (notificationComp && notificationComp.showNotification) { - app.globalData.globalNotification = { - show: (data) => notificationComp.showNotification(data), - hide: () => notificationComp.hideNotification() - }; - } - }, - - // 加载商品类型(图片拼接) - async loadShangpinLeixing() { - try { - const res = await request({ - url: '/dingdan/dsqdhqddlx', - method: 'POST', - header: { 'content-type': 'application/json' } - }); - if (res.data && (res.data.code === 200 || res.data.code === 0)) { - let list = res.data.data.list || res.data.data || []; - if (!Array.isArray(list)) list = []; - const oss = this.data.ossImageUrl; - const processed = list.map(item => ({ - ...item, - full_tupian_url: item.tupian_url && !item.tupian_url.startsWith('http') - ? oss + item.tupian_url - : (item.tupian_url || '/images/default-type.png') - })); - this.setData({ - shangpinleixing: processed, - xuanzhongLeixingId: processed[0]?.id || null - }); - this.loadCurrentStatusOrders(true); - } - } catch (e) { - console.error('加载商品类型失败', e); - } - }, - - selectLeixing(e) { - const id = e.currentTarget.dataset.id; - if (id === this.data.xuanzhongLeixingId) return; - this.setData({ xuanzhongLeixingId: id }); - this.resetCurrentStatusData(); - this.loadCurrentStatusOrders(true); - }, - - switchOrderType(e) { - const type = e.currentTarget.dataset.type; - if (type === this.data.orderType) return; - this.setData({ orderType: type }); - this.resetCurrentStatusData(); - this.loadCurrentStatusOrders(true); - }, - - onSearchInput(e) { - this.setData({ searchKeyword: e.detail.value }); - if (this._searchTimer) clearTimeout(this._searchTimer); - this._searchTimer = setTimeout(() => { - this.onSearchConfirm(); - }, 500); - }, - - onSearchConfirm() { - this.resetCurrentStatusData(); - this.loadCurrentStatusOrders(true); - }, - - clearSearch() { - this.setData({ searchKeyword: '' }); - this.resetCurrentStatusData(); - this.loadCurrentStatusOrders(true); - }, - - switchStatus(e) { - const key = e.currentTarget.dataset.key; - if (key === this.data.currentStatusKey) return; - this.setData({ currentStatusKey: key }); - const tabData = this.data.dsDingdanShuju[key]; - if (tabData.list.length === 0 && tabData.hasMore && !tabData.isLoading) { - this.loadCurrentStatusOrders(true); - } else { - this.refreshCurrentListView(); - } - }, - - // 加载订单(核心) - async loadCurrentStatusOrders(isRefresh = false) { - const key = this.data.currentStatusKey; - const tabData = this.data.dsDingdanShuju[key]; - if (tabData.isLoading) return; - const page = isRefresh ? 1 : tabData.page; - - this.setData({ - [`dsDingdanShuju.${key}.isLoading`]: true, - isLoading: true, - isLoadingMore: !isRefresh - }); - - try { - const apiUrl = this.data.orderType === 'peihu' - ? '/dingdan/phddlbhq' - : '/dingdan/dshqdingdan'; - - const statusItem = this.data.statusList.find(s => s.key === key); - const zhuangtaiList = statusItem.zhuangtaiList.length > 0 ? statusItem.zhuangtaiList : undefined; - const params = { - zhuangtai_list: zhuangtaiList, - page: page, - page_size: 5, - leixing_id: this.data.xuanzhongLeixingId, - keyword: this.data.searchKeyword || undefined - }; - - const res = await request({ - url: apiUrl, - method: 'POST', - data: params, - header: { 'content-type': 'application/json' } - }); - - const code = res.data.code; - if (code === 200 || code === 0) { - const newList = res.data.data.list || []; - const hasMore = res.data.data.has_more || false; - - // ✅ 图片URL拼接(核心) - const processed = newList.map(item => ({ - ...item, - zhuangtaiZh: this.getZhuangtaiZh(item.zhuangtai), - zhuangtaiColor: statusItem.color, - tupian: item.tupian - ? (item.tupian.startsWith('http') ? item.tupian : this.data.ossImageUrl + item.tupian) - : this.data.defaultImg - })); - - const currentList = this.data.dsDingdanShuju[key].list; - const updatedList = isRefresh ? processed : [...currentList, ...processed]; - - this.setData({ - [`dsDingdanShuju.${key}.list`]: updatedList, - [`dsDingdanShuju.${key}.page`]: page, - [`dsDingdanShuju.${key}.hasMore`]: hasMore, - [`dsDingdanShuju.${key}.isLoading`]: false, - isLoading: false, - isLoadingMore: false, - scrollViewRefreshing: false - }); - this.refreshCurrentListView(); - } else { - throw new Error(res.data.msg || '加载失败'); - } - } catch (err) { - console.error('加载订单失败', err); - wx.showToast({ title: err.message || '加载失败', icon: 'none' }); - this.setData({ - [`dsDingdanShuju.${key}.isLoading`]: false, - isLoading: false, - isLoadingMore: false, - scrollViewRefreshing: false - }); - this.refreshCurrentListView(); - } - }, - - refreshCurrentListView() { - const key = this.data.currentStatusKey; - const tabData = this.data.dsDingdanShuju[key]; - this.setData({ - currentList: tabData.list, - hasMore: tabData.hasMore, - isLoading: tabData.isLoading - }); - }, - - resetCurrentStatusData() { - const key = this.data.currentStatusKey; - this.setData({ - [`dsDingdanShuju.${key}.list`]: [], - [`dsDingdanShuju.${key}.page`]: 1, - [`dsDingdanShuju.${key}.hasMore`]: true, - [`dsDingdanShuju.${key}.isLoading`]: false - }); - this.refreshCurrentListView(); - }, - - // 下拉刷新(由 scroll-view 触发) - onPullDownRefresh() { - if (this.data.isLoading) { - this.setData({ scrollViewRefreshing: false }); - return; - } - this.setData({ scrollViewRefreshing: true }); - this.resetCurrentStatusData(); - this.loadCurrentStatusOrders(true); - }, - - // 上拉加载更多(由 scroll-view 触发) - onReachBottom() { - const key = this.data.currentStatusKey; - const tabData = this.data.dsDingdanShuju[key]; - if (tabData.isLoading || !tabData.hasMore) return; - this.setData({ [`dsDingdanShuju.${key}.page`]: tabData.page + 1 }); - this.loadCurrentStatusOrders(false); - }, - - // ✅ 手动点击“加载更多”按钮 - onLoadMoreTap() { - const key = this.data.currentStatusKey; - const tabData = this.data.dsDingdanShuju[key]; - if (tabData.isLoading || !tabData.hasMore) return; - this.setData({ [`dsDingdanShuju.${key}.page`]: tabData.page + 1 }); - this.loadCurrentStatusOrders(false); - }, - - getZhuangtaiZh(zhuangtai) { - const map = { - 1: '已下单', 2: '进行中', 3: '已完成', - 4: '退款中', 5: '已退款', 6: '退款失败', - 7: '指定中', 8: '结算中' - }; - return map[zhuangtai] || '未知状态'; - }, - - goToDsDingdanXiangqing(e) { - const item = e.currentTarget.dataset.item; - if (!item) return; - const dataStr = encodeURIComponent(JSON.stringify(item)); - - if (this.data.orderType === 'peihu') { - wx.navigateTo({ - url: `/pages/escort-orders/escort-orders?dingdanData=${dataStr}` - }); - } else { - wx.navigateTo({ - url: `/pages/fighter-order-detail/fighter-order-detail?dingdanData=${dataStr}` - }); - } - }, - - onImageError() {} -}); \ No newline at end of file +const app = getApp(); +import { createPage, request } from '../../utils/base-page.js'; +import { reconnectForRole } from '../../utils/role-tab-bar.js'; +import { getOrderStatusText } from '../../utils/api-helper.js'; + +Page(createPage({ + data: { + // 商品类型 + shangpinleixing: [], + xuanzhongLeixingId: null, + + // 订单类型: normal 普通, peihu 陪护 + orderType: 'normal', + + // 搜索关键字 + searchKeyword: '', + + // 状态列表(增加全部) + statusList: [ + { name: '全部', key: 'all', zhuangtaiList: [], color: '#333333' }, + { name: '进行中', key: 'jinxingzhong', zhuangtaiList: [2], color: '#2196F3' }, + { name: '退款中', key: 'tuikuanzhong', zhuangtaiList: [4], color: '#FF9800' }, + { name: '已退款', key: 'yituikuan', zhuangtaiList: [5], color: '#9E9E9E' }, + { name: '退款失败', key: 'tuikuanshibai', zhuangtaiList: [6], color: '#F44336' }, + { name: '已完成', key: 'yiwancheng', zhuangtaiList: [3], color: '#4CAF50' }, + { name: '结算中', key: 'jiesuanzhong', zhuangtaiList: [8], color: '#FF5722' } + ], + currentStatusKey: 'all', + + // 每个状态独立的数据集 + dsDingdanShuju: { + all: { list: [], page: 1, hasMore: true, isLoading: false }, + jinxingzhong: { list: [], page: 1, hasMore: true, isLoading: false }, + tuikuanzhong: { list: [], page: 1, hasMore: true, isLoading: false }, + yituikuan: { list: [], page: 1, hasMore: true, isLoading: false }, + tuikuanshibai: { list: [], page: 1, hasMore: true, isLoading: false }, + yiwancheng: { list: [], page: 1, hasMore: true, isLoading: false }, + jiesuanzhong: { list: [], page: 1, hasMore: true, isLoading: false } + }, + + currentList: [], + hasMore: true, + isLoading: false, + isLoadingMore: false, + scrollViewRefreshing: false, + defaultImg: '/images/default-order.png', + ossImageUrl: app.globalData.ossImageUrl || '', + }, + + onLoad() { + wx.setNavigationBarTitle({ title: '我的接单' }); + this.loadShangpinLeixing(); + this.registerNotificationComponent(); + }, + + onShow() { + this.registerNotificationComponent(); + if (wx.getStorageSync('uid')) { + reconnectForRole('dashou'); + if (app.startImWhenReady) app.startImWhenReady(); + } + if (this.data.currentStatusKey && this.data.xuanzhongLeixingId) { + this.loadCurrentStatusOrders(true); + } + }, + + // 加载商品类型(图片拼接) + async loadShangpinLeixing() { + try { + const res = await request({ + url: '/dingdan/dsqdhqddlx', + method: 'POST', + header: { 'content-type': 'application/json' } + }); + if (res.data && (res.data.code === 200 || res.data.code === 0)) { + let list = res.data.data.list || res.data.data || []; + if (!Array.isArray(list)) list = []; + const oss = this.data.ossImageUrl; + const processed = list.map(item => ({ + ...item, + full_tupian_url: item.tupian_url && !item.tupian_url.startsWith('http') + ? oss + item.tupian_url + : (item.tupian_url || '/images/default-type.png') + })); + this.setData({ + shangpinleixing: processed, + xuanzhongLeixingId: processed[0]?.id || null + }); + this.loadCurrentStatusOrders(true); + } + } catch (e) { + console.error('加载商品类型失败', e); + } + }, + + selectLeixing(e) { + const id = e.currentTarget.dataset.id; + if (id === this.data.xuanzhongLeixingId) return; + this.setData({ xuanzhongLeixingId: id }); + this.resetCurrentStatusData(); + this.loadCurrentStatusOrders(true); + }, + + switchOrderType(e) { + const type = e.currentTarget.dataset.type; + if (type === this.data.orderType) return; + this.setData({ orderType: type }); + this.resetCurrentStatusData(); + this.loadCurrentStatusOrders(true); + }, + + onSearchInput(e) { + this.setData({ searchKeyword: e.detail.value }); + if (this._searchTimer) clearTimeout(this._searchTimer); + this._searchTimer = setTimeout(() => { + this.onSearchConfirm(); + }, 500); + }, + + onSearchConfirm() { + this.resetCurrentStatusData(); + this.loadCurrentStatusOrders(true); + }, + + clearSearch() { + this.setData({ searchKeyword: '' }); + this.resetCurrentStatusData(); + this.loadCurrentStatusOrders(true); + }, + + switchStatus(e) { + const key = e.currentTarget.dataset.key; + if (key === this.data.currentStatusKey) return; + this.setData({ currentStatusKey: key }); + const tabData = this.data.dsDingdanShuju[key]; + if (tabData.list.length === 0 && tabData.hasMore && !tabData.isLoading) { + this.loadCurrentStatusOrders(true); + } else { + this.refreshCurrentListView(); + } + }, + + // 加载订单(核心) + async loadCurrentStatusOrders(isRefresh = false) { + const key = this.data.currentStatusKey; + const tabData = this.data.dsDingdanShuju[key]; + if (tabData.isLoading) return; + const page = isRefresh ? 1 : tabData.page; + + this.setData({ + [`dsDingdanShuju.${key}.isLoading`]: true, + isLoading: true, + isLoadingMore: !isRefresh + }); + + try { + const apiUrl = this.data.orderType === 'peihu' + ? '/dingdan/phddlbhq' + : '/dingdan/dshqdingdan'; + + const statusItem = this.data.statusList.find(s => s.key === key); + const zhuangtaiList = statusItem.zhuangtaiList.length > 0 ? statusItem.zhuangtaiList : undefined; + const params = { + zhuangtai_list: zhuangtaiList, + page: page, + page_size: 5, + leixing_id: this.data.xuanzhongLeixingId, + keyword: this.data.searchKeyword || undefined + }; + + const res = await request({ + url: apiUrl, + method: 'POST', + data: params, + header: { 'content-type': 'application/json' } + }); + + const code = res.data.code; + if (code === 200 || code === 0) { + const newList = res.data.data.list || []; + const hasMore = res.data.data.has_more || false; + + // ✅ 图片URL拼接(核心) + const processed = newList.map(item => ({ + ...item, + zhuangtaiZh: getOrderStatusText(item.zhuangtai), + zhuangtaiColor: statusItem.color, + tupian: item.tupian + ? (item.tupian.startsWith('http') ? item.tupian : this.data.ossImageUrl + item.tupian) + : this.data.defaultImg + })); + + const currentList = this.data.dsDingdanShuju[key].list; + const updatedList = isRefresh ? processed : [...currentList, ...processed]; + + this.setData({ + [`dsDingdanShuju.${key}.list`]: updatedList, + [`dsDingdanShuju.${key}.page`]: page, + [`dsDingdanShuju.${key}.hasMore`]: hasMore, + [`dsDingdanShuju.${key}.isLoading`]: false, + isLoading: false, + isLoadingMore: false, + scrollViewRefreshing: false + }); + this.refreshCurrentListView(); + } else { + throw new Error(res.data.msg || '加载失败'); + } + } catch (err) { + console.error('加载订单失败', err); + wx.showToast({ title: err.message || '加载失败', icon: 'none' }); + this.setData({ + [`dsDingdanShuju.${key}.isLoading`]: false, + isLoading: false, + isLoadingMore: false, + scrollViewRefreshing: false + }); + this.refreshCurrentListView(); + } + }, + + refreshCurrentListView() { + const key = this.data.currentStatusKey; + const tabData = this.data.dsDingdanShuju[key]; + this.setData({ + currentList: tabData.list, + hasMore: tabData.hasMore, + isLoading: tabData.isLoading + }); + }, + + resetCurrentStatusData() { + const key = this.data.currentStatusKey; + this.setData({ + [`dsDingdanShuju.${key}.list`]: [], + [`dsDingdanShuju.${key}.page`]: 1, + [`dsDingdanShuju.${key}.hasMore`]: true, + [`dsDingdanShuju.${key}.isLoading`]: false + }); + this.refreshCurrentListView(); + }, + + // 下拉刷新(由 scroll-view 触发) + onPullDownRefresh() { + if (this.data.isLoading) { + this.setData({ scrollViewRefreshing: false }); + return; + } + this.setData({ scrollViewRefreshing: true }); + this.resetCurrentStatusData(); + this.loadCurrentStatusOrders(true); + }, + + // 上拉加载更多(由 scroll-view 触发) + onReachBottom() { + const key = this.data.currentStatusKey; + const tabData = this.data.dsDingdanShuju[key]; + if (tabData.isLoading || !tabData.hasMore) return; + this.setData({ [`dsDingdanShuju.${key}.page`]: tabData.page + 1 }); + this.loadCurrentStatusOrders(false); + }, + + // ✅ 手动点击"加载更多"按钮 + onLoadMoreTap() { + const key = this.data.currentStatusKey; + const tabData = this.data.dsDingdanShuju[key]; + if (tabData.isLoading || !tabData.hasMore) return; + this.setData({ [`dsDingdanShuju.${key}.page`]: tabData.page + 1 }); + this.loadCurrentStatusOrders(false); + }, + + goToDsDingdanXiangqing(e) { + const item = e.currentTarget.dataset.item; + if (!item) return; + const dataStr = encodeURIComponent(JSON.stringify(item)); + + if (this.data.orderType === 'peihu') { + wx.navigateTo({ + url: `/pages/escort-orders/escort-orders?dingdanData=${dataStr}` + }); + } else { + wx.navigateTo({ + url: `/pages/fighter-order-detail/fighter-order-detail?dingdanData=${dataStr}` + }); + } + }, + + onImageError() {} +})) diff --git a/pages/fighter-recharge/fighter-recharge.wxml b/pages/fighter-recharge/fighter-recharge.wxml index 04f074e..a5187df 100644 --- a/pages/fighter-recharge/fighter-recharge.wxml +++ b/pages/fighter-recharge/fighter-recharge.wxml @@ -503,7 +503,7 @@ - 星阙网络 © 7891 科技 + 星阙网络 © 科技 diff --git a/pages/fighter/fighter.js b/pages/fighter/fighter.js index cc2f07e..dbdca01 100644 --- a/pages/fighter/fighter.js +++ b/pages/fighter/fighter.js @@ -1,26 +1,15 @@ // pages/dashouzhongxin/dashouzhongxin.js -import request from '../../utils/request.js' -import popupService from '../../services/popupService.js' -import { - isRoleStatusActive, - isCenterPageActive, - syncRoleStatuses, - syncProfileFields, - syncGroupFields, - ensureRoleOnCenterPage, - clearCacheAndEnterNormal, -} from '../../utils/role-tab-bar.js' -import { lockPrimaryRole, migrateLegacyCenterRole, enterLockedRole } from '../../utils/primary-role.js' -import { resolveAvatarUrl, resolveAvatarFromStorage } from '../../utils/avatar.js' +import { createPage, request, isRoleStatusActive, isCenterPageActive, syncRoleStatuses, syncProfileFields, syncGroupFields, ensureRoleOnCenterPage, clearCacheAndEnterNormal, lockPrimaryRole, migrateLegacyCenterRole, enterLockedRole, parseSceneOptions } from '../../utils/base-page.js' +import { resolveAvatarUrl } from '../../utils/avatar.js' import { openPrivateChat, buildInviterPeerId } from '../../utils/im-user.js' const PLATFORM_INVITE_CODE = '0000008RffVgKHMj7kQC' -Page({ +Page(createPage({ data: { // 图片路径对象 imgUrls: {}, - + isDashou: false, isGuanshi: false, isZuzhang: false, @@ -31,7 +20,6 @@ Page({ assetBreakdown: [], pendingAuthList: [], showAuthSection: false, - isLoading: false, inviteCode: '', guanshiInviteCode: '', zuzhangInviteCode: '', @@ -48,8 +36,6 @@ Page({ clumber: [], chengjiaoliang: '', zaixianZhuangtai: 0, - lastRefreshTime: 0, - canRefresh: true, isAutoRegistering: false, inviterCache: null, zuzhangInviterCache: null, @@ -165,17 +151,10 @@ Page({ }); this.checkRoleStatuses(); - const registerType = options.registerType || 'dashou'; - if (options.scene) { - try { - const scene = decodeURIComponent(options.scene); - options.inviteCode = scene; - } catch (e) { - console.error('scene解码失败', e); - } - } + const parsed = parseSceneOptions(options); + const registerType = parsed.registerType || 'dashou'; + const { inviteCode } = parsed; this.checkDashouStatus(); - const { inviteCode } = options || {}; if (inviteCode) { if (registerType === 'guanshi') { this.setData({ guanshiInviteCode: inviteCode }); @@ -189,26 +168,7 @@ Page({ }, 500); } } - // 注意:不再重复声明 app - if (!app.globalData.hasShownPopupOnColdStart) { - app.globalData.hasShownPopupOnColdStart = true; - setTimeout(() => { - popupService.checkAndShow(this, 'dashouduan'); - }, 300); - } - }, - - onReady() { - if (isCenterPageActive(this, 'isDashou', 'dashoustatus')) { - ensureRoleOnCenterPage(this, 'dashou'); - } - }, - - onHide() { - const popupComp = this.selectComponent('#popupNotice'); - if (popupComp && popupComp.cleanup) { - popupComp.cleanup(); - } + this.checkColdStartPopup('dashouduan'); }, onShow() { @@ -324,60 +284,36 @@ Page({ }); }, - _isApiSuccess(body) { - if (!body) return false; - const code = Number(body.code); - return code === 200 || code === 0; - }, - - _getApiMsg(body) { - if (!body) return ''; - return body.msg || body.message || ''; - }, - - _extractUserData(body) { - if (!body) return {}; - if (body.data && typeof body.data === 'object' && !Array.isArray(body.data)) { - return body.data; - } - const userData = { ...body }; - delete userData.code; - delete userData.msg; - delete userData.message; - return userData; - }, - async refreshAllInfo(showToast = true) { - const now = Date.now(); - if (!this.data.canRefresh || (now - this.data.lastRefreshTime < 3000)) return; - this.setData({ lastRefreshTime: now, canRefresh: false, isLoading: true }); - try { - this.checkRoleStatuses(); - const results = await Promise.allSettled([ - this._fetchDashouInfoSilent(), - this.fetchChenghaoList(), - this._fetchGuanshiInfoSilent(), - this.fetchGuanshiChenghaoList(), - this._fetchZuzhangInfoSilent(), - this._fetchKaoheguanInfoSilent(), - ]); - this.checkRoleStatuses(); - const dashouOk = results[0].status === 'fulfilled'; - if (showToast) { - wx.showToast({ - title: dashouOk ? '刷新成功' : '刷新失败', - icon: dashouOk ? 'success' : 'none', - duration: 1500, - }); + this.throttledRefresh(async () => { + this.setData({ isLoading: true }); + try { + this.checkRoleStatuses(); + const results = await Promise.allSettled([ + this._fetchDashouInfoSilent(), + this.fetchChenghaoList(), + this._fetchGuanshiInfoSilent(), + this.fetchGuanshiChenghaoList(), + this._fetchZuzhangInfoSilent(), + this._fetchKaoheguanInfoSilent(), + ]); + this.checkRoleStatuses(); + const dashouOk = results[0].status === 'fulfilled'; + if (showToast) { + wx.showToast({ + title: dashouOk ? '刷新成功' : '刷新失败', + icon: dashouOk ? 'success' : 'none', + duration: 1500, + }); + } + } catch (e) { + if (showToast) { + wx.showToast({ title: '刷新失败', icon: 'none' }); + } + } finally { + this.setData({ isLoading: false }); } - } catch (e) { - if (showToast) { - wx.showToast({ title: '刷新失败', icon: 'none' }); - } - } finally { - this.setData({ isLoading: false }); - setTimeout(() => this.setData({ canRefresh: true }), 3000); - } + }, 3000); }, refreshDashouInfo() { @@ -875,17 +811,6 @@ Page({ }); }, - registerNotificationComponent() { - const app = getApp(); - const notificationComp = this.selectComponent('#global-notification'); - if (notificationComp && notificationComp.showNotification) { - app.globalData.globalNotification = { - show: (data) => notificationComp.showNotification(data), - hide: () => notificationComp.hideNotification() - }; - } - }, - checkDashouStatus() { try { const dashoustatus = wx.getStorageSync('dashoustatus'); @@ -905,11 +830,6 @@ Page({ } }, - loadAvatar() { - const avatarUrl = resolveAvatarFromStorage(getApp()); - this.setData({ avatarUrl }); - }, - checkGlobalData() { const app = getApp(); const globalData = app.globalData || {}; @@ -1058,19 +978,6 @@ Page({ ensureRoleOnCenterPage(this, 'dashou'); }, - copyUid() { - const { uid } = this.data; - if (!uid) { - wx.showToast({ title: 'UID不存在', icon: 'none', duration: 1500 }); - return; - } - wx.setClipboardData({ - data: uid, - success: () => wx.showToast({ title: '已复制UID', icon: 'success', duration: 1500 }), - fail: () => wx.showToast({ title: '复制失败', icon: 'error', duration: 1500 }) - }); - }, - previewAvatar() { const { avatarUrl } = this.data; if (avatarUrl) wx.previewImage({ urls: [avatarUrl] }); @@ -1079,19 +986,6 @@ Page({ goToWithdraw() { wx.navigateTo({ url: '/pages/withdraw/withdraw' }) }, goToTotalWithdraw() { this.goToWithdraw(); }, - goToKefu() { - const app = getApp(); - const { link, enterpriseId } = app.globalData.kefuConfig || {}; - if (!link || !enterpriseId) { - wx.showToast({ title: '客服配置未加载', icon: 'none' }); - return; - } - wx.openCustomerServiceChat({ - extInfo: { url: link }, - corpId: enterpriseId, - }); - }, - goToAuth(e) { const type = e.currentTarget.dataset.type; if (!type || !['dashou', 'shangjia', 'guanshi', 'zuzhang'].includes(type)) return; @@ -1171,20 +1065,4 @@ Page({ }); }, - clearCache() { - wx.showModal({ - title: '清除缓存', - content: '将清除所有登录与身份数据,并回到点单端,确定继续?', - confirmText: '确定', - cancelText: '取消', - success: (r) => { - if (r.confirm) clearCacheAndEnterNormal(this); - }, - }); - }, - - switchToNormal() { - lockPrimaryRole('normal'); - wx.reLaunch({ url: '/pages/mine/mine' }); - }, -}) +}, { roleConfig: { role: 'dashou', statusKey: 'dashoustatus', dataFlag: 'isDashou', pageId: 'dashouduan', apiUrl: '/yonghu/dashouxinxi' } })) diff --git a/pages/fighter/fighter.wxml b/pages/fighter/fighter.wxml index 59b4daf..a3de9c1 100644 --- a/pages/fighter/fighter.wxml +++ b/pages/fighter/fighter.wxml @@ -193,7 +193,7 @@ > - + {{item.name}} {{item.sub}} diff --git a/pages/fighter/fighter.wxss b/pages/fighter/fighter.wxss index 6a299ba..bedaf83 100644 --- a/pages/fighter/fighter.wxss +++ b/pages/fighter/fighter.wxss @@ -469,7 +469,7 @@ page { position: relative; display: flex; align-items: center; - padding: 0 20rpx; + padding: 0 24rpx; box-sizing: border-box; border-radius: 16rpx; overflow: hidden; @@ -477,25 +477,27 @@ page { .rank-bg-icon { position: absolute; - width: 120rpx; - height: 120rpx; - right: -12rpx; + width: 100rpx; + height: 100rpx; + right: 8rpx; top: 50%; transform: translateY(-50%) rotate(-10deg); - opacity: 0.15; + opacity: 0.12; z-index: 0; pointer-events: none; } -.rank-content { +.rank-cell-content { position: relative; z-index: 1; + display: flex; + flex-direction: column; } .rank-main-icon { - width: 56rpx; - height: 56rpx; - margin-bottom: 6rpx; + width: 44rpx; + height: 44rpx; + margin-bottom: 8rpx; } .rank-shape-right .rank-cell-inner { @@ -509,7 +511,7 @@ page { transform: translateY(-50%) rotate(10deg); } -.rank-shape-right .rank-content { +.rank-shape-right .rank-cell-content { text-align: right; } diff --git a/pages/group-chat/group-chat.wxml b/pages/group-chat/group-chat.wxml index d22a391..2abee15 100644 --- a/pages/group-chat/group-chat.wxml +++ b/pages/group-chat/group-chat.wxml @@ -1,92 +1,92 @@ - - 加载更早消息... + 加载更早消息... - - {{item.formattedTime}} - + + {{item.formattedTime}} + - + {{item.senderData.name || item.senderId}} - - {{item.payload.text}} - - - [订单] - ID: {{item.payload.orderId}} - 内容: {{item.payload.jieshao}} - 金额: ¥{{item.payload.jine}} - 点击查看详情 + {{item.payload.text}} + + + [订单] + ID: {{item.payload.orderId}} + 内容: {{item.payload.jieshao}} + 金额: ¥{{item.payload.jine}} + 点击查看详情 - + - - - - - 😀 - 表情 + + + + + 😀 + 表情 - - 📷 - 图片 + + 📷 + 图片 - - 📋 - 订单 + + 📋 + 订单 - - - - + + + + - {{item}} + {{item}} - - - + + + - - - 发送 - - + + + 发送 + + - - - {{detailText}} - + + + {{detailText}} + - \ No newline at end of file + diff --git a/pages/group-chat/group-chat.wxss b/pages/group-chat/group-chat.wxss index 6496c30..dab11fa 100644 --- a/pages/group-chat/group-chat.wxss +++ b/pages/group-chat/group-chat.wxss @@ -1,97 +1,6 @@ -.chat-page { width:100%; height:100vh; background:#f5f5f5; } +/* 使用全局 chat-* 样式 */ -.msg-list { - position: absolute; - left:0; right:0; - padding: 0 20rpx; - box-sizing: border-box; - overflow-y: auto; -} - -.loading-more { text-align:center; font-size:24rpx; color:#999; padding:20rpx 0; } -.time-tag { text-align:center; margin:28rpx 0; font-size:24rpx; color:#b0b0b0; } -.msg-wrapper { margin-bottom:12rpx; } -.msg-row { display:flex; align-items:flex-start; margin-bottom:4rpx; } -.right { justify-content:flex-end; } -.left { justify-content:flex-start; } -.avatar { width:76rpx; height:76rpx; border-radius:50%; background:#e0e0e0; margin:0 12rpx; flex-shrink:0; } -.bubble { max-width:70%; padding:16rpx 20rpx; border-radius:16rpx; position:relative; word-break:break-all; font-size:30rpx; line-height:1.5; } -.bubble-left { background:#fff; border-top-left-radius:4rpx; } -.bubble-right { background:#00aaff; color:#fff; border-top-right-radius:4rpx; } -.msg-text { color:inherit; } -.msg-image { max-width:240rpx; border-radius:10rpx; } - -.order-bubble { background:#fff7e0; padding:12rpx; border-radius:8rpx; } -.order-label { font-size:24rpx; color:#e6a23c; font-weight:600; } -.order-info { font-size:24rpx; color:#333; margin:6rpx 0; } -.order-detail-link { color:#007aff; font-size:24rpx; margin-top:8rpx; } - -.input-area { - position: fixed; - left:0; right:0; - background:#f7f7f7; - border-top:1rpx solid #ddd; - z-index:10; -} - -.plus-panel { - position: absolute; - bottom:100%; left:0; right:0; - background:#fff; - border-top:1rpx solid #e0e0e0; - padding: 20rpx; - transform: translateY(100%); - opacity:0; - transition: transform 0.3s, opacity 0.3s; - pointer-events:none; - z-index:9; -} -.plus-panel.show { transform: translateY(0); opacity:1; pointer-events:auto; } -.plus-grid { display: flex; justify-content: space-around; } -.plus-item { display: flex; flex-direction: column; align-items: center; width: 120rpx; } -.plus-icon { font-size: 60rpx; } -.plus-label { font-size: 24rpx; color: #666; margin-top: 8rpx; } - -.emoji-mask { position:fixed; top:0; left:0; right:0; bottom:0; background:transparent; z-index:8; } -.emoji-panel { - position: absolute; bottom:100%; left:0; right:0; - background:#fff; border-top:1rpx solid #e0e0e0; - height:380rpx; - transform: translateY(100%); opacity:0; transition:0.3s; - pointer-events:none; z-index:9; -} -.emoji-panel.show { transform:translateY(0); opacity:1; pointer-events:auto; } -.emoji-scroll { height:100%; } -.emoji-grid { display:flex; flex-wrap:wrap; padding:20rpx 10rpx; } -.emoji-item { width:14.28%; text-align:center; padding:16rpx 0; font-size:44rpx; } - -.pending-image { display:flex; align-items:center; padding:10rpx 20rpx; background:#fff; } -.pending-thumb { width:80rpx; height:80rpx; border-radius:8rpx; } -.pending-remove { font-size:30rpx; color:#999; margin-left:10rpx; } - -.write-row { display:flex; padding:0 20rpx 10rpx; align-items:center; } -.text-input { - flex:1; height:76rpx; background:#fff; border-radius:12rpx; - padding:0 20rpx; font-size:30rpx; margin-right:12rpx; -} -.send-btn { - width:120rpx; height:76rpx; background:#00aaff; color:#fff; - font-size:28rpx; font-weight:500; border-radius:12rpx; - display:flex; align-items:center; justify-content:center; -} -.plus-btn { - width:56rpx; height:56rpx; - display:flex; align-items:center; justify-content:center; - margin-left: 10rpx; -} -.plus-icon-single { font-size:46rpx; color:#00aaff; } - -.detail-modal { position:fixed; top:0; left:0; right:0; bottom:0; background:rgba(0,0,0,0.7); z-index:999; display:flex; align-items:center; justify-content:center; } -.detail-content { width:80%; max-height:80%; background:#fff; border-radius:16rpx; padding:40rpx; } -.detail-text { font-size:32rpx; line-height:1.6; word-break:break-all; overflow-y:auto; } -.copy-btn { width:100%; height:80rpx; background:#00aaff; color:#fff; border-radius:12rpx; margin-top:20rpx; font-size:30rpx; } - -/* 发送者昵称样式 */ +/* 发送者昵称样式(群聊特有) */ .sender-info { display: flex; flex-direction: column; @@ -106,4 +15,4 @@ overflow: hidden; text-overflow: ellipsis; white-space: nowrap; -} \ No newline at end of file +} diff --git a/pages/index/index.js b/pages/index/index.js index 0ae32b5..09651b3 100644 --- a/pages/index/index.js +++ b/pages/index/index.js @@ -4,11 +4,12 @@ import { ensureLogin } from '../../utils/login'; import { fetchShopGoods, bindShop } from '../../utils/shop'; import { backfillUserProfileCache, getSessionToken } from '../../utils/role-tab-bar'; import PopupService from '../../services/popupService.js'; +import { createPage } from '../../utils/base-page.js'; // 扫码场景参数名 const SCENE_PARAM = 'scene'; -Page({ +Page(createPage({ data: { ossImageUrl: '', lunbozhanwei: '', @@ -26,7 +27,7 @@ Page({ isLoading: false, isRefreshing: false, hasInitialized: false, - + currentTime: '' }, @@ -43,7 +44,7 @@ Page({ console.error('scene解码失败', e); } } - + // 等待全局配置加载完成 this.waitForConfigAndInit(); // 已登录时才检查弹窗 @@ -107,16 +108,6 @@ Page({ } }, - registerNotificationComponent() { - const notificationComp = this.selectComponent('#global-notification'); - if (notificationComp && notificationComp.showNotification) { - app.globalData.globalNotification = { - show: (data) => notificationComp.showNotification(data), - hide: () => notificationComp.hideNotification() - }; - } - }, - getCurrentTime() { const now = new Date(); const year = now.getFullYear(); @@ -234,12 +225,12 @@ Page({ */ filterShangpinData() { const { selectedLeixingId, shangpinzhuanqu, shangpinliebiao } = this.data; - + if (!selectedLeixingId || !shangpinzhuanqu || !shangpinliebiao) { this.setData({ filteredData: [] }); return; } - + const zhuanquByLeixing = new Map(); shangpinzhuanqu.forEach(zhuanqu => { if (zhuanqu.leixing_id === selectedLeixingId) { @@ -249,7 +240,7 @@ Page({ zhuanquByLeixing.get(selectedLeixingId).push(zhuanqu); } }); - + const shangpinByZhuanqu = new Map(); shangpinliebiao.forEach(shangpin => { if (shangpin.leixing_id === selectedLeixingId && shangpin.zhuanqu_id) { @@ -257,7 +248,7 @@ Page({ if (!shangpinByZhuanqu.has(zhuanquId)) { shangpinByZhuanqu.set(zhuanquId, []); } - + const jiage = parseFloat(shangpin.jiage) || 0; const priceInteger = Math.floor(jiage); let priceDecimal = '00'; @@ -265,7 +256,7 @@ Page({ if (decimalPart > 0) { priceDecimal = decimalPart.toString().split('.')[1]; } - + shangpinByZhuanqu.get(zhuanquId).push({ id: shangpin.id, biaoqian: shangpin.biaoqian, @@ -278,10 +269,10 @@ Page({ }); } }); - + const filteredData = []; const currentZhuanquList = zhuanquByLeixing.get(selectedLeixingId) || []; - + currentZhuanquList.forEach(zhuanqu => { const shangpinList = shangpinByZhuanqu.get(zhuanqu.id) || []; if (shangpinList.length > 0) { @@ -295,14 +286,14 @@ Page({ }); } }); - + const sortedFilteredData = filteredData.sort((a, b) => { const paixuA = a.zhuanqu.paixu || 0; const paixuB = b.zhuanqu.paixu || 0; if (paixuB !== paixuA) return paixuB - paixuA; return a.zhuanqu.id - b.zhuanqu.id; }); - + this.setData({ filteredData: sortedFilteredData }); }, @@ -315,18 +306,21 @@ Page({ }, showGonggaoDetail() { - this.setData({ + this.setData({ showGonggaoModal: true, + _modalLeaving: false, gonggaoAnim: false, currentTime: this.getCurrentTime() }); }, hideGonggaoDetail() { - this.setData({ + this.setData({ showGonggaoModal: false, - gonggaoAnim: true + _modalLeaving: true, + gonggaoAnim: true }); + setTimeout(() => this.setData({ _modalLeaving: false }), 300); }, /** @@ -388,4 +382,4 @@ Page({ onUnload() { this.setData({ gonggaoAnim: false }); } -}); \ No newline at end of file +})) diff --git a/pages/index/index.wxml b/pages/index/index.wxml index 515fcf3..9251efd 100644 --- a/pages/index/index.wxml +++ b/pages/index/index.wxml @@ -146,9 +146,9 @@ - - - + + + 公告详情 × diff --git a/pages/index/index.wxss b/pages/index/index.wxss index d2866df..fbcc9aa 100644 --- a/pages/index/index.wxss +++ b/pages/index/index.wxss @@ -411,3 +411,82 @@ align-items: center; } +.gonggao-modal .modal-mask { + position: absolute; + top: 0; left: 0; right: 0; bottom: 0; + background: rgba(0, 0, 0, 0.5); + z-index: 0; + transition: opacity 0.25s ease; +} + +.gonggao-modal .modal-mask--in { + opacity: 1; +} + +.gonggao-modal .modal-mask--out { + opacity: 0; +} + +.gonggao-modal .modal-content { + position: relative; + z-index: 1; + width: 620rpx; + max-height: 70vh; + background: #fff; + border-radius: 20rpx; + overflow: hidden; + display: flex; + flex-direction: column; + transition: transform 0.25s ease, opacity 0.25s ease; +} + +.gonggao-modal .modal-content--in { + transform: scale(1); + opacity: 1; +} + +.gonggao-modal .modal-content--out { + transform: scale(0.9); + opacity: 0; +} + +.gonggao-modal .modal-header { + display: flex; + align-items: center; + justify-content: space-between; + padding: 28rpx 32rpx 20rpx; + border-bottom: 1rpx solid #f0f0f0; +} + +.gonggao-modal .modal-title { + font-size: 30rpx; + font-weight: 700; + color: #1a1a1a; +} + +.gonggao-modal .modal-close { + width: 48rpx; + height: 48rpx; + display: flex; + align-items: center; + justify-content: center; + font-size: 36rpx; + color: #999; + border-radius: 50%; + background: #f5f5f5; + line-height: 1; +} + +.gonggao-modal .modal-body { + flex: 1; + padding: 28rpx 32rpx 36rpx; + max-height: 55vh; +} + +.gonggao-modal .modal-text { + font-size: 28rpx; + color: #444; + line-height: 1.8; + word-break: break-all; +} + diff --git a/pages/leader/leader.js b/pages/leader/leader.js index 028903d..5bf935a 100644 --- a/pages/leader/leader.js +++ b/pages/leader/leader.js @@ -1,225 +1,135 @@ -// pages/leader/leader.js -import request from '../../utils/request.js' -import { - isRoleStatusActive, - isCenterPageActive, - syncRoleStatuses, - syncProfileFields, - ensureRoleOnCenterPage, -} from '../../utils/role-tab-bar.js' - -Page({ - data: { - // 页面状态 - isZuzhang: false, - isLoading: false, - - // 邀请码 - inviteCode: '', - - // 用户信息 - uid: '', - nicheng: '', - avatarUrl: '', - - // 组长信息 - ketixian: '0.00', - guanshiCount: 0, - fenhongZonge: '0.00', - }, - - onLoad() { - wx.redirectTo({ url: '/pages/fighter/fighter' }); - return; - // 以下保留供恢复参考 - this.checkLoginStatus() - }, - - onReady() { - if (isCenterPageActive(this, 'isZuzhang', 'zuzhangstatus')) { - ensureRoleOnCenterPage(this, 'zuzhang') - } - }, - - onShow() { - this.registerNotificationComponent() - if (isCenterPageActive(this, 'isZuzhang', 'zuzhangstatus')) { - if (!this.data.isZuzhang) this.setData({ isZuzhang: true }) - ensureRoleOnCenterPage(this, 'zuzhang') - this.getZuzhangInfo() - } - }, - - // 检查登录状态 - checkLoginStatus() { - const token = wx.getStorageSync('token') - if (!token) { - wx.showModal({ - title: '提示', - content: '您尚未登录,请先登录', - confirmText: '去登录', - success: (res) => { - if (res.confirm) { - wx.reLaunch({ url: '/pages/gerenzhongxin/gerenzhongxin' }) - } - } - }) - return false - } - this.checkZuzhangStatus() - return true - }, - - // 检查组长状态 - checkZuzhangStatus() { - try { - const zuzhangstatus = wx.getStorageSync('zuzhangstatus') - if (isRoleStatusActive(zuzhangstatus)) { - this.setData({ isZuzhang: true }) - this.loadUserInfo() - this.getZuzhangInfo() - ensureRoleOnCenterPage(this, 'zuzhang') - } else { - this.setData({ isZuzhang: false }) - } - } catch (error) { - console.error('读取缓存失败:', error) - this.setData({ isZuzhang: false }) - } - }, - - // 加载用户信息 - loadUserInfo() { - const app = getApp() - const globalData = app.globalData || {} - - const touxiang = wx.getStorageSync('touxiang') - const ossImageUrl = globalData.ossImageUrl || '' - const morentouxiang = globalData.morentouxiang || 'avatar/default.jpg' - - let avatarUrl = '' - if (touxiang) { - avatarUrl = ossImageUrl + (touxiang.startsWith('/') ? touxiang : '/' + touxiang) - } else { - avatarUrl = ossImageUrl + (morentouxiang.startsWith('/') ? morentouxiang : '/' + morentouxiang) - } - - const uid = wx.getStorageSync('uid') || '' - const nicheng = wx.getStorageSync('nicheng') || globalData.nicheng || '' - - this.setData({ avatarUrl, uid, nicheng }) - }, - - // 注册通知组件 - registerNotificationComponent() { - const app = getApp() - const notificationComp = this.selectComponent('#global-notification') - if (notificationComp && notificationComp.showNotification) { - app.globalData.globalNotification = { - show: (data) => notificationComp.showNotification(data), - hide: () => notificationComp.hideNotification() - } - } - }, - - // 获取组长信息(刷新) - async getZuzhangInfo() { - this.setData({ isLoading: true }) - try { - const res = await request({ - url: '/yonghu/zuzhangxinxi', - method: 'POST' - }) - if (res && res.data.code === 200) { - const data = res.data.data - this.setData({ - ketixian: data.ketixian || '0.00', - guanshiCount: data.guanshiCount || 0, - fenhongZonge: data.fenhongZonge || '0.00', - isLoading: false - }) - wx.showToast({ title: '刷新成功', icon: 'success', duration: 1500 }) - } else { - throw new Error(res?.data?.msg || '获取信息失败') - } - } catch (error) { - console.error('获取组长信息失败:', error) - this.setData({ isLoading: false }) - wx.showToast({ title: error.message || '刷新失败', icon: 'none' }) - } - }, - - // 刷新按钮点击 - refreshZuzhangInfo() { - this.getZuzhangInfo() - }, - - // 邀请码输入 - onInviteCodeInput(e) { - this.setData({ inviteCode: e.detail.value.trim() }) - }, - - // 注册组长 - async onRegister() { - const { inviteCode } = this.data - if (!inviteCode) { - wx.showToast({ title: '请输入邀请码', icon: 'none', duration: 2000 }) - return - } - if (inviteCode.length > 100) { - wx.showToast({ title: '邀请码不能超过100字符', icon: 'none', duration: 2000 }) - return - } - - this.setData({ isLoading: true }) - try { - const res = await request({ - url: '/yonghu/zuzhangzhuce', - method: 'POST', - data: { inviteCode } - }) - if (res && res.data.code === 200) { - const data = res.data.data || {} - syncRoleStatuses(data) - syncProfileFields(data) - if (!Object.prototype.hasOwnProperty.call(data, 'zuzhangstatus')) { - wx.setStorageSync('zuzhangstatus', 1) - } - this.setData({ - isZuzhang: true, - ketixian: data.ketixian || '0.00', - guanshiCount: data.guanshiCount || 0, - fenhongZonge: data.fenhongZonge || '0.00', - isLoading: false - }) - this.loadUserInfo() - ensureRoleOnCenterPage(this, 'zuzhang') - wx.showToast({ title: '注册成功!', icon: 'success', duration: 2000 }) - } else { - throw new Error(res?.data?.msg || '注册失败') - } - } catch (error) { - console.error('注册失败:', error) - this.setData({ isLoading: false }) - wx.showToast({ title: error.message || '注册失败', icon: 'error' }) - } - }, - - // 跳转到提现页面 - goToWithdraw() { - const { ketixian } = this.data - wx.navigateTo({ - url: `/pages/withdraw/withdraw?from=zuzhang&amount=${ketixian}` - }) - }, - - // 跳转到分红记录 - goToFenhongJilu() { - wx.navigateTo({ url: '/pages/leader-bonus-log/leader-bonus-log' }) - }, - - // 跳转到邀请管事 - goToYaoqingGuanshi() { - wx.navigateTo({ url: '/pages/invite-manager/invite-manager' }) - } -}) \ No newline at end of file +// pages/leader/leader.js +import { createPage, request, syncRoleStatuses, syncProfileFields, ensureRoleOnCenterPage, isCenterPageActive, parseSceneOptions } from '../../utils/base-page.js' + +Page(createPage({ + data: { + isZuzhang: false, + + // 邀请码 + inviteCode: '', + + // 用户信息 + uid: '', + nicheng: '', + avatarUrl: '', + + // 组长信息 + ketixian: '0.00', + guanshiCount: 0, + fenhongZonge: '0.00', + }, + + onLoad(options) { + const parsed = parseSceneOptions(options) + if (parsed.inviteCode) { + this.setData({ inviteCode: parsed.inviteCode }) + } + wx.redirectTo({ url: '/pages/fighter/fighter' }) + return + // 以下保留供恢复参考 + this.checkLoginStatus() + }, + + onShow() { + this.registerNotificationComponent() + this.checkColdStartPopup('zuzhangduan') + if (isCenterPageActive(this, 'isZuzhang', 'zuzhangstatus')) { + if (!this.data.isZuzhang) this.setData({ isZuzhang: true }) + ensureRoleOnCenterPage(this, 'zuzhang') + this.getZuzhangInfo() + } + }, + + // 检查登录状态 + checkLoginStatus() { + const token = wx.getStorageSync('token') + if (!token) { + wx.showModal({ + title: '提示', + content: '您尚未登录,请先登录', + confirmText: '去登录', + success: (res) => { + if (res.confirm) { + wx.reLaunch({ url: '/pages/gerenzhongxin/gerenzhongxin' }) + } + } + }) + return false + } + this.checkRoleStatus() + return true + }, + + // 角色状态检查后的额外数据加载(由 mixin 的 checkRoleStatus 调用) + checkGlobalData() { + this.loadUserInfo() + this.getZuzhangInfo() + }, + + // 获取组长信息(刷新) + async getZuzhangInfo() { + this.loadData({ + url: '/yonghu/zuzhangxinxi', + method: 'POST', + errorMsg: '获取信息失败', + onSuccess: (data) => { + this.setData({ + ketixian: data.ketixian || '0.00', + guanshiCount: data.guanshiCount || 0, + fenhongZonge: data.fenhongZonge || '0.00', + }) + wx.showToast({ title: '刷新成功', icon: 'success', duration: 1500 }) + } + }) + }, + + // 刷新按钮点击 + refreshZuzhangInfo() { + this.throttledRefresh(this.getZuzhangInfo) + }, + + // 邀请码输入 + onInviteCodeInput(e) { + this.setData({ inviteCode: e.detail.value.trim() }) + }, + + // 注册组长 + onRegister() { + const { inviteCode } = this.data + this.registerWithInviteCode({ + inviteCode, + apiUrl: '/yonghu/zuzhangzhuce', + role: 'zuzhang', + statusKey: 'zuzhangstatus', + successMsg: '注册成功!', + onSuccess: (userData) => { + this.setData({ + isZuzhang: true, + ketixian: userData.ketixian || '0.00', + guanshiCount: userData.guanshiCount || 0, + fenhongZonge: userData.fenhongZonge || '0.00', + }) + this.loadUserInfo() + ensureRoleOnCenterPage(this, 'zuzhang') + } + }) + }, + + // 跳转到提现页面 + goToWithdraw() { + const { ketixian } = this.data + wx.navigateTo({ + url: `/pages/withdraw/withdraw?from=zuzhang&amount=${ketixian}` + }) + }, + + // 跳转到分红记录 + goToFenhongJilu() { + wx.navigateTo({ url: '/pages/leader-bonus-log/leader-bonus-log' }) + }, + + // 跳转到邀请管事 + goToYaoqingGuanshi() { + wx.navigateTo({ url: '/pages/invite-manager/invite-manager' }) + } +}, { roleConfig: { role: 'dashou', statusKey: 'zuzhangstatus', dataFlag: 'isZuzhang', pageId: 'zuzhangduan', apiUrl: '/yonghu/zuzhangxinxi' } })) diff --git a/pages/manager-rank/manager-rank.wxml b/pages/manager-rank/manager-rank.wxml index ae0e82e..3b71ae7 100644 --- a/pages/manager-rank/manager-rank.wxml +++ b/pages/manager-rank/manager-rank.wxml @@ -1,235 +1,205 @@ - - - + + + - + - + - + - - - - + + + + - - - + - + - + - - 管事排行榜 - - GUANSHI RANKING + + 管事排行榜 + + GUANSHI RANKING - + - - - + + - 今日充值人数 - + 今日充值人数 + - - 今月充值人数 - + 今月充值人数 + - + - - - - - - - - 数据加载中 - Connecting to server... + + + + + + 数据加载中 + Connecting to server... - + - - + + - - 📡 - - 排行榜数据准备中 - 当前显示为模拟数据,实时排名即将开放 + + 📡 + + 排行榜数据准备中 + 当前显示为模拟数据,实时排名即将开放 - - + - - + + - - - - - 2 + + + 2 - + - - - - - - - - - + + + + + + + - 2 + 2 - + - - 申诉 - 立即缴纳 - 关闭 + 申诉 + 立即缴纳 + 关闭 @@ -100,8 +100,8 @@ - 取消 - 提交申诉 + 取消 + 提交申诉 diff --git a/pages/penalty/components/fakuan-list/fakuan-list.wxss b/pages/penalty/components/fakuan-list/fakuan-list.wxss index 3e02bb2..8b27be6 100644 --- a/pages/penalty/components/fakuan-list/fakuan-list.wxss +++ b/pages/penalty/components/fakuan-list/fakuan-list.wxss @@ -50,10 +50,10 @@ page { background: #f5f5f5; } .progress-text { font-size: 22rpx; color: #2e7d32; text-align: right; margin-top: 4rpx; } .modal-foot { width: 100%; box-sizing: border-box; display: flex; padding: 16rpx 30rpx 30rpx; gap: 20rpx; border-top: 1px solid #eee; } -.btn { flex: 1; text-align: center; padding: 22rpx 0; border-radius: 16rpx; font-size: 28rpx; font-weight: 600; } +.pbtn { flex: 1; text-align: center; padding: 22rpx 0; border-radius: 16rpx; font-size: 28rpx; font-weight: 600; } .primary { background: #2e7d32; color: #fff; } .default { background: #f0f0f0; color: #555; } -.btn:active { opacity: 0.8; } +.pbtn:active { opacity: 0.8; } /* ===== 获取更多按钮 ===== */ diff --git a/pages/penalty/components/fakuan-pay/fakuan-pay.wxml b/pages/penalty/components/fakuan-pay/fakuan-pay.wxml index d3a9c3a..92de658 100644 --- a/pages/penalty/components/fakuan-pay/fakuan-pay.wxml +++ b/pages/penalty/components/fakuan-pay/fakuan-pay.wxml @@ -48,8 +48,8 @@ 确认后将从您的佣金/分红中扣除 - 取消 - 确认 + 取消 + 确认 diff --git a/pages/penalty/components/fakuan-pay/fakuan-pay.wxss b/pages/penalty/components/fakuan-pay/fakuan-pay.wxss index 1f7f292..6cc93a8 100644 --- a/pages/penalty/components/fakuan-pay/fakuan-pay.wxss +++ b/pages/penalty/components/fakuan-pay/fakuan-pay.wxss @@ -53,7 +53,7 @@ @keyframes spin { to { transform: rotate(360deg); } } .loading-text { color: #fff; font-size: 26rpx; } - .btn { flex: 1; text-align: center; padding: 22rpx 0; border-radius: 16rpx; font-size: 28rpx; font-weight: 600; } + .pbtn { flex: 1; text-align: center; padding: 22rpx 0; border-radius: 16rpx; font-size: 28rpx; font-weight: 600; } .primary { background: #2e7d32; color: #fff; } .default { background: #f0f0f0; color: #555; } - .btn:active { opacity: 0.8; } \ No newline at end of file + .pbtn:active { opacity: 0.8; } \ No newline at end of file diff --git a/pages/penalty/components/jifen-list/jifen-list.wxml b/pages/penalty/components/jifen-list/jifen-list.wxml index ccef2b0..a70dbb5 100644 --- a/pages/penalty/components/jifen-list/jifen-list.wxml +++ b/pages/penalty/components/jifen-list/jifen-list.wxml @@ -60,8 +60,8 @@ - 申诉 - 关闭 + 申诉 + 关闭 @@ -97,8 +97,8 @@ - 取消 - 提交申诉 + 取消 + 提交申诉 diff --git a/pages/penalty/components/jifen-list/jifen-list.wxss b/pages/penalty/components/jifen-list/jifen-list.wxss index 5cf6c07..bc802da 100644 --- a/pages/penalty/components/jifen-list/jifen-list.wxss +++ b/pages/penalty/components/jifen-list/jifen-list.wxss @@ -59,10 +59,10 @@ page { background: #f5f5f5; } /* 🔥 底部按钮区左右 padding 严格均等 */ .modal-foot { width: 100%; box-sizing: border-box; display: flex; padding: 16rpx 30rpx 30rpx; gap: 20rpx; border-top: 1px solid #eee; } -.btn { flex: 1; text-align: center; padding: 22rpx 0; border-radius: 16rpx; font-size: 28rpx; font-weight: 600; } +.pbtn { flex: 1; text-align: center; padding: 22rpx 0; border-radius: 16rpx; font-size: 28rpx; font-weight: 600; } .primary { background: #2e7d32; color: #fff; } .default { background: #f0f0f0; color: #555; } -.btn:active { opacity: 0.8; } +.pbtn:active { opacity: 0.8; } /* ===== 获取更多按钮 ===== */ diff --git a/pages/poster/poster.wxml b/pages/poster/poster.wxml index aa2c48d..f64dc1f 100644 --- a/pages/poster/poster.wxml +++ b/pages/poster/poster.wxml @@ -25,38 +25,38 @@ - - - 📥 - 保存二维码 + + + 📥 + 保存二维码 - - - {{qrcodeUrl ? '重新获取' : '获取二维码'}} + + + {{qrcodeUrl ? '重新获取' : '获取二维码'}} - + - 生成中... + 生成中... - - - 🎨 - 生成我的专属海报 + + + 🎨 + 生成我的专属海报 - + - - 📥 - 保存海报 + + 📥 + 保存海报 - - ↩️ - 返回 + + ↩️ + 返回 @@ -68,4 +68,4 @@ - \ No newline at end of file + diff --git a/pages/poster/poster.wxss b/pages/poster/poster.wxss index e52db1f..0157a40 100644 --- a/pages/poster/poster.wxss +++ b/pages/poster/poster.wxss @@ -1,269 +1,237 @@ -/* 机甲风格推广海报页面(含新增专属海报样式) */ +/* 推广海报页面 */ page { - background: #0a0e17; - min-height: 100vh; - font-family: 'Avenir', 'PingFang SC', 'Helvetica Neue', sans-serif; - color: #fff; - } - - /* 背景网格 */ - .grid-bg { - position: fixed; - top: 0; - left: 0; - width: 100%; - height: 100%; - background-image: - linear-gradient(rgba(0, 160, 255, 0.05) 1px, transparent 1px), - linear-gradient(90deg, rgba(0, 160, 255, 0.05) 1px, transparent 1px); - background-size: 50rpx 50rpx; - pointer-events: none; - z-index: 0; - } - - .page-container { - position: relative; - z-index: 2; - min-height: 100vh; - padding: 30rpx 25rpx; - box-sizing: border-box; - } - - /* 机械装饰线条 */ - .mech-line { - position: absolute; - pointer-events: none; - z-index: 1; - } - .line-h { - top: 200rpx; - left: 0; - width: 100%; - height: 2rpx; - background: linear-gradient(90deg, transparent, #00a6ff, transparent); - } - .line-v { - right: 50rpx; - top: 100rpx; - width: 2rpx; - height: 300rpx; - background: linear-gradient(180deg, transparent, #00a6ff, transparent); - } - - .content { - position: relative; - z-index: 3; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - min-height: 100vh; - padding: 40rpx 0; - } - - /* 标题区域 */ - .title-section { - text-align: center; - margin-bottom: 60rpx; - } - .title { - font-size: 44rpx; - font-weight: bold; - color: #fff; - text-shadow: 0 0 30rpx #00a6ff; - letter-spacing: 2rpx; - display: block; - margin-bottom: 20rpx; - } - .subtitle { - font-size: 28rpx; - color: #9cf; - opacity: 0.9; - letter-spacing: 1rpx; - } - - /* 二维码区域(方形,无斜切) */ - .qrcode-section { - width: 100%; - display: flex; - justify-content: center; - align-items: center; - margin-bottom: 60rpx; - } - .qrcode-wrapper { - position: relative; - width: 400rpx; - height: 400rpx; - background: rgba(0, 20, 40, 0.8); - border: 4rpx solid #00a6ff; - /* 移除 clip-path,保持方形 */ - display: flex; - align-items: center; - justify-content: center; - box-shadow: 0 0 80rpx rgba(0, 166, 255, 0.4); - animation: qrcodePulse 2s infinite alternate; - } - .qrcode-img { - width: 90%; - height: 90%; - object-fit: contain; - } - .qrcode-glow { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - background: radial-gradient(circle at 30% 30%, rgba(0, 166, 255, 0.2), transparent 70%); - pointer-events: none; - } - .empty-qrcode { - width: 400rpx; - height: 400rpx; - background: rgba(0, 20, 40, 0.8); - border: 4rpx dashed #00a6ff; - /* 方形 */ - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - } - .empty-icon { - font-size: 80rpx; - color: #00a6ff; - margin-bottom: 20rpx; - opacity: 0.5; - } - .empty-text { - font-size: 26rpx; - color: #9cf; - text-align: center; - padding: 0 40rpx; - } - - /* 按钮区域(机甲斜切风格) */ - .button-section { - width: 100%; - display: flex; - justify-content: center; - margin-bottom: 40rpx; - } - .btn-group { - display: flex; - gap: 20rpx; - flex-wrap: wrap; - justify-content: center; - } - .btn { - width: 220rpx; - height: 80rpx; - background: rgba(0, 30, 60, 0.8); - border: 2rpx solid #00a6ff; - border-right: none; - border-bottom: none; - clip-path: polygon(0 0, 100% 0, 90% 100%, 0 100%); - display: flex; - align-items: center; - justify-content: center; - position: relative; - transition: 0.2s; - cursor: pointer; - } - .btn:active { - transform: scale(0.96); - box-shadow: 0 0 40rpx #00a6ff; - } - .save-btn { - border-color: #00a6ff; - } - .refresh-btn { - border-color: #ffaa00; - } - .refresh-btn .btn-icon { - color: #ffaa00; - } - /* 🔥【新增】生成海报按钮样式 */ - .poster-btn { - border-color: #ffaa00; - } - .poster-btn .btn-icon { - color: #ffaa00; - } - .loading-btn { - opacity: 0.7; - pointer-events: none; - } - .btn-icon { - font-size: 36rpx; - margin-right: 8rpx; - } - .btn-text { - font-size: 26rpx; - font-weight: 500; - color: #fff; - } - .loading-spinner-small { - width: 30rpx; - height: 30rpx; - border: 4rpx solid rgba(255,255,255,0.2); - border-top: 4rpx solid #fff; - border-radius: 50%; - animation: spin 1s linear infinite; - margin-right: 10rpx; - } - - /* 🔥【新增】专属海报展示区 */ - .poster-section { - margin-top: 40rpx; - display: flex; - flex-direction: column; - align-items: center; - width: 100%; - } - .poster-canvas { - width: 600rpx; - height: 900rpx; - background: #1a1f2e; /* 占位色 */ - border: 4rpx solid #00a6ff; - box-shadow: 0 0 60rpx #00a6ff; - /* 保持方形,无斜切 */ - } - .poster-actions { - display: flex; - gap: 30rpx; - margin-top: 30rpx; - justify-content: center; - } - .save-poster-btn { - background: linear-gradient(45deg, #0066cc, #00a6ff); - border-color: #00a6ff; - } - .back-btn { - background: rgba(0, 30, 60, 0.8); - border-color: #00a6ff; - } - - /* 使用说明(保留) */ - .instruction { - margin-top: 30rpx; - padding: 20rpx 40rpx; - background: rgba(0, 30, 50, 0.5); - border-left: 4rpx solid #00a6ff; - clip-path: polygon(0 0, 100% 0, 98% 100%, 0 100%); - } - .instruction-text { - font-size: 24rpx; - color: #9cf; - line-height: 1.6; - } - - /* 动画 */ - @keyframes qrcodePulse { - 0% { box-shadow: 0 0 40rpx rgba(0, 166, 255, 0.3); } - 100% { box-shadow: 0 0 100rpx rgba(0, 166, 255, 0.6); } - } - @keyframes spin { - 0% { transform: rotate(0deg); } - 100% { transform: rotate(360deg); } - } \ No newline at end of file + background: #0a0e17; + min-height: 100vh; + font-family: 'Avenir', 'PingFang SC', 'Helvetica Neue', sans-serif; + color: #fff; +} + +/* 背景网格 */ +.grid-bg { + position: fixed; + top: 0; left: 0; + width: 100%; height: 100%; + background-image: + linear-gradient(rgba(0, 160, 255, 0.05) 1px, transparent 1px), + linear-gradient(90deg, rgba(0, 160, 255, 0.05) 1px, transparent 1px); + background-size: 50rpx 50rpx; + pointer-events: none; + z-index: 0; +} + +.page-container { + position: relative; + z-index: 2; + min-height: 100vh; + padding: 30rpx 25rpx; + box-sizing: border-box; +} + +/* 机械装饰线条 */ +.mech-line { + position: absolute; + pointer-events: none; + z-index: 1; +} +.line-h { + top: 200rpx; left: 0; + width: 100%; height: 2rpx; + background: linear-gradient(90deg, transparent, #00a6ff, transparent); +} +.line-v { + right: 50rpx; top: 100rpx; + width: 2rpx; height: 300rpx; + background: linear-gradient(180deg, transparent, #00a6ff, transparent); +} + +.content { + position: relative; + z-index: 3; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + min-height: 100vh; + padding: 40rpx 0; +} + +/* 标题区域 */ +.title-section { + text-align: center; + margin-bottom: 60rpx; +} +.title { + font-size: 44rpx; + font-weight: bold; + color: #fff; + text-shadow: 0 0 30rpx #00a6ff; + letter-spacing: 2rpx; + display: block; + margin-bottom: 20rpx; +} +.subtitle { + font-size: 28rpx; + color: #9cf; + opacity: 0.9; + letter-spacing: 1rpx; +} + +/* 二维码区域 */ +.qrcode-section { + width: 100%; + display: flex; + justify-content: center; + align-items: center; + margin-bottom: 60rpx; +} +.qrcode-wrapper { + position: relative; + width: 400rpx; height: 400rpx; + background: rgba(0, 20, 40, 0.8); + border: 4rpx solid #00a6ff; + display: flex; + align-items: center; + justify-content: center; + box-shadow: 0 0 80rpx rgba(0, 166, 255, 0.4); + animation: qrcodePulse 2s infinite alternate; +} +.qrcode-img { + width: 90%; height: 90%; + object-fit: contain; +} +.qrcode-glow { + position: absolute; + top: 0; left: 0; + width: 100%; height: 100%; + background: radial-gradient(circle at 30% 30%, rgba(0, 166, 255, 0.2), transparent 70%); + pointer-events: none; +} +.empty-qrcode { + width: 400rpx; height: 400rpx; + background: rgba(0, 20, 40, 0.8); + border: 4rpx dashed #00a6ff; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} +.empty-icon { + font-size: 80rpx; + color: #00a6ff; + margin-bottom: 20rpx; + opacity: 0.5; +} +.empty-text { + font-size: 26rpx; + color: #9cf; + text-align: center; + padding: 0 40rpx; +} + +/* 按钮区域 */ +.button-section { + width: 100%; + display: flex; + justify-content: center; + margin-bottom: 40rpx; +} +.pbtn-group { + display: flex; + gap: 20rpx; + flex-wrap: wrap; + justify-content: center; +} +.pbtn { + width: 220rpx; + height: 80rpx; + background: rgba(0, 30, 60, 0.8); + border: 2rpx solid #00a6ff; + border-right: none; + border-bottom: none; + clip-path: polygon(0 0, 100% 0, 90% 100%, 0 100%); + display: flex; + align-items: center; + justify-content: center; + position: relative; + transition: 0.2s; +} +.pbtn:active { + transform: scale(0.96); + box-shadow: 0 0 40rpx #00a6ff; +} +.pbtn-save { border-color: #00a6ff; } +.pbtn-refresh { border-color: #ffaa00; } +.pbtn-refresh .pbtn-icon { color: #ffaa00; } +.pbtn-poster { border-color: #ffaa00; } +.pbtn-poster .pbtn-icon { color: #ffaa00; } +.pbtn-loading { + opacity: 0.7; + pointer-events: none; +} +.pbtn-icon { + font-size: 36rpx; + margin-right: 8rpx; +} +.pbtn-text { + font-size: 26rpx; + font-weight: 500; + color: #fff; +} +.loading-spinner-small { + width: 30rpx; height: 30rpx; + border: 4rpx solid rgba(255,255,255,0.2); + border-top: 4rpx solid #fff; + border-radius: 50%; + animation: spin 1s linear infinite; + margin-right: 10rpx; +} + +/* 专属海报展示区 */ +.poster-section { + margin-top: 40rpx; + display: flex; + flex-direction: column; + align-items: center; + width: 100%; +} +.poster-canvas { + width: 600rpx; height: 900rpx; + background: #1a1f2e; + border: 4rpx solid #00a6ff; + box-shadow: 0 0 60rpx #00a6ff; +} +.poster-actions { + display: flex; + gap: 30rpx; + margin-top: 30rpx; + justify-content: center; +} +.pbtn-save-poster { + background: linear-gradient(45deg, #0066cc, #00a6ff); + border-color: #00a6ff; +} +.pbtn-back { + background: rgba(0, 30, 60, 0.8); + border-color: #00a6ff; +} + +/* 使用说明 */ +.instruction { + margin-top: 30rpx; + padding: 20rpx 40rpx; + background: rgba(0, 30, 50, 0.5); + border-left: 4rpx solid #00a6ff; + clip-path: polygon(0 0, 100% 0, 98% 100%, 0 100%); +} +.instruction-text { + font-size: 24rpx; + color: #9cf; + line-height: 1.6; +} + +/* 动画 */ +@keyframes qrcodePulse { + 0% { box-shadow: 0 0 40rpx rgba(0, 166, 255, 0.3); } + 100% { box-shadow: 0 0 100rpx rgba(0, 166, 255, 0.6); } +} diff --git a/pages/product-detail/product-detail.js b/pages/product-detail/product-detail.js index f619c1f..1f698b8 100644 --- a/pages/product-detail/product-detail.js +++ b/pages/product-detail/product-detail.js @@ -140,8 +140,7 @@ Page({ that.handleLoadError(res.data?.msg || '加载失败') } }, - fail(error) { - console.error('商品详情加载失败:', error) + fail() { that.handleLoadError('网络错误,请重试') } }) @@ -234,6 +233,10 @@ Page({ this.loadShangpinDetail() }, + goToKefu() { + wx.navigateTo({ url: '/pages/cs-chat/cs-chat' }) + }, + onShareAppMessage() { const { shangpinData, shangpinId } = this.data diff --git a/pages/product-detail/product-detail.wxml b/pages/product-detail/product-detail.wxml index 4ad5e00..aa92f2a 100644 --- a/pages/product-detail/product-detail.wxml +++ b/pages/product-detail/product-detail.wxml @@ -1,112 +1,123 @@ - - - - - - - - - - 加载中... - - - - - - - 加载失败 - - - - - - - - - - - - {{shangpinData.kucunText || '库存充足'}} - - - - - - - - - - {{shangpinData.biaoti || ''}} - - - - - - ¥ - {{shangpinData.priceInteger || '0'}} - .{{shangpinData.priceDecimal || '00'}} - - - - - - {{shangpinData.xiaoliangText || '0'}}次购买 - - - - - - - - - 商品介绍: - - - {{shangpinData.xiangqing}} - - - - - - - - 购买须知: - - - {{shangpinData.xiadanxuzhi}} - - - - - - - - - - - - - - - - - - - - - - 立即预约 - - - - - \ No newline at end of file + + + + + + 加载中... + + + + + 加载失败 + 重新加载 + + + + + + + + + + {{shangpinData.kucun <= 0 ? '已售罄' : '现货'}} + + + + + + + ¥ + {{shangpinData.priceInteger || '0'}} + .{{shangpinData.priceDecimal || '00'}} + + + 已拼{{shangpinData.xiaoliangText || '0'}}件 + + + + + + + 热卖 + + {{shangpinData.biaoti || ''}} + + + + + + + 极速接单 + + + + 资金担保 + + + + 售后保障 + + + + 隐私保护 + + + + + + + 选择 + 服务规格、购买数量 + + > + + + + + + 商品介绍 + + {{shangpinData.xiangqing}} + + + + + + 购买须知 + + {{shangpinData.xiadanxuzhi}} + + + + + + 服务规则 + + + + + + + + + + + + + + 客服 + + + + {{shangpinData.kucun <= 0 ? '已售罄' : '立即下单'}} + ¥{{shangpinData.priceInteger || '0'}}.{{shangpinData.priceDecimal || '00'}} + + + + + + diff --git a/pages/product-detail/product-detail.wxss b/pages/product-detail/product-detail.wxss index 8590c7c..ef2b7ac 100644 --- a/pages/product-detail/product-detail.wxss +++ b/pages/product-detail/product-detail.wxss @@ -1,349 +1,347 @@ -/* pages/product-detail/product-detail.wxss */ -.detail-page { - width: 100%; - background: #f5f5f5; - } - - /* 加载状态 */ - .loading-container { - position: absolute; - background: #f5f5f5; - z-index: 100; - } - - .loading-content { - display: flex; - flex-direction: column; - align-items: center; - } - - .loading-spinner { - width: 80rpx; - height: 80rpx; - position: relative; - margin-bottom: 30rpx; - } - - .spinner-ring { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - border: 4rpx solid rgba(82, 196, 26, 0.2); - border-radius: 50%; - } - - .spinner-dot { - position: absolute; - top: 0; - left: 50%; - width: 12rpx; - height: 12rpx; - background: #52c41a; - border-radius: 50%; - transform: translateX(-50%); - animation: spin 1s linear infinite; - } - - .loading-text { - font-size: 28rpx; - color: #666; - font-weight: 500; - } - - /* 错误状态 */ - .error-container { - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - background: #f5f5f5; - z-index: 100; - padding: 0; - } - - .error-icon { - width: 180rpx; - height: 180rpx; - opacity: 0.3; - margin-bottom: 30rpx; - } - - .error-text { - font-size: 30rpx; - color: #999; - margin-bottom: 40rpx; - } - - .error-btn { - font-weight: 500; - padding: 20rpx 50rpx; - border-radius: 50rpx; - box-shadow: 0 8rpx 24rpx rgba(82, 196, 26, 0.3); - } - - /* 商品详情内容 */ - .detail-content { - padding-bottom: 140rpx; /* 给底部按钮留出空间 */ - } - - /* 商品图片区域(占满屏幕) */ - .image-section { - width: 100%; - background: white; - } - - .image-container { - width: 100%; - height: 750rpx; /* 正方形,占满屏幕宽度 */ - position: relative; - overflow: hidden; - background: #f5f5f5; - } - - .product-image { - width: 100%; - height: 100%; - } - - /* 库存标签 */ - .kucun-tag { - position: absolute; - bottom: 30rpx; - right: 30rpx; - padding: 12rpx 24rpx; - border-radius: 30rpx; - backdrop-filter: blur(10rpx); - z-index: 10; - } - - .kucun-normal { - background: rgba(0, 0, 0, 0.6); - color: white; - } - - .kucun-none { - background: rgba(255, 77, 79, 0.8); - color: white; - } - - .kucun-text { - font-size: 24rpx; - font-weight: 500; - } - - /* 下方内容区域(顶部有圆角,与图片重叠一部分) */ - .content-area { - position: relative; - background: white; - border-radius: 40rpx 40rpx 0 0; - margin-top: -40rpx; /* 与图片重叠一部分,实现平滑过渡 */ - padding: 40rpx 30rpx 0; - min-height: 400rpx; - } - - /* 标题和价格区域 */ - .title-price-section { - margin-bottom: 40rpx; - } - - .title-row { - margin-bottom: 30rpx; - } - - .product-title { - font-size: 38rpx; - font-weight: 700; - color: #222; - line-height: 1.5; - display: -webkit-box; - -webkit-box-orient: vertical; - -webkit-line-clamp: 2; - overflow: hidden; - } - - /* 价格和销量行 */ - .price-row { - display: flex; - justify-content: space-between; - align-items: center; - } - - /* 价格部分 */ - .price-section { - display: flex; - align-items: baseline; - } - - .price-icon { - font-size: 34rpx; - color: #ff4d4f; - font-weight: 600; - } - - .price-integer { - font-size: 58rpx; - color: #ff4d4f; - font-weight: 700; - margin-left: 4rpx; - } - - .price-decimal { - font-size: 34rpx; - color: #ff4d4f; - font-weight: 600; - } - - /* 销量部分 */ - .sales-section { - display: flex; - align-items: center; - } - - .sales-icon { - width: 30rpx; - height: 30rpx; - margin-right: 12rpx; - opacity: 0.6; - } - - .sales-text { - font-size: 28rpx; - color: #999; - font-weight: 400; - } - - /* 信息区域(商品介绍、购买须知) */ - .info-section { - margin-bottom: 40rpx; - padding-top: 30rpx; - border-top: 1rpx solid #f0f0f0; - } - - .section-header { - display: flex; - align-items: center; - margin-bottom: 20rpx; - } - - .section-icon { - width: 36rpx; - height: 36rpx; - margin-right: 16rpx; - } - - .section-title { - font-size: 32rpx; - font-weight: 600; - color: #52c41a; /* 绿色 */ - } - - .section-content { - line-height: 1.8; - padding-left: 52rpx; /* 与图标对齐 */ - } - - .content-text { - font-size: 28rpx; - color: #333; - font-weight: 400; - white-space: pre-line; - line-height: 1.8; - } - - /* 规则图片区域 */ - .rule-section { - margin: 40rpx 0; - padding-top: 30rpx; - border-top: 1rpx solid #f0f0f0; - } - - .rule-image { - width: 100%; - border-radius: 16rpx; - display: block; - box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.08); - } - - /* 占位区域 */ - .placeholder { - height: 140rpx; - } - - /* 立即下单按钮容器(固定底部) */ - .order-btn-container { - background: linear-gradient(transparent, rgba(255, 255, 255, 0.9) 70%); - } - - /* 立即下单按钮 */ - .order-btn { - border-radius: 50rpx; - height: 100rpx; - box-shadow: - 0 12rpx 36rpx rgba(82, 196, 26, 0.4), - 0 4rpx 12rpx rgba(82, 196, 26, 0.3); - transition: all 0.3s cubic-bezier(0.34, 1.56, 0.64, 1); - } - - .order-btn:active { - box-shadow: - 0 6rpx 24rpx rgba(82, 196, 26, 0.5), - 0 2rpx 8rpx rgba(82, 196, 26, 0.4); - } - - /* 按钮发光特效 */ - .btn-glow { - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - background: rgba(255, 255, 255, 0.1); - animation: glowPulse 2s infinite; - } - - /* 按钮流光特效 */ - .btn-streamer { - position: absolute; - top: 0; - left: 0; - width: 100rpx; - height: 100%; - background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.3), transparent); - transform: translateX(-100%); - animation: streamerFlow 3s infinite; - } - - /* 按钮文字 */ - .btn-text { - font-size: 36rpx; - color: white; - font-weight: 600; - letter-spacing: 2rpx; - position: relative; - z-index: 2; - } - - /* 动画定义 */ - @keyframes glowPulse { - 0%, 100% { - opacity: 0.5; - } - 50% { - opacity: 0.8; - } - } - - @keyframes streamerFlow { - 0% { - transform: translateX(-100%); - } - 50% { - transform: translateX(100%); - } - 100% { - transform: translateX(100%); - } - } \ No newline at end of file +/* pages/product-detail/product-detail.wxss */ + +page { + background: #f2f2f2; +} + +.page { + min-height: 100vh; + padding-bottom: 140rpx; + box-sizing: border-box; +} + +/* 加载 */ +.loading-box { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + height: 60vh; +} + +.spinner { + width: 56rpx; + height: 56rpx; + border: 4rpx solid #f0f0f0; + border-top-color: #e02e24; + border-radius: 50%; + animation: spin 0.7s linear infinite; + margin-bottom: 20rpx; +} + +.loading-text { + font-size: 26rpx; + color: #999; +} + +/* 错误 */ +.error-box { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + height: 60vh; +} + +.error-text { + font-size: 30rpx; + color: #999; + margin-bottom: 32rpx; +} + +.error-btn { + padding: 16rpx 48rpx; + background: #e02e24; + color: #fff; + font-size: 28rpx; + font-weight: 600; + border-radius: 40rpx; +} + +/* 商品大图 */ +.hero { + position: relative; + width: 100%; + height: 750rpx; + background: #fff; +} + +.hero-img { + width: 100%; + height: 100%; +} + +.hero-badge { + position: absolute; + top: 24rpx; + left: 24rpx; + padding: 8rpx 16rpx; + border-radius: 6rpx; + background: rgba(224, 46, 36, 0.85); + display: flex; + align-items: center; + justify-content: center; + line-height: 1; +} + +.hero-badge-text { + font-size: 20rpx; + color: #fff; + font-weight: 600; + line-height: 1; +} + +/* 价格区 - 拼多多红底 */ +.price-block { + display: flex; + align-items: baseline; + justify-content: space-between; + padding: 24rpx 28rpx; + background: linear-gradient(135deg, #e02e24, #f05545); +} + +.price-left { + display: flex; + align-items: baseline; +} + +.price-sym { + font-size: 28rpx; + color: #fff; + font-weight: 700; +} + +.price-int { + font-size: 60rpx; + color: #fff; + font-weight: 800; + line-height: 1; + margin-left: 2rpx; +} + +.price-dec { + font-size: 28rpx; + color: rgba(255, 255, 255, 0.9); + font-weight: 700; +} + +.price-right { + display: flex; + align-items: center; +} + +.sold-text { + font-size: 24rpx; + color: rgba(255, 255, 255, 0.85); + background: rgba(0, 0, 0, 0.15); + padding: 6rpx 16rpx; + border-radius: 20rpx; +} + +/* 标题区 */ +.title-block { + background: #fff; + padding: 24rpx 28rpx 20rpx; +} + +.title-tags { + display: flex; + align-items: center; + margin-bottom: 12rpx; +} + +.tag-hot { + font-size: 20rpx; + color: #fff; + background: #e02e24; + padding: 4rpx 12rpx; + border-radius: 4rpx; + font-weight: 600; + margin-right: 12rpx; +} + +.title-text { + font-size: 32rpx; + font-weight: 600; + color: #222; + line-height: 1.6; + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 3; + overflow: hidden; +} + +/* 服务保障条 */ +.service-strip { + display: flex; + flex-wrap: wrap; + gap: 0; + padding: 16rpx 28rpx; + background: #fff; + margin-bottom: 16rpx; +} + +.service-item { + display: flex; + align-items: center; + margin-right: 24rpx; + margin-bottom: 8rpx; +} + +.service-check { + font-size: 20rpx; + color: #e02e24; + font-weight: 700; + margin-right: 4rpx; +} + +.service-label { + font-size: 22rpx; + color: #666; +} + +/* 选择SKU入口 */ +.sku-trigger { + display: flex; + align-items: center; + justify-content: space-between; + background: #fff; + padding: 24rpx 28rpx; + margin-bottom: 16rpx; +} + +.sku-trigger-left { + display: flex; + align-items: center; +} + +.sku-label { + font-size: 26rpx; + color: #999; + margin-right: 16rpx; +} + +.sku-value { + font-size: 26rpx; + color: #333; +} + +.sku-arrow { + font-size: 28rpx; + color: #ccc; + margin-left: 12rpx; +} + +/* 详情卡片 */ +.detail-card { + background: #fff; + margin-bottom: 16rpx; + padding: 28rpx; +} + +.detail-card-head { + margin-bottom: 20rpx; +} + +.detail-card-title { + font-size: 30rpx; + font-weight: 700; + color: #222; + position: relative; + padding-left: 16rpx; +} + +.detail-card-title::before { + content: ''; + position: absolute; + left: 0; + top: 4rpx; + width: 6rpx; + height: 28rpx; + border-radius: 3rpx; + background: #e02e24; +} + +.detail-card-body { + font-size: 26rpx; + color: #555; + line-height: 1.8; + white-space: pre-line; +} + +.rule-img { + width: 100%; + border-radius: 8rpx; + display: block; +} + +/* 底部留白 */ +.bottom-space { + height: 40rpx; +} + +/* 底部操作栏 */ +.bottom-bar { + position: fixed; + bottom: 0; + left: 0; + right: 0; + z-index: 9999; + background: #fff; + padding-bottom: env(safe-area-inset-bottom); + border-top: 1rpx solid #f0f0f0; +} + +.bottom-bar-inner { + display: flex; + align-items: stretch; + height: 100rpx; +} + +.bar-icon-btn { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 0 28rpx; + height: 96rpx; +} + +.bar-icon-text { + font-size: 22rpx; + color: #666; + margin-top: 4rpx; +} + +.bar-buy-btn { + width: 50%; + height: 100%; + border-radius: 0; + display: flex; + align-items: center; + justify-content: center; + background: #e02e24; + margin-left: auto; +} + +.bar-buy-btn--disabled { + background: #ccc; +} + +.bar-buy-btn--hover { + opacity: 0.9; + transform: scale(0.98); +} + +.bar-buy-text { + font-size: 30rpx; + font-weight: 700; + color: #fff; + margin-right: 8rpx; +} + +.bar-buy-price { + font-size: 26rpx; + font-weight: 700; + color: rgba(255, 255, 255, 0.9); +} diff --git a/pages/submit/submit.js b/pages/submit/submit.js index f26b7c2..37e5cfd 100644 --- a/pages/submit/submit.js +++ b/pages/submit/submit.js @@ -1,547 +1,332 @@ -// pages/submit/submit.js -const app = getApp() -import request from '../../utils/request.js' -// 引入弹窗服务(路径请根据项目实际调整) -import PopupService from '../../services/popupService.js' - -Page({ - data: { - // 全局变量 - ossImageUrl: '', - apiBaseUrl: '', - - // 从商品详情页传入的数据 - shangpinData: null, - shangpinId: '', - - // 表单数据 - nicheng: '', // 游戏昵称/ID - beizhu: '', // 备注信息 - zhiding: '', // 指定打手ID(新增) - jine: 0, // 总金额 - - // 页面状态 - isSubmitting: false, - canSubmit: false, - isPaying: false, - - // 支付相关 - orderId: '', - payParams: null, - checkCount: 0, - maxCheckCount: 3 - }, - - onLoad(options) { - // 获取全局变量 - this.setData({ - ossImageUrl: app.globalData.ossImageUrl || '', - apiBaseUrl: app.globalData.apiBaseUrl || '' - }) - - // 设置页面标题 - wx.setNavigationBarTitle({ - title: '提交订单' - }) - - // 解析传递的商品数据 - try { - if (options.data) { - const shangpinData = JSON.parse(decodeURIComponent(options.data)) - - // 处理图片URL(拼接完整路径) - if (shangpinData.image && !shangpinData.image.startsWith('http')) { - shangpinData.image = this.data.ossImageUrl + shangpinData.image - } - - // 处理价格显示 - if (shangpinData.price) { - const price = parseFloat(shangpinData.price) || 0 - const priceInteger = Math.floor(price) - let priceDecimal = '00' - - const decimalPart = (price - priceInteger).toFixed(2) - if (decimalPart > 0) { - priceDecimal = decimalPart.toString().split('.')[1] - } - - shangpinData.priceInteger = priceInteger - shangpinData.priceDecimal = priceDecimal - } - - // 设置商品数据 - this.setData({ - shangpinData: shangpinData, - shangpinId: shangpinData.id || '', - jine: parseFloat(shangpinData.price) || 0, - }) - } else { - this.handleDataError('商品数据不完整') - } - } catch (error) { - this.handleDataError('解析商品数据失败') - } - }, - - onShow() { - // 原有代码:注册通知组件 - this.registerNotificationComponent(); - - // 🆕 进入页面时触发弹窗检查(PopupService 会自动判断是否展示) - PopupService.checkAndShow(this, 'tijiao'); - }, - - // pages/submit/submit.js 中添加 onHide 方法(如果已有则合并内容) -onHide() { - const popupComp = this.selectComponent('#popupNotice'); - if (popupComp && popupComp.cleanup) { - popupComp.cleanup(); - } - }, - - // 新增:注册通知组件 - registerNotificationComponent() { - const app = getApp(); - const notificationComp = this.selectComponent('#global-notification'); - - if (notificationComp && notificationComp.showNotification) { - //console.log('🏪 商城页面注册通知组件'); - app.globalData.globalNotification = { - show: (data) => notificationComp.showNotification(data), - hide: () => notificationComp.hideNotification() - }; - } - }, - - /** - * 处理数据错误 - */ - handleDataError(errorMsg) { - wx.showToast({ - title: errorMsg || '数据错误', - icon: 'none' - }) - - setTimeout(() => { - wx.navigateBack() - }, 1500) - }, - - /** - * 游戏昵称输入事件 - */ - onNicknameInput(e) { - const value = e.detail.value.trim() - this.setData({ - nicheng: value - }, () => { - this.checkFormValid() - }) - }, - - /** - * 备注信息输入事件 - */ - onRemarkInput(e) { - const value = e.detail.value.trim() - this.setData({ - beizhu: value - }) - }, - - /** - * 指定打手ID输入事件 - */ - onZhidingInput(e) { - const value = e.detail.value.trim() - this.setData({ - zhiding: value - }) - }, - - /** - * 检查表单是否有效 - */ - checkFormValid() { - const { nicheng } = this.data - - const isValid = nicheng && nicheng.length > 0 && nicheng.length <= 15 - - this.setData({ - canSubmit: isValid - }) - }, - - /** - * 提交订单 - */ - onSubmitOrder() { - const { nicheng, beizhu, zhiding, jine, shangpinId, canSubmit, isSubmitting } = this.data - - // 验证表单 - if (!canSubmit) { - wx.showToast({ - title: '请填写游戏昵称', - icon: 'none' - }) - return - } - - // 防止重复提交 - if (isSubmitting) { - return - } - - this.setData({ - isSubmitting: true, - isPaying: false - }) - - // 显示loading(超时设为10秒) - wx.showLoading({ - title: '创建订单中...', - mask: true - }) - - const timeoutTimer = setTimeout(() => { - wx.hideLoading() - wx.showToast({ - title: '网络超时,请检查您是否已登录', - icon: 'none' - }) - this.setData({ - isSubmitting: false, - isPaying: false - }) - }, 10000) - - // 构建请求数据(添加zhiding字段) - const orderData = { - shangpin_id: shangpinId, // 商品ID - nicheng: nicheng, // 游戏昵称 - beizhu: beizhu || '', // 备注(可为空) - zhiding: zhiding || '', // 指定打手ID(可为空) - jine: jine // 金额 - } - - // 调用后端创建订单接口 - request({ - url: '/dingdan/xiadan', - method: 'POST', - data: orderData - }) - .then(res => { - clearTimeout(timeoutTimer) - wx.hideLoading() - - if (res.statusCode === 200 && res.data) { - const response = res.data - - if (response.code === 0 && response.data) { - // 保存订单ID和支付参数 - const orderId = response.data.dingdanid - const payParams = response.data.payParams || {} - - this.setData({ - orderId: orderId, - payParams: payParams, - isSubmitting: false, - isPaying: true, - checkCount: 0 - }) - - // 调起微信支付 - this.triggerWxPayment(payParams) - } else { - wx.showToast({ - title: response.msg || '创建订单失败', - icon: 'none' - }) - this.setData({ - isSubmitting: false, - isPaying: false - }) - } - } else { - wx.showToast({ - title: '网络错误,请重试', - icon: 'none' - }) - this.setData({ - isSubmitting: false, - isPaying: false - }) - } - }) - .catch(error => { - clearTimeout(timeoutTimer) - wx.hideLoading() - wx.showToast({ - title: '创建订单失败', - icon: 'none' - }) - this.setData({ - isSubmitting: false, - isPaying: false - }) - }) - }, - - /** - * 调起微信支付 - */ - triggerWxPayment(payParams) { - // 验证支付参数是否完整 - if (!payParams || !payParams.timeStamp || !payParams.nonceStr || - !payParams.package || !payParams.signType || !payParams.paySign) { - wx.showToast({ - title: '支付参数不完整', - icon: 'none' - }) - this.setData({ - isSubmitting: false, - isPaying: false - }) - return - } - - wx.requestPayment({ - timeStamp: payParams.timeStamp, - nonceStr: payParams.nonceStr, - package: payParams.package, - signType: payParams.signType, - paySign: payParams.paySign, - - success: (res) => { - // 支付成功,开始双重确认 - this.onPaymentSuccess() - }, - - fail: (err) => { - // 根据错误类型处理 - if (err.errMsg === 'requestPayment:fail cancel') { - // 用户取消支付 - this.handleUserCancel() - } else { - // 其他支付错误 - this.handlePaymentFail(err) - } - } - }) - }, - - /** - * 处理用户取消支付 - */ - handleUserCancel() { - const { orderId } = this.data - - wx.showToast({ - title: '已取消支付', - icon: 'none', - duration: 1500 - }) - - // 通知后端用户取消(静默请求,不阻塞用户) - if (orderId) { - request({ - url: '/dingdan/shibai', - method: 'POST', - data: { - dingdanid: orderId, - yuanyin: 'user_cancel' - } - }).catch(() => { - // 静默失败 - }) - } - - this.setData({ - isSubmitting: false, - isPaying: false - }) - }, - - /** - * 处理支付失败 - */ - handlePaymentFail(err) { - const { orderId } = this.data - - wx.showToast({ - title: '支付失败', - icon: 'none', - duration: 1500 - }) - - // 通知后端支付失败(简化流程,直接请求,不显示loading) - if (orderId) { - request({ - url: '/dingdan/shibai', - method: 'POST', - data: { - dingdanid: orderId, - yuanyin: 'payment_fail', - cuowu: err.errMsg || '' - } - }).catch(() => { - // 静默失败 - }).finally(() => { - this.setData({ - isSubmitting: false, - isPaying: false - }) - }) - } else { - this.setData({ - isSubmitting: false, - isPaying: false - }) - } - }, - - /** - * 支付成功处理(双重确认) - */ - onPaymentSuccess() { - const { orderId } = this.data - - if (!orderId) { - wx.showToast({ - title: '订单ID不存在', - icon: 'none' - }) - this.setData({ - isSubmitting: false, - isPaying: false - }) - return - } - - // 显示支付成功提示(时间缩短) - wx.showToast({ - title: '支付成功,确认中...', - icon: 'none', - duration: 1500 - }) - - // 开始双重确认流程 - this.checkOrderStatus() - }, - - /** - * 检查订单状态(双重确认) - */ - checkOrderStatus() { - const { orderId, checkCount, maxCheckCount } = this.data - - // 显示加载(时间缩短) - wx.showLoading({ - title: `确认支付状态${checkCount > 0 ? `(${checkCount + 1})` : ''}`, - mask: true - }) - - // 超时设为5秒 - const timeoutTimer = setTimeout(() => { - wx.hideLoading() - this.handleConfirmError('网络超时') - }, 5000) - - // 向后端发起支付复查请求 - request({ - url: '/dingdan/fucha', - method: 'POST', - data: { - dingdanid: orderId - } - }) - .then(res => { - clearTimeout(timeoutTimer) - wx.hideLoading() - - if (res.statusCode === 200 && res.data) { - const response = res.data - - if (response.code === 0) { - // 确认成功,订单状态已更新 - this.handleConfirmSuccess() - } else { - // 后端返回错误 - this.handleConfirmError(response.msg || '订单状态异常') - } - } else { - // 网络错误 - this.handleConfirmError('网络错误') - } - }) - .catch(error => { - clearTimeout(timeoutTimer) - wx.hideLoading() - this.handleConfirmError('检查失败') - }) - }, - - /** - * 处理确认成功 - */ - handleConfirmSuccess() { - // 显示最终成功提示 - wx.showToast({ - title: '购买成功!', - icon: 'success', - duration: 1500 - }) - - // 1.5秒后跳转到首页 - setTimeout(() => { - wx.switchTab({ - url: '/pages/index/index' - }) - }, 1500) - - this.setData({ - isSubmitting: false, - isPaying: false - }) - }, - - /** - * 处理确认错误 - */ - handleConfirmError(errorMsg) { - const { checkCount, maxCheckCount } = this.data - const newCheckCount = checkCount + 1 - - if (newCheckCount < maxCheckCount) { - // 还可以重试 - this.setData({ - checkCount: newCheckCount - }) - - // 1秒后重试 - setTimeout(() => { - this.checkOrderStatus() - }, 1000) - } else { - // 达到最大重试次数 - wx.showModal({ - title: '支付确认中', - content: '支付已成功,但订单状态确认异常。请稍后在订单列表中查看状态,如有问题请联系客服。', - showCancel: false, - confirmText: '我知道了', - success: (res) => { - if (res.confirm) { - wx.switchTab({ - url: '/pages/index/index' - }) - } - } - }) - - this.setData({ - isSubmitting: false, - isPaying: false - }) - } - } -}) \ No newline at end of file +// pages/submit/submit.js +const app = getApp() +import request from '../../utils/request.js' +import PopupService from '../../services/popupService.js' + +Page({ + data: { + ossImageUrl: '', + apiBaseUrl: '', + + shangpinData: null, + shangpinId: '', + + nicheng: '', + beizhu: '', + zhiding: '', + quantity: 1, + jine: 0, + + totalPriceInteger: '0', + totalPriceDecimal: '00', + + isSubmitting: false, + canSubmit: false, + isPaying: false, + + orderId: '', + payParams: null, + checkCount: 0, + maxCheckCount: 3 + }, + + onLoad(options) { + this.setData({ + ossImageUrl: app.globalData.ossImageUrl || '', + apiBaseUrl: app.globalData.apiBaseUrl || '' + }) + + wx.setNavigationBarTitle({ title: '提交订单' }) + + try { + if (options.data) { + const shangpinData = JSON.parse(decodeURIComponent(options.data)) + + if (shangpinData.image && !shangpinData.image.startsWith('http')) { + shangpinData.image = this.data.ossImageUrl + shangpinData.image + } + + if (shangpinData.price) { + const price = parseFloat(shangpinData.price) || 0 + const priceInteger = Math.floor(price) + let priceDecimal = '00' + const decimalPart = (price - priceInteger).toFixed(2) + if (decimalPart > 0) { + priceDecimal = decimalPart.toString().split('.')[1] + } + shangpinData.priceInteger = priceInteger + shangpinData.priceDecimal = priceDecimal + } + + this.setData({ + shangpinData: shangpinData, + shangpinId: shangpinData.id || '', + jine: parseFloat(shangpinData.price) || 0, + }, () => this.updateTotalPrice()) + } else { + this.handleDataError('商品数据不完整') + } + } catch (error) { + this.handleDataError('解析商品数据失败') + } + }, + + onShow() { + this.registerNotificationComponent() + PopupService.checkAndShow(this, 'tijiao') + }, + + onHide() { + const popupComp = this.selectComponent('#popupNotice') + if (popupComp && popupComp.cleanup) popupComp.cleanup() + }, + + registerNotificationComponent() { + const app = getApp() + const notificationComp = this.selectComponent('#global-notification') + if (notificationComp && notificationComp.showNotification) { + app.globalData.globalNotification = { + show: (data) => notificationComp.showNotification(data), + hide: () => notificationComp.hideNotification() + } + } + }, + + updateTotalPrice() { + const total = this.data.jine * this.data.quantity + const intPart = Math.floor(total) + const decPart = (total - intPart).toFixed(2).split('.')[1] + this.setData({ + totalPriceInteger: intPart, + totalPriceDecimal: decPart + }) + }, + + onQtyMinus() { + if (this.data.quantity <= 1) return + this.setData({ quantity: this.data.quantity - 1 }, () => this.updateTotalPrice()) + }, + + onQtyPlus() { + if (this.data.quantity >= 99) return + this.setData({ quantity: this.data.quantity + 1 }, () => this.updateTotalPrice()) + }, + + handleDataError(errorMsg) { + wx.showToast({ title: errorMsg || '数据错误', icon: 'none' }) + setTimeout(() => wx.navigateBack(), 1500) + }, + + goBack() { + wx.navigateBack() + }, + + onNicknameInput(e) { + this.setData({ nicheng: e.detail.value.trim() }, () => this.checkFormValid()) + }, + + onRemarkInput(e) { + this.setData({ beizhu: e.detail.value.trim() }) + }, + + onZhidingInput(e) { + this.setData({ zhiding: e.detail.value.trim() }) + }, + + checkFormValid() { + const { nicheng } = this.data + this.setData({ canSubmit: nicheng && nicheng.length > 0 && nicheng.length <= 15 }) + }, + + onSubmitOrder() { + const { nicheng, beizhu, zhiding, jine, quantity, shangpinId, canSubmit, isSubmitting } = this.data + + if (!canSubmit) { + wx.showToast({ title: '请填写游戏昵称', icon: 'none' }) + return + } + + if (isSubmitting) return + + this.setData({ isSubmitting: true, isPaying: false }) + + wx.showLoading({ title: '创建订单中...', mask: true }) + + const timeoutTimer = setTimeout(() => { + wx.hideLoading() + wx.showToast({ title: '网络超时,请检查您是否已登录', icon: 'none' }) + this.setData({ isSubmitting: false, isPaying: false }) + }, 10000) + + const orderData = { + shangpin_id: shangpinId, + nicheng: nicheng, + beizhu: beizhu || '', + zhiding: zhiding || '', + jine: jine * quantity, + shuliang: quantity + } + + request({ + url: '/dingdan/xiadan', + method: 'POST', + data: orderData + }) + .then(res => { + clearTimeout(timeoutTimer) + wx.hideLoading() + + if (res.statusCode === 200 && res.data) { + const response = res.data + if (response.code === 0 && response.data) { + const orderId = response.data.dingdanid + const payParams = response.data.payParams || {} + this.setData({ + orderId: orderId, + payParams: payParams, + isSubmitting: false, + isPaying: true, + checkCount: 0 + }) + this.triggerWxPayment(payParams) + } else { + wx.showToast({ title: response.msg || '创建订单失败', icon: 'none' }) + this.setData({ isSubmitting: false, isPaying: false }) + } + } else { + wx.showToast({ title: '网络错误,请重试', icon: 'none' }) + this.setData({ isSubmitting: false, isPaying: false }) + } + }) + .catch(() => { + clearTimeout(timeoutTimer) + wx.hideLoading() + wx.showToast({ title: '创建订单失败', icon: 'none' }) + this.setData({ isSubmitting: false, isPaying: false }) + }) + }, + + triggerWxPayment(payParams) { + if (!payParams || !payParams.timeStamp || !payParams.nonceStr || + !payParams.package || !payParams.signType || !payParams.paySign) { + wx.showToast({ title: '支付参数不完整', icon: 'none' }) + this.setData({ isSubmitting: false, isPaying: false }) + return + } + + wx.requestPayment({ + timeStamp: payParams.timeStamp, + nonceStr: payParams.nonceStr, + package: payParams.package, + signType: payParams.signType, + paySign: payParams.paySign, + success: () => this.onPaymentSuccess(), + fail: (err) => { + if (err.errMsg === 'requestPayment:fail cancel') { + this.handleUserCancel() + } else { + this.handlePaymentFail(err) + } + } + }) + }, + + handleUserCancel() { + const { orderId } = this.data + wx.showToast({ title: '已取消支付', icon: 'none', duration: 1500 }) + if (orderId) { + request({ + url: '/dingdan/shibai', + method: 'POST', + data: { dingdanid: orderId, yuanyin: 'user_cancel' } + }).catch(() => {}) + } + this.setData({ isSubmitting: false, isPaying: false }) + }, + + handlePaymentFail(err) { + const { orderId } = this.data + wx.showToast({ title: '支付失败', icon: 'none', duration: 1500 }) + if (orderId) { + request({ + url: '/dingdan/shibai', + method: 'POST', + data: { dingdanid: orderId, yuanyin: 'payment_fail', cuowu: err.errMsg || '' } + }).catch(() => {}).finally(() => { + this.setData({ isSubmitting: false, isPaying: false }) + }) + } else { + this.setData({ isSubmitting: false, isPaying: false }) + } + }, + + onPaymentSuccess() { + const { orderId } = this.data + if (!orderId) { + wx.showToast({ title: '订单ID不存在', icon: 'none' }) + this.setData({ isSubmitting: false, isPaying: false }) + return + } + wx.showToast({ title: '支付成功,确认中...', icon: 'none', duration: 1500 }) + this.checkOrderStatus() + }, + + checkOrderStatus() { + const { orderId, checkCount, maxCheckCount } = this.data + wx.showLoading({ title: `确认支付状态${checkCount > 0 ? `(${checkCount + 1})` : ''}`, mask: true }) + + const timeoutTimer = setTimeout(() => { + wx.hideLoading() + this.handleConfirmError('网络超时') + }, 5000) + + request({ + url: '/dingdan/fucha', + method: 'POST', + data: { dingdanid: orderId } + }) + .then(res => { + clearTimeout(timeoutTimer) + wx.hideLoading() + if (res.statusCode === 200 && res.data) { + if (res.data.code === 0) { + this.handleConfirmSuccess() + } else { + this.handleConfirmError(res.data.msg || '订单状态异常') + } + } else { + this.handleConfirmError('网络错误') + } + }) + .catch(() => { + clearTimeout(timeoutTimer) + wx.hideLoading() + this.handleConfirmError('检查失败') + }) + }, + + handleConfirmSuccess() { + wx.showToast({ title: '购买成功!', icon: 'success', duration: 1500 }) + setTimeout(() => wx.switchTab({ url: '/pages/index/index' }), 1500) + this.setData({ isSubmitting: false, isPaying: false }) + }, + + handleConfirmError(errorMsg) { + const newCheckCount = this.data.checkCount + 1 + if (newCheckCount < this.data.maxCheckCount) { + this.setData({ checkCount: newCheckCount }) + setTimeout(() => this.checkOrderStatus(), 1000) + } else { + wx.showModal({ + title: '支付确认中', + content: '支付已成功,但订单状态确认异常。请稍后在订单列表中查看状态,如有问题请联系客服。', + showCancel: false, + confirmText: '我知道了', + success: (res) => { + if (res.confirm) wx.switchTab({ url: '/pages/index/index' }) + } + }) + this.setData({ isSubmitting: false, isPaying: false }) + } + } +}) diff --git a/pages/submit/submit.wxml b/pages/submit/submit.wxml index d3d41bc..8c3e900 100644 --- a/pages/submit/submit.wxml +++ b/pages/submit/submit.wxml @@ -1,124 +1,132 @@ - - - - - - - - - - - - - - {{shangpinData && shangpinData.title ? shangpinData.title : '商品标题'}} - - - - - 价格: - - ¥ - {{shangpinData && shangpinData.priceInteger ? shangpinData.priceInteger : '0'}} - .{{shangpinData && shangpinData.priceDecimal ? shangpinData.priceDecimal : '00'}} - - - - - - - - - - - - - 昵称 - * - - - - - - - - 备注 - - - - - - - - 指定服务者ID(选填) - - - - - - - - - 微信支付 - - - - - - - - - - - - 总价: - - ¥ - {{shangpinData && shangpinData.priceInteger ? shangpinData.priceInteger : '0'}} - .{{shangpinData && shangpinData.priceDecimal ? shangpinData.priceDecimal : '00'}} - - - - - {{isSubmitting ? '提交中...' : '提交订单'}} - - - - - - - - - - \ No newline at end of file + + + + + + + + ¥ + {{totalPriceInteger}} + .{{totalPriceDecimal}} + + 库存{{shangpinData.kucun || 0}}件 + 已选:{{quantity}}份 + + × + + + + + 购买数量 + + - + {{quantity}} + + + + + + + + 服务信息 + + + + 游戏昵称 + 必填 + + + + + + + 备注 + 选填 + + + + + + + 指定服务者 + 选填 + + + + + + + + 服务保障 + + + + 极速接单 + + + + 资金担保 + + + + 售后保障 + + + + 隐私保护 + + + + + + + 支付方式 + + + 微信支付 + + + + + + + + + + + + + {{isSubmitting ? '提交中...' : '确定'}} + + + + + + diff --git a/pages/submit/submit.wxss b/pages/submit/submit.wxss index 484080e..65cdac0 100644 --- a/pages/submit/submit.wxss +++ b/pages/submit/submit.wxss @@ -1,280 +1,296 @@ -/* pages/submit/submit.wxss */ - -.submit-page { - display: flex; - flex-direction: column; - min-height: 100vh; - background: linear-gradient(135deg, #f8f9ff 0%, #f0f3ff 100%); - box-sizing: border-box; - } - - /* 商品信息区域 */ - .product-section { - padding: 30rpx 30rpx 20rpx 30rpx; - flex-shrink: 0; - } - - .product-card { - background: white; - border-radius: 20rpx; - padding: 30rpx; - display: flex; - align-items: center; - box-shadow: 0 8rpx 32rpx rgba(0, 0, 0, 0.06); - } - - .product-image-box { - width: 160rpx; - height: 160rpx; - border-radius: 16rpx; - overflow: hidden; - background: #f5f5f5; - flex-shrink: 0; - margin-right: 24rpx; - } - - .product-image { - width: 100%; - height: 100%; - } - - .product-info { - flex: 1; - display: flex; - flex-direction: column; - justify-content: space-between; - height: 160rpx; - } - - .product-title { - margin-bottom: 20rpx; - } - - .title-text { - font-size: 30rpx; - font-weight: 600; - color: #222; - line-height: 1.4; - display: -webkit-box; - -webkit-box-orient: vertical; - -webkit-line-clamp: 2; - overflow: hidden; - } - - .price-info { - display: flex; - align-items: baseline; - } - - .price-label { - font-size: 26rpx; - color: #666; - font-weight: 500; - } - - .price-value { - display: flex; - align-items: baseline; - } - - .price-icon { - font-size: 28rpx; - color: #ff4d4f; - font-weight: 600; - } - - .price-integer { - font-size: 38rpx; - color: #ff4d4f; - font-weight: 700; - margin-left: 4rpx; - } - - .price-decimal { - font-size: 28rpx; - color: #ff4d4f; - font-weight: 600; - } - - /* 表单区域 */ - .form-section { - padding: 0 30rpx 20rpx 30rpx; - flex-shrink: 0; - } - - .form-card { - background: white; - border-radius: 20rpx; - padding: 30rpx; - box-shadow: 0 6rpx 24rpx rgba(0, 0, 0, 0.05); - } - - .form-item-row { - display: flex; - align-items: center; - padding: 25rpx 0; - border-bottom: 1rpx solid #f5f5f5; - } - - .form-item-row:last-child { - border-bottom: none; - } - - .item-label-row { - width: 220rpx; - flex-shrink: 0; - display: flex; - align-items: center; - } - - .label-text-bold { - font-size: 30rpx; - font-weight: 600; - color: #333; - } - - .label-required { - font-size: 30rpx; - color: #ff4d4f; - margin-left: 8rpx; - } - - .item-input-row { - flex: 1; - font-size: 28rpx; - color: #222; - height: 60rpx; - line-height: 60rpx; - padding: 0 20rpx; - text-align: right; - background: #fafafa; - border-radius: 10rpx; - } - - .input-placeholder-gray { - color: #999; - font-size: 28rpx; - } - - /* 支付方式 */ - .payment-item { - margin-top: 30rpx; - padding-top: 30rpx; - border-top: 1rpx solid #f0f0f0; - } - - .payment-method { - display: flex; - align-items: center; - padding: 20rpx 0; - } - - .payment-icon { - width: 48rpx; - height: 48rpx; - margin-right: 20rpx; - } - - .payment-text { - font-size: 28rpx; - color: #333; - font-weight: 500; - flex: 1; - } - - .payment-checkbox { - width: 40rpx; - height: 40rpx; - border-radius: 50%; - background: #52c41a; - display: flex; - align-items: center; - justify-content: center; - } - - .checkbox-text { - color: white; - font-size: 28rpx; - font-weight: 600; - } - - /* 底部操作栏 */ - .footer-section { - display: flex; - align-items: center; - justify-content: space-between; - padding: 20rpx 30rpx; - background: white; - box-shadow: 0 -4rpx 20rpx rgba(0, 0, 0, 0.08); - margin-top: auto; - flex-shrink: 0; - } - - .total-price { - display: flex; - align-items: baseline; - flex: 1; - } - - .total-label { - font-size: 30rpx; - color: #333; - font-weight: 600; - } - - .total-value { - display: flex; - align-items: baseline; - } - - .submit-btn { - width: 240rpx; - height: 88rpx; - border-radius: 44rpx; - display: flex; - align-items: center; - justify-content: center; - position: relative; - overflow: hidden; - transition: all 0.3s; - } - - .submit-btn-active { - background: linear-gradient(135deg, #52c41a, #389e0d); - box-shadow: 0 8rpx 32rpx rgba(255, 77, 79, 0.4); - } - - .submit-btn-disabled { - background: #ccc; - box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.1); - } - - .submit-btn-active:active { - transform: scale(0.98); - box-shadow: 0 4rpx 20rpx rgba(255, 77, 79, 0.5); - } - - .btn-text { - font-size: 32rpx; - color: white; - font-weight: 600; - position: relative; - z-index: 2; - } - - .btn-glow { - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent); - transform: translateX(-100%); - animation: btnGlow 3s infinite; - } - - @keyframes btnGlow { - 0% { transform: translateX(-100%); } - 50% { transform: translateX(100%); } - 100% { transform: translateX(100%); } - } \ No newline at end of file +/* pages/submit/submit.wxss */ + +page { + background: #f2f2f2; +} + +.page { + min-height: 100vh; + padding-bottom: 140rpx; + box-sizing: border-box; +} + +/* SKU头部 - 拼多多弹窗风格 */ +.sku-header { + display: flex; + background: #fff; + padding: 24rpx; + position: relative; + margin-bottom: 16rpx; +} + +.sku-thumb { + width: 180rpx; + height: 180rpx; + border-radius: 12rpx; + flex-shrink: 0; + margin-right: 20rpx; + background: #f5f5f5; +} + +.sku-header-info { + flex: 1; + display: flex; + flex-direction: column; + justify-content: space-between; + padding: 4rpx 0; +} + +.sku-price-row { + display: flex; + align-items: baseline; +} + +.sku-price-sym { + font-size: 28rpx; + color: #e02e24; + font-weight: 700; +} + +.sku-price-int { + font-size: 48rpx; + color: #e02e24; + font-weight: 800; + line-height: 1; + margin-left: 2rpx; +} + +.sku-price-dec { + font-size: 28rpx; + color: #e02e24; + font-weight: 700; +} + +.sku-stock { + font-size: 24rpx; + color: #999; +} + +.sku-selected { + font-size: 24rpx; + color: #666; +} + +.sku-close { + position: absolute; + top: 16rpx; + right: 16rpx; + width: 48rpx; + height: 48rpx; + display: flex; + align-items: center; + justify-content: center; + font-size: 36rpx; + color: #999; + border-radius: 50%; + background: #f5f5f5; + line-height: 1; +} + +/* SKU区块 */ +.sku-section { + background: #fff; + padding: 24rpx 28rpx; + margin-bottom: 16rpx; +} + +.sku-section-title { + font-size: 28rpx; + font-weight: 700; + color: #222; + margin-bottom: 20rpx; + display: block; +} + +/* 数量选择器 */ +.qty-row { + display: flex; + align-items: center; +} + +.qty-btn { + width: 64rpx; + height: 64rpx; + border-radius: 8rpx; + background: #f5f5f5; + display: flex; + align-items: center; + justify-content: center; + font-size: 32rpx; + font-weight: 700; + color: #333; + border: 1rpx solid #e8e8e8; +} + +.qty-btn--disabled { + color: #ccc; + border-color: #f0f0f0; + background: #fafafa; +} + +.qty-val { + min-width: 80rpx; + text-align: center; + font-size: 32rpx; + font-weight: 700; + color: #222; + margin: 0 20rpx; +} + +/* 表单字段 */ +.form-field { + padding: 16rpx 0; + border-bottom: 1rpx solid #f5f5f5; +} + +.form-field:last-child { + border-bottom: none; + padding-bottom: 0; +} + +.field-top { + display: flex; + align-items: center; + margin-bottom: 10rpx; +} + +.field-name { + font-size: 26rpx; + font-weight: 600; + color: #333; +} + +.field-required { + font-size: 20rpx; + color: #e02e24; + background: #fff1f0; + padding: 2rpx 10rpx; + border-radius: 4rpx; + margin-left: 10rpx; +} + +.field-optional { + font-size: 20rpx; + color: #bbb; + margin-left: 10rpx; +} + +.field-input { + width: 100%; + height: 72rpx; + background: #f7f7f7; + border-radius: 8rpx; + padding: 0 20rpx; + font-size: 26rpx; + color: #333; + border: 1rpx solid #eee; + box-sizing: border-box; +} + +.ph { + color: #bbb; + font-size: 26rpx; +} + +/* 服务保障 */ +.guarantee-row { + display: flex; + flex-wrap: wrap; + gap: 12rpx 20rpx; +} + +.guarantee-item { + display: flex; + align-items: center; +} + +.g-check { + font-size: 20rpx; + color: #e02e24; + font-weight: 700; + margin-right: 4rpx; +} + +.g-text { + font-size: 24rpx; + color: #666; +} + +/* 支付方式 */ +.pay-row { + display: flex; + align-items: center; +} + +.pay-icon { + width: 44rpx; + height: 44rpx; + margin-right: 16rpx; +} + +.pay-label { + flex: 1; + font-size: 28rpx; + color: #333; + font-weight: 500; +} + +.pay-check { + width: 36rpx; + height: 36rpx; + border-radius: 50%; + background: #52c41a; + display: flex; + align-items: center; + justify-content: center; +} + +.pay-check-icon { + color: #fff; + font-size: 22rpx; + font-weight: 700; +} + +/* 底部留白 */ +.bottom-space { + height: 20rpx; +} + +/* 底部确认按钮 */ +.confirm-bar { + position: fixed; + bottom: 0; + right: 0; + width: 50%; + height: 100rpx; + background: #fff; + padding-bottom: env(safe-area-inset-bottom); + z-index: 9999; + display: flex; +} + +.confirm-btn { + width: 100%; + height: 100%; + border-radius: 0; + display: flex; + align-items: center; + justify-content: center; + font-size: 32rpx; + font-weight: 700; + color: #fff; + letter-spacing: 4rpx; +} + +.confirm-btn--active { + background: #e02e24; +} + +.confirm-btn--disabled { + background: #ccc; +} + +.confirm-btn--hover { + opacity: 0.9; + transform: scale(0.98); +} diff --git a/pages/verify/verify.js b/pages/verify/verify.js index 1bb9cde..0d1bd15 100644 --- a/pages/verify/verify.js +++ b/pages/verify/verify.js @@ -1,169 +1,159 @@ -// pages/verify/verify.js -import request from '../../utils/request.js' -import { enterLockedRole } from '../../utils/primary-role.js' - -const API_MAP = { - dashou: '/yonghu/dashouzhuce', - shangjia: '/yonghu/shangjiahuce', - guanshi: '/yonghu/guanshizhuce', - zuzhang: '/yonghu/zuzhangzhuce', - kaoheguan: '/dengji/khgzc' -} - -const STATUS_KEY_MAP = { - dashou: 'dashoustatus', - shangjia: 'shangjiastatus', - guanshi: 'guanshistatus', - zuzhang: 'zuzhangstatus', - kaoheguan: 'kaoheguanstatus' -} - -const NAME_MAP = { - dashou: '接单员', - shangjia: '商家', - guanshi: '管事', - zuzhang: '组长', - kaoheguan: '考核官' -} - -Page({ - data: { - authType: '', - authName: '', - pageState: 'need_register', - inviteCode: '', - isLoading: false, - }, - - onLoad(options) { - const type = options.type || '' - if (!API_MAP[type]) { - wx.showToast({ title: '认证类型错误', icon: 'none' }) - setTimeout(() => wx.navigateBack(), 1500) - return - } - this.setData({ authType: type, authName: NAME_MAP[type] }) - this.checkStatus() - }, - - onShow() { - if (this.data.authType) this.checkStatus() - }, - - checkStatus() { - const key = STATUS_KEY_MAP[this.data.authType] - const val = wx.getStorageSync(key) - if (val === 1) { - this.setData({ pageState: 'done' }) - } else { - this.setData({ pageState: 'need_register' }) - } - }, - - onInviteCodeInput(e) { - this.setData({ inviteCode: e.detail.value.trim() }) - }, - - async onRegister() { - const { inviteCode, authType } = this.data - if (!inviteCode) { - wx.showToast({ title: '请输入邀请码', icon: 'none' }) - return - } - if (inviteCode.length > 100) { - wx.showToast({ title: '邀请码不能超过100字符', icon: 'none' }) - return - } - - this.setData({ isLoading: true }) - wx.showLoading({ title: '注册中...', mask: true }) - - try { - const res = await request({ - url: API_MAP[authType], - method: 'POST', - data: { inviteCode } - }) - - if (res && res.data.code === 200) { - const data = res.data.data || res.data - const app = getApp() - - // 缓存身份状态 - const key = STATUS_KEY_MAP[authType] - if (data[key] !== undefined) { - wx.setStorageSync(key, data[key]) - app.globalData[key] = data[key] - } else { - wx.setStorageSync(key, 1) - app.globalData[key] = 1 - } - - // 更新其他可能返回的身份状态字段 - const allKeys = ['dashoustatus', 'shangjiastatus', 'guanshistatus', 'zuzhangstatus', 'kaoheguanstatus'] - allKeys.forEach(k => { - if (data[k] !== undefined && k !== key) { - wx.setStorageSync(k, data[k]) - app.globalData[k] = data[k] - } - }) - - // 保存各身份详细信息 - if (authType === 'dashou') { - const fields = ['dashounicheng','zhanghaostatus','yongjin','zonge','yajin','chenghao','jifen','clumber','chengjiaoliang','zaixianzhuangtai'] - fields.forEach(f => { if (data[f] !== undefined) app.globalData[f] = data[f] }) - } else if (authType === 'shangjia') { - app.globalData.shangjia = app.globalData.shangjia || {} - const sf = ['nicheng','sjyue','fabu','chengjiao','tuikuan','jinridingdan','jinriliushui','jinyuedingdan','jinyueliushui','zhuangtai'] - sf.forEach(f => { if (data[f] !== undefined) app.globalData.shangjia[f] = data[f] }) - } else if (authType === 'guanshi') { - app.globalData.guanshi = app.globalData.guanshi || {} - const gf = ['gszhstatus','yaoqingzongshu','fenyongzonge','fenyongtixian','yichongzhiDashou'] - gf.forEach(f => { if (data[f] !== undefined) app.globalData.guanshi[f] = data[f] }) - } else if (authType === 'zuzhang') { - const zf = ['ketixian','guanshiCount','fenhongZonge'] - zf.forEach(f => { if (data[f] !== undefined) app.globalData[f] = data[f] }) - } else if (authType === 'kaoheguan') { - app.globalData.kaoheguan = app.globalData.kaoheguan || {} - const kf = ['shenhe_zongshu','tongguo_zongshu','yue','zonge','shenhe_zhuangtai','bankuai_list'] - kf.forEach(f => { if (data[f] !== undefined) app.globalData.kaoheguan[f] = data[f] }) - } - - wx.hideLoading() - this.setData({ isLoading: false, pageState: 'done' }) - wx.showToast({ title: '认证成功', icon: 'success' }) - - if (authType === 'dashou' || authType === 'shangjia') { - setTimeout(() => enterLockedRole(authType), 1200) - } else if (authType === 'guanshi') { - setTimeout(() => { - const pages = getCurrentPages() - if (pages.length > 1) { - wx.navigateBack() - } else { - wx.redirectTo({ url: '/pages/fighter/fighter' }) - } - }, 800) - } - } else { - wx.hideLoading() - this.setData({ isLoading: false }) - wx.showToast({ title: res?.data?.msg || '注册失败', icon: 'none' }) - } - } catch (err) { - wx.hideLoading() - this.setData({ isLoading: false }) - wx.showToast({ title: err.message || '注册失败', icon: 'none' }) - } - }, - - goToKefu() { - const app = getApp() - const { link, enterpriseId } = app.globalData.kefuConfig || {} - if (!link || !enterpriseId) { - wx.showToast({ title: '客服配置未加载', icon: 'none' }) - return - } - wx.openCustomerServiceChat({ extInfo: { url: link }, corpId: enterpriseId }) - } -}) \ No newline at end of file +// pages/verify/verify.js +import { createPage, request } from '../../utils/base-page.js' +import { enterLockedRole } from '../../utils/primary-role.js' + +const API_MAP = { + dashou: '/yonghu/dashouzhuce', + shangjia: '/yonghu/shangjiahuce', + guanshi: '/yonghu/guanshizhuce', + zuzhang: '/yonghu/zuzhangzhuce', + kaoheguan: '/dengji/khgzc' +} + +const STATUS_KEY_MAP = { + dashou: 'dashoustatus', + shangjia: 'shangjiastatus', + guanshi: 'guanshistatus', + zuzhang: 'zuzhangstatus', + kaoheguan: 'kaoheguanstatus' +} + +const NAME_MAP = { + dashou: '接单员', + shangjia: '商家', + guanshi: '管事', + zuzhang: '组长', + kaoheguan: '考核官' +} + +Page(createPage({ + data: { + authType: '', + authName: '', + pageState: 'need_register', + inviteCode: '', + isLoading: false, + }, + + onLoad(options) { + const type = options.type || '' + if (!API_MAP[type]) { + wx.showToast({ title: '认证类型错误', icon: 'none' }) + setTimeout(() => wx.navigateBack(), 1500) + return + } + this.setData({ authType: type, authName: NAME_MAP[type] }) + this.checkStatus() + }, + + onShow() { + if (this.data.authType) this.checkStatus() + }, + + checkStatus() { + const key = STATUS_KEY_MAP[this.data.authType] + const val = wx.getStorageSync(key) + if (val === 1) { + this.setData({ pageState: 'done' }) + } else { + this.setData({ pageState: 'need_register' }) + } + }, + + onInviteCodeInput(e) { + this.setData({ inviteCode: e.detail.value.trim() }) + }, + + async onRegister() { + const { inviteCode, authType } = this.data + if (!inviteCode) { + wx.showToast({ title: '请输入邀请码', icon: 'none' }) + return + } + if (inviteCode.length > 100) { + wx.showToast({ title: '邀请码不能超过100字符', icon: 'none' }) + return + } + + this.setData({ isLoading: true }) + wx.showLoading({ title: '注册中...', mask: true }) + + try { + const res = await request({ + url: API_MAP[authType], + method: 'POST', + data: { inviteCode } + }) + + if (res && res.data.code === 200) { + const data = res.data.data || res.data + const app = getApp() + + // 缓存身份状态 + const key = STATUS_KEY_MAP[authType] + if (data[key] !== undefined) { + wx.setStorageSync(key, data[key]) + app.globalData[key] = data[key] + } else { + wx.setStorageSync(key, 1) + app.globalData[key] = 1 + } + + // 更新其他可能返回的身份状态字段 + const allKeys = ['dashoustatus', 'shangjiastatus', 'guanshistatus', 'zuzhangstatus', 'kaoheguanstatus'] + allKeys.forEach(k => { + if (data[k] !== undefined && k !== key) { + wx.setStorageSync(k, data[k]) + app.globalData[k] = data[k] + } + }) + + // 保存各身份详细信息 + if (authType === 'dashou') { + const fields = ['dashounicheng','zhanghaostatus','yongjin','zonge','yajin','chenghao','jifen','clumber','chengjiaoliang','zaixianzhuangtai'] + fields.forEach(f => { if (data[f] !== undefined) app.globalData[f] = data[f] }) + } else if (authType === 'shangjia') { + app.globalData.shangjia = app.globalData.shangjia || {} + const sf = ['nicheng','sjyue','fabu','chengjiao','tuikuan','jinridingdan','jinriliushui','jinyuedingdan','jinyueliushui','zhuangtai'] + sf.forEach(f => { if (data[f] !== undefined) app.globalData.shangjia[f] = data[f] }) + } else if (authType === 'guanshi') { + app.globalData.guanshi = app.globalData.guanshi || {} + const gf = ['gszhstatus','yaoqingzongshu','fenyongzonge','fenyongtixian','yichongzhiDashou'] + gf.forEach(f => { if (data[f] !== undefined) app.globalData.guanshi[f] = data[f] }) + } else if (authType === 'zuzhang') { + const zf = ['ketixian','guanshiCount','fenhongZonge'] + zf.forEach(f => { if (data[f] !== undefined) app.globalData[f] = data[f] }) + } else if (authType === 'kaoheguan') { + app.globalData.kaoheguan = app.globalData.kaoheguan || {} + const kf = ['shenhe_zongshu','tongguo_zongshu','yue','zonge','shenhe_zhuangtai','bankuai_list'] + kf.forEach(f => { if (data[f] !== undefined) app.globalData.kaoheguan[f] = data[f] }) + } + + wx.hideLoading() + this.setData({ isLoading: false, pageState: 'done' }) + wx.showToast({ title: '认证成功', icon: 'success' }) + + if (authType === 'dashou' || authType === 'shangjia') { + setTimeout(() => enterLockedRole(authType), 1200) + } else if (authType === 'guanshi') { + setTimeout(() => { + const pages = getCurrentPages() + if (pages.length > 1) { + wx.navigateBack() + } else { + wx.redirectTo({ url: '/pages/fighter/fighter' }) + } + }, 800) + } + } else { + wx.hideLoading() + this.setData({ isLoading: false }) + wx.showToast({ title: res?.data?.msg || '注册失败', icon: 'none' }) + } + } catch (err) { + wx.hideLoading() + this.setData({ isLoading: false }) + wx.showToast({ title: err.message || '注册失败', icon: 'none' }) + } + }, +})) diff --git a/pages/withdraw/withdraw.js b/pages/withdraw/withdraw.js index 1246f6c..1902b0a 100644 --- a/pages/withdraw/withdraw.js +++ b/pages/withdraw/withdraw.js @@ -1,72 +1,54 @@ -// pages/withdraw/withdraw.js -import request from '../../utils/request.js'; -import PopupService from '../../services/popupService.js'; - -const app = getApp(); - -Page({ - data: { - pageMode: 0, - pageOptions: {}, - loadError: false, - }, - - onLoad(options) { - this.setData({ - pageOptions: options || {} - }); - this.fetchPageMode(); - }, - - onShow() { - this.registerNotificationComponent(); - PopupService.checkAndShow(this, 'tixian'); - }, - - onHide() { - const popupComp = this.selectComponent('#popupNotice'); - if (popupComp && popupComp.cleanup) { - popupComp.cleanup(); - } - }, - - // 注册通知组件 - registerNotificationComponent() { - const notificationComp = this.selectComponent('#global-notification'); - if (notificationComp && notificationComp.showNotification) { - app.globalData.globalNotification = { - show: (data) => notificationComp.showNotification(data), - hide: () => notificationComp.hideNotification() - }; - } - }, - - async fetchPageMode() { - try { - const res = await request({ - url: '/peizhi/hqtxzsym', - method: 'POST' - }); - - if (res.statusCode === 200 && res.data && res.data.code === 0) { - const mode = parseInt(res.data.data.mode); - this.setData({ - pageMode: (mode === 1 || mode === 2) ? mode : 1, - loadError: false - }); - } else { - this.setData({ pageMode: 1, loadError: false }); - } - } catch (err) { - console.error('获取展示模式失败:', err); - this.setData({ loadError: true }); - } - }, - - retryFetch() { - this.setData({ loadError: false, pageMode: 0 }); - this.fetchPageMode(); - }, - - stopPropagation() {} -}); \ No newline at end of file +// pages/withdraw/withdraw.js +import { createPage, request } from '../../utils/base-page.js'; +import PopupService from '../../services/popupService.js'; + +const app = getApp(); + +Page(createPage({ + data: { + pageMode: 0, + pageOptions: {}, + loadError: false, + }, + + onLoad(options) { + this.setData({ + pageOptions: options || {} + }); + this.fetchPageMode(); + }, + + onShow() { + this.registerNotificationComponent(); + PopupService.checkAndShow(this, 'tixian'); + }, + + async fetchPageMode() { + try { + const res = await request({ + url: '/peizhi/hqtxzsym', + method: 'POST' + }); + + if (res.statusCode === 200 && res.data && res.data.code === 0) { + const mode = parseInt(res.data.data.mode); + this.setData({ + pageMode: (mode === 1 || mode === 2) ? mode : 1, + loadError: false + }); + } else { + this.setData({ pageMode: 1, loadError: false }); + } + } catch (err) { + console.error('获取展示模式失败:', err); + this.setData({ loadError: true }); + } + }, + + retryFetch() { + this.setData({ loadError: false, pageMode: 0 }); + this.fetchPageMode(); + }, + + stopPropagation() {} +})) diff --git a/tab-bar/index.wxss b/tab-bar/index.wxss index cdbb7a9..3c7ae1f 100644 --- a/tab-bar/index.wxss +++ b/tab-bar/index.wxss @@ -34,6 +34,7 @@ box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.8); padding: 0 20rpx; position: relative; + overflow: hidden; } .tab-capsule::before { @@ -46,19 +47,19 @@ background: linear-gradient( 90deg, transparent 0%, - rgba(255, 255, 255, 0.25) 20%, - rgba(255, 255, 255, 0.35) 50%, - rgba(255, 255, 255, 0.25) 80%, + rgba(255, 255, 255, 0.15) 20%, + rgba(255, 255, 255, 0.22) 50%, + rgba(255, 255, 255, 0.15) 80%, transparent 100% ); - animation: shine 3s infinite; + animation: shine 8s infinite; z-index: 1; } @keyframes shine { - 0% { left: -100%; } - 25% { left: 150%; } - 100% { left: 150%; } + 0% { left: -60%; } + 15% { left: 100%; } + 100% { left: 100%; } } .tab-item { diff --git a/utils/api-helper.js b/utils/api-helper.js new file mode 100644 index 0000000..e7bfa6b --- /dev/null +++ b/utils/api-helper.js @@ -0,0 +1,46 @@ +// utils/api-helper.js - API 响应解析 + +export function isApiSuccess(body) { + if (!body) return false; + const code = Number(body.code); + return code === 200 || code === 0; +} + +export function getApiMsg(body) { + if (!body) return ''; + return body.msg || body.message || ''; +} + +export function extractUserData(body) { + if (!body) return {}; + if (body.data && typeof body.data === 'object' && !Array.isArray(body.data)) { + return body.data; + } + const userData = { ...body }; + delete userData.code; + delete userData.msg; + delete userData.message; + return userData; +} + +/** 解析扫码场景参数 */ +export function parseSceneOptions(options) { + if (!options || !options.scene) return options || {}; + try { + const scene = decodeURIComponent(options.scene); + return { ...options, inviteCode: scene }; + } catch (e) { + return options; + } +} + +/** 订单状态中文映射 */ +const ORDER_STATUS_MAP = { + 1: '已下单', 2: '进行中', 3: '已完成', + 4: '退款中', 5: '已退款', 6: '退款失败', + 7: '指定中', 8: '结算中', +}; + +export function getOrderStatusText(status) { + return ORDER_STATUS_MAP[status] || '未知状态'; +} diff --git a/utils/base-page.js b/utils/base-page.js new file mode 100644 index 0000000..e7e0de6 --- /dev/null +++ b/utils/base-page.js @@ -0,0 +1,362 @@ +// utils/base-page.js - 页面公共框架 +import request from './request.js'; +import popupService from '../services/popupService.js'; +import { + isRoleStatusActive, + isCenterPageActive, + syncRoleStatuses, + syncProfileFields, + syncGroupFields, + ensureRoleOnCenterPage, + clearCacheAndEnterNormal, + refreshTabBar, +} from './role-tab-bar.js'; +import { lockPrimaryRole, migrateLegacyCenterRole, enterLockedRole } from './primary-role.js'; +import { resolveAvatarFromStorage } from './avatar.js'; +import { isApiSuccess, getApiMsg, extractUserData, parseSceneOptions } from './api-helper.js'; + +// ===== 基类数据 ===== +const BASE_DATA = { + isLoading: false, + lastRefreshTime: 0, + canRefresh: true, +}; + +// ===== 基类方法 ===== +const BASE_METHODS = { + + // ---------- 通知组件 ---------- + registerNotificationComponent() { + const app = getApp(); + const comp = this.selectComponent('#global-notification'); + if (comp && comp.showNotification) { + app.globalData.globalNotification = { + show: (data) => comp.showNotification(data), + hide: () => comp.hideNotification(), + }; + } + }, + + // ---------- 弹窗清理 ---------- + cleanupPopup() { + const comp = this.selectComponent('#popupNotice'); + if (comp && comp.cleanup) comp.cleanup(); + }, + + // ---------- 冷启动弹窗 ---------- + checkColdStartPopup(pageId) { + const app = getApp(); + if (!app.globalData.hasShownPopupOnColdStart) { + app.globalData.hasShownPopupOnColdStart = true; + setTimeout(() => popupService.checkAndShow(this, pageId), 300); + } + }, + + // ---------- 头像加载 ---------- + loadAvatar() { + this.setData({ avatarUrl: resolveAvatarFromStorage(getApp()) }); + }, + + // ---------- 通用用户信息加载 ---------- + loadUserInfo() { + const app = getApp(); + const { nick, uid, avatarUrl } = (app.getUserProfileForDisplay && app.getUserProfileForDisplay()) || {}; + this.setData({ + avatarUrl: avatarUrl || resolveAvatarFromStorage(app), + uid: uid || wx.getStorageSync('uid') || '', + nicheng: nick || wx.getStorageSync('nicheng') || '', + }); + }, + + // ---------- 通用数据加载 ---------- + async loadData(opts) { + const { + url, method = 'POST', data: reqData, + fields, loadingKey = 'isLoading', + successCode = 200, errorMsg = '加载失败', + onSuccess, onError, + } = opts; + + this.setData({ [loadingKey]: true }); + try { + const res = await request({ url, method, data: reqData }); + if (res && (res.data.code === successCode || (successCode === 0 && res.data.code === 0))) { + const resData = res.data.data || {}; + if (fields) { + const updateData = {}; + Object.keys(fields).forEach((pageKey) => { + const apiField = fields[pageKey]; + updateData[pageKey] = apiField in resData ? resData[apiField] : (typeof apiField === 'object' ? apiField.default : ''); + }); + this.setData({ ...updateData, [loadingKey]: false }); + } + if (onSuccess) onSuccess(resData, res); + return resData; + } + throw new Error(res?.data?.msg || res?.data?.message || errorMsg); + } catch (error) { + this.setData({ [loadingKey]: false }); + if (onError) { + onError(error); + } else { + wx.showToast({ title: error.message || errorMsg, icon: 'none' }); + } + return null; + } + }, + + // ---------- 节流刷新 ---------- + throttledRefresh(fn, interval = 3000) { + const now = Date.now(); + if (!this.data.canRefresh || (now - this.data.lastRefreshTime < interval)) { + wx.showToast({ title: '操作过快', icon: 'none' }); + return; + } + this.setData({ lastRefreshTime: now, canRefresh: false }); + fn.call(this); + setTimeout(() => this.setData({ canRefresh: true }), interval); + }, + + // ---------- 复制 UID ---------- + copyUid() { + const uid = this.data.uid; + if (!uid) { + wx.showToast({ title: 'UID不存在', icon: 'none' }); + return; + } + wx.setClipboardData({ + data: uid, + success: () => wx.showToast({ title: '已复制UID', icon: 'success' }), + fail: () => wx.showToast({ title: '复制失败', icon: 'none' }), + }); + }, + + // ---------- 清除缓存 ---------- + clearCache() { + wx.showModal({ + title: '清除缓存', + content: '将清除所有登录与身份数据,并回到点单端,确定继续?', + confirmText: '确定', + cancelText: '取消', + success: (r) => { if (r.confirm) clearCacheAndEnterNormal(this); }, + }); + }, + + // ---------- 返回点单端 ---------- + switchToNormal() { + lockPrimaryRole('normal'); + wx.reLaunch({ url: '/pages/mine/mine' }); + }, + + // ---------- 客服 ---------- + goToKefu() { + const app = getApp(); + const { link, enterpriseId } = app.globalData.kefuConfig || {}; + if (!link || !enterpriseId) { + wx.showToast({ title: '客服配置未加载', icon: 'none' }); + return; + } + wx.openCustomerServiceChat({ extInfo: { url: link }, corpId: enterpriseId }); + }, + + // ---------- API 辅助(快捷访问) ---------- + _isApiSuccess: isApiSuccess, + _getApiMsg: getApiMsg, + _extractUserData: extractUserData, + + // ---------- 邀请码注册通用流程 ---------- + async registerWithInviteCode(opts) { + const { + inviteCode, apiUrl, role, statusKey, + alreadyMsg, successMsg = '注册成功', + onSuccess, + } = opts; + + if (!inviteCode) { + wx.showToast({ title: '请输入邀请码', icon: 'none' }); + return; + } + if (inviteCode.length > 100) { + wx.showToast({ title: '邀请码不能超过100字符', icon: 'none' }); + return; + } + + this.setData({ isLoading: true }); + try { + const res = await request({ url: apiUrl, method: 'POST', data: { inviteCode } }); + const body = res?.data || {}; + + if (isApiSuccess(body)) { + const userData = extractUserData(body); + syncRoleStatuses(userData); + syncProfileFields(userData); + syncGroupFields(userData); + if (userData.token) { + wx.setStorageSync('token', userData.token); + getApp().globalData.token = userData.token; + } + if (!Object.prototype.hasOwnProperty.call(userData, statusKey) && + !isRoleStatusActive(wx.getStorageSync(statusKey))) { + wx.setStorageSync(statusKey, 1); + getApp().globalData[statusKey] = 1; + } + if (onSuccess) { + await onSuccess(userData, body); + } + this.setData({ isLoading: false }); + wx.showToast({ title: successMsg, icon: 'success' }); + } else { + const msg = getApiMsg(body); + if (alreadyMsg && msg && msg.includes(alreadyMsg)) { + if (onSuccess) { + await onSuccess(extractUserData(body), body); + } + this.setData({ isLoading: false }); + wx.showToast({ title: msg, icon: 'success' }); + } else { + this.setData({ isLoading: false }); + wx.showToast({ title: msg || '注册失败', icon: 'none' }); + } + } + } catch (error) { + this.setData({ isLoading: false }); + wx.showToast({ title: error.message || '注册失败', icon: 'none' }); + } + }, + + // ---------- 邀请码 + 登录检查 ---------- + handleInviteCodeWithLoginCheck(opts) { + const { inviteCode, role, statusKey, alreadyMsg, onNeedLogin, onAlreadyRegistered } = opts; + const token = wx.getStorageSync('token'); + const uid = wx.getStorageSync('uid'); + + if (!token || !uid) { + if (onNeedLogin) onNeedLogin(inviteCode); + return; + } + + const isActive = isRoleStatusActive(wx.getStorageSync(statusKey)); + if (!isActive) { + wx.showLoading({ title: '注册中...', mask: true }); + this.registerWithInviteCode(opts); + } else { + if (onAlreadyRegistered) onAlreadyRegistered(); + else wx.showToast({ title: alreadyMsg || '您已注册', icon: 'success' }); + } + }, + + // ---------- 微信登录 + 注册 ---------- + loginAndRegisterWithInviteCode(opts) { + const { inviteCode, onLoginSuccess } = opts; + wx.showLoading({ title: '登录注册中...', mask: true }); + wx.login({ + success: (loginRes) => { + if (loginRes.code) { + if (onLoginSuccess) onLoginSuccess(loginRes.code, inviteCode); + } else { + wx.hideLoading(); + wx.showToast({ title: '微信登录失败', icon: 'none' }); + } + }, + fail: () => { + wx.hideLoading(); + wx.showToast({ title: '微信登录失败', icon: 'none' }); + }, + }); + }, + + // ---------- 图片 URL 初始化 ---------- + initImageUrls(urlMap) { + const ossImageUrl = getApp().globalData.ossImageUrl || ''; + const result = {}; + Object.keys(urlMap).forEach((key) => { + const path = urlMap[key]; + result[key] = path.startsWith('http') || path.startsWith('/') ? path : ossImageUrl + path; + }); + return result; + }, +}; + +// ===== 角色中心页增强 ===== +function createRoleCenterMixin(roleConfig) { + const { role, statusKey, dataFlag, pageId, apiUrl } = roleConfig; + + return { + onReady() { + if (isCenterPageActive(this, dataFlag, statusKey)) { + ensureRoleOnCenterPage(this, role); + } + }, + + onHide() { + this.cleanupPopup(); + }, + + checkRoleStatus() { + try { + const status = wx.getStorageSync(statusKey); + const uid = wx.getStorageSync('uid'); + if (isRoleStatusActive(status)) { + this.setData({ [dataFlag]: true, uid: uid || '' }); + this.loadAvatar(); + if (this.checkGlobalData) this.checkGlobalData(); + ensureRoleOnCenterPage(this, role); + } else { + this.setData({ [dataFlag]: false }); + } + } catch (error) { + this.setData({ [dataFlag]: false }); + } + }, + }; +} + +/** + * 创建页面配置(合并基类方法) + * @param {Object} pageConfig - 页面特有配置 + * @param {Object} [options] - 可选配置 + * @param {Object} [options.roleConfig] - 角色中心页配置 { role, statusKey, dataFlag, pageId, apiUrl } + * @returns {Object} 完整的 Page 配置 + */ +export function createPage(pageConfig, options = {}) { + const { roleConfig } = options; + + // 合并 data + const data = { ...BASE_DATA, ...(pageConfig.data || {}) }; + + // 合并方法 + const methods = { ...BASE_METHODS }; + + // 如果是角色中心页,混入角色相关方法 + if (roleConfig) { + Object.assign(methods, createRoleCenterMixin(roleConfig)); + } + + // 页面特有方法覆盖基类方法 + const allMethods = { ...methods, ...pageConfig }; + + // 确保 data 正确 + allMethods.data = data; + + return allMethods; +} + +// 导出工具函数供页面直接使用 +export { + request, + isRoleStatusActive, + isCenterPageActive, + syncRoleStatuses, + syncProfileFields, + syncGroupFields, + ensureRoleOnCenterPage, + clearCacheAndEnterNormal, + refreshTabBar, + lockPrimaryRole, + migrateLegacyCenterRole, + enterLockedRole, + isApiSuccess, + getApiMsg, + extractUserData, + parseSceneOptions, +};