91 lines
6.2 KiB
Vue
91 lines
6.2 KiB
Vue
<script setup lang="ts">
|
|
import '@/styles/adminDensity.css'
|
|
import '../overseasBusinessDensity.css'
|
|
import { computed, reactive, ref, watch, onBeforeUnmount } from 'vue'
|
|
import { useRoute, useRouter } from 'vue-router'
|
|
import { ArrowLeft, Save } from '@lucide/vue'
|
|
import { ElMessage } from 'element-plus'
|
|
import AdminPage from '@/shared/components/AdminPage.vue'
|
|
import ApiErrorMeta from '@/shared/components/ApiErrorMeta.vue'
|
|
import DetailPageLayout from '@/shared/components/DetailPageLayout.vue'
|
|
import FormSection from '@/shared/components/FormSection.vue'
|
|
import LoadErrorState from '@/shared/components/LoadErrorState.vue'
|
|
import OperationErrorToast from '@/shared/components/OperationErrorToast.vue'
|
|
import { getApiErrorDetails, type ApiErrorDetails } from '@/shared/api/http'
|
|
import { useUnsavedWorkRegistration } from '@/core/unsaved-work/useUnsavedWorkRegistration'
|
|
import { useAccessPreviewStore } from '@/core/access/accessPreview'
|
|
import { getOverseasPurchaseEditor, updateOverseasPurchase } from '../api/platformOverseasBusiness'
|
|
import { overseasBusinessPermissions } from '../permissions'
|
|
|
|
const route = useRoute(); const router = useRouter(); const access = useAccessPreviewStore()
|
|
const purchaseId = computed(() => typeof route.params.purchaseId === 'string' ? route.params.purchaseId : '')
|
|
const customerId = computed(() => typeof route.query.customerId === 'string' ? route.query.customerId : '')
|
|
const loading = ref(false); const saving = ref(false); const loaded = ref(false)
|
|
const loadError = ref<ApiErrorDetails | null>(null); const submitError = ref<ApiErrorDetails | null>(null)
|
|
const initial = () => ({ orderNo: '', productName: '', purchaseQuantity: 0, remark: '', version: 0 })
|
|
const form = reactive(initial())
|
|
const canSave = computed(() => loaded.value && !loading.value && !saving.value && access.can(overseasBusinessPermissions.purchaseEdit))
|
|
const { captureBaseline } = useUnsavedWorkRegistration({ key: 'overseas-purchase-editor', label: computed(() => '编辑采购内部备注'), isOpen: computed(() => true), model: form, requiredPermission: computed(() => overseasBusinessPermissions.purchaseEdit) })
|
|
let generation = 0
|
|
onBeforeUnmount(() => { generation++ })
|
|
function back() { return router.push({ name: 'overseas-business-purchase-detail', params: { purchaseId: purchaseId.value }, query: { customerId: customerId.value } }) }
|
|
async function load() {
|
|
const current = ++generation; const orderNo = purchaseId.value; const customer = customerId.value
|
|
loaded.value = false; loading.value = false; Object.assign(form, initial()); captureBaseline()
|
|
loadError.value = null; submitError.value = null
|
|
if (!orderNo || !customer) { loadError.value = { message: '缺少采购订单号或客户标识,请从采购订单列表重新进入' }; return }
|
|
loading.value = true
|
|
try {
|
|
const detail = await getOverseasPurchaseEditor(orderNo, customer)
|
|
if (current !== generation) return
|
|
const order = detail.customerView.order
|
|
Object.assign(form, { orderNo: order.orderNo, productName: order.productName, purchaseQuantity: order.purchaseQuantity, version: order.version, remark: detail.internalRemark ?? '' })
|
|
loaded.value = true; captureBaseline()
|
|
} catch (error) { if (current === generation) loadError.value = getApiErrorDetails(error, '采购订单加载失败') }
|
|
finally { if (current === generation) loading.value = false }
|
|
}
|
|
async function save() {
|
|
if (!canSave.value) return
|
|
saving.value = true; submitError.value = null
|
|
const current = generation; const orderNo = purchaseId.value; const customer = customerId.value
|
|
try {
|
|
const updated = await updateOverseasPurchase(orderNo, customer, { version: form.version, remark: form.remark.trim() || null })
|
|
if (current !== generation) return
|
|
form.version = updated.version; captureBaseline(); ElMessage.success('内部备注已保存')
|
|
await back()
|
|
} catch (error) { if (current === generation) submitError.value = getApiErrorDetails(error, '内部备注保存失败') }
|
|
finally { saving.value = false }
|
|
}
|
|
watch([purchaseId, customerId], () => { void load() }, { immediate: true })
|
|
</script>
|
|
|
|
<template>
|
|
<AdminPage class="admin-density overseas-business-editor">
|
|
<OperationErrorToast :details="submitError" />
|
|
<DetailPageLayout class="overseas-business-form-layout" aria-label="编辑采购内部备注">
|
|
<template #header><div class="density-detail-header"><div class="density-detail-heading">
|
|
<el-button link class="density-back" @click="back"><ArrowLeft :size="16" />返回详情</el-button>
|
|
<h1 class="density-detail-title">编辑采购内部备注</h1><span class="form-reference">{{ form.orderNo || '—' }}</span>
|
|
</div></div></template>
|
|
<LoadErrorState v-if="loadError" title="采购订单暂时无法编辑" :message="loadError.message" :loading="loading" @retry="load"><template #details><ApiErrorMeta :details="loadError" /></template></LoadErrorState>
|
|
<section v-else v-loading="loading" class="form-panel">
|
|
<el-form class="density-form" :model="form" label-position="top" :disabled="!canSave">
|
|
<FormSection class="density-form-section" title="采购订单"><div class="form-grid">
|
|
<el-form-item label="订单编号"><el-input :model-value="form.orderNo || '—'" readonly /></el-form-item>
|
|
<el-form-item label="商品名称"><el-input :model-value="form.productName || '—'" readonly /></el-form-item>
|
|
<el-form-item label="采购数量"><el-input :model-value="loaded ? form.purchaseQuantity : '—'" readonly /></el-form-item>
|
|
<el-form-item class="span-3" label="内部备注"><el-input v-model="form.remark" type="textarea" :rows="4" maxlength="1000" show-word-limit placeholder="仅供平台内部使用" /></el-form-item>
|
|
</div></FormSection>
|
|
</el-form>
|
|
<footer class="form-actions"><el-button type="primary" :disabled="!canSave" :loading="saving" @click="save"><Save :size="16" />保存</el-button><el-button :disabled="saving" @click="back">取消</el-button></footer>
|
|
</section>
|
|
</DetailPageLayout>
|
|
</AdminPage>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.form-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
|
|
.span-3 { grid-column: 1 / -1; }
|
|
@media (max-width: 760px) { .form-grid { grid-template-columns: 1fr; } }
|
|
</style>
|