Compare commits
6 Commits
prod
...
6e62ec4348
| Author | SHA1 | Date | |
|---|---|---|---|
| 6e62ec4348 | |||
| d8a94c8ff6 | |||
| 29c6b174d8 | |||
| bb43de3a62 | |||
| d2dc6c51cf | |||
| 8c66ef78c6 |
@@ -6,8 +6,7 @@ try {
|
||||
|
||||
switch (envVersion) {
|
||||
case "develop": // 开发版
|
||||
BASE_URL = "http://localhost:8000/api/shoot";
|
||||
// BASE_URL = "https://apitest.shelingxingqiu.com/api/shoot";
|
||||
BASE_URL = "https://apitest.shelingxingqiu.com/api/shoot";
|
||||
break;
|
||||
case "trial": // 体验版
|
||||
BASE_URL = "https://apitest.shelingxingqiu.com/api/shoot";
|
||||
|
||||
@@ -26,20 +26,21 @@ defineProps({
|
||||
.container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 15px;
|
||||
padding: 0 26rpx 0 28rpx;
|
||||
margin-bottom: 14rpx;
|
||||
width: clac(100% - 30px);
|
||||
width: clac(100% - 54rpx);
|
||||
}
|
||||
.container .shooter2 {
|
||||
width: 150rpx;
|
||||
height: 162rpx;
|
||||
display: block;
|
||||
width: 133rpx;
|
||||
height: 144rpx;
|
||||
}
|
||||
.container .bg-box {
|
||||
color: #fff;
|
||||
font-size: 28rpx;
|
||||
position: relative;
|
||||
flex: 1;
|
||||
min-height: 55px;
|
||||
height: 128rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
@@ -6,7 +6,7 @@ import Avatar from "@/components/Avatar.vue";
|
||||
import useStore from "@/store";
|
||||
import { storeToRefs } from "pinia";
|
||||
const store = useStore();
|
||||
const { user } = storeToRefs(store);
|
||||
const { user, game } = storeToRefs(store);
|
||||
|
||||
const currentPage = computed(() => {
|
||||
const pages = getCurrentPages();
|
||||
@@ -59,8 +59,6 @@ const loading = ref(false);
|
||||
const pointBook = ref(null);
|
||||
const showProgress = ref(false);
|
||||
const heat = ref(0);
|
||||
/** 当前对战房间的房号,由 battle-room 页面通过 uni.$emit 传入 */
|
||||
const battleRoomNumber = ref("");
|
||||
|
||||
const updateLoading = (value) => {
|
||||
loading.value = value;
|
||||
@@ -70,14 +68,6 @@ const updateHot = (value) => {
|
||||
heat.value = value;
|
||||
};
|
||||
|
||||
/**
|
||||
* 接收 battle-room 页面发出的房间信息,更新房号展示
|
||||
* @param {{ roomNumber: string }} info
|
||||
*/
|
||||
const updateBattleRoomInfo = (info) => {
|
||||
battleRoomNumber.value = info.roomNumber || "";
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
const pages = getCurrentPages();
|
||||
const currentPage = pages[pages.length - 1];
|
||||
@@ -94,11 +84,9 @@ onMounted(() => {
|
||||
showProgress.value = true;
|
||||
}
|
||||
uni.$on("update-hot", updateHot);
|
||||
uni.$on("battle-room-info", updateBattleRoomInfo);
|
||||
});
|
||||
onBeforeUnmount(() => {
|
||||
uni.$off("update-hot", updateHot);
|
||||
uni.$off("battle-room-info", updateBattleRoomInfo);
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -185,14 +173,14 @@ onBeforeUnmount(() => {
|
||||
<view v-if="showProgress" class="battle-progress">
|
||||
<HeaderProgress />
|
||||
</view>
|
||||
<!-- 对战房间:整个胶囊为分享按钮,点击任意区域均可触发分享 -->
|
||||
<!-- 对战房间:整个胶囊为分享按钮,房号从 Store 读取 -->
|
||||
<button
|
||||
v-if="currentPage === 'pages/battle-room' && battleRoomNumber"
|
||||
v-if="currentPage === 'pages/battle-room' && game.roomNumber"
|
||||
open-type="share"
|
||||
hover-class="none"
|
||||
class="battle-room-number"
|
||||
>
|
||||
<text class="battle-room-number__text">房号: {{ battleRoomNumber }}</text>
|
||||
<text class="battle-room-number__text">房号: {{ game.roomNumber }}</text>
|
||||
<image src="../static/share2.png" mode="widthFix" class="battle-room-number__icon" />
|
||||
</button>
|
||||
</view>
|
||||
@@ -285,7 +273,7 @@ onBeforeUnmount(() => {
|
||||
/* 对战房间:整个胶囊作为分享按钮,靠右对齐 */
|
||||
.battle-room-number {
|
||||
margin-left: auto;
|
||||
margin-right: 10rpx;
|
||||
margin-right: 20rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
@@ -39,6 +39,9 @@
|
||||
{
|
||||
"path": "pages/battle-result"
|
||||
},
|
||||
{
|
||||
"path": "pages/friend-battle-result"
|
||||
},
|
||||
{
|
||||
"path": "pages/point-book-edit"
|
||||
},
|
||||
|
||||
@@ -117,7 +117,7 @@ const checkBowData = () => {
|
||||
}deg)`,
|
||||
}"
|
||||
>
|
||||
<view v-if="data.mvp && data.mvp[0].totalRings">
|
||||
<view v-if="data.mvp && data.mvp.totalRings">
|
||||
<image src="../static/title-mvp.png" mode="widthFix" />
|
||||
<text
|
||||
>斩获<text
|
||||
@@ -127,7 +127,7 @@ const checkBowData = () => {
|
||||
margin: '0 3px',
|
||||
fontWeight: '600',
|
||||
}"
|
||||
>{{ data.mvp[0].totalRings }}</text
|
||||
>{{ data.mvp.totalRings }}</text
|
||||
>环</text
|
||||
>
|
||||
</view>
|
||||
|
||||
@@ -42,16 +42,40 @@ const battleTitle = computed(() => {
|
||||
return `${half}v${half}对抗赛`;
|
||||
});
|
||||
|
||||
/** 靶纸尺寸(cm),由 URL 参数或 API 返回的 targetType 字段填充 */
|
||||
const targetSize = ref(0);
|
||||
|
||||
/**
|
||||
* 根据 targetSize 动态生成靶纸尺寸文本,如"40cm"
|
||||
* 数据未就绪时显示 "--";数据来源:创建者取 URL 参数 target,加入者取 API 返回的 targetType
|
||||
*/
|
||||
const targetSizeLabel = computed(() =>
|
||||
targetSize.value ? `${targetSize.value}cm` : '--'
|
||||
);
|
||||
|
||||
const ready = ref(false);
|
||||
const allReady = ref(false);
|
||||
const timer = ref(null);
|
||||
const goBattle = ref(false);
|
||||
|
||||
/**
|
||||
* 从服务端刷新当前房间数据,更新成员列表、准备状态等信息
|
||||
* 仅在 roomNumber 有效且房间未开始时执行
|
||||
*/
|
||||
async function refreshRoomData() {
|
||||
if (!roomNumber.value) return;
|
||||
const result = await getRoomAPI(roomNumber.value);
|
||||
if (result.started) return;
|
||||
room.value = result;
|
||||
// 加入者通过 API 返回的 targetType 字段同步靶纸尺寸,并持久化到本地缓存
|
||||
if (result.targetType) {
|
||||
targetSize.value = result.targetType;
|
||||
uni.setStorageSync(`targetSize_${roomNumber.value}`, result.targetType);
|
||||
} else if (targetSize.value === 0) {
|
||||
// API 无该字段时,从本地缓存兜底(如"返回房间"场景)
|
||||
const stored = uni.getStorageSync(`targetSize_${roomNumber.value}`);
|
||||
if (stored) targetSize.value = stored;
|
||||
}
|
||||
owner.value = {};
|
||||
opponent.value = {};
|
||||
const members = result.members || [];
|
||||
@@ -109,9 +133,6 @@ async function refreshRoomData() {
|
||||
}
|
||||
if (timer.value) clearInterval(timer.value);
|
||||
// timer.value = setTimeout(refreshRoomData, 2000);
|
||||
|
||||
// 通知 Header 组件更新房号展示
|
||||
uni.$emit("battle-room-info", { roomNumber: roomNumber.value });
|
||||
}
|
||||
|
||||
const startGame = async () => {
|
||||
@@ -240,27 +261,64 @@ const canClick = computed(() => {
|
||||
return true;
|
||||
});
|
||||
|
||||
/**
|
||||
* 根据对战类型和人数动态生成分享文案
|
||||
* 1v1 / 默认 → "星球论箭,来一决高下敢否?"
|
||||
* 2v2 → "2v2对抗赛,是兄弟来助我一把!"
|
||||
* 3v3 → "3v3对抗赛,来了马上发车!"
|
||||
* 乱斗 → "热血乱斗赛,敢来争锋?"
|
||||
*/
|
||||
const shareTitle = computed(() => {
|
||||
const { battleType, count } = room.value;
|
||||
if (battleType === 2) return '热血乱斗赛,敢来争锋?';
|
||||
if (battleType === 1 && count === 4) return '2v2对抗赛,是兄弟来助我一把!';
|
||||
if (battleType === 1 && count === 6) return '3v3对抗赛,来了马上发车!';
|
||||
return '星球论箭,来一决高下敢否?';
|
||||
});
|
||||
|
||||
onShareAppMessage(() => {
|
||||
return {
|
||||
title: "邀请您进入房间对战",
|
||||
title: shareTitle.value,
|
||||
path: "/pages/friend-battle?roomID=" + roomNumber.value,
|
||||
imageUrl: "",
|
||||
};
|
||||
});
|
||||
|
||||
/**
|
||||
* onShow 生命周期:页面显示时刷新房间数据
|
||||
* 注:uni-app 中 onShow 可能早于 onLoad 执行,此时 roomNumber 尚未赋值,
|
||||
* refreshRoomData 会提前 return。
|
||||
*/
|
||||
onShow(() => {
|
||||
goBattle.value = false;
|
||||
refreshRoomData();
|
||||
});
|
||||
|
||||
/**
|
||||
* 页面加载时解析路由参数,初始化房间号
|
||||
* - 存入本地 ref(供页面内部逻辑使用)
|
||||
* - 同步到 Pinia Store(供 Header 组件展示房号胶囊)
|
||||
*/
|
||||
onLoad(async (options) => {
|
||||
if (options.roomNumber) {
|
||||
roomNumber.value = options.roomNumber;
|
||||
// 立即通知 Header 显示房号,无需等待异步 API 返回
|
||||
uni.$emit("battle-room-info", { roomNumber: options.roomNumber });
|
||||
store.updateRoomNumber(options.roomNumber);
|
||||
}
|
||||
// 创建者通过 URL 参数 target(1→20cm,2→40cm)初始化靶纸尺寸,并持久化到本地缓存
|
||||
if (options.target) {
|
||||
targetSize.value = parseInt(options.target) * 20;
|
||||
uni.setStorageSync(`targetSize_${roomNumber.value}`, targetSize.value);
|
||||
} else if (roomNumber.value) {
|
||||
// "返回房间"等无 target 参数的场景:从本地缓存恢复(待 refreshRoomData 进一步覆盖)
|
||||
const stored = uni.getStorageSync(`targetSize_${roomNumber.value}`);
|
||||
if (stored) targetSize.value = stored;
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* 组件挂载后:保持屏幕常亮、注册 WebSocket 消息监听
|
||||
* 房间号已通过 onLoad 同步到 Store,Header 从 Store 直接读取,无需额外通知
|
||||
*/
|
||||
onMounted(() => {
|
||||
uni.setKeepScreenOn({
|
||||
keepScreenOn: true,
|
||||
@@ -285,11 +343,8 @@ onBeforeUnmount(() => {
|
||||
<GuideTwo>
|
||||
<view class="battle-guide">
|
||||
<view class="guide-tips">
|
||||
<text>弓箭手们,人都到齐了吗?</text>
|
||||
<text v-if="room.battleType === 1">{{
|
||||
`${room.count / 2}v${room.count / 2}比赛即将开始!`
|
||||
}}</text>
|
||||
<text v-if="room.battleType === 2">大乱斗即将开始! </text>
|
||||
<text class="guide-tips__target">请使用{{ targetSizeLabel }}全环靶</text>
|
||||
<text class="guide-tips__warn">如果实际靶纸与选择靶纸不同,将导致射箭无效</text>
|
||||
</view>
|
||||
</view>
|
||||
</GuideTwo>
|
||||
@@ -804,4 +859,23 @@ onBeforeUnmount(() => {
|
||||
border: 1rpx solid #a3793f66;
|
||||
color: #fed847;
|
||||
}
|
||||
|
||||
.guide-tips__target {
|
||||
font-weight: 400;
|
||||
font-size: 26rpx;
|
||||
color: rgba(255, 217, 71, 0.8);
|
||||
}
|
||||
|
||||
.guide-tips__warn {
|
||||
font-weight: 400;
|
||||
font-size: 22rpx;
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
margin-top: 6rpx;
|
||||
}
|
||||
|
||||
.guide-tips {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding-left: 20rpx;
|
||||
}
|
||||
</style>
|
||||
|
||||
1032
src/pages/friend-battle-result.vue
Normal file
@@ -38,10 +38,14 @@ const showRoundTip = ref(false);
|
||||
const isFinalShoot = ref(false);
|
||||
const matchStatus = ref(undefined);
|
||||
const updateRemainSecond = ref(0);
|
||||
/** 对战来源类型(1=好友约战,2=匹配对战),用于结算页分流 */
|
||||
const battleWay = ref(0);
|
||||
|
||||
const recoverData = (battleInfo, {force = false, arrowOnly = false} = {}) => {
|
||||
try {
|
||||
battleId.value = battleInfo.matchId;
|
||||
// 存储对战来源,供结算跳转分流使用
|
||||
if (battleInfo.way !== undefined) battleWay.value = battleInfo.way;
|
||||
|
||||
// 优先使用接口返回的队伍数据,如果没有则尝试从缓存读取(应对匹配刚完成接口未就绪的情况)
|
||||
const t1 = battleInfo.teams?.[1] || {};
|
||||
@@ -118,16 +122,25 @@ function onAudioEnded(s) {
|
||||
uni.$emit("update-remain", {stop: false, value: updateRemainSecond.value, team: team});
|
||||
}
|
||||
if (s.indexOf("比赛结束") >= 0) {
|
||||
console.log("比赛结束");
|
||||
onBattleEnd()
|
||||
}
|
||||
}
|
||||
|
||||
function onBattleEnd() {
|
||||
if (matchStatus.value === 2) {
|
||||
// 好友约战(way===1)跳转专属结算页,其他模式保持跳转旧结算页
|
||||
// 后期如需新增更多模式的专属页面,在此扩展 if/else 分支即可
|
||||
if (battleWay.value === 1) {
|
||||
uni.redirectTo({
|
||||
url: `/pages/friend-battle-result?battleId=${battleId.value}`,
|
||||
});
|
||||
} else {
|
||||
uni.redirectTo({
|
||||
url: `/pages/battle-result?battleId=${battleId.value}`,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function onNewRound(msg) {
|
||||
@@ -202,13 +215,17 @@ onShow(async () => {
|
||||
const result = await getBattleAPI(battleId.value);
|
||||
if (!result) return;
|
||||
if (result.status === 2) {
|
||||
uni.showToast({
|
||||
title: "比赛已结束",
|
||||
icon: "none",
|
||||
// 比赛已结束(如切后台再回来):跳结算页,按 way 分流
|
||||
// 与 onBattleEnd 保持一致,避免返回首页
|
||||
if (result.way === 1) {
|
||||
uni.redirectTo({
|
||||
url: `/pages/friend-battle-result?battleId=${result.matchId}`,
|
||||
});
|
||||
uni.navigateBack({
|
||||
delta: 2,
|
||||
} else {
|
||||
uni.redirectTo({
|
||||
url: `/pages/battle-result?battleId=${result.matchId}`,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
recoverData(result, {force: true});
|
||||
}
|
||||
|
||||
BIN
src/static/friend-battle-result/game-lose.png
Normal file
|
After Width: | Height: | Size: 49 KiB |
BIN
src/static/friend-battle-result/game-over.png
Normal file
|
After Width: | Height: | Size: 80 KiB |
BIN
src/static/friend-battle-result/game-win.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
11
src/static/friend-battle-result/rank-one.svg
Normal file
@@ -0,0 +1,11 @@
|
||||
<svg width="156" height="58" viewBox="0 0 156 58" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="编组 5">
|
||||
<rect id="矩形备份" width="156" height="58" fill="url(#paint0_linear_1394_738459)"/>
|
||||
</g>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_1394_738459" x1="0" y1="87" x2="156" y2="87" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#7E3627"/>
|
||||
<stop offset="1" stop-color="#292826" stop-opacity="0.01"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 540 B |
11
src/static/friend-battle-result/rank-three.svg
Normal file
@@ -0,0 +1,11 @@
|
||||
<svg width="156" height="58" viewBox="0 0 156 58" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="编组 12">
|
||||
<rect id="矩形备份" width="156" height="58" fill="url(#paint0_linear_1394_738487)"/>
|
||||
</g>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_1394_738487" x1="0" y1="87" x2="156" y2="87" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#573E25"/>
|
||||
<stop offset="1" stop-color="#292826" stop-opacity="0.01"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 541 B |
11
src/static/friend-battle-result/rank-two.svg
Normal file
@@ -0,0 +1,11 @@
|
||||
<svg width="156" height="58" viewBox="0 0 156 58" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="编组 9">
|
||||
<rect id="矩形备份" width="156" height="58" fill="url(#paint0_linear_1394_738473)"/>
|
||||
</g>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_1394_738473" x1="0.251953" y1="86.9532" x2="156" y2="86.9532" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#25476F"/>
|
||||
<stop offset="1" stop-color="#292826" stop-opacity="0.01"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 557 B |
BIN
src/static/friend-battle-result/vs-bg.png
Normal file
|
After Width: | Height: | Size: 32 KiB |
BIN
src/static/mvp-blue.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
src/static/mvp-red.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
src/static/mvp-tip.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 25 KiB |
@@ -79,6 +79,7 @@ export default defineStore("store", {
|
||||
game: {
|
||||
roomID: "",
|
||||
inBattle: false,
|
||||
roomNumber: "", // 当前房间号,供 Header 展示房号胶囊
|
||||
},
|
||||
}),
|
||||
|
||||
@@ -135,6 +136,10 @@ export default defineStore("store", {
|
||||
this.game.roomID = roomID;
|
||||
this.game.inBattle = inBattle;
|
||||
},
|
||||
/** 更新当前房间号,供 Header 组件展示房号胶囊 */
|
||||
updateRoomNumber(number) {
|
||||
this.game.roomNumber = number;
|
||||
},
|
||||
},
|
||||
|
||||
// 开启数据持久化
|
||||
|
||||
@@ -14,8 +14,7 @@ function createWebSocket(token, onMessage) {
|
||||
|
||||
switch (envVersion) {
|
||||
case "develop": // 开发版
|
||||
url = "ws://localhost:8000/socket";
|
||||
// url = "wss://apitest.shelingxingqiu.com/socket";
|
||||
url = "wss://apitest.shelingxingqiu.com/socket";
|
||||
break;
|
||||
case "trial": // 体验版
|
||||
url = "wss://apitest.shelingxingqiu.com/socket";
|
||||
|
||||