@sugarat/theme 0.4.9 → 0.4.10

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/node.d.ts CHANGED
@@ -199,6 +199,10 @@ declare namespace Theme {
199
199
  analysis?: HomeAnalysis;
200
200
  }
201
201
  interface ArticleConfig {
202
+ /**
203
+ * 文章分析数据展示标题
204
+ */
205
+ analyzeTitles?: ArticleAnalyzeTitles;
202
206
  readingTime?: boolean;
203
207
  /**
204
208
  * 阅读时间分析展示位置
@@ -207,6 +211,48 @@ declare namespace Theme {
207
211
  readingTimePosition?: 'inline' | 'newLine' | 'top';
208
212
  hiddenCover?: boolean;
209
213
  }
214
+ interface ArticleAnalyzeTitles {
215
+ /**
216
+ * 字数:{{value}} 个字
217
+ */
218
+ topWordCount?: string;
219
+ /**
220
+ * 预计:{{value}} 分钟
221
+ */
222
+ topReadTime?: string;
223
+ /**
224
+ * {{value}} 个字
225
+ */
226
+ inlineWordCount?: string;
227
+ /**
228
+ * {{value}} 分钟
229
+ */
230
+ inlineReadTime?: string;
231
+ /**
232
+ * 文章字数
233
+ */
234
+ wordCount?: string;
235
+ /**
236
+ * 预计阅读时间
237
+ */
238
+ readTime?: string;
239
+ /**
240
+ * 本文作者
241
+ */
242
+ author?: string;
243
+ /**
244
+ * 发布时间
245
+ */
246
+ publishDate?: string;
247
+ /**
248
+ * 最近修改时间
249
+ */
250
+ lastUpdated?: string;
251
+ /**
252
+ * 标签
253
+ */
254
+ tag?: string;
255
+ }
210
256
  interface Alert {
211
257
  type: 'success' | 'warning' | 'info' | 'error';
212
258
  /**
@@ -335,6 +381,10 @@ declare namespace Theme {
335
381
  type ThemeColor = 'vp-default' | 'vp-green' | 'vp-yellow' | 'vp-red' | 'el-blue' | 'el-yellow' | 'el-green' | 'el-red';
336
382
  interface BlogConfig {
337
383
  blog?: false;
384
+ /**
385
+ * 展示日期格式化
386
+ */
387
+ formatShowDate?: FormatShowDate;
338
388
  /**
339
389
  * 内置一些主题色
340
390
  * @default 'vp-default'
@@ -441,6 +491,32 @@ declare namespace Theme {
441
491
  */
442
492
  imageStyle?: ImageStyleConfig;
443
493
  }
494
+ type FormatShowDate = {
495
+ /**
496
+ * 刚刚
497
+ */
498
+ justNow?: string;
499
+ /**
500
+ * 秒前
501
+ */
502
+ secondsAgo?: string;
503
+ /**
504
+ * 分钟前
505
+ */
506
+ minutesAgo?: string;
507
+ /**
508
+ * 小时前
509
+ */
510
+ hoursAgo?: string;
511
+ /**
512
+ * 天前
513
+ */
514
+ daysAgo?: string;
515
+ /**
516
+ * 周前
517
+ */
518
+ weeksAgo?: string;
519
+ } | ((date: Date) => string);
444
520
  interface BackToTop {
445
521
  /**
446
522
  * 距离顶部多少距离出现
package/node.js CHANGED
@@ -40,7 +40,7 @@ module.exports = __toCommonJS(node_exports);
40
40
  // src/utils/node/mdPlugins.ts
41
41
  var import_module = require("module");
42
42
 
43
- // ../../node_modules/.pnpm/vitepress-plugin-tabs@0.2.0_vitepress@1.3.1_@algolia+client-search@4.19.1_@types+node@20.6.3__tymgq2io4kjnp3wlifsw2g5vnu/node_modules/vitepress-plugin-tabs/dist/index.js
43
+ // ../../node_modules/.pnpm/vitepress-plugin-tabs@0.2.0_vitepress@1.3.2_@algolia+client-search@4.19.1_@types+node@20.6.3__i342z2vy4vtzoo4ecdc3cz7rya/node_modules/vitepress-plugin-tabs/dist/index.js
44
44
  var tabsMarker = "=tabs";
45
45
  var tabsMarkerLen = tabsMarker.length;
46
46
  var ruleBlockTabs = (state, startLine, endLine, silent) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sugarat/theme",
3
- "version": "0.4.9",
3
+ "version": "0.4.10",
4
4
  "description": "简约风的 Vitepress 博客主题,sugarat vitepress blog theme",
5
5
  "author": "sugar",
6
6
  "license": "MIT",
@@ -52,7 +52,7 @@
52
52
  "vitepress-plugin-tabs": "0.2.0",
53
53
  "@sugarat/theme-shared": "0.0.2",
54
54
  "vitepress-plugin-pagefind": "0.4.10",
55
- "vitepress-plugin-rss": "0.2.9"
55
+ "vitepress-plugin-rss": "0.2.10"
56
56
  },
57
57
  "devDependencies": {
58
58
  "@element-plus/icons-vue": "^2.3.1",
@@ -62,7 +62,7 @@
62
62
  "sass": "^1.77.8",
63
63
  "typescript": "^5.4.5",
64
64
  "vite": "^5.2.11",
65
- "vitepress": "1.3.1",
65
+ "vitepress": "1.3.2",
66
66
  "vue": "^3.4.26"
67
67
  },
68
68
  "scripts": {
@@ -1,8 +1,8 @@
1
1
  <script lang="ts" setup>
2
2
  // 阅读时间计算方式参考
3
3
  // https://zhuanlan.zhihu.com/p/36375802
4
- import { useData, useRoute } from 'vitepress'
5
- import { computed, onMounted, ref, watch } from 'vue'
4
+ import { useData } from 'vitepress'
5
+ import { computed, onMounted, ref } from 'vue'
6
6
  import { ElIcon } from 'element-plus'
7
7
  import {
8
8
  AlarmClock,
@@ -11,11 +11,12 @@ import {
11
11
  EditPen,
12
12
  UserFilled
13
13
  } from '@element-plus/icons-vue'
14
- import { useBlogConfig, useCurrentArticle, useDocMetaInsertPosition, useDocMetaInsertSelector } from '../composables/config/blog'
15
- import countWord, { formatDate, formatShowDate } from '../utils/client'
14
+ import { useAnalyzeTitles, useBlogConfig, useCurrentArticle, useDocMetaInsertPosition, useDocMetaInsertSelector, useFormatShowDate } from '../composables/config/blog'
15
+ import countWord, { formatDate } from '../utils/client'
16
16
  import type { Theme } from '../composables/config'
17
17
  import BlogDocCover from './BlogDocCover.vue'
18
18
 
19
+ const formatShowDate = useFormatShowDate()
19
20
  const { article, authorList } = useBlogConfig()
20
21
  const readingTimePosition = article?.readingTimePosition || 'inline'
21
22
 
@@ -57,7 +58,6 @@ const readTime = computed(() => {
57
58
  const docMetaInsertSelector = useDocMetaInsertSelector()
58
59
  const docMetaInsertPosition = useDocMetaInsertPosition()
59
60
 
60
- const route = useRoute()
61
61
  const $des = ref<HTMLDivElement>()
62
62
 
63
63
  function analyze() {
@@ -100,9 +100,6 @@ onMounted(() => {
100
100
  analyze()
101
101
  })
102
102
 
103
- // 阅读量
104
- const pv = ref(6666)
105
-
106
103
  const currentArticle = useCurrentArticle()
107
104
  const publishDate = computed(() => {
108
105
  return formatShowDate(currentArticle.value?.meta?.date || '')
@@ -112,9 +109,6 @@ const hoverDate = computed(() => {
112
109
  return currentArticle.value?.meta?.date ? `: ${formatDate(currentArticle.value?.meta?.date)}` : ''
113
110
  })
114
111
 
115
- const timeTitle = computed(() =>
116
- frontmatter.value.date ? '发布时间' : '最近修改时间'
117
- )
118
112
  const hiddenTime = computed(() => frontmatter.value.date === false)
119
113
 
120
114
  const { theme } = useData<Theme.Config>()
@@ -129,15 +123,9 @@ const currentAuthorInfo = computed(() =>
129
123
  )
130
124
  const hiddenAuthor = computed(() => frontmatter.value.author === false)
131
125
 
132
- watch(
133
- () => route.path,
134
- () => {
135
- // TODO: 调用接口取数据
136
- pv.value = 123
137
- },
138
- {
139
- immediate: true
140
- }
126
+ const { topWordCount, topReadTime, inlineWordCount, inlineReadTime, authorTitle, readTimeTitle, wordCountTitle, publishDateTitle, lastUpdatedTitle, tagTitle } = useAnalyzeTitles(wordCount, readTime)
127
+ const timeTitle = computed(() =>
128
+ frontmatter.value.date ? publishDateTitle.value : lastUpdatedTitle.value
141
129
  )
142
130
  </script>
143
131
 
@@ -145,16 +133,16 @@ watch(
145
133
  <div v-if="showAnalyze && readingTimePosition === 'top'" class="doc-analyze" data-pagefind-ignore="all">
146
134
  <span>
147
135
  <ElIcon><EditPen /></ElIcon>
148
- 字数:{{ wordCount }} 个字
136
+ {{ topWordCount }}
149
137
  </span>
150
138
  <span>
151
139
  <ElIcon><AlarmClock /></ElIcon>
152
- 预计:{{ readTime }} 分钟
140
+ {{ topReadTime }}
153
141
  </span>
154
142
  </div>
155
143
  <div id="hack-article-des" ref="$des" class="meta-des">
156
144
  <!-- TODO:是否需要原创?转载等标签,理论上可以添加标签解决,可以参考 charles7c -->
157
- <span v-if="author && !hiddenAuthor" class="author" title="本文作者">
145
+ <span v-if="author && !hiddenAuthor" class="author" :title="authorTitle">
158
146
  <ElIcon><UserFilled /></ElIcon>
159
147
  <a
160
148
  v-if="currentAuthorInfo"
@@ -172,30 +160,30 @@ watch(
172
160
  <ElIcon><Clock /></ElIcon>
173
161
  {{ publishDate }}
174
162
  </span>
175
- <span v-if="tags.length" class="tags" title="标签">
163
+ <span v-if="tags.length" class="tags" :title="tagTitle">
176
164
  <ElIcon><CollectionTag /></ElIcon>
177
165
  <a v-for="tag in tags" :key="tag" class="link" :href="`/?tag=${tag}`">{{ tag }}
178
166
  </a>
179
167
  </span>
180
168
  <template v-if="readingTimePosition === 'inline' && showAnalyze">
181
- <span title="文章字数">
169
+ <span :title="wordCountTitle">
182
170
  <ElIcon><EditPen /></ElIcon>
183
- {{ wordCount }} 个字
171
+ {{ inlineWordCount }}
184
172
  </span>
185
- <span title="预计阅读时间">
173
+ <span :title="readTimeTitle">
186
174
  <ElIcon><AlarmClock /></ElIcon>
187
- {{ readTime }} 分钟
175
+ {{ inlineReadTime }}
188
176
  </span>
189
177
  </template>
190
178
  <template v-if="readingTimePosition === 'newLine' && showAnalyze">
191
179
  <div style="width: 100%;" class="new-line-meta-des">
192
- <span title="文章字数">
180
+ <span :title="wordCountTitle">
193
181
  <ElIcon><EditPen /></ElIcon>
194
- {{ wordCount }} 个字
182
+ {{ inlineWordCount }}
195
183
  </span>
196
- <span title="预计阅读时间">
184
+ <span :title="readTimeTitle">
197
185
  <ElIcon><AlarmClock /></ElIcon>
198
- {{ readTime }} 分钟
186
+ {{ inlineReadTime }}
199
187
  </span>
200
188
  </div>
201
189
  </template>
@@ -2,10 +2,12 @@
2
2
  import { computed, ref } from 'vue'
3
3
  import { ElButton } from 'element-plus'
4
4
  import { useRouter, withBase } from 'vitepress'
5
- import { useArticles, useBlogConfig, useCleanUrls } from '../composables/config/blog'
6
- import { formatShowDate, wrapperCleanUrls } from '../utils/client'
5
+ import { useArticles, useBlogConfig, useCleanUrls, useFormatShowDate } from '../composables/config/blog'
6
+ import { wrapperCleanUrls } from '../utils/client'
7
7
  import { fireSVG } from '../constants/svg'
8
8
 
9
+ const formatShowDate = useFormatShowDate()
10
+
9
11
  const { hotArticle: _hotArticle } = useBlogConfig()
10
12
 
11
13
  const hotArticle = computed(() =>
@@ -1,8 +1,8 @@
1
1
  <script lang="ts" setup>
2
2
  import { useRouter, withBase } from 'vitepress'
3
3
  import { computed } from 'vue'
4
- import { formatShowDate, wrapperCleanUrls } from '../utils/client'
5
- import { useCleanUrls, useImageStyle } from '../composables/config/blog'
4
+ import { wrapperCleanUrls } from '../utils/client'
5
+ import { useCleanUrls, useFormatShowDate, useImageStyle } from '../composables/config/blog'
6
6
 
7
7
  const props = defineProps<{
8
8
  route: string
@@ -16,6 +16,9 @@ const props = defineProps<{
16
16
  cover?: string | false
17
17
  pin?: number
18
18
  }>()
19
+
20
+ const formatShowDate = useFormatShowDate()
21
+
19
22
  const showTime = computed(() => {
20
23
  return formatShowDate(props.date)
21
24
  })
@@ -2,11 +2,13 @@
2
2
  import { computed, onMounted, ref } from 'vue'
3
3
  import { useRoute, useRouter, withBase } from 'vitepress'
4
4
  import { ElButton } from 'element-plus'
5
- import { formatShowDate, wrapperCleanUrls } from '../utils/client'
6
- import { useArticles, useBlogConfig, useCleanUrls } from '../composables/config/blog'
5
+ import { wrapperCleanUrls } from '../utils/client'
6
+ import { useArticles, useBlogConfig, useCleanUrls, useFormatShowDate } from '../composables/config/blog'
7
7
  import { recommendSVG } from '../constants/svg'
8
8
  import type { Theme } from '../composables/config/index'
9
9
 
10
+ const formatShowDate = useFormatShowDate()
11
+
10
12
  const { recommend: _recommend } = useBlogConfig()
11
13
 
12
14
  const sidebarStyle = computed(() =>
@@ -1,6 +1,7 @@
1
1
  import { useData, useRoute, withBase } from 'vitepress'
2
2
  import type {
3
3
  Component,
4
+ ComputedRef,
4
5
  InjectionKey,
5
6
  Ref
6
7
  } from 'vue'
@@ -17,6 +18,7 @@ import {
17
18
  } from 'vue'
18
19
  import { useColorMode } from '@vueuse/core'
19
20
 
21
+ import { formatDate, replaceValue } from '../../utils/client'
20
22
  import type { Theme } from './index'
21
23
 
22
24
  const configSymbol: InjectionKey<Ref<Theme.Config>> = Symbol('theme-config')
@@ -237,3 +239,104 @@ export function useImageStyle() {
237
239
  export function useHomeAnalysis() {
238
240
  return inject(configSymbol)?.value?.blog?.home?.analysis
239
241
  }
242
+
243
+ export function useAnalyzeTitles(wordCount: Ref<number>, readTime: ComputedRef<number>) {
244
+ const { article } = useBlogConfig()
245
+
246
+ const topWordCount = computed(() =>
247
+ replaceValue(article?.analyzeTitles?.topWordCount || '字数:{{value}} 个字', wordCount.value)
248
+ )
249
+ const topReadTime = computed(() =>
250
+ replaceValue(article?.analyzeTitles?.topReadTime || '预计:{{value}} 分钟', readTime.value)
251
+ )
252
+ const inlineWordCount = computed(() =>
253
+ replaceValue(article?.analyzeTitles?.inlineWordCount || '{{value}} 个字', wordCount.value)
254
+ )
255
+ const inlineReadTime = computed(() =>
256
+ replaceValue(article?.analyzeTitles?.inlineReadTime || '{{value}} 分钟', readTime.value)
257
+ )
258
+
259
+ const wordCountTitle = computed(() =>
260
+ article?.analyzeTitles?.wordCount || '文章字数'
261
+ )
262
+ const readTimeTitle = computed(() =>
263
+ article?.analyzeTitles?.readTime || '预计阅读时间'
264
+ )
265
+
266
+ const authorTitle = computed(() =>
267
+ article?.analyzeTitles?.author || '本文作者'
268
+ )
269
+
270
+ const publishDateTitle = computed(() =>
271
+ article?.analyzeTitles?.publishDate || '发布时间'
272
+ )
273
+
274
+ const lastUpdatedTitle = computed(() =>
275
+ article?.analyzeTitles?.lastUpdated || '最近修改时间'
276
+ )
277
+
278
+ const tagTitle = computed(() =>
279
+ article?.analyzeTitles?.tag || '标签'
280
+ )
281
+
282
+ return {
283
+ topWordCount,
284
+ topReadTime,
285
+ inlineWordCount,
286
+ inlineReadTime,
287
+ wordCountTitle,
288
+ readTimeTitle,
289
+ authorTitle,
290
+ publishDateTitle,
291
+ lastUpdatedTitle,
292
+ tagTitle
293
+ }
294
+ }
295
+
296
+ export function useFormatShowDate() {
297
+ 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
320
+ }
321
+ const mapValue = langMap
322
+
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
+ }
338
+
339
+ return formatDate(new Date(date), 'yyyy-MM-dd')
340
+ }
341
+ return formatShowDate
342
+ }
@@ -211,6 +211,10 @@ export namespace Theme {
211
211
  }
212
212
 
213
213
  export interface ArticleConfig {
214
+ /**
215
+ * 文章分析数据展示标题
216
+ */
217
+ analyzeTitles?: ArticleAnalyzeTitles
214
218
  readingTime?: boolean
215
219
  /**
216
220
  * 阅读时间分析展示位置
@@ -219,6 +223,49 @@ export namespace Theme {
219
223
  readingTimePosition?: 'inline' | 'newLine' | 'top'
220
224
  hiddenCover?: boolean
221
225
  }
226
+
227
+ export interface ArticleAnalyzeTitles {
228
+ /**
229
+ * 字数:{{value}} 个字
230
+ */
231
+ topWordCount?: string
232
+ /**
233
+ * 预计:{{value}} 分钟
234
+ */
235
+ topReadTime?: string
236
+ /**
237
+ * {{value}} 个字
238
+ */
239
+ inlineWordCount?: string
240
+ /**
241
+ * {{value}} 分钟
242
+ */
243
+ inlineReadTime?: string
244
+ /**
245
+ * 文章字数
246
+ */
247
+ wordCount?: string
248
+ /**
249
+ * 预计阅读时间
250
+ */
251
+ readTime?: string
252
+ /**
253
+ * 本文作者
254
+ */
255
+ author?: string
256
+ /**
257
+ * 发布时间
258
+ */
259
+ publishDate?: string
260
+ /**
261
+ * 最近修改时间
262
+ */
263
+ lastUpdated?: string
264
+ /**
265
+ * 标签
266
+ */
267
+ tag?: string
268
+ }
222
269
  export interface Alert {
223
270
  type: 'success' | 'warning' | 'info' | 'error'
224
271
  /**
@@ -368,6 +415,10 @@ export namespace Theme {
368
415
  | 'el-red'
369
416
  export interface BlogConfig {
370
417
  blog?: false
418
+ /**
419
+ * 展示日期格式化
420
+ */
421
+ formatShowDate?: FormatShowDate
371
422
  /**
372
423
  * 内置一些主题色
373
424
  * @default 'vp-default'
@@ -476,6 +527,32 @@ export namespace Theme {
476
527
  imageStyle?: ImageStyleConfig
477
528
  }
478
529
 
530
+ export type FormatShowDate = {
531
+ /**
532
+ * 刚刚
533
+ */
534
+ justNow?: string
535
+ /**
536
+ * 秒前
537
+ */
538
+ secondsAgo?: string
539
+ /**
540
+ * 分钟前
541
+ */
542
+ minutesAgo?: string
543
+ /**
544
+ * 小时前
545
+ */
546
+ hoursAgo?: string
547
+ /**
548
+ * 天前
549
+ */
550
+ daysAgo?: string
551
+ /**
552
+ * 周前
553
+ */
554
+ weeksAgo?: string
555
+ } | ((date: Date) => string)
479
556
  export interface BackToTop {
480
557
  /**
481
558
  * 距离顶部多少距离出现
@@ -177,3 +177,7 @@ export function wrapperCleanUrls(cleanUrls: boolean, route: string) {
177
177
  const tempUrl = route.replace(/\.html$/, '')
178
178
  return cleanUrls ? tempUrl : `${tempUrl}.html`
179
179
  }
180
+
181
+ export function replaceValue(str: string, value: any) {
182
+ return str.replace(/\{\{value\}\}/, value)
183
+ }