表单封装
由于表单的通用性,封装了一个基于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>
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!