最近在项目中使用cascader来处理省市异步加载级联多选功能,在异步加载,获取选中value&label,编辑回显处理时遇到点小麻烦,最终经过一整天的处理,解决掉问题。我把所有的代码发出来,给也遇到同样问题的小伙伴一个参考:
<!-- 企业人才库(求职者档案) 未加企业微信的C端用户,如通过招聘小程序投简历过来的用户 -->
<template>
<div class="app-container">
<!-- 搜索表单 -->
<el-form
ref="buttonsForm"
size="small"
:inline="true"
>
<el-form-item>
<el-button v-has-permission="['enterpriseservice:talentpool:add']" type="primary" icon="el-icon-plus" @click="handleAdd">新增</el-button>
<el-button v-has-permission="['enterpriseservice:talentpool:edit']" type="success" icon="el-icon-edit" :disabled="single" @click="handleUpdate">修改</el-button>
<el-button v-has-permission="['enterpriseservice:talentpool:delete']" type="danger" icon="el-icon-delete" :disabled="multiple" @click="handleDelete">删除</el-button>
</el-form-item>
</el-form>
<!-- 数据表格 -->
<div class="el-table-div">
<el-table
v-loading="loading"
:data="pageList"
border
@selection-change="handleSelectionChange"
>
<el-table-column type="selection" width="50" align="center" />
<el-table-column label="姓名" prop="name" />
<el-table-column label="性别" prop="gender" align="center" />
<el-table-column label="年龄" prop="age" align="center" />
<el-table-column label="手机" prop="mobile" align="center" />
<el-table-column label="期望工作地点" prop="workPlaces" align="center" width="150">
<template slot-scope="scope">
<p v-for="(item, index) in scope.row.workPlaces" :key="index">{{ item.provinceName + '/' + item.cityName }}</p>
</template>
</el-table-column>
<el-table-column label="自我简介" prop="selfIntroduction" />
<el-table-column label="求职意向" prop="jobIntention" />
<el-table-column label="备注" prop="notes" />
<el-table-column label="添加时间" prop="createTime" align="center" />
<el-table-column label="操作" align="center" width="100">
<template slot-scope="scope">
<el-button
v-has-permission="['enterpriseservice:talentpool:edit']"
type="primary"
icon="el-icon-edit"
size="mini"
circle
plain
@click.stop="handleUpdate(scope.row)"
/>
<el-button
v-has-permission="['enterpriseservice:talentpool:delete']"
type="danger"
icon="el-icon-delete"
size="mini"
circle
plain
@click.stop="handleDelete(scope.row)"
/>
</template>
</el-table-column>
</el-table>
</div>
<!--分页-->
<pagination
v-show="pagination.total>0"
:total="pagination.total"
:page.sync="pagination.page"
:limit.sync="pagination.limit"
@pagination="handleQuery"
/>
<!-- 表单弹窗 -->
<el-dialog :title="dialog.title" :visible.sync="dialog.visible" width="800px">
<el-form ref="form" :model="form" :rules="rules" label-width="130px">
<el-form-item label="姓名" prop="name">
<el-input v-model="form.name" placeholder="请输入姓名" maxlength="5" show-word-limit />
</el-form-item>
<el-form-item label="性别" prop="gender">
<el-radio-group v-model="form.gender">
<el-radio :label="1">男</el-radio>
<el-radio :label="2">女</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="年龄" prop="age">
<el-input-number v-model="form.age" controls-position="right" :min="18" :max="99" />
</el-form-item>
<el-form-item label="手机" prop="mobile">
<el-input v-model="form.mobile" placeholder="请输入手机" maxlength="11" show-word-limit />
</el-form-item>
<el-form-item label="期望工作地点" prop="workplaceIds">
<el-cascader
ref="cascader"
v-model="form.workplaceIds"
:options="workplaceOptions"
:props="workplaceProps"
style="width: 100%"
clearable
:show-all-levels="true"
@change="cascaderChange"
/>
</el-form-item>
<el-form-item label="自我介绍" prop="selfIntroduction">
<el-input v-model="form.selfIntroduction" placeholder="请输入自我介绍" maxlength="50" show-word-limit />
</el-form-item>
<el-form-item label="求职意向" prop="jobIntention">
<el-input v-model="form.jobIntention" placeholder="请输入求职意向" maxlength="50" show-word-limit />
</el-form-item>
<el-form-item label="备注" prop="notes">
<el-input v-model="form.notes" placeholder="请输入备注" maxlength="50" show-word-limit />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="handleSubmit">确 定</el-button>
<el-button @click="dialog.visible=false">取 消</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import Pagination from '@/components/Pagination'
import { getProvince, getCity, getAllCity } from '@/api/platform/address'
import { pagelist, detail, update, add, batchDelete } from '@/api/enterpriseservice/talentpool'
export default {
components: { Pagination },
data() {
return {
// 遮罩层
loading: true,
// 选中数组
ids: [],
// 非单个禁用
single: true,
// 非多个禁用
selectParams: {
},
multiple: true,
pagination: {
page: 1,
limit: 10,
total: 0
},
pageList: [],
dialog: {
title: undefined,
visible: false,
type: undefined // type 操作类型:1-新增 2-修改
},
// 表单参数
form: {
id: undefined,
name: undefined,
gender: undefined,
age: undefined,
mobile: undefined,
workplaceIds: undefined,
selfIntroduction: undefined,
notes: undefined,
createTime: undefined,
// 向后台提交的完整数据
workplaceFullData: []
},
// 表单校验
rules: {
name: [
{ required: true, message: '姓名不能为空', trigger: 'blur' }
],
gender: [
{ required: true, message: '请选择性别', trigger: 'blur' }
],
age: [
{ required: true, message: '年龄不能为空', trigger: 'blur' }
],
mobile: [
{ required: true, message: '电话不能为空', trigger: 'blur' },
{
pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/,
message: '请输入正确的手机号码',
trigger: 'blur'
}
],
notes: [
{ required: true, message: '备注不能为空', trigger: 'blur' }
]
},
workplaceProps: {
that: this,
value: 'id',
label: 'name',
multiple: true,
expandTrigger: 'click',
// checkStrictly: true, // 选择任意一级
lazy: true,
lazyLoad(node, resolve) {
const { value, level } = node
// level=1是因为城市数据只能选到第2级(市),第1级加载第2级数据
if (level === 1) {
setTimeout(() => {
getCity(value).then(response => {
const cities = response.data.map(item => {
// leaf必须加,就是让组件识别是不是最后一级 必须给最后一级加上leaf属性,不然识别不了是最后一级,所以无法选中默认值
item.leaf = true
return item
})
if (node.hasChildren && node.children.length) {
const newChildren = []
cities.forEach(city => {
if (node.children.findIndex(x => x.value === city.id) === -1) {
newChildren.push(city)
}
})
resolve(newChildren)
} else {
resolve(cities)
}
})
}, 500)
} else {
// 这里不再加载下级数据了,如果加载区就是:else if (level === 2) {}
resolve([])
}
}
},
// 回显必须要把需要回显的都写到 options 里面,且最后一项要加上 leaf:true, 表示无下级了才可以回显;
workplaceOptions: [{
label: undefined,
value: undefined,
children: [{
label: undefined,
value: undefined
}]
}],
citiesAll: []
}
},
async created() {
// 初始化表格数据
this.handleQuery()
},
methods: {
handleQuery() {
this.selectParams.page = this.pagination.page
this.selectParams.limit = this.pagination.limit
pagelist(this.selectParams).then(response => {
this.pageList = response.data.list
this.pagination.total = response.data.total
this.loading = false
})
},
handleInitProvince() {
getProvince().then(response => {
const { data } = response
this.workplaceOptions = data
})
},
handleReset() {
this.pagination = {
page: 1,
limit: 10,
total: 0
}
this.handleQuery()
},
handleSelectionChange(selection) {
this.ids = selection.map(item => item.id)
this.single = selection.length !== 1
this.multiple = !selection.length
},
handleAdd() {
this.resetForm()
this.dialog = {
title: '新增',
visible: true,
type: 'add'
}
// 解决问题:级联选择器点击但未选中时,再次点击触发打开时,还是上次点击的选项
const _cascader = this.$refs['cascader']
if (_cascader) {
_cascader.$refs.panel.checkedValue = []
_cascader.$refs.panel.activePath = []
_cascader.$refs.panel.syncActivePath()
}
this.handleInitProvince()
},
handleUpdate(row) {
this.resetForm()
// 取所有的市数据
getAllCity().then(response => {
const { data } = response
this.citiesAll = data
this.dialog = {
title: '修改',
visible: true,
type: 'edit'
}
// 解决问题:级联选择器点击但未选中时,再次点击触发打开时,还是上次点击的选项
const _cascader = this.$refs['cascader']
if (_cascader) {
_cascader.$refs.panel.checkedValue = []
_cascader.$refs.panel.activePath = []
_cascader.$refs.panel.syncActivePath()
}
const id = row.id || this.ids
detail(id).then(response => {
const { data } = response
this.form = data
// 处理已选中的ID,便于回显
const selectedWorkplaceIds = []
// 处理已选中的Options
const selectedWorkplaceOptions = []
response.data.workPlaces.forEach(element => {
const selectedIds = []
selectedIds.push(element.provinceId)
selectedIds.push(element.cityId)
selectedWorkplaceIds.push(selectedIds)
// 先把已选中的省市数据写到 Options
if (selectedWorkplaceOptions.findIndex(item => item.id === element.provinceId) === -1) {
// 找已选中省下的市数据
const cityChildren = []
// 找到当前这个省下的所有市
const selectedCities = this.citiesAll.filter(city => city.parentId === element.provinceId)
selectedCities.forEach(cityElement => {
cityChildren.push({ id: cityElement.id, name: cityElement.name, leaf: true, children: [] })
})
// 省市数据写到 Options
selectedWorkplaceOptions.push(
{
id: element.provinceId,
name: element.provinceName,
leaf: false,
children: cityChildren
}
)
}
})
this.form.workplaceIds = selectedWorkplaceIds
this.workplaceOptions = selectedWorkplaceOptions
// 加载省数据,把未选中的省数据追加到 Options
getProvince().then(response => {
response.data.forEach(element => {
if (selectedWorkplaceOptions.findIndex(item => item.id === element.id) === -1) {
this.workplaceOptions.push({
id: element.id,
name: element.name,
leaf: false,
children: []
})
}
})
})
})
})
},
handleDelete(row) {
const ids = [row.id || this.ids].join(',')
this.$confirm('确认删除已选中的数据项?', '警告', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
batchDelete(ids).then(() => {
this.$message.success('删除成功')
this.handleQuery()
})
}).catch(() =>
this.$message.info('已取消删除')
)
},
handleSubmit: function() {
this.$refs['form'].validate(valid => {
if (valid) {
// 处理期望工作地点
this.form.workplaceFullData = []
const checkedNodes = this.$refs['cascader'].getCheckedNodes()
checkedNodes.forEach(element => {
if (element.parent) {
this.form.workplaceFullData.push({
provinceId: element.parent.value,
provinceName: element.parent.label,
cityId: element.value,
cityName: element.label
})
}
})
if (this.dialog.type === 'edit') {
update(this.form).then(() => {
this.$message.success('修改成功')
this.closeDialog()
this.handleQuery()
})
} else {
add(this.form).then(() => {
this.$message.success('新增成功')
this.closeDialog()
this.handleQuery()
})
}
}
})
},
resetForm() {
this.form = {
id: undefined,
name: undefined,
gender: undefined,
age: undefined,
mobile: undefined,
workplaceIds: undefined,
selfIntroduction: undefined,
notes: undefined,
createTime: undefined,
// 向后台提交的完整数据
workplaceFullData: []
}
if (this.$refs['form']) {
this.$refs['form'].resetFields()
}
},
closeDialog() {
this.dialog = {
title: undefined,
visible: false,
type: undefined
}
},
cascaderChange(val) {
console.log('cascaderChanged')
}
}
}
</script>
<style lang="scss" scoped>
::-webkit-scrollbar {
width: 5px;
height: 1px;
}
::-webkit-scrollbar-thumb { //滑块部分
border-radius: 5px;
background-color: rgb(201, 201, 201);
}
::-webkit-scrollbar-track { //轨道部分
box-shadow: inset 0 0 5px rgba(0,0,0,0.2);
background: #ededed;
border-radius: 5px;
}
.el-table-div{
height: 70vh;
overflow: hidden;
overflow-y: auto;
padding-right: 15px;
line-height: 25px;
}
</style>
总结一下思路
1)cascader编回显需要在数据即workplaceOptions一、二级都有(编辑时查出来的详情数据)且workplaceIds设置了回显的ID(一个二维数组)的情况下才可以回显;
2)一个细节:比如用户选了河北省 - 唐山市 ,注意,河北省只选了唐山,其他市并未选,但编辑回显时需要把选中的河北省下所有市都查出来,不能只查询选中了的唐山,这样会造成编辑时再次点河北重新加载河北省下的所有市,数据变化了,默认回显项也就消失了;
3)未选中的省份在回显时不处理;