feat(pl): 运费结算审批分流与车辆挂车号等 PL 能力完善
拆分运费审批/出款页面,补充结算发票附件与菜单 SQL,并同步前后端模型与车辆相关展示。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
66d5a8714b
commit
98aec80d71
37 files changed
+1362
-195
No files matched your search
@@ -2,7 +2,7 @@
|
||||
<el-dialog
|
||||
v-model="visible"
|
||||
title="财务发放登记"
|
||||
width="640px"
|
||||
width="1200px"
|
||||
align-center
|
||||
append-to-body
|
||||
destroy-on-close
|
||||
@@ -10,60 +10,156 @@
|
||||
modal-class="order-module-dialog-mask"
|
||||
:close-on-click-modal="false"
|
||||
>
|
||||
<el-form
|
||||
ref="formRef"
|
||||
v-loading="loading"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
label-position="top"
|
||||
scroll-to-error
|
||||
:scroll-into-view-options="{ behavior: 'smooth', block: 'center' }"
|
||||
>
|
||||
<div class="form-grid-2">
|
||||
<el-form-item label="结算单号">
|
||||
<el-input :model-value="settlement?.settlementNo" disabled />
|
||||
</el-form-item>
|
||||
<el-form-item label="运费合计(元)">
|
||||
<el-input :model-value="formatAmount(settlement?.totalFreight)" disabled />
|
||||
</el-form-item>
|
||||
<el-form-item label="汇款日期" prop="remittanceDate">
|
||||
<el-date-picker
|
||||
v-model="form.remittanceDate"
|
||||
type="date"
|
||||
value-format="YYYY-MM-DD"
|
||||
placeholder="选择汇款日期"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="汇款金额(元)" prop="remittanceAmount">
|
||||
<el-input-number
|
||||
v-model="form.remittanceAmount"
|
||||
:min="0.01"
|
||||
:precision="2"
|
||||
:controls="false"
|
||||
placeholder="请输入汇款金额"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="汇款凭证" prop="voucherUrls" class="form-col-span-2">
|
||||
<div v-loading="loading" class="disburse-dialog-body">
|
||||
<section v-if="settlement" class="dialog-section">
|
||||
<h3 class="dialog-section-title">结算摘要</h3>
|
||||
<div class="summary-grid">
|
||||
<div class="summary-item">
|
||||
<span class="summary-label">结算单号</span>
|
||||
<span class="summary-value">{{ settlement.settlementNo }}</span>
|
||||
</div>
|
||||
<div class="summary-item">
|
||||
<span class="summary-label">运费合计(元)</span>
|
||||
<span class="summary-value summary-value--amount">{{
|
||||
formatAmount(settlement.totalFreight)
|
||||
}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section v-if="settlement" class="dialog-section">
|
||||
<h3 class="dialog-section-title">发票信息</h3>
|
||||
<div class="info-kv-grid info-kv-grid--cols-3">
|
||||
<div class="info-kv-item">
|
||||
<span class="info-kv-label">发票号</span>
|
||||
<span class="info-kv-value">{{ settlement.invoiceNo || '—' }}</span>
|
||||
</div>
|
||||
<div class="info-kv-item">
|
||||
<span class="info-kv-label">开票日期</span>
|
||||
<span class="info-kv-value">{{ settlement.invoiceDate || '—' }}</span>
|
||||
</div>
|
||||
<div class="info-kv-item">
|
||||
<span class="info-kv-label">发票金额(元)</span>
|
||||
<span class="info-kv-value">{{
|
||||
settlement.invoiceAmount ? formatAmount(settlement.invoiceAmount) : '—'
|
||||
}}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="settlement.invoiceAttachmentUrls" class="info-kv-item info-kv-item--full">
|
||||
<span class="info-kv-label">发票附件</span>
|
||||
<AttachmentUploadField
|
||||
v-model="voucherUrlsStr"
|
||||
folder="pl/freight-voucher"
|
||||
hint="请上传汇款凭证,至少 1 个附件"
|
||||
:model-value="settlement.invoiceAttachmentUrls"
|
||||
disabled
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="remark" class="form-col-span-2">
|
||||
<el-input
|
||||
v-model="form.remark"
|
||||
type="textarea"
|
||||
:rows="3"
|
||||
placeholder="请输入备注"
|
||||
maxlength="500"
|
||||
show-word-limit
|
||||
/>
|
||||
</el-form-item>
|
||||
</div>
|
||||
</el-form>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="dialog-section">
|
||||
<h3 class="dialog-section-title">发放登记</h3>
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
label-position="top"
|
||||
scroll-to-error
|
||||
:scroll-into-view-options="{ behavior: 'smooth', block: 'center' }"
|
||||
>
|
||||
<div class="form-grid-2">
|
||||
<el-form-item label="汇款日期" prop="remittanceDate">
|
||||
<el-date-picker
|
||||
v-model="form.remittanceDate"
|
||||
type="date"
|
||||
value-format="YYYY-MM-DD"
|
||||
placeholder="选择汇款日期"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="汇款金额(元)" prop="remittanceAmount">
|
||||
<el-input-number
|
||||
v-model="form.remittanceAmount"
|
||||
:min="0.01"
|
||||
:precision="2"
|
||||
:controls="false"
|
||||
placeholder="请输入汇款金额"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="汇款凭证附件" prop="voucherUrls" class="form-col-span-2">
|
||||
<AttachmentUploadField
|
||||
ref="attachmentRef"
|
||||
v-model="form.voucherUrls"
|
||||
folder="pl/freight-voucher"
|
||||
hint="请上传银行回单、转账截图等汇款凭证,至少 1 个附件"
|
||||
@update:model-value="formRef?.validateField('voucherUrls')"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="remark" class="form-col-span-2">
|
||||
<el-input
|
||||
v-model="form.remark"
|
||||
type="textarea"
|
||||
:rows="3"
|
||||
placeholder="汇款金额与运费合计不一致时请填写说明"
|
||||
maxlength="500"
|
||||
show-word-limit
|
||||
/>
|
||||
</el-form-item>
|
||||
</div>
|
||||
</el-form>
|
||||
</section>
|
||||
|
||||
<section v-if="settlement" class="dialog-section">
|
||||
<h3 class="dialog-section-title">关联运输信息</h3>
|
||||
<div class="panel dialog-list-panel">
|
||||
<div class="list-body list-wrap dialog-list-body-only">
|
||||
<el-table
|
||||
:data="settlement.transports || []"
|
||||
class="list-table"
|
||||
stripe
|
||||
empty-text="暂无数据"
|
||||
>
|
||||
<el-table-column type="index" label="序号" min-width="60" align="center" />
|
||||
<el-table-column
|
||||
prop="transportNo"
|
||||
label="运输编号"
|
||||
min-width="160"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column prop="licensePlate" label="车牌号" min-width="110" align="center">
|
||||
<template #default="{ row }">{{ row.licensePlate || '—' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="driverName" label="司机" min-width="90" align="center">
|
||||
<template #default="{ row }">{{ row.driverName || '—' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="carrierName"
|
||||
label="承运商"
|
||||
min-width="120"
|
||||
show-overflow-tooltip
|
||||
>
|
||||
<template #default="{ row }">{{ row.carrierName || '—' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="transportMedium"
|
||||
label="运输介质"
|
||||
min-width="100"
|
||||
align="center"
|
||||
>
|
||||
<template #default="{ row }">{{ row.transportMedium || '—' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="startPoint" label="出发地" min-width="140" show-overflow-tooltip>
|
||||
<template #default="{ row }">{{ row.startPoint || '—' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="endPoint" label="目的地" min-width="140" show-overflow-tooltip>
|
||||
<template #default="{ row }">{{ row.endPoint || '—' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="freightAmount" label="运费(元)" min-width="110" align="right">
|
||||
<template #default="{ row }">{{ formatAmount(row.freightAmount) }}</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
<el-button @click="visible = false">
|
||||
@@ -89,11 +185,12 @@ const loading = ref(false)
|
||||
const submitLoading = ref(false)
|
||||
const settlement = ref<FreightSettlement | null>(null)
|
||||
const formRef = ref<ElFormInstance>()
|
||||
const voucherUrlsStr = ref('')
|
||||
const attachmentRef = ref<InstanceType<typeof AttachmentUploadField>>()
|
||||
|
||||
const form = reactive({
|
||||
remittanceDate: '',
|
||||
remittanceAmount: undefined as number | undefined,
|
||||
voucherUrls: '',
|
||||
remark: ''
|
||||
})
|
||||
|
||||
@@ -102,12 +199,12 @@ const rules = {
|
||||
remittanceAmount: [{ required: true, message: '请输入汇款金额', trigger: 'blur' }],
|
||||
voucherUrls: [
|
||||
{
|
||||
validator: (_rule: unknown, _value: unknown, callback: (err?: Error) => void) => {
|
||||
const urls = voucherUrlsStr.value
|
||||
validator: (_rule: unknown, value: string, callback: (err?: Error) => void) => {
|
||||
const urls = String(value || '')
|
||||
.split(',')
|
||||
.map((s) => s.trim())
|
||||
.filter(Boolean)
|
||||
if (!urls.length) callback(new Error('请上传至少一个汇款凭证'))
|
||||
if (!urls.length) callback(new Error('请上传至少一个汇款凭证附件'))
|
||||
else callback()
|
||||
},
|
||||
trigger: 'change'
|
||||
@@ -120,13 +217,18 @@ function formatAmount(amount?: number) {
|
||||
return formatMoneyDisplay(amount)
|
||||
}
|
||||
|
||||
function resetForm() {
|
||||
form.remittanceDate = ''
|
||||
form.remittanceAmount = undefined
|
||||
form.voucherUrls = ''
|
||||
form.remark = ''
|
||||
}
|
||||
|
||||
async function open(id: string) {
|
||||
visible.value = true
|
||||
loading.value = true
|
||||
form.remittanceDate = ''
|
||||
form.remittanceAmount = undefined
|
||||
form.remark = ''
|
||||
voucherUrlsStr.value = ''
|
||||
resetForm()
|
||||
settlement.value = null
|
||||
try {
|
||||
const res = await freightApi.get(id)
|
||||
settlement.value = (res.data ?? res) as FreightSettlement
|
||||
@@ -138,8 +240,12 @@ async function open(id: string) {
|
||||
|
||||
async function handleSubmit() {
|
||||
if (!settlement.value) return
|
||||
if (attachmentRef.value?.hasUploading?.()) {
|
||||
ElMessage.warning('附件上传中,请稍候')
|
||||
return
|
||||
}
|
||||
await formRef.value?.validate()
|
||||
const voucherUrls = voucherUrlsStr.value
|
||||
const voucherUrls = form.voucherUrls
|
||||
.split(',')
|
||||
.map((s) => s.trim())
|
||||
.filter(Boolean)
|
||||
@@ -161,3 +267,103 @@ async function handleSubmit() {
|
||||
|
||||
defineExpose({ open })
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.disburse-dialog-body {
|
||||
min-height: 120px;
|
||||
}
|
||||
|
||||
.dialog-section {
|
||||
margin-bottom: 20px;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.dialog-section-title {
|
||||
margin: 0 0 12px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.summary-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 12px 24px;
|
||||
padding: 16px;
|
||||
border-radius: 10px;
|
||||
background: var(--primary-bg, #ecfdf5);
|
||||
border: 1px solid #a7f3d0;
|
||||
}
|
||||
|
||||
.summary-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.summary-label {
|
||||
font-size: 12px;
|
||||
color: var(--text-muted, #64748b);
|
||||
}
|
||||
|
||||
.summary-value {
|
||||
font-size: 14px;
|
||||
color: var(--text, #1e293b);
|
||||
word-break: break-all;
|
||||
|
||||
&--amount {
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
color: var(--primary-dark, #059669);
|
||||
}
|
||||
}
|
||||
|
||||
.info-kv-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 12px 24px;
|
||||
|
||||
&--cols-3 {
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
.info-kv-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
min-width: 0;
|
||||
|
||||
&--full {
|
||||
margin-top: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.info-kv-label {
|
||||
font-size: 12px;
|
||||
color: var(--text-muted, #64748b);
|
||||
}
|
||||
|
||||
.info-kv-value {
|
||||
font-size: 14px;
|
||||
color: var(--text, #1e293b);
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.info-kv-grid--cols-3 {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.summary-grid,
|
||||
.info-kv-grid,
|
||||
.info-kv-grid--cols-3 {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,8 +1,8 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
v-model="visible"
|
||||
title="编辑结算单"
|
||||
width="720px"
|
||||
:title="dialogTitle"
|
||||
width="1200px"
|
||||
align-center
|
||||
append-to-body
|
||||
destroy-on-close
|
||||
@@ -26,6 +26,8 @@
|
||||
<el-form-item label="运费合计(元)">
|
||||
<el-input :model-value="formatAmount(settlement?.totalFreight)" disabled />
|
||||
</el-form-item>
|
||||
</div>
|
||||
<div class="form-grid-3">
|
||||
<el-form-item label="发票号" prop="invoiceNo">
|
||||
<el-input v-model="form.invoiceNo" placeholder="请输入发票号" clearable />
|
||||
</el-form-item>
|
||||
@@ -48,30 +50,77 @@
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="remark" class="form-col-span-2">
|
||||
<el-input
|
||||
v-model="form.remark"
|
||||
type="textarea"
|
||||
:rows="3"
|
||||
placeholder="请输入备注"
|
||||
maxlength="500"
|
||||
show-word-limit
|
||||
/>
|
||||
</el-form-item>
|
||||
</div>
|
||||
<el-form-item label="发票附件" prop="invoiceAttachmentUrls" class="form-col-span-2">
|
||||
<AttachmentUploadField
|
||||
ref="attachmentRef"
|
||||
v-model="invoiceAttachmentUrlsStr"
|
||||
folder="pl/freight-invoice"
|
||||
hint="支持上传发票扫描件或电子发票,可多文件"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input
|
||||
v-model="form.remark"
|
||||
type="textarea"
|
||||
:rows="3"
|
||||
placeholder="请输入备注"
|
||||
maxlength="500"
|
||||
show-word-limit
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<section class="dialog-section">
|
||||
<h3 class="dialog-section-title">关联运输明细</h3>
|
||||
<div class="list-body list-wrap dialog-list-body-only">
|
||||
<el-table :data="settlement?.transports || []" class="list-table" stripe empty-text="暂无数据">
|
||||
<el-table-column type="index" label="序号" min-width="60" align="center" />
|
||||
<el-table-column prop="transportNo" label="运输编号" min-width="160" show-overflow-tooltip />
|
||||
<el-table-column prop="licensePlate" label="车牌号" min-width="120" align="center" />
|
||||
<el-table-column prop="freightAmount" label="运费(元)" min-width="120" align="right">
|
||||
<template #default="{ row }">{{ formatAmount(row.freightAmount) }}</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<h3 class="dialog-section-title">关联运输信息</h3>
|
||||
<div class="panel dialog-list-panel">
|
||||
<div class="list-body list-wrap dialog-list-body-only">
|
||||
<el-table
|
||||
:data="settlement?.transports || []"
|
||||
class="list-table"
|
||||
stripe
|
||||
empty-text="暂无数据"
|
||||
>
|
||||
<el-table-column type="index" label="序号" min-width="60" align="center" />
|
||||
<el-table-column
|
||||
prop="transportNo"
|
||||
label="运输编号"
|
||||
min-width="160"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column prop="licensePlate" label="车牌号" min-width="110" align="center">
|
||||
<template #default="{ row }">{{ row.licensePlate || '—' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="driverName" label="司机" min-width="90" align="center">
|
||||
<template #default="{ row }">{{ row.driverName || '—' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="carrierName"
|
||||
label="承运商"
|
||||
min-width="120"
|
||||
show-overflow-tooltip
|
||||
>
|
||||
<template #default="{ row }">{{ row.carrierName || '—' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="transportMedium"
|
||||
label="运输介质"
|
||||
min-width="100"
|
||||
align="center"
|
||||
>
|
||||
<template #default="{ row }">{{ row.transportMedium || '—' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="startPoint" label="出发地" min-width="140" show-overflow-tooltip>
|
||||
<template #default="{ row }">{{ row.startPoint || '—' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="endPoint" label="目的地" min-width="140" show-overflow-tooltip>
|
||||
<template #default="{ row }">{{ row.endPoint || '—' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="freightAmount" label="运费(元)" min-width="110" align="right">
|
||||
<template #default="{ row }">{{ formatAmount(row.freightAmount) }}</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -89,6 +138,7 @@
|
||||
<script setup lang="ts">
|
||||
import { Check, Close } from '@element-plus/icons-vue'
|
||||
import { freightApi, type FreightSettlement } from '@/api/pl'
|
||||
import AttachmentUploadField from '@/components/business/attachment-upload-field/index.vue'
|
||||
import { formatMoneyDisplay } from '@/utils/money-format'
|
||||
|
||||
const emit = defineEmits<{ success: [] }>()
|
||||
@@ -98,6 +148,8 @@ const loading = ref(false)
|
||||
const submitLoading = ref(false)
|
||||
const settlement = ref<FreightSettlement | null>(null)
|
||||
const formRef = ref<ElFormInstance>()
|
||||
const attachmentRef = ref<InstanceType<typeof AttachmentUploadField>>()
|
||||
const invoiceAttachmentUrlsStr = ref('')
|
||||
|
||||
const form = reactive({
|
||||
invoiceNo: '',
|
||||
@@ -108,6 +160,11 @@ const form = reactive({
|
||||
|
||||
const rules = {}
|
||||
|
||||
const dialogTitle = computed(() => {
|
||||
if (settlement.value?.settlementStatus === 'approved') return '发票信息'
|
||||
return '编辑结算单'
|
||||
})
|
||||
|
||||
function formatAmount(amount?: number) {
|
||||
if (!amount) return '¥0.00'
|
||||
return formatMoneyDisplay(amount)
|
||||
@@ -116,6 +173,7 @@ function formatAmount(amount?: number) {
|
||||
async function open(id: string) {
|
||||
visible.value = true
|
||||
loading.value = true
|
||||
invoiceAttachmentUrlsStr.value = ''
|
||||
try {
|
||||
const res = await freightApi.get(id)
|
||||
settlement.value = (res.data ?? res) as FreightSettlement
|
||||
@@ -123,6 +181,7 @@ async function open(id: string) {
|
||||
form.invoiceDate = settlement.value.invoiceDate || ''
|
||||
form.invoiceAmount = settlement.value.invoiceAmount
|
||||
form.remark = settlement.value.remark || ''
|
||||
invoiceAttachmentUrlsStr.value = settlement.value.invoiceAttachmentUrls || ''
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
@@ -130,12 +189,17 @@ async function open(id: string) {
|
||||
|
||||
async function handleSubmit() {
|
||||
if (!settlement.value) return
|
||||
if (attachmentRef.value?.hasUploading?.()) {
|
||||
ElMessage.warning('附件上传中,请稍候')
|
||||
return
|
||||
}
|
||||
submitLoading.value = true
|
||||
try {
|
||||
await freightApi.update(settlement.value.id, {
|
||||
invoiceNo: form.invoiceNo || undefined,
|
||||
invoiceDate: form.invoiceDate || undefined,
|
||||
invoiceAmount: form.invoiceAmount,
|
||||
invoiceAttachmentUrls: invoiceAttachmentUrlsStr.value || undefined,
|
||||
remark: form.remark || undefined
|
||||
})
|
||||
ElMessage.success('保存成功')
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<el-dialog
|
||||
v-model="visible"
|
||||
:title="settlement?.settlementNo || '结算单详情'"
|
||||
width="calc(100vw - 100px)"
|
||||
width="1200px"
|
||||
align-center
|
||||
append-to-body
|
||||
destroy-on-close
|
||||
@@ -25,7 +25,7 @@
|
||||
|
||||
<section v-if="settlement" class="view-section">
|
||||
<h4 class="view-section-title">发票信息</h4>
|
||||
<div class="view-kv-grid">
|
||||
<div class="view-kv-grid view-kv-grid--cols-3">
|
||||
<div class="view-kv-item">
|
||||
<span class="view-kv-label">发票号</span>
|
||||
<span class="view-kv-value">{{ settlement.invoiceNo || '—' }}</span>
|
||||
@@ -40,11 +40,22 @@
|
||||
settlement.invoiceAmount ? formatAmount(settlement.invoiceAmount) : '—'
|
||||
}}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="view-kv-item view-kv-item--full">
|
||||
<span class="view-kv-label">发票附件</span>
|
||||
<AttachmentUploadField
|
||||
v-if="settlement.invoiceAttachmentUrls"
|
||||
:model-value="settlement.invoiceAttachmentUrls"
|
||||
disabled
|
||||
/>
|
||||
<span v-else class="view-kv-value">—</span>
|
||||
</div>
|
||||
<div class="view-kv-grid view-kv-grid--cols-2 view-kv-grid--mt">
|
||||
<div class="view-kv-item">
|
||||
<span class="view-kv-label">创建时间</span>
|
||||
<span class="view-kv-value">{{ formatDateTime(settlement.createTime) }}</span>
|
||||
</div>
|
||||
<div class="view-kv-item view-kv-item--full">
|
||||
<div class="view-kv-item">
|
||||
<span class="view-kv-label">备注</span>
|
||||
<span class="view-kv-value">{{ settlement.remark || '—' }}</span>
|
||||
</div>
|
||||
@@ -52,17 +63,55 @@
|
||||
</section>
|
||||
|
||||
<section v-if="settlement" class="view-section">
|
||||
<h4 class="view-section-title">关联运输明细</h4>
|
||||
<div class="list-body list-wrap dialog-list-body-only">
|
||||
<el-table :data="settlement.transports || []" class="list-table" stripe empty-text="暂无数据">
|
||||
<el-table-column type="index" label="序号" min-width="60" align="center" />
|
||||
<el-table-column prop="transportNo" label="运输编号" min-width="160" show-overflow-tooltip />
|
||||
<el-table-column prop="licensePlate" label="车牌号" min-width="120" align="center" />
|
||||
<el-table-column prop="driverName" label="司机" min-width="100" align="center" />
|
||||
<el-table-column prop="freightAmount" label="运费(元)" min-width="120" align="right">
|
||||
<template #default="{ row }">{{ formatAmount(row.freightAmount) }}</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<h4 class="view-section-title">关联运输信息</h4>
|
||||
<div class="panel dialog-list-panel">
|
||||
<div class="list-body list-wrap dialog-list-body-only">
|
||||
<el-table
|
||||
:data="settlement.transports || []"
|
||||
class="list-table"
|
||||
stripe
|
||||
empty-text="暂无数据"
|
||||
>
|
||||
<el-table-column type="index" label="序号" min-width="60" align="center" />
|
||||
<el-table-column
|
||||
prop="transportNo"
|
||||
label="运输编号"
|
||||
min-width="160"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column prop="licensePlate" label="车牌号" min-width="110" align="center">
|
||||
<template #default="{ row }">{{ row.licensePlate || '—' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="driverName" label="司机" min-width="90" align="center">
|
||||
<template #default="{ row }">{{ row.driverName || '—' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="carrierName"
|
||||
label="承运商"
|
||||
min-width="120"
|
||||
show-overflow-tooltip
|
||||
>
|
||||
<template #default="{ row }">{{ row.carrierName || '—' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="transportMedium"
|
||||
label="运输介质"
|
||||
min-width="100"
|
||||
align="center"
|
||||
>
|
||||
<template #default="{ row }">{{ row.transportMedium || '—' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="startPoint" label="出发地" min-width="140" show-overflow-tooltip>
|
||||
<template #default="{ row }">{{ row.startPoint || '—' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="endPoint" label="目的地" min-width="140" show-overflow-tooltip>
|
||||
<template #default="{ row }">{{ row.endPoint || '—' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="freightAmount" label="运费(元)" min-width="110" align="right">
|
||||
<template #default="{ row }">{{ formatAmount(row.freightAmount) }}</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -76,17 +125,42 @@
|
||||
class="view-section"
|
||||
>
|
||||
<h4 class="view-section-title">汇款凭证</h4>
|
||||
<div class="list-body list-wrap dialog-list-body-only">
|
||||
<el-table :data="settlement.vouchers" class="list-table" stripe>
|
||||
<el-table-column prop="remittanceDate" label="汇款日期" min-width="120" align="center" />
|
||||
<el-table-column prop="remittanceAmount" label="汇款金额" min-width="120" align="right">
|
||||
<template #default="{ row }">{{ formatAmount(row.remittanceAmount) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="remark" label="备注" min-width="160" show-overflow-tooltip />
|
||||
<el-table-column prop="createTime" label="登记时间" min-width="160" align="center">
|
||||
<template #default="{ row }">{{ formatDateTime(row.createTime) }}</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div
|
||||
v-for="(voucher, index) in settlement.vouchers"
|
||||
:key="voucher.id || index"
|
||||
class="voucher-block"
|
||||
>
|
||||
<div class="view-kv-grid view-kv-grid--cols-3">
|
||||
<div class="view-kv-item">
|
||||
<span class="view-kv-label">汇款日期</span>
|
||||
<span class="view-kv-value">{{ voucher.remittanceDate || '—' }}</span>
|
||||
</div>
|
||||
<div class="view-kv-item">
|
||||
<span class="view-kv-label">汇款金额</span>
|
||||
<span class="view-kv-value">{{
|
||||
voucher.remittanceAmount ? formatAmount(voucher.remittanceAmount) : '—'
|
||||
}}</span>
|
||||
</div>
|
||||
<div class="view-kv-item">
|
||||
<span class="view-kv-label">登记时间</span>
|
||||
<span class="view-kv-value">{{
|
||||
voucher.createTime ? formatDateTime(voucher.createTime) : '—'
|
||||
}}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="voucher.remark" class="view-kv-item view-kv-item--full">
|
||||
<span class="view-kv-label">备注</span>
|
||||
<span class="view-kv-value">{{ voucher.remark }}</span>
|
||||
</div>
|
||||
<div class="view-kv-item view-kv-item--full">
|
||||
<span class="view-kv-label">汇款凭证附件</span>
|
||||
<AttachmentUploadField
|
||||
v-if="normalizeVoucherUrls(voucher.voucherUrls)"
|
||||
:model-value="normalizeVoucherUrls(voucher.voucherUrls)"
|
||||
disabled
|
||||
/>
|
||||
<span v-else class="view-kv-value">—</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
@@ -103,6 +177,8 @@
|
||||
import { Close } from '@element-plus/icons-vue'
|
||||
import { freightApi, type FreightSettlement } from '@/api/pl'
|
||||
import ApprovalFlowView from '@/components/bx/base/ApprovalFlowView.vue'
|
||||
import AttachmentUploadField from '@/components/business/attachment-upload-field/index.vue'
|
||||
import { normalizeAttachmentUrls } from '@/utils/download-attachment'
|
||||
import {
|
||||
getSettlementStatusLabel,
|
||||
getSettlementStatusTagType,
|
||||
@@ -124,6 +200,10 @@ function formatAmount(amount?: number) {
|
||||
return formatMoneyDisplay(amount)
|
||||
}
|
||||
|
||||
function normalizeVoucherUrls(raw?: string) {
|
||||
return normalizeAttachmentUrls(raw)
|
||||
}
|
||||
|
||||
async function open(id: string) {
|
||||
visible.value = true
|
||||
loading.value = true
|
||||
@@ -199,16 +279,29 @@ defineExpose({ open })
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 12px 24px;
|
||||
|
||||
&--cols-3 {
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
&--cols-2 {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
&--mt {
|
||||
margin-top: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.view-kv-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
}
|
||||
min-width: 0;
|
||||
|
||||
.view-kv-item--full {
|
||||
grid-column: 1 / -1;
|
||||
&--full {
|
||||
margin-top: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.view-kv-label {
|
||||
@@ -222,8 +315,27 @@ defineExpose({ open })
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.voucher-block {
|
||||
padding: 16px;
|
||||
border: 1px solid var(--border, #d9e8e2);
|
||||
border-radius: 10px;
|
||||
background: #fff;
|
||||
|
||||
& + & {
|
||||
margin-top: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.view-kv-grid--cols-3 {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.view-kv-grid {
|
||||
.view-kv-grid,
|
||||
.view-kv-grid--cols-3,
|
||||
.view-kv-grid--cols-2 {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -313,18 +313,20 @@
|
||||
copyPlanSummary({
|
||||
pickupAddress: formData.pickupAddress,
|
||||
destinationAddress: formData.destinationAddress,
|
||||
pickupNotes: formData.pickupNotes,
|
||||
destinationNotes: formData.destinationNotes,
|
||||
licensePlate: vehicleList.find(v => v.id === formData.vehicleId)?.licensePlate,
|
||||
driverUrl: arrangeResult?.driverUrl
|
||||
})
|
||||
"
|
||||
>
|
||||
<el-icon><CopyDocument /></el-icon>复制计划简述
|
||||
<el-icon><CopyDocument /></el-icon>复制给司机
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="hasSiteInventoryRecords"
|
||||
@click="copySiteSummary({ licensePlate: vehicleList.find(v => v.id === formData.vehicleId)?.licensePlate })"
|
||||
>
|
||||
<el-icon><CopyDocument /></el-icon>复制现场简述
|
||||
<el-icon><CopyDocument /></el-icon>复制给现场
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="hasSiteInventoryRecords && siteRecords.length > 1"
|
||||
@@ -1058,7 +1060,7 @@ async function handleSubmit() {
|
||||
|
||||
<style lang="scss">
|
||||
.el-dialog.pl-plan-arrange-dialog.dialog-form-layout {
|
||||
width: min(1680px, calc(100vw - 80px)) !important;
|
||||
width: min(1840px, calc(100vw - 48px)) !important;
|
||||
max-height: calc(100vh - 48px);
|
||||
}
|
||||
</style>
|
||||
@@ -74,7 +74,7 @@
|
||||
</div>
|
||||
|
||||
<h4 class="pl-plan-subtitle">合同产品预览</h4>
|
||||
<PlPlanFormProductTable :products="formData.products" />
|
||||
<PlPlanFormProductTable :products="contractProductRows" />
|
||||
</template>
|
||||
</section>
|
||||
|
||||
@@ -281,6 +281,7 @@ import PlVehicleViewDialog from '@/components/business/pl-vehicle-view-dialog/in
|
||||
import PlPlanFormProductTable from './PlPlanFormProductTable.vue'
|
||||
import {
|
||||
applyContractToPlanForm,
|
||||
mapContractProducts,
|
||||
type PlanFormProductRow
|
||||
} from '@/composables/usePlContractToPlan'
|
||||
import { enrichPlanProductsHazardFromCrm } from '@/composables/usePlToxicProductCertCheck'
|
||||
@@ -464,6 +465,11 @@ const contractBasicFieldRows = computed(() => {
|
||||
const contractBasicRow1Fields = computed(() => contractBasicFieldRows.value.row1)
|
||||
const contractBasicRow2Fields = computed(() => contractBasicFieldRows.value.row2)
|
||||
|
||||
/** 合同产品预览:固定展示合同原始明细,与下方可编辑的计划产品分离 */
|
||||
const contractProductRows = computed<PlanFormProductRow[]>(() =>
|
||||
mapContractProducts(contractSnapshot.value?.products)
|
||||
)
|
||||
|
||||
function resolveSelfCarrierId() {
|
||||
const matched = carrierList.value.find(item => item.carrierName === SELF_CARRIER_NAME)
|
||||
formData.carrierId = matched?.id || ''
|
||||
|
||||
@@ -65,10 +65,10 @@
|
||||
<div class="dialog-footer pl-plan-view-footer">
|
||||
<template v-if="showArrangeActions">
|
||||
<el-button @click="copyPlanSummary()">
|
||||
<el-icon><CopyDocument /></el-icon>复制计划简述
|
||||
<el-icon><CopyDocument /></el-icon>复制给司机
|
||||
</el-button>
|
||||
<el-button v-if="hasSiteInventoryRecords" @click="copySiteSummary()">
|
||||
<el-icon><CopyDocument /></el-icon>复制现场简述
|
||||
<el-icon><CopyDocument /></el-icon>复制给现场
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="hasSiteInventoryRecords && siteRecords.length > 1"
|
||||
|
||||
@@ -135,7 +135,7 @@
|
||||
<div class="pl-plan-dibang-head-actions">
|
||||
<el-button class="btn-op-item" @click="copySiteSummaryForRow(row)">
|
||||
<el-icon><CopyDocument /></el-icon>
|
||||
复制现场简述
|
||||
复制给现场
|
||||
</el-button>
|
||||
<el-button class="btn-op-item" @click="copySiteLinkForRow(row)">
|
||||
<el-icon><CopyDocument /></el-icon>
|
||||
@@ -374,6 +374,7 @@
|
||||
buildSiteSummaryFromInventoryLinks
|
||||
} from '@/utils/db-site-summary'
|
||||
import {
|
||||
formatPlanProductListText,
|
||||
getPlFreightTypeLabel,
|
||||
getPlPlanCompanyLabel,
|
||||
getPlPlanTypeLabel,
|
||||
@@ -492,15 +493,21 @@
|
||||
ElMessage.warning('暂无现场链接')
|
||||
return
|
||||
}
|
||||
const product = products.value.find(item => item.id === row.link.planProductId)
|
||||
const productText = product
|
||||
? formatPlanProductListText(product, unit => dictStore.getLabel('crm_product_unit', unit) || unit, {
|
||||
usePlannedQuantity: true
|
||||
})
|
||||
: row.link.productName || '—'
|
||||
const text = buildSiteSummaryFromInventoryLinks({
|
||||
planNo: row.link.planNo || plan.value?.planNo,
|
||||
licensePlate: plan.value?.licensePlate || transport.value?.licensePlate,
|
||||
pickupTime: plan.value?.pickupTime,
|
||||
planDate: plan.value?.planDate,
|
||||
records: [{ ...row.link, siteUrl: url }]
|
||||
records: [{ ...row.link, siteUrl: url, productText }]
|
||||
})
|
||||
await navigator.clipboard.writeText(text)
|
||||
ElMessage.success('已复制现场简述')
|
||||
ElMessage.success('已复制给现场')
|
||||
}
|
||||
|
||||
const displayFeedbackTime = computed(() => formatDateTime(transport.value?.feedbackTime) || '—')
|
||||
|
||||
@@ -6,6 +6,9 @@
|
||||
<span class="pl-vehicle-summary-value pl-vehicle-summary-value--strong">
|
||||
{{ detail.licensePlate || '—' }}
|
||||
</span>
|
||||
<span v-if="detail.trailerNo" class="pl-vehicle-summary-value">
|
||||
{{ detail.trailerNo }}(挂车)
|
||||
</span>
|
||||
<el-tag :type="getPlVehicleTypeTagType(detail.vehicleType)" size="small">
|
||||
{{ getPlVehicleTypeLabel(detail.vehicleType) }}
|
||||
</el-tag>
|
||||
|
||||
@@ -80,6 +80,7 @@ const viewSections = computed<ViewSection[]>(() => {
|
||||
tableMode: 'quad',
|
||||
fields: [
|
||||
{ label: '车牌号', value: d.licensePlate },
|
||||
{ label: '挂车号', value: d.trailerNo || '—' },
|
||||
{
|
||||
label: '车辆类型',
|
||||
value: getPlVehicleTypeLabel(d.vehicleType),
|
||||
|
||||
@@ -67,6 +67,7 @@ const vehicleViewFields = computed<ViewField[]>(() => {
|
||||
const d = props.detail
|
||||
return [
|
||||
{ label: '车牌号', value: d.licensePlate },
|
||||
{ label: '挂车号', value: d.trailerNo || '—' },
|
||||
{
|
||||
label: '车辆类型',
|
||||
value: getPlVehicleTypeLabel(d.vehicleType),
|
||||
|
||||
Reference in new issue
Block a user