This commit is contained in:
gyq
2026-07-17 14:31:37 +08:00
parent 3c86457ca8
commit 81cd8746b0
12 changed files with 1136 additions and 37 deletions

View File

@@ -81,4 +81,26 @@ export default {
label: "退单",
},
],
platformType: [
{
key: "PC",
label: "收银机客户端",
},
{
key: "wechat",
label: "微信小程序",
},
{
key: "alipay",
label: "支付宝小程序",
},
{
key: "admin-pc",
label: "PC管理端",
},
{
key: "admin-app",
label: "APP管理端",
},
]
};

View File

@@ -2,6 +2,7 @@ import OrderApi from "@/api/order/order";
import type { editRequest } from "@/api/system/version";
import type { IContentConfig } from "@/components/CURD/types";
import { returnOptionsLabel } from "./config";
import { useRoute } from 'vue-router'
const contentConfig: IContentConfig = {
pageName: "sys:user",
table: {
@@ -22,7 +23,9 @@ const contentConfig: IContentConfig = {
delete params.createAt;
}
const res = await OrderApi.getList(params)
const route = useRoute()
const res = await OrderApi.getList({ ...params, userId: route.query.userId })
console.log('OrderApi.getList', res);
res.records.map(item => {

View File

@@ -0,0 +1,744 @@
<template>
<div style="padding: 20px 0;width: 100%;flex: 1;">
<div class="header">
<div class="title">
<div class="back">
<el-icon :size="20" @click="router.back()">
<Back />
</el-icon>
</div>
订单详情
</div>
</div>
<div class="order_detail_container">
<div class="order_top">
<!-- <div class="info_content">
<div class="item">
<div class="label">会员信息</div>
<div class="row">
<div>会员昵称-{{ user.nickName || "" }}</div>
<div>联系电话-{{ user.phone || "" }}</div>
</div>
</div>
</div> -->
<div class="table">
<div class="item div1">
<el-text type="info">台桌信息</el-text>
<el-text size="large">{{ detail.tableName || '无台桌' }}</el-text>
<el-tag :type="detail.status == 'closed' ? 'success' : 'primary'" disable-transitions size="large"
style="margin-left: 14px;">
{{ statusFilter(detail.status) }}
</el-tag>
</div>
<div class="item div2">
<el-text type="info">订单类型</el-text>
<el-text>{{ sendTypeFilter(detail.dineMode) }}</el-text>
</div>
<div class="item div3">
<el-text type="info">下单方式</el-text>
<el-text>{{ platformTypeFilter(detail.platformType) }}</el-text>
</div>
<div class="item div4">
<el-text type="info">订单编号</el-text>
<el-text>{{ detail.orderNo }}</el-text>
</div>
<div class="item div5">
<el-text type="info">支付时间</el-text>
<el-text>{{ timeFilter(detail.paidTime) }}</el-text>
</div>
<div class="item div6">
<el-text type="info">下单时间</el-text>
<el-text>{{ timeFilter(detail.createTime) }}</el-text>
</div>
</div>
<!-- <div class="table">
<div class="item">
<div class="t">订单编号</div>
<div class="b">
{{ detail.orderNo }}
</div>
</div>
<div class="item">
<div class="t">下单时间</div>
<div class="b">{{ timeFilter(detail.createTime) }}</div>
</div>
<div class="item">
<div class="t">支付时间</div>
<div class="b">
{{ timeFilter(detail.paidTime) }}
</div>
</div>
</div> -->
</div>
<div class="info_content">
<div class="item">
<div class="title">订单金额</div>
<div class="row">
<div class="row_col">
<el-text type="info">实收金额</el-text>
<el-text size="large">{{ detail.payAmount }}</el-text>
<el-button v-if="detail.status != 'unpaid' && detail.refundAmount < detail.payAmount" size="small"
type="danger" class="u-m-l-10" @click="tuikuan()">
<span>退款</span>
</el-button>
</div>
<div class="row_col">
<el-text type="info">支付方式</el-text>
<el-text>{{ returnPayType(detail.payType) || '-' }}</el-text>
</div>
</div>
<div class="row">
<div class="row_col">
<el-text type="info">订单原价</el-text>
<el-text>{{ detail.originAmount }}</el-text>
</div>
<div class="row_col">
<el-text type="info">打包费</el-text>
<el-text>{{ detail.packFee || "-" }}</el-text>
</div>
<div class="row_col">
<el-text type="info">餐位费</el-text>
<el-text>{{ detail.seatAmount || "-" }}</el-text>
</div>
<div class="row_col">
<el-text type="info">商品券抵扣</el-text>
<el-text>{{ detail.productCouponDiscountAmount || '-' }}</el-text>
</div>
<div class="row_col">
<el-text type="info">优惠券抵扣</el-text>
<el-text>{{ detail.otherCouponDiscountAmount || '-' }}</el-text>
</div>
<div class="row_col"></div>
</div>
<div class="row">
<div class="row_col">
<el-text type="info">满减活动</el-text>
<el-text>{{ detail.discountActAmount || "-" }}</el-text>
</div>
<div class="row_col">
<el-text type="info">新客立减</el-text>
<el-text>{{ detail.newCustomerDiscountAmount || "-" }}</el-text>
</div>
<div class="row_col">
<el-text type="info">退单金额</el-text>
<el-text>{{ detail.refundAmount || "-" }}</el-text>
</div>
<div class="row_col">
<el-text type="info">整单改价</el-text>
<el-text>{{ detail.discountAmount || '-' }}</el-text>
</div>
<div class="row_col">
<el-text type="info">积分抵扣</el-text>
<el-text>{{ detail.pointsDiscountAmount || '-' }}</el-text>
</div>
<div class="row_col">
<el-text type="info">抹零</el-text>
<el-text>{{ detail.roundAmount || '-' }}</el-text>
</div>
</div>
<!-- <div class="row">
<div>满减活动{{ detail.discountActAmount || "-" }}</div>
<div>新客立减{{ detail.newCustomerDiscountAmount || "-" }}</div>
<div>会员折扣{{ detail.vipDiscountAmount || "-" }}</div>
</div>
<div class="row">
<div>退单金额{{ detail.refundAmount }}</div>
<div>整单改价{{ detail.discountAmount }}</div>
<div>积分抵扣{{ detail.pointsDiscountAmount }}</div>
<div>抹零{{ detail.roundAmount }}</div>
<div>
实收金额
<span style="color: red">{{ detail.payAmount }}</span>
</div>
</div> -->
<!-- <div class="row">
<div>支付方式{{ returnPayType(detail.payType) }}</div>
<div>
退款金额{{ detail.refundAmount }}
</div>
<div class="color-red">
<span v-if="detail.refundType">
退款方式{{ detail.refundType === "cash" ? "现金退款" : "原路退回" }}
</span>
</div>
</div> -->
</div>
<div class="item" v-if="detail.refundType">
<div class="title">退款详情</div>
<div class="row">
<div class="row_col">
<el-text type="info">退款金额</el-text>
<el-text type="danger">{{ detail.refundAmount }}</el-text>
</div>
<div class="row_col">
<el-text type="info">退款方式</el-text>
<el-text type="danger">{{ detail.refundType === "cash" ? "现金退款" : "原路退回" }}</el-text>
</div>
<div class="row_col">
<el-text type="info">退款原因</el-text>
<el-text type="danger">{{ detail.refundRemark }}</el-text>
</div>
</div>
</div>
</div>
<div class="info_content" v-if="user.id">
<div class="title">用户信息</div>
<div class="user_info_wrap">
<div class="user_info_wrap_item" style="width: 60px;flex: none;">
<el-avatar :size="40" :src="user.headImg">{{ user.nickName || '-' }}</el-avatar>
</div>
<div class="user_info_wrap_item">
<el-text type="info">用户名称</el-text>
<el-text>{{ user.nickName || '-' }}</el-text>
</div>
<div class="user_info_wrap_item">
<el-text type="info">ID</el-text>
<el-text>{{ user.id || '-' }}</el-text>
</div>
<div class="user_info_wrap_item">
<el-text type="info">当前余额</el-text>
<el-text>{{ user.amount || '-' }}</el-text>
</div>
<div class="user_info_wrap_item">
<el-text type="info">会员</el-text>
<el-text>{{ user.isVip ? user.memberLevelName : '暂未成为会员' }}</el-text>
</div>
<div class="user_info_wrap_item">
<el-text type="info">消费累计</el-text>
<el-text>{{ user.consumeAmount || '-' }}</el-text>
</div>
<div class="user_info_wrap_item">
<el-text type="info">注册时间</el-text>
<el-text>{{ user.createTime || '-' }}</el-text>
</div>
<div class="user_info_wrap_item" style="padding-left: 24px;">
<el-button type="primary"
@click="router.push({ path: '/order/index', query: { userId: user.userId } })">查看所有订单</el-button>
</div>
</div>
</div>
<div class="shop_info_wrap">
<div style="margin-bottom: 16px; font-size: 16px">商品详情</div>
<template v-for="(item, index) in detail.detailMap" :key="index">
<h4>{{ index }}次下单</h4>
<el-table :data="item" :ref="el => { if (el) tableRefs[`refTable${index}`] = el }"
@select-all="tableSelectAll($event, index)">
<el-table-column label="数量" type="selection">
<template #default="scope">
<div v-if="detail.status == 'unpaid'">
<el-checkbox v-if="scope.row.num - scope.row.returnNum > 0" v-model="scope.row.checked" />
</div>
<div v-else>
<el-checkbox v-if="scope.row.num - scope.row.refundNum > 0" v-model="scope.row.checked" />
</div>
</template>
</el-table-column>
<el-table-column label="商品" width="150">
<template #default="scope">
<div class="shop_info">
<el-image :src="scope.row.productImg" style="width: 40px; height: 40px"></el-image>
<div class="info">
<span :class="[scope.row.isVip == 1 ? 'colorStyle' : '']">
{{ scope.row.productName }}
</span>
<span style="color: #999">{{ scope.row.productSkuName }}</span>
<div>
<el-tag v-if="scope.row.isTimeDiscount" type="danger" disable-transitions>
限时折扣
</el-tag>
</div>
</div>
</div>
</template>
</el-table-column>
<el-table-column label="下单数量">
<template #default="scope">x{{ scope.row.num }}</template>
</el-table-column>
<el-table-column label="退菜数量">
<template #default="scope">x{{ scope.row.returnNum }}</template>
</el-table-column>
<el-table-column label="单价">
<template #default="scope">{{ scope.row.unitPrice }}</template>
</el-table-column>
<el-table-column label="备注" prop="remark"></el-table-column>
<el-table-column label="出菜时间" prop="dishOutTime" width="180"></el-table-column>
<el-table-column label="上菜时间" prop="foodServeTime" width="180"></el-table-column>
<el-table-column label="小计">
<template #default="scope">{{ scope.row.payAmount }}</template>
</el-table-column>
<el-table-column label="实付">
<template #default="scope">{{ scope.row.payAmount }}</template>
</el-table-column>
<el-table-column v-if="detail.status == 'unpaid'" label="可退菜数量" align="center" width="130px">
<template #default="scope">
<el-input-number v-if="scope.row.checked" :min="0" style="width: 100px" v-model="scope.row.selNumber"
:max="scope.row.num - scope.row.returnNum"></el-input-number>
<span v-else>{{ scope.row.num - scope.row.returnNum }}</span>
</template>
</el-table-column>
<el-table-column v-else label="可退款数量" align="center" width="130px">
<template #default="scope">
<el-input-number v-if="scope.row.checked" :min="0" style="width: 100px" v-model="scope.row.selNumber"
:max="scope.row.num - scope.row.refundNum - scope.row.returnNum"></el-input-number>
<span v-else>
{{ scope.row.num - scope.row.refundNum - scope.row.returnNum }}
</span>
</template>
</el-table-column>
<el-table-column label="已退款数量" width="100" align="center">
<template #default="scope">
<span v-if="detail.status == 'unpaid'">{{ scope.row.returnNum }}</span>
<span v-else>{{ scope.row.refundNum }}</span>
</template>
</el-table-column>
<el-table-column label="操作" fixed="right">
<template #default="scope">
<template v-if="detail.status != 'unpaid'">
<el-button v-if="canTuikuan(scope.row)" link size="small" @click="tuikuan(scope.row)">
<span>退款</span>
</el-button>
<span class="color-999" v-if="scope.row.status == 'refund'">已退款</span>
</template>
<template v-if="detail.status == 'unpaid'">
<el-button v-if="canTuicai(scope.row)" link size="small" @click="tuicai(scope.row)">
<span>退菜</span>
</el-button>
<span class="color-999" v-else>已退菜</span>
</template>
</template>
</el-table-column>
</el-table>
</template>
<div class="u-p-20 u-flex u-row-right"
v-if="detail.status !== 'refund' && detail.status !== 'unpaid' && detail.status !== 'cancelled'">
<el-checkbox v-model="allSelected" @change="allSelectedChange" label="全选"></el-checkbox>
<el-button type="danger" class="u-m-l-20" @click.stop="tuikuan('all')">退款</el-button>
</div>
<div class="u-p-20 u-flex u-row-right" v-if="detail.status == 'unpaid'">
<el-checkbox v-model="allSelected" @change="allSelectedChange" label="全选"></el-checkbox>
<el-button type="danger" class="u-m-l-20" @click.stop="tuicai('all')">退菜</el-button>
</div>
</div>
</div>
<return-money :modal="false" ref="refReturnMoney" :max="selGoods.num" :goods="selGoods"
@confirm="refReturnMoneyConfirm"></return-money>
<order-return-cart ref="refReturnCart" @confirm="refReturnCartConfirm"></order-return-cart>
</div>
</template>
<script setup>
import { ref, computed, watch, defineEmits, onMounted } from 'vue'
import { useRoute, useRouter } from 'vue-router';
import returnMoney from "./components/return-money.vue";
import orderReturnCart from "./components/order-return-cart.vue";
import { ElMessage } from "element-plus";
import { returnOptionsLabel } from "./config/config";
import * as $util from "./order_goods_util.js";
import orderApi from "@/api/order/order";
import shopUserApi from "@/api/account/shopUser";
import orderEnum from "./components/orderEnum";
import dayjs from "dayjs";
import { use } from 'echarts';
const route = useRoute()
const router = useRouter()
const emit = defineEmits(['close'])
// 基础变量
const allSelected = ref(false)
const user = ref("")
const drawer = ref(false)
const type = ref("1")
const detail = ref("")
const loading = ref(false)
const refoundList = ref([])
const selGoods = ref({ num: 1 })
const tableRefs = ref({})
// 子组件ref
const refReturnMoney = ref(null)
const refReturnCart = ref(null)
// 计算属性
const returnOriginAmount = computed(() => {
let amount = 0;
if (detail.value && detail.value.originAmount) {
return detail.value.originAmount;
}
if (!detail.value) {
return 0;
}
for (const i in detail.value.detailMap) {
amount += detail.value.detailMap[i].reduce((pre, cur) => pre + cur.payAmount, 0);
}
return amount.toFixed(2);
})
// 监听关闭抽屉
watch(drawer, (newval) => {
if (!newval) {
reset();
}
})
// 方法
const tableSelectAll = (e, index) => {
const arr = detail.value.detailMap[index];
for (const i in arr) {
arr[i].checked = e.length ? true : false;
}
}
const allSelectedChange = (newval) => {
for (const i in detail.value.detailMap) {
for (const key in detail.value.detailMap[i]) {
detail.value.detailMap[i][key].checked = newval;
}
}
}
const reset = () => {
user.value = "";
allSelected.value = false;
emit("close");
}
const returnPayType = (payType) => {
if (!payType) return "";
return returnOptionsLabel("payType", payType);
}
const sendTypeFilter = (t) => {
if (!t) return t;
const item = orderEnum.sendType.find(item => item.key == t);
return item ? item.label : "-";
}
const platformTypeFilter = (t) => {
if (!t) return t;
const item = orderEnum.platformType.find(item => item.key == t);
return item ? item.label : "-";
}
const statusFilter = (t) => {
return returnOptionsLabel("status", t);
}
const timeFilter = (t) => {
return t ? dayjs(t).format("YYYY-MM-DD HH:mm:ss") : "-";
}
const canTuikuan = (item) => $util.canTuiKuan(detail.value, item)
const canTuicai = (item) => $util.canTuicai(detail.value, item)
const refReturnMoneyConfirm = async (e) => {
await orderApi.refundOrder({ ...e, orderId: detail.value.id });
ElMessage.success("退款成功");
update();
}
const update = () => {
tbOrderInfoDetail(detail.value.id);
}
const refReturnCartConfirm = async (e) => {
await orderApi.refundOrder({ ...e, orderId: detail.value.id });
ElMessage.success("退菜成功");
update();
}
const tuikuan = (item) => {
if (!item) {
refReturnMoney.value.open([], detail.value);
return;
}
const arr = [];
if (item === "all") {
for (const i in detail.value.detailMap) {
detail.value.detailMap[i].forEach(v => {
if (v.checked && v.selNumber) arr.push(v);
});
}
} else {
arr.push(item);
}
if (arr.length === 0) {
return ElMessage.error("请选择要退款的商品和数量");
}
refReturnMoney.value.open(arr, detail.value);
}
const tuicai = (item) => {
const arr = [];
if (item === "all") {
for (const i in detail.value.detailMap) {
detail.value.detailMap[i].forEach(v => {
if (v.checked && v.selNumber) arr.push(v);
});
}
} else {
arr.push(item);
}
if (arr.length === 0) {
return ElMessage.error("请选择要退菜的商品和数量");
}
refReturnCart.value.open(arr, detail.value);
}
// 拉取订单详情
const tbOrderInfoDetail = async (id) => {
try {
loading.value = true;
const res = await orderApi.get({ orderId: id });
if (res.userId) {
shopUserApi.get({ userId: res.userId }).then(res1 => {
user.value = res1;
});
}
for (const i in res.detailMap) {
res.detailMap[i] = res.detailMap[i].map(v => {
return {
...v,
checked: false,
selNumber: v.num - (res.status == "unpaid" ? v.returnNum : v.refundNum),
};
});
}
detail.value = res;
loading.value = false;
} catch (error) {
console.log(error);
}
}
const show = (obj) => {
drawer.value = true;
type.value = "1";
detail.value = "";
tbOrderInfoDetail(obj.id);
}
// 保留废弃方法防止外部调用报错
const tableSelect = () => { }
const tableSelectionChange = () => { }
const rowClick = () => { }
const isShowGoodsVipPrice = (item) => $util.isShowGoodsVipPrice(item)
const isUseCalcPrice = (item) => $util.isUseCalcPrice(detail.value, item)
const close = () => emit("close")
const isTui = (item) => $util.isTui(item)
const to2 = (n) => Number(n).toFixed(2)
const orderTypeFilter = (t) => {
if (!t) return t;
const row = orderEnum.orderType.find(item => item.key == t);
return row?.label;
}
onMounted(() => {
if (route.query.orderId) {
tbOrderInfoDetail(route.query.orderId);
}
})
</script>
<style scoped lang="scss">
.packeFee {
width: 40px;
box-sizing: border-box;
height: 40px;
background: #3f9eff;
color: #fff;
font-size: 12px;
display: flex;
justify-content: center;
align-items: center;
white-space: nowrap;
}
.shop_info {
display: flex;
.info {
flex: 1;
display: flex;
flex-direction: column;
padding-left: 6px;
}
}
.header {
padding: 0 20px 0;
margin-bottom: 20px;
.title {
display: flex;
align-items: center;
gap: 10px;
.back {
display: flex;
align-items: center;
justify-content: center;
&:hover {
cursor: pointer;
}
}
}
}
.line-th {
text-decoration: line-through;
}
.order_detail_container {
width: 100%;
--gap: 20px;
padding: 0 var(--gap);
font-size: 14px;
.order_top {
background-color: #fff;
border-radius: 8px;
padding: 14px;
.table {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-rows: repeat(3, 1fr);
grid-column-gap: 0px;
grid-row-gap: 10px;
.item {
display: flex;
align-items: center;
}
.div1 {
grid-area: 1 / 1 / 2 / 4;
}
.div2 {
grid-area: 2 / 1 / 3 / 2;
}
.div3 {
grid-area: 2 / 2 / 3 / 3;
}
.div4 {
grid-area: 3 / 1 / 4 / 2;
}
.div5 {
grid-area: 3 / 2 / 4 / 3;
}
.div6 {
grid-area: 3 / 3 / 4 / 4;
}
}
}
.info_content {
padding: var(--gap);
background-color: #fff;
border-radius: 8px;
margin-top: var(--gap);
.item {
// border-bottom: 1px dashed #ececec;
padding-bottom: 20px;
&:not(:first-child) {
margin-top: 20px;
}
.title {
font-size: 16px;
color: #333;
}
.row {
display: flex;
color: #555;
padding-top: 20px;
.row_col {
flex: 1;
display: flex;
align-items: center;
}
div {
width: 25%;
}
}
}
.user_info_wrap {
display: flex;
margin-top: var(--gap);
.user_info_wrap_item {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
}
}
}
.shop_info_wrap {
background-color: #fff;
border-radius: 8px;
padding: var(--gap);
margin-top: var(--gap);
}
.refund_wrap {
.row {
border-bottom: 1px dashed #ececec;
padding-bottom: 20px;
&:not(:first-child) {
margin-top: 20px;
}
.time {
font-weight: bold;
color: #333;
}
.list {
.list_row {
display: flex;
padding-top: 10px;
.item {
flex: 1;
display: flex;
color: #555;
.name {
margin-left: 6px;
}
}
}
}
.foot {
color: #333;
display: flex;
justify-content: flex-end;
}
}
}
}
.colorStyle {
color: #ffc315;
}
</style>

View File

@@ -238,7 +238,8 @@ function toPayOrder(order: getListResponse) {
//详情
const refDetail = ref();
function showdetail(row: OrderInfoVo) {
refDetail.value.show(row);
// refDetail.value.show(row);
router.push({ name: 'order_detail', query: { orderId: row.id } })
}
</script>