Files
mp-vue/src/views/shop/shopOrder/index.vue

690 lines
20 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<a-page-header :title="getPageTitle()" @back="() => $router.go(-1)">
<a-card style="margin-bottom: 20px">
<search
@search="reload"
:selection="selection"
@add="openEdit"
@remove="removeBatch"
@batchMove="openMove"
/>
</a-card>
<a-card :bordered="false" :body-style="{ padding: '16px' }">
<a-tabs type="card" v-model:activeKey="activeKey" @change="onTabs">
<a-tab-pane key="all" tab="全部"/>
<a-tab-pane key="unpaid" tab="待付款"/>
<a-tab-pane key="undelivered" tab="待发货"/>
<a-tab-pane key="unreceived" tab="待收货"/>
<a-tab-pane key="completed" tab="已完成"/>
<a-tab-pane key="cancelled" tab="已取消"/>
<!-- <a-tab-pane key="unevaluated" tab="待评价"/>-->
<a-tab-pane key="refunded" tab="已退款"/>
<!-- <a-tab-pane key="deleted" tab="已删除"/>-->
</a-tabs>
<ele-pro-table
ref="tableRef"
row-key="orderId"
:columns="columns"
:datasource="datasource"
:customRow="customRow"
:toolbar="false"
tool-class="ele-toolbar-form"
class="sys-org-table"
>
<template #toolbar>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'name'">
<div @click="onSearch(record)" class="cursor-pointer">{{ record.name || '匿名' }}</div>
</template>
<template v-if="column.key === 'orderGoods'">
<template v-for="(item,index) in record.orderGoods" :key="index">
<div class="item py-1">
<a-space :id="`g-${index}`">
<a-avatar :src="item.image" shape="square"/>
<span>{{ item.goodsName }}</span>
</a-space>
</div>
</template>
</template>
<template v-if="column.key === 'phone'">
<div v-if="record.mobile" class="text-gray-400">{{ record.mobile }}</div>
<div v-else class="text-gray-600">{{ record.phone }}</div>
</template>
<template v-if="column.key === 'payType'">
<template v-for="item in getPayType()">
<template v-if="record.payStatus == 1">
<span v-if="item.value == record.payType">{{ item.label }}</span>
</template>
<template v-else>
<span></span>
</template>
</template>
</template>
<template v-if="column.key === 'payStatus'">
<a-tag v-if="record.payStatus" color="green" @click.stop="updatePayStatus(record)"
class="cursor-pointer">已付款
</a-tag>
<a-tag v-if="!record.payStatus" @click.stop="updatePayStatus(record)" class="cursor-pointer">未付款
</a-tag>
<a-tag v-if="record.payStatus == 3">未付款,占场中</a-tag>
</template>
<template v-if="column.key === 'image'">
<a-image :src="record.image" :width="50"/>
</template>
<template v-if="column.key === 'sex'">
<a-tag v-if="record.sex === 1"></a-tag>
<a-tag v-if="record.sex === 2"></a-tag>
</template>
<template v-if="column.key === 'deliveryStatus'">
<a-tag v-if="record.deliveryStatus == 10">未发货</a-tag>
<a-tag v-if="record.deliveryStatus == 20" color="green">已发货</a-tag>
<a-tag v-if="record.deliveryStatus == 30" color="bule">部分发货</a-tag>
</template>
<template v-if="column.key === 'orderStatus'">
<a-tag v-if="record.orderStatus === 0">未完成</a-tag>
<a-tag v-if="record.orderStatus === 1" color="green">已完成</a-tag>
<a-tag v-if="record.orderStatus === 2" color="red">已取消</a-tag>
<a-tag v-if="record.orderStatus === 3" color="red">取消中</a-tag>
<a-tag v-if="record.orderStatus === 4" color="red">退款申请中</a-tag>
<a-tag v-if="record.orderStatus === 5" color="red">退款被拒绝</a-tag>
<a-tag v-if="record.orderStatus === 6" color="orange">退款成功</a-tag>
<a-tag v-if="record.orderStatus === 7" color="pink">客户端申请退款</a-tag>
</template>
<template v-if="column.key === 'isInvoice'">
<a-tag v-if="record.isInvoice == 0">未开具</a-tag>
<a-tag v-if="record.isInvoice == 1" color="green">已开具</a-tag>
<a-tag v-if="record.isInvoice == 2" color="blue">不能开具</a-tag>
</template>
<template v-if="column.key === 'status'">
<a-tag v-if="record.status === 0" color="green">显示</a-tag>
<a-tag v-if="record.status === 1" color="red">隐藏</a-tag>
</template>
<template v-if="column.key === 'action'">
<!-- 查看详情 - 所有状态都可以查看 -->
<a @click.stop="openEdit(record)">
<EyeOutlined /> 详情
</a>
<!-- 未付款状态的操作 -->
<template v-if="!record.payStatus && record.orderStatus === 0">
<a-divider type="vertical"/>
<a @click.stop="handleEditOrder(record)">
<EditOutlined /> 修改
</a>
<a-divider type="vertical"/>
<a
@click.stop="openEdit(record)"
>
<a class="ele-text-warning">
<CloseOutlined /> 取消
</a>
</a>
</template>
<!-- 已付款未发货状态的操作 -->
<template v-if="record.payStatus && record.deliveryStatus === 10 && !isCancelledStatus(record.orderStatus)">
<a-divider type="vertical"/>
<a @click.stop="handleDelivery(record)" class="ele-text-primary">
<SendOutlined /> 发货
</a>
<a-divider type="vertical"/>
<a @click.stop="handleApplyRefund(record)">
<UndoOutlined /> 退款
</a>
</template>
<!-- 已发货未完成状态的操作 -->
<template v-if="record.payStatus && record.deliveryStatus === 20 && record.orderStatus === 0">
<a-divider type="vertical"/>
<a @click.stop="handleConfirmReceive(record)" class="ele-text-primary">
<CheckOutlined /> 确认收货
</a>
<a-divider type="vertical"/>
<a @click.stop="handleApplyRefund(record)">
<UndoOutlined /> 退款
</a>
</template>
<!-- 退款相关状态的操作 -->
<template v-if="isRefundStatus(record.orderStatus)">
<template v-if="record.orderStatus === 4 || record.orderStatus === 7">
<a-divider type="vertical"/>
<a @click.stop="handleApproveRefund(record)" class="ele-text-success">
<CheckCircleOutlined /> 同意退款
</a>
<a-divider type="vertical"/>
<a @click.stop="handleRejectRefund(record)" class="ele-text-danger">
<CloseCircleOutlined /> 拒绝退款
</a>
</template>
<template v-if="record.orderStatus === 5">
<a-divider type="vertical"/>
<a @click.stop="handleRetryRefund(record)">
<RedoOutlined /> 重新处理
</a>
</template>
</template>
<!-- 已完成状态的操作 -->
<template v-if="record.orderStatus === 1">
<a-divider type="vertical"/>
<a @click.stop="handleApplyRefund(record)">
<UndoOutlined /> 申请退款
</a>
</template>
<!-- 删除操作 - 已完成已取消退款成功的订单可以删除 -->
<template v-if="canDeleteOrder(record)">
<a-divider type="vertical"/>
<a-popconfirm
title="确定要删除此订单吗?删除后无法恢复。"
@confirm.stop="remove(record)"
>
<a class="ele-text-danger">
<DeleteOutlined /> 删除
</a>
</a-popconfirm>
</template>
</template>
</template>
</ele-pro-table>
</a-card>
<!-- 编辑弹窗 -->
<OrderInfo v-model:visible="showEdit" :data="current" @done="reload"/>
</a-page-header>
</template>
<script lang="ts" setup>
import {createVNode, ref} from 'vue';
import type {EleProTable} from 'ele-admin-pro';
import type {
DatasourceFunction,
ColumnItem
} from 'ele-admin-pro/es/ele-pro-table/types';
import {
ExclamationCircleOutlined,
EyeOutlined,
EditOutlined,
CloseOutlined,
SendOutlined,
UndoOutlined,
CheckOutlined,
CheckCircleOutlined,
CloseCircleOutlined,
RedoOutlined,
DeleteOutlined
} from '@ant-design/icons-vue';
import Search from './components/search.vue';
import {getPageTitle} from "@/utils/common";
import {toDateString} from 'ele-admin-pro';
import OrderInfo from './components/orderInfo.vue';
import {ShopOrder, ShopOrderParam} from "@/api/shop/shopOrder/model";
import {pageShopOrder, repairOrder, removeShopOrder, removeBatchShopOrder, updateShopOrder} from "@/api/shop/shopOrder";
import {updateUser} from "@/api/system/user";
import {getPayType} from '@/utils/shop';
import {message, Modal} from 'ant-design-vue';
// 表格实例
const tableRef = ref<InstanceType<typeof EleProTable> | null>(null);
// 表格选中数据
const selection = ref<ShopOrder[]>([]);
// 当前编辑数据
const current = ref<ShopOrder | null>(null);
// 是否显示编辑弹窗
const showEdit = ref(false);
// 是否显示批量移动弹窗
const showMove = ref(false);
// 加载状态
const loading = ref(true);
// 激活的标签
const activeKey = ref<string>('all');
// 表格数据源
const datasource: DatasourceFunction = ({
page,
limit,
where,
orders,
filters
}) => {
if (filters) {
where.status = filters.status;
}
return pageShopOrder({
...where,
...orders,
page,
limit
});
};
// 表格列配置
const columns = ref<ColumnItem[]>([
{
title: '订单编号',
dataIndex: 'orderNo',
key: 'orderNo',
align: 'center',
width: 200,
},
{
title: '商品信息',
dataIndex: 'orderGoods',
key: 'orderGoods',
width: 360,
},
{
title: '实付金额',
dataIndex: 'payPrice',
key: 'payPrice',
align: 'center',
customRender: ({text}) => '¥' + text
},
{
title: '支付方式',
dataIndex: 'payType',
key: 'payType',
align: 'center'
},
{
title: '支付状态',
dataIndex: 'payStatus',
key: 'payStatus',
align: 'center'
},
{
title: '发货状态',
dataIndex: 'deliveryStatus',
key: 'deliveryStatus',
align: 'center',
},
{
title: '开票状态',
dataIndex: 'isInvoice',
key: 'isInvoice',
align: 'center',
},
{
title: '订单状态',
dataIndex: 'orderStatus',
key: 'orderStatus',
align: 'center',
},
// {
// title: '备注',
// dataIndex: 'comments',
// key: 'comments',
// align: 'center',
// },
// {
// title: '支付时间',
// dataIndex: 'payTime',
// key: 'payTime',
// align: 'center',
// width: 180,
// sorter: true,
// ellipsis: true
// },
{
title: '下单时间',
dataIndex: 'createTime',
key: 'createTime',
align: 'center',
width: 180,
sorter: true,
ellipsis: true,
customRender: ({text}) => toDateString(text)
},
{
title: '操作',
key: 'action',
width: 280,
fixed: 'right',
align: 'center',
hideInSetting: true
}
]);
/* 搜索 */
const reload = (where?: ShopOrderParam) => {
selection.value = [];
tableRef?.value?.reload({where: where});
};
const onTabs = () => {
// 使用statusFilter进行筛选这是后端专门为订单状态筛选设计的字段
const filterParams: Record<string, any> = {};
// 根据后端 statusFilter 的值对应:
// undefined全部0待付款1待发货2待核销3待收货4待评价5已完成6已退款7已删除
switch (activeKey.value) {
case 'all':
// 全部订单不传statusFilter参数
// filterParams.statusFilter = undefined; // 不设置该字段
break;
case 'unpaid':
// 待付款pay_status = false
filterParams.statusFilter = 0;
break;
case 'undelivered':
// 待发货pay_status = true AND delivery_status = 10
filterParams.statusFilter = 1;
break;
case 'unverified':
// 待核销pay_status = true AND delivery_status = 10 (与待发货相同)
filterParams.statusFilter = 2;
break;
case 'unreceived':
// 待收货pay_status = true AND delivery_status = 20
filterParams.statusFilter = 3;
break;
case 'unevaluated':
// 待评价order_status = 1 (与已完成相同)
filterParams.statusFilter = 4;
break;
case 'completed':
// 已完成order_status = 1
filterParams.statusFilter = 5;
break;
case 'cancelled':
// 已取消order_status = 2
filterParams.statusFilter = 8;
break;
case 'refunded':
// 已退款order_status = 6
filterParams.statusFilter = 6;
break;
case 'deleted':
// 已删除deleted = 1
filterParams.statusFilter = 7;
break;
}
reload(filterParams);
}
const onSearch = (item: ShopOrder) => {
reload({userId: item.userId})
}
/* 打开编辑弹窗 */
const openEdit = (row?: ShopOrder) => {
current.value = row ?? null;
showEdit.value = true;
};
/* 打开批量移动弹窗 */
const openMove = () => {
showMove.value = true;
};
/**
* 修复订单支付状态
*/
const updatePayStatus = (record: ShopOrder) => {
// 修复订单数据
repairOrder({
...record
}).then(() => {
if (!record.realName) {
// 更新用户真实姓名
updateUser({
userId: record.userId,
realName: record.realName
}).then(() => {
})
}
reload();
})
}
/* 查询 */
const query = () => {
loading.value = true;
};
/* 辅助判断函数 */
// 判断是否为取消状态
const isCancelledStatus = (orderStatus?: number) => {
return [2, 3].includes(orderStatus || 0);
};
// 判断是否为退款相关状态
const isRefundStatus = (orderStatus?: number) => {
return [4, 5, 6, 7].includes(orderStatus || 0);
};
// 判断是否可以删除订单
const canDeleteOrder = (order: ShopOrder) => {
// 已完成、已取消、退款成功的订单可以删除
return [1, 2, 6].includes(order.orderStatus || 0);
};
/* 订单操作方法 */
// 修改订单
const handleEditOrder = (record: ShopOrder) => {
message.info('订单修改功能开发中...');
// TODO: 实现订单修改功能
};
// 取消订单
const handleCancelOrder = (record: ShopOrder) => {
Modal.confirm({
title: '确认取消订单',
content: '确定要取消此订单吗?取消后无法恢复。',
onOk: async () => {
try {
await updateShopOrder({
...record,
orderStatus: 2 // 已取消
});
message.success('订单已取消');
reload();
} catch (error: any) {
message.error(error.message || '取消订单失败');
}
}
});
};
// 发货处理
const handleDelivery = (record: ShopOrder) => {
Modal.confirm({
title: '确认发货',
content: '确定要将此订单标记为已发货吗?',
onOk: async () => {
try {
const now = new Date();
const deliveryTime = toDateString(now, 'yyyy-MM-dd HH:mm:ss');
await updateShopOrder({
...record,
deliveryStatus: 20, // 已发货
deliveryTime: deliveryTime
});
message.success('发货成功');
reload();
} catch (error: any) {
message.error(error.message || '发货失败');
}
}
});
};
// 确认收货
const handleConfirmReceive = (record: ShopOrder) => {
Modal.confirm({
title: '确认收货',
content: '确定要将此订单标记为已收货并完成吗?',
onOk: async () => {
try {
await updateShopOrder({
...record,
deliveryStatus: 30, // 已收货
orderStatus: 1 // 已完成
});
message.success('确认收货成功');
reload();
} catch (error: any) {
message.error(error.message || '确认收货失败');
}
}
});
};
// 同意退款
const handleApproveRefund = (record: ShopOrder) => {
Modal.confirm({
title: '同意退款',
content: '确定要同意此订单的退款申请吗?',
onOk: async () => {
try {
const now = new Date();
const refundTime = toDateString(now, 'yyyy-MM-dd HH:mm:ss');
await updateShopOrder({
...record,
orderStatus: 6, // 退款成功
refundTime: refundTime
});
message.success('退款处理成功');
reload();
} catch (error: any) {
message.error(error.message || '退款处理失败');
}
}
});
};
// 拒绝退款
const handleRejectRefund = (record: ShopOrder) => {
Modal.confirm({
title: '拒绝退款',
content: '确定要拒绝此订单的退款申请吗?',
onOk: async () => {
try {
await updateShopOrder({
...record,
orderStatus: 5 // 退款被拒绝
});
message.success('已拒绝退款申请');
reload();
} catch (error: any) {
message.error(error.message || '操作失败');
}
}
});
};
// 重新处理退款
const handleRetryRefund = (record: ShopOrder) => {
Modal.confirm({
title: '重新处理退款',
content: '确定要重新处理此订单的退款吗?',
onOk: async () => {
try {
await updateShopOrder({
...record,
orderStatus: 4 // 退款申请中
});
message.success('已重新提交退款申请');
reload();
} catch (error: any) {
message.error(error.message || '操作失败');
}
}
});
};
// 申请退款
const handleApplyRefund = (record: ShopOrder) => {
Modal.confirm({
title: '申请退款',
content: '确定要为此订单申请退款吗?',
onOk: async () => {
try {
const now = new Date();
const refundApplyTime = toDateString(now, 'yyyy-MM-dd HH:mm:ss');
await updateShopOrder({
...record,
orderStatus: 4, // 退款申请中
refundApplyTime: refundApplyTime
});
message.success('退款申请已提交');
reload();
} catch (error: any) {
message.error(error.message || '申请退款失败');
}
}
});
};
/* 删除单个订单 */
const remove = (row: ShopOrder) => {
removeShopOrder(row.orderId)
.then(() => {
message.success('删除成功');
reload();
})
.catch((e) => {
message.error(e.message);
});
};
/* 批量删除订单 */
const removeBatch = () => {
if (!selection.value.length) {
message.error('请至少选择一条数据');
return;
}
Modal.confirm({
title: '提示',
content: '确定要删除选中的记录吗?',
icon: createVNode(ExclamationCircleOutlined),
maskClosable: true,
onOk: () => {
const ids = selection.value.map((d) => d.orderId);
removeBatchShopOrder(ids)
.then(() => {
message.success('删除成功');
reload();
})
.catch((e) => {
message.error(e.message);
});
}
});
};
/* 自定义行属性 */
const customRow = (record: ShopOrder) => {
return {
// 行点击事件
onClick: () => {
openEdit(record);
},
// 行双击事件
onDblclick: () => {
// openEdit(record);
}
};
};
query();
</script>
<script lang="ts">
import * as MenuIcons from '@/layout/menu-icons';
export default {
name: 'BszxOrder',
components: MenuIcons
};
</script>
<style lang="less" scoped></style>