完善筛选功能 新增一言 完成关于页面 🐛 解决归档页的ssg问题

This commit is contained in:
li-chx 2025-10-06 03:44:54 +08:00
parent 1cefa4c661
commit 6c963257cf
9 changed files with 207 additions and 39 deletions

View File

@ -9,15 +9,18 @@ const props = withDefaults(defineProps<{
const emits = defineEmits<{
(event: 'filterRuleChange', rule: (data: PostMetaData) => boolean): void;
}>();
const articleCount = computed(() => props.postsMetaData?.filter((post) => !post.draft && post.type === 'article').length || 0);
const announcementCount = computed(() => props.postsMetaData?.filter((post) => !post.draft && post.type === 'announcement').length || 0);
const ramblingCount = computed(() => props.postsMetaData?.filter((post) => !post.draft && post.type === 'rambling').length || 0);
const countGroup = [
{ name: '文章', count: articleCount, type: 'article' },
{ name: '絮语', count: ramblingCount, type: 'rambling' },
{ name: '公告', count: announcementCount, type: 'announcement' },
];
const categoryEnableStatus: Ref<boolean[]> = ref(Array(countGroup.length).fill(true));
const typeGroup = computed(() => {
const articleCount = computed(() => props.postsMetaData?.filter((post) => !post.draft && post.type === 'article').length || 0);
const announcementCount = computed(() => props.postsMetaData?.filter((post) => !post.draft && post.type === 'announcement').length || 0);
const ramblingCount = computed(() => props.postsMetaData?.filter((post) => !post.draft && post.type === 'rambling').length || 0);
return [
{ name: '文章', count: articleCount, type: 'article' },
{ name: '絮语', count: ramblingCount, type: 'rambling' },
{ name: '公告', count: announcementCount, type: 'announcement' },
];
});
const typeEnableStatus: Ref<boolean[]> = ref(Array(typeGroup.value.length).fill(true));
const categories = computed(() => {
const categoryMap = new Map<string, number>();
@ -43,32 +46,97 @@ const tags = computed(() => {
return tagArray;
});
function updateCategoryEnableStatus(index: number) {
if (categoryEnableStatus.value.reduce((last, cur) => last && cur, true)) {
for (let i = 0; i < categoryEnableStatus.value.length; i++) {
const categoriesEnableStatus: Ref<boolean[]> = ref(new Array(categories.value.length).fill(true));
const tagsEnableStatus: Ref<boolean[]> = ref(new Array(tags.value.length).fill(true));
function updateTypeEnableStatus(index: number) {
if (typeEnableStatus.value.reduce((last, cur) => last && cur, true)) {
for (let i = 0; i < typeEnableStatus.value.length; i++) {
if (i !== index) {
categoryEnableStatus.value[i] = false;
typeEnableStatus.value[i] = false;
}
}
} else if (!categoryEnableStatus.value.reduce((last, cur, localIndex) => last || (localIndex === index ? false : cur), false)) {
for (let i = 0; i < categoryEnableStatus.value.length; i++) {
categoryEnableStatus.value[i] = true;
} else if (!typeEnableStatus.value.reduce((last, cur, localIndex) => last || (localIndex === index ? false : cur), false)) {
for (let i = 0; i < typeEnableStatus.value.length; i++) {
typeEnableStatus.value[i] = true;
}
} else
categoryEnableStatus.value[index] = !categoryEnableStatus.value[index];
typeEnableStatus.value[index] = !typeEnableStatus.value[index];
updateRule();
}
function updateCategoryEnableStatus(index: number) {
if (categoriesEnableStatus.value.reduce((last, cur) => last && cur, true)) {
for (let i = 0; i < categoriesEnableStatus.value.length; i++) {
if (i !== index) {
categoriesEnableStatus.value[i] = false;
}
}
} else if (categoriesEnableStatus.value[index] && !categoriesEnableStatus.value.reduce((last, cur, localIndex) => last || (localIndex === index ? false : cur), false)) {
for (let i = 0; i < categoriesEnableStatus.value.length; i++) {
categoriesEnableStatus.value[i] = true;
}
} else
categoriesEnableStatus.value[index] = !categoriesEnableStatus.value[index];
updateRule();
}
function updateTagEnableStatus(index: number) {
if (tagsEnableStatus.value.reduce((last, cur) => last && cur, true)) {
for (let i = 0; i < tagsEnableStatus.value.length; i++) {
if (i !== index) {
tagsEnableStatus.value[i] = false;
}
}
} else if (tagsEnableStatus.value[index] && !tagsEnableStatus.value.reduce((last, cur, localIndex) => last || (localIndex === index ? false : cur), false)) {
for (let i = 0; i < tagsEnableStatus.value.length; i++) {
tagsEnableStatus.value[i] = true;
}
} else
tagsEnableStatus.value[index] = !tagsEnableStatus.value[index];
updateRule();
}
function updateRule() {
const enabledCategories = categoriesEnableStatus.value.reduce((last, cur, localIndex) => {
if (cur) last.add(categories.value[localIndex]![0]);
return last;
}, new Set<string>());
const enabledTags = tagsEnableStatus.value.reduce((last, cur, localIndex) => {
if (cur) last.add(tags.value[localIndex]![0]);
return last;
}, new Set<string>());
// const enable
emits('filterRuleChange', (post) => {
for (let i = 0; i < categoryEnableStatus.value.length; i++) {
if (categoryEnableStatus.value[i] && post.type === countGroup[i]!.type) {
console.log('filter', post.title, 'true');
return true;
// type check
let tempAns = false;
for (let i = 0; i < typeEnableStatus.value.length; i++) {
if (typeEnableStatus.value[i] && post.type === typeGroup.value[i]!.type) {
tempAns = true;
}
}
console.log('filter', post.title, 'false');
return false;
// category check
if (tempAns)
tempAns = false;
else
return false;
if (post.category && enabledCategories.has(post.category))
tempAns = true;
else tempAns = !post.category && enabledCategories.size === categories.value.length;
// tag check
if (tempAns)
tempAns = false;
else
return false;
if (tagsEnableStatus.value.length === enabledTags.size)
tempAns = true;
else for (const tag of post.tags || []) {
if (enabledTags.has(tag)) {
tempAns = true;
break;
}
}
return tempAns;
});
}
</script>
@ -83,11 +151,11 @@ function updateRule() {
<hr class="border-0 h-[1px] bg-old-neutral-600 mt-3 mb-1"/>
<div class="flex mt-4">
<div
v-for="(data, index) of countGroup"
v-for="(data, index) of typeGroup"
:key="data.name"
class="flex items-center flex-col flex-1 text-xl cursor-pointer hover:text-sky-400 dark:hover:text-[#cccaff] transition-colors duration-300"
:class="{'text-old-neutral-400': !categoryEnableStatus[index]}"
@click="updateCategoryEnableStatus(index)"
:class="{'text-old-neutral-400': !typeEnableStatus[index]}"
@click="updateTypeEnableStatus(index)"
>
<div>{{ data.name }}</div>
<div>{{ data.count }}</div>
@ -102,7 +170,10 @@ function updateRule() {
<hr class="border-0 h-[1px] bg-old-neutral-600 mt-3 mb-1"/>
<div
v-for="([name,count],index) of categories" :key="index"
class="flex justify-between pl-4 pr-4 hover:text-sky-400 dark:hover:text-[#cccaff] transition-colors duration-300">
class="flex justify-between pl-4 pr-4 hover:text-sky-400 dark:hover:text-[#cccaff] transition-colors duration-300"
:class="{'text-old-neutral-400': !categoriesEnableStatus[index]}"
@click="updateCategoryEnableStatus(index)"
>
<div class="flex items-center">
<Icon
name="material-symbols:book-outline"
@ -123,7 +194,10 @@ function updateRule() {
<div class="flex flex-wrap">
<div
v-for="([name,count],index) of tags" :key="index"
class="flex items-center justify-between text-[15px] pl-2 pr-2 m-1 rounded-2xl shadow-[0_0_0_1px_#888] hover:text-sky-400 dark:hover:text-[#cccaff] hover:shadow-[0_0_0_1px_#00bcff] dark:hover:shadow-[0_0_0_1px_#cccaff] transition-colors transition-shadow duration-300">
class="flex items-center justify-between text-[15px] pl-2 pr-2 m-1 rounded-2xl shadow-[0_0_0_1px_#888] hover:text-sky-400 dark:hover:text-[#cccaff] hover:shadow-[0_0_0_1px_#00bcff] dark:hover:shadow-[0_0_0_1px_#cccaff] transition-all duration-300"
:class="{'text-old-neutral-400': !tagsEnableStatus[index]}"
@click="updateTagEnableStatus(index)"
>
<Icon
name="clarity:hashtag-solid"
size="17"

View File

@ -69,7 +69,7 @@ onMounted(() => {
</script>
<template>
<div class="w-full min-h-[100vh] h-full">
<div class="bg-old-neutral-50 dark:bg-[#0b0d0d] w-full min-h-[100vh] h-full transition-colors duration-500">
<UApp>
<div
:class=" (collapsed ? 'h-[20vh]': 'h-[40vh]')"
@ -147,6 +147,7 @@ onMounted(() => {
</div>
</div>
<!-- content -->
<div class="flex justify-center items-center duration-500 bg-white dark:bg-[#16191b] h-full">
<div
:class="collapsed ? 'min-h-[80vh]' : 'min-h-[60vh]'"

View File

@ -2,6 +2,12 @@
import useColorModeStore from '~/stores/colorModeStore';
import breakpointsHelper from '~/utils/BreakpointsHelper';
const hitokoto = ref('加载中...');
onMounted(async () => {
const src = await $fetch<{ hitokoto: string; from_who?: string; from?: string }>('https://v1.hitokoto.cn?c=k');
hitokoto.value = `${src.hitokoto} —— ${src.from_who ? src.from_who : '佚名'}${src.from ? `${src.from}` : '未知来源'}`;
});
</script>
<template>
@ -68,6 +74,9 @@ import breakpointsHelper from '~/utils/BreakpointsHelper';
</template>
<template #footer>
<div class="w-full flex flex-col justify-center items-center p-10 text-old-neutral-500">
<div>
{{ hitokoto }}
</div>
<div>
© 2025 随机存取. 由Lichx制作
</div>

View File

@ -0,0 +1,45 @@
<script setup lang="tsx">
const techStack = ['Vue', 'Nuxt', 'TypeScript', 'Python', 'Java', 'C#', 'Rust'];
const techStackPercent = [86, 80, 92, 75, 60, 90, 50];
const techStackIconNames = ['mdi:vuejs', 'lineicons:nuxt', 'mdi:language-typescript', 'mdi:language-python', 'mdi:language-java', 'mdi:language-csharp', 'mdi:language-rust'];
const techStackThemeColors = ['#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4', '#FFEAA7', '#DDA0DD', '#FFB347', '#98D8C8', '#F7DC6F', '#BB8FCE'];
const mounted = ref(false);
onMounted(() => {
mounted.value = true;
});
</script>
<template>
<div>
<div class="light:bg-old-neutral-200 dark:bg-old-neutral-800 p-5 mt-3 transition-colors duration-300">
<div class="text-2xl pb-2">关于作者</div>
lichx目前就读于武汉理工大学技术涉猎广泛但不精仍在持续学习中 <br/>
</div>
<div class="light:bg-old-neutral-200 dark:bg-old-neutral-800 p-5 mt-3 transition-colors duration-300">
<div class="text-2xl pb-2">技术栈相对熟练度</div>
<TechStackCard
v-if="mounted" async-key="about page" :tech-stack="techStack"
:tech-stack-percent="techStackPercent" :tech-stack-icon-names="techStackIconNames"
:tech-stack-theme-colors="techStackThemeColors"/>
</div>
<div class="light:bg-old-neutral-200 dark:bg-old-neutral-800 p-5 mt-3 transition-colors duration-300">
<div class="text-2xl pb-2">在其他渠道关注 / 联系我</div>
<a href="https://github.com/li-chx" title="github不常用">
<icon name="mdi:github" class="inline-block w-10 h-10 mr-4"/>
</a>
<a href="https://git.lichx.top/li_chx" title="gitea主要代码托管平台">
<icon name="pajamas:gitea" class="inline-block w-10 h-10 mr-4"/>
</a>
<a href="https://leetcode.cn/u/gallant-paynekan/" title="leetcode刷点水题骗骗自己">
<icon name="tabler:brand-leetcode" class="inline-block w-10 h-10 mr-4"/>
</a>
<a href="mailto:751176501@qq.com" title="能不能看见全看运气">
<icon name="material-symbols:mail-rounded" class="inline-block w-10 h-10"/>
</a>
</div>
</div>
</template>
<style scoped>
</style>

View File

@ -0,0 +1,8 @@
<template>
<div class="light:bg-old-neutral-200 dark:bg-old-neutral-800 p-5 transition-colors duration-300">
<div class="text-2xl pb-2">关于本站</div>
本站随机存取起这个名字纯粹是因为作者想不出来了最后基于我对该网站的需求决定叫随机存取<br/>
这个网站最重要的还是记录我认为有趣的 学习 / 调试 过程便于我随时取用所以写的时候完全就是梦到哪写到哪<br/>
但是如果本人闲暇之余写的文章能帮到你当是莫大的荣幸<br/>
</div>
</template>

View File

@ -1,11 +1,18 @@
<script setup lang="ts">
import AuthorDescription from '~/pages/index/about/compoents/AuthorDescription.vue';
import SiteDescription from '~/pages/index/about/compoents/SiteDescription.vue';
</script>
<template>
<div>
<div class="mt-6 mb-6 w-full">
<SiteDescription />
<AuthorDescription />
</div>
</div>
</template>
<style scoped>
</style>
</style>

View File

@ -4,15 +4,23 @@ import type { PostMetaData } from '~/types/PostMetaData';
import TimeLine from '~/pages/index/archive/components/TimeLine.vue';
import type { RadioGroupItem } from '@nuxt/ui';
const { data: srcPostsMetaData } = useAsyncData(async () => sortMetaData((await queryCollection('content').all()).map((x) => toMetaDataType(x)), 'published_at', true));
const srcPostsMetaData = ref<PostMetaData[]>([]);
const postsMetaData = ref<PostMetaData[]>([]);
const currentChoice = ref<'time' | 'category'>('time');
async function loadPostsMetaData() {
srcPostsMetaData.value = sortMetaData((await queryCollection('content').all()).map((x) => toMetaDataType(x)), 'published_at', true) || [];
srcPostsMetaData.value = srcPostsMetaData.value.filter((x) => !x.draft);
postsMetaData.value = srcPostsMetaData.value;
}
await loadPostsMetaData();
watch(srcPostsMetaData, () => {
postsMetaData.value = srcPostsMetaData.value?.filter((x) => !x.draft) || [];
postsMetaData.value = srcPostsMetaData.value || [];
});
const currentChoice = ref<'time' | 'category'>('time');
const choiceItems = ref<RadioGroupItem>([
{ label: '时间', value: 'time' },
{ label: '类别', value: 'category' },
@ -21,7 +29,7 @@ const choiceItems = ref<RadioGroupItem>([
</script>
<template>
<div>
<div class="table w-full mt-6">
<div class="table w-full mt-6 mb-6">
<div class="sticky top-16 float-left bg-old-neutral-200 dark:bg-old-neutral-800 max-h-[calc(100vh-4rem)]">
<div class="relative duration-500 transition-all xl:w-80 w-0 mr-2/3 overflow-hidden">
<div class="w-80 top-0 left-0 text-gray-800 dark:text-white p-5">

View File

@ -5,7 +5,6 @@ import ArticleHeader from '~/pages/index/article/[articleID]/components/ArticleH
const articleId = useRoute().params.articleID as string;
const { data: article } = useAsyncData(async () => await queryCollection('content').where('id', '=', articleId).first());
const editorId = 'article-previewer';
</script>

View File

@ -10,6 +10,7 @@ const postsMetaData = ref<PostMetaData[]>([]);
async function loadPostsMetaData() {
srcPostsMetaData.value = sortMetaData((await queryCollection('content').all()).map((x) => toMetaDataType(x)), 'published_at', true) || [];
srcPostsMetaData.value = srcPostsMetaData.value.filter((x) => !x.draft);
postsMetaData.value = srcPostsMetaData.value;
}
@ -34,7 +35,6 @@ function filterRuleChange(rule: (data: PostMetaData) => boolean) {
<div class="sticky top-16 float-left max-h-[calc(100vh-4rem)]">
<div class="relative duration-500 transition-all xl:w-80 w-0 overflow-hidden">
<div class="w-80 top-0 left-0 text-gray-800 dark:text-white">
<!-- <PersonalCard/>-->
<ArticleDescriptionCards
v-if="postsMetaData"
class="mb-5" :posts-meta-data="srcPostsMetaData!"
@ -46,15 +46,32 @@ function filterRuleChange(rule: (data: PostMetaData) => boolean) {
<!-- <ArticleCard class="mb-6 w-full transition-shadow duration-300 shadow-lg hover:shadow-old-neutral-600"/>-->
<div
v-for="post in postsMetaData" :key="post.id"
class="mb-6 w-full transition-shadow duration-300 shadow-lg hover:shadow-old-neutral-600 hover:cursor-pointer">
class="w-full transition-shadow duration-300 shadow-lg hover:shadow-old-neutral-600 hover:cursor-pointer">
<ArticleCard
v-if="!post.draft && post.type === 'article'"
class="w-full"
class="mb-6 w-full"
:meta-data="post"
@click="toArticlePage(post)"/>
<SimpleCard
v-else-if="!post.draft && (post.type === 'rambling' || post.type === 'announcement')"
class="mb-6 w-full"
:meta-data="post"/>
<div v-else-if="post.draft">
</div>
<div v-else>
{{post}}
</div>
</div>
<div v-if="postsMetaData.length === 0" class="w-full">
<div
class="w-full light:bg-old-neutral-200 dark:bg-old-neutral-800 transition-all duration-300 shadow-lg hover:shadow-old-neutral-600 hover:cursor-pointer">
<div class="pt-5 text-center text-2xl">
没有找到符合条件的文章
</div>
<div class="pt-3 pb-3 text-center text-sm text-old-neutral-500">
tips类型分类标签间的关系为且一类筛选下各个选项间的关系为或
</div>
</div>
</div>
</div>
</div>