feat:【mall 商城】交易订单(10% antd detail 初始化)
This commit is contained in:
@@ -60,6 +60,8 @@ export namespace MallOrderApi {
|
||||
createTime?: Date;
|
||||
/** 用户类型 */
|
||||
userType?: number;
|
||||
/** 用户编号 */
|
||||
userId?: number;
|
||||
}
|
||||
|
||||
/** 订单 */
|
||||
|
||||
@@ -57,7 +57,7 @@ const routes: RouteRecordRaw[] = [
|
||||
title: '订单详情',
|
||||
activePath: '/mall/trade/order',
|
||||
},
|
||||
component: () => import('#/views/mall/trade/order/modules/detail.vue'),
|
||||
component: () => import('#/views/mall/trade/order/detail/index.vue'),
|
||||
},
|
||||
{
|
||||
path: String.raw`after-sale/detail/:id(\d+)`,
|
||||
|
||||
239
apps/web-antd/src/views/mall/trade/order/detail/data.ts
Normal file
239
apps/web-antd/src/views/mall/trade/order/detail/data.ts
Normal file
@@ -0,0 +1,239 @@
|
||||
import type { MallOrderApi } from '#/api/mall/trade/order';
|
||||
import type { DescriptionItemSchema } from '#/components/description';
|
||||
import type { VxeGridPropTypes } from 'vxe-table';
|
||||
|
||||
import { h } from 'vue';
|
||||
|
||||
import { DICT_TYPE } from '@vben/constants';
|
||||
import { fenToYuan, formatDate } from '@vben/utils';
|
||||
|
||||
import { DictTag } from '#/components/dict-tag';
|
||||
|
||||
/** 订单基础信息 schema */
|
||||
export function useOrderInfoSchema(): DescriptionItemSchema[] {
|
||||
return [
|
||||
{
|
||||
field: 'no',
|
||||
label: '订单号',
|
||||
},
|
||||
{
|
||||
field: 'user.nickname',
|
||||
label: '买家',
|
||||
},
|
||||
{
|
||||
field: 'type',
|
||||
label: '订单类型',
|
||||
content: (data: MallOrderApi.Order) =>
|
||||
h(DictTag, {
|
||||
type: DICT_TYPE.TRADE_ORDER_TYPE,
|
||||
value: data?.type,
|
||||
}),
|
||||
},
|
||||
{
|
||||
field: 'terminal',
|
||||
label: '订单来源',
|
||||
content: (data: MallOrderApi.Order) =>
|
||||
h(DictTag, {
|
||||
type: DICT_TYPE.TERMINAL,
|
||||
value: data?.terminal,
|
||||
}),
|
||||
},
|
||||
{
|
||||
field: 'userRemark',
|
||||
label: '买家留言',
|
||||
},
|
||||
{
|
||||
field: 'remark',
|
||||
label: '商家备注',
|
||||
},
|
||||
{
|
||||
field: 'payOrderId',
|
||||
label: '支付单号',
|
||||
},
|
||||
{
|
||||
field: 'payChannelCode',
|
||||
label: '付款方式',
|
||||
content: (data: MallOrderApi.Order) =>
|
||||
h(DictTag, {
|
||||
type: DICT_TYPE.PAY_CHANNEL_CODE,
|
||||
value: data?.payChannelCode,
|
||||
}),
|
||||
},
|
||||
{
|
||||
field: 'brokerageUser.nickname',
|
||||
label: '推广用户',
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** 订单状态信息 schema */
|
||||
export function useOrderStatusSchema(): DescriptionItemSchema[] {
|
||||
return [
|
||||
{
|
||||
field: 'status',
|
||||
label: '订单状态',
|
||||
content: (data: MallOrderApi.Order) =>
|
||||
h(DictTag, {
|
||||
type: DICT_TYPE.TRADE_ORDER_STATUS,
|
||||
value: data?.status,
|
||||
}),
|
||||
},
|
||||
{
|
||||
field: 'reminder',
|
||||
label: '提醒',
|
||||
content: () =>
|
||||
h('div', { class: 'text-red-500 space-y-1' }, [
|
||||
h('div', '买家付款成功后,货款将直接进入您的商户号(微信、支付宝)'),
|
||||
h('div', '请及时关注你发出的包裹状态,确保可以配送至买家手中'),
|
||||
h('div', '如果买家表示没收到货或货物有问题,请及时联系买家处理,友好协商'),
|
||||
]),
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** 订单金额信息 schema */
|
||||
export function useOrderPriceSchema(): DescriptionItemSchema[] {
|
||||
return [
|
||||
{
|
||||
field: 'totalPrice',
|
||||
label: '商品总额',
|
||||
content: (data: MallOrderApi.Order) => `${fenToYuan(data?.totalPrice ?? 0)} 元`,
|
||||
},
|
||||
{
|
||||
field: 'deliveryPrice',
|
||||
label: '运费金额',
|
||||
content: (data: MallOrderApi.Order) => `${fenToYuan(data?.deliveryPrice ?? 0)} 元`,
|
||||
},
|
||||
{
|
||||
field: 'adjustPrice',
|
||||
label: '订单调价',
|
||||
content: (data: MallOrderApi.Order) => `${fenToYuan(data?.adjustPrice ?? 0)} 元`,
|
||||
},
|
||||
{
|
||||
field: 'couponPrice',
|
||||
label: '优惠劵优惠',
|
||||
content: (data: MallOrderApi.Order) =>
|
||||
h('span', { class: 'text-red-500' }, `${fenToYuan(data?.couponPrice ?? 0)} 元`),
|
||||
},
|
||||
{
|
||||
field: 'vipPrice',
|
||||
label: 'VIP 优惠',
|
||||
content: (data: MallOrderApi.Order) =>
|
||||
h('span', { class: 'text-red-500' }, `${fenToYuan(data?.vipPrice ?? 0)} 元`),
|
||||
},
|
||||
{
|
||||
field: 'discountPrice',
|
||||
label: '活动优惠',
|
||||
content: (data: MallOrderApi.Order) =>
|
||||
h('span', { class: 'text-red-500' }, `${fenToYuan(data?.discountPrice ?? 0)} 元`),
|
||||
},
|
||||
{
|
||||
field: 'pointPrice',
|
||||
label: '积分抵扣',
|
||||
content: (data: MallOrderApi.Order) =>
|
||||
h('span', { class: 'text-red-500' }, `${fenToYuan(data?.pointPrice ?? 0)} 元`),
|
||||
},
|
||||
{
|
||||
field: 'payPrice',
|
||||
label: '应付金额',
|
||||
content: (data: MallOrderApi.Order) => `${fenToYuan(data?.payPrice ?? 0)} 元`,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** 收货信息 schema */
|
||||
export function useDeliveryInfoSchema(): DescriptionItemSchema[] {
|
||||
return [
|
||||
{
|
||||
field: 'deliveryType',
|
||||
label: '配送方式',
|
||||
content: (data: MallOrderApi.Order) =>
|
||||
h(DictTag, {
|
||||
type: DICT_TYPE.TRADE_DELIVERY_TYPE,
|
||||
value: data?.deliveryType,
|
||||
}),
|
||||
},
|
||||
{
|
||||
field: 'receiverName',
|
||||
label: '收货人',
|
||||
},
|
||||
{
|
||||
field: 'receiverMobile',
|
||||
label: '联系电话',
|
||||
},
|
||||
{
|
||||
field: 'receiverAddress',
|
||||
label: '收货地址',
|
||||
content: (data: MallOrderApi.Order) =>
|
||||
`${data?.receiverAreaName} ${data?.receiverDetailAddress}`.trim(),
|
||||
},
|
||||
{
|
||||
field: 'logisticsTime',
|
||||
label: '发货时间',
|
||||
content: (data: MallOrderApi.Order) => formatDate(data?.deliveryTime) as string,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** 商品信息 columns */
|
||||
export function useProductColumns(): VxeGridPropTypes.Columns {
|
||||
return [
|
||||
{
|
||||
type: 'seq',
|
||||
width: 60,
|
||||
title: '序号',
|
||||
},
|
||||
{
|
||||
field: 'spuName',
|
||||
title: '商品',
|
||||
minWidth: 300,
|
||||
slots: { default: 'spuName' },
|
||||
},
|
||||
{
|
||||
field: 'price',
|
||||
title: '商品原价',
|
||||
width: 150,
|
||||
formatter: ({ cellValue }: any) => `${fenToYuan(cellValue)} 元`,
|
||||
},
|
||||
{
|
||||
field: 'count',
|
||||
title: '数量',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
field: 'payPrice',
|
||||
title: '合计',
|
||||
width: 150,
|
||||
formatter: ({ cellValue }: any) => `${fenToYuan(cellValue)} 元`,
|
||||
},
|
||||
{
|
||||
field: 'afterSaleStatus',
|
||||
title: '售后状态',
|
||||
width: 120,
|
||||
slots: { default: 'afterSaleStatus' },
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** 操作日志 columns */
|
||||
export function useOperateLogColumns(): VxeGridPropTypes.Columns {
|
||||
return [
|
||||
{
|
||||
field: 'createTime',
|
||||
title: '操作时间',
|
||||
width: 180,
|
||||
formatter: ({ cellValue }: any) => formatDate(cellValue) as string,
|
||||
},
|
||||
{
|
||||
field: 'userType',
|
||||
title: '操作人',
|
||||
width: 100,
|
||||
slots: { default: 'userType' },
|
||||
},
|
||||
{
|
||||
field: 'content',
|
||||
title: '操作内容',
|
||||
minWidth: 200,
|
||||
},
|
||||
];
|
||||
}
|
||||
414
apps/web-antd/src/views/mall/trade/order/detail/index.vue
Normal file
414
apps/web-antd/src/views/mall/trade/order/detail/index.vue
Normal file
@@ -0,0 +1,414 @@
|
||||
<script lang="ts" setup>
|
||||
import type { MallDeliveryExpressApi } from '#/api/mall/trade/delivery/express';
|
||||
import type { MallDeliveryPickUpStoreApi } from '#/api/mall/trade/delivery/pickUpStore';
|
||||
import type { MallOrderApi } from '#/api/mall/trade/order';
|
||||
|
||||
import { onMounted, ref } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
|
||||
import { confirm, Page, useVbenModal } from '@vben/common-ui';
|
||||
import {
|
||||
DeliveryTypeEnum,
|
||||
DICT_TYPE,
|
||||
TradeOrderStatusEnum,
|
||||
} from '@vben/constants';
|
||||
import { getDictLabel, getDictObj } from '@vben/hooks';
|
||||
import { formatDate } from '@vben/utils';
|
||||
import { useTabs } from '@vben/hooks';
|
||||
|
||||
import { Card, Tag, Timeline } from 'ant-design-vue';
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
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';
|
||||
|
||||
import DeliveryForm from '../modules/delevery-form.vue';
|
||||
import OrderUpdateAddressForm from '../modules/update-address-form.vue';
|
||||
import OrderUpdatePriceForm from '../modules/update-price-form.vue';
|
||||
import OrderUpdateRemarkForm from '../modules/update-remark-form.vue';
|
||||
import {
|
||||
useDeliveryInfoSchema,
|
||||
useOperateLogColumns,
|
||||
useOrderInfoSchema,
|
||||
useOrderPriceSchema,
|
||||
useOrderStatusSchema,
|
||||
useProductColumns,
|
||||
} from './data';
|
||||
|
||||
defineOptions({ name: 'TradeOrderDetail' });
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const tabs = useTabs();
|
||||
|
||||
// 查询参数
|
||||
const props = defineProps({
|
||||
id: {
|
||||
type: Number,
|
||||
default: undefined,
|
||||
}, // 订单ID
|
||||
showPickUp: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
}, // 显示核销按钮
|
||||
});
|
||||
|
||||
const loading = ref(false);
|
||||
const orderId = ref(0);
|
||||
const formData = ref<MallOrderApi.Order>({
|
||||
logs: [],
|
||||
});
|
||||
|
||||
const deliveryExpressList = ref<MallDeliveryExpressApi.SimpleDeliveryExpress[]>([]);
|
||||
const expressTrackList = ref<any[]>([]);
|
||||
const pickUpStore = ref<MallDeliveryPickUpStoreApi.PickUpStore>(
|
||||
{} as MallDeliveryPickUpStoreApi.PickUpStore,
|
||||
);
|
||||
|
||||
// Description 组件
|
||||
const [OrderInfoDescriptions] = useDescription({
|
||||
componentProps: {
|
||||
title: '订单信息',
|
||||
bordered: false,
|
||||
column: 3,
|
||||
},
|
||||
schema: useOrderInfoSchema(),
|
||||
});
|
||||
|
||||
const [OrderStatusDescriptions] = useDescription({
|
||||
componentProps: {
|
||||
title: '订单状态',
|
||||
bordered: false,
|
||||
column: 2,
|
||||
},
|
||||
schema: useOrderStatusSchema(),
|
||||
});
|
||||
|
||||
const [OrderPriceDescriptions] = useDescription({
|
||||
componentProps: {
|
||||
title: '费用信息',
|
||||
bordered: false,
|
||||
column: 4,
|
||||
},
|
||||
schema: useOrderPriceSchema(),
|
||||
});
|
||||
|
||||
const [DeliveryInfoDescriptions] = useDescription({
|
||||
componentProps: {
|
||||
title: '收货信息',
|
||||
bordered: false,
|
||||
column: 3,
|
||||
},
|
||||
schema: useDeliveryInfoSchema(),
|
||||
});
|
||||
|
||||
// Grid 组件
|
||||
const [ProductGrid, productGridApi] = useVbenVxeGrid({
|
||||
gridOptions: {
|
||||
cellConfig: {
|
||||
height: 60,
|
||||
},
|
||||
columns: useProductColumns(),
|
||||
data: [],
|
||||
height: 'auto',
|
||||
border: true,
|
||||
pagerConfig: {
|
||||
enabled: false,
|
||||
},
|
||||
toolbarConfig: {
|
||||
refresh: true,
|
||||
search: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const [OperateLogGrid, operateLogGridApi] = useVbenVxeGrid({
|
||||
gridOptions: {
|
||||
columns: useOperateLogColumns(),
|
||||
data: [],
|
||||
border: true,
|
||||
pagerConfig: {
|
||||
enabled: false,
|
||||
},
|
||||
toolbarConfig: {
|
||||
refresh: true,
|
||||
search: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Modal 组件
|
||||
const [DeliveryFormModal, deliveryFormModalApi] = useVbenModal({
|
||||
connectedComponent: DeliveryForm,
|
||||
destroyOnClose: true,
|
||||
});
|
||||
|
||||
const [OrderUpdateRemarkFormModal, remarkFormModalApi] = useVbenModal({
|
||||
connectedComponent: OrderUpdateRemarkForm,
|
||||
destroyOnClose: true,
|
||||
});
|
||||
|
||||
const [OrderUpdateAddressFormModal, addressFormModalApi] = useVbenModal({
|
||||
connectedComponent: OrderUpdateAddressForm,
|
||||
destroyOnClose: true,
|
||||
});
|
||||
|
||||
const [OrderUpdatePriceFormModal, priceFormModalApi] = useVbenModal({
|
||||
connectedComponent: OrderUpdatePriceForm,
|
||||
destroyOnClose: true,
|
||||
});
|
||||
|
||||
/** 获得 userType 颜色 */
|
||||
const getUserTypeColor = (type: number) => {
|
||||
const dict = getDictObj(DICT_TYPE.USER_TYPE, type);
|
||||
switch (dict?.colorType) {
|
||||
case 'danger': {
|
||||
return '#F56C6C';
|
||||
}
|
||||
case 'info': {
|
||||
return '#909399';
|
||||
}
|
||||
case 'success': {
|
||||
return '#67C23A';
|
||||
}
|
||||
case 'warning': {
|
||||
return '#E6A23C';
|
||||
}
|
||||
}
|
||||
return '#409EFF';
|
||||
};
|
||||
|
||||
/** 各种操作 */
|
||||
const handleRemark = () => {
|
||||
remarkFormModalApi.setData(formData.value).open();
|
||||
};
|
||||
|
||||
const handleDelivery = () => {
|
||||
deliveryFormModalApi.setData(formData.value).open();
|
||||
};
|
||||
|
||||
const handleUpdateAddress = () => {
|
||||
addressFormModalApi.setData(formData.value).open();
|
||||
};
|
||||
|
||||
const handleUpdatePrice = () => {
|
||||
priceFormModalApi.setData(formData.value).open();
|
||||
};
|
||||
|
||||
/** 核销 */
|
||||
const handlePickUp = async () => {
|
||||
await confirm('确认核销订单吗?');
|
||||
const hideLoading = message.loading({
|
||||
content: '正在处理中...',
|
||||
duration: 0,
|
||||
});
|
||||
try {
|
||||
await TradeOrderApi.pickUpOrder(formData.value.id!);
|
||||
message.success('核销成功');
|
||||
await getDetail();
|
||||
} finally {
|
||||
hideLoading();
|
||||
}
|
||||
};
|
||||
|
||||
/** 获得详情 */
|
||||
const getDetail = async () => {
|
||||
loading.value = true;
|
||||
try {
|
||||
const res = await TradeOrderApi.getOrder(orderId.value);
|
||||
if (!res) {
|
||||
message.error('交易订单不存在');
|
||||
handleBack();
|
||||
return;
|
||||
}
|
||||
formData.value = res;
|
||||
productGridApi.setGridOptions({ data: res.items || [] });
|
||||
operateLogGridApi.setGridOptions({ data: res.logs || [] });
|
||||
|
||||
// 如果配送方式为快递,则查询物流公司
|
||||
if (res.deliveryType === DeliveryTypeEnum.EXPRESS.type) {
|
||||
deliveryExpressList.value = await DeliveryExpressApi.getSimpleDeliveryExpressList();
|
||||
if (res.logisticsId) {
|
||||
expressTrackList.value = await TradeOrderApi.getExpressTrackList(res.id!);
|
||||
}
|
||||
} else if (
|
||||
res.deliveryType === DeliveryTypeEnum.PICK_UP.type &&
|
||||
res.pickUpStoreId
|
||||
) {
|
||||
pickUpStore.value = await DeliveryPickUpStoreApi.getDeliveryPickUpStore(
|
||||
res.pickUpStoreId,
|
||||
);
|
||||
}
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
/** 返回列表页 */
|
||||
function handleBack() {
|
||||
tabs.closeCurrentTab();
|
||||
router.push('/mall/trade/order');
|
||||
}
|
||||
|
||||
|
||||
/** 初始化 */
|
||||
onMounted(async () => {
|
||||
orderId.value = Number(route.params.id) || props.id || 0;
|
||||
if (orderId.value) {
|
||||
await getDetail();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page auto-content-height :title="formData.no" :loading="loading">
|
||||
<template #extra>
|
||||
<TableAction
|
||||
:actions="[
|
||||
{
|
||||
label: '返回',
|
||||
type: 'default',
|
||||
icon: 'lucide:arrow-left',
|
||||
onClick: handleBack,
|
||||
},
|
||||
{
|
||||
label: '调整价格',
|
||||
type: 'primary',
|
||||
onClick: handleUpdatePrice,
|
||||
ifShow: formData.status === TradeOrderStatusEnum.UNPAID.status,
|
||||
},
|
||||
{
|
||||
label: '备注',
|
||||
type: 'primary',
|
||||
onClick: handleRemark,
|
||||
},
|
||||
{
|
||||
label: '发货',
|
||||
type: 'primary',
|
||||
onClick: handleDelivery,
|
||||
ifShow:
|
||||
formData.status === TradeOrderStatusEnum.UNDELIVERED.status &&
|
||||
formData.deliveryType === DeliveryTypeEnum.EXPRESS.type,
|
||||
},
|
||||
{
|
||||
label: '修改地址',
|
||||
type: 'primary',
|
||||
onClick: handleUpdateAddress,
|
||||
ifShow:
|
||||
formData.status === TradeOrderStatusEnum.UNDELIVERED.status &&
|
||||
formData.deliveryType === DeliveryTypeEnum.EXPRESS.type,
|
||||
},
|
||||
{
|
||||
label: '核销',
|
||||
type: 'primary',
|
||||
onClick: handlePickUp,
|
||||
ifShow:
|
||||
formData.status === TradeOrderStatusEnum.UNDELIVERED.status &&
|
||||
formData.deliveryType === DeliveryTypeEnum.PICK_UP.type &&
|
||||
showPickUp,
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<!-- 各种操作的弹窗 -->
|
||||
<DeliveryFormModal @success="getDetail" />
|
||||
<OrderUpdateRemarkFormModal @success="getDetail" />
|
||||
<OrderUpdateAddressFormModal @success="getDetail" />
|
||||
<OrderUpdatePriceFormModal @success="getDetail" />
|
||||
|
||||
<!-- 订单信息 -->
|
||||
<Card class="mb-4">
|
||||
<OrderInfoDescriptions :data="formData" />
|
||||
</Card>
|
||||
|
||||
<!-- 订单状态 -->
|
||||
<Card class="mb-4">
|
||||
<OrderStatusDescriptions :data="formData" />
|
||||
</Card>
|
||||
|
||||
<!-- 商品信息 -->
|
||||
<div class="mb-4">
|
||||
<ProductGrid table-title="商品信息">
|
||||
<template #spuName="{ row }">
|
||||
<div class="flex flex-1 flex-col items-start gap-1 text-left">
|
||||
<span class="text-sm">{{ row.spuName }}</span>
|
||||
<div class="flex flex-wrap gap-1">
|
||||
<Tag
|
||||
v-for="property in row.properties"
|
||||
:key="property.propertyId!"
|
||||
size="small"
|
||||
>
|
||||
{{ property.propertyName }}: {{ property.valueName }}
|
||||
</Tag>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template #afterSaleStatus="{ row }">
|
||||
<DictTag
|
||||
:type="DICT_TYPE.TRADE_ORDER_ITEM_AFTER_SALE_STATUS"
|
||||
:value="row.afterSaleStatus"
|
||||
/>
|
||||
</template>
|
||||
</ProductGrid>
|
||||
</div>
|
||||
|
||||
<!-- 费用信息 -->
|
||||
<Card class="mb-4">
|
||||
<OrderPriceDescriptions :data="formData" />
|
||||
</Card>
|
||||
|
||||
<!-- 收货信息 -->
|
||||
<Card class="mb-4">
|
||||
<DeliveryInfoDescriptions :data="formData" />
|
||||
<!-- 物流详情 -->
|
||||
<div v-if="expressTrackList.length > 0" class="mt-4">
|
||||
<h4 class="mb-2 font-semibold">物流详情</h4>
|
||||
<Timeline>
|
||||
<Timeline.Item
|
||||
v-for="(track, index) in expressTrackList"
|
||||
:key="index"
|
||||
>
|
||||
<template #dot>
|
||||
<div class="h-2 w-2 rounded-full bg-blue-500"></div>
|
||||
</template>
|
||||
<div>
|
||||
<div class="text-sm text-gray-600">
|
||||
{{ formatDate(track.time) }}
|
||||
</div>
|
||||
<div class="text-sm">{{ track.content }}</div>
|
||||
</div>
|
||||
</Timeline.Item>
|
||||
</Timeline>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<!-- 操作日志 -->
|
||||
<div>
|
||||
<OperateLogGrid table-title="操作日志">
|
||||
<template #userType="{ row }">
|
||||
<Tag
|
||||
v-if="row.userId === 0"
|
||||
color="default"
|
||||
>
|
||||
系统
|
||||
</Tag>
|
||||
<div v-else class="flex items-center gap-2">
|
||||
<span
|
||||
:style="{ backgroundColor: getUserTypeColor(row.userType!) }"
|
||||
class="inline-flex h-5 w-5 items-center justify-center rounded-full text-xs text-white"
|
||||
>
|
||||
{{ getDictLabel(DICT_TYPE.USER_TYPE, row.userType)[0] }}
|
||||
</span>
|
||||
<DictTag :type="DICT_TYPE.USER_TYPE" :value="row.userType" />
|
||||
</div>
|
||||
</template>
|
||||
</OperateLogGrid>
|
||||
</div>
|
||||
</Page>
|
||||
</template>
|
||||
@@ -23,16 +23,18 @@ import { $t } from '#/locales';
|
||||
import { useGridColumns, useGridFormSchema } from './data';
|
||||
import DeleveryForm from './modules/delevery-form.vue';
|
||||
|
||||
const { push } = useRouter();
|
||||
|
||||
const [DeleveryFormModal, deleveryFormModalApi] = useVbenModal({
|
||||
connectedComponent: DeleveryForm,
|
||||
destroyOnClose: true,
|
||||
});
|
||||
|
||||
/** 刷新表格 */
|
||||
function onRefresh() {
|
||||
function handleRefresh() {
|
||||
gridApi.query();
|
||||
}
|
||||
const { push } = useRouter();
|
||||
|
||||
/** 详情 */
|
||||
function handleDetail(row: MallOrderApi.Order) {
|
||||
push({ name: 'TradeOrderDetail', params: { id: row.id } });
|
||||
@@ -61,7 +63,7 @@ function handleRemake(row: MallOrderApi.Order) {
|
||||
id: row.id as number,
|
||||
remark: val,
|
||||
});
|
||||
onRefresh();
|
||||
handleRefresh();
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -113,7 +115,7 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
||||
url="https://doc.iocoder.cn/mall/trade-cart/"
|
||||
/>
|
||||
</template>
|
||||
<DeleveryFormModal @success="onRefresh" />
|
||||
<DeleveryFormModal @success="handleRefresh" />
|
||||
<Grid table-title="订单列表">
|
||||
<template #expand_content="{ row }">
|
||||
<List item-layout="vertical" :data-source="row.items">
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
import { Page } from '@vben/common-ui';
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page>
|
||||
<!-- TODO @xingyu:待开发 -->
|
||||
<div>detail</div>
|
||||
</Page>
|
||||
</template>
|
||||
@@ -0,0 +1,131 @@
|
||||
<script lang="ts" setup>
|
||||
import type { MallOrderApi } from '#/api/mall/trade/order';
|
||||
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
import { updateOrderAddress } from '#/api/mall/trade/order';
|
||||
import { getAreaTree } from '#/api/system/area';
|
||||
import { $t } from '#/locales';
|
||||
|
||||
const emit = defineEmits(['success']);
|
||||
|
||||
const formData = ref<MallOrderApi.DeliveryRequest>();
|
||||
|
||||
const [Form, formApi] = useVbenForm({
|
||||
commonConfig: {
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
formItemClass: 'col-span-2',
|
||||
labelWidth: 120,
|
||||
},
|
||||
layout: 'horizontal',
|
||||
schema: [
|
||||
{
|
||||
component: 'Input',
|
||||
fieldName: 'id',
|
||||
dependencies: {
|
||||
triggerFields: [''],
|
||||
show: () => false,
|
||||
},
|
||||
},
|
||||
// TODO @xingyu:发货默认选中第一个?
|
||||
{
|
||||
fieldName: 'receiverName',
|
||||
label: '收件人',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
placeholder: '请输入收件人名称',
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'receiverMobile',
|
||||
label: '手机号',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
placeholder: '请输入收件人手机号',
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'receiverAreaId',
|
||||
label: '所在地',
|
||||
component: 'ApiTreeSelect',
|
||||
componentProps: {
|
||||
api: () => getAreaTree(),
|
||||
labelField: 'name',
|
||||
valueField: 'id',
|
||||
childrenField: 'children',
|
||||
placeholder: '请选择收件人所在地',
|
||||
treeDefaultExpandAll: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'receiverDetailAddress',
|
||||
label: '详细地址',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
placeholder: '请输入收件人详细地址',
|
||||
type: 'textarea',
|
||||
rows: 3,
|
||||
},
|
||||
},
|
||||
],
|
||||
showDefaultActions: false,
|
||||
});
|
||||
|
||||
const [Modal, modalApi] = useVbenModal({
|
||||
async onConfirm() {
|
||||
const { valid } = await formApi.validate();
|
||||
if (!valid) {
|
||||
return;
|
||||
}
|
||||
modalApi.lock();
|
||||
// 提交表单
|
||||
const data = (await formApi.getValues()) as MallOrderApi.AddressRequest;
|
||||
try {
|
||||
await updateOrderAddress(data);
|
||||
// 关闭并提示
|
||||
await modalApi.close();
|
||||
emit('success');
|
||||
message.success($t('ui.actionMessage.operationSuccess'));
|
||||
} finally {
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
async onOpenChange(isOpen: boolean) {
|
||||
if (!isOpen) {
|
||||
formData.value = undefined;
|
||||
return;
|
||||
}
|
||||
// 加载数据
|
||||
const data = modalApi.getData<MallOrderApi.Order>();
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
modalApi.lock();
|
||||
try {
|
||||
await formApi.setValues({
|
||||
id: data.id,
|
||||
receiverName: data.receiverName,
|
||||
receiverMobile: data.receiverMobile,
|
||||
receiverAreaId: data.receiverAreaId,
|
||||
receiverDetailAddress: data.receiverDetailAddress,
|
||||
});
|
||||
// 设置到 values
|
||||
} finally {
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Modal class="w-1/3" title="修改收货地址">
|
||||
<Form class="mx-4" />
|
||||
</Modal>
|
||||
</template>
|
||||
@@ -0,0 +1,134 @@
|
||||
<script lang="ts" setup>
|
||||
import type { MallOrderApi } from '#/api/mall/trade/order';
|
||||
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
import { floatToFixed2 } from '@vben/utils';
|
||||
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
import { updateOrderPrice } from '#/api/mall/trade/order';
|
||||
import { $t } from '#/locales';
|
||||
|
||||
const emit = defineEmits(['success']);
|
||||
|
||||
const formData = ref<MallOrderApi.DeliveryRequest>();
|
||||
const newPayPrice = ref<string>();
|
||||
|
||||
const [Form, formApi] = useVbenForm({
|
||||
commonConfig: {
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
formItemClass: 'col-span-2',
|
||||
labelWidth: 120,
|
||||
},
|
||||
layout: 'horizontal',
|
||||
schema: [
|
||||
{
|
||||
component: 'Input',
|
||||
fieldName: 'id',
|
||||
dependencies: {
|
||||
triggerFields: [''],
|
||||
show: () => false,
|
||||
},
|
||||
},
|
||||
// TODO @xingyu:发货默认选中第一个?
|
||||
{
|
||||
fieldName: 'payPrice',
|
||||
label: '应付金额(总)',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
placeholder: '请输入应付金额(总)',
|
||||
disabled: true,
|
||||
formatter: (value: string) => `${floatToFixed2(value)}元`,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'adjustPrice',
|
||||
label: '订单调价',
|
||||
component: 'InputNumber',
|
||||
componentProps: {
|
||||
class: 'w-100%',
|
||||
placeholder: '请输入订单调价',
|
||||
step: 0.1,
|
||||
precision: 2,
|
||||
onChange: async (value: number) => {
|
||||
const { payPrice } = await formApi.getValues();
|
||||
await formApi.setValues({
|
||||
newPayPrice: (payPrice + value * 100).toFixed(2),
|
||||
});
|
||||
},
|
||||
},
|
||||
description: '订单调价。 正数,加价;负数,减价',
|
||||
},
|
||||
{
|
||||
fieldName: 'newPayPrice',
|
||||
label: '调价后',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
disabled: true,
|
||||
formatter: (value: string) => `${floatToFixed2(value)}元`,
|
||||
},
|
||||
},
|
||||
],
|
||||
showDefaultActions: false,
|
||||
});
|
||||
|
||||
const [Modal, modalApi] = useVbenModal({
|
||||
async onConfirm() {
|
||||
const { valid } = await formApi.validate();
|
||||
if (!valid) {
|
||||
return;
|
||||
}
|
||||
modalApi.lock();
|
||||
// 提交表单
|
||||
const data = (await formApi.getValues()) as MallOrderApi.PriceRequest;
|
||||
if (data.adjustPrice) {
|
||||
data.adjustPrice = data.adjustPrice * 100;
|
||||
}
|
||||
try {
|
||||
await updateOrderPrice(data);
|
||||
// 关闭并提示
|
||||
await modalApi.close();
|
||||
emit('success');
|
||||
message.success($t('ui.actionMessage.operationSuccess'));
|
||||
} finally {
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
async onOpenChange(isOpen: boolean) {
|
||||
if (!isOpen) {
|
||||
formData.value = undefined;
|
||||
return;
|
||||
}
|
||||
// 加载数据
|
||||
const data = modalApi.getData<MallOrderApi.Order>();
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
modalApi.lock();
|
||||
try {
|
||||
newPayPrice.value = data.payPrice?.toString();
|
||||
data.adjustPrice = data.adjustPrice ? data.adjustPrice / 100 : 0;
|
||||
await formApi.setValues({
|
||||
id: data.id,
|
||||
payPrice: data.payPrice,
|
||||
adjustPrice: data.adjustPrice,
|
||||
newPayPrice: newPayPrice.value,
|
||||
});
|
||||
// 设置到 values
|
||||
} finally {
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Modal class="w-1/3" title="调整价格">
|
||||
<Form class="mx-4" />
|
||||
</Modal>
|
||||
</template>
|
||||
@@ -0,0 +1,94 @@
|
||||
<script lang="ts" setup>
|
||||
import type { MallOrderApi } from '#/api/mall/trade/order';
|
||||
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
import { updateOrderRemark } from '#/api/mall/trade/order';
|
||||
import { $t } from '#/locales';
|
||||
|
||||
const emit = defineEmits(['success']);
|
||||
|
||||
const formData = ref<MallOrderApi.DeliveryRequest>();
|
||||
|
||||
const [Form, formApi] = useVbenForm({
|
||||
commonConfig: {
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
formItemClass: 'col-span-2',
|
||||
labelWidth: 120,
|
||||
},
|
||||
layout: 'horizontal',
|
||||
schema: [
|
||||
{
|
||||
component: 'Input',
|
||||
fieldName: 'id',
|
||||
dependencies: {
|
||||
triggerFields: [''],
|
||||
show: () => false,
|
||||
},
|
||||
},
|
||||
// TODO @xingyu:发货默认选中第一个?
|
||||
{
|
||||
fieldName: 'remark',
|
||||
label: '备注',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
type: 'textarea',
|
||||
rows: 3,
|
||||
},
|
||||
},
|
||||
],
|
||||
showDefaultActions: false,
|
||||
});
|
||||
|
||||
const [Modal, modalApi] = useVbenModal({
|
||||
async onConfirm() {
|
||||
const { valid } = await formApi.validate();
|
||||
if (!valid) {
|
||||
return;
|
||||
}
|
||||
modalApi.lock();
|
||||
// 提交表单
|
||||
const data = (await formApi.getValues()) as MallOrderApi.RemarkRequest;
|
||||
try {
|
||||
await updateOrderRemark(data);
|
||||
// 关闭并提示
|
||||
await modalApi.close();
|
||||
emit('success');
|
||||
message.success($t('ui.actionMessage.operationSuccess'));
|
||||
} finally {
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
async onOpenChange(isOpen: boolean) {
|
||||
if (!isOpen) {
|
||||
formData.value = undefined;
|
||||
return;
|
||||
}
|
||||
// 加载数据
|
||||
const data = modalApi.getData<MallOrderApi.Order>();
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
modalApi.lock();
|
||||
try {
|
||||
await formApi.setValues({ id: data.id, remark: data.remark });
|
||||
// 设置到 values
|
||||
} finally {
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Modal class="w-1/3" title="订单备注">
|
||||
<Form class="mx-4" />
|
||||
</Modal>
|
||||
</template>
|
||||
Reference in New Issue
Block a user