zen-gitsync 2.0.4 → 2.0.5

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.
@@ -16,10 +16,11 @@ const props = defineProps({
16
16
 
17
17
  const gitLogStore = useGitLogStore()
18
18
  const gitStore = useGitStore()
19
- const status = ref('加载中...')
19
+ // 移除本地status定义,直接使用store中的statusText
20
+ // const status = ref('加载中...')
20
21
  // const socket = io()
21
22
  const isRefreshing = computed(() => gitLogStore.isLoadingStatus)
22
- const fileList = ref<{path: string, type: string}[]>([])
23
+ // 移除本地fileList定义,改用store中的fileList
23
24
  const selectedFile = ref('')
24
25
  const diffContent = ref('')
25
26
  const diffDialogVisible = ref(false)
@@ -37,29 +38,6 @@ const directoryItems = ref<{name: string, path: string, type: string}[]>([])
37
38
  const isBrowsing = ref(false)
38
39
  const browseErrorMessage = ref('')
39
40
 
40
- // 解析 git status 输出,提取文件及类型
41
- function parseStatus(statusText: string) {
42
- if (statusText === undefined) return
43
- const lines = statusText.split('\n')
44
- const files: {path: string, type: string}[] = []
45
- for (const line of lines) {
46
- // 匹配常见的 git status --porcelain 格式
47
- // M: 修改, A: 新增, D: 删除, ??: 未跟踪
48
- const match = line.match(/^([ MADRCU\?]{2})\s+(.+)$/)
49
- if (match) {
50
- let type = ''
51
- const code = match[1].trim()
52
- if (code === 'M' || code === 'MM' || code === 'AM' || code === 'RM') type = 'modified'
53
- else if (code === 'A' || code === 'AA') type = 'added'
54
- else if (code === 'D' || code === 'AD' || code === 'DA') type = 'deleted'
55
- else if (code === '??') type = 'untracked'
56
- else type = 'other'
57
- files.push({ path: match[2], type })
58
- }
59
- }
60
- fileList.value = files
61
- }
62
-
63
41
  const currentDirectory = ref(props.initialDirectory || '');
64
42
  async function loadStatus() {
65
43
  try {
@@ -72,26 +50,17 @@ async function loadStatus() {
72
50
 
73
51
  // 如果不是Git仓库,直接显示提示并返回
74
52
  if (!gitStore.isGitRepo) {
75
- status.value = '当前目录不是一个Git仓库'
76
- fileList.value = []
77
53
  return
78
54
  }
79
55
 
80
- // 直接获取Git状态,不再通过store调用
81
- const response = await fetch('/api/status')
82
- const data = await response.json()
83
- status.value = data.status
84
-
85
- const response_porcelain = await fetch('/api/status_porcelain')
86
- const data_porcelain = await response_porcelain.json()
87
- parseStatus(data_porcelain.status)
56
+ // 使用gitLogStore获取Git状态
57
+ await gitLogStore.fetchStatus()
58
+
88
59
  ElMessage({
89
60
  message: 'Git 状态已刷新',
90
61
  type: 'success',
91
62
  })
92
63
  } catch (error) {
93
- status.value = '加载状态失败: ' + (error as Error).message
94
- fileList.value = []
95
64
  ElMessage({
96
65
  message: '刷新失败: ' + (error as Error).message,
97
66
  type: 'error',
@@ -145,7 +114,7 @@ async function getFileDiff(filePath: string) {
145
114
  isLoadingDiff.value = true
146
115
  selectedFile.value = filePath
147
116
  // 设置当前文件索引
148
- currentFileIndex.value = fileList.value.findIndex(file => file.path === filePath)
117
+ currentFileIndex.value = gitLogStore.fileList.findIndex(file => file.path === filePath)
149
118
  const response = await fetch(`/api/diff?file=${encodeURIComponent(filePath)}`)
150
119
  const data = await response.json()
151
120
  diffContent.value = data.diff || '没有变更'
@@ -163,19 +132,19 @@ async function getFileDiff(filePath: string) {
163
132
 
164
133
  // 添加切换到上一个文件的方法
165
134
  async function goToPreviousFile() {
166
- if (fileList.value.length === 0 || currentFileIndex.value <= 0) return
135
+ if (gitLogStore.fileList.length === 0 || currentFileIndex.value <= 0) return
167
136
 
168
137
  const newIndex = currentFileIndex.value - 1
169
- const prevFile = fileList.value[newIndex]
138
+ const prevFile = gitLogStore.fileList[newIndex]
170
139
  await getFileDiff(prevFile.path)
171
140
  }
172
141
 
173
142
  // 添加切换到下一个文件的方法
174
143
  async function goToNextFile() {
175
- if (fileList.value.length === 0 || currentFileIndex.value >= fileList.value.length - 1) return
144
+ if (gitLogStore.fileList.length === 0 || currentFileIndex.value >= gitLogStore.fileList.length - 1) return
176
145
 
177
146
  const newIndex = currentFileIndex.value + 1
178
- const nextFile = fileList.value[newIndex]
147
+ const nextFile = gitLogStore.fileList[newIndex]
179
148
  await getFileDiff(nextFile.path)
180
149
  }
181
150
 
@@ -316,9 +285,6 @@ async function changeDirectory() {
316
285
  await loadStatus()
317
286
  } else {
318
287
  ElMessage.warning('当前目录不是一个Git仓库')
319
- status.value = '当前目录不是一个Git仓库'
320
- fileList.value = []
321
-
322
288
  // 清空Git相关状态
323
289
  gitStore.$reset() // 使用pinia的reset方法重置状态
324
290
  }
@@ -429,11 +395,13 @@ defineExpose({
429
395
  :loading="isRefreshing"
430
396
  />
431
397
  </div>
432
- <div class="status-box">{{ status }}</div>
398
+ <div class="status-box">
399
+ {{ !gitStore.isGitRepo ? '当前目录不是一个Git仓库' : gitLogStore.statusText || '加载中...' }}
400
+ </div>
433
401
  <!-- 颜色区分不同类型文件 -->
434
- <div v-if="fileList.length" class="file-list">
402
+ <div v-if="gitLogStore.fileList.length" class="file-list">
435
403
  <div
436
- v-for="file in fileList"
404
+ v-for="file in gitLogStore.fileList"
437
405
  :key="file.path"
438
406
  :class="['file-item', file.type]"
439
407
  >
@@ -514,18 +482,20 @@ defineExpose({
514
482
  </div>
515
483
 
516
484
  <!-- 目录内容列表 -->
517
- <ul class="directory-items">
518
- <li
519
- v-for="item in directoryItems"
520
- :key="item.path"
521
- :class="['directory-item', item.type]"
522
- @click="selectDirectoryItem(item)"
523
- >
524
- <el-icon v-if="item.type === 'directory'"><Folder /></el-icon>
525
- <el-icon v-else><Document /></el-icon>
526
- <span>{{ item.name }}</span>
527
- </li>
528
- </ul>
485
+ <div class="directory-items-container">
486
+ <ul class="directory-items">
487
+ <li
488
+ v-for="item in directoryItems"
489
+ :key="item.path"
490
+ :class="['directory-item', item.type]"
491
+ @click="selectDirectoryItem(item)"
492
+ >
493
+ <el-icon v-if="item.type === 'directory'"><Folder /></el-icon>
494
+ <el-icon v-else><Document /></el-icon>
495
+ <span>{{ item.name }}</span>
496
+ </li>
497
+ </ul>
498
+ </div>
529
499
  </div>
530
500
  </el-dialog>
531
501
 
@@ -546,14 +516,14 @@ defineExpose({
546
516
  <el-button
547
517
  :icon="ArrowLeft"
548
518
  @click="goToPreviousFile"
549
- :disabled="currentFileIndex <= 0 || fileList.length === 0"
519
+ :disabled="currentFileIndex <= 0 || gitLogStore.fileList.length === 0"
550
520
  circle
551
521
  />
552
- <span class="file-counter">{{ currentFileIndex + 1 }} / {{ fileList.length }}</span>
522
+ <span class="file-counter">{{ currentFileIndex + 1 }} / {{ gitLogStore.fileList.length }}</span>
553
523
  <el-button
554
524
  :icon="ArrowRight"
555
525
  @click="goToNextFile"
556
- :disabled="currentFileIndex >= fileList.length - 1 || fileList.length === 0"
526
+ :disabled="currentFileIndex >= gitLogStore.fileList.length - 1 || gitLogStore.fileList.length === 0"
557
527
  circle
558
528
  />
559
529
  </div>
@@ -730,14 +700,25 @@ defineExpose({
730
700
 
731
701
  .directory-browser {
732
702
  padding: 10px;
733
- max-height: 400px;
734
- overflow-y: auto;
703
+ height: 400px;
704
+ display: flex;
705
+ flex-direction: column;
735
706
  }
736
707
 
737
708
  .browser-nav {
738
709
  margin-bottom: 10px;
739
710
  display: flex;
740
711
  justify-content: space-between;
712
+ position: sticky;
713
+ top: 0;
714
+ background-color: #fff;
715
+ z-index: 1;
716
+ padding: 10px 0;
717
+ }
718
+
719
+ .directory-items-container {
720
+ flex: 1;
721
+ overflow-y: auto;
741
722
  }
742
723
 
743
724
  .directory-items {
@@ -39,6 +39,9 @@ const minScale = 0.5
39
39
  const maxScale = 1.5
40
40
  const scaleStep = 0.1
41
41
 
42
+ // 添加日志被刷新的提示状态
43
+ const logRefreshed = ref(false)
44
+
42
45
  // 加载提交历史
43
46
  async function loadLog(all = false) {
44
47
  // 从gitStore获取仓库状态
@@ -95,6 +98,11 @@ async function loadLog(all = false) {
95
98
 
96
99
  console.log(`logsData长度: ${logsData.length}`) // 添加调试日志
97
100
 
101
+ // 设置刷新提示状态
102
+ logRefreshed.value = true
103
+ // 2秒后隐藏提示
104
+ setTimeout(() => { logRefreshed.value = false }, 2000)
105
+
98
106
  // 加载完数据后渲染图表
99
107
  if (showGraphView.value) {
100
108
  setTimeout(renderGraph, 0)
@@ -365,11 +373,17 @@ function fitGraphToContainer() {
365
373
  size="small"
366
374
  @click="refreshLog()"
367
375
  :loading="isLoading"
376
+ :class="{ 'refresh-button-animated': logRefreshed }"
368
377
  />
369
378
  </div>
370
379
  </div>
371
380
  <div v-if="errorMessage">{{ errorMessage }}</div>
372
381
  <div v-else>
382
+ <!-- 添加刷新提示 -->
383
+ <div v-if="logRefreshed" class="refresh-notification">
384
+ 提交历史已刷新
385
+ </div>
386
+
373
387
  <!-- 图表视图 -->
374
388
  <div v-if="showGraphView" class="graph-view">
375
389
  <div class="commit-count" v-if="logsData.length > 0">
@@ -528,6 +542,34 @@ function fitGraphToContainer() {
528
542
  font-size: 14px;
529
543
  color: #606266;
530
544
  }
545
+
546
+ .refresh-button-animated {
547
+ animation: pulse 1s;
548
+ }
549
+
550
+ .refresh-notification {
551
+ background-color: #f0f9eb;
552
+ color: #67c23a;
553
+ padding: 8px;
554
+ border-radius: 4px;
555
+ margin-bottom: 10px;
556
+ text-align: center;
557
+ font-size: 14px;
558
+ border-left: 4px solid #67c23a;
559
+ animation: fadeOut 2s forwards;
560
+ }
561
+
562
+ @keyframes pulse {
563
+ 0% { transform: scale(1); }
564
+ 50% { transform: scale(1.2); }
565
+ 100% { transform: scale(1); }
566
+ }
567
+
568
+ @keyframes fadeOut {
569
+ 0% { opacity: 1; }
570
+ 70% { opacity: 1; }
571
+ 100% { opacity: 0; }
572
+ }
531
573
  </style>
532
574
 
533
575
  /* 添加表格列调整样式 */
@@ -3,6 +3,9 @@ import { ref } from 'vue'
3
3
  import { ElMessage } from 'element-plus'
4
4
  import { useGitStore } from './gitStore'
5
5
 
6
+ // 定义Git操作间隔时间(毫秒)
7
+ const GIT_OPERATION_DELAY = 300
8
+
6
9
  export const useGitLogStore = defineStore('gitLog', () => {
7
10
  // 引用gitStore获取仓库状态
8
11
  const gitStore = useGitStore()
@@ -14,11 +17,45 @@ export const useGitLogStore = defineStore('gitLog', () => {
14
17
  unstaged: [],
15
18
  untracked: []
16
19
  })
20
+ // 添加Git状态文本
21
+ const statusText = ref('')
22
+ // 添加fileList状态用于保存porcelain格式的状态
23
+ const fileList = ref<{path: string, type: string}[]>([])
17
24
  const isLoadingLog = ref(false)
18
25
  const isLoadingStatus = ref(false)
19
26
  const isAddingFiles = ref(false)
27
+ const isCommiting = ref(false)
28
+ const isPushing = ref(false)
20
29
  const isResetting = ref(false)
21
30
 
31
+ // 解析 git status --porcelain 输出,提取文件及类型
32
+ function parseStatusPorcelain(statusText: string) {
33
+ if (statusText === undefined || statusText === '') {
34
+ // 如果状态为空字符串,清空文件列表
35
+ fileList.value = []
36
+ return
37
+ }
38
+
39
+ const lines = statusText.split('\n')
40
+ const files: {path: string, type: string}[] = []
41
+ for (const line of lines) {
42
+ // 匹配常见的 git status --porcelain 格式
43
+ // M: 修改, A: 新增, D: 删除, ??: 未跟踪
44
+ const match = line.match(/^([ MADRCU\?]{2})\s+(.+)$/)
45
+ if (match) {
46
+ let type = ''
47
+ const code = match[1].trim()
48
+ if (code === 'M' || code === 'MM' || code === 'AM' || code === 'RM') type = 'modified'
49
+ else if (code === 'A' || code === 'AA') type = 'added'
50
+ else if (code === 'D' || code === 'AD' || code === 'DA') type = 'deleted'
51
+ else if (code === '??') type = 'untracked'
52
+ else type = 'other'
53
+ files.push({ path: match[2], type })
54
+ }
55
+ }
56
+ fileList.value = files
57
+ }
58
+
22
59
  // 获取提交日志
23
60
  async function fetchLog() {
24
61
  // 检查是否是Git仓库
@@ -32,8 +69,8 @@ export const useGitLogStore = defineStore('gitLog', () => {
32
69
  console.log('开始加载提交历史...')
33
70
  const response = await fetch('/api/log')
34
71
  const data = await response.json()
35
- if (data.log && Array.isArray(data.log)) {
36
- log.value = data.log
72
+ if (data && Array.isArray(data)) {
73
+ log.value = data
37
74
  }
38
75
  console.log(`提交历史加载完成,共 ${log.value.length} 条记录`)
39
76
  } catch (error) {
@@ -60,12 +97,17 @@ export const useGitLogStore = defineStore('gitLog', () => {
60
97
  const response = await fetch('/api/status')
61
98
  const data = await response.json()
62
99
  if (data.status) {
100
+ // 更新状态文本
101
+ statusText.value = data.status
63
102
  status.value = {
64
103
  staged: data.status.staged || [],
65
104
  unstaged: data.status.unstaged || [],
66
105
  untracked: data.status.untracked || []
67
106
  }
68
107
  }
108
+
109
+ // 同时获取porcelain格式的状态
110
+ await fetchStatusPorcelain()
69
111
  } catch (error) {
70
112
  console.error('获取Git状态失败:', error)
71
113
  ElMessage({
@@ -77,6 +119,34 @@ export const useGitLogStore = defineStore('gitLog', () => {
77
119
  }
78
120
  }
79
121
 
122
+ // 获取Git状态 (porcelain格式)
123
+ async function fetchStatusPorcelain() {
124
+ // 检查是否是Git仓库
125
+ if (!gitStore.isGitRepo) {
126
+ console.log('当前目录不是Git仓库,跳过加载Git状态')
127
+ return
128
+ }
129
+
130
+ try {
131
+ const response = await fetch('/api/status_porcelain')
132
+ const data = await response.json()
133
+ if (data.status !== undefined) {
134
+ parseStatusPorcelain(data.status)
135
+ } else {
136
+ // 如果没有收到有效的 status 字段,清空文件列表
137
+ fileList.value = []
138
+ }
139
+ } catch (error) {
140
+ console.error('获取Git状态(porcelain)失败:', error)
141
+ ElMessage({
142
+ message: `获取Git状态(porcelain)失败: ${(error as Error).message}`,
143
+ type: 'error'
144
+ })
145
+ // 清空文件列表
146
+ fileList.value = []
147
+ }
148
+ }
149
+
80
150
  // 添加文件到暂存区 (git add .)
81
151
  async function addToStage() {
82
152
  // 检查是否是Git仓库
@@ -120,6 +190,11 @@ export const useGitLogStore = defineStore('gitLog', () => {
120
190
  }
121
191
  }
122
192
 
193
+ // 添加延时函数
194
+ function delay(ms: number) {
195
+ return new Promise(resolve => setTimeout(resolve, ms))
196
+ }
197
+
123
198
  // 提交更改
124
199
  async function commitChanges(message: string, noVerify = false) {
125
200
  // 检查是否是Git仓库
@@ -129,6 +204,7 @@ export const useGitLogStore = defineStore('gitLog', () => {
129
204
  }
130
205
 
131
206
  try {
207
+ isCommiting.value = true
132
208
  const response = await fetch('/api/commit', {
133
209
  method: 'POST',
134
210
  headers: {
@@ -166,6 +242,8 @@ export const useGitLogStore = defineStore('gitLog', () => {
166
242
  type: 'error'
167
243
  })
168
244
  return false
245
+ } finally {
246
+ isCommiting.value = false
169
247
  }
170
248
  }
171
249
 
@@ -178,6 +256,7 @@ export const useGitLogStore = defineStore('gitLog', () => {
178
256
  }
179
257
 
180
258
  try {
259
+ isPushing.value = true
181
260
  const response = await fetch('/api/push', {
182
261
  method: 'POST'
183
262
  })
@@ -188,6 +267,8 @@ export const useGitLogStore = defineStore('gitLog', () => {
188
267
  message: '推送成功',
189
268
  type: 'success'
190
269
  })
270
+ // 刷新状态
271
+ fetchStatus()
191
272
 
192
273
  // 刷新日志
193
274
  fetchLog()
@@ -206,54 +287,57 @@ export const useGitLogStore = defineStore('gitLog', () => {
206
287
  type: 'error'
207
288
  })
208
289
  return false
290
+ } finally {
291
+ isPushing.value = false
209
292
  }
210
293
  }
211
294
 
212
295
  // 暂存并提交
213
296
  async function addAndCommit(message: string, noVerify = false) {
214
- try {
215
- // 先添加到暂存区
216
- const addResult = await addToStage()
217
- if (!addResult) {
218
- return false
219
- }
220
-
221
- // 再提交
222
- return await commitChanges(message, noVerify)
223
- } catch (error) {
224
- ElMessage({
225
- message: `暂存并提交失败: ${(error as Error).message}`,
226
- type: 'error'
227
- })
228
- return false
229
- }
297
+ const addResult = await addToStage()
298
+ if (!addResult) return false
299
+
300
+ // 使用新的延时常量
301
+ await delay(GIT_OPERATION_DELAY)
302
+
303
+ return await commitChanges(message, noVerify)
230
304
  }
231
305
 
232
306
  // 暂存、提交并推送
233
307
  async function addCommitAndPush(message: string, noVerify = false) {
234
308
  try {
235
- // 先添加并提交
236
- const commitResult = await addAndCommit(message, noVerify)
237
- if (!commitResult) {
238
- return false
239
- }
309
+ const addResult = await addToStage()
310
+ if (!addResult) return false
240
311
 
241
- // 再推送
242
- const pushResult = await pushToRemote()
312
+ // 使用新的延时常量
313
+ await delay(GIT_OPERATION_DELAY)
243
314
 
244
- // 推送成功后,确保刷新日志
245
- if (pushResult) {
246
- // 添加延迟以确保服务器处理完成
247
- setTimeout(() => {
248
- console.log('刷新提交历史...')
249
- fetchLog()
250
- }, 300)
251
- }
315
+ const commitResult = await commitChanges(message, noVerify)
316
+ if (!commitResult) return false
317
+
318
+ // 使用新的延时常量
319
+ await delay(GIT_OPERATION_DELAY)
252
320
 
253
- return pushResult
321
+ return await pushToRemote()
254
322
  } catch (error) {
323
+ // 如果发生错误,尝试删除 index.lock 文件
324
+ try {
325
+ const response = await fetch('/api/remove-lock', {
326
+ method: 'POST'
327
+ })
328
+ const result = await response.json()
329
+ if (result.success) {
330
+ ElMessage({
331
+ message: '已清理锁定文件,请重试操作',
332
+ type: 'warning'
333
+ })
334
+ }
335
+ } catch (e) {
336
+ console.error('清理锁定文件失败:', e)
337
+ }
338
+
255
339
  ElMessage({
256
- message: `暂存、提交并推送失败: ${(error as Error).message}`,
340
+ message: `操作失败: ${(error as Error).message}`,
257
341
  type: 'error'
258
342
  })
259
343
  return false
@@ -351,114 +435,30 @@ export const useGitLogStore = defineStore('gitLog', () => {
351
435
  }
352
436
  }
353
437
 
354
- // 暂存文件
355
- async function stageFiles(files: string[]) {
356
- // 检查是否是Git仓库
357
- if (!gitStore.isGitRepo) {
358
- ElMessage.warning('当前目录不是Git仓库')
359
- return false
360
- }
361
-
362
- try {
363
- const response = await fetch('/api/stage', {
364
- method: 'POST',
365
- headers: {
366
- 'Content-Type': 'application/json'
367
- },
368
- body: JSON.stringify({ files })
369
- })
370
-
371
- const result = await response.json()
372
- if (result.success) {
373
- ElMessage({
374
- message: '暂存文件成功',
375
- type: 'success'
376
- })
377
-
378
- // 刷新状态
379
- fetchStatus()
380
-
381
- return true
382
- } else {
383
- ElMessage({
384
- message: `暂存文件失败: ${result.error}`,
385
- type: 'error'
386
- })
387
- return false
388
- }
389
- } catch (error) {
390
- ElMessage({
391
- message: `暂存文件失败: ${(error as Error).message}`,
392
- type: 'error'
393
- })
394
- return false
395
- }
396
- }
397
-
398
- // 取消暂存文件
399
- async function unstageFiles(files: string[]) {
400
- // 检查是否是Git仓库
401
- if (!gitStore.isGitRepo) {
402
- ElMessage.warning('当前目录不是Git仓库')
403
- return false
404
- }
405
-
406
- try {
407
- const response = await fetch('/api/unstage', {
408
- method: 'POST',
409
- headers: {
410
- 'Content-Type': 'application/json'
411
- },
412
- body: JSON.stringify({ files })
413
- })
414
-
415
- const result = await response.json()
416
- if (result.success) {
417
- ElMessage({
418
- message: '取消暂存成功',
419
- type: 'success'
420
- })
421
-
422
- // 刷新状态
423
- fetchStatus()
424
-
425
- return true
426
- } else {
427
- ElMessage({
428
- message: `取消暂存失败: ${result.error}`,
429
- type: 'error'
430
- })
431
- return false
432
- }
433
- } catch (error) {
434
- ElMessage({
435
- message: `取消暂存失败: ${(error as Error).message}`,
436
- type: 'error'
437
- })
438
- return false
439
- }
440
- }
441
-
442
438
  return {
443
439
  // 状态
444
440
  log,
445
441
  status,
442
+ statusText,
443
+ fileList,
446
444
  isLoadingLog,
447
445
  isLoadingStatus,
448
446
  isAddingFiles,
449
447
  isResetting,
448
+ isCommiting,
449
+ isPushing,
450
450
 
451
451
  // 方法
452
452
  fetchLog,
453
453
  fetchStatus,
454
+ fetchStatusPorcelain,
455
+ parseStatusPorcelain,
454
456
  addToStage,
455
457
  commitChanges,
456
458
  pushToRemote,
457
459
  addAndCommit,
458
460
  addCommitAndPush,
459
461
  resetHead,
460
- resetToRemote,
461
- stageFiles,
462
- unstageFiles
462
+ resetToRemote
463
463
  }
464
464
  })