From 09217c48219a6fa8dfcdea79f24131444dbdcc22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E9=B9=8F?= Date: Tue, 7 Jul 2026 17:41:09 +0800 Subject: [PATCH] feat: list group buy orders for dispatch --- admin-web/src/api/groupBuyApi.test.ts | 40 ++++++++++++++++++++ admin-web/src/api/groupBuyApi.ts | 14 ++++++- admin-web/src/types.ts | 16 ++++++++ admin-web/src/views/DeliveryDispatchView.vue | 22 +++++++++-- 4 files changed, 87 insertions(+), 5 deletions(-) create mode 100644 admin-web/src/api/groupBuyApi.test.ts diff --git a/admin-web/src/api/groupBuyApi.test.ts b/admin-web/src/api/groupBuyApi.test.ts new file mode 100644 index 0000000..a4f6a60 --- /dev/null +++ b/admin-web/src/api/groupBuyApi.test.ts @@ -0,0 +1,40 @@ +import { describe, expect, it } from 'vitest' +import { filterPendingDeliveryGroupBuyOrders, groupBuyOrderDispatchLabel } from './groupBuyApi' +import type { GroupBuyOrder } from '../types' + +describe('group buy dispatch helpers', () => { + it('keeps only group buy orders waiting for delivery', () => { + const orders = [ + groupBuyOrder({ id: 1, status: 'PENDING_DELIVERY' }), + groupBuyOrder({ id: 2, status: 'DELIVERING' }), + groupBuyOrder({ id: 3, status: 'PENDING_PICKUP' }) + ] + + expect(filterPendingDeliveryGroupBuyOrders(orders).map((item) => item.id)).toEqual([1]) + }) + + it('builds a readable dispatch label', () => { + expect(groupBuyOrderDispatchLabel(groupBuyOrder({ + orderNo: 'GB00000001', + groupBuyTitle: 'Today eggs', + quantity: 2, + amountCent: 5980 + }))).toBe('GB00000001 Today eggs x2 ¥59.80') + }) +}) + +function groupBuyOrder(overrides: Partial): GroupBuyOrder { + return { + id: 1, + orderNo: 'GB00000001', + groupBuyId: 1, + groupBuyTitle: 'Today eggs', + quantity: 1, + priceCent: 2990, + amountCent: 2990, + deliveryMethod: 'IMMEDIATE', + status: 'PENDING_DELIVERY', + offlinePayStatus: 'UNPAID', + ...overrides + } +} diff --git a/admin-web/src/api/groupBuyApi.ts b/admin-web/src/api/groupBuyApi.ts index b16a97d..6cf369b 100644 --- a/admin-web/src/api/groupBuyApi.ts +++ b/admin-web/src/api/groupBuyApi.ts @@ -1,10 +1,22 @@ -import type { GroupBuy } from '../types' +import type { GroupBuy, GroupBuyOrder } from '../types' import { apiGet, apiPost, apiPut } from './http' export function listGroupBuys() { return apiGet('/api/admin/group-buys') } +export function listGroupBuyOrders(groupBuyId: number) { + return apiGet(`/api/admin/group-buys/${groupBuyId}/orders`) +} + +export function filterPendingDeliveryGroupBuyOrders(orders: GroupBuyOrder[]) { + return orders.filter((order) => order.status === 'PENDING_DELIVERY') +} + +export function groupBuyOrderDispatchLabel(order: GroupBuyOrder) { + return `${order.orderNo} ${order.groupBuyTitle} x${order.quantity} ¥${(order.amountCent / 100).toFixed(2)}` +} + export function createGroupBuy(data: Partial) { return apiPost('/api/admin/group-buys', data) } diff --git a/admin-web/src/types.ts b/admin-web/src/types.ts index e2e8fcf..d494be5 100644 --- a/admin-web/src/types.ts +++ b/admin-web/src/types.ts @@ -101,6 +101,22 @@ export interface GroupBuy { pickupAddress?: string } +export interface GroupBuyOrder { + id: number + orderNo: string + groupBuyId: number + groupBuyTitle: string + coverUrl?: string + quantity: number + priceCent: number + amountCent: number + deliveryMethod: string + status: string + offlinePayStatus: string + remark?: string + createdAt?: string +} + export interface SecondGoods { id: number title: string diff --git a/admin-web/src/views/DeliveryDispatchView.vue b/admin-web/src/views/DeliveryDispatchView.vue index 80f76eb..bdcf482 100644 --- a/admin-web/src/views/DeliveryDispatchView.vue +++ b/admin-web/src/views/DeliveryDispatchView.vue @@ -3,6 +3,12 @@ import { onMounted, reactive, ref } from 'vue' import { Plus, Refresh, Van } from '@element-plus/icons-vue' import { ElMessage } from 'element-plus' import { listGoodsOrders, listExpressOrders } from '../api/orderApi' +import { + filterPendingDeliveryGroupBuyOrders, + groupBuyOrderDispatchLabel, + listGroupBuyOrders, + listGroupBuys +} from '../api/groupBuyApi' import { assignExpressOrder, assignGoodsOrder, @@ -11,7 +17,7 @@ import { listDeliveryOrders, listDeliveryUsers } from '../api/deliveryApi' -import type { DeliveryOrder, DeliveryUser, ExpressOrder, GoodsOrder } from '../types' +import type { DeliveryOrder, DeliveryUser, ExpressOrder, GoodsOrder, GroupBuyOrder } from '../types' import { statusText } from '../utils/format' const loading = ref(false) @@ -19,22 +25,28 @@ const riders = ref([]) const tasks = ref([]) const goodsOrders = ref([]) const expressOrders = ref([]) +const groupBuyOrders = ref([]) const riderForm = reactive({ userId: 3, name: '', phone: '', enabled: true }) const dispatchForm = reactive({ goodsOrderId: undefined as number | undefined, expressOrderId: undefined as number | undefined, groupBuyOrderId: undefined as number | undefined, riderId: undefined as number | undefined }) async function load() { loading.value = true try { - const [riderRows, taskRows, goodsRows, expressRows] = await Promise.all([ + const [riderRows, taskRows, goodsRows, expressRows, groupBuyRows] = await Promise.all([ listDeliveryUsers(), listDeliveryOrders(), listGoodsOrders({ status: 'PENDING_DELIVERY' }), - listExpressOrders('PENDING_PICKUP') + listExpressOrders('PENDING_PICKUP'), + listGroupBuys() ]) + const groupOrderRows = groupBuyRows.length + ? ([] as GroupBuyOrder[]).concat(...await Promise.all(groupBuyRows.map((item) => listGroupBuyOrders(item.id)))) + : [] riders.value = riderRows tasks.value = taskRows goodsOrders.value = goodsRows expressOrders.value = expressRows + groupBuyOrders.value = filterPendingDeliveryGroupBuyOrders(groupOrderRows) if (!dispatchForm.riderId && riderRows[0]) dispatchForm.riderId = riderRows[0].id } catch (error) { ElMessage.error(error instanceof Error ? error.message : '加载失败') @@ -113,7 +125,9 @@ onMounted(load)
- + + + 派单