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.
Files changed (34) hide show
  1. package/README.md +9 -0
  2. package/package.json +10 -3
  3. package/src/config.js +2 -1
  4. package/src/gitCommit.js +7 -0
  5. package/src/ui/client/README.md +5 -0
  6. package/src/ui/client/auto-imports.d.ts +10 -0
  7. package/src/ui/client/components.d.ts +31 -0
  8. package/src/ui/client/index.html +13 -0
  9. package/src/ui/client/package.json +27 -0
  10. package/src/ui/client/public/favicon.svg +27 -0
  11. package/src/ui/client/public/logo.svg +27 -0
  12. package/src/ui/client/public/vite.svg +1 -0
  13. package/src/ui/client/src/App.vue +539 -0
  14. package/src/ui/client/src/assets/logo.svg +27 -0
  15. package/src/ui/client/src/components/CommitForm.vue +904 -0
  16. package/src/ui/client/src/components/GitStatus.vue +799 -0
  17. package/src/ui/client/src/components/LogList.vue +270 -0
  18. package/src/ui/client/src/main.ts +4 -0
  19. package/src/ui/client/src/vite-env.d.ts +1 -0
  20. package/src/ui/client/stats.html +4949 -0
  21. package/src/ui/client/tsconfig.app.json +14 -0
  22. package/src/ui/client/tsconfig.json +7 -0
  23. package/src/ui/client/tsconfig.node.json +24 -0
  24. package/src/ui/client/vite.config.ts +48 -0
  25. package/src/ui/public/assets/index-BHmYZROy.css +1 -0
  26. package/src/ui/public/assets/index-kfMX1bxz.js +9 -0
  27. package/src/ui/public/assets/vendor-Dp0FkvMe.css +1 -0
  28. package/src/ui/public/assets/vendor-DxvF30ca.js +41 -0
  29. package/src/ui/public/favicon.svg +27 -0
  30. package/src/ui/public/index.html +16 -0
  31. package/src/ui/public/logo.svg +27 -0
  32. package/src/ui/public/vite.svg +1 -0
  33. package/src/ui/server/index.js +598 -0
  34. package/src/utils/index.js +4 -0
@@ -0,0 +1,904 @@
1
+ <script setup lang="ts">
2
+ import { ref, onMounted, defineEmits, computed, watch } from "vue";
3
+ import { ElMessage } from "element-plus";
4
+ import { Setting } from "@element-plus/icons-vue";
5
+
6
+ const emit = defineEmits(["commit-success", "push-success"]);
7
+ const commitMessage = ref("");
8
+ const commitBtnText = ref("提交");
9
+ const pushBtnText = ref("推送到远程");
10
+ const isCommitting = ref(false);
11
+ const isPushing = ref(false);
12
+ // 添加提交并推送的状态变量
13
+ const isCommitAndPushing = ref(false);
14
+ const commitAndPushBtnText = ref("提交并推送");
15
+ const placeholder = ref("输入提交信息...");
16
+ // 添加默认提交信息变量
17
+ const defaultCommitMessage = ref("");
18
+ const isStandardCommit = ref(false);
19
+ const commitType = ref("feat");
20
+ const commitScope = ref("");
21
+ const commitDescription = ref("");
22
+ const commitBody = ref("");
23
+ const commitFooter = ref("");
24
+
25
+ // 提交模板相关变量
26
+ const descriptionTemplates = ref<string[]>([]);
27
+ // 添加对话框可见性变量
28
+ const descriptionDialogVisible = ref(false);
29
+ const newTemplateName = ref("");
30
+
31
+ // 作用域模板相关变量
32
+ const scopeTemplates = ref<string[]>([]);
33
+ const scopeDialogVisible = ref(false);
34
+ const newScopeTemplate = ref("");
35
+
36
+ // 跳过钩子
37
+ const skipHooks = ref(false);
38
+
39
+ // 提交类型选项
40
+ const commitTypeOptions = [
41
+ { value: "feat", label: "feat: 新功能" },
42
+ { value: "fix", label: "fix: 修复bug" },
43
+ { value: "docs", label: "docs: 文档修改" },
44
+ { value: "style", label: "style: 样式修改" },
45
+ { value: "refactor", label: "refactor: 代码重构" },
46
+ { value: "test", label: "test: 测试代码" },
47
+ { value: "chore", label: "chore: 构建/工具修改" },
48
+ ];
49
+
50
+ // 监听标准化提交状态变化,保存到localStorage
51
+ watch(isStandardCommit, (newValue) => {
52
+ localStorage.setItem("zen-gitsync-standard-commit", newValue.toString());
53
+ });
54
+
55
+ // 监听跳过钩子状态变化,保存到localStorage
56
+ watch(skipHooks, (newValue) => {
57
+ localStorage.setItem("zen-gitsync-skip-hooks", newValue.toString());
58
+ });
59
+
60
+ // 计算最终的提交信息
61
+ const finalCommitMessage = computed(() => {
62
+ if (!isStandardCommit.value) {
63
+ // 如果用户没有输入提交信息,则使用默认提交信息
64
+ return commitMessage.value || defaultCommitMessage.value;
65
+ }
66
+
67
+ // 构建标准化提交信息
68
+ let message = `${commitType.value || ""}`;
69
+ if (commitScope.value) {
70
+ message += `(${commitScope.value})`;
71
+ }
72
+ message += `: ${commitDescription.value}`;
73
+
74
+ if (commitBody.value) {
75
+ message += `\n\n${commitBody.value}`;
76
+ }
77
+
78
+ if (commitFooter.value) {
79
+ message += `\n\n${commitFooter.value}`;
80
+ }
81
+
82
+ return message;
83
+ });
84
+
85
+ // 加载配置
86
+ async function loadConfig() {
87
+ try {
88
+ const response = await fetch("/api/config/getConfig");
89
+ const config = await response.json();
90
+ placeholder.value = `输入提交信息 (默认: ${config.defaultCommitMessage})`;
91
+ // 保存默认提交信息到变量中,以便后续使用
92
+ defaultCommitMessage.value = config.defaultCommitMessage || "";
93
+
94
+ // 加载描述模板
95
+ if (
96
+ config.descriptionTemplates &&
97
+ Array.isArray(config.descriptionTemplates)
98
+ ) {
99
+ descriptionTemplates.value = config.descriptionTemplates;
100
+ }
101
+
102
+ // 加载作用域模板
103
+ if (config.scopeTemplates && Array.isArray(config.scopeTemplates)) {
104
+ scopeTemplates.value = config.scopeTemplates;
105
+ }
106
+ } catch (error) {
107
+ console.error("加载配置失败:", error);
108
+ }
109
+ }
110
+
111
+ // 保存描述模板
112
+ async function saveDescriptionTemplate() {
113
+ if (!newTemplateName.value.trim()) {
114
+ ElMessage({
115
+ message: "请输入模板内容",
116
+ type: "warning",
117
+ });
118
+ return;
119
+ }
120
+
121
+ try {
122
+ // 检查是否已存在相同模板
123
+ if (descriptionTemplates.value.includes(newTemplateName.value)) {
124
+ ElMessage({
125
+ message: "该模板已存在",
126
+ type: "warning",
127
+ });
128
+ return;
129
+ }
130
+
131
+ // 添加到本地数组
132
+ descriptionTemplates.value.push(newTemplateName.value);
133
+
134
+ // 保存到服务器
135
+ const response = await fetch("/api/config/save-template", {
136
+ method: "POST",
137
+ headers: {
138
+ "Content-Type": "application/json",
139
+ },
140
+ body: JSON.stringify({
141
+ template: newTemplateName.value,
142
+ type: "description",
143
+ }),
144
+ });
145
+
146
+ const result = await response.json();
147
+ if (result.success) {
148
+ ElMessage({
149
+ message: "模板保存成功!",
150
+ type: "success",
151
+ });
152
+ newTemplateName.value = "";
153
+ } else {
154
+ ElMessage({
155
+ message: "模板保存失败: " + result.error,
156
+ type: "error",
157
+ });
158
+ }
159
+ } catch (error) {
160
+ ElMessage({
161
+ message: "模板保存失败: " + (error as Error).message,
162
+ type: "error",
163
+ });
164
+ }
165
+ }
166
+
167
+ // 保存作用域模板
168
+ async function saveScopeTemplate() {
169
+ if (!newScopeTemplate.value.trim()) {
170
+ ElMessage({
171
+ message: "请输入模板内容",
172
+ type: "warning",
173
+ });
174
+ return;
175
+ }
176
+
177
+ try {
178
+ // 检查是否已存在相同模板
179
+ if (scopeTemplates.value.includes(newScopeTemplate.value)) {
180
+ ElMessage({
181
+ message: "该模板已存在",
182
+ type: "warning",
183
+ });
184
+ return;
185
+ }
186
+
187
+ // 添加到本地数组
188
+ scopeTemplates.value.push(newScopeTemplate.value);
189
+
190
+ // 保存到服务器
191
+ const response = await fetch("/api/config/save-template", {
192
+ method: "POST",
193
+ headers: {
194
+ "Content-Type": "application/json",
195
+ },
196
+ body: JSON.stringify({
197
+ template: newScopeTemplate.value,
198
+ type: "scope",
199
+ }),
200
+ });
201
+
202
+ const result = await response.json();
203
+ if (result.success) {
204
+ ElMessage({
205
+ message: "作用域模板保存成功!",
206
+ type: "success",
207
+ });
208
+ newScopeTemplate.value = "";
209
+ } else {
210
+ ElMessage({
211
+ message: "作用域模板保存失败: " + result.error,
212
+ type: "error",
213
+ });
214
+ }
215
+ } catch (error) {
216
+ ElMessage({
217
+ message: "作用域模板保存失败: " + (error as Error).message,
218
+ type: "error",
219
+ });
220
+ }
221
+ }
222
+
223
+ // 删除描述模板
224
+ async function deleteDescriptionTemplate(template: string) {
225
+ try {
226
+ // 从本地数组中删除
227
+ const index = descriptionTemplates.value.indexOf(template);
228
+ if (index !== -1) {
229
+ descriptionTemplates.value.splice(index, 1);
230
+ }
231
+
232
+ // 从服务器删除
233
+ const response = await fetch("/api/config/delete-template", {
234
+ method: "POST",
235
+ headers: {
236
+ "Content-Type": "application/json",
237
+ },
238
+ body: JSON.stringify({
239
+ template,
240
+ type: "description",
241
+ }),
242
+ });
243
+
244
+ const result = await response.json();
245
+ if (result.success) {
246
+ ElMessage({
247
+ message: "模板删除成功!",
248
+ type: "success",
249
+ });
250
+ } else {
251
+ ElMessage({
252
+ message: "模板删除失败: " + result.error,
253
+ type: "error",
254
+ });
255
+ }
256
+ } catch (error) {
257
+ ElMessage({
258
+ message: "模板删除失败: " + (error as Error).message,
259
+ type: "error",
260
+ });
261
+ }
262
+ }
263
+
264
+ // 删除作用域模板
265
+ async function deleteScopeTemplate(template: string) {
266
+ try {
267
+ // 从本地数组中删除
268
+ const index = scopeTemplates.value.indexOf(template);
269
+ if (index !== -1) {
270
+ scopeTemplates.value.splice(index, 1);
271
+ }
272
+
273
+ // 从服务器删除
274
+ const response = await fetch("/api/config/delete-template", {
275
+ method: "POST",
276
+ headers: {
277
+ "Content-Type": "application/json",
278
+ },
279
+ body: JSON.stringify({
280
+ template,
281
+ type: "scope",
282
+ }),
283
+ });
284
+
285
+ const result = await response.json();
286
+ if (result.success) {
287
+ ElMessage({
288
+ message: "作用域模板删除成功!",
289
+ type: "success",
290
+ });
291
+ } else {
292
+ ElMessage({
293
+ message: "作用域模板删除失败: " + result.error,
294
+ type: "error",
295
+ });
296
+ }
297
+ } catch (error) {
298
+ ElMessage({
299
+ message: "作用域模板删除失败: " + (error as Error).message,
300
+ type: "error",
301
+ });
302
+ }
303
+ }
304
+
305
+ // 使用模板
306
+ function useTemplate(template: string) {
307
+ commitDescription.value = template;
308
+ descriptionDialogVisible.value = false;
309
+ }
310
+
311
+ // 使用作用域模板
312
+ function useScopeTemplate(template: string) {
313
+ commitScope.value = template;
314
+ scopeDialogVisible.value = false;
315
+ }
316
+
317
+ // 打开设置弹窗
318
+ function openDescriptionSettings() {
319
+ descriptionDialogVisible.value = true;
320
+ }
321
+
322
+ // 打开作用域设置弹窗
323
+ function openScopeSettings() {
324
+ scopeDialogVisible.value = true;
325
+ }
326
+
327
+
328
+ // 从localStorage加载标准化提交设置
329
+ function loadCommitPreference() {
330
+ const savedPreference = localStorage.getItem("zen-gitsync-standard-commit");
331
+ if (savedPreference !== null) {
332
+ isStandardCommit.value = savedPreference === "true";
333
+ }
334
+
335
+ // 加载跳过钩子设置
336
+ const savedSkipHooks = localStorage.getItem("zen-gitsync-skip-hooks");
337
+ if (savedSkipHooks !== null) {
338
+ skipHooks.value = savedSkipHooks === "true";
339
+ }
340
+ }
341
+
342
+ // 提交更改
343
+ async function commitChanges() {
344
+ const message = finalCommitMessage.value;
345
+ if (!message && isStandardCommit.value && !commitDescription.value) {
346
+ ElMessage({
347
+ message: "请输入提交描述",
348
+ type: "warning",
349
+ });
350
+ return;
351
+ }
352
+
353
+ try {
354
+ isCommitting.value = true;
355
+ commitBtnText.value = "提交中...";
356
+
357
+ // 先执行 git add .
358
+ const addResponse = await fetch("/api/add", {
359
+ method: "POST",
360
+ });
361
+
362
+ const addResult = await addResponse.json();
363
+ if (!addResult.success) {
364
+ ElMessage({
365
+ message: "添加文件失败: " + addResult.error,
366
+ type: "error",
367
+ });
368
+ return;
369
+ }
370
+
371
+ const response = await fetch("/api/commit", {
372
+ method: "POST",
373
+ headers: {
374
+ "Content-Type": "application/json",
375
+ },
376
+ body: JSON.stringify({
377
+ message,
378
+ // 添加一个标志,表示消息包含换行符
379
+ hasNewlines: message.includes("\n"),
380
+ // 添加 no-verify 选项
381
+ noVerify: skipHooks.value,
382
+ }),
383
+ });
384
+
385
+ const result = await response.json();
386
+ if (result.success) {
387
+ // 清空输入
388
+ if (isStandardCommit.value) {
389
+ commitDescription.value = "";
390
+ commitBody.value = "";
391
+ commitFooter.value = "";
392
+ } else {
393
+ commitMessage.value = "";
394
+ }
395
+
396
+ ElMessage({
397
+ message: "提交成功!",
398
+ type: "success",
399
+ });
400
+ // 发出提交成功事件
401
+ emit("commit-success");
402
+ } else {
403
+ ElMessage({
404
+ message: "提交失败: " + result.error,
405
+ type: "error",
406
+ });
407
+ }
408
+ } catch (error) {
409
+ ElMessage({
410
+ message: "提交失败: " + (error as Error).message,
411
+ type: "error",
412
+ });
413
+ } finally {
414
+ isCommitting.value = false;
415
+ commitBtnText.value = "提交";
416
+ }
417
+ }
418
+
419
+ // 推送更改
420
+ async function pushChanges() {
421
+ try {
422
+ isPushing.value = true;
423
+ pushBtnText.value = "推送中...";
424
+
425
+ const response = await fetch("/api/push", {
426
+ method: "POST",
427
+ });
428
+
429
+ const result = await response.json();
430
+ if (result.success) {
431
+ ElMessage({
432
+ message: "推送成功!",
433
+ type: "success",
434
+ });
435
+ // 发出推送成功事件
436
+ emit("push-success");
437
+ } else {
438
+ ElMessage({
439
+ message: "推送失败: " + result.error,
440
+ type: "error",
441
+ });
442
+ }
443
+ } catch (error) {
444
+ ElMessage({
445
+ message: "推送失败: " + (error as Error).message,
446
+ type: "error",
447
+ });
448
+ } finally {
449
+ isPushing.value = false;
450
+ pushBtnText.value = "推送到远程";
451
+ }
452
+ }
453
+
454
+ // 提交并推送更改
455
+ async function commitAndPush() {
456
+ const message = finalCommitMessage.value;
457
+ if (!message && isStandardCommit.value && !commitDescription.value) {
458
+ ElMessage({
459
+ message: "请输入提交描述",
460
+ type: "warning",
461
+ });
462
+ return;
463
+ }
464
+
465
+ try {
466
+ isCommitAndPushing.value = true;
467
+ commitAndPushBtnText.value = "处理中...";
468
+
469
+ // 先执行 git add .
470
+ const addResponse = await fetch("/api/add", {
471
+ method: "POST",
472
+ });
473
+
474
+ const addResult = await addResponse.json();
475
+ if (!addResult.success) {
476
+ ElMessage({
477
+ message: "添加文件失败: " + addResult.error,
478
+ type: "error",
479
+ });
480
+ return;
481
+ }
482
+
483
+ // 再提交
484
+ const commitResponse = await fetch("/api/commit", {
485
+ method: "POST",
486
+ headers: {
487
+ "Content-Type": "application/json",
488
+ },
489
+ body: JSON.stringify({
490
+ message,
491
+ // 添加一个标志,表示消息包含换行符
492
+ hasNewlines: message.includes("\n"),
493
+ // 添加 no-verify 选项
494
+ noVerify: skipHooks.value,
495
+ }),
496
+ });
497
+
498
+ const commitResult = await commitResponse.json();
499
+ if (!commitResult.success) {
500
+ ElMessage({
501
+ message: "提交失败: " + commitResult.error,
502
+ type: "error",
503
+ });
504
+ return;
505
+ }
506
+
507
+ // 清空输入
508
+ if (isStandardCommit.value) {
509
+ commitDescription.value = "";
510
+ commitBody.value = "";
511
+ commitFooter.value = "";
512
+ } else {
513
+ commitMessage.value = "";
514
+ }
515
+
516
+ // 再推送
517
+ const pushResponse = await fetch("/api/push", {
518
+ method: "POST",
519
+ });
520
+
521
+ const pushResult = await pushResponse.json();
522
+ if (pushResult.success) {
523
+ commitMessage.value = "";
524
+ ElMessage({
525
+ message: "提交并推送成功!",
526
+ type: "success",
527
+ });
528
+ // 发出提交和推送成功事件
529
+ emit("commit-success");
530
+ emit("push-success");
531
+ } else {
532
+ ElMessage({
533
+ message: "推送失败: " + pushResult.error,
534
+ type: "error",
535
+ });
536
+ }
537
+ } catch (error) {
538
+ ElMessage({
539
+ message: "操作失败: " + (error as Error).message,
540
+ type: "error",
541
+ });
542
+ } finally {
543
+ isCommitAndPushing.value = false;
544
+ commitAndPushBtnText.value = "提交并推送";
545
+ }
546
+ }
547
+
548
+
549
+ onMounted(() => {
550
+ loadConfig();
551
+ loadCommitPreference();
552
+ });
553
+ </script>
554
+
555
+ <template>
556
+ <div class="card">
557
+ <h2>提交更改</h2>
558
+
559
+ <div class="commit-options">
560
+ <div class="commit-mode-toggle">
561
+ <el-switch
562
+ v-model="isStandardCommit"
563
+ active-text="标准化提交"
564
+ inactive-text="普通提交"
565
+ />
566
+ </div>
567
+
568
+ <div class="no-verify-toggle">
569
+ <el-tooltip content="跳过 Git 钩子检查 (--no-verify)" placement="top">
570
+ <el-switch v-model="skipHooks" active-text="跳过钩子 (--no-verify)" />
571
+ </el-tooltip>
572
+ </div>
573
+ </div>
574
+
575
+ <!-- 普通提交表单 -->
576
+ <div v-if="!isStandardCommit" class="commit-form">
577
+ <el-input v-model="commitMessage" :placeholder="placeholder" clearable />
578
+ <el-button
579
+ type="primary"
580
+ @click="commitChanges"
581
+ :loading="isCommitting"
582
+ >{{ commitBtnText }}</el-button
583
+ >
584
+ </div>
585
+
586
+ <!-- 标准化提交表单 -->
587
+ <div v-else class="standard-commit-form">
588
+ <div class="standard-commit-header">
589
+ <el-select
590
+ v-model="commitType"
591
+ placeholder="提交类型"
592
+ class="type-select"
593
+ clearable
594
+ >
595
+ <el-option
596
+ v-for="item in commitTypeOptions"
597
+ :key="item.value"
598
+ :label="item.label"
599
+ :value="item.value"
600
+ />
601
+ </el-select>
602
+
603
+ <div class="scope-container">
604
+ <el-input
605
+ v-model="commitScope"
606
+ placeholder="作用域(可选)"
607
+ class="scope-input"
608
+ clearable
609
+ />
610
+ <el-button
611
+ type="primary"
612
+ :icon="Setting"
613
+ circle
614
+ size="small"
615
+ class="settings-button"
616
+ @click="openScopeSettings"
617
+ >
618
+ </el-button>
619
+ </div>
620
+
621
+ <div class="description-container">
622
+ <el-input
623
+ v-model="commitDescription"
624
+ placeholder="简短描述(必填)"
625
+ class="description-input"
626
+ clearable
627
+ />
628
+ <el-button
629
+ type="primary"
630
+ :icon="Setting"
631
+ circle
632
+ size="small"
633
+ class="settings-button"
634
+ @click="openDescriptionSettings"
635
+ >
636
+ </el-button>
637
+ </div>
638
+ </div>
639
+
640
+ <el-input
641
+ v-model="commitBody"
642
+ type="textarea"
643
+ :rows="4"
644
+ placeholder="正文(可选):详细描述本次提交的内容和原因"
645
+ class="body-input"
646
+ clearable
647
+ />
648
+
649
+ <el-input
650
+ v-model="commitFooter"
651
+ placeholder="页脚(可选):如 Closes #123"
652
+ class="footer-input"
653
+ clearable
654
+ />
655
+
656
+ <div class="preview-section">
657
+ <div class="preview-title">预览:</div>
658
+ <pre class="preview-content">{{ finalCommitMessage }}</pre>
659
+ </div>
660
+
661
+ <el-button
662
+ type="primary"
663
+ @click="commitChanges"
664
+ :loading="isCommitting"
665
+ >{{ commitBtnText }}</el-button
666
+ >
667
+ </div>
668
+
669
+ <div class="button-group">
670
+ <el-button type="success" @click="pushChanges" :loading="isPushing">{{
671
+ pushBtnText
672
+ }}</el-button>
673
+ <el-button
674
+ type="warning"
675
+ @click="commitAndPush"
676
+ :loading="isCommitAndPushing"
677
+ >{{ commitAndPushBtnText }}</el-button
678
+ >
679
+ </div>
680
+
681
+ <!-- 简短描述设置弹窗 -->
682
+ <el-dialog
683
+ title="简短描述模板设置"
684
+ v-model="descriptionDialogVisible"
685
+ width="80vw"
686
+ style="height: 80vh"
687
+ >
688
+ <div class="template-container">
689
+ <div class="template-form">
690
+ <el-input
691
+ v-model="newTemplateName"
692
+ placeholder="输入新模板内容"
693
+ class="template-input"
694
+ clearable
695
+ />
696
+ <el-button
697
+ type="primary"
698
+ @click="saveDescriptionTemplate"
699
+ :disabled="!newTemplateName.trim()"
700
+ >添加模板</el-button
701
+ >
702
+ </div>
703
+
704
+ <div class="template-list">
705
+ <h3>已保存模板</h3>
706
+ <el-empty
707
+ v-if="descriptionTemplates.length === 0"
708
+ description="暂无保存的模板"
709
+ />
710
+ <el-card
711
+ v-for="(template, index) in descriptionTemplates"
712
+ :key="index"
713
+ class="template-item"
714
+ >
715
+ <!-- 两端对齐 -->
716
+ <el-row justify="space-between" align="middle" style="width: 100%">
717
+ <div class="template-content">{{ template }}</div>
718
+ <div class="template-actions">
719
+ <el-button
720
+ type="primary"
721
+ size="small"
722
+ @click="useTemplate(template)"
723
+ >使用</el-button
724
+ >
725
+ <el-button
726
+ type="danger"
727
+ size="small"
728
+ @click="deleteDescriptionTemplate(template)"
729
+ >删除</el-button
730
+ >
731
+ </div>
732
+ </el-row>
733
+ </el-card>
734
+ </div>
735
+ </div>
736
+ </el-dialog>
737
+
738
+ <!-- 作用域设置弹窗 -->
739
+ <el-dialog
740
+ title="作用域模板设置"
741
+ v-model="scopeDialogVisible"
742
+ width="80%"
743
+ style="height: 80vh"
744
+ >
745
+ <div class="template-container">
746
+ <div class="template-form">
747
+ <el-input
748
+ v-model="newScopeTemplate"
749
+ placeholder="输入新作用域模板"
750
+ class="template-input"
751
+ clearable
752
+ />
753
+ <el-button
754
+ type="primary"
755
+ @click="saveScopeTemplate"
756
+ :disabled="!newScopeTemplate.trim()"
757
+ >添加模板</el-button
758
+ >
759
+ </div>
760
+
761
+ <div class="template-list">
762
+ <h3>已保存作用域</h3>
763
+ <el-empty
764
+ v-if="scopeTemplates.length === 0"
765
+ description="暂无保存的作用域"
766
+ />
767
+ <el-card
768
+ v-for="(template, index) in scopeTemplates"
769
+ :key="index"
770
+ class="template-item"
771
+ >
772
+ <el-row justify="space-between" align="middle" style="width: 100%">
773
+ <div class="template-content">{{ template }}</div>
774
+ <div class="template-actions">
775
+ <el-button
776
+ type="primary"
777
+ size="small"
778
+ @click="useScopeTemplate(template)"
779
+ >使用</el-button
780
+ >
781
+ <el-button
782
+ type="danger"
783
+ size="small"
784
+ @click="deleteScopeTemplate(template)"
785
+ >删除</el-button
786
+ >
787
+ </div>
788
+ </el-row>
789
+ </el-card>
790
+ </div>
791
+ </div>
792
+ </el-dialog>
793
+ </div>
794
+ </template>
795
+
796
+ <style scoped>
797
+ .commit-form {
798
+ display: flex;
799
+ margin-bottom: 15px;
800
+ gap: 10px;
801
+ }
802
+ .button-group {
803
+ display: flex;
804
+ gap: 10px;
805
+ }
806
+ .commit-mode-toggle {
807
+ margin-bottom: 15px;
808
+ }
809
+ .standard-commit-form {
810
+ display: flex;
811
+ flex-direction: column;
812
+ gap: 15px;
813
+ margin-bottom: 15px;
814
+ }
815
+ .standard-commit-header {
816
+ display: flex;
817
+ gap: 10px;
818
+ width: 100%;
819
+ }
820
+ .type-select {
821
+ width: 120px;
822
+ flex-shrink: 0;
823
+ }
824
+ .scope-container {
825
+ display: flex;
826
+ align-items: center;
827
+ gap: 5px;
828
+ flex-grow: 0;
829
+ width: 200px;
830
+ }
831
+ .scope-input {
832
+ flex-grow: 1;
833
+ }
834
+ .description-container {
835
+ display: flex;
836
+ align-items: center;
837
+ gap: 5px;
838
+ flex-grow: 1;
839
+ }
840
+ .description-input {
841
+ flex-grow: 1;
842
+ min-width: 200px;
843
+ }
844
+ .settings-button {
845
+ flex-shrink: 0;
846
+ }
847
+ .preview-section {
848
+ background-color: #f5f7fa;
849
+ padding: 10px;
850
+ border-radius: 4px;
851
+ }
852
+ .preview-title {
853
+ font-weight: bold;
854
+ margin-bottom: 5px;
855
+ }
856
+ .preview-content {
857
+ white-space: pre-wrap;
858
+ font-family: monospace;
859
+ margin: 0;
860
+ padding: 10px;
861
+ background-color: #ebeef5;
862
+ border-radius: 4px;
863
+ }
864
+ .template-container {
865
+ display: flex;
866
+ flex-direction: column;
867
+ height: calc(85vh - 100px);
868
+ overflow-y: auto;
869
+ }
870
+
871
+ .template-form {
872
+ margin-bottom: 20px;
873
+ }
874
+
875
+ .template-list {
876
+ flex: 1;
877
+ overflow-y: auto;
878
+ }
879
+ .template-input {
880
+ flex-grow: 1;
881
+ }
882
+ .template-list {
883
+ overflow-y: auto;
884
+ height: 100%;
885
+ }
886
+ .template-item {
887
+ margin-bottom: 10px;
888
+ }
889
+ .template-item:hover {
890
+ background-color: #f5f7fa;
891
+ }
892
+ .template-content {
893
+ flex-grow: 1;
894
+ margin-right: 10px;
895
+ word-break: break-all;
896
+ }
897
+ .template-actions {
898
+ display: flex;
899
+ gap: 5px;
900
+ justify-content: flex-end;
901
+ min-width: 120px;
902
+ flex-shrink: 0;
903
+ }
904
+ </style>