@sugarat/theme 0.1.49 → 0.2.0
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 +41 -1
- package/node.js +24 -20
- package/package.json +24 -24
- package/src/components/BlogAlert.vue +17 -17
- package/src/components/BlogApp.vue +91 -50
- package/src/components/BlogArticleAnalyze.vue +56 -57
- package/src/components/BlogAuthor.vue +55 -0
- package/src/components/BlogComment.vue +53 -50
- package/src/components/BlogDocCover.vue +4 -4
- package/src/components/BlogFooter.vue +131 -0
- package/src/components/BlogFriendLink.vue +40 -31
- package/src/components/BlogHomeBanner.vue +22 -16
- package/src/components/BlogHomeInfo.vue +4 -0
- package/src/components/BlogHomeOverview.vue +20 -20
- package/src/components/BlogHomeTags.vue +49 -40
- package/src/components/BlogHotArticle.vue +43 -36
- package/src/components/BlogImagePreview.vue +7 -5
- package/src/components/BlogItem.vue +42 -43
- package/src/components/BlogList.vue +46 -42
- package/src/components/BlogPopover.vue +41 -39
- package/src/components/BlogRecommendArticle.vue +58 -48
- package/src/components/BlogSearch.vue +143 -145
- package/src/components/UserWorks.vue +214 -210
- package/src/composables/config/blog.ts +14 -5
- package/src/composables/config/index.ts +74 -31
- package/src/constants/svg.ts +11 -2
- package/src/index.ts +1 -2
- package/src/node.ts +2 -2
- package/src/styles/gongan.png +0 -0
- package/src/styles/scss/global.scss +0 -5
- package/src/utils/client/index.ts +9 -8
- package/src/utils/node/genFeed.ts +8 -7
- package/src/utils/node/index.ts +8 -6
- package/src/utils/node/mdPlugins.ts +29 -22
- package/src/utils/node/theme.ts +16 -13
- package/src/utils/node/vitePlugins.ts +7 -6
- package/types/vue-shim.d.ts +1 -1
|
@@ -1,12 +1,186 @@
|
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
import { ElCarousel, ElCarouselItem, ElImage, ElMessage } from 'element-plus'
|
|
3
|
+
import VPDocAsideOutline from 'vitepress/dist/client/theme-default/components/VPDocAsideOutline.vue'
|
|
4
|
+
import { computed, reactive, ref, watch, watchEffect } from 'vue'
|
|
5
|
+
import { slugify } from '@mdit-vue/shared'
|
|
6
|
+
import { useWindowSize } from '@vueuse/core'
|
|
7
|
+
import {
|
|
8
|
+
formatDate,
|
|
9
|
+
getGithubDirUpdateTime,
|
|
10
|
+
getGithubUpdateTime
|
|
11
|
+
} from '../utils/client'
|
|
12
|
+
import {
|
|
13
|
+
useActiveAnchor,
|
|
14
|
+
useAutoUpdateAnchor,
|
|
15
|
+
useUserWorks
|
|
16
|
+
} from '../composables/config/blog'
|
|
17
|
+
import type { Theme } from '../composables/config'
|
|
18
|
+
|
|
19
|
+
const currentAnchor = useAutoUpdateAnchor()
|
|
20
|
+
// 更新锚点的时候更新 url 中的 hash
|
|
21
|
+
watch(
|
|
22
|
+
() => currentAnchor.id,
|
|
23
|
+
(val) => {
|
|
24
|
+
if (val) {
|
|
25
|
+
window.history.replaceState(null, '', `#${val}`)
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
)
|
|
29
|
+
const mountActiveAnchorEl = useActiveAnchor()
|
|
30
|
+
watch(mountActiveAnchorEl, () => {
|
|
31
|
+
const { value } = mountActiveAnchorEl
|
|
32
|
+
if (value) {
|
|
33
|
+
value.scroll({
|
|
34
|
+
behavior: 'smooth'
|
|
35
|
+
})
|
|
36
|
+
}
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
const works = useUserWorks()
|
|
40
|
+
const workList = reactive<
|
|
41
|
+
(Theme.UserWork & {
|
|
42
|
+
year?: string | undefined
|
|
43
|
+
startTime: string
|
|
44
|
+
lastUpdate?: string
|
|
45
|
+
endTime?: string
|
|
46
|
+
covers?: string[]
|
|
47
|
+
coverLayout?: string
|
|
48
|
+
})[]
|
|
49
|
+
>([])
|
|
50
|
+
|
|
51
|
+
// 格式化数据
|
|
52
|
+
watch(
|
|
53
|
+
works,
|
|
54
|
+
(val) => {
|
|
55
|
+
const sortDate = [...val.list].map((v) => {
|
|
56
|
+
const { time } = v
|
|
57
|
+
|
|
58
|
+
// 格式化时间
|
|
59
|
+
const metaInfo
|
|
60
|
+
= typeof time === 'string'
|
|
61
|
+
? {
|
|
62
|
+
startTime: time,
|
|
63
|
+
endTime: '',
|
|
64
|
+
lastUpdate: ''
|
|
65
|
+
}
|
|
66
|
+
: {
|
|
67
|
+
startTime: time.start,
|
|
68
|
+
endTime: time.end,
|
|
69
|
+
lastUpdate: time.lastupdate
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// 格式化封面信息
|
|
73
|
+
const covers: string[] = []
|
|
74
|
+
let coverLayout = 'swiper'
|
|
75
|
+
|
|
76
|
+
if (typeof v.cover === 'string') {
|
|
77
|
+
covers.push(v.cover)
|
|
78
|
+
}
|
|
79
|
+
else if (Array.isArray(v.cover)) {
|
|
80
|
+
covers.push(...v.cover)
|
|
81
|
+
}
|
|
82
|
+
else if (typeof v.cover === 'object') {
|
|
83
|
+
covers.push(...v.cover.urls)
|
|
84
|
+
coverLayout = v.cover.layout ?? coverLayout
|
|
85
|
+
}
|
|
86
|
+
return {
|
|
87
|
+
...v,
|
|
88
|
+
...metaInfo,
|
|
89
|
+
covers,
|
|
90
|
+
coverLayout
|
|
91
|
+
}
|
|
92
|
+
})
|
|
93
|
+
// 过滤出置顶数据
|
|
94
|
+
const topDate = sortDate.filter(v => v.top !== undefined)
|
|
95
|
+
const normalDate = sortDate.filter(v => v.top === undefined)
|
|
96
|
+
// 数据排序
|
|
97
|
+
topDate.sort((a, b) => a.top! - b.top!)
|
|
98
|
+
normalDate.sort((a, b) => +new Date(b.startTime) - +new Date(a.startTime))
|
|
99
|
+
if (topDate.length) {
|
|
100
|
+
// @ts-expect-error
|
|
101
|
+
topDate[0].year = works.value.topTitle ?? '置顶'
|
|
102
|
+
}
|
|
103
|
+
// 数据分组
|
|
104
|
+
const groupDate = normalDate.reduce((prev, cur) => {
|
|
105
|
+
const { startTime } = cur
|
|
106
|
+
const year = new Date(startTime).getFullYear()
|
|
107
|
+
const data = { ...cur }
|
|
108
|
+
if (!prev[year]) {
|
|
109
|
+
prev[year] = []
|
|
110
|
+
// 第一项数据加上year属性
|
|
111
|
+
// @ts-expect-error
|
|
112
|
+
data.year = year
|
|
113
|
+
}
|
|
114
|
+
prev[year].push(data)
|
|
115
|
+
return prev
|
|
116
|
+
}, {} as Record<string, (Theme.UserWork & { year?: string; startTime: string })[]>)
|
|
117
|
+
workList.push(...topDate, ...Object.values(groupDate).reverse().flat())
|
|
118
|
+
},
|
|
119
|
+
{ immediate: true }
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
const init = ref(true)
|
|
123
|
+
// 更新时间信息
|
|
124
|
+
watchEffect(() => {
|
|
125
|
+
if (workList.length && init.value) {
|
|
126
|
+
init.value = false
|
|
127
|
+
workList.forEach((data) => {
|
|
128
|
+
// 接口获取最后更新时间
|
|
129
|
+
if (!data.lastUpdate && data.github) {
|
|
130
|
+
data.lastUpdate = '获取中...'
|
|
131
|
+
const { github } = data
|
|
132
|
+
if (typeof github === 'string') {
|
|
133
|
+
getGithubUpdateTime(github)
|
|
134
|
+
.then((time) => {
|
|
135
|
+
data.lastUpdate = formatDate(time, 'yyyy-MM-dd')
|
|
136
|
+
})
|
|
137
|
+
.catch(() => {
|
|
138
|
+
data.lastUpdate = '地址解析失败'
|
|
139
|
+
})
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
const { owner, repo, path, branch } = github
|
|
143
|
+
// 拼接Github链接
|
|
144
|
+
let githubUrl = `https://github.com/${owner}/${repo}`
|
|
145
|
+
if (path) {
|
|
146
|
+
githubUrl += `/tree/${branch || 'master'}/${path}`
|
|
147
|
+
}
|
|
148
|
+
else if (branch) {
|
|
149
|
+
githubUrl += `/tree/${branch}`
|
|
150
|
+
}
|
|
151
|
+
data.github = githubUrl
|
|
152
|
+
getGithubDirUpdateTime(owner, repo, path ?? '', branch)
|
|
153
|
+
.then((time) => {
|
|
154
|
+
data.lastUpdate = formatDate(time, 'yyyy-MM-dd')
|
|
155
|
+
})
|
|
156
|
+
.catch(() => {
|
|
157
|
+
data.lastUpdate = '地址解析失败'
|
|
158
|
+
})
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
})
|
|
162
|
+
}
|
|
163
|
+
})
|
|
164
|
+
|
|
165
|
+
const { width } = useWindowSize()
|
|
166
|
+
const isCardMode = computed(() => width.value > 768)
|
|
167
|
+
function handleChooseTag(tag: string) {
|
|
168
|
+
ElMessage({
|
|
169
|
+
message: `点击了${tag}标签,标签过滤功能开发中ing...`,
|
|
170
|
+
type: 'warning'
|
|
171
|
+
})
|
|
172
|
+
}
|
|
173
|
+
</script>
|
|
174
|
+
|
|
1
175
|
<template>
|
|
2
176
|
<div class="user-works-page VPDoc">
|
|
3
177
|
<div class="aside-container">
|
|
4
178
|
<!-- TODO:过滤,可吸顶 -->
|
|
5
179
|
<div class="filter">
|
|
6
180
|
<!-- 时间: -->
|
|
7
|
-
<div
|
|
181
|
+
<div />
|
|
8
182
|
<!-- TODO: tags -->
|
|
9
|
-
<div
|
|
183
|
+
<div />
|
|
10
184
|
</div>
|
|
11
185
|
</div>
|
|
12
186
|
<!-- 作品列表 -->
|
|
@@ -16,21 +190,23 @@
|
|
|
16
190
|
{{ works.description }}
|
|
17
191
|
</p>
|
|
18
192
|
<!-- 标题,描述信息,时间,线上链接,代码仓库,示例图片(几张,多种展示样式支持) -->
|
|
19
|
-
<div
|
|
193
|
+
<div v-for="(work, idx) in workList" :key="idx" class="work">
|
|
20
194
|
<!-- 大日期标题 -->
|
|
21
|
-
<h2 :id="`work_${work.year}`"
|
|
195
|
+
<h2 v-if="work.year" :id="`work_${work.year}`">
|
|
22
196
|
<a :href="`#work_${work.year}`">{{ work.year }}</a>
|
|
23
197
|
</h2>
|
|
24
198
|
<!-- 作品标题 -->
|
|
25
|
-
<h3
|
|
26
|
-
<a class="pin" :href="
|
|
199
|
+
<h3 :id="slugify(work.title)" class="title">
|
|
200
|
+
<a class="pin" :href="`#${slugify(work.title)}`" />
|
|
27
201
|
<a v-if="work.url" rel="noopener" target="_blank" :href="work.url">{{
|
|
28
202
|
work.title
|
|
29
203
|
}}</a>
|
|
30
204
|
<span v-else>{{ work.title }}</span>
|
|
31
|
-
<Badge v-if="work.status" :type="work.status?.type || 'tip'">
|
|
32
|
-
|
|
33
|
-
|
|
205
|
+
<Badge v-if="work.status" :type="work.status?.type || 'tip'">
|
|
206
|
+
{{
|
|
207
|
+
work.status.text
|
|
208
|
+
}}
|
|
209
|
+
</Badge>
|
|
34
210
|
</h3>
|
|
35
211
|
<!-- 补充信息 -->
|
|
36
212
|
<div class="info">
|
|
@@ -46,18 +222,18 @@
|
|
|
46
222
|
<path
|
|
47
223
|
d="M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8s8 3.58 8 8s-3.58 8-8 8zm-.22-13h-.06c-.4 0-.72.32-.72.72v4.72c0 .35.18.68.49.86l4.15 2.49c.34.2.78.1.98-.24a.71.71 0 0 0-.25-.99l-3.87-2.3V7.72c0-.4-.32-.72-.72-.72z"
|
|
48
224
|
fill="currentColor"
|
|
49
|
-
|
|
225
|
+
/>
|
|
50
226
|
</svg>
|
|
51
227
|
</span>
|
|
52
228
|
<span>{{ work.startTime }}</span>
|
|
53
229
|
<span v-if="work.endTime"> - {{ work.endTime }}</span>
|
|
54
230
|
</div>
|
|
55
|
-
<!-- GitHub links-->
|
|
56
|
-
<div
|
|
231
|
+
<!-- GitHub links -->
|
|
232
|
+
<div v-if="work.github" class="links">
|
|
57
233
|
<a
|
|
58
|
-
class="github-link"
|
|
59
234
|
v-if="work.github"
|
|
60
|
-
|
|
235
|
+
class="github-link"
|
|
236
|
+
:href="work.github as string"
|
|
61
237
|
target="_blank"
|
|
62
238
|
rel="noopener"
|
|
63
239
|
>
|
|
@@ -70,17 +246,15 @@
|
|
|
70
246
|
<path
|
|
71
247
|
d="M165.9 397.4c0 2-2.3 3.6-5.2 3.6c-3.3.3-5.6-1.3-5.6-3.6c0-2 2.3-3.6 5.2-3.6c3-.3 5.6 1.3 5.6 3.6zm-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9c2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3zm44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9c.3 2 2.9 3.3 5.9 2.6c2.9-.7 4.9-2.6 4.6-4.6c-.3-1.9-3-3.2-5.9-2.9zM244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2c12.8 2.3 17.3-5.6 17.3-12.1c0-6.2-.3-40.4-.3-61.4c0 0-70 15-84.7-29.8c0 0-11.4-29.1-27.8-36.6c0 0-22.9-15.7 1.6-15.4c0 0 24.9 2 38.6 25.8c21.9 38.6 58.6 27.5 72.9 20.9c2.3-16 8.8-27.1 16-33.7c-55.9-6.2-112.3-14.3-112.3-110.5c0-27.5 7.6-41.3 23.6-58.9c-2.6-6.5-11.1-33.3 2.6-67.9c20.9-6.5 69 27 69 27c20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27c13.7 34.7 5.2 61.4 2.6 67.9c16 17.7 25.8 31.5 25.8 58.9c0 96.5-58.9 104.2-114.8 110.5c9.2 7.9 17 22.9 17 46.4c0 33.7-.3 75.4-.3 83.6c0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252C496 113.3 383.5 8 244.8 8zM97.2 352.9c-1.3 1-1 3.3.7 5.2c1.6 1.6 3.9 2.3 5.2 1c1.3-1 1-3.3-.7-5.2c-1.6-1.6-3.9-2.3-5.2-1zm-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9c1.6 1 3.6.7 4.3-.7c.7-1.3-.3-2.9-2.3-3.9c-2-.6-3.6-.3-4.3.7zm32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2c2.3 2.3 5.2 2.6 6.5 1c1.3-1.3.7-4.3-1.3-6.2c-2.2-2.3-5.2-2.6-6.5-1zm-11.4-14.7c-1.6 1-1.6 3.6 0 5.9c1.6 2.3 4.3 3.3 5.6 2.3c1.6-1.3 1.6-3.9 0-6.2c-1.4-2.3-4-3.3-5.6-2z"
|
|
72
248
|
fill="currentColor"
|
|
73
|
-
|
|
249
|
+
/>
|
|
74
250
|
</svg>
|
|
75
251
|
</i>
|
|
76
|
-
<span class="lastupdate"
|
|
77
|
-
>最后更新时间:{{ work.lastUpdate }}</span
|
|
78
|
-
>
|
|
252
|
+
<span v-if="work.lastUpdate" class="lastupdate">最后更新时间:{{ work.lastUpdate }}</span>
|
|
79
253
|
</a>
|
|
80
254
|
</div>
|
|
81
255
|
<!-- 其它自定义链接 -->
|
|
82
|
-
<div
|
|
83
|
-
<i
|
|
256
|
+
<div v-if="work.links?.length" class="links">
|
|
257
|
+
<i v-if="work.links?.length" class="icon">
|
|
84
258
|
<svg
|
|
85
259
|
xmlns="http://www.w3.org/2000/svg"
|
|
86
260
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
|
@@ -95,18 +269,18 @@
|
|
|
95
269
|
>
|
|
96
270
|
<path
|
|
97
271
|
d="M10 14a3.5 3.5 0 0 0 5 0l4-4a3.5 3.5 0 0 0-5-5l-.5.5"
|
|
98
|
-
|
|
272
|
+
/>
|
|
99
273
|
<path
|
|
100
274
|
d="M14 10a3.5 3.5 0 0 0-5 0l-4 4a3.5 3.5 0 0 0 5 5l.5-.5"
|
|
101
|
-
|
|
275
|
+
/>
|
|
102
276
|
</g>
|
|
103
277
|
</svg>
|
|
104
278
|
</i>
|
|
105
279
|
<a
|
|
106
|
-
class="link"
|
|
107
280
|
v-for="link in work.links || []"
|
|
108
|
-
:href="link.url"
|
|
109
281
|
:key="link.url"
|
|
282
|
+
class="link"
|
|
283
|
+
:href="link.url"
|
|
110
284
|
:title="link.title"
|
|
111
285
|
target="_blank"
|
|
112
286
|
rel="noopener"
|
|
@@ -115,7 +289,7 @@
|
|
|
115
289
|
</a>
|
|
116
290
|
</div>
|
|
117
291
|
<!-- tags -->
|
|
118
|
-
<div
|
|
292
|
+
<div v-if="work.tags?.length" class="tags">
|
|
119
293
|
<i class="icon">
|
|
120
294
|
<svg
|
|
121
295
|
xmlns="http://www.w3.org/2000/svg"
|
|
@@ -125,48 +299,48 @@
|
|
|
125
299
|
<path
|
|
126
300
|
fill="currentColor"
|
|
127
301
|
d="M256 128v698.88l196.032-156.864a96 96 0 0 1 119.936 0L768 826.816V128H256zm-32-64h576a32 32 0 0 1 32 32v797.44a32 32 0 0 1-51.968 24.96L531.968 720a32 32 0 0 0-39.936 0L243.968 918.4A32 32 0 0 1 192 893.44V96a32 32 0 0 1 32-32z"
|
|
128
|
-
|
|
302
|
+
/>
|
|
129
303
|
</svg>
|
|
130
304
|
</i>
|
|
131
305
|
<span
|
|
132
|
-
@click="handleChooseTag(tag)"
|
|
133
|
-
class="tag"
|
|
134
306
|
v-for="tag in work.tags"
|
|
135
307
|
:key="tag"
|
|
136
|
-
|
|
308
|
+
class="tag"
|
|
309
|
+
@click="handleChooseTag(tag)"
|
|
310
|
+
>{{ tag }}
|
|
137
311
|
</span>
|
|
138
312
|
</div>
|
|
139
313
|
</div>
|
|
140
314
|
<!-- 封面图 -->
|
|
141
|
-
<div
|
|
315
|
+
<div v-if="work.covers?.length" class="images">
|
|
142
316
|
<!-- swiper -->
|
|
143
317
|
<div v-if="work.coverLayout === 'swiper'" class="swiper-mode">
|
|
144
|
-
<
|
|
318
|
+
<ElCarousel
|
|
145
319
|
autoplay
|
|
146
320
|
height="260px"
|
|
147
321
|
:type="isCardMode && work.covers.length >= 3 ? 'card' : ''"
|
|
148
322
|
>
|
|
149
|
-
<
|
|
150
|
-
style="text-align: center"
|
|
323
|
+
<ElCarouselItem
|
|
151
324
|
v-for="(url, idx) in work.covers"
|
|
152
325
|
:key="url"
|
|
326
|
+
style="text-align: center"
|
|
153
327
|
>
|
|
154
|
-
<
|
|
155
|
-
preview-teleported
|
|
328
|
+
<ElImage
|
|
156
329
|
:key="url"
|
|
330
|
+
preview-teleported
|
|
157
331
|
:src="url"
|
|
158
332
|
loading="lazy"
|
|
159
333
|
:preview-src-list="work.covers"
|
|
160
334
|
:initial-index="idx"
|
|
161
335
|
hide-on-click-modal
|
|
162
|
-
:alt="work.title
|
|
336
|
+
:alt="`${work.title}-${idx}`"
|
|
163
337
|
/>
|
|
164
|
-
</
|
|
165
|
-
</
|
|
338
|
+
</ElCarouselItem>
|
|
339
|
+
</ElCarousel>
|
|
166
340
|
</div>
|
|
167
341
|
<!-- list -->
|
|
168
342
|
<div v-if="work.coverLayout === 'list'" class="list-mode">
|
|
169
|
-
<
|
|
343
|
+
<ElImage
|
|
170
344
|
v-for="(url, idx) in work.covers"
|
|
171
345
|
:key="url"
|
|
172
346
|
:src="url"
|
|
@@ -177,7 +351,7 @@
|
|
|
177
351
|
/>
|
|
178
352
|
</div>
|
|
179
353
|
</div>
|
|
180
|
-
<div class="description" v-html="work.description"
|
|
354
|
+
<div class="description" v-html="work.description" />
|
|
181
355
|
</div>
|
|
182
356
|
</div>
|
|
183
357
|
<div class="aside-container">
|
|
@@ -188,176 +362,6 @@
|
|
|
188
362
|
</div>
|
|
189
363
|
</template>
|
|
190
364
|
|
|
191
|
-
<script lang="ts" setup>
|
|
192
|
-
import { ElImage, ElCarousel, ElCarouselItem, ElMessage } from 'element-plus'
|
|
193
|
-
import VPDocAsideOutline from 'vitepress/dist/client/theme-default/components/VPDocAsideOutline.vue'
|
|
194
|
-
import { computed, reactive, ref, watch, watchEffect } from 'vue'
|
|
195
|
-
import { slugify } from '@mdit-vue/shared'
|
|
196
|
-
import { useWindowSize } from '@vueuse/core'
|
|
197
|
-
import {
|
|
198
|
-
getGithubUpdateTime,
|
|
199
|
-
formatDate,
|
|
200
|
-
getGithubDirUpdateTime
|
|
201
|
-
} from '../utils/client'
|
|
202
|
-
import {
|
|
203
|
-
useUserWorks,
|
|
204
|
-
useActiveAnchor,
|
|
205
|
-
useAutoUpdateAnchor
|
|
206
|
-
} from '../composables/config/blog'
|
|
207
|
-
import { Theme } from '../composables/config'
|
|
208
|
-
|
|
209
|
-
const currentAnchor = useAutoUpdateAnchor()
|
|
210
|
-
// 更新锚点的时候更新 url 中的 hash
|
|
211
|
-
watch(
|
|
212
|
-
() => currentAnchor.id,
|
|
213
|
-
(val) => {
|
|
214
|
-
if (val) {
|
|
215
|
-
window.history.replaceState(null, '', `#${val}`)
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
)
|
|
219
|
-
const mountActiveAnchorEl = useActiveAnchor()
|
|
220
|
-
watch(mountActiveAnchorEl, () => {
|
|
221
|
-
const { value } = mountActiveAnchorEl
|
|
222
|
-
if (value) {
|
|
223
|
-
value.scroll({
|
|
224
|
-
behavior: 'smooth'
|
|
225
|
-
})
|
|
226
|
-
}
|
|
227
|
-
})
|
|
228
|
-
|
|
229
|
-
const works = useUserWorks()
|
|
230
|
-
const workList = reactive<
|
|
231
|
-
(Theme.UserWork & {
|
|
232
|
-
year?: string | undefined
|
|
233
|
-
startTime: string
|
|
234
|
-
lastUpdate?: string
|
|
235
|
-
endTime?: string
|
|
236
|
-
covers?: string[]
|
|
237
|
-
coverLayout?: string
|
|
238
|
-
})[]
|
|
239
|
-
>([])
|
|
240
|
-
|
|
241
|
-
// 格式化数据
|
|
242
|
-
watch(
|
|
243
|
-
works,
|
|
244
|
-
(val) => {
|
|
245
|
-
const sortDate = [...val.list].map((v) => {
|
|
246
|
-
const { time } = v
|
|
247
|
-
|
|
248
|
-
// 格式化时间
|
|
249
|
-
const metaInfo =
|
|
250
|
-
typeof time === 'string'
|
|
251
|
-
? {
|
|
252
|
-
startTime: time,
|
|
253
|
-
endTime: '',
|
|
254
|
-
lastUpdate: ''
|
|
255
|
-
}
|
|
256
|
-
: {
|
|
257
|
-
startTime: time.start,
|
|
258
|
-
endTime: time.end,
|
|
259
|
-
lastUpdate: time.lastupdate
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
// 格式化封面信息
|
|
263
|
-
const covers: string[] = []
|
|
264
|
-
let coverLayout = 'swiper'
|
|
265
|
-
|
|
266
|
-
if (typeof v.cover === 'string') {
|
|
267
|
-
covers.push(v.cover)
|
|
268
|
-
} else if (Array.isArray(v.cover)) {
|
|
269
|
-
covers.push(...v.cover)
|
|
270
|
-
} else if (typeof v.cover === 'object') {
|
|
271
|
-
covers.push(...v.cover.urls)
|
|
272
|
-
coverLayout = v.cover.layout ?? coverLayout
|
|
273
|
-
}
|
|
274
|
-
return {
|
|
275
|
-
...v,
|
|
276
|
-
...metaInfo,
|
|
277
|
-
covers,
|
|
278
|
-
coverLayout
|
|
279
|
-
}
|
|
280
|
-
})
|
|
281
|
-
// 过滤出置顶数据
|
|
282
|
-
const topDate = sortDate.filter((v) => v.top !== undefined)
|
|
283
|
-
const normalDate = sortDate.filter((v) => v.top === undefined)
|
|
284
|
-
// 数据排序
|
|
285
|
-
topDate.sort((a, b) => a.top! - b.top!)
|
|
286
|
-
normalDate.sort((a, b) => +new Date(b.startTime) - +new Date(a.startTime))
|
|
287
|
-
if (topDate.length) {
|
|
288
|
-
// @ts-ignore
|
|
289
|
-
topDate[0].year = works.value.topTitle ?? '置顶'
|
|
290
|
-
}
|
|
291
|
-
// 数据分组
|
|
292
|
-
const groupDate = normalDate.reduce((prev, cur) => {
|
|
293
|
-
const { startTime } = cur
|
|
294
|
-
const year = new Date(startTime).getFullYear()
|
|
295
|
-
const data = { ...cur }
|
|
296
|
-
if (!prev[year]) {
|
|
297
|
-
prev[year] = []
|
|
298
|
-
// 第一项数据加上year属性
|
|
299
|
-
// @ts-ignore
|
|
300
|
-
data.year = year
|
|
301
|
-
}
|
|
302
|
-
prev[year].push(data)
|
|
303
|
-
return prev
|
|
304
|
-
}, {} as Record<string, (Theme.UserWork & { year?: string; startTime: string })[]>)
|
|
305
|
-
workList.push(...topDate, ...Object.values(groupDate).reverse().flat())
|
|
306
|
-
},
|
|
307
|
-
{ immediate: true }
|
|
308
|
-
)
|
|
309
|
-
|
|
310
|
-
const init = ref(true)
|
|
311
|
-
// 更新时间信息
|
|
312
|
-
watchEffect(() => {
|
|
313
|
-
if (workList.length && init.value) {
|
|
314
|
-
init.value = false
|
|
315
|
-
workList.forEach((data) => {
|
|
316
|
-
// 接口获取最后更新时间
|
|
317
|
-
if (!data.lastUpdate && data.github) {
|
|
318
|
-
data.lastUpdate = '获取中...'
|
|
319
|
-
const { github } = data
|
|
320
|
-
if (typeof github === 'string') {
|
|
321
|
-
getGithubUpdateTime(github)
|
|
322
|
-
.then((time) => {
|
|
323
|
-
data.lastUpdate = formatDate(time, 'yyyy-MM-dd')
|
|
324
|
-
})
|
|
325
|
-
.catch(() => {
|
|
326
|
-
data.lastUpdate = '地址解析失败'
|
|
327
|
-
})
|
|
328
|
-
} else {
|
|
329
|
-
const { owner, repo, path, branch } = github
|
|
330
|
-
// 拼接Github链接
|
|
331
|
-
let githubUrl = `https://github.com/${owner}/${repo}`
|
|
332
|
-
if (path) {
|
|
333
|
-
githubUrl += `/tree/${branch || 'master'}/${path}`
|
|
334
|
-
} else if (branch) {
|
|
335
|
-
githubUrl += `/tree/${branch}`
|
|
336
|
-
}
|
|
337
|
-
data.github = githubUrl
|
|
338
|
-
getGithubDirUpdateTime(owner, repo, path ?? '', branch)
|
|
339
|
-
.then((time) => {
|
|
340
|
-
data.lastUpdate = formatDate(time, 'yyyy-MM-dd')
|
|
341
|
-
})
|
|
342
|
-
.catch(() => {
|
|
343
|
-
data.lastUpdate = '地址解析失败'
|
|
344
|
-
})
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
})
|
|
348
|
-
}
|
|
349
|
-
})
|
|
350
|
-
|
|
351
|
-
const { width } = useWindowSize()
|
|
352
|
-
const isCardMode = computed(() => width.value > 768)
|
|
353
|
-
const handleChooseTag = (tag: string) => {
|
|
354
|
-
ElMessage({
|
|
355
|
-
message: `点击了${tag}标签,标签过滤功能开发中ing...`,
|
|
356
|
-
type: 'warning'
|
|
357
|
-
})
|
|
358
|
-
}
|
|
359
|
-
</script>
|
|
360
|
-
|
|
361
365
|
<style lang="scss" scoped>
|
|
362
366
|
.user-works-page {
|
|
363
367
|
display: flex;
|
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
import { useData, useRoute, withBase } from 'vitepress'
|
|
2
|
-
import {
|
|
2
|
+
import type {
|
|
3
3
|
Component,
|
|
4
|
+
InjectionKey,
|
|
5
|
+
Ref
|
|
6
|
+
} from 'vue'
|
|
7
|
+
import {
|
|
4
8
|
computed,
|
|
5
9
|
defineComponent,
|
|
6
10
|
h,
|
|
7
11
|
inject,
|
|
8
|
-
InjectionKey,
|
|
9
|
-
provide,
|
|
10
|
-
Ref,
|
|
11
12
|
onMounted,
|
|
12
13
|
onUnmounted,
|
|
14
|
+
provide,
|
|
13
15
|
reactive,
|
|
14
16
|
ref
|
|
15
17
|
} from 'vue'
|
|
@@ -25,12 +27,15 @@ const currentPageNum: InjectionKey<Ref<number>> = Symbol('home-page-num')
|
|
|
25
27
|
|
|
26
28
|
const userWorks: InjectionKey<Ref<Theme.UserWorks>> = Symbol('user-works')
|
|
27
29
|
|
|
30
|
+
const homeFooter: InjectionKey<Theme.Footer | undefined> = Symbol('home-footer')
|
|
31
|
+
|
|
28
32
|
export function withConfigProvider(App: Component) {
|
|
29
33
|
return defineComponent({
|
|
30
34
|
name: 'ConfigProvider',
|
|
31
35
|
setup(_, { slots }) {
|
|
32
36
|
const { theme } = useData()
|
|
33
37
|
const config = computed(() => resolveConfig(theme.value))
|
|
38
|
+
provide(homeFooter, config.value.blog?.footer)
|
|
34
39
|
provide(configSymbol, config)
|
|
35
40
|
provide(
|
|
36
41
|
userWorks,
|
|
@@ -119,7 +124,7 @@ export function useCurrentArticle() {
|
|
|
119
124
|
...[`${currentPath}index`, `${decodeURIComponent(currentPath)}index`]
|
|
120
125
|
)
|
|
121
126
|
}
|
|
122
|
-
return docs.value?.find(
|
|
127
|
+
return docs.value?.find(v => okPaths.includes(withBase(v.route)))
|
|
123
128
|
})
|
|
124
129
|
|
|
125
130
|
return currentArticle
|
|
@@ -195,3 +200,7 @@ export function useAutoUpdateAnchor() {
|
|
|
195
200
|
// 返回当前锚点的响应式对象
|
|
196
201
|
return currentAnchor
|
|
197
202
|
}
|
|
203
|
+
|
|
204
|
+
export function useHomeFooterConfig() {
|
|
205
|
+
return inject(homeFooter)
|
|
206
|
+
}
|