前言
工作中难免遇到重复劳动。每次对着 element-ui 中 的 el-table 一顿乱撸。为了避免重复劳动,代码生成器一定是一个不错的选择。附图。
el-table 优化
在日常开发中难免对后台返回的数据进行一些处理。然而在使用 el-table-colume 的时候,经常使用 el-table-column 的插槽会增加最少一行代码,同时也会降低代码的阅读理解能力。通过对文档的研究发现发现 formatter 参数可以对值对象进行格式化。使用该参数可以提高数据处理的复用能力,同时也将省略slot插槽,提高代码的阅读流畅度。
优化前
<el-table-column prop="startTime" label="开始时间">
<template slot-scope="{ row }">
{{ row.startTime | dayjs('YYYY-MM-DD HH:mm:ss') }}
</template>
</el-table-column>
// 封装优化代码
export class ElTableUtil {
// 日期格式化
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 ? '' : ' - '
return total + dayjs(str).format(format) + tailIndicator
}, '')
}
}
// 除法
static divide(num: number, indicator = 2) {
if (num === 0) throw new Error('ElementUI table 除法不能除0')
return (row, column, cellValue, index) => {
return (cellValue / num).toFixed(indicator)
}
}
// 除以另外一个字段
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)
}
}
// 秒或毫秒转 => xx天xx小时
// 数值 => 数值/100 + '%'
// 数值 => 解析对应中文
}
优化后
<!-- 日期格式化 -->
<el-table-column prop="startTime" label="时间" :formatter="ElTableUtil.dateFormatter()" />
<!-- 除法并限制小数点 -->
<el-table-column prop="rate" label="XX率(%)":formatter="ElTableUtil.divide(1, 0)" />
代码生成器
最初想到这个东西的时候,后台开始启用 Swagger,前端开始使用 TypeScript 来进行项目开发。既然后端已经有了类型为何我们不把后端的类型拿来用呢?
灵感
通过对调查,发现 swagger 返回的 json 中会带有一些固定类型,比如枚举,字符串,数字等。通过对此json和类型的解析。可以自动生成 TypeScript 中的 type 类型。 代码生成器:Swagger Codegen
// Swagger api 会返回类似的 json
// QRCodeDTO 的 description 可以解析成页面的 title
// 对象的 key 可以解析成 column 的 prop
// description 可以解析成 column 的 label
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": "生成QRCODE用的数据"
}
}
// 通过 代码生成器 可转化为
type Check = {
'xxId' ? : string
'XXXId' ? : boolean
'XXXXId' : string
};
生成器
建议先大致看一下模板引擎 [EJS](https://ejs.bootcss.com/) 的语法。不然应该不好看明白。
既然可以生成 typescript 为什么不直接生成页面呢。自动写代码何乐而不为!采用 ejs 作为模板引擎。 首先做json转列表的解析,对特定字段进行正则匹配,然后解析成对应的组件。
字段解析成组件模板
<% 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>
<% } 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" />
<% } 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>
<% } 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"/>
<% } 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"/>
<% } else if (item.type == 'boolean') {%>
<el-table-column label="<%= item.description || item.id %>" prop="<%= item.id %>" show-overflow-tooltip :formatter="ElTableUtil.boolean('是','否')" align="center"/>
<% } 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"/>
<% } else { %>
<el-table-column label="<%= item.description || item.id %>" prop="<%= item.id %>" show-overflow-tooltip align="center" />
<% } %>
字段解析查询框模板
<% 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>
<% } else if (item.type == 'boolean') {%>
<el-select v-model="condition.<%= item.id %>" clearable>
<el-option label="是" :value="true" />
<el-option label="否" :value="false" />
</el-select>
<% } else if (item.type == 'integer') {%>
<el-input-number v-model="condition.<%= item.id %>" :min="1" :max="10" label="<%= item.description %>"></el-input-number>
<% } else if (item.type == 'string' || item.type == 'number') { %>
<el-input v-model.trim="condition.<%= item.id %>" clearable/>
<% } else {%>
<!-- 不知道是啥 -->
<component v-model="condition.<%= item.id %>" />
<% } %>
页面解析模板
<template>
<page-list>
<!-- 查询条件 -->
<template slot="search">
<% conditions.forEach(condition => { %>
<el-form-item label="<%= condition.description || condition.id %>">
<%= formatEdit({item: condition}) %>
</el-form-item>
<% }) %>
</template>
<!-- 列表部分 -->
<template slot="list">
<% list.children.forEach(item => { %>
<%= formatlist({item: item}) %>
<% }) %>
<el-table-column label="操作" width="130" fixed="right">
<template #default="{ row }">
<el-button icon="el-icon-s-check" type="text" @click="handleEdit(row.id)" />
</template>
</el-table-column>
</template>
</page-list>
</template>
解析
import { saveAs } from 'file-saver'
// condition 和 list 均为swagger 返回后的json
const gendata = { conditions: this.selectNode?.children?.filter(e => e.search) || [], list: this.selectNode }
// model, search, list, pagelist 均为 ejs 模板
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' })
// 页面解析模板,解析的结果
saveAs(file, _.kebabCase(this.selectNode.label) + '.vue')
总结
通过对 swagger 生成 json 的解析,我们可以利用模板引擎一键生成标准模板页面。通过 file-saver 一键下载到本地。不过缺点也是显而易见的,后端没有对应swagger对象的时候。我们还是无能为力的,需要后端先行确认数据结构。
文章末尾请带上以下文字及链接:本文正在参与「掘金 2021 春招闯关活动」, 点击查看 活动详情
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!