苍穹外卖前端开发实战:员工与套餐管理模块
介绍苍穹外卖项目前端开发流程。基于 Node.js、Vue、ElementUI 及 TypeScript 构建环境。详细演示员工管理模块的分页查询、状态切换、新增与修改功能,以及套餐管理模块的分页查询、售卖状态控制、批量删除与新增操作。通过封装 Axios 请求、配置 Vue Router 及使用 ElementUI 组件实现前后端数据交互与界面展示。

介绍苍穹外卖项目前端开发流程。基于 Node.js、Vue、ElementUI 及 TypeScript 构建环境。详细演示员工管理模块的分页查询、状态切换、新增与修改功能,以及套餐管理模块的分页查询、售卖状态控制、批量删除与新增操作。通过封装 Axios 请求、配置 Vue Router 及使用 ElementUI 组件实现前后端数据交互与界面展示。

使用的前端技术栈:Node.js、Vue、ElementUI、Axios、Vuex、Vue Router、TypeScript
核心目录 / 文件:
| 目录 / 文件 | 说明 |
|---|---|
| api | 封装 Ajax 请求的文件目录 |
| components | 公共组件存放目录 |
| views | 视图组件存放目录 |
| App.vue | 项目主组件、页面入口文件 |
| main.ts | 整个项目的入口文件 |
| router.ts | 路由配置文件 |
安装依赖包(生成 node_modules 目录):
npm install
启动前端项目(需同时启动后端 Java 服务):
npm run serve

步骤一:制作页面头部

<div>
<label>员工姓名:</label>
<el-input placeholder="请输入员工姓名" />
<el-button type="primary">查询</el-button>
<el-button type="primary">+ 添加员工</el-button>
</div>
说明:输入框和按钮均使用 ElementUI 提供的组件,可参考其官方文档进行修改。
步骤二:实现前后端数据交互
绑定查询事件:为查询按钮添加 @click="pageQuery()" 事件。
<el-button type="primary" @click="pageQuery()">查询</el-button>
定义查询方法:在 methods 中定义 pageQuery 方法,验证方法能否正常执行。
<script lang="ts">
export default {
methods: {
// 分页查询
pageQuery() {
// 验证当前方法能否成功执行
alert(1)
}
}
}
</script>
封装 API 请求:在 src/api/employee.ts 中定义 getEmployeeList 方法,用于发送 Ajax 请求获取分页数据。
// 分页查询
export const getEmployeeList = (params: any) => {
return request({ url: '/employee/page', method: 'get', params: params })
}
导入 API 并定义模型数据:在员工管理组件中导入 getEmployeeList 方法,并在 data() 中定义分页相关的模型数据。
import { getEmployeeList } from '@/api/employee'
export default {
// 模型数据
data() {
return {
name: '', // 员工姓名,对应上面的输入框
page: 1, // 页码
pageSize: 10, // 每页记录数
total: 0, // 总记录数
records: [] // 当前页要展示的数据集合
}
}
}
双向绑定输入框:将 name 属性与员工姓名输入框进行双向绑定。
<el-input v-model="name" placeholder="请输入员工姓名" clearable />
完善查询方法:在 pageQuery 方法中调用 getEmployeeList 方法,处理返回数据。
// 分页查询
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 请求,查询第一页数据。
// 声明周期方法
created() {
this.pageQuery()
}
步骤四:使用表格展示分页数据
使用 ElementUI 的表格组件展示后端返回的员工数据。
<el-table :data="records" stripe>
<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="操作">
<template slot-scope="scope">
< = =>修改
{{ scope.row.status === 1 ? '禁用' : '启用' }}
删除
步骤五:使用分页条实现翻页效果
官方示例:https://element.eleme.io/#/zh-CN/component/pagination
分页组件代码:
<el-pagination
: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"
/>
事件处理函数:
// 分页条的事件处理函数,pageSize 改变时会触发
handleSizeChange(pageSize) {
this.pageSize = pageSize
this.pageQuery()
},
// 分页条的事件处理函数,currentPage 改变时会触发
handleCurrentChange(currentPage) {
this.page = currentPage
this.pageQuery()
}

步骤一:绑定按钮单击事件
为表格中的 "启用 / 禁用" 按钮绑定 handleStartOrStop 事件,并根据当前状态动态显示按钮文字。
<el-button type="text" size="small" @click="handleStartOrStop(scope.row)">
{{ scope.row.status == '1' ? '禁用' : '启用' }}
</el-button>
步骤二:编写对应的处理函数
在 methods 中定义 handleStartOrStop 方法,验证方法能否成功执行。
// 启用、禁用员工账号
handleStartOrStop(row) {
alert(`id=${row.id} status=${row.status}`)
}
步骤三:封装 API 请求
在 src/api/employee.ts 中定义 enableOrDisableEmployee 方法,用于发送 Ajax 请求更新员工状态。
// 启用禁用员工账号
export const enableOrDisableEmployee = (params: any) => {
return request({ url: `/employee/status/${params.status}`, method: 'post', params: { id: params.id } })
}
步骤四:完善处理函数
在员工管理组件中导入 enableOrDisableEmployee 方法,并完善 handleStartOrStop 方法,添加确认弹窗和状态更新逻辑。
// 启用、禁用员工账号
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 方法中添加判断,如果是管理员账号则不允许修改状态并给出提示。
// 启用、禁用员工账号
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)
})
})
}
产品原型:


步骤一:为 "添加员工" 按钮绑定单击事件
<div>
<label>员工姓名:</label>
<el-input v-model="name" placeholder="请输入员工姓名" clearable />
<el-button type="primary" @click="pageQuery()">查询</el-button>
<el-button type="primary" @click="handleAddEmp">+ 添加员工</el-button>
</div>
步骤二:编写 handleAddEmp 方法,进行路由跳转
// 添加员工,跳转至添加员工页面(组件)
handleAddEmp() {
this.$router.push('/employee/add')
}
路由配置(已在路由文件中定义):
{
path: "/employee/add",
component: () => import("@/views/employee/addEmployee.vue"),
meta: { title: "添加/修改员工", hidden: true }
}
步骤三:开发新增页面表单元素
<template>
<div>
<div>
<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= =>
男
女
保存
保存并继续添加员工
返回
步骤四:定义模型数据和表单校验规则
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,
: ,
: {
(value === || !(.(value))) {
( ())
} {
()
}
}
}
]
}
}
}
}
步骤五:在 employee.ts 中封装新增员工方法
// 新增员工
export const addEmployee = (params: any) => {
return request({ url: '/employee', method: 'post', data: params })
}
步骤六:定义提交表单的方法 submitForm:
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)
}
})
}
})
}
}


步骤一:为 "修改" 按钮绑定单击事件
<el-button type="text" size="small" @click="handleUpdateEmp(scope.row)">修改</el-button>
步骤二:编写 handleUpdateEmp 方法,实现路由跳转
在员工列表组件的 methods 中定义跳转方法,并对管理员账号进行保护:
// 修改员工,跳转至修改员工页面(组件)
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 生命周期中根据路由参数判断:
<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 查询员工的方法
用于修改操作时的数据回显:
// 根据 id 查询员工
export const queryEmployeeById = (id: number) => {
return request({ url: `/employee/${id}`, method: 'get' })
}
步骤五:在 addEmployee.vue 中实现数据回显
在 created 方法中,如果是修改操作,则调用查询方法回显数据:
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 指令,仅在新增操作时显示该按钮:
<div>
<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 中封装修改员工的方法
// 修改员工
export const updateEmployee = (params: any) => {
return request({ url: '/employee', method: 'put', data: params })
}
步骤八:修改 submitForm 方法,区分新增和修改操作
在组件的 methods 中,根据 optType 执行不同的请求:
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)
}
})
}
}
})
}
产品原型:



步骤一:制作页面头部效果
<div>
<label>套餐名称:</label>
<el-input v-model="name" clearable />
<label>套餐分类:</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>售卖状态:</label>
<el-select v-model="status" placeholder="请选择" clearable>
<el-option v-for="item in statusArr" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
<el-button type="primary">查询</el-button>
批量删除
+ 新建套餐
模型数据定义:
export default {
data() {
return {
name: '',
categoryId: '', // 分类 id
status: '', // 售卖状态
options: [], // 为套餐分类下拉框提供的选项
statusArr: [ // 为售卖状态下拉框提供的数据
{ value: '1', label: '启售' },
{ value: '0', label: '停售' }
]
}
}
}
步骤二:动态填充套餐分类下拉框数据
封装 API 请求:在 src/api/category.ts 中已定义 getCategoryByType 方法,用于根据类型查询分类。
// 根据类型查询分类:1 为菜品分类 2 为套餐分类
export const getCategoryByType = (params: any) => {
return request({ url: '/category/list', method: 'get', params: params })
}
导入并调用 API:在套餐管理组件中导入该方法,并在 created 生命周期中调用,动态填充下拉框。
<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" 事件。
<el-button type="primary" @click="pageQuery()">查询</el-button>
封装 API 请求:在 src/api/setMeal.ts 中定义 getSetmealPage 方法,用于发送 Ajax 请求获取套餐分页数据。
// 套餐分页查询
export const getSetmealPage = (params: any) => {
return request({ url: '/setmeal/page', method: 'get', params: params })
}
导入 API 并定义模型数据:在套餐管理组件中导入 getSetmealPage 方法,并在 data() 中定义分页相关的模型数据。
import { getSetmealPage } from '@/api/setMeal'
export default {
data() {
return {
page: 1, // 页码
pageSize: 10, // 每页记录数
total: 0, // 总记录数
records: [], // 当前页要展示的数据集合
name: '',
categoryId: '',
status: '',
options: [],
statusArr: [/* ... */]
}
}
}
完善查询方法:在 pageQuery 方法中调用 getSetmealPage 方法,处理返回数据。
// 套餐分页查询
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 请求,查询第一页数据。
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
<el-table :data="records" stripe>
<el-table-column prop="image" label="图片">
<template slot-scope="scope">
<el-image :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>
</>
修改
{{ scope.row.status === 1 ? '停售' : '启售' }}
删除
步骤六:使用分页条实现翻页效果
官方示例:https://element.eleme.io/#/zh-CN/component/pagination
分页组件代码:
<el-pagination
background
@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>
事件处理函数:
// 分页条的事件处理函数,pageSize 改变时会触发
handleSizeChange(pageSize) {
this.pageSize = pageSize
this.pageQuery()
},
// 分页条的事件处理函数,currentPage 改变时会触发
handleCurrentChange(page) {
this.page = page
this.pageQuery()
}
产品原型:


步骤一:绑定按钮单击事件
为表格中的 "起售 / 停售" 按钮绑定 handleStartOrStop 事件,并根据当前状态动态显示按钮文字。
<el-button type="text" size="small" @click="handleStartOrStop(scope.row)">
{{ scope.row.status == '1' ? '停售' : '启售' }}
</el-button>
步骤二:编写对应的处理函数
在 methods 中定义 handleStartOrStop 方法,验证方法能否成功执行。
// 套餐起售、停售
handleStartOrStop(row) {
alert(`id=${row.id} status=${row.status}`)
}
步骤三:封装 API 请求
在 src/api/setMeal.ts 中定义 enableOrDisableSetmeal 方法,用于发送 Ajax 请求更新套餐状态。
// 套餐起售禁售
export const enableOrDisableSetmeal = (params: any) => {
return request({ url: `/setmeal/status/${params.status}`, method: 'post', params: { id: params.id } })
}
步骤四:完善处理函数
在套餐管理组件中导入 enableOrDisableSetmeal 方法,并完善 handleStartOrStop 方法,添加确认弹窗和状态更新逻辑。
// 套餐起售、停售
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 请求删除套餐。
// 删除套餐接口
export const deleteSetmeal = (ids: string) => {
return request({ url: '/setmeal', method: 'delete', params: { ids: ids } })
}
步骤二:为 "批量删除" 按钮绑定事件
为批量删除按钮绑定 handleDelete 事件,验证方法执行。
<el-button type="danger" @click="handleDelete">批量删除</el-button>
// 删除套餐
handleDelete() {
alert('删除套餐')
}
步骤三:监听表格选择变化
为表格添加 selection-change 事件,动态获取当前勾选的套餐行。
<el-table :data="records" stripe @selection-change="handleSelectionChange">
<el-table-column type="selection" />
<!-- 其他列 -->
</el-table>
在 data() 中定义存储选中行的数组:
data() {
return {
// ...其他数据
multipleSelection: [] // 当前被选中的行
}
}
编写事件处理函数:
// 当选择项发生变化时会触发该事件
handleSelectionChange(val) {
this.multipleSelection = val
//alert(this.multipleSelection.length)
}
步骤四:完善 handleDelete 方法,处理批量删除
在 handleDelete 方法中,获取选中的套餐 ID 并拼接成字符串。
// 删除套餐
handleDelete() {
const arr = new Array()
this.multipleSelection.forEach(element => {
// 将套餐 id 放入数组中
arr.push(element.id)
})
const ids = arr.join(',') // 将数组中的 id 拼接到一起,中间用逗号分隔
alert(ids)
}
步骤五:为 "删除" 按钮绑定事件
为单个删除按钮绑定 handleDelete 事件,并通过参数区分操作类型。
<el-button type="text" size="small" @click="handleDelete('S',scope.row.id)">删除</el-button>
步骤六:调整 handleDelete 方法,兼容单个和批量删除
修改 handleDelete 方法,根据传入的 type 参数(S 表示单个删除,B 表示批量删除)执行不同逻辑。
// 删除套餐
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 方法,添加提示和确认
// 删除套餐
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 => {
(res.. === ) {
..()
.()
} {
..(res..)
}
})
})
}
产品原型:






步骤一:找到新建套餐按钮及绑定事件
在套餐管理列表页面中,找到新建套餐按钮,其绑定的点击事件为 handleAdd:
<el-button type="info" @click="handleAdd">+ 新建套餐</el-button>
步骤二:查看 handleAdd 方法的路由跳转逻辑
在 methods 中找到 handleAdd 方法,它通过路由跳转到新增套餐页面:
// 新增套餐,跳转到新增页面(组件)
handleAdd() {
this.$router.push('/setmeal/add')
}
步骤三:在路由文件中定位对应组件
在路由配置文件中,路径 /setmeal/add 对应的视图组件为 src/views/setmeal/addSetmeal.vue:
{
path: "/setmeal/add",
component: () => import("@/views/setmeal/addSetmeal.vue"),
meta: { title: "添加套餐", hidden: true }
}
步骤四:核心代码解读
解读 src/views/setmeal/addSetmeal.vue 文件:
<template>
<div>
<div>
<div>
<label>套餐名称:</label>
<el-input clearable v-model="name" />
<label>套餐分类:</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>售卖状态:</label>
<el-select v-model="status" placeholder="请选择" clearable>
<el-option v-for="item in statusArr" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
<el-button type="primary" @click="pageQuery()">查询</el-button>
<div>
<el-button type="danger" @click="handleDelete('B')">批量删除</el-button>
<el-button type="info" @click="handleAdd">+ 新建套餐</el-button>
</div>
</div>
<el-table :data="records" stripe @selection-change="handleSelectionChange">
<el-table-column type="selection" />
<el-table-column prop="name" label="套餐名称" />
<el-table-column label="图片">
<template slot-scope="scope">
<el-image :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="{ '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="操作">
<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 :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>

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online