|
@@ -10,21 +10,27 @@
|
|
|
<view class="card-content">
|
|
<view class="card-content">
|
|
|
<view class="info-item">
|
|
<view class="info-item">
|
|
|
<view class="info-item-label">就餐桌号</view>
|
|
<view class="info-item-label">就餐桌号</view>
|
|
|
- <view class="info-item-value">1234567890</view>
|
|
|
|
|
|
|
+ <view class="info-item-value">{{ orderInfo.tableId || '—' }}</view>
|
|
|
</view>
|
|
</view>
|
|
|
<view class="info-item">
|
|
<view class="info-item">
|
|
|
<view class="info-item-label">用餐人数</view>
|
|
<view class="info-item-label">用餐人数</view>
|
|
|
- <view class="info-item-value">1234567890</view>
|
|
|
|
|
|
|
+ <view class="info-item-value">{{ orderInfo.diners || '—' }}人</view>
|
|
|
</view>
|
|
</view>
|
|
|
<view class="info-item">
|
|
<view class="info-item">
|
|
|
<view class="info-item-label">联系电话</view>
|
|
<view class="info-item-label">联系电话</view>
|
|
|
- <view class="info-item-value">1234567890</view>
|
|
|
|
|
|
|
+ <view class="info-item-value">{{ orderInfo.contactPhone || '—' }}</view>
|
|
|
</view>
|
|
</view>
|
|
|
<view class="info-item">
|
|
<view class="info-item">
|
|
|
<view class="info-item-label">备注信息</view>
|
|
<view class="info-item-label">备注信息</view>
|
|
|
- <view class="info-item-value"></view>
|
|
|
|
|
</view>
|
|
</view>
|
|
|
- <textarea placeholder="请输入特殊需求,如少辣、不要香菜等" class="info-item-textarea"></textarea>
|
|
|
|
|
|
|
+ <textarea
|
|
|
|
|
+ v-model="orderInfo.remark"
|
|
|
|
|
+ placeholder="请输入特殊需求,如少辣、不要香菜等(最多30字)"
|
|
|
|
|
+ class="info-item-textarea"
|
|
|
|
|
+ maxlength="30"
|
|
|
|
|
+ :disabled="fromCheckout"
|
|
|
|
|
+ :readonly="fromCheckout"
|
|
|
|
|
+ ></textarea>
|
|
|
</view>
|
|
</view>
|
|
|
</view>
|
|
</view>
|
|
|
|
|
|
|
@@ -36,13 +42,13 @@
|
|
|
</view>
|
|
</view>
|
|
|
<view class="card-content">
|
|
<view class="card-content">
|
|
|
<view class="info-food">
|
|
<view class="info-food">
|
|
|
- <view v-for="(item, index) in foodList" :key="item.id || index" class="food-item">
|
|
|
|
|
- <!-- 菜品图片 -->
|
|
|
|
|
- <image :src="getFileUrl(item.image)" mode="aspectFill" class="food-item__image"></image>
|
|
|
|
|
|
|
+ <view v-for="(item, index) in foodList" :key="item.id || item.cuisineId || index" class="food-item">
|
|
|
|
|
+ <!-- 菜品图片:兼容 cuisineImage/image,逗号分隔取首图 -->
|
|
|
|
|
+ <image :src="getItemImage(item)" mode="aspectFill" class="food-item__image"></image>
|
|
|
|
|
|
|
|
<!-- 菜品信息 -->
|
|
<!-- 菜品信息 -->
|
|
|
<view class="food-item__info">
|
|
<view class="food-item__info">
|
|
|
- <view class="food-item__name">{{ item.name }}</view>
|
|
|
|
|
|
|
+ <view class="food-item__name">{{ item.name || item.cuisineName }}</view>
|
|
|
<view class="food-item__desc" v-if="item.tags && item.tags.length > 0">
|
|
<view class="food-item__desc" v-if="item.tags && item.tags.length > 0">
|
|
|
<text v-for="(tag, tagIndex) in item.tags" :key="tagIndex" class="food-item__tag">
|
|
<text v-for="(tag, tagIndex) in item.tags" :key="tagIndex" class="food-item__tag">
|
|
|
{{ tag.text }}<text v-if="tagIndex < item.tags.length - 1">,</text>
|
|
{{ tag.text }}<text v-if="tagIndex < item.tags.length - 1">,</text>
|
|
@@ -53,7 +59,7 @@
|
|
|
<!-- 价格和数量 -->
|
|
<!-- 价格和数量 -->
|
|
|
<view class="food-item__right">
|
|
<view class="food-item__right">
|
|
|
<view class="food-item__price">
|
|
<view class="food-item__price">
|
|
|
- <text class="price-main">¥{{ item.price }}</text>
|
|
|
|
|
|
|
+ <text class="price-main">¥{{ formatPrice(getItemPrice(item)) }}</text>
|
|
|
</view>
|
|
</view>
|
|
|
<view class="food-item__quantity">{{ item.quantity || 1 }}份</view>
|
|
<view class="food-item__quantity">{{ item.quantity || 1 }}份</view>
|
|
|
</view>
|
|
</view>
|
|
@@ -72,27 +78,27 @@
|
|
|
<view class="card-content">
|
|
<view class="card-content">
|
|
|
<view class="info-item">
|
|
<view class="info-item">
|
|
|
<view class="info-item-label">菜品总价</view>
|
|
<view class="info-item-label">菜品总价</view>
|
|
|
- <view class="info-item-value">1234567890</view>
|
|
|
|
|
|
|
+ <view class="info-item-value">¥{{ formatPrice(orderInfo.totalAmount) }}</view>
|
|
|
</view>
|
|
</view>
|
|
|
<view class="info-item">
|
|
<view class="info-item">
|
|
|
<view class="info-item-label">餐具费</view>
|
|
<view class="info-item-label">餐具费</view>
|
|
|
- <view class="info-item-value">1234567890</view>
|
|
|
|
|
|
|
+ <view class="info-item-value">¥{{ formatPrice(orderInfo.utensilFee ?? 0) }}</view>
|
|
|
</view>
|
|
</view>
|
|
|
<view class="info-item">
|
|
<view class="info-item">
|
|
|
<view class="info-item-label">优惠券</view>
|
|
<view class="info-item-label">优惠券</view>
|
|
|
- <view class="info-item-value">1234567890</view>
|
|
|
|
|
|
|
+ <view class="info-item-value">¥{{ formatPrice(orderInfo.discountAmount ?? 0) }}</view>
|
|
|
</view>
|
|
</view>
|
|
|
<view class="price-line">
|
|
<view class="price-line">
|
|
|
<view class="price-line-label">应付金额</view>
|
|
<view class="price-line-label">应付金额</view>
|
|
|
- <view class="price-line-value">¥890</view>
|
|
|
|
|
|
|
+ <view class="price-line-value">¥{{ formatPrice(orderInfo.payAmount ?? orderInfo.totalAmount) }}</view>
|
|
|
</view>
|
|
</view>
|
|
|
</view>
|
|
</view>
|
|
|
</view>
|
|
</view>
|
|
|
|
|
|
|
|
- <!-- 底部按钮 -->
|
|
|
|
|
|
|
+ <!-- 底部按钮:带金额(来自去结算)显示「确认支付」且备注不可改;不带金额显示「确认下单」且备注可改 -->
|
|
|
<view class="bottom-button">
|
|
<view class="bottom-button">
|
|
|
- <view class="bottom-button-text" hover-class="hover-active" @click="handleConfirmOrder" v-if="payType === 'confirmOrder'">确认下单</view>
|
|
|
|
|
- <view class="bottom-button-text" hover-class="hover-active" @click="handleConfirmPay" v-if="payType === 'confirmPay'">确认支付 ¥890</view>
|
|
|
|
|
|
|
+ <view class="bottom-button-text" hover-class="hover-active" @click="handleConfirmPay" v-if="fromCheckout">确认支付 ¥{{ formatPrice(orderInfo.payAmount ?? orderInfo.totalAmount) }}</view>
|
|
|
|
|
+ <view class="bottom-button-text" hover-class="hover-active" @click="handleConfirmOrder" v-else>确认下单</view>
|
|
|
</view>
|
|
</view>
|
|
|
|
|
|
|
|
</view>
|
|
</view>
|
|
@@ -103,43 +109,143 @@ import { onLoad } from "@dcloudio/uni-app";
|
|
|
import { ref } from "vue";
|
|
import { ref } from "vue";
|
|
|
import { go } from "@/utils/utils.js";
|
|
import { go } from "@/utils/utils.js";
|
|
|
import { getFileUrl } from "@/utils/file.js";
|
|
import { getFileUrl } from "@/utils/file.js";
|
|
|
|
|
+import { useUserStore } from "@/store/user.js";
|
|
|
|
|
+import { PostOrderCreate } from "@/api/dining.js";
|
|
|
|
|
|
|
|
const payType = ref('confirmOrder'); // confirmOrder 确认下单 confirmPay 确认支付
|
|
const payType = ref('confirmOrder'); // confirmOrder 确认下单 confirmPay 确认支付
|
|
|
|
|
+/** 仅当从订单列表点击「去结算」进入时为 true,底部确认下单按钮才显示金额 */
|
|
|
|
|
+const fromCheckout = ref(false);
|
|
|
|
|
+
|
|
|
|
|
+// 订单信息(从购物车带过来或 URL 参数)
|
|
|
|
|
+const orderInfo = ref({
|
|
|
|
|
+ tableId: '',
|
|
|
|
|
+ diners: '',
|
|
|
|
|
+ contactPhone: '',
|
|
|
|
|
+ remark: '',
|
|
|
|
|
+ totalAmount: 0,
|
|
|
|
|
+ utensilFee: 0,
|
|
|
|
|
+ discountAmount: 0,
|
|
|
|
|
+ payAmount: 0
|
|
|
|
|
+});
|
|
|
|
|
+
|
|
|
|
|
+// 菜品列表(从购物车带过来)
|
|
|
|
|
+const foodList = ref([]);
|
|
|
|
|
|
|
|
-// 菜品列表数据(示例数据,实际应该从购物车或订单数据中获取)
|
|
|
|
|
-const foodList = ref([
|
|
|
|
|
- {
|
|
|
|
|
- id: 1,
|
|
|
|
|
- name: '石板肉酱豆腐',
|
|
|
|
|
- price: 19.9,
|
|
|
|
|
- image: '/static/demo.png',
|
|
|
|
|
- tags: [
|
|
|
|
|
- { text: '份', type: 'normal' },
|
|
|
|
|
- { text: '微辣', type: 'spicy' }
|
|
|
|
|
- ],
|
|
|
|
|
- quantity: 1
|
|
|
|
|
- },
|
|
|
|
|
- {
|
|
|
|
|
- id: 2,
|
|
|
|
|
- name: '经典三杯鸡',
|
|
|
|
|
- price: 26.9,
|
|
|
|
|
- image: '/static/demo.png',
|
|
|
|
|
- tags: [
|
|
|
|
|
- { text: '份', type: 'normal' }
|
|
|
|
|
- ],
|
|
|
|
|
- quantity: 1
|
|
|
|
|
|
|
+// 菜品图片:兼容 cuisineImage/image,逗号分隔取首图
|
|
|
|
|
+function getItemImage(item) {
|
|
|
|
|
+ const raw = item?.cuisineImage ?? item?.image ?? item?.imageUrl ?? '';
|
|
|
|
|
+ const url = typeof raw === 'string' ? raw.split(',')[0].trim() : raw;
|
|
|
|
|
+ return url ? getFileUrl(url) : '';
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 单品单价:列表展示用 unitPrice/price
|
|
|
|
|
+function getItemPrice(item) {
|
|
|
|
|
+ const p = item?.unitPrice ?? item?.price ?? item?.totalPrice ?? item?.salePrice ?? 0;
|
|
|
|
|
+ return Number(p) || 0;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function formatPrice(price) {
|
|
|
|
|
+ const num = Number(price);
|
|
|
|
|
+ return Number.isNaN(num) ? '0.00' : num.toFixed(2);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 将存储的购物车项转为确认页展示格式(兼容 cuisineName/cuisineImage/unitPrice 等)
|
|
|
|
|
+function normalizeCartItem(item) {
|
|
|
|
|
+ const image = item?.cuisineImage ?? item?.image ?? item?.imageUrl ?? '';
|
|
|
|
|
+ const url = typeof image === 'string' ? image.split(',')[0].trim() : image;
|
|
|
|
|
+ return {
|
|
|
|
|
+ id: item?.id ?? item?.cuisineId,
|
|
|
|
|
+ name: item?.name ?? item?.cuisineName,
|
|
|
|
|
+ cuisineName: item?.cuisineName,
|
|
|
|
|
+ price: item?.unitPrice ?? item?.price,
|
|
|
|
|
+ image: url,
|
|
|
|
|
+ cuisineImage: item?.cuisineImage,
|
|
|
|
|
+ quantity: Number(item?.quantity) || 0,
|
|
|
|
|
+ tags: item?.tags ?? [],
|
|
|
|
|
+ subtotalAmount: item?.subtotalAmount,
|
|
|
|
|
+ remark: item?.remark
|
|
|
|
|
+ };
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const handleConfirmOrder = async () => {
|
|
|
|
|
+ const tableId = orderInfo.value.tableId;
|
|
|
|
|
+ const contactPhone = orderInfo.value.contactPhone ?? '';
|
|
|
|
|
+ const dinerCount = Number(orderInfo.value.diners) || 0;
|
|
|
|
|
+ if (!tableId) {
|
|
|
|
|
+ uni.showToast({ title: '请先选择桌号', icon: 'none' });
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!contactPhone) {
|
|
|
|
|
+ uni.showToast({ title: '请先填写联系电话', icon: 'none' });
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ const remark = (orderInfo.value.remark ?? '').trim().slice(0, 30);
|
|
|
|
|
+ uni.showLoading({ title: '提交中...' });
|
|
|
|
|
+ try {
|
|
|
|
|
+ const res = await PostOrderCreate({
|
|
|
|
|
+ tableId,
|
|
|
|
|
+ contactPhone,
|
|
|
|
|
+ couponId: null,
|
|
|
|
|
+ dinerCount: dinerCount || undefined,
|
|
|
|
|
+ immediatePay: 0,
|
|
|
|
|
+ remark: remark || undefined
|
|
|
|
|
+ });
|
|
|
|
|
+ uni.hideLoading();
|
|
|
|
|
+ const orderId = res?.id ?? res?.orderId ?? res?.data?.id ?? res?.data?.orderId;
|
|
|
|
|
+ // 结果页「去结算」需带金额和备注,保存本次下单的桌号、人数、金额、备注
|
|
|
|
|
+ const total = orderInfo.value.payAmount ?? orderInfo.value.totalAmount ?? 0;
|
|
|
|
|
+ uni.setStorageSync('lastPlaceOrderInfo', JSON.stringify({
|
|
|
|
|
+ tableId: orderInfo.value.tableId,
|
|
|
|
|
+ diners: orderInfo.value.diners,
|
|
|
|
|
+ totalAmount: total,
|
|
|
|
|
+ remark: (orderInfo.value.remark ?? '').trim().slice(0, 30)
|
|
|
|
|
+ }));
|
|
|
|
|
+ if (orderId != null) {
|
|
|
|
|
+ go(`/pages/result/index?id=${encodeURIComponent(orderId)}`);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ go('/pages/result/index');
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ uni.hideLoading();
|
|
|
|
|
+ uni.showToast({ title: e?.message || '下单失败', icon: 'none' });
|
|
|
}
|
|
}
|
|
|
-]);
|
|
|
|
|
|
|
+};
|
|
|
|
|
|
|
|
-const handleConfirmOrder = () => {
|
|
|
|
|
- go('/pages/result/index');
|
|
|
|
|
|
|
+const handleConfirmPay = () => {
|
|
|
|
|
+ go('/pages/result/index?payType=confirmPay');
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
-onLoad((e) => {
|
|
|
|
|
- if(e.payType) payType.value = e.payType;
|
|
|
|
|
- // TODO: 从存储或接口获取订单菜品数据
|
|
|
|
|
- // const cartList = uni.getStorageSync('cartList') || [];
|
|
|
|
|
- // foodList.value = cartList.filter(item => item.quantity > 0);
|
|
|
|
|
|
|
+onLoad((options) => {
|
|
|
|
|
+ if (options?.payType) payType.value = options.payType;
|
|
|
|
|
+ const userStore = useUserStore();
|
|
|
|
|
+ const contactPhone = userStore.getUserInfo?.phone ?? userStore.getUserInfo?.contactPhone ?? userStore.getUserInfo?.mobile ?? '';
|
|
|
|
|
+ orderInfo.value.contactPhone = contactPhone;
|
|
|
|
|
+ if (options?.tableId) orderInfo.value.tableId = options.tableId;
|
|
|
|
|
+ if (options?.diners) orderInfo.value.diners = options.diners;
|
|
|
|
|
+ if (options?.remark != null && options?.remark !== '') orderInfo.value.remark = decodeURIComponent(options.remark);
|
|
|
|
|
+ if (options?.totalAmount != null && options?.totalAmount !== '') {
|
|
|
|
|
+ fromCheckout.value = true;
|
|
|
|
|
+ const total = Number(options.totalAmount) || 0;
|
|
|
|
|
+ orderInfo.value.totalAmount = total;
|
|
|
|
|
+ orderInfo.value.payAmount = total;
|
|
|
|
|
+ }
|
|
|
|
|
+ const raw = uni.getStorageSync('placeOrderCart');
|
|
|
|
|
+ if (raw) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ const data = JSON.parse(raw);
|
|
|
|
|
+ const list = Array.isArray(data.list) ? data.list : [];
|
|
|
|
|
+ foodList.value = list.map(normalizeCartItem).filter((item) => (item.quantity || 0) > 0);
|
|
|
|
|
+ if (data.tableId != null) orderInfo.value.tableId = data.tableId;
|
|
|
|
|
+ if (data.diners != null) orderInfo.value.diners = data.diners;
|
|
|
|
|
+ if (data.remark != null) orderInfo.value.remark = data.remark;
|
|
|
|
|
+ const total = Number(data.totalAmount) || 0;
|
|
|
|
|
+ orderInfo.value.totalAmount = total;
|
|
|
|
|
+ orderInfo.value.payAmount = total;
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ console.error('解析购物车数据失败:', e);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ if (orderInfo.value.tableId) uni.setStorageSync('currentTableId', String(orderInfo.value.tableId));
|
|
|
});
|
|
});
|
|
|
</script>
|
|
</script>
|
|
|
|
|
|