新增菜品/套餐分类
页面原型
当我们在后台系统中添加菜品/套餐
时,需要选择一个菜品/套餐分类,在移动端也会按照菜品分类和套餐分类来展示对应的菜品和套餐
第一步: 用户点击确定按钮
执行submitForm
函数发送Ajax请求,将新增菜品/套餐
表单中输入的数据以json形式提交给服务端,等待服务端响应数据后执行回调函数
<!--用户在表单页面category/list.html中填写分类名称和排序数字-->
<el-form class="demo-form-inline" label-width="100px"><el-form-item label="分类名称:"><el-input v-model="classData.name" placeholder="请输入分类名称" maxlength="14"/></el-form-item><el-form-item label="排序:"><el-input v-model="classData.sort" type="number" placeholder="请输入排序" /></el-form-item>
</el-form><span slot="footer" class="dialog-footer"><el-button size="medium" @click="classData.dialogVisible = false">取 消</el-button><el-button type="primary" size="medium" @click="submitForm()">确 定</el-button><el-button v-if="action != 'edit'" type="primary" size="medium" class="continue" @click="submitForm('go')"> 保存并继续添加 </el-button>
submitForm(st) {const classData = this.classDataconst valid = (classData.name === 0 ||classData.name) && (classData.sort === 0 || classData.sort)if (this.action === 'add') {if (valid) {const reg = /^\d+$/if (reg.test(classData.sort)) {addCategory({'name': classData.name,'type':this.type, sort: classData.sort}).then(res => {console.log(res)if (res.code === 1) {this.$message.success('分类添加成功!')if (!st) {this.classData.dialogVisible = false} else {this.classData.name = ''this.classData.sort = ''}this.handleQuery()}}}}}
第二步: 服务端Controller接收页面提交的数据并通过Service调用Mapper操作数据库保存添加的菜品/套餐分类信息
数据模型
数据模型category表
: id是主键,name字段
表示分类名称是唯一的,type字段
为1表示菜品分类,为2表示套餐分类
后端处理请求
第一步: 创建实体类Category
,对于createUser和createTime
等公共字段使用自动填充
功能
@Data
public class Category implements Serializable {private static final long serialVersionUID = 1L;private Long id;//类型 1 菜品分类 2 套餐分类private Integer type;//分类名称private String name;//顺序private Integer sort;//创建时间@TableField(fill = FieldFill.INSERT)private LocalDateTime createTime;//更新时间@TableField(fill = FieldFill.INSERT_UPDATE)private LocalDateTime updateTime;//创建人@TableField(fill = FieldFill.INSERT)private Long createUser;//修改人@TableField(fill = FieldFill.INSERT_UPDATE)private Long updateUser;
}
第二步: 创建Mapper接口CategoryMapper
和Service接口CategoryService
及其实现类CatrgoryServiceImpl
@Mapper
public interface CategoryMapper extends BaseMapper<Category> {
}public interface CategoryService extends IService<Category> {
}@Service
public class CategoryServiceImpl extends ServiceImpl<CategoryMapper, Category> implements CategoryService {
}
第三步: 在CategoryController
控制层中编写控制器方法,将接收到的json数据添加到数据库中并响应一个成功的提示信息
@Slf4j
@RestController
@RequestMapping("/category")
public class CategoryController {@Autowiredprivate CategoryService categoryService;@PostMappingpublic Result<String> save(@RequestBody Category category) {log.info("category:{}", category);categoryService.save(category);return Result.success("新增分类成功");}
}
分页查询所有菜品/套餐分类
页面原型
第一步:用户点击分页管理
按钮时携带分页查询的相关参数page(默认1)、pageSize(默认10)
发送Ajax请求到服务端去查询菜品/套餐的分类信息
第二步: 服务端Controller接受到请求之后通过Service调用Mapper操作数据库查询相关的菜品/套餐
信息
第三步: Controller将查询到的所有数据和分页信息响应给前端
第四步: 前端接收到后端响应的数据后,通过ElementUI的Table组件
渲染到页面上
- 页面加载完毕之后调用
created
钩子函数,在该钩子函数内又调用init函数
进行初始化
created() {this.init()
}
async init () {await getCategoryPage({'page': this.page, 'pageSize': this.pageSize}).then(res => {if (String(res.code) === '1') {//将服务端查询到的所有数据赋给tableData,然后就能看到了this.tableData = res.data.recordsthis.counts = Number(res.data.total)} else {this.$message.error(res.msg || '操作失败')}}).catch(err => {this.$message.error('请求出错了:' + err)})
}
// 查询列表接口
const getCategoryPage = (params) => {return $axios({url: '/category/page',method: 'get',params})
}
后端处理请求
执行categoryService
提供的page
方法进行分页查询并将查询到的数据和分页信息响应给前端
@GetMapping("/page")
public Result<Page> page(int page, int pageSize) {// 创建分页构造器Page<Category> pageInfo = new Page<>(page, pageSize);// 构造条件查询器LambdaQueryWrapper<Category> queryWrapper = new LambdaQueryWrapper<>();// 添加排序条件,根据sort属性进行排序queryWrapper.orderByDesc(Category::getSort);// 执行分页查询的语句categoryService.page(pageInfo, queryWrapper);// 将查询到的所有数据和分页相关的信息响应给前端return Result.success(pageInfo);
}
删除某个分类(判断有无关联)
页面原型
第一步:在分类管理列表页面, 用户点击某个分类对应的删除
按钮发送Ajax请求,请求参数就是该菜品/套餐分类
单元格对应的属性Id
- 删除是个危险的操作,当用户点击
删除
按钮时先给一个提示信息防止误操作,当用户点确定
后才会执行deleCategory回调函数
<el-buttontype="text"size="small"class="delBut non"<!--删除按钮绑定了deleteHandle函数,参数就是当前单元格的Id-->@click="deleteHandle(scope.row.id)">
</el-button>
<script>deleteHandle(id) {this.$confirm('此操作将永久删除该文件, 是否继续?', '提示', {'confirmButtonText': '确定','cancelButtonText': '取消','type': 'warning'}).then(() => {deleCategory(id).then(res => {// 服务端成功删除后提示'删除成功!'if (res.code === 1) {this.$message.success('删除成功!')this.handleQuery()} else {this.$message.error(res.msg || '操作失败')}}).catch(err => {this.$message.error('请求出错了:' + err)})})}// 发其Ajax请求删除当前行const deleCategory = (id) => {return $axios({url: '/category',method: 'delete',params: {id}})}
</script>
第二步:服务端Controller接收页面提交的菜品/套餐分类的Id
,通过Service调用Mapper根据Id
删除对应的纪录,如果该分类关联了某个菜品或套餐时不允许被删除
后端处理请求(无关联)
@DeleteMapping
private Result<String> delete(Long id) {log.info("将被删除的id:{}", id);categoryService.removeById(id);return Result.success("分类信息删除成功");
}
查询菜品/套餐
删除某个分类时需要先拿着当前分类的Id
值去对应的菜品/套餐表
中进行查询,如果能查询到数据则说明该分类关联了菜品/套餐,此时不能删除该分类
数据模型
套餐信息表setmeal
菜品信息表dish
后端处理请求
第一步: 创建菜品表和套餐表对应的模型类Dish
和Stemal
,对于createUser和createTime
和createUser和updateUser
等公共字段使用自动填充
功能
/**菜品*/
@Data
public class Dish implements Serializable {private static final long serialVersionUID = 1L;private Long id;//菜品名称private String name;//菜品分类idprivate Long categoryId;//菜品价格private BigDecimal price;//商品码private String code;//图片private String image;//描述信息private String description;//0 停售 1 起售private Integer status;//顺序private Integer sort;@TableField(fill = FieldFill.INSERT)private LocalDateTime createTime;@TableField(fill = FieldFill.INSERT_UPDATE)private LocalDateTime updateTime;@TableField(fill = FieldFill.INSERT)private Long createUser;@TableField(fill = FieldFill.INSERT_UPDATE)private Long updateUser;
}
/*** 套餐*/
@Data
public class Setmeal implements Serializable {private static final long serialVersionUID = 1L;private Long id;//分类idprivate Long categoryId;//套餐名称private String name;//套餐价格private BigDecimal price;//状态 0:停用 1:启用private Integer status;//编码private String code;//描述信息private String description;//图片private String image;@TableField(fill = FieldFill.INSERT)private LocalDateTime createTime;@TableField(fill = FieldFill.INSERT_UPDATE)private LocalDateTime updateTime;@TableField(fill = FieldFill.INSERT)private Long createUser;@TableField(fill = FieldFill.INSERT_UPDATE)private Long updateUser;
}
第二步: 编写对应的Mapper接口和Service接口及其实现类
@Mapper
public interface DishMapper extends BaseMapper<Dish> {
}@Mapper
public interface SetmealMapper extends BaseMapper<Setmeal> {
}public interface SetmealService extends IService<Setmeal> {
}
@Service
public class SetmealServiceImpl extends ServiceImpl<SetmealMapper, Setmeal> implements SetmealService {
}public interface DishService extends IService<Dish> {
}
@Service
public class DishServiceImpl extends ServiceImpl<DishMapper, Dish> implements DishService {
}
第三步: 在common
包下新增CustomException
异常类用于封装我们的自定义异常
public class CustomException extends RuntimeException{public CustomException(String msg){super(msg);}
}
第四步: 在全局异常处理器中使用@ExceptionHandler注解
专门处理CustomerException
类型的异常
@ExceptionHandler(CustomException.class)
public Result<String> exceptionHandler(CustomException exception) {log.error(exception.getMessage());return Result.error(exception.getMessage());
}
第五步: CategoryService
接口中新增remove
方法, 在删除数据之前先根据当前菜品/套餐分类的categoryId
值去dish表和setmeal表
中查询是否关联了数据
如果查询到了数据
: 说明存在关联的菜品/套餐数据即不能删除需要抛出一个自定义异常如果查询不到数据
: 说明不存在关联的菜品/套餐数据那么可以正常删除
@Service
@Slf4j
public class CategoryServiceImpl extends ServiceImpl<CategoryMapper, Category> implements CategoryService {@AutowiredDishService dishService;@AutowiredSetmealService setmealService;/*** 根据id删除分类,删除之前需要进行判断* @param id*/@Overridepublic void remove(Long id) {// 根据菜品的分类id去菜品表中查询关联的菜品记录LambdaQueryWrapper<Dish> dishLambdaQueryWrapper = new LambdaQueryWrapper<>();dishLambdaQueryWrapper.eq(Dish::getCategoryId, id);int count1 = dishService.count(dishLambdaQueryWrapper);// 判断菜品当前分类是否关联了菜品,如果已经关联则抛出异常if (count1 > 0){throw new CustomException("当前分类下关联了菜品,不能删除");}// 根据套餐的分类id去套餐表中查询关联的套餐记录LambdaQueryWrapper<Setmeal> setmealLambdaQueryWrapper = new LambdaQueryWrapper<>();setmealLambdaQueryWrapper.eq(Setmeal::getCategoryId,id);// 判断当前套餐分类是否关联了套餐,如果已经关联则抛出异常int count2 = setmealService.count(setmealLambdaQueryWrapper);if (count2 > 0){throw new CustomException("当前分类下关联了套餐,不能删除");}// 没有关联菜品/套餐则调用CategoryService自带的removeById方法正常删除super.removeById(id);}
}
第六步: 在Controller中调用CategoryService
新增的remove方法
@DeleteMapping
public Result<String> delete(Long id){log.info("将要删除的分类id:{}",id);categoryService.remove(id);return Result.success("分类信息删除成功");
}
修改分类信息
页面原型
第一步: 用户在分类管理列表页面中点击修改"按钮
后弹出修改窗口,此时会回显当前菜品/套餐分类的信息并等待用户修改(通过Vue的数据绑定功能实现自动回显)
<el-buttontype="text"size="small"class="blueBug"<!--回显当前分类的信息,参数就是当前单元格的信息-->@click="editHandle(scope.row)"
>
修改
</el-button><el-formclass="demo-form-inline"label-width="100px"><el-form-item label="分类名称:"><el-inputv-model="classData.name"placeholder="请输入分类名称"maxlength="14"/></el-form-item><el-form-item label="排序:"><el-input v-model="classData.sort" type="number" placeholder="请输入排序" /></el-form-item>
</el-form><script>editHandle(dat) {// 这里并没有从后端查数据进行回显,而是对Vue双向绑定的数据classData下的name和sort属性进行赋值实现回显效果this.classData.title = '修改分类'this.action = 'edit'this.classData.name = dat.namethis.classData.sort = dat.sortthis.classData.id = dat.idthis.classData.dialogVisible = true}classData: {'title': '添加菜品分类','dialogVisible': false,'categoryId': '','name': '',sort: ''}
</script>
第二步: 用户点击确定
按钮后执行通用的submitForm
函数发起Ajax请求,以json格式提交修改后的菜品/套餐分类信息
<script>submitForm(st) {const classData = this.classDataconst valid = (classData.name === 0 ||classData.name) && (classData.sort === 0 || classData.sort)// 添加操作从这里执行if (this.action === 'add') {if (valid) {const reg = /^\d+$/if (reg.test(classData.sort)) {addCategory({'name': classData.name,'type':this.type, sort: classData.sort}).then(res => {console.log(res)if (res.code === 1) {this.$message.success('分类添加成功!')if (!st) {this.classData.dialogVisible = false} else {this.classData.name = ''this.classData.sort = ''}this.handleQuery()} else {this.$message.error(res.msg || '操作失败')}}).catch(err => {this.$message.error('请求出错了:' + err)})} else {this.$message.error('排序只能输入数字类型')}} else {this.$message.error('请输入分类名称或排序')}// 修改操作从这里执行} else if (valid) {const reg = /^\d+$/if (reg.test(this.classData.sort)) {// 发起ajax请求修改员工数据editCategory({'id':this.classData.id,'name': this.classData.name, sort: this.classData.sort}).then(res => {if (res.code === 1) {this.$message.success('分类修改成功!')this.classData.dialogVisible = falsethis.handleQuery()} else {this.$message.error(res.msg || '操作失败')}}).catch(err => {this.$message.error('请求出错了:' + err)})} else {this.$message.error('排序只能输入数字类型')}} else {this.$message.error('请输入分类名称或排序')}}// 修改接口const editCategory = (params) => {return $axios({url: '/category',method: 'put',data: { ...params }})}
</script>
后端处理请求
根据用户提交的json格式的菜品/套餐分类信息
,去数据表中更新对应的菜品/套餐记录
@PutMapping
public Result<String> update(@RequestBody Category category) {log.info("修改分类信息为:{}", category);categoryService.updateById(category);return Result.success("修改分类信息成功");
}