233 lines
5.0 KiB
Vue
233 lines
5.0 KiB
Vue
<script setup lang="ts">
|
||
import { computed } from 'vue'
|
||
import { ref } from 'vue'
|
||
import type { BusinessType } from '@/types/api'
|
||
import type { Case } from '@/types/business/cases'
|
||
|
||
interface Props {
|
||
cases: Case[]
|
||
}
|
||
|
||
const props = defineProps<Props>()
|
||
|
||
const emit = defineEmits<{ categoryChange: [category: BusinessType | 'all'] }>()
|
||
|
||
const activeCategory = ref<BusinessType | 'all'>('all')
|
||
|
||
const categories: Array<{ label: string; value: BusinessType | 'all' }> = [
|
||
{ label: '全部', value: 'all' },
|
||
{ label: '互联网软件', value: 'internet' },
|
||
{ label: '硬件电路', value: 'hardware' },
|
||
{ label: '物联网全案', value: 'iot' },
|
||
{ label: '生产制造', value: 'manufacturing' },
|
||
]
|
||
|
||
function switchCategory(value: BusinessType | 'all'): void {
|
||
activeCategory.value = value
|
||
emit('categoryChange', value)
|
||
}
|
||
|
||
const visibleCases = computed<Case[]>(() => props.cases.slice(0, 4))
|
||
</script>
|
||
|
||
<template>
|
||
<section class="home-cases">
|
||
<div class="home-cases__head">
|
||
<h3 class="home-cases__title">案例库精选</h3>
|
||
<div class="home-cases__tabs">
|
||
<button
|
||
v-for="cat in categories"
|
||
:key="cat.value"
|
||
class="home-cases__tab"
|
||
:class="{ 'is-active': activeCategory === cat.value }"
|
||
@click="switchCategory(cat.value)"
|
||
>
|
||
{{ cat.label }}
|
||
</button>
|
||
</div>
|
||
<RouterLink to="/cases" class="home-cases__more link">
|
||
查看全部 >
|
||
</RouterLink>
|
||
</div>
|
||
|
||
<div class="home-cases__grid">
|
||
<article
|
||
v-for="item in visibleCases"
|
||
:key="item.id"
|
||
class="home-cases__card"
|
||
@click="$router.push(`/cases/${item.id}`)"
|
||
>
|
||
<div class="home-cases__cover">
|
||
<img
|
||
v-if="item.cover"
|
||
v-lazy="item.cover"
|
||
class="home-cases__img"
|
||
:alt="item.title"
|
||
/>
|
||
<div v-else class="home-cases__img home-cases__img--placeholder" />
|
||
</div>
|
||
|
||
<div class="home-cases__body">
|
||
<h4 class="home-cases__name">
|
||
{{ item.title }}
|
||
</h4>
|
||
<ul class="home-cases__meta">
|
||
<li>客户类型:{{ item.clientType }}</li>
|
||
<li>所属行业:{{ item.industry }}</li>
|
||
<li>规模:{{ item.scale }}</li>
|
||
<li>项目周期:{{ item.duration }}</li>
|
||
<li>服务团队:{{ item.teamSize }}人</li>
|
||
<li>完成时间:{{ item.finishDate }}</li>
|
||
</ul>
|
||
<span class="home-cases__view">查看详情></span>
|
||
</div>
|
||
</article>
|
||
</div>
|
||
</section>
|
||
</template>
|
||
|
||
<style lang="less" scoped>
|
||
.home-cases {
|
||
padding: @spacing-xl;
|
||
border: none;
|
||
border-radius: @radius-base;
|
||
background: @white;
|
||
|
||
&__head {
|
||
.flex(row, flex-start, center);
|
||
gap: @spacing-lg;
|
||
margin-bottom: @spacing-lg;
|
||
}
|
||
|
||
&__title {
|
||
font-size: @font-size-xl;
|
||
font-weight: 600;
|
||
color: @home-ink;
|
||
}
|
||
|
||
&__tabs {
|
||
.flex(row, center);
|
||
gap: @spacing-lg;
|
||
}
|
||
|
||
&__tab {
|
||
position: relative;
|
||
height: auto;
|
||
padding: 0;
|
||
border: 0;
|
||
background: transparent;
|
||
color: @home-ink;
|
||
font-size: @font-size-md;
|
||
font-weight: 500;
|
||
transition: color 0.2s;
|
||
|
||
&.is-active {
|
||
background: transparent;
|
||
color: @home-tab-active;
|
||
|
||
&::after {
|
||
content: '';
|
||
position: absolute;
|
||
right: 0;
|
||
bottom: -8px;
|
||
left: 0;
|
||
height: 2px;
|
||
background: @primary-color;
|
||
}
|
||
}
|
||
}
|
||
|
||
&__more {
|
||
margin-left: auto;
|
||
font-size: @font-size-md;
|
||
}
|
||
|
||
&__grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(4, 1fr);
|
||
gap: @spacing-lg;
|
||
}
|
||
|
||
&__card {
|
||
overflow: hidden;
|
||
min-width: 0;
|
||
min-height: 220px;
|
||
box-sizing: border-box;
|
||
border: 1px solid @border-color;
|
||
border-radius: @radius-base;
|
||
background: @white;
|
||
box-shadow: none;
|
||
cursor: pointer;
|
||
transition:
|
||
transform 0.2s,
|
||
box-shadow 0.2s,
|
||
border-color 0.2s;
|
||
|
||
&:hover {
|
||
border-color: @primary-color;
|
||
box-shadow: @shadow-base;
|
||
transform: translateY(-2px);
|
||
|
||
.home-cases__view {
|
||
color: @primary-color;
|
||
}
|
||
}
|
||
}
|
||
|
||
&__cover {
|
||
position: relative;
|
||
height: 88px;
|
||
overflow: hidden;
|
||
}
|
||
|
||
&__img {
|
||
width: 100%;
|
||
height: 100%;
|
||
object-fit: cover;
|
||
|
||
&--placeholder {
|
||
background: linear-gradient(135deg, @primary-light, @bg-color);
|
||
}
|
||
}
|
||
|
||
&__body {
|
||
padding: 0 20px 10px 20px;
|
||
}
|
||
|
||
&__name {
|
||
margin: 21px 0 0;
|
||
font-size: @font-size-md;
|
||
font-weight: 500;
|
||
line-height: 22px;
|
||
color: @home-body;
|
||
.multi-ellipsis(1);
|
||
}
|
||
|
||
&__meta {
|
||
display: grid;
|
||
grid-template-columns: 1fr 1fr 1fr;
|
||
gap: 0 @spacing-lg;
|
||
margin-top: 20px;
|
||
font-size: 13px;
|
||
line-height: 20px;
|
||
color: @home-body;
|
||
|
||
li {
|
||
.ellipsis();
|
||
}
|
||
}
|
||
|
||
&__view {
|
||
display: block;
|
||
width: fit-content;
|
||
margin: 6px 0 0 auto;
|
||
padding: 0;
|
||
border: 0;
|
||
color: @primary-strong;
|
||
font-size: 13px;
|
||
line-height: 15px;
|
||
transition: color 0.2s;
|
||
}
|
||
}
|
||
</style>
|