feat: 添加移动设备下博客列表页 avatar UI交互
This commit is contained in:
parent
1ebab7054a
commit
616672fa59
@ -3,6 +3,7 @@ import { usePageData } from '@vuepress/client'
|
||||
import type { PlumeThemePageData } from '../../shared/index.js'
|
||||
import Archives from './Archives.vue'
|
||||
import BlogAside from './BlogAside.vue'
|
||||
import BlogExtract from './BlogExtract.vue'
|
||||
import PostList from './PostList.vue'
|
||||
import Tags from './Tags.vue'
|
||||
|
||||
@ -15,6 +16,7 @@ const page = usePageData<PlumeThemePageData>()
|
||||
<Tags v-if="page.type === 'blog-tags'" />
|
||||
<Archives v-if="page.type === 'blog-archives'" />
|
||||
<BlogAside />
|
||||
<BlogExtract />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
156
theme/src/client/components/BlogExtract.vue
Normal file
156
theme/src/client/components/BlogExtract.vue
Normal file
@ -0,0 +1,156 @@
|
||||
<script lang="ts" setup>
|
||||
import { computed, ref, watch } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { useBlogExtract, useThemeLocaleData } from '../composables/index.js'
|
||||
import AutoLink from './AutoLink.vue'
|
||||
import IconArchive from './icons/IconArchive.vue'
|
||||
import IconBlogExt from './icons/IconBlogExt.vue'
|
||||
import IconTag from './icons/IconTag.vue'
|
||||
|
||||
|
||||
const theme = useThemeLocaleData()
|
||||
const route = useRoute()
|
||||
|
||||
const avatar = computed(() => theme.value.avatar)
|
||||
const { hasBlogExtract, tags, archives } = useBlogExtract()
|
||||
const open = ref(false)
|
||||
|
||||
watch(() => route.path, () => {
|
||||
open.value = false
|
||||
})
|
||||
|
||||
const showBlogExtract = computed(() => {
|
||||
return avatar.value || hasBlogExtract.value
|
||||
})
|
||||
</script>
|
||||
<template>
|
||||
<div v-if="showBlogExtract" class="blog-extract" @click="open = !open">
|
||||
<IconBlogExt class="icon" />
|
||||
</div>
|
||||
<div v-if="showBlogExtract" class="blog-modal" :class="{ open }" @click.self="open = false">
|
||||
<div class="blog-modal-container">
|
||||
<div v-if="avatar" class="avatar-profile">
|
||||
<p v-if="avatar.url" class="avatar">
|
||||
<img :src="avatar.url" :alt="avatar.name" />
|
||||
</p>
|
||||
<div>
|
||||
<h3>{{ avatar.name }}</h3>
|
||||
<p class="desc">{{ avatar.description }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="hasBlogExtract" class="blog-nav">
|
||||
<AutoLink class="nav-link" :href="tags.link">
|
||||
<IconTag class="icon" />
|
||||
<span>{{ tags.text }}</span>
|
||||
</AutoLink>
|
||||
<AutoLink class="nav-link" :href="archives.link">
|
||||
<IconArchive class="icon" />
|
||||
<span>{{ archives.text }}</span>
|
||||
</AutoLink>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.blog-extract {
|
||||
display: block;
|
||||
position: fixed;
|
||||
right: 0;
|
||||
bottom: 30%;
|
||||
padding: 4px 10px;
|
||||
border-top-left-radius: 99px;
|
||||
border-bottom-left-radius: 99px;
|
||||
border: solid 1px var(--vp-c-divider);
|
||||
border-right: none;
|
||||
box-shadow: var(--vp-shadow-2);
|
||||
z-index: calc(var(--vp-z-index-nav) - 1);
|
||||
background-color: var(--vp-c-bg);
|
||||
}
|
||||
|
||||
.blog-extract .icon {
|
||||
font-size: 16px;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.blog-extract {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.blog-modal {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: var(--vp-z-index-sidebar);
|
||||
width: 100%;
|
||||
opacity: 0;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
transform: translateY(100%);
|
||||
transition: opacity 0.25s, transform 0.5s cubic-bezier(0.19, 1, 0.22, 1);
|
||||
background-color: rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.blog-modal.open {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
transform: translateY(0);
|
||||
transition: opacity 0.25s, transform 0.5s cubic-bezier(0.19, 1, 0.22, 1);
|
||||
}
|
||||
|
||||
.blog-modal-container {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
padding: 24px;
|
||||
background-color: var(--vp-c-bg);
|
||||
border-top-left-radius: 8px;
|
||||
border-top-right-radius: 8px;
|
||||
}
|
||||
|
||||
.avatar-profile {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.avatar-profile .avatar {
|
||||
width: 64px;
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
.avatar-profile h3 {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.avatar-profile .desc {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.blog-nav {
|
||||
padding: 10px 0 0;
|
||||
margin: 24px 0 0;
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
border-top: solid 1px var(--vp-c-divider);
|
||||
}
|
||||
|
||||
.nav-link {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 3px;
|
||||
color: var(--vp-c-brand-1);
|
||||
font-weight: 600;
|
||||
border-radius: 8px;
|
||||
transition: all var(--t-color);
|
||||
}
|
||||
|
||||
.nav-link .icon {
|
||||
margin-right: 4px;
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
}
|
||||
</style>
|
||||
@ -17,11 +17,11 @@ const {
|
||||
<div class="post-list">
|
||||
<PostItem v-for="post in postList" :key="post.path" :post="post" />
|
||||
<div v-if="isPaginationEnabled" class="pagination">
|
||||
<button type="button" class="btn prev" :disabled="isFirstPage" @click="changePage(-1)">
|
||||
<button type="button" class="btn prev" :disabled="isFirstPage" @click="() => changePage(-1)">
|
||||
{{ pagination?.prevPageText || 'Prev' }}
|
||||
</button>
|
||||
<span class="page-info">{{ page }} / {{ totalPage }}</span>
|
||||
<button type="button" class="btn next" :disabled="isLastPage" @click="changePage(1)">
|
||||
<button type="button" class="btn next" :disabled="isLastPage" @click="() => changePage(1)">
|
||||
{{ pagination?.nextPageText || 'Next' }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
3
theme/src/client/components/icons/IconBlogExt.vue
Normal file
3
theme/src/client/components/icons/IconBlogExt.vue
Normal file
@ -0,0 +1,3 @@
|
||||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="currentColor" d="M22 3H5a2 2 0 0 0-2 2v4h2V5h17v14H5v-4H3v4a2 2 0 0 0 2 2h17a2 2 0 0 0 2-2V5a2 2 0 0 0-2-2M7 15v-2H0v-2h7V9l4 3zm13-2h-7v-2h7zm0-4h-7V7h7zm-3 8h-4v-2h4z"/></svg>
|
||||
</template>
|
||||
@ -56,7 +56,7 @@ export const usePostListControl = () => {
|
||||
|
||||
const changePage = (offset: number) => {
|
||||
page.value += offset
|
||||
window.scrollTo({ top: 0, left: 0, behavior: 'smooth' })
|
||||
window.scrollTo({ top: 0, left: 0, behavior: 'instant' })
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
@ -120,11 +120,13 @@
|
||||
text-underline-offset: 2px;
|
||||
transition:
|
||||
color 0.25s,
|
||||
opacity 0.25s;
|
||||
opacity 0.25s,
|
||||
text-underline-offset 0.25s;
|
||||
}
|
||||
|
||||
.plume-content a:hover {
|
||||
color: var(--vp-c-brand-2);
|
||||
text-underline-offset: 4px;
|
||||
}
|
||||
|
||||
.plume-content strong {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user