yk-web/src/components/CaseSection.vue

89 lines
3.1 KiB
Vue

<script setup>
import { computed, ref } from 'vue'
import { caseCategories, cases } from '../data/homepage'
import LucideHospitalIcon from './LucideHospitalIcon.vue'
import SectionTitle from './SectionTitle.vue'
const active = ref(caseCategories[0])
const filteredCases = computed(() => cases.filter(item => item.category === active.value))
const activeIndex = computed(() => caseCategories.findIndex(category => category === active.value))
const categoryLineMap = {
国家医学中心: ['国家医学', '中心'],
公立医院: ['公立医院'],
妇幼保健客户: ['妇幼保健', '客户'],
医疗集团客户: ['医疗集团', '客户']
}
const getCategoryLines = (category) => categoryLineMap[category] ?? [category]
const selectCategory = (category) => {
active.value = category
}
const handleCategoryKeydown = (event, index) => {
const keyMap = {
ArrowRight: 1,
ArrowDown: 1,
ArrowLeft: -1,
ArrowUp: -1
}
if (event.key === 'Home' || event.key === 'End' || keyMap[event.key]) {
event.preventDefault()
} else {
return
}
const nextIndex =
event.key === 'Home'
? 0
: event.key === 'End'
? caseCategories.length - 1
: (index + keyMap[event.key] + caseCategories.length) % caseCategories.length
selectCategory(caseCategories[nextIndex])
document.getElementById(`case-filter-${nextIndex}`)?.focus()
}
</script>
<template>
<section id="cases" class="case-section page-section page-section--compact">
<div class="container">
<SectionTitle title="客户案例" />
<div class="case-layout">
<aside class="case-filter" role="tablist" aria-label="客户案例分类">
<button
v-for="(category, index) in caseCategories"
:key="category"
:id="`case-filter-${index}`"
type="button"
:class="['case-filter-btn', { active: category === active }]"
role="tab"
:aria-selected="category === active"
:aria-controls="'case-panel'"
:tabindex="category === active ? 0 : -1"
@click="selectCategory(category)"
@keydown="handleCategoryKeydown($event, index)"
>
<span class="case-filter-text">
<span v-for="line in getCategoryLines(category)" :key="line">{{ line }}</span>
</span>
</button>
<span class="case-tab-indicator" :style="{ transform: `translateX(${activeIndex * 100}%)` }"></span>
</aside>
<div id="case-panel" class="case-grid" role="tabpanel" :aria-label="active">
<article v-for="item in filteredCases" :key="`${active}-${item.name}`" class="case-card">
<div :class="['case-logo-slot', item.logoFit && `case-logo-slot--${item.logoFit}`, { 'case-logo-slot--placeholder': !item.logo }]">
<img v-if="item.logo" :src="item.logo" :alt="`${item.name.replace(/\n/g, '')} logo`" />
<span v-else class="case-logo-fallback">
<LucideHospitalIcon :size="30" />
</span>
</div>
<p>{{ item.name }}</p>
</article>
</div>
</div>
</div>
</section>
</template>