前端开发设计-常用数据查询设计
本文纯个人刚开始尝试开发总结,写的不好,大家随便看看。另外我这还有个前端小群(9个人),基本纯问题回答,有想加的可以微我:youqian_clw。发广告的就别来了,谢谢呀。
常用的数据查询一般由3个组件组成,一个是生成查询条件的form组件,一个是生成表格的table组件,一个是生成分页的pagination组件。
常见的数据查询如此,有时候还会左侧有组织树,点击树节点进行数据查询。但这个必要性并不高,或者说关联性不高,我们要设计的是 常用的数据查询封装组件,而非高定制性的数据查询组件。
一、查询条件设计
查询条件,一般如下图:
这是我用element的form组件设计,下面是直接用element官网form组件生成的:
很明显,排版并不好看,所以我们需要先进行样式优化。
另外考虑查询条件需要支持多种多样,比如文本输入,单选,多选,远程搜索,日期,范围日期等等。
首先先生成组件
因为使用的是element,所以直接复制官网的form组件,然后把可能的查询条件格式一股脑扔进去,当然,都是常用的,特殊比较困难的可以自己单独设计。然后就生成了这样的效果:
然后处理样式
然后我们用grid布局进行排版,form组件上设置样式:
display: grid;
grid-template-columns: repeat(4, 25%);
效果如下,看起来有点顺序了:
然后对输入框进行处理,因为element支持左侧文本这块长度的设定,那么只要处理好输入框这块就行了,让它100%占据除文本外的内容即可。
很快效果就完成了:
接着处理dom
样式完成,我们需要对dom进行整理。抽离共用部分,查询条件数据类型设置为数组,遍历数组进行查询条件渲染。数组的每个元素都是一个对象,里面配置了这个查询条件的各种所需,包括key,条件名,条件类型,如果类型是下拉框,加个list参数配置下拉选项。
上面是整理前,下面是整理后:
看起来舒服多了。
二、表格+分页设计
我个人喜欢把两者设计在一个组件中,如果我不需要分页,我加个v-if判断即可。
这个组件设计主要是布局排版,其他没什么难度。对了,table的高度要高度自适应,所以需要在页面初始化时以及监听resize,设置table的高度,逻辑就是:
this.tableHeight = this.$refs.tablePagination.clientHeight - this.$refs.pagination?.clientHeight || 0;
超级简单,把大容器高度算出来,减掉非table组件的高度就是table的高度了。
我觉得还是直接贴代码大家自己在项目里试试,对,我不想写了,以后再重新编辑
前缀:vue-cli搭建,elementui引入。
查询条件组件:
<!--
condition.vue
查询条件格式类型:
- 输入框:
- 下拉框:单选下拉框,多选下拉框,固定下拉框,搜索下拉框,远程搜索下拉框
- 日期框:单时间,范围时间,单日期,范围日期
formList具体格式:
key: form绑定的key值。
defaultValue: 默认值。
type: formItem类型,有text-输入框文本,select-单选下拉框,mSelect-多选下拉框,rSelect-远程搜索下拉框,time-单时间,
rTime-范围时间,date-单日期,rDate-范围日期,radio-单选框。
list: 下拉选项。
label: label名。
placeholder: 提示信息。
isShow: 是否显示。
-->
<template>
<div class="condition">
<el-form ref="form" :inline="true" :model="form" class="form" :style="{gridTemplateColumns: `repeat(${cols}, ${100/cols}%)`}" :size="size" :label-width="labelWidth">
<template v-for="(item, index) in formList">
<el-form-item :label="item.label" :prop="item.key">
<!-- 文本输入框 -->
<el-input v-if="item.type === 'text'" v-show="item.isShow" class="width100" v-model="form[item.key]" :placeholder="item.placeholder"></el-input>
<!-- 单选下拉框 -->
<el-select v-else-if="item.type === 'select'" v-show="item.isShow" class="width100" v-model="form[item.key]" :placeholder="item.placeholder" clearable>
<el-option v-for="li in item.list" :label="li.label" :value="li.value"></el-option>
</el-select>
<!-- 多选下拉框 -->
<el-select v-else-if="item.type === 'mSelect'" v-show="item.isShow" class="width100" v-model="form[item.key]" :placeholder="item.placeholder" multiple collapse-tags>
<el-option v-for="li in item.list" :label="li.label" :value="li.value"></el-option>
</el-select>
<!-- 单时间 -->
<el-time-select v-else-if="item.type === 'time'" v-show="item.isShow" class="width100" v-model="form[item.key]" :placeholder="item.placeholder"></el-time-select>
<!-- 范围时间 -->
<el-time-picker v-else-if="item.type === 'rTime'" v-show="item.isShow" class="width100" is-range v-model="form[item.key]" range-separator="-" start-placeholder="开始时间" end-placeholder="结束时间" :placeholder="item.placeholder"></el-time-picker>
<!-- 单日期 -->
<el-date-picker v-else-if="item.type === 'date'" v-show="item.isShow" class="width100" v-model="form[item.key]" type="date" :placeholder="item.placeholder"></el-date-picker>
<!-- 范围日期 -->
<el-date-picker v-else-if="item.type === 'rDate'" v-show="item.isShow" class="width100" v-model="form[item.key]" type="daterange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" :placeholder="item.placeholder"></el-date-picker>
</el-form-item>
</template>
<el-form-item>
<el-button type="primary" :size="size" @click="handlerSubmit">查询</el-button>
<el-button type="ghost" :size="size" @click="resetForm">重置</el-button>
<i class="el-icon-d-arrow-right" :style="{transform: isShowAllCondition ? 'rotate(270deg)' : 'rotate(90deg)'}" @click="isShowAllCondition = !isShowAllCondition"></i>
</el-form-item>
</el-form>
</div>
</template>
<script>
export default {
name: "condition",
props: {
labelWidth: {
type: String,
default: '120px'
},
cols: {
type: Number,
default: 4
},
formList: {
type: Array,
default: []
},
size: {
type: String,
default: 'small' // medium / small / mini
}
},
data() {
return {
isShowAllCondition: false,
form: {}
}
},
watch: {
formList(val) {
val.forEach(item => {
// 生成form
this.form[item.key] = item.defaultValue || ''
});
}
},
methods: {
// 查询
handlerSubmit() {
this.$emit('query', this.form);
},
// 重置
resetForm() {
debugger;
this.$refs.form.resetFields();
}
},
mounted() {
// js动态设置样式变量
document.querySelector('body').style.setProperty('--query-form-label-width', this.labelWidth);
}
}
</script>
<style>
/* css的变量用法 */
:root{
--query-form-label-width: 120px;
}
</style>
<style scoped lang="scss">
$query-form-label-width: var(--query-form-label-width);
.form{
text-align: left;
display: grid;
grid-template-columns: repeat(4, 25%);
/deep/.el-form-item__content{
width: calc(100% - var(--query-form-label-width));
}
.width100{
width: 100%;
}
.el-icon-d-arrow-right{
cursor: pointer;
margin-left: 10px;
}
}
</style>
表格+分页组件:
<!-- tablePagination.vue -->
<template>
<div class="table-pagination" ref="tablePagination">
<div class="table">
<el-table :data="tableData" :size="size" :height="tableHeight" border stripe style="width: 100%">
<template v-for="(item, index) in columns">
<el-table-column :prop="item.key" :label="item.label" :width="item.width"></el-table-column>
</template>
</el-table>
</div>
<div class="pagination" ref="pagination">
<el-pagination
@size-change="changePageSize"
@current-change="changePage"
:current-page="currentPage"
:page-sizes="pageSizeOpt"
:page-size="currentPageSize"
layout="total, sizes, prev, pager, next, jumper"
:total="tableData.length">
</el-pagination>
</div>
</div>
</template>
<script>
export default {
name: "tablePagination",
props: {
tableData: {
type: Array,
default: []
},
columns: {
type: Array,
default: []
},
size: {
type: String,
default: 'small'
}
},
data() {
return {
currentPageSize: 10,
currentPage: 1,
pageSizeOpt: [10, 20, 50, 100],
tableHeight: 200
}
},
methods: {
changePage(val) {
this.currentPage = val;
this.getTableData();
},
changePageSize(val) {
this.currentPageSize = val;
this.getTableData();
},
getTableData() {
this.$emit('getTableData', {
page: this.currentPage,
pageSize: this.currentPageSize
})
},
initTableHeight() {
this.tableHeight = this.$refs.tablePagination.clientHeight - this.$refs.pagination?.clientHeight || 0;
}
},
mounted() {
this.$nextTick(() => {
this.initTableHeight();
window.addEventListener('resize', this.initTableHeight);
});
},
activated() {
window.addEventListener('resize', this.initTableHeight);
},
deactivated() {
window.removeEventListener('resize', this.initTableHeight);
},
beforeDestroy() {
window.removeEventListener('resize', this.initTableHeight);
}
}
</script>
<style scoped lang="scss">
.table-pagination{
flex: 1;
display: flex;
flex-direction: column;
.table{
flex: 1;
}
.pagination{
text-align: right;
}
}
</style>
归总,数据查询页面展示:
<!-- 查询组件 -->
<template>
<div class="query-container">
<condition :form-list="formList" @query="query"></condition>
<tablePagination ref="tablePagination" :columns="columns" :table-data="tableData" @getTableData="getTableData"></tablePagination>
</div>
</template>
<script>
// 引入查询条件组件和表格+分页组件
import Condition from '../components/query/condition';
import TablePagination from '../components/query/tablePagination';
export default {
name: "searchComponent",
components: {
Condition,
TablePagination
},
data() {
return {
formList: [
{
key: 'user',
label: '文本输入框',
defaultValue: '',
placeholder: '请输入审批人',
type: 'text',
isShow: true
},
{
key: 'select',
label: '单选下拉框',
type: 'select',
isShow: true,
list: [
{
label: '选项1',
value: 'val1'
},
{
label: '选项2',
value: 'val2'
}
]
},
{
key: 'mSelect',
label: '多选下拉框',
type: 'mSelect',
isShow: true,
list: [
{
label: '选项1',
value: 'val1'
},
{
label: '选项2',
value: 'val2'
}
]
},
{
key: 'time',
label: '单时间',
isShow: true,
type: 'time'
},
{
key: 'rTime',
label: '范围时间',
isShow: true,
type: 'rTime'
},
{
key: 'date',
label: '单日期',
isShow: true,
type: 'date'
},
{
key: 'rDate',
label: '范围日期',
isShow: true,
type: 'rDate'
},
],
tbData: [
{
key1: '1',
key2: '2',
key3: '3'
},
{
key1: '11',
key2: '22',
key3: '33'
},
{
key1: '111',
key2: '222',
key3: '333'
},
{
key1: '1',
key2: '2',
key3: '3'
},
{
key1: '11',
key2: '22',
key3: '33'
},
{
key1: '111',
key2: '222',
key3: '333'
},
{
key1: '1',
key2: '2',
key3: '3'
},
{
key1: '11',
key2: '22',
key3: '33'
},
{
key1: '111',
key2: '222',
key3: '333'
},
{
key1: '1',
key2: '2',
key3: '3'
},
{
key1: '11',
key2: '22',
key3: '33'
},
{
key1: '111',
key2: '222',
key3: '333'
},
{
key1: '1',
key2: '2',
key3: '3'
},
{
key1: '11',
key2: '22',
key3: '33'
},
{
key1: '111',
key2: '222',
key3: '333'
},
],
tableData: [],
columns: [
{
key: 'key1',
label: '标题1'
},
{
key: 'key2',
label: '标题2'
},
{
key: 'key3',
label: '标题3'
}
]
}
},
methods: {
query(val) {
let tablePagination = this.$refs.tablePagination;
this.getTableData({
...val,
page: tablePagination.currentPage,
pageSize: tablePagination.currentPageSize
})
},
getTableData(param) {
this.tableData = this.tbData.slice(param.page-1, param.page * param.pageSize);
}
}
}
</script>
<style scoped>
.query-container{
height: 100%;
display: flex;
flex-direction: column;
}
</style>