valaxy-theme-yun 0.19.13 → 0.20.0-beta.1
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/App.vue +30 -4
- package/client/constants/index.ts +13 -0
- package/components/ValaxyMain.vue +48 -52
- package/components/YunAside.vue +66 -43
- package/components/YunBackToTop.vue +11 -4
- package/components/YunBanner.vue +31 -15
- package/components/YunBg.vue +2 -0
- package/components/YunCard.vue +5 -1
- package/components/YunCategories.vue +14 -34
- package/components/YunCategory.vue +42 -11
- package/components/YunClassifyPopover.vue +59 -0
- package/components/YunCloud.vue +5 -10
- package/components/YunConfig.vue +2 -19
- package/components/YunDebug.vue +47 -0
- package/components/YunDock.vue +223 -0
- package/components/YunFooter.vue +56 -4
- package/components/YunFullscreenMenu.vue +57 -0
- package/components/YunGirlItem.vue +98 -0
- package/components/YunGirls.vue +2 -73
- package/components/YunGoDown.vue +8 -15
- package/components/YunLinkItem.vue +62 -0
- package/components/YunLinks.vue +15 -47
- package/components/YunNavMenu.vue +125 -0
- package/components/YunOutline.vue +1 -2
- package/components/YunOverlay.vue +31 -0
- package/components/YunOverview.vue +2 -74
- package/components/YunPageHeader.vue +1 -1
- package/components/YunPagination.vue +105 -0
- package/components/YunPostCard.vue +32 -3
- package/components/YunPostCategories.vue +3 -3
- package/components/YunPostCollapse.vue +7 -72
- package/components/YunPostCollapseItem.vue +137 -0
- package/components/YunPostDateMeta.vue +30 -0
- package/components/YunPostList.vue +6 -11
- package/components/YunPostMeta.vue +31 -39
- package/components/YunPostTags.vue +2 -2
- package/components/YunPostsInfo.vue +41 -0
- package/components/YunPrologue.vue +24 -0
- package/components/YunSearchBtn.vue +8 -6
- package/components/YunSelect.vue +1 -11
- package/components/YunSidebar.vue +7 -4
- package/components/YunSidebarCard.vue +10 -0
- package/components/YunSidebarNav.vue +0 -1
- package/components/YunSiteInfo.vue +72 -0
- package/components/YunSponsor.vue +21 -6
- package/components/animation/LineBurstEffects.vue +75 -0
- package/components/author/YunAuthorAvatar.vue +12 -0
- package/components/author/YunAuthorIntro.vue +11 -0
- package/components/author/YunAuthorName.vue +14 -0
- package/components/config/YunToggleDark.vue +37 -0
- package/components/layout/YunLayoutLeft.vue +12 -0
- package/components/layout/YunLayoutRight.vue +21 -0
- package/components/layout/YunLayoutWrapper.vue +17 -0
- package/components/menu/YunNavMenuItem.vue +22 -0
- package/components/menu/YunNavMenuTitle.vue +86 -0
- package/components/menu/YunPostClassifyNavItem.vue +30 -0
- package/components/page/YunPageHeaderGradient.vue +38 -0
- package/components/project/YunProjectCard.vue +94 -0
- package/components/project/YunProjectCollection.vue +15 -0
- package/components/project/YunProjectLinkItem.vue +60 -0
- package/components/project/YunProjectToggleButton.vue +16 -0
- package/components/project/YunProjects.vue +48 -0
- package/components/prologue/PrologueSocialIcon.vue +58 -0
- package/components/prologue/PrologueSocialLinks.vue +16 -0
- package/components/prologue/PrologueSquare.vue +144 -0
- package/components/prologue/YunAEFrame.vue +155 -0
- package/components/prologue/YunAERect.vue +127 -0
- package/components/site/YunFullscreenMenuItem.vue +26 -0
- package/components/site/YunFullscreenMenuList.vue +19 -0
- package/components/site/YunSiteLinkItem.vue +26 -0
- package/components/site/YunSiteLinks.vue +19 -0
- package/components/site/YunSiteTitle.vue +59 -0
- package/components/third/YunWalineMeta.vue +17 -5
- package/docs/zh-CN/config.md +0 -3
- package/layouts/archives.vue +33 -21
- package/layouts/categories.vue +43 -31
- package/layouts/default.vue +10 -5
- package/layouts/empty.vue +3 -0
- package/layouts/home.vue +12 -5
- package/layouts/post.vue +23 -20
- package/layouts/posts.vue +10 -0
- package/layouts/projects.vue +25 -0
- package/layouts/tags.vue +45 -41
- package/node/config.ts +2 -5
- package/package.json +10 -5
- package/pages/page/[page].vue +3 -6
- package/pages/posts/index.vue +11 -0
- package/setup/main.ts +51 -9
- package/stores/app.ts +25 -3
- package/styles/animations/index.scss +36 -0
- package/styles/common/markdown.scss +4 -0
- package/styles/css-vars.scss +19 -1
- package/styles/global.scss +8 -0
- package/styles/index.scss +1 -0
- package/styles/layout/index.scss +3 -0
- package/styles/modules/prologue.scss +1 -0
- package/styles/primevue/index.ts +7 -0
- package/styles/primevue/tooltip.scss +55 -0
- package/styles/primevue/tooltip.ts +14 -0
- package/styles/vars.scss +23 -2
- package/styles/widgets/banner.scss +26 -6
- package/types/index.d.ts +53 -5
- package/types/projects.ts +48 -0
- package/unocss.config.ts +1 -1
- package/utils/animation.ts +33 -0
- package/utils/index.ts +2 -0
@@ -0,0 +1,59 @@
|
|
1
|
+
<script setup lang="ts">
|
2
|
+
import { useMotion } from '@vueuse/motion'
|
3
|
+
import Popover from 'primevue/popover'
|
4
|
+
import { onMounted, ref } from 'vue'
|
5
|
+
|
6
|
+
const op = ref<typeof Popover>()
|
7
|
+
|
8
|
+
const pContentRef = ref<HTMLElement>()
|
9
|
+
const motion = useMotion(pContentRef, {
|
10
|
+
initial: {
|
11
|
+
opacity: 0,
|
12
|
+
translateY: 30,
|
13
|
+
},
|
14
|
+
enter: {
|
15
|
+
opacity: 1,
|
16
|
+
translateY: 0,
|
17
|
+
transition: {
|
18
|
+
type: 'spring',
|
19
|
+
duration: 200,
|
20
|
+
damping: 9,
|
21
|
+
},
|
22
|
+
},
|
23
|
+
})
|
24
|
+
|
25
|
+
onMounted(() => {
|
26
|
+
motion.variant.value = 'initial'
|
27
|
+
|
28
|
+
// 滚动时隐藏
|
29
|
+
window.addEventListener('scroll', () => {
|
30
|
+
motion.variant.value = 'initial'
|
31
|
+
setTimeout(() => {
|
32
|
+
op.value?.hide()
|
33
|
+
}, 200)
|
34
|
+
})
|
35
|
+
})
|
36
|
+
|
37
|
+
function toggle(event: Event) {
|
38
|
+
op.value?.toggle(event)
|
39
|
+
motion.variant.value = op.value?.visible ? 'enter' : 'initial'
|
40
|
+
}
|
41
|
+
</script>
|
42
|
+
|
43
|
+
<template>
|
44
|
+
<YunNavMenuItem
|
45
|
+
icon="i-ri-mind-map" @click="toggle"
|
46
|
+
/>
|
47
|
+
<Popover
|
48
|
+
ref="op"
|
49
|
+
>
|
50
|
+
<div
|
51
|
+
ref="pContentRef"
|
52
|
+
class="p-4 shadow-xl"
|
53
|
+
bg="$va-c-bg-light"
|
54
|
+
>
|
55
|
+
<YunSiteInfo class="text-center" />
|
56
|
+
<YunPostsInfo />
|
57
|
+
</div>
|
58
|
+
</Popover>
|
59
|
+
</template>
|
package/components/YunCloud.vue
CHANGED
@@ -17,26 +17,21 @@
|
|
17
17
|
<style lang="scss">
|
18
18
|
@use 'valaxy/client/styles/mixins/index.scss' as *;
|
19
19
|
|
20
|
+
.dark .yun-cloud {
|
21
|
+
--yun-c-cloud: var(--va-c-bg-soft);
|
22
|
+
}
|
23
|
+
|
20
24
|
.yun-cloud {
|
21
25
|
display: flex;
|
22
26
|
width: 100%;
|
23
|
-
position: absolute;
|
24
|
-
bottom: 0;
|
25
|
-
left: 0;
|
26
|
-
right: 0;
|
27
27
|
z-index: var(--yun-z-cloud);
|
28
28
|
box-sizing: border-box;
|
29
|
-
mix-blend-mode: overlay;
|
30
29
|
|
31
30
|
.waves {
|
32
31
|
display: flex;
|
33
32
|
position: relative;
|
34
33
|
width: 100%;
|
35
|
-
height:
|
36
|
-
|
37
|
-
@include screen('md') {
|
38
|
-
height: 40px;
|
39
|
-
}
|
34
|
+
height: 40px;
|
40
35
|
}
|
41
36
|
|
42
37
|
.parallax {
|
package/components/YunConfig.vue
CHANGED
@@ -1,27 +1,10 @@
|
|
1
1
|
<script lang="ts" setup>
|
2
|
-
|
3
|
-
import { computed } from 'vue'
|
4
|
-
import { useAppStore } from 'valaxy'
|
5
|
-
|
6
|
-
const appStore = useAppStore()
|
7
|
-
const { t } = useI18n()
|
8
|
-
|
9
|
-
const themeTitle = computed(() => {
|
10
|
-
return appStore.isDark ? t('button.toggle_light') : t('button.toggle_dark')
|
11
|
-
})
|
12
|
-
|
13
|
-
const styles = computed(() => {
|
14
|
-
return {
|
15
|
-
color: appStore.isDark ? '' : '#f1cb64',
|
16
|
-
}
|
17
|
-
})
|
2
|
+
// sidebar config
|
18
3
|
</script>
|
19
4
|
|
20
5
|
<template>
|
21
6
|
<div>
|
22
|
-
<
|
23
|
-
<div i="ri-sun-line dark:ri-moon-line" />
|
24
|
-
</button>
|
7
|
+
<YunToggleDark />
|
25
8
|
|
26
9
|
<YunToggleLocale />
|
27
10
|
</div>
|
@@ -0,0 +1,47 @@
|
|
1
|
+
<script setup lang="ts">
|
2
|
+
import { computed } from 'vue'
|
3
|
+
import { useYunAppStore } from '../stores'
|
4
|
+
|
5
|
+
const yun = useYunAppStore()
|
6
|
+
|
7
|
+
const infoList = computed(() => {
|
8
|
+
return [
|
9
|
+
{
|
10
|
+
label: 'xs',
|
11
|
+
value: yun.size.isXs,
|
12
|
+
},
|
13
|
+
{
|
14
|
+
label: 'sm',
|
15
|
+
value: yun.size.isSm,
|
16
|
+
},
|
17
|
+
{
|
18
|
+
label: 'md',
|
19
|
+
value: yun.size.isMd,
|
20
|
+
},
|
21
|
+
{
|
22
|
+
label: 'lg',
|
23
|
+
value: yun.size.isLg,
|
24
|
+
},
|
25
|
+
{
|
26
|
+
label: 'xl',
|
27
|
+
value: yun.size.isXl,
|
28
|
+
},
|
29
|
+
{
|
30
|
+
label: '2xl',
|
31
|
+
value: yun.size.is2xl,
|
32
|
+
},
|
33
|
+
]
|
34
|
+
})
|
35
|
+
</script>
|
36
|
+
|
37
|
+
<template>
|
38
|
+
<div
|
39
|
+
class="bg-black/50 fixed bottom-2 left-2 p-2 gap-1 rounded z-9999"
|
40
|
+
text="xs white" flex="~ col"
|
41
|
+
>
|
42
|
+
<div v-for="item in infoList" :key="item.label" class="gap-2 inline-flex">
|
43
|
+
<span class="w-6" font="bold">{{ item.label }}: </span>
|
44
|
+
<span>{{ item.value ? '✅' : '❌' }}</span>
|
45
|
+
</div>
|
46
|
+
</div>
|
47
|
+
</template>
|
@@ -0,0 +1,223 @@
|
|
1
|
+
<script setup>
|
2
|
+
import { onBeforeUnmount, onMounted, ref, watch } from 'vue'
|
3
|
+
import { useToast } from 'primevue/usetoast'
|
4
|
+
import TerminalService from 'primevue/terminalservice'
|
5
|
+
import Dock from 'primevue/dock'
|
6
|
+
import Toast from 'primevue/toast'
|
7
|
+
import { useYunAppStore } from '../stores'
|
8
|
+
|
9
|
+
onMounted(() => {
|
10
|
+
TerminalService.on('command', commandHandler)
|
11
|
+
})
|
12
|
+
|
13
|
+
onBeforeUnmount(() => {
|
14
|
+
TerminalService.off('command', commandHandler)
|
15
|
+
})
|
16
|
+
|
17
|
+
const yunApp = useYunAppStore()
|
18
|
+
const showDock = ref(false)
|
19
|
+
watch(() => yunApp.scrollY, () => {
|
20
|
+
if (yunApp.scrollY > 10)
|
21
|
+
showDock.value = true
|
22
|
+
else
|
23
|
+
showDock.value = false
|
24
|
+
})
|
25
|
+
|
26
|
+
const displayFinder = ref(false)
|
27
|
+
const displayTerminal = ref(false)
|
28
|
+
const displayPhotos = ref(false)
|
29
|
+
const toast = useToast()
|
30
|
+
const items = ref([
|
31
|
+
{
|
32
|
+
label: 'Finder',
|
33
|
+
icon: 'https://primefaces.org/cdn/primevue//images/dock/finder.svg',
|
34
|
+
command: () => {
|
35
|
+
displayFinder.value = true
|
36
|
+
},
|
37
|
+
},
|
38
|
+
{
|
39
|
+
label: 'Terminal',
|
40
|
+
icon: 'https://primefaces.org/cdn/primevue//images/dock/terminal.svg',
|
41
|
+
command: () => {
|
42
|
+
displayTerminal.value = true
|
43
|
+
},
|
44
|
+
},
|
45
|
+
{
|
46
|
+
label: 'App Store',
|
47
|
+
icon: 'https://primefaces.org/cdn/primevue//images/dock/appstore.svg',
|
48
|
+
command: () => {
|
49
|
+
toast.add({ severity: 'error', summary: 'An unexpected error occurred while signing in.', detail: 'UNTRUSTED_CERT_TITLE', group: 'tc', life: 3000 })
|
50
|
+
},
|
51
|
+
},
|
52
|
+
{
|
53
|
+
label: 'Safari',
|
54
|
+
icon: 'https://primefaces.org/cdn/primevue//images/dock/safari.svg',
|
55
|
+
command: () => {
|
56
|
+
toast.add({ severity: 'warn', summary: 'Safari has stopped working', group: 'tc', life: 3000 })
|
57
|
+
},
|
58
|
+
},
|
59
|
+
{
|
60
|
+
label: 'Photos',
|
61
|
+
icon: 'https://primefaces.org/cdn/primevue//images/dock/photos.svg',
|
62
|
+
command: () => {
|
63
|
+
displayPhotos.value = true
|
64
|
+
},
|
65
|
+
},
|
66
|
+
{
|
67
|
+
label: 'GitHub',
|
68
|
+
icon: 'https://primefaces.org/cdn/primevue//images/dock/github.svg',
|
69
|
+
},
|
70
|
+
{
|
71
|
+
label: 'Trash',
|
72
|
+
icon: 'https://primefaces.org/cdn/primevue//images/dock/trash.png',
|
73
|
+
command: () => {
|
74
|
+
toast.add({ severity: 'info', summary: 'Empty Trash', life: 3000 })
|
75
|
+
},
|
76
|
+
},
|
77
|
+
])
|
78
|
+
|
79
|
+
function onDockItemClick(event, item) {
|
80
|
+
if (item.command)
|
81
|
+
item.command()
|
82
|
+
|
83
|
+
event.preventDefault()
|
84
|
+
}
|
85
|
+
|
86
|
+
function commandHandler(text) {
|
87
|
+
let response
|
88
|
+
const argsIndex = text.indexOf(' ')
|
89
|
+
const command = argsIndex !== -1 ? text.substring(0, argsIndex) : text
|
90
|
+
|
91
|
+
switch (command) {
|
92
|
+
case 'date':
|
93
|
+
response = `Today is ${new Date().toDateString()}`
|
94
|
+
break
|
95
|
+
|
96
|
+
case 'greet':
|
97
|
+
response = `Hola ${text.substring(argsIndex + 1)}`
|
98
|
+
break
|
99
|
+
|
100
|
+
case 'random':
|
101
|
+
response = Math.floor(Math.random() * 100)
|
102
|
+
break
|
103
|
+
|
104
|
+
default:
|
105
|
+
response = `Unknown command: ${command}`
|
106
|
+
}
|
107
|
+
|
108
|
+
TerminalService.emit('response', response)
|
109
|
+
}
|
110
|
+
</script>
|
111
|
+
|
112
|
+
<template>
|
113
|
+
<Toast position="top-center" group="tc" />
|
114
|
+
|
115
|
+
<!-- yun-dock slide in -->
|
116
|
+
<Transition
|
117
|
+
name="custom-classes"
|
118
|
+
enter-from-class="opacity-0 translate-y-10"
|
119
|
+
enter-to-class="opacity-100 translate-y-0"
|
120
|
+
leave-from-class="opacity-100 translate-y-0"
|
121
|
+
leave-to-class="opacity-0 translate-y-10"
|
122
|
+
>
|
123
|
+
<Dock
|
124
|
+
v-show="showDock"
|
125
|
+
class="fixed bottom-0 left-0 right-0 z-99 transition" flex="~"
|
126
|
+
:model="items"
|
127
|
+
>
|
128
|
+
<template #item="{ item }">
|
129
|
+
<a
|
130
|
+
v-tooltip.top="item.label" href="#"
|
131
|
+
class="yun-dock-item-link" @click="onDockItemClick($event, item)"
|
132
|
+
>
|
133
|
+
<img :alt="item.label" :src="item.icon" style="width: 100%">
|
134
|
+
</a>
|
135
|
+
</template>
|
136
|
+
</Dock>
|
137
|
+
</Transition>
|
138
|
+
|
139
|
+
<!-- <div class="dock-window dock-advanced">
|
140
|
+
<Dialog v-model:visible="displayTerminal" header="Terminal" :breakpoints="{ '960px': '50vw' }" :style="{ width: '40vw' }" :maximizable="true">
|
141
|
+
<Terminal welcome-message="Welcome to PrimeVue(cmd: 'date', 'greet {0}' and 'random')" prompt="primevue $" />
|
142
|
+
</Dialog>
|
143
|
+
|
144
|
+
<Dialog v-model:visible="displayFinder" header="Finder" :breakpoints="{ '960px': '50vw' }" :style="{ width: '40vw' }" :maximizable="true">
|
145
|
+
<Tree :value="nodes" />
|
146
|
+
</Dialog>
|
147
|
+
|
148
|
+
<Galleria v-model:visible="displayPhotos" :value="images" :responsive-options="responsiveOptions" :num-visible="2" container-style="width: 400px" :circular="true" :full-screen="true" :show-thumbnails="false" :show-item-navigators="true">
|
149
|
+
<template #item="slotProps">
|
150
|
+
<img :src="slotProps.item.itemImageSrc" :alt="slotProps.item.alt" style="width: 100%">
|
151
|
+
</template>
|
152
|
+
</Galleria>
|
153
|
+
</div> -->
|
154
|
+
</template>
|
155
|
+
|
156
|
+
<style lang="scss">
|
157
|
+
:root {
|
158
|
+
--p-dock-background: rgb(255 255 255 / 0.1);
|
159
|
+
--p-dock-border-color: rgb(255 255 255 / 0.2);
|
160
|
+
--p-dock-padding: 0.5rem;
|
161
|
+
--p-dock-border-radius: var(--p-border-radius-xl);
|
162
|
+
--p-dock-item-border-radius: var(--p-content-border-radius);
|
163
|
+
--p-dock-item-padding: 0.5rem;
|
164
|
+
--p-dock-item-size: 3rem;
|
165
|
+
--p-dock-item-focus-ring-width: var(--p-focus-ring-width);
|
166
|
+
--p-dock-item-focus-ring-style: var(--p-focus-ring-style);
|
167
|
+
--p-dock-item-focus-ring-color: var(--p-focus-ring-color);
|
168
|
+
--p-dock-item-focus-ring-offset: var(--p-focus-ring-offset);
|
169
|
+
--p-dock-item-focus-ring-shadow: var(--p-focus-ring-shadow);
|
170
|
+
}
|
171
|
+
|
172
|
+
.yun-dock {
|
173
|
+
position: fixed;
|
174
|
+
display: flex;
|
175
|
+
justify-content: center;
|
176
|
+
align-items: center;
|
177
|
+
z-index: var(--yun-z-dock);
|
178
|
+
|
179
|
+
&-bottom {
|
180
|
+
left: 0;
|
181
|
+
bottom: 0;
|
182
|
+
width: 100%;
|
183
|
+
}
|
184
|
+
|
185
|
+
.yun-dock-list-container {
|
186
|
+
display: flex;
|
187
|
+
pointer-events: auto;
|
188
|
+
background: var(--p-dock-background);
|
189
|
+
border: 1px solid var(--p-dock-border-color);
|
190
|
+
padding: var(--p-dock-padding);
|
191
|
+
border-radius: var(--p-dock-border-radius);
|
192
|
+
}
|
193
|
+
}
|
194
|
+
|
195
|
+
.yun-dock-list {
|
196
|
+
margin: 0;
|
197
|
+
padding: 0;
|
198
|
+
list-style: none;
|
199
|
+
display: flex;
|
200
|
+
align-items: center;
|
201
|
+
justify-content: center;
|
202
|
+
outline: 0 none;
|
203
|
+
}
|
204
|
+
|
205
|
+
.yun-dock-item {
|
206
|
+
transition: all 0.2s cubic-bezier(0.4,0,0.2,1);
|
207
|
+
will-change: transform;
|
208
|
+
padding: var(--p-dock-item-padding);
|
209
|
+
border-radius: var(--p-dock-item-border-radius);
|
210
|
+
}
|
211
|
+
|
212
|
+
.yun-dock-item-link {
|
213
|
+
display: flex;
|
214
|
+
flex-direction: column;
|
215
|
+
align-items: center;
|
216
|
+
justify-content: center;
|
217
|
+
position: relative;
|
218
|
+
overflow: hidden;
|
219
|
+
cursor: pointer;
|
220
|
+
width: var(--p-dock-item-size);
|
221
|
+
height: var(--p-dock-item-size);
|
222
|
+
}
|
223
|
+
</style>
|
package/components/YunFooter.vue
CHANGED
@@ -1,10 +1,25 @@
|
|
1
1
|
<script lang="ts" setup>
|
2
2
|
import { capitalize, computed } from 'vue'
|
3
|
-
import { useSiteConfig, useValaxyConfig } from 'valaxy'
|
3
|
+
import { useSiteConfig, useValaxyConfig, useValaxyDark } from 'valaxy'
|
4
4
|
import { useI18n } from 'vue-i18n'
|
5
5
|
import pkg from 'valaxy/package.json'
|
6
6
|
import { useThemeConfig } from '../composables'
|
7
7
|
|
8
|
+
// background-image: linear-gradient(120deg, #a1c4fd 0%, #c2e9fb 100%);
|
9
|
+
const { isDark } = useValaxyDark()
|
10
|
+
const gradientStyles = computed(() => {
|
11
|
+
if (isDark.value) {
|
12
|
+
return {
|
13
|
+
'--gradient-from': '0 0 0',
|
14
|
+
'--gradient-to': '0 0 0',
|
15
|
+
}
|
16
|
+
}
|
17
|
+
return {
|
18
|
+
'--gradient-from': '161 196 253',
|
19
|
+
'--gradient-to': '194 233 251',
|
20
|
+
}
|
21
|
+
})
|
22
|
+
|
8
23
|
const { t } = useI18n()
|
9
24
|
const config = useValaxyConfig()
|
10
25
|
const siteConfig = useSiteConfig()
|
@@ -15,7 +30,7 @@ const isThisYear = computed(() => {
|
|
15
30
|
return year === themeConfig.value.footer.since
|
16
31
|
})
|
17
32
|
|
18
|
-
const poweredHtml = computed(() => t('footer.powered', [`<a href="${pkg.repository.url}" target="_blank" rel="noopener">Valaxy</a> v${pkg.version}
|
33
|
+
const poweredHtml = computed(() => t('footer.powered', [`<a href="${pkg.repository.url}" target="_blank" rel="noopener">Valaxy</a> <span class="op-60">v${pkg.version}</span>`]))
|
19
34
|
const footerIcon = computed(() => themeConfig.value.footer.icon || {
|
20
35
|
url: pkg.repository.url,
|
21
36
|
name: 'i-ri-cloud-line',
|
@@ -24,7 +39,14 @@ const footerIcon = computed(() => themeConfig.value.footer.icon || {
|
|
24
39
|
</script>
|
25
40
|
|
26
41
|
<template>
|
27
|
-
<footer
|
42
|
+
<footer
|
43
|
+
flex="~ col"
|
44
|
+
class="relative yun-footer va-footer px-4 py-4 pt-0 text-$va-c-text-light w-full mt-14"
|
45
|
+
bg="white dark:$va-c-bg-soft"
|
46
|
+
text="center sm"
|
47
|
+
>
|
48
|
+
<YunCloud class="absolute top--10 left-0 right-0" />
|
49
|
+
|
28
50
|
<div v-if="themeConfig.footer.beian?.enable && themeConfig.footer.beian.icp" class="beian" m="y-2">
|
29
51
|
<a href="https://beian.miit.gov.cn/" target="_blank" rel="noopener">
|
30
52
|
{{ themeConfig.footer.beian.icp }}
|
@@ -54,9 +76,39 @@ const footerIcon = computed(() => themeConfig.value.footer.icon || {
|
|
54
76
|
</div>
|
55
77
|
|
56
78
|
<div v-if="themeConfig.footer.powered" class="powered" m="2">
|
57
|
-
<span v-html="poweredHtml" />
|
79
|
+
<span v-html="poweredHtml" />
|
80
|
+
<span mx-1>|</span>
|
81
|
+
<span>
|
82
|
+
<span>{{ t('footer.theme') }}</span>
|
83
|
+
<span mx-1>-</span>
|
84
|
+
<a :href="themeConfig.pkg.repository.url" :title="themeConfig.pkg.name" target="_blank">{{ capitalize(config.theme) }}</a>
|
85
|
+
<span class="ml-1 op-60">v{{ themeConfig.pkg.version }}</span>
|
86
|
+
</span>
|
58
87
|
</div>
|
59
88
|
|
60
89
|
<slot />
|
90
|
+
|
91
|
+
<div class="yun-footer-gradient" :style="gradientStyles" />
|
61
92
|
</footer>
|
62
93
|
</template>
|
94
|
+
|
95
|
+
<style lang="scss">
|
96
|
+
.yun-footer {
|
97
|
+
letter-spacing: 0.05rem;
|
98
|
+
line-height: 1.8;
|
99
|
+
}
|
100
|
+
|
101
|
+
.yun-footer-gradient {
|
102
|
+
position: absolute;
|
103
|
+
bottom: 0;
|
104
|
+
left: 0;
|
105
|
+
right: 0;
|
106
|
+
width: 100%;
|
107
|
+
height: 300px;
|
108
|
+
z-index: 999;
|
109
|
+
pointer-events: none;
|
110
|
+
background: linear-gradient(to right,rgb(var(--gradient-from) / 0.2) 0,rgb(var(--gradient-to) / .2) 100%);
|
111
|
+
mask-image: linear-gradient(#fff0,#000 70%);
|
112
|
+
animation: fade-in 2s;
|
113
|
+
}
|
114
|
+
</style>
|
@@ -0,0 +1,57 @@
|
|
1
|
+
<script setup lang="ts">
|
2
|
+
import { ref } from 'vue'
|
3
|
+
import { useYunAppStore } from '../stores'
|
4
|
+
|
5
|
+
const yunApp = useYunAppStore()
|
6
|
+
const fullscreenMenuRef = ref<HTMLElement>()
|
7
|
+
</script>
|
8
|
+
|
9
|
+
<template>
|
10
|
+
<Transition name="slide-down">
|
11
|
+
<div
|
12
|
+
v-if="yunApp.fullscreenMenu.isOpen"
|
13
|
+
ref="fullscreenMenuRef"
|
14
|
+
p="t-20 md:t-26"
|
15
|
+
class="yun-fullscreen-menu fixed left-0 right-0 bottom-0 top-0 bg-$va-c-bg-soft z-$yun-z-fullscreen-menu overflow-auto"
|
16
|
+
>
|
17
|
+
<div class="flex-center gap-2">
|
18
|
+
<YunToggleDark transition />
|
19
|
+
<YunToggleLocale />
|
20
|
+
</div>
|
21
|
+
|
22
|
+
<YunFullscreenMenuList>
|
23
|
+
<YunFullscreenMenuItem
|
24
|
+
:page="{
|
25
|
+
name: '站点主页',
|
26
|
+
icon: 'i-ri-home-2-line',
|
27
|
+
url: '/',
|
28
|
+
}"
|
29
|
+
/>
|
30
|
+
</YunFullscreenMenuList>
|
31
|
+
|
32
|
+
<div v-if="!yunApp.size.isLg" class="mt-4">
|
33
|
+
<YunSiteInfo class="text-center" />
|
34
|
+
<YunPostsInfo />
|
35
|
+
</div>
|
36
|
+
</div>
|
37
|
+
</Transition>
|
38
|
+
</template>
|
39
|
+
|
40
|
+
<style lang="scss">
|
41
|
+
@use 'sass:map';
|
42
|
+
@use 'valaxy-theme-yun/styles/vars.scss' as *;
|
43
|
+
|
44
|
+
.slide-down-enter-active,
|
45
|
+
.slide-down-leave-active {
|
46
|
+
opacity: 1;
|
47
|
+
transition: transform 0.4s map.get($cubic-bezier, 'ease-in-out'),
|
48
|
+
opacity 0.2s map.get($cubic-bezier, 'ease-in-out');
|
49
|
+
transform: translateY(0);
|
50
|
+
}
|
51
|
+
|
52
|
+
.slide-down-enter-from,
|
53
|
+
.slide-down-leave-to {
|
54
|
+
opacity: 0;
|
55
|
+
transform: translateY(-100%);
|
56
|
+
}
|
57
|
+
</style>
|
@@ -0,0 +1,98 @@
|
|
1
|
+
<script setup lang="ts">
|
2
|
+
import { ref } from 'vue'
|
3
|
+
import { useMotion } from '@vueuse/motion'
|
4
|
+
import { onImgError } from '../utils'
|
5
|
+
import type { GirlType } from '../types'
|
6
|
+
|
7
|
+
const props = defineProps<{
|
8
|
+
i: number
|
9
|
+
girl: GirlType
|
10
|
+
}>()
|
11
|
+
|
12
|
+
const itemRef = ref()
|
13
|
+
useMotion(itemRef, {
|
14
|
+
initial: {
|
15
|
+
opacity: 0,
|
16
|
+
translateY: 40,
|
17
|
+
},
|
18
|
+
enter: {
|
19
|
+
opacity: 1,
|
20
|
+
translateY: 0,
|
21
|
+
transition: {
|
22
|
+
type: 'spring',
|
23
|
+
duration: 400,
|
24
|
+
damping: 8,
|
25
|
+
delay: props.i * 50,
|
26
|
+
},
|
27
|
+
},
|
28
|
+
})
|
29
|
+
</script>
|
30
|
+
|
31
|
+
<template>
|
32
|
+
<li ref="itemRef" class="girl-item">
|
33
|
+
<a
|
34
|
+
class="girl-item-link"
|
35
|
+
:href="girl.url || `https://zh.moegirl.org/${girl.name}`"
|
36
|
+
:title="girl.reason" alt="portrait" target="_blank" rel="noopener"
|
37
|
+
>
|
38
|
+
<figure class="girl-info">
|
39
|
+
<img class="girl-avatar" loading="lazy" :src="girl.avatar" :alt="girl.name" :onError="onImgError">
|
40
|
+
<figcaption class="girl-name" :title="(i + 1).toString()">{{ girl.name }}</figcaption>
|
41
|
+
<figcaption class="girl-from">{{ girl.from }}</figcaption>
|
42
|
+
</figure>
|
43
|
+
</a>
|
44
|
+
</li>
|
45
|
+
</template>
|
46
|
+
|
47
|
+
<style lang="scss">
|
48
|
+
.girl-item {
|
49
|
+
display: inline-flex;
|
50
|
+
text-align: center;
|
51
|
+
justify-content: center;
|
52
|
+
width: 8rem;
|
53
|
+
margin: 1rem;
|
54
|
+
|
55
|
+
.girl {
|
56
|
+
&-info {
|
57
|
+
width: 100%;
|
58
|
+
padding: 0;
|
59
|
+
margin: 0;
|
60
|
+
}
|
61
|
+
|
62
|
+
&-avatar {
|
63
|
+
object-fit: cover;
|
64
|
+
object-position: center top;
|
65
|
+
width: 4rem;
|
66
|
+
height: 4rem;
|
67
|
+
border-radius: 50%;
|
68
|
+
padding: 0.2rem;
|
69
|
+
background-color: #fff;
|
70
|
+
box-shadow: 0 0 1rem rgb(0 0 0 / 0.12);
|
71
|
+
transition: 0.5s;
|
72
|
+
|
73
|
+
&:hover {
|
74
|
+
box-shadow: 0 0 2rem rgb(0 0 0 / 0.12);
|
75
|
+
}
|
76
|
+
}
|
77
|
+
|
78
|
+
&-name {
|
79
|
+
font-size: 0.9rem;
|
80
|
+
}
|
81
|
+
|
82
|
+
&-from {
|
83
|
+
font-size: 12px;
|
84
|
+
font-family: var(--va-font-serif);
|
85
|
+
font-weight: bold;
|
86
|
+
color: var(--va-c-text-light);
|
87
|
+
|
88
|
+
&::before {
|
89
|
+
content: '「';
|
90
|
+
}
|
91
|
+
|
92
|
+
&::after {
|
93
|
+
content: '」';
|
94
|
+
}
|
95
|
+
}
|
96
|
+
}
|
97
|
+
}
|
98
|
+
</style>
|