feat(tongji): 接入小游戏真实周榜与微信分享
This commit is contained in:
@@ -197,9 +197,9 @@
|
||||
<view>
|
||||
<view class="eg-weekly-title-line">
|
||||
<text class="eg-weekly-title">本周7人同行榜</text>
|
||||
<text class="eg-weekly-preview">界面预览</text>
|
||||
<text class="eg-weekly-preview" :class="{ 'is-offline': !platformConnected }">{{ weeklyStatusLabel }}</text>
|
||||
</view>
|
||||
<text class="eg-weekly-sub">{{ weeklyRangeLabel }} · 每周一更新</text>
|
||||
<text class="eg-weekly-sub">{{ weeklyRangeLabel }} · {{ weeklySexLabel }} · {{ weeklyMemberCount }}/7人</text>
|
||||
</view>
|
||||
<view class="eg-weekly-medal">{{ weeklyMyRank }}</view>
|
||||
</view>
|
||||
@@ -225,7 +225,10 @@
|
||||
:class="{ 'is-me': player.isMe }"
|
||||
>
|
||||
<text class="eg-weekly-place">{{ player.rank }}</text>
|
||||
<view class="eg-weekly-avatar" :class="`is-tone-${player.tone}`">{{ player.avatar }}</view>
|
||||
<view class="eg-weekly-avatar" :class="`is-tone-${player.tone}`">
|
||||
<image v-if="player.avatarUrl" class="eg-weekly-avatar-image" :src="player.avatarUrl" mode="aspectFill" />
|
||||
<text v-else>{{ player.avatar }}</text>
|
||||
</view>
|
||||
<text class="eg-weekly-name">{{ player.name }}</text>
|
||||
<text v-if="player.isMe" class="eg-weekly-me">我</text>
|
||||
<text class="eg-weekly-count">{{ player.count }}</text>
|
||||
@@ -242,7 +245,8 @@
|
||||
</view>
|
||||
|
||||
<view class="eg-weekly-start" @tap="closeWeeklyRank">开始消除</view>
|
||||
<text class="eg-weekly-footnote">当前为排行效果预览,真实好友数据接入后替换</text>
|
||||
<button class="eg-weekly-share" open-type="share">邀请亲友一起认糖</button>
|
||||
<text class="eg-weekly-footnote">使用平台昵称参加真实周榜,每周一重新分组;分享不会自动绑定家庭关系</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@@ -284,6 +288,7 @@
|
||||
<view class="eg-result-score"><text>本局得分</text><text>{{ score }}</text></view>
|
||||
<view class="eg-result-best"><text>历史最高</text><text>{{ bestScore }}</text></view>
|
||||
<view class="eg-primary-btn" @tap="startGame">再玩一局</view>
|
||||
<button class="eg-share-btn" open-type="share">分享本局认糖成绩</button>
|
||||
<view class="eg-secondary-btn" @tap="goBack">返回血糖周报</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -292,10 +297,12 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, ref } from 'vue'
|
||||
import { onLoad, onUnload } from '@dcloudio/uni-app'
|
||||
import { computed, getCurrentInstance, ref } from 'vue'
|
||||
import { onLoad, onShareAppMessage, onShareTimeline, onShow, onUnload } from '@dcloudio/uni-app'
|
||||
import TongjiIcon from './components/TongjiIcon.vue'
|
||||
import { useGameSfx } from './composables/useGameSfx.js'
|
||||
import { useGamePlatform } from './composables/useGamePlatform.js'
|
||||
import { useTongjiAuth } from '../composables/useTongjiAuth.js'
|
||||
|
||||
const FOOD_IMAGE_MODULES = import.meta.glob('./assets/food/*.jpg', {
|
||||
eager: true,
|
||||
@@ -318,20 +325,6 @@ const COMBO_PITY_TURNS = 6
|
||||
const MEGA_COMBO_SCORE = 300
|
||||
const DANGER_DROP_CHANCE = 0.05 + GAME_DIFFICULTY * 0.01
|
||||
const COLUMN_GROUP_BIAS = 0.86 - GAME_DIFFICULTY * 0.028
|
||||
const WEEKLY_PREVIEW_BASE = 208
|
||||
const WEEKLY_PREVIEW_PLAYERS = [
|
||||
{ id: 'li', name: '李阿姨', avatar: '李', count: 286, tone: 1 },
|
||||
{ id: 'health', name: '健康每一天', avatar: '健', count: 248, tone: 2 },
|
||||
{ id: 'wang', name: '王师傅', avatar: '王', count: 226, tone: 3 },
|
||||
{ id: 'me', name: '我', avatar: '我', count: 0, tone: 4, isMe: true },
|
||||
{ id: 'happy', name: '快乐生活', avatar: '乐', count: 191, tone: 5 },
|
||||
{ id: 'chen', name: '陈叔', avatar: '陈', count: 174, tone: 6 },
|
||||
{ id: 'sunny', name: '天天好心情', avatar: '晴', count: 152, tone: 7 }
|
||||
]
|
||||
const WEEKLY_START_RANK = WEEKLY_PREVIEW_PLAYERS
|
||||
.map(player => ({ ...player, count: player.isMe ? WEEKLY_PREVIEW_BASE : player.count }))
|
||||
.sort((a, b) => b.count - a.count)
|
||||
.findIndex(player => player.isMe) + 1
|
||||
const CHEER_MESSAGES = [
|
||||
{ source: '同行鼓励', text: '你需要加油哦!再向前一点,就能超过上一名。', tone: 'warm' },
|
||||
{ source: '同行鼓励', text: '你太棒了!今天也在认真认识食物。', tone: 'bright' },
|
||||
@@ -404,6 +397,23 @@ const {
|
||||
warmUp: warmUpSfx
|
||||
} = useGameSfx()
|
||||
|
||||
const { proxy } = getCurrentInstance()
|
||||
const { ensureLoggedIn } = useTongjiAuth(proxy)
|
||||
const {
|
||||
connected: platformConnected,
|
||||
loading: platformLoading,
|
||||
syncStatus: platformSyncStatus,
|
||||
leaderboard: platformLeaderboard,
|
||||
confirmedSessionLearned,
|
||||
connect: connectPlatform,
|
||||
beginSession: beginPlatformSession,
|
||||
queueProgress: queuePlatformProgress,
|
||||
flushProgress: flushPlatformProgress,
|
||||
syncAndRefresh: syncAndRefreshPlatform,
|
||||
recordShare: recordPlatformShare,
|
||||
dispose: disposePlatform
|
||||
} = useGamePlatform(proxy, ensureLoggedIn)
|
||||
|
||||
const board = ref([])
|
||||
const selected = ref(-1)
|
||||
const score = ref(0)
|
||||
@@ -441,7 +451,6 @@ const reshuffleDone = ref(false)
|
||||
const reshuffling = ref(false)
|
||||
const confettiVisible = ref(false)
|
||||
const weeklyRankVisible = ref(false)
|
||||
const weeklyPreviewProgress = ref(0)
|
||||
const cheerIndex = ref(0)
|
||||
const CONFETTI_COLORS = ['#ff3154', '#ffd928', '#28c76f', '#4d8dff', '#a855f7', '#ff7a1a']
|
||||
const confettiPieces = Array.from({ length: 34 }, (_, index) => ({
|
||||
@@ -466,6 +475,7 @@ let taskRewardTimer = null
|
||||
let pendingThemeMatchCount = 0
|
||||
let taskRewardStatusUntil = 0
|
||||
let sfxGesturePrimed = false
|
||||
let incomingInviteCode = ''
|
||||
|
||||
const currentTheme = computed(() => FOOD_GROUPS[themeIndex.value % FOOD_GROUPS.length])
|
||||
const currentTask = computed(() => {
|
||||
@@ -483,13 +493,38 @@ const highCount = computed(() => board.value.filter(tile => tile && foodOf(tile.
|
||||
const boardRows = computed(() => Array.from({ length: ROWS }, (_, row) => (
|
||||
board.value.slice(row * COLS, row * COLS + COLS).map((tile, col) => ({ ...tile, index: row * COLS + col }))
|
||||
)))
|
||||
const weeklyMyCount = computed(() => WEEKLY_PREVIEW_BASE + weeklyPreviewProgress.value)
|
||||
const weeklyRankRows = computed(() => WEEKLY_PREVIEW_PLAYERS
|
||||
.map(player => ({ ...player, count: player.isMe ? weeklyMyCount.value : player.count }))
|
||||
.sort((a, b) => b.count - a.count)
|
||||
.map((player, index) => ({ ...player, rank: index + 1 })))
|
||||
const weeklyMyRank = computed(() => weeklyRankRows.value.find(player => player.isMe)?.rank || 7)
|
||||
const weeklyRankRise = computed(() => Math.max(0, WEEKLY_START_RANK - weeklyMyRank.value))
|
||||
const pendingWeeklyLearned = computed(() => Math.max(0, learned.value - confirmedSessionLearned.value))
|
||||
const weeklyMyCount = computed(() => Number(platformLeaderboard.value.me?.count || 0) + pendingWeeklyLearned.value)
|
||||
const weeklyRankRows = computed(() => {
|
||||
const remote = Array.isArray(platformLeaderboard.value.players)
|
||||
? platformLeaderboard.value.players
|
||||
: []
|
||||
const userData = uni.getStorageSync('userData') || {}
|
||||
const fallbackName = String(userData.nickname || '我')
|
||||
const source = remote.length
|
||||
? remote.map((player, index) => ({
|
||||
...player,
|
||||
isMe: !!(player.is_me || player.isMe),
|
||||
avatarUrl: player.avatar || '',
|
||||
avatar: String(player.name || '同行').slice(0, 1),
|
||||
tone: index % 7 + 1,
|
||||
count: player.is_me || player.isMe ? weeklyMyCount.value : Number(player.count || 0)
|
||||
}))
|
||||
: [{
|
||||
id: 'local-me',
|
||||
name: fallbackName,
|
||||
avatar: fallbackName.slice(0, 1) || '我',
|
||||
avatarUrl: userData.avatar || '',
|
||||
count: weeklyMyCount.value,
|
||||
tone: 1,
|
||||
isMe: true
|
||||
}]
|
||||
return source
|
||||
.sort((a, b) => b.count - a.count || Number(b.best_score || 0) - Number(a.best_score || 0))
|
||||
.map((player, index) => ({ ...player, rank: index + 1 }))
|
||||
})
|
||||
const weeklyMyRank = computed(() => weeklyRankRows.value.find(player => player.isMe)?.rank || 1)
|
||||
const weeklyRankRise = computed(() => 0)
|
||||
const weeklyIsFirst = computed(() => weeklyMyRank.value === 1)
|
||||
const weeklyDistance = computed(() => {
|
||||
const mineIndex = weeklyRankRows.value.findIndex(player => player.isMe)
|
||||
@@ -497,17 +532,60 @@ const weeklyDistance = computed(() => {
|
||||
return Math.max(1, weeklyRankRows.value[mineIndex - 1].count - weeklyMyCount.value + 1)
|
||||
})
|
||||
const currentCheer = computed(() => CHEER_MESSAGES[cheerIndex.value % CHEER_MESSAGES.length])
|
||||
const weeklyRangeLabel = computed(() => getWeekRangeLabel(new Date()))
|
||||
const weeklyRangeLabel = computed(() => {
|
||||
const start = platformLeaderboard.value.week_start
|
||||
const end = platformLeaderboard.value.week_end
|
||||
if (!start || !end) return getWeekRangeLabel(new Date())
|
||||
return `${formatWeekDate(start)}—${formatWeekDate(end)}`
|
||||
})
|
||||
const weeklySexLabel = computed(() => platformLeaderboard.value.sex_label || '同行')
|
||||
const weeklyMemberCount = computed(() => Math.max(1, Number(platformLeaderboard.value.member_count || weeklyRankRows.value.length)))
|
||||
const weeklyStatusLabel = computed(() => {
|
||||
if (platformLoading.value) return '连接中'
|
||||
if (!platformConnected.value) return '离线记录中'
|
||||
if (platformSyncStatus.value === 'pending' || platformSyncStatus.value === 'syncing') return '保存中'
|
||||
return '真实周榜'
|
||||
})
|
||||
|
||||
onLoad(() => {
|
||||
onShareAppMessage(() => {
|
||||
recordPlatformShare()
|
||||
return {
|
||||
title: gameShareTitle(),
|
||||
path: gameSharePath()
|
||||
}
|
||||
})
|
||||
|
||||
onShareTimeline(() => {
|
||||
recordPlatformShare()
|
||||
const inviteCode = String(platformLeaderboard.value.invite_code || '')
|
||||
return {
|
||||
title: gameShareTitle(),
|
||||
query: inviteCode ? `from=share&invite_code=${encodeURIComponent(inviteCode)}` : 'from=share'
|
||||
}
|
||||
})
|
||||
|
||||
onLoad((options = {}) => {
|
||||
const info = uni.getSystemInfoSync()
|
||||
safeTop.value = info.safeAreaInsets?.top || info.statusBarHeight || 20
|
||||
bestScore.value = Number(uni.getStorageSync('tongji_endless_best') || 0)
|
||||
incomingInviteCode = String(options.invite_code || '').trim()
|
||||
startGame()
|
||||
weeklyRankVisible.value = true
|
||||
connectPlatform(incomingInviteCode)
|
||||
})
|
||||
|
||||
onShow(() => {
|
||||
connectPlatform(incomingInviteCode).then((ok) => {
|
||||
if (ok) flushPlatformProgress()
|
||||
})
|
||||
})
|
||||
|
||||
onUnload(() => {
|
||||
if (learned.value > 0 || gameOver.value) {
|
||||
queuePlatformProgress(learned.value, score.value, gameOver.value)
|
||||
flushPlatformProgress()
|
||||
}
|
||||
disposePlatform()
|
||||
if (hintTimer) clearTimeout(hintTimer)
|
||||
if (jackpotTimer) clearTimeout(jackpotTimer)
|
||||
if (confettiTimer) clearTimeout(confettiTimer)
|
||||
@@ -534,6 +612,25 @@ function getWeekRangeLabel(now) {
|
||||
return `${label(monday)}—${label(sunday)}`
|
||||
}
|
||||
|
||||
function formatWeekDate(value) {
|
||||
const parts = String(value).split('-').map(Number)
|
||||
if (parts.length !== 3) return String(value)
|
||||
return `${parts[1]}月${parts[2]}日`
|
||||
}
|
||||
|
||||
function gameShareTitle() {
|
||||
if (learned.value > 0) {
|
||||
return `我在控糖消消乐认识了${learned.value}份食物,一起来挑战吧`
|
||||
}
|
||||
return '控糖消消乐:边玩边认识常见食物'
|
||||
}
|
||||
|
||||
function gameSharePath() {
|
||||
const inviteCode = String(platformLeaderboard.value.invite_code || '')
|
||||
const query = inviteCode ? `?from=share&invite_code=${encodeURIComponent(inviteCode)}` : '?from=share'
|
||||
return `/tongji/endless-game/index${query}`
|
||||
}
|
||||
|
||||
function openWeeklyRank() {
|
||||
primeSfxFromGesture()
|
||||
if (gameOver.value) return
|
||||
@@ -542,6 +639,7 @@ function openWeeklyRank() {
|
||||
return
|
||||
}
|
||||
weeklyRankVisible.value = true
|
||||
syncAndRefreshPlatform(learned.value, score.value)
|
||||
playSfx('select', .72)
|
||||
}
|
||||
|
||||
@@ -560,6 +658,7 @@ function cycleCheer() {
|
||||
|
||||
function startGame() {
|
||||
if (hintTimer) clearTimeout(hintTimer)
|
||||
beginPlatformSession()
|
||||
themeIndex.value = 0
|
||||
board.value = buildPlayableBoard(openingKeysForTheme(currentTheme.value))
|
||||
selected.value = -1
|
||||
@@ -798,6 +897,7 @@ async function trySwap(a, b, soundPlayed = false) {
|
||||
endedFood.value = foodOf(highTile.key)
|
||||
saveBest()
|
||||
gameOver.value = true
|
||||
queuePlatformProgress(learned.value, score.value, true)
|
||||
busy.value = false
|
||||
return
|
||||
}
|
||||
@@ -872,10 +972,10 @@ async function resolveMatches(initialGroups, movedIndex) {
|
||||
vibrate(largestMatch >= 5 || wave >= 3 ? 'medium' : 'light')
|
||||
turnConsumed += removedNormal.length
|
||||
learned.value += removedNormal.length
|
||||
weeklyPreviewProgress.value += removedNormal.length
|
||||
updateFoodTask(removedNormal)
|
||||
const multiplier = feastMoves.value > 0 ? 2 : 1
|
||||
score.value += (removedNormal.length * 12 + Math.max(0, wave - 1) * 20 + madeSpecial * 35) * multiplier
|
||||
queuePlatformProgress(learned.value, score.value, false)
|
||||
await wait(specialEffects.hasFire ? 330 : 210)
|
||||
const next = [...board.value]
|
||||
remove.forEach(index => { next[index] = null })
|
||||
|
||||
Reference in New Issue
Block a user