优化: 变更区完善(文件 Diff 预览 + 分支只读列表)
- 变更文件选中后展开 Diff 红绿预览(原 selectFile 拉了 diffContent 但未渲染) - 分支切换按钮实现只读列表弹窗(拉取分支列表 + 当前分支标记) - 分支切换提示走 AI 助手(git_branch 工具)
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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)',
|
||||
},
|
||||
}
|
||||
|
||||
@@ -14,5 +14,8 @@ export default {
|
||||
detached: 'HEAD 分离',
|
||||
loadMore: '加载更多',
|
||||
switchBranch: '切换分支',
|
||||
branches: '分支列表',
|
||||
noBranches: '无分支',
|
||||
branchSwitchHint: '分支切换请通过 AI 助手执行(git_branch 工具)',
|
||||
},
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user