@sugarat/theme 0.4.13 → 0.5.1

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 (36) hide show
  1. package/node.d.ts +13 -1
  2. package/node.js +147 -52
  3. package/node.mjs +755 -0
  4. package/package.json +13 -10
  5. package/src/components/BlogAlert.vue +10 -9
  6. package/src/components/BlogArticleAnalyze.vue +8 -9
  7. package/src/components/BlogAuthor.vue +6 -5
  8. package/src/components/BlogBackToTop.vue +8 -10
  9. package/src/components/BlogButtonAfterArticle.vue +5 -14
  10. package/src/components/BlogCommentWrapper.vue +11 -28
  11. package/src/components/BlogDocCover.vue +3 -3
  12. package/src/components/BlogFooter.vue +3 -1
  13. package/src/components/BlogFriendLink.vue +4 -3
  14. package/src/components/BlogHomeBanner.vue +7 -6
  15. package/src/components/BlogHomeHeaderAvatar.vue +3 -3
  16. package/src/components/BlogHomeOverview.vue +4 -4
  17. package/src/components/BlogHomeTags.vue +5 -5
  18. package/src/components/BlogHotArticle.vue +4 -7
  19. package/src/components/BlogItem.vue +4 -3
  20. package/src/components/BlogList.vue +7 -6
  21. package/src/components/BlogRecommendArticle.vue +11 -14
  22. package/src/components/BlogSidebar.vue +9 -15
  23. package/src/components/CommentArtalk.vue +7 -5
  24. package/src/components/CommentGiscus.vue +7 -8
  25. package/src/components/Icon.vue +33 -0
  26. package/src/composables/config/blog.ts +207 -89
  27. package/src/composables/config/index.ts +9 -0
  28. package/src/hooks/useOml2d.ts +15 -8
  29. package/src/index.ts +3 -0
  30. package/src/node.ts +5 -1
  31. package/src/styles/index.scss +6 -6
  32. package/src/utils/node/hot-reload-plugin.ts +31 -1
  33. package/src/utils/node/index.ts +0 -2
  34. package/src/utils/node/mdPlugins.ts +4 -0
  35. package/src/utils/node/theme.ts +15 -18
  36. package/src/utils/node/vitePlugins.ts +120 -34
@@ -3,27 +3,21 @@ import { computed, onMounted, ref } from 'vue'
3
3
  import { useRoute, useRouter, withBase } from 'vitepress'
4
4
  import { ElButton } from 'element-plus'
5
5
  import { wrapperCleanUrls } from '../utils/client'
6
- import { useArticles, useBlogConfig, useCleanUrls, useFormatShowDate } from '../composables/config/blog'
6
+ import { useArticles, useCleanUrls, useFormatShowDate, useRecommendConfig, useShowRecommend } from '../composables/config/blog'
7
7
  import { recommendSVG } from '../constants/svg'
8
8
  import type { Theme } from '../composables/config/index'
9
9
 
10
10
  const formatShowDate = useFormatShowDate()
11
11
 
12
- const { recommend: _recommend } = useBlogConfig()
12
+ const recommend = useRecommendConfig()
13
+ const show = useShowRecommend()
13
14
 
14
15
  const sidebarStyle = computed(() =>
15
- _recommend && _recommend?.style ? _recommend.style : 'sidebar'
16
+ recommend.value?.style ?? 'sidebar'
16
17
  )
17
18
 
18
- const recommendPadding = computed(() =>
19
- sidebarStyle.value === 'card' ? '10px' : '0px'
20
- )
21
- const recommend = computed(() =>
22
- _recommend === false ? undefined : _recommend
23
- )
24
-
25
- const showDate = computed(() => (_recommend && _recommend?.showDate) ?? true)
26
- const showNum = computed(() => (_recommend && _recommend?.showNum) ?? true)
19
+ const showDate = computed(() => recommend.value?.showDate ?? true)
20
+ const showNum = computed(() => recommend.value?.showNum ?? true)
27
21
 
28
22
  const title = computed(() => recommend.value?.title ?? (`<span class="svg-icon">${recommendSVG}</span>` + '相关文章'))
29
23
  const pageSize = computed(() => recommend.value?.pageSize || 9)
@@ -162,7 +156,7 @@ function handleLinkClick(link: string) {
162
156
 
163
157
  <template>
164
158
  <div
165
- v-if="_recommend !== false && (recommendList.length || emptyText)" class="recommend"
159
+ v-if="show && (recommendList.length || emptyText)" class="recommend"
166
160
  :class="{ card: sidebarStyle === 'card' }" data-pagefind-ignore="all"
167
161
  >
168
162
  <!-- 头部 -->
@@ -233,7 +227,10 @@ function handleLinkClick(link: string) {
233
227
 
234
228
  .recommend {
235
229
  flex-direction: column;
236
- padding: v-bind(recommendPadding);
230
+ padding: 0px;
231
+ }
232
+ .recommend.card{
233
+ padding: 10px;
237
234
  }
238
235
 
239
236
  .recommend-container {
@@ -1,35 +1,29 @@
1
1
  <script lang="ts" setup>
2
2
  import { computed } from 'vue'
3
- import { useBlogConfig } from '../composables/config/blog'
3
+ import { useRecommendConfig, useShowRecommend } from '../composables/config/blog'
4
4
  import BlogRecommendArticle from './BlogRecommendArticle.vue'
5
5
 
6
- const { recommend: _recommend } = useBlogConfig()
7
-
6
+ const recommend = useRecommendConfig()
7
+ const show = useShowRecommend()
8
8
  const sidebarStyle = computed(() =>
9
- _recommend && _recommend?.style ? _recommend.style : 'card'
10
- )
11
- const marginTop = computed(() =>
12
- sidebarStyle.value === 'card' ? '40px' : '0px'
13
- )
14
- const marginTopMini = computed(() =>
15
- sidebarStyle.value === 'card' ? '60px' : '0px'
9
+ recommend.value?.style || 'sidebar'
16
10
  )
17
11
  </script>
18
12
 
19
13
  <template>
20
- <div v-if="_recommend !== false" class="sidebar" data-pagefind-ignore="all">
14
+ <div v-if="show" class="sidebar" :class="{ card: sidebarStyle === 'card' }" data-pagefind-ignore="all">
21
15
  <BlogRecommendArticle />
22
16
  </div>
23
17
  </template>
24
18
 
25
19
  <style lang="scss" scoped>
26
- .sidebar {
27
- margin-top: v-bind(marginTop);
20
+ .sidebar.card {
21
+ margin-top: 40px;
28
22
  }
29
23
 
30
24
  @media screen and (min-width: 960px) and (max-width: 1120px) {
31
- .sidebar {
32
- margin-top: v-bind(marginTopMini);
25
+ .sidebar.card {
26
+ margin-top: 60px;
33
27
  }
34
28
  }
35
29
  </style>
@@ -3,7 +3,7 @@ import { computed, onMounted, onUnmounted, ref, watch } from 'vue'
3
3
  import { useData, useRoute } from 'vitepress'
4
4
  import type Artalk from 'artalk'
5
5
 
6
- import { useBlogConfig } from '../composables/config/blog'
6
+ import { useCommentConfig, useOpenCommentConfig } from '../composables/config/blog'
7
7
 
8
8
  const { isDark, page } = useData()
9
9
  const el = ref<HTMLDivElement>()
@@ -12,14 +12,16 @@ const route = useRoute()
12
12
 
13
13
  const artalk = ref<Artalk>()
14
14
 
15
- const { comment } = useBlogConfig()
15
+ const comment = useCommentConfig()
16
+
16
17
  const commentConfig = computed(() => {
17
- if (comment && 'type' in comment && comment.type === 'artalk') {
18
- return comment.options
18
+ if (comment.value && 'type' in comment.value && comment.value?.type === 'artalk') {
19
+ return comment.value.options
19
20
  }
20
21
 
21
22
  return false
22
23
  })
24
+ const open = useOpenCommentConfig()
23
25
 
24
26
  onMounted(() => {
25
27
  // CDN 异步加载,有优化空间
@@ -64,7 +66,7 @@ watch(isDark, () => {
64
66
  </script>
65
67
 
66
68
  <template>
67
- <div v-if="commentConfig" ref="el" class="artalk-container" />
69
+ <div v-if="open" ref="el" class="artalk-container" />
68
70
  </template>
69
71
 
70
72
  <style lang="scss" scoped>
@@ -2,19 +2,18 @@
2
2
  import { useData, useRoute } from 'vitepress'
3
3
  import { computed, nextTick, ref, watch } from 'vue'
4
4
  import Giscus from '@giscus/vue'
5
- import { useBlogConfig } from '../composables/config/blog'
5
+ import { useCommentConfig } from '../composables/config/blog'
6
6
 
7
- // 读取配制
8
- const { comment } = useBlogConfig()
7
+ const comment = useCommentConfig()
9
8
  const commentConfig = computed(() => {
10
- if (!comment) {
9
+ if (!comment.value) {
11
10
  return false
12
11
  }
13
- if ('type' in comment && comment.type === 'giscus') {
14
- return comment.options
12
+ if ('type' in comment.value && comment.value?.type === 'giscus') {
13
+ return comment.value.options
15
14
  }
16
- else if (!('type' in comment)) {
17
- return comment
15
+ else if (!('type' in comment.value)) {
16
+ return comment.value
18
17
  }
19
18
 
20
19
  return false
@@ -0,0 +1,33 @@
1
+ <script lang="ts" setup>
2
+ import { computed } from 'vue'
3
+
4
+ const props = defineProps<{
5
+ size?: string | number
6
+ icon?: string
7
+ }>()
8
+
9
+ const size = computed(() => props.size && (typeof props.size === 'number' ? `${props.size}px` : props.size))
10
+ </script>
11
+
12
+ <template>
13
+ <i v-if="props.icon" class="sugar-theme-icon" :style="{ fontSize: size }" v-html="props.icon" />
14
+ <i v-else class="sugar-theme-icon" :style="{ fontSize: size }">
15
+ <slot />
16
+ </i>
17
+ </template>
18
+
19
+ <style lang="css" scoped>
20
+ .sugar-theme-icon {
21
+ --color: inherit;
22
+ align-items: center;
23
+ display: inline-flex;
24
+ height: 1em;
25
+ justify-content: center;
26
+ line-height: 1em;
27
+ position: relative;
28
+ width: 1em;
29
+ fill: currentColor;
30
+ color: var(--color);
31
+ font-size: inherit;
32
+ }
33
+ </style>
@@ -14,7 +14,8 @@ import {
14
14
  onUnmounted,
15
15
  provide,
16
16
  reactive,
17
- ref
17
+ ref,
18
+ watch,
18
19
  } from 'vue'
19
20
  import { useColorMode } from '@vueuse/core'
20
21
 
@@ -27,28 +28,14 @@ const activeTagSymbol: InjectionKey<Ref<Theme.activeTag>> = Symbol('active-tag')
27
28
 
28
29
  const currentPageNum: InjectionKey<Ref<number>> = Symbol('home-page-num')
29
30
 
30
- const userWorks: InjectionKey<Ref<Theme.UserWorks>> = Symbol('user-works')
31
-
32
- const homeFooter: InjectionKey<Theme.Footer | Theme.Footer[] | undefined> = Symbol('home-footer')
33
-
34
31
  export function withConfigProvider(App: Component) {
35
32
  return defineComponent({
36
33
  name: 'ConfigProvider',
37
34
  setup(_, { slots }) {
38
- const { theme } = useData()
39
- const config = computed(() => resolveConfig(theme.value))
40
- provide(homeFooter, config.value.blog?.footer)
35
+ const { theme, localeIndex } = useData()
36
+ const config = computed(() => resolveConfig(theme.value, localeIndex.value))
37
+
41
38
  provide(configSymbol, config)
42
- provide(
43
- userWorks,
44
- ref(
45
- config.value.blog?.works || {
46
- title: '',
47
- description: '',
48
- list: []
49
- }
50
- )
51
- )
52
39
 
53
40
  const activeTag = ref<Theme.activeTag>({
54
41
  label: '',
@@ -73,37 +60,122 @@ export function withConfigProvider(App: Component) {
73
60
  'el-red': 'el-red'
74
61
  }
75
62
  })
76
- mode.value = config.value.blog?.themeColor ?? 'vp-default'
63
+ watch(config, () => {
64
+ mode.value = config.value.blog?.themeColor ?? 'vp-default'
65
+ }, {
66
+ immediate: true
67
+ })
68
+
77
69
  return () => h(App, null, slots)
78
70
  }
79
71
  })
80
72
  }
73
+
74
+ export function useGlobalAuthor() {
75
+ const blogConfig = useBlogConfig()
76
+ return computed(() => blogConfig.value?.author || '')
77
+ }
78
+
79
+ export function useArticleConfig() {
80
+ const blogConfig = useBlogConfig()
81
+ return computed(() => blogConfig.value?.article)
82
+ }
83
+
84
+ export function useAuthorList() {
85
+ const blogConfig = useBlogConfig()
86
+ return computed(() => blogConfig.value?.authorList)
87
+ }
88
+
89
+ export function useHotArticleConfig() {
90
+ const blogConfig = useBlogConfig()
91
+ return computed(() => {
92
+ const cfg = blogConfig.value?.hotArticle
93
+ return cfg === false ? undefined : cfg
94
+ })
95
+ }
96
+
97
+ export function useShowHotArticle() {
98
+ const blogConfig = useBlogConfig()
99
+ return computed(() => {
100
+ const cfg = blogConfig.value?.hotArticle
101
+ return cfg !== false
102
+ })
103
+ }
104
+
105
+ export function useRecommendConfig() {
106
+ const blogConfig = useBlogConfig()
107
+ return computed(() => {
108
+ const cfg = blogConfig.value?.recommend
109
+ return cfg === false ? undefined : cfg
110
+ })
111
+ }
112
+
113
+ export function useShowRecommend() {
114
+ const blogConfig = useBlogConfig()
115
+ return computed(() => {
116
+ const cfg = blogConfig.value?.recommend
117
+ return cfg !== false
118
+ })
119
+ }
120
+
121
+ export function useAlertConfig() {
122
+ const blogConfig = useBlogConfig()
123
+ return computed(() => blogConfig.value?.alert)
124
+ }
125
+
126
+ export function useHomeConfig() {
127
+ const blogConfig = useBlogConfig()
128
+ return computed(() => blogConfig.value?.home)
129
+ }
130
+
131
+ export function useHomeTagsConfig() {
132
+ const blogConfig = useBlogConfig()
133
+ return computed(() => blogConfig.value?.homeTags)
134
+ }
135
+
81
136
  export function useDocMetaInsertSelector() {
82
137
  const blogConfig = useConfig()
83
138
  const { frontmatter } = useData()
84
- return computed(() => frontmatter.value?.docMetaInsertSelector || blogConfig.config?.blog?.docMetaInsertSelector || 'h1')
139
+ return computed(() => frontmatter.value?.docMetaInsertSelector || blogConfig?.value?.blog?.docMetaInsertSelector || 'h1')
85
140
  }
86
141
 
87
142
  export function useDocMetaInsertPosition() {
88
143
  const blogConfig = useConfig()
89
144
  const { frontmatter } = useData()
90
- return computed(() => frontmatter.value?.docMetaInsertPosition || blogConfig.config?.blog?.docMetaInsertPosition || 'after')
145
+ return computed(() => frontmatter.value?.docMetaInsertPosition || blogConfig?.value?.blog?.docMetaInsertPosition || 'after')
91
146
  }
92
147
 
93
148
  export function useConfig() {
94
- return {
95
- config: inject(configSymbol)!.value
96
- }
149
+ return inject(configSymbol)
97
150
  }
98
151
 
99
152
  export function useBlogConfig() {
100
- return inject(configSymbol)!.value.blog!
153
+ const resolveConfig = useConfig()
154
+ return computed(() => resolveConfig?.value?.blog)
101
155
  }
156
+
157
+ export function useButtonAfterConfig() {
158
+ const blogConfig = useBlogConfig()
159
+ const { frontmatter } = useData()
160
+ const frontmatterConfig = computed(() => frontmatter.value.buttonAfterArticle)
161
+
162
+ const buttonAfterArticleConfig = computed<Theme.ButtonAfterArticleConfig | false>(() => {
163
+ if (frontmatterConfig.value === false || (!frontmatterConfig.value && !blogConfig.value?.buttonAfterArticle)) {
164
+ return false
165
+ }
166
+
167
+ return { ...blogConfig.value?.buttonAfterArticle, ...frontmatterConfig.value }
168
+ })
169
+
170
+ return buttonAfterArticleConfig
171
+ }
172
+
102
173
  /**
103
174
  * 获取 oh-my-live2d的配置选项
104
175
  */
105
176
  export function useOml2dOptions() {
106
- return inject(configSymbol)!.value.blog?.oml2d
177
+ const blogConfig = useBlogConfig()
178
+ return computed(() => blogConfig.value?.oml2d)
107
179
  }
108
180
 
109
181
  export function useDarkTransitionConfig() {
@@ -116,7 +188,16 @@ export function useBlogThemeMode() {
116
188
 
117
189
  export function useArticles() {
118
190
  const blogConfig = useConfig()
119
- const articles = computed(() => blogConfig.config?.blog?.pagesData || [])
191
+ const { localeIndex, site } = useData()
192
+
193
+ const localeKeys = computed(() => Object.keys(site.value.locales))
194
+
195
+ const articles = computed(() => {
196
+ if (localeKeys.value.length === 0) {
197
+ return (blogConfig?.value?.blog?.pagesData || [])
198
+ }
199
+ return blogConfig?.value?.blog?.locales?.[localeIndex.value]?.pagesData || []
200
+ })
120
201
  return articles
121
202
  }
122
203
 
@@ -128,10 +209,9 @@ export function useCurrentPageNum() {
128
209
  }
129
210
 
130
211
  export function useCurrentArticle() {
131
- const blogConfig = useConfig()
132
212
  const route = useRoute()
133
213
 
134
- const docs = computed(() => blogConfig.config?.blog?.pagesData)
214
+ const docs = useArticles()
135
215
  const currentArticle = computed(() => {
136
216
  const currentPath = route.path.replace(/.html$/, '')
137
217
  // 兼容中文路径
@@ -149,16 +229,25 @@ export function useCurrentArticle() {
149
229
  }
150
230
 
151
231
  export function useUserWorks() {
152
- return inject(userWorks)!
232
+ const blogConfig = useBlogConfig()
233
+
234
+ return computed(() => blogConfig.value?.works || {
235
+ title: '',
236
+ description: '',
237
+ list: []
238
+ })
153
239
  }
154
- function resolveConfig(config: Theme.Config): Theme.Config {
155
- return {
240
+ function resolveConfig(config: Theme.Config, locale: string = 'root'): Theme.Config {
241
+ const mergeConfig = {
156
242
  ...config,
157
243
  blog: {
158
244
  ...config?.blog,
159
- pagesData: config?.blog?.pagesData || []
245
+ pagesData: config?.blog?.pagesData || [],
246
+ // i18n 支持
247
+ ...config?.blog?.locales?.[locale]
160
248
  }
161
249
  }
250
+ return mergeConfig
162
251
  }
163
252
 
164
253
  /**
@@ -220,11 +309,36 @@ export function useAutoUpdateAnchor() {
220
309
  }
221
310
 
222
311
  export function useHomeFooterConfig() {
223
- return inject(homeFooter)
312
+ const blogConfig = useBlogConfig()
313
+ return computed(() => blogConfig.value?.footer)
224
314
  }
225
315
 
226
316
  export function useBackToTopConfig() {
227
- return useBlogConfig().backToTop
317
+ const blogConfig = useBlogConfig()
318
+ return computed(() => typeof blogConfig.value?.backToTop === 'boolean' ? undefined : blogConfig.value?.backToTop)
319
+ }
320
+
321
+ export function useOpenBackToTop() {
322
+ const blogConfig = useBlogConfig()
323
+ return computed(() => blogConfig.value?.backToTop !== false)
324
+ }
325
+
326
+ export function useCommentConfig() {
327
+ const blogConfig = useBlogConfig()
328
+ return computed(() => {
329
+ return blogConfig.value?.comment === false ? undefined : blogConfig.value?.comment
330
+ })
331
+ }
332
+
333
+ export function useOpenCommentConfig() {
334
+ const blogConfig = useBlogConfig()
335
+ const { frontmatter } = useData()
336
+ return computed(() => !!blogConfig.value?.comment && frontmatter.value.comment !== false)
337
+ }
338
+
339
+ export function useFriendData() {
340
+ const blogConfig = useBlogConfig()
341
+ return computed(() => blogConfig.value?.friend)
228
342
  }
229
343
 
230
344
  export function useCleanUrls() {
@@ -233,50 +347,52 @@ export function useCleanUrls() {
233
347
  }
234
348
 
235
349
  export function useImageStyle() {
236
- return inject(configSymbol)?.value?.blog?.imageStyle || {} as Theme.ImageStyleConfig
350
+ const blogConfig = useBlogConfig()
351
+ return computed<Theme.ImageStyleConfig>(() => blogConfig.value?.imageStyle || {})
237
352
  }
238
353
 
239
354
  export function useHomeAnalysis() {
240
- return inject(configSymbol)?.value?.blog?.home?.analysis
355
+ const home = useHomeConfig()
356
+ return computed(() => home.value?.analysis)
241
357
  }
242
358
 
243
359
  export function useAnalyzeTitles(wordCount: Ref<number>, readTime: ComputedRef<number>) {
244
- const { article } = useBlogConfig()
360
+ const article = computed(() => useConfig()?.value.blog?.article)
245
361
 
246
362
  const topWordCount = computed(() =>
247
- replaceValue(article?.analyzeTitles?.topWordCount || '字数:{{value}} 个字', wordCount.value)
363
+ replaceValue(article.value?.analyzeTitles?.topWordCount || '字数:{{value}} 个字', wordCount.value)
248
364
  )
249
365
  const topReadTime = computed(() =>
250
- replaceValue(article?.analyzeTitles?.topReadTime || '预计:{{value}} 分钟', readTime.value)
366
+ replaceValue(article.value?.analyzeTitles?.topReadTime || '预计:{{value}} 分钟', readTime.value)
251
367
  )
252
368
  const inlineWordCount = computed(() =>
253
- replaceValue(article?.analyzeTitles?.inlineWordCount || '{{value}} 个字', wordCount.value)
369
+ replaceValue(article.value?.analyzeTitles?.inlineWordCount || '{{value}} 个字', wordCount.value)
254
370
  )
255
371
  const inlineReadTime = computed(() =>
256
- replaceValue(article?.analyzeTitles?.inlineReadTime || '{{value}} 分钟', readTime.value)
372
+ replaceValue(article.value?.analyzeTitles?.inlineReadTime || '{{value}} 分钟', readTime.value)
257
373
  )
258
374
 
259
375
  const wordCountTitle = computed(() =>
260
- article?.analyzeTitles?.wordCount || '文章字数'
376
+ article.value?.analyzeTitles?.wordCount || '文章字数'
261
377
  )
262
378
  const readTimeTitle = computed(() =>
263
- article?.analyzeTitles?.readTime || '预计阅读时间'
379
+ article.value?.analyzeTitles?.readTime || '预计阅读时间'
264
380
  )
265
381
 
266
382
  const authorTitle = computed(() =>
267
- article?.analyzeTitles?.author || '本文作者'
383
+ article.value?.analyzeTitles?.author || '本文作者'
268
384
  )
269
385
 
270
386
  const publishDateTitle = computed(() =>
271
- article?.analyzeTitles?.publishDate || '发布时间'
387
+ article.value?.analyzeTitles?.publishDate || '发布时间'
272
388
  )
273
389
 
274
390
  const lastUpdatedTitle = computed(() =>
275
- article?.analyzeTitles?.lastUpdated || '最近修改时间'
391
+ article.value?.analyzeTitles?.lastUpdated || '最近修改时间'
276
392
  )
277
393
 
278
394
  const tagTitle = computed(() =>
279
- article?.analyzeTitles?.tag || '标签'
395
+ article.value?.analyzeTitles?.tag || '标签'
280
396
  )
281
397
 
282
398
  return {
@@ -295,48 +411,50 @@ export function useAnalyzeTitles(wordCount: Ref<number>, readTime: ComputedRef<n
295
411
 
296
412
  export function useFormatShowDate() {
297
413
  const blog = useBlogConfig()
298
- if (typeof blog.formatShowDate === 'function') {
299
- return blog.formatShowDate
300
- }
301
-
302
- function formatShowDate(date: any) {
303
- const source = +new Date(date)
304
- const now = +new Date()
305
- const diff = now - source
306
- const oneSeconds = 1000
307
- const oneMinute = oneSeconds * 60
308
- const oneHour = oneMinute * 60
309
- const oneDay = oneHour * 24
310
- const oneWeek = oneDay * 7
311
-
312
- const langMap = {
313
- justNow: '刚刚',
314
- secondsAgo: '秒前',
315
- minutesAgo: '分钟前',
316
- hoursAgo: '小时前',
317
- daysAgo: '天前',
318
- weeksAgo: '周前',
319
- ...blog.formatShowDate
414
+ return computed(() => {
415
+ if (typeof blog.value?.formatShowDate === 'function') {
416
+ return blog.value.formatShowDate
320
417
  }
321
- const mapValue = langMap
322
418
 
323
- if (diff < 10) {
324
- return mapValue.justNow
325
- }
326
- if (diff < oneMinute) {
327
- return `${Math.floor(diff / oneSeconds)}${mapValue.secondsAgo}`
328
- }
329
- if (diff < oneHour) {
330
- return `${Math.floor(diff / oneMinute)}${mapValue.minutesAgo}`
331
- }
332
- if (diff < oneDay) {
333
- return `${Math.floor(diff / oneHour)}${mapValue.hoursAgo}`
334
- }
335
- if (diff < oneWeek) {
336
- return `${Math.floor(diff / oneDay)}${mapValue.daysAgo}`
337
- }
419
+ function formatShowDate(date: any) {
420
+ const source = +new Date(date)
421
+ const now = +new Date()
422
+ const diff = now - source
423
+ const oneSeconds = 1000
424
+ const oneMinute = oneSeconds * 60
425
+ const oneHour = oneMinute * 60
426
+ const oneDay = oneHour * 24
427
+ const oneWeek = oneDay * 7
428
+
429
+ const langMap = {
430
+ justNow: '刚刚',
431
+ secondsAgo: '秒前',
432
+ minutesAgo: '分钟前',
433
+ hoursAgo: '小时前',
434
+ daysAgo: '天前',
435
+ weeksAgo: '周前',
436
+ ...blog.value?.formatShowDate
437
+ }
438
+ const mapValue = langMap
338
439
 
339
- return formatDate(new Date(date), 'yyyy-MM-dd')
340
- }
341
- return formatShowDate
440
+ if (diff < 10) {
441
+ return mapValue.justNow
442
+ }
443
+ if (diff < oneMinute) {
444
+ return `${Math.floor(diff / oneSeconds)}${mapValue.secondsAgo}`
445
+ }
446
+ if (diff < oneHour) {
447
+ return `${Math.floor(diff / oneMinute)}${mapValue.minutesAgo}`
448
+ }
449
+ if (diff < oneDay) {
450
+ return `${Math.floor(diff / oneHour)}${mapValue.hoursAgo}`
451
+ }
452
+ if (diff < oneWeek) {
453
+ return `${Math.floor(diff / oneDay)}${mapValue.daysAgo}`
454
+ }
455
+
456
+ return formatDate(new Date(date), 'yyyy-MM-dd')
457
+ }
458
+ return formatShowDate
459
+ })
342
460
  }
@@ -349,6 +349,7 @@ export namespace Theme {
349
349
  | 'el-red'
350
350
  export interface BlogConfig {
351
351
  blog?: false
352
+ locales?: Record<string, Omit<BlogConfig, 'locales' | 'pagesData' | 'search' | 'popover' | 'RSS'> & { pagesData?: PageData[] }>
352
353
  /**
353
354
  * 展示日期格式化
354
355
  */
@@ -360,6 +361,11 @@ export namespace Theme {
360
361
  */
361
362
  themeColor?: ThemeColor
362
363
  pagesData: PageData[]
364
+ /**
365
+ * @deprecated 请使用 VitePress 官方 srcDir 替代
366
+ *
367
+ * @doc https://vitepress.dev/zh/reference/site-config#srcdir
368
+ */
363
369
  srcDir?: string
364
370
  author?: string
365
371
  hotArticle?: HotArticle | false
@@ -459,6 +465,9 @@ export namespace Theme {
459
465
  * 渲染时替换图片地址
460
466
  */
461
467
  imageStyle?: ImageStyleConfig
468
+ groupIcon?: {
469
+ customIcon: Record<string, string>
470
+ }
462
471
  }
463
472
 
464
473
  export type FormatShowDate = {