实现房表生成第二种名单样式
This commit is contained in:
1 parent
14eda89b0b
commit
eaea8787d9
22 files changed
+645
-64
No files matched your search
@@ -116,6 +116,56 @@
|
||||
{{ fieldError('roomType') }}
|
||||
</span>
|
||||
</label>
|
||||
|
||||
<div class="field">
|
||||
<label
|
||||
for="rooming-list-arrival-input"
|
||||
class="field-label"
|
||||
>
|
||||
{{ t('roomingList.arrival') }}
|
||||
</label>
|
||||
<LocalizedDatePicker
|
||||
v-model="form.arrival"
|
||||
input-id="rooming-list-arrival-input"
|
||||
test-id="rooming-list-arrival"
|
||||
:placeholder="t('roomingList.datePlaceholder')"
|
||||
:input-class="validationClass('arrival')"
|
||||
:invalid="Boolean(fieldError('arrival'))"
|
||||
:described-by="fieldError('arrival') ? fieldErrorId('arrival') : undefined"
|
||||
/>
|
||||
<span
|
||||
v-if="fieldError('arrival')"
|
||||
:id="fieldErrorId('arrival')"
|
||||
class="field-error"
|
||||
>
|
||||
{{ fieldError('arrival') }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label
|
||||
for="rooming-list-departure-input"
|
||||
class="field-label"
|
||||
>
|
||||
{{ t('roomingList.departure') }}
|
||||
</label>
|
||||
<LocalizedDatePicker
|
||||
v-model="form.departure"
|
||||
input-id="rooming-list-departure-input"
|
||||
test-id="rooming-list-departure"
|
||||
:placeholder="t('roomingList.datePlaceholder')"
|
||||
:input-class="validationClass('departure')"
|
||||
:invalid="Boolean(fieldError('departure'))"
|
||||
:described-by="fieldError('departure') ? fieldErrorId('departure') : undefined"
|
||||
/>
|
||||
<span
|
||||
v-if="fieldError('departure')"
|
||||
:id="fieldErrorId('departure')"
|
||||
class="field-error"
|
||||
>
|
||||
{{ fieldError('departure') }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -252,6 +302,7 @@
|
||||
import { computed, reactive, ref } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
import LocalizedDatePicker from '@/components/common/LocalizedDatePicker.vue'
|
||||
import {
|
||||
generateReservationRoomingListExcel,
|
||||
RoomingListGenerationError,
|
||||
@@ -264,6 +315,8 @@ type AlertTone = 'success' | 'error'
|
||||
type RoomingListFieldKey =
|
||||
| 'file'
|
||||
| 'peoplePerRoom'
|
||||
| 'arrival'
|
||||
| 'departure'
|
||||
| 'roomType'
|
||||
| 'paymentType'
|
||||
| 'nationality'
|
||||
@@ -277,6 +330,8 @@ const authStore = useAuthStore()
|
||||
const form = reactive({
|
||||
file: null as File | null,
|
||||
peoplePerRoom: '',
|
||||
arrival: '',
|
||||
departure: '',
|
||||
roomType: '',
|
||||
paymentType: 'BTQR',
|
||||
nationality: '',
|
||||
@@ -340,6 +395,8 @@ function buildGenerationInput(file: File): RoomingListGenerationInput {
|
||||
file,
|
||||
hotelId: authStore.selectedHotelId,
|
||||
peoplePerRoom: Number(form.peoplePerRoom),
|
||||
arrival: form.arrival,
|
||||
departure: form.departure,
|
||||
roomType: form.roomType,
|
||||
paymentType: form.paymentType,
|
||||
nationality: form.nationality,
|
||||
@@ -387,6 +444,21 @@ function collectFieldErrors(): FieldErrors {
|
||||
if (!isPositiveInteger(form.peoplePerRoom)) {
|
||||
errors.peoplePerRoom = t('roomingList.fieldErrors.positiveInteger')
|
||||
}
|
||||
const arrival = form.arrival.trim()
|
||||
const departure = form.departure.trim()
|
||||
if (arrival && !isDateInputValue(arrival)) {
|
||||
errors.arrival = t('roomingList.fieldErrors.dateFormat')
|
||||
}
|
||||
if (departure && !isDateInputValue(departure)) {
|
||||
errors.departure = t('roomingList.fieldErrors.dateFormat')
|
||||
}
|
||||
if (arrival && !departure) {
|
||||
errors.departure = t('roomingList.fieldErrors.departureRequired')
|
||||
} else if (!arrival && departure) {
|
||||
errors.arrival = t('roomingList.fieldErrors.arrivalRequired')
|
||||
} else if (arrival && departure && isDateInputValue(arrival) && isDateInputValue(departure) && departure <= arrival) {
|
||||
errors.departure = t('roomingList.fieldErrors.departureAfterArrival')
|
||||
}
|
||||
if (!form.roomType.trim()) {
|
||||
errors.roomType = t('roomingList.fieldErrors.roomTypeRequired')
|
||||
}
|
||||
@@ -438,6 +510,14 @@ function isPositiveInteger(value: unknown): boolean {
|
||||
return /^[1-9]\d*$/.test(normalizeInputText(value))
|
||||
}
|
||||
|
||||
function isDateInputValue(value: string): boolean {
|
||||
if (!/^\d{4}-\d{2}-\d{2}$/.test(value)) {
|
||||
return false
|
||||
}
|
||||
const date = new Date(`${value}T00:00:00Z`)
|
||||
return !Number.isNaN(date.getTime()) && date.toISOString().slice(0, 10) === value
|
||||
}
|
||||
|
||||
function normalizeInputText(value: unknown): string {
|
||||
if (typeof value === 'string') {
|
||||
return value.trim()
|
||||
|
||||
Reference in new issue
Block a user