Compare commits
2 Commits
e0713c3fd8
...
75760d25fd
| Author | SHA1 | Date | |
|---|---|---|---|
| 75760d25fd | |||
| 74ed19eee1 |
@ -81,9 +81,9 @@ App({
|
|||||||
sessionKey: wxInfo.sessionKey,
|
sessionKey: wxInfo.sessionKey,
|
||||||
encryptedData,
|
encryptedData,
|
||||||
iv,
|
iv,
|
||||||
nickname: userProfile?.nickName || "",
|
nickname: (userProfile && userProfile.nickName) || "",
|
||||||
avatar: userProfile?.avatarUrl || "",
|
avatar: (userProfile && userProfile.avatarUrl) || "",
|
||||||
gender: userProfile?.gender || 0,
|
gender: (userProfile && userProfile.gender) || 0,
|
||||||
},
|
},
|
||||||
success: (loginRes) => {
|
success: (loginRes) => {
|
||||||
if (loginRes.data.code === 0) {
|
if (loginRes.data.code === 0) {
|
||||||
@ -93,7 +93,7 @@ App({
|
|||||||
// 处理天梯用户信息
|
// 处理天梯用户信息
|
||||||
if (loginRes.data.data.userInfo.ladderUsers && loginRes.data.data.userInfo.ladderUsers.length > 0) {
|
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(
|
const currentStoreLadderUser = loginRes.data.data.userInfo.ladderUsers.find(
|
||||||
lu => lu.storeId === this.globalData.currentStore.storeId
|
lu => lu.storeId === this.globalData.currentStore.storeId
|
||||||
);
|
);
|
||||||
@ -139,7 +139,7 @@ App({
|
|||||||
// 处理天梯用户信息
|
// 处理天梯用户信息
|
||||||
if (res.data.ladderUsers && res.data.ladderUsers.length > 0) {
|
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(
|
const currentStoreLadderUser = res.data.ladderUsers.find(
|
||||||
lu => lu.storeId === this.globalData.currentStore.storeId
|
lu => lu.storeId === this.globalData.currentStore.storeId
|
||||||
);
|
);
|
||||||
@ -179,11 +179,11 @@ App({
|
|||||||
this.globalData.currentStore = res.data;
|
this.globalData.currentStore = res.data;
|
||||||
|
|
||||||
// 如果当前门店有 ladderUserId,获取该门店的天梯用户信息
|
// 如果当前门店有 ladderUserId,获取该门店的天梯用户信息
|
||||||
if (res.data?.ladderUserId) {
|
if (res.data && res.data.ladderUserId) {
|
||||||
this.getLadderUser(res.data.storeId);
|
this.getLadderUser(res.data.storeId);
|
||||||
} else if (res.data?.storeId) {
|
} else if (res.data && res.data.storeId) {
|
||||||
// 如果当前门店没有 ladderUserId,但用户信息中有该门店的天梯用户,使用它
|
// 如果当前门店没有 ladderUserId,但用户信息中有该门店的天梯用户,使用它
|
||||||
if (this.globalData.userInfo?.ladderUsers) {
|
if (this.globalData.userInfo && this.globalData.userInfo.ladderUsers) {
|
||||||
const currentStoreLadderUser = this.globalData.userInfo.ladderUsers.find(
|
const currentStoreLadderUser = this.globalData.userInfo.ladderUsers.find(
|
||||||
lu => lu.storeId === res.data.storeId
|
lu => lu.storeId === res.data.storeId
|
||||||
);
|
);
|
||||||
@ -207,11 +207,11 @@ App({
|
|||||||
this.globalData.currentStore = res.data;
|
this.globalData.currentStore = res.data;
|
||||||
|
|
||||||
// 如果当前门店有 ladderUserId,获取该门店的天梯用户信息
|
// 如果当前门店有 ladderUserId,获取该门店的天梯用户信息
|
||||||
if (res.data?.ladderUserId) {
|
if (res.data && res.data.ladderUserId) {
|
||||||
this.getLadderUser(res.data.storeId);
|
this.getLadderUser(res.data.storeId);
|
||||||
} else if (res.data?.storeId) {
|
} else if (res.data && res.data.storeId) {
|
||||||
// 如果当前门店没有 ladderUserId,但用户信息中有该门店的天梯用户,使用它
|
// 如果当前门店没有 ladderUserId,但用户信息中有该门店的天梯用户,使用它
|
||||||
if (this.globalData.userInfo?.ladderUsers) {
|
if (this.globalData.userInfo && this.globalData.userInfo.ladderUsers) {
|
||||||
const currentStoreLadderUser = this.globalData.userInfo.ladderUsers.find(
|
const currentStoreLadderUser = this.globalData.userInfo.ladderUsers.find(
|
||||||
lu => lu.storeId === res.data.storeId
|
lu => lu.storeId === res.data.storeId
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
"pages": [
|
"pages": [
|
||||||
"pages/index/index",
|
"pages/index/index",
|
||||||
|
"pages/player/index",
|
||||||
"pages/user/index",
|
"pages/user/index",
|
||||||
"pages/match/challenge/index",
|
"pages/match/challenge/index",
|
||||||
"pages/match/challenge-detail/index",
|
"pages/match/challenge-detail/index",
|
||||||
|
|||||||
@ -26,6 +26,19 @@ page {
|
|||||||
--accent-light: #e6fbf7;
|
--accent-light: #e6fbf7;
|
||||||
--accent-soft: rgba(0, 201, 167, 0.1);
|
--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-page: #f7f8fa;
|
||||||
--bg-white: #ffffff;
|
--bg-white: #ffffff;
|
||||||
|
|||||||
@ -6,9 +6,9 @@
|
|||||||
// 开发环境配置
|
// 开发环境配置
|
||||||
const devConfig = {
|
const devConfig = {
|
||||||
// API 基础地址(本地开发)
|
// API 基础地址(本地开发)
|
||||||
baseUrl: "https://yingsa-server.ethan.team",
|
baseUrl: "http://127.0.0.1:3000",
|
||||||
// WebSocket 地址(本地开发)
|
// WebSocket 地址(本地开发)
|
||||||
wsUrl: "wss://yingsa-server.ethan.team/ws",
|
wsUrl: "ws://127.0.0.1:3000/ws",
|
||||||
};
|
};
|
||||||
|
|
||||||
// 生产环境配置
|
// 生产环境配置
|
||||||
@ -25,7 +25,10 @@ const prodConfig = {
|
|||||||
const getEnv = () => {
|
const getEnv = () => {
|
||||||
try {
|
try {
|
||||||
// 尝试获取微信环境
|
// 尝试获取微信环境
|
||||||
const envVersion = __wxConfig?.envVersion || "develop";
|
const envVersion =
|
||||||
|
typeof __wxConfig !== "undefined" && __wxConfig && __wxConfig.envVersion
|
||||||
|
? __wxConfig.envVersion
|
||||||
|
: "develop";
|
||||||
return envVersion === "release" ? "production" : "development";
|
return envVersion === "release" ? "production" : "development";
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return "development";
|
return "development";
|
||||||
@ -35,14 +38,9 @@ const getEnv = () => {
|
|||||||
const env = getEnv();
|
const env = getEnv();
|
||||||
const config = env === "production" ? prodConfig : devConfig;
|
const config = env === "production" ? prodConfig : devConfig;
|
||||||
|
|
||||||
module.exports = {
|
module.exports = Object.assign({}, config, {
|
||||||
...config,
|
|
||||||
env,
|
env,
|
||||||
// 其他配置项
|
|
||||||
// 上传文件大小限制 (MB)
|
|
||||||
uploadMaxSize: 5,
|
uploadMaxSize: 5,
|
||||||
// 请求超时时间 (ms)
|
|
||||||
requestTimeout: 30000,
|
requestTimeout: 30000,
|
||||||
// 版本号
|
|
||||||
version: "1.0.0",
|
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,24 +1,26 @@
|
|||||||
const app = getApp()
|
const app = getApp();
|
||||||
const util = require('../../utils/util')
|
const util = require("../../utils/util");
|
||||||
|
|
||||||
Page({
|
Page({
|
||||||
data: {
|
data: {
|
||||||
currentStore: null,
|
currentStore: null,
|
||||||
gender: '',
|
gender: "",
|
||||||
list: [],
|
list: [],
|
||||||
loading: false,
|
loading: false,
|
||||||
page: 1,
|
page: 1,
|
||||||
pageSize: 20,
|
pageSize: 20,
|
||||||
hasMore: true
|
hasMore: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
onLoad() {
|
onLoad() {
|
||||||
this.initData()
|
this.initData();
|
||||||
},
|
},
|
||||||
|
|
||||||
onShow() {
|
onShow() {
|
||||||
const newStore = app.globalData.currentStore
|
const newStore = app.globalData.currentStore;
|
||||||
const oldStoreId = this.data.currentStore?.storeId
|
const oldStoreId = this.data.currentStore
|
||||||
|
? this.data.currentStore.storeId
|
||||||
|
: null;
|
||||||
|
|
||||||
// 检查门店是否切换
|
// 检查门店是否切换
|
||||||
if (newStore && newStore.storeId !== oldStoreId) {
|
if (newStore && newStore.storeId !== oldStoreId) {
|
||||||
@ -26,32 +28,32 @@ Page({
|
|||||||
currentStore: newStore,
|
currentStore: newStore,
|
||||||
page: 1,
|
page: 1,
|
||||||
hasMore: true,
|
hasMore: true,
|
||||||
list: []
|
list: [],
|
||||||
})
|
});
|
||||||
this.fetchData()
|
this.fetchData();
|
||||||
} else if (app.globalData.storeChanged) {
|
} else if (app.globalData.storeChanged) {
|
||||||
// 全局标记门店已切换
|
// 全局标记门店已切换
|
||||||
app.globalData.storeChanged = false
|
app.globalData.storeChanged = false;
|
||||||
this.setData({
|
this.setData({
|
||||||
currentStore: newStore,
|
currentStore: newStore,
|
||||||
page: 1,
|
page: 1,
|
||||||
hasMore: true,
|
hasMore: true,
|
||||||
list: []
|
list: [],
|
||||||
})
|
});
|
||||||
this.fetchData()
|
this.fetchData();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
onPullDownRefresh() {
|
onPullDownRefresh() {
|
||||||
this.setData({ page: 1, hasMore: true })
|
this.setData({ page: 1, hasMore: true });
|
||||||
this.fetchData().then(() => {
|
this.fetchData().then(() => {
|
||||||
wx.stopPullDownRefresh()
|
wx.stopPullDownRefresh();
|
||||||
})
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
onReachBottom() {
|
onReachBottom() {
|
||||||
if (this.data.hasMore && !this.data.loading) {
|
if (this.data.hasMore && !this.data.loading) {
|
||||||
this.loadMore()
|
this.loadMore();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -59,66 +61,76 @@ Page({
|
|||||||
// 检查是否已登录(有 token)
|
// 检查是否已登录(有 token)
|
||||||
if (!app.globalData.token) {
|
if (!app.globalData.token) {
|
||||||
// 未登录,跳转到用户页面进行登录
|
// 未登录,跳转到用户页面进行登录
|
||||||
wx.switchTab({ url: '/pages/user/index' })
|
wx.switchTab({ url: "/pages/user/index" });
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取当前门店
|
// 获取当前门店
|
||||||
try {
|
try {
|
||||||
const store = await app.getCurrentStore()
|
const store = await app.getCurrentStore();
|
||||||
this.setData({ currentStore: store })
|
this.setData({ currentStore: store });
|
||||||
this.fetchData()
|
this.fetchData();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('获取门店失败:', e)
|
console.error("获取门店失败:", e);
|
||||||
// 如果是认证失败,跳转到登录页
|
// 如果是认证失败,跳转到登录页
|
||||||
if (e.code === 401) {
|
if (e.code === 401) {
|
||||||
wx.switchTab({ url: '/pages/user/index' })
|
wx.switchTab({ url: "/pages/user/index" });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
async fetchData() {
|
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 {
|
try {
|
||||||
const res = await app.request('/api/ladder/ranking', {
|
const res = await app.request("/api/ladder/ranking", {
|
||||||
store_id: this.data.currentStore.storeId,
|
store_id: this.data.currentStore.storeId,
|
||||||
gender: this.data.gender,
|
gender: this.data.gender,
|
||||||
page: this.data.page,
|
page: this.data.page,
|
||||||
pageSize: this.data.pageSize
|
pageSize: this.data.pageSize,
|
||||||
})
|
});
|
||||||
|
|
||||||
const list = res.data.list || []
|
const list = res.data.list || [];
|
||||||
this.setData({
|
this.setData({
|
||||||
list: this.data.page === 1 ? list : [...this.data.list, ...list],
|
list: this.data.page === 1 ? list : this.data.list.concat(list),
|
||||||
hasMore: list.length >= this.data.pageSize
|
hasMore: list.length >= this.data.pageSize,
|
||||||
})
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('获取排名失败:', e)
|
console.error("获取排名失败:", e);
|
||||||
} finally {
|
} finally {
|
||||||
this.setData({ loading: false })
|
this.setData({ loading: false });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
loadMore() {
|
loadMore() {
|
||||||
this.setData({ page: this.data.page + 1 })
|
this.setData({ page: this.data.page + 1 });
|
||||||
this.fetchData()
|
this.fetchData();
|
||||||
},
|
},
|
||||||
|
|
||||||
setGender(e) {
|
setGender(e) {
|
||||||
const gender = e.currentTarget.dataset.gender
|
const gender = e.currentTarget.dataset.gender;
|
||||||
this.setData({ gender, page: 1, hasMore: true })
|
this.setData({ gender, page: 1, hasMore: true });
|
||||||
this.fetchData()
|
this.fetchData();
|
||||||
},
|
},
|
||||||
|
|
||||||
selectStore() {
|
selectStore() {
|
||||||
wx.navigateTo({ url: '/pages/store/index' })
|
wx.navigateTo({ url: "/pages/store/index" });
|
||||||
},
|
},
|
||||||
|
|
||||||
viewPlayer(e) {
|
viewPlayer(e) {
|
||||||
const id = e.currentTarget.dataset.id
|
const player = e.currentTarget.dataset.player;
|
||||||
wx.navigateTo({ url: `/pages/player/index?id=${id}` })
|
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"
|
bindtap="setGender"
|
||||||
data-gender="1"
|
data-gender="1"
|
||||||
>
|
>
|
||||||
♂ 男子
|
男子
|
||||||
</view>
|
</view>
|
||||||
<view
|
<view
|
||||||
class="filter-item {{gender === '2' ? 'active' : ''}}"
|
class="filter-item {{gender === '2' ? 'active' : ''}}"
|
||||||
bindtap="setGender"
|
bindtap="setGender"
|
||||||
data-gender="2"
|
data-gender="2"
|
||||||
>
|
>
|
||||||
♀ 女子
|
女子
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@ -62,11 +62,11 @@
|
|||||||
wx:key="id"
|
wx:key="id"
|
||||||
bindtap="viewPlayer"
|
bindtap="viewPlayer"
|
||||||
data-id="{{item.id}}"
|
data-id="{{item.id}}"
|
||||||
|
data-player="{{item}}"
|
||||||
>
|
>
|
||||||
<!-- 排名徽章 -->
|
<!-- 排名徽章 -->
|
||||||
<view class="rank-badge {{item.rank === 1 ? 'top1' : item.rank === 2 ? 'top2' : item.rank === 3 ? 'top3' : 'normal'}}">
|
<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>{{item.rank}}</text>
|
||||||
<text wx:else>{{item.rank}}</text>
|
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 选手头像 -->
|
<!-- 选手头像 -->
|
||||||
|
|||||||
@ -49,7 +49,7 @@
|
|||||||
width: 12rpx;
|
width: 12rpx;
|
||||||
height: 12rpx;
|
height: 12rpx;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
background: #ff6b35;
|
background: var(--primary);
|
||||||
box-shadow: 0 0 8rpx rgba(255, 107, 53, 0.4);
|
box-shadow: 0 0 8rpx rgba(255, 107, 53, 0.4);
|
||||||
animation: pulse 2s ease-in-out infinite;
|
animation: pulse 2s ease-in-out infinite;
|
||||||
}
|
}
|
||||||
@ -57,7 +57,7 @@
|
|||||||
.store-name {
|
.store-name {
|
||||||
font-size: 30rpx;
|
font-size: 30rpx;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: #333;
|
color: var(--text-primary);
|
||||||
letter-spacing: 0.5rpx;
|
letter-spacing: 0.5rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,13 +77,13 @@
|
|||||||
|
|
||||||
.change-store-text {
|
.change-store-text {
|
||||||
font-size: 24rpx;
|
font-size: 24rpx;
|
||||||
color: #666;
|
color: var(--text-secondary);
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
.change-store-arrow {
|
.change-store-arrow {
|
||||||
font-size: 22rpx;
|
font-size: 22rpx;
|
||||||
color: #999;
|
color: var(--text-muted);
|
||||||
font-weight: 300;
|
font-weight: 300;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -100,7 +100,7 @@
|
|||||||
display: block;
|
display: block;
|
||||||
font-size: 52rpx;
|
font-size: 52rpx;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
color: #1a1a1a;
|
color: var(--text-primary);
|
||||||
margin-bottom: 8rpx;
|
margin-bottom: 8rpx;
|
||||||
letter-spacing: 1rpx;
|
letter-spacing: 1rpx;
|
||||||
}
|
}
|
||||||
@ -108,7 +108,7 @@
|
|||||||
.page-subtitle {
|
.page-subtitle {
|
||||||
display: block;
|
display: block;
|
||||||
font-size: 26rpx;
|
font-size: 26rpx;
|
||||||
color: #999;
|
color: var(--text-muted);
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
letter-spacing: 0.5rpx;
|
letter-spacing: 0.5rpx;
|
||||||
}
|
}
|
||||||
@ -149,9 +149,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.filter-item.active {
|
.filter-item.active {
|
||||||
background: linear-gradient(135deg, #ff6b35 0%, #ff8c42 100%);
|
background: var(--primary-gradient);
|
||||||
color: #fff;
|
color: var(--text-white);
|
||||||
box-shadow: 0 4rpx 16rpx rgba(255, 107, 53, 0.3);
|
box-shadow: var(--shadow-primary);
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -168,12 +168,12 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 24rpx;
|
padding: 24rpx;
|
||||||
background: #fff;
|
background: var(--bg-card);
|
||||||
border-radius: 20rpx;
|
border-radius: 20rpx;
|
||||||
margin-bottom: 16rpx;
|
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);
|
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 {
|
.ranking-item:last-child {
|
||||||
@ -186,9 +186,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.ranking-item.top-rank {
|
.ranking-item.top-rank {
|
||||||
background: linear-gradient(135deg, #fff5f0 0%, #fff 100%);
|
background: linear-gradient(135deg, var(--primary-soft) 0%, var(--bg-white) 100%);
|
||||||
border: 2rpx solid rgba(255, 107, 53, 0.2);
|
border: 2rpx solid var(--border-primary);
|
||||||
box-shadow: 0 6rpx 20rpx rgba(255, 107, 53, 0.15);
|
box-shadow: var(--shadow-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 排名徽章 */
|
/* 排名徽章 */
|
||||||
@ -233,8 +233,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.rank-badge.normal {
|
.rank-badge.normal {
|
||||||
background: linear-gradient(135deg, #f5f5f5 0%, #e8e8e8 100%);
|
background: linear-gradient(135deg, var(--bg-soft) 0%, var(--bg-card-hover) 100%);
|
||||||
color: #666;
|
color: var(--text-secondary);
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -244,10 +244,10 @@
|
|||||||
height: 80rpx;
|
height: 80rpx;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
margin-right: 20rpx;
|
margin-right: 20rpx;
|
||||||
border: 3rpx solid #fff;
|
border: 3rpx solid var(--bg-white);
|
||||||
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.1);
|
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.1);
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
background: #f5f5f5;
|
background: var(--bg-soft);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 选手信息 */
|
/* 选手信息 */
|
||||||
@ -260,7 +260,7 @@
|
|||||||
display: block;
|
display: block;
|
||||||
font-size: 30rpx;
|
font-size: 30rpx;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: #333;
|
color: var(--text-primary);
|
||||||
margin-bottom: 8rpx;
|
margin-bottom: 8rpx;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
@ -292,7 +292,7 @@
|
|||||||
|
|
||||||
.player-stats {
|
.player-stats {
|
||||||
font-size: 24rpx;
|
font-size: 24rpx;
|
||||||
color: #666;
|
color: var(--text-secondary);
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -306,14 +306,14 @@
|
|||||||
display: block;
|
display: block;
|
||||||
font-size: 36rpx;
|
font-size: 36rpx;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
color: #ff6b35;
|
color: var(--primary);
|
||||||
line-height: 1.2;
|
line-height: 1.2;
|
||||||
margin-bottom: 4rpx;
|
margin-bottom: 4rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.power-label {
|
.power-label {
|
||||||
font-size: 22rpx;
|
font-size: 22rpx;
|
||||||
color: #999;
|
color: var(--text-muted);
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -198,8 +198,8 @@ Page({
|
|||||||
canConfirmScore = true
|
canConfirmScore = true
|
||||||
console.log('进行中状态:设置确认比分权限(对方已提交,等待确认)', {
|
console.log('进行中状态:设置确认比分权限(对方已提交,等待确认)', {
|
||||||
submitBy: game.submitBy,
|
submitBy: game.submitBy,
|
||||||
challengerId: matchInfo.challenger?.id,
|
challengerId: matchInfo.challenger ? matchInfo.challenger.id : null,
|
||||||
defenderId: matchInfo.defender?.id,
|
defenderId: matchInfo.defender ? matchInfo.defender.id : null,
|
||||||
myRole
|
myRole
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -214,11 +214,11 @@ Page({
|
|||||||
console.log('尝试通过游戏信息识别角色:', {
|
console.log('尝试通过游戏信息识别角色:', {
|
||||||
player1Id: game.player1Id,
|
player1Id: game.player1Id,
|
||||||
player2Id: game.player2Id,
|
player2Id: game.player2Id,
|
||||||
challengerId: matchInfo.challenger?.id,
|
challengerId: matchInfo.challenger ? matchInfo.challenger.id : null,
|
||||||
defenderId: matchInfo.defender?.id,
|
defenderId: matchInfo.defender ? matchInfo.defender.id : null,
|
||||||
currentUserId: currentUser.id,
|
currentUserId: currentUser.id,
|
||||||
challengerUserId: matchInfo.challenger?.userId,
|
challengerUserId: matchInfo.challenger ? matchInfo.challenger.userId : null,
|
||||||
defenderUserId: matchInfo.defender?.userId
|
defenderUserId: matchInfo.defender ? matchInfo.defender.userId : null
|
||||||
})
|
})
|
||||||
|
|
||||||
// 通过比较 challenger/defender 的 id(ladder_user_id)和 player1_id/player2_id 来判断
|
// 通过比较 challenger/defender 的 id(ladder_user_id)和 player1_id/player2_id 来判断
|
||||||
@ -262,8 +262,8 @@ Page({
|
|||||||
// 如果游戏状态为2(已提交)且对方已提交,等待我确认
|
// 如果游戏状态为2(已提交)且对方已提交,等待我确认
|
||||||
else if (game.status === 2 && game.submitBy) {
|
else if (game.status === 2 && game.submitBy) {
|
||||||
// 判断当前用户是否是提交者
|
// 判断当前用户是否是提交者
|
||||||
const isSubmitter = (myRole === 'challenger' && game.submitBy == matchInfo.challenger?.id) ||
|
const isSubmitter = (myRole === 'challenger' && matchInfo.challenger && game.submitBy == matchInfo.challenger.id) ||
|
||||||
(myRole === 'defender' && game.submitBy == matchInfo.defender?.id)
|
(myRole === 'defender' && matchInfo.defender && game.submitBy == matchInfo.defender.id)
|
||||||
|
|
||||||
if (!isSubmitter && game.confirmStatus === 0) {
|
if (!isSubmitter && game.confirmStatus === 0) {
|
||||||
canConfirmScore = true
|
canConfirmScore = true
|
||||||
@ -317,10 +317,10 @@ Page({
|
|||||||
canAccept,
|
canAccept,
|
||||||
canReject,
|
canReject,
|
||||||
matchInfoStatus: matchInfo.status,
|
matchInfoStatus: matchInfo.status,
|
||||||
defenderUserId: matchInfo.defender?.userId,
|
defenderUserId: matchInfo.defender ? matchInfo.defender.userId : null,
|
||||||
currentUserId: app.globalData.userInfo?.id,
|
currentUserId: app.globalData.userInfo ? app.globalData.userInfo.id : null,
|
||||||
defenderPhone: matchInfo.defender?.phone,
|
defenderPhone: matchInfo.defender ? matchInfo.defender.phone : null,
|
||||||
currentUserPhone: app.globalData.userInfo?.phone
|
currentUserPhone: app.globalData.userInfo ? app.globalData.userInfo.phone : null
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@ -471,7 +471,7 @@ Page({
|
|||||||
|
|
||||||
// 确认比分
|
// 确认比分
|
||||||
async confirmScore(confirm) {
|
async confirmScore(confirm) {
|
||||||
const game = this.data.matchInfo.games?.[0]
|
const game = this.data.matchInfo.games && this.data.matchInfo.games[0]
|
||||||
if (!game) {
|
if (!game) {
|
||||||
wx.showToast({ title: '比赛信息错误', icon: 'none' })
|
wx.showToast({ title: '比赛信息错误', icon: 'none' })
|
||||||
return
|
return
|
||||||
@ -503,7 +503,7 @@ Page({
|
|||||||
|
|
||||||
// 确认比分按钮
|
// 确认比分按钮
|
||||||
confirmScoreBtn() {
|
confirmScoreBtn() {
|
||||||
const game = this.data.matchInfo.games?.[0]
|
const game = this.data.matchInfo.games && this.data.matchInfo.games[0]
|
||||||
if (!game) {
|
if (!game) {
|
||||||
wx.showToast({ title: '比赛信息错误', icon: 'none' })
|
wx.showToast({ title: '比赛信息错误', icon: 'none' })
|
||||||
return
|
return
|
||||||
@ -521,12 +521,12 @@ Page({
|
|||||||
myScore = game.player1Score || 0
|
myScore = game.player1Score || 0
|
||||||
opponentScore = game.player2Score || 0
|
opponentScore = game.player2Score || 0
|
||||||
myName = this.data.matchInfo.challenger.realName || '挑战者'
|
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) {
|
} else if (this.data.matchInfo.challenger && this.data.matchInfo.challenger.id == game.player2Id) {
|
||||||
myScore = game.player2Score || 0
|
myScore = game.player2Score || 0
|
||||||
opponentScore = game.player1Score || 0
|
opponentScore = game.player1Score || 0
|
||||||
myName = this.data.matchInfo.challenger.realName || '挑战者'
|
myName = this.data.matchInfo.challenger.realName || '挑战者'
|
||||||
opponentName = this.data.matchInfo.defender?.realName || '被挑战者'
|
opponentName = (this.data.matchInfo.defender && this.data.matchInfo.defender.realName) || '被挑战者'
|
||||||
} else {
|
} else {
|
||||||
// 如果无法确定,使用默认显示
|
// 如果无法确定,使用默认显示
|
||||||
myScore = game.player1Score || 0
|
myScore = game.player1Score || 0
|
||||||
@ -538,12 +538,12 @@ Page({
|
|||||||
myScore = game.player1Score || 0
|
myScore = game.player1Score || 0
|
||||||
opponentScore = game.player2Score || 0
|
opponentScore = game.player2Score || 0
|
||||||
myName = this.data.matchInfo.defender.realName || '被挑战者'
|
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) {
|
} else if (this.data.matchInfo.defender && this.data.matchInfo.defender.id == game.player2Id) {
|
||||||
myScore = game.player2Score || 0
|
myScore = game.player2Score || 0
|
||||||
opponentScore = game.player1Score || 0
|
opponentScore = game.player1Score || 0
|
||||||
myName = this.data.matchInfo.defender.realName || '被挑战者'
|
myName = this.data.matchInfo.defender.realName || '被挑战者'
|
||||||
opponentName = this.data.matchInfo.challenger?.realName || '挑战者'
|
opponentName = (this.data.matchInfo.challenger && this.data.matchInfo.challenger.realName) || '挑战者'
|
||||||
} else {
|
} else {
|
||||||
// 如果无法确定,使用默认显示
|
// 如果无法确定,使用默认显示
|
||||||
myScore = game.player1Score || 0
|
myScore = game.player1Score || 0
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
.page-container {
|
.page-container {
|
||||||
min-height: 100vh;
|
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;
|
padding: 20rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -9,14 +9,14 @@
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
height: 60vh;
|
height: 60vh;
|
||||||
color: #666;
|
color: var(--text-secondary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.match-info {
|
.match-info {
|
||||||
background: #fff;
|
background: var(--bg-card);
|
||||||
border-radius: 24rpx;
|
border-radius: var(--radius-lg);
|
||||||
padding: 40rpx;
|
padding: 40rpx;
|
||||||
box-shadow: 0 8rpx 32rpx rgba(0, 0, 0, 0.1);
|
box-shadow: var(--shadow-card);
|
||||||
}
|
}
|
||||||
|
|
||||||
.match-header {
|
.match-header {
|
||||||
@ -25,13 +25,13 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
margin-bottom: 40rpx;
|
margin-bottom: 40rpx;
|
||||||
padding-bottom: 30rpx;
|
padding-bottom: 30rpx;
|
||||||
border-bottom: 2rpx solid #f0f0f0;
|
border-bottom: 2rpx solid var(--border-soft);
|
||||||
}
|
}
|
||||||
|
|
||||||
.match-title {
|
.match-title {
|
||||||
font-size: 36rpx;
|
font-size: 36rpx;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: #333;
|
color: var(--text-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.match-status {
|
.match-status {
|
||||||
@ -41,23 +41,23 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.status-0 {
|
.status-0 {
|
||||||
background: #fff3cd;
|
background: var(--warning-soft);
|
||||||
color: #856404;
|
color: var(--warning-text);
|
||||||
}
|
}
|
||||||
|
|
||||||
.status-1 {
|
.status-1 {
|
||||||
background: #d1ecf1;
|
background: var(--info-soft);
|
||||||
color: #0c5460;
|
color: var(--info-text);
|
||||||
}
|
}
|
||||||
|
|
||||||
.status-2 {
|
.status-2 {
|
||||||
background: #d4edda;
|
background: var(--success-soft);
|
||||||
color: #155724;
|
color: var(--success-text);
|
||||||
}
|
}
|
||||||
|
|
||||||
.status-3 {
|
.status-3 {
|
||||||
background: #f8d7da;
|
background: var(--danger-soft);
|
||||||
color: #721c24;
|
color: var(--danger-text);
|
||||||
}
|
}
|
||||||
|
|
||||||
.opponent-section {
|
.opponent-section {
|
||||||
@ -70,7 +70,7 @@
|
|||||||
|
|
||||||
.opponent-label {
|
.opponent-label {
|
||||||
font-size: 24rpx;
|
font-size: 24rpx;
|
||||||
color: #999;
|
color: var(--text-muted);
|
||||||
margin-bottom: 20rpx;
|
margin-bottom: 20rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,7 +84,7 @@
|
|||||||
width: 100rpx;
|
width: 100rpx;
|
||||||
height: 100rpx;
|
height: 100rpx;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
border: 4rpx solid #e0e0e0;
|
border: 4rpx solid var(--border-light);
|
||||||
}
|
}
|
||||||
|
|
||||||
.opponent-details {
|
.opponent-details {
|
||||||
@ -97,12 +97,12 @@
|
|||||||
.opponent-name {
|
.opponent-name {
|
||||||
font-size: 32rpx;
|
font-size: 32rpx;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: #333;
|
color: var(--text-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.opponent-level {
|
.opponent-level {
|
||||||
font-size: 24rpx;
|
font-size: 24rpx;
|
||||||
color: #666;
|
color: var(--text-secondary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.vs-divider {
|
.vs-divider {
|
||||||
@ -110,25 +110,25 @@
|
|||||||
margin: 30rpx 0;
|
margin: 30rpx 0;
|
||||||
font-size: 32rpx;
|
font-size: 32rpx;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: #999;
|
color: var(--text-muted);
|
||||||
}
|
}
|
||||||
|
|
||||||
.match-progress {
|
.match-progress {
|
||||||
margin-bottom: 40rpx;
|
margin-bottom: 40rpx;
|
||||||
padding-top: 30rpx;
|
padding-top: 30rpx;
|
||||||
border-top: 2rpx solid #f0f0f0;
|
border-top: 2rpx solid var(--border-soft);
|
||||||
}
|
}
|
||||||
|
|
||||||
.progress-title {
|
.progress-title {
|
||||||
font-size: 28rpx;
|
font-size: 28rpx;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: #333;
|
color: var(--text-primary);
|
||||||
margin-bottom: 20rpx;
|
margin-bottom: 20rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.game-item {
|
.game-item {
|
||||||
background: #f8f9fa;
|
background: var(--bg-soft);
|
||||||
border-radius: 16rpx;
|
border-radius: var(--radius-md);
|
||||||
padding: 24rpx;
|
padding: 24rpx;
|
||||||
margin-bottom: 16rpx;
|
margin-bottom: 16rpx;
|
||||||
}
|
}
|
||||||
@ -141,33 +141,33 @@
|
|||||||
|
|
||||||
.score-label {
|
.score-label {
|
||||||
font-size: 24rpx;
|
font-size: 24rpx;
|
||||||
color: #666;
|
color: var(--text-secondary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.score-value {
|
.score-value {
|
||||||
font-size: 32rpx;
|
font-size: 32rpx;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: #333;
|
color: var(--text-primary);
|
||||||
margin-left: 12rpx;
|
margin-left: 12rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.game-status {
|
.game-status {
|
||||||
font-size: 24rpx;
|
font-size: 24rpx;
|
||||||
color: #999;
|
color: var(--text-muted);
|
||||||
}
|
}
|
||||||
|
|
||||||
.confirm-tip {
|
.confirm-tip {
|
||||||
margin-top: 16rpx;
|
margin-top: 16rpx;
|
||||||
padding: 16rpx 20rpx;
|
padding: 16rpx 20rpx;
|
||||||
background: linear-gradient(135deg, #fff5f0 0%, #ffe8d6 100%);
|
background: var(--primary-gradient-soft);
|
||||||
border-radius: 12rpx;
|
border-radius: var(--radius-sm);
|
||||||
border-left: 4rpx solid #ff6b35;
|
border-left: 4rpx solid var(--primary);
|
||||||
box-shadow: 0 2rpx 8rpx rgba(255, 107, 53, 0.1);
|
box-shadow: var(--shadow-sm);
|
||||||
}
|
}
|
||||||
|
|
||||||
.confirm-tip .tip-text {
|
.confirm-tip .tip-text {
|
||||||
font-size: 26rpx;
|
font-size: 26rpx;
|
||||||
color: #d84315;
|
color: var(--primary-dark);
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -181,13 +181,13 @@
|
|||||||
margin-top: 40rpx;
|
margin-top: 40rpx;
|
||||||
padding: 30rpx;
|
padding: 30rpx;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
background: #f8f9fa;
|
background: var(--bg-soft);
|
||||||
border-radius: 16rpx;
|
border-radius: var(--radius-md);
|
||||||
}
|
}
|
||||||
|
|
||||||
.tip-text {
|
.tip-text {
|
||||||
font-size: 28rpx;
|
font-size: 28rpx;
|
||||||
color: #999;
|
color: var(--text-muted);
|
||||||
}
|
}
|
||||||
|
|
||||||
.action-btn {
|
.action-btn {
|
||||||
@ -203,41 +203,42 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.accept-btn {
|
.accept-btn {
|
||||||
background: linear-gradient(135deg, #ff6b35 0%, #ff8c42 100%);
|
background: var(--primary-gradient);
|
||||||
color: #fff;
|
color: #fff;
|
||||||
box-shadow: 0 4rpx 16rpx rgba(255, 107, 53, 0.3);
|
box-shadow: var(--shadow-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.accept-btn:active {
|
.accept-btn:active {
|
||||||
background: linear-gradient(135deg, #e55a2b 0%, #e67e2f 100%);
|
background: var(--primary-dark);
|
||||||
box-shadow: 0 2rpx 8rpx rgba(255, 107, 53, 0.4);
|
box-shadow: var(--shadow-md);
|
||||||
}
|
}
|
||||||
|
|
||||||
.reject-btn {
|
.reject-btn {
|
||||||
background: #f5f5f5;
|
background: var(--bg-white);
|
||||||
color: #666;
|
color: var(--text-secondary);
|
||||||
|
border: 2rpx solid var(--border-light);
|
||||||
}
|
}
|
||||||
|
|
||||||
.submit-btn {
|
.submit-btn {
|
||||||
background: linear-gradient(135deg, #ff6b35 0%, #ff8c42 100%);
|
background: var(--primary-gradient);
|
||||||
color: #fff;
|
color: #fff;
|
||||||
box-shadow: 0 4rpx 16rpx rgba(255, 107, 53, 0.3);
|
box-shadow: var(--shadow-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.submit-btn:active {
|
.submit-btn:active {
|
||||||
background: linear-gradient(135deg, #e55a2b 0%, #e67e2f 100%);
|
background: var(--primary-dark);
|
||||||
box-shadow: 0 2rpx 8rpx rgba(255, 107, 53, 0.4);
|
box-shadow: var(--shadow-md);
|
||||||
}
|
}
|
||||||
|
|
||||||
.confirm-btn {
|
.confirm-btn {
|
||||||
background: linear-gradient(135deg, #ff6b35 0%, #ff8c42 100%);
|
background: var(--primary-gradient);
|
||||||
color: #fff;
|
color: #fff;
|
||||||
box-shadow: 0 4rpx 16rpx rgba(255, 107, 53, 0.3);
|
box-shadow: var(--shadow-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.confirm-btn:active {
|
.confirm-btn:active {
|
||||||
background: linear-gradient(135deg, #e55a2b 0%, #e67e2f 100%);
|
background: var(--primary-dark);
|
||||||
box-shadow: 0 2rpx 8rpx rgba(255, 107, 53, 0.4);
|
box-shadow: var(--shadow-md);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 填写比分弹框 */
|
/* 填写比分弹框 */
|
||||||
@ -257,10 +258,10 @@
|
|||||||
.score-modal-content {
|
.score-modal-content {
|
||||||
width: 600rpx;
|
width: 600rpx;
|
||||||
max-width: 90%;
|
max-width: 90%;
|
||||||
background: #fff;
|
background: var(--bg-card);
|
||||||
border-radius: 24rpx;
|
border-radius: var(--radius-lg);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
box-shadow: 0 16rpx 48rpx rgba(0, 0, 0, 0.15);
|
box-shadow: var(--shadow-lg);
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-header {
|
.modal-header {
|
||||||
@ -269,7 +270,7 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 32rpx 40rpx;
|
padding: 32rpx 40rpx;
|
||||||
border-bottom: 2rpx solid rgba(255, 255, 255, 0.2);
|
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 {
|
.modal-header .modal-title {
|
||||||
@ -311,7 +312,7 @@
|
|||||||
.input-label {
|
.input-label {
|
||||||
display: block;
|
display: block;
|
||||||
font-size: 28rpx;
|
font-size: 28rpx;
|
||||||
color: #333;
|
color: var(--text-primary);
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
margin-bottom: 12rpx;
|
margin-bottom: 12rpx;
|
||||||
}
|
}
|
||||||
@ -319,27 +320,27 @@
|
|||||||
.score-input {
|
.score-input {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 88rpx;
|
height: 88rpx;
|
||||||
background: #fff;
|
background: var(--bg-white);
|
||||||
border-radius: 12rpx;
|
border-radius: var(--radius-sm);
|
||||||
padding: 0 24rpx;
|
padding: 0 24rpx;
|
||||||
font-size: 32rpx;
|
font-size: 32rpx;
|
||||||
color: #333;
|
color: var(--text-primary);
|
||||||
border: 2rpx solid #e0e0e0;
|
border: 2rpx solid var(--border-light);
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.score-input:focus {
|
.score-input:focus {
|
||||||
border-color: #ff6b35;
|
border-color: var(--primary);
|
||||||
background: #fff5f0;
|
background: var(--primary-soft);
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-footer {
|
.modal-footer {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 20rpx;
|
gap: 20rpx;
|
||||||
padding: 32rpx 40rpx;
|
padding: 32rpx 40rpx;
|
||||||
border-top: 2rpx solid #f0f0f0;
|
border-top: 2rpx solid var(--border-soft);
|
||||||
background: #fafafa;
|
background: var(--bg-card-hover);
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-btn {
|
.modal-btn {
|
||||||
@ -353,31 +354,31 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.1);
|
box-shadow: var(--shadow-sm);
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-btn:active {
|
.modal-btn:active {
|
||||||
transform: scale(0.98);
|
transform: scale(0.98);
|
||||||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.15);
|
box-shadow: var(--shadow-md);
|
||||||
}
|
}
|
||||||
|
|
||||||
.cancel-btn {
|
.cancel-btn {
|
||||||
background: #fff;
|
background: var(--bg-white);
|
||||||
color: #666;
|
color: var(--text-secondary);
|
||||||
border: 2rpx solid #e0e0e0;
|
border: 2rpx solid var(--border-light);
|
||||||
}
|
}
|
||||||
|
|
||||||
.cancel-btn:active {
|
.cancel-btn:active {
|
||||||
background: #f5f5f5;
|
background: var(--bg-soft);
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-btn.submit-btn {
|
.modal-btn.submit-btn {
|
||||||
background: linear-gradient(135deg, #ff6b35 0%, #ff8c42 100%);
|
background: var(--primary-gradient);
|
||||||
color: #fff;
|
color: #fff;
|
||||||
box-shadow: 0 4rpx 16rpx rgba(255, 107, 53, 0.3);
|
box-shadow: var(--shadow-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-btn.submit-btn:active {
|
.modal-btn.submit-btn:active {
|
||||||
background: linear-gradient(135deg, #e55a2b 0%, #e67e2f 100%);
|
background: var(--primary-dark);
|
||||||
box-shadow: 0 2rpx 8rpx rgba(255, 107, 53, 0.4);
|
box-shadow: var(--shadow-md);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,27 +1,27 @@
|
|||||||
const app = getApp()
|
const app = getApp();
|
||||||
|
|
||||||
Page({
|
Page({
|
||||||
data: {
|
data: {
|
||||||
userInfo: null,
|
userInfo: null,
|
||||||
ladderUser: null,
|
ladderUser: null,
|
||||||
currentStore: null,
|
currentStore: null,
|
||||||
ongoingMatches: [], // 正在进行中的比赛
|
ongoingMatches: [], // 正在进行中的比赛
|
||||||
pendingGames: [] // 待确认的比赛
|
pendingGames: [], // 待确认的比赛
|
||||||
},
|
},
|
||||||
|
|
||||||
onLoad() {
|
onLoad() {
|
||||||
this.initData()
|
this.initData();
|
||||||
},
|
},
|
||||||
|
|
||||||
onShow() {
|
onShow() {
|
||||||
this.initData()
|
this.initData();
|
||||||
},
|
},
|
||||||
|
|
||||||
async onPullDownRefresh() {
|
async onPullDownRefresh() {
|
||||||
try {
|
try {
|
||||||
await this.initData()
|
await this.initData();
|
||||||
} finally {
|
} finally {
|
||||||
wx.stopPullDownRefresh()
|
wx.stopPullDownRefresh();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -29,259 +29,284 @@ Page({
|
|||||||
// 检查是否已登录(有 token)
|
// 检查是否已登录(有 token)
|
||||||
if (!app.globalData.token) {
|
if (!app.globalData.token) {
|
||||||
// 未登录,跳转到用户页面进行登录
|
// 未登录,跳转到用户页面进行登录
|
||||||
wx.switchTab({ url: '/pages/user/index' })
|
wx.switchTab({ url: "/pages/user/index" });
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 每次显示页面时重新获取门店和天梯信息
|
// 每次显示页面时重新获取门店和天梯信息
|
||||||
try {
|
try {
|
||||||
await app.getCurrentStore()
|
await app.getCurrentStore();
|
||||||
// 如果有门店,获取该门店的天梯信息
|
// 如果有门店,获取该门店的天梯信息
|
||||||
if (app.globalData.currentStore?.storeId) {
|
if (app.globalData.currentStore && app.globalData.currentStore.storeId) {
|
||||||
await app.getLadderUser(app.globalData.currentStore.storeId)
|
await app.getLadderUser(app.globalData.currentStore.storeId);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('获取门店/天梯信息失败:', e)
|
console.error("获取门店/天梯信息失败:", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.refreshData()
|
this.refreshData();
|
||||||
},
|
},
|
||||||
|
|
||||||
refreshData() {
|
refreshData() {
|
||||||
this.setData({
|
this.setData({
|
||||||
userInfo: app.globalData.userInfo,
|
userInfo: app.globalData.userInfo,
|
||||||
ladderUser: app.globalData.ladderUser,
|
ladderUser: app.globalData.ladderUser,
|
||||||
currentStore: app.globalData.currentStore
|
currentStore: app.globalData.currentStore,
|
||||||
})
|
});
|
||||||
|
|
||||||
if (app.globalData.ladderUser) {
|
if (app.globalData.ladderUser) {
|
||||||
this.fetchOngoingMatches()
|
this.fetchOngoingMatches();
|
||||||
this.fetchPendingGames()
|
this.fetchPendingGames();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// 获取正在进行中的比赛
|
// 获取正在进行中的比赛
|
||||||
async fetchOngoingMatches() {
|
async fetchOngoingMatches() {
|
||||||
try {
|
try {
|
||||||
const res = await app.request('/api/match/ongoing', {
|
const res = await app.request("/api/match/ongoing", {
|
||||||
store_id: this.data.currentStore?.storeId
|
store_id: this.data.currentStore
|
||||||
})
|
? this.data.currentStore.storeId
|
||||||
this.setData({ ongoingMatches: res.data || [] })
|
: null,
|
||||||
|
});
|
||||||
|
this.setData({ ongoingMatches: res.data || [] });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('获取进行中比赛失败:', e)
|
console.error("获取进行中比赛失败:", e);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// 手动刷新天梯信息
|
// 手动刷新天梯信息
|
||||||
async refreshLadderInfo() {
|
async refreshLadderInfo() {
|
||||||
wx.showLoading({ title: '刷新中...' })
|
wx.showLoading({ title: "刷新中..." });
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 重新获取门店信息
|
// 重新获取门店信息
|
||||||
await app.getCurrentStore()
|
await app.getCurrentStore();
|
||||||
|
|
||||||
// 重新获取天梯信息
|
// 重新获取天梯信息
|
||||||
if (app.globalData.currentStore?.storeId) {
|
if (app.globalData.currentStore && app.globalData.currentStore.storeId) {
|
||||||
await app.getLadderUser(app.globalData.currentStore.storeId)
|
await app.getLadderUser(app.globalData.currentStore.storeId);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.refreshData()
|
this.refreshData();
|
||||||
wx.hideLoading()
|
wx.hideLoading();
|
||||||
|
|
||||||
if (app.globalData.ladderUser) {
|
if (app.globalData.ladderUser) {
|
||||||
wx.showToast({ title: '已加入天梯', icon: 'success' })
|
wx.showToast({ title: "已加入天梯", icon: "success" });
|
||||||
} else {
|
} else {
|
||||||
wx.showToast({ title: '暂未开通天梯', icon: 'none' })
|
wx.showToast({ title: "暂未开通天梯", icon: "none" });
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
wx.hideLoading()
|
wx.hideLoading();
|
||||||
console.error('刷新天梯信息失败:', e)
|
console.error("刷新天梯信息失败:", e);
|
||||||
wx.showToast({ title: '刷新失败', icon: 'none' })
|
wx.showToast({ title: "刷新失败", icon: "none" });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
async fetchPendingGames() {
|
async fetchPendingGames() {
|
||||||
try {
|
try {
|
||||||
const res = await app.request('/api/match/pending-confirm', {
|
const res = await app.request("/api/match/pending-confirm", {
|
||||||
store_id: this.data.currentStore?.storeId
|
store_id: this.data.currentStore
|
||||||
})
|
? this.data.currentStore.storeId
|
||||||
this.setData({ pendingGames: res.data || [] })
|
: null,
|
||||||
|
});
|
||||||
|
this.setData({ pendingGames: res.data || [] });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('获取待确认比赛失败:', e)
|
console.error("获取待确认比赛失败:", e);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
startChallenge() {
|
startChallenge() {
|
||||||
if (!this.data.ladderUser) {
|
if (!this.data.ladderUser) {
|
||||||
wx.showToast({ title: '请先加入天梯系统', icon: 'none' })
|
wx.showToast({ title: "请先加入天梯系统", icon: "none" });
|
||||||
return
|
return;
|
||||||
|
}
|
||||||
|
if (!this.data.currentStore || !this.data.currentStore.storeId) {
|
||||||
|
wx.showToast({ title: "请先选择门店", icon: "none" });
|
||||||
|
wx.navigateTo({ url: "/pages/store/index" });
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
wx.scanCode({
|
wx.scanCode({
|
||||||
onlyFromCamera: false,
|
onlyFromCamera: false,
|
||||||
scanType: ['qrCode'],
|
scanType: ["qrCode"],
|
||||||
success: async (res) => {
|
success: async (res) => {
|
||||||
const memberCode = res.result
|
const memberCode = res.result;
|
||||||
this.checkAndChallenge(memberCode)
|
this.checkAndChallenge(memberCode);
|
||||||
},
|
},
|
||||||
fail: (err) => {
|
fail: (err) => {
|
||||||
if (err.errMsg !== 'scanCode:fail cancel') {
|
if (err.errMsg !== "scanCode:fail cancel") {
|
||||||
wx.showToast({ title: '扫码失败', icon: 'none' })
|
wx.showToast({ title: "扫码失败", icon: "none" });
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
async checkAndChallenge(memberCode) {
|
async checkAndChallenge(memberCode) {
|
||||||
wx.showLoading({ title: '检查中...' })
|
wx.showLoading({ title: "检查中..." });
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const res = await app.request(`/api/match/challenge/check/${memberCode}`, {
|
const res = await app.request(
|
||||||
store_id: this.data.currentStore.storeId
|
`/api/match/challenge/check/${memberCode}`,
|
||||||
})
|
{
|
||||||
|
store_id: this.data.currentStore.storeId,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
wx.hideLoading()
|
wx.hideLoading();
|
||||||
|
|
||||||
if (!res.data.canChallenge) {
|
if (!res.data.canChallenge) {
|
||||||
wx.showModal({
|
wx.showModal({
|
||||||
title: '无法挑战',
|
title: "无法挑战",
|
||||||
content: res.data.reason,
|
content: res.data.reason,
|
||||||
showCancel: false
|
showCancel: false,
|
||||||
})
|
});
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 显示确认弹窗
|
// 显示确认弹窗
|
||||||
const target = res.data.targetUser
|
const target = res.data.targetUser;
|
||||||
wx.showModal({
|
wx.showModal({
|
||||||
title: '确认挑战',
|
title: "确认挑战",
|
||||||
content: `确定要向 ${target.ladderUser.realName}(Lv${target.ladderUser.level}, 战力${target.ladderUser.powerScore}) 发起挑战吗?`,
|
content: `确定要向 ${target.ladderUser.realName}(Lv${target.ladderUser.level}, 战力${target.ladderUser.powerScore}) 发起挑战吗?`,
|
||||||
success: async (modalRes) => {
|
success: async (modalRes) => {
|
||||||
if (modalRes.confirm) {
|
if (modalRes.confirm) {
|
||||||
await this.createChallenge(memberCode)
|
await this.createChallenge(memberCode);
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
wx.hideLoading()
|
wx.hideLoading();
|
||||||
console.error('检查挑战失败:', e)
|
console.error("检查挑战失败:", e);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
async createChallenge(memberCode) {
|
async createChallenge(memberCode) {
|
||||||
wx.showLoading({ title: '发起挑战中...' })
|
wx.showLoading({ title: "发起挑战中..." });
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const res = await app.request('/api/match/challenge/create', {
|
const res = await app.request(
|
||||||
store_id: this.data.currentStore.storeId,
|
"/api/match/challenge/create",
|
||||||
target_member_code: memberCode
|
{
|
||||||
}, 'POST')
|
store_id: this.data.currentStore.storeId,
|
||||||
|
target_member_code: memberCode,
|
||||||
|
},
|
||||||
|
"POST",
|
||||||
|
);
|
||||||
|
|
||||||
wx.hideLoading()
|
wx.hideLoading();
|
||||||
wx.showToast({ title: '挑战已发起', icon: 'success' })
|
wx.showToast({ title: "挑战已发起", icon: "success" });
|
||||||
|
|
||||||
// 跳转到挑战赛详情页面
|
// 跳转到挑战赛详情页面
|
||||||
if (res.data && res.data.matchId) {
|
if (res.data && res.data.matchId) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
wx.navigateTo({
|
wx.navigateTo({
|
||||||
url: `/pages/match/challenge-detail/index?id=${res.data.matchId}`
|
url: `/pages/match/challenge-detail/index?id=${res.data.matchId}`,
|
||||||
})
|
});
|
||||||
}, 1500)
|
}, 1500);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
wx.hideLoading()
|
wx.hideLoading();
|
||||||
console.error('发起挑战失败:', e)
|
console.error("发起挑战失败:", e);
|
||||||
const errorMsg = e.message || e.data?.message || '发起挑战失败'
|
const errorMsg =
|
||||||
wx.showToast({ title: errorMsg, icon: 'none', duration: 2000 })
|
e.message || (e.data && e.data.message) || "发起挑战失败";
|
||||||
|
wx.showToast({ title: errorMsg, icon: "none", duration: 2000 });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
joinRankingMatch() {
|
joinRankingMatch() {
|
||||||
if (!this.data.ladderUser) {
|
if (!this.data.ladderUser) {
|
||||||
wx.showToast({ title: '请先加入天梯系统', icon: 'none' })
|
wx.showToast({ title: "请先加入天梯系统", icon: "none" });
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
wx.scanCode({
|
wx.scanCode({
|
||||||
onlyFromCamera: false,
|
onlyFromCamera: false,
|
||||||
scanType: ['qrCode'],
|
scanType: ["qrCode"],
|
||||||
success: async (res) => {
|
success: async (res) => {
|
||||||
const matchCode = res.result
|
const matchCode = res.result;
|
||||||
wx.showLoading({ title: '加入中...' })
|
wx.showLoading({ title: "加入中..." });
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const joinRes = await app.request('/api/match/ranking/join', {
|
const joinRes = await app.request(
|
||||||
match_code: matchCode
|
"/api/match/ranking/join",
|
||||||
}, 'POST')
|
{
|
||||||
|
match_code: matchCode,
|
||||||
|
},
|
||||||
|
"POST",
|
||||||
|
);
|
||||||
|
|
||||||
wx.hideLoading()
|
wx.hideLoading();
|
||||||
wx.showToast({ title: '加入成功', icon: 'success' })
|
wx.showToast({ title: "加入成功", icon: "success" });
|
||||||
|
|
||||||
// 跳转到排位赛详情
|
// 跳转到排位赛详情
|
||||||
wx.navigateTo({
|
wx.navigateTo({
|
||||||
url: `/pages/match/ranking/index?code=${matchCode}`
|
url: `/pages/match/ranking/index?code=${matchCode}`,
|
||||||
})
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
wx.hideLoading()
|
wx.hideLoading();
|
||||||
console.error('加入排位赛失败:', e)
|
console.error("加入排位赛失败:", e);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
fail: (err) => {
|
fail: (err) => {
|
||||||
if (err.errMsg !== 'scanCode:fail cancel') {
|
if (err.errMsg !== "scanCode:fail cancel") {
|
||||||
wx.showToast({ title: '扫码失败', icon: 'none' })
|
wx.showToast({ title: "扫码失败", icon: "none" });
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
goToStore() {
|
goToStore() {
|
||||||
wx.navigateTo({ url: '/pages/store/index' })
|
wx.navigateTo({ url: "/pages/store/index" });
|
||||||
},
|
},
|
||||||
|
|
||||||
// 跳转到比赛详情
|
// 跳转到比赛详情
|
||||||
goToMatchDetail(e) {
|
goToMatchDetail(e) {
|
||||||
const match = e.currentTarget.dataset.match
|
const match = e.currentTarget.dataset.match;
|
||||||
if (match.type === 1) {
|
if (match.type === 1) {
|
||||||
// 挑战赛详情
|
// 挑战赛详情
|
||||||
wx.navigateTo({
|
wx.navigateTo({
|
||||||
url: `/pages/match/challenge-detail/index?id=${match.id}`
|
url: `/pages/match/challenge-detail/index?id=${match.id}`,
|
||||||
})
|
});
|
||||||
} else {
|
} else {
|
||||||
// 排位赛详情
|
// 排位赛详情
|
||||||
wx.navigateTo({
|
wx.navigateTo({
|
||||||
url: `/pages/match/ranking/index?code=${match.matchCode}`
|
url: `/pages/match/ranking/index?code=${match.matchCode}`,
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
confirmGame(e) {
|
confirmGame(e) {
|
||||||
const game = e.currentTarget.dataset.game
|
const game = e.currentTarget.dataset.game;
|
||||||
|
|
||||||
wx.showModal({
|
wx.showModal({
|
||||||
title: '确认比分',
|
title: "确认比分",
|
||||||
content: `确认比分 ${game.myScore} : ${game.opponentScore} 吗?`,
|
content: `确认比分 ${game.myScore} : ${game.opponentScore} 吗?`,
|
||||||
confirmText: '确认',
|
confirmText: "确认",
|
||||||
cancelText: '有争议',
|
cancelText: "有争议",
|
||||||
success: async (res) => {
|
success: async (res) => {
|
||||||
wx.showLoading({ title: '处理中...' })
|
wx.showLoading({ title: "处理中..." });
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await app.request('/api/match/challenge/confirm-score', {
|
await app.request(
|
||||||
game_id: game.id,
|
"/api/match/challenge/confirm-score",
|
||||||
confirm: res.confirm
|
{
|
||||||
}, 'POST')
|
game_id: game.id,
|
||||||
|
confirm: res.confirm,
|
||||||
|
},
|
||||||
|
"POST",
|
||||||
|
);
|
||||||
|
|
||||||
wx.hideLoading()
|
wx.hideLoading();
|
||||||
wx.showToast({
|
wx.showToast({
|
||||||
title: res.confirm ? '确认成功' : '已标记争议',
|
title: res.confirm ? "确认成功" : "已标记争议",
|
||||||
icon: 'success'
|
icon: "success",
|
||||||
})
|
});
|
||||||
this.fetchPendingGames()
|
this.fetchPendingGames();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
wx.hideLoading()
|
wx.hideLoading();
|
||||||
console.error('确认比分失败:', e)
|
console.error("确认比分失败:", e);
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
|
|||||||
@ -9,20 +9,22 @@
|
|||||||
<view class="main-content">
|
<view class="main-content">
|
||||||
<!-- 页面标题 -->
|
<!-- 页面标题 -->
|
||||||
<view class="page-header">
|
<view class="page-header">
|
||||||
<text class="page-title">🏸 发起挑战</text>
|
<text class="page-title">发起挑战</text>
|
||||||
<text class="page-subtitle">扫描对手会员码,开启对决</text>
|
<text class="page-subtitle">扫描对手会员码,开启对决</text>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 当前门店 -->
|
<!-- 当前门店 -->
|
||||||
<view class="store-bar" wx:if="{{currentStore}}" bindtap="goToStore">
|
<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-name">{{currentStore.storeName}}</text>
|
||||||
<text class="store-arrow">›</text>
|
<text class="store-arrow">›</text>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 未登录或非天梯用户提示 -->
|
<!-- 未登录或非天梯用户提示 -->
|
||||||
<view class="notice-card animate-fadeInUp" wx:if="{{!ladderUser}}">
|
<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">
|
<view class="notice-content">
|
||||||
<text class="notice-title">暂未开通天梯</text>
|
<text class="notice-title">暂未开通天梯</text>
|
||||||
<text class="notice-desc">请联系门店工作人员加入天梯系统</text>
|
<text class="notice-desc">请联系门店工作人员加入天梯系统</text>
|
||||||
@ -67,7 +69,7 @@
|
|||||||
<view class="scan-grid animate-fadeInUp" style="animation-delay: 0.1s">
|
<view class="scan-grid animate-fadeInUp" style="animation-delay: 0.1s">
|
||||||
<view class="scan-card challenge" bindtap="startChallenge">
|
<view class="scan-card challenge" bindtap="startChallenge">
|
||||||
<view class="scan-icon-wrapper">
|
<view class="scan-icon-wrapper">
|
||||||
<text class="scan-icon">⚔️</text>
|
<image class="scan-icon-img" src="/images/icon-challenge.svg" mode="aspectFit"></image>
|
||||||
</view>
|
</view>
|
||||||
<text class="scan-title">挑战赛</text>
|
<text class="scan-title">挑战赛</text>
|
||||||
<text class="scan-desc">1v1 对决</text>
|
<text class="scan-desc">1v1 对决</text>
|
||||||
@ -76,7 +78,7 @@
|
|||||||
|
|
||||||
<view class="scan-card ranking" bindtap="joinRankingMatch">
|
<view class="scan-card ranking" bindtap="joinRankingMatch">
|
||||||
<view class="scan-icon-wrapper">
|
<view class="scan-icon-wrapper">
|
||||||
<text class="scan-icon">🏆</text>
|
<image class="scan-icon-img" src="/images/icon-ranking.svg" mode="aspectFit"></image>
|
||||||
</view>
|
</view>
|
||||||
<text class="scan-title">排位赛</text>
|
<text class="scan-title">排位赛</text>
|
||||||
<text class="scan-desc">多人竞技</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-card animate-fadeInUp" style="animation-delay: 0.12s" wx:if="{{ongoingMatches.length > 0}}">
|
||||||
<view class="ongoing-header">
|
<view class="ongoing-header">
|
||||||
<view class="ongoing-header-left">
|
<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>
|
<text class="ongoing-title">进行中的比赛</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="ongoing-count">{{ongoingMatches.length}}</view>
|
<view class="ongoing-count">{{ongoingMatches.length}}</view>
|
||||||
@ -134,9 +136,9 @@
|
|||||||
<text class="current-name">{{item.opponent.realName}}</text>
|
<text class="current-name">{{item.opponent.realName}}</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="my-status {{item.myStatus}}">
|
<view class="my-status {{item.myStatus}}">
|
||||||
<text wx:if="{{item.myStatus === 'waiting'}}">⏳ 等待中</text>
|
<text wx:if="{{item.myStatus === 'waiting'}}">等待中</text>
|
||||||
<text wx:elif="{{item.myStatus === 'playing'}}">🎾 比赛中</text>
|
<text wx:elif="{{item.myStatus === 'playing'}}">比赛中</text>
|
||||||
<text wx:else>✅ 已完成</text>
|
<text wx:else>已完成</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</block>
|
</block>
|
||||||
@ -152,7 +154,7 @@
|
|||||||
<!-- 待确认比赛 -->
|
<!-- 待确认比赛 -->
|
||||||
<view class="pending-card animate-fadeInUp" style="animation-delay: 0.15s" wx:if="{{pendingGames.length > 0}}">
|
<view class="pending-card animate-fadeInUp" style="animation-delay: 0.15s" wx:if="{{pendingGames.length > 0}}">
|
||||||
<view class="pending-header">
|
<view class="pending-header">
|
||||||
<text class="pending-title">📋 待确认比分</text>
|
<text class="pending-title">待确认比分</text>
|
||||||
<view class="pending-count">{{pendingGames.length}}</view>
|
<view class="pending-count">{{pendingGames.length}}</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="pending-list">
|
<view class="pending-list">
|
||||||
@ -171,7 +173,9 @@
|
|||||||
<!-- 战力值规则 -->
|
<!-- 战力值规则 -->
|
||||||
<view class="rules-card animate-fadeInUp" style="animation-delay: 0.2s">
|
<view class="rules-card animate-fadeInUp" style="animation-delay: 0.2s">
|
||||||
<view class="rules-header">
|
<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>
|
<text class="rules-title">战力值规则</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="rules-grid">
|
<view class="rules-grid">
|
||||||
@ -197,7 +201,9 @@
|
|||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="rule-item">
|
<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">
|
<view class="rule-text">
|
||||||
<text class="rule-label">新手保护</text>
|
<text class="rule-label">新手保护</text>
|
||||||
<text class="rule-value">输分减半</text>
|
<text class="rule-value">输分减半</text>
|
||||||
@ -205,7 +211,7 @@
|
|||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="rules-note">
|
<view class="rules-note">
|
||||||
💡 同一对手30天内仅限挑战1次
|
提示:同一对手30天内仅限挑战1次
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|||||||
@ -4,7 +4,12 @@
|
|||||||
|
|
||||||
.page-container {
|
.page-container {
|
||||||
min-height: 100vh;
|
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;
|
position: relative;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
@ -42,7 +47,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@keyframes spin {
|
@keyframes spin {
|
||||||
to { transform: translateX(-50%) rotate(360deg); }
|
to {
|
||||||
|
transform: translateX(-50%) rotate(360deg);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 主要内容 */
|
/* 主要内容 */
|
||||||
@ -63,7 +70,7 @@
|
|||||||
display: block;
|
display: block;
|
||||||
font-size: 52rpx;
|
font-size: 52rpx;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
color: #1a1a1a;
|
color: var(--text-primary);
|
||||||
margin-bottom: 12rpx;
|
margin-bottom: 12rpx;
|
||||||
letter-spacing: 1rpx;
|
letter-spacing: 1rpx;
|
||||||
}
|
}
|
||||||
@ -71,7 +78,7 @@
|
|||||||
.page-subtitle {
|
.page-subtitle {
|
||||||
display: block;
|
display: block;
|
||||||
font-size: 26rpx;
|
font-size: 26rpx;
|
||||||
color: #999;
|
color: var(--text-muted);
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
letter-spacing: 0.5rpx;
|
letter-spacing: 0.5rpx;
|
||||||
}
|
}
|
||||||
@ -88,11 +95,12 @@
|
|||||||
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04);
|
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04);
|
||||||
margin: 0 auto 24rpx;
|
margin: 0 auto 24rpx;
|
||||||
width: fit-content;
|
width: fit-content;
|
||||||
border: 1rpx solid rgba(0, 0, 0, 0.04);
|
border: 1rpx solid var(--border-soft);
|
||||||
}
|
}
|
||||||
|
|
||||||
.store-icon {
|
.store-icon {
|
||||||
font-size: 28rpx;
|
width: 28rpx;
|
||||||
|
height: 28rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.store-name {
|
.store-name {
|
||||||
@ -117,8 +125,12 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 20rpx;
|
gap: 20rpx;
|
||||||
padding: 28rpx 24rpx;
|
padding: 28rpx 24rpx;
|
||||||
background: linear-gradient(135deg, #FFF9F5 0%, #FFFFFF 100%);
|
background: linear-gradient(
|
||||||
border: 2rpx solid #FFE8D5;
|
135deg,
|
||||||
|
var(--primary-soft) 0%,
|
||||||
|
var(--bg-white) 100%
|
||||||
|
);
|
||||||
|
border: 2rpx solid var(--border-primary);
|
||||||
border-radius: 24rpx;
|
border-radius: 24rpx;
|
||||||
margin-bottom: 24rpx;
|
margin-bottom: 24rpx;
|
||||||
box-shadow: 0 4rpx 16rpx rgba(255, 152, 0, 0.1);
|
box-shadow: 0 4rpx 16rpx rgba(255, 152, 0, 0.1);
|
||||||
@ -127,12 +139,16 @@
|
|||||||
.notice-icon {
|
.notice-icon {
|
||||||
width: 80rpx;
|
width: 80rpx;
|
||||||
height: 80rpx;
|
height: 80rpx;
|
||||||
background: linear-gradient(135deg, #FFF3E0, #FFE0B2);
|
background: var(--primary-gradient-soft);
|
||||||
border-radius: 20rpx;
|
border-radius: 20rpx;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
font-size: 40rpx;
|
}
|
||||||
|
|
||||||
|
.notice-icon-img {
|
||||||
|
width: 44rpx;
|
||||||
|
height: 44rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.notice-content {
|
.notice-content {
|
||||||
@ -143,21 +159,21 @@
|
|||||||
display: block;
|
display: block;
|
||||||
font-size: 30rpx;
|
font-size: 30rpx;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
color: #E65100;
|
color: var(--primary-dark);
|
||||||
margin-bottom: 6rpx;
|
margin-bottom: 6rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.notice-desc {
|
.notice-desc {
|
||||||
display: block;
|
display: block;
|
||||||
font-size: 24rpx;
|
font-size: 24rpx;
|
||||||
color: #F57C00;
|
color: var(--primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.notice-action {
|
.notice-action {
|
||||||
padding: 16rpx 28rpx;
|
padding: 16rpx 28rpx;
|
||||||
background: linear-gradient(135deg, #FF8A65, #FF6B35);
|
background: var(--primary-gradient);
|
||||||
border-radius: 50rpx;
|
border-radius: 50rpx;
|
||||||
box-shadow: 0 4rpx 12rpx rgba(255, 107, 53, 0.3);
|
box-shadow: var(--shadow-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.notice-action:active {
|
.notice-action:active {
|
||||||
@ -174,13 +190,17 @@
|
|||||||
用户信息卡片 - 全新设计
|
用户信息卡片 - 全新设计
|
||||||
========================================== */
|
========================================== */
|
||||||
.user-card {
|
.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;
|
border-radius: 28rpx;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
margin-bottom: 24rpx;
|
margin-bottom: 24rpx;
|
||||||
box-shadow: 0 12rpx 40rpx rgba(0, 0, 0, 0.08);
|
box-shadow: 0 12rpx 40rpx rgba(0, 0, 0, 0.08);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
border: 1rpx solid rgba(255, 107, 53, 0.1);
|
border: 1rpx solid var(--border-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.user-card-inner {
|
.user-card-inner {
|
||||||
@ -192,13 +212,13 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.user-card-inner::before {
|
.user-card-inner::before {
|
||||||
content: '';
|
content: "";
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
height: 6rpx;
|
height: 6rpx;
|
||||||
background: linear-gradient(90deg, #FF8A65, #FF6B35, #FFB74D);
|
background: var(--primary-gradient);
|
||||||
}
|
}
|
||||||
|
|
||||||
.user-avatar-box {
|
.user-avatar-box {
|
||||||
@ -209,10 +229,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.user-avatar-box::before {
|
.user-avatar-box::before {
|
||||||
content: '';
|
content: "";
|
||||||
position: absolute;
|
position: absolute;
|
||||||
inset: -6rpx;
|
inset: -6rpx;
|
||||||
background: linear-gradient(135deg, #FF8A65, #FFB74D);
|
background: var(--primary-gradient);
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
z-index: 0;
|
z-index: 0;
|
||||||
}
|
}
|
||||||
@ -253,11 +273,26 @@
|
|||||||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
|
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.user-level.lv1 { background: linear-gradient(135deg, #81C784, #66BB6A); color: #fff; }
|
.user-level.lv1 {
|
||||||
.user-level.lv2 { background: linear-gradient(135deg, #64B5F6, #42A5F5); color: #fff; }
|
background: linear-gradient(135deg, #81c784, #66bb6a);
|
||||||
.user-level.lv3 { background: linear-gradient(135deg, #FFB74D, #FFA726); color: #fff; }
|
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.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 {
|
.user-stats-row {
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -278,7 +313,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.mini-stat-value.win {
|
.mini-stat-value.win {
|
||||||
color: #00C853;
|
color: #00c853;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mini-stat-label {
|
.mini-stat-label {
|
||||||
@ -318,7 +353,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.scan-card::before {
|
.scan-card::before {
|
||||||
content: '';
|
content: "";
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
@ -329,11 +364,19 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.scan-card.challenge::before {
|
.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 {
|
.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 {
|
.scan-card:active {
|
||||||
@ -357,15 +400,16 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.scan-card.challenge .scan-icon-wrapper {
|
.scan-card.challenge .scan-icon-wrapper {
|
||||||
background: linear-gradient(135deg, #FFE8DD, #FFCCBC);
|
background: linear-gradient(135deg, #ffe8dd, #ffccbc);
|
||||||
}
|
}
|
||||||
|
|
||||||
.scan-card.ranking .scan-icon-wrapper {
|
.scan-card.ranking .scan-icon-wrapper {
|
||||||
background: linear-gradient(135deg, #FFF8E1, #FFE082);
|
background: linear-gradient(135deg, #fff8e1, #ffe082);
|
||||||
}
|
}
|
||||||
|
|
||||||
.scan-icon {
|
.scan-icon-img {
|
||||||
font-size: 52rpx;
|
width: 56rpx;
|
||||||
|
height: 56rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.scan-title {
|
.scan-title {
|
||||||
@ -387,7 +431,7 @@
|
|||||||
.scan-badge {
|
.scan-badge {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
padding: 8rpx 20rpx;
|
padding: 8rpx 20rpx;
|
||||||
background: linear-gradient(135deg, #FF8A65, #FF6B35);
|
background: linear-gradient(135deg, #ff8a65, #ff6b35);
|
||||||
color: #fff;
|
color: #fff;
|
||||||
font-size: 22rpx;
|
font-size: 22rpx;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
@ -396,8 +440,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.scan-badge.accent {
|
.scan-badge.accent {
|
||||||
background: linear-gradient(135deg, #FFD54F, #FFB300);
|
background: linear-gradient(135deg, #ffd54f, #ffb300);
|
||||||
color: #5D4037;
|
color: #5d4037;
|
||||||
box-shadow: 0 4rpx 12rpx rgba(255, 179, 0, 0.3);
|
box-shadow: 0 4rpx 12rpx rgba(255, 179, 0, 0.3);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -418,7 +462,7 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
padding: 20rpx 24rpx;
|
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);
|
border-bottom: 1rpx solid rgba(255, 152, 0, 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -428,8 +472,10 @@
|
|||||||
gap: 10rpx;
|
gap: 10rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ongoing-icon {
|
.ongoing-icon-img {
|
||||||
font-size: 28rpx;
|
width: 28rpx;
|
||||||
|
height: 28rpx;
|
||||||
|
opacity: 0.9;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ongoing-title {
|
.ongoing-title {
|
||||||
@ -442,7 +488,7 @@
|
|||||||
min-width: 40rpx;
|
min-width: 40rpx;
|
||||||
height: 40rpx;
|
height: 40rpx;
|
||||||
padding: 0 14rpx;
|
padding: 0 14rpx;
|
||||||
background: linear-gradient(135deg, #FF5722, #FF8A65);
|
background: linear-gradient(135deg, #ff5722, #ff8a65);
|
||||||
color: #fff;
|
color: #fff;
|
||||||
font-size: 24rpx;
|
font-size: 24rpx;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
@ -458,7 +504,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.ongoing-item {
|
.ongoing-item {
|
||||||
background: linear-gradient(135deg, #FAFAFA, #F5F5F5);
|
background: linear-gradient(135deg, #fafafa, #f5f5f5);
|
||||||
border-radius: 20rpx;
|
border-radius: 20rpx;
|
||||||
margin-bottom: 16rpx;
|
margin-bottom: 16rpx;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
@ -498,13 +544,13 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.match-type-tag.challenge {
|
.match-type-tag.challenge {
|
||||||
background: linear-gradient(135deg, #FFE8DD, #FFCCBC);
|
background: linear-gradient(135deg, #ffe8dd, #ffccbc);
|
||||||
color: #E65100;
|
color: #e65100;
|
||||||
}
|
}
|
||||||
|
|
||||||
.match-type-tag.ranking {
|
.match-type-tag.ranking {
|
||||||
background: linear-gradient(135deg, #FFF8E1, #FFE082);
|
background: linear-gradient(135deg, #fff8e1, #ffe082);
|
||||||
color: #F57C00;
|
color: #f57c00;
|
||||||
}
|
}
|
||||||
|
|
||||||
.match-status-tag {
|
.match-status-tag {
|
||||||
@ -515,19 +561,24 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.match-status-tag.waiting {
|
.match-status-tag.waiting {
|
||||||
background: #E3F2FD;
|
background: #e3f2fd;
|
||||||
color: #1565C0;
|
color: #1565c0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.match-status-tag.playing {
|
.match-status-tag.playing {
|
||||||
background: #E8F5E9;
|
background: #e8f5e9;
|
||||||
color: #2E7D32;
|
color: #2e7d32;
|
||||||
animation: pulse 2s infinite;
|
animation: pulse 2s infinite;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes pulse {
|
@keyframes pulse {
|
||||||
0%, 100% { opacity: 1; }
|
0%,
|
||||||
50% { opacity: 0.7; }
|
100% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.ongoing-item-body {
|
.ongoing-item-body {
|
||||||
@ -545,7 +596,7 @@
|
|||||||
width: 80rpx;
|
width: 80rpx;
|
||||||
height: 80rpx;
|
height: 80rpx;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
border: 3rpx solid #FFE0B2;
|
border: 3rpx solid #ffe0b2;
|
||||||
}
|
}
|
||||||
|
|
||||||
.opponent-detail {
|
.opponent-detail {
|
||||||
@ -567,7 +618,7 @@
|
|||||||
|
|
||||||
.opponent-level {
|
.opponent-level {
|
||||||
padding: 4rpx 12rpx;
|
padding: 4rpx 12rpx;
|
||||||
background: linear-gradient(135deg, #FFB74D, #FFA726);
|
background: linear-gradient(135deg, #ffb74d, #ffa726);
|
||||||
color: #fff;
|
color: #fff;
|
||||||
font-size: 20rpx;
|
font-size: 20rpx;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
@ -601,8 +652,8 @@
|
|||||||
|
|
||||||
.ranking-stage {
|
.ranking-stage {
|
||||||
padding: 4rpx 12rpx;
|
padding: 4rpx 12rpx;
|
||||||
background: #E3F2FD;
|
background: #e3f2fd;
|
||||||
color: #1565C0;
|
color: #1565c0;
|
||||||
border-radius: 6rpx;
|
border-radius: 6rpx;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
@ -633,11 +684,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.my-status.waiting {
|
.my-status.waiting {
|
||||||
color: #1565C0;
|
color: #1565c0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.my-status.playing {
|
.my-status.playing {
|
||||||
color: #2E7D32;
|
color: #2e7d32;
|
||||||
}
|
}
|
||||||
|
|
||||||
.my-status.finished {
|
.my-status.finished {
|
||||||
@ -655,7 +706,7 @@
|
|||||||
|
|
||||||
.match-weight {
|
.match-weight {
|
||||||
padding: 4rpx 12rpx;
|
padding: 4rpx 12rpx;
|
||||||
background: linear-gradient(135deg, #FF8A65, #FF6B35);
|
background: linear-gradient(135deg, #ff8a65, #ff6b35);
|
||||||
color: #fff;
|
color: #fff;
|
||||||
font-size: 20rpx;
|
font-size: 20rpx;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
@ -685,7 +736,7 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
padding: 20rpx 24rpx;
|
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);
|
border-bottom: 1rpx solid rgba(255, 107, 53, 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -699,7 +750,7 @@
|
|||||||
min-width: 40rpx;
|
min-width: 40rpx;
|
||||||
height: 40rpx;
|
height: 40rpx;
|
||||||
padding: 0 14rpx;
|
padding: 0 14rpx;
|
||||||
background: linear-gradient(135deg, #FF8A65, #FF6B35);
|
background: linear-gradient(135deg, #ff8a65, #ff6b35);
|
||||||
color: #fff;
|
color: #fff;
|
||||||
font-size: 24rpx;
|
font-size: 24rpx;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
@ -718,7 +769,7 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 18rpx 20rpx;
|
padding: 18rpx 20rpx;
|
||||||
background: linear-gradient(135deg, #FAFAFA, #F5F5F5);
|
background: linear-gradient(135deg, #fafafa, #f5f5f5);
|
||||||
border-radius: 16rpx;
|
border-radius: 16rpx;
|
||||||
margin-bottom: 12rpx;
|
margin-bottom: 12rpx;
|
||||||
transition: all 0.2s;
|
transition: all 0.2s;
|
||||||
@ -729,7 +780,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.pending-item:active {
|
.pending-item:active {
|
||||||
background: linear-gradient(135deg, #F5F5F5, #EEEEEE);
|
background: linear-gradient(135deg, #f5f5f5, #eeeeee);
|
||||||
}
|
}
|
||||||
|
|
||||||
.game-info {
|
.game-info {
|
||||||
@ -741,7 +792,7 @@
|
|||||||
|
|
||||||
.vs-tag {
|
.vs-tag {
|
||||||
padding: 6rpx 14rpx;
|
padding: 6rpx 14rpx;
|
||||||
background: linear-gradient(135deg, #FF8A65, #FF6B35);
|
background: linear-gradient(135deg, #ff8a65, #ff6b35);
|
||||||
color: #fff;
|
color: #fff;
|
||||||
font-size: 20rpx;
|
font-size: 20rpx;
|
||||||
font-weight: 800;
|
font-weight: 800;
|
||||||
@ -759,12 +810,12 @@
|
|||||||
font-weight: 800;
|
font-weight: 800;
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
padding: 0 20rpx;
|
padding: 0 20rpx;
|
||||||
font-family: 'SF Mono', 'Monaco', monospace;
|
font-family: "SF Mono", "Monaco", monospace;
|
||||||
}
|
}
|
||||||
|
|
||||||
.confirm-btn {
|
.confirm-btn {
|
||||||
padding: 14rpx 28rpx;
|
padding: 14rpx 28rpx;
|
||||||
background: linear-gradient(135deg, #00C853, #00E676);
|
background: linear-gradient(135deg, #00c853, #00e676);
|
||||||
color: #fff;
|
color: #fff;
|
||||||
font-size: 24rpx;
|
font-size: 24rpx;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
@ -798,12 +849,16 @@
|
|||||||
.rules-icon {
|
.rules-icon {
|
||||||
width: 48rpx;
|
width: 48rpx;
|
||||||
height: 48rpx;
|
height: 48rpx;
|
||||||
background: linear-gradient(135deg, #FFF3E0, #FFE0B2);
|
background: linear-gradient(135deg, #fff3e0, #ffe0b2);
|
||||||
border-radius: 14rpx;
|
border-radius: 14rpx;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
font-size: 26rpx;
|
}
|
||||||
|
|
||||||
|
.rules-icon-img {
|
||||||
|
width: 28rpx;
|
||||||
|
height: 28rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.rules-title {
|
.rules-title {
|
||||||
@ -823,7 +878,7 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 14rpx;
|
gap: 14rpx;
|
||||||
padding: 18rpx;
|
padding: 18rpx;
|
||||||
background: linear-gradient(135deg, #FAFAFA, #F5F5F5);
|
background: linear-gradient(135deg, #fafafa, #f5f5f5);
|
||||||
border-radius: 18rpx;
|
border-radius: 18rpx;
|
||||||
transition: all 0.2s;
|
transition: all 0.2s;
|
||||||
}
|
}
|
||||||
@ -844,24 +899,29 @@
|
|||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.rule-icon-img {
|
||||||
|
width: 28rpx;
|
||||||
|
height: 28rpx;
|
||||||
|
}
|
||||||
|
|
||||||
.rule-icon.win {
|
.rule-icon.win {
|
||||||
background: linear-gradient(135deg, #E8F5E9, #C8E6C9);
|
background: linear-gradient(135deg, #e8f5e9, #c8e6c9);
|
||||||
color: #2E7D32;
|
color: #2e7d32;
|
||||||
}
|
}
|
||||||
|
|
||||||
.rule-icon.lose {
|
.rule-icon.lose {
|
||||||
background: linear-gradient(135deg, #FFEBEE, #FFCDD2);
|
background: linear-gradient(135deg, #ffebee, #ffcdd2);
|
||||||
color: #C62828;
|
color: #c62828;
|
||||||
}
|
}
|
||||||
|
|
||||||
.rule-icon.bonus {
|
.rule-icon.bonus {
|
||||||
background: linear-gradient(135deg, #FFF8E1, #FFECB3);
|
background: linear-gradient(135deg, #fff8e1, #ffecb3);
|
||||||
color: #F57C00;
|
color: #f57c00;
|
||||||
}
|
}
|
||||||
|
|
||||||
.rule-icon.shield {
|
.rule-icon.shield {
|
||||||
background: linear-gradient(135deg, #E3F2FD, #BBDEFB);
|
background: linear-gradient(135deg, #e3f2fd, #bbdefb);
|
||||||
color: #1565C0;
|
color: #1565c0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.rule-text {
|
.rule-text {
|
||||||
@ -884,21 +944,21 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.rule-value.positive {
|
.rule-value.positive {
|
||||||
color: #2E7D32;
|
color: #2e7d32;
|
||||||
}
|
}
|
||||||
|
|
||||||
.rule-value.negative {
|
.rule-value.negative {
|
||||||
color: #C62828;
|
color: #c62828;
|
||||||
}
|
}
|
||||||
|
|
||||||
.rules-note {
|
.rules-note {
|
||||||
margin-top: 18rpx;
|
margin-top: 18rpx;
|
||||||
padding: 18rpx;
|
padding: 18rpx;
|
||||||
background: linear-gradient(135deg, #FFF8E1, #FFFDE7);
|
background: linear-gradient(135deg, #fff8e1, #fffde7);
|
||||||
border-radius: 14rpx;
|
border-radius: 14rpx;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: 24rpx;
|
font-size: 24rpx;
|
||||||
color: #F57C00;
|
color: #f57c00;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
border: 1rpx solid rgba(255, 152, 0, 0.15);
|
border: 1rpx solid rgba(255, 152, 0, 0.15);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
const app = getApp()
|
const app = getApp();
|
||||||
const util = require('../../../utils/util')
|
const util = require("../../../utils/util");
|
||||||
|
|
||||||
Page({
|
Page({
|
||||||
data: {
|
data: {
|
||||||
@ -7,89 +7,89 @@ Page({
|
|||||||
loading: false,
|
loading: false,
|
||||||
page: 1,
|
page: 1,
|
||||||
pageSize: 20,
|
pageSize: 20,
|
||||||
hasMore: true
|
hasMore: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
onLoad() {
|
onLoad() {
|
||||||
this.fetchMatches()
|
this.fetchMatches();
|
||||||
},
|
},
|
||||||
|
|
||||||
onShow() {
|
onShow() {
|
||||||
// 门店切换后刷新数据
|
// 门店切换后刷新数据
|
||||||
if (app.globalData.storeChanged) {
|
if (app.globalData.storeChanged) {
|
||||||
app.globalData.storeChanged = false
|
app.globalData.storeChanged = false;
|
||||||
this.setData({ page: 1, hasMore: true, matches: [] })
|
this.setData({ page: 1, hasMore: true, matches: [] });
|
||||||
this.fetchMatches()
|
this.fetchMatches();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
onPullDownRefresh() {
|
onPullDownRefresh() {
|
||||||
this.setData({ page: 1, hasMore: true })
|
this.setData({ page: 1, hasMore: true });
|
||||||
this.fetchMatches().then(() => {
|
this.fetchMatches().then(() => {
|
||||||
wx.stopPullDownRefresh()
|
wx.stopPullDownRefresh();
|
||||||
})
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
onReachBottom() {
|
onReachBottom() {
|
||||||
if (this.data.hasMore && !this.data.loading) {
|
if (this.data.hasMore && !this.data.loading) {
|
||||||
this.loadMore()
|
this.loadMore();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
async fetchMatches() {
|
async fetchMatches() {
|
||||||
const currentStore = app.globalData.currentStore
|
const currentStore = app.globalData.currentStore;
|
||||||
if (!currentStore?.storeId) {
|
if (!currentStore || !currentStore.storeId) {
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setData({ loading: true })
|
this.setData({ loading: true });
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const res = await app.request('/api/match/my-matches', {
|
const res = await app.request("/api/match/my-matches", {
|
||||||
store_id: currentStore.storeId,
|
store_id: currentStore.storeId,
|
||||||
page: this.data.page,
|
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 是数字类型,移除可能存在的加号和其他非数字字符
|
// 确保 powerChange 是数字类型,移除可能存在的加号和其他非数字字符
|
||||||
let powerChange = match.powerChange
|
let powerChange = match.powerChange;
|
||||||
if (powerChange != null && powerChange !== undefined) {
|
if (powerChange != null && powerChange !== undefined) {
|
||||||
// 如果是字符串,移除所有加号、空格等非数字字符(保留负号)
|
// 如果是字符串,移除所有加号、空格等非数字字符(保留负号)
|
||||||
if (typeof powerChange === 'string') {
|
if (typeof powerChange === "string") {
|
||||||
// 保留负号,移除所有加号和其他字符
|
// 保留负号,移除所有加号和其他字符
|
||||||
const cleaned = powerChange.replace(/\+/g, '').trim()
|
const cleaned = powerChange.replace(/\+/g, "").trim();
|
||||||
powerChange = parseFloat(cleaned) || 0
|
powerChange = parseFloat(cleaned) || 0;
|
||||||
}
|
}
|
||||||
// 确保是数字类型
|
// 确保是数字类型
|
||||||
powerChange = Number(powerChange)
|
powerChange = Number(powerChange);
|
||||||
// 如果是 NaN,设为 0
|
// 如果是 NaN,设为 0
|
||||||
if (isNaN(powerChange)) {
|
if (isNaN(powerChange)) {
|
||||||
powerChange = 0
|
powerChange = 0;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
powerChange = 0
|
powerChange = 0;
|
||||||
}
|
}
|
||||||
return {
|
return Object.assign({}, match, {
|
||||||
...match,
|
|
||||||
powerChange: powerChange,
|
powerChange: powerChange,
|
||||||
confirmedAt: util.formatDate(match.confirmedAt)
|
confirmedAt: util.formatDate(match.confirmedAt),
|
||||||
}
|
});
|
||||||
})
|
});
|
||||||
|
|
||||||
this.setData({
|
this.setData({
|
||||||
matches: this.data.page === 1 ? matches : [...this.data.matches, ...matches],
|
matches:
|
||||||
hasMore: matches.length >= this.data.pageSize
|
this.data.page === 1 ? matches : this.data.matches.concat(matches),
|
||||||
})
|
hasMore: matches.length >= this.data.pageSize,
|
||||||
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('获取比赛记录失败:', e)
|
console.error("获取比赛记录失败:", e);
|
||||||
} finally {
|
} finally {
|
||||||
this.setData({ loading: false })
|
this.setData({ loading: false });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
loadMore() {
|
loadMore() {
|
||||||
this.setData({ page: this.data.page + 1 })
|
this.setData({ page: this.data.page + 1 });
|
||||||
this.fetchMatches()
|
this.fetchMatches();
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
.container {
|
.container {
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
background: #f5f5f5;
|
background: var(--bg-page);
|
||||||
padding: 20rpx;
|
padding: 20rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -15,10 +15,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.match-item {
|
.match-item {
|
||||||
background: #fff;
|
background: var(--bg-card);
|
||||||
border-radius: 20rpx;
|
border-radius: 20rpx;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.08);
|
box-shadow: var(--shadow-card);
|
||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -32,7 +32,7 @@
|
|||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 24rpx 28rpx;
|
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);
|
border-bottom: 2rpx solid rgba(255, 255, 255, 0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,13 +74,13 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.result.win {
|
.result.win {
|
||||||
background: linear-gradient(135deg, #4caf50 0%, #66bb6a 100%);
|
background: linear-gradient(135deg, var(--success) 0%, #34d399 100%);
|
||||||
color: #fff;
|
color: #fff;
|
||||||
box-shadow: 0 4rpx 12rpx rgba(76, 175, 80, 0.3);
|
box-shadow: 0 4rpx 12rpx rgba(76, 175, 80, 0.3);
|
||||||
}
|
}
|
||||||
|
|
||||||
.result.lose {
|
.result.lose {
|
||||||
background: linear-gradient(135deg, #f44336 0%, #ef5350 100%);
|
background: linear-gradient(135deg, var(--danger) 0%, #f87171 100%);
|
||||||
color: #fff;
|
color: #fff;
|
||||||
box-shadow: 0 4rpx 12rpx rgba(244, 67, 54, 0.3);
|
box-shadow: 0 4rpx 12rpx rgba(244, 67, 54, 0.3);
|
||||||
}
|
}
|
||||||
@ -94,14 +94,14 @@
|
|||||||
|
|
||||||
.opponent {
|
.opponent {
|
||||||
font-size: 28rpx;
|
font-size: 28rpx;
|
||||||
color: #666;
|
color: var(--text-secondary);
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
.score {
|
.score {
|
||||||
font-size: 40rpx;
|
font-size: 40rpx;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
color: #333;
|
color: var(--text-primary);
|
||||||
letter-spacing: 4rpx;
|
letter-spacing: 4rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,13 +126,13 @@
|
|||||||
|
|
||||||
.match-footer {
|
.match-footer {
|
||||||
padding: 20rpx 28rpx;
|
padding: 20rpx 28rpx;
|
||||||
background: #fafafa;
|
background: var(--bg-card-hover);
|
||||||
border-top: 1rpx solid #f0f0f0;
|
border-top: 1rpx solid var(--border-soft);
|
||||||
}
|
}
|
||||||
|
|
||||||
.match-time {
|
.match-time {
|
||||||
font-size: 24rpx;
|
font-size: 24rpx;
|
||||||
color: #999;
|
color: var(--text-muted);
|
||||||
}
|
}
|
||||||
|
|
||||||
.empty-state {
|
.empty-state {
|
||||||
|
|||||||
@ -9,7 +9,9 @@
|
|||||||
<view class="main-content">
|
<view class="main-content">
|
||||||
<!-- 比赛头部信息 -->
|
<!-- 比赛头部信息 -->
|
||||||
<view class="match-header animate-fadeInUp">
|
<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-title">{{match.name || '排位赛'}}</view>
|
||||||
<view class="match-status status-{{match.status}}">
|
<view class="match-status status-{{match.status}}">
|
||||||
<text class="status-dot"></text>
|
<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="my-status-card animate-fadeInUp" style="animation-delay: 0.1s" wx:if="{{myPlayer}}">
|
||||||
<view class="card-header">
|
<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>
|
<text class="card-title">我的状态</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="status-content">
|
<view class="status-content">
|
||||||
<view class="status-main">
|
<view class="status-main">
|
||||||
<view class="status-badge {{myPlayer.status}}">
|
<view class="status-badge {{myPlayer.status}}">
|
||||||
<text wx:if="{{myPlayer.status === 'playing'}}">🎾 比赛中</text>
|
<text wx:if="{{myPlayer.status === 'playing'}}">比赛中</text>
|
||||||
<text wx:elif="{{myPlayer.status === 'finished'}}">✅ 已完成</text>
|
<text wx:elif="{{myPlayer.status === 'finished'}}">已完成</text>
|
||||||
<text wx:else>⏳ 等待匹配</text>
|
<text wx:else>等待匹配</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="win-lose-stats">
|
<view class="win-lose-stats">
|
||||||
<view class="stat win">
|
<view class="stat win">
|
||||||
@ -86,7 +88,7 @@
|
|||||||
<!-- 参赛选手 -->
|
<!-- 参赛选手 -->
|
||||||
<view class="players-card animate-fadeInUp" style="animation-delay: 0.15s">
|
<view class="players-card animate-fadeInUp" style="animation-delay: 0.15s">
|
||||||
<view class="card-header">
|
<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="card-title">参赛选手</text>
|
||||||
<text class="player-count">{{match.players.length || 0}}人</text>
|
<text class="player-count">{{match.players.length || 0}}人</text>
|
||||||
</view>
|
</view>
|
||||||
@ -112,7 +114,7 @@
|
|||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="empty-players" wx:else>
|
<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>
|
<text class="empty-text">暂无参赛选手</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|||||||
@ -4,7 +4,12 @@
|
|||||||
|
|
||||||
.page-container {
|
.page-container {
|
||||||
min-height: 100vh;
|
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;
|
position: relative;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
@ -42,7 +47,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@keyframes spin {
|
@keyframes spin {
|
||||||
to { transform: translateX(-50%) rotate(360deg); }
|
to {
|
||||||
|
transform: translateX(-50%) rotate(360deg);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 主要内容 */
|
/* 主要内容 */
|
||||||
@ -61,15 +68,22 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.match-badge {
|
.match-badge {
|
||||||
font-size: 64rpx;
|
width: 72rpx;
|
||||||
|
height: 72rpx;
|
||||||
|
margin: 0 auto;
|
||||||
margin-bottom: 16rpx;
|
margin-bottom: 16rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.match-badge-img {
|
||||||
|
width: 72rpx;
|
||||||
|
height: 72rpx;
|
||||||
|
}
|
||||||
|
|
||||||
.match-title {
|
.match-title {
|
||||||
display: block;
|
display: block;
|
||||||
font-size: 48rpx;
|
font-size: 48rpx;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
color: #1a1a1a;
|
color: var(--text-primary);
|
||||||
margin-bottom: 16rpx;
|
margin-bottom: 16rpx;
|
||||||
letter-spacing: 1rpx;
|
letter-spacing: 1rpx;
|
||||||
}
|
}
|
||||||
@ -83,14 +97,14 @@
|
|||||||
border-radius: 50rpx;
|
border-radius: 50rpx;
|
||||||
font-size: 26rpx;
|
font-size: 26rpx;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: #666;
|
color: var(--text-secondary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.status-dot {
|
.status-dot {
|
||||||
width: 12rpx;
|
width: 12rpx;
|
||||||
height: 12rpx;
|
height: 12rpx;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
background: #ff6b35;
|
background: var(--primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.match-status.status-1 .status-dot {
|
.match-status.status-1 .status-dot {
|
||||||
@ -98,17 +112,24 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@keyframes pulse {
|
@keyframes pulse {
|
||||||
0%, 100% { opacity: 1; transform: scale(1); }
|
0%,
|
||||||
50% { opacity: 0.5; transform: scale(1.2); }
|
100% {
|
||||||
|
opacity: 1;
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
opacity: 0.5;
|
||||||
|
transform: scale(1.2);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 信息卡片 */
|
/* 信息卡片 */
|
||||||
.info-card {
|
.info-card {
|
||||||
background: #fff;
|
background: var(--bg-card);
|
||||||
border-radius: 28rpx;
|
border-radius: 28rpx;
|
||||||
padding: 28rpx;
|
padding: 28rpx;
|
||||||
margin-bottom: 20rpx;
|
margin-bottom: 20rpx;
|
||||||
box-shadow: 0 8rpx 32rpx rgba(0, 0, 0, 0.06);
|
box-shadow: var(--shadow-card);
|
||||||
}
|
}
|
||||||
|
|
||||||
.info-grid {
|
.info-grid {
|
||||||
@ -131,7 +152,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.info-value.accent {
|
.info-value.accent {
|
||||||
color: #FF9800;
|
color: var(--warning);
|
||||||
}
|
}
|
||||||
|
|
||||||
.info-label {
|
.info-label {
|
||||||
@ -149,10 +170,22 @@
|
|||||||
margin-bottom: 8rpx;
|
margin-bottom: 8rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.stage-tag.stage-0 { background: #E3F2FD; color: #1565C0; }
|
.stage-tag.stage-0 {
|
||||||
.stage-tag.stage-1 { background: #E8F5E9; color: #2E7D32; }
|
background: #e3f2fd;
|
||||||
.stage-tag.stage-2 { background: #FFF3E0; color: #E65100; }
|
color: #1565c0;
|
||||||
.stage-tag.stage-3 { background: #ECEFF1; color: #546E7A; }
|
}
|
||||||
|
.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 {
|
.my-status-card {
|
||||||
@ -168,12 +201,13 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 12rpx;
|
gap: 12rpx;
|
||||||
padding: 20rpx 24rpx;
|
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);
|
border-bottom: 1rpx solid rgba(255, 152, 0, 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.card-icon {
|
.card-icon {
|
||||||
font-size: 28rpx;
|
width: 28rpx;
|
||||||
|
height: 28rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.card-title {
|
.card-title {
|
||||||
@ -209,18 +243,18 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.status-badge.waiting {
|
.status-badge.waiting {
|
||||||
background: linear-gradient(135deg, #E3F2FD, #BBDEFB);
|
background: linear-gradient(135deg, #e3f2fd, #bbdefb);
|
||||||
color: #1565C0;
|
color: #1565c0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.status-badge.playing {
|
.status-badge.playing {
|
||||||
background: linear-gradient(135deg, #E8F5E9, #C8E6C9);
|
background: linear-gradient(135deg, #e8f5e9, #c8e6c9);
|
||||||
color: #2E7D32;
|
color: #2e7d32;
|
||||||
}
|
}
|
||||||
|
|
||||||
.status-badge.finished {
|
.status-badge.finished {
|
||||||
background: linear-gradient(135deg, #ECEFF1, #CFD8DC);
|
background: linear-gradient(135deg, #eceff1, #cfd8dc);
|
||||||
color: #546E7A;
|
color: #546e7a;
|
||||||
}
|
}
|
||||||
|
|
||||||
.win-lose-stats {
|
.win-lose-stats {
|
||||||
@ -240,11 +274,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.stat.win .stat-num {
|
.stat.win .stat-num {
|
||||||
color: #2E7D32;
|
color: #2e7d32;
|
||||||
}
|
}
|
||||||
|
|
||||||
.stat.lose .stat-num {
|
.stat.lose .stat-num {
|
||||||
color: #C62828;
|
color: #c62828;
|
||||||
}
|
}
|
||||||
|
|
||||||
.stat-text {
|
.stat-text {
|
||||||
@ -265,10 +299,15 @@
|
|||||||
|
|
||||||
.game-divider::before,
|
.game-divider::before,
|
||||||
.game-divider::after {
|
.game-divider::after {
|
||||||
content: '';
|
content: "";
|
||||||
flex: 1;
|
flex: 1;
|
||||||
height: 1rpx;
|
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 {
|
.divider-text {
|
||||||
@ -282,7 +321,7 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 16rpx;
|
gap: 16rpx;
|
||||||
padding: 18rpx;
|
padding: 18rpx;
|
||||||
background: linear-gradient(135deg, #FFF8E1, #FFFDE7);
|
background: linear-gradient(135deg, #fff8e1, #fffde7);
|
||||||
border-radius: 16rpx;
|
border-radius: 16rpx;
|
||||||
border: 2rpx solid rgba(255, 152, 0, 0.15);
|
border: 2rpx solid rgba(255, 152, 0, 0.15);
|
||||||
}
|
}
|
||||||
@ -291,7 +330,7 @@
|
|||||||
width: 80rpx;
|
width: 80rpx;
|
||||||
height: 80rpx;
|
height: 80rpx;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
border: 3rpx solid #FFE082;
|
border: 3rpx solid #ffe082;
|
||||||
}
|
}
|
||||||
|
|
||||||
.opponent-info {
|
.opponent-info {
|
||||||
@ -319,11 +358,26 @@
|
|||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
.level-tag.lv1 { background: linear-gradient(135deg, #81C784, #66BB6A); color: #fff; }
|
.level-tag.lv1 {
|
||||||
.level-tag.lv2 { background: linear-gradient(135deg, #64B5F6, #42A5F5); color: #fff; }
|
background: linear-gradient(135deg, #81c784, #66bb6a);
|
||||||
.level-tag.lv3 { background: linear-gradient(135deg, #FFB74D, #FFA726); color: #fff; }
|
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.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 {
|
.opponent-power {
|
||||||
font-size: 24rpx;
|
font-size: 24rpx;
|
||||||
@ -347,7 +401,7 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 14rpx;
|
gap: 14rpx;
|
||||||
padding: 16rpx 18rpx;
|
padding: 16rpx 18rpx;
|
||||||
background: linear-gradient(135deg, #FAFAFA, #F5F5F5);
|
background: linear-gradient(135deg, #fafafa, #f5f5f5);
|
||||||
border-radius: 16rpx;
|
border-radius: 16rpx;
|
||||||
margin-bottom: 12rpx;
|
margin-bottom: 12rpx;
|
||||||
transition: all 0.2s;
|
transition: all 0.2s;
|
||||||
@ -358,7 +412,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.player-item.is-me {
|
.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);
|
border: 2rpx solid rgba(255, 152, 0, 0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -380,17 +434,17 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.player-rank.rank-1 {
|
.player-rank.rank-1 {
|
||||||
background: linear-gradient(135deg, #FFD54F, #FFB300);
|
background: linear-gradient(135deg, #ffd54f, #ffb300);
|
||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.player-rank.rank-2 {
|
.player-rank.rank-2 {
|
||||||
background: linear-gradient(135deg, #E0E0E0, #BDBDBD);
|
background: linear-gradient(135deg, #e0e0e0, #bdbdbd);
|
||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.player-rank.rank-3 {
|
.player-rank.rank-3 {
|
||||||
background: linear-gradient(135deg, #FFCC80, #FF9800);
|
background: linear-gradient(135deg, #ffcc80, #ff9800);
|
||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -418,7 +472,7 @@
|
|||||||
|
|
||||||
.player-me {
|
.player-me {
|
||||||
padding: 2rpx 10rpx;
|
padding: 2rpx 10rpx;
|
||||||
background: linear-gradient(135deg, #FF8A65, #FF6B35);
|
background: linear-gradient(135deg, #ff8a65, #ff6b35);
|
||||||
color: #fff;
|
color: #fff;
|
||||||
font-size: 18rpx;
|
font-size: 18rpx;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
@ -432,12 +486,12 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.record-win {
|
.record-win {
|
||||||
color: #2E7D32;
|
color: #2e7d32;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
.record-lose {
|
.record-lose {
|
||||||
color: #C62828;
|
color: #c62828;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -445,20 +499,20 @@
|
|||||||
width: 12rpx;
|
width: 12rpx;
|
||||||
height: 12rpx;
|
height: 12rpx;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
background: #BDBDBD;
|
background: #bdbdbd;
|
||||||
}
|
}
|
||||||
|
|
||||||
.player-status-dot.waiting {
|
.player-status-dot.waiting {
|
||||||
background: #1565C0;
|
background: #1565c0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.player-status-dot.playing {
|
.player-status-dot.playing {
|
||||||
background: #2E7D32;
|
background: #2e7d32;
|
||||||
animation: pulse 1.5s infinite;
|
animation: pulse 1.5s infinite;
|
||||||
}
|
}
|
||||||
|
|
||||||
.player-status-dot.finished {
|
.player-status-dot.finished {
|
||||||
background: #9E9E9E;
|
background: #9e9e9e;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 空状态 */
|
/* 空状态 */
|
||||||
@ -470,7 +524,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.empty-icon {
|
.empty-icon {
|
||||||
font-size: 80rpx;
|
width: 160rpx;
|
||||||
|
height: 160rpx;
|
||||||
margin-bottom: 16rpx;
|
margin-bottom: 16rpx;
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
}
|
}
|
||||||
@ -495,4 +550,3 @@
|
|||||||
.animate-fadeInUp {
|
.animate-fadeInUp {
|
||||||
animation: fadeInUp 0.5s ease-out forwards;
|
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({
|
Page({
|
||||||
data: {
|
data: {
|
||||||
@ -10,150 +10,156 @@ Page({
|
|||||||
pageSize: 20,
|
pageSize: 20,
|
||||||
hasMore: true,
|
hasMore: true,
|
||||||
showProductModal: false,
|
showProductModal: false,
|
||||||
currentProduct: null
|
currentProduct: null,
|
||||||
},
|
},
|
||||||
|
|
||||||
onLoad() {
|
onLoad() {
|
||||||
this.initData()
|
this.initData();
|
||||||
},
|
},
|
||||||
|
|
||||||
onShow() {
|
onShow() {
|
||||||
this.setData({
|
this.setData({
|
||||||
userInfo: app.globalData.userInfo,
|
userInfo: app.globalData.userInfo,
|
||||||
currentStore: app.globalData.currentStore
|
currentStore: app.globalData.currentStore,
|
||||||
})
|
});
|
||||||
|
|
||||||
// 门店切换后刷新商品
|
// 门店切换后刷新商品
|
||||||
if (app.globalData.storeChanged) {
|
if (app.globalData.storeChanged) {
|
||||||
app.globalData.storeChanged = false
|
app.globalData.storeChanged = false;
|
||||||
this.setData({ page: 1, hasMore: true, products: [] })
|
this.setData({ page: 1, hasMore: true, products: [] });
|
||||||
this.fetchProducts()
|
this.fetchProducts();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
onPullDownRefresh() {
|
onPullDownRefresh() {
|
||||||
this.setData({ page: 1, hasMore: true })
|
this.setData({ page: 1, hasMore: true });
|
||||||
this.fetchProducts().then(() => {
|
this.fetchProducts().then(() => {
|
||||||
wx.stopPullDownRefresh()
|
wx.stopPullDownRefresh();
|
||||||
})
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
onReachBottom() {
|
onReachBottom() {
|
||||||
if (this.data.hasMore && !this.data.loading) {
|
if (this.data.hasMore && !this.data.loading) {
|
||||||
this.loadMore()
|
this.loadMore();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
async initData() {
|
async initData() {
|
||||||
if (!app.globalData.token) {
|
if (!app.globalData.token) {
|
||||||
try {
|
try {
|
||||||
await app.login()
|
await app.login();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('登录失败:', e)
|
console.error("登录失败:", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setData({
|
this.setData({
|
||||||
userInfo: app.globalData.userInfo,
|
userInfo: app.globalData.userInfo,
|
||||||
currentStore: app.globalData.currentStore
|
currentStore: app.globalData.currentStore,
|
||||||
})
|
});
|
||||||
this.fetchProducts()
|
this.fetchProducts();
|
||||||
},
|
},
|
||||||
|
|
||||||
async fetchProducts() {
|
async fetchProducts() {
|
||||||
this.setData({ loading: true })
|
this.setData({ loading: true });
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const params = {
|
const params = {
|
||||||
page: this.data.page,
|
page: this.data.page,
|
||||||
pageSize: this.data.pageSize
|
pageSize: this.data.pageSize,
|
||||||
}
|
};
|
||||||
|
|
||||||
// 根据当前门店筛选商品
|
// 根据当前门店筛选商品
|
||||||
if (this.data.currentStore?.storeId) {
|
if (this.data.currentStore && this.data.currentStore.storeId) {
|
||||||
params.store_id = 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({
|
this.setData({
|
||||||
products: this.data.page === 1 ? products : [...this.data.products, ...products],
|
products:
|
||||||
hasMore: products.length >= this.data.pageSize
|
this.data.page === 1 ? products : this.data.products.concat(products),
|
||||||
})
|
hasMore: products.length >= this.data.pageSize,
|
||||||
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('获取商品列表失败:', e)
|
console.error("获取商品列表失败:", e);
|
||||||
} finally {
|
} finally {
|
||||||
this.setData({ loading: false })
|
this.setData({ loading: false });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
loadMore() {
|
loadMore() {
|
||||||
this.setData({ page: this.data.page + 1 })
|
this.setData({ page: this.data.page + 1 });
|
||||||
this.fetchProducts()
|
this.fetchProducts();
|
||||||
},
|
},
|
||||||
|
|
||||||
viewProduct(e) {
|
viewProduct(e) {
|
||||||
const product = e.currentTarget.dataset.product
|
const product = e.currentTarget.dataset.product;
|
||||||
this.setData({
|
this.setData({
|
||||||
currentProduct: product,
|
currentProduct: product,
|
||||||
showProductModal: true
|
showProductModal: true,
|
||||||
})
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
closeProductModal() {
|
closeProductModal() {
|
||||||
this.setData({ showProductModal: false })
|
this.setData({ showProductModal: false });
|
||||||
},
|
},
|
||||||
|
|
||||||
async exchangeProduct() {
|
async exchangeProduct() {
|
||||||
const product = this.data.currentProduct
|
const product = this.data.currentProduct;
|
||||||
|
|
||||||
wx.showModal({
|
wx.showModal({
|
||||||
title: '确认兑换',
|
title: "确认兑换",
|
||||||
content: `确定使用 ${product.pointsRequired} 积分兑换「${product.name}」吗?\n请到 ${product.storeName} 领取`,
|
content: `确定使用 ${product.pointsRequired} 积分兑换「${product.name}」吗?\n请到 ${product.storeName} 领取`,
|
||||||
success: async (res) => {
|
success: async (res) => {
|
||||||
if (!res.confirm) return
|
if (!res.confirm) return;
|
||||||
|
|
||||||
wx.showLoading({ title: '兑换中...' })
|
wx.showLoading({ title: "兑换中..." });
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const exchangeRes = await app.request('/api/points/exchange', {
|
const exchangeRes = await app.request(
|
||||||
product_id: product.id
|
"/api/points/exchange",
|
||||||
}, 'POST')
|
{
|
||||||
|
product_id: product.id,
|
||||||
|
},
|
||||||
|
"POST",
|
||||||
|
);
|
||||||
|
|
||||||
wx.hideLoading()
|
wx.hideLoading();
|
||||||
|
|
||||||
// 更新用户积分
|
// 更新用户积分
|
||||||
const newPoints = this.data.userInfo.totalPoints - product.pointsRequired
|
const newPoints =
|
||||||
app.globalData.userInfo.totalPoints = newPoints
|
this.data.userInfo.totalPoints - product.pointsRequired;
|
||||||
|
app.globalData.userInfo.totalPoints = newPoints;
|
||||||
this.setData({
|
this.setData({
|
||||||
'userInfo.totalPoints': newPoints,
|
"userInfo.totalPoints": newPoints,
|
||||||
showProductModal: false
|
showProductModal: false,
|
||||||
})
|
});
|
||||||
|
|
||||||
wx.showModal({
|
wx.showModal({
|
||||||
title: '兑换成功',
|
title: "兑换成功",
|
||||||
content: `请到 ${product.storeName} 出示兑换码领取\n兑换码: ${exchangeRes.data.exchangeCode}`,
|
content: `请到 ${product.storeName} 出示兑换码领取\n兑换码: ${exchangeRes.data.exchangeCode}`,
|
||||||
showCancel: false,
|
showCancel: false,
|
||||||
success: () => {
|
success: () => {
|
||||||
wx.navigateTo({ url: '/pages/points/order/index' })
|
wx.navigateTo({ url: "/pages/points/order/index" });
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
|
|
||||||
this.fetchProducts()
|
this.fetchProducts();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
wx.hideLoading()
|
wx.hideLoading();
|
||||||
console.error('兑换失败:', e)
|
console.error("兑换失败:", e);
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
goToRecords() {
|
goToRecords() {
|
||||||
wx.navigateTo({ url: '/pages/points/records/index' })
|
wx.navigateTo({ url: "/pages/points/records/index" });
|
||||||
},
|
},
|
||||||
|
|
||||||
goToOrders() {
|
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 res = await app.request('/api/points/orders', params)
|
||||||
|
|
||||||
const orders = (res.data.list || []).map(order => ({
|
const orders = (res.data.list || []).map(order =>
|
||||||
...order,
|
Object.assign({}, order, {
|
||||||
createdAt: util.formatDate(order.createdAt)
|
createdAt: util.formatDate(order.createdAt)
|
||||||
}))
|
})
|
||||||
|
)
|
||||||
|
|
||||||
this.setData({
|
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
|
hasMore: orders.length >= this.data.pageSize
|
||||||
})
|
})
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@ -81,11 +82,10 @@ Page({
|
|||||||
|
|
||||||
wx.hideLoading()
|
wx.hideLoading()
|
||||||
|
|
||||||
const orderData = {
|
const orderData = Object.assign({}, res.data, {
|
||||||
...res.data,
|
|
||||||
createdAt: util.formatDate(res.data.createdAt),
|
createdAt: util.formatDate(res.data.createdAt),
|
||||||
qrcodeImage: ''
|
qrcodeImage: ''
|
||||||
}
|
})
|
||||||
|
|
||||||
this.setData({
|
this.setData({
|
||||||
currentOrder: orderData,
|
currentOrder: orderData,
|
||||||
|
|||||||
@ -45,13 +45,14 @@ Page({
|
|||||||
pageSize: this.data.pageSize
|
pageSize: this.data.pageSize
|
||||||
})
|
})
|
||||||
|
|
||||||
const records = (res.data.list || []).map(record => ({
|
const records = (res.data.list || []).map(record =>
|
||||||
...record,
|
Object.assign({}, record, {
|
||||||
createdAt: util.formatDate(record.createdAt)
|
createdAt: util.formatDate(record.createdAt)
|
||||||
}))
|
})
|
||||||
|
)
|
||||||
|
|
||||||
this.setData({
|
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
|
hasMore: records.length >= this.data.pageSize
|
||||||
})
|
})
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|||||||
@ -47,7 +47,7 @@ module.exports = { formatDistance: formatDistance };
|
|||||||
<view class="store-section">
|
<view class="store-section">
|
||||||
<view class="section-header">
|
<view class="section-header">
|
||||||
<view class="section-title">
|
<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>
|
<text class="section-text">附近门店</text>
|
||||||
</view>
|
</view>
|
||||||
<text class="section-count">共 {{stores.length}} 家</text>
|
<text class="section-count">共 {{stores.length}} 家</text>
|
||||||
@ -70,7 +70,7 @@ module.exports = { formatDistance: formatDistance };
|
|||||||
<text class="store-item-address">{{item.address}}</text>
|
<text class="store-item-address">{{item.address}}</text>
|
||||||
<view class="store-item-meta">
|
<view class="store-item-meta">
|
||||||
<text class="store-distance" wx:if="{{item.distance}}">{{util.formatDistance(item.distance)}}</text>
|
<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>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
|
|||||||
@ -171,7 +171,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.section-icon {
|
.section-icon {
|
||||||
font-size: 28rpx;
|
width: 28rpx;
|
||||||
|
height: 28rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.section-text {
|
.section-text {
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
const app = getApp()
|
const app = getApp();
|
||||||
|
|
||||||
Page({
|
Page({
|
||||||
data: {
|
data: {
|
||||||
@ -6,41 +6,41 @@ Page({
|
|||||||
ladderUser: null,
|
ladderUser: null,
|
||||||
currentStore: null,
|
currentStore: null,
|
||||||
showQrcode: false,
|
showQrcode: false,
|
||||||
qrcodeImage: '',
|
qrcodeImage: "",
|
||||||
qrcodeLoading: false,
|
qrcodeLoading: false,
|
||||||
// 完善资料弹框
|
// 完善资料弹框
|
||||||
showProfileModal: false,
|
showProfileModal: false,
|
||||||
profileForm: {
|
profileForm: {
|
||||||
avatar: '',
|
avatar: "",
|
||||||
nickname: ''
|
nickname: "",
|
||||||
},
|
},
|
||||||
isEditMode: false // true: 编辑模式,false: 完善模式(登录时)
|
isEditMode: false, // true: 编辑模式,false: 完善模式(登录时)
|
||||||
},
|
},
|
||||||
|
|
||||||
onLoad() {
|
onLoad() {
|
||||||
this.initData()
|
this.initData();
|
||||||
},
|
},
|
||||||
|
|
||||||
onShow() {
|
onShow() {
|
||||||
// 检查门店是否切换
|
// 检查门店是否切换
|
||||||
if (app.globalData.storeChanged) {
|
if (app.globalData.storeChanged) {
|
||||||
app.globalData.storeChanged = false
|
app.globalData.storeChanged = false;
|
||||||
this.refreshData()
|
this.refreshData();
|
||||||
} else {
|
} else {
|
||||||
// 同步最新数据
|
// 同步最新数据
|
||||||
this.setData({
|
this.setData({
|
||||||
userInfo: app.globalData.userInfo,
|
userInfo: app.globalData.userInfo,
|
||||||
ladderUser: app.globalData.ladderUser,
|
ladderUser: app.globalData.ladderUser,
|
||||||
currentStore: app.globalData.currentStore
|
currentStore: app.globalData.currentStore,
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
async onPullDownRefresh() {
|
async onPullDownRefresh() {
|
||||||
try {
|
try {
|
||||||
await this.refreshData()
|
await this.refreshData();
|
||||||
} finally {
|
} finally {
|
||||||
wx.stopPullDownRefresh()
|
wx.stopPullDownRefresh();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -48,79 +48,84 @@ Page({
|
|||||||
// 先进行微信登录获取openid
|
// 先进行微信登录获取openid
|
||||||
if (!app.globalData.wxLoginInfo) {
|
if (!app.globalData.wxLoginInfo) {
|
||||||
try {
|
try {
|
||||||
await app.wxLogin()
|
await app.wxLogin();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('微信登录失败:', e)
|
console.error("微信登录失败:", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (app.globalData.token) {
|
if (app.globalData.token) {
|
||||||
await this.refreshData()
|
await this.refreshData();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
async refreshData() {
|
async refreshData() {
|
||||||
if (!app.globalData.token) return
|
if (!app.globalData.token) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await app.getUserInfo()
|
await app.getUserInfo();
|
||||||
|
|
||||||
// 如果当前门店有 ladderUserId,确保获取该门店的天梯用户信息
|
// 如果当前门店有 ladderUserId,确保获取该门店的天梯用户信息
|
||||||
if (app.globalData.currentStore?.storeId && !app.globalData.ladderUser) {
|
if (
|
||||||
|
app.globalData.currentStore &&
|
||||||
|
app.globalData.currentStore.storeId &&
|
||||||
|
!app.globalData.ladderUser
|
||||||
|
) {
|
||||||
try {
|
try {
|
||||||
await app.getLadderUser(app.globalData.currentStore.storeId)
|
await app.getLadderUser(app.globalData.currentStore.storeId);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('获取天梯用户信息失败:', e)
|
console.error("获取天梯用户信息失败:", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setData({
|
this.setData({
|
||||||
userInfo: app.globalData.userInfo,
|
userInfo: app.globalData.userInfo,
|
||||||
ladderUser: app.globalData.ladderUser,
|
ladderUser: app.globalData.ladderUser,
|
||||||
currentStore: app.globalData.currentStore
|
currentStore: app.globalData.currentStore,
|
||||||
})
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('获取用户信息失败:', e)
|
console.error("获取用户信息失败:", e);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// 获取手机号授权
|
// 获取手机号授权
|
||||||
async onGetPhoneNumber(e) {
|
async onGetPhoneNumber(e) {
|
||||||
if (e.detail.errMsg !== 'getPhoneNumber:ok') {
|
if (e.detail.errMsg !== "getPhoneNumber:ok") {
|
||||||
wx.showToast({ title: '需要授权手机号才能登录', icon: 'none' })
|
wx.showToast({ title: "需要授权手机号才能登录", icon: "none" });
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
wx.showLoading({ title: '登录中...' })
|
wx.showLoading({ title: "登录中..." });
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 如果没有微信登录信息,先登录
|
// 如果没有微信登录信息,先登录
|
||||||
if (!app.globalData.wxLoginInfo) {
|
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()
|
await app.getCurrentStore();
|
||||||
|
|
||||||
const userInfo = app.globalData.userInfo
|
const userInfo = app.globalData.userInfo;
|
||||||
|
|
||||||
this.setData({
|
this.setData({
|
||||||
userInfo: userInfo,
|
userInfo: userInfo,
|
||||||
ladderUser: app.globalData.ladderUser,
|
ladderUser: app.globalData.ladderUser,
|
||||||
currentStore: app.globalData.currentStore
|
currentStore: app.globalData.currentStore,
|
||||||
})
|
});
|
||||||
|
|
||||||
wx.hideLoading()
|
wx.hideLoading();
|
||||||
|
|
||||||
// 检查是否需要完善资料(没有头像或昵称为默认值)
|
// 检查是否需要完善资料(没有头像或昵称为默认值)
|
||||||
const needProfile = !userInfo.avatar ||
|
const needProfile =
|
||||||
userInfo.avatar === '' ||
|
!userInfo.avatar ||
|
||||||
!userInfo.nickname ||
|
userInfo.avatar === "" ||
|
||||||
userInfo.nickname === '新用户' ||
|
!userInfo.nickname ||
|
||||||
userInfo.nickname === ''
|
userInfo.nickname === "新用户" ||
|
||||||
|
userInfo.nickname === "";
|
||||||
|
|
||||||
if (needProfile) {
|
if (needProfile) {
|
||||||
// 弹出完善资料弹框
|
// 弹出完善资料弹框
|
||||||
@ -128,94 +133,101 @@ Page({
|
|||||||
showProfileModal: true,
|
showProfileModal: true,
|
||||||
isEditMode: false,
|
isEditMode: false,
|
||||||
profileForm: {
|
profileForm: {
|
||||||
avatar: userInfo.avatar || '/images/avatar-default.svg',
|
avatar: userInfo.avatar || "/images/avatar-default.svg",
|
||||||
nickname: userInfo.nickname === '新用户' ? '' : (userInfo.nickname || '')
|
nickname:
|
||||||
}
|
userInfo.nickname === "新用户" ? "" : userInfo.nickname || "",
|
||||||
})
|
},
|
||||||
wx.showToast({ title: '登录成功,请完善资料', icon: 'none' })
|
});
|
||||||
|
wx.showToast({ title: "登录成功,请完善资料", icon: "none" });
|
||||||
} else {
|
} else {
|
||||||
wx.showToast({ title: '登录成功', icon: 'success' })
|
wx.showToast({ title: "登录成功", icon: "success" });
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
wx.hideLoading()
|
wx.hideLoading();
|
||||||
console.error('登录失败:', e)
|
console.error("登录失败:", e);
|
||||||
wx.showToast({ title: e.message || '登录失败', icon: 'none' })
|
wx.showToast({ title: e.message || "登录失败", icon: "none" });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// 点击头像,打开编辑资料弹框
|
// 点击头像,打开编辑资料弹框
|
||||||
onTapAvatar() {
|
onTapAvatar() {
|
||||||
if (!this.data.userInfo?.phone) return
|
if (!this.data.userInfo || !this.data.userInfo.phone) return;
|
||||||
|
|
||||||
this.setData({
|
this.setData({
|
||||||
showProfileModal: true,
|
showProfileModal: true,
|
||||||
isEditMode: true,
|
isEditMode: true,
|
||||||
profileForm: {
|
profileForm: {
|
||||||
avatar: this.data.userInfo.avatar || '/images/avatar-default.svg',
|
avatar: this.data.userInfo.avatar || "/images/avatar-default.svg",
|
||||||
nickname: this.data.userInfo.nickname || ''
|
nickname: this.data.userInfo.nickname || "",
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
// 选择头像(新API:button open-type="chooseAvatar")
|
// 选择头像(新API:button open-type="chooseAvatar")
|
||||||
onChooseAvatarNew(e) {
|
onChooseAvatarNew(e) {
|
||||||
const avatarUrl = e.detail.avatarUrl
|
const avatarUrl = e.detail.avatarUrl;
|
||||||
this.setData({
|
this.setData({
|
||||||
'profileForm.avatar': avatarUrl
|
"profileForm.avatar": avatarUrl,
|
||||||
})
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
// 输入昵称
|
// 输入昵称
|
||||||
onNicknameInput(e) {
|
onNicknameInput(e) {
|
||||||
this.setData({
|
this.setData({
|
||||||
'profileForm.nickname': e.detail.value
|
"profileForm.nickname": e.detail.value,
|
||||||
})
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
// 确认保存资料
|
// 确认保存资料
|
||||||
async saveProfile() {
|
async saveProfile() {
|
||||||
const { avatar, nickname } = this.data.profileForm
|
const { avatar, nickname } = this.data.profileForm;
|
||||||
|
|
||||||
if (!nickname || nickname.trim() === '') {
|
if (!nickname || nickname.trim() === "") {
|
||||||
wx.showToast({ title: '请输入昵称', icon: 'none' })
|
wx.showToast({ title: "请输入昵称", icon: "none" });
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
wx.showLoading({ title: '保存中...' })
|
wx.showLoading({ title: "保存中..." });
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 如果选择了新头像,先上传
|
// 如果选择了新头像,先上传
|
||||||
let avatarUrl = avatar
|
let avatarUrl = avatar;
|
||||||
if (avatar && (avatar.startsWith('wxfile://') || avatar.startsWith('http://tmp'))) {
|
if (
|
||||||
avatarUrl = await this.uploadAvatar(avatar)
|
avatar &&
|
||||||
|
(avatar.startsWith("wxfile://") || avatar.startsWith("http://tmp"))
|
||||||
|
) {
|
||||||
|
avatarUrl = await this.uploadAvatar(avatar);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 调用更新资料接口
|
// 调用更新资料接口
|
||||||
const res = await app.request('/api/user/profile', {
|
const res = await app.request(
|
||||||
nickname: nickname.trim(),
|
"/api/user/profile",
|
||||||
avatar: avatarUrl
|
{
|
||||||
}, 'PUT')
|
nickname: nickname.trim(),
|
||||||
|
avatar: avatarUrl,
|
||||||
|
},
|
||||||
|
"PUT",
|
||||||
|
);
|
||||||
|
|
||||||
// 更新本地数据(服务端已返回完整URL)
|
// 更新本地数据(服务端已返回完整URL)
|
||||||
const userInfo = {
|
const userInfo = Object.assign({}, this.data.userInfo, {
|
||||||
...this.data.userInfo,
|
nickname: (res.data && res.data.nickname) || nickname.trim(),
|
||||||
nickname: res.data?.nickname || nickname.trim(),
|
avatar: (res.data && res.data.avatar) || avatarUrl,
|
||||||
avatar: res.data?.avatar || avatarUrl
|
});
|
||||||
}
|
app.globalData.userInfo = userInfo;
|
||||||
app.globalData.userInfo = userInfo
|
|
||||||
|
|
||||||
this.setData({
|
this.setData({
|
||||||
userInfo: userInfo,
|
userInfo: userInfo,
|
||||||
showProfileModal: false,
|
showProfileModal: false,
|
||||||
profileForm: { avatar: '', nickname: '' }
|
profileForm: { avatar: "", nickname: "" },
|
||||||
})
|
});
|
||||||
|
|
||||||
wx.hideLoading()
|
wx.hideLoading();
|
||||||
wx.showToast({ title: '保存成功', icon: 'success' })
|
wx.showToast({ title: "保存成功", icon: "success" });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
wx.hideLoading()
|
wx.hideLoading();
|
||||||
console.error('保存资料失败:', e)
|
console.error("保存资料失败:", e);
|
||||||
wx.showToast({ title: e.message || '保存失败', icon: 'none' })
|
wx.showToast({ title: e.message || "保存失败", icon: "none" });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -225,29 +237,29 @@ Page({
|
|||||||
wx.uploadFile({
|
wx.uploadFile({
|
||||||
url: `${app.globalData.baseUrl}/api/upload/avatar`,
|
url: `${app.globalData.baseUrl}/api/upload/avatar`,
|
||||||
filePath: filePath,
|
filePath: filePath,
|
||||||
name: 'file',
|
name: "file",
|
||||||
header: {
|
header: {
|
||||||
'Authorization': `Bearer ${app.globalData.token}`
|
Authorization: `Bearer ${app.globalData.token}`,
|
||||||
},
|
},
|
||||||
success: (res) => {
|
success: (res) => {
|
||||||
try {
|
try {
|
||||||
const data = JSON.parse(res.data)
|
const data = JSON.parse(res.data);
|
||||||
if (data.code === 0 && data.data?.url) {
|
if (data.code === 0 && data.data && data.data.url) {
|
||||||
resolve(data.data.url)
|
resolve(data.data.url);
|
||||||
} else {
|
} else {
|
||||||
console.error('上传头像失败:', data)
|
console.error("上传头像失败:", data);
|
||||||
resolve(filePath)
|
resolve(filePath);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
resolve(filePath)
|
resolve(filePath);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
fail: (err) => {
|
fail: (err) => {
|
||||||
console.error('上传头像失败:', err)
|
console.error("上传头像失败:", err);
|
||||||
resolve(filePath)
|
resolve(filePath);
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
})
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
// 关闭资料弹框
|
// 关闭资料弹框
|
||||||
@ -255,63 +267,63 @@ Page({
|
|||||||
// 如果是完善模式,提示用户
|
// 如果是完善模式,提示用户
|
||||||
if (!this.data.isEditMode) {
|
if (!this.data.isEditMode) {
|
||||||
wx.showModal({
|
wx.showModal({
|
||||||
title: '提示',
|
title: "提示",
|
||||||
content: '完善资料后可以让好友更容易找到你,确定跳过?',
|
content: "完善资料后可以让好友更容易找到你,确定跳过?",
|
||||||
confirmText: '跳过',
|
confirmText: "跳过",
|
||||||
cancelText: '继续完善',
|
cancelText: "继续完善",
|
||||||
success: (res) => {
|
success: (res) => {
|
||||||
if (res.confirm) {
|
if (res.confirm) {
|
||||||
this.setData({ showProfileModal: false })
|
this.setData({ showProfileModal: false });
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
} else {
|
} else {
|
||||||
this.setData({ showProfileModal: false })
|
this.setData({ showProfileModal: false });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
async showMemberCode() {
|
async showMemberCode() {
|
||||||
if (!this.data.userInfo?.memberCode) return
|
if (!this.data.userInfo || !this.data.userInfo.memberCode) return;
|
||||||
|
|
||||||
this.setData({
|
this.setData({
|
||||||
showQrcode: true,
|
showQrcode: true,
|
||||||
qrcodeLoading: true
|
qrcodeLoading: true,
|
||||||
})
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 调用接口获取二维码
|
// 调用接口获取二维码
|
||||||
const res = await app.request('/api/user/qrcode')
|
const res = await app.request("/api/user/qrcode");
|
||||||
if (res.data && res.data.qrcode) {
|
if (res.data && res.data.qrcode) {
|
||||||
this.setData({
|
this.setData({
|
||||||
qrcodeImage: res.data.qrcode,
|
qrcodeImage: res.data.qrcode,
|
||||||
qrcodeLoading: false
|
qrcodeLoading: false,
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('获取二维码失败:', e)
|
console.error("获取二维码失败:", e);
|
||||||
this.setData({ qrcodeLoading: false })
|
this.setData({ qrcodeLoading: false });
|
||||||
wx.showToast({ title: '获取二维码失败', icon: 'none' })
|
wx.showToast({ title: "获取二维码失败", icon: "none" });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
hideQrcode() {
|
hideQrcode() {
|
||||||
this.setData({
|
this.setData({
|
||||||
showQrcode: false,
|
showQrcode: false,
|
||||||
qrcodeImage: ''
|
qrcodeImage: "",
|
||||||
})
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
goTo(e) {
|
goTo(e) {
|
||||||
const url = e.currentTarget.dataset.url
|
const url = e.currentTarget.dataset.url;
|
||||||
if (!app.globalData.token) {
|
if (!app.globalData.token) {
|
||||||
wx.showToast({ title: '请先登录', icon: 'none' })
|
wx.showToast({ title: "请先登录", icon: "none" });
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
wx.navigateTo({ url })
|
wx.navigateTo({ url });
|
||||||
},
|
},
|
||||||
|
|
||||||
// 阻止事件冒泡
|
// 阻止事件冒泡
|
||||||
preventBubble() {
|
preventBubble() {
|
||||||
// 空函数,仅用于阻止事件冒泡
|
// 空函数,仅用于阻止事件冒泡
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
|
|||||||
@ -67,7 +67,7 @@
|
|||||||
<text class="login-subtitle">授权手机号,加入英飒俱乐部</text>
|
<text class="login-subtitle">授权手机号,加入英飒俱乐部</text>
|
||||||
</view>
|
</view>
|
||||||
<button class="login-btn-primary" open-type="getPhoneNumber" bindgetphonenumber="onGetPhoneNumber">
|
<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>
|
<text class="btn-text">手机号快捷登录</text>
|
||||||
</button>
|
</button>
|
||||||
</view>
|
</view>
|
||||||
@ -117,7 +117,9 @@
|
|||||||
</view>
|
</view>
|
||||||
<!-- 未加入天梯提示 -->
|
<!-- 未加入天梯提示 -->
|
||||||
<view class="notice-card animate-fadeInUp" style="animation-delay: 0.2s" wx:elif="{{userInfo && userInfo.phone}}">
|
<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">
|
<view class="notice-content">
|
||||||
<text class="notice-title">尚未加入天梯系统</text>
|
<text class="notice-title">尚未加入天梯系统</text>
|
||||||
<text class="notice-desc">请联系门店工作人员,开启你的天梯之旅</text>
|
<text class="notice-desc">请联系门店工作人员,开启你的天梯之旅</text>
|
||||||
@ -183,7 +185,7 @@
|
|||||||
</view>
|
</view>
|
||||||
<view class="qrcode-tips">
|
<view class="qrcode-tips">
|
||||||
<view class="tip-item">
|
<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>
|
<text class="tip-text">请出示给对方扫描发起挑战</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@ -199,7 +201,7 @@
|
|||||||
<view class="profile-modal-body">
|
<view class="profile-modal-body">
|
||||||
<!-- 提示信息 -->
|
<!-- 提示信息 -->
|
||||||
<view class="profile-tips" wx:if="{{!isEditMode}}">
|
<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>
|
<text class="tips-text">完善资料后,好友可以更容易找到你</text>
|
||||||
</view>
|
</view>
|
||||||
<!-- 头像选择 -->
|
<!-- 头像选择 -->
|
||||||
@ -208,7 +210,7 @@
|
|||||||
<button class="avatar-choose-btn" open-type="chooseAvatar" bindchooseavatar="onChooseAvatarNew">
|
<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>
|
<image class="profile-avatar-preview" src="{{profileForm.avatar || '/images/avatar-default.svg'}}" mode="aspectFill"></image>
|
||||||
<view class="avatar-choose-badge">
|
<view class="avatar-choose-badge">
|
||||||
<text class="choose-icon">📷</text>
|
<image class="choose-icon" src="/images/icon-scan.svg" mode="aspectFit"></image>
|
||||||
</view>
|
</view>
|
||||||
</button>
|
</button>
|
||||||
<text class="avatar-tip">点击更换头像</text>
|
<text class="avatar-tip">点击更换头像</text>
|
||||||
|
|||||||
@ -153,7 +153,7 @@
|
|||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
bottom: 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 {
|
.member-card-content {
|
||||||
@ -233,7 +233,7 @@
|
|||||||
.scan-hint {
|
.scan-hint {
|
||||||
position: relative;
|
position: relative;
|
||||||
padding: 12rpx 24rpx;
|
padding: 12rpx 24rpx;
|
||||||
background: linear-gradient(90deg, var(--primary-soft), #FFF8F5);
|
background: linear-gradient(90deg, var(--primary-soft), #fff8f5);
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: 22rpx;
|
font-size: 22rpx;
|
||||||
color: var(--primary);
|
color: var(--primary);
|
||||||
@ -321,8 +321,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.login-btn-primary .btn-icon {
|
.login-btn-primary .btn-icon {
|
||||||
font-size: 32rpx;
|
width: 32rpx;
|
||||||
|
height: 32rpx;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.login-btn-primary .btn-text {
|
.login-btn-primary .btn-text {
|
||||||
@ -360,7 +362,7 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
padding: 20rpx 24rpx;
|
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);
|
border-bottom: 1rpx solid var(--border-soft);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -397,11 +399,21 @@
|
|||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.stat-icon.lv1 { background: linear-gradient(135deg, #4CAF50, #8BC34A); }
|
.stat-icon.lv1 {
|
||||||
.stat-icon.lv2 { background: linear-gradient(135deg, #2196F3, #03A9F4); }
|
background: linear-gradient(135deg, #4caf50, #8bc34a);
|
||||||
.stat-icon.lv3 { background: linear-gradient(135deg, #FF9800, #FFC107); }
|
}
|
||||||
.stat-icon.lv4 { background: linear-gradient(135deg, #E91E63, #FF5722); }
|
.stat-icon.lv2 {
|
||||||
.stat-icon.lv5 { background: linear-gradient(135deg, #9C27B0, #673AB7); }
|
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 {
|
.stat-name {
|
||||||
font-size: 30rpx;
|
font-size: 30rpx;
|
||||||
@ -447,12 +459,17 @@
|
|||||||
|
|
||||||
.ladder-record {
|
.ladder-record {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-around;
|
justify-content: space-between;
|
||||||
padding-top: 20rpx;
|
padding-top: 20rpx;
|
||||||
border-top: 1rpx solid var(--border-soft);
|
border-top: 1rpx solid var(--border-soft);
|
||||||
}
|
}
|
||||||
|
|
||||||
.record-item {
|
.record-item {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -486,14 +503,27 @@
|
|||||||
gap: 16rpx;
|
gap: 16rpx;
|
||||||
margin: 0 24rpx 20rpx;
|
margin: 0 24rpx 20rpx;
|
||||||
padding: 24rpx;
|
padding: 24rpx;
|
||||||
background: #FFFBF5;
|
background: #fffbf5;
|
||||||
border: 1rpx solid #FFE8D5;
|
border: 1rpx solid #ffe8d5;
|
||||||
border-radius: var(--radius-lg);
|
border-radius: var(--radius-lg);
|
||||||
animation: fadeInUp 0.5s cubic-bezier(0.4, 0, 0.2, 1) 0.2s backwards;
|
animation: fadeInUp 0.5s cubic-bezier(0.4, 0, 0.2, 1) 0.2s backwards;
|
||||||
}
|
}
|
||||||
|
|
||||||
.notice-icon {
|
.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 {
|
.notice-content {
|
||||||
@ -504,14 +534,14 @@
|
|||||||
display: block;
|
display: block;
|
||||||
font-size: 26rpx;
|
font-size: 26rpx;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: #B7791F;
|
color: #b7791f;
|
||||||
margin-bottom: 6rpx;
|
margin-bottom: 6rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.notice-desc {
|
.notice-desc {
|
||||||
display: block;
|
display: block;
|
||||||
font-size: 22rpx;
|
font-size: 22rpx;
|
||||||
color: #C68A42;
|
color: #c68a42;
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -556,19 +586,19 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.menu-icon.history {
|
.menu-icon.history {
|
||||||
background: linear-gradient(135deg, #E3F2FD, #BBDEFB);
|
background: linear-gradient(135deg, #e3f2fd, #bbdefb);
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu-icon.points {
|
.menu-icon.points {
|
||||||
background: linear-gradient(135deg, #FFF8E1, #FFECB3);
|
background: linear-gradient(135deg, #fff8e1, #ffecb3);
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu-icon.order {
|
.menu-icon.order {
|
||||||
background: linear-gradient(135deg, #E8F5E9, #C8E6C9);
|
background: linear-gradient(135deg, #e8f5e9, #c8e6c9);
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu-icon.store {
|
.menu-icon.store {
|
||||||
background: linear-gradient(135deg, #FFF3E0, #FFE0B2);
|
background: linear-gradient(135deg, #fff3e0, #ffe0b2);
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu-icon image {
|
.menu-icon image {
|
||||||
@ -627,7 +657,7 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
padding: 24rpx 28rpx;
|
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);
|
border-bottom: 1rpx solid var(--border-soft);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -667,7 +697,7 @@
|
|||||||
.qrcode-border {
|
.qrcode-border {
|
||||||
width: 320rpx;
|
width: 320rpx;
|
||||||
height: 320rpx;
|
height: 320rpx;
|
||||||
background: #FFFFFF;
|
background: #ffffff;
|
||||||
border-radius: var(--radius-lg);
|
border-radius: var(--radius-lg);
|
||||||
padding: 16rpx;
|
padding: 16rpx;
|
||||||
box-shadow: var(--shadow-md);
|
box-shadow: var(--shadow-md);
|
||||||
@ -723,10 +753,30 @@
|
|||||||
border-style: solid;
|
border-style: solid;
|
||||||
}
|
}
|
||||||
|
|
||||||
.corner.tl { top: 0; left: 0; border-width: 6rpx 0 0 6rpx; border-radius: 10rpx 0 0 0; }
|
.corner.tl {
|
||||||
.corner.tr { top: 0; right: 0; border-width: 6rpx 6rpx 0 0; border-radius: 0 10rpx 0 0; }
|
top: 0;
|
||||||
.corner.bl { bottom: 0; left: 0; border-width: 0 0 6rpx 6rpx; border-radius: 0 0 0 10rpx; }
|
left: 0;
|
||||||
.corner.br { bottom: 0; right: 0; border-width: 0 6rpx 6rpx 0; border-radius: 0 0 10rpx 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 {
|
.qrcode-info {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
@ -762,7 +812,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.tip-icon {
|
.tip-icon {
|
||||||
font-size: 24rpx;
|
width: 24rpx;
|
||||||
|
height: 24rpx;
|
||||||
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tip-text {
|
.tip-text {
|
||||||
@ -837,7 +889,7 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
padding: 28rpx 32rpx;
|
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);
|
border-bottom: 1rpx solid var(--border-soft);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -874,19 +926,21 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 12rpx;
|
gap: 12rpx;
|
||||||
padding: 20rpx 24rpx;
|
padding: 20rpx 24rpx;
|
||||||
background: #FFFBF5;
|
background: #fffbf5;
|
||||||
border: 1rpx solid #FFE8D5;
|
border: 1rpx solid #ffe8d5;
|
||||||
border-radius: var(--radius-lg);
|
border-radius: var(--radius-lg);
|
||||||
margin-bottom: 32rpx;
|
margin-bottom: 32rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tips-icon {
|
.tips-icon {
|
||||||
font-size: 32rpx;
|
width: 32rpx;
|
||||||
|
height: 32rpx;
|
||||||
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tips-text {
|
.tips-text {
|
||||||
font-size: 24rpx;
|
font-size: 24rpx;
|
||||||
color: #B7791F;
|
color: #b7791f;
|
||||||
line-height: 1.4;
|
line-height: 1.4;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -948,8 +1002,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.avatar-choose-badge .choose-icon {
|
.avatar-choose-badge .choose-icon {
|
||||||
font-size: 24rpx;
|
width: 24rpx;
|
||||||
line-height: 1;
|
height: 24rpx;
|
||||||
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.avatar-tip {
|
.avatar-tip {
|
||||||
|
|||||||
@ -1,28 +1,46 @@
|
|||||||
const { LadderUser, User, Store } = require('../models');
|
const { LadderUser, User, Store } = require("../models");
|
||||||
const { LADDER_LEVEL_NAMES, LADDER_LEVEL_DESC, POWER_CALC } = require('../config/constants');
|
const {
|
||||||
const { success, error, getPagination, pageResult } = require('../utils/helper');
|
LADDER_LEVEL_NAMES,
|
||||||
const { Op } = require('sequelize');
|
LADDER_LEVEL_DESC,
|
||||||
const sequelize = require('../config/database');
|
POWER_CALC,
|
||||||
|
} = require("../config/constants");
|
||||||
|
const {
|
||||||
|
success,
|
||||||
|
error,
|
||||||
|
getPagination,
|
||||||
|
pageResult,
|
||||||
|
} = require("../utils/helper");
|
||||||
|
const { Op } = require("sequelize");
|
||||||
|
const sequelize = require("../config/database");
|
||||||
|
|
||||||
class LadderController {
|
class LadderController {
|
||||||
// 获取天梯排名
|
// 获取天梯排名
|
||||||
async getRanking(req, res) {
|
async getRanking(req, res) {
|
||||||
try {
|
try {
|
||||||
const { store_id, gender, level, page = 1, pageSize = 50, is_display } = req.query;
|
const {
|
||||||
|
store_id,
|
||||||
|
gender,
|
||||||
|
level,
|
||||||
|
page = 1,
|
||||||
|
pageSize = 50,
|
||||||
|
is_display,
|
||||||
|
} = req.query;
|
||||||
const { limit, offset } = getPagination(page, pageSize);
|
const { limit, offset } = getPagination(page, pageSize);
|
||||||
|
|
||||||
if (!store_id) {
|
if (!store_id) {
|
||||||
return res.status(400).json(error('缺少门店ID', 400));
|
return res.status(400).json(error("缺少门店ID", 400));
|
||||||
}
|
}
|
||||||
|
|
||||||
const where = {
|
const where = {
|
||||||
store_id,
|
store_id,
|
||||||
status: 1
|
status: 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
// 如果不是大屏显示,则需要满足每月最低参赛场次限制
|
// 如果不是大屏显示,则需要满足每月最低参赛场次限制
|
||||||
if (!is_display) {
|
if (!is_display) {
|
||||||
where.monthly_match_count = { [Op.gte]: POWER_CALC.MIN_MONTHLY_MATCHES };
|
where.monthly_match_count = {
|
||||||
|
[Op.gte]: POWER_CALC.MIN_MONTHLY_MATCHES,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gender) {
|
if (gender) {
|
||||||
@ -35,11 +53,11 @@ class LadderController {
|
|||||||
const { rows, count } = await LadderUser.findAndCountAll({
|
const { rows, count } = await LadderUser.findAndCountAll({
|
||||||
where,
|
where,
|
||||||
include: [
|
include: [
|
||||||
{ model: User, as: 'user', attributes: ['nickname', 'avatar'] }
|
{ model: User, as: "user", attributes: ["nickname", "avatar"] },
|
||||||
],
|
],
|
||||||
order: [['power_score', 'DESC']],
|
order: [["power_score", "DESC"]],
|
||||||
limit,
|
limit,
|
||||||
offset
|
offset,
|
||||||
});
|
});
|
||||||
|
|
||||||
// 添加排名
|
// 添加排名
|
||||||
@ -57,13 +75,16 @@ class LadderController {
|
|||||||
powerScore: lu.power_score,
|
powerScore: lu.power_score,
|
||||||
matchCount: lu.match_count,
|
matchCount: lu.match_count,
|
||||||
winCount: lu.win_count,
|
winCount: lu.win_count,
|
||||||
winRate: lu.match_count > 0 ? Math.round(lu.win_count / lu.match_count * 100) : 0
|
winRate:
|
||||||
|
lu.match_count > 0
|
||||||
|
? Math.round((lu.win_count / lu.match_count) * 100)
|
||||||
|
: 0,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
res.json(pageResult(list, count, page, pageSize));
|
res.json(pageResult(list, count, page, pageSize));
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('获取排名失败:', err);
|
console.error("获取排名失败:", err);
|
||||||
res.status(500).json(error('获取失败'));
|
res.status(500).json(error("获取失败"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,13 +95,17 @@ class LadderController {
|
|||||||
|
|
||||||
const ladderUser = await LadderUser.findByPk(id, {
|
const ladderUser = await LadderUser.findByPk(id, {
|
||||||
include: [
|
include: [
|
||||||
{ model: User, as: 'user', attributes: ['nickname', 'avatar', 'member_code'] },
|
{
|
||||||
{ model: Store, as: 'store', attributes: ['id', 'name'] }
|
model: User,
|
||||||
]
|
as: "user",
|
||||||
|
attributes: ["nickname", "avatar", "member_code"],
|
||||||
|
},
|
||||||
|
{ model: Store, as: "store", attributes: ["id", "name"] },
|
||||||
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!ladderUser || ladderUser.status !== 1) {
|
if (!ladderUser || ladderUser.status !== 1) {
|
||||||
return res.status(404).json(error('用户不存在', 404));
|
return res.status(404).json(error("用户不存在", 404));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 计算排名
|
// 计算排名
|
||||||
@ -90,62 +115,138 @@ class LadderController {
|
|||||||
gender: ladderUser.gender,
|
gender: ladderUser.gender,
|
||||||
status: 1,
|
status: 1,
|
||||||
power_score: { [Op.gt]: ladderUser.power_score },
|
power_score: { [Op.gt]: ladderUser.power_score },
|
||||||
monthly_match_count: { [Op.gte]: POWER_CALC.MIN_MONTHLY_MATCHES }
|
monthly_match_count: { [Op.gte]: POWER_CALC.MIN_MONTHLY_MATCHES },
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
res.json(success({
|
res.json(
|
||||||
id: ladderUser.id,
|
success({
|
||||||
userId: ladderUser.user_id,
|
id: ladderUser.id,
|
||||||
realName: ladderUser.real_name,
|
userId: ladderUser.user_id,
|
||||||
nickname: ladderUser.user?.nickname,
|
realName: ladderUser.real_name,
|
||||||
avatar: ladderUser.user?.avatar,
|
nickname: ladderUser.user?.nickname,
|
||||||
memberCode: ladderUser.user?.member_code,
|
avatar: ladderUser.user?.avatar,
|
||||||
gender: ladderUser.gender,
|
memberCode: ladderUser.user?.member_code,
|
||||||
level: ladderUser.level,
|
gender: ladderUser.gender,
|
||||||
levelName: LADDER_LEVEL_NAMES[ladderUser.level],
|
level: ladderUser.level,
|
||||||
levelDesc: LADDER_LEVEL_DESC[ladderUser.level],
|
levelName: LADDER_LEVEL_NAMES[ladderUser.level],
|
||||||
powerScore: ladderUser.power_score,
|
levelDesc: LADDER_LEVEL_DESC[ladderUser.level],
|
||||||
matchCount: ladderUser.match_count,
|
powerScore: ladderUser.power_score,
|
||||||
winCount: ladderUser.win_count,
|
matchCount: ladderUser.match_count,
|
||||||
monthlyMatchCount: ladderUser.monthly_match_count,
|
winCount: ladderUser.win_count,
|
||||||
winRate: ladderUser.match_count > 0 ? Math.round(ladderUser.win_count / ladderUser.match_count * 100) : 0,
|
monthlyMatchCount: ladderUser.monthly_match_count,
|
||||||
rank: higherCount + 1,
|
winRate:
|
||||||
storeId: ladderUser.store_id,
|
ladderUser.match_count > 0
|
||||||
storeName: ladderUser.store?.name,
|
? Math.round(
|
||||||
lastMatchTime: ladderUser.last_match_time
|
(ladderUser.win_count / ladderUser.match_count) * 100,
|
||||||
}));
|
)
|
||||||
|
: 0,
|
||||||
|
rank: higherCount + 1,
|
||||||
|
storeId: ladderUser.store_id,
|
||||||
|
storeName: ladderUser.store?.name,
|
||||||
|
lastMatchTime: ladderUser.last_match_time,
|
||||||
|
}),
|
||||||
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('获取用户详情失败:', err);
|
console.error("获取用户详情失败:", err);
|
||||||
res.status(500).json(error('获取失败'));
|
res.status(500).json(error("获取失败"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 选手详情(兼容小程序端:/api/ladder/player?id=xxx)
|
||||||
|
async getPlayerDetail(req, res) {
|
||||||
|
try {
|
||||||
|
const id = req.query && req.query.id ? String(req.query.id) : null;
|
||||||
|
if (!id) {
|
||||||
|
return res.status(400).json(error("缺少选手ID", 400));
|
||||||
|
}
|
||||||
|
|
||||||
|
const ladderUser = await LadderUser.findByPk(id, {
|
||||||
|
include: [
|
||||||
|
{
|
||||||
|
model: User,
|
||||||
|
as: "user",
|
||||||
|
attributes: ["nickname", "avatar", "member_code"],
|
||||||
|
},
|
||||||
|
{ model: Store, as: "store", attributes: ["id", "name"] },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!ladderUser || ladderUser.status !== 1) {
|
||||||
|
return res.status(404).json(error("用户不存在", 404));
|
||||||
|
}
|
||||||
|
|
||||||
|
const higherCount = await LadderUser.count({
|
||||||
|
where: {
|
||||||
|
store_id: ladderUser.store_id,
|
||||||
|
gender: ladderUser.gender,
|
||||||
|
status: 1,
|
||||||
|
power_score: { [Op.gt]: ladderUser.power_score },
|
||||||
|
monthly_match_count: { [Op.gte]: POWER_CALC.MIN_MONTHLY_MATCHES },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const matchCount = ladderUser.match_count || 0;
|
||||||
|
const winCount = ladderUser.win_count || 0;
|
||||||
|
|
||||||
|
res.json(
|
||||||
|
success({
|
||||||
|
id: ladderUser.id,
|
||||||
|
userId: ladderUser.user_id,
|
||||||
|
realName: ladderUser.real_name,
|
||||||
|
nickname: ladderUser.user && ladderUser.user.nickname,
|
||||||
|
avatar: ladderUser.user && ladderUser.user.avatar,
|
||||||
|
memberCode: ladderUser.user && ladderUser.user.member_code,
|
||||||
|
gender: ladderUser.gender,
|
||||||
|
level: ladderUser.level,
|
||||||
|
levelName: LADDER_LEVEL_NAMES[ladderUser.level],
|
||||||
|
levelDesc: LADDER_LEVEL_DESC[ladderUser.level],
|
||||||
|
powerScore: ladderUser.power_score,
|
||||||
|
matchCount: matchCount,
|
||||||
|
winCount: winCount,
|
||||||
|
loseCount: Math.max(matchCount - winCount, 0),
|
||||||
|
monthlyMatchCount: ladderUser.monthly_match_count,
|
||||||
|
winRate:
|
||||||
|
matchCount > 0 ? Math.round((winCount / matchCount) * 100) : 0,
|
||||||
|
rank: higherCount + 1,
|
||||||
|
storeId: ladderUser.store_id,
|
||||||
|
storeName: ladderUser.store && ladderUser.store.name,
|
||||||
|
lastMatchTime: ladderUser.last_match_time,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
} catch (err) {
|
||||||
|
console.error("获取用户详情失败:", err);
|
||||||
|
res.status(500).json(error("获取失败"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取等级说明
|
// 获取等级说明
|
||||||
async getLevelInfo(req, res) {
|
async getLevelInfo(req, res) {
|
||||||
try {
|
try {
|
||||||
const levels = Object.keys(LADDER_LEVEL_NAMES).map(level => ({
|
const levels = Object.keys(LADDER_LEVEL_NAMES).map((level) => ({
|
||||||
level: parseInt(level),
|
level: parseInt(level),
|
||||||
name: LADDER_LEVEL_NAMES[level],
|
name: LADDER_LEVEL_NAMES[level],
|
||||||
description: LADDER_LEVEL_DESC[level]
|
description: LADDER_LEVEL_DESC[level],
|
||||||
}));
|
}));
|
||||||
|
|
||||||
res.json(success({
|
res.json(
|
||||||
levels,
|
success({
|
||||||
powerCalcRules: {
|
levels,
|
||||||
baseWin: POWER_CALC.BASE_WIN,
|
powerCalcRules: {
|
||||||
baseLose: POWER_CALC.BASE_LOSE,
|
baseWin: POWER_CALC.BASE_WIN,
|
||||||
underdogThreshold: POWER_CALC.UNDERDOG_THRESHOLD,
|
baseLose: POWER_CALC.BASE_LOSE,
|
||||||
underdogRate: POWER_CALC.UNDERDOG_RATE,
|
underdogThreshold: POWER_CALC.UNDERDOG_THRESHOLD,
|
||||||
maxChange: POWER_CALC.MAX_CHANGE,
|
underdogRate: POWER_CALC.UNDERDOG_RATE,
|
||||||
newbieProtection: POWER_CALC.NEWBIE_PROTECTION,
|
maxChange: POWER_CALC.MAX_CHANGE,
|
||||||
minMonthlyMatches: POWER_CALC.MIN_MONTHLY_MATCHES,
|
newbieProtection: POWER_CALC.NEWBIE_PROTECTION,
|
||||||
challengeCooldown: POWER_CALC.CHALLENGE_COOLDOWN
|
minMonthlyMatches: POWER_CALC.MIN_MONTHLY_MATCHES,
|
||||||
}
|
challengeCooldown: POWER_CALC.CHALLENGE_COOLDOWN,
|
||||||
}));
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('获取等级信息失败:', err);
|
console.error("获取等级信息失败:", err);
|
||||||
res.status(500).json(error('获取失败'));
|
res.status(500).json(error("获取失败"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -756,6 +756,90 @@ class MatchController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获取选手比赛记录(用于选手详情页)
|
||||||
|
async getPlayerHistory(req, res) {
|
||||||
|
try {
|
||||||
|
const { player_id, page = 1, pageSize = 20 } = req.query;
|
||||||
|
const { limit, offset } = getPagination(page, pageSize);
|
||||||
|
|
||||||
|
if (!player_id) {
|
||||||
|
return res.status(400).json(error('缺少选手ID', 400));
|
||||||
|
}
|
||||||
|
|
||||||
|
const player = await LadderUser.findByPk(player_id);
|
||||||
|
if (!player || player.status !== 1) {
|
||||||
|
return res.json(pageResult([], 0, page, pageSize));
|
||||||
|
}
|
||||||
|
|
||||||
|
const { rows, count } = await MatchGame.findAndCountAll({
|
||||||
|
where: {
|
||||||
|
[Op.or]: [
|
||||||
|
{ player1_id: player.id },
|
||||||
|
{ player2_id: player.id }
|
||||||
|
],
|
||||||
|
confirm_status: CONFIRM_STATUS.CONFIRMED
|
||||||
|
},
|
||||||
|
include: [
|
||||||
|
{ model: Match, as: 'match', attributes: ['id', 'name', 'type', 'weight'] }
|
||||||
|
],
|
||||||
|
order: [['confirmed_at', 'DESC']],
|
||||||
|
limit,
|
||||||
|
offset
|
||||||
|
});
|
||||||
|
|
||||||
|
const opponentIds = [];
|
||||||
|
rows.forEach((g) => {
|
||||||
|
if (g.player1_id === player.id) opponentIds.push(g.player2_id);
|
||||||
|
else opponentIds.push(g.player1_id);
|
||||||
|
});
|
||||||
|
const uniqueOpponentIds = Array.from(new Set(opponentIds));
|
||||||
|
const opponents = await LadderUser.findAll({
|
||||||
|
where: { id: { [Op.in]: uniqueOpponentIds } },
|
||||||
|
include: [{ model: User, as: 'user', attributes: ['nickname', 'avatar'] }]
|
||||||
|
});
|
||||||
|
const opponentMap = new Map(opponents.map((o) => [String(o.id), o]));
|
||||||
|
|
||||||
|
const list = rows.map((game) => {
|
||||||
|
const isPlayer1 = game.player1_id === player.id;
|
||||||
|
const opponentId = isPlayer1 ? game.player2_id : game.player1_id;
|
||||||
|
const opponent = opponentMap.get(String(opponentId));
|
||||||
|
|
||||||
|
const myScore = isPlayer1 ? game.player1_score : game.player2_score;
|
||||||
|
const opponentScore = isPlayer1 ? game.player2_score : game.player1_score;
|
||||||
|
const isWin = game.winner_id === player.id;
|
||||||
|
const typeName = game.match && game.match.type === MATCH_TYPES.CHALLENGE ? '挑战赛' : '排位赛';
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: game.id,
|
||||||
|
matchId: game.match_id,
|
||||||
|
name: (game.match && game.match.name) || typeName,
|
||||||
|
type: game.match && game.match.type,
|
||||||
|
typeName,
|
||||||
|
createTime: game.confirmed_at,
|
||||||
|
desc: opponent ? `vs ${opponent.real_name} ${myScore}:${opponentScore}` : `${myScore}:${opponentScore}`,
|
||||||
|
result: isWin ? 'win' : 'lose',
|
||||||
|
resultName: isWin ? '胜' : '负',
|
||||||
|
powerChange: isWin ? game.winner_power_change : game.loser_power_change,
|
||||||
|
opponent: opponent
|
||||||
|
? {
|
||||||
|
id: opponent.id,
|
||||||
|
realName: opponent.real_name,
|
||||||
|
nickname: opponent.user && opponent.user.nickname,
|
||||||
|
avatar: opponent.user && opponent.user.avatar,
|
||||||
|
level: opponent.level,
|
||||||
|
powerScore: opponent.power_score
|
||||||
|
}
|
||||||
|
: null
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
res.json(pageResult(list, count, page, pageSize));
|
||||||
|
} catch (err) {
|
||||||
|
console.error('获取选手比赛记录失败:', err);
|
||||||
|
res.status(500).json(error('获取失败'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 获取正在进行中的比赛
|
// 获取正在进行中的比赛
|
||||||
async getOngoingMatches(req, res) {
|
async getOngoingMatches(req, res) {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@ -9,6 +9,9 @@ router.get('/ranking', ladderController.getRanking);
|
|||||||
// 获取天梯用户详情
|
// 获取天梯用户详情
|
||||||
router.get('/user/:id', ladderController.getUserDetail);
|
router.get('/user/:id', ladderController.getUserDetail);
|
||||||
|
|
||||||
|
// 选手详情(兼容小程序端:/api/ladder/player?id=xxx)
|
||||||
|
router.get('/player', ladderController.getPlayerDetail);
|
||||||
|
|
||||||
// 获取等级说明
|
// 获取等级说明
|
||||||
router.get('/levels', ladderController.getLevelInfo);
|
router.get('/levels', ladderController.getLevelInfo);
|
||||||
|
|
||||||
|
|||||||
@ -39,6 +39,9 @@ router.post('/ranking/confirm-score', authUser, matchController.confirmRankingSc
|
|||||||
// 获取正在进行中的比赛
|
// 获取正在进行中的比赛
|
||||||
router.get('/ongoing', authUser, matchController.getOngoingMatches);
|
router.get('/ongoing', authUser, matchController.getOngoingMatches);
|
||||||
|
|
||||||
|
// 获取选手比赛记录(用于选手详情页)
|
||||||
|
router.get('/history', authUser, matchController.getPlayerHistory);
|
||||||
|
|
||||||
// 获取我的比赛记录
|
// 获取我的比赛记录
|
||||||
router.get('/my-matches', authUser, matchController.getMyMatches);
|
router.get('/my-matches', authUser, matchController.getMyMatches);
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user