@sugarat/theme 0.1.48 → 0.1.50
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 +2 -1
- package/node.js +27 -21
- package/package.json +23 -23
- package/src/components/BlogAlert.vue +17 -17
- package/src/components/BlogApp.vue +81 -49
- package/src/components/BlogArticleAnalyze.vue +56 -57
- package/src/components/BlogComment.vue +53 -50
- package/src/components/BlogDocCover.vue +4 -4
- package/src/components/BlogFriendLink.vue +40 -31
- package/src/components/BlogHomeBanner.vue +22 -16
- 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 +7 -5
- package/src/composables/config/index.ts +34 -31
- package/src/constants/svg.ts +2 -2
- package/src/index.ts +1 -2
- package/src/node.ts +2 -2
- 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 +21 -15
- package/src/utils/node/vitePlugins.ts +7 -6
- package/types/vue-shim.d.ts +1 -1
|
@@ -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;
|
|
@@ -1,36 +1,5 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<div class="recommend" :class="{ card: sidebarStyle === 'card' }"
|
|
3
|
-
v-if="_recommend !== false && (recommendList.length || emptyText)" data-pagefind-ignore="all">
|
|
4
|
-
<!-- 头部 -->
|
|
5
|
-
<div class="card-header">
|
|
6
|
-
<span class="title" v-if="title" v-html="title"></span>
|
|
7
|
-
<el-button v-if="showChangeBtn" size="small" type="primary" text @click="changePage">{{ nextText }}</el-button>
|
|
8
|
-
</div>
|
|
9
|
-
<!-- 文章列表 -->
|
|
10
|
-
<ol class="recommend-container" v-if="currentWikiData.length">
|
|
11
|
-
<li v-for="(v, idx) in currentWikiData" :key="v.route">
|
|
12
|
-
<!-- 序号 -->
|
|
13
|
-
<i class="num">{{ startIdx + idx + 1 }}</i>
|
|
14
|
-
<!-- 简介 -->
|
|
15
|
-
<div class="des">
|
|
16
|
-
<!-- title -->
|
|
17
|
-
<el-link type="info" class="title" :class="{
|
|
18
|
-
current: isCurrentDoc(v.route)
|
|
19
|
-
}" :href="v.route">{{ v.meta.title }}</el-link>
|
|
20
|
-
<!-- 描述信息 -->
|
|
21
|
-
<div class="suffix">
|
|
22
|
-
<!-- 日期 -->
|
|
23
|
-
<span class="tag">{{ formatShowDate(v.meta.date) }}</span>
|
|
24
|
-
</div>
|
|
25
|
-
</div>
|
|
26
|
-
</li>
|
|
27
|
-
</ol>
|
|
28
|
-
<div class="empty-text" v-else>{{ emptyText }}</div>
|
|
29
|
-
</div>
|
|
30
|
-
</template>
|
|
31
|
-
|
|
32
1
|
<script lang="ts" setup>
|
|
33
|
-
import {
|
|
2
|
+
import { computed, ref } from 'vue'
|
|
34
3
|
import { useRoute, withBase } from 'vitepress'
|
|
35
4
|
import { ElButton, ElLink } from 'element-plus'
|
|
36
5
|
import { formatShowDate } from '../utils/client'
|
|
@@ -63,43 +32,43 @@ const recommendList = computed(() => {
|
|
|
63
32
|
const paths = decodeURIComponent(route.path).split('/')
|
|
64
33
|
|
|
65
34
|
const origin = docs.value
|
|
66
|
-
.map(
|
|
35
|
+
.map(v => ({ ...v, route: withBase(v.route) }))
|
|
67
36
|
// 过滤出公共路由前缀
|
|
68
37
|
// 限制为同路由前缀
|
|
69
38
|
.filter(
|
|
70
|
-
|
|
71
|
-
v.route.split('/').length === paths.length
|
|
72
|
-
v.route.startsWith(paths.slice(0, paths.length - 1).join('/'))
|
|
39
|
+
v =>
|
|
40
|
+
v.route.split('/').length === paths.length
|
|
41
|
+
&& v.route.startsWith(paths.slice(0, paths.length - 1).join('/'))
|
|
73
42
|
)
|
|
74
43
|
// 过滤出带标题的
|
|
75
|
-
.filter(
|
|
44
|
+
.filter(v => !!v.meta.title)
|
|
76
45
|
// 过滤掉自己
|
|
77
46
|
.filter(
|
|
78
|
-
|
|
79
|
-
(recommend.value?.showSelf ?? true)
|
|
80
|
-
v.route !== decodeURIComponent(route.path).replace(/.html$/, '')
|
|
47
|
+
v =>
|
|
48
|
+
(recommend.value?.showSelf ?? true)
|
|
49
|
+
|| v.route !== decodeURIComponent(route.path).replace(/.html$/, '')
|
|
81
50
|
)
|
|
82
51
|
// 过滤掉不需要展示的
|
|
83
|
-
.filter(
|
|
84
|
-
.filter(
|
|
52
|
+
.filter(v => v.meta.recommend !== false)
|
|
53
|
+
.filter(v => recommend.value?.filter?.(v) ?? true)
|
|
85
54
|
|
|
86
|
-
const topList = origin.filter(
|
|
55
|
+
const topList = origin.filter(v => v.meta?.recommend)
|
|
87
56
|
topList.sort((a, b) => Number(a.meta.recommend) - Number(b.meta.recommend))
|
|
88
57
|
|
|
89
|
-
const normalList = origin.filter(
|
|
58
|
+
const normalList = origin.filter(v => !v.meta?.recommend)
|
|
90
59
|
normalList.sort((a, b) => +new Date(b.meta.date) - +new Date(a.meta.date))
|
|
91
60
|
|
|
92
61
|
return topList.concat(normalList)
|
|
93
62
|
})
|
|
94
63
|
|
|
95
|
-
|
|
64
|
+
function isCurrentDoc(value: string) {
|
|
96
65
|
return value === decodeURIComponent(route.path).replace(/.html$/, '')
|
|
97
66
|
}
|
|
98
67
|
|
|
99
68
|
const currentPage = ref(1)
|
|
100
|
-
|
|
101
|
-
const newIdx
|
|
102
|
-
currentPage.value % Math.ceil(recommendList.value.length / pageSize.value)
|
|
69
|
+
function changePage() {
|
|
70
|
+
const newIdx
|
|
71
|
+
= currentPage.value % Math.ceil(recommendList.value.length / pageSize.value)
|
|
103
72
|
currentPage.value = newIdx + 1
|
|
104
73
|
}
|
|
105
74
|
// 当前页开始的序号
|
|
@@ -116,6 +85,47 @@ const showChangeBtn = computed(() => {
|
|
|
116
85
|
})
|
|
117
86
|
</script>
|
|
118
87
|
|
|
88
|
+
<template>
|
|
89
|
+
<div
|
|
90
|
+
v-if="_recommend !== false && (recommendList.length || emptyText)" class="recommend"
|
|
91
|
+
:class="{ card: sidebarStyle === 'card' }" data-pagefind-ignore="all"
|
|
92
|
+
>
|
|
93
|
+
<!-- 头部 -->
|
|
94
|
+
<div class="card-header">
|
|
95
|
+
<span v-if="title" class="title" v-html="title" />
|
|
96
|
+
<ElButton v-if="showChangeBtn" size="small" type="primary" text @click="changePage">
|
|
97
|
+
{{ nextText }}
|
|
98
|
+
</ElButton>
|
|
99
|
+
</div>
|
|
100
|
+
<!-- 文章列表 -->
|
|
101
|
+
<ol v-if="currentWikiData.length" class="recommend-container">
|
|
102
|
+
<li v-for="(v, idx) in currentWikiData" :key="v.route">
|
|
103
|
+
<!-- 序号 -->
|
|
104
|
+
<i class="num">{{ startIdx + idx + 1 }}</i>
|
|
105
|
+
<!-- 简介 -->
|
|
106
|
+
<div class="des">
|
|
107
|
+
<!-- title -->
|
|
108
|
+
<ElLink
|
|
109
|
+
type="info" class="title" :class="{
|
|
110
|
+
current: isCurrentDoc(v.route),
|
|
111
|
+
}" :href="v.route"
|
|
112
|
+
>
|
|
113
|
+
{{ v.meta.title }}
|
|
114
|
+
</ElLink>
|
|
115
|
+
<!-- 描述信息 -->
|
|
116
|
+
<div class="suffix">
|
|
117
|
+
<!-- 日期 -->
|
|
118
|
+
<span class="tag">{{ formatShowDate(v.meta.date) }}</span>
|
|
119
|
+
</div>
|
|
120
|
+
</div>
|
|
121
|
+
</li>
|
|
122
|
+
</ol>
|
|
123
|
+
<div v-else class="empty-text">
|
|
124
|
+
{{ emptyText }}
|
|
125
|
+
</div>
|
|
126
|
+
</div>
|
|
127
|
+
</template>
|
|
128
|
+
|
|
119
129
|
<style lang="scss" scoped>
|
|
120
130
|
.card {
|
|
121
131
|
position: relative;
|