修复: 页面反馈真实性与竞态守卫八处
- 知识创建失败不再误关弹窗弹成功提示(补返回值检查) - AuditLog 翻页补请求序号守卫防慢响应覆盖新页 - TaskDrawer 切任务慢响应校验 taskId 才写入 - GitChanges 竞态序号+moduleId 双校验,三处静默失败补错误提示 - 回收站恢复/彻底删除操作中禁用防双击重复提交 - 灵感状态切换失败补错误反馈(原 unhandled rejection) - FileExplorer 三处失败仅 console 补 Message 提示 - Dashboard 消费 store.error 故障不再伪装空数据
This commit is contained in:
@@ -34,10 +34,16 @@
|
||||
<div class="gc-branch-picker-hint">{{ $t('gitChanges.branchSwitchHint') }}</div>
|
||||
</div>
|
||||
|
||||
<!-- 行内错误提示(loadMore/selectFile 失败,不挤掉列表) -->
|
||||
<div v-if="actionError" class="gc-error-strip">⚠ {{ actionError }}</div>
|
||||
|
||||
<div v-if="loading && !gitStatus" class="gc-skeleton">
|
||||
<div class="gc-skeleton-row" v-for="i in 6" :key="i"></div>
|
||||
</div>
|
||||
|
||||
<!-- 加载失败(gitStatus 为空,不再伪装空白) -->
|
||||
<div v-else-if="loadError" class="gc-empty gc-error">⚠ {{ loadError }}</div>
|
||||
|
||||
<!-- 非 Git 仓库 -->
|
||||
<div v-else-if="gitStatus && !gitStatus.is_git_repo" class="gc-empty">
|
||||
{{ $t('gitChanges.notGitRepo') }}
|
||||
@@ -180,6 +186,12 @@ const showHistory = ref(false)
|
||||
const selectedFile = ref('')
|
||||
const diffContent = ref('')
|
||||
const diffLoading = ref(false)
|
||||
// 行内错误提示:loadError 主区展示(loadStatus 失败),actionError 分支条下方展示(loadMore/selectFile 失败)
|
||||
const loadError = ref('')
|
||||
const actionError = ref('')
|
||||
// 请求序号守卫:moduleId 切换与 refreshKey 刷新双触发源并发时,旧响应晚到不覆盖新模块数据
|
||||
// (对齐 ProjectDetail taskReqSeq 模式)
|
||||
let statusReqSeq = 0
|
||||
|
||||
/** 单条提交记录形状(对齐 getModuleCommits / GitRecentCommit)。 */
|
||||
interface CommitItem {
|
||||
@@ -346,13 +358,20 @@ function pathEqualsAnySide(parsed: string, targetPath: string): boolean {
|
||||
}
|
||||
|
||||
async function loadStatus() {
|
||||
if (!props.moduleId) return
|
||||
const mid = props.moduleId
|
||||
if (!mid) return
|
||||
const seq = ++statusReqSeq
|
||||
loading.value = true
|
||||
loadError.value = ''
|
||||
actionError.value = ''
|
||||
try {
|
||||
gitStatus.value = await moduleApi.getModuleGitStatus(props.moduleId)
|
||||
const res = await moduleApi.getModuleGitStatus(mid)
|
||||
// 序号 + moduleId 双校验:响应回来时仍是最新请求且模块未切换才写入
|
||||
if (seq !== statusReqSeq || mid !== props.moduleId) return
|
||||
gitStatus.value = res
|
||||
// 复用 getModuleGitStatus 内部已返回的 recent_commits(避免再发一次 getModuleCommits
|
||||
// 造成首次 git log -50 重复请求 + recent_commits 被丢弃)。
|
||||
const recent = gitStatus.value?.recent_commits ?? []
|
||||
const recent = res?.recent_commits ?? []
|
||||
commits.value = recent.map((c) => ({
|
||||
hash: c.hash,
|
||||
subject: c.subject,
|
||||
@@ -360,7 +379,7 @@ async function loadStatus() {
|
||||
author: c.author,
|
||||
}))
|
||||
// 全量提交数读后端 total_commits(git rev-list --count HEAD),非 commits.length(后者受分页限制)。
|
||||
const backendTotal = gitStatus.value?.total_commits ?? 0
|
||||
const backendTotal = res?.total_commits ?? 0
|
||||
totalCommits.value = backendTotal
|
||||
commitSkip.value = commits.value.length
|
||||
// has_more:后端无 total 时(commits 仍可能未全载)用页大小推断;有 total 时按已载 < total 判定。
|
||||
@@ -368,11 +387,13 @@ async function loadStatus() {
|
||||
backendTotal > 0
|
||||
? commits.value.length < backendTotal
|
||||
: commits.value.length >= COMMIT_PAGE_SIZE
|
||||
} catch {
|
||||
} catch (e) {
|
||||
if (seq !== statusReqSeq || mid !== props.moduleId) return // 旧请求的失败不污染当前视图
|
||||
gitStatus.value = null
|
||||
commits.value = []
|
||||
loadError.value = String(e)
|
||||
} finally {
|
||||
loading.value = false
|
||||
if (seq === statusReqSeq) loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -407,6 +428,7 @@ function closeBranchPicker() {
|
||||
async function loadMoreCommits() {
|
||||
if (loadingMore.value || !hasMoreCommits.value) return
|
||||
loadingMore.value = true
|
||||
actionError.value = ''
|
||||
try {
|
||||
const res = await moduleApi.getModuleCommits(props.moduleId, commitSkip.value, COMMIT_PAGE_SIZE)
|
||||
// 按哈希去重(防止分页边界与 recent_commits 重叠)
|
||||
@@ -418,8 +440,8 @@ async function loadMoreCommits() {
|
||||
commitSkip.value += res.commits.length
|
||||
// 全量提交数读后端 total(git rev-list --count HEAD),非 commits.length(后者受分页限制)。
|
||||
totalCommits.value = res.total
|
||||
} catch {
|
||||
// 静默失败
|
||||
} catch (e) {
|
||||
actionError.value = String(e)
|
||||
} finally {
|
||||
loadingMore.value = false
|
||||
}
|
||||
@@ -435,12 +457,14 @@ async function selectFile(path: string) {
|
||||
// 先切预览到该文件(内容视图),diff 拉到后回填,点文件即展示变化
|
||||
emit('select-file', path)
|
||||
diffContent.value = ''
|
||||
actionError.value = ''
|
||||
diffLoading.value = true
|
||||
try {
|
||||
const res = await moduleApi.getModuleFileDiff(props.moduleId, path)
|
||||
diffContent.value = res.diff || ''
|
||||
} catch {
|
||||
} catch (e) {
|
||||
diffContent.value = ''
|
||||
actionError.value = String(e)
|
||||
} finally {
|
||||
diffLoading.value = false
|
||||
}
|
||||
@@ -668,7 +692,7 @@ watch(() => props.refreshKey, () => {
|
||||
.gc-st-modified { color: var(--df-git-modified); }
|
||||
.gc-st-added { color: var(--df-git-added); }
|
||||
.gc-st-deleted { color: var(--df-git-deleted); }
|
||||
.gc-st-untracked { color: #999; }
|
||||
.gc-st-untracked { color: var(--df-git-untracked); }
|
||||
.gc-file-path {
|
||||
flex: 1; min-width: 0;
|
||||
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
||||
@@ -768,6 +792,12 @@ watch(() => props.refreshKey, () => {
|
||||
padding: 40px 0; color: var(--df-text-dim); font-size: 13px;
|
||||
}
|
||||
.gc-empty-inline { padding: 24px 0; }
|
||||
.gc-error { color: var(--df-danger); }
|
||||
.gc-error-strip {
|
||||
padding: 6px 14px; font-size: 12px; color: var(--df-danger);
|
||||
background: var(--df-danger-bg); border-bottom: 0.5px solid var(--df-border);
|
||||
flex-shrink: 0; word-break: break-all;
|
||||
}
|
||||
.spinner {
|
||||
width: 16px; height: 16px;
|
||||
border: 2px solid var(--df-border); border-top-color: var(--df-accent);
|
||||
|
||||
Reference in New Issue
Block a user