7f70052afd
- payment 模块所有表单页底栏按钮顺序及布局全量对齐,取消居中改为右对齐浮动 - settlement 模块弹窗及独立页 footer 添加 sticky 浮动,主操作按钮样式调整为 plain - 彻底修复浮动底栏不吸底问题,所有相关 footer 由 position: sticky 改为 position: fixed - 统一 fixed 底栏样式模板,包含左偏移支持侧栏折叠和横向布局切换 - 更新项目文档,明确浮动底栏必须用 fixed 定位,避免 overflow:hidden 祖先导致 sticky 失效 - 清理所有残留 sticky 样式,确保底栏始终固定在视口底部,提升用户体验
820 lines
29 KiB
Vue
820 lines
29 KiB
Vue
<template>
|
|
<basic-container class="receipt-form-page">
|
|
<div class="archive-page-form__title">
|
|
{{ readonly ? '查看收票登记' : recordId ? '编辑收票登记' : '新增收票登记' }}
|
|
</div>
|
|
|
|
<el-form
|
|
ref="formRef"
|
|
:model="form"
|
|
:rules="rules"
|
|
label-position="right"
|
|
label-width="auto"
|
|
class="receipt-form-page__form"
|
|
>
|
|
<section-card title="收票基本信息">
|
|
<el-row :gutter="32">
|
|
<el-col :span="6">
|
|
<el-form-item label="发票号码" prop="invoiceNo">
|
|
<el-input v-model="form.invoiceNo" readonly placeholder="请选择金蝶票据池发票">
|
|
<template v-if="!readonly" #append>
|
|
<el-tooltip content="查询金蝶票据池" placement="top">
|
|
<el-button :icon="Search" @click="openInvoiceDialog" />
|
|
</el-tooltip>
|
|
</template>
|
|
</el-input>
|
|
</el-form-item>
|
|
</el-col>
|
|
<el-col :span="6">
|
|
<el-form-item label="开票日期">
|
|
<el-input v-model="form.invoiceDate" disabled placeholder="从金蝶票据池带出" />
|
|
</el-form-item>
|
|
</el-col>
|
|
<el-col :span="6">
|
|
<el-form-item label="发票类型">
|
|
<el-input v-model="form.invoiceType" disabled placeholder="从金蝶票据池带出" />
|
|
</el-form-item>
|
|
</el-col>
|
|
<el-col :span="6">
|
|
<el-form-item label="税率">
|
|
<el-input
|
|
:model-value="formatRate(form.taxRate)"
|
|
disabled
|
|
placeholder="从金蝶票据池带出"
|
|
/>
|
|
</el-form-item>
|
|
</el-col>
|
|
<el-col :span="6">
|
|
<el-form-item label="开票金额">
|
|
<el-input
|
|
:model-value="form.kingdeeInvoicePoolId ? formatMoney(form.invoiceAmount) : ''"
|
|
disabled
|
|
placeholder="从金蝶票据池带出"
|
|
/>
|
|
</el-form-item>
|
|
</el-col>
|
|
<el-col :span="6">
|
|
<el-form-item label="税额">
|
|
<el-input
|
|
:model-value="form.kingdeeInvoicePoolId ? formatMoney(form.taxAmount) : ''"
|
|
disabled
|
|
placeholder="从金蝶票据池带出"
|
|
/>
|
|
</el-form-item>
|
|
</el-col>
|
|
<el-col :span="6">
|
|
<el-form-item label="受票单位">
|
|
<el-input v-model="form.receiverName" disabled placeholder="从金蝶票据池带出" />
|
|
</el-form-item>
|
|
</el-col>
|
|
<el-col :span="6">
|
|
<el-form-item label="开票单位">
|
|
<el-input v-model="form.issuerName" disabled placeholder="从金蝶票据池带出" />
|
|
</el-form-item>
|
|
</el-col>
|
|
<el-col :span="6">
|
|
<el-form-item label="关联结算单" prop="settlementIds">
|
|
<el-input :model-value="settlementLabel" readonly placeholder="请选择应付正式结算单">
|
|
<template v-if="!readonly" #append>
|
|
<el-tooltip content="选择正式结算单" placement="top">
|
|
<el-button :icon="Search" @click="openSettlementDialog" />
|
|
</el-tooltip>
|
|
</template>
|
|
</el-input>
|
|
</el-form-item>
|
|
</el-col>
|
|
<el-col :span="6">
|
|
<el-form-item label="开户行">
|
|
<el-input v-model="form.bankName" disabled placeholder="从金蝶票据池带出" />
|
|
</el-form-item>
|
|
</el-col>
|
|
<el-col :span="6">
|
|
<el-form-item label="开户账号">
|
|
<el-input v-model="form.bankAccount" disabled placeholder="从金蝶票据池带出" />
|
|
</el-form-item>
|
|
</el-col>
|
|
<el-col :span="6">
|
|
<el-form-item label="开票行">
|
|
<el-input v-model="form.issuingBank" disabled placeholder="从金蝶票据池带出" />
|
|
</el-form-item>
|
|
</el-col>
|
|
<el-col :span="6">
|
|
<el-form-item label="电话">
|
|
<el-input v-model="form.phone" :disabled="readonly" maxlength="50" />
|
|
</el-form-item>
|
|
</el-col>
|
|
<el-col :span="6">
|
|
<el-form-item label="客户邮箱" prop="customerEmails">
|
|
<el-select
|
|
v-model="form.customerEmails"
|
|
:disabled="readonly"
|
|
multiple
|
|
filterable
|
|
allow-create
|
|
default-first-option
|
|
:multiple-limit="3"
|
|
>
|
|
<el-option
|
|
v-for="email in customerEmailOptions"
|
|
:key="email"
|
|
:label="email"
|
|
:value="email"
|
|
/>
|
|
</el-select>
|
|
</el-form-item>
|
|
</el-col>
|
|
<el-col :span="6">
|
|
<el-form-item label="部门邮箱" prop="departmentEmails">
|
|
<el-select
|
|
v-model="form.departmentEmails"
|
|
:disabled="readonly"
|
|
multiple
|
|
filterable
|
|
allow-create
|
|
default-first-option
|
|
:multiple-limit="3"
|
|
>
|
|
<el-option
|
|
v-for="email in departmentEmailOptions"
|
|
:key="email"
|
|
:label="email"
|
|
:value="email"
|
|
/>
|
|
</el-select>
|
|
</el-form-item>
|
|
</el-col>
|
|
</el-row>
|
|
</section-card>
|
|
|
|
<section-card title="结算信息">
|
|
<el-table :data="form.settlements" border>
|
|
<el-table-column type="index" label="序号" width="64" align="center" />
|
|
<el-table-column label="结算单号" min-width="180" align="center">
|
|
<template #default="{ row }">
|
|
<el-link type="primary" @click="openSettlementDetail(row)">
|
|
{{ row.formalSettlementNo }}
|
|
</el-link>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column label="结算总金额" min-width="150" align="right">
|
|
<template #default="{ row }">{{ formatMoney(row.settlementAmount) }}</template>
|
|
</el-table-column>
|
|
<el-table-column label="已收票金额" min-width="150" align="right">
|
|
<template #default="{ row }">{{ formatMoney(row.receivedInvoiceAmount) }}</template>
|
|
</el-table-column>
|
|
<el-table-column label="分摊发票金额" min-width="180">
|
|
<template #default="{ row }">
|
|
<el-input-number
|
|
v-model="row.allocatedInvoiceAmount"
|
|
:disabled="readonly"
|
|
:min="0"
|
|
:max="remainingAmount(row)"
|
|
:precision="2"
|
|
:controls="false"
|
|
/>
|
|
</template>
|
|
</el-table-column>
|
|
</el-table>
|
|
</section-card>
|
|
|
|
<section-card title="附件信息">
|
|
<template #extra>
|
|
<el-button
|
|
v-if="form.attachments.length"
|
|
type="primary"
|
|
plain
|
|
:icon="Download"
|
|
@click="downloadAttachments"
|
|
>批量下载</el-button
|
|
>
|
|
</template>
|
|
<el-table :data="form.attachments" border>
|
|
<el-table-column type="index" label="序号" width="64" align="center" />
|
|
<el-table-column label="文件名" min-width="220">
|
|
<template #default="{ row }">{{ row.originalName || row.name || '-' }}</template>
|
|
</el-table-column>
|
|
<el-table-column label="附件描述" min-width="240">
|
|
<template #default="{ row }">
|
|
<el-input v-model="row.description" :disabled="readonly" maxlength="200" />
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column prop="size" label="文件大小" width="120" />
|
|
<el-table-column prop="uploadUserName" label="上传人" width="140" />
|
|
<el-table-column prop="uploadTime" label="上传时间" width="170" />
|
|
<el-table-column label="操作" width="120" align="center">
|
|
<template #default="{ row, $index }">
|
|
<div class="receipt-form-page__links">
|
|
<el-link type="primary" :href="row.url || row.link" target="_blank">查看</el-link>
|
|
<el-link v-if="!readonly" type="danger" @click="form.attachments.splice($index, 1)"
|
|
>删除</el-link
|
|
>
|
|
</div>
|
|
</template>
|
|
</el-table-column>
|
|
</el-table>
|
|
<vehicle-attachment-upload
|
|
v-model="form.attachments"
|
|
:readonly="readonly"
|
|
:multiple="true"
|
|
:limit="20"
|
|
:max-size="500"
|
|
:file-types="attachmentFileTypes"
|
|
:show-file-list="false"
|
|
button-text="上传附件"
|
|
class="receipt-form-page__uploader"
|
|
@change="normalizeAttachments"
|
|
/>
|
|
</section-card>
|
|
|
|
<section-card title="备注">
|
|
<el-form-item class="receipt-form-page__remark">
|
|
<el-input
|
|
v-model="form.remark"
|
|
:disabled="readonly"
|
|
type="textarea"
|
|
:rows="2"
|
|
maxlength="200"
|
|
show-word-limit
|
|
/>
|
|
</el-form-item>
|
|
</section-card>
|
|
|
|
<div class="receipt-form-page__actions">
|
|
<el-button @click="goBack">返回</el-button>
|
|
<el-button v-if="!readonly" type="primary" plain @click="syncReferenceData">同步</el-button>
|
|
<el-button
|
|
v-if="!readonly && canSave"
|
|
type="primary"
|
|
plain
|
|
@click="saveDraft"
|
|
>保存</el-button>
|
|
<el-button
|
|
v-if="!readonly && canSave && hasPermission('invoice_receipt_submit')"
|
|
type="primary"
|
|
@click="submitForm"
|
|
>提交</el-button>
|
|
</div>
|
|
</el-form>
|
|
|
|
<el-dialog v-model="invoiceDialog.visible" title="查询金蝶票据池" width="86%" append-to-body>
|
|
<div class="receipt-form-page__dialog-search">
|
|
<el-input
|
|
v-model="invoiceDialog.keyword"
|
|
clearable
|
|
placeholder="发票号码、开票单位或受票单位"
|
|
@keyup.enter="loadInvoicePool"
|
|
/>
|
|
<el-button type="primary" @click="loadInvoicePool">查询</el-button>
|
|
</div>
|
|
<el-table
|
|
v-loading="invoiceDialog.loading"
|
|
:data="invoiceDialog.rows"
|
|
border
|
|
highlight-current-row
|
|
@current-change="invoiceDialog.current = $event"
|
|
@row-dblclick="confirmInvoice"
|
|
>
|
|
<el-table-column prop="invoiceNo" label="发票号码" min-width="180" />
|
|
<el-table-column prop="invoiceDate" label="开票日期" min-width="120" />
|
|
<el-table-column prop="invoiceType" label="发票类型" min-width="130" />
|
|
<el-table-column prop="issuerName" label="开票单位" min-width="170" />
|
|
<el-table-column prop="receiverName" label="受票单位" min-width="170" />
|
|
<el-table-column label="开票金额" min-width="130" align="right">
|
|
<template #default="{ row }">{{ formatMoney(row.invoiceAmount) }}</template>
|
|
</el-table-column>
|
|
<el-table-column label="税率" min-width="100" align="center">
|
|
<template #default="{ row }">{{ formatRate(row.taxRate) }}</template>
|
|
</el-table-column>
|
|
<el-table-column label="税额" min-width="120" align="right">
|
|
<template #default="{ row }">{{ formatMoney(row.taxAmount) }}</template>
|
|
</el-table-column>
|
|
<el-table-column prop="kingdeeBillNo" label="金蝶单据号" min-width="150" />
|
|
</el-table>
|
|
<template #footer>
|
|
<el-button @click="invoiceDialog.visible = false">取消</el-button>
|
|
<el-button type="primary" @click="confirmInvoice()">确定</el-button>
|
|
</template>
|
|
</el-dialog>
|
|
|
|
<el-dialog
|
|
v-model="settlementDialog.visible"
|
|
title="选择应付正式结算单"
|
|
width="86%"
|
|
append-to-body
|
|
>
|
|
<div class="receipt-form-page__dialog-search">
|
|
<el-input
|
|
v-model="settlementDialog.keyword"
|
|
clearable
|
|
placeholder="结算单号、项目或合同"
|
|
@keyup.enter="loadSettlementCandidates"
|
|
/>
|
|
<el-button type="primary" @click="loadSettlementCandidates">查询</el-button>
|
|
</div>
|
|
<el-table
|
|
ref="settlementTable"
|
|
v-loading="settlementDialog.loading"
|
|
:data="settlementDialog.rows"
|
|
border
|
|
@selection-change="settlementDialog.selection = $event"
|
|
>
|
|
<el-table-column type="selection" width="52" align="center" />
|
|
<el-table-column prop="formalSettlementNo" label="结算单号" min-width="170" />
|
|
<el-table-column prop="projectName" label="所属项目" min-width="150" />
|
|
<el-table-column prop="deptName" label="所属组织" min-width="150" />
|
|
<el-table-column prop="contractNo" label="合同编号" min-width="150" />
|
|
<el-table-column prop="payerName" label="付款方" min-width="150" />
|
|
<el-table-column prop="payeeName" label="收款方" min-width="150" />
|
|
<el-table-column label="结算总金额" min-width="140" align="right">
|
|
<template #default="{ row }">{{ formatMoney(row.settlementAmount) }}</template>
|
|
</el-table-column>
|
|
<el-table-column label="已收票金额" min-width="140" align="right">
|
|
<template #default="{ row }">{{ formatMoney(row.receivedInvoiceAmount) }}</template>
|
|
</el-table-column>
|
|
</el-table>
|
|
<template #footer>
|
|
<el-button @click="settlementDialog.visible = false">取消</el-button>
|
|
<el-button type="primary" @click="confirmSettlements">确定</el-button>
|
|
</template>
|
|
</el-dialog>
|
|
</basic-container>
|
|
</template>
|
|
|
|
<script>
|
|
import { Download, Search } from '@element-plus/icons-vue';
|
|
import { mapGetters } from 'vuex';
|
|
import * as api from '@/api/payment/invoiceReceipt';
|
|
|
|
const emptyForm = () => ({
|
|
id: null,
|
|
kingdeeInvoicePoolId: null,
|
|
invoiceNo: '',
|
|
invoiceDate: '',
|
|
invoiceType: '',
|
|
taxRate: null,
|
|
invoiceAmount: null,
|
|
taxAmount: null,
|
|
receiverName: '',
|
|
issuerName: '',
|
|
bankName: '',
|
|
bankAccount: '',
|
|
issuingBank: '',
|
|
phone: '',
|
|
customerEmails: [],
|
|
departmentEmails: [],
|
|
projectName: '',
|
|
deptName: '',
|
|
payerName: '',
|
|
payeeName: '',
|
|
settlements: [],
|
|
attachments: [],
|
|
remark: '',
|
|
});
|
|
|
|
export default {
|
|
name: 'InvoiceReceiptForm',
|
|
data() {
|
|
return {
|
|
Download,
|
|
Search,
|
|
form: emptyForm(),
|
|
customerEmailOptions: [],
|
|
departmentEmailOptions: [],
|
|
attachmentFileTypes: [
|
|
'pdf',
|
|
'bmp',
|
|
'jpeg',
|
|
'png',
|
|
'jpg',
|
|
'doc',
|
|
'docx',
|
|
'ppt',
|
|
'pptx',
|
|
'xlsx',
|
|
'xls',
|
|
'eml',
|
|
'msg',
|
|
'zip',
|
|
],
|
|
invoiceDialog: { visible: false, loading: false, keyword: '', rows: [], current: null },
|
|
settlementDialog: {
|
|
visible: false,
|
|
loading: false,
|
|
keyword: '',
|
|
rows: [],
|
|
selection: [],
|
|
},
|
|
rules: {
|
|
invoiceNo: [{ required: true, validator: this.validateInvoice, trigger: 'change' }],
|
|
settlementIds: [{ required: true, validator: this.validateSettlements, trigger: 'change' }],
|
|
customerEmails: [{ validator: this.validateEmails, trigger: 'change' }],
|
|
departmentEmails: [{ validator: this.validateEmails, trigger: 'change' }],
|
|
},
|
|
};
|
|
},
|
|
computed: {
|
|
...mapGetters(['permission']),
|
|
recordId() {
|
|
return this.$route.query.id || '';
|
|
},
|
|
readonly() {
|
|
return this.$route.query.mode === 'view';
|
|
},
|
|
canSave() {
|
|
const code = this.recordId ? 'invoice_receipt_edit' : 'invoice_receipt_add';
|
|
return this.hasPermission(code);
|
|
},
|
|
settlementLabel() {
|
|
return this.form.settlements
|
|
.map(item => item.formalSettlementNo)
|
|
.filter(Boolean)
|
|
.join(',');
|
|
},
|
|
settlementIds() {
|
|
return this.form.settlements.map(item => item.formalSettlementId || item.settlementId);
|
|
},
|
|
allocatedTotal() {
|
|
return this.form.settlements.reduce(
|
|
(total, item) => total + Number(item.allocatedInvoiceAmount || 0),
|
|
0
|
|
);
|
|
},
|
|
},
|
|
created() {
|
|
this.initialize();
|
|
},
|
|
methods: {
|
|
hasPermission(code) {
|
|
return this.permission?.[code] !== false;
|
|
},
|
|
unwrapData(response) {
|
|
const body = response?.data || response || {};
|
|
return body?.data || body;
|
|
},
|
|
parse(value) {
|
|
if (!value) return [];
|
|
if (Array.isArray(value)) return value;
|
|
try {
|
|
return JSON.parse(value) || [];
|
|
} catch {
|
|
return [];
|
|
}
|
|
},
|
|
parseEmails(value) {
|
|
return String(value || '')
|
|
.split(/[;,,;]/)
|
|
.map(item => item.trim())
|
|
.filter(Boolean);
|
|
},
|
|
async initialize() {
|
|
const userInfo = this.$store.getters.userInfo || {};
|
|
const userEmail = userInfo.email || '';
|
|
this.departmentEmailOptions = userEmail ? [userEmail] : [];
|
|
if (!this.recordId) {
|
|
if (userEmail) this.form.departmentEmails = [userEmail];
|
|
return;
|
|
}
|
|
const data = this.unwrapData(await api.getDetail(this.recordId));
|
|
this.form = {
|
|
...emptyForm(),
|
|
...data,
|
|
customerEmails: this.parseEmails(data.customerEmails),
|
|
departmentEmails: this.parseEmails(data.departmentEmails),
|
|
settlements: (data.settlements || []).map(item => ({
|
|
...item,
|
|
settlementId: item.formalSettlementId,
|
|
})),
|
|
attachments: this.parse(data.attachmentsJson),
|
|
};
|
|
if (this.settlementIds.length) await this.loadReferenceInformation();
|
|
},
|
|
validateInvoice(rule, value, callback) {
|
|
if (!value || !this.form.kingdeeInvoicePoolId) {
|
|
callback(new Error('请选择金蝶票据池发票'));
|
|
return;
|
|
}
|
|
callback();
|
|
},
|
|
validateSettlements(rule, value, callback) {
|
|
if (!this.form.settlements.length) {
|
|
callback(new Error('请选择应付正式结算单'));
|
|
return;
|
|
}
|
|
callback();
|
|
},
|
|
validateEmails(rule, value, callback) {
|
|
const emails = Array.isArray(value) ? value : this.parseEmails(value);
|
|
const valid = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
if (emails.length > 3) {
|
|
callback(new Error('最多填写3个邮箱'));
|
|
return;
|
|
}
|
|
if (emails.some(email => !valid.test(email))) {
|
|
callback(new Error('请输入正确的邮箱'));
|
|
return;
|
|
}
|
|
callback();
|
|
},
|
|
async openInvoiceDialog() {
|
|
this.invoiceDialog.visible = true;
|
|
this.invoiceDialog.keyword = this.form.invoiceNo || '';
|
|
await this.loadInvoicePool();
|
|
},
|
|
async loadInvoicePool() {
|
|
this.invoiceDialog.loading = true;
|
|
try {
|
|
this.invoiceDialog.rows =
|
|
this.unwrapData(await api.getInvoicePool(this.invoiceDialog.keyword)) || [];
|
|
} finally {
|
|
this.invoiceDialog.loading = false;
|
|
}
|
|
},
|
|
confirmInvoice(row, options = {}) {
|
|
const selected = row?.id ? row : this.invoiceDialog.current;
|
|
if (!selected) {
|
|
this.$message.warning('请选择一张金蝶票据池发票');
|
|
return;
|
|
}
|
|
if (this.form.settlements.length) {
|
|
const first = this.form.settlements[0];
|
|
if (selected.receiverName !== first.payerName || selected.issuerName !== first.payeeName) {
|
|
this.$message.warning('金蝶发票的开票单位、受票单位与结算单收付款方不一致');
|
|
return;
|
|
}
|
|
}
|
|
const preserveUserInput = options.preserveUserInput === true;
|
|
Object.assign(this.form, {
|
|
kingdeeInvoicePoolId: selected.id,
|
|
invoiceNo: selected.invoiceNo,
|
|
invoiceDate: selected.invoiceDate,
|
|
invoiceType: selected.invoiceType,
|
|
taxRate: selected.taxRate,
|
|
invoiceAmount: selected.invoiceAmount,
|
|
taxAmount: selected.taxAmount,
|
|
receiverName: selected.receiverName,
|
|
issuerName: selected.issuerName,
|
|
bankName: selected.bankName,
|
|
bankAccount: selected.bankAccount,
|
|
issuingBank: selected.issuingBank,
|
|
phone: preserveUserInput ? this.form.phone : selected.phone || '',
|
|
customerEmails: preserveUserInput
|
|
? this.form.customerEmails
|
|
: this.parseEmails(selected.customerEmails),
|
|
departmentEmails: preserveUserInput
|
|
? this.form.departmentEmails
|
|
: this.parseEmails(selected.departmentEmails),
|
|
attachments: preserveUserInput
|
|
? this.form.attachments
|
|
: this.parse(selected.attachmentsJson),
|
|
});
|
|
this.customerEmailOptions = [...this.form.customerEmails];
|
|
this.departmentEmailOptions = [
|
|
...new Set([...this.departmentEmailOptions, ...this.form.departmentEmails]),
|
|
];
|
|
this.invoiceDialog.visible = false;
|
|
this.$refs.formRef?.validateField('invoiceNo').catch(() => {});
|
|
return true;
|
|
},
|
|
async openSettlementDialog() {
|
|
this.settlementDialog.visible = true;
|
|
await this.loadSettlementCandidates();
|
|
},
|
|
async loadSettlementCandidates() {
|
|
this.settlementDialog.loading = true;
|
|
try {
|
|
this.settlementDialog.rows =
|
|
this.unwrapData(
|
|
await api.getSettlementCandidates(this.settlementDialog.keyword, this.form.id)
|
|
) || [];
|
|
} finally {
|
|
this.settlementDialog.loading = false;
|
|
}
|
|
},
|
|
async confirmSettlements() {
|
|
const rows = this.settlementDialog.selection;
|
|
if (!rows.length) {
|
|
this.$message.warning('请至少选择一张应付正式结算单');
|
|
return;
|
|
}
|
|
const first = rows[0];
|
|
const incompatible = rows.some(
|
|
item =>
|
|
String(item.projectId) !== String(first.projectId) ||
|
|
String(item.deptId) !== String(first.deptId) ||
|
|
item.payerName !== first.payerName ||
|
|
item.payeeName !== first.payeeName
|
|
);
|
|
if (incompatible) {
|
|
this.$message.warning('关联结算单必须属于同一项目、组织及收付款方');
|
|
return;
|
|
}
|
|
if (
|
|
this.form.kingdeeInvoicePoolId &&
|
|
(this.form.receiverName !== first.payerName || this.form.issuerName !== first.payeeName)
|
|
) {
|
|
this.$message.warning('金蝶发票的开票单位、受票单位与结算单收付款方不一致');
|
|
return;
|
|
}
|
|
this.form.settlements = rows.map(item => ({
|
|
...item,
|
|
formalSettlementId: item.id,
|
|
settlementId: item.id,
|
|
allocatedInvoiceAmount: 0,
|
|
}));
|
|
this.form.projectName = first.projectName;
|
|
this.form.deptName = first.deptName;
|
|
this.form.payerName = first.payerName;
|
|
this.form.payeeName = first.payeeName;
|
|
this.settlementDialog.visible = false;
|
|
await this.loadReferenceInformation();
|
|
this.$refs.formRef?.validateField('settlementIds').catch(() => {});
|
|
},
|
|
async loadReferenceInformation() {
|
|
const data = this.unwrapData(await api.getReferenceInformation(this.settlementIds.join(',')));
|
|
this.customerEmailOptions = data.customerEmails || [];
|
|
this.departmentEmailOptions = [
|
|
...new Set([...this.departmentEmailOptions, ...(data.departmentEmails || [])]),
|
|
];
|
|
if (!this.form.customerEmails.length && this.customerEmailOptions.length) {
|
|
this.form.customerEmails = [this.customerEmailOptions[0]];
|
|
}
|
|
},
|
|
remainingAmount(row) {
|
|
return Math.max(
|
|
Number(row.settlementAmount || 0) - Number(row.receivedInvoiceAmount || 0),
|
|
0
|
|
);
|
|
},
|
|
normalizeAttachments(files) {
|
|
const userInfo = this.$store.getters.userInfo || {};
|
|
const userName = userInfo.realName || userInfo.userName || '';
|
|
const time = this.$dayjs().format('YYYY-MM-DD HH:mm:ss');
|
|
this.form.attachments = (files || []).map(file => ({
|
|
...file,
|
|
description: file.description || '',
|
|
uploadUserName: file.uploadUserName || userName,
|
|
uploadTime: file.uploadTime || time,
|
|
}));
|
|
},
|
|
downloadAttachments() {
|
|
this.form.attachments.forEach(file => {
|
|
const url = file.url || file.link;
|
|
if (!url) return;
|
|
const anchor = document.createElement('a');
|
|
anchor.href = url;
|
|
anchor.download = file.originalName || file.name || '';
|
|
anchor.target = '_blank';
|
|
anchor.click();
|
|
});
|
|
},
|
|
openSettlementDetail(row) {
|
|
const id = row.formalSettlementId || row.settlementId || row.id;
|
|
if (!id) return;
|
|
this.$router.push({
|
|
path: '/settlement/formal-settlement/form',
|
|
query: { mode: 'view', id, name: '查看正式结算' },
|
|
});
|
|
},
|
|
validateBusiness() {
|
|
const invoiceAmount = Number(this.form.invoiceAmount || 0);
|
|
if (invoiceAmount <= 0) throw new Error('开票金额必须大于0');
|
|
if (Math.abs(this.allocatedTotal - invoiceAmount) > 0.001) {
|
|
throw new Error('分摊发票金额总和必须等于开票金额');
|
|
}
|
|
for (const row of this.form.settlements) {
|
|
const allocated = Number(row.allocatedInvoiceAmount);
|
|
if (!Number.isFinite(allocated) || allocated < 0) {
|
|
throw new Error('分摊发票金额必须大于或等于0');
|
|
}
|
|
if (allocated - this.remainingAmount(row) > 0.001) {
|
|
throw new Error(`结算单${row.formalSettlementNo}的累计收票金额不能超过结算总金额`);
|
|
}
|
|
}
|
|
},
|
|
payload() {
|
|
return {
|
|
id: this.form.id,
|
|
kingdeeInvoicePoolId: this.form.kingdeeInvoicePoolId,
|
|
phone: this.form.phone,
|
|
customerEmails: this.form.customerEmails.join(';'),
|
|
departmentEmails: this.form.departmentEmails.join(';'),
|
|
attachmentsJson: JSON.stringify(this.form.attachments || []),
|
|
remark: this.form.remark,
|
|
settlements: this.form.settlements.map(item => ({
|
|
settlementId: item.formalSettlementId || item.settlementId,
|
|
allocatedInvoiceAmount: item.allocatedInvoiceAmount,
|
|
})),
|
|
};
|
|
},
|
|
async saveDraft() {
|
|
await this.$refs.formRef.validate();
|
|
try {
|
|
this.validateBusiness();
|
|
} catch (error) {
|
|
this.$message.warning(error.message);
|
|
return false;
|
|
}
|
|
const id = this.unwrapData(await api.save(this.payload()));
|
|
this.form.id = id;
|
|
this.$message.success('保存成功');
|
|
return true;
|
|
},
|
|
async submitForm() {
|
|
const saved = await this.saveDraft();
|
|
if (!saved) return;
|
|
await api.submit({ id: this.form.id });
|
|
this.$message.success('提交成功');
|
|
this.goBack();
|
|
},
|
|
async syncReferenceData() {
|
|
if (!this.form.invoiceNo) {
|
|
this.$message.warning('请先选择金蝶票据池发票');
|
|
return;
|
|
}
|
|
const rows = this.unwrapData(await api.getInvoicePool(this.form.invoiceNo)) || [];
|
|
const invoice = rows.find(item => item.invoiceNo === this.form.invoiceNo);
|
|
if (!invoice) {
|
|
this.$message.warning('金蝶票据池中未查询到该发票');
|
|
return;
|
|
}
|
|
if (!this.confirmInvoice(invoice, { preserveUserInput: true })) return;
|
|
if (this.settlementIds.length) await this.loadReferenceInformation();
|
|
this.$message.success('同步成功');
|
|
},
|
|
goBack() {
|
|
this.$router.push('/payment/invoice-receipt');
|
|
},
|
|
formatMoney(value) {
|
|
return Number(value || 0).toFixed(2);
|
|
},
|
|
formatRate(value) {
|
|
return value === null || value === undefined || value === '' ? '' : `${Number(value)}%`;
|
|
},
|
|
},
|
|
};
|
|
</script>
|
|
|
|
<style scoped lang="scss">
|
|
.receipt-form-page__form :deep(.el-form-item) {
|
|
margin-bottom: 14px;
|
|
}
|
|
.receipt-form-page__form :deep(.el-select),
|
|
.receipt-form-page__form :deep(.el-input-number) {
|
|
width: 100%;
|
|
}
|
|
.receipt-form-page__links {
|
|
display: flex;
|
|
justify-content: center;
|
|
gap: 8px;
|
|
flex-wrap: wrap;
|
|
}
|
|
.receipt-form-page__remark :deep(.el-form-item__content) {
|
|
margin-left: 0 !important;
|
|
}
|
|
.receipt-form-page__uploader {
|
|
margin-top: 12px;
|
|
}
|
|
.receipt-form-page__actions {
|
|
display: flex;
|
|
justify-content: flex-end;
|
|
gap: 12px;
|
|
padding: 12px 24px;
|
|
position: fixed;
|
|
right: 0;
|
|
left: 230px;
|
|
bottom: 0;
|
|
margin: 0;
|
|
z-index: 10;
|
|
background: #fff;
|
|
border-top: 1px solid #eff1f7;
|
|
box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.06);
|
|
}
|
|
:global(.avue--collapse .receipt-form-page__actions) {
|
|
left: 60px;
|
|
}
|
|
:global(.avue-layout--horizontal .receipt-form-page__actions) {
|
|
left: 0;
|
|
}
|
|
.receipt-form-page__dialog-search {
|
|
display: flex;
|
|
justify-content: flex-end;
|
|
gap: 8px;
|
|
margin-bottom: 12px;
|
|
}
|
|
.receipt-form-page__dialog-search .el-input {
|
|
width: 360px;
|
|
}
|
|
.receipt-form-page :deep(.el-table) {
|
|
--el-table-border-color: #eff1f7;
|
|
}
|
|
.receipt-form-page :deep(.el-table__row:nth-child(even) > td.el-table__cell) {
|
|
background: #fafafa;
|
|
}
|
|
@media (max-width: 900px) {
|
|
.receipt-form-page__form :deep(.el-col-6) {
|
|
max-width: 100%;
|
|
flex: 0 0 100%;
|
|
}
|
|
}
|
|
</style>
|