diff --git a/src/apis.js b/src/apis.js index 590d837..c515e0e 100644 --- a/src/apis.js +++ b/src/apis.js @@ -23,7 +23,9 @@ try { console.error("获取环境信息失败,使用默认正式环境", e); } -function request(method, url, data = {}) { +const ADDONS_BASE_URL = BASE_URL.replace(/\/api\/shoot$/, "/api/shoot"); + +function request(method, url, data = {}, baseUrl = BASE_URL) { const token = uni.getStorageSync( `${uni.getAccountInfoSync().miniProgram.envVersion}_token` ); @@ -31,7 +33,7 @@ function request(method, url, data = {}) { if (token) header.Authorization = `Bearer ${token || ""}`; return new Promise((resolve, reject) => { uni.request({ - url: `${BASE_URL}${url}`, + url: `${baseUrl}${url}`, method, header, data, @@ -41,6 +43,7 @@ function request(method, url, data = {}) { const {code, data, message} = res.data; if (code === 0) resolve(data); else if (message) { + const error = {code, data, message}; if (message.indexOf("登录身份已失效") !== -1) { console.log('1111111111111111111,token失效') uni.removeStorageSync( @@ -50,6 +53,10 @@ function request(method, url, data = {}) { reject({ type: "AUTH_INVALID", message }); return; } + if (message.indexOf("已达上限") !== -1) { + reject(error); + return; + } if (message === "ROOM_FULL") { resolve({full: true}); return; @@ -88,8 +95,10 @@ function request(method, url, data = {}) { title: message, icon: "none", }); + reject(error); + return; } - reject(""); + reject({code, data, message}); } }, fail: (err) => { @@ -158,6 +167,10 @@ export const getAppConfig = () => { return request("GET", "/index/appConfig"); }; +export const getDailyCountAPI = () => { + return request("GET", "/index/dailyCount", {}, ADDONS_BASE_URL); +}; + export const getHomeData = (seasonId) => { return request("GET", `/user/myHome?seasonId=${seasonId}`); }; diff --git a/src/canvas.js b/src/canvas.js index e5e41f4..7c5b66b 100644 --- a/src/canvas.js +++ b/src/canvas.js @@ -456,23 +456,29 @@ export const generateShareImage = async (canvasId, data) => { // 2D 即时绘制,无需 ctx.draw() } catch (e) { console.error("generateShareImage 绘制失败:", e); + throw e; } }; // 顶部导入与工具方法 async function getCanvas2DContext(canvasId, targetWidth, targetHeight) { - return new Promise((resolve) => { + return new Promise((resolve, reject) => { const query = uni.createSelectorQuery(); query .select(`#${canvasId}`) .fields({ node: true, size: true }) .exec((res) => { - const { node: canvas } = res[0] || {}; + const canvasInfo = res && res[0]; + const { node: canvas } = canvasInfo || {}; + if (!canvas || typeof canvas.getContext !== "function") { + reject(new Error(`canvas ${canvasId} not found`)); + return; + } const ctx = canvas.getContext("2d"); const dpr = uni.getSystemInfoSync().pixelRatio || 1; - const w = targetWidth || res[0].width; - const h = targetHeight || res[0].height; + const w = targetWidth || canvasInfo.width; + const h = targetHeight || canvasInfo.height; canvas.width = w * dpr; canvas.height = h * dpr; @@ -561,6 +567,7 @@ export const sharePointData = async (canvasId, data) => { // 2D 即时绘制,无需 ctx.draw() } catch (e) { console.error("generateShareImage 绘制失败:", e); + throw e; } }; @@ -778,6 +785,7 @@ export async function sharePractiseData(canvasId, type, user, data) { // 2D 模式下无需 ctx.draw() } catch (err) { console.log(err); + throw err; } } diff --git a/src/pages/battle-room.vue b/src/pages/battle-room.vue index 0449663..2ae2dc3 100644 --- a/src/pages/battle-room.vue +++ b/src/pages/battle-room.vue @@ -7,6 +7,7 @@ import GuideTwo from "@/components/GuideTwo.vue"; import SButton from "@/components/SButton.vue"; import Avatar from "@/components/Avatar.vue"; import ScreenHint from "@/components/ScreenHint.vue"; +import ModalDialog from "@/components/ModalDialog.vue"; import { getRoomAPI, exitRoomAPI, @@ -14,6 +15,7 @@ import { getReadyAPI, kickPlayerAPI, } from "@/apis"; +import { isLimitError } from "@/util"; import { MESSAGETYPES, MESSAGETYPESV2 } from "@/constants"; import useStore from "@/store"; import { storeToRefs } from "pinia"; @@ -56,6 +58,7 @@ const ready = ref(false); const allReady = ref(false); const timer = ref(null); const goBattle = ref(false); +const showLimitModal = ref(false); /** 从结算页返回时为 true,跳过进场靶纸语音 */ const skipTargetAudio = ref(false); @@ -137,7 +140,26 @@ async function refreshRoomData() { } const getReady = async () => { - await getReadyAPI(roomNumber.value); + try { + await getReadyAPI(roomNumber.value); + } catch (error) { + if (isLimitError(error)) { + showLimitModal.value = true; + return; + } + console.log("room ready error", error); + } +}; + +const closeLimitModal = () => { + showLimitModal.value = false; +}; + +const goVipPage = () => { + showLimitModal.value = false; + uni.navigateTo({ + url: "/pages/member/be-vip", + }); }; const refreshMembers = (members = []) => { @@ -456,6 +478,14 @@ onBeforeUnmount(() => { + diff --git a/src/pages/first-try.vue b/src/pages/first-try.vue index 8201644..cc40e37 100644 --- a/src/pages/first-try.vue +++ b/src/pages/first-try.vue @@ -48,6 +48,7 @@ const practiseId = ref(""); const showGuide = ref(false); const laserActive = ref(false); const guideSwiperIndex = ref(0); +const sharing = ref(false); const guideImages = [ "https://static.shelingxingqiu.com/shootmini/static/target.png", @@ -139,8 +140,19 @@ async function onReceiveMessage(msg) { } const onClickShare = debounce(async () => { - await sharePractiseData("shareCanvas", 1, user.value, practiseResult.value); - await wxShare("shareCanvas"); + if (sharing.value) return; + sharing.value = true; + try { + await sharePractiseData("shareCanvas", 1, user.value, practiseResult.value); + await wxShare("shareCanvas"); + } catch (e) { + uni.showToast({ + title: "海报生成失败,请稍后重试", + icon: "none", + }); + } finally { + sharing.value = false; + } }); onMounted(() => { diff --git a/src/pages/friend-battle.vue b/src/pages/friend-battle.vue index de326a8..fd53ad6 100644 --- a/src/pages/friend-battle.vue +++ b/src/pages/friend-battle.vue @@ -8,14 +8,16 @@ import SModal from "@/components/SModal.vue"; import Signin from "@/components/Signin.vue"; import CreateRoom from "@/components/CreateRoom.vue"; import Avatar from "@/components/Avatar.vue"; +import ModalDialog from "@/components/ModalDialog.vue"; -import { getRoomAPI, joinRoomAPI, getBattleDataAPI } from "@/apis"; -import { debounce, canEenter } from "@/util"; +import { getRoomAPI, joinRoomAPI, getBattleDataAPI, getDailyCountAPI } from "@/apis"; +import { debounce, canEenter, getLimitCountText, isLimitReached } from "@/util"; import useStore from "@/store"; import { storeToRefs } from "pinia"; const store = useStore(); -const { user, device, online, game } = storeToRefs(store); +const { user, device, online, game, dailyCount } = storeToRefs(store); +const { updateDailyCount } = store; const showModal = ref(false); const showSignin = ref(false); @@ -24,8 +26,12 @@ const roomNumber = ref(""); const data = ref({}); const roomID = ref(""); const loading = ref(false); +const showLimitModal = ref(false); const isSVip = computed(() => user.value.sVip === true); const isVip = computed(() => user.value.vip === true && !isSVip.value); +const challengeLimitText = computed(() => + getLimitCountText("约战", dailyCount.value.challenge) +); const enterRoom = debounce(async (number) => { if (loading.value) return; @@ -67,9 +73,37 @@ const enterRoom = debounce(async (number) => { }); const onCreateRoom = async () => { if (!canEenter(user.value, device.value, online.value)) return; + const countData = await loadDailyCount(); + if (isLimitReached(countData.challenge)) { + showLimitModal.value = true; + return; + } warnning.value = ""; showModal.value = true; }; + +const closeLimitModal = () => { + showLimitModal.value = false; +}; + +const goVipPage = () => { + showLimitModal.value = false; + uni.navigateTo({ + url: "/pages/member/be-vip", + }); +}; + +const loadDailyCount = async () => { + if (!user.value.id) return dailyCount.value; + try { + const result = await getDailyCountAPI(); + updateDailyCount(result); + return result || dailyCount.value; + } catch (error) { + console.log("load daily count error", error); + return dailyCount.value; + } +}; const onSignin = () => { if (roomID.value && user.value.id) enterRoom(roomID.value); showSignin.value = false; @@ -83,7 +117,10 @@ const goMyRecord = () => { }; onShow(async () => { if (user.value.id) { - const result = await getBattleDataAPI(); + const [result] = await Promise.all([ + getBattleDataAPI(), + loadDailyCount(), + ]); data.value = result; } }); @@ -168,8 +205,8 @@ onLoad(async (options) => { - - + + {{ challengeLimitText }} 创建约战房 @@ -186,6 +223,14 @@ onLoad(async (options) => { +