feat: 添加移动设备下博客列表页 avatar UI交互

This commit is contained in:
pengzhanbo 2023-12-26 21:59:54 +08:00
parent 1ebab7054a
commit 616672fa59
6 changed files with 167 additions and 4 deletions

View File

@ -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>

View 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>

View File

@ -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>

View 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>

View File

@ -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 {

View File

@ -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 {