feat(fleet): select versioned sales quotes and isolate cost drafts

This commit is contained in:
2026-10-03 13:20:35 +08:00
parent 3440f9dd11
commit 5641ea0f7e
15 changed files with 288 additions and 31 deletions
@@ -19,7 +19,7 @@ const rows = ref<TripCost[]>([]), summary = ref<CostSummary | null>(null), page
const error = ref<ApiErrorDetails | null>(null), saveError = ref<ApiErrorDetails | null>(null)
const editorOpen = ref(false), editing = ref<TripCost | null>(null), opening = ref(false), saving = ref(false), reason = ref('')
const form = reactive<{ category: CostCategory; currency: CostCurrency; amount: number | null; incurredAt: string; remark: string }>({ category: 'FUEL', currency: 'USD', amount: null, incurredAt: '', remark: '' })
const pendingCreate = ref<{ key: string; details: TripCostDraft } | null>(null)
const pendingCreate = ref<{ tripNo: string; key: string; details: TripCostDraft } | null>(null)
const writable = computed(() => allowed.value && access.can(editing.value ? permission.tripCostEdit : permission.tripCostCreate))
const historyOpen = ref(false), historyNo = ref(''), historyPage = ref(1), historyTotal = ref(0), historyLoading = ref(false)
const histories = ref<TripCostHistory[]>([]), historyError = ref<ApiErrorDetails | null>(null)
@@ -66,13 +66,14 @@ async function load() {
finally { if (request === generation) loading.value = false }
}
watch([() => props.tripNo, page, size, allowed], (current, previous) => {
if (previous?.length && current[0] !== previous[0]) { scopeGeneration++; generation++; historyGeneration++; editorOpen.value = false; historyOpen.value = false; pendingCreate.value = null; opening.value = false; saving.value = false }
if (previous?.length && current[0] !== previous[0]) { scopeGeneration++; generation++; historyGeneration++; editorOpen.value = false; historyOpen.value = false; opening.value = false; saving.value = false }
if (!current[3]) { scopeGeneration++; editorOpen.value = false; historyOpen.value = false; histories.value = []; historyGeneration++; opening.value = false; saving.value = false }
if (previous?.length && (current[0] !== previous[0] || current[2] !== previous[2]) && page.value !== 1) { page.value = 1; return }
void load()
}, { immediate: true })
async function beginEdit(row: TripCost | null) {
if (opening.value || saving.value) return
if (pendingCreate.value && pendingCreate.value.tripNo !== props.tripNo) { ElMessage.warning(`请先返回车次 ${pendingCreate.value.tripNo} 核对上次登记结果`); return }
if (row && pendingCreate.value) { ElMessage.warning('请先重新打开登记窗口,核对上次保存结果'); return }
if (!allowed.value || !access.can(row ? permission.tripCostEdit : permission.tripCostCreate)) return
const scope = props.tripNo, request = scopeGeneration
@@ -86,7 +87,8 @@ async function beginEdit(row: TripCost | null) {
}
if (request !== scopeGeneration || scope !== props.tripNo) return
editing.value = current; reason.value = ''
if (!pendingCreate.value) Object.assign(form, current ? { ...current.details, remark: current.details.remark ?? '' }
if (pendingCreate.value) Object.assign(form, pendingCreate.value.details, { remark: pendingCreate.value.details.remark ?? '' })
else Object.assign(form, current ? { ...current.details, remark: current.details.remark ?? '' }
: { category: 'FUEL', currency: 'USD', amount: null, incurredAt: new Date().toISOString(), remark: '' })
lastSaved = current ? JSON.stringify(draft()) : ''
editorOpen.value = true
@@ -95,6 +97,7 @@ async function beginEdit(row: TripCost | null) {
}
async function save() {
if (saving.value || !writable.value) return
if (pendingCreate.value && pendingCreate.value.tripNo !== props.tripNo) return
if (form.amount == null || !Number.isFinite(form.amount) || form.amount < 0 || !form.incurredAt) { ElMessage.warning('请填写金额和实际发生时间'); return }
const details = pendingCreate.value?.details ?? draft()
if (editing.value && JSON.stringify(details) === lastSaved) { editorOpen.value = false; return }
@@ -103,7 +106,7 @@ async function save() {
try {
if (editing.value) await updateTripCost(scope, editing.value, details, reason.value)
else {
pendingCreate.value ??= { key: crypto.randomUUID(), details }
pendingCreate.value ??= { tripNo: scope, key: crypto.randomUUID(), details }
await createTripCost(scope, pendingCreate.value.key, pendingCreate.value.details)
}
if (scope !== props.tripNo || request !== scopeGeneration) return