各种搞了好几天了一直出问题,请大神给个思路,感觉是自己对数组,数据操作的时候问题
问题描述
添加运费模板,用户选择地区后该地区不可选或者删除,当用户编辑的时候数据回显
问题出现的环境背景及自己尝试过哪些方法
把选过的id放到一个数组disabled中并存到vuex中,然后再次编辑的时候将编辑的项从disabled中删除
但是左侧数据只初始化一次,再次编辑操作的时候数据会相互影响
相关代码
数据结构
{"name":",模板",
"valuation_type":"1",
"valuation_rules":
[{"regions":[
{"id":"13","title":"河北省","pid":"0","open":false},
{"id":"1301","title":"石家庄市","pid":"13"}],
"first_amount":1,
"first_fee":"0.00",
"additional_amount":"0",
"additional_fee":"0.00"
}.{"regions":[
{"id":"11","title":"北京市","pid":"0","open":false},
{"id":"1101","title":"市辖区","pid":"11"}
"first_amount":1,
"first_fee":"0.00",
"additional_amount":"0",
"additional_fee":"0.00"
}]
}
调用组件
<div class="app-container">
<div class="content">
<el-form :model="freForm" status-icon :rules="freRules" ref="freForm" label-width="100px" class="fre-form" size="mini">
<el-form-item label="模板名称" prop="name">
<el-input type="text" v-model="freForm.name" auto-complete="off"></el-input>
</el-form-item>
<el-form-item label="计费方式" prop="valuation_type">
<el-radio-group v-model="freForm.valuation_type">
<el-radio :label="2">按件数</el-radio>
<el-radio :label="3">按重量</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="配送区域" prop="checkPass">
<table class="fre-table sui-table table-bordered " style="width:900px">
<thead>
<tr>
<th class="center">可配送区域</th>
<th class="center">首件(个)</th>
<th class="center">运费(元)</th>
<th class="center">续件(个)</th>
<th class="center">续费(元)</th>
</tr>
</thead>
<tbody>
<tr v-for="(item,index) in freForm.valuation_rules" :key="index">
<td class="left-td">
<span v-for="area in item.regions" :key="area.id" class="area-title" :class="{'strong':area.pid==0}"> {{area.title}} </span>
<el-button type="text" size="small" @click="editArea(index)">编辑</el-button>
<el-button type="text" size="small">删除</el-button>
</td>
<td class="center">
<el-input type="text" size="mini" v-model="item.first_amount" auto-complete="off"></el-input>
</td>
<td class="center">
<el-input type="text" size="mini" v-model="item.first_fee" auto-complete="off"></el-input>
</td>
<td class="center">
<el-input type="text" size="mini" v-model="item.additional_amount" auto-complete="off"></el-input>
</td>
<td class="center">
<el-input type="text" size="mini" v-model="item.additional_fee" auto-complete="off"></el-input>
</td>
</tr>
</tbody>
<tfoot>
<tr>
<td colspan="5">
<el-button type="text" size="small" @click="addArea">指定可配送区域和运费</el-button>
</td>
</tr>
</tfoot>
</table>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitForm('freForm')">保存</el-button>
<el-button @click="resetForm('freForm')">重置</el-button>
</el-form-item>
</el-form>
</div>
<!--address dialog-->
<el-dialog :visible.sync="dialogVisible" width="630px" center>
<synctree ref="synctree" v-model="finalData"></synctree>
<span slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false">取 消</el-button>
<el-button type="primary" @click="submitArea">保 存</el-button>
</span>
</el-dialog>
<!--address dialog-->
</div>
<script>
import Synctree from '@/components/Synctree'
import originData from '@/components/Synctree/data' //地址数据
export default {
components: {
Synctree
},
data() {
return {
dialogVisible: false, //显示弹出
originData: originData, //地址数据
freForm: { // 数据
valuation_rules: [],
name: '',
valuation_type: 2
},
}
}
},
methods: {
// 点击添加
addArea() {
this.dialogVisible = true
},
// 点击保存
submitArea() {
// 如果编辑地址
if (this.editIndex !== null) {
// 更新编辑数据
this.freForm.valuation_rules[this.editIndex].regions = this.finalData
// 编辑id重置
this.editIndex = null
} else {
const temp = {
regions: this.finalData,
first_amount: 1,
first_fee: '0.00',
additional_amount: '0',
additional_fee: '0.00'
}
// 添加一行数据
if (this.finalData.length > 0) {
this.freForm.valuation_rules.push(temp)
}
}
// 清空选择地址数据,为下次添加准备
this.finalData = []
this.dialogVisible = false
},
// 编辑选择配送区域
editArea(index) {
this.editIndex = index
// 获取编辑数据重新复制
this.finalData = this.freForm.valuation_rules[index].regions
this.dialogVisible = true
},
init() {
if (this.templateId) {
const formData = {
templateId: this.templateId
}
getFreightDetail(formData).then(res => {
this.freForm = res.data.supplierFreightDetails
console.log(this.freForm)
})
}
}
},
子组件
<template>
<div class="v-component-sync-tree">
<div class="origin-tree tree-item">
<div class="tree-header">选择可配送区域</div>
<div class="tree-body">
<el-tree :data="originTree" show-checkbox ref="originTree" node-key="id" :props="defaultProps" @check-change="getOriginKeys"></el-tree>
</div>
</div>
<div class="tree-cut">
<i class="ivu-icon ivu-icon-ios-arrow-right"></i>
<el-icon class="el-icon-arrow-right"></el-icon>
</div>
<div class="final-tree tree-item">
<div class="tree-header">配送区域</div>
<div class="tree-body">
<div class="body-main">
<ul>
<li v-for="(item,index) of finalTree" :key="item.id">
<span class="item-text item-expand" @click.stop="toggleOpen(item.id)"> <el-icon class="item-icon" :class="item.open ?'el-icon-caret-bottom':'el-icon-caret-right'"></el-icon>{{ item.title }}</span>
<!--<span class="item-edit" @click.stop="popEditModal(item.id, item.title)">
<el-icon class="el-icon-edit"></el-icon>
</span>-->
<span class="item-del" @click.stop="popDelModal(item.id, item.pid)">
<el-icon class="el-icon-delete"></el-icon>
</span>
<ul class="tree-child" style="margin-left:20px;" :class="{'tree-show': item.open}">
<li v-for="child of item.children" :key="child.id">
<span class="item-text">{{ child.title }}</span>
<!--<span class="item-edit" @click.stop="popEditModal(child.id, child.title)">
<el-icon class="el-icon-edit"></el-icon>
</span>-->
<span class="item-del" @click.stop="popDelModal(child.id, child.pid)">
<el-icon class="el-icon-delete"></el-icon>
</span>
</li>
</ul>
</li>
</ul>
</div>
<div class="edit-mask" v-if="editShow">
<div class="edit-wrapper">
<el-input class="edit-input" v-model="editText"></el-input>
<div class="edit-opra-wrapper">
<el-button-group>
<el-button class="edit-cancel" type="default" @click="editCancel">
取消
</el-button>
<el-button class="edit-ok" type="primary" @click="editOk">
确认
</el-button>
</el-button-group>
</div>
</div>
</div>
<div class="del-mask" v-if="delShow">
<div class="del-wrapper">
<div class="del-text">
<el-icon class="el-icon-warning"></el-icon>
是否删除
</div>
<div class="del-opra-wrapper">
<el-button-group>
<el-button class="del-cancel" @click="delCancel">
取消
</el-button>
<el-button class="del-ok" type="primary" @click="delOk">
确认
</el-button>
</el-button-group>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import Tool from './tool'
require('./vue-component-sync-tree.scss')
import {
mapGetters
} from 'vuex'
export default {
name: 'synctree',
model: {
prop: 'finalList',
event: 'change'
},
props: {
// 目的列表数据
finalList: {
type: Array,
default: () => []
},
// 源列表数据
data: {
type: Array,
default: () => []
}
},
data() {
return {
delShow: false, // 删除确认弹出层控制器
delId: '', // 删除弹出层 => id
delPid: '', // 删除弹出层 => pid
editShow: false, // 修改弹出层控制器
editText: '', // 修改弹出层 => text
editId: '', // 修改弹出层 => id
originTree: [], // 源列表Tree
originKeys: [], // 源列表keys
defaultProps: {
children: 'children',
label: 'title'
},
showChild: false,
tempList: [],
delKeys: [],
ableKeys: []
}
},
computed: {
// 源列表数据
originList() {
return this.data;
},
// 目的列表Tree
finalTree() {
this.finalList.forEach(item => {
if (item.open === undefined && Number(item.pid) === 0) {
this.$set(item, 'open', false)
}
})
return Tool.arrToTree(this.finalList)
},
// 目的列表keys
finalKeys() {
return this.finalList.map(item => item.id)
},
// 根据源列表选择的keys生成的目的列表的keys
originToFinalKeys() {
const parentKeys = []
this.originList.forEach(item => {
if (this.originKeys.includes(item.id) && Number(item.pid) !== 0) {
parentKeys.push(item.pid)
}
})
return this.unique([...parentKeys, ...this.originKeys])
}
},
created() {
// 初始化源列表Tree
this.initOriginTree()
},
mounted() {
// 初始化源列表点选
this.initOriginChecked()
// this.initOriginDisChecked()
},
methods: {
// 弹出删除弹出层
popDelModal(id, pid) {
this.delId = id
this.delPid = pid
this.delShow = true
},
getFinalKeys() {
// console.log(this.originToFinalKeys)
},
// 删除弹出层确认
delOk() {
const keys = []
keys.push(this.delId)
if (Number(this.delPid) === 0) {
// 如果删除的key是一级分类
this.finalTree.forEach(item => {
if (item.id === this.delId) {
item.children.forEach(child => {
keys.push(child.id)
})
}
})
} else {
// 如果删除的key是二级分类
keys.push(this.delPid)
}
// 删除
keys.forEach(key => {
this.originKeys = this.removeByValue(this.originKeys, key)
})
// 同步到左侧
this.$refs.originTree.setCheckedKeys(this.originKeys)
this.delCancel()
},
// 删除弹出层取消
delCancel() {
this.delShow = false
this.delId = ''
this.delPid = ''
},
// 初始化源列表Tree
initOriginTree() {
this.originTree = Tool.arrToTree(this.originList)
},
// 初始化源列表点选
initOriginChecked() {
const checkedKeys = []
this.finalList.forEach(item => {
if (Number(item.pid) !== 0) {
checkedKeys.push(item.id)
} else {
// 判断是否只有一级分类
if (!this.finalList.map(i => i.pid).includes(item.id)) {
checkedKeys.push(item.id)
}
}
})
this.$refs.originTree.setCheckedKeys(checkedKeys)
},
// 获取源列表keys
getOriginKeys(data, checked, indeterminate) {
// console.log(data, checked, indeterminate)
// if (checked === true) {
// this.disabledKeys.push(data.id)
// } else {
// this.disabledKeys.splice(this.disabledKeys.findIndex(item => item === data.id), 1)
// }
this.originKeys = this.$refs.originTree.getCheckedKeys()
// 点选源列表时关闭目的列表编辑弹出层
// this.editCancel()
// 点选源列表时关闭目的列表删除弹出层
// this.delCancel()
},
// 在 target 数组中取出 arg 数组中没有的值组成数组返回
findOut(target, arg) {
return target.filter(v => !arg.includes(v))
},
// 删除数组中的某个值
removeByValue(target, arg) {
const index = target.indexOf(arg)
if (index !== -1) {
target.splice(index, 1)
}
return target
},
// 数组去重
unique(arr) {
const newArr = []
arr.forEach(item => {
if (!newArr.includes(item)) {
newArr.push(item)
}
})
return newArr
},
// 展开
toggleOpen(id) {
// 修改的是finalList 不是 finalTree,所以要通过id去获取而非index
this.finalList.forEach(item => {
if (item.id === id) {
this.$set(item, 'open', !item.open)
}
})
}
},
watch: {
// 根据源列表的操作,同步目的列表
originToFinalKeys() {
// 点选源列表生成的目的列表中存在,但当前目的列表中没有
this.findOut(this.originToFinalKeys, this.finalKeys).forEach(key => {
this.originList.forEach(item => {
if (item.id === key) {
this.finalList.push(Tool.deepClone(item))
}
})
})
// 源列表取消选中,去除目的列表中的item
this.findOut(this.finalKeys, this.originToFinalKeys).forEach(key => {
this.finalList.forEach(item => {
if (item.id === key) {
this.finalList = this.removeByValue(this.finalList, item)
}
})
})
},
finalList() {
// 初始化左侧点选
this.initOriginChecked()
}
}
}
</script>
<style rel="stylesheet/scss" lang="scss">
.item-icon {
font-size: 16px;
vertical-align: middle;
color: #c0c4cc;
}
.tree-child {
display: none;
}
.tree-show {
display: block!important;
}
</style>
你期待的结果是什么?实际看到的错误信息又是什么?
选过的数据不可选,选过的数据不可选可以时间,但是编辑的时候回显出现问题
自己解决了,关键是重新初始化了数据,每次弹出框的时候,将数据拷贝一份,传入组件,
每次重新渲染组件的方式就是用v-if
<synctree v-if="reloadTree" :data.sync="tempList" v-model="finalData"></synctree>
每次点击的时候重新渲染,不过这样有个弊端,数据多了性能差,希望大家能有其他的解决办法
闲了会去整理一个小的demo出来,欢迎去我的github围观
