又是一个令人头疼的前端需求
在同事的帮助下实现了这个功能,但是非常繁琐,如果你实现写不出来,可以借鉴这个方法,但是你要是有时间可以自己写,建议看一下https://blog.youkuaiyun.com/ClamJ092/article/details/103183703我的这篇文章,找一下思路,但是没有那么多时间了,所以只能冗余的实现这个需求,以后有时间了(不)可(可)以(能)优化一下。
看一下效果
就是页面加载显示一组二级联动,点击添加按钮,再添加一组二级联动,并且后边多了一个删除按钮,如果只有一组二级联动的情况下,不显示删除按钮。
然后二级联动的数据,每次选择软件的时候,如果软件名字变了,版本要清空,并且加载新的版本数据源。
下边这段话,等你先实现出来再回头看:
如果已经选择了软件A,那么在下一组软件框选择的时候,要不能排除软件A了。这里有一个坑,就是你去先添加几组二级联动的时候,再去选择了软件A,就不会再检测数组变化,所以下边依然可以选择软件A,或者你选过了之后,把软件A 移除了,但是由于下边的选框数据没有更新,所以即使你移除了软件A,但是其他选框还是没有软件A的数据。
反正专业测试去测试的时候,会发现很多小问题,但是已经改进了,不过我觉得还是用监听+隐藏实现比较好。(无代码无真相)
再有就是验证,动态生成的选框也好,输入框也好,还有这个复杂的二级联动也好,都有一个校验得名字和实际的名字必须保持一致的问题,所以不要瞎命名。
下边上一下代码
<div class="block-wrapper">
<div class="block-content">
<el-row v-for="(softItem, index) in postForm.softwareList" :key="index" type="flex">
<el-form-item
label="软件:"
:prop="'softwareList.' + index +'.softwareName'"
:rules="[
{ required: true, message: '请选择软件', trigger: 'change' }
]"
>
<!--一级菜单-->
<el-select v-model="softItem.softwareName" placeholder="选择软件" clearable style="width: 200px;" @change="getVersionList($event, index)" @focus="openSelect($event,index)">
<el-option v-for="(item,index2) in softItem.optionList" :key="index2" :value="item.softwareName" />
</el-select>
</el-form-item>
<!--二级菜单-->
<el-form-item
label="软件版本:"
:prop="'softwareList.' + index +'.softwareVersion'"
:rules="[
{ required: true, message: '请选择软件版本', trigger: 'change'}
]"
>
<el-select v-model="softItem.softwareVersion" placeholder="选择软件版本" clearable style="width: 200px;" @change="getSoftwareListChanged">
<el-option v-for="version in softItem.versionList" :key="version.id" :value="version.softwareVersion" :label="version.softwareVersion" />
</el-select>
</el-form-item>
<el-form-item v-show="postForm.softwareList.length > 1">
<i class="el-icon-delete" style="font-size: 22px; margin-left: 20px; margin-top: 8px" @click="deleteSoft(index)" />
</el-form-item>
</el-row>
<el-row>
<el-form-item>
<el-button type="primary" @click="addSoft">添加软件</el-button>
</el-form-item>
</el-row>
</div>
</div>
postForm: {
softwareList: [],
//这个由于我要只传输ID,所以必须要把ID存起来给后边,所以上边的softwareList没有用,只是用来校验的
ids: []
},
// 软件名称选项
softOptions: [],
created() {
// 获取软件级联结果集
this.getSoftOptions()
},
这里我的数据结构是 二级联动的结构,因为当时没有想到好的解决办法,所以只能允许数据不用必须加载最新的,就保证你刷新页面是有那一批数据就可以了
getSoftOptions() {
getSoftwareList().then(response => {
this.softOptions = response.data
// 将表单选项 存进postForm
this.postForm.softwareList.push({ 'optionList': this.softOptions })
}).catch((err) => {
if (err.status !== '-1') {
this.$message({
message: err.msg,
type: 'error',
duration: 3 * 1000
})
}
})
},
getSoftwareListChanged() {
this.$forceUpdate()
},
openSelect($event, index) {
var selectSoftwareList = this.postForm.softwareList.map(m => m.softwareName).filter(m => m !== this.postForm.softwareList[index].softwareName)
var allOptions = Object.assign(this.postForm.softwareList[index].optionList)
selectSoftwareList.forEach(m => {
allOptions = allOptions.filter(n => n.softwareName !== m)
})
this.postForm.softwareList[index].optionList = allOptions
},
getVersionList($event, index) {
if ((this.postForm.softwareList[index].softwareName) !== '') {
for (let i = 0; i < this.softOptions.length; i++) {
if (this.softOptions[i].softwareName === this.postForm.softwareList[index].softwareName) {
const currSoftList = this.postForm.softwareList[index]
currSoftList.versionList = this.softOptions[i].versionList
currSoftList.softwareVersion = ''
this.listLoading = false
Vue.set(this.postForm.softwareList, index, JSON.parse(JSON.stringify(currSoftList)))
break
} else {
continue
}
}
} else {
this.postForm.softwareList[index].softwareVersion = ''
this.softOptions.versionList = []
}
EvenBus.$emit('ddd', this.postForm.softwareList[index].softwareName)
let softOptionsTemp = this.softOptions
for (let x = 0; x < this.postForm.softwareList.length; x++) {
for (let i = 0; i < this.postForm.softwareList.length; i++) {
if (x === i) {
continue
}
softOptionsTemp = softOptionsTemp.filter(item => item.softwareName !== this.postForm.softwareList[i].softwareName)
}
const currSoftList = this.postForm.softwareList[x]
currSoftList.optionList = softOptionsTemp
Vue.set(this.postForm.softwareList, x, currSoftList)
}
},
addSoft() {
const softOptionsTemp = this.softOptions
// for (var i = 0; i < this.postForm.softwareList.length; i++) {
// softOptionsTemp = softOptionsTemp.filter(item => item.softwareName !== this.postForm.softwareList[i].softwareName)
// }
this.postForm.softwareList.push({ 'optionList': softOptionsTemp })
},
deleteSoft(index) {
this.postForm.softwareList.splice(index, 1)
},
dispose() {
// 处理软件id
var arr = []
this.postForm.softwareList.forEach(function(software) {
return software.versionList.find(function(v) {
if (v.softwareVersion === software.softwareVersion) {
arr.push(v.id)
}
})
})
this.postForm.ids = arr
// 之前想把数据塞进list后端再取,但是因为数组长度变化,走到这一步的时候会多出来一组二级联动框,所以就冗余了一下,又加了个ids,自己同时写前后端就是好,爱咋写咋写
// var arr = []
// this.postForm.softwareList.forEach(function(item) {
// arr.push({ 'softwareName': item.softwareName, 'softwareVersion': item.softwareVersion })
// })
// arr.push({ 'ids': ids })
// this.postForm.softwareList = arr
},
createData() {
var vm = this
this.$forceUpdate()
this.$refs.postForm.validate(valid => {
if (valid) {
this.dispose()
vm.listLoading = true
saveTask(this.postForm).then(response => {
vm.$message({
message: response.msg,
type: 'success',
duration: 2000
})
this.reload()
setTimeout(function() {
vm.listLoading = false
vm.$router.push({
name: 'TaskList'
})
}, 2 * 1000)
}).catch((err) => {
if (err.status !== '-1') {
vm.$message({
message: err.msg,
type: 'error',
duration: 3 * 1000
})
}
})
vm.listLoading = false
} else {
// this.$message({
// message: '表单数据填写不正确',
// type: 'warning',
// duration: 3 * 1000
// })
return false
}
})
// 这里是因为填了数据,但是不选版本,先提交表单但是会提示软件版本为空,想的解决办法,但是没有用,后来发现是原来是校验检测名字跟实际的名字不一样。
// this.$refs['postForm'].clearValidate()
},
这是新建 几乎99%没毛病了,但是编辑的时候,提交表单还是有问题,就是校验检测名字跟实际的名字不一样,导致校验不通过。我还在想办法。。再补充。
不过如果你真的有时间 强烈不建议这样写,因为数据结构超级乱。还是用监听+隐藏解决吧
编辑的问题解决了,我以为是动态监测的问题,但是同事说是数据存的位置的问题
<div class="block-wrapper">
<div class="block-content">
<el-row v-for="(softItem, index) in postForm.softwareList" :key="index" type="flex">
<el-form-item
label="选择软件:"
:prop="'softwareList.' + index +'.softwareName'"
:rules="[
{ required: true, message: '请选择软件', trigger: 'change' }
]"
>
<!--一级菜单 -->
<el-select v-model="softItem.softwareName" placeholder="选择软件" clearable style="width: 200px;" :disabled="readonlyYES" @change="getVersionList($event, index)" @focus="openSelect($event,index)">
<el-option v-for="(item,index2) in softItem.optionList" :key="index2" :value="item.softwareName" />
</el-select>
</el-form-item>
<!--二级菜单-->
<el-form-item
label="软件版本:"
:prop="'softwareList.' + index +'.softwareVersion'"
:rules="[
{ required: true, message: '请选择软件版本', trigger: 'change' }
]"
>
<el-select v-model="softItem.softwareVersion" placeholder="选择软件版本" :disabled="readonlyYES" clearable style="width: 200px;" @change="getSoftwareListChanged">
<el-option v-for="version in softItem.versionList" :key="version.id" :value="version.softwareVersion" :label="version.softwareVersion" />
</el-select>
</el-form-item>
<el-form-item v-show="postForm.softwareList.length > 1">
<i class="el-icon-delete" style="font-size: 22px; margin-left: 20px; margin-top: 8px" @click="deleteSoft(index)" />
</el-form-item>
</el-row>
<el-row>
<el-form-item>
<el-button v-if="!readonlyYES" type="primary" style="margin-left: 5px; width: 128.36px" @click="addSoft">添加软件</el-button>
</el-form-item>
</el-row>
</div>
</div>
// 将软件名称选项 放进这里
// this.postForm.softwareList.push({ 'optionList': this.softOptions })
getSoftwareListChanged() {
this.$forceUpdate()
},
// 这是什么方法
openSelect($event, index) {
var selectSoftwareList = this.postForm.softwareList.map(m => m.softwareName).filter(m => m !== this.postForm.softwareList[index].softwareName)
var allOptions = Object.assign(this.postForm.softwareList[index].optionList)
selectSoftwareList.forEach(m => {
allOptions = allOptions.filter(n => n.softwareName !== m)
})
this.postForm.softwareList[index].optionList = allOptions
},
// 拆分软件版本 根据名称调用
getVersionList($event, index) {
for (let i = 0; i < this.softOptions.length; i++) {
if (this.softOptions[i].softwareName === this.postForm.softwareList[index].softwareName) {
const currSoftList = this.postForm.softwareList[index]
currSoftList.versionList = this.softOptions[i].versionList
currSoftList.softwareVersion = ''
Vue.set(this.postForm.softwareList, index, JSON.parse(JSON.stringify(currSoftList)))
// this.postForm.softwareList[index].softwareVersion = ''
break
}
}
var softOptionsTemp = this.softOptions
for (let x = 0; x < this.postForm.softwareList.length; x++) {
for (let i = 0; i < this.postForm.softwareList.length; i++) {
if (x === i) {
continue
}
softOptionsTemp = softOptionsTemp.filter(item => item.softwareName !== this.postForm.softwareList[i].softwareName)
}
const currSoftList = this.postForm.softwareList[x]
currSoftList.optionList = softOptionsTemp
Vue.set(this.postForm.softwareList, x, currSoftList)
}
},
// openSelect($event, index) {
// console.log(2)
// // console.log(this.postForm.softwareList)
// var selectSoftwareList = this.postForm.softwareList.map(m => m.softwareName).filter(m => m !== this.postForm.softwareList[index].softwareName)
// var allOptions = Object.assign(this.postForm.softwareList[index].optionList)
// selectSoftwareList.forEach(m => {
// allOptions = allOptions.filter(n => n.softwareName !== m)
// })
// this.postForm.softwareList[index].optionList = allOptions
// },
addSoft() {
var softOptionsTemp = this.softOptions
for (var i = 0; i < this.postForm.softwareList.length; i++) {
softOptionsTemp = softOptionsTemp.filter(item => item.softwareName !== this.postForm.softwareList[i].softwareName)
}
this.postForm.softwareList.push({ 'optionList': softOptionsTemp })
},
deleteSoft(index) {
this.postForm.softwareList.splice(index, 1)
},
dispose() {
// 处理软件id
var arr = []
this.postForm.softwareList.forEach(function(software) {
return software.versionList.find(function(v) {
if (v.softwareVersion === software.softwareVersion) {
arr.push(v.id)
}
})
})
this.postForm.ids = arr
},
disSoft(data) {
if (data.length === 0) {
this.postForm.softwareList.push({ 'optionList': this.softOptions })
} else {
this.postForm.softwareList = data
this.postForm.softwareList.forEach(item => {
item.softwareVersion = item.versionList[0].softwareVersion
// item.versionList.forEach(items => {
// this.postForm.softwareList.softwareVersion = items.softwareVersion
// console.log(items.softwareVersion)
// console.log(this.postForm.softwareList.softwareVersion)
// })
})
}
// for (let i = 0; i < this.postForm.softwareList.length; i++) {
// console.log(this.postForm.softwareList[i])
// for (let j = 0; j < this.postForm.softwareList[i].versionList.length; j++) {
// this.postForm.softwareList[i].softwareVersion = this.versionList[j].softwareVersion
// }
// }
},
// setTagsViewTitle() {
// const title = '编辑子任务'
// const route = Object.assign({}, this.tempRoute, { title: `${title}` })
// console.log(route)
// this.$store.dispatch('updateVisitedView', route)
// },
// 基于已选择的软件信息构建数据结构
buildOptions() {
var softOptionsTemp = this.softOptions
for (let x = 0; x < this.postForm.softwareList.length; x++) {
for (let i = 0; i < this.postForm.softwareList.length; i++) {
if (x === i) {
continue
}
softOptionsTemp = softOptionsTemp.filter(item => item.softwareName !== this.postForm.softwareList[i].softwareName)
}
const currSoftList = this.postForm.softwareList[x]
currSoftList.optionList = softOptionsTemp
Vue.set(this.postForm.softwareList, x, currSoftList)
}
},
// 基于已选择的软件信息构建软件版本数据结构
buildVersion() {
var softOptions = this.softOptions
for (let x = 0; x < this.postForm.softwareList.length; x++) {
var softOptionsTemp = softOptions.filter(item => item.softwareName === this.postForm.softwareList[x].softwareName)
const currSoftList = this.postForm.softwareList[x]
currSoftList.versionList = softOptionsTemp[0].versionList
Vue.set(this.postForm.softwareList, x, currSoftList)
}
},
// 处理响应数据
processing(res) {
this.softOptions = res.data.softOptions
this.disSoft(res.data.softwareList)
this.buildOptions()
this.buildVersion()
}