diff --git a/miniprogram/app.js b/miniprogram/app.js index 0e021b83..1afb139f 100644 --- a/miniprogram/app.js +++ b/miniprogram/app.js @@ -81,9 +81,9 @@ App({ sessionKey: wxInfo.sessionKey, encryptedData, iv, - nickname: userProfile?.nickName || "", - avatar: userProfile?.avatarUrl || "", - gender: userProfile?.gender || 0, + nickname: (userProfile && userProfile.nickName) || "", + avatar: (userProfile && userProfile.avatarUrl) || "", + gender: (userProfile && userProfile.gender) || 0, }, success: (loginRes) => { if (loginRes.data.code === 0) { @@ -93,7 +93,7 @@ App({ // 处理天梯用户信息 if (loginRes.data.data.userInfo.ladderUsers && loginRes.data.data.userInfo.ladderUsers.length > 0) { // 如果有当前门店,优先选择当前门店的天梯用户 - if (this.globalData.currentStore?.storeId) { + if (this.globalData.currentStore && this.globalData.currentStore.storeId) { const currentStoreLadderUser = loginRes.data.data.userInfo.ladderUsers.find( lu => lu.storeId === this.globalData.currentStore.storeId ); @@ -139,7 +139,7 @@ App({ // 处理天梯用户信息 if (res.data.ladderUsers && res.data.ladderUsers.length > 0) { // 如果有当前门店,优先选择当前门店的天梯用户 - if (this.globalData.currentStore?.storeId) { + if (this.globalData.currentStore && this.globalData.currentStore.storeId) { const currentStoreLadderUser = res.data.ladderUsers.find( lu => lu.storeId === this.globalData.currentStore.storeId ); @@ -179,11 +179,11 @@ App({ this.globalData.currentStore = res.data; // 如果当前门店有 ladderUserId,获取该门店的天梯用户信息 - if (res.data?.ladderUserId) { + if (res.data && res.data.ladderUserId) { this.getLadderUser(res.data.storeId); - } else if (res.data?.storeId) { + } else if (res.data && res.data.storeId) { // 如果当前门店没有 ladderUserId,但用户信息中有该门店的天梯用户,使用它 - if (this.globalData.userInfo?.ladderUsers) { + if (this.globalData.userInfo && this.globalData.userInfo.ladderUsers) { const currentStoreLadderUser = this.globalData.userInfo.ladderUsers.find( lu => lu.storeId === res.data.storeId ); @@ -207,11 +207,11 @@ App({ this.globalData.currentStore = res.data; // 如果当前门店有 ladderUserId,获取该门店的天梯用户信息 - if (res.data?.ladderUserId) { + if (res.data && res.data.ladderUserId) { this.getLadderUser(res.data.storeId); - } else if (res.data?.storeId) { + } else if (res.data && res.data.storeId) { // 如果当前门店没有 ladderUserId,但用户信息中有该门店的天梯用户,使用它 - if (this.globalData.userInfo?.ladderUsers) { + if (this.globalData.userInfo && this.globalData.userInfo.ladderUsers) { const currentStoreLadderUser = this.globalData.userInfo.ladderUsers.find( lu => lu.storeId === res.data.storeId ); diff --git a/miniprogram/app.json b/miniprogram/app.json index 65545ac9..ca91daf4 100644 --- a/miniprogram/app.json +++ b/miniprogram/app.json @@ -1,6 +1,7 @@ { "pages": [ "pages/index/index", + "pages/player/index", "pages/user/index", "pages/match/challenge/index", "pages/match/challenge-detail/index", diff --git a/miniprogram/app.wxss b/miniprogram/app.wxss index d6b91009..2c40f1a9 100644 --- a/miniprogram/app.wxss +++ b/miniprogram/app.wxss @@ -26,6 +26,19 @@ page { --accent-light: #e6fbf7; --accent-soft: rgba(0, 201, 167, 0.1); + --info: #3b82f6; + --info-soft: rgba(59, 130, 246, 0.12); + --info-text: #1d4ed8; + --success: #16a34a; + --success-soft: rgba(22, 163, 74, 0.12); + --success-text: #166534; + --warning: #ffba08; + --warning-soft: rgba(255, 186, 8, 0.14); + --warning-text: #8a5a00; + --danger: #ef4444; + --danger-soft: rgba(239, 68, 68, 0.12); + --danger-text: #b91c1c; + /* 浅色背景系 */ --bg-page: #f7f8fa; --bg-white: #ffffff; diff --git a/miniprogram/config.js b/miniprogram/config.js index 58c5888a..f3e72b06 100644 --- a/miniprogram/config.js +++ b/miniprogram/config.js @@ -6,9 +6,9 @@ // 开发环境配置 const devConfig = { // API 基础地址(本地开发) - baseUrl: "https://yingsa-server.ethan.team", + baseUrl: "http://127.0.0.1:3000", // WebSocket 地址(本地开发) - wsUrl: "wss://yingsa-server.ethan.team/ws", + wsUrl: "ws://127.0.0.1:3000/ws", }; // 生产环境配置 @@ -25,7 +25,10 @@ const prodConfig = { const getEnv = () => { try { // 尝试获取微信环境 - const envVersion = __wxConfig?.envVersion || "develop"; + const envVersion = + typeof __wxConfig !== "undefined" && __wxConfig && __wxConfig.envVersion + ? __wxConfig.envVersion + : "develop"; return envVersion === "release" ? "production" : "development"; } catch (e) { return "development"; @@ -35,14 +38,9 @@ const getEnv = () => { const env = getEnv(); const config = env === "production" ? prodConfig : devConfig; -module.exports = { - ...config, +module.exports = Object.assign({}, config, { env, - // 其他配置项 - // 上传文件大小限制 (MB) uploadMaxSize: 5, - // 请求超时时间 (ms) requestTimeout: 30000, - // 版本号 version: "1.0.0", -}; +}); diff --git a/miniprogram/images/icon-phone.svg b/miniprogram/images/icon-phone.svg new file mode 100644 index 00000000..eb079fee --- /dev/null +++ b/miniprogram/images/icon-phone.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/miniprogram/images/icon-shield.svg b/miniprogram/images/icon-shield.svg new file mode 100644 index 00000000..8ca18e82 --- /dev/null +++ b/miniprogram/images/icon-shield.svg @@ -0,0 +1,4 @@ + + + + diff --git a/miniprogram/images/icon-user.svg b/miniprogram/images/icon-user.svg new file mode 100644 index 00000000..190e4963 --- /dev/null +++ b/miniprogram/images/icon-user.svg @@ -0,0 +1,4 @@ + + + + diff --git a/miniprogram/images/icon-users.svg b/miniprogram/images/icon-users.svg new file mode 100644 index 00000000..8a0881d3 --- /dev/null +++ b/miniprogram/images/icon-users.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/miniprogram/pages/index/index.js b/miniprogram/pages/index/index.js index 1f26461d..2024f7d3 100644 --- a/miniprogram/pages/index/index.js +++ b/miniprogram/pages/index/index.js @@ -1,57 +1,59 @@ -const app = getApp() -const util = require('../../utils/util') +const app = getApp(); +const util = require("../../utils/util"); Page({ data: { currentStore: null, - gender: '', + gender: "", list: [], loading: false, page: 1, pageSize: 20, - hasMore: true + hasMore: true, }, onLoad() { - this.initData() + this.initData(); }, onShow() { - const newStore = app.globalData.currentStore - const oldStoreId = this.data.currentStore?.storeId + const newStore = app.globalData.currentStore; + const oldStoreId = this.data.currentStore + ? this.data.currentStore.storeId + : null; // 检查门店是否切换 if (newStore && newStore.storeId !== oldStoreId) { - this.setData({ + this.setData({ currentStore: newStore, page: 1, hasMore: true, - list: [] - }) - this.fetchData() + list: [], + }); + this.fetchData(); } else if (app.globalData.storeChanged) { // 全局标记门店已切换 - app.globalData.storeChanged = false - this.setData({ + app.globalData.storeChanged = false; + this.setData({ currentStore: newStore, page: 1, hasMore: true, - list: [] - }) - this.fetchData() + list: [], + }); + this.fetchData(); } }, onPullDownRefresh() { - this.setData({ page: 1, hasMore: true }) + this.setData({ page: 1, hasMore: true }); this.fetchData().then(() => { - wx.stopPullDownRefresh() - }) + wx.stopPullDownRefresh(); + }); }, onReachBottom() { if (this.data.hasMore && !this.data.loading) { - this.loadMore() + this.loadMore(); } }, @@ -59,66 +61,76 @@ Page({ // 检查是否已登录(有 token) if (!app.globalData.token) { // 未登录,跳转到用户页面进行登录 - wx.switchTab({ url: '/pages/user/index' }) - return + wx.switchTab({ url: "/pages/user/index" }); + return; } // 获取当前门店 try { - const store = await app.getCurrentStore() - this.setData({ currentStore: store }) - this.fetchData() + const store = await app.getCurrentStore(); + this.setData({ currentStore: store }); + this.fetchData(); } catch (e) { - console.error('获取门店失败:', e) + console.error("获取门店失败:", e); // 如果是认证失败,跳转到登录页 if (e.code === 401) { - wx.switchTab({ url: '/pages/user/index' }) + wx.switchTab({ url: "/pages/user/index" }); } } }, async fetchData() { - if (!this.data.currentStore?.storeId) return + if (!this.data.currentStore || !this.data.currentStore.storeId) return; - this.setData({ loading: true }) + this.setData({ loading: true }); try { - const res = await app.request('/api/ladder/ranking', { + const res = await app.request("/api/ladder/ranking", { store_id: this.data.currentStore.storeId, gender: this.data.gender, page: this.data.page, - pageSize: this.data.pageSize - }) + pageSize: this.data.pageSize, + }); - const list = res.data.list || [] + const list = res.data.list || []; this.setData({ - list: this.data.page === 1 ? list : [...this.data.list, ...list], - hasMore: list.length >= this.data.pageSize - }) + list: this.data.page === 1 ? list : this.data.list.concat(list), + hasMore: list.length >= this.data.pageSize, + }); } catch (e) { - console.error('获取排名失败:', e) + console.error("获取排名失败:", e); } finally { - this.setData({ loading: false }) + this.setData({ loading: false }); } }, loadMore() { - this.setData({ page: this.data.page + 1 }) - this.fetchData() + this.setData({ page: this.data.page + 1 }); + this.fetchData(); }, setGender(e) { - const gender = e.currentTarget.dataset.gender - this.setData({ gender, page: 1, hasMore: true }) - this.fetchData() + const gender = e.currentTarget.dataset.gender; + this.setData({ gender, page: 1, hasMore: true }); + this.fetchData(); }, selectStore() { - wx.navigateTo({ url: '/pages/store/index' }) + wx.navigateTo({ url: "/pages/store/index" }); }, viewPlayer(e) { - const id = e.currentTarget.dataset.id - wx.navigateTo({ url: `/pages/player/index?id=${id}` }) - } -}) + const player = e.currentTarget.dataset.player; + const id = player && player.id ? player.id : e.currentTarget.dataset.id; + if (!id) return; + + wx.navigateTo({ + url: `/pages/player/index?id=${id}`, + success: (res) => { + if (res && res.eventChannel && player) { + res.eventChannel.emit("player", player); + } + }, + }); + }, +}); diff --git a/miniprogram/pages/index/index.wxml b/miniprogram/pages/index/index.wxml index 813e8ec4..45a47157 100644 --- a/miniprogram/pages/index/index.wxml +++ b/miniprogram/pages/index/index.wxml @@ -41,14 +41,14 @@ bindtap="setGender" data-gender="1" > - ♂ 男子 + 男子 - ♀ 女子 + 女子 @@ -62,11 +62,11 @@ wx:key="id" bindtap="viewPlayer" data-id="{{item.id}}" + data-player="{{item}}" > - {{item.rank === 1 ? '👑' : item.rank === 2 ? '🥈' : '🥉'}} - {{item.rank}} + {{item.rank}} diff --git a/miniprogram/pages/index/index.wxss b/miniprogram/pages/index/index.wxss index a0a29c8c..2965a154 100644 --- a/miniprogram/pages/index/index.wxss +++ b/miniprogram/pages/index/index.wxss @@ -49,7 +49,7 @@ width: 12rpx; height: 12rpx; border-radius: 50%; - background: #ff6b35; + background: var(--primary); box-shadow: 0 0 8rpx rgba(255, 107, 53, 0.4); animation: pulse 2s ease-in-out infinite; } @@ -57,7 +57,7 @@ .store-name { font-size: 30rpx; font-weight: 600; - color: #333; + color: var(--text-primary); letter-spacing: 0.5rpx; } @@ -77,13 +77,13 @@ .change-store-text { font-size: 24rpx; - color: #666; + color: var(--text-secondary); font-weight: 500; } .change-store-arrow { font-size: 22rpx; - color: #999; + color: var(--text-muted); font-weight: 300; } @@ -100,7 +100,7 @@ display: block; font-size: 52rpx; font-weight: 700; - color: #1a1a1a; + color: var(--text-primary); margin-bottom: 8rpx; letter-spacing: 1rpx; } @@ -108,7 +108,7 @@ .page-subtitle { display: block; font-size: 26rpx; - color: #999; + color: var(--text-muted); font-weight: 400; letter-spacing: 0.5rpx; } @@ -149,9 +149,9 @@ } .filter-item.active { - background: linear-gradient(135deg, #ff6b35 0%, #ff8c42 100%); - color: #fff; - box-shadow: 0 4rpx 16rpx rgba(255, 107, 53, 0.3); + background: var(--primary-gradient); + color: var(--text-white); + box-shadow: var(--shadow-primary); font-weight: 600; } @@ -168,12 +168,12 @@ display: flex; align-items: center; padding: 24rpx; - background: #fff; + background: var(--bg-card); border-radius: 20rpx; margin-bottom: 16rpx; - box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.08); + box-shadow: var(--shadow-card); transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); - border: 1rpx solid #f0f0f0; + border: 1rpx solid var(--border-soft); } .ranking-item:last-child { @@ -186,9 +186,9 @@ } .ranking-item.top-rank { - background: linear-gradient(135deg, #fff5f0 0%, #fff 100%); - border: 2rpx solid rgba(255, 107, 53, 0.2); - box-shadow: 0 6rpx 20rpx rgba(255, 107, 53, 0.15); + background: linear-gradient(135deg, var(--primary-soft) 0%, var(--bg-white) 100%); + border: 2rpx solid var(--border-primary); + box-shadow: var(--shadow-primary); } /* 排名徽章 */ @@ -233,8 +233,8 @@ } .rank-badge.normal { - background: linear-gradient(135deg, #f5f5f5 0%, #e8e8e8 100%); - color: #666; + background: linear-gradient(135deg, var(--bg-soft) 0%, var(--bg-card-hover) 100%); + color: var(--text-secondary); font-weight: 600; } @@ -244,10 +244,10 @@ height: 80rpx; border-radius: 50%; margin-right: 20rpx; - border: 3rpx solid #fff; + border: 3rpx solid var(--bg-white); box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.1); flex-shrink: 0; - background: #f5f5f5; + background: var(--bg-soft); } /* 选手信息 */ @@ -260,7 +260,7 @@ display: block; font-size: 30rpx; font-weight: 600; - color: #333; + color: var(--text-primary); margin-bottom: 8rpx; overflow: hidden; text-overflow: ellipsis; @@ -292,7 +292,7 @@ .player-stats { font-size: 24rpx; - color: #666; + color: var(--text-secondary); font-weight: 500; } @@ -306,14 +306,14 @@ display: block; font-size: 36rpx; font-weight: 700; - color: #ff6b35; + color: var(--primary); line-height: 1.2; margin-bottom: 4rpx; } .power-label { font-size: 22rpx; - color: #999; + color: var(--text-muted); font-weight: 500; } diff --git a/miniprogram/pages/match/challenge-detail/index.js b/miniprogram/pages/match/challenge-detail/index.js index e44d30bf..bccdde2c 100644 --- a/miniprogram/pages/match/challenge-detail/index.js +++ b/miniprogram/pages/match/challenge-detail/index.js @@ -198,8 +198,8 @@ Page({ canConfirmScore = true console.log('进行中状态:设置确认比分权限(对方已提交,等待确认)', { submitBy: game.submitBy, - challengerId: matchInfo.challenger?.id, - defenderId: matchInfo.defender?.id, + challengerId: matchInfo.challenger ? matchInfo.challenger.id : null, + defenderId: matchInfo.defender ? matchInfo.defender.id : null, myRole }) } @@ -214,11 +214,11 @@ Page({ console.log('尝试通过游戏信息识别角色:', { player1Id: game.player1Id, player2Id: game.player2Id, - challengerId: matchInfo.challenger?.id, - defenderId: matchInfo.defender?.id, + challengerId: matchInfo.challenger ? matchInfo.challenger.id : null, + defenderId: matchInfo.defender ? matchInfo.defender.id : null, currentUserId: currentUser.id, - challengerUserId: matchInfo.challenger?.userId, - defenderUserId: matchInfo.defender?.userId + challengerUserId: matchInfo.challenger ? matchInfo.challenger.userId : null, + defenderUserId: matchInfo.defender ? matchInfo.defender.userId : null }) // 通过比较 challenger/defender 的 id(ladder_user_id)和 player1_id/player2_id 来判断 @@ -262,8 +262,8 @@ Page({ // 如果游戏状态为2(已提交)且对方已提交,等待我确认 else if (game.status === 2 && game.submitBy) { // 判断当前用户是否是提交者 - const isSubmitter = (myRole === 'challenger' && game.submitBy == matchInfo.challenger?.id) || - (myRole === 'defender' && game.submitBy == matchInfo.defender?.id) + const isSubmitter = (myRole === 'challenger' && matchInfo.challenger && game.submitBy == matchInfo.challenger.id) || + (myRole === 'defender' && matchInfo.defender && game.submitBy == matchInfo.defender.id) if (!isSubmitter && game.confirmStatus === 0) { canConfirmScore = true @@ -317,10 +317,10 @@ Page({ canAccept, canReject, matchInfoStatus: matchInfo.status, - defenderUserId: matchInfo.defender?.userId, - currentUserId: app.globalData.userInfo?.id, - defenderPhone: matchInfo.defender?.phone, - currentUserPhone: app.globalData.userInfo?.phone + defenderUserId: matchInfo.defender ? matchInfo.defender.userId : null, + currentUserId: app.globalData.userInfo ? app.globalData.userInfo.id : null, + defenderPhone: matchInfo.defender ? matchInfo.defender.phone : null, + currentUserPhone: app.globalData.userInfo ? app.globalData.userInfo.phone : null }) } } catch (e) { @@ -471,7 +471,7 @@ Page({ // 确认比分 async confirmScore(confirm) { - const game = this.data.matchInfo.games?.[0] + const game = this.data.matchInfo.games && this.data.matchInfo.games[0] if (!game) { wx.showToast({ title: '比赛信息错误', icon: 'none' }) return @@ -503,7 +503,7 @@ Page({ // 确认比分按钮 confirmScoreBtn() { - const game = this.data.matchInfo.games?.[0] + const game = this.data.matchInfo.games && this.data.matchInfo.games[0] if (!game) { wx.showToast({ title: '比赛信息错误', icon: 'none' }) return @@ -521,12 +521,12 @@ Page({ myScore = game.player1Score || 0 opponentScore = game.player2Score || 0 myName = this.data.matchInfo.challenger.realName || '挑战者' - opponentName = this.data.matchInfo.defender?.realName || '被挑战者' + opponentName = (this.data.matchInfo.defender && this.data.matchInfo.defender.realName) || '被挑战者' } else if (this.data.matchInfo.challenger && this.data.matchInfo.challenger.id == game.player2Id) { myScore = game.player2Score || 0 opponentScore = game.player1Score || 0 myName = this.data.matchInfo.challenger.realName || '挑战者' - opponentName = this.data.matchInfo.defender?.realName || '被挑战者' + opponentName = (this.data.matchInfo.defender && this.data.matchInfo.defender.realName) || '被挑战者' } else { // 如果无法确定,使用默认显示 myScore = game.player1Score || 0 @@ -538,12 +538,12 @@ Page({ myScore = game.player1Score || 0 opponentScore = game.player2Score || 0 myName = this.data.matchInfo.defender.realName || '被挑战者' - opponentName = this.data.matchInfo.challenger?.realName || '挑战者' + opponentName = (this.data.matchInfo.challenger && this.data.matchInfo.challenger.realName) || '挑战者' } else if (this.data.matchInfo.defender && this.data.matchInfo.defender.id == game.player2Id) { myScore = game.player2Score || 0 opponentScore = game.player1Score || 0 myName = this.data.matchInfo.defender.realName || '被挑战者' - opponentName = this.data.matchInfo.challenger?.realName || '挑战者' + opponentName = (this.data.matchInfo.challenger && this.data.matchInfo.challenger.realName) || '挑战者' } else { // 如果无法确定,使用默认显示 myScore = game.player1Score || 0 diff --git a/miniprogram/pages/match/challenge-detail/index.wxss b/miniprogram/pages/match/challenge-detail/index.wxss index 6efe6d0b..512420c2 100644 --- a/miniprogram/pages/match/challenge-detail/index.wxss +++ b/miniprogram/pages/match/challenge-detail/index.wxss @@ -1,6 +1,6 @@ .page-container { min-height: 100vh; - background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%); + background: linear-gradient(135deg, var(--bg-page) 0%, var(--primary-soft) 100%); padding: 20rpx; } @@ -9,14 +9,14 @@ justify-content: center; align-items: center; height: 60vh; - color: #666; + color: var(--text-secondary); } .match-info { - background: #fff; - border-radius: 24rpx; + background: var(--bg-card); + border-radius: var(--radius-lg); padding: 40rpx; - box-shadow: 0 8rpx 32rpx rgba(0, 0, 0, 0.1); + box-shadow: var(--shadow-card); } .match-header { @@ -25,13 +25,13 @@ align-items: center; margin-bottom: 40rpx; padding-bottom: 30rpx; - border-bottom: 2rpx solid #f0f0f0; + border-bottom: 2rpx solid var(--border-soft); } .match-title { font-size: 36rpx; font-weight: 600; - color: #333; + color: var(--text-primary); } .match-status { @@ -41,23 +41,23 @@ } .status-0 { - background: #fff3cd; - color: #856404; + background: var(--warning-soft); + color: var(--warning-text); } .status-1 { - background: #d1ecf1; - color: #0c5460; + background: var(--info-soft); + color: var(--info-text); } .status-2 { - background: #d4edda; - color: #155724; + background: var(--success-soft); + color: var(--success-text); } .status-3 { - background: #f8d7da; - color: #721c24; + background: var(--danger-soft); + color: var(--danger-text); } .opponent-section { @@ -70,7 +70,7 @@ .opponent-label { font-size: 24rpx; - color: #999; + color: var(--text-muted); margin-bottom: 20rpx; } @@ -84,7 +84,7 @@ width: 100rpx; height: 100rpx; border-radius: 50%; - border: 4rpx solid #e0e0e0; + border: 4rpx solid var(--border-light); } .opponent-details { @@ -97,12 +97,12 @@ .opponent-name { font-size: 32rpx; font-weight: 600; - color: #333; + color: var(--text-primary); } .opponent-level { font-size: 24rpx; - color: #666; + color: var(--text-secondary); } .vs-divider { @@ -110,25 +110,25 @@ margin: 30rpx 0; font-size: 32rpx; font-weight: 600; - color: #999; + color: var(--text-muted); } .match-progress { margin-bottom: 40rpx; padding-top: 30rpx; - border-top: 2rpx solid #f0f0f0; + border-top: 2rpx solid var(--border-soft); } .progress-title { font-size: 28rpx; font-weight: 600; - color: #333; + color: var(--text-primary); margin-bottom: 20rpx; } .game-item { - background: #f8f9fa; - border-radius: 16rpx; + background: var(--bg-soft); + border-radius: var(--radius-md); padding: 24rpx; margin-bottom: 16rpx; } @@ -141,33 +141,33 @@ .score-label { font-size: 24rpx; - color: #666; + color: var(--text-secondary); } .score-value { font-size: 32rpx; font-weight: 600; - color: #333; + color: var(--text-primary); margin-left: 12rpx; } .game-status { font-size: 24rpx; - color: #999; + color: var(--text-muted); } .confirm-tip { margin-top: 16rpx; padding: 16rpx 20rpx; - background: linear-gradient(135deg, #fff5f0 0%, #ffe8d6 100%); - border-radius: 12rpx; - border-left: 4rpx solid #ff6b35; - box-shadow: 0 2rpx 8rpx rgba(255, 107, 53, 0.1); + background: var(--primary-gradient-soft); + border-radius: var(--radius-sm); + border-left: 4rpx solid var(--primary); + box-shadow: var(--shadow-sm); } .confirm-tip .tip-text { font-size: 26rpx; - color: #d84315; + color: var(--primary-dark); font-weight: 500; } @@ -181,13 +181,13 @@ margin-top: 40rpx; padding: 30rpx; text-align: center; - background: #f8f9fa; - border-radius: 16rpx; + background: var(--bg-soft); + border-radius: var(--radius-md); } .tip-text { font-size: 28rpx; - color: #999; + color: var(--text-muted); } .action-btn { @@ -203,41 +203,42 @@ } .accept-btn { - background: linear-gradient(135deg, #ff6b35 0%, #ff8c42 100%); + background: var(--primary-gradient); color: #fff; - box-shadow: 0 4rpx 16rpx rgba(255, 107, 53, 0.3); + box-shadow: var(--shadow-primary); } .accept-btn:active { - background: linear-gradient(135deg, #e55a2b 0%, #e67e2f 100%); - box-shadow: 0 2rpx 8rpx rgba(255, 107, 53, 0.4); + background: var(--primary-dark); + box-shadow: var(--shadow-md); } .reject-btn { - background: #f5f5f5; - color: #666; + background: var(--bg-white); + color: var(--text-secondary); + border: 2rpx solid var(--border-light); } .submit-btn { - background: linear-gradient(135deg, #ff6b35 0%, #ff8c42 100%); + background: var(--primary-gradient); color: #fff; - box-shadow: 0 4rpx 16rpx rgba(255, 107, 53, 0.3); + box-shadow: var(--shadow-primary); } .submit-btn:active { - background: linear-gradient(135deg, #e55a2b 0%, #e67e2f 100%); - box-shadow: 0 2rpx 8rpx rgba(255, 107, 53, 0.4); + background: var(--primary-dark); + box-shadow: var(--shadow-md); } .confirm-btn { - background: linear-gradient(135deg, #ff6b35 0%, #ff8c42 100%); + background: var(--primary-gradient); color: #fff; - box-shadow: 0 4rpx 16rpx rgba(255, 107, 53, 0.3); + box-shadow: var(--shadow-primary); } .confirm-btn:active { - background: linear-gradient(135deg, #e55a2b 0%, #e67e2f 100%); - box-shadow: 0 2rpx 8rpx rgba(255, 107, 53, 0.4); + background: var(--primary-dark); + box-shadow: var(--shadow-md); } /* 填写比分弹框 */ @@ -257,10 +258,10 @@ .score-modal-content { width: 600rpx; max-width: 90%; - background: #fff; - border-radius: 24rpx; + background: var(--bg-card); + border-radius: var(--radius-lg); overflow: hidden; - box-shadow: 0 16rpx 48rpx rgba(0, 0, 0, 0.15); + box-shadow: var(--shadow-lg); } .modal-header { @@ -269,7 +270,7 @@ align-items: center; padding: 32rpx 40rpx; border-bottom: 2rpx solid rgba(255, 255, 255, 0.2); - background: linear-gradient(135deg, #ff6b35 0%, #ff8c42 100%); + background: var(--primary-gradient); } .modal-header .modal-title { @@ -311,7 +312,7 @@ .input-label { display: block; font-size: 28rpx; - color: #333; + color: var(--text-primary); font-weight: 500; margin-bottom: 12rpx; } @@ -319,27 +320,27 @@ .score-input { width: 100%; height: 88rpx; - background: #fff; - border-radius: 12rpx; + background: var(--bg-white); + border-radius: var(--radius-sm); padding: 0 24rpx; font-size: 32rpx; - color: #333; - border: 2rpx solid #e0e0e0; + color: var(--text-primary); + border: 2rpx solid var(--border-light); box-sizing: border-box; transition: all 0.3s ease; } .score-input:focus { - border-color: #ff6b35; - background: #fff5f0; + border-color: var(--primary); + background: var(--primary-soft); } .modal-footer { display: flex; gap: 20rpx; padding: 32rpx 40rpx; - border-top: 2rpx solid #f0f0f0; - background: #fafafa; + border-top: 2rpx solid var(--border-soft); + background: var(--bg-card-hover); } .modal-btn { @@ -353,31 +354,31 @@ align-items: center; justify-content: center; transition: all 0.3s ease; - box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.1); + box-shadow: var(--shadow-sm); } .modal-btn:active { transform: scale(0.98); - box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.15); + box-shadow: var(--shadow-md); } .cancel-btn { - background: #fff; - color: #666; - border: 2rpx solid #e0e0e0; + background: var(--bg-white); + color: var(--text-secondary); + border: 2rpx solid var(--border-light); } .cancel-btn:active { - background: #f5f5f5; + background: var(--bg-soft); } .modal-btn.submit-btn { - background: linear-gradient(135deg, #ff6b35 0%, #ff8c42 100%); + background: var(--primary-gradient); color: #fff; - box-shadow: 0 4rpx 16rpx rgba(255, 107, 53, 0.3); + box-shadow: var(--shadow-primary); } .modal-btn.submit-btn:active { - background: linear-gradient(135deg, #e55a2b 0%, #e67e2f 100%); - box-shadow: 0 2rpx 8rpx rgba(255, 107, 53, 0.4); + background: var(--primary-dark); + box-shadow: var(--shadow-md); } diff --git a/miniprogram/pages/match/challenge/index.js b/miniprogram/pages/match/challenge/index.js index 9da9d621..23032703 100644 --- a/miniprogram/pages/match/challenge/index.js +++ b/miniprogram/pages/match/challenge/index.js @@ -1,27 +1,27 @@ -const app = getApp() +const app = getApp(); Page({ data: { userInfo: null, ladderUser: null, currentStore: null, - ongoingMatches: [], // 正在进行中的比赛 - pendingGames: [] // 待确认的比赛 + ongoingMatches: [], // 正在进行中的比赛 + pendingGames: [], // 待确认的比赛 }, onLoad() { - this.initData() + this.initData(); }, onShow() { - this.initData() + this.initData(); }, async onPullDownRefresh() { try { - await this.initData() + await this.initData(); } finally { - wx.stopPullDownRefresh() + wx.stopPullDownRefresh(); } }, @@ -29,259 +29,284 @@ Page({ // 检查是否已登录(有 token) if (!app.globalData.token) { // 未登录,跳转到用户页面进行登录 - wx.switchTab({ url: '/pages/user/index' }) - return + wx.switchTab({ url: "/pages/user/index" }); + return; } // 每次显示页面时重新获取门店和天梯信息 try { - await app.getCurrentStore() + await app.getCurrentStore(); // 如果有门店,获取该门店的天梯信息 - if (app.globalData.currentStore?.storeId) { - await app.getLadderUser(app.globalData.currentStore.storeId) + if (app.globalData.currentStore && app.globalData.currentStore.storeId) { + await app.getLadderUser(app.globalData.currentStore.storeId); } } catch (e) { - console.error('获取门店/天梯信息失败:', e) + console.error("获取门店/天梯信息失败:", e); } - this.refreshData() + this.refreshData(); }, refreshData() { this.setData({ userInfo: app.globalData.userInfo, ladderUser: app.globalData.ladderUser, - currentStore: app.globalData.currentStore - }) + currentStore: app.globalData.currentStore, + }); if (app.globalData.ladderUser) { - this.fetchOngoingMatches() - this.fetchPendingGames() + this.fetchOngoingMatches(); + this.fetchPendingGames(); } }, // 获取正在进行中的比赛 async fetchOngoingMatches() { try { - const res = await app.request('/api/match/ongoing', { - store_id: this.data.currentStore?.storeId - }) - this.setData({ ongoingMatches: res.data || [] }) + const res = await app.request("/api/match/ongoing", { + store_id: this.data.currentStore + ? this.data.currentStore.storeId + : null, + }); + this.setData({ ongoingMatches: res.data || [] }); } catch (e) { - console.error('获取进行中比赛失败:', e) + console.error("获取进行中比赛失败:", e); } }, // 手动刷新天梯信息 async refreshLadderInfo() { - wx.showLoading({ title: '刷新中...' }) - + wx.showLoading({ title: "刷新中..." }); + try { // 重新获取门店信息 - await app.getCurrentStore() - + await app.getCurrentStore(); + // 重新获取天梯信息 - if (app.globalData.currentStore?.storeId) { - await app.getLadderUser(app.globalData.currentStore.storeId) + if (app.globalData.currentStore && app.globalData.currentStore.storeId) { + await app.getLadderUser(app.globalData.currentStore.storeId); } - - this.refreshData() - wx.hideLoading() - + + this.refreshData(); + wx.hideLoading(); + if (app.globalData.ladderUser) { - wx.showToast({ title: '已加入天梯', icon: 'success' }) + wx.showToast({ title: "已加入天梯", icon: "success" }); } else { - wx.showToast({ title: '暂未开通天梯', icon: 'none' }) + wx.showToast({ title: "暂未开通天梯", icon: "none" }); } } catch (e) { - wx.hideLoading() - console.error('刷新天梯信息失败:', e) - wx.showToast({ title: '刷新失败', icon: 'none' }) + wx.hideLoading(); + console.error("刷新天梯信息失败:", e); + wx.showToast({ title: "刷新失败", icon: "none" }); } }, async fetchPendingGames() { try { - const res = await app.request('/api/match/pending-confirm', { - store_id: this.data.currentStore?.storeId - }) - this.setData({ pendingGames: res.data || [] }) + const res = await app.request("/api/match/pending-confirm", { + store_id: this.data.currentStore + ? this.data.currentStore.storeId + : null, + }); + this.setData({ pendingGames: res.data || [] }); } catch (e) { - console.error('获取待确认比赛失败:', e) + console.error("获取待确认比赛失败:", e); } }, startChallenge() { if (!this.data.ladderUser) { - wx.showToast({ title: '请先加入天梯系统', icon: 'none' }) - return + wx.showToast({ title: "请先加入天梯系统", icon: "none" }); + return; + } + if (!this.data.currentStore || !this.data.currentStore.storeId) { + wx.showToast({ title: "请先选择门店", icon: "none" }); + wx.navigateTo({ url: "/pages/store/index" }); + return; } wx.scanCode({ onlyFromCamera: false, - scanType: ['qrCode'], + scanType: ["qrCode"], success: async (res) => { - const memberCode = res.result - this.checkAndChallenge(memberCode) + const memberCode = res.result; + this.checkAndChallenge(memberCode); }, fail: (err) => { - if (err.errMsg !== 'scanCode:fail cancel') { - wx.showToast({ title: '扫码失败', icon: 'none' }) + if (err.errMsg !== "scanCode:fail cancel") { + wx.showToast({ title: "扫码失败", icon: "none" }); } - } - }) + }, + }); }, async checkAndChallenge(memberCode) { - wx.showLoading({ title: '检查中...' }) + wx.showLoading({ title: "检查中..." }); try { - const res = await app.request(`/api/match/challenge/check/${memberCode}`, { - store_id: this.data.currentStore.storeId - }) + const res = await app.request( + `/api/match/challenge/check/${memberCode}`, + { + store_id: this.data.currentStore.storeId, + }, + ); - wx.hideLoading() + wx.hideLoading(); if (!res.data.canChallenge) { wx.showModal({ - title: '无法挑战', + title: "无法挑战", content: res.data.reason, - showCancel: false - }) - return + showCancel: false, + }); + return; } // 显示确认弹窗 - const target = res.data.targetUser + const target = res.data.targetUser; wx.showModal({ - title: '确认挑战', + title: "确认挑战", content: `确定要向 ${target.ladderUser.realName}(Lv${target.ladderUser.level}, 战力${target.ladderUser.powerScore}) 发起挑战吗?`, success: async (modalRes) => { if (modalRes.confirm) { - await this.createChallenge(memberCode) + await this.createChallenge(memberCode); } - } - }) + }, + }); } catch (e) { - wx.hideLoading() - console.error('检查挑战失败:', e) + wx.hideLoading(); + console.error("检查挑战失败:", e); } }, async createChallenge(memberCode) { - wx.showLoading({ title: '发起挑战中...' }) + wx.showLoading({ title: "发起挑战中..." }); try { - const res = await app.request('/api/match/challenge/create', { - store_id: this.data.currentStore.storeId, - target_member_code: memberCode - }, 'POST') + const res = await app.request( + "/api/match/challenge/create", + { + store_id: this.data.currentStore.storeId, + target_member_code: memberCode, + }, + "POST", + ); + + wx.hideLoading(); + wx.showToast({ title: "挑战已发起", icon: "success" }); - wx.hideLoading() - wx.showToast({ title: '挑战已发起', icon: 'success' }) - // 跳转到挑战赛详情页面 if (res.data && res.data.matchId) { setTimeout(() => { wx.navigateTo({ - url: `/pages/match/challenge-detail/index?id=${res.data.matchId}` - }) - }, 1500) + url: `/pages/match/challenge-detail/index?id=${res.data.matchId}`, + }); + }, 1500); } } catch (e) { - wx.hideLoading() - console.error('发起挑战失败:', e) - const errorMsg = e.message || e.data?.message || '发起挑战失败' - wx.showToast({ title: errorMsg, icon: 'none', duration: 2000 }) + wx.hideLoading(); + console.error("发起挑战失败:", e); + const errorMsg = + e.message || (e.data && e.data.message) || "发起挑战失败"; + wx.showToast({ title: errorMsg, icon: "none", duration: 2000 }); } }, joinRankingMatch() { if (!this.data.ladderUser) { - wx.showToast({ title: '请先加入天梯系统', icon: 'none' }) - return + wx.showToast({ title: "请先加入天梯系统", icon: "none" }); + return; } wx.scanCode({ onlyFromCamera: false, - scanType: ['qrCode'], + scanType: ["qrCode"], success: async (res) => { - const matchCode = res.result - wx.showLoading({ title: '加入中...' }) + const matchCode = res.result; + wx.showLoading({ title: "加入中..." }); try { - const joinRes = await app.request('/api/match/ranking/join', { - match_code: matchCode - }, 'POST') + const joinRes = await app.request( + "/api/match/ranking/join", + { + match_code: matchCode, + }, + "POST", + ); - wx.hideLoading() - wx.showToast({ title: '加入成功', icon: 'success' }) + wx.hideLoading(); + wx.showToast({ title: "加入成功", icon: "success" }); // 跳转到排位赛详情 wx.navigateTo({ - url: `/pages/match/ranking/index?code=${matchCode}` - }) + url: `/pages/match/ranking/index?code=${matchCode}`, + }); } catch (e) { - wx.hideLoading() - console.error('加入排位赛失败:', e) + wx.hideLoading(); + console.error("加入排位赛失败:", e); } }, fail: (err) => { - if (err.errMsg !== 'scanCode:fail cancel') { - wx.showToast({ title: '扫码失败', icon: 'none' }) + if (err.errMsg !== "scanCode:fail cancel") { + wx.showToast({ title: "扫码失败", icon: "none" }); } - } - }) + }, + }); }, goToStore() { - wx.navigateTo({ url: '/pages/store/index' }) + wx.navigateTo({ url: "/pages/store/index" }); }, // 跳转到比赛详情 goToMatchDetail(e) { - const match = e.currentTarget.dataset.match + const match = e.currentTarget.dataset.match; if (match.type === 1) { // 挑战赛详情 wx.navigateTo({ - url: `/pages/match/challenge-detail/index?id=${match.id}` - }) + url: `/pages/match/challenge-detail/index?id=${match.id}`, + }); } else { // 排位赛详情 wx.navigateTo({ - url: `/pages/match/ranking/index?code=${match.matchCode}` - }) + url: `/pages/match/ranking/index?code=${match.matchCode}`, + }); } }, confirmGame(e) { - const game = e.currentTarget.dataset.game + const game = e.currentTarget.dataset.game; wx.showModal({ - title: '确认比分', + title: "确认比分", content: `确认比分 ${game.myScore} : ${game.opponentScore} 吗?`, - confirmText: '确认', - cancelText: '有争议', + confirmText: "确认", + cancelText: "有争议", success: async (res) => { - wx.showLoading({ title: '处理中...' }) + wx.showLoading({ title: "处理中..." }); try { - await app.request('/api/match/challenge/confirm-score', { - game_id: game.id, - confirm: res.confirm - }, 'POST') + await app.request( + "/api/match/challenge/confirm-score", + { + game_id: game.id, + confirm: res.confirm, + }, + "POST", + ); - wx.hideLoading() + wx.hideLoading(); wx.showToast({ - title: res.confirm ? '确认成功' : '已标记争议', - icon: 'success' - }) - this.fetchPendingGames() + title: res.confirm ? "确认成功" : "已标记争议", + icon: "success", + }); + this.fetchPendingGames(); } catch (e) { - wx.hideLoading() - console.error('确认比分失败:', e) + wx.hideLoading(); + console.error("确认比分失败:", e); } - } - }) - } -}) + }, + }); + }, +}); diff --git a/miniprogram/pages/match/challenge/index.wxml b/miniprogram/pages/match/challenge/index.wxml index 045358e9..3fe73198 100644 --- a/miniprogram/pages/match/challenge/index.wxml +++ b/miniprogram/pages/match/challenge/index.wxml @@ -9,20 +9,22 @@ - 🏸 发起挑战 + 发起挑战 扫描对手会员码,开启对决 - 📍 + {{currentStore.storeName}} - 🏸 + + + 暂未开通天梯 请联系门店工作人员加入天梯系统 @@ -67,7 +69,7 @@ - ⚔️ + 挑战赛 1v1 对决 @@ -76,7 +78,7 @@ - 🏆 + 排位赛 多人竞技 @@ -88,7 +90,7 @@ - 🔥 + 进行中的比赛 {{ongoingMatches.length}} @@ -134,9 +136,9 @@ {{item.opponent.realName}} - ⏳ 等待中 - 🎾 比赛中 - ✅ 已完成 + 等待中 + 比赛中 + 已完成 @@ -152,7 +154,7 @@ - 📋 待确认比分 + 待确认比分 {{pendingGames.length}} @@ -171,7 +173,9 @@ - 📖 + + + 战力值规则 @@ -197,7 +201,9 @@ - 🛡 + + + 新手保护 输分减半 @@ -205,7 +211,7 @@ - 💡 同一对手30天内仅限挑战1次 + 提示:同一对手30天内仅限挑战1次 diff --git a/miniprogram/pages/match/challenge/index.wxss b/miniprogram/pages/match/challenge/index.wxss index 4a5b9ad3..af11074f 100644 --- a/miniprogram/pages/match/challenge/index.wxss +++ b/miniprogram/pages/match/challenge/index.wxss @@ -4,7 +4,12 @@ .page-container { min-height: 100vh; - background: linear-gradient(180deg, #FEF7F3 0%, #FAFAFA 30%, #F5F5F5 100%); + background: linear-gradient( + 180deg, + var(--primary-soft) 0%, + var(--bg-page) 30%, + var(--bg-soft) 100% + ); position: relative; overflow: hidden; } @@ -42,7 +47,9 @@ } @keyframes spin { - to { transform: translateX(-50%) rotate(360deg); } + to { + transform: translateX(-50%) rotate(360deg); + } } /* 主要内容 */ @@ -63,7 +70,7 @@ display: block; font-size: 52rpx; font-weight: 700; - color: #1a1a1a; + color: var(--text-primary); margin-bottom: 12rpx; letter-spacing: 1rpx; } @@ -71,7 +78,7 @@ .page-subtitle { display: block; font-size: 26rpx; - color: #999; + color: var(--text-muted); font-weight: 400; letter-spacing: 0.5rpx; } @@ -88,11 +95,12 @@ box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04); margin: 0 auto 24rpx; width: fit-content; - border: 1rpx solid rgba(0, 0, 0, 0.04); + border: 1rpx solid var(--border-soft); } .store-icon { - font-size: 28rpx; + width: 28rpx; + height: 28rpx; } .store-name { @@ -117,8 +125,12 @@ align-items: center; gap: 20rpx; padding: 28rpx 24rpx; - background: linear-gradient(135deg, #FFF9F5 0%, #FFFFFF 100%); - border: 2rpx solid #FFE8D5; + background: linear-gradient( + 135deg, + var(--primary-soft) 0%, + var(--bg-white) 100% + ); + border: 2rpx solid var(--border-primary); border-radius: 24rpx; margin-bottom: 24rpx; box-shadow: 0 4rpx 16rpx rgba(255, 152, 0, 0.1); @@ -127,12 +139,16 @@ .notice-icon { width: 80rpx; height: 80rpx; - background: linear-gradient(135deg, #FFF3E0, #FFE0B2); + background: var(--primary-gradient-soft); border-radius: 20rpx; display: flex; align-items: center; justify-content: center; - font-size: 40rpx; +} + +.notice-icon-img { + width: 44rpx; + height: 44rpx; } .notice-content { @@ -143,21 +159,21 @@ display: block; font-size: 30rpx; font-weight: 700; - color: #E65100; + color: var(--primary-dark); margin-bottom: 6rpx; } .notice-desc { display: block; font-size: 24rpx; - color: #F57C00; + color: var(--primary); } .notice-action { padding: 16rpx 28rpx; - background: linear-gradient(135deg, #FF8A65, #FF6B35); + background: var(--primary-gradient); border-radius: 50rpx; - box-shadow: 0 4rpx 12rpx rgba(255, 107, 53, 0.3); + box-shadow: var(--shadow-primary); } .notice-action:active { @@ -174,13 +190,17 @@ 用户信息卡片 - 全新设计 ========================================== */ .user-card { - background: linear-gradient(135deg, #FFFFFF 0%, #FAFAFA 100%); + background: linear-gradient( + 135deg, + var(--bg-white) 0%, + var(--bg-card-hover) 100% + ); border-radius: 28rpx; padding: 0; margin-bottom: 24rpx; box-shadow: 0 12rpx 40rpx rgba(0, 0, 0, 0.08); overflow: hidden; - border: 1rpx solid rgba(255, 107, 53, 0.1); + border: 1rpx solid var(--border-primary); } .user-card-inner { @@ -192,13 +212,13 @@ } .user-card-inner::before { - content: ''; + content: ""; position: absolute; top: 0; left: 0; right: 0; height: 6rpx; - background: linear-gradient(90deg, #FF8A65, #FF6B35, #FFB74D); + background: var(--primary-gradient); } .user-avatar-box { @@ -209,10 +229,10 @@ } .user-avatar-box::before { - content: ''; + content: ""; position: absolute; inset: -6rpx; - background: linear-gradient(135deg, #FF8A65, #FFB74D); + background: var(--primary-gradient); border-radius: 50%; z-index: 0; } @@ -253,11 +273,26 @@ box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1); } -.user-level.lv1 { background: linear-gradient(135deg, #81C784, #66BB6A); color: #fff; } -.user-level.lv2 { background: linear-gradient(135deg, #64B5F6, #42A5F5); color: #fff; } -.user-level.lv3 { background: linear-gradient(135deg, #FFB74D, #FFA726); color: #fff; } -.user-level.lv4 { background: linear-gradient(135deg, #F06292, #EC407A); color: #fff; } -.user-level.lv5 { background: linear-gradient(135deg, #BA68C8, #AB47BC); color: #fff; } +.user-level.lv1 { + background: linear-gradient(135deg, #81c784, #66bb6a); + color: #fff; +} +.user-level.lv2 { + background: linear-gradient(135deg, #64b5f6, #42a5f5); + color: #fff; +} +.user-level.lv3 { + background: linear-gradient(135deg, #ffb74d, #ffa726); + color: #fff; +} +.user-level.lv4 { + background: linear-gradient(135deg, #f06292, #ec407a); + color: #fff; +} +.user-level.lv5 { + background: linear-gradient(135deg, #ba68c8, #ab47bc); + color: #fff; +} .user-stats-row { display: flex; @@ -278,7 +313,7 @@ } .mini-stat-value.win { - color: #00C853; + color: #00c853; } .mini-stat-label { @@ -318,7 +353,7 @@ } .scan-card::before { - content: ''; + content: ""; position: absolute; top: 0; left: 0; @@ -329,11 +364,19 @@ } .scan-card.challenge::before { - background: linear-gradient(180deg, rgba(255, 107, 53, 0.08) 0%, transparent 50%); + background: linear-gradient( + 180deg, + rgba(255, 107, 53, 0.08) 0%, + transparent 50% + ); } .scan-card.ranking::before { - background: linear-gradient(180deg, rgba(255, 193, 7, 0.1) 0%, transparent 50%); + background: linear-gradient( + 180deg, + rgba(255, 193, 7, 0.1) 0%, + transparent 50% + ); } .scan-card:active { @@ -357,15 +400,16 @@ } .scan-card.challenge .scan-icon-wrapper { - background: linear-gradient(135deg, #FFE8DD, #FFCCBC); + background: linear-gradient(135deg, #ffe8dd, #ffccbc); } .scan-card.ranking .scan-icon-wrapper { - background: linear-gradient(135deg, #FFF8E1, #FFE082); + background: linear-gradient(135deg, #fff8e1, #ffe082); } -.scan-icon { - font-size: 52rpx; +.scan-icon-img { + width: 56rpx; + height: 56rpx; } .scan-title { @@ -387,7 +431,7 @@ .scan-badge { display: inline-block; padding: 8rpx 20rpx; - background: linear-gradient(135deg, #FF8A65, #FF6B35); + background: linear-gradient(135deg, #ff8a65, #ff6b35); color: #fff; font-size: 22rpx; font-weight: 700; @@ -396,8 +440,8 @@ } .scan-badge.accent { - background: linear-gradient(135deg, #FFD54F, #FFB300); - color: #5D4037; + background: linear-gradient(135deg, #ffd54f, #ffb300); + color: #5d4037; box-shadow: 0 4rpx 12rpx rgba(255, 179, 0, 0.3); } @@ -418,7 +462,7 @@ align-items: center; justify-content: space-between; padding: 20rpx 24rpx; - background: linear-gradient(90deg, #FFF3E0, #FFFFFF); + background: linear-gradient(90deg, #fff3e0, #ffffff); border-bottom: 1rpx solid rgba(255, 152, 0, 0.1); } @@ -428,8 +472,10 @@ gap: 10rpx; } -.ongoing-icon { - font-size: 28rpx; +.ongoing-icon-img { + width: 28rpx; + height: 28rpx; + opacity: 0.9; } .ongoing-title { @@ -442,7 +488,7 @@ min-width: 40rpx; height: 40rpx; padding: 0 14rpx; - background: linear-gradient(135deg, #FF5722, #FF8A65); + background: linear-gradient(135deg, #ff5722, #ff8a65); color: #fff; font-size: 24rpx; font-weight: 700; @@ -458,7 +504,7 @@ } .ongoing-item { - background: linear-gradient(135deg, #FAFAFA, #F5F5F5); + background: linear-gradient(135deg, #fafafa, #f5f5f5); border-radius: 20rpx; margin-bottom: 16rpx; overflow: hidden; @@ -498,13 +544,13 @@ } .match-type-tag.challenge { - background: linear-gradient(135deg, #FFE8DD, #FFCCBC); - color: #E65100; + background: linear-gradient(135deg, #ffe8dd, #ffccbc); + color: #e65100; } .match-type-tag.ranking { - background: linear-gradient(135deg, #FFF8E1, #FFE082); - color: #F57C00; + background: linear-gradient(135deg, #fff8e1, #ffe082); + color: #f57c00; } .match-status-tag { @@ -515,19 +561,24 @@ } .match-status-tag.waiting { - background: #E3F2FD; - color: #1565C0; + background: #e3f2fd; + color: #1565c0; } .match-status-tag.playing { - background: #E8F5E9; - color: #2E7D32; + background: #e8f5e9; + color: #2e7d32; animation: pulse 2s infinite; } @keyframes pulse { - 0%, 100% { opacity: 1; } - 50% { opacity: 0.7; } + 0%, + 100% { + opacity: 1; + } + 50% { + opacity: 0.7; + } } .ongoing-item-body { @@ -545,7 +596,7 @@ width: 80rpx; height: 80rpx; border-radius: 50%; - border: 3rpx solid #FFE0B2; + border: 3rpx solid #ffe0b2; } .opponent-detail { @@ -567,7 +618,7 @@ .opponent-level { padding: 4rpx 12rpx; - background: linear-gradient(135deg, #FFB74D, #FFA726); + background: linear-gradient(135deg, #ffb74d, #ffa726); color: #fff; font-size: 20rpx; font-weight: 600; @@ -601,8 +652,8 @@ .ranking-stage { padding: 4rpx 12rpx; - background: #E3F2FD; - color: #1565C0; + background: #e3f2fd; + color: #1565c0; border-radius: 6rpx; font-weight: 600; } @@ -633,11 +684,11 @@ } .my-status.waiting { - color: #1565C0; + color: #1565c0; } .my-status.playing { - color: #2E7D32; + color: #2e7d32; } .my-status.finished { @@ -655,7 +706,7 @@ .match-weight { padding: 4rpx 12rpx; - background: linear-gradient(135deg, #FF8A65, #FF6B35); + background: linear-gradient(135deg, #ff8a65, #ff6b35); color: #fff; font-size: 20rpx; font-weight: 600; @@ -685,7 +736,7 @@ align-items: center; justify-content: space-between; padding: 20rpx 24rpx; - background: linear-gradient(90deg, #FFF5F2, #FFFFFF); + background: linear-gradient(90deg, #fff5f2, #ffffff); border-bottom: 1rpx solid rgba(255, 107, 53, 0.1); } @@ -699,7 +750,7 @@ min-width: 40rpx; height: 40rpx; padding: 0 14rpx; - background: linear-gradient(135deg, #FF8A65, #FF6B35); + background: linear-gradient(135deg, #ff8a65, #ff6b35); color: #fff; font-size: 24rpx; font-weight: 700; @@ -718,7 +769,7 @@ display: flex; align-items: center; padding: 18rpx 20rpx; - background: linear-gradient(135deg, #FAFAFA, #F5F5F5); + background: linear-gradient(135deg, #fafafa, #f5f5f5); border-radius: 16rpx; margin-bottom: 12rpx; transition: all 0.2s; @@ -729,7 +780,7 @@ } .pending-item:active { - background: linear-gradient(135deg, #F5F5F5, #EEEEEE); + background: linear-gradient(135deg, #f5f5f5, #eeeeee); } .game-info { @@ -741,7 +792,7 @@ .vs-tag { padding: 6rpx 14rpx; - background: linear-gradient(135deg, #FF8A65, #FF6B35); + background: linear-gradient(135deg, #ff8a65, #ff6b35); color: #fff; font-size: 20rpx; font-weight: 800; @@ -759,12 +810,12 @@ font-weight: 800; color: var(--text-primary); padding: 0 20rpx; - font-family: 'SF Mono', 'Monaco', monospace; + font-family: "SF Mono", "Monaco", monospace; } .confirm-btn { padding: 14rpx 28rpx; - background: linear-gradient(135deg, #00C853, #00E676); + background: linear-gradient(135deg, #00c853, #00e676); color: #fff; font-size: 24rpx; font-weight: 700; @@ -798,12 +849,16 @@ .rules-icon { width: 48rpx; height: 48rpx; - background: linear-gradient(135deg, #FFF3E0, #FFE0B2); + background: linear-gradient(135deg, #fff3e0, #ffe0b2); border-radius: 14rpx; display: flex; align-items: center; justify-content: center; - font-size: 26rpx; +} + +.rules-icon-img { + width: 28rpx; + height: 28rpx; } .rules-title { @@ -823,7 +878,7 @@ align-items: center; gap: 14rpx; padding: 18rpx; - background: linear-gradient(135deg, #FAFAFA, #F5F5F5); + background: linear-gradient(135deg, #fafafa, #f5f5f5); border-radius: 18rpx; transition: all 0.2s; } @@ -844,24 +899,29 @@ flex-shrink: 0; } +.rule-icon-img { + width: 28rpx; + height: 28rpx; +} + .rule-icon.win { - background: linear-gradient(135deg, #E8F5E9, #C8E6C9); - color: #2E7D32; + background: linear-gradient(135deg, #e8f5e9, #c8e6c9); + color: #2e7d32; } .rule-icon.lose { - background: linear-gradient(135deg, #FFEBEE, #FFCDD2); - color: #C62828; + background: linear-gradient(135deg, #ffebee, #ffcdd2); + color: #c62828; } .rule-icon.bonus { - background: linear-gradient(135deg, #FFF8E1, #FFECB3); - color: #F57C00; + background: linear-gradient(135deg, #fff8e1, #ffecb3); + color: #f57c00; } .rule-icon.shield { - background: linear-gradient(135deg, #E3F2FD, #BBDEFB); - color: #1565C0; + background: linear-gradient(135deg, #e3f2fd, #bbdefb); + color: #1565c0; } .rule-text { @@ -884,21 +944,21 @@ } .rule-value.positive { - color: #2E7D32; + color: #2e7d32; } .rule-value.negative { - color: #C62828; + color: #c62828; } .rules-note { margin-top: 18rpx; padding: 18rpx; - background: linear-gradient(135deg, #FFF8E1, #FFFDE7); + background: linear-gradient(135deg, #fff8e1, #fffde7); border-radius: 14rpx; text-align: center; font-size: 24rpx; - color: #F57C00; + color: #f57c00; font-weight: 600; border: 1rpx solid rgba(255, 152, 0, 0.15); } diff --git a/miniprogram/pages/match/history/index.js b/miniprogram/pages/match/history/index.js index 2bb1804d..25257b96 100644 --- a/miniprogram/pages/match/history/index.js +++ b/miniprogram/pages/match/history/index.js @@ -1,5 +1,5 @@ -const app = getApp() -const util = require('../../../utils/util') +const app = getApp(); +const util = require("../../../utils/util"); Page({ data: { @@ -7,89 +7,89 @@ Page({ loading: false, page: 1, pageSize: 20, - hasMore: true + hasMore: true, }, onLoad() { - this.fetchMatches() + this.fetchMatches(); }, onShow() { // 门店切换后刷新数据 if (app.globalData.storeChanged) { - app.globalData.storeChanged = false - this.setData({ page: 1, hasMore: true, matches: [] }) - this.fetchMatches() + app.globalData.storeChanged = false; + this.setData({ page: 1, hasMore: true, matches: [] }); + this.fetchMatches(); } }, onPullDownRefresh() { - this.setData({ page: 1, hasMore: true }) + this.setData({ page: 1, hasMore: true }); this.fetchMatches().then(() => { - wx.stopPullDownRefresh() - }) + wx.stopPullDownRefresh(); + }); }, onReachBottom() { if (this.data.hasMore && !this.data.loading) { - this.loadMore() + this.loadMore(); } }, async fetchMatches() { - const currentStore = app.globalData.currentStore - if (!currentStore?.storeId) { - return + const currentStore = app.globalData.currentStore; + if (!currentStore || !currentStore.storeId) { + return; } - this.setData({ loading: true }) + this.setData({ loading: true }); try { - const res = await app.request('/api/match/my-matches', { + const res = await app.request("/api/match/my-matches", { store_id: currentStore.storeId, page: this.data.page, - pageSize: this.data.pageSize - }) + pageSize: this.data.pageSize, + }); - const matches = (res.data.list || []).map(match => { + const matches = (res.data.list || []).map((match) => { // 确保 powerChange 是数字类型,移除可能存在的加号和其他非数字字符 - let powerChange = match.powerChange + let powerChange = match.powerChange; if (powerChange != null && powerChange !== undefined) { // 如果是字符串,移除所有加号、空格等非数字字符(保留负号) - if (typeof powerChange === 'string') { + if (typeof powerChange === "string") { // 保留负号,移除所有加号和其他字符 - const cleaned = powerChange.replace(/\+/g, '').trim() - powerChange = parseFloat(cleaned) || 0 + const cleaned = powerChange.replace(/\+/g, "").trim(); + powerChange = parseFloat(cleaned) || 0; } // 确保是数字类型 - powerChange = Number(powerChange) + powerChange = Number(powerChange); // 如果是 NaN,设为 0 if (isNaN(powerChange)) { - powerChange = 0 + powerChange = 0; } } else { - powerChange = 0 + powerChange = 0; } - return { - ...match, + return Object.assign({}, match, { powerChange: powerChange, - confirmedAt: util.formatDate(match.confirmedAt) - } - }) + confirmedAt: util.formatDate(match.confirmedAt), + }); + }); this.setData({ - matches: this.data.page === 1 ? matches : [...this.data.matches, ...matches], - hasMore: matches.length >= this.data.pageSize - }) + matches: + this.data.page === 1 ? matches : this.data.matches.concat(matches), + hasMore: matches.length >= this.data.pageSize, + }); } catch (e) { - console.error('获取比赛记录失败:', e) + console.error("获取比赛记录失败:", e); } finally { - this.setData({ loading: false }) + this.setData({ loading: false }); } }, loadMore() { - this.setData({ page: this.data.page + 1 }) - this.fetchMatches() - } -}) + this.setData({ page: this.data.page + 1 }); + this.fetchMatches(); + }, +}); diff --git a/miniprogram/pages/match/history/index.wxss b/miniprogram/pages/match/history/index.wxss index ba5a4d79..7460e4e1 100644 --- a/miniprogram/pages/match/history/index.wxss +++ b/miniprogram/pages/match/history/index.wxss @@ -4,7 +4,7 @@ .container { min-height: 100vh; - background: #f5f5f5; + background: var(--bg-page); padding: 20rpx; } @@ -15,10 +15,10 @@ } .match-item { - background: #fff; + background: var(--bg-card); border-radius: 20rpx; overflow: hidden; - box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.08); + box-shadow: var(--shadow-card); transition: all 0.3s ease; } @@ -32,7 +32,7 @@ justify-content: space-between; align-items: center; padding: 24rpx 28rpx; - background: linear-gradient(135deg, #ff6b35 0%, #ff8c42 100%); + background: var(--primary-gradient); border-bottom: 2rpx solid rgba(255, 255, 255, 0.2); } @@ -74,13 +74,13 @@ } .result.win { - background: linear-gradient(135deg, #4caf50 0%, #66bb6a 100%); + background: linear-gradient(135deg, var(--success) 0%, #34d399 100%); color: #fff; box-shadow: 0 4rpx 12rpx rgba(76, 175, 80, 0.3); } .result.lose { - background: linear-gradient(135deg, #f44336 0%, #ef5350 100%); + background: linear-gradient(135deg, var(--danger) 0%, #f87171 100%); color: #fff; box-shadow: 0 4rpx 12rpx rgba(244, 67, 54, 0.3); } @@ -94,14 +94,14 @@ .opponent { font-size: 28rpx; - color: #666; + color: var(--text-secondary); font-weight: 500; } .score { font-size: 40rpx; font-weight: 700; - color: #333; + color: var(--text-primary); letter-spacing: 4rpx; } @@ -126,13 +126,13 @@ .match-footer { padding: 20rpx 28rpx; - background: #fafafa; - border-top: 1rpx solid #f0f0f0; + background: var(--bg-card-hover); + border-top: 1rpx solid var(--border-soft); } .match-time { font-size: 24rpx; - color: #999; + color: var(--text-muted); } .empty-state { diff --git a/miniprogram/pages/match/ranking/index.wxml b/miniprogram/pages/match/ranking/index.wxml index 46c089c9..e65546b7 100644 --- a/miniprogram/pages/match/ranking/index.wxml +++ b/miniprogram/pages/match/ranking/index.wxml @@ -9,7 +9,9 @@ - 🏆 + + + {{match.name || '排位赛'}} @@ -42,15 +44,15 @@ - 👤 + 我的状态 - 🎾 比赛中 - ✅ 已完成 - ⏳ 等待匹配 + 比赛中 + 已完成 + 等待匹配 @@ -86,7 +88,7 @@ - 👥 + 参赛选手 {{match.players.length || 0}}人 @@ -112,7 +114,7 @@ - 🏸 + 暂无参赛选手 diff --git a/miniprogram/pages/match/ranking/index.wxss b/miniprogram/pages/match/ranking/index.wxss index 7416db0f..cc4b4e61 100644 --- a/miniprogram/pages/match/ranking/index.wxss +++ b/miniprogram/pages/match/ranking/index.wxss @@ -4,7 +4,12 @@ .page-container { min-height: 100vh; - background: linear-gradient(180deg, #FEF7F3 0%, #FAFAFA 30%, #F5F5F5 100%); + background: linear-gradient( + 180deg, + var(--primary-soft) 0%, + var(--bg-page) 30%, + var(--bg-soft) 100% + ); position: relative; overflow: hidden; } @@ -42,7 +47,9 @@ } @keyframes spin { - to { transform: translateX(-50%) rotate(360deg); } + to { + transform: translateX(-50%) rotate(360deg); + } } /* 主要内容 */ @@ -61,15 +68,22 @@ } .match-badge { - font-size: 64rpx; + width: 72rpx; + height: 72rpx; + margin: 0 auto; margin-bottom: 16rpx; } +.match-badge-img { + width: 72rpx; + height: 72rpx; +} + .match-title { display: block; font-size: 48rpx; font-weight: 700; - color: #1a1a1a; + color: var(--text-primary); margin-bottom: 16rpx; letter-spacing: 1rpx; } @@ -83,14 +97,14 @@ border-radius: 50rpx; font-size: 26rpx; font-weight: 600; - color: #666; + color: var(--text-secondary); } .status-dot { width: 12rpx; height: 12rpx; border-radius: 50%; - background: #ff6b35; + background: var(--primary); } .match-status.status-1 .status-dot { @@ -98,17 +112,24 @@ } @keyframes pulse { - 0%, 100% { opacity: 1; transform: scale(1); } - 50% { opacity: 0.5; transform: scale(1.2); } + 0%, + 100% { + opacity: 1; + transform: scale(1); + } + 50% { + opacity: 0.5; + transform: scale(1.2); + } } /* 信息卡片 */ .info-card { - background: #fff; + background: var(--bg-card); border-radius: 28rpx; padding: 28rpx; margin-bottom: 20rpx; - box-shadow: 0 8rpx 32rpx rgba(0, 0, 0, 0.06); + box-shadow: var(--shadow-card); } .info-grid { @@ -131,7 +152,7 @@ } .info-value.accent { - color: #FF9800; + color: var(--warning); } .info-label { @@ -149,10 +170,22 @@ margin-bottom: 8rpx; } -.stage-tag.stage-0 { background: #E3F2FD; color: #1565C0; } -.stage-tag.stage-1 { background: #E8F5E9; color: #2E7D32; } -.stage-tag.stage-2 { background: #FFF3E0; color: #E65100; } -.stage-tag.stage-3 { background: #ECEFF1; color: #546E7A; } +.stage-tag.stage-0 { + background: #e3f2fd; + color: #1565c0; +} +.stage-tag.stage-1 { + background: #e8f5e9; + color: #2e7d32; +} +.stage-tag.stage-2 { + background: #fff3e0; + color: #e65100; +} +.stage-tag.stage-3 { + background: #eceff1; + color: #546e7a; +} /* 我的状态卡片 */ .my-status-card { @@ -168,12 +201,13 @@ align-items: center; gap: 12rpx; padding: 20rpx 24rpx; - background: linear-gradient(90deg, #FFF8E1, #FFFFFF); + background: linear-gradient(90deg, #fff8e1, #ffffff); border-bottom: 1rpx solid rgba(255, 152, 0, 0.1); } .card-icon { - font-size: 28rpx; + width: 28rpx; + height: 28rpx; } .card-title { @@ -209,18 +243,18 @@ } .status-badge.waiting { - background: linear-gradient(135deg, #E3F2FD, #BBDEFB); - color: #1565C0; + background: linear-gradient(135deg, #e3f2fd, #bbdefb); + color: #1565c0; } .status-badge.playing { - background: linear-gradient(135deg, #E8F5E9, #C8E6C9); - color: #2E7D32; + background: linear-gradient(135deg, #e8f5e9, #c8e6c9); + color: #2e7d32; } .status-badge.finished { - background: linear-gradient(135deg, #ECEFF1, #CFD8DC); - color: #546E7A; + background: linear-gradient(135deg, #eceff1, #cfd8dc); + color: #546e7a; } .win-lose-stats { @@ -240,11 +274,11 @@ } .stat.win .stat-num { - color: #2E7D32; + color: #2e7d32; } .stat.lose .stat-num { - color: #C62828; + color: #c62828; } .stat-text { @@ -265,10 +299,15 @@ .game-divider::before, .game-divider::after { - content: ''; + content: ""; flex: 1; height: 1rpx; - background: linear-gradient(90deg, transparent, rgba(0,0,0,0.08), transparent); + background: linear-gradient( + 90deg, + transparent, + rgba(0, 0, 0, 0.08), + transparent + ); } .divider-text { @@ -282,7 +321,7 @@ align-items: center; gap: 16rpx; padding: 18rpx; - background: linear-gradient(135deg, #FFF8E1, #FFFDE7); + background: linear-gradient(135deg, #fff8e1, #fffde7); border-radius: 16rpx; border: 2rpx solid rgba(255, 152, 0, 0.15); } @@ -291,7 +330,7 @@ width: 80rpx; height: 80rpx; border-radius: 50%; - border: 3rpx solid #FFE082; + border: 3rpx solid #ffe082; } .opponent-info { @@ -319,11 +358,26 @@ font-weight: 600; } -.level-tag.lv1 { background: linear-gradient(135deg, #81C784, #66BB6A); color: #fff; } -.level-tag.lv2 { background: linear-gradient(135deg, #64B5F6, #42A5F5); color: #fff; } -.level-tag.lv3 { background: linear-gradient(135deg, #FFB74D, #FFA726); color: #fff; } -.level-tag.lv4 { background: linear-gradient(135deg, #F06292, #EC407A); color: #fff; } -.level-tag.lv5 { background: linear-gradient(135deg, #BA68C8, #AB47BC); color: #fff; } +.level-tag.lv1 { + background: linear-gradient(135deg, #81c784, #66bb6a); + color: #fff; +} +.level-tag.lv2 { + background: linear-gradient(135deg, #64b5f6, #42a5f5); + color: #fff; +} +.level-tag.lv3 { + background: linear-gradient(135deg, #ffb74d, #ffa726); + color: #fff; +} +.level-tag.lv4 { + background: linear-gradient(135deg, #f06292, #ec407a); + color: #fff; +} +.level-tag.lv5 { + background: linear-gradient(135deg, #ba68c8, #ab47bc); + color: #fff; +} .opponent-power { font-size: 24rpx; @@ -347,7 +401,7 @@ align-items: center; gap: 14rpx; padding: 16rpx 18rpx; - background: linear-gradient(135deg, #FAFAFA, #F5F5F5); + background: linear-gradient(135deg, #fafafa, #f5f5f5); border-radius: 16rpx; margin-bottom: 12rpx; transition: all 0.2s; @@ -358,7 +412,7 @@ } .player-item.is-me { - background: linear-gradient(135deg, #FFF8E1, #FFFDE7); + background: linear-gradient(135deg, #fff8e1, #fffde7); border: 2rpx solid rgba(255, 152, 0, 0.2); } @@ -380,17 +434,17 @@ } .player-rank.rank-1 { - background: linear-gradient(135deg, #FFD54F, #FFB300); + background: linear-gradient(135deg, #ffd54f, #ffb300); color: #fff; } .player-rank.rank-2 { - background: linear-gradient(135deg, #E0E0E0, #BDBDBD); + background: linear-gradient(135deg, #e0e0e0, #bdbdbd); color: #fff; } .player-rank.rank-3 { - background: linear-gradient(135deg, #FFCC80, #FF9800); + background: linear-gradient(135deg, #ffcc80, #ff9800); color: #fff; } @@ -418,7 +472,7 @@ .player-me { padding: 2rpx 10rpx; - background: linear-gradient(135deg, #FF8A65, #FF6B35); + background: linear-gradient(135deg, #ff8a65, #ff6b35); color: #fff; font-size: 18rpx; font-weight: 600; @@ -432,12 +486,12 @@ } .record-win { - color: #2E7D32; + color: #2e7d32; font-weight: 600; } .record-lose { - color: #C62828; + color: #c62828; font-weight: 600; } @@ -445,20 +499,20 @@ width: 12rpx; height: 12rpx; border-radius: 50%; - background: #BDBDBD; + background: #bdbdbd; } .player-status-dot.waiting { - background: #1565C0; + background: #1565c0; } .player-status-dot.playing { - background: #2E7D32; + background: #2e7d32; animation: pulse 1.5s infinite; } .player-status-dot.finished { - background: #9E9E9E; + background: #9e9e9e; } /* 空状态 */ @@ -470,7 +524,8 @@ } .empty-icon { - font-size: 80rpx; + width: 160rpx; + height: 160rpx; margin-bottom: 16rpx; opacity: 0.5; } @@ -495,4 +550,3 @@ .animate-fadeInUp { animation: fadeInUp 0.5s ease-out forwards; } - diff --git a/miniprogram/pages/player/index.js b/miniprogram/pages/player/index.js new file mode 100644 index 00000000..7df88590 --- /dev/null +++ b/miniprogram/pages/player/index.js @@ -0,0 +1,68 @@ +const app = getApp() + +Page({ + data: { + playerId: null, + player: null, + matches: [], + loadingMatches: false + }, + + onLoad(options) { + const playerId = options && options.id ? String(options.id) : null + this.setData({ playerId }) + + const eventChannel = this.getOpenerEventChannel ? this.getOpenerEventChannel() : null + if (eventChannel) { + eventChannel.on('player', (player) => { + if (player) this.setData({ player }) + }) + } + + this.refresh() + }, + + async onPullDownRefresh() { + try { + await this.refresh() + } finally { + wx.stopPullDownRefresh() + } + }, + + async refresh() { + await Promise.all([this.fetchPlayer(), this.fetchMatches()]) + }, + + async fetchPlayer() { + if (!this.data.playerId) return + try { + const res = await app.request('/api/ladder/player', { id: this.data.playerId }) + if (res && res.data) this.setData({ player: res.data }) + } catch (e) { + } + }, + + async fetchMatches() { + if (!this.data.playerId) return + this.setData({ loadingMatches: true }) + try { + const res = await app.request('/api/match/history', { player_id: this.data.playerId }) + const list = Array.isArray(res && res.data) ? res.data : (res && res.data && res.data.list) || [] + const matches = list.map((item) => { + return Object.assign({}, item, { + timeText: item.timeText || item.createTime || item.matchTime || '', + resultClass: item.resultClass || (item.result === 'win' ? 'win' : item.result === 'lose' ? 'lose' : ''), + resultText: + item.resultText || + (item.result === 'win' ? '胜' : item.result === 'lose' ? '负' : item.resultName || '') + }) + }) + this.setData({ matches }) + } catch (e) { + this.setData({ matches: [] }) + } finally { + this.setData({ loadingMatches: false }) + } + } +}) diff --git a/miniprogram/pages/player/index.json b/miniprogram/pages/player/index.json new file mode 100644 index 00000000..c6a72d70 --- /dev/null +++ b/miniprogram/pages/player/index.json @@ -0,0 +1,5 @@ +{ + "navigationBarTitleText": "选手资料", + "enablePullDownRefresh": true, + "backgroundTextStyle": "dark" +} diff --git a/miniprogram/pages/player/index.wxml b/miniprogram/pages/player/index.wxml new file mode 100644 index 00000000..26932662 --- /dev/null +++ b/miniprogram/pages/player/index.wxml @@ -0,0 +1,58 @@ + + + + + + + {{player.realName || player.nickname || '未命名'}} + Lv{{player.level || 1}} + + + 战力 {{player.powerScore || 0}} + · + 胜率 {{player.winRate || 0}}% + + + + + + {{player.matchCount || 0}} + 总场次 + + + {{player.winCount || 0}} + 胜场 + + + {{player.loseCount || 0}} + 负场 + + + + + + + 近期比赛 + 加载中 + + + + + + {{item.name || item.typeName || '比赛'}} + {{item.timeText || item.createTime || ''}} + + + {{item.desc || ''}} + {{item.resultText || ''}} + + + + + + + 暂无比赛记录 + 完成比赛后会在这里展示 + + + diff --git a/miniprogram/pages/player/index.wxss b/miniprogram/pages/player/index.wxss new file mode 100644 index 00000000..70d8615b --- /dev/null +++ b/miniprogram/pages/player/index.wxss @@ -0,0 +1,209 @@ +.page-container { + min-height: 100vh; + background: var(--bg-page); + padding: 24rpx; +} + +.profile-card { + padding: 28rpx; +} + +.profile-top { + display: flex; + gap: 20rpx; + align-items: center; +} + +.avatar { + width: 120rpx; + height: 120rpx; + border-radius: 50%; + border: 4rpx solid var(--bg-white); + box-shadow: var(--shadow-sm); + background: var(--bg-soft); + flex-shrink: 0; +} + +.profile-main { + flex: 1; + overflow: hidden; +} + +.name-row { + display: flex; + align-items: center; + gap: 16rpx; + margin-bottom: 10rpx; +} + +.name { + font-size: 36rpx; + font-weight: 700; + color: var(--text-primary); + max-width: 360rpx; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.level-pill { + padding: 6rpx 14rpx; + border-radius: var(--radius-full); + font-size: 22rpx; + font-weight: 700; + background: var(--primary-soft); + color: var(--primary-dark); + border: 1rpx solid var(--border-primary); +} + +.meta-row { + display: flex; + align-items: center; + gap: 10rpx; + color: var(--text-muted); + font-size: 24rpx; +} + +.meta-divider { + opacity: 0.8; +} + +.profile-stats { + margin-top: 24rpx; + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 12rpx; + padding-top: 24rpx; + border-top: 1rpx solid var(--border-soft); +} + +.stat { + text-align: center; + background: var(--bg-soft); + border-radius: var(--radius-md); + padding: 18rpx 12rpx; +} + +.stat-value { + display: block; + font-size: 32rpx; + font-weight: 800; + color: var(--text-primary); + line-height: 1.2; + margin-bottom: 6rpx; +} + +.stat-label { + display: block; + font-size: 22rpx; + color: var(--text-muted); +} + +.section-card { + padding: 28rpx; +} + +.section-header { + display: flex; + justify-content: space-between; + align-items: baseline; + margin-bottom: 18rpx; +} + +.section-title { + font-size: 30rpx; + font-weight: 700; + color: var(--text-primary); +} + +.section-sub { + font-size: 24rpx; + color: var(--text-muted); +} + +.match-list { + display: flex; + flex-direction: column; + gap: 16rpx; +} + +.match-item { + padding: 18rpx 20rpx; + border-radius: var(--radius-md); + background: var(--bg-white); + border: 1rpx solid var(--border-soft); +} + +.match-row { + display: flex; + justify-content: space-between; + align-items: center; + gap: 16rpx; +} + +.match-row + .match-row { + margin-top: 8rpx; +} + +.match-name { + font-size: 26rpx; + color: var(--text-primary); + font-weight: 600; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.match-time { + font-size: 22rpx; + color: var(--text-muted); + flex-shrink: 0; +} + +.match-desc { + font-size: 24rpx; + color: var(--text-secondary); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.match-result { + font-size: 24rpx; + font-weight: 700; + color: var(--text-muted); + flex-shrink: 0; +} + +.match-result.win { + color: var(--success-text); +} + +.match-result.lose { + color: var(--danger-text); +} + +.empty-state { + display: flex; + flex-direction: column; + align-items: center; + padding: 60rpx 24rpx 20rpx; +} + +.empty-icon { + width: 160rpx; + height: 160rpx; + opacity: 0.75; + margin-bottom: 18rpx; +} + +.empty-title { + font-size: 28rpx; + color: var(--text-secondary); + margin-bottom: 8rpx; +} + +.empty-desc { + font-size: 24rpx; + color: var(--text-muted); +} diff --git a/miniprogram/pages/points/mall/index.js b/miniprogram/pages/points/mall/index.js index a1933f26..397deedc 100644 --- a/miniprogram/pages/points/mall/index.js +++ b/miniprogram/pages/points/mall/index.js @@ -1,4 +1,4 @@ -const app = getApp() +const app = getApp(); Page({ data: { @@ -10,150 +10,156 @@ Page({ pageSize: 20, hasMore: true, showProductModal: false, - currentProduct: null + currentProduct: null, }, onLoad() { - this.initData() + this.initData(); }, onShow() { - this.setData({ + this.setData({ userInfo: app.globalData.userInfo, - currentStore: app.globalData.currentStore - }) - + currentStore: app.globalData.currentStore, + }); + // 门店切换后刷新商品 if (app.globalData.storeChanged) { - app.globalData.storeChanged = false - this.setData({ page: 1, hasMore: true, products: [] }) - this.fetchProducts() + app.globalData.storeChanged = false; + this.setData({ page: 1, hasMore: true, products: [] }); + this.fetchProducts(); } }, onPullDownRefresh() { - this.setData({ page: 1, hasMore: true }) + this.setData({ page: 1, hasMore: true }); this.fetchProducts().then(() => { - wx.stopPullDownRefresh() - }) + wx.stopPullDownRefresh(); + }); }, onReachBottom() { if (this.data.hasMore && !this.data.loading) { - this.loadMore() + this.loadMore(); } }, async initData() { if (!app.globalData.token) { try { - await app.login() + await app.login(); } catch (e) { - console.error('登录失败:', e) + console.error("登录失败:", e); } } - this.setData({ + this.setData({ userInfo: app.globalData.userInfo, - currentStore: app.globalData.currentStore - }) - this.fetchProducts() + currentStore: app.globalData.currentStore, + }); + this.fetchProducts(); }, async fetchProducts() { - this.setData({ loading: true }) + this.setData({ loading: true }); try { const params = { page: this.data.page, - pageSize: this.data.pageSize - } - + pageSize: this.data.pageSize, + }; + // 根据当前门店筛选商品 - if (this.data.currentStore?.storeId) { - params.store_id = this.data.currentStore.storeId + if (this.data.currentStore && this.data.currentStore.storeId) { + params.store_id = this.data.currentStore.storeId; } - const res = await app.request('/api/points/products', params) + const res = await app.request("/api/points/products", params); - const products = res.data.list || [] + const products = res.data.list || []; this.setData({ - products: this.data.page === 1 ? products : [...this.data.products, ...products], - hasMore: products.length >= this.data.pageSize - }) + products: + this.data.page === 1 ? products : this.data.products.concat(products), + hasMore: products.length >= this.data.pageSize, + }); } catch (e) { - console.error('获取商品列表失败:', e) + console.error("获取商品列表失败:", e); } finally { - this.setData({ loading: false }) + this.setData({ loading: false }); } }, loadMore() { - this.setData({ page: this.data.page + 1 }) - this.fetchProducts() + this.setData({ page: this.data.page + 1 }); + this.fetchProducts(); }, viewProduct(e) { - const product = e.currentTarget.dataset.product + const product = e.currentTarget.dataset.product; this.setData({ currentProduct: product, - showProductModal: true - }) + showProductModal: true, + }); }, closeProductModal() { - this.setData({ showProductModal: false }) + this.setData({ showProductModal: false }); }, async exchangeProduct() { - const product = this.data.currentProduct + const product = this.data.currentProduct; wx.showModal({ - title: '确认兑换', + title: "确认兑换", content: `确定使用 ${product.pointsRequired} 积分兑换「${product.name}」吗?\n请到 ${product.storeName} 领取`, success: async (res) => { - if (!res.confirm) return + if (!res.confirm) return; - wx.showLoading({ title: '兑换中...' }) + wx.showLoading({ title: "兑换中..." }); try { - const exchangeRes = await app.request('/api/points/exchange', { - product_id: product.id - }, 'POST') + const exchangeRes = await app.request( + "/api/points/exchange", + { + product_id: product.id, + }, + "POST", + ); - wx.hideLoading() + wx.hideLoading(); // 更新用户积分 - const newPoints = this.data.userInfo.totalPoints - product.pointsRequired - app.globalData.userInfo.totalPoints = newPoints + const newPoints = + this.data.userInfo.totalPoints - product.pointsRequired; + app.globalData.userInfo.totalPoints = newPoints; this.setData({ - 'userInfo.totalPoints': newPoints, - showProductModal: false - }) + "userInfo.totalPoints": newPoints, + showProductModal: false, + }); wx.showModal({ - title: '兑换成功', + title: "兑换成功", content: `请到 ${product.storeName} 出示兑换码领取\n兑换码: ${exchangeRes.data.exchangeCode}`, showCancel: false, success: () => { - wx.navigateTo({ url: '/pages/points/order/index' }) - } - }) + wx.navigateTo({ url: "/pages/points/order/index" }); + }, + }); - this.fetchProducts() + this.fetchProducts(); } catch (e) { - wx.hideLoading() - console.error('兑换失败:', e) + wx.hideLoading(); + console.error("兑换失败:", e); } - } - }) + }, + }); }, goToRecords() { - wx.navigateTo({ url: '/pages/points/records/index' }) + wx.navigateTo({ url: "/pages/points/records/index" }); }, goToOrders() { - wx.navigateTo({ url: '/pages/points/order/index' }) - } -}) + wx.navigateTo({ url: "/pages/points/order/index" }); + }, +}); diff --git a/miniprogram/pages/points/order/index.js b/miniprogram/pages/points/order/index.js index 93f66625..a71abd06 100644 --- a/miniprogram/pages/points/order/index.js +++ b/miniprogram/pages/points/order/index.js @@ -44,13 +44,14 @@ Page({ const res = await app.request('/api/points/orders', params) - const orders = (res.data.list || []).map(order => ({ - ...order, - createdAt: util.formatDate(order.createdAt) - })) + const orders = (res.data.list || []).map(order => + Object.assign({}, order, { + createdAt: util.formatDate(order.createdAt) + }) + ) this.setData({ - orders: this.data.page === 1 ? orders : [...this.data.orders, ...orders], + orders: this.data.page === 1 ? orders : this.data.orders.concat(orders), hasMore: orders.length >= this.data.pageSize }) } catch (e) { @@ -81,11 +82,10 @@ Page({ wx.hideLoading() - const orderData = { - ...res.data, + const orderData = Object.assign({}, res.data, { createdAt: util.formatDate(res.data.createdAt), qrcodeImage: '' - } + }) this.setData({ currentOrder: orderData, diff --git a/miniprogram/pages/points/records/index.js b/miniprogram/pages/points/records/index.js index 38b952a4..750a7303 100644 --- a/miniprogram/pages/points/records/index.js +++ b/miniprogram/pages/points/records/index.js @@ -45,13 +45,14 @@ Page({ pageSize: this.data.pageSize }) - const records = (res.data.list || []).map(record => ({ - ...record, - createdAt: util.formatDate(record.createdAt) - })) + const records = (res.data.list || []).map(record => + Object.assign({}, record, { + createdAt: util.formatDate(record.createdAt) + }) + ) this.setData({ - records: this.data.page === 1 ? records : [...this.data.records, ...records], + records: this.data.page === 1 ? records : this.data.records.concat(records), hasMore: records.length >= this.data.pageSize }) } catch (e) { diff --git a/miniprogram/pages/store/index.wxml b/miniprogram/pages/store/index.wxml index c8c5362a..392bb257 100644 --- a/miniprogram/pages/store/index.wxml +++ b/miniprogram/pages/store/index.wxml @@ -47,7 +47,7 @@ module.exports = { formatDistance: formatDistance }; - 📍 + 附近门店 共 {{stores.length}} 家 @@ -70,7 +70,7 @@ module.exports = { formatDistance: formatDistance }; {{item.address}} {{util.formatDistance(item.distance)}} - {{item.sportType === 1 ? '🏸 羽毛球' : '🎾 网球'}} + {{item.sportType === 1 ? '羽毛球' : '网球'}} diff --git a/miniprogram/pages/store/index.wxss b/miniprogram/pages/store/index.wxss index ad6ae4c6..3fb1408e 100644 --- a/miniprogram/pages/store/index.wxss +++ b/miniprogram/pages/store/index.wxss @@ -171,7 +171,8 @@ } .section-icon { - font-size: 28rpx; + width: 28rpx; + height: 28rpx; } .section-text { diff --git a/miniprogram/pages/user/index.js b/miniprogram/pages/user/index.js index f7414a61..34b1eb32 100644 --- a/miniprogram/pages/user/index.js +++ b/miniprogram/pages/user/index.js @@ -1,4 +1,4 @@ -const app = getApp() +const app = getApp(); Page({ data: { @@ -6,41 +6,41 @@ Page({ ladderUser: null, currentStore: null, showQrcode: false, - qrcodeImage: '', + qrcodeImage: "", qrcodeLoading: false, // 完善资料弹框 showProfileModal: false, profileForm: { - avatar: '', - nickname: '' + avatar: "", + nickname: "", }, - isEditMode: false // true: 编辑模式,false: 完善模式(登录时) + isEditMode: false, // true: 编辑模式,false: 完善模式(登录时) }, onLoad() { - this.initData() + this.initData(); }, onShow() { // 检查门店是否切换 if (app.globalData.storeChanged) { - app.globalData.storeChanged = false - this.refreshData() + app.globalData.storeChanged = false; + this.refreshData(); } else { // 同步最新数据 this.setData({ userInfo: app.globalData.userInfo, ladderUser: app.globalData.ladderUser, - currentStore: app.globalData.currentStore - }) + currentStore: app.globalData.currentStore, + }); } }, async onPullDownRefresh() { try { - await this.refreshData() + await this.refreshData(); } finally { - wx.stopPullDownRefresh() + wx.stopPullDownRefresh(); } }, @@ -48,79 +48,84 @@ Page({ // 先进行微信登录获取openid if (!app.globalData.wxLoginInfo) { try { - await app.wxLogin() + await app.wxLogin(); } catch (e) { - console.error('微信登录失败:', e) + console.error("微信登录失败:", e); } } if (app.globalData.token) { - await this.refreshData() + await this.refreshData(); } }, async refreshData() { - if (!app.globalData.token) return + if (!app.globalData.token) return; try { - await app.getUserInfo() - + await app.getUserInfo(); + // 如果当前门店有 ladderUserId,确保获取该门店的天梯用户信息 - if (app.globalData.currentStore?.storeId && !app.globalData.ladderUser) { + if ( + app.globalData.currentStore && + app.globalData.currentStore.storeId && + !app.globalData.ladderUser + ) { try { - await app.getLadderUser(app.globalData.currentStore.storeId) + await app.getLadderUser(app.globalData.currentStore.storeId); } catch (e) { - console.error('获取天梯用户信息失败:', e) + console.error("获取天梯用户信息失败:", e); } } - + this.setData({ userInfo: app.globalData.userInfo, ladderUser: app.globalData.ladderUser, - currentStore: app.globalData.currentStore - }) + currentStore: app.globalData.currentStore, + }); } catch (e) { - console.error('获取用户信息失败:', e) + console.error("获取用户信息失败:", e); } }, // 获取手机号授权 async onGetPhoneNumber(e) { - if (e.detail.errMsg !== 'getPhoneNumber:ok') { - wx.showToast({ title: '需要授权手机号才能登录', icon: 'none' }) - return + if (e.detail.errMsg !== "getPhoneNumber:ok") { + wx.showToast({ title: "需要授权手机号才能登录", icon: "none" }); + return; } - wx.showLoading({ title: '登录中...' }) + wx.showLoading({ title: "登录中..." }); try { // 如果没有微信登录信息,先登录 if (!app.globalData.wxLoginInfo) { - await app.wxLogin() + await app.wxLogin(); } // 手机号登录(先不传头像昵称) - await app.phoneLogin(e.detail.encryptedData, e.detail.iv, null) - + await app.phoneLogin(e.detail.encryptedData, e.detail.iv, null); + // 获取门店信息 - await app.getCurrentStore() - - const userInfo = app.globalData.userInfo + await app.getCurrentStore(); + + const userInfo = app.globalData.userInfo; this.setData({ userInfo: userInfo, ladderUser: app.globalData.ladderUser, - currentStore: app.globalData.currentStore - }) + currentStore: app.globalData.currentStore, + }); + + wx.hideLoading(); - wx.hideLoading() - // 检查是否需要完善资料(没有头像或昵称为默认值) - const needProfile = !userInfo.avatar || - userInfo.avatar === '' || - !userInfo.nickname || - userInfo.nickname === '新用户' || - userInfo.nickname === '' + const needProfile = + !userInfo.avatar || + userInfo.avatar === "" || + !userInfo.nickname || + userInfo.nickname === "新用户" || + userInfo.nickname === ""; if (needProfile) { // 弹出完善资料弹框 @@ -128,94 +133,101 @@ Page({ showProfileModal: true, isEditMode: false, profileForm: { - avatar: userInfo.avatar || '/images/avatar-default.svg', - nickname: userInfo.nickname === '新用户' ? '' : (userInfo.nickname || '') - } - }) - wx.showToast({ title: '登录成功,请完善资料', icon: 'none' }) + avatar: userInfo.avatar || "/images/avatar-default.svg", + nickname: + userInfo.nickname === "新用户" ? "" : userInfo.nickname || "", + }, + }); + wx.showToast({ title: "登录成功,请完善资料", icon: "none" }); } else { - wx.showToast({ title: '登录成功', icon: 'success' }) + wx.showToast({ title: "登录成功", icon: "success" }); } } catch (e) { - wx.hideLoading() - console.error('登录失败:', e) - wx.showToast({ title: e.message || '登录失败', icon: 'none' }) + wx.hideLoading(); + console.error("登录失败:", e); + wx.showToast({ title: e.message || "登录失败", icon: "none" }); } }, // 点击头像,打开编辑资料弹框 onTapAvatar() { - if (!this.data.userInfo?.phone) return - + if (!this.data.userInfo || !this.data.userInfo.phone) return; + this.setData({ showProfileModal: true, isEditMode: true, profileForm: { - avatar: this.data.userInfo.avatar || '/images/avatar-default.svg', - nickname: this.data.userInfo.nickname || '' - } - }) + avatar: this.data.userInfo.avatar || "/images/avatar-default.svg", + nickname: this.data.userInfo.nickname || "", + }, + }); }, // 选择头像(新API:button open-type="chooseAvatar") onChooseAvatarNew(e) { - const avatarUrl = e.detail.avatarUrl + const avatarUrl = e.detail.avatarUrl; this.setData({ - 'profileForm.avatar': avatarUrl - }) + "profileForm.avatar": avatarUrl, + }); }, // 输入昵称 onNicknameInput(e) { this.setData({ - 'profileForm.nickname': e.detail.value - }) + "profileForm.nickname": e.detail.value, + }); }, // 确认保存资料 async saveProfile() { - const { avatar, nickname } = this.data.profileForm + const { avatar, nickname } = this.data.profileForm; - if (!nickname || nickname.trim() === '') { - wx.showToast({ title: '请输入昵称', icon: 'none' }) - return + if (!nickname || nickname.trim() === "") { + wx.showToast({ title: "请输入昵称", icon: "none" }); + return; } - wx.showLoading({ title: '保存中...' }) + wx.showLoading({ title: "保存中..." }); try { // 如果选择了新头像,先上传 - let avatarUrl = avatar - if (avatar && (avatar.startsWith('wxfile://') || avatar.startsWith('http://tmp'))) { - avatarUrl = await this.uploadAvatar(avatar) + let avatarUrl = avatar; + if ( + avatar && + (avatar.startsWith("wxfile://") || avatar.startsWith("http://tmp")) + ) { + avatarUrl = await this.uploadAvatar(avatar); } // 调用更新资料接口 - const res = await app.request('/api/user/profile', { - nickname: nickname.trim(), - avatar: avatarUrl - }, 'PUT') + const res = await app.request( + "/api/user/profile", + { + nickname: nickname.trim(), + avatar: avatarUrl, + }, + "PUT", + ); // 更新本地数据(服务端已返回完整URL) - const userInfo = { - ...this.data.userInfo, - nickname: res.data?.nickname || nickname.trim(), - avatar: res.data?.avatar || avatarUrl - } - app.globalData.userInfo = userInfo + const userInfo = Object.assign({}, this.data.userInfo, { + nickname: (res.data && res.data.nickname) || nickname.trim(), + avatar: (res.data && res.data.avatar) || avatarUrl, + }); + app.globalData.userInfo = userInfo; this.setData({ userInfo: userInfo, showProfileModal: false, - profileForm: { avatar: '', nickname: '' } - }) + profileForm: { avatar: "", nickname: "" }, + }); - wx.hideLoading() - wx.showToast({ title: '保存成功', icon: 'success' }) + wx.hideLoading(); + wx.showToast({ title: "保存成功", icon: "success" }); } catch (e) { - wx.hideLoading() - console.error('保存资料失败:', e) - wx.showToast({ title: e.message || '保存失败', icon: 'none' }) + wx.hideLoading(); + console.error("保存资料失败:", e); + wx.showToast({ title: e.message || "保存失败", icon: "none" }); } }, @@ -225,29 +237,29 @@ Page({ wx.uploadFile({ url: `${app.globalData.baseUrl}/api/upload/avatar`, filePath: filePath, - name: 'file', + name: "file", header: { - 'Authorization': `Bearer ${app.globalData.token}` + Authorization: `Bearer ${app.globalData.token}`, }, success: (res) => { try { - const data = JSON.parse(res.data) - if (data.code === 0 && data.data?.url) { - resolve(data.data.url) + const data = JSON.parse(res.data); + if (data.code === 0 && data.data && data.data.url) { + resolve(data.data.url); } else { - console.error('上传头像失败:', data) - resolve(filePath) + console.error("上传头像失败:", data); + resolve(filePath); } } catch (e) { - resolve(filePath) + resolve(filePath); } }, fail: (err) => { - console.error('上传头像失败:', err) - resolve(filePath) - } - }) - }) + console.error("上传头像失败:", err); + resolve(filePath); + }, + }); + }); }, // 关闭资料弹框 @@ -255,63 +267,63 @@ Page({ // 如果是完善模式,提示用户 if (!this.data.isEditMode) { wx.showModal({ - title: '提示', - content: '完善资料后可以让好友更容易找到你,确定跳过?', - confirmText: '跳过', - cancelText: '继续完善', + title: "提示", + content: "完善资料后可以让好友更容易找到你,确定跳过?", + confirmText: "跳过", + cancelText: "继续完善", success: (res) => { if (res.confirm) { - this.setData({ showProfileModal: false }) + this.setData({ showProfileModal: false }); } - } - }) + }, + }); } else { - this.setData({ showProfileModal: false }) + this.setData({ showProfileModal: false }); } }, async showMemberCode() { - if (!this.data.userInfo?.memberCode) return + if (!this.data.userInfo || !this.data.userInfo.memberCode) return; - this.setData({ + this.setData({ showQrcode: true, - qrcodeLoading: true - }) + qrcodeLoading: true, + }); try { // 调用接口获取二维码 - const res = await app.request('/api/user/qrcode') + const res = await app.request("/api/user/qrcode"); if (res.data && res.data.qrcode) { - this.setData({ + this.setData({ qrcodeImage: res.data.qrcode, - qrcodeLoading: false - }) + qrcodeLoading: false, + }); } } catch (e) { - console.error('获取二维码失败:', e) - this.setData({ qrcodeLoading: false }) - wx.showToast({ title: '获取二维码失败', icon: 'none' }) + console.error("获取二维码失败:", e); + this.setData({ qrcodeLoading: false }); + wx.showToast({ title: "获取二维码失败", icon: "none" }); } }, hideQrcode() { - this.setData({ + this.setData({ showQrcode: false, - qrcodeImage: '' - }) + qrcodeImage: "", + }); }, goTo(e) { - const url = e.currentTarget.dataset.url + const url = e.currentTarget.dataset.url; if (!app.globalData.token) { - wx.showToast({ title: '请先登录', icon: 'none' }) - return + wx.showToast({ title: "请先登录", icon: "none" }); + return; } - wx.navigateTo({ url }) + wx.navigateTo({ url }); }, // 阻止事件冒泡 preventBubble() { // 空函数,仅用于阻止事件冒泡 - } -}) + }, +}); diff --git a/miniprogram/pages/user/index.wxml b/miniprogram/pages/user/index.wxml index 7a2e425b..99ce10b6 100644 --- a/miniprogram/pages/user/index.wxml +++ b/miniprogram/pages/user/index.wxml @@ -67,7 +67,7 @@ @@ -117,7 +117,9 @@ - 🏸 + + + 尚未加入天梯系统 请联系门店工作人员,开启你的天梯之旅 @@ -183,7 +185,7 @@ - 📱 + 请出示给对方扫描发起挑战 @@ -199,7 +201,7 @@ - 💡 + 完善资料后,好友可以更容易找到你 @@ -208,7 +210,7 @@ 点击更换头像 diff --git a/miniprogram/pages/user/index.wxss b/miniprogram/pages/user/index.wxss index fd84aaf5..72fd72bf 100644 --- a/miniprogram/pages/user/index.wxss +++ b/miniprogram/pages/user/index.wxss @@ -153,7 +153,7 @@ left: 0; right: 0; bottom: 0; - background: linear-gradient(135deg, #FFF8F5 0%, #FFFFFF 50%, #FFF5F0 100%); + background: linear-gradient(135deg, #fff8f5 0%, #ffffff 50%, #fff5f0 100%); } .member-card-content { @@ -233,7 +233,7 @@ .scan-hint { position: relative; padding: 12rpx 24rpx; - background: linear-gradient(90deg, var(--primary-soft), #FFF8F5); + background: linear-gradient(90deg, var(--primary-soft), #fff8f5); text-align: center; font-size: 22rpx; color: var(--primary); @@ -321,8 +321,10 @@ } .login-btn-primary .btn-icon { - font-size: 32rpx; + width: 32rpx; + height: 32rpx; flex-shrink: 0; + display: block; } .login-btn-primary .btn-text { @@ -360,7 +362,7 @@ align-items: center; justify-content: space-between; padding: 20rpx 24rpx; - background: linear-gradient(90deg, #FFF8F5, var(--bg-white)); + background: linear-gradient(90deg, #fff8f5, var(--bg-white)); border-bottom: 1rpx solid var(--border-soft); } @@ -397,11 +399,21 @@ color: #fff; } -.stat-icon.lv1 { background: linear-gradient(135deg, #4CAF50, #8BC34A); } -.stat-icon.lv2 { background: linear-gradient(135deg, #2196F3, #03A9F4); } -.stat-icon.lv3 { background: linear-gradient(135deg, #FF9800, #FFC107); } -.stat-icon.lv4 { background: linear-gradient(135deg, #E91E63, #FF5722); } -.stat-icon.lv5 { background: linear-gradient(135deg, #9C27B0, #673AB7); } +.stat-icon.lv1 { + background: linear-gradient(135deg, #4caf50, #8bc34a); +} +.stat-icon.lv2 { + background: linear-gradient(135deg, #2196f3, #03a9f4); +} +.stat-icon.lv3 { + background: linear-gradient(135deg, #ff9800, #ffc107); +} +.stat-icon.lv4 { + background: linear-gradient(135deg, #e91e63, #ff5722); +} +.stat-icon.lv5 { + background: linear-gradient(135deg, #9c27b0, #673ab7); +} .stat-name { font-size: 30rpx; @@ -447,12 +459,17 @@ .ladder-record { display: flex; - justify-content: space-around; + justify-content: space-between; padding-top: 20rpx; border-top: 1rpx solid var(--border-soft); } .record-item { + flex: 1; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; text-align: center; } @@ -486,14 +503,27 @@ gap: 16rpx; margin: 0 24rpx 20rpx; padding: 24rpx; - background: #FFFBF5; - border: 1rpx solid #FFE8D5; + background: #fffbf5; + border: 1rpx solid #ffe8d5; border-radius: var(--radius-lg); animation: fadeInUp 0.5s cubic-bezier(0.4, 0, 0.2, 1) 0.2s backwards; } .notice-icon { - font-size: 36rpx; + width: 72rpx; + height: 72rpx; + background: var(--primary-gradient-soft); + border-radius: 20rpx; + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; +} + +.notice-icon-img { + width: 40rpx; + height: 40rpx; + display: block; } .notice-content { @@ -504,14 +534,14 @@ display: block; font-size: 26rpx; font-weight: 600; - color: #B7791F; + color: #b7791f; margin-bottom: 6rpx; } .notice-desc { display: block; font-size: 22rpx; - color: #C68A42; + color: #c68a42; line-height: 1.5; } @@ -556,19 +586,19 @@ } .menu-icon.history { - background: linear-gradient(135deg, #E3F2FD, #BBDEFB); + background: linear-gradient(135deg, #e3f2fd, #bbdefb); } .menu-icon.points { - background: linear-gradient(135deg, #FFF8E1, #FFECB3); + background: linear-gradient(135deg, #fff8e1, #ffecb3); } .menu-icon.order { - background: linear-gradient(135deg, #E8F5E9, #C8E6C9); + background: linear-gradient(135deg, #e8f5e9, #c8e6c9); } .menu-icon.store { - background: linear-gradient(135deg, #FFF3E0, #FFE0B2); + background: linear-gradient(135deg, #fff3e0, #ffe0b2); } .menu-icon image { @@ -627,7 +657,7 @@ align-items: center; justify-content: space-between; padding: 24rpx 28rpx; - background: linear-gradient(90deg, #FFF8F5, var(--bg-white)); + background: linear-gradient(90deg, #fff8f5, var(--bg-white)); border-bottom: 1rpx solid var(--border-soft); } @@ -667,7 +697,7 @@ .qrcode-border { width: 320rpx; height: 320rpx; - background: #FFFFFF; + background: #ffffff; border-radius: var(--radius-lg); padding: 16rpx; box-shadow: var(--shadow-md); @@ -723,10 +753,30 @@ border-style: solid; } -.corner.tl { top: 0; left: 0; border-width: 6rpx 0 0 6rpx; border-radius: 10rpx 0 0 0; } -.corner.tr { top: 0; right: 0; border-width: 6rpx 6rpx 0 0; border-radius: 0 10rpx 0 0; } -.corner.bl { bottom: 0; left: 0; border-width: 0 0 6rpx 6rpx; border-radius: 0 0 0 10rpx; } -.corner.br { bottom: 0; right: 0; border-width: 0 6rpx 6rpx 0; border-radius: 0 0 10rpx 0; } +.corner.tl { + top: 0; + left: 0; + border-width: 6rpx 0 0 6rpx; + border-radius: 10rpx 0 0 0; +} +.corner.tr { + top: 0; + right: 0; + border-width: 6rpx 6rpx 0 0; + border-radius: 0 10rpx 0 0; +} +.corner.bl { + bottom: 0; + left: 0; + border-width: 0 0 6rpx 6rpx; + border-radius: 0 0 0 10rpx; +} +.corner.br { + bottom: 0; + right: 0; + border-width: 0 6rpx 6rpx 0; + border-radius: 0 0 10rpx 0; +} .qrcode-info { text-align: center; @@ -762,7 +812,9 @@ } .tip-icon { - font-size: 24rpx; + width: 24rpx; + height: 24rpx; + display: block; } .tip-text { @@ -837,7 +889,7 @@ align-items: center; justify-content: space-between; padding: 28rpx 32rpx; - background: linear-gradient(90deg, #FFF8F5, var(--bg-white)); + background: linear-gradient(90deg, #fff8f5, var(--bg-white)); border-bottom: 1rpx solid var(--border-soft); } @@ -874,19 +926,21 @@ align-items: center; gap: 12rpx; padding: 20rpx 24rpx; - background: #FFFBF5; - border: 1rpx solid #FFE8D5; + background: #fffbf5; + border: 1rpx solid #ffe8d5; border-radius: var(--radius-lg); margin-bottom: 32rpx; } .tips-icon { - font-size: 32rpx; + width: 32rpx; + height: 32rpx; + display: block; } .tips-text { font-size: 24rpx; - color: #B7791F; + color: #b7791f; line-height: 1.4; } @@ -948,8 +1002,9 @@ } .avatar-choose-badge .choose-icon { - font-size: 24rpx; - line-height: 1; + width: 24rpx; + height: 24rpx; + display: block; } .avatar-tip {