zen-gitsync 1.5.6 → 2.0.3
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/README.md +9 -0
- package/package.json +10 -3
- package/src/config.js +2 -1
- package/src/gitCommit.js +7 -0
- package/src/ui/client/README.md +5 -0
- package/src/ui/client/auto-imports.d.ts +10 -0
- package/src/ui/client/components.d.ts +31 -0
- package/src/ui/client/index.html +13 -0
- package/src/ui/client/package.json +27 -0
- package/src/ui/client/public/favicon.svg +27 -0
- package/src/ui/client/public/logo.svg +27 -0
- package/src/ui/client/public/vite.svg +1 -0
- package/src/ui/client/src/App.vue +539 -0
- package/src/ui/client/src/assets/logo.svg +27 -0
- package/src/ui/client/src/components/CommitForm.vue +904 -0
- package/src/ui/client/src/components/GitStatus.vue +799 -0
- package/src/ui/client/src/components/LogList.vue +270 -0
- package/src/ui/client/src/main.ts +4 -0
- package/src/ui/client/src/vite-env.d.ts +1 -0
- package/src/ui/client/stats.html +4949 -0
- package/src/ui/client/tsconfig.app.json +14 -0
- package/src/ui/client/tsconfig.json +7 -0
- package/src/ui/client/tsconfig.node.json +24 -0
- package/src/ui/client/vite.config.ts +48 -0
- package/src/ui/public/assets/index-BHmYZROy.css +1 -0
- package/src/ui/public/assets/index-kfMX1bxz.js +9 -0
- package/src/ui/public/assets/vendor-Dp0FkvMe.css +1 -0
- package/src/ui/public/assets/vendor-DxvF30ca.js +41 -0
- package/src/ui/public/favicon.svg +27 -0
- package/src/ui/public/index.html +16 -0
- package/src/ui/public/logo.svg +27 -0
- package/src/ui/public/vite.svg +1 -0
- package/src/ui/server/index.js +598 -0
- package/src/utils/index.js +4 -0
|
@@ -0,0 +1,539 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { ref, onMounted } from 'vue'
|
|
3
|
+
import GitStatus from './components/GitStatus.vue'
|
|
4
|
+
import CommitForm from './components/CommitForm.vue'
|
|
5
|
+
import LogList from './components/LogList.vue'
|
|
6
|
+
import { ElMessage } from 'element-plus'
|
|
7
|
+
import { Plus } from '@element-plus/icons-vue'
|
|
8
|
+
import logo from './assets/logo.svg'
|
|
9
|
+
|
|
10
|
+
const configInfo = ref('')
|
|
11
|
+
// 添加组件实例类型
|
|
12
|
+
const logListRef = ref<InstanceType<typeof LogList> | null>(null)
|
|
13
|
+
const gitStatusRef = ref<InstanceType<typeof GitStatus> | null>(null)
|
|
14
|
+
const currentBranch = ref('') // 添加当前分支状态变量
|
|
15
|
+
const userName = ref('') // 添加用户名变量
|
|
16
|
+
const userEmail = ref('') // 添加用户邮箱变量
|
|
17
|
+
const allBranches = ref<string[]>([]) // 添加所有分支列表
|
|
18
|
+
const isChangingBranch = ref(false) // 添加分支切换状态
|
|
19
|
+
|
|
20
|
+
// 加载配置
|
|
21
|
+
async function loadConfig() {
|
|
22
|
+
try {
|
|
23
|
+
const response = await fetch('/api/config/getConfig')
|
|
24
|
+
const config = await response.json()
|
|
25
|
+
configInfo.value = `默认提交信息: ${config.defaultCommitMessage}`
|
|
26
|
+
} catch (error) {
|
|
27
|
+
console.error('加载配置失败:', error)
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// 获取当前分支
|
|
32
|
+
async function getCurrentBranch() {
|
|
33
|
+
try {
|
|
34
|
+
const response = await fetch('/api/branch')
|
|
35
|
+
const data = await response.json()
|
|
36
|
+
if (data.branch) {
|
|
37
|
+
currentBranch.value = data.branch
|
|
38
|
+
}
|
|
39
|
+
} catch (error) {
|
|
40
|
+
console.error('获取分支信息失败:', error)
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// 获取所有分支
|
|
45
|
+
async function getAllBranches() {
|
|
46
|
+
try {
|
|
47
|
+
const response = await fetch('/api/branches')
|
|
48
|
+
const data = await response.json()
|
|
49
|
+
if (data.branches && Array.isArray(data.branches)) {
|
|
50
|
+
allBranches.value = data.branches
|
|
51
|
+
}
|
|
52
|
+
} catch (error) {
|
|
53
|
+
console.error('获取所有分支信息失败:', error)
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// 切换分支
|
|
58
|
+
async function changeBranch(branch: string) {
|
|
59
|
+
console.log('切换到分支:', branch)
|
|
60
|
+
|
|
61
|
+
try {
|
|
62
|
+
isChangingBranch.value = true
|
|
63
|
+
const response = await fetch('/api/checkout', {
|
|
64
|
+
method: 'POST',
|
|
65
|
+
headers: {
|
|
66
|
+
'Content-Type': 'application/json'
|
|
67
|
+
},
|
|
68
|
+
body: JSON.stringify({ branch })
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
const result = await response.json()
|
|
72
|
+
if (result.success) {
|
|
73
|
+
ElMessage({
|
|
74
|
+
message: `已切换到分支: ${branch}`,
|
|
75
|
+
type: 'success'
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
// 刷新状态
|
|
79
|
+
getCurrentBranch()
|
|
80
|
+
|
|
81
|
+
// 刷新Git状态
|
|
82
|
+
if (gitStatusRef.value) {
|
|
83
|
+
gitStatusRef.value.refreshStatus()
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// 刷新提交历史
|
|
87
|
+
if (logListRef.value) {
|
|
88
|
+
logListRef.value.refreshLog()
|
|
89
|
+
}
|
|
90
|
+
} else {
|
|
91
|
+
ElMessage({
|
|
92
|
+
message: `切换分支失败: ${result.error}`,
|
|
93
|
+
type: 'error'
|
|
94
|
+
})
|
|
95
|
+
// 恢复选择框为当前分支
|
|
96
|
+
currentBranch.value = currentBranch.value
|
|
97
|
+
}
|
|
98
|
+
} catch (error) {
|
|
99
|
+
ElMessage({
|
|
100
|
+
message: `切换分支失败: ${(error as Error).message}`,
|
|
101
|
+
type: 'error'
|
|
102
|
+
})
|
|
103
|
+
// 恢复选择框为当前分支
|
|
104
|
+
currentBranch.value = currentBranch.value
|
|
105
|
+
} finally {
|
|
106
|
+
isChangingBranch.value = false
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// 获取Git用户信息
|
|
111
|
+
async function getUserInfo() {
|
|
112
|
+
try {
|
|
113
|
+
const response = await fetch('/api/user-info')
|
|
114
|
+
const data = await response.json()
|
|
115
|
+
if (data.name && data.email) {
|
|
116
|
+
userName.value = data.name
|
|
117
|
+
userEmail.value = data.email
|
|
118
|
+
}
|
|
119
|
+
} catch (error) {
|
|
120
|
+
console.error('获取用户信息失败:', error)
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
onMounted(() => {
|
|
125
|
+
loadConfig()
|
|
126
|
+
getCurrentBranch() // 初始加载分支信息
|
|
127
|
+
getAllBranches() // 加载所有分支
|
|
128
|
+
getUserInfo() // 初始加载用户信息
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
// 处理提交成功事件
|
|
132
|
+
function handleCommitSuccess() {
|
|
133
|
+
// 刷新提交历史
|
|
134
|
+
if (logListRef.value) {
|
|
135
|
+
logListRef.value.refreshLog()
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// 刷新Git状态
|
|
139
|
+
if (gitStatusRef.value) {
|
|
140
|
+
gitStatusRef.value.refreshStatus()
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// 处理推送成功事件
|
|
145
|
+
function handlePushSuccess() {
|
|
146
|
+
// 刷新提交历史
|
|
147
|
+
if (logListRef.value) {
|
|
148
|
+
logListRef.value.refreshLog()
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// 刷新Git状态
|
|
152
|
+
if (gitStatusRef.value) {
|
|
153
|
+
gitStatusRef.value.refreshStatus()
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// 刷新分支信息
|
|
157
|
+
getCurrentBranch()
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const createBranchDialogVisible = ref(false)
|
|
161
|
+
const newBranchName = ref('')
|
|
162
|
+
const selectedBaseBranch = ref('')
|
|
163
|
+
const isCreatingBranch = ref(false)
|
|
164
|
+
|
|
165
|
+
// 创建新分支
|
|
166
|
+
async function createNewBranch() {
|
|
167
|
+
if (!newBranchName.value.trim()) {
|
|
168
|
+
ElMessage({
|
|
169
|
+
message: '分支名称不能为空',
|
|
170
|
+
type: 'warning'
|
|
171
|
+
})
|
|
172
|
+
return
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
try {
|
|
176
|
+
isCreatingBranch.value = true
|
|
177
|
+
|
|
178
|
+
const response = await fetch('/api/create-branch', {
|
|
179
|
+
method: 'POST',
|
|
180
|
+
headers: {
|
|
181
|
+
'Content-Type': 'application/json'
|
|
182
|
+
},
|
|
183
|
+
body: JSON.stringify({
|
|
184
|
+
newBranchName: newBranchName.value,
|
|
185
|
+
baseBranch: selectedBaseBranch.value || currentBranch.value
|
|
186
|
+
})
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
const result = await response.json()
|
|
190
|
+
if (result.success) {
|
|
191
|
+
ElMessage({
|
|
192
|
+
message: `已创建并切换到分支: ${newBranchName.value}`,
|
|
193
|
+
type: 'success'
|
|
194
|
+
})
|
|
195
|
+
|
|
196
|
+
// 关闭对话框
|
|
197
|
+
createBranchDialogVisible.value = false
|
|
198
|
+
|
|
199
|
+
// 重置表单
|
|
200
|
+
newBranchName.value = ''
|
|
201
|
+
|
|
202
|
+
// 刷新状态
|
|
203
|
+
getCurrentBranch()
|
|
204
|
+
getAllBranches()
|
|
205
|
+
|
|
206
|
+
// 刷新Git状态
|
|
207
|
+
if (gitStatusRef.value) {
|
|
208
|
+
gitStatusRef.value.refreshStatus()
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// 刷新提交历史
|
|
212
|
+
if (logListRef.value) {
|
|
213
|
+
logListRef.value.refreshLog()
|
|
214
|
+
}
|
|
215
|
+
} else {
|
|
216
|
+
ElMessage({
|
|
217
|
+
message: `创建分支失败: ${result.error}`,
|
|
218
|
+
type: 'error'
|
|
219
|
+
})
|
|
220
|
+
}
|
|
221
|
+
} catch (error) {
|
|
222
|
+
ElMessage({
|
|
223
|
+
message: `创建分支失败: ${(error as Error).message}`,
|
|
224
|
+
type: 'error'
|
|
225
|
+
})
|
|
226
|
+
} finally {
|
|
227
|
+
isCreatingBranch.value = false
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// 打开创建分支对话框
|
|
232
|
+
function openCreateBranchDialog() {
|
|
233
|
+
selectedBaseBranch.value = currentBranch.value
|
|
234
|
+
createBranchDialogVisible.value = true
|
|
235
|
+
}
|
|
236
|
+
</script>
|
|
237
|
+
|
|
238
|
+
<template>
|
|
239
|
+
<header class="main-header">
|
|
240
|
+
<div class="header-left">
|
|
241
|
+
<img :src="logo" alt="Zen GitSync Logo" class="logo" />
|
|
242
|
+
<h1>Zen GitSync UI</h1>
|
|
243
|
+
</div>
|
|
244
|
+
<div class="header-info">
|
|
245
|
+
<div id="user-info" v-if="userName && userEmail">
|
|
246
|
+
<span class="user-label">用户:</span>
|
|
247
|
+
<span class="user-name">{{ userName }}</span>
|
|
248
|
+
<span class="user-email"><{{ userEmail }}></span>
|
|
249
|
+
</div>
|
|
250
|
+
<!-- <div id="config-info">{{ configInfo }}</div> -->
|
|
251
|
+
</div>
|
|
252
|
+
</header>
|
|
253
|
+
|
|
254
|
+
<div class="container">
|
|
255
|
+
<div class="layout-container">
|
|
256
|
+
<!-- 左侧Git状态 -->
|
|
257
|
+
<div class="left-panel">
|
|
258
|
+
<GitStatus ref="gitStatusRef" />
|
|
259
|
+
</div>
|
|
260
|
+
|
|
261
|
+
<!-- 右侧提交表单和历史 -->
|
|
262
|
+
<div class="right-panel">
|
|
263
|
+
<CommitForm
|
|
264
|
+
@commit-success="handleCommitSuccess"
|
|
265
|
+
@push-success="handlePushSuccess"
|
|
266
|
+
/>
|
|
267
|
+
<LogList ref="logListRef" />
|
|
268
|
+
</div>
|
|
269
|
+
|
|
270
|
+
<!-- 创建分支对话框 -->
|
|
271
|
+
<el-dialog
|
|
272
|
+
v-model="createBranchDialogVisible"
|
|
273
|
+
title="创建新分支"
|
|
274
|
+
width="30%"
|
|
275
|
+
destroy-on-close
|
|
276
|
+
>
|
|
277
|
+
<el-form :model="{ newBranchName, selectedBaseBranch }">
|
|
278
|
+
<el-form-item label="新分支名称">
|
|
279
|
+
<el-input v-model="newBranchName" placeholder="请输入新分支名称" />
|
|
280
|
+
</el-form-item>
|
|
281
|
+
<el-form-item label="基于分支">
|
|
282
|
+
<el-select v-model="selectedBaseBranch" placeholder="选择基础分支" style="width: 100%;">
|
|
283
|
+
<el-option
|
|
284
|
+
v-for="branch in allBranches"
|
|
285
|
+
:key="branch"
|
|
286
|
+
:label="branch"
|
|
287
|
+
:value="branch"
|
|
288
|
+
/>
|
|
289
|
+
</el-select>
|
|
290
|
+
</el-form-item>
|
|
291
|
+
</el-form>
|
|
292
|
+
<template #footer>
|
|
293
|
+
<span class="dialog-footer">
|
|
294
|
+
<el-button @click="createBranchDialogVisible = false">取消</el-button>
|
|
295
|
+
<el-button
|
|
296
|
+
type="primary"
|
|
297
|
+
@click="createNewBranch"
|
|
298
|
+
:loading="isCreatingBranch"
|
|
299
|
+
>
|
|
300
|
+
创建
|
|
301
|
+
</el-button>
|
|
302
|
+
</span>
|
|
303
|
+
</template>
|
|
304
|
+
</el-dialog>
|
|
305
|
+
|
|
306
|
+
</div>
|
|
307
|
+
</div>
|
|
308
|
+
|
|
309
|
+
<footer class="main-footer">
|
|
310
|
+
<div class="branch-info" v-if="currentBranch">
|
|
311
|
+
<span class="branch-label">当前分支:</span>
|
|
312
|
+
<el-select
|
|
313
|
+
v-model="currentBranch"
|
|
314
|
+
size="small"
|
|
315
|
+
@change="changeBranch"
|
|
316
|
+
:loading="isChangingBranch"
|
|
317
|
+
class="branch-select"
|
|
318
|
+
>
|
|
319
|
+
<el-option
|
|
320
|
+
v-for="branch in allBranches"
|
|
321
|
+
:key="branch"
|
|
322
|
+
:label="branch"
|
|
323
|
+
:value="branch"
|
|
324
|
+
/>
|
|
325
|
+
</el-select>
|
|
326
|
+
<el-button
|
|
327
|
+
type="primary"
|
|
328
|
+
size="small"
|
|
329
|
+
@click="openCreateBranchDialog"
|
|
330
|
+
style="margin-left: 5px;"
|
|
331
|
+
>
|
|
332
|
+
<el-icon><Plus /></el-icon>
|
|
333
|
+
</el-button>
|
|
334
|
+
</div>
|
|
335
|
+
<div class="footer-right">
|
|
336
|
+
<!-- <span>Zen GitSync © 2024</span> -->
|
|
337
|
+
</div>
|
|
338
|
+
</footer>
|
|
339
|
+
</template>
|
|
340
|
+
|
|
341
|
+
<style>
|
|
342
|
+
body {
|
|
343
|
+
font-family: 'Arial', sans-serif;
|
|
344
|
+
margin: 0;
|
|
345
|
+
padding: 0;
|
|
346
|
+
background-color: #f5f5f5;
|
|
347
|
+
}
|
|
348
|
+
.container {
|
|
349
|
+
margin: 0 auto;
|
|
350
|
+
padding: 20px 30px;
|
|
351
|
+
}
|
|
352
|
+
.main-header {
|
|
353
|
+
background-color: #24292e;
|
|
354
|
+
color: white;
|
|
355
|
+
padding: 15px 20px;
|
|
356
|
+
display: flex;
|
|
357
|
+
justify-content: space-between;
|
|
358
|
+
align-items: center;
|
|
359
|
+
}
|
|
360
|
+
.header-left {
|
|
361
|
+
display: flex;
|
|
362
|
+
align-items: center;
|
|
363
|
+
gap: 10px;
|
|
364
|
+
}
|
|
365
|
+
.logo {
|
|
366
|
+
height: 32px;
|
|
367
|
+
width: auto;
|
|
368
|
+
}
|
|
369
|
+
h1 {
|
|
370
|
+
margin: 0;
|
|
371
|
+
font-size: 24px;
|
|
372
|
+
}
|
|
373
|
+
.header-info {
|
|
374
|
+
display: flex;
|
|
375
|
+
flex-direction: column;
|
|
376
|
+
align-items: flex-end;
|
|
377
|
+
gap: 5px;
|
|
378
|
+
}
|
|
379
|
+
#branch-info, #user-info {
|
|
380
|
+
background-color: rgba(255, 255, 255, 0.1);
|
|
381
|
+
padding: 4px 8px;
|
|
382
|
+
border-radius: 4px;
|
|
383
|
+
font-size: 14px;
|
|
384
|
+
}
|
|
385
|
+
.branch-label, .user-label {
|
|
386
|
+
font-weight: bold;
|
|
387
|
+
margin-right: 5px;
|
|
388
|
+
}
|
|
389
|
+
.user-name {
|
|
390
|
+
font-weight: bold;
|
|
391
|
+
margin-right: 5px;
|
|
392
|
+
}
|
|
393
|
+
.user-email {
|
|
394
|
+
color: #e0e0e0;
|
|
395
|
+
}
|
|
396
|
+
.branch-name {
|
|
397
|
+
font-family: monospace;
|
|
398
|
+
}
|
|
399
|
+
.card {
|
|
400
|
+
background-color: white;
|
|
401
|
+
border-radius: 5px;
|
|
402
|
+
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
|
|
403
|
+
margin-bottom: 20px;
|
|
404
|
+
padding: 20px;
|
|
405
|
+
}
|
|
406
|
+
.status-box {
|
|
407
|
+
background-color: #f6f8fa;
|
|
408
|
+
border: 1px solid #e1e4e8;
|
|
409
|
+
border-radius: 3px;
|
|
410
|
+
padding: 15px;
|
|
411
|
+
white-space: pre-wrap;
|
|
412
|
+
font-family: monospace;
|
|
413
|
+
max-height: 300px;
|
|
414
|
+
overflow-y: auto;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
/* 新增布局样式 */
|
|
418
|
+
.layout-container {
|
|
419
|
+
display: flex;
|
|
420
|
+
gap: 20px;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
.left-panel {
|
|
424
|
+
flex: 0 0 30%;
|
|
425
|
+
max-width: 30%;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
.right-panel {
|
|
429
|
+
flex: 0 0 70%;
|
|
430
|
+
max-width: 70%;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
/* 响应式布局 */
|
|
434
|
+
@media (max-width: 768px) {
|
|
435
|
+
.layout-container {
|
|
436
|
+
flex-direction: column;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
.left-panel, .right-panel {
|
|
440
|
+
flex: 0 0 100%;
|
|
441
|
+
max-width: 100%;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
.header-left {
|
|
445
|
+
gap: 8px;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
.logo {
|
|
449
|
+
height: 24px;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
h1 {
|
|
453
|
+
font-size: 20px;
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
.commit-form {
|
|
457
|
+
display: flex;
|
|
458
|
+
margin-bottom: 15px;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
.log-item {
|
|
462
|
+
padding: 10px 0;
|
|
463
|
+
border-bottom: 1px solid #eee;
|
|
464
|
+
}
|
|
465
|
+
.log-item:last-child {
|
|
466
|
+
border-bottom: none;
|
|
467
|
+
}
|
|
468
|
+
.log-hash {
|
|
469
|
+
color: #6f42c1;
|
|
470
|
+
font-family: monospace;
|
|
471
|
+
}
|
|
472
|
+
.log-author {
|
|
473
|
+
color: #6a737d;
|
|
474
|
+
}
|
|
475
|
+
.log-date {
|
|
476
|
+
color: #6a737d;
|
|
477
|
+
}
|
|
478
|
+
.log-message {
|
|
479
|
+
font-weight: bold;
|
|
480
|
+
}
|
|
481
|
+
.log-branch {
|
|
482
|
+
display: inline-block;
|
|
483
|
+
background-color: #0366d6;
|
|
484
|
+
color: white;
|
|
485
|
+
border-radius: 3px;
|
|
486
|
+
padding: 2px 6px;
|
|
487
|
+
margin-left: 8px;
|
|
488
|
+
font-size: 12px;
|
|
489
|
+
}
|
|
490
|
+
/* 添加分支选择框样式 */
|
|
491
|
+
.branch-select {
|
|
492
|
+
width: 150px;
|
|
493
|
+
margin-left: 5px;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
/* 调整下拉选择框在深色背景下的样式 */
|
|
497
|
+
.branch-select :deep(.el-input__inner) {
|
|
498
|
+
background-color: rgba(255, 255, 255, 0.1);
|
|
499
|
+
color: white;
|
|
500
|
+
border: none;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
.branch-select :deep(.el-input__suffix) {
|
|
504
|
+
color: white;
|
|
505
|
+
}
|
|
506
|
+
</style>
|
|
507
|
+
|
|
508
|
+
<style scoped>
|
|
509
|
+
.logo {
|
|
510
|
+
will-change: filter;
|
|
511
|
+
transition: filter 300ms;
|
|
512
|
+
}
|
|
513
|
+
.logo:hover {
|
|
514
|
+
filter: drop-shadow(0 0 2em #42b883aa);
|
|
515
|
+
}
|
|
516
|
+
.main-footer {
|
|
517
|
+
background-color: #24292e;
|
|
518
|
+
color: white;
|
|
519
|
+
padding: 10px 20px;
|
|
520
|
+
display: flex;
|
|
521
|
+
justify-content: space-between;
|
|
522
|
+
align-items: center;
|
|
523
|
+
position: fixed;
|
|
524
|
+
bottom: 0;
|
|
525
|
+
left: 0;
|
|
526
|
+
right: 0;
|
|
527
|
+
z-index: 100;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
.branch-info {
|
|
531
|
+
display: flex;
|
|
532
|
+
align-items: center;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
.footer-right {
|
|
536
|
+
color: rgba(255, 255, 255, 0.7);
|
|
537
|
+
font-size: 12px;
|
|
538
|
+
}
|
|
539
|
+
</style>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
<svg width="200" height="200" viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<!-- 主分支 -->
|
|
3
|
+
<line x1="40" y1="40" x2="40" y2="160" stroke="#34495e" stroke-width="8" stroke-linecap="round" />
|
|
4
|
+
|
|
5
|
+
<!-- 特性分支 -->
|
|
6
|
+
<path d="M40,70 C60,70 80,90 80,110 L80,130" stroke="#3498db" stroke-width="8" stroke-linecap="round" stroke-linejoin="round" fill="none" />
|
|
7
|
+
|
|
8
|
+
<!-- 开发分支 -->
|
|
9
|
+
<path d="M40,100 C60,100 100,110 100,130 L100,160" stroke="#2ecc71" stroke-width="8" stroke-linecap="round" stroke-linejoin="round" fill="none" />
|
|
10
|
+
|
|
11
|
+
<!-- 修复分支 -->
|
|
12
|
+
<path d="M40,130 C60,130 120,140 120,160" stroke="#e74c3c" stroke-width="8" stroke-linecap="round" stroke-linejoin="round" fill="none" />
|
|
13
|
+
|
|
14
|
+
<!-- 合并点 -->
|
|
15
|
+
<path d="M80,130 C80,145 60,145 40,145" stroke="#3498db" stroke-width="8" stroke-linecap="round" stroke-linejoin="round" fill="none" />
|
|
16
|
+
|
|
17
|
+
<!-- 节点 -->
|
|
18
|
+
<circle cx="40" cy="40" r="10" fill="#34495e" />
|
|
19
|
+
<circle cx="40" cy="70" r="10" fill="#34495e" />
|
|
20
|
+
<circle cx="40" cy="100" r="10" fill="#34495e" />
|
|
21
|
+
<circle cx="40" cy="130" r="10" fill="#34495e" />
|
|
22
|
+
<circle cx="40" cy="145" r="10" fill="#34495e" />
|
|
23
|
+
<circle cx="40" cy="160" r="10" fill="#34495e" />
|
|
24
|
+
<circle cx="80" cy="130" r="10" fill="#3498db" />
|
|
25
|
+
<circle cx="100" cy="160" r="10" fill="#2ecc71" />
|
|
26
|
+
<circle cx="120" cy="160" r="10" fill="#e74c3c" />
|
|
27
|
+
</svg>
|