性能、逻辑、结构优化

This commit is contained in:
Han 2025-04-02 19:14:32 +08:00
parent 7dab27d0a3
commit f333964d10
50 changed files with 1674 additions and 1259 deletions

View File

@ -1,138 +1,134 @@
section.vh-container {
&>section.vh-archive-main {
flex: 1;
padding: 2rem 1rem;
border-radius: 1rem;
background-color: #fff;
overflow: hidden;
section.vh-archive-main {
flex: 1;
padding: 2rem 1rem;
border-radius: 1rem;
background-color: var(--vh-white-color);
overflow: hidden;
&>.archive-list {
&>.archive-list {
display: flex;
flex-direction: column;
gap: 1rem;
&>.archive-list-item {
display: flex;
flex-direction: column;
gap: 1rem;
&>.archive-list-item {
&>p,
a {
display: flex;
flex-direction: column;
&>p,
a {
display: flex;
align-items: center;
width: 100%;
height: 2.5rem;
border-radius: 1rem;
font-size: var(--vh-size-p);
transition: background .16s;
align-items: center;
width: 100%;
height: 2.5rem;
border-radius: 1rem;
font-size: 1rem;
transition: background .18s;
&:not(.title) {
&:hover {
background-color: var(--vh-main-color);
&>i {
&::before {
height: 50.66%;
}
}
&>span {
padding-left: 0.28rem;
}
}
&>em {
color: var(--vh-black-38);
font-size: var(--vh-size-span);
font-weight: 500;
}
&:not(.title) {
&:hover {
background-color: #EEEFF3;
&>i {
&::before {
width: 0.25rem;
height: 0.25rem;
border: none;
border-radius: 0.25rem;
background-color: #4E667F;
}
&::after {
content: "";
position: absolute;
width: 0.125rem;
height: 1rem;
left: calc(50% - 0.0625rem);
transform: translateY(-50%);
border-left: 2px dashed rgba(0, 0, 0, .1);
pointer-events: none;
height: 50.66%;
}
}
&>span {
font-size: var(--vh-size-p);
color: var(--vh-black-100);
font-weight: 600;
padding-left: 0.28rem;
}
}
&>em {
flex-shrink: 0;
width: 5.05rem;
height: 3.75rem;
color: var(--vh-black-100);
font-size: 1.5rem;
font-weight: 600;
line-height: 3.75rem;
text-align: right;
font-style: normal;
color: var(--vh-font-28);
font-size: 0.875rem;
font-weight: 500;
}
&>i {
position: relative;
width: 2.5rem;
height: 100%;
&::before {
content: '';
width: 0.25rem;
height: 0.25rem;
border: none;
border-radius: 0.25rem;
background-color: #4E667F;
}
&::after {
content: "";
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
display: block;
width: 0.36rem;
height: 0.36rem;
border: solid 0.28rem #0000006D;
border-radius: 50%;
background-color: #fff;
transition: all .3s;
width: 0.125rem;
height: 1rem;
left: calc(50% - 0.0625rem);
transform: translateY(-50%);
border-left: 2px dashed rgba(0, 0, 0, .1);
pointer-events: none;
}
}
&>span {
flex: 1;
white-space: nowrap;
overflow: hidden;
color: var(--vh-black-66);
font-weight: 500;
text-overflow: ellipsis;
transition: all .16s;
}
&>cite {
margin-left: auto;
width: 7.8rem;
font-size: var(--vh-size-span);
color: var(--vh-black-66);
font-weight: 400;
font-style: normal;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
&>span {
font-size: 1rem;
color: var(--vh-font-color);
font-weight: 600;
}
}
&>em {
flex-shrink: 0;
width: 5.05rem;
height: 3.75rem;
color: var(--vh-font-color);
font-size: 1.5rem;
font-weight: 600;
line-height: 3.75rem;
text-align: right;
font-style: normal;
}
&>i {
position: relative;
width: 2.5rem;
height: 100%;
&::before {
content: '';
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
display: block;
width: 0.36rem;
height: 0.36rem;
border: solid 0.28rem #0000006D;
border-radius: 50%;
background-color: var(--vh-white-color);
transition: all .3s;
}
}
&>span {
flex: 1;
white-space: nowrap;
overflow: hidden;
color: var(--vh-font-66);
font-weight: 500;
text-overflow: ellipsis;
transition: all .18s;
}
&>cite {
margin-left: auto;
width: 7.8rem;
font-size: 0.875rem;
color: var(--vh-font-66);
font-weight: 400;
font-style: normal;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}
}
}
@ -140,18 +136,15 @@ section.vh-container {
@media screen and (max-width: 1198px) {
section.vh-container {
&>section.vh-archive-main {
&>.archive-list {
&>.archive-list-item {
section.vh-archive-main {
&>.archive-list {
&>.archive-list-item {
&>p,
a {
&>cite {
display: none;
}
&>p,
a {
&>cite {
display: none;
}
}
}
@ -160,40 +153,37 @@ section.vh-container {
}
@media screen and (max-width: 888px) {
section.vh-container {
&>section.vh-archive-main {
padding: 0;
background-color: transparent;
section.vh-archive-main {
padding: 0;
background-color: transparent;
&>.archive-list {
&>.archive-list-item {
&>p,
a {
&:not(.title) {
&>em {
font-size: var(--vh-size-h3);
}
&>span {
font-size: var(--vh-size-h2);
}
}
&>.archive-list {
&>.archive-list-item {
&>p,
a {
&:not(.title) {
&>em {
width: 3rem;
font-size: 1.2rem;
}
&>i {
width: 1.8rem;
font-size: 0.8rem;
}
&>span {
font-size: var(--vh-size-h3);
font-size: 0.88rem;
}
}
&>em {
width: 3rem;
font-size: 1.2rem;
}
&>i {
width: 1.8rem;
}
&>span {
font-size: 0.8rem;
}
}
}
}

View File

@ -13,13 +13,13 @@ import "./ArticleCard.less";
<article class="vh-article-item vh-animation vh-animation-init">
<a class={`vh-article-link${post.data.top ? " active" : ""}`} href={`/article/${post.data.id}`}>
<section class="vh-article-banner"><Image src="/assets/images/lazy-loading.webp" data-vh-lz-src={ARTICLE_COVER} alt={post.data.title} width="10" height="10" /></section>
<section class="vh-article-banner"><Image src="/assets/images/lazy-loading.webp" data-vh-lz-src={ARTICLE_COVER} alt={post.data.title} width="1" height="1" /></section>
<section class="vh-article-desc">
<header>
<span class={`vh-article-categories vh-categories-${post.data.categories}`}>{post.data.categories}</span>
<h2 class="title">{post.data.title}</h2>
<span class={`vh-article-cat vh-cat-${post.data.categories}`}>{post.data.categories}</span>
<h2 class="title vh-ellipsis">{post.data.title}</h2>
</header>
<p class="vh-article-excerpt">{description}</p>
<p class="vh-article-excerpt vh-ellipsis-2">{description}</p>
<footer>{fmtTime(post.data.date)}</footer>
</section>
</a>

View File

@ -1,226 +1,222 @@
section.article-list {
flex: 1;
.article-list-main {
position: relative;
padding-bottom: 6rem;
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 1.6rem;
box-sizing: border-box;
padding-bottom: 6.88rem;
width: 100%;
min-width: 0;
height: max-content;
overflow: hidden;
&>.vh-article-item {
box-sizing: border-box;
&>section.article-list {
position: relative;
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 1.288rem;
width: 100%;
height: initial;
min-height: max-content;
border: solid 1px #eee;
border-radius: 1rem;
background-color: #fff;
min-width: 0;
height: max-content;
overflow: hidden;
&>a.vh-article-link {
position: relative;
&>.vh-article-item {
box-sizing: border-box;
display: flex;
flex-direction: column;
width: 100%;
height: max-content;
min-height: 100%;
border-radius: 0.618rem;
height: initial;
min-height: max-content;
border: solid 1px #eee;
border-radius: 1rem;
background-color: #fff;
overflow: hidden;
transition: background 0.16s;
&:hover {
&>.vh-article-banner {
&>img {
transform: scale(1.08);
}
}
}
&.active {
&::before {
content: "置顶";
position: absolute;
top: 1.6rem;
left: 0.66rem;
box-sizing: border-box;
padding: 0.18rem 1rem;
width: max-content;
height: max-content;
font-size: var(--vh-size-p);
color: #fff;
border-radius: 0.66rem;
transform: rotate(-45deg);
/* 确保元素有半透明背景 */
background: #00000033;
box-shadow: 0 2rem 1rem rgba(0, 0, 0, 0.1);
z-index: 1;
/* 高斯模糊效果 */
backdrop-filter: blur(6px);
-webkit-backdrop-filter: blur(6px);
/* 兼容旧版浏览器 */
}
&>.vh-article-banner {
&::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.06);
z-index: 1;
pointer-events: none;
}
}
}
&>.vh-article-banner {
&>a.vh-article-link {
position: relative;
flex-shrink: 0;
box-sizing: border-box;
width: 100%;
height: 12rem;
overflow: hidden;
&>img {
width: 100%;
height: 100%;
object-fit: cover;
// transition公共区域有动画效果
}
}
&>.vh-article-desc {
flex: 1;
box-sizing: border-box;
padding: 1.188rem;
display: flex;
flex-direction: column;
width: 100%;
height: 100%;
min-height: max-content;
height: max-content;
min-height: 100%;
border-radius: 0.618rem;
overflow: hidden;
transition: background 0.18s;
&>header {
&:hover {
&>.vh-article-banner {
&>img {
transform: scale(1.08);
}
}
}
&.active {
&::before {
content: "置顶";
position: absolute;
top: 1.6rem;
left: 0.66rem;
box-sizing: border-box;
padding: 0.18rem 1rem;
width: max-content;
height: max-content;
font-size: 1rem;
color: #fff;
border-radius: 0.66rem;
transform: rotate(-45deg);
/* 确保元素有半透明背景 */
background: #00000033;
box-shadow: 0 2rem 1rem rgba(0, 0, 0, 0.1);
z-index: 1;
/* 高斯模糊效果 */
backdrop-filter: blur(6px);
-webkit-backdrop-filter: blur(6px);
/* 兼容旧版浏览器 */
}
&>.vh-article-banner {
&::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.06);
z-index: 1;
pointer-events: none;
}
}
}
&>.vh-article-banner {
flex-shrink: 0;
position: relative;
box-sizing: border-box;
width: 100%;
height: 12rem;
overflow: hidden;
&>img {
width: 100%;
height: 100%;
object-fit: cover;
}
}
&>.vh-article-desc {
flex: 1;
box-sizing: border-box;
padding: 1.188rem;
display: flex;
flex-direction: column;
gap: 0.36rem;
&>.vh-article-categories {
box-sizing: border-box;
width: max-content;
font-size: var(--vh-size-span);
font-weight: 700;
/* 默认渐变色起始 */
--gradient-color-1: #a32cc4;
/* 默认渐变色中间 */
--gradient-color-2: #b65fcf;
/* 默认渐变色结束 */
--gradient-color-3: #e39ff6;
background: linear-gradient(118deg, var(--gradient-color-1), var(--gradient-color-2) 26%, var(--gradient-color-3));
background-clip: text;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
color: transparent;
&.vh-categories-Code {
/* 默认渐变色起始 */
--gradient-color-1: #f48600;
/* 默认渐变色中间 */
--gradient-color-2: #ffa12e;
/* 默认渐变色结束 */
--gradient-color-3: #ffe9c9;
}
&.vh-categories-Daily {
/* 默认渐变色起始 */
--gradient-color-1: #6270ee;
/* 默认渐变色中间 */
--gradient-color-2: #2b90d9;
/* 默认渐变色结束 */
--gradient-color-3: #37dede;
}
&.vh-categories-Software {
/* 默认渐变色起始 */
--gradient-color-1: #006633;
/* 默认渐变色中间 */
--gradient-color-2: #00cc66;
/* 默认渐变色结束 */
--gradient-color-3: #66ff99;
}
&.vh-categories-Share {
/* 默认渐变色起始 */
--gradient-color-1: rgb(240, 45, 133);
/* 默认渐变色中间 */
--gradient-color-2: #f95ba5;
/* 默认渐变色结束 */
--gradient-color-3: #f4bdd0;
}
}
&>.title {
margin: 0;
font-size: var(--vh-size-h1);
font-weight: 600;
line-height: 1.36;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
text-overflow: ellipsis;
word-break: break-all;
overflow: hidden;
}
}
&>.vh-article-excerpt {
margin: 0.88rem 0;
width: 100%;
height: max-content;
color: var(--vh-black-66);
font-size: var(--vh-size-small);
letter-spacing: 0.88px;
line-height: 1.28rem;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
text-overflow: ellipsis;
word-break: break-all;
height: 100%;
min-height: max-content;
overflow: hidden;
}
&>footer {
margin-top: auto;
font-size: var(--vh-size-mini);
color: var(--vh-black-100);
&>header {
display: flex;
flex-direction: column;
gap: 0.36rem;
&>.vh-article-cat {
box-sizing: border-box;
width: max-content;
font-size: 0.875rem;
font-weight: 700;
/* 默认渐变色起始 */
--gradient-color-1: #a32cc4;
/* 默认渐变色中间 */
--gradient-color-2: #b65fcf;
/* 默认渐变色结束 */
--gradient-color-3: #e39ff6;
background: linear-gradient(118deg, var(--gradient-color-1), var(--gradient-color-2) 26%, var(--gradient-color-3));
background-clip: text;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
color: transparent;
&.vh-cat-Code {
/* 默认渐变色起始 */
--gradient-color-1: #f48600;
/* 默认渐变色中间 */
--gradient-color-2: #ffa12e;
/* 默认渐变色结束 */
--gradient-color-3: #ffe9c9;
}
&.vh-cat-Daily {
/* 默认渐变色起始 */
--gradient-color-1: #6270ee;
/* 默认渐变色中间 */
--gradient-color-2: #2b90d9;
/* 默认渐变色结束 */
--gradient-color-3: #37dede;
}
&.vh-cat-Software {
/* 默认渐变色起始 */
--gradient-color-1: #006633;
/* 默认渐变色中间 */
--gradient-color-2: #00cc66;
/* 默认渐变色结束 */
--gradient-color-3: #66ff99;
}
&.vh-cat-Share {
/* 默认渐变色起始 */
--gradient-color-1: rgb(240, 45, 133);
/* 默认渐变色中间 */
--gradient-color-2: #f95ba5;
/* 默认渐变色结束 */
--gradient-color-3: #f4bdd0;
}
}
&>.title {
font-size: 1.06rem;
font-weight: 600;
line-height: 1.36;
}
}
&>.vh-article-excerpt {
margin: 0.88rem 0;
width: 100%;
height: max-content;
color: var(--vh-font-66);
font-size: 0.805rem;
letter-spacing: 0.88px;
line-height: 1.28rem;
}
&>footer {
margin-top: auto;
font-size: 0.805rem;
color: var(--vh-font-color);
}
}
}
}
}
&>.vh-article-pagination {
position: absolute;
bottom: 0;
right: 0;
&>.vh-article-pagination {
position: absolute;
bottom: 0;
right: 0;
}
}
}
@media screen and (max-width: 1150px) {
section.article-list {
grid-template-columns: repeat(2, 1fr);
.article-list-main {
&>section.article-list {
grid-template-columns: repeat(2, 1fr);
}
}
}
@media screen and (max-width: 556px) {
section.article-list {
grid-template-columns: repeat(1, 1fr);
.article-list-main {
&>section.article-list {
grid-template-columns: repeat(1, 1fr);
}
}
}

View File

@ -7,13 +7,16 @@ import { Image } from "astro:assets";
import { fmtTime } from "@/utils/index";
// 获取用户配置数据
import SITE_CONFIG from "@/config";
const { Avatar, Author, Motto, WebSites, GoogleAds } = SITE_CONFIG;
const { Avatar, Author, Motto, WebSites, GoogleAds, AsideShow } = SITE_CONFIG;
// 获取文章数据
import { getCategories, getTags, getRecommendArticles } from "@/utils/getPostInfo";
import { getCategories, getTags, getRecommendArticles, getCountInfo } from "@/utils/getPostInfo";
// 获取数据统计
// 分类列表
const categories = getCategories();
// 热门标签
const tags = getTags();
const tags = getTags().slice(0, 16);
// 获取网站统计数据
const CountInfo = getCountInfo();
// 最新文章
const recommendArticles = getRecommendArticles();
// Google 广告组件
@ -24,68 +27,107 @@ import "./Aside.less";
<aside class="vh-aside">
<!-- 头像块 -->
<section class="vh-aside-item user">
<Image class="vh-aside-avatar" src="/assets/images/lazy-loading.webp" data-vh-lz-src={Avatar} alt={Author} width="1" height="1" />
<span class="vh-aside-auther">{Author}</span>
<p class="vh-aside-motto">{Motto}</p>
<section class="vh-aside-links">
{
WebSites.map(item => (
<a class="vh-aside-links-item" href={item.link} title={item.text} target="_blank" rel="noopener nofollow">
<Svg src={item.icon} />
</a>
))
}
</section>
</section>
{
AsideShow.WebSitesShow && (
<section class="vh-aside-item user">
<Image class="vh-aside-avatar" src="/assets/images/lazy-loading.webp" data-vh-lz-src={Avatar} alt={Author} width="1" height="1" />
<span class="vh-aside-auther">{Author}</span>
<p class="vh-aside-motto">{Motto}</p>
<section class="vh-aside-links">
{WebSites.map(item => (
<a class="vh-aside-links-item" href={item.link} title={item.text} target="_blank" rel="noopener nofollow">
<Svg src={item.icon} />
</a>
))}
</section>
<section class="vh-aside-info">
<div class="art-item count">
<span>{CountInfo.ArticleCount}</span>
<p>文章数</p>
</div>
<div class="cat-item count">
<span>{CountInfo.CategoryCount}</span>
<p>分类数</p>
</div>
<div class="tag-item count">
<span>{CountInfo.TagCount}</span>
<p>标签数</p>
</div>
</section>
<canvas class="vh-aside-canvas" width="888" height="1888" />
</section>
)
}
<!-- 公告块 -->
{
AsideShow.TipsShow && (
<section class="vh-aside-item tips">
<span>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M3 9.662c2 2.338 2 4.338 0 6.338c3 .5 4.5 1 5 4c2 -3 6 -4 9 0c0 -3 1 -4 4 -4.004q -3 -2.995 0 -5.996c-3 0 -5 -2 -5 -5c-2 4 -5 3 -7.5 -1c-.5 3 -2.5 5 -5.5 5.662" />
</svg>
公告
</span>
<div class="tips-content">
<p>欢迎访问我的博客,我是一名前端工程师,喜欢分享前端知识和技巧。</p>
<p>如果你对我的博客感兴趣,欢迎关注我的公众号,我会定期发布一些前端技术文章和教程。</p>
</div>
</section>
)
}
<!-- 分类块 -->
<section class="vh-aside-item categories">
<h3>分类</h3>
<div class="vh-aside-categories">
{
categories
.sort((a: any, b: any) => b.count - a.count)
.map(i => (
<a href={`/categories/${i.title}`}>
<span>{i.title}</span>
<i>{i.count}</i>
</a>
))
}
</div>
</section>
{
AsideShow.CategoriesShow && (
<section class="vh-aside-item cat">
<h3>分类</h3>
<div class="vh-aside-cat">
{categories
.sort((a: any, b: any) => b.count - a.count)
.map(i => (
<a href={`/categories/${i.title}`}>
<span>{i.title}</span>
<i>{i.count}</i>
</a>
))}
</div>
</section>
)
}
<!-- 标签块 -->
<section class="vh-aside-item tags">
<h3>热门标签</h3>
<div class="vh-aside-tags">
{
tags.map(i => (
<a href={`/tag/${i}`}>
<span>{i}</span>
</a>
))
}
</div>
</section>
{
AsideShow.TagsShow && (
<section class="vh-aside-item tags">
<h3>热门标签</h3>
<div class="vh-aside-tags">
{tags.map(i => (
<a href={`/tag/${i[0]}`}>
<span>{i[0]}</span>
<em>{i[1]}</em>
</a>
))}
</div>
</section>
)
}
<section class="sticky-aside">
<!-- 最新文章块 -->
{
recommendArticles.length && (
recommendArticles.length && AsideShow.recommendArticleShow && (
<section class="vh-aside-item articles">
<h3>推荐文章</h3>
<div class="vh-aside-articles">
{recommendArticles.map(async i => (
<a href={`/article/${(await i).id}`}>
<p class="cover">
<Image src="/assets/images/lazy-loading.webp" data-vh-lz-src={(await i).cover} alt={(await i).title} width="1" height="1" />
</p>
<p class="info">
<span>{(await i).title}</span>
<time>{fmtTime((await i).date, "YYYY-MM-DD A")}</time>
</p>
{recommendArticles.map((i, idx) => (
<a href={`/article/${i.id}`}>
<span>
{idx < 3 ? <i>{idx + 1}</i> : <em>{idx + 1}.</em>}
<cite class="vh-ellipsis">{i.title}</cite>
</span>
<time>{fmtTime(i.date, "YYYY-MM-DD A")}</time>
</a>
))}
</div>

View File

@ -1,26 +1,29 @@
aside.vh-aside {
flex-shrink: 0;
position: relative;
box-sizing: border-box;
padding: 0.88rem 0.66rem;
display: flex;
flex-direction: column;
gap: 1rem;
width: 288px;
height: initial;
width: var(--vh-aside-width);
height: auto;
&>.sticky-aside {
position: -webkit-sticky;
position: sticky;
top: var(--vh-padding-top);
top: calc(66px + 1rem);
box-sizing: border-box;
display: flex;
flex-direction: column;
gap: 1rem;
width: 100%;
height: max-content;
}
.vh-aside-item {
position: relative;
box-sizing: border-box;
padding: 0.75rem;
padding: 1.28rem;
display: flex;
flex-direction: column;
align-items: center;
@ -28,28 +31,16 @@ aside.vh-aside {
width: 100%;
max-width: 100%;
height: max-content;
border-radius: 1rem;
background-color: #fff;
color: #000;
box-shadow: 0 3px 8px 6px rgba(7, 17, 27, 0.05);
border-radius: var(--vh-main-radius);
background-color: var(--vh-white-color);
box-shadow: var(--vh-box-shadow);
overflow: hidden;
&>h3 {
padding-bottom: 0.618rem;
display: flex;
align-items: center;
gap: 0.68rem;
width: 100%;
font-size: var(--vh-size-h1);
font-weight: 600;
&::before {
content: '';
width: 0.25rem;
height: 1.16rem;
border-radius: 0.5rem;
background-color: var(--vh-main-color-88);
}
font-size: 0.68rem;
font-weight: 400;
color: var(--vh-font-56);
}
// 用户信息
@ -84,7 +75,7 @@ aside.vh-aside {
&>.vh-aside-motto {
font-size: 0.86rem;
color: var(--vh-black-66);
color: var(--vh-font-66);
line-height: 1.5;
text-align: center;
}
@ -102,12 +93,15 @@ aside.vh-aside {
width: 1.88rem;
height: 1.88rem;
border: solid 1px #333;
background-color: #fff;
background-color: var(--vh-white-color);
border-radius: 0.38rem;
overflow: hidden;
z-index: 1;
&:hover {
color: var(--vh-white-color);
border-color: var(--vh-white-color);
&::after {
height: 100%;
}
@ -121,7 +115,7 @@ aside.vh-aside {
width: 100%;
height: 0;
background-color: var(--vh-main-color-88);
transition: height 0.16s ease-in-out;
transition: height 0.18s ease-in-out;
z-index: -1;
}
@ -133,11 +127,90 @@ aside.vh-aside {
}
}
}
&>.vh-aside-info {
box-sizing: border-box;
padding: 0 1rem;
display: grid;
grid-template-columns: repeat(3, 1fr);
width: 100%;
height: max-content;
&>.count {
box-sizing: border-box;
padding: 1rem 0;
display: flex;
flex-direction: column;
justify-content: center;
gap: 0.66rem;
text-align: center;
background: var(--vh-white-color);
border-radius: var(--vh-main-radius);
user-select: none;
transition: box-shadow 0.18s ease-in-out;
&:hover {
box-shadow: var(--vh-box-shadow);
z-index: 1;
}
&>span {
font-size: 1.05rem;
color: var(--vh-font-color);
}
&>p {
font-size: 0.8rem;
color: var(--vh-font-56);
}
}
}
&>.vh-aside-canvas {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: hidden;
z-index: 1;
pointer-events: none;
}
}
// 公告
&.tips {
align-items: flex-start;
&>span {
display: flex;
align-items: center;
justify-content: flex-start;
gap: 0.288rem;
font-size: 1rem;
&>svg {
margin-bottom: 0.188rem;
color: var(--vh-main-color);
width: 1.18rem;
height: 1.18rem;
object-fit: contain;
animation: talking-tag-active 0.18s ease-in-out infinite;
}
}
&>.tips-content {
font-size: 0.88rem;
p {
padding: 0.28rem 0;
}
}
}
// 分类
&.categories {
&>.vh-aside-categories {
&.cat {
&>.vh-aside-cat {
display: flex;
flex-direction: column;
width: 100%;
@ -149,20 +222,21 @@ aside.vh-aside {
&>a {
flex-shrink: 0;
box-sizing: border-box;
padding: 0.5rem 0.28rem;
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
height: 2.36rem;
height: 2.18rem;
font-size: 0.88rem;
border-radius: 0.38rem;
background-color: #fff;
transition: all 0.16s ease-in-out;
background-color: var(--vh-white-color);
transition: all 0.18s ease-in-out;
overflow: hidden;
&:hover {
padding-left: 0.66rem;
background-color: var(--vh-main-color-66);
color: var(--vh-white-color);
&>i {
background-color: transparent;
@ -173,13 +247,14 @@ aside.vh-aside {
display: flex;
align-items: center;
justify-content: center;
width: 2rem;
height: 1.75rem;
font-size: var(--vh-size-span);
width: 1.68rem;
height: 1.28rem;
color: var(--vh-white-color);
font-size: 0.66rem;
font-style: normal;
border-radius: 0.38rem;
background-color: var(--vh-main-color-66);
transition: background 0.16s ease-in-out;
transition: background 0.18s ease-in-out;
overflow: hidden;
}
}
@ -191,35 +266,39 @@ aside.vh-aside {
&>.vh-aside-tags {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
gap: 0.68rem 1rem;
width: 100%;
height: max-content;
overflow: hidden;
&>a {
display: flex;
align-items: center;
gap: 0.18rem;
width: max-content;
height: max-content;
&:hover {
&>span {
background-color: var(--vh-main-color);
color: var(--vh-main-color);
}
}
&>span {
display: block;
box-sizing: border-box;
padding: 0 0.618rem;
width: max-content;
height: 1.75rem;
border-radius: 0.38rem;
background-color: var(--vh-main-color-38);
font-size: var(--vh-size-span);
font-style: normal;
line-height: 1.75rem;
transition: background 0.16s ease-in-out;
font-size: 0.8rem;
line-height: 1.5;
transition: color 0.18s ease-in-out;
overflow: hidden;
}
&>em {
// display: none;
color: var(--vh-font-28);
font-size: 0.688rem;
font-style: normal;
}
}
}
}
@ -236,73 +315,93 @@ aside.vh-aside {
&>a {
box-sizing: border-box;
padding: 0 0.625rem;
display: flex;
align-items: center;
gap: 0.625rem;
flex-direction: column;
justify-content: space-between;
width: 100%;
height: 4.6rem;
border-bottom: 1px dashed #f3f2f2;
height: 2.68rem;
overflow: hidden;
&:nth-last-of-type(1) {
border: none;
}
&:hover {
&>span {
&>cite {
color: var(--vh-main-color);
&>p {
&>img {
transform: scale(1.2);
&::after {
width: 100%;
}
}
}
}
&>p {
&:nth-of-type(2) {
&>span {
&>i {
background-color: var(--vh-warning);
}
}
}
&:nth-of-type(3) {
&>span {
&>i {
background-color: var(--vh-import);
}
}
}
&>span {
box-sizing: border-box;
display: flex;
flex-direction: column;
justify-content: center;
overflow: hidden;
align-items: center;
gap: 0.618rem;
width: 100%;
&.cover {
flex-shrink: 0;
width: 3.88rem;
height: 3.88rem;
border-radius: 0.288rem;
overflow: hidden;
&>cite {
width: max-content;
max-width: 100%;
border-bottom: 1px solid transparent;
font-size: 0.8rem;
line-height: 1.5;
font-style: normal;
transition: color 0.288s ease-in-out;
&>img {
width: 100%;
height: 100%;
object-fit: cover;
transition: transform 0.16s ease-in-out;
&::after {
content: '';
display: block;
width: 0;
height: 1px;
background-color: var(--vh-main-color);
transition: width 0.58s cubic-bezier(.6, .1, 0, 1);
}
}
&.info {
flex: 1;
width: 100%;
height: 100%;
&>i {
flex-shrink: 0;
width: 0.875rem;
height: 0.875rem;
border-radius: 0.18rem;
color: var(--vh-white-color);
text-align: center;
font-size: 0.66rem;
font-style: normal;
background-color: var(--vh-success);
}
&>span {
width: 100%;
font-size: var(--vh-size-h3);
line-height: 1.5;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
text-overflow: ellipsis;
word-break: break-all;
overflow: hidden;
}
&>time {
color: #858585;
font-size: 0.68rem;
}
&>em {
flex-shrink: 0;
width: max-content;
font-style: normal;
font-size: 0.8125rem;
color: var(--vh-font-66);
}
}
&>time {
color: var(--vh-font-56);
font-size: 0.688rem;
}
}
}
}

View File

@ -6,7 +6,7 @@ section.vh-back-top {
height: 2.6rem;
border-radius: 50%;
border: 1px solid #e5e7eb;
background-color: #fff;
background-color: var(--vh-white-color);
transform: translateX(1rem);
cursor: pointer;
opacity: 0;

View File

@ -4,13 +4,13 @@
width: 100%;
height: max-content;
border-radius: 0.5rem;
background-color: #fff;
box-shadow: 0 3px 8px 6px rgba(7, 17, 27, 0.05);
background-color: var(--vh-white-color);
box-shadow: var(--vh-box-shadow);
overflow: hidden;
// 评论样式
.twikoo {
font-size: var(--vh-size-h2);
font-size: 0.88rem;
img:not(.tk-avatar-img) {
cursor: pointer;
@ -28,7 +28,7 @@
ol {
li {
color: #33333388;
font-size: var(--vh-size-span);
font-size: 0.875rem;
line-height: 1.6;
}
@ -39,7 +39,7 @@
:not(pre) {
&>code {
background-color: var(--vh-black-16);
background-color: var(--vh-font-16);
padding: .125rem .375rem;
font-size: 0.8125rem;
}
@ -53,19 +53,19 @@
}
p {
color: var(--vh-black-100);
color: var(--vh-font-color);
font-weight: 400;
font-size: var(--vh-size-span);
font-size: 0.875rem;
line-height: 28px;
a {
color: var(--vh-black-56);
box-shadow: inset 0 -.12rem #60a5fa;
color: var(--vh-font-56);
box-shadow: inset 0 -.12rem var(--vh-main-color);
transition: box-shadow .2s ease-in-out, color .2s ease-in-out;
&:hover {
box-shadow: inset 0 -1.5rem #60a5fa66;
box-shadow: inset 0 -1.5rem var(--vh-main-color-38);
}
}
}
@ -78,7 +78,7 @@
}
p {
color: #fff;
color: var(--vh-white-color);
}
.tk-admin-warn p {
@ -190,7 +190,7 @@
width: 0.8rem;
height: 0.8rem;
border-radius: 50%;
background: #fff url("/public/assets/images/admin.svg") no-repeat;
background: var(--vh-white-color) url("/public/assets/images/admin.svg") no-repeat;
background-size: cover;
}
}
@ -215,7 +215,7 @@
&>.tk-nick {
position: relative;
color: var(--vh-black-100);
color: var(--vh-font-color);
line-height: 1.18rem;
&.tk-nick-link {
@ -229,7 +229,7 @@
bottom: 0;
width: 0;
height: 1px;
background-color: var(--vh-black-88);
background-color: var(--vh-font-88);
transition: all 0.28s;
}
@ -292,7 +292,7 @@
box-sizing: border-box;
padding: 4px;
border-radius: 0.2rem;
background-color: var(--vh-black-6);
background-color: var(--vh-font-6);
}
}
@ -324,7 +324,7 @@
// 头像尺寸和圆角
--waline-avatar-size: 2.56rem;
--waline-avatar-radius: 0.58rem;
font-size: var(--vh-size-h2);
font-size: 0.88rem;
.wl-comment {
.wl-panel {
@ -350,8 +350,8 @@
&>.wl-head {
.wl-nick {
color: var(--vh-black-100);
font-size: var(--vh-size-h2);
color: var(--vh-font-color);
font-size: 0.88rem;
line-height: 1.18;
&::after {
@ -362,7 +362,7 @@
bottom: 0;
width: 0;
height: 1px;
background-color: var(--vh-black-88);
background-color: var(--vh-font-88);
transition: all 0.28s;
}
@ -389,8 +389,8 @@
p {
padding: 0.18rem 0;
color: var(--vh-black-100);
font-size: var(--vh-size-span);
color: var(--vh-font-color);
font-size: 0.875rem;
line-height: 1.75rem;
}
@ -400,7 +400,7 @@
}
a {
color: var(--vh-black-100);
color: var(--vh-font-color);
font-size: 0.76rem;
font-weight: 700;
}
@ -415,7 +415,7 @@
// item 中 wl-card 虚线间距
&>.wl-card {
padding-bottom: 1.25rem;
border-bottom-color: var(--vh-black-16);
border-bottom-color: var(--vh-font-16);
}
}
}
@ -448,7 +448,7 @@
}
a {
color: var(--vh-black-56);
color: var(--vh-font-56);
line-height: 1.288;
}

View File

@ -15,7 +15,7 @@
position: relative;
width: max-content;
height: max-content;
color: #33333388;
color: var(--vh-font-56) !important;
box-shadow: none !important;
overflow: hidden;
@ -26,7 +26,7 @@
content: '';
width: 0;
height: 2px;
background-color: #60a5fa;
background-color: var(--vh-main-color);
transition: all 0.3s ease-in-out;
}
@ -48,7 +48,7 @@
font-size: 0.875rem !important;
&>span {
color: #9CC5FB;
color: var(--vh-main-color);
}
}

View File

@ -51,7 +51,7 @@
width: max-content;
height: 100%;
line-height: 1.25rem;
color: #fff;
color: var(--vh-white-color);
font-style: normal;
}
@ -96,11 +96,11 @@
&.text {
&>p {
font-size: var(--vh-size-h2);
font-size: 0.88rem;
&>a {
color: #94A3B8;
transition: opacity 0.16s ease-in-out;
transition: opacity 0.18s ease-in-out;
&:hover {
opacity: 0.88;

View File

@ -1,4 +1,7 @@
---
// 当前路由
const { activeNav } = Astro.props;
// 站点配置
import SITE_CONFIG from "@/config";
const { Navs } = SITE_CONFIG;
// Svg 组件
@ -11,24 +14,22 @@ import "./Header.less";
<header class="vh-header">
<section class="main">
<a href="/" class="index">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-smart-home"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M19 8.71l-5.333 -4.148a2.666 2.666 0 0 0 -3.274 0l-5.334 4.148a2.665 2.665 0 0 0 -1.029 2.105v7.2a2 2 0 0 0 2 2h12a2 2 0 0 0 2 -2v-7.2c0 -.823 -.38 -1.6 -1.03 -2.105"></path><path d="M16 15c-2.21 1.333 -5.792 1.333 -8 0"></path></svg>
<a href="/" class="home vh-hover">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M19 8.71l-5.333 -4.148a2.666 2.666 0 0 0 -3.274 0l-5.334 4.148a2.665 2.665 0 0 0 -1.029 2.105v7.2a2 2 0 0 0 2 2h12a2 2 0 0 0 2 -2v-7.2c0 -.823 -.38 -1.6 -1.03 -2.105"></path><path d="M16 15c-2.21 1.333 -5.792 1.333 -8 0"></path></svg>
<span>Home</span>
</a>
<div class="link-list vh-link-list">
<nav>
{
Navs.map(i => (
<a class={i.link.replace("/", "")} href={i.link} target={i.target ? "_blank" : "_self"}>
<a class={`nav-link vh-hover${i.link.includes(activeNav) ? " active" : ""}`} href={i.link} target={i.target ? "_blank" : "_self"}>
{i.text}
<Svg src={i.icon} />
</a>
))
}
</div>
<div class="nav-btn">
<span class="nav-btn-item search-btn"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"> <path d="M416 208c0 45.9-14.9 88.3-40 122.7L502.6 457.4c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0L330.7 376c-34.4 25.2-76.8 40-122.7 40C93.1 416 0 322.9 0 208S93.1 0 208 0S416 93.1 416 208zM208 352a144 144 0 1 0 0-288 144 144 0 1 0 0 288z"></path></svg> 搜索</span>
<span class="nav-btn-item menu-btn"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path d="M0 96C0 78.3 14.3 64 32 64l384 0c17.7 0 32 14.3 32 32s-14.3 32-32 32L32 128C14.3 128 0 113.7 0 96zM0 256c0-17.7 14.3-32 32-32l384 0c17.7 0 32 14.3 32 32s-14.3 32-32 32L32 288c-17.7 0-32-14.3-32-32zM448 416c0 17.7-14.3 32-32 32L32 448c-17.7 0-32-14.3-32-32s14.3-32 32-32l384 0c17.7 0 32 14.3 32 32z"></path></svg></span>
</div>
<span class="nav-link vh-hover search-btn"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M10 10m-7 0a7 7 0 1 0 14 0a7 7 0 1 0 -14 0"></path><path d="M21 21l-6 -6"></path></svg> 搜索</span>
<span class="nav-link vh-hover menu-btn"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M4 6h16"></path><path d="M7 12h13"></path><path d="M10 18h10"></path></svg></span>
</nav>
<Search />
</section>
</header>
</header>

View File

@ -3,70 +3,46 @@ header.vh-header {
left: 0;
top: 0;
box-sizing: border-box;
padding: 0 1rem;
display: flex;
align-items: center;
justify-content: center;
width: 100vw;
height: 3.6rem;
background-color: var(--vh-white-6);
height: 66px;
backdrop-filter: blur(2rem);
box-shadow: 0 3px 8px 6px rgba(7, 17, 27, 0.05);
background-color: rgba(255, 255, 255, 0.36);
box-shadow: var(--vh-box-shadow);
transition: background-color 0.18s ease-in-out;
z-index: 9;
&>section.main {
position: relative;
&>section {
margin: 0 auto;
box-sizing: border-box;
padding: 0 1rem;
padding: 0 0.88rem;
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
height: 80%;
height: 100%;
max-width: var(--vh-main-max-width);
a,
span.nav-btn-item {
flex-shrink: 0;
position: relative;
&>.home {
box-sizing: border-box;
padding: 0 0.5rem;
padding: 0.266rem 0.188rem;
display: flex;
align-items: center;
gap: 0.5rem;
gap: 0.618rem;
width: max-content;
height: 80%;
font-size: var(--vh-size-p);
font-weight: 700;
z-index: 1;
transition: all 0.08s ease-in-out;
user-select: none;
cursor: pointer;
height: max-content;
color: var(--vh-font-color);
&:hover {
&::before {
transform: scale(1.2);
opacity: 1;
}
&::after {
opacity: 0;
&>span {
color: var(--vh-white-color);
}
}
&::before {
content: '';
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
border-radius: 0.5rem;
background-color: var(--vh-main-color);
opacity: 0;
transition: transform 0.16s ease-in-out, opacity 0.16s ease-in-out;
z-index: -1;
&>span {
font-size: 0.98rem;
font-weight: 600;
padding-top: 0.188rem;
transition: all 0.1s;
font-weight: 700;
}
&>svg {
@ -74,123 +50,63 @@ header.vh-header {
width: auto;
object-fit: contain;
}
&.index {
padding-right: 2rem;
&>span {
box-sizing: border-box;
padding-top: 0.188rem;
width: max-content;
height: max-content;
font-size: 1.088rem;
line-height: 1;
}
}
&.search-btn {
&>svg {
width: 1rem;
height: 1rem;
}
}
}
&>.link-list,
&>.nav-btn {
flex: 1;
&>nav {
box-sizing: border-box;
padding-right: 1rem;
display: flex;
align-items: center;
justify-content: flex-end;
gap: 0.88rem;
margin-left: auto;
gap: 1.18rem;
height: 100%;
width: max-content;
max-width: 100%;
&:nth-last-of-type(1) {
padding-right: 0;
}
&>.nav-link {
box-sizing: border-box;
padding: 0.288rem 0.36rem;
display: flex;
align-items: center;
gap: 0.28rem;
line-height: inherit;
height: max-content;
font-size: 0.98rem;
font-weight: 600;
user-select: none;
&>a,
&>span {
&.active {
pointer-events: none;
&.search-btn,
&.menu-btn {
cursor: pointer;
}
&::before {
transform: scale(1);
opacity: 1;
&.menu-btn {
display: none;
&>svg {
height: 1.28rem;
}
}
&>svg {
pointer-events: none;
height: 1rem;
width: auto;
height: 1.18rem;
object-fit: contain;
stroke: var(--vh-black-88);
stroke-width: 2.88px;
}
}
}
&>.nav-btn {
flex: none;
width: max-content;
&>.menu-btn {
display: none;
&>svg {
box-sizing: border-box;
padding: 0.36rem;
width: 1.88rem;
height: 1.88rem;
border: 1px solid var(--vh-black-16);
border-radius: 0.6rem;
}
}
}
}
}
@media screen and (max-width: 888px) {
header.vh-header {
padding: 0;
&>section.main {
a {
font-size: var(--vh-size-span);
&:hover {
&::before {
transform: scale(1.1);
}
}
&>svg {
width: 1.18rem;
height: 1.18rem;
}
}
&>.link-list {
display: none;
}
}
}
}
@media screen and (max-width: 888px) {
header.vh-header {
&>section.main {
&>.nav-btn {
&>.menu-btn {
&>section {
&>nav {
&>a.nav-link {
display: none;
}
&>.menu-btn.nav-link {
display: flex;
}
}

View File

@ -0,0 +1,12 @@
---
import SITE_INFO from "@/config";
import "./MainHeader.less";
---
<div class="header-main">
<div class="avatar">
<img src="/assets/images/lazy-loading.webp" data-vh-lz-src={SITE_INFO.Avatar} alt="avatar" />
</div>
<h3 class="auther">{SITE_INFO.Author || "-"}</h3>
<p class="desc"></p>
</div>

View File

@ -0,0 +1,105 @@
.header-main {
position: relative;
box-sizing: border-box;
display: flex;
align-items: center;
flex-direction: column;
justify-content: center;
gap: 0.88rem;
width: 100%;
height: var(--vh-main-header-height);
transition: height 0.3s ease-in-out;
z-index: 1;
&::before {
content: "";
position: absolute;
inset: 0;
pointer-events: none;
background: var(--vh-home-banner) no-repeat top center;
background-size: cover;
z-index: -1;
}
&>.avatar {
width: 8.5rem;
height: 8.5rem;
border: 2px solid var(--vh-white-color);
border-radius: 50%;
transition: transform 0.3s ease-in-out;
overflow: hidden;
&:hover {
transform: rotate(18.88deg);
}
&>img {
width: 100%;
height: 100%;
object-fit: cover;
}
}
&>.auther {
padding-top: 1.88rem;
font-size: 1.88rem;
font-weight: 600;
color: var(--vh-white-color);
}
&>.desc {
position: relative;
box-sizing: border-box;
padding: 0.66rem 0.66rem 0;
width: 100%;
max-width: 26rem;
height: max-content;
color: var(--vh-white-color);
text-align: center;
font-size: 0.88rem;
line-height: 1.5rem;
&::before {
content: "";
display: block;
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 1px;
opacity: .5;
background: -webkit-gradient(linear, left top, right top, color-stop(0%, rgb(0 0 0 / 70%)), color-stop(35%, var(--vh-white-color)), color-stop(65%, var(--vh-white-color)), color-stop(100%, rgb(0 0 0 / 70%)));
}
&::after {
content: "|";
padding-left: 0.188rem;
display: inline-block;
animation: blink 1s infinite;
color: #fff;
}
@keyframes blink {
50% {
opacity: 0;
}
}
}
}
@media screen and (max-width: 1150px) {
.header-main {
height: 26rem;
&>.avatar {
width: 6rem;
height: 6rem;
}
&>.auther {
padding-top: 0;
}
}
}

View File

@ -7,7 +7,7 @@
box-sizing: border-box;
background-color: rgba(0, 0, 0, 0.26);
overflow: hidden;
transition: opacity 0.16s ease-in-out, z-index 0.16s ease-in-out;
transition: opacity 0.18s ease-in-out;
opacity: 0;
pointer-events: none;
z-index: 10;
@ -38,9 +38,9 @@
width: 100%;
max-width: 17.6rem;
height: 100%;
background-color: #fff;
background-color: var(--vh-white-color);
transform: translateX(-1rem);
transition: transform 0.16s ease-in;
transition: transform 0.18s ease-in;
overflow: hidden;
&>.vh-mobilesidebar-list {
@ -69,7 +69,7 @@
gap: 0.618rem;
width: 100%;
height: 2.25rem;
font-size: var(--vh-size-p);
font-size: 1rem;
color: #66758c;
font-weight: 600;
border-radius: 0.375rem;
@ -87,11 +87,11 @@
&:hover,
&.active {
color: #fff;
background-color: #3366FF;
color: var(--vh-white-color);
background-color: var(--vh-main-color);
&>svg {
stroke: #fff;
stroke: var(--vh-white-color);
}
}
@ -99,7 +99,7 @@
width: auto;
height: 0.888rem;
object-fit: contain;
transition: stroke 0.16s ease-in-out;
transition: stroke 0.18s ease-in-out;
}
}
}

View File

@ -6,12 +6,10 @@ import { fmtPage } from "@/utils/index";
import "./Pagination.less";
---
<section class="vh-article-pagination">
<section class="vh-art-page">
<!-- 上一页 -->
<a class={`vh-pagination-item${!data.prev ? " disabled" : ""}`} href={data.prev || "javascript:;"} title="上一页">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512">
<path d="M9.4 233.4c-12.5 12.5-12.5 32.8 0 45.3l192 192c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L77.3 256 246.6 86.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0l-192 192z"></path>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M15 6l-6 6l6 6"></path></svg>
</a>
<!-- 首页 -->
{
@ -49,8 +47,6 @@ import "./Pagination.less";
}
<!-- 下一页 -->
<a class={`vh-pagination-item${!data.next ? " disabled" : ""}`} href={data.next || "javascript:;"} title="下一页">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512">
<path d="M310.6 233.4c12.5 12.5 12.5 32.8 0 45.3l-192 192c-12.5 12.5-32.8 12.5-45.3 0s-12.5-32.8 0-45.3L242.7 256 73.4 86.6c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0l192 192z"></path>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M9 6l6 6l-6 6"></path></svg>
</a>
</section>

View File

@ -1,7 +1,7 @@
section.vh-article-pagination {
section.vh-art-page {
position: absolute;
left: 0;
bottom: 0;
bottom: 0.88rem;
right: 0;
box-sizing: border-box;
padding: 1rem;
display: flex;
@ -10,6 +10,7 @@ section.vh-article-pagination {
gap: 1rem;
width: 100%;
height: max-content;
overflow: hidden;
&>a.vh-pagination-item {
display: flex;
@ -18,20 +19,21 @@ section.vh-article-pagination {
width: 2rem;
height: 2rem;
border-radius: 0.5rem;
background-color: #fff;
box-shadow: 0 3px 8px 6px rgba(7, 17, 27, 0.05);
font-size: var(--vh-size-span);
background-color: var(--vh-white-color);
box-shadow: var(--vh-box-shadow);
font-size: 0.875rem;
color: #99a9bf;
user-select: none;
transition: background-color 0.16s ease-in-out;
transition: background-color 0.18s ease-in-out;
cursor: pointer;
overflow: hidden;
&:hover {
color: #fff;
color: var(--vh-white-color);
background-color: rgba(129, 146, 174, 0.36);
&>svg {
fill: #fff;
color: var(--vh-white-color);
}
}
@ -39,22 +41,21 @@ section.vh-article-pagination {
background-color: #8192AE;
cursor: default;
pointer-events: none;
color: #fff;
color: var(--vh-white-color);
}
&.disabled {
pointer-events: none;
&>svg {
fill: #E8E8E8;
color: #E8E8E8;
}
}
&>svg {
width: 0.8rem;
height: 0.8rem;
fill: #99a9bf;
transition: fill 0.16s ease-in-out;
height: 1.18rem;
width: auto;
object-fit: contain;
}
}
}

View File

@ -7,8 +7,9 @@
gap: 0.88rem;
width: 100%;
height: max-content;
background-color: #fff;
box-shadow: 0 3px 8px 6px rgba(7, 17, 27, 0.05);
border-radius: var(--vh-main-radius);
background-color: var(--vh-white-color);
box-shadow: var(--vh-box-shadow);
&>p {
padding: 0 !important;
@ -36,9 +37,9 @@
width: max-content;
height: 2rem;
border-radius: 0.28rem;
color: #fff;
color: var(--vh-white-color);
cursor: pointer;
transition: opacity 0.16s, transform 0.16s;
transition: opacity 0.18s, transform 0.18s;
user-select: none;
&:hover {
@ -82,8 +83,8 @@
background-size: cover;
z-index: 1;
overflow: hidden;
box-shadow: 0 3px 8px 6px rgba(7, 17, 27, 0.05);
transition: opacity 0.16s, transform 0.16s;
transition: opacity 0.18s, transform 0.18s;
}

View File

@ -11,7 +11,7 @@
flex-direction: column;
background-color: rgba(0, 0, 0, 0.26);
overflow: hidden;
transition: opacity 0.16s ease-in-out;
transition: opacity 0.18s ease-in-out;
opacity: 0;
pointer-events: none;
z-index: 1;
@ -37,10 +37,10 @@
height: max-content;
max-height: 88vh;
border-radius: 1rem;
background-color: #fff;
box-shadow: 0 3px 8px 6px rgba(7, 17, 27, 0.05);
background-color: var(--vh-white-color);
transform: translateY(-1rem);
transition: transform 0.16s;
transition: transform 0.18s;
overflow: hidden;
@ -53,11 +53,10 @@
gap: 0.28rem;
width: 100%;
height: 2.425rem;
background-color: var(--vh-white-100);
background-color: var(--vh-font-6);
border-radius: 0.66rem;
overflow: hidden;
&>svg {
flex-shrink: 0;
width: 1.28rem;
@ -69,13 +68,13 @@
width: 100%;
height: 100%;
border: none;
color: var(--vh-black-88);
font-size: var(--vh-size-span);
color: var(--vh-font-88);
font-size: 0.875rem;
background-color: transparent;
transition: width 0.16s ease-in-out;
transition: width 0.18s ease-in-out;
&::placeholder {
color: var(--vh-black-88);
color: var(--vh-font-88);
}
}
}
@ -86,7 +85,7 @@
box-sizing: border-box;
width: 100%;
height: max-content;
background-color: #fff;
background-color: var(--vh-white-color);
user-select: none;
overflow-x: hidden;
overflow-y: auto;
@ -117,9 +116,9 @@
&>span {
width: 100%;
font-size: var(--vh-size-h1);
font-size: 1.125rem;
font-weight: 600;
color: var(--vh-black-100);
color: var(--vh-font-color);
line-height: 1.16;
white-space: nowrap;
overflow: hidden;
@ -128,8 +127,8 @@
&>p {
width: 100%;
font-size: var(--vh-size-span);
color: var(--vh-black-56);
font-size: 0.875rem;
color: var(--vh-font-56);
font-weight: 400;
line-height: 1.25rem;
display: -webkit-box;

View File

@ -1,19 +1,27 @@
---
const { title, keywords, description, pagecover } = Astro.props;
const { title, keywords, description, pagecover, activeNav } = Astro.props;
// 网站配置
import SITE_INFO from "@/config";
const { GoogleAds } = SITE_INFO;
const { GoogleAds, Theme, HomeBanner } = SITE_INFO;
const { ad_Client, asideAD_Slot, articleAD_Slot } = GoogleAds;
// Head 依赖
import Head from "@/components/Head/Head.astro";
// 顶部 Header
import Header from "@/components/Header/Header.astro";
// Main 区域 Header
import MainHeader from "@/components/MainHeader/MainHeader.astro";
// Aside组件
import Aside from "@/components/Aside/Aside.astro";
// 底部 Footer
import Footer from "@/components/Footer/Footer.astro";
// 返回顶部
import BackTop from "@/components/BackTop/BackTop.astro";
// 手机端侧边栏
import MobileSidebar from "@/components/MobileSidebar/MobileSidebar.astro";
// A Modern CSS Reset
import "@/styles/Reset.less";
// 全局基础样式
import "@/styles/Base.less";
// Layout 样式
import "./Layout.less";
---
@ -22,15 +30,31 @@ import "./Layout.less";
<Head Title={title} Keywords={keywords} Description={description} PageCover={pagecover}>
<!-- 谷歌广告JS加载项 -->
{ad_Client && (asideAD_Slot || articleAD_Slot) && <script is:inline async src={`https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=${ad_Client}`} crossorigin="anonymous" />}
<!-- 泡泡🫧 -->
<script is:inline src="/assets/js/vhPaopao.js" defer></script>
<!-- 彩旗🎈 -->
<script is:inline src="/assets/js/vhCaiqi.js" defer></script>
<!-- 设置主题颜色 -->
<Fragment
set:html={`<style>:root {${Object.entries(Theme)
.map(([key, value]) => `${key}:${value};`)
.join("")}--vh-home-banner:url('${HomeBanner.cover}')}</style>`}
/>
</Head>
<body>
<MobileSidebar />
<Header />
<main class="vh-main">
<slot />
<Header activeNav={activeNav} />
<main class="main">
{HomeBanner.enable && <MainHeader />}
<section class="main-inner" style={`padding-top:${HomeBanner.enable ? "2rem" : "calc(66px + 0.68rem)"}`}>
<section class="main-inner-content">
<slot />
</section>
<Aside />
</section>
<BackTop />
</main>
<Footer />
<BackTop />
<script>
import InitFn from "@/scripts/Init";
// 全局初始化

View File

@ -1,53 +1,39 @@
body {
&::before {
content: "";
position: fixed;
left: 0;
top: 0;
width: 100vw;
height: 100vh;
background: #fff url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32' width='8' height='8' fill='none' stroke='rgb(0 0 0 / 0.1)'%3e%3cpath d='M0 .5H31.5V32'/%3e%3c/svg%3e");
z-index: -1;
}
main.main {
box-sizing: border-box;
display: flex;
flex-direction: column;
align-items: center;
width: 100%;
height: max-content;
overflow: visible !important;
&>main.vh-main {
&>.main-inner {
flex-shrink: 0;
position: relative;
box-sizing: border-box;
display: flex;
flex-direction: column;
gap: 1rem;
flex-direction: row-reverse;
width: 100%;
max-width: var(--vh-main-max-width);
height: max-content;
z-index: 1;
&>section.vh-container {
position: relative;
&>.main-inner-content {
flex: 1 1 0%;
box-sizing: border-box;
display: flex;
flex-direction: row-reverse;
gap: 1.8rem;
padding: 0.88rem 0.66rem;
width: 100%;
min-width: 0;
height: max-content;
&>section:not(.article-list) {
box-sizing: border-box;
display: flex;
flex-direction: column;
width: 100%;
min-width: 0;
height: max-content;
}
overflow: hidden;
}
}
}
@media screen and (max-width: 888px) {
body {
&>main.vh-main {
&>section.vh-container {
flex-direction: column;
}
main.main {
&>.main-inner {
flex-direction: column;
}
}
}

View File

@ -14,18 +14,17 @@ import Comment from "@/components/Comment/Comment.astro";
import "@/styles/About.less";
// 文章内容基础样式
import "@/styles/ArticleBase.less";
// PageLayout 样式
import "./PageLayout.less";
---
<Layout title={frontmatter.title || Title} description={Description}>
<section class="vh-container">
<section class="vh-about vh-animation vh-animation-init">
<header class="vh-page-header">
<h1>{frontmatter.h1}</h1>
<p>{frontmatter.desc}</p>
</header>
<main><slot /></main>
{checkComment() && frontmatter.comment != false && <Comment />}
</section>
<Aside />
<Layout title={frontmatter.title || Title} description={Description} activeNav={frontmatter.type || "-"}>
<section class="vh-page vh-about vh-animation vh-animation-init">
<header class="vh-page-header">
<h1>{frontmatter.h1}</h1>
<p>{frontmatter.desc}</p>
</header>
<main><slot /></main>
{checkComment() && frontmatter.comment != false && <Comment />}
</section>
</Layout>

View File

@ -0,0 +1,18 @@
.vh-page {
display: flex;
flex-direction: column;
gap: 1.25rem;
&>.vh-page-header {
box-sizing: border-box;
padding: 1rem;
box-shadow: var(--vh-box-shadow);
background-color: var(--vh-white-color);
border-radius: 0.5rem;
h1,
p {
padding: 0;
}
}
}

View File

@ -16,17 +16,14 @@ import "./ToolLayout.less";
import "@/styles/ArticleBase.less";
---
<Layout title={frontmatter.title || Title} description={Description}>
<section class="vh-container">
<section class="vh-tools-main vh-animation vh-animation-init">
<header class="vh-page-header">
<h1>{frontmatter.h1}</h1>
<p>{frontmatter.desc}</p>
</header>
<main><slot /></main>
<main class={`${frontmatter.type}-main main`}><section class="vh-space-loading"><span></span><span></span><span></span></section></main>
{checkComment() && frontmatter.comment != false && <Comment />}
</section>
<Aside />
<Layout title={frontmatter.title || Title} description={Description} activeNav={frontmatter.type || "-"}>
<section class="vh-tools-main vh-animation vh-animation-init">
<header class="vh-page-header">
<h1>{frontmatter.h1}</h1>
<p>{frontmatter.desc}</p>
</header>
<main><slot /></main>
<main class={`${frontmatter.type}-main main`}><section class="vh-space-loading"><span></span><span></span><span></span></section></main>
{checkComment() && frontmatter.comment != false && <Comment />}
</section>
</Layout>

View File

@ -1,7 +1,26 @@
// 说说页评论
.vh-tools-main {
display: flex;
flex-direction: column;
gap: 1.25rem;
.vh-node {
margin: -0.58rem 0 !important;
}
&>.vh-page-header {
box-sizing: border-box;
padding: 1rem;
background-color: var(--vh-white-color);
box-shadow: var(--vh-box-shadow);
border-radius: 0.5rem;
h1,
p {
padding: 0;
}
}
&>main {
box-sizing: border-box;
border-radius: 0.5rem;
@ -16,9 +35,7 @@
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 1rem;
background-color: #fff;
box-shadow: 0 3px 8px 6px rgba(7, 17, 27, 0.05);
background-color: var(--vh-white-color);
&>a {
position: relative;
@ -30,12 +47,12 @@
width: 100%;
height: 5.5rem;
border-radius: 0.88rem;
transition: background-color 0.16s ease-in-out;
transition: background-color 0.18s ease-in-out;
overflow: hidden;
z-index: 1;
&:hover {
background-color: var(--vh-black-16);
background-color: var(--vh-font-16);
&>.avatar {
transform: scale(1.2) rotate(8deg);
@ -52,7 +69,7 @@
border: solid 1px #E8E8E8;
overflow: hidden;
opacity: 1;
transition: all .16s;
transition: all .18s;
}
&>.link-info {
@ -67,7 +84,7 @@
z-index: 1;
&>span {
font-size: var(--vh-size-p);
font-size: 1rem;
font-weight: 700;
line-height: 1rem;
}
@ -97,8 +114,7 @@
gap: 1.25rem;
&>.vh-space-loading {
background-color: #fff;
box-shadow: 0 3px 8px 6px rgba(7, 17, 27, 0.05);
background-color: var(--vh-white-color);
}
&>article {
@ -107,8 +123,8 @@
width: 100%;
height: max-content;
border-radius: 0.5rem;
box-shadow: 0 3px 8px 6px rgba(7, 17, 27, 0.05);
background: #fff linear-gradient(90deg, #EDEEF388, #fff) no-repeat 100% 100% / 0 1px;
box-shadow: var(--vh-box-shadow);
background: var(--vh-white-color) linear-gradient(90deg, #EDEEF388, var(--vh-white-color)) no-repeat 100% 100% / 0 1px;
background-position: 0 100%;
background-size: 0 100%;
transition: .7s cubic-bezier(.6, .1, 0, 1), background-position 0s;
@ -139,7 +155,7 @@
&>p {
padding: 0;
color: #26262680;
font-size: var(--vh-size-span);
font-size: 0.875rem;
line-height: 1.8;
display: -webkit-box;
-webkit-box-orient: vertical;
@ -164,7 +180,7 @@
align-items: center;
gap: 0.5rem;
height: 1.56rem;
font-size: var(--vh-size-h3);
font-size: 0.8rem;
overflow: hidden;
&>em {
@ -206,8 +222,7 @@
gap: 1.25rem;
&>.vh-space-loading {
background-color: #fff;
box-shadow: 0 3px 8px 6px rgba(7, 17, 27, 0.05);
background-color: var(--vh-white-color);
}
&>article {
@ -215,9 +230,11 @@
padding: 2.188rem 1.875rem;
display: flex;
flex-direction: column;
width: 100%;
height: max-content;
border-radius: 0.5rem;
background-color: #fff;
box-shadow: 0 3px 8px 6px rgba(7, 17, 27, 0.05);
background-color: var(--vh-white-color);
box-shadow: var(--vh-box-shadow);
overflow: hidden;
&>header {
@ -244,7 +261,7 @@
overflow: hidden;
&>span {
font-size: var(--vh-size-span);
font-size: 0.875rem;
}
&>time {
@ -258,7 +275,7 @@
&>.main {
box-sizing: border-box;
padding: 1rem 0;
font-size: var(--vh-size-span);
font-size: 0.875rem;
font-weight: 500;
img {
@ -309,8 +326,6 @@
}
}
&>footer {
box-sizing: border-box;
display: flex;
@ -325,17 +340,17 @@
align-items: center;
height: 1.68rem;
width: max-content;
border: 1px solid var(--vh-info);
border-radius: 0.88rem;
background-color: #fff;
border: 1px solid var(--vh-main-color);
border-radius: var(--vh-main-radius);
background-color: var(--vh-white-color);
font-size: 0.72rem;
color: var(--vh-info);
color: var(--vh-main-color);
transition: all .2s ease-in-out;
user-select: none;
cursor: pointer;
&:hover {
animation: talking-tag-active 0.16s ease-in-out infinite;
animation: talking-tag-active 0.18s ease-in-out infinite;
}
}
}
@ -389,32 +404,4 @@
}
}
}
}
// 动画效果
@keyframes talking-tag-active {
0% {
transform: translateX(0);
}
25% {
transform: translateX(-1.66px);
}
50% {
transform: translateX(1.66px);
}
75% {
transform: translateX(-1.66px);
}
100% {
transform: translateX(0);
}
}

View File

@ -1,19 +0,0 @@
---
// 公共 Layout
import Layout from "@/layouts/Layout/Layout.astro";
// Aside组件
import Aside from "@/components/Aside/Aside.astro";
// 文章页面样式
import "@/styles/Article.less";
---
<Layout title="404 Not Found" keywords={[404]} description="404 Not Found">
<section class="vh-container">
<article class="vh-article-main vh-animation vh-animation-init">
<header>
<h1 class="error-title">404 Not Found</h1>
</header>
</article>
<Aside />
</section>
</Layout>

10
src/pages/404.md Normal file
View File

@ -0,0 +1,10 @@
---
title: "404 Not Found"
h1: "404 Not Found"
layout: "@/layouts/PageLayout/PageLayout.astro"
comment: false
---
:::note{type="error"}
你来到了一个不存在的页面。
:::

View File

@ -26,21 +26,18 @@ const { Description } = SITE_CONFIG;
import ArticleCard from "@/components/ArticleCard/ArticleCard.astro";
// 公共 Layout
import Layout from "@/layouts/Layout/Layout.astro";
// Aside组件
import Aside from "@/components/Aside/Aside.astro";
// 分页组件
import Pagination from "@/components/Pagination/Pagination.astro";
const currentPage = page_data.url.current.replace("/", "");
---
<Layout title={currentPage ? `第${currentPage}页文章` : ""} description={Description}>
<section class="vh-container">
<section class="article-list vh-animation vh-animation-init">
<section class="article-list-main">
<section class="article-list">
<!-- 文章列表 -->
{data.map((post, index) => <ArticleCard post={post} index={index} />)}
<!-- 分页 -->
<Pagination data={page_data.url} />
</section>
<Aside />
<Pagination data={page_data.url} />
</section>
</Layout>

View File

@ -3,23 +3,42 @@ title: "关于"
h1: "关于我"
desc: "Hi there, Im Han 👋"
layout: "@/layouts/PageLayout/PageLayout.astro"
type: "about"
---
:::note{type="success"}
我是一名热爱前端开发并拥有丰富经验的工程师,喜欢探索新技术并应用于实际项目中
我是韩小韩,一位对技术充满热情、涉猎广泛的探索者,同时也是一名热衷于探索前沿技术的实践者
我始终保持对新技术的热情,并将我的知识与经验分享在我的博客中。我期待在这里与你分享我的见解、经验和最新的技术动态。
我始终保持对新技术的热情,并将我的知识与经验分享在我的博客中。
我的技术兴趣广泛涵盖前端工程、云计算、自托管服务、AI 应用以及网络架构优化。我始终相信,优秀的技术人不仅要有深度,更要有广度,因此我不断学习新知识,并将其转化为实际解决方案。
我期待在这里与你分享我的见解、经验和最新的技术动态。
:::
## Hi there, I'm Han 👋
<div class="enfj-dom">
<div class="text">
<em>主人公</em>
<span>ENFJ-A</span>
<a class="more-enfj" href="https://www.16personalities.com/ch/enfj-%E4%BA%BA%E6%A0%BC">在 16personalities 了解更多关于 主人公</a>
</div>
</div>
**在 Web 开发方面**,我深耕 Vue.js 生态,并且关注了现代 CSS 框架如 Tailwind CSS。同时对前端构建工具如 Webpack、Rollup 和 Vite 以及代码质量和规范工具如 ESLint 进行了研究,我还关注了前端性能优化,例如代码分割、资源加载策略以及 Astro、Hexo 等现代网站构建工具的使用。
**在后端开发和云计算领域**,我对 Node.js 生态系统非常熟悉,并积极探索 Serverless 架构。我长期使用 Cloudflare Workers、Vercel 和腾讯云 EdgeOne 等平台优化边缘计算、KV 存储和全球 CDN 加速方案,确保应用的高可用性和低延迟。
**在自托管Homelab和个人服务器管理方面**,我对个人服务器运维充满热情,搭建了基于 Jellyfin 的媒体中心、Home Assistant 智能家居系统,并利用 OpenWrt 优化家庭网络。在 Linux 系统管理、Docker 容器化部署和自动化脚本方面积累了丰富经验。
**在人工智能与大型语言模型LLMs领域**,我密切关注 AI 领域的发展,尤其是 DeepSeek、Gemini 和 Claude 等大模型的应用。我尝试将 AI 能力整合到开发流程中,例如自动化文档生成、代码优化辅助,并且对 AI SDK 和相关工具保持关注。
**在网络与 DevOps 方面**,我熟悉 DNS 解析、CDN 加速、SSL 证书管理,并研究 TCP/IP、HTTP/3 等协议优化。我实践 Git 工作流、CI/CD 自动化GitHub Actions / Cloudflare Pages并利用 Docker 实现开发环境标准化。
**🚀 技术理念**
- **持续学习:** 技术日新月异,我始终保持开放心态,学习新框架、新工具,并评估其适用性。
- **实践驱动:** 无论是个人项目还是开源贡献,我都倾向于动手实践,而非仅停留在理论层面。
- **效率优先:** 我注重自动化与工具链优化,减少重复劳动,提升开发体验。
**🎯 未来方向**
未来,我计划进一步探索:
- **边缘计算与全球化部署**,优化分布式应用的性能。
- **AI 增强开发**,探索 LLM 在代码生成、调试和文档管理中的应用。
- **智能家居与自动化**,结合 Home Assistant 打造更高效的家庭 lab。
## Languages and Tools
@ -45,12 +64,12 @@ layout: "@/layouts/PageLayout/PageLayout.astro"
## 小站点
| 主&emsp;页 | <https://www.4ce.cn> |
| :-----------------: | :-----------------: |
| **博&emsp;客** | **<https://www.vvhan.com>** |
| **Web&emsp;API** | **<https://api.vvhan.com>** |
| **Han Analytics** | **<https://analytics.vvhan.com>** |
| **Cloudflare 优选** | **<https://cf.vvhan.com>** |
| 主&emsp;页 | <https://www.4ce.cn> |
| :-----------------: | :-------------------------------: |
| **博&emsp;客** | **<https://www.vvhan.com>** |
| **Web&emsp;API** | **<https://api.vvhan.com>** |
| **Han Analytics** | **<https://analytics.vvhan.com>** |
| **Cloudflare 优选** | **<https://cf.vvhan.com>** |
## 联系我

View File

@ -13,7 +13,7 @@ import Aside from "@/components/Aside/Aside.astro";
import Archive from "@/components/Archive/Archive.astro";
---
<Layout title="归档" description={Description}>
<Layout title="归档" description={Description} activeNav="archives">
<section class="vh-container">
<Archive articleList={articleList} />
<Aside />

View File

@ -22,8 +22,6 @@ const { Content, remarkPluginFrontmatter } = await render(post);
const { reading_time, article_word_count } = remarkPluginFrontmatter;
// 公共 Layout
import Layout from "@/layouts/Layout/Layout.astro";
// Aside组件
import Aside from "@/components/Aside/Aside.astro";
// Copyright 组件
import Copyright from "@/components/Copyright/Copyright.astro";
// Reward 组件
@ -40,39 +38,47 @@ import "@/styles/ArticleBase.less";
---
<Layout title={post.data.title} keywords={post.data.tags} description={description} pagecover={ARTICLE_COVER}>
<section class="vh-container">
<article class="vh-article-main vh-animation vh-animation-init">
<header>
<h1>{post.data.title}</h1>
<div class="article-meta">
<span class="article-meta-item">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M12 12m-9 0a9 9 0 1 0 18 0a9 9 0 1 0 -18 0"></path><path d="M12 12h3.5"></path><path d="M12 7v5"></path></svg>
<time>{fmtTime(post.data.date, "YYYY-MM-DD A")}</time>
<span class="count"><strong>{article_word_count || "一点"}</strong>字</span>
<span class="time"><strong>{parseFloat((Number(reading_time) || 0).toFixed(1).replace(/\.0+$/, ""))}</strong>分钟</span>
</span>
<a class="article-meta-item" href={`/categories/${post.data.categories}`}>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M4 6h16"></path><path d="M7 12h13"></path><path d="M10 18h10"></path></svg>
<span>{post.data.categories}</span>
</a>
</div>
</header>
<main>
<Content />
<nav class="tag-list">
{post.data.tags.map((i: any) => <a href={`/tag/${i}`}>{i}</a>)}
</nav>
</main>
<footer>
<!-- 打赏组件 -->
<Reward />
<!-- 版权©️信息 -->
<Copyright site={Site} id={post.data.id} title={post.data.title} sitename={Title} time={fmtTime(post.data.date, "YYYY-MM-DD A")} auther={Author} />
<!-- 底部谷歌广告 -->
{GoogleAds.ad_Client && GoogleAds.articleAD_Slot && <GoogleAd className="vh-article-ad" slotID={GoogleAds.articleAD_Slot} />}
</footer>
{checkComment() && <Comment />}
</article>
<Aside />
</section>
<article class="vh-article-main vh-animation vh-animation-init">
<header>
<h1>{post.data.title}</h1>
<div class="article-meta">
<span class="article-meta-item">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M12 12m-9 0a9 9 0 1 0 18 0a9 9 0 1 0 -18 0"></path><path d="M12 12h3.5"></path><path d="M12 7v5"></path></svg>
<time>{fmtTime(post.data.date, "YYYY-MM-DD A")}</time>
<span class="count"><strong>{article_word_count || "一点"}</strong>字</span>
<span class="time"><strong>{parseFloat((Number(reading_time) || 0).toFixed(1).replace(/\.0+$/, ""))}</strong>分钟</span>
</span>
<a class="article-meta-item" href={`/categories/${post.data.categories}`}>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M4 6h16"></path><path d="M7 12h13"></path><path d="M10 18h10"></path></svg>
<span>{post.data.categories}</span>
</a>
</div>
</header>
<main>
<Content />
<nav class="tag-list">
{
post.data.tags.map((i: any) => (
<a href={`/tag/${i}`}>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M7.5 7.5m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0" />
<path d="M3 6v5.172a2 2 0 0 0 .586 1.414l7.71 7.71a2.41 2.41 0 0 0 3.408 0l5.592 -5.592a2.41 2.41 0 0 0 0 -3.408l-7.71 -7.71a2 2 0 0 0 -1.414 -.586h-5.172a3 3 0 0 0 -3 3z" />
</svg>
{i}
</a>
))
}
</nav>
</main>
<footer>
<!-- 打赏组件 -->
<Reward />
<!-- 版权©️信息 -->
<Copyright site={Site} id={post.data.id} title={post.data.title} sitename={Title} time={fmtTime(post.data.date, "YYYY-MM-DD A")} auther={Author} />
<!-- 底部谷歌广告 -->
{GoogleAds.ad_Client && GoogleAds.articleAD_Slot && <GoogleAd className="vh-article-ad" slotID={GoogleAds.articleAD_Slot} />}
</footer>
{checkComment() && <Comment />}
</article>
</Layout>

View File

@ -13,15 +13,10 @@ import SITE_CONFIG from "@/config";
const { Description } = SITE_CONFIG;
// 公共 Layout
import Layout from "@/layouts/Layout/Layout.astro";
// Aside组件
import Aside from "@/components/Aside/Aside.astro";
// 文章列表组件
import Archive from "@/components/Archive/Archive.astro";
---
<Layout title={`分类 ${categories} 下的文章`} description={Description}>
<section class="vh-container">
<Archive articleList={articleList} />
<Aside />
</section>
<Layout title={`分类 ${categories} 下的文章`} description={Description} activeNav="categories">
<Archive articleList={articleList} />
</Layout>

View File

@ -22,8 +22,5 @@ import Archive from "@/components/Archive/Archive.astro";
---
<Layout title={`标签 ${tags} 下的文章`} description={Description}>
<section class="vh-container">
<Archive articleList={articleList} />
<Aside />
</section>
<Archive articleList={articleList} />
</Layout>

View File

@ -15,7 +15,16 @@ const WalineFn = async (commentDOM: string, walineInit: any) => {
import('@waline/client/waline-meta.css');
const { init } = await import('@waline/client');
walineInit = init({
el: commentDOM, path: window.location.pathname.replace(/\/$/, ''), serverURL: SITE_INFO.Comment.Waline.serverURL, emoji: ['https://registry.npmmirror.com/@waline/emojis/1.3.0/files/alus', 'https://registry.npmmirror.com/@waline/emojis/1.3.0/files/bilibili', 'https://registry.npmmirror.com/@waline/emojis/1.3.0/files/bmoji', 'https://registry.npmmirror.com/@waline/emojis/1.3.0/files/qq', 'https://registry.npmmirror.com/@waline/emojis/1.3.0/files/tieba', 'https://registry.npmmirror.com/@waline/emojis/1.3.0/files/weibo', 'https://registry.npmmirror.com/@waline/emojis/1.3.0/files/soul-emoji'],
el: commentDOM, path: window.location.pathname.replace(/\/$/, ''), serverURL: SITE_INFO.Comment.Waline.serverURL,
emoji: ['https://registry.npmmirror.com/@waline/emojis/1.3.0/files/alus', 'https://registry.npmmirror.com/@waline/emojis/1.3.0/files/bilibili', 'https://registry.npmmirror.com/@waline/emojis/1.3.0/files/bmoji', 'https://registry.npmmirror.com/@waline/emojis/1.3.0/files/qq', 'https://registry.npmmirror.com/@waline/emojis/1.3.0/files/tieba', 'https://registry.npmmirror.com/@waline/emojis/1.3.0/files/weibo', 'https://registry.npmmirror.com/@waline/emojis/1.3.0/files/soul-emoji'],
reaction: [
"https://registry.npmmirror.com/@waline/emojis/1.3.0/files/tieba/tieba_agree.png",
"https://registry.npmmirror.com/@waline/emojis/1.3.0/files/tieba/tieba_look_down.png",
"https://registry.npmmirror.com/@waline/emojis/1.3.0/files/tieba/tieba_sunglasses.png",
"https://registry.npmmirror.com/@waline/emojis/1.3.0/files/tieba/tieba_pick_nose.png",
"https://registry.npmmirror.com/@waline/emojis/1.3.0/files/tieba/tieba_awkward.png",
"https://registry.npmmirror.com/@waline/emojis/1.3.0/files/tieba/tieba_sleep.png",
],
imageUploader: async (file: any) => {
const body = new FormData();
body.append('file', file);

View File

@ -6,7 +6,7 @@ import { $GET } from '@/utils/index'
import vhLzImgInit from "@/scripts/vhLazyImg";
const FriendsInit = async (data: any) => {
const friendsDOM = document.querySelector('.vh-container>.vh-tools-main>main.friends-main')
const friendsDOM = document.querySelector('.main-inner-content>.vh-tools-main>main.friends-main')
if (!friendsDOM) return;
try {
let res = data;

View File

@ -0,0 +1,4 @@
import { $POST } from '@/utils/index'
export default async () => {
await $POST('https://vh-api.4ce.cn/api/seoPush', { url: window.location.href.replace(/\/$/, '') }, { 'Content-Type': 'application/json; charset=utf-8' })
}

View File

@ -1,11 +0,0 @@
const linkListArr = ['links', 'friends', 'talking', 'archives', 'message', 'about']
export default () => {
const linkARR = document.querySelectorAll('.vh-link-list>a');
if (!linkARR.length) return;
linkARR.forEach((i: any) => {
i.classList.remove('active');
const linkName = (window.location.pathname).split('/')[1]
if (!linkListArr.includes(linkName)) return;
document.querySelectorAll(`.${linkName}`).forEach((i: any) => i.classList.add('active'));
})
}

View File

@ -1,4 +1,6 @@
import { inRouter, outRouter } from "@/utils/updateRouter";
// Banner 打字效果
import TypeWriteInit from "@/scripts/TypeWrite";
// 初始化文章代码块
import codeInit from "@/scripts/Code";
// 初始化视频播放器
@ -15,8 +17,6 @@ import { searchFn, vhSearchInit } from "@/scripts/Search";
import vhLzImgInit from "@/scripts/vhLazyImg";
// 图片灯箱
import ViewImage from "@/scripts/ViewImage";
// 顶部导航 Current 状态
import initLinkCurrent from "@/scripts/Header";
// 底部网站运行时间
import initWebSiteTime from "@/scripts/Footer";
// 友情链接初始化
@ -33,6 +33,8 @@ import initMobileSidebar from "@/scripts/MobileSidebar";
import GoogleAdInit from "@/scripts/GoogleAd";
// Han Analytics 统计
import HanAnalyticsInit from "@/scripts/HanAnalytics";
// 谷歌 SEO 推送
import GoogleSEOInit from "@/scripts/GoogleSeoPush";
// SmoothScroll 滚动优化
import SmoothScroll from "@/scripts/Smoothscroll";
@ -43,28 +45,26 @@ const videoList: any[] = [];
const MusicList: any[] = [];
let commentLIst: any = { walineInit: null };
const indexInit = async (only: boolean = true) => {
// 预加载搜索数据
only && searchFn("");
// 初始化搜索功能
only && vhSearchInit();
// 打字效果
only && TypeWriteInit();
// 初始化网站运行时间
only && initWebSiteTime();
// 初始化BackTop组件
only && BackTopInitFn();
// 移动端侧边栏初始化
only && initMobileSidebar();
// SmoothScroll 滚动优化
only && SmoothScroll();
// 图片灯箱
only && ViewImage();
// 顶部导航 Current 状态
initLinkCurrent()
// 初始化文章代码块
codeInit();
// 文章评论初始化
checkComment() && commentInit(checkComment(), commentLIst)
// 图片懒加载初始化
vhLzImgInit();
// 初始化 LivePhoto
livePhotoInit();
// 文章视频播放器初始化
videoInit(videoList);
// 文章音乐播放器初始化
musicInit(MusicList);
// 友情链接初始化
initLinks();
// 朋友圈 RSS 初始化
@ -73,14 +73,18 @@ const indexInit = async (only: boolean = true) => {
initTalking();
// Google 广告
GoogleAdInit();
// 谷歌 SEO 推送
GoogleSEOInit();
// 文章评论初始化
checkComment() && commentInit(checkComment(), commentLIst)
// Han Analytics 统计
HanAnalyticsInit();
// 文章视频播放器初始化
videoInit(videoList);
// 文章音乐播放器初始化
musicInit(MusicList);
// 初始化 LivePhoto
livePhotoInit();
// 预加载搜索数据
only && searchFn("");
// 初始化搜索功能
vhSearchInit();
// 移动端侧边栏初始化
initMobileSidebar();
};
export default () => {

View File

@ -4,7 +4,7 @@ import { $GET } from '@/utils/index'
import vhLzImgInit from "@/scripts/vhLazyImg";
// 渲染
const LinksInit = async (data: any) => {
const linksDOM = document.querySelector('.vh-container>.vh-tools-main>main.links-main')
const linksDOM = document.querySelector('.main-inner-content>.vh-tools-main>main.links-main')
if (!linksDOM) return;
try {
let res = data;

View File

@ -1,6 +1,6 @@
// 初始化侧边栏
export default () => {
const menuDOM: any = document.querySelector(".vh-header>.main>.nav-btn>span.menu-btn");
const menuDOM: any = document.querySelector(".vh-header>.main>nav>span.menu-btn");
const mobileSidebarDOM: any = document.querySelector("body>.vh-mobilesidebar");
const addActive = () => setTimeout(() => mobileSidebarDOM.classList.add("active"));
const removeActive = () => setTimeout(() => mobileSidebarDOM.classList.remove("active"));

View File

@ -44,7 +44,7 @@ const searchInputChange = (v: any) => {
// 初始化搜索框
const vhSearchInit = () => {
const searchDOM: any = document.querySelector(".vh-header>.main>.nav-btn>span.search-btn");
const searchDOM: any = document.querySelector(".vh-header>.main>nav>span.search-btn");
const searchMainDOM: any = document.querySelector(".vh-header>.main>.vh-search>main");
const searchListDOM: any = document.querySelector(".vh-header>.main>.vh-search");
const addActive = () => setTimeout(() => {

View File

@ -6,7 +6,7 @@ import { $GET } from '@/utils/index'
import vhLzImgInit from "@/scripts/vhLazyImg";
const TalkingInit = async (data: any) => {
const talkingDOM = document.querySelector('.vh-container>.vh-tools-main>main.talking-main')
const talkingDOM = document.querySelector('.main-inner-content>.vh-tools-main>main.talking-main')
if (!talkingDOM) return;
try {
let res = data;

38
src/scripts/TypeWrite.ts Normal file
View File

@ -0,0 +1,38 @@
import SITE_INFO from '@/config';
export default () => {
const writeDom = document.querySelector('.header-main>.desc');
if (!writeDom) return;
const TypeWriteList = SITE_INFO.TypeWriteList;
let TypeWriteListIndex = 0;
let index = 0;
let isDeleting = false;
// 主动画函数
const run = () => {
writeDom.innerHTML = TypeWriteList[TypeWriteListIndex].substring(0, index);
// 正常打字阶段
if (!isDeleting) {
if (index < TypeWriteList[TypeWriteListIndex].length) {
index++;
setTimeout(run, 188); // 打字速度
} else {
// 完整展示后开始删除
setTimeout(() => {
isDeleting = true;
run();
}, 2888);
}
} else {
if (index > 0) {
index--;
setTimeout(run, 88); // 删除速度(比打字快)
} else {
isDeleting = false;
TypeWriteListIndex++;
TypeWriteListIndex = TypeWriteListIndex % TypeWriteList.length;
setTimeout(run, 500);
}
}
}
// 启动动画
run();
}

View File

@ -4,7 +4,7 @@ import { LoadScript } from "@/utils/index";
declare const ViewImage: any;
const ViewImgList: string[] = [
// 文章内图片
".vh-container>article.vh-article-main img.vh-article-img",
"article.vh-article-main img.vh-article-img",
// 动态页面图片
"main.talking-main>article>.main img",
// Twikoo 评论区图片

View File

@ -4,7 +4,7 @@ import LazyLoad from "vanilla-lazyload";
// 初始化图片懒加载
let lazyLoadStatus: any = null;
export default () => {
document.querySelectorAll("main>.vh-container img:not(.view-image-container)").forEach((i: any) => {
document.querySelectorAll(".main-inner>.main-inner-content img:not(.view-image-container)").forEach((i: any) => {
// 是否包含data-vh-lz-src
if (!i.hasAttribute("data-vh-lz-src")) {
i.setAttribute("data-vh-lz-src", i.getAttribute("src"));

View File

@ -4,11 +4,12 @@
&>main {
box-sizing: border-box;
padding: 1rem;
border-radius: 0.5rem;
background-color: #fff;
box-shadow: 0 3px 8px 6px rgba(7, 17, 27, 0.05);
width: 100%;
height: max-content;
min-height: 5.5rem;
border-radius: 0.5rem;
background-color: var(--vh-white-color);
box-shadow: var(--vh-box-shadow);
overflow: hidden;
&>h3 {
@ -16,7 +17,7 @@
padding: 0.88rem 0;
width: 100%;
height: max-content;
font-size: var(--vh-size-h1);
font-size: 1.125rem;
}
&>.language-tool {
@ -34,7 +35,7 @@
height: 2rem;
line-height: 1;
overflow: hidden;
transition: transform 0.16s ease-in-out;
transition: transform 0.18s ease-in-out;
&>img {
height: calc(100% - 1px);

View File

@ -1,176 +1,243 @@
section.vh-container {
&>article.vh-article-main {
flex: 1;
min-width: 0;
article.vh-article-main {
position: relative;
box-sizing: border-box;
display: flex;
flex-direction: column;
width: 100%;
height: max-content;
&>header {
box-sizing: border-box;
padding: 2rem 1rem 1rem;
display: flex;
flex-direction: column;
&>header {
box-sizing: border-box;
padding: 2rem 1rem 1rem;
display: flex;
flex-direction: column;
width: 100%;
height: max-content;
background-color: var(--vh-white-color);
border-top-left-radius: 0.5rem;
border-top-right-radius: 0.5rem;
box-shadow: var(--vh-box-shadow);
overflow: hidden;
&>h1 {
width: 100%;
height: max-content;
background-color: #fff;
border-top-left-radius: 0.5rem;
border-top-right-radius: 0.5rem;
box-shadow: 0 -3px 8px 6px rgba(7, 17, 27, 0.05);
overflow: hidden;
font-weight: 700;
}
&>h1 {
width: 100%;
font-weight: 700;
}
&>.article-meta {
display: flex;
align-items: center;
gap: 0.88rem;
font-size: 0.85rem;
&>.article-meta {
&>.article-meta-item {
display: flex;
align-items: center;
gap: 0.88rem;
font-size: 0.85rem;
gap: 0.18rem;
width: max-content;
&>.article-meta-item {
display: flex;
align-items: center;
gap: 0.18rem;
width: max-content;
&>svg {
flex-shrink: 0;
height: 0.888rem;
width: auto;
}
&>svg {
flex-shrink: 0;
height: 0.888rem;
width: auto;
}
&>time {
color: var(--vh-font-66);
}
&>time {
color: var(--vh-black-66);
}
&>span {
&.count {
padding-left: 0.38rem;
color: #3FA67F;
&>span {
&.count {
padding-left: 0.38rem;
strong {
color: #3FA67F;
strong {
color: #3FA67F;
}
}
}
&.time {
&.time {
color: #E9B740;
strong {
color: #E9B740;
strong {
color: #E9B740;
}
}
}
}
}
}
}
&>main {
&>main {
box-sizing: border-box;
padding: 1rem;
display: flex;
flex-direction: column;
width: 100%;
height: max-content;
background-color: var(--vh-white-color);
border-bottom-left-radius: 0.5rem;
border-bottom-right-radius: 0.5rem;
box-shadow: 0 12px 8px 6px #07111b0d;
overflow: hidden;
.aplayer-list {
max-height: max-content !important;
&>ol {
max-height: 566px !important;
}
}
.dplayer-controller {
span {
color: var(--vh-white-color);
}
}
&>.tag-list {
margin-top: 2rem;
position: relative;
box-sizing: border-box;
padding: 1rem;
padding: 1rem 0;
display: flex;
flex-direction: column;
flex-wrap: wrap;
gap: 0.58rem;
width: 100%;
height: max-content;
background-color: #fff;
border-bottom-left-radius: 0.5rem;
border-bottom-right-radius: 0.5rem;
box-shadow: 0 12px 8px 6px rgba(7, 17, 27, 0.05);
overflow: hidden;
.aplayer-list {
max-height: max-content !important;
&>ol {
max-height: 566px !important;
}
}
.dplayer-controller {
span {
color: #fff;
}
}
&>.tag-list {
&>a {
position: relative;
box-sizing: border-box;
padding-top: 2rem;
padding: 0 0.3125rem;
display: flex;
flex-wrap: wrap;
gap: 0.58rem;
width: 100%;
align-items: center;
gap: 0.28rem;
width: max-content;
height: 1.5rem;
color: var(--vh-info);
border-radius: 0.28rem;
font-size: 0.76rem;
transition: color 0.288s ease-in-out;
overflow: hidden;
z-index: 1;
&>a {
position: relative;
box-sizing: border-box;
padding: 0.28rem 0.68rem;
padding-left: 2rem;
display: flex;
align-items: center;
gap: 0.18rem;
height: 1.68rem;
width: max-content;
font-size: var(--vh-size-span);
border-radius: 1rem;
background-color: var(--vh-main-color-56);
transition: background 0.16s ease-in-out, transform 0.16s ease-in-out;
&:hover {
background-color: var(--vh-main-color-88);
transform: translateY(-0.18rem);
}
&:hover {
color: var(--vh-main-color) !important;
&::before {
position: absolute;
left: 3px;
top: 50%;
transform: translateY(-50%);
content: '';
width: 1.38rem;
height: 1.38rem;
background: #fff url('data:image/svg+xml;charset=utf-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20512%20512%22%3E%3Cpath%20d%3D%22M345%2039.1L472.8%20168.4c52.4%2053%2052.4%20138.2%200%20191.2L360.8%20472.9c-9.3%209.4-24.5%209.5-33.9%20.2s-9.5-24.5-.2-33.9L438.6%20325.9c33.9-34.3%2033.9-89.4%200-123.7L310.9%2072.9c-9.3-9.4-9.2-24.6%20.2-33.9s24.6-9.2%2033.9%20.2zM0%20229.5L0%2080C0%2053.5%2021.5%2032%2048%2032l149.5%200c17%200%2033.3%206.7%2045.3%2018.7l168%20168c25%2025%2025%2065.5%200%2090.5L277.3%20442.7c-25%2025-65.5%2025-90.5%200l-168-168C6.7%20262.7%200%20246.5%200%20229.5zM144%20144a32%2032%200%201%200%20-64%200%2032%2032%200%201%200%2064%200z%22%2F%3E%3C%2Fsvg%3E') no-repeat center center;
background-size: 0.88rem;
border-radius: 1rem;
overflow: hidden;
width: 100%;
left: 0;
right: auto;
}
&::after {
width: 0;
right: 0;
left: auto;
}
}
&::before,
&::after {
content: '';
position: absolute;
left: auto;
right: 0;
top: 0;
width: 0;
height: 100%;
z-index: 1;
transition: width 0.288s ease-in-out;
background-color: var(--vh-main-color-16);
}
&::after {
width: 100%;
left: 0;
right: auto;
z-index: -1;
}
&:nth-of-type(2n+1) {
color: var(--vh-info);
&::after {
background-color: var(--vh-info-hover);
}
}
&:nth-of-type(2n+2) {
color: var(--vh-success);
&::after {
background-color: var(--vh-success-hover);
}
}
&:nth-of-type(2n+3) {
color: var(--vh-warning);
&::after {
background-color: var(--vh-warning-hover);
}
}
&:nth-of-type(2n+4) {
color: var(--vh-error);
&::after {
background-color: var(--vh-error-hover);
}
}
&:nth-of-type(2n+5) {
color: var(--vh-import);
&::after {
background-color: var(--vh-import-hover);
}
}
&>svg {
height: 0.8rem;
width: auto;
object-fit: contain;
transform: rotate(90deg);
}
}
}
}
&>footer {
box-sizing: border-box;
width: 100%;
height: max-content;
}
&>footer {
box-sizing: border-box;
width: 100%;
height: max-content;
}
}
@media screen and (max-width: 999px) {
section.vh-container {
&>article.vh-article-main {
&>header {
&>h1 {
font-size: 1.36rem;
}
}
h1 {
article.vh-article-main {
&>header {
&>h1 {
font-size: 1.36rem;
}
}
h2 {
font-size: 1.26rem;
}
h1 {
font-size: 1.36rem;
}
h3 {
font-size: var(--vh-size-h1);
}
h2 {
font-size: 1.26rem;
}
h3 {
font-size: 1.125rem;
}
.vh-node {
&.vh-picture {
grid-template-columns: repeat(auto-fit, minmax(48%, 1fr));
}
.vh-node {
&.vh-picture {
grid-template-columns: repeat(auto-fit, minmax(48%, 1fr));
}
}
}

View File

@ -1,4 +1,4 @@
.vh-container {
.main-inner {
&>section,
&>article {
@ -12,7 +12,7 @@
display: inline-block;
box-sizing: border-box;
margin: 1.666rem 0 0.36rem;
font-size: var(--vh-size-p);
font-size: 1rem;
width: 100%;
max-width: 100%;
word-break: break-all;
@ -26,8 +26,8 @@
&::before {
padding-right: 0.56rem;
content: "#";
color: var(--vh-black-38);
transition: color 0.16s ease-in-out;
color: var(--vh-font-28);
transition: color 0.18s ease-in-out;
}
}
@ -54,7 +54,7 @@
}
h4 {
font-size: var(--vh-size-h1);
font-size: 1.125rem;
}
// p标签样式
@ -68,25 +68,25 @@
line-height: 1.6;
li {
color: var(--vh-black-66);
font-size: var(--vh-size-span);
color: var(--vh-font-66);
font-size: 0.875rem;
line-height: 1.6;
}
// 其下面的 a 标签样式
a {
color: var(--vh-black-56);
box-shadow: inset 0 -.12rem #60a5fa;
color: var(--vh-font-56);
box-shadow: inset 0 -.12rem var(--vh-main-color);
transition: box-shadow .2s ease-in-out, color .2s ease-in-out;
&:hover {
box-shadow: inset 0 -1.5rem #60a5fa66;
box-shadow: inset 0 -1.5rem var(--vh-main-color-28);
}
}
// 其下面的code标签样式
code {
background-color: var(--vh-black-16);
background-color: var(--vh-font-16);
padding: .125rem .375rem;
font-size: 0.8125rem;
}
@ -109,7 +109,7 @@
border-radius: 0.618rem;
&>p {
font-size: var(--vh-size-span);
font-size: 0.875rem;
font-weight: 500;
line-height: 1.58rem;
color: #6A737D;
@ -124,14 +124,14 @@
// border-collapse: collapse;
border-spacing: 0;
font-size: 0.9rem;
background-color: #fff;
background-color: var(--vh-white-color);
border: 1px solid #EEEEEE;
border-radius: 0.38rem;
overflow: hidden;
a {
color: #49B1F5;
transition: all .16s;
transition: all .18s;
&:hover {
color: #1b99ee;
@ -165,7 +165,7 @@
}
tr {
transition: background-color 0.16s ease-in-out;
transition: background-color 0.18s ease-in-out;
&:nth-last-of-type(1) {
td {
@ -180,7 +180,7 @@
// 其下面的code标签样式
code {
background-color: var(--vh-black-16);
background-color: var(--vh-font-16);
padding: .125rem .375rem;
font-size: 0.8125rem;
}
@ -242,8 +242,9 @@
}
p {
font-size: var(--vh-size-span);
line-height: 2;
padding: 0.38rem 0;
font-size: 0.875rem;
line-height: 1.388rem;
font-weight: 500;
}
}
@ -261,7 +262,7 @@
height: 2.28rem;
border: solid 1px #333;
border-radius: 0.28rem;
background-color: #fff;
background-color: var(--vh-white-color);
cursor: pointer;
transition: background-color 0.18s ease-in-out;
z-index: 1;
@ -270,31 +271,31 @@
&:hover {
&::before {
height: 100%;
left: 0;
width: 100%;
}
}
&::after {
flex-shrink: 0;
content: '';
background-color: #000;
mask: url('data:image/svg+xml,%3Csvg%20%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20%20width%3D%22100%25%22%20%20height%3D%22100%25%22%20%20viewBox%3D%220%200%2024%2024%22%20%20fill%3D%22none%22%20%20stroke%3D%22currentColor%22%20%20stroke-width%3D%222%22%20%20stroke-linecap%3D%22round%22%20%20stroke-linejoin%3D%22round%22%20%20class%3D%22icon%20icon-tabler%20icons-tabler-outline%20icon-tabler-unlink%22%3E%3Cpath%20stroke%3D%22none%22%20d%3D%22M0%200h24v24H0z%22%20fill%3D%22none%22%2F%3E%3Cpath%20d%3D%22M17%2022v-2%22%20%2F%3E%3Cpath%20d%3D%22M9%2015l6%20-6%22%20%2F%3E%3Cpath%20d%3D%22M11%206l.463%20-.536a5%205%200%200%201%207.071%207.072l-.534%20.464%22%20%2F%3E%3Cpath%20d%3D%22M13%2018l-.397%20.534a5.068%205.068%200%200%201%20-7.127%200a4.972%204.972%200%200%201%200%20-7.071l.524%20-.463%22%20%2F%3E%3Cpath%20d%3D%22M20%2017h2%22%20%2F%3E%3Cpath%20d%3D%22M2%207h2%22%20%2F%3E%3Cpath%20d%3D%22M7%202v2%22%20%2F%3E%3C%2Fsvg%3E') no-repeat center center;
mask: url('data:image/svg+xml,%3Csvg%20%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20%20width%3D%22100%25%22%20%20height%3D%22100%25%22%20%20viewBox%3D%220%200%2024%2024%22%20%20fill%3D%22none%22%20%20stroke%3D%22currentColor%22%20%20stroke-width%3D%222%22%20%20stroke-linecap%3D%22round%22%20%20stroke-linejoin%3D%22round%22%3E%3Cpath%20stroke%3D%22none%22%20d%3D%22M0%200h24v24H0z%22%20fill%3D%22none%22%2F%3E%3Cpath%20d%3D%22M17%2022v-2%22%20%2F%3E%3Cpath%20d%3D%22M9%2015l6%20-6%22%20%2F%3E%3Cpath%20d%3D%22M11%206l.463%20-.536a5%205%200%200%201%207.071%207.072l-.534%20.464%22%20%2F%3E%3Cpath%20d%3D%22M13%2018l-.397%20.534a5.068%205.068%200%200%201%20-7.127%200a4.972%204.972%200%200%201%200%20-7.071l.524%20-.463%22%20%2F%3E%3Cpath%20d%3D%22M20%2017h2%22%20%2F%3E%3Cpath%20d%3D%22M2%207h2%22%20%2F%3E%3Cpath%20d%3D%22M7%202v2%22%20%2F%3E%3C%2Fsvg%3E') no-repeat center center;
background-size: contain;
width: 1.18rem;
height:1.18rem;
height: 1.18rem;
overflow: hidden;
}
&::before {
content: '';
position: absolute;
left: 0;
bottom: 0;
width: 100%;
height: 0;
background-color: var(--vh-main-color);
transition: height 0.16s ease-in-out;
right: 0;
top: 0;
width: 0;
height: 100%;
background-color: var(--vh-font-6);
transition: width 0.18s ease-in-out;
z-index: -1;
}
@ -302,7 +303,7 @@
box-sizing: border-box;
color: #000;
font-weight: 600;
font-size: var(--vh-size-span);
font-size: 0.875rem;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
@ -316,7 +317,7 @@
color: var(--vh-success);
}
&::before{
&::before {
background-color: var(--vh-success-hover);
}
@ -332,7 +333,7 @@
color: var(--vh-info);
}
&::before{
&::before {
background-color: var(--vh-info-hover);
}
@ -348,7 +349,7 @@
color: var(--vh-warning);
}
&::before{
&::before {
background-color: var(--vh-warning-hover);
}
@ -364,7 +365,7 @@
color: var(--vh-error);
}
&::before{
&::before {
background-color: var(--vh-error-hover);
}
@ -380,7 +381,7 @@
color: var(--vh-import);
}
&::before{
&::before {
background-color: var(--vh-import-hover);
}
@ -464,7 +465,7 @@
justify-content: center;
width: 100%;
aspect-ratio: 16 / 9;
background: #fff url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32' width='8' height='8' fill='none' stroke='rgb(0 0 0 / 0.1)'%3e%3cpath d='M0 .5H31.5V32'/%3e%3c/svg%3e");
background: var(--vh-white-color) url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32' width='8' height='8' fill='none' stroke='rgb(0 0 0 / 0.1)'%3e%3cpath d='M0 .5H31.5V32'/%3e%3c/svg%3e");
&.vhLivePhoto-y {
aspect-ratio: 9 / 16;
@ -524,7 +525,7 @@
overflow: hidden;
cursor: pointer;
opacity: 0.66;
transition: all .16s;
transition: all .18s;
&:hover {
opacity: 1;
@ -539,7 +540,7 @@
background: rgba(125, 125, 125, .2) url('data:image/svg+xml,%0A%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%20-960%20960%20960%22%3E%3Cpath%20fill%3D%22%23808080%22%20d%3D%22M368.37-237.37q-34.48%200-58.74-24.26-24.26-24.26-24.26-58.74v-474.26q0-34.48%2024.26-58.74%2024.26-24.26%2058.74-24.26h378.26q34.48%200%2058.74%2024.26%2024.26%2024.26%2024.26%2058.74v474.26q0%2034.48-24.26%2058.74-24.26%2024.26-58.74%2024.26H368.37Zm0-83h378.26v-474.26H368.37v474.26Zm-155%20238q-34.48%200-58.74-24.26-24.26-24.26-24.26-58.74v-515.76q0-17.45%2011.96-29.48%2011.97-12.02%2029.33-12.02t29.54%2012.02q12.17%2012.03%2012.17%2029.48v515.76h419.76q17.45%200%2029.48%2011.96%2012.02%2011.97%2012.02%2029.33t-12.02%2029.54q-12.03%2012.17-29.48%2012.17H213.37Zm155-238v-474.26%20474.26Z%22%2F%3E%3C%2Fsvg%3E') no-repeat center center;
background-size: 1.16rem 1.16rem;
opacity: 0;
transition: opacity 0.16s;
transition: opacity 0.18s;
}
&::after {
@ -572,7 +573,7 @@
border-radius: 0.618rem;
height: max-content;
max-height: 888px;
background-color: var(--vh-white-66) !important;
background-color: #F3F4F7 !important;
code {
box-sizing: border-box;
@ -580,10 +581,15 @@
&>span {
box-sizing: border-box;
font-size: var(--vh-size-span);
font-size: 0.875rem;
line-height: 1.66;
&>span {
text-decoration: none !important;
}
&::before {
box-sizing: content-box;
counter-increment: line;
content: counter(line);
display: inline-block;
@ -595,7 +601,6 @@
text-align: center;
user-select: none;
}
}
}
}

View File

@ -1,34 +1,30 @@
@import url('/assets/font/index.css');
:root {
// 基础色
--vh-black-color: #34495e; //文字颜色
--vh-white-color: #EDEEF3; //背景颜色
--vh-black-100: rgb(from var(--vh-black-color) r g b);
--vh-black-88: rgb(from var(--vh-black-color) r g b / 88%);
--vh-black-66: rgb(from var(--vh-black-color) r g b / 66%);
--vh-black-56: rgb(from var(--vh-black-color) r g b / 56%);
--vh-black-38: rgb(from var(--vh-black-color) r g b / 38.88%);
--vh-black-28: rgb(from var(--vh-black-color) r g b / 28%);
--vh-black-16: rgb(from var(--vh-black-color) r g b / 16%);
--vh-black-6: rgb(from var(--vh-black-color) r g b / 6%);
--vh-white-100: rgb(from var(--vh-white-color) r g b);
--vh-white-88: rgb(from var(--vh-white-color) r g b / 88%);
--vh-white-66: rgb(from var(--vh-white-color) r g b / 66%);
--vh-white-38: rgb(from var(--vh-white-color) r g b / 38.88%);
--vh-white-28: rgb(from var(--vh-white-color) r g b / 28%);
--vh-white-16: rgb(from var(--vh-white-color) r g b / 16%);
--vh-white-6: rgb(from var(--vh-white-color) r g b / 6%);
// 白色颜色
--vh-white-color: #fff;
// BoxShadow
--vh-box-shadow: 0 3px 8px 6px #07111b0d;
// Body 背景颜色
--vh-body-bg: #F8F8F8;
// Back Top 按钮位置
--vh-back-top: calc((calc(100vw - 2rem) - min((100vw - 2rem), var(--vh-main-max-width))) / 2);
// Main Header 封面高度
--vh-main-header-height: 38.88rem;
// 字体颜色
--vh-font-88: rgb(from var(--vh-font-color) r g b / 88%);
--vh-font-66: rgb(from var(--vh-font-color) r g b / 66%);
--vh-font-56: rgb(from var(--vh-font-color) r g b / 56%);
--vh-font-28: rgb(from var(--vh-font-color) r g b / 28%);
--vh-font-16: rgb(from var(--vh-font-color) r g b / 16%);
--vh-font-6: rgb(from var(--vh-font-color) r g b / 6%);
// 主题色
--vh-main-color: #EDEEF3;
--vh-main-color-88: rgb(from var(--vh-main-color) r g b / 88%);
--vh-main-color-66: rgb(from var(--vh-main-color) r g b / 66%);
--vh-main-color-56: rgb(from var(--vh-main-color) r g b / 56%);
--vh-main-color-38: rgb(from var(--vh-main-color) r g b / 38.88%);
--vh-main-color-28: rgb(from var(--vh-main-color) r g b / 28%);
--vh-main-color-16: rgb(from var(--vh-main-color) r g b / 16%);
--vh-main-color-6: rgb(from var(--vh-main-color) r g b / 6%);
// Info
--vh-info: #3253b4;
--vh-info-hover: #3253b418;
@ -44,67 +40,9 @@
// Import
--vh-import: #B984DF;
--vh-import-hover: #B984DF18;
// 尺寸
--vh-size-h1: 1.125rem;
--vh-size-h2: 0.88rem;
--vh-size-h3: 0.8rem;
--vh-size-p: 1rem;
--vh-size-span: 0.875rem;
--vh-size-small: 0.8625rem;
--vh-size-mini: 0.805rem;
--vh-padding-top: calc(66px + 1rem);
--vh-main-max-width: 1388px;
--vh-back-top: calc((calc(100vw - 2rem) - min((100vw - 2rem), var(--vh-main-max-width))) / 2 + 1rem);
}
h1,
h2,
h3,
h4,
h5,
h6,
p,
blockquote {
margin: 0;
padding: 0;
text-decoration: none;
}
span {
text-decoration: none !important;
}
p {
line-height: 2;
}
a {
margin: 0;
padding: 0;
text-decoration: none;
line-height: 1.6;
}
pre,
code {
margin: 0;
padding: 0;
font-family: 'SF Mono' !important;
font-weight: normal;
}
img {
max-width: 100%;
max-height: 100%;
overflow: hidden;
}
* {
color: var(--vh-black-100);
// IOS 点击阴影
-webkit-tap-highlight-color: transparent;
// 隐藏滚动条
@ -112,69 +50,127 @@ img {
-ms-overflow-style: none;
// 平滑滚动
scroll-behavior: smooth;
// 去除下划线
text-decoration: none;
// 消除边框
outline: none; // 隐藏滚动条
outline: none;
// 隐藏滚动条
::-webkit-scrollbar {
display: none;
}
}
// 设置文本选中颜色
::selection,
::-webkit-selection {
color: #fff;
background: var(--vh-main-color-88);
}
html {
word-wrap: break-word;
overflow-wrap: break-word;
font-size: 16px;
font-weight: 400;
&>body {
position: relative;
margin: 0;
box-sizing: border-box;
padding: var(--vh-padding-top) 0.5rem 0;
display: flex;
flex-direction: column;
align-items: center;
width: 100vw;
height: max-content;
color: var(--vh-font-color);
font-family: 'HarmonyOS Sans SC', serif;
font-weight: 400;
cursor: url(/assets/images/default.cur), default;
cursor: url('/assets/images/default.cur'), default;
overflow-x: hidden;
&::before {
content: "";
position: fixed;
left: 0;
top: 0;
width: 100vw;
height: 100vh;
background: #fff url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32' width='8' height='8' fill='none' stroke='rgb(0 0 0 / 0.1)'%3e%3cpath d='M0 .5H31.5V32'/%3e%3c/svg%3e");
z-index: -1;
}
&>footer {
box-sizing: border-box;
width: 100%;
height: max-content;
}
}
}
pre,
code {
font-family: 'SF Mono' !important;
}
a {
color: var(--vh-font-color);
}
img[data-vh-lz-src] {
transition: all 0.18s ease-in;
&:not(.loaded) {
filter: blur(0.88rem) brightness(1);
}
}
// ===================================================================== Other
// 悬浮背景 a 标签
.vh-hover {
position: relative;
transition: all 0.18s ease-in-out;
&:hover,
&.active {
color: var(--vh-white-color) !important;
&::before {
transform: scale(1.2);
opacity: 1;
}
}
// 其他页面Header 封装
header.vh-page-header {
box-sizing: border-box;
padding: 1rem;
&::before {
content: "";
position: absolute;
left: 0;
top: 0;
width: 100%;
height: max-content;
border-radius: 0.5rem;
background-color: #fff;
box-shadow: 0 3px 8px 6px rgba(7, 17, 27, 0.05);
overflow: hidden;
&>h1 {
padding: 0;
font-size: 1.5rem;
}
&>p {
padding: 0;
font-size: var(--vh-size-p);
line-height: 2;
}
height: 100%;
border-radius: .5rem;
background-color: var(--vh-main-color-88);
opacity: 0;
z-index: -1;
transition: all 0.18s ease-in-out;
}
}
// 设置图片懒加载样式
main>.vh-container {
img[data-vh-lz-src] {
transition: all 0.18s ease-in;
&:not(.loaded) {
filter: blur(0.88rem) brightness(1);
}
}
// 超出一行省略
.vh-ellipsis {
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
// 超出两行省略
.vh-ellipsis-2 {
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow: hidden;
}
@media screen and (max-width: 768px) {
// 手机端 禁止选择
user-select: none;
@ -183,12 +179,24 @@ main>.vh-container {
-ms-user-select: none;
}
// 设置图片懒加载样式
.main-inner>.main-inner-content,
.avatar {
img[data-vh-lz-src] {
transition: all 0.18s ease-in;
&:not(.loaded) {
filter: blur(0.88rem) brightness(1);
}
}
}
// 首次加载效果
.vh-animation.vh-animation-init {
opacity: 0;
animation: 300ms vh-init-show;
animation-fill-mode: forwards;
transition: opacity 0.16s ease-in-out, transform 0.16s ease-in-out;
transition: opacity 0.18s ease-in-out, transform 0.18s ease-in-out;
}
// swup 动画效果
@ -250,7 +258,6 @@ html.is-animating {
}
// 谷歌广告模块
.vh-ad {
position: relative;
display: block;
@ -265,8 +272,8 @@ html.is-animating {
box-sizing: border-box;
padding: 0.75rem;
border-radius: 0.5rem;
background-color: #fff;
box-shadow: 0 3px 8px 6px rgba(7, 17, 27, 0.05);
background-color: var(--vh-white-color);
box-shadow: var(--vh-box-shadow);
overflow: hidden;
&::before {
@ -329,4 +336,50 @@ html.is-animating {
0% {
height: 5px
}
}
// 动画效果
@keyframes talking-tag-active {
0% {
transform: translateX(0);
}
25% {
transform: translateX(-1.66px);
}
50% {
transform: translateX(1.66px);
}
75% {
transform: translateX(-1.66px);
}
100% {
transform: translateX(0);
}
}
@-webkit-keyframes talking-tag-active {
0% {
transform: translateX(0);
}
25% {
transform: translateX(-1.66px);
}
50% {
transform: translateX(1.66px);
}
75% {
transform: translateX(-1.66px);
}
100% {
transform: translateX(0);
}
}

68
src/styles/Reset.less Normal file
View File

@ -0,0 +1,68 @@
/* Box sizing rules */
*,
*::before,
*::after {
box-sizing: border-box;
}
/* Remove default margin */
body,
h1,
h2,
h3,
h4,
p,
pre,
code,
figure,
blockquote,
dl,
dd {
margin: 0;
}
/* Remove list styles on ul, ol elements with a list role, which suggests default styling will be removed */
ul[role="list"],
ol[role="list"] {
list-style: none;
}
/* Set core root defaults */
html:focus-within {
scroll-behavior: smooth;
}
/* Set core body defaults */
body {
min-height: 100vh;
line-height: 1.5;
}
/* A elements that don't have a class get default styles */
a:not([class]) {
text-decoration-skip-ink: auto;
}
/* Inherit fonts for inputs and buttons */
input,
button,
textarea,
select {
font: inherit;
}
/* Remove all animations and transitions for people that prefer not to see them */
@media (prefers-reduced-motion: reduce) {
html:focus-within {
scroll-behavior: auto;
}
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}

View File

@ -1,6 +1,4 @@
import { getCollection } from "astro:content";
// 获取封面图
import getCover from "@/utils/getCover";
const posts = (await getCollection("blog")).sort((a, b) => b.data.date.valueOf() - a.data.date.valueOf());
// 获取文章分类
const getCategories = () => {
@ -11,21 +9,26 @@ const getCategories = () => {
return Object.entries(categoriesList).map(([title, count]) => ({ title, count }));
}
// 获取统计数据
const getCountInfo = () => {
return { ArticleCount: posts.length, CategoryCount: getCategories().length, TagCount: getTags().length }
}
// 获取文章标签
const getTags = () => {
const frequencyMap = new Map();
// 统计每个元素出现的频率
posts.forEach(i => {
i.data.tags.forEach(tag => frequencyMap.set(tag, (frequencyMap.get(tag) || 0) + 1));
});
const sortedArray = [...frequencyMap.entries()].sort((a, b) => b[1] - a[1]);
return sortedArray.slice(0, 16).map(item => item[0]);
const tagList = posts.reduce((acc: any, i: any) => {
i.data.tags.forEach((tag: string) => {
acc[tag] = (acc[tag] || 0) + 1;
});
return acc;
}, {});
return Object.entries(tagList).sort((a: any, b: any) => b[1] - a[1]);
}
// 获取推荐文章 (给文章添加 recommend: true 字段)
const getRecommendArticles = () => {
const recommendList = posts.filter(i => i.data.recommend);
return (recommendList.length ? recommendList : posts.slice(0, 6)).map(async i => ({ title: i.data.title, date: i.data.date, id: i.data.id, cover: await getCover(i.data.cover) }))
return (recommendList.length ? recommendList : posts.slice(0, 6)).map(i => ({ title: i.data.title, date: i.data.date, id: i.data.id }))
};
export { getCategories, getTags, getRecommendArticles };
export { getCategories, getTags, getRecommendArticles, getCountInfo };