1. 商品分类实现
1.1 环境搭建
1.1.1 表设计
表说明:模块大部分都是单表查询方式
CREATE TABLE `item_cat` (
`id` int NOT NULL AUTO_INCREMENT,
`parent_id` int DEFAULT NULL,
`name` varchar(150) DEFAULT NULL,
`status` int DEFAULT NULL,
`level` int DEFAULT NULL,
`created` datetime DEFAULT NULL,
`updated` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1194 DEFAULT CHARSET=utf8mb3;
1.1.2 项目改造
1.1.2.1 导入jar包
<!--Mybatis Plus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.3</version>
</dependency>
1.1.2.2 编辑ItemCat POJO
package com.jt.pojo;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.List;
@Data
@Accessors(chain = true)
@TableName("item_cat")
public class ItemCat extends BasePojo{
@TableId(type = IdType.AUTO)
private Integer id; //定义主键
private Integer parentId; //定义父级菜单
private String name; //分类名称
private Boolean status; //分类状态 0 停用 1 正常
private Integer level; //商品分类等级 1 2 3
/**
* 属性SQL不负责操作
*/
@TableField(exist = false)
private List<ItemCat> children;
}
1.1.2.3 编辑ItemCat Mapper
package com.jt.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.jt.pojo.ItemCat;
public interface ItemCatMapper extends BaseMapper<ItemCat> {
//CURD操作如果没有特殊需求可以省略
//如果没有sql的需求,则xml映射文件可以简化
}
1.1.2.4 编辑yml配置文件
#SpringBoot整合mybatisPlus
mybatis-plus:
#指定别名包
type-aliases-package: com.jt.pojo
#加载指定的xml映射文件
mapper-locations: classpath:/mybatis/mappers/*.xml
#开启驼峰映射
configuration:
map-underscore-to-camel-case: true
1.1.2.5 层级代码搭建
2. ItemCat 列表实现
- Html
<!-- 2.2定义表格数据-->
<el-table :data="itemCatList" style="width: 100%;margin-bottom: 20px;" row-key="id" border stripe>
<el-table-column type="index" label="序号">
</el-table-column>
<el-table-column prop="name" label="分类名称">
</el-table-column>
<el-table-column prop="status" label="状态">
<!-- 定义作用域插槽 展现数据 scope.row展现行级元素 -->
<template slot-scope="scope">
<el-switch v-model="scope.row.status" active-color="#13ce66" inactive-color="#ff4949"
@change="updateStatus(scope.row)"></el-switch>
</template>
</el-table-column>
<el-table-column prop="level" label="等级">
<!-- 定义作用域插槽 定义标签等级-->
<template slot-scope="scope">
<el-tag effect="dark" v-if="scope.row.level == 1">一级分类</el-tag>
<el-tag effect="dark" type="warning" v-if="scope.row.level == 2">二级分类</el-tag>
<el-tag effect="dark" type="danger" v-if="scope.row.level == 3">三级分类</el-tag>
</template>
</el-table-column>
<el-table-column label="操作">
<!-- 定义作用域插槽 定义标签等级-->
<template slot-scope="scope">
<el-button type="success" icon="el-icon-edit" @click="updateItemCatBtn(scope.row)">编辑</el-button>
<el-button type="danger" icon="el-icon-delete" @click="deleteItemCatBtn(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
- JS
export default {
//定义初始化函数
created() {
//默认获取商品分类列表数据
this.findItemCatList()
},
methods: {
async findItemCatList() {
const {
data: result
} = await this.$http.get("/itemCat/findItemCatList/3")
if (result.status !== 200) return this.$message.error("获取商品分类列表失败!!")
this.itemCatList = result.data
},
}
}
- ItemCatController
/**
* 需求: 查询3级分类数据信息
* URL: /itemCat/findItemCatList/{level}
*/
@GetMapping("/findItemCatList/{level}")
public SysResult findItemCatList(@PathVariable Integer level) {
List<ItemCat> list = itemCatService.findItemCatList(level);
return SysResult.success(list);
}
- ItemCatServiceImpl
@Override
public List<ItemCat> findItemCatList(Integer level) {
long startTime = System.currentTimeMillis();
Map<Integer, List<ItemCat>> map = getMap();
if(level == 1) {
return map.get(0);
}
if(level == 2) {
return getTwoList(map);
}
List<ItemCat> threeList = getThreeList(map);
long endTime = System.currentTimeMillis();
System.out.println((endTime - startTime) + "毫秒");
return threeList;
}
private List<ItemCat> getTwoList(Map<Integer, List<ItemCat>> map) {
List<ItemCat> catList = map.get(0);
for(ItemCat oneItemCat : catList) {
int parentId = oneItemCat.getId();
List<ItemCat> twoList = map.get(parentId);
oneItemCat.setChildren(twoList);
}
return catList;
}
private List<ItemCat> getThreeList(Map<Integer, List<ItemCat>> map) {
List<ItemCat> oneList = getTwoList(map);
for(ItemCat itemCat : oneList){
List<ItemCat> twoList = itemCat.getChildren();
if(twoList == null || twoList.size() == 0) {
continue;
}
for(ItemCat twoItemCat : twoList) {
int parentId = twoItemCat.getId();
List<ItemCat> threeList = map.get(parentId);
twoItemCat.setChildren(threeList);
}
}
return oneList;
}
3. ItemCat 增加
- html
<el-row>
<el-col :span="24">
<el-button type="primary" @click="showAddItemCatDialog">新增分类</el-button>
</el-col>
</el-row>
<!-- 添加新增分类对话框-->
<el-dialog title="新增商品分类" :visible.sync="addItemCatDialogVisible" width="50%" @close="closeAddItemCatDialog">
<!-- 定义分类表单 -->
<el-form :model="itemCatForm" :rules="rules" ref="itemCatFormRef" label-width="100px">
<el-form-item label="分类名称:" prop="name">
<el-input v-model="itemCatForm.name"></el-input>
</el-form-item>
<!-- 定义父级分类选项 -->
<el-form-item label="父级分类名称:">
<!-- 通过级联选择器定义1/2级商品分类
步骤: 1.注意标签导入
2.options 表示数据的来源
3.指定props属性指定数据配置
-->
<el-cascader v-model="selectedKeys" :props="props" :options="parentItemCatList" clearable
@change="parentItemCatChange"></el-cascader>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="addItemCatDialogVisible = false">取 消</el-button>
<el-button type="primary" @click="addItemCatForm">确 定</el-button>
</span>
</el-dialog>
- JS
data(){
return {
//定义商品分类校验规则
rules: {
name: [{
required: true,
message: '请输入分类名称',
trigger: 'blur'
}]
},
}
},
methods:{
showAddItemCatDialog() {
this.findParentItemCatList()
this.addItemCatDialogVisible = true
},
async findParentItemCatList() {
//动态获取商品分类信息 type=2表示获取2级商品分类信息
const {
data: result
} = await this.$http.get("/itemCat/findItemCatList/2")
if (result.status !== 200) return this.$message.error("获取商品分类列表失败!!")
this.parentItemCatList = result.data
},
async addItemCatForm() {
//先将整个表单进行校验
this.$refs.itemCatFormRef.validate(async validate => {
if (!validate) return
const {
data: result
} = await this.$http.post("/itemCat/saveItemCat", this.itemCatForm)
if (result.status !== 200) return this.$message.error("新增商品分类失败")
this.$message.success("新增商品分类成功!!!")
//新增成功,则刷新分类列表信息
this.findItemCatList();
this.addItemCatDialogVisible = false
})
}
}
- ItemCatController
@PostMapping("/saveItemCat")
public SysResult saveItemCat(@RequestBody ItemCat itemCat) {
itemCatService.saveItemCat(itemCat);
return SysResult.success();
}
- ItemCatServiceImpl
//优化:自动生成时间
@Override
@Transactional
public void saveItemCat(ItemCat itemCat) {
// Date date = new Date();
itemCat.setStatus(true);
// .setCreated(date)
// .setUpdated(date);
itemCatMapper.insert(itemCat);
}
- BasePojo
实现created、updated自动生成
//pojo基类,完成2个任务,2个日期,实现序列化
@Data
@Accessors(chain=true)
public class BasePojo implements Serializable{
//自动填充
@TableField(fill = FieldFill.INSERT)
private Date created; //表示入库时需要赋值
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updated; //表示入库/更新时赋值.
}
- ItemCat
@Data
@Accessors(chain = true)
@TableName("item_cat")
public class ItemCat extends BasePojo{
@TableId(type = IdType.AUTO)
private Integer id; //定义主键
private Integer parentId; //定义父级菜单
private String name; //分类名称
private Boolean status; //分类状态 0 停用 1 正常
private Integer level; //商品分类等级 1 2 3
/**
* 属性SQL不负责操作
*/
@TableField(exist = false)
private List<ItemCat> children;
}
4. ItemCat修改
- html
<el-table-column label="操作">
<!-- 定义作用域插槽 定义标签等级-->
<template slot-scope="scope">
<el-button type="success" icon="el-icon-edit" @click="updateItemCatBtn(scope.row)">编辑</el-button>
<el-button type="danger" icon="el-icon-delete" @click="deleteItemCatBtn(scope.row)">删除</el-button>
</template>
</el-table-column>
<!-- 添加修改分类对话框 -->
<el-dialog title="修改商品分类" :visible.sync="updateItemCatDialogVisible" width="50%">
<!-- 定义分类表单 -->
<el-form :model="updateItemCatForm" :rules="rules" ref="upDateItemCatForm" label-width="100px">
<el-form-item label="分类名称:" prop="name">
<el-input v-model="updateItemCatForm.name"></el-input>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="updateItemCatDialogVisible = false">取 消</el-button>
<el-button type="primary" @click="updateItemCat">确 定</el-button>
</span>
</el-dialog>
- js
updateItemCatBtn(itemCat) {
this.updateItemCatForm = itemCat
this.updateItemCatDialogVisible = true
},
async updateItemCat() {
//修改商品分类信息
const {
data: result
} = await this.$http.put('/itemCat/updateItemCat', this.updateItemCatForm)
if (result.status !== 200) return this.$message.error("更新商品分类失败")
this.$message.success("更新商品分类成功")
this.findItemCatList();
this.updateItemCatDialogVisible = false;
},
- Controller
@PutMapping("/updateItemCat")
public SysResult updateItemCat(@RequestBody ItemCat itemCat) {
System.out.println(itemCat);
itemCatService.updateItemCat(itemCat);
return SysResult.success();
}
- ItemCatServiceImpl
@Override
@Transactional
public void updateItemCat(ItemCat itemCat) {
ItemCat temp = new ItemCat();
temp.setId(itemCat.getId())
.setName(itemCat.getName());
itemCatMapper.updateById(temp);
}
5. 商品管理模块
前端
<!-- 定义卡片 -->
<template>
<div>
<!-- 定义面包屑导航 -->
<el-breadcrumb separator-class="el-icon-arrow-right">
<el-breadcrumb-item :to="{ path: '/home' }">首页</el-breadcrumb-item>
<el-breadcrumb-item>商品管理</el-breadcrumb-item>
<el-breadcrumb-item>商品列表</el-breadcrumb-item>
</el-breadcrumb>
<!-- 定义卡片 -->
<el-card class="box-card">
<!-- 定义第一行数据 -->
<el-row :gutter="20">
<el-col :span="8">
<el-input placeholder="请输入内容" v-model="queryItemInfo.query" clearable @clear="getItemList">
<el-button slot="append" icon="el-icon-search" @click="getItemList"></el-button>
</el-input>
</el-col>
<el-col :span="4">
<el-button type="primary" @click="toAddItem">添加商品</el-button>
</el-col>
</el-row>
<!-- 获取商品列表数据 -->
<el-table :data="itemList" style="width: 100%" stripe border>
<el-table-column type="index" label="序号" width="50px">
</el-table-column>
<el-table-column prop="title" label="商品标题" width="400px">
</el-table-column>
<el-table-column prop="sellPoint" label="卖点信息" width="300px">
</el-table-column>
<!-- 使用过滤器修改时间 在main.js中定义过滤器 -->
<el-table-column prop="price" label="价格(元)" width="80px">
<template slot-scope="scope">
{{scope.row.price | priceFormat}}
</template>
</el-table-column>
<el-table-column prop="num" label="数量" width="80px">
</el-table-column>
<el-table-column prop="status" label="状态" width="80px">
<template slot-scope="scope">
<el-switch v-model="scope.row.status" active-color="#13ce66" inactive-color="#ff4949"
@change="updateStatus(scope.row)"></el-switch>
</template>
</el-table-column>
<!-- <el-table-column prop="updated" label="更新时间" width="180px" :formatter="formatDate">
</el-table-column> -->
<el-table-column label="操作">
<template slot-scope="scope">
<el-button type="primary" icon="el-icon-edit" size="mini" @click="updateItemBtn(scope.row)">修改</el-button>
<el-button type="danger" icon="el-icon-delete" size="mini" @click="deleteItemBtn(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 定义分页插件-->
<el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange"
:current-page="queryItemInfo.pageNum" :page-sizes="[1, 5, 10, 20]" :page-size="queryItemInfo.pageSize"
layout="total, sizes, prev, pager, next, jumper" :total="total">
</el-pagination>
</el-card>
</div>
</template>
<script>
export default {
data() {
return {
//1.定义商品列表信息
itemList: [],
//2.定义分页对象
queryItemInfo: {
query: '', //定义查询参数
pageNum: 1,
pageSize: 10
},
total: 0
}
},
created() {
//1.获取商品列表数据
this.getItemList()
},
methods: {
//实现商品信息分页查询
async getItemList() {
const {
data: result
} = await this.$http.get("/item/getItemList", {
params: this.queryItemInfo
})
if (result.status !== 200) return this.$message.error("商品列表查询失败")
this.itemList = result.data.rows
this.total = result.data.total
},
//通过JS格式化时间
formatDate(row, column, cellValue, index) {
let date = new Date(cellValue)
let year = date.getFullYear()
let month = (date.getMonth() + 1 + '').padStart(2, '0')
let day = (date.getDate() + '').padStart(2, '0')
let HH = (date.getHours() + '').padStart(2, '0')
let MM = (date.getMinutes() + '').padStart(2, '0')
let SS = (date.getSeconds() + '').padStart(2, '0')
return year + '-' + month + '-' + day + ' ' + HH + ":" + MM + ":" + SS
},
//条数变化时 调用
handleSizeChange(size) {
this.queryItemInfo.pageSize = size
this.getItemList()
},
//页码值变化时 调用
handleCurrentChange(current) {
this.queryItemInfo.pageNum = current
this.getItemList()
},
async updateStatus(item) {
const {
data: result
} = await this.$http.put("/item/updateItemStatus", {
id: item.id,
status: item.status
})
if (result.status !== 200) return this.$message.error("更新状态失败")
this.$message.success("更新状态成功")
},
async deleteItemBtn(item) {
//消息确认框
this.$confirm('此操作将永久删除该文件, 是否继续?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(async () => {
//根据id删除数据
const {
data: result
} = await this.$http.delete("/item/deleteItemById", {
params: {
id: item.id
}
})
if (result.status !== 200) return this.$message.error("商品删除失败")
this.$message.success("商品删除成功")
//重新获取商品列表信息
this.getItemList()
}).catch(() => {
this.$message({
type: 'info',
message: '已取消删除'
});
});
},
//转向到商品新增页面
toAddItem(){
this.$router.push("/item/addItem")
},
updateItemBtn(row){
// console.log(row)
this.$router.push(`/item/editItem/${row.id}`)
}
}
}
</script>
<!-- 防止组件样式冲突 -->
<style lang="less" scoped>
</style>
5.1 商品列表
- pojo/Item
package com.jt.pojo;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.experimental.Accessors;
@Data
@Accessors(chain = true)
@TableName("item")
public class Item extends BasePojo{
@TableId(type = IdType.AUTO)
private Integer id; //商品Id号
private String title; //商品标题信息
private String sellPoint; //卖点信息
private Integer price; //商品价格
private Integer num; //商品数量
private String images; //商品图片
private Integer itemCatId; //商品分类ID号
private Boolean status; //状态信息 0 下架 1 上架
}
- vo/PageResult
package com.jt.vo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
@Data
@Accessors(chain = true)
@NoArgsConstructor
@AllArgsConstructor
public class PageResult {
private String query; //查询数据
private Integer pageNum; //页数
private Integer pageSize; //条数
private Long total; //总数
private Object rows; //查询结果
}
- service/ItemServiceImpl.java
@Override
public PageResult getItemList(PageResult pageResult) {
//判断用户的数据是否有值
boolean flag = StringUtils.hasLength(pageResult.getQuery());
QueryWrapper<Item> queryWrapper = new QueryWrapper<>();
queryWrapper.like(flag,"title", pageResult.getQuery());
IPage<Item> page = new Page<>(pageResult.getPageNum(), pageResult.getPageSize());
page = itemMapper.selectPage(page, queryWrapper);
//获取总数
long total = page.getTotal();
//获取记录数
List<Item> rows = page.getRecords();
return pageResult.setTotal(total).setRows(rows);
}
- Controller
/**
* url: /item/getItemList?query=&pageNum=1&pageSize=10
* 参数: pageResult
* 返回值:SysResult(pageResult)
*/
@GetMapping("/getItemList")
public SysResult getItemList(PageResult pageResult){
pageResult = itemService.getItemList(pageResult);
System.out.println(pageResult);
return SysResult.success(pageResult);
}
5.2 修改商品状态
- Controller
@PutMapping("/updateItemStatus")
public SysResult updateItemStatus(@RequestBody Item item) {
itemService.statusItem(item);
return SysResult.success();
}
- ServiceImpl
@Override
public void statusItem(Item item) {
itemMapper.updateById(item);
}
5.3 删除商品
- Controller
/**
* URL:/item/deleteItemById?id=13
*/
@DeleteMapping("/deleteItemById")
public SysResult deleteItemById(Item item) {
itemService.deleteItem(item);
return SysResult.success();
}
- ServiceImpl
@Override
public void deleteItem(Item item) {
itemMapper.deleteById(item);
}
5.4 商品新增
<template>
<div>
<!-- 定义面包屑导航 -->
<el-breadcrumb separator-class="el-icon-arrow-right">
<el-breadcrumb-item :to="{ path: '/home' }">首页</el-breadcrumb-item>
<el-breadcrumb-item>商品管理</el-breadcrumb-item>
<el-breadcrumb-item>商品新增</el-breadcrumb-item>
</el-breadcrumb>
<!-- 定义卡片视图 -->
<el-card class="box-card">
<!-- 定义页面提示信息 -->
<el-alert title="新增商品流程" type="info" effect="dark" center show-icon :closable="false"></el-alert>
<!-- 定义步骤条 字符串需要转化为数值 -->
<el-steps :active="activeIndex - 0" finish-status="success" align-center>
<el-step title="基本信息"></el-step>
<el-step title="商品图片"></el-step>
<el-step title="商品内容"></el-step>
<el-step title="完成"></el-step>
</el-steps>
<!-- 定义标签页 before-leave:切换标签之前的钩子,若返回 false 或者返回 Promise 且被 reject,则阻止切换。 -->
<el-form :model="addItemForm" :rules="addItemFormRules" ref="addItemFormRef" label-width="100px"
label-position="top">
<el-tabs v-model="activeIndex" :tab-position="'left'" :before-leave="beforeLeave">
<el-tab-pane label="基本信息" name="0">
<el-form-item label="商品标题" prop="title">
<el-input v-model="addItemForm.title"></el-input>
</el-form-item>
<el-form-item label="商品卖点" prop="sellPoint">
<el-input v-model="addItemForm.sellPoint"></el-input>
</el-form-item>
<el-form-item label="商品价格" prop="price">
<el-input v-model="addItemForm.price" type="number"></el-input>
</el-form-item>
<el-form-item label="商品数量" prop="num">
<el-input v-model="addItemForm.num" type="number"></el-input>
</el-form-item>
<el-form-item label="商品分类信息" prop="price">
<!-- 通过级联获取商品分类信息-->
<el-cascader v-model="itemCatIds" :options="itemCatList" :props="props" @change="changeCascader">
</el-cascader>
</el-form-item>
</el-tab-pane>
<!-- 实现图片上传 multiple支持多选文件 -->
<el-tab-pane label="商品图片" name="1">
<el-upload class="upload-demo" :action="uploadUrl" :on-preview="handlePreview" :on-remove="handleRemove"
:on-success="handleSuccess" list-type="picture" multiple drag>
<el-button size="small" type="primary">点击上传</el-button>
<div slot="tip" class="el-upload__tip">只能上传jpg/png文件,且不超过500kb</div>
</el-upload>
</el-tab-pane>
<el-tab-pane label="商品详情" name="2">
<!-- 定义富文本编辑器-->
<quill-editor ref="myQuillEditor" v-model="itemDesc.itemDesc">
</quill-editor>
<!-- 定义添加商品按钮-->
<el-button type="primary" class="addItemBtnClass" @click="addItemBtn">添加商品</el-button>
</el-tab-pane>
</el-tabs>
</el-form>
</el-card>
<!-- 定义图片展现对话框 -->
<el-dialog title="图片预览" :visible.sync="dialogImageVisible">
<img :src="imageUrlPath" width="100%" height="100%" />
</el-dialog>
</div>
</template>
<script>
export default {
data() {
return {
activeIndex: '0',
addItemForm: {
title: '',
sellPoint: '',
price: 0,
num: 0,
itemCatId: '',
images: []
//itemDesc: '',
//dynamicArgs: [],
//staticArgs: []
},
itemDesc: {
itemDesc: ""
},
itemParam: {
dynamicArray: [],
staticArray: [],
dynamicArgs: "",
staticArgs: ""
}
,
addItemFormRules: {
title: [{
required: true,
message: '请输入商品标题信息',
trigger: 'blur'
}],
sellPoint: [{
required: true,
message: '请输入商品卖点信息',
trigger: 'blur'
}],
price: [{
required: true,
message: '请输入商品价格信息',
trigger: 'blur'
}],
num: [{
required: true,
message: '请输入商品数量信息',
trigger: 'blur'
}],
},
itemCatIds: [],
itemCatList: [],
props: {
expandTrigger: 'hover',
value: "id", //选中数据的value值
label: "name", //选中数据展现名称
children: "children", //自选项数据
},
//设置商品动态参数
dynamicTableData: [],
staticTableData: [],
//定义文件上传路径地址
uploadUrl: "http://manage.jt.com/file/upload",
// uploadUrl: "http://localhost:8091/file/upload",
// uploadUrl: "http://localhost:8091/file/uploadt2",
//uploadUrl: "http://manage.harrylyj.com/file/upload",
//定义图片网络访问地址
imageUrlPath: "",
//定义图片控制开关
dialogImageVisible: false
}
},
created() {
this.findItemCatList()
},
methods: {
//1.获取所有商品分类信息
async findItemCatList() {
const {
data: result } = await this.$http.get("/itemCat/findItemCatList/3")
if (result.status !== 200) return this.$message.error("查询商品分类信息失败")
this.itemCatList = result.data
},
//修改商品分类选项
changeCascader() {
this.addItemForm.itemCatId = this.itemCatIds[2]
//console.log(this.addItemForm.itemCatId)
},
//当标签页数据没有完成选择时 不让切换标签页
beforeLeave(activeName, oldActiveName) {
//console.log(this.itemCatIds.length)
//console.log(typeof(oldActiveName))
//注意oldActiveName的数据类型为字符串
if (this.itemCatIds.length !== 3 && oldActiveName === '0') {
this.$message.error("请先选择商品分类")
return false
}
},
//预览图片的方法
handlePreview(file) {
//获取图片的虚拟路径
this.imageUrlPath = file.response.data.urlPath
this.dialogImageVisible = true
},
//移除图片的方法
async handleRemove(file) {
//移除数组中的数据
let virtualPath = file.response.data.virtualPath
//通过findIndex函数 获取数组中指定数据的位置
let index = this.addItemForm.images.findIndex(x => x === virtualPath)
//删除数组中指定的数据
this.addItemForm.images.splice(index, 1)
//删除服务中的文件
let {
data: result
} = await this.$http.delete("/file/deleteFile", {
params: {
virtualPath: virtualPath
}
})
if (result.status !== 200) return this.$message.error("删除图片失败")
this.$message.success("删除图片成功")
},
//如果文件上传成功之后调用
handleSuccess(response, file) {
if (response.status !== 200) return this.$message.error("文件上传失败")
file.name = response.data.fileName
//获取虚拟路径
let virtualPath = response.data.virtualPath
//将数据封装到Form表单中
this.addItemForm.images.push(virtualPath)
},
/* 添加商品按钮 */
async addItemBtn(){
//console.log(this.addItemForm)
//1.完成表单校验
this.$refs.addItemFormRef.validate( valid => {
if(!valid) return this.$message.error("请输入商品必填项")
})
//2.完成商品参数的封装
//2.0 将商品价格扩大100倍
this.addItemForm.price = this.addItemForm.price * 100
//2.1 将商品图片的数据转化为字符串
this.addItemForm.images = this.addItemForm.images.join(",")
//2.5 实现商品数据提交
let submitAddItem = {
item : this.addItemForm,
itemDesc: this.itemDesc
}
console.log(submitAddItem)
let {data: result} = await this.$http.post("/item/saveItem",submitAddItem)
if(result.status !== 200) return this.$message.error("商品添加失败")
this.$message.success("商品添加成功")
//2.5添加完成之后,将数据重定向到商品展现页面
this.$router.push("/item")
}
}
}
</script>
<style lang="less" scoped>
.el-steps {
margin: 20px 0;
}
.el-cascader {
width: 30%;
}
/* 控制复选框的右边距5像素 !important 表示优先执行 IE6不兼容*/
.el-checkbox {
margin: 0 10px 0 0 !important;
}
.addItemBtnClass{
margin-top: 15px;
}
</style>
- Controller
/**
* 需求:实现商品新增
* URL:http://localhost:8091/item/saveItem
* 参数:{item: this.addItemForm, itemDesc: this.itemDesc}
* 接收数据: ItemVO
* 返回值:SysResult对象
*/
@PostMapping("/saveItem")
public SysResult saveItem(@RequestBody ItemVO itemVO) {
itemService.saveItem(itemVO);
return SysResult.success();
}
- ItemVO
package com.jt.vo;
import com.jt.pojo.Item;
import com.jt.pojo.ItemDesc;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
@Data
@Accessors(chain = true)
@NoArgsConstructor
@AllArgsConstructor
public class ItemVO { //该对象封装商品所有的参数信息
private Item item;
private ItemDesc itemDesc;
}
- Item pojo
package com.jt.pojo;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.experimental.Accessors;
@Data
@Accessors(chain = true)
@TableName("item")
public class Item extends BasePojo{
@TableId(type = IdType.AUTO)
private Integer id; //商品Id号
private String title; //商品标题信息
private String sellPoint; //卖点信息
private Integer price; //商品价格
private Integer num; //商品数量
private String images; //商品图片
private Integer itemCatId; //商品分类ID号
private Boolean status; //状态信息 0 下架 1 上架
}
- ItemServiceImpl
@Override
@Transactional
public void saveItem(ItemVO itemVO) {
Item item = itemVO.getItem();
item.setStatus(true);
//MP数据入库之后,数据自动回显,可以获取新增ID
itemMapper.insert(item);
ItemDesc itemDesc = itemVO.getItemDesc();
itemDesc.setId(item.getId());
itemDescMapper.insert(itemDesc);
}
5.5 商品图片上传
- Controller
@PostMapping("/upload")
public SysResult upload2(MultipartFile file) {
ImageVO imageVO = fileService.upload(file);
return SysResult.success(imageVO);
}
@DeleteMapping("/deleteFile")
public SysResult deleteFile(String virtualPath) {
fileService.deleteFile(virtualPath);
return SysResult.success();
}
- FileServiceImpl
package com.jt.service;
import com.jt.vo.ImageVO;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.UUID;
@Service
public class FileServiceImpl implements FileService {
// private String localDir = "D:/w/tmp/jt";
private String localDir = "/opt/web/images";
private String urlPath = "http://image.jt.com";
/**
* 1. 校验图片类型 xx.jpg
* 2. 校验是否为恶意程序 xx.exe.jpg
* 3. 将文件分目录存储
* 4. 为了保证图片唯一性,自定义文件名称
*
* @param file
* @return
*/
@Override
public ImageVO upload(MultipartFile file) {
String fileName = file.getOriginalFilename().toLowerCase();
if(!fileName.matches("^.+\\.(jpg|png|gif)$")){
return null;
}
//图片类型正确
try {
BufferedImage bufferedImage = ImageIO.read(file.getInputStream());
int width = bufferedImage.getWidth();
int height = bufferedImage.getHeight();
if(width == 0 || height == 0) {
return null;
}
String dateDir = new SimpleDateFormat("/yyyy/MM/dd/").format(new Date());
// E:/images + /2022/11/11/
String dirPath = localDir + dateDir;
File dirFile = new File(dirPath);
if(!dirFile.exists()) {
dirFile.mkdirs();
}
System.out.println("用户上传的是图片");
String uuid = UUID.randomUUID()
.toString().replace("-", "");
int index = fileName.lastIndexOf(".");
String fileType = fileName.substring(index);
String newFile = uuid + fileType;
String path = dirPath + newFile;
file.transferTo(new File(path));
String virtualPath = dateDir + newFile;
String fileNameVO = newFile;
String url = urlPath + virtualPath;
System.out.println(url);
return new ImageVO(virtualPath, url, fileNameVO);
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
@Override
public void deleteFile(String virtualPath) {
String path = localDir + virtualPath;
File file = new File(path);
if(file.exists()) {
file.delete();
}
}
}
- ImageVO
package com.jt.vo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
@Data
@Accessors(chain = true)
@NoArgsConstructor
@AllArgsConstructor
public class ImageVO {
private String virtualPath;
private String urlPath;
private String fileName;
}