fix
This commit is contained in:
@@ -211,7 +211,7 @@
|
||||
</el-form-item>
|
||||
</section-card>
|
||||
|
||||
<section-card title="使用记录">
|
||||
<section-card v-if="readonly" title="使用记录">
|
||||
<el-table :data="usageRecords" border>
|
||||
<el-table-column type="index" label="序号" width="64" align="center" />
|
||||
<el-table-column prop="applicationNo" label="申请单号" min-width="180" align="center" />
|
||||
@@ -311,7 +311,7 @@ export default {
|
||||
billType: [{ required: true, message: '请选择汇票类型', trigger: 'change' }],
|
||||
faceAmount: [{ validator: this.validateFaceAmount, trigger: 'change' }],
|
||||
issueDate: [{ required: true, message: '请选择出票日期', trigger: 'change' }],
|
||||
maturityDate: [{ validator: this.validateMaturityDate, trigger: 'change' }],
|
||||
maturityDate: [{ required: true, validator: this.validateMaturityDate, trigger: 'change' }],
|
||||
availableDeptIds: [{ validator: this.validateDepartments, trigger: 'change' }],
|
||||
feeBearerId: [{ required: true, message: '请选择费用承担方', trigger: 'change' }],
|
||||
confirmedDiscountRate: [{ validator: this.validateRate, trigger: 'change' }],
|
||||
@@ -359,6 +359,8 @@ export default {
|
||||
this.form = {
|
||||
...emptyForm(),
|
||||
...data,
|
||||
confirmedDiscountRate: this.normalizeDiscountRate(data.confirmedDiscountRate),
|
||||
bankDiscountReferenceRate: this.normalizeDiscountRate(data.bankDiscountReferenceRate),
|
||||
availableDeptIds: this.parse(data.availableDeptIdsJson),
|
||||
attachments: this.parse(data.attachmentsJson),
|
||||
};
|
||||
@@ -461,6 +463,11 @@ export default {
|
||||
}
|
||||
callback();
|
||||
},
|
||||
normalizeDiscountRate(value) {
|
||||
return value === null || value === undefined || value === '' || Number(value) === -1
|
||||
? null
|
||||
: value;
|
||||
},
|
||||
normalizeAttachments(files) {
|
||||
const userName = this.userInfo?.realName || this.userInfo?.userName || '';
|
||||
const time = this.$dayjs().format('YYYY-MM-DD HH:mm:ss');
|
||||
@@ -492,9 +499,9 @@ export default {
|
||||
availableDeptIdsJson: JSON.stringify(this.form.availableDeptIds || []),
|
||||
availableDeptNames: this.form.availableDeptNames,
|
||||
feeBearerId: this.form.feeBearerId,
|
||||
confirmedDiscountRate: this.form.confirmedDiscountRate,
|
||||
confirmedDiscountRate: this.normalizeDiscountRate(this.form.confirmedDiscountRate),
|
||||
issuingBank: this.form.issuingBank,
|
||||
bankDiscountReferenceRate: this.form.bankDiscountReferenceRate,
|
||||
bankDiscountReferenceRate: this.normalizeDiscountRate(this.form.bankDiscountReferenceRate),
|
||||
attachmentsJson: JSON.stringify(this.form.attachments || []),
|
||||
remark: this.form.remark,
|
||||
};
|
||||
|
||||
@@ -12,16 +12,89 @@
|
||||
:loading="loading"
|
||||
:page="page"
|
||||
@add="openCreate"
|
||||
@view="openView"
|
||||
@edit="openEdit"
|
||||
@delete="handleDelete"
|
||||
@page-change="handlePageChange"
|
||||
@size-change="handleSizeChange"
|
||||
/>
|
||||
<el-dialog
|
||||
v-model="detailDialog.visible"
|
||||
title="汇票台账详情"
|
||||
width="86%"
|
||||
append-to-body
|
||||
destroy-on-close
|
||||
>
|
||||
<div v-loading="detailDialog.loading" class="bill-ledger-detail">
|
||||
<section-card title="基本信息">
|
||||
<el-descriptions :column="3" border>
|
||||
<el-descriptions-item label="票据编号">{{
|
||||
detailValue('billNo')
|
||||
}}</el-descriptions-item>
|
||||
<el-descriptions-item label="汇票类型">{{
|
||||
detailValue('billTypeName', 'billType')
|
||||
}}</el-descriptions-item>
|
||||
<el-descriptions-item label="出票单位">{{
|
||||
detailValue('issuerName')
|
||||
}}</el-descriptions-item>
|
||||
<el-descriptions-item label="收票单位">{{
|
||||
detailValue('receiverName')
|
||||
}}</el-descriptions-item>
|
||||
<el-descriptions-item label="票面金额">{{
|
||||
formatMoney(detailDialog.row.faceAmount)
|
||||
}}</el-descriptions-item>
|
||||
<el-descriptions-item label="可用余额">{{
|
||||
formatMoney(detailDialog.row.availableBalance)
|
||||
}}</el-descriptions-item>
|
||||
<el-descriptions-item label="出票日期">{{
|
||||
detailValue('issueDate')
|
||||
}}</el-descriptions-item>
|
||||
<el-descriptions-item label="到期日期">{{
|
||||
detailValue('maturityDate')
|
||||
}}</el-descriptions-item>
|
||||
<el-descriptions-item label="出票行">{{
|
||||
detailValue('issuingBank')
|
||||
}}</el-descriptions-item>
|
||||
<el-descriptions-item label="可用部门">{{
|
||||
detailValue('availableDeptNames')
|
||||
}}</el-descriptions-item>
|
||||
<el-descriptions-item label="费用承担方">{{
|
||||
detailValue('feeBearerName')
|
||||
}}</el-descriptions-item>
|
||||
<el-descriptions-item label="银行贴现参考率">
|
||||
{{ formatRate(detailDialog.row.bankDiscountReferenceRate) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="双方确认贴现率">
|
||||
{{ formatRate(detailDialog.row.confirmedDiscountRate) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="预计贴现费用">
|
||||
{{ formatMoney(estimatedDiscountFee()) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="备注" :span="3">
|
||||
{{ detailValue('remark') }}
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</section-card>
|
||||
<section-card title="使用记录">
|
||||
<el-table :data="detailDialog.usageRecords" border>
|
||||
<el-table-column type="index" label="序号" width="64" align="center" />
|
||||
<el-table-column prop="applicationNo" label="申请单号" min-width="180" align="center" />
|
||||
<el-table-column label="使用金额" min-width="150" align="right">
|
||||
<template #default="{ row }">{{ formatMoney(row.usedAmount) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="useDeptName" label="使用部门" min-width="180" align="center" />
|
||||
<el-table-column prop="statusName" label="状态" min-width="120" align="center" />
|
||||
</el-table>
|
||||
<el-empty v-if="!detailDialog.usageRecords.length" description="暂无使用记录" />
|
||||
</section-card>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</basic-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as api from '@/api/payment/billLedger';
|
||||
import * as billPaymentApi from '@/api/payment/billPayment';
|
||||
import BillLedgerSearch from './components/bill-ledger-search.vue';
|
||||
import BillLedgerTable from './components/bill-ledger-table.vue';
|
||||
|
||||
@@ -45,6 +118,7 @@ export default {
|
||||
rows: [],
|
||||
loading: false,
|
||||
page: { current: 1, size: 10, total: 0 },
|
||||
detailDialog: { visible: false, loading: false, row: {}, usageRecords: [] },
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
@@ -89,6 +163,31 @@ export default {
|
||||
openCreate() {
|
||||
this.$router.push({ path: '/payment/bill-ledger/form', query: { mode: 'add' } });
|
||||
},
|
||||
async openView(row) {
|
||||
this.detailDialog = { visible: true, loading: true, row: { ...row }, usageRecords: [] };
|
||||
try {
|
||||
const [detailResponse, paymentResponse] = await Promise.all([
|
||||
api.getDetail(row.id),
|
||||
billPaymentApi.getList(1, 1000, { billLedgerId: row.id }),
|
||||
]);
|
||||
const detail = this.unwrapData(detailResponse) || {};
|
||||
const paymentData = this.unwrapData(paymentResponse) || {};
|
||||
const paymentRows = paymentData.records || (Array.isArray(paymentData) ? paymentData : []);
|
||||
const sourceRecords = paymentRows.length ? paymentRows : detail.usageRecords || [];
|
||||
this.detailDialog.row = { ...row, ...detail };
|
||||
this.detailDialog.usageRecords = sourceRecords.map(item => ({
|
||||
...item,
|
||||
applicationNo: item.applicationNo || item.paymentNo || '-',
|
||||
usedAmount: Number(item.usedAmount || 0),
|
||||
useDeptName: item.useDeptName || item.deptName || '-',
|
||||
statusName: item.statusName || item.approvalStatusName || '已审核',
|
||||
}));
|
||||
} catch (error) {
|
||||
this.$message.error('票据详情加载失败,请稍后重试');
|
||||
} finally {
|
||||
this.detailDialog.loading = false;
|
||||
}
|
||||
},
|
||||
openEdit(row) {
|
||||
this.$router.push({
|
||||
path: '/payment/bill-ledger/form',
|
||||
@@ -111,6 +210,26 @@ export default {
|
||||
this.page.size = size;
|
||||
this.loadData();
|
||||
},
|
||||
detailValue(...fields) {
|
||||
const value = fields
|
||||
.map(field => this.detailDialog.row?.[field])
|
||||
.find(item => item !== undefined && item !== null && item !== '');
|
||||
return value === undefined ? '-' : value;
|
||||
},
|
||||
formatMoney(value) {
|
||||
return Number(value || 0).toFixed(2);
|
||||
},
|
||||
formatRate(value) {
|
||||
if (Number(value) === -1) return '';
|
||||
return value === null || value === undefined || value === '' ? '-' : `${Number(value)}%`;
|
||||
},
|
||||
estimatedDiscountFee() {
|
||||
return (
|
||||
(Number(this.detailDialog.row.faceAmount || 0) *
|
||||
Number(this.detailDialog.row.confirmedDiscountRate || 0)) /
|
||||
100
|
||||
);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -15,7 +15,10 @@
|
||||
show-overflow-tooltip
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<span v-if="column.money">{{ formatMoney(row[column.prop]) }}</span>
|
||||
<el-link v-if="column.link" type="primary" @click="$emit('view', row)">{{
|
||||
displayValue(row[column.prop])
|
||||
}}</el-link>
|
||||
<span v-else-if="column.money">{{ formatMoney(row[column.prop]) }}</span>
|
||||
<span v-else>{{ displayValue(row[column.prop]) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
@@ -65,7 +68,7 @@ export default {
|
||||
loading: { type: Boolean, default: false },
|
||||
page: { type: Object, required: true },
|
||||
},
|
||||
emits: ['add', 'edit', 'delete', 'page-change', 'size-change'],
|
||||
emits: ['add', 'view', 'edit', 'delete', 'page-change', 'size-change'],
|
||||
data() {
|
||||
return { columns: billLedgerTableColumns };
|
||||
},
|
||||
|
||||
@@ -192,12 +192,6 @@
|
||||
<div v-for="(sheet, sheetIndex) in form.sheets" :key="sheet.localId" class="invoice-sheet">
|
||||
<div class="invoice-sheet__header">
|
||||
<span>第{{ sheetIndex + 1 }}张</span>
|
||||
<el-link
|
||||
v-if="!readonly && form.sheets.length > 1"
|
||||
type="danger"
|
||||
@click="removeSheet(sheetIndex)"
|
||||
>删除</el-link
|
||||
>
|
||||
</div>
|
||||
<el-table :data="sheet.lines" border>
|
||||
<el-table-column type="index" label="序号" width="64" align="center" />
|
||||
@@ -314,6 +308,9 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div v-if="!readonly && form.sheets.length > 1" class="invoice-sheet__actions">
|
||||
<el-link type="danger" @click="removeSheet(sheetIndex)">删除</el-link>
|
||||
</div>
|
||||
</div>
|
||||
</section-card>
|
||||
|
||||
@@ -1080,6 +1077,9 @@ export default {
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.invoice-form-page__form {
|
||||
padding-bottom: 72px;
|
||||
}
|
||||
.invoice-form-page__form :deep(.el-select),
|
||||
.invoice-form-page__form :deep(.el-input-number) {
|
||||
width: 100%;
|
||||
@@ -1095,6 +1095,17 @@ export default {
|
||||
background: #fafafa;
|
||||
font-weight: 600;
|
||||
}
|
||||
.invoice-sheet__actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
padding: 8px 12px 0;
|
||||
}
|
||||
.invoice-form-page__links {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
}
|
||||
.invoice-form-page__remark :deep(.el-form-item__content) {
|
||||
margin-left: 0 !important;
|
||||
}
|
||||
|
||||
@@ -84,10 +84,21 @@
|
||||
:key="column.prop"
|
||||
v-bind="column"
|
||||
align="center"
|
||||
show-overflow-tooltip
|
||||
:show-overflow-tooltip="column.prop !== 'settlementNos'"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<el-link v-if="column.link && row[column.prop]" type="primary" @click="openView(row)">
|
||||
<div v-if="column.prop === 'settlementNos'" class="invoice-page__settlement-nos">
|
||||
<span
|
||||
v-for="settlementNo in settlementNumberList(row.settlementNos)"
|
||||
:key="settlementNo"
|
||||
class="invoice-page__settlement-no"
|
||||
@click.stop="openSettlementDetail(settlementNo)"
|
||||
>
|
||||
{{ settlementNo }}
|
||||
</span>
|
||||
<span v-if="!settlementNumberList(row.settlementNos).length">-</span>
|
||||
</div>
|
||||
<el-link v-else-if="column.link && row[column.prop]" type="primary" @click="openView(row)">
|
||||
{{ row[column.prop] }}
|
||||
</el-link>
|
||||
<el-tag v-else-if="column.status" :type="statusType(row.approvalStatus)" class="status-text">
|
||||
@@ -221,6 +232,7 @@ import { ArrowDown, ArrowUp, Refresh } from '@element-plus/icons-vue';
|
||||
import { mapGetters } from 'vuex';
|
||||
import * as XLSX from 'xlsx';
|
||||
import * as api from '@/api/payment/invoiceApplication';
|
||||
import * as formalSettlementApi from '@/api/settlement/formalSettlement';
|
||||
import { invoiceApplicationTableColumns } from '@/option/payment/invoiceApplication';
|
||||
|
||||
const emptyQuery = () => ({
|
||||
@@ -303,6 +315,34 @@ export default {
|
||||
query: { mode: 'view', id: row.id },
|
||||
});
|
||||
},
|
||||
settlementNumberList(value) {
|
||||
return String(value || '')
|
||||
.split(/[、,,]/)
|
||||
.map(item => item.trim())
|
||||
.filter(Boolean);
|
||||
},
|
||||
async openSettlementDetail(settlementNo) {
|
||||
const normalizedNo = String(settlementNo || '').trim();
|
||||
if (!normalizedNo) return;
|
||||
try {
|
||||
const data = this.unwrapData(
|
||||
await formalSettlementApi.getList(1, 10, { formalSettlementNo: normalizedNo })
|
||||
);
|
||||
const row = (data.records || []).find(
|
||||
item => String(item.formalSettlementNo || '').trim() === normalizedNo
|
||||
);
|
||||
if (!row?.id) {
|
||||
this.$message.warning('未找到对应的正式结算单');
|
||||
return;
|
||||
}
|
||||
this.$router.push({
|
||||
path: '/settlement/formal-settlement/form',
|
||||
query: { mode: 'view', id: row.id, name: '查看正式结算' },
|
||||
});
|
||||
} catch (error) {
|
||||
this.$message.error('正式结算单详情加载失败');
|
||||
}
|
||||
},
|
||||
async openFlow(row) {
|
||||
this.flowDialog.row = { ...row };
|
||||
this.flowDialog.records = [];
|
||||
@@ -454,6 +494,22 @@ export default {
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
}
|
||||
.invoice-page__settlement-nos {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
gap: 4px 8px;
|
||||
white-space: normal;
|
||||
}
|
||||
.invoice-page__settlement-no {
|
||||
color: #409eff;
|
||||
cursor: pointer;
|
||||
white-space: normal;
|
||||
word-break: break-all;
|
||||
}
|
||||
.invoice-page__settlement-no:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
.invoice-page__pagination {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
|
||||
@@ -137,17 +137,15 @@
|
||||
<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" type="primary" plain @click="saveDraft"
|
||||
>保存</el-button
|
||||
>
|
||||
<el-button
|
||||
v-if="!readonly && canSave && hasPermission('invoice_receipt_submit')"
|
||||
type="primary"
|
||||
@click="submitForm"
|
||||
>提交</el-button>
|
||||
>提交</el-button
|
||||
>
|
||||
</div>
|
||||
</el-form>
|
||||
|
||||
@@ -251,7 +249,7 @@ import { mapGetters } from 'vuex';
|
||||
import * as api from '@/api/payment/invoiceReceipt';
|
||||
import VehicleAttachmentTable from '@/components/vehicle-attachment-table/main.vue';
|
||||
|
||||
const invoicePoolMockRows = [
|
||||
const invoicePoolMockTemplates = [
|
||||
{
|
||||
id: 90000001,
|
||||
invoiceNo: '25312000000000000101',
|
||||
@@ -311,6 +309,39 @@ const invoicePoolMockRows = [
|
||||
},
|
||||
];
|
||||
|
||||
const randomDigits = length =>
|
||||
Array.from({ length }, () => Math.floor(Math.random() * 10)).join('');
|
||||
|
||||
const formatMockDate = date => {
|
||||
const year = date.getFullYear();
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||||
const day = String(date.getDate()).padStart(2, '0');
|
||||
return `${year}-${month}-${day}`;
|
||||
};
|
||||
|
||||
const createInvoicePoolMockRows = () =>
|
||||
invoicePoolMockTemplates.map((template, index) => {
|
||||
const invoiceDateValue = new Date();
|
||||
invoiceDateValue.setDate(invoiceDateValue.getDate() - Math.floor(Math.random() * 15));
|
||||
const invoiceDate = formatMockDate(invoiceDateValue);
|
||||
const invoiceAmount = Math.floor(10000 + Math.random() * 190000);
|
||||
const taxAmount = Number(
|
||||
(
|
||||
(invoiceAmount * Number(template.taxRate || 0)) /
|
||||
(100 + Number(template.taxRate || 0))
|
||||
).toFixed(2)
|
||||
);
|
||||
return {
|
||||
...template,
|
||||
id: Date.now() + index * 100 + Math.floor(Math.random() * 100),
|
||||
invoiceNo: `25${randomDigits(18)}`,
|
||||
invoiceDate,
|
||||
invoiceAmount,
|
||||
taxAmount,
|
||||
kingdeeBillNo: `KD-FP-${invoiceDate.replaceAll('-', '')}${randomDigits(3)}`,
|
||||
};
|
||||
});
|
||||
|
||||
const emptyForm = () => ({
|
||||
id: null,
|
||||
kingdeeInvoicePoolId: null,
|
||||
@@ -348,6 +379,7 @@ export default {
|
||||
return {
|
||||
Search,
|
||||
form: emptyForm(),
|
||||
invoicePoolMockRows: [],
|
||||
customerEmailOptions: [],
|
||||
departmentEmailOptions: [],
|
||||
invoiceDialog: { visible: false, loading: false, keyword: '', rows: [], current: null },
|
||||
@@ -415,6 +447,7 @@ export default {
|
||||
.filter(Boolean);
|
||||
},
|
||||
async initialize() {
|
||||
this.invoicePoolMockRows = createInvoicePoolMockRows();
|
||||
const userInfo = this.$store.getters.userInfo || {};
|
||||
const userEmail = userInfo.email || '';
|
||||
this.departmentEmailOptions = userEmail ? [userEmail] : [];
|
||||
@@ -466,6 +499,7 @@ export default {
|
||||
async openInvoiceDialog() {
|
||||
this.invoiceDialog.visible = true;
|
||||
this.invoiceDialog.keyword = this.form.invoiceNo || '';
|
||||
if (!this.recordId) this.invoicePoolMockRows = createInvoicePoolMockRows();
|
||||
await this.loadInvoicePool();
|
||||
},
|
||||
async loadInvoicePool() {
|
||||
@@ -485,8 +519,8 @@ export default {
|
||||
const normalizedKeyword = String(keyword || '')
|
||||
.trim()
|
||||
.toLowerCase();
|
||||
if (!normalizedKeyword) return invoicePoolMockRows.map(item => ({ ...item }));
|
||||
return invoicePoolMockRows
|
||||
if (!normalizedKeyword) return this.invoicePoolMockRows.map(item => ({ ...item }));
|
||||
return this.invoicePoolMockRows
|
||||
.filter(item =>
|
||||
[item.invoiceNo, item.issuerName, item.receiverName].some(value =>
|
||||
String(value || '')
|
||||
@@ -705,6 +739,9 @@ export default {
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.receipt-form-page__form {
|
||||
padding-bottom: 72px;
|
||||
}
|
||||
.receipt-form-page__form :deep(.el-form-item) {
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
|
||||
@@ -41,30 +41,61 @@
|
||||
></el-form-item
|
||||
></el-col
|
||||
>
|
||||
<el-col :span="6"
|
||||
><el-form-item label="所属项目"
|
||||
><el-input v-model="form.projectName" disabled
|
||||
><template v-if="form.paymentType === 'project_advance'" #append
|
||||
><el-button :disabled="readonly" @click="openReference">选择</el-button></template
|
||||
></el-input
|
||||
></el-form-item
|
||||
></el-col
|
||||
>
|
||||
<el-col :span="6"
|
||||
><el-form-item label="合同名称"
|
||||
><el-input v-model="form.contractName" disabled /></el-form-item
|
||||
></el-col>
|
||||
<el-col :span="6"
|
||||
<el-col :span="6">
|
||||
<el-form-item label="所属项目">
|
||||
<el-select
|
||||
v-if="form.paymentType === 'project_advance'"
|
||||
v-model="form.projectId"
|
||||
:disabled="readonly"
|
||||
filterable
|
||||
clearable
|
||||
:loading="projectLoading"
|
||||
placeholder="请选择项目"
|
||||
@change="handleProjectChange"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in projectOptions"
|
||||
:key="item.id"
|
||||
:label="item.projectName || item.projectShortName || item.projectCode"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
<el-input v-else v-model="form.projectName" disabled />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="合同名称">
|
||||
<el-select
|
||||
v-if="form.paymentType === 'project_advance'"
|
||||
v-model="form.contractId"
|
||||
:disabled="readonly || !form.projectId"
|
||||
filterable
|
||||
clearable
|
||||
:loading="contractLoading"
|
||||
placeholder="请先选择项目"
|
||||
@change="handleAdvanceContractChange"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in contractOptions"
|
||||
:key="item.id"
|
||||
:label="item.contractName || item.contractNo"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
<el-input v-else v-model="form.contractName" disabled />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col v-if="form.paymentType !== 'project_advance'" :span="6"
|
||||
><el-form-item label="结算金额" prop="settlementAmount"
|
||||
><el-input-number
|
||||
v-model="form.settlementAmount"
|
||||
:disabled="readonly || form.paymentType !== 'project_advance'"
|
||||
:disabled="readonly"
|
||||
:min="0"
|
||||
:precision="2"
|
||||
:controls="false"
|
||||
style="width: 100%" /></el-form-item
|
||||
></el-col>
|
||||
<el-col :span="6"
|
||||
<el-col v-if="form.paymentType !== 'project_advance'" :span="6"
|
||||
><el-form-item label="可付款金额"
|
||||
><el-input v-model="form.payableAmount" disabled /></el-form-item
|
||||
></el-col>
|
||||
@@ -73,7 +104,7 @@
|
||||
><el-input v-model="form.billType" disabled /></el-form-item
|
||||
></el-col>
|
||||
<el-col :span="6"
|
||||
><el-form-item label="付款比例" prop="paymentRatio"
|
||||
><el-form-item label="付款比例(%)" prop="paymentRatio"
|
||||
><div class="payment-form-page__percentage-input">
|
||||
<el-input-number
|
||||
v-model="form.paymentRatio"
|
||||
@@ -108,7 +139,7 @@
|
||||
v-bind="item" /></el-select></el-form-item
|
||||
></el-col>
|
||||
<el-col v-if="billPayment" :span="6"
|
||||
><el-form-item label="汇票票据" prop="billLedgerId"
|
||||
><el-form-item label="汇票单号" prop="billLedgerId"
|
||||
><el-select
|
||||
v-model="form.billLedgerId"
|
||||
:disabled="readonly"
|
||||
@@ -184,7 +215,7 @@
|
||||
label="可付款金额"
|
||||
min-width="130" /><el-table-column
|
||||
prop="paymentRatio"
|
||||
label="付款比例"
|
||||
label="付款比例(%)"
|
||||
min-width="110" /><el-table-column
|
||||
prop="appliedAmount"
|
||||
label="付款金额"
|
||||
@@ -395,8 +426,9 @@
|
||||
v-loading="formalPage.loading"
|
||||
:data="formalRows"
|
||||
border
|
||||
@row-click="selectFormal"
|
||||
@selection-change="formalSelection = $event"
|
||||
>
|
||||
<el-table-column type="selection" width="50" />
|
||||
<el-table-column prop="formalSettlementNo" label="结算单号" />
|
||||
<el-table-column prop="projectName" label="所属项目" />
|
||||
<el-table-column prop="contractName" label="合同名称" />
|
||||
@@ -407,6 +439,9 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div class="payment-form-page__reference-confirm">
|
||||
<el-button type="primary" :disabled="!formalSelection.length" @click="confirmFormalSelection">确定</el-button>
|
||||
</div>
|
||||
<div class="payment-form-page__reference-pagination">
|
||||
<el-pagination
|
||||
v-model:current-page="formalPage.current"
|
||||
@@ -420,7 +455,8 @@
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="预结算单" name="pre">
|
||||
<el-table v-loading="prePage.loading" :data="preRows" border @row-click="selectPre">
|
||||
<el-table v-loading="prePage.loading" :data="preRows" border @selection-change="preSelection = $event">
|
||||
<el-table-column type="selection" width="50" />
|
||||
<el-table-column prop="preSettlementNo" label="结算单号" />
|
||||
<el-table-column prop="projectName" label="所属项目" />
|
||||
<el-table-column prop="contractName" label="合同名称" />
|
||||
@@ -442,6 +478,9 @@
|
||||
@size-change="handlePreSizeChange"
|
||||
/>
|
||||
</div>
|
||||
<div class="payment-form-page__reference-confirm">
|
||||
<el-button type="primary" :disabled="!preSelection.length" @click="confirmPreSelection">确定</el-button>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs></div
|
||||
></el-dialog>
|
||||
@@ -496,6 +535,7 @@ import pdfWorkerSrc from 'pdfjs-dist/build/pdf.worker.mjs?url';
|
||||
import { mapGetters } from 'vuex';
|
||||
import * as api from '@/api/payment/paymentApplication';
|
||||
import * as billLedgerApi from '@/api/payment/billLedger';
|
||||
import { getList as getProjectList } from '@/api/business/project-apply';
|
||||
import { getDetail as getContractDetail } from '@/api/business/contract-manage';
|
||||
import * as formalApi from '@/api/settlement/formalSettlement';
|
||||
import * as preApi from '@/api/settlement/preSettlement';
|
||||
@@ -521,7 +561,7 @@ const ATTACHMENT_MATERIALS = {
|
||||
contract: {
|
||||
key: 'contract',
|
||||
label: '合同签章文件',
|
||||
keywords: ['合同签章文件', '合同签章', '签章'],
|
||||
keywords: ['合同签章文件', '合同签章', '签章', '合同'],
|
||||
},
|
||||
specialApproval: {
|
||||
key: 'specialApproval',
|
||||
@@ -543,6 +583,7 @@ const emptyForm = () => ({
|
||||
paymentNo: '',
|
||||
paymentType: 'project_advance',
|
||||
settlementId: null,
|
||||
settlementIds: [],
|
||||
preSettlementId: null,
|
||||
preSettlementIds: [],
|
||||
projectId: null,
|
||||
@@ -594,7 +635,9 @@ export default {
|
||||
contractName: '',
|
||||
},
|
||||
formalRows: [],
|
||||
formalSelection: [],
|
||||
preRows: [],
|
||||
preSelection: [],
|
||||
formalPage: {
|
||||
current: 1,
|
||||
size: 10,
|
||||
@@ -607,6 +650,10 @@ export default {
|
||||
total: 0,
|
||||
loading: false,
|
||||
},
|
||||
projectOptions: [],
|
||||
projectLoading: false,
|
||||
contractOptions: [],
|
||||
contractLoading: false,
|
||||
contractRows: [],
|
||||
billOptions: [],
|
||||
billLoading: false,
|
||||
@@ -674,13 +721,11 @@ export default {
|
||||
return this.hasPermission(code);
|
||||
},
|
||||
billPayment() {
|
||||
return ['bank_draft', 'commercial_draft'].includes(this.form.paymentMethod);
|
||||
return this.form.paymentMethod !== 'bank_transfer';
|
||||
},
|
||||
paymentAmountBase() {
|
||||
const amount =
|
||||
this.form.paymentType === 'project_advance'
|
||||
? this.form.payableAmount
|
||||
: this.form.settlementAmount;
|
||||
if (this.form.paymentType === 'project_advance') return 0;
|
||||
const amount = this.form.settlementAmount;
|
||||
return Number(amount || 0);
|
||||
},
|
||||
referenceLabel() {
|
||||
@@ -697,12 +742,15 @@ export default {
|
||||
return this.referenceRows.map(item => ({
|
||||
...item,
|
||||
paymentRatio: this.form.paymentRatio,
|
||||
appliedAmount: Number(
|
||||
(
|
||||
(Number(item.settlementAmount || 0) * Number(this.form.paymentRatio || 0)) /
|
||||
100
|
||||
).toFixed(2)
|
||||
),
|
||||
appliedAmount:
|
||||
item.fixedAppliedAmount === true
|
||||
? Number(item.appliedAmount || 0)
|
||||
: Number(
|
||||
(
|
||||
(Number(item.settlementAmount || 0) * Number(this.form.paymentRatio || 0)) /
|
||||
100
|
||||
).toFixed(2)
|
||||
),
|
||||
}));
|
||||
}
|
||||
if (!this.referenceLabel) return [];
|
||||
@@ -809,6 +857,10 @@ export default {
|
||||
this.$nextTick(() => this.$refs.formRef?.validateField('referenceId').catch(() => {}));
|
||||
},
|
||||
validateSettlementAmount(rule, value, callback) {
|
||||
if (this.form.paymentType === 'project_advance') {
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
const amount = Number(value);
|
||||
if (!Number.isFinite(amount) || amount < 0) {
|
||||
callback(new Error('结算金额不能小于0'));
|
||||
@@ -823,7 +875,11 @@ export default {
|
||||
callback(new Error('申请付款金额不能小于0'));
|
||||
return;
|
||||
}
|
||||
if (payableAmount > 0 && amount > payableAmount) {
|
||||
if (
|
||||
this.form.paymentType !== 'project_advance' &&
|
||||
payableAmount > 0 &&
|
||||
amount > payableAmount
|
||||
) {
|
||||
callback(new Error('申请付款金额不能超过可付款金额'));
|
||||
return;
|
||||
}
|
||||
@@ -835,7 +891,7 @@ export default {
|
||||
return;
|
||||
}
|
||||
if (!value) {
|
||||
callback(new Error('请选择可用汇票'));
|
||||
callback(new Error('请选择汇票单号'));
|
||||
return;
|
||||
}
|
||||
const selected = this.billOptions.find(item => String(item.id) === String(value));
|
||||
@@ -911,6 +967,10 @@ export default {
|
||||
const referenceRow = this.createReferenceRow(data);
|
||||
this.referenceRows = referenceRow.settlementNo ? [referenceRow] : [];
|
||||
this.paymentRecords = data.paymentRecords || [];
|
||||
if (this.form.paymentType === 'project_advance') {
|
||||
await this.loadProjectOptions();
|
||||
await this.loadContractOptions(this.form.projectId);
|
||||
}
|
||||
await this.loadReceiptAccountOptions(false);
|
||||
await this.loadAttachmentRuleData(await this.loadCurrentSettlementDetails());
|
||||
if (this.billPayment) await this.loadBillOptions('', data.billLedgerId);
|
||||
@@ -921,7 +981,12 @@ export default {
|
||||
this.form.applyDate = this.$dayjs().format('YYYY-MM-DD');
|
||||
this.form.applicantName =
|
||||
this.$store.getters.userInfo?.realName || this.$store.getters.userInfo?.userName || '';
|
||||
await this.loadTransferredPreSettlements();
|
||||
if (this.form.paymentType === 'project_advance') await this.loadProjectOptions();
|
||||
if (this.transferPayload?.sourceFormalSettlements?.length) {
|
||||
await this.loadTransferredFormalSettlements();
|
||||
} else {
|
||||
await this.loadTransferredPreSettlements();
|
||||
}
|
||||
}
|
||||
},
|
||||
initialAddPaymentType() {
|
||||
@@ -938,7 +1003,12 @@ export default {
|
||||
.split(',')
|
||||
.map(item => item.trim())
|
||||
.filter(Boolean);
|
||||
return [...new Set([...payloadIds, ...queryIds].map(String))];
|
||||
return [...new Set([...payloadIds, ...queryIds].map(String))].filter(id =>
|
||||
this.isValidReferenceId(id)
|
||||
);
|
||||
},
|
||||
isValidReferenceId(id) {
|
||||
return id !== null && id !== undefined && String(id).trim() !== '-1';
|
||||
},
|
||||
async loadTransferredPreSettlements() {
|
||||
const fallbackRows = this.transferPayload?.sourcePreSettlements || [];
|
||||
@@ -973,6 +1043,97 @@ export default {
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
async loadTransferredFormalSettlements() {
|
||||
const fallbackRows = this.transferPayload?.sourceFormalSettlements || [];
|
||||
const rows = await Promise.all(
|
||||
fallbackRows.map(async row => {
|
||||
if (!this.isValidReferenceId(row.id)) return row;
|
||||
try {
|
||||
return { ...row, ...this.unwrapData(await formalApi.getDetail(row.id)) };
|
||||
} catch (error) {
|
||||
return row;
|
||||
}
|
||||
})
|
||||
);
|
||||
this.applyTransferredFormalSettlements(rows.filter(item => this.isValidReferenceId(item?.id)));
|
||||
await Promise.all([this.loadReceiptAccountOptions(), this.loadAttachmentRuleData(rows)]);
|
||||
},
|
||||
applyTransferredFormalSettlements(rows = []) {
|
||||
if (!rows.length) return;
|
||||
const contractIds = new Set(rows.map(item => String(item.contractId || '')));
|
||||
if (contractIds.size !== 1 || contractIds.has('')) {
|
||||
this.$message.warning('所选正式结算单合同信息不一致,请返回后重新选择');
|
||||
return;
|
||||
}
|
||||
const first = rows[0];
|
||||
const referenceRows = rows.map(item => ({
|
||||
...this.createReferenceRow(item),
|
||||
settlementNo: item.formalSettlementNo || item.settlementNo || '',
|
||||
appliedAmount: Number(item.appliedAmount || 0),
|
||||
fixedAppliedAmount: true,
|
||||
}));
|
||||
const settlementAmount = referenceRows.reduce(
|
||||
(total, item) => total + Number(item.settlementAmount || 0),
|
||||
0
|
||||
);
|
||||
const payableAmount = referenceRows.reduce(
|
||||
(total, item) => total + Number(item.payableAmount || 0),
|
||||
0
|
||||
);
|
||||
const appliedAmount = referenceRows.reduce(
|
||||
(total, item) => total + Number(item.appliedAmount || 0),
|
||||
0
|
||||
);
|
||||
const paymentRatio = settlementAmount
|
||||
? Number(((appliedAmount / settlementAmount) * 100).toFixed(2))
|
||||
: 0;
|
||||
const settlementIds = rows.map(item => item.settlementId || item.id).filter(Boolean);
|
||||
this.amountSyncing = true;
|
||||
this.referenceRows = referenceRows;
|
||||
Object.assign(this.form, {
|
||||
paymentType: 'settlement_payment',
|
||||
settlementId: settlementIds[0] || null,
|
||||
settlementIds,
|
||||
preSettlementId: null,
|
||||
preSettlementIds: [],
|
||||
settlementNo: referenceRows
|
||||
.map(item => item.settlementNo)
|
||||
.filter(Boolean)
|
||||
.join('、'),
|
||||
preSettlementNo: '',
|
||||
projectId: first.projectId,
|
||||
projectName: first.projectName,
|
||||
deptId: first.deptId,
|
||||
deptName: first.deptName,
|
||||
contractId: first.contractId,
|
||||
contractNo: first.contractNo,
|
||||
contractName: first.contractName,
|
||||
payerName: first.payerName,
|
||||
payeeName: first.payeeName,
|
||||
settlementAmount: Number(settlementAmount.toFixed(2)),
|
||||
payableAmount: Number(payableAmount.toFixed(2)),
|
||||
billType: '正式结算单',
|
||||
paymentRatio,
|
||||
appliedAmount: Number(appliedAmount.toFixed(2)),
|
||||
invoices: rows.flatMap(item =>
|
||||
this.createInvoiceRows(item.invoices || [], item.formalSettlementNo || item.settlementNo)
|
||||
),
|
||||
});
|
||||
this.paymentRecords = rows.flatMap(item =>
|
||||
(item.paymentRecords || item.paymentApplications || []).map(record => ({
|
||||
...record,
|
||||
paidAmount: Number(record.paidAmount || 0),
|
||||
paidDate: record.paidDate || record.paymentDate || record.createTime || '',
|
||||
paymentNo: record.paymentNo || '',
|
||||
voucherJson: record.voucherJson || '',
|
||||
kingdeeBillNo: record.kingdeeBillNo || '',
|
||||
}))
|
||||
);
|
||||
this.$nextTick(() => {
|
||||
this.amountSyncing = false;
|
||||
this.$refs.formRef?.clearValidate();
|
||||
});
|
||||
},
|
||||
createReferenceRow(row) {
|
||||
const settlementAmount = Number(row.settlementAmount || 0);
|
||||
const appliedAmount = Number(row.advanceAppliedAmount || row.appliedPaymentAmount || 0);
|
||||
@@ -1156,6 +1317,22 @@ export default {
|
||||
material.keywords.some(keyword => fileName.includes(keyword.toLocaleLowerCase())))
|
||||
);
|
||||
},
|
||||
resolveAttachmentTypeByFileName(file) {
|
||||
let fileName = this.attachmentFileName(file) || this.attachmentFileUrl(file);
|
||||
fileName = String(fileName || '').split(/[?#]/)[0].toLocaleLowerCase();
|
||||
try {
|
||||
fileName = decodeURIComponent(fileName);
|
||||
} catch {}
|
||||
const material = [...Object.values(ATTACHMENT_MATERIALS)]
|
||||
.sort(
|
||||
(a, b) =>
|
||||
Math.max(...b.keywords.map(String.length)) - Math.max(...a.keywords.map(String.length))
|
||||
)
|
||||
.find(item =>
|
||||
item.keywords.some(keyword => fileName.includes(String(keyword).toLocaleLowerCase()))
|
||||
);
|
||||
return material ? MATERIAL_TYPE_MAP[material.key] : 'other';
|
||||
},
|
||||
hasAttachmentMaterial(material) {
|
||||
const hasUploadedAttachment = (this.form.attachments || []).some(file =>
|
||||
this.attachmentMatchesMaterial(file, material)
|
||||
@@ -1204,8 +1381,12 @@ export default {
|
||||
async loadCurrentSettlementDetails() {
|
||||
if (this.form.paymentType === 'project_advance') return [];
|
||||
const requests = [];
|
||||
if (this.form.preSettlementId) requests.push(preApi.getDetail(this.form.preSettlementId));
|
||||
if (this.form.settlementId) requests.push(formalApi.getDetail(this.form.settlementId));
|
||||
if (this.isValidReferenceId(this.form.preSettlementId)) {
|
||||
requests.push(preApi.getDetail(this.form.preSettlementId));
|
||||
}
|
||||
if (this.isValidReferenceId(this.form.settlementId)) {
|
||||
requests.push(formalApi.getDetail(this.form.settlementId));
|
||||
}
|
||||
if (!requests.length) return [];
|
||||
const responses = await Promise.all(requests);
|
||||
return responses.map(response => this.unwrapData(response)).filter(item => item?.id);
|
||||
@@ -1279,15 +1460,32 @@ export default {
|
||||
handleTypeChange() {
|
||||
this.form.invoices = [];
|
||||
this.paymentRecords = [];
|
||||
if (this.form.paymentType === 'project_advance') {
|
||||
this.form.settlementId = null;
|
||||
this.form.preSettlementId = null;
|
||||
this.form.preSettlementIds = [];
|
||||
this.form.settlementNo = '';
|
||||
this.form.preSettlementNo = '';
|
||||
this.referenceRows = [];
|
||||
}
|
||||
Object.assign(this.form, {
|
||||
settlementId: null,
|
||||
settlementIds: [],
|
||||
preSettlementId: null,
|
||||
preSettlementIds: [],
|
||||
settlementNo: '',
|
||||
preSettlementNo: '',
|
||||
projectId: null,
|
||||
projectName: '',
|
||||
deptId: null,
|
||||
deptName: '',
|
||||
contractId: null,
|
||||
contractNo: '',
|
||||
contractName: '',
|
||||
payerName: '',
|
||||
payeeName: '',
|
||||
settlementAmount: 0,
|
||||
payableAmount: 0,
|
||||
appliedAmount: 0,
|
||||
billType: '',
|
||||
});
|
||||
this.referenceRows = [];
|
||||
this.contractOptions = [];
|
||||
this.clearReceiptAccount();
|
||||
this.loadAttachmentRuleData([]);
|
||||
if (this.form.paymentType === 'project_advance') this.loadProjectOptions();
|
||||
},
|
||||
clearReceiptAccount() {
|
||||
Object.assign(this.form, {
|
||||
@@ -1300,6 +1498,108 @@ export default {
|
||||
customerRecords(response) {
|
||||
return this.unwrapData(response)?.records || [];
|
||||
},
|
||||
async loadProjectOptions() {
|
||||
if (this.projectLoading) return;
|
||||
this.projectLoading = true;
|
||||
try {
|
||||
const response = await getProjectList(1, 9999, {});
|
||||
this.projectOptions = this.customerRecords(response);
|
||||
if (
|
||||
this.form.projectId &&
|
||||
this.form.projectName &&
|
||||
!this.projectOptions.some(item => String(item.id) === String(this.form.projectId))
|
||||
) {
|
||||
this.projectOptions.unshift({
|
||||
id: this.form.projectId,
|
||||
projectName: this.form.projectName,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
this.projectOptions = [];
|
||||
this.$message.warning('项目列表加载失败,请稍后重试');
|
||||
} finally {
|
||||
this.projectLoading = false;
|
||||
}
|
||||
},
|
||||
async loadContractOptions(projectId) {
|
||||
if (!projectId) {
|
||||
this.contractOptions = [];
|
||||
return;
|
||||
}
|
||||
this.contractLoading = true;
|
||||
try {
|
||||
const response = await formalApi.getContractOptions('', projectId);
|
||||
this.contractOptions = (this.unwrapData(response) || []).filter(
|
||||
item => String(item.projectId || '') === String(projectId)
|
||||
);
|
||||
if (
|
||||
this.form.contractId &&
|
||||
this.form.contractName &&
|
||||
!this.contractOptions.some(item => String(item.id) === String(this.form.contractId))
|
||||
) {
|
||||
this.contractOptions.unshift({
|
||||
id: this.form.contractId,
|
||||
contractNo: this.form.contractNo,
|
||||
contractName: this.form.contractName,
|
||||
projectId,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
this.contractOptions = [];
|
||||
this.$message.warning('项目合同加载失败,请稍后重试');
|
||||
} finally {
|
||||
this.contractLoading = false;
|
||||
}
|
||||
},
|
||||
async handleProjectChange(projectId) {
|
||||
const project = this.projectOptions.find(item => String(item.id) === String(projectId));
|
||||
this.form.projectName = project?.projectName || project?.projectShortName || '';
|
||||
Object.assign(this.form, {
|
||||
deptId: project?.undertakeDeptId || project?.businessDeptId || null,
|
||||
deptName: project?.undertakeDeptName || project?.businessDeptName || '',
|
||||
contractId: null,
|
||||
contractNo: '',
|
||||
contractName: '',
|
||||
payerName: '',
|
||||
payeeName: '',
|
||||
settlementAmount: 0,
|
||||
payableAmount: 0,
|
||||
appliedAmount: 0,
|
||||
billType: '',
|
||||
});
|
||||
this.contractOptions = [];
|
||||
this.clearReceiptAccount();
|
||||
this.form.invoices = [];
|
||||
this.paymentRecords = [];
|
||||
await this.loadContractOptions(projectId);
|
||||
await this.loadAttachmentRuleData([]);
|
||||
},
|
||||
async handleAdvanceContractChange(contractId) {
|
||||
const contract = this.contractOptions.find(
|
||||
item => String(item.id) === String(contractId)
|
||||
);
|
||||
if (!contract) {
|
||||
Object.assign(this.form, {
|
||||
contractNo: '',
|
||||
contractName: '',
|
||||
payerName: '',
|
||||
payeeName: '',
|
||||
});
|
||||
this.clearReceiptAccount();
|
||||
return;
|
||||
}
|
||||
Object.assign(this.form, {
|
||||
contractNo: contract.contractNo || '',
|
||||
contractName: contract.contractName || '',
|
||||
payerName: contract.payerName || contract.partyA || '',
|
||||
payeeName: contract.payeeName || contract.partyB || '',
|
||||
settlementAmount: 0,
|
||||
payableAmount: 0,
|
||||
billType: '',
|
||||
});
|
||||
await Promise.all([this.loadReceiptAccountOptions(), this.loadAttachmentRuleData([])]);
|
||||
if (this.billPayment) this.loadBillOptions();
|
||||
},
|
||||
async loadReceiptAccountOptions(autoSelectDefault = true) {
|
||||
const payeeName = String(this.form.payeeName || '').trim();
|
||||
const preserveCurrentValue = autoSelectDefault === false;
|
||||
@@ -1483,12 +1783,10 @@ export default {
|
||||
this.loadPreReferences();
|
||||
},
|
||||
async openReference() {
|
||||
if (this.form.paymentType === 'project_advance') return;
|
||||
this.referenceVisible = true;
|
||||
if (this.form.paymentType === 'project_advance') {
|
||||
const response = await formalApi.getContractOptions('');
|
||||
this.contractRows = this.unwrapData(response) || [];
|
||||
return;
|
||||
}
|
||||
this.formalSelection = [];
|
||||
this.preSelection = [];
|
||||
this.formalPage.current = 1;
|
||||
this.prePage.current = 1;
|
||||
await this.loadSettlementReferences();
|
||||
@@ -1498,7 +1796,6 @@ export default {
|
||||
this.form.invoices = [];
|
||||
this.paymentRecords = [];
|
||||
this.clearReceiptAccount();
|
||||
const settlementType = row.settlementType || 'payable';
|
||||
Object.assign(this.form, {
|
||||
projectId: row.projectId,
|
||||
projectName: row.projectName,
|
||||
@@ -1507,21 +1804,24 @@ export default {
|
||||
contractId: row.id,
|
||||
contractNo: row.contractNo,
|
||||
contractName: row.contractName,
|
||||
payerName:
|
||||
row.payerName || (settlementType === 'receivable' ? row.partyB : row.partyA) || '',
|
||||
payeeName:
|
||||
row.payeeName || (settlementType === 'receivable' ? row.partyA : row.partyB) || '',
|
||||
payableAmount: Number((Number(row.fundDemand || 0) * 10000).toFixed(2)),
|
||||
billType: row.settlementMode ? '项目预付' : '',
|
||||
payerName: row.payerName || row.partyA || '',
|
||||
payeeName: row.payeeName || row.partyB || '',
|
||||
settlementAmount: 0,
|
||||
payableAmount: 0,
|
||||
appliedAmount: 0,
|
||||
billType: '',
|
||||
});
|
||||
this.referenceVisible = false;
|
||||
await Promise.all([this.loadReceiptAccountOptions(), this.loadAttachmentRuleData([])]);
|
||||
if (this.billPayment) this.loadBillOptions();
|
||||
},
|
||||
async selectFormal(row) {
|
||||
const validReferenceId = this.isValidReferenceId(row.id);
|
||||
const [amountResponse, detailResponse] = await Promise.all([
|
||||
api.getReferenceAmount('settlement_payment', row.id, this.form.id),
|
||||
formalApi.getDetail(row.id),
|
||||
validReferenceId
|
||||
? api.getReferenceAmount('settlement_payment', row.id, this.form.id)
|
||||
: Promise.resolve(null),
|
||||
validReferenceId ? formalApi.getDetail(row.id) : Promise.resolve(null),
|
||||
]);
|
||||
const amount = this.unwrapData(amountResponse) || {};
|
||||
const settlementAmount = Number(amount.settlementAmount ?? row.settlementAmount ?? 0);
|
||||
@@ -1531,6 +1831,7 @@ export default {
|
||||
Object.assign(this.form, {
|
||||
paymentType: 'settlement_payment',
|
||||
settlementId: row.id,
|
||||
settlementIds: [row.id],
|
||||
preSettlementId: null,
|
||||
preSettlementIds: [],
|
||||
settlementNo: row.formalSettlementNo,
|
||||
@@ -1565,10 +1866,27 @@ export default {
|
||||
]);
|
||||
if (this.billPayment) this.loadBillOptions();
|
||||
},
|
||||
async confirmFormalSelection() {
|
||||
const rows = this.formalSelection || [];
|
||||
if (!rows.length) return;
|
||||
if (new Set(rows.map(item => String(item.contractId || ''))).size !== 1) {
|
||||
this.$message.warning('所选正式结算单必须属于同一合同');
|
||||
return;
|
||||
}
|
||||
const validRows = rows.filter(row => this.isValidReferenceId(row.id));
|
||||
const details = await Promise.all(validRows.map(row => formalApi.getDetail(row.id)));
|
||||
this.applyTransferredFormalSettlements(
|
||||
details.map((response, index) => ({ ...validRows[index], ...this.unwrapData(response) }))
|
||||
);
|
||||
this.referenceVisible = false;
|
||||
},
|
||||
async selectPre(row) {
|
||||
const validReferenceId = this.isValidReferenceId(row.id);
|
||||
const [amountResponse, detailResponse] = await Promise.all([
|
||||
api.getReferenceAmount('progress_advance', row.id, this.form.id),
|
||||
preApi.getDetail(row.id),
|
||||
validReferenceId
|
||||
? api.getReferenceAmount('progress_advance', row.id, this.form.id)
|
||||
: Promise.resolve(null),
|
||||
validReferenceId ? preApi.getDetail(row.id) : Promise.resolve(null),
|
||||
]);
|
||||
const amount = this.unwrapData(amountResponse) || {};
|
||||
const settlementAmount = Number(amount.settlementAmount ?? row.settlementAmount ?? 0);
|
||||
@@ -1582,6 +1900,7 @@ export default {
|
||||
preSettlementId: row.id,
|
||||
preSettlementIds: [row.id],
|
||||
settlementId: null,
|
||||
settlementIds: [],
|
||||
preSettlementNo: row.preSettlementNo,
|
||||
settlementNo: '',
|
||||
projectId: row.projectId,
|
||||
@@ -1610,13 +1929,33 @@ export default {
|
||||
]);
|
||||
if (this.billPayment) this.loadBillOptions();
|
||||
},
|
||||
async confirmPreSelection() {
|
||||
const rows = this.preSelection || [];
|
||||
if (!rows.length) return;
|
||||
if (new Set(rows.map(item => String(item.contractId || ''))).size !== 1) {
|
||||
this.$message.warning('所选预结算单必须属于同一合同');
|
||||
return;
|
||||
}
|
||||
const validRows = rows.filter(row => this.isValidReferenceId(row.id));
|
||||
const details = await Promise.all(validRows.map(row => preApi.getDetail(row.id)));
|
||||
this.applyTransferredPreSettlements(
|
||||
details.map((response, index) => ({ ...validRows[index], ...this.unwrapData(response) }))
|
||||
);
|
||||
this.referenceVisible = false;
|
||||
},
|
||||
normalizeAttachments(files) {
|
||||
this.form.attachments = (files || []).map(file => ({
|
||||
...file,
|
||||
attachmentType:
|
||||
file.attachmentType ||
|
||||
MATERIAL_TYPE_MAP[this.resolveAttachmentMaterial(file)?.key] ||
|
||||
'other',
|
||||
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 || '',
|
||||
}));
|
||||
},
|
||||
@@ -1664,7 +2003,7 @@ export default {
|
||||
this.downloadableAttachments.forEach((file, index) => {
|
||||
window.setTimeout(
|
||||
() => downloadFileByUrl(this.attachmentFileUrl(file), this.attachmentFileName(file)),
|
||||
index * 300
|
||||
index * 500
|
||||
);
|
||||
});
|
||||
},
|
||||
@@ -1673,6 +2012,7 @@ export default {
|
||||
id,
|
||||
paymentType,
|
||||
settlementId,
|
||||
settlementIds,
|
||||
preSettlementId,
|
||||
preSettlementIds,
|
||||
projectId,
|
||||
@@ -1704,6 +2044,7 @@ export default {
|
||||
id,
|
||||
paymentType,
|
||||
settlementId,
|
||||
settlementIds,
|
||||
preSettlementId,
|
||||
preSettlementIds,
|
||||
projectId,
|
||||
|
||||
@@ -75,7 +75,11 @@
|
||||
{{ displayValue(row[column.prop]) }}
|
||||
</el-link>
|
||||
<span v-else-if="column.link">{{ displayValue(row[column.prop]) }}</span>
|
||||
<el-tag v-else-if="column.status" :type="statusType(row.claimStatus)" class="status-text">
|
||||
<el-tag
|
||||
v-else-if="column.status"
|
||||
:type="statusType(row.claimStatus)"
|
||||
class="status-text"
|
||||
>
|
||||
{{ displayValue(row[column.prop]) }}
|
||||
</el-tag>
|
||||
<span v-else-if="column.money">{{ formatMoney(row[column.prop]) }}</span>
|
||||
@@ -109,7 +113,7 @@ import { mapGetters } from 'vuex';
|
||||
import * as api from '@/api/payment/receiptFlow';
|
||||
import { receiptFlowTableColumns } from '@/option/payment/receiptFlow';
|
||||
|
||||
const receiptFlowMockRows = [
|
||||
const receiptFlowMockTemplates = [
|
||||
{
|
||||
receiptNoticeNo: 'SKTZ20260827001',
|
||||
payerName: '华东供应链管理有限公司',
|
||||
@@ -136,6 +140,34 @@ const receiptFlowMockRows = [
|
||||
},
|
||||
];
|
||||
|
||||
const randomDigits = length =>
|
||||
Array.from({ length }, () => Math.floor(Math.random() * 10)).join('');
|
||||
|
||||
const formatMockDateTime = date => {
|
||||
const pad = value => String(value).padStart(2, '0');
|
||||
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(
|
||||
date.getHours()
|
||||
)}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`;
|
||||
};
|
||||
|
||||
const createReceiptFlowMockRows = () =>
|
||||
receiptFlowMockTemplates.map((template, index) => {
|
||||
const transactionDate = new Date();
|
||||
transactionDate.setDate(transactionDate.getDate() - Math.floor(Math.random() * 7));
|
||||
transactionDate.setHours(8 + Math.floor(Math.random() * 10), Math.floor(Math.random() * 60), 0);
|
||||
const transactionTime = formatMockDateTime(transactionDate);
|
||||
const dateCode = transactionTime.slice(0, 10).replaceAll('-', '');
|
||||
return {
|
||||
...template,
|
||||
receiptNoticeNo: `SKTZ${dateCode}${randomDigits(5)}`,
|
||||
receiptAmount: Math.floor(10000 + Math.random() * 90000),
|
||||
counterpartyAccount: `${template.counterpartyAccount.slice(0, -6)}${randomDigits(6)}`,
|
||||
transactionTime,
|
||||
detailSerialNo: `MOCK-RF-${dateCode}-${index + 1}${randomDigits(4)}`,
|
||||
sourceUpdatedTime: transactionTime,
|
||||
};
|
||||
});
|
||||
|
||||
const emptyQuery = () => ({
|
||||
receiptNoticeNo: '',
|
||||
counterpartyName: '',
|
||||
@@ -210,9 +242,8 @@ export default {
|
||||
async handleSync() {
|
||||
this.syncing = true;
|
||||
try {
|
||||
const syncedCount = Number(
|
||||
this.unwrapData(await api.sync({ flows: receiptFlowMockRows })) || 0
|
||||
);
|
||||
const mockRows = createReceiptFlowMockRows();
|
||||
const syncedCount = Number(this.unwrapData(await api.sync({ flows: mockRows })) || 0);
|
||||
this.$message.success(`流水同步完成,共同步${syncedCount}条`);
|
||||
await this.loadTable();
|
||||
} finally {
|
||||
|
||||
Reference in New Issue
Block a user