苍穹外卖(前端)

苍穹外卖(前端)

前端环境搭建:

技术选型:

使用的前端技术栈:node.js、vue、ElementUI、axios、vuex、vue-router、typescript

代码结构:

核心目录 / 文件:

目录 / 文件说明
apki封装 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"> <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

分页组件代码:

<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="性别" 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> </div> </div> </template>

步骤四:定义模型数据和表单校验规则

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 中封装新增员工方法

//新增员工 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> <div> <el-button type="danger">批量删除</el-button> <el-button type="info">+ 新建套餐</el-button> </div> </div>

模型数据定义:

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> </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"> {{ 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

分页组件代码:

<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 //判断当前是单个删除还是批量删除 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 //判断当前是单个删除还是批量删除 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:

<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 //判断当前是单个删除还是批量删除 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> 

Read more

【OpenClaw从入门到精通】第10篇:OpenClaw生产环境部署全攻略:性能优化+安全加固+监控运维(2026实测版)

【OpenClaw从入门到精通】第10篇:OpenClaw生产环境部署全攻略:性能优化+安全加固+监控运维(2026实测版)

摘要:本文聚焦OpenClaw从测试环境走向生产环境的核心痛点,围绕“性能优化、安全加固、监控运维”三大维度展开实操讲解。先明确生产环境硬件/系统选型标准,再通过硬件层资源管控、模型调度策略、缓存优化等手段提升响应速度(实测响应效率提升50%+);接着从网络、权限、数据三层构建安全防护体系,集成火山引擎安全方案拦截高危操作;最后落地TenacitOS可视化监控与Prometheus告警体系,配套完整故障排查清单和虚拟实战案例。全文所有配置、代码均经实测验证,兼顾新手入门实操性和进阶读者的生产级部署需求,帮助开发者真正实现OpenClaw从“能用”到“放心用”的跨越。 优质专栏欢迎订阅! 【DeepSeek深度应用】【Python高阶开发:AI自动化与数据工程实战】【YOLOv11工业级实战】 【机器视觉:C# + HALCON】【大模型微调实战:平民级微调技术全解】 【人工智能之深度学习】【AI 赋能:Python 人工智能应用实战】【数字孪生与仿真技术实战指南】 【AI工程化落地与YOLOv8/v9实战】【C#工业上位机高级应用:高并发通信+性能优化】 【Java生产级避坑指南:

By Ne0inhk
ARM Linux 驱动开发篇--- Linux 并发与竞争实验(互斥体实现 LED 设备互斥访问)--- Ubuntu20.04互斥体实验

ARM Linux 驱动开发篇--- Linux 并发与竞争实验(互斥体实现 LED 设备互斥访问)--- Ubuntu20.04互斥体实验

🎬 渡水无言:个人主页渡水无言 ❄专栏传送门: 《linux专栏》《嵌入式linux驱动开发》《linux系统移植专栏》 ❄专栏传送门: 《freertos专栏》《STM32 HAL库专栏》 ⭐️流水不争先,争的是滔滔不绝  📚博主简介:第二十届中国研究生电子设计竞赛全国二等奖 |国家奖学金 | 省级三好学生 | 省级优秀毕业生获得者 | ZEEKLOG新星杯TOP18 | 半导纵横专栏博主 | 211在读研究生 在这里主要分享自己学习的linux嵌入式领域知识;有分享错误或者不足的地方欢迎大佬指导,也欢迎各位大佬互相三连 目录 前言  一、实验基础说明 1.1、互斥体简介 1.2 本次实验设计思路 二、硬件原理分析(看过之前博客的可以忽略) 三、实验程序编写 3.1 互斥体 LED 驱动代码(mutex.c) 3.2.1、设备结构体定义(28-39

By Ne0inhk
Flutter for OpenHarmony:swagger_dart_code_generator 接口代码自动化生成的救星(OpenAPI/Swagger) 深度解析与鸿蒙适配指南

Flutter for OpenHarmony:swagger_dart_code_generator 接口代码自动化生成的救星(OpenAPI/Swagger) 深度解析与鸿蒙适配指南

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net 前言 后端工程师扔给你一个 Swagger (OpenAPI) 文档地址,你会怎么做? 1. 对着文档,手写 Dart Model 类(容易写错字段类型)。 2. 手写 Retrofit/Dio 的 API 接口定义(容易拼错 URL)。 3. 当后端修改了字段名,你对着报错修半天。 这是重复劳动的地狱。 swagger_dart_code_generator 可以将 Swagger (JSON/YAML) 文件直接转换为高质量的 Dart 代码,包括: * Model 类:支持 json_serializable,带 fromJson/

By Ne0inhk
Linux 开发别再卡壳!makefile/git/gdb 全流程实操 + 作业解析,新手看完直接用----《Hello Linux!》(5)

Linux 开发别再卡壳!makefile/git/gdb 全流程实操 + 作业解析,新手看完直接用----《Hello Linux!》(5)

文章目录 * 前言 * make/makefile * 文件的三个时间 * Linux第一个小程序-进度条 * 回车和换行 * 缓冲区 * 程序的代码展示 * git指令 * 关于gitee * Linux调试器-gdb使用 * 作业部分 前言 做 Linux 开发时,你是不是也遇到过这些 “卡脖子” 时刻?写 makefile 时,明明语法没错却报错,最后发现是依赖方法行没加 Tab;想提交代码到 gitee,记不清 git add/commit/push 的 “三板斧”,还得反复搜教程;用 gdb 调试程序,输了命令没反应,才想起编译时没加-g生成 debug 版本;甚至连写个进度条,都搞不懂\r和\n的区别,导致进度条乱跳…… 其实这些问题,

By Ne0inhk