1、问题引入:在开发一个项目中,为了提升效率,通常会将项目中结构大体相同的页面封装。实现多次利用
2、实现的页面截图如下(如下截图已将页面拆分为小的模块,可以利用插槽配合组件的方式来实现)
2.1 这张截图展示的是利用动态注册组件的形式去渲染图片中的第4项(中间内容-表格部分)(图1)
2.2 利用动态注册组件的形式去渲染图片中的第4项(中间内容-表格部分)代码截图如下:(图2)
3 这张截图展示的是通过自定义列表内容(在这个项目中,由于图一的表格出现次数较多,所以将如下这种表格内容定义为自定义内容)的形式去渲染图片中的第4项(中间内容-表格部分)(图3)
代码部分
1、公共组件BaseList
<template>
<!-- 列表公共结构 -->
<div>
<!-- 1、顶部页面title(这个组件我就不放代码了,主要是样式。topTitle数据是通过父组件传递过来的) -->
<CommonTitle :topTitle="topTitle"></CommonTitle>
<!-- 2、顶部统计筛选(通过接收的isShowTopFilter数据,判断是否显示该项) -->
<div v-if="isShowTopFilter" style="height:108px;margin-top:18px;overflow:hidden">
<slot name="topFilter"></slot>
</div>
<!-- 主体部分 -->
<div style="margin-top:20px;padding:20px 20px 0;background:#fff;border-radius:4px">
<!-- 3、搜索部分 -->
<div class="search">
<div class="search-left">
<!-- 3.1 搜索项 -->
<slot name="searchOption"></slot>
<el-button type="primary" size="small" @click="onSearch" style="margin-left:30px;">查找</el-button>
</div>
<!-- 3.2 搜索右边内容(右侧按钮) -->
<slot name="searchOptionRight"></slot>
</div>
<!-- 4、列表部分 -->
<div ref="contentWrap" class="content-wrap" :style="{height: contentHight + 'px'}">
<div :style="{height: listBoxHight + 'px'}" class="list-box-wrap">
<template v-if="isCustomTable">
<div class="list-box">
<slot name="tableContent"></slot>
</div>
</template>
<template v-else>
<ul v-if="tableList.length > 0" class="list-box">
<li v-for="(item,index) in tableList" :key="index" class="item">
<component :is="tablItemComponent" :itemData="item" :index="index" :pageIndex="pageIndex" :pageSize="pageSize"></component>
</li>
</ul>
<!-- 无数据时占位 -->
<el-empty class="list-box" v-else :image-size="200" />
</template>
</div>
<!-- 5、分页 -->
<div ref="paginationWrap" style="display:flex;justify-content:flex-end;margin-right:30px">
<!-- 这是分页控件,这里我就不放具体代码了。如想了解分页控件的封装,可查看我的文章:(暂时空着,后续补上) -->
</div>
</div>
</div>
</div>
</template>
<script>
// import CommonTitle from '@/components/dailySupervision/commonTitle'
// import Pagination from '@/components/pagination/index.vue'
export default {
name: 'BaseList',
components: {
// Pagination,
// CommonTitle
},
props: {
// 页面顶部标题
topTitle: {
type: String,
required: true,
default: ''
},
// 是否自定义表格内容(slot name: tableContent)
isCustomTable: {
type: Boolean,
default: false
},
// 列表数据
tableList: {
type: Array,
required: true,
default() {return []}
},
// 列表项组件名(存放在components/table-item中)
tablItemComponentName: {
type: String,
required: true,
default: ''
},
// 是否显示顶部统计筛选
isShowTopFilter: {
type: Boolean,
default: true
},
pageIndex: {
type: Number,
default() {return 1}
},
pageSize: {
type: Number,
default() {return 10}
},
total: {
type: Number,
default() {return 0}
}
},
data() {
return {
// 列表项组件
tablItemComponent: '',
// 列表 + 间距 + 分页高度
contentHight: '300px',
// 列表高度
listBoxHight: '200px'
}
},
computed: {
},
beforeMount() {
// 注册组件
// 在动态注册组件时也需要注意`import('../table-item/' + this.tablItemComponentName + '.vue')`import里写的就是文件的路径
this.tablItemComponent=() => import('../table-item/' + this.tablItemComponentName + '.vue')
},
created() {
},
mounted() {
this.$nextTick(() => {
this.updateHeight()
window.onresize = () => this.updateHeight()
})
},
methods: {
updateHeight() {
const that = this
if(!that.$refs.paginationWrap) {
return
}
// 分页28px, 上下padding20px
const paginationH = that.$refs.paginationWrap.clientHeight // 28 + 20 + 20
// 距离底部间距
const bottomSpace = 10
const boundingClientRect = that.$refs.contentWrap.getBoundingClientRect()
that.contentHight = document.body.clientHeight - boundingClientRect.top - bottomSpace
that.listBoxHight = that.contentHight - paginationH
// 如果页面有切换动画,则需要延迟700左右,等待页面切换完毕
},
// 搜索按钮
onSearch() {
this.$emit('onSearch')
},
// 切换页事件
handlePaginationChange(obj) {
this.$emit('handlePaginationChange', obj)
},
}
}
</script>
<style lang="scss" scoped>
// 搜索区域
.search {
margin-bottom: 20px;
display: flex;
justify-content: space-between;
align-items: center;
height:34px;margin-bottom:20px;overflow:hidden;
.search-left {
display: flex;
align-items: center;
.el-select:nth-child(n+2) {
margin-left: 10px;
}
}
}
.list-box-wrap {
// 整体内容上下内边距各20px, 顶部页面标题33px, 顶部统计高度固定126px(108+外面距18), 列表内容上下内边距各20px, 列表内容上外边距20px, 搜索部分高度54px(34+下外边距20), 分页68px, 留白34px
// height:calc(100vh - 40px - 33px - 126px - 40px - 20px - 54px - 68px - 34px);
overflow-y:auto;
padding: 0 4px;
}
.list-box-wrap::-webkit-scrollbar-track-piece {
//滚动条凹槽的颜色,还可以设置边框属性
background-color: #f1f1f1;
}
.list-box-wrap::-webkit-scrollbar {
//滚动条的宽度
width: 3px;
height: 10px;
}
.list-box-wrap::-webkit-scrollbar-thumb {
//滚动条的设置
background-color: #c1c1c1;
background-clip: padding-box;
min-height: 28px;
border-radius: 8px;
}
.list-box-wrap::-webkit-scrollbar-thumb:hover {
background-color: #a8a8a8;
}
.list-box {
.item {
padding: 28px 0;
border-bottom: 1px solid #e5e5e5;
}
.flex1 {
overflow: hidden;
}
.item:first-child {
padding-top: 0;
}
}
::v-deep .el-pagination {
padding: 0 !important;
}
</style>
1.1 关于BaseList组件的注意事项
isCustomTable是否自定义表格内容(slot name: tableContent)(为true时表示自定义) 如果在父组件中不传isCustomTable或传false时,则采用图1的表格形式通过tableList去渲染tablItemComponentName列表项组件名(存放在components/table-item中) 此项配合isCustomTable和这行代码使用:
beforeMount() {
// 注册组件
this.tablItemComponent=() => import('../table-item/' + this.tablItemComponentName + '.vue')
},
-
在动态注册组件时也需要注意
import('../table-item/' + this.tablItemComponentName + '.vue')import里写的就是文件的路径 -
函数中
updateHeight的作用就是动态的计算列表区域的高度,实现整个页面列表区域Y轴滚动,其他内容固定。实现思想是根据分页容器往上的区域减去列表搜索栏区域、头部、分页高度等
第一种使用方式(isCustomTable为false时,表示图1的列表形式)
2、使用该组件
<template>
<div v-loading.fullscreen.lock="loading" class="content-root">
<base-list
ref="baseList"
topTitle='婚丧喜事台账'
:tableList="tableList"
:pageIndex="pageIndex"
:pageSize="pageSize"
:total="total"
tablItemComponentName="WeddingsAndFuneralsTableItem"
@onSearch="onSearch"
>
<template slot="topFilter">
<!-- 2、顶部统计筛选 -->
<div style="margin-top:10px">
<!-- 这里是顶部统计筛选(可以配合组件使用) -->
</div>
</template>
<!-- 3、搜索项 -->
<template slot="searchOption">
<!-- 3.1 这里是表格搜索项(可以配合组件使用) -->
</template>
<template slot="searchOptionRight">
<!-- 3.2 搜索右边内容(按钮)(可以配合组件使用) -->
</template>
</base-list>
</div>
</template>
<script>
// 公共列表组件
import BaseList from '@/components/base/BaseList.vue'
export default {
name: "weddingsAndFuneralsList",
components: {
BaseList
},
data() {
return {
loading: false, // 是否展示loading
pageIndex: 1,
pageSize: 10,
total: 0,
tableList: []
}
},
methods: {
// 搜索事件
onSearch() {
this.pageIndex = 1
// this.getPageList() // 查询列表数据
},
}
}
</script>
2.1 注意事项
tablItemComponentName为列表组件的文件名称(isCustomTable为true时,tablItemComponentName也必传,但不会起作用。所以可以用一个已存在的组件名称)onSearch方法为BaseList组件中抛出的一个查询事件,用于搜索列表(这个地方可以将查询按钮不放在组件BaseList中,均可)
第二种使用方式(isCustomTable为true时,表示图3的列表形式)
3、使用该组件(自定义表格内容图3)
<template>
<div v-loading.fullscreen.lock="loading" class="content-root">
<base-list
ref="baseList"
topTitle='决策议题管理台账'
:tableList="tableList"
:pageIndex="pageIndex"
:pageSize="pageSize"
:total="total"
:isCustomTable="true"
tablItemComponentName="WeddingsAndFuneralsTableItem"
@onSearch="onSearch"
>
<template slot="topFilter">
<!-- 2、顶部统计筛选 -->
<div style="margin-top:10px">
<!-- 这里是顶部统计筛选(可以配合组件使用) -->
</div>
</template>
<!-- 3、搜索项 -->
<template slot="searchOption">
<!-- 3.1 这里是表格搜索项(可以配合组件使用) -->
</template>
<template slot="searchOptionRight">
<!-- 3.2 搜索右边内容(按钮)(可以配合组件使用) -->
</template>
<!-- 4 主体内容区域-表格部分(可以配合组件使用) -->
<template> slot="tableContent">
</template>
</base-list>
</div>
</template>
<script>
// 公共列表组件
import BaseList from '@/components/base/BaseList.vue'
export default {
name: "IssueManageList",
components: {
BaseList
},
data() {
return {
loading: false, // 是否展示loading
pageIndex: 1,
pageSize: 10,
total: 0,
tableList: []
}
},
methods: {
// 搜索事件
onSearch() {
this.pageIndex = 1
// this.getPageList() // 查询列表数据
},
}
}
</script>