平台属性管理
三级联动
1、封装为全局组件 2、使用element-ui设置静态样式 3、接口
import request from '@/utils/request';
export const reqCategory1List = ()=>request({url:'/admin/product/getCategory1',method:'get'});
export const reqCategory2List = (category1Id)=>request({url:`/admin/product/getCategory2/${category1Id}`,method:'get'});
export const reqCategory3List = (category2Id)=>request({url:`/admin/product/getCategory3/${category2Id}`,method:'get'});
4、组件挂载完毕发送请求,获取数据
async getCategory1List() {
let result = await this.$API.attr.reqCategory1List();
if (result.code == 200) {
this.list1 = result.data;
}
},
async handler1() {
this.list2 = [];
this.list3 = [];
this.cForm.category2Id = "";
this.cForm.category3Id = "";
const { category1Id } = this.cForm;
this.$emit("getCategoryId", { categoryId: category1Id, level: 1 });
let result = await this.$API.attr.reqCategory2List(category1Id);
if (result.code == 200) {
this.list2 = result.data;
}
},
async handler2() {
this.list3 = [];
this.cForm.category3Id = "";
const { category2Id } = this.cForm;
this.$emit("getCategoryId", { categoryId: category2Id, level: 2 });
let result = await this.$API.attr.reqCategory3List(category2Id);
if (result.code == 200) {
this.list3 = result.data;
}
},
handler3() {
const { category3Id } = this.cForm;
this.$emit("getCategoryId", { categoryId: category3Id, level: 3 });
},
5、数据展示
<el-form :inline="true" class="demo-form-inline" :model="cForm">
<el-form-item label="一级分类">
<el-select
placeholder="请选择"
v-model="cForm.category1Id"
@change="handler1"
:disabled="show"
>
<el-option
:label="c1.name"
:value="c1.id"
v-for="(c1, index) in list1"
:key="c1.id"
></el-option>
</el-select>
</el-form-item>
<el-form-item label="二级分类">
<el-select
placeholder="请选择"
v-model="cForm.category2Id"
@change="handler2"
:disabled="show"
>
<el-option
:label="c2.name"
:value="c2.id"
v-for="(c2, index) in list2"
:key="c2.id"
></el-option>
</el-select>
</el-form-item>
<el-form-item label="三级分类">
<el-select
placeholder="请选择"
v-model="cForm.category3Id"
@change="handler3"
:disabled="show"
>
<el-option
:label="c3.name"
:value="c3.id"
v-for="(c3, index) in list3"
:key="c3.id"
></el-option>
</el-select>
</el-form-item>
</el-form>
逻辑1: 当获得二级分类的数据时,需要向服务器传递一级分类的id,当获得三级分类时,需要向服务器传递二级分类的id,所以此时需要使用v-model收集二级和一级id。 逻辑2: 当一级列表的选中的内容即option发生变化时,二级分类的相应数据就应该被获取,三级分类和二级分类一样。 逻辑3: 当三个级别的列表都已经选择完毕之后,再次修改一级列表内容时,二三级列表内容及id也应该被清空,当二级列表内容修改之后,三级列表内容及id也会清空。
平台属性展示
子组件通过自定义事件向父组件传递数据
this.$emit("getCategoryId", { categoryId: category1Id, level: 1 });
getCategoryId({ categoryId, level }) {
if (level == 1) {
this.category1Id = categoryId;
this.category2Id = "";
this.category3Id = "";
} else if (level == 2) {
this.category2Id = categoryId;
this.category3Id = "";
} else {
this.category3Id = categoryId;
this.getAttrList();
}
},
},
逻辑1: 当子组件触发生成id传递给父组件时,父组件会同时获得三个组件的id,但是父组件无法分辨是几级分类的id,所以此时子组件应该向父组件传递的是一个对象,对象中包含id加level,一二三级分类level值都不一样,此时父组件可以判断得到对象的level来判断是几级id,然后将id存储在相应的位置。 逻辑2: 当三个级别的列表都已经选择完毕之后,再次修改一级列表内容时,二三级列表id也应该被修改,当二级列表内容修改之后,三级列表id也应该清空。
根据收集的id获取数据并展示
1、接口
export const reqAttrList = (category1Id,category2Id,category3Id)=>request({url:`/admin/product/attrInfoList/${category1Id}/${category2Id}/${category3Id}`,method:'get'});
2、发请求获取数据
async getAttrList() {
const { category1Id, category2Id, category3Id } = this;
let result = await this.$API.attr.reqAttrList(
category1Id,
category2Id,
category3Id
);
if (result.code == 200) {
this.attrList = result.data;
}
3、静态 4、数据展示
<el-card style="margin: 20px 0px">
<CategorySelect @getCategoryId="getCategoryId" :show="!isShowTable"></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>
添加属性
1、让添加属性和原本展示的模块进行动态展示与隐藏
将添加属性和原本展示分成两个模块,给原本展示的模块添加一个属性,使用v-show进行展示。当点击添加按钮和编辑属性按钮隐藏。
2、添加属性静态页面
</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>
<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 width="width" prop="prop" label="属性值名称">
<template slot-scope="{ row, $index }">
<el-input
v-model="row.valueName"
placeholder="请输入属性值名称"
size="mini"
v-if="row.flag"
@blur="toLook(row)"
@keyup.native.enter="toLook(row)"
:ref="$index"
></el-input>
<span
v-else
@click="toEdit(row, $index)"
style="display: block"
>{{ row.valueName }}</span
>
</template>
</el-table-column>
<el-table-column width="width" prop="prop" label="操作">
<template slot-scope="{ row, $index }">
</el-table>
<el-button type="primary" @click="addOrUpdateAttr" :disabled="attrInfo.attrValueList.length<1">保存</el-button>
<el-button @click="isShowTable = true">取消</el-button>
</div>
3、收集属性名与属性值 (1)接口
export const reqAddOrUpdateAttr = (data)=>request({url:'/admin/product/saveAttrInfo',method:'post',data});
{
"attrName": "",
"attrValueList": [
{
"attrId": 0,
"valueName": "string"
}
],
"categoryId": 0,
"categoryLevel":3,
}
(2)属性名使用v-model进行收集 (3)属性值收集,书写添加属性值的回调
逻辑1: 因为对象的是无序的,收集属性值时不要在data中收集,所以此时书写一个添加属性值的回调,利用数组的方式收集 逻辑2: 当点击修改属性值时,添加的属性值应该带有自己属性的id,而当点击新增属性时,新增属性是没有id的
(4)当点击取消按钮
当点击取消按钮则代表写入的数据没有用了,所以当下一次点击添加属性值的按钮时,输入框内的值应该被清空。所以此时选择在每次点击添加属性值按钮时都清空一次输入框内的数据
(5)修改属性,此时需要用到深拷贝,按需引入lodash当中的深拷贝
import cloneDeep from "lodash/cloneDeep";
this.attrInfo = cloneDeep(row);
4、查看模式和修改模式切换
查看模式:显示span 编辑模式:显示input 注意:通过flag标记进行切换查看模式与编辑模式,但是需要注意的时候,一个属性flag没有办法控制全部的属性值的切换
使用信号量flag来判断为查看模式还是修改模式,但是此flag不能添加到data中,应该添加在每个属性值对象里面,这样才能修改一个属性值的模式而不对其他属性值造成影响,然后再用v-if和v-else进行展示,并且添击事件
5、输入的属性值校验,即在切换为查看模式之前先校验用户输入的数据是否正确 6、修改属性的查看模式和编辑模式
修改属性时的属性值里没有flag,所以需要给要修改的属性值添加一个flag,但是不能使用item.flag = false 这种方式,因为这种方式添加的属性flag并不是响应式属性,此时需要用到$set。
$set:向响应式对象中添加一个property,并确保这个新的property同样是响应式的,且触发视图更新。它必须用于向响应式对象上添加新的property,因为Vue无法探测普通的新增property
this.$set(item, "flag", false);
7、表单元素自动聚焦
逻辑: 给span添加点击事件,点击之后flag变成true,即生成input,再使用ref获取input节点,当生成多个input时,然后使用index来区分是哪一个生成的input。
注意: 点击span的时候,切换为input变为编辑模式,但是需要注意,对于浏览器而言,页面重绘与重拍耗时间的,点击span的时候,我们不可能一点击span立马获取到input。
$nextTick:当节点渲染完毕了,会执行一次
8、删除属性值,不需要发请求
数据展示的是服务器返回的数据,而服务器获得的数据也是页面所收集的数据,所以此时并不需要修改。
9、当添加属性或修改属性点击保存按钮时向服务器发请求
整理参数: 1、如果用户添加很多属性值,且属性值为空的不应该提交给服务器 2、提交给服务器数据当中不应该出现flag字段
注意:当保存完数据之后,应该再次调用新的数据然后进行展示,并且isShowTable变成true。
三级联动以及保存按钮的可操作性
三级联动的可操作和不可操作由isShowTable决定,使用props将父组件的isShowTable传递给子组件
保存按钮的可操作和不可操作由属性值数组的长度,当长度小于1时不展示。
代码
平台属性管理
<template>
<div>
<el-card style="margin: 20px 0px">
<CategorySelect @getCategoryId="getCategoryId" :show="!isShowTable"></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>
<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 width="width" prop="prop" label="属性值名称">
<template slot-scope="{ row, $index }">
<el-input
v-model="row.valueName"
placeholder="请输入属性值名称"
size="mini"
v-if="row.flag"
@blur="toLook(row)"
@keyup.native.enter="toLook(row)"
:ref="$index"
></el-input>
<span
v-else
@click="toEdit(row, $index)"
style="display: block"
>{{ row.valueName }}</span
>
</template>
</el-table-column>
<el-table-column width="width" prop="prop" label="操作">
<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="addOrUpdateAttr" :disabled="attrInfo.attrValueList.length<1">保存</el-button>
<el-button
@click="isShowTable = true">取消</el-button>
</div>
</el-card>
</div>
</template>
<script>
import cloneDeep from "lodash/cloneDeep";
export default {
name: "Attr",
data() {
return {
category1Id: "",
category2Id: "",
category3Id: "",
attrList: [],
isShowTable: true,
attrInfo: {
attrName: "",
attrValueList: [
],
categoryId: 0,
categoryLevel: 3,
},
};
},
methods: {
getCategoryId({ categoryId, level }) {
if (level == 1) {
this.category1Id = categoryId;
this.category2Id = "";
this.category3Id = "";
} else if (level == 2) {
this.category2Id = categoryId;
this.category3Id = "";
} else {
this.category3Id = categoryId;
this.getAttrList();
}
},
async getAttrList() {
const { category1Id, category2Id, category3Id } = this;
let result = await this.$API.attr.reqAttrList(
category1Id,
category2Id,
category3Id
);
if (result.code == 200) {
this.attrList = result.data;
}
},
addAttrValue() {
this.attrInfo.attrValueList.push({
attrId: this.attrInfo.id,
valueName: "",
flag: true,
});
this.$nextTick(() => {
this.$refs[this.attrInfo.attrValueList.length - 1].focus();
});
},
addAttr() {
this.isShowTable = false;
this.attrInfo = {
attrName: "",
attrValueList: [
],
categoryId: this.category3Id,
categoryLevel: 3,
};
},
updateAttr(row) {
this.isShowTable = false;
this.attrInfo = cloneDeep(row);
this.attrInfo.attrValueList.forEach((item) => {
this.$set(item, "flag", false);
});
},
toLook(row) {
if(row.valueName.trim()==''){
this.$message('请你输入一个正常的属性值');
return;
}
let isRepat = this.attrInfo.attrValueList.some(item=>{
if(row!==item){
return row.valueName==item.valueName;
}
});
if(isRepat) return;
row.flag = false;
},
toEdit(row, index) {
row.flag = true;
this.$nextTick(() => {
this.$refs[index].focus();
});
},
deleteAttrValue(index){
this.attrInfo.attrValueList.splice(index,1);
},
async addOrUpdateAttr(){
this.attrInfo.attrValueList = this.attrInfo.attrValueList.filter(item=>{
if(item.valueName!=''){
delete item.flag;
return true;
}
})
try {
await this.$API.attr.reqAddOrUpdateAttr(this.attrInfo);
this.isShowTable = true;
this.$message({type:'success',message:'保存成功'});
this.getAttrList();
} catch (error) {
}
}
},
};
</script>
三级联动组件
<template>
<div>
<el-form :inline="true" class="demo-form-inline" :model="cForm">
<el-form-item label="一级分类">
<el-select
placeholder="请选择"
v-model="cForm.category1Id"
@change="handler1"
:disabled="show"
>
<el-option
:label="c1.name"
:value="c1.id"
v-for="(c1, index) in list1"
:key="c1.id"
></el-option>
</el-select>
</el-form-item>
<el-form-item label="二级分类">
<el-select
placeholder="请选择"
v-model="cForm.category2Id"
@change="handler2"
:disabled="show"
>
<el-option
:label="c2.name"
:value="c2.id"
v-for="(c2, index) in list2"
:key="c2.id"
></el-option>
</el-select>
</el-form-item>
<el-form-item label="三级分类">
<el-select
placeholder="请选择"
v-model="cForm.category3Id"
@change="handler3"
:disabled="show"
>
<el-option
:label="c3.name"
:value="c3.id"
v-for="(c3, index) in list3"
:key="c3.id"
></el-option>
</el-select>
</el-form-item>
</el-form>
</div>
</template>
<script>
export default {
name: "CategorySelect",
props: ["show"],
data() {
return {
list1: [],
list2: [],
list3: [],
cForm: {
category1Id: "",
category2Id: "",
category3Id: "",
},
};
},
mounted() {
this.getCategory1List();
},
methods: {
async getCategory1List() {
let result = await this.$API.attr.reqCategory1List();
if (result.code == 200) {
this.list1 = result.data;
}
},
async handler1() {
this.list2 = [];
this.list3 = [];
this.cForm.category2Id = "";
this.cForm.category3Id = "";
const { category1Id } = this.cForm;
this.$emit("getCategoryId", { categoryId: category1Id, level: 1 });
let result = await this.$API.attr.reqCategory2List(category1Id);
if (result.code == 200) {
this.list2 = result.data;
}
},
async handler2() {
this.list3 = [];
this.cForm.category3Id = "";
const { category2Id } = this.cForm;
this.$emit("getCategoryId", { categoryId: category2Id, level: 2 });
let result = await this.$API.attr.reqCategory3List(category2Id);
if (result.code == 200) {
this.list3 = result.data;
}
},
handler3() {
const { category3Id } = this.cForm;
this.$emit("getCategoryId", { categoryId: category3Id, level: 3 });
},
},
};
</script>
|