前端环境搭建:
技术选型:
使用的前端技术栈:node.js、vue、ElementUI、axios、vuex、vue-router、typescript
代码结构:
核心目录 / 文件:
| 目录 / 文件 | 说明 |
|---|---|
| apki | 封装 Ajax 请求的文件目录 |
| components | 公共组件存放目录 |
| views | 视图组件存放目录 |
| App.vue | 项目主组件、页面入口文件 |
| main.ts | 整个项目的入口文件 |
| router.ts | 路由配置文件 |
环境准备:
安装依赖包(生成 node_modules 目录):
bash
npm install
启动前端项目(需同时启动后端 Java 服务):
bash
npm run serve
员工管理:
员工分页查询:
需求分析和接口设计:

代码开发:
步骤一:制作页面头部

html
<div class="tableBar">
<label style="margin-right: 5px">
员工姓名:
</label>
<el-input placeholder="请输入员工姓名" style="width: 15%" />
<el-button type="primary" style="margin-left: 20px">查询</el-button>
<el-button type="primary" style="float: right">+添加员工</el-button>
</div>
说明:输入框和按钮均使用 ElementUI 提供的组件,可参考其官方文档进行修改
步骤二:实现前后端数据交互
绑定查询事件:为查询按钮添加 @click="pageQuery()" 事件
html
<el-button type="primary" style="margin-left: 20px" @click="pageQuery()">查询</el-button>
定义查询方法:在 methods 中定义 pageQuery 方法,验证方法能否正常执行
TypeScript
<script lang="ts">
export default {
methods: {
//分页查询
pageQuery() {
//验证当前方法能否成功执行
alert(1)
}
}
}
</script>
封装 API 请求:在 src/api/employee.ts 中定义 getEmployeeList 方法,用于发送 Ajax 请求获取分页数据
TypeScript
//分页查询
export const getEmployeeList = (params: any) => {
return request({
url: '/employee/page',
method: 'get',
params: params
})
}
导入 API 并定义模型数据:在员工管理组件中导入 getEmployeeList 方法,并在 data() 中定义分页相关的模型数据
TypeScript
import { getEmployeeList } from '@/api/employee'
export default {
//模型数据
data() {
return {
name: '', //员工姓名,对应上面的输入框
page: 1, //页码
pageSize: 10, //每页记录数
total: 0, //总记录数
records: [] //当前页要展示的数据集合
}
}
}
双向绑定输入框:将 name 属性与员工姓名输入框进行双向绑定
html
<el-input v-model="name" placeholder="请输入员工姓名" style="width: 15%" clearable />
完善查询方法:在 pageQuery 方法中调用 getEmployeeList 方法,处理返回数据
TypeScript
//分页查询
pageQuery() {
// 准备参数
const params = {
page: this.page,
pageSize: this.pageSize,
name: this.name
}
//发送请求
getEmployeeList(params)
.then((res) => {
//解析结果
if (res.data.code === 1) {
this.records = res.data.data.records
this.total = res.data.data.total
}
})
.catch((err) => {
this.$message.error('请求出错了: ' + err.message)
})
}
步骤三:自动发送 Ajax 请求
使用 Vue 的 created 生命周期钩子,可以在组件加载后自动发送 Ajax 请求,查询第一页数据
TypeScript
//声明周期方法
created() {
this.pageQuery()
}
步骤四:使用表格展示分页数据
使用 ElementUI 的表格组件展示后端返回的员工数据
html
<el-table :data="records" stripe class="tableBox">
<el-table-column prop="name" label="员工姓名" />
<el-table-column prop="username" label="账号" />
<el-table-column prop="phone" label="手机号" />
<el-table-column prop="status" label="账号状态">
<template slot-scope="scope">
<span :class="scope.row.status === 0 ? 'stopUse' : 'stopUse'">
{{ scope.row.status === 0 ? '禁用' : '启用' }}
</span>
</template>
</el-table-column>
<el-table-column prop="updateTime" label="最后操作时间" />
<el-table-column prop="操作" label="操作" align="center">
<template slot-scope="scope">
<el-button size="small" type="text">修改</el-button>
<el-button size="small" type="text">
{{ scope.row.status === 1 ? '禁用' : '启用' }}
</el-button>
<el-button size="small" type="text">删除</el-button>
</template>
</el-table-column>
</el-table>
步骤五:使用分页条实现翻页效果
官方示例:https://element.eleme.io/#/zh-CN/component/pagination
分页组件代码:
html
<el-pagination
class="pageList"
:page-sizes="[10, 20, 30, 40, 50]"
:page-size="pageSize"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
事件处理函数:
TypeScript
//分页条的事件处理函数,pageSize改变时会触发
handleSizeChange(pageSize) {
this.pageSize = pageSize
this.pageQuery()
},
//分页条的事件处理函数,currentPage改变时会触发
handleCurrentChange(currentPage) {
this.page = currentPage
this.pageQuery()
}
启用禁用员工账号:
需求分析和接口设计:

代码开发:
步骤一:绑定按钮单击事件
为表格中的 "启用 / 禁用" 按钮绑定 handleStartOrStop 事件,并根据当前状态动态显示按钮文字
html
<el-button type="text" size="small" @click="handleStartOrStop(scope.row)">
{{ scope.row.status == '1' ? '禁用' : '启用' }}
</el-button>
步骤二:编写对应的处理函数
在 methods 中定义 handleStartOrStop 方法,验证方法能否成功执行
TypeScript
//启用、禁用员工账号
handleStartOrStop(row) {
alert(`id=${row.id} status=${row.status}`)
}
步骤三:封装 API 请求
在 src/api/employee.ts 中定义 enableOrDisableEmployee 方法,用于发送 Ajax 请求更新员工状态
TypeScript
//启用禁用员工账号
export const enableOrDisableEmployee = (params: any) => {
return request({
url: `/employee/status/${params.status}`,
method: 'post',
params: { id: params.id }
})
}
步骤四:完善处理函数
在员工管理组件中导入 enableOrDisableEmployee 方法,并完善 handleStartOrStop 方法,添加确认弹窗和状态更新逻辑
TypeScript
//启用、禁用员工账号
handleStartOrStop(row) {
this.$confirm('确认调整该账号的状态?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}).then(() => {
enableOrDisableEmployee({ id: row.id, status: !row.status ? 1 : 0 })
.then((res) => {
if (res.status === 200) {
this.$message.success('账号状态更改成功!')
this.pageQuery() //刷新数据
}
})
.catch((err) => {
this.$message.error('请求出错了: ' + err.message)
})
})
}
步骤五:代码优化
在 handleStartOrStop 方法中添加判断,如果是管理员账号则不允许修改状态并给出提示
TypeScript
//启用、禁用员工账号
handleStartOrStop(row) {
if (row.username === 'admin') {
this.$message.error('admin为管理员账号,不能更改账号状态!')
return
}
this.$confirm('确认调整该账号的状态?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}).then(() => {
enableOrDisableEmployee({ id: row.id, status: !row.status ? 1 : 0 })
.then((res) => {
if (res.status === 200) {
this.$message.success('账号状态更改成功!')
this.pageQuery()
}
})
.catch((err) => {
this.$message.error('请求出错了: ' + err.message)
})
})
}
新增员工:
需求分析和接口设计:
产品原型:


代码开发:
步骤一:为 "添加员工" 按钮绑定单击事件
html
<div class="tableBar">
<label style="margin-right: 5px">员工姓名: </label>
<el-input
v-model="name"
placeholder="请输入员工姓名"
style="width: 15%"
clearable
/>
<el-button type="primary" style="margin-left: 20px" @click="pageQuery()">查询</el-button>
<el-button type="primary" style="float: right" @click="handleAddEmp">+ 添加员工</el-button>
</div>
步骤二:编写 handleAddEmp 方法,进行路由跳转
TypeScript
//添加员工,跳转至添加员工页面(组件)
handleAddEmp() {
this.$router.push('/employee/add')
}
路由配置(已在路由文件中定义):
TypeScript
{
path: "/employee/add",
component: () => import("@/views/employee/addEmployee.vue"),
meta: {
title: "添加/修改员工",
hidden: true
}
}
步骤三:开发新增页面表单元素
html
<template>
<div class="addBrand-container">
<div class="container">
<el-form :model="ruleForm" :rules="rules" ref="ruleForm" label-width="180px">
<el-form-item label="账号" prop="username">
<el-input v-model="ruleForm.username"></el-input>
</el-form-item>
<el-form-item label="员工姓名" prop="name">
<el-input v-model="ruleForm.name"></el-input>
</el-form-item>
<el-form-item label="手机号" prop="phone">
<el-input v-model="ruleForm.phone"></el-input>
</el-form-item>
<el-form-item label="性别" prop="sex">
<el-radio v-model="ruleForm.sex" label="1">男</el-radio>
<el-radio v-model="ruleForm.sex" label="2">女</el-radio>
</el-form-item>
<el-form-item label="身份证号" prop="idNumber">
<el-input v-model="ruleForm.idNumber"></el-input>
</el-form-item>
<div class="subBox">
<el-button type="primary" @click="submitForm('ruleForm',false)">保存</el-button>
<el-button type="primary" @click="submitForm('ruleForm',true)">保存并继续添加员工</el-button>
<el-button @click="() => this.$router.push('/employee')">返回</el-button>
</div>
</el-form>
</div>
</div>
</template>
步骤四:定义模型数据和表单校验规则
TypeScript
export default {
data() {
return {
ruleForm: {
name: '',
username: '',
sex: '1',
phone: '',
idNumber: ''
},
rules: {
name: [
{ required: true, message: '请输入员工姓名', trigger: 'blur' }
],
username: [
{ required: true, message: '请输入账号', trigger: 'blur' }
],
phone: [
{ required: true, trigger: 'blur', validator: (rule, value, callback) => {
if (value === '' || !(/^1[3|4|5|6|7|8]\d{9}$/.test(value))) {
callback(new Error('请输入正确的手机号'));
} else {
callback()
}
}}
],
idNumber: [
{ required: true, trigger: 'blur', validator: (rule, value, callback) => {
if (value === '' || !(/(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/.test(value))) {
callback(new Error('请输入正确的身份证号'));
} else {
callback()
}
}}
]
}
}
}
}
步骤五:在 employee.ts 中封装新增员工方法
TypeScript
//新增员工
export const addEmployee = (params: any) => {
return request({
url: '/employee',
method: 'post',
data: params
})
}
步骤六:定义提交表单的方法 submitForm:
TypeScript
methods: {
//提交表单数据
submitForm(formName, isContinue) {
//表单数据校验
this.$refs[formName].validate((valid) => {
if (valid) {
addEmployee(this.ruleForm)
.then((res: any) => {
if (res.data.code === 1) {
this.$message.success('员工添加成功!')
if (isContinue) {
this.$router.push({ path: '/employee/add' })
} else {
this.ruleForm = {
username: '',
name: '',
phone: '',
sex: '1',
idNumber: ''
}
}
} else {
this.$message.error(res.data.msg)
}
})
}
});
}
}
修改员工:
需求分析和接口设计:


代码开发:
步骤一:为 "修改" 按钮绑定单击事件
html
<el-button type="text" size="small" @click="handleUpdateEmp(scope.row)">
修改
</el-button>
步骤二:编写 handleUpdateEmp 方法,实现路由跳转
在员工列表组件的 methods 中定义跳转方法,并对管理员账号进行保护:
TypeScript
//修改员工,跳转至修改员工页面(组件)
handleUpdateEmp(row) {
if (row.username === 'admin') {
//如果是内置管理员账号,则不允许修改
this.$message.error('admin为管理员账号,不能修改!')
return
}
//跳转到修改页面,通过地址栏传递参数
this.$router.push({ path: '/employee/add', query: { id: row.id } })
}
地址栏传递参数:this.$router.push({path: 路由路径, query:{参数名:参数值}})
步骤三:在 addEmployee.vue 中定义操作类型并区分新增 / 修改
在组件的 data() 中定义 optType 用于区分操作类型,并在 created 生命周期中根据路由参数判断:
TypeScript
<script lang="ts">
import { addEmployee } from '@/api/employee'
export default {
data() {
return {
optType: '', //当前操作类型:新增(add)或者修改(update)
ruleForm: { /* ... */ },
rules: { /* ... */ }
};
},
created() {
//获取路由参数,如果有则为修改操作,否则为新增操作
this.optType = this.$route.query.id ? 'update' : 'add'
},
methods: { /* ... */ }
}
</script>
获取路由参数:this.$router.query.参数名
步骤四:在 employee.ts 中封装根据 ID 查询员工的方法
用于修改操作时的数据回显:
TypeScript
//根据id查询员工
export const queryEmployeeById = (id: number) => {
return request({
url: `/employee/${id}`,
method: 'get'
})
}
步骤五:在 addEmployee.vue 中实现数据回显
在 created 方法中,如果是修改操作,则调用查询方法回显数据:
TypeScript
created() {
//获取路由参数,如果有则为修改操作,否则为新增操作
this.optType = this.$route.query.id ? 'update' : 'add'
if (this.optType === 'update') {
//修改操作,需要根据id查询原始数据,用于回显
queryEmployeeById(this.$route.query.id)
.then((res) => {
if (res.data.code === 1) {
this.ruleForm = res.data.data
}
})
}
}
步骤六:控制 "保存并继续添加员工" 按钮的显示
在模板中使用 v-if 指令,仅在新增操作时显示该按钮:
html
<div class="subBox">
<el-button type="primary" @click="submitForm('ruleForm',false)">保存</el-button>
<el-button
v-if="this.optType === 'add'"
type="primary"
@click="submitForm('ruleForm',true)"
>保存并继续添加员工</el-button>
<el-button @click="() => this.$router.push('/employee')">返回</el-button>
</div>
步骤七:在 employee.ts 中封装修改员工的方法
TypeScript
//修改员工
export const updateEmployee = (params: any) => {
return request({
url: '/employee',
method: 'put',
data: params
})
}
步骤八:修改 submitForm 方法,区分新增和修改操作
在组件的 methods 中,根据 optType 执行不同的请求:
TypeScript
submitForm(formName, isContinue) {
//表单数据校验
this.$refs[formName].validate((valid) => {
if (valid) {
//根据操作类型执行新增或者修改操作
if (this.optType === 'add') { //新增操作
addEmployee(this.ruleForm)
.then((res: any) => { /* ... */ })
} else { //修改操作
updateEmployee(this.ruleForm)
.then((res: any) => {
if (res.data.code === 1) {
this.$message.success('员工修改成功!')
this.$router.push({ path: '/employee' })
} else {
this.$message.error(res.data.msg)
}
})
}
}
});
}
套餐管理:
套餐分页查询:
需求分析和接口设计:
产品原型:



代码开发:
步骤一:制作页面头部效果
html
<div class="tableBar">
<label style="margin-right: 10px">套餐名称: </label>
<el-input v-model="name" style="width: 14%" clearable />
<label style="margin-right: 10px; margin-left: 20px">套餐分类: </label>
<el-select v-model="categoryId" placeholder="请选择">
<el-option
v-for="item in options"
:key="item.id"
:label="item.name"
:value="item.id"
/>
</el-select>
<label style="margin-right: 10px; margin-left: 20px">售卖状态: </label>
<el-select v-model="status" style="width: 14%" placeholder="请选择" clearable>
<el-option
v-for="item in statusArr"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
<el-button type="primary" style="margin-left: 20px">查询</el-button>
<div style="float: right">
<el-button type="danger">批量删除</el-button>
<el-button type="info">+ 新建套餐</el-button>
</div>
</div>
模型数据定义:
TypeScript
export default {
data() {
return {
name: '',
categoryId: '', // 分类id
status: '', // 售卖状态
options: [], // 为套餐分类下拉框提供的选项
statusArr: [ // 为售卖状态下拉框提供的数据
{
value: '1',
label: '启售'
}, {
value: '0',
label: '停售'
}
]
}
}
}
步骤二:动态填充套餐分类下拉框数据
封装 API 请求:在 src/api/category.ts 中已定义 getCategoryByType 方法,用于根据类型查询分类
TypeScript
//根据类型查询分类:1为菜品分类 2为套餐分类
export const getCategoryByType = (params: any) => {
return request({
url: '/category/list',
method: 'get',
params: params
})
}
导入并调用 API:在套餐管理组件中导入该方法,并在 created 生命周期中调用,动态填充下拉框
TypeScript
<script lang="ts">
import { getCategoryByType } from '@/api/category'
export default {
data() { /* ... */ },
created() {
//查询套餐分类,用于填充查询页面的下拉框
getCategoryByType({type: 2})
.then((res) => {
if (res.data.code === 1) {
this.options = res.data.data
}
})
}
}
</script>
步骤三:动态获取套餐分页数据
绑定查询事件:为查询按钮添加 @click="pageQuery" 事件
html
<el-button type="primary" style="margin-left: 20px" @click="pageQuery()">查询</el-button>
封装 API 请求:在 src/api/setMeal.ts 中定义 getSetmealPage 方法,用于发送 Ajax 请求获取套餐分页数据
TypeScript
//套餐分页查询
export const getSetmealPage = (params: any) => {
return request({
url: '/setmeal/page',
method: 'get',
params: params
})
}
导入 API 并定义模型数据:在套餐管理组件中导入 getSetmealPage 方法,并在 data() 中定义分页相关的模型数据
TypeScript
import { getSetmealPage } from '@/api/setMeal'
export default {
data() {
return {
page: 1, //页码
pageSize: 10, //每页记录数
total: 0, //总记录数
records: [], //当前页要展示的数据集合
name: '',
categoryId: '',
status: '',
options: [],
statusArr: [ /* ... */ ]
}
}
}
完善查询方法:在 pageQuery 方法中调用 getSetmealPage 方法,处理返回数据
TypeScript
//套餐分页查询
pageQuery() {
//封装分页查询参数
const params = {
page: this.page,
pageSize: this.pageSize,
name: this.name,
status: this.status,
categoryId: this.categoryId
}
//调用分页查询接口
getSetmealPage(params)
.then((res) => {
if (res.data.code === 1) {
this.total = res.data.data.total
this.records = res.data.data.records
}
})
}
步骤四:自动发送 Ajax 请求
在 created 生命周期中调用 pageQuery 方法,可以在组件加载后自动发送 Ajax 请求,查询第一页数据
TypeScript
created() {
//查询套餐分类,用于填充查询页面的下拉框
getCategoryByType({type: 2})
.then((res) => {
if (res.data.code === 1) {
this.options = res.data.data
}
})
// 查询套餐分页数据
this.pageQuery()
}
步骤五:使用表格展示分页数据
官方示例:https://element.eleme.io/#/zh-CN/component/table
html
<el-table :data="records" stripe class="tableBox">
<el-table-column prop="image" label="图片" width="80px">
<template slot-scope="scope">
<el-image style="width: 80px; height: 40px; border: none" :src="scope.row.image"></el-image>
</template>
</el-table-column>
<el-table-column prop="name" label="套餐名称" />
<el-table-column prop="price" label="套餐价" />
<el-table-column prop="categoryName" label="套餐分类" />
<el-table-column label="售卖状态">
<template slot-scope="scope">
<div :class="scope.row.status === 0 ? 'stopUse' : 'stopUse'">
{{ scope.row.status === 0 ? '停售' : '启售' }}
</div>
</template>
</el-table-column>
<el-table-column prop="updateTime" label="最后操作时间" />
<el-table-column label="操作" align="center">
<template slot-scope="scope">
<el-button type="text" size="small">修改</el-button>
<el-button type="text" size="small">
{{ scope.row.status === 1 ? '停售' : '启售' }}
</el-button>
<el-button type="text" size="small">删除</el-button>
</template>
</el-table-column>
</el-table>
步骤六:使用分页条实现翻页效果
官方示例:https://element.eleme.io/#/zh-CN/component/pagination
分页组件代码:
html
<el-pagination
background
class="pageList"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="page"
:page-sizes="[10, 20, 30, 40, 50]"
:page-size="pageSize"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
>
</el-pagination>
事件处理函数:
TypeScript
//分页条的事件处理函数,pageSize改变时会触发
handleSizeChange(pageSize) {
this.pageSize = pageSize
this.pageQuery()
},
//分页条的事件处理函数,currentPage改变时会触发
handleCurrentChange(page) {
this.page = page
this.pageQuery()
}
启售停售套餐:
需求分析和接口设计:
产品原型:


代码开发:
步骤一:绑定按钮单击事件
为表格中的 "起售 / 停售" 按钮绑定 handleStartOrStop 事件,并根据当前状态动态显示按钮文字
html
<el-button type="text" size="small" @click="handleStartOrStop(scope.row)">
{{ scope.row.status == '1' ? '停售' : '启售' }}
</el-button>
步骤二:编写对应的处理函数
在 methods 中定义 handleStartOrStop 方法,验证方法能否成功执行
TypeScript
//套餐起售、停售
handleStartOrStop(row) {
alert(`id=${row.id} status=${row.status}`)
}
步骤三:封装 API 请求
在 src/api/setMeal.ts 中定义 enableOrDisableSetmeal 方法,用于发送 Ajax 请求更新套餐状态
TypeScript
//套餐起售禁售
export const enableOrDisableSetmeal = (params: any) => {
return request({
url: `/setmeal/status/${params.status}`,
method: 'post',
params: { id: params.id }
})
}
步骤四:完善处理函数
在套餐管理组件中导入 enableOrDisableSetmeal 方法,并完善 handleStartOrStop 方法,添加确认弹窗和状态更新逻辑
TypeScript
//套餐起售、停售
handleStartOrStop(row) {
this.$confirm('确认调整该套餐的售卖状态?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}).then(() => {
enableOrDisableSetmeal({ id: row.id, status: !row.status ? 1 : 0 })
.then((res) => {
if (res.status === 200) {
this.$message.success('套餐售卖状态更改成功!')
this.pageQuery() // 刷新数据
}
})
.catch((err) => {
this.$message.error('请求出错了: ' + err.message)
})
})
}
删除套餐:
需求分析和接口设计:
产品原型:


代码开发:
步骤一:封装删除套餐的 API 方法
在 src/api/setMeal.ts 中定义 deleteSetmeal 方法,用于发送 Ajax 请求删除套餐
TypeScript
//删除套餐接口
export const deleteSetmeal = (ids: string) => {
return request({
url: '/setmeal',
method: 'delete',
params: { ids: ids }
})
}
步骤二:为 "批量删除" 按钮绑定事件
为批量删除按钮绑定 handleDelete 事件,验证方法执行
html
<el-button type="danger" @click="handleDelete">批量删除</el-button>
TypeScript
//删除套餐
handleDelete() {
alert('删除套餐')
}
步骤三:监听表格选择变化
为表格添加 selection-change 事件,动态获取当前勾选的套餐行
html
<el-table :data="records" stripe class="tableBox" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="25" />
<!-- 其他列 -->
</el-table>
在 data() 中定义存储选中行的数组:
TypeScript
data() {
return {
// ...其他数据
multipleSelection: [] //当前被选中的行
}
}
编写事件处理函数:
TypeScript
//当选择项发生变化时会触发该事件
handleSelectionChange(val) {
this.multipleSelection = val
//alert(this.multipleSelection.length)
}
步骤四:完善 handleDelete 方法,处理批量删除
在 handleDelete 方法中,获取选中的套餐 ID 并拼接成字符串
TypeScript
//删除套餐
handleDelete() {
const arr = new Array
this.multipleSelection.forEach(element => {
//将套餐id放入数组中
arr.push(element.id)
})
const ids = arr.join(',') //将数组中的id拼接到一起,中间用逗号分隔
alert(ids)
}
步骤五:为 "删除" 按钮绑定事件
为单个删除按钮绑定 handleDelete 事件,并通过参数区分操作类型
html
<el-button type="text" size="small" @click="handleDelete('S',scope.row.id)">删除</el-button>
步骤六:调整 handleDelete 方法,兼容单个和批量删除
修改 handleDelete 方法,根据传入的 type 参数(S 表示单个删除,B表示批量删除)执行不同逻辑
TypeScript
//删除套餐
handleDelete(type: string, id: string) {
let param = ''
//判断当前是单个删除还是批量删除
if (type === 'S') {
//单个删除
param = id
} else {
//批量删除
const arr = new Array
this.multipleSelection.forEach(element => {
//将套餐id放入数组中
arr.push(element.id)
})
param = arr.join(',') //将数组中的id拼接到一起,中间用逗号分隔
}
deleteSetmeal(param)
.then(res => {
if (res.data.code === 1) {
this.$message.success('删除成功!')
this.pageQuery()
} else {
this.$message.error(res.data.msg)
}
})
}
步骤七:完善 handleDelete 方法,添加提示和确认
TypeScript
//删除套餐
handleDelete(type: string, id: string) {
if (type === 'B' && this.multipleSelection.length === 0) {
this.$message('请选择需要删除的套餐!')
return
}
this.$confirm('确定删除该套餐?', '确定删除', {
confirmButtonText: '删除',
cancelButtonText: '取消',
type: 'warning',
}).then(() => {
let param = ''
//判断当前是单个删除还是批量删除
if (type === 'S') {
//单个删除
param = id
} else {
//批量删除
const arr = new Array
this.multipleSelection.forEach(element => {
//将套餐id放入数组中
arr.push(element.id)
})
param = arr.join(',') //将数组中的id拼接到一起,中间用逗号分隔
}
deleteSetmeal(param)
.then(res => {
if (res.data.code === 1) {
this.$message.success('删除成功!')
this.pageQuery()
} else {
this.$message.error(res.data.msg)
}
})
})
}
新增套餐:
需求分析和接口设计:
产品原型:






代码解读:
步骤一:找到新建套餐按钮及绑定事件
在套餐管理列表页面中,找到新建套餐按钮,其绑定的点击事件为 handleAdd:
html
<el-button type="info" @click="handleAdd"> + 新建套餐 </el-button>
步骤二:查看 handleAdd 方法的路由跳转逻辑
在 methods 中找到 handleAdd 方法,它通过路由跳转到新增套餐页面:
TypeScript
//新增套餐,跳转到新增页面(组件)
handleAdd() {
this.$router.push('/setmeal/add')
}
步骤三:在路由文件中定位对应组件
在路由配置文件中,路径 /setmeal/add 对应的视图组件为 src/views/setmeal/addSetmeal.vue:
TypeScript
{
path: "/setmeal/add",
component: () => import("@/views/setmeal/addSetmeal.vue"),
meta: {
title: "添加套餐",
hidden: true
}
}
步骤四:核心代码解读
解读 src/views/setmeal/addSetmeal.vue 文件:
html
<template>
<div class="dashboard-container">
<div class="container">
<div class="tableBar">
<label style="margin-right: 10px">套餐名称:</label>
<el-input style="width: 14%" clearable v-model="name" />
<label style="margin-right: 10px; margin-left: 20px">套餐分类:</label>
<el-select v-model="categoryId" placeholder="请选择" clearable>
<el-option
v-for="item in options"
:key="item.id"
:label="item.name"
:value="item.id">
</el-option>
</el-select>
<label style="margin-right: 10px; margin-left: 20px">售卖状态:</label>
<el-select v-model="status" style="width: 14%" placeholder="请选择" clearable>
<el-option v-for="item in statusArr"
:key="item.value"
:label="item.label"
:value="item.value" />
</el-select>
<el-button type="primary" style="margin-left: 20px" @click="pageQuery()"> 查询 </el-button>
<div style="float: right">
<el-button type="danger" @click="handleDelete('B')"> 批量删除 </el-button>
<el-button type="info" @click="handleAdd"> + 新建套餐 </el-button>
</div>
</div>
<el-table :data="records" stripe class="tableBox" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="25" />
<el-table-column prop="name" label="套餐名称" />
<el-table-column label="图片">
<template slot-scope="scope">
<el-image style="width: 80px; height: 40px; border: none" :src="scope.row.image"></el-image>
</template>
</el-table-column>
<el-table-column prop="categoryName" label="套餐分类" />
<el-table-column prop="price" label="套餐价"/>
<el-table-column label="售卖状态">
<template slot-scope="scope">
<div class="tableColumn-status" :class="{ 'stop-use': scope.row.status === 0 }">
{{ scope.row.status === 0 ? '停售' : '启售' }}
</div>
</template>
</el-table-column>
<el-table-column prop="updateTime" label="最后操作时间" />
<el-table-column label="操作" align="center" width="250px">
<template slot-scope="scope">
<el-button type="text" size="small"> 修改 </el-button>
<el-button type="text" size="small" @click="handleStartOrStop(scope.row)">
{{ scope.row.status == '1' ? '停售' : '启售' }}
</el-button>
<el-button type="text" size="small" @click="handleDelete('S',scope.row.id)"> 删除 </el-button>
</template>
</el-table-column>
</el-table>
<el-pagination class="pageList"
:page-sizes="[10, 20, 30, 40]"
:page-size="pageSize"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
@size-change="handleSizeChange"
@current-change="handleCurrentChange" />
</div>
</div>
</template>
<script lang="ts">
import {getCategoryByType} from '@/api/category'
import { getSetmealPage,enableOrDisableSetmeal,deleteSetmeal } from '@/api/setMeal';
export default {
data() {
return {
page: 1,
pageSize: 10,
name: '', //套餐名称
status: '', //售卖状态
categoryId: '', //分类id
total: 0,
records: [],
options: [],
statusArr: [ //为售卖状态下拉框提供的数据
{
value: '1',
label: '启售'
}, {
value: '0',
label: '停售'
}
],
multipleSelection: [] //当前被选中的行
}
},
created() {
// 查询套餐分类,用于填充查询页面的下拉框
getCategoryByType({type:2})
.then((res) => {
if(res.data.code == 1){
this.options = res.data.data
}
})
// 查询套餐分页数据
this.pageQuery()
},
methods: {
// 套餐分页查询
pageQuery(){
//封装分页查询参数
const params = {
page: this.page,
pageSize: this.pageSize,
name: this.name,
status: this.status,
categoryId: this.categoryId
}
//调用分页查询接口
getSetmealPage(params)
.then(res => {
if(res.data.code === 1) {
this.total = res.data.data.total
this.records = res.data.data.records
}
})
},
//分页条的事件处理函数,pageSize 改变时会触发
handleSizeChange(pageSize) {
this.pageSize = pageSize
this.pageQuery()
},
//分页条的事件处理函数,currentPage 改变时会触发
handleCurrentChange(page) {
this.page = page
this.pageQuery()
},
//套餐起售、停售
handleStartOrStop(row) {
this.$confirm('确认调整该套餐的售卖状态?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}).then(() => {
enableOrDisableSetmeal({ id: row.id, status: !row.status ? 1 : 0 })
.then((res) => {
if (res.status === 200) {
this.$message.success('套餐售卖状态更改成功!')
this.pageQuery()
}
})
.catch((err) => {
this.$message.error('请求出错了:' + err.message)
})
})
},
//删除套餐
handleDelete(type: string, id: string){
if(type === 'B' && this.multipleSelection.length == 0){
this.$message('请选择需要删除的套餐!')
return
}
this.$confirm('确定删除该套餐?', '确定删除', {
confirmButtonText: '删除',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
let param = ''
//判断当前是单个删除还是批量删除
if(type === 'S'){
//单个删除
param = id
}else {
//批量删除
const arr = new Array
this.multipleSelection.forEach(element => {
//将套餐id放入数组中
arr.push(element.id)
})
param = arr.join(',') //将数组中的id拼接到一起,中间用逗号分隔
}
deleteSetmeal(param)
.then(res => {
if(res.data.code === 1){
this.$message.success('删除成功!')
this.pageQuery()
}else{
this.$message.error(res.data.msg)
}
})
})
},
//当选择项发生变化时会触发该事件
handleSelectionChange(val) {
this.multipleSelection = val
//alert(this.multipleSelection.length)
},
//新增套餐,跳转到新增页面(组件)
handleAdd() {
this.$router.push('/setmeal/add')
}
}
}
</script>
<style lang="scss">
.el-table-column--selection .cell {
padding-left: 10px;
}
</style>
<style lang="scss" scoped>
.dashboard {
&-container {
margin: 30px;
.container {
background: #fff;
position: relative;
z-index: 1;
padding: 30px 28px;
border-radius: 4px;
.tableBar {
margin-bottom: 20px;
.tableLab {
float: right;
span {
cursor: pointer;
display: inline-block;
font-size: 14px;
padding: 0 20px;
color: $gray-2;
}
}
}
.tableBox {
width: 100%;
border: 1px solid $gray-5;
border-bottom: 0;
}
.pageList {
text-align: center;
margin-top: 30px;
}
//查询黑色按钮样式
.normal-btn {
background: #333333;
color: white;
margin-left: 20px;
}
}
}
}
</style>