preface

It is hard to avoid repetitive work. Every time you masturbate to the El-Table in Element-UI. To avoid reinventing the wheel, a code generator must be a good choice. The appended drawings.

El – table optimization

In daily development, it is unavoidable to deal with the data returned from the background. However, when using el-table-colume, the frequent use of the el-table-column slot will add at least one line of code, and also reduce the code reading ability. An examination of the document reveals that the formatter parameter can format a value object. Using this parameter improves the reuse of data processing, while eliminating slot slots and improving code reading fluency.

Before optimization


<el-table-column prop="startTime" label="Start time">
    <template slot-scope="{ row }">
        {{ row.startTime | dayjs('YYYY-MM-DD HH:mm:ss') }}
    </template>
</el-table-column>
Copy the code
// Encapsulate optimized code
export class ElTableUtil {
    // Format the date
    static dateFormatter(format = 'YYYY-MM-DD HH:mm:ss') {
        return (row, column, cellValue, index) = > {
            if(! cellValue)return ' '
            const _cellValue = _.castArray(cellValue || ' ')
            return _cellValue.reduce((total, str, index) = > {
                const tailIndicator = index === _cellValue.length - 1 ? ' ' : The '-'
                return total + dayjs(str).format(format) + tailIndicator
            }, ' ')}}/ / division
    static divide(num: number, indicator = 2) {
        if (num === 0) throw new Error('ElementUI table division cannot divide 0')
        return (row, column, cellValue, index) = > {
            return (cellValue / num).toFixed(indicator)
        }
    }
    // Divide by another field
    static divideBy(key: string, indicator = 2) {
        return (row, column, cellValue, index) = > {
            if(! row[key] || row[key] ===0) return NaN
            return (cellValue / row[key]).toFixed(indicator)
        }
    }
    // Seconds or milliseconds => XX days xx hours
    // Number => number /100 + '%'
    // Value => Parsing corresponding Chinese characters
}
Copy the code

The optimized

<! -- Date formatting -->
<el-table-column prop="startTime" label="Time" :formatter="ElTableUtil.dateFormatter()" />
<! Divide and limit the decimal point -->
<el-table-column prop="rate" label="XX rate (%)":formatter="ElTableUtil.divide(1, 0)" />
Copy the code

Code generator

When we first came up with this idea, Swagger was enabled in the background and TypeScript was used in the front end for project development. So why don’t we use the backend type if we already have the backend type?

inspiration

Through investigation, we find that Swagger returns JSON with fixed types such as enumeration, string, number, etc. By parsing this JSON and type. The type in TypeScript is automatically generated. Code Generator: Swagger Codegen

// The Swagger API returns similar JSON
// QRCodeDTO's description can be resolved to the page title
// The key of the object can be resolved to a prop for column
// Description can be resolved to label for column
const swaggerJson = {
    "QRCodeDTO": {
      "type": "object"."properties": {
        "xxId": { "type": "string"."description": "xxid" },
        "XXXId": { "type": "string"."description": "XXXid" },
        "XXXXId": {
          "type": "string"."description": "XXXXid"."required": true}},"title": "QRCodeDTO"."description": "Data for generating QRCODE"}}// The code generator can be converted to
type Check = {
    'xxId' ? : string
    'XXXId' ? : boolean
    'XXXXId' : string
};
Copy the code

The generator

It is recommended to take a quick look at the syntax of the template engine [EJS](https://ejs.bootcss.com/). It's not going to be easy to understand.Copy the code

Why not generate pages when you can generate typescript. Write code automatically! Ejs is used as the template engine. The json list is parsed first, with regular matches for specific fields, and then parsed into the corresponding components.

Fields are resolved into component templates

The < %if (/.*img.*/.test(_.lowerCase(item.id)) || /.*image.*/.test(_.lowerCase(item.id))) {%> 
    <el-table-column label="<%= item.description || item.id %>" prop="<%= item.id %>" show-overflow-tooltip align="center">
        <template slot-scope="scope">
            <el-img :value="scope.row.<%= item.id %>" />
        </template>
    </el-table-column>The < %}else if (/.*id.*/.test(_.replace(_.lowerCase(item.id), ' '.' ')) && item.id ! ='id') { %> 
    <el-table-column label="<%= item.description || item.id %>" prop="<%= item.id %>" show-overflow-tooltip :formatter="ElTableUtil.get('<%= item.id %>_name')" align="center" />The < %}else if (/.*status.*/.test(_.replace(_.lowerCase(item.id), ' '.' '))) { %> 
    <el-table-column label="<%= item.description || item.id %>" prop="<%= item.id %>" show-overflow-tooltip align="center">
        <template slot-scope="scope">
            <el-tag type="default|success|info|warning|danger">{{ scope.row.<%= item.id %> }}</el-tag>
        </template>
    </el-table-column>The < %}else if (/.*time.*/.test(_.lowerCase(item.id))) {%> 
    <el-table-column label="<%= item.description || item.id %>" prop="<%= item.id %>" show-overflow-tooltip :formatter="ElTableUtil.dateFormatter()" align="center"/>The < %}else if (item.type == 'number') {% ><el-table-column label="<%= item.description || item.id %>" prop="<%= item.id %>" show-overflow-tooltip :formatter="ElTableUtil.divide(1)" align="center"/>The < %}else if (item.type == 'boolean') {% ><el-table-column label="<%= item.description || item.id %>" prop="<%= item.id %>" show-overflow-tooltip :formatter="Eltableutil. Boolean (' yes ',' no ')" align="center"/>The < %}else if (item.type == 'string' && item.enum) {%> 
    <el-table-column label="<%= item.description || item.id %>" prop="<%= item.id %>" show-overflow-tooltip :formatter="ElTableUtil.enum('')" align="center"/>The < %}else { %>
    <el-table-column label="<%= item.description || item.id %>" prop="<%= item.id %>" show-overflow-tooltip align="center" />The < %} % >Copy the code

Field resolution query box template

The < %if (/.*org.*/.test(_.replace(_.lowerCase(item.id), ' '.' '))) % > < %} {else if (/.*time.*/.test(_.replace(_.lowerCase(item.id), ' '.' ')) || /.*date.*/.test(_.replace(_.lowerCase(item.id), ' '.' '))) {% ><el-date-picker v-model="condition.<%= item.id %>" value-format="timestamp" type="daterange" :default-time="[' 00:00:00 ', '23:59:59]"> </el-date-picker>The < %}else if (item.type == 'boolean') {% ><el-select v-model="condition.<%= item.id %>" clearable>
        <el-option label="Yes" :value="true" />
        <el-option label="No." " :value="false" />
    </el-select>The < %}else if (item.type == 'integer') {% ><el-input-number v-model="condition.<%= item.id %>" :min="1" :max="10" label="<%= item.description %>"></el-input-number>The < %}else if (item.type == 'string' || item.type == 'number') { %> 
    <el-input v-model.trim="condition.<%= item.id %>" clearable/>The < %}else{% > <! -- Don't know what it is --><component v-model="condition.<%= item.id %>" />The < %} % >Copy the code

Page parsing template

<template>
    <page-list>
         <! -- Query condition -->
        <template slot="search">
            <% conditions.forEach(condition => {  %>
                <el-form-item label="<%= condition.description || condition.id %>">
                    <%= formatEdit({item: condition}) %>
                </el-form-item>The < %}) % ></template>
         <! -- List section -->
        <template slot="list">
            <% list.children.forEach(item => {  %>
                <%= formatlist({item: item}) %>
            <% }) %> 
            <el-table-column label="Operation" width="130" fixed="right">
                <template #default="{ row }">
                    <el-button icon="el-icon-s-check" type="text" title="View details"  @click="handleEdit(row.id)" />
                </template>
            </el-table-column>
        </template>
    </page-list>
</template>
Copy the code

parsing

import { saveAs } from 'file-saver'
Condition and list are json returned by swagger
const gendata = { conditions: this.selectNode? .children? .filter(e= > e.search) || [], list: this.selectNode }
// Model, search, list, pagelist are ejS templates
let fn = ejs.render(`<%- pagelist({conditions:conditions,list:list,formatlist:formatlist,formatEdit:formatEdit,formatModel:formatModel})%>`, {
            formatModel: model,
            formatEdit: search,
            formatlist: list,
            pagelist: pagelist, ... gendata })const file = new File([fn], 'hello world.txt', { type: 'text/plain; charset=utf-8' })
// Page parsing template, parsing results
saveAs(file, _.kebabCase(this.selectNode.label) + '.vue') 
Copy the code

conclusion

By parsing swagger’s JSON generation, we can use the template engine to generate standard template pages in one click. Use file-saver to download it locally. However, the disadvantages are also obvious, when there is no corresponding Swagger object on the back end. There’s nothing we can do about it. We need the back end to validate the data structure first.

Please bring the following text and link at the end of the article: This article is participating in the “Digging Gold 2021 Spring Recruitment Campaign”, click to view the details of the activity