我们经常会封装一下组件 使用props emit 来进行 父子组件之间的 绑定 其实这种方式我们可以解决开发中的大部分问题 但是有更好的方式 v-model 语法糖。
我直接上大家能够看懂 并且能够使用的部分 我拿我的 抽屉组件来看
需求
我现在有个作品信息 很多地方都需要使用 并且 组件内容是以一样的 显示的信息 等 可能只有按钮不一样 我们可以吧按钮写成插槽
现在我直接上组件代码
<template>
<div class="book-detail-component">
<!-- 作品信息 -->
<el-drawer
v-model="workDetailFlag"
title="作品信息"
size="60%"
direction="rtl"
:before-close="handleWorkDetailFlagClose"
>
<el-tabs v-model="activeName" @tab-click="handleClick">
<el-tab-pane label="作品信息" name="first">
<div class="book-container">
<div class="data-set">
<div class="title">基础信息</div>
<!-- <div class="set">
<el-button type="primary" round>签约</el-button>
</div> -->
<!-- 使用插槽,父组件可以在这里插入自定义内容 -->
<slot name="action-buttons">
<!-- 默认内容 -->
<el-button type="primary" round>签约</el-button>
</slot>
</div>
<div class="data-info">
<div class="item">
<div class="label">书籍封面</div>
<div class="value">
<img
src="../../../assets/book.jpg"
style="width: 120px; border-radius: 6px"
alt=""
/>
</div>
</div>
<div class="item">
<div class="label">书籍名称</div>
<div class="value">测试环境2</div>
</div>
<div class="item">
<div class="label">目标读者</div>
<div class="value">女频</div>
</div>
<div class="item">
<div class="label">标签</div>
<div class="value">女频</div>
</div>
<div class="item">
<div class="label">标签</div>
<div class="value">阿勒、阿明</div>
</div>
<div class="item">
<div class="label">作品简介</div>
<div class="value">
这个是测试数据数据的健身房好的减肥还是得减肥的精神发挥大数据恢复时间的恢复
</div>
</div>
</div>
<div class="data-set">
<div class="title">其他信息</div>
<div class="set"></div>
</div>
<div class="other-info">
<div class="row">
<div class="item">
<div class="label">书号</div>
<div class="value">2025101510151508547</div>
</div>
<div class="item">
<div class="label">签约状态</div>
<div class="value">未签约</div>
</div>
</div>
<div class="row">
<div class="item">
<div class="label">创建时间</div>
<div class="value">2025-10-15 10:15:15</div>
</div>
<div class="item">
<div class="label">更新状态</div>
<div class="value">连载状态</div>
</div>
</div>
<div class="row">
<div class="item">
<div class="label">上架起始章节</div>
<div class="value">无</div>
</div>
</div>
</div>
<div class="info-card">
<div class="card-header">
<h3>章节信息</h3>
</div>
<div class="card-body">
<el-table :data="chapterList" style="width: 100%" class="chapter-table">
<el-table-column prop="name" label="章节名称/ID" min-width="180">
<template #default="{ row }">
<div class="chapter-name">
<span class="name">{{ row.name }}</span>
<span class="id">{{ row.id }}</span>
</div>
</template>
</el-table-column>
<el-table-column prop="updateTime" label="更新时间" width="150" />
<el-table-column prop="payStatus" label="付费状态" width="80">
<template #default="{ row }">
<el-tag :type="row.payStatus === '付费' ? 'danger' : 'info'" size="small">
{{ row.payStatus }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="auditStatus" label="审核状态" width="80">
<template #default="{ row }">
<el-tag type="success" size="small">{{ row.auditStatus }}</el-tag>
</template>
</el-table-column>
<el-table-column prop="clickCount" label="点击人数" width="80" align="center">
<template #default="{ row }">
<span class="click-count">{{ row.clickCount }}</span>
</template>
</el-table-column>
<el-table-column label="操作" width="120" fixed="right">
<!-- <template #default="{ row }">
<el-button type="primary" link size="small">编辑</el-button>
<el-button type="primary" link size="small">查看</el-button>
</template> -->
</el-table-column>
</el-table>
<div class="pagination">
<div></div>
<el-pagination background layout="prev, pager, next" :total="1000" />
</div>
</div>
</div>
</div>
</el-tab-pane>
</el-tabs>
</el-drawer>
</div>
</template>
<script setup>
import { ref, reactive, computed } from 'vue'
const props = defineProps({
visible: {
type: Boolean,
default: false
}
})
const emit = defineEmits(['update:visible'])
const workDetailFlag = computed({
get: () => props.visible,
set: (value) => emit('update:visible', value)
})
const handleWorkDetailFlagClose = () => {
workDetailFlag.value = false
done()
}
// 章节列表数据
const chapterList = ref([
{
name: '第2章 PM测试第二次',
id: '2013年',
updateTime: '2025-09-29 11:12:01',
payStatus: '付费',
auditStatus: '已发布',
clickCount: 0
},
{
name: '第1章 PM测试',
id: '2013年',
updateTime: '2025-09-28 11:52:26',
payStatus: '付费',
auditStatus: '已发布',
clickCount: 0
}
])
const activeName = ref('first')
//切换tabs
const handleClick = (tab, event) => {
console.log(tab, event)
}
</script>
<style lang="less" scoped>
.book-detail-component {
.book-container {
.data-set {
display: flex;
align-items: center;
justify-content: space-between;
margin-top: 40px;
.title {
color: #000;
font-weight: 700;
}
}
.data-info {
margin-top: 20px;
.item {
display: flex;
.label {
width: 100px;
color: #999;
}
.value {
flex: 1;
margin-left: 30px;
color: #000;
}
}
.item:nth-child(n + 2) {
margin-top: 15px;
}
}
.info-card {
background: #fff;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
margin-bottom: 20px;
.card-header {
padding: 16px 20px;
border-bottom: 1px solid #e8e8e8;
display: flex;
justify-content: space-between;
align-items: center;
h3 {
margin: 0;
font-size: 16px;
font-weight: 600;
color: #303133;
}
}
.card-body {
padding: 20px;
.pagination {
margin-top: 20px;
display: flex;
align-items: center;
justify-content: space-between;
}
}
}
.other-info {
margin-top: 20px;
.row {
display: flex;
align-items: center;
margin-top: 15px;
.item {
display: flex;
width: 500px;
.label {
width: 100px;
color: #999;
}
.value {
flex: 1;
margin-left: 30px;
color: #000;
}
}
.item:nth-child(n + 2) {
margin-left: 15px;
}
}
}
}
}
</style>
const props = defineProps({
visible: {
type: Boolean,
default: false
}
})
const emit = defineEmits(['update:visible'])
const workDetailFlag = computed({
get: () => props.visible,
set: (value) => emit('update:visible', value)
})
这里面主要需要看这个代码 组件接受了props 以及自定义时间
我们一般 会使用computed来写一下复杂的 get 和set 以后都可以按照我的来写 当然你如果不设置visible 这个名称 默认是modalvalue 设置的话 就是你设置的
这个事件是 updated:visible
插槽的话 就是 你需要哪里需呀父组件 传递自己的东西 使用默认插槽就好了
父组件使用
<BookDetailCom v-model:visible="bookDetailVisible"></BookDetailCom>
使用就很简单了
我们见过的 所有的组件库 vue3 版本的其实我感觉是这样封装的