Files
shoot-miniprograms/src/components/Container.vue
2026-02-07 10:52:56 +08:00

336 lines
8.2 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, computed, onMounted, onBeforeUnmount } from "vue";
import { onShow } from "@dcloudio/uni-app";
import AppBackground from "@/components/AppBackground.vue";
import Header from "@/components/Header.vue";
import ScreenHint from "@/components/ScreenHint.vue";
import BackToGame from "@/components/BackToGame.vue";
import { laserAimAPI, getBattleAPI } from "@/apis";
import { capsuleHeight, debounce } from "@/util";
import AudioManager from "@/audioManager";
const props = defineProps({
title: {
type: String,
default: "",
},
bgType: {
type: Number,
default: 0,
},
onBack: {
type: Function,
default: null,
},
scroll: {
type: Boolean,
default: true,
},
isHome: {
type: Boolean,
default: false,
},
showBackToGame: {
type: Boolean,
default: false,
},
bgColor: {
type: String,
default: "#050b19",
},
whiteBackArrow: {
type: Boolean,
default: true,
},
showBottom: {
type: Boolean,
default: true,
},
});
const isIOS = uni.getDeviceInfo().osName === "ios";
const showHint = ref(false);
const hintType = ref(0);
const isLoading = ref(false);
const audioInitProgress = ref(1);
const audioProgress = ref(0);
const audioTimer = ref(null);
const showGlobalHint = (type) => {
hintType.value = type;
showHint.value = true;
};
const hideGlobalHint = () => {
showHint.value = false;
};
const restart = () => {
uni.restartMiniProgram({
path: "/pages/index",
});
};
const checkAudioProgress = async () => {
return new Promise((resolve, reject) => {
try {
audioInitProgress.value = AudioManager.getLoadProgress();
if (audioInitProgress.value === 1) return resolve();
audioTimer.value = setInterval(() => {
audioProgress.value = AudioManager.getLoadProgress();
if (audioProgress.value === 1) {
setTimeout(() => {
audioInitProgress.value = 1;
}, 200);
clearInterval(audioTimer.value);
resolve();
}
}, 200);
} catch (err) {
reject(err);
}
});
};
const audioFinalProgress = computed(() => {
const left = 1 - audioInitProgress.value;
return Math.max(0, (audioProgress.value - audioInitProgress.value) / left);
});
onBeforeUnmount(() => {
if (audioTimer.value) clearInterval(audioTimer.value);
});
onShow(() => {
uni.$showHint = showGlobalHint;
uni.$hideHint = hideGlobalHint;
uni.$checkAudio = checkAudioProgress;
showHint.value = false;
});
const backToGame = debounce(async () => {
if (isLoading.value) return; // 防止重复点击
try {
isLoading.value = true;
const result = await getBattleAPI();
if (result && result.matchId) {
await checkAudioProgress();
if (result.mode <= 3) {
uni.navigateTo({
url: `/pages/team-battle?battleId=${result.matchId}`,
});
} else {
uni.navigateTo({
url: `/pages/melee-battle?battleId=${result.matchId}`,
});
}
}
} catch (error) {
console.error("获取当前游戏失败:", error);
} finally {
isLoading.value = false;
}
});
const goBack = () => {
uni.navigateBack();
};
const goCalibration = async () => {
await laserAimAPI();
uni.navigateTo({
url: "/pages/calibration",
});
};
</script>
<template>
<view :style="{ paddingTop: capsuleHeight + 'px' }">
<AppBackground :type="bgType" :bgColor="bgColor" />
<Header
v-if="!isHome"
:title="title"
:onBack="onBack"
:whiteBackArrow="whiteBackArrow"
/>
<BackToGame v-if="showBackToGame" />
<scroll-view
:scroll-y="scroll"
:enhanced="true"
:bounces="false"
:show-scrollbar="false"
:style="{
height: `calc(100vh - ${capsuleHeight + (isHome ? 0 : 50)}px - ${
$slots.bottom && showBottom ? (isIOS ? '75px' : '65px') : '0px'
})`,
}"
>
<slot></slot>
</scroll-view>
<view
class="bottom-part"
v-if="$slots.bottom && showBottom"
:style="{ height: isIOS ? '65px' : '55px', paddingTop: '10px' }"
>
<slot name="bottom"></slot>
</view>
<ScreenHint :show="showHint">
<view v-if="hintType === 1" class="tip-content">
<text>完成进行中的对局才能开启新的</text>
<text>您有正在进行中的对局是否进入?</text>
<view>
<button hover-class="none" @click="() => (showHint = false)">
不进入
</button>
<button hover-class="none" @click="backToGame" :disabled="isLoading">
{{ isLoading ? "加载中..." : "进入" }}
</button>
</view>
</view>
<view v-if="hintType === 2" class="tip-content">
<text>离开比赛可能会导致比赛失败</text>
<text>确认离开吗</text>
<view>
<button hover-class="none" @click="goBack">离开比赛</button>
<button hover-class="none" @click="() => (showHint = false)">
继续比赛
</button>
</view>
</view>
<view v-if="hintType === 3" class="tip-content">
<text>今天不玩了吗</text>
<view>
<button hover-class="none" @click="() => (showHint = false)">
取消
</button>
<button hover-class="none" @click="goBack">确认</button>
</view>
</view>
<view v-if="hintType === 4" class="tip-content">
<text>完成智能弓校准即可解锁全部功能</text>
<view>
<button hover-class="none" @click="() => (showHint = false)">
取消
</button>
<button hover-class="none" @click="goCalibration">去校准</button>
</view>
</view>
</ScreenHint>
<view v-if="audioInitProgress < 1" class="audio-progress">
<image
src="https://static.shelingxingqiu.com/attachment/2025-11-26/deihtj15xjwcz3c1tx.png"
mode="widthFix"
/>
<view>
<view :style="{ width: `${audioFinalProgress * 100}%` }">
<!-- <image
src="https://static.shelingxingqiu.com/attachment/2025-11-24/degu91a7si77sg9jqv.png"
mode="widthFix"
/> -->
</view>
</view>
<view>
<text>若加载时间过长</text>
<button hover-class="none" @click="restart">点击这里重启</button>
</view>
</view>
</view>
</template>
<style scoped>
.tip-content {
flex-direction: column;
display: flex;
align-items: center;
justify-content: center;
color: #fff;
width: 100%;
font-size: 14px;
}
.tip-content > text {
text-align: center;
}
.tip-content > view {
display: flex;
align-items: center;
justify-content: space-around;
margin-top: 50rpx;
width: 100%;
}
.tip-content > view > button {
padding: 12px;
border-radius: 20px;
background-color: #fff6;
color: #fff;
width: 45%;
font-size: 16px;
}
.tip-content > view > button:last-child {
background-color: #fed847;
color: #000;
}
.tip-content > view > button:disabled {
background-color: #ccc;
color: #666;
opacity: 0.6;
}
.audio-progress {
z-index: 999;
width: 100vw;
height: 100vh;
position: fixed;
top: 0;
left: 0;
background: rgb(0 0 0 / 0.8);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.audio-progress > image:nth-child(1) {
width: 140rpx;
height: 150rpx;
margin-bottom: 20rpx;
}
.audio-progress > view:nth-child(2) {
width: 380rpx;
height: 6rpx;
background: #595959;
border-radius: 4rpx;
display: flex;
flex-direction: column;
align-items: flex-start;
justify-content: flex-start;
}
.audio-progress > view:nth-child(2) > view {
background: #ffe431;
min-height: 6rpx;
border-radius: 4rpx;
display: flex;
align-items: center;
justify-content: flex-end;
transition: width 0.5s ease;
}
.audio-progress > view:nth-child(2) > view > image {
width: 46rpx;
height: 26rpx;
}
.audio-progress > view:nth-child(3) {
display: flex;
align-items: center;
justify-content: center;
}
.audio-progress > view:nth-child(3) > text {
font-size: 22rpx;
color: #a2a2a2;
text-align: center;
line-height: 32rpx;
}
.audio-progress > view:nth-child(3) > button {
font-size: 22rpx;
color: #ffe431;
line-height: 32rpx;
padding: 20rpx 0;
}
</style>