This commit is contained in:
2026-08-12 09:25:55 +08:00
parent ffb2116741
commit fb542e0b6f
24 changed files with 983 additions and 75 deletions
@@ -33,6 +33,12 @@ describe('PortSchedulesView', () => {
expect(source).not.toContain('label="订舱 ID"')
})
it('hides inactive customers while preserving the customer bound to an existing schedule', () => {
expect(source).toContain("customer.status === 'ACTIVE' || String(customer.id) === String(form.customerId)")
expect(source).toContain('v-for="customer in selectableCustomers"')
expect(source).not.toContain(':disabled="customer.status !== \'ACTIVE\'')
})
it('protects candidate requests and exposes loading, empty, error and retry states', () => {
expect(source).toContain('customerRequestSerial')
expect(source).toContain('bookingRequestSerial')
@@ -34,6 +34,9 @@ const dialogOpen = ref(false); const dialogMode = ref<DialogMode>('create'); con
const rules: FormRules = { customerId: [{ required: true, message: '请选择客户', trigger: 'change' }], oceanBookingId: [{ required: true, message: '请选择订舱记录', trigger: 'change' }], sourceName: [{ required: true, message: '请输入信息来源', trigger: 'blur' }], sourceRef: [{ required: true, message: '请输入来源链接或参考号', trigger: 'blur' }], vesselName: [{ required: true, message: '请输入船名', trigger: 'blur' }], voyageNo: [{ required: true, message: '请输入航次', trigger: 'blur' }], departurePortCode: [{ required: true, message: '请输入起运港代码', trigger: 'blur' }], destinationPortCode: [{ required: true, message: '请输入目的港代码', trigger: 'blur' }], observedAt: [{ required: true, message: '请选择查询时间', trigger: 'change' }], expiresAt: [{ required: true, message: '请选择过期时间', trigger: 'change' }] }
const customers = ref<PlatformTenantResource[]>([]); const customersLoading = ref(false); const customersError = ref<ApiErrorDetails | null>(null); let customerRequestSerial = 0
const bookings = ref<OceanBookingResource[]>([]); const bookingsLoading = ref(false); const bookingsError = ref<ApiErrorDetails | null>(null); let bookingRequestSerial = 0
const selectableCustomers = computed(() => customers.value.filter((customer) => (
customer.status === 'ACTIVE' || String(customer.id) === String(form.customerId)
)))
const canCreate = computed(() => access.can('platform:firstleg:port-schedule:create'))
const dialogTitle = computed(() => dialogMode.value === 'create' ? '新增船期' : dialogMode.value === 'edit' ? '编辑船期' : '查看船期')
const dialogReadonly = computed(() => dialogMode.value === 'view')
@@ -60,7 +63,7 @@ watch([page, pageSize], () => void load()); onMounted(load)
</DataTable></section>
</ListPageLayout>
<AppDialog v-model="dialogOpen" :title="dialogTitle" width="820px" :confirm-loading="saving" :show-confirm="!dialogReadonly" :cancel-text="dialogReadonly ? '关闭' : '取消'" @confirm="save"><el-alert v-if="actionError" type="error" :closable="false"><ApiErrorMeta :details="actionError" /></el-alert><div v-if="customersError" class="selector-error"><span>{{ customersError.message }}</span><el-button link type="primary" @click="loadCustomers">重新加载客户</el-button></div><div v-if="bookingsError" class="selector-error"><span>{{ bookingsError.message }}</span><el-button link type="primary" @click="loadBookings()">重新加载订舱</el-button></div><el-form ref="formRef" :model="form" :rules="rules" :disabled="dialogReadonly" label-position="top" class="schedule-form"><el-row :gutter="16">
<el-col :xs="24" :sm="12"><el-form-item label="客户" prop="customerId"><el-select :model-value="form.customerId" filterable placeholder="请选择客户" :loading="customersLoading" no-data-text="暂无可用客户" style="width:100%" @update:model-value="handleCustomerChange(String($event || ''))"><el-option v-for="customer in customers" :key="String(customer.id)" :label="customerLabel(customer)" :value="String(customer.id)" :disabled="customer.status !== 'ACTIVE' && String(customer.id) !== String(form.customerId)" /></el-select></el-form-item></el-col>
<el-col :xs="24" :sm="12"><el-form-item label="客户" prop="customerId"><el-select :model-value="form.customerId" filterable placeholder="请选择客户" :loading="customersLoading" no-data-text="暂无可用客户" style="width:100%" @update:model-value="handleCustomerChange(String($event || ''))"><el-option v-for="customer in selectableCustomers" :key="String(customer.id)" :label="customerLabel(customer)" :value="String(customer.id)" /></el-select></el-form-item></el-col>
<el-col :xs="24" :sm="12"><el-form-item label="订舱记录" prop="oceanBookingId"><el-select v-model="form.oceanBookingId" filterable placeholder="请先选择客户,再选择订舱记录" :loading="bookingsLoading" :disabled="!form.customerId" no-data-text="该客户暂无订舱记录" style="width:100%"><el-option v-for="booking in bookings" :key="booking.id" :label="bookingLabel(booking)" :value="booking.id" /></el-select></el-form-item></el-col>
<el-col :xs="24" :sm="12"><el-form-item label="信息来源" prop="sourceName"><el-input v-model="form.sourceName" placeholder="如美森官网" /></el-form-item></el-col><el-col :xs="24" :sm="12"><el-form-item label="来源参考" prop="sourceRef"><el-input v-model="form.sourceRef" placeholder="查询链接或参考号" /></el-form-item></el-col>
<el-col :xs="24" :sm="12"><el-form-item label="船名" prop="vesselName"><el-input v-model="form.vesselName" placeholder="请输入船名" /></el-form-item></el-col><el-col :xs="24" :sm="12"><el-form-item label="航次" prop="voyageNo"><el-input v-model="form.voyageNo" placeholder="请输入航次" /></el-form-item></el-col>