|
@@ -17,21 +17,21 @@
|
|
|
<el-button
|
|
<el-button
|
|
|
:type="activeCategory === 'drink' ? 'primary' : ''"
|
|
:type="activeCategory === 'drink' ? 'primary' : ''"
|
|
|
:plain="activeCategory !== 'drink'"
|
|
:plain="activeCategory !== 'drink'"
|
|
|
- @click="activeCategory = 'drink'"
|
|
|
|
|
|
|
+ @click="handleCategoryClick('drink')"
|
|
|
>
|
|
>
|
|
|
酒水
|
|
酒水
|
|
|
</el-button>
|
|
</el-button>
|
|
|
<el-button
|
|
<el-button
|
|
|
:type="activeCategory === 'food' ? 'primary' : ''"
|
|
:type="activeCategory === 'food' ? 'primary' : ''"
|
|
|
:plain="activeCategory !== 'food'"
|
|
:plain="activeCategory !== 'food'"
|
|
|
- @click="activeCategory = 'food'"
|
|
|
|
|
|
|
+ @click="handleCategoryClick('food')"
|
|
|
>
|
|
>
|
|
|
餐食
|
|
餐食
|
|
|
</el-button>
|
|
</el-button>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
<!-- 内容区域 -->
|
|
<!-- 内容区域 -->
|
|
|
- <div v-if="filteredDishList.length > 0" class="content-section">
|
|
|
|
|
|
|
+ <div v-if="dishList.length > 0" class="content-section">
|
|
|
<!-- 菜品卡片网格 -->
|
|
<!-- 菜品卡片网格 -->
|
|
|
<div class="dish-grid">
|
|
<div class="dish-grid">
|
|
|
<div v-for="(dish, index) in paginatedDishList" :key="dish.id" class="dish-card">
|
|
<div v-for="(dish, index) in paginatedDishList" :key="dish.id" class="dish-card">
|
|
@@ -81,87 +81,89 @@
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
<!-- 新建/编辑酒单弹窗 -->
|
|
<!-- 新建/编辑酒单弹窗 -->
|
|
|
- <el-dialog v-model="dialogVisible" :title="dialogTitle" width="600px" @close="resetForm">
|
|
|
|
|
- <el-form ref="formRef" :model="formData" :rules="rules" label-width="100px">
|
|
|
|
|
- <el-form-item label="类型" prop="itemType">
|
|
|
|
|
- <el-radio-group v-model="formData.itemType">
|
|
|
|
|
- <el-radio label="drink"> 酒水 </el-radio>
|
|
|
|
|
- <el-radio label="food"> 餐食 </el-radio>
|
|
|
|
|
- </el-radio-group>
|
|
|
|
|
- </el-form-item>
|
|
|
|
|
- <el-form-item label="名称*" prop="dishName">
|
|
|
|
|
- <el-input v-model="formData.dishName" placeholder="请输入" maxlength="10" show-word-limit clearable />
|
|
|
|
|
- </el-form-item>
|
|
|
|
|
- <el-form-item label="价格 (¥)*" prop="dishPrice">
|
|
|
|
|
- <el-input-number
|
|
|
|
|
- v-model="formData.dishPrice"
|
|
|
|
|
- :min="0"
|
|
|
|
|
- :max="99999.99"
|
|
|
|
|
- :precision="2"
|
|
|
|
|
- :step="0.01"
|
|
|
|
|
- placeholder="请输入"
|
|
|
|
|
- style="width: 100%"
|
|
|
|
|
- />
|
|
|
|
|
- </el-form-item>
|
|
|
|
|
- <el-form-item label="成本价 (¥)*" prop="costPrice">
|
|
|
|
|
- <el-input-number
|
|
|
|
|
- v-model="formData.costPrice"
|
|
|
|
|
- :min="0"
|
|
|
|
|
- :max="99999.99"
|
|
|
|
|
- :precision="2"
|
|
|
|
|
- :step="0.01"
|
|
|
|
|
- placeholder="请输入"
|
|
|
|
|
- style="width: 100%"
|
|
|
|
|
- />
|
|
|
|
|
- </el-form-item>
|
|
|
|
|
- <el-form-item v-if="formData.itemType === 'drink'" label="品类" prop="category">
|
|
|
|
|
- <el-input v-model="formData.category" placeholder="请输入" clearable />
|
|
|
|
|
- </el-form-item>
|
|
|
|
|
- <el-form-item v-if="formData.itemType === 'drink'" label="酒精度 (%vol)" prop="alcoholContent">
|
|
|
|
|
- <el-input-number
|
|
|
|
|
- v-model="formData.alcoholContent"
|
|
|
|
|
- :min="0"
|
|
|
|
|
- :max="100"
|
|
|
|
|
- :precision="1"
|
|
|
|
|
- :step="0.1"
|
|
|
|
|
- placeholder="请输入"
|
|
|
|
|
- style="width: 100%"
|
|
|
|
|
- />
|
|
|
|
|
- </el-form-item>
|
|
|
|
|
- <el-form-item v-if="formData.itemType === 'drink'" label="风味" prop="flavor">
|
|
|
|
|
- <el-input v-model="formData.flavor" placeholder="请输入" clearable />
|
|
|
|
|
- </el-form-item>
|
|
|
|
|
- <el-form-item label="单位" prop="dishesUnit">
|
|
|
|
|
- <el-select v-model="formData.dishesUnit" placeholder="请选择" style="width: 100%">
|
|
|
|
|
- <el-option v-for="unit in unitOptions" :key="unit.value" :label="unit.label" :value="unit.value" />
|
|
|
|
|
- </el-select>
|
|
|
|
|
- </el-form-item>
|
|
|
|
|
- <el-form-item label="图片*" prop="imgUrl">
|
|
|
|
|
- <UploadImg
|
|
|
|
|
- v-model:image-url="formData.imgUrl"
|
|
|
|
|
- :width="'200px'"
|
|
|
|
|
- :height="'200px'"
|
|
|
|
|
- :file-size="9999"
|
|
|
|
|
- :api="uploadImg"
|
|
|
|
|
- :file-type="['image/jpeg', 'image/png', 'image/gif', 'image/webp']"
|
|
|
|
|
- :border-radius="'8px'"
|
|
|
|
|
- />
|
|
|
|
|
- </el-form-item>
|
|
|
|
|
- <el-form-item label="描述" prop="description">
|
|
|
|
|
- <el-input
|
|
|
|
|
- v-model="formData.description"
|
|
|
|
|
- type="textarea"
|
|
|
|
|
- :rows="4"
|
|
|
|
|
- placeholder="请输入"
|
|
|
|
|
- maxlength="300"
|
|
|
|
|
- show-word-limit
|
|
|
|
|
- clearable
|
|
|
|
|
- />
|
|
|
|
|
- </el-form-item>
|
|
|
|
|
- <el-form-item label="" prop="dishType">
|
|
|
|
|
- <el-checkbox v-model="formData.isRecommended"> 设为推荐 </el-checkbox>
|
|
|
|
|
- </el-form-item>
|
|
|
|
|
- </el-form>
|
|
|
|
|
|
|
+ <el-dialog v-model="dialogVisible" :title="dialogTitle" width="600px" class="wine-menu-dialog" @close="resetForm">
|
|
|
|
|
+ <div class="dialog-content-wrapper">
|
|
|
|
|
+ <el-form ref="formRef" :model="formData" :rules="rules" label-width="100px">
|
|
|
|
|
+ <el-form-item label="类型" prop="dishMenuType">
|
|
|
|
|
+ <el-radio-group v-model="formData.dishMenuType">
|
|
|
|
|
+ <el-radio label="drink"> 酒水 </el-radio>
|
|
|
|
|
+ <el-radio label="food"> 餐食 </el-radio>
|
|
|
|
|
+ </el-radio-group>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="名称*" prop="dishName">
|
|
|
|
|
+ <el-input v-model="formData.dishName" placeholder="请输入" maxlength="10" show-word-limit clearable />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="价格 (¥)*" prop="dishPrice">
|
|
|
|
|
+ <el-input-number
|
|
|
|
|
+ v-model="formData.dishPrice"
|
|
|
|
|
+ :min="0"
|
|
|
|
|
+ :max="99999.99"
|
|
|
|
|
+ :precision="2"
|
|
|
|
|
+ :step="0.01"
|
|
|
|
|
+ placeholder="请输入"
|
|
|
|
|
+ style="width: 100%"
|
|
|
|
|
+ />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="成本价 (¥)*" prop="costPrice">
|
|
|
|
|
+ <el-input-number
|
|
|
|
|
+ v-model="formData.costPrice"
|
|
|
|
|
+ :min="0"
|
|
|
|
|
+ :max="99999.99"
|
|
|
|
|
+ :precision="2"
|
|
|
|
|
+ :step="0.01"
|
|
|
|
|
+ placeholder="请输入"
|
|
|
|
|
+ style="width: 100%"
|
|
|
|
|
+ />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item v-if="formData.dishMenuType === 'drink'" label="品类" prop="category">
|
|
|
|
|
+ <el-input v-model="formData.category" placeholder="请输入" clearable />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item v-if="formData.dishMenuType === 'drink'" label="酒精度 (%vol)" prop="alcoholContent">
|
|
|
|
|
+ <el-input-number
|
|
|
|
|
+ v-model="formData.alcoholContent"
|
|
|
|
|
+ :min="0"
|
|
|
|
|
+ :max="100"
|
|
|
|
|
+ :precision="1"
|
|
|
|
|
+ :step="0.1"
|
|
|
|
|
+ placeholder="请输入"
|
|
|
|
|
+ style="width: 100%"
|
|
|
|
|
+ />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item v-if="formData.dishMenuType === 'drink'" label="风味" prop="flavor">
|
|
|
|
|
+ <el-input v-model="formData.flavor" placeholder="请输入" clearable />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="单位" prop="dishesUnit">
|
|
|
|
|
+ <el-select v-model="formData.dishesUnit" placeholder="请选择" style="width: 100%">
|
|
|
|
|
+ <el-option v-for="unit in unitOptions" :key="unit.value" :label="unit.label" :value="unit.value" />
|
|
|
|
|
+ </el-select>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="图片*" prop="imgUrl">
|
|
|
|
|
+ <UploadImg
|
|
|
|
|
+ v-model:image-url="formData.imgUrl"
|
|
|
|
|
+ :width="'200px'"
|
|
|
|
|
+ :height="'200px'"
|
|
|
|
|
+ :file-size="9999"
|
|
|
|
|
+ :api="uploadImg"
|
|
|
|
|
+ :file-type="['image/jpeg', 'image/png', 'image/gif', 'image/webp']"
|
|
|
|
|
+ :border-radius="'8px'"
|
|
|
|
|
+ />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="描述" prop="description">
|
|
|
|
|
+ <el-input
|
|
|
|
|
+ v-model="formData.description"
|
|
|
|
|
+ type="textarea"
|
|
|
|
|
+ :rows="4"
|
|
|
|
|
+ placeholder="请输入"
|
|
|
|
|
+ maxlength="300"
|
|
|
|
|
+ show-word-limit
|
|
|
|
|
+ clearable
|
|
|
|
|
+ />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="" prop="dishType">
|
|
|
|
|
+ <el-checkbox v-model="formData.isRecommended"> 设为推荐 </el-checkbox>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ </el-form>
|
|
|
|
|
+ </div>
|
|
|
<template #footer>
|
|
<template #footer>
|
|
|
<div class="dialog-footer">
|
|
<div class="dialog-footer">
|
|
|
<el-button @click="dialogVisible = false"> 取消 </el-button>
|
|
<el-button @click="dialogVisible = false"> 取消 </el-button>
|
|
@@ -174,30 +176,43 @@
|
|
|
|
|
|
|
|
<!-- 批量导入弹窗 -->
|
|
<!-- 批量导入弹窗 -->
|
|
|
<el-dialog v-model="batchImportVisible" title="批量导入" width="500px">
|
|
<el-dialog v-model="batchImportVisible" title="批量导入" width="500px">
|
|
|
- <el-upload
|
|
|
|
|
- ref="uploadRef"
|
|
|
|
|
- :auto-upload="false"
|
|
|
|
|
- :on-change="handleFileChange"
|
|
|
|
|
- :on-remove="handleFileRemove"
|
|
|
|
|
- :limit="1"
|
|
|
|
|
- accept=".xlsx,.xls"
|
|
|
|
|
- drag
|
|
|
|
|
- >
|
|
|
|
|
- <el-icon class="el-icon--upload">
|
|
|
|
|
- <upload-filled />
|
|
|
|
|
- </el-icon>
|
|
|
|
|
- <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
|
|
|
|
|
- <template #tip>
|
|
|
|
|
- <div class="el-upload__tip">只能上传 xlsx/xls 文件</div>
|
|
|
|
|
- </template>
|
|
|
|
|
- </el-upload>
|
|
|
|
|
- <div class="import-tip">
|
|
|
|
|
- <el-link type="primary" :underline="false" @click="downloadTemplate"> 下载导入模板 </el-link>
|
|
|
|
|
|
|
+ <div class="import-steps">
|
|
|
|
|
+ <!-- 第一步:下载模板 -->
|
|
|
|
|
+ <div class="import-step">
|
|
|
|
|
+ <div class="step-header">
|
|
|
|
|
+ <div class="step-number">1</div>
|
|
|
|
|
+ <div class="step-title">下载模板</div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <el-button type="primary" @click="downloadTemplate"> 下载excel模板 </el-button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 第二步:上传文件 -->
|
|
|
|
|
+ <div class="import-step">
|
|
|
|
|
+ <div class="step-header">
|
|
|
|
|
+ <div class="step-number">2</div>
|
|
|
|
|
+ <div class="step-title">上传文件</div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <el-upload
|
|
|
|
|
+ ref="uploadRef"
|
|
|
|
|
+ :auto-upload="false"
|
|
|
|
|
+ :on-change="handleFileChange"
|
|
|
|
|
+ :on-remove="handleFileRemove"
|
|
|
|
|
+ :limit="1"
|
|
|
|
|
+ accept=".xlsx,.xls"
|
|
|
|
|
+ drag
|
|
|
|
|
+ class="import-upload"
|
|
|
|
|
+ >
|
|
|
|
|
+ <el-icon class="el-icon--upload">
|
|
|
|
|
+ <UploadFilled />
|
|
|
|
|
+ </el-icon>
|
|
|
|
|
+ <div class="el-upload__text">点击上传文件或拖拽上传文件</div>
|
|
|
|
|
+ </el-upload>
|
|
|
|
|
+ </div>
|
|
|
</div>
|
|
</div>
|
|
|
<template #footer>
|
|
<template #footer>
|
|
|
<div class="dialog-footer">
|
|
<div class="dialog-footer">
|
|
|
<el-button @click="batchImportVisible = false"> 取消 </el-button>
|
|
<el-button @click="batchImportVisible = false"> 取消 </el-button>
|
|
|
- <el-button type="primary" :loading="importLoading" @click="handleImportSubmit"> 确定 </el-button>
|
|
|
|
|
|
|
+ <el-button type="primary" :loading="importLoading" @click="handleImportSubmit"> 导入 </el-button>
|
|
|
</div>
|
|
</div>
|
|
|
</template>
|
|
</template>
|
|
|
</el-dialog>
|
|
</el-dialog>
|
|
@@ -212,7 +227,15 @@ import { Picture, UploadFilled } from "@element-plus/icons-vue";
|
|
|
import UploadImg from "@/components/Upload/Img.vue";
|
|
import UploadImg from "@/components/Upload/Img.vue";
|
|
|
import { uploadImg } from "@/api/modules/newLoginApi";
|
|
import { uploadImg } from "@/api/modules/newLoginApi";
|
|
|
import { localGet } from "@/utils";
|
|
import { localGet } from "@/utils";
|
|
|
-import { createOrUpdateDish, getDishList, getDishDetail, deleteDish } from "@/api/modules/storeDecoration";
|
|
|
|
|
|
|
+import {
|
|
|
|
|
+ getBarMenuList,
|
|
|
|
|
+ saveOrUpdateBarMenu,
|
|
|
|
|
+ deleteBarMenu,
|
|
|
|
|
+ getDishDetail,
|
|
|
|
|
+ downloadBarMenuTemplate,
|
|
|
|
|
+ importBarMenuExcel,
|
|
|
|
|
+ saveOfficialImg
|
|
|
|
|
+} from "@/api/modules/storeDecoration";
|
|
|
|
|
|
|
|
// 酒单接口
|
|
// 酒单接口
|
|
|
interface Dish {
|
|
interface Dish {
|
|
@@ -225,7 +248,7 @@ interface Dish {
|
|
|
description?: string;
|
|
description?: string;
|
|
|
dishType: number; // 0:未推荐, 1:推荐
|
|
dishType: number; // 0:未推荐, 1:推荐
|
|
|
likeCount?: number;
|
|
likeCount?: number;
|
|
|
- itemType?: string; // 'drink' | 'food'
|
|
|
|
|
|
|
+ dishMenuType: string | number; // 'drink' | 'food' 或 1(餐食) | 2(酒水)
|
|
|
category?: string;
|
|
category?: string;
|
|
|
alcoholContent?: number;
|
|
alcoholContent?: number;
|
|
|
flavor?: string;
|
|
flavor?: string;
|
|
@@ -258,21 +281,6 @@ const unitOptions = [
|
|
|
// 酒单列表
|
|
// 酒单列表
|
|
|
const dishList = ref<Dish[]>([]);
|
|
const dishList = ref<Dish[]>([]);
|
|
|
|
|
|
|
|
-// 根据分类筛选后的列表
|
|
|
|
|
-const filteredDishList = computed(() => {
|
|
|
|
|
- if (activeTab.value === "recommended") {
|
|
|
|
|
- return dishList.value;
|
|
|
|
|
- }
|
|
|
|
|
- // 在菜单tab下,根据分类筛选
|
|
|
|
|
- return dishList.value.filter(dish => {
|
|
|
|
|
- if (activeCategory.value === "drink") {
|
|
|
|
|
- return dish.itemType === "drink";
|
|
|
|
|
- } else {
|
|
|
|
|
- return dish.itemType === "food";
|
|
|
|
|
- }
|
|
|
|
|
- });
|
|
|
|
|
-});
|
|
|
|
|
-
|
|
|
|
|
// 分页数据
|
|
// 分页数据
|
|
|
const pageable = reactive({
|
|
const pageable = reactive({
|
|
|
pageNum: 1,
|
|
pageNum: 1,
|
|
@@ -282,7 +290,7 @@ const pageable = reactive({
|
|
|
|
|
|
|
|
// 分页后的酒单列表
|
|
// 分页后的酒单列表
|
|
|
const paginatedDishList = computed(() => {
|
|
const paginatedDishList = computed(() => {
|
|
|
- const list = filteredDishList.value;
|
|
|
|
|
|
|
+ const list = dishList.value;
|
|
|
const start = (pageable.pageNum - 1) * pageable.pageSize;
|
|
const start = (pageable.pageNum - 1) * pageable.pageSize;
|
|
|
const end = start + pageable.pageSize;
|
|
const end = start + pageable.pageSize;
|
|
|
return list.slice(start, end);
|
|
return list.slice(start, end);
|
|
@@ -293,7 +301,7 @@ const dialogTitle = computed(() => (editIndex.value !== null ? "编辑" : "新
|
|
|
|
|
|
|
|
// 表单数据
|
|
// 表单数据
|
|
|
const formData = reactive({
|
|
const formData = reactive({
|
|
|
- itemType: "drink" as "drink" | "food",
|
|
|
|
|
|
|
+ dishMenuType: "drink" as "drink" | "food",
|
|
|
dishName: "",
|
|
dishName: "",
|
|
|
dishPrice: undefined as number | undefined,
|
|
dishPrice: undefined as number | undefined,
|
|
|
costPrice: undefined as number | undefined,
|
|
costPrice: undefined as number | undefined,
|
|
@@ -360,23 +368,26 @@ const handleTabClick = async val => {
|
|
|
});
|
|
});
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
|
|
+// 分类点击处理
|
|
|
|
|
+const handleCategoryClick = async (category: "drink" | "food") => {
|
|
|
|
|
+ if (activeCategory.value === category) {
|
|
|
|
|
+ return; // 如果已经是当前分类,不重复调用
|
|
|
|
|
+ }
|
|
|
|
|
+ activeCategory.value = category;
|
|
|
|
|
+ await handleCategoryChange();
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
// 分类切换
|
|
// 分类切换
|
|
|
-const handleCategoryChange = () => {
|
|
|
|
|
|
|
+const handleCategoryChange = async () => {
|
|
|
pageable.pageNum = 1;
|
|
pageable.pageNum = 1;
|
|
|
|
|
+ // 重新调用接口获取数据
|
|
|
|
|
+ await loadDishList();
|
|
|
updatePagination();
|
|
updatePagination();
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
-// 监听分类变化
|
|
|
|
|
-watch(
|
|
|
|
|
- () => activeCategory.value,
|
|
|
|
|
- () => {
|
|
|
|
|
- handleCategoryChange();
|
|
|
|
|
- }
|
|
|
|
|
-);
|
|
|
|
|
-
|
|
|
|
|
// 更新分页总数
|
|
// 更新分页总数
|
|
|
const updatePagination = () => {
|
|
const updatePagination = () => {
|
|
|
- pageable.total = filteredDishList.value.length;
|
|
|
|
|
|
|
+ pageable.total = dishList.value.length;
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
// 分页大小改变
|
|
// 分页大小改变
|
|
@@ -407,26 +418,27 @@ const editDish = async (dish: Dish, index: number) => {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
try {
|
|
|
- // 调用获取酒单详情接口
|
|
|
|
|
- const res: any = await getDishDetail({ id: dish.id });
|
|
|
|
|
- if (res && (res.code === 200 || res.code === "200") && res.data) {
|
|
|
|
|
- const dishDetail = res.data;
|
|
|
|
|
- editId.value = dishDetail.id;
|
|
|
|
|
- formData.itemType = dishDetail.itemType || "drink";
|
|
|
|
|
- formData.dishName = dishDetail.dishName || "";
|
|
|
|
|
- formData.dishPrice = dishDetail.dishPrice;
|
|
|
|
|
- formData.costPrice = dishDetail.costPrice;
|
|
|
|
|
- formData.dishesUnit = dishDetail.dishesUnit || "份";
|
|
|
|
|
- formData.imgUrl = dishDetail.imgUrl || "";
|
|
|
|
|
- formData.description = dishDetail.description || "";
|
|
|
|
|
- formData.isRecommended = dishDetail.dishType === 1;
|
|
|
|
|
- formData.category = dishDetail.category || "";
|
|
|
|
|
- formData.alcoholContent = dishDetail.alcoholContent;
|
|
|
|
|
- formData.flavor = dishDetail.flavor || "";
|
|
|
|
|
- dialogVisible.value = true;
|
|
|
|
|
|
|
+ // 使用列表中的数据作为详情(新接口可能没有单独的详情接口)
|
|
|
|
|
+ // 或者可以从列表中直接获取
|
|
|
|
|
+ const dishDetail = dish;
|
|
|
|
|
+ editId.value = dishDetail.id || null;
|
|
|
|
|
+ // 将dishMenuType转换为字符串格式:1=餐食(food), 2=酒水(drink)
|
|
|
|
|
+ if (typeof dishDetail.dishMenuType === "number") {
|
|
|
|
|
+ formData.dishMenuType = dishDetail.dishMenuType === 2 ? "drink" : "food";
|
|
|
} else {
|
|
} else {
|
|
|
- ElMessage.error(res?.msg || "获取酒单详情失败");
|
|
|
|
|
|
|
+ formData.dishMenuType = (dishDetail.dishMenuType || "drink") as "drink" | "food";
|
|
|
}
|
|
}
|
|
|
|
|
+ formData.dishName = dishDetail.dishName || "";
|
|
|
|
|
+ formData.dishPrice = dishDetail.dishPrice;
|
|
|
|
|
+ formData.costPrice = dishDetail.costPrice;
|
|
|
|
|
+ formData.dishesUnit = dishDetail.dishesUnit || "份";
|
|
|
|
|
+ formData.imgUrl = dishDetail.imgUrl || "";
|
|
|
|
|
+ formData.description = dishDetail.description || "";
|
|
|
|
|
+ formData.isRecommended = dishDetail.dishType === 1;
|
|
|
|
|
+ formData.category = dishDetail.category || "";
|
|
|
|
|
+ formData.alcoholContent = dishDetail.alcoholContent;
|
|
|
|
|
+ formData.flavor = dishDetail.flavor || "";
|
|
|
|
|
+ dialogVisible.value = true;
|
|
|
} catch (error: any) {
|
|
} catch (error: any) {
|
|
|
console.error("获取酒单详情失败:", error);
|
|
console.error("获取酒单详情失败:", error);
|
|
|
ElMessage.error(error?.msg || "获取酒单详情失败,请重试");
|
|
ElMessage.error(error?.msg || "获取酒单详情失败,请重试");
|
|
@@ -448,16 +460,8 @@ const deleteDishHandler = async (id: string | number, index: number) => {
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // 根据当前tab确定dishType:0表示菜单, 1表示推荐
|
|
|
|
|
- const dishType = activeTab.value === "recommended" ? 1 : 0;
|
|
|
|
|
-
|
|
|
|
|
- // 调用删除接口
|
|
|
|
|
- const params = {
|
|
|
|
|
- dishType: dishType,
|
|
|
|
|
- ids: id
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- const res: any = await deleteDish(params);
|
|
|
|
|
|
|
+ // 调用删除接口,新接口只需要id参数
|
|
|
|
|
+ const res: any = await deleteBarMenu({ id: Number(id) });
|
|
|
if (res && (res.code === 200 || res.code === "200")) {
|
|
if (res && (res.code === 200 || res.code === "200")) {
|
|
|
ElMessage.success("删除成功");
|
|
ElMessage.success("删除成功");
|
|
|
await loadDishList();
|
|
await loadDishList();
|
|
@@ -494,6 +498,10 @@ const setRecommend = async (id: string | number, index: number) => {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// 通过更新接口设置推荐
|
|
// 通过更新接口设置推荐
|
|
|
|
|
+ // 将dishMenuType转换为数字:drink=2, food=1
|
|
|
|
|
+ const dishMenuTypeNum =
|
|
|
|
|
+ typeof dish.dishMenuType === "string" ? (dish.dishMenuType === "drink" ? 2 : 1) : dish.dishMenuType || 2;
|
|
|
|
|
+
|
|
|
const params: any = {
|
|
const params: any = {
|
|
|
id: dish.id,
|
|
id: dish.id,
|
|
|
storeId: Number(storeId),
|
|
storeId: Number(storeId),
|
|
@@ -504,13 +512,13 @@ const setRecommend = async (id: string | number, index: number) => {
|
|
|
imgUrl: dish.imgUrl,
|
|
imgUrl: dish.imgUrl,
|
|
|
description: dish.description || "",
|
|
description: dish.description || "",
|
|
|
dishType: 1, // 设置为推荐
|
|
dishType: 1, // 设置为推荐
|
|
|
- itemType: dish.itemType || "drink",
|
|
|
|
|
|
|
+ dishMenuType: dishMenuTypeNum,
|
|
|
category: dish.category || "",
|
|
category: dish.category || "",
|
|
|
alcoholContent: dish.alcoholContent,
|
|
alcoholContent: dish.alcoholContent,
|
|
|
flavor: dish.flavor || ""
|
|
flavor: dish.flavor || ""
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
- const res: any = await createOrUpdateDish(params);
|
|
|
|
|
|
|
+ const res: any = await saveOrUpdateBarMenu(params);
|
|
|
if (res && (res.code === 200 || res.code === "200")) {
|
|
if (res && (res.code === 200 || res.code === "200")) {
|
|
|
ElMessage.success("设置推荐成功");
|
|
ElMessage.success("设置推荐成功");
|
|
|
await loadDishList();
|
|
await loadDishList();
|
|
@@ -541,6 +549,10 @@ const cancelRecommend = async (id: string | number, index: number) => {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// 通过更新接口取消推荐
|
|
// 通过更新接口取消推荐
|
|
|
|
|
+ // 将dishMenuType转换为数字:drink=2, food=1
|
|
|
|
|
+ const dishMenuTypeNum =
|
|
|
|
|
+ typeof dish.dishMenuType === "string" ? (dish.dishMenuType === "drink" ? 2 : 1) : dish.dishMenuType || 2;
|
|
|
|
|
+
|
|
|
const params: any = {
|
|
const params: any = {
|
|
|
id: dish.id,
|
|
id: dish.id,
|
|
|
storeId: Number(storeId),
|
|
storeId: Number(storeId),
|
|
@@ -551,13 +563,13 @@ const cancelRecommend = async (id: string | number, index: number) => {
|
|
|
imgUrl: dish.imgUrl,
|
|
imgUrl: dish.imgUrl,
|
|
|
description: dish.description || "",
|
|
description: dish.description || "",
|
|
|
dishType: 0, // 设置为未推荐
|
|
dishType: 0, // 设置为未推荐
|
|
|
- itemType: dish.itemType || "drink",
|
|
|
|
|
|
|
+ dishMenuType: dishMenuTypeNum,
|
|
|
category: dish.category || "",
|
|
category: dish.category || "",
|
|
|
alcoholContent: dish.alcoholContent,
|
|
alcoholContent: dish.alcoholContent,
|
|
|
flavor: dish.flavor || ""
|
|
flavor: dish.flavor || ""
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
- const res: any = await createOrUpdateDish(params);
|
|
|
|
|
|
|
+ const res: any = await saveOrUpdateBarMenu(params);
|
|
|
if (res && (res.code === 200 || res.code === "200")) {
|
|
if (res && (res.code === 200 || res.code === "200")) {
|
|
|
ElMessage.success("取消推荐成功");
|
|
ElMessage.success("取消推荐成功");
|
|
|
await loadDishList();
|
|
await loadDishList();
|
|
@@ -573,7 +585,7 @@ const cancelRecommend = async (id: string | number, index: number) => {
|
|
|
|
|
|
|
|
// 重置表单
|
|
// 重置表单
|
|
|
const resetForm = () => {
|
|
const resetForm = () => {
|
|
|
- formData.itemType = "drink";
|
|
|
|
|
|
|
+ formData.dishMenuType = "drink";
|
|
|
formData.dishName = "";
|
|
formData.dishName = "";
|
|
|
formData.dishPrice = undefined;
|
|
formData.dishPrice = undefined;
|
|
|
formData.costPrice = undefined;
|
|
formData.costPrice = undefined;
|
|
@@ -610,6 +622,9 @@ const handleSubmit = async () => {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// 构建请求参数
|
|
// 构建请求参数
|
|
|
|
|
+ // 将dishMenuType转换为数字:drink=2(酒水), food=1(餐食)
|
|
|
|
|
+ const dishMenuTypeNum = formData.dishMenuType === "drink" ? 2 : 1;
|
|
|
|
|
+
|
|
|
const params: any = {
|
|
const params: any = {
|
|
|
storeId: Number(storeId),
|
|
storeId: Number(storeId),
|
|
|
dishName: formData.dishName,
|
|
dishName: formData.dishName,
|
|
@@ -619,7 +634,7 @@ const handleSubmit = async () => {
|
|
|
imgUrl: formData.imgUrl,
|
|
imgUrl: formData.imgUrl,
|
|
|
description: formData.description || "",
|
|
description: formData.description || "",
|
|
|
dishType: formData.isRecommended ? 1 : 0,
|
|
dishType: formData.isRecommended ? 1 : 0,
|
|
|
- itemType: formData.itemType,
|
|
|
|
|
|
|
+ dishMenuType: dishMenuTypeNum,
|
|
|
category: formData.category || "",
|
|
category: formData.category || "",
|
|
|
alcoholContent: formData.alcoholContent,
|
|
alcoholContent: formData.alcoholContent,
|
|
|
flavor: formData.flavor || ""
|
|
flavor: formData.flavor || ""
|
|
@@ -630,8 +645,28 @@ const handleSubmit = async () => {
|
|
|
params.id = editId.value;
|
|
params.id = editId.value;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- const res: any = await createOrUpdateDish(params);
|
|
|
|
|
|
|
+ // 新接口需要将参数包装在storeMenu对象中
|
|
|
|
|
+ const res: any = await saveOrUpdateBarMenu(params);
|
|
|
if (res && (res.code === 200 || res.code === "200")) {
|
|
if (res && (res.code === 200 || res.code === "200")) {
|
|
|
|
|
+ // 调用saveOfficialImg接口保存图片信息
|
|
|
|
|
+ if (formData.imgUrl) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ const saveImgParams = [
|
|
|
|
|
+ {
|
|
|
|
|
+ imgUrl: formData.imgUrl,
|
|
|
|
|
+ imgType: 7, // 参数imageType设置为7
|
|
|
|
|
+ businessId: res.data?.id || editId.value, // 使用返回的id或编辑的id作为businessId
|
|
|
|
|
+ storeId: Number(storeId),
|
|
|
|
|
+ imgSort: 0 // 默认排序
|
|
|
|
|
+ }
|
|
|
|
|
+ ];
|
|
|
|
|
+ await saveOfficialImg(saveImgParams);
|
|
|
|
|
+ } catch (error: any) {
|
|
|
|
|
+ console.error("保存图片信息失败:", error);
|
|
|
|
|
+ // 图片保存失败不影响主流程
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
ElMessage.success(editId.value ? "编辑成功" : "新建成功");
|
|
ElMessage.success(editId.value ? "编辑成功" : "新建成功");
|
|
|
dialogVisible.value = false;
|
|
dialogVisible.value = false;
|
|
|
resetForm();
|
|
resetForm();
|
|
@@ -659,11 +694,32 @@ const loadDishList = async () => {
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // 根据tab切换调用不同的dishType参数
|
|
|
|
|
- // dishType: 0表示菜单, 1表示推荐
|
|
|
|
|
- const dishType = activeTab.value === "recommended" ? 1 : 0;
|
|
|
|
|
- const res: any = await getDishList({ storeId: Number(storeId), dishType });
|
|
|
|
|
|
|
+ // 根据分类筛选传递dishMenuType参数
|
|
|
|
|
+ // dishMenuType: 1-菜单(餐食), 2-酒水
|
|
|
|
|
+ // 根据tab传递dishType参数
|
|
|
|
|
+ // dishType: 0-非推荐, 1-推荐
|
|
|
|
|
+ let dishMenuType: number | undefined;
|
|
|
|
|
+ let dishType: number | undefined;
|
|
|
|
|
+
|
|
|
|
|
+ if (activeTab.value === "menu") {
|
|
|
|
|
+ // 在菜单tab下,根据activeCategory传递dishMenuType,dishType为0(非推荐)
|
|
|
|
|
+ dishMenuType = activeCategory.value === "drink" ? 2 : 1;
|
|
|
|
|
+ // dishType = 0;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // 在推荐tab下,不传dishMenuType(获取所有类型),dishType为1(推荐)
|
|
|
|
|
+ dishType = 1;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
|
|
+ const params: any = { storeId: Number(storeId) };
|
|
|
|
|
+ if (dishMenuType !== undefined) {
|
|
|
|
|
+ params.dishMenuType = dishMenuType;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (dishType !== undefined) {
|
|
|
|
|
+ params.dishType = dishType;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const res: any = await getBarMenuList(params);
|
|
|
|
|
+ console.log(res, "res--x-res--xres-res");
|
|
|
if (res && (res.code === 200 || res.code === "200") && res.data) {
|
|
if (res && (res.code === 200 || res.code === "200") && res.data) {
|
|
|
const dataList = Array.isArray(res.data) ? res.data : [];
|
|
const dataList = Array.isArray(res.data) ? res.data : [];
|
|
|
dishList.value = dataList.map((dish: any) => ({
|
|
dishList.value = dataList.map((dish: any) => ({
|
|
@@ -676,11 +732,14 @@ const loadDishList = async () => {
|
|
|
description: dish.description || "",
|
|
description: dish.description || "",
|
|
|
dishType: dish.dishType !== undefined ? dish.dishType : 0,
|
|
dishType: dish.dishType !== undefined ? dish.dishType : 0,
|
|
|
likeCount: dish.likeCount || 0,
|
|
likeCount: dish.likeCount || 0,
|
|
|
- itemType: dish.itemType || "drink",
|
|
|
|
|
|
|
+ // 将后端返回的数字转换为字符串:1-餐食(food), 2-酒水(drink)
|
|
|
|
|
+ dishMenuType:
|
|
|
|
|
+ typeof dish.dishMenuType === "number" ? (dish.dishMenuType === 2 ? "drink" : "food") : dish.dishMenuType || "drink",
|
|
|
category: dish.category || "",
|
|
category: dish.category || "",
|
|
|
alcoholContent: dish.alcoholContent,
|
|
alcoholContent: dish.alcoholContent,
|
|
|
flavor: dish.flavor || ""
|
|
flavor: dish.flavor || ""
|
|
|
}));
|
|
}));
|
|
|
|
|
+ console.log(dishList.value, "dishList-xdishList");
|
|
|
}
|
|
}
|
|
|
} catch (error) {
|
|
} catch (error) {
|
|
|
console.error("获取酒单列表失败:", error);
|
|
console.error("获取酒单列表失败:", error);
|
|
@@ -703,24 +762,70 @@ const handleFileRemove = () => {
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
// 下载模板
|
|
// 下载模板
|
|
|
-const downloadTemplate = () => {
|
|
|
|
|
- ElMessage.info("模板下载功能开发中");
|
|
|
|
|
- // TODO: 实现模板下载
|
|
|
|
|
|
|
+const downloadTemplate = async () => {
|
|
|
|
|
+ try {
|
|
|
|
|
+ const res: any = await downloadBarMenuTemplate();
|
|
|
|
|
+ // 如果响应是blob类型,直接使用;否则尝试从data中获取
|
|
|
|
|
+ const blob =
|
|
|
|
|
+ res instanceof Blob
|
|
|
|
|
+ ? res
|
|
|
|
|
+ : new Blob([res], {
|
|
|
|
|
+ type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
|
|
|
|
|
+ });
|
|
|
|
|
+ // 创建下载链接
|
|
|
|
|
+ const url = window.URL.createObjectURL(blob);
|
|
|
|
|
+ const link = document.createElement("a");
|
|
|
|
|
+ link.href = url;
|
|
|
|
|
+ link.download = `酒单导入模板_${new Date().getTime()}.xlsx`;
|
|
|
|
|
+ document.body.appendChild(link);
|
|
|
|
|
+ link.click();
|
|
|
|
|
+ document.body.removeChild(link);
|
|
|
|
|
+ window.URL.revokeObjectURL(url);
|
|
|
|
|
+ ElMessage.success("模板下载成功");
|
|
|
|
|
+ } catch (error: any) {
|
|
|
|
|
+ console.error("下载模板失败:", error);
|
|
|
|
|
+ ElMessage.error(error?.msg || "模板下载失败,请重试");
|
|
|
|
|
+ }
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
// 提交导入
|
|
// 提交导入
|
|
|
const handleImportSubmit = async () => {
|
|
const handleImportSubmit = async () => {
|
|
|
- if (!importFile.value) {
|
|
|
|
|
|
|
+ if (!importFile.value || !importFile.value.raw) {
|
|
|
ElMessage.warning("请先选择文件");
|
|
ElMessage.warning("请先选择文件");
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ const userInfo: any = localGet("geeker-user")?.userInfo || {};
|
|
|
|
|
+ const storeId = userInfo.storeId;
|
|
|
|
|
+ if (!storeId) {
|
|
|
|
|
+ ElMessage.error("未找到店铺ID");
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
importLoading.value = true;
|
|
importLoading.value = true;
|
|
|
try {
|
|
try {
|
|
|
- // TODO: 实现批量导入逻辑
|
|
|
|
|
- ElMessage.info("批量导入功能开发中");
|
|
|
|
|
- batchImportVisible.value = false;
|
|
|
|
|
|
|
+ // 创建FormData对象,只包含file
|
|
|
|
|
+ const formData = new FormData();
|
|
|
|
|
+ formData.append("file", importFile.value.raw);
|
|
|
|
|
+
|
|
|
|
|
+ // storeId作为query参数传递
|
|
|
|
|
+ const res: any = await importBarMenuExcel(formData, storeId);
|
|
|
|
|
+ if (res && (res.code === 200 || res.code === "200")) {
|
|
|
|
|
+ ElMessage.success("导入成功");
|
|
|
|
|
+ batchImportVisible.value = false;
|
|
|
|
|
+ importFile.value = null;
|
|
|
|
|
+ // 清空上传组件的文件列表
|
|
|
|
|
+ if (uploadRef.value) {
|
|
|
|
|
+ uploadRef.value.clearFiles();
|
|
|
|
|
+ }
|
|
|
|
|
+ // 重新加载酒单列表
|
|
|
|
|
+ await loadDishList();
|
|
|
|
|
+ updatePagination();
|
|
|
|
|
+ } else {
|
|
|
|
|
+ ElMessage.error(res?.msg || "导入失败");
|
|
|
|
|
+ }
|
|
|
} catch (error: any) {
|
|
} catch (error: any) {
|
|
|
|
|
+ console.error("导入失败:", error);
|
|
|
ElMessage.error(error?.msg || "导入失败,请重试");
|
|
ElMessage.error(error?.msg || "导入失败,请重试");
|
|
|
} finally {
|
|
} finally {
|
|
|
importLoading.value = false;
|
|
importLoading.value = false;
|
|
@@ -850,9 +955,66 @@ onMounted(async () => {
|
|
|
gap: 12px;
|
|
gap: 12px;
|
|
|
justify-content: flex-end;
|
|
justify-content: flex-end;
|
|
|
}
|
|
}
|
|
|
- .import-tip {
|
|
|
|
|
- margin-top: 10px;
|
|
|
|
|
- text-align: center;
|
|
|
|
|
|
|
+ .import-steps {
|
|
|
|
|
+ .import-step {
|
|
|
|
|
+ margin-bottom: 30px;
|
|
|
|
|
+ &:last-child {
|
|
|
|
|
+ margin-bottom: 0;
|
|
|
|
|
+ }
|
|
|
|
|
+ .step-header {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ gap: 12px;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ margin-bottom: 16px;
|
|
|
|
|
+ .step-number {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ justify-content: center;
|
|
|
|
|
+ width: 24px;
|
|
|
|
|
+ height: 24px;
|
|
|
|
|
+ font-size: 14px;
|
|
|
|
|
+ font-weight: 500;
|
|
|
|
|
+ color: white;
|
|
|
|
|
+ background-color: var(--el-color-primary);
|
|
|
|
|
+ border-radius: 50%;
|
|
|
|
|
+ }
|
|
|
|
|
+ .step-title {
|
|
|
|
|
+ font-size: 16px;
|
|
|
|
|
+ font-weight: 500;
|
|
|
|
|
+ color: var(--el-text-color-primary);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ .import-upload {
|
|
|
|
|
+ :deep(.el-upload-dragger) {
|
|
|
|
|
+ width: 100%;
|
|
|
|
|
+ padding: 40px 20px;
|
|
|
|
|
+ .el-icon--upload {
|
|
|
|
|
+ font-size: 48px;
|
|
|
|
|
+ color: var(--el-text-color-placeholder);
|
|
|
|
|
+ }
|
|
|
|
|
+ .el-upload__text {
|
|
|
|
|
+ margin-top: 16px;
|
|
|
|
|
+ font-size: 14px;
|
|
|
|
|
+ color: var(--el-text-color-regular);
|
|
|
|
|
+ em {
|
|
|
|
|
+ font-style: normal;
|
|
|
|
|
+ color: var(--el-color-primary);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+:deep(.wine-menu-dialog) {
|
|
|
|
|
+ .el-dialog__body {
|
|
|
|
|
+ height: 600px;
|
|
|
|
|
+ padding: 20px;
|
|
|
|
|
+ overflow-y: auto;
|
|
|
|
|
+ }
|
|
|
|
|
+ .dialog-content-wrapper {
|
|
|
|
|
+ height: 100%;
|
|
|
|
|
+ overflow-y: auto;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
</style>
|
|
</style>
|