mall:完善商品分类的管理后台界面

pull/2/head
YunaiV 2022-07-30 20:28:01 +08:00
parent 6a0f713452
commit 969c387764
7 changed files with 48 additions and 176 deletions

View File

@ -20,25 +20,6 @@ SET NAMES utf8mb4;
-- ---------------------------- -- ----------------------------
-- Table structure for product_category -- Table structure for product_category
-- ---------------------------- -- ----------------------------
DROP TABLE IF EXISTS `product_category`;
CREATE TABLE `product_category`
(
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '',
`parent_id` bigint NOT NULL COMMENT '',
`name` varchar(255) NOT NULL COMMENT '',
`icon` varchar(100) NOT NULL DEFAULT '#' COMMENT '',
`banner_url` varchar(255) NOT NULL COMMENT '',
`sort` int DEFAULT '0' COMMENT '',
`description` varchar(1024) DEFAULT NULL COMMENT '',
`status` tinyint NOT NULL COMMENT '',
`creator` varchar(64) DEFAULT '' COMMENT '',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '',
`updater` varchar(64) DEFAULT '' COMMENT '',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '',
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '',
`tenant_id` bigint NOT NULL DEFAULT '0' COMMENT '',
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB COMMENT='';
-- ---------------------------- -- ----------------------------
-- Table structure for product_brand -- Table structure for product_brand
@ -83,6 +64,7 @@ INSERT INTO `system_menu`(`name`, `permission`, `type`, `sort`, `parent_id`, `pa
VALUES ('', 'product:category:delete', 3, 4, @parentId, '', '', '', 0); VALUES ('', 'product:category:delete', 3, 4, @parentId, '', '', '', 0);
INSERT INTO `system_menu`(`name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`) INSERT INTO `system_menu`(`name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`)
VALUES ('', 'product:category:export', 3, 5, @parentId, '', '', '', 0); VALUES ('', 'product:category:export', 3, 5, @parentId, '', '', '', 0);
-- SQL -- SQL
INSERT INTO `system_menu`(`name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`) INSERT INTO `system_menu`(`name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`)
VALUES ('', '', 2, 1, 2001, 'brand', '', 'mall/product/brand/index', 0); VALUES ('', '', 2, 1, 2001, 'brand', '', 'mall/product/brand/index', 0);

View File

@ -35,6 +35,4 @@ public class AppCategoryController {
return success(CategoryConvert.INSTANCE.convertList03(list)); return success(CategoryConvert.INSTANCE.convertList03(list));
} }
} }

View File

@ -8,7 +8,7 @@ import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull; import javax.validation.constraints.NotNull;
@Data @Data
@ApiModel(value = "App - 商品分类 列表 Request VO", description = "参数和 CategoryBaseVO 是一致的") @ApiModel(value = "App - 商品分类 Response VO")
public class AppCategoryListRespVO { public class AppCategoryListRespVO {
@ApiModelProperty(value = "分类编号", required = true, example = "2") @ApiModelProperty(value = "分类编号", required = true, example = "2")
@ -22,17 +22,8 @@ public class AppCategoryListRespVO {
@NotBlank(message = "分类名称不能为空") @NotBlank(message = "分类名称不能为空")
private String name; private String name;
@ApiModelProperty(value = "分类图标")
@NotBlank(message = "分类图标不能为空")
private String icon;
@ApiModelProperty(value = "分类图片", required = true) @ApiModelProperty(value = "分类图片", required = true)
@NotBlank(message = "分类图片不能为空") @NotBlank(message = "分类图片不能为空")
private String bannerUrl; private String picUrl;
@ApiModelProperty(value = "分类排序", required = true, example = "1")
private Integer sort;
@ApiModelProperty(value = "分类描述", required = true, example = "描述")
private String description;
} }

View File

@ -1,7 +1,7 @@
import request from '@/utils/request' import request from '@/utils/request'
// 创建商品分类 // 创建商品分类
export function createCategory(data) { export function createProductCategory(data) {
return request({ return request({
url: '/product/category/create', url: '/product/category/create',
method: 'post', method: 'post',
@ -10,7 +10,7 @@ export function createCategory(data) {
} }
// 更新商品分类 // 更新商品分类
export function updateCategory(data) { export function updateProductCategory(data) {
return request({ return request({
url: '/product/category/update', url: '/product/category/update',
method: 'put', method: 'put',
@ -19,7 +19,7 @@ export function updateCategory(data) {
} }
// 删除商品分类 // 删除商品分类
export function deleteCategory(id) { export function deleteProductCategory(id) {
return request({ return request({
url: '/product/category/delete?id=' + id, url: '/product/category/delete?id=' + id,
method: 'delete' method: 'delete'
@ -27,37 +27,18 @@ export function deleteCategory(id) {
} }
// 获得商品分类 // 获得商品分类
export function getCategory(id) { export function getProductCategory(id) {
return request({ return request({
url: '/product/category/get?id=' + id, url: '/product/category/get?id=' + id,
method: 'get' method: 'get'
}) })
} }
// 获得商品分类 // 获得商品分类列表
export function listCategory(query) { export function getProductCategoryList(query) {
return request({ return request({
url: '/product/category/listByQuery', url: '/product/category/list',
method: 'get', method: 'get',
params: query params: query
}) })
} }
// 获得商品分类分页
export function getCategoryPage(query) {
return request({
url: '/product/category/page',
method: 'get',
params: query
})
}
// 导出商品分类 Excel
export function exportCategoryExcel(query) {
return request({
url: '/product/category/export-excel',
method: 'get',
params: query,
responseType: 'blob'
})
}

View File

@ -118,7 +118,7 @@ import {
getBrandPage, getBrandPage,
updateBrand updateBrand
} from "@/api/mall/product/brand"; } from "@/api/mall/product/brand";
import {listCategory} from "@/api/mall/product/category"; import {getProductCategoryList} from "@/api/mall/product/category";
import ImageUpload from '@/components/ImageUpload'; import ImageUpload from '@/components/ImageUpload';
import Treeselect from "@riophae/vue-treeselect"; import Treeselect from "@riophae/vue-treeselect";
import "@riophae/vue-treeselect/dist/vue-treeselect.css"; import "@riophae/vue-treeselect/dist/vue-treeselect.css";
@ -194,7 +194,7 @@ export default {
}, },
/** 查询分类下拉树结构 */ /** 查询分类下拉树结构 */
getTreeselect() { getTreeselect() {
listCategory().then(response => { getProductCategoryList().then(response => {
this.categoryOptions = []; this.categoryOptions = [];
const menu = {id: 0, name: '商品分类', children: []}; const menu = {id: 0, name: '商品分类', children: []};
menu.children = this.handleTree(response.data, "id", "pid"); menu.children = this.handleTree(response.data, "id", "pid");

View File

@ -6,12 +6,6 @@
<el-form-item label="分类名称" prop="name"> <el-form-item label="分类名称" prop="name">
<el-input v-model="queryParams.name" placeholder="请输入分类名称" clearable @keyup.enter.native="handleQuery"/> <el-input v-model="queryParams.name" placeholder="请输入分类名称" clearable @keyup.enter.native="handleQuery"/>
</el-form-item> </el-form-item>
<el-form-item label="开启状态" prop="status">
<el-select v-model="queryParams.status" placeholder="请选择开启状态" clearable size="small">
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.COMMON_STATUS)"
:key="dict.value" :label="dict.label" :value="dict.value"/>
</el-select>
</el-form-item>
<el-form-item> <el-form-item>
<el-button type="primary" icon="el-icon-search" @click="handleQuery"></el-button> <el-button type="primary" icon="el-icon-search" @click="handleQuery"></el-button>
<el-button icon="el-icon-refresh" @click="resetQuery"></el-button> <el-button icon="el-icon-refresh" @click="resetQuery"></el-button>
@ -28,27 +22,16 @@
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="info" plain icon="el-icon-sort" size="mini" @click="toggleExpandAll">/</el-button> <el-button type="info" plain icon="el-icon-sort" size="mini" @click="toggleExpandAll">/</el-button>
</el-col> </el-col>
<el-col :span="1.5">
<el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport"
:loading="exportLoading"
v-hasPermi="['product:category:export']">导出
</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar> <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row> </el-row>
<!-- 列表 --> <!-- 列表 -->
<el-table v-if="refreshTable" v-loading="loading" :data="list" row-key="id" :default-expand-all="isExpandAll" <el-table v-if="refreshTable" v-loading="loading" :data="list" row-key="id" :default-expand-all="isExpandAll"
:tree-props="{children: 'children', hasChildren: 'hasChildren'}"> :tree-props="{children: 'children', hasChildren: 'hasChildren'}">
<el-table-column label="分类名称" align="center" prop="name"/> <el-table-column label="分类名称" prop="name"/>
<el-table-column label="分类图标" align="center" prop="icon"> <el-table-column label="分类图片" align="center" prop="picUrl">
<template slot-scope="scope"> <template slot-scope="scope">
<svg-icon :icon-class="scope.row.icon" /> <img v-if="scope.row.picUrl" :src="scope.row.picUrl" alt="分类图片" style="height: 100px"/>
</template>
</el-table-column>
<el-table-column label="分类图片" align="center" prop="bannerUrl">
<template slot-scope="scope">
<img v-if="scope.row.bannerUrl" :src="scope.row.bannerUrl" alt="分类图片" class="img-height"/>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="分类排序" align="center" prop="sort"/> <el-table-column label="分类排序" align="center" prop="sort"/>
@ -78,32 +61,22 @@
<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body> <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="80px"> <el-form ref="form" :model="form" :rules="rules" label-width="80px">
<el-form-item label="上级分类" prop="parentId"> <el-form-item label="上级分类" prop="parentId">
<Treeselect v-model="form.parentId" :options="parentCategoryOptions" :normalizer="normalizer" <el-select v-model="form.parentId" placeholder="请选择上级分类" clearable size="small">
:show-count="true" <el-option :key="0" label="顶级分类" :value="0"/>
placeholder="上级分类"/> <el-option v-for="item in list.filter(v => v.parentId === 0)" :key="item.id" :label="item.name" :value="item.id"/>
</el-select>
</el-form-item> </el-form-item>
<el-form-item label="分类名称" prop="name"> <el-form-item label="分类名称" prop="name">
<el-input v-model="form.name" placeholder="请输入分类名称"/> <el-input v-model="form.name" placeholder="请输入分类名称"/>
</el-form-item> </el-form-item>
<el-form-item label="分类图标" prop="icon">
<el-popover placement="bottom-start" width="460" trigger="click" @show="$refs['iconSelect'].reset()">
<IconSelect ref="iconSelect" @selected="iconSelected"/>
<el-input slot="reference" v-model="form.icon" placeholder="点击选择分类图标" readonly>
<svg-icon v-if="form.icon" slot="prefix" :icon-class="form.icon" class="el-input__icon"
style="height: 32px;width: 16px;"/>
<i v-else slot="prefix" class="el-icon-search el-input__icon"/>
</el-input>
</el-popover>
</el-form-item>
<el-form-item label="分类图片" prop="bannerUrl"> <el-form-item label="分类图片" prop="bannerUrl">
<ImageUpload v-model="form.bannerUrl" :limit="1"/> <ImageUpload v-model="form.picUrl" :limit="1" :is-show-tip="false" />
<div v-if="form.parentId === 0" style="font-size: 10px"> 200x100 </div>
<div v-else style="font-size: 10px">推荐 100x100 图片分辨率</div>
</el-form-item> </el-form-item>
<el-form-item label="分类排序" prop="sort"> <el-form-item label="分类排序" prop="sort">
<el-input-number v-model="form.sort" controls-position="right" :min="0" /> <el-input-number v-model="form.sort" controls-position="right" :min="0" />
</el-form-item> </el-form-item>
<el-form-item label="分类描述">
<editor v-model="form.description" :min-height="192"/>
</el-form-item>
<el-form-item label="开启状态" prop="status"> <el-form-item label="开启状态" prop="status">
<el-radio-group v-model="form.status"> <el-radio-group v-model="form.status">
<el-radio v-for="dict in this.getDictDatas(DICT_TYPE.COMMON_STATUS)" <el-radio v-for="dict in this.getDictDatas(DICT_TYPE.COMMON_STATUS)"
@ -111,6 +84,9 @@
</el-radio> </el-radio>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<el-form-item label="分类描述">
<el-input v-model="form.description" type="textarea" placeholder="请输入分类描述" />
</el-form-item>
</el-form> </el-form>
<div slot="footer" class="dialog-footer"> <div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button> <el-button type="primary" @click="submitForm"> </el-button>
@ -122,34 +98,31 @@
<script> <script>
import { import {
createCategory, createProductCategory,
deleteCategory, deleteProductCategory,
exportCategoryExcel, exportCategoryExcel,
getCategory, getProductCategory,
listCategory, getProductCategoryList,
updateCategory updateProductCategory
} from "@/api/mall/product/category"; } from "@/api/mall/product/category";
import Editor from '@/components/Editor'; import Editor from '@/components/Editor';
import Treeselect from "@riophae/vue-treeselect"; import Treeselect from "@riophae/vue-treeselect";
import "@riophae/vue-treeselect/dist/vue-treeselect.css"; import "@riophae/vue-treeselect/dist/vue-treeselect.css";
import IconSelect from "@/components/IconSelect"; import IconSelect from "@/components/IconSelect";
import ImageUpload from '@/components/ImageUpload'; import ImageUpload from '@/components/ImageUpload';
import {CommonStatusEnum} from "@/utils/constants";
export default { export default {
name: "Category", name: "Category",
components: { components: {
Editor, Treeselect, IconSelect, ImageUpload Editor, Treeselect, ImageUpload
}, },
data() { data() {
return { return {
// //
loading: true, loading: true,
//
exportLoading: false,
// //
showSearch: true, showSearch: true,
//
total: 0,
// //
list: [], list: [],
// //
@ -164,10 +137,7 @@ export default {
refreshTable: true, refreshTable: true,
// //
queryParams: { queryParams: {
pageNo: 1,
pageSize: 10,
name: null, name: null,
status: null,
}, },
// //
form: {}, form: {},
@ -175,8 +145,8 @@ export default {
rules: { rules: {
parentId: [{required: true, message: "请选择上级分类", trigger: "blur"}], parentId: [{required: true, message: "请选择上级分类", trigger: "blur"}],
name: [{required: true, message: "分类名称不能为空", trigger: "blur"}], name: [{required: true, message: "分类名称不能为空", trigger: "blur"}],
icon: [{required: true, message: "分类图不能为空", trigger: "blur"}], picUrl: [{required: true, message: "分类图不能为空", trigger: "blur"}],
bannerUrl: [{required: true, message: "分类图片不能为空", trigger: "blur"}], sort: [{required: true, message: "分类排序不能为空", trigger: "blur"}],
status: [{required: true, message: "开启状态不能为空", trigger: "blur"}], status: [{required: true, message: "开启状态不能为空", trigger: "blur"}],
} }
}; };
@ -191,35 +161,11 @@ export default {
// //
let params = {...this.queryParams}; let params = {...this.queryParams};
// //
listCategory(params).then(response => { getProductCategoryList(params).then(response => {
this.list = this.handleTree(response.data, "id", "parentId"); this.list = this.handleTree(response.data, "id", "parentId");
this.loading = false; this.loading = false;
}); });
}, },
//
iconSelected(name) {
this.form.icon = name;
},
/** 转换菜单数据结构 */
normalizer(node) {
if (node.children && !node.children.length) {
delete node.children;
}
return {
id: node.id,
label: node.name,
children: node.children
};
},
/** 查询分类下拉树结构 */
getTreeselect() {
listCategory().then(response => {
this.parentCategoryOptions = [];
const menu = {id: 0, name: '主分类', children: []};
menu.children = this.handleTree(response.data, "id", "parentId");
this.parentCategoryOptions.push(menu);
});
},
/** 取消按钮 */ /** 取消按钮 */
cancel() { cancel() {
this.open = false; this.open = false;
@ -231,11 +177,10 @@ export default {
id: undefined, id: undefined,
parentId: undefined, parentId: undefined,
name: undefined, name: undefined,
icon: undefined, pirUrl: undefined,
bannerUrl: undefined, sort: 0,
sort: undefined,
description: undefined, description: undefined,
status: undefined, status: CommonStatusEnum.ENABLE,
}; };
this.resetForm("form"); this.resetForm("form");
}, },
@ -260,16 +205,14 @@ export default {
/** 新增按钮操作 */ /** 新增按钮操作 */
handleAdd() { handleAdd() {
this.reset(); this.reset();
this.getTreeselect();
this.open = true; this.open = true;
this.title = "添加商品分类"; this.title = "添加商品分类";
}, },
/** 修改按钮操作 */ /** 修改按钮操作 */
handleUpdate(row) { handleUpdate(row) {
this.reset(); this.reset();
this.getTreeselect();
const id = row.id; const id = row.id;
getCategory(id).then(response => { getProductCategory(id).then(response => {
this.form = response.data; this.form = response.data;
this.open = true; this.open = true;
this.title = "修改商品分类"; this.title = "修改商品分类";
@ -283,7 +226,7 @@ export default {
} }
// //
if (this.form.id != null) { if (this.form.id != null) {
updateCategory(this.form).then(response => { updateProductCategory(this.form).then(response => {
this.$modal.msgSuccess("修改成功"); this.$modal.msgSuccess("修改成功");
this.open = false; this.open = false;
this.getList(); this.getList();
@ -291,7 +234,7 @@ export default {
return; return;
} }
// //
createCategory(this.form).then(response => { createProductCategory(this.form).then(response => {
this.$modal.msgSuccess("新增成功"); this.$modal.msgSuccess("新增成功");
this.open = false; this.open = false;
this.getList(); this.getList();
@ -302,36 +245,13 @@ export default {
handleDelete(row) { handleDelete(row) {
const id = row.id; const id = row.id;
this.$modal.confirm('是否确认删除商品分类编号为"' + id + '"的数据项?').then(function () { this.$modal.confirm('是否确认删除商品分类编号为"' + id + '"的数据项?').then(function () {
return deleteCategory(id); return deleteProductCategory(id);
}).then(() => { }).then(() => {
this.getList(); this.getList();
this.$modal.msgSuccess("删除成功"); this.$modal.msgSuccess("删除成功");
}).catch(() => { }).catch(() => {
}); });
},
/** 导出按钮操作 */
handleExport() {
//
let params = {...this.queryParams};
params.pageNo = undefined;
params.pageSize = undefined;
//
this.$modal.confirm('是否确认导出所有商品分类数据项?').then(() => {
this.exportLoading = true;
return exportCategoryExcel(params);
}).then(response => {
this.$download.excel(response, '商品分类.xls');
this.exportLoading = false;
}).catch(() => {
});
} }
} }
}; };
</script> </script>
<style scoped lang="scss">
//
.img-height {
height: 150px;
}
</style>

View File

@ -283,12 +283,12 @@
<script> <script>
import {createSpu, updateSpu, deleteSpu, getSpu, getSpuPage, exportSpuExcel} from "@/api/mall/product/spu"; import {createSpu, updateSpu, deleteSpu, getSpu, getSpuPage, exportSpuExcel} from "@/api/mall/product/spu";
import { import {
createCategory, createProductCategory,
deleteCategory, deleteProductCategory,
exportCategoryExcel, exportCategoryExcel,
getCategory, getProductCategory,
listCategory, getProductCategoryList,
updateCategory updateProductCategory
} from "@/api/mall/product/category"; } from "@/api/mall/product/category";
import { import {
createProperty, createProperty,
@ -585,7 +585,7 @@ export default {
/** 查询分类 */ /** 查询分类 */
getListCategory() { getListCategory() {
// //
listCategory().then(response => { getProductCategoryList().then(response => {
this.categoryList = this.handleTree(response.data, "id", "parentId"); this.categoryList = this.handleTree(response.data, "id", "parentId");
}); });