feat: 网页配置顺丰运费时改为月结并填写顺丰单号
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
afcb4e9ebe
commit
9561f94074
7 files changed
+312
-98
No files matched your search
@@ -18,7 +18,11 @@ export type SampleFreight = {
|
||||
freightNo?: string
|
||||
transportMode: string
|
||||
transportModeLabel?: string
|
||||
freightAmount: number
|
||||
freightAmount: number | null
|
||||
waybillNo?: string
|
||||
settleStatus?: string
|
||||
settleTime?: string
|
||||
freightAmountLabel?: string
|
||||
linkedSampleCount?: number
|
||||
createBy?: string
|
||||
createByName?: string
|
||||
@@ -63,7 +67,8 @@ export function fetchSampleFreightDetail(id: string) {
|
||||
|
||||
export function fetchCreateSampleFreight(data: {
|
||||
transportMode: string
|
||||
freightAmount: number
|
||||
freightAmount: number | null
|
||||
waybillNo?: string
|
||||
items: SampleFreightItem[]
|
||||
}) {
|
||||
return request.post<SampleFreight>({
|
||||
@@ -75,7 +80,7 @@ export function fetchCreateSampleFreight(data: {
|
||||
|
||||
export function fetchUpdateSampleFreight(
|
||||
id: string,
|
||||
data: { transportMode?: string; freightAmount?: number }
|
||||
data: { transportMode?: string; freightAmount?: number | null; waybillNo?: string }
|
||||
) {
|
||||
return request.put<SampleFreight>({
|
||||
url: `/web/admin/sample-freights/${id}`,
|
||||
|
||||
@@ -6,19 +6,11 @@
|
||||
<el-icon><Plus /></el-icon>
|
||||
添加样本
|
||||
</el-button>
|
||||
<el-button
|
||||
class="btn-ghost"
|
||||
:disabled="!selectedSamples.length"
|
||||
@click="openFreightDialog"
|
||||
>
|
||||
<el-button class="btn-ghost" :disabled="!selectedSamples.length" @click="openFreightDialog">
|
||||
<el-icon><Van /></el-icon>
|
||||
配置运费
|
||||
</el-button>
|
||||
<el-button
|
||||
class="btn-ghost"
|
||||
:disabled="!canUnlinkFreight"
|
||||
@click="openUnlinkFreightDialog"
|
||||
>
|
||||
<el-button class="btn-ghost" :disabled="!canUnlinkFreight" @click="openUnlinkFreightDialog">
|
||||
<el-icon><Delete /></el-icon>
|
||||
取消运费
|
||||
</el-button>
|
||||
@@ -104,12 +96,7 @@
|
||||
<el-table-column label="运输方式" min-width="120">
|
||||
<template #default="{ row }">{{ freightModeLabel(row) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
label="运费(元)"
|
||||
min-width="120"
|
||||
align="right"
|
||||
header-align="center"
|
||||
>
|
||||
<el-table-column label="运费(元)" min-width="120" align="right" header-align="center">
|
||||
<template #default="{ row }">{{ freightAmountText(row) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="预计到达" min-width="160" show-overflow-tooltip>
|
||||
@@ -209,11 +196,7 @@
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="showPrice" label="原价格(元)">
|
||||
<el-input
|
||||
:model-value="fmtMoney(sampleDraft.originalPrice)"
|
||||
disabled
|
||||
placeholder="—"
|
||||
/>
|
||||
<el-input :model-value="fmtMoney(sampleDraft.originalPrice)" disabled placeholder="—" />
|
||||
</el-form-item>
|
||||
<el-form-item v-if="showPrice" label="优惠价格(元)">
|
||||
<AmountInput v-model="sampleDraft.price" :min="0" placeholder="请输入优惠价格" />
|
||||
@@ -318,9 +301,25 @@
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="运费(元)">
|
||||
<el-form-item v-if="!isSfExpress(freightForm.transportMode)" label="运费(元)">
|
||||
<AmountInput v-model="freightForm.freightAmount" :min="0" placeholder="请输入运费" />
|
||||
</el-form-item>
|
||||
<el-form-item v-else label="运费(元)">
|
||||
<el-input model-value="月结" readonly />
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-if="isSfExpress(freightForm.transportMode)"
|
||||
label="顺丰单号"
|
||||
required
|
||||
class="form-col-span-2"
|
||||
>
|
||||
<el-input
|
||||
v-model="freightForm.waybillNo"
|
||||
maxlength="64"
|
||||
clearable
|
||||
placeholder="请输入顺丰单号"
|
||||
/>
|
||||
</el-form-item>
|
||||
</div>
|
||||
</el-form>
|
||||
<div v-else class="link-existing-block">
|
||||
@@ -382,6 +381,12 @@
|
||||
} from '@/components/business/freight-unlink-dialog/index.vue'
|
||||
import { sampleStatusLabel, sampleStatusTagType } from '@/utils/status-tag-registry'
|
||||
import { formatMoneyDisplay } from '@/utils/money-format'
|
||||
import {
|
||||
buildFreightHeaderPayload,
|
||||
freightAmountText as formatFreightAmountText,
|
||||
isSfExpress,
|
||||
validateFreightHeader
|
||||
} from '@/utils/sf-express-freight'
|
||||
import {
|
||||
partitionFreightConfigSamples,
|
||||
freightConfigSkipMessage,
|
||||
@@ -466,7 +471,8 @@
|
||||
const freightSubmitting = ref(false)
|
||||
const freightForm = reactive({
|
||||
transportMode: '' as string,
|
||||
freightAmount: null as number | null
|
||||
freightAmount: null as number | null,
|
||||
waybillNo: ''
|
||||
})
|
||||
const transportModeOptions = ref<Array<{ label: string; value: string }>>([])
|
||||
const existingFreightList = ref<SampleFreight[]>([])
|
||||
@@ -487,9 +493,7 @@
|
||||
const sampleDialogIndex = ref(-1)
|
||||
const sampleDraft = reactive<Record<string, any>>({})
|
||||
|
||||
const canUnlinkFreight = computed(() =>
|
||||
selectedSamples.value.some((r) => sampleHasFreight(r))
|
||||
)
|
||||
const canUnlinkFreight = computed(() => selectedSamples.value.some((r) => sampleHasFreight(r)))
|
||||
|
||||
function fmtMoney(v: unknown): string {
|
||||
if (v == null || v === '') return '—'
|
||||
@@ -506,11 +510,12 @@
|
||||
}
|
||||
|
||||
function freightAmountText(row: Record<string, any>) {
|
||||
const mode = row.freight?.transportMode || row.freightDraft?.transportMode
|
||||
const amount = row.freight?.freightAmount ?? row.freightDraft?.freightAmount
|
||||
if (amount == null || amount === '') return '—'
|
||||
const base = fmtMoney(amount)
|
||||
const text = formatFreightAmountText(mode, amount)
|
||||
if (!text) return '—'
|
||||
const n = Number(row.freight?.linkedSampleCount || 0)
|
||||
return n > 1 ? `${base}(共用)` : base
|
||||
return n > 1 ? `${text}(共用)` : text
|
||||
}
|
||||
|
||||
function normalizeSampleRow(raw: Record<string, any>, _index: number) {
|
||||
@@ -603,8 +608,7 @@
|
||||
if (currentId && sc.id === currentId) return true
|
||||
if (sc.usedInProtocol) return false
|
||||
return !props.samples.some(
|
||||
(s, i) =>
|
||||
(editingIndex < 0 || i !== editingIndex) && String(s.sampleCodeId || '') === sc.id
|
||||
(s, i) => (editingIndex < 0 || i !== editingIndex) && String(s.sampleCodeId || '') === sc.id
|
||||
)
|
||||
})
|
||||
})
|
||||
@@ -693,16 +697,21 @@
|
||||
? {
|
||||
id: freight.id,
|
||||
transportMode: freight.transportMode,
|
||||
transportModeLabel: freight.transportModeLabel || transportModeLabelOf(String(freight.transportMode || '')),
|
||||
transportModeLabel:
|
||||
freight.transportModeLabel ||
|
||||
transportModeLabelOf(String(freight.transportMode || '')),
|
||||
freightAmount: freight.freightAmount,
|
||||
waybillNo: freight.waybillNo,
|
||||
linkedSampleCount: freight.linkedSampleCount
|
||||
}
|
||||
: {
|
||||
id: '',
|
||||
transportMode: freight.transportMode,
|
||||
transportModeLabel:
|
||||
freight.transportModeLabel || transportModeLabelOf(String(freight.transportMode || '')),
|
||||
freight.transportModeLabel ||
|
||||
transportModeLabelOf(String(freight.transportMode || '')),
|
||||
freightAmount: freight.freightAmount,
|
||||
waybillNo: freight.waybillNo,
|
||||
linkedSampleCount: freight.linkedSampleCount
|
||||
},
|
||||
freightId: freight.id || '',
|
||||
@@ -764,7 +773,9 @@
|
||||
keyword: existingFreightKeyword.value || undefined
|
||||
})
|
||||
const data = (res as any)?.data || res
|
||||
existingFreightList.value = data?.list || []
|
||||
existingFreightList.value = ((data?.list || []) as SampleFreight[]).filter(
|
||||
(row) => row.settleStatus !== 'yjs'
|
||||
)
|
||||
} catch {
|
||||
existingFreightList.value = []
|
||||
} finally {
|
||||
@@ -789,6 +800,7 @@
|
||||
freightMode.value = 'create'
|
||||
freightForm.transportMode = ''
|
||||
freightForm.freightAmount = null
|
||||
freightForm.waybillNo = ''
|
||||
selectedExistingFreight.value = null
|
||||
selectedFreightId.value = ''
|
||||
existingFreightKeyword.value = ''
|
||||
@@ -813,39 +825,52 @@
|
||||
freightSubmitting.value = true
|
||||
try {
|
||||
if (freightMode.value === 'create') {
|
||||
if (!freightForm.transportMode || freightForm.freightAmount == null) {
|
||||
ElMessage.warning('请填写运输方式与运费')
|
||||
return
|
||||
}
|
||||
const amount = Number(freightForm.freightAmount)
|
||||
if (Number.isNaN(amount) || amount < 0) {
|
||||
ElMessage.warning('运费须为不小于 0 的数字')
|
||||
const message = validateFreightHeader({
|
||||
transportMode: freightForm.transportMode,
|
||||
freightAmount: freightForm.freightAmount,
|
||||
waybillNo: freightForm.waybillNo
|
||||
})
|
||||
if (message) {
|
||||
ElMessage.warning(message)
|
||||
return
|
||||
}
|
||||
const header = buildFreightHeaderPayload({
|
||||
transportMode: freightForm.transportMode,
|
||||
freightAmount: freightForm.freightAmount,
|
||||
waybillNo: freightForm.waybillNo
|
||||
})
|
||||
let created: SampleFreight | null = null
|
||||
if (persisted.length) {
|
||||
const res = await fetchCreateSampleFreight({
|
||||
transportMode: freightForm.transportMode,
|
||||
freightAmount: amount,
|
||||
transportMode: header.transportMode,
|
||||
freightAmount: header.freightAmount,
|
||||
waybillNo: header.waybillNo,
|
||||
items: toFreightItems(persisted)
|
||||
})
|
||||
created = ((res as any)?.data || res) as SampleFreight
|
||||
applyFreightToRows(persisted, created)
|
||||
applyFreightToRows(persisted, {
|
||||
...created,
|
||||
transportMode: header.transportMode,
|
||||
freightAmount: header.freightAmount,
|
||||
waybillNo: header.waybillNo
|
||||
})
|
||||
}
|
||||
if (drafts.length) {
|
||||
const clientKey = `fd_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`
|
||||
applyFreightToRows(drafts, {
|
||||
id: created?.id || '',
|
||||
transportMode: freightForm.transportMode,
|
||||
transportModeLabel: transportModeLabelOf(freightForm.transportMode),
|
||||
freightAmount: amount,
|
||||
transportMode: header.transportMode,
|
||||
transportModeLabel: transportModeLabelOf(header.transportMode),
|
||||
freightAmount: header.freightAmount,
|
||||
waybillNo: header.waybillNo,
|
||||
linkedSampleCount: (created?.linkedSampleCount || 0) + drafts.length,
|
||||
freightDraft: created?.id
|
||||
? undefined
|
||||
: {
|
||||
clientKey,
|
||||
transportMode: freightForm.transportMode,
|
||||
freightAmount: amount
|
||||
transportMode: header.transportMode,
|
||||
waybillNo: header.waybillNo,
|
||||
freightAmount: header.freightAmount
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -907,11 +932,11 @@
|
||||
|
||||
if (!persistedSeeds.length && draftOnly.length) {
|
||||
try {
|
||||
await ElMessageBox.confirm(
|
||||
'确定取消所选样本的运费吗?取消后可再次配置运费。',
|
||||
'取消运费',
|
||||
{ type: 'warning', confirmButtonText: '确定', cancelButtonText: '取消' }
|
||||
)
|
||||
await ElMessageBox.confirm('确定取消所选样本的运费吗?取消后可再次配置运费。', '取消运费', {
|
||||
type: 'warning',
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消'
|
||||
})
|
||||
} catch {
|
||||
return
|
||||
}
|
||||
@@ -922,9 +947,7 @@
|
||||
|
||||
const freightIds = [
|
||||
...new Set(
|
||||
persistedSeeds
|
||||
.map((r) => String(r.freight?.id || r.freightId || '').trim())
|
||||
.filter(Boolean)
|
||||
persistedSeeds.map((r) => String(r.freight?.id || r.freightId || '').trim()).filter(Boolean)
|
||||
)
|
||||
]
|
||||
const byKey = new Map<string, FreightUnlinkRow>()
|
||||
@@ -1077,8 +1100,7 @@
|
||||
return
|
||||
}
|
||||
const dup = props.samples.some(
|
||||
(s, i) =>
|
||||
i !== sampleDialogIndex.value && String(s.sampleCode || '').trim() === code
|
||||
(s, i) => i !== sampleDialogIndex.value && String(s.sampleCode || '').trim() === code
|
||||
)
|
||||
if (dup) {
|
||||
ElMessage.warning('样本编码重复,请重新添加')
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import test from 'node:test'
|
||||
import {
|
||||
buildFreightHeaderPayload,
|
||||
buildSettlePayload,
|
||||
freightAmountText,
|
||||
validateFreightHeader
|
||||
} from './sf-express-freight.ts'
|
||||
|
||||
test('sf express hides amount and requires waybill', () => {
|
||||
assert.equal(
|
||||
validateFreightHeader({ transportMode: 'sfkd', freightAmount: null, waybillNo: ' SF1 ' }),
|
||||
''
|
||||
)
|
||||
assert.match(
|
||||
validateFreightHeader({ transportMode: 'sfkd', freightAmount: null, waybillNo: ' ' }),
|
||||
/请填写顺丰单号/
|
||||
)
|
||||
assert.deepEqual(
|
||||
buildFreightHeaderPayload({ transportMode: 'sfkd', freightAmount: 8, waybillNo: ' SF1 ' }),
|
||||
{ transportMode: 'sfkd', waybillNo: 'SF1', freightAmount: null }
|
||||
)
|
||||
assert.equal(freightAmountText('sfkd', null), '月结')
|
||||
assert.equal(freightAmountText('sfkd', 8), '8.00')
|
||||
})
|
||||
|
||||
test('other transport still requires amount', () => {
|
||||
assert.match(
|
||||
validateFreightHeader({ transportMode: 'zxc', freightAmount: null, waybillNo: '' }),
|
||||
/请填写运费/
|
||||
)
|
||||
assert.equal(
|
||||
buildFreightHeaderPayload({ transportMode: 'zxc', freightAmount: 12.3, waybillNo: 'SF1' })
|
||||
.freightAmount,
|
||||
12.3
|
||||
)
|
||||
assert.equal(
|
||||
buildFreightHeaderPayload({ transportMode: 'zxc', freightAmount: 12.3, waybillNo: 'SF1' })
|
||||
.waybillNo,
|
||||
''
|
||||
)
|
||||
})
|
||||
|
||||
test('blank settle omits amount and batch settle never sends amount', () => {
|
||||
assert.deepEqual(buildSettlePayload(['a'], ' '), { ids: ['a'] })
|
||||
assert.deepEqual(buildSettlePayload(['a'], '6.5'), { ids: ['a'], freightAmount: 6.5 })
|
||||
assert.deepEqual(buildSettlePayload(['a', 'b'], '6.5'), { ids: ['a', 'b'] })
|
||||
})
|
||||
@@ -0,0 +1,59 @@
|
||||
export const SF_TRANSPORT_MODE = 'sfkd'
|
||||
|
||||
export function isSfExpress(mode: string | null | undefined) {
|
||||
return String(mode || '').trim() === SF_TRANSPORT_MODE
|
||||
}
|
||||
|
||||
export function freightAmountText(
|
||||
mode: string | null | undefined,
|
||||
amount: number | string | null | undefined
|
||||
) {
|
||||
if (isSfExpress(mode) && (amount === null || amount === undefined || amount === '')) return '月结'
|
||||
if (amount === null || amount === undefined || amount === '') return ''
|
||||
const value = Number(amount)
|
||||
if (!Number.isFinite(value)) return ''
|
||||
return value.toFixed(2)
|
||||
}
|
||||
|
||||
export function validateFreightHeader(input: {
|
||||
transportMode: string
|
||||
freightAmount: number | null
|
||||
waybillNo: string
|
||||
}) {
|
||||
if (!String(input.transportMode || '').trim()) return '请选择运输方式'
|
||||
if (isSfExpress(input.transportMode)) {
|
||||
const waybill = String(input.waybillNo || '').trim()
|
||||
if (!waybill) return '请填写顺丰单号'
|
||||
if (waybill.length > 64) return '顺丰单号不能超过 64 个字符'
|
||||
return ''
|
||||
}
|
||||
if (
|
||||
input.freightAmount == null ||
|
||||
!Number.isFinite(Number(input.freightAmount)) ||
|
||||
Number(input.freightAmount) < 0
|
||||
) {
|
||||
return '请填写运费'
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
export function buildFreightHeaderPayload(input: {
|
||||
transportMode: string
|
||||
freightAmount: number | null
|
||||
waybillNo: string
|
||||
}) {
|
||||
const sf = isSfExpress(input.transportMode)
|
||||
return {
|
||||
transportMode: String(input.transportMode || '').trim(),
|
||||
waybillNo: sf ? String(input.waybillNo || '').trim() : '',
|
||||
freightAmount: sf ? null : Number(input.freightAmount)
|
||||
}
|
||||
}
|
||||
|
||||
export function buildSettlePayload(ids: string[], freightAmount: string) {
|
||||
const payload: { ids: string[]; freightAmount?: number } = { ids }
|
||||
if (ids.length === 1 && String(freightAmount || '').trim()) {
|
||||
payload.freightAmount = Number(String(freightAmount).trim())
|
||||
}
|
||||
return payload
|
||||
}
|
||||
@@ -36,9 +36,25 @@
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="运费(元)" required>
|
||||
<el-form-item v-if="!isSfExpress(form.transportMode)" label="运费(元)" required>
|
||||
<AmountInput v-model="form.freightAmount" :min="0" placeholder="请输入运费" />
|
||||
</el-form-item>
|
||||
<el-form-item v-else label="运费(元)">
|
||||
<el-input model-value="月结" readonly />
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-if="isSfExpress(form.transportMode)"
|
||||
label="顺丰单号"
|
||||
required
|
||||
class="form-col-span-2"
|
||||
>
|
||||
<el-input
|
||||
v-model="form.waybillNo"
|
||||
maxlength="64"
|
||||
clearable
|
||||
placeholder="请输入顺丰单号"
|
||||
/>
|
||||
</el-form-item>
|
||||
</div>
|
||||
</el-form>
|
||||
|
||||
@@ -200,6 +216,11 @@
|
||||
type SampleFreightItem
|
||||
} from '@/api/sample-freight'
|
||||
import { receptionSourceLabel, receptionSourceTagType } from '@/utils/status-tag-registry'
|
||||
import {
|
||||
buildFreightHeaderPayload,
|
||||
isSfExpress,
|
||||
validateFreightHeader
|
||||
} from '@/utils/sf-express-freight'
|
||||
|
||||
defineOptions({ name: 'FreightConfigDialog' })
|
||||
|
||||
@@ -229,7 +250,8 @@
|
||||
})
|
||||
const form = reactive({
|
||||
transportMode: '',
|
||||
freightAmount: null as number | null
|
||||
freightAmount: null as number | null,
|
||||
waybillNo: ''
|
||||
})
|
||||
|
||||
async function loadTransportModeOptions() {
|
||||
@@ -258,7 +280,9 @@
|
||||
keyword: existingKeyword.value || undefined
|
||||
})
|
||||
const data = (res as any)?.data || res
|
||||
existingFreightList.value = data?.list || []
|
||||
existingFreightList.value = ((data?.list || []) as SampleFreight[]).filter(
|
||||
(row) => row.settleStatus !== 'yjs'
|
||||
)
|
||||
} catch {
|
||||
existingFreightList.value = []
|
||||
} finally {
|
||||
@@ -316,6 +340,7 @@
|
||||
mode.value = 'create'
|
||||
form.transportMode = ''
|
||||
form.freightAmount = null
|
||||
form.waybillNo = ''
|
||||
selectedExisting.value = null
|
||||
selectedFreightId.value = ''
|
||||
existingKeyword.value = ''
|
||||
@@ -370,18 +395,24 @@
|
||||
submitting.value = true
|
||||
try {
|
||||
if (mode.value === 'create') {
|
||||
if (!form.transportMode || form.freightAmount == null) {
|
||||
ElMessage.warning('请填写运输方式与运费')
|
||||
return
|
||||
}
|
||||
const amount = Number(form.freightAmount)
|
||||
if (Number.isNaN(amount) || amount < 0) {
|
||||
ElMessage.warning('运费须为不小于 0 的数字')
|
||||
return
|
||||
}
|
||||
await fetchCreateSampleFreight({
|
||||
const message = validateFreightHeader({
|
||||
transportMode: form.transportMode,
|
||||
freightAmount: amount,
|
||||
freightAmount: form.freightAmount,
|
||||
waybillNo: form.waybillNo
|
||||
})
|
||||
if (message) {
|
||||
ElMessage.warning(message)
|
||||
return
|
||||
}
|
||||
const header = buildFreightHeaderPayload({
|
||||
transportMode: form.transportMode,
|
||||
freightAmount: form.freightAmount,
|
||||
waybillNo: form.waybillNo
|
||||
})
|
||||
await fetchCreateSampleFreight({
|
||||
transportMode: header.transportMode,
|
||||
freightAmount: header.freightAmount,
|
||||
waybillNo: header.waybillNo,
|
||||
items: toItems(samples)
|
||||
})
|
||||
} else {
|
||||
|
||||
@@ -41,9 +41,25 @@
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="运费(元)" prop="freightAmount">
|
||||
<el-form-item v-if="!isSfExpress(form.transportMode)" label="运费(元)" required>
|
||||
<AmountInput v-model="form.freightAmount" :min="0" placeholder="请输入运费" />
|
||||
</el-form-item>
|
||||
<el-form-item v-else label="运费(元)">
|
||||
<el-input model-value="月结" readonly />
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-if="isSfExpress(form.transportMode)"
|
||||
label="顺丰单号"
|
||||
required
|
||||
class="form-col-span-2"
|
||||
>
|
||||
<el-input
|
||||
v-model="form.waybillNo"
|
||||
maxlength="64"
|
||||
clearable
|
||||
placeholder="请输入顺丰单号"
|
||||
/>
|
||||
</el-form-item>
|
||||
</div>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
@@ -65,6 +81,11 @@
|
||||
import AmountInput from '@/components/common/amount-input/index.vue'
|
||||
import { fetchDictDataOptions } from '@/api/admin'
|
||||
import { fetchUpdateSampleFreight, type SampleFreight } from '@/api/sample-freight'
|
||||
import {
|
||||
buildFreightHeaderPayload,
|
||||
isSfExpress,
|
||||
validateFreightHeader
|
||||
} from '@/utils/sf-express-freight'
|
||||
|
||||
defineOptions({ name: 'FreightEditDialog' })
|
||||
|
||||
@@ -78,12 +99,12 @@
|
||||
const transportModeOptions = ref<Array<{ label: string; value: string }>>([])
|
||||
const form = reactive({
|
||||
transportMode: '',
|
||||
freightAmount: null as number | null
|
||||
freightAmount: null as number | null,
|
||||
waybillNo: ''
|
||||
})
|
||||
|
||||
const rules: FormRules = {
|
||||
transportMode: [{ required: true, message: '请选择运输方式', trigger: 'change' }],
|
||||
freightAmount: [{ required: true, message: '请输入运费', trigger: 'blur' }]
|
||||
transportMode: [{ required: true, message: '请选择运输方式', trigger: 'change' }]
|
||||
}
|
||||
|
||||
async function loadTransportModeOptions() {
|
||||
@@ -107,6 +128,7 @@
|
||||
linkedCount.value = row.linkedSampleCount ?? row.items?.length ?? 0
|
||||
form.transportMode = row.transportMode || ''
|
||||
form.freightAmount = row.freightAmount ?? null
|
||||
form.waybillNo = row.waybillNo || ''
|
||||
await loadTransportModeOptions()
|
||||
visible.value = true
|
||||
}
|
||||
@@ -115,21 +137,31 @@
|
||||
formRef.value?.resetFields()
|
||||
freightId.value = ''
|
||||
linkedCount.value = 0
|
||||
form.waybillNo = ''
|
||||
}
|
||||
|
||||
async function handleSubmit() {
|
||||
if (!formRef.value) return
|
||||
await formRef.value.validate()
|
||||
const amount = Number(form.freightAmount)
|
||||
if (Number.isNaN(amount) || amount < 0) {
|
||||
ElMessage.warning('运费须为不小于 0 的数字')
|
||||
const message = validateFreightHeader({
|
||||
transportMode: form.transportMode,
|
||||
freightAmount: form.freightAmount,
|
||||
waybillNo: form.waybillNo
|
||||
})
|
||||
if (message) {
|
||||
ElMessage.warning(message)
|
||||
return
|
||||
}
|
||||
const header = buildFreightHeaderPayload({
|
||||
transportMode: form.transportMode,
|
||||
freightAmount: form.freightAmount,
|
||||
waybillNo: form.waybillNo
|
||||
})
|
||||
submitting.value = true
|
||||
try {
|
||||
await fetchUpdateSampleFreight(freightId.value, {
|
||||
transportMode: form.transportMode,
|
||||
freightAmount: amount
|
||||
transportMode: header.transportMode,
|
||||
waybillNo: header.waybillNo,
|
||||
freightAmount: header.freightAmount
|
||||
})
|
||||
visible.value = false
|
||||
emit('success')
|
||||
|
||||
@@ -33,7 +33,12 @@
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="样本来源">
|
||||
<el-select v-model="searchForm.sampleSource" placeholder="全部" clearable style="width: 100%">
|
||||
<el-select
|
||||
v-model="searchForm.sampleSource"
|
||||
placeholder="全部"
|
||||
clearable
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-option label="细胞存储" value="cell_storage" />
|
||||
<el-option label="科研捐赠" value="research_donation" />
|
||||
</el-select>
|
||||
@@ -100,7 +105,7 @@
|
||||
min-width="48"
|
||||
align="center"
|
||||
header-align="center"
|
||||
:selectable="(row: FlatRow) => row.isFirst"
|
||||
:selectable="(row: FlatRow) => row.isFirst && row.freight.settleStatus !== 'yjs'"
|
||||
/>
|
||||
<el-table-column
|
||||
prop="batchNo"
|
||||
@@ -123,7 +128,11 @@
|
||||
:type="transportTypeTagType(String(row.transportMode || ''))"
|
||||
size="small"
|
||||
>
|
||||
{{ row.transportModeLabel || transportTypeLabel(String(row.transportMode || '')) || '—' }}
|
||||
{{
|
||||
row.transportModeLabel ||
|
||||
transportTypeLabel(String(row.transportMode || '')) ||
|
||||
'—'
|
||||
}}
|
||||
</el-tag>
|
||||
<span v-else class="muted">—</span>
|
||||
</template>
|
||||
@@ -135,7 +144,12 @@
|
||||
align="right"
|
||||
header-align="center"
|
||||
>
|
||||
<template #default="{ row }">¥{{ formatMoneyDisplay(row.freightAmount) }}</template>
|
||||
<template #default="{ row }">{{
|
||||
freightAmountText(
|
||||
row.transportMode,
|
||||
row.freightAmount ?? row.freightDraft?.freightAmount
|
||||
)
|
||||
}}</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="linkedSampleCount"
|
||||
@@ -228,7 +242,7 @@
|
||||
header-align="center"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<div v-if="row.isFirst" class="op-row">
|
||||
<div v-if="row.isFirst && row.freight.settleStatus !== 'yjs'" class="op-row">
|
||||
<el-button class="btn-op-item" @click="openEditById(row.freightId)">
|
||||
<el-icon><Edit /></el-icon>
|
||||
编辑
|
||||
@@ -289,7 +303,7 @@
|
||||
type FreightUnlinkRow
|
||||
} from '@/components/business/freight-unlink-dialog/index.vue'
|
||||
import { formatDisplayOrDash } from '@/utils/datetime-display'
|
||||
import { formatMoneyDisplay } from '@/utils/money-format'
|
||||
import { freightAmountText } from '@/utils/sf-express-freight'
|
||||
import {
|
||||
receptionSourceLabel,
|
||||
receptionSourceTagType,
|
||||
@@ -429,11 +443,11 @@
|
||||
const linked = Number(row.linkedSampleCount || 0)
|
||||
const linkHint =
|
||||
linked > 0 ? `将解除其全部 ${linked} 条关联样本,并删除该运费单。` : '将删除该运费单。'
|
||||
ElMessageBox.confirm(
|
||||
`确认删除运费批次「${batchNo}」吗?${linkHint}`,
|
||||
'删除确认',
|
||||
{ type: 'warning', confirmButtonText: '删除', cancelButtonText: '取消' }
|
||||
)
|
||||
ElMessageBox.confirm(`确认删除运费批次「${batchNo}」吗?${linkHint}`, '删除确认', {
|
||||
type: 'warning',
|
||||
confirmButtonText: '删除',
|
||||
cancelButtonText: '取消'
|
||||
})
|
||||
.then(async () => {
|
||||
await fetchDeleteSampleFreight(freightId)
|
||||
await loadData()
|
||||
@@ -441,7 +455,10 @@
|
||||
.catch(() => undefined)
|
||||
}
|
||||
|
||||
function toUnlinkRow(it: NonNullable<SampleFreight['items']>[number], freightId: string): FreightUnlinkRow {
|
||||
function toUnlinkRow(
|
||||
it: NonNullable<SampleFreight['items']>[number],
|
||||
freightId: string
|
||||
): FreightUnlinkRow {
|
||||
return {
|
||||
...it,
|
||||
freightId,
|
||||
|
||||
Reference in new issue
Block a user