🌈 规则组件示例
☀️ 为什么需要规则组件
数据可视化平台底层JSON解析器提供了模板字符串的支持,例如:
'#{data.count > 10 ? "red" : "yellow"}'
在可视化搭建中,模板字符串使用并不友好:
- 用户需要学习模板字符串语法,并且在编辑器中编写模板字符串并不方便
- 变量名需要手动编写,容易出现错误
🌧 设计要求
凡是模板字符串能够实现的,规则组件要全部实现。
- 单一变量结果,如
#{data.count}
- 条件判断,如
'#{data.count > 10 ? "red" : "yellow"}'
- 多条件判断,如
#{data.count > 10? "red" : data.price > 100 ? "yellow" : "blue"}
- 同时满足多个条件,如
#{data.count > 10 && data.price > 100 ? "red" : "yellow"}
🌟 设计思路
整体思路
模板字符串拿到的结果是唯一的,即当前条件满足后使用该结果,不满足条件继续向后判断。
可以设计成数组的格式:
[
{ conditions: conditions1, result: 'result1' },
{ conditions: conditions2, result: 'result2' },
{ result: 'result3' }
]
数组中每组元素是或关系,当前条件满足后,使用该组结果,无需向后判断。
有的组不需要条件判断,默认满足,则不需要设置conditions。
如此满足设计要求1,2,3.
conditions思路
为方便构成data.count > 10
的结构,conditions由三个字段组成: 变量(variable) 、比较符(operator)、比较值(value)。
conditions: { variable: 'data.count', operator: '===', value: 10 },
为了满足多条件(设计要求4),同样将conditions设计成数组格式,每组元素是与关系,需同时满足。
conditions: [
{ variable: 'data.count', operator: '===', value: 10 },
{ variable: 'data.price', operator: '>', value: 100 }
],
☃️ 格式解析
组件的Json格式定好后,就要考虑格式转换了。
借助内置的模板字符串解析函数template
,获取condition执行结果:
function transformValue(value) {
if (typeof value === 'string') {
return `'${value}'`;
}
return value;
}
function parseCondition(condition, data) {
const { variable, operator, value } = condition;
const conditionStr = operator
? `#{${variable} ${operator} ${transformValue(value)}}`
: `#{${variable}}`;
let result;
try {
// 把data传入模板字符串,返回模板字符串执行结果
result = template(conditionStr, data);
} catch (e) {}
return result;
}
其他的只要按照设计思路编写代码就好
function transform(config, data) {
let finalResult;
for (let i = 0; i < config.length; i += 1) {
const { conditions = [], result } = config[i];
if (!conditions.length) {
finalResult = result;
break;
}
if (conditions.every((condition) => parseCondition(condition, data))) {
finalResult = result;
break;
}
}
return finalResult;
}
🌈 代码实现思路
数组配置能力
整体基于rc-field-form
的List
组件实现,为了美化样式增加左侧连接线。
连接线
原理:每组元素以中心为基准绘制向上向下的两条线,隐藏首元素向上的线和末元素向下的线即可。
代码实现:github
result结果项配置
为方便管理和编写代码,整体采用JSON化配置方式实现。示例中代码使用如下结构:
resultFields: [
{
name: 'children',
label: '文本',
wrapperCol: 24,
labelCol: 2,
field: {
type: 'autocomplete',
props: {
allowClear: true,
}
}
},
{
name: 'type',
label: '类型',
field: {
type: 'autocomplete',
props: {
source: [
{ text: 'success', value: 'success' },
{ text: 'danger', value: 'danger' },
],
resultType: 'textType',
},
},
},
{
name: 'style',
label: '样式',
field: 'editor',
}
]
实现原理可以参考这篇文章:sula-插件实现
// 核心代码如下
import { FunctionComponent, ComponentClass } from 'react';
class PluginStore {
plugins = {};
registerField = (key, field) => {
if (this.plugins[key]) return;
this.plugins[key] = field;
};
getPlugins = () => {
return this.plugins;
};
}
const pluginStore = new PluginStore();
export default pluginStore;
👀 结束
插个广告:
前端配置化框架 - sula