Guard the news detail panel against stale responses and load errors
The detail is now a persistent side panel, so quick clicks can interleave requests: only the latest request's result is applied, and a failed load shows an error state with a retry button instead of spinning forever. Also correct the position history comments: only plans from before plan 3.1 could hand their position over to a replacing plan.
This commit is contained in:
1 parent
fb63ebb697
commit
a2e51a3011
3 files changed
+32
-7
No files matched your search
@@ -149,7 +149,7 @@ class PositionHistoryOut(BaseModel):
|
|||||||
opened_at: datetime
|
opened_at: datetime
|
||||||
closed_at: datetime
|
closed_at: datetime
|
||||||
close_reason: str
|
close_reason: str
|
||||||
# 本计划的入场成交;计划被替换时持仓由新计划接管,新计划没有自己的入场成交,这两项为空
|
# 本计划的入场成交。计划 3.1 之前,替换计划时持仓会交给新计划接管(如 #8),这类旧记录没有自己的入场成交,这两项为空
|
||||||
quantity: Decimal | None
|
quantity: Decimal | None
|
||||||
entry_price: Decimal | None
|
entry_price: Decimal | None
|
||||||
# 止盈、止损、市价平仓的成交均价;外部平仓(如网页手动平仓)没有成交记录,为空
|
# 止盈、止损、市价平仓的成交均价;外部平仓(如网页手动平仓)没有成交记录,为空
|
||||||
|
|||||||
@@ -275,7 +275,7 @@ export interface PositionHistory {
|
|||||||
opened_at: string
|
opened_at: string
|
||||||
closed_at: string
|
closed_at: string
|
||||||
close_reason: string
|
close_reason: string
|
||||||
/** 计划被替换后接管持仓的新计划没有自己的入场成交,为空 */
|
/** 计划 3.1 之前替换计划时会接管旧计划的持仓,这类旧记录没有自己的入场成交,为空 */
|
||||||
quantity: string | null
|
quantity: string | null
|
||||||
entry_price: string | null
|
entry_price: string | null
|
||||||
/** 外部平仓没有成交记录,为空 */
|
/** 外部平仓没有成交记录,为空 */
|
||||||
|
|||||||
@@ -13,16 +13,36 @@ const revisions = ref<Revision[]>([])
|
|||||||
const activeTab = ref("content")
|
const activeTab = ref("content")
|
||||||
// 当前查看的修订序号,与上一版对比
|
// 当前查看的修订序号,与上一版对比
|
||||||
const selectedRevision = ref<number>()
|
const selectedRevision = ref<number>()
|
||||||
|
// 加载失败(错误提示已由 http 拦截器弹出):停止转圈,显示重试
|
||||||
|
const failed = ref(false)
|
||||||
|
// 连续点击不同新闻时,只采用最后一次请求的结果
|
||||||
|
let requestSeq = 0
|
||||||
|
|
||||||
watch(() => props.newsId, async (id) => {
|
async function load(id: number) {
|
||||||
if (id == null)
|
const seq = ++requestSeq
|
||||||
return
|
failed.value = false
|
||||||
activeTab.value = "content"
|
try {
|
||||||
detail.value = undefined
|
|
||||||
const [news, revs] = await Promise.all([getNews(id), listRevisions(id)])
|
const [news, revs] = await Promise.all([getNews(id), listRevisions(id)])
|
||||||
|
if (seq !== requestSeq)
|
||||||
|
return
|
||||||
detail.value = news
|
detail.value = news
|
||||||
revisions.value = revs
|
revisions.value = revs
|
||||||
selectedRevision.value = revs[0]?.revision_no
|
selectedRevision.value = revs[0]?.revision_no
|
||||||
|
} catch (error) {
|
||||||
|
if (seq === requestSeq)
|
||||||
|
failed.value = true
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(() => props.newsId, (id) => {
|
||||||
|
activeTab.value = "content"
|
||||||
|
detail.value = undefined
|
||||||
|
revisions.value = []
|
||||||
|
if (id == null)
|
||||||
|
requestSeq++
|
||||||
|
else
|
||||||
|
load(id)
|
||||||
})
|
})
|
||||||
|
|
||||||
// 第三方正文放进无脚本权限的 sandbox iframe,避免其中的脚本访问本站
|
// 第三方正文放进无脚本权限的 sandbox iframe,避免其中的脚本访问本站
|
||||||
@@ -110,6 +130,11 @@ const diffStats = computed(() => ({
|
|||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
</el-tabs>
|
</el-tabs>
|
||||||
</template>
|
</template>
|
||||||
|
<el-empty v-else-if="failed" description="加载失败" class="m-auto">
|
||||||
|
<el-button type="primary" @click="load(newsId)">
|
||||||
|
重试
|
||||||
|
</el-button>
|
||||||
|
</el-empty>
|
||||||
<div v-else v-loading="true" class="h-full" />
|
<div v-else v-loading="true" class="h-full" />
|
||||||
</el-card>
|
</el-card>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
Reference in new issue
Block a user