在开发表单页面时, 经常碰到一种情况。 同一表单存在 新建
编辑
查看
几种状态。页面的大体组成基本相同,但又需要做部分针对当前状态的处理,处理这类问题,一般存在两种方式
- 直接拷贝多分,在不同页面中做处理
- 优点: 处理简单,页面各个状态之间不会相互影响
- 缺点: 复用率低,后期修改麻烦
- 在同一页面中做处理,页面跳转时提供状态标识符号,通过标识符切换页面显示
- 优点:复用率高,修改同一
- 缺点:逻辑复杂度高, 状态处理麻烦,页面逻辑不利于阅读
jsx + mixins 页面复用
这里提供一种使用 jsx 编写基础页面,子类页面通过修改基础页面对象,实现属于子类的页面对象
例子
这里是用 ant 实现简单表单,表单存在两种状态
新增
编辑
- 目录结构
- view
- base-form.jsx
- create-form.vue
- edit-form.vue
- base-form.jsx
/*
* 表单组件列表
*/
export function formCmps(vm, form){
return [
{
key: 'orderName',
cmp: (h) =>{
return (
<a-form-item label='订单名称' label-col={{span: 4}} wrapper-col={ {span: 12} }>
{ form.getFieldDecorator('orderName')(<a-input/>) }
</a-form-item>
)
}
},
{
key: 'orderType',
cmp: (h) =>{
const options = [
{
key: 'inland',
label: '国内'
},
{
key: 'overseas',
label: '海外'
}
]
return (
<a-form-item label='订单类型' label-col={{span: 4}} wrapper-col={ {span: 12} }>
{
form.getFieldDecorator('orderType')(
<a-select>
{ options.map(({key, label}) => <a-select-option key={key}> { label } </a-select-option> ) }
</a-select>
)
}
</a-form-item>
)
}
},
{
key: 'createTime',
cmp: (h) =>{
return (
<a-form-item label='创建时间' label-col={{span: 4}} wrapper-col={ {span: 12} } >
{
form.getFieldDecorator('createTime', {
initialValue: (new Date()).toString()
})( <a-date-picker format="YYYY-MM-DD HH:mm:ss" style={ {width: '100%'} } /> )
}
</a-form-item>
)
}
},
{
key: 'controll',
cmp: (h) =>{
return (
<a-form-item style={ {'text-align': 'center'} } label='' label-col={{span: 4}} wrapper-col={ {span: 12} }>
<a-button type='primary' onClick={ vm.submit }> 提交 </a-button>
<a-button onClick={ vm.reset } style={{ marginLeft: '10px' }} > 重置 </a-button>
</a-form-item>
)
}
}
]
}
/**
* 渲染函数(render)
* 这里提供渲染模板,通过包装函数传递相应的上下文对象既 vue 实例
*/
export function warpRender({vm, formItems}){
formItems = formItems || formCmps(vm, vm.form)
// 这里 h 时必须的
return function(h){
return (
<div class="form">
<h4 class="title">
{ vm.title }
</h4>
<a-form form={ vm.form }>
{ formItems.map(item => item.cmp(h)) }
</a-form>
</div>
)
}
}
/**
* 基础组件实例
* 这里将挂载通用属性及方法
*/
const BaseForm = {
name: 'base-form',
computed:{
orderId(){
return this.$route.params.id
},
},
data(){
return{
title: '默认标题',
form: this.$form.createForm(this)
}
},
methods: {
reset(){},
submit(){},
upDate(data){
console.log(`submit data:`, data)
},
},
}
export default BaseForm
- create-form.vue
<script>
import Vue from 'vue'
import BaseForm, { warpRender, formCmps } from './base-form'
export default {
name: 'create',
mixins: [BaseForm],
data(){
return {}
},
render(h){
const { form } = this
// 渲染基础函数
return warpRender({ vm: this })(h)
},
methods:{
// 改写默认方法
submit(){
this.upDate(this.form.getFieldsValue())
}
}
}
</script>
<style lang="stylus" scoped>
.form
overflow hidden
max-width 800px
margin 20px auto
padding 10px
border-radius 4px
box-shadow 0 12px 6px -4px rgba(120, 120, 120, .1)
background #efefef;
.title{
margin-bottom 30px;
}
</style>
新增
一基础信息出入为主,主要继承基础组件
- edit-form.vue
<script>
import Vue from 'vue'
import BaseForm, { warpRender, formCmps } from './base-form'
// 编辑页私有组件
function editCmp(vm, form){
return [
{
key: 'editTime',
cmp: (h) => {
return (
<a-form-item label='编辑时间' format="YYYY-MM-DD HH:mm:ss" label-col={{span: 4}} wrapper-col={ {span: 12} } >
{
form.getFieldDecorator('editTime')( <a-date-picker style={ {width: '100%'} } /> )
}
</a-form-item>
)
}
}
]
}
export default {
name: 'edit',
mixins: [BaseForm],
data(){
return {
title: '编辑页',
sourceForm: {},
}
},
render(h){
const { form } = this
const cmps = formCmps(this, form).map(item => {
if(item.key === 'createTime'){
item.cmp = (h) => (
// 替换时间组件
<a-form-item label='创建时间' label-col={{span: 4}} wrapper-col={ {span: 12} } >
{ form.getFieldValue('createTime') }
</a-form-item>
)
}
return item
})
// 添加私有组件
cmps.splice(cmps.length - 1, 0, ...editCmp(this, form))
return warpRender({ vm: this, formItems: cmps })(h)
},
methods:{
submit(){
this.upDate(this.form.getFieldsValue())
},
// 添加私有方法
getOrder(id){
return Promise.resolve({
orderName: 'F16',
orderType: 'inland',
createTime: (new Date()).toString(),
})
},
reset(){
console.log(this.sourceForm)
this.form.setFieldsValue(this.sourceForm)
}
},
created(){
// 设置表单初始数据
this.getOrder(this.id).then(data => this.sourceForm = data ).then(this.reset)
}
}
</script>
<style lang="stylus" scoped>
.form
overflow hidden
max-width 800px
margin 20px auto
padding 10px
border-radius 4px
box-shadow 0 12px 6px -4px rgba(120, 120, 120, .1)
background #efefef
.title
margin: 20px auto 30px auto
margin-bottom 30px
font-size 18px
text-align center
</style>
编辑
状态下, 需要提供默认数据。同时禁用部分功能
优点
- 保证复用的情况下,劲量分割差异。
缺点
- 基础组件编写相较
模板模式
复杂,更接近react
总结
这里使用 jsx 的目的在于可以动态编辑页面模板。在需要增减组件时,能提供比较灵活的手段。
其实这种处理方式,使用 vue 组件和模板方式依然可以做到。只是相较之下使用组件的方式,将产生很多碎片文件,且需要提供数据接口。对于只是小幅修改的情况比较麻烦。
网友评论