Vue表单
丘比特惩罚陆 人气:0普通表单
我们先可以创建一个普通的表单,我们知道的是表单是相对比较复杂的,antv被我们诟病为就是其表单,这个设计出来的理念就是和别的组件库不一样,我们就在这篇文章分成四个部分来讲述,现在我们的第一个部分,就是我们的普通的表单。
操作的步骤:先再main.js引入注册Form和Input,在BasicForm拷贝刚才的表单布局,这个就是最基础的表单。接下来就是需要添加一个错误校验,有对应的属性传递对应的状态等。拷贝validateStatus到item中加上对应的help文本,如果想要动态输入的话也可以,给input双向绑定fieldA和fieldB,在data里面注册这两个值,同时validateStatus也变成动态的指向fieldAStatus,文本也是动态指向fieldAHelp。通过监听fieldA的长度,如果小于5改变状态和文本,否则就都置为空就行了。同样的,给按钮也绑定一个事件handleSubmit,拷贝刚才的监听逻辑,如果通过就console.log输出一下。
如此就完成了一个最简单的表单校验,像这种手动添加错误校验信息是最灵活简单但是不够渐变,最好是组件内部自动校验。
<!--登录表单区域--> <el-form ref="loginFormRef" :model="loginForm" :rules="loginFormRules" label-width="0px" class="login_form"> <!--用户名--> <el-form-item prop="username"> <el-input v-model="loginForm.username" prefix-icon="el-icon-user"></el-input> </el-form-item> <!--密码--> <el-form-item prop="password"> <el-input v-model="loginForm.password" prefix-icon="el-icon-lock" type="password"></el-input> <!--type="password使得密码隐藏"--> </el-form-item> <!--按钮区域--> <el-form-item class="btns"> <el-button type="primary" @click="login">mua</el-button> <el-button type="info" @click="resetLoginForm">重置</el-button> </el-form-item> </el-form>
<script> export default { data () { return { // 这里是登录表单数据 loginForm: { username: 'admin', password: '991130' }, // 表单的验证规则对象 loginFormRules: { username: [ { required: true, message: '请输入登录名称', trigger: 'blur' }, { min: 3, max: 10, message: '长度在 3 到 10 个字符', trigger: 'blur' } ], password: [ { required: true, message: '请输入登录密码', trigger: 'blur' }, { min: 6, max: 15, message: '长度在 6 到 15 个字符', trigger: 'blur' } ] } } }, // 点击重置按钮,重置登录表单 methods: { resetLoginForm () { // console.log(this) this.$refs.loginFormRef.resetFields() }, login () { this.$refs.loginFormRef.validate(async valid => { if (!valid) return const { data: res } = await this.$http.post('login', this.loginForm) if (res.meta.status !== 200) return this.$message.error('登录失败!') this.$message.success('登录成功') // 1.将登陆成功之后的token,保存到客户端的sessionstorage中 // 1.1项目中除了登录之外的其他api接口,必须在登陆之后才能访问 // 1.2token只应在当前网站打开期间生效,所以将token保存在sessionstorage中 window.sessionStorage.setItem('token', res.data.token) // 2.通过编程式导航跳转到后台主页,路由地址为/home this.$router.push('home') }) } } } </script>
初始数据与自动校验与动态赋值
目前表单方案主要有两种,一种是基于双向绑定的表单校验(iviewelement)另一种就是ant。数据加规则映射到表单视图,通过双向绑定在表单变化的时候继续同步到数据中对比规则,这个是理想情况。但是很经常是多个地方使用一个数据,使用双向绑定的话可以实现表单与其它数据一起修改,这可能是想要的,但是也有可能是我们希望表单数据提交后经过后台等等最后返回出来的数据再进行展示,对这种情况我们就需要复制或者深复制一份数据,这个就是双向绑定的模型。Ant选择把所有数据放到表单,data只是提供一个初始化,后面表单就是一个黑盒,里面处理的不会影响外面的数据,后续数据如果需要同步到data就可以通过API来同步到data和其它组件等等。
回到form,在data里面实例化一个from(因为main中有use自动把$from挂载到vue实例上,所以就可以访问到),同时把this进去(用于组件底层内部,数据改变时调用这个this来更新当前组件也就是basicForm)。把这个实例传递到最外层那,如此就不需要手动监听、双向绑定和watch监听了。给input绑定一个v-decorator,接收数组,第一个是字段名称、第二个是配对的规则(required是否必须,min最小长度,message报错信息)、第三个是初始值的字段。v-decorator的源码如下,可以看到什么都没做,仅仅是提供一个标志位,也就是当我们渲染我们的item时,会查找子组件,如果带有这个标志位就会劫持然后交由我们的from去控制
export function antDecorator(Vue) { return Vue.directive('decorator',{}); } export default { // install: Vue =>{ antDecorator(Vue); } }
按钮的点击事件也要改一下,通过form的validateFields,接收err错误信息和values接收到的值,如果没有err说明校验通过。如此就实现了自动校验。接下来我们想要数据改变后同步给data里面的fieldA和fieldB(即后台校验通过后),我们当然可以一个个赋值,也可以通过Object.assign往this上赋值即可。如果想通过接口动态改变表单的值也可以通过form的setFieldsValueAPI来动态改变其值,值得注意的是初始化的数据只是在第一次生效,后续改变都要通过这个API。
handleSubmit(){ this.form.validateFields((err,value)=> { if(!err) { console.log(values); object.assign(this,values)//全部赋值 } } }
data() { this.form = this.$form.createForm(this)//初始化一个表单 return { formLauout:'horizontal', fieldA:'hello', fieldB:'' }; } mounted () { setTimeout(() = >{ //定时改变一次舒适化改变 this.form.setFieldsValue({ fieldA:'byebye'}) },3000) },
发布表单
结合vuex和vue-router,建一个store文件夹在其下面建modules文件夹,下面再建一个form.js,导入router、request,定义一个state对象,下包含step对象里面只有payAccount即可。定义一个actions对象,下面一个异步的submitStepForm接收{commit}和{payload},调用request请求,url是‘/api/form’,方式是post,数据是payload,返回请求后我们commit一个mutation,命名为saveStepFormData,把payload传过去,然后路由跳转到‘formep-form/result’结果页。
定义一个mutations对象,saveStepFormData接收state和{payload},我们要在里面改变state里面的step值(拓展运算符摊开step和payload,因为理论上是有很多的数据)。最后是暴露出这三个对象(同时开启命名空间!)在store下面再建一个文件index.js,导入刚才的form.js,在实例化中加一个modules对象,里面丢个form,如此就完成了store,不过需要注意的是外面还有一个store.js(无用可以删),而之前的mian.js默认是找到这个store.js(原本是找到刚才新建的index,被外面这个拦住了),可能会报错,重启工程或者指明更清楚的路径就行。
既然定义了API就往mock里面加多一个form.js,修改方法为POST,返回数据为message即可。接下来就是正式写表单了,首先是第一个表单Step1,手写
一个a-form指定为horizontal,item的label为付款账户。往data里面丢一个布局配置formItemLayout对象,里面设置labelCol和wrapperCol,当然我们还需要在外面创建一个form实例指向当前的form,item的labelCol也指向对象的labelCol,wrapperCol也指定一下。再加一个a-input,指定decorator,名称为payAccount,初始化值需要从vuex中取(可以利用计算属性拿到),规则是必填否则报错误提示。再来一个item,里面放button,type为primary,触发点击事件handleSubmit,在方法中定义,先从this中拿到form、$router、$store就不用一个个this.,再调用form的validateFields校验一下,无错则提交给vuex的store,直接commit而不是dispatch,因为第一步无需异步,只是储存数据到step字段,给个type和payload(传递values字段),完成后router跳转到第二步(/formep-form-confirm),如此就完成了第一步的表单。接下来开发这个第二步的表单,复制第一步的,把input删除了,这一步就是要显示这个付款账户的{{step.payAccount}},再来个item,里面放input用来输入密码,type改为password,下一个item中加多一个button为提交,另一个改为上一步,分别绑定handleSubmit和onPrev两个事件,上一步的按钮给个样式(margin-left让它们两个有点距离)。onPrev的话是
将路由跳转到‘/formep-form/info’,提交事件的话就是通过dispatch,来调vuex中的actions中的submit最后由其commit来修改store中的值。注意这里传值的时候记得多传递一个第一步的信息(...step、...values),完成之后无需跳转(因为commit完就在那里执行了跳转的逻辑,不用重复写)。表单三的话给个小提示就行
<template> <div> <!--面包屑导航区--> <el-breadcrumb separator-class="el-icon-arrow-right"> <el-breadcrumb-item :to="{ path: '/home' }">首页</el-breadcrumb-item> <el-breadcrumb-item>商品管理</el-breadcrumb-item> <el-breadcrumb-item>商品列表</el-breadcrumb-item> </el-breadcrumb> <!-- 卡片视图区域 --> <el-card> <el-row :gutter="20"> <el-col :span="8"> <el-input placeholder="请输入内容" v-model="queryInfo.query" clearable> <el-button slot="append" icon="el-icon-search" @click="getGoodsList"></el-button> </el-input> </el-col> <el-col :span="4"> <el-button type="primary" @click="goAddpage">添加商品</el-button> </el-col> </el-row> <!-- table表格区域 --> <el-table :data="goodslist" border stripe> <el-table-column type="index"> </el-table-column> <el-table-column label="商品名称" prop="goods_name"></el-table-column> <el-table-column label="商品价格(元)" prop="goods_price" width="95px"></el-table-column> <el-table-column label="商品重量" prop="goods_weight" width="70px"></el-table-column> <el-table-column label="创建时间" prop="add_time" width="140px"> <template slot-scope="scope">{{scope.row.add_time | dateFormat}}</template> </el-table-column> <el-table-column label="操作" width="130px"> <template slot-scope="scope"> <el-button size="mini" type="primary" icon="el-icon-edit">编辑</el-button> <el-button size="mini" type="danger" icon="el-icon-delete" @click="removeById(scope.row.goods_id)">删除</el-button> </template> </el-table-column> </el-table> <!-- 分页区域 --> <el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="queryInfo.pagenum" :page-sizes="[5, 10, 15, 20]" :page-size="queryInfo.pagesize" layout="total, sizes, prev, pager, next, jumper" :total="total" background> </el-pagination> </el-card> </div> </template> <script> export default { data () { return { queryInfo: { query: '', pagenum: 1, pagesize: 10 }, // 商品列表 goodslist: [], // 总书记条数 total: 0 } }, created () { this.getGoodsList() }, methods: { // 根据分页获取对应的商品列表 async getGoodsList () { const { data: res } = await this.$http.get('goods', { params: this.queryInfo }) if (res.meta.status !== 200) { return this.$message.error('获取商品列表失败!') } this.$message.success('获取商品列表成功!') console.log = res.data this.goodslist = res.data.goodslist this.total = res.data.total }, handleSizeChange (newSize) { this.queryInfo.pagesize = newSize this.getGoodsList() }, handleCurrentChange (newPage) { this.queryInfo.pagenum = newPage this.getGoodsList() }, async removeById (id) { const confirmResult = await this.$confirm('此操作将永久删除该商品, 是否继续?', '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).catch(err => err) if (confirmResult !== 'confirm') { return this.$message.info('已取消删除!') } // eslint-disable-next-line no-undef const { data: res } = await this.$http.delete(`goods/${id}`) if (res.meta.status !== 200) { return this.$message.error('删除失败!') } this.$message.success('删除成功!') this.getGoodsList() }, goAddpage () { this.$router.push('/goods/add') } } } </script> <style lang="less" scoped> </style>
加载全部内容