Awesome
jformer
根据 vjform 的设计思路,扩展一个简化版的 json 定义方式
集成了 vjform 目前所有开发的扩展,简化了 json 定义方式且支持 vjform 全部功能
配置可用 可视化设计器 编辑
起步
安装包
npm i jformer
项目运行
npm i
npm run dev
特性
支持 vjform 全部原有功能,除此之外加入属性名前缀转换解析,可以直接在属性名上加入特定前缀,实现绑定、计算、事件的转换
整合了组件部分属性,让组件使用更清晰简便
组件属性
整合了部分属性到 config
中
前缀 | 说明 |
---|---|
v-model | 值 |
params | 输入的参数值,只会由组件外部更新 |
config | 包含 fields datasource listeners ,对应 vjform 相应属性 |
components | 单独引用的组件,理论上 jformer 支持 vue 项目中引用的任何组件, 如果未在项目中 use 则可以传到这里实现组件支持 |
fields 组件定义规则
fields 属性定义内部组件,一个内部组件具有如下属性
属性 | 说明 |
---|---|
component | 组件名称,支持所有 html 标签和 vue 项目中引用的组件具有的名称 |
fieldOptions | 组件的属性,是 vue 的 render 方法中 createElement 的第二个参数,具体参考 Vue 官方定义, 自定义组件 props 定义的属性均支持 |
children | 组件的下级组件 |
json 定义示例
{
"component": "div",
"fieldOptions": {
"class": "",
"style": {
"margin": "10px"
},
"domProps": {}
},
"children": []
}
组件支持属性的快捷定义,使用 vjform
注册的快捷定义均可支持,常用快捷定义如下
属性 | 说明 |
---|---|
text | 相当于 fieldOptions.domProps.innerText 属性 |
condition | 根据条件决定组件是否存在,可配合 $ 前缀实现表达式和关联 |
events | 定义组件的事件行为 |
model | 具有交互行为的组件可将值关联到model 里的某个属性,组件的交互行为会改变 model 里的属性值,例如:input 、select |
[
{
"component": "p",
"$:condition": "model.text.length > 4", // 条件显示
"text": "文本"
},
{
"component": "button",
"events": [{ "name": "click", "@:handler": "alert('点击事件')" }]
},
{
"component": "input",
"model": "text" // 关联 model.text 属性
}
]
fieldOptions 里的属性和组件的快捷定义均可以通过前缀定义实现程序逻辑
前缀定义
基于属性名的前缀定义
基于属性名的前缀定义实际上是简化了原来的转换定义,在属性名上加上相应前缀实现转换功能
前缀 | 说明 |
---|---|
$ | 表达式 |
# | 文本模板 |
@ | 事件 |
@<model 属性> | 更新 model 属性的事件 |
^ | 禁止转换 |
表达式
在属性前面加上 $
前缀,实现将表达式的结果关联到属性上,直接支持 js 语法对属性进行处理
{
"component": "div",
"fieldOptions": {
"props": {},
"domProps": {
"$:innerText": "model.text.length + 1" // 这里支持直接取 model,params,datasource 的属性进行计算关联到属性
}
}
}
文本模板
在属性前面加上 #
前缀,实现直接用 es6
的模板字符串输出内容
{
"component": "div",
"fieldOptions": {
"props": {},
"domProps": {
"#:innerText": "输入了 ${model.text}"
}
}
}
事件
在属性前面加上 @
前缀,可将表达式生成一个 function
关联到组件事件上
{
"component": "button",
// 同样支持 vjform 的快速定义事件
"events": [
{
"name": "click",
"@:handler": "alert('clicked!')"
}
]
}
如果在 @
后面加上 model
里的属性名,则可以实现触发事件后将表达式的结果直接更新到 model
属性上
{
"component": "input",
"fieldOptions": {
"domProps": {
"$:value": "model.text"
},
"on": {
"@text:input": "arguments[0].target.value" // 将输入结果更新到 model.text
}
}
}
禁止转换
在属性上加入 ^
前缀,则属性的值不做转换,保持原有的值
当出现 jformer 嵌套 jformer 或者自定义组件里使用了 jformer 的时候,嵌套的 jformer 属性需要在内部进行处理而不是由外层统一转换处理的时候,可以在组件相应属性上加入 ^
标识
实现一个 Repeat 组件,内部使用 jformer 呈现动态视图
<template>
<div v-if="!updating">
<j-former
:key="index"
v-for="(item, index) in data"
:value="item"
:params="{ ...params, $index: index }"
:config="options"
></j-former>
</div>
</template>
<script>
export default {
props: {
data: Array,
params: Object,
options: Object
},
watch: {
'data.length': {
handler() {
this.updating = true
this.$nextTick(() => {
this.updating = false
})
}
}
},
data() {
return {
updating: false
}
}
}
</script>
在 jformer 中使用 Repeat 组件
{
"fields": [
{
"component": "v-repeat", // 自定义组件,实现根据数组数据遍历生成界面,内部使用 jformer 呈现界面
"fieldOptions": {
"props": {
"$:data": "model.listData",
"params": {
"$:listData": "model.listData" // 为了在组件内部实现删除功能,以 params 方式将原数组传递给内部
},
// 使用 ^ 前缀,外层 jformer 不对属性值进行转换,而是由内部处理
"^:options": {
"fields": [
{
"component": "div",
"fieldOptions": {
"style": { "border": "1px solid black", "margin": "10px", "padding": "5px" }
},
"children": [
{ "component": "p", "#:text": "第 ${params.$index + 1} 项" },
{ "component": "input", "model": ["text"] },
{
"component": "button",
"text": "delete",
"fieldOptions": {
"on": { "@:click": "params.listData.splice(params.$index ,1)" }
}
}
]
}
]
}
}
}
},
{
"component": "button",
"text": "add item",
"fieldOptions": { "on": { "@:click": "model.listData.push({})" } }
}
]
}
基于属性值的转换定义
基于属性值的转换定义规则和前面一样,区别是转换的标识放在属性值里,例如:
模板字符串
{
"text": "#:输入了 ${model.text.length} 个字"
}
表达式
{
"text": "$:model.value1 + model.value2"
}
事件
{
"on": {
"click": "@:alert('xxx')", // 执行 js
"input": "@text:arguments[0]" // 更新 model 里的属性
}
}
JSON 定义完整示例
{
"datasource": {
"list": {
"type": "request",
"method": "GET",
"url": "http://localhost:8080/paths/data.json",
"params": {}
},
"detail": {
"type": "request",
"method": "GET",
"url": "http://localhost:8080/paths/detail.json",
"params": {}
}
},
"listeners": [
{
"watch": "model.text",
"deep": false,
"actions": [
{ "@:handler": "datasource.load()", "$:condition": "model.text.length >= 4" },
{ "@:handler": "detail.load()" },
{ "@text2:handler": "model.text + 'xxx'" }
]
}
],
"model": {
"text": "",
"text2": ""
},
"fields": [
{
"component": "input",
"fieldOptions": {
"domProps": {
"$:value": "model.text"
},
"on": {
"@text:input": "arguments[0].target.value"
}
}
},
{
"component": "input",
"model": "text2"
},
{
"component": "div",
"fieldOptions": {
"props": {},
"domProps": {
"$:innerText": "model.text.length + 1"
}
}
},
{
"component": "div",
"fieldOptions": {
"props": {},
"domProps": {
"#:innerText": "输入了 ${model.text}"
}
}
},
{
"component": "button",
"fieldOptions": {
"props": {},
"on": {
"@:click": "datasource.load()"
}
}
}
]
}
追加功能
在事件转换设置 model 属性值时,model 属性名可以使用模板字符串
例如:
{
"component": "el-table",
"children": [
{
"component": "el-table-column",
"children": [
{
"component": "el-input",
"fieldOptions": {
"scopedSlot": "default",
"props": {
"value": "@:model.list[scope.$index].text"
},
"on": {
"input": "@list[${scope.$index}].text:arguments[0]" // 属性用模板字符串更新数组某个索引元素的属性
}
}
}
]
}
]
}