feat: 为现场日历增加单据类型并同步派车记录,减少重复录入。

按起止地关键词生成现场出入库计划,同步来源修改和删除,保留历史记录不补录。调整卡片及复制文本的单据类型格式,并记录测试库和生产库迁移结果。
This commit is contained in:
wuzilong committed 2026-10-30 14:04:24 +08:00
1 parent 1ae8603045
commit fcbf8a9b65
17 files changed
+732 -42

No files matched your search

@@ -10,10 +10,12 @@ export interface DispatchCalendarBody {
destination: string
quantity: string | null
calendarKind: DispatchCalendarKind
documentType?: string | null
remark: string
}
export interface DispatchCalendarRecord extends DispatchCalendarBody {
id: string
sourceRecordId?: string | null
licensePlate: string
productName: string
createTime: string | null
@@ -171,11 +171,7 @@
:label="row.productName" /></el-select
></el-form-item>
<el-form-item label="数量"
><el-input
v-model="draft.quantity"
maxlength="15"
placeholder="选填"
aria-label="数量"
><el-input v-model="draft.quantity" maxlength="15" placeholder="选填" aria-label="数量"
><template #append>吨</template></el-input
></el-form-item
>
@@ -185,6 +181,21 @@
<el-form-item label="目的地"
><el-input v-model="draft.destination" maxlength="255" placeholder="请输入目的地"
/></el-form-item>
<el-form-item v-if="calendarKind === 'site'" label="单据类型" class="form-col-span-2">
<el-select
v-model="draft.documentType"
filterable
allow-create
default-first-option
clearable
popper-class="manual-calendar-popper"
placeholder="选择或输入单据类型"
aria-label="单据类型"
>
<el-option label="入库计划" value="入库计划" />
<el-option label="出库计划" value="出库计划" />
</el-select>
</el-form-item>
<el-form-item label="备注" class="form-col-span-2"
><el-input
v-model="draft.remark"
@@ -222,7 +233,7 @@
width="720px"
>
<template #hero-actions
><template v-if="canManage && viewRow"
><template v-if="canManage && viewRow && !viewRow.sourceRecordId"
><el-button :disabled="saving" @click="editViewed"
><el-icon><Edit /></el-icon>编辑</el-button
><el-button type="danger" :disabled="saving" @click="deleteViewed"
@@ -302,6 +313,18 @@
fields: viewRow.value
? [
{ label: '派车日期', value: viewRow.value.dispatchDate },
...(viewRow.value.sourceRecordId
? [{ label: '维护方式', value: '自动同步,请在来源派车日历修改或删除' }]
: []),
...(viewRow.value.calendarKind === 'site'
? [
{
label: '单据类型',
value: viewRow.value.documentType,
tagType: 'primary' as const
}
]
: []),
{ label: '车牌号', value: viewRow.value.licensePlate },
{ label: '产品名称', value: viewRow.value.productName },
{ label: '始发地', value: viewRow.value.origin },
@@ -35,7 +35,12 @@
}}</span>
1车</span
>
<span class="route">{{ group.rows[0].origin }} → {{ group.rows[0].destination }}</span>
<span class="route"
>{{ group.rows[0].origin }} → {{ group.rows[0].destination
}}<template v-if="group.rows[0].calendarKind === 'site' && group.rows[0].documentType"
>({{ group.rows[0].documentType }})</template
></span
>
<span v-if="group.rows[0].remark" class="remark">{{ group.rows[0].remark }}</span>
</component>
<div v-else class="pair">
@@ -58,7 +63,10 @@
><span v-if="manualQuantityLabel(row.quantity)" class="quantity">{{
' ' + manualQuantityLabel(row.quantity)
}}</span>
· {{ row.origin }} → {{ row.destination }}</span
· {{ row.origin }} → {{ row.destination
}}<template v-if="row.calendarKind === 'site' && row.documentType"
>({{ row.documentType }})</template
></span
>
<span v-if="row.remark" class="remark">{{ row.remark }}</span>
</component>
@@ -37,10 +37,7 @@ function byCreateOrder(a: DispatchCalendarRecord, b: DispatchCalendarRecord) {
}
function groupByVehicle(rows: DispatchCalendarRecord[]) {
const vehicles = new Map<
string,
{ id: string; plate: string; rows: DispatchCalendarRecord[] }
>()
const vehicles = new Map<string, { id: string; plate: string; rows: DispatchCalendarRecord[] }>()
for (const row of rows) {
if (!vehicles.has(row.vehicleId))
vehicles.set(row.vehicleId, { id: row.vehicleId, plate: row.licensePlate, rows: [] })
@@ -91,9 +88,13 @@ export function manualQuantityLabel(quantity?: string | null) {
}
function manualTripText(row: DispatchCalendarRecord) {
const remark = row.remark ? `(${row.remark.replace(/\s+/g, ' ')})` : ''
const quantity = manualQuantityLabel(row.quantity)
const amount = quantity ? ` ${quantity}` : ''
if (row.calendarKind === 'site') {
const documentType = (row.documentType || '').trim().replace(/\s+/g, ' ')
return `${row.productName}${amount}${documentType ? `(${documentType})` : ''}`
}
const remark = row.remark ? `(${row.remark.replace(/\s+/g, ' ')})` : ''
return `${row.productName}${amount} ${row.origin} → ${row.destination}${remark}`
}
@@ -123,5 +124,6 @@ export function validateManualDraft(body: DispatchCalendarBody): string {
if (manualQuantityText(body.quantity) && !QUANTITY_TEXT.test(manualQuantityText(body.quantity)))
return '数量须为数字,最多两位小数'
if (body.remark.length > 500) return '备注最多500个字符'
if ((body.documentType || '').trim().length > 50) return '单据类型最多50个字符'
return ''
}
@@ -54,6 +54,7 @@ export function useManualCalendar(
destination: '',
quantity: '',
calendarKind,
documentType: '',
remark: ''
})
const draft = reactive<DispatchCalendarBody>(blank(selectedDate.value))
@@ -113,7 +114,7 @@ export function useManualCalendar(
editorOpen.value = true
}
function openEdit(row: DispatchCalendarRecord) {
if (!canManage.value || !live() || saving.value) return
if (!canManage.value || !live() || saving.value || row.sourceRecordId) return
editingId.value = row.id
Object.assign(draft, {
dispatchDate: row.dispatchDate,
@@ -123,6 +124,7 @@ export function useManualCalendar(
destination: row.destination,
quantity: row.quantity || '',
calendarKind,
documentType: row.documentType || '',
remark: row.remark || ''
})
formError.value = ''
@@ -139,6 +141,7 @@ export function useManualCalendar(
origin: draft.origin.trim(),
destination: draft.destination.trim(),
quantity: (draft.quantity || '').trim(),
documentType: (draft.documentType || '').trim(),
remark: draft.remark.trim()
}
try {
@@ -157,7 +160,7 @@ export function useManualCalendar(
}
}
async function remove(row: DispatchCalendarRecord) {
if (!live() || !canManage.value || saving.value) return false
if (!live() || !canManage.value || saving.value || row.sourceRecordId) return false
saving.value = true
try {
await dispatchCalendarApi.delete(row.id)