1、新增结算模块
2、调整业务模块 3、调整客商模块
This commit is contained in:
@@ -0,0 +1,764 @@
|
||||
<template>
|
||||
<el-dialog v-model="visible" :title="title" width="96%" top="2vh" append-to-body destroy-on-close>
|
||||
<div v-loading="loading" class="formal-editor">
|
||||
<section class="formal-editor__section">
|
||||
<div class="dialog-section-title">结算基本信息</div>
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
label-position="right"
|
||||
label-width="auto"
|
||||
>
|
||||
<el-row :gutter="24">
|
||||
<el-col v-for="field in fields" :key="field.prop" :span="8">
|
||||
<el-form-item :label="field.label" :prop="field.prop">
|
||||
<el-date-picker
|
||||
v-if="field.type === 'date' && editable"
|
||||
v-model="form[field.prop]"
|
||||
type="date"
|
||||
value-format="YYYY-MM-DD"
|
||||
placeholder="请选择"
|
||||
/>
|
||||
<el-select
|
||||
v-else-if="field.type === 'contract' && editable"
|
||||
v-model="form.contractId"
|
||||
filterable
|
||||
remote
|
||||
:remote-method="loadContracts"
|
||||
placeholder="请选择合同"
|
||||
@change="handleContractChange"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in contracts"
|
||||
:key="item.id"
|
||||
:label="`${item.contractNo} / ${item.contractName}`"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
<el-input-number
|
||||
v-else-if="field.type === 'number' && editable"
|
||||
v-model="form[field.prop]"
|
||||
:min="0.000001"
|
||||
:precision="6"
|
||||
:controls="false"
|
||||
/>
|
||||
<span v-else-if="field.money">{{ formatMoney(form[field.prop]) }}</span>
|
||||
<span v-else-if="field.type === 'contract'">{{
|
||||
displayValue(form.contractName)
|
||||
}}</span>
|
||||
<span v-else>{{ displayValue(form[field.prop]) }}</span>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input
|
||||
v-if="editable"
|
||||
v-model="form.remark"
|
||||
type="textarea"
|
||||
maxlength="200"
|
||||
show-word-limit
|
||||
/>
|
||||
<span v-else>{{ displayValue(form.remark) }}</span>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</section>
|
||||
|
||||
<section class="formal-editor__section">
|
||||
<div class="formal-editor__section-head">
|
||||
<div class="dialog-section-title">来源预结算单</div>
|
||||
<div class="formal-editor__actions">
|
||||
<el-button v-if="editable" type="primary" plain @click="openCandidateDialog"
|
||||
>选择预结算单</el-button
|
||||
>
|
||||
<el-button v-if="editable" type="primary" plain @click="openDetailDialog"
|
||||
>选择结算明细</el-button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<el-table :data="sources" border>
|
||||
<el-table-column type="index" label="序号" width="64" align="center" />
|
||||
<el-table-column
|
||||
v-for="column in sourceTableColumns"
|
||||
:key="column.prop"
|
||||
v-bind="column"
|
||||
align="center"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<span v-if="column.money">{{ formatMoney(row[column.prop]) }}</span>
|
||||
<span v-else>{{ displayValue(row[column.prop]) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column v-if="editable" label="操作" width="90" align="center">
|
||||
<template #default="{ $index }"
|
||||
><el-link type="danger" @click="removeSource($index)">删除</el-link></template
|
||||
>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</section>
|
||||
|
||||
<section v-if="details.length" class="formal-editor__section">
|
||||
<div class="dialog-section-title">结算明细</div>
|
||||
<el-table :data="details" border>
|
||||
<el-table-column type="index" label="序号" width="64" fixed="left" align="center" />
|
||||
<el-table-column
|
||||
v-for="column in detailTableColumns"
|
||||
:key="column.prop"
|
||||
v-bind="column"
|
||||
align="center"
|
||||
show-overflow-tooltip
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<span v-if="column.money">{{ formatMoney(row[column.prop]) }}</span>
|
||||
<span v-else>{{ displayValue(row[column.prop]) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column v-if="editable" label="操作" width="150" fixed="right" align="center">
|
||||
<template #default="{ row, $index }">
|
||||
<div class="formal-editor__links">
|
||||
<el-link v-if="row.formalSettlementId" type="primary" @click="openAdjustDialog(row)"
|
||||
>调整</el-link
|
||||
>
|
||||
<el-link
|
||||
v-if="!row.sourcePreSettlementId"
|
||||
type="danger"
|
||||
@click="removeDetail($index)"
|
||||
>删除</el-link
|
||||
>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</section>
|
||||
|
||||
<section class="formal-editor__section">
|
||||
<div class="dialog-section-title">附件</div>
|
||||
<vehicle-attachment-upload
|
||||
v-model="attachments"
|
||||
:readonly="!editable"
|
||||
:multiple="true"
|
||||
:limit="20"
|
||||
:max-size="500"
|
||||
:file-types="attachmentFileTypes"
|
||||
/>
|
||||
</section>
|
||||
|
||||
<section v-if="readonly && payments.length" class="formal-editor__section">
|
||||
<div class="dialog-section-title">付款信息</div>
|
||||
<el-table :data="payments" border>
|
||||
<el-table-column type="index" label="序号" width="64" align="center" />
|
||||
<el-table-column
|
||||
v-for="column in paymentTableColumns"
|
||||
:key="column.prop"
|
||||
v-bind="column"
|
||||
align="center"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<span v-if="column.money">{{ formatMoney(row[column.prop]) }}</span>
|
||||
<span v-else-if="column.prop === 'paymentTypeName'">{{
|
||||
row.paymentType === 'final' ? '尾款' : displayValue(row.paymentType)
|
||||
}}</span>
|
||||
<span v-else-if="column.prop === 'billStatusName'">{{
|
||||
row.billStatus === 'reviewing' ? '审批中' : displayValue(row.billStatus)
|
||||
}}</span>
|
||||
<span v-else>{{ displayValue(row[column.prop]) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</section>
|
||||
</div>
|
||||
<template #footer>
|
||||
<el-button @click="visible = false">取消</el-button>
|
||||
<el-button v-if="editable" type="primary" :loading="saving" @click="handleSave"
|
||||
>提交</el-button
|
||||
>
|
||||
</template>
|
||||
|
||||
<el-dialog v-model="candidate.visible" title="选择预结算单" width="88%" append-to-body>
|
||||
<el-form :model="candidate.query" inline label-position="right" label-width="160px">
|
||||
<el-form-item label="预结算单号"
|
||||
><el-input v-model="candidate.query.preSettlementNo" clearable
|
||||
/></el-form-item>
|
||||
<el-form-item label="合同编号"
|
||||
><el-input v-model="candidate.query.contractNo" clearable
|
||||
/></el-form-item>
|
||||
<el-form-item
|
||||
><el-button @click="resetCandidates">重置</el-button
|
||||
><el-button type="primary" @click="loadCandidates">查询</el-button></el-form-item
|
||||
>
|
||||
</el-form>
|
||||
<el-table
|
||||
ref="candidateTable"
|
||||
v-loading="candidate.loading"
|
||||
:data="candidate.rows"
|
||||
border
|
||||
@selection-change="candidate.selected = $event"
|
||||
>
|
||||
<el-table-column type="selection" width="52" fixed="left" align="center" />
|
||||
<el-table-column type="index" label="序号" width="64" fixed="left" align="center" />
|
||||
<el-table-column
|
||||
v-for="column in candidateTableColumns"
|
||||
:key="column.prop"
|
||||
v-bind="column"
|
||||
align="center"
|
||||
>
|
||||
<template #default="{ row }"
|
||||
><span v-if="column.money">{{ formatMoney(row[column.prop]) }}</span
|
||||
><span v-else>{{ displayValue(row[column.prop]) }}</span></template
|
||||
>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div class="formal-editor__pagination">
|
||||
<el-pagination
|
||||
v-model:current-page="candidate.page.current"
|
||||
v-model:page-size="candidate.page.size"
|
||||
:total="candidate.page.total"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
@current-change="loadCandidates"
|
||||
@size-change="loadCandidates"
|
||||
/>
|
||||
</div>
|
||||
<template #footer
|
||||
><el-button @click="candidate.visible = false">取消</el-button
|
||||
><el-button type="primary" @click="confirmCandidates">确定</el-button></template
|
||||
>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog v-model="detailCandidate.visible" title="选择结算明细" width="92%" append-to-body>
|
||||
<el-form :model="detailCandidate.query" inline label-position="right" label-width="160px">
|
||||
<el-form-item label="批次号"
|
||||
><el-input v-model="detailCandidate.query.batchNo" clearable
|
||||
/></el-form-item>
|
||||
<el-form-item label="费用日期"
|
||||
><el-date-picker
|
||||
v-model="detailCandidate.query.feeDateRange"
|
||||
type="daterange"
|
||||
value-format="YYYY-MM-DD"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
/></el-form-item>
|
||||
<el-form-item
|
||||
><el-button @click="resetDetailCandidates">重置</el-button
|
||||
><el-button type="primary" @click="loadDetailCandidates">查询</el-button></el-form-item
|
||||
>
|
||||
</el-form>
|
||||
<el-table
|
||||
v-loading="detailCandidate.loading"
|
||||
:data="detailCandidate.rows"
|
||||
border
|
||||
@selection-change="detailCandidate.selected = $event"
|
||||
>
|
||||
<el-table-column type="selection" width="52" fixed="left" align="center" />
|
||||
<el-table-column type="index" label="序号" width="64" fixed="left" align="center" />
|
||||
<el-table-column
|
||||
v-for="column in candidateDetailTableColumns"
|
||||
:key="column.prop"
|
||||
v-bind="column"
|
||||
align="center"
|
||||
>
|
||||
<template #default="{ row }"
|
||||
><span v-if="column.money">{{ formatMoney(row[column.prop]) }}</span
|
||||
><span v-else>{{ displayValue(row[column.prop]) }}</span></template
|
||||
>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div class="formal-editor__pagination">
|
||||
<el-pagination
|
||||
v-model:current-page="detailCandidate.page.current"
|
||||
v-model:page-size="detailCandidate.page.size"
|
||||
:total="detailCandidate.page.total"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
@current-change="loadDetailCandidates"
|
||||
@size-change="loadDetailCandidates"
|
||||
/>
|
||||
</div>
|
||||
<template #footer
|
||||
><el-button @click="detailCandidate.visible = false">取消</el-button
|
||||
><el-button type="primary" @click="confirmDetailCandidates">确定</el-button></template
|
||||
>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog v-model="adjust.visible" title="结算明细调整" width="92%" append-to-body>
|
||||
<el-table v-loading="adjust.loading" :data="adjust.rows" border>
|
||||
<el-table-column type="index" label="序号" width="64" align="center" />
|
||||
<el-table-column prop="cargoName" label="货物名称" min-width="130" align="center" />
|
||||
<el-table-column prop="cargoType" label="货物类型" min-width="130" align="center" />
|
||||
<el-table-column label="运输总量" min-width="130" align="center"
|
||||
><template #default="{ row }"
|
||||
><el-input-number
|
||||
v-model="row.transportQuantity"
|
||||
:min="0"
|
||||
:precision="6"
|
||||
:controls="false" /></template
|
||||
></el-table-column>
|
||||
<el-table-column label="里程(KM)" min-width="130" align="center"
|
||||
><template #default="{ row }"
|
||||
><el-input-number
|
||||
v-model="row.mileage"
|
||||
:min="0"
|
||||
:precision="2"
|
||||
:controls="false" /></template
|
||||
></el-table-column>
|
||||
<el-table-column label="运输单价" min-width="130" align="center"
|
||||
><template #default="{ row }"
|
||||
><el-input-number
|
||||
v-model="row.unitPrice"
|
||||
:min="0"
|
||||
:precision="2"
|
||||
:controls="false" /></template
|
||||
></el-table-column>
|
||||
<el-table-column label="运费" min-width="130" align="center"
|
||||
><template #default="{ row }"
|
||||
><el-input-number
|
||||
v-model="row.freightAmount"
|
||||
:min="0"
|
||||
:precision="2"
|
||||
:controls="false" /></template
|
||||
></el-table-column>
|
||||
<el-table-column label="结算金额(含税)" min-width="165" align="center"
|
||||
><template #default="{ row }"
|
||||
><el-input-number
|
||||
v-model="row.settlementAmountTax"
|
||||
:min="0"
|
||||
:precision="2"
|
||||
:controls="false" /></template
|
||||
></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"
|
||||
><template #default="{ row }"><el-input v-model="row.remark" maxlength="200" /></template
|
||||
></el-table-column>
|
||||
</el-table>
|
||||
<el-form label-position="right" label-width="auto" class="formal-editor__adjust-reason">
|
||||
<el-form-item label="调整原因" required
|
||||
><el-input v-model="adjust.reason" type="textarea" maxlength="200" show-word-limit
|
||||
/></el-form-item>
|
||||
</el-form>
|
||||
<template #footer
|
||||
><el-button @click="adjust.visible = false">取消</el-button
|
||||
><el-button type="primary" :loading="adjust.saving" @click="saveAdjustment"
|
||||
>保存</el-button
|
||||
></template
|
||||
>
|
||||
</el-dialog>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
adjustDetail,
|
||||
getCandidateDetails,
|
||||
getCandidates,
|
||||
getContractOptions,
|
||||
getDetail,
|
||||
getDetailFees,
|
||||
save,
|
||||
} from '@/api/settlement/formalSettlement';
|
||||
import {
|
||||
createFormalSettlementForm,
|
||||
formalSettlementFormFields,
|
||||
} from '@/option/settlement/formalSettlementForm';
|
||||
import {
|
||||
candidateColumns,
|
||||
candidateDetailColumns,
|
||||
detailColumns,
|
||||
paymentColumns,
|
||||
sourceColumns,
|
||||
} from '@/option/settlement/formalSettlementTable';
|
||||
|
||||
export default {
|
||||
name: 'FormalSettlementEditor',
|
||||
props: { modelValue: Boolean, recordId: [String, Number], readonly: Boolean },
|
||||
emits: ['update:modelValue', 'success'],
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
saving: false,
|
||||
form: createFormalSettlementForm(),
|
||||
sources: [],
|
||||
details: [],
|
||||
payments: [],
|
||||
attachments: [],
|
||||
attachmentFileTypes: [
|
||||
'pdf',
|
||||
'bmp',
|
||||
'jpeg',
|
||||
'png',
|
||||
'jpg',
|
||||
'doc',
|
||||
'docx',
|
||||
'ppt',
|
||||
'pptx',
|
||||
'xlsx',
|
||||
'xls',
|
||||
'eml',
|
||||
'msg',
|
||||
'zip',
|
||||
'rar',
|
||||
],
|
||||
contracts: [],
|
||||
fields: formalSettlementFormFields,
|
||||
sourceTableColumns: sourceColumns,
|
||||
detailTableColumns: detailColumns,
|
||||
paymentTableColumns: paymentColumns,
|
||||
candidateTableColumns: candidateColumns,
|
||||
candidateDetailTableColumns: candidateDetailColumns,
|
||||
rules: {
|
||||
contractId: [{ required: true, message: '请选择合同', trigger: 'change' }],
|
||||
exchangeRateDate: [{ required: true, message: '请选择汇率日期', trigger: 'change' }],
|
||||
exchangeRate: [{ required: true, message: '请输入结算汇率', trigger: 'blur' }],
|
||||
},
|
||||
candidate: {
|
||||
visible: false,
|
||||
loading: false,
|
||||
query: {},
|
||||
rows: [],
|
||||
selected: [],
|
||||
page: { current: 1, size: 10, total: 0 },
|
||||
},
|
||||
detailCandidate: {
|
||||
visible: false,
|
||||
loading: false,
|
||||
query: {},
|
||||
rows: [],
|
||||
selected: [],
|
||||
page: { current: 1, size: 10, total: 0 },
|
||||
},
|
||||
adjust: {
|
||||
visible: false,
|
||||
loading: false,
|
||||
saving: false,
|
||||
detailId: null,
|
||||
reason: '',
|
||||
rows: [],
|
||||
},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
visible: {
|
||||
get() {
|
||||
return this.modelValue;
|
||||
},
|
||||
set(value) {
|
||||
this.$emit('update:modelValue', value);
|
||||
},
|
||||
},
|
||||
editable() {
|
||||
return !this.readonly;
|
||||
},
|
||||
title() {
|
||||
return this.readonly ? '查看正式结算单' : this.recordId ? '编辑正式结算单' : '新增正式结算单';
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
modelValue(value) {
|
||||
if (value) this.initialize();
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
async initialize() {
|
||||
this.form = createFormalSettlementForm();
|
||||
this.sources = [];
|
||||
this.details = [];
|
||||
this.payments = [];
|
||||
this.attachments = [];
|
||||
await this.loadContracts();
|
||||
if (!this.recordId) {
|
||||
this.form.exchangeRateDate = this.$dayjs().format('YYYY-MM-DD');
|
||||
return;
|
||||
}
|
||||
this.loading = true;
|
||||
try {
|
||||
const response = await getDetail(this.recordId);
|
||||
const data = this.unwrapData(response);
|
||||
this.form = {
|
||||
...createFormalSettlementForm(),
|
||||
...data,
|
||||
sourcePreSettlementIds: (data.sources || []).map(item => item.preSettlementId),
|
||||
sourceDetailIds: (data.details || [])
|
||||
.filter(item => !item.sourcePreSettlementId)
|
||||
.map(item => item.sourceDetailId),
|
||||
};
|
||||
this.sources = data.sources || [];
|
||||
this.details = data.details || [];
|
||||
this.payments = data.payments || [];
|
||||
this.attachments = this.parseAttachments(data.attachmentsJson);
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
async loadContracts(keyword = '') {
|
||||
const response = await getContractOptions(keyword);
|
||||
this.contracts = this.unwrapData(response) || [];
|
||||
},
|
||||
handleContractChange(id) {
|
||||
const contract = this.contracts.find(item => String(item.id) === String(id));
|
||||
if (!contract) return;
|
||||
Object.assign(this.form, contract, {
|
||||
contractId: contract.id,
|
||||
settlementTypeName: contract.settlementType === 'receivable' ? '应收' : '应付',
|
||||
});
|
||||
this.sources = [];
|
||||
this.details = [];
|
||||
this.form.sourcePreSettlementIds = [];
|
||||
this.form.sourceDetailIds = [];
|
||||
},
|
||||
openCandidateDialog() {
|
||||
this.candidate.visible = true;
|
||||
this.candidate.page.current = 1;
|
||||
this.loadCandidates();
|
||||
},
|
||||
async loadCandidates() {
|
||||
this.candidate.loading = true;
|
||||
try {
|
||||
const response = await getCandidates(
|
||||
this.candidate.page.current,
|
||||
this.candidate.page.size,
|
||||
{
|
||||
...this.candidate.query,
|
||||
contractId: this.form.contractId || undefined,
|
||||
}
|
||||
);
|
||||
const data = this.unwrapData(response);
|
||||
this.candidate.rows = data.records || [];
|
||||
this.candidate.page.total = data.total || 0;
|
||||
} finally {
|
||||
this.candidate.loading = false;
|
||||
}
|
||||
},
|
||||
resetCandidates() {
|
||||
this.candidate.query = {};
|
||||
this.candidate.page.current = 1;
|
||||
this.loadCandidates();
|
||||
},
|
||||
confirmCandidates() {
|
||||
const merged = [...this.sources, ...this.candidate.selected];
|
||||
const map = new Map(
|
||||
merged.map(item => [
|
||||
String(item.preSettlementId || item.id),
|
||||
{ ...item, preSettlementId: item.preSettlementId || item.id },
|
||||
])
|
||||
);
|
||||
this.sources = [...map.values()];
|
||||
this.form.sourcePreSettlementIds = this.sources.map(item => item.preSettlementId);
|
||||
if (this.sources.length) {
|
||||
Object.assign(this.form, this.sources[0], {
|
||||
contractId: this.sources[0].contractId,
|
||||
formalSettlementNo: this.form.formalSettlementNo,
|
||||
});
|
||||
}
|
||||
this.form.settlementAmount = this.sources.reduce(
|
||||
(sum, item) => sum + Number(item.settlementAmount || 0),
|
||||
0
|
||||
);
|
||||
this.form.localSettlementAmount =
|
||||
this.form.settlementAmount * Number(this.form.exchangeRate || 1);
|
||||
this.candidate.visible = false;
|
||||
},
|
||||
removeSource(index) {
|
||||
this.sources.splice(index, 1);
|
||||
this.form.sourcePreSettlementIds = this.sources.map(item => item.preSettlementId || item.id);
|
||||
},
|
||||
openDetailDialog() {
|
||||
if (!this.form.contractId) return this.$message.warning('请先选择合同');
|
||||
this.detailCandidate.visible = true;
|
||||
this.detailCandidate.page.current = 1;
|
||||
this.loadDetailCandidates();
|
||||
},
|
||||
async loadDetailCandidates() {
|
||||
this.detailCandidate.loading = true;
|
||||
try {
|
||||
const range = this.detailCandidate.query.feeDateRange || [];
|
||||
const params = {
|
||||
contractId: this.form.contractId,
|
||||
settlementType: this.form.settlementType,
|
||||
batchNo: this.detailCandidate.query.batchNo,
|
||||
feeStartDate: range[0],
|
||||
feeEndDate: range[1],
|
||||
};
|
||||
const response = await getCandidateDetails(
|
||||
this.detailCandidate.page.current,
|
||||
this.detailCandidate.page.size,
|
||||
params
|
||||
);
|
||||
const data = this.unwrapData(response);
|
||||
this.detailCandidate.rows = data.records || [];
|
||||
this.detailCandidate.page.total = data.total || 0;
|
||||
} finally {
|
||||
this.detailCandidate.loading = false;
|
||||
}
|
||||
},
|
||||
resetDetailCandidates() {
|
||||
this.detailCandidate.query = {};
|
||||
this.detailCandidate.page.current = 1;
|
||||
this.loadDetailCandidates();
|
||||
},
|
||||
confirmDetailCandidates() {
|
||||
const existing = new Map(
|
||||
this.details
|
||||
.filter(item => !item.formalSettlementId)
|
||||
.map(item => [String(item.sourceDetailId || item.id), item])
|
||||
);
|
||||
this.detailCandidate.selected.forEach(item =>
|
||||
existing.set(String(item.id), {
|
||||
...item,
|
||||
sourceDetailId: item.id,
|
||||
settlementAmountTax: item.totalAmount,
|
||||
})
|
||||
);
|
||||
this.details = [
|
||||
...this.details.filter(item => item.formalSettlementId),
|
||||
...existing.values(),
|
||||
];
|
||||
this.form.sourceDetailIds = [...existing.values()].map(item => item.sourceDetailId);
|
||||
this.form.settlementAmount =
|
||||
this.sources.reduce((sum, item) => sum + Number(item.settlementAmount || 0), 0) +
|
||||
[...existing.values()].reduce((sum, item) => sum + Number(item.totalAmount || 0), 0);
|
||||
this.detailCandidate.visible = false;
|
||||
},
|
||||
removeDetail(index) {
|
||||
this.details.splice(index, 1);
|
||||
this.form.sourceDetailIds = this.details
|
||||
.filter(item => !item.sourcePreSettlementId)
|
||||
.map(item => item.sourceDetailId);
|
||||
},
|
||||
async openAdjustDialog(row) {
|
||||
this.adjust = {
|
||||
visible: true,
|
||||
loading: true,
|
||||
saving: false,
|
||||
detailId: row.id,
|
||||
reason: '',
|
||||
rows: [],
|
||||
};
|
||||
try {
|
||||
const response = await getDetailFees(row.id);
|
||||
const data = this.unwrapData(response);
|
||||
this.adjust.rows = (data || []).map(item => ({
|
||||
...item,
|
||||
feeItems: this.parseFeeItems(item.feeItemsJson),
|
||||
}));
|
||||
} finally {
|
||||
this.adjust.loading = false;
|
||||
}
|
||||
},
|
||||
async saveAdjustment() {
|
||||
if (!this.adjust.reason.trim()) return this.$message.warning('请输入调整原因');
|
||||
this.adjust.saving = true;
|
||||
try {
|
||||
await adjustDetail({
|
||||
detailId: this.adjust.detailId,
|
||||
changeReason: this.adjust.reason,
|
||||
rows: this.adjust.rows,
|
||||
});
|
||||
this.$message.success('调整保存成功');
|
||||
this.adjust.visible = false;
|
||||
await this.initialize();
|
||||
} finally {
|
||||
this.adjust.saving = false;
|
||||
}
|
||||
},
|
||||
async handleSave() {
|
||||
await this.$refs.formRef.validate();
|
||||
if (!this.form.sourcePreSettlementIds.length && !this.form.sourceDetailIds.length) {
|
||||
return this.$message.warning('请选择预结算单或结算明细');
|
||||
}
|
||||
this.saving = true;
|
||||
try {
|
||||
await save({
|
||||
id: this.form.id,
|
||||
contractId: this.form.contractId,
|
||||
settlementType: this.form.settlementType,
|
||||
sourcePreSettlementIds: this.form.sourcePreSettlementIds,
|
||||
sourceDetailIds: this.form.sourceDetailIds,
|
||||
exchangeRateDate: this.form.exchangeRateDate,
|
||||
exchangeRate: this.form.exchangeRate,
|
||||
attachmentsJson: JSON.stringify(this.attachments || []),
|
||||
remark: this.form.remark,
|
||||
});
|
||||
this.$message.success('保存成功');
|
||||
this.visible = false;
|
||||
this.$emit('success');
|
||||
} finally {
|
||||
this.saving = false;
|
||||
}
|
||||
},
|
||||
displayValue(value) {
|
||||
return value === null || value === undefined || value === '' ? '-' : value;
|
||||
},
|
||||
unwrapData(response) {
|
||||
const body = response?.data || response || {};
|
||||
return body?.data || body;
|
||||
},
|
||||
formatMoney(value) {
|
||||
return `${Number(value || 0).toFixed(2)} RMB`;
|
||||
},
|
||||
parseFeeItems(value) {
|
||||
if (!value) return {};
|
||||
if (typeof value === 'object') return value;
|
||||
try {
|
||||
return JSON.parse(value);
|
||||
} catch {
|
||||
return {};
|
||||
}
|
||||
},
|
||||
parseAttachments(value) {
|
||||
if (!value) return [];
|
||||
if (Array.isArray(value)) return value;
|
||||
try {
|
||||
const parsed = JSON.parse(value);
|
||||
return Array.isArray(parsed) ? parsed : [];
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.formal-editor__section {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
.formal-editor__section-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.formal-editor__actions {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
.formal-editor__links {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
}
|
||||
.formal-editor :deep(.el-form-item) {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.formal-editor :deep(.el-table) {
|
||||
--el-table-border-color: #eff1f7;
|
||||
}
|
||||
.formal-editor :deep(.el-table__body tr:nth-child(even) > td.el-table__cell) {
|
||||
background: #fafafa;
|
||||
}
|
||||
.formal-editor__pagination {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin-top: 12px;
|
||||
}
|
||||
.formal-editor__adjust-reason {
|
||||
margin-top: 16px;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user