feat:增加主要刷新
This commit is contained in:
parent
d9473fda7a
commit
a2d36cb7a0
@ -1,5 +1,9 @@
|
||||
<script>
|
||||
import { getGlobalSocket } from '@/utils/socket'
|
||||
import { emitAppReturnFromBackground } from '@/utils/backgroundRefreshBus.js'
|
||||
|
||||
// 记录上次隐藏时间的 storage key
|
||||
const HIDE_TIME_KEY = 'app_last_hide_time'
|
||||
|
||||
export default {
|
||||
onLaunch: function() {
|
||||
@ -8,11 +12,14 @@ export default {
|
||||
},
|
||||
onShow: function() {
|
||||
console.log('App Show')
|
||||
this.handleBackgroundReturn()
|
||||
},
|
||||
onHide: function() {
|
||||
console.log('App Hide')
|
||||
// 关闭所有 WebSocket 连接
|
||||
this.closeWebSocket()
|
||||
// 记录切到后台的时间,用于 onShow 时判断是否"从后台返回"
|
||||
uni.setStorageSync(HIDE_TIME_KEY, Date.now())
|
||||
},
|
||||
methods: {
|
||||
initWebSocket() {
|
||||
@ -27,6 +34,20 @@ export default {
|
||||
console.log('关闭全局 WebSocket 连接')
|
||||
const globalSocket = getGlobalSocket()
|
||||
globalSocket.closeAll()
|
||||
},
|
||||
/**
|
||||
* 处理"从后台切回前台"事件
|
||||
* 通过 storage 中的上次隐藏时间判断本次 onShow 是否由后台返回触发
|
||||
* 若是,则通知所有订阅了 useBackgroundRefresh 的页面执行刷新
|
||||
*/
|
||||
handleBackgroundReturn() {
|
||||
const lastHide = uni.getStorageSync(HIDE_TIME_KEY) || 0
|
||||
if (!lastHide) return
|
||||
// 清理标记,避免下次普通 onShow(如首次启动)误触发
|
||||
uni.removeStorageSync(HIDE_TIME_KEY)
|
||||
// 时间异常保护
|
||||
if (Date.now() - lastHide < 0) return
|
||||
emitAppReturnFromBackground()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
32
frontend/composables/useBackgroundRefresh.js
Normal file
32
frontend/composables/useBackgroundRefresh.js
Normal file
@ -0,0 +1,32 @@
|
||||
import { onMounted, onBeforeUnmount } from 'vue'
|
||||
import { onAppReturnFromBackground } from '@/utils/backgroundRefreshBus.js'
|
||||
|
||||
/**
|
||||
* 页面启用「从后台切回时自动刷新」能力
|
||||
*
|
||||
* 仅当 App 从后台切回前台时触发,不会因页面间普通跳转而触发。
|
||||
* 由 App.vue 统一调度,页面只需关心自己的刷新逻辑。
|
||||
*
|
||||
* @param {Function} refreshFn 刷新逻辑(通常就是重新拉数据的函数)
|
||||
*
|
||||
* @example
|
||||
* // 在 <script setup> 中:
|
||||
* useBackgroundRefresh(() => {
|
||||
* fetchList()
|
||||
* })
|
||||
*/
|
||||
export function useBackgroundRefresh(refreshFn) {
|
||||
if (typeof refreshFn !== 'function') {
|
||||
throw new Error('useBackgroundRefresh: refreshFn must be a function')
|
||||
}
|
||||
|
||||
let unregister = null
|
||||
|
||||
onMounted(() => {
|
||||
unregister = onAppReturnFromBackground(refreshFn)
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
if (typeof unregister === 'function') unregister()
|
||||
})
|
||||
}
|
||||
@ -143,6 +143,7 @@ import Avatar from "./Avatar.vue";
|
||||
import DailyTasks from "@/pages/tasks/daily-tasks.vue";
|
||||
import GuideModal from "@/pages/tasks/GuideModal.vue";
|
||||
import { useBanner } from "@/pages/square/composables/useBanner.js";
|
||||
import { useBackgroundRefresh } from "@/composables/useBackgroundRefresh.js";
|
||||
import { reportEvent } from "@/utils/task-api.js";
|
||||
import { getEarningsSummaryApi } from "@/utils/api.js";
|
||||
|
||||
@ -333,6 +334,11 @@ onShow(() => {
|
||||
loadEarningsSummary();
|
||||
});
|
||||
|
||||
// App 从后台切回前台时,刷新 banner 活动(onShow 已经在做收益刷新,这里不重复)
|
||||
useBackgroundRefresh(() => {
|
||||
loadBannerActivities();
|
||||
});
|
||||
|
||||
// 组件卸载时移除事件监听
|
||||
onUnmounted(() => {
|
||||
uni.$off("avatarUpdated", handleAvatarUpdate);
|
||||
|
||||
@ -59,6 +59,7 @@ import ScatteredRanks from "./ScatteredRanks.vue";
|
||||
import { generateRingPositions } from "./config.js";
|
||||
import { getHotRankingApi } from "@/utils/api.js";
|
||||
import { getAssetCoverRealUrl } from "@/utils/assetImageHelper.js";
|
||||
import { useBackgroundRefresh } from "@/composables/useBackgroundRefresh.js";
|
||||
|
||||
const emit = defineEmits(["cardClick"]);
|
||||
|
||||
@ -108,6 +109,11 @@ onMounted(() => {
|
||||
loadData();
|
||||
});
|
||||
|
||||
// App 从后台切回前台时刷新星河榜单
|
||||
useBackgroundRefresh(() => {
|
||||
loadData();
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
// 清理(如有 timer)
|
||||
});
|
||||
|
||||
@ -11,11 +11,19 @@ const FALLBACK_BANNER = [
|
||||
}
|
||||
]
|
||||
|
||||
export function useBanner() {
|
||||
// 旧的活动 banner(square 顶部)
|
||||
/**
|
||||
* 模块级共享状态(单例)
|
||||
*
|
||||
* 背景:square.vue 和 pages/components/Header.vue 都会调 useBanner(),
|
||||
* 旧实现每次调用都 new ref,导致两个组件各持一份独立的 bannerActivities,
|
||||
* 状态可能不一致。
|
||||
*
|
||||
* 改单例后:所有调用 useBanner() 的地方拿到的是同一对 ref,
|
||||
* 任一处调 loadBannerActivities() / loadBanners() 都会同步刷新所有订阅者。
|
||||
*
|
||||
* 不会把数据搬到 Vuex,原因是 banner 数据目前只有这两个使用方,没必要全局 store 化。
|
||||
*/
|
||||
const bannerActivities = ref([])
|
||||
|
||||
// 新的运营 banner(铸造活动)
|
||||
const banners = ref([])
|
||||
|
||||
const loadBannerActivities = async () => {
|
||||
@ -63,6 +71,8 @@ export function useBanner() {
|
||||
}
|
||||
}
|
||||
|
||||
export function useBanner() {
|
||||
// 返回模块级单例的引用(API 不变,调用方零改动)
|
||||
return {
|
||||
bannerActivities,
|
||||
banners,
|
||||
|
||||
@ -107,6 +107,7 @@ import CreationGrid from "./components/CreationGrid.vue";
|
||||
import StarGalaxy from "./components/StarGalaxy/index.vue";
|
||||
// import { clearSubStepProgress, shouldShowGuideStartModal } from '@/utils/guideConfig.js'
|
||||
import { useBanner } from "./composables/useBanner.js";
|
||||
import { useBackgroundRefresh } from "@/composables/useBackgroundRefresh.js";
|
||||
import { doubleTapLike } from "@/utils/likeHelper.js";
|
||||
|
||||
// ========== Store & User Info ==========
|
||||
@ -268,6 +269,16 @@ onShow(() => {
|
||||
activeContentTab.value = "xinghe";
|
||||
});
|
||||
|
||||
// ========== 后台切回自动刷新 ==========
|
||||
// App 从后台切回前台时,刷新 banner 数据。
|
||||
// 三个 Tab 内容区中:
|
||||
// - StarGalaxy:默认 Tab 且 v-if 命中后会保持挂载,需要在 StarGalaxy 内部独立接入 useBackgroundRefresh
|
||||
// - HotCategoryBlock / CreationGrid:onShow 会切回 xinghe,自身被 v-if 卸载后再切回时重新挂载即重新拉数据
|
||||
useBackgroundRefresh(() => {
|
||||
loadBannerActivities();
|
||||
loadBanners();
|
||||
});
|
||||
|
||||
// onLoad((options) => {
|
||||
// if (options && 'guide_debug' in options) {
|
||||
// const debugValue = options.guide_debug
|
||||
|
||||
BIN
frontend/static/rank/jindut-2.png
Normal file
BIN
frontend/static/rank/jindut-2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 20 KiB |
BIN
frontend/static/rank/jindut.png
Normal file
BIN
frontend/static/rank/jindut.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 38 KiB |
45
frontend/utils/backgroundRefreshBus.js
Normal file
45
frontend/utils/backgroundRefreshBus.js
Normal file
@ -0,0 +1,45 @@
|
||||
/**
|
||||
* 后台返回事件总线
|
||||
*
|
||||
* 触发方:App.vue 在 onShow 中检测到"从后台切回前台"时调用
|
||||
* emitAppReturnFromBackground()
|
||||
* 订阅方:页面通过 composables/useBackgroundRefresh.js 注册回调
|
||||
*
|
||||
* 为什么不用 uni.$emit / uni.$on:
|
||||
* 1. 事件名是字符串,缺乏约束,容易和其他业务事件撞名
|
||||
* 2. 集中在一处便于后续加日志、节流、灰度等横切逻辑
|
||||
*/
|
||||
|
||||
const listeners = new Set()
|
||||
|
||||
/**
|
||||
* 注册后台返回监听
|
||||
* @param {Function} fn 回调函数
|
||||
* @returns {Function} 取消监听的函数
|
||||
*/
|
||||
export function onAppReturnFromBackground(fn) {
|
||||
if (typeof fn !== 'function') return () => {}
|
||||
listeners.add(fn)
|
||||
return () => listeners.delete(fn)
|
||||
}
|
||||
|
||||
/**
|
||||
* 触发后台返回事件,通知所有订阅者
|
||||
*/
|
||||
export function emitAppReturnFromBackground() {
|
||||
listeners.forEach(fn => {
|
||||
try {
|
||||
fn()
|
||||
} catch (e) {
|
||||
// 单个监听者异常不影响其他监听者
|
||||
console.error('[backgroundRefreshBus] listener error:', e)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空所有监听(仅用于测试或强制重置)
|
||||
*/
|
||||
export function clearAppReturnFromBackgroundListeners() {
|
||||
listeners.clear()
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user