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> <div>
<OperateLogGrid table-title="操作日志"> <OperateLogGrid table-title="操作日志">
<template #userType="{ row }"> <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" /> <DictTag :type="DICT_TYPE.USER_TYPE" :value="row.userType" />
</template> </template>
</OperateLogGrid> </OperateLogGrid>

View File

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

View File

@@ -14,14 +14,14 @@ import {
TradeOrderStatusEnum, TradeOrderStatusEnum,
} from '@vben/constants'; } from '@vben/constants';
import { useTabs } from '@vben/hooks'; 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 { useVbenVxeGrid } from '#/adapter/vxe-table';
import * as DeliveryExpressApi from '#/api/mall/trade/delivery/express'; import * as DeliveryExpressApi from '#/api/mall/trade/delivery/express';
import * as DeliveryPickUpStoreApi from '#/api/mall/trade/delivery/pickUpStore'; import * as DeliveryPickUpStoreApi from '#/api/mall/trade/delivery/pickUpStore';
import * as TradeOrderApi from '#/api/mall/trade/order'; import * as TradeOrderApi from '#/api/mall/trade/order';
import { useDescription } from '#/components/description';
import { DictTag } from '#/components/dict-tag'; import { DictTag } from '#/components/dict-tag';
import { TableAction } from '#/components/table-action'; 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 PriceForm from '../modules/price-form.vue';
import RemarkForm from '../modules/remark-form.vue'; import RemarkForm from '../modules/remark-form.vue';
import { import {
useDeliveryInfoSchema,
useExpressTrackColumns, useExpressTrackColumns,
useOperateLogColumns, useOperateLogColumns,
useOrderInfoSchema,
useOrderPriceSchema,
useOrderStatusSchema,
useProductColumns, useProductColumns,
} from './data'; } from './data';
@@ -52,6 +56,42 @@ const deliveryExpressList = ref<MallDeliveryExpressApi.SimpleDeliveryExpress[]>(
const expressTrackList = ref<any[]>([]); const expressTrackList = ref<any[]>([]);
const pickUpStore = ref<MallDeliveryPickUpStoreApi.PickUpStore | undefined>(); 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({ const [ProductGrid, productGridApi] = useVbenVxeGrid({
gridOptions: { gridOptions: {
cellConfig: { cellConfig: {
@@ -180,10 +220,8 @@ const handleUpdatePrice = () => {
/** 核销 */ /** 核销 */
const handlePickUp = async () => { const handlePickUp = async () => {
await confirm('确认核销订单吗?'); await confirm('确认核销订单吗?');
const loadingInstance = ElMessage({ const loadingInstance = ElLoading.service({
message: '正在处理中...', text: '正在处理中...',
duration: 0,
type: 'info',
}); });
try { try {
await TradeOrderApi.pickUpOrder(order.value.id!); await TradeOrderApi.pickUpOrder(order.value.id!);
@@ -265,75 +303,12 @@ onMounted(async () => {
<!-- 订单信息 --> <!-- 订单信息 -->
<ElCard class="mb-4"> <ElCard class="mb-4">
<template #header> <OrderInfoDescriptions :data="order" />
<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>
</ElCard> </ElCard>
<!-- 订单状态 --> <!-- 订单状态 -->
<ElCard class="mb-4"> <ElCard class="mb-4">
<template #header> <OrderStatusDescriptions :data="order" />
<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>
</ElCard> </ElCard>
<!-- 商品信息 --> <!-- 商品信息 -->
<div class="mb-4"> <div class="mb-4">
<ProductGrid table-title="商品信息"> <ProductGrid table-title="商品信息">
@@ -342,8 +317,8 @@ onMounted(async () => {
<span class="text-sm">{{ row.spuName }}</span> <span class="text-sm">{{ row.spuName }}</span>
<div class="flex flex-wrap gap-1"> <div class="flex flex-wrap gap-1">
<ElTag <ElTag
v-for="(property, index) in row.properties" v-for="property in row.properties"
:key="index" :key="property.propertyId!"
size="small" size="small"
> >
{{ property.propertyName }}: {{ property.valueName }} {{ property.propertyName }}: {{ property.valueName }}
@@ -353,91 +328,23 @@ onMounted(async () => {
</template> </template>
</ProductGrid> </ProductGrid>
</div> </div>
<!-- 费用信息 --> <!-- 费用信息 -->
<ElCard class="mb-4"> <ElCard class="mb-4">
<template #header> <OrderPriceDescriptions :data="order" />
<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>
</ElCard> </ElCard>
<!-- 收货信息 --> <!-- 收货信息 -->
<ElCard class="mb-4"> <ElCard class="mb-4">
<template #header> <DeliveryInfoDescriptions :data="order" />
<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>
</ElCard> </ElCard>
<!-- 物流详情 --> <!-- 物流详情 -->
<div v-if="expressTrackList.length > 0" class="mt-4"> <div v-if="expressTrackList.length > 0" class="mt-4">
<ExpressTrackGrid table-title="物流详情" /> <ExpressTrackGrid table-title="物流详情" />
</div> </div>
<!-- 操作日志 --> <!-- 操作日志 -->
<div> <div>
<OperateLogGrid table-title="操作日志"> <OperateLogGrid table-title="操作日志">
<template #userType="{ row }"> <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" /> <DictTag v-else :type="DICT_TYPE.USER_TYPE" :value="row.userType" />
</template> </template>
</OperateLogGrid> </OperateLogGrid>

View File

@@ -108,27 +108,33 @@ const [Grid, gridApi] = useVbenVxeGrid({
<RemarkFormModal @success="handleRefresh" /> <RemarkFormModal @success="handleRefresh" />
<Grid table-title="订单列表"> <Grid table-title="订单列表">
<template #expand_content="{ row }"> <template #expand_content="{ row }">
<div class="order-items"> <div class="py-2">
<div v-for="item in row.items" :key="item.id!" class="order-item"> <div
<div class="order-item-image"> 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" /> <ElImage :src="item.picUrl" class="h-10 w-10" />
</div> </div>
<div class="order-item-content"> <div class="flex-1">
<div class="order-item-name"> <div class="mb-1 font-medium">
{{ item.spuName }} {{ item.spuName }}
<ElTag <ElTag
v-for="property in item.properties" v-for="property in item.properties"
:key="property.id!" :key="property.id"
class="ml-1" class="ml-1"
size="small" size="small"
> >
{{ property.propertyName }}: {{ property.valueName }} {{ property.propertyName }}: {{ property.valueName }}
</ElTag> </ElTag>
</div> </div>
<div class="order-item-info"> <div
class="flex items-center justify-between text-xs text-gray-500"
>
<span> <span>
原价{{ fenToYuan(item.price || 0) }} / 数量{{ 原价{{ fenToYuan(item.price) }} / 数量{{
item.count || 0 item.count
}} }}
</span> </span>
<DictTag <DictTag
@@ -174,42 +180,3 @@ const [Grid, gridApi] = useVbenVxeGrid({
</Grid> </Grid>
</Page> </Page>
</template> </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>