@mecc/form
v1.2.0
Published
Advanced vue form component based on el-form.
Downloads
6
Maintainers
Readme
@mecc/form
Intro 简介
@mecc/form
是一款表单组件,基于 vue
和 element-ui
进行的二次封装,无需繁琐的模板代码,所有的表单配置项均可通过属性传递,使你的代码更干净。
Feature 特色
@mecc/form
没有预设表单组件,所有表单组件均通过render
属性传递,通过灵活的 JSX 语法实现高度自定义组件,因此,它非常小巧,不过需要提前安装vue
和element-ui
;@mecc/form
底层采用$attrs
和$listeners
接收参数和监听事件,无缝对接element-ui
中的Form
文档板块,上手更快(所有<el-form>
接受的参数<mecc-form>
都支持,所有<el-form-item>
接受的参数,column都有相应的字段可以设置,所有的方法、事件和插槽,除了 Form-Item Methods,其他都支持);针对简单场景,可传递
formatter
属性进行格式化输出,在绑定了表单的情况下,可省略render
和formatter
属性,@mecc/form 会默认返回<span>
标签包裹的表单值,当然,你还可以自定义当前的class
!针对复杂表单,比如你的表单可能是下面这样,
@mecc/form
可以满足你!
export default {
data() {
return {
form: {
name: '',
time: {
start: '2020/01',
end: '2020/03'
},
hobby: [
sport: { name: 'basketball', point: 10 },
drink: { name: 'tea', point: 9 } // 根据需要可以动态增减
]
}
表单需要远程搜索?没问题!
更复杂的场景,想使用自定义组件?没问题!
表单太庞大,考虑模块化开发,方便复用?没问题!
Install 安装
- 通过
npm
或者yarn
安装项目
npm i @mecc/form
# 或者
yarn add @mecc/form
- 引用组件,根据需要可全局引入或者局部引入
// 组件依赖 vue 和 element-ui
// 全局引入,可配置选项
// >>> main.js
import Vue from 'vue'
import Element from 'element-ui'
import 'element-ui/libs/theme-chalk/index.css'
import MeccForm from '@mecc/form'
Vue.use(MeccForm, {
emptyText: '--' // 没有内容的占位符,默认为空
})
// 局部引入
// >>> Demo.vue
<script>
import MeccForm from '@mecc/form'
export default {
components: {
MeccForm
}
}
</script>
Options 配置项
配置项内容可在全局引入时设置,或者直接使用 <mecc-form {...options} />
,需注意:直接使用的优先级高于全局配置
| 参数 | 数据类型 | 默认值 | 可选值 | 说明 | |:---------:|:--------:|:------:|:------:|:----------------------------:| | emptyText | String | '-' | - | 表单数据为空时显示的文本内容 | | formClass | String | '' | - | 自定义class名称 |
Form Attributes 表单属性
仅展示必填项和新增项,其余参数见 Element Doc Form #Form Attributes
| 参数 | 数据类型 | 是否必须 | 默认值 | 可选值 | 说明 | |:------:|:--------:|:--------:|:------:|:------:|:--------------------------------:| | column | Array | 是 | - | - | 表单列配置项,具体内容见下方说明 | | model | Object | 是 | - | - | 表单数据 |
Form Methods 表单方法
支持全部 el-form
方法,详见 Element Doc Form #Form Methods
Form Events 表单事件
支持全部 el-form
方法,详见 Element Doc Form #Form Events
Form-Item Attributes 表单列属性
支持全部 el-form-item
属性,详见 Element Doc Form #Form Events
Form-Item Methods 表单列方法
暂不支持
Column 列配置项
仅展示必填项和新增项,其余参数见 Element Doc Form #Form-column Attributes
| 参数 | 数据类型 | 是否必须 | 说明 | |:-----------:|:-------------------------------------:|:--------:|:---------------------------------------------------------------------------------:| | prop | String | 是 | 设置表单列的别名 | | label | String | 是 | 设置表单列的显示标签 | | render | Function(h, form, root) => VNode | 否 | 自定义渲染内容,可选返回VNode | | formatter | Function(form, root) => string | 否 | 自定义渲染内容,可选返回字符串 | | children | Array | 否 | 当数据项类型为[object]时使用,返回column数组,与render/item互斥 | | item | Function(form, root) => [column, ...] | 否 | 当数据项类型为[array]时使用,可动态增删子节点,返回column数组,与render/children互斥 | | value | - | 否 | 当上一级数据项类型为[array],切传递了[item]属性时使用,可设置数据项初始值 | | show | Function(form, root) => boolean | 否 | 是否渲染该列,默认渲染 | | layout | Object | 否 | 设置布局模式,可传入[el-row]和[el-col]支持的所有属性 | | renderLabel | Function(h, form, root)/VNode | 否 | 自定义标签内容 | | renderError | Function(h, form, root, {error})/VNode | 否 | 自定义表单校验信息的显示方式 |
Usage Example 使用示例
- 基础配置示例
- column(表格列配置): 数组类型,必传
- model(表格变量绑定):对象类型,必传
<template>
<mecc-form
ref="meccFormRef"
:model="form"
:column="column"
:rules="rules"
size="small"
label-width="100px"
empty-text="--"
/>
</template>
<script>
export default {
data() {
return {
form: {
name: 'mecc',
age: 18,
gender: 'male',
hobby: ['html', 'css', 'js'],
time: {
start: new Date().getTime(),
end: new Date().getTime()
},
skill: [
{
name: 'math',
point: 90
}
]
},
rules: {
name: [{ required: true, message: '请输入姓名', trigger: 'blur' }]
}
};
},
computed: {
column() {
/**
* 注意:如果不传 render 和 formatter 属性
* 则需在 <mecc-form> 上通过 :model="form" 绑定
*/
return [
// 简易模式
// 返回数据示例:<span>{{ form.name }}<span>
{
prop: 'name',
label: '姓名'
},
// show接收一个方法,根据返回值决定表单项是否显示
{
prop: 'custom_hidden',
label: '自定义隐藏',
show: (form, root) => false
},
// 使用formatter格式化数据
// 返回数据示例:<span class="gender-icon>{{ genderNameMap[form.gender] }}</span>
{
prop: 'gender',
label: '性别',
class: 'gender-icon',
formatter: (form, root) => {
const genderNameMap = { male: '男生', female: '女生' };
return genderNameMap[form.gender];
}
},
/**
* render: 自定义显示元素
* 可使用v-model进行双向绑定
*
* rules: 可单独设置校验规则
* renderLabel: 自定义标签文本的内容,参数为 (h, form, root)
* renderError: 自定义表单校验信息的显示方式,参数为 (h, form, root, {error})
**/
{
prop: 'age',
renderLabel: (h, form, root) => <span style='color: red;'>年龄</span>,
renderError: (h, form, root, { error }) => (
<span style='color: blue'>{error}</span>
),
rules: [
{ required: true, message: '请输入年龄', trigger: 'blur' },
{ type: 'number', min: 1, message: 'hahaha', trigger: 'blur' }
],
render: (h, form, root) => (
<el-input-number
v-model={form.age}
onChange={this.handleChange}
max={20}
label='描述文字'
/>
)
},
// 注意Vue中JSX语法的书写规则,部分属性无法传递,需进行包裹后方可传递
{
prop: 'hobby',
label: '兴趣',
render: (h, form, root) => {
const options = [
{
name: '前端',
id: 'front',
children: [
{
name: 'HTML',
id: 'html'
},
{
name: 'JavaScript',
id: 'js'
},
{
name: 'CSS',
id: 'css'
}
]
},
{
name: '后端',
id: 'back',
children: [
{
name: 'JAVA',
id: 'java'
},
{
name: 'Golang',
id: 'golang'
},
{
name: 'Python',
id: 'python'
}
]
}
];
/**
* 特别注意
* 由于 el-cascader 需要传递名称为 'props' 的属性
* 而在 vue 的 JSX 语法解析中,'props' 属性无法正常传递,所以这里需要特殊处理下
* 详情可参考 https://www.yuque.com/zeka/vue/vu60wg
*/
const cascaderProps = {
options,
props: {
checkStrictly: true,
label: 'name',
value: 'id',
multiple: true,
emitPath: false
},
clearable: true,
filterable: true
};
return (
<el-cascader {...{ props: cascaderProps }} v-model={form.hobby} />
);
}
},
/**
* 嵌套对象的表单 + 快速布局
*
* 配置项:children
* 数据类型:数组(column)
* 含义:设置对象的 column列
*
* 拓展项:layout
* 数据类型:对象
* 含义:引入<el-row>和<el-col>进行布局,接受所有参数
**/
{
prop: 'time',
label: '活动时间',
layout: {
type: 'flex',
align: 'middle',
justify: 'start'
},
children: [
{
prop: 'start',
label: '开始时间',
layout: {
span: 10
},
render: (h, form, root) => {
return <el-date-picker v-model={form.start} />;
}
},
{
prop: 'end',
label: '结束时间',
layout: {
span: 10,
offset: 1
},
render: (h, form, root) => {
return <el-date-picker v-model={form.end} />;
}
}
]
},
/**
* 嵌套数组的表单
*
* 配置项:item
* 数据类型:函数
* 数据结构:(form, root) => column数组
* 含义:设置数组每一项的数据结构
*
* 拓展项:value
* 数据类型:any
* 含义:新增数据项时的默认值
**/
{
prop: 'skill',
label: '能力评级',
item: (form, root) => {
return [
{
prop: 'name',
label: '名称',
render: (h, form, root) => {
return <el-input v-model={form.name} />;
}
},
{
prop: 'point',
label: '评分',
value: 80,
render: (h, form, root) => {
return <el-input-number v-model={form.point} />;
}
}
];
}
}
];
}
},
methods: {
handleChange(val) {
// 这里同样可以使用 el-form 的 Form Methods,同原生 element-ui 的使用方式相同
this.$refs.meccFormRef.validateField('age', (err) => {
if (err) {
console.error(err);
return;
}
console.log(val);
});
}
}
};
</script>
- 模块化开发
当表单的复杂度进一步提高,模块化解耦就成了刚需,@mecc/form
的解决方案:本质上是构造 column 数组。
- 根组件示例:
Demo.vue
<template>
<mecc-form
ref="meccFormRef"
:model="form"
:column="column"
/>
</template>
<script>
import Location from './Location.js';
import Info from './Info.js';
import mixinUtils from './mixin-utils.js'
export default {
mixins: [mixinUitls],
data() {
return {
form: {
name: 'mecc',
location: '',
gender: 'male',
hobby: ['html', 'css', 'js']
}
};
},
computed: {
column() {
return [
{
prop: 'name',
label: '姓名'
},
Location.call(this, '地区'),
...Info.call(this)
];
}
}
};
</script>
- 单模块示例:
Location.js
import { v4 as uuidv4 } from 'uuid';
// 生成随机ID,保存在指定变量下
const location = uuidv4()
/**
* 远程搜索接口
* @param {string} name 搜索关键字
*/
async function fetchData(str) {
if (!str) return;
try {
const resp = await this.$api.search(str);
// this.setValue 双向绑定数据,见后文`mixin.js`
this.setValue(location, resp.data);
} catch (err) {
console.error(err);
this.setValue(location, []);
}
}
export default function Location(customLabel) {
// 初始化列表数据
fetchData();
return {
prop: 'location',
label: customLabel
render: (h, form, root) => (
// this.getValue 获取双向绑定的数据
const optionList = (this.getValue(name) || []).map((opt, idx) => (
<el-option key={idx} label={opt.label} value={opt.value} />
));
return (
<el-select
v-model={form.location}
filterable
clearable
remote
remote-method={fetchData.bind(this)}
>
{optionList}
</el-select>
)
};
}
看过这些示例,相信聪明的你一定发现了,render/renderLabel/renderError
函数中都传递了 h
参数,但大部分的场景下函数体内并没有直接使用,那可不可以去掉呢?
在组件的设计中,我尝试过,在 *.vue
文件中可以正常使用,但是抽离到 *.js
文件的时候就会报错,查阅资料后,在 vue
的官网上有这样一段话:
将h作为createElement的别名是 Vue 生态系统中的一个通用惯例,实际上也是 JSX 所要求的。从 Vue 的 Babel 插件的3.4.0 版本开始,我们会在以 ES2015 语法声明的含有 JSX 的任何方法和 getter 中 (不是函数或箭头函数中) 自动注入const h = this.$createElement,这样你就可以去掉(h)参数了。对于更早版本的插件,如果h在当前作用域中不可用,应用会抛错。
所以,为了兼容更多的情况,我还原了 h
函数,如果你有更好的解决方案,欢迎提PR~
- 组合模块示例:
Info.js
export function Info() {
return [
{
prop: 'gender',
label: '性别',
formatter: (form, root) => {
const genderNameMap = { male: '男生', female: '女生' };
return genderNameMap[form.gender];
}
},
{
prop: 'hobby',
label: '兴趣',
render: (h, form, root) => {
const optionList = ['html', 'css', 'javascript'].map(
return <el-option label={item} value={item} key={item} />
)
return <el-select>{optionList}</el-select>
}
}
]
}
- 辅助函数:
mixin-utils.js
export default {
data() {
return { formFetchData: {} };
},
methods: {
setValue(key, val) {
this.$set(this.formFetchData, key, val);
},
getValue(key) {
return this.formFetchData[key];
}
}
};
欢迎使用配套Table组件:@mecc/table