فهرست منبع

餐饮模块部分功能开发

徐滕 2 هفته پیش
والد
کامیت
7ea8f9e4de

+ 41 - 7
forge-admin-ui/src/views/restaurant/dishOrder.vue

@@ -51,6 +51,7 @@
         <div v-else class="order-table">
           <div class="order-table-head">
             <span style="flex:2;">菜品</span>
+            <span style="width:80px;text-align:center;">图片</span>
             <span style="width:120px;text-align:right;">单价</span>
             <span style="width:120px;text-align:right;">总价</span>
             <span style="width:80px;text-align:center;">数量</span>
@@ -58,6 +59,10 @@
           <div class="order-table-body">
             <div v-for="item in orderedList" :key="item.dishId" class="order-table-row">
               <span style="flex:2;">{{ item.dishName }}</span>
+              <span style="width:80px;text-align:center;">
+                <img v-if="item.previewImageUrl" style="width:48px;height:48px;border-radius:4px;object-fit:cover;cursor:pointer;" :src="item.previewImageUrl" @click="openDishPreview(item)">
+                <span v-else style="color:#ccc;font-size:12px;">无图</span>
+              </span>
               <span style="width:120px;text-align:right;">¥{{ formatPrice(item.salePrice) }}</span>
               <span style="width:120px;text-align:right;color:#f56c6c;">¥{{ formatPrice(itemTotal(item)) }}</span>
               <span style="width:80px;text-align:center;">{{ item.quantity }}</span>
@@ -121,9 +126,9 @@
                     </div>
                     <div class="dish-bottom">
                       <span class="dish-price">¥{{ formatPrice(item.salePrice) }}</span>
-                      <div v-if="orderedRow(item)" class="card-stepper">
+                      <div v-if="orderedRow(item) && (hasCurrentOrder ? orderedRow(item).pendingQuantity > 0 : true)" class="card-stepper">
                         <span class="step-btn" @click="reduceDish(orderedRow(item))">-</span>
-                        <span class="step-qty">{{ orderedRow(item).quantity }}</span>
+                        <span class="step-qty">{{ hasCurrentOrder ? orderedRow(item).pendingQuantity : orderedRow(item).quantity }}</span>
                         <span class="step-btn plus" @click="increaseOrderedDish(orderedRow(item))">+</span>
                       </div>
                       <NButton v-else size="tiny" type="primary" @click="addDish(item)">
@@ -183,16 +188,16 @@
                 ¥{{ formatPrice(item.salePrice) }}
               </div>
               <div class="ordered-total">
-                ¥{{ formatPrice(itemTotal(item)) }}
+                ¥{{ formatPrice(pendingItemTotal(item)) }}
               </div>
               <div class="ordered-qty">
-                <NButton v-if="!hasCurrentOrder" size="tiny" circle @click="reduceDish(item)">
+                <NButton size="tiny" circle @click="reduceDish(item)">
                   <template #icon>
                     <NIcon><RemoveOutline /></NIcon>
                   </template>
                 </NButton>
-                <span>{{ item.quantity }}</span>
-                <NButton v-if="!hasCurrentOrder" size="tiny" circle type="primary" @click="increaseOrderedDish(item)">
+                <span>{{ hasCurrentOrder ? item.pendingQuantity : item.quantity }}</span>
+                <NButton size="tiny" circle type="primary" @click="increaseOrderedDish(item)">
                   <template #icon>
                     <NIcon><AddOutline /></NIcon>
                   </template>
@@ -559,7 +564,7 @@ const displayOrderedList = computed(() => {
 })
 
 const displayOrderTotal = computed(() => {
-  return displayOrderedList.value.reduce((total, item) => total + itemTotal(item), 0)
+  return displayOrderedList.value.reduce((total, item) => total + pendingItemTotal(item), 0)
 })
 
 const submitDetailList = computed(() => {
@@ -591,6 +596,7 @@ function initPage() {
         return sortA - sortB
       })
       orderedList.value = sorted.map(item => toOrderedItem(item, item.quantity || 0, 0))
+      loadOrderImages()
       viewMode.value = 'order'
     }
     else {
@@ -631,6 +637,7 @@ function loadCurrentOrder(roomId) {
         return sortA - sortB
       })
       orderedList.value = sorted.map(item => toOrderedItem(item, item.quantity || 0, 0))
+      loadOrderImages()
       refreshDishSort()
     }
     orderLoading.value = false
@@ -715,6 +722,21 @@ function getPreviewUrl(url) {
   return resolveRenderableFileUrl(url).catch(() => url)
 }
 
+// 加载订单菜品图片
+function loadOrderImages() {
+  orderedList.value.forEach((row) => {
+    const urls = getImageUrls(row.image)
+    if (urls.length === 0) {
+      row.previewImageUrl = ''
+      return
+    }
+    row.previewImageUrl = urls[0]
+    Promise.all(urls.map(url => getPreviewUrl(url))).then((list) => {
+      row.previewImageUrl = list[0]
+    })
+  })
+}
+
 // ==================== 点菜操作 ====================
 function addDish(dish) {
   const row = orderedList.value.find(item => item.dishId === dish.dishId)
@@ -733,6 +755,9 @@ function increaseOrderedDish(item) {
 }
 
 function reduceDish(item) {
+  // 加菜模式下防止 pendingQuantity 减到负数
+  if (hasCurrentOrder.value && item.pendingQuantity <= 0)
+    return
   if (item.quantity <= 0)
     return
   item.quantity -= 1
@@ -762,6 +787,8 @@ function toOrderedItem(item, quantity, pendingQuantity) {
     baseQuantity: quantity - pendingQuantity,
     pendingQuantity,
     dishSort: item.dishSort || 0,
+    image: item.image,
+    previewImageUrl: '',
   }
 }
 
@@ -775,6 +802,12 @@ function itemTotal(item) {
   return Number(item.salePrice || 0) * Number(item.quantity || 0)
 }
 
+// 加菜模式下按 pendingQuantity 计算单项总价(只算本次新增部分)
+function pendingItemTotal(item) {
+  const qty = hasCurrentOrder.value ? item.pendingQuantity : item.quantity
+  return Number(item.salePrice || 0) * Number(qty || 0)
+}
+
 function formatMoney(value) {
   return Number(value || 0).toFixed(2)
 }
@@ -796,6 +829,7 @@ function goToOrderPage() {
         return sortA - sortB
       })
       orderedList.value = sorted.map(item => toOrderedItem(item, item.quantity || 0, 0))
+      loadOrderImages()
       viewMode.value = 'order'
     }
     else {

+ 3 - 0
forge-server/forge-business/forge-restaurant/src/main/java/com/mdframe/forge/business/core/restaurant/vo/DishOrderItemVO.java

@@ -48,4 +48,7 @@ public class DishOrderItemVO {
 
     /** 退菜原因 */
     private String reason;
+
+    /** 菜品图片(文件ID) */
+    private String image;
 }

+ 4 - 2
forge-server/forge-business/forge-restaurant/src/main/resources/mapper/business/restaurant/DishOrderDetailMapper.xml

@@ -9,12 +9,14 @@
                SUM(d.quantity) AS quantity,
                SUM(d.amount) AS amount,
                MIN(d.dish_sort) AS dish_sort,
-               d.is_add_dish
+               d.is_add_dish,
+               lib.image
         FROM restaurant_dish_order_detail d
+        LEFT JOIN restaurant_dish_library lib ON lib.id = d.dish_id AND lib.del_flag = 0
         WHERE d.order_id = #{orderId}
           AND d.del_flag = 0
         GROUP BY d.dish_id, d.dish_code, d.dish_name, d.type_id, d.type_name,
-                 d.unit, d.spec, d.sale_price, d.is_add_dish
+                 d.unit, d.spec, d.sale_price, d.is_add_dish, lib.image
         HAVING SUM(d.quantity) > 0
         ORDER BY MIN(d.dish_sort) ASC
     </select>

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 40 - 29
forge-server/forge-business/forge-restaurant/餐饮模块迁移分析文档.md