feat: 新增选手资料页面并优化界面设计
- 新增选手资料页面,支持查看选手信息及比赛记录 - 添加多个SVG图标替换原有emoji,提升视觉一致性 - 扩展CSS变量系统,增加信息、成功、警告、危险等状态颜色 - 优化多个页面的样式,统一使用CSS变量和现代设计语言 - 修复可选链操作符兼容性问题,改用传统条件判断 - 改进数据加载逻辑,使用Object.assign替代展开运算符 - 调整开发环境配置,使用本地服务器地址
This commit is contained in:
parent
e0713c3fd8
commit
74ed19eee1
@ -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
|
||||
);
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
{
|
||||
"pages": [
|
||||
"pages/index/index",
|
||||
"pages/player/index",
|
||||
"pages/user/index",
|
||||
"pages/match/challenge/index",
|
||||
"pages/match/challenge-detail/index",
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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",
|
||||
};
|
||||
});
|
||||
|
||||
5
miniprogram/images/icon-phone.svg
Normal file
5
miniprogram/images/icon-phone.svg
Normal file
@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="48" height="48">
|
||||
<rect x="14" y="4" width="20" height="40" rx="4" fill="#666666"/>
|
||||
<rect x="16.5" y="8" width="15" height="28" rx="2" fill="#999999"/>
|
||||
<circle cx="24" cy="39.5" r="1.6" fill="#666666"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 282 B |
4
miniprogram/images/icon-shield.svg
Normal file
4
miniprogram/images/icon-shield.svg
Normal file
@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="48" height="48">
|
||||
<path fill="#666666" d="M24 4l16 6v14c0 10.7-6.8 18.8-16 20-9.2-1.2-16-9.3-16-20V10l16-6z"/>
|
||||
<path fill="#999999" d="M24 10l10 3.8V24c0 7-4.3 12.8-10 14-5.7-1.2-10-7-10-14V13.8L24 10z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 282 B |
4
miniprogram/images/icon-user.svg
Normal file
4
miniprogram/images/icon-user.svg
Normal file
@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="48" height="48">
|
||||
<circle cx="24" cy="18" r="8" fill="#666666"/>
|
||||
<path d="M10 44c0-8 6.3-14 14-14s14 6 14 14" fill="#999999"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 204 B |
6
miniprogram/images/icon-users.svg
Normal file
6
miniprogram/images/icon-users.svg
Normal file
@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="48" height="48">
|
||||
<circle cx="18" cy="18" r="7" fill="#666666"/>
|
||||
<circle cx="32" cy="19" r="6" fill="#777777"/>
|
||||
<path d="M6 44c0-7.5 5.8-13.5 13-13.5S32 36.5 32 44" fill="#999999"/>
|
||||
<path d="M24 44c0-6.5 4.8-11.5 11-11.5S46 37.5 46 44" fill="#b0b0b0"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 334 B |
@ -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);
|
||||
}
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
@ -41,14 +41,14 @@
|
||||
bindtap="setGender"
|
||||
data-gender="1"
|
||||
>
|
||||
♂ 男子
|
||||
男子
|
||||
</view>
|
||||
<view
|
||||
class="filter-item {{gender === '2' ? 'active' : ''}}"
|
||||
bindtap="setGender"
|
||||
data-gender="2"
|
||||
>
|
||||
♀ 女子
|
||||
女子
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@ -62,11 +62,11 @@
|
||||
wx:key="id"
|
||||
bindtap="viewPlayer"
|
||||
data-id="{{item.id}}"
|
||||
data-player="{{item}}"
|
||||
>
|
||||
<!-- 排名徽章 -->
|
||||
<view class="rank-badge {{item.rank === 1 ? 'top1' : item.rank === 2 ? 'top2' : item.rank === 3 ? 'top3' : 'normal'}}">
|
||||
<text wx:if="{{item.rank <= 3}}">{{item.rank === 1 ? '👑' : item.rank === 2 ? '🥈' : '🥉'}}</text>
|
||||
<text wx:else>{{item.rank}}</text>
|
||||
<text>{{item.rank}}</text>
|
||||
</view>
|
||||
|
||||
<!-- 选手头像 -->
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
@ -9,20 +9,22 @@
|
||||
<view class="main-content">
|
||||
<!-- 页面标题 -->
|
||||
<view class="page-header">
|
||||
<text class="page-title">🏸 发起挑战</text>
|
||||
<text class="page-title">发起挑战</text>
|
||||
<text class="page-subtitle">扫描对手会员码,开启对决</text>
|
||||
</view>
|
||||
|
||||
<!-- 当前门店 -->
|
||||
<view class="store-bar" wx:if="{{currentStore}}" bindtap="goToStore">
|
||||
<text class="store-icon">📍</text>
|
||||
<image class="store-icon" src="/images/icon-store.svg" mode="aspectFit"></image>
|
||||
<text class="store-name">{{currentStore.storeName}}</text>
|
||||
<text class="store-arrow">›</text>
|
||||
</view>
|
||||
|
||||
<!-- 未登录或非天梯用户提示 -->
|
||||
<view class="notice-card animate-fadeInUp" wx:if="{{!ladderUser}}">
|
||||
<view class="notice-icon">🏸</view>
|
||||
<view class="notice-icon">
|
||||
<image class="notice-icon-img" src="/images/icon-challenge.svg" mode="aspectFit"></image>
|
||||
</view>
|
||||
<view class="notice-content">
|
||||
<text class="notice-title">暂未开通天梯</text>
|
||||
<text class="notice-desc">请联系门店工作人员加入天梯系统</text>
|
||||
@ -67,7 +69,7 @@
|
||||
<view class="scan-grid animate-fadeInUp" style="animation-delay: 0.1s">
|
||||
<view class="scan-card challenge" bindtap="startChallenge">
|
||||
<view class="scan-icon-wrapper">
|
||||
<text class="scan-icon">⚔️</text>
|
||||
<image class="scan-icon-img" src="/images/icon-challenge.svg" mode="aspectFit"></image>
|
||||
</view>
|
||||
<text class="scan-title">挑战赛</text>
|
||||
<text class="scan-desc">1v1 对决</text>
|
||||
@ -76,7 +78,7 @@
|
||||
|
||||
<view class="scan-card ranking" bindtap="joinRankingMatch">
|
||||
<view class="scan-icon-wrapper">
|
||||
<text class="scan-icon">🏆</text>
|
||||
<image class="scan-icon-img" src="/images/icon-ranking.svg" mode="aspectFit"></image>
|
||||
</view>
|
||||
<text class="scan-title">排位赛</text>
|
||||
<text class="scan-desc">多人竞技</text>
|
||||
@ -88,7 +90,7 @@
|
||||
<view class="ongoing-card animate-fadeInUp" style="animation-delay: 0.12s" wx:if="{{ongoingMatches.length > 0}}">
|
||||
<view class="ongoing-header">
|
||||
<view class="ongoing-header-left">
|
||||
<text class="ongoing-icon">🔥</text>
|
||||
<image class="ongoing-icon-img" src="/images/icon-history.svg" mode="aspectFit"></image>
|
||||
<text class="ongoing-title">进行中的比赛</text>
|
||||
</view>
|
||||
<view class="ongoing-count">{{ongoingMatches.length}}</view>
|
||||
@ -134,9 +136,9 @@
|
||||
<text class="current-name">{{item.opponent.realName}}</text>
|
||||
</view>
|
||||
<view class="my-status {{item.myStatus}}">
|
||||
<text wx:if="{{item.myStatus === 'waiting'}}">⏳ 等待中</text>
|
||||
<text wx:elif="{{item.myStatus === 'playing'}}">🎾 比赛中</text>
|
||||
<text wx:else>✅ 已完成</text>
|
||||
<text wx:if="{{item.myStatus === 'waiting'}}">等待中</text>
|
||||
<text wx:elif="{{item.myStatus === 'playing'}}">比赛中</text>
|
||||
<text wx:else>已完成</text>
|
||||
</view>
|
||||
</view>
|
||||
</block>
|
||||
@ -152,7 +154,7 @@
|
||||
<!-- 待确认比赛 -->
|
||||
<view class="pending-card animate-fadeInUp" style="animation-delay: 0.15s" wx:if="{{pendingGames.length > 0}}">
|
||||
<view class="pending-header">
|
||||
<text class="pending-title">📋 待确认比分</text>
|
||||
<text class="pending-title">待确认比分</text>
|
||||
<view class="pending-count">{{pendingGames.length}}</view>
|
||||
</view>
|
||||
<view class="pending-list">
|
||||
@ -171,7 +173,9 @@
|
||||
<!-- 战力值规则 -->
|
||||
<view class="rules-card animate-fadeInUp" style="animation-delay: 0.2s">
|
||||
<view class="rules-header">
|
||||
<view class="rules-icon">📖</view>
|
||||
<view class="rules-icon">
|
||||
<image class="rules-icon-img" src="/images/icon-info.svg" mode="aspectFit"></image>
|
||||
</view>
|
||||
<text class="rules-title">战力值规则</text>
|
||||
</view>
|
||||
<view class="rules-grid">
|
||||
@ -197,7 +201,9 @@
|
||||
</view>
|
||||
</view>
|
||||
<view class="rule-item">
|
||||
<view class="rule-icon shield">🛡</view>
|
||||
<view class="rule-icon shield">
|
||||
<image class="rule-icon-img" src="/images/icon-shield.svg" mode="aspectFit"></image>
|
||||
</view>
|
||||
<view class="rule-text">
|
||||
<text class="rule-label">新手保护</text>
|
||||
<text class="rule-value">输分减半</text>
|
||||
@ -205,7 +211,7 @@
|
||||
</view>
|
||||
</view>
|
||||
<view class="rules-note">
|
||||
💡 同一对手30天内仅限挑战1次
|
||||
提示:同一对手30天内仅限挑战1次
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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();
|
||||
},
|
||||
});
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -9,7 +9,9 @@
|
||||
<view class="main-content">
|
||||
<!-- 比赛头部信息 -->
|
||||
<view class="match-header animate-fadeInUp">
|
||||
<view class="match-badge">🏆</view>
|
||||
<view class="match-badge">
|
||||
<image class="match-badge-img" src="/images/icon-ranking.svg" mode="aspectFit"></image>
|
||||
</view>
|
||||
<view class="match-title">{{match.name || '排位赛'}}</view>
|
||||
<view class="match-status status-{{match.status}}">
|
||||
<text class="status-dot"></text>
|
||||
@ -42,15 +44,15 @@
|
||||
<!-- 我的状态 -->
|
||||
<view class="my-status-card animate-fadeInUp" style="animation-delay: 0.1s" wx:if="{{myPlayer}}">
|
||||
<view class="card-header">
|
||||
<text class="card-icon">👤</text>
|
||||
<image class="card-icon" src="/images/icon-user.svg" mode="aspectFit"></image>
|
||||
<text class="card-title">我的状态</text>
|
||||
</view>
|
||||
<view class="status-content">
|
||||
<view class="status-main">
|
||||
<view class="status-badge {{myPlayer.status}}">
|
||||
<text wx:if="{{myPlayer.status === 'playing'}}">🎾 比赛中</text>
|
||||
<text wx:elif="{{myPlayer.status === 'finished'}}">✅ 已完成</text>
|
||||
<text wx:else>⏳ 等待匹配</text>
|
||||
<text wx:if="{{myPlayer.status === 'playing'}}">比赛中</text>
|
||||
<text wx:elif="{{myPlayer.status === 'finished'}}">已完成</text>
|
||||
<text wx:else>等待匹配</text>
|
||||
</view>
|
||||
<view class="win-lose-stats">
|
||||
<view class="stat win">
|
||||
@ -86,7 +88,7 @@
|
||||
<!-- 参赛选手 -->
|
||||
<view class="players-card animate-fadeInUp" style="animation-delay: 0.15s">
|
||||
<view class="card-header">
|
||||
<text class="card-icon">👥</text>
|
||||
<image class="card-icon" src="/images/icon-users.svg" mode="aspectFit"></image>
|
||||
<text class="card-title">参赛选手</text>
|
||||
<text class="player-count">{{match.players.length || 0}}人</text>
|
||||
</view>
|
||||
@ -112,7 +114,7 @@
|
||||
</view>
|
||||
|
||||
<view class="empty-players" wx:else>
|
||||
<text class="empty-icon">🏸</text>
|
||||
<image class="empty-icon" src="/images/empty-match.svg" mode="aspectFit"></image>
|
||||
<text class="empty-text">暂无参赛选手</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
68
miniprogram/pages/player/index.js
Normal file
68
miniprogram/pages/player/index.js
Normal file
@ -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 })
|
||||
}
|
||||
}
|
||||
})
|
||||
5
miniprogram/pages/player/index.json
Normal file
5
miniprogram/pages/player/index.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"navigationBarTitleText": "选手资料",
|
||||
"enablePullDownRefresh": true,
|
||||
"backgroundTextStyle": "dark"
|
||||
}
|
||||
58
miniprogram/pages/player/index.wxml
Normal file
58
miniprogram/pages/player/index.wxml
Normal file
@ -0,0 +1,58 @@
|
||||
<view class="page-container">
|
||||
<view class="card profile-card" wx:if="{{player}}">
|
||||
<view class="profile-top">
|
||||
<image class="avatar" src="{{player.avatar || '/images/avatar-default.svg'}}" mode="aspectFill"></image>
|
||||
<view class="profile-main">
|
||||
<view class="name-row">
|
||||
<text class="name">{{player.realName || player.nickname || '未命名'}}</text>
|
||||
<view class="level-pill">Lv{{player.level || 1}}</view>
|
||||
</view>
|
||||
<view class="meta-row">
|
||||
<text class="meta">战力 {{player.powerScore || 0}}</text>
|
||||
<text class="meta-divider">·</text>
|
||||
<text class="meta">胜率 {{player.winRate || 0}}%</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="profile-stats">
|
||||
<view class="stat">
|
||||
<text class="stat-value">{{player.matchCount || 0}}</text>
|
||||
<text class="stat-label">总场次</text>
|
||||
</view>
|
||||
<view class="stat">
|
||||
<text class="stat-value">{{player.winCount || 0}}</text>
|
||||
<text class="stat-label">胜场</text>
|
||||
</view>
|
||||
<view class="stat">
|
||||
<text class="stat-value">{{player.loseCount || 0}}</text>
|
||||
<text class="stat-label">负场</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="card section-card">
|
||||
<view class="section-header">
|
||||
<text class="section-title">近期比赛</text>
|
||||
<text class="section-sub" wx:if="{{loadingMatches}}">加载中</text>
|
||||
</view>
|
||||
|
||||
<view class="match-list" wx:if="{{matches.length > 0}}">
|
||||
<view class="match-item" wx:for="{{matches}}" wx:key="id">
|
||||
<view class="match-row">
|
||||
<text class="match-name">{{item.name || item.typeName || '比赛'}}</text>
|
||||
<text class="match-time">{{item.timeText || item.createTime || ''}}</text>
|
||||
</view>
|
||||
<view class="match-row">
|
||||
<text class="match-desc">{{item.desc || ''}}</text>
|
||||
<text class="match-result {{item.resultClass || ''}}">{{item.resultText || ''}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="empty-state" wx:else>
|
||||
<image class="empty-icon" src="/images/empty-records.svg" mode="aspectFit"></image>
|
||||
<text class="empty-title">暂无比赛记录</text>
|
||||
<text class="empty-desc">完成比赛后会在这里展示</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
209
miniprogram/pages/player/index.wxss
Normal file
209
miniprogram/pages/player/index.wxss
Normal file
@ -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);
|
||||
}
|
||||
@ -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" });
|
||||
},
|
||||
});
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -47,7 +47,7 @@ module.exports = { formatDistance: formatDistance };
|
||||
<view class="store-section">
|
||||
<view class="section-header">
|
||||
<view class="section-title">
|
||||
<text class="section-icon">📍</text>
|
||||
<image class="section-icon" src="/images/icon-store.svg" mode="aspectFit"></image>
|
||||
<text class="section-text">附近门店</text>
|
||||
</view>
|
||||
<text class="section-count">共 {{stores.length}} 家</text>
|
||||
@ -70,7 +70,7 @@ module.exports = { formatDistance: formatDistance };
|
||||
<text class="store-item-address">{{item.address}}</text>
|
||||
<view class="store-item-meta">
|
||||
<text class="store-distance" wx:if="{{item.distance}}">{{util.formatDistance(item.distance)}}</text>
|
||||
<text class="store-users">{{item.sportType === 1 ? '🏸 羽毛球' : '🎾 网球'}}</text>
|
||||
<text class="store-users">{{item.sportType === 1 ? '羽毛球' : '网球'}}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
|
||||
@ -171,7 +171,8 @@
|
||||
}
|
||||
|
||||
.section-icon {
|
||||
font-size: 28rpx;
|
||||
width: 28rpx;
|
||||
height: 28rpx;
|
||||
}
|
||||
|
||||
.section-text {
|
||||
|
||||
@ -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() {
|
||||
// 空函数,仅用于阻止事件冒泡
|
||||
}
|
||||
})
|
||||
},
|
||||
});
|
||||
|
||||
@ -67,7 +67,7 @@
|
||||
<text class="login-subtitle">授权手机号,加入英飒俱乐部</text>
|
||||
</view>
|
||||
<button class="login-btn-primary" open-type="getPhoneNumber" bindgetphonenumber="onGetPhoneNumber">
|
||||
<text class="btn-icon">📱</text>
|
||||
<image class="btn-icon" src="/images/icon-phone.svg" mode="aspectFit"></image>
|
||||
<text class="btn-text">手机号快捷登录</text>
|
||||
</button>
|
||||
</view>
|
||||
@ -117,7 +117,9 @@
|
||||
</view>
|
||||
<!-- 未加入天梯提示 -->
|
||||
<view class="notice-card animate-fadeInUp" style="animation-delay: 0.2s" wx:elif="{{userInfo && userInfo.phone}}">
|
||||
<view class="notice-icon">🏸</view>
|
||||
<view class="notice-icon">
|
||||
<image class="notice-icon-img" src="/images/icon-challenge.svg" mode="aspectFit"></image>
|
||||
</view>
|
||||
<view class="notice-content">
|
||||
<text class="notice-title">尚未加入天梯系统</text>
|
||||
<text class="notice-desc">请联系门店工作人员,开启你的天梯之旅</text>
|
||||
@ -183,7 +185,7 @@
|
||||
</view>
|
||||
<view class="qrcode-tips">
|
||||
<view class="tip-item">
|
||||
<text class="tip-icon">📱</text>
|
||||
<image class="tip-icon" src="/images/icon-qrcode.svg" mode="aspectFit"></image>
|
||||
<text class="tip-text">请出示给对方扫描发起挑战</text>
|
||||
</view>
|
||||
</view>
|
||||
@ -199,7 +201,7 @@
|
||||
<view class="profile-modal-body">
|
||||
<!-- 提示信息 -->
|
||||
<view class="profile-tips" wx:if="{{!isEditMode}}">
|
||||
<text class="tips-icon">💡</text>
|
||||
<image class="tips-icon" src="/images/icon-info.svg" mode="aspectFit"></image>
|
||||
<text class="tips-text">完善资料后,好友可以更容易找到你</text>
|
||||
</view>
|
||||
<!-- 头像选择 -->
|
||||
@ -208,7 +210,7 @@
|
||||
<button class="avatar-choose-btn" open-type="chooseAvatar" bindchooseavatar="onChooseAvatarNew">
|
||||
<image class="profile-avatar-preview" src="{{profileForm.avatar || '/images/avatar-default.svg'}}" mode="aspectFill"></image>
|
||||
<view class="avatar-choose-badge">
|
||||
<text class="choose-icon">📷</text>
|
||||
<image class="choose-icon" src="/images/icon-scan.svg" mode="aspectFit"></image>
|
||||
</view>
|
||||
</button>
|
||||
<text class="avatar-tip">点击更换头像</text>
|
||||
|
||||
@ -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 {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user