八、后台管理端【菜品管理】页面
8.1 菜品类目管理
菜品类目管理模块 主要针对于菜品类目的查询、添加、修改等相关操作
渲染菜品类目列表
最基础的表格渲染 + 分页效果
添加菜品类目
UI结构:
html
<!-- 添加/修改 类目Dialog -->
<el-dialog width="30%" title="菜品类目" :visible.sync="dialogVisible" :before-close="dialogClose">
<el-form ref="formRef" :model="formInput" label-width="80px" label-position="left">
<el-form-item label="类目名">
<el-input v-model="formInput.label" />
</el-form-item>
<el-form-item label="排序值">
<el-input type="number" v-model="formInput.rank" />
</el-form-item>
</el-form>
<div slot="footer">
<el-button @click="dialogClose">取 消</el-button>
<el-button type="primary" @click="submitForm">确 定</el-button>
</div>
</el-dialog>
JS逻辑
js
dialogOpen(row) {
if (row) {
this.formInput = { ...row }
}
this.dialogVisible = true
},
dialogClose() {
this.formInput = {}
this.dialogVisible = false
},
// 提交dialog内容【新增 / 修改】
async submitForm() {
const { label, id } = this.formInput
// 判断操作类型
const requestApi = id ? setCategoryApi : addCategoryApi
await requestApi({ ...this.formInput, value: label })
this.$message.success('操作成功')
this.getTableData()
this.dialogClose()
}
修改菜品类目
同上
删除菜品类目
js
// 删除类目信息
async delCategory(id) {
await delCategoryApi({ id })
this.$message.success('删除成功')
this.getTableData()
},
8.2 菜品管理
菜品管理模块 主要针对于菜品信息的查询、添加、修改、上下架管理
菜品查询
核心功能: 列表渲染、分页显示、菜品名搜索、菜品状态筛选
代码略
菜品添加
核心功能: 收集表单数据、 菜品单位加载与添加、菜品图片上传
收集表单数据:
对应字段v-model绑定即可
菜品单位加载与添加:
- 菜品单位需默认加载 供用户选择
- 用户也可以通过自定义单位 实现添加单位
提示
点击自定义单位 显示添加单位操作项
- 菜品图片上传: 使用封装的图片上传组件
提交数据: 准备数据 -> 发起请求 -> 跳转页面
js
// 提交数据
async submitForm() {
// 准备表单需要的数据
const cid = this.categoryList.find(item => item.value === this.formInput.category).id
const data = { ...this.formInput, cid }
// 发起请求
try {
const requestApi = this.id ? setDishApi : addDishApi
await requestApi(data)
this.$message('操作成功')
this.$router.push('/dish')
} catch (error) {
console.log('失败')
}
}
菜品修改
实现思路: 跳转页面携带菜品ID -> 根据菜品ID加载数据 -> 提交数据进行修改
保存ID:
this.id = this.$route.query.id
获取数据:
js// 获取菜品信息【编辑】 async getDish() { if (!this.id) return // 获取页面参数信息 const { data } = await getDishInfoApi(this.id) this.formInput = data }
菜品上下架
上下架按钮需要配置显示条件 onsale为1
显示下架 onsale为0
显示上架
js
// 上下架菜品
async changeStates(id, state) {
await dishStatusApi({ id, state })
this.$message.success('操作成功')
this.getTableData()
}
8.3 ProTable封装
目的: 解决表格重复逻辑问题
ProTable.vue
Props
tabColumns
表格结构 [格式需求: { type: text | tag | image | link , prop: 字段名, title: 标题 }]
jstabColumns: { required: true, type: Array },
formColumns
表单结构 [格式需求: { type: text | number | select | radio, prop: 字段名, label: 标题, enum: 选项列表[label, value] }]
jsformColumns: { type: Array, default: () => [] },
requestApi
请求API函数
jsrequestApi: Function,
initParams
初始化参数
jsinitParams: { type: Object, default: () => ({}) }
UI结构
vue
<template>
<div class="proTable">
<!-- 查询区域 -->
<div class="query-view">
<el-form :model="queryParams" inline label-width="100px">
<el-form-item :label="item.label" v-for="item in formColumns" :key="item.prop">
<SearchFormItem :item="item" :searchParams="queryParams"></SearchFormItem>
</el-form-item>
</el-form>
</div>
<!-- 操作区域 -->
<div class="button-view">
<!-- 左区域 -->
<div class="lf">
<slot name="buttonAction"></slot>
</div>
<!-- 右区域 -->
<div class="rf">
<el-button icon="el-icon-refresh" size="small" @click="reset">重置</el-button>
<el-button icon="el-icon-search" size="small" type="primary" @click="search">查询</el-button>
</div>
</div>
<!-- 表格区域 -->
<div class="table-view">
<!-- 表头 -->
<div class="tab-list">
<span v-for="tab in tabColumns" :key="tab.title">{{ tab.title }}</span>
</div>
<!-- 表格 -->
<div class="tab-table" v-for="(rows, index) in tabData" :key="index">
<div v-for="tab in tabColumns" :key="tab.title">
<slot :name="tab.prop" :rows="rows" :$index="index">
<!-- 图片类型 -->
<el-image v-if="tab.type === 'img'" :src="rows[tab.prop]" class="tab-img"/>
<!-- 标签类型 -->
<el-tag v-else-if="tab.type === 'tag'">{{ rows[tab.prop] }}</el-tag>
<!-- 链接类型 -->
<el-link v-else-if="tab.type === 'link'" type="primary" :href="rows[tab.prop]" target="_blank">{{ rows[tab.prop] }}</el-link>
<!-- 文本类型 -->
<span v-else>{{ rows[tab.prop] }}</span>
</slot>
</div>
</div>
<!-- 分页 -->
<el-pagination
background
layout="prev, pager, next"
:hide-on-single-page="true"
:page-size="pageable.pageSize"
:total="pageable.total"
:current-page="pageable.page"
@current-change="pageChange"
/>
</div>
<!-- 空状态 -->
<div class="nodatas" v-if="false">暂无数据</div>
</div>
</template>
逻辑
定义data
jsdata() { return { // 表数据 tabData: [], // 分页参数 pageable: { page: 1, pageSize: 5 }, // 查询参数 queryParams: {}, // 总参数 totalParams: {} } },
初始化加载
jsmounted () { // 将初始参数进行合并 // 合并分页参数 const { page = 1, pageSize = 5 } = this.initParams this.pageable.page = page this.pageable.pageSize = pageSize // 合并查询参数 this.queryParams = { ...this.queryParams, ...this.initParams } // 查询数据【目的携带值】 this.search() },
核心方法
js// 加载数据 async getTableData() { // 1. 发起请求【先合并分页参数】 Object.assign(this.totalParams, this.pageable) const { data } = await this.requestApi(this.totalParams) // 2. 渲染数据 this.tabData = data.list this.pageable = { page: data.page, total: data.total, pageSize: data.pageSize } }, // 页码改变 pageChange(page) { this.pageable.page = page this.getTableData() }, // 查询 search () { // 1. 合并查询参数 Object.assign(this.totalParams, this.queryParams) // 2. 加载数据 this.getTableData() }, // 重置 reset() { this.pageable.page = 1 this.queryParams = { ...this.initParams } this.search() }, // 刷新表格 reload() { this.getTableData() }
动态表单
vue
<template>
<!-- 数字框 -->
<el-input v-if="item.type === 'number'" v-model="searchParams[item.prop]" placeholder="请输入" size="small"></el-input>
<!-- 下拉选择框 -->
<el-select v-else-if="item.type === 'select'" v-model="searchParams[item.prop]" placeholder="请选择" size="small">
<el-option v-for="itemValue in item.enum" :key="itemValue.value" :label="itemValue.label" :value="itemValue.value" />
</el-select>
<!-- 单选框组 -->
<el-radio-group v-else-if="item.type === 'radio'" v-model="searchParams[item.prop]" size="small">
<el-radio-button v-for="itemValue in item.enum" :key="itemValue.value" :label="itemValue.label" :value="itemValue.value" />
</el-radio-group>
<!-- 文本框 -->
<el-input v-else v-model="searchParams[item.prop]" placeholder="请输入" size="small"></el-input>
</template>
<script>
export default {
name: 'SearchFormItem',
props: {
item: { required: true, type: Object },
searchParams: { type: Object, default: () => {} }
}
}
</script>
<style lang="scss" scoped></style>
使用
vue
<ProTable ref="proTable" :formColumns="formColumns" :tabColumns="tabColumns" :requestApi="getDishCateApi" :initParams="initParams">
<!-- 自定义表格内容 -->
<template #opera="{ rows }">
<el-button size="mini" @click="openDialog(rows)">编辑</el-button>
<el-button type="danger" size="mini" @click="handleRemove(rows.id)">删除</el-button>
</template>
</ProTable>