206 lines
4.4 KiB
Vue
206 lines
4.4 KiB
Vue
<script setup lang="ts">
|
||
import { computed } from 'vue'
|
||
import { formatBusinessType } from '@/utils/format'
|
||
import type { Case } from '@/types/business/cases'
|
||
|
||
interface Props {
|
||
item: Case
|
||
}
|
||
|
||
const props = defineProps<Props>()
|
||
|
||
const emit = defineEmits<{
|
||
view: [id: string]
|
||
favorite: [id: string]
|
||
}>()
|
||
|
||
const meta = computed<Array<{ label: string; value: string }>>(() => [
|
||
{ label: '客户类型', value: props.item.clientType || '--' },
|
||
{ label: '所属行业', value: props.item.industry || '--' },
|
||
{ label: '规模', value: props.item.scale || '--' },
|
||
{ label: '项目周期', value: props.item.duration || '--' },
|
||
{ label: '服务团队', value: props.item.teamSize ? `${props.item.teamSize}人` : '--' },
|
||
{ label: '完成时间', value: props.item.finishDate ? props.item.finishDate.replace(/-/g, '/') : '--' },
|
||
])
|
||
</script>
|
||
|
||
<template>
|
||
<article class="case-card" @click="emit('view', item.id)">
|
||
<div class="case-card__cover">
|
||
<!-- TODO(蓝湖): 替换案例封面切图 -->
|
||
<img v-if="item.cover" v-lazy="item.cover" class="case-card__img" :alt="item.title">
|
||
<div v-else class="case-card__img case-card__img--placeholder" />
|
||
|
||
<span class="case-card__tag" :class="`case-card__tag--${item.category}`">
|
||
{{ formatBusinessType(item.category) }}
|
||
</span>
|
||
|
||
<button
|
||
class="case-card__fav"
|
||
:class="{ 'is-active': item.isFavorite }"
|
||
:aria-label="item.isFavorite ? '取消收藏' : '收藏案例'"
|
||
@click.stop="emit('favorite', item.id)"
|
||
>
|
||
<svg class="case-card__fav-icon" viewBox="0 0 24 24" aria-hidden="true">
|
||
<path d="M12 20.5s-7.2-4.6-8.4-9A5.1 5.1 0 0 1 12 5.6a5.1 5.1 0 0 1 8.4 5.9c-1.2 4.4-8.4 9-8.4 9Z" />
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
|
||
<div class="case-card__body">
|
||
<h3 class="case-card__title">
|
||
{{ item.title }}
|
||
</h3>
|
||
|
||
<ul class="case-card__meta">
|
||
<li v-for="row in meta" :key="row.label">
|
||
<span class="case-card__label">{{ row.label }}:</span>{{ row.value }}
|
||
</li>
|
||
</ul>
|
||
|
||
<span class="case-card__more">查看详情 ></span>
|
||
</div>
|
||
</article>
|
||
</template>
|
||
|
||
<style lang="less" scoped>
|
||
.case-card {
|
||
overflow: hidden;
|
||
height: 267px;
|
||
box-sizing: border-box;
|
||
border: 1px solid @cases-card-border;
|
||
border-radius: @radius-base;
|
||
background: @white;
|
||
cursor: pointer;
|
||
transition: box-shadow 0.2s, transform 0.2s;
|
||
|
||
&:hover {
|
||
box-shadow: @shadow-lg;
|
||
transform: translateY(-2px);
|
||
}
|
||
|
||
&__cover {
|
||
position: relative;
|
||
height: 137px;
|
||
overflow: hidden;
|
||
}
|
||
|
||
&__img {
|
||
width: 100%;
|
||
height: 100%;
|
||
object-fit: cover;
|
||
|
||
&--placeholder {
|
||
background: @primary-strong;
|
||
}
|
||
}
|
||
|
||
&__tag {
|
||
position: absolute;
|
||
top: 87px;
|
||
left: 22px;
|
||
height: 44px;
|
||
padding: 0 14px;
|
||
border-radius: @radius-sm;
|
||
background: @primary-strong;
|
||
color: @white;
|
||
font-size: 28px;
|
||
font-weight: 500;
|
||
line-height: 44px;
|
||
|
||
&--hardware {
|
||
background: @tag-hardware;
|
||
}
|
||
|
||
&--iot {
|
||
background: @tag-iot;
|
||
}
|
||
|
||
&--manufacturing {
|
||
background: @tag-manufacturing;
|
||
}
|
||
|
||
&--other {
|
||
background: @tag-other;
|
||
}
|
||
}
|
||
|
||
&__fav {
|
||
position: absolute;
|
||
top: 10px;
|
||
right: 10px;
|
||
.flex(row, center);
|
||
width: 28px;
|
||
height: 28px;
|
||
border-radius: @radius-round;
|
||
background: rgba(255, 255, 255, 0.85);
|
||
transition: background 0.2s;
|
||
|
||
&:hover {
|
||
background: @white;
|
||
}
|
||
|
||
&-icon {
|
||
width: 15px;
|
||
height: 15px;
|
||
fill: none;
|
||
stroke: @text-color-secondary;
|
||
stroke-width: 1.8;
|
||
transition: all 0.2s;
|
||
}
|
||
|
||
&.is-active {
|
||
background: @white;
|
||
|
||
.case-card__fav-icon {
|
||
fill: @danger-color;
|
||
stroke: @danger-color;
|
||
}
|
||
}
|
||
}
|
||
|
||
&__body {
|
||
position: relative;
|
||
padding: 18px 20px 0;
|
||
height: 129px;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
&__title {
|
||
margin: 0;
|
||
font-size: 16px;
|
||
font-weight: 500;
|
||
line-height: 21.5px;
|
||
color: @cases-text;
|
||
.ellipsis();
|
||
}
|
||
|
||
&__meta {
|
||
display: grid;
|
||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||
gap: 21.5px 12px;
|
||
margin-top: 10px;
|
||
font-size: 10px;
|
||
line-height: 14px;
|
||
color: @cases-text;
|
||
|
||
li {
|
||
overflow-wrap: anywhere;
|
||
}
|
||
}
|
||
|
||
&__label {
|
||
color: inherit;
|
||
}
|
||
|
||
&__more {
|
||
position: absolute;
|
||
right: 20px;
|
||
bottom: 14px;
|
||
color: @primary-strong;
|
||
font-size: 12px;
|
||
font-weight: 400;
|
||
}
|
||
}
|
||
</style>
|