fix: 修复帮助内容加载、搜索滚动和账户同步的问题
- 完善帮助内容接口的响应校验,增加数据格式和范围验证 - 优化搜索页面的滚动位置管理,避免H5页面激活时的组件重建问题 - 增加账户切换时的定时器清理,防止内存泄漏和重复请求
This commit is contained in:
@@ -82,7 +82,8 @@
|
||||
const targetId = ref(''); const targetItem = ref<FeedbackItem | null>(null); const targetLoading = ref(false); const targetError = ref('')
|
||||
let detailRequest = 0
|
||||
let visible = false; let generation = 0; let faqRequest = 0; let listRequest = 0; let page = 0
|
||||
let owner = ''
|
||||
let owner = ''; let accountTimer = 0
|
||||
function stopAccountTimer() { if (accountTimer != 0) { clearInterval(accountTimer); accountTimer = 0 } }
|
||||
const groups = computed((): HelpGroup[] => {
|
||||
const results: HelpGroup[] = []
|
||||
helpCategories.forEach((category: string) => {
|
||||
@@ -122,8 +123,10 @@
|
||||
if (!visible || ticket != generation) return false
|
||||
const sessionToken = getSession()?.getString('token') ?? ''
|
||||
if (sessionToken == token) return true
|
||||
generation++; clearFeedback(); targetId.value = ''; owner = sessionToken; loggedIn.value = owner.length > 0
|
||||
if (loggedIn.value) { restorePending(); reloadAfterAccountChange() }
|
||||
generation++; faqRequest++; loading.value = false; errorMessage.value = ''; expanded.value = ''
|
||||
clearFeedback(); targetId.value = ''; owner = sessionToken; loggedIn.value = owner.length > 0
|
||||
if (loggedIn.value) restorePending()
|
||||
reloadAfterAccountChange()
|
||||
return false
|
||||
}
|
||||
reconcileAccount = () => { current(generation, owner) }
|
||||
@@ -143,10 +146,12 @@
|
||||
}
|
||||
const load = async () => {
|
||||
if (!visible || loading.value) return
|
||||
const account = generation; const token = owner
|
||||
if (!current(account, token)) return
|
||||
const ticket = ++faqRequest
|
||||
loading.value = true; errorMessage.value = ''
|
||||
try { const items = await fetchHelpQuestions(); if (visible && ticket == faqRequest) questions.value = items }
|
||||
catch (_error) { if (visible && ticket == faqRequest) errorMessage.value = '暂时无法加载帮助内容,请稍后重试' }
|
||||
loading.value = true; errorMessage.value = ''; questions.value = []; expanded.value = ''
|
||||
try { const items = await fetchHelpQuestions(); if (current(account, token) && ticket == faqRequest) questions.value = items }
|
||||
catch (_error) { if (current(account, token) && ticket == faqRequest) errorMessage.value = '暂时无法加载帮助内容,请稍后重试' }
|
||||
finally { if (ticket == faqRequest) loading.value = false }
|
||||
}
|
||||
async function loadFeedback(append: boolean) {
|
||||
@@ -170,9 +175,11 @@
|
||||
} finally { if (ticket == generation && request == listRequest) { feedbackLoading.value = false; loadingMore.value = false } }
|
||||
}
|
||||
function reloadFeedback() { loadFeedback(false) }
|
||||
reloadAfterAccountChange = () => { reloadTarget(); reloadFeedback() }
|
||||
reloadAfterAccountChange = () => { load(); if (tab.value == 'feedback') { reloadTarget(); reloadFeedback() } }
|
||||
const loadMore = () => { loadFeedback(true) }
|
||||
const changeTab = (value: string) => {
|
||||
if (!visible) return
|
||||
reconcileAccount()
|
||||
tab.value = value
|
||||
if (value == 'feedback') { reloadTarget(); reloadFeedback() } else if (questions.value.length == 0) load()
|
||||
}
|
||||
@@ -226,9 +233,10 @@
|
||||
visible = true; generation++; const token = getSession()?.getString('token') ?? ''
|
||||
if (token != owner) { if (owner.length > 0) targetId.value = ''; clearFeedback() }
|
||||
owner = token; loggedIn.value = token.length > 0; restorePending(); load(); if (tab.value == 'feedback') { reloadTarget(); reloadFeedback() }
|
||||
stopAccountTimer(); accountTimer = setInterval(() => { reconcileAccount() }, 1000)
|
||||
})
|
||||
onHide(() => { visible = false; generation++; faqRequest++; listRequest++; detailRequest++; targetItem.value = null; targetLoading.value = false; targetError.value = ''; loading.value = false; feedbackLoading.value = false; loadingMore.value = false; sending.value = false; records.value = [] })
|
||||
onUnload(() => { visible = false; generation++; faqRequest++; clearFeedback() })
|
||||
onHide(() => { visible = false; stopAccountTimer(); generation++; faqRequest++; listRequest++; detailRequest++; targetItem.value = null; targetLoading.value = false; targetError.value = ''; loading.value = false; feedbackLoading.value = false; loadingMore.value = false; sending.value = false; records.value = [] })
|
||||
onUnload(() => { visible = false; stopAccountTimer(); generation++; faqRequest++; clearFeedback() })
|
||||
</script>
|
||||
<style>
|
||||
.help-body { overflow: hidden; }
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<view class="search-tabs">
|
||||
<view v-for="item in tabs" :key="item.id" :class="activeType == item.id ? 'search-tab selected-tab' : 'search-tab'" @click="selectType(item.id)"><text :class="activeType == item.id ? 'tab-label selected-label' : 'tab-label'">{{ item.label }}</text></view>
|
||||
</view>
|
||||
<scroll-view scroll-y :key="listVersion" :style="contentStyle(296, false)">
|
||||
<scroll-view scroll-y :scroll-top="scrollTop" :style="contentStyle(296, false)">
|
||||
<view class="search-content" :style="bottomStyle(40)">
|
||||
<view v-if="!searched" class="search-guide">
|
||||
<text class="guide-title">想了解什么?</text><text class="guide-copy">查找融资资讯,学习实用知识</text>
|
||||
@@ -50,12 +50,17 @@
|
||||
const loading = ref(false)
|
||||
const errorMessage = ref('')
|
||||
const results = ref<ContentSearchResult[]>([])
|
||||
const listVersion = ref(0)
|
||||
const scrollTop = ref(0)
|
||||
const total = ref(0); const hasMore = ref(false); const loadingMore = ref(false); const moreError = ref('')
|
||||
let page = 1
|
||||
let requestVersion = 0
|
||||
let visible = false
|
||||
const resetResults = () => { requestVersion++; results.value = []; total.value = 0; hasMore.value = false; loadingMore.value = false; moreError.value = ''; searched.value = false; loading.value = false; errorMessage.value = ''; listVersion.value++ }
|
||||
const resetScroll = () => {
|
||||
// 保留滚动组件,避免H5返回页激活时重建导致框架读取空引用。
|
||||
scrollTop.value = 1
|
||||
nextTick(() => { scrollTop.value = 0 })
|
||||
}
|
||||
const resetResults = () => { requestVersion++; results.value = []; total.value = 0; hasMore.value = false; loadingMore.value = false; moreError.value = ''; searched.value = false; loading.value = false; errorMessage.value = ''; resetScroll() }
|
||||
const clearSearch = () => { keyword.value = ''; resetResults() }
|
||||
async function load(append: boolean) {
|
||||
if (!visible) return
|
||||
@@ -65,7 +70,7 @@
|
||||
uni.hideKeyboard()
|
||||
const currentRequest = append ? requestVersion : ++requestVersion
|
||||
if (append) { loadingMore.value = true; moreError.value = '' }
|
||||
else { searched.value = true; loading.value = true; loadingMore.value = false; errorMessage.value = ''; moreError.value = ''; results.value = []; total.value = 0; hasMore.value = false; page = 1; listVersion.value++ }
|
||||
else { searched.value = true; loading.value = true; loadingMore.value = false; errorMessage.value = ''; moreError.value = ''; results.value = []; total.value = 0; hasMore.value = false; page = 1; resetScroll() }
|
||||
try {
|
||||
const result = await searchContent(keyword.value, activeType.value, append ? page + 1 : 1)
|
||||
// 输入、切换分类或退出页面后,忽略旧请求,避免结果被覆盖。
|
||||
|
||||
@@ -4,15 +4,19 @@ export type HelpQuestion = { id: string, category: string, question: string, ans
|
||||
export const helpCategories: string[] = ['账户使用', '融资评估', '资料与隐私']
|
||||
export async function fetchHelpQuestions(): Promise<HelpQuestion[]> {
|
||||
const response = await requestJson('/help/faqs', 'GET', {})
|
||||
const items = response['items'] as UTSJSONObject[] | null
|
||||
if (items == null) throw new Error('帮助内容加载失败')
|
||||
const items = response['items']
|
||||
if (items == null || !Array.isArray(items)) throw new Error('帮助内容响应不完整,请重试')
|
||||
const results: HelpQuestion[] = []
|
||||
items.forEach((item) => {
|
||||
const id = item['id'] as string ?? ''; const category = item['category'] as string ?? ''
|
||||
const question = item['question'] as string ?? ''; const answer = item['answer'] as string ?? ''
|
||||
if (id.length > 0 && helpCategories.includes(category) && question.length > 0 && answer.length > 0 && !results.some((entry: HelpQuestion): boolean => entry.id == id)) {
|
||||
const result: HelpQuestion = { id, category, question, answer }; results.push(result)
|
||||
}
|
||||
;(items as any[]).forEach((value: any) => {
|
||||
if (value == null || typeof value != 'object' || Array.isArray(value)) throw new Error('帮助内容响应不完整,请重试')
|
||||
const item = value as UTSJSONObject
|
||||
const id = item.getString('id'); const category = item.getString('category')
|
||||
const question = item.getString('question'); const answer = item.getString('answer')
|
||||
if (id == null || !/^[1-9][0-9]{0,9}$/.test(id) || parseFloat(id) > 2147483647
|
||||
|| category == null || !helpCategories.includes(category) || question == null || question.trim().length == 0
|
||||
|| answer == null || answer.trim().length == 0 || results.some((entry: HelpQuestion): boolean => entry.id == id))
|
||||
throw new Error('帮助内容响应不完整,请重试')
|
||||
const result: HelpQuestion = { id, category, question, answer }; results.push(result)
|
||||
})
|
||||
return results
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user