feat:【mall 商城】交易订单(100% ele 部分)

This commit is contained in:
YunaiV
2025-10-15 21:17:05 +08:00
parent daf01c0da8
commit d9da7bd6fb
4 changed files with 90 additions and 215 deletions

View File

@@ -345,7 +345,7 @@ onMounted(async () => {
<div>
<OperateLogGrid table-title="操作日志">
<template #userType="{ row }">
<Tag v-if="row.userId === 0" color="default"> 系统 </Tag>
<Tag v-if="row.userType === 0" color="default"> 系统 </Tag>
<DictTag :type="DICT_TYPE.USER_TYPE" :value="row.userType" />
</template>
</OperateLogGrid>

View File

@@ -1,5 +1,6 @@
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { MallOrderApi } from '#/api/mall/trade/order';
import type { DescriptionItemSchema } from '#/components/description';
import { h } from 'vue';
@@ -9,7 +10,7 @@ import { fenToYuan, formatDateTime } from '@vben/utils';
import { DictTag } from '#/components/dict-tag';
/** 订单基础信息 schema */
export function useOrderInfoSchema() {
export function useOrderInfoSchema(): DescriptionItemSchema[] {
return [
{
field: 'no',
@@ -22,7 +23,7 @@ export function useOrderInfoSchema() {
{
field: 'type',
label: '订单类型',
render: (data: MallOrderApi.Order) =>
content: (data: MallOrderApi.Order) =>
h(DictTag, {
type: DICT_TYPE.TRADE_ORDER_TYPE,
value: data?.type,
@@ -31,7 +32,7 @@ export function useOrderInfoSchema() {
{
field: 'terminal',
label: '订单来源',
render: (data: MallOrderApi.Order) =>
content: (data: MallOrderApi.Order) =>
h(DictTag, {
type: DICT_TYPE.TERMINAL,
value: data?.terminal,
@@ -52,7 +53,7 @@ export function useOrderInfoSchema() {
{
field: 'payChannelCode',
label: '付款方式',
render: (data: MallOrderApi.Order) =>
content: (data: MallOrderApi.Order) =>
h(DictTag, {
type: DICT_TYPE.PAY_CHANNEL_CODE,
value: data?.payChannelCode,
@@ -66,12 +67,12 @@ export function useOrderInfoSchema() {
}
/** 订单状态信息 schema */
export function useOrderStatusSchema() {
export function useOrderStatusSchema(): DescriptionItemSchema[] {
return [
{
field: 'status',
label: '订单状态',
render: (data: MallOrderApi.Order) =>
content: (data: MallOrderApi.Order) =>
h(DictTag, {
type: DICT_TYPE.TRADE_ORDER_STATUS,
value: data?.status,
@@ -80,7 +81,7 @@ export function useOrderStatusSchema() {
{
field: 'reminder',
label: '提醒',
render: () =>
content: () =>
h('div', { class: 'space-y-1' }, [
h('div', '买家付款成功后,货款将直接进入您的商户号(微信、支付宝)'),
h('div', '请及时关注你发出的包裹状态,确保可以配送至买家手中'),
@@ -94,30 +95,30 @@ export function useOrderStatusSchema() {
}
/** 订单金额信息 schema */
export function useOrderPriceSchema() {
export function useOrderPriceSchema(): DescriptionItemSchema[] {
return [
{
field: 'totalPrice',
label: '商品总额',
render: (data: MallOrderApi.Order) =>
content: (data: MallOrderApi.Order) =>
`${fenToYuan(data?.totalPrice ?? 0)}`,
},
{
field: 'deliveryPrice',
label: '运费金额',
render: (data: MallOrderApi.Order) =>
content: (data: MallOrderApi.Order) =>
`${fenToYuan(data?.deliveryPrice ?? 0)}`,
},
{
field: 'adjustPrice',
label: '订单调价',
render: (data: MallOrderApi.Order) =>
content: (data: MallOrderApi.Order) =>
`${fenToYuan(data?.adjustPrice ?? 0)}`,
},
{
field: 'couponPrice',
label: '优惠劵优惠',
render: (data: MallOrderApi.Order) =>
content: (data: MallOrderApi.Order) =>
h(
'span',
{ class: 'text-red-500' },
@@ -127,7 +128,7 @@ export function useOrderPriceSchema() {
{
field: 'vipPrice',
label: 'VIP 优惠',
render: (data: MallOrderApi.Order) =>
content: (data: MallOrderApi.Order) =>
h(
'span',
{ class: 'text-red-500' },
@@ -137,7 +138,7 @@ export function useOrderPriceSchema() {
{
field: 'discountPrice',
label: '活动优惠',
render: (data: MallOrderApi.Order) =>
content: (data: MallOrderApi.Order) =>
h(
'span',
{ class: 'text-red-500' },
@@ -147,7 +148,7 @@ export function useOrderPriceSchema() {
{
field: 'pointPrice',
label: '积分抵扣',
render: (data: MallOrderApi.Order) =>
content: (data: MallOrderApi.Order) =>
h(
'span',
{ class: 'text-red-500' },
@@ -157,19 +158,19 @@ export function useOrderPriceSchema() {
{
field: 'payPrice',
label: '应付金额',
render: (data: MallOrderApi.Order) =>
content: (data: MallOrderApi.Order) =>
`${fenToYuan(data?.payPrice ?? 0)}`,
},
];
}
/** 收货信息 schema */
export function useDeliveryInfoSchema() {
export function useDeliveryInfoSchema(): DescriptionItemSchema[] {
return [
{
field: 'deliveryType',
label: '配送方式',
render: (data: MallOrderApi.Order) =>
content: (data: MallOrderApi.Order) =>
h(DictTag, {
type: DICT_TYPE.TRADE_DELIVERY_TYPE,
value: data?.deliveryType,
@@ -186,14 +187,14 @@ export function useDeliveryInfoSchema() {
{
field: 'receiverAddress',
label: '收货地址',
render: (data: MallOrderApi.Order) =>
content: (data: MallOrderApi.Order) =>
`${data?.receiverAreaName} ${data?.receiverDetailAddress}`.trim(),
},
{
field: 'deliveryTime',
label: '发货时间',
render: (data: MallOrderApi.Order) =>
formatDateTime(data?.deliveryTime || '') as string,
content: (data: MallOrderApi.Order) =>
formatDateTime(data?.deliveryTime) as string,
},
];
}
@@ -274,4 +275,4 @@ export function useOperateLogColumns(): VxeTableGridOptions['columns'] {
minWidth: 200,
},
];
}
}

View File

@@ -14,14 +14,14 @@ import {
TradeOrderStatusEnum,
} from '@vben/constants';
import { useTabs } from '@vben/hooks';
import { fenToYuan, formatDateTime } from '@vben/utils';
import { ElCard, ElMessage, ElTag } from 'element-plus';
import { ElCard, ElLoading, ElMessage, ElTag } from 'element-plus';
import { useVbenVxeGrid } from '#/adapter/vxe-table';
import * as DeliveryExpressApi from '#/api/mall/trade/delivery/express';
import * as DeliveryPickUpStoreApi from '#/api/mall/trade/delivery/pickUpStore';
import * as TradeOrderApi from '#/api/mall/trade/order';
import { useDescription } from '#/components/description';
import { DictTag } from '#/components/dict-tag';
import { TableAction } from '#/components/table-action';
@@ -30,8 +30,12 @@ import DeliveryForm from '../modules/delivery-form.vue';
import PriceForm from '../modules/price-form.vue';
import RemarkForm from '../modules/remark-form.vue';
import {
useDeliveryInfoSchema,
useExpressTrackColumns,
useOperateLogColumns,
useOrderInfoSchema,
useOrderPriceSchema,
useOrderStatusSchema,
useProductColumns,
} from './data';
@@ -52,6 +56,42 @@ const deliveryExpressList = ref<MallDeliveryExpressApi.SimpleDeliveryExpress[]>(
const expressTrackList = ref<any[]>([]);
const pickUpStore = ref<MallDeliveryPickUpStoreApi.PickUpStore | undefined>();
const [OrderInfoDescriptions] = useDescription({
componentProps: {
title: '订单信息',
border: false,
column: 3,
},
schema: useOrderInfoSchema(),
});
const [OrderStatusDescriptions] = useDescription({
componentProps: {
title: '订单状态',
border: false,
column: 1,
},
schema: useOrderStatusSchema(),
});
const [OrderPriceDescriptions] = useDescription({
componentProps: {
title: '费用信息',
border: false,
column: 4,
},
schema: useOrderPriceSchema(),
});
const [DeliveryInfoDescriptions] = useDescription({
componentProps: {
title: '收货信息',
border: false,
column: 3,
},
schema: useDeliveryInfoSchema(),
});
const [ProductGrid, productGridApi] = useVbenVxeGrid({
gridOptions: {
cellConfig: {
@@ -180,10 +220,8 @@ const handleUpdatePrice = () => {
/** 核销 */
const handlePickUp = async () => {
await confirm('确认核销订单吗?');
const loadingInstance = ElMessage({
message: '正在处理中...',
duration: 0,
type: 'info',
const loadingInstance = ElLoading.service({
text: '正在处理中...',
});
try {
await TradeOrderApi.pickUpOrder(order.value.id!);
@@ -265,75 +303,12 @@ onMounted(async () => {
<!-- 订单信息 -->
<ElCard class="mb-4">
<template #header>
<span class="font-bold">订单信息</span>
</template>
<div class="grid grid-cols-1 gap-4 md:grid-cols-3">
<div class="flex">
<span class="mr-2 min-w-fit text-gray-600">订单号:</span>
<span>{{ order.no }}</span>
</div>
<div class="flex">
<span class="mr-2 min-w-fit text-gray-600">买家:</span>
<span>{{ order.user?.nickname }}</span>
</div>
<div class="flex">
<span class="mr-2 min-w-fit text-gray-600">订单类型:</span>
<DictTag :type="DICT_TYPE.TRADE_ORDER_TYPE" :value="order.type" />
</div>
<div class="flex">
<span class="mr-2 min-w-fit text-gray-600">订单来源:</span>
<DictTag :type="DICT_TYPE.TERMINAL" :value="order.terminal" />
</div>
<div class="flex">
<span class="mr-2 min-w-fit text-gray-600">买家留言:</span>
<span>{{ order.userRemark }}</span>
</div>
<div class="flex">
<span class="mr-2 min-w-fit text-gray-600">商家备注:</span>
<span>{{ order.remark }}</span>
</div>
<div class="flex">
<span class="mr-2 min-w-fit text-gray-600">支付单号:</span>
<span>{{ order.payOrderId }}</span>
</div>
<div class="flex">
<span class="mr-2 min-w-fit text-gray-600">付款方式:</span>
<DictTag
:type="DICT_TYPE.PAY_CHANNEL_CODE"
:value="order.payChannelCode"
/>
</div>
<div class="flex">
<span class="mr-2 min-w-fit text-gray-600">推广用户:</span>
<span>{{ order.brokerageUser?.nickname }}</span>
</div>
</div>
<OrderInfoDescriptions :data="order" />
</ElCard>
<!-- 订单状态 -->
<ElCard class="mb-4">
<template #header>
<span class="font-bold">订单状态</span>
</template>
<div class="space-y-2">
<div class="flex">
<span class="mr-2 min-w-fit text-gray-600">订单状态:</span>
<DictTag :type="DICT_TYPE.TRADE_ORDER_STATUS" :value="order.status" />
</div>
<div class="flex">
<span class="mr-2 min-w-fit text-gray-600">提醒:</span>
<div class="space-y-1">
<div>买家付款成功后货款将直接进入您的商户号微信支付宝</div>
<div>请及时关注你发出的包裹状态确保可以配送至买家手中</div>
<div>
如果买家表示没收到货或货物有问题请及时联系买家处理友好协商
</div>
</div>
</div>
</div>
<OrderStatusDescriptions :data="order" />
</ElCard>
<!-- 商品信息 -->
<div class="mb-4">
<ProductGrid table-title="商品信息">
@@ -342,8 +317,8 @@ onMounted(async () => {
<span class="text-sm">{{ row.spuName }}</span>
<div class="flex flex-wrap gap-1">
<ElTag
v-for="(property, index) in row.properties"
:key="index"
v-for="property in row.properties"
:key="property.propertyId!"
size="small"
>
{{ property.propertyName }}: {{ property.valueName }}
@@ -353,91 +328,23 @@ onMounted(async () => {
</template>
</ProductGrid>
</div>
<!-- 费用信息 -->
<ElCard class="mb-4">
<template #header>
<span class="font-bold">费用信息</span>
</template>
<div class="grid grid-cols-1 gap-4 md:grid-cols-4">
<div class="flex">
<span class="mr-2 min-w-fit text-gray-600">商品总额:</span>
<span>{{ fenToYuan(order.totalPrice ?? 0) }} </span>
</div>
<div class="flex">
<span class="mr-2 min-w-fit text-gray-600">运费金额:</span>
<span>{{ fenToYuan(order.deliveryPrice ?? 0) }} </span>
</div>
<div class="flex">
<span class="mr-2 min-w-fit text-gray-600">订单调价:</span>
<span>{{ fenToYuan(order.adjustPrice ?? 0) }} </span>
</div>
<div class="flex">
<span class="mr-2 min-w-fit text-gray-600">优惠劵优惠:</span>
<span class="text-red-500">{{ fenToYuan(order.couponPrice ?? 0) }} </span>
</div>
<div class="flex">
<span class="mr-2 min-w-fit text-gray-600">VIP 优惠:</span>
<span class="text-red-500">{{ fenToYuan(order.vipPrice ?? 0) }} </span>
</div>
<div class="flex">
<span class="mr-2 min-w-fit text-gray-600">活动优惠:</span>
<span class="text-red-500">{{ fenToYuan(order.discountPrice ?? 0) }} </span>
</div>
<div class="flex">
<span class="mr-2 min-w-fit text-gray-600">积分抵扣:</span>
<span class="text-red-500">{{ fenToYuan(order.pointPrice ?? 0) }} </span>
</div>
<div class="flex">
<span class="mr-2 min-w-fit text-gray-600">应付金额:</span>
<span>{{ fenToYuan(order.payPrice ?? 0) }} </span>
</div>
</div>
<OrderPriceDescriptions :data="order" />
</ElCard>
<!-- 收货信息 -->
<ElCard class="mb-4">
<template #header>
<span class="font-bold">收货信息</span>
</template>
<div class="grid grid-cols-1 gap-4 md:grid-cols-3">
<div class="flex">
<span class="mr-2 min-w-fit text-gray-600">配送方式:</span>
<DictTag
:type="DICT_TYPE.TRADE_DELIVERY_TYPE"
:value="order.deliveryType"
/>
</div>
<div class="flex">
<span class="mr-2 min-w-fit text-gray-600">收货人:</span>
<span>{{ order.receiverName }}</span>
</div>
<div class="flex">
<span class="mr-2 min-w-fit text-gray-600">联系电话:</span>
<span>{{ order.receiverMobile }}</span>
</div>
<div class="flex md:col-span-3">
<span class="mr-2 min-w-fit text-gray-600">收货地址:</span>
<span>{{ order.receiverAreaName }}
{{ order.receiverDetailAddress }}</span>
</div>
<div class="flex">
<span class="mr-2 min-w-fit text-gray-600">发货时间:</span>
<span>{{ formatDateTime(order.deliveryTime || '') }}</span>
</div>
</div>
<DeliveryInfoDescriptions :data="order" />
</ElCard>
<!-- 物流详情 -->
<div v-if="expressTrackList.length > 0" class="mt-4">
<ExpressTrackGrid table-title="物流详情" />
</div>
<!-- 操作日志 -->
<div>
<OperateLogGrid table-title="操作日志">
<template #userType="{ row }">
<ElTag v-if="row.userId === 0" type="info"> 系统 </ElTag>
<ElTag v-if="row.userType === 0" type="info"> 系统 </ElTag>
<DictTag v-else :type="DICT_TYPE.USER_TYPE" :value="row.userType" />
</template>
</OperateLogGrid>

View File

@@ -108,27 +108,33 @@ const [Grid, gridApi] = useVbenVxeGrid({
<RemarkFormModal @success="handleRefresh" />
<Grid table-title="订单列表">
<template #expand_content="{ row }">
<div class="order-items">
<div v-for="item in row.items" :key="item.id!" class="order-item">
<div class="order-item-image">
<div class="py-2">
<div
v-for="item in row.items"
:key="item.id!"
class="flex items-start border-b border-gray-100 py-2 last:border-b-0"
>
<div class="mr-3 flex-shrink-0">
<ElImage :src="item.picUrl" class="h-10 w-10" />
</div>
<div class="order-item-content">
<div class="order-item-name">
<div class="flex-1">
<div class="mb-1 font-medium">
{{ item.spuName }}
<ElTag
v-for="property in item.properties"
:key="property.id!"
:key="property.id"
class="ml-1"
size="small"
>
{{ property.propertyName }}: {{ property.valueName }}
</ElTag>
</div>
<div class="order-item-info">
<div
class="flex items-center justify-between text-xs text-gray-500"
>
<span>
原价{{ fenToYuan(item.price || 0) }} / 数量{{
item.count || 0
原价{{ fenToYuan(item.price) }} / 数量{{
item.count
}}
</span>
<DictTag
@@ -174,42 +180,3 @@ const [Grid, gridApi] = useVbenVxeGrid({
</Grid>
</Page>
</template>
<style lang="scss" scoped>
.order-items {
padding: 8px 0;
}
.order-item {
display: flex;
align-items: flex-start;
padding: 8px 0;
border-bottom: 1px solid #f0f0f0;
&:last-child {
border-bottom: none;
}
}
.order-item-image {
flex-shrink: 0;
margin-right: 12px;
}
.order-item-content {
flex: 1;
}
.order-item-name {
margin-bottom: 4px;
font-weight: 500;
}
.order-item-info {
display: flex;
align-items: center;
justify-content: space-between;
font-size: 13px;
color: #666;
}
</style>