This commit is contained in:
Your Name
2026-08-11 17:41:36 +08:00
parent cfe4c82c90
commit 03fe4ddf9d
18771 changed files with 3617239 additions and 0 deletions
@@ -0,0 +1,64 @@
"use strict";
const common_vendor = require("../../../common/vendor.js");
function useGameAuth(proxy) {
let loginPromise = null;
function hasToken() {
return !!String(common_vendor.index.getStorageSync("token") || "").trim();
}
function clearToken() {
common_vendor.index.removeStorageSync("token");
}
function wxLoginCode() {
return new Promise((resolve, reject) => {
common_vendor.index.login({
provider: "weixin",
success: (res) => (res == null ? void 0 : res.code) ? resolve(res.code) : reject(new Error("微信登录未返回 code")),
fail: reject
});
});
}
async function loginWithCode(code) {
var _a;
const res = await proxy.apiUrl({
url: "/api/login/mnpLogin",
method: "POST",
data: { code }
}, false);
if ((res == null ? void 0 : res.code) !== 1 || !((_a = res.data) == null ? void 0 : _a.token)) {
clearToken();
throw new Error((res == null ? void 0 : res.msg) || "登录失败");
}
common_vendor.index.setStorageSync("token", res.data.token);
common_vendor.index.setStorageSync("userData", res.data);
return true;
}
async function verifyOrLogin() {
if (hasToken()) {
try {
const res = await proxy.apiUrl({ url: "/api/user/info", method: "POST" }, false);
if ((res == null ? void 0 : res.code) === 1 && res.data) {
common_vendor.index.setStorageSync("userData", res.data);
return true;
}
if ((res == null ? void 0 : res.code) !== -1)
return true;
} catch (_) {
return true;
}
clearToken();
}
const code = await wxLoginCode();
return loginWithCode(code);
}
async function ensureLoggedIn() {
if (loginPromise)
return loginPromise;
loginPromise = verifyOrLogin().then((ok) => !!ok).catch(() => false).finally(() => {
loginPromise = null;
});
return loginPromise;
}
return { ensureLoggedIn };
}
exports.useGameAuth = useGameAuth;
//# sourceMappingURL=../../../../.sourcemap/mp-weixin/tongji/endless-game/composables/useGameAuth.js.map
@@ -0,0 +1,240 @@
"use strict";
const common_vendor = require("../../../common/vendor.js");
const EMPTY_BOARD = {
week_start: "",
week_end: "",
sex_label: "同行",
group_size: 7,
member_count: 0,
players: [],
me: { count: 0, rank: 1, best_score: 0, distance: 0, is_first: true },
invite_code: ""
};
const PENDING_SYNC_KEY = "tongji_endless_pending_sync_v1";
function createSessionKey() {
const random = Math.random().toString(36).slice(2, 12);
return `game_${Date.now().toString(36)}_${random}`;
}
function readPendingPayloads() {
try {
const stored = common_vendor.index.getStorageSync(PENDING_SYNC_KEY);
if (!Array.isArray(stored))
return [];
return stored.filter((item) => item && /^[A-Za-z0-9_-]{16,64}$/.test(String(item.session_key || "")) && Number(item.learned_count) >= 0).slice(-8);
} catch (_) {
return [];
}
}
function useGamePlatform(proxy, ensureLoggedIn) {
const connected = common_vendor.ref(false);
const loading = common_vendor.ref(false);
const syncStatus = common_vendor.ref("idle");
const leaderboard = common_vendor.ref({ ...EMPTY_BOARD });
const confirmedSessionLearned = common_vendor.ref(0);
let sessionKey = createSessionKey();
let syncTimer = null;
let syncing = false;
let queuedPayloads = readPendingPayloads();
let connectPromise = null;
function persistPendingPayloads() {
try {
common_vendor.index.setStorageSync(PENDING_SYNC_KEY, queuedPayloads.slice(-8));
} catch (_) {
}
}
async function api(request) {
if (!(proxy == null ? void 0 : proxy.apiUrl))
throw new Error("平台接口未初始化");
return proxy.apiUrl(request, false);
}
function applyLeaderboard(data, confirmedForSession = "") {
if (!data || !Array.isArray(data.players))
return;
const inactiveSessionResponse = confirmedForSession && confirmedForSession !== sessionKey;
if (!inactiveSessionResponse) {
leaderboard.value = {
...EMPTY_BOARD,
...data,
me: { ...EMPTY_BOARD.me, ...data.me || {} },
players: data.players
};
}
if (confirmedForSession === sessionKey && data.confirmed_session_learned != null) {
confirmedSessionLearned.value = Number(data.confirmed_session_learned) || 0;
}
connected.value = true;
syncStatus.value = "synced";
}
async function refreshLeaderboard() {
const res = await api({
url: "/api/tcm/gameWeeklyLeaderboard",
method: "GET"
});
if ((res == null ? void 0 : res.code) !== 1 || !res.data) {
throw new Error((res == null ? void 0 : res.msg) || "同行榜加载失败");
}
applyLeaderboard(res.data);
return res.data;
}
async function acceptShare(inviteCode) {
const code = String(inviteCode || "").trim().toUpperCase();
if (!code)
return;
try {
await api({
url: "/api/tcm/gameAcceptShare",
method: "POST",
data: { invite_code: code }
});
} catch (_) {
}
}
async function connect(inviteCode = "") {
if (connectPromise)
return connectPromise;
connectPromise = (async () => {
loading.value = true;
try {
const loggedIn = await ensureLoggedIn();
if (!loggedIn)
throw new Error("登录失败");
await acceptShare(inviteCode);
await refreshLeaderboard();
if (queuedPayloads.length)
flushProgress();
return true;
} catch (_) {
connected.value = false;
syncStatus.value = "offline";
return false;
} finally {
loading.value = false;
connectPromise = null;
}
})();
return connectPromise;
}
function beginSession() {
if (syncTimer)
clearTimeout(syncTimer);
sessionKey = createSessionKey();
confirmedSessionLearned.value = 0;
syncStatus.value = queuedPayloads.length ? "pending" : connected.value ? "synced" : "offline";
return sessionKey;
}
function queueProgress(learnedCount, score, ended = false) {
const nextPayload = {
session_key: sessionKey,
learned_count: Math.max(0, Number(learnedCount) || 0),
score: Math.max(0, Number(score) || 0),
ended: ended ? 1 : 0
};
const existingIndex = queuedPayloads.findIndex((item) => item.session_key === sessionKey);
if (existingIndex >= 0) {
const existing = queuedPayloads[existingIndex];
queuedPayloads[existingIndex] = {
...nextPayload,
learned_count: Math.max(existing.learned_count, nextPayload.learned_count),
score: Math.max(existing.score, nextPayload.score),
ended: Math.max(existing.ended, nextPayload.ended)
};
} else {
queuedPayloads.push(nextPayload);
}
persistPendingPayloads();
syncStatus.value = "pending";
if (syncTimer)
clearTimeout(syncTimer);
if (ended) {
flushProgress();
} else {
syncTimer = setTimeout(flushProgress, 1e3);
}
}
async function flushProgress() {
if (syncTimer)
clearTimeout(syncTimer);
syncTimer = null;
if (syncing || !queuedPayloads.length)
return;
const payload = queuedPayloads[0];
syncing = true;
syncStatus.value = "syncing";
try {
if (!connected.value) {
const loggedIn = await ensureLoggedIn();
if (!loggedIn)
throw new Error("未登录");
}
const res = await api({
url: "/api/tcm/gameSubmitProgress",
method: "POST",
data: payload
});
if ((res == null ? void 0 : res.code) !== 1 || !res.data) {
throw new Error((res == null ? void 0 : res.msg) || "成绩保存失败");
}
applyLeaderboard(res.data, payload.session_key);
queuedPayloads = queuedPayloads.filter((item) => item.session_key !== payload.session_key || Number(item.learned_count) > Number(payload.learned_count) || Number(item.score) > Number(payload.score) || Number(item.ended) > Number(payload.ended));
persistPendingPayloads();
} catch (_) {
connected.value = false;
syncStatus.value = "offline";
persistPendingPayloads();
} finally {
syncing = false;
if (queuedPayloads.length && connected.value) {
setTimeout(flushProgress, 80);
}
}
}
async function syncAndRefresh(learnedCount, score) {
queueProgress(learnedCount, score, false);
await flushProgress();
if (!connected.value) {
await connect();
if (queuedPayloads.length)
await flushProgress();
} else {
try {
await refreshLeaderboard();
} catch (_) {
}
}
}
async function recordShare() {
var _a;
try {
if (!connected.value && !await connect())
return;
const res = await api({ url: "/api/tcm/gameRecordShare", method: "POST" });
if ((res == null ? void 0 : res.code) === 1 && ((_a = res.data) == null ? void 0 : _a.invite_code)) {
leaderboard.value = { ...leaderboard.value, invite_code: res.data.invite_code };
}
} catch (_) {
}
}
function dispose() {
if (syncTimer)
clearTimeout(syncTimer);
syncTimer = null;
persistPendingPayloads();
}
return {
connected,
loading,
syncStatus,
leaderboard,
confirmedSessionLearned,
connect,
beginSession,
queueProgress,
flushProgress,
syncAndRefresh,
refreshLeaderboard,
recordShare,
dispose
};
}
exports.useGamePlatform = useGamePlatform;
//# sourceMappingURL=../../../../.sourcemap/mp-weixin/tongji/endless-game/composables/useGamePlatform.js.map
@@ -0,0 +1,366 @@
"use strict";
const common_vendor = require("../../../common/vendor.js");
const STORAGE_KEY = "tongji_game_sfx_enabled";
const COS = "https://gz-1349751149.cos.ap-guangzhou.myqcloud.com/uploads/file";
const SRC = {
tap: `${COS}/20260526/20260526105128557ef8669.mp3`,
swoosh: `${COS}/20260527/202605271539371ebe13672.mp3`,
pop: `${COS}/20260526/202605261051282a0a94508.mp3`,
buzz: `${COS}/20260527/202605271539376ff628472.mp3`,
chime: `${COS}/20260528/202605280937077bad76716.mp3`,
fanfare: `${COS}/20260528/20260528093707b021f5794.mp3`,
blast: `${COS}/20260528/20260528093707ebd4d5733.mp3`,
sparkle: `${COS}/20260528/20260528093707016f07427.mp3`,
stinger: `${COS}/20260528/20260528093709c669f4659.mp3`
};
const SFX = {
// —— 基础素材别名(供分层音效直接调用)——
tap: { src: SRC.tap, volume: 0.32, rate: 1, pool: 3 },
pop: { src: SRC.pop, volume: 0.3, rate: 1, pool: 4 },
fanfare: { src: SRC.fanfare, volume: 0.56, rate: 1, pool: 2 },
blast: { src: SRC.blast, volume: 0.62, rate: 1, pool: 2 },
sparkle: { src: SRC.sparkle, volume: 0.5, rate: 1, pool: 2 },
// —— 交互 ——
select: { src: SRC.tap, volume: 0.32, rate: 1.05, pool: 3 },
deselect: { src: SRC.tap, volume: 0.22, rate: 0.82, pool: 2 },
hint: { src: SRC.tap, volume: 0.26, rate: 1.28, pool: 2 },
swap: { src: SRC.swoosh, volume: 0.38, rate: 1, pool: 3 },
swapFail: { src: SRC.buzz, volume: 0.36, rate: 1.18, pool: 2 },
drop: { src: SRC.pop, volume: 0.3, rate: 0.88, pool: 4 },
dropLight: { src: SRC.pop, volume: 0.22, rate: 1.22, pool: 2 },
// —— 消除(按连数)——
match3: { src: SRC.chime, volume: 0.48, rate: 1, pool: 4 },
match4: { src: SRC.fanfare, volume: 0.54, rate: 1.06, pool: 3 },
match5: { src: SRC.blast, volume: 0.62, rate: 1, pool: 3 },
// —— 消除(按 GI 主色)——
matchLowGi: { src: SRC.chime, volume: 0.42, rate: 0.86, pool: 2 },
matchMidGi: { src: SRC.chime, volume: 0.46, rate: 1.02, pool: 2 },
matchHighGi: { src: SRC.buzz, volume: 0.44, rate: 0.92, pool: 2 },
// —— 连消 ——
combo2: { src: SRC.fanfare, volume: 0.56, rate: 1.12, pool: 2 },
combo3: { src: SRC.blast, volume: 0.64, rate: 1.08, pool: 2 },
comboMega: { src: SRC.blast, volume: 0.72, rate: 1.22, pool: 2 },
// —— 道具 ——
insulin: { src: SRC.sparkle, volume: 0.5, rate: 1.15, pool: 2 },
fiber: { src: SRC.swoosh, volume: 0.48, rate: 1.32, pool: 2 },
meal: { src: SRC.stinger, volume: 0.42, rate: 1.35, pool: 1 },
reshuffle: { src: SRC.swoosh, volume: 0.46, rate: 0.72, pool: 2 },
// —— 反馈 ——
score: { src: SRC.tap, volume: 0.28, rate: 1.38, pool: 2 },
goalTick: { src: SRC.chime, volume: 0.36, rate: 1.45, pool: 2 },
meterWarn: { src: SRC.buzz, volume: 0.5, rate: 1, pool: 2 },
meterDanger: { src: SRC.buzz, volume: 0.58, rate: 0.78, pool: 2 },
meterStable: { src: SRC.pop, volume: 0.24, rate: 1.05, pool: 1 },
win: { src: SRC.fanfare, volume: 0.66, rate: 1, pool: 1 },
winStinger: { src: SRC.stinger, volume: 0.38, rate: 1.5, pool: 1 },
lose: { src: SRC.buzz, volume: 0.52, rate: 0.68, pool: 1 },
// 兼容旧名
match: { src: SRC.chime, volume: 0.48, rate: 1, pool: 4 },
combo: { src: SRC.fanfare, volume: 0.56, rate: 1.1, pool: 3 },
heal: { src: SRC.pop, volume: 0.44, rate: 1.18, pool: 2 },
boost: { src: SRC.sparkle, volume: 0.52, rate: 1, pool: 2 },
invalid: { src: SRC.buzz, volume: 0.4, rate: 1.15, pool: 2 },
danger: { src: SRC.blast, volume: 0.58, rate: 0.95, pool: 2 }
};
const WARMUP_SFX = [
"select",
"swap",
"swapFail",
"drop",
"dropLight",
"match3",
"match4",
"match5",
"combo2",
"combo3",
"comboMega",
"insulin",
"sparkle",
"reshuffle",
"lose"
];
function createAudioContext() {
if (typeof Audio !== "undefined") {
const audio = new Audio();
audio.preload = "auto";
return {
get src() {
return audio.src;
},
set src(value) {
audio.src = value;
},
get volume() {
return audio.volume;
},
set volume(value) {
audio.volume = value;
},
get playbackRate() {
return audio.playbackRate;
},
set playbackRate(value) {
audio.playbackRate = value;
},
play() {
const promise = audio.play();
if (promise == null ? void 0 : promise.catch)
promise.catch(() => {
});
},
stop() {
audio.pause();
try {
audio.currentTime = 0;
} catch (_) {
}
},
seek(time) {
try {
audio.currentTime = time;
} catch (_) {
}
},
destroy() {
audio.pause();
audio.removeAttribute("src");
}
};
}
return common_vendor.index.createInnerAudioContext();
}
class SfxPool {
constructor(src, size, volume, rate = 1) {
this.src = src;
this.size = size;
this.volume = volume;
this.rate = rate;
this.list = [];
this.cursor = 0;
this.warmedUp = false;
}
createOne() {
const ctx = createAudioContext();
ctx.src = this.src;
ctx.obeyMuteSwitch = false;
ctx.autoplay = false;
ctx.volume = this.volume;
try {
ctx.playbackRate = this.rate;
} catch (_) {
}
this.list.push(ctx);
return ctx;
}
create() {
while (this.list.length < this.size)
this.createOne();
}
warmUp() {
if (this.warmedUp)
return;
const ctx = this.list[0] || this.createOne();
try {
ctx.volume = 0;
ctx.play();
setTimeout(() => {
try {
ctx.stop();
ctx.volume = this.volume;
} catch (_) {
}
}, 60);
} catch (_) {
}
if (this.size > 1)
this.cursor = 1;
this.warmedUp = true;
}
play(scale = 1, rateMul = 1) {
if (this.list.length < this.size)
this.create();
const ctx = this.list[this.cursor % this.list.length];
this.cursor += 1;
const vol = Math.min(1, this.volume * scale);
const rate = Math.min(2, Math.max(0.5, this.rate * rateMul));
try {
ctx.volume = vol;
try {
ctx.playbackRate = rate;
} catch (_) {
}
ctx.stop();
ctx.seek(0);
ctx.play();
} catch (_) {
try {
ctx.play();
} catch (e) {
}
}
}
destroy() {
this.list.forEach((ctx) => {
try {
ctx.stop();
ctx.destroy();
} catch (_) {
}
});
this.list = [];
this.warmedUp = false;
}
}
function dominantGi(tiles = []) {
const cnt = { low: 0, mid: 0, high: 0 };
tiles.forEach((t) => {
var _a;
const g = (_a = t == null ? void 0 : t.type) == null ? void 0 : _a.gi;
if (g && cnt[g] !== void 0)
cnt[g] += 1;
});
let best = "mid";
let max = 0;
Object.entries(cnt).forEach(([k, v]) => {
if (v > max) {
max = v;
best = k;
}
});
return best;
}
function useGameSfx() {
const enabled = common_vendor.ref(true);
const pools = {};
try {
const stored = common_vendor.index.getStorageSync(STORAGE_KEY);
if (stored === false || stored === "0" || stored === 0) {
enabled.value = false;
}
} catch (_) {
}
function ensureAudioOption() {
try {
common_vendor.index.setInnerAudioOption({
obeyMuteSwitch: false,
mixWithOther: true
});
} catch (_) {
}
}
function getPool(name) {
const cfg = SFX[name];
if (!cfg)
return null;
if (!pools[name]) {
pools[name] = new SfxPool(cfg.src, cfg.pool, cfg.volume, cfg.rate ?? 1);
}
return pools[name];
}
function warmUp() {
if (!enabled.value)
return;
ensureAudioOption();
const names = typeof Audio !== "undefined" ? ["select"] : WARMUP_SFX;
names.forEach((name) => {
var _a;
(_a = getPool(name)) == null ? void 0 : _a.warmUp();
});
}
function play(name, scale = 1, rateMul = 1) {
var _a;
if (!enabled.value)
return;
(_a = getPool(name)) == null ? void 0 : _a.play(scale, rateMul);
}
function playLayer(primary, secondary, delayMs = 70, secScale = 0.6) {
play(primary);
if (secondary) {
setTimeout(() => play(secondary, secScale), delayMs);
}
}
function playMatch({ matchCount = 3, chain = 0, tiles = [] } = {}) {
if (!enabled.value)
return;
const giTrack = (() => {
const gi = dominantGi(tiles);
if (gi === "low")
return "matchLowGi";
if (gi === "high")
return "matchHighGi";
return "matchMidGi";
})();
if (chain >= 3) {
playLayer("comboMega", giTrack, 90, 0.55);
return;
}
if (chain === 2) {
playLayer("combo3", giTrack, 80, 0.5);
return;
}
if (chain === 1) {
playLayer("combo2", giTrack, 70, 0.45);
return;
}
if (matchCount >= 5) {
playLayer("match5", "matchHighGi", 85, 0.5);
} else if (matchCount >= 4) {
playLayer("match4", giTrack, 65, 0.48);
} else {
play(giTrack, 1, 1);
setTimeout(() => play("match3", 0.85), 40);
}
}
function playDrop(count = 1) {
if (!enabled.value || count <= 0)
return;
const name = count >= 4 ? "drop" : "dropLight";
play(name, Math.min(1.2, 0.85 + count * 0.04), count >= 6 ? 0.9 : 1);
}
function playMeter(delta, level) {
if (!enabled.value)
return;
if (level > 70)
play("meterDanger", 1 + (level - 70) * 0.02);
else if (level < 30)
play("meterWarn", 0.9, 0.95);
else if (delta > 8)
play("meterWarn", 0.75);
else if (delta < -5)
play("meterStable", 1.1);
}
function toggleEnabled() {
enabled.value = !enabled.value;
try {
common_vendor.index.setStorageSync(STORAGE_KEY, enabled.value ? "1" : "0");
} catch (_) {
}
if (enabled.value) {
warmUp();
play("select");
}
}
function destroyAll() {
Object.keys(pools).forEach((key) => {
var _a;
(_a = pools[key]) == null ? void 0 : _a.destroy();
delete pools[key];
});
}
common_vendor.onUnmounted(() => {
destroyAll();
});
return {
enabled,
play,
playLayer,
playMatch,
playDrop,
playMeter,
warmUp,
toggleEnabled,
destroyAll
};
}
exports.useGameSfx = useGameSfx;
//# sourceMappingURL=../../../../.sourcemap/mp-weixin/tongji/endless-game/composables/useGameSfx.js.map
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,11 @@
{
"navigationStyle": "custom",
"navigationBarTitleText": "识糖小课堂",
"backgroundColor": "#eefbf4",
"disableScroll": false,
"enableShareAppMessage": true,
"enableShareTimeline": true,
"usingComponents": {
"tongji-icon": "../components/TongjiIcon"
}
}
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large Load Diff