猿实战是一个原创系列文章,通过实战的方式,采用前后端分离的技术结合SpringMVC Spring Mybatis,手把手教你撸一个完整的电商系统,跟着教程走下来,变身猿人找到工作不是问题。更多精彩内容,敬请大家关注公主号猿人工厂,点击猿人养成获取!
上一个章节,猿人君教会了你如何去用树状的办法去展示属性值,今天我们就一起来完成新增类目属性这个方法。
功能概览
在新增类目属性这个功能中,我们可以在输入需要增加的属性后,点击查询,检索需要新增的属性。然后通过勾选的方式,将需要需要新增的属性,勾选到右边列表,点击保存后,完成新增类目属性的功能。
新增类目属性整体前端
新增类目属性,从本质上讲是一个子组件,通过我们上一章节中展示属性库的数据(猿实战11——类目属性绑定之el-tree的使用),然后通过通过动态表单的方式,提供类目属性的动态新增和删除功能。现在将页面整体都给到你。
<template>
<div id="evaluateSearchDiv">
<!-- <el-card shadow="never">
<div> -->
<el-form v-if="!showFlag" ref="listQuery" :model="listQuery" :inline="true">
<el-form-item prop="attributeName">
<el-input v-model="listQuery.propertyNameLike" placeholder="快捷查询属性组/属性名" clearable />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="fetchData()">查询</el-button>
<el-button icon="el-icon-s-tools" @click="resetForm('listQuery')">重置</el-button>
</el-form-item>
</el-form>
<!-- </div>
</el-card> -->
<el-row>
<el-col v-if="!showFlag" :span="6">
<el-card shadow="never">
<div>
<el-tree
ref="tree"
:data="data"
show-checkbox
node-key="id"
:default-expanded-keys="[1]"
:props="defaultProps"
:render-content="renderContent"
@check-change="updateKeyChildren"
/>
</div>
</el-card>
</el-col>
<el-col v-if="!showFlag" :span="1"> </el-col>
<el-col v-if="!showFlag" :span="17">
<el-card shadow="never">
<div>
<div>
<el-table
ref="table"
:data="list"
style="width: 100%;"
border
>
<el-table-column label="已选属性">
<template slot-scope="scope">
{{ scope.row.propertyName }}
</template>
</el-table-column>
<el-table-column label="排序">
<template slot-scope="scope">
<el-input v-model="scope.row.sortOrder" clearable />
</template>
</el-table-column>
<el-table-column label="状态">
<template slot-scope="scope">
<el-select v-model="scope.row.status" clearable placeholder="请选择">
<el-option
v-for="item in statusList"
:key="item.value + '^-^'"
:label="item.label"
:value="item.value"
/>
</el-select>
</template>
</el-table-column>
<el-table-column label="类型" width="170px">
<template slot-scope="scope">
<el-select v-model="scope.row.propertyType" clearable placeholder="请选择">
<el-option
v-for="item in propertyTypeList"
:key="item.value + '^-^'"
:label="item.label"
:value="item.value"
/>
</el-select>
</template>
</el-table-column>
<el-table-column label="其他">
<template slot-scope="scope">
<el-select v-model="scope.row.inputType" clearable placeholder="请选择">
<el-option
v-for="item in inputTypeList"
:key="item.value + '^-^'"
:label="item.label"
:value="item.value"
/>
</el-select>
<el-select v-model="scope.row.required" clearable placeholder="请选择">
<el-option
v-for="item in requiredList"
:key="item.value + '^-^'"
:label="item.label"
:value="item.value"
/>
</el-select>
<el-select v-model="scope.row.nav" clearable placeholder="请选择">
<el-option
v-for="item in navList"
:key="item.value + '^-^'"
:label="item.label"
:value="item.value"
/>
</el-select>
</template>
</el-table-column>
</el-table>
</div>
</div>
</el-card>
</el-col>
<el-col v-if="showFlag">
<el-card shadow="never">
<div>
<div>
<el-table
ref="table"
:data="list"
style="width: 100%;"
max-height="300"
border
>
<el-table-column label="已选属性">
<template slot-scope="scope">
{{ scope.row.propertyName }}
</template>
</el-table-column>
<el-table-column label="排序">
<template slot-scope="scope">
<el-input v-model="scope.row.sortOrder" clearable />
</template>
</el-table-column>
<el-table-column label="状态">
<template slot-scope="scope">
<el-select v-model="scope.row.status" clearable placeholder="请选择">
<el-option
v-for="item in statusList"
:key="item.value + '^-^'"
:label="item.label"
:value="item.value"
/>
</el-select>
</template>
</el-table-column>
<el-table-column label="类型" width="170px">
<template slot-scope="scope">
<el-select v-model="scope.row.propertyType" clearable placeholder="请选择">
<el-option
v-for="item in propertyTypeList"
:key="item.value + '^-^'"
:label="item.label"
:value="item.value"
/>
</el-select>
</template>
</el-table-column>
<el-table-column label="其他">
<template slot-scope="scope">
<el-select v-model="scope.row.inputType" clearable placeholder="请选择">
<el-option
v-for="item in inputTypeList"
:key="item.value + '^-^'"
:label="item.label"
:value="item.value"
/>
</el-select>
<el-select v-model="scope.row.required" clearable placeholder="请选择">
<el-option
v-for="item in requiredList"
:key="item.value + '^-^'"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-select>
</template>
</el-table-column>
</el-table>
</div>
</div>
</el-card>
</el-col>
</el-row>
<div style="text-align: center;margin-top:20px;">
<el-button type="primary" @click="addClick">
{{ bottonName }}
</el-button>
<el-button type="info" @click="closeClick">
关闭
</el-button>
</div>
</div>
</template>
<script>
import { fetchForSelect, createMallCategoryProperty, updateMallCategoryProperty } from '@/api/product-manage'
export default {
props: { attributeFlag: Boolean },
data() {
return {
bottonName: '确定',
showFlag: false,
categoryId: undefined,
updateFlag: false,
list: [
],
requiredList: [
{
value: 1,
label: '必填'
}, {
value: 0,
label: '选填'
}
],
navList: [
{
value: 1,
label: '导航属性'
}, {
value: 0,
label: '非导航属性'
}
],
inputTypeList: [
{
value: 0,
label: '单选'
}, {
value: 1,
label: '多选'
},
{
value: 2,
label: '输入'
}
],
// 类型
statusList: [
{
value: 1,
label: '启用'
}, {
value: 0,
label: '停用'
}
],
// 状态
propertyTypeList: [
{
value: 1,
label: '普通属性'
}, {
value: 2,
label: '关键属性'
},
{
value: 3,
label: '文字销售属性'
}, {
value: 4,
label: '图片销售属性'
}
],
data: [],
defaultProps: {
children: 'children',
label: 'label'
},
listQuery: {
catOneId: undefined,
catTwoId: undefined,
catThreeId: undefined
}
}
},
created() {
// 判断一些按钮是否显示
if (this.attributeFlag !== undefined) {
this.showFlag = this.attributeFlag
}
},
methods: {
updateDate(flag, row, categoryId, catOneId, catTwoId, catThreeId) {
console.log(catOneId + ';' + catTwoId + ';' + catThreeId)
this.list = []
if (flag !== undefined) {
if (row.propertyType !== null && row.propertyType !== undefined && row.propertyType !== '') {
this.updateFlag = true
this.list.push(row)
this.bottonName = '保存'
} else {
console.log(categoryId)
this.categoryId = categoryId
this.listQuery.catOneId = catOneId
this.listQuery.catTwoId = catTwoId
this.listQuery.catThreeId = catThreeId
this.bottonName = '新增'
}
}
},
updateKeyChildren(data, key1, key2) {
// this.list.push(this.atteibute)
const checkedNodes = this.$refs.tree.getCheckedNodes()
// const checkedMenuIds = new Set()
// this.list = []
if (checkedNodes != null && checkedNodes.length > 0) {
for (let i = 0; i < checkedNodes.length; i++) {
if (checkedNodes[i].label === undefined) {
this.atteibute = {
categoryId: this.categoryId,
propertyId: checkedNodes[i].propertyId,
propertyName: checkedNodes[i].propertyName,
groupId: checkedNodes[i].groupId,
sortOrder: 1,
status: 1,
propertyType: 1,
inputType: 1,
required: 0,
nav: 0
}
const checkedNode = checkedNodes[i]
var flag = true
for (let j = 0; j < this.list.length; j++) {
if (checkedNode.propertyId === this.list[j].propertyId) {
flag = false
break
}
}
if (flag) {
this.list.push(this.atteibute)
}
}
}
}
},
// 新增
addClick() {
if (this.updateFlag) {
updateMallCategoryProperty(this.list[0]).then(() => {
// const index = this.list.findIndex(v => v.id === data[0].id)
// this.list.unshift(tempData)
this.dialogFormVisible = false
this.$notify({
title: 'Success',
message: 'Update Successfully',
type: 'success',
duration: 2000
})
this.$emit('closeClick')
})
} else {
if (this.list.length < 1) {
this.$notify({
title: 'error',
message: '请选择需要新增的属性',
type: 'error',
duration: 2000
})
return false
}
createMallCategoryProperty(this.list).then(() => {
// const index = this.list.findIndex(v => v.id === data[0].id)
// this.list.unshift(tempData)
this.dialogFormVisible = false
this.$notify({
title: 'Success',
message: 'Update Successfully',
type: 'success',
duration: 2000
})
this.$emit('closeClick')
})
}
},
// 关闭
closeClick() {
this.$emit('closeClick')
},
// 查询方法
fetchData() {
fetchForSelect(this.listQuery).then(response => {
this.data = response.model
// Just to simulate the time of the request
setTimeout(() => {
this.listLoading = false
}, 1.5 * 1000)
})
},
// 重置表单
resetForm(formName) {
this.$refs[formName].resetFields()
},
renderContent(h, { node, data, store }) {
if (node.label === undefined) {
return (
<span>
<span>{node.data.propertyName}</span>
</span>)
}
return (
<span>
<span>{node.label}</span>
</span>)
}
}
}
</script>
<style scoped>
</style>
其中有一些你需要特别注意的东西。比如右侧的已经选择的属性列表,是一个动态列表。一般来说我们使用el-table这样的组件来做到它。
关于如何做到勾选左侧展示的属性,到右侧的动态表单中,需要利用之前在el-tree中定义的回调函数。如何获取已选中的数据节点?使用this.$refs.tree.getCheckedNodes()来获取。
页面API的封装
要完成这样一个功能自然离不开调用后端的数据接口,调用后端的api封装我们可以看一下。
export function createMallCategoryProperty(data) {
return request({
url: '/categoryProperty/addMallCategoryPropertyBatch',
method: 'post',
data: data
})
}
export function updateMallCategoryProperty(data) {
return request({
url: '/categoryProperty/updateMallCategoryProperty',
method: 'post',
data: data
})
}
// 新增类目属性获取可以选择的属性
export function fetchForSelect(query) {
return request({
url: '/categoryProperty/findForSelect',
method: 'post',
data: query
})
}
后端数据接口封装
后端的实现较为复杂的上一章节已经讲述过了,现在把剩余的Controller层代码给到你。
/**
* Copyright(c) 2004-2020 pangzi
* com.pz.basic.mall.controller.product.category.MallCategoryPropertyController.java
*/
package com.pz.basic.mall.controller.product.category;
import com.pz.basic.mall.domain.base.Result;
import com.pz.basic.mall.domain.product.category.MallCategoryProperty;
import com.pz.basic.mall.domain.product.category.query.QueryMallCategoryProperty;
import com.pz.basic.mall.domain.product.category.vo.MallCategoryPropertyVo;
import com.pz.basic.mall.domain.product.property.query.QueryMallProperty;
import com.pz.basic.mall.domain.product.property.vo.MallGroupVo;
import com.pz.basic.mall.service.product.category.MallCategoryPropertyService;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
*
* @author pangzi
* @date 2020-06-22 20:47:27
*
*
*/
@RestController
@RequestMapping("/categoryProperty")
public class MallCategoryPropertyController {
private MallCategoryPropertyService mallCategoryPropertyService;
public void setMallCategoryPropertyService(MallCategoryPropertyService MallCategoryPropertyService) {
this.mallCategoryPropertyService = MallCategoryPropertyService;
}
/**
* 批量新增类目属性
* @param mallCategoryPropertyList
* @return
*/
@RequestMapping("/addMallCategoryPropertyBatch")
public Result<List<MallCategoryProperty>> addMallCategoryProperty(@RequestBody List<MallCategoryProperty> mallCategoryPropertyList){
try{
return mallCategoryPropertyService.addMallCategoryPropertyBatch(mallCategoryPropertyList);
}catch(Exception e){
e.printStackTrace();
return new Result(false);
}
}
/**
* 修改类目属性
* @param mallCategoryProperty
* @return
*/
@RequestMapping("/updateMallCategoryProperty")
public Result updateMallCategoryProperty(@RequestBody MallCategoryProperty mallCategoryProperty){
try{
return mallCategoryPropertyService.updateMallCategoryPropertyById(mallCategoryProperty);
}catch(Exception e){
e.printStackTrace();
return new Result(false);
}
}
/**
* 分页返回类目属性列表
* @param queryMallCategoryProperty
* @return
*/
@RequestMapping("/findByPage")
public Result<List<MallCategoryProperty>> findByPage(@RequestBody QueryMallCategoryProperty queryMallCategoryProperty){
return mallCategoryPropertyService.getMallCategoryPropertysByPage(queryMallCategoryProperty);
}
/**
* 删除类目属性
* @param mallCategoryProperty
* @return
*/
@RequestMapping("/deleteMallCategoryProperty")
public Result deleteMallCategoryProperty(@RequestBody MallCategoryProperty mallCategoryProperty){
try{
MallCategoryProperty updateMallCategoryProperty = new MallCategoryProperty();
updateMallCategoryProperty.setCategoryPropertyId(mallCategoryProperty.getCategoryPropertyId());
updateMallCategoryProperty.setStatus(-1);
return mallCategoryPropertyService.updateMallCategoryPropertyById(updateMallCategoryProperty);
}catch(Exception e){
e.printStackTrace();
return new Result(false);
}
}
/**
* 分页返回类目属性列表
* @param queryMallProperty
* @return
*/
@RequestMapping("/findForSelect")
public Result<List<MallGroupVo>> findForSelect(@RequestBody QueryMallProperty queryMallProperty){
return mallCategoryPropertyService.getMallPropertyGroup(queryMallProperty);
}
}
还是那句老话,代码要尽量自己去实现,才能有真正的提高,所以猿人君一直会留下一些小尾巴给你,帮助你提升你的技能。相信你已经能够完成新增属性的前提下,修改属性值的事情,应该也难不倒你,就暂且不讲了噢。