vue element ui tree 选择数据后隐藏,编辑时显示

各种搞了好几天了一直出问题,请大神给个思路,感觉是自己对数组,数据操作的时候问题

问题描述

添加运费模板,用户选择地区后该地区不可选或者删除,当用户编辑的时候数据回显

问题出现的环境背景及自己尝试过哪些方法

把选过的id放到一个数组disabled中并存到vuex中,然后再次编辑的时候将编辑的项从disabled中删除
但是左侧数据只初始化一次,再次编辑操作的时候数据会相互影响

相关代码

clipboard.png

数据结构

{"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>

你期待的结果是什么?实际看到的错误信息又是什么?

选过的数据不可选,选过的数据不可选可以时间,但是编辑的时候回显出现问题

阅读 10.9k
1 个回答

自己解决了,关键是重新初始化了数据,每次弹出框的时候,将数据拷贝一份,传入组件,
每次重新渲染组件的方式就是用v-if
<synctree v-if="reloadTree" :data.sync="tempList" v-model="finalData"></synctree>
每次点击的时候重新渲染,不过这样有个弊端,数据多了性能差,希望大家能有其他的解决办法

     handleAdd() {
        this.reloadTree = true
        // 拷贝数据
        this.tempList = deepClone(this.originData)
        // 修改不可选数据disabled属性
        this.disabledKeys.forEach(key => {
          this.tempList.forEach(item => {
            if (key === item.id) {
              this.$set(item, 'disabled', true)
            }
          })
        })
        // 显示弹框
        this.dialogVisible = true
      },

闲了会去整理一个小的demo出来,欢迎去我的github围观
clipboard.png

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题