Build a wheel and implement a table component for a complex scene (UniApp)

Make writing a habit together! This is my first day to participate in the “Gold Digging Day New Plan · April More text challenge”, click to see the details of the activity.

Is a mature program ape, to know how to build their own wheels (uniApp’s plug-in market has not found the need for a plug-in, there is no way, can only build a wheel). This paper aims to replicate + record. Usage scenario: uniApp, mobile terminal (compatible with small program, App, H5)

To the chase

Organize specific functions according to requirements:

Need to sort out

  1. Table name
    1. Configurable background
    2. Font style modifiable (size, color)
    3. Menu button (external exposure event required)
  2. header
    1. Supports multiple table headers
    2. Header fixed
    3. Table header rows support custom names
  3. form
    1. Support for setting cell width
    2. Fixed the first column
    3. Support tree data
    4. Content support images, links
  4. other
    1. Internal implementation sort
    2. Internal implementation of paging
    3. Internal calculation of total rows

Some thoughts on the whole component

  1. More complex functionality, less elegant and messy to squeeze into a file -> split into several modules in a large direction (fine granularity)
  2. More requirements, intuitive is the need to pass a lot of parameters -> according to the module definition, the parameters are also classified
  3. More parameters, how to more elegant management, reduce the difficulty of getting started? -> Configuration fileconfig.jsAnd set the default value in it, toFields thatandDefault State ManagementThe role of
  4. Some ICONS will be used -> selectediconfontIcon library

Technical Implementation difficulties

Due to the limitation of using environment: uniApp’s table related components are relatively simple, and there is a large limitation for non-H5 environment (for example, rowSPAN and ColSPAN cannot be set), which is also troublesome to use and cannot meet the requirements of the project. Finally, WE decided to build a wheel by ourselves.

Header section

The main difficulty lies in the processing of multi-level table headers and how to drive the display according to the data. At the beginning, I planned to implement it in the way of HTML table. During the development process, I encountered many problems. Firstly, data processing was troublesome, including calculating the number of rows, colSPAN and Rowspan of each row cell. And there is no TD, TR and other components, need their own additional implementation.

The data of columns is a tree, as follows

columns = [
    { "title": "Area"."dataIndex": "Area" },
	{
		"title": "Guangzhou District 1"."children": [{"title": "Sales"."dataIndex": "Sales in Guangzhou District 1"},
			{ "title": "Planned sale"."dataIndex": "Planned Sales in Guangzhou Area 1" },
			{ "title": "A"."dataIndex": "Reached in Guangzhou District 1"}},// ...
]

Copy the code

The flex layout seems to center each grid vertically, traversing the recursive rendering if there are children, and separating the recursive part into a component called titleColumn since the rendering needs to be recursively called. First post a code (the code has been posted to the community, you can check out the portal if you are interested) :

table-header.vue

titleColumn.vue

There is a catch: in normal VUE recursive components are not introduced, but in uniApp they are.

// titleColumn.vue
import titleColumn from "./title-column.vue"
Copy the code

The style aspect is not expanded, so it is not easy to write. See the results directly (feel good about yourself, hahaha) :

Form and content

Here we first need to process the data of columns (considering multi-level table headers), and obtain the actual columns to be rendered according to the columns above:

  1. Create a new variabledataIndexsIs used to hold the column data that needs to be actually rendered
  2. Recursive processingcolumnsTake the final leaf node and save it.

Key code:

// Get the actual rendered Column in the body based on Column
fmtColumns(list) {
    // Save the leaf node
    this.dataIndexs = []
    if(! list || ! list.length)return
    // Get the actual line
    this.columnsDeal(list)
},

// 
columnsDeal(list, level = 0) {
    list.forEach(item= > {
        let{ children, ... res } = itemif (children && children.length) {
            this.columnsDeal(children, level + 1)}else {
            this.dataIndexs.push({ ... res }) } }) },Copy the code

The next step is to deal with the tree structure in the list data. Let’s look at the data structure tableData:

tableData = [
    {
		"key": 1."Area": "Guangzhou"."Sales": 100."Planned sale": 200."A": "50.0%"."Reach the rank": 1."GroupIndex": 1."GroupLayer": 1."GroupKey": "Guangzhou"."children": [{
				"key": 11."Area": "Guangzhou District 1"."Community": "Guangzhou District 1"."Sales": 60."Planned sale": 120."A": "50.0%"."Reach the rank": 1.children: [{
					"key": 111."Area": "Guangzhou District 1 1"."Community": "Guangzhou District 1 1"."Sales": 60."Planned sale": 120."A": "50.0%"."Reach the rank": 1,}}, {"key": 12."Area": "Guangzhou District 2"."Community": "Guangzhou District 2"."Sales": 40."Planned sale": 80."A": "50.0%"."Reach the rank": 1},],},]Copy the code

Tree structure, key is the unique value. I thought about using recursive components, but it would involve expansion and collapse. It’s also a hassle. The final solution is to flatten the data, adding hierarchy, child data, parent ID and other attributes to each data. An array variable is used to record the expanded row and control the display of child data. The processed data is stored in dataList flat processing function:

// Recursively process data, tree => Array
listFmt(list, level, parentIds = []) {
    return list.reduce((ls, item) = > {
        let{ children, ... res } = item// Error message
        if (res[this.idKey] === undefined| |! res[this.idKey] === null) {
            // console.error(' tableData has [idKey] property in data, no data, please check ')
        }
        letnowItem = { ... res, level,hasChildren: children && children.length,
            parentIds,
            children,
            [this.idKey]: res[this.idKey] && res[this.idKey].toString()
        }
        ls.push(nowItem)
        if (children && children.length) {
            this.isTree = true
            ls = ls.concat(this.listFmt(children, level + 1, [...parentIds, nowItem[this.idKey]]))
        }
        return ls
    }, [])
},
Copy the code

The final data are as follows:

I’m ready to render the data,

Need to nest two layers of traversal:

First layer traversaldataListGet the line

Layer 2 traversaldataIndexsGet the column

Final render:

Fix the first column, fix the table header

usecssProperties:position: stickyThe implementation. Stickily positioned elements. We are all mature front-end ape ~~, I will not be specific. A few details to pay attention to:

compatibility



Uniapp small program mode, App mode is supported!!

limit

  1. If position:sticky is set, either top left right bottom must be specified. Unset performance and relative positioning is the same. If top and bottom or left and right are set at the same time, the priorities of top and left are high.

  2. The overflow property of any parent of the position:sticky element must be visible otherwise it will not take effect.

other

It’s easy to make a wheel, not easy to make a wheel that works.

Involve some layout and CSS part of the things in the article is not good to express, not in detail, interested can pull the code to see. portal

During the development process, I also encountered a lot of problems, which were patched up all the way. If the idea was not well conceived in the early stage, the subsequent development would be hit and miss (at the beginning, the modules and parameters were not well divided, and the whole logic was chaotic. Later, I stopped to rethink and adjust, and felt suddenly happy).

To move bricks ~