精华内容
下载资源
问答
  • vue 封装Form表单组件

    千次阅读 2020-07-07 20:17:09
    根据element-ui 的Form表单组件,写了一个公共的组件,可以满足大多数的表单类型的验证,做的这个组件主要是以弹窗的形式在页面上展示 主要的功能: -显示弹窗(根据传入的数据来决定来显示表单) -验证表单信息 -...

    - 前景

    在项目中很常见的交互:回显表单信息 + 验证表单 + 提交表单信息,而表单的类型也有很多(复选框,单选框,下拉框,输入框,文本框等等等)如果多个页面都有表单验证的交互且表单的内容不一样,难道我们就要去写多个表单组件吗???那该怎么办呢????

    - 作用

    根据element-ui 的Form表单组件,写了一个公共的组件,可以满足大多数的表单类型的验证,做的这个组件主要是以弹窗的形式在页面上展示
    主要的功能:
    -显示弹窗(根据传入的数据来决定来显示表单)
    -验证表单信息
    -提交表单信息

    - 具体代码

    App.vue

    想法:把弹窗组件挂载在app上,通过provide/inject实现跨级访问祖先组件的数据

    <template>
        <div id="app">
            <router-view :key="key"/>
            <form-dialog ref="formDialog" />
        </div>
    </template>
    
    <script>
    import formDialog from '@/components/dialogFrom'
    export default {
        name: 'App',
        components: {
            formDialog
        },
        computed: {
            key() {
                return this.$route.path + Math.random()
            }
        },
        provide() {
            const me = this
            return {
                showForm(...args) {
                    me.$refs.formDialog.showDialog(...args)
                }
            }
        }
    }
    </script>
    

    dialogFrom.vue(对应的弹窗组件)

    <template>
        <el-dialog
            :title="formTitle"
            :visible.sync="formVisible"
            :close-on-click-modal="false"
            width="960px"
            class="common-form">
            <el-form ref="form" :model="formData" :rules="formRule" label-width="130px">
                <el-form-item v-for="(item, i) in formColumns" :key="i" :prop="item.property" :label="item.title">
                    <!-- 文本框textarea -->
                    <el-input
                        v-if="item.type === 'textarea'"
                        v-model.number="formData[item.property]"
                        v-bind="item.bind"
                        :placeholder="item.placeholder||'请输入'"
                        rows="7"
                        type="textarea"
                    />
                    <!-- radio -->
                    <div v-else-if="item.type=='radio'" class="radio-wrapper">
                        <el-radio v-for="(opt, j) in item.options" :key="j" :label="opt.id" v-model="formData[item.property]">{{ opt.name }}</el-radio>
                    </div>
                    <!-- 多选框checkbox -->
                    <div v-else-if="item.type=='checkbox'" class="checkbox-wrapper">
                        <el-checkbox v-if="item.isAllCheck" :indeterminate="item.isIndeterminate" v-model="item.checkAll" @change="handleCheckAllChange(item)">全选</el-checkbox>
                        <el-checkbox-group v-model="formData[item.property]" @change="handleCheckedUserGroupChange(item)">
                            <el-checkbox v-for="(opt, j) in item.options" :key="j" :label="opt.id">{{ opt.name }}</el-checkbox>
                        </el-checkbox-group>
                    </div>
                    <!-- 两个input + 加减按钮 -->
                    <div v-else-if="item.type === 'mutliInput'" class="mutli-input-wrapper">
                        <ul class="mutli-input" >
                            <li v-for="(element, index) in formData[item.property]" :key="index" class="input-list">
                                <div class="input-wrapper">
                                    <el-input
                                        v-model="element.key"
                                        :placeholder="item.placeholder ||'请输入'" />
                                    <el-input
                                        v-model="element.value"
                                        :placeholder="item.placeholder||'请输入'" />
                                </div>
                                <div class="handle-btn">
                                    <el-button type="primary" icon="el-icon-plus" circle @click="addConfigInput(item, formData[item.property], index)"/>
                                    <el-button :disabled="item.isDelete" type="danger" icon="el-icon-delete" circle @click="cutConfigInput(item, formData[item.property], index)" />
                                </div>
                            </li>
                        </ul>
                    </div>
                    <!-- 下拉框 + 是否可以多选 -->
                    <el-select
                        v-else-if="item.type === 'select'"
                        :multiple="item.multiple"
                        :disabled="item.disabled || false"
                        v-model="formData[item.property]"
                        v-bind="item.bind"
                        :placeholder="item.placeholder || '请选择'"
                    >
                        <el-option v-for="(opt, j) in item.options" :key="j" :label="opt.name" :value="opt.id" />
                    </el-select>
                    <div v-else-if="item.type === 'upload'" class="avatarUploader">
                        <el-upload
                            :show-file-list="false"
                            :before-upload="beforeUpload('image', item.property)"
                            accept="image/gif, image/jpeg"
                            drag
                            action="">
                            <img v-if="formData[item.property]" :src="formData[item.property]" class="avatar">
                            <i v-else class="el-icon-plus avatar-uploader-icon"></i>
                        </el-upload>
                    </div>
                    <div v-else-if="item.type === 'uploadAudio'" class="avatarUploader">
                        <el-upload
                            :show-file-list="false"
                            :before-upload="beforeUpload('audio', item.property)"
                            drag
                            action="">
                            <i class="el-icon-plus avatar-uploader-icon"></i>
                        </el-upload>
                        <span v-if="formData[item.property]">{{ formData[item.property] }}</span>
                    </div>
                    <!-- 密码 -->
                    <div v-else-if="item.type === 'password'" class="pass-box">
                        <el-input
                            v-model="formData[item.property]"
                            v-bind="item.bind"
                            :placeholder="item.placeholder||'请输入'"
                            :type="item.pwdType"
                        />
                        <span class="show-pwd" @click="showPwd(item)">
                            <svg-icon :icon-class="item.pwdType === 'password' ? 'eye' : 'eye-open'" />
                        </span>
                    </div>
                    <el-input
                        v-else-if="item.type === 'englishText'"
                        v-model="formData[item.property]"
                        v-bind="item.bind"
                        :placeholder="item.placeholder||'请输入'"
                    />
                    <el-input
                        v-else-if="item.type === 'preText'"
                        v-model="formData[item.property]"
                        v-bind="item.bind"
                        :placeholder="item.placeholder||'请输入'">
                        <template slot="prepend">/</template>
                    </el-input>
                    <el-input
                        v-else-if="item.type === 'authText'"
                        v-model="formData[item.property]"
                        v-bind="item.bind"
                        :placeholder="item.placeholder||'请输入'">
                    </el-input>
                    <el-input
                        v-else-if="item.type === 'text' || item.type === 'input'"
                        :disabled="item.disabled || false"
                        v-model="formData[item.property]"
                        v-bind="item.bind"
                        :placeholder="item.placeholder||'请输入'"
                    />
                </el-form-item>
                <el-form-item>
                    <div class="dialog-btn">
                        <el-button type="primary" @click="onSubmit">确定</el-button>
                        <el-button @click="onHide">取消</el-button>
                    </div>
                </el-form-item>
            </el-form>
        </el-dialog>
    </template>
    <script>
    var formCallback = () => {}
    const qiniu = require('qiniu-js')
    export default {
        data() {
            return {
                formTitle: '编辑',
                formVisible: false,
                formColumns: [],
                formRule: [],
                formData: {},
                currentName: '',
                reEnglish: /^[a-zA-Z_]{5,}$/,
                reMath: /^\d{6}$/,
                rePassword: /^.{6,18}$/
            }
        },
        watch: {
            formData: {
                handler(val, oldVal) {
                    this.setDataStatus(val)
                },
                deep: true,
                immediate: true
            }
        },
        methods: {
            setDataStatus(val) {
                // 多个input + 加减按钮
                const mutliInputInfo = this.formColumns.find(item => item.property === 'config_content' && item.type === 'mutliInput')
                // 复选框
                const checkboxInfo = this.formColumns.find(item => item.type === 'checkbox')
                if (mutliInputInfo) {
                    mutliInputInfo.isDelete = val[mutliInputInfo.property].length === 1
                }
                if (checkboxInfo) {
                    this.handleCheckedUserGroupChange(checkboxInfo)
                }
            },
            showDialog({ formColumns, formData, formTitle }, callback) {
                formCallback = callback
                this.formTitle = formTitle || this.formTitle
                this.formVisible = true
                this.formColumns = formColumns
                this.createForm(formColumns, formData)
                this.$nextTick(() => {
                    this.$refs.form.clearValidate()
                })
            },
            onSubmit() {
                this.$refs.form.validate((valid) => {
                    if (valid) {
                        Promise.resolve(formCallback(Object.assign({}, this.formData))).then((result) => {
                            if(result === true){
                                this.formVisible = false
                            }
                        })
                    } else {
                        console.log('validate fail')
                    }
                })
            },
            onHide() {
                this.formVisible = false
                this.$refs.form.resetFields()
                formCallback = () => {}
            },
            createForm(formColumns, formData) {
                var data = {}
                var rule = {}
                formColumns.forEach(col => {
                    if (col.type === 'select' && col.multiple) {
                        data[col.property] =  formData[col.property] === 'undefined' ? [] : formData[col.property]
                        rule[col.property] = this.genRule(col)
                    } else if(col.hasOwnProperty('property')){
                        data[col.property] = formData[col.property]
                        rule[col.property] = this.genRule(col)
                    } else if(typeof formData[col.property] === 'undefined'){
                        data[col.name] = typeof formData[col.name] === 'undefined' ? '' : formData[col.name]
                        rule[col.name] = this.genRule(col)
                    }
                })
                this.formData = data
                this.formRule = rule
            },
            addConfigInput(item, element, index) {
                if (item.isLimit && element.length >= item.isLimit) {
                    this.$message({
                        message: `最多只能添加${item.isLimit}条`,
                        type: 'warning'
                    })
                    return
                }
                element.splice(index + 1, 0, { key: '', value: '' })
            },
            cutConfigInput(item, element, index) {
                if (item.isDelete) return
                element.splice(index, 1)
            },
            // 复选框勾选
            handleCheckedUserGroupChange(item) {
                const checkedCount = this.formData[item.property].length
                item.checkAll = checkedCount === item.options.length
                item.isIndeterminate = checkedCount > 0 && checkedCount < item.options.length
            },
            // 复选框全选
            handleCheckAllChange(item) {
                this.formData[item.property] = item.checkAll ? item.options.map(item => item.id) : []
                item.isIndeterminate = false
            },
            // 得到当前上传图片的name
            getCurrentName(ev) {
                this.currentName = ev.currentTarget.getAttribute('data-name')
            },
            // 上传图片
            beforeUpload(mime, property) {
                // console.log('beforeUpload', mime, property)
                const me = this
                return (file)=>{
                    this.$request.get('file/token', { params: { dir: 'adminas', num: 1, mime: "" }}).then(res => {
                    // console.log(res, 'res')
                        res = res[0]
                        const observable = qiniu.upload(file, res.key, res.token)
                        observable.subscribe({
                            complete(imageInfo) {
                                me.formData[property] = imageInfo.data.url
                            },
                            error(err) {
                                console.log("error:", err)
                            }
                        })
                        return false
                    }).catch(error => {
                        console.error(error)
                    })
                }
            },
            // 是否显示密码
            showPwd(it) {
                if (it.pwdType === 'password') {
                    it.pwdType = ''
                } else {
                    it.pwdType = 'password'
                }
            },
            genRule(column) {
                const r = {
                    message: `请输入${column.title || column.name}`,
                    trigger: 'blur'
                }
                // 验证规则
                this.verifyInput(r, column)
                r.required = !!column.required
                return r
            },
            verifyInput(r, column) {
                switch (column.type) {
                    case 'englishText':
                        r.validator = (rule, value, callback) => {
                            if (!value) {
                                return callback(new Error('英文名称不能为空!'))
                            }
                            if (this.reEnglish.test(value)) {
                                callback()
                            }else{
                                callback(new Error('该项必须为英文字母,并且长度不能少于5个字符!'))
                            }
                        }
                        break
                    case 'authText':
                        r.validator = (rule, value, callback) => {
                            if (!value) {
                                return callback(new Error('验证码不能为空!'))
                            }
                            if (this.reMath.test(value)) {
                                callback()
                            }else{
                                callback(new Error('验证码必须为6个字符的数字!'))
                            }
                        }
                        break
                    case 'password':
                        r.validator = (rule, value, callback) => {
                            if (!value) {
                                return callback(new Error('密码不能为空!'))
                            }
                            if (this.rePassword.test(value)) {
                                callback()
                            } else {
                                callback(new Error('密码长度应该在6-18个字符之间!'))
                            }
                        }
                        break
                    case 'mutliInput':
                        r.validator = (rule, value, callback) => {
                            const isRequiredConfig = value.every(item => item.key !== '' && item.value !== '')
                            // 判断key值是否重复
                            const keyList = []
                            value.forEach(item => {
                                if (keyList.indexOf(item.key) !== -1) {
                                    return callback(`${column.title || column.name}的key值不得重复`)
                                }
                                keyList.push(item.key)
                            })
                            if (!isRequiredConfig) {
                                return callback(r.message)
                            }
                            callback()
                        }
                        break
                }
                r.message = ''
            }
        }
    }
    </script>
    <style>
    .common-form .el-upload-dragger {
        width: 100px !important;
        height: 100px !important;
    }
    </style>
    <style lang="scss">
        .common-form {
            .avatar-uploader .el-upload {
                border: 1px dashed #d9d9d9;
                border-radius: 6px;
                cursor: pointer;
                position: relative;
                overflow: hidden;
            }
            .avatar-uploader .el-upload:hover {
                border-color: #409EFF;
            }
            .avatar-uploader-icon {
                font-size: 28px;
                color: #8c939d;
                width: 100px;
                height: 100px;
                line-height: 100px;
                text-align: center;
            }
            .avatar {
                width: 100px;
                height: 100px;
                display: block;
            }
            .pass-box{
                position: relative;
                .show-pwd{
                    position: absolute;
                    right: 335px;
                    top: 2px;
                    cursor: pointer;
                    user-select: none;
                }
            }
            .mutli-input-wrapper {
                .input-list {
                    font-size: 0;
                    margin-bottom: 15px;
                    &:last-child {
                        margin-bottom: 0;
                    }
                }
                .input-wrapper, .handle-btn {
                    display: inline-block;
                    vertical-align: middle;
                    font-size: 14px;
                }
                .el-input {
                    width: 300px !important;
                    margin-right: 15px;
                    &:last-child {
                        margin-right: 0;
                    }
                }
                .handle-btn {
                    margin-left: 15px;
                }
            }
        }
    </style>
    

    如何去使用上面的这个表单组件呢?只需要在把下面的代码放在点击事件里就可以了

    this.showForm({
        formTitle: '', // 弹窗的标题
         formColumns: [], // 表单的类型
         formData: {}  // 初始化表单数据
     }, (data) => {
         console.log(data) // 表单里输入的数据
         // 把数据提交到后台, 注意这里的return, return true 主要是为了处理只要信息提交成功之后,才可以关闭弹窗
         return this.$request({
             method: method,
             url: url,
             data: data
         }).then(res => {
             this.getTableList()
             return true
         }).catch(error => {
             console.error(error)
         }).finally(() => {})
     })
    

    关于表单组件的各个字段的说明

    formTitle   		string        	弹窗的标题
    
    formColumns 	Array    		表单的类型
    eg:
     [{ property: 'name', title: '配置名:', type: 'text', required: true, placeholder: '请输入配置名' },
         { property: 'config_content', title: '配置内容:', type: 'mutliInput', required: true, placeholder: '配置内容', isLimit: 5, isDelete: false },
         { property: 'type', title: '类型:', type: 'select', required: true, disabled: false, placeholder: '请选择类型', multiple: false, options: [{ id: 'custom', name: 'custom' }, { id: 'service', name: 'service' }] },
         { property: 'checkbox_name', type: 'checkbox', required: true, isAllCheck: true, checkAll: false, isIndeterminate: true, options: [] } ]
    
    property   表示存储表单某类型数据的key值
    title   表示表单某类型对应的标题
    type 表示表单对应的类型
    required Boolean 是否为必填
    
    formData Object 初始化表单的数据
    eg: { checkbox_name: [] }
    

    上面只是进行了一个简单的表单类型的使用说明,大家有需要可以看一下dialogFrom.vue就知道怎么用啦

    展开全文
  • 1.问题 我们公司的项目比较大 表格 表单的页面都不胜数 ,基于此封装了一些 可复用的代码。 2.分析 vue给了我们不一样的前端代码体验 element ui 给我们一套功能强大的组件 减少了我们大量的开发时间 。双剑合璧 ...
  • 前期准备:在项目下创建一个工具包文件夹util,文件夹下创建一个formValidation.js文件 1、formValidation.js 文件 let tel = /^1(3|4|5|7|8|9)\d{9}$/; //手机号码 let name = /^[\u4E00-\u9FA5\uf900-\ufa2d·s]{...

    前期准备:在项目下创建一个工具包文件夹util,文件夹下创建一个formValidation.js文件

    1、formValidation.js 文件

    let tel = /^1(3|4|5|7|8|9)\d{9}$/; //手机号码
    let name = /^[\u4E00-\u9FA5\uf900-\ufa2d·s]{2,10}$/; //姓名
    
    let FormValidate = (function() {
      function FormValidate() {}
      FormValidate.Form = function() {  // From表单验证规则,可用于公用的校验部分
        return {
          // 电话号码的验证
          Tel: (rule, value, callback) => {
            if (!value) {
              return callback(new Error('联系电话不能为空!'))
            }
            if (!tel.test(value)) {
              callback(new Error('手机号码有误,请重新填写!'))
            } else {
              callback()
            }
          },
          
        }
      }
    
      // FromOne表单验证规则  用于FromOne个性化的校验
      FormValidate.FormOne = function() {
        return {
          // 姓名的验证规则
          Name: function(rule, value, callback) {
            if (!value) {
              return callback(new Error('姓名不能为空'))
            }
            if (!name.test(value)) {
              callback(new Error('请输入正确姓名!'))
            } else {
              callback()
            }
          }
        }
      }
    
      return FormValidate
    }())
    exports.FormValidate = FormValidate
    
      return FormValidate
    }())
    
    exports.FormValidate = FormValidate
    
    

    2、在src下的 main.js 入口文件注册全局组件

    import fromCommon from "../util/fromCommon.js" // 1、导入文件
    
    Vue.prototype.$fromCommon = fromCommon;       //  2、注册成全局验证
    

    3、使用

    addRules: {
       tgeiContactPhone: [{
         required: true,
         validator: this.$fromCommon.FormValidate.Form().Tel,
         trigger: 'blur'
       }],
    }
    
    展开全文
  • 参考官网:... Form表单验证规则的封装 <el-form :model="dynamicValidateForm" ref="dynamicValidateForm" label-width="100px" class="demo-dynamic"> <el-form-it...

    参考官网:http://element.eleme.io/#/zh-CN/component/form

    Form表单验证规则的封装

    <el-form :model="dynamicValidateForm" ref="dynamicValidateForm" label-width="100px" class="demo-dynamic">
      <el-form-item
        prop="email"
        label="邮箱"
        :rules="[
          { required: true, message: '请输入邮箱地址', trigger: 'blur' },
          { type: 'email', message: '请输入正确的邮箱地址', trigger: 'blur,change' }
        ]"
      >
      <el-input v-model="dynamicValidateForm.mobil"></el-input>
     </el-form-item>
     <el-form-item 
        label="姓名:" 
        prop="name" 
        :rules="[{ required: true, message: '请输入姓名', trigger: 'blur' }]">
     <el-input v-model="dynamicValidateForm.name"></el-input>
    </el-form-item>
    <el-form-item label="手机号:" prop="mobil" 
        :rules="filter_rules({required:true,type:'mobile'})">
        <el-input v-model="dynamicValidateForm.mobil"></el-input>
    </el-form-item>
    
    </el-form>
    

     全局rules:

    exports.install = function (Vue, options) {
    
        Vue.prototype.filter_rules = function (item){   
        
            return  [{},{}]
           
         }
        
    
    }
    

    main.js 注册 

     验证示例代码:

    import { validateMoneyNumber,qq,mobile,regexn,integer} from '@/utils/validate'
    
    exports.install = function (Vue, options) {
        
        
        /**
         * 注意:  定义type 规则时 不用做非空验证 
         *        只需要传入 required:true 即可
         * */
        /*保留两位小数*/
        const isvalidateMoney = (rule, value, callback) => {
            if(value != null && value != "") {
                if(!validateMoneyNumber(value)) {
                callback(new Error('请输入正确的数字,最多保留两位小数!'))
                } else {
                    callback()
                }
            }
            else{
                callback();
            }
        }
        /*验证QQ号码*/
        const isvalidateQQ= (rule, value, callback) => {        
            if(value != null && value != "") {
                if(!qq(value)) {
                    callback(new Error('您输入的QQ号不正确!'))
                } else {
                    callback()
                }
            }
            else{
                callback();
            }
        }
        /*验证手机号*/
           const isvalidateMobile= (rule, value, callback) => {        
            if(value != null && value != "") {
                if(!mobile(value)) {
                    callback(new Error('您输入的手机号不正确!'))
                } else {
                    callback()
                }
            }
            else{
                callback();
            }
        }
           
           /*含有非法字符(只能输入字母、汉字)*/
           const isvalidateRegexn= (rule, value, callback) => {        
            if(value != null && value != "") {
                if(!regexn(value)) {
                    callback(new Error('含有非法字符(只能输入字母、汉字)!'))
                } else {
                    callback()
                }
            }
            else{
                callback();
            }
        }
            /*请输入正整数*/
           const isvalidateInteger= (rule, value, callback) => {        
            if(value != null && value != "") {
                if(!integer(value)) {
                    callback(new Error('请输入正整数!'))
                } else {
                    callback()
                }
            }
            else{
                callback();
            }
        }
        
        
        
        
        /**
         * 参数 item 
         * required true  必填项
         * maxLength  字符串的最大长度
         * min 和 max 必须同时给 min < max  type=number
         * type 手机号 mobile
         *      邮箱   email
         *      网址   url 
         *      各种自定义类型   定义在 src/utils/validate 中    持续添加中.......
         * */
        
        Vue.prototype.filter_rules = function (item){
            let rules = [];
            if(item.required){
               rules.push({ required: true, message: '该输入项为必填项!', trigger: 'blur' });
            }
            if(item.maxLength){
               rules.push({ min:1,max:item.maxLength, message: '最多输入'+item.maxLength+'个字符!', trigger: 'blur' })
            }
            if(item.min&&item.max){       
               rules.push({ min:item.min,max:item.max, message: '字符长度在'+item.min+'至'+item.max+'之间!', trigger: 'blur' })
            }
            if(item.type){
                let type = item.type;
                switch(type) {
                    case 'email':
                        rules.push({ type: 'email', message: '请输入正确的邮箱地址', trigger: 'blur,change'  });
                        break;
                    case 'qq':
                        rules.push( { validator: isvalidateQQ, trigger: 'blur,change' });
                        break;
                    case 'mobile':
                        rules.push( { validator: isvalidateMobile, trigger: 'blur,change' });
                        break;    
                    case 'regexn':
                        rules.push( { validator: isvalidateRegexn, trigger: 'blur,change' });
                        break;    
                    case 'integer':
                        rules.push( { validator: isvalidateInteger, trigger: 'blur,change' });
                        break;        
                    default:
                        rule.push({});
                        break;
                }
            }
           
         
            return rules;
        };
    };
    

     

    转载于:https://www.cnblogs.com/boonya/p/7902925.html

    展开全文
  • src/components/data/seller/create/contract.vue &lt;template&gt; &lt;create-portlet title="合同信息"&gt; &lt;j-form :model="model" ref="form"&...
    
     

    src/components/data/seller/create/contract.vue

    <template>
      <create-portlet title="合同信息">
        <j-form :model="model" ref="form">
          <div class="row">
    
            <div class="col-xs-4">
              <j-form-item
                prop="contract.cooperationTypeMap"
                :rules="[
                {validator: selectRequired, message: '合作方式必须', trigger: 'change'}
                ]"
              >
                <j-select
                  v-model="model.contract.cooperationTypeMap"
                  :options="methodCooperation"
                >
                  <span slot="left" class="input-group-addon">合作方式:</span>
                  <span slot="right" class="input-group-addon"><span class="font-red">*</span></span>
                </j-select>
              </j-form-item>
            </div>
    
            <div class="col-xs-4">
              <j-form-item
                prop="contract.isSignContract"
                :rules="[
                  {required: true, message: '是否签合同必须', trigger: 'change'}
                ]"
              >
                <div class="input-group j-input-group">
                  <span class="input-group-addon">是否签合同:</span>
                  <j-radio-group v-model="model.contract.isSignContract">
                    <j-radio checked-value="1">是</j-radio>
                    <j-radio checked-value="0">否</j-radio>
                  </j-radio-group>
                  <span slot="right" class="input-group-addon"><span class="font-red">*</span></span>
                </div>
              </j-form-item>
            </div>
    
            <template v-if="model.contract.isSignContract == 1">
              <div class="col-xs-4">
                <j-form-item
                  prop="contract.startTime"
                  :rules="[
                        {required: true, message: '合同结束时间必须', trigger: 'change'}
                      ]"
                >
                  <j-date-group
                    v-model="model.contract.startTime"
                    :end-date="model.contract.endTime"
                  >
                    <span slot="left" class="input-group-addon">合同开始时间:</span>
                    <span slot="right" class="input-group-addon font-red">*</span>
                  </j-date-group>
                </j-form-item>
              </div>
    
              <div class="col-xs-4">
                <j-form-item
                  prop="contract.endTime"
                  :rules="[
                        {required: true, message: '合同结束时间必须', trigger: 'change'}
                      ]"
                >
                  <j-date-group
                    v-model="model.contract.endTime"
                    :start-date="model.contract.startTime"
                  >
                    <span slot="left" class="input-group-addon">合同结束时间:</span>
                    <span slot="right" class="input-group-addon"><span class="font-red">*</span></span>
                  </j-date-group>
                </j-form-item>
              </div>
    
              <div class="col-xs-4">
                <j-form-item
                  prop="contract.contractNo"
                  :rules="
                [
                  {required: true, message: '合同编号必须', trigger: 'blur'},
                  {validator: range, min: 1, max: 20, isString: true, message: '合同编号必须1-20字', trigger: 'blur'},
                  {validator: eliminateSpace, message: '合同编号不能有空格', trigger: 'blur'}
                ]"
                >
                  <j-input-group
                    v-model="model.contract.contractNo"
                  >
                    <span slot="left" class="input-group-addon">合同编号:</span>
                    <span slot="right" class="input-group-addon"><span class="font-red">*</span></span>
                  </j-input-group>
                </j-form-item>
              </div>
            </template>
    
    
          </div>
    
        </j-form>
      </create-portlet>
    </template>
    <script>
      import createPortlet from 'components/base/createPortlet'
      import {
        isPhone,
        selectRequired,
        arrayRequired,
        isNumber,
        requiredIf,
        range,
        isText,
        eliminateSpace
      } from 'assets/scripts/base/validator'
      import {unixTimestampFilter} from 'assets/scripts/base/filters'
      import {getMerchantCooperationWay} from 'assets/scripts/business/store'
      import {mapGetValue, isEqual, dotData} from 'assets/scripts/base/functions'
      import cascadeAddress from 'components/base/cascadeAddress'
      import jFormItem from 'components/base/form/jFormItem'
      import jInputGroup from 'components/base/form/jInputGroup'
      import jForm from 'components/base/form/jForm'
      import jSelect from 'components/base/form/jSelect'
      import jDateGroup from 'components/base/form/jDateGroup'
      import jAddressGroup from 'components/base/form/jAddressGroup'
      import jCheckbox from 'components/base/form/jCheckbox'
      import jCheckboxGroup from 'components/base/form/jCheckboxGroup'
      import jRadio from 'components/base/form/jRadio'
      import jRadioGroup from 'components/base/form/jRadioGroup'
      export default {
        name: 'dataSellerCreateCompany',
        components: {
          createPortlet,
          cascadeAddress,
          jFormItem,
          jInputGroup,
          jForm,
          jSelect,
          jDateGroup,
          jAddressGroup,
          jCheckbox,
          jCheckboxGroup,
          jRadio,
          jRadioGroup
        },
        props: {
          detail: {
            type: Object,
            required: true
          }
        },
        data () {
          return {
            model: {
              contract: {
                startTime: '',
                endTime: '',
                isSignContract: '1',
                cooperationTypeMap: {value: '', text: ''},
                contractNo: ''
              }
            },
            methodCooperation: []
          }
        },
        watch: {
          detail (val, oldVal) {
            if (isEqual(val, oldVal)) {
              return
            }
            // 合作方式
            const cooperationType = dotData(val, 'merchantContractInfo.cooperationType') || dotData(val, 'contract.cooperationType')
            if (cooperationType) {
              this.model.contract.cooperationTypeMap = {value: cooperationType, text: ''}
            }
            // 合同
            let isSignContract = dotData(val, 'merchantContractInfo.isSignContract')
            if (isSignContract !== 0 || isSignContract !== 1) {
              isSignContract = dotData(val, 'contract.isSignContract')
            }
            isSignContract = String(isSignContract)
            if (isSignContract === '0' || isSignContract === '1') {
              this.model.contract.isSignContract = isSignContract
            }
            // 开始时间
            const startTime = dotData(val, 'merchantContractInfo.startTime') || dotData(val, 'contract.startTime')
            this.model.contract.startTime = unixTimestampFilter(startTime, 'YYYY-MM-DD')
    
            const endTime = dotData(val, 'merchantContractInfo.endTime') || dotData(val, 'contract.endTime')
            if (endTime) {
              this.model.contract.endTime = unixTimestampFilter(endTime, 'YYYY-MM-DD')
            }
    
            const contractNo = dotData(val, 'merchantContractInfo.contractNo') || dotData(val, 'contract.contractNo')
            this.model.contract.contractNo = contractNo
            this.model.contract.id = dotData(val, 'merchantContractInfo.id') || dotData(val, 'contract.id')
          }
        },
        created () {
          getMerchantCooperationWay().then(arr => {
            this.methodCooperation = arr
          })
        },
        methods: {
          isPhone,
          selectRequired,
          arrayRequired,
          isNumber,
          requiredIf,
          range,
          isText,
          eliminateSpace,
          // 验证并获取数据
          getModel () {
            return this.$refs.form.validate().then((result) => {
              if (result) {
                let data = this.model
                return mapGetValue(data)
              }
              return result
            })
          },
          // 获取数据没有验证
          getModelWithoutValidation () {
            let data = this.model
            return mapGetValue(data)
          }
        }
      }
    </script>
    

    点击 公司地址输入框:弹出地址选择 组件:

    src/components/data/seller/create/company.vue

    <template>
      <create-portlet title="公司信息">
        <j-form :model="model" ref="form">
          <div class="row">
    
            <div class="col-xs-3 col-sm-3">
              <j-form-item
                prop="company"
                :rules="[
                  {validator: range, min: 1, max: 16, isString: true, message: '公司名称必须1-16字', trigger: 'blur'},
                  {validator: eliminateSpace,  message: '公司名称不能有空格', trigger: 'blur'}
                ]"
              >
                <j-input-group
                  v-model="model.company"
                >
                  <span slot="left" class="input-group-addon">公司名称:</span>
                </j-input-group>
              </j-form-item>
            </div>
    
            <div class="col-xs-3 col-sm-3">
              <j-form-item
                prop="legalPerson"
                :rules="[
                  {validator: range, min: 1, max: 16, isString: true, message: '法人代表必须1-16字', trigger: 'blur'},
                  {validator: eliminateSpace,  message: '法人代表不能有空格', trigger: 'blur'}
                ]"
              >
                <j-input-group
                  v-model="model.legalPerson"
                >
                  <span slot="left" class="input-group-addon">法人代表:</span>
                </j-input-group>
              </j-form-item>
            </div>
    
    
          </div>
    
          <div class="row">
    
            <div class="col-xs-6 col-sm-6">
              <j-form-item
              >
                <j-address-group
                  :level="3"
                  v-model="model.addressArr"
                >
                  <span slot="left" class="input-group-addon">公司地址:</span>
                </j-address-group>
              </j-form-item>
            </div>
    
            <div class="col-xs-6 col-sm-6">
              <j-form-item
                prop="hqAddress"
                :rules="[
                {validator: range, min: 1, max: 50, isString: true, message: '详细地址必须少于50字', trigger: 'blur'}
               ]"
              >
                <j-input-group
                  v-model="model.hqAddress"
                >
                  <span slot="left" class="input-group-addon">详细地址:</span>
                </j-input-group>
              </j-form-item>
            </div>
    
          </div>
    
        </j-form>
      </create-portlet>
    </template>
    <script>
      import createPortlet from 'components/base/createPortlet'
      import {
        isPhone,
        selectRequired,
        arrayRequired,
        isNumber,
        requiredIf,
        range,
        isText,
        eliminateSpace
      } from 'assets/scripts/base/validator'
      import {dotData, copy, isEqual} from 'assets/scripts/base/functions'
      import cascadeAddress from 'components/base/cascadeAddress'
      import jFormItem from 'components/base/form/jFormItem'
      import jInputGroup from 'components/base/form/jInputGroup'
      import jForm from 'components/base/form/jForm'
      import jSelect from 'components/base/form/jSelect'
      import jDateGroup from 'components/base/form/jDateGroup'
      import jAddressGroup from 'components/base/form/jAddressGroup'
      import jCheckbox from 'components/base/form/jCheckbox'
      import jCheckboxGroup from 'components/base/form/jCheckboxGroup'
      import jRadio from 'components/base/form/jRadio'
      import jRadioGroup from 'components/base/form/jRadioGroup'
      export default {
        name: 'dataSellerCreateCompany',
        components: {
          createPortlet,
          cascadeAddress,
          jFormItem,
          jInputGroup,
          jForm,
          jSelect,
          jDateGroup,
          jAddressGroup,
          jCheckbox,
          jCheckboxGroup,
          jRadio,
          jRadioGroup
        },
        props: {
          detail: {
            type: Object,
            required: true
          }
        },
        data () {
          return {
            model: {
              company: '',
              legalPerson: '',
              addressArr: [],
              hqAddress: ''
            }
          }
        },
        watch: {
          detail (val, oldVal) {
            if (isEqual(val, oldVal)) {
              return
            }
            this.setEditValue(val)
          }
        },
        created () {
          this.setEditValue(this.detail)
        },
        methods: {
          isPhone,
          selectRequired,
          arrayRequired,
          isNumber,
          requiredIf,
          range,
          isText,
          eliminateSpace,
          // 验证并获取数据
          getModel () {
            return this.$refs.form.validate().then((result) => {
              if (result) {
                let data = copy(this.model, true)
                data.hqProvinceNo = dotData(data, 'addressArr.0.value')
                data.hqCityNo = dotData(data, 'addressArr.1.value')
                data.hqDistrictNo = dotData(data, 'addressArr.2.value')
                delete data.addressArr
                return data
              }
              return result
            })
          },
          // 获取数据没有验证
          getModelWithoutValidation () {
            let data = copy(this.model, true)
            data.hqProvinceNo = dotData(data, 'addressArr.0.value')
            data.hqCityNo = dotData(data, 'addressArr.1.value')
            data.hqDistrictNo = dotData(data, 'addressArr.2.value')
            delete data.addressArr
            return data
          },
          setEditValue (val) {
            // 公司名称
            const company = dotData(val, 'company')
            this.model.company = company
            // 法人代表
            const legalPerson = dotData(val, 'legalPerson')
            this.model.legalPerson = legalPerson
            // 公司地址
            const hqProvinceNo = dotData(val, 'hqProvinceNo')
            const hqProvince = dotData(val, 'hqProvince')
    
            const hqCityNo = dotData(val, 'hqCityNo')
            const hqCity = dotData(val, 'hqCity')
    
            const hqDistrictNo = dotData(val, 'hqDistrictNo')
            const hqDistrict = dotData(val, 'hqDistrict')
            if (hqProvinceNo && hqProvince) {
              this.model.addressArr = [
                {text: hqProvince, value: hqProvinceNo},
                {text: hqCity, value: hqCityNo},
                {text: hqDistrict, value: hqDistrictNo}
              ]
            }
            const hqAddress = dotData(val, 'hqAddress')
            this.model.hqAddress = hqAddress
          }
        }
      }
    </script>
    

    封装 表单块: jFormItem:   src/components/base/form/jFormItem.vue

    <template>
      <div class="form-group j-form-item" :class="{'has-error': hasError}">
        <slot></slot>
        <span v-if="hasError" class="error-block">{{ error }}</span>
      </div>
    </template>
    <script>
      import parent from 'assets/scripts/mixins/parent'
      import AsyncValidator from 'async-validator'
      export default {
        name: 'jFormItem',
        componentName: 'jFormItem',
        mixins: [parent],
        props: {
          rules: {
            type: [Array, Object, Boolean],
            default () {
              return []
            }
          },
          prop: {
            type: String,
            default: 'key'
          }
        },
        data () {
          return {
            error: '',
            value: '',
            unique: null
          }
        },
        computed: {
          hasError () {
            return this.error.length !== 0
          },
          jForm () {
            return this.findParentByComponentName('jForm')
          }
        },
        created () {
          this.$on('j-form-validate', this.validate)
          if (this.jForm !== null) {
            this.unique = Symbol('j-form-item')
            this.jForm.$emit.apply(this.jForm, ['j.form.addField'].concat([this, this.unique]))
          }
          // 一个传递value
          this.$on('j.form.addValueFiled', (field, key) => {
            this.jForm.$emit.apply(this.jForm, ['j.form.addValueFiled'].concat([field, key]))
          })
          this.$on('j.form.removeValueField', (key) => { // 这里以前是 addValueFiled
            this.jForm.$emit.apply(this.jForm, ['j.form.removeValueField'].concat([key]))
          })
        },
        destroyed () {
          if (this.jForm !== null) {
            this.jForm.$emit.apply(this.jForm, ['j.form.removeField'].concat([this.unique]))
          }
        },
        methods: {
          validate (event = null) {
            return new Promise((resolve, reject) => {
              const rules = this.getRules(event)
              if (rules.length > 0) {
                let descriptor = {}
                descriptor[this.prop] = rules
                let obj = {}
                obj[this.prop] = this.getValue(this.prop)
                const validator = new AsyncValidator(descriptor)
                validator.validate(obj, {firstFields: true}, (errors, fields) => {
                  this.error = errors ? errors[0].message : ''
                  resolve(!this.hasError)
                })
              } else {
                resolve(true)
              }
            })
          },
          // 获取验证规则
          getRules (type) {
            if (type) {
              return this.rules.filter(rule => {
                return !rule.trigger || rule.trigger.indexOf(type) !== -1
              })
            }
            return this.rules
          },
          // 重置
          resetField () {
            this.error = []
          },
          getValue (name) {
            return this.getPropByPath(this.jForm.model, name).v
          },
          // 根据path 获取值
          getPropByPath (obj, path) {
            let tempObj = obj
            path = path.replace(/\[(\w+)\]/g, '.$1')
            path = path.replace(/^\./, '')
            let keyArr = path.split('.')
            let i = 0
            for (let len = keyArr.length; i < len - 1; ++i) {
              let key = keyArr[i]
              if (key in tempObj) {
                tempObj = tempObj[key]
              } else {
                throw new Error('please transfer a valid prop path to form item!')
              }
            }
            return {
              o: tempObj,
              k: keyArr[i],
              v: tempObj[keyArr[i]]
            }
          }
        }
      }
    </script>
    

    封装 下拉选项:src/components/base/form/jSelect.vue

    <template>
      <div
        class="input-group j-select-group dropdown"
        @keyup.down.stop.prevent.capture="down"
        @keyup.up.stop.prevent="up"
        @keyup.enter.stop.prevent="enter"
        @click.prevent="selectClick"
        :class="{open: isOpen}"
      >
        <slot name="left"></slot>
        <div class="j-select-group-cell">
          <div class="j-select-group-box on-popper" v-if="multiple">
            <span v-for="item of selectedOptions" class="tag label label-info j-select-group-label">
              {{ item.text }}
              <span data-role="remove" @click.prevent.stop="removeMultiple(item)">x</span>
            </span>
            &nbsp;
          </div>
          <input v-else type="text" class="form-control on-popper"
                 :readonly="readonly"
                 v-model="inputValue"
                 :tabindex="tabindex"
                 @keyup.prevent="inputHandle"
                 :placeholder="placeholder"
          />
          <span class="j-input-placeholder-span" v-if="!supportPlaceHolder && isFocus === false && !inputValue"
                @click.prevent="labelClick">{{ placeholder }}</span>
          <i class="fa fa-angle-down fa-j-angle-down font-green"></i>
        </div>
        <ul class="dropdown-menu dropdown-menu-default" style="width: 100%;">
          <li>
            <ul class="dropdown-menu-ul j-select-down" :data-height="slimHeight">
              <li v-for="(option, index) in innerOptions" @click.prevent="choose(option, index, $event)"
                  :class="{active: option.isSelected, disabled: option.disabled}">
                <a href="javascript: void (0);">
                  {{ option.text }}
                </a>
              </li>
            </ul>
          </li>
        </ul>
        <slot name="right"></slot>
        <slot name="right-btn"></slot>
      </div>
    </template>
    <script>
      import {supportPlaceHolder, copy, isEqual, dotData} from 'assets/scripts/base/functions'
      import parent from 'assets/scripts/mixins/parent'
      import lodash from 'lodash'
      export default {
        name: 'JSelect',
        mixins: [parent],
        props: {
          options: {
            type: Array,
            default () {
              return []
            }
          },
          slimHeight: {
            type: Number,
            default: 250
          },
          multiple: {
            type: Boolean,
            default: false
          },
          remote: {
            type: [Function, Boolean],
            default: false
          },
          placeholder: {
            type: String,
            default: ''
          },
          value: {
            type: [String, Object, Array],
            default: ''
          },
          forceReadonly: {
            type: Boolean,
            default: false
          },
          forbiddenInputUndefined: {
            type: Boolean,
            default: false
          },
          returnItem: {
            type: Object,
            default: function () {
              return {}
            }
          }
        },
        data () {
          return {
            isOpen: false,
            initSlimScroll: false,
            innerOptions: copy(this.options, true),
            inputValue: '',
            isFocus: false,
            unique: '',
            downUpIndex: null
          }
        },
        watch: {
          options (val, oldVal) {
            if (isEqual(val, oldVal)) {
              return
            }
            this.innerOptions = copy(val, true)
            this.initSlimScroll = false
            this.getInnerOptions().then(options => {
              this.selectValue(options, this.value)
            })
          },
          value (val, oldVal) {
            if (isEqual(val, oldVal)) {
              return
            }
            if ((!val.value && this.multiple === false) || (this.multiple === true && Array.isArray(val) && val.length === 0)) {
    //          this.resetField()
              this.inputValue = ''
            }
            if (lodash.isFunction(this.remote)) {
              const text = dotData(this.value, 'text')
              if (text) {
                this.setInputValue(text)
              }
            } else {
              this.getInnerOptions().then(options => {
                this.selectValue(options, val)
              })
            }
          }
        },
        computed: {
          jFormItem () {
            return this.findParentByComponentName('jFormItem')
          },
          jFormTableItem () {
            return this.findParentByComponentName('jFormTableItem')
          },
          hiddenOpen () {
            return () => {
              this.isOpen = false
            }
          },
          keyUp () {
            return (event) => {
    //          event.preventDefault()
            }
          },
          selectedOptions () {
            let temp = []
            for (let v of this.innerOptions) {
              if (v.isSelected) {
                temp.push({value: v.value, text: v.text})
              }
            }
            return temp
          },
          readonly () {
            return !lodash.isFunction(this.remote)
          },
          supportPlaceHolder () {
            return supportPlaceHolder()
          },
          jForm () {
            return this.findParentByComponentName('jForm')
          },
          tabindex () {
            if (this.disabled) {
              return '-1'
            }
            return false
          }
        },
        created () {
          document.addEventListener('click', this.hiddenOpen, true)
          this.getInnerOptions().then(options => {
            this.selectValue(options, this.value)
          })
          if (this.jForm !== null) {
            this.unique = Symbol('j-form-value')
            this.jForm.$emit.apply(this.jForm, ['j.form.addValueFiled'].concat([this, this.unique]))
          }
          // 设置默认值
          if (lodash.isFunction(this.remote)) {
            const text = dotData(this.value, 'text')
            if (text) {
              this.setInputValue(text)
            }
          }
        },
        destroyed () {
          document.removeEventListener('click', this.hiddenOpen, true)
          if (!this.forbiddenInputUndefined) {
            this.$emit('input', {value: '', text: ''})
          }
          if (this.jForm !== null) {
            this.jForm.$emit.apply(this.jForm, ['j.form.removeValueField'].concat([this.unique]))
          }
        },
        methods: {
          close () {
            this.isOpen = false
            document.removeEventListener('keydown', this.keyUp)
          },
          inputHandle () {
            this.$emit('remoteValue', this.inputValue)
            this.getRemoteOptions()
          },
          choose (option, index, $event) {
            if (option.disabled) {
              return
            }
            if (this.multiple) {
              if (option.isSelected) {
                this.$set(option, 'isSelected', false)
              } else {
                this.$set(option, 'isSelected', true)
              }
              this.$emit('choose', this.selectedOptions)
            } else {
              if (option.isSelected) {
                $event.stopPropagation()
                return
              }
              this.clearSelected()
              this.$set(option, 'isSelected', true)
              $event.stopPropagation()
              this.$emit('choose', this.selectedOptions[0], this.returnItem)
            }
            this.emitAndSetInputValue()
            this.emitValidate()
          },
          emitValidate () {
            if (this.jFormItem) {
              this.jFormItem.$emit('j-form-validate', 'change')
            }
            if (this.jFormTableItem) {
              this.jFormTableItem.$emit('j-form-validate', 'change')
            }
          },
          selectClick () {
            if (this.forceReadonly) {
              return
            }
            if (lodash.isFunction(this.remote)) {
              this.$el.querySelector('input').focus()
              this.getRemoteOptions()
            } else {
              this.openMenu()
            }
          },
          openMenu () {
            this.isOpen = true
            this.$nextTick(_ => {
              const height = this.$el.querySelector('.dropdown-menu-ul').clientHeight
              if (height >= this.slimHeight) {
                window.App.initSlimScroll(this.$el.querySelector('.j-select-down'))
              }
            })
          },
          clearSelected () {
            this.innerOptions.forEach(v => {
              this.$set(v, 'isSelected', false)
            })
          },
          removeMultiple (item) {
            const value = item.value
            for (let v of this.innerOptions) {
              if (v.value === value) {
                this.$set(v, 'isSelected', false)
                this.$emit('input', this.selectedOptions)
                this.emitValidate()
                return
              }
            }
          },
          handleBlur () {
            this.isFocus = false
          },
          handleFocus () {
            this.isFocus = true
          },
    
          labelClick () {
            this.$el.querySelector('input').focus()
          },
          getInnerOptions () {
            return Promise.resolve(this.innerOptions)
          },
          selectValue (options, value) {
            this.clearSelected()
            const type = (typeof value).toLowerCase()
            if (type === 'string' || type === 'number') {
              for (let option of options) {
                if (Array.isArray(value)) {
                  value.forEach(v => {
                    if (option.value === v.value) {
                      this.$set(option, 'isSelected', true)
                    }
                  })
                } else {
                  if (option.value === value.value) {
                    this.$set(option, 'isSelected', true)
                  }
                }
              }
            } else if (type === 'object') {
              for (let option of options) {
                if (Array.isArray(value)) {
                  value.forEach(v => {
                    if (option.value === v.value) {
                      this.$set(option, 'isSelected', true)
                    }
                  })
                } else {
                  if (option.value === value.value) {
                    this.$set(option, 'isSelected', true)
                  }
                }
              }
            }
            this.emitAndSetInputValue()
          },
          emitAndSetInputValue () {
            if (this.selectedOptions.length === 0) {
              return
            }
            if (this.multiple) {
              this.$emit('input', this.selectedOptions)
            } else {
              this.$emit('input', this.selectedOptions[0])
              const text = dotData(this.selectedOptions[0], 'text')
              this.inputValue = text
            }
          },
          // 设置inputValue
          setInputValue (value) {
            this.inputValue = value
          },
          getRemoteOptions: lodash.debounce(function () {
            if (lodash.isFunction(this.remote)) {
              if (!this.inputValue) {
                this.$emit('input', [])
                return
              }
              this.remote(this.inputValue).then(options => {
                if (options.length === 0) {
                  this.innerOptions = [{value: null, text: '无数据', disabled: true}]
                } else {
                  this.innerOptions = copy(options, true)
                }
                this.openMenu()
              })
            }
          }, 800),
          // 清空表单
          resetField () {
            this.clearSelected()
            if (this.multiple) {
              this.$emit('input', [])
            } else {
              this.$emit('input', {text: '', value: ''})
              this.inputValue = ''
            }
          },
          // 向下或向上
          setDownUpIndex (type) {
            if (type === 1) { // 向下
              if (this.downUpIndex === null) {
                this.downUpIndex = 0
              } else {
                if (this.downUpIndex + 1 === this.innerOptions.length) {
                } else {
                  this.downUpIndex++
                }
              }
            } else if (type === 2) {
              if (this.downUpIndex === null) {
                this.downUpIndex = 0
              } else {
                if (this.downUpIndex === 0) {
                } else {
                  this.downUpIndex--
                }
              }
            }
            return this.downUpIndex
          },
          down ($event) {
            $event.preventDefault()
            const index = this.setDownUpIndex(1)
            this.clearSelected()
            this.$set(this.innerOptions[index], 'isSelected', true)
          },
          up ($event) {
            $event.preventDefault()
            const index = this.setDownUpIndex(2)
            this.clearSelected()
            this.$set(this.innerOptions[index], 'isSelected', true)
          },
          enter ($event) {
            this.$emit('choose', this.selectedOptions[0], this.returnItem)
            this.emitAndSetInputValue()
            this.emitValidate()
            this.close()
          }
        }
      }
    </script>
    

    封装 “地址选择”表单组件:src/components/base/form/jAddressGroup.vue

    <template>
      <div class="dropdown j-input-group input-group j-address-group readonly" @click.prevent="openMenu" v-clickoutside="handleClose" :class="{open: isOpen}">
        <slot name="left"></slot>
        <div class="j-address-group-box" style="display: table-cell; position: relative">
          <input type="text" class="form-control"
                 readonly
                 :value="inputText"
                 :placeholder="placeholder"
                 style="border-style: solid; cursor: pointer;"
          />
          <i class="fa fa-angle-down fa-j-angle-down font-green"></i>
        </div>
    
        <div class="dropdown-menu address-drop-down-tab" :style="{width: dropDownWidth}">
          <ul class="nav nav-tabs">
            <li :class="{disabled: !province.length, active: current == 1}" @click.prevent="changeCurrent(province,1)">
              <a href="javascript:void (0);">
                省
              </a>
            </li>
            <li v-if="level >= 2" :class="{disabled: !city.length, active: current == 2}"
                @click.prevent="changeCurrent(city,2)">
              <a href="javascript:void (0);">
                市
              </a>
            </li>
            <li v-if="level >= 3" :class="{disabled: !area.length, active: current == 3}"
                @click.prevent="changeCurrent(area,3)">
              <a href="javascript:void (0);">
                区
              </a>
            </li>
            <li v-if="level >= 4" :class="{disabled: !street.length, active: current == 4}"
                @click.prevent="changeCurrent(street,4)">
              <a href="javascript:void (0);">
                街道
              </a>
            </li>
          </ul>
          <div class="tab-content address-drop-content scroller" data-height="250px">
            <div class="tab-pane fade" :class="{'in active': current == 1}">
              <div v-if="canChangeProvince === false" class="alert alert-danger"><strong>提示!</strong> 不可选择 </div>
              <label class="label"
                     :class="[provinceNo == item.value ? 'label-info' : 'label-success']"
                     v-for="item in province"
                     v-text="item.text"
                     @click.prevent.stop="chooseProvince(item, $event)">
              </label>
            </div>
            <div class="tab-pane fade" v-if="level >= 2" :class="{'in active': current == 2}">
              <div v-if="canChangeCity === false" class="alert alert-danger"><strong>提示!</strong> 不可选择 </div>
              <label class="label"
                     :class="[cityNo == item.value ? 'label-info' : 'label-success']"
                     v-for="item in city"
                     v-text="item.text"
                     @click.prevent.stop="chooseCity(item, $event)">
              </label>
            </div>
            <div class="tab-pane fade" v-if="level >= 3" :class="{'in active': current == 3}">
              <div v-if="canChangeDistrict === false" class="alert alert-danger"><strong>提示!</strong> 不可选择 </div>
              <label class="label"
                     :class="[areaNo == item.value ? 'label-info' : 'label-success']"
                     v-for="item in area"
                     v-text="item.text"
                     @click.prevent.stop="chooseArea(item, $event)">
              </label>
            </div>
            <div class="tab-pane fade" v-if="level >= 4" :class="{'in active': current == 4}">
              <div v-if="canChangeStreet === false" class="alert alert-danger"><strong>提示!</strong> 不可选择 </div>
              <label class="label"
                     :class="[streetNo == item.value ? 'label-info' : 'label-success']"
                     v-for="item in street"
                     v-text="item.text"
                     @click.prevent.stop="chooseStreet(item, $event)">
              </label>
            </div>
          </div>
        </div>
        <slot name="right"></slot>
      </div>
    </template>
    <script>
      import parent from 'assets/scripts/mixins/parent'
      import {arrayColumn, isEqual, dotData} from 'assets/scripts/base/functions'
      import {mapActions} from 'vuex'
      import clickoutside from 'assets/directive/clickoutside'
      export default {
        name: 'JAddressGroup',
        mixins: [parent],
        directives: { clickoutside },
        props: {
          value: {
            type: [Array, String],
            default () {
              return []
            }
          },
          level: {
            type: Number,
            default: 4
          },
          changeOnSelect: {
            type: Boolean,
            default: false
          },
          separator: {
            type: String,
            default: '/'
          },
          dropDownWidth: {
            type: String,
            default: '500px'
          },
          forbiddenInputUndefined: {
            type: Boolean,
            default: false
          },
          isChange: {
            type: Boolean,
            default: false
          },
          canChangeProvince: {
            type: [String, Boolean],
            default: ''
          },
          canChangeCity: {
            type: [String, Boolean],
            default: ''
          },
          canChangeDistrict: {
            type: [String, Boolean],
            default: ''
          },
          canChangeStreet: {
            type: [String, Boolean],
            default: ''
          },
          readonly: {
            type: Boolean,
            default: false
          },
          placeholder: {
            type: String,
            default: ''
          },
          returnItem: {
            type: Object,
            default: function () {
              return {}
            }
          }
        },
        data () {
          return {
            choose: Array.isArray(this.value) ? this.value : [],
            province: [],
            city: [],
            area: [],
            street: [],
            current: 1,
            spanText: '',
            pullRight: false,
            isOpen: false,
            inputText: ''
          }
        },
        watch: {
          value (val, oldVal) {
            if (isEqual(val, oldVal)) {
              return
            }
            this.initValue(val)
          }
        },
        computed: {
          jFormItem () {
            return this.findParentByComponentName('jFormItem')
          },
          hiddenOpen () {
            return () => {
              this.isOpen = false
            }
          },
          provinceNo () {
            return dotData(this.choose, '0.value') || ''
          },
          cityNo () {
            return dotData(this.choose, '1.value') || ''
          },
          areaNo () {
            return dotData(this.choose, '2.value') || ''
          },
          streetNo () {
            return dotData(this.choose, '3.value') || ''
          }
        },
        mounted () {
          window.App.initSlimScroll('.scroller')
          if (Array.isArray(this.value) && this.value.length > 0) {
            this.setInputText(this.value)
          }
          // 获取初始的city area street
          this.initValue(this.value)
        },
        destroyed () {
          if (!this.forbiddenInputUndefined) {
            this.$emit('input', undefined)
          }
        },
        methods: {
          initValue (val) {
            if (val && typeof val === 'string') {
              val = JSON.parse(val)
            }
            if (Array.isArray(val)) {
              this.choose = val
              this.setInputText(this.choose)
            }
            if (Array.isArray(val) && val.length === 0) {
              this.current = 1
              this.province = []
              this.city = []
              this.area = []
              this.street = []
            }
          },
          openMenu () {
            if (this.readonly) {
              return
            }
            if (this.isChange) {
              this.isOpen = false
            } else {
              this.isOpen = true
              if (this.province.length < 1) {
                this.getAddressData({value: '10000'}).then(arr => {
                  this.province = arr
                })
              }
              // 获取市
              if (this.provinceNo) {
                this.getAddressData({value: this.provinceNo}).then(arr => {
                  this.city = arr
                })
              }
              // 获取区
              if (this.cityNo) {
                this.getAddressData({value: this.cityNo}).then(arr => {
                  this.area = arr
                })
              }
              // 获取街道
              if (this.areaNo) {
                this.getAddressData({value: this.areaNo}).then(arr => {
                  this.street = arr
                })
              }
            }
          },
          // 关闭
          handleClose () {
            this.isOpen = false
          },
          chooseProvince (data, $event) {
            if (this.canChangeProvince === false) {
              return
            }
            this.$emit('chooseP', this.choose)
            this.chooseAddress(0, data)
            const nextLevel = 2
            this.city = []
            this.area = []
            this.street = []
            if (this.level === 1) {
              this.closeAddress($event)
              return
            }
            this.getAddressData({value: data.value}).then((city) => {
              this.city = city
              this.current = nextLevel
            })
          },
          chooseCity (data, $event) {
            if (this.canChangeCity === false) {
              return
            }
            this.$emit('chooseC', this.choose)
            this.chooseAddress(1, data)
            const nextLevel = 3
            this.area = []
            this.street = []
            if (this.level === 2) {
              this.closeAddress($event)
              return
            }
            this.getAddressData({value: data.value}).then((area) => {
              this.area = area
              this.current = nextLevel
            })
          },
          chooseArea (data, $event) {
            if (this.canChangeDistrict === false) {
              return
            }
            this.$emit('chooseA', this.choose)
            this.chooseAddress(2, data)
            const nextLevel = 4
            if (this.level === 3) {
              this.closeAddress($event)
              return
            }
            this.street = []
            this.getAddressData({value: data.value}).then((street) => {
              this.street = street
              this.current = nextLevel
            })
          },
          chooseStreet (data, $event) {
            if (this.canChangeDistrict === false) {
              return
            }
            this.$emit('canChangeStreet', this.choose)
            this.chooseAddress(3, data)
            if (this.level === 4) {
              this.closeAddress($event)
              return
            }
          },
          changeCurrent (data, level) {
            if (data.length) {
              this.current = level
            }
          },
          closeAddress ($event) {
            $event.stopPropagation()
            this.isOpen = false
            this.current = 1
            this.$emit('input', this.choose)
            this.$emit('change', this.choose, this.returnItem)
            this.setInputText(this.choose)
            this.emitValidate('change')
          },
          setInputText (choose) {
            this.inputText = arrayColumn(choose, 'text').join(this.separator)
          },
          chooseAddress (index, data) {
            this.choose[index] = data
            if (index < 1) {
              this.$emit('chooseProvinceData', this.choose, this.returnItem)
            }
            this.choose.splice(index + 1)
            if (this.changeOnSelect) {
              this.$emit('input', this.choose)
              this.$emit('change', this.choose, this.returnItem)
              this.setInputText(this.choose)
              this.emitValidate('change')
            }
          },
          // 发起验证
          emitValidate (type) {
            if (this.jFormItem) {
              this.jFormItem.$emit('j-form-validate', type)
            }
          },
          refreshInputText () {
            this.setInputText(this.choose)
          },
          ...mapActions([
            'getAddressData'
          ])
        }
      }
    </script>
    

    封装时间日期选择  组件:

    components/base/form/jDateGroup
    <template>
      <div class="input-group j-input-group readonly">
        <slot name="left"></slot>
        <input type="text"
               :value="value"
               class="form-control j-date-group"
               :readonly="true"
               style="border-style: solid; cursor: pointer;"
        />
        <slot name="right"></slot>
      </div>
    </template>
    <script>
      import moment from 'moment'
      import parent from 'assets/scripts/mixins/parent'
      export default {
        name: 'JDateGroup',
        mixins: [parent],
        props: {
          autoclose: {
            type: Boolean,
            default: true
          },
          format: {
            type: String,
            default: 'yyyy-mm-dd'
          },
          minView: {
            type: Number,
            default: 2
          },
          maxView: {
            type: Number,
            default: 3
          },
          minuteStep: {
            type: Number,
            default: 5
          },
          startDate: {
            type: String,
            default: ''
          },
          endDate: {
            type: String,
            default: ''
          },
          value: {
            type: String
          },
          pickerPosition: {
            type: String,
            default: 'bottom-right'
          },
          forbiddenInputUndefined: {
            type: Boolean,
            default: false
          },
          readonly: {
            type: Boolean,
            default: false
          }
        },
        data () {
          return {
            inputElement: '',
            unique: ''
          }
        },
        computed: {
          jFormItem () {
            return this.findParentByComponentName('jFormItem')
          },
          jFormTableItem () {
            return this.findParentByComponentName('jFormTableItem')
          },
          jForm () {
            return this.findParentByComponentName('jForm')
          }
        },
        watch: {
          startDate (val, oldVal) {
            if (val === oldVal) {
              return
            }
            const inputElement = this.getInputElement()
            window.$(inputElement).datetimepicker('setStartDate', val)
          },
          endDate (val, oldVal) {
            if (val === oldVal) {
              return
            }
            const inputElement = this.getInputElement()
            window.$(inputElement).datetimepicker('setEndDate', val)
          }
        },
        created () {
          if (this.jForm !== null) {
            this.unique = Symbol('j-form-value')
            this.jForm.$emit.apply(this.jForm, ['j.form.addValueFiled'].concat([this, this.unique]))
          }
        },
        destroyed () {
          if (!this.forbiddenInputUndefined) {
            this.$emit('input', undefined)
          }
          if (this.jForm !== null) {
            this.jForm.$emit.apply(this.jForm, ['j.form.removeValueField'].concat([this.unique]))
          }
        },
        mounted () {
          if (this.readonly === false) {
            const inputElement = this.getInputElement()
            let options = {
              autoclose: this.autoclose,
              language: 'zh-CN',
              format: this.format,
              minuteStep: this.minuteStep,
              minView: this.minView,
              maxView: this.maxView,
              pickerPosition: this.pickerPosition
            }
            if (this.startDate) {
              options.startDate = this.startDate
            }
            if (this.endDate) {
              options.endDate = this.endDate
            }
            if (this.value) {
              options.initialDate = this.value
            }
            window.$(inputElement).datetimepicker(options).on('changeDate', (ev) => {
              const momentFormat = this.format.replace(/y/g, 'Y').replace(/m/g, 'M').replace(/d/g, 'D').replace(/i/g, 'm')
              this.$emit('input', moment(ev.date).subtract(8, 'h').format(momentFormat))
              this.emitValidate('change')
            })
          }
          if (this.value) {
            this.setInputText(this.value)
          }
        },
        methods: {
          getInputElement () {
            this.inputElement = this.inputElement ? this.inputElement : this.$el.querySelector('.j-date-group')
            return this.inputElement
          },
          emitValidate (type) {
            if (this.jFormItem) {
              this.jFormItem.$emit('j-form-validate', type)
            }
            if (this.jFormTableItem) {
              this.jFormTableItem.$emit('j-form-validate', type)
            }
          },
          // 设置文本内容
          setInputText (text) {
            const inputElement = this.getInputElement()
            window.$(inputElement).val(text)
          },
          // 重置表单
          resetField () {
            this.setInputText('')
            this.$emit('input', undefined)
          }
        }
      }
    </script>
    

     

    展开全文
  • 最近我们公司将前端框架由easyui 改为 vue+...1.问题 我们公司的项目比较大 表格 表单的页面都不胜数 ,基于此封装了一些 可复用的代码。2.分析 vue给了我们不一样的前端代码体验 element ui 给我们一套功能...
  • 最近我们公司将前端框架由easyui 改为 vue+elementui 。...1.问题 我们公司的项目比较大 表格 表单的页面都不胜数 ,基于此封装了一些 可复用的代码。 2.分析 vue给了我们不一样的前端代码体验 element ui 给我们...
  • 在ant-design-vueform表单验证中,有很多封装很好的组件,但不完全满足于你的需求,更多的是在现有的基础上扩展出其他。需求:点击按钮弹出dialog选择项,当用户选取需要项,关闭dialog,根据判断用户是否选择一个...
  • 最近我们公司将前端框架由easyui 改为 vue+...1.问题 我们公司的项目比较大 表格 表单的页面都不胜数 ,基于此封装了一些 可复用的代码。2.分析 vue给了我们不一样的前端代码体验 element ui 给我们一套功能...
  • 效果图片动态表单和子表单 upholdjx/tao-form简介: 基于 Vue 和 ElementUI 构建的form表单和子表单动态构建,具有动态渲染,参数校验,数据双向绑定动态表单目录结构表单组件库文件说明:config.js 扫描widget下的...
  • api 接口封装 export function userTypeObj(obj,callback) { // obj 表示你的参数集合 // 返回的接口草callback里边 return request({ method:"PUT", url:"接口地址", data:obj, transformRequest: ...
  • 在methods:中写: //检查手机号 isCellPhone(val) { if (!/^1(3|4|5|6|7|8)\d{9}$/.test(val)) { return false; } else { return true; } }, 在template中 <el-form :model...
  • 基于element-plus form表单二次封装之前发布过一篇基于element form表单的二次封装 之前发布过一篇基于element form表单的二次...由于element-plus 已发布多时,现在基于vue3 以及element-puls做了封装form表单 ...
  • VUEform表单里面嵌套table并且实现每一个单元格都有input框校验 最近项目需求要求form表单中有一项“主要领导成员”和“联系人”(他们的输入格式一致)而且领导人至少输入一位,最多输入两位位,效果图如下 校验...
  • vue封装form插件

    2019-06-19 21:57:21
    我们知道网页开发中很多的地方都要重复的利用form表单的input输入,今天就来分一个InputGroup插件。此插件目前应用于输入手机号获取验证码注册或登陆。 1:在组件中用一个div包裹input、button、、错误提醒的dom元素...
  • vue+element-ui form表单的简单封装

    千次阅读 2019-08-30 17:48:48
    vue+element-ui form表单的简单封装,加上校验逻辑
  • import { emitter } from './ValidateForm.vue' const emailReg = /^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/ interface RuleProp { type: 'required' | 'email' | 'custom'; ...
  • Vue 项目封装表单类型组件 iform....1、i-input.vue 公用组件,是i-form组件的最重要的组件,主要封装的就是这个,i-form就是将i-input组件套了一层外壳(el-form)。因此,总的来说,是我们这章节主要封装的对象。 2
  • 但我们往往忽略了其中的实现逻辑,如果想深入了解其中的实现细节,本文章从0到1,手把手教你封装一个属于自己的Form组件! 实例代码 https://github.com/zhengjunxiang/vue-formForm 组件概览表单类组件有多种,比如...
  • 封装表单校验 2.1基础校验 在src下新建一个model文件夹,在model里建两个文件,一个存放基础校验规则,一个存放自定义检验规则如下图所示: 需要校验的html表单代码如下图所示(prop绑定的是需要校验的字段,这个...

空空如也

空空如也

1 2 3 4 5 ... 11
收藏数 204
精华内容 81
关键字:

vue封装form表单

vue 订阅