Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”
This article has participated in the “Digitalstar Project” and won a creative gift package to challenge the creative incentive money.
function
Continue with the previous articlePackage a better table component based on the Element-UI framework, we start writing the query component. What does it take to query a component? Below I have drawn a rough prototype that basically describes what the query component needs to do.
- Basic query function
[Input conditions, select drop-down data and click Query]
- Add the query drop-down panel
[Many queries, one line is not fit, need to give a more query dropdown panel, remove the default query conditions, all drop down panel, specific default query conditions put several, can be made through the input parameter control]
- Add conditional display
[If there are more query panels, do not open the query panel, it is not clear which criteria the data is found.]
- Add a function button area
[Generally place function buttons for batch operation such as add, delete and change]
Basic query function
The query function needs to be implemented with element-UI input boxes, drop-down boxes, date boxes and other components. With our component, we hope to use an array to describe the query condition, and then the component identifies and renders the condition according to the array. First, we need to define what this array actually looks like.
let seachArr = [
{
type: 'input'./ / input box
name: 'name'.// label Specifies the name of the condition to display
key: 'name' // The field passed to the background}]Copy the code
At present, the most basic is the three fields, the meaning of the field has been written in the code annotation. There may be additional fields that can be extended in this object. For example, dropdown data is usually delivered to the front end through the background interface. We can add a URL, param, method to define the interface name, parameters, and request method. Of course, we’ll talk about that later when we implement it. So let’s start developing.
The query conditions are initialized
Mounted (searchArr) ¶ After passing in searchArr data, we must first traverse it and set the response data to our searchForm.
// methods
initSearch () {
const searchArr = this.searchArr
if (searchArr && searchArr.length) {
let searchForm = {}
searchArr.forEach((val) = > {
searchForm[val.key] = ' '
})
this.searchForm = searchForm
}
}
// mounted
mounted () {
this.initSearch()
},
Copy the code
To render the page
Because we design the query page is in two parts, including the query criteria displayed outside and display in more query criteria. In this case, if we define the structure in the template, we will find that we need to write the same code twice. If the rear extension of the new function up, it is a bad physical work. Therefore, we will write JSX in the render function, which can make the structure more flexible, the code can be better reuse. That’s it, so start writing code
// props passes an array of searchArr to describe what the query criteria are
/** * Query condition description */
searchArr: {
type: Array.default: () = >[]},// Data defines two variables, searchForm: the query field passed to the background, and selectOption: the data drop-down
searchForm: {},
selectOption: {}
// mounted Initializes data
initSearch () {
const searchArr = this.searchArr
if (searchArr && searchArr.length) {
let searchForm = {}
searchArr.forEach((val) = > {
searchForm[val.__key] = ' ' // Take the user-defined field, place it in the searchForm, and assign an empty string
this.setOption(val) // If it is a dropdown, assign the dropdown list to the selectOption
})
this.searchForm = searchForm
}
},
// methods Set drop-down data
async setOption (val) {
if(~ ['select'.'mulSelect'].indexOf(val.__type)) {
// The first case is if the pull-down data is written locally
if (val.data && Array.isArray(val.data)) {
this.$set(this.selectOption, val.__key, val.data)
} else if (val.fetchUrl) {
// In the second case, if the drop-down data is passed from the background interface
const result = await request({
url: val.fetchUrl,
method: val.method || 'post'
})
if (result && result.data && result.data.success) {
this.$set(this.selectOption, val.__key, result.data.list || [])
}
}
}
},
Copy the code
Ok, after we finish the initialization, we can start rendering. As we mentioned earlier, JSX will be more flexible with the render function, so let’s start writing this part of the work.
/ / render function
<div class="searchTable">
<el-form
inline={true}
props={{model: this.searchForm}}
ref="searchForm"
class="searchForm">
{
searchArr.map((o) => {
return components[o.__type] ? components[o.__type](h, o, this) : ''
})
}
<el-form-item>
<el-button size="small" type="primary" icon="el-icon-search" on-click={ queryTable} >The query</el-button>
<el-form-item>
<el-link
style="margin-left: 10px;"
type="primary"
underline={ false }
on-click={() = >{ this.moreShow = ! This.moreshow}}> More queries</el-link>
</el-form-item>
</el-form-item>
<div class="more-search animated"
class={ this.moreShow ? 'fadeInDown' : 'fadeOutUp'}v-show={ this.moreShow} >/ /... More queries</div>
</el-form>
</div>
Copy the code
One thing to note about JSX operations is that in the latest VUe-CLI, the use of v-model and other commands in JSX is already supported, but the use of el-input in element-UI will give an error, and the use of v-show commands below will work. But the v-model is just a syntax sugar. We use the input event to get the latest input data and then assign it to the searchForm. This code is pulled out, because it is used for both externally displayed and condensed queries, so you don’t have to write repetitive logic. Here are the components pulled out
/** * Why is the input field removed? Here we just need to do a table query. * If you want to wrap a popover form submission component. You can reuse the logic here *@param { Function } H vue provides h functions *@param { Object } Item Description object * written by the user@param { Object } Vm vue instance */
export const input = function (h, item, vm) {
const { searchForm } = vm
return (
<el-form-item label={item.label}>
<el-input
size="small"
on-input={(v)= >{searchForm[item.__key] = v}} props={{value: searchForm[item.__key], placeholder: "Input information ",... item }} ></el-input>
</el-form-item>)}/ * * * *@param { Function } H vue provides h functions *@param { Object } Item Description object * written by the user@param { Object } Vm vue instance */
export const select = function (h, item, vm) {
const {
searchForm = {},
selectOption = {},
} = vm
/** * listen for pull-down change events *@param { String | Number | Boolean } Value Select the value * of the drop-down data@param { Object } value
*/
const selectChange = function (value, item) {
searchForm[item.__key] = value
vm.$emit('on-change', value)
}
return (
<el-form-item label={item.label}>
<el-select
props={{
value: searchForm[item.__key].placeholder:"= = =Please select a= = =", filterable: true, clearable: true, ... item }} on-change={ (val) => { selectChange(val, item) } } size="small">
{
selectOption[item.__key] && selectOption[item.__key].map((o) => {
return (
<el-option
key={ o.value }
label={ o.text }
value={ o.value } >
</el-option>)})}</el-select>
</el-form-item>)}/** ** multi-select drop-down box, you can add two attributes on the basis of single, write a method, the user can slightly fewer a few words. *@param { Function } H vue provides h functions *@param { Object } Item Description object * written by the user@param { Object } Vm vue instance */
export const mulSelect = function (h, item, vm) {
item['multiple'] = true
item['collapse-tags'] = true
return select(h, item, vm)
}
Copy the code
In fact, the above method is to separate several element components into independent methods. When we use external components, we can directly match the corresponding component rendering according to the type passed by the user. And if you want to add a new query component later, you only need to extend it here, and do not need to change it elsewhere. Easier to maintain. In addition, we have some default behavior inside the component, such as default search drop-down, default empty button, etc., if the system does not need to write the corresponding attribute overwrite in searchArr.
return components[o.__type] ? components[o.__type](h, o, this) : ' '
Copy the code
The basic rendering is written, and then we use the components outside
searchArr: [
{
__type: 'input'.label: 'name'.__key: 'name'
},
{
__type: 'select'.label: 'gender'.__key: 'sex'.fetchUrl: '/getSelect'.method: 'post'
// data: [
// { text: '男', value: '1' },
// {text: 'female ', value: '0'},
// ]},]Copy the code
Write the corresponding mock data
Mock.mock(/\/getSelect/.'post'.() = > {
return {
status: 200.success: true.message: 'Obtain success'.list: [{text: 'the man'.value: '1'
},
{
text: 'woman'.value: '2'}].total: data.list.length
}
})
Copy the code
Ok, so that’s a simple way to render the condition as an array description. Next, we have to doMore queries
Panel query conditions.
More queries and display optimizations
The number of query conditions displayed outside can be displayed according to the number given by the user. For more queries, the number of query conditions displayed in a row can also be displayed according to the parameters given by the user
=============== props ===============
/** * How many external query criteria are displayed */
frontCount: {
type: Number.default: 2
},
/** * how many more query criteria are displayed */
backCount: {
type: Number.default: 3
}
=============== end props ===============
=============== computed ===============
// Query criteria displayed on the page
frontSearchArr: function () {
return this.searchArr.slice(0.this.frontCount)
},
// The conditions displayed in more queries
backSearchArr: function () {
return this.searchArr.slice(this.frontCount, this.searchArr.length)
},
// Return the width
getSpan: function () {
const yu = this.backSearchArr.length % this.backCount / / remainder
const duan = 24 / this.backCount // The width of each condition
if (yu === 0) {
return 24
} else {
return 24 - duan
}
}
=============== end computed ===============
============== render ==============
<div
style={`width:The ${this.backCount * 265 + 30}px`}
class={ this.moreShow? ` ${className} fadeInDown` :` ${className} fadeOutUp`}v-show={ this.moreShow} >
<el-row>
{
backSearchArr.map((o) => {
return (
<el-col span={24 / this.backCount} >
{ components[o.__type] ? components[o.__type](h, o, this) : '' }
</el-col>)})}<el-col class="searchBtn" span={ getSpan } >
<el-button size="small" type="primary" icon="el-icon-search" on-click={ queryTable} >The query</el-button>
<el-button size="small" type="default" icon="el-icon-upload2" on-click={() = >{ this.moreShow = ! This.moreshow}}> Shrink</el-button>
</el-col>
</el-row>
</div>
============== end render ==============
Copy the code
Set the external and internal display data in the calculation properties. Cut the data into two parts. The search component has a fixed length of 265. Then calculate the width of more query panels based on the criteria, and calculate the span width given to each row of data based on the getSpan calculation property. The query and shrink buttons calculate where to put them based on the number of query criteria.
After testing, the effect basically meets the requirements.
Drop-down list for component linkage query
For example, if we need to write tertiary linkage with our query component, how should we implement it?
- choose
province
After, need to choose according toprovince
Load the correspondingThe city
The drop-down data - If you remove
province
After, need to put the correspondingprovince
andThe city
The drop down is also cleared
Linkage is to do these two functions well. One is to load the corresponding data, one is to clear the corresponding data. Ok, now that we know what we need to do, let’s go ahead and do it.
{
__type: 'select'.__key: 'province'.__fetchUrl: '/getProvince'.__method: 'get'.__nextKey: 'city'.__nextFetch: '/getCity'.__nextParam: ['province'].__nextMethod: 'get'.__emptyArr: ['city'.'district'].label: 'province'.labelWidth: '40px'}, {__type: 'select'.__key: 'city'.__method: 'get'.__nextKey: 'district'.__nextFetch: '/getDistrict'.__nextParam: ['province'.'city'].__nextMethod: 'get'.__emptyArr: ['district'].label: 'the city'.labelWidth: '40px'}, {__type: 'select'.__key: 'district'.label: 'area'.labelWidth: '40px',}Copy the code
__fetchUrl: ‘/getProvince’ and __method: ‘get’ define the request interface to retrieve the drop down data. Then, when selecting the save pull down, you need to request data according to the selected province in the change event. At this time, we need to define what we need to do when we change the data. Here’s the code:
__nextKey: 'city'.// Assign a value to the dropdown data
__nextFetch: '/getCity'.// Assign the request interface
__nextParam: ['province'].// Request parameters
__nextMethod: 'get'.// Request mode
__emptyArr: ['city'.'district'] // Which dropdown categories need to be cleared when the dropdown data is modified
/** * listen for pull-down change events *@param { String | Number | Boolean } Value Select the value * of the drop-down data@param { Object } value
*/
const selectChange = async function (value, item) {
searchForm[item.__key] = value
// Empty the drop-down list and the drop-down data
if (item && item.__emptyArr) {
for (let i = 0; i < item.__emptyArr.length; i++) {
let key = item.__emptyArr[i]
if (selectOption[key]) {
selectOption[key] = []
}
vm.$set(searchForm, key, ' ')}}if (item && item.__nextFetch && item.__nextParam) {
let param = {}
for (let j = 0; j < item.__nextParam.length; j++) {
let value = searchForm[item.__nextParam[j]]
if (value) {
param[item.__nextParam[j]] = value
}
}
const res = await request({
url: item.__nextFetch,
method: item.__nextMethod || 'post'
}, param)
if (res) {
let { data } = res
if (data && data.success) {
vm.$set(selectOption, item.__nextKey, data.list)
}
}
}
vm.$emit('on-change', value)
}
Copy the code
Ok, this basically completes the three-level linkage function. Below are the renderings
Component extensions
Component extensions are easy. If you are missing a component, you can add one to components.js
/** * date picker *@param {*} h
* @param {*} item
* @param {*} vm
*/
export const date = function (h, item, vm) {
const { searchForm } = vm
return (
<el-form-item label={item.label} labelWidth={item.labelWidth}>
<el-date-picker
on-input={(v)= >{ searchForm[item.__key] = v }} props={{ type: 'date', size: 'small', value: searchForm[item.__key], placeholder: "Select date ",... item }} ></el-date-picker>
</el-form-item>)}// searchArr is added when used
{
__type: 'date'.__key: 'birthday'.label: 'birthday'
}
Copy the code
Nothing else needs to be changed, and a new query condition type is added. Here is the effect
Search criteria display
Search criteria function is relatively simple, is in the user click search, display input criteria. Let’s separate this out as a separate component
<query-info ref="queryInfo" on-remove={this.removeTag} selectionOption={ this.selectOption }></query-info>
Copy the code
This component passes in a drop down array, and as we said when we were designing it, it can also hit a cross to remove the condition, so we have to throw a remove custom event, and then when we remove the condition, we empty the condition and call the query method. When you query, pass the search criteria to the component and make it display. To note here, we can’t directly introduced the search condition through the props to the components inside, if it is passed in this way, because the data is the response type, when you input conditions, below will be displayed, the effect of this is not what we want, we want to effect is when click the query button, display is query query conditions. Parse (json.stringify (val)) to create a deep copy of the query button.
removeTag (name) {
this.searchForm[name] = ' '
this.queryTable()
}
/ / query
queryTable () {
this.$refs.queryInfo && this.$refs.queryInfo.queryTable(this.searchForm)
this.moreShow = false
this.$emit('search'.this.searchForm)
}
Copy the code
The component implementation is as follows:
<template>
<div class="queryInfo">
<el-tag
v-for="tag in tags"
:key="tag"
closable
style="margin-right: 10px;"
@close="close(tag)"
>
{{displayName(tag)}}
</el-tag>
</div>
</template>
<script>
import { getTitle } from '@/utils/utils'
export default {
props: {
// Drop down data
selectionOption: {
type: Object.default: () = > []
}
},
data () {
return {
// Query conditions
searchForm: {}}}.computed: {
/// Compute an array of input criteria
tags: function () {
let tags = []
let keys = Object.keys(this.searchForm)
keys.forEach((key) = > {
if (this.searchForm[key]) {
tags.push(key)
}
})
return tags
}
},
methods: {
// Click close
close (tag) {
this.searchForm[tag] = ' '
this.$emit('remove', tag)
},
// Externally called methods
queryTable (searchForm) {
this.searchForm = JSON.parse(JSON.stringify(searchForm))
},
// Display the name, if it is a drop down data, to match the name in the drop down data display
displayName(key) {
let value = this.searchForm[key]
if (this.selectionOption[key]) {
return getTitle(value, this.selectionOption[key])
} else {
return value
}
}
}
}
</script>
<style scoped>
.queryInfo {
margin-bottom: 10px;
}
</style>
Copy the code
The results are as follows:
Add a function button area
This is currently added as a slot in the searchTable component
<div class="right">
{this.$slots.rightButton}
</div>
/ / use
<search-table :searchArr="searchArr" @search="search">
<template v-slot:rightButton>
<el-button
size="small"
type="success"
@click="() = > {MessageBox ({message: 'developing', type: 'warning'})}">add</el-button>
</template>
</search-table>
Copy the code
Effect:
Write in the last
At this point, everything the query component wants to do has been done. In fact, it is not complicated to do, the most important point is not to do, but to think clearly before doing. After we put the dynamic table header to do a good job, then roughly about the table of the relevant requirements are completed. If you think the article has some of the most useful words, might as well like a wave of attention! So I’ll see you next time.