优化: 变更区完善(文件 Diff 预览 + 分支只读列表)

- 变更文件选中后展开 Diff 红绿预览(原 selectFile 拉了 diffContent 但未渲染)
- 分支切换按钮实现只读列表弹窗(拉取分支列表 + 当前分支标记)
- 分支切换提示走 AI 助手(git_branch 工具)
This commit is contained in:
2026-07-01 00:01:25 +08:00
parent 976ce29ad9
commit 78ef5dcc44
3 changed files with 144 additions and 2 deletions

View File

@@ -10,6 +10,29 @@
</button>
</div>
<!-- 分支列表弹窗(只读查看,切换走 AI 工具) -->
<div v-if="showBranchPicker" class="gc-branch-picker">
<div class="gc-branch-picker-header">
<span>{{ $t('gitChanges.branches') }}</span>
<button class="gc-branch-picker-close" @click="closeBranchPicker"></button>
</div>
<div v-if="branchLoading" class="gc-loading"><span class="spinner"></span></div>
<div v-else class="gc-branch-list">
<div
v-for="b in branchList"
:key="b.name"
class="gc-branch-item"
:class="{ current: b.is_current }"
>
<span class="gc-branch-icon">{{ b.is_current ? '●' : '○' }}</span>
<span class="gc-branch-name">{{ b.name }}</span>
<span v-if="b.is_current" class="gc-branch-tag">HEAD</span>
</div>
<div v-if="branchList.length === 0" class="gc-empty">{{ $t('gitChanges.noBranches') }}</div>
</div>
<div class="gc-branch-picker-hint">{{ $t('gitChanges.branchSwitchHint') }}</div>
</div>
<div v-if="loading && !gitStatus" class="gc-skeleton">
<div class="gc-skeleton-row" v-for="i in 6" :key="i"></div>
</div>
@@ -49,6 +72,25 @@
<span class="gc-file-name">{{ filePathLeaf(f.path) }}</span>
</div>
</div>
<!-- 变更文件 diff 预览(选中文件时在列表下方展开) -->
<div v-if="selectedFile" class="gc-file-diff">
<div class="gc-file-diff-header">
<span class="gc-file-diff-path">{{ selectedFile }}</span>
<button class="gc-file-diff-close" @click="selectedFile = ''; diffContent = ''"></button>
</div>
<div v-if="diffLoading" class="gc-loading"><span class="spinner"></span></div>
<div v-else-if="!diffContent" class="gc-diff-empty">{{ $t('gitChanges.noDiff') }}</div>
<div v-else class="gc-diff-content">
<div v-for="(ln, idx) in fileDiffLines" :key="idx" class="diff-line" :class="'diff-' + ln.type">
<span class="diff-hdr-text" v-if="ln.type === 'hdr'">{{ ln.text }}</span>
<template v-else>
<span class="diff-line-prefix">{{ ln.prefix }}</span>
<span class="diff-line-text">{{ ln.text }}</span>
</template>
</div>
</div>
</div>
</div>
<!-- ====== 提交历史 ====== -->
@@ -200,6 +242,7 @@ interface DiffLine {
}
const commitDiffLines = computed<DiffLine[]>(() => parseDiff(commitDiff.value))
const fileDiffLines = computed<DiffLine[]>(() => parseDiff(diffContent.value))
function parseDiff(text: string): DiffLine[] {
if (!text) return []
@@ -277,8 +320,31 @@ async function loadStatus() {
}
/** 占位:分支切换后续接入(需拉取分支列表 + checkout IPC)。 */
function onSwitchBranch() {
// TODO: 弹出分支列表选择 + 调用 git checkout
const showBranchPicker = ref(false)
const branchList = ref<{ name: string; is_current: boolean }[]>([])
const branchLoading = ref(false)
const currentBranch = ref('')
async function onSwitchBranch() {
if (showBranchPicker.value) {
showBranchPicker.value = false
return
}
showBranchPicker.value = true
branchLoading.value = true
try {
const res = await moduleApi.listBranches(props.moduleId)
branchList.value = res.branches
currentBranch.value = res.current
} catch {
branchList.value = []
} finally {
branchLoading.value = false
}
}
function closeBranchPicker() {
showBranchPicker.value = false
}
async function loadMoreCommits() {
@@ -417,6 +483,27 @@ watch(() => props.moduleId, loadStatus, { immediate: true })
position: sticky; top: 0; z-index: 1;
}
/* 变更文件 diff 预览(列表下方展开) */
.gc-file-diff {
margin-top: 8px;
border-top: 0.5px solid var(--df-border);
}
.gc-file-diff-header {
display: flex;
align-items: center;
padding: 6px 14px;
background: rgba(255,255,255,0.03);
font-family: var(--df-font-mono, Consolas, monospace);
font-size: 11px;
border-bottom: 0.5px solid var(--df-border);
}
.gc-file-diff-path { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--df-text); }
.gc-file-diff-close {
background: none; border: none; color: var(--df-text-dim);
cursor: pointer; font-size: 14px; padding: 0 4px;
}
.gc-file-diff-close:hover { color: var(--df-text); }
/* Diff 预览 */
.gc-diff-content {
font-family: var(--df-font-mono, Consolas, monospace);
@@ -511,6 +598,55 @@ watch(() => props.moduleId, loadStatus, { immediate: true })
}
.gc-load-more:hover { background: rgba(255,255,255,0.04); }
/* 分支列表弹窗 */
.gc-branch-picker {
border-bottom: 0.5px solid var(--df-border);
background: var(--df-bg);
}
.gc-branch-picker-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 6px 14px;
font-size: 11px;
font-weight: 600;
color: var(--df-text-dim);
text-transform: uppercase;
}
.gc-branch-picker-close {
background: none; border: none; color: var(--df-text-dim);
cursor: pointer; font-size: 14px; padding: 0 4px;
}
.gc-branch-list {
max-height: 200px;
overflow-y: auto;
scrollbar-width: thin;
}
.gc-branch-item {
display: flex;
align-items: center;
gap: 8px;
padding: 5px 14px;
font-size: 12px;
cursor: default;
}
.gc-branch-item.current { color: var(--df-accent); }
.gc-branch-icon { font-size: 10px; flex-shrink: 0; }
.gc-branch-name { font-family: var(--df-font-mono, Consolas, monospace); }
.gc-branch-tag {
font-size: 9px;
padding: 1px 5px;
border-radius: 3px;
background: rgba(108,99,255,0.15);
color: var(--df-accent);
}
.gc-branch-picker-hint {
padding: 6px 14px;
font-size: 11px;
color: var(--df-text-dim);
border-top: 0.5px solid var(--df-border);
}
/* 骨架屏 */
.gc-skeleton {
padding: 14px;

View File

@@ -14,5 +14,8 @@ export default {
detached: 'HEAD detached',
loadMore: 'Load more',
switchBranch: 'Switch branch',
branches: 'Branches',
noBranches: 'No branches',
branchSwitchHint: 'Use AI assistant to switch branches (git_branch tool)',
},
}

View File

@@ -14,5 +14,8 @@ export default {
detached: 'HEAD 分离',
loadMore: '加载更多',
switchBranch: '切换分支',
branches: '分支列表',
noBranches: '无分支',
branchSwitchHint: '分支切换请通过 AI 助手执行(git_branch 工具)',
},
}