Commit d3edaec2b1d7c54bc5e03450431fb68983ec6368

Authored by JeromeSoar
1 parent 4e67b6bb

商品分类维护

sql/mall.sql
@@ -26,10 +26,10 @@ CREATE TABLE `product_category` @@ -26,10 +26,10 @@ CREATE TABLE `product_category`
26 `id` bigint NOT NULL AUTO_INCREMENT COMMENT '分类编号', 26 `id` bigint NOT NULL AUTO_INCREMENT COMMENT '分类编号',
27 `pid` bigint NOT NULL COMMENT '父分类编号', 27 `pid` bigint NOT NULL COMMENT '父分类编号',
28 `name` varchar(255) NOT NULL COMMENT '分类名称', 28 `name` varchar(255) NOT NULL COMMENT '分类名称',
29 - `icon` varchar(100) DEFAULT '#' COMMENT '分类图标', 29 + `icon` varchar(100) NOT NULL DEFAULT '#' COMMENT '分类图标',
30 `banner_url` varchar(255) NOT NULL COMMENT '分类图片', 30 `banner_url` varchar(255) NOT NULL COMMENT '分类图片',
31 - `sort` int NOT NULL DEFAULT '0' COMMENT '分类排序',  
32 - `description` varchar(1024) NOT NULL COMMENT '分类描述', 31 + `sort` int DEFAULT '0' COMMENT '分类排序',
  32 + `description` varchar(1024) DEFAULT NULL COMMENT '分类描述',
33 `status` tinyint NOT NULL COMMENT '开启状态', 33 `status` tinyint NOT NULL COMMENT '开启状态',
34 `creator` varchar(64) DEFAULT '' COMMENT '创建者', 34 `creator` varchar(64) DEFAULT '' COMMENT '创建者',
35 `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', 35 `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/category/CategoryController.java
1 package cn.iocoder.yudao.module.product.controller.admin.category; 1 package cn.iocoder.yudao.module.product.controller.admin.category;
2 2
3 -import org.springframework.web.bind.annotation.*;  
4 -import javax.annotation.Resource;  
5 -import org.springframework.validation.annotation.Validated;  
6 -import org.springframework.security.access.prepost.PreAuthorize;  
7 -import io.swagger.annotations.*;  
8 -  
9 -import javax.validation.constraints.*;  
10 -import javax.validation.*;  
11 -import javax.servlet.http.*;  
12 -import java.util.*;  
13 -import java.io.IOException;  
14 -  
15 -import cn.iocoder.yudao.framework.common.pojo.PageResult;  
16 import cn.iocoder.yudao.framework.common.pojo.CommonResult; 3 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
17 -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;  
18 - 4 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
19 import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; 5 import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
20 -  
21 import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; 6 import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
22 -import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.*;  
23 -  
24 import cn.iocoder.yudao.module.product.controller.admin.category.vo.*; 7 import cn.iocoder.yudao.module.product.controller.admin.category.vo.*;
25 -import cn.iocoder.yudao.module.product.dal.dataobject.category.CategoryDO;  
26 import cn.iocoder.yudao.module.product.convert.category.CategoryConvert; 8 import cn.iocoder.yudao.module.product.convert.category.CategoryConvert;
  9 +import cn.iocoder.yudao.module.product.dal.dataobject.category.CategoryDO;
27 import cn.iocoder.yudao.module.product.service.category.CategoryService; 10 import cn.iocoder.yudao.module.product.service.category.CategoryService;
  11 +import io.swagger.annotations.Api;
  12 +import io.swagger.annotations.ApiImplicitParam;
  13 +import io.swagger.annotations.ApiOperation;
  14 +import org.springframework.security.access.prepost.PreAuthorize;
  15 +import org.springframework.validation.annotation.Validated;
  16 +import org.springframework.web.bind.annotation.*;
  17 +
  18 +import javax.annotation.Resource;
  19 +import javax.servlet.http.HttpServletResponse;
  20 +import javax.validation.Valid;
  21 +import java.io.IOException;
  22 +import java.util.Collection;
  23 +import java.util.List;
  24 +
  25 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  26 +import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
28 27
29 @Api(tags = "管理后台 - 商品分类") 28 @Api(tags = "管理后台 - 商品分类")
30 @RestController 29 @RestController
@@ -68,15 +67,22 @@ public class CategoryController { @@ -68,15 +67,22 @@ public class CategoryController {
68 return success(CategoryConvert.INSTANCE.convert(category)); 67 return success(CategoryConvert.INSTANCE.convert(category));
69 } 68 }
70 69
71 - @GetMapping("/list") 70 + @GetMapping("/listByIds")
72 @ApiOperation("获得商品分类列表") 71 @ApiOperation("获得商品分类列表")
73 - @ApiImplicitParam(name = "ids", value = "编号列表", required = true, example = "1024,2048", dataTypeClass = List.class)  
74 @PreAuthorize("@ss.hasPermission('product:category:query')") 72 @PreAuthorize("@ss.hasPermission('product:category:query')")
75 public CommonResult<List<CategoryRespVO>> getCategoryList(@RequestParam("ids") Collection<Long> ids) { 73 public CommonResult<List<CategoryRespVO>> getCategoryList(@RequestParam("ids") Collection<Long> ids) {
76 List<CategoryDO> list = categoryService.getCategoryList(ids); 74 List<CategoryDO> list = categoryService.getCategoryList(ids);
77 return success(CategoryConvert.INSTANCE.convertList(list)); 75 return success(CategoryConvert.INSTANCE.convertList(list));
78 } 76 }
79 77
  78 + @GetMapping("/listByQuery")
  79 + @ApiOperation("获得商品分类列表")
  80 + @PreAuthorize("@ss.hasPermission('product:category:query')")
  81 + public CommonResult<List<CategoryRespVO>> listByQuery() {
  82 + List<CategoryDO> list = categoryService.listByQuery();
  83 + return success(CategoryConvert.INSTANCE.convertList(list));
  84 + }
  85 +
80 @GetMapping("/page") 86 @GetMapping("/page")
81 @ApiOperation("获得商品分类分页") 87 @ApiOperation("获得商品分类分页")
82 @PreAuthorize("@ss.hasPermission('product:category:query')") 88 @PreAuthorize("@ss.hasPermission('product:category:query')")
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/category/vo/CategoryBaseVO.java
1 package cn.iocoder.yudao.module.product.controller.admin.category.vo; 1 package cn.iocoder.yudao.module.product.controller.admin.category.vo;
2 2
3 -import lombok.*;  
4 -import java.util.*;  
5 -import io.swagger.annotations.*;  
6 -import javax.validation.constraints.*; 3 +import io.swagger.annotations.ApiModelProperty;
  4 +import lombok.Data;
  5 +
  6 +import javax.validation.constraints.NotBlank;
  7 +import javax.validation.constraints.NotNull;
7 8
8 /** 9 /**
9 * 商品分类 Base VO,提供给添加、修改、详细的子 VO 使用 10 * 商品分类 Base VO,提供给添加、修改、详细的子 VO 使用
@@ -17,22 +18,21 @@ public class CategoryBaseVO { @@ -17,22 +18,21 @@ public class CategoryBaseVO {
17 private Long pid; 18 private Long pid;
18 19
19 @ApiModelProperty(value = "分类名称", required = true, example = "办公文具") 20 @ApiModelProperty(value = "分类名称", required = true, example = "办公文具")
20 - @NotNull(message = "分类名称不能为空") 21 + @NotBlank(message = "分类名称不能为空")
21 private String name; 22 private String name;
22 23
23 @ApiModelProperty(value = "分类图标") 24 @ApiModelProperty(value = "分类图标")
  25 + @NotBlank(message = "分类图标不能为空")
24 private String icon; 26 private String icon;
25 27
26 @ApiModelProperty(value = "分类图片", required = true) 28 @ApiModelProperty(value = "分类图片", required = true)
27 - @NotNull(message = "分类图片不能为空") 29 + @NotBlank(message = "分类图片不能为空")
28 private String bannerUrl; 30 private String bannerUrl;
29 31
30 @ApiModelProperty(value = "分类排序", required = true, example = "1") 32 @ApiModelProperty(value = "分类排序", required = true, example = "1")
31 - @NotNull(message = "分类排序不能为空")  
32 private Integer sort; 33 private Integer sort;
33 34
34 @ApiModelProperty(value = "分类描述", required = true, example = "描述") 35 @ApiModelProperty(value = "分类描述", required = true, example = "描述")
35 - @NotNull(message = "分类描述不能为空")  
36 private String description; 36 private String description;
37 37
38 @ApiModelProperty(value = "开启状态", required = true, example = "0") 38 @ApiModelProperty(value = "开启状态", required = true, example = "0")
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/category/CategoryService.java
1 package cn.iocoder.yudao.module.product.service.category; 1 package cn.iocoder.yudao.module.product.service.category;
2 2
3 -import java.util.*;  
4 -import javax.validation.*;  
5 -import cn.iocoder.yudao.module.product.controller.admin.category.vo.*;  
6 -import cn.iocoder.yudao.module.product.dal.dataobject.category.CategoryDO;  
7 import cn.iocoder.yudao.framework.common.pojo.PageResult; 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.module.product.controller.admin.category.vo.CategoryCreateReqVO;
  5 +import cn.iocoder.yudao.module.product.controller.admin.category.vo.CategoryExportReqVO;
  6 +import cn.iocoder.yudao.module.product.controller.admin.category.vo.CategoryPageReqVO;
  7 +import cn.iocoder.yudao.module.product.controller.admin.category.vo.CategoryUpdateReqVO;
  8 +import cn.iocoder.yudao.module.product.dal.dataobject.category.CategoryDO;
  9 +
  10 +import javax.validation.Valid;
  11 +import java.util.Collection;
  12 +import java.util.List;
8 13
9 /** 14 /**
10 * 商品分类 Service 接口 15 * 商品分类 Service 接口
@@ -67,4 +72,10 @@ public interface CategoryService { @@ -67,4 +72,10 @@ public interface CategoryService {
67 */ 72 */
68 List<CategoryDO> getCategoryList(CategoryExportReqVO exportReqVO); 73 List<CategoryDO> getCategoryList(CategoryExportReqVO exportReqVO);
69 74
  75 + /**
  76 + * 获得商品分类列表
  77 + *
  78 + * @return 商品分类列表
  79 + */
  80 + List<CategoryDO> listByQuery();
70 } 81 }
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/category/CategoryServiceImpl.java
1 package cn.iocoder.yudao.module.product.service.category; 1 package cn.iocoder.yudao.module.product.service.category;
2 2
3 -import org.springframework.stereotype.Service;  
4 -import javax.annotation.Resource;  
5 -import org.springframework.validation.annotation.Validated;  
6 -  
7 -import java.util.*;  
8 -import cn.iocoder.yudao.module.product.controller.admin.category.vo.*;  
9 -import cn.iocoder.yudao.module.product.dal.dataobject.category.CategoryDO;  
10 import cn.iocoder.yudao.framework.common.pojo.PageResult; 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
11 - 4 +import cn.iocoder.yudao.module.product.controller.admin.category.vo.CategoryCreateReqVO;
  5 +import cn.iocoder.yudao.module.product.controller.admin.category.vo.CategoryExportReqVO;
  6 +import cn.iocoder.yudao.module.product.controller.admin.category.vo.CategoryPageReqVO;
  7 +import cn.iocoder.yudao.module.product.controller.admin.category.vo.CategoryUpdateReqVO;
12 import cn.iocoder.yudao.module.product.convert.category.CategoryConvert; 8 import cn.iocoder.yudao.module.product.convert.category.CategoryConvert;
  9 +import cn.iocoder.yudao.module.product.dal.dataobject.category.CategoryDO;
13 import cn.iocoder.yudao.module.product.dal.mysql.category.CategoryMapper; 10 import cn.iocoder.yudao.module.product.dal.mysql.category.CategoryMapper;
  11 +import org.springframework.stereotype.Service;
  12 +import org.springframework.validation.annotation.Validated;
  13 +
  14 +import javax.annotation.Resource;
  15 +import java.util.Collection;
  16 +import java.util.List;
14 17
15 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 18 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
16 -import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.*; 19 +import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.CATEGORY_NOT_EXISTS;
17 20
18 /** 21 /**
19 * 商品分类 Service 实现类 22 * 商品分类 Service 实现类
@@ -79,4 +82,9 @@ public class CategoryServiceImpl implements CategoryService { @@ -79,4 +82,9 @@ public class CategoryServiceImpl implements CategoryService {
79 return categoryMapper.selectList(exportReqVO); 82 return categoryMapper.selectList(exportReqVO);
80 } 83 }
81 84
  85 + @Override
  86 + public List<CategoryDO> listByQuery() {
  87 + return categoryMapper.selectList();
  88 + }
  89 +
82 } 90 }
yudao-ui-admin/src/api/mall/product/category.js
@@ -34,6 +34,15 @@ export function getCategory(id) { @@ -34,6 +34,15 @@ export function getCategory(id) {
34 }) 34 })
35 } 35 }
36 36
  37 +// 获得商品分类
  38 +export function listCategory(query) {
  39 + return request({
  40 + url: '/product/category/listByQuery',
  41 + method: 'get',
  42 + params: query
  43 + })
  44 +}
  45 +
37 // 获得商品分类分页 46 // 获得商品分类分页
38 export function getCategoryPage(query) { 47 export function getCategoryPage(query) {
39 return request({ 48 return request({
yudao-ui-admin/src/views/mall/product/category/index.vue
@@ -40,13 +40,18 @@ @@ -40,13 +40,18 @@
40 40
41 <!-- 列表 --> 41 <!-- 列表 -->
42 <el-table v-loading="loading" :data="list"> 42 <el-table v-loading="loading" :data="list">
43 - <el-table-column label="分类编号" align="center" prop="id"/>  
44 - <el-table-column label="父分类编号" align="center" prop="pid"/>  
45 <el-table-column label="分类名称" align="center" prop="name"/> 43 <el-table-column label="分类名称" align="center" prop="name"/>
46 - <el-table-column label="分类图标" align="center" prop="icon"/>  
47 - <el-table-column label="分类图片" align="center" prop="bannerUrl"/> 44 + <el-table-column label="分类图标" align="center" prop="icon">
  45 + <template slot-scope="scope">
  46 + <svg-icon :icon-class="scope.row.icon" />
  47 + </template>
  48 + </el-table-column>
  49 + <el-table-column label="分类图片" align="center" prop="bannerUrl">
  50 + <template slot-scope="scope">
  51 + <img v-if="scope.row.bannerUrl" :src="scope.row.bannerUrl" alt="分类图片"/>
  52 + </template>
  53 + </el-table-column>
48 <el-table-column label="分类排序" align="center" prop="sort"/> 54 <el-table-column label="分类排序" align="center" prop="sort"/>
49 - <el-table-column label="分类描述" align="center" prop="description"/>  
50 <el-table-column label="开启状态" align="center" prop="status"> 55 <el-table-column label="开启状态" align="center" prop="status">
51 <template slot-scope="scope"> 56 <template slot-scope="scope">
52 <dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status"/> 57 <dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status"/>
@@ -75,20 +80,28 @@ @@ -75,20 +80,28 @@
75 <!-- 对话框(添加 / 修改) --> 80 <!-- 对话框(添加 / 修改) -->
76 <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body> 81 <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
77 <el-form ref="form" :model="form" :rules="rules" label-width="80px"> 82 <el-form ref="form" :model="form" :rules="rules" label-width="80px">
78 - <el-form-item label="父分类编号" prop="pid">  
79 - <el-input v-model="form.pid" placeholder="请输入父分类编号"/> 83 + <el-form-item label="上级分类" prop="pid">
  84 + <Treeselect v-model="form.pid" :options="parentCategoryOptions" :normalizer="normalizer" :show-count="true"
  85 + placeholder="上级分类"/>
80 </el-form-item> 86 </el-form-item>
81 <el-form-item label="分类名称" prop="name"> 87 <el-form-item label="分类名称" prop="name">
82 <el-input v-model="form.name" placeholder="请输入分类名称"/> 88 <el-input v-model="form.name" placeholder="请输入分类名称"/>
83 </el-form-item> 89 </el-form-item>
84 <el-form-item label="分类图标" prop="icon"> 90 <el-form-item label="分类图标" prop="icon">
85 - <el-input v-model="form.icon" placeholder="请输入分类图标"/> 91 + <el-popover placement="bottom-start" width="460" trigger="click" @show="$refs['iconSelect'].reset()">
  92 + <IconSelect ref="iconSelect" @selected="iconSelected"/>
  93 + <el-input slot="reference" v-model="form.icon" placeholder="点击选择分类图标" readonly>
  94 + <svg-icon v-if="form.icon" slot="prefix" :icon-class="form.icon" class="el-input__icon"
  95 + style="height: 32px;width: 16px;"/>
  96 + <i v-else slot="prefix" class="el-icon-search el-input__icon"/>
  97 + </el-input>
  98 + </el-popover>
86 </el-form-item> 99 </el-form-item>
87 <el-form-item label="分类图片" prop="bannerUrl"> 100 <el-form-item label="分类图片" prop="bannerUrl">
88 <el-input v-model="form.bannerUrl" placeholder="请输入分类图片"/> 101 <el-input v-model="form.bannerUrl" placeholder="请输入分类图片"/>
89 </el-form-item> 102 </el-form-item>
90 <el-form-item label="分类排序" prop="sort"> 103 <el-form-item label="分类排序" prop="sort">
91 - <el-input v-model="form.sort" placeholder="请输入分类排序"/> 104 + <el-input-number v-model="form.sort" controls-position="right" :min="0" />
92 </el-form-item> 105 </el-form-item>
93 <el-form-item label="分类描述"> 106 <el-form-item label="分类描述">
94 <editor v-model="form.description" :min-height="192"/> 107 <editor v-model="form.description" :min-height="192"/>
@@ -116,14 +129,19 @@ import { @@ -116,14 +129,19 @@ import {
116 exportCategoryExcel, 129 exportCategoryExcel,
117 getCategory, 130 getCategory,
118 getCategoryPage, 131 getCategoryPage,
  132 + listCategory,
119 updateCategory 133 updateCategory
120 } from "@/api/mall/product/category"; 134 } from "@/api/mall/product/category";
121 import Editor from '@/components/Editor'; 135 import Editor from '@/components/Editor';
  136 +import Treeselect from "@riophae/vue-treeselect";
  137 +import "@riophae/vue-treeselect/dist/vue-treeselect.css";
  138 +import IconSelect from "@/components/IconSelect";
  139 +import ImageUpload from '@/components/ImageUpload';
122 140
123 export default { 141 export default {
124 name: "Category", 142 name: "Category",
125 components: { 143 components: {
126 - Editor, 144 + Editor, Treeselect, IconSelect, ImageUpload
127 }, 145 },
128 data() { 146 data() {
129 return { 147 return {
@@ -137,6 +155,8 @@ export default { @@ -137,6 +155,8 @@ export default {
137 total: 0, 155 total: 0,
138 // 商品分类列表 156 // 商品分类列表
139 list: [], 157 list: [],
  158 + // 商品分类树选项
  159 + parentCategoryOptions: [],
140 // 弹出层标题 160 // 弹出层标题
141 title: "", 161 title: "",
142 // 是否显示弹出层 162 // 是否显示弹出层
@@ -153,11 +173,10 @@ export default { @@ -153,11 +173,10 @@ export default {
153 form: {}, 173 form: {},
154 // 表单校验 174 // 表单校验
155 rules: { 175 rules: {
156 - pid: [{required: true, message: "父分类编号不能为空", trigger: "blur"}], 176 + pid: [{required: true, message: "请选择上级分类", trigger: "blur"}],
157 name: [{required: true, message: "分类名称不能为空", trigger: "blur"}], 177 name: [{required: true, message: "分类名称不能为空", trigger: "blur"}],
  178 + icon: [{required: true, message: "分类图标不能为空", trigger: "blur"}],
158 bannerUrl: [{required: true, message: "分类图片不能为空", trigger: "blur"}], 179 bannerUrl: [{required: true, message: "分类图片不能为空", trigger: "blur"}],
159 - sort: [{required: true, message: "分类排序不能为空", trigger: "blur"}],  
160 - description: [{required: true, message: "分类描述不能为空", trigger: "blur"}],  
161 status: [{required: true, message: "开启状态不能为空", trigger: "blur"}], 180 status: [{required: true, message: "开启状态不能为空", trigger: "blur"}],
162 } 181 }
163 }; 182 };
@@ -179,6 +198,30 @@ export default { @@ -179,6 +198,30 @@ export default {
179 this.loading = false; 198 this.loading = false;
180 }); 199 });
181 }, 200 },
  201 + // 选择图标
  202 + iconSelected(name) {
  203 + this.form.icon = name;
  204 + },
  205 + /** 转换菜单数据结构 */
  206 + normalizer(node) {
  207 + if (node.children && !node.children.length) {
  208 + delete node.children;
  209 + }
  210 + return {
  211 + id: node.id,
  212 + label: node.name,
  213 + children: node.children
  214 + };
  215 + },
  216 + /** 查询分类下拉树结构 */
  217 + getTreeselect() {
  218 + listCategory().then(response => {
  219 + this.parentCategoryOptions = [];
  220 + const menu = {id: 0, name: '主分类', children: []};
  221 + menu.children = this.handleTree(response.data, "id", "pid");
  222 + this.parentCategoryOptions.push(menu);
  223 + });
  224 + },
182 /** 取消按钮 */ 225 /** 取消按钮 */
183 cancel() { 226 cancel() {
184 this.open = false; 227 this.open = false;
@@ -212,12 +255,14 @@ export default { @@ -212,12 +255,14 @@ export default {
212 /** 新增按钮操作 */ 255 /** 新增按钮操作 */
213 handleAdd() { 256 handleAdd() {
214 this.reset(); 257 this.reset();
  258 + this.getTreeselect();
215 this.open = true; 259 this.open = true;
216 this.title = "添加商品分类"; 260 this.title = "添加商品分类";
217 }, 261 },
218 /** 修改按钮操作 */ 262 /** 修改按钮操作 */
219 handleUpdate(row) { 263 handleUpdate(row) {
220 this.reset(); 264 this.reset();
  265 + this.getTreeselect();
221 const id = row.id; 266 const id = row.id;
222 getCategory(id).then(response => { 267 getCategory(id).then(response => {
223 this.form = response.data; 268 this.form = response.data;