接入订单列表V4继续处理入口
This commit is contained in:
@@ -391,6 +391,11 @@ describe('reservationService real API mode', () => {
|
||||
display_name: 'GRP-001',
|
||||
open_task_count: 2,
|
||||
next_processable_task_id: '10002',
|
||||
next_v4_order_task_id: '9001',
|
||||
next_v4_action_card_id: '9101',
|
||||
next_v4_action_type: 'REVIEW',
|
||||
next_v4_action_status: 'REVIEW_REQUIRED',
|
||||
v4_open_order_task_count: 1,
|
||||
created_at: '2026-07-08T03:00:00Z',
|
||||
updated_at: '2026-07-08T03:10:00Z',
|
||||
},
|
||||
@@ -415,6 +420,10 @@ describe('reservationService real API mode', () => {
|
||||
)
|
||||
expect(result.items[0]?.display_order_key).toBe('GRP-001')
|
||||
expect(result.items[0]?.next_processable_task_id).toBe('10002')
|
||||
expect(result.items[0]?.next_v4_order_task_id).toBe('9001')
|
||||
expect(result.items[0]?.next_v4_action_type).toBe('REVIEW')
|
||||
expect(result.items[0]?.next_v4_action_status).toBe('REVIEW_REQUIRED')
|
||||
expect(result.items[0]?.v4_open_order_task_count).toBe(1)
|
||||
})
|
||||
|
||||
it('passes order status and business-key filters to the backend', async () => {
|
||||
|
||||
@@ -11,6 +11,7 @@ import ReservationSourceMessageConversationView from '@/views/reservation/Reserv
|
||||
import ReservationTaskListView from '@/views/reservation/ReservationTaskListView.vue'
|
||||
import { useAuthStore } from '@/stores/authStore'
|
||||
import type {
|
||||
ReservationOrderListResult,
|
||||
ReservationV4OrderTaskListResult,
|
||||
ReservationV4WorkbenchListResult,
|
||||
SourceMessageConversationResult,
|
||||
@@ -38,7 +39,8 @@ function createOrderListResult(
|
||||
page_size: 20,
|
||||
total: 1,
|
||||
},
|
||||
) {
|
||||
orderOverrides: Partial<ReservationOrderListResult['items'][number]> = {},
|
||||
): ReservationOrderListResult {
|
||||
return {
|
||||
items: [
|
||||
{
|
||||
@@ -54,6 +56,7 @@ function createOrderListResult(
|
||||
next_processable_task_id: '10002',
|
||||
created_at: '2026-07-08T03:00:00Z',
|
||||
updated_at: '2026-07-08T03:10:00Z',
|
||||
...orderOverrides,
|
||||
},
|
||||
],
|
||||
page,
|
||||
@@ -455,7 +458,8 @@ async function mountWithPlugins(component: object, initialPath = '/', stubs: Rec
|
||||
plugins: [pinia, i18n, router],
|
||||
stubs: {
|
||||
RouterLink: {
|
||||
template: '<a><slot /></a>',
|
||||
props: ['to'],
|
||||
template: '<a :href="to"><slot /></a>',
|
||||
},
|
||||
...stubs,
|
||||
},
|
||||
@@ -512,6 +516,83 @@ describe('reservation P0 views', () => {
|
||||
expect(wrapper.text()).not.toContain('接口待接入')
|
||||
})
|
||||
|
||||
it('routes continue action to the V4 order task when the backend returns next_v4_order_task_id', async () => {
|
||||
vi.mocked(service.fetchReservationOrders).mockResolvedValue(createOrderListResult('GRP-001', {
|
||||
page_num: 1,
|
||||
page_size: 20,
|
||||
total: 1,
|
||||
}, {
|
||||
next_v4_order_task_id: '9001',
|
||||
next_v4_action_card_id: '9101',
|
||||
next_v4_action_type: 'CONFIRM',
|
||||
next_v4_action_status: 'PENDING_CONFIRM',
|
||||
v4_open_order_task_count: 1,
|
||||
}))
|
||||
|
||||
const wrapper = await mountWithPlugins(ReservationOrderListView)
|
||||
await vi.dynamicImportSettled()
|
||||
|
||||
expect(wrapper.find('a[href="/reservation/orders/20001"]').text()).toContain('查看详情')
|
||||
expect(wrapper.find('a[href="/reservation/order-tasks/9001"]').text()).toContain('继续处理')
|
||||
expect(wrapper.find('a[href="/reservation/tasks/10002"]').exists()).toBe(false)
|
||||
expect(wrapper.text()).toContain('待确认')
|
||||
})
|
||||
|
||||
it('shows review state for V4 review entrypoints in the order list', async () => {
|
||||
vi.mocked(service.fetchReservationOrders).mockResolvedValue(createOrderListResult('GRP-001', {
|
||||
page_num: 1,
|
||||
page_size: 20,
|
||||
total: 1,
|
||||
}, {
|
||||
next_v4_order_task_id: '9002',
|
||||
next_v4_action_card_id: '9102',
|
||||
next_v4_action_type: 'REVIEW',
|
||||
next_v4_action_status: 'REVIEW_REQUIRED',
|
||||
v4_open_order_task_count: 1,
|
||||
}))
|
||||
|
||||
const wrapper = await mountWithPlugins(ReservationOrderListView)
|
||||
await vi.dynamicImportSettled()
|
||||
|
||||
expect(wrapper.find('a[href="/reservation/order-tasks/9002"]').text()).toContain('继续处理')
|
||||
expect(wrapper.text()).toContain('待复核')
|
||||
})
|
||||
|
||||
it('falls back to the legacy task detail route when there is no V4 order task entrypoint', async () => {
|
||||
vi.mocked(service.fetchReservationOrders).mockResolvedValue(createOrderListResult())
|
||||
|
||||
const wrapper = await mountWithPlugins(ReservationOrderListView)
|
||||
await vi.dynamicImportSettled()
|
||||
|
||||
expect(wrapper.find('a[href="/reservation/orders/20001"]').text()).toContain('查看详情')
|
||||
expect(wrapper.find('a[href="/reservation/tasks/10002"]').text()).toContain('继续处理')
|
||||
expect(wrapper.find('a[href="/reservation/order-tasks/9001"]').exists()).toBe(false)
|
||||
})
|
||||
|
||||
it('shows no open task state when neither V4 nor legacy entrypoint exists', async () => {
|
||||
vi.mocked(service.fetchReservationOrders).mockResolvedValue(createOrderListResult('GRP-001', {
|
||||
page_num: 1,
|
||||
page_size: 20,
|
||||
total: 1,
|
||||
}, {
|
||||
open_task_count: 0,
|
||||
next_processable_task_id: null,
|
||||
next_v4_order_task_id: null,
|
||||
next_v4_action_card_id: null,
|
||||
next_v4_action_type: 'NONE',
|
||||
next_v4_action_status: null,
|
||||
v4_open_order_task_count: 0,
|
||||
}))
|
||||
|
||||
const wrapper = await mountWithPlugins(ReservationOrderListView)
|
||||
await vi.dynamicImportSettled()
|
||||
|
||||
expect(wrapper.find('a[href="/reservation/orders/20001"]').text()).toContain('查看详情')
|
||||
expect(wrapper.find('a[href^="/reservation/tasks/"]').exists()).toBe(false)
|
||||
expect(wrapper.find('a[href^="/reservation/order-tasks/"]').exists()).toBe(false)
|
||||
expect(wrapper.text()).toContain('无待处理任务')
|
||||
})
|
||||
|
||||
it('filters order list with order status and business-key fields', async () => {
|
||||
vi.mocked(service.fetchReservationOrders).mockResolvedValue(createOrderListResult())
|
||||
|
||||
|
||||
@@ -58,6 +58,9 @@ export interface ReservationOrderListFilters {
|
||||
page_size?: number
|
||||
}
|
||||
|
||||
export type ReservationV4OrderListActionType = 'CONFIRM' | 'REVIEW' | 'NONE'
|
||||
export type ReservationV4OrderListActionStatus = 'PENDING_CONFIRM' | 'REVIEW_REQUIRED'
|
||||
|
||||
export interface ReservationOrderListItem {
|
||||
order_id: string
|
||||
hotel_id: string
|
||||
@@ -69,6 +72,11 @@ export interface ReservationOrderListItem {
|
||||
display_name: string
|
||||
open_task_count: number | null
|
||||
next_processable_task_id: string | null
|
||||
next_v4_order_task_id?: string | null
|
||||
next_v4_action_card_id?: string | null
|
||||
next_v4_action_type?: ReservationV4OrderListActionType | null
|
||||
next_v4_action_status?: ReservationV4OrderListActionStatus | null
|
||||
v4_open_order_task_count?: number | null
|
||||
created_at: string | null
|
||||
updated_at: string | null
|
||||
}
|
||||
|
||||
@@ -132,14 +132,20 @@
|
||||
{{ t('orderList.viewDetail') }}
|
||||
</RouterLink>
|
||||
<RouterLink
|
||||
v-if="order.next_processable_task_id"
|
||||
v-if="resolveContinueTarget(order)"
|
||||
class="row-action"
|
||||
:to="`/reservation/tasks/${order.next_processable_task_id}`"
|
||||
:to="resolveContinueTarget(order) ?? '/'"
|
||||
>
|
||||
{{ t('orderList.continueTask') }}
|
||||
</RouterLink>
|
||||
<span
|
||||
v-else
|
||||
v-if="formatContinueActionLabel(order)"
|
||||
class="row-action-tag"
|
||||
>
|
||||
{{ formatContinueActionLabel(order) }}
|
||||
</span>
|
||||
<span
|
||||
v-if="!resolveContinueTarget(order)"
|
||||
class="row-action row-action--muted"
|
||||
>
|
||||
{{ t('orderList.noOpenTask') }}
|
||||
@@ -302,6 +308,29 @@ function formatOrderKey(order: ReservationOrderListItem): string {
|
||||
order.order_id
|
||||
)
|
||||
}
|
||||
|
||||
function resolveContinueTarget(order: ReservationOrderListItem): string | null {
|
||||
if (order.next_v4_order_task_id) {
|
||||
return `/reservation/order-tasks/${order.next_v4_order_task_id}`
|
||||
}
|
||||
if (order.next_processable_task_id) {
|
||||
return `/reservation/tasks/${order.next_processable_task_id}`
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
function formatContinueActionLabel(order: ReservationOrderListItem): string {
|
||||
if (!order.next_v4_order_task_id) {
|
||||
return ''
|
||||
}
|
||||
if (order.next_v4_action_type === 'REVIEW' || order.next_v4_action_status === 'REVIEW_REQUIRED') {
|
||||
return t('status.REVIEW_REQUIRED')
|
||||
}
|
||||
if (order.next_v4_action_type === 'CONFIRM' || order.next_v4_action_status === 'PENDING_CONFIRM') {
|
||||
return t('status.PENDING_CONFIRM')
|
||||
}
|
||||
return ''
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@@ -374,6 +403,15 @@ function formatOrderKey(order: ReservationOrderListItem): string {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.row-action-tag {
|
||||
border-radius: 999px;
|
||||
background: var(--th-color-blue-100);
|
||||
color: var(--th-color-blue-600);
|
||||
font-size: 12px;
|
||||
font-weight: 800;
|
||||
padding: 2px 8px;
|
||||
}
|
||||
|
||||
.empty-state {
|
||||
min-height: 220px;
|
||||
display: grid;
|
||||
|
||||
Reference in New Issue
Block a user