valaxy-theme-hairy 1.0.1 → 1.0.2
Sign up to get free protection for your applications and to get access to all the features.
- package/LICENSE +21 -21
- package/client/index.ts +1 -1
- package/components/HairyBody.vue +49 -49
- package/components/HairyCodepen.vue +40 -40
- package/components/HairyComment.vue +33 -33
- package/components/HairyContainer.vue +17 -17
- package/components/HairyDrawer.vue +44 -44
- package/components/HairyFooter.vue +62 -62
- package/components/HairyHeader.vue +32 -32
- package/components/HairyImage.vue +15 -15
- package/components/HairyImageGroup.vue +65 -65
- package/components/HairyNavbar.vue +56 -56
- package/components/HairyPageArchives.vue +59 -59
- package/components/HairyPageTags.vue +48 -48
- package/components/HairyPosts.vue +54 -54
- package/components/HairySearch.vue +201 -201
- package/components/HairySidebar.vue +30 -30
- package/components/HairyTabbar.vue +56 -56
- package/components/PageTags.vue +48 -48
- package/components/ValaxyMain.vue +45 -45
- package/components/navbar/HairyNav.vue +16 -16
- package/components/navbar/HairyNavExpand.vue +12 -12
- package/components/navbar/HairyNavItem.vue +35 -35
- package/components/navbar/HairyNavbarBackground.vue +7 -7
- package/components/navbar/HairyNavbarSearch.vue +8 -8
- package/components/navbar/HairyNavbarTitle.vue +15 -15
- package/components/navbar/HairyNavbarToggleDark.vue +22 -22
- package/components/parts/HairyBreadcrumb.vue +51 -51
- package/components/parts/HairyBreadcrumbItem.vue +11 -11
- package/components/parts/HairyFootFish.js +352 -352
- package/components/parts/HairyFootFish.vue +38 -38
- package/components/parts/HairyHeadHero.vue +34 -34
- package/components/parts/HairyHeadWaves.vue +67 -67
- package/components/parts/HairyImageGlobal.vue +51 -51
- package/components/parts/HairyImageViewer.vue +23 -23
- package/components/parts/HairyLink.vue +21 -21
- package/components/parts/HairyMenu.vue +16 -16
- package/components/parts/HairyMenuItem.vue +47 -47
- package/components/parts/HairyOutline.vue +99 -99
- package/components/parts/HairyOutlineItem.vue +48 -48
- package/components/parts/HairySocialLinks.vue +27 -27
- package/components/parts/HairyTimelineContent.vue +39 -39
- package/components/parts/HairyUserNav.vue +95 -95
- package/components/parts/HairyUserStats.vue +18 -18
- package/components/posts/HairyArticleImage.vue +126 -126
- package/components/posts/HairyArticleSeries.vue +89 -89
- package/components/posts/HairyArticleText.vue +43 -43
- package/components/posts/HairyPostFooter.vue +15 -15
- package/components/posts/HairyPostImageList.vue +27 -27
- package/components/posts/HairyPostTextsList.vue +22 -22
- package/components/posts/HairyPostToggleLayout.vue +36 -36
- package/components/third/HairyAlgoliaSearch.vue +17 -17
- package/components/third/HairyFuseSearch.vue +10 -10
- package/components/third/HairyFuseSearchDialog.vue +32 -32
- package/components/third/HairyFuseSearchDropdown.vue +77 -77
- package/components/third/HairyFuseSearchFooter.vue +28 -28
- package/components/third/HairyFuseSearchHeader.vue +30 -30
- package/components/third/HairyFuseSearchHit.vue +52 -52
- package/components/third/HairySearchBtnDisplay.vue +29 -29
- package/components/third/HairySearchBtnInput.vue +20 -20
- package/components/third/HairySearchBtnKeys.vue +19 -19
- package/components/third/HairySwiperCarousel.vue +45 -45
- package/composables/archives.ts +48 -48
- package/composables/category.ts +43 -43
- package/composables/config.ts +11 -11
- package/composables/dark.ts +13 -13
- package/composables/fuse.ts +60 -60
- package/composables/index.ts +7 -7
- package/composables/layout.ts +16 -16
- package/composables/outline.ts +49 -49
- package/composables/tags.ts +36 -36
- package/layouts/archive-month.vue +13 -13
- package/layouts/archive-year.vue +13 -13
- package/layouts/archives.vue +11 -11
- package/layouts/categories.vue +13 -13
- package/layouts/default.vue +13 -15
- package/layouts/home.vue +33 -33
- package/layouts/post.vue +54 -54
- package/layouts/tag.vue +10 -10
- package/layouts/tags.vue +10 -14
- package/library/loading.scss +535 -535
- package/library/loading.ts +60 -60
- package/library/scroll.ts +22 -22
- package/locales/en.yml +1 -1
- package/locales/zh-CN.yml +1 -1
- package/node/images/default.json +139 -139
- package/node/images/index.ts +46 -46
- package/node/images/shims.d.ts +8 -8
- package/node/index.ts +2 -2
- package/node/theme/index.ts +78 -78
- package/package.json +1 -1
- package/pages/archives/[year]/[month]/index.vue +48 -48
- package/pages/archives/[year]/index.vue +73 -73
- package/pages/archives/index.md +6 -0
- package/pages/categories/[...its].vue +108 -108
- package/pages/index.vue +8 -8
- package/pages/page/[page].vue +12 -12
- package/pages/tags/[tag]/index.vue +38 -38
- package/pages/tags/index.md +7 -0
- package/setup/main.ts +9 -9
- package/store/index.ts +1 -1
- package/store/modules/global.ts +12 -12
- package/styles/components/aplayer.scss +75 -75
- package/styles/components/index.scss +3 -3
- package/styles/components/markdown.scss +89 -89
- package/styles/components/nprogress.scss +15 -15
- package/styles/components/scrollbar.scss +25 -25
- package/styles/css-vars.scss +171 -171
- package/styles/element-plus/index.scss +1 -1
- package/styles/element-plus/tabs.scss +25 -25
- package/styles/element-plus/timeline.scss +18 -18
- package/styles/font-face.scss +19 -19
- package/styles/global.scss +38 -38
- package/styles/index.scss +3 -3
- package/tsconfig.json +27 -27
- package/types/index.d.ts +163 -163
- package/unocss.config.ts +43 -43
- package/utils/index.ts +37 -37
- package/valaxy.config.ts +26 -26
- package/pages/archives/index.vue +0 -6
- package/pages/tags/index.vue +0 -6
package/node/theme/index.ts
CHANGED
@@ -1,78 +1,78 @@
|
|
1
|
-
import type { ResolvedValaxyOptions, ValaxyTheme } from 'valaxy'
|
2
|
-
import type { ThemeConfig } from '../../types'
|
3
|
-
|
4
|
-
/**
|
5
|
-
* Default Config
|
6
|
-
*/
|
7
|
-
export const defaultThemeConfig: ThemeConfig = {
|
8
|
-
colors: {
|
9
|
-
primary: '#0078E7',
|
10
|
-
},
|
11
|
-
|
12
|
-
footer: {
|
13
|
-
since: 2022,
|
14
|
-
icon: {
|
15
|
-
name: 'i-ri-cloud-line',
|
16
|
-
animated: true,
|
17
|
-
color: 'var(--va-c-primary)',
|
18
|
-
url: 'https://sponsors.yunyoujun.cn',
|
19
|
-
title: 'Sponsor YunYouJun',
|
20
|
-
},
|
21
|
-
|
22
|
-
powered: true,
|
23
|
-
|
24
|
-
beian: {
|
25
|
-
enable: false,
|
26
|
-
icp: '',
|
27
|
-
},
|
28
|
-
},
|
29
|
-
|
30
|
-
nav: [],
|
31
|
-
}
|
32
|
-
|
33
|
-
// write a vite plugin
|
34
|
-
// https://vitejs.dev/guide/api-plugin.html
|
35
|
-
export function withThemeConfig(options: ResolvedValaxyOptions<ThemeConfig>): ValaxyTheme<ThemeConfig> {
|
36
|
-
const themeConfig = options.config.themeConfig || {}
|
37
|
-
|
38
|
-
return {
|
39
|
-
themeConfig: defaultThemeConfig,
|
40
|
-
vite: {
|
41
|
-
plugins: [
|
42
|
-
{
|
43
|
-
name: 'vite-plugin-hairy:theme',
|
44
|
-
config() {
|
45
|
-
return {
|
46
|
-
css: {
|
47
|
-
preprocessorOptions: {
|
48
|
-
scss: {
|
49
|
-
additionalData: `$c-primary: ${themeConfig.colors?.primary || '#0078E7'} !default;`,
|
50
|
-
},
|
51
|
-
},
|
52
|
-
},
|
53
|
-
|
54
|
-
valaxy: {},
|
55
|
-
}
|
56
|
-
},
|
57
|
-
},
|
58
|
-
],
|
59
|
-
},
|
60
|
-
unocss: {
|
61
|
-
safelist: generateSafelist(options.config.themeConfig as ThemeConfig),
|
62
|
-
},
|
63
|
-
}
|
64
|
-
}
|
65
|
-
|
66
|
-
/**
|
67
|
-
* generateSafelist by config
|
68
|
-
* @param themeConfig
|
69
|
-
*/
|
70
|
-
export function generateSafelist(themeConfig: ThemeConfig) {
|
71
|
-
const safelist: string[] = []
|
72
|
-
|
73
|
-
const footerIcon = themeConfig.footer?.icon?.name
|
74
|
-
if (footerIcon)
|
75
|
-
safelist.push(footerIcon)
|
76
|
-
|
77
|
-
return safelist
|
78
|
-
}
|
1
|
+
import type { ResolvedValaxyOptions, ValaxyTheme } from 'valaxy'
|
2
|
+
import type { ThemeConfig } from '../../types'
|
3
|
+
|
4
|
+
/**
|
5
|
+
* Default Config
|
6
|
+
*/
|
7
|
+
export const defaultThemeConfig: ThemeConfig = {
|
8
|
+
colors: {
|
9
|
+
primary: '#0078E7',
|
10
|
+
},
|
11
|
+
|
12
|
+
footer: {
|
13
|
+
since: 2022,
|
14
|
+
icon: {
|
15
|
+
name: 'i-ri-cloud-line',
|
16
|
+
animated: true,
|
17
|
+
color: 'var(--va-c-primary)',
|
18
|
+
url: 'https://sponsors.yunyoujun.cn',
|
19
|
+
title: 'Sponsor YunYouJun',
|
20
|
+
},
|
21
|
+
|
22
|
+
powered: true,
|
23
|
+
|
24
|
+
beian: {
|
25
|
+
enable: false,
|
26
|
+
icp: '',
|
27
|
+
},
|
28
|
+
},
|
29
|
+
|
30
|
+
nav: [],
|
31
|
+
}
|
32
|
+
|
33
|
+
// write a vite plugin
|
34
|
+
// https://vitejs.dev/guide/api-plugin.html
|
35
|
+
export function withThemeConfig(options: ResolvedValaxyOptions<ThemeConfig>): ValaxyTheme<ThemeConfig> {
|
36
|
+
const themeConfig = options.config.themeConfig || {}
|
37
|
+
|
38
|
+
return {
|
39
|
+
themeConfig: defaultThemeConfig,
|
40
|
+
vite: {
|
41
|
+
plugins: [
|
42
|
+
{
|
43
|
+
name: 'vite-plugin-hairy:theme',
|
44
|
+
config() {
|
45
|
+
return {
|
46
|
+
css: {
|
47
|
+
preprocessorOptions: {
|
48
|
+
scss: {
|
49
|
+
additionalData: `$c-primary: ${themeConfig.colors?.primary || '#0078E7'} !default;`,
|
50
|
+
},
|
51
|
+
},
|
52
|
+
},
|
53
|
+
|
54
|
+
valaxy: {},
|
55
|
+
}
|
56
|
+
},
|
57
|
+
},
|
58
|
+
],
|
59
|
+
},
|
60
|
+
unocss: {
|
61
|
+
safelist: generateSafelist(options.config.themeConfig as ThemeConfig),
|
62
|
+
},
|
63
|
+
}
|
64
|
+
}
|
65
|
+
|
66
|
+
/**
|
67
|
+
* generateSafelist by config
|
68
|
+
* @param themeConfig
|
69
|
+
*/
|
70
|
+
export function generateSafelist(themeConfig: ThemeConfig) {
|
71
|
+
const safelist: string[] = []
|
72
|
+
|
73
|
+
const footerIcon = themeConfig.footer?.icon?.name
|
74
|
+
if (footerIcon)
|
75
|
+
safelist.push(footerIcon)
|
76
|
+
|
77
|
+
return safelist
|
78
|
+
}
|
package/package.json
CHANGED
@@ -1,48 +1,48 @@
|
|
1
|
-
<script lang="ts" setup>
|
2
|
-
import { ElTimeline, ElTimelineItem } from 'element-plus
|
3
|
-
import { computed } from 'vue'
|
4
|
-
import { useRoute } from 'vue-router'
|
5
|
-
import { useYearArchives } from '../../../../composables'
|
6
|
-
import { getArchiveLink } from '../../../../utils'
|
7
|
-
|
8
|
-
const year = computed(() => useRoute().params.year as string)
|
9
|
-
const month = computed(() => useRoute().params.month as string)
|
10
|
-
|
11
|
-
const activities = useYearArchives()
|
12
|
-
const months = computed(() =>
|
13
|
-
activities.value
|
14
|
-
.filter(item => item.year === year.value)
|
15
|
-
.filter(item => item.month === month.value),
|
16
|
-
)
|
17
|
-
const count = computed(() => months.value.reduce((total, value) => total + value.count, 0))
|
18
|
-
const post = computed(() => months.value.flatMap(item => item.posts))
|
19
|
-
</script>
|
20
|
-
|
21
|
-
<template>
|
22
|
-
<HairyBreadcrumb class="mb-5" size="large" :after="`归档(${count}篇)`">
|
23
|
-
<HairyBreadcrumbItem to="/archives/">
|
24
|
-
全部
|
25
|
-
</HairyBreadcrumbItem>
|
26
|
-
<HairyBreadcrumbItem :to="getArchiveLink(year)">
|
27
|
-
{{ year }}年
|
28
|
-
</HairyBreadcrumbItem>
|
29
|
-
<HairyBreadcrumbItem :to="getArchiveLink(year)">
|
30
|
-
{{ month }}月
|
31
|
-
</HairyBreadcrumbItem>
|
32
|
-
</HairyBreadcrumb>
|
33
|
-
<ElTimeline>
|
34
|
-
<ElTimelineItem
|
35
|
-
v-for="(item, index) in post"
|
36
|
-
:key="index"
|
37
|
-
hollow
|
38
|
-
size="large"
|
39
|
-
>
|
40
|
-
<HairyTimelineContent :post="item" />
|
41
|
-
</ElTimelineItem>
|
42
|
-
</ElTimeline>
|
43
|
-
</template>
|
44
|
-
|
45
|
-
<route lang="yaml">
|
46
|
-
meta:
|
47
|
-
layout: archive-month
|
48
|
-
</route>
|
1
|
+
<script lang="ts" setup>
|
2
|
+
import { ElTimeline, ElTimelineItem } from 'element-plus'
|
3
|
+
import { computed } from 'vue'
|
4
|
+
import { useRoute } from 'vue-router'
|
5
|
+
import { useYearArchives } from '../../../../composables'
|
6
|
+
import { getArchiveLink } from '../../../../utils'
|
7
|
+
|
8
|
+
const year = computed(() => useRoute().params.year as string)
|
9
|
+
const month = computed(() => useRoute().params.month as string)
|
10
|
+
|
11
|
+
const activities = useYearArchives()
|
12
|
+
const months = computed(() =>
|
13
|
+
activities.value
|
14
|
+
.filter(item => item.year === year.value)
|
15
|
+
.filter(item => item.month === month.value),
|
16
|
+
)
|
17
|
+
const count = computed(() => months.value.reduce((total, value) => total + value.count, 0))
|
18
|
+
const post = computed(() => months.value.flatMap(item => item.posts))
|
19
|
+
</script>
|
20
|
+
|
21
|
+
<template>
|
22
|
+
<HairyBreadcrumb class="mb-5" size="large" :after="`归档(${count}篇)`">
|
23
|
+
<HairyBreadcrumbItem to="/archives/">
|
24
|
+
全部
|
25
|
+
</HairyBreadcrumbItem>
|
26
|
+
<HairyBreadcrumbItem :to="getArchiveLink(year)">
|
27
|
+
{{ year }}年
|
28
|
+
</HairyBreadcrumbItem>
|
29
|
+
<HairyBreadcrumbItem :to="getArchiveLink(year)">
|
30
|
+
{{ month }}月
|
31
|
+
</HairyBreadcrumbItem>
|
32
|
+
</HairyBreadcrumb>
|
33
|
+
<ElTimeline>
|
34
|
+
<ElTimelineItem
|
35
|
+
v-for="(item, index) in post"
|
36
|
+
:key="index"
|
37
|
+
hollow
|
38
|
+
size="large"
|
39
|
+
>
|
40
|
+
<HairyTimelineContent :post="item" />
|
41
|
+
</ElTimelineItem>
|
42
|
+
</ElTimeline>
|
43
|
+
</template>
|
44
|
+
|
45
|
+
<route lang="yaml">
|
46
|
+
meta:
|
47
|
+
layout: archive-month
|
48
|
+
</route>
|
@@ -1,73 +1,73 @@
|
|
1
|
-
<script lang="ts" setup>
|
2
|
-
import { ElTimeline, ElTimelineItem } from 'element-plus
|
3
|
-
import { computed } from 'vue'
|
4
|
-
import type { Post } from 'valaxy'
|
5
|
-
import { useRoute } from 'vue-router'
|
6
|
-
import { getArchiveLink } from '../../../utils'
|
7
|
-
import { useYearArchives } from '../../../composables'
|
8
|
-
|
9
|
-
interface TimeLineByPost extends Post {
|
10
|
-
type: 'link' | 'post'
|
11
|
-
month: string
|
12
|
-
link: string
|
13
|
-
count: number
|
14
|
-
}
|
15
|
-
|
16
|
-
const activities = useYearArchives()
|
17
|
-
|
18
|
-
const year = computed(() => useRoute().params.year as string)
|
19
|
-
|
20
|
-
const filterYear = computed(() => activities.value.filter(item => item.year === year.value))
|
21
|
-
const timelines = computed(() => {
|
22
|
-
const timeLines: Partial<TimeLineByPost>[] = []
|
23
|
-
for (const { year, count, month, posts } of filterYear.value) {
|
24
|
-
timeLines.push({
|
25
|
-
type: 'link',
|
26
|
-
month,
|
27
|
-
count,
|
28
|
-
link: getArchiveLink(year, month),
|
29
|
-
})
|
30
|
-
for (const post of posts)
|
31
|
-
timeLines.push({ ...post, type: 'post' })
|
32
|
-
}
|
33
|
-
return timeLines as TimeLineByPost[]
|
34
|
-
})
|
35
|
-
</script>
|
36
|
-
|
37
|
-
<template>
|
38
|
-
<HairyBreadcrumb class="mb-5" size="large" after="归档">
|
39
|
-
<HairyBreadcrumbItem to="/archives/">
|
40
|
-
全部
|
41
|
-
</HairyBreadcrumbItem>
|
42
|
-
<HairyBreadcrumbItem>
|
43
|
-
{{ year }}年
|
44
|
-
</HairyBreadcrumbItem>
|
45
|
-
</HairyBreadcrumb>
|
46
|
-
|
47
|
-
<ElTimeline>
|
48
|
-
<template v-for="(item, index) in timelines" :key="index">
|
49
|
-
<ElTimelineItem
|
50
|
-
v-if="item.type === 'link'"
|
51
|
-
hollow
|
52
|
-
:index="index"
|
53
|
-
size="large"
|
54
|
-
>
|
55
|
-
<HairyLink @click="$router.push(getArchiveLink(year, item.month))">
|
56
|
-
{{ item.month }}月
|
57
|
-
</HairyLink>
|
58
|
-
</ElTimelineItem>
|
59
|
-
<ElTimelineItem
|
60
|
-
v-else
|
61
|
-
hollow
|
62
|
-
size="normal"
|
63
|
-
>
|
64
|
-
<HairyTimelineContent :post="item" />
|
65
|
-
</ElTimelineItem>
|
66
|
-
</template>
|
67
|
-
</ElTimeline>
|
68
|
-
</template>
|
69
|
-
|
70
|
-
<route lang="yaml">
|
71
|
-
meta:
|
72
|
-
layout: archive-year
|
73
|
-
</route>
|
1
|
+
<script lang="ts" setup>
|
2
|
+
import { ElTimeline, ElTimelineItem } from 'element-plus'
|
3
|
+
import { computed } from 'vue'
|
4
|
+
import type { Post } from 'valaxy'
|
5
|
+
import { useRoute } from 'vue-router'
|
6
|
+
import { getArchiveLink } from '../../../utils'
|
7
|
+
import { useYearArchives } from '../../../composables'
|
8
|
+
|
9
|
+
interface TimeLineByPost extends Post {
|
10
|
+
type: 'link' | 'post'
|
11
|
+
month: string
|
12
|
+
link: string
|
13
|
+
count: number
|
14
|
+
}
|
15
|
+
|
16
|
+
const activities = useYearArchives()
|
17
|
+
|
18
|
+
const year = computed(() => useRoute().params.year as string)
|
19
|
+
|
20
|
+
const filterYear = computed(() => activities.value.filter(item => item.year === year.value))
|
21
|
+
const timelines = computed(() => {
|
22
|
+
const timeLines: Partial<TimeLineByPost>[] = []
|
23
|
+
for (const { year, count, month, posts } of filterYear.value) {
|
24
|
+
timeLines.push({
|
25
|
+
type: 'link',
|
26
|
+
month,
|
27
|
+
count,
|
28
|
+
link: getArchiveLink(year, month),
|
29
|
+
})
|
30
|
+
for (const post of posts)
|
31
|
+
timeLines.push({ ...post, type: 'post' })
|
32
|
+
}
|
33
|
+
return timeLines as TimeLineByPost[]
|
34
|
+
})
|
35
|
+
</script>
|
36
|
+
|
37
|
+
<template>
|
38
|
+
<HairyBreadcrumb class="mb-5" size="large" after="归档">
|
39
|
+
<HairyBreadcrumbItem to="/archives/">
|
40
|
+
全部
|
41
|
+
</HairyBreadcrumbItem>
|
42
|
+
<HairyBreadcrumbItem>
|
43
|
+
{{ year }}年
|
44
|
+
</HairyBreadcrumbItem>
|
45
|
+
</HairyBreadcrumb>
|
46
|
+
|
47
|
+
<ElTimeline>
|
48
|
+
<template v-for="(item, index) in timelines" :key="index">
|
49
|
+
<ElTimelineItem
|
50
|
+
v-if="item.type === 'link'"
|
51
|
+
hollow
|
52
|
+
:index="index"
|
53
|
+
size="large"
|
54
|
+
>
|
55
|
+
<HairyLink @click="$router.push(getArchiveLink(year, item.month))">
|
56
|
+
{{ item.month }}月
|
57
|
+
</HairyLink>
|
58
|
+
</ElTimelineItem>
|
59
|
+
<ElTimelineItem
|
60
|
+
v-else
|
61
|
+
hollow
|
62
|
+
size="normal"
|
63
|
+
>
|
64
|
+
<HairyTimelineContent :post="item" />
|
65
|
+
</ElTimelineItem>
|
66
|
+
</template>
|
67
|
+
</ElTimeline>
|
68
|
+
</template>
|
69
|
+
|
70
|
+
<route lang="yaml">
|
71
|
+
meta:
|
72
|
+
layout: archive-year
|
73
|
+
</route>
|
@@ -1,108 +1,108 @@
|
|
1
|
-
<script lang="ts" setup>
|
2
|
-
import { computed } from 'vue'
|
3
|
-
import { useI18n } from 'vue-i18n'
|
4
|
-
import { useRoute, useRouter } from 'vue-router'
|
5
|
-
import { useThemeConfig } from 'valaxy'
|
6
|
-
import { ElTimeline, ElTimelineItem } from 'element-plus
|
7
|
-
|
8
|
-
import type { HairyTheme } from '../..'
|
9
|
-
import { useCategory, useCategoryPost } from '../../composables'
|
10
|
-
|
11
|
-
const themeConfig = useThemeConfig<HairyTheme.Config>()
|
12
|
-
const layout = computed(() => themeConfig.value.layout?.categories || 'post')
|
13
|
-
|
14
|
-
const router = useRouter()
|
15
|
-
const route = useRoute()
|
16
|
-
|
17
|
-
const categories = computed(() => route.params.its as string)
|
18
|
-
const paths = computed(() => categories.value.split('/').filter(Boolean))
|
19
|
-
const current = useCategory(paths)
|
20
|
-
const posts = useCategoryPost(paths)
|
21
|
-
|
22
|
-
const i18n = useI18n()
|
23
|
-
|
24
|
-
function getBreadcrumbPath(index: number) {
|
25
|
-
const paths = categories.value.split('/').filter(Boolean)
|
26
|
-
if (paths[index] === paths[paths.length - 1])
|
27
|
-
return ''
|
28
|
-
return `/categories/${paths.slice(0, index + 1).join('/')}`
|
29
|
-
}
|
30
|
-
|
31
|
-
function displayCategory(key: string) {
|
32
|
-
router.push({ path: `/categories/${[...paths.value, key].join('/')}` })
|
33
|
-
}
|
34
|
-
</script>
|
35
|
-
|
36
|
-
<template>
|
37
|
-
<HairyBreadcrumb class="mb-5" size="large" after="分类">
|
38
|
-
<HairyBreadcrumbItem to="/">
|
39
|
-
首页
|
40
|
-
</HairyBreadcrumbItem>
|
41
|
-
<HairyBreadcrumbItem :to="paths.length && '/categories/' || ''">
|
42
|
-
全部
|
43
|
-
</HairyBreadcrumbItem>
|
44
|
-
<HairyBreadcrumbItem v-for="(key, index) in categories.split('/')" :key="key" :to="getBreadcrumbPath(index)">
|
45
|
-
{{ i18n.t(key) }}
|
46
|
-
</HairyBreadcrumbItem>
|
47
|
-
</HairyBreadcrumb>
|
48
|
-
|
49
|
-
<div class="grid__view dark:text-gray-3 flex-wrap">
|
50
|
-
<template v-for="([key, item]) in current.children" :key="key">
|
51
|
-
<div
|
52
|
-
v-if="!key.startsWith('/post')"
|
53
|
-
class="relative flex items-center flex-col cursor-pointer hover:text-primary transition-colors"
|
54
|
-
@click="displayCategory(key)"
|
55
|
-
>
|
56
|
-
<div class="i-material-symbols-folder-open-rounded text-22 lt-sm:text-15" />
|
57
|
-
<div class="text-center leading-normal">
|
58
|
-
{{ i18n.t(key) }}
|
59
|
-
</div>
|
60
|
-
<div class="badge text-12px right-20px top-12px">
|
61
|
-
{{ item.total }} dir
|
62
|
-
</div>
|
63
|
-
</div>
|
64
|
-
</template>
|
65
|
-
</div>
|
66
|
-
<div class="border-t border-gray-200 dark:border-gray-500 mt-5" />
|
67
|
-
|
68
|
-
<ElTimeline v-if="layout === 'timeline'" class="pt-5 pl-10">
|
69
|
-
<ElTimelineItem v-for="(item, index) in posts" :key="index" hollow size="large">
|
70
|
-
<HairyTimelinePostItem :post="item" />
|
71
|
-
</ElTimelineItem>
|
72
|
-
</ElTimeline>
|
73
|
-
<template v-else>
|
74
|
-
<HairyPosts :posts="posts" />
|
75
|
-
</template>
|
76
|
-
</template>
|
77
|
-
|
78
|
-
<style lang="scss" scoped>
|
79
|
-
.badge {
|
80
|
-
position: absolute;
|
81
|
-
// padding-left: 6px;
|
82
|
-
// padding-top: 2px;
|
83
|
-
// padding-bottom: 2px;
|
84
|
-
// padding-right: 6px;
|
85
|
-
line-height: normal;
|
86
|
-
border-radius: 50%;
|
87
|
-
color: #fff;
|
88
|
-
}
|
89
|
-
|
90
|
-
.grid__view {
|
91
|
-
display: grid;
|
92
|
-
grid-template-columns: repeat(auto-fill, 7.5rem);
|
93
|
-
gap: 3rem;
|
94
|
-
|
95
|
-
@media (max-width: 640px) {
|
96
|
-
grid-template-columns: repeat(auto-fill, 6.5rem);
|
97
|
-
}
|
98
|
-
|
99
|
-
&>* {
|
100
|
-
height: var(--height);
|
101
|
-
}
|
102
|
-
}
|
103
|
-
</style>
|
104
|
-
|
105
|
-
<route lang="yaml">
|
106
|
-
meta:
|
107
|
-
layout: categories
|
108
|
-
</route>
|
1
|
+
<script lang="ts" setup>
|
2
|
+
import { computed } from 'vue'
|
3
|
+
import { useI18n } from 'vue-i18n'
|
4
|
+
import { useRoute, useRouter } from 'vue-router'
|
5
|
+
import { useThemeConfig } from 'valaxy'
|
6
|
+
import { ElTimeline, ElTimelineItem } from 'element-plus'
|
7
|
+
|
8
|
+
import type { HairyTheme } from '../..'
|
9
|
+
import { useCategory, useCategoryPost } from '../../composables'
|
10
|
+
|
11
|
+
const themeConfig = useThemeConfig<HairyTheme.Config>()
|
12
|
+
const layout = computed(() => themeConfig.value.layout?.categories || 'post')
|
13
|
+
|
14
|
+
const router = useRouter()
|
15
|
+
const route = useRoute()
|
16
|
+
|
17
|
+
const categories = computed(() => route.params.its as string)
|
18
|
+
const paths = computed(() => categories.value.split('/').filter(Boolean))
|
19
|
+
const current = useCategory(paths)
|
20
|
+
const posts = useCategoryPost(paths)
|
21
|
+
|
22
|
+
const i18n = useI18n()
|
23
|
+
|
24
|
+
function getBreadcrumbPath(index: number) {
|
25
|
+
const paths = categories.value.split('/').filter(Boolean)
|
26
|
+
if (paths[index] === paths[paths.length - 1])
|
27
|
+
return ''
|
28
|
+
return `/categories/${paths.slice(0, index + 1).join('/')}`
|
29
|
+
}
|
30
|
+
|
31
|
+
function displayCategory(key: string) {
|
32
|
+
router.push({ path: `/categories/${[...paths.value, key].join('/')}` })
|
33
|
+
}
|
34
|
+
</script>
|
35
|
+
|
36
|
+
<template>
|
37
|
+
<HairyBreadcrumb class="mb-5" size="large" after="分类">
|
38
|
+
<HairyBreadcrumbItem to="/">
|
39
|
+
首页
|
40
|
+
</HairyBreadcrumbItem>
|
41
|
+
<HairyBreadcrumbItem :to="paths.length && '/categories/' || ''">
|
42
|
+
全部
|
43
|
+
</HairyBreadcrumbItem>
|
44
|
+
<HairyBreadcrumbItem v-for="(key, index) in categories.split('/')" :key="key" :to="getBreadcrumbPath(index)">
|
45
|
+
{{ i18n.t(key) }}
|
46
|
+
</HairyBreadcrumbItem>
|
47
|
+
</HairyBreadcrumb>
|
48
|
+
|
49
|
+
<div class="grid__view dark:text-gray-3 flex-wrap">
|
50
|
+
<template v-for="([key, item]) in current.children" :key="key">
|
51
|
+
<div
|
52
|
+
v-if="!key.startsWith('/post')"
|
53
|
+
class="relative flex items-center flex-col cursor-pointer hover:text-primary transition-colors"
|
54
|
+
@click="displayCategory(key)"
|
55
|
+
>
|
56
|
+
<div class="i-material-symbols-folder-open-rounded text-22 lt-sm:text-15" />
|
57
|
+
<div class="text-center leading-normal">
|
58
|
+
{{ i18n.t(key) }}
|
59
|
+
</div>
|
60
|
+
<div class="badge text-12px right-20px top-12px">
|
61
|
+
{{ item.total }} dir
|
62
|
+
</div>
|
63
|
+
</div>
|
64
|
+
</template>
|
65
|
+
</div>
|
66
|
+
<div class="border-t border-gray-200 dark:border-gray-500 mt-5" />
|
67
|
+
|
68
|
+
<ElTimeline v-if="layout === 'timeline'" class="pt-5 pl-10">
|
69
|
+
<ElTimelineItem v-for="(item, index) in posts" :key="index" hollow size="large">
|
70
|
+
<HairyTimelinePostItem :post="item" />
|
71
|
+
</ElTimelineItem>
|
72
|
+
</ElTimeline>
|
73
|
+
<template v-else>
|
74
|
+
<HairyPosts :posts="posts" />
|
75
|
+
</template>
|
76
|
+
</template>
|
77
|
+
|
78
|
+
<style lang="scss" scoped>
|
79
|
+
.badge {
|
80
|
+
position: absolute;
|
81
|
+
// padding-left: 6px;
|
82
|
+
// padding-top: 2px;
|
83
|
+
// padding-bottom: 2px;
|
84
|
+
// padding-right: 6px;
|
85
|
+
line-height: normal;
|
86
|
+
border-radius: 50%;
|
87
|
+
color: #fff;
|
88
|
+
}
|
89
|
+
|
90
|
+
.grid__view {
|
91
|
+
display: grid;
|
92
|
+
grid-template-columns: repeat(auto-fill, 7.5rem);
|
93
|
+
gap: 3rem;
|
94
|
+
|
95
|
+
@media (max-width: 640px) {
|
96
|
+
grid-template-columns: repeat(auto-fill, 6.5rem);
|
97
|
+
}
|
98
|
+
|
99
|
+
&>* {
|
100
|
+
height: var(--height);
|
101
|
+
}
|
102
|
+
}
|
103
|
+
</style>
|
104
|
+
|
105
|
+
<route lang="yaml">
|
106
|
+
meta:
|
107
|
+
layout: categories
|
108
|
+
</route>
|
package/pages/index.vue
CHANGED
@@ -1,8 +1,8 @@
|
|
1
|
-
<template>
|
2
|
-
<HairyPosts pagination />
|
3
|
-
</template>
|
4
|
-
|
5
|
-
<route lang="yaml">
|
6
|
-
meta:
|
7
|
-
layout: home
|
8
|
-
</route>
|
1
|
+
<template>
|
2
|
+
<HairyPosts pagination />
|
3
|
+
</template>
|
4
|
+
|
5
|
+
<route lang="yaml">
|
6
|
+
meta:
|
7
|
+
layout: home
|
8
|
+
</route>
|