feat(pl): 增加百链充值类型,并保留计划产品与证件上传时间。
充值单号按类型使用 ETC、YK、SX、BL 前缀;更换装货客户清空产品时不抹掉已有计划产品;运输证件列表展示上传时间。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
af8d4d90fa
commit
cc5cf78bcd
21 files changed
+274
-97
No files matched your search
@@ -1798,7 +1798,7 @@ export interface VehicleRechargeApply {
|
||||
applyNo: string
|
||||
licensePlate: string
|
||||
boundFuelCardNo?: string | null
|
||||
rechargeType: 'etc' | 'fuel' | 'shanxia'
|
||||
rechargeType: 'etc' | 'fuel' | 'shanxia' | 'bailian'
|
||||
applyDate: string
|
||||
odometerKm?: number | null
|
||||
attachmentUrls?: string
|
||||
@@ -1830,14 +1830,14 @@ export interface VehicleRechargeApplyOption {
|
||||
id: string
|
||||
applyNo: string
|
||||
licensePlate: string
|
||||
rechargeType: 'etc' | 'fuel' | 'shanxia'
|
||||
rechargeType: 'etc' | 'fuel' | 'shanxia' | 'bailian'
|
||||
}
|
||||
|
||||
export interface VehicleRechargeRecord {
|
||||
id: string
|
||||
rechargeOrderNo: string
|
||||
rechargeDate: string
|
||||
rechargeType: 'etc' | 'fuel' | 'shanxia'
|
||||
rechargeType: 'etc' | 'fuel' | 'shanxia' | 'bailian'
|
||||
cardNo?: string
|
||||
gasStation?: string
|
||||
amount: number
|
||||
@@ -1864,7 +1864,7 @@ export interface VehicleRechargeDisburseBoardItem {
|
||||
applyNo: string
|
||||
licensePlate: string
|
||||
boundFuelCardNo?: string | null
|
||||
rechargeType: 'etc' | 'fuel' | 'shanxia'
|
||||
rechargeType: 'etc' | 'fuel' | 'shanxia' | 'bailian'
|
||||
applyDate: string
|
||||
odometerKm?: number | null
|
||||
applicantName?: string
|
||||
|
||||
@@ -712,6 +712,7 @@
|
||||
enrichStopProductsDisplayFromPlan,
|
||||
mapLoadProductsToPlanFormRows,
|
||||
mergePlanProductsFromLoadMirror,
|
||||
shouldRetainPlanProductsWhileLoadEmpty,
|
||||
resolveStopDibangDefaults,
|
||||
sumStopProductsPlanQty,
|
||||
type PlanDeliveryForm,
|
||||
@@ -1460,6 +1461,7 @@
|
||||
/** 装货产品变更后:仅镜像计划层产品(不再同步到送货段) */
|
||||
function syncFromLoadProducts() {
|
||||
if (suppressStopProductSync.value || detailLoading.value) return
|
||||
if (shouldRetainPlanProductsWhileLoadEmpty(loadForm.value.products || [], formData.products)) return
|
||||
mirrorPlanProductsFromLoad()
|
||||
}
|
||||
|
||||
|
||||
@@ -551,7 +551,7 @@ export function parsePlFeedbackImages(raw?: string | string[] | null): string[]
|
||||
}
|
||||
|
||||
export function getPlRechargeTypeLabel(type: string) {
|
||||
const labels: Record<string, string> = { etc: 'ETC', fuel: '油卡', shanxia: '山峡' }
|
||||
const labels: Record<string, string> = { etc: 'ETC', fuel: '油卡', shanxia: '山峡', bailian: '百链' }
|
||||
return labels[type] || type
|
||||
}
|
||||
|
||||
|
||||
@@ -768,6 +768,17 @@ export function mapLoadProductsToPlanFormRows(
|
||||
}))
|
||||
}
|
||||
|
||||
/**
|
||||
* 更换装货客户会先清空装货产品。这段空窗不要抹掉已有计划产品,
|
||||
* 重新选回同一产品和包装时才能继承原 id。正式保存仍按当前装货产品镜像。
|
||||
*/
|
||||
export function shouldRetainPlanProductsWhileLoadEmpty(
|
||||
loadProducts: Array<unknown>,
|
||||
previous: Array<unknown>
|
||||
): boolean {
|
||||
return loadProducts.length === 0 && previous.length > 0
|
||||
}
|
||||
|
||||
/** 合并装货映射行与既有计划产品元数据(危化证、税额等) */
|
||||
export function mergePlanProductsFromLoadMirror(
|
||||
fromLoad: PlanFormProductRow[],
|
||||
|
||||
@@ -14,30 +14,49 @@
|
||||
<div class="mp-wv-card__head">
|
||||
<div>
|
||||
<div class="mp-wv-card__no">{{ row.certNo }}</div>
|
||||
<div class="mp-wv-card__sub">{{ row.certType === 'transport' ? '运输证' : '购买证' }}</div>
|
||||
<div class="mp-wv-card__sub">{{
|
||||
row.certType === 'transport' ? '运输证' : '购买证'
|
||||
}}</div>
|
||||
</div>
|
||||
<span class="status-tag" :class="`status-tag--${statusTag(row.status)}`">{{ statusLabel(row.status) }}</span>
|
||||
<span class="status-tag" :class="`status-tag--${statusTag(row.status)}`">{{
|
||||
statusLabel(row.status)
|
||||
}}</span>
|
||||
</div>
|
||||
<div class="mp-wv-card__kv">
|
||||
<div class="mp-wv-card__kv-row"><span>销售单位</span><span>{{ salePartyName(row) || '—' }}</span></div>
|
||||
<div class="mp-wv-card__kv-row"><span>购买单位</span><span>{{ buyPartyName(row) || '—' }}</span></div>
|
||||
<div class="mp-wv-card__kv-row"><span>有效次数</span><span>{{ row.validUseText || '—' }}</span></div>
|
||||
<div class="mp-wv-card__kv">
|
||||
<div class="mp-wv-card__kv-row"
|
||||
><span>销售单位</span><span>{{ salePartyName(row) || '—' }}</span></div
|
||||
>
|
||||
<div class="mp-wv-card__kv-row"
|
||||
><span>购买单位</span><span>{{ buyPartyName(row) || '—' }}</span></div
|
||||
>
|
||||
<div class="mp-wv-card__kv-row"
|
||||
><span>有效次数</span><span>{{ row.validUseText || '—' }}</span></div
|
||||
>
|
||||
<div class="mp-wv-card__kv-row">
|
||||
<span>是否预警</span>
|
||||
<span v-if="warningText(row)" class="status-tag status-tag--danger">{{ warningText(row) }}</span>
|
||||
<span v-if="warningText(row)" class="status-tag status-tag--danger">{{
|
||||
warningText(row)
|
||||
}}</span>
|
||||
<span v-else>—</span>
|
||||
</div>
|
||||
<div class="mp-wv-card__kv-row"><span>品名</span><span>{{ row.productNameOnCert || '—' }}</span></div>
|
||||
<div class="mp-wv-card__kv-row"
|
||||
><span>品名</span><span>{{ row.productNameOnCert || '—' }}</span></div
|
||||
>
|
||||
<div class="mp-wv-card__kv-row">
|
||||
<span>数量</span><span>{{ qty(row.totalQty) }} {{ unit(row.quantityUnit) }}</span>
|
||||
</div>
|
||||
<div class="mp-wv-card__kv-row">
|
||||
<span>可用余额</span><span>{{ qty(row.availableQty) }} {{ unit(row.quantityUnit) }}</span>
|
||||
</div>
|
||||
<div class="mp-wv-card__kv-row"
|
||||
><span>上传时间</span><span>{{ uploadTime(row.createTime) }}</span></div
|
||||
>
|
||||
</div>
|
||||
<div class="mp-wv-card-actions" @click.stop>
|
||||
<button class="mp-wv-btn" type="button" @click="open(row)">查看</button>
|
||||
<button v-if="canEdit" class="mp-wv-btn mp-wv-btn--primary" type="button" @click="edit(row)">编辑</button>
|
||||
<button v-if="canEdit" class="mp-wv-btn mp-wv-btn--primary" type="button" @click="edit(row)"
|
||||
>编辑</button
|
||||
>
|
||||
<button
|
||||
v-if="canEdit && row.status !== 'void' && row.status !== 'closed'"
|
||||
class="mp-wv-btn"
|
||||
@@ -70,9 +89,13 @@
|
||||
>
|
||||
新增运输证
|
||||
</button>
|
||||
<button class="mp-wv-speed-item primary" type="button" @click="create('purchase')">新增购买证</button>
|
||||
<button class="mp-wv-speed-item primary" type="button" @click="create('purchase')"
|
||||
>新增购买证</button
|
||||
>
|
||||
</div>
|
||||
<button class="mp-wv-speed-main" type="button" aria-label="新增证件" @click="onCreate">+</button>
|
||||
<button class="mp-wv-speed-main" type="button" aria-label="新增证件" @click="onCreate"
|
||||
>+</button
|
||||
>
|
||||
</div>
|
||||
</template>
|
||||
</main>
|
||||
@@ -82,6 +105,7 @@
|
||||
import { quotaCertApi, type CertType, type QuotaCert } from '@/api/pl/quota-cert'
|
||||
import ArchiveForm from './archive-form.vue'
|
||||
import CertConfirmSheet from './CertConfirmSheet.vue'
|
||||
import { formatDateTime } from '@/utils/format'
|
||||
import { formatCertQty } from '@/views/pl/quota-cert/model'
|
||||
import { buyPartyName, expiryWarning, salePartyName } from '@/views/pl/quota-cert/cert-list-face'
|
||||
import { useDictStore } from '@/store/modules/dict'
|
||||
@@ -115,7 +139,9 @@
|
||||
return !props.expiringSoon || Boolean(expiryWarning(row.validTo))
|
||||
})
|
||||
)
|
||||
const expiringCount = computed(() => rows.value.filter((row) => expiryWarning(row.validTo)).length)
|
||||
const expiringCount = computed(
|
||||
() => rows.value.filter((row) => expiryWarning(row.validTo)).length
|
||||
)
|
||||
watch(expiringCount, (count) => emit('expiring-count', count), { immediate: true })
|
||||
let epoch = 0
|
||||
|
||||
@@ -134,6 +160,9 @@
|
||||
function qty(value: unknown) {
|
||||
return formatCertQty(value)
|
||||
}
|
||||
function uploadTime(value?: string | null) {
|
||||
return formatDateTime(value) || '—'
|
||||
}
|
||||
function unit(value?: string | null) {
|
||||
const label = getProductUnitLabel(useDictStore().getLabel, value)
|
||||
return !label || label === '—' ? '' : label
|
||||
|
||||
@@ -9,8 +9,12 @@
|
||||
v-model="keyword"
|
||||
placeholder="编号"
|
||||
@search="search"
|
||||
@clear="search" /><label class="cert-expiry-switch" @click.prevent="expiringSoon = !expiringSoon"
|
||||
><el-switch :model-value="expiringSoon" /><span>近7日到期({{ expiringCount }}个)</span></label
|
||||
@clear="search" /><label
|
||||
class="cert-expiry-switch"
|
||||
@click.prevent="expiringSoon = !expiringSoon"
|
||||
><el-switch :model-value="expiringSoon" /><span
|
||||
>近7日到期({{ expiringCount }}个)</span
|
||||
></label
|
||||
><span class="list-toolbar-selected">共 {{ filteredRows.length }} 张</span
|
||||
><el-button v-if="canEdit" class="btn-main" type="primary" @click="edit()"
|
||||
><el-icon><Plus /></el-icon>新增证件</el-button
|
||||
@@ -41,7 +45,7 @@
|
||||
:key="column.prop"
|
||||
:prop="column.prop"
|
||||
:label="column.label"
|
||||
:min-width="column.type === 'text' ? 190 : 140"
|
||||
:min-width="column.prop === 'createTime' ? 170 : column.type === 'text' ? 190 : 140"
|
||||
><template #header
|
||||
><div class="col-header-with-filter"
|
||||
><span class="col-header-with-filter__label">{{ column.label }}</span>
|
||||
@@ -70,10 +74,15 @@
|
||||
><span v-else-if="column.type === 'number'">{{
|
||||
qtyText(row[column.prop], row.quantityUnit)
|
||||
}}</span
|
||||
><el-tag v-else-if="column.prop === 'expiryAlert' && expiryText(row)" type="danger" size="small">{{
|
||||
expiryText(row)
|
||||
}}</el-tag
|
||||
><el-tag
|
||||
v-else-if="column.prop === 'expiryAlert' && expiryText(row)"
|
||||
type="danger"
|
||||
size="small"
|
||||
>{{ expiryText(row) }}</el-tag
|
||||
><span v-else-if="column.prop === 'expiryAlert'">—</span
|
||||
><span v-else-if="column.prop === 'createTime'">{{
|
||||
formatDateTime(row.createTime) || '—'
|
||||
}}</span
|
||||
><span v-else>{{ row[column.prop] || '—' }}</span></template
|
||||
></el-table-column
|
||||
><el-table-column label="操作" min-width="320" fixed="right"
|
||||
@@ -266,7 +275,10 @@
|
||||
</el-form-item>
|
||||
</div>
|
||||
<p
|
||||
v-if="group.fields.some((field) => field[0] === 'vehicleNumbersJson') && lockedPlates.length"
|
||||
v-if="
|
||||
group.fields.some((field) => field[0] === 'vehicleNumbersJson') &&
|
||||
lockedPlates.length
|
||||
"
|
||||
class="form-tip"
|
||||
>
|
||||
已有车号不能删除,只能追加。保存后证件仍可安排运输。
|
||||
@@ -397,49 +409,51 @@
|
||||
type="danger"
|
||||
title="移除引用"
|
||||
aria-label="移除引用"
|
||||
:disabled="saving || form.referencesEditable === false || referenceRowLocked(row)"
|
||||
:disabled="
|
||||
saving || form.referencesEditable === false || referenceRowLocked(row)
|
||||
"
|
||||
@click="removeReference(index)"
|
||||
><el-icon><Delete /></el-icon
|
||||
></el-button>
|
||||
</header>
|
||||
<div class="form-grid-2 cert-reference-fields">
|
||||
<el-form-item label="引用类型">
|
||||
<el-select
|
||||
v-model="row.referenceType"
|
||||
:disabled="saving || referenceRowLocked(row)"
|
||||
@change="edited.add('purchaseRefs')"
|
||||
>
|
||||
<el-option
|
||||
v-for="(label, value) in refLabels"
|
||||
:key="value"
|
||||
:value="value"
|
||||
:label="label"
|
||||
<el-form-item label="引用类型">
|
||||
<el-select
|
||||
v-model="row.referenceType"
|
||||
:disabled="saving || referenceRowLocked(row)"
|
||||
@change="edited.add('purchaseRefs')"
|
||||
>
|
||||
<el-option
|
||||
v-for="(label, value) in refLabels"
|
||||
:key="value"
|
||||
:value="value"
|
||||
:label="label"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item :label="purchasePermitLabel">
|
||||
<el-input
|
||||
v-model="row.referenceNo"
|
||||
:disabled="saving || referenceRowLocked(row)"
|
||||
@input="edited.add('purchaseRefs')"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item :label="purchasePermitLabel">
|
||||
<el-input
|
||||
v-model="row.referenceNo"
|
||||
:disabled="saving || referenceRowLocked(row)"
|
||||
@input="edited.add('purchaseRefs')"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="对应购买证档案">
|
||||
<el-select
|
||||
v-model="row.purchaseCertId"
|
||||
:disabled="saving || referenceRowLocked(row)"
|
||||
filterable
|
||||
clearable
|
||||
@change="onPurchaseArchiveChange(row)"
|
||||
>
|
||||
<el-option
|
||||
v-for="c in purchases"
|
||||
:key="c.id"
|
||||
:value="c.id"
|
||||
:label="c.certNo"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form-item>
|
||||
<el-form-item label="对应购买证档案">
|
||||
<el-select
|
||||
v-model="row.purchaseCertId"
|
||||
:disabled="saving || referenceRowLocked(row)"
|
||||
filterable
|
||||
clearable
|
||||
@change="onPurchaseArchiveChange(row)"
|
||||
>
|
||||
<el-option
|
||||
v-for="c in purchases"
|
||||
:key="c.id"
|
||||
:value="c.id"
|
||||
:label="c.certNo"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
@@ -595,18 +609,11 @@
|
||||
|
||||
import QuotaHistoryDialog from './QuotaHistoryDialog.vue'
|
||||
const historyDialog = ref<InstanceType<typeof QuotaHistoryDialog>>()
|
||||
import {
|
||||
Check,
|
||||
Close,
|
||||
Connection,
|
||||
Delete,
|
||||
Document,
|
||||
Edit,
|
||||
Plus
|
||||
} from '@element-plus/icons-vue'
|
||||
import { Check, Close, Connection, Delete, Document, Edit, Plus } from '@element-plus/icons-vue'
|
||||
import { useUserStore } from '@/store/modules/user'
|
||||
import { useDictStore } from '@/store/modules/dict'
|
||||
import { getProductUnitLabel } from '@/utils/crm-product-display'
|
||||
import { formatDateTime } from '@/utils/format'
|
||||
import { randomOperationId } from '@/utils/random-id'
|
||||
import { fetchGetMenuCodes } from '@/api/auth'
|
||||
import {
|
||||
@@ -1131,9 +1138,7 @@
|
||||
return row?.certType === 'transport' || row?.certType === 'purchase' ? row.certType : kind.value
|
||||
}
|
||||
function plateList(value: unknown) {
|
||||
return Array.isArray(value)
|
||||
? value.map((item) => String(item).trim()).filter(Boolean)
|
||||
: []
|
||||
return Array.isArray(value) ? value.map((item) => String(item).trim()).filter(Boolean) : []
|
||||
}
|
||||
function plateLocked(plate: string) {
|
||||
return lockedPlates.value.includes(plate)
|
||||
@@ -1529,7 +1534,9 @@
|
||||
form.value.contractBindings = next
|
||||
}
|
||||
function referenceRowLocked(row: { id?: string; purchaseCertId?: string | null }) {
|
||||
return form.value.referencesEditable === false && !!row.id && linkedRefIds.value.includes(row.id)
|
||||
return (
|
||||
form.value.referencesEditable === false && !!row.id && linkedRefIds.value.includes(row.id)
|
||||
)
|
||||
}
|
||||
function onPurchaseArchiveChange() {
|
||||
edited.add('purchaseRefs')
|
||||
@@ -1678,10 +1685,17 @@
|
||||
enumOptions: Object.entries(statusLabels).map(([value, label]) => ({ value, label }))
|
||||
},
|
||||
...qtyColumns.map((c) => ({ prop: c.key, label: c.label, type: 'number' as const })),
|
||||
{ prop: 'validTo', label: '有效期限止', type: 'datetime' }
|
||||
{ prop: 'validTo', label: '有效期限止', type: 'datetime' },
|
||||
{
|
||||
prop: 'createTime',
|
||||
label: '上传时间',
|
||||
type: 'datetime' as const,
|
||||
getValue: (row: { createTime?: string | null }) =>
|
||||
String(row.createTime || '').replace('T', ' ')
|
||||
}
|
||||
]
|
||||
const { visibleColumns, checkAll, isIndeterminate, resetToDefault } = usePersistedListColumns(
|
||||
'pl-quota-cert-columns-v3',
|
||||
'pl-quota-cert-columns-v4',
|
||||
listColumnDefs
|
||||
)
|
||||
const visibleDefs = computed(() =>
|
||||
@@ -1704,7 +1718,9 @@
|
||||
() => columnRows.value.filter((row) => expiryWarning(row.validTo)).length
|
||||
)
|
||||
const filteredRows = computed(() =>
|
||||
expiringSoon.value ? columnRows.value.filter((row) => expiryWarning(row.validTo)) : columnRows.value
|
||||
expiringSoon.value
|
||||
? columnRows.value.filter((row) => expiryWarning(row.validTo))
|
||||
: columnRows.value
|
||||
)
|
||||
const pageRows = computed(() => filteredRows.value.slice((page.value - 1) * 20, page.value * 20))
|
||||
function search() {
|
||||
|
||||
@@ -3,7 +3,8 @@ import type { ColumnFilterDef } from '@/composables/useClientColumnFilters'
|
||||
const RECHARGE_TYPE_OPTIONS = [
|
||||
{ value: 'etc', label: 'ETC' },
|
||||
{ value: 'fuel', label: '油卡' },
|
||||
{ value: 'shanxia', label: '山峡' }
|
||||
{ value: 'shanxia', label: '山峡' },
|
||||
{ value: 'bailian', label: '百链' }
|
||||
]
|
||||
|
||||
const RECHARGE_STATUS_OPTIONS = [
|
||||
|
||||
@@ -372,6 +372,7 @@
|
||||
<el-option label="ETC" value="etc" />
|
||||
<el-option label="油卡" value="fuel" />
|
||||
<el-option label="山峡" value="shanxia" />
|
||||
<el-option label="百链" value="bailian" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="formFuelCard.show.value" label="绑定油卡卡号">
|
||||
@@ -767,7 +768,7 @@ const formData = reactive({
|
||||
applyNo: '',
|
||||
applicantId: '',
|
||||
licensePlate: '',
|
||||
rechargeType: 'etc' as 'etc' | 'fuel' | 'shanxia',
|
||||
rechargeType: 'etc' as 'etc' | 'fuel' | 'shanxia' | 'bailian',
|
||||
applyDate: '',
|
||||
odometerKm: null as number | null,
|
||||
attachmentUrls: '',
|
||||
|
||||
@@ -463,6 +463,7 @@
|
||||
<el-option label="ETC" value="etc" />
|
||||
<el-option label="油卡" value="fuel" />
|
||||
<el-option label="山峡" value="shanxia" />
|
||||
<el-option label="百链" value="bailian" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</template>
|
||||
@@ -779,7 +780,7 @@ const formData = reactive({
|
||||
rechargeOrderNo: '',
|
||||
applyId: '',
|
||||
licensePlate: '',
|
||||
rechargeType: 'etc' as 'etc' | 'fuel' | 'shanxia',
|
||||
rechargeType: 'etc' as 'etc' | 'fuel' | 'shanxia' | 'bailian',
|
||||
rechargeDate: '',
|
||||
cardNo: '',
|
||||
gasStation: '',
|
||||
|
||||
Reference in new issue
Block a user