import { defineStore } from 'pinia' import { ref, reactive, computed } from 'vue' import type { ChangeOrderFormData, ForecastChangeOrder } from '@/types/forecast-order' import type { ForecastOrder } from '@/types/forecast-order' import { getById } from '@/api/forecast/forecast-order' import { saveChangeDraft, submitChange, approveChange, rejectChange, withdrawChangeOrder, findChangeOrdersByForecastId } from '@/api/forecast/forecast-order' import { useI18n } from 'vue-i18n' import { reqIsSucceed } from '@/utils' import { handleApiError } from '@/utils/errorHandler' import { ElMessage } from 'element-plus' export const useChangeOrderFormStore = defineStore('changeOrderForm', () => { const { t } = useI18n() // State const changeFcId = ref(undefined) const changeId = ref(undefined) const snapshot = ref<{ originalCustomerCode?: string originalCustomerName?: string originalDeliveryDate?: string originalCustomsManualNo?: string remainingQty?: number standardization?: string foStatus?: string }>({}) const changeForm = reactive({ forecastOrderId: 0, }) const errors = ref>({}) const submitting = ref(false) const actionLoading = ref(false) const modalVisible = ref(false) const approveModalVisible = ref(false) const changeList = ref([]) const forecastOrder = ref(null) // Getters const canRelease = computed(() => { if (!snapshot.value.standardization || !snapshot.value.foStatus) return false return snapshot.value.standardization === 'STANDARD' && (snapshot.value.foStatus === 'SCHEDULED' || snapshot.value.foStatus === 'PARTIAL_CONVERTED') }) /** * 是否有未完成的变更单(草稿/已提交状态) */ const hasPendingChange = computed(() => { return changeList.value.some( (ch) => ch.coStatus === 'DRAFT' || ch.coStatus === 'SUBMITTED', ) }) // Actions async function initCreate(forecastOrderId: number) { changeFcId.value = forecastOrderId changeId.value = undefined Object.assign(changeForm, { changeOrderId: undefined, forecastOrderId, newCustomerCode: '', newCustomerName: '', newDeliveryDate: '', newCustomsManualNo: '', releaseQty: undefined, rejectReason: '', versionNum: undefined, }) errors.value = {} // 加载预测单详情作为快照 try { const res = await getById(forecastOrderId) if (reqIsSucceed(res)) { const fo = res.data forecastOrder.value = fo snapshot.value = { originalCustomerCode: fo.customerCode, originalCustomerName: fo.customerName, originalDeliveryDate: fo.expectedDeliveryDate, originalCustomsManualNo: fo.customsManualNo || '', remainingQty: (fo.actualScheduleQty || 0) - (fo.convertedQty || 0) - (fo.releasedQty || 0), standardization: fo.standardization, foStatus: fo.foStatus, productCode: fo.productCode, } } } catch (error: unknown) { const err = error as { code?: string; message?: string } handleApiError(err?.code || 'unknown', err?.message) } modalVisible.value = true } function updateField(field: K, value: ChangeOrderFormData[K]) { changeForm[field] = value if (errors.value[field]) { delete errors.value[field] } } function validateForm(): boolean { errors.value = {} if (changeForm.releaseQty !== undefined && changeForm.releaseQty !== null) { const remaining = snapshot.value.remainingQty || 0 if (changeForm.releaseQty > remaining) { errors.value.releaseQty = t('forecast-order.validation.releaseQtyExceeded', { remaining }) } if (changeForm.releaseQty <= 0) { errors.value.releaseQty = t('forecast-order.validation.releaseQtyPositive') } } // 至少填写一个变更字段 if (!changeForm.newCustomerCode && !changeForm.newDeliveryDate && !changeForm.newCustomsManualNo && !changeForm.releaseQty) { errors.value.general = t('forecast-order.validation.noChangeField') } return Object.keys(errors.value).length === 0 } async function saveDraftAction() { if (submitting.value) return submitting.value = true try { const res = await saveChangeDraft({ params: { ...changeForm } }) if (reqIsSucceed(res)) { ElMessage.success(t('forecast-order.toast.changeDraftSaved')) modalVisible.value = false return res.data } else { handleApiError(res.code, res.msg) } } catch (error: unknown) { const err = error as { code?: string; message?: string } handleApiError(err?.code || 'unknown', err?.message) } finally { submitting.value = false } } async function submitAction() { if (!validateForm()) return if (submitting.value) return submitting.value = true try { const res = await submitChange({ params: { ...changeForm } }) if (reqIsSucceed(res)) { ElMessage.success(t('forecast-order.toast.changeSubmitted')) modalVisible.value = false return res.data } else { handleApiError(res.code, res.msg) } } catch (error: unknown) { const err = error as { code?: string; message?: string } handleApiError(err?.code || 'unknown', err?.message) } finally { submitting.value = false } } async function doApprove(changeOrderId: number) { actionLoading.value = true try { const res = await approveChange(changeOrderId) if (reqIsSucceed(res)) { ElMessage.success(t('forecast-order.toast.approveSuccess')) approveModalVisible.value = false return res.data } else { handleApiError(res.code, res.msg) } } catch (error: unknown) { const err = error as { code?: string; message?: string } handleApiError(err?.code || 'unknown', err?.message) } finally { actionLoading.value = false } } async function doReject(changeOrderId: number, rejectReason?: string) { actionLoading.value = true try { const res = await rejectChange({ params: { changeOrderId, rejectReason } }) if (reqIsSucceed(res)) { ElMessage.success(t('forecast-order.toast.rejectSuccess')) approveModalVisible.value = false return res.data } else { handleApiError(res.code, res.msg) } } catch (error: unknown) { const err = error as { code?: string; message?: string } handleApiError(err?.code || 'unknown', err?.message) } finally { actionLoading.value = false } } async function doWithdraw(changeOrderId: number) { actionLoading.value = true try { const res = await withdrawChangeOrder(changeOrderId) if (reqIsSucceed(res)) { ElMessage.success(t('forecast-order.toast.withdrawSuccess')) } else { handleApiError(res.code, res.msg) } } catch (error: unknown) { const err = error as { code?: string; message?: string } handleApiError(err?.code || 'unknown', err?.message) } finally { actionLoading.value = false } } async function fetchChangeList(forecastOrderId: number) { try { const res = await findChangeOrdersByForecastId(forecastOrderId) if (reqIsSucceed(res)) { changeList.value = res.data || [] } } catch (error: unknown) { const err = error as { code?: string; message?: string } handleApiError(err?.code || 'unknown', err?.message) } } function reset() { changeFcId.value = undefined changeId.value = undefined snapshot.value = {} errors.value = {} submitting.value = false modalVisible.value = false approveModalVisible.value = false changeList.value = [] } return { changeFcId, changeId, snapshot, changeForm, errors, submitting, actionLoading, modalVisible, approveModalVisible, changeList, forecastOrder, canRelease, hasPendingChange, initCreate, updateField, validateForm, saveDraftAction, submitAction, doApprove, doReject, doWithdraw, fetchChangeList, reset, } })