@tnotesjs/core 0.1.22 → 0.1.24

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 (101) hide show
  1. package/README.md +87 -87
  2. package/package.json +1 -1
  3. package/types/config.ts +60 -60
  4. package/types/index.ts +11 -11
  5. package/types/note.ts +33 -33
  6. package/vitepress/assets/icons/icon__check.svg +3 -3
  7. package/vitepress/assets/icons/icon__clipboard.svg +7 -7
  8. package/vitepress/assets/icons/icon__close.svg +1 -1
  9. package/vitepress/assets/icons/icon__collapse.svg +1 -1
  10. package/vitepress/assets/icons/icon__confirm.svg +1 -1
  11. package/vitepress/assets/icons/icon__copy.svg +4 -4
  12. package/vitepress/assets/icons/icon__focus.svg +1 -1
  13. package/vitepress/assets/icons/icon__fold.svg +3 -3
  14. package/vitepress/assets/icons/icon__folder.svg +1 -1
  15. package/vitepress/assets/icons/icon__fullscreen.svg +1 -1
  16. package/vitepress/assets/icons/icon__fullscreen_exit.svg +1 -1
  17. package/vitepress/assets/icons/icon__github.svg +3 -3
  18. package/vitepress/assets/icons/icon__mindmap.svg +1 -1
  19. package/vitepress/assets/icons/icon__next.svg +1 -1
  20. package/vitepress/assets/icons/icon__number_gray.svg +1 -1
  21. package/vitepress/assets/icons/icon__number_purple.svg +1 -1
  22. package/vitepress/assets/icons/icon__prev.svg +1 -1
  23. package/vitepress/assets/icons/icon__restore.svg +1 -1
  24. package/vitepress/assets/icons/icon__rotate.svg +4 -4
  25. package/vitepress/assets/icons/icon__search.svg +1 -1
  26. package/vitepress/assets/icons/icon__sidebar_collapsed.svg +1 -1
  27. package/vitepress/assets/icons/icon__sidebar_opened.svg +1 -1
  28. package/vitepress/assets/icons/icon__totop.svg +5 -5
  29. package/vitepress/assets/icons/icon__vscode.svg +5 -5
  30. package/vitepress/assets/icons/icon__zoom_fit.svg +1 -1
  31. package/vitepress/assets/icons/icon__zoom_in.svg +1 -1
  32. package/vitepress/assets/icons/icon__zoom_out.svg +1 -1
  33. package/vitepress/assets/icons/icon__zoom_reset.svg +1 -1
  34. package/vitepress/assets/icons/index.ts +39 -39
  35. package/vitepress/components/BilibiliOutsidePlayer/BilibiliOutsidePlayer.vue +20 -20
  36. package/vitepress/components/CodeBlockFullscreen/CodeBlockFullscreen.vue +369 -369
  37. package/vitepress/components/CodeBlockFullscreen/index.ts +209 -209
  38. package/vitepress/components/CodeBlockFullscreen/styles.css +126 -126
  39. package/vitepress/components/Discussions/Discussions.vue +242 -242
  40. package/vitepress/components/EnWordList/EnWordList.vue +664 -664
  41. package/vitepress/components/EnWordList/RightClickMenu.vue +87 -87
  42. package/vitepress/components/Footprints/Footprints.vue +472 -472
  43. package/vitepress/components/Layout/AboutModal.vue +329 -329
  44. package/vitepress/components/Layout/AboutPanel.vue +485 -485
  45. package/vitepress/components/Layout/ContentCollapse.vue +591 -591
  46. package/vitepress/components/Layout/CustomSidebar.vue +817 -817
  47. package/vitepress/components/Layout/DocBeforeControls.vue +145 -145
  48. package/vitepress/components/Layout/DocFooter.vue +233 -233
  49. package/vitepress/components/Layout/ImagePreview.vue +485 -485
  50. package/vitepress/components/Layout/Layout.vue +744 -744
  51. package/vitepress/components/Layout/NoteStatus.vue +140 -140
  52. package/vitepress/components/Layout/SidebarItems.vue +1083 -1083
  53. package/vitepress/components/Layout/SidebarNavBefore.vue +170 -170
  54. package/vitepress/components/Layout/SidebarResizeHandle.vue +368 -311
  55. package/vitepress/components/Layout/Swiper.vue +167 -167
  56. package/vitepress/components/Layout/ToggleFullContent.vue +45 -45
  57. package/vitepress/components/Layout/ToggleSidebar.vue +35 -35
  58. package/vitepress/components/Layout/composables/useCollapseControl.ts +88 -88
  59. package/vitepress/components/Layout/composables/useNoteConfig.ts +119 -119
  60. package/vitepress/components/Layout/composables/useNoteSave.ts +178 -178
  61. package/vitepress/components/Layout/composables/useNoteValidation.ts +85 -85
  62. package/vitepress/components/Layout/composables/useRedirect.ts +113 -113
  63. package/vitepress/components/Layout/composables/useSidebarLayout.ts +122 -122
  64. package/vitepress/components/Layout/composables/useVSCodeIntegration.ts +85 -85
  65. package/vitepress/components/Layout/homeReadme.data.ts +124 -124
  66. package/vitepress/components/MarkMap/MarkMap.vue +562 -562
  67. package/vitepress/components/Mermaid/Mermaid.vue +644 -644
  68. package/vitepress/components/NotesTable/NotesTable.vue +175 -175
  69. package/vitepress/components/NotesTable/README.md +67 -67
  70. package/vitepress/components/Settings/Settings.vue +711 -711
  71. package/vitepress/components/SidebarCard/MindMapView.vue +484 -484
  72. package/vitepress/components/SidebarCard/NotesTrendChart.vue +108 -108
  73. package/vitepress/components/SidebarCard/SidebarCard.vue +945 -945
  74. package/vitepress/components/Tooltip/Tooltip.vue +70 -70
  75. package/vitepress/components/constants.ts +109 -109
  76. package/vitepress/components/notesConfig.data.ts +73 -73
  77. package/vitepress/components/sidebar.data.ts +59 -59
  78. package/vitepress/components/tnotes-config.data.ts +21 -21
  79. package/vitepress/components/utils.ts +26 -26
  80. package/vitepress/config/index.ts +169 -169
  81. package/vitepress/configs/constants.ts +26 -26
  82. package/vitepress/configs/head.config.ts +25 -25
  83. package/vitepress/configs/index.ts +9 -9
  84. package/vitepress/configs/markdown-it.d.ts +23 -23
  85. package/vitepress/configs/markdown.config.ts +368 -368
  86. package/vitepress/configs/theme.config.ts +108 -108
  87. package/vitepress/plugins/buildProgressPlugin.ts +434 -434
  88. package/vitepress/plugins/getNoteByConfigIdPlugin.ts +107 -107
  89. package/vitepress/plugins/renameNotePlugin.ts +68 -68
  90. package/vitepress/plugins/sidebarStructurePlugin.ts +260 -260
  91. package/vitepress/plugins/updateConfigPlugin.ts +63 -63
  92. package/vitepress/theme/index.ts +137 -137
  93. package/vitepress/theme/styles/base.scss +66 -64
  94. package/vitepress/theme/styles/components/404.scss +31 -31
  95. package/vitepress/theme/styles/components/collapse.scss +172 -172
  96. package/vitepress/theme/styles/components/markmap.scss +101 -101
  97. package/vitepress/theme/styles/components/swiper.scss +255 -255
  98. package/vitepress/theme/styles/index.scss +25 -25
  99. package/vitepress/theme/styles/layout.scss +79 -78
  100. package/vitepress/theme/styles/utilities.scss +39 -39
  101. package/vitepress/theme/styles/vitepress-override.scss +25 -25
@@ -1,485 +1,485 @@
1
- <template>
2
- <div class="timeModalContent">
3
- <!-- 笔记编号(仅笔记页显示) -->
4
- <div
5
- class="timeLine"
6
- v-if="!isHomeReadme && currentNoteId"
7
- title="笔记编号"
8
- >
9
- <div class="timeLabel">
10
- <strong>🔢 笔记编号</strong>
11
- </div>
12
- <div class="timeValue">{{ currentNoteId }}</div>
13
- </div>
14
-
15
- <!-- 笔记标题(仅笔记页显示) -->
16
- <div
17
- class="timeLine"
18
- v-if="!isHomeReadme && currentNoteId"
19
- title="笔记标题"
20
- >
21
- <div class="timeLabel">
22
- <strong>📝 笔记标题</strong>
23
- </div>
24
- <div class="timeValue">
25
- <input
26
- v-model="editableNoteTitle"
27
- type="text"
28
- class="titleInput"
29
- :class="{ error: titleError }"
30
- :disabled="!isDev"
31
- @input="onTitleInput"
32
- @blur="onTitleBlur"
33
- placeholder="请输入笔记标题"
34
- />
35
- <div v-if="titleError" class="errorMessage">
36
- {{ titleError }}
37
- </div>
38
- </div>
39
- </div>
40
-
41
- <!-- 笔记简介(仅笔记页显示) -->
42
- <div
43
- class="timeLine"
44
- v-if="!isHomeReadme && currentNoteId"
45
- title="笔记简介"
46
- >
47
- <div class="timeLabel">
48
- <strong>📄 笔记简介</strong>
49
- </div>
50
- <div class="timeValue">
51
- <textarea
52
- v-model="editableDescription"
53
- class="descriptionInput"
54
- :disabled="!isDev"
55
- @input="onDescriptionInput"
56
- placeholder="请输入笔记的一句话简介(可选)"
57
- rows="2"
58
- />
59
- </div>
60
- </div>
61
-
62
- <!-- 笔记状态(仅笔记页显示且非开发环境只读) -->
63
- <div
64
- class="timeLine"
65
- v-if="!isHomeReadme && currentNoteId"
66
- title="笔记状态"
67
- >
68
- <div class="timeLabel">
69
- <strong>📝 完成状态</strong>
70
- </div>
71
- <div class="timeValue">
72
- <select
73
- v-model="editableNoteStatus"
74
- class="statusSelect"
75
- :disabled="!isDev"
76
- @change="onConfigChange"
77
- >
78
- <option :value="true">✅ 已完成</option>
79
- <option :value="false">⏰ 待处理</option>
80
- </select>
81
- </div>
82
- </div>
83
-
84
- <!-- 评论状态(仅笔记页显示且非开发环境只读) -->
85
- <div
86
- class="timeLine"
87
- v-if="!isHomeReadme && currentNoteId"
88
- title="评论状态"
89
- >
90
- <div class="timeLabel">
91
- <strong>🫧 评论状态</strong>
92
- </div>
93
- <div class="timeValue">
94
- <select
95
- v-model="editableDiscussionsEnabled"
96
- class="statusSelect"
97
- :disabled="!isDev"
98
- @change="onConfigChange"
99
- >
100
- <option :value="true">✅ 开启</option>
101
- <option :value="false">❌ 关闭</option>
102
- </select>
103
- </div>
104
- </div>
105
-
106
- <!-- 首次提交时间 -->
107
- <div class="timeLine" title="首次提交时间">
108
- <div class="timeLabel"><strong>⌛️ 首次提交</strong></div>
109
- <div class="timeValue">
110
- {{ formatOptionalDate(modalCreatedAt) }}
111
- </div>
112
- </div>
113
-
114
- <!-- 最近提交时间 -->
115
- <div class="timeLine" title="最近提交时间">
116
- <div class="timeLabel"><strong>⌛️ 最近提交</strong></div>
117
- <div class="timeValue">
118
- {{ formatOptionalDate(modalUpdatedAt) }}
119
- </div>
120
- </div>
121
-
122
- <!-- GitHub -->
123
- <div
124
- class="timeLine"
125
- v-if="modalGithubUrl"
126
- :title="
127
- isHomeReadme ? '在 GitHub 中打开知识库' : '在 GitHub 中打开当前笔记'
128
- "
129
- >
130
- <div class="timeLabel">
131
- <strong>🔗 GitHub</strong>
132
- </div>
133
- <div class="timeValue">
134
- <a
135
- :href="modalGithubUrl"
136
- target="_blank"
137
- rel="noopener"
138
- class="githubLink"
139
- >
140
- {{
141
- isHomeReadme ? '在 GitHub 中打开知识库' : '在 GitHub 中打开当前笔记'
142
- }}
143
- </a>
144
- </div>
145
- </div>
146
-
147
- <!-- GitHub Page -->
148
- <div
149
- class="timeLine"
150
- v-if="modalGithubPageUrl"
151
- :title="
152
- isHomeReadme
153
- ? '在 GitHub Page 中打开知识库'
154
- : '在 GitHub Page 中打开当前笔记'
155
- "
156
- >
157
- <div class="timeLabel">
158
- <strong>🔗 GitHub Page</strong>
159
- </div>
160
- <div class="timeValue">
161
- <a
162
- :href="modalGithubPageUrl"
163
- target="_blank"
164
- rel="noopener"
165
- class="githubLink"
166
- >
167
- {{
168
- isHomeReadme
169
- ? '在 GitHub Page 中打开知识库'
170
- : '在 GitHub Page 中打开当前笔记'
171
- }}
172
- </a>
173
- </div>
174
- </div>
175
-
176
- <!-- 完成进度(仅首页显示) -->
177
- <div
178
- class="timeLine"
179
- v-if="isHomeReadme && completionPercentage !== null"
180
- title="笔记完成进度"
181
- >
182
- <div class="timeLabel">
183
- <strong>📊 完成进度</strong>
184
- </div>
185
- <div class="timeValue">
186
- {{ completionPercentage }}% ({{ doneNotesLen }} / {{ totalNotesLen }})
187
- </div>
188
- </div>
189
- </div>
190
- </template>
191
-
192
- <script setup lang="ts">
193
- import { computed } from 'vue'
194
-
195
- import { formatDate } from '../utils'
196
-
197
- const props = defineProps<{
198
- isHomeReadme: boolean
199
- currentNoteId: string | null
200
- isDev: boolean
201
- editableNoteTitle: string
202
- editableDescription: string
203
- editableNoteStatus: boolean
204
- editableDiscussionsEnabled: boolean
205
- titleError: string
206
- modalCreatedAt: number | undefined
207
- modalUpdatedAt: number | undefined
208
- modalGithubUrl: string
209
- modalGithubPageUrl: string
210
- completionPercentage: number | null
211
- doneNotesLen: number
212
- totalNotesLen: number
213
- }>()
214
-
215
- const emit = defineEmits<{
216
- 'update:editableNoteTitle': [value: string]
217
- 'update:editableDescription': [value: string]
218
- 'update:editableNoteStatus': [value: boolean]
219
- 'update:editableDiscussionsEnabled': [value: boolean]
220
- 'update:titleError': [value: string]
221
- titleInput: []
222
- titleBlur: []
223
- descriptionInput: []
224
- configChange: []
225
- }>()
226
-
227
- const editableNoteTitle = computed({
228
- get: () => props.editableNoteTitle,
229
- set: (value) => emit('update:editableNoteTitle', value),
230
- })
231
-
232
- const editableDescription = computed({
233
- get: () => props.editableDescription,
234
- set: (value) => emit('update:editableDescription', value),
235
- })
236
-
237
- const editableNoteStatus = computed({
238
- get: () => props.editableNoteStatus,
239
- set: (value) => emit('update:editableNoteStatus', value),
240
- })
241
-
242
- const editableDiscussionsEnabled = computed({
243
- get: () => props.editableDiscussionsEnabled,
244
- set: (value) => emit('update:editableDiscussionsEnabled', value),
245
- })
246
-
247
- function onTitleInput() {
248
- emit('titleInput')
249
- }
250
-
251
- function onTitleBlur() {
252
- emit('titleBlur')
253
- }
254
-
255
- function onDescriptionInput() {
256
- emit('descriptionInput')
257
- }
258
-
259
- function onConfigChange() {
260
- emit('configChange')
261
- }
262
-
263
- function formatOptionalDate(timestamp: number | undefined) {
264
- return timestamp === undefined ? '-' : formatDate(timestamp)
265
- }
266
- </script>
267
-
268
- <style scoped lang="scss">
269
- /* time-modal-content: 通用容器 */
270
- .timeModalContent {
271
- display: flex;
272
- flex-direction: column;
273
- gap: 0.75rem;
274
- padding: 0.25rem 0;
275
- font-size: 0.95rem;
276
-
277
- /* 每一行:移动端默认纵向排列(标签在上,值在下),在宽屏时变成左右两栏 */
278
- .timeLine {
279
- display: flex;
280
- flex-direction: column;
281
- gap: 0.25rem;
282
- align-items: flex-start;
283
- padding: 0.5rem;
284
- border-radius: 8px;
285
- transition: background-color 0.2s ease;
286
-
287
- &:hover {
288
- background-color: var(--vp-c-bg-soft);
289
- }
290
- }
291
-
292
- /* 标签(左侧) */
293
- .timeLabel {
294
- font-weight: 700;
295
- color: var(--vp-c-text);
296
- font-size: 0.98rem;
297
-
298
- strong {
299
- display: inline-flex;
300
- align-items: center;
301
- gap: 0.25rem;
302
- }
303
- }
304
-
305
- /* 值(右侧/下方) */
306
- .timeValue {
307
- color: var(--vp-c-text-2);
308
- font-size: 0.88rem;
309
- line-height: 1.35;
310
- word-break: break-word;
311
- font-family: var(--vp-font-family-mono);
312
- }
313
-
314
- /* GitHub 链接样式 */
315
- .githubLink {
316
- color: var(--vp-c-brand-1);
317
- text-decoration: none;
318
- transition: all 0.2s ease;
319
- display: inline-flex;
320
- align-items: center;
321
- gap: 0.25rem;
322
-
323
- &:hover {
324
- color: var(--vp-c-brand-2);
325
- text-decoration: underline;
326
- }
327
- }
328
-
329
- /* 状态选择框样式 */
330
- .statusSelect {
331
- padding: 0.375rem 0.75rem;
332
- border: 1px solid var(--vp-c-divider);
333
- border-radius: 6px;
334
- background-color: var(--vp-c-bg);
335
- color: var(--vp-c-text-1);
336
- font-size: 0.875rem;
337
- font-family: inherit;
338
- cursor: pointer;
339
- transition: all 0.2s ease;
340
- min-width: 120px;
341
-
342
- &:hover:not(:disabled) {
343
- border-color: var(--vp-c-brand-1);
344
- background-color: var(--vp-c-bg-soft);
345
- }
346
-
347
- &:focus {
348
- outline: none;
349
- border-color: var(--vp-c-brand-1);
350
- box-shadow: 0 0 0 3px rgba(var(--vp-c-brand-rgb), 0.1);
351
- }
352
-
353
- &:disabled {
354
- opacity: 0.6;
355
- cursor: not-allowed;
356
- }
357
-
358
- option {
359
- padding: 0.5rem;
360
- background-color: var(--vp-c-bg);
361
- color: var(--vp-c-text-1);
362
- }
363
- }
364
-
365
- /* 标题输入框样式 */
366
- .titleInput {
367
- width: 100%;
368
- padding: 0.5rem 0.75rem;
369
- border: 1px solid var(--vp-c-divider);
370
- border-radius: 6px;
371
- background-color: var(--vp-c-bg);
372
- color: var(--vp-c-text-1);
373
- font-size: 0.875rem;
374
- font-family: inherit;
375
- transition: all 0.2s ease;
376
-
377
- &:hover:not(:disabled) {
378
- border-color: var(--vp-c-brand-1);
379
- background-color: var(--vp-c-bg-soft);
380
- }
381
-
382
- &:focus {
383
- outline: none;
384
- border-color: var(--vp-c-brand-1);
385
- box-shadow: 0 0 0 3px rgba(var(--vp-c-brand-rgb), 0.1);
386
- }
387
-
388
- &:disabled {
389
- opacity: 0.6;
390
- cursor: not-allowed;
391
- background-color: var(--vp-c-bg-soft);
392
- }
393
-
394
- &.error {
395
- border-color: var(--vp-c-danger-1);
396
-
397
- &:focus {
398
- box-shadow: 0 0 0 3px rgba(255, 0, 0, 0.1);
399
- }
400
- }
401
- }
402
-
403
- /* 错误提示样式 */
404
- .errorMessage {
405
- margin-top: 0.25rem;
406
- color: var(--vp-c-danger-1);
407
- font-size: 0.75rem;
408
- }
409
-
410
- /* 简介文本框样式 */
411
- .descriptionInput {
412
- width: 100%;
413
- padding: 0.5rem 0.75rem;
414
- border: 1px solid var(--vp-c-divider);
415
- border-radius: 6px;
416
- background-color: var(--vp-c-bg);
417
- color: var(--vp-c-text-1);
418
- font-size: 0.875rem;
419
- font-family: inherit;
420
- line-height: 1.5;
421
- resize: vertical;
422
- min-height: 3rem;
423
- transition: all 0.2s ease;
424
-
425
- &:hover:not(:disabled) {
426
- border-color: var(--vp-c-brand-1);
427
- background-color: var(--vp-c-bg-soft);
428
- }
429
-
430
- &:focus {
431
- outline: none;
432
- border-color: var(--vp-c-brand-1);
433
- box-shadow: 0 0 0 3px rgba(var(--vp-c-brand-rgb), 0.1);
434
- }
435
-
436
- &:disabled {
437
- opacity: 0.6;
438
- cursor: not-allowed;
439
- background-color: var(--vp-c-bg-soft);
440
- }
441
- }
442
- }
443
-
444
- /* 宽屏样式:在 >=720px 时,改为左右两栏布局 */
445
- @media (min-width: 720px) {
446
- .timeModalContent {
447
- gap: 1rem;
448
-
449
- .timeLine {
450
- flex-direction: row;
451
- align-items: center;
452
- justify-content: flex-start;
453
- gap: 1rem;
454
- }
455
-
456
- .timeLabel {
457
- width: 9.5rem;
458
- text-align: left;
459
- font-size: 1rem;
460
- }
461
-
462
- .timeValue {
463
- flex: 1;
464
- white-space: nowrap;
465
- overflow: hidden;
466
- text-overflow: ellipsis;
467
- }
468
- }
469
- }
470
-
471
- /* 更明显的可触控目标(移动端) */
472
- @media (max-width: 520px) {
473
- .timeModalContent {
474
- gap: 0.9rem;
475
-
476
- .timeLabel {
477
- font-size: 1.02rem;
478
- }
479
-
480
- .timeValue {
481
- font-size: 1rem;
482
- }
483
- }
484
- }
485
- </style>
1
+ <template>
2
+ <div class="timeModalContent">
3
+ <!-- 笔记编号(仅笔记页显示) -->
4
+ <div
5
+ class="timeLine"
6
+ v-if="!isHomeReadme && currentNoteId"
7
+ title="笔记编号"
8
+ >
9
+ <div class="timeLabel">
10
+ <strong>🔢 笔记编号</strong>
11
+ </div>
12
+ <div class="timeValue">{{ currentNoteId }}</div>
13
+ </div>
14
+
15
+ <!-- 笔记标题(仅笔记页显示) -->
16
+ <div
17
+ class="timeLine"
18
+ v-if="!isHomeReadme && currentNoteId"
19
+ title="笔记标题"
20
+ >
21
+ <div class="timeLabel">
22
+ <strong>📝 笔记标题</strong>
23
+ </div>
24
+ <div class="timeValue">
25
+ <input
26
+ v-model="editableNoteTitle"
27
+ type="text"
28
+ class="titleInput"
29
+ :class="{ error: titleError }"
30
+ :disabled="!isDev"
31
+ @input="onTitleInput"
32
+ @blur="onTitleBlur"
33
+ placeholder="请输入笔记标题"
34
+ />
35
+ <div v-if="titleError" class="errorMessage">
36
+ {{ titleError }}
37
+ </div>
38
+ </div>
39
+ </div>
40
+
41
+ <!-- 笔记简介(仅笔记页显示) -->
42
+ <div
43
+ class="timeLine"
44
+ v-if="!isHomeReadme && currentNoteId"
45
+ title="笔记简介"
46
+ >
47
+ <div class="timeLabel">
48
+ <strong>📄 笔记简介</strong>
49
+ </div>
50
+ <div class="timeValue">
51
+ <textarea
52
+ v-model="editableDescription"
53
+ class="descriptionInput"
54
+ :disabled="!isDev"
55
+ @input="onDescriptionInput"
56
+ placeholder="请输入笔记的一句话简介(可选)"
57
+ rows="2"
58
+ />
59
+ </div>
60
+ </div>
61
+
62
+ <!-- 笔记状态(仅笔记页显示且非开发环境只读) -->
63
+ <div
64
+ class="timeLine"
65
+ v-if="!isHomeReadme && currentNoteId"
66
+ title="笔记状态"
67
+ >
68
+ <div class="timeLabel">
69
+ <strong>📝 完成状态</strong>
70
+ </div>
71
+ <div class="timeValue">
72
+ <select
73
+ v-model="editableNoteStatus"
74
+ class="statusSelect"
75
+ :disabled="!isDev"
76
+ @change="onConfigChange"
77
+ >
78
+ <option :value="true">✅ 已完成</option>
79
+ <option :value="false">⏰ 待处理</option>
80
+ </select>
81
+ </div>
82
+ </div>
83
+
84
+ <!-- 评论状态(仅笔记页显示且非开发环境只读) -->
85
+ <div
86
+ class="timeLine"
87
+ v-if="!isHomeReadme && currentNoteId"
88
+ title="评论状态"
89
+ >
90
+ <div class="timeLabel">
91
+ <strong>🫧 评论状态</strong>
92
+ </div>
93
+ <div class="timeValue">
94
+ <select
95
+ v-model="editableDiscussionsEnabled"
96
+ class="statusSelect"
97
+ :disabled="!isDev"
98
+ @change="onConfigChange"
99
+ >
100
+ <option :value="true">✅ 开启</option>
101
+ <option :value="false">❌ 关闭</option>
102
+ </select>
103
+ </div>
104
+ </div>
105
+
106
+ <!-- 首次提交时间 -->
107
+ <div class="timeLine" title="首次提交时间">
108
+ <div class="timeLabel"><strong>⌛️ 首次提交</strong></div>
109
+ <div class="timeValue">
110
+ {{ formatOptionalDate(modalCreatedAt) }}
111
+ </div>
112
+ </div>
113
+
114
+ <!-- 最近提交时间 -->
115
+ <div class="timeLine" title="最近提交时间">
116
+ <div class="timeLabel"><strong>⌛️ 最近提交</strong></div>
117
+ <div class="timeValue">
118
+ {{ formatOptionalDate(modalUpdatedAt) }}
119
+ </div>
120
+ </div>
121
+
122
+ <!-- GitHub -->
123
+ <div
124
+ class="timeLine"
125
+ v-if="modalGithubUrl"
126
+ :title="
127
+ isHomeReadme ? '在 GitHub 中打开知识库' : '在 GitHub 中打开当前笔记'
128
+ "
129
+ >
130
+ <div class="timeLabel">
131
+ <strong>🔗 GitHub</strong>
132
+ </div>
133
+ <div class="timeValue">
134
+ <a
135
+ :href="modalGithubUrl"
136
+ target="_blank"
137
+ rel="noopener"
138
+ class="githubLink"
139
+ >
140
+ {{
141
+ isHomeReadme ? '在 GitHub 中打开知识库' : '在 GitHub 中打开当前笔记'
142
+ }}
143
+ </a>
144
+ </div>
145
+ </div>
146
+
147
+ <!-- GitHub Page -->
148
+ <div
149
+ class="timeLine"
150
+ v-if="modalGithubPageUrl"
151
+ :title="
152
+ isHomeReadme
153
+ ? '在 GitHub Page 中打开知识库'
154
+ : '在 GitHub Page 中打开当前笔记'
155
+ "
156
+ >
157
+ <div class="timeLabel">
158
+ <strong>🔗 GitHub Page</strong>
159
+ </div>
160
+ <div class="timeValue">
161
+ <a
162
+ :href="modalGithubPageUrl"
163
+ target="_blank"
164
+ rel="noopener"
165
+ class="githubLink"
166
+ >
167
+ {{
168
+ isHomeReadme
169
+ ? '在 GitHub Page 中打开知识库'
170
+ : '在 GitHub Page 中打开当前笔记'
171
+ }}
172
+ </a>
173
+ </div>
174
+ </div>
175
+
176
+ <!-- 完成进度(仅首页显示) -->
177
+ <div
178
+ class="timeLine"
179
+ v-if="isHomeReadme && completionPercentage !== null"
180
+ title="笔记完成进度"
181
+ >
182
+ <div class="timeLabel">
183
+ <strong>📊 完成进度</strong>
184
+ </div>
185
+ <div class="timeValue">
186
+ {{ completionPercentage }}% ({{ doneNotesLen }} / {{ totalNotesLen }})
187
+ </div>
188
+ </div>
189
+ </div>
190
+ </template>
191
+
192
+ <script setup lang="ts">
193
+ import { computed } from 'vue'
194
+
195
+ import { formatDate } from '../utils'
196
+
197
+ const props = defineProps<{
198
+ isHomeReadme: boolean
199
+ currentNoteId: string | null
200
+ isDev: boolean
201
+ editableNoteTitle: string
202
+ editableDescription: string
203
+ editableNoteStatus: boolean
204
+ editableDiscussionsEnabled: boolean
205
+ titleError: string
206
+ modalCreatedAt: number | undefined
207
+ modalUpdatedAt: number | undefined
208
+ modalGithubUrl: string
209
+ modalGithubPageUrl: string
210
+ completionPercentage: number | null
211
+ doneNotesLen: number
212
+ totalNotesLen: number
213
+ }>()
214
+
215
+ const emit = defineEmits<{
216
+ 'update:editableNoteTitle': [value: string]
217
+ 'update:editableDescription': [value: string]
218
+ 'update:editableNoteStatus': [value: boolean]
219
+ 'update:editableDiscussionsEnabled': [value: boolean]
220
+ 'update:titleError': [value: string]
221
+ titleInput: []
222
+ titleBlur: []
223
+ descriptionInput: []
224
+ configChange: []
225
+ }>()
226
+
227
+ const editableNoteTitle = computed({
228
+ get: () => props.editableNoteTitle,
229
+ set: (value) => emit('update:editableNoteTitle', value),
230
+ })
231
+
232
+ const editableDescription = computed({
233
+ get: () => props.editableDescription,
234
+ set: (value) => emit('update:editableDescription', value),
235
+ })
236
+
237
+ const editableNoteStatus = computed({
238
+ get: () => props.editableNoteStatus,
239
+ set: (value) => emit('update:editableNoteStatus', value),
240
+ })
241
+
242
+ const editableDiscussionsEnabled = computed({
243
+ get: () => props.editableDiscussionsEnabled,
244
+ set: (value) => emit('update:editableDiscussionsEnabled', value),
245
+ })
246
+
247
+ function onTitleInput() {
248
+ emit('titleInput')
249
+ }
250
+
251
+ function onTitleBlur() {
252
+ emit('titleBlur')
253
+ }
254
+
255
+ function onDescriptionInput() {
256
+ emit('descriptionInput')
257
+ }
258
+
259
+ function onConfigChange() {
260
+ emit('configChange')
261
+ }
262
+
263
+ function formatOptionalDate(timestamp: number | undefined) {
264
+ return timestamp === undefined ? '-' : formatDate(timestamp)
265
+ }
266
+ </script>
267
+
268
+ <style scoped lang="scss">
269
+ /* time-modal-content: 通用容器 */
270
+ .timeModalContent {
271
+ display: flex;
272
+ flex-direction: column;
273
+ gap: 0.75rem;
274
+ padding: 0.25rem 0;
275
+ font-size: 0.95rem;
276
+
277
+ /* 每一行:移动端默认纵向排列(标签在上,值在下),在宽屏时变成左右两栏 */
278
+ .timeLine {
279
+ display: flex;
280
+ flex-direction: column;
281
+ gap: 0.25rem;
282
+ align-items: flex-start;
283
+ padding: 0.5rem;
284
+ border-radius: 8px;
285
+ transition: background-color 0.2s ease;
286
+
287
+ &:hover {
288
+ background-color: var(--vp-c-bg-soft);
289
+ }
290
+ }
291
+
292
+ /* 标签(左侧) */
293
+ .timeLabel {
294
+ font-weight: 700;
295
+ color: var(--vp-c-text);
296
+ font-size: 0.98rem;
297
+
298
+ strong {
299
+ display: inline-flex;
300
+ align-items: center;
301
+ gap: 0.25rem;
302
+ }
303
+ }
304
+
305
+ /* 值(右侧/下方) */
306
+ .timeValue {
307
+ color: var(--vp-c-text-2);
308
+ font-size: 0.88rem;
309
+ line-height: 1.35;
310
+ word-break: break-word;
311
+ font-family: var(--vp-font-family-mono);
312
+ }
313
+
314
+ /* GitHub 链接样式 */
315
+ .githubLink {
316
+ color: var(--vp-c-brand-1);
317
+ text-decoration: none;
318
+ transition: all 0.2s ease;
319
+ display: inline-flex;
320
+ align-items: center;
321
+ gap: 0.25rem;
322
+
323
+ &:hover {
324
+ color: var(--vp-c-brand-2);
325
+ text-decoration: underline;
326
+ }
327
+ }
328
+
329
+ /* 状态选择框样式 */
330
+ .statusSelect {
331
+ padding: 0.375rem 0.75rem;
332
+ border: 1px solid var(--vp-c-divider);
333
+ border-radius: 6px;
334
+ background-color: var(--vp-c-bg);
335
+ color: var(--vp-c-text-1);
336
+ font-size: 0.875rem;
337
+ font-family: inherit;
338
+ cursor: pointer;
339
+ transition: all 0.2s ease;
340
+ min-width: 120px;
341
+
342
+ &:hover:not(:disabled) {
343
+ border-color: var(--vp-c-brand-1);
344
+ background-color: var(--vp-c-bg-soft);
345
+ }
346
+
347
+ &:focus {
348
+ outline: none;
349
+ border-color: var(--vp-c-brand-1);
350
+ box-shadow: 0 0 0 3px rgba(var(--vp-c-brand-rgb), 0.1);
351
+ }
352
+
353
+ &:disabled {
354
+ opacity: 0.6;
355
+ cursor: not-allowed;
356
+ }
357
+
358
+ option {
359
+ padding: 0.5rem;
360
+ background-color: var(--vp-c-bg);
361
+ color: var(--vp-c-text-1);
362
+ }
363
+ }
364
+
365
+ /* 标题输入框样式 */
366
+ .titleInput {
367
+ width: 100%;
368
+ padding: 0.5rem 0.75rem;
369
+ border: 1px solid var(--vp-c-divider);
370
+ border-radius: 6px;
371
+ background-color: var(--vp-c-bg);
372
+ color: var(--vp-c-text-1);
373
+ font-size: 0.875rem;
374
+ font-family: inherit;
375
+ transition: all 0.2s ease;
376
+
377
+ &:hover:not(:disabled) {
378
+ border-color: var(--vp-c-brand-1);
379
+ background-color: var(--vp-c-bg-soft);
380
+ }
381
+
382
+ &:focus {
383
+ outline: none;
384
+ border-color: var(--vp-c-brand-1);
385
+ box-shadow: 0 0 0 3px rgba(var(--vp-c-brand-rgb), 0.1);
386
+ }
387
+
388
+ &:disabled {
389
+ opacity: 0.6;
390
+ cursor: not-allowed;
391
+ background-color: var(--vp-c-bg-soft);
392
+ }
393
+
394
+ &.error {
395
+ border-color: var(--vp-c-danger-1);
396
+
397
+ &:focus {
398
+ box-shadow: 0 0 0 3px rgba(255, 0, 0, 0.1);
399
+ }
400
+ }
401
+ }
402
+
403
+ /* 错误提示样式 */
404
+ .errorMessage {
405
+ margin-top: 0.25rem;
406
+ color: var(--vp-c-danger-1);
407
+ font-size: 0.75rem;
408
+ }
409
+
410
+ /* 简介文本框样式 */
411
+ .descriptionInput {
412
+ width: 100%;
413
+ padding: 0.5rem 0.75rem;
414
+ border: 1px solid var(--vp-c-divider);
415
+ border-radius: 6px;
416
+ background-color: var(--vp-c-bg);
417
+ color: var(--vp-c-text-1);
418
+ font-size: 0.875rem;
419
+ font-family: inherit;
420
+ line-height: 1.5;
421
+ resize: vertical;
422
+ min-height: 3rem;
423
+ transition: all 0.2s ease;
424
+
425
+ &:hover:not(:disabled) {
426
+ border-color: var(--vp-c-brand-1);
427
+ background-color: var(--vp-c-bg-soft);
428
+ }
429
+
430
+ &:focus {
431
+ outline: none;
432
+ border-color: var(--vp-c-brand-1);
433
+ box-shadow: 0 0 0 3px rgba(var(--vp-c-brand-rgb), 0.1);
434
+ }
435
+
436
+ &:disabled {
437
+ opacity: 0.6;
438
+ cursor: not-allowed;
439
+ background-color: var(--vp-c-bg-soft);
440
+ }
441
+ }
442
+ }
443
+
444
+ /* 宽屏样式:在 >=720px 时,改为左右两栏布局 */
445
+ @media (min-width: 720px) {
446
+ .timeModalContent {
447
+ gap: 1rem;
448
+
449
+ .timeLine {
450
+ flex-direction: row;
451
+ align-items: center;
452
+ justify-content: flex-start;
453
+ gap: 1rem;
454
+ }
455
+
456
+ .timeLabel {
457
+ width: 9.5rem;
458
+ text-align: left;
459
+ font-size: 1rem;
460
+ }
461
+
462
+ .timeValue {
463
+ flex: 1;
464
+ white-space: nowrap;
465
+ overflow: hidden;
466
+ text-overflow: ellipsis;
467
+ }
468
+ }
469
+ }
470
+
471
+ /* 更明显的可触控目标(移动端) */
472
+ @media (max-width: 520px) {
473
+ .timeModalContent {
474
+ gap: 0.9rem;
475
+
476
+ .timeLabel {
477
+ font-size: 1.02rem;
478
+ }
479
+
480
+ .timeValue {
481
+ font-size: 1rem;
482
+ }
483
+ }
484
+ }
485
+ </style>