前言
- 常网IT戳我呀!
- 常网IT源码上线啦!
- 本篇录入技术选型专栏,希望能祝君拿下Offer一臂之力,各位看官感兴趣可移步🚶。
- 有人说面试造火箭,进去拧螺丝;其实个人觉得问的问题是项目中涉及的点 || 热门的技术栈都是很好的面试体验,不要是旁门左道冷门的知识,实际上并不会用到的。
- 最近在做低代码平台相关的,想分享一些自己在项目中遇到的场景。
鲁迅曾经说过:中国人的性情总是喜欢调和折中的。
如果你说这屋子太暗,要在这里开一个天窗,大家一定是不允许的。但如果你主张拆掉屋顶,他们就会来调和,反而同意拆掉天窗。
车队民工拿不到工资,刀检察院门口闹一下,市里就给垫款了;
小韩见义勇为,学校装聋作哑,韩母把事情闹大了,学校立刻就破案了;
冷眼的村民,看到小王的妻子跳楼后,终于敢站出来作证了;
仔细看下去,相信能收获一些干货滴~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
一、问题剖析
那是一个倾盆大雨的早上,花瓣随风雨落在我的肩膀上,是五颜六色的花朵。
我轻轻抚摸着他,随后拨开第一朵花瓣,她不爱我,该怎么办。
花瓣似乎在说:爱她。
拨开第二朵,她对我已经没有那种感觉了。
花瓣似乎在说:那就去爱她。
拨开第三朵,你还没有理解,我是说他对我已经没有了爱的感觉。
花瓣似乎在说:就是因为她对你已经没有了爱的感觉,所以才要去爱她。
正当我沉迷于幻想中,实习生小白🙋喊道:前端大佬,有一个需求。
我说🙋🏻♂️:什么时候要?
小白🙋:别问,问就是明天!
🙋🏻♂️真是的,慌慌张张的,说吧~
需求是这样子的:
现在我们现在的A页面,北京那边也想要看到这个页面,且有一些搜索条件、表格字段要隐藏掉。
如果是刚刚毕业时的我,拿到这个需求,不管三七二十一,先把A页面拷贝一份出来,给北京那边用。
这样子缺点是:要维护两套,尽管他们有相似处达到80%。
如今的我,想的是:把A页面做成动态渲染。
二、实现思路
一个根据配置渲染搜索条件、表格的组件。
init重置搜索条件
搜索条件,传入的是一个数组:内有一个输入框。
需要先进行深拷贝 JSON.parse
一层,在用户点击重置的时候,将最初的值赋值与他。
但使用 $set
将整个数组替换无效,直接赋值无效,因为是数组内结构太深,所以用key刷新组件。
javascript
<search :key="searchPanelKey" />
searchPanelKey: 0
mounted() {
this.searchFieldsData = this.searchFields;
// 先将旧的值保存起来,方便后续重置
this.searchFieldsDataOld = JSON.parse(JSON.stringify(this.searchFields));
},
// 重置
init() {
this.searchPanelKey++;
this.searchFieldsData = JSON.parse(JSON.stringify(this.searchFieldsDataOld));
},
class
该组件因为是公共组件,所以内置了一个class类方便管理该组件。
Render内提供一个class,暴露出多个方法,给父组件使用:
- set:根据prop设置searchFieldsData的属性
- get:根据prop获取searchFieldsData
- getObj:searchFieldsData数组转为对象{ prop: value }
- init:初始化
set方法是使用 $set
来更新值,但抽离出js文件,$set
是vue文件独享,所以需要 new Class
的时候传入。
三、table表格组件
核心功能还是table组件,内含复杂的功能。
支持多级表头。(这个后续会补充文章来写,挺精彩的)
操作按钮
可以支持鼠标移入显示title,使用el-tooltip组件。
el-tooltip这里有个坑。
最初的代码:大概的意思,真实不是这样
javascript
<el-tooltip :content="btn.title">
<el-button v-if="isShow">{{ btn.label }}</el-button>
</el-tooltip>
按钮是每一行都会显示的,然后我第一行显示按钮A,第二行不显示按钮A。
此时第一行鼠标移入按钮,不会显示title,但我明明已经设置了。
排查到的原因: 因为第二行按钮被隐藏了,所以el-tooltip也会不生效,会影响到第二行。
解决方案:将v-if不要写在按钮身上,需要写到el-tooltip身上就正常,可看下面的代码。
怎么控制按钮的显隐
我们知道,我们会依赖于接口返回的字段状态来控制按钮的显隐或者样式。
那我们需要配置接口的参数,如:interface:{ name: "张三" },就是name为张三的这条数据才显示按钮。
javascript
v-if="(btn.interface ? getInterface(btn, scope) : true) && (!btn.isShow ? true : btn.isShow(scope.row))"
getInterface
介绍一下v-if中的这个方法,下面的computed有写。
将传入的interface对象,看看是不是全等,且的关系,判断是不是都命中,如果是,就认为显示,否则则不显示。(这种情况就是有传interface)
又有一种情况,你怎么配置,name != '张三'
、又或者 age != 30 || name == '张三'
呢?
getInterface
方法只是判断等于的情况,且是every全等的。
当然也是可以配置,比如:传入
javascript
{
prop: "name",
value:"张三",
operation: "notEqual", // notEqual:不等于;equal:等于
link: "some" // some:或;every:且
}
以这种形式也可以。
当然有另外一种方法二:
就是不配置interface对象,在外面接受一个isShow方法,该方法接受接口返回的数据,返回布尔类型
那么你就可以愉快的写:
javascript
`isShow(row) =>{ row.name=='张三' || row.age != 30 }`
一种是用对象,另外一种是方法,两者都可支持
按钮的样式style
根据接口返回的数据的状态来显示style,比如:name为张三这条数据,按钮显示禁用;其他的状态就正常。
javascript
<el-button :style=" getInterface(btn, scope) ? btn.style : ''" type="text" >
这样子也是可以的。
方法二:但后面想了一下,我们可以传入两组按钮。两个按钮的配置是一样的,只不过按钮1显示样式1;按钮2显示样式2,只要有传style就渲染:
javascript
<el-button :style=" btn.style " type="text" >
鼠标移入提示
其实和按钮的style一样,有传就显示。
content默认传入title,disabled的话,如果没传title就给其不显示,传了title就显示
javascript
<el-tooltip
v-if="(btn.interface ? getInterface(btn, scope) : true) && (!btn.isShow ? true : btn.isShow(scope.row))"
:content="btn.title"
:disabled="!btn.title"
>
代码如下
javascript
<el-table-column v-if="columns.handle" v-bind="columns.handle">
<template slot-scope="scope">
<template v-for="(btn, btnIndex) in columns.handle.btn">
<el-tooltip
v-if="(btn.interface ? getInterface(btn, scope) : true) && (!btn.isShow ? true : btn.isShow(scope.row))"
:key="btnIndex"
effect="dark"
:content="btn.title"
:disabled="!btn.title"
placement="top"
>
<el-button :style="btn.style" type="text" @click="$emit('btnClick', btn, scope.row)">{{ btn.label }}</el-button>
</el-tooltip>
</template>
</template>
</el-table-column>
computed: {
getInterface() {
return (btn, scope) => {
if (getVal(btn, "interface")) {
let bool = [];
for (const key in btn.interface) {
if (Object.hasOwnProperty.call(btn.interface, key)) {
const element = btn.interface[key];
bool.push(scope.row[key] == element);
}
}
return bool.every((e) => e);
} else {
return false;
}
};
},
},
表格内容
需求:可以支持显示显示icon,鼠标移入可显示。
配置:表头添加一个icon,鼠标移上去显示(后续文章补充)
但是之前只是支持:通过动态组件渲染icon(组件名字),利用v-html解析实现换行和颜色,鼠标移入也可显示文本。
之前代码如下
javascript
<!-- v-bind绑定属性(传入对象) -->
<component :is="item.name" v-bind="item.attribute">
<template slot="content">
<span v-html="item.attribute.content"></span>
</template>
<span v-html="item.ele"></span>
</component>
现在需求提升:内含【详情】按钮,点击需要跳转路由。而且还需要根据接口返回的id跳转路由。
那我在想:怎么从外面传进来参数,或者从里面拿到外面的参数?
如果不依赖于外部参数,这样子按照原本的渲染逻辑是可以的。
javascript
item.attribute.content = `数量。<br /><span style="color: #409EFF;" onclick="console.log(11)">详情</span>
`
但根据id路由跳转,我尝试在 onclick
内打印this,是他本身,于是我想着外部传入id绑定在span上,然后内部通过this.getAttribute('ids')可以获取到ids的值,进行路由跳转。
javascript
item.attribute.content = `数量。<br /><span ids="从外部循环改ids传入的" style="color: #409EFF;" onclick="console.log(11)">详情</span>
`
相对来说是比较麻烦的一种方式。
还有另外一种方法是:从外部传入。
可以看到content从字符串的形式,变成函数,传入scope, coloumnHeader
参数,内部接收可随意配置。
至于路由跳转,这里$emit('componentClick')
把事件传出去,给父组件传递一个事件,父组件可进行路由跳转操作。
javascript
# 动态组件DynamicComponents
<component :is="item.name" v-bind="item.attribute">
<template slot="content">
<span v-if="item.attribute.contentSlot" v-html="item.attribute.content(scope, coloumnHeader)" @click="$emit('componentClick',scope, coloumnHeader)"></span>
</template>
<span v-html="item.ele"></span>
</component>
这里scope.row.name
也可以使用接口的值渲染。
javascript
item.attribute.content : (scope, coloumnHeader) => {
return `填报单位:已收到指标任务总和的单位${scope.row.name}数量。<br /><br />
<span class="detailed-link" style="cursor: pointer;color: #409EFF;">详情</span>`;
}
`
需求提升:想可以配置两个的。
比如:一个icon,一个标签
你看,像这种标签,我们name传入el-tag
利用动态组件即可渲染出。
那我们需要循环渲染。
循环 coloumnHeader.tempBody
数组。
我们注意到DynamicComponents
组件的v-if接收interface
方法,可以根据接口的数据的状态来控制其显隐。
javascript
<template v-if="getVal(coloumnHeader, 'tempBody', 'length')">
<template v-for="(tempBody, tempBodyIndex) in coloumnHeader.tempBody">
// 这个就是我们上面写的动态组件
<DynamicComponents
v-if="getVal(tempBody, 'show') && (tempBody.interface ? tempBody.interface(scope, coloumnHeader) : true ) "
:key="tempBodyIndex"
:item="getVal(tempBody)"
:scope="scope"
:coloumnHeader="coloumnHeader"
@componentClick="$emit('componentClick')"
/>
</template>
</template>
使用:
javascript
// 配置表格内容
tempBody: [
// icon
{
show: true,
position: "left",
name: "el-tooltip",
ele: '<i class="el-icon-warning" style="cursor: pointer;margin-right: 5px;color: #E6A23C;"></i>',
// 满足这种条件下,才显示
interface:(val) =>{
return val.row.name != '示例'
},
attribute: {
placement: "top",
effect: "dark",
contentSlot: (scope, coloumnHeader) => {
return `填${scope.row.name}数量。<br /><br />
<span class="detailed-link" style="cursor: pointer;color: #409EFF;" onclick="console.log(11)">详情</span>
数量。`;
},
},
},
// 标签
{
show: true,
position: "left",
name: "el-tag",
ele: "标签",
attribute: {
type: "success",
style: "margin-left:20px;",
effect: "plain",
},
},
],
后记
这种类似的场景一般用于可视化低代码平台中。
会根据配置来动态渲染。
做好扩展性,遵守开闭原则。
当然,我们还要考虑安全性防XSS注入,防止外部嵌入,导致安全问题。
如果有其他更好的方法也欢迎评论区见,这里提供的只是诸多方法之一。
最后,祝君能拿下满意的offer。
我是Dignity_呱,来交个朋友呀,有朋自远方来,不亦乐乎呀!深夜末班车
👍 如果对您有帮助,您的点赞是我前进的润滑剂。