Skip to content

套餐管理

课程内容

  • 套餐分页查询
  • 启售停售套餐
  • 删除套餐
  • 新增套餐

1. 套餐分页查询

1.1 需求分析和接口设计

根据产品原型来了解需求,套餐分页查询的产品原型如下:

image-20231019100236805

业务规则:

  • 根据页码展示套餐信息(套餐名称、套餐图片、套餐分类、价格、售卖状态、最后操作时间等)
  • 每页展示10条数据
  • 分页查询时可以根据需要,输入套餐名、套餐分类、售卖状态 进行查询

要展示套餐分页数据,就需要前后端进行数据交互,对应的接口有两个:

  • 分类查询接口(用于套餐分类下拉框中分类数据展示)
  • 套餐分页查询接口

(1)分类查询接口

基本信息

Path: /admin/category/list

Method: GET

请求参数

Query

参数名称 是否必须 示例 备注
type 2 分类类型:1为菜品分类,2为套餐分类

返回数据

名称 类型 是否必须 默认值 备注 其他信息
code integer 必须 format: int32
data object [] 非必须 item 类型: object
├─ createTime string 非必须 format: date-time
├─ createUser integer 非必须 format: int64
├─ id integer 非必须 format: int64
├─ name string 非必须
├─ sort integer 非必须 format: int32
├─ status integer 非必须 format: int32
├─ type integer 非必须 format: int32
├─ updateTime string 非必须 format: date-time
├─ updateUser integer 非必须 format: int64
msg string 非必须

(2)套餐分页查询接口

基本信息

Path: /admin/setmeal/page

Method: GET

请求参数

Query

参数名称 是否必须 示例 备注
categoryId 分类id
name 套餐名称
page 页码
pageSize 每页记录数
status 套餐起售状态

返回数据

名称 类型 是否必须 默认值 备注 其他信息
code number 必须
msg null 非必须
data object 非必须
├─ total number 非必须
├─ records object [] 非必须 item 类型: object
├─ id number 必须
├─ categoryId number 必须
├─ name string 必须
├─ price number 必须
├─ status number 必须
├─ description string 必须
├─ image string 必须
├─ updateTime string 必须
├─ categoryName string 必须

1.2 代码开发

要开发前端代码,首先需要找到对应的组件。从路由文件 router.ts 中找到套餐管理页面(组件)。

image-20231020143129575

可以看到,套餐管理页面(组件)的位置为:src/views/setmeal/index.vue。我们只需要在此文件中开发套餐分页查询相关的前端代码即可,整个开发过程大概可以分为以下几个关键步骤:

  1. 根据产品原型,制作页面头部效果(输入框、下拉框、查询按钮等)
  2. 动态填充套餐分类下拉框中的分类数据
  3. 为查询按钮绑定单击事件,发送Ajax请求,查询套餐分页数据,实现前后端交互
  4. 提供 vue 的初始化方法,在页面加载后就查询分页数据
  5. 使用ElementUI提供的表格组件展示分页数据
  6. 使用ElementUI提供的分页条组件实现翻页效果

注意:开发过程中,并不是所有的代码都实现了再测试,而是开发一部分,就需要测试一下,看效果,如果有问题再调整。没有问题,再继续开发、测试。所以,这是一个逐渐完善的过程。下面我们就按照上面的几个关键步骤来开发,每开发完一个关键步骤,就需要测试一下,来验证我们的代码是否正确。

2.2.1 制作页面头部效果

根据产品原型,制作页面头部效果(输入框、查询按钮等)。产品原型中的头部效果如下:

image-20231020153003427

注意:输入框和按钮都是使用 ElementUI 提供的组件,对于前端的组件只需要参考 ElementUI 提供的文档,进行修改即可。实现代码如下:

image-20231020153413719

注意:当前套餐分类下拉框中的数据是直接在页面固定写死的,后续需要改为从后端动态获取。

2.2.2 动态填充套餐分类下拉框数据

现在需要将套餐分类下拉框中的数据改为动态获取,即前端需要发送Ajax请求,调用后端的分类查询接口,然后将后端返回的套餐分类数据动态展示在下拉框中。因为本次前后端交互是需要查询分类数据,所以按照项目规范,发送Ajax请求的代码需要定义到 src/api/category.ts 文件中。其实在此文件中已经定义了此方法,如下:

image-20231020153956869

所以,此处只需要将此方法(getCategoryByType)导入当前组件,然后在 created 方法中调用此方法,获取套餐分类数据,动态填充套餐分类下拉框即可。具体代码如下:

image-20231020154449782

注意:因为此处我们要查询的是套餐分类,所以传递的参数type值为2。

前面我们已经初步实现了页面头部制作,并且可以填充下拉框中的数据了。但是命名上并不是特别规范,所以我们需要进行一个调整,具体修改后端的代码如下:

image-20231020154747092

两个下拉框的测试效果如下:

image-20231020154909513

2.2.3 动态获取套餐分页数据

前面我们已经完成了页面头部效果开发,接下来就需要开发前后端数据交互的动态效果。

第一步:为查询按钮绑定单击事件

image-20231020155054149

第二步:在methods中定义 pageQuery 方法,先验证当前方法能否正常执行

image-20231020155113565

注意:按照开发规范,真正发送Ajax请求的代码需要封装到 api目录下的ts文件中(src/api/setMeal.ts)

第三步:在src/api/setMeal.ts 中定义 getSetmealPage 方法,实现发送Ajax请求获取分页数据

image-20231020155235487

注意:发送 Ajax 请求的URL地址需要和前面我们设计的分页查询接口对应

第四步:在套餐管理组件中导入 setMeal.ts 中定义的方法,并在data() 方法中定义分页相关的模型数据

image-20231020155347403

注意:需要将属性和上面的输入框、下拉框进行双向绑定。

第五步:在pageQuery 方法中调用 getSetmealPage方法,实现前后端数据交互

image-20231020155526957

2.2.4 自动发送Ajax请求

前面的代码我们已经实现了前后端数据交互,但是有一个问题,就是只有在点击查询按钮时才会发生Ajax请求,实现分页数据查询。我们通常需要的是在当前页面(组件)加载后,就需要发送Ajax请求,查询第一页的数据。要实现这个效果,我们可以通过vue的生命周期方法,即created方法来做到,代码如下:

image-20231020155637956

2.2.5 使用表格展示分页数据

前面我们已经实现了前后端数据交互,现在就需要将后端返回的数据通过表格展示出来,我们可以使用ElementUI提供的表格组件,具体使用方法可以参照官方提供的示例 https://element.eleme.io/#/zh-CN/component/table

image-20231020155734122

2.2.6 使用分页条实现翻页效果

使用 ElementUI 提供的分页条组件,并绑定事件处理函数,具体使用方法可以参照官方提供的示例 https://element.eleme.io/#/zh-CN/component/pagination

image-20231020155812305

1.3 功能测试

可以通过下面两种方式来测试:

  • 直接进行前后端联调,查看页面效果
  • 通过浏览器F12查看数据交互过程

image-20231020155938508

2. 启售停售套餐

2.1 需求分析和接口设计

根据产品原型来进行需求分析:

image-20231023094228836

可以对状态为“启售” 的套餐进行“停售”操作

可以对状态为“停售”的套餐进行“启售”操作

状态为“停售”的套餐不展示在用户端小程序中,所以用户不能购买停售的套餐

接口设计如下:

基本信息

Path: /admin/setmeal/status/{status}

Method: POST

请求参数

Headers

参数名称 参数值 是否必须 示例 备注
Content-Type application/json

路径参数

参数名称 示例 备注
status 1 套餐状态,1表示起售,0表示停售

Query

参数名称 是否必须 示例 备注
id 101 套餐id

返回数据

名称 类型 是否必须 默认值 备注 其他信息
code integer 必须 format: int32
data object 非必须
msg string 非必须

2.2 代码开发

第一步:为启售停售按钮绑定单击事件

image-20231023095123819

第二步:编写对应的处理函数handleStartOrStop

image-20231023095249208

到此可以先测试一下,检查当前方法能否成功执行,页面效果如下:

image-20231023095322300

第三步:在 setMeal.ts 中封装套餐起售停售方法,发送Ajax请求

image-20231023095630819

注意:发送请求的方式和相关参数,必须和前面的接口设计保持一致

第四步:在套餐管理组件中引入上面定义的enableOrDisableSetmeal方法,并完善 handleStartOrStop 方法

image-20231023095730185

注意:

  • 在进行套餐启售停售操作时,建议先弹出确认框,用户点击确定按钮后再进行前后端交互
  • 在传递套餐状态参数status时,需要进行简单的处理,即:如果当前套餐状态值为1,则传递过去的参数为0;如果当前套餐状态值为0,则传递过去的参数为1

2.3 功能测试

直接进行前后端联调,查看页面效果

通过浏览器F12查看数据交互过程

image-20231023100008708

3. 删除套餐

3.1 需求分析和接口设计

根据产品原型来进行需求分析,产品原型如下:

image-20231023142125944

  • 点击 删除 按钮,删除指定的一个套餐
  • 勾选需要删除的套餐,点击 批量删除 按钮,删除选中的一个或多个套餐
  • 状态为 “启售” 的套餐不能删除,需要给出操作提示

可以看到,删除套餐功能在操作时有两种方式。一种是点击【删除】按钮,可以删除对应的一个套餐;一种是勾选需要删除的套餐,然后点击【批量删除】按钮,可以删除勾选的多个套餐。我们在设计接口时可以兼容这两种不同的操作方式,也就是只需要一个接口即可。

接口设计如下:

基本信息

Path: /admin/setmeal

Method: DELETE

请求参数

Query

参数名称 是否必须 示例 备注
ids 1,2,3 ids

返回数据

名称 类型 是否必须 默认值 备注 其他信息
code integer 非必须 format: int32
data object 非必须
msg string 非必须

3.2 代码开发

要开发删除套餐前端代码,首先需要了解删除套餐业务功能的操作步骤:

  1. 在套餐管理列表页面,点击 【删除】按钮,或者勾选套餐然后点击【批量删除】按钮,弹出确认对话框
  2. 点击确认对话框中的【确定】按钮,则执行删除操作。如果套餐状态为“启售”,则不能删除,弹出信息提示
  3. 点击确认对话框中的【取消】按钮,则关闭对话框,不执行删除操作

接下来我们就可以按照上面的操作步骤来具体开发前端的代码。

第一步:在 setMeal.ts 中封装删除套餐方法,发送Ajax请求,用于实现前后端交互

image-20231023163921668

注意:

  • 发送请求的方式和相关参数,必须和前面的接口设计保持一致
  • 使用此方法时,别忘了在组件中通过import导入

第二步:为【批量删除】按钮绑定单击事件,并在methods中编写对应的处理函数

image-20231023164110268

image-20231023164255891

到目前为止我们点击【批量删除】按钮,是可以执行handleDelete方法的。接下来我们需要解决一个问题,就是当前选中了哪些套餐呢?我们需要能够动态获取到,因为我们需要将这些套餐的id作为参数传递到后端。

第三步:参考 ElementUI 的官方文档,为表格组件添加 selection-change 事件和对应的处理函数,通过此事件我们就可以动态获取到当前勾选的套餐有哪些

image-20231023165937804

image-20231023170202159

注: selection-change 事件为 当选择项发生变化时触发的事件

第四步:完善 handleDelete 方法,获取当前被选中的行,并进行参数准备

image-20231023170312864

注意:单个删除 和 批量删除,都是调用 handleDelete 方法,所以此方法还需要进一步调整

第五步:为【删除】按钮绑定单击事件,处理函数还是 handleDelete

image-20231023170440585

注意:

  • 在 handleDelete 方法中通过第一个参数来区分是单个删除还是批量删除
  • S表示单个删除(single),B表示批量删除(batch)

第六步:调整 handleDelete 方法,使其兼容单个删除和批量删除

image-20231023170612705

第七步:完善 handleDelete 方法,进行相应提示

image-20231023170710212

注:

  • 批量删除时,如果没有选中套餐,给出提示
  • 删除之前需要弹出确认框,让用户确认

3.3 功能测试

直接进行前后端联调,查看页面效果

通过浏览器F12查看数据交互过程

image-20231023170832442

4. 新增套餐

4.1 需求分析和接口设计

根据产品原型来进行需求分析,产品原型如下:

image-20231025113638867

image-20231025113707572

新增套餐时需要录入套餐名称、所属分类、套餐价格、套餐包含的菜品、套餐图片、描述等信息。其中套餐包含的菜品需要在弹出的添加菜品窗口中勾选。在弹出的添加菜品窗口中需要按照分类来展示菜品。

新增套餐功能涉及到4个接口,分别是:

  • 根据类型查询分类 接口
  • 根据分类查询菜品 接口
  • 文件上传 接口
  • 新增套餐 接口

(1) 根据类型查询分类 接口

基本信息

Path: /admin/category/list

Method: GET

请求参数

Query

参数名称 是否必须 示例 备注
type 2 分类类型:1为菜品分类,2为套餐分类

返回数据

名称 类型 是否必须 默认值 备注 其他信息
code integer 必须 format: int32
data object [] 非必须 item 类型: object
├─ createTime string 非必须 format: date-time
├─ createUser integer 非必须 format: int64
├─ id integer 非必须 format: int64
├─ name string 非必须
├─ sort integer 非必须 format: int32
├─ status integer 非必须 format: int32
├─ type integer 非必须 format: int32
├─ updateTime string 非必须 format: date-time
├─ updateUser integer 非必须 format: int64
msg string 非必须

(2) 根据分类查询菜品 接口

基本信息

Path: /admin/dish/list

Method: GET

请求参数

Query

参数名称 是否必须 示例 备注
categoryId 101 分类id

返回数据

名称 类型 是否必须 默认值 备注 其他信息
code integer 必须 format: int32
data object [] 非必须 item 类型: object
├─ categoryId integer 非必须 format: int64
├─ createTime string 非必须 format: date-time
├─ createUser integer 非必须 format: int64
├─ description string 非必须
├─ id integer 非必须 format: int64
├─ image string 非必须
├─ name string 非必须
├─ price number 非必须
├─ status integer 非必须 format: int32
├─ updateTime string 非必须 format: date-time
├─ updateUser integer 非必须 format: int64
msg string 非必须

(3) 文件上传 接口

基本信息

Path: /admin/common/upload

Method: POST

请求参数

Headers

参数名称 参数值 是否必须 示例 备注
Content-Type multipart/form-data

Body

参数名称 参数类型 是否必须 示例 备注
file file 文件

返回数据

名称 类型 是否必须 默认值 备注 其他信息
code integer 必须 format: int32
data string 必须 文件上传路径
msg string 非必须

(4) 新增套餐 接口

基本信息

Path: /admin/setmeal

Method: POST

接口描述:

请求参数

Headers

参数名称 参数值 是否必须 示例 备注
Content-Type application/json

Body

名称 类型 是否必须 默认值 备注 其他信息
categoryId integer 必须 分类id format: int64
description string 非必须 套餐描述
id integer 非必须 套餐id format: int64
image string 必须 套餐图片
name string 必须 套餐名称
price number 必须 套餐价格
setmealDishes object [] 必须 套餐包含的菜品 item 类型: object
├─ copies integer 必须 份数 format: int32
├─ dishId integer 必须 菜品id format: int64
├─ id integer 非必须 套餐和菜品关系id format: int64
├─ name string 必须 菜品名称
├─ price number 必须 菜品价格
├─ setmealId integer 必须 套餐id format: int64
status integer 必须 套餐状态:1位起售 0为停售 format: int32

返回数据

名称 类型 是否必须 默认值 备注 其他信息
code integer 必须 format: int32
data object 非必须
msg string 非必须

4.2 代码解读

新增套餐操作步骤:

①点击 “新建套餐”按钮,跳转到新增页面

②在新增套餐页面录入套餐相关信息

③点击“保存”按钮完成新增操作

首先需要找到新增套餐页面,可以通过操作过程来找:

第一步:在套餐管理列表页面中找到【新建套餐】按钮,查看按钮绑定的事件和对应的处理函数

image-20231026101005983

第二步:在methods中找到handleAdd函数,查看跳转的路由路径

image-20231026101037315

第三步:在路由文件中找到此路径对应的视图组件,可以看到是src/views/setmeal/addSetmeal.vue

image-20231026101135442

第四步:解读src/views/setmeal/addSetmeal.vue这个文件即可 总的来说,开发内容在前面都几乎都讲过的,因此这里可以略过了.不过这里草率地讲了一下自定义组件,这样可以解耦一些复杂的界面.这些后面有需要的时候我们自己再看吧.(主要是什么继承vue,什么@component装饰器等等,最终就可以把自定义组件像elementui提供的组件一样直接通过标签来使用)

4.3 功能测试

直接进行前后端联调,查看页面效果

通过浏览器F12查看数据交互过程