Vue后台管理系统项目(19)添加与修改属性,查看模式与编辑模式切换及注意事项(完全体)

目录

gitee仓库地址:

业务需求

1.新增的属性值id问题

2.添加属性值按钮进入的查看模式与编辑模式切换

​编辑

找到对应的结构进行书写

问题1:

问题2:

问题3

3.修改按钮进入的查看模式与编辑模式切换 

完整代码:


gitee仓库地址:

https://gitee.com/CMD-UROOT/my_project/commits/master

大家根据上传历史进行查找你需要的代码

业务需求

1.新增的属性值id问题

我们知道已有的属性,数据是服务器返回过来的,而且已有的属性已经有了相应的id

我们点击修改按钮进入,点击添加属性值新增一个华为,但是从数据中看,没有属性值的id,然后已经有的属性值安卓和苹果手机的数据中是有id的,所以我们需要给我们新增的属性值以id,那么什么时候给新增的属性值id呢?

当我们点击添加属性值的时候,就应该给新增的属性值添加id

那么新增的属性值的id是谁呢?
新增属性值的id就是attrInfo的id

 在views/product/Attr/index.vue中:

在views/product/Attr/index.vue中: 

这里解决了点击修改按钮进入,然后点添加属性值按钮,新增属性值id的问题

methods中

2.添加属性值按钮进入的查看模式与编辑模式切换

找到对应的结构进行书写

 

问题1:

查看模式:显示span

编辑模式:显示input

注意:通过flag标记进行切换查看模式与编辑模式,但是需要注意的时候,一个属性flag没有办法控制全部的属性值的切换

 但是我们肯定还是需要信号量来控制到底谁显示,谁隐藏,解决思路是,让每一个属性值去控制当前是查看模式还是编辑模式,意思就是flag不要放在data中,放data中只有一个,我们点击添加属性值的按钮的时候,我们给新增的属性值身上都加一个标记,让新增的属性控制自己是查看模式还是编辑模式

我们在添加属性值的回调中这样写:

效果实现完成

问题2:

但是我们发现用户如果不输入内容是空值的情况也有 

我们需要进行判断:

关于trim()    Trim是String型数据的一个方法,作用是去掉字符串开头和结尾的空格,比如说字符串a="____a__",其中_表示一个空格,那么a.Trim() = "a",字符串前后的空格都被清掉了。调用这个方法后,a的值是不变的,也就是说,虽然a.Trim() = "a",但a="____a__"还是成立的。

效果:实现

 问题3

属性的属性值不应该有重复的 

我们知道row是用户输入的最新的属性值就是最后添加的属性值,而所有的属性值在attrInfo里面的attrValueList里面

 所有我们需要进行判断

some遍历attrValueList这个数组的时候,应该把自身排除,不能拿自己的valueName和自己判断,不然永远都是重复的,item是数组中每一个属性值,

 

 问题解决

3.修改按钮进入的查看模式与编辑模式切换 

我们点击修改按钮进入的界面 

 这个界面我们无法进行编辑模式和查看模式的切换

原因是:

在数据中 属性的数值值的身上没有flag字段,因此没办法实现查看模式与编辑模式的切换

我们知道修改属性的属性值,是当我们点击修改属性的按钮的时候

 这样书写也可以给属性值添加flag属性,但是会发现视图不会跟着变化(因为flag不是响应式数据)

 我们需要用到一个新的属性$set来解决问题

官方文档:API — Vue.js

 我们利用$set完美解决

 效果完成

完整代码:

<template>
  <div>
    <el-card style="margin:20px 0px;">
      <CategorySelect @getCategoryId="getCategoryId"></CategorySelect>
    </el-card>
    <el-card>
      <div v-show="isShowTable">
        <el-button type="primary" icon="el-icon-plus" :disabled="!category3Id" @click="addAttr">添加属性</el-button>
        <!-- 表格:展示平台属性 -->
        <el-table style="width:100%" border :data="attrList">
          <el-table-column type="index" label="序号" width="80" align="center"></el-table-column>
          <el-table-column prop="attrName" label="属性名称" width="150"></el-table-column>
          <el-table-column prop="prop" label="属性值列表" width="width">
            <template slot-scope="{row,$index}">
              <el-tag type="success" v-for="(attrValue,index) in row.attrValueList" :key="attrValue.id"
                style="margin:0px 20px">{
   
   { attrValue.valueName }}</el-tag>
            </template>
          </el-table-column>
          <el-table-column prop="prop" label="操作" width="150">
            <template slot-scope="{row, $index}">
                <el-button type="warning" icon="el-icon-edit" size="mini" @click="updateAttr(row)"></el-button>
                <el-button type="danger" icon="el-icon-delete" size="mini"></el-button>
            </template>
          </el-table-column>
        </el-table>
      </div>
      <!-- 添加属性|修改属性的结构 -->
        <div v-show="!isShowTable">
          <el-form :inline="true" ref="form" label-width="80px" :model="attrInfo">
            <el-form-item label="属性名">
              <el-input placeholder="请输入属性名" v-model="attrInfo.attrName"></el-input>
            </el-form-item>
          </el-form>
          <!-- :disabled="!attrInfo.attrName"当有属性名的时候,按钮才可以用 -->
          <el-button type="primary" icon="el-icon-plus" @click="addAttrValue" :disabled="!attrInfo.attrName">添加属性值</el-button>
          <el-button @click="isShowTable=true">取消</el-button>
          <el-table style="width:100%;margin:20px 0px" border :data="attrInfo.attrValueList">
            <el-table-column align="center" type="index" label="序号" width="80"></el-table-column>
            <el-table-column prop="prop" label="属性值名称" width="width">
              <template slot-scope="{row,$index}">
                <!-- 
                  这里的结构需要用到span和input进行来回的切换 
                  @blur="" 鼠标离开隐藏input显示span
                  @click="" 点击span显示input隐藏span  
                  @keyup.native.enter="" input按回车显示span,隐藏input
                  修饰符enter只能用于DOM事件,这里利用native来让他在这里可用
                  style="display:block"因为是span为了点击方便,我们把他转为块级元素,让点击范围大一些
                -->
                <el-input v-model="row.valueName" placeholder="请输入属性值名称" size="mini" v-if="row.flag"
                  @blur="toLook(row)"  @keyup.native.enter="toLook(row)">
                </el-input>
                <span v-else @click="row.flag=true" style="display:block">{
   
   {row.valueName}}</span>
              </template>
            </el-table-column>
            <el-table-column prop="prop" label="操作" width="width">
              <template slot-scope="{row,$index}">
                <el-button type="primary" icon="el-icon-delete" size="mini"></el-button>
              </template>
            </el-table-column>
          </el-table>
          <el-button type="primary">保存</el-button>
          <el-button @click="isShowTable=true">取消</el-button>
        </div>
    </el-card>
  </div>
</template>

<script>
//按需引入lodash当中的深拷贝
import cloneDeep from 'lodash/cloneDeep'
  export default {
    name:'Attr',
    data() {
      return {
        //存储id将来需要发请求的时候用
        category1Id: "",
        category2Id: "",
        category3Id: "",
        //接受平台属性的数据
        attrList: [],
        //这个属性控制table表格显示与隐藏
        isShowTable:false,
        //收集新增属性|修改属性使用的
        attrInfo: {
          attrName: "", //属性名
          attrValueList: [
            //属性值,因为属性值可以有多个因此用数组,每一个属性值都是一个对象需要attrId,valueName
            // {  这块由点击事件来添加
            //   attrId:0,//相应的属性名id
            //   valueName:'',
            // }
          ],
          categoryId: 0, //三级分类的id
          categoryLevel: 3, //因为服务器也需要区分几级id
        },
      }
    },
    methods: {
      //自定义事件的回调
      getCategoryId({ categoryId, level }) {
        //区分三级分类相应的id,以及父组件进行存储
        if (level == 1) {//当重选一级分类的时候,2-3级分类的id需要清空
          this.category1Id = categoryId;
          this.category2Id = "";
          this.category3Id = "";
        } else if (level == 2) {//当重选二级分类的时候,3级分类的id需要清空
          this.category2Id = categoryId;
          this.category3Id = "";
        } else {
          //代表三级分类的id有了
          this.category3Id = categoryId;
          //发请求获取平台的属性数据
          this.getAttrList();
        }
      },
      //获取平台属性的数据
      //当用户确定三级分类的数据时候,可以向服务器发请求获取平台属性进行展示
      async getAttrList() {
        //获取分类的ID
        const { category1Id, category2Id, category3Id } = this;
        //获取属性列表的数据
        let result = await this.$API.attr.reqAttrList(category1Id,category2Id,category3Id);
        // console.log(result)
        if (result.code == 200) {
          this.attrList = result.data;
        }
      }, 
      //添加属性值得回调 (也可是修改按钮里面添加)
      addAttrValue(){
        //向属性值的数组里面添加元素
        //attrId:是你相应的属性的id,目前而言我们是添加属性的操作,还没有相应的属性的id,目前而言带给服务器的id为undefined
        //valueName:相应的属性值的名称
        this.attrInfo.attrValueList.push({
          attrId: this.attrInfo.id, //对于修改某一个属性的时候,可以在已有的属性值基础之上新增新的属性值(新增属性值的时候,需要把已有的属性的id带上)
          valueName:'',
          flag:true //控制每一个新增的属性值的显示与隐藏的标记,用于切换查看模式与编辑模式,好处是每一个属性值可以控制自己的属性切换,当前的flag是一个响应式属性(数据变化视图跟着变化),因为这里的数组attrInfo、attrValueList是都在data中声明了,在data中声明的数据都是响应式数据,而且这里的数组是push进来的,数组的检查可以通过替换和变更,push是变更,所以flag是响应式的,vue底层可以检测到flag
        })
      },
      //添加属性按钮的回调
      addAttr() {
        //切换table显示与隐藏
        this.isShowTable = false;
        //清除数据
        //收集三级分类的id
        this.attrInfo = {
          attrName: "", //属性名
          attrValueList: [
            //属性值,因为属性值可以有多个因此用数组,每一个属性值都是一个对象需要attrId,valueName
          ],
          categoryId: this.category3Id, //三级分类的id
          categoryLevel: 3, //因为服务器也需要区分几级id
        };
      },
      //修改某一个属性
      updateAttr(row){
        //console.log(row)//row里面就是我们要修改的数据
        //isShowTable变为false
        this.isShowTable = false
        //将选中的属性赋值给attrInfo
        //由于数据结构当中存在对象里面套数组,数组里面有套对象,因此需要使用深拷贝解决这类问题
        //深拷贝,浅拷贝在面试的时候出现频率很高,切记达到手写深拷贝与浅拷贝
        this.attrInfo = cloneDeep(row)
        //在修改某一个属性的时候,将相应的属性值元素添加上flag这个标记
        this.attrInfo.attrValueList.forEach(item=>{
          //这样书写也可以给属性值添加flag属性,但是会发现视图不会跟着变化(因为flag不是响应式数据)item.flag = false
          //因为 Vue 无法探测普通的新增 property,这样书写的属性并非响应式属性(数据变化视图跟着变)
          //第一个参数:对象  第二个参数:添加新的响应式属性  第三参数:新的属性的属性值
          this.$set(item,'flag',false)
        })
      },
      //失去焦点以及回车的事件---切换为查看模式,展示span
      toLook(row){
        //如果属性值为空不能作为新的属性值,需要给用户提示,让他输入一个新的属性值
        //Trim是String型数据的一个方法,作用是去掉字符串开头和结尾的空格,比如说字符串a="____a__",其中_表示一个空格,那么a.Trim() = "a",字符串前后的空格都被清掉了。调用这个方法后,a的值是不变的,也就是说,虽然a.Trim() = "a",但a="____a__"还是成立的。
        if(row.valueName.trim()==''){
          this.$message('请你输入一个正常的属性值')//提示
          return
        }
        //新增的属性值不能与已有的属性值重复
        let isRepat = this.attrInfo.attrValueList.some(item=>{//item是数组中每一个属性值
          //需要将row从数组里面判断的时候去除
          //row是最新新增的属性值[数组的最后一项元素]
          //判断的时候,需要把已有的数组当中新增的这个属性值去除
          if(row!==item){//row!==item,那么走这个if的就是排除了row最新的属性值的情况
            return row.valueName==item.valueName
          }
        })
        //console.log(isRepat)//出现重复为true,否则为false
        if(isRepat) return  //如果出现重复的,直接return,不执行后面代码
        //row:形参是当前用户添加的最新的属性值
        //当前的编辑模式变为查看模式[让input消失,显示span]
        row.flag = false
      }
    },
  }
</script>

<style scoped>

</style>

猜你喜欢

转载自blog.csdn.net/qq_46368050/article/details/125088438