@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,22 +1,3 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<div class="card overview-data">
|
|
3
|
-
<div class="overview-item">
|
|
4
|
-
<span class="count">{{ notHiddenArticles.length }}</span>
|
|
5
|
-
<span class="label">博客文章</span>
|
|
6
|
-
</div>
|
|
7
|
-
<div class="split"></div>
|
|
8
|
-
<div class="overview-item">
|
|
9
|
-
<span class="count">+{{ currentMonth?.length }}</span>
|
|
10
|
-
<span class="label">本月更新</span>
|
|
11
|
-
</div>
|
|
12
|
-
<div class="split"></div>
|
|
13
|
-
<div class="overview-item">
|
|
14
|
-
<span class="count">+{{ currentWeek?.length }}</span>
|
|
15
|
-
<span class="label">本周更新</span>
|
|
16
|
-
</div>
|
|
17
|
-
</div>
|
|
18
|
-
</template>
|
|
19
|
-
|
|
20
1
|
<script lang="ts" setup>
|
|
21
2
|
import { computed } from 'vue'
|
|
22
3
|
import { isCurrentWeek } from '../utils/client'
|
|
@@ -24,7 +5,7 @@ import { useArticles } from '../composables/config/blog'
|
|
|
24
5
|
|
|
25
6
|
const docs = useArticles()
|
|
26
7
|
const notHiddenArticles = computed(() => {
|
|
27
|
-
return docs.value.filter(
|
|
8
|
+
return docs.value.filter(v => v.meta?.publish !== false)
|
|
28
9
|
})
|
|
29
10
|
const nowMonth = new Date().getMonth()
|
|
30
11
|
const nowYear = new Date().getFullYear()
|
|
@@ -43,6 +24,25 @@ const currentWeek = computed(() => {
|
|
|
43
24
|
})
|
|
44
25
|
</script>
|
|
45
26
|
|
|
27
|
+
<template>
|
|
28
|
+
<div class="card overview-data">
|
|
29
|
+
<div class="overview-item">
|
|
30
|
+
<span class="count">{{ notHiddenArticles.length }}</span>
|
|
31
|
+
<span class="label">博客文章</span>
|
|
32
|
+
</div>
|
|
33
|
+
<div class="split" />
|
|
34
|
+
<div class="overview-item">
|
|
35
|
+
<span class="count">+{{ currentMonth?.length }}</span>
|
|
36
|
+
<span class="label">本月更新</span>
|
|
37
|
+
</div>
|
|
38
|
+
<div class="split" />
|
|
39
|
+
<div class="overview-item">
|
|
40
|
+
<span class="count">+{{ currentWeek?.length }}</span>
|
|
41
|
+
<span class="label">本周更新</span>
|
|
42
|
+
</div>
|
|
43
|
+
</div>
|
|
44
|
+
</template>
|
|
45
|
+
|
|
46
46
|
<style lang="scss" scoped>
|
|
47
47
|
.card {
|
|
48
48
|
position: relative;
|
|
@@ -1,38 +1,3 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<div class="card tags" v-if="tags.length" data-pagefind-ignore="all">
|
|
3
|
-
<!-- 头部 -->
|
|
4
|
-
<div class="card-header">
|
|
5
|
-
<span class="title svg-icon"><svg t="1695048840129" class="icon" viewBox="0 0 1024 1024" version="1.1"
|
|
6
|
-
xmlns="http://www.w3.org/2000/svg" p-id="4290" width="200" height="200">
|
|
7
|
-
<path
|
|
8
|
-
d="M810.88 245.888a118.432 118.432 0 1 0 0 236.864 118.432 118.432 0 0 0 0-236.864z m-151.008 118.432a151.008 151.008 0 1 1 302.016 0 151.008 151.008 0 0 1-302.016 0z"
|
|
9
|
-
fill="#D3D3D3" p-id="4291"></path>
|
|
10
|
-
<path
|
|
11
|
-
d="M774.08 565.6l61.76-160.64c6.4-16.64 2.56-35.84-10.24-48.64l-151.04-151.04c-12.8-12.8-31.68-16.64-48.64-10.24l-160.64 61.76c-12.16 4.8-23.36 11.84-32.64 21.12l-355.2 355.2c-17.92 17.92-17.92 46.72 0 64.32l256 256c17.92 17.92 46.72 17.92 64.32 0l355.2-355.2c9.28-9.28 16.32-20.16 21.12-32.64z m-159.36-149.12c-22.08-22.08-22.08-57.6 0-79.68 22.08-22.08 57.6-22.08 79.68 0 22.08 22.08 22.08 57.6 0 79.68-22.08 21.76-57.92 21.76-79.68 0z"
|
|
12
|
-
fill="#FCD53F" p-id="4292"></path>
|
|
13
|
-
<path
|
|
14
|
-
d="M654.4 320.48c14.4 0 28.8 5.44 39.68 16.64 22.08 22.08 22.08 57.6 0 79.68-10.88 10.88-25.28 16.64-39.68 16.64-14.4 0-28.8-5.44-39.68-16.64-22.08-22.08-22.08-57.6 0-79.68 10.88-11.2 25.28-16.64 39.68-16.64z m0-30.08c-23.04 0-44.8 8.96-61.12 25.28a86.72 86.72 0 0 0 0 122.24c16.32 16.32 38.08 25.28 61.12 25.28s44.8-8.96 61.12-25.28a86.72 86.72 0 0 0 0-122.24c-16.32-16.32-38.08-25.28-61.12-25.28z"
|
|
15
|
-
fill="#F8312F" p-id="4293"></path>
|
|
16
|
-
<path
|
|
17
|
-
d="M676.16 348.032c8.992 0 16.288 7.296 16.288 16.288a118.144 118.144 0 0 0 64.288 105.44h0.064c22.24 11.296 47.36 15.264 71.68 11.84a16.288 16.288 0 0 1 4.48 32.32 154.24 154.24 0 0 1-90.848-15.04 150.72 150.72 0 0 1-82.24-134.56c0-8.992 7.296-16.288 16.288-16.288z"
|
|
18
|
-
fill="#D3D3D3" p-id="4294"></path>
|
|
19
|
-
</svg> 标签</span>
|
|
20
|
-
<el-tag v-if="activeTag.label" :type="(activeTag.type as any)" :effect="colorMode" closable @close="handleCloseTag">
|
|
21
|
-
{{ activeTag.label }}
|
|
22
|
-
</el-tag>
|
|
23
|
-
</div>
|
|
24
|
-
<!-- 标签列表 -->
|
|
25
|
-
<ul class="tag-list">
|
|
26
|
-
<li v-for="(tag, idx) in tags" :key="tag">
|
|
27
|
-
<el-tag :type="tagType[idx % tagType.length]" @click="handleTagClick(tag, tagType[idx % tagType.length])"
|
|
28
|
-
:effect="colorMode">
|
|
29
|
-
{{ tag }}
|
|
30
|
-
</el-tag>
|
|
31
|
-
</li>
|
|
32
|
-
</ul>
|
|
33
|
-
</div>
|
|
34
|
-
</template>
|
|
35
|
-
|
|
36
1
|
<script lang="ts" setup>
|
|
37
2
|
import { computed, watch } from 'vue'
|
|
38
3
|
import { ElTag } from 'element-plus'
|
|
@@ -47,7 +12,7 @@ import {
|
|
|
47
12
|
const docs = useArticles()
|
|
48
13
|
|
|
49
14
|
const tags = computed(() => {
|
|
50
|
-
return [...new Set(docs.value.map(
|
|
15
|
+
return [...new Set(docs.value.map(v => v.meta.tag || []).flat(3))]
|
|
51
16
|
})
|
|
52
17
|
|
|
53
18
|
const activeTag = useActiveTag()
|
|
@@ -60,18 +25,18 @@ const colorMode = computed(() => (isDark.value ? 'light' : 'dark'))
|
|
|
60
25
|
|
|
61
26
|
const tagType: any = ['', 'info', 'success', 'warning', 'danger']
|
|
62
27
|
const currentPage = useCurrentPageNum()
|
|
28
|
+
const router = useRouter()
|
|
63
29
|
|
|
64
|
-
|
|
30
|
+
function handleCloseTag() {
|
|
65
31
|
activeTag.value.label = ''
|
|
66
32
|
activeTag.value.type = ''
|
|
67
33
|
currentPage.value = 1
|
|
68
34
|
router.go(`${window.location.origin}${router.route.path}`)
|
|
69
35
|
}
|
|
70
36
|
|
|
71
|
-
const router = useRouter()
|
|
72
37
|
const location = useBrowserLocation()
|
|
73
38
|
|
|
74
|
-
|
|
39
|
+
function handleTagClick(tag: string, type: string) {
|
|
75
40
|
if (tag === activeTag.value.label) {
|
|
76
41
|
handleCloseTag()
|
|
77
42
|
return
|
|
@@ -99,6 +64,49 @@ watch(
|
|
|
99
64
|
)
|
|
100
65
|
</script>
|
|
101
66
|
|
|
67
|
+
<template>
|
|
68
|
+
<div v-if="tags.length" class="card tags" data-pagefind-ignore="all">
|
|
69
|
+
<!-- 头部 -->
|
|
70
|
+
<div class="card-header">
|
|
71
|
+
<span class="title svg-icon"><svg
|
|
72
|
+
t="1695048840129" class="icon" viewBox="0 0 1024 1024" version="1.1"
|
|
73
|
+
xmlns="http://www.w3.org/2000/svg" p-id="4290" width="200" height="200"
|
|
74
|
+
>
|
|
75
|
+
<path
|
|
76
|
+
d="M810.88 245.888a118.432 118.432 0 1 0 0 236.864 118.432 118.432 0 0 0 0-236.864z m-151.008 118.432a151.008 151.008 0 1 1 302.016 0 151.008 151.008 0 0 1-302.016 0z"
|
|
77
|
+
fill="#D3D3D3" p-id="4291"
|
|
78
|
+
/>
|
|
79
|
+
<path
|
|
80
|
+
d="M774.08 565.6l61.76-160.64c6.4-16.64 2.56-35.84-10.24-48.64l-151.04-151.04c-12.8-12.8-31.68-16.64-48.64-10.24l-160.64 61.76c-12.16 4.8-23.36 11.84-32.64 21.12l-355.2 355.2c-17.92 17.92-17.92 46.72 0 64.32l256 256c17.92 17.92 46.72 17.92 64.32 0l355.2-355.2c9.28-9.28 16.32-20.16 21.12-32.64z m-159.36-149.12c-22.08-22.08-22.08-57.6 0-79.68 22.08-22.08 57.6-22.08 79.68 0 22.08 22.08 22.08 57.6 0 79.68-22.08 21.76-57.92 21.76-79.68 0z"
|
|
81
|
+
fill="#FCD53F" p-id="4292"
|
|
82
|
+
/>
|
|
83
|
+
<path
|
|
84
|
+
d="M654.4 320.48c14.4 0 28.8 5.44 39.68 16.64 22.08 22.08 22.08 57.6 0 79.68-10.88 10.88-25.28 16.64-39.68 16.64-14.4 0-28.8-5.44-39.68-16.64-22.08-22.08-22.08-57.6 0-79.68 10.88-11.2 25.28-16.64 39.68-16.64z m0-30.08c-23.04 0-44.8 8.96-61.12 25.28a86.72 86.72 0 0 0 0 122.24c16.32 16.32 38.08 25.28 61.12 25.28s44.8-8.96 61.12-25.28a86.72 86.72 0 0 0 0-122.24c-16.32-16.32-38.08-25.28-61.12-25.28z"
|
|
85
|
+
fill="#F8312F" p-id="4293"
|
|
86
|
+
/>
|
|
87
|
+
<path
|
|
88
|
+
d="M676.16 348.032c8.992 0 16.288 7.296 16.288 16.288a118.144 118.144 0 0 0 64.288 105.44h0.064c22.24 11.296 47.36 15.264 71.68 11.84a16.288 16.288 0 0 1 4.48 32.32 154.24 154.24 0 0 1-90.848-15.04 150.72 150.72 0 0 1-82.24-134.56c0-8.992 7.296-16.288 16.288-16.288z"
|
|
89
|
+
fill="#D3D3D3" p-id="4294"
|
|
90
|
+
/>
|
|
91
|
+
</svg> 标签</span>
|
|
92
|
+
<ElTag v-if="activeTag.label" :type="activeTag.type as any" :effect="colorMode" closable @close="handleCloseTag">
|
|
93
|
+
{{ activeTag.label }}
|
|
94
|
+
</ElTag>
|
|
95
|
+
</div>
|
|
96
|
+
<!-- 标签列表 -->
|
|
97
|
+
<ul class="tag-list">
|
|
98
|
+
<li v-for="(tag, idx) in tags" :key="tag">
|
|
99
|
+
<ElTag
|
|
100
|
+
:type="tagType[idx % tagType.length]" :effect="colorMode"
|
|
101
|
+
@click="handleTagClick(tag, tagType[idx % tagType.length])"
|
|
102
|
+
>
|
|
103
|
+
{{ tag }}
|
|
104
|
+
</ElTag>
|
|
105
|
+
</li>
|
|
106
|
+
</ul>
|
|
107
|
+
</div>
|
|
108
|
+
</template>
|
|
109
|
+
|
|
102
110
|
<style lang="scss" scoped>
|
|
103
111
|
.card {
|
|
104
112
|
position: relative;
|
|
@@ -145,4 +153,5 @@ watch(
|
|
|
145
153
|
margin-bottom: 10px;
|
|
146
154
|
cursor: pointer;
|
|
147
155
|
}
|
|
148
|
-
}
|
|
156
|
+
}
|
|
157
|
+
</style>
|
|
@@ -1,40 +1,11 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<div class="card recommend" v-if="recommendList.length || empty" data-pagefind-ignore="all">
|
|
3
|
-
<!-- 头部 -->
|
|
4
|
-
<div class="card-header">
|
|
5
|
-
<span class="title" v-html="title"></span>
|
|
6
|
-
<el-button v-if="showChangeBtn" size="small" type="primary" text @click="changePage">{{ nextText }}</el-button>
|
|
7
|
-
</div>
|
|
8
|
-
<!-- 文章列表 -->
|
|
9
|
-
<ol class="recommend-container" v-if="currentWikiData.length">
|
|
10
|
-
<li v-for="(v, idx) in currentWikiData" :key="v.route">
|
|
11
|
-
<!-- 序号 -->
|
|
12
|
-
<i class="num">{{ idx + 1 }}</i>
|
|
13
|
-
<!-- 简介 -->
|
|
14
|
-
<div class="des">
|
|
15
|
-
<!-- title -->
|
|
16
|
-
<el-link type="info" class="title" :href="withBase(v.route)">{{
|
|
17
|
-
v.meta.title
|
|
18
|
-
}}</el-link>
|
|
19
|
-
<!-- 描述信息 -->
|
|
20
|
-
<div class="suffix">
|
|
21
|
-
<!-- 日期 -->
|
|
22
|
-
<span class="tag">{{ formatShowDate(v.meta.date) }}</span>
|
|
23
|
-
</div>
|
|
24
|
-
</div>
|
|
25
|
-
</li>
|
|
26
|
-
</ol>
|
|
27
|
-
<div class="empty-text" v-else>{{ empty }}</div>
|
|
28
|
-
</div>
|
|
29
|
-
</template>
|
|
30
|
-
|
|
31
1
|
<script lang="ts" setup>
|
|
32
|
-
import {
|
|
2
|
+
import { computed, ref } from 'vue'
|
|
33
3
|
import { ElButton, ElLink } from 'element-plus'
|
|
34
4
|
import { withBase } from 'vitepress'
|
|
35
5
|
import { useArticles, useBlogConfig } from '../composables/config/blog'
|
|
36
6
|
import { formatShowDate } from '../utils/client'
|
|
37
7
|
import { fireSVG } from '../constants/svg'
|
|
8
|
+
|
|
38
9
|
const { hotArticle } = useBlogConfig()
|
|
39
10
|
const title = computed(() => hotArticle?.title || (`<span class="svg-icon">${fireSVG}</span>` + ' 精选文章'))
|
|
40
11
|
const nextText = computed(() => hotArticle?.nextText || '换一组')
|
|
@@ -44,15 +15,15 @@ const empty = computed(() => hotArticle?.empty ?? '暂无精选内容')
|
|
|
44
15
|
const docs = useArticles()
|
|
45
16
|
|
|
46
17
|
const recommendList = computed(() => {
|
|
47
|
-
const data = docs.value.filter(
|
|
18
|
+
const data = docs.value.filter(v => v.meta.sticky)
|
|
48
19
|
data.sort((a, b) => b.meta.sticky! - a.meta.sticky!)
|
|
49
20
|
return [...data]
|
|
50
21
|
})
|
|
51
22
|
|
|
52
23
|
const currentPage = ref(1)
|
|
53
|
-
|
|
54
|
-
const newIdx
|
|
55
|
-
currentPage.value % Math.ceil(recommendList.value.length / pageSize.value)
|
|
24
|
+
function changePage() {
|
|
25
|
+
const newIdx
|
|
26
|
+
= currentPage.value % Math.ceil(recommendList.value.length / pageSize.value)
|
|
56
27
|
currentPage.value = newIdx + 1
|
|
57
28
|
}
|
|
58
29
|
|
|
@@ -67,6 +38,42 @@ const showChangeBtn = computed(() => {
|
|
|
67
38
|
})
|
|
68
39
|
</script>
|
|
69
40
|
|
|
41
|
+
<template>
|
|
42
|
+
<div v-if="recommendList.length || empty" class="card recommend" data-pagefind-ignore="all">
|
|
43
|
+
<!-- 头部 -->
|
|
44
|
+
<div class="card-header">
|
|
45
|
+
<span class="title" v-html="title" />
|
|
46
|
+
<ElButton v-if="showChangeBtn" size="small" type="primary" text @click="changePage">
|
|
47
|
+
{{ nextText }}
|
|
48
|
+
</ElButton>
|
|
49
|
+
</div>
|
|
50
|
+
<!-- 文章列表 -->
|
|
51
|
+
<ol v-if="currentWikiData.length" class="recommend-container">
|
|
52
|
+
<li v-for="(v, idx) in currentWikiData" :key="v.route">
|
|
53
|
+
<!-- 序号 -->
|
|
54
|
+
<i class="num">{{ idx + 1 }}</i>
|
|
55
|
+
<!-- 简介 -->
|
|
56
|
+
<div class="des">
|
|
57
|
+
<!-- title -->
|
|
58
|
+
<ElLink type="info" class="title" :href="withBase(v.route)">
|
|
59
|
+
{{
|
|
60
|
+
v.meta.title
|
|
61
|
+
}}
|
|
62
|
+
</ElLink>
|
|
63
|
+
<!-- 描述信息 -->
|
|
64
|
+
<div class="suffix">
|
|
65
|
+
<!-- 日期 -->
|
|
66
|
+
<span class="tag">{{ formatShowDate(v.meta.date) }}</span>
|
|
67
|
+
</div>
|
|
68
|
+
</div>
|
|
69
|
+
</li>
|
|
70
|
+
</ol>
|
|
71
|
+
<div v-else class="empty-text">
|
|
72
|
+
{{ empty }}
|
|
73
|
+
</div>
|
|
74
|
+
</div>
|
|
75
|
+
</template>
|
|
76
|
+
|
|
70
77
|
<style lang="scss" scoped>
|
|
71
78
|
.card {
|
|
72
79
|
position: relative;
|
|
@@ -170,4 +177,4 @@ const showChangeBtn = computed(() => {
|
|
|
170
177
|
font-size: 14px;
|
|
171
178
|
text-align: center;
|
|
172
179
|
}
|
|
173
|
-
</style>
|
|
180
|
+
</style>
|
|
@@ -10,15 +10,15 @@ const previewImageInfo = reactive<{ url: string; list: string[]; idx: number }>(
|
|
|
10
10
|
idx: 0
|
|
11
11
|
}
|
|
12
12
|
)
|
|
13
|
-
|
|
13
|
+
function previewImage(e: Event) {
|
|
14
14
|
const target = e.target as HTMLElement
|
|
15
15
|
const currentTarget = e.currentTarget as HTMLElement
|
|
16
16
|
if (target.tagName.toLowerCase() === 'img') {
|
|
17
17
|
const imgs = currentTarget.querySelectorAll<HTMLImageElement>(
|
|
18
18
|
'.content-container .main img'
|
|
19
19
|
)
|
|
20
|
-
const idx = Array.from(imgs).findIndex(
|
|
21
|
-
const urls = Array.from(imgs).map(
|
|
20
|
+
const idx = Array.from(imgs).findIndex(el => el === target)
|
|
21
|
+
const urls = Array.from(imgs).map(el => el.src)
|
|
22
22
|
|
|
23
23
|
const url = target.getAttribute('src')
|
|
24
24
|
previewImageInfo.url = url!
|
|
@@ -45,6 +45,8 @@ onUnmounted(() => {
|
|
|
45
45
|
</script>
|
|
46
46
|
|
|
47
47
|
<template>
|
|
48
|
-
<ElImageViewer
|
|
49
|
-
|
|
48
|
+
<ElImageViewer
|
|
49
|
+
v-if="show" :infinite="false" hide-on-click-modal teleported :url-list="previewImageInfo.list"
|
|
50
|
+
:initial-index="previewImageInfo.idx" @close="show = false"
|
|
51
|
+
/>
|
|
50
52
|
</template>
|
|
@@ -1,51 +1,9 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<a class="blog-item" :href="withBase(route)">
|
|
3
|
-
<i class="pin" v-if="!!pin"></i>
|
|
4
|
-
<!-- 标题 -->
|
|
5
|
-
<p class="title" v-if="inMobile">{{ title }}</p>
|
|
6
|
-
<div class="info-container">
|
|
7
|
-
<!-- 左侧信息 -->
|
|
8
|
-
<div class="info-part">
|
|
9
|
-
<!-- 标题 -->
|
|
10
|
-
<p class="title" v-if="!inMobile">{{ title }}</p>
|
|
11
|
-
<!-- 简短描述 -->
|
|
12
|
-
<p class="description" v-if="!descriptionHTML && !!description">
|
|
13
|
-
{{ description }}
|
|
14
|
-
</p>
|
|
15
|
-
<template v-if="descriptionHTML">
|
|
16
|
-
<div class="description-html" v-html="descriptionHTML"></div>
|
|
17
|
-
</template>
|
|
18
|
-
<!-- 底部补充描述 -->
|
|
19
|
-
<div class="badge-list" v-if="!inMobile">
|
|
20
|
-
<span class="split" v-if="author">{{ author }}</span>
|
|
21
|
-
<span class="split">{{ showTime }}</span>
|
|
22
|
-
<span class="split" v-if="tag?.length">{{ tag.join(' · ') }}</span>
|
|
23
|
-
</div>
|
|
24
|
-
</div>
|
|
25
|
-
<!-- 右侧封面图 -->
|
|
26
|
-
<div
|
|
27
|
-
v-if="cover"
|
|
28
|
-
class="cover-img"
|
|
29
|
-
:style="`background-image: url(${cover});`"
|
|
30
|
-
></div>
|
|
31
|
-
</div>
|
|
32
|
-
<!-- 底部补充描述 -->
|
|
33
|
-
<div class="badge-list" v-if="inMobile">
|
|
34
|
-
<span class="split" v-if="author">{{ author }}</span>
|
|
35
|
-
<span class="split">{{ showTime }}</span>
|
|
36
|
-
<span class="split" v-if="tag?.length">{{ tag.join(' · ') }}</span>
|
|
37
|
-
</div>
|
|
38
|
-
</a>
|
|
39
|
-
</template>
|
|
40
|
-
|
|
41
1
|
<script lang="ts" setup>
|
|
42
2
|
import { withBase } from 'vitepress'
|
|
43
3
|
import { computed } from 'vue'
|
|
44
4
|
import { useWindowSize } from '@vueuse/core'
|
|
45
5
|
import { formatShowDate } from '../utils/client'
|
|
46
6
|
|
|
47
|
-
const { width } = useWindowSize()
|
|
48
|
-
const inMobile = computed(() => width.value <= 500)
|
|
49
7
|
const props = defineProps<{
|
|
50
8
|
route: string
|
|
51
9
|
title: string
|
|
@@ -58,7 +16,8 @@ const props = defineProps<{
|
|
|
58
16
|
cover?: string | boolean
|
|
59
17
|
pin?: number
|
|
60
18
|
}>()
|
|
61
|
-
|
|
19
|
+
const { width } = useWindowSize()
|
|
20
|
+
const inMobile = computed(() => width.value <= 500)
|
|
62
21
|
const showTime = computed(() => {
|
|
63
22
|
return formatShowDate(props.date)
|
|
64
23
|
})
|
|
@@ -77,6 +36,46 @@ const showTime = computed(() => {
|
|
|
77
36
|
// }
|
|
78
37
|
</script>
|
|
79
38
|
|
|
39
|
+
<template>
|
|
40
|
+
<a class="blog-item" :href="withBase(route)">
|
|
41
|
+
<i v-if="!!pin" class="pin" />
|
|
42
|
+
<!-- 标题 -->
|
|
43
|
+
<p v-if="inMobile" class="title">{{ title }}</p>
|
|
44
|
+
<div class="info-container">
|
|
45
|
+
<!-- 左侧信息 -->
|
|
46
|
+
<div class="info-part">
|
|
47
|
+
<!-- 标题 -->
|
|
48
|
+
<p v-if="!inMobile" class="title">{{ title }}</p>
|
|
49
|
+
<!-- 简短描述 -->
|
|
50
|
+
<p v-if="!descriptionHTML && !!description" class="description">
|
|
51
|
+
{{ description }}
|
|
52
|
+
</p>
|
|
53
|
+
<template v-if="descriptionHTML">
|
|
54
|
+
<div class="description-html" v-html="descriptionHTML" />
|
|
55
|
+
</template>
|
|
56
|
+
<!-- 底部补充描述 -->
|
|
57
|
+
<div v-if="!inMobile" class="badge-list">
|
|
58
|
+
<span v-if="author" class="split">{{ author }}</span>
|
|
59
|
+
<span class="split">{{ showTime }}</span>
|
|
60
|
+
<span v-if="tag?.length" class="split">{{ tag.join(' · ') }}</span>
|
|
61
|
+
</div>
|
|
62
|
+
</div>
|
|
63
|
+
<!-- 右侧封面图 -->
|
|
64
|
+
<div
|
|
65
|
+
v-if="cover"
|
|
66
|
+
class="cover-img"
|
|
67
|
+
:style="`background-image: url(${cover});`"
|
|
68
|
+
/>
|
|
69
|
+
</div>
|
|
70
|
+
<!-- 底部补充描述 -->
|
|
71
|
+
<div v-if="inMobile" class="badge-list">
|
|
72
|
+
<span v-if="author" class="split">{{ author }}</span>
|
|
73
|
+
<span class="split">{{ showTime }}</span>
|
|
74
|
+
<span v-if="tag?.length" class="split">{{ tag.join(' · ') }}</span>
|
|
75
|
+
</div>
|
|
76
|
+
</a>
|
|
77
|
+
</template>
|
|
78
|
+
|
|
80
79
|
<style lang="scss" scoped>
|
|
81
80
|
.blog-item .pin {
|
|
82
81
|
position: absolute;
|
|
@@ -1,49 +1,16 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<ul data-pagefind-ignore="all">
|
|
3
|
-
<li v-for="v in currentWikiData" :key="v.route">
|
|
4
|
-
<blog-item
|
|
5
|
-
:route="v.route"
|
|
6
|
-
:title="v.meta.title"
|
|
7
|
-
:description="v.meta.description"
|
|
8
|
-
:description-h-t-m-l="v.meta.descriptionHTML"
|
|
9
|
-
:date="v.meta.date"
|
|
10
|
-
:tag="v.meta.tag"
|
|
11
|
-
:cover="v.meta.cover"
|
|
12
|
-
:author="v.meta.author || globalAuthor"
|
|
13
|
-
:pin="v.meta.top"
|
|
14
|
-
/>
|
|
15
|
-
</li>
|
|
16
|
-
</ul>
|
|
17
|
-
<!-- 解决element-ui bug -->
|
|
18
|
-
<ClientOnly>
|
|
19
|
-
<div class="el-pagination-wrapper">
|
|
20
|
-
<el-pagination
|
|
21
|
-
v-if="wikiList.length >= pageSize"
|
|
22
|
-
small
|
|
23
|
-
background
|
|
24
|
-
:default-current-page="1"
|
|
25
|
-
:current-page="currentPage"
|
|
26
|
-
@update:current-page="handleUpdatePageNum"
|
|
27
|
-
:page-size="pageSize"
|
|
28
|
-
:total="filterData.length"
|
|
29
|
-
layout="prev, pager, next, jumper"
|
|
30
|
-
/>
|
|
31
|
-
</div>
|
|
32
|
-
</ClientOnly>
|
|
33
|
-
</template>
|
|
34
1
|
<script setup lang="ts">
|
|
35
2
|
import { computed, watch } from 'vue'
|
|
36
3
|
import { ElPagination } from 'element-plus'
|
|
37
4
|
import { useData, useRouter } from 'vitepress'
|
|
38
5
|
import { useBrowserLocation } from '@vueuse/core'
|
|
39
|
-
import BlogItem from './BlogItem.vue'
|
|
40
6
|
import {
|
|
41
|
-
useArticles,
|
|
42
7
|
useActiveTag,
|
|
8
|
+
useArticles,
|
|
43
9
|
useBlogConfig,
|
|
44
10
|
useCurrentPageNum
|
|
45
11
|
} from '../composables/config/blog'
|
|
46
|
-
import { Theme } from '../composables/config'
|
|
12
|
+
import type { Theme } from '../composables/config'
|
|
13
|
+
import BlogItem from './BlogItem.vue'
|
|
47
14
|
|
|
48
15
|
const { theme, frontmatter } = useData<Theme.Config>()
|
|
49
16
|
const globalAuthor = computed(() => theme.value.blog?.author || '')
|
|
@@ -54,22 +21,23 @@ const activeTag = useActiveTag()
|
|
|
54
21
|
const activeTagLabel = computed(() => activeTag.value.label)
|
|
55
22
|
|
|
56
23
|
const wikiList = computed(() => {
|
|
57
|
-
const topList = docs.value.filter(
|
|
24
|
+
const topList = docs.value.filter(v => !v.meta.hidden && !!v.meta.top)
|
|
58
25
|
topList.sort((a, b) => {
|
|
59
26
|
const aTop = a?.meta?.top
|
|
60
27
|
const bTop = b?.meta.top
|
|
61
28
|
return Number(aTop) - Number(bTop)
|
|
62
29
|
})
|
|
63
30
|
const data = docs.value.filter(
|
|
64
|
-
|
|
31
|
+
v => v.meta.date && v.meta.title && !v.meta.top && !v.meta.hidden
|
|
65
32
|
)
|
|
66
33
|
data.sort((a, b) => +new Date(b.meta.date) - +new Date(a.meta.date))
|
|
67
34
|
return topList.concat(data)
|
|
68
35
|
})
|
|
69
36
|
|
|
70
37
|
const filterData = computed(() => {
|
|
71
|
-
if (!activeTagLabel.value)
|
|
72
|
-
|
|
38
|
+
if (!activeTagLabel.value)
|
|
39
|
+
return wikiList.value
|
|
40
|
+
return wikiList.value.filter(v =>
|
|
73
41
|
v.meta?.tag?.includes(activeTagLabel.value)
|
|
74
42
|
)
|
|
75
43
|
})
|
|
@@ -88,7 +56,7 @@ const currentWikiData = computed(() => {
|
|
|
88
56
|
const router = useRouter()
|
|
89
57
|
const location = useBrowserLocation()
|
|
90
58
|
const queryPageNumKey = 'pageNum'
|
|
91
|
-
|
|
59
|
+
function handleUpdatePageNum(current: number) {
|
|
92
60
|
if (currentPage.value === current) {
|
|
93
61
|
return
|
|
94
62
|
}
|
|
@@ -108,7 +76,8 @@ watch(
|
|
|
108
76
|
const { searchParams } = new URL(location.value.href)
|
|
109
77
|
if (searchParams.has(queryPageNumKey)) {
|
|
110
78
|
currentPage.value = Number(searchParams.get(queryPageNumKey))
|
|
111
|
-
}
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
112
81
|
currentPage.value = 1
|
|
113
82
|
}
|
|
114
83
|
}
|
|
@@ -118,6 +87,41 @@ watch(
|
|
|
118
87
|
}
|
|
119
88
|
)
|
|
120
89
|
</script>
|
|
90
|
+
|
|
91
|
+
<template>
|
|
92
|
+
<ul data-pagefind-ignore="all">
|
|
93
|
+
<li v-for="v in currentWikiData" :key="v.route">
|
|
94
|
+
<BlogItem
|
|
95
|
+
:route="v.route"
|
|
96
|
+
:title="v.meta.title"
|
|
97
|
+
:description="v.meta.description"
|
|
98
|
+
:description-h-t-m-l="v.meta.descriptionHTML"
|
|
99
|
+
:date="v.meta.date"
|
|
100
|
+
:tag="v.meta.tag"
|
|
101
|
+
:cover="v.meta.cover"
|
|
102
|
+
:author="v.meta.author || globalAuthor"
|
|
103
|
+
:pin="v.meta.top"
|
|
104
|
+
/>
|
|
105
|
+
</li>
|
|
106
|
+
</ul>
|
|
107
|
+
<!-- 解决element-ui bug -->
|
|
108
|
+
<ClientOnly>
|
|
109
|
+
<div class="el-pagination-wrapper">
|
|
110
|
+
<ElPagination
|
|
111
|
+
v-if="wikiList.length >= pageSize"
|
|
112
|
+
small
|
|
113
|
+
background
|
|
114
|
+
:default-current-page="1"
|
|
115
|
+
:current-page="currentPage"
|
|
116
|
+
:page-size="pageSize"
|
|
117
|
+
:total="filterData.length"
|
|
118
|
+
layout="prev, pager, next, jumper"
|
|
119
|
+
@update:current-page="handleUpdatePageNum"
|
|
120
|
+
/>
|
|
121
|
+
</div>
|
|
122
|
+
</ClientOnly>
|
|
123
|
+
</template>
|
|
124
|
+
|
|
121
125
|
<style lang="scss" scoped>
|
|
122
126
|
.el-pagination-wrapper {
|
|
123
127
|
:deep(.el-pagination li.is-active.number) {
|
|
@@ -1,39 +1,7 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<div class="theme-blog-popover" v-show="show" data-pagefind-ignore="all">
|
|
3
|
-
<div class="header">
|
|
4
|
-
<div class="title-wrapper">
|
|
5
|
-
<el-icon size="20px"><Flag /></el-icon>
|
|
6
|
-
<span class="title">{{ popoverProps?.title }}</span>
|
|
7
|
-
</div>
|
|
8
|
-
<el-icon @click="show = false" class="close-icon" size="20px"
|
|
9
|
-
><CircleCloseFilled
|
|
10
|
-
/></el-icon>
|
|
11
|
-
</div>
|
|
12
|
-
<div class="body content" v-if="bodyContent.length">
|
|
13
|
-
<PopoverValue v-for="(v, idx) in bodyContent" :key="idx" :item="v">
|
|
14
|
-
{{ v.type !== 'image' ? v.content : '' }}
|
|
15
|
-
</PopoverValue>
|
|
16
|
-
<hr v-if="footerContent.length" />
|
|
17
|
-
</div>
|
|
18
|
-
<div class="footer content">
|
|
19
|
-
<PopoverValue v-for="(v, idx) in footerContent" :key="idx" :item="v">
|
|
20
|
-
{{ v.type !== 'image' ? v.content : '' }}
|
|
21
|
-
</PopoverValue>
|
|
22
|
-
</div>
|
|
23
|
-
</div>
|
|
24
|
-
<div
|
|
25
|
-
class="theme-blog-popover-close"
|
|
26
|
-
v-show="!show && (popoverProps?.reopen ?? true) && popoverProps?.title"
|
|
27
|
-
@click="show = true"
|
|
28
|
-
>
|
|
29
|
-
<el-icon size="20px"><Flag /></el-icon>
|
|
30
|
-
</div>
|
|
31
|
-
</template>
|
|
32
|
-
|
|
33
1
|
<script lang="ts" setup>
|
|
34
|
-
import {
|
|
35
|
-
import {
|
|
36
|
-
import { computed, onMounted, ref
|
|
2
|
+
import { ElButton, ElIcon } from 'element-plus'
|
|
3
|
+
import { CircleCloseFilled, Flag } from '@element-plus/icons-vue'
|
|
4
|
+
import { computed, h, onMounted, ref } from 'vue'
|
|
37
5
|
import type { BlogPopover } from '@sugarat/theme'
|
|
38
6
|
import { parseStringStyle } from '@vue/shared'
|
|
39
7
|
import { useBlogConfig } from '../composables/config/blog'
|
|
@@ -77,10 +45,8 @@ onMounted(() => {
|
|
|
77
45
|
}
|
|
78
46
|
})
|
|
79
47
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
{ slots }: any
|
|
83
|
-
) => {
|
|
48
|
+
function PopoverValue(props: { key: number; item: BlogPopover.Value },
|
|
49
|
+
{ slots }: any) {
|
|
84
50
|
const { key, item } = props
|
|
85
51
|
if (item.type === 'title') {
|
|
86
52
|
return h(
|
|
@@ -130,6 +96,42 @@ const PopoverValue = (
|
|
|
130
96
|
}
|
|
131
97
|
</script>
|
|
132
98
|
|
|
99
|
+
<template>
|
|
100
|
+
<div v-show="show" class="theme-blog-popover" data-pagefind-ignore="all">
|
|
101
|
+
<div class="header">
|
|
102
|
+
<div class="title-wrapper">
|
|
103
|
+
<ElIcon size="20px">
|
|
104
|
+
<Flag />
|
|
105
|
+
</ElIcon>
|
|
106
|
+
<span class="title">{{ popoverProps?.title }}</span>
|
|
107
|
+
</div>
|
|
108
|
+
<ElIcon class="close-icon" size="20px" @click="show = false">
|
|
109
|
+
<CircleCloseFilled />
|
|
110
|
+
</ElIcon>
|
|
111
|
+
</div>
|
|
112
|
+
<div v-if="bodyContent.length" class="body content">
|
|
113
|
+
<PopoverValue v-for="(v, idx) in bodyContent" :key="idx" :item="v">
|
|
114
|
+
{{ v.type !== 'image' ? v.content : '' }}
|
|
115
|
+
</PopoverValue>
|
|
116
|
+
<hr v-if="footerContent.length">
|
|
117
|
+
</div>
|
|
118
|
+
<div class="footer content">
|
|
119
|
+
<PopoverValue v-for="(v, idx) in footerContent" :key="idx" :item="v">
|
|
120
|
+
{{ v.type !== 'image' ? v.content : '' }}
|
|
121
|
+
</PopoverValue>
|
|
122
|
+
</div>
|
|
123
|
+
</div>
|
|
124
|
+
<div
|
|
125
|
+
v-show="!show && (popoverProps?.reopen ?? true) && popoverProps?.title"
|
|
126
|
+
class="theme-blog-popover-close"
|
|
127
|
+
@click="show = true"
|
|
128
|
+
>
|
|
129
|
+
<ElIcon size="20px">
|
|
130
|
+
<Flag />
|
|
131
|
+
</ElIcon>
|
|
132
|
+
</div>
|
|
133
|
+
</template>
|
|
134
|
+
|
|
133
135
|
<style lang="scss" scoped>
|
|
134
136
|
.theme-blog-popover {
|
|
135
137
|
width: 258px;
|