效果如下图:在线预览
APIs
Card
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
width | 卡片宽度,单位 px |
number | string | ‘auto’ |
title | 卡片标题 | string | slot | undefined |
extra | 卡片右上角的操作区域 | string | slot | undefined |
bordered | 是否有边框 | boolean | true |
loading | 当卡片内容还在加载中时,可以用 loading 展示一个占位 |
boolean | false |
size | 卡片的尺寸 | ‘default’ | ‘small’ | ‘default’ |
headStyle | 标题区域自定义样式 | CSSProperties | {} |
bodyStyle | 内容区域自定义样式 | CSSProperties | {} |
创建卡片组件Card.vue
其中引入使用了以下组件和工具函数:
<script setup lang="ts">
import { computed } from 'vue'
import type { CSSProperties } from 'vue'
import Skeleton from '../skeleton'
import { useSlotsExist } from '../utils'
interface Props {
width?: number | string // 卡片宽度,单位 px
title?: string // 卡片标题 string | slot
extra?: string // 卡片右上角的操作区域 string | slot
bordered?: boolean // 是否有边框
loading?: boolean // 当卡片内容还在加载中时,可以用 loading 展示一个占位
size?: 'default' | 'small' // 卡片的尺寸
headStyle?: CSSProperties // 标题区域自定义样式
bodyStyle?: CSSProperties // 内容区域自定义样式
}
const props = withDefaults(defineProps<Props>(), {
width: 'auto',
title: undefined,
extra: undefined,
bordered: true,
loading: false,
size: 'default',
headStyle: () => ({}),
bodyStyle: () => ({})
})
const cardWidth = computed(() => {
if (typeof props.width === 'number') {
return props.width + 'px'
}
return props.width
})
const slotsExist = useSlotsExist(['title', 'extra'])
const showHeader = computed(() => {
return slotsExist.title || slotsExist.extra || props.title || props.extra
})
</script>
<template>
<div
class="m-card"
:class="{ 'card-bordered': bordered, 'card-small': size === 'small' }"
:style="`width: ${cardWidth};`"
>
<div class="m-card-head" :style="headStyle" v-if="showHeader">
<div class="m-head-wrapper">
<div class="head-title">
<slot name="title">{
{ title }}</slot>
</div>
<div class="head-extra">
<slot name="extra">{
{ extra }}</slot>
</div>
</div>
</div>
<div class="m-card-body" :style="bodyStyle">
<Skeleton :title="false" :loading="loading">
<slot></slot>
</Skeleton>
</div>
</div>
</template>
<style lang="less" scoped>
.m-card {
font-size: 14px;
color: rgba(0, 0, 0, 0.88);
line-height: 1.5714285714285714;
position: relative;
background: #ffffff;
border-radius: 8px;
text-align: left;
.m-card-head {
display: flex;
justify-content: center;
flex-direction: column;
min-height: 56px;
margin-bottom: -1px;
padding: 0 24px;
color: rgba(0, 0, 0, 0.88);
font-weight: 600;
font-size: 16px;
background: transparent;
border-bottom: 1px solid #f0f0f0;
border-radius: 8px 8px 0 0;
.m-head-wrapper {
width: 100%;
display: flex;
align-items: center;
.head-title {
display: inline-block;
flex: 1;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.head-extra {
margin-inline-start: auto;
font-weight: normal;
font-size: 14px;
}
}
}
.m-card-body {
padding: 24px;
border-radius: 0 0 8px 8px;
}
}
.card-bordered {
border: 1px solid #f0f0f0;
}
.card-small {
.m-card-head {
min-height: 38px;
padding: 0 12px;
font-size: 14px;
}
.m-card-body {
padding: 12px;
}
}
</style>
在要使用的页面引入
其中引入使用了以下组件:
<script setup lang="ts">
import Card from './Card.vue'
import { ref } from 'vue'
const loading = ref(true)
</script>
<template>
<div>
<h1>{
{ $route.name }} {
{ $route.meta.title }}</h1>
<h2 class="mt30 mb10">基本使用</h2>
<Card title="Default size card" :width="300">
<template #extra>
<a href="#">more</a>
</template>
<p>card content</p>
<p>card content</p>
<p>card content</p>
</Card>
<h2 class="mt30 mb10">小尺寸卡片</h2>
<Card size="small" title="Small size card" :width="300">
<template #extra>
<a href="#">more</a>
</template>
<p>card content</p>
<p>card content</p>
<p>card content</p>
</Card>
<h2 class="mt30 mb10">简洁卡片</h2>
<Card :width="300">
<p>Card content</p>
<p>Card content</p>
<p>Card content</p>
</Card>
<h2 class="mt30 mb10">预加载卡片</h2>
<Space vertical>
<Card :loading="loading" title="Card title" :width="300">
<p>Card content</p>
<p>Card content</p>
<p>Card content</p>
</Card>
<Button @click="loading = !loading">Toggle loading</Button>
</Space>
<h2 class="mt30 mb10">在灰色背景上使用无边框的卡片</h2>
<div style="display: inline-block; background: #ececec; padding: 30px; border-radius: 8px">
<Card title="Card title" :bordered="false" :width="300">
<p>Card content</p>
<p>Card content</p>
<p>Card content</p>
</Card>
</div>
<h2 class="mt30 mb10">自定义标题和内容区域样式</h2>
<Card
title="Default size card"
:width="300"
:headStyle="{ fontSize: '18px', color: '#fff', backgroundColor: '#1677ff' }"
:bodyStyle="{ fontSize: '16px', color: '#fff', backgroundColor: '#52c41a' }"
>
<template #extra>
<a href="#">more</a>
</template>
<p>card content</p>
<p>card content</p>
<p>card content</p>
</Card>
<h2 class="mt30 mb10">内部卡片</h2>
<Card title="Card title" :width="360">
<p style="font-size: 14px; color: rgba(0, 0, 0, 0.85); margin-bottom: 16px; font-weight: 500">Group title</p>
<Card title="Inner card title">
<template #extra>
<a href="#">More</a>
</template>
Inner Card content
</Card>
<Card title="Inner card title" :style="{ marginTop: '16px' }">
<template #extra>
<a href="#">More</a>
</template>
Inner Card content
</Card>
</Card>
<h2 class="mt30 mb10">栅格卡片</h2>
<div style="background-color: #ececec; padding: 20px; border-radius: 8px">
<Row :gutter="16">
<Col :span="8">
<Card title="Card title" :bordered="false">
<p>card content</p>
</Card>
</Col>
<Col :span="8">
<Card title="Card title" :bordered="false">
<p>card content</p>
</Card>
</Col>
<Col :span="8">
<Card title="Card title" :bordered="false">
<p>card content</p>
</Card>
</Col>
</Row>
</div>
</div>
</template>