使用vue表单验证库async-validator封装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>
</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>