前端调整V4预订事项用户化展示

This commit is contained in:
andy committed 2026-07-21 20:54:24 +07:00
1 parent 350bb3310b
commit c521eb6dc0
20 files changed
+703 -657

No files matched your search

+4 -4
View File
@@ -248,7 +248,7 @@ describe('reservationService real API mode', () => {
page_size: 20,
})
await fetchReservationV4RateCodeLookups({
keyword: 'BAR',
keyword: 'GRPA2-850UP',
page_num: 1,
page_size: 20,
})
@@ -265,7 +265,7 @@ describe('reservationService real API mode', () => {
)
expect(fetchMock).toHaveBeenNthCalledWith(
3,
'/api/reservation/lookups/rate-codes?hotel_id=HOTEL-BKK&keyword=BAR&page_num=1&page_size=20',
'/api/reservation/lookups/rate-codes?hotel_id=HOTEL-BKK&keyword=GRPA2-850UP&page_num=1&page_size=20',
expect.objectContaining({ method: 'GET' }),
)
})
@@ -336,7 +336,7 @@ describe('reservationService real API mode', () => {
field_overrides: [
{
field_pointer: '/business_fields/room_items/0/room_type_code',
value: 'TWN',
value: 'RM2',
},
],
})
@@ -357,7 +357,7 @@ describe('reservationService real API mode', () => {
field_overrides: [
{
field_pointer: '/business_fields/room_items/0/room_type_code',
value: 'TWN',
value: 'RM2',
},
],
}),
@@ -37,12 +37,12 @@ describe('reservationV4FieldRules', () => {
it('builds initial values from backend fields using field pointers', () => {
const fields = [
createV4Field('/basic_information/account_code', { value: 'HANATOUR' }),
createV4Field('/business_fields/after/room_items/0/room_type_code', { value: 'TWN' }),
createV4Field('/business_fields/after/room_items/0/room_type_code', { value: 'RM2' }),
]
expect(buildReservationV4InitialFieldValues(fields)).toEqual({
'/basic_information/account_code': 'HANATOUR',
'/business_fields/after/room_items/0/room_type_code': 'TWN',
'/business_fields/after/room_items/0/room_type_code': 'RM2',
})
})
@@ -55,7 +55,7 @@ describe('reservationV4FieldRules', () => {
raw_readonly: true,
}),
createV4Field('/business_fields/after/room_items/0/room_type_code', {
value: 'KING',
value: 'SU1',
edit_scope: 'MANUAL_REVIEW_ONLY',
write_target: 'REVIEW_RESOLUTION_FIELD_OVERRIDES',
}),
@@ -64,7 +64,7 @@ describe('reservationV4FieldRules', () => {
const payload = buildReservationV4ConfirmedPayload(fields, {
'/basic_information/account_code': 'HANATOUR',
'/basic_information/read_only_label': 'SHOULD_NOT_SEND',
'/business_fields/after/room_items/0/room_type_code': 'TWN',
'/business_fields/after/room_items/0/room_type_code': 'RM2',
})
expect(payload).toEqual({
@@ -82,19 +82,19 @@ describe('reservationV4FieldRules', () => {
write_target: 'REVIEW_RESOLUTION_FIELD_OVERRIDES',
}),
createV4Field('/room_information/final_values/room_items/0/room_type_raw', {
value: 'TWN',
value: 'RM2',
editable: false,
write_target: 'NONE',
}),
]
expect(buildReservationV4ReviewOverrides(fields, {
'/room_information/final_values/room_items/0/room_type_code': 'TWN',
'/room_information/final_values/room_items/0/room_type_code': 'RM2',
'/room_information/final_values/room_items/0/room_type_raw': 'Twin',
})).toEqual([
{
field_pointer: '/room_information/final_values/room_items/0/room_type_code',
value: 'TWN',
value: 'RM2',
},
])
})
@@ -111,10 +111,10 @@ describe('reservationV4FieldRules', () => {
expect(isReservationV4ConfirmWritableField(confirmField)).toBe(false)
expect(isReservationV4ReviewWritableField(reviewField)).toBe(false)
expect(buildReservationV4ConfirmedPayload([confirmField], {
'/business_fields/after/room_items/0/rate_code': 'BAR',
'/business_fields/after/room_items/0/rate_code': 'GRPA2-850UP',
})).toEqual({})
expect(buildReservationV4ReviewOverrides([reviewField], {
'/business_fields/after/room_items/0/room_type_code': 'TWN',
'/business_fields/after/room_items/0/room_type_code': 'RM2',
})).toEqual([])
})
@@ -184,7 +184,7 @@ describe('reservationV4FieldRules', () => {
expect(buildReservationV4ReviewOverrides(fields, {
'/source_message/subject': 'SHOULD_NOT_SEND',
'/room_information/final_values/arrival_date': '2026-07-26',
'/business_fields/after/room_items/0/room_type_code': 'TWN',
'/business_fields/after/room_items/0/room_type_code': 'RM2',
})).toEqual([
{
field_pointer: '/room_information/final_values/arrival_date',
@@ -192,7 +192,7 @@ describe('reservationV4FieldRules', () => {
},
{
field_pointer: '/business_fields/after/room_items/0/room_type_code',
value: 'TWN',
value: 'RM2',
},
])
})
@@ -203,7 +203,7 @@ describe('reservationV4FieldRules', () => {
group_code: 'GRP-001',
raw_evidence: 'raw email body',
nested: {
room_type_code: 'TWN',
room_type_code: 'RM2',
oss_url: 'https://oss.example/private-file.pdf',
},
safe_url_value: 'https://oss.example/also-hidden.pdf',
@@ -211,7 +211,7 @@ describe('reservationV4FieldRules', () => {
}, 'empty', 'hidden')
expect(text).toContain('GRP-001')
expect(text).toContain('TWN')
expect(text).toContain('RM2')
expect(text).not.toContain('raw email body')
expect(text).not.toContain('https://oss.example')
expect(text).not.toContain('oss_url')
+75 -48
View File
@@ -52,7 +52,7 @@ describe('reservation V4 pages', () => {
mockCatalogLookups()
})
it('renders V4 order task cards, loads lookup options and confirms only selected codes', async () => {
it('renders reservation handling cards, hides default diagnostics and confirms only selected codes', async () => {
const detail = createOrderTaskDetail()
vi.mocked(service.fetchReservationV4OrderTaskDetail).mockResolvedValue(detail)
vi.mocked(service.confirmReservationV4OrderTaskCard).mockResolvedValue({
@@ -69,11 +69,18 @@ describe('reservation V4 pages', () => {
)
await flushPromises()
expect(wrapper.text()).toContain('V4 订单任务详情')
expect(wrapper.text()).toContain('订单事项办理')
expect(wrapper.text()).toContain('Booking Request')
expect(wrapper.text()).toContain('基础信息卡')
expect(wrapper.text()).toContain('业务任务卡')
expect(wrapper.text()).toContain('Backend contract issue')
expect(wrapper.text()).toContain('预订基础信息')
expect(wrapper.text()).toContain('预订事项')
expect(wrapper.text()).toContain('房型与日期')
expect(wrapper.text()).not.toContain('V4 订单任务详情')
expect(wrapper.text()).not.toContain('Backend contract issue')
expect(wrapper.text()).not.toContain('card-basic')
expect(wrapper.text()).not.toContain('card-room')
expect(wrapper.text()).not.toContain('#9001')
expect(wrapper.text()).not.toContain(zhCN.taskV4.orderTaskId)
expect(wrapper.text()).not.toContain(zhCN.taskV4.displayPayload)
expect(service.fetchReservationV4AccountLookups).toHaveBeenCalledWith({
hotel_id: 'HOTEL-TEST',
page_num: 1,
@@ -102,7 +109,7 @@ describe('reservation V4 pages', () => {
})
})
it('keeps V4 task card layout classes for styled card metadata and payload summary', async () => {
it('keeps task card layout classes and places the primary card action in the card header', async () => {
const detail = createOrderTaskDetail()
detail.business_cards[0]!.card_type = 'VOUCHER'
detail.business_cards[0]!.display_payload = {
@@ -119,10 +126,10 @@ describe('reservation V4 pages', () => {
expect(wrapper.findAll('.task-card-section')).toHaveLength(2)
expect(wrapper.find('.task-card-section__header').exists()).toBe(true)
expect(wrapper.find('.card-meta-grid').exists()).toBe(true)
expect(wrapper.find('.card-meta-item').text()).toContain(zhCN.task.reviewStatus)
expect(wrapper.find('details.safe-payload > summary').text()).toContain(zhCN.taskV4.displayPayload)
expect(wrapper.find('.card-actions .primary-button').text()).toContain(zhCN.taskV4.confirmCard)
expect(wrapper.find('.card-meta-grid').exists()).toBe(false)
expect(wrapper.find('details.safe-payload').exists()).toBe(false)
expect(wrapper.find('.task-card-section__header .primary-button').text()).toContain(zhCN.taskV4.confirmCard)
expect(wrapper.find('.card-actions .primary-button').exists()).toBe(false)
})
it('does not render direct attachment URLs from V4 source message payload', async () => {
@@ -184,17 +191,16 @@ describe('reservation V4 pages', () => {
)
await flushPromises()
expect(service.fetchSourceMessageConversation).toHaveBeenCalledWith('30001')
const pageText = wrapper.text()
const basicIndex = pageText.indexOf(zhCN.taskV4.basicInformationCard)
const businessIndex = pageText.indexOf(zhCN.taskV4.businessCards)
const sourceIndex = pageText.indexOf(zhCN.taskV4.sourceMessageCard)
expect(basicIndex).toBeGreaterThanOrEqual(0)
expect(businessIndex).toBeGreaterThan(basicIndex)
expect(sourceIndex).toBeGreaterThan(businessIndex)
const sections = wrapper.findAll('.th-section')
expect(sections[sections.length - 1]?.text()).toContain(zhCN.taskV4.sourceMessageCard)
expect(pageText).toContain('Current trigger email body opening.')
expect(sections[sections.length - 1]?.text()).toContain(zhCN.taskV4.sourceMessage.bodyCollapsed)
expect(service.fetchSourceMessageConversation).not.toHaveBeenCalled()
expect(pageText).not.toContain('Current trigger email body opening.')
expect(pageText).not.toContain('OLDER_MESSAGE_BODY')
expect(pageText).not.toContain('REPLY_MESSAGE_BODY')
expect(pageText).not.toContain('CURRENT_BODY_TAIL')
@@ -209,6 +215,8 @@ describe('reservation V4 pages', () => {
await expandButton!.trigger('click')
await flushPromises()
expect(service.fetchSourceMessageConversation).toHaveBeenCalledWith('30001')
expect(wrapper.text()).toContain('Current trigger email body opening.')
expect(wrapper.text()).toContain('CURRENT_BODY_TAIL')
expect(wrapper.text()).toContain(zhCN.taskV4.sourceMessage.collapseBody)
})
@@ -228,6 +236,17 @@ describe('reservation V4 pages', () => {
await flushPromises()
expect(wrapper.text()).toContain('Please book one twin room.')
expect(wrapper.text()).not.toContain(zhCN.taskV4.sourceMessage.bodyLoadFailed)
expect(service.fetchSourceMessageConversation).not.toHaveBeenCalled()
const expandButton = wrapper
.findAll('button')
.find((button) => button.text().includes(zhCN.taskV4.sourceMessage.showFullBody))
expect(expandButton).toBeTruthy()
await expandButton!.trigger('click')
await flushPromises()
expect(service.fetchSourceMessageConversation).toHaveBeenCalledWith('30001')
expect(wrapper.text()).toContain(zhCN.taskV4.sourceMessage.bodyLoadFailed)
expect(wrapper.text()).toContain(zhCN.task.viewConversation)
})
@@ -250,7 +269,7 @@ describe('reservation V4 pages', () => {
)
await flushPromises()
expect(wrapper.text()).toContain('Sanitized current email opening.')
expect(wrapper.text()).not.toContain('Sanitized current email opening.')
expect(wrapper.text()).not.toContain('SANITIZED_HTML_TAIL')
expect(wrapper.text()).not.toContain('RAW_HTML_SHOULD_NOT_RENDER')
@@ -261,6 +280,7 @@ describe('reservation V4 pages', () => {
await expandButton!.trigger('click')
await flushPromises()
expect(wrapper.text()).toContain('Sanitized current email opening.')
expect(wrapper.text()).toContain('SANITIZED_HTML_TAIL')
expect(wrapper.html()).toContain('<p>Sanitized current email opening.')
expect(wrapper.html()).not.toContain('RAW_HTML_SHOULD_NOT_RENDER')
@@ -283,6 +303,14 @@ describe('reservation V4 pages', () => {
)
await flushPromises()
expect(wrapper.text()).not.toContain('Plain text fallback body.')
const expandButton = wrapper
.findAll('button')
.find((button) => button.text().includes(zhCN.taskV4.sourceMessage.showFullBody))
expect(expandButton).toBeTruthy()
await expandButton!.trigger('click')
await flushPromises()
expect(wrapper.text()).toContain('Plain text fallback body.')
expect(wrapper.text()).not.toContain('SANITIZED_HTML_SHOULD_NOT_RENDER')
expect(wrapper.text()).not.toContain('RAW_HTML_SHOULD_NOT_RENDER')
@@ -373,7 +401,7 @@ describe('reservation V4 pages', () => {
const paymentCard = wrapper
.findAll('.task-card-section')
.find((section) => section.text().includes(zhCN.cardType.PAYMENT))
.find((section) => section.text().includes(zhCN.taskV4.paymentCard))
expect(paymentCard).toBeTruthy()
await paymentCard!.find('button.primary-button').trigger('click')
await flushPromises()
@@ -409,9 +437,8 @@ describe('reservation V4 pages', () => {
expect(roomingListCard.text()).toContain('GRP-001')
expect(roomingListSection!.findAll('a').filter((link) => link.text() === zhCN.task.viewOrder)).toHaveLength(1)
expect(wrapper.text()).toContain(zhCN.taskV4.sourceMessageCard)
expect(wrapper.text().indexOf(zhCN.taskV4.sourceMessageCard)).toBeGreaterThan(
wrapper.text().indexOf(zhCN.taskV4.roomingList.title),
)
const sections = wrapper.findAll('.th-section')
expect(sections[sections.length - 1]?.text()).toContain(zhCN.taskV4.sourceMessageCard)
expect(roomingListSection!.find('details.safe-payload').exists()).toBe(false)
expect(roomingListSection!.text()).not.toContain('ROW_SHOULD_NOT_RENDER')
@@ -592,13 +619,13 @@ describe('reservation V4 pages', () => {
.find((section) => section.find('[data-testid="trace-card"]').exists())
expect(traceSection).toBeTruthy()
expect(traceSection!.text()).toContain('SET EXTRA BED')
expect(traceSection!.find('select[name="/trace_items/0/target_room_type_code"]').text()).toContain('TWN - Twin')
expect(traceSection!.find('select[name="/trace_items/0/target_room_type_code"]').text()).toContain('RM2')
const roomCountInput = traceSection!.find('input[name="/trace_items/0/extra_bed_room_count"]')
expect(roomCountInput.attributes('type')).toBe('number')
expect(roomCountInput.attributes('min')).toBe('1')
expect(roomCountInput.attributes('step')).toBe('1')
await traceSection!.find('select[name="/trace_items/0/target_room_type_code"]').setValue('TWN')
await traceSection!.find('select[name="/trace_items/0/target_room_type_code"]').setValue('RM2')
await roomCountInput.setValue('0')
await traceSection!.find('select[name="/trace_items/0/department_code"]').setValue('FO+HSK')
await traceSection!.find('button.primary-button').trigger('click')
@@ -616,7 +643,7 @@ describe('reservation V4 pages', () => {
confirmed_payload: {
trace_items: [
{
target_room_type_code: 'TWN',
target_room_type_code: 'RM2',
extra_bed_room_count: 2,
department_code: 'FO+HSK',
},
@@ -770,11 +797,11 @@ describe('reservation V4 pages', () => {
group_block_name: '前端修正团队名',
arrival_date: '2026-07-26',
departure_date: '2026-07-29',
rate_code: 'GROUP',
rate_code: 'GRPA2-850UP',
group_booking_status: 'DEF',
room_items: [
{
room_type_code: 'TWN',
room_type_code: 'RM2',
room_count: 2,
},
],
@@ -858,13 +885,13 @@ describe('reservation V4 pages', () => {
arrival_date: '2026-07-26',
departure_date: '2026-07-29',
nights: 3,
rate_code: 'GROUP',
rate_code: 'GRPA2-850UP',
breakfast_included: true,
group_booking_status: 'TEN',
group_booking_status_label: 'TEN-Tentative',
room_items: [
{
room_type_code: 'TWN',
room_type_code: 'RM2',
room_count: 2,
},
],
@@ -874,7 +901,7 @@ describe('reservation V4 pages', () => {
departure_date: '2026-07-31',
room_items: [
{
room_type_code: 'DBL',
room_type_code: 'RM3',
room_count: 3,
},
],
@@ -884,13 +911,13 @@ describe('reservation V4 pages', () => {
arrival_date: '2026-07-27',
departure_date: '2026-07-31',
nights: 4,
rate_code: 'GROUP',
rate_code: 'GRPA2-850UP',
breakfast_included: true,
group_booking_status: 'TEN',
group_booking_status_label: 'TEN-Tentative',
room_items: [
{
room_type_code: 'DBL',
room_type_code: 'RM3',
room_count: 3,
},
],
@@ -953,13 +980,13 @@ describe('reservation V4 pages', () => {
arrival_date: '2026-08-01',
departure_date: '2026-08-05',
nights: 4,
rate_code: 'GROUP',
rate_code: 'GRPA2-850UP',
breakfast_included: true,
group_booking_status: 'DEF',
group_booking_status_label: 'DEF-Definite',
room_items: [
{
room_type_code: 'KING',
room_type_code: 'SU1',
room_count: 1,
},
],
@@ -969,13 +996,13 @@ describe('reservation V4 pages', () => {
arrival_date: '2026-08-01',
departure_date: '2026-08-05',
nights: 4,
rate_code: 'GROUP',
rate_code: 'GRPA2-850UP',
breakfast_included: true,
group_booking_status: 'DEF',
group_booking_status_label: 'DEF-Definite',
room_items: [
{
room_type_code: 'KING',
room_type_code: 'SU1',
room_count: 1,
},
],
@@ -994,7 +1021,7 @@ describe('reservation V4 pages', () => {
expect(roomCard.text()).toContain(zhCN.taskV4.roomInformation.currentValues)
expect(roomCard.text()).toContain(zhCN.taskV4.roomInformation.finalValues)
expect(roomCard.text()).toContain('GRP-V4-RI-CANCEL-001')
expect(roomCard.text()).toContain('KING')
expect(roomCard.text()).toContain('SU1')
expect(roomCard.find('input[name="/room_information/final_values/arrival_date"]').exists()).toBe(false)
expect(roomCard.find('select[name="/room_information/final_values/group_booking_status"]').exists()).toBe(false)
})
@@ -1027,7 +1054,7 @@ describe('reservation V4 pages', () => {
await flushPromises()
const roomCard = wrapper.find('[data-testid="room-information-card"]')
await roomCard.find('select[name="/room_information/final_values/room_items/0/room_type_code"]').setValue('TWN')
await roomCard.find('select[name="/room_information/final_values/room_items/0/room_type_code"]').setValue('RM2')
await wrapper.find('input[name="v4_confirmed_order_id"]').setValue('order-2001')
await wrapper.find('textarea').setValue('confirmed by email evidence')
await roomCard.find('button').trigger('click')
@@ -1040,7 +1067,7 @@ describe('reservation V4 pages', () => {
field_overrides: [
{
field_pointer: '/room_information/final_values/room_items/0/room_type_code',
value: 'TWN',
value: 'RM2',
},
],
})
@@ -1073,8 +1100,8 @@ describe('reservation V4 pages', () => {
expect(wrapper.text()).toContain(zhCN.taskV4.lookup.empty)
expect(wrapper.text()).toContain(zhCN.taskV4.lookup.stale)
expect(wrapper.text()).toContain('PMS sync is stale')
expect(wrapper.text()).toContain('PMS_SYNC')
expect(wrapper.text()).toContain('stale-v1')
expect(wrapper.text()).not.toContain('PMS_SYNC')
expect(wrapper.text()).not.toContain('stale-v1')
})
it('does not submit a catalog field value that is missing from the loaded lookup result', async () => {
@@ -1359,8 +1386,8 @@ function mockCatalogLookups() {
stale: false,
items: [
{
code: 'TWN',
display_name: 'Twin',
code: 'RM2',
display_name: 'RM2',
status: 'ACTIVE',
catalog_source: 'SYSTEM_MANAGED',
market_code: null,
@@ -1386,8 +1413,8 @@ function mockCatalogLookups() {
stale: false,
items: [
{
code: 'GROUP',
display_name: 'Group Rate',
code: 'GRPA2-850UP',
display_name: 'GRPA2-850UP',
status: 'ACTIVE',
catalog_source: 'SYSTEM_MANAGED',
market_code: null,
@@ -1750,7 +1777,7 @@ function useTraceBusinessCard(
itemType === 'EXTRA_BED'
? {
item_type: 'EXTRA_BED',
target_room_type_code: 'TWN',
target_room_type_code: 'RM2',
extra_bed_room_count: 1,
department_code: 'FO',
content: 'CONTENT_SHOULD_NOT_RENDER',
@@ -1770,7 +1797,7 @@ function useTraceBusinessCard(
? [
createField('/trace_items/0/target_room_type_code', {
display_name: 'Target Room Type',
value: 'TWN',
value: 'RM2',
options_source: 'RESERVATION_V4_ROOM_TYPE_CATALOG',
control_type: 'SELECT',
edit_scope: isReviewRequired ? 'REVIEW' : 'CONFIRM',
@@ -1891,13 +1918,13 @@ function createRoomInformationDisplayPayload(overrides: {
arrival_date: '2026-07-26',
departure_date: '2026-07-29',
nights: 3,
rate_code: 'GROUP',
rate_code: 'GRPA2-850UP',
breakfast_included: true,
group_booking_status: 'TEN',
group_booking_status_label: 'TEN-Tentative',
room_items: [
{
room_type_code: 'TWN',
room_type_code: 'RM2',
room_count: 2,
},
],
@@ -1962,7 +1989,7 @@ function createRoomInformationFields(reviewMode = false): ReservationV4TaskCardR
}),
createField('/room_information/final_values/rate_code', {
display_name: 'Rate Code',
value: 'GROUP',
value: 'GRPA2-850UP',
required: true,
control_type: 'SELECT',
options_source: 'RESERVATION_V4_RATE_CODE_CATALOG',
@@ -1972,7 +1999,7 @@ function createRoomInformationFields(reviewMode = false): ReservationV4TaskCardR
}),
createField('/room_information/final_values/room_items/0/room_type_code', {
display_name: '房型代码',
value: reviewMode ? '' : 'TWN',
value: reviewMode ? '' : 'RM2',
required: true,
control_type: 'SELECT',
options_source: 'RESERVATION_V4_ROOM_TYPE_CATALOG',
+53 -33
View File
@@ -570,32 +570,36 @@ describe('reservation P0 views', () => {
vi.mocked(service.fetchSourceMessageConversation).mockReset()
})
it('renders the V4 order overview, next action, related source messages and safe card timeline', async () => {
it('renders the user-facing order overview, next action, related source emails and processing records', async () => {
vi.mocked(service.fetchReservationOrderDetail).mockResolvedValue(createOrderDetailResult())
const wrapper = await mountWithPlugins(ReservationOrderDetailView, '/reservation/orders/20001')
await vi.dynamicImportSettled()
expect(wrapper.text()).toContain('V4 当前确认快照')
expect(wrapper.text()).toContain('订单信息总览')
expect(wrapper.text()).toContain('QBD_TRAVEL')
expect(wrapper.text()).toContain('Q.B.D. TRAVEL GROUP CO., LTD')
expect(wrapper.text()).toContain('2026-07-26')
expect(wrapper.text()).toContain('2026-07-29')
expect(wrapper.text()).toContain('RM1 × 2')
expect(wrapper.text()).toContain('下一步处理')
expect(wrapper.find('a[href="/reservation/order-tasks/9001"]').text()).toContain('进入处理页')
expect(wrapper.find('a[href="/reservation/order-tasks/9001"]').text()).toContain('进入办理页')
expect(wrapper.find('.primary-link[href="/reservation/order-tasks/9001"]').exists()).toBe(true)
expect(wrapper.text()).toContain('关联来源消息')
expect(wrapper.text()).toContain('关联来源邮件')
expect(wrapper.text()).toContain('v4@example.test')
expect(wrapper.text()).toContain('查看邮件会话')
expect(wrapper.find('a[href="/reservation/source-messages/30003/conversation"]').exists()).toBe(true)
expect(wrapper.text()).toContain(zhCN.order.v4TaskTimeline)
expect(wrapper.text()).toContain('order-ref-1')
expect(wrapper.text()).toContain('处理记录')
expect(wrapper.text()).not.toContain('order-ref-1')
expect(wrapper.text()).toContain('V4 Booking Package')
expect(wrapper.find('.v4-timeline-item__main[href="/reservation/order-tasks/9001"]').exists()).toBe(true)
expect(wrapper.text()).toContain('基础信息卡')
expect(wrapper.text()).toContain('事项状态摘要')
expect(wrapper.text()).toContain('预订基础信息')
expect(wrapper.text()).toContain('frontend-query-admin')
expect(wrapper.text()).toContain('待复核')
expect(wrapper.text()).not.toContain('V4 当前确认快照')
expect(wrapper.text()).not.toContain('任务卡编号')
expect(wrapper.text()).not.toContain('card-basic')
expect(wrapper.text()).not.toContain('unsafe ai payload must stay hidden')
expect(wrapper.text()).not.toContain('unsafe display payload must stay hidden')
expect(wrapper.text()).not.toContain('unsafe confirmed payload must stay hidden')
@@ -622,9 +626,9 @@ describe('reservation P0 views', () => {
const wrapper = await mountWithPlugins(ReservationOrderDetailView, '/reservation/orders/20001')
await vi.dynamicImportSettled()
expect(wrapper.text()).toContain('暂无已确认的 V4 订单快照')
expect(wrapper.text()).toContain('暂无下一步 V4 处理动作')
expect(wrapper.text()).toContain('暂无关联来源消息')
expect(wrapper.text()).toContain('暂无已确认的订单信息')
expect(wrapper.text()).toContain('暂无下一步处理事项')
expect(wrapper.text()).toContain('暂无关联来源邮件')
expect(wrapper.text()).toContain(zhCN.order.v4TaskTimelineEmpty)
expect(wrapper.find('a[href^="/reservation/order-tasks/"]').exists()).toBe(false)
})
@@ -830,10 +834,11 @@ describe('reservation P0 views', () => {
expect(wrapper.text()).toContain('GRP-001')
expect(wrapper.text()).toContain('Booking Request')
expect(wrapper.text()).toContain('订单任务')
expect(wrapper.text()).toContain('预订事项')
expect(wrapper.text()).toContain('待确认 1')
expect(wrapper.text()).toContain('待复核 1')
expect(wrapper.text()).toContain('查看邮件会话')
expect(wrapper.text()).not.toContain('#9001')
expect(wrapper.text()).not.toContain('接口待补')
expect(service.fetchReservationV4WorkbenchItems).toHaveBeenCalledWith(
expect.objectContaining({
@@ -866,10 +871,12 @@ describe('reservation P0 views', () => {
expect(wrapper.text()).toContain('待确认已读')
expect(wrapper.text()).toContain('查看通知')
expect(wrapper.text()).toContain('查看邮件会话')
expect(wrapper.text()).toContain('该邮件不关联订单,请查看邮件内容。')
expect(wrapper.text()).not.toContain('S10/S99')
expect(wrapper.text()).not.toContain('查看订单')
})
it('filters V4 workbench with supported backend fields only', async () => {
it('filters the reservation workbench with user-facing filter choices only', async () => {
vi.mocked(service.fetchReservationV4WorkbenchItems)
.mockResolvedValueOnce(createV4WorkbenchResult('GRP-001', 'Booking Request', { page_num: 1, page_size: 20, total: 45 }))
.mockResolvedValueOnce(createV4SourceNotificationWorkbenchResult('S10'))
@@ -877,14 +884,18 @@ describe('reservation P0 views', () => {
const wrapper = await mountWithPlugins(ReservationTaskListView)
await vi.dynamicImportSettled()
const itemTypeOptions = wrapper.find('select[name="item_type"]').findAll('option')
const itemTypeOptions = wrapper.find('select[name="business_status"]').findAll('option')
.map((option) => option.attributes('value'))
expect(itemTypeOptions).toEqual(['', 'ORDER_TASK', 'SOURCE_NOTIFICATION'])
expect(wrapper.find('select[name="item_type"]').text()).toContain('来源通知')
expect(wrapper.find('select[name="item_type"]').text()).not.toContain('INFORMATIONAL_MESSAGE')
expect(itemTypeOptions).toEqual(['', 'PENDING_CONFIRM', 'REVIEW_REQUIRED', 'COMPLETED', 'SOURCE_NOTIFICATION'])
expect(wrapper.find('select[name="business_status"]').text()).toContain('来源通知')
expect(wrapper.find('select[name="business_status"]').text()).not.toContain('ORDER_TASK')
expect(wrapper.find('select[name="business_status"]').text()).not.toContain('SOURCE_NOTIFICATION')
expect(wrapper.find('select[name="item_type"]').exists()).toBe(false)
expect(wrapper.find('select[name="order_task_status"]').exists()).toBe(false)
expect(wrapper.find('select[name="card_status"]').exists()).toBe(false)
await wrapper.find('select[name="item_type"]').setValue('SOURCE_NOTIFICATION')
await wrapper.find('select[name="business_status"]').setValue('SOURCE_NOTIFICATION')
await vi.dynamicImportSettled()
expect(service.fetchReservationV4WorkbenchItems).toHaveBeenLastCalledWith(
@@ -903,35 +914,45 @@ describe('reservation P0 views', () => {
expect(lastTaskRequest).not.toHaveProperty('route_code')
})
it('filters and paginates V4 business order tasks with supported backend fields', async () => {
it('shows a filter-specific empty state for completed reservation items', async () => {
vi.mocked(service.fetchReservationV4WorkbenchItems)
.mockResolvedValueOnce(createV4WorkbenchResult('GRP-001', 'Booking Request'))
vi.mocked(service.fetchReservationV4OrderTasks)
.mockResolvedValueOnce({
items: [],
page: {
page_num: 1,
page_size: 20,
total: 0,
},
})
const wrapper = await mountWithPlugins(ReservationTaskListView)
await vi.dynamicImportSettled()
await wrapper.find('select[name="business_status"]').setValue('COMPLETED')
await vi.dynamicImportSettled()
expect(wrapper.text()).toContain('暂无已完成的预订事项')
expect(wrapper.text()).not.toContain('暂无需要处理的预订事项')
})
it('filters and paginates reservation work items with supported backend fields', async () => {
vi.mocked(service.fetchReservationV4WorkbenchItems)
.mockResolvedValueOnce(createV4WorkbenchResult('GRP-001', 'Booking Request', { page_num: 1, page_size: 20, total: 45 }))
vi.mocked(service.fetchReservationV4OrderTasks)
.mockResolvedValueOnce(createV4OrderTaskListResult('GRP-001', 'Booking Request', { page_num: 1, page_size: 20, total: 45 }))
.mockResolvedValueOnce(createV4OrderTaskListResult('GRP-001', 'Booking Request', { page_num: 1, page_size: 20, total: 45 }))
.mockResolvedValueOnce(createV4OrderTaskListResult('GRP-001', 'Booking Request', { page_num: 1, page_size: 20, total: 45 }))
.mockResolvedValueOnce(createV4OrderTaskListResult('GRP-002', 'Booking Update', { page_num: 2, page_size: 20, total: 45 }))
const wrapper = await mountWithPlugins(ReservationTaskListView)
await vi.dynamicImportSettled()
expect(wrapper.find('select[name="order_task_status"]').attributes('disabled')).toBeDefined()
expect(wrapper.find('select[name="card_status"]').attributes('disabled')).toBeDefined()
await wrapper.find('select[name="item_type"]').setValue('ORDER_TASK')
await vi.dynamicImportSettled()
expect(wrapper.find('select[name="order_task_status"]').attributes('disabled')).toBeUndefined()
expect(wrapper.find('select[name="card_status"]').attributes('disabled')).toBeUndefined()
await wrapper.find('select[name="order_task_status"]').setValue('OPEN')
await vi.dynamicImportSettled()
await wrapper.find('select[name="card_status"]').setValue('REVIEW_REQUIRED')
await wrapper.find('select[name="business_status"]').setValue('REVIEW_REQUIRED')
await vi.dynamicImportSettled()
const taskListCalls = vi.mocked(service.fetchReservationV4OrderTasks).mock.calls
const filteredTaskRequest = taskListCalls[taskListCalls.length - 1]?.[0] ?? {}
expect(filteredTaskRequest).toMatchObject({
order_task_status: 'OPEN',
card_status: 'REVIEW_REQUIRED',
page_num: 1,
})
@@ -941,7 +962,6 @@ describe('reservation P0 views', () => {
expect(service.fetchReservationV4OrderTasks).toHaveBeenLastCalledWith(
expect.objectContaining({
order_task_status: 'OPEN',
card_status: 'REVIEW_REQUIRED',
page_num: 2,
page_size: 20,