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.
Files changed (121) hide show
  1. package/LICENSE +21 -21
  2. package/client/index.ts +1 -1
  3. package/components/HairyBody.vue +49 -49
  4. package/components/HairyCodepen.vue +40 -40
  5. package/components/HairyComment.vue +33 -33
  6. package/components/HairyContainer.vue +17 -17
  7. package/components/HairyDrawer.vue +44 -44
  8. package/components/HairyFooter.vue +62 -62
  9. package/components/HairyHeader.vue +32 -32
  10. package/components/HairyImage.vue +15 -15
  11. package/components/HairyImageGroup.vue +65 -65
  12. package/components/HairyNavbar.vue +56 -56
  13. package/components/HairyPageArchives.vue +59 -59
  14. package/components/HairyPageTags.vue +48 -48
  15. package/components/HairyPosts.vue +54 -54
  16. package/components/HairySearch.vue +201 -201
  17. package/components/HairySidebar.vue +30 -30
  18. package/components/HairyTabbar.vue +56 -56
  19. package/components/PageTags.vue +48 -48
  20. package/components/ValaxyMain.vue +45 -45
  21. package/components/navbar/HairyNav.vue +16 -16
  22. package/components/navbar/HairyNavExpand.vue +12 -12
  23. package/components/navbar/HairyNavItem.vue +35 -35
  24. package/components/navbar/HairyNavbarBackground.vue +7 -7
  25. package/components/navbar/HairyNavbarSearch.vue +8 -8
  26. package/components/navbar/HairyNavbarTitle.vue +15 -15
  27. package/components/navbar/HairyNavbarToggleDark.vue +22 -22
  28. package/components/parts/HairyBreadcrumb.vue +51 -51
  29. package/components/parts/HairyBreadcrumbItem.vue +11 -11
  30. package/components/parts/HairyFootFish.js +352 -352
  31. package/components/parts/HairyFootFish.vue +38 -38
  32. package/components/parts/HairyHeadHero.vue +34 -34
  33. package/components/parts/HairyHeadWaves.vue +67 -67
  34. package/components/parts/HairyImageGlobal.vue +51 -51
  35. package/components/parts/HairyImageViewer.vue +23 -23
  36. package/components/parts/HairyLink.vue +21 -21
  37. package/components/parts/HairyMenu.vue +16 -16
  38. package/components/parts/HairyMenuItem.vue +47 -47
  39. package/components/parts/HairyOutline.vue +99 -99
  40. package/components/parts/HairyOutlineItem.vue +48 -48
  41. package/components/parts/HairySocialLinks.vue +27 -27
  42. package/components/parts/HairyTimelineContent.vue +39 -39
  43. package/components/parts/HairyUserNav.vue +95 -95
  44. package/components/parts/HairyUserStats.vue +18 -18
  45. package/components/posts/HairyArticleImage.vue +126 -126
  46. package/components/posts/HairyArticleSeries.vue +89 -89
  47. package/components/posts/HairyArticleText.vue +43 -43
  48. package/components/posts/HairyPostFooter.vue +15 -15
  49. package/components/posts/HairyPostImageList.vue +27 -27
  50. package/components/posts/HairyPostTextsList.vue +22 -22
  51. package/components/posts/HairyPostToggleLayout.vue +36 -36
  52. package/components/third/HairyAlgoliaSearch.vue +17 -17
  53. package/components/third/HairyFuseSearch.vue +10 -10
  54. package/components/third/HairyFuseSearchDialog.vue +32 -32
  55. package/components/third/HairyFuseSearchDropdown.vue +77 -77
  56. package/components/third/HairyFuseSearchFooter.vue +28 -28
  57. package/components/third/HairyFuseSearchHeader.vue +30 -30
  58. package/components/third/HairyFuseSearchHit.vue +52 -52
  59. package/components/third/HairySearchBtnDisplay.vue +29 -29
  60. package/components/third/HairySearchBtnInput.vue +20 -20
  61. package/components/third/HairySearchBtnKeys.vue +19 -19
  62. package/components/third/HairySwiperCarousel.vue +45 -45
  63. package/composables/archives.ts +48 -48
  64. package/composables/category.ts +43 -43
  65. package/composables/config.ts +11 -11
  66. package/composables/dark.ts +13 -13
  67. package/composables/fuse.ts +60 -60
  68. package/composables/index.ts +7 -7
  69. package/composables/layout.ts +16 -16
  70. package/composables/outline.ts +49 -49
  71. package/composables/tags.ts +36 -36
  72. package/layouts/archive-month.vue +13 -13
  73. package/layouts/archive-year.vue +13 -13
  74. package/layouts/archives.vue +11 -11
  75. package/layouts/categories.vue +13 -13
  76. package/layouts/default.vue +13 -15
  77. package/layouts/home.vue +33 -33
  78. package/layouts/post.vue +54 -54
  79. package/layouts/tag.vue +10 -10
  80. package/layouts/tags.vue +10 -14
  81. package/library/loading.scss +535 -535
  82. package/library/loading.ts +60 -60
  83. package/library/scroll.ts +22 -22
  84. package/locales/en.yml +1 -1
  85. package/locales/zh-CN.yml +1 -1
  86. package/node/images/default.json +139 -139
  87. package/node/images/index.ts +46 -46
  88. package/node/images/shims.d.ts +8 -8
  89. package/node/index.ts +2 -2
  90. package/node/theme/index.ts +78 -78
  91. package/package.json +1 -1
  92. package/pages/archives/[year]/[month]/index.vue +48 -48
  93. package/pages/archives/[year]/index.vue +73 -73
  94. package/pages/archives/index.md +6 -0
  95. package/pages/categories/[...its].vue +108 -108
  96. package/pages/index.vue +8 -8
  97. package/pages/page/[page].vue +12 -12
  98. package/pages/tags/[tag]/index.vue +38 -38
  99. package/pages/tags/index.md +7 -0
  100. package/setup/main.ts +9 -9
  101. package/store/index.ts +1 -1
  102. package/store/modules/global.ts +12 -12
  103. package/styles/components/aplayer.scss +75 -75
  104. package/styles/components/index.scss +3 -3
  105. package/styles/components/markdown.scss +89 -89
  106. package/styles/components/nprogress.scss +15 -15
  107. package/styles/components/scrollbar.scss +25 -25
  108. package/styles/css-vars.scss +171 -171
  109. package/styles/element-plus/index.scss +1 -1
  110. package/styles/element-plus/tabs.scss +25 -25
  111. package/styles/element-plus/timeline.scss +18 -18
  112. package/styles/font-face.scss +19 -19
  113. package/styles/global.scss +38 -38
  114. package/styles/index.scss +3 -3
  115. package/tsconfig.json +27 -27
  116. package/types/index.d.ts +163 -163
  117. package/unocss.config.ts +43 -43
  118. package/utils/index.ts +37 -37
  119. package/valaxy.config.ts +26 -26
  120. package/pages/archives/index.vue +0 -6
  121. package/pages/tags/index.vue +0 -6
@@ -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,6 +1,6 @@
1
1
  {
2
2
  "name": "valaxy-theme-hairy",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "packageManager": "pnpm@8.10.5",
5
5
  "author": {
6
6
  "email": "wwu710632@gmail.com",
@@ -1,48 +1,48 @@
1
- <script lang="ts" setup>
2
- import { ElTimeline, ElTimelineItem } from 'element-plus/es/components/timeline/index'
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/es/components/timeline/index'
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>
@@ -0,0 +1,6 @@
1
+ ---
2
+ layout: archives
3
+ title: 归档
4
+ nav: false
5
+ comment: false
6
+ ---
@@ -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/es/components/timeline/index'
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>