zen-gitsync 2.0.3 → 2.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/ui/client/package.json +1 -0
- package/src/ui/client/src/App.vue +174 -171
- package/src/ui/client/src/components/CommitForm.vue +591 -290
- package/src/ui/client/src/components/GitStatus.vue +47 -25
- package/src/ui/client/src/components/LogList.vue +351 -85
- package/src/ui/client/src/main.ts +3 -0
- package/src/ui/client/src/stores/gitLogStore.ts +464 -0
- package/src/ui/client/src/stores/gitStore.ts +216 -0
- package/src/ui/client/stats.html +1 -1
- package/src/ui/public/assets/index-D5irnfho.css +1 -0
- package/src/ui/public/assets/index-DBck3u67.js +8 -0
- package/src/ui/public/assets/vendor-CdJ34PvS.js +45 -0
- package/src/ui/public/index.html +3 -3
- package/src/ui/server/index.js +109 -4
- package/src/ui/public/assets/index-BHmYZROy.css +0 -1
- package/src/ui/public/assets/index-kfMX1bxz.js +0 -9
- package/src/ui/public/assets/vendor-DxvF30ca.js +0 -41
|
@@ -1,12 +1,24 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import { ref, onMounted,
|
|
2
|
+
import { ref, onMounted, computed } from 'vue'
|
|
3
3
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
|
4
4
|
// import { io } from 'socket.io-client'
|
|
5
5
|
import { Refresh, ArrowLeft, ArrowRight, Folder, Document, ArrowUp, RefreshRight } from '@element-plus/icons-vue'
|
|
6
|
+
import { useGitLogStore } from '../stores/gitLogStore'
|
|
7
|
+
import { useGitStore } from '../stores/gitStore'
|
|
8
|
+
|
|
9
|
+
// 定义props
|
|
10
|
+
const props = defineProps({
|
|
11
|
+
initialDirectory: {
|
|
12
|
+
type: String,
|
|
13
|
+
default: ''
|
|
14
|
+
}
|
|
15
|
+
})
|
|
6
16
|
|
|
17
|
+
const gitLogStore = useGitLogStore()
|
|
18
|
+
const gitStore = useGitStore()
|
|
7
19
|
const status = ref('加载中...')
|
|
8
20
|
// const socket = io()
|
|
9
|
-
const isRefreshing =
|
|
21
|
+
const isRefreshing = computed(() => gitLogStore.isLoadingStatus)
|
|
10
22
|
const fileList = ref<{path: string, type: string}[]>([])
|
|
11
23
|
const selectedFile = ref('')
|
|
12
24
|
const diffContent = ref('')
|
|
@@ -48,23 +60,24 @@ function parseStatus(statusText: string) {
|
|
|
48
60
|
fileList.value = files
|
|
49
61
|
}
|
|
50
62
|
|
|
51
|
-
const currentDirectory = ref('')
|
|
63
|
+
const currentDirectory = ref(props.initialDirectory || '');
|
|
52
64
|
async function loadStatus() {
|
|
53
65
|
try {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
66
|
+
// 如果没有初始目录,才需要请求当前目录
|
|
67
|
+
if (!currentDirectory.value) {
|
|
68
|
+
const responseDir = await fetch('/api/current_directory')
|
|
69
|
+
const dirData = await responseDir.json()
|
|
70
|
+
currentDirectory.value = dirData.directory || '未知目录'
|
|
71
|
+
}
|
|
59
72
|
|
|
60
|
-
// 如果不是Git
|
|
61
|
-
if (
|
|
73
|
+
// 如果不是Git仓库,直接显示提示并返回
|
|
74
|
+
if (!gitStore.isGitRepo) {
|
|
62
75
|
status.value = '当前目录不是一个Git仓库'
|
|
63
76
|
fileList.value = []
|
|
64
|
-
ElMessage.warning('当前目录不是一个Git仓库')
|
|
65
77
|
return
|
|
66
78
|
}
|
|
67
79
|
|
|
80
|
+
// 直接获取Git状态,不再通过store调用
|
|
68
81
|
const response = await fetch('/api/status')
|
|
69
82
|
const data = await response.json()
|
|
70
83
|
status.value = data.status
|
|
@@ -83,8 +96,6 @@ async function loadStatus() {
|
|
|
83
96
|
message: '刷新失败: ' + (error as Error).message,
|
|
84
97
|
type: 'error',
|
|
85
98
|
})
|
|
86
|
-
} finally {
|
|
87
|
-
isRefreshing.value = false
|
|
88
99
|
}
|
|
89
100
|
}
|
|
90
101
|
|
|
@@ -289,13 +300,28 @@ async function changeDirectory() {
|
|
|
289
300
|
currentDirectory.value = result.directory
|
|
290
301
|
isDirectoryDialogVisible.value = false
|
|
291
302
|
|
|
292
|
-
//
|
|
293
|
-
|
|
303
|
+
// 直接使用API返回的Git仓库状态
|
|
304
|
+
gitStore.isGitRepo = result.isGitRepo
|
|
305
|
+
|
|
306
|
+
// 如果是Git仓库,加载Git相关数据
|
|
307
|
+
if (result.isGitRepo) {
|
|
308
|
+
// 加载Git分支和用户信息
|
|
309
|
+
await Promise.all([
|
|
310
|
+
gitStore.getCurrentBranch(),
|
|
311
|
+
gitStore.getAllBranches(),
|
|
312
|
+
gitStore.getUserInfo()
|
|
313
|
+
])
|
|
314
|
+
|
|
315
|
+
// 刷新Git状态
|
|
316
|
+
await loadStatus()
|
|
317
|
+
} else {
|
|
294
318
|
ElMessage.warning('当前目录不是一个Git仓库')
|
|
319
|
+
status.value = '当前目录不是一个Git仓库'
|
|
320
|
+
fileList.value = []
|
|
321
|
+
|
|
322
|
+
// 清空Git相关状态
|
|
323
|
+
gitStore.$reset() // 使用pinia的reset方法重置状态
|
|
295
324
|
}
|
|
296
|
-
|
|
297
|
-
// 刷新状态
|
|
298
|
-
loadStatus()
|
|
299
325
|
} else {
|
|
300
326
|
ElMessage.error(result.error || '切换目录失败')
|
|
301
327
|
}
|
|
@@ -368,13 +394,9 @@ async function revertFileChanges(filePath: string) {
|
|
|
368
394
|
}
|
|
369
395
|
|
|
370
396
|
onMounted(() => {
|
|
397
|
+
// App.vue已经加载了Git相关数据,此时只需加载状态
|
|
398
|
+
// 如果已有初始目录,则只需加载状态
|
|
371
399
|
loadStatus()
|
|
372
|
-
|
|
373
|
-
// Socket.io 事件
|
|
374
|
-
// socket.on('status_update', (data: { status: string }) => {
|
|
375
|
-
// status.value = data.status
|
|
376
|
-
// parseStatus(data.status)
|
|
377
|
-
// })
|
|
378
400
|
})
|
|
379
401
|
|
|
380
402
|
// onUnmounted(() => {
|
|
@@ -397,7 +419,7 @@ defineExpose({
|
|
|
397
419
|
</el-button>
|
|
398
420
|
</div>
|
|
399
421
|
<div class="status-header">
|
|
400
|
-
<h2>Git
|
|
422
|
+
<h2>Git 状态(git status)</h2>
|
|
401
423
|
<el-button
|
|
402
424
|
type="primary"
|
|
403
425
|
:icon="Refresh"
|
|
@@ -1,105 +1,192 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import { ref, onMounted,
|
|
3
|
-
import { ElTable, ElTableColumn, ElTag, ElButton } from 'element-plus'
|
|
4
|
-
import { RefreshRight } from '@element-plus/icons-vue'
|
|
2
|
+
import { ref, onMounted, computed, watch } from 'vue'
|
|
3
|
+
import { ElTable, ElTableColumn, ElTag, ElButton, ElSlider } from 'element-plus'
|
|
4
|
+
import { RefreshRight, ZoomIn, ZoomOut } from '@element-plus/icons-vue'
|
|
5
5
|
import 'element-plus/dist/index.css'
|
|
6
6
|
import { createGitgraph } from '@gitgraph/js'
|
|
7
|
+
import { useGitLogStore } from '../stores/gitLogStore'
|
|
8
|
+
import { useGitStore } from '../stores/gitStore'
|
|
7
9
|
|
|
8
10
|
interface LogItem {
|
|
9
11
|
hash: string
|
|
10
12
|
date: string
|
|
11
13
|
author: string
|
|
12
|
-
email: string
|
|
14
|
+
email: string
|
|
13
15
|
message: string
|
|
14
|
-
branch?: string
|
|
15
|
-
parents?: string[]
|
|
16
|
+
branch?: string
|
|
17
|
+
parents?: string[]
|
|
16
18
|
}
|
|
17
19
|
|
|
18
|
-
|
|
20
|
+
// 使用Git状态和日志Store
|
|
21
|
+
const gitLogStore = useGitLogStore()
|
|
22
|
+
const gitStore = useGitStore()
|
|
23
|
+
|
|
24
|
+
// 获取日志数据
|
|
25
|
+
let logsData: LogItem[] = []
|
|
26
|
+
const logs = ref<LogItem[]>(logsData)
|
|
19
27
|
const errorMessage = ref('')
|
|
20
|
-
|
|
28
|
+
// 定义本地加载状态,而不是依赖于computed
|
|
29
|
+
const localLoading = ref(false)
|
|
30
|
+
const isLoading = computed(() => gitLogStore.isLoadingLog || localLoading.value)
|
|
21
31
|
const showAllCommits = ref(false)
|
|
22
32
|
const totalCommits = ref(0)
|
|
23
|
-
const showGraphView = ref(true)
|
|
33
|
+
const showGraphView = ref(true)
|
|
24
34
|
const graphContainer = ref<HTMLElement | null>(null)
|
|
25
35
|
|
|
36
|
+
// 添加图表缩放控制
|
|
37
|
+
const graphScale = ref(1)
|
|
38
|
+
const minScale = 0.5
|
|
39
|
+
const maxScale = 1.5
|
|
40
|
+
const scaleStep = 0.1
|
|
41
|
+
|
|
26
42
|
// 加载提交历史
|
|
27
43
|
async function loadLog(all = false) {
|
|
44
|
+
// 从gitStore获取仓库状态
|
|
45
|
+
const gitStore = useGitStore()
|
|
46
|
+
|
|
47
|
+
// 检查是否是Git仓库
|
|
48
|
+
if (!gitStore.isGitRepo) {
|
|
49
|
+
errorMessage.value = '当前目录不是Git仓库'
|
|
50
|
+
return
|
|
51
|
+
}
|
|
52
|
+
|
|
28
53
|
try {
|
|
29
|
-
isLoading.value = true
|
|
30
54
|
showAllCommits.value = all
|
|
31
|
-
|
|
55
|
+
|
|
56
|
+
// 设置本地加载状态
|
|
57
|
+
localLoading.value = true
|
|
58
|
+
|
|
59
|
+
// 保留graph参数,但服务器端其实不做特殊处理
|
|
60
|
+
// 这样可以兼容之前的代码,避免大量修改
|
|
32
61
|
const url = all ? '/api/log?all=true&graph=true' : '/api/log?graph=true'
|
|
62
|
+
console.log(`加载日志数据: ${url}`)
|
|
63
|
+
|
|
33
64
|
const response = await fetch(url)
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
65
|
+
const data = await response.json()
|
|
66
|
+
|
|
67
|
+
// 清空现有数据
|
|
68
|
+
logsData.length = 0
|
|
69
|
+
|
|
70
|
+
// 更新本地数据
|
|
71
|
+
if (Array.isArray(data)) {
|
|
72
|
+
// 新的API格式,直接返回数组
|
|
73
|
+
console.log(`日志加载完成: 共${data.length}条记录`)
|
|
74
|
+
|
|
75
|
+
// 填充logsData
|
|
76
|
+
data.forEach((item: LogItem) => logsData.push(item))
|
|
77
|
+
|
|
78
|
+
totalCommits.value = data.length
|
|
79
|
+
} else if (data.log && Array.isArray(data.log)) {
|
|
80
|
+
// 旧版API格式,兼容处理
|
|
81
|
+
console.log(`日志加载完成: 共${data.log.length}条记录`)
|
|
82
|
+
|
|
83
|
+
// 填充logsData
|
|
84
|
+
data.log.forEach((item: LogItem) => logsData.push(item))
|
|
85
|
+
|
|
86
|
+
totalCommits.value = data.log.length
|
|
87
|
+
} else {
|
|
88
|
+
console.error('未知的日志数据格式:', data)
|
|
89
|
+
errorMessage.value = '日志数据格式错误'
|
|
90
|
+
return
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// 确保logs.value也更新
|
|
94
|
+
logs.value = [...logsData]
|
|
95
|
+
|
|
96
|
+
console.log(`logsData长度: ${logsData.length}`) // 添加调试日志
|
|
37
97
|
|
|
38
98
|
// 加载完数据后渲染图表
|
|
39
99
|
if (showGraphView.value) {
|
|
40
100
|
setTimeout(renderGraph, 0)
|
|
41
101
|
}
|
|
102
|
+
|
|
103
|
+
errorMessage.value = ''
|
|
42
104
|
} catch (error) {
|
|
43
|
-
errorMessage.value = '加载提交历史失败: ' + (error
|
|
105
|
+
errorMessage.value = '加载提交历史失败: ' + (error instanceof Error ? error.message : String(error))
|
|
106
|
+
console.error('加载日志失败:', error)
|
|
44
107
|
} finally {
|
|
45
|
-
|
|
108
|
+
// 重置本地加载状态
|
|
109
|
+
localLoading.value = false
|
|
46
110
|
}
|
|
47
111
|
}
|
|
48
112
|
|
|
49
113
|
// 渲染Git图表
|
|
50
114
|
async function renderGraph() {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
// 清空容器
|
|
54
|
-
graphContainer.value.innerHTML = ''
|
|
115
|
+
console.log(`开始渲染图表...数据长度: ${logsData.length}`)
|
|
55
116
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
// 创建gitgraph实例
|
|
61
|
-
const gitgraph = createGitgraph(graphContainer.value, {
|
|
62
|
-
// 自定义选项
|
|
63
|
-
// @ts-ignore: true
|
|
64
|
-
orientation: 'vertical-reverse', // 从上到下的方向
|
|
65
|
-
// @ts-ignore: true
|
|
66
|
-
template: 'metro', // 使用metro模板
|
|
67
|
-
author: '提交者 <committer@example.com>'
|
|
68
|
-
})
|
|
117
|
+
if (!graphContainer.value) {
|
|
118
|
+
console.error('图表容器未找到')
|
|
119
|
+
return
|
|
120
|
+
}
|
|
69
121
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
branches[currentBranch || 'main'] = mainBranch
|
|
122
|
+
if (logsData.length === 0) {
|
|
123
|
+
console.error('没有日志数据可渲染')
|
|
124
|
+
return
|
|
125
|
+
}
|
|
75
126
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
let currentBranch = mainBranch
|
|
127
|
+
try {
|
|
128
|
+
// 清空容器
|
|
129
|
+
graphContainer.value.innerHTML = ''
|
|
80
130
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
131
|
+
console.log(`创建gitgraph实例,分支: ${gitStore.currentBranch || 'main'}`)
|
|
132
|
+
|
|
133
|
+
// 创建gitgraph实例
|
|
134
|
+
const gitgraph = createGitgraph(graphContainer.value, {
|
|
135
|
+
// 自定义选项
|
|
136
|
+
orientation: 'vertical-reverse' as any, // 使用类型断言解决类型错误
|
|
137
|
+
template: 'metro' as any, // 使用类型断言解决类型错误
|
|
138
|
+
author: '提交者 <committer@example.com>'
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
// 处理分支和提交数据
|
|
142
|
+
const branches: Record<string, any> = {}
|
|
143
|
+
const mainBranch = gitgraph.branch(gitStore.currentBranch || 'main')
|
|
144
|
+
branches[gitStore.currentBranch || 'main'] = mainBranch
|
|
89
145
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
146
|
+
console.log(`开始创建提交图...共${logsData.length}条提交`)
|
|
147
|
+
|
|
148
|
+
// 简化示例 - 实际实现需要根据API返回的数据结构调整
|
|
149
|
+
logsData.forEach((commit, index) => {
|
|
150
|
+
// 这里需要根据实际数据结构构建分支图
|
|
151
|
+
let currentBranch = mainBranch
|
|
152
|
+
|
|
153
|
+
// 如果有分支信息,使用对应的分支
|
|
154
|
+
if (commit.branch) {
|
|
155
|
+
const branchName = formatBranchName(commit.branch.split(',')[0])
|
|
156
|
+
if (!branches[branchName]) {
|
|
157
|
+
branches[branchName] = gitgraph.branch(branchName)
|
|
158
|
+
}
|
|
159
|
+
currentBranch = branches[branchName]
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// 创建提交,添加邮箱信息
|
|
163
|
+
currentBranch.commit({
|
|
164
|
+
hash: commit.hash,
|
|
165
|
+
subject: commit.message,
|
|
166
|
+
author: `${commit.author} <${commit.email}>`
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
if (index % 10 === 0) {
|
|
170
|
+
console.log(`已渲染 ${index + 1}/${logsData.length} 个提交`)
|
|
171
|
+
}
|
|
95
172
|
})
|
|
96
|
-
|
|
173
|
+
|
|
174
|
+
console.log('图表渲染完成')
|
|
175
|
+
|
|
176
|
+
// 确保渲染完成后调用自适应缩放
|
|
177
|
+
setTimeout(() => {
|
|
178
|
+
fitGraphToContainer()
|
|
179
|
+
}, 100)
|
|
180
|
+
} catch (error) {
|
|
181
|
+
console.error('渲染图表失败:', error)
|
|
182
|
+
errorMessage.value = '渲染图表失败: ' + (error instanceof Error ? error.message : String(error))
|
|
183
|
+
}
|
|
97
184
|
}
|
|
98
185
|
|
|
99
186
|
// 切换视图模式
|
|
100
187
|
function toggleViewMode() {
|
|
101
188
|
showGraphView.value = !showGraphView.value
|
|
102
|
-
if (showGraphView.value &&
|
|
189
|
+
if (showGraphView.value && logsData.length > 0) {
|
|
103
190
|
// 延迟执行以确保DOM已更新
|
|
104
191
|
setTimeout(renderGraph, 0)
|
|
105
192
|
}
|
|
@@ -110,14 +197,146 @@ function toggleAllCommits() {
|
|
|
110
197
|
loadLog(!showAllCommits.value)
|
|
111
198
|
}
|
|
112
199
|
|
|
200
|
+
// 格式化分支名(实现该函数,因为在模板中调用了)
|
|
201
|
+
function formatBranchName(ref: string) {
|
|
202
|
+
// 处理HEAD、远程分支等情况
|
|
203
|
+
if (ref.includes('HEAD -> ')) {
|
|
204
|
+
return ref.replace('HEAD -> ', '')
|
|
205
|
+
}
|
|
206
|
+
if (ref.includes('origin/')) {
|
|
207
|
+
return ref
|
|
208
|
+
}
|
|
209
|
+
return ref.trim()
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// 获取分支标签类型(实现该函数,因为在模板中调用了)
|
|
213
|
+
function getBranchTagType(ref: string) {
|
|
214
|
+
if (ref.includes('HEAD')) return 'success'
|
|
215
|
+
if (ref.includes('origin/')) return 'warning'
|
|
216
|
+
return 'info'
|
|
217
|
+
}
|
|
218
|
+
|
|
113
219
|
onMounted(() => {
|
|
114
|
-
|
|
220
|
+
// 检查gitLogStore中是否已有数据
|
|
221
|
+
if (gitStore.isGitRepo) {
|
|
222
|
+
if (gitLogStore.log.length > 0) {
|
|
223
|
+
// 如果已经有数据,直接使用现有数据
|
|
224
|
+
console.log('使用已加载的日志数据')
|
|
225
|
+
|
|
226
|
+
// 清空并填充logsData
|
|
227
|
+
logsData.length = 0
|
|
228
|
+
gitLogStore.log.forEach(item => logsData.push(item))
|
|
229
|
+
|
|
230
|
+
// 由于TypeScript类型错误,我们直接设置totalCommits而不是使用logs.value.length
|
|
231
|
+
totalCommits.value = gitLogStore.log.length
|
|
232
|
+
|
|
233
|
+
// 确保视图被渲染
|
|
234
|
+
if (showGraphView.value) {
|
|
235
|
+
setTimeout(() => {
|
|
236
|
+
console.log(`准备渲染图表,数据长度: ${logsData.length}`)
|
|
237
|
+
renderGraph()
|
|
238
|
+
}, 100)
|
|
239
|
+
}
|
|
240
|
+
} else {
|
|
241
|
+
// 否则加载数据
|
|
242
|
+
console.log('初始加载日志数据')
|
|
243
|
+
loadLog()
|
|
244
|
+
}
|
|
245
|
+
} else {
|
|
246
|
+
errorMessage.value = '当前目录不是Git仓库'
|
|
247
|
+
}
|
|
115
248
|
})
|
|
116
|
-
|
|
249
|
+
|
|
250
|
+
// 简化刷新函数,只需调用loadLog即可
|
|
251
|
+
const refreshLog = () => {
|
|
252
|
+
if (!gitStore.isGitRepo) {
|
|
253
|
+
errorMessage.value = '当前目录不是Git仓库'
|
|
254
|
+
return
|
|
255
|
+
}
|
|
256
|
+
loadLog(showAllCommits.value)
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// 监听store中的日志变化
|
|
260
|
+
watch(() => gitLogStore.log, (newLogs) => {
|
|
261
|
+
console.log('监听到gitLogStore.log变化,更新图表数据')
|
|
262
|
+
|
|
263
|
+
// 清空logsData
|
|
264
|
+
logsData.length = 0
|
|
265
|
+
|
|
266
|
+
// 重新填充数据
|
|
267
|
+
newLogs.forEach((item: LogItem) => logsData.push(item))
|
|
268
|
+
|
|
269
|
+
// 更新计数器
|
|
270
|
+
totalCommits.value = newLogs.length
|
|
271
|
+
|
|
272
|
+
// 尝试解决logs.value赋值问题
|
|
273
|
+
try {
|
|
274
|
+
// @ts-ignore - 忽略TypeScript错误
|
|
275
|
+
logs.value = [...logsData]
|
|
276
|
+
} catch (error) {
|
|
277
|
+
console.warn('无法更新logs.value:', error)
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
console.log(`数据更新完成,准备渲染图表(${logsData.length}条记录)`)
|
|
281
|
+
|
|
282
|
+
if (showGraphView.value && logsData.length > 0) {
|
|
283
|
+
setTimeout(renderGraph, 0)
|
|
284
|
+
}
|
|
285
|
+
})
|
|
286
|
+
|
|
117
287
|
// 暴露方法给父组件
|
|
118
288
|
defineExpose({
|
|
119
289
|
refreshLog
|
|
120
290
|
})
|
|
291
|
+
|
|
292
|
+
// 增加/减少缩放比例
|
|
293
|
+
function zoomIn() {
|
|
294
|
+
if (graphScale.value < maxScale) {
|
|
295
|
+
graphScale.value = Math.min(maxScale, graphScale.value + scaleStep)
|
|
296
|
+
applyScale()
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
function zoomOut() {
|
|
301
|
+
if (graphScale.value > minScale) {
|
|
302
|
+
graphScale.value = Math.max(minScale, graphScale.value - scaleStep)
|
|
303
|
+
applyScale()
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// 应用缩放比例
|
|
308
|
+
function applyScale() {
|
|
309
|
+
if (!graphContainer.value) return
|
|
310
|
+
|
|
311
|
+
const svgElement = graphContainer.value.querySelector('svg')
|
|
312
|
+
if (svgElement) {
|
|
313
|
+
svgElement.style.transform = `scale(${graphScale.value})`
|
|
314
|
+
svgElement.style.transformOrigin = 'top left'
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// 自适应图表大小
|
|
319
|
+
function fitGraphToContainer() {
|
|
320
|
+
if (!graphContainer.value) return
|
|
321
|
+
|
|
322
|
+
const svgElement = graphContainer.value.querySelector('svg')
|
|
323
|
+
if (!svgElement) return
|
|
324
|
+
|
|
325
|
+
// 获取SVG和容器的宽度
|
|
326
|
+
const svgWidth = svgElement.getBoundingClientRect().width / graphScale.value
|
|
327
|
+
const containerWidth = graphContainer.value.clientWidth
|
|
328
|
+
|
|
329
|
+
// 计算合适的缩放比例
|
|
330
|
+
if (svgWidth > containerWidth) {
|
|
331
|
+
// 如果SVG宽度大于容器,需要缩小
|
|
332
|
+
graphScale.value = Math.max(minScale, containerWidth / svgWidth)
|
|
333
|
+
} else {
|
|
334
|
+
// 否则恢复到默认比例
|
|
335
|
+
graphScale.value = 1
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
applyScale()
|
|
339
|
+
}
|
|
121
340
|
</script>
|
|
122
341
|
|
|
123
342
|
<template>
|
|
@@ -138,7 +357,7 @@ defineExpose({
|
|
|
138
357
|
@click="toggleAllCommits"
|
|
139
358
|
:loading="isLoading"
|
|
140
359
|
>
|
|
141
|
-
{{ showAllCommits ? '显示最近
|
|
360
|
+
{{ showAllCommits ? '显示最近30条' : '显示所有提交' }}
|
|
142
361
|
</el-button>
|
|
143
362
|
<el-button
|
|
144
363
|
:icon="RefreshRight"
|
|
@@ -153,16 +372,61 @@ defineExpose({
|
|
|
153
372
|
<div v-else>
|
|
154
373
|
<!-- 图表视图 -->
|
|
155
374
|
<div v-if="showGraphView" class="graph-view">
|
|
156
|
-
<div class="commit-count" v-if="
|
|
157
|
-
显示 {{
|
|
375
|
+
<div class="commit-count" v-if="logsData.length > 0">
|
|
376
|
+
显示 {{ logsData.length }} 条提交记录 {{ showAllCommits ? '(全部)' : '(最近30条)' }}
|
|
377
|
+
</div>
|
|
378
|
+
|
|
379
|
+
<!-- 添加缩放控制 -->
|
|
380
|
+
<div class="graph-controls">
|
|
381
|
+
<div class="zoom-controls">
|
|
382
|
+
<el-button
|
|
383
|
+
type="primary"
|
|
384
|
+
:icon="ZoomOut"
|
|
385
|
+
circle
|
|
386
|
+
size="small"
|
|
387
|
+
@click="zoomOut"
|
|
388
|
+
:disabled="graphScale <= minScale"
|
|
389
|
+
/>
|
|
390
|
+
|
|
391
|
+
<el-slider
|
|
392
|
+
v-model="graphScale"
|
|
393
|
+
:min="minScale"
|
|
394
|
+
:max="maxScale"
|
|
395
|
+
:step="scaleStep"
|
|
396
|
+
@change="applyScale"
|
|
397
|
+
class="zoom-slider"
|
|
398
|
+
/>
|
|
399
|
+
|
|
400
|
+
<el-button
|
|
401
|
+
type="primary"
|
|
402
|
+
:icon="ZoomIn"
|
|
403
|
+
circle
|
|
404
|
+
size="small"
|
|
405
|
+
@click="zoomIn"
|
|
406
|
+
:disabled="graphScale >= maxScale"
|
|
407
|
+
/>
|
|
408
|
+
|
|
409
|
+
<el-button
|
|
410
|
+
type="primary"
|
|
411
|
+
size="small"
|
|
412
|
+
@click="fitGraphToContainer"
|
|
413
|
+
>
|
|
414
|
+
自适应大小
|
|
415
|
+
</el-button>
|
|
416
|
+
</div>
|
|
417
|
+
|
|
418
|
+
<div class="scale-info">
|
|
419
|
+
缩放: {{ Math.round(graphScale * 100) }}%
|
|
420
|
+
</div>
|
|
158
421
|
</div>
|
|
422
|
+
|
|
159
423
|
<div ref="graphContainer" class="graph-container"></div>
|
|
160
424
|
</div>
|
|
161
425
|
|
|
162
426
|
<!-- 表格视图 -->
|
|
163
427
|
<div v-else>
|
|
164
428
|
<div class="commit-count" v-if="logs.length > 0">
|
|
165
|
-
显示 {{ logs.length }} 条提交记录 {{ showAllCommits ? '(全部)' : '(最近
|
|
429
|
+
显示 {{ logs.length }} 条提交记录 {{ showAllCommits ? '(全部)' : '(最近30条)' }}
|
|
166
430
|
</div>
|
|
167
431
|
<el-table :data="logs" style="width: 100%" stripe border v-loading="isLoading">
|
|
168
432
|
<el-table-column prop="hash" label="提交哈希" width="100" resizable />
|
|
@@ -232,37 +496,39 @@ defineExpose({
|
|
|
232
496
|
border-radius: 4px;
|
|
233
497
|
padding: 10px;
|
|
234
498
|
background-color: #fff;
|
|
235
|
-
|
|
499
|
+
position: relative;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
.graph-container svg {
|
|
503
|
+
transform-origin: top left;
|
|
504
|
+
transition: transform 0.2s ease;
|
|
236
505
|
}
|
|
237
506
|
|
|
238
507
|
.graph-view {
|
|
239
508
|
width: 100%;
|
|
240
509
|
}
|
|
241
|
-
</style>
|
|
242
510
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
// 移除 "origin/" 前缀
|
|
250
|
-
ref = ref.replace(/^origin\//, '')
|
|
251
|
-
|
|
252
|
-
// 移除 "tag: " 前缀,但保留标签名
|
|
253
|
-
ref = ref.replace(/^tag:\s*/, '')
|
|
254
|
-
|
|
255
|
-
return ref.trim()
|
|
511
|
+
.graph-controls {
|
|
512
|
+
display: flex;
|
|
513
|
+
justify-content: space-between;
|
|
514
|
+
align-items: center;
|
|
515
|
+
margin-bottom: 10px;
|
|
256
516
|
}
|
|
257
517
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
if (ref.includes('tag:')) return 'warning'
|
|
262
|
-
if (ref.includes('origin/')) return 'info'
|
|
263
|
-
return 'primary' // 修改空字符串为有效的type值
|
|
518
|
+
.zoom-controls {
|
|
519
|
+
display: flex;
|
|
520
|
+
gap: 8px;
|
|
264
521
|
}
|
|
265
|
-
|
|
522
|
+
|
|
523
|
+
.zoom-slider {
|
|
524
|
+
width: 200px;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
.scale-info {
|
|
528
|
+
font-size: 14px;
|
|
529
|
+
color: #606266;
|
|
530
|
+
}
|
|
531
|
+
</style>
|
|
266
532
|
|
|
267
533
|
/* 添加表格列调整样式 */
|
|
268
534
|
.el-table .el-table__cell .cell {
|