Compare commits
9 Commits
9b9e652afd
...
a9b4ab069b
| Author | SHA1 | Date | |
|---|---|---|---|
| a9b4ab069b | |||
| 449e4e1884 | |||
| dea760ea1c | |||
| 2451efdfcb | |||
| a75ac1cfa4 | |||
| a10eea247f | |||
| 4fb8c74b5c | |||
| a3ff00eaaf | |||
| d901d484fd |
@@ -24,10 +24,11 @@ export const getDetail = id =>
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export const getCarrierContracts = () =>
|
export const getCarrierContracts = projectIds =>
|
||||||
request({
|
request({
|
||||||
url: `${baseUrl}/carrier-contracts`,
|
url: `${baseUrl}/carrier-contracts`,
|
||||||
method: 'get',
|
method: 'get',
|
||||||
|
params: { projectIds: (projectIds || []).join(',') },
|
||||||
});
|
});
|
||||||
|
|
||||||
export const saveDraft = row =>
|
export const saveDraft = row =>
|
||||||
|
|||||||
@@ -19,6 +19,12 @@ export const changeRoute = data =>
|
|||||||
method: 'post',
|
method: 'post',
|
||||||
data,
|
data,
|
||||||
});
|
});
|
||||||
|
export const maintainMileage = data =>
|
||||||
|
request({
|
||||||
|
url: `${baseUrl}/maintain-mileage`,
|
||||||
|
method: 'post',
|
||||||
|
data,
|
||||||
|
});
|
||||||
export const roadLoading = ids =>
|
export const roadLoading = ids =>
|
||||||
request({
|
request({
|
||||||
url: `${baseUrl}/road-loading`,
|
url: `${baseUrl}/road-loading`,
|
||||||
|
|||||||
@@ -5,8 +5,12 @@ const baseUrl = '/blade-transport/invoice-application';
|
|||||||
export const getList = (current, size, params) =>
|
export const getList = (current, size, params) =>
|
||||||
request({ url: `${baseUrl}/list`, method: 'get', params: { current, size, ...params } });
|
request({ url: `${baseUrl}/list`, method: 'get', params: { current, size, ...params } });
|
||||||
export const getDetail = id => request({ url: `${baseUrl}/detail`, method: 'get', params: { id } });
|
export const getDetail = id => request({ url: `${baseUrl}/detail`, method: 'get', params: { id } });
|
||||||
export const getSettlementCandidates = keyword =>
|
export const getSettlementCandidates = (current, size, params) =>
|
||||||
request({ url: `${baseUrl}/settlement-candidates`, method: 'get', params: { keyword } });
|
request({
|
||||||
|
url: `${baseUrl}/settlement-candidates`,
|
||||||
|
method: 'get',
|
||||||
|
params: { current, size, ...params },
|
||||||
|
});
|
||||||
export const getSettlementDetails = settlementIds =>
|
export const getSettlementDetails = settlementIds =>
|
||||||
request({ url: `${baseUrl}/settlement-details`, method: 'get', params: { settlementIds } });
|
request({ url: `${baseUrl}/settlement-details`, method: 'get', params: { settlementIds } });
|
||||||
export const getReceiverInformation = settlementIds =>
|
export const getReceiverInformation = settlementIds =>
|
||||||
|
|||||||
@@ -32,6 +32,12 @@ export const syncKingdee = id =>
|
|||||||
request({ url: `${baseUrl}/sync-kingdee`, method: 'post', params: { id } });
|
request({ url: `${baseUrl}/sync-kingdee`, method: 'post', params: { id } });
|
||||||
export const getDetailFees = detailId =>
|
export const getDetailFees = detailId =>
|
||||||
request({ url: `${baseUrl}/detail-fees`, method: 'get', params: { detailId } });
|
request({ url: `${baseUrl}/detail-fees`, method: 'get', params: { detailId } });
|
||||||
|
export const getReceiptClaims = formalSettlementId =>
|
||||||
|
request({
|
||||||
|
url: `${baseUrl}/receipt-claims`,
|
||||||
|
method: 'get',
|
||||||
|
params: { formalSettlementId },
|
||||||
|
});
|
||||||
export const adjustDetail = data =>
|
export const adjustDetail = data =>
|
||||||
request({ url: `${baseUrl}/adjust-detail`, method: 'post', data });
|
request({ url: `${baseUrl}/adjust-detail`, method: 'post', data });
|
||||||
export const applyPayment = data =>
|
export const applyPayment = data =>
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ export const getFormalSettlements = keyword =>
|
|||||||
request({ url: `${baseUrl}/candidate-formal-settlements`, method: 'get', params: { keyword } });
|
request({ url: `${baseUrl}/candidate-formal-settlements`, method: 'get', params: { keyword } });
|
||||||
export const getFormalDetails = formalSettlementId =>
|
export const getFormalDetails = formalSettlementId =>
|
||||||
request({ url: `${baseUrl}/formal-details`, method: 'get', params: { formalSettlementId } });
|
request({ url: `${baseUrl}/formal-details`, method: 'get', params: { formalSettlementId } });
|
||||||
|
export const getFeeOptions = () =>
|
||||||
|
request({ url: `${baseUrl}/fee-options`, method: 'get' });
|
||||||
export const save = data => request({ url: `${baseUrl}/save`, method: 'post', data });
|
export const save = data => request({ url: `${baseUrl}/save`, method: 'post', data });
|
||||||
export const remove = id => request({ url: `${baseUrl}/remove`, method: 'post', params: { id } });
|
export const remove = id => request({ url: `${baseUrl}/remove`, method: 'post', params: { id } });
|
||||||
export const submit = id => request({ url: `${baseUrl}/submit`, method: 'post', data: { id } });
|
export const submit = id => request({ url: `${baseUrl}/submit`, method: 'post', data: { id } });
|
||||||
|
|||||||
@@ -51,9 +51,9 @@
|
|||||||
<vehicle-attachment-upload
|
<vehicle-attachment-upload
|
||||||
:model-value="rows"
|
:model-value="rows"
|
||||||
:file-types="attachmentFileTypes"
|
:file-types="attachmentFileTypes"
|
||||||
:max-size="500"
|
:max-size="maxSize"
|
||||||
:show-tip="true"
|
:show-tip="true"
|
||||||
tip="支持pdf、bmp、jpeg、png、jpg、doc、docx、ppt、pptx、xlsx、xls、eml、msg、zip的文件格式,单个文件不超过500M"
|
:tip="`支持pdf、bmp、jpeg、png、jpg、doc、docx、ppt、pptx、xlsx、xls、eml、msg、zip的文件格式,单个文件不超过${maxSize}MB`"
|
||||||
:show-file-list="false"
|
:show-file-list="false"
|
||||||
button-text="上传附件"
|
button-text="上传附件"
|
||||||
@update:model-value="handleUploadChange"
|
@update:model-value="handleUploadChange"
|
||||||
@@ -149,6 +149,10 @@ export default {
|
|||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
|
maxSize: {
|
||||||
|
type: Number,
|
||||||
|
default: 500,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
emits: ['update:modelValue'],
|
emits: ['update:modelValue'],
|
||||||
data() {
|
data() {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
export const receiptFlowTableColumns = [
|
export const receiptFlowTableColumns = [
|
||||||
{ prop: 'receiptNoticeNo', label: '认领通知单', minWidth: 150 },
|
{ prop: 'receiptNoticeNo', label: '认领通知单', minWidth: 150, link: true },
|
||||||
{ prop: 'payerName', label: '付款人', minWidth: 130 },
|
{ prop: 'payerName', label: '付款人', minWidth: 130 },
|
||||||
{ prop: 'receiptAmount', label: '付款金额', minWidth: 130, money: true },
|
{ prop: 'receiptAmount', label: '付款金额', minWidth: 130, money: true },
|
||||||
{ prop: 'counterpartyName', label: '对方户名', minWidth: 150 },
|
{ prop: 'counterpartyName', label: '对方户名', minWidth: 150 },
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ export const formalSettlementFormFields = [
|
|||||||
{ label: '汇率日期', prop: 'exchangeRateDate', type: 'date', required: true },
|
{ label: '汇率日期', prop: 'exchangeRateDate', type: 'date', required: true },
|
||||||
{ label: '结算汇率', prop: 'exchangeRate', type: 'number', required: true },
|
{ label: '结算汇率', prop: 'exchangeRate', type: 'number', required: true },
|
||||||
{ label: '本位币合计', prop: 'localSettlementAmount', readonly: true, money: true },
|
{ label: '本位币合计', prop: 'localSettlementAmount', readonly: true, money: true },
|
||||||
{ label: '申请付款金额(含预付)', prop: 'appliedPaymentAmount', readonly: true, money: true },
|
{ label: '创建人', prop: 'createUserName', readonly: true },
|
||||||
{ label: '已收/已付合计', prop: 'paidAmount', readonly: true, money: true },
|
{ label: '创建时间', prop: 'createTime', readonly: true },
|
||||||
];
|
];
|
||||||
|
|
||||||
export const createFormalSettlementForm = () => ({
|
export const createFormalSettlementForm = () => ({
|
||||||
@@ -27,6 +27,8 @@ export const createFormalSettlementForm = () => ({
|
|||||||
sourceDetailIds: [],
|
sourceDetailIds: [],
|
||||||
exchangeRateDate: '',
|
exchangeRateDate: '',
|
||||||
exchangeRate: 1,
|
exchangeRate: 1,
|
||||||
|
createUserName: '',
|
||||||
|
createTime: '',
|
||||||
attachmentsJson: '[]',
|
attachmentsJson: '[]',
|
||||||
remark: '',
|
remark: '',
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -7,11 +7,21 @@ export const approvalStatusOptions = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
export const invoiceStatusOptions = [
|
export const invoiceStatusOptions = [
|
||||||
{ label: '未收/开票', value: 'unreceived' },
|
{ label: '未开/收票', value: 'unreceived' },
|
||||||
{ label: '部分收/开票', value: 'partial' },
|
{ label: '部分开/收票', value: 'partial' },
|
||||||
{ label: '已收/开票', value: 'completed' },
|
{ label: '已开/收票', value: 'completed' },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
export const invoiceStatusName = (value, settlementType) => {
|
||||||
|
const payable = settlementType === 'payable';
|
||||||
|
const labels = {
|
||||||
|
unreceived: payable ? '未收票' : '未开票',
|
||||||
|
partial: payable ? '部分收票' : '部分开票',
|
||||||
|
completed: payable ? '已收票' : '已开票',
|
||||||
|
};
|
||||||
|
return labels[value] || value || '-';
|
||||||
|
};
|
||||||
|
|
||||||
export const paymentStatusOptions = [
|
export const paymentStatusOptions = [
|
||||||
{ label: '未收/付款', value: 'unpaid' },
|
{ label: '未收/付款', value: 'unpaid' },
|
||||||
{ label: '部分收/付款', value: 'partial' },
|
{ label: '部分收/付款', value: 'partial' },
|
||||||
|
|||||||
@@ -10,8 +10,18 @@ export const preSettlementTableColumns = [
|
|||||||
{ label: '原币结算金额', prop: 'settlementAmount', minWidth: 140, money: true, precision: 2 },
|
{ label: '原币结算金额', prop: 'settlementAmount', minWidth: 140, money: true, precision: 2 },
|
||||||
{ label: '本位币结算金额', prop: 'localSettlementAmount', minWidth: 150, money: true, precision: 2 },
|
{ label: '本位币结算金额', prop: 'localSettlementAmount', minWidth: 150, money: true, precision: 2 },
|
||||||
{ label: '结算汇率', prop: 'exchangeRate', minWidth: 110, precision: 2 },
|
{ label: '结算汇率', prop: 'exchangeRate', minWidth: 110, precision: 2 },
|
||||||
{ label: '申请预付金额', prop: 'advanceAppliedAmount', minWidth: 140, money: true },
|
{
|
||||||
{ label: '已付款金额', prop: 'advancePaidAmount', minWidth: 130, money: true },
|
label: '申请预付金额',
|
||||||
|
prop: 'advanceAppliedAmount',
|
||||||
|
minWidth: 190,
|
||||||
|
money: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '已付款金额',
|
||||||
|
prop: 'advancePaidAmount',
|
||||||
|
minWidth: 200,
|
||||||
|
money: true,
|
||||||
|
},
|
||||||
{ label: '审核状态', prop: 'approvalStatusName', minWidth: 110, status: true },
|
{ label: '审核状态', prop: 'approvalStatusName', minWidth: 110, status: true },
|
||||||
{ label: '当前节点', prop: 'currentNode', minWidth: 130 },
|
{ label: '当前节点', prop: 'currentNode', minWidth: 130 },
|
||||||
{ label: '当前处理人', prop: 'currentProcessor', minWidth: 130 },
|
{ label: '当前处理人', prop: 'currentProcessor', minWidth: 130 },
|
||||||
|
|||||||
@@ -1,13 +1,15 @@
|
|||||||
export const settlementAdjustmentFormFields = [
|
export const settlementAdjustmentFormFields = [
|
||||||
{ label: '单据号', prop: 'adjustmentNo' },
|
{ label: '单据号', prop: 'adjustmentNo' },
|
||||||
{ label: '关联结算单号', prop: 'formalSettlementId' },
|
{ label: '关联结算单号', prop: 'formalSettlementId' },
|
||||||
{ label: '客户/客商名称', prop: 'customerName' },
|
{ label: '客户', prop: 'customerName' },
|
||||||
{ label: '结算单类型', prop: 'settlementTypeName' },
|
{ label: '结算单类型', prop: 'settlementTypeName' },
|
||||||
{ label: '关联合同名称', prop: 'contractName' },
|
{ label: '关联合同名称', prop: 'contractName' },
|
||||||
{ label: '关联项目', prop: 'projectName' },
|
{ label: '关联项目', prop: 'projectName' },
|
||||||
{ label: '原结算金额', prop: 'originalSettlementAmount', money: true },
|
{ label: '原结算金额', prop: 'originalSettlementAmount', money: true },
|
||||||
{ label: '调整金额', prop: 'adjustmentAmount', money: true },
|
{ label: '调整金额', prop: 'adjustmentAmount', money: true },
|
||||||
{ label: '调整后结算金额', prop: 'adjustedSettlementAmount', money: true },
|
{ label: '调整后结算金额', prop: 'adjustedSettlementAmount', money: true },
|
||||||
|
{ label: '创建日期', prop: 'createTime' },
|
||||||
|
{ label: '创建人', prop: 'createUserName' },
|
||||||
];
|
];
|
||||||
|
|
||||||
export const createSettlementAdjustmentForm = () => ({
|
export const createSettlementAdjustmentForm = () => ({
|
||||||
@@ -24,6 +26,8 @@ export const createSettlementAdjustmentForm = () => ({
|
|||||||
adjustmentAmount: 0,
|
adjustmentAmount: 0,
|
||||||
originalSettlementAmount: 0,
|
originalSettlementAmount: 0,
|
||||||
adjustedSettlementAmount: 0,
|
adjustedSettlementAmount: 0,
|
||||||
|
createTime: '',
|
||||||
|
createUserName: '',
|
||||||
attachmentsJson: '[]',
|
attachmentsJson: '[]',
|
||||||
remark: '',
|
remark: '',
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -8,8 +8,6 @@ export const settlementAdjustmentTableColumns = [
|
|||||||
{ label: '合同编号', prop: 'contractNo', minWidth: 150 },
|
{ label: '合同编号', prop: 'contractNo', minWidth: 150 },
|
||||||
{ label: '合同名称', prop: 'contractName', minWidth: 170 },
|
{ label: '合同名称', prop: 'contractName', minWidth: 170 },
|
||||||
{ label: '调整金额', prop: 'adjustmentAmount', minWidth: 130, money: true },
|
{ label: '调整金额', prop: 'adjustmentAmount', minWidth: 130, money: true },
|
||||||
{ label: '原结算金额', prop: 'originalSettlementAmount', minWidth: 140, money: true },
|
|
||||||
{ label: '调整后结算金额', prop: 'adjustedSettlementAmount', minWidth: 160, money: true },
|
|
||||||
{ label: '审核状态', prop: 'approvalStatusName', minWidth: 110, status: true },
|
{ label: '审核状态', prop: 'approvalStatusName', minWidth: 110, status: true },
|
||||||
{ label: '当前节点', prop: 'currentNode', minWidth: 120 },
|
{ label: '当前节点', prop: 'currentNode', minWidth: 120 },
|
||||||
{ label: '当前处理人', prop: 'currentProcessor', minWidth: 130 },
|
{ label: '当前处理人', prop: 'currentProcessor', minWidth: 130 },
|
||||||
|
|||||||
@@ -666,7 +666,7 @@
|
|||||||
class="business-crud-page__task-span-1"
|
class="business-crud-page__task-span-1"
|
||||||
>
|
>
|
||||||
<el-select
|
<el-select
|
||||||
v-model="form.carrierName"
|
v-model="taskCarrierValue"
|
||||||
:placeholder="`请选择${taskCarrierLabel}`"
|
:placeholder="`请选择${taskCarrierLabel}`"
|
||||||
filterable
|
filterable
|
||||||
clearable
|
clearable
|
||||||
@@ -674,7 +674,6 @@
|
|||||||
:loading="taskCarrierLoading"
|
:loading="taskCarrierLoading"
|
||||||
:disabled="dialogReadonly || (isWaybillDetailLayout && !form.projectId)"
|
:disabled="dialogReadonly || (isWaybillDetailLayout && !form.projectId)"
|
||||||
@visible-change="visible => visible && loadTaskCarrierOptions()"
|
@visible-change="visible => visible && loadTaskCarrierOptions()"
|
||||||
@change="handleTaskCarrierChange"
|
|
||||||
>
|
>
|
||||||
<el-option
|
<el-option
|
||||||
v-for="item in taskCarrierOptions"
|
v-for="item in taskCarrierOptions"
|
||||||
@@ -686,8 +685,8 @@
|
|||||||
item.name ||
|
item.name ||
|
||||||
item.customerCode
|
item.customerCode
|
||||||
"
|
"
|
||||||
:label="item.customerName || item.carrierName || item.fullName || item.name"
|
:label="item.label || item.customerName || item.carrierName || item.fullName || item.name"
|
||||||
:value="item.customerName || item.carrierName || item.fullName || item.name"
|
:value="item.value || item.customerName || item.carrierName || item.fullName || item.name"
|
||||||
/>
|
/>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
@@ -1206,7 +1205,7 @@
|
|||||||
<template v-if="hasTransportType">
|
<template v-if="hasTransportType">
|
||||||
<el-form-item v-if="taskCarrierRequired" :label="taskCarrierLabel" required>
|
<el-form-item v-if="taskCarrierRequired" :label="taskCarrierLabel" required>
|
||||||
<el-select
|
<el-select
|
||||||
v-model="form.carrierName"
|
v-model="taskCarrierValue"
|
||||||
:placeholder="`请选择${taskCarrierLabel}`"
|
:placeholder="`请选择${taskCarrierLabel}`"
|
||||||
filterable
|
filterable
|
||||||
clearable
|
clearable
|
||||||
@@ -1217,7 +1216,6 @@
|
|||||||
!taskCarrierRequired
|
!taskCarrierRequired
|
||||||
"
|
"
|
||||||
@visible-change="visible => visible && loadTaskCarrierOptions()"
|
@visible-change="visible => visible && loadTaskCarrierOptions()"
|
||||||
@change="handleTaskCarrierChange"
|
|
||||||
>
|
>
|
||||||
<el-option
|
<el-option
|
||||||
v-for="item in taskCarrierOptions"
|
v-for="item in taskCarrierOptions"
|
||||||
@@ -1229,8 +1227,8 @@
|
|||||||
item.name ||
|
item.name ||
|
||||||
item.customerCode
|
item.customerCode
|
||||||
"
|
"
|
||||||
:label="item.customerName || item.carrierName || item.fullName || item.name"
|
:label="item.label || item.customerName || item.carrierName || item.fullName || item.name"
|
||||||
:value="item.customerName || item.carrierName || item.fullName || item.name"
|
:value="item.value || item.customerName || item.carrierName || item.fullName || item.name"
|
||||||
/>
|
/>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
@@ -2145,6 +2143,9 @@
|
|||||||
<el-link type="primary" v-if="canComplete(row)" @click="handleAction('complete', row)">
|
<el-link type="primary" v-if="canComplete(row)" @click="handleAction('complete', row)">
|
||||||
{{ completeActionLabel }}
|
{{ completeActionLabel }}
|
||||||
</el-link>
|
</el-link>
|
||||||
|
<el-link type="primary" v-if="canMaintainMileage(row)" @click="openMileageDialog(row)">
|
||||||
|
维护里程
|
||||||
|
</el-link>
|
||||||
<el-link
|
<el-link
|
||||||
v-for="operation in customOperations(row)"
|
v-for="operation in customOperations(row)"
|
||||||
:key="operation.action"
|
:key="operation.action"
|
||||||
@@ -2158,6 +2159,59 @@
|
|||||||
</template>
|
</template>
|
||||||
</component>
|
</component>
|
||||||
|
|
||||||
|
<el-dialog
|
||||||
|
v-model="mileageDialog.visible"
|
||||||
|
width="520px"
|
||||||
|
append-to-body
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
class="business-crud-page__mileage-dialog"
|
||||||
|
@closed="resetMileageDialog"
|
||||||
|
>
|
||||||
|
<template #header>
|
||||||
|
<div class="dialog-section-title">维护里程</div>
|
||||||
|
</template>
|
||||||
|
<el-form
|
||||||
|
ref="mileageFormRef"
|
||||||
|
:model="mileageForm"
|
||||||
|
:rules="mileageRules"
|
||||||
|
label-position="right"
|
||||||
|
label-width="auto"
|
||||||
|
class="business-crud-page__mileage-form"
|
||||||
|
>
|
||||||
|
<el-form-item label="里程(公里)" prop="mileage">
|
||||||
|
<el-input
|
||||||
|
:model-value="mileageForm.mileage"
|
||||||
|
inputmode="numeric"
|
||||||
|
maxlength="10"
|
||||||
|
placeholder="请输入里程"
|
||||||
|
@input="handleMaintainMileageInput"
|
||||||
|
>
|
||||||
|
<template #append>公里</template>
|
||||||
|
</el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="备注" prop="mileageRemark">
|
||||||
|
<el-input
|
||||||
|
v-model="mileageForm.mileageRemark"
|
||||||
|
type="textarea"
|
||||||
|
:rows="3"
|
||||||
|
maxlength="200"
|
||||||
|
show-word-limit
|
||||||
|
placeholder="请输入里程维护备注"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<el-button @click="mileageDialog.visible = false">取消</el-button>
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
:loading="mileageDialog.submitting"
|
||||||
|
@click="submitMileageMaintenance"
|
||||||
|
>
|
||||||
|
提交
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
<empty-pagination
|
<empty-pagination
|
||||||
v-show="!isStandaloneFormPage"
|
v-show="!isStandaloneFormPage"
|
||||||
:page="page"
|
:page="page"
|
||||||
@@ -5509,6 +5563,26 @@ export default {
|
|||||||
waybillRouteChangeRecords: [],
|
waybillRouteChangeRecords: [],
|
||||||
waybillRouteChangeDragIndex: -1,
|
waybillRouteChangeDragIndex: -1,
|
||||||
waybillRouteChangeSaving: false,
|
waybillRouteChangeSaving: false,
|
||||||
|
mileageDialog: {
|
||||||
|
visible: false,
|
||||||
|
submitting: false,
|
||||||
|
row: null,
|
||||||
|
},
|
||||||
|
mileageForm: {
|
||||||
|
id: '',
|
||||||
|
mileage: '',
|
||||||
|
mileageRemark: '',
|
||||||
|
},
|
||||||
|
mileageRules: {
|
||||||
|
mileage: [
|
||||||
|
{ required: true, message: '请输入里程', trigger: 'blur' },
|
||||||
|
{
|
||||||
|
pattern: /^[1-9]\d{0,9}$/,
|
||||||
|
message: '里程必须为不超过10位的正整数',
|
||||||
|
trigger: 'blur',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
waybillCommonAddressBox: false,
|
waybillCommonAddressBox: false,
|
||||||
waybillCommonAddressLoading: false,
|
waybillCommonAddressLoading: false,
|
||||||
waybillCommonAddressQuery: {
|
waybillCommonAddressQuery: {
|
||||||
@@ -5992,6 +6066,17 @@ export default {
|
|||||||
taskCarrierLabel() {
|
taskCarrierLabel() {
|
||||||
return this.form.carrierType === '网货平台' ? '承运方' : '承运商';
|
return this.form.carrierType === '网货平台' ? '承运方' : '承运商';
|
||||||
},
|
},
|
||||||
|
taskCarrierValue: {
|
||||||
|
get() {
|
||||||
|
if (this.isWaybillDetailLayout && this.form.carrierType === '承运商') {
|
||||||
|
return this.form.carrierContractId || '';
|
||||||
|
}
|
||||||
|
return this.form.carrierName || '';
|
||||||
|
},
|
||||||
|
set(value) {
|
||||||
|
this.handleTaskCarrierChange(value);
|
||||||
|
},
|
||||||
|
},
|
||||||
showRoadEscortFields() {
|
showRoadEscortFields() {
|
||||||
return this.isRoadTransport && !this.isCarrierMode;
|
return this.isRoadTransport && !this.isCarrierMode;
|
||||||
},
|
},
|
||||||
@@ -6708,6 +6793,15 @@ export default {
|
|||||||
? status === 'dispatching'
|
? status === 'dispatching'
|
||||||
: status === 'processing';
|
: status === 'processing';
|
||||||
},
|
},
|
||||||
|
canMaintainMileage(row) {
|
||||||
|
return (
|
||||||
|
this.config.permission === 'waybill_manage' &&
|
||||||
|
this.hasPermission(`${this.config.permission}_mileage`) &&
|
||||||
|
typeof this.api.maintainMileage === 'function' &&
|
||||||
|
this.statusValue(row) === 'completed' &&
|
||||||
|
row.mileageMaintainable === true
|
||||||
|
);
|
||||||
|
},
|
||||||
canReassign(row) {
|
canReassign(row) {
|
||||||
return (
|
return (
|
||||||
this.hasAction('reassign') &&
|
this.hasAction('reassign') &&
|
||||||
@@ -8179,6 +8273,7 @@ export default {
|
|||||||
this.taskCarrierLoading = false;
|
this.taskCarrierLoading = false;
|
||||||
this.form.carrierName = '';
|
this.form.carrierName = '';
|
||||||
this.form.carrierId = '';
|
this.form.carrierId = '';
|
||||||
|
this.form.carrierContractId = '';
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -8189,6 +8284,7 @@ export default {
|
|||||||
if (this.isWaybillDetailLayout) {
|
if (this.isWaybillDetailLayout) {
|
||||||
this.form.carrierName = '';
|
this.form.carrierName = '';
|
||||||
this.form.carrierId = '';
|
this.form.carrierId = '';
|
||||||
|
this.form.carrierContractId = '';
|
||||||
this.loadTaskCarrierOptions(project);
|
this.loadTaskCarrierOptions(project);
|
||||||
}
|
}
|
||||||
if (this.contractSelectEnabled || this.config.enableTransportPlanForm) {
|
if (this.contractSelectEnabled || this.config.enableTransportPlanForm) {
|
||||||
@@ -8414,6 +8510,11 @@ export default {
|
|||||||
carrierInfo.customerName ||
|
carrierInfo.customerName ||
|
||||||
carrierInfo.name ||
|
carrierInfo.name ||
|
||||||
'';
|
'';
|
||||||
|
this.form.carrierContractId =
|
||||||
|
this.form.carrierContractId ||
|
||||||
|
taskInfo.carrierContractId ||
|
||||||
|
carrierInfo.carrierContractId ||
|
||||||
|
'';
|
||||||
this.form.driverName =
|
this.form.driverName =
|
||||||
this.form.driverName || taskInfo.driverName || carrierInfo.driverName || '';
|
this.form.driverName || taskInfo.driverName || carrierInfo.driverName || '';
|
||||||
this.form.driverPhone =
|
this.form.driverPhone =
|
||||||
@@ -8560,7 +8661,14 @@ export default {
|
|||||||
this.form.carrierName = '';
|
this.form.carrierName = '';
|
||||||
this.form.carrierId = '';
|
this.form.carrierId = '';
|
||||||
}
|
}
|
||||||
|
if (nextValue !== '承运商') {
|
||||||
|
this.form.carrierContractId = '';
|
||||||
|
}
|
||||||
this.form.carrierType = nextValue;
|
this.form.carrierType = nextValue;
|
||||||
|
if (this.isWaybillDetailLayout) {
|
||||||
|
this.taskCarrierRequestId += 1;
|
||||||
|
this.taskCarrierOptions = [];
|
||||||
|
}
|
||||||
},
|
},
|
||||||
isTaskCarrierRequired(carrierType) {
|
isTaskCarrierRequired(carrierType) {
|
||||||
return ['承运商', '网货平台'].includes(String(carrierType || '').trim());
|
return ['承运商', '网货平台'].includes(String(carrierType || '').trim());
|
||||||
@@ -8570,6 +8678,27 @@ export default {
|
|||||||
if (this.isWaybillDetailLayout) {
|
if (this.isWaybillDetailLayout) {
|
||||||
const projectId = project.id || project.projectId || this.form.projectId;
|
const projectId = project.id || project.projectId || this.form.projectId;
|
||||||
const requestId = ++this.taskCarrierRequestId;
|
const requestId = ++this.taskCarrierRequestId;
|
||||||
|
if (this.form.carrierType === '承运商') {
|
||||||
|
if (!projectId) {
|
||||||
|
this.taskCarrierOptions = [];
|
||||||
|
return Promise.resolve([]);
|
||||||
|
}
|
||||||
|
this.taskCarrierLoading = true;
|
||||||
|
return getContractList(1, 9999, {
|
||||||
|
projectId,
|
||||||
|
projectName: project.projectName || this.form.projectName,
|
||||||
|
contractCategory: '承运商合同',
|
||||||
|
})
|
||||||
|
.then(res => {
|
||||||
|
if (requestId !== this.taskCarrierRequestId) return this.taskCarrierOptions;
|
||||||
|
this.taskCarrierOptions = this.getWaybillCarrierContractOptions(extractRecords(res));
|
||||||
|
return this.taskCarrierOptions;
|
||||||
|
})
|
||||||
|
.catch(() => this.taskCarrierOptions)
|
||||||
|
.finally(() => {
|
||||||
|
if (requestId === this.taskCarrierRequestId) this.taskCarrierLoading = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
const currentProject = this.projectOptions.find(
|
const currentProject = this.projectOptions.find(
|
||||||
item => String(item.id) === String(projectId)
|
item => String(item.id) === String(projectId)
|
||||||
);
|
);
|
||||||
@@ -8607,10 +8736,16 @@ export default {
|
|||||||
},
|
},
|
||||||
handleTaskCarrierChange(value) {
|
handleTaskCarrierChange(value) {
|
||||||
const carrier = this.taskCarrierOptions.find(item =>
|
const carrier = this.taskCarrierOptions.find(item =>
|
||||||
[item.customerName, item.carrierName, item.fullName, item.name].some(
|
[item.value, item.customerName, item.carrierName, item.fullName, item.name].some(name =>
|
||||||
name => String(name || '') === value
|
String(name || '') === String(value || '')
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
if (this.isWaybillDetailLayout && this.form.carrierType === '承运商') {
|
||||||
|
this.form.carrierContractId = carrier?.carrierContractId || '';
|
||||||
|
this.form.carrierId = carrier?.carrierId || '';
|
||||||
|
this.form.carrierName = carrier?.carrierName || value || '';
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.form.carrierId = carrier?.id || '';
|
this.form.carrierId = carrier?.id || '';
|
||||||
this.form.carrierName = value || '';
|
this.form.carrierName = value || '';
|
||||||
},
|
},
|
||||||
@@ -8959,6 +9094,9 @@ export default {
|
|||||||
if (this.isTaskCarrierRequired(row.carrierType)) {
|
if (this.isTaskCarrierRequired(row.carrierType)) {
|
||||||
requiredFields.push(['carrierName', row.carrierType === '网货平台' ? '承运方' : '承运商']);
|
requiredFields.push(['carrierName', row.carrierType === '网货平台' ? '承运方' : '承运商']);
|
||||||
}
|
}
|
||||||
|
if (this.isWaybillDetailLayout && row.carrierType === '承运商') {
|
||||||
|
requiredFields.push(['carrierContractId', '承运商合同']);
|
||||||
|
}
|
||||||
if (this.isRoadTransport && row.carrierType === '自运') {
|
if (this.isRoadTransport && row.carrierType === '自运') {
|
||||||
requiredFields.push(['driverName', '司机']);
|
requiredFields.push(['driverName', '司机']);
|
||||||
requiredFields.push(['driverPhone', '手机号']);
|
requiredFields.push(['driverPhone', '手机号']);
|
||||||
@@ -9035,6 +9173,7 @@ export default {
|
|||||||
carrierType: row.carrierType,
|
carrierType: row.carrierType,
|
||||||
carrierId: row.carrierId,
|
carrierId: row.carrierId,
|
||||||
carrierName: row.carrierName,
|
carrierName: row.carrierName,
|
||||||
|
carrierContractId: row.carrierContractId,
|
||||||
driverId: row.driverId,
|
driverId: row.driverId,
|
||||||
driverName: row.driverName,
|
driverName: row.driverName,
|
||||||
driverPhone: row.driverPhone,
|
driverPhone: row.driverPhone,
|
||||||
@@ -9062,6 +9201,7 @@ export default {
|
|||||||
carrierType: row.carrierType,
|
carrierType: row.carrierType,
|
||||||
carrierId: row.carrierId,
|
carrierId: row.carrierId,
|
||||||
carrierName: row.carrierName,
|
carrierName: row.carrierName,
|
||||||
|
carrierContractId: row.carrierContractId,
|
||||||
driverId: row.driverId,
|
driverId: row.driverId,
|
||||||
driverName: row.driverName,
|
driverName: row.driverName,
|
||||||
driverPhone: row.driverPhone,
|
driverPhone: row.driverPhone,
|
||||||
@@ -11517,6 +11657,46 @@ export default {
|
|||||||
this.onLoad(this.page, this.query);
|
this.onLoad(this.page, this.query);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
openMileageDialog(row) {
|
||||||
|
this.mileageDialog = {
|
||||||
|
visible: true,
|
||||||
|
submitting: false,
|
||||||
|
row,
|
||||||
|
};
|
||||||
|
this.mileageForm = {
|
||||||
|
id: row.id,
|
||||||
|
mileage: row.mileage === null || row.mileage === undefined ? '' : String(row.mileage),
|
||||||
|
mileageRemark: row.mileageRemark || '',
|
||||||
|
};
|
||||||
|
this.$nextTick(() => this.$refs.mileageFormRef?.clearValidate());
|
||||||
|
},
|
||||||
|
handleMaintainMileageInput(value) {
|
||||||
|
this.mileageForm.mileage = String(value || '')
|
||||||
|
.replace(/\D/g, '')
|
||||||
|
.slice(0, 10);
|
||||||
|
},
|
||||||
|
async submitMileageMaintenance() {
|
||||||
|
const valid = await this.$refs.mileageFormRef?.validate().catch(() => false);
|
||||||
|
if (!valid || this.mileageDialog.submitting) return;
|
||||||
|
this.mileageDialog.submitting = true;
|
||||||
|
try {
|
||||||
|
await this.api.maintainMileage({
|
||||||
|
id: this.mileageForm.id,
|
||||||
|
mileage: Number(this.mileageForm.mileage),
|
||||||
|
mileageRemark: String(this.mileageForm.mileageRemark || '').trim(),
|
||||||
|
});
|
||||||
|
this.$message.success('里程维护成功');
|
||||||
|
this.mileageDialog.visible = false;
|
||||||
|
this.onLoad(this.page, this.query);
|
||||||
|
} finally {
|
||||||
|
this.mileageDialog.submitting = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
resetMileageDialog() {
|
||||||
|
this.mileageDialog.row = null;
|
||||||
|
this.mileageForm = { id: '', mileage: '', mileageRemark: '' };
|
||||||
|
this.$refs.mileageFormRef?.clearValidate();
|
||||||
|
},
|
||||||
handleAction(action, row) {
|
handleAction(action, row) {
|
||||||
const actionName = {
|
const actionName = {
|
||||||
enable: '启用',
|
enable: '启用',
|
||||||
@@ -12298,6 +12478,31 @@ export default {
|
|||||||
names.forEach(append);
|
names.forEach(append);
|
||||||
return options;
|
return options;
|
||||||
},
|
},
|
||||||
|
getWaybillCarrierContractOptions(contracts = []) {
|
||||||
|
const carrierNameCounts = contracts.reduce((counts, contract) => {
|
||||||
|
const carrierName = String(contract.partyB || '').trim();
|
||||||
|
if (carrierName) counts[carrierName] = (counts[carrierName] || 0) + 1;
|
||||||
|
return counts;
|
||||||
|
}, {});
|
||||||
|
return contracts
|
||||||
|
.map(contract => {
|
||||||
|
const carrierName = String(contract.partyB || '').trim();
|
||||||
|
if (!carrierName || !contract.id) return null;
|
||||||
|
const contractIdentifier = contract.contractName || contract.contractNo || contract.id;
|
||||||
|
return {
|
||||||
|
id: contract.id,
|
||||||
|
value: contract.id,
|
||||||
|
label:
|
||||||
|
carrierNameCounts[carrierName] > 1
|
||||||
|
? `${carrierName}(${contractIdentifier})`
|
||||||
|
: carrierName,
|
||||||
|
carrierName,
|
||||||
|
fullName: carrierName,
|
||||||
|
carrierContractId: contract.id,
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.filter(Boolean);
|
||||||
|
},
|
||||||
loadDispatchCarrierOptions(plan = this.dispatchRow) {
|
loadDispatchCarrierOptions(plan = this.dispatchRow) {
|
||||||
if (this.dispatchCarrierLoading) return Promise.resolve(this.dispatchCarrierOptions);
|
if (this.dispatchCarrierLoading) return Promise.resolve(this.dispatchCarrierOptions);
|
||||||
const projectId = plan?.projectId;
|
const projectId = plan?.projectId;
|
||||||
@@ -15512,6 +15717,18 @@ export default {
|
|||||||
top: 92px !important;
|
top: 92px !important;
|
||||||
left: 0 !important;
|
left: 0 !important;
|
||||||
}
|
}
|
||||||
|
:global(.business-crud-page__mileage-dialog .business-crud-page__mileage-form) {
|
||||||
|
padding: 4px 20px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.business-crud-page__mileage-dialog .business-crud-page__mileage-form .el-form-item) {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.business-crud-page__mileage-dialog .business-crud-page__mileage-form .el-input),
|
||||||
|
:global(.business-crud-page__mileage-dialog .business-crud-page__mileage-form .el-textarea) {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
.el-input__icon {
|
.el-input__icon {
|
||||||
color: #1e90ff !important;
|
color: #1e90ff !important;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -143,7 +143,7 @@
|
|||||||
<el-form :model="route" label-position="right" label-width="auto" class="dispatch-form carrier-form">
|
<el-form :model="route" label-position="right" label-width="auto" class="dispatch-form carrier-form">
|
||||||
<el-row :gutter="16">
|
<el-row :gutter="16">
|
||||||
<template v-if="isRoad(route)">
|
<template v-if="isRoad(route)">
|
||||||
<el-col v-if="route.carrierType !== '自运'" :span="6"><el-form-item label="承运商" required><el-select v-model="route.carrierContractId" filterable clearable placeholder="请选择" :loading="carrierLoading" @change="value => handleCarrierContractChange(route, value)"><el-option v-for="item in carrierOptions" :key="item.contractId" :label="item.carrierName" :value="item.contractId" /></el-select></el-form-item></el-col>
|
<el-col v-if="route.carrierType === '承运商'" :span="6"><el-form-item label="承运商" required><el-select v-model="route.carrierContractId" filterable clearable placeholder="请选择" :loading="carrierLoading" @change="value => handleCarrierContractChange(route, value)"><el-option v-for="item in carrierOptions" :key="item.contractId" :label="carrierOptionLabel(item)" :value="item.contractId" /></el-select></el-form-item></el-col>
|
||||||
<el-col :span="6"><el-form-item label="司机" :required="route.carrierType !== '承运商'"><el-autocomplete :ref="element => setDriverInput(route.segmentNo, element)" v-model="route.driverName" :debounce="300" :fetch-suggestions="fetchDriverSuggestions" clearable placeholder="请输入司机" :loading="driverLoading" @select="item => handleDriverSuggestionSelect(route, item)" /></el-form-item></el-col>
|
<el-col :span="6"><el-form-item label="司机" :required="route.carrierType !== '承运商'"><el-autocomplete :ref="element => setDriverInput(route.segmentNo, element)" v-model="route.driverName" :debounce="300" :fetch-suggestions="fetchDriverSuggestions" clearable placeholder="请输入司机" :loading="driverLoading" @select="item => handleDriverSuggestionSelect(route, item)" /></el-form-item></el-col>
|
||||||
<el-col :span="6"><el-form-item :label="route.carrierType === '承运商' ? '手机号' : '司机手机号'" :required="route.carrierType !== '承运商'"><el-input v-model="route.driverPhone" placeholder="请输入" /></el-form-item></el-col>
|
<el-col :span="6"><el-form-item :label="route.carrierType === '承运商' ? '手机号' : '司机手机号'" :required="route.carrierType !== '承运商'"><el-input v-model="route.driverPhone" placeholder="请输入" /></el-form-item></el-col>
|
||||||
<el-col :span="6"><el-form-item label="车牌号" required><el-input v-model="route.vehicleNo" placeholder="请输入" /></el-form-item></el-col>
|
<el-col :span="6"><el-form-item label="车牌号" required><el-input v-model="route.vehicleNo" placeholder="请输入" /></el-form-item></el-col>
|
||||||
@@ -152,7 +152,7 @@
|
|||||||
<el-col v-if="route.carrierType !== '承运商'" :span="6"><el-form-item label="押运人手机号" required><el-input v-model="route.escortPhone" placeholder="请输入" /></el-form-item></el-col>
|
<el-col v-if="route.carrierType !== '承运商'" :span="6"><el-form-item label="押运人手机号" required><el-input v-model="route.escortPhone" placeholder="请输入" /></el-form-item></el-col>
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<el-col v-if="route.carrierType !== '自运'" :span="6"><el-form-item label="承运商" required><el-select v-model="route.carrierContractId" filterable clearable placeholder="请选择" :loading="carrierLoading" @change="value => handleCarrierContractChange(route, value)"><el-option v-for="item in carrierOptions" :key="item.contractId" :label="item.carrierName" :value="item.contractId" /></el-select></el-form-item></el-col>
|
<el-col v-if="route.carrierType === '承运商'" :span="6"><el-form-item label="承运商" required><el-select v-model="route.carrierContractId" filterable clearable placeholder="请选择" :loading="carrierLoading" @change="value => handleCarrierContractChange(route, value)"><el-option v-for="item in carrierOptions" :key="item.contractId" :label="carrierOptionLabel(item)" :value="item.contractId" /></el-select></el-form-item></el-col>
|
||||||
<el-col :span="6"><el-form-item label="船/航/班列号" required><el-input v-model="route.vehicleNo" placeholder="请输入" /></el-form-item></el-col>
|
<el-col :span="6"><el-form-item label="船/航/班列号" required><el-input v-model="route.vehicleNo" placeholder="请输入" /></el-form-item></el-col>
|
||||||
<el-col :span="6"><el-form-item label="船长" required><el-input v-model="route.captainName" placeholder="请输入" /></el-form-item></el-col>
|
<el-col :span="6"><el-form-item label="船长" required><el-input v-model="route.captainName" placeholder="请输入" /></el-form-item></el-col>
|
||||||
<el-col :span="6"><el-form-item label="联系电话" required><el-input v-model="route.driverPhone" placeholder="请输入" /></el-form-item></el-col>
|
<el-col :span="6"><el-form-item label="联系电话" required><el-input v-model="route.driverPhone" placeholder="请输入" /></el-form-item></el-col>
|
||||||
@@ -527,7 +527,7 @@ export default {
|
|||||||
route.escortName = '';
|
route.escortName = '';
|
||||||
route.escortPhone = '';
|
route.escortPhone = '';
|
||||||
}
|
}
|
||||||
if (value === '自运') {
|
if (value !== '承运商') {
|
||||||
route.carrierContractId = '';
|
route.carrierContractId = '';
|
||||||
route.carrierName = '';
|
route.carrierName = '';
|
||||||
}
|
}
|
||||||
@@ -539,6 +539,12 @@ export default {
|
|||||||
route.carrierContractId = contract?.contractId || '';
|
route.carrierContractId = contract?.contractId || '';
|
||||||
route.carrierName = contract?.carrierName || '';
|
route.carrierName = contract?.carrierName || '';
|
||||||
},
|
},
|
||||||
|
carrierOptionLabel(item) {
|
||||||
|
const duplicateCount = this.carrierOptions.filter(option => option.carrierName === item.carrierName).length;
|
||||||
|
return duplicateCount > 1
|
||||||
|
? `${item.carrierName}(${item.contractName || item.contractId})`
|
||||||
|
: item.carrierName;
|
||||||
|
},
|
||||||
driverOptionLabel(item) { return item.driverName || item.name || ''; },
|
driverOptionLabel(item) { return item.driverName || item.name || ''; },
|
||||||
driverOptionKey(item) { return item.id || item.driverId || this.driverOptionLabel(item) || item.mobile; },
|
driverOptionKey(item) { return item.id || item.driverId || this.driverOptionLabel(item) || item.mobile; },
|
||||||
handleDriverChange(route, value) {
|
handleDriverChange(route, value) {
|
||||||
@@ -611,9 +617,9 @@ export default {
|
|||||||
if (!this.validateRoutePhones(route)) return;
|
if (!this.validateRoutePhones(route)) return;
|
||||||
if (route.documentType === '运单') {
|
if (route.documentType === '运单') {
|
||||||
if (this.isRoad(route)) {
|
if (this.isRoad(route)) {
|
||||||
if (route.carrierType !== '自运' && (!route.carrierContractId || !route.carrierName || !route.vehicleNo)) return this.$message.warning('请选择承运商并填写车牌号');
|
if (route.carrierType === '承运商' && (!route.carrierContractId || !route.carrierName || !route.vehicleNo)) return this.$message.warning('请选择承运商并填写车牌号');
|
||||||
if (route.carrierType !== '承运商' && (!route.driverName || !route.driverPhone || !route.vehicleNo || !route.trailerVehicleNo || !route.escortName || !route.escortPhone)) return this.$message.warning('请补全自运或网货平台的车辆与人员信息');
|
if (route.carrierType !== '承运商' && (!route.driverName || !route.driverPhone || !route.vehicleNo || !route.trailerVehicleNo || !route.escortName || !route.escortPhone)) return this.$message.warning('请补全自运或网货平台的车辆与人员信息');
|
||||||
} else if (!route.vehicleNo || !route.captainName || !route.driverPhone || !route.containerNo || !route.cabinNo || (route.carrierType !== '自运' && (!route.carrierContractId || !route.carrierName))) {
|
} else if (!route.vehicleNo || !route.captainName || !route.driverPhone || !route.containerNo || !route.cabinNo || (route.carrierType === '承运商' && (!route.carrierContractId || !route.carrierName))) {
|
||||||
return this.$message.warning('请补全非公路运输的承运信息');
|
return this.$message.warning('请补全非公路运输的承运信息');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -823,7 +823,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="loading-manage-dialog__grid loading-manage-dialog__task-grid">
|
<div class="loading-manage-dialog__grid loading-manage-dialog__task-grid">
|
||||||
<el-form-item
|
<el-form-item
|
||||||
v-if="dialogForm.carrierType !== '自运'"
|
v-if="dialogForm.carrierType === '承运商'"
|
||||||
label="承运商"
|
label="承运商"
|
||||||
required
|
required
|
||||||
>
|
>
|
||||||
@@ -1606,7 +1606,7 @@ export default {
|
|||||||
this.dialogForm.waybillIdsJson,
|
this.dialogForm.waybillIdsJson,
|
||||||
this.dialogForm.loadingSubNos
|
this.dialogForm.loadingSubNos
|
||||||
);
|
);
|
||||||
if (this.dialogForm.carrierType === '自运') {
|
if (this.dialogForm.carrierType !== '承运商') {
|
||||||
this.dialogForm.carrierContractId = '';
|
this.dialogForm.carrierContractId = '';
|
||||||
this.dialogForm.carrierName = '';
|
this.dialogForm.carrierName = '';
|
||||||
}
|
}
|
||||||
@@ -2131,7 +2131,7 @@ export default {
|
|||||||
normalizePayload() {
|
normalizePayload() {
|
||||||
this.syncRouteAddressFields();
|
this.syncRouteAddressFields();
|
||||||
this.rebuildSummaryFromWaybills(false);
|
this.rebuildSummaryFromWaybills(false);
|
||||||
if (this.dialogForm.carrierType === '自运') {
|
if (this.dialogForm.carrierType !== '承运商') {
|
||||||
this.dialogForm.carrierContractId = '';
|
this.dialogForm.carrierContractId = '';
|
||||||
this.dialogForm.carrierName = '';
|
this.dialogForm.carrierName = '';
|
||||||
}
|
}
|
||||||
@@ -2142,6 +2142,7 @@ export default {
|
|||||||
taskInfoJson: JSON.stringify({
|
taskInfoJson: JSON.stringify({
|
||||||
carrierType: this.dialogForm.carrierType,
|
carrierType: this.dialogForm.carrierType,
|
||||||
carrierName: this.dialogForm.carrierName,
|
carrierName: this.dialogForm.carrierName,
|
||||||
|
carrierContractId: this.dialogForm.carrierContractId,
|
||||||
driverName: this.dialogForm.driverName,
|
driverName: this.dialogForm.driverName,
|
||||||
driverPhone: this.dialogForm.driverPhone,
|
driverPhone: this.dialogForm.driverPhone,
|
||||||
vehicleNo: this.dialogForm.vehicleNo,
|
vehicleNo: this.dialogForm.vehicleNo,
|
||||||
@@ -2194,7 +2195,7 @@ export default {
|
|||||||
ElMessage.warning('请输入车牌号');
|
ElMessage.warning('请输入车牌号');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (this.dialogForm.carrierType !== '自运' && !this.dialogForm.carrierContractId) {
|
if (this.dialogForm.carrierType === '承运商' && !this.dialogForm.carrierContractId) {
|
||||||
ElMessage.warning('请选择承运商合同');
|
ElMessage.warning('请选择承运商合同');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -2316,10 +2317,13 @@ export default {
|
|||||||
this.dialogForm.escortName = '';
|
this.dialogForm.escortName = '';
|
||||||
this.dialogForm.escortPhone = '';
|
this.dialogForm.escortPhone = '';
|
||||||
}
|
}
|
||||||
if (this.dialogForm.carrierType === '自运') {
|
if (this.dialogForm.carrierType !== '承运商') {
|
||||||
this.dialogForm.carrierContractId = '';
|
this.dialogForm.carrierContractId = '';
|
||||||
this.dialogForm.carrierName = '';
|
this.dialogForm.carrierName = '';
|
||||||
|
this.taskCarrierOptions = [];
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
this.loadTaskCarrierOptions();
|
||||||
},
|
},
|
||||||
handleTaskCarrierChange(contractId) {
|
handleTaskCarrierChange(contractId) {
|
||||||
const contract = this.taskCarrierOptions.find(item => String(item.id) === String(contractId));
|
const contract = this.taskCarrierOptions.find(item => String(item.id) === String(contractId));
|
||||||
@@ -2427,7 +2431,7 @@ export default {
|
|||||||
},
|
},
|
||||||
async loadTaskCarrierOptions() {
|
async loadTaskCarrierOptions() {
|
||||||
const requestId = ++this.taskCarrierRequestId;
|
const requestId = ++this.taskCarrierRequestId;
|
||||||
if (!this.selectedWaybillRows.length) {
|
if (this.dialogForm.carrierType !== '承运商' || !this.selectedWaybillRows.length) {
|
||||||
this.taskCarrierOptions = [];
|
this.taskCarrierOptions = [];
|
||||||
this.taskCarrierLoading = false;
|
this.taskCarrierLoading = false;
|
||||||
this.dialogForm.carrierContractId = '';
|
this.dialogForm.carrierContractId = '';
|
||||||
@@ -2436,7 +2440,10 @@ export default {
|
|||||||
}
|
}
|
||||||
this.taskCarrierLoading = true;
|
this.taskCarrierLoading = true;
|
||||||
try {
|
try {
|
||||||
const response = await loadingApi.getCarrierContracts();
|
const projectIds = [
|
||||||
|
...new Set(this.selectedWaybillRows.map(row => row.projectId).filter(Boolean)),
|
||||||
|
];
|
||||||
|
const response = await loadingApi.getCarrierContracts(projectIds);
|
||||||
if (requestId !== this.taskCarrierRequestId) return [];
|
if (requestId !== this.taskCarrierRequestId) return [];
|
||||||
const contracts = response.data?.data || response.data || [];
|
const contracts = response.data?.data || response.data || [];
|
||||||
this.taskCarrierOptions = contracts;
|
this.taskCarrierOptions = contracts;
|
||||||
|
|||||||
@@ -75,7 +75,7 @@
|
|||||||
</section-card>
|
</section-card>
|
||||||
|
|
||||||
<section-card title="附件信息">
|
<section-card title="附件信息">
|
||||||
<vehicle-attachment-table v-model="form.attachments" :readonly="readonly" />
|
<vehicle-attachment-table v-model="form.attachments" :readonly="readonly" :max-size="10" />
|
||||||
</section-card>
|
</section-card>
|
||||||
|
|
||||||
<div class="bill-payment-form-page__actions">
|
<div class="bill-payment-form-page__actions">
|
||||||
|
|||||||
@@ -15,6 +15,8 @@
|
|||||||
@edit="openEdit"
|
@edit="openEdit"
|
||||||
@delete="handleDelete"
|
@delete="handleDelete"
|
||||||
@flow="openFlow"
|
@flow="openFlow"
|
||||||
|
@approve="handleApprove"
|
||||||
|
@return="handleReturn"
|
||||||
@void="handleVoid"
|
@void="handleVoid"
|
||||||
@page-change="handlePageChange"
|
@page-change="handlePageChange"
|
||||||
@size-change="handleSizeChange"
|
@size-change="handleSizeChange"
|
||||||
@@ -127,6 +129,17 @@ export default {
|
|||||||
this.flowDialog.row = row;
|
this.flowDialog.row = row;
|
||||||
this.flowDialog.visible = true;
|
this.flowDialog.visible = true;
|
||||||
},
|
},
|
||||||
|
async handleApprove(row) {
|
||||||
|
await api.approve({ id: row.id });
|
||||||
|
this.$message.success('审批通过');
|
||||||
|
this.loadTable();
|
||||||
|
},
|
||||||
|
async handleReturn(row) {
|
||||||
|
const { value } = await this.$prompt('请输入驳回原因', '审批驳回');
|
||||||
|
await api.returnBill({ id: row.id, reason: value });
|
||||||
|
this.$message.success('已驳回');
|
||||||
|
this.loadTable();
|
||||||
|
},
|
||||||
async handleVoid(row) {
|
async handleVoid(row) {
|
||||||
const { value } = await this.$prompt('请输入作废原因', '作废汇票付款');
|
const { value } = await this.$prompt('请输入作废原因', '作废汇票付款');
|
||||||
await api.voidBill({ id: row.id, reason: value });
|
await api.voidBill({ id: row.id, reason: value });
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
<span v-else>{{ displayValue(row[column.prop]) }}</span>
|
<span v-else>{{ displayValue(row[column.prop]) }}</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="操作" width="220" fixed="right" align="center">
|
<el-table-column label="操作" width="320" fixed="right" align="center">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<div class="bill-payment-table__links">
|
<div class="bill-payment-table__links">
|
||||||
<el-link
|
<el-link
|
||||||
@@ -55,6 +55,18 @@
|
|||||||
@click="$emit('flow', row)"
|
@click="$emit('flow', row)"
|
||||||
>流程</el-link
|
>流程</el-link
|
||||||
>
|
>
|
||||||
|
<el-link
|
||||||
|
v-if="row.approvalStatus === 'reviewing' && hasPermission('bill_payment_approve')"
|
||||||
|
type="primary"
|
||||||
|
@click="$emit('approve', row)"
|
||||||
|
>通过</el-link
|
||||||
|
>
|
||||||
|
<el-link
|
||||||
|
v-if="row.approvalStatus === 'reviewing' && hasPermission('bill_payment_approve')"
|
||||||
|
type="danger"
|
||||||
|
@click="$emit('return', row)"
|
||||||
|
>驳回</el-link
|
||||||
|
>
|
||||||
<el-link
|
<el-link
|
||||||
v-if="row.approvalStatus === 'approved' && hasPermission('bill_payment_void')"
|
v-if="row.approvalStatus === 'approved' && hasPermission('bill_payment_void')"
|
||||||
type="danger"
|
type="danger"
|
||||||
@@ -90,7 +102,18 @@ export default {
|
|||||||
loading: { type: Boolean, default: false },
|
loading: { type: Boolean, default: false },
|
||||||
page: { type: Object, required: true },
|
page: { type: Object, required: true },
|
||||||
},
|
},
|
||||||
emits: ['add', 'view', 'edit', 'delete', 'flow', 'void', 'page-change', 'size-change'],
|
emits: [
|
||||||
|
'add',
|
||||||
|
'view',
|
||||||
|
'edit',
|
||||||
|
'delete',
|
||||||
|
'flow',
|
||||||
|
'approve',
|
||||||
|
'return',
|
||||||
|
'void',
|
||||||
|
'page-change',
|
||||||
|
'size-change',
|
||||||
|
],
|
||||||
data() {
|
data() {
|
||||||
return { columns: billPaymentTableColumns };
|
return { columns: billPaymentTableColumns };
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
<el-row :gutter="24">
|
<el-row :gutter="24">
|
||||||
<el-col :span="6">
|
<el-col :span="6">
|
||||||
<el-form-item label="结算单" prop="settlementIds">
|
<el-form-item label="结算单" prop="settlementIds">
|
||||||
<el-input :model-value="settlementLabel" readonly placeholder="请选择应付正式结算单">
|
<el-input :model-value="settlementLabel" readonly placeholder="请选择应收正式结算单">
|
||||||
<template v-if="!readonly" #append>
|
<template v-if="!readonly" #append>
|
||||||
<el-button @click="openSettlementDialog">选择</el-button>
|
<el-button @click="openSettlementDialog">选择</el-button>
|
||||||
</template>
|
</template>
|
||||||
@@ -188,7 +188,10 @@
|
|||||||
</el-row>
|
</el-row>
|
||||||
</section-card>
|
</section-card>
|
||||||
|
|
||||||
<section-card title="开票内容"><template #extra><el-button v-if="!readonly" type="primary" @click="addSheet">添加</el-button></template>
|
<section-card title="开票内容"
|
||||||
|
><template #extra
|
||||||
|
><el-button v-if="!readonly" type="primary" @click="addSheet">添加</el-button></template
|
||||||
|
>
|
||||||
<div v-for="(sheet, sheetIndex) in form.sheets" :key="sheet.localId" class="invoice-sheet">
|
<div v-for="(sheet, sheetIndex) in form.sheets" :key="sheet.localId" class="invoice-sheet">
|
||||||
<div class="invoice-sheet__header">
|
<div class="invoice-sheet__header">
|
||||||
<span>第{{ sheetIndex + 1 }}张</span>
|
<span>第{{ sheetIndex + 1 }}张</span>
|
||||||
@@ -247,17 +250,6 @@
|
|||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="不含税单价" min-width="130">
|
|
||||||
<template #default="{ row }">
|
|
||||||
<el-input-number
|
|
||||||
v-model="row.unitPriceNoTax"
|
|
||||||
:disabled="readonly"
|
|
||||||
:min="0"
|
|
||||||
:precision="2"
|
|
||||||
:controls="false"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="含税金额" min-width="130">
|
<el-table-column label="含税金额" min-width="130">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<el-input-number
|
<el-input-number
|
||||||
@@ -362,7 +354,11 @@
|
|||||||
show-overflow-tooltip
|
show-overflow-tooltip
|
||||||
>
|
>
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<el-link v-if="column.link && row[column.prop]" type="primary">
|
<el-link
|
||||||
|
v-if="column.link && row[column.prop]"
|
||||||
|
type="primary"
|
||||||
|
@click.stop="handleDetailLink(row, column)"
|
||||||
|
>
|
||||||
{{ row[column.prop] }}
|
{{ row[column.prop] }}
|
||||||
</el-link>
|
</el-link>
|
||||||
<span
|
<span
|
||||||
@@ -371,9 +367,13 @@
|
|||||||
Number(row[column.prop]) === -1
|
Number(row[column.prop]) === -1
|
||||||
"
|
"
|
||||||
></span>
|
></span>
|
||||||
<span v-else-if="column.feeItemKeys">{{ formatMoney(getFeeItemValue(row, column.feeItemKeys)) }}</span>
|
<span v-else-if="column.feeItemKeys">{{
|
||||||
|
formatMoney(getFeeItemValue(row, column.feeItemKeys))
|
||||||
|
}}</span>
|
||||||
<span v-else-if="column.money">{{ formatMoney(row[column.prop]) }}</span>
|
<span v-else-if="column.money">{{ formatMoney(row[column.prop]) }}</span>
|
||||||
<span v-else-if="column.prop === 'transportType'">{{ transportTypeLabel(row.transportType) }}</span>
|
<span v-else-if="column.prop === 'transportType'">{{
|
||||||
|
transportTypeLabel(row.transportType)
|
||||||
|
}}</span>
|
||||||
<span v-else>{{ displayValue(row[column.prop]) }}</span>
|
<span v-else>{{ displayValue(row[column.prop]) }}</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
@@ -400,23 +400,21 @@
|
|||||||
<div class="invoice-form-page__actions">
|
<div class="invoice-form-page__actions">
|
||||||
<el-button @click="goBack">取消</el-button>
|
<el-button @click="goBack">取消</el-button>
|
||||||
<el-button v-if="!readonly" type="primary" plain @click="syncReferenceData">同步</el-button>
|
<el-button v-if="!readonly" type="primary" plain @click="syncReferenceData">同步</el-button>
|
||||||
<el-button
|
<el-button v-if="!readonly && canSave" type="primary" plain @click="saveDraft"
|
||||||
v-if="!readonly && canSave"
|
>保存</el-button
|
||||||
type="primary"
|
>
|
||||||
plain
|
|
||||||
@click="saveDraft"
|
|
||||||
>保存</el-button>
|
|
||||||
<el-button
|
<el-button
|
||||||
v-if="!readonly && canSave && hasPermission('invoice_application_submit')"
|
v-if="!readonly && canSave && hasPermission('invoice_application_submit')"
|
||||||
type="primary"
|
type="primary"
|
||||||
@click="submitForm"
|
@click="submitForm"
|
||||||
>提交</el-button>
|
>提交</el-button
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
</el-form>
|
</el-form>
|
||||||
|
|
||||||
<el-dialog
|
<el-dialog
|
||||||
v-model="settlementDialog.visible"
|
v-model="settlementDialog.visible"
|
||||||
title="选择应付正式结算单"
|
title="选择应收正式结算单"
|
||||||
width="86%"
|
width="86%"
|
||||||
append-to-body
|
append-to-body
|
||||||
>
|
>
|
||||||
@@ -425,18 +423,19 @@
|
|||||||
v-model="settlementDialog.keyword"
|
v-model="settlementDialog.keyword"
|
||||||
clearable
|
clearable
|
||||||
placeholder="结算单号、项目或合同"
|
placeholder="结算单号、项目或合同"
|
||||||
@keyup.enter="loadSettlementCandidates"
|
@keyup.enter="handleSettlementSearch"
|
||||||
/>
|
/>
|
||||||
<el-button type="primary" @click="loadSettlementCandidates">查询</el-button>
|
<el-button type="primary" @click="handleSettlementSearch">查询</el-button>
|
||||||
</div>
|
</div>
|
||||||
<el-table
|
<el-table
|
||||||
ref="settlementTable"
|
ref="settlementTable"
|
||||||
v-loading="settlementDialog.loading"
|
v-loading="settlementDialog.loading"
|
||||||
:data="settlementDialog.rows"
|
:data="settlementDialog.rows"
|
||||||
|
row-key="id"
|
||||||
border
|
border
|
||||||
@selection-change="settlementDialog.selection = $event"
|
@selection-change="settlementDialog.selection = $event"
|
||||||
>
|
>
|
||||||
<el-table-column type="selection" width="52" align="center" />
|
<el-table-column type="selection" width="52" align="center" reserve-selection />
|
||||||
<el-table-column prop="formalSettlementNo" label="结算单号" min-width="170" />
|
<el-table-column prop="formalSettlementNo" label="结算单号" min-width="170" />
|
||||||
<el-table-column prop="projectName" label="所属项目" min-width="150" />
|
<el-table-column prop="projectName" label="所属项目" min-width="150" />
|
||||||
<el-table-column prop="deptName" label="所属组织" min-width="150" />
|
<el-table-column prop="deptName" label="所属组织" min-width="150" />
|
||||||
@@ -456,6 +455,17 @@
|
|||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
|
<div class="invoice-form-page__dialog-pagination">
|
||||||
|
<el-pagination
|
||||||
|
v-model:current-page="settlementDialog.page.current"
|
||||||
|
v-model:page-size="settlementDialog.page.size"
|
||||||
|
:total="settlementDialog.page.total"
|
||||||
|
:page-sizes="[10, 20, 50, 100]"
|
||||||
|
layout="total, sizes, prev, pager, next, jumper"
|
||||||
|
@current-change="loadSettlementCandidates"
|
||||||
|
@size-change="handleSettlementSizeChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<el-button @click="settlementDialog.visible = false">取消</el-button>
|
<el-button @click="settlementDialog.visible = false">取消</el-button>
|
||||||
<el-button type="primary" @click="confirmSettlements">确定</el-button>
|
<el-button type="primary" @click="confirmSettlements">确定</el-button>
|
||||||
@@ -468,6 +478,8 @@
|
|||||||
import { mapGetters } from 'vuex';
|
import { mapGetters } from 'vuex';
|
||||||
import * as api from '@/api/payment/invoiceApplication';
|
import * as api from '@/api/payment/invoiceApplication';
|
||||||
import { getAll as getInvoiceItems } from '@/api/base/invoice-item';
|
import { getAll as getInvoiceItems } from '@/api/base/invoice-item';
|
||||||
|
import { getList as getWaybillList } from '@/api/business/waybill-manage';
|
||||||
|
import { getList as getReceivablePayableDetailList } from '@/api/settlement/receivable-payable-detail';
|
||||||
import { getDictionary } from '@/api/system/dictbiz';
|
import { getDictionary } from '@/api/system/dictbiz';
|
||||||
import { invoiceApplicationDetailColumns } from '@/option/payment/invoiceApplication';
|
import { invoiceApplicationDetailColumns } from '@/option/payment/invoiceApplication';
|
||||||
import VehicleAttachmentTable from '@/components/vehicle-attachment-table/main.vue';
|
import VehicleAttachmentTable from '@/components/vehicle-attachment-table/main.vue';
|
||||||
@@ -539,6 +551,7 @@ export default {
|
|||||||
keyword: '',
|
keyword: '',
|
||||||
rows: [],
|
rows: [],
|
||||||
selection: [],
|
selection: [],
|
||||||
|
page: { current: 1, size: 10, total: 0 },
|
||||||
},
|
},
|
||||||
rules: {
|
rules: {
|
||||||
settlementIds: [{ validator: this.validateSettlements, trigger: 'change' }],
|
settlementIds: [{ validator: this.validateSettlements, trigger: 'change' }],
|
||||||
@@ -662,7 +675,8 @@ export default {
|
|||||||
if (this.settlementIds.length) {
|
if (this.settlementIds.length) {
|
||||||
await Promise.all([this.loadReceiverInformation(), this.loadSettlementDetails(false)]);
|
await Promise.all([this.loadReceiverInformation(), this.loadSettlementDetails(false)]);
|
||||||
}
|
}
|
||||||
if (this.readonly) this.$nextTick(() => this.$refs.formRef?.clearValidate('departmentEmails'));
|
if (this.readonly)
|
||||||
|
this.$nextTick(() => this.$refs.formRef?.clearValidate('departmentEmails'));
|
||||||
this.$nextTick(this.restoreDetailSelection);
|
this.$nextTick(this.restoreDetailSelection);
|
||||||
},
|
},
|
||||||
async loadInvoiceItems() {
|
async loadInvoiceItems() {
|
||||||
@@ -683,8 +697,7 @@ export default {
|
|||||||
transportTypeLabel(value) {
|
transportTypeLabel(value) {
|
||||||
if (!value) return '-';
|
if (!value) return '-';
|
||||||
return (
|
return (
|
||||||
this.transportTypeOptions.find(item => String(item.value) === String(value))?.label ||
|
this.transportTypeOptions.find(item => String(item.value) === String(value))?.label || value
|
||||||
value
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
invoiceItemNames(categoryName) {
|
invoiceItemNames(categoryName) {
|
||||||
@@ -798,23 +811,49 @@ export default {
|
|||||||
},
|
},
|
||||||
async openSettlementDialog() {
|
async openSettlementDialog() {
|
||||||
this.settlementDialog.visible = true;
|
this.settlementDialog.visible = true;
|
||||||
|
this.settlementDialog.page.current = 1;
|
||||||
|
this.settlementDialog.selection = [];
|
||||||
|
this.$nextTick(() => this.$refs.settlementTable?.clearSelection());
|
||||||
await this.loadSettlementCandidates();
|
await this.loadSettlementCandidates();
|
||||||
},
|
},
|
||||||
async loadSettlementCandidates() {
|
async loadSettlementCandidates() {
|
||||||
this.settlementDialog.loading = true;
|
this.settlementDialog.loading = true;
|
||||||
try {
|
try {
|
||||||
const data = this.unwrapData(
|
const data = this.unwrapData(
|
||||||
await api.getSettlementCandidates(this.settlementDialog.keyword)
|
await api.getSettlementCandidates(
|
||||||
|
this.settlementDialog.page.current,
|
||||||
|
this.settlementDialog.page.size,
|
||||||
|
{
|
||||||
|
keyword: this.settlementDialog.keyword,
|
||||||
|
contractCategory: '客户合同',
|
||||||
|
settlementType: 'receivable',
|
||||||
|
invoiceStatus: 'unreceived',
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
const rows = Array.isArray(data) ? data : data?.records || [];
|
||||||
|
this.settlementDialog.rows = rows;
|
||||||
|
this.settlementDialog.page.total = Number(
|
||||||
|
Array.isArray(data) ? data.length : data?.total || 0
|
||||||
);
|
);
|
||||||
this.settlementDialog.rows = Array.isArray(data) ? data : data?.records || [];
|
|
||||||
} finally {
|
} finally {
|
||||||
this.settlementDialog.loading = false;
|
this.settlementDialog.loading = false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
handleSettlementSearch() {
|
||||||
|
this.settlementDialog.page.current = 1;
|
||||||
|
this.settlementDialog.selection = [];
|
||||||
|
this.$refs.settlementTable?.clearSelection();
|
||||||
|
this.loadSettlementCandidates();
|
||||||
|
},
|
||||||
|
handleSettlementSizeChange() {
|
||||||
|
this.settlementDialog.page.current = 1;
|
||||||
|
this.loadSettlementCandidates();
|
||||||
|
},
|
||||||
async confirmSettlements() {
|
async confirmSettlements() {
|
||||||
const rows = this.settlementDialog.selection;
|
const rows = this.settlementDialog.selection;
|
||||||
if (!rows.length) {
|
if (!rows.length) {
|
||||||
this.$message.warning('请至少选择一张应付正式结算单');
|
this.$message.warning('请至少选择一张应收正式结算单');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await this.applySettlements(rows);
|
await this.applySettlements(rows);
|
||||||
@@ -840,7 +879,7 @@ export default {
|
|||||||
...item,
|
...item,
|
||||||
formalSettlementId: item.id,
|
formalSettlementId: item.id,
|
||||||
settlementId: item.id,
|
settlementId: item.id,
|
||||||
allocatedInvoiceAmount: 0,
|
allocatedInvoiceAmount: rows.length === 1 ? Number(item.availableInvoiceAmount || 0) : 0,
|
||||||
}));
|
}));
|
||||||
this.form.projectName = first.projectName;
|
this.form.projectName = first.projectName;
|
||||||
this.form.deptName = first.deptName;
|
this.form.deptName = first.deptName;
|
||||||
@@ -906,12 +945,10 @@ export default {
|
|||||||
const invoiceInfoEmails = [
|
const invoiceInfoEmails = [
|
||||||
...this.receiverInvoiceOptions.flatMap(item => this.parseEmails(item.email)),
|
...this.receiverInvoiceOptions.flatMap(item => this.parseEmails(item.email)),
|
||||||
];
|
];
|
||||||
this.departmentEmailOptions = invoiceInfoEmails
|
this.departmentEmailOptions = invoiceInfoEmails.filter(Boolean).reduce((emails, email) => {
|
||||||
.filter(Boolean)
|
if (!emails.some(item => item.toLowerCase() === email.toLowerCase())) emails.push(email);
|
||||||
.reduce((emails, email) => {
|
return emails;
|
||||||
if (!emails.some(item => item.toLowerCase() === email.toLowerCase())) emails.push(email);
|
}, []);
|
||||||
return emails;
|
|
||||||
}, []);
|
|
||||||
this.form.departmentEmails = this.form.departmentEmails
|
this.form.departmentEmails = this.form.departmentEmails
|
||||||
.filter(email =>
|
.filter(email =>
|
||||||
this.departmentEmailOptions.some(option => option.toLowerCase() === email.toLowerCase())
|
this.departmentEmailOptions.some(option => option.toLowerCase() === email.toLowerCase())
|
||||||
@@ -962,6 +999,56 @@ export default {
|
|||||||
if (this.readonly) return;
|
if (this.readonly) return;
|
||||||
this.selectedDetailIds = rows.map(item => item.formalSettlementDetailId || item.id);
|
this.selectedDetailIds = rows.map(item => item.formalSettlementDetailId || item.id);
|
||||||
},
|
},
|
||||||
|
handleDetailLink(row, column) {
|
||||||
|
if (column.prop === 'documentNo') {
|
||||||
|
this.openReceivableDetail(row);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (column.prop === 'waybillNo') this.openWaybillDetail(row);
|
||||||
|
},
|
||||||
|
async openReceivableDetail(row) {
|
||||||
|
let detailId = row.sourceDetailId || '';
|
||||||
|
if (!detailId && row.documentNo) {
|
||||||
|
try {
|
||||||
|
const data = this.unwrapData(
|
||||||
|
await getReceivablePayableDetailList(1, 1, {
|
||||||
|
documentNo: row.documentNo,
|
||||||
|
settlementType: 'receivable',
|
||||||
|
})
|
||||||
|
);
|
||||||
|
detailId = (Array.isArray(data) ? data : data?.records || [])[0]?.id || '';
|
||||||
|
} catch {
|
||||||
|
detailId = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!detailId) {
|
||||||
|
this.$message.info('当前记录未找到对应的应收明细');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.$router.push({
|
||||||
|
path: '/settlement/receivable-detail',
|
||||||
|
query: { detailId },
|
||||||
|
});
|
||||||
|
},
|
||||||
|
async openWaybillDetail(row) {
|
||||||
|
let waybillId = row.waybillId || '';
|
||||||
|
if (!waybillId && row.waybillNo) {
|
||||||
|
try {
|
||||||
|
const data = this.unwrapData(await getWaybillList(1, 1, { waybillNo: row.waybillNo }));
|
||||||
|
waybillId = (Array.isArray(data) ? data : data?.records || [])[0]?.id || '';
|
||||||
|
} catch {
|
||||||
|
waybillId = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!waybillId) {
|
||||||
|
this.$message.info('当前记录未找到对应的运单详情');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.$router.push({
|
||||||
|
path: '/business/waybill-manage',
|
||||||
|
query: { detailId: waybillId },
|
||||||
|
});
|
||||||
|
},
|
||||||
restoreDetailSelection() {
|
restoreDetailSelection() {
|
||||||
if (this.readonly || !this.$refs.detailTable) return;
|
if (this.readonly || !this.$refs.detailTable) return;
|
||||||
const selected = new Set(this.selectedDetailIds.map(String));
|
const selected = new Set(this.selectedDetailIds.map(String));
|
||||||
@@ -1138,6 +1225,11 @@ export default {
|
|||||||
.invoice-form-page__dialog-search .el-input {
|
.invoice-form-page__dialog-search .el-input {
|
||||||
width: 360px;
|
width: 360px;
|
||||||
}
|
}
|
||||||
|
.invoice-form-page__dialog-pagination {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
margin-top: 12px;
|
||||||
|
}
|
||||||
.invoice-form-page :deep(.el-table) {
|
.invoice-form-page :deep(.el-table) {
|
||||||
--el-table-border-color: #eff1f7;
|
--el-table-border-color: #eff1f7;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -163,6 +163,7 @@
|
|||||||
v-loading="invoiceDialog.loading"
|
v-loading="invoiceDialog.loading"
|
||||||
:data="invoiceDialog.rows"
|
:data="invoiceDialog.rows"
|
||||||
border
|
border
|
||||||
|
empty-text="暂无票据数据"
|
||||||
highlight-current-row
|
highlight-current-row
|
||||||
@current-change="invoiceDialog.current = $event"
|
@current-change="invoiceDialog.current = $event"
|
||||||
@row-dblclick="confirmInvoice"
|
@row-dblclick="confirmInvoice"
|
||||||
@@ -188,6 +189,17 @@
|
|||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
|
<div class="receipt-form-page__dialog-pagination">
|
||||||
|
<el-pagination
|
||||||
|
v-model:current-page="invoiceDialog.page.current"
|
||||||
|
v-model:page-size="invoiceDialog.page.size"
|
||||||
|
:total="invoiceDialog.page.total"
|
||||||
|
:page-sizes="[10, 20, 50, 100]"
|
||||||
|
layout="total, sizes, prev, pager, next, jumper"
|
||||||
|
@current-change="refreshInvoicePoolPage"
|
||||||
|
@size-change="handleInvoicePoolSizeChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<el-button @click="invoiceDialog.visible = false">取消</el-button>
|
<el-button @click="invoiceDialog.visible = false">取消</el-button>
|
||||||
<el-button type="primary" @click="confirmInvoice()">确定</el-button>
|
<el-button type="primary" @click="confirmInvoice()">确定</el-button>
|
||||||
@@ -247,67 +259,12 @@
|
|||||||
import { Search } from '@element-plus/icons-vue';
|
import { Search } from '@element-plus/icons-vue';
|
||||||
import { mapGetters } from 'vuex';
|
import { mapGetters } from 'vuex';
|
||||||
import * as api from '@/api/payment/invoiceReceipt';
|
import * as api from '@/api/payment/invoiceReceipt';
|
||||||
|
import { getList as getCustomerList } from '@/api/vehicle/customer-archive';
|
||||||
import VehicleAttachmentTable from '@/components/vehicle-attachment-table/main.vue';
|
import VehicleAttachmentTable from '@/components/vehicle-attachment-table/main.vue';
|
||||||
|
|
||||||
const invoicePoolMockTemplates = [
|
const mockInvoiceTypes = ['增值税专用发票', '增值税普通发票'];
|
||||||
{
|
const mockTaxRates = [3, 6, 9, 13];
|
||||||
id: 90000001,
|
const mockBankNames = ['中国工商银行', '中国农业银行', '中国银行', '中国建设银行', '招商银行'];
|
||||||
invoiceNo: '25312000000000000101',
|
|
||||||
invoiceDate: '2026-08-25',
|
|
||||||
invoiceType: '增值税专用发票',
|
|
||||||
taxRate: 9,
|
|
||||||
invoiceAmount: 109000,
|
|
||||||
taxAmount: 9000,
|
|
||||||
receiverName: '华东供应链管理有限公司',
|
|
||||||
issuerName: '上海安捷运输有限公司',
|
|
||||||
bankName: '中国工商银行上海分行',
|
|
||||||
bankAccount: '1001000100000000019',
|
|
||||||
issuingBank: '中国工商银行上海分行营业部',
|
|
||||||
phone: '021-66880001',
|
|
||||||
customerEmails: 'finance@huadong.example.com',
|
|
||||||
departmentEmails: 'transport@huadong.example.com',
|
|
||||||
attachmentsJson: '[]',
|
|
||||||
kingdeeBillNo: 'KD-FP-20260825001',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 90000002,
|
|
||||||
invoiceNo: '25312000000000000102',
|
|
||||||
invoiceDate: '2026-08-26',
|
|
||||||
invoiceType: '增值税普通发票',
|
|
||||||
taxRate: 6,
|
|
||||||
invoiceAmount: 53000,
|
|
||||||
taxAmount: 3000,
|
|
||||||
receiverName: '华东供应链管理有限公司',
|
|
||||||
issuerName: '江苏联运物流有限公司',
|
|
||||||
bankName: '中国建设银行南京分行',
|
|
||||||
bankAccount: '3201000100000000026',
|
|
||||||
issuingBank: '中国建设银行南京城南支行',
|
|
||||||
phone: '025-66880002',
|
|
||||||
customerEmails: 'finance@huadong.example.com',
|
|
||||||
departmentEmails: 'logistics@huadong.example.com',
|
|
||||||
attachmentsJson: '[]',
|
|
||||||
kingdeeBillNo: 'KD-FP-20260826002',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 90000003,
|
|
||||||
invoiceNo: '25312000000000000103',
|
|
||||||
invoiceDate: '2026-08-27',
|
|
||||||
invoiceType: '增值税专用发票',
|
|
||||||
taxRate: 3,
|
|
||||||
invoiceAmount: 20600,
|
|
||||||
taxAmount: 600,
|
|
||||||
receiverName: '华东供应链管理有限公司',
|
|
||||||
issuerName: '浙江通达物流有限公司',
|
|
||||||
bankName: '招商银行杭州分行',
|
|
||||||
bankAccount: '5719000100000000033',
|
|
||||||
issuingBank: '招商银行杭州高新支行',
|
|
||||||
phone: '0571-66880003',
|
|
||||||
customerEmails: 'finance@huadong.example.com',
|
|
||||||
departmentEmails: 'settlement@huadong.example.com',
|
|
||||||
attachmentsJson: '[]',
|
|
||||||
kingdeeBillNo: 'KD-FP-20260827003',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const randomDigits = length =>
|
const randomDigits = length =>
|
||||||
Array.from({ length }, () => Math.floor(Math.random() * 10)).join('');
|
Array.from({ length }, () => Math.floor(Math.random() * 10)).join('');
|
||||||
@@ -319,28 +276,59 @@ const formatMockDate = date => {
|
|||||||
return `${year}-${month}-${day}`;
|
return `${year}-${month}-${day}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
const createInvoicePoolMockRows = () =>
|
const customerName = customer =>
|
||||||
invoicePoolMockTemplates.map((template, index) => {
|
customer.fullName || customer.shortName || customer.customerCode || `客商${customer.id}`;
|
||||||
const invoiceDateValue = new Date();
|
|
||||||
invoiceDateValue.setDate(invoiceDateValue.getDate() - Math.floor(Math.random() * 15));
|
const customerEmail = (customer, index) => {
|
||||||
const invoiceDate = formatMockDate(invoiceDateValue);
|
const prefix = String(customer.customerCode || customer.id || `customer${index + 1}`)
|
||||||
const invoiceAmount = Math.floor(10000 + Math.random() * 190000);
|
.replace(/[^a-zA-Z0-9_-]/g, '')
|
||||||
const taxAmount = Number(
|
.toLowerCase();
|
||||||
(
|
return `finance@${prefix || `customer${index + 1}`}.example.com`;
|
||||||
(invoiceAmount * Number(template.taxRate || 0)) /
|
};
|
||||||
(100 + Number(template.taxRate || 0))
|
|
||||||
).toFixed(2)
|
const createInvoicePoolMockRows = (customers, counterpartyName, departmentEmail) => {
|
||||||
);
|
const rows = [];
|
||||||
return {
|
const baseId = Date.now();
|
||||||
...template,
|
customers.forEach((customer, customerIndex) => {
|
||||||
id: Date.now() + index * 100 + Math.floor(Math.random() * 100),
|
const name = customerName(customer);
|
||||||
invoiceNo: `25${randomDigits(18)}`,
|
['issuer', 'receiver'].forEach((role, roleIndex) => {
|
||||||
invoiceDate,
|
const count = 3 + Math.floor(Math.random() * 5);
|
||||||
invoiceAmount,
|
for (let index = 0; index < count; index += 1) {
|
||||||
taxAmount,
|
const rowIndex = rows.length;
|
||||||
kingdeeBillNo: `KD-FP-${invoiceDate.replaceAll('-', '')}${randomDigits(3)}`,
|
const invoiceDateValue = new Date();
|
||||||
};
|
invoiceDateValue.setDate(invoiceDateValue.getDate() - Math.floor(Math.random() * 90));
|
||||||
|
const invoiceDate = formatMockDate(invoiceDateValue);
|
||||||
|
const taxRate = mockTaxRates[(customerIndex + roleIndex + index) % mockTaxRates.length];
|
||||||
|
const invoiceAmount = Math.floor(10000 + Math.random() * 190000);
|
||||||
|
const taxAmount = Number(
|
||||||
|
((invoiceAmount * Number(taxRate)) / (100 + Number(taxRate))).toFixed(2)
|
||||||
|
);
|
||||||
|
const bankName = mockBankNames[(customerIndex + index) % mockBankNames.length];
|
||||||
|
rows.push({
|
||||||
|
id: baseId + rowIndex,
|
||||||
|
invoiceNo: `25${randomDigits(18)}`,
|
||||||
|
invoiceDate,
|
||||||
|
invoiceType: mockInvoiceTypes[(customerIndex + index) % mockInvoiceTypes.length],
|
||||||
|
taxRate,
|
||||||
|
invoiceAmount,
|
||||||
|
taxAmount,
|
||||||
|
receiverName: role === 'receiver' ? name : counterpartyName,
|
||||||
|
issuerName: role === 'issuer' ? name : counterpartyName,
|
||||||
|
bankName,
|
||||||
|
bankAccount: randomDigits(19),
|
||||||
|
issuingBank: `${bankName}营业部`,
|
||||||
|
phone: customer.contactPhone || '',
|
||||||
|
customerEmails: customerEmail(customer, customerIndex),
|
||||||
|
departmentEmails: departmentEmail || '',
|
||||||
|
attachmentsJson: '[]',
|
||||||
|
kingdeeBillNo: `KD-FP-${invoiceDate.replaceAll('-', '')}${randomDigits(5)}`,
|
||||||
|
kingdeeStatus: 'unsynced',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
return rows;
|
||||||
|
};
|
||||||
|
|
||||||
const emptyForm = () => ({
|
const emptyForm = () => ({
|
||||||
id: null,
|
id: null,
|
||||||
@@ -380,9 +368,18 @@ export default {
|
|||||||
Search,
|
Search,
|
||||||
form: emptyForm(),
|
form: emptyForm(),
|
||||||
invoicePoolMockRows: [],
|
invoicePoolMockRows: [],
|
||||||
|
invoicePoolMockLoaded: false,
|
||||||
customerEmailOptions: [],
|
customerEmailOptions: [],
|
||||||
departmentEmailOptions: [],
|
departmentEmailOptions: [],
|
||||||
invoiceDialog: { visible: false, loading: false, keyword: '', rows: [], current: null },
|
invoiceDialog: {
|
||||||
|
visible: false,
|
||||||
|
loading: false,
|
||||||
|
keyword: '',
|
||||||
|
sourceRows: [],
|
||||||
|
rows: [],
|
||||||
|
current: null,
|
||||||
|
page: { current: 1, size: 10, total: 0 },
|
||||||
|
},
|
||||||
settlementDialog: {
|
settlementDialog: {
|
||||||
visible: false,
|
visible: false,
|
||||||
loading: false,
|
loading: false,
|
||||||
@@ -447,7 +444,6 @@ export default {
|
|||||||
.filter(Boolean);
|
.filter(Boolean);
|
||||||
},
|
},
|
||||||
async initialize() {
|
async initialize() {
|
||||||
this.invoicePoolMockRows = createInvoicePoolMockRows();
|
|
||||||
const userInfo = this.$store.getters.userInfo || {};
|
const userInfo = this.$store.getters.userInfo || {};
|
||||||
const userEmail = userInfo.email || '';
|
const userEmail = userInfo.email || '';
|
||||||
this.departmentEmailOptions = userEmail ? [userEmail] : [];
|
this.departmentEmailOptions = userEmail ? [userEmail] : [];
|
||||||
@@ -499,22 +495,72 @@ export default {
|
|||||||
async openInvoiceDialog() {
|
async openInvoiceDialog() {
|
||||||
this.invoiceDialog.visible = true;
|
this.invoiceDialog.visible = true;
|
||||||
this.invoiceDialog.keyword = this.form.invoiceNo || '';
|
this.invoiceDialog.keyword = this.form.invoiceNo || '';
|
||||||
if (!this.recordId) this.invoicePoolMockRows = createInvoicePoolMockRows();
|
|
||||||
await this.loadInvoicePool();
|
await this.loadInvoicePool();
|
||||||
},
|
},
|
||||||
async loadInvoicePool() {
|
async loadInvoicePool() {
|
||||||
this.invoiceDialog.loading = true;
|
this.invoiceDialog.loading = true;
|
||||||
|
this.invoiceDialog.page.current = 1;
|
||||||
|
this.invoiceDialog.current = null;
|
||||||
try {
|
try {
|
||||||
if (!this.recordId) {
|
if (!this.recordId) {
|
||||||
this.invoiceDialog.rows = this.filterInvoicePoolMockRows(this.invoiceDialog.keyword);
|
if (!this.invoicePoolMockLoaded) await this.loadInvoicePoolMockRows();
|
||||||
|
this.invoiceDialog.sourceRows = this.filterInvoicePoolMockRows(
|
||||||
|
this.invoiceDialog.keyword
|
||||||
|
);
|
||||||
|
this.refreshInvoicePoolPage();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const data = this.unwrapData(await api.getInvoicePool(this.invoiceDialog.keyword));
|
const data = this.unwrapData(await api.getInvoicePool(this.invoiceDialog.keyword));
|
||||||
this.invoiceDialog.rows = Array.isArray(data) ? data : data?.records || [];
|
this.invoiceDialog.sourceRows = Array.isArray(data) ? data : data?.records || [];
|
||||||
|
this.refreshInvoicePoolPage();
|
||||||
} finally {
|
} finally {
|
||||||
this.invoiceDialog.loading = false;
|
this.invoiceDialog.loading = false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
async loadInvoicePoolMockRows() {
|
||||||
|
const pageSize = 500;
|
||||||
|
const firstData = this.unwrapData(await getCustomerList(1, pageSize, {}));
|
||||||
|
const firstRows = Array.isArray(firstData) ? firstData : firstData?.records || [];
|
||||||
|
const total = Number(firstData?.total || firstRows.length);
|
||||||
|
const pageCount = Math.ceil(total / pageSize);
|
||||||
|
const remainingPages = await Promise.all(
|
||||||
|
Array.from({ length: Math.max(pageCount - 1, 0) }, (_, index) =>
|
||||||
|
getCustomerList(index + 2, pageSize, {})
|
||||||
|
)
|
||||||
|
);
|
||||||
|
const customers = [
|
||||||
|
...firstRows,
|
||||||
|
...remainingPages.flatMap(response => {
|
||||||
|
const data = this.unwrapData(response);
|
||||||
|
return Array.isArray(data) ? data : data?.records || [];
|
||||||
|
}),
|
||||||
|
].filter(
|
||||||
|
(customer, index, rows) =>
|
||||||
|
rows.findIndex(item => String(item.id) === String(customer.id)) === index
|
||||||
|
);
|
||||||
|
const userInfo = this.$store.getters.userInfo || {};
|
||||||
|
const counterpartyName = userInfo.deptName || userInfo.dept_name || '本企业';
|
||||||
|
this.invoicePoolMockRows = createInvoicePoolMockRows(
|
||||||
|
customers,
|
||||||
|
counterpartyName,
|
||||||
|
userInfo.email || ''
|
||||||
|
);
|
||||||
|
this.invoicePoolMockLoaded = true;
|
||||||
|
},
|
||||||
|
refreshInvoicePoolPage() {
|
||||||
|
const page = this.invoiceDialog.page;
|
||||||
|
const total = this.invoiceDialog.sourceRows.length;
|
||||||
|
const maxPage = Math.max(Math.ceil(total / page.size), 1);
|
||||||
|
if (page.current > maxPage) page.current = maxPage;
|
||||||
|
const start = (page.current - 1) * page.size;
|
||||||
|
page.total = total;
|
||||||
|
this.invoiceDialog.current = null;
|
||||||
|
this.invoiceDialog.rows = this.invoiceDialog.sourceRows.slice(start, start + page.size);
|
||||||
|
},
|
||||||
|
handleInvoicePoolSizeChange() {
|
||||||
|
this.invoiceDialog.page.current = 1;
|
||||||
|
this.refreshInvoicePoolPage();
|
||||||
|
},
|
||||||
filterInvoicePoolMockRows(keyword) {
|
filterInvoicePoolMockRows(keyword) {
|
||||||
const normalizedKeyword = String(keyword || '')
|
const normalizedKeyword = String(keyword || '')
|
||||||
.trim()
|
.trim()
|
||||||
@@ -781,6 +827,11 @@ export default {
|
|||||||
.receipt-form-page__dialog-search .el-input {
|
.receipt-form-page__dialog-search .el-input {
|
||||||
width: 360px;
|
width: 360px;
|
||||||
}
|
}
|
||||||
|
.receipt-form-page__dialog-pagination {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
margin-top: 12px;
|
||||||
|
}
|
||||||
.receipt-form-page :deep(.el-table) {
|
.receipt-form-page :deep(.el-table) {
|
||||||
--el-table-border-color: #eff1f7;
|
--el-table-border-color: #eff1f7;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -164,9 +164,11 @@
|
|||||||
></el-col>
|
></el-col>
|
||||||
<el-col :span="6"
|
<el-col :span="6"
|
||||||
><el-form-item label="收款账号" prop="receiptAccountId"
|
><el-form-item label="收款账号" prop="receiptAccountId"
|
||||||
|
><span v-if="readonly">{{ form.bankAccount || '-' }}</span
|
||||||
><el-select
|
><el-select
|
||||||
|
v-else
|
||||||
v-model="form.receiptAccountId"
|
v-model="form.receiptAccountId"
|
||||||
:disabled="readonly || !form.payeeName"
|
:disabled="!form.payeeName"
|
||||||
:loading="receiptAccountLoading"
|
:loading="receiptAccountLoading"
|
||||||
filterable
|
filterable
|
||||||
clearable
|
clearable
|
||||||
@@ -238,9 +240,19 @@
|
|||||||
<el-table-column label="匹配金额(含税)" min-width="150">
|
<el-table-column label="匹配金额(含税)" min-width="150">
|
||||||
<template #default="{ row }">{{ formatMoney(row.matchedAmount) }}</template>
|
<template #default="{ row }">{{ formatMoney(row.matchedAmount) }}</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column v-if="!readonly" label="操作" width="100">
|
<el-table-column label="操作" width="220" fixed="right" align="center">
|
||||||
<template #default="{ $index }">
|
<template #default="{ row, $index }">
|
||||||
<el-link type="danger" @click="form.invoices.splice($index, 1)">删除</el-link>
|
<div class="payment-form-page__invoice-actions">
|
||||||
|
<el-link type="primary" @click="viewInvoiceAttachment(row)">查看</el-link>
|
||||||
|
<el-link type="primary" @click="downloadInvoiceAttachment(row)">下载</el-link>
|
||||||
|
<el-link
|
||||||
|
v-if="!readonly"
|
||||||
|
type="danger"
|
||||||
|
@click="form.invoices.splice($index, 1)"
|
||||||
|
>
|
||||||
|
删除
|
||||||
|
</el-link>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
@@ -342,7 +354,6 @@
|
|||||||
:show-file-list="false"
|
:show-file-list="false"
|
||||||
button-text="上传附件"
|
button-text="上传附件"
|
||||||
@change="normalizeAttachments"
|
@change="normalizeAttachments"
|
||||||
@success="handleManualAttachmentUpload"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</section-card>
|
</section-card>
|
||||||
@@ -598,7 +609,7 @@ const emptyForm = () => ({
|
|||||||
settlementAmount: 0,
|
settlementAmount: 0,
|
||||||
payableAmount: 0,
|
payableAmount: 0,
|
||||||
billType: '',
|
billType: '',
|
||||||
paymentRatio: 50,
|
paymentRatio: null,
|
||||||
appliedAmount: 0,
|
appliedAmount: 0,
|
||||||
paymentMethod: 'bank_transfer',
|
paymentMethod: 'bank_transfer',
|
||||||
billLedgerId: null,
|
billLedgerId: null,
|
||||||
@@ -661,6 +672,8 @@ export default {
|
|||||||
receiptAccountLoading: false,
|
receiptAccountLoading: false,
|
||||||
firstContractPayment: false,
|
firstContractPayment: false,
|
||||||
contractSignatureFileAvailable: false,
|
contractSignatureFileAvailable: false,
|
||||||
|
contractPaymentRatioLimit: null,
|
||||||
|
contractPaymentRatioExceeded: false,
|
||||||
payeeCustomerLevel: '',
|
payeeCustomerLevel: '',
|
||||||
attachmentRuleLoading: false,
|
attachmentRuleLoading: false,
|
||||||
attachmentImagePreviewVisible: false,
|
attachmentImagePreviewVisible: false,
|
||||||
@@ -794,6 +807,7 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
'form.paymentRatio'(value) {
|
'form.paymentRatio'(value) {
|
||||||
|
this.checkContractPaymentRatio(value);
|
||||||
if (this.amountSyncing || !this.paymentAmountBase) return;
|
if (this.amountSyncing || !this.paymentAmountBase) return;
|
||||||
this.amountSyncing = true;
|
this.amountSyncing = true;
|
||||||
this.form.appliedAmount = Number(
|
this.form.appliedAmount = Number(
|
||||||
@@ -946,6 +960,11 @@ export default {
|
|||||||
throw new Error('付款记录金额合计不能超过申请付款金额');
|
throw new Error('付款记录金额合计不能超过申请付款金额');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
formatLocalDate(value) {
|
||||||
|
if (!value) return null;
|
||||||
|
const date = this.$dayjs(value);
|
||||||
|
return date.isValid() ? date.format('YYYY-MM-DD') : null;
|
||||||
|
},
|
||||||
unwrapData(response) {
|
unwrapData(response) {
|
||||||
const body = response?.data || response || {};
|
const body = response?.data || response || {};
|
||||||
return body?.data || body;
|
return body?.data || body;
|
||||||
@@ -1008,7 +1027,8 @@ export default {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
isValidReferenceId(id) {
|
isValidReferenceId(id) {
|
||||||
return id !== null && id !== undefined && String(id).trim() !== '-1';
|
const value = String(id ?? '').trim();
|
||||||
|
return value !== '' && value !== '-1' && value !== '0';
|
||||||
},
|
},
|
||||||
async loadTransferredPreSettlements() {
|
async loadTransferredPreSettlements() {
|
||||||
const fallbackRows = this.transferPayload?.sourcePreSettlements || [];
|
const fallbackRows = this.transferPayload?.sourcePreSettlements || [];
|
||||||
@@ -1241,6 +1261,44 @@ export default {
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
invoiceAttachment(row = {}) {
|
||||||
|
const value = row.attachmentJson;
|
||||||
|
if (!value) return {};
|
||||||
|
if (typeof value === 'object') return Array.isArray(value) ? value[0] || {} : value;
|
||||||
|
try {
|
||||||
|
const parsed = JSON.parse(value);
|
||||||
|
return Array.isArray(parsed) ? parsed[0] || {} : parsed || {};
|
||||||
|
} catch {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
invoiceAttachmentUrl(row) {
|
||||||
|
return this.attachmentFileUrl(this.invoiceAttachment(row));
|
||||||
|
},
|
||||||
|
invoiceAttachmentName(row) {
|
||||||
|
return (
|
||||||
|
this.attachmentFileName(this.invoiceAttachment(row)) || `${row.invoiceNo || '发票'}.pdf`
|
||||||
|
);
|
||||||
|
},
|
||||||
|
viewInvoiceAttachment(row) {
|
||||||
|
const attachment = this.invoiceAttachment(row);
|
||||||
|
if (!this.attachmentFileUrl(attachment)) {
|
||||||
|
this.$message.warning('当前发票暂无可查看附件');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.previewAttachment(
|
||||||
|
{ ...attachment, name: this.invoiceAttachmentName(row) },
|
||||||
|
[attachment]
|
||||||
|
);
|
||||||
|
},
|
||||||
|
downloadInvoiceAttachment(row) {
|
||||||
|
const url = this.invoiceAttachmentUrl(row);
|
||||||
|
if (!url) {
|
||||||
|
this.$message.warning('当前发票暂无可下载附件');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
downloadFileByUrl(url, this.invoiceAttachmentName(row));
|
||||||
|
},
|
||||||
attachmentFileName(file) {
|
attachmentFileName(file) {
|
||||||
return String(file?.originalName || file?.name || file?.fileName || '').trim();
|
return String(file?.originalName || file?.name || file?.fileName || '').trim();
|
||||||
},
|
},
|
||||||
@@ -1277,14 +1335,14 @@ export default {
|
|||||||
this.attachmentFileExtension(file) === 'pdf'
|
this.attachmentFileExtension(file) === 'pdf'
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
previewAttachment(file) {
|
previewAttachment(file, files = this.form.attachments) {
|
||||||
const url = this.attachmentFileUrl(file);
|
const url = this.attachmentFileUrl(file);
|
||||||
if (!url) {
|
if (!url) {
|
||||||
this.$message.warning('附件地址为空,无法预览');
|
this.$message.warning('附件地址为空,无法预览');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (this.isAttachmentImage(file)) {
|
if (this.isAttachmentImage(file)) {
|
||||||
this.attachmentImagePreviewUrls = (this.form.attachments || [])
|
this.attachmentImagePreviewUrls = (files || [])
|
||||||
.filter(item => this.isAttachmentImage(item) && this.attachmentFileUrl(item))
|
.filter(item => this.isAttachmentImage(item) && this.attachmentFileUrl(item))
|
||||||
.map(item => this.attachmentFileUrl(item));
|
.map(item => this.attachmentFileUrl(item));
|
||||||
this.attachmentImagePreviewIndex = Math.max(
|
this.attachmentImagePreviewIndex = Math.max(
|
||||||
@@ -1379,22 +1437,29 @@ export default {
|
|||||||
if (imported.length) this.form.attachments = [...this.form.attachments, ...imported];
|
if (imported.length) this.form.attachments = [...this.form.attachments, ...imported];
|
||||||
},
|
},
|
||||||
async loadCurrentSettlementDetails() {
|
async loadCurrentSettlementDetails() {
|
||||||
if (this.form.paymentType === 'project_advance') return [];
|
if (
|
||||||
const requests = [];
|
this.form.paymentType === 'progress_advance' &&
|
||||||
if (this.isValidReferenceId(this.form.preSettlementId)) {
|
this.isValidReferenceId(this.form.preSettlementId)
|
||||||
requests.push(preApi.getDetail(this.form.preSettlementId));
|
) {
|
||||||
|
const response = await preApi.getDetail(this.form.preSettlementId);
|
||||||
|
const detail = this.unwrapData(response);
|
||||||
|
return detail?.id ? [detail] : [];
|
||||||
}
|
}
|
||||||
if (this.isValidReferenceId(this.form.settlementId)) {
|
if (
|
||||||
requests.push(formalApi.getDetail(this.form.settlementId));
|
this.form.paymentType === 'settlement_payment' &&
|
||||||
|
this.isValidReferenceId(this.form.settlementId)
|
||||||
|
) {
|
||||||
|
const response = await formalApi.getDetail(this.form.settlementId);
|
||||||
|
const detail = this.unwrapData(response);
|
||||||
|
return detail?.id ? [detail] : [];
|
||||||
}
|
}
|
||||||
if (!requests.length) return [];
|
return [];
|
||||||
const responses = await Promise.all(requests);
|
|
||||||
return responses.map(response => this.unwrapData(response)).filter(item => item?.id);
|
|
||||||
},
|
},
|
||||||
async loadAttachmentRuleData(settlementRows = []) {
|
async loadAttachmentRuleData(settlementRows = []) {
|
||||||
this.attachmentRuleLoading = true;
|
this.attachmentRuleLoading = true;
|
||||||
this.firstContractPayment = false;
|
this.firstContractPayment = false;
|
||||||
this.contractSignatureFileAvailable = false;
|
this.contractSignatureFileAvailable = false;
|
||||||
|
this.resetContractPaymentRatioLimit();
|
||||||
try {
|
try {
|
||||||
const settlementMaterials = [
|
const settlementMaterials = [
|
||||||
ATTACHMENT_MATERIALS.weighingSlip,
|
ATTACHMENT_MATERIALS.weighingSlip,
|
||||||
@@ -1412,6 +1477,7 @@ export default {
|
|||||||
const contractResponse = await getContractDetail(this.form.contractId);
|
const contractResponse = await getContractDetail(this.form.contractId);
|
||||||
const contract = this.unwrapData(contractResponse) || {};
|
const contract = this.unwrapData(contractResponse) || {};
|
||||||
if (this.form.paymentType === 'project_advance') {
|
if (this.form.paymentType === 'project_advance') {
|
||||||
|
this.applyContractPaymentRatioLimit(contract);
|
||||||
const contractFiles = this.parse(contract.contractFileJson);
|
const contractFiles = this.parse(contract.contractFileJson);
|
||||||
this.contractSignatureFileAvailable = contractFiles.some(file =>
|
this.contractSignatureFileAvailable = contractFiles.some(file =>
|
||||||
Boolean(this.attachmentFileUrl(file))
|
Boolean(this.attachmentFileUrl(file))
|
||||||
@@ -1448,6 +1514,41 @@ export default {
|
|||||||
this.attachmentRuleLoading = false;
|
this.attachmentRuleLoading = false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
resetContractPaymentRatioLimit() {
|
||||||
|
this.contractPaymentRatioLimit = null;
|
||||||
|
this.contractPaymentRatioExceeded = false;
|
||||||
|
},
|
||||||
|
applyContractPaymentRatioLimit(contract = {}) {
|
||||||
|
const [firstRatio] = this.parse(contract.paymentRatioJson);
|
||||||
|
const ratioLimitValue = firstRatio?.ratioLimit;
|
||||||
|
const ratioLimit = Number(ratioLimitValue);
|
||||||
|
this.contractPaymentRatioLimit =
|
||||||
|
ratioLimitValue === null ||
|
||||||
|
ratioLimitValue === undefined ||
|
||||||
|
String(ratioLimitValue).trim() === '' ||
|
||||||
|
!Number.isFinite(ratioLimit)
|
||||||
|
? null
|
||||||
|
: ratioLimit;
|
||||||
|
this.checkContractPaymentRatio(this.form.paymentRatio);
|
||||||
|
},
|
||||||
|
checkContractPaymentRatio(value) {
|
||||||
|
const paymentRatio = Number(value);
|
||||||
|
const hasPaymentRatio =
|
||||||
|
value !== null &&
|
||||||
|
value !== undefined &&
|
||||||
|
String(value).trim() !== '' &&
|
||||||
|
Number.isFinite(paymentRatio);
|
||||||
|
const exceeded =
|
||||||
|
this.form.paymentType === 'project_advance' &&
|
||||||
|
Boolean(this.form.contractId) &&
|
||||||
|
this.contractPaymentRatioLimit !== null &&
|
||||||
|
hasPaymentRatio &&
|
||||||
|
paymentRatio > this.contractPaymentRatioLimit;
|
||||||
|
if (exceeded && !this.contractPaymentRatioExceeded) {
|
||||||
|
this.$message.warning('已超合同配置比例');
|
||||||
|
}
|
||||||
|
this.contractPaymentRatioExceeded = exceeded;
|
||||||
|
},
|
||||||
validateRequiredAttachments() {
|
validateRequiredAttachments() {
|
||||||
if (!this.missingAttachmentMaterials.length) return true;
|
if (!this.missingAttachmentMaterials.length) return true;
|
||||||
this.$message.warning(
|
this.$message.warning(
|
||||||
@@ -1944,38 +2045,26 @@ export default {
|
|||||||
this.referenceVisible = false;
|
this.referenceVisible = false;
|
||||||
},
|
},
|
||||||
normalizeAttachments(files) {
|
normalizeAttachments(files) {
|
||||||
this.form.attachments = (files || []).map(file => ({
|
const existingAttachments = this.form.attachments || [];
|
||||||
...file,
|
|
||||||
attachmentType: (() => {
|
|
||||||
const matchedType = this.resolveAttachmentTypeByFileName(file);
|
|
||||||
const existingType = [
|
|
||||||
...Object.values(MATERIAL_TYPE_MAP),
|
|
||||||
'other',
|
|
||||||
].includes(file.attachmentType)
|
|
||||||
? file.attachmentType
|
|
||||||
: 'other';
|
|
||||||
return matchedType !== 'other' ? matchedType : existingType;
|
|
||||||
})(),
|
|
||||||
description: file.description || '',
|
|
||||||
}));
|
|
||||||
},
|
|
||||||
handleManualAttachmentUpload(uploadedFile) {
|
|
||||||
const uploadedUrl = this.attachmentFileUrl(uploadedFile);
|
|
||||||
const uploadedUid = uploadedFile?.uid;
|
|
||||||
const userInfo = this.$store.getters.userInfo || {};
|
const userInfo = this.$store.getters.userInfo || {};
|
||||||
const uploadUserName = userInfo.realName || userInfo.userName || '';
|
const uploadUserName = userInfo.realName || userInfo.userName || '';
|
||||||
const uploadTime = this.$dayjs().format('YYYY-MM-DD HH:mm:ss');
|
const uploadTime = this.$dayjs().format('YYYY-MM-DD HH:mm:ss');
|
||||||
this.form.attachments = (this.form.attachments || []).map(file => {
|
this.form.attachments = (files || []).map(file => {
|
||||||
const isUploadedFile =
|
const fileUrl = this.attachmentFileUrl(file);
|
||||||
(uploadedUid && file.uid === uploadedUid) ||
|
const existingFile = existingAttachments.find(item => {
|
||||||
(uploadedUrl && this.attachmentFileUrl(file) === uploadedUrl);
|
const sameUid = file.uid && item.uid && String(file.uid) === String(item.uid);
|
||||||
return isUploadedFile
|
const sameUrl = fileUrl && this.attachmentFileUrl(item) === fileUrl;
|
||||||
? {
|
return sameUid || sameUrl;
|
||||||
...file,
|
});
|
||||||
uploadUserName,
|
return {
|
||||||
uploadTime,
|
...existingFile,
|
||||||
}
|
...file,
|
||||||
: file;
|
attachmentType:
|
||||||
|
existingFile?.attachmentType || this.resolveAttachmentTypeByFileName(file),
|
||||||
|
description: file.description || existingFile?.description || '',
|
||||||
|
uploadUserName: existingFile?.uploadUserName || file.uploadUserName || uploadUserName,
|
||||||
|
uploadTime: existingFile?.uploadTime || file.uploadTime || uploadTime,
|
||||||
|
};
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
formatAttachmentSize(file = {}) {
|
formatAttachmentSize(file = {}) {
|
||||||
@@ -2088,7 +2177,7 @@ export default {
|
|||||||
? []
|
? []
|
||||||
: this.paymentRecords.map(record => ({
|
: this.paymentRecords.map(record => ({
|
||||||
paidAmount: Number(record.paidAmount || 0),
|
paidAmount: Number(record.paidAmount || 0),
|
||||||
paidDate: record.paidDate || null,
|
paidDate: this.formatLocalDate(record.paidDate),
|
||||||
paymentNo: record.paymentNo || '',
|
paymentNo: record.paymentNo || '',
|
||||||
voucherJson: record.voucherJson || '',
|
voucherJson: record.voucherJson || '',
|
||||||
kingdeeBillNo: record.kingdeeBillNo || '',
|
kingdeeBillNo: record.kingdeeBillNo || '',
|
||||||
@@ -2201,6 +2290,12 @@ export default {
|
|||||||
.payment-form-page__attachment-download {
|
.payment-form-page__attachment-download {
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
}
|
}
|
||||||
|
.payment-form-page__invoice-actions {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
.payment-form-page__attachment-file-name-cell {
|
.payment-form-page__attachment-file-name-cell {
|
||||||
display: block;
|
display: block;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|||||||
@@ -371,6 +371,9 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
.receipt-claim-form-page__form {
|
||||||
|
padding-bottom: 72px;
|
||||||
|
}
|
||||||
.receipt-claim-form-page__form :deep(.el-select),
|
.receipt-claim-form-page__form :deep(.el-select),
|
||||||
.receipt-claim-form-page__form :deep(.el-input-number) {
|
.receipt-claim-form-page__form :deep(.el-input-number) {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|||||||
@@ -71,10 +71,9 @@
|
|||||||
show-overflow-tooltip
|
show-overflow-tooltip
|
||||||
>
|
>
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<el-link v-if="column.link && canClaim(row)" type="primary" @click="openClaim(row)">
|
<el-link v-if="column.link" type="primary" @click="openDetail(row)">
|
||||||
{{ displayValue(row[column.prop]) }}
|
{{ displayValue(row[column.prop]) }}
|
||||||
</el-link>
|
</el-link>
|
||||||
<span v-else-if="column.link">{{ displayValue(row[column.prop]) }}</span>
|
|
||||||
<el-tag
|
<el-tag
|
||||||
v-else-if="column.status"
|
v-else-if="column.status"
|
||||||
:type="statusType(row.claimStatus)"
|
:type="statusType(row.claimStatus)"
|
||||||
@@ -105,6 +104,74 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<el-dialog
|
||||||
|
v-model="detailDialog.visible"
|
||||||
|
title="收款流水详情"
|
||||||
|
width="960px"
|
||||||
|
append-to-body
|
||||||
|
destroy-on-close
|
||||||
|
>
|
||||||
|
<div v-loading="detailDialog.loading">
|
||||||
|
<el-descriptions :column="3" border>
|
||||||
|
<el-descriptions-item label="认领通知单">
|
||||||
|
{{ displayValue(detailDialog.data.receiptNoticeNo) }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="付款人">
|
||||||
|
{{ displayValue(detailDialog.data.payerName) }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="付款金额">
|
||||||
|
{{ formatMoney(detailDialog.data.receiptAmount) }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="交易时间">
|
||||||
|
{{ displayValue(detailDialog.data.transactionTime) }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="对方户名">
|
||||||
|
{{ displayValue(detailDialog.data.counterpartyName) }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="对方开户行">
|
||||||
|
{{ displayValue(detailDialog.data.counterpartyBank) }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="对方账号">
|
||||||
|
{{ displayValue(detailDialog.data.counterpartyAccount) }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="明细流水号">
|
||||||
|
{{ displayValue(detailDialog.data.detailSerialNo) }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="认领状态">
|
||||||
|
<el-tag :type="statusType(detailDialog.data.claimStatus)">
|
||||||
|
{{ displayValue(detailDialog.data.claimStatusName) }}
|
||||||
|
</el-tag>
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="已认领金额">
|
||||||
|
{{ formatMoney(detailDialog.data.claimedAmount) }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="剩余可认领金额">
|
||||||
|
{{ formatMoney(detailDialog.data.remainingAmount) }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="摘要">
|
||||||
|
{{ displayValue(detailDialog.data.summary) }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
</el-descriptions>
|
||||||
|
|
||||||
|
<div class="dialog-section-title receipt-flow-page__detail-title">认领记录</div>
|
||||||
|
<el-table :data="detailDialog.claimRecords" border empty-text="暂无认领记录">
|
||||||
|
<el-table-column type="index" label="序号" width="64" align="center" />
|
||||||
|
<el-table-column prop="createTime" label="认领时间" min-width="170" align="center" />
|
||||||
|
<el-table-column label="认领金额" min-width="130" align="right">
|
||||||
|
<template #default="{ row }">{{ formatMoney(row.operationAmount) }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="operatorName" label="认领人" min-width="120" align="center" />
|
||||||
|
<el-table-column label="状态变化" min-width="170" align="center">
|
||||||
|
<template #default="{ row }">{{ claimStatusChange(row) }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="content" label="认领说明" min-width="220" />
|
||||||
|
</el-table>
|
||||||
|
</div>
|
||||||
|
<template #footer>
|
||||||
|
<el-button @click="detailDialog.visible = false">关闭</el-button>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
</basic-container>
|
</basic-container>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -150,8 +217,10 @@ const formatMockDateTime = date => {
|
|||||||
)}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`;
|
)}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
const createReceiptFlowMockRows = () =>
|
const createReceiptFlowMockRows = () => {
|
||||||
receiptFlowMockTemplates.map((template, index) => {
|
const count = 20 + Math.floor(Math.random() * 31);
|
||||||
|
return Array.from({ length: count }, (_, index) => {
|
||||||
|
const template = receiptFlowMockTemplates[index % receiptFlowMockTemplates.length];
|
||||||
const transactionDate = new Date();
|
const transactionDate = new Date();
|
||||||
transactionDate.setDate(transactionDate.getDate() - Math.floor(Math.random() * 7));
|
transactionDate.setDate(transactionDate.getDate() - Math.floor(Math.random() * 7));
|
||||||
transactionDate.setHours(8 + Math.floor(Math.random() * 10), Math.floor(Math.random() * 60), 0);
|
transactionDate.setHours(8 + Math.floor(Math.random() * 10), Math.floor(Math.random() * 60), 0);
|
||||||
@@ -167,6 +236,7 @@ const createReceiptFlowMockRows = () =>
|
|||||||
sourceUpdatedTime: transactionTime,
|
sourceUpdatedTime: transactionTime,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const emptyQuery = () => ({
|
const emptyQuery = () => ({
|
||||||
receiptNoticeNo: '',
|
receiptNoticeNo: '',
|
||||||
@@ -190,6 +260,7 @@ export default {
|
|||||||
syncing: false,
|
syncing: false,
|
||||||
searchExpanded: false,
|
searchExpanded: false,
|
||||||
page: { current: 1, size: 10, total: 0 },
|
page: { current: 1, size: 10, total: 0 },
|
||||||
|
detailDialog: { visible: false, loading: false, data: {}, claimRecords: [] },
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -250,6 +321,21 @@ export default {
|
|||||||
this.syncing = false;
|
this.syncing = false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
async openDetail(row) {
|
||||||
|
this.detailDialog = {
|
||||||
|
visible: true,
|
||||||
|
loading: true,
|
||||||
|
data: { ...row },
|
||||||
|
claimRecords: [],
|
||||||
|
};
|
||||||
|
try {
|
||||||
|
const data = this.unwrapData(await api.getDetail(row.id));
|
||||||
|
this.detailDialog.data = data || {};
|
||||||
|
this.detailDialog.claimRecords = data?.claimRecords || [];
|
||||||
|
} finally {
|
||||||
|
this.detailDialog.loading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
openClaim(row) {
|
openClaim(row) {
|
||||||
this.$router.push({
|
this.$router.push({
|
||||||
path: '/payment/receipt-flow/form',
|
path: '/payment/receipt-flow/form',
|
||||||
@@ -265,6 +351,11 @@ export default {
|
|||||||
statusType(status) {
|
statusType(status) {
|
||||||
return { claimed: 'success', partial: 'warning', unclaimed: 'info' }[status] || '';
|
return { claimed: 'success', partial: 'warning', unclaimed: 'info' }[status] || '';
|
||||||
},
|
},
|
||||||
|
claimStatusChange(row) {
|
||||||
|
const label = status =>
|
||||||
|
({ claimed: '认领完成', partial: '部分认领', unclaimed: '未认领' }[status] || '-');
|
||||||
|
return `${label(row.fromStatus)} → ${label(row.toStatus)}`;
|
||||||
|
},
|
||||||
displayValue(value) {
|
displayValue(value) {
|
||||||
return value === null || value === undefined || value === '' ? '-' : value;
|
return value === null || value === undefined || value === '' ? '-' : value;
|
||||||
},
|
},
|
||||||
@@ -314,6 +405,9 @@ export default {
|
|||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
padding: 16px 0;
|
padding: 16px 0;
|
||||||
}
|
}
|
||||||
|
.receipt-flow-page__detail-title {
|
||||||
|
margin: 20px 0 12px;
|
||||||
|
}
|
||||||
.receipt-flow-page :deep(.el-table) {
|
.receipt-flow-page :deep(.el-table) {
|
||||||
--el-table-border-color: #eff1f7;
|
--el-table-border-color: #eff1f7;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,7 +60,7 @@
|
|||||||
v-else-if="field.type === 'number' && editable"
|
v-else-if="field.type === 'number' && editable"
|
||||||
v-model="form[field.prop]"
|
v-model="form[field.prop]"
|
||||||
:min="0.000001"
|
:min="0.000001"
|
||||||
:precision="6"
|
:precision="2"
|
||||||
:controls="false"
|
:controls="false"
|
||||||
/>
|
/>
|
||||||
<span v-else-if="field.money">{{ formatMoney(form[field.prop]) }}</span>
|
<span v-else-if="field.money">{{ formatMoney(form[field.prop]) }}</span>
|
||||||
@@ -153,8 +153,7 @@
|
|||||||
<span
|
<span
|
||||||
v-else-if="isSummaryMoney(column.prop)"
|
v-else-if="isSummaryMoney(column.prop)"
|
||||||
:class="{
|
:class="{
|
||||||
'formal-editor__negative-amount':
|
'formal-editor__negative-amount': isNegativeAdjustedAmount(row, column.prop),
|
||||||
column.prop === 'adjustAmount' && Number(row[column.prop] || 0) < 0,
|
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
{{ formatMoney(row[column.prop]) }}
|
{{ formatMoney(row[column.prop]) }}
|
||||||
@@ -224,7 +223,10 @@
|
|||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<span v-if="column.money">{{ formatMoney(row[column.prop]) }}</span>
|
<span v-if="column.money">{{ formatMoney(row[column.prop]) }}</span>
|
||||||
<span v-else-if="column.prop === 'transportQuantity'">
|
<span v-else-if="column.prop === 'transportQuantity'">
|
||||||
{{ formatQuantity(row[column.prop]) }}
|
{{ formatDetailTransportQuantity(row[column.prop]) }}
|
||||||
|
</span>
|
||||||
|
<span v-else-if="column.prop === 'mileage'">
|
||||||
|
{{ formatDetailMileage(row[column.prop]) }}
|
||||||
</span>
|
</span>
|
||||||
<span v-else-if="column.prop === 'transportType'">
|
<span v-else-if="column.prop === 'transportType'">
|
||||||
{{ transportTypeName(row[column.prop]) }}
|
{{ transportTypeName(row[column.prop]) }}
|
||||||
@@ -235,11 +237,9 @@
|
|||||||
<el-table-column v-if="editable" label="操作" width="150" fixed="right" align="center">
|
<el-table-column v-if="editable" label="操作" width="150" fixed="right" align="center">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<div class="formal-editor__links">
|
<div class="formal-editor__links">
|
||||||
<el-link v-if="row.formalSettlementId" type="primary" @click="openAdjustDialog(row)"
|
<el-link type="primary" @click="openAdjustDialog(row)">调整</el-link>
|
||||||
>调整</el-link
|
|
||||||
>
|
|
||||||
<el-link v-if="!row.sourcePreSettlementId" type="danger" @click="removeDetail(row)"
|
<el-link v-if="!row.sourcePreSettlementId" type="danger" @click="removeDetail(row)"
|
||||||
>删除</el-link
|
>踢出</el-link
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -254,7 +254,13 @@
|
|||||||
{{ missingAttachmentTypeText }}
|
{{ missingAttachmentTypeText }}
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
<el-table :data="attachments" border>
|
<div class="formal-editor__attachment-actions">
|
||||||
|
<el-button type="primary" :disabled="!attachments.length" @click="downloadAttachments">
|
||||||
|
批量下载
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
<el-table :data="attachments" border @selection-change="handleAttachmentSelectionChange">
|
||||||
|
<el-table-column type="selection" width="55" align="center" />
|
||||||
<el-table-column type="index" label="序号" width="64" align="center" />
|
<el-table-column type="index" label="序号" width="64" align="center" />
|
||||||
<el-table-column label="类型" min-width="180" align="center">
|
<el-table-column label="类型" min-width="180" align="center">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
@@ -323,7 +329,7 @@
|
|||||||
</section-card>
|
</section-card>
|
||||||
|
|
||||||
<section-card title="发票信息">
|
<section-card title="发票信息">
|
||||||
<div v-if="editable" class="formal-editor__invoice-claim">
|
<div class="formal-editor__invoice-claim">
|
||||||
<el-form inline label-position="right" label-width="auto" @submit.prevent>
|
<el-form inline label-position="right" label-width="auto" @submit.prevent>
|
||||||
<el-form-item label="发票号码">
|
<el-form-item label="发票号码">
|
||||||
<el-input
|
<el-input
|
||||||
@@ -394,17 +400,58 @@
|
|||||||
<div class="formal-editor__links">
|
<div class="formal-editor__links">
|
||||||
<el-link type="primary" @click="viewInvoiceAttachment(row)">查看</el-link>
|
<el-link type="primary" @click="viewInvoiceAttachment(row)">查看</el-link>
|
||||||
<el-link type="primary" @click="downloadInvoiceAttachment(row)">下载</el-link>
|
<el-link type="primary" @click="downloadInvoiceAttachment(row)">下载</el-link>
|
||||||
<el-link v-if="editable" type="danger" @click="removeInvoice($index)">
|
<el-link type="danger" @click="removeInvoice($index)">删除</el-link>
|
||||||
删除
|
|
||||||
</el-link>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
</section-card>
|
</section-card>
|
||||||
|
|
||||||
<section-card v-if="readonly" title="付款信息">
|
<section-card
|
||||||
<el-table :data="paymentApplications" border>
|
v-if="readonly"
|
||||||
|
:title="form.settlementType === 'receivable' ? '收款信息' : '付款信息'"
|
||||||
|
>
|
||||||
|
<el-table
|
||||||
|
v-if="form.settlementType === 'receivable'"
|
||||||
|
:data="receiptClaims"
|
||||||
|
border
|
||||||
|
empty-text="暂无收款认领数据"
|
||||||
|
>
|
||||||
|
<el-table-column type="index" label="序号" width="64" align="center" />
|
||||||
|
<el-table-column
|
||||||
|
prop="receiptNoticeNo"
|
||||||
|
label="认领通知单"
|
||||||
|
min-width="160"
|
||||||
|
align="center"
|
||||||
|
/>
|
||||||
|
<el-table-column prop="payerName" label="付款人" min-width="150" align="center">
|
||||||
|
<template #default="{ row }">{{ displayValue(row.payerName) }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="receiptAmount" label="收款金额" min-width="130" align="center">
|
||||||
|
<template #default="{ row }">{{ formatMoney(row.receiptAmount) }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column
|
||||||
|
prop="allocatedReceiptAmount"
|
||||||
|
label="本次认领金额"
|
||||||
|
min-width="145"
|
||||||
|
align="center"
|
||||||
|
>
|
||||||
|
<template #default="{ row }">{{ formatMoney(row.allocatedReceiptAmount) }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="transactionTime" label="交易时间" min-width="170" align="center">
|
||||||
|
<template #default="{ row }">{{ displayValue(row.transactionTime) }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="claimerName" label="认领人" min-width="120" align="center">
|
||||||
|
<template #default="{ row }">{{ displayValue(row.claimerName) }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="claimDate" label="认领日期" min-width="130" align="center">
|
||||||
|
<template #default="{ row }">{{ displayValue(row.claimDate) }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="claimStatusName" label="认领状态" min-width="120" align="center">
|
||||||
|
<template #default="{ row }">{{ displayValue(row.claimStatusName) }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<el-table v-else :data="paymentApplications" border>
|
||||||
<el-table-column type="index" label="序号" width="64" align="center" />
|
<el-table-column type="index" label="序号" width="64" align="center" />
|
||||||
<el-table-column prop="paymentTypeName" label="付款类型" min-width="120" align="center">
|
<el-table-column prop="paymentTypeName" label="付款类型" min-width="120" align="center">
|
||||||
<template #default="{ row }">{{ displayValue(row.paymentTypeName) }}</template>
|
<template #default="{ row }">{{ displayValue(row.paymentTypeName) }}</template>
|
||||||
@@ -626,6 +673,39 @@
|
|||||||
>
|
>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
|
||||||
|
<el-dialog
|
||||||
|
v-model="detailAmountAdjust.visible"
|
||||||
|
title="结算明细调整"
|
||||||
|
width="520px"
|
||||||
|
append-to-body
|
||||||
|
>
|
||||||
|
<el-form
|
||||||
|
:model="detailAmountAdjust"
|
||||||
|
label-position="right"
|
||||||
|
label-width="auto"
|
||||||
|
class="formal-editor__amount-adjust"
|
||||||
|
>
|
||||||
|
<el-form-item label="原金额">
|
||||||
|
<span>{{ formatMoney(detailAmountAdjust.originalAmount) }}</span>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="调整金额" required>
|
||||||
|
<el-input-number
|
||||||
|
v-model="detailAmountAdjust.adjustAmount"
|
||||||
|
:precision="2"
|
||||||
|
:step="0.01"
|
||||||
|
:controls="false"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="结算金额">
|
||||||
|
<span>{{ formatMoney(detailAmountAdjustSettlementAmount) }}</span>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<el-button @click="detailAmountAdjust.visible = false">取消</el-button>
|
||||||
|
<el-button type="primary" @click="confirmDetailAmountAdjustment">提交</el-button>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
<el-dialog v-model="adjust.visible" title="结算明细调整" width="92%" append-to-body>
|
<el-dialog v-model="adjust.visible" title="结算明细调整" width="92%" append-to-body>
|
||||||
<el-table v-loading="adjust.loading" :data="adjust.rows" border>
|
<el-table v-loading="adjust.loading" :data="adjust.rows" border>
|
||||||
<el-table-column type="index" label="序号" width="64" align="center" />
|
<el-table-column type="index" label="序号" width="64" align="center" />
|
||||||
@@ -671,14 +751,6 @@
|
|||||||
:precision="2"
|
:precision="2"
|
||||||
:controls="false" /></template
|
:controls="false" /></template
|
||||||
></el-table-column>
|
></el-table-column>
|
||||||
<el-table-column label="结算金额(不含税)" min-width="180" align="center"
|
|
||||||
><template #default="{ row }"
|
|
||||||
><el-input-number
|
|
||||||
v-model="row.settlementAmountNoTax"
|
|
||||||
:min="0"
|
|
||||||
:precision="2"
|
|
||||||
:controls="false" /></template
|
|
||||||
></el-table-column>
|
|
||||||
<el-table-column label="备注" min-width="180" align="center"
|
<el-table-column label="备注" min-width="180" align="center"
|
||||||
><template #default="{ row }"><el-input v-model="row.remark" maxlength="200" /></template
|
><template #default="{ row }"><el-input v-model="row.remark" maxlength="200" /></template
|
||||||
></el-table-column>
|
></el-table-column>
|
||||||
@@ -708,6 +780,7 @@ import {
|
|||||||
getDetailFees,
|
getDetailFees,
|
||||||
getFeeOptions,
|
getFeeOptions,
|
||||||
getNextNo,
|
getNextNo,
|
||||||
|
getReceiptClaims,
|
||||||
save,
|
save,
|
||||||
} from '@/api/settlement/formalSettlement';
|
} from '@/api/settlement/formalSettlement';
|
||||||
import { getDetail as getPreSettlementDetail } from '@/api/settlement/preSettlement';
|
import { getDetail as getPreSettlementDetail } from '@/api/settlement/preSettlement';
|
||||||
@@ -723,6 +796,7 @@ import {
|
|||||||
summaryColumns,
|
summaryColumns,
|
||||||
} from '@/option/settlement/formalSettlementTable';
|
} from '@/option/settlement/formalSettlementTable';
|
||||||
import { getDictionary } from '@/api/system/dictbiz';
|
import { getDictionary } from '@/api/system/dictbiz';
|
||||||
|
import { h } from 'vue';
|
||||||
import { mapGetters } from 'vuex';
|
import { mapGetters } from 'vuex';
|
||||||
import { downloadFileByUrl } from '@/utils/util';
|
import { downloadFileByUrl } from '@/utils/util';
|
||||||
import * as XLSX from 'xlsx';
|
import * as XLSX from 'xlsx';
|
||||||
@@ -749,9 +823,11 @@ export default {
|
|||||||
details: [],
|
details: [],
|
||||||
summaryFees: [],
|
summaryFees: [],
|
||||||
paymentApplications: [],
|
paymentApplications: [],
|
||||||
|
receiptClaims: [],
|
||||||
adjustments: [],
|
adjustments: [],
|
||||||
changeRecords: [],
|
changeRecords: [],
|
||||||
attachments: [],
|
attachments: [],
|
||||||
|
selectedAttachmentRows: [],
|
||||||
invoices: [],
|
invoices: [],
|
||||||
invoiceClaimNo: '',
|
invoiceClaimNo: '',
|
||||||
invoiceClaimLoading: false,
|
invoiceClaimLoading: false,
|
||||||
@@ -815,6 +891,12 @@ export default {
|
|||||||
reason: '',
|
reason: '',
|
||||||
rows: [],
|
rows: [],
|
||||||
},
|
},
|
||||||
|
detailAmountAdjust: {
|
||||||
|
visible: false,
|
||||||
|
row: null,
|
||||||
|
originalAmount: 0,
|
||||||
|
adjustAmount: 0,
|
||||||
|
},
|
||||||
detailCollapsed: false,
|
detailCollapsed: false,
|
||||||
detailQuery: {
|
detailQuery: {
|
||||||
documentNo: '',
|
documentNo: '',
|
||||||
@@ -865,6 +947,14 @@ export default {
|
|||||||
summaryTotal() {
|
summaryTotal() {
|
||||||
return this.summaryFees.reduce((total, row) => total + Number(row.settlementAmount || 0), 0);
|
return this.summaryFees.reduce((total, row) => total + Number(row.settlementAmount || 0), 0);
|
||||||
},
|
},
|
||||||
|
detailAmountAdjustSettlementAmount() {
|
||||||
|
return Number(
|
||||||
|
(
|
||||||
|
Number(this.detailAmountAdjust.originalAmount || 0) +
|
||||||
|
Number(this.detailAmountAdjust.adjustAmount || 0)
|
||||||
|
).toFixed(2)
|
||||||
|
);
|
||||||
|
},
|
||||||
filteredDetails() {
|
filteredDetails() {
|
||||||
return this.details.filter(row =>
|
return this.details.filter(row =>
|
||||||
Object.entries(this.appliedDetailQuery).every(([field, keyword]) => {
|
Object.entries(this.appliedDetailQuery).every(([field, keyword]) => {
|
||||||
@@ -901,17 +991,32 @@ export default {
|
|||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
async initialize() {
|
async initialize() {
|
||||||
|
const newRecordAudit = this.recordId
|
||||||
|
? null
|
||||||
|
: {
|
||||||
|
createUserName: this.userInfo?.realName || this.userInfo?.userName || '',
|
||||||
|
createTime: this.$dayjs().format('YYYY-MM-DD HH:mm:ss'),
|
||||||
|
};
|
||||||
this.form = createFormalSettlementForm();
|
this.form = createFormalSettlementForm();
|
||||||
|
if (newRecordAudit) Object.assign(this.form, newRecordAudit);
|
||||||
this.sources = [];
|
this.sources = [];
|
||||||
this.details = [];
|
this.details = [];
|
||||||
this.summaryFees = [];
|
this.summaryFees = [];
|
||||||
this.paymentApplications = [];
|
this.paymentApplications = [];
|
||||||
|
this.receiptClaims = [];
|
||||||
this.adjustments = [];
|
this.adjustments = [];
|
||||||
this.changeRecords = [];
|
this.changeRecords = [];
|
||||||
this.attachments = [];
|
this.attachments = [];
|
||||||
|
this.selectedAttachmentRows = [];
|
||||||
this.invoices = [];
|
this.invoices = [];
|
||||||
this.invoiceClaimNo = '';
|
this.invoiceClaimNo = '';
|
||||||
this.attachmentUploadFiles = [];
|
this.attachmentUploadFiles = [];
|
||||||
|
this.detailAmountAdjust = {
|
||||||
|
visible: false,
|
||||||
|
row: null,
|
||||||
|
originalAmount: 0,
|
||||||
|
adjustAmount: 0,
|
||||||
|
};
|
||||||
this.detailCollapsed = false;
|
this.detailCollapsed = false;
|
||||||
this.resetDetailQuery(false);
|
this.resetDetailQuery(false);
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
@@ -923,6 +1028,7 @@ export default {
|
|||||||
if (!this.recordId) {
|
if (!this.recordId) {
|
||||||
this.form.exchangeRateDate = this.$dayjs().format('YYYY-MM-DD');
|
this.form.exchangeRateDate = this.$dayjs().format('YYYY-MM-DD');
|
||||||
if (this.initialData) await this.applyInitialData();
|
if (this.initialData) await this.applyInitialData();
|
||||||
|
Object.assign(this.form, newRecordAudit);
|
||||||
await this.refreshFormalSettlementNo();
|
await this.refreshFormalSettlementNo();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -942,9 +1048,13 @@ export default {
|
|||||||
this.details = data.details || [];
|
this.details = data.details || [];
|
||||||
this.summaryFees = data.summaryFees || [];
|
this.summaryFees = data.summaryFees || [];
|
||||||
this.paymentApplications = data.paymentApplications || [];
|
this.paymentApplications = data.paymentApplications || [];
|
||||||
|
if (this.readonly && this.form.settlementType === 'receivable') {
|
||||||
|
this.receiptClaims = this.unwrapData(await getReceiptClaims(this.recordId)) || [];
|
||||||
|
}
|
||||||
this.adjustments = data.adjustments || [];
|
this.adjustments = data.adjustments || [];
|
||||||
this.changeRecords = data.changeRecords || [];
|
this.changeRecords = data.changeRecords || [];
|
||||||
this.attachments = this.parseAttachments(data.attachmentsJson);
|
this.attachments = this.parseAttachments(data.attachmentsJson);
|
||||||
|
this.selectedAttachmentRows = [];
|
||||||
this.invoices = data.invoices || [];
|
this.invoices = data.invoices || [];
|
||||||
this.sortAttachments();
|
this.sortAttachments();
|
||||||
this.contracts = this.allContracts.filter(
|
this.contracts = this.allContracts.filter(
|
||||||
@@ -1272,7 +1382,8 @@ export default {
|
|||||||
this.buildSummaryFeesFromDetails();
|
this.buildSummaryFeesFromDetails();
|
||||||
this.detailCandidate.visible = false;
|
this.detailCandidate.visible = false;
|
||||||
},
|
},
|
||||||
removeDetail(row) {
|
async removeDetail(row) {
|
||||||
|
await this.$confirm('确认将该明细踢出正式结算单?', '提示', { type: 'warning' });
|
||||||
const index = this.details.indexOf(row);
|
const index = this.details.indexOf(row);
|
||||||
if (index < 0) return;
|
if (index < 0) return;
|
||||||
this.details.splice(index, 1);
|
this.details.splice(index, 1);
|
||||||
@@ -1309,16 +1420,26 @@ export default {
|
|||||||
isSummaryMoney(prop) {
|
isSummaryMoney(prop) {
|
||||||
return ['originalAmount', 'adjustAmount', 'settlementAmount'].includes(prop);
|
return ['originalAmount', 'adjustAmount', 'settlementAmount'].includes(prop);
|
||||||
},
|
},
|
||||||
|
isNegativeAdjustedAmount(row, prop) {
|
||||||
|
return (
|
||||||
|
Number(row.adjustAmount || 0) < 0 && ['adjustAmount', 'settlementAmount'].includes(prop)
|
||||||
|
);
|
||||||
|
},
|
||||||
recalculateSummaryRow(row) {
|
recalculateSummaryRow(row) {
|
||||||
row.settlementAmount = Number(row.originalAmount || 0) + Number(row.adjustAmount || 0);
|
row.settlementAmount = Number(row.originalAmount || 0) + Number(row.adjustAmount || 0);
|
||||||
},
|
},
|
||||||
getSummarySums({ columns, data }) {
|
getSummarySums({ columns, data }) {
|
||||||
const sumProps = ['originalAmount', 'adjustAmount', 'settlementAmount'];
|
const sumProps = ['originalAmount', 'adjustAmount', 'settlementAmount'];
|
||||||
|
const hasNegativeAdjustment = data.some(row => Number(row.adjustAmount || 0) < 0);
|
||||||
return columns.map((column, index) => {
|
return columns.map((column, index) => {
|
||||||
if (index === 0) return '合计';
|
if (index === 0) return '合计';
|
||||||
if (!sumProps.includes(column.property)) return '';
|
if (!sumProps.includes(column.property)) return '';
|
||||||
const total = data.reduce((sum, row) => sum + Number(row[column.property] || 0), 0);
|
const total = data.reduce((sum, row) => sum + Number(row[column.property] || 0), 0);
|
||||||
return this.formatMoney(total);
|
const totalText = this.formatMoney(total);
|
||||||
|
if (column.property === 'settlementAmount' && hasNegativeAdjustment) {
|
||||||
|
return h('span', { style: { color: 'var(--el-color-danger)' } }, totalText);
|
||||||
|
}
|
||||||
|
return totalText;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
buildSummaryFeesFromDetails() {
|
buildSummaryFeesFromDetails() {
|
||||||
@@ -1406,6 +1527,20 @@ export default {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
async openAdjustDialog(row) {
|
async openAdjustDialog(row) {
|
||||||
|
if (!row.formalSettlementId) {
|
||||||
|
const originalAmount = Number(
|
||||||
|
row.originalAmount ??
|
||||||
|
row.totalAmount ??
|
||||||
|
Number(row.settlementAmountTax || 0) - Number(row.adjustAmount || 0)
|
||||||
|
);
|
||||||
|
this.detailAmountAdjust = {
|
||||||
|
visible: true,
|
||||||
|
row,
|
||||||
|
originalAmount: Number(originalAmount.toFixed(2)),
|
||||||
|
adjustAmount: Number(row.adjustAmount || 0),
|
||||||
|
};
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.adjust = {
|
this.adjust = {
|
||||||
visible: true,
|
visible: true,
|
||||||
loading: true,
|
loading: true,
|
||||||
@@ -1425,6 +1560,20 @@ export default {
|
|||||||
this.adjust.loading = false;
|
this.adjust.loading = false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
confirmDetailAmountAdjustment() {
|
||||||
|
const settlementAmount = this.detailAmountAdjustSettlementAmount;
|
||||||
|
if (!Number.isFinite(settlementAmount) || settlementAmount < 0) {
|
||||||
|
return this.$message.warning('调整后的结算金额不能小于0');
|
||||||
|
}
|
||||||
|
const row = this.detailAmountAdjust.row;
|
||||||
|
if (!row) return;
|
||||||
|
row.originalAmount = this.detailAmountAdjust.originalAmount;
|
||||||
|
row.adjustAmount = Number(this.detailAmountAdjust.adjustAmount || 0);
|
||||||
|
row.settlementAmountTax = settlementAmount;
|
||||||
|
row.pendingDetailAdjustment = true;
|
||||||
|
this.buildSummaryFeesFromDetails();
|
||||||
|
this.detailAmountAdjust.visible = false;
|
||||||
|
},
|
||||||
async saveAdjustment() {
|
async saveAdjustment() {
|
||||||
if (!this.adjust.reason.trim()) return this.$message.warning('请输入调整原因');
|
if (!this.adjust.reason.trim()) return this.$message.warning('请输入调整原因');
|
||||||
this.adjust.saving = true;
|
this.adjust.saving = true;
|
||||||
@@ -1586,6 +1735,14 @@ export default {
|
|||||||
settlementType: this.form.settlementType,
|
settlementType: this.form.settlementType,
|
||||||
sourcePreSettlementIds: this.form.sourcePreSettlementIds,
|
sourcePreSettlementIds: this.form.sourcePreSettlementIds,
|
||||||
sourceDetailIds: this.form.sourceDetailIds,
|
sourceDetailIds: this.form.sourceDetailIds,
|
||||||
|
detailAdjustments: this.details
|
||||||
|
.filter(row => row.pendingDetailAdjustment)
|
||||||
|
.map(row => ({
|
||||||
|
sourcePreSettlementDetailId:
|
||||||
|
row.sourcePreSettlementDetailId || (row.sourcePreSettlementId ? row.id : undefined),
|
||||||
|
sourceDetailId: row.sourcePreSettlementId ? undefined : row.sourceDetailId || row.id,
|
||||||
|
adjustAmount: Number(row.adjustAmount || 0),
|
||||||
|
})),
|
||||||
exchangeRateDate: this.form.exchangeRateDate,
|
exchangeRateDate: this.form.exchangeRateDate,
|
||||||
exchangeRate: this.form.exchangeRate,
|
exchangeRate: this.form.exchangeRate,
|
||||||
summaryFees: this.summaryFees
|
summaryFees: this.summaryFees
|
||||||
@@ -1639,6 +1796,12 @@ export default {
|
|||||||
const quantity = Number(value);
|
const quantity = Number(value);
|
||||||
return Number.isFinite(quantity) ? quantity.toFixed(2) : '-';
|
return Number.isFinite(quantity) ? quantity.toFixed(2) : '-';
|
||||||
},
|
},
|
||||||
|
formatDetailTransportQuantity(value) {
|
||||||
|
return this.readonly && Number(value) === -1 ? '-' : this.formatQuantity(value);
|
||||||
|
},
|
||||||
|
formatDetailMileage(value) {
|
||||||
|
return this.readonly && Number(value) === -1 ? '-' : this.displayValue(value);
|
||||||
|
},
|
||||||
async loadTransportTypeOptions() {
|
async loadTransportTypeOptions() {
|
||||||
const { data } = await getDictionary({ code: 'transport_type' });
|
const { data } = await getDictionary({ code: 'transport_type' });
|
||||||
this.transportTypeOptions = data?.data || [];
|
this.transportTypeOptions = data?.data || [];
|
||||||
@@ -1736,12 +1899,46 @@ export default {
|
|||||||
});
|
});
|
||||||
this.sortAttachments();
|
this.sortAttachments();
|
||||||
},
|
},
|
||||||
|
handleAttachmentSelectionChange(rows) {
|
||||||
|
this.selectedAttachmentRows = rows || [];
|
||||||
|
},
|
||||||
removeAttachment(index) {
|
removeAttachment(index) {
|
||||||
this.attachments.splice(index, 1);
|
this.attachments.splice(index, 1);
|
||||||
|
this.selectedAttachmentRows = this.selectedAttachmentRows.filter(file =>
|
||||||
|
this.attachments.includes(file)
|
||||||
|
);
|
||||||
},
|
},
|
||||||
getAttachmentUrl(file = {}) {
|
getAttachmentUrl(file = {}) {
|
||||||
return file.url || file.link || file.src || file.domain || '';
|
return file.url || file.link || file.src || file.domain || '';
|
||||||
},
|
},
|
||||||
|
getAttachmentName(file = {}) {
|
||||||
|
return (
|
||||||
|
file.originalName || file.name || this.getAttachmentFileName(this.getAttachmentUrl(file))
|
||||||
|
);
|
||||||
|
},
|
||||||
|
downloadAttachment(file) {
|
||||||
|
const url = this.getAttachmentUrl(file);
|
||||||
|
if (!url) {
|
||||||
|
this.$message.warning('附件地址为空,无法下载');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
downloadFileByUrl(url, this.getAttachmentName(file) || '附件');
|
||||||
|
},
|
||||||
|
downloadAttachments() {
|
||||||
|
const files = this.selectedAttachmentRows.length
|
||||||
|
? this.selectedAttachmentRows
|
||||||
|
: this.attachments;
|
||||||
|
const downloadableFiles = files.filter(file => this.getAttachmentUrl(file));
|
||||||
|
if (!downloadableFiles.length) {
|
||||||
|
this.$message.warning(
|
||||||
|
this.selectedAttachmentRows.length ? '所选附件无法下载' : '暂无可下载附件'
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
downloadableFiles.forEach((file, index) => {
|
||||||
|
window.setTimeout(() => this.downloadAttachment(file), index * 200);
|
||||||
|
});
|
||||||
|
},
|
||||||
previewAttachment(file) {
|
previewAttachment(file) {
|
||||||
const url = this.getAttachmentUrl(file);
|
const url = this.getAttachmentUrl(file);
|
||||||
if (!url) return this.$message.warning('附件地址为空,无法预览');
|
if (!url) return this.$message.warning('附件地址为空,无法预览');
|
||||||
@@ -1844,7 +2041,7 @@ export default {
|
|||||||
}
|
}
|
||||||
.formal-editor__detail-filter-actions {
|
.formal-editor__detail-filter-actions {
|
||||||
grid-column: 1 / -1;
|
grid-column: 1 / -1;
|
||||||
justify-self: start;
|
justify-self: end;
|
||||||
}
|
}
|
||||||
.formal-editor__page-actions {
|
.formal-editor__page-actions {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -1892,12 +2089,20 @@ export default {
|
|||||||
.formal-editor__adjust-reason {
|
.formal-editor__adjust-reason {
|
||||||
margin-top: 16px;
|
margin-top: 16px;
|
||||||
}
|
}
|
||||||
|
.formal-editor__amount-adjust :deep(.el-input-number) {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
.formal-editor__attachment-missing {
|
.formal-editor__attachment-missing {
|
||||||
margin-left: 12px;
|
margin-left: 12px;
|
||||||
color: var(--el-color-danger);
|
color: var(--el-color-danger);
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
}
|
}
|
||||||
|
.formal-editor__attachment-actions {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
.formal-editor__attachment-upload {
|
.formal-editor__attachment-upload {
|
||||||
margin-top: 12px;
|
margin-top: 12px;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -77,9 +77,10 @@
|
|||||||
>{{ displayValue(row[column.prop]) }}</el-tag
|
>{{ displayValue(row[column.prop]) }}</el-tag
|
||||||
>
|
>
|
||||||
<span v-else-if="column.money">{{ formatMoney(row[column.prop], row.currency) }}</span>
|
<span v-else-if="column.money">{{ formatMoney(row[column.prop], row.currency) }}</span>
|
||||||
<span v-else-if="column.prop === 'invoiceStatusName'">{{
|
<span
|
||||||
invoiceName(row.invoiceStatus)
|
v-else-if="column.prop === 'invoiceStatusName'"
|
||||||
}}</span>
|
:class="['fs-table-panel__invoice-status', `is-${row.invoiceStatus || 'unreceived'}`]"
|
||||||
|
>{{ invoiceName(row.invoiceStatus, row.settlementType) }}</span>
|
||||||
<span v-else-if="column.prop === 'paymentStatusName'">{{
|
<span v-else-if="column.prop === 'paymentStatusName'">{{
|
||||||
paymentName(row.paymentStatus)
|
paymentName(row.paymentStatus)
|
||||||
}}</span>
|
}}</span>
|
||||||
@@ -120,7 +121,7 @@
|
|||||||
<script>
|
<script>
|
||||||
import { Refresh } from '@element-plus/icons-vue';
|
import { Refresh } from '@element-plus/icons-vue';
|
||||||
import {
|
import {
|
||||||
invoiceStatusOptions,
|
invoiceStatusName,
|
||||||
kingdeeSyncStatusOptions,
|
kingdeeSyncStatusOptions,
|
||||||
paymentStatusOptions,
|
paymentStatusOptions,
|
||||||
} from '@/option/settlement/formalSettlementSearch';
|
} from '@/option/settlement/formalSettlementSearch';
|
||||||
@@ -210,8 +211,8 @@ export default {
|
|||||||
''
|
''
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
invoiceName(value) {
|
invoiceName(value, settlementType) {
|
||||||
return invoiceStatusOptions.find(item => item.value === value)?.label || value || '-';
|
return invoiceStatusName(value, settlementType);
|
||||||
},
|
},
|
||||||
paymentName(value) {
|
paymentName(value) {
|
||||||
return paymentStatusOptions.find(item => item.value === value)?.label || value || '-';
|
return paymentStatusOptions.find(item => item.value === value)?.label || value || '-';
|
||||||
@@ -247,6 +248,17 @@ export default {
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
}
|
}
|
||||||
|
.fs-table-panel__invoice-status {
|
||||||
|
&.is-unreceived {
|
||||||
|
color: #f56c6c;
|
||||||
|
}
|
||||||
|
&.is-partial {
|
||||||
|
color: #e6a23c;
|
||||||
|
}
|
||||||
|
&.is-completed {
|
||||||
|
color: #67c23a;
|
||||||
|
}
|
||||||
|
}
|
||||||
.fs-table-panel__pagination {
|
.fs-table-panel__pagination {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
|
|||||||
@@ -51,7 +51,7 @@
|
|||||||
v-else-if="field.type === 'number'"
|
v-else-if="field.type === 'number'"
|
||||||
v-model="form[field.prop]"
|
v-model="form[field.prop]"
|
||||||
:min="0"
|
:min="0"
|
||||||
:precision="6"
|
:precision="2"
|
||||||
:controls="false"
|
:controls="false"
|
||||||
:disabled="!editable || form.currency === 'RMB'"
|
:disabled="!editable || form.currency === 'RMB'"
|
||||||
@change="recalculateLocalAmount"
|
@change="recalculateLocalAmount"
|
||||||
@@ -104,10 +104,10 @@
|
|||||||
@change="handleManualFeeTypeChange(row)"
|
@change="handleManualFeeTypeChange(row)"
|
||||||
>
|
>
|
||||||
<el-option
|
<el-option
|
||||||
v-for="item in feeOptions"
|
v-for="item in feeCategoryOptions"
|
||||||
:key="item.feeType"
|
:key="item.id || item.dictValue"
|
||||||
:label="item.feeTypeName || item.feeType"
|
:label="item.dictKey || item.dictValue"
|
||||||
:value="item.feeType"
|
:value="item.dictValue"
|
||||||
/>
|
/>
|
||||||
</el-select>
|
</el-select>
|
||||||
<el-select
|
<el-select
|
||||||
@@ -899,14 +899,25 @@ export default {
|
|||||||
currency: first.currency || 'RMB',
|
currency: first.currency || 'RMB',
|
||||||
localCurrency: first.localCurrency || 'RMB',
|
localCurrency: first.localCurrency || 'RMB',
|
||||||
});
|
});
|
||||||
this.details = rows.map(row => ({
|
this.details = rows.map(row => this.normalizeSourceDetail(row));
|
||||||
|
this.buildSummaryFeesFromDetails();
|
||||||
|
},
|
||||||
|
normalizeSourceDetail(row) {
|
||||||
|
const originalAmount =
|
||||||
|
row.originalAmount ?? row.totalAmount ?? row.afterAmount ?? row.settlementAmount ?? 0;
|
||||||
|
return {
|
||||||
...row,
|
...row,
|
||||||
sourceDetailId: row.sourceDetailId || row.id,
|
sourceDetailId: row.sourceDetailId || row.id,
|
||||||
|
originalAmount,
|
||||||
|
adjustAmount: row.adjustAmount ?? 0,
|
||||||
settlementAmountTax:
|
settlementAmountTax:
|
||||||
row.settlementAmountTax ?? row.totalAmount ?? row.afterAmount ?? row.settlementAmount,
|
row.settlementAmountTax ??
|
||||||
|
row.totalAmount ??
|
||||||
|
row.afterAmount ??
|
||||||
|
row.settlementAmount ??
|
||||||
|
originalAmount,
|
||||||
feeItemsJson: row.feeItemsJson || JSON.stringify(row.feeItems || {}),
|
feeItemsJson: row.feeItemsJson || JSON.stringify(row.feeItems || {}),
|
||||||
}));
|
};
|
||||||
this.buildSummaryFeesFromDetails();
|
|
||||||
},
|
},
|
||||||
resetEditor() {
|
resetEditor() {
|
||||||
this.form = emptyPreSettlementForm();
|
this.form = emptyPreSettlementForm();
|
||||||
@@ -1054,7 +1065,7 @@ export default {
|
|||||||
sourceDetailIds: this.details.map(row => row.sourceDetailId || row.id),
|
sourceDetailIds: this.details.map(row => row.sourceDetailId || row.id),
|
||||||
summaryFees: this.summaryFees.map(row => ({
|
summaryFees: this.summaryFees.map(row => ({
|
||||||
id: row.id || undefined,
|
id: row.id || undefined,
|
||||||
feeType: row.feeType || '',
|
feeType: this.feeCategoryValue(row.feeType),
|
||||||
feeItem: row.feeItem,
|
feeItem: row.feeItem,
|
||||||
originalAmount: Number(row.originalAmount || 0),
|
originalAmount: Number(row.originalAmount || 0),
|
||||||
adjustAmount: Number(row.adjustAmount || 0),
|
adjustAmount: Number(row.adjustAmount || 0),
|
||||||
@@ -1079,14 +1090,25 @@ export default {
|
|||||||
if (!this.manualFeeItems(row.feeType).includes(row.feeItem)) row.feeItem = '';
|
if (!this.manualFeeItems(row.feeType).includes(row.feeItem)) row.feeItem = '';
|
||||||
},
|
},
|
||||||
manualFeeItems(feeType) {
|
manualFeeItems(feeType) {
|
||||||
return this.feeOptions.find(item => item.feeType === feeType)?.feeItems || [];
|
return (
|
||||||
|
this.feeOptions.find(
|
||||||
|
item => this.feeCategoryValue(item.feeType) === this.feeCategoryValue(feeType)
|
||||||
|
)?.feeItems || []
|
||||||
|
);
|
||||||
|
},
|
||||||
|
feeCategoryValue(value) {
|
||||||
|
if (value === undefined || value === null || value === '') return '';
|
||||||
|
const option = this.feeCategoryOptions.find(
|
||||||
|
item => String(item.dictKey) === String(value) || String(item.dictValue) === String(value)
|
||||||
|
);
|
||||||
|
return option?.dictValue || value;
|
||||||
},
|
},
|
||||||
feeCategoryName(value) {
|
feeCategoryName(value) {
|
||||||
if (value === undefined || value === null || value === '') return '';
|
if (value === undefined || value === null || value === '') return '';
|
||||||
const option = this.feeCategoryOptions.find(
|
const option = this.feeCategoryOptions.find(
|
||||||
item => String(item.dictKey) === String(value) || String(item.dictValue) === String(value)
|
item => String(item.dictKey) === String(value) || String(item.dictValue) === String(value)
|
||||||
);
|
);
|
||||||
return option?.dictValue || value;
|
return option?.dictKey || value;
|
||||||
},
|
},
|
||||||
transportTypeName(value) {
|
transportTypeName(value) {
|
||||||
if (value === undefined || value === null || value === '') return '';
|
if (value === undefined || value === null || value === '') return '';
|
||||||
@@ -1124,7 +1146,7 @@ export default {
|
|||||||
const feeOption = this.feeOptions.find(item =>
|
const feeOption = this.feeOptions.find(item =>
|
||||||
(item.feeItems || []).some(name => String(name) === String(feeItem))
|
(item.feeItems || []).some(name => String(name) === String(feeItem))
|
||||||
);
|
);
|
||||||
const feeType = feeOption?.feeType || fallbackFeeType || '';
|
const feeType = this.feeCategoryValue(feeOption?.feeType || fallbackFeeType);
|
||||||
const key = `${feeType}\u0000${feeItem}`;
|
const key = `${feeType}\u0000${feeItem}`;
|
||||||
const current = summaryMap.get(key) || {
|
const current = summaryMap.get(key) || {
|
||||||
id: '',
|
id: '',
|
||||||
@@ -1239,7 +1261,7 @@ export default {
|
|||||||
const existingIds = new Set(this.details.map(row => String(row.sourceDetailId || row.id)));
|
const existingIds = new Set(this.details.map(row => String(row.sourceDetailId || row.id)));
|
||||||
this.candidateSelection.forEach(row => {
|
this.candidateSelection.forEach(row => {
|
||||||
if (!existingIds.has(String(row.id))) {
|
if (!existingIds.has(String(row.id))) {
|
||||||
this.details.push({ ...row, sourceDetailId: row.id });
|
this.details.push(this.normalizeSourceDetail(row));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
this.candidateDialog.confirming = true;
|
this.candidateDialog.confirming = true;
|
||||||
|
|||||||
@@ -42,6 +42,8 @@
|
|||||||
? settlementTypeName
|
? settlementTypeName
|
||||||
: field.prop === 'formalSettlementId'
|
: field.prop === 'formalSettlementId'
|
||||||
? form.formalSettlementNo || form.formalSettlementId
|
? form.formalSettlementNo || form.formalSettlementId
|
||||||
|
: field.prop === 'createTime'
|
||||||
|
? formatCreateDate(form[field.prop])
|
||||||
: form[field.prop]
|
: form[field.prop]
|
||||||
)
|
)
|
||||||
}}</span></el-form-item
|
}}</span></el-form-item
|
||||||
@@ -68,23 +70,55 @@
|
|||||||
v-if="editable && form.formalSettlementId"
|
v-if="editable && form.formalSettlementId"
|
||||||
type="primary"
|
type="primary"
|
||||||
plain
|
plain
|
||||||
@click="openFeeDialog"
|
@click="addFeeRow"
|
||||||
>添加费用</el-button
|
>添加费用</el-button
|
||||||
>
|
>
|
||||||
</template>
|
</template>
|
||||||
<el-table :data="details" border>
|
<el-table :data="details" border>
|
||||||
<el-table-column type="index" label="序号" width="64" align="center" /><el-table-column
|
<el-table-column type="index" label="序号" width="64" align="center" />
|
||||||
prop="feeType"
|
<el-table-column prop="feeType" label="费用类型" min-width="180" align="center">
|
||||||
label="费用类型"
|
<template #default="{ row }">
|
||||||
min-width="150"
|
<el-select
|
||||||
align="center"
|
v-if="editable && row.manualFlag === 1"
|
||||||
/><el-table-column
|
v-model="row.feeType"
|
||||||
prop="feeItem"
|
filterable
|
||||||
label="费用项目"
|
allow-create
|
||||||
min-width="180"
|
default-first-option
|
||||||
align="center"
|
clearable
|
||||||
show-overflow-tooltip
|
placeholder="请选择或输入"
|
||||||
/><el-table-column
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="item in feeOptions"
|
||||||
|
:key="item.feeType"
|
||||||
|
:label="item.feeTypeName || item.feeType"
|
||||||
|
:value="item.feeType"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
<span v-else>{{ displayValue(feeTypeName(row.feeType)) }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="feeItem" label="费用项目" min-width="200" align="center">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-select
|
||||||
|
v-if="editable && row.manualFlag === 1"
|
||||||
|
v-model="row.feeItem"
|
||||||
|
filterable
|
||||||
|
allow-create
|
||||||
|
default-first-option
|
||||||
|
clearable
|
||||||
|
placeholder="请选择或输入"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="item in feeItemOptions(row.feeType)"
|
||||||
|
:key="item"
|
||||||
|
:label="item"
|
||||||
|
:value="item"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
<span v-else>{{ displayValue(row.feeItem) }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column
|
||||||
prop="originalAmountTax"
|
prop="originalAmountTax"
|
||||||
label="原金额(含税)"
|
label="原金额(含税)"
|
||||||
min-width="145"
|
min-width="145"
|
||||||
@@ -98,7 +132,9 @@
|
|||||||
><el-input-number
|
><el-input-number
|
||||||
v-if="editable"
|
v-if="editable"
|
||||||
v-model="row.adjustmentAmountTax"
|
v-model="row.adjustmentAmountTax"
|
||||||
|
:class="{ negative: Number(row.adjustmentAmountTax) < 0 }"
|
||||||
:precision="2"
|
:precision="2"
|
||||||
|
:step="0.01"
|
||||||
:controls="false"
|
:controls="false"
|
||||||
@change="recalculate"
|
@change="recalculate"
|
||||||
/><span v-else :class="{ negative: Number(row.adjustmentAmountTax) < 0 }">{{
|
/><span v-else :class="{ negative: Number(row.adjustmentAmountTax) < 0 }">{{
|
||||||
@@ -134,7 +170,7 @@
|
|||||||
></template
|
></template
|
||||||
></el-table-column
|
></el-table-column
|
||||||
>
|
>
|
||||||
<template #empty><el-empty description="请选择调整费用明细" /></template>
|
<template #empty><el-empty description="暂无调整费用" /></template>
|
||||||
</el-table>
|
</el-table>
|
||||||
<div class="settlement-adjustment-editor__summary">
|
<div class="settlement-adjustment-editor__summary">
|
||||||
调整金额合计:<strong :class="{ negative: Number(form.adjustmentAmount) < 0 }">{{
|
调整金额合计:<strong :class="{ negative: Number(form.adjustmentAmount) < 0 }">{{
|
||||||
@@ -210,53 +246,19 @@
|
|||||||
</div>
|
</div>
|
||||||
<template #footer
|
<template #footer
|
||||||
><el-button @click="visible = false">取消</el-button
|
><el-button @click="visible = false">取消</el-button
|
||||||
|
><el-button
|
||||||
|
v-if="editable"
|
||||||
|
type="primary"
|
||||||
|
plain
|
||||||
|
:loading="syncing"
|
||||||
|
:disabled="!form.formalSettlementId"
|
||||||
|
@click="handleSync"
|
||||||
|
>同步</el-button
|
||||||
><el-button v-if="editable" type="primary" :loading="saving" @click="handleSave"
|
><el-button v-if="editable" type="primary" :loading="saving" @click="handleSave"
|
||||||
>保存</el-button
|
>保存</el-button
|
||||||
></template
|
></template
|
||||||
>
|
>
|
||||||
|
|
||||||
<el-dialog v-model="feeDialog.visible" title="选择调整费用" width="88%" append-to-body>
|
|
||||||
<el-table
|
|
||||||
ref="feeTable"
|
|
||||||
:data="feeRows"
|
|
||||||
border
|
|
||||||
@selection-change="feeDialog.selected = $event"
|
|
||||||
><el-table-column type="selection" width="52" align="center" /><el-table-column
|
|
||||||
type="index"
|
|
||||||
label="序号"
|
|
||||||
width="64"
|
|
||||||
align="center"
|
|
||||||
/><el-table-column
|
|
||||||
prop="documentNo"
|
|
||||||
label="单据号"
|
|
||||||
min-width="150"
|
|
||||||
align="center"
|
|
||||||
/><el-table-column
|
|
||||||
prop="feeType"
|
|
||||||
label="费用类型"
|
|
||||||
min-width="140"
|
|
||||||
align="center"
|
|
||||||
/><el-table-column
|
|
||||||
prop="feeItem"
|
|
||||||
label="费用项目"
|
|
||||||
min-width="180"
|
|
||||||
align="center"
|
|
||||||
show-overflow-tooltip
|
|
||||||
/><el-table-column
|
|
||||||
prop="originalAmountTax"
|
|
||||||
label="原金额(含税)"
|
|
||||||
min-width="145"
|
|
||||||
align="center"
|
|
||||||
><template #default="{ row }">{{
|
|
||||||
formatMoney(row.originalAmountTax)
|
|
||||||
}}</template></el-table-column
|
|
||||||
></el-table
|
|
||||||
>
|
|
||||||
<template #footer
|
|
||||||
><el-button @click="feeDialog.visible = false">取消</el-button
|
|
||||||
><el-button type="primary" @click="confirmFees">确定</el-button></template
|
|
||||||
>
|
|
||||||
</el-dialog>
|
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -274,6 +276,7 @@ export default {
|
|||||||
data: () => ({
|
data: () => ({
|
||||||
loading: false,
|
loading: false,
|
||||||
saving: false,
|
saving: false,
|
||||||
|
syncing: false,
|
||||||
form: createSettlementAdjustmentForm(),
|
form: createSettlementAdjustmentForm(),
|
||||||
fields: settlementAdjustmentFormFields,
|
fields: settlementAdjustmentFormFields,
|
||||||
details: [],
|
details: [],
|
||||||
@@ -296,11 +299,10 @@ export default {
|
|||||||
'zip',
|
'zip',
|
||||||
],
|
],
|
||||||
candidates: [],
|
candidates: [],
|
||||||
feeRows: [],
|
feeOptions: [],
|
||||||
rules: {
|
rules: {
|
||||||
formalSettlementId: [{ required: true, message: '请选择关联正式结算单', trigger: 'change' }],
|
formalSettlementId: [{ required: true, message: '请选择关联正式结算单', trigger: 'change' }],
|
||||||
},
|
},
|
||||||
feeDialog: { visible: false, selected: [] },
|
|
||||||
}),
|
}),
|
||||||
computed: {
|
computed: {
|
||||||
visible: {
|
visible: {
|
||||||
@@ -333,22 +335,29 @@ export default {
|
|||||||
methods: {
|
methods: {
|
||||||
async initialize() {
|
async initialize() {
|
||||||
this.form = createSettlementAdjustmentForm();
|
this.form = createSettlementAdjustmentForm();
|
||||||
|
const userInfo = this.$store.getters.userInfo || {};
|
||||||
|
this.form.createTime = this.$dayjs().format('YYYY-MM-DD');
|
||||||
|
this.form.createUserName = userInfo.realName || userInfo.userName || userInfo.account || '';
|
||||||
this.details = [];
|
this.details = [];
|
||||||
this.attachments = [];
|
this.attachments = [];
|
||||||
this.selectedAttachments = [];
|
this.selectedAttachments = [];
|
||||||
this.candidates = [];
|
this.candidates = [];
|
||||||
this.feeRows = [];
|
this.feeOptions = [];
|
||||||
if (!this.recordId) {
|
if (!this.recordId) {
|
||||||
await this.loadFormalSettlements('');
|
await Promise.all([this.loadFormalSettlements(''), this.loadFeeOptions()]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
try {
|
try {
|
||||||
const response = await api.getDetail(this.recordId);
|
const [response] = await Promise.all([api.getDetail(this.recordId), this.loadFeeOptions()]);
|
||||||
const data = response?.data?.data || response?.data || response || {};
|
const data = response?.data?.data || response?.data || response || {};
|
||||||
this.form = { ...createSettlementAdjustmentForm(), ...data };
|
this.form = { ...createSettlementAdjustmentForm(), ...data };
|
||||||
this.details = (data.details || []).map(item => ({
|
this.details = (data.details || []).map(item => ({
|
||||||
...item,
|
...item,
|
||||||
|
manualFlag:
|
||||||
|
item.formalSettlementDetailId == null && item.formalSettlementDetailFeeId == null
|
||||||
|
? 1
|
||||||
|
: 0,
|
||||||
adjustmentAmountTax: Number(item.adjustmentAmountTax || 0),
|
adjustmentAmountTax: Number(item.adjustmentAmountTax || 0),
|
||||||
adjustmentAmountNoTax:
|
adjustmentAmountNoTax:
|
||||||
item.adjustmentAmountNoTax == null ? null : Number(item.adjustmentAmountNoTax),
|
item.adjustmentAmountNoTax == null ? null : Number(item.adjustmentAmountNoTax),
|
||||||
@@ -363,6 +372,10 @@ export default {
|
|||||||
const { data } = await api.getFormalSettlements(keyword);
|
const { data } = await api.getFormalSettlements(keyword);
|
||||||
this.candidates = data?.data || data || [];
|
this.candidates = data?.data || data || [];
|
||||||
},
|
},
|
||||||
|
async loadFeeOptions() {
|
||||||
|
const { data } = await api.getFeeOptions();
|
||||||
|
this.feeOptions = data?.data || data || [];
|
||||||
|
},
|
||||||
async handleFormalChange(id) {
|
async handleFormalChange(id) {
|
||||||
if (!id) {
|
if (!id) {
|
||||||
Object.assign(this.form, {
|
Object.assign(this.form, {
|
||||||
@@ -397,6 +410,7 @@ export default {
|
|||||||
if (String(this.form.formalSettlementId) !== String(formalSettlementId)) return;
|
if (String(this.form.formalSettlementId) !== String(formalSettlementId)) return;
|
||||||
this.details = (data?.data || data || []).map(fee => ({
|
this.details = (data?.data || data || []).map(fee => ({
|
||||||
...fee,
|
...fee,
|
||||||
|
manualFlag: 0,
|
||||||
originalAmountTax: Number(fee.originalAmountTax || 0),
|
originalAmountTax: Number(fee.originalAmountTax || 0),
|
||||||
adjustmentAmountTax: 0,
|
adjustmentAmountTax: 0,
|
||||||
adjustmentAmountNoTax: null,
|
adjustmentAmountNoTax: null,
|
||||||
@@ -407,25 +421,28 @@ export default {
|
|||||||
this.loading = false;
|
this.loading = false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async openFeeDialog() {
|
addFeeRow() {
|
||||||
const { data } = await api.getFormalDetails(this.form.formalSettlementId);
|
this.details.push({
|
||||||
const used = new Set(this.details.map(item => String(item.formalSettlementDetailFeeId)));
|
formalSettlementDetailId: null,
|
||||||
this.feeRows = (data?.data || data || []).filter(
|
formalSettlementDetailFeeId: null,
|
||||||
item => !used.has(String(item.formalSettlementDetailFeeId))
|
feeType: '',
|
||||||
);
|
feeItem: '',
|
||||||
this.feeDialog = { visible: true, selected: [] };
|
originalAmountTax: 0,
|
||||||
|
adjustmentAmountTax: 0,
|
||||||
|
adjustmentAmountNoTax: null,
|
||||||
|
remark: '',
|
||||||
|
manualFlag: 1,
|
||||||
|
});
|
||||||
},
|
},
|
||||||
confirmFees() {
|
feeTypeName(value) {
|
||||||
this.feeDialog.selected.forEach(item =>
|
return (
|
||||||
this.details.push({
|
this.feeOptions.find(item => String(item.feeType) === String(value))?.feeTypeName || value
|
||||||
...item,
|
);
|
||||||
adjustmentAmountTax: 0,
|
},
|
||||||
adjustmentAmountNoTax: null,
|
feeItemOptions(feeType) {
|
||||||
remark: '',
|
return (
|
||||||
})
|
this.feeOptions.find(item => String(item.feeType) === String(feeType))?.feeItems || []
|
||||||
);
|
);
|
||||||
this.recalculate();
|
|
||||||
this.feeDialog.visible = false;
|
|
||||||
},
|
},
|
||||||
recalculate() {
|
recalculate() {
|
||||||
const total = this.details.reduce(
|
const total = this.details.reduce(
|
||||||
@@ -437,13 +454,65 @@ export default {
|
|||||||
(Number(this.form.originalSettlementAmount || 0) + total).toFixed(2)
|
(Number(this.form.originalSettlementAmount || 0) + total).toFixed(2)
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
async handleSync() {
|
||||||
|
const formalSettlementId = this.form.formalSettlementId;
|
||||||
|
if (!formalSettlementId) return this.$message.warning('请先选择正式结算单');
|
||||||
|
this.syncing = true;
|
||||||
|
try {
|
||||||
|
const [candidateResponse, detailResponse] = await Promise.all([
|
||||||
|
api.getFormalSettlements(this.form.formalSettlementNo || ''),
|
||||||
|
api.getFormalDetails(formalSettlementId),
|
||||||
|
]);
|
||||||
|
if (String(this.form.formalSettlementId) !== String(formalSettlementId)) return;
|
||||||
|
const candidateData = candidateResponse?.data?.data || candidateResponse?.data || [];
|
||||||
|
const formalSettlements = Array.isArray(candidateData) ? candidateData : [];
|
||||||
|
const formalSettlement = formalSettlements.find(
|
||||||
|
item => String(item.id) === String(formalSettlementId)
|
||||||
|
);
|
||||||
|
if (formalSettlement) {
|
||||||
|
const { id, ...formalInformation } = formalSettlement;
|
||||||
|
Object.assign(this.form, formalInformation, {
|
||||||
|
formalSettlementId: id,
|
||||||
|
formalSettlementNo: formalSettlement.formalSettlementNo,
|
||||||
|
originalSettlementAmount: Number(formalSettlement.settlementAmount || 0),
|
||||||
|
settlementTypeName: formalSettlement.settlementTypeName,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const existingDetailMap = new Map(
|
||||||
|
this.details
|
||||||
|
.filter(item => item.formalSettlementDetailFeeId != null)
|
||||||
|
.map(item => [String(item.formalSettlementDetailFeeId), item])
|
||||||
|
);
|
||||||
|
const manualDetails = this.details.filter(item => Number(item.manualFlag) === 1);
|
||||||
|
const detailData = detailResponse?.data?.data || detailResponse?.data || [];
|
||||||
|
const formalDetails = Array.isArray(detailData) ? detailData : [];
|
||||||
|
this.details = formalDetails
|
||||||
|
.map(fee => {
|
||||||
|
const existing = existingDetailMap.get(String(fee.formalSettlementDetailFeeId));
|
||||||
|
return {
|
||||||
|
...fee,
|
||||||
|
manualFlag: 0,
|
||||||
|
originalAmountTax: Number(fee.originalAmountTax || 0),
|
||||||
|
adjustmentAmountTax: Number(existing?.adjustmentAmountTax || 0),
|
||||||
|
adjustmentAmountNoTax:
|
||||||
|
existing?.adjustmentAmountNoTax == null
|
||||||
|
? null
|
||||||
|
: Number(existing.adjustmentAmountNoTax),
|
||||||
|
remark: existing?.remark || '',
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.concat(manualDetails);
|
||||||
|
this.recalculate();
|
||||||
|
this.$message.success('同步成功');
|
||||||
|
} finally {
|
||||||
|
this.syncing = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
async handleSave() {
|
async handleSave() {
|
||||||
await this.$refs.formRef.validate();
|
|
||||||
if (!this.details.length) return this.$message.warning('请至少添加一条调整费用');
|
|
||||||
this.saving = true;
|
this.saving = true;
|
||||||
try {
|
try {
|
||||||
await api.save({
|
const { data } = await api.save({
|
||||||
...(this.recordId ? { id: this.form.id } : {}),
|
id: this.form.id || undefined,
|
||||||
formalSettlementId: this.form.formalSettlementId,
|
formalSettlementId: this.form.formalSettlementId,
|
||||||
attachmentsJson: JSON.stringify(this.attachments || []),
|
attachmentsJson: JSON.stringify(this.attachments || []),
|
||||||
remark: this.form.remark,
|
remark: this.form.remark,
|
||||||
@@ -452,11 +521,13 @@ export default {
|
|||||||
formalSettlementDetailFeeId: item.formalSettlementDetailFeeId,
|
formalSettlementDetailFeeId: item.formalSettlementDetailFeeId,
|
||||||
feeType: item.feeType,
|
feeType: item.feeType,
|
||||||
feeItem: item.feeItem,
|
feeItem: item.feeItem,
|
||||||
|
originalAmountTax: item.originalAmountTax,
|
||||||
adjustmentAmountTax: item.adjustmentAmountTax,
|
adjustmentAmountTax: item.adjustmentAmountTax,
|
||||||
adjustmentAmountNoTax: item.adjustmentAmountNoTax,
|
adjustmentAmountNoTax: item.adjustmentAmountNoTax,
|
||||||
remark: item.remark,
|
remark: item.remark,
|
||||||
})),
|
})),
|
||||||
});
|
});
|
||||||
|
this.form.id = data?.data || data || this.form.id;
|
||||||
this.$message.success('保存成功');
|
this.$message.success('保存成功');
|
||||||
this.visible = false;
|
this.visible = false;
|
||||||
this.$emit('success');
|
this.$emit('success');
|
||||||
@@ -470,6 +541,11 @@ export default {
|
|||||||
formatMoney(value) {
|
formatMoney(value) {
|
||||||
return Number(value || 0).toFixed(2);
|
return Number(value || 0).toFixed(2);
|
||||||
},
|
},
|
||||||
|
formatCreateDate(value) {
|
||||||
|
if (!value) return '';
|
||||||
|
const date = this.$dayjs(value);
|
||||||
|
return date.isValid() ? date.format('YYYY-MM-DD') : value;
|
||||||
|
},
|
||||||
handleAttachmentChange(files) {
|
handleAttachmentChange(files) {
|
||||||
const userInfo = this.$store.getters.userInfo || {};
|
const userInfo = this.$store.getters.userInfo || {};
|
||||||
const uploadUserName = userInfo.realName || userInfo.userName || '';
|
const uploadUserName = userInfo.realName || userInfo.userName || '';
|
||||||
@@ -573,4 +649,7 @@ export default {
|
|||||||
.negative {
|
.negative {
|
||||||
color: #f56c6c;
|
color: #f56c6c;
|
||||||
}
|
}
|
||||||
|
.settlement-adjustment-editor :deep(.el-input-number.negative .el-input__inner) {
|
||||||
|
color: #f56c6c;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -174,8 +174,8 @@ import { createSettlementTransfer } from '@/utils/settlement-transfer';
|
|||||||
import * as api from '@/api/settlement/formalSettlement';
|
import * as api from '@/api/settlement/formalSettlement';
|
||||||
import * as paymentApi from '@/api/payment/paymentApplication';
|
import * as paymentApi from '@/api/payment/paymentApplication';
|
||||||
import {
|
import {
|
||||||
|
invoiceStatusName,
|
||||||
formalSettlementSearchFields,
|
formalSettlementSearchFields,
|
||||||
invoiceStatusOptions,
|
|
||||||
paymentStatusOptions,
|
paymentStatusOptions,
|
||||||
} from '@/option/settlement/formalSettlementSearch';
|
} from '@/option/settlement/formalSettlementSearch';
|
||||||
import organizationSearch from '@/mixins/organization-search';
|
import organizationSearch from '@/mixins/organization-search';
|
||||||
@@ -518,7 +518,7 @@ export default {
|
|||||||
原币结算金额: item.settlementAmount,
|
原币结算金额: item.settlementAmount,
|
||||||
本位币结算金额: item.localSettlementAmount,
|
本位币结算金额: item.localSettlementAmount,
|
||||||
结算汇率: item.exchangeRate,
|
结算汇率: item.exchangeRate,
|
||||||
发票状态: this.invoiceName(item.invoiceStatus),
|
发票状态: this.invoiceName(item.invoiceStatus, item.settlementType),
|
||||||
收付款状态: this.paymentName(item.paymentStatus),
|
收付款状态: this.paymentName(item.paymentStatus),
|
||||||
审核状态: item.approvalStatusName,
|
审核状态: item.approvalStatusName,
|
||||||
金蝶单据号: item.kingdeeBillNo,
|
金蝶单据号: item.kingdeeBillNo,
|
||||||
@@ -564,8 +564,8 @@ export default {
|
|||||||
''
|
''
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
invoiceName(value) {
|
invoiceName(value, settlementType) {
|
||||||
return invoiceStatusOptions.find(item => item.value === value)?.label || value || '-';
|
return invoiceStatusName(value, settlementType);
|
||||||
},
|
},
|
||||||
paymentName(value) {
|
paymentName(value) {
|
||||||
return paymentStatusOptions.find(item => item.value === value)?.label || value || '-';
|
return paymentStatusOptions.find(item => item.value === value)?.label || value || '-';
|
||||||
|
|||||||
@@ -141,6 +141,9 @@
|
|||||||
<span v-else-if="column.precision !== undefined">
|
<span v-else-if="column.precision !== undefined">
|
||||||
{{ formatNumber(row[column.prop], column.precision) }}
|
{{ formatNumber(row[column.prop], column.precision) }}
|
||||||
</span>
|
</span>
|
||||||
|
<span v-else-if="column.prop === 'sourceType'">
|
||||||
|
{{ row[column.prop] === '应收应付' ? '应付' : displayValue(row[column.prop]) }}
|
||||||
|
</span>
|
||||||
<span v-else>{{ displayValue(row[column.prop]) }}</span>
|
<span v-else>{{ displayValue(row[column.prop]) }}</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
|||||||
@@ -15,16 +15,6 @@
|
|||||||
start-placeholder="年/月/日"
|
start-placeholder="年/月/日"
|
||||||
end-placeholder="年/月/日"
|
end-placeholder="年/月/日"
|
||||||
/>
|
/>
|
||||||
<el-cascader
|
|
||||||
v-else-if="field.type === 'cascader'"
|
|
||||||
v-model="query[field.prop]"
|
|
||||||
:options="organizationTreeOptions"
|
|
||||||
:props="organizationCascaderProps"
|
|
||||||
clearable
|
|
||||||
filterable
|
|
||||||
style="width: 100%"
|
|
||||||
placeholder="请选择"
|
|
||||||
/>
|
|
||||||
<el-input v-else v-model="query[field.prop]" clearable placeholder="请输入" />
|
<el-input v-else v-model="query[field.prop]" clearable placeholder="请输入" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</template>
|
</template>
|
||||||
@@ -44,7 +34,14 @@
|
|||||||
<div class="settlement-detail-page__toolbar-left">
|
<div class="settlement-detail-page__toolbar-left">
|
||||||
<el-button type="primary" @click="openGenerateDialog">生成费用</el-button>
|
<el-button type="primary" @click="openGenerateDialog">生成费用</el-button>
|
||||||
<el-button type="primary" plain @click="openUpdateFeeDialog">更新费用</el-button>
|
<el-button type="primary" plain @click="openUpdateFeeDialog">更新费用</el-button>
|
||||||
<el-button type="primary" plain @click="openTransferDialog">批量转结算</el-button>
|
<el-button
|
||||||
|
v-if="isPayable || isReceivable"
|
||||||
|
type="primary"
|
||||||
|
plain
|
||||||
|
@click="openTransferDialog"
|
||||||
|
>
|
||||||
|
批量转结算
|
||||||
|
</el-button>
|
||||||
<el-button type="primary" plain @click="handleExport">导出</el-button>
|
<el-button type="primary" plain @click="handleExport">导出</el-button>
|
||||||
</div>
|
</div>
|
||||||
<div class="settlement-detail-page__toolbar-right">
|
<div class="settlement-detail-page__toolbar-right">
|
||||||
@@ -200,7 +197,7 @@
|
|||||||
<div
|
<div
|
||||||
class="settlement-detail-page__detail-panel-body settlement-detail-page__adjust-panel-body"
|
class="settlement-detail-page__detail-panel-body settlement-detail-page__adjust-panel-body"
|
||||||
>
|
>
|
||||||
<div v-if="isReceivable" class="settlement-detail-page__adjust-toolbar">
|
<div v-if="isPayable" class="settlement-detail-page__adjust-toolbar">
|
||||||
<el-button type="primary" :disabled="adjustDialog.loading" @click="addAdjustFee">
|
<el-button type="primary" :disabled="adjustDialog.loading" @click="addAdjustFee">
|
||||||
新增费用
|
新增费用
|
||||||
</el-button>
|
</el-button>
|
||||||
@@ -219,18 +216,26 @@
|
|||||||
show-overflow-tooltip
|
show-overflow-tooltip
|
||||||
>
|
>
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<span
|
<el-select
|
||||||
v-if="row.manualFee && ['billingFactor', 'billingType'].includes(column.prop)"
|
v-if="column.prop === 'cargoName' && row.manualFee"
|
||||||
>
|
|
||||||
-
|
|
||||||
</span>
|
|
||||||
<el-input
|
|
||||||
v-else-if="column.prop === 'cargoName' && row.manualFee"
|
|
||||||
v-model="row.cargoName"
|
v-model="row.cargoName"
|
||||||
|
class="settlement-detail-page__adjust-control"
|
||||||
|
:loading="commonCargoLoading"
|
||||||
clearable
|
clearable
|
||||||
maxlength="100"
|
filterable
|
||||||
placeholder="请输入"
|
allow-create
|
||||||
/>
|
default-first-option
|
||||||
|
placeholder="请选择或输入"
|
||||||
|
@visible-change="visible => visible && loadCommonCargoOptions()"
|
||||||
|
@change="value => handleAdjustCargoNameChange(row, value)"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="item in manualCargoNameOptions(row)"
|
||||||
|
:key="item.value"
|
||||||
|
:label="item.label"
|
||||||
|
:value="item.value"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
<span v-else-if="column.prop === 'cargoName'">
|
<span v-else-if="column.prop === 'cargoName'">
|
||||||
{{ formatDetailCell(row, column.prop) }}
|
{{ formatDetailCell(row, column.prop) }}
|
||||||
</span>
|
</span>
|
||||||
@@ -251,6 +256,56 @@
|
|||||||
<span v-else-if="column.prop === 'cargoType'">
|
<span v-else-if="column.prop === 'cargoType'">
|
||||||
{{ formatDetailCell(row, column.prop) }}
|
{{ formatDetailCell(row, column.prop) }}
|
||||||
</span>
|
</span>
|
||||||
|
<el-select
|
||||||
|
v-else-if="column.prop === 'specification' && row.manualFee"
|
||||||
|
v-model="row.specification"
|
||||||
|
class="settlement-detail-page__adjust-control"
|
||||||
|
:loading="commonCargoLoading"
|
||||||
|
clearable
|
||||||
|
filterable
|
||||||
|
allow-create
|
||||||
|
default-first-option
|
||||||
|
placeholder="请选择或输入"
|
||||||
|
@visible-change="visible => visible && loadCommonCargoOptions()"
|
||||||
|
@change="value => handleAdjustSpecificationChange(row, value)"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="item in manualSpecificationOptions(row)"
|
||||||
|
:key="item.value"
|
||||||
|
:label="item.label"
|
||||||
|
:value="item.value"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
<el-select
|
||||||
|
v-else-if="column.prop === 'billingFactor' && row.manualFee"
|
||||||
|
v-model="row.billingFactor"
|
||||||
|
class="settlement-detail-page__adjust-control"
|
||||||
|
clearable
|
||||||
|
placeholder="请选择"
|
||||||
|
@change="handleAdjustBillingFactorChange(row)"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="item in adjustBillingElements"
|
||||||
|
:key="item"
|
||||||
|
:label="item"
|
||||||
|
:value="item"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
<el-select
|
||||||
|
v-else-if="column.prop === 'billingType' && row.manualFee"
|
||||||
|
v-model="row.billingType"
|
||||||
|
class="settlement-detail-page__adjust-control"
|
||||||
|
:disabled="!row.billingFactor"
|
||||||
|
clearable
|
||||||
|
placeholder="请选择"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="item in adjustBillingTypes(row)"
|
||||||
|
:key="item"
|
||||||
|
:label="item"
|
||||||
|
:value="item"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
<el-select
|
<el-select
|
||||||
v-else-if="column.prop === 'priceUnit'"
|
v-else-if="column.prop === 'priceUnit'"
|
||||||
v-model="row.priceUnit"
|
v-model="row.priceUnit"
|
||||||
@@ -337,7 +392,12 @@
|
|||||||
</div>
|
</div>
|
||||||
<footer class="settlement-detail-page__detail-panel-footer">
|
<footer class="settlement-detail-page__detail-panel-footer">
|
||||||
<el-button @click="closeAdjustPanel">取消</el-button>
|
<el-button @click="closeAdjustPanel">取消</el-button>
|
||||||
<el-button type="primary" :loading="adjustDialog.submitting" @click="saveAdjustFee">
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
:loading="adjustDialog.submitting"
|
||||||
|
:disabled="hasAdjustCalculationPending"
|
||||||
|
@click="saveAdjustFee"
|
||||||
|
>
|
||||||
保存
|
保存
|
||||||
</el-button>
|
</el-button>
|
||||||
</footer>
|
</footer>
|
||||||
@@ -402,7 +462,7 @@
|
|||||||
v-model="transferForm.settlementBillType"
|
v-model="transferForm.settlementBillType"
|
||||||
@change="handleTransferTypeChange"
|
@change="handleTransferTypeChange"
|
||||||
>
|
>
|
||||||
<el-radio label="pre">预结算单</el-radio>
|
<el-radio v-if="isPayable" label="pre">预结算单</el-radio>
|
||||||
<el-radio label="formal">正式结算单</el-radio>
|
<el-radio label="formal">正式结算单</el-radio>
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
@@ -631,16 +691,35 @@ import {
|
|||||||
} from '@/option/settlement/receivable-payable-detail';
|
} from '@/option/settlement/receivable-payable-detail';
|
||||||
import * as api from '@/api/settlement/receivable-payable-detail';
|
import * as api from '@/api/settlement/receivable-payable-detail';
|
||||||
import { getList as getContractList } from '@/api/business/contract-manage';
|
import { getList as getContractList } from '@/api/business/contract-manage';
|
||||||
|
import { getList as getCommonCargoList } from '@/api/business/common-cargo';
|
||||||
import { getContractOptions as getSettlementContractOptions } from '@/api/settlement/preSettlement';
|
import { getContractOptions as getSettlementContractOptions } from '@/api/settlement/preSettlement';
|
||||||
import { getList as getCargoTypeList } from '@/api/base/cargo-type';
|
import { getList as getCargoTypeList } from '@/api/base/cargo-type';
|
||||||
import { exportBlob } from '@/api/common';
|
import { exportBlob } from '@/api/common';
|
||||||
import { getDictionary } from '@/api/system/dictbiz';
|
import { getDictionary } from '@/api/system/dictbiz';
|
||||||
import { downloadXls } from '@/utils/util';
|
import { downloadXls } from '@/utils/util';
|
||||||
import { createSettlementTransfer } from '@/utils/settlement-transfer';
|
import { createSettlementTransfer } from '@/utils/settlement-transfer';
|
||||||
import organizationSearch from '@/mixins/organization-search';
|
|
||||||
|
const ADJUST_BILLING_ELEMENTS = [
|
||||||
|
'按重量',
|
||||||
|
'按体积',
|
||||||
|
'按车辆',
|
||||||
|
'按里程',
|
||||||
|
'按吨·公里',
|
||||||
|
'固定金额(整单一口价)',
|
||||||
|
'按数量',
|
||||||
|
];
|
||||||
|
|
||||||
|
const ADJUST_BILLING_TYPE_MAP = {
|
||||||
|
按重量: ['固定单价', '区间单价', '阶梯单价', '区间阶梯一口价'],
|
||||||
|
按体积: ['固定单价', '区间单价', '阶梯单价', '区间阶梯一口价'],
|
||||||
|
按车辆: ['固定单价'],
|
||||||
|
按里程: ['固定单价', '区间单价', '阶梯单价', '区间阶梯一口价'],
|
||||||
|
'按吨·公里': ['固定单价', '区间单价', '阶梯单价', '区间阶梯一口价'],
|
||||||
|
'固定金额(整单一口价)': ['固定一口价'],
|
||||||
|
按数量: ['固定单价', '区间单价', '阶梯单价', '区间阶梯一口价'],
|
||||||
|
};
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
mixins: [organizationSearch],
|
|
||||||
props: {
|
props: {
|
||||||
settlementType: {
|
settlementType: {
|
||||||
type: String,
|
type: String,
|
||||||
@@ -679,6 +758,10 @@ export default {
|
|||||||
adjustRows: [],
|
adjustRows: [],
|
||||||
adjustDynamicFeeColumns: [],
|
adjustDynamicFeeColumns: [],
|
||||||
adjustTextProps: ['specification', 'model', 'billingFactor', 'billingType'],
|
adjustTextProps: ['specification', 'model', 'billingFactor', 'billingType'],
|
||||||
|
adjustBillingElements: ADJUST_BILLING_ELEMENTS,
|
||||||
|
commonCargoOptions: [],
|
||||||
|
commonCargoLoading: false,
|
||||||
|
commonCargoRequest: null,
|
||||||
cargoTypeOptions: [],
|
cargoTypeOptions: [],
|
||||||
cargoTypeFlatOptions: [],
|
cargoTypeFlatOptions: [],
|
||||||
cargoTypeLoading: false,
|
cargoTypeLoading: false,
|
||||||
@@ -707,7 +790,7 @@ export default {
|
|||||||
billingPlanOptions: [],
|
billingPlanOptions: [],
|
||||||
transferDialog: { visible: false, loading: false, submitting: false },
|
transferDialog: { visible: false, loading: false, submitting: false },
|
||||||
transferQuery: {},
|
transferQuery: {},
|
||||||
transferForm: { settlementBillType: 'formal' },
|
transferForm: { settlementBillType: 'pre' },
|
||||||
transferRows: [],
|
transferRows: [],
|
||||||
transferSelection: [],
|
transferSelection: [],
|
||||||
transferPage: { current: 1, size: 10, total: 0 },
|
transferPage: { current: 1, size: 10, total: 0 },
|
||||||
@@ -730,6 +813,12 @@ export default {
|
|||||||
isReceivable() {
|
isReceivable() {
|
||||||
return this.settlementType === 'receivable';
|
return this.settlementType === 'receivable';
|
||||||
},
|
},
|
||||||
|
isPayable() {
|
||||||
|
return this.settlementType === 'payable';
|
||||||
|
},
|
||||||
|
hasAdjustCalculationPending() {
|
||||||
|
return this.adjustRows.some(row => row.calculating);
|
||||||
|
},
|
||||||
visibleSearchFields() {
|
visibleSearchFields() {
|
||||||
return this.searchExpanded ? this.searchFields : this.searchFields.slice(0, 4);
|
return this.searchExpanded ? this.searchFields : this.searchFields.slice(0, 4);
|
||||||
},
|
},
|
||||||
@@ -737,7 +826,9 @@ export default {
|
|||||||
return [...feeDetailBaseColumns, ...this.dynamicFeeColumns, ...feeDetailTailColumns];
|
return [...feeDetailBaseColumns, ...this.dynamicFeeColumns, ...feeDetailTailColumns];
|
||||||
},
|
},
|
||||||
displayTableColumns() {
|
displayTableColumns() {
|
||||||
const columns = [...this.tableColumns];
|
const columns = this.tableColumns.filter(
|
||||||
|
column => this.settlementType !== 'receivable' || column.prop !== 'preSettlementNo'
|
||||||
|
);
|
||||||
const totalIndex = columns.findIndex(column => column.prop === 'totalAmountText');
|
const totalIndex = columns.findIndex(column => column.prop === 'totalAmountText');
|
||||||
const dynamicColumns = this.tableFeeItemNames.map((name, index) => ({
|
const dynamicColumns = this.tableFeeItemNames.map((name, index) => ({
|
||||||
label: name,
|
label: name,
|
||||||
@@ -766,12 +857,20 @@ export default {
|
|||||||
return this.cargoTypeOptions.filter(item => item.children?.length);
|
return this.cargoTypeOptions.filter(item => item.children?.length);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
mounted() {
|
watch: {
|
||||||
this.loadOrganizationOptions();
|
'$route.fullPath'() {
|
||||||
|
this.openRouteDetail();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
async mounted() {
|
||||||
this.loadTransportTypeOptions();
|
this.loadTransportTypeOptions();
|
||||||
this.loadCargoTypeOptions();
|
this.loadCargoTypeOptions();
|
||||||
this.loadPriceUnitOptions();
|
this.loadPriceUnitOptions();
|
||||||
this.loadTable();
|
await this.loadTable();
|
||||||
|
await this.openRouteDetail();
|
||||||
|
},
|
||||||
|
beforeUnmount() {
|
||||||
|
this.clearAdjustCalculations();
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
async loadTransportTypeOptions() {
|
async loadTransportTypeOptions() {
|
||||||
@@ -819,6 +918,82 @@ export default {
|
|||||||
});
|
});
|
||||||
return this.priceUnitRequest;
|
return this.priceUnitRequest;
|
||||||
},
|
},
|
||||||
|
loadCommonCargoOptions() {
|
||||||
|
if (this.commonCargoOptions.length) return Promise.resolve(this.commonCargoOptions);
|
||||||
|
if (this.commonCargoRequest) return this.commonCargoRequest;
|
||||||
|
this.commonCargoLoading = true;
|
||||||
|
this.commonCargoRequest = getCommonCargoList(1, 9999)
|
||||||
|
.then(res => {
|
||||||
|
this.commonCargoOptions = this.extractRecords(res);
|
||||||
|
return this.commonCargoOptions;
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.commonCargoLoading = false;
|
||||||
|
this.commonCargoRequest = null;
|
||||||
|
});
|
||||||
|
return this.commonCargoRequest;
|
||||||
|
},
|
||||||
|
commonCargoMatches(row) {
|
||||||
|
const cargoType = String(row.cargoType || '').trim();
|
||||||
|
if (!cargoType) return this.commonCargoOptions;
|
||||||
|
const matched = this.commonCargoOptions.filter(item =>
|
||||||
|
[item.secondCargoTypeName, item.firstCargoTypeName, item.cargoType].some(
|
||||||
|
value => String(value || '').trim() === cargoType
|
||||||
|
)
|
||||||
|
);
|
||||||
|
return matched.length ? matched : this.commonCargoOptions;
|
||||||
|
},
|
||||||
|
manualCargoNameOptions(row) {
|
||||||
|
const options = new Map();
|
||||||
|
this.commonCargoMatches(row).forEach(item => {
|
||||||
|
const value = String(item.cargoName || '').trim();
|
||||||
|
if (value && !options.has(value)) options.set(value, { label: value, value });
|
||||||
|
});
|
||||||
|
return Array.from(options.values());
|
||||||
|
},
|
||||||
|
manualSpecificationOptions(row) {
|
||||||
|
const cargoName = String(row.cargoName || '').trim();
|
||||||
|
const records = cargoName
|
||||||
|
? this.commonCargoMatches(row).filter(
|
||||||
|
item => String(item.cargoName || '').trim() === cargoName
|
||||||
|
)
|
||||||
|
: this.commonCargoMatches(row);
|
||||||
|
const options = new Map();
|
||||||
|
records.forEach(item => {
|
||||||
|
const value = String(item.specification || item.spec || '').trim();
|
||||||
|
if (value && !options.has(value)) options.set(value, { label: value, value });
|
||||||
|
});
|
||||||
|
return Array.from(options.values());
|
||||||
|
},
|
||||||
|
handleAdjustCargoNameChange(row, value) {
|
||||||
|
row.cargoName = String(value || '').trim();
|
||||||
|
const cargo = this.commonCargoMatches(row).find(
|
||||||
|
item => String(item.cargoName || '').trim() === row.cargoName
|
||||||
|
);
|
||||||
|
if (cargo) this.applyCommonCargoToAdjustRow(row, cargo);
|
||||||
|
},
|
||||||
|
handleAdjustSpecificationChange(row, value) {
|
||||||
|
row.specification = String(value || '').trim();
|
||||||
|
const cargo = this.commonCargoMatches(row).find(
|
||||||
|
item =>
|
||||||
|
String(item.cargoName || '').trim() === String(row.cargoName || '').trim() &&
|
||||||
|
String(item.specification || item.spec || '').trim() === row.specification
|
||||||
|
);
|
||||||
|
if (cargo) this.applyCommonCargoToAdjustRow(row, cargo);
|
||||||
|
},
|
||||||
|
applyCommonCargoToAdjustRow(row, cargo) {
|
||||||
|
row.cargoName = cargo.cargoName || row.cargoName || '';
|
||||||
|
row.specification = cargo.specification || cargo.spec || row.specification || '';
|
||||||
|
row.model = cargo.model || row.model || '';
|
||||||
|
row.priceUnit = cargo.priceUnit || row.priceUnit || '';
|
||||||
|
row.cargoType =
|
||||||
|
cargo.secondCargoTypeName ||
|
||||||
|
cargo.cargoType ||
|
||||||
|
cargo.firstCargoTypeName ||
|
||||||
|
row.cargoType ||
|
||||||
|
'';
|
||||||
|
row.cargoTypePath = this.resolveCargoTypePath(row.cargoType);
|
||||||
|
},
|
||||||
buildCargoTypeTree(cargoTypes = []) {
|
buildCargoTypeTree(cargoTypes = []) {
|
||||||
const flatCargoTypes = [];
|
const flatCargoTypes = [];
|
||||||
const collectCargoTypes = list => {
|
const collectCargoTypes = list => {
|
||||||
@@ -933,6 +1108,8 @@ export default {
|
|||||||
row.cargoTypePath = path;
|
row.cargoTypePath = path;
|
||||||
row.cargoType = labels.length ? labels[labels.length - 1] : '';
|
row.cargoType = labels.length ? labels[labels.length - 1] : '';
|
||||||
row.cargoName = '';
|
row.cargoName = '';
|
||||||
|
row.specification = '';
|
||||||
|
row.model = '';
|
||||||
},
|
},
|
||||||
formatColumnValue(row, column) {
|
formatColumnValue(row, column) {
|
||||||
if (column.prop === 'transportType') return this.transportTypeLabel(row[column.prop]);
|
if (column.prop === 'transportType') return this.transportTypeLabel(row[column.prop]);
|
||||||
@@ -950,15 +1127,11 @@ export default {
|
|||||||
this.tableFeeItemNames = [];
|
this.tableFeeItemNames = [];
|
||||||
try {
|
try {
|
||||||
const params = this.buildRequestParams(
|
const params = this.buildRequestParams(
|
||||||
// 所属组织级联返回 id 路径,转成组织名称传给后端(用副本,避免污染搜索框回显)
|
this.normalizeQuery(
|
||||||
this.normalizeOrganizationSearch(
|
this.query,
|
||||||
this.normalizeQuery(
|
'generateDateRange',
|
||||||
{ ...this.query },
|
'generateStartDate',
|
||||||
'generateDateRange',
|
'generateEndDate'
|
||||||
'generateStartDate',
|
|
||||||
'generateEndDate'
|
|
||||||
),
|
|
||||||
'deptName'
|
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
const res = await api.getList(this.page.current, this.page.size, params);
|
const res = await api.getList(this.page.current, this.page.size, params);
|
||||||
@@ -1006,6 +1179,7 @@ export default {
|
|||||||
this.detailDialog.row = null;
|
this.detailDialog.row = null;
|
||||||
},
|
},
|
||||||
closeAdjustPanel() {
|
closeAdjustPanel() {
|
||||||
|
this.clearAdjustCalculations();
|
||||||
this.adjustDialog.visible = false;
|
this.adjustDialog.visible = false;
|
||||||
this.adjustDialog.row = null;
|
this.adjustDialog.row = null;
|
||||||
},
|
},
|
||||||
@@ -1031,6 +1205,29 @@ export default {
|
|||||||
}
|
}
|
||||||
this.$router.push({ path, query: { detailNo } });
|
this.$router.push({ path, query: { detailNo } });
|
||||||
},
|
},
|
||||||
|
async openRouteDetail(detailId = this.$route.query.detailId) {
|
||||||
|
if (
|
||||||
|
!detailId ||
|
||||||
|
this.settlementType !== 'payable' ||
|
||||||
|
this.$route.path !== '/settlement/payable-detail'
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let row = this.rows.find(item => String(item.id) === String(detailId));
|
||||||
|
if (!row) {
|
||||||
|
const res = await api.getList(1, 1, {
|
||||||
|
ids: detailId,
|
||||||
|
settlementType: this.settlementType,
|
||||||
|
});
|
||||||
|
const data = this.unwrapPage(res);
|
||||||
|
if (data.records?.[0]) row = this.decorateRow(data.records[0]);
|
||||||
|
}
|
||||||
|
if (!row) {
|
||||||
|
this.$message.info('当前记录未找到对应的应付明细');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await this.openDetailDialog(row);
|
||||||
|
},
|
||||||
openWaybillDetail(row) {
|
openWaybillDetail(row) {
|
||||||
if (!row.waybillId) {
|
if (!row.waybillId) {
|
||||||
this.$message.info('当前记录未关联运单详情');
|
this.$message.info('当前记录未关联运单详情');
|
||||||
@@ -1058,6 +1255,7 @@ export default {
|
|||||||
try {
|
try {
|
||||||
const [res] = await Promise.all([
|
const [res] = await Promise.all([
|
||||||
api.getFeeDetail(row.id),
|
api.getFeeDetail(row.id),
|
||||||
|
this.loadCommonCargoOptions(),
|
||||||
this.loadCargoTypeOptions(),
|
this.loadCargoTypeOptions(),
|
||||||
this.loadPriceUnitOptions(),
|
this.loadPriceUnitOptions(),
|
||||||
]);
|
]);
|
||||||
@@ -1085,9 +1283,14 @@ export default {
|
|||||||
originalAmount: Number(item.originalAmount || 0),
|
originalAmount: Number(item.originalAmount || 0),
|
||||||
feeItems,
|
feeItems,
|
||||||
dataSource:
|
dataSource:
|
||||||
item.dataSource || (item.billingFactor === '手工调整' ? '手工录入' : '自动生成'),
|
item.dataSource || (item.billingFactor === '手工调整' ? '手动录入' : '自动生成'),
|
||||||
manualFee: item.dataSource === '手工录入' || item.billingFactor === '手工调整',
|
manualFee:
|
||||||
|
['手动录入', '手动添加', '手工录入'].includes(item.dataSource) ||
|
||||||
|
item.billingFactor === '手工调整',
|
||||||
cargoTypePath: this.resolveCargoTypePath(item.cargoType),
|
cargoTypePath: this.resolveCargoTypePath(item.cargoType),
|
||||||
|
calculating: false,
|
||||||
|
adjustCalculateTimer: null,
|
||||||
|
adjustCalculateVersion: 0,
|
||||||
};
|
};
|
||||||
this.recalculateAdjustRow(adjusted);
|
this.recalculateAdjustRow(adjusted);
|
||||||
return adjusted;
|
return adjusted;
|
||||||
@@ -1097,20 +1300,20 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
addAdjustFee() {
|
addAdjustFee() {
|
||||||
if (!this.isReceivable) return;
|
if (!this.isPayable) return;
|
||||||
const feeItems = this.adjustDynamicFeeColumns.reduce((items, column) => {
|
const feeItems = this.adjustDynamicFeeColumns.reduce((items, column) => {
|
||||||
items[column.feeItemName] = 0;
|
items[column.feeItemName] = 0;
|
||||||
return items;
|
return items;
|
||||||
}, {});
|
}, {});
|
||||||
this.adjustRows.push({
|
this.adjustRows.push({
|
||||||
dataSource: '手工录入',
|
dataSource: '手动录入',
|
||||||
cargoName: '',
|
cargoName: '',
|
||||||
cargoType: '',
|
cargoType: '',
|
||||||
cargoTypePath: [],
|
cargoTypePath: [],
|
||||||
specification: '',
|
specification: '',
|
||||||
model: '',
|
model: '',
|
||||||
billingFactor: '-',
|
billingFactor: '',
|
||||||
billingType: '-',
|
billingType: '',
|
||||||
transportQuantity: '',
|
transportQuantity: '',
|
||||||
priceUnit: '',
|
priceUnit: '',
|
||||||
unitPrice: '',
|
unitPrice: '',
|
||||||
@@ -1124,10 +1327,19 @@ export default {
|
|||||||
manualFee: true,
|
manualFee: true,
|
||||||
remark: '',
|
remark: '',
|
||||||
changeReason: '',
|
changeReason: '',
|
||||||
|
calculating: false,
|
||||||
|
adjustCalculateTimer: null,
|
||||||
|
adjustCalculateVersion: 0,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
feeSourceLabel(value) {
|
feeSourceLabel(value) {
|
||||||
return value === '手工录入' ? '手工录入' : '自动生成';
|
return ['手动录入', '手动添加', '手工录入'].includes(value) ? '手动录入' : '自动生成';
|
||||||
|
},
|
||||||
|
adjustBillingTypes(row) {
|
||||||
|
return ADJUST_BILLING_TYPE_MAP[row.billingFactor] || [];
|
||||||
|
},
|
||||||
|
handleAdjustBillingFactorChange(row) {
|
||||||
|
if (!this.adjustBillingTypes(row).includes(row.billingType)) row.billingType = '';
|
||||||
},
|
},
|
||||||
adjustTextMaxlength(prop) {
|
adjustTextMaxlength(prop) {
|
||||||
return ['billingFactor', 'billingType'].includes(prop) ? 100 : 255;
|
return ['billingFactor', 'billingType'].includes(prop) ? 100 : 255;
|
||||||
@@ -1148,6 +1360,10 @@ export default {
|
|||||||
},
|
},
|
||||||
handleAdjustDecimalInput(row, prop, value, changedField) {
|
handleAdjustDecimalInput(row, prop, value, changedField) {
|
||||||
row[prop] = this.normalizeAdjustDecimal(value);
|
row[prop] = this.normalizeAdjustDecimal(value);
|
||||||
|
if (!row.manualFee && row.id && ['transportQuantity', 'mileage'].includes(prop)) {
|
||||||
|
this.scheduleAdjustFeeCalculation(row);
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.recalculateAdjustRow(row, changedField);
|
this.recalculateAdjustRow(row, changedField);
|
||||||
},
|
},
|
||||||
handleAdjustFeeItemInput(row, feeItemName, value) {
|
handleAdjustFeeItemInput(row, feeItemName, value) {
|
||||||
@@ -1172,6 +1388,50 @@ export default {
|
|||||||
);
|
);
|
||||||
row.adjustAmount = Number((row.afterAmount - Number(row.originalAmount || 0)).toFixed(2));
|
row.adjustAmount = Number((row.afterAmount - Number(row.originalAmount || 0)).toFixed(2));
|
||||||
},
|
},
|
||||||
|
scheduleAdjustFeeCalculation(row) {
|
||||||
|
if (row.adjustCalculateTimer) clearTimeout(row.adjustCalculateTimer);
|
||||||
|
row.adjustCalculateVersion += 1;
|
||||||
|
row.calculating = true;
|
||||||
|
const version = row.adjustCalculateVersion;
|
||||||
|
row.adjustCalculateTimer = setTimeout(() => {
|
||||||
|
row.adjustCalculateTimer = null;
|
||||||
|
this.calculateAdjustRow(row, version);
|
||||||
|
}, 300);
|
||||||
|
},
|
||||||
|
async calculateAdjustRow(row, version) {
|
||||||
|
try {
|
||||||
|
const res = await api.calculateAdjustedFee({
|
||||||
|
detailId: this.adjustDialog.row?.id,
|
||||||
|
feeId: row.id,
|
||||||
|
transportQuantity: Number(row.transportQuantity || 0),
|
||||||
|
mileage: Number(row.mileage || 0),
|
||||||
|
freightAmount: Number(row.freightAmount || 0),
|
||||||
|
feeItems: Object.fromEntries(
|
||||||
|
Object.entries(row.feeItems || {}).map(([name, amount]) => [name, Number(amount || 0)])
|
||||||
|
),
|
||||||
|
});
|
||||||
|
if (version !== row.adjustCalculateVersion || !this.adjustDialog.visible) return;
|
||||||
|
const data = res.data?.data || res.data || res || {};
|
||||||
|
row.freightAmount = Number(data.freightAmount || 0);
|
||||||
|
row.feeItems = Object.fromEntries(
|
||||||
|
Object.entries(data.feeItems || {}).map(([name, amount]) => [name, Number(amount || 0)])
|
||||||
|
);
|
||||||
|
row.adjustAmount = Number(data.adjustAmount || 0);
|
||||||
|
row.afterAmount = Number(data.afterAmount || 0);
|
||||||
|
} catch (error) {
|
||||||
|
row.calculateError = error.message;
|
||||||
|
} finally {
|
||||||
|
if (version === row.adjustCalculateVersion) row.calculating = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
clearAdjustCalculations() {
|
||||||
|
this.adjustRows.forEach(row => {
|
||||||
|
if (row.adjustCalculateTimer) clearTimeout(row.adjustCalculateTimer);
|
||||||
|
row.adjustCalculateTimer = null;
|
||||||
|
row.adjustCalculateVersion += 1;
|
||||||
|
row.calculating = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
isFreightFeeItem(name) {
|
isFreightFeeItem(name) {
|
||||||
return String(name || '').includes('运费') || String(name || '').includes('运输费');
|
return String(name || '').includes('运费') || String(name || '').includes('运输费');
|
||||||
},
|
},
|
||||||
@@ -1180,15 +1440,20 @@ export default {
|
|||||||
this.$message.warning('没有可调整的费用明细');
|
this.$message.warning('没有可调整的费用明细');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (this.hasAdjustCalculationPending) {
|
||||||
|
this.$message.warning('费用正在重新计算,请稍候');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!this.validateManualAdjustRows()) return;
|
||||||
this.adjustDialog.submitting = true;
|
this.adjustDialog.submitting = true;
|
||||||
try {
|
try {
|
||||||
await api.adjustFee({
|
await api.adjustFee({
|
||||||
detailId: this.adjustDialog.row.id,
|
detailId: this.adjustDialog.row.id,
|
||||||
rows: this.adjustRows.map(row => ({
|
rows: this.adjustRows.map(row => ({
|
||||||
id: row.id,
|
id: row.id,
|
||||||
cargoName: row.cargoName,
|
cargoName: String(row.cargoName || '').trim(),
|
||||||
cargoType: row.cargoType,
|
cargoType: row.cargoType,
|
||||||
specification: row.specification,
|
specification: String(row.specification || '').trim(),
|
||||||
model: row.model,
|
model: row.model,
|
||||||
billingFactor: row.billingFactor,
|
billingFactor: row.billingFactor,
|
||||||
billingType: row.billingType,
|
billingType: row.billingType,
|
||||||
@@ -1210,6 +1475,32 @@ export default {
|
|||||||
this.adjustDialog.submitting = false;
|
this.adjustDialog.submitting = false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
validateManualAdjustRows() {
|
||||||
|
for (const [index, row] of this.adjustRows.entries()) {
|
||||||
|
if (!row.manualFee) continue;
|
||||||
|
if (!String(row.cargoName || '').trim()) {
|
||||||
|
this.$message.warning(`第${index + 1}行货物名称不能为空`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (String(row.cargoName).trim().length > 100) {
|
||||||
|
this.$message.warning(`第${index + 1}行货物名称不能超过100个字`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (String(row.specification || '').trim().length > 255) {
|
||||||
|
this.$message.warning(`第${index + 1}行规格不能超过255个字`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!ADJUST_BILLING_ELEMENTS.includes(row.billingFactor)) {
|
||||||
|
this.$message.warning(`第${index + 1}行请选择计费要素`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!this.adjustBillingTypes(row).includes(row.billingType)) {
|
||||||
|
this.$message.warning(`第${index + 1}行请选择计费要素对应的计费类型`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
async loadFeeDetail() {
|
async loadFeeDetail() {
|
||||||
if (!this.detailDialog.row) return;
|
if (!this.detailDialog.row) return;
|
||||||
this.detailDialog.loading = true;
|
this.detailDialog.loading = true;
|
||||||
@@ -1309,7 +1600,7 @@ export default {
|
|||||||
},
|
},
|
||||||
openTransferDialog() {
|
openTransferDialog() {
|
||||||
this.transferDialog.visible = true;
|
this.transferDialog.visible = true;
|
||||||
this.transferForm = { settlementBillType: 'formal' };
|
this.transferForm = { settlementBillType: this.isReceivable ? 'formal' : 'pre' };
|
||||||
this.transferQuery = {};
|
this.transferQuery = {};
|
||||||
this.transferRows = [];
|
this.transferRows = [];
|
||||||
this.transferSelection = [];
|
this.transferSelection = [];
|
||||||
@@ -1439,7 +1730,9 @@ export default {
|
|||||||
try {
|
try {
|
||||||
const transferRows = await this.resolveTransferContractIds(this.transferSelection);
|
const transferRows = await this.resolveTransferContractIds(this.transferSelection);
|
||||||
if (!transferRows) return;
|
if (!transferRows) return;
|
||||||
const settlementBillType = this.transferForm.settlementBillType;
|
const settlementBillType = this.isReceivable
|
||||||
|
? 'formal'
|
||||||
|
: this.transferForm.settlementBillType;
|
||||||
const transferToken = createSettlementTransfer({
|
const transferToken = createSettlementTransfer({
|
||||||
settlementBillType,
|
settlementBillType,
|
||||||
settlementType: this.settlementType || transferRows[0]?.settlementType,
|
settlementType: this.settlementType || transferRows[0]?.settlementType,
|
||||||
@@ -1858,6 +2151,7 @@ export default {
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
gap: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.settlement-detail-page__pagination {
|
.settlement-detail-page__pagination {
|
||||||
|
|||||||
@@ -942,14 +942,7 @@
|
|||||||
<el-input v-model="receiptForm.accountHolderName" maxlength="50" />
|
<el-input v-model="receiptForm.accountHolderName" maxlength="50" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="开户行" prop="bankName">
|
<el-form-item label="开户行" prop="bankName">
|
||||||
<el-select v-model="receiptForm.bankName" filterable clearable>
|
<el-input v-model="receiptForm.bankName" maxlength="100" />
|
||||||
<el-option
|
|
||||||
v-for="item in bankListOptions"
|
|
||||||
:key="item.value"
|
|
||||||
:label="item.label"
|
|
||||||
:value="item.value"
|
|
||||||
/>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="收款账号" prop="bankAccount">
|
<el-form-item label="收款账号" prop="bankAccount">
|
||||||
<el-input v-model="receiptForm.bankAccount" maxlength="50" />
|
<el-input v-model="receiptForm.bankAccount" maxlength="50" />
|
||||||
@@ -1030,14 +1023,7 @@
|
|||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="开户行名称" prop="bankName">
|
<el-form-item label="开户行名称" prop="bankName">
|
||||||
<el-select v-model="invoiceForm.bankName" filterable clearable>
|
<el-input v-model="invoiceForm.bankName" maxlength="100" />
|
||||||
<el-option
|
|
||||||
v-for="item in bankListOptions"
|
|
||||||
:key="item.value"
|
|
||||||
:label="item.label"
|
|
||||||
:value="item.value"
|
|
||||||
/>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="银行账号" prop="bankAccount">
|
<el-form-item label="银行账号" prop="bankAccount">
|
||||||
<el-input v-model="invoiceForm.bankAccount" maxlength="50" />
|
<el-input v-model="invoiceForm.bankAccount" maxlength="50" />
|
||||||
@@ -1601,7 +1587,6 @@ export default {
|
|||||||
customerNatureOptions: [],
|
customerNatureOptions: [],
|
||||||
customerTypeOptions: [],
|
customerTypeOptions: [],
|
||||||
businessScopeOptions: [],
|
businessScopeOptions: [],
|
||||||
bankListOptions: [],
|
|
||||||
qualificationTypeOptions: [],
|
qualificationTypeOptions: [],
|
||||||
creditLevelOptions: ['A', 'B', 'C', 'D', 'E', 'F'].map(item => ({
|
creditLevelOptions: ['A', 'B', 'C', 'D', 'E', 'F'].map(item => ({
|
||||||
label: `${item}级`,
|
label: `${item}级`,
|
||||||
@@ -2583,7 +2568,6 @@ export default {
|
|||||||
customerTypeColumn.dicData = [{ label: '全部', value: '' }, ...options];
|
customerTypeColumn.dicData = [{ label: '全部', value: '' }, ...options];
|
||||||
});
|
});
|
||||||
this.loadDictOptions('scope_of_business', 'businessScopeOptions');
|
this.loadDictOptions('scope_of_business', 'businessScopeOptions');
|
||||||
this.loadDictOptions('bank_list', 'bankListOptions');
|
|
||||||
this.loadDictOptions('customer_attachment_types', 'qualificationTypeOptions', [], () => {
|
this.loadDictOptions('customer_attachment_types', 'qualificationTypeOptions', [], () => {
|
||||||
this.sortQualificationFiles();
|
this.sortQualificationFiles();
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user