最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • uni-app 封装通用表单组件

    正文概述 掘金(Friendlyjesse)   2021-01-25   979

    表单封装

    由于表单的通用性,封装了一个基于uview的表单,因为后端需要的是每编辑一条就保存一条便在其中暴露出来了userLeave事件,目前只有几个通用组件,后续可以添加通用扩展

    ComForm/index.vue

    <template>
      <view class="com-form">
        <u-form
          v-if="formRest"
          :model="formData"
          ref="uForm"
          label-width="140"
        >
          <u-form-item
            v-for="(item, key) in formDesc"
            :key="key"
            :label="['rate', 'PropertyType'].includes(item.type) ? '' : item.label"
            :label-style="item.labelStyle"
          >
            <!-- input -->
            <u-input @blur="onUserLeave({item, value: formData[key]})" :clearable="false" v-if="item.type === 'input'" v-model="formData[key]" :input-align="item.attr.inputAlign" :placeholder="item.attr.placeholder" />
            
            <!-- select -->
            <template v-if="item.type === 'select'">
              <u-input 
    						type="select"
    						:value="formData[key]"
    						@click="item.show = true"
                :placeholder="item.attr.placeholder"
    						:input-align="item.attr.inputAlign"
    					/>
              <u-select
                v-model="item.show"
                :mode="item.mode"
                :list="item.options"
                :label-name="item.attr.labelName"
                :value-name="item.attr.valueName"
                @confirm="e => select(e, key, item)"
              />
            </template>
    
            <!-- houseType -->
            <u-input
              v-if="item.type === 'houseType'"
              :value="formData[key]"
              type="select"
              @click="houseSelectOpen(key, item)"
              :placeholder="item.attr.placeholder"
              :input-align="item.attr.inputAlign"
            />
            
            <!-- region -->
            <template v-if="item.type === 'region'">
              <u-input
                :value="formData[key] && (formData[key].province.label + formData[key].city.label + formData[key].area.label)"
                type="select"
                @click="regionShow = true"
                :placeholder="item.attr.placeholder"
                :input-align="item.attr.inputAlign"
              />
              <u-picker
                mode="region"
                v-model="regionShow"
                :default-region="formData[key] && [formData[key].province.label, formData[key].city.label, formData[key].area.label]"
                @confirm="e => regionChange(e, key, item)"
              />
            </template>
    
            <!-- rate -->
            <rate
              v-if="item.type === 'rate'"
              :label="item.label"
              :init-data="formData[key]"
              @change="e => rateChange(e, key, item)"
            />
    
            <!-- Tags -->
            <tags
              v-if="item.type === 'PropertyType'"
              v-model="formData[key]"
              @tagChange="onUserLeave({item, value: formData[key]})"
              :label="item.label"
              :custom="item.allowCustom"
            />
    
          </u-form-item>
        </u-form>
        <house-select ref="houseSelect" :seleData="houseListData" label="户型选择" @change="numSeleChange" />
      </view>
    </template>
    
    <script>
    import HouseSelect from '@/components/houseType/compoundNumSele'
    import Rate from './components/Rate'
    import Tags from './components/Tags'
    
    export default {
      name: 'ComForm',
      components: {
        HouseSelect,
        Rate,
        Tags
      },
      model: {
        prop: 'formData',
        event: 'input'
      },
      props: {
        // 表单描述
        formDesc: {
          type: Object,
          required: true
        },
        // 表单数据
        formData: {
          type: Object,
          required: true
        }
      },
      data () {
        return {
          defaultRegion: ['江西省', '南昌市', '西湖区'],
          regionShow: false,
          formRest: true,
          houseListData: [
            {
              label: "卧室数量",
              key: "字段名",
              value: "选中的值",
              unit: "室",
              custom: false, //当前值是否是自定义的
              customValue:'', //自定义的值
              data: [
                { label: "1", value: "1", cheched: false },
                { label: "2", value: "2", cheched: false },
                { label: "3", value: "3", cheched: false }
              ]
            },
            {
              label: "客厅餐厅数量",
              key: "字段名",
              value: "选中的值",
              unit: "厅",
              custom: false, //当前值是否是自定义的
              customValue:'', //自定义的值
              data: [
                { label: "0", value: "0", cheched: false },
                { label: "1", value: "1", cheched: false },
                { label: "2", value: "2", cheched: false }
              ]
            },
            {
              label: "卫生间数量",
              key: "字段名",
              value: "选中的值",
              unit: "卫",
              custom: false, //当前值是否是自定义的
              customValue:'', //自定义的值
              data: [
                { label: "0", value: "0", cheched: false },
                { label: "1", value: "1", cheched: false },
                { label: "2", value: "2", cheched: false }
              ]
            }
          ]
        }
      },
      methods: {
        getValue(field) {
          return this.formData[field]
        },
        houseSelectOpen (key, item) {
    			this.$refs.houseSelect.open(key, item)
        },
        onUserLeave(e) { // 用户离开事件
          this.$emit('userLeave', e)
        },
        numSeleChange ({list, key, item}) {
    	  this.houseListData = list
    	  let value = ''
          this.houseListData.map((item, index) => {
              if (item.custom) {
                  value === '' ? value = item.customValue + item.unit : value = value + item.customValue + item.unit
              } else {
                  value === '' ? value = item.value + item.unit : value = value + item.value + item.unit
              }
          })
          this.formData[key] = value
          this.onUserLeave({item, value: this.formData[key]})
        },
        regionChange (e, key, item) {
          this.formData[key] = e
          this.onUserLeave({item, value: this.formData[key]})
        },
        select (e, key, item) {
          let str = ''
          e.forEach(item => {
            str += item.value
          })
          this.formData[key] = str
    
          this.onUserLeave({item, value: this.formData[key]})
        },
        rateChange (e, key, item) {
          this.formData[key] = e
          this.onUserLeave({item, value: this.formData[key]})
        }
      }
    }
    </script>
    

    星级组件

    ComForm/components/Rate.vue

    <template>
      <view class="rate">
        <view class="title">
          <view>{{label}}</view>
          <!-- <view>一般</view> -->
        </view>
        <view class="rate__content">
          <u-rate
            :count="count"
            :size="size"
            :active-color="activeColor"
            v-model="rate"
            @change="e => $emit('change', e)"
          />
        </view>
      </view>
    </template>
    
    <script>
    export default {
      name: 'Rate',
      props: {
        label: {
          type: String,
          default: '客户意向度'
        },
        initData: {
          type: Number,
          default: 0
        }
      },
      data () {
        return {
          count: 10,
          size: '53',
          rate: 0,
          activeColor: '#FF7716'
        }
      },
      created () {
        this.rate = this.initData
      }
    }
    </script>
    
    <style lang="scss" scoped>
    .rate {
      width: 100vw;
      margin: -20rpx -32rpx 0;
      border-top: 10rpx solid #F7F8FA;
      padding: 27rpx 32rpx;
      background: white;
      .title {
        display: flex;
        justify-content: space-between;
      }
      &__content {
        margin-top: 30rpx;
        margin-left: -5rpx;
      }
    }
    </style>
    

    标签组件

    ComForm/components/Tags.vue

    <template>
      <view class="tags">
        <view class="title">{{label}}</view>
        <view>
          <template v-for="(tag, index) in tags">
            <u-tag
              v-if="tag.selected"
              :key="tag.text + index"
              :text="tag.text"
              mode="dark"
              type="primary"
              :closeable="custom"
              @click="tag.selected = false"
              @close="removeTag(index)"
            />
            <u-tag
              v-else
              :key="tag.text + index"
              :text="tag.text"
              mode="dark"
              type="info"
              bg-color="#F7F8FA"
              color="#323233"
              :closeable="custom"
              @click="tag.selected = true"
              @close="removeTag(index)"
            />
          </template>
          <view v-if="custom" class="add" @click="modalShow = true">
            <text class="iconfont icon-jiahao" />添加标签
          </view>
        </view>
        <u-modal 
          v-model="modalShow"
          show-cancel-button
          :mask-close-able="true"
          
          confirm-text="保存"
          @confirm="addTag"
        >
          <view class="slot-content">
            <u-field
              v-model="modalValue"
              label-width="0"
              placeholder="标签不超过8个字"
              :border-bottom="false"
              focus
              maxlength="8"
              confirm-type="保存"
              :field-style="fieldStyle"
            />
          </view>
       </u-modal>
      </view>
    </template>
    
    <script>
    export default {
      name: 'Tags',
      model: {
        prop: 'tags',
        event: 'input'
      },
      props: {
        tags: {
          type: Array,
          default: () => []
        },
        label: {
          type: String,
          default: '物业类型'
        },
        custom: {
          type: Boolean,
          default: false
        }
      },
      watch: {
        tags: {
          deep: true,
          handler (newVal) {
            this.$emit('tagChange')
            // this.$emit('input', newVal)
          }
        }
      },
      data () {
        return {
          modalShow: false,
          modalValue: '',
          fieldStyle: {
            border: '1px solid #C0C4CC',
            padding: '13rpx 21rpx',
            borderRadius: '4px'
          }
        }
      },
      methods: {
        addTag () {
          this.tags.push({
            text: this.modalValue,
            selected: true
          })
          this.modalValue = ''
        },
        removeTag (index) {
          this.tags.splice(index, 1)
        }
      }
    }
    </script>
    
    <style lang="scss" scoped>
    .tags {
      margin-bottom: 25rpx;
      .title {
        margin-bottom: 18rpx;
      }
      u-tag, .u-tag {
        display: inline-block;
        margin-right: 20rpx;
        margin-bottom: 20rpx;
        font-size: 26rpx;
        &:last-child {
          margin-right: 0;
        }
      }
      .add {
        position: relative;
        display: inline-block;
        line-height: initial;
        font-size: 24rpx;
        padding: 12rpx 22rpx;
        padding-left: 70rpx;
        background: #F7F8FA;
        border-radius: 3px;
        .iconfont {
          position: absolute;
          top: 50%;
          left: 22rpx;
          margin-right: 10rpx;
          transform: translateY(-50%);
        }
      }
    }
    </style>
    

    使用

    <template>
    	<com-form
          v-model="form"
          :form-desc="formDesc"
          @userLeave="onUserLeave"
        />
    </template>
    
    <script>
    const formType = [
      'input', // 单行文本
      'textarea', // 多行文本
      'region', // 地址
      'date', // 时间
      'select', // 下拉框
      'PropertyType', // 标签选择
      'houseType', // 户型选择
      'rate', // 星级意向度
    ]
    
    export default {
        data () {
            return {
                formDesc: {
    				name: {
    					type: 'input',
    					label: '姓名',
    					attr: {
    						placeholder: '请备注客户姓名',
    						inputAlign: 'right'
    					}
    				},
    				phone: {
    					type: 'input',
    					label: '电话',
    					attr: {
    						placeholder: '请备注客户电话',
    						inputAlign: 'right'
    					}
    				},
    				houseType: {
    					type: 'houseType',
    					label: '房型',
    					attr: {
    						placeholder: '请备注户型',
    						inputAlign: 'right'
    					}
    				},
    				region: {
    					type: 'region',
    					label: '地区范围',
    					attr: {
    						placeholder: '请备注地区范围',
    						inputAlign: 'right'
    					}
    				},
    				rate: {
    					type: 'rate'
    				}
    			},
                form: {}
            }
        },
        methods: {
            setFormDesc (data) { // 渲染form
    			const formDesc = {}
    			const form = {}
    			data.forEach((item, index) => {
    				formDesc[item.code] = {
    					type: formType[item.dataType],
    					label: item.label,
    					allowCustom: item.allowCustom,
    					attr: {
    						placeholder: item.placeholder,
    						inputAlign: 'right',
    						customIntentionId: item.customIntentionId,
    						id: item.id,
    						code: item.code
    					}
    				}
    
    				if (formType[item.dataType] === 'region') { // 地区处理
    					let splitArr = []
    					let data = {
    						province: {
    							label: ''
    						},
    						city: {
    							label: ''
    						},
    						area: {
    							label: ''
    						}
    					}
    					if (item.customerIntentionValue) {
    						splitArr = item.customerIntentionValue.split('|')
    					} else if (item.initData) {
    						splitArr = item.initData.split('|')
    					}
    					if (splitArr.length) {
    						data = {
    							province: {
    								label: splitArr[0]
    							},
    							city: {
    								label: splitArr[1]
    							},
    							area: {
    								label: splitArr[2]
    							}
    						}
    					}
    					form[item.code] = data
    				} else if (formType[item.dataType] === 'PropertyType') { // 标签类型特殊处理
    					const data = []
    					let splitArr = item.initData ? item.initData.split('|') : []
    					let splitSelectedArr = item.customerIntentionValue ? item.customerIntentionValue.split('|') : []
    					// if (item.customerIntentionValue) {
    					// 	splitArr = item.customerIntentionValue.split('|')
    					// } else if (item.initData) {
    					// 	splitArr = item.initData.split('|')
    					// }
    				
    					if (item.allowCustom && splitSelectedArr !== '') {	// 后端要求,自定义标签只有选中的
    						splitSelectedArr.forEach(el => {
    							let tagItem = {
    								text: el,
    								selected: true
    							}
    							data.push(tagItem)
    						})
    						form[item.code] = data
    						return
    					}
    					if (splitArr !== '') { // 普通标签
    						splitArr.forEach(el => { // 赋值
    							let tagItem = {
    								text: el,
    								selected: false
    							}
    							if (splitSelectedArr !== '') {
    								let splitSelected = splitSelectedArr.find(selectedItem => selectedItem === el)
    								if (splitSelected) {
    									tagItem.selected = true
    								}
    							}
    							data.push(tagItem)
    						})
    					}
    					form[item.code] = data
    				} else if (formType[item.dataType] === 'rate') { // 后端传的字符串转换一下
    					form[item.code] = Number(item.customerIntentionValue) || Number(item.initData)
    				} else {
    					form[item.code] = item.customerIntentionValue || item.initData
    				}
    			})
    			this.formDesc = formDesc
    			this.form = form
    		}
        }
    }
    </script>
    

    起源地下载网 » uni-app 封装通用表单组件

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元