50 lines
3.0 KiB
Vue
50 lines
3.0 KiB
Vue
<script setup lang="ts">
|
||
import { inject, onBeforeUnmount, ref } from 'vue'
|
||
import { onBeforeRouteLeave, onBeforeRouteUpdate } from 'vue-router'
|
||
import { ElMessage } from 'element-plus'
|
||
import AppDialog from '@/shared/components/AppDialog.vue'
|
||
import LoadErrorState from '@/shared/components/LoadErrorState.vue'
|
||
import { getApiErrorDetails, type ApiErrorDetails } from '@/shared/api/http'
|
||
import { useAccessPreviewStore } from '@/core/access/accessPreview'
|
||
import { deleteAppointment } from './api'
|
||
const props = defineProps<{ appointmentNo: string; version: number; selectedCount: number }>()
|
||
const emit = defineEmits<{ close: []; cancelled: [] }>()
|
||
const access = useAccessPreviewStore()
|
||
const reason = ref(''), saving = ref(false), error = ref<ApiErrorDetails | null>(null)
|
||
const pending = ref<{number: string; version: number; reason: string} | null>(null)
|
||
function canLeave() {
|
||
if (saving.value || pending.value) { ElMessage.warning('请先核实本次取消结果,再关闭或切换'); return false }
|
||
return true
|
||
}
|
||
function beforeClose(done: () => void) { if (canLeave()) done() }
|
||
function close() { if (canLeave()) emit('close') }
|
||
const register = inject<((guard: () => boolean) => () => void) | undefined>('appointment-register-leave-guard', undefined)
|
||
const unregister = register?.(canLeave)
|
||
onBeforeUnmount(() => unregister?.())
|
||
onBeforeRouteLeave(canLeave)
|
||
onBeforeRouteUpdate(canLeave)
|
||
async function save() {
|
||
if (!access.can('platform:appointment:delete') || saving.value) return
|
||
if (!pending.value && !reason.value.trim()) { ElMessage.warning('请填写实际取消原因'); return }
|
||
pending.value ||= {number: props.appointmentNo, version: props.version, reason: reason.value.trim()}
|
||
saving.value = true; error.value = null
|
||
try {
|
||
await deleteAppointment(pending.value.number, pending.value.version, pending.value.reason)
|
||
pending.value = null; emit('cancelled')
|
||
} catch (cause) {
|
||
error.value = getApiErrorDetails(cause, '取消结果待核实,请重试本次取消')
|
||
if (['400','403','404','409','A0400','A0403','A0404','A0409'].includes(error.value.code || '')) pending.value = null
|
||
} finally { saving.value = false }
|
||
}
|
||
</script>
|
||
<template>
|
||
<AppDialog :model-value="true" title="取消预约" width="min(600px, calc(100vw - 48px))" class="admin-density-dialog" confirm-type="danger" :confirm-text="pending ? '重试本次取消' : '确认取消预约'" :show-confirm="access.can('platform:appointment:delete')" :confirm-loading="saving" :before-close="beforeClose" @update:model-value="close" @confirm="save">
|
||
<p>{{ appointmentNo }} · {{ selectedCount }} 票货物</p>
|
||
<p>取消后释放货物占用,保留原备注、时间及取消历史;不会回退库存或取消车次。</p>
|
||
<LoadErrorState v-if="error" title="取消未完成" :message="error.message" :show-retry="false" />
|
||
<el-form label-position="top" :disabled="saving || !!pending">
|
||
<el-form-item label="取消原因" required><el-input v-model="reason" type="textarea" maxlength="1000" show-word-limit /></el-form-item>
|
||
</el-form>
|
||
</AppDialog>
|
||
</template>
|