Commit d3edaec2b1d7c54bc5e03450431fb68983ec6368
1 parent
4e67b6bb
商品分类维护
Showing
7 changed files
with
137 additions
and
58 deletions
sql/mall.sql
| ... | ... | @@ -26,10 +26,10 @@ CREATE TABLE `product_category` |
| 26 | 26 | `id` bigint NOT NULL AUTO_INCREMENT COMMENT '分类编号', |
| 27 | 27 | `pid` bigint NOT NULL COMMENT '父分类编号', |
| 28 | 28 | `name` varchar(255) NOT NULL COMMENT '分类名称', |
| 29 | - `icon` varchar(100) DEFAULT '#' COMMENT '分类图标', | |
| 29 | + `icon` varchar(100) NOT NULL DEFAULT '#' COMMENT '分类图标', | |
| 30 | 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 | 33 | `status` tinyint NOT NULL COMMENT '开启状态', |
| 34 | 34 | `creator` varchar(64) DEFAULT '' COMMENT '创建者', |
| 35 | 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 | 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 | 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 | 5 | import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; |
| 20 | - | |
| 21 | 6 | import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; |
| 22 | -import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.*; | |
| 23 | - | |
| 24 | 7 | import cn.iocoder.yudao.module.product.controller.admin.category.vo.*; |
| 25 | -import cn.iocoder.yudao.module.product.dal.dataobject.category.CategoryDO; | |
| 26 | 8 | import cn.iocoder.yudao.module.product.convert.category.CategoryConvert; |
| 9 | +import cn.iocoder.yudao.module.product.dal.dataobject.category.CategoryDO; | |
| 27 | 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 | 28 | @Api(tags = "管理后台 - 商品分类") |
| 30 | 29 | @RestController |
| ... | ... | @@ -68,15 +67,22 @@ public class CategoryController { |
| 68 | 67 | return success(CategoryConvert.INSTANCE.convert(category)); |
| 69 | 68 | } |
| 70 | 69 | |
| 71 | - @GetMapping("/list") | |
| 70 | + @GetMapping("/listByIds") | |
| 72 | 71 | @ApiOperation("获得商品分类列表") |
| 73 | - @ApiImplicitParam(name = "ids", value = "编号列表", required = true, example = "1024,2048", dataTypeClass = List.class) | |
| 74 | 72 | @PreAuthorize("@ss.hasPermission('product:category:query')") |
| 75 | 73 | public CommonResult<List<CategoryRespVO>> getCategoryList(@RequestParam("ids") Collection<Long> ids) { |
| 76 | 74 | List<CategoryDO> list = categoryService.getCategoryList(ids); |
| 77 | 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 | 86 | @GetMapping("/page") |
| 81 | 87 | @ApiOperation("获得商品分类分页") |
| 82 | 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 | 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 | 10 | * 商品分类 Base VO,提供给添加、修改、详细的子 VO 使用 |
| ... | ... | @@ -17,22 +18,21 @@ public class CategoryBaseVO { |
| 17 | 18 | private Long pid; |
| 18 | 19 | |
| 19 | 20 | @ApiModelProperty(value = "分类名称", required = true, example = "办公文具") |
| 20 | - @NotNull(message = "分类名称不能为空") | |
| 21 | + @NotBlank(message = "分类名称不能为空") | |
| 21 | 22 | private String name; |
| 22 | 23 | |
| 23 | 24 | @ApiModelProperty(value = "分类图标") |
| 25 | + @NotBlank(message = "分类图标不能为空") | |
| 24 | 26 | private String icon; |
| 25 | 27 | |
| 26 | 28 | @ApiModelProperty(value = "分类图片", required = true) |
| 27 | - @NotNull(message = "分类图片不能为空") | |
| 29 | + @NotBlank(message = "分类图片不能为空") | |
| 28 | 30 | private String bannerUrl; |
| 29 | 31 | |
| 30 | 32 | @ApiModelProperty(value = "分类排序", required = true, example = "1") |
| 31 | - @NotNull(message = "分类排序不能为空") | |
| 32 | 33 | private Integer sort; |
| 33 | 34 | |
| 34 | 35 | @ApiModelProperty(value = "分类描述", required = true, example = "描述") |
| 35 | - @NotNull(message = "分类描述不能为空") | |
| 36 | 36 | private String description; |
| 37 | 37 | |
| 38 | 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 | 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 | 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 | 15 | * 商品分类 Service 接口 |
| ... | ... | @@ -67,4 +72,10 @@ public interface CategoryService { |
| 67 | 72 | */ |
| 68 | 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 | 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 | 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 | 8 | import cn.iocoder.yudao.module.product.convert.category.CategoryConvert; |
| 9 | +import cn.iocoder.yudao.module.product.dal.dataobject.category.CategoryDO; | |
| 13 | 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 | 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 | 22 | * 商品分类 Service 实现类 |
| ... | ... | @@ -79,4 +82,9 @@ public class CategoryServiceImpl implements CategoryService { |
| 79 | 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 | 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 | 47 | export function getCategoryPage(query) { |
| 39 | 48 | return request({ | ... | ... |
yudao-ui-admin/src/views/mall/product/category/index.vue
| ... | ... | @@ -40,13 +40,18 @@ |
| 40 | 40 | |
| 41 | 41 | <!-- 列表 --> |
| 42 | 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 | 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 | 54 | <el-table-column label="分类排序" align="center" prop="sort"/> |
| 49 | - <el-table-column label="分类描述" align="center" prop="description"/> | |
| 50 | 55 | <el-table-column label="开启状态" align="center" prop="status"> |
| 51 | 56 | <template slot-scope="scope"> |
| 52 | 57 | <dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status"/> |
| ... | ... | @@ -75,20 +80,28 @@ |
| 75 | 80 | <!-- 对话框(添加 / 修改) --> |
| 76 | 81 | <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body> |
| 77 | 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 | 86 | </el-form-item> |
| 81 | 87 | <el-form-item label="分类名称" prop="name"> |
| 82 | 88 | <el-input v-model="form.name" placeholder="请输入分类名称"/> |
| 83 | 89 | </el-form-item> |
| 84 | 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 | 99 | </el-form-item> |
| 87 | 100 | <el-form-item label="分类图片" prop="bannerUrl"> |
| 88 | 101 | <el-input v-model="form.bannerUrl" placeholder="请输入分类图片"/> |
| 89 | 102 | </el-form-item> |
| 90 | 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 | 105 | </el-form-item> |
| 93 | 106 | <el-form-item label="分类描述"> |
| 94 | 107 | <editor v-model="form.description" :min-height="192"/> |
| ... | ... | @@ -116,14 +129,19 @@ import { |
| 116 | 129 | exportCategoryExcel, |
| 117 | 130 | getCategory, |
| 118 | 131 | getCategoryPage, |
| 132 | + listCategory, | |
| 119 | 133 | updateCategory |
| 120 | 134 | } from "@/api/mall/product/category"; |
| 121 | 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 | 141 | export default { |
| 124 | 142 | name: "Category", |
| 125 | 143 | components: { |
| 126 | - Editor, | |
| 144 | + Editor, Treeselect, IconSelect, ImageUpload | |
| 127 | 145 | }, |
| 128 | 146 | data() { |
| 129 | 147 | return { |
| ... | ... | @@ -137,6 +155,8 @@ export default { |
| 137 | 155 | total: 0, |
| 138 | 156 | // 商品分类列表 |
| 139 | 157 | list: [], |
| 158 | + // 商品分类树选项 | |
| 159 | + parentCategoryOptions: [], | |
| 140 | 160 | // 弹出层标题 |
| 141 | 161 | title: "", |
| 142 | 162 | // 是否显示弹出层 |
| ... | ... | @@ -153,11 +173,10 @@ export default { |
| 153 | 173 | form: {}, |
| 154 | 174 | // 表单校验 |
| 155 | 175 | rules: { |
| 156 | - pid: [{required: true, message: "父分类编号不能为空", trigger: "blur"}], | |
| 176 | + pid: [{required: true, message: "请选择上级分类", trigger: "blur"}], | |
| 157 | 177 | name: [{required: true, message: "分类名称不能为空", trigger: "blur"}], |
| 178 | + icon: [{required: true, message: "分类图标不能为空", trigger: "blur"}], | |
| 158 | 179 | bannerUrl: [{required: true, message: "分类图片不能为空", trigger: "blur"}], |
| 159 | - sort: [{required: true, message: "分类排序不能为空", trigger: "blur"}], | |
| 160 | - description: [{required: true, message: "分类描述不能为空", trigger: "blur"}], | |
| 161 | 180 | status: [{required: true, message: "开启状态不能为空", trigger: "blur"}], |
| 162 | 181 | } |
| 163 | 182 | }; |
| ... | ... | @@ -179,6 +198,30 @@ export default { |
| 179 | 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 | 226 | cancel() { |
| 184 | 227 | this.open = false; |
| ... | ... | @@ -212,12 +255,14 @@ export default { |
| 212 | 255 | /** 新增按钮操作 */ |
| 213 | 256 | handleAdd() { |
| 214 | 257 | this.reset(); |
| 258 | + this.getTreeselect(); | |
| 215 | 259 | this.open = true; |
| 216 | 260 | this.title = "添加商品分类"; |
| 217 | 261 | }, |
| 218 | 262 | /** 修改按钮操作 */ |
| 219 | 263 | handleUpdate(row) { |
| 220 | 264 | this.reset(); |
| 265 | + this.getTreeselect(); | |
| 221 | 266 | const id = row.id; |
| 222 | 267 | getCategory(id).then(response => { |
| 223 | 268 | this.form = response.data; | ... | ... |