实现V4 Rooming List轻量事项卡
This commit is contained in:
1 parent
61380a9ed8
commit
0c4f5e298d
7 files changed
+380
-6
No files matched your search
@@ -0,0 +1,124 @@
|
||||
<template>
|
||||
<section
|
||||
class="rooming-list-card"
|
||||
data-testid="rooming-list-card"
|
||||
>
|
||||
<div class="rooming-list-card__body">
|
||||
<div>
|
||||
<span>{{ t('taskV4.roomingList.targetOrder') }}</span>
|
||||
<strong>{{ orderLabel }}</strong>
|
||||
</div>
|
||||
<RouterLink
|
||||
v-if="boundOrderId"
|
||||
class="rooming-list-card__link"
|
||||
:to="`/reservation/orders/${boundOrderId}`"
|
||||
>
|
||||
{{ t('task.viewOrder') }}
|
||||
</RouterLink>
|
||||
</div>
|
||||
|
||||
<p>{{ t('taskV4.roomingList.description') }}</p>
|
||||
<small>{{ t('taskV4.roomingList.confirmHint') }}</small>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { RouterLink } from 'vue-router'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
orderReference?: string | null
|
||||
targetLocator?: string | null
|
||||
boundOrderId?: string | null
|
||||
boundOrderDisplayName?: string | null
|
||||
}>(), {
|
||||
orderReference: null,
|
||||
targetLocator: null,
|
||||
boundOrderId: null,
|
||||
boundOrderDisplayName: null,
|
||||
})
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const orderLabel = computed(() =>
|
||||
readableValue(props.boundOrderDisplayName) ||
|
||||
readableValue(props.orderReference) ||
|
||||
readableValue(props.targetLocator) ||
|
||||
t('taskV4.roomingList.unboundOrder'),
|
||||
)
|
||||
|
||||
function readableValue(value?: string | null): string {
|
||||
const trimmedValue = value?.trim() ?? ''
|
||||
return trimmedValue === '-' ? '' : trimmedValue
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.rooming-list-card {
|
||||
display: grid;
|
||||
gap: 12px;
|
||||
margin: 0 20px 18px;
|
||||
border: 1px solid var(--th-color-slate-200);
|
||||
border-radius: var(--th-radius-sm);
|
||||
background: var(--th-color-slate-50);
|
||||
padding: 14px;
|
||||
}
|
||||
|
||||
.rooming-list-card__body {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.rooming-list-card__body div {
|
||||
display: grid;
|
||||
gap: 4px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.rooming-list-card__body span {
|
||||
color: var(--th-color-slate-500);
|
||||
font-size: 12px;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.rooming-list-card__body strong {
|
||||
color: var(--th-color-slate-900);
|
||||
font-size: 14px;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
.rooming-list-card p,
|
||||
.rooming-list-card small {
|
||||
margin: 0;
|
||||
color: var(--th-color-slate-600);
|
||||
font-size: 13px;
|
||||
line-height: 1.55;
|
||||
}
|
||||
|
||||
.rooming-list-card small {
|
||||
color: var(--th-color-slate-500);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.rooming-list-card__link {
|
||||
color: var(--th-color-blue-600);
|
||||
flex: 0 0 auto;
|
||||
font-size: 13px;
|
||||
font-weight: 900;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.rooming-list-card__link:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
@media (max-width: 760px) {
|
||||
.rooming-list-card__body {
|
||||
align-items: flex-start;
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -46,8 +46,15 @@
|
||||
@update:model-value="emit('update:modelValue', $event)"
|
||||
/>
|
||||
<template v-else>
|
||||
<ReservationV4RoomingListCard
|
||||
v-if="isRoomingListCard"
|
||||
:order-reference="orderReference"
|
||||
:target-locator="targetLocator"
|
||||
:bound-order-id="boundOrderId"
|
||||
:bound-order-display-name="boundOrderDisplayName"
|
||||
/>
|
||||
<ReservationV4PaymentAttachmentPreview
|
||||
v-if="isPaymentCard"
|
||||
v-else-if="isPaymentCard"
|
||||
:card="card"
|
||||
:source-message-id="sourceMessageId"
|
||||
/>
|
||||
@@ -65,7 +72,7 @@
|
||||
</template>
|
||||
|
||||
<details
|
||||
v-if="!isRoomInformationCard && safePayloadRows.length"
|
||||
v-if="!isRoomInformationCard && !isRoomingListCard && safePayloadRows.length"
|
||||
class="safe-payload"
|
||||
>
|
||||
<summary>
|
||||
@@ -133,7 +140,7 @@
|
||||
{{ submitting ? t('taskV4.confirmingCard') : t('taskV4.confirmCard') }}
|
||||
</button>
|
||||
<RouterLink
|
||||
v-if="boundOrderId"
|
||||
v-if="boundOrderId && !isRoomingListCard"
|
||||
class="card-order-link"
|
||||
:to="`/reservation/orders/${boundOrderId}`"
|
||||
>
|
||||
@@ -155,7 +162,7 @@
|
||||
{{ submitting ? t('taskV4.confirmingCard') : t('taskV4.confirmCard') }}
|
||||
</button>
|
||||
<RouterLink
|
||||
v-if="boundOrderId"
|
||||
v-if="boundOrderId && !isRoomingListCard"
|
||||
class="card-order-link"
|
||||
:to="`/reservation/orders/${boundOrderId}`"
|
||||
>
|
||||
@@ -173,6 +180,7 @@ import { useI18n } from 'vue-i18n'
|
||||
import ReservationStatusBadge from '@/components/reservation/ReservationStatusBadge.vue'
|
||||
import ReservationV4PaymentAttachmentPreview from '@/components/reservation/ReservationV4PaymentAttachmentPreview.vue'
|
||||
import ReservationV4RoomInformationCard from '@/components/reservation/ReservationV4RoomInformationCard.vue'
|
||||
import ReservationV4RoomingListCard from '@/components/reservation/ReservationV4RoomingListCard.vue'
|
||||
import ReservationV4TaskCardFieldRenderer from '@/components/reservation/ReservationV4TaskCardFieldRenderer.vue'
|
||||
import type { ReservationRecord, ReservationV4TaskCardResult } from '@/types/reservation'
|
||||
import { formatReservationReadonlyReason } from '@/utils/reservationDisplay'
|
||||
@@ -196,6 +204,8 @@ const props = withDefaults(defineProps<{
|
||||
hotelId?: string
|
||||
orderTaskId?: string | null
|
||||
sourceMessageId?: string | null
|
||||
orderReference?: string | null
|
||||
targetLocator?: string | null
|
||||
actionErrors?: string[]
|
||||
successMessage?: string
|
||||
reviewForm?: ReviewForm
|
||||
@@ -203,11 +213,14 @@ const props = withDefaults(defineProps<{
|
||||
canConfirm: boolean
|
||||
canReview: boolean
|
||||
boundOrderId?: string | null
|
||||
boundOrderDisplayName?: string | null
|
||||
}>(), {
|
||||
validationErrors: () => ({}),
|
||||
hotelId: undefined,
|
||||
orderTaskId: null,
|
||||
sourceMessageId: null,
|
||||
orderReference: null,
|
||||
targetLocator: null,
|
||||
actionErrors: () => [],
|
||||
successMessage: '',
|
||||
reviewForm: () => ({
|
||||
@@ -215,6 +228,7 @@ const props = withDefaults(defineProps<{
|
||||
reason: '',
|
||||
}),
|
||||
boundOrderId: null,
|
||||
boundOrderDisplayName: null,
|
||||
})
|
||||
|
||||
const emit = defineEmits<{
|
||||
@@ -227,6 +241,7 @@ const emit = defineEmits<{
|
||||
const { t } = useI18n()
|
||||
const isRoomInformationCard = computed(() => props.card.card_type === 'ROOM_INFORMATION')
|
||||
const isPaymentCard = computed(() => props.card.card_type === 'PAYMENT')
|
||||
const isRoomingListCard = computed(() => props.card.card_type === 'ROOMING_LIST')
|
||||
const visibleFields = computed(() =>
|
||||
isPaymentCard.value
|
||||
? props.card.fields.filter((field) => !isPaymentAttachmentIdField(field))
|
||||
|
||||
Reference in new issue
Block a user