feat(web-admin): add plan draft save UI and drafting follow-status filters
Rename pending tab to 待安排/草稿, default-select drafting, and add 暂存 with saveMode. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
ae91fbc63e
commit
3c7e3c69c5
4 files changed
+77
-9
No files matched your search
@@ -691,6 +691,7 @@ export interface PlanCreateFullPayload {
|
||||
followUserId?: string
|
||||
followUserName?: string
|
||||
remark?: string
|
||||
saveMode?: 'draft' | 'formal'
|
||||
supplierContracts?: PlanContractLink[]
|
||||
demanderContracts?: PlanContractLink[]
|
||||
load?: PlanLoad
|
||||
|
||||
@@ -254,6 +254,13 @@
|
||||
<el-button @click="innerVisible = false">
|
||||
<el-icon><Close /></el-icon>取消
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="showDraftButton"
|
||||
:loading="submitLoading"
|
||||
@click="handleSubmit('draft')"
|
||||
>
|
||||
<el-icon><Document /></el-icon>暂存
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="showSaveButton"
|
||||
:type="showSaveAndArrangeButton ? 'default' : 'primary'"
|
||||
@@ -320,6 +327,7 @@
|
||||
Check,
|
||||
Close,
|
||||
Delete,
|
||||
Document,
|
||||
DocumentCopy,
|
||||
FolderOpened,
|
||||
Plus,
|
||||
@@ -482,6 +490,7 @@
|
||||
/** 加载详情 / 重置时禁止计划产品 → 段产品自动对齐 */
|
||||
const suppressStopProductSync = ref(false)
|
||||
const loadedTransportStatus = ref('pending')
|
||||
const loadedFollowStatus = ref('')
|
||||
const loadForm = ref<PlanLoadForm>(createEmptyLoadForm())
|
||||
const deliveryForms = ref<PlanDeliveryForm[]>([createEmptyDeliveryForm(1)])
|
||||
const loadedPlanSnapshot = ref<Plan | null>(null)
|
||||
@@ -489,6 +498,12 @@
|
||||
|
||||
/** 已安排:禁止仅「保存」,须走保存并安排/调整运输,避免计划与地磅等联动字段脱节 */
|
||||
const showSaveButton = computed(() => loadedTransportStatus.value !== 'arranged')
|
||||
const showDraftButton = computed(() => {
|
||||
if (!isEdit.value) return true
|
||||
return (
|
||||
loadedFollowStatus.value === 'drafting' || loadedFollowStatus.value === 'pending_arrange'
|
||||
)
|
||||
})
|
||||
const showSaveAndArrangeButton = computed(() => {
|
||||
if (!isEdit.value) return true
|
||||
if (props.openForArrange && loadedTransportStatus.value === 'arranged') return false
|
||||
@@ -1577,6 +1592,7 @@
|
||||
|
||||
function resetForm() {
|
||||
loadedTransportStatus.value = 'pending'
|
||||
loadedFollowStatus.value = ''
|
||||
loadedPlanSnapshot.value = null
|
||||
vehicleBundle.value = null
|
||||
selectedDriverId.value = ''
|
||||
@@ -1669,6 +1685,7 @@
|
||||
})
|
||||
applyStopsFromPlan(data)
|
||||
loadedTransportStatus.value = data.transportStatus || 'pending'
|
||||
loadedFollowStatus.value = data.followStatus || ''
|
||||
loadedPlanSnapshot.value = data
|
||||
syncCommittedPartyIds()
|
||||
|
||||
@@ -1731,6 +1748,7 @@
|
||||
products: row.products.map((product) => ({ ...product, planProductId: undefined }))
|
||||
}))
|
||||
loadedTransportStatus.value = 'pending'
|
||||
loadedFollowStatus.value = ''
|
||||
syncCommittedPartyIds()
|
||||
|
||||
formData.products = await enrichPlanProductLinesAlias(
|
||||
@@ -1976,11 +1994,51 @@
|
||||
return missing
|
||||
}
|
||||
|
||||
async function handleSubmit(mode: 'save' | 'saveAndArrange' | 'rearrange' = 'save') {
|
||||
async function handleSubmit(
|
||||
mode: 'draft' | 'save' | 'saveAndArrange' | 'rearrange' = 'save'
|
||||
) {
|
||||
// 必须在 await validate 之前置位,避免连点并发多次 createFull
|
||||
if (submitLoading.value) return
|
||||
submitLoading.value = true
|
||||
try {
|
||||
if (mode === 'draft') {
|
||||
const companyOk = !!(formData.belongsCompany || '').trim()
|
||||
const dateOk = !!(formData.createDate || '').trim()
|
||||
if (!companyOk || !dateOk) {
|
||||
ElMessage.warning('暂存请至少填写归属公司和创建日期')
|
||||
await basicFormRef.value
|
||||
?.validateField?.(['belongsCompany', 'createDate'])
|
||||
.catch(() => undefined)
|
||||
return
|
||||
}
|
||||
mirrorPlanProductsFromLoad()
|
||||
const draftPayload = {
|
||||
...buildPlanCommonPayload({ allowClearTransport: isEdit.value }),
|
||||
saveMode: 'draft' as const,
|
||||
hasFreight: formData.hasFreight
|
||||
}
|
||||
let planId = props.planId || ''
|
||||
if (isEdit.value) {
|
||||
if (!props.planId) return
|
||||
await planApi.update(props.planId, draftPayload)
|
||||
planId = props.planId
|
||||
} else {
|
||||
const res = await planApi.createFull(
|
||||
draftPayload as Parameters<typeof planApi.createFull>[0]
|
||||
)
|
||||
const created = pickBxData<Plan>(res)
|
||||
planId = String(created?.id || '')
|
||||
if (!planId) {
|
||||
ElMessage.error('暂存成功但未返回计划 ID')
|
||||
return
|
||||
}
|
||||
}
|
||||
ElMessage.success('暂存成功')
|
||||
innerVisible.value = false
|
||||
emit('success', { planId, arranged: false })
|
||||
return
|
||||
}
|
||||
|
||||
const valid = await basicFormRef.value?.validate().catch(() => false)
|
||||
if (!valid || !validateBusinessRules()) return
|
||||
if (!validateSingleContractPerStop()) return
|
||||
@@ -2000,7 +2058,10 @@
|
||||
}
|
||||
|
||||
mirrorPlanProductsFromLoad()
|
||||
const commonPayload = buildPlanCommonPayload({ allowClearTransport: isEdit.value })
|
||||
const commonPayload = {
|
||||
...buildPlanCommonPayload({ allowClearTransport: isEdit.value }),
|
||||
saveMode: 'formal' as const
|
||||
}
|
||||
let planId = props.planId || ''
|
||||
const wasCreate = !isEdit.value
|
||||
|
||||
|
||||
@@ -366,7 +366,7 @@ export const PL_PLAN_TYPE_TABS: { label: string; name: PlPlanTypeTab }[] = [
|
||||
]
|
||||
|
||||
export const PL_PLAN_LIST_TABS: { label: string; name: PlPlanListTab }[] = [
|
||||
{ label: '待安排', name: 'pending_arrange' },
|
||||
{ label: '待安排/草稿', name: 'pending_arrange' }, // name 保留;语义含 drafting
|
||||
...PL_PLAN_TYPE_TABS
|
||||
]
|
||||
|
||||
@@ -384,7 +384,8 @@ export function plPlanListTabToFilters(tab: PlPlanListTab): {
|
||||
followStatus?: string
|
||||
} {
|
||||
if (tab === 'pending_arrange') {
|
||||
return { planType: '', followStatus: 'pending_arrange' }
|
||||
// followStatus 由列表 searchForm 控制(默认 pending_arrange + drafting)
|
||||
return { planType: '' }
|
||||
}
|
||||
return { planType: plPlanTypeTabToFilter(tab) }
|
||||
}
|
||||
@@ -419,6 +420,7 @@ export function plPlanTypeFilterToTab(planType?: string): PlPlanTypeTab {
|
||||
|
||||
/** 运输计划跟进状态选项(筛选) */
|
||||
export const PL_PLAN_FOLLOW_STATUS_OPTIONS = [
|
||||
{ label: '草稿中', value: 'drafting' },
|
||||
{ label: '等通知', value: 'wait_notice' },
|
||||
{ label: '待安排', value: 'pending_arrange' },
|
||||
{ label: '已安排', value: 'arranged' },
|
||||
@@ -433,6 +435,7 @@ export const PL_PLAN_DEFAULT_FOLLOW_STATUSES = PL_PLAN_FOLLOW_STATUS_OPTIONS.fil
|
||||
|
||||
export function getPlPlanFollowStatusTagType(status?: string) {
|
||||
const map: Record<string, string> = {
|
||||
drafting: 'info',
|
||||
wait_notice: 'warning',
|
||||
pending_arrange: 'danger',
|
||||
arranged: 'success',
|
||||
@@ -447,6 +450,7 @@ export function getPlPlanFollowStatusTagType(status?: string) {
|
||||
|
||||
export function getPlPlanFollowStatusLabel(status?: string) {
|
||||
const map: Record<string, string> = {
|
||||
drafting: '草稿中',
|
||||
wait_notice: '等通知',
|
||||
pending_arrange: '待安排',
|
||||
arranged: '已安排',
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
placeholder="全部"
|
||||
clearable
|
||||
multiple
|
||||
:disabled="activePlanTypeTab === 'pending_arrange'"
|
||||
>
|
||||
<el-option
|
||||
v-for="opt in PL_PLAN_FOLLOW_STATUS_OPTIONS"
|
||||
@@ -1154,7 +1153,7 @@ tableEl: planTableWrapRef,
|
||||
const searchForm = reactive({
|
||||
planNo: '',
|
||||
planType: '',
|
||||
followStatus: ['pending_arrange'] as string[],
|
||||
followStatus: ['pending_arrange', 'drafting'] as string[],
|
||||
customerName: '',
|
||||
productName: '',
|
||||
licensePlate: ''
|
||||
@@ -1214,7 +1213,7 @@ tableEl: planTableWrapRef,
|
||||
const filters = plPlanListTabToFilters(nextTab)
|
||||
searchForm.planType = filters.planType
|
||||
if (nextTab === 'pending_arrange') {
|
||||
searchForm.followStatus = ['pending_arrange']
|
||||
searchForm.followStatus = ['pending_arrange', 'drafting']
|
||||
} else if (prevTab === 'pending_arrange') {
|
||||
searchForm.followStatus = [...PL_PLAN_DEFAULT_FOLLOW_STATUSES]
|
||||
}
|
||||
@@ -1226,7 +1225,7 @@ tableEl: planTableWrapRef,
|
||||
activePlanTypeTab.value = 'pending_arrange'
|
||||
searchForm.planNo = ''
|
||||
searchForm.planType = ''
|
||||
searchForm.followStatus = ['pending_arrange']
|
||||
searchForm.followStatus = ['pending_arrange', 'drafting']
|
||||
searchForm.customerName = ''
|
||||
searchForm.productName = ''
|
||||
searchForm.licensePlate = ''
|
||||
@@ -1317,7 +1316,10 @@ tableEl: planTableWrapRef,
|
||||
}
|
||||
|
||||
function canWaitNotice(row: Plan) {
|
||||
return row.followStatus === 'pending_arrange' && row.archiveStatus !== 'archived'
|
||||
return (
|
||||
(row.followStatus === 'pending_arrange' || row.followStatus === 'drafting') &&
|
||||
row.archiveStatus !== 'archived'
|
||||
)
|
||||
}
|
||||
|
||||
function canCancelWaitNotice(row: Plan) {
|
||||
|
||||
Reference in new issue
Block a user