Skip to content
本页目录

八、后台管理端【菜品管理】页面

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绑定即可

  • 菜品单位加载与添加:

  1. 菜品单位需默认加载 供用户选择
  2. 用户也可以通过自定义单位 实现添加单位

提示

点击自定义单位 显示添加单位操作项

  • 菜品图片上传: 使用封装的图片上传组件

提交数据: 准备数据 -> 发起请求 -> 跳转页面

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: 标题 }]

    js
    tabColumns: { required: true, type: Array },
    
  • formColumns

    表单结构 [格式需求: { type: text | number | select | radio, prop: 字段名, label: 标题, enum: 选项列表[label, value] }]

    js
    formColumns: { type: Array, default: () => [] },
    
  • requestApi

    请求API函数

    js
    requestApi: Function,
    
  • initParams

    初始化参数

    js
    initParams: { 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

    js
    data() {
        return {
          // 表数据
          tabData: [],
          // 分页参数
          pageable: { page: 1, pageSize: 5 },
          // 查询参数
          queryParams: {},
          // 总参数
          totalParams: {}
        }
    },
    
  • 初始化加载

    js
    mounted () {
        // 将初始参数进行合并
        // 合并分页参数
        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>