Files
shoot-miniprograms/src/pages/index.vue
2026-01-13 11:35:36 +08:00

712 lines
18 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<script setup>
import { ref, onMounted } from "vue";
import { onShow, onShareAppMessage, onShareTimeline } from "@dcloudio/uni-app";
import AppFooter from "@/components/AppFooter.vue";
import Signin from "@/components/Signin.vue";
import NoticeBar from "@/components/NoticeBar.vue";
import TopRank from "@/components/TopRank.vue";
import MyRank from "@/components/MyRank.vue";
import LiveItem from "@/components/LiveItem.vue";
import {
getAppConfig,
getRankListAPI,
getHomeData,
getMyDevicesAPI,
getDeviceBatteryAPI,
} from "@/apis";
import { topThreeColors } from "@/constants";
import { canEenter, capsuleHeight } from "@/util";
import useStore from "@/store";
import { storeToRefs } from "pinia";
const store = useStore();
const {
updateConfig,
updateUser,
updateDevice,
updateRank,
getLvlName,
updateOnline,
} = store;
const { user, device, rankData, online, game } = storeToRefs(store);
const isIOS = uni.getDeviceInfo().osName === "ios";
const showModal = ref(false);
const showGuide = ref(false);
const selected = ref(0);
const liveType = ref(0);
const liveTypes = {
大神赛: "../static/dashen.png",
新人王: "../static/xinren.png",
女神赛: "../static/nvshen.png",
};
const toPage = async (path) => {
if (!user.value.id) {
showModal.value = true;
return;
}
if (path === "/pages/first-try") {
if (canEenter(user.value, device.value, online.value, path)) {
await uni.$checkAudio();
} else {
return;
}
}
uni.navigateTo({ url: path });
};
const toRankListPage = () => {
uni.navigateTo({
url: "/pages/rank-list",
});
};
onShow(async () => {
const token = uni.getStorageSync(
`${uni.getAccountInfoSync().miniProgram.envVersion}_token`
);
const promises = [getRankListAPI()];
if (token) {
promises.push(getHomeData());
}
const [rankList, homeData] = await Promise.all(promises);
console.log("排行数据", rankList);
updateRank(rankList);
if (homeData) {
console.log("首页数据:", homeData);
if (homeData.user) {
updateUser(homeData.user);
if ("823,209,293,257".indexOf(homeData.user.id) !== -1) {
const show = uni.getStorageSync("show-the-user");
if (!show) {
showTheUser.value = true;
uni.setStorageSync("show-the-user", true);
}
}
if (homeData.user.trio <= 0) {
showGuide.value = true;
setTimeout(() => {
showGuide.value = false;
}, 3000);
}
const devices = await getMyDevicesAPI();
if (devices.bindings && devices.bindings.length) {
updateDevice(
devices.bindings[0].deviceId,
devices.bindings[0].deviceName
);
const data = await getDeviceBatteryAPI();
updateOnline(data.online);
}
}
}
});
onMounted(async () => {
const config = await getAppConfig();
updateConfig(config);
console.log("全局配置:", config);
});
onShareAppMessage(() => {
return {
title: "智能真弓:实时捕捉+毫秒级同步,弓箭选手全球竞技!", // 分享卡片的标题
path: "/pages/index", // 用户点击分享卡片后跳转的页面路径
imageUrl:
"https://static.shelingxingqiu.com/attachment/2025-09-12/dcqoz26q0268wxmzjg.png", // 分享卡片的配图,可以是本地或网络图片
};
});
onShareTimeline(() => {
return {
title: "智能真弓:实时捕捉+毫秒级同步,弓箭选手全球竞技!", // 分享到朋友圈的标题
query: "from=timeline", // 用户通过朋友圈点击后,在页面 onShow 的 options 中可以获取到的参数
imageUrl:
"https://static.shelingxingqiu.com/attachment/2025-09-12/dcqoz26q0268wxmzjg.png", // 分享到朋友圈的配图
};
});
</script>
<template>
<view
class="container"
:style="{
paddingTop: capsuleHeight + 'px',
height: 'calc(100vh - ' + capsuleHeight + 'px)',
}"
>
<view :style="{ flex: 1, overflow: 'hidden' }">
<image
src="https://static.shelingxingqiu.com/attachment/2026-01-12/dfmg11wd20o1bagd4k.png"
mode="widthFix"
class="top-bg"
/>
<view class="header">
<image
src="https://static.shelingxingqiu.com/attachment/2026-01-12/dfmf4cjlds7oxd0tqd.png"
mode="widthFix"
/>
</view>
<scroll-view
class="body"
scroll-y
:show-scrollbar="false"
:enhanced="true"
:bounces="false"
>
<view class="main-btns" :style="{ marginBottom: '10rpx' }">
<view @click="() => toPage('/pages/my-device')">
<image
src="https://static.shelingxingqiu.com/attachment/2026-01-12/dfmgwjhsjc19tl0t7c.png"
mode="widthFix"
/>
<view v-if="user.id" class="device-info">
<text v-if="!device.deviceId">绑定我的智能弓</text>
<text v-else-if="!online">设备离线</text>
<text v-else-if="online">设备在线</text>
</view>
</view>
<view class="main-sub-btns">
<view @click="() => toPage('/pages/first-try')">
<image
src="https://static.shelingxingqiu.com/attachment/2026-01-12/dfmgwjhren1tfefi7k.png"
mode="widthFix"
/>
</view>
<view @click="() => toPage('/pages/practise')">
<image
src="https://static.shelingxingqiu.com/attachment/2026-01-12/dfmgwjht6q8l8zcduq.png"
mode="widthFix"
/>
</view>
</view>
</view>
<view class="main-btns">
<view @click="() => toPage('/pages/ranking')">
<image
src="https://static.shelingxingqiu.com/attachment/2026-01-12/dfmgwjhsi4s5qa7hch.png"
mode="widthFix"
/>
</view>
<view @click="() => toPage('/pages/friend-battle')">
<image
src="https://static.shelingxingqiu.com/attachment/2026-01-12/dfmgwjht17th6vl9a6.png"
mode="widthFix"
/>
</view>
</view>
<NoticeBar />
<view class="rank-info">
<image
src="../static/trophy-bg.png"
mode="widthFix"
class="trophy-bg"
/>
<view class="rank-info-header">
<view>
<image src="../static/rank-title.png" mode="widthFix" />
<text>星球榜</text>
</view>
<button hover-class="none">
<text>更多榜单</text>
<image src="../static/enter.png" mode="widthFix" />
</button>
</view>
<view class="rank-info-body">
<view :style="{ width: '45%' }">
<TopRank />
</view>
<view :style="{ width: '55%' }">
<MyRank />
</view>
</view>
</view>
<view class="live-bar">
<button
hover-class="none"
v-for="(item, index) in Object.keys(liveTypes)"
:key="index"
@click="liveType = index"
:style="{
color: liveType === index ? '#fff' : '#fff9',
background:
liveType === index
? 'linear-gradient( 133deg, #FFD19A 0%, #A17636 100%)'
: '#252831',
}"
>
<image :src="liveTypes[item]" mode="widthFix" />
<text>{{ item }}</text>
</button>
</view>
<swiper
:current="liveType"
@change="(e) => (liveType = e.detail.current)"
>
<swiper-item>
<LiveItem />
</swiper-item>
<swiper-item>
<LiveItem />
</swiper-item>
<swiper-item>
<LiveItem />
</swiper-item>
</swiper>
</scroll-view>
<Signin :show="showModal" :onClose="() => (showModal = false)" />
</view>
<AppFooter
:selected="selected"
:onChange="(index) => (selected = index)"
:onSignin="() => (showModal = true)"
/>
<!-- <view class="top-theme">
<image
src="https://static.shelingxingqiu.com/attachment/2025-12-31/dfc9dxrq4xn7e6y2pp.png"
mode="widthFix"
/>
</view>
<view :style="{ padding: '12px 10px' }">
<view class="feature-grid">
<view class="bow-card">
<image
v-if="online"
src="https://static.shelingxingqiu.com/attachment/2025-08-07/dbvt1o6dvhr2rop3kn.webp"
mode="widthFix"
@click="() => toPage('/pages/my-device')"
/>
<image
v-else
src="https://static.shelingxingqiu.com/attachment/2026-01-04/dffohwtk1gwh0xfa6h.png"
mode="widthFix"
@click="() => toPage('/pages/my-device')"
/>
<block v-if="user.id">
<text v-if="!device.deviceId">绑定我的智能弓</text>
<text v-else-if="!online">设备离线</text>
<text v-else-if="online">设备在线</text>
</block>
<image
src="../static/first-try.png"
mode="widthFix"
@click="() => toPage('/pages/first-try')"
/>
<BubbleTip v-if="showGuide" :location="{ top: '60%', left: '47%' }">
<text>新人必刷</text>
<text>快来报到吧~</text>
</BubbleTip>
</view>
<view class="play-card">
<view @click="() => toPage('/pages/practise')">
<image src="../static/my-practise.png" mode="widthFix" />
</view>
<view @click="() => toPage('/pages/friend-battle')">
<image src="../static/friend-battle.png" mode="widthFix" />
</view>
</view>
</view>
<view class="ranking-section">
<image
src="https://static.shelingxingqiu.com/attachment/2025-09-25/dd1p9ci9v7frcrsxhj.png"
mode="widthFix"
/>
<button
class="into-btn"
@click="() => toPage('/pages/ranking')"
hover-class="none"
></button>
<view class="ranking-players" @click="toRankListPage">
<img src="../static/juezhanbang.png" mode="widthFix" />
<view class="divide-line"></view>
<view class="player-avatars">
<view
v-for="i in 6"
:key="i"
class="player-avatar"
:style="{
zIndex: 8 - i,
borderColor: rankData.rank[i - 1]
? topThreeColors[i - 1] || '#000'
: '#000',
}"
>
<image v-if="i === 1" src="../static/champ1.png" />
<image v-if="i === 2" src="../static/champ2.png" />
<image v-if="i === 3" src="../static/champ3.png" />
<view v-if="i > 3">{{ i }}</view>
<image
:src="
rankData.rank[i - 1]
? rankData.rank[i - 1].avatar
: '../static/user-icon-dark.png'
"
mode="aspectFill"
/>
</view>
<view class="more-players">
<text>{{ rankData.rank.length }}</text>
</view>
</view>
</view>
<view class="my-data">
<view @click="() => toPage('/pages/my-growth')">
<image src="../static/my-growth.png" mode="widthFix" />
</view>
<view @click="() => toPage('/pages/ranking')">
<view>
<text>段位</text>
<text>{{
user.rankLvl ? getLvlName(user.rankLvl) : "暂无"
}}</text>
</view>
<view>
<text>赛季平均环数</text>
<text>{{ user.avg_ring ? user.avg_ring + "环" : "暂无" }}</text>
</view>
<view>
<text>赛季胜率</text>
<text>{{
user.avg_win
? Number((user.avg_win * 100).toFixed(2)) + "%"
: "暂无"
}}</text>
</view>
</view>
</view>
</view>
</view> -->
</view>
</template>
<style scoped lang="scss">
.container {
width: 100vw;
height: 100vh;
display: flex;
flex-direction: column;
justify-content: space-between;
background-color: #000;
}
.top-bg {
width: 100%;
position: fixed;
top: 0;
left: 0;
}
.header {
height: 50px;
position: relative;
display: flex;
align-items: center;
padding-left: 30rpx;
}
.header > image {
width: 200rpx;
height: 50rpx;
}
.body {
width: calc(100vw - 50rpx);
height: calc(100% - 25rpx);
padding: 0 25rpx;
position: relative;
margin-bottom: 25rpx;
}
.main-btns {
width: 100%;
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 20rpx;
}
.main-btns > view {
position: relative;
}
.main-btns image {
width: 100%;
}
.main-sub-btns {
display: flex;
flex-direction: column;
justify-content: space-between;
}
.device-info {
position: absolute;
width: 100%;
bottom: 50rpx;
font-size: 24rpx;
color: #fff9;
display: flex;
justify-content: center;
}
.rank-info {
margin-top: 10rpx;
background: linear-gradient(180deg, #2f2d2b 0%, #252831 100%);
border-radius: 20rpx;
position: relative;
padding: 20rpx;
display: flex;
flex-direction: column;
margin-top: 20rpx;
}
.trophy-bg {
position: absolute;
top: 0;
right: 0;
width: 188rpx;
height: 192rpx;
}
.rank-info-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 30rpx;
}
.rank-info-header > view {
width: 186rpx;
height: 56rpx;
font-weight: 500;
font-size: 28rpx;
color: #ffffff;
}
.rank-info-header > view > image {
width: 100%;
height: 100%;
}
.rank-info-header > view > text {
line-height: 56rpx;
display: block;
transform: translate(75rpx, -65rpx);
}
.rank-info-header > button {
font-size: 20rpx;
color: #999999;
display: flex;
align-items: center;
}
.rank-info-header > button > image {
width: 30rpx;
height: 30rpx;
}
.rank-info-body {
display: flex;
align-items: center;
}
.rank-info-body > view:first-child {
border-right: 1rpx solid #e8e8e81a;
box-sizing: border-box;
}
.live-bar {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 20rpx;
margin: 20rpx 0;
}
.live-bar > button {
display: flex;
align-items: center;
justify-content: center;
height: 68rpx;
border-radius: 34rpx;
font-weight: 600;
font-size: 28rpx;
}
.live-bar > button > image {
width: 46rpx;
height: 40rpx;
margin-right: 15rpx;
}
/* .feature-grid {
width: 100%;
display: flex;
margin-bottom: 5px;
}
.feature-grid > view {
position: relative;
display: flex;
flex-direction: column;
}
.bow-card {
width: 50%;
border-radius: 25rpx;
overflow: hidden;
}
.feature-grid > view > image {
width: 100%;
}
.bow-card > text {
position: absolute;
top: 66%;
left: 50%;
transform: translate(-50%, -50%);
white-space: nowrap;
font-size: 13px;
color: #b3b3b3;
}
.bow-card > image:nth-child(3) {
transform: translateY(-1px);
}
.play-card {
width: 48%;
margin-left: 2%;
}
.play-card > view > image {
width: 100%;
}
.ranking-section {
border-radius: 15px;
padding: 15px;
position: relative;
}
.ranking-section > image {
position: absolute;
top: 0;
left: 0;
width: 100%;
z-index: -1;
}
.into-btn {
position: absolute;
top: 40px;
left: calc(50% - 100px);
width: 200px;
height: 100px;
}
.ranking-players {
display: flex;
align-items: center;
padding-bottom: 20px;
margin-top: 42%;
border-bottom: 1rpx solid rgba(255, 255, 255, 0.2);
}
.ranking-players > image:first-child {
width: 28%;
transform: translateX(-10px) translateY(-8px);
}
.player-avatars {
display: flex;
align-items: center;
}
.divide-line {
width: 1px;
height: 35px;
background-color: #80808033;
margin-right: 8px;
}
.player-avatar,
.more-players {
width: 82rpx;
height: 82rpx;
border-radius: 50%;
margin-right: -20rpx;
border: 1rpx solid #312f35;
position: relative;
box-sizing: border-box;
}
.player-avatar > image:first-child,
.player-avatar > view:first-child {
position: absolute;
top: -24rpx;
left: 22rpx;
width: 32rpx;
height: 32rpx;
}
.player-avatar > view:first-child {
border-radius: 50%;
background: #777777;
text-align: center;
font-size: 10px;
line-height: 18px;
width: 18px;
height: 18px;
color: #fff;
}
.player-avatar > image:last-child {
width: 100%;
height: 100%;
border-radius: 50%;
}
.more-players {
background: #3c445a;
font-size: 9px;
line-height: 80rpx;
text-align: center;
z-index: 1;
}
.more-players > text {
margin-left: 2px;
color: #fff;
}
.my-data {
display: flex;
margin-top: 20px;
justify-content: space-between;
}
.my-data > view:first-child {
width: 28%;
}
.my-data > view:first-child > image {
width: 100%;
transform: translateX(-8px);
}
.my-data > view:nth-child(2) {
width: 68%;
font-size: 12px;
color: #fff6;
display: flex;
justify-content: space-between;
}
.my-data > view:nth-child(2) > view:nth-child(2) {
width: 38%;
}
.my-data > view:nth-child(2) > view {
width: 28%;
border-radius: 10px;
background: linear-gradient(180deg, #303b4c 30%, #2c384a 100%);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.my-data > view:nth-child(2) > view > text:last-child {
color: #fff;
line-height: 25px;
font-weight: 500;
}
.top-theme {
position: absolute;
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 60px;
z-index: -1;
}
.top-theme > image {
width: 300rpx;
transform: translate(-4%, -14%);
} */
</style>