You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

184 lines
5.8 KiB
Vue

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<template>
<div class="p-6 min-h-full bg-white">
<!-- 顶部区域扁平化标题与搜索 -->
<div class="flex flex-col md:flex-row md:items-center justify-between mb-8 pb-6 border-b-2 border-gray-100">
<div class="mb-4 md:mb-0">
<h1 class="text-2xl font-black text-gray-900 tracking-tight">JS <span class="text-primary">CORE</span> NAV</h1>
<p class="text-sm text-gray-500 mt-1 font-medium">JavaScript 核心基础知识点速查</p>
</div>
<div class="relative">
<el-input
v-model="searchText"
placeholder="输入关键字过滤..."
class="custom-search !w-full md:!w-80"
clearable
>
<template #prefix>
<iconify-icon-online
icon="ep:search"
class="text-gray-400"
/>
</template>
</el-input>
</div>
</div>
<!-- 导航网格:扁平化高对比度设计 -->
<div
v-if="filteredList.length > 0"
class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4"
>
<div
v-for="(item, index) in filteredList"
:key="item.path"
class="nav-card group cursor-pointer relative overflow-hidden border-2 transition-all duration-200"
:style="{ borderColor: getThemeColor(index, 'border') }"
@click="handleNav(item)"
>
<!-- 左侧色条对比 -->
<div
class="absolute left-0 top-0 bottom-0 w-1.5"
:style="{ backgroundColor: getThemeColor(index, 'main') }"
></div>
<div class="p-5 pl-7">
<!-- 标题:高对比度加粗 -->
<h3
class="text-lg font-bold mb-2 transition-colors duration-200"
:style="{ color: getThemeColor(index, 'text') }"
>
{{ item.title }}
</h3>
<!-- 描述:简洁明了 -->
<p class="text-sm text-gray-600 leading-relaxed font-medium line-clamp-2">
{{ item.desc }}
</p>
<!-- 底部装饰线 -->
<div
class="mt-4 w-8 h-1 transition-all duration-300 group-hover:w-full"
:style="{ backgroundColor: getThemeColor(index, 'main') }"
></div>
</div>
</div>
</div>
<!-- 空状态 -->
<el-empty
v-else
description="未找到相关内容"
/>
</div>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue';
import { useRouter } from 'vue-router';
defineOptions({
name: 'JsCoreNav',
});
const router = useRouter();
const searchText = ref('');
interface NavItem {
title: string;
path: string;
desc: string;
}
/**
* ()
*/
const THEME_COLORS = [
{ main: '#3B82F6', border: '#DBEafe', text: '#1E40AF' }, //
{ main: '#EF4444', border: '#FEE2E2', text: '#991B1B' }, //
{ main: '#10B981', border: '#D1FAE5', text: '#065F46' }, // 绿
{ main: '#F59E0B', border: '#FEF3C7', text: '#92400E' }, //
{ main: '#8B5CF6', border: '#EDE9FE', text: '#5B21B6' }, //
{ main: '#EC4899', border: '#FCE7F3', text: '#9D174D' }, //
{ main: '#06B6D4', border: '#CFFAFE', text: '#155E75' }, //
];
const getThemeColor = (index: number, type: 'main' | 'border' | 'text') => {
const colorSet = THEME_COLORS[index % THEME_COLORS.length];
return colorSet[type];
};
/**
* 导航数据
*/
const navList = ref<NavItem[]>([
{
title: '变量提升与作用域',
path: '/demo/jsHoistingScope',
desc: '执行上下文、词法作用域与 var/let/const 的底层差异。',
},
{
title: '闭包与内存模型',
path: '/js/closures',
desc: '函数作用域链的延续、私有变量实现与内存泄漏规避。',
},
{
title: '原型继承链',
path: '/demo/prototype',
desc: '从 __proto__ 到 prototype构建 JavaScript 的对象继承大厦。',
},
{
title: 'This 上下文绑定',
path: '/js/this-context',
desc: '默认绑定、隐式绑定、显式绑定与箭头函数的静态指向。',
},
{
title: '事件循环模型',
path: '/demo/jsEventLoop',
desc: '宏任务与微任务的交替执行,浏览器渲染帧的调度机制。',
},
]);
const filteredList = computed(() => {
const keyword = searchText.value.trim().toLowerCase();
if (!keyword) return navList.value;
return navList.value.filter((item) => item.title.toLowerCase().includes(keyword) || item.desc.toLowerCase().includes(keyword));
});
const handleNav = (item: NavItem) => {
if (item.path.startsWith('http')) {
window.open(item.path, '_blank');
} else {
router.push(item.path).catch(() => {});
}
};
</script>
<style lang="scss" scoped>
.nav-card {
background-color: #fff;
&:hover {
transform: translateX(4px);
background-color: #f9fafb;
}
}
.custom-search {
:deep(.el-input__wrapper) {
box-shadow: none !important;
border: 2px solid #f3f4f6;
border-radius: 0;
padding: 8px 12px;
&.is-focus {
border-color: var(--el-color-primary);
}
}
}
.text-primary {
color: var(--el-color-primary);
}
</style>