ソースを参照

Merge branch 'feature_点餐优化' of elab-damai-h5/h5-dm-orderFront into master

tangy 1 ヶ月 前
コミット
12b7c6c37c

+ 4 - 0
src/api/order.ts

@@ -47,3 +47,7 @@ export function orderCheckout(params?: any) {
 export function deskOrderedDishListAll(params?: any) {
     return request.get({ url: '/orders/orderDishes', params }) //已点商品
 }
+
+export function searchCoupons(params?: any) {
+    return request.get({ url: '/orders/queryUserCouponList', params }) //已点商品
+}

+ 13 - 3
src/components/upload/index.vue

@@ -7,7 +7,7 @@
             :limit="limit"
             :show-file-list="false"
             :headers="headers"
-            :data="data"
+            :data="uploadData"
             :on-progress="handleProgress"
             :on-success="handleSuccess"
             :on-exceed="handleExceed"
@@ -98,7 +98,16 @@ export default defineComponent({
         const visible = ref(false)
         const fileList = ref<any[]>([])
 
+        // 定义响应式的上传参数
+        const uploadData = ref(props.data)
+
         const beforeUpload: UploadProps['beforeUpload'] = (rawFile: UploadRawFile) => {
+            // 更新上传参数
+            uploadData.value = {
+                ...props.data,
+                fileName: rawFile.name
+            }
+            
             let q = 0.5
             if (rawFile.size > 1000000 && rawFile.size < 5000000) {
                 q = 0.5
@@ -155,7 +164,7 @@ export default defineComponent({
         const getAccept = computed(() => {
             switch (props.type) {
                 case 'image':
-                    return '.jpj,.png,.gif,.jpeg,.ico,.bmp'
+                    return '.jpg,.png,.gif,.jpeg,.ico,.bmp'
                 case 'video':
                     return '.wmv,.avi,.mov,.mp4,.flv,.rmvb'
                 default:
@@ -174,7 +183,8 @@ export default defineComponent({
             handleError,
             handleExceed,
             handleClose,
-            beforeUpload
+            beforeUpload,
+            uploadData
         }
     }
 })

+ 155 - 4
src/views/dish/lists/edit.vue

@@ -20,7 +20,7 @@
                                     placeholder="请输入菜品名称"
                                     type="textarea"
                                     :autosize="{ minRows: 3, maxRows: 3 }"
-                                    maxlength="20"
+                                    maxlength="10"
                                     show-word-limit
                                     clearable
                                 />
@@ -71,6 +71,54 @@
                                 </div>
                             </div>
                         </el-form-item>
+                      <el-form-item label="菜品规格" prop="specs">
+                          <div class="w-full block mb-4">
+                            <el-button type="primary" @click="handleSpecs">+</el-button>
+                          </div>
+                          <div class="flex flex-col gap-4">
+                            <div v-for="(spec, index) in formData.specs" :key="index" class="w-full">
+                                <div class="flex items-center">
+                                    <el-input
+                                        v-model="spec.name"
+                                        placeholder="请输入规格名称"
+                                        class="w-60 mr-2"
+                                    />
+<!--                                    <el-button type="primary" @click="handleSpecs()">添加规格项</el-button>-->
+                                    <el-button type="danger" @click="removeSpec(index)">删除</el-button>
+                                </div>
+
+                                <div class="mt-2 ml-4">
+                                    <el-tag
+                                        v-for="(tag, idx) in spec.items"
+                                        :key="idx"
+                                        class="mx-1 mb-2"
+                                        closable
+                                        :disable-transitions="false"
+                                        @close="handleClose(index, idx)"
+                                    >
+                                        {{ tag.tag }}
+                                    </el-tag>
+                                    <el-input
+                                        v-if="spec.inputVisible"
+                                        ref="InputRef"
+                                        v-model="spec.inputValue"
+                                        class="w-20 ml-1"
+                                        size="small"
+                                        @keyup.enter="handleInputConfirm(index)"
+                                        @blur="handleInputConfirm(index)"
+                                    />
+                                    <el-button
+                                        v-else
+                                        class="button-new-tag ml-1"
+                                        size="small"
+                                        @click="showInput(index)"
+                                    >
+                                        + 添加
+                                    </el-button>
+                                </div>
+                            </div>
+                        </div>
+                      </el-form-item>
                         <el-form-item label="排序" prop="sort">
                             <div>
                                 <el-input-number v-model="formData.sort" :min="0" :max="9999" />
@@ -113,7 +161,8 @@ const formData = reactive<any>({
     visit: 0,
     sort: 0,
     isShow: 1,
-    summary: 0
+    summary: 0,
+    specs: [] // 规格
 })
 
 const { removeTab } = useMultipleTabs()
@@ -125,6 +174,7 @@ const rules = reactive({
     image: [{ required: true, message: '添加菜品图片', trigger: 'change' }]
 })
 
+// getDetails 函数修改
 const getDetails = async () => {
     const data = await articleDetail({
         id: route.query.id
@@ -136,6 +186,26 @@ const getDetails = async () => {
             formData[key] = Number(formData[key])
         }
     })
+
+    // 转换规格数据
+    if (data.specsList && data.specsList.length > 0) {
+        const specsMap = new Map()
+        data.specsList.forEach((spec: { name: string; value: string; id: number }) => {
+            if (!specsMap.has(spec.name)) {
+                specsMap.set(spec.name, {
+                    name: spec.name,
+                    items: [],
+                    inputVisible: false,
+                    inputValue: ''
+                })
+            }
+            specsMap.get(spec.name).items.push({
+                id: spec.id,
+                tag: spec.value
+            })
+        })
+        formData.specs = Array.from(specsMap.values())
+    }
 }
 
 const { optionsData } = useDictOptions<{
@@ -148,10 +218,40 @@ const { optionsData } = useDictOptions<{
 
 const handleSave = async () => {
     await formRef.value?.validate()
+    if (formData.specs.length >= 5) {
+        feedback.msgWarning('最多添加5个规格')
+        return
+    }
+    const specsList: Array<{
+        name: string;
+        value: string;
+        status: number;
+        articleId: number | null;
+        id: number | null;
+        img: string | null;
+    }> = [];
+    formData.specs.forEach((spec: { name: string; items: Array<{id: number|null, tag: string}> }) => {
+        spec.items.forEach(item => {
+            specsList.push({
+                name: spec.name,
+                value: item.tag,
+                status: 1,
+                articleId: formData.id || null,
+                id: item.id || null,
+                img: null
+            })
+        })
+    })
+
+    const submitData = {
+        ...formData,
+        specsList
+    }
+
     if (route.query.id) {
-        await articleEdit(formData)
+        await articleEdit(submitData)
     } else {
-        await articleAdd(formData)
+        await articleAdd(submitData)
     }
     feedback.msgSuccess('操作成功')
     removeTab()
@@ -159,4 +259,55 @@ const handleSave = async () => {
 }
 
 route.query.id && getDetails()
+
+// 添加规格
+const handleSpecs = () => {
+    if (!formData.specs) {
+        formData.specs = []
+    }
+    formData.specs.push({
+        name: '',
+        items: [],
+        inputVisible: false,
+        inputValue: ''
+    })
+}
+
+// 删除规格
+const removeSpec = (index: number) => {
+    formData.specs.splice(index, 1)
+}
+
+// 显示输入框
+const showInput = (specIndex: number) => {
+    formData.specs[specIndex].inputVisible = true
+    nextTick(() => {
+        InputRef.value?.input?.focus()
+    })
+}
+
+// 确认输入
+const handleInputConfirm = (specIndex: number) => {
+    const spec = formData.specs[specIndex]
+    if (spec.inputValue) {
+        spec.items.push({
+            id: null,
+            tag: spec.inputValue
+        })
+    }
+    spec.inputVisible = false
+    spec.inputValue = ''
+}
+
+// 删除标签
+const handleClose = (specIndex: number, tagIndex: number) => {
+    formData.specs[specIndex].items.splice(tagIndex, 1)
+}
+
+// 添加规格项
+const handleAddSpecItem = (index: number) => {
+    showInput(index)
+}
+
+const InputRef = ref()
 </script>

+ 519 - 99
src/views/order/console.vue

@@ -14,7 +14,12 @@
                             class="justify-between items-center flex text-xl font-semibold font-mono"
                         >
                             <span>{{ '桌台:' + data.deskName }}</span>
-                            <span>{{ data.currentNum + '/' + data.deskCap }}</span>
+                            <div class="flex items-center">
+                                <span>{{ data.currentNum + '/' + data.deskCap }}</span>
+                                <el-icon class="ml-2 cursor-pointer" @click="refreshdishes">
+                                    <Refresh />
+                                </el-icon>
+                            </div>
                         </div>
                     </template>
                     <el-scrollbar ref="scrollbarRef" class="scrollbar">
@@ -42,21 +47,93 @@
                         show-word-limit
                         type="textarea"
                     />
-                    <el-divider />
+                    <div class="flex justify-between items-center mt-2">
+                        <div
+                            class="flex-1 h-12 border rounded-md flex items-center justify-center cursor-pointer relative mr-2"
+                            :class="{'border-primary text-primary bg-[#EDEFFF]': params.diningMethods === 1}"
+                            @click="params.diningMethods = 1"
+                        >
+                            <span>店内就餐</span>
+                            <el-icon v-if="params.diningMethods === 1" class="absolute right-0 bottom-0 text-primary" style="font-size: 16px;">
+                                <Check />
+                            </el-icon>
+                        </div>
+                        <div
+                            class="flex-1 h-12 border rounded-md flex items-center justify-center cursor-pointer relative ml-2"
+                            :class="{'border-primary text-primary bg-[#EDEFFF]': params.diningMethods === 2}"
+                            @click="params.diningMethods = 2"
+                        >
+                            <span>打包外带</span>
+                            <el-icon v-if="params.diningMethods === 2" class="absolute right-0 bottom-0 text-primary" style="font-size: 16px;">
+                                <Check />
+                            </el-icon>
+                        </div>
+                    </div>
+                    <div class="mb-4" style="margin-top: 5px;">
+                        <div class="flex items-center">
+                            <el-input
+                                v-model="data.phone"
+                                placeholder="请输入手机号查询优惠券"
+                                class="flex-1"
+                                maxlength="11"
+                            >
+                                <template #append>
+                                    <el-button :icon="Search" @click="searchUserCoupons" :loading="searchLoading"/>
+                                </template>
+                            </el-input>
+                        </div>
+
+                        <div class="mt-2 coupon-container" v-loading="searchLoading">
+                            <el-scrollbar height="200px">
+                                <el-empty v-if="!data.couponList.length" description="暂无可用优惠券" :image-size="60" />
+                                <div v-else class="coupon-list" style="padding: 7px;">
+                                    <div
+                                        v-for="coupon in data.couponList"
+                                        :key="coupon.id"
+                                        class="coupon-item"
+                                        :class="{
+                                            'is-selected': params.couponId === coupon.ticketRecordId,
+                                            'is-disabled': orderData.sumPriceSum < coupon.consumeScore / 100
+                                        }"
+                                        @click="handleCouponClick(coupon)" @dblclick="handleCouponDoubleClick(coupon)"
+                                    >
+                                        <div class="left-part">
+                                            <div class="amount-wrapper">
+                                                <div class="amount">{{ coupon.consumeScore / 100 }}</div>
+                                            </div>
+                                            <!-- <div class="condition">满{{ coupon.consumeScore / 100 }}可用</div> -->
+                                        </div>
+                                        <div class="right-part">
+                                            <div class="name">{{ coupon.productName }}</div>
+                                            <div class="date">券码:{{ coupon.ticketNo}}</div>
+                                        </div>
+                                    </div>
+                                </div>
+                            </el-scrollbar>
+                        </div>
+                    </div>
                     <el-alert :closable="false" style="padding: 8px 0px;">
                         <template #title>
                             <div class="justify-between flex w-full">
                                 <div class="text-xl" style="font-size: 14px;">
-                                    出单价格:<b
-                                        style="color: sandybrown"
-                                        v-text="orderData.priceSum.toFixed(2)"
-                                    ></b>
+                                    出单价格:<b style="color: sandybrown" v-text="orderData.priceSum.toFixed(2)"></b>
+                                </div>
+                                <div class="text-xl" style="font-size: 14px;">
+                                    总价格:<b style="color: sandybrown" v-text="orderData.sumPriceSum.toFixed(2)"></b>
+                                </div>
+                            </div>
+                            <div class="flex" style="justify-content: space-between;">
+                                <div class="text-xl" style="font-size: 14px;">
+                                    优惠抵扣:
+                                    <template v-if="params.couponAmount != '-'">
+                                        <b style="color: red" v-text="'-' + params.couponAmount"></b>
+                                    </template>
+                                    <template v-else>
+                                        <b style="color: red" v-text="'-'"></b>
+                                    </template>
                                 </div>
-                                <div class="text-xl">
-                                    总价格:<b
-                                        style="color: sandybrown"
-                                        v-text="orderData.sumPriceSum.toFixed(2)"
-                                    ></b>
+                                <div class="text-xl" style="font-size: 14px;">
+                                    需支付:<b style="color: #67c23a" v-text="orderData.payAmount"></b>
                                 </div>
                             </div>
                         </template>
@@ -65,12 +142,12 @@
                                 <div class="text-xl" style="font-size: 14px;">
                                     出单数量:<b v-text="orderData.numSum.toString()"></b>
                                 </div>
-                                <div class="text-xl">
+                                <div class="text-xl" style="font-size: 14px;">
                                     总数量:<b v-text="orderData.sumNumSum.toString()"></b>
                                 </div>
-                            </div> 
-                        </template
-                    ></el-alert>
+                            </div>
+                        </template>
+                    </el-alert>
                     <div class="pt-2 justify-between flex">
                         <el-button @click="toEmpty()">清空未出单菜品</el-button
                         ><span>
@@ -145,6 +222,39 @@
             </div>
         </el-drawer>
     </div>
+
+    <el-dialog
+        v-model="specDialogVisible"
+        title="选择规格"
+        width="30%"
+        :before-close="handleSpecDialogClose"
+    >
+        <div v-if="currentItem">
+        <div v-for="(specs, name) in groupSpecsByName(currentItem.specsList)" :key="name" class="mb-4">
+            <div class="font-bold mb-2">{{ name }}</div>
+            <div class="flex flex-wrap gap-2">
+                <div
+                    v-for="spec in specs"
+                    :key="spec.id"
+                    class="flex-1 h-12 border rounded-md flex items-center justify-center cursor-pointer relative"
+                    :class="{'border-primary text-primary bg-[#EDEFFF]': selectedSpecs[name] === spec.id}"
+                    @click="selectedSpecs[name] = spec.id"
+                >
+                    <span>{{ spec.value }}</span>
+                    <el-icon v-if="selectedSpecs[name] === spec.id" class="absolute right-0 bottom-0 text-primary" style="font-size: 16px;">
+                        <Check />
+                    </el-icon>
+                </div>
+            </div>
+        </div>
+        </div>
+        <template #footer>
+        <span class="dialog-footer">
+            <el-button @click="specDialogVisible = false">取消</el-button>
+            <el-button type="primary" @click="handleSpecConfirm">确认</el-button>
+        </span>
+        </template>
+    </el-dialog>
 </template>
 
 <script setup lang="ts">
@@ -160,7 +270,8 @@ import {
     dishListAll,
     toEmptyy,
     orderSubmit,
-    deskOrderedDishListAll
+    deskOrderedDishListAll,
+    searchCoupons
 } from '@/api/order'
 import feedback from '@/utils/feedback'
 // const route = useRoute()
@@ -168,7 +279,7 @@ const innerRef = ref<HTMLDivElement>()
 const dishCate = ref<any[]>([])
 const dishList = ref<any[]>([])
 let dishListall: any[] = []
-var orderLoading = ref(false)
+const orderLoading = ref(false);
 const scrollbarRef = ref<InstanceType<typeof ElScrollbar>>()
 const search = ref('')
 const emit = defineEmits(['init'])
@@ -180,17 +291,24 @@ const data = reactive<any>({
     currentNum: 0,
     selectGoods: [],
     oldGoods: [],
+    phone: '', // 手机号
+    couponList: [], // 优惠券列表
 })
 const orderData = reactive<any>({
     priceSum: 0,
     sumPriceSum: 0,
     numSum: 0,
     sumNumSum: 0,
-    
+    payAmount: 0, // 新增支付金额
+
 })
 const params = reactive({
     remark: '', //订单备注
-    number: '' //订单id
+    number: '', //订单id
+    userId: '', //用户id
+    couponId: '', //优惠券id
+    couponAmount: '-',
+    diningMethods: 1, // 就餐方式:1店内就餐 2打包外带
 })
 const submit = () => {
     //出单
@@ -198,8 +316,16 @@ const submit = () => {
         feedback.alert('未点菜品!')
         return false;
     }
+    if (orderData.payAmount <=0) {
+      feedback.alert('支付金额需大于0!')
+        return false;
+    }
     feedback.loading('正在出单...')
-    orderSubmit(params).then(() => {
+    orderSubmit({
+        ...params,
+        userId: params.userId || null,
+        couponId: params.couponId || null
+    }).then(() => {
         // data.selectGoods.length = 0
         // orderData.priceSum = 0
         // orderData.numSum = 0
@@ -211,36 +337,105 @@ const submit = () => {
         feedback.closeLoading()
         feedback.notifySuccess('出单成功')
         showOrderConsole.value = false;
-        
+
     })
 }
+
+// 添加优惠券点击处理方法
+const handleCouponClick = (coupon: any) => {
+    if (orderData.sumPriceSum < coupon.consumeScore / 100) {
+        return
+    }
+    params.couponId = coupon.ticketRecordId
+    params.couponAmount = (coupon.consumeScore / 100).toFixed(2)
+    // 计算支付金额
+orderData.payAmount = (Number(orderData.sumPriceSum) - Number(coupon.consumeScore / 100)).toFixed(2)
+}
+
+// 添加双击处理方法
+const handleCouponDoubleClick = (coupon: any) => {
+    if (params.couponId === coupon.ticketRecordId) {
+        params.couponId = ''
+        params.couponAmount = '-'
+        orderData.payAmount = orderData.sumPriceSum
+    }
+}
+
 const open = (item: any, num?: number, orderNumber?: any) => {
+    data.couponList = []
     showOrderConsole.value = true
     data.deskName = item.name
     data.deskCap = item.num
     data.currentNum = item.userNum ? item.userNum : num
     params.number = item.ordersId ? item.ordersId : orderNumber
+    params.userId = '';
+    params.couponId = '';
     //查询当前订单下的所有菜品
     deskOrderedDishListAll({ id: params.number }).then((res) => {
+        res.forEach((good: { id:number, ordersDishId: number }) => {
+            good.ordersDishId = good.id
+        })
+        console.log("***deskOrderedDishListAll***",res)
         data.selectGoods = res
         data.oldGoods = JSON.parse(JSON.stringify(res));//记录下进入时当前餐桌已有的食物
         if (res.length > 0) {
+            data.phone = res[0].mobile;
             params.remark = res[0].remark;
+            params.userId = res[0].userId || null;
+            params.couponId = res[0].ticketId || null;
+            params.diningMethods = res[0].diningMethods || 1;
+            if (params.couponId && params.userId) {
+                searchUserCoupons();
+            }
+            params.couponAmount = res[0].ticketAmount? Number(res[0].ticketAmount / 100).toFixed(2) : '-'
             let _list = res.filter((it: { status: number })=>it.status!=1);//找到没有出单的餐品来计算价格
-            orderData.priceSum = _list.reduce((accumulator, currentValue) => accumulator + (currentValue.summary * currentValue.num), 0);
-            orderData.numSum = _list.reduce((accumulator, currentValue) => accumulator + currentValue.num, 0);
+            orderData.priceSum = _list.reduce((accumulator: number, currentValue: { summary: number; num: number }) => accumulator + (currentValue.summary * currentValue.num), 0);
+            orderData.numSum = _list.reduce((accumulator: number, currentValue: { summary: number; num: number }) => accumulator + currentValue.num, 0);
             //所有餐品的总价以及总数量
-            orderData.sumPriceSum = res.reduce((accumulator, currentValue) => accumulator + (currentValue.summary * currentValue.num), 0);
-            orderData.sumNumSum = res.reduce((accumulator, currentValue) => accumulator + currentValue.num, 0);
+            orderData.sumPriceSum = res.reduce((accumulator: number, currentValue: { summary: number; num: number }) => accumulator + (currentValue.summary * currentValue.num), 0);
+            orderData.sumNumSum = res.reduce((accumulator: number, currentValue: { summary: number; num: number }) => accumulator + currentValue.num, 0);
+            orderData.payAmount = res[0].payAmount ? Number((res[0].payAmount / 100).toFixed(2)) : Number(orderData.sumPriceSum).toFixed(2);
         }else{
             orderData.priceSum = 0
             orderData.numSum = 0
             orderData.sumPriceSum = 0
             orderData.sumNumSum = 0
+            data.phone = ''
+            params.remark = ''
+            params.userId = ''
+            params.couponId = ''
+            params.couponAmount = '-'
+            orderData.payAmount = 0
+            data.couponList = []
         }
     })
     dishList.value = dishListall;//显示所有菜品
 }
+
+const refreshdishes = () => {
+    deskOrderedDishListAll({ id: params.number }).then((res) => {
+        res.forEach((good: { id:number, ordersDishId: number }) => {
+            good.ordersDishId = good.id
+        })
+        console.log("***refreshdishes***",res)
+        data.selectGoods = res
+        if (res.length > 0) {
+            let _list = res.filter((it: { status: number })=>it.status!=1);//找到没有出单的餐品来计算价格
+            orderData.priceSum = _list.reduce((accumulator: number, currentValue: { summary: number; num: number }) => accumulator + (currentValue.summary * currentValue.num), 0);
+            orderData.numSum = _list.reduce((accumulator: number, currentValue: { summary: number; num: number }) => accumulator + currentValue.num, 0);
+            //所有餐品的总价以及总数量
+            orderData.sumPriceSum = res.reduce((accumulator: number, currentValue: { summary: number; num: number }) => accumulator + (currentValue.summary * currentValue.num), 0);
+            orderData.sumNumSum = res.reduce((accumulator: number, currentValue: { summary: number; num: number }) => accumulator + currentValue.num, 0);
+            orderData.payAmount = res[0].payAmount ? Number((res[0].payAmount / 100).toFixed(2)) : Number(orderData.sumPriceSum).toFixed(2);
+        }else{
+            orderData.priceSum = 0
+            orderData.numSum = 0
+            orderData.sumPriceSum = 0
+            orderData.sumNumSum = 0
+            orderData.payAmount = 0
+        }
+    })
+}
 watch(search, (value) => {
     if (!value) {
         if(buttonType.value == "all"){
@@ -276,11 +471,11 @@ const clickSortButton = (name: any) => {
 const toEmpty = () => {
     if (data.selectGoods.length != 0) {
         //清空未出单的餐品-保留出单餐品
-        data.selectGoods = data.selectGoods.filter((good) => {
+        data.selectGoods = data.selectGoods.filter((good: { status: number }) => {
             return good.status == 1
         })
-        orderData.sumPriceSum = data.selectGoods.reduce((accumulator, currentValue) => accumulator + (currentValue.summary * currentValue.num), 0);
-        orderData.sumNumSum = data.selectGoods.reduce((accumulator, currentValue) => accumulator + currentValue.num, 0);
+        orderData.sumPriceSum = data.selectGoods.reduce((accumulator: number, currentValue: { summary: number; num: number }) => accumulator + (currentValue.summary * currentValue.num), 0);
+        orderData.sumNumSum = data.selectGoods.reduce((accumulator: number, currentValue: { summary: number; num: number }) => accumulator + currentValue.num, 0);
         // data.selectGoods.length = 0
         orderData.priceSum = 0
         orderData.numSum = 0
@@ -333,67 +528,137 @@ const beforeClose = () => {
     emit('init')
 }
 const add = (item: any) => {
-    addGoods(item)
-}
-const addGoods = (item: any) => {
-    console.warn("***addGoods-添加的菜品信息***",item)
-    if(orderLoading.value){
-        return false;
-    }
-    orderLoading.value = true
-    //查找未出单的餐品列表
-    const list = data.selectGoods.filter((element: any) => {return element.status != 1})
-    var tempGoods: string | any[] = [];
-    if(item.artId){//说明操作的是历史已经添加过的餐品
-        tempGoods = [item];
-    }else{
-        //先从历史下单但是没有结单的餐品中找出要操作的餐品
-        tempGoods = list.filter((element: any) => {
-            return element.artId == item.id && element.artId
+    // addGoods(item)
+    dishInc({ id: item.id}).then(()=>{
+        data.selectGoods[data.selectGoods.indexOf(item)].num++
+        addHandle(item);
+    }).finally(()=>{
+        orderLoading.value = false;
+        nextTick(() => {
+            scrollbarRef.value!.setScrollTop(innerRef.value!.clientHeight)
         })
-        if(tempGoods.length==0){//历史没有,则从当次下单的餐品列表中找
-            tempGoods = list.filter((element: any) => {
-                return element.id == item.id && !element.artId
-            })
-        }
+    })
+}
+// 添加新的响应式变量
+const specDialogVisible = ref(false)
+const currentItem = ref<any>(null)
+const selectedSpecs = ref<any>({})
+
+// 添加新的方法
+const groupSpecsByName = (specsList: any[]) => {
+  const groups: any = {}
+  specsList?.forEach(spec => {
+    if (!groups[spec.name]) {
+      groups[spec.name] = []
     }
-    if (tempGoods.length == 0) {
-        item.num = 1
-        dishAdd({
-            orderId: params.number,
-            dishId: item.id
-        }).then((res) => {
-            //拿到orders_dish的id
-            item.ordersDishId = res
-            data.selectGoods.push(item)
-            feedback.msgSuccess('成功添加商品' + item.title)
-            addHandle(item)
-        }).finally(()=>{
-            orderLoading.value = false;
-            nextTick(() => {
-                scrollbarRef.value!.setScrollTop(innerRef.value!.clientHeight)
-            })
-        })
-    } else {
-        dishInc({ id: item.ordersDishId || tempGoods[0].id }).then(()=>{
-            data.selectGoods[data.selectGoods.indexOf(tempGoods[0])].num++
-            addHandle(item);
-        }).finally(()=>{
-            orderLoading.value = false;
-            nextTick(() => {
-                scrollbarRef.value!.setScrollTop(innerRef.value!.clientHeight)
-            })
-        })
+    groups[spec.name].push(spec)
+  })
+  return groups
+}
+
+const handleSpecDialogClose = () => {
+  specDialogVisible.value = false
+  currentItem.value = null
+  selectedSpecs.value = {}
+}
+
+const handleSpecConfirm = () => {
+  // 检查是否所有规格都已选择
+  const allSpecsSelected = Object.keys(groupSpecsByName(currentItem.value.specsList)).every(
+    name => selectedSpecs.value[name]
+  )
+
+  if (!allSpecsSelected) {
+    feedback.msgError('请选择所有规格')
+    return
+  }
+
+  // 将选中的规格ID添加到商品中
+  const item = { ...currentItem.value, selectedSpecIds: Object.values(selectedSpecs.value) }
+  handleAddGoods(item)
+  handleSpecDialogClose()
+}
+
+// 修改 addGoods 方法
+const addGoods = (item: any) => {
+    if (item.specsList?.length > 0) {
+        currentItem.value = item
+        selectedSpecs.value = {}
+        specDialogVisible.value = true
+        return
     }
-    
+    handleAddGoods(item)
+}
+
+// 将原来的 addGoods 逻辑移到这个新方法中
+const handleAddGoods = (item: any) => {
+  if(orderLoading.value){
+    return false
+  }
+  console.log('handleAddGoods', item)
+  orderLoading.value = true
+  //查找未出单的餐品列表
+  const list = data.selectGoods.filter((element: any) => {return element.status != 1})
+  console.log('list', list)
+  var tempGoods: string | any[] = [];
+  //先从历史下单但是没有结单的餐品中找出要操作的餐品
+  tempGoods = list.filter((element: any) => {
+      //如果有specsIds,说明是有规格的餐品
+      let specsIds: number[] = [];
+      element.specsList?.forEach((spec: any) => {
+        specsIds.push(spec.specsId)
+      })
+      return element.artId && element.artId == item.id &&
+        specsIds.sort((a: number, b: number) => a - b).join(',') == (item.selectedSpecIds || []).sort((a: number, b: number) => a - b).join(',')
+  })
+  console.log('tempGoods', tempGoods)
+  if (tempGoods.length == 0) {
+      item.num = 1
+      dishAdd({
+          orderId: params.number,
+          dishId: item.id,
+          specsIds: (item.selectedSpecIds || []).join(',') // 将数组转换为逗号分隔的字符串
+      }).then((res) => {
+          //拿到orders_dish的id
+          item.ordersDishId = res
+          item.specsList = item.selectedSpecIds?.map((specId: any) => {
+              const o = item.specsList.find((spec: any) => spec.id == specId)
+              o.specsId = specId
+              return o;
+          })
+          item.artId = item.id
+          data.selectGoods.push(item)
+          console.log("data.selectGoods",data.selectGoods)
+          feedback.msgSuccess('成功添加商品' + item.title)
+          addHandle(item)
+      }).finally(()=>{
+          orderLoading.value = false;
+          nextTick(() => {
+              scrollbarRef.value!.setScrollTop(innerRef.value!.clientHeight)
+          })
+      })
+  } else {
+      dishInc({ id: tempGoods[0].ordersDishId }).then(()=>{
+          data.selectGoods[data.selectGoods.indexOf(tempGoods[0])].num++
+          addHandle(item);
+      }).finally(()=>{
+          orderLoading.value = false;
+          nextTick(() => {
+              scrollbarRef.value!.setScrollTop(innerRef.value!.clientHeight)
+          })
+      })
+  }
+
 }
 //只有正常执行了接口才触发
 const addHandle = (item: { summary: any })=>{
     console.warn("***addGoods-dish***",item,data.selectGoods)
+    console.warn("***orderData***",orderData)
     orderData.numSum++
     orderData.sumNumSum++
     orderData.priceSum += Number(item.summary)
     orderData.sumPriceSum += Number(item.summary)
+    orderData.payAmount = Number(orderData.sumPriceSum - (params.couponAmount === '-' ? 0 : Number(params.couponAmount))).toFixed(2)
 }
 const reduce = (item: any) => {
     if(orderLoading.value){
@@ -401,32 +666,37 @@ const reduce = (item: any) => {
     }
     orderLoading.value = true
     //查找未出单的餐品列表
-    const list = data.selectGoods.filter((element: any) => {return element.status != 1})
-    var tempGoods: string | any[] = [];
-    if(item.artId){//说明操作的是历史已经添加过的餐品
-        tempGoods = [item];
-    }else{
-        //先从历史下单但是没有结单的餐品中找出要操作的餐品
-        tempGoods = list.filter((element: any) => {
-            return element.artId == item.id && element.artId
-        })
-        if(tempGoods.length==0){//历史没有,则从当次下单的餐品列表中找
-            tempGoods = list.filter((element: any) => {
-                return element.id == item.id && !element.artId
-            })
-        }
-    }
-    const t = data.selectGoods.indexOf(tempGoods[0])
-    const n = data.selectGoods[t].num
-    if (n > 1) {
-        dishDec({ id: item.ordersDishId || tempGoods[0].id}).then(() => {
+    // const list = data.selectGoods.filter((element: any) => {return element.status != 1})
+    // let tempGoods: string | any[] = []
+    // //先从历史下单但是没有结单的餐品中找出要操作的餐品
+    // tempGoods = list.filter((element: any) => {
+    //   const specsIds: number[] = [];
+    //   element.specsList?.forEach((spec: any) => {
+    //     specsIds.push(spec.specsId)
+    //   })
+    //   return element.artId && element.artId == item.artId &&
+    //       specsIds.sort((a: number, b: number) => a - b).join(',') == (item.selectedSpecIds || []).sort((a: number, b: number) => a - b).join(',')
+    // })
+    // if(tempGoods.length==0){//历史没有,则从当次下单的餐品列表中找
+    //     tempGoods = list.filter((element: any) => {
+    //         return element.id == item.id && !element.artId
+    //     })
+    // }
+
+    // console.log('tempGoods', tempGoods)
+    console.log('data.selectGoods', data.selectGoods)
+    const t = data.selectGoods.indexOf(item)
+    // const n = data.selectGoods[t].num
+    console.log('reduce', t, item)
+    if (item.num > 1) {
+        dishDec({ id: item.ordersDishId}).then(() => {
             data.selectGoods[t].num--
             reduceHandle(item)
         }).finally(()=>{
             orderLoading.value = false;
         })
     } else {//餐品数量为0,要删减该餐品
-        dishDel({ id: item.ordersDishId || tempGoods[0].id}).then(() => {
+        dishDel({ id: item.ordersDishId}).then(() => {
             data.selectGoods.splice(t, 1)
             reduceHandle(item)
         }).finally(()=>{
@@ -441,6 +711,7 @@ const reduceHandle = (item: { summary: number })=>{
     orderData.sumNumSum--
     orderData.priceSum -= item.summary
     orderData.sumPriceSum -= item.summary
+    orderData.payAmount = Number((orderData.sumPriceSum - (params.couponAmount === '-' ? 0 : Number(params.couponAmount))).toFixed(2))
     if(orderData.priceSum <= 0){
         orderData.priceSum = 0;
     }
@@ -453,6 +724,31 @@ const reduceHandle = (item: { summary: number })=>{
     if(orderData.sumNumSum <= 0){
         orderData.sumNumSum = 0;
     }
+    if(orderData.payAmount <= 0){
+        orderData.payAmount = 0;
+    }
+}
+// 添加搜索用户优惠券的方法
+// ... 其他代码保持不变 ...
+
+// 添加 loading 变量
+const searchLoading = ref(false)
+
+// 修改搜索方法
+const searchUserCoupons = () => {
+    if (!data.phone) {
+        feedback.msgError('请输入手机号')
+        return
+    }
+    searchLoading.value = true
+    searchCoupons({ mobile: data.phone}).then((res: any) => {
+        if (res) {
+            data.couponList = res ? res.couponList || [] : []
+            params.userId = res ? res.userId || null : null
+        }
+    }).finally(() => {
+        searchLoading.value = false
+    })
 }
 
 onMounted(() => {
@@ -473,6 +769,130 @@ defineExpose({
         width: 100%;
     }
     .scrollbar{
-        height:calc(100vh - 54px - 32px - 16px - 61px - 20px - 52px - 49px - 64px - 40px);
+        height:calc(100vh - 54px - 32px - 16px - 61px - 20px - 52px - 49px - 64px - 40px - 260px);
+    }
+</style>
+<style>
+.coupon-card {
+    display: flex;
+    align-items: center;
+    padding: 8px;
+}
+
+.coupon-item {
+    display: flex;
+    margin-bottom: 12px;
+    cursor: pointer;
+    position: relative;
+    background: #fff;
+    border-radius: 4px;
+    transition: all 0.3s;
+    border: 1px solid #E8E8E8;
+
+    &:last-child {
+        margin-bottom: 0;
+    }
+
+    &.is-selected {
+        border-color: coral;
+        .left-part {
+            background: coral;
+        }
+    }
+
+    &.is-disabled {
+        cursor: not-allowed;
+        opacity: 0.6;
+
+        .left-part {
+            background: #999;
+        }
+    }
+}
+
+.left-part {
+    width: 90px;
+    background: coral;
+    color: #fff;
+    padding: 12px 8px;
+    text-align: center;
+    position: relative;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+
+    .amount-wrapper {
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        height: 100%;
+    }
+
+    &::after {
+        content: '';
+        position: absolute;
+        right: 0;
+        top: 0;
+        bottom: 0;
+        width: 4px;
+        background-image: radial-gradient(circle at 0 6px, transparent 3px, #fff 3px);
+        background-size: 4px 12px;
+        background-repeat: repeat-y;
+    }
+
+    .amount {
+        font-size: 24px;
+        font-weight: bold;
+        line-height: 1;
+    }
+
+    .unit {
+        font-size: 12px;
+        margin-top: 2px;
+    }
+
+    .condition {
+        font-size: 12px;
+        margin-top: 6px;
+        opacity: 0.9;
+    }
+}
+
+.right-part {
+    flex: 1;
+    padding: 12px;
+    display: flex;
+    flex-direction: column;
+    justify-content: space-between;
+
+    .name {
+        font-size: 14px;
+        color: #333;
+        font-weight: 500;
     }
+
+    .date {
+        font-size: 12px;
+        color: #999;
+        margin-top: 4px;
+    }
+
+    .check {
+        position: absolute;
+        right: 12px;
+        top: 50%;
+        transform: translateY(-50%);
+        color: #CD8E5F;
+        font-size: 12px;
+    }
+}
+
+.coupon-container {
+    border: 1px solid #EBEEF5;
+    border-radius: 4px;
+
+    :deep(.el-scrollbar__wrap) {
+        padding: 8px;
+    }
+}
 </style>

+ 17 - 6
src/views/order/goodsItem.vue

@@ -2,19 +2,30 @@
     <div class="goods-item">
         <el-card class="w-full" shadow="never" :body-style="{ padding: '10px' }">
             <p class="text-xl font-bold flex" style="justify-content: space-between;">{{ props.goodsItem.title }}
-            <span v-if="props.goodsItem.status==1" class="p-1 rounded ml-2" style="background-color: #e0eee0;font-size: 12px;color:red;height: 20px;padding: 2px;">已出单</span>
+                <span v-if="props.goodsItem.status==1" class="p-1 rounded ml-2" style="background-color: #e0eee0;font-size: 12px;color:red;height: 20px;padding: 2px;">已出单</span>
             </p>
+            <!-- 添加规格展示区域 -->
+            <div v-if="props.goodsItem.specsList && props.goodsItem.specsList.length > 0" class="mb-2">
+                <el-tag
+                    v-for="spec in props.goodsItem.specsList"
+                    :key="spec.id"
+                    size="small"
+                    class="mr-1 mb-1"
+                >
+                    {{ spec.name }}: {{ spec.value }}
+                </el-tag>
+            </div>
             <div>
-                <b style="color: coral">{{ props.goodsItem.summary }}</b
-                ><span class="p-1 rounded ml-2" style="background-color: #e0eee0">{{
+                <b style="color: coral">{{ props.goodsItem.summary }}</b>
+                <span class="p-1 rounded ml-2" style="background-color: #e0eee0">{{
                     '×' + props.goodsItem.num
                 }}</span>
                 <span class="float-right">
-                    <el-button-group size="small" >
+                    <el-button-group size="small">
                         <el-button @click="reduce()" :disabled="props.goodsItem.status==1"> - </el-button>
                         <el-button @click="add()" :disabled="props.goodsItem.status==1"> + </el-button>
-                    </el-button-group></span
-                >
+                    </el-button-group>
+                </span>
             </div>
         </el-card>
     </div>

+ 2 - 1
src/views/order/index.vue

@@ -15,7 +15,7 @@
                 :key="i"
                 class="p-1 flex-wrap float-left text-center md:w-28 h-28 w-1/3"
             >
-            
+
                 <a href="javascript:void(0);" @click="order_(i)"
                     ><div
                         class="rounded w-full h-full pt-3 relative"
@@ -142,6 +142,7 @@ const refresh = () => {
     init()
 }
 const checkout = () => {
+    refresh()
     //结账
     console.warn("***结账***",currentDesk)
     if(currentDesk && !currentDesk.amount){

+ 29 - 13
src/views/orders/list.vue

@@ -48,7 +48,23 @@
                                                 class="dish-image" fit="cover" />
                                         </template>
                                     </el-table-column>
-                                    <el-table-column prop="name" label="菜品名称" />
+                                    <el-table-column prop="name" label="菜品名称">
+                                        <template #default="{ row }">
+                                            <div>
+                                                {{ row.name }}
+                                                <div v-if="row.specsList && row.specsList.length" class="mt-1">
+                                                    <el-tag
+                                                        v-for="spec in row.specsList"
+                                                        :key="spec.id"
+                                                        size="small"
+                                                        class="mr-1 mb-1"
+                                                    >
+                                                        {{ spec.name }}: {{ spec.value }}
+                                                    </el-tag>
+                                                </div>
+                                            </div>
+                                        </template>
+                                    </el-table-column>
                                     <el-table-column prop="amount" label="单价">
                                         <template #default="{ row }">
                                             ¥{{ row.amount }}
@@ -78,8 +94,8 @@
                 <el-table-column label="支付金额" min-width="160" prop="amount">
                     <template #default="{ row }">
                         <div>
-                            <div style="text-decoration: line-through;">订单金额:¥{{row.amount}}</div>
-                            <div style="color: #f01414;">实付金额:¥{{row.payAmount || row.amount}}</div>
+                            <div :style="row.status > 1 ? 'text-decoration: line-through;': ''" v-if="row.status > 0">订单金额:¥{{row.amount}}</div>
+                            <div style="color: #f01414;" v-if="row.status > 1">实付金额:¥{{row.payAmount || row.amount}}</div>
                             <div v-if="row.ticketNo" class="refund-info">
                                 <div style="font-size: 12px;">抵扣券券号:{{ row.ticketNo }}</div>
                                 <div style="font-size: 12px;">抵扣券金额:¥{{ row.ticketAmount }}</div>
@@ -96,11 +112,11 @@
                     </template> -->
                 </el-table-column>
                 <el-table-column label="类型" prop="type"
-                    :formatter="(row) => (row.type == 0 ? '后台点单' : '扫码点单')"></el-table-column>
+                    :formatter="(row: any) => (row.type == 0 ? '后台点单' : '扫码点单')"></el-table-column>
                 <el-table-column label="状态" min-width="160" prop="status" >
                     <template #default="{ row }">
                         <div>
-                            {{ row.status == 4 ? '退款成功' : row.status == 2 ? '已完成' : row.status == 0 ? '待下单' 
+                            {{ row.status == 4 ? '退款成功' : row.status == 2 ? '已完成' : row.status == 0 ? '待下单'
                             : row.status == 1 ? '待支付' : row.status == 5 ? '退款中' : '已取消' }}
                             <div v-if="row.refundStatus=='SUCCESS'" class="refund-info">
                                 <div>退款时间:{{ row.refundTime }}</div>
@@ -122,14 +138,14 @@
                 <pagination v-model="pager" @change="getLists" />
             </div>
         </el-card>
-        <el-dialog 
+        <el-dialog
             v-model="refundDialogVisible"
-            title="退款" 
+            title="退款"
             width="400px"
             @close="refundDialogVisible = false">
             <div class="refund-dialog-content">
-                <el-input 
-                    v-model="refundAmount" 
+                <el-input
+                    v-model="refundAmount"
                     placeholder="请输入退款金额"
                     type="number">
                     <template #prepend><icon :size="25" name="el-icon-money" /></template>
@@ -197,7 +213,7 @@
         refundAmount: number;
         payAmount: number;
         amount: number;
-        status: number; id: any; 
+        status: number; id: any;
     };
     // 修改refund函数
     const refund = async (row: any) => {
@@ -338,7 +354,7 @@
 
     .refund-dialog-content {
         padding: 20px 0px;
-        
+
         :deep(.el-input) {
             width: 100%;
         }
@@ -346,7 +362,7 @@
 
     .dialog-footer {
         text-align: right;
-        
+
         .el-button + .el-button {
             margin-left: 12px;
         }
@@ -368,4 +384,4 @@
         line-height: 1.5;
         max-height: 3em; // 2行的高度
     }
-</style>
+</style>