Vue项目开发 筛选组件跟表格组件结合使用,从此做一个cv工程师
不难发现后台管理系统列表页面长得基本都一样(特殊的不考虑),上面是筛选,下面是表格,表格下面是分页,然后在某个位置整几个按钮。
就以上面图片来说,其实可以分为四部分, 筛选
操作按钮
表格
分页
筛选组件
筛选组件常见的控件就是 输入框
下拉框
日期
,当然也有公司内部的一些 人员选择框
部门选择框
,其实都一样,我们试想一下,如果我们的项目里面有20个列表,每个列表里面都有4~5个筛选条件, 那我们可能会写好一份筛选的标签,进行拷贝然后修改,再想想,突然有一天老大告诉你,每一个列表都要加一个xxx筛选条件,怎么办?这样只能硬着头皮复制粘贴20次。作为一个有含量的 cv程序猿
肯定是不能这么玩的,在开发之前就要想到这些问题,应该怎么预防这些问题。
那么问题来了,我们怎么去封装筛选组件?当然在封装的时候可以根据项目的需求去进行封装,下面就开始说一下我的逻辑(不喜勿喷,不是最好,但是很香)
组件代码
g-table-filter
<template>
<div class="table-filter">
<el-row class="g-row" :gutter="20">
<slot name="prefix"></slot>
<el-col
v-for="(val, index) in data"
:key="index"
:xl="8"
:lg="8"
:md="12"
:sm="12"
:xs="24"
>
<div class="g-item">
<p class="g-label ellipsis" :title="val.name">{{ val.name }}</p>
<component
:is="val.type"
:info="val"
size="mini"
/>
</div>
</el-col>
</el-row>
<div class="g-filter-btns">
<div class="btns">
<el-button size="mini" type="primary" icon="el-icon-search" @click="handleQuery">查询</el-button>
<el-button size="mini" type="primary" plain icon="el-icon-refresh" @click="handleReset">重置</el-button>
</div>
</div>
</div>
</template>
<script>
import {
GInput,
GDate,
GSelect
} from '../types'
export default {
name: 'TableFilter',
components: {
GInput,
GDate,
GSelect
},
props: {
data: {
type: Array,
default: () => {
return []
}
},
// 是否有根据app进行查询 默认是有app查询
isApp: {
type: Boolean,
default: true
}
},
data() {
return {
arrowStatus: false
}
},
mounted() {
if (this.isApp) {
this.$store.dispatch('getAuitSuccessAppList').then(res => {
if (res.code === 0) {
this.data.unshift({
name: '所属应用',
type: 'g-select',
value: '',
model: 'appId',
placeholder: '请选择所属应用',
options: {
list: res.data.map(item => {
return { label: item.name, value: item.appId }
})
}
})
} else {
this.$message.error(res.message)
}
})
.catch(err => {
console.log(err)
})
}
},
methods: {
// 查询
handleQuery() {
const params = {}
this.data.forEach(item => {
switch (item.type) {
case 'g-date':
// 该版本先考虑范围查询
if (item.value && item.value.length > 0) {
params['beginTime'] = item.value[0]
params['endTime'] = item.value[1]
} else {
params['beginTime'] = ''
params['endTime'] = ''
}
break
default:
// 并未考虑下拉框多选的情况
params[item.model] = item.value === undefined ? '' : item.value
break
}
})
this.$emit('on-query', { ...params, pageNum: 1, pageSize: 10 })
},
// 重置
handleReset() {
this.$emit('on-reset')
this.data.forEach(val => { val['value'] = '' })
this.$parent.getResetTableData()
}
}
}
</script>
<style lang="scss">
@import "./index.scss";
</style>
g-input
<template>
<el-input
v-model="info.value"
class="g-input"
:placeholder="info.placeholder"
:size="size"
/>
</template>
<script>
export default {
name: 'GInput',
props: {
info: {
type: Object,
default: () => {
return {}
}
},
size: {
type: String,
default: 'mini'
}
}
}
</script>
思路
先看 g-table-filter
组件代码,核心就是 component
根据传过来的数据进行动态寻找相应的组件,当然代码里面内置了开始时间,结束时间的逻辑,因为项目需求,所有查询都要有这玩意,这不是重点。然后看查询方法,其实查询方法里面可以直接去调用父组件里面的查询方法,这边就是往外暴露了一个 自定义事件
这样主要是为了可以更灵活的对数据去做特殊的处理,重置方法就没啥了,把组件的数据清空,然后直接查询列表就OK了, this.$parent.getResetTableData()
很灵魂,为什么要这么使用,下面会讲到。
使用方式
可以封装成npm包
- template
<table-filter
:data="filterData"
@on-query="handleQuery"
@on-reset="handleReset"
:isApp="false">
<!--
提供了一个插槽,主要是有一些特殊的业务需求,可以手动写代码去进行实现。
-->
<template v-slot:prefix>
<el-col :xl="8" :lg="8" :md="12" :sm="12" :xs="24">
<div class="g-item">
<p class="g-label ellipsis">开发者</p>
<el-select size="small" style="width: 100%" placeholder="请选择开发者" v-model="filterParams.developerId" @change="handleChange">
<el-option v-for="val in developerList" :key="val.developerId" :label="val.name" :value="val.developerId"></el-option>
</el-select>
</div>
</el-col>
<el-col :xl="8" :lg="8" :md="12" :sm="12" :xs="24">
<div class="g-item">
<p class="g-label ellipsis">所属应用</p>
<el-select size="small" style="width: 100%" placeholder="请选择开发者" v-model="filterParams.appId">
<el-option v-for="val in appList" :key="val.appId" :label="val.name" :value="val.appId"></el-option>
</el-select>
</div>
</el-col>
</template>
</table-filter>
- js
看上面代码可以发现,筛选组件需要接收一个数组,下面就是关于数组的一些属性配置
filterData: [
{
name: '企业名称', // label名称
model: 'userName', // key
value: '', // value 传给后台的值
placeholder: '请输入企业名称',
type: 'g-input' // 组件类型
},
{
name: '企业状态',
model: 'status',
value: '',
placeholder: '请选择企业状态',
type: 'g-select',
options: { // 复杂组件的一些特殊配置,根据实际请款不过
list: [
{ label: '正常', value: 1 },
{ label: '锁定', value: 2 },
{ label: '注销', value: 0 }
]
}
},
{
name: '创建时间',
model: 'date',
value: '',
placeholder: '请选择创建时间',
type: 'g-date',
options: {
type: 'date'
}
}
]
表格组件
表格组件的思路就是,表格跟筛选进行结合封装成一个组件,具体的还是看下面代码。
<template>
<div class="page-table-card" v-loading="loading">
<div class="top-action">
<slot name="top-action" />
</div>
<el-table stripe :data="tableData" border @selection-change="handleSelectionChange">
<template slot="empty">
<NoData />
</template>
<el-table-column type="selection" align="center" header-align="center" width="55" />
<slot />
</el-table>
<el-pagination
v-if="isPagination"
class="pagination"
:current-page.sync="current"
:page-sizes="[10, 30, 40, 50]"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
:page-size="10"
background
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
</template>
<script>
export default {
name: 'PageTableCard',
props: {
tableData: {
type: Array,
default: () => {
return []
}
},
total: {
type: [Number, String],
default: 0
},
// 是否支持分页
isPagination: {
type: Boolean,
default: true
},
loading: {
type: Boolean,
default: false
},
// 数据的主键key
rowIdKey: {
type: String,
default: 'id'
}
},
data() {
return {
current: 1
}
},
methods: {
handleSizeChange(size) {
this.$emit('on-size', size)
},
handleCurrentChange(page) {
this.$emit('on-page', page)
},
// 列表选中的数据
handleSelectionChange(rows) {
this.$emit('on-check-row', rows.map(item => item[this.rowIdKey]))
}
}
}
</script>
<style lang="scss">
@import './index';
</style>