跳到主要内容
苍穹外卖前端开发实战:Vue + TypeScript + ElementUI | 极客日志
TypeScript Node.js 大前端
苍穹外卖前端开发实战:Vue + TypeScript + ElementUI 基于 Vue 3 与 TypeScript 技术栈,结合 ElementUI 组件库完成苍穹外卖系统的前端模块开发。内容涵盖员工管理与套餐管理两大核心功能,包括分页查询、状态变更、新增修改及批量删除等 CRUD 操作。通过封装 Axios 请求、配置路由守卫及表单校验规则,实现了前后端数据的高效交互与界面动态渲染。文章详细梳理了从环境搭建到具体业务逻辑实现的完整流程,适合希望掌握企业级中后台管理系统开发的开发者参考。
ArchDesign 发布于 2026/4/8 0 浏览前端环境搭建
技术选型
本项目采用 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
员工管理
分页查询
功能需求
实现员工列表的分页展示,支持按姓名筛选,并在表格中显示账号状态及操作按钮。
页面头部构建
使用 ElementUI 的 Input 和 Button 组件制作搜索栏。输入框与按钮均基于官方文档组件进行配置。
<div >
<label > 员工姓名:</label >
<el-input placeholder ="请输入员工姓名" />
<el-button type ="primary" > 查询</el-button >
<el-button type ="primary" > + 添加员工</el-button >
</div >
前后端数据交互
为查询按钮绑定 @click="pageQuery()" 事件。在 methods 中定义 pageQuery 方法,验证逻辑执行。
<script lang="ts" >
export default {
methods : {
pageQuery ( ) {
console .log ('Query triggered' )
}
}
}
</script>
封装 API 请求:在 src/api/employee.ts 中定义 getEmployeeList 方法,用于发送 Ajax 请求获取分页数据。
export const getEmployeeList = (params : any ) => {
return request ({ url : '/employee/page' , method : 'get' , 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 )
})
}
自动加载数据 利用 Vue 的 created 生命周期钩子,在组件加载后自动发送 Ajax 请求,查询第一页数据。
created ( ) {
this .pageQuery ()
}
表格展示 使用 ElementUI 的 Table 组件展示后端返回的员工数据。注意状态列的样式处理。
<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' : 'activeUse'" >
{{ scope.row.status === 0 ? '禁用' : '启用' }}
</span >
</template >
</el-table-column >
<el-table-column prop ="updateTime" label ="最后操作时间" />
<el-table-column prop ="操作" label ="操作" >
<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 >
分页条实现 使用 Pagination 组件实现翻页效果。监听 size-change 和 current-change 事件以刷新数据。
<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"
/>
handleSizeChange (pageSize ) {
this .pageSize = pageSize
this .pageQuery ()
},
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 方法,结合 ElementUI 的确认弹窗 confirm 确保操作安全。
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 )
})
})
}
封装 API 请求:在 src/api/employee.ts 中定义 enableOrDisableEmployee 方法。
export const enableOrDisableEmployee = (params : any ) => {
return request ({
url : `/employee/status/${params.status} ` ,
method : 'post' ,
params : { id : params.id }
})
}
新增员工
功能需求 通过表单录入员工信息,包含账号、姓名、手机、性别、身份证号等字段,并进行校验。
路由跳转 handleAddEmp ( ) {
this .$router .push ('/employee/add' )
}
{ path : "/employee/add" , component : () => import ("@/views/employee/addEmployee.vue" ), meta : { title : "添加/修改员工" , hidden : true } }
表单元素开发 使用 Form 组件构建表单,设置 rules 进行校验。
<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 >
<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 >
表单校验规则 rules : {
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 ()
}
}
}
]
}
提交逻辑 封装 addEmployee 方法,根据 isContinue 参数决定是否保留表单或跳转。
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 )
}
})
}
})
}
修改员工
功能需求 复用新增页面组件,通过路由参数区分新增与修改模式,支持数据回显。
路由传参 handleUpdateEmp (row ) {
if (row.username === 'admin' ) {
this .$message .error ('admin 为管理员账号,不能修改!' )
return
}
this .$router .push ({ path : '/employee/add' , query : { id : row.id } })
}
组件内判断 在 addEmployee.vue 中通过 optType 区分操作类型,并在 created 中根据路由参数初始化。
created ( ) {
this .optType = this .$route .query .id ? 'update' : 'add'
if (this .optType === 'update' ) {
queryEmployeeById (this .$route .query .id ).then ((res ) => {
if (res.data .code === 1 ) {
this .ruleForm = res.data .data
}
})
}
}
接口区分 封装 queryEmployeeById 和 updateEmployee 方法,分别对应 GET 和 PUT 请求。
export const queryEmployeeById = (id : number ) => {
return request ({ url : `/employee/${id} ` , method : 'get' })
}
export const updateEmployee = (params : any ) => {
return request ({ url : '/employee' , method : 'put' , data : params })
}
按钮控制 <el-button v-if ="this.optType === 'add'" type ="primary" @click ="submitForm('ruleForm',true)" >
保存并继续添加员工
</el-button >
套餐管理
套餐分页查询
功能需求 支持按套餐名称、分类、售卖状态筛选,展示图片、价格及操作选项。
页面头部构建 <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 >
<div >
<el-button type ="danger" > 批量删除</el-button >
<el-button type ="info" > + 新建套餐</el-button >
</div >
</div >
动态填充分类数据 在 created 生命周期中调用分类接口,填充下拉框选项。
import { getCategoryByType } from '@/api/category'
created ( ) {
getCategoryByType ({type : 2 }).then ((res ) => {
if (res.data .code === 1 ) {
this .options = res.data .data
}
})
}
表格展示 使用 Image 组件展示套餐图片,状态列根据数值渲染文本。
<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' : 'activeUse'" >
{{ scope.row.status === 0 ? '停售' : '启售' }}
</div >
</template >
</el-table-column >
<el-table-column label ="操作" >
<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 >
启售停售套餐
功能需求
状态切换逻辑 封装 enableOrDisableSetmeal 方法,结合确认弹窗防止误操作。
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 )
})
})
}
删除套餐
功能需求
批量删除实现 监听表格 selection-change 事件获取选中行 ID,拼接后发送给后端。
handleDelete (type: string, id: string ) {
let param
if (type === 'B' && this .multipleSelection .length === 0 ) {
this .$message('请选择需要删除的套餐!' )
return
}
this .$confirm('确定删除该套餐?' , '确定删除' , {
confirmButtonText : '删除' ,
cancelButtonText : '取消' ,
type : 'warning'
}).then (() => {
if (type === 'S' ) {
param = id
} else {
const arr = new Array ()
this .multipleSelection .forEach (element => {
arr.push (element.id )
})
param = arr.join (',' )
}
deleteSetmeal (param).then (res => {
if (res.data .code === 1 ) {
this .$message .success ('删除成功!' )
this .pageQuery ()
} else {
this .$message .error (res.data .msg )
}
})
})
}
新增套餐
功能需求
路由与组件 点击'新建套餐'跳转至 /setmeal/add,复用类似员工管理的表单逻辑,但数据结构不同。
核心逻辑 主要涉及套餐数据的提交与分类数据的联动。具体表单结构与员工新增类似,重点在于 setMeal 相关 API 的调用。
本文档涵盖了苍穹外卖系统前端的核心业务模块实现细节,从基础环境搭建到复杂 CRUD 操作,展示了 Vue 生态在企业级应用中的典型用法。
相关免费在线工具 Base64 字符串编码/解码 将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
Base64 文件转换器 将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
Markdown转HTML 将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online
HTML转Markdown 将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online
JSON 压缩 通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online
JSON美化和格式化 将JSON字符串修饰为友好的可读格式。 在线工具,JSON美化和格式化在线工具,online