zen-gitsync 2.1.2 → 2.1.6
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/LICENSE +21 -21
- package/README.md +96 -96
- package/index.js +2 -2
- package/package.json +69 -66
- package/src/config.js +51 -51
- package/src/gitCommit.js +261 -261
- package/src/ui/public/assets/index-8gQo1ABk.js +20 -0
- package/src/ui/public/assets/index-IcGOG2Ja.css +1 -0
- package/src/ui/public/assets/{vendor-Dy1zosHw.js → vendor-Bm8yNvvz.js} +1 -1
- package/src/ui/public/favicon.svg +26 -26
- package/src/ui/public/index.html +3 -3
- package/src/ui/public/logo.svg +26 -26
- package/src/ui/server/index.js +141 -51
- package/src/ui/client/README.md +0 -5
- package/src/ui/client/auto-imports.d.ts +0 -10
- package/src/ui/client/components.d.ts +0 -33
- package/src/ui/client/index.html +0 -13
- package/src/ui/client/package.json +0 -28
- package/src/ui/client/public/favicon.svg +0 -27
- package/src/ui/client/public/logo.svg +0 -27
- package/src/ui/client/public/vite.svg +0 -1
- package/src/ui/client/src/App.vue +0 -984
- package/src/ui/client/src/assets/logo.svg +0 -27
- package/src/ui/client/src/components/CommitForm.vue +0 -2167
- package/src/ui/client/src/components/GitStatus.vue +0 -1621
- package/src/ui/client/src/components/LogList.vue +0 -1937
- package/src/ui/client/src/main.ts +0 -7
- package/src/ui/client/src/stores/configStore.ts +0 -212
- package/src/ui/client/src/stores/gitLogStore.ts +0 -790
- package/src/ui/client/src/stores/gitStore.ts +0 -443
- package/src/ui/client/src/vite-env.d.ts +0 -1
- package/src/ui/client/stats.html +0 -4949
- package/src/ui/client/tsconfig.app.json +0 -14
- package/src/ui/client/tsconfig.json +0 -7
- package/src/ui/client/tsconfig.node.json +0 -24
- package/src/ui/client/vite.config.ts +0 -50
- package/src/ui/public/assets/index-C0FIVyIy.css +0 -1
- package/src/ui/public/assets/index-FuuBZ-mS.js +0 -20
|
@@ -1,1621 +0,0 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
import { ref, onMounted, computed, watch } from 'vue'
|
|
3
|
-
import { ElMessage, ElMessageBox } from 'element-plus'
|
|
4
|
-
// import { io } from 'socket.io-client'
|
|
5
|
-
import { Refresh, ArrowLeft, ArrowRight, Folder, Document, ArrowUp, RefreshRight, Check, Close, Download, Connection } 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
|
-
})
|
|
16
|
-
|
|
17
|
-
const gitLogStore = useGitLogStore()
|
|
18
|
-
const gitStore = useGitStore()
|
|
19
|
-
// 移除本地status定义,直接使用store中的statusText
|
|
20
|
-
// const status = ref('加载中...')
|
|
21
|
-
// const socket = io()
|
|
22
|
-
const isRefreshing = computed(() => gitLogStore.isLoadingStatus)
|
|
23
|
-
// 移除本地fileList定义,改用store中的fileList
|
|
24
|
-
const selectedFile = ref('')
|
|
25
|
-
const diffContent = ref('')
|
|
26
|
-
const diffDialogVisible = ref(false)
|
|
27
|
-
const isLoadingDiff = ref(false)
|
|
28
|
-
// 添加当前文件索引
|
|
29
|
-
const currentFileIndex = ref(-1)
|
|
30
|
-
// 添加切换目录相关的状态
|
|
31
|
-
const isDirectoryDialogVisible = ref(false)
|
|
32
|
-
const newDirectoryPath = ref('')
|
|
33
|
-
const isChangingDirectory = ref(false)
|
|
34
|
-
// 添加目录浏览相关的状态
|
|
35
|
-
const isDirectoryBrowserVisible = ref(false)
|
|
36
|
-
const currentBrowsePath = ref('')
|
|
37
|
-
const directoryItems = ref<{name: string, path: string, type: string}[]>([])
|
|
38
|
-
const isBrowsing = ref(false)
|
|
39
|
-
const browseErrorMessage = ref('')
|
|
40
|
-
|
|
41
|
-
// 添加git操作相关状态
|
|
42
|
-
const isGitPulling = ref(false)
|
|
43
|
-
const isGitFetching = ref(false)
|
|
44
|
-
|
|
45
|
-
const currentDirectory = ref(props.initialDirectory || '');
|
|
46
|
-
async function loadStatus() {
|
|
47
|
-
try {
|
|
48
|
-
// 如果没有初始目录,才需要请求当前目录
|
|
49
|
-
if (!currentDirectory.value) {
|
|
50
|
-
const responseDir = await fetch('/api/current_directory')
|
|
51
|
-
const dirData = await responseDir.json()
|
|
52
|
-
currentDirectory.value = dirData.directory || '未知目录'
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// 如果不是Git仓库,直接显示提示并返回
|
|
56
|
-
if (!gitStore.isGitRepo) {
|
|
57
|
-
return
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// 使用gitLogStore获取Git状态
|
|
61
|
-
await gitLogStore.fetchStatus()
|
|
62
|
-
|
|
63
|
-
// 同时刷新分支状态信息,确保显示最新的领先/落后提交数
|
|
64
|
-
await gitStore.getBranchStatus()
|
|
65
|
-
|
|
66
|
-
ElMessage({
|
|
67
|
-
message: 'Git 状态已刷新',
|
|
68
|
-
type: 'success',
|
|
69
|
-
})
|
|
70
|
-
} catch (error) {
|
|
71
|
-
ElMessage({
|
|
72
|
-
message: '刷新失败: ' + (error as Error).message,
|
|
73
|
-
type: 'error',
|
|
74
|
-
})
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// 格式化差异内容,添加颜色
|
|
79
|
-
function formatDiff(diffText: string) {
|
|
80
|
-
if (!diffText) return '';
|
|
81
|
-
|
|
82
|
-
// 将差异内容按行分割
|
|
83
|
-
const lines = diffText.split('\n');
|
|
84
|
-
|
|
85
|
-
// 转义 HTML 标签的函数
|
|
86
|
-
function escapeHtml(text: string) {
|
|
87
|
-
return text
|
|
88
|
-
.replace(/&/g, '&')
|
|
89
|
-
.replace(/</g, '<')
|
|
90
|
-
.replace(/>/g, '>')
|
|
91
|
-
.replace(/"/g, '"')
|
|
92
|
-
.replace(/'/g, ''');
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
// 为每行添加适当的 CSS 类
|
|
96
|
-
return lines.map(line => {
|
|
97
|
-
// 先转义 HTML 标签,再添加样式
|
|
98
|
-
const escapedLine = escapeHtml(line);
|
|
99
|
-
|
|
100
|
-
if (line.startsWith('diff --git')) {
|
|
101
|
-
return `<div class="diff-header">${escapedLine}</div>`;
|
|
102
|
-
} else if (line.startsWith('---')) {
|
|
103
|
-
return `<div class="diff-old-file">${escapedLine}</div>`;
|
|
104
|
-
} else if (line.startsWith('+++')) {
|
|
105
|
-
return `<div class="diff-new-file">${escapedLine}</div>`;
|
|
106
|
-
} else if (line.startsWith('@@')) {
|
|
107
|
-
return `<div class="diff-hunk-header">${escapedLine}</div>`;
|
|
108
|
-
} else if (line.startsWith('+')) {
|
|
109
|
-
return `<div class="diff-added">${escapedLine}</div>`;
|
|
110
|
-
} else if (line.startsWith('-')) {
|
|
111
|
-
return `<div class="diff-removed">${escapedLine}</div>`;
|
|
112
|
-
} else {
|
|
113
|
-
return `<div class="diff-context">${escapedLine}</div>`;
|
|
114
|
-
}
|
|
115
|
-
}).join('');
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
// 获取文件差异
|
|
119
|
-
async function getFileDiff(filePath: string) {
|
|
120
|
-
try {
|
|
121
|
-
isLoadingDiff.value = true
|
|
122
|
-
selectedFile.value = filePath
|
|
123
|
-
// 设置当前文件索引
|
|
124
|
-
currentFileIndex.value = gitLogStore.fileList.findIndex(file => file.path === filePath)
|
|
125
|
-
|
|
126
|
-
// 获取当前文件的状态类型
|
|
127
|
-
const currentFile = gitLogStore.fileList[currentFileIndex.value]
|
|
128
|
-
|
|
129
|
-
// 对未跟踪文件特殊处理
|
|
130
|
-
if (currentFile && currentFile.type === 'untracked') {
|
|
131
|
-
try {
|
|
132
|
-
// 获取未跟踪文件的内容
|
|
133
|
-
const response = await fetch(`/api/file-content?file=${encodeURIComponent(filePath)}`)
|
|
134
|
-
const data = await response.json()
|
|
135
|
-
|
|
136
|
-
if (data.success && data.content) {
|
|
137
|
-
// 构建一个类似diff的格式来显示新文件内容
|
|
138
|
-
diffContent.value = `diff --git a/${filePath} b/${filePath}\n` +
|
|
139
|
-
`新文件: ${filePath}\n` +
|
|
140
|
-
`--- /dev/null\n` +
|
|
141
|
-
`+++ b/${filePath}\n` +
|
|
142
|
-
`@@ -0,0 +1,${data.content.split('\n').length} @@\n` +
|
|
143
|
-
data.content.split('\n').map((line: string) => `+${line}`).join('\n')
|
|
144
|
-
} else {
|
|
145
|
-
diffContent.value = '这是一个新文件,尚未被Git跟踪。\n添加到暂存区后可以提交该文件。'
|
|
146
|
-
}
|
|
147
|
-
} catch (error) {
|
|
148
|
-
console.error('获取未跟踪文件内容失败:', error)
|
|
149
|
-
diffContent.value = '这是一个新文件,尚未被Git跟踪。\n添加到暂存区后可以提交该文件。'
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
diffDialogVisible.value = true
|
|
153
|
-
} else {
|
|
154
|
-
// 对于已跟踪的文件,获取常规差异
|
|
155
|
-
const response = await fetch(`/api/diff?file=${encodeURIComponent(filePath)}`)
|
|
156
|
-
const data = await response.json()
|
|
157
|
-
diffContent.value = data.diff || '没有变更'
|
|
158
|
-
diffDialogVisible.value = true
|
|
159
|
-
}
|
|
160
|
-
} catch (error) {
|
|
161
|
-
ElMessage({
|
|
162
|
-
message: '获取文件差异失败: ' + (error as Error).message,
|
|
163
|
-
type: 'error',
|
|
164
|
-
})
|
|
165
|
-
diffContent.value = '获取差异失败: ' + (error as Error).message
|
|
166
|
-
} finally {
|
|
167
|
-
isLoadingDiff.value = false
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
// 添加切换到上一个文件的方法
|
|
172
|
-
async function goToPreviousFile() {
|
|
173
|
-
if (gitLogStore.fileList.length === 0 || currentFileIndex.value <= 0) return
|
|
174
|
-
|
|
175
|
-
const newIndex = currentFileIndex.value - 1
|
|
176
|
-
const prevFile = gitLogStore.fileList[newIndex]
|
|
177
|
-
await getFileDiff(prevFile.path)
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
// 添加切换到下一个文件的方法
|
|
181
|
-
async function goToNextFile() {
|
|
182
|
-
if (gitLogStore.fileList.length === 0 || currentFileIndex.value >= gitLogStore.fileList.length - 1) return
|
|
183
|
-
|
|
184
|
-
const newIndex = currentFileIndex.value + 1
|
|
185
|
-
const nextFile = gitLogStore.fileList[newIndex]
|
|
186
|
-
await getFileDiff(nextFile.path)
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
// 打开切换目录对话框
|
|
190
|
-
function openDirectoryDialog() {
|
|
191
|
-
newDirectoryPath.value = currentDirectory.value
|
|
192
|
-
isDirectoryDialogVisible.value = true
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
// 打开目录浏览器
|
|
196
|
-
function openDirectoryBrowser() {
|
|
197
|
-
browseErrorMessage.value = ''
|
|
198
|
-
currentBrowsePath.value = newDirectoryPath.value || currentDirectory.value
|
|
199
|
-
isDirectoryBrowserVisible.value = true
|
|
200
|
-
browseDirectory(currentBrowsePath.value)
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
// 浏览目录
|
|
204
|
-
async function browseDirectory(directoryPath: string) {
|
|
205
|
-
try {
|
|
206
|
-
isBrowsing.value = true
|
|
207
|
-
browseErrorMessage.value = ''
|
|
208
|
-
|
|
209
|
-
// 确保Windows盘符路径格式正确
|
|
210
|
-
let normalizedPath = directoryPath
|
|
211
|
-
if (/^[A-Za-z]:$/.test(normalizedPath)) {
|
|
212
|
-
normalizedPath += '/'
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
const response = await fetch(`/api/browse_directory?path=${encodeURIComponent(normalizedPath)}`)
|
|
216
|
-
|
|
217
|
-
if (response.status === 403) {
|
|
218
|
-
const data = await response.json()
|
|
219
|
-
browseErrorMessage.value = data.error || '目录浏览功能未启用'
|
|
220
|
-
return
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
if (!response.ok) {
|
|
224
|
-
const data = await response.json()
|
|
225
|
-
browseErrorMessage.value = data.error || '获取目录内容失败'
|
|
226
|
-
return
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
const data = await response.json()
|
|
230
|
-
|
|
231
|
-
if (data.success) {
|
|
232
|
-
directoryItems.value = data.items
|
|
233
|
-
currentBrowsePath.value = data.currentPath
|
|
234
|
-
} else {
|
|
235
|
-
browseErrorMessage.value = data.error || '获取目录内容失败'
|
|
236
|
-
}
|
|
237
|
-
} catch (error) {
|
|
238
|
-
browseErrorMessage.value = `获取目录内容失败: ${(error as Error).message}`
|
|
239
|
-
} finally {
|
|
240
|
-
isBrowsing.value = false
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
// 导航到父目录
|
|
245
|
-
function navigateToParent() {
|
|
246
|
-
// 检查是否已经是根目录
|
|
247
|
-
// Windows盘符根目录情况 (如 "E:")
|
|
248
|
-
if (/^[A-Za-z]:$/.test(currentBrowsePath.value) ||
|
|
249
|
-
/^[A-Za-z]:[\\/]$/.test(currentBrowsePath.value) ||
|
|
250
|
-
currentBrowsePath.value === '/') {
|
|
251
|
-
// 已经是根目录,不做任何操作
|
|
252
|
-
return
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
// 获取当前路径的父目录
|
|
256
|
-
let pathParts = currentBrowsePath.value.split(/[/\\]/)
|
|
257
|
-
|
|
258
|
-
// 移除最后一个目录部分
|
|
259
|
-
pathParts.pop()
|
|
260
|
-
|
|
261
|
-
// 处理Windows盘符特殊情况
|
|
262
|
-
let parentPath = pathParts.join('/')
|
|
263
|
-
if (pathParts.length === 1 && /^[A-Za-z]:$/.test(pathParts[0])) {
|
|
264
|
-
// 如果只剩下盘符,确保添加斜杠 (例如 "E:/")
|
|
265
|
-
parentPath = pathParts[0] + '/'
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
if (parentPath) {
|
|
269
|
-
browseDirectory(parentPath)
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
// 选择目录项
|
|
274
|
-
function selectDirectoryItem(item: {name: string, path: string, type: string}) {
|
|
275
|
-
if (item.type === 'directory') {
|
|
276
|
-
browseDirectory(item.path)
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
// 选择当前目录
|
|
281
|
-
function selectCurrentDirectory() {
|
|
282
|
-
newDirectoryPath.value = currentBrowsePath.value
|
|
283
|
-
isDirectoryBrowserVisible.value = false
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
// 切换工作目录
|
|
287
|
-
async function changeDirectory() {
|
|
288
|
-
if (!newDirectoryPath.value) {
|
|
289
|
-
ElMessage.warning('目录路径不能为空')
|
|
290
|
-
return
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
try {
|
|
294
|
-
isChangingDirectory.value = true
|
|
295
|
-
const response = await fetch('/api/change_directory', {
|
|
296
|
-
method: 'POST',
|
|
297
|
-
headers: {
|
|
298
|
-
'Content-Type': 'application/json'
|
|
299
|
-
},
|
|
300
|
-
body: JSON.stringify({ path: newDirectoryPath.value })
|
|
301
|
-
})
|
|
302
|
-
|
|
303
|
-
const result = await response.json()
|
|
304
|
-
|
|
305
|
-
if (result.success) {
|
|
306
|
-
ElMessage.success('已切换工作目录')
|
|
307
|
-
currentDirectory.value = result.directory
|
|
308
|
-
isDirectoryDialogVisible.value = false
|
|
309
|
-
|
|
310
|
-
// 直接使用API返回的Git仓库状态
|
|
311
|
-
gitStore.isGitRepo = result.isGitRepo
|
|
312
|
-
|
|
313
|
-
// 如果是Git仓库,加载Git相关数据
|
|
314
|
-
if (result.isGitRepo) {
|
|
315
|
-
// 加载Git分支和用户信息
|
|
316
|
-
await Promise.all([
|
|
317
|
-
gitStore.getCurrentBranch(),
|
|
318
|
-
gitStore.getAllBranches(),
|
|
319
|
-
gitStore.getUserInfo()
|
|
320
|
-
])
|
|
321
|
-
|
|
322
|
-
// 刷新Git状态
|
|
323
|
-
await loadStatus()
|
|
324
|
-
} else {
|
|
325
|
-
ElMessage.warning('当前目录不是一个Git仓库')
|
|
326
|
-
// 清空Git相关状态
|
|
327
|
-
gitStore.$reset() // 使用pinia的reset方法重置状态
|
|
328
|
-
}
|
|
329
|
-
} else {
|
|
330
|
-
ElMessage.error(result.error || '切换目录失败')
|
|
331
|
-
}
|
|
332
|
-
} catch (error) {
|
|
333
|
-
ElMessage.error(`切换目录失败: ${(error as Error).message}`)
|
|
334
|
-
} finally {
|
|
335
|
-
isChangingDirectory.value = false
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
// 处理文件点击
|
|
340
|
-
function handleFileClick(file: {path: string, type: string}) {
|
|
341
|
-
getFileDiff(file.path)
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
// 暂存单个文件
|
|
345
|
-
async function stageFile(filePath: string) {
|
|
346
|
-
await gitLogStore.addFileToStage(filePath)
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
// 取消暂存单个文件
|
|
350
|
-
async function unstageFile(filePath: string) {
|
|
351
|
-
await gitLogStore.unstageFile(filePath)
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
// 刷新Git状态的方法
|
|
355
|
-
async function refreshStatus() {
|
|
356
|
-
await loadStatus()
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
// 添加git pull操作方法
|
|
360
|
-
async function handleGitPull() {
|
|
361
|
-
isGitPulling.value = true
|
|
362
|
-
try {
|
|
363
|
-
await gitStore.gitPull()
|
|
364
|
-
// 刷新Git状态
|
|
365
|
-
await loadStatus()
|
|
366
|
-
} finally {
|
|
367
|
-
isGitPulling.value = false
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
// 添加git fetch --all操作方法
|
|
372
|
-
async function handleGitFetchAll() {
|
|
373
|
-
isGitFetching.value = true
|
|
374
|
-
try {
|
|
375
|
-
await gitStore.gitFetchAll()
|
|
376
|
-
// 刷新Git状态
|
|
377
|
-
await loadStatus()
|
|
378
|
-
} finally {
|
|
379
|
-
isGitFetching.value = false
|
|
380
|
-
}
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
// 添加撤回文件修改的方法
|
|
384
|
-
async function revertFileChanges(filePath: string) {
|
|
385
|
-
try {
|
|
386
|
-
// 请求用户确认
|
|
387
|
-
await ElMessageBox.confirm(
|
|
388
|
-
`确定要撤回文件 "${filePath}" 的所有修改吗?此操作无法撤销。`,
|
|
389
|
-
'撤回修改',
|
|
390
|
-
{
|
|
391
|
-
confirmButtonText: '确定',
|
|
392
|
-
cancelButtonText: '取消',
|
|
393
|
-
type: 'warning'
|
|
394
|
-
}
|
|
395
|
-
)
|
|
396
|
-
|
|
397
|
-
// 发送请求到后端API
|
|
398
|
-
const response = await fetch('/api/revert_file', {
|
|
399
|
-
method: 'POST',
|
|
400
|
-
headers: {
|
|
401
|
-
'Content-Type': 'application/json'
|
|
402
|
-
},
|
|
403
|
-
body: JSON.stringify({ filePath })
|
|
404
|
-
})
|
|
405
|
-
|
|
406
|
-
const result = await response.json()
|
|
407
|
-
|
|
408
|
-
if (result.success) {
|
|
409
|
-
ElMessage.success('已撤回文件修改')
|
|
410
|
-
// 刷新Git状态
|
|
411
|
-
await loadStatus()
|
|
412
|
-
} else {
|
|
413
|
-
// 使用自定义错误信息,避免显示undefined
|
|
414
|
-
ElMessage.error(result.error ? `撤回失败: ${result.error}` : '撤回文件修改失败,请重试')
|
|
415
|
-
}
|
|
416
|
-
} catch (error) {
|
|
417
|
-
// 用户取消操作不显示错误
|
|
418
|
-
if ((error as any) === 'cancel' || (error as Error).message === 'cancel') {
|
|
419
|
-
// 用户取消操作,不做任何处理,也不显示错误
|
|
420
|
-
return
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
// 其他错误情况才显示错误消息
|
|
424
|
-
// 避免显示undefined错误信息
|
|
425
|
-
const errorMessage = (error as Error).message || '未知错误';
|
|
426
|
-
if (errorMessage !== 'undefined') {
|
|
427
|
-
ElMessage.error(`撤回文件修改失败: ${errorMessage}`)
|
|
428
|
-
} else {
|
|
429
|
-
ElMessage.error('撤回文件修改失败,请重试')
|
|
430
|
-
}
|
|
431
|
-
}
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
// 提取文件名和目录
|
|
435
|
-
function getFileName(path: string): string {
|
|
436
|
-
const parts = path.split('/')
|
|
437
|
-
return parts[parts.length - 1]
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
function getFileDirectory(path: string): string {
|
|
441
|
-
const parts = path.split('/')
|
|
442
|
-
if (parts.length <= 1) return ''
|
|
443
|
-
|
|
444
|
-
// 保留所有除最后一个部分的路径
|
|
445
|
-
return parts.slice(0, -1).join('/')
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
onMounted(() => {
|
|
449
|
-
// App.vue已经加载了Git相关数据,此时只需加载状态
|
|
450
|
-
loadStatus()
|
|
451
|
-
})
|
|
452
|
-
|
|
453
|
-
// 监听autoUpdateEnabled的变化,手动调用toggleAutoUpdate
|
|
454
|
-
watch(() => gitLogStore.autoUpdateEnabled, (newValue, oldValue) => {
|
|
455
|
-
console.log(`自动更新状态变更: ${oldValue} -> ${newValue}`)
|
|
456
|
-
// 调用store中的方法来实现服务器通信功能
|
|
457
|
-
gitLogStore.toggleAutoUpdate()
|
|
458
|
-
}, { immediate: false })
|
|
459
|
-
|
|
460
|
-
// onUnmounted(() => {
|
|
461
|
-
// socket.disconnect()
|
|
462
|
-
// })
|
|
463
|
-
// 暴露刷新方法给父组件
|
|
464
|
-
defineExpose({
|
|
465
|
-
refreshStatus
|
|
466
|
-
})
|
|
467
|
-
</script>
|
|
468
|
-
|
|
469
|
-
<template>
|
|
470
|
-
<div class="card">
|
|
471
|
-
<div class="status-header">
|
|
472
|
-
<h2>Git 状态</h2>
|
|
473
|
-
<div class="header-actions">
|
|
474
|
-
<el-tooltip
|
|
475
|
-
:content="gitLogStore.autoUpdateEnabled ? '禁用自动更新文件状态' : '启用自动更新文件状态'"
|
|
476
|
-
placement="top"
|
|
477
|
-
:hide-after="1000"
|
|
478
|
-
>
|
|
479
|
-
<el-switch
|
|
480
|
-
v-model="gitLogStore.autoUpdateEnabled"
|
|
481
|
-
style="--el-switch-on-color: #67C23A; --el-switch-off-color: #909399; margin-right: 10px;"
|
|
482
|
-
inline-prompt
|
|
483
|
-
:active-icon="Check"
|
|
484
|
-
:inactive-icon="Close"
|
|
485
|
-
class="auto-update-switch"
|
|
486
|
-
/>
|
|
487
|
-
</el-tooltip>
|
|
488
|
-
|
|
489
|
-
<!-- 添加Git Pull按钮 -->
|
|
490
|
-
<el-tooltip content="Git Pull (拉取远程更新)" placement="top" :hide-after="1000">
|
|
491
|
-
<el-button
|
|
492
|
-
type="primary"
|
|
493
|
-
:icon="Download"
|
|
494
|
-
circle
|
|
495
|
-
size="small"
|
|
496
|
-
@click="handleGitPull"
|
|
497
|
-
:loading="isGitPulling"
|
|
498
|
-
:disabled="!gitStore.hasUpstream"
|
|
499
|
-
/>
|
|
500
|
-
</el-tooltip>
|
|
501
|
-
|
|
502
|
-
<!-- 添加Git Fetch All按钮 -->
|
|
503
|
-
<el-tooltip content="Git Fetch All (获取所有远程分支)" placement="top" :hide-after="1000">
|
|
504
|
-
<el-button
|
|
505
|
-
type="info"
|
|
506
|
-
:icon="Connection"
|
|
507
|
-
circle
|
|
508
|
-
size="small"
|
|
509
|
-
@click="handleGitFetchAll"
|
|
510
|
-
:loading="isGitFetching"
|
|
511
|
-
/>
|
|
512
|
-
</el-tooltip>
|
|
513
|
-
|
|
514
|
-
<el-tooltip content="刷新状态" placement="top" :hide-after="1000">
|
|
515
|
-
<el-button
|
|
516
|
-
type="primary"
|
|
517
|
-
:icon="Refresh"
|
|
518
|
-
circle
|
|
519
|
-
size="small"
|
|
520
|
-
@click="refreshStatus"
|
|
521
|
-
:loading="isRefreshing"
|
|
522
|
-
/>
|
|
523
|
-
</el-tooltip>
|
|
524
|
-
</div>
|
|
525
|
-
</div>
|
|
526
|
-
|
|
527
|
-
<div class="card-content">
|
|
528
|
-
<div class="current-directory">
|
|
529
|
-
<el-icon><Folder /></el-icon>
|
|
530
|
-
<span>{{ currentDirectory }}</span>
|
|
531
|
-
<el-button type="primary" size="small" @click="openDirectoryDialog" plain>
|
|
532
|
-
切换目录
|
|
533
|
-
</el-button>
|
|
534
|
-
</div>
|
|
535
|
-
|
|
536
|
-
<div v-if="!gitStore.isGitRepo" class="status-box">
|
|
537
|
-
<div class="empty-status">
|
|
538
|
-
<p>当前目录不是Git仓库</p>
|
|
539
|
-
<el-button type="primary" size="small" @click="openDirectoryDialog">
|
|
540
|
-
切换目录
|
|
541
|
-
</el-button>
|
|
542
|
-
</div>
|
|
543
|
-
</div>
|
|
544
|
-
|
|
545
|
-
<div v-else>
|
|
546
|
-
<!-- 分支同步状态信息 -->
|
|
547
|
-
<div v-if="gitStore.hasUpstream && (gitStore.branchAhead > 0 || gitStore.branchBehind > 0)" class="branch-sync-status">
|
|
548
|
-
<div class="sync-status-content">
|
|
549
|
-
<el-tooltip content="本地分支与远程分支的状态对比" placement="top">
|
|
550
|
-
<div class="status-badges">
|
|
551
|
-
<el-tag v-if="gitStore.branchAhead > 0" size="small" type="warning" class="status-badge">
|
|
552
|
-
<template #default>
|
|
553
|
-
<span class="badge-content">
|
|
554
|
-
<el-icon><ArrowUp /></el-icon> 你的分支领先 'origin/{{ gitStore.currentBranch }}' {{ gitStore.branchAhead }} 个提交
|
|
555
|
-
</span>
|
|
556
|
-
</template>
|
|
557
|
-
</el-tag>
|
|
558
|
-
<el-tag v-if="gitStore.branchBehind > 0" size="small" type="info" class="status-badge">
|
|
559
|
-
<template #default>
|
|
560
|
-
<span class="badge-content">
|
|
561
|
-
<el-icon><ArrowDown /></el-icon> 你的分支落后 'origin/{{ gitStore.currentBranch }}' {{ gitStore.branchBehind }} 个提交
|
|
562
|
-
</span>
|
|
563
|
-
</template>
|
|
564
|
-
</el-tag>
|
|
565
|
-
</div>
|
|
566
|
-
</el-tooltip>
|
|
567
|
-
</div>
|
|
568
|
-
</div>
|
|
569
|
-
|
|
570
|
-
<!-- 默认状态信息 -->
|
|
571
|
-
<div v-if="!gitStore.hasUpstream || (gitStore.branchAhead === 0 && gitStore.branchBehind === 0)" class="git-status-message">
|
|
572
|
-
<p>当前工作在 <el-tag size="small" type="success">{{ gitStore.currentBranch }}</el-tag> 分支</p>
|
|
573
|
-
</div>
|
|
574
|
-
|
|
575
|
-
<!-- 现代化、简洁的文件列表 -->
|
|
576
|
-
<div v-if="gitLogStore.fileList.length" class="file-list-container">
|
|
577
|
-
<!-- 分组显示文件 -->
|
|
578
|
-
<div v-if="gitLogStore.fileList.some(f => f.type === 'added')" class="file-group">
|
|
579
|
-
<div class="file-group-header">已暂存的更改</div>
|
|
580
|
-
<div class="file-list">
|
|
581
|
-
<div
|
|
582
|
-
v-for="file in gitLogStore.fileList.filter(f => f.type === 'added')"
|
|
583
|
-
:key="file.path"
|
|
584
|
-
class="file-item"
|
|
585
|
-
@click="handleFileClick(file)"
|
|
586
|
-
>
|
|
587
|
-
<div class="file-info">
|
|
588
|
-
<div class="file-path-container">
|
|
589
|
-
<span class="file-name">{{ getFileName(file.path) }}</span>
|
|
590
|
-
<span class="file-directory">{{ getFileDirectory(file.path) }}</span>
|
|
591
|
-
</div>
|
|
592
|
-
</div>
|
|
593
|
-
<div class="file-actions">
|
|
594
|
-
<el-tooltip content="取消暂存" placement="top" :hide-after="1000">
|
|
595
|
-
<el-button
|
|
596
|
-
type="warning"
|
|
597
|
-
size="small"
|
|
598
|
-
circle
|
|
599
|
-
@click.stop="unstageFile(file.path)"
|
|
600
|
-
>-</el-button>
|
|
601
|
-
</el-tooltip>
|
|
602
|
-
</div>
|
|
603
|
-
</div>
|
|
604
|
-
</div>
|
|
605
|
-
</div>
|
|
606
|
-
|
|
607
|
-
<div v-if="gitLogStore.fileList.some(f => f.type === 'modified' || f.type === 'deleted')" class="file-group">
|
|
608
|
-
<div class="file-group-header">未暂存的更改</div>
|
|
609
|
-
<div class="file-list">
|
|
610
|
-
<div
|
|
611
|
-
v-for="file in gitLogStore.fileList.filter(f => f.type === 'modified' || f.type === 'deleted')"
|
|
612
|
-
:key="file.path"
|
|
613
|
-
class="file-item"
|
|
614
|
-
@click="handleFileClick(file)"
|
|
615
|
-
>
|
|
616
|
-
<div class="file-info">
|
|
617
|
-
<div class="file-status-indicator" :class="file.type"></div>
|
|
618
|
-
<div class="file-path-container">
|
|
619
|
-
<span class="file-name">{{ getFileName(file.path) }}</span>
|
|
620
|
-
<span class="file-directory">{{ getFileDirectory(file.path) }}</span>
|
|
621
|
-
</div>
|
|
622
|
-
</div>
|
|
623
|
-
<div class="file-actions">
|
|
624
|
-
<el-tooltip content="添加到暂存区" placement="top" :hide-after="1000">
|
|
625
|
-
<el-button
|
|
626
|
-
type="success"
|
|
627
|
-
size="small"
|
|
628
|
-
circle
|
|
629
|
-
@click.stop="stageFile(file.path)"
|
|
630
|
-
>+</el-button>
|
|
631
|
-
</el-tooltip>
|
|
632
|
-
<el-tooltip content="撤回修改" placement="top" :hide-after="1000">
|
|
633
|
-
<el-button
|
|
634
|
-
type="danger"
|
|
635
|
-
size="small"
|
|
636
|
-
:icon="RefreshRight"
|
|
637
|
-
circle
|
|
638
|
-
@click.stop="revertFileChanges(file.path)"
|
|
639
|
-
/>
|
|
640
|
-
</el-tooltip>
|
|
641
|
-
</div>
|
|
642
|
-
</div>
|
|
643
|
-
</div>
|
|
644
|
-
</div>
|
|
645
|
-
|
|
646
|
-
<div v-if="gitLogStore.fileList.some(f => f.type === 'untracked')" class="file-group">
|
|
647
|
-
<div class="file-group-header">未跟踪的文件</div>
|
|
648
|
-
<div class="file-list">
|
|
649
|
-
<div
|
|
650
|
-
v-for="file in gitLogStore.fileList.filter(f => f.type === 'untracked')"
|
|
651
|
-
:key="file.path"
|
|
652
|
-
class="file-item"
|
|
653
|
-
@click="handleFileClick(file)"
|
|
654
|
-
>
|
|
655
|
-
<div class="file-info">
|
|
656
|
-
<div class="file-status-indicator untracked"></div>
|
|
657
|
-
<div class="file-path-container">
|
|
658
|
-
<span class="file-name">{{ getFileName(file.path) }}</span>
|
|
659
|
-
<span class="file-directory">{{ getFileDirectory(file.path) }}</span>
|
|
660
|
-
</div>
|
|
661
|
-
</div>
|
|
662
|
-
<div class="file-actions">
|
|
663
|
-
<el-tooltip content="添加到暂存区" placement="top" :hide-after="1000">
|
|
664
|
-
<el-button
|
|
665
|
-
type="success"
|
|
666
|
-
size="small"
|
|
667
|
-
circle
|
|
668
|
-
@click.stop="stageFile(file.path)"
|
|
669
|
-
>+</el-button>
|
|
670
|
-
</el-tooltip>
|
|
671
|
-
<el-tooltip content="删除文件" placement="top" :hide-after="1000">
|
|
672
|
-
<el-button
|
|
673
|
-
type="danger"
|
|
674
|
-
size="small"
|
|
675
|
-
:icon="Close"
|
|
676
|
-
circle
|
|
677
|
-
@click.stop="revertFileChanges(file.path)"
|
|
678
|
-
/>
|
|
679
|
-
</el-tooltip>
|
|
680
|
-
</div>
|
|
681
|
-
</div>
|
|
682
|
-
</div>
|
|
683
|
-
</div>
|
|
684
|
-
</div>
|
|
685
|
-
|
|
686
|
-
<div v-else-if="gitStore.isGitRepo" class="empty-status">
|
|
687
|
-
<div class="empty-icon">
|
|
688
|
-
<el-icon><Document /></el-icon>
|
|
689
|
-
</div>
|
|
690
|
-
<div class="empty-text">没有检测到任何更改</div>
|
|
691
|
-
<div class="empty-subtext">工作区是干净的</div>
|
|
692
|
-
|
|
693
|
-
<!-- 添加分支信息 -->
|
|
694
|
-
<div class="branch-info">
|
|
695
|
-
<p>当前工作在 <el-tag size="small" type="success">{{ gitStore.currentBranch }}</el-tag> 分支</p>
|
|
696
|
-
|
|
697
|
-
<!-- 显示分支同步状态 -->
|
|
698
|
-
<div v-if="gitStore.hasUpstream">
|
|
699
|
-
<span v-if="gitStore.branchAhead > 0" class="branch-sync-info warning">
|
|
700
|
-
<el-icon><ArrowUp /></el-icon> 你的分支领先 'origin/{{ gitStore.currentBranch }}' {{ gitStore.branchAhead }} 个提交
|
|
701
|
-
</span>
|
|
702
|
-
<span v-else-if="gitStore.branchBehind > 0" class="branch-sync-info info">
|
|
703
|
-
<el-icon><ArrowDown /></el-icon> 你的分支落后 'origin/{{ gitStore.currentBranch }}' {{ gitStore.branchBehind }} 个提交
|
|
704
|
-
</span>
|
|
705
|
-
<span v-else class="branch-sync-info success">
|
|
706
|
-
<el-icon><Check /></el-icon> 你的分支与 'origin/{{ gitStore.currentBranch }}' 同步
|
|
707
|
-
</span>
|
|
708
|
-
</div>
|
|
709
|
-
</div>
|
|
710
|
-
</div>
|
|
711
|
-
</div>
|
|
712
|
-
</div>
|
|
713
|
-
|
|
714
|
-
<!-- 切换目录对话框 -->
|
|
715
|
-
<el-dialog
|
|
716
|
-
v-model="isDirectoryDialogVisible"
|
|
717
|
-
title="切换工作目录"
|
|
718
|
-
width="80vw"
|
|
719
|
-
top="10vh"
|
|
720
|
-
style="height: 200px;"
|
|
721
|
-
>
|
|
722
|
-
<el-form>
|
|
723
|
-
<el-form-item label="目录路径">
|
|
724
|
-
<el-input v-model="newDirectoryPath" placeholder="请输入目录路径" clearable />
|
|
725
|
-
<div class="directory-buttons">
|
|
726
|
-
<el-button @click="openDirectoryBrowser" type="primary" plain class="no-padding-left">
|
|
727
|
-
<el-icon><Folder /></el-icon>
|
|
728
|
-
浏览
|
|
729
|
-
</el-button>
|
|
730
|
-
<el-button @click="changeDirectory" :loading="isChangingDirectory" type="primary">
|
|
731
|
-
切换
|
|
732
|
-
</el-button>
|
|
733
|
-
</div>
|
|
734
|
-
</el-form-item>
|
|
735
|
-
</el-form>
|
|
736
|
-
</el-dialog>
|
|
737
|
-
|
|
738
|
-
<!-- 目录浏览对话框 -->
|
|
739
|
-
<el-dialog
|
|
740
|
-
v-model="isDirectoryBrowserVisible"
|
|
741
|
-
title="浏览目录"
|
|
742
|
-
width="80vw"
|
|
743
|
-
top="70px"
|
|
744
|
-
>
|
|
745
|
-
<div class="browser-current-path">
|
|
746
|
-
<span>当前路径: {{ currentBrowsePath }}</span>
|
|
747
|
-
</div>
|
|
748
|
-
|
|
749
|
-
<div v-if="browseErrorMessage" class="browser-error">
|
|
750
|
-
{{ browseErrorMessage }}
|
|
751
|
-
</div>
|
|
752
|
-
|
|
753
|
-
<div v-loading="isBrowsing" class="directory-browser">
|
|
754
|
-
<!-- 导航栏 -->
|
|
755
|
-
<div class="browser-nav">
|
|
756
|
-
<el-button
|
|
757
|
-
@click="navigateToParent"
|
|
758
|
-
:disabled="!currentBrowsePath || isBrowsing"
|
|
759
|
-
size="small"
|
|
760
|
-
class="no-padding-left"
|
|
761
|
-
>
|
|
762
|
-
<el-icon><ArrowUp /></el-icon>
|
|
763
|
-
上级目录
|
|
764
|
-
</el-button>
|
|
765
|
-
<el-button
|
|
766
|
-
@click="selectCurrentDirectory"
|
|
767
|
-
type="primary"
|
|
768
|
-
size="small"
|
|
769
|
-
class="no-padding-left"
|
|
770
|
-
>
|
|
771
|
-
选择当前目录
|
|
772
|
-
</el-button>
|
|
773
|
-
</div>
|
|
774
|
-
|
|
775
|
-
<!-- 目录内容列表 -->
|
|
776
|
-
<div class="directory-items-container">
|
|
777
|
-
<ul class="directory-items">
|
|
778
|
-
<li
|
|
779
|
-
v-for="item in directoryItems"
|
|
780
|
-
:key="item.path"
|
|
781
|
-
:class="['directory-item', item.type]"
|
|
782
|
-
@click="selectDirectoryItem(item)"
|
|
783
|
-
>
|
|
784
|
-
<el-icon v-if="item.type === 'directory'"><Folder /></el-icon>
|
|
785
|
-
<el-icon v-else><Document /></el-icon>
|
|
786
|
-
<span>{{ item.name }}</span>
|
|
787
|
-
</li>
|
|
788
|
-
</ul>
|
|
789
|
-
</div>
|
|
790
|
-
</div>
|
|
791
|
-
</el-dialog>
|
|
792
|
-
|
|
793
|
-
<!-- 文件差异对话框 -->
|
|
794
|
-
<el-dialog
|
|
795
|
-
v-model="diffDialogVisible"
|
|
796
|
-
width="80vw"
|
|
797
|
-
top="70px"
|
|
798
|
-
destroy-on-close
|
|
799
|
-
class="diff-dialog"
|
|
800
|
-
:show-close="false"
|
|
801
|
-
style="height: calc(100vh - 140px);"
|
|
802
|
-
:modal-append-to-body="false"
|
|
803
|
-
:close-on-click-modal="false"
|
|
804
|
-
>
|
|
805
|
-
<template #header>
|
|
806
|
-
<div class="diff-dialog-header">
|
|
807
|
-
<div class="file-title">
|
|
808
|
-
<el-icon class="file-icon"><Document /></el-icon>
|
|
809
|
-
<span class="file-path">{{ selectedFile }}</span>
|
|
810
|
-
</div>
|
|
811
|
-
<div class="header-actions">
|
|
812
|
-
<el-button
|
|
813
|
-
@click="diffDialogVisible = false"
|
|
814
|
-
circle
|
|
815
|
-
size="small"
|
|
816
|
-
:icon="Close"
|
|
817
|
-
class="close-button"
|
|
818
|
-
/>
|
|
819
|
-
</div>
|
|
820
|
-
</div>
|
|
821
|
-
</template>
|
|
822
|
-
|
|
823
|
-
<div v-loading="isLoadingDiff" class="diff-content">
|
|
824
|
-
<div v-if="diffContent" v-html="formatDiff(diffContent)" class="diff-formatted"></div>
|
|
825
|
-
<div v-else class="no-diff">该文件没有差异或是新文件</div>
|
|
826
|
-
</div>
|
|
827
|
-
|
|
828
|
-
<template #footer>
|
|
829
|
-
<div class="file-navigation">
|
|
830
|
-
<el-button
|
|
831
|
-
type="primary"
|
|
832
|
-
:icon="ArrowLeft"
|
|
833
|
-
@click="goToPreviousFile"
|
|
834
|
-
:disabled="currentFileIndex <= 0 || gitLogStore.fileList.length === 0"
|
|
835
|
-
plain
|
|
836
|
-
class="nav-button"
|
|
837
|
-
>
|
|
838
|
-
上一个文件
|
|
839
|
-
</el-button>
|
|
840
|
-
|
|
841
|
-
<div class="file-counter">
|
|
842
|
-
<el-tag type="info" effect="plain" class="counter-tag">
|
|
843
|
-
{{ currentFileIndex + 1 }} / {{ gitLogStore.fileList.length }}
|
|
844
|
-
</el-tag>
|
|
845
|
-
</div>
|
|
846
|
-
|
|
847
|
-
<el-button
|
|
848
|
-
type="primary"
|
|
849
|
-
:icon="ArrowRight"
|
|
850
|
-
@click="goToNextFile"
|
|
851
|
-
:disabled="currentFileIndex >= gitLogStore.fileList.length - 1 || gitLogStore.fileList.length === 0"
|
|
852
|
-
plain
|
|
853
|
-
class="nav-button"
|
|
854
|
-
>
|
|
855
|
-
下一个文件
|
|
856
|
-
<template #icon>
|
|
857
|
-
<el-icon class="el-icon--right"><ArrowRight /></el-icon>
|
|
858
|
-
</template>
|
|
859
|
-
</el-button>
|
|
860
|
-
</div>
|
|
861
|
-
</template>
|
|
862
|
-
</el-dialog>
|
|
863
|
-
</div>
|
|
864
|
-
</template>
|
|
865
|
-
|
|
866
|
-
<style scoped>
|
|
867
|
-
.card {
|
|
868
|
-
background-color: #fff;
|
|
869
|
-
border-radius: 8px;
|
|
870
|
-
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.03);
|
|
871
|
-
border: 1px solid rgba(0, 0, 0, 0.03);
|
|
872
|
-
height: 100%;
|
|
873
|
-
width: 100%;
|
|
874
|
-
display: flex;
|
|
875
|
-
flex-direction: column;
|
|
876
|
-
overflow: hidden;
|
|
877
|
-
}
|
|
878
|
-
|
|
879
|
-
.status-header {
|
|
880
|
-
display: flex;
|
|
881
|
-
justify-content: space-between;
|
|
882
|
-
align-items: center;
|
|
883
|
-
padding: 8px 16px;
|
|
884
|
-
border-bottom: 1px solid #f0f0f0;
|
|
885
|
-
height: 36px;
|
|
886
|
-
}
|
|
887
|
-
|
|
888
|
-
.status-header h2 {
|
|
889
|
-
margin: 0;
|
|
890
|
-
font-size: 16px;
|
|
891
|
-
font-weight: 500;
|
|
892
|
-
color: #303133;
|
|
893
|
-
}
|
|
894
|
-
|
|
895
|
-
.header-actions {
|
|
896
|
-
display: flex;
|
|
897
|
-
align-items: center;
|
|
898
|
-
}
|
|
899
|
-
|
|
900
|
-
.card-content {
|
|
901
|
-
padding: 15px;
|
|
902
|
-
overflow-y: auto;
|
|
903
|
-
flex: 1;
|
|
904
|
-
display: flex;
|
|
905
|
-
flex-direction: column;
|
|
906
|
-
min-height: 300px; /* 确保内容区有最小高度 */
|
|
907
|
-
}
|
|
908
|
-
|
|
909
|
-
.current-directory {
|
|
910
|
-
display: flex;
|
|
911
|
-
align-items: center;
|
|
912
|
-
margin-bottom: 16px;
|
|
913
|
-
padding: 10px;
|
|
914
|
-
background-color: #f8f9fa;
|
|
915
|
-
border-radius: 6px;
|
|
916
|
-
font-family: monospace;
|
|
917
|
-
border: 1px solid #f0f0f0;
|
|
918
|
-
}
|
|
919
|
-
|
|
920
|
-
.current-directory .el-icon {
|
|
921
|
-
margin-right: 8px;
|
|
922
|
-
color: #409eff;
|
|
923
|
-
}
|
|
924
|
-
|
|
925
|
-
.current-directory span {
|
|
926
|
-
flex-grow: 1;
|
|
927
|
-
word-break: break-all;
|
|
928
|
-
margin-right: 10px;
|
|
929
|
-
}
|
|
930
|
-
|
|
931
|
-
.status-box {
|
|
932
|
-
white-space: pre-wrap;
|
|
933
|
-
font-family: monospace;
|
|
934
|
-
background-color: #f8f9fa;
|
|
935
|
-
padding: 16px;
|
|
936
|
-
border-radius: 6px;
|
|
937
|
-
margin-bottom: 20px;
|
|
938
|
-
max-height: 200px;
|
|
939
|
-
overflow-y: auto;
|
|
940
|
-
border: 1px solid #f0f0f0;
|
|
941
|
-
font-size: 14px;
|
|
942
|
-
line-height: 1.5;
|
|
943
|
-
}
|
|
944
|
-
|
|
945
|
-
/* 现代化的文件列表容器 */
|
|
946
|
-
.file-list-container {
|
|
947
|
-
flex: 1;
|
|
948
|
-
overflow: hidden;
|
|
949
|
-
display: flex;
|
|
950
|
-
flex-direction: column;
|
|
951
|
-
margin-bottom: 0;
|
|
952
|
-
gap: 12px;
|
|
953
|
-
height: auto; /* 改为自适应高度,不固定高度 */
|
|
954
|
-
min-height: 100px; /* 设置最小高度 */
|
|
955
|
-
}
|
|
956
|
-
|
|
957
|
-
.file-group {
|
|
958
|
-
background-color: #f8f9fa;
|
|
959
|
-
border-radius: 6px;
|
|
960
|
-
overflow: hidden;
|
|
961
|
-
border: 1px solid #ebeef5;
|
|
962
|
-
margin-bottom: 12px;
|
|
963
|
-
display: flex;
|
|
964
|
-
flex-direction: column;
|
|
965
|
-
}
|
|
966
|
-
|
|
967
|
-
/* 让每个文件组根据内容自动增长 */
|
|
968
|
-
.file-group {
|
|
969
|
-
flex: 0 1 auto; /* 不主动增长,但允许缩小,基于内容大小 */
|
|
970
|
-
}
|
|
971
|
-
|
|
972
|
-
/* 最后一个分组可以吸收剩余空间 */
|
|
973
|
-
.file-group:last-child {
|
|
974
|
-
margin-bottom: 0;
|
|
975
|
-
flex: 1 1 auto; /* 可以增长占用剩余空间 */
|
|
976
|
-
}
|
|
977
|
-
|
|
978
|
-
.file-group-header {
|
|
979
|
-
font-size: 14px;
|
|
980
|
-
font-weight: bold;
|
|
981
|
-
padding: 8px 12px;
|
|
982
|
-
background-color: #f0f2f5;
|
|
983
|
-
color: #606266;
|
|
984
|
-
border-bottom: 1px solid #ebeef5;
|
|
985
|
-
flex-shrink: 0; /* 防止header被压缩 */
|
|
986
|
-
}
|
|
987
|
-
|
|
988
|
-
.file-list {
|
|
989
|
-
overflow-y: auto;
|
|
990
|
-
min-height: 40px; /* 最小高度 */
|
|
991
|
-
max-height: 200px; /* 最大高度限制,避免过长列表 */
|
|
992
|
-
flex-grow: 1; /* 允许文件列表在文件组内扩展 */
|
|
993
|
-
padding: 0;
|
|
994
|
-
margin: 0;
|
|
995
|
-
scrollbar-width: thin; /* Firefox */
|
|
996
|
-
scrollbar-color: rgba(144, 147, 153, 0.3) transparent; /* Firefox */
|
|
997
|
-
}
|
|
998
|
-
|
|
999
|
-
/* Webkit浏览器的滚动条样式 */
|
|
1000
|
-
.file-list::-webkit-scrollbar {
|
|
1001
|
-
width: 6px;
|
|
1002
|
-
height: 6px;
|
|
1003
|
-
}
|
|
1004
|
-
|
|
1005
|
-
.file-list::-webkit-scrollbar-thumb {
|
|
1006
|
-
background-color: rgba(144, 147, 153, 0.3);
|
|
1007
|
-
border-radius: 4px;
|
|
1008
|
-
}
|
|
1009
|
-
|
|
1010
|
-
.file-list::-webkit-scrollbar-thumb:hover {
|
|
1011
|
-
background-color: rgba(144, 147, 153, 0.5);
|
|
1012
|
-
}
|
|
1013
|
-
|
|
1014
|
-
.file-list::-webkit-scrollbar-track {
|
|
1015
|
-
background-color: transparent;
|
|
1016
|
-
}
|
|
1017
|
-
|
|
1018
|
-
/* 让每个文件列表自适应高度,使其在容器中更好地分配空间 */
|
|
1019
|
-
.file-list:empty {
|
|
1020
|
-
display: none; /* 如果列表为空,不显示 */
|
|
1021
|
-
}
|
|
1022
|
-
|
|
1023
|
-
/* 当没有足够的项目填充列表时,禁用滚动 */
|
|
1024
|
-
.file-list:has(.empty-file-group) {
|
|
1025
|
-
overflow-y: hidden;
|
|
1026
|
-
}
|
|
1027
|
-
|
|
1028
|
-
/* 替换为更兼容的选择器 */
|
|
1029
|
-
/* 改用直接为empty-file-group父容器添加样式 */
|
|
1030
|
-
.empty-file-container {
|
|
1031
|
-
overflow-y: hidden !important; /* 强制禁用滚动 */
|
|
1032
|
-
display: flex;
|
|
1033
|
-
flex-direction: column;
|
|
1034
|
-
align-items: stretch;
|
|
1035
|
-
flex: 1;
|
|
1036
|
-
}
|
|
1037
|
-
|
|
1038
|
-
.empty-file-group {
|
|
1039
|
-
padding: 16px;
|
|
1040
|
-
text-align: center;
|
|
1041
|
-
color: #909399;
|
|
1042
|
-
font-size: 13px;
|
|
1043
|
-
font-style: italic;
|
|
1044
|
-
display: flex;
|
|
1045
|
-
align-items: center;
|
|
1046
|
-
justify-content: center;
|
|
1047
|
-
min-height: 50px; /* 增加最小高度 */
|
|
1048
|
-
background-color: #f8f9fa;
|
|
1049
|
-
border-radius: 4px;
|
|
1050
|
-
margin: 8px;
|
|
1051
|
-
}
|
|
1052
|
-
|
|
1053
|
-
/* 自定义每个分组的展开逻辑 */
|
|
1054
|
-
/* 已暂存文件分组 - 保持小巧 */
|
|
1055
|
-
.file-group:nth-child(1) {
|
|
1056
|
-
flex: 0 1 auto;
|
|
1057
|
-
}
|
|
1058
|
-
|
|
1059
|
-
/* 未暂存更改分组 - 稍微大一些 */
|
|
1060
|
-
.file-group:nth-child(2) {
|
|
1061
|
-
flex: 0 1 auto;
|
|
1062
|
-
}
|
|
1063
|
-
|
|
1064
|
-
/* 未跟踪文件分组 - 可以占据剩余空间 */
|
|
1065
|
-
.file-group:nth-child(3) {
|
|
1066
|
-
flex: 1 1 auto;
|
|
1067
|
-
}
|
|
1068
|
-
|
|
1069
|
-
.file-item {
|
|
1070
|
-
padding: 8px 12px;
|
|
1071
|
-
display: flex;
|
|
1072
|
-
align-items: center;
|
|
1073
|
-
justify-content: space-between;
|
|
1074
|
-
border-bottom: 1px solid #ebeef5;
|
|
1075
|
-
cursor: pointer;
|
|
1076
|
-
transition: background-color 0.2s;
|
|
1077
|
-
}
|
|
1078
|
-
|
|
1079
|
-
.file-item:last-child {
|
|
1080
|
-
border-bottom: none;
|
|
1081
|
-
}
|
|
1082
|
-
|
|
1083
|
-
.file-item:hover {
|
|
1084
|
-
background-color: #ecf5ff;
|
|
1085
|
-
}
|
|
1086
|
-
|
|
1087
|
-
.file-info {
|
|
1088
|
-
display: flex;
|
|
1089
|
-
align-items: center;
|
|
1090
|
-
flex-grow: 1;
|
|
1091
|
-
overflow: hidden;
|
|
1092
|
-
white-space: nowrap;
|
|
1093
|
-
gap: 8px;
|
|
1094
|
-
}
|
|
1095
|
-
|
|
1096
|
-
.file-status-indicator {
|
|
1097
|
-
width: 6px;
|
|
1098
|
-
height: 6px;
|
|
1099
|
-
border-radius: 50%;
|
|
1100
|
-
background-color: #409eff;
|
|
1101
|
-
flex-shrink: 0;
|
|
1102
|
-
}
|
|
1103
|
-
|
|
1104
|
-
.file-status-indicator.added {
|
|
1105
|
-
background-color: #67c23a;
|
|
1106
|
-
}
|
|
1107
|
-
|
|
1108
|
-
.file-status-indicator.modified {
|
|
1109
|
-
background-color: #409eff;
|
|
1110
|
-
}
|
|
1111
|
-
|
|
1112
|
-
.file-status-indicator.deleted {
|
|
1113
|
-
background-color: #f56c6c;
|
|
1114
|
-
}
|
|
1115
|
-
|
|
1116
|
-
.file-status-indicator.untracked {
|
|
1117
|
-
background-color: #e6a23c;
|
|
1118
|
-
}
|
|
1119
|
-
|
|
1120
|
-
.file-path-container {
|
|
1121
|
-
display: flex;
|
|
1122
|
-
flex-direction: column;
|
|
1123
|
-
overflow: hidden;
|
|
1124
|
-
}
|
|
1125
|
-
|
|
1126
|
-
.file-name {
|
|
1127
|
-
font-weight: 500;
|
|
1128
|
-
color: #303133;
|
|
1129
|
-
overflow: hidden;
|
|
1130
|
-
text-overflow: ellipsis;
|
|
1131
|
-
line-height: 1.2;
|
|
1132
|
-
}
|
|
1133
|
-
|
|
1134
|
-
.file-directory {
|
|
1135
|
-
font-size: 12px;
|
|
1136
|
-
color: #909399;
|
|
1137
|
-
max-width: 200px;
|
|
1138
|
-
overflow: hidden;
|
|
1139
|
-
white-space: nowrap;
|
|
1140
|
-
text-overflow: ellipsis;
|
|
1141
|
-
}
|
|
1142
|
-
|
|
1143
|
-
.file-actions {
|
|
1144
|
-
display: flex;
|
|
1145
|
-
gap: 5px;
|
|
1146
|
-
opacity: 0;
|
|
1147
|
-
transition: opacity 0.2s;
|
|
1148
|
-
}
|
|
1149
|
-
|
|
1150
|
-
.file-item:hover .file-actions {
|
|
1151
|
-
opacity: 1;
|
|
1152
|
-
}
|
|
1153
|
-
|
|
1154
|
-
.empty-status {
|
|
1155
|
-
display: flex;
|
|
1156
|
-
flex-direction: column;
|
|
1157
|
-
align-items: center;
|
|
1158
|
-
justify-content: center;
|
|
1159
|
-
padding: 40px 20px;
|
|
1160
|
-
text-align: center;
|
|
1161
|
-
background-color: #f9f9f9;
|
|
1162
|
-
border-radius: 8px;
|
|
1163
|
-
margin-top: 10px;
|
|
1164
|
-
}
|
|
1165
|
-
|
|
1166
|
-
.empty-icon {
|
|
1167
|
-
width: 60px;
|
|
1168
|
-
height: 60px;
|
|
1169
|
-
display: flex;
|
|
1170
|
-
align-items: center;
|
|
1171
|
-
justify-content: center;
|
|
1172
|
-
background-color: #ebeef5;
|
|
1173
|
-
border-radius: 50%;
|
|
1174
|
-
margin-bottom: 15px;
|
|
1175
|
-
font-size: 24px;
|
|
1176
|
-
color: #909399;
|
|
1177
|
-
animation: pulse 2s infinite ease-in-out;
|
|
1178
|
-
}
|
|
1179
|
-
|
|
1180
|
-
@keyframes pulse {
|
|
1181
|
-
0% { transform: scale(1); opacity: 0.7; }
|
|
1182
|
-
50% { transform: scale(1.05); opacity: 1; }
|
|
1183
|
-
100% { transform: scale(1); opacity: 0.7; }
|
|
1184
|
-
}
|
|
1185
|
-
|
|
1186
|
-
.empty-text {
|
|
1187
|
-
font-size: 16px;
|
|
1188
|
-
font-weight: 500;
|
|
1189
|
-
color: #606266;
|
|
1190
|
-
margin-bottom: 5px;
|
|
1191
|
-
}
|
|
1192
|
-
|
|
1193
|
-
.empty-subtext {
|
|
1194
|
-
font-size: 14px;
|
|
1195
|
-
color: #909399;
|
|
1196
|
-
margin-bottom: 20px;
|
|
1197
|
-
}
|
|
1198
|
-
|
|
1199
|
-
/* 分支信息样式 */
|
|
1200
|
-
.branch-info {
|
|
1201
|
-
margin-top: 15px;
|
|
1202
|
-
padding: 10px 15px;
|
|
1203
|
-
background-color: #ebeef5;
|
|
1204
|
-
border-radius: 6px;
|
|
1205
|
-
text-align: left;
|
|
1206
|
-
width: 100%;
|
|
1207
|
-
max-width: 400px;
|
|
1208
|
-
}
|
|
1209
|
-
|
|
1210
|
-
.branch-info p {
|
|
1211
|
-
margin: 5px 0;
|
|
1212
|
-
font-size: 14px;
|
|
1213
|
-
color: #606266;
|
|
1214
|
-
}
|
|
1215
|
-
|
|
1216
|
-
.branch-sync-info {
|
|
1217
|
-
display: flex;
|
|
1218
|
-
align-items: center;
|
|
1219
|
-
gap: 5px;
|
|
1220
|
-
font-size: 13px;
|
|
1221
|
-
margin-top: 5px;
|
|
1222
|
-
padding: 5px 8px;
|
|
1223
|
-
border-radius: 4px;
|
|
1224
|
-
background-color: #f5f7fa;
|
|
1225
|
-
}
|
|
1226
|
-
|
|
1227
|
-
.branch-sync-info.warning {
|
|
1228
|
-
color: #e6a23c;
|
|
1229
|
-
background-color: #fdf6ec;
|
|
1230
|
-
}
|
|
1231
|
-
|
|
1232
|
-
.branch-sync-info.info {
|
|
1233
|
-
color: #909399;
|
|
1234
|
-
background-color: #f4f4f5;
|
|
1235
|
-
}
|
|
1236
|
-
|
|
1237
|
-
.branch-sync-info.success {
|
|
1238
|
-
color: #67c23a;
|
|
1239
|
-
background-color: #f0f9eb;
|
|
1240
|
-
}
|
|
1241
|
-
|
|
1242
|
-
/* 添加针对空内容区域的样式 */
|
|
1243
|
-
.card-content:empty {
|
|
1244
|
-
display: flex;
|
|
1245
|
-
align-items: center;
|
|
1246
|
-
justify-content: center;
|
|
1247
|
-
background-color: #f8f9fa;
|
|
1248
|
-
border-radius: 6px;
|
|
1249
|
-
border: 1px dashed #dcdfe6;
|
|
1250
|
-
color: #909399;
|
|
1251
|
-
height: 100%;
|
|
1252
|
-
}
|
|
1253
|
-
|
|
1254
|
-
.card-content:empty::after {
|
|
1255
|
-
content: '没有Git状态信息可显示';
|
|
1256
|
-
font-size: 14px;
|
|
1257
|
-
}
|
|
1258
|
-
|
|
1259
|
-
/* 添加目录浏览相关样式 */
|
|
1260
|
-
.browser-current-path {
|
|
1261
|
-
font-family: monospace;
|
|
1262
|
-
margin-bottom: 15px;
|
|
1263
|
-
padding: 10px;
|
|
1264
|
-
background-color: #f5f7fa;
|
|
1265
|
-
border-radius: 6px;
|
|
1266
|
-
overflow-x: auto;
|
|
1267
|
-
white-space: nowrap;
|
|
1268
|
-
border: 1px solid #ebeef5;
|
|
1269
|
-
}
|
|
1270
|
-
|
|
1271
|
-
.browser-error {
|
|
1272
|
-
color: #f56c6c;
|
|
1273
|
-
margin: 10px 0;
|
|
1274
|
-
padding: 10px;
|
|
1275
|
-
background-color: #fef0f0;
|
|
1276
|
-
border-radius: 4px;
|
|
1277
|
-
border-left: 3px solid #f56c6c;
|
|
1278
|
-
}
|
|
1279
|
-
|
|
1280
|
-
.browser-nav {
|
|
1281
|
-
display: flex;
|
|
1282
|
-
gap: 10px;
|
|
1283
|
-
margin-bottom: 10px;
|
|
1284
|
-
}
|
|
1285
|
-
|
|
1286
|
-
.no-padding-left {
|
|
1287
|
-
padding-left: 12px;
|
|
1288
|
-
}
|
|
1289
|
-
|
|
1290
|
-
.directory-browser {
|
|
1291
|
-
height: 400px;
|
|
1292
|
-
border: 1px solid #ebeef5;
|
|
1293
|
-
border-radius: 6px;
|
|
1294
|
-
overflow: hidden;
|
|
1295
|
-
display: flex;
|
|
1296
|
-
flex-direction: column;
|
|
1297
|
-
}
|
|
1298
|
-
|
|
1299
|
-
.directory-items-container {
|
|
1300
|
-
flex: 1;
|
|
1301
|
-
overflow-y: auto;
|
|
1302
|
-
background-color: #f8f9fa;
|
|
1303
|
-
padding: 10px;
|
|
1304
|
-
}
|
|
1305
|
-
|
|
1306
|
-
.directory-items {
|
|
1307
|
-
list-style: none;
|
|
1308
|
-
padding: 0;
|
|
1309
|
-
margin: 0;
|
|
1310
|
-
display: grid;
|
|
1311
|
-
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
|
|
1312
|
-
gap: 10px;
|
|
1313
|
-
}
|
|
1314
|
-
|
|
1315
|
-
.directory-item {
|
|
1316
|
-
display: flex;
|
|
1317
|
-
align-items: center;
|
|
1318
|
-
padding: 8px;
|
|
1319
|
-
cursor: pointer;
|
|
1320
|
-
border-radius: 4px;
|
|
1321
|
-
background-color: white;
|
|
1322
|
-
border: 1px solid #ebeef5;
|
|
1323
|
-
transition: all 0.2s;
|
|
1324
|
-
overflow: hidden;
|
|
1325
|
-
white-space: nowrap;
|
|
1326
|
-
text-overflow: ellipsis;
|
|
1327
|
-
gap: 5px;
|
|
1328
|
-
}
|
|
1329
|
-
|
|
1330
|
-
.directory-item:hover {
|
|
1331
|
-
background-color: #ecf5ff;
|
|
1332
|
-
border-color: #c6e2ff;
|
|
1333
|
-
}
|
|
1334
|
-
|
|
1335
|
-
.directory-item.directory {
|
|
1336
|
-
background-color: #f0f7ff;
|
|
1337
|
-
color: #409eff;
|
|
1338
|
-
}
|
|
1339
|
-
|
|
1340
|
-
.directory-item .el-icon {
|
|
1341
|
-
margin-right: 5px;
|
|
1342
|
-
flex-shrink: 0;
|
|
1343
|
-
}
|
|
1344
|
-
|
|
1345
|
-
.directory-item.directory .el-icon {
|
|
1346
|
-
color: #409eff;
|
|
1347
|
-
}
|
|
1348
|
-
|
|
1349
|
-
.directory-item.file .el-icon {
|
|
1350
|
-
color: #909399;
|
|
1351
|
-
}
|
|
1352
|
-
|
|
1353
|
-
.directory-buttons {
|
|
1354
|
-
display: flex;
|
|
1355
|
-
gap: 10px;
|
|
1356
|
-
margin-top: 10px;
|
|
1357
|
-
}
|
|
1358
|
-
|
|
1359
|
-
/* 添加分支状态样式 */
|
|
1360
|
-
.branch-sync-status {
|
|
1361
|
-
display: flex;
|
|
1362
|
-
align-items: center;
|
|
1363
|
-
padding: 12px 15px;
|
|
1364
|
-
background-color: #f8f9fa;
|
|
1365
|
-
border-radius: 4px;
|
|
1366
|
-
margin-bottom: 15px;
|
|
1367
|
-
border-left: 3px solid #e6a23c;
|
|
1368
|
-
}
|
|
1369
|
-
|
|
1370
|
-
/* 添加默认分支状态信息样式 */
|
|
1371
|
-
.git-status-message {
|
|
1372
|
-
padding: 12px 15px;
|
|
1373
|
-
background-color: #f8f9fa;
|
|
1374
|
-
border-radius: 4px;
|
|
1375
|
-
margin-bottom: 15px;
|
|
1376
|
-
border-left: 3px solid #67c23a;
|
|
1377
|
-
}
|
|
1378
|
-
|
|
1379
|
-
.git-status-message p {
|
|
1380
|
-
margin: 0;
|
|
1381
|
-
display: flex;
|
|
1382
|
-
align-items: center;
|
|
1383
|
-
gap: 8px;
|
|
1384
|
-
font-size: 14px;
|
|
1385
|
-
color: #606266;
|
|
1386
|
-
}
|
|
1387
|
-
|
|
1388
|
-
.sync-status-content {
|
|
1389
|
-
display: flex;
|
|
1390
|
-
align-items: center;
|
|
1391
|
-
flex-wrap: wrap;
|
|
1392
|
-
gap: 10px;
|
|
1393
|
-
}
|
|
1394
|
-
|
|
1395
|
-
.status-badges {
|
|
1396
|
-
display: flex;
|
|
1397
|
-
flex-direction: column;
|
|
1398
|
-
gap: 8px;
|
|
1399
|
-
width: 100%;
|
|
1400
|
-
}
|
|
1401
|
-
|
|
1402
|
-
.status-badge {
|
|
1403
|
-
display: flex;
|
|
1404
|
-
align-items: center;
|
|
1405
|
-
width: 100%;
|
|
1406
|
-
}
|
|
1407
|
-
|
|
1408
|
-
.status-badge.el-tag--warning {
|
|
1409
|
-
background-color: #fdf6ec;
|
|
1410
|
-
border-color: #faecd8;
|
|
1411
|
-
}
|
|
1412
|
-
|
|
1413
|
-
.status-badge.el-tag--info {
|
|
1414
|
-
background-color: #f4f4f5;
|
|
1415
|
-
border-color: #e9e9eb;
|
|
1416
|
-
}
|
|
1417
|
-
|
|
1418
|
-
.badge-content {
|
|
1419
|
-
display: flex;
|
|
1420
|
-
align-items: center;
|
|
1421
|
-
gap: 6px;
|
|
1422
|
-
font-size: 13px;
|
|
1423
|
-
}
|
|
1424
|
-
.diff-dialog {
|
|
1425
|
-
height: calc(100vh - 150px);
|
|
1426
|
-
}
|
|
1427
|
-
|
|
1428
|
-
.diff-dialog-header {
|
|
1429
|
-
display: flex;
|
|
1430
|
-
justify-content: space-between;
|
|
1431
|
-
align-items: center;
|
|
1432
|
-
padding: 16px 20px;
|
|
1433
|
-
border-bottom: 1px solid #ebeef5;
|
|
1434
|
-
background-color: #f9f9fb;
|
|
1435
|
-
}
|
|
1436
|
-
|
|
1437
|
-
.file-title {
|
|
1438
|
-
display: flex;
|
|
1439
|
-
align-items: center;
|
|
1440
|
-
font-size: 16px;
|
|
1441
|
-
font-weight: 500;
|
|
1442
|
-
color: #303133;
|
|
1443
|
-
gap: 10px;
|
|
1444
|
-
overflow: hidden;
|
|
1445
|
-
}
|
|
1446
|
-
|
|
1447
|
-
.file-icon {
|
|
1448
|
-
color: #409eff;
|
|
1449
|
-
font-size: 20px;
|
|
1450
|
-
}
|
|
1451
|
-
|
|
1452
|
-
.file-path {
|
|
1453
|
-
white-space: nowrap;
|
|
1454
|
-
overflow: hidden;
|
|
1455
|
-
text-overflow: ellipsis;
|
|
1456
|
-
font-family: monospace;
|
|
1457
|
-
}
|
|
1458
|
-
|
|
1459
|
-
.diff-content {
|
|
1460
|
-
flex: 1;
|
|
1461
|
-
overflow-y: auto;
|
|
1462
|
-
padding: 10px 20px;
|
|
1463
|
-
background-color: #fafafa;
|
|
1464
|
-
/* height: 100%;
|
|
1465
|
-
overflow: auto; */
|
|
1466
|
-
}
|
|
1467
|
-
:deep(.el-dialog__body) {
|
|
1468
|
-
height: calc(100vh - 320px);
|
|
1469
|
-
overflow: auto;
|
|
1470
|
-
}
|
|
1471
|
-
.diff-formatted {
|
|
1472
|
-
font-family: 'Consolas', 'Courier New', monospace;
|
|
1473
|
-
font-size: 14px;
|
|
1474
|
-
line-height: 1.5;
|
|
1475
|
-
white-space: pre-wrap;
|
|
1476
|
-
padding-bottom: 20px;
|
|
1477
|
-
}
|
|
1478
|
-
|
|
1479
|
-
.no-diff {
|
|
1480
|
-
display: flex;
|
|
1481
|
-
align-items: center;
|
|
1482
|
-
justify-content: center;
|
|
1483
|
-
height: 100%;
|
|
1484
|
-
color: #909399;
|
|
1485
|
-
font-size: 14px;
|
|
1486
|
-
}
|
|
1487
|
-
|
|
1488
|
-
.file-navigation {
|
|
1489
|
-
display: flex;
|
|
1490
|
-
justify-content: space-between;
|
|
1491
|
-
align-items: center;
|
|
1492
|
-
padding: 10px 20px;
|
|
1493
|
-
border-top: 1px solid #ebeef5;
|
|
1494
|
-
background-color: #f9f9fb;
|
|
1495
|
-
}
|
|
1496
|
-
|
|
1497
|
-
.counter-tag {
|
|
1498
|
-
font-family: monospace;
|
|
1499
|
-
font-size: 14px;
|
|
1500
|
-
padding: 6px 12px;
|
|
1501
|
-
min-width: 80px;
|
|
1502
|
-
text-align: center;
|
|
1503
|
-
}
|
|
1504
|
-
|
|
1505
|
-
.nav-button {
|
|
1506
|
-
min-width: 120px;
|
|
1507
|
-
}
|
|
1508
|
-
|
|
1509
|
-
/* 确保文件差异对话框里的内容滚动条样式一致 */
|
|
1510
|
-
.diff-formatted::-webkit-scrollbar,
|
|
1511
|
-
.diff-content::-webkit-scrollbar {
|
|
1512
|
-
width: 6px;
|
|
1513
|
-
height: 6px;
|
|
1514
|
-
}
|
|
1515
|
-
|
|
1516
|
-
.diff-formatted::-webkit-scrollbar-thumb,
|
|
1517
|
-
.diff-content::-webkit-scrollbar-thumb {
|
|
1518
|
-
background-color: rgba(144, 147, 153, 0.3);
|
|
1519
|
-
border-radius: 4px;
|
|
1520
|
-
}
|
|
1521
|
-
|
|
1522
|
-
.diff-formatted::-webkit-scrollbar-thumb:hover,
|
|
1523
|
-
.diff-content::-webkit-scrollbar-thumb:hover {
|
|
1524
|
-
background-color: rgba(144, 147, 153, 0.5);
|
|
1525
|
-
}
|
|
1526
|
-
|
|
1527
|
-
.diff-formatted::-webkit-scrollbar-track,
|
|
1528
|
-
.diff-content::-webkit-scrollbar-track {
|
|
1529
|
-
background-color: transparent;
|
|
1530
|
-
}
|
|
1531
|
-
|
|
1532
|
-
/* 兼容Firefox滚动条样式 */
|
|
1533
|
-
.diff-formatted,
|
|
1534
|
-
.diff-content {
|
|
1535
|
-
scrollbar-width: thin;
|
|
1536
|
-
scrollbar-color: rgba(144, 147, 153, 0.3) transparent;
|
|
1537
|
-
}
|
|
1538
|
-
</style>
|
|
1539
|
-
|
|
1540
|
-
<!-- 非scoped样式,使diff格式化样式对动态内容生效 -->
|
|
1541
|
-
<style>
|
|
1542
|
-
.diff-header {
|
|
1543
|
-
font-weight: bold;
|
|
1544
|
-
background-color: #e6f1fc;
|
|
1545
|
-
padding: 5px;
|
|
1546
|
-
margin: 8px 0;
|
|
1547
|
-
border-radius: 4px;
|
|
1548
|
-
color: #0366d6;
|
|
1549
|
-
border-bottom: 1px solid #c8e1ff;
|
|
1550
|
-
}
|
|
1551
|
-
|
|
1552
|
-
.diff-old-file, .diff-new-file {
|
|
1553
|
-
color: #586069;
|
|
1554
|
-
padding: 2px 5px;
|
|
1555
|
-
font-family: monospace;
|
|
1556
|
-
}
|
|
1557
|
-
|
|
1558
|
-
.diff-old-file {
|
|
1559
|
-
color: #cb2431;
|
|
1560
|
-
}
|
|
1561
|
-
|
|
1562
|
-
.diff-new-file {
|
|
1563
|
-
color: #22863a;
|
|
1564
|
-
}
|
|
1565
|
-
|
|
1566
|
-
.diff-hunk-header {
|
|
1567
|
-
color: #6f42c1;
|
|
1568
|
-
background-color: #f1f8ff;
|
|
1569
|
-
padding: 2px 5px;
|
|
1570
|
-
margin: 5px 0;
|
|
1571
|
-
border-radius: 3px;
|
|
1572
|
-
font-family: monospace;
|
|
1573
|
-
}
|
|
1574
|
-
|
|
1575
|
-
.diff-added {
|
|
1576
|
-
background-color: #e6ffed;
|
|
1577
|
-
color: #22863a;
|
|
1578
|
-
padding: 0 5px;
|
|
1579
|
-
border-left: 4px solid #22863a;
|
|
1580
|
-
font-family: monospace;
|
|
1581
|
-
display: block;
|
|
1582
|
-
margin: 2px 0;
|
|
1583
|
-
}
|
|
1584
|
-
|
|
1585
|
-
.diff-removed {
|
|
1586
|
-
background-color: #ffeef0;
|
|
1587
|
-
color: #cb2431;
|
|
1588
|
-
padding: 0 5px;
|
|
1589
|
-
border-left: 4px solid #cb2431;
|
|
1590
|
-
font-family: monospace;
|
|
1591
|
-
display: block;
|
|
1592
|
-
margin: 2px 0;
|
|
1593
|
-
}
|
|
1594
|
-
|
|
1595
|
-
.diff-context {
|
|
1596
|
-
color: #444;
|
|
1597
|
-
padding: 0 5px;
|
|
1598
|
-
font-family: monospace;
|
|
1599
|
-
display: block;
|
|
1600
|
-
margin: 2px 0;
|
|
1601
|
-
background-color: #fafbfc;
|
|
1602
|
-
}
|
|
1603
|
-
|
|
1604
|
-
/* 增加自动更新开关的样式 */
|
|
1605
|
-
.auto-update-switch .el-switch__core {
|
|
1606
|
-
transition: all 0.3s ease-in-out;
|
|
1607
|
-
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
|
|
1608
|
-
}
|
|
1609
|
-
|
|
1610
|
-
.auto-update-switch .el-switch__core:hover {
|
|
1611
|
-
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
|
|
1612
|
-
}
|
|
1613
|
-
|
|
1614
|
-
.auto-update-switch.is-checked .el-switch__core {
|
|
1615
|
-
box-shadow: 0 2px 5px rgba(103, 194, 58, 0.3);
|
|
1616
|
-
}
|
|
1617
|
-
|
|
1618
|
-
.auto-update-switch.is-checked .el-switch__core:hover {
|
|
1619
|
-
box-shadow: 0 2px 8px rgba(103, 194, 58, 0.5);
|
|
1620
|
-
}
|
|
1621
|
-
</style>
|