新增: Git 提交历史显示作者 + 提交详情含作者/时间
- 后端 git log format 加 %an 作者名(状态查询+分页查询) - 提交列表每行显示 hash/作者/主题/时间 - 提交详情头部补充作者显示 - 分支切换按钮占位(后续接入分支列表 IPC)
This commit is contained in:
@@ -266,6 +266,8 @@ struct GitRecentCommit {
|
||||
subject: String,
|
||||
/// Unix 时间戳(秒),供前端格式化显示。
|
||||
timestamp: i64,
|
||||
/// 作者名(git an)
|
||||
author: String,
|
||||
}
|
||||
|
||||
/// Git 状态返回结构(无 .git 时各字段空,前端据此显示"非 Git 仓库")。
|
||||
@@ -367,23 +369,25 @@ fn run_git_status(dir: &str) -> GitStatus {
|
||||
}
|
||||
}
|
||||
|
||||
// 3) 最近提交:`git log -50 --format="%h %ct %s"`(hash + unix 时间戳 + subject)
|
||||
// 3) 最近提交:`git log -50 --format="%h %ct %an %s"`(hash + 时间戳 + 作者 + subject)
|
||||
let mut recent_commits = Vec::new();
|
||||
if let Some(out) = run_git_cmd(path, &["log", "-50", "--format=%h %ct %s"], timeout) {
|
||||
if let Some(out) = run_git_cmd(path, &["log", "-50", "--format=%h %ct %an %s"], timeout) {
|
||||
for line in out.lines() {
|
||||
// format:"<hash> <unix_ts> <subject>"
|
||||
// format:"<hash> <unix_ts> <author> <subject>"(作者不含空格)
|
||||
let line = line.trim();
|
||||
if line.is_empty() { continue; }
|
||||
let mut parts = line.splitn(3, ' ');
|
||||
let mut parts = line.splitn(4, ' ');
|
||||
let hash = parts.next().unwrap_or("").to_string();
|
||||
let ts_str = parts.next().unwrap_or("0");
|
||||
let timestamp: i64 = ts_str.parse().unwrap_or(0);
|
||||
let author = parts.next().unwrap_or("").to_string();
|
||||
let subject = parts.next().unwrap_or("").to_string();
|
||||
if !hash.is_empty() {
|
||||
recent_commits.push(GitRecentCommit {
|
||||
hash,
|
||||
subject,
|
||||
timestamp,
|
||||
author,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -922,7 +926,7 @@ pub async fn get_module_commits(
|
||||
"log",
|
||||
&format!("--skip={}", skip),
|
||||
&format!("-{}", fetch_plus),
|
||||
"--format=%h %ct %s",
|
||||
"--format=%h %ct %an %s",
|
||||
])
|
||||
.current_dir(&dir_for_git)
|
||||
.env("LANG", "en_US.UTF-8")
|
||||
@@ -937,16 +941,18 @@ pub async fn get_module_commits(
|
||||
let line = line.trim();
|
||||
if line.is_empty() { continue; }
|
||||
if commits.len() >= fetch as usize { break; }
|
||||
let mut parts = line.splitn(3, ' ');
|
||||
let mut parts = line.splitn(4, ' ');
|
||||
let hash = parts.next().unwrap_or("").to_string();
|
||||
let ts_str = parts.next().unwrap_or("0");
|
||||
let timestamp: i64 = ts_str.parse().unwrap_or(0);
|
||||
let author = parts.next().unwrap_or("").to_string();
|
||||
let subject = parts.next().unwrap_or("").to_string();
|
||||
if !hash.is_empty() {
|
||||
commits.push(serde_json::json!({
|
||||
"hash": hash,
|
||||
"subject": subject,
|
||||
"timestamp": timestamp,
|
||||
"author": author,
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,6 +65,8 @@ export interface GitRecentCommit {
|
||||
subject: string
|
||||
/** Unix 时间戳(秒) */
|
||||
timestamp: number
|
||||
/** 作者名 */
|
||||
author: string
|
||||
}
|
||||
|
||||
/** getModuleGitStatus 返回结构。 */
|
||||
@@ -151,7 +153,7 @@ export const moduleApi = {
|
||||
|
||||
/** 分页查询工程 Git 提交历史。返回 { commits, has_more }。 */
|
||||
getModuleCommits(moduleId: string, skip?: number, limit?: number): Promise<{
|
||||
commits: { hash: string; subject: string; timestamp: number }[]
|
||||
commits: { hash: string; subject: string; timestamp: number; author: string }[]
|
||||
has_more: boolean
|
||||
}> {
|
||||
return invoke('get_module_commits', { moduleId, skip: skip ?? 0, limit: limit ?? 50 })
|
||||
|
||||
@@ -5,6 +5,9 @@
|
||||
<span class="branch-icon">⎇</span>
|
||||
<span class="branch-name">{{ branchName }}</span>
|
||||
<span v-if="!showHistory" class="branch-count">{{ gitStatus?.changed_files.length ?? 0 }} {{ $t('gitChanges.changedFiles') }}</span>
|
||||
<button v-if="!showHistory" class="branch-switch-btn" @click="onSwitchBranch" :title="$t('gitChanges.switchBranch')">
|
||||
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="6" y1="3" x2="6" y2="15"/><circle cx="18" cy="6" r="3"/><circle cx="6" cy="18" r="3"/><path d="M18 9a9 9 0 0 1-9 9"/></svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div v-if="loading && !gitStatus" class="gc-skeleton">
|
||||
@@ -54,6 +57,7 @@
|
||||
<div v-if="selectedCommit" class="gc-commit-detail">
|
||||
<div class="gc-commit-detail-header">
|
||||
<span class="gc-commit-detail-hash">{{ selectedCommit.hash }}</span>
|
||||
<span class="gc-commit-detail-author">{{ selectedCommit.author }}</span>
|
||||
<span class="gc-commit-detail-msg">{{ selectedCommit.subject }}</span>
|
||||
<span class="gc-commit-detail-time">{{ formatTime(selectedCommit.timestamp) }}</span>
|
||||
<button class="gc-detail-close" @click="selectedCommit = null; commitDetail = null">✕</button>
|
||||
@@ -96,6 +100,7 @@
|
||||
@click="selectCommit(c)"
|
||||
>
|
||||
<span class="gc-commit-hash">{{ c.hash.slice(0, 8) }}</span>
|
||||
<span class="gc-commit-author">{{ c.author }}</span>
|
||||
<span class="gc-commit-msg">{{ c.subject }}</span>
|
||||
<span class="gc-commit-time">{{ formatTime(c.timestamp) }}</span>
|
||||
</div>
|
||||
@@ -137,7 +142,7 @@ const diffContent = ref('')
|
||||
const diffLoading = ref(false)
|
||||
|
||||
// 提交历史(分页)
|
||||
const commits = ref<{ hash: string; subject: string; timestamp: number }[]>([])
|
||||
const commits = ref<{ hash: string; subject: string; timestamp: number; author: string }[]>([])
|
||||
const totalCommits = ref(0)
|
||||
const hasMoreCommits = ref(false)
|
||||
const loadingMore = ref(false)
|
||||
@@ -145,7 +150,7 @@ const commitSkip = ref(0)
|
||||
const COMMIT_PAGE_SIZE = 50
|
||||
|
||||
// 已选提交详情
|
||||
const selectedCommit = ref<{ hash: string; subject: string; timestamp: number } | null>(null)
|
||||
const selectedCommit = ref<{ hash: string; subject: string; timestamp: number; author: string } | null>(null)
|
||||
const commitDetail = ref<{ files: { status: string; path: string }[]; diff: string } | null>(null)
|
||||
const commitDetailLoading = ref(false)
|
||||
const commitSelectedFile = ref('')
|
||||
@@ -271,6 +276,11 @@ async function loadStatus() {
|
||||
}
|
||||
}
|
||||
|
||||
/** 占位:分支切换后续接入(需拉取分支列表 + checkout IPC)。 */
|
||||
function onSwitchBranch() {
|
||||
// TODO: 弹出分支列表选择 + 调用 git checkout
|
||||
}
|
||||
|
||||
async function loadMoreCommits() {
|
||||
if (loadingMore.value || !hasMoreCommits.value) return
|
||||
loadingMore.value = true
|
||||
@@ -311,7 +321,7 @@ async function selectFile(path: string) {
|
||||
}
|
||||
|
||||
/** 点击提交行 → 加载该提交的变更文件列表 */
|
||||
async function selectCommit(c: { hash: string; subject: string; timestamp: number }) {
|
||||
async function selectCommit(c: { hash: string; subject: string; timestamp: number; author: string }) {
|
||||
selectedCommit.value = c
|
||||
commitDetail.value = null
|
||||
commitSelectedFile.value = ''
|
||||
@@ -370,6 +380,21 @@ watch(() => props.moduleId, loadStatus, { immediate: true })
|
||||
.branch-icon { font-size: 16px; }
|
||||
.branch-name { font-weight: 600; color: var(--df-accent); }
|
||||
.branch-count { margin-left: auto; font-size: 12px; color: var(--df-text-dim); }
|
||||
.branch-switch-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: var(--df-text-dim);
|
||||
cursor: pointer;
|
||||
padding: 2px 6px;
|
||||
border-radius: 3px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
transition: all 0.15s;
|
||||
}
|
||||
.branch-switch-btn:hover {
|
||||
color: var(--df-text);
|
||||
background: rgba(255,255,255,0.05);
|
||||
}
|
||||
|
||||
/* Tabs */
|
||||
.gc-tabs { display: flex; border-bottom: 0.5px solid var(--df-border); flex-shrink: 0; }
|
||||
@@ -424,6 +449,12 @@ watch(() => props.moduleId, loadStatus, { immediate: true })
|
||||
font-size: 11px; color: var(--df-accent); flex-shrink: 0;
|
||||
}
|
||||
.gc-commit-msg { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 12px; }
|
||||
.gc-commit-author {
|
||||
font-size: 11px; color: var(--df-text-dim);
|
||||
flex-shrink: 0;
|
||||
max-width: 100px;
|
||||
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
||||
}
|
||||
.gc-commit-time { font-size: 11px; color: var(--df-text-dim); flex-shrink: 0; }
|
||||
|
||||
/* 提交详情 */
|
||||
@@ -438,6 +469,10 @@ watch(() => props.moduleId, loadStatus, { immediate: true })
|
||||
font-size: 11px; color: var(--df-accent);
|
||||
}
|
||||
.gc-commit-detail-msg { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 12px; }
|
||||
.gc-commit-detail-author {
|
||||
font-size: 11px; color: var(--df-text-dim);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.gc-commit-detail-time { font-size: 11px; color: var(--df-text-dim); flex-shrink: 0; }
|
||||
.gc-detail-close {
|
||||
background: none; border: none; color: var(--df-text-dim);
|
||||
|
||||
@@ -13,5 +13,6 @@ export default {
|
||||
notGitRepo: 'Not a git repository',
|
||||
detached: 'HEAD detached',
|
||||
loadMore: 'Load more',
|
||||
switchBranch: 'Switch branch',
|
||||
},
|
||||
}
|
||||
|
||||
@@ -13,5 +13,6 @@ export default {
|
||||
notGitRepo: '非 Git 仓库',
|
||||
detached: 'HEAD 分离',
|
||||
loadMore: '加载更多',
|
||||
switchBranch: '切换分支',
|
||||
},
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user