@sugarat/theme 0.2.29 → 0.3.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 +36 -4
- package/node.js +12 -1
- package/package.json +5 -4
- package/src/components/BlogApp.vue +11 -4
- package/src/components/BlogButtonAfterArticle.vue +122 -0
- package/src/components/{BlogComment.vue → BlogCommentWrapper.vue} +14 -54
- package/src/components/BlogHomeTags.vue +5 -4
- package/src/components/BlogHotArticle.vue +11 -6
- package/src/components/BlogItem.vue +39 -19
- package/src/components/CommentArtalk.vue +74 -0
- package/src/components/CommentGiscus.vue +49 -0
- package/src/composables/config/blog.ts +0 -5
- package/src/composables/config/index.ts +37 -4
- package/src/constants/svg.ts +23 -0
- package/src/node.ts +3 -1
- package/src/utils/node/theme.ts +9 -1
- package/types/vue-shim.d.ts +10 -0
- package/src/components/BlogSearch.vue +0 -419
- package/src/components/LogoPagefind.vue +0 -55
package/node.d.ts
CHANGED
|
@@ -93,7 +93,8 @@ declare namespace Theme {
|
|
|
93
93
|
label: string;
|
|
94
94
|
type: string;
|
|
95
95
|
}
|
|
96
|
-
|
|
96
|
+
type CommentConfig = ((GiscusOption & CommentCommonConfig) | GiscusConfig | ArtalkConfig);
|
|
97
|
+
interface CommentCommonConfig {
|
|
97
98
|
/**
|
|
98
99
|
* @default '评论'
|
|
99
100
|
*/
|
|
@@ -109,7 +110,15 @@ declare namespace Theme {
|
|
|
109
110
|
*/
|
|
110
111
|
mobileMinify?: boolean;
|
|
111
112
|
}
|
|
112
|
-
interface GiscusConfig {
|
|
113
|
+
interface GiscusConfig extends CommentCommonConfig {
|
|
114
|
+
type: 'giscus';
|
|
115
|
+
options: GiscusOption;
|
|
116
|
+
}
|
|
117
|
+
interface ArtalkConfig extends CommentCommonConfig {
|
|
118
|
+
type: 'artalk';
|
|
119
|
+
options: ArtalkOption;
|
|
120
|
+
}
|
|
121
|
+
interface GiscusOption {
|
|
113
122
|
repo: Repo;
|
|
114
123
|
repoId: string;
|
|
115
124
|
category: string;
|
|
@@ -119,6 +128,10 @@ declare namespace Theme {
|
|
|
119
128
|
lang?: string;
|
|
120
129
|
loading?: 'lazy' | 'eager';
|
|
121
130
|
}
|
|
131
|
+
interface ArtalkOption {
|
|
132
|
+
site: string;
|
|
133
|
+
server: string;
|
|
134
|
+
}
|
|
122
135
|
interface HotArticle {
|
|
123
136
|
title?: string;
|
|
124
137
|
pageSize?: number;
|
|
@@ -308,7 +321,7 @@ declare namespace Theme {
|
|
|
308
321
|
pagesData: PageData[];
|
|
309
322
|
srcDir?: string;
|
|
310
323
|
author?: string;
|
|
311
|
-
hotArticle?: HotArticle;
|
|
324
|
+
hotArticle?: HotArticle | false;
|
|
312
325
|
home?: HomeBlog;
|
|
313
326
|
/**
|
|
314
327
|
* 本地全文搜索定制
|
|
@@ -319,7 +332,8 @@ declare namespace Theme {
|
|
|
319
332
|
search?: SearchConfig;
|
|
320
333
|
/**
|
|
321
334
|
* 配置评论
|
|
322
|
-
*
|
|
335
|
+
* giscus: https://giscus.app/zh-CN
|
|
336
|
+
* artalk: https://artalk.js.org/
|
|
323
337
|
*/
|
|
324
338
|
comment?: CommentConfig | false;
|
|
325
339
|
/**
|
|
@@ -390,6 +404,8 @@ declare namespace Theme {
|
|
|
390
404
|
* 详见 https://oml2d.com/options/Options.html
|
|
391
405
|
*/
|
|
392
406
|
oml2d?: Options;
|
|
407
|
+
homeTags?: boolean;
|
|
408
|
+
buttonAfterArticle?: ButtonAfterArticleConfig | false;
|
|
393
409
|
}
|
|
394
410
|
interface BackToTop {
|
|
395
411
|
/**
|
|
@@ -469,6 +485,22 @@ declare namespace Theme {
|
|
|
469
485
|
*/
|
|
470
486
|
handleChangeSlogan?: (oldSlogan: string) => string | Promise<string>;
|
|
471
487
|
}
|
|
488
|
+
interface ButtonAfterArticleConfig {
|
|
489
|
+
openTitle?: string;
|
|
490
|
+
closeTitle?: string;
|
|
491
|
+
content?: string;
|
|
492
|
+
icon?: 'aliPay' | 'wechatPay' | string;
|
|
493
|
+
/**
|
|
494
|
+
* 按钮尺寸
|
|
495
|
+
* @default 'default'
|
|
496
|
+
*/
|
|
497
|
+
size?: 'small' | 'default' | 'large';
|
|
498
|
+
/**
|
|
499
|
+
* 默认展开
|
|
500
|
+
* @default false
|
|
501
|
+
*/
|
|
502
|
+
expand?: boolean;
|
|
503
|
+
}
|
|
472
504
|
}
|
|
473
505
|
|
|
474
506
|
/**
|
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.0.
|
|
43
|
+
// ../../node_modules/.pnpm/vitepress-plugin-tabs@0.2.0_vitepress@1.0.2_vue@3.4.21/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) => {
|
|
@@ -453,6 +453,16 @@ function getArticles(cfg) {
|
|
|
453
453
|
}).filter((v) => v.meta.layout !== "home");
|
|
454
454
|
return data;
|
|
455
455
|
}
|
|
456
|
+
function patchVPConfig(vpConfig, cfg) {
|
|
457
|
+
vpConfig.head = vpConfig.head || [];
|
|
458
|
+
if (cfg?.comment && "type" in cfg.comment && cfg?.comment?.type === "artalk") {
|
|
459
|
+
const server = cfg.comment?.options?.server;
|
|
460
|
+
if (server) {
|
|
461
|
+
vpConfig.head.push(["link", { href: `${server}/dist/Artalk.css`, rel: "stylesheet" }]);
|
|
462
|
+
vpConfig.head.push(["script", { src: `${server}/dist/Artalk.js`, id: "artalk-script" }]);
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
}
|
|
456
466
|
function patchVPThemeConfig(cfg, vpThemeConfig = {}) {
|
|
457
467
|
vpThemeConfig.sidebar = patchDefaultThemeSideBar(cfg)?.sidebar;
|
|
458
468
|
return vpThemeConfig;
|
|
@@ -574,6 +584,7 @@ function getThemeConfig(cfg) {
|
|
|
574
584
|
registerMdPlugins(extraVPConfig, markdownPlugin);
|
|
575
585
|
patchMermaidPluginCfg(extraVPConfig);
|
|
576
586
|
patchOptimizeDeps(extraVPConfig);
|
|
587
|
+
patchVPConfig(extraVPConfig, cfg);
|
|
577
588
|
return {
|
|
578
589
|
themeConfig: {
|
|
579
590
|
blog: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sugarat/theme",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "简约风的 Vitepress 博客主题,sugarat vitepress blog theme",
|
|
5
5
|
"author": "sugar",
|
|
6
6
|
"license": "MIT",
|
|
@@ -42,22 +42,23 @@
|
|
|
42
42
|
"gray-matter": "^4.0.3",
|
|
43
43
|
"markdown-it-task-checkbox": "^1.0.6",
|
|
44
44
|
"mermaid": "^10.2.4",
|
|
45
|
-
"oh-my-live2d": "^0.
|
|
45
|
+
"oh-my-live2d": "^0.15.2",
|
|
46
46
|
"swiper": "^11.0.5",
|
|
47
47
|
"vitepress-markdown-timeline": "^1.2.1",
|
|
48
48
|
"vitepress-plugin-mermaid": "2.0.13",
|
|
49
|
-
"vitepress-plugin-pagefind": "0.2.
|
|
49
|
+
"vitepress-plugin-pagefind": "0.2.14",
|
|
50
50
|
"vitepress-plugin-rss": "0.2.2",
|
|
51
51
|
"vitepress-plugin-tabs": "0.2.0"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
54
|
"@element-plus/icons-vue": "^2.1.0",
|
|
55
|
+
"artalk": "^2.8.3",
|
|
55
56
|
"element-plus": "^2.3.4",
|
|
56
57
|
"javascript-stringify": "^2.1.0",
|
|
57
58
|
"pagefind": "1.0.3",
|
|
58
59
|
"sass": "^1.56.1",
|
|
59
60
|
"typescript": "^4.8.2",
|
|
60
|
-
"vitepress": "1.0.
|
|
61
|
+
"vitepress": "1.0.2",
|
|
61
62
|
"vue": "^3.4.21"
|
|
62
63
|
},
|
|
63
64
|
"scripts": {
|
|
@@ -7,9 +7,6 @@ import { useBlogThemeMode } from '../composables/config/blog'
|
|
|
7
7
|
import BlogHomeInfo from './BlogHomeInfo.vue'
|
|
8
8
|
import BlogHomeBanner from './BlogHomeBanner.vue'
|
|
9
9
|
import BlogList from './BlogList.vue'
|
|
10
|
-
import BlogComment from './BlogComment.vue'
|
|
11
|
-
|
|
12
|
-
// import BlogSearch from './BlogSearch.vue'
|
|
13
10
|
import BlogSidebar from './BlogSidebar.vue'
|
|
14
11
|
import BlogImagePreview from './BlogImagePreview.vue'
|
|
15
12
|
import BlogArticleAnalyze from './BlogArticleAnalyze.vue'
|
|
@@ -18,6 +15,11 @@ import BlogPopover from './BlogPopover.vue'
|
|
|
18
15
|
import BlogFooter from './BlogFooter.vue'
|
|
19
16
|
import BlogHomeHeaderAvatar from './BlogHomeHeaderAvatar.vue'
|
|
20
17
|
import BlogBackToTop from './BlogBackToTop.vue'
|
|
18
|
+
import CommentGiscus from './CommentGiscus.vue'
|
|
19
|
+
|
|
20
|
+
import CommentArtalk from './CommentArtalk.vue'
|
|
21
|
+
import BlogButtonAfterArticle from './BlogButtonAfterArticle.vue'
|
|
22
|
+
import BlogCommentWrapper from './BlogCommentWrapper.vue'
|
|
21
23
|
|
|
22
24
|
const { frontmatter } = useData()
|
|
23
25
|
const layout = computed(() => frontmatter.value.layout)
|
|
@@ -75,8 +77,12 @@ useOml2d()
|
|
|
75
77
|
<slot name="doc-after" />
|
|
76
78
|
<!-- 评论 -->
|
|
77
79
|
<ClientOnly>
|
|
80
|
+
<BlogButtonAfterArticle />
|
|
78
81
|
<BlogBackToTop />
|
|
79
|
-
<
|
|
82
|
+
<BlogCommentWrapper>
|
|
83
|
+
<CommentArtalk />
|
|
84
|
+
<CommentGiscus />
|
|
85
|
+
</BlogCommentWrapper>
|
|
80
86
|
</ClientOnly>
|
|
81
87
|
</template>
|
|
82
88
|
<template #layout-bottom>
|
|
@@ -192,6 +198,7 @@ useOml2d()
|
|
|
192
198
|
.blog-list-wrapper {
|
|
193
199
|
width: 100%;
|
|
194
200
|
}
|
|
201
|
+
|
|
195
202
|
.blog-info-wrapper {
|
|
196
203
|
margin-left: 16px;
|
|
197
204
|
position: sticky;
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
import { ElButton } from 'element-plus'
|
|
3
|
+
import { computed, ref, watch } from 'vue'
|
|
4
|
+
import { useData } from 'vitepress'
|
|
5
|
+
import { useBlogConfig } from '../composables/config/blog'
|
|
6
|
+
import { aliPaySVG, weChatPaySVG } from '../constants/svg'
|
|
7
|
+
|
|
8
|
+
const { buttonAfterArticle: _buttonAfterArticle } = useBlogConfig()
|
|
9
|
+
const { frontmatter } = useData()
|
|
10
|
+
const frontmatterConfig = computed(() => frontmatter.value.buttonAfterArticle)
|
|
11
|
+
|
|
12
|
+
const buttonAfterArticleConfig = computed(() => {
|
|
13
|
+
if (frontmatterConfig.value === false || (!frontmatterConfig.value && !_buttonAfterArticle)) {
|
|
14
|
+
return false
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return { ..._buttonAfterArticle, ...frontmatterConfig.value }
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
const showContent = ref(false)
|
|
21
|
+
|
|
22
|
+
watch(buttonAfterArticleConfig, () => {
|
|
23
|
+
showContent.value = !!buttonAfterArticleConfig.value?.expand
|
|
24
|
+
}, {
|
|
25
|
+
immediate: true
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
const svg = computed(() => {
|
|
29
|
+
const icon = buttonAfterArticleConfig.value?.icon
|
|
30
|
+
if (icon === 'aliPay') {
|
|
31
|
+
return aliPaySVG
|
|
32
|
+
}
|
|
33
|
+
else if (icon === 'wechatPay') {
|
|
34
|
+
return weChatPaySVG
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
return icon as string
|
|
38
|
+
}
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
function toggleContent() {
|
|
42
|
+
showContent.value = !showContent.value
|
|
43
|
+
}
|
|
44
|
+
</script>
|
|
45
|
+
|
|
46
|
+
<template>
|
|
47
|
+
<div v-if="buttonAfterArticleConfig" class="appreciation-container">
|
|
48
|
+
<ElButton :size="buttonAfterArticleConfig.size || 'default'" class="content-button" :type="showContent ? 'danger' : 'primary'" @click="toggleContent">
|
|
49
|
+
<span class="content-icon" v-html="svg" />
|
|
50
|
+
{{ showContent ? buttonAfterArticleConfig.closeTitle : buttonAfterArticleConfig.openTitle }}
|
|
51
|
+
</ElButton>
|
|
52
|
+
<transition name="content">
|
|
53
|
+
<div v-if="showContent" class="content-container" v-html="buttonAfterArticleConfig.content" />
|
|
54
|
+
</transition>
|
|
55
|
+
</div>
|
|
56
|
+
</template>
|
|
57
|
+
|
|
58
|
+
<style scoped lang="scss">
|
|
59
|
+
.appreciation-container {
|
|
60
|
+
text-align: center;
|
|
61
|
+
padding: 20px;
|
|
62
|
+
font-size: 14px;
|
|
63
|
+
color: #606266;
|
|
64
|
+
|
|
65
|
+
:deep(.el-button.el-button--primary){
|
|
66
|
+
background-color: var(--vp-c-brand-2);
|
|
67
|
+
border-color: var(--vp-c-brand-2);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.content-container {
|
|
72
|
+
position: relative;
|
|
73
|
+
display: flex;
|
|
74
|
+
justify-content: center;
|
|
75
|
+
align-items: center;
|
|
76
|
+
margin-top: 20px;
|
|
77
|
+
:deep(img){
|
|
78
|
+
height: 260px;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.content-icon {
|
|
83
|
+
font-family: "iconfont" !important;
|
|
84
|
+
font-size: 16px;
|
|
85
|
+
margin-right: 8px;
|
|
86
|
+
font-style: normal;
|
|
87
|
+
-webkit-font-smoothing: antialiased;
|
|
88
|
+
-moz-osx-font-smoothing: grayscale;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/* 进入动画 */
|
|
92
|
+
.content-enter-active {
|
|
93
|
+
animation: fadeIn 0.5s ease forwards;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/* 离开动画 */
|
|
97
|
+
.content-leave-active {
|
|
98
|
+
animation: fadeOut 0.3s ease forwards;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/* 淡入 */
|
|
102
|
+
@keyframes fadeIn {
|
|
103
|
+
from {
|
|
104
|
+
opacity: 0;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
to {
|
|
108
|
+
opacity: 1;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/* 淡出 */
|
|
113
|
+
@keyframes fadeOut {
|
|
114
|
+
from {
|
|
115
|
+
opacity: 1;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
to {
|
|
119
|
+
opacity: 0;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
</style>
|
|
@@ -1,66 +1,33 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { useElementSize, useElementVisibility, useWindowSize } from '@vueuse/core'
|
|
3
|
-
import { useData
|
|
4
|
-
import { computed, h, ref
|
|
3
|
+
import { useData } from 'vitepress'
|
|
4
|
+
import { computed, h, ref } from 'vue'
|
|
5
5
|
import { ElIcon } from 'element-plus'
|
|
6
6
|
import { Comment } from '@element-plus/icons-vue'
|
|
7
|
-
import
|
|
8
|
-
import { useGiscusConfig } from '../composables/config/blog'
|
|
9
|
-
import type { Theme } from '../composables/config/index'
|
|
7
|
+
import { useBlogConfig } from '../composables/config/blog'
|
|
10
8
|
|
|
11
9
|
const { frontmatter } = useData()
|
|
12
10
|
const commentEl = ref(null)
|
|
13
11
|
const commentIsVisible = useElementVisibility(commentEl)
|
|
14
12
|
|
|
15
13
|
function handleScrollToComment() {
|
|
16
|
-
document.querySelector('#
|
|
14
|
+
document.querySelector('#blog-comment-wrapper')?.scrollIntoView({
|
|
17
15
|
behavior: 'smooth',
|
|
18
16
|
block: 'start'
|
|
19
17
|
})
|
|
20
18
|
}
|
|
21
|
-
const giscusConfig = useGiscusConfig()
|
|
22
19
|
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
return giscusConfig
|
|
28
|
-
})
|
|
20
|
+
const { comment: _comment } = useBlogConfig()
|
|
21
|
+
const commentConfig = computed(() =>
|
|
22
|
+
_comment === false ? undefined : _comment
|
|
23
|
+
)
|
|
29
24
|
|
|
30
25
|
const show = computed(() => {
|
|
31
|
-
|
|
32
|
-
return frontmatter.value.comment
|
|
33
|
-
}
|
|
34
|
-
if (!giscusConfig) {
|
|
35
|
-
return giscusConfig
|
|
36
|
-
}
|
|
37
|
-
return (
|
|
38
|
-
giscusConfig.repo
|
|
39
|
-
&& giscusConfig.repoId
|
|
40
|
-
&& giscusConfig.category
|
|
41
|
-
&& giscusConfig.categoryId
|
|
42
|
-
)
|
|
26
|
+
return _comment && frontmatter.value.comment !== false
|
|
43
27
|
})
|
|
44
28
|
|
|
45
|
-
const { isDark } = useData()
|
|
46
|
-
|
|
47
|
-
const route = useRoute()
|
|
48
|
-
const showComment = ref(true)
|
|
49
|
-
watch(
|
|
50
|
-
() => route.path,
|
|
51
|
-
() => {
|
|
52
|
-
showComment.value = false
|
|
53
|
-
setTimeout(() => {
|
|
54
|
-
showComment.value = true
|
|
55
|
-
}, 200)
|
|
56
|
-
},
|
|
57
|
-
{
|
|
58
|
-
immediate: true
|
|
59
|
-
}
|
|
60
|
-
)
|
|
61
|
-
|
|
62
29
|
const { width } = useWindowSize()
|
|
63
|
-
const mobileMinify = computed(() => width.value < 768 && (commentConfig.value
|
|
30
|
+
const mobileMinify = computed(() => width.value < 768 && (commentConfig.value?.mobileMinify ?? true))
|
|
64
31
|
|
|
65
32
|
const CommentIcon = commentConfig.value?.icon
|
|
66
33
|
? h('i', {
|
|
@@ -78,20 +45,13 @@ const { width: _docWidth } = useElementSize(el)
|
|
|
78
45
|
const docWidth = computed(() => `${_docWidth.value}px`)
|
|
79
46
|
|
|
80
47
|
const labelText = computed(() => {
|
|
81
|
-
return commentConfig.value
|
|
48
|
+
return commentConfig.value?.label ?? '评论'
|
|
82
49
|
})
|
|
83
50
|
</script>
|
|
84
51
|
|
|
85
52
|
<template>
|
|
86
|
-
<div v-if="show && _docWidth" id="
|
|
87
|
-
<
|
|
88
|
-
v-if="showComment" :repo="commentConfig.repo" :repo-id="commentConfig.repoId"
|
|
89
|
-
:category="commentConfig.category" :category-id="commentConfig.categoryId"
|
|
90
|
-
:mapping="commentConfig.mapping || 'pathname'" reactions-enabled="1" emit-metadata="0"
|
|
91
|
-
:input-position="commentConfig.inputPosition || 'top'" :theme="isDark ? 'dark' : 'light'"
|
|
92
|
-
:lang="commentConfig.lang || 'zh-CN'" :loading="commentConfig.loading || 'eager'"
|
|
93
|
-
/>
|
|
94
|
-
|
|
53
|
+
<div v-if="show && _docWidth" id="blog-comment-wrapper" ref="commentEl" class="blog-comment-wrapper" data-pagefind-ignore="all">
|
|
54
|
+
<slot />
|
|
95
55
|
<div v-show="!commentIsVisible" class="comment-btn-wrapper">
|
|
96
56
|
<span v-if="!mobileMinify && labelText" class="icon-wrapper-text" @click="handleScrollToComment">
|
|
97
57
|
<ElIcon :size="20">
|
|
@@ -151,7 +111,7 @@ const labelText = computed(() => {
|
|
|
151
111
|
border-radius: 2px;
|
|
152
112
|
padding: 2px 6px;
|
|
153
113
|
|
|
154
|
-
span.text{
|
|
114
|
+
span.text {
|
|
155
115
|
font-size: 12px;
|
|
156
116
|
margin-left: 4px;
|
|
157
117
|
}
|
|
@@ -2,16 +2,17 @@
|
|
|
2
2
|
import { computed, watch } from 'vue'
|
|
3
3
|
import { ElTag } from 'element-plus'
|
|
4
4
|
import { useBrowserLocation, useDark, useUrlSearchParams } from '@vueuse/core'
|
|
5
|
-
import {
|
|
5
|
+
import { useRoute, useRouter } from 'vitepress'
|
|
6
6
|
import {
|
|
7
7
|
useActiveTag,
|
|
8
8
|
useArticles,
|
|
9
|
-
|
|
9
|
+
useConfig,
|
|
10
|
+
useCurrentPageNum,
|
|
10
11
|
} from '../composables/config/blog'
|
|
11
12
|
|
|
12
13
|
const route = useRoute()
|
|
13
14
|
const docs = useArticles()
|
|
14
|
-
|
|
15
|
+
const showTags = useConfig()?.config?.blog?.homeTags ?? true
|
|
15
16
|
const tags = computed(() => {
|
|
16
17
|
return [...new Set(docs.value.map(v => v.meta.tag || []).flat(3))]
|
|
17
18
|
})
|
|
@@ -77,7 +78,7 @@ watch(
|
|
|
77
78
|
</script>
|
|
78
79
|
|
|
79
80
|
<template>
|
|
80
|
-
<div v-if="tags.length" class="card tags" data-pagefind-ignore="all">
|
|
81
|
+
<div v-if="showTags && tags.length" class="card tags" data-pagefind-ignore="all">
|
|
81
82
|
<!-- 头部 -->
|
|
82
83
|
<div class="card-header">
|
|
83
84
|
<span class="title svg-icon"><svg
|
|
@@ -6,11 +6,16 @@ import { useArticles, useBlogConfig, useCleanUrls } from '../composables/config/
|
|
|
6
6
|
import { formatShowDate, wrapperCleanUrls } from '../utils/client'
|
|
7
7
|
import { fireSVG } from '../constants/svg'
|
|
8
8
|
|
|
9
|
-
const { hotArticle } = useBlogConfig()
|
|
10
|
-
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
9
|
+
const { hotArticle: _hotArticle } = useBlogConfig()
|
|
10
|
+
|
|
11
|
+
const hotArticle = computed(() =>
|
|
12
|
+
_hotArticle === false ? undefined : _hotArticle
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
const title = computed(() => hotArticle.value?.title || (`<span class="svg-icon">${fireSVG}</span>` + ' 精选文章'))
|
|
16
|
+
const nextText = computed(() => hotArticle.value?.nextText || '换一组')
|
|
17
|
+
const pageSize = computed(() => hotArticle.value?.pageSize || 9)
|
|
18
|
+
const empty = computed(() => hotArticle.value?.empty ?? '暂无精选内容')
|
|
14
19
|
|
|
15
20
|
const docs = useArticles()
|
|
16
21
|
|
|
@@ -43,7 +48,7 @@ const showChangeBtn = computed(() => {
|
|
|
43
48
|
</script>
|
|
44
49
|
|
|
45
50
|
<template>
|
|
46
|
-
<div v-if="recommendList.length || empty" class="card recommend" data-pagefind-ignore="all">
|
|
51
|
+
<div v-if="_hotArticle !== false && (recommendList.length || empty) " class="card recommend" data-pagefind-ignore="all">
|
|
47
52
|
<!-- 头部 -->
|
|
48
53
|
<div class="card-header">
|
|
49
54
|
<span class="title" v-html="title" />
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
<script lang="ts" setup>
|
|
2
2
|
import { withBase } from 'vitepress'
|
|
3
3
|
import { computed } from 'vue'
|
|
4
|
-
import { useWindowSize } from '@vueuse/core'
|
|
5
4
|
import { formatShowDate, wrapperCleanUrls } from '../utils/client'
|
|
6
5
|
import { useCleanUrls } from '../composables/config/blog'
|
|
7
6
|
|
|
@@ -17,8 +16,6 @@ const props = defineProps<{
|
|
|
17
16
|
cover?: string | boolean
|
|
18
17
|
pin?: number
|
|
19
18
|
}>()
|
|
20
|
-
const { width } = useWindowSize()
|
|
21
|
-
const inMobile = computed(() => width.value <= 500)
|
|
22
19
|
const showTime = computed(() => {
|
|
23
20
|
return formatShowDate(props.date)
|
|
24
21
|
})
|
|
@@ -28,15 +25,16 @@ const link = computed(() => withBase(wrapperCleanUrls(!!cleanUrls, props.route))
|
|
|
28
25
|
</script>
|
|
29
26
|
|
|
30
27
|
<template>
|
|
28
|
+
<!-- TODO: 响应式优化使用纯 CSS -->
|
|
31
29
|
<a class="blog-item" :href="link">
|
|
32
30
|
<i v-if="!!pin" class="pin" />
|
|
33
31
|
<!-- 标题 -->
|
|
34
|
-
<p
|
|
32
|
+
<p class="title mobile-visible">{{ title }}</p>
|
|
35
33
|
<div class="info-container">
|
|
36
34
|
<!-- 左侧信息 -->
|
|
37
35
|
<div class="info-part">
|
|
38
36
|
<!-- 标题 -->
|
|
39
|
-
<p
|
|
37
|
+
<p class="title pc-visible">{{ title }}</p>
|
|
40
38
|
<!-- 简短描述 -->
|
|
41
39
|
<p v-if="!descriptionHTML && !!description" class="description">
|
|
42
40
|
{{ description }}
|
|
@@ -45,21 +43,17 @@ const link = computed(() => withBase(wrapperCleanUrls(!!cleanUrls, props.route))
|
|
|
45
43
|
<div class="description-html" v-html="descriptionHTML" />
|
|
46
44
|
</template>
|
|
47
45
|
<!-- 底部补充描述 -->
|
|
48
|
-
<div
|
|
46
|
+
<div class="badge-list pc-visible">
|
|
49
47
|
<span v-if="author" class="split">{{ author }}</span>
|
|
50
48
|
<span class="split">{{ showTime }}</span>
|
|
51
49
|
<span v-if="tag?.length" class="split">{{ tag.join(' · ') }}</span>
|
|
52
50
|
</div>
|
|
53
51
|
</div>
|
|
54
52
|
<!-- 右侧封面图 -->
|
|
55
|
-
<div
|
|
56
|
-
v-if="cover"
|
|
57
|
-
class="cover-img"
|
|
58
|
-
:style="`background-image: url(${withBase(`${cover}`)});`"
|
|
59
|
-
/>
|
|
53
|
+
<div v-if="cover" class="cover-img" :style="`background-image: url(${withBase(`${cover}`)});`" />
|
|
60
54
|
</div>
|
|
61
55
|
<!-- 底部补充描述 -->
|
|
62
|
-
<div
|
|
56
|
+
<div class="badge-list mobile-visible">
|
|
63
57
|
<span v-if="author" class="split">{{ author }}</span>
|
|
64
58
|
<span class="split">{{ showTime }}</span>
|
|
65
59
|
<span v-if="tag?.length" class="split">{{ tag.join(' · ') }}</span>
|
|
@@ -77,19 +71,19 @@ const link = computed(() => withBase(wrapperCleanUrls(!!cleanUrls, props.route))
|
|
|
77
71
|
left: -4px;
|
|
78
72
|
opacity: 0.5;
|
|
79
73
|
}
|
|
74
|
+
|
|
80
75
|
.blog-item:hover .pin {
|
|
81
76
|
opacity: 1;
|
|
82
77
|
}
|
|
78
|
+
|
|
83
79
|
.blog-item .pin::before {
|
|
84
80
|
content: '';
|
|
85
81
|
position: absolute;
|
|
86
82
|
width: 120%;
|
|
87
83
|
height: 30px;
|
|
88
|
-
background-image: linear-gradient(
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
var(--blog-theme-color)
|
|
92
|
-
);
|
|
84
|
+
background-image: linear-gradient(45deg,
|
|
85
|
+
var(--blog-theme-color),
|
|
86
|
+
var(--blog-theme-color));
|
|
93
87
|
transform: rotate(-45deg) translateY(-20px);
|
|
94
88
|
display: flex;
|
|
95
89
|
align-items: center;
|
|
@@ -111,10 +105,12 @@ const link = computed(() => withBase(wrapperCleanUrls(!!cleanUrls, props.route))
|
|
|
111
105
|
cursor: pointer;
|
|
112
106
|
display: flex;
|
|
113
107
|
flex-direction: column;
|
|
108
|
+
|
|
114
109
|
&:hover {
|
|
115
110
|
box-shadow: var(--box-shadow-hover);
|
|
116
111
|
}
|
|
117
112
|
}
|
|
113
|
+
|
|
118
114
|
.info-container {
|
|
119
115
|
display: flex;
|
|
120
116
|
align-items: center;
|
|
@@ -124,11 +120,13 @@ const link = computed(() => withBase(wrapperCleanUrls(!!cleanUrls, props.route))
|
|
|
124
120
|
.info-part {
|
|
125
121
|
flex: 1;
|
|
126
122
|
}
|
|
123
|
+
|
|
127
124
|
.title {
|
|
128
125
|
font-size: 18px;
|
|
129
126
|
font-weight: 600;
|
|
130
127
|
margin-bottom: 8px;
|
|
131
128
|
}
|
|
129
|
+
|
|
132
130
|
.description {
|
|
133
131
|
color: var(--description-font-color);
|
|
134
132
|
font-size: 14px;
|
|
@@ -140,13 +138,16 @@ const link = computed(() => withBase(wrapperCleanUrls(!!cleanUrls, props.route))
|
|
|
140
138
|
-webkit-line-clamp: 2;
|
|
141
139
|
-webkit-box-orient: vertical;
|
|
142
140
|
}
|
|
141
|
+
|
|
143
142
|
.description-html {
|
|
144
143
|
font-size: 14px;
|
|
145
144
|
}
|
|
145
|
+
|
|
146
146
|
.badge-list {
|
|
147
147
|
font-size: 13px;
|
|
148
148
|
color: var(--badge-font-color);
|
|
149
149
|
margin-top: 8px;
|
|
150
|
+
|
|
150
151
|
.split:not(:last-child) {
|
|
151
152
|
&::after {
|
|
152
153
|
content: '';
|
|
@@ -158,20 +159,39 @@ const link = computed(() => withBase(wrapperCleanUrls(!!cleanUrls, props.route))
|
|
|
158
159
|
}
|
|
159
160
|
}
|
|
160
161
|
}
|
|
162
|
+
|
|
161
163
|
.cover-img {
|
|
162
164
|
width: 120px;
|
|
163
165
|
height: 80px;
|
|
164
166
|
margin-left: 24px;
|
|
165
167
|
border-radius: 2px;
|
|
166
168
|
background-repeat: no-repeat;
|
|
167
|
-
background-size:
|
|
169
|
+
background-size: contain;
|
|
170
|
+
background-position: center;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
.pc-visible {
|
|
174
|
+
display: block;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
.mobile-visible {
|
|
178
|
+
display: none;
|
|
168
179
|
}
|
|
169
180
|
|
|
170
181
|
@media screen and (max-width: 500px) {
|
|
171
182
|
.cover-img {
|
|
172
183
|
width: 100px;
|
|
173
184
|
height: 60px;
|
|
174
|
-
background-size:
|
|
185
|
+
background-size: contain;
|
|
186
|
+
background-position: center;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
.pc-visible {
|
|
190
|
+
display: none;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
.mobile-visible {
|
|
194
|
+
display: block;
|
|
175
195
|
}
|
|
176
196
|
}
|
|
177
197
|
</style>
|