167 lines
3.7 KiB
Vue
167 lines
3.7 KiB
Vue
<template>
|
||
<div class="api-reference-container">
|
||
<!-- 顶部标题+搜索栏 -->
|
||
<div class="header-bar">
|
||
<div class="main-title">{{ mainTitle }}</div>
|
||
<div v-if="showFilter" class="filter-wrap">
|
||
<span class="filter-label">过滤</span>
|
||
<input
|
||
v-model="filterKeyword"
|
||
class="filter-input"
|
||
placeholder="搜索内容"
|
||
/>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 各个大分组:全局API、组合式API -->
|
||
<div class="group-wrapper" v-for="group in filteredGroupList" :key="group.groupTitle">
|
||
<div class="group-title">{{ group.groupTitle }}</div>
|
||
<div class="card-row">
|
||
<!-- 子卡片 -->
|
||
<div class="sub-card" v-for="card in group.list" :key="card.subTitle">
|
||
<div class="sub-title">{{ card.subTitle }}</div>
|
||
<RouteLink
|
||
class="item-line"
|
||
v-for="item in card.items"
|
||
:key="item.link"
|
||
:to="item.link"
|
||
>
|
||
{{ item.name }}
|
||
</RouteLink>
|
||
</div>
|
||
</div>
|
||
<hr class="group-divider" />
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref, computed } from 'vue'
|
||
import { RouteLink } from 'vuepress/client'
|
||
|
||
const props = defineProps({
|
||
mainTitle: {
|
||
type: String,
|
||
required: true,
|
||
default: 'API 参考'
|
||
},
|
||
pageData: {
|
||
type: Array,
|
||
required: true,
|
||
default: () => []
|
||
},
|
||
showFilter: {
|
||
type: Boolean,
|
||
default: false
|
||
}
|
||
})
|
||
|
||
const filterKeyword = ref('')
|
||
|
||
// 搜索过滤逻辑:匹配分组标题、子卡片标题、条目名称
|
||
const filteredGroupList = computed(() => {
|
||
if (!filterKeyword.value) return props.pageData
|
||
const kw = filterKeyword.value.toLowerCase()
|
||
return props.pageData
|
||
.map(group => {
|
||
const filteredCards = group.list
|
||
.map(card => {
|
||
const matchItems = card.items.filter(i => i.name.toLowerCase().includes(kw))
|
||
const cardMatch = card.subTitle.toLowerCase().includes(kw)
|
||
return {
|
||
...card,
|
||
items: cardMatch ? card.items : matchItems
|
||
}
|
||
})
|
||
.filter(card => card.items.length > 0 || card.subTitle.toLowerCase().includes(kw))
|
||
return { ...group, list: filteredCards }
|
||
})
|
||
.filter(g => g.list.length > 0 || g.groupTitle.toLowerCase().includes(kw))
|
||
})
|
||
</script>
|
||
|
||
<style scoped>
|
||
.api-reference-container {
|
||
width: 100%;
|
||
margin: 16px 0;
|
||
}
|
||
.header-bar {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 16px;
|
||
}
|
||
.main-title {
|
||
width: 100%;
|
||
font-size: 32px;
|
||
font-weight: 600;
|
||
color: #1f2937;
|
||
padding-bottom: 40px;
|
||
margin: 20px 0;
|
||
border-bottom: 1px solid #e7e7e7;
|
||
}
|
||
.filter-wrap {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
}
|
||
.filter-label {
|
||
color: #6b7280;
|
||
font-size: 15px;
|
||
}
|
||
.filter-input {
|
||
border: 1px solid #d1d5db;
|
||
border-radius: 6px;
|
||
padding: 6px 10px;
|
||
width: 180px;
|
||
font-size: 15px;
|
||
}
|
||
.filter-input:focus {
|
||
outline: none;
|
||
border-color: #3eaf7c;
|
||
box-shadow: 0 0 0 2px rgba(62, 175, 124, 0.15);
|
||
}
|
||
|
||
.group-title {
|
||
font-size: 22px;
|
||
font-weight: 600;
|
||
color: #1f2937;
|
||
margin: 24px 0 14px;
|
||
}
|
||
.card-row {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 16px;
|
||
}
|
||
.sub-card {
|
||
background: #f7f8f9;
|
||
border-radius: 8px;
|
||
padding: 18px 20px;
|
||
min-width: 240px;
|
||
flex: 1;
|
||
max-width: calc(33% - 12px);
|
||
}
|
||
.sub-title {
|
||
color: #3eaf7c;
|
||
font-weight: 600;
|
||
font-size: 16px;
|
||
margin-bottom: 12px;
|
||
}
|
||
.item-line {
|
||
display: block;
|
||
color: #334155;
|
||
font-size: 15px;
|
||
line-height: 1.7;
|
||
margin: 4px 0;
|
||
text-decoration: none;
|
||
}
|
||
.item-line:hover {
|
||
color: #3eaf7c;
|
||
}
|
||
.group-divider {
|
||
border: none;
|
||
border-top: 1px solid #e5e7eb;
|
||
margin: 28px 0;
|
||
}
|
||
</style>
|