You've already forked agentic-coding-workflow
功能补齐、查看git提交历史
This commit is contained in:
@@ -94,3 +94,11 @@ export interface CommitBody {
|
||||
export interface CommitResp {
|
||||
sha: string
|
||||
}
|
||||
|
||||
export interface CommitLog {
|
||||
hash: string
|
||||
author: string
|
||||
email: string
|
||||
date: string
|
||||
subject: string
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import type {
|
||||
CreateWorktreeBody,
|
||||
CommitBody,
|
||||
CommitResp,
|
||||
CommitLog,
|
||||
} from './types'
|
||||
|
||||
const enc = (v: string) => encodeURIComponent(v)
|
||||
@@ -63,10 +64,14 @@ export const workspacesApi = {
|
||||
request<CommitResp>(`/api/v1/workspaces/${enc(wsID)}/main/commit`, { method: 'POST', body }),
|
||||
pushOnMain: (wsID: string) =>
|
||||
request<void>(`/api/v1/workspaces/${enc(wsID)}/main/push`, { method: 'POST' }),
|
||||
logOnMain: (wsID: string, n = 50) =>
|
||||
request<CommitLog[]>(`/api/v1/workspaces/${enc(wsID)}/main/log?n=${n}`),
|
||||
statusOnWorktree: (wtID: string) =>
|
||||
request<FileStatus[]>(`/api/v1/worktrees/${enc(wtID)}/status`),
|
||||
commitOnWorktree: (wtID: string, body: CommitBody) =>
|
||||
request<CommitResp>(`/api/v1/worktrees/${enc(wtID)}/commit`, { method: 'POST', body }),
|
||||
pushOnWorktree: (wtID: string) =>
|
||||
request<void>(`/api/v1/worktrees/${enc(wtID)}/push`, { method: 'POST' }),
|
||||
logOnWorktree: (wtID: string, n = 50) =>
|
||||
request<CommitLog[]>(`/api/v1/worktrees/${enc(wtID)}/log?n=${n}`),
|
||||
}
|
||||
|
||||
@@ -34,6 +34,15 @@
|
||||
:ws="store.current"
|
||||
/>
|
||||
</NTabPane>
|
||||
<NTabPane
|
||||
name="commits"
|
||||
tab="Commits"
|
||||
>
|
||||
<CommitHistoryTab
|
||||
v-if="store.current"
|
||||
:ws="store.current"
|
||||
/>
|
||||
</NTabPane>
|
||||
<NTabPane
|
||||
name="runs"
|
||||
tab="Run"
|
||||
@@ -66,13 +75,14 @@ import { useWorkspacesStore } from '@/stores/workspaces'
|
||||
import SyncBar from './components/SyncBar.vue'
|
||||
import WorktreesTab from './components/WorktreesTab.vue'
|
||||
import MainTab from './components/MainTab.vue'
|
||||
import CommitHistoryTab from './components/CommitHistoryTab.vue'
|
||||
import SettingsTab from './components/SettingsTab.vue'
|
||||
import RunsTab from './components/RunsTab.vue'
|
||||
|
||||
const route = useRoute()
|
||||
const store = useWorkspacesStore()
|
||||
const msg = useMessage()
|
||||
const tab = ref<'worktrees' | 'main' | 'settings' | 'runs'>('worktrees')
|
||||
const tab = ref<'worktrees' | 'main' | 'commits' | 'settings' | 'runs'>('worktrees')
|
||||
|
||||
const projectSlug = computed(() => route.params.slug as string)
|
||||
const wsSlug = computed(() => route.params.wsSlug as string)
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
<template>
|
||||
<div class="space-y-4">
|
||||
<!-- 目标选择:main 或某个 worktree -->
|
||||
<div class="flex items-center gap-3">
|
||||
<span class="text-sm text-gray-500">查看目标</span>
|
||||
<NSelect
|
||||
v-model:value="target"
|
||||
:options="targetOptions"
|
||||
size="small"
|
||||
class="w-64"
|
||||
/>
|
||||
<NButton
|
||||
size="small"
|
||||
:loading="loading"
|
||||
@click="refresh"
|
||||
>
|
||||
刷新
|
||||
</NButton>
|
||||
</div>
|
||||
|
||||
<NDataTable
|
||||
:columns="columns"
|
||||
:data="commits"
|
||||
:loading="loading"
|
||||
size="small"
|
||||
:scroll-x="800"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, h, onMounted, ref, watch } from 'vue'
|
||||
import { NButton, NDataTable, NSelect, NTag, NTooltip } from 'naive-ui'
|
||||
import type { DataTableColumns } from 'naive-ui'
|
||||
|
||||
import { workspacesApi } from '@/api/workspaces'
|
||||
import { useWorkspacesStore } from '@/stores/workspaces'
|
||||
import type { CommitLog, Workspace } from '@/api/types'
|
||||
|
||||
const props = defineProps<{ ws: Workspace }>()
|
||||
const store = useWorkspacesStore()
|
||||
|
||||
const target = ref<string>('main')
|
||||
const commits = ref<CommitLog[]>([])
|
||||
const loading = ref(false)
|
||||
|
||||
const targetOptions = computed(() => {
|
||||
const opts = [{ label: `Main (${props.ws.default_branch})`, value: 'main' }]
|
||||
for (const wt of store.worktrees) {
|
||||
opts.push({ label: wt.branch, value: wt.id })
|
||||
}
|
||||
return opts
|
||||
})
|
||||
|
||||
function formatDate(d: string) {
|
||||
return new Date(d).toLocaleString('zh-CN', { hour12: false })
|
||||
}
|
||||
|
||||
function shortHash(hash: string) {
|
||||
return hash.slice(0, 8)
|
||||
}
|
||||
|
||||
const columns = computed<DataTableColumns<CommitLog>>(() => [
|
||||
{
|
||||
title: 'Commit',
|
||||
key: 'hash',
|
||||
width: 100,
|
||||
fixed: 'left',
|
||||
render(row: CommitLog) {
|
||||
return h(
|
||||
NTag,
|
||||
{ size: 'tiny', type: 'default' },
|
||||
{ default: () => shortHash(row.hash) },
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '提交信息',
|
||||
key: 'subject',
|
||||
ellipsis: { tooltip: true },
|
||||
},
|
||||
{
|
||||
title: '作者',
|
||||
key: 'author',
|
||||
width: 140,
|
||||
render(row: CommitLog) {
|
||||
return h(
|
||||
NTooltip,
|
||||
{},
|
||||
{
|
||||
trigger: () => h('span', row.author),
|
||||
default: () => row.email,
|
||||
},
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '时间',
|
||||
key: 'date',
|
||||
width: 160,
|
||||
render(row: CommitLog) {
|
||||
return h('span', { class: 'text-xs text-gray-500' }, formatDate(row.date))
|
||||
},
|
||||
},
|
||||
])
|
||||
|
||||
async function refresh() {
|
||||
loading.value = true
|
||||
try {
|
||||
if (target.value === 'main') {
|
||||
commits.value = await workspacesApi.logOnMain(props.ws.id)
|
||||
} else {
|
||||
commits.value = await workspacesApi.logOnWorktree(target.value)
|
||||
}
|
||||
} catch (e: unknown) {
|
||||
commits.value = []
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
watch(target, refresh)
|
||||
|
||||
onMounted(() => {
|
||||
refresh()
|
||||
})
|
||||
</script>
|
||||
Reference in New Issue
Block a user