目录
三级联动
三级联动全局组件静态页面
注册全局组件必须提供name
import componentSelect from './components/Select/index.vue'
// 注册全局组件
Vue.component(componentSelect.name, componentSelect)
v-model的原理
v-bind绑定value属性的值;
v-on绑定input事件监听到函数中,函数会获取最新的值赋值到绑定的属性中;
<div>
<!-- inline 行内表单:一行可以放值多个表单元素 -->
<el-form :inline="true" class="demo-form-inline">
<el-form-item label="一级分类">
<el-select placeholder="清选择" value="">
<el-option label="区域一" value="shanghai"></el-option>
<el-option label="区域二" value="beijing"></el-option>
</el-select>
</el-form-item>
<el-form-item label="一级分类">
<el-select placeholder="清选择" value="">
<el-option label="区域一" value="shanghai"></el-option>
<el-option label="区域二" value="beijing"></el-option>
</el-select>
</el-form-item>
<el-form-item label="一级分类">
<el-select placeholder="清选择" value="">
<el-option label="区域一" value="shanghai"></el-option>
<el-option label="区域二" value="beijing"></el-option>
</el-select>
</el-form-item>
</el-form>
</div>
ui组件介绍
- el-form inline 行内表单:一行可以放值多个表单元素
- el-select v-model收集选择的值 对应options的value changeoptions发生变化时触发
- el-option label 展示的数据 value要收集的数据
代码
父组件定义事件 子组件通过 this.$emit('事件名,传递的值)传递id
<template>
<div>
<!-- inline 行内表单:一行可以放值多个表单元素 -->
<el-form :inline="true" class="demo-form-inline">
<el-form-item label="一级分类">
<el-select
placeholder="清选择"
v-model="cFROM.getCategory1ID"
@change="handle1"
:disabled="isShow"
>
<el-option
:label="item.name"
:value="item.id"
v-for="item in list1"
:key="item.id"
></el-option>
</el-select>
</el-form-item>
<el-form-item label="二级分类">
<el-select
placeholder="清选择"
v-model="cFROM.getCategory2ID"
@change="handle2"
:disabled="isShow"
>
<el-option
:label="item.name"
:value="item.id"
v-for="item in list2"
:key="item.id"
></el-option>
</el-select>
</el-form-item>
<el-form-item label="三级分类">
<el-select
placeholder="清选择"
v-model="cFROM.getCategory3ID"
@change="handle3"
:disabled="isShow"
>
<el-option
:label="item.name"
:value="item.id"
v-for="item in list3"
:key="item.id"
></el-option>
</el-select>
</el-form-item>
</el-form>
</div>
</template>
<script>
export default {
name: "componentSelect",
data() {
return {
// 一级分类数据
list1: [],
// 二级分类数据
list2: [],
// 三级分类数据
list3: [],
//收集相应的分类id
cFROM: {
getCategory1ID: "",
getCategory2ID: "",
getCategory3ID: "",
},
};
},
props: ["isShow"],
//组件初始化完毕获取一级分类
created() {
//获取一级分类
this.getCategory1();
},
methods: {
//获取一级分类不需要携带id
async getCategory1() {
let { code, data } = await this.$api.attr.getCategory1();
if (code !== 200) return;
this.list1 = data;
},
//一级下拉框事件,option发生变化获取二级
async handle1() {
// 清除数据
this.list2 = [];
this.list3 = [];
this.cFROM.getCategory2ID = "";
this.cFROM.getCategory3ID = "";
//解构出一级分类的id
let { getCategory1ID } = this.cFROM;
//获取二级分类
let { code, data } = await this.$api.attr.getCategory2(getCategory1ID);
if (code !== 200) return;
this.list2 = data;
},
//二级下拉框事件,option发生变化获取三级
async handle2() {
this.list3 = [];
this.cFROM.getCategory3ID = "";
//解构出二级分类的id
let { getCategory2ID } = this.cFROM;
//获取三级分类
let { code, data } = await this.$api.attr.getCategory3(getCategory2ID);
if (code !== 200) return;
this.list3 = data;
},
//三级分类
handle3() {
this.$emit("getShopId", this.cFROM);
},
},
};
</script>
<style>
</style>
返回按钮数据回显问题
当使用data里面的数据进行返显修改的时候,注意要使用克隆
浅克隆:直接将存储在栈中的值赋值给对应变量,如果是基本数据类型,则直接赋值对应的值,如果是引用类型,则赋值的是地址。{展开运算符}
深克隆:把数据赋值给对应的变量,从而产生一个与源数据不相干的新数据(数据地址已变化)。深克隆是克隆对象各个层级的属性。
Lodash 安装
浏览器环境:
<script src="lodash.js"></script>
通过 npm:
$ npm i -g npm
$ npm i --save lodash
Lodash使用
import lodash from 'lodash'
lodash.cloneDeep(objects)
查看模式与修改模式切换
添加属性中的 查看模式与编辑模式 切换
查看模式:显示span
编辑模式:显示input
注意:通过flag标记进行切换查看模式与编辑模式,但是需要注意的时候,一个属性flag没有办法控制全部的属性值的切换
处理非响应式数据
vue2 对象做了 设置 读取的劫持 不能添加 删除
数组不能操作下标 重写了7大方法 push pop shift unshift sort reverse splice
vue无法探测普通的新增的属性 如果想添加一个响应式数据使用 this.$set(对象,属性名,属性值)
.$nextTick 异步回调
nextTick 节点渲染完毕执行一次拿到最新dom
ref 需要在dom渲染完成后才会有,在使用的时候确保dom已经渲染完成。比如在生命周期 mounted(){} 钩子中调用,或者在 this.$nextTick(()=>{}) 中调用。
例如 获取input节点,实现自动聚焦,注意点击span的时候,切换为input的时候,对于浏览器而言,页面的重绘重排是耗时间的,点击span的时候重绘重排一个input是耗时间的,因此不能一点击span立即获取到input 要使用nextTick
完整代码
接口
//平台属性管理模块请求文件
import request from '@/utils/request';
//获取一级分类数据接口
// /admin/product/getCategory1 get
export const getCategory1 = () => {
return request.get('/admin/product/getCategory1')
}
//获取二级分类数据接口
//admin/product/getCategory2/{category1Id} get
export const getCategory2 = (category1Id) => {
return request.get(`/admin/product/getCategory2/${category1Id}`)
}
//获取三级分类数据接口
///admin/product/getCategory3/{category2Id} get
export const getCategory3 = (category2Id) => {
return request.get(`/admin/product/getCategory3/${category2Id}`)
}
//获取平台属性接口
///admin/product/attrInfoList/{category1Id}/{category2Id}/{category3Id} get
export const getInfoList = (category1Id, category2Id, category3Id) => {
return request.get(`/admin/product/attrInfoList/${category1Id}/${category2Id}/${category3Id}`)
}
//添加属性与属性值接口
///admin/product/saveAttrInfo post
export const saveAttrInfo = (data) => {
return request.post('/admin/product/saveAttrInfo', data)
}
/* {
"attrName": "", 属性名
"attrValueList": [ 属性名中属性值,因为属性值可以是多个,因此需要的是数组
{
"attrId": 0, 属性的id(归属于那个属性名)
"valueName": "string" 属性值
}
],
"categoryId": 0, category3Id
"categoryLevel":3,
} */
<template>
<div>
<el-card>
<componentSelect
@getShopId="getShopId"
:isShow="!isShow"
></componentSelect>
</el-card>
<el-card>
<div v-show="isShow">
<el-button
type="primary"
icon="el-icon-plus"
:disabled="!listId"
@click="addAttr"
>
添加属性</el-button
>
<!-- 表格 平台属性-->
<el-table :data="attrList" border>
<el-table-column
label="序号"
width="80"
type="index"
align="center"
></el-table-column>
<el-table-column label="属性名称" width="150" prop="attrName">
</el-table-column>
<el-table-column label="属性值列表">
<template slot-scope="{ row }">
<el-tag v-for="item in row.attrValueList" :key="item.id">
{{ item.valueName }}</el-tag
>
</template>
</el-table-column>
<el-table-column label="操作" width="150">
<template slot-scope="{ row }">
<el-button
type="warning"
icon="el-icon-edit"
size="mini"
@click="edit(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="!isShow">
<el-form :inline="true" label-width="80px">
<el-form-item label="属性名">
<el-input
placeholder="请输入用户名"
v-model="attrInfo.attrName"
></el-input>
</el-form-item>
</el-form>
<el-button
type="primary "
icon="el-icon-plus"
@click="addAttrValue"
:disabled="!attrInfo.attrName"
>添加属性值</el-button
>
<el-button @click="isShow = true">取消</el-button>
<el-table border style="margin: 20px" :data="attrInfo.attrValueList">
<el-table-column
label="序号"
width="80"
type="index"
align="center"
></el-table-column>
<el-table-column label="属性值名称" align="center" width="width">
<template slot-scope="{ row, $index }">
<el-input
placeholder="请输入属性值名称"
v-model="row.valueName"
size="mini"
v-if="row.flag"
@blur="tolook(row)"
@keyup.native.enter="tolook(row)"
:ref="$index"
></el-input>
<span
v-else
@click="editSpan(row, $index)"
style="display: block"
>{{ row.valueName }}</span
>
</template>
</el-table-column>
<el-table-column label="操作" align="center" width="width">
<!-- 气泡确认框 -->
<template slot-scope="{ row, $index }">
<el-popconfirm
:title="`确定删除${row.valueName}吗`"
@onConfirm="deleteAttrValue($index)"
>
<el-button
type="danger"
icon="el-icon-delete"
size="mini"
slot="reference"
></el-button>
</el-popconfirm>
</template>
</el-table-column>
</el-table>
<el-button
type="primary"
@click="saveAttrInfo"
:disabled="attrInfo.attrValueList.length < 1"
>保存</el-button
>
<el-button @click="isShow = true">取消</el-button>
</div>
</el-card>
</div>
</template>
<script>
import lodash from "lodash";
export default {
name: "Attr",
data() {
return {
//平台属性的字段
attrList: [],
//控制显示隐藏
isShow: true,
listId: null,
// 收集新增和修改属性
attrInfo: {
attrName: "", // 属性名
//属性名中属性值,因为属性值可以是多个,因此需要的是数组
attrValueList: [],
categoryId: 0, // 3级分类的id
categoryLevel: 3, //服务器区分
},
};
},
methods: {
// 自定义事件的获取id
getShopId(listId) {
this.listId = listId;
this.getInfoList();
},
async getInfoList() {
//获取数据
let { getCategory1ID, getCategory2ID, getCategory3ID } = this.listId;
let { code, data } = await this.$api.attr.getInfoList(
getCategory1ID,
getCategory2ID,
getCategory3ID
);
if (code !== 200) return;
this.attrList = data;
},
//添加属性值
addAttrValue() {
//向属性值数组里添加数据
// attrId 是相对应属性的id 添加属性还没有对应属性的id 应为undefined
// valueName 相应的属性值的名称
//对于修改某一个属性的时候,可以在已有的属性值基础之上新增新的属性值,(新增属性值的时候,需要把已有属性的id带上)
this.attrInfo.attrValueList.push({
attrId: this.attrInfo.id,
valueName: "",
flag: true, //给每一个属性值添加一个flag,用于切换查看模式与编辑魔兽,好处:每一个属性周可以控制自己的模式切换,响应式数据
});
this.$nextTick(() => {
this.$refs[this.attrInfo.attrValueList.length - 1].focus();
});
},
//添加属性
addAttr() {
this.isShow = false;
this.attrInfo = {
attrName: "", // 属性名
//属性名中属性值,因为属性值可以是多个,因此需要的是数组
attrValueList: [],
categoryId: this.listId.getCategory3ID, // 3级分类的id
categoryLevel: 3, //服务器区分
};
},
//修改
edit(row) {
this.isShow = false;
//由于对象里·面有数组,数组里面右有对象 使用lodash进行深克隆
this.attrInfo = lodash.cloneDeep(row);
// 在修改某一个属性的时候,将相应的属性值元素添加上flag标记
this.attrInfo.attrValueList.forEach((item) => {
// vue无法探测普通的新增的属性 如果想添加一个响应式数据使用 this.$set(对象,属性名,属性值)
this.$set(item, "flag", false);
});
},
// 失去焦点的回调 查看功能
tolook(row) {
//【如果属性值为空不能作为新的属性值,需要给用户一个提示】
if (row.valueName.trim() == "") {
this.$message("请你输入一个正常的属性值");
return;
}
//新增的属性值不能与已有的属性值重复()
let isRepat = this.attrInfo.attrValueList.some((item) => {
//需要将row从数组里面判断的时候去除
//row是最新的属性值【数组里最后一项元素】
if (row != item) {
return row.valueName == item.valueName;
}
});
if (isRepat) {
this.$message("已有该属性值");
return;
}
// row 用户添加的属性值,
// 编辑模式变查看模式 【让input消失显示,span】
row.flag = false;
},
//点击span的回调变为编辑模式
editSpan(row, index) {
row.flag = true;
//获取input节点,实现自动聚焦
//注意点击span的时候,切换为input的时候,对于浏览器而言,页面的重绘重排是耗时间的
//点击span的时候重绘重排一个input是耗时间的,因此不能一点击span立即获取到input
this.$nextTick(() => {
//获取相对应的input 表单元素实现聚焦
console.log(this.$refs[index]);
this.$refs[index].focus();
});
},
//气泡确认框删除属性值
deleteAttrValue(index) {
//不需要发请求
this.attrInfo.attrValueList.splice(index, 1);
},
//保存按钮,添加属性和修改属性的操作
async saveAttrInfo() {
// 整理数据 如果用户添加空属性值,不提交 数据中不需要flag
this.attrInfo.attrValueList = this.attrInfo.attrValueList.filter(
(item) => {
//过滤掉属性值不为空的
if (item.valueName != "") {
delete item.flag;
return true;
}
}
);
//发请求
await this.$api.attr.saveAttrInfo(this.attrInfo);
// 展示平台属性的型号量切换
this.isShow = true;
// 提示消息
this.$message({ type: "success", message: "保存成功" });
// 在次获取平台属性进行展示
this.getInfoList();
},
},
};
</script>
<style>
.el-car {
margin: 20px 0;
}
.el-tag {
margin: 0 20px;
}
.el-table {
width: 100%;
}
</style>