1573 lines
58 KiB
Vue
1573 lines
58 KiB
Vue
<template>
|
||
<div class="bill-detail-container">
|
||
<el-tabs v-model="activeTab">
|
||
<el-tab-pane label="账单基本信息" name="basicInfo">
|
||
<div class="bill-header">
|
||
<div class="bill-header-left">
|
||
<h3>账单信息</h3>
|
||
</div>
|
||
<div class="bill-header-right">
|
||
<el-button
|
||
type="primary"
|
||
@click="openPaymentDialog"
|
||
:disabled="!canPayBill">
|
||
账单支付
|
||
</el-button>
|
||
</div>
|
||
</div>
|
||
<el-descriptions :column="3" border size="medium">
|
||
<el-descriptions-item label="账单编号">{{ billDetail.totBillNo }}</el-descriptions-item>
|
||
<el-descriptions-item label="账单来源">{{ getBillSourceName(billDetail.billSource) }}</el-descriptions-item>
|
||
<el-descriptions-item label="账单状态">
|
||
<el-tag :type="billDetail.billStatus === '1' ? 'success' : 'info'">
|
||
{{ billDetail.billStatus === '1' ? '开启' : '关闭' }}
|
||
</el-tag>
|
||
</el-descriptions-item>
|
||
<el-descriptions-item label="对方名称">{{ billDetail.paysdName }}</el-descriptions-item>
|
||
<el-descriptions-item label="支付联系方式">{{ billDetail.payeeContact }}</el-descriptions-item>
|
||
<el-descriptions-item label="关联合同">{{ billDetail.contractNumber || '无' }}</el-descriptions-item>
|
||
<el-descriptions-item label="费用类型">{{ billDetail.feTpName }}</el-descriptions-item>
|
||
<el-descriptions-item label="计费周期">
|
||
{{ billDetail.chggBgnDt }} 至 {{ billDetail.chggEndDt }}
|
||
</el-descriptions-item>
|
||
<el-descriptions-item label="应收日期">{{ billDetail.pybDt }}</el-descriptions-item>
|
||
<el-descriptions-item label="结清状态">
|
||
<el-tag :type="getClearStatusType(billDetail.clearStatus)">
|
||
{{ getClearStatusName(billDetail.clearStatus) }}
|
||
</el-tag>
|
||
</el-descriptions-item>
|
||
<el-descriptions-item label="逾期状态">
|
||
<el-tag :type="billDetail.overdueStatus === '1' ? 'danger' : 'success'">
|
||
{{ billDetail.overdueStatus === '1' ? '逾期' : '正常' }}
|
||
</el-tag>
|
||
</el-descriptions-item>
|
||
<el-descriptions-item label="滞纳金状态">
|
||
<el-tag :type="getLateFeeStatusType(billDetail.ovdueStatus)">
|
||
{{ getLateFeeStatusName(billDetail.ovdueStatus) }}
|
||
</el-tag>
|
||
</el-descriptions-item>
|
||
<el-descriptions-item label="账单金额">{{ formatAmount(billDetail.accblAmt) }}</el-descriptions-item>
|
||
<el-descriptions-item label="应收金额">{{ formatAmount(billDetail.accigRcvAmt) }}</el-descriptions-item>
|
||
<el-descriptions-item label="实收金额">{{ formatAmount(billDetail.atmRecvAmt) }}</el-descriptions-item>
|
||
<el-descriptions-item label="需收金额">{{ formatAmount(billDetail.needAmount) }}</el-descriptions-item>
|
||
<el-descriptions-item label="调整金额">{{ formatAmount(billDetail.adjAmt) }}</el-descriptions-item>
|
||
<el-descriptions-item label="应收滞纳金">{{ formatAmount(billDetail.receivableOvdueAmt) }}</el-descriptions-item>
|
||
<el-descriptions-item label="税率">{{ billDetail.taxRate }}%</el-descriptions-item>
|
||
<el-descriptions-item label="税额">{{ formatAmount(billDetail.paybleTaxAmount) }}</el-descriptions-item>
|
||
<el-descriptions-item label="含税规则">{{ billDetail.taxInclusiveRule }}</el-descriptions-item>
|
||
<el-descriptions-item label="特殊账单类型">{{ billDetail.specialBillType }}</el-descriptions-item>
|
||
<el-descriptions-item label="开据状态">
|
||
<el-tag :type="billDetail.receiptStatus === '1' ? 'success' : 'info'">
|
||
{{ billDetail.receiptStatus === '1' ? '已开据' : '未开据' }}
|
||
</el-tag>
|
||
</el-descriptions-item>
|
||
<el-descriptions-item label="所属公司">{{ billDetail.corNm }}</el-descriptions-item>
|
||
<el-descriptions-item label="租赁数(计租面积)">{{ billDetail.rentArea }} ㎡</el-descriptions-item>
|
||
<el-descriptions-item label="项目名称">{{ billDetail.projectName }}</el-descriptions-item>
|
||
<el-descriptions-item label="账单备注" :span="3">{{ billDetail.billRemark || '无' }}</el-descriptions-item>
|
||
</el-descriptions>
|
||
</el-tab-pane>
|
||
|
||
<el-tab-pane label="账单明细查询" name="billDetails">
|
||
<el-table :data="billDetailsList" border style="width: 100%">
|
||
<el-table-column prop="feeTypeName" label="费用类型" min-width="120" show-overflow-tooltip></el-table-column>
|
||
<el-table-column label="应收金额" min-width="120" align="right">
|
||
<template slot-scope="scope">
|
||
{{ formatAmount(scope.row.receivableAmount) }}
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="税率" min-width="80" align="center">
|
||
<template slot-scope="scope">
|
||
{{ scope.row.taxRate }}%
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="税额" min-width="120" align="right">
|
||
<template slot-scope="scope">
|
||
{{ formatAmount(scope.row.taxAmount) }}
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column prop="startDate" label="开始日期" min-width="120" align="center"></el-table-column>
|
||
<el-table-column prop="endDate" label="结束日期" min-width="120" align="center"></el-table-column>
|
||
<el-table-column prop="remark" label="账单备注" min-width="200" show-overflow-tooltip></el-table-column>
|
||
<el-table-column label="操作" width="120" align="center" fixed="right">
|
||
<template slot-scope="scope">
|
||
<el-button
|
||
size="mini"
|
||
type="text"
|
||
@click="handleAdjustment(scope.row)"
|
||
:disabled="!canAdjustBill">
|
||
调整
|
||
</el-button>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
<div v-if="billDetailsList.length === 0" class="empty-data">
|
||
<el-empty description="暂无账单明细"></el-empty>
|
||
</div>
|
||
</el-tab-pane>
|
||
|
||
<el-tab-pane label="账单调整" name="billAdjustment">
|
||
<div class="bill-adjustment-container">
|
||
<div class="adjustment-list">
|
||
<el-table :data="adjustmentList" border style="width: 100%; margin-top: 15px">
|
||
<el-table-column prop="adjTpCd" label="调整类型" min-width="100">
|
||
<template slot-scope="scope">
|
||
{{ scope.row.adjTpCd === '1' ? '调增' : '调减' }}
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column prop="adjDate" label="调整日期" min-width="120" align="center"></el-table-column>
|
||
<el-table-column prop="adjMethod" label="调整方式" min-width="120">
|
||
<template slot-scope="scope">
|
||
{{ scope.row.adjMethod === '1' ? '按金额调整' : '按比例调整' }}
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="调整金额" min-width="120" align="right">
|
||
<template slot-scope="scope">
|
||
{{ formatAmount(scope.row.adjAmt) }}
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="调整比例" min-width="100" align="center">
|
||
<template slot-scope="scope">
|
||
{{ scope.row.adjRate ? scope.row.adjRate + '%' : '-' }}
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column prop="adjStatus" label="调整状态" min-width="100">
|
||
<template slot-scope="scope">
|
||
<el-tag :type="scope.row.adjStatus === '1' ? 'success' : 'info'">
|
||
{{ scope.row.adjStatus === '1' ? '正常' : '作废' }}
|
||
</el-tag>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column prop="billDetailType" label="账单明细类型" min-width="120">
|
||
<template slot-scope="scope">
|
||
{{ scope.row.billDetailType === '1' ? '滞纳金' : '原账单' }}
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column prop="adjSource" label="调整来源" min-width="120">
|
||
<template slot-scope="scope">
|
||
{{ scope.row.adjSource === '1' ? '账单直接调整' : '合同调整' }}
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column prop="remark" label="备注" min-width="150" show-overflow-tooltip></el-table-column>
|
||
<el-table-column prop="createTime" label="创建时间" min-width="150" align="center"></el-table-column>
|
||
<el-table-column label="操作" width="120" align="center" fixed="right">
|
||
<template slot-scope="scope">
|
||
<el-button size="mini" type="text" @click="viewAdjustmentDetail(scope.row)" v-if="scope.row.adjStatus === '1'">附件详情</el-button>
|
||
<el-button size="mini" type="text" class="delete-btn" @click="voidAdjustment(scope.row)" v-if="scope.row.adjStatus === '1'">作废</el-button>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
<div v-if="adjustmentList.length === 0" class="empty-data">
|
||
<el-empty description="暂无账单调整记录"></el-empty>
|
||
</div>
|
||
<div class="pagination-container" v-if="adjustmentList.length > 0">
|
||
<el-pagination
|
||
background
|
||
@size-change="handleAdjustmentSizeChange"
|
||
@current-change="handleAdjustmentCurrentChange"
|
||
:current-page="adjustmentQuery.pageNum"
|
||
:page-sizes="[5, 10, 20, 50]"
|
||
:page-size="adjustmentQuery.pageSize"
|
||
layout="total, sizes, prev, pager, next, jumper"
|
||
:total="adjustmentTotal">
|
||
</el-pagination>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</el-tab-pane>
|
||
|
||
<el-tab-pane label="房源信息" name="roomInfo">
|
||
<el-table :data="roomList" border style="width: 100%">
|
||
<el-table-column prop="roomNumber" label="房号" min-width="100"></el-table-column>
|
||
<el-table-column prop="floorName" label="楼层" min-width="100"></el-table-column>
|
||
<el-table-column prop="buildingName" label="楼宇" min-width="120"></el-table-column>
|
||
<el-table-column prop="projectName" label="项目" min-width="150"></el-table-column>
|
||
<el-table-column prop="rentArea" label="计租面积(㎡)" min-width="120" align="right"></el-table-column>
|
||
</el-table>
|
||
<div v-if="roomList.length === 0" class="empty-data">
|
||
<el-empty description="暂无房源信息"></el-empty>
|
||
</div>
|
||
</el-tab-pane>
|
||
|
||
<el-tab-pane label="附件信息" name="attachment">
|
||
<div class="attachment-header">
|
||
<el-upload
|
||
class="upload-area"
|
||
action="#"
|
||
:http-request="uploadFile"
|
||
:before-upload="beforeFileUpload"
|
||
multiple
|
||
:limit="10">
|
||
<el-button type="primary" icon="el-icon-plus">添加附件</el-button>
|
||
<div slot="tip" class="el-upload__tip">只能上传jpg/png/pdf文件,且不超过5MB</div>
|
||
</el-upload>
|
||
</div>
|
||
<el-table :data="attachmentList" border style="width: 100%; margin-top: 15px">
|
||
<el-table-column prop="fileName" label="文件名" min-width="200" show-overflow-tooltip></el-table-column>
|
||
<el-table-column prop="operatorName" label="操作人" width="150"></el-table-column>
|
||
<el-table-column prop="operateTime" label="操作时间" width="180"></el-table-column>
|
||
<el-table-column label="操作" width="200" align="center">
|
||
<template slot-scope="scope">
|
||
<el-button size="mini" type="text" @click="previewFile(scope.row)">预览</el-button>
|
||
<el-button size="mini" type="text" @click="downloadFile(scope.row)">下载</el-button>
|
||
<el-button size="mini" type="text" class="delete-btn" @click="deleteFile(scope.row)">删除</el-button>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
<div v-if="attachmentList.length === 0" class="empty-data">
|
||
<el-empty description="暂无附件信息"></el-empty>
|
||
</div>
|
||
</el-tab-pane>
|
||
|
||
<el-tab-pane label="操作记录" name="operationLog">
|
||
<el-table :data="operationList" border style="width: 100%">
|
||
<el-table-column prop="oprContent" label="操作内容" min-width="300" show-overflow-tooltip></el-table-column>
|
||
<el-table-column prop="oprPersonNm" label="操作人" width="150"></el-table-column>
|
||
<el-table-column prop="oprTime" label="操作时间" width="180"></el-table-column>
|
||
</el-table>
|
||
<div v-if="operationList.length === 0" class="empty-data">
|
||
<el-empty description="暂无操作记录"></el-empty>
|
||
</div>
|
||
<div class="pagination-container" v-if="operationTotal > 0">
|
||
<el-pagination
|
||
background
|
||
@size-change="handleOperationSizeChange"
|
||
@current-change="handleOperationCurrentChange"
|
||
:current-page="operationQuery.pageNum"
|
||
:page-sizes="[5, 10, 20, 50]"
|
||
:page-size="operationQuery.pageSize"
|
||
layout="total, sizes, prev, pager, next, jumper"
|
||
:total="operationTotal">
|
||
</el-pagination>
|
||
</div>
|
||
</el-tab-pane>
|
||
</el-tabs>
|
||
|
||
<!-- 文件预览对话框 -->
|
||
<el-dialog title="附件预览" :visible.sync="previewDialogVisible" width="800px" append-to-body>
|
||
<div class="preview-container" v-loading="previewLoading">
|
||
<div v-if="previewUrl && isPdf" class="preview-iframe">
|
||
<iframe :src="previewUrl" width="100%" height="500px" frameborder="0"></iframe>
|
||
</div>
|
||
<div v-else-if="previewUrl && isImage" class="preview-image">
|
||
<img :src="previewUrl" style="max-width: 100%;" />
|
||
</div>
|
||
<div v-else-if="!previewLoading" class="preview-error">
|
||
<i class="el-icon-warning"></i>
|
||
<p>预览加载失败,请尝试下载后查看</p>
|
||
<div class="preview-actions">
|
||
<el-button type="primary" @click="downloadCurrentPreview">下载文件</el-button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div slot="footer" class="dialog-footer">
|
||
<el-button @click="previewDialogVisible = false">关 闭</el-button>
|
||
</div>
|
||
</el-dialog>
|
||
|
||
<!-- 账单调整对话框 -->
|
||
<el-dialog title="账单调整" :visible.sync="adjustmentDialogVisible" width="700px" append-to-body @close="resetAdjustmentForm">
|
||
<el-form ref="adjustmentForm" :model="adjustmentForm" :rules="adjustmentRules" label-width="120px">
|
||
<el-form-item label="调整类型" prop="adjTpCd" required>
|
||
<el-radio-group v-model="adjustmentForm.adjTpCd">
|
||
<el-radio label="1">调增</el-radio>
|
||
<el-radio label="2">调减</el-radio>
|
||
</el-radio-group>
|
||
</el-form-item>
|
||
|
||
<el-form-item label="调整日期" prop="adjDate" required>
|
||
<el-date-picker
|
||
v-model="adjustmentForm.adjDate"
|
||
type="date"
|
||
placeholder="选择日期"
|
||
value-format="yyyy-MM-dd"
|
||
style="width: 220px">
|
||
</el-date-picker>
|
||
</el-form-item>
|
||
|
||
<el-form-item label="调整方式" prop="adjMethod" required>
|
||
<el-radio-group v-model="adjustmentForm.adjMethod" @change="handleAdjMethodChange">
|
||
<el-radio label="1">按金额调整</el-radio>
|
||
<el-radio label="2">按比例调整</el-radio>
|
||
</el-radio-group>
|
||
</el-form-item>
|
||
|
||
<el-form-item label="调整金额" prop="adjAmt" required v-if="adjustmentForm.adjMethod == '1'">
|
||
<el-input-number
|
||
v-model="adjustmentForm.adjAmt"
|
||
:min="0"
|
||
:precision="2"
|
||
:max="adjustmentForm.adjTpCd === '2' ? billDetail.accigRcvAmt : undefined"
|
||
@change="calculateAdjustAmount"
|
||
style="width: 220px">
|
||
</el-input-number>
|
||
<span class="form-hint">调整后金额: {{ formatAmount(adjustedAmount) }}</span>
|
||
</el-form-item>
|
||
|
||
<el-form-item label="调整比例(%)" prop="adjRate" required v-if="adjustmentForm.adjMethod == '2'">
|
||
<el-input-number
|
||
v-model="adjustmentForm.adjRate"
|
||
:min="0"
|
||
:max="adjustmentForm.adjTpCd === '2' ? 100 : undefined"
|
||
:precision="2"
|
||
@change="calculateAdjustAmount"
|
||
style="width: 220px">
|
||
</el-input-number>
|
||
<span class="form-hint">调整后金额: {{ formatAmount(adjustedAmount) }}</span>
|
||
</el-form-item>
|
||
|
||
<el-form-item label="备注" prop="remark">
|
||
<el-input
|
||
type="textarea"
|
||
v-model="adjustmentForm.remark"
|
||
placeholder="请输入调整原因或其他备注信息"
|
||
:rows="3"
|
||
style="width: 400px">
|
||
</el-input>
|
||
</el-form-item>
|
||
|
||
<el-form-item label="附件">
|
||
<el-upload
|
||
class="upload-area"
|
||
action="#"
|
||
:http-request="uploadAdjustmentAttachment"
|
||
:file-list="adjustmentFileList"
|
||
:before-upload="beforeAdjustmentUpload"
|
||
multiple
|
||
:limit="10">
|
||
<el-button size="small" type="primary">添加附件</el-button>
|
||
<div slot="tip" class="el-upload__tip">只能上传jpg/png/pdf文件,且不超过5MB</div>
|
||
</el-upload>
|
||
|
||
<el-table v-if="adjustmentFileList.length > 0" :data="adjustmentFileList" border style="width: 100%; margin-top: 15px">
|
||
<el-table-column prop="name" label="文件名" min-width="200" show-overflow-tooltip></el-table-column>
|
||
<el-table-column label="操作" width="150" align="center">
|
||
<template slot-scope="scope">
|
||
<el-button size="mini" type="text" @click="previewAdjustmentFile(scope.row)">预览</el-button>
|
||
<el-button size="mini" type="text" class="delete-btn" @click="deleteAdjustmentFile(scope.$index)">删除</el-button>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
</el-form-item>
|
||
</el-form>
|
||
<div slot="footer" class="dialog-footer">
|
||
<el-button @click="adjustmentDialogVisible = false">取 消</el-button>
|
||
<el-button type="primary" @click="submitAdjustment" :loading="adjustmentSubmitting">确 定</el-button>
|
||
</div>
|
||
</el-dialog>
|
||
|
||
<!-- 账单调整附件详情弹窗 -->
|
||
<el-dialog title="账单调整附件详情" :visible.sync="adjustmentAttachmentDialogVisible" width="800px" append-to-body>
|
||
<el-table :data="adjustmentAttachmentList" border style="width: 100%">
|
||
<el-table-column prop="fileName" label="文件名" min-width="200" show-overflow-tooltip></el-table-column>
|
||
<el-table-column prop="createUserId" label="操作人" width="150"></el-table-column>
|
||
<el-table-column prop="createTime" label="操作时间" width="180"></el-table-column>
|
||
<el-table-column label="操作" width="200" align="center">
|
||
<template slot-scope="scope">
|
||
<el-button size="mini" type="text" @click="previewFile(scope.row)">预览</el-button>
|
||
<el-button size="mini" type="text" @click="downloadFile(scope.row)">下载</el-button>
|
||
<el-button size="mini" type="text" class="delete-btn" @click="deleteAdjustmentAttachment(scope.row)">删除</el-button>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
<div v-if="adjustmentAttachmentList.length === 0" class="empty-data">
|
||
<el-empty description="暂无附件信息"></el-empty>
|
||
</div>
|
||
<div slot="footer" class="dialog-footer">
|
||
<el-button @click="adjustmentAttachmentDialogVisible = false">关 闭</el-button>
|
||
</div>
|
||
</el-dialog>
|
||
|
||
<!-- 账单支付对话框 -->
|
||
<el-dialog title="账单支付" :visible.sync="paymentDialogVisible" width="800px" append-to-body @close="resetPaymentForm">
|
||
<el-form ref="paymentForm" :model="paymentForm" :rules="paymentRules" label-width="120px" class="payment-form">
|
||
<!-- 第一部分:结清状态 -->
|
||
<div class="payment-section">
|
||
<h4>账单状态</h4>
|
||
<el-descriptions :column="1" border size="medium">
|
||
<el-descriptions-item label="结清状态">
|
||
<el-tag :type="getClearStatusType(billDetail.clearStatus)">
|
||
{{ getClearStatusName(billDetail.clearStatus) }}
|
||
</el-tag>
|
||
</el-descriptions-item>
|
||
</el-descriptions>
|
||
</div>
|
||
|
||
<!-- 第二部分:可编辑费用列表 -->
|
||
<div class="payment-section">
|
||
<h4>费用信息</h4>
|
||
<el-descriptions :column="2" border size="medium">
|
||
<el-descriptions-item label="费用类型">{{ billDetail.feTpName }}</el-descriptions-item>
|
||
<el-descriptions-item label="应收金额">{{ formatAmount(billDetail.accigRcvAmt) }}</el-descriptions-item>
|
||
<el-descriptions-item label="实收金额">{{ formatAmount(billDetail.atmRecvAmt) }}</el-descriptions-item>
|
||
<el-descriptions-item label="需收金额">{{ formatAmount(billDetail.needAmount) }}</el-descriptions-item>
|
||
</el-descriptions>
|
||
|
||
<el-form-item style="margin-top: 20px;" label="本次收款" prop="occuAmt" required>
|
||
<div class="amount-input-wrapper">
|
||
<el-input-number
|
||
v-model="paymentForm.occuAmt"
|
||
:min="0.01"
|
||
:max="billDetail.needAmount || 0"
|
||
:precision="2"
|
||
:step="0.01"
|
||
@change="calculateRemainingAmount"
|
||
style="width: 300px;margin-right: 10px;">
|
||
</el-input-number>
|
||
<el-button size="small" type="primary" @click="fillFullAmount" class="fill-btn">收完</el-button>
|
||
</div>
|
||
</el-form-item>
|
||
|
||
<el-form-item label="剩余待收">
|
||
<span>{{ formatAmount(remainingAmount) }}</span>
|
||
</el-form-item>
|
||
|
||
<el-form-item label="入账时间" prop="inaccDate" required>
|
||
<el-date-picker
|
||
v-model="paymentForm.inaccDate"
|
||
type="date"
|
||
placeholder="选择日期"
|
||
value-format="yyyy-MM-dd"
|
||
style="width: 300px">
|
||
</el-date-picker>
|
||
</el-form-item>
|
||
</div>
|
||
|
||
<!-- 第三部分:支付方式 -->
|
||
<div class="payment-section">
|
||
<h4>支付方式</h4>
|
||
<el-form-item label="支付方式" prop="payModeName" required>
|
||
<el-select v-model="paymentForm.payModeName" placeholder="请选择支付方式" style="width: 300px">
|
||
<el-option
|
||
v-for="item in paymentMethods"
|
||
:key="item.value"
|
||
:label="item.label"
|
||
:value="item.value">
|
||
</el-option>
|
||
</el-select>
|
||
</el-form-item>
|
||
|
||
<el-form-item label="凭证号">
|
||
<el-input v-model="paymentForm.txVchrNo" placeholder="请输入凭证号" style="width: 300px"></el-input>
|
||
</el-form-item>
|
||
|
||
<el-form-item label="摘要">
|
||
<el-input v-model="paymentForm.summ" placeholder="请输入摘要" style="width: 300px"></el-input>
|
||
</el-form-item>
|
||
|
||
<el-form-item label="备注">
|
||
<el-input
|
||
type="textarea"
|
||
v-model="paymentForm.remark"
|
||
placeholder="请输入备注信息"
|
||
:rows="3"
|
||
style="width: 300px">
|
||
</el-input>
|
||
</el-form-item>
|
||
</div>
|
||
|
||
<!-- 第四部分:附件信息 -->
|
||
<div class="payment-section">
|
||
<h4>附件信息</h4>
|
||
<div class="upload-wrapper">
|
||
<el-upload
|
||
class="upload-area"
|
||
action="#"
|
||
:http-request="uploadPaymentAttachment"
|
||
:file-list="paymentFileList"
|
||
:before-upload="beforePaymentUpload"
|
||
multiple
|
||
:limit="10">
|
||
<el-button size="small" type="primary">添加附件</el-button>
|
||
<div slot="tip" class="el-upload__tip">只能上传jpg/png/pdf文件,且不超过5MB</div>
|
||
</el-upload>
|
||
</div>
|
||
|
||
<el-table v-if="paymentFileList.length > 0" :data="paymentFileList" border style="width: 100%; margin-top: 15px">
|
||
<el-table-column prop="name" label="文件名" min-width="200" show-overflow-tooltip></el-table-column>
|
||
<el-table-column label="操作" width="150" align="center">
|
||
<template slot-scope="scope">
|
||
<el-button size="mini" type="text" @click="previewPaymentFile(scope.row)">预览</el-button>
|
||
<el-button size="mini" type="text" class="delete-btn" @click="deletePaymentFile(scope.$index)">删除</el-button>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
</div>
|
||
</el-form>
|
||
<div slot="footer" class="dialog-footer">
|
||
<el-button @click="paymentDialogVisible = false">取 消</el-button>
|
||
<el-button type="primary" @click="submitPayment" :loading="paymentSubmitting">确 定</el-button>
|
||
</div>
|
||
</el-dialog>
|
||
</div>
|
||
</template>
|
||
|
||
<script>
|
||
import { getBillDetail, getBillAttachmentList, deleteBillAttachment, getBillAdjustmentList, addBillAdjustment, voidBillAdjustment, uploadBillAdjustmentAttachment, uploadBillAttachment, getBillAdjustmentAttachmentList, deleteBillAdjustmentAttachment, getBillOperationRecords, submitBillPayment, uploadBillPaymentAttachment, deleteBillPaymentAttachment } from '@/api/finance'
|
||
|
||
export default {
|
||
name: 'BillDetail',
|
||
props: {
|
||
billId: {
|
||
type: [Number, String],
|
||
required: true
|
||
}
|
||
},
|
||
data() {
|
||
return {
|
||
activeTab: 'basicInfo',
|
||
loading: false,
|
||
billDetail: {},
|
||
billDetailsList: [],
|
||
roomList: [],
|
||
attachmentList: [],
|
||
operationList: [],
|
||
|
||
// 账单调整相关
|
||
adjustmentList: [],
|
||
adjustmentTotal: 0,
|
||
adjustmentQuery: {
|
||
pageNum: 1,
|
||
pageSize: 10,
|
||
billId: ''
|
||
},
|
||
adjustmentLoading: false,
|
||
adjustmentDialogVisible: false,
|
||
adjustmentSubmitting: false,
|
||
adjustedAmount: 0,
|
||
adjustmentForm: {
|
||
billId: '',
|
||
billDetailId: '',
|
||
adjTpCd: '1', // 默认调增
|
||
adjDate: new Date().toISOString().slice(0, 10), // 默认今天
|
||
adjMethod: '1', // 默认按金额调整
|
||
adjAmt: 0,
|
||
adjRate: 0,
|
||
billDetailType: '原账单',
|
||
adjSource: '账单直接调整',
|
||
projectId: '',
|
||
remark: '',
|
||
adjSource: '1' // 默认 账单直接调整
|
||
},
|
||
adjustmentRules: {
|
||
adjTpCd: [
|
||
{ required: true, message: '请选择调整类型', trigger: 'change' }
|
||
],
|
||
adjDate: [
|
||
{ required: true, message: '请选择调整日期', trigger: 'change' }
|
||
],
|
||
adjMethod: [
|
||
{ required: true, message: '请选择调整方式', trigger: 'change' }
|
||
],
|
||
adjAmt: [
|
||
{ required: true, message: '请输入调整金额', trigger: 'blur' }
|
||
],
|
||
adjRate: [
|
||
{ required: true, message: '请输入调整比例', trigger: 'blur' }
|
||
]
|
||
},
|
||
adjustmentFileList: [],
|
||
|
||
// 预览相关
|
||
previewDialogVisible: false,
|
||
previewLoading: false,
|
||
previewUrl: '',
|
||
previewBlob: null,
|
||
currentPreviewName: '',
|
||
isPdf: false,
|
||
isImage: false,
|
||
|
||
// 调整附件相关
|
||
adjustmentAttachmentDialogVisible: false,
|
||
adjustmentAttachmentList: [],
|
||
currentAdjustmentId: '',
|
||
|
||
// 操作记录相关
|
||
operationTotal: 0,
|
||
operationQuery: {
|
||
pageNum: 1,
|
||
pageSize: 10
|
||
},
|
||
|
||
// 支付相关
|
||
paymentDialogVisible: false,
|
||
paymentSubmitting: false,
|
||
paymentForm: {
|
||
billId: '',
|
||
billDetailId: '',
|
||
occuAmt: 0,
|
||
inaccDate: new Date().toISOString().slice(0, 10),
|
||
payModeName: '1',
|
||
txVchrNo: '',
|
||
summ: '',
|
||
remark: ''
|
||
},
|
||
paymentRules: {
|
||
occuAmt: [
|
||
{ required: true, message: '请输入本次收款金额', trigger: 'blur' }
|
||
],
|
||
inaccDate: [
|
||
{ required: true, message: '请选择入账时间', trigger: 'change' }
|
||
],
|
||
payModeName: [
|
||
{ required: true, message: '请选择支付方式', trigger: 'change' }
|
||
]
|
||
},
|
||
paymentFileList: [],
|
||
paymentMethods: [
|
||
{ value: '1', label: '现金' },
|
||
{ value: '2', label: '网银转账' },
|
||
{ value: '3', label: 'POS机' },
|
||
{ value: '4', label: '支付宝' },
|
||
{ value: '5', label: '微信' },
|
||
{ value: '6', label: '转账支票' },
|
||
{ value: '7', label: '其他方式' },
|
||
{ value: '8', label: '线上支付' }
|
||
],
|
||
remainingAmount: 0, // 剩余待收金额
|
||
}
|
||
},
|
||
created() {
|
||
this.getBillData()
|
||
},
|
||
computed: {
|
||
// 判断是否可以调整账单
|
||
canAdjustBill() {
|
||
if (!this.billDetail) return false
|
||
// 账单状态为开启且不在支付中
|
||
return this.billDetail.billStatus === '1' && this.billDetail.clearStatus !== '6'
|
||
},
|
||
// 判断是否可以支付账单
|
||
canPayBill() {
|
||
if (!this.billDetail) return false
|
||
// 账单不能是已结清,且不能在支付中
|
||
return this.billDetail.clearStatus !== '1' && this.billDetail.clearStatus !== '6'
|
||
}
|
||
},
|
||
methods: {
|
||
// 获取账单数据
|
||
getBillData() {
|
||
this.loading = true
|
||
|
||
// 获取账单详情
|
||
getBillDetail(this.billId).then(response => {
|
||
this.billDetail = response.data || {}
|
||
|
||
// 处理账单明细
|
||
this.processBillDetails()
|
||
|
||
// 设置调整查询参数的账单ID
|
||
this.adjustmentQuery.billId = this.billId
|
||
// 获取账单调整记录
|
||
this.getAdjustmentList()
|
||
|
||
// 模拟房源信息数据,实际应从接口获取或者账单详情中提取
|
||
this.roomList = this.billDetail.rooms
|
||
|
||
this.loading = false
|
||
}).catch(() => {
|
||
this.loading = false
|
||
})
|
||
|
||
// 获取附件列表
|
||
this.getAttachmentList()
|
||
|
||
// 获取操作记录
|
||
this.getOperationList()
|
||
},
|
||
// 处理账单明细数据
|
||
processBillDetails() {
|
||
this.billDetailsList = []
|
||
|
||
// 添加原始账单明细
|
||
if (this.billDetail) {
|
||
this.billDetailsList.push({
|
||
feeTypeName: this.billDetail.feTpName || '-',
|
||
receivableAmount: this.billDetail.accigRcvAmt,
|
||
taxRate: this.billDetail.taxRate || 0,
|
||
taxAmount: this.billDetail.paybleTaxAmount || 0,
|
||
startDate: this.billDetail.chggBgnDt || '-',
|
||
endDate: this.billDetail.chggEndDt || '-',
|
||
remark: this.billDetail.billRemark || '-',
|
||
billDetailType: '0' // 原账单类型
|
||
})
|
||
}
|
||
|
||
// 添加滞纳金明细(如果存在)
|
||
if (this.billDetail.ovdueStatus === '1' && this.billDetail.receivableOvdueAmt > 0) {
|
||
this.billDetailsList.push({
|
||
feeTypeName: '滞纳金',
|
||
receivableAmount: this.billDetail.receivableOvdueAmt || 0,
|
||
taxRate: 0,
|
||
taxAmount: 0,
|
||
startDate: this.billDetail.pybDt || '-', // 滞纳金产生日期,使用应收日期
|
||
endDate: this.formatDate(new Date()) || '-', // 滞纳金产生的结束日期,使用当前日期
|
||
remark: '滞纳金',
|
||
billDetailType: '1' // 滞纳金类型
|
||
})
|
||
}
|
||
},
|
||
// 格式化日期
|
||
formatDate(date) {
|
||
if (!(date instanceof Date)) return '';
|
||
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}`;
|
||
},
|
||
// 格式化金额
|
||
formatAmount(amount) {
|
||
if (amount === undefined || amount === null) {
|
||
return '0.00'
|
||
}
|
||
return parseFloat(amount).toFixed(2) + ' 元'
|
||
},
|
||
// 获取账单来源名称
|
||
getBillSourceName(source) {
|
||
const sourceMap = {
|
||
'1': '合同账单',
|
||
'2': '收费标准账单',
|
||
'3': '自建全部账单',
|
||
'4': '自建关联合同账单',
|
||
'5': '自建未关联合同账单'
|
||
}
|
||
return sourceMap[source] || '未知来源'
|
||
},
|
||
// 获取结清状态类型
|
||
getClearStatusType(status) {
|
||
const statusMap = {
|
||
'0': 'info',
|
||
'1': 'success',
|
||
'2': 'warning',
|
||
'3': 'danger',
|
||
'5': 'primary',
|
||
'6': 'info',
|
||
'7': 'success'
|
||
}
|
||
return statusMap[status] || 'info'
|
||
},
|
||
// 获取结清状态名称
|
||
getClearStatusName(status) {
|
||
const statusMap = {
|
||
'0': '未付款',
|
||
'1': '已结清',
|
||
'2': '部分结清',
|
||
'3': '待退款',
|
||
'5': '待收款',
|
||
'6': '对账确认中',
|
||
'7': '已付款'
|
||
}
|
||
return statusMap[status] || '未知状态'
|
||
},
|
||
// 获取滞纳金状态类型
|
||
getLateFeeStatusType(status) {
|
||
const statusMap = {
|
||
'0': 'info',
|
||
'1': 'warning',
|
||
'2': 'success'
|
||
}
|
||
return statusMap[status] || 'info'
|
||
},
|
||
// 获取滞纳金状态名称
|
||
getLateFeeStatusName(status) {
|
||
const statusMap = {
|
||
'0': '无滞纳金',
|
||
'1': '有滞纳金',
|
||
'2': '滞纳金免除'
|
||
}
|
||
return statusMap[status] || '未知状态'
|
||
},
|
||
// 预览文件
|
||
previewFile(file) {
|
||
this.previewLoading = true
|
||
this.previewDialogVisible = true
|
||
this.currentPreviewName = file.fileName || '附件'
|
||
this.previewUrl = ''
|
||
this.isPdf = false
|
||
this.isImage = false
|
||
|
||
// 根据文件名后缀判断文件类型
|
||
const fileExt = file.fileName ? file.fileName.split('.').pop().toLowerCase() : ''
|
||
|
||
try {
|
||
// 创建blob对象 - 实际项目中应该调用预览API获取文件数据
|
||
fetch(file.storePath)
|
||
.then(response => response.blob())
|
||
.then(blob => {
|
||
this.previewBlob = blob
|
||
|
||
// 判断文件类型
|
||
if (fileExt === 'pdf') {
|
||
this.isPdf = true
|
||
this.isImage = false
|
||
} else if (['jpg', 'jpeg', 'png', 'gif'].includes(fileExt)) {
|
||
this.isPdf = false
|
||
this.isImage = true
|
||
}
|
||
|
||
// 创建URL用于预览
|
||
this.previewUrl = URL.createObjectURL(blob)
|
||
this.previewLoading = false
|
||
})
|
||
.catch(error => {
|
||
console.error('预览失败', error)
|
||
this.$message.error('获取预览数据失败')
|
||
this.previewLoading = false
|
||
})
|
||
} catch (error) {
|
||
console.error('预览失败', error)
|
||
this.$message.error('获取预览数据失败')
|
||
this.previewLoading = false
|
||
}
|
||
},
|
||
|
||
// 下载当前预览的文件
|
||
downloadCurrentPreview() {
|
||
if (!this.previewBlob) {
|
||
this.$message.error('没有可下载的文件')
|
||
return
|
||
}
|
||
|
||
const url = URL.createObjectURL(this.previewBlob)
|
||
const link = document.createElement('a')
|
||
link.href = url
|
||
link.setAttribute('download', this.currentPreviewName)
|
||
document.body.appendChild(link)
|
||
link.click()
|
||
document.body.removeChild(link)
|
||
URL.revokeObjectURL(url)
|
||
},
|
||
|
||
// 下载文件
|
||
downloadFile(file) {
|
||
// 实际应调用API下载
|
||
const link = document.createElement('a')
|
||
link.href = file.storePath
|
||
link.download = file.fileName
|
||
link.click()
|
||
},
|
||
|
||
// 删除附件
|
||
deleteFile(file) {
|
||
this.$confirm('确认删除该附件?', '提示', {
|
||
confirmButtonText: '确定',
|
||
cancelButtonText: '取消',
|
||
type: 'warning'
|
||
}).then(() => {
|
||
deleteBillAttachment(file.id).then(response => {
|
||
if (response.code === '0000000000000000') {
|
||
this.$message.success('删除成功')
|
||
// 重新获取附件列表
|
||
this.getAttachmentList()
|
||
} else {
|
||
this.$message.error(response.message || '删除失败')
|
||
}
|
||
}).catch(() => {
|
||
this.$message.error('删除失败')
|
||
})
|
||
}).catch(() => {})
|
||
},
|
||
|
||
// 获取附件列表
|
||
getAttachmentList() {
|
||
getBillAttachmentList(this.billId).then(response => {
|
||
this.attachmentList = response.data || []
|
||
})
|
||
},
|
||
// 获取账单调整记录
|
||
getAdjustmentList() {
|
||
this.adjustmentLoading = true
|
||
getBillAdjustmentList({
|
||
...this.adjustmentQuery,
|
||
billId: this.billId
|
||
}).then(response => {
|
||
if (response.code === '0000000000000000') {
|
||
this.adjustmentList = response.data.list || []
|
||
this.adjustmentTotal = response.data.total || 0
|
||
} else {
|
||
this.$message.error(response.message || '获取账单调整记录失败')
|
||
this.adjustmentList = []
|
||
this.adjustmentTotal = 0
|
||
}
|
||
this.adjustmentLoading = false
|
||
}).catch(() => {
|
||
this.adjustmentList = []
|
||
this.adjustmentTotal = 0
|
||
this.adjustmentLoading = false
|
||
})
|
||
},
|
||
// 账单调整分页大小变化
|
||
handleAdjustmentSizeChange(val) {
|
||
this.adjustmentQuery.pageSize = val
|
||
this.getAdjustmentList()
|
||
},
|
||
// 账单调整页码变化
|
||
handleAdjustmentCurrentChange(val) {
|
||
this.adjustmentQuery.pageNum = val
|
||
this.getAdjustmentList()
|
||
},
|
||
// 查看账单调整详情
|
||
viewAdjustmentDetail(row) {
|
||
this.adjustmentAttachmentDialogVisible = true
|
||
this.adjustmentAttachmentList = []
|
||
this.currentAdjustmentId = row.id
|
||
this.getAdjustmentAttachmentList()
|
||
},
|
||
// 进行账单调整
|
||
handleAdjustment(row = null) {
|
||
this.adjustmentDialogVisible = true
|
||
this.adjustmentForm.billId = this.billId
|
||
this.adjustmentForm.billDetailId = this.billDetail.billDetails && this.billDetail.billDetails.length > 0
|
||
? this.billDetail.billDetails[0].id
|
||
: ''
|
||
this.adjustmentForm.projectId = this.billDetail.projectId || ''
|
||
|
||
// 设置账单明细类型
|
||
if (row && row.billDetailType) {
|
||
this.adjustmentForm.billDetailType = row.billDetailType
|
||
} else {
|
||
this.adjustmentForm.billDetailType = '0' // 默认原账单
|
||
}
|
||
|
||
// 预设调整金额为0,方便用户输入
|
||
this.adjustmentForm.adjAmt = 0
|
||
this.adjustmentForm.adjRate = 0
|
||
this.calculateAdjustAmount()
|
||
},
|
||
// 调整方式变更
|
||
handleAdjMethodChange() {
|
||
// 重新计算调整后金额
|
||
this.calculateAdjustAmount()
|
||
},
|
||
// 计算调整后金额
|
||
calculateAdjustAmount() {
|
||
const originalAmount = parseFloat(this.billDetail.accigRcvAmt || 0)
|
||
let adjustmentValue = 0
|
||
|
||
if (this.adjustmentForm.adjMethod === '1') {
|
||
adjustmentValue = parseFloat(this.adjustmentForm.adjAmt || 0)
|
||
} else if (this.adjustmentForm.adjMethod === '2') {
|
||
// 按比例计算调整金额
|
||
adjustmentValue = originalAmount * parseFloat(this.adjustmentForm.adjRate || 0) / 100
|
||
}
|
||
|
||
// 根据调整类型计算最终金额
|
||
if (this.adjustmentForm.adjTpCd === '1') { // 调增
|
||
this.adjustedAmount = originalAmount + adjustmentValue
|
||
} else { // 调减
|
||
this.adjustedAmount = Math.max(0, originalAmount - adjustmentValue)
|
||
}
|
||
},
|
||
// 上传附件前检查
|
||
beforeAdjustmentUpload(file) {
|
||
const isValidType = file.type === 'image/jpeg' || file.type === 'image/png' || file.type === 'application/pdf'
|
||
const isLt5M = file.size / 1024 / 1024 < 5
|
||
|
||
if (!isValidType) {
|
||
this.$message.error('只能上传JPG/PNG/PDF格式的文件!')
|
||
}
|
||
|
||
if (!isLt5M) {
|
||
this.$message.error('文件大小不能超过5MB!')
|
||
}
|
||
|
||
return isValidType && isLt5M
|
||
},
|
||
// 上传调整附件
|
||
uploadAdjustmentAttachment(options) {
|
||
const file = options.file
|
||
|
||
// 创建临时URL供预览
|
||
const tempUrl = URL.createObjectURL(file)
|
||
|
||
// 向上传列表中添加文件
|
||
this.adjustmentFileList.push({
|
||
name: file.name,
|
||
file: file,
|
||
url: tempUrl,
|
||
status: 'ready'
|
||
})
|
||
},
|
||
// 预览调整附件
|
||
previewAdjustmentFile(file) {
|
||
this.previewLoading = true
|
||
this.previewDialogVisible = true
|
||
this.currentPreviewName = file.name || '附件'
|
||
this.previewUrl = ''
|
||
this.isPdf = false
|
||
this.isImage = false
|
||
|
||
// 根据文件名后缀判断文件类型
|
||
const fileExt = file.name ? file.name.split('.').pop().toLowerCase() : ''
|
||
|
||
try {
|
||
// 获取文件内容
|
||
const blob = file.file ? file.file : new Blob()
|
||
this.previewBlob = blob
|
||
|
||
// 设置文件类型标志
|
||
if (fileExt === 'pdf') {
|
||
this.isPdf = true
|
||
} else if (['jpg', 'jpeg', 'png', 'gif'].includes(fileExt)) {
|
||
this.isImage = true
|
||
}
|
||
|
||
// 创建URL用于预览
|
||
this.previewUrl = file.url
|
||
this.previewLoading = false
|
||
} catch (error) {
|
||
console.error('预览失败', error)
|
||
this.$message.error('获取预览数据失败')
|
||
this.previewLoading = false
|
||
}
|
||
},
|
||
// 删除调整附件
|
||
deleteAdjustmentFile(index) {
|
||
this.adjustmentFileList.splice(index, 1)
|
||
},
|
||
// 重置调整表单
|
||
resetAdjustmentForm() {
|
||
if (this.$refs.adjustmentForm) {
|
||
this.$refs.adjustmentForm.resetFields()
|
||
}
|
||
this.adjustmentFileList = []
|
||
this.adjustedAmount = 0
|
||
this.adjustmentForm = {
|
||
billId: '',
|
||
billDetailId: '',
|
||
adjTpCd: '1',
|
||
adjDate: new Date().toISOString().slice(0, 10),
|
||
adjMethod: '1',
|
||
adjAmt: 0,
|
||
adjRate: 0,
|
||
billDetailType: '原账单',
|
||
adjSource: '账单直接调整',
|
||
projectId: '',
|
||
remark: '',
|
||
adjSource: '1'
|
||
}
|
||
},
|
||
// 提交账单调整
|
||
submitAdjustment() {
|
||
this.$refs.adjustmentForm.validate(valid => {
|
||
if (valid) {
|
||
this.adjustmentSubmitting = true
|
||
|
||
// 构建请求数据
|
||
const adjustmentData = { ...this.adjustmentForm }
|
||
|
||
// 如果是比例调整,需要计算实际调整金额
|
||
if (adjustmentData.adjMethod === '2') {
|
||
const originalAmount = parseFloat(this.billDetail.accigRcvAmt || 0)
|
||
adjustmentData.adjAmt = originalAmount * parseFloat(adjustmentData.adjRate || 0) / 100
|
||
}
|
||
|
||
// 提交调整
|
||
addBillAdjustment(adjustmentData)
|
||
.then(response => {
|
||
if (response.code === '0000000000000000') {
|
||
// 上传附件
|
||
if (this.adjustmentFileList.length > 0) {
|
||
this.uploadAdjustmentAttachments(response.data)
|
||
} else {
|
||
this.$message.success('账单调整成功')
|
||
this.adjustmentDialogVisible = false
|
||
// 重新获取账单信息和调整记录
|
||
this.getBillData()
|
||
this.adjustmentSubmitting = false
|
||
}
|
||
} else {
|
||
this.$message.error(response.message || '账单调整失败')
|
||
this.adjustmentSubmitting = false
|
||
}
|
||
})
|
||
.catch(error => {
|
||
console.error('账单调整失败', error)
|
||
this.$message.error('账单调整失败,请稍后重试')
|
||
this.adjustmentSubmitting = false
|
||
})
|
||
}
|
||
})
|
||
},
|
||
// 上传调整附件
|
||
uploadAdjustmentAttachments(adjustmentId) {
|
||
const uploadPromises = this.adjustmentFileList.map(fileItem => {
|
||
return uploadBillAdjustmentAttachment(adjustmentId, fileItem.file)
|
||
})
|
||
|
||
Promise.all(uploadPromises)
|
||
.then(() => {
|
||
this.$message.success('账单调整成功')
|
||
this.adjustmentDialogVisible = false
|
||
// 重新获取账单信息和调整记录
|
||
this.getBillData()
|
||
this.adjustmentSubmitting = false
|
||
})
|
||
.catch(error => {
|
||
console.error('附件上传失败', error)
|
||
this.$message.warning('账单调整成功,但部分附件上传失败')
|
||
this.adjustmentDialogVisible = false
|
||
// 重新获取账单信息和调整记录
|
||
this.getBillData()
|
||
this.adjustmentSubmitting = false
|
||
})
|
||
},
|
||
// 作废调整记录
|
||
voidAdjustment(row) {
|
||
this.$confirm('确定要作废此账单调整吗?', '提示', {
|
||
confirmButtonText: '确定',
|
||
cancelButtonText: '取消',
|
||
type: 'warning'
|
||
}).then(() => {
|
||
voidBillAdjustment({
|
||
adjustmentId: row.id,
|
||
canceledDate: new Date().toISOString().slice(0, 10),
|
||
remark: '用户手动作废'
|
||
}).then(response => {
|
||
if (response.code === '0000000000000000') {
|
||
this.$message.success('作废成功')
|
||
// 重新获取账单信息和调整记录
|
||
this.getBillData()
|
||
} else {
|
||
this.$message.error(response.message || '作废失败')
|
||
}
|
||
}).catch(() => {
|
||
this.$message.error('作废失败')
|
||
})
|
||
}).catch(() => {})
|
||
},
|
||
// 上传前检查文件
|
||
beforeFileUpload(file) {
|
||
const isValidType = file.type === 'image/jpeg' || file.type === 'image/png' || file.type === 'application/pdf'
|
||
const isLt5M = file.size / 1024 / 1024 < 5
|
||
|
||
if (!isValidType) {
|
||
this.$message.error('只能上传JPG/PNG/PDF格式的文件!')
|
||
}
|
||
|
||
if (!isLt5M) {
|
||
this.$message.error('文件大小不能超过5MB!')
|
||
}
|
||
|
||
return isValidType && isLt5M
|
||
},
|
||
// 上传文件
|
||
uploadFile(options) {
|
||
const file = options.file
|
||
this.$confirm('确认上传该文件?', '提示', {
|
||
confirmButtonText: '确定',
|
||
cancelButtonText: '取消',
|
||
type: 'info'
|
||
}).then(() => {
|
||
this.$message.info('开始上传附件,请稍候...')
|
||
// 调用上传附件API
|
||
uploadBillAttachment(this.billDetail.id, file).then(response => {
|
||
if (response.code === '0000000000000000') {
|
||
this.$message.success('上传成功')
|
||
// 重新获取附件列表
|
||
this.getAttachmentList()
|
||
} else {
|
||
this.$message.error(response.message || '上传失败')
|
||
}
|
||
}).catch(() => {
|
||
this.$message.error('上传失败')
|
||
})
|
||
}).catch(() => {
|
||
// 取消上传
|
||
})
|
||
},
|
||
// 获取调整附件列表
|
||
getAdjustmentAttachmentList() {
|
||
getBillAdjustmentAttachmentList(this.currentAdjustmentId).then(response => {
|
||
if (response.code === '0000000000000000') {
|
||
this.adjustmentAttachmentList = response.data || []
|
||
} else {
|
||
this.$message.error(response.message || '获取附件列表失败')
|
||
this.adjustmentAttachmentList = []
|
||
}
|
||
}).catch(() => {
|
||
this.$message.error('获取附件列表失败')
|
||
this.adjustmentAttachmentList = []
|
||
})
|
||
},
|
||
// 删除账单调整附件
|
||
deleteAdjustmentAttachment(file) {
|
||
this.$confirm('确认删除该附件?', '提示', {
|
||
confirmButtonText: '确定',
|
||
cancelButtonText: '取消',
|
||
type: 'warning'
|
||
}).then(() => {
|
||
deleteBillAdjustmentAttachment(file.id).then(response => {
|
||
if (response.code === '0000000000000000') {
|
||
this.$message.success('删除成功')
|
||
// 重新获取附件列表
|
||
this.getAdjustmentAttachmentList()
|
||
} else {
|
||
this.$message.error(response.message || '删除失败')
|
||
}
|
||
}).catch(() => {
|
||
this.$message.error('删除失败')
|
||
})
|
||
}).catch(() => {})
|
||
},
|
||
// 操作记录分页大小变化
|
||
handleOperationSizeChange(val) {
|
||
this.operationQuery.pageSize = val
|
||
this.getOperationList()
|
||
},
|
||
// 操作记录页码变化
|
||
handleOperationCurrentChange(val) {
|
||
this.operationQuery.pageNum = val
|
||
this.getOperationList()
|
||
},
|
||
// 获取操作记录列表
|
||
getOperationList() {
|
||
getBillOperationRecords(this.billId, this.operationQuery).then(response => {
|
||
if (response.code === '0000000000000000') {
|
||
this.operationList = response.data.list || []
|
||
this.operationTotal = response.data.total || 0
|
||
} else {
|
||
this.$message.error(response.message || '获取操作记录失败')
|
||
this.operationList = []
|
||
this.operationTotal = 0
|
||
}
|
||
}).catch(() => {
|
||
this.$message.error('获取操作记录失败')
|
||
this.operationList = []
|
||
this.operationTotal = 0
|
||
})
|
||
},
|
||
// 打开支付对话框
|
||
openPaymentDialog() {
|
||
// 重置表单
|
||
this.resetPaymentForm()
|
||
|
||
this.paymentDialogVisible = true
|
||
this.paymentForm.billId = this.billId
|
||
this.paymentForm.billDetailId = this.billDetail.billDetails && this.billDetail.billDetails.length > 0
|
||
? this.billDetail.billDetails[0].id
|
||
: ''
|
||
this.paymentForm.occuAmt = 0
|
||
this.calculateRemainingAmount()
|
||
},
|
||
// 计算剩余待收金额
|
||
calculateRemainingAmount() {
|
||
const needAmount = parseFloat(this.billDetail.needAmount || 0)
|
||
const paymentAmount = parseFloat(this.paymentForm.occuAmt || 0)
|
||
this.remainingAmount = Math.max(0, needAmount - paymentAmount)
|
||
},
|
||
// 填充全额
|
||
fillFullAmount() {
|
||
this.paymentForm.occuAmt = parseFloat(this.billDetail.needAmount || 0)
|
||
this.calculateRemainingAmount()
|
||
},
|
||
// 重置支付表单
|
||
resetPaymentForm() {
|
||
if (this.$refs.paymentForm) {
|
||
this.$refs.paymentForm.resetFields()
|
||
}
|
||
this.paymentFileList = []
|
||
this.remainingAmount = 0
|
||
this.paymentForm = {
|
||
billId: '',
|
||
billDetailId: '',
|
||
occuAmt: 0,
|
||
inaccDate: new Date().toISOString().slice(0, 10),
|
||
payModeName: '1',
|
||
txVchrNo: '',
|
||
summ: '',
|
||
remark: ''
|
||
}
|
||
},
|
||
// 上传前检查支付附件
|
||
beforePaymentUpload(file) {
|
||
const isValidType = file.type === 'image/jpeg' || file.type === 'image/png' || file.type === 'application/pdf'
|
||
const isLt5M = file.size / 1024 / 1024 < 5
|
||
|
||
if (!isValidType) {
|
||
this.$message.error('只能上传JPG/PNG/PDF格式的文件!')
|
||
return false
|
||
}
|
||
|
||
if (!isLt5M) {
|
||
this.$message.error('文件大小不能超过5MB!')
|
||
return false
|
||
}
|
||
|
||
return isValidType && isLt5M
|
||
},
|
||
// 上传支付附件
|
||
uploadPaymentAttachment(options) {
|
||
const file = options.file
|
||
|
||
// 创建临时URL供预览
|
||
const tempUrl = URL.createObjectURL(file)
|
||
|
||
// 向上传列表中添加文件
|
||
this.paymentFileList.push({
|
||
name: file.name,
|
||
file: file,
|
||
url: tempUrl,
|
||
status: 'ready'
|
||
})
|
||
},
|
||
// 预览支付附件
|
||
previewPaymentFile(file) {
|
||
this.previewLoading = true
|
||
this.previewDialogVisible = true
|
||
this.currentPreviewName = file.name || '附件'
|
||
this.previewUrl = ''
|
||
this.isPdf = false
|
||
this.isImage = false
|
||
|
||
// 根据文件名后缀判断文件类型
|
||
const fileExt = file.name ? file.name.split('.').pop().toLowerCase() : ''
|
||
|
||
try {
|
||
// 获取文件内容
|
||
const blob = file.file ? file.file : new Blob()
|
||
this.previewBlob = blob
|
||
|
||
// 设置文件类型标志
|
||
if (fileExt === 'pdf') {
|
||
this.isPdf = true
|
||
} else if (['jpg', 'jpeg', 'png', 'gif'].includes(fileExt)) {
|
||
this.isImage = true
|
||
}
|
||
|
||
// 创建URL用于预览
|
||
this.previewUrl = file.url
|
||
this.previewLoading = false
|
||
} catch (error) {
|
||
console.error('预览失败', error)
|
||
this.$message.error('获取预览数据失败')
|
||
this.previewLoading = false
|
||
}
|
||
},
|
||
// 删除支付附件
|
||
deletePaymentFile(index) {
|
||
this.paymentFileList.splice(index, 1)
|
||
},
|
||
// 提交支付
|
||
submitPayment() {
|
||
this.$refs.paymentForm.validate(valid => {
|
||
if (valid) {
|
||
// 验证支付金额
|
||
if (this.paymentForm.occuAmt <= 0) {
|
||
this.$message.error('支付金额必须大于0')
|
||
return
|
||
}
|
||
|
||
if (this.paymentForm.occuAmt > this.billDetail.needAmount) {
|
||
this.$message.error('支付金额不能超过需收金额')
|
||
return
|
||
}
|
||
|
||
this.paymentSubmitting = true
|
||
|
||
// 准备表单数据
|
||
const paymentData = {...this.paymentForm}
|
||
|
||
// 如果有附件,需要转换日期格式,后端接收Date类型
|
||
if (paymentData.inaccDate) {
|
||
// 保持字符串格式,后端会处理
|
||
}
|
||
|
||
submitBillPayment(paymentData)
|
||
.then(response => {
|
||
if (response.code === '0000000000000000') {
|
||
// 上传附件
|
||
if (this.paymentFileList.length > 0) {
|
||
this.uploadPaymentAttachments(response.data.id)
|
||
} else {
|
||
this.$message.success('账单支付成功')
|
||
this.paymentDialogVisible = false
|
||
// 重新获取账单信息
|
||
this.getBillData()
|
||
this.paymentSubmitting = false
|
||
}
|
||
} else {
|
||
this.$message.error(response.message || '账单支付失败')
|
||
this.paymentSubmitting = false
|
||
}
|
||
})
|
||
.catch(error => {
|
||
console.error('账单支付失败', error)
|
||
this.$message.error('账单支付失败,请稍后重试')
|
||
this.paymentSubmitting = false
|
||
})
|
||
}
|
||
})
|
||
},
|
||
// 上传支付附件
|
||
uploadPaymentAttachments(transactionId) {
|
||
if (this.paymentFileList.length === 0) {
|
||
this.$message.success('账单支付成功')
|
||
this.paymentDialogVisible = false
|
||
// 重新获取账单信息
|
||
this.getBillData()
|
||
this.paymentSubmitting = false
|
||
return
|
||
}
|
||
|
||
const uploadPromises = this.paymentFileList.map(fileItem => {
|
||
return uploadBillPaymentAttachment(transactionId, fileItem.file)
|
||
})
|
||
|
||
Promise.all(uploadPromises)
|
||
.then(() => {
|
||
this.$message.success('账单支付成功')
|
||
this.paymentDialogVisible = false
|
||
// 重新获取账单信息
|
||
this.getBillData()
|
||
this.paymentSubmitting = false
|
||
})
|
||
.catch(error => {
|
||
console.error('附件上传失败', error)
|
||
this.$message.warning('账单支付成功,但部分附件上传失败')
|
||
this.paymentDialogVisible = false
|
||
// 重新获取账单信息
|
||
this.getBillData()
|
||
this.paymentSubmitting = false
|
||
})
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.delete-btn {
|
||
color: #F56C6C;
|
||
}
|
||
|
||
.pagination-container {
|
||
margin-top: 20px;
|
||
text-align: right;
|
||
}
|
||
|
||
.bill-detail-container {
|
||
padding: 10px;
|
||
|
||
.bill-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 15px;
|
||
|
||
.bill-header-left {
|
||
h3 {
|
||
margin: 0;
|
||
}
|
||
}
|
||
}
|
||
|
||
.payment-form {
|
||
.el-form-item {
|
||
margin-bottom: 18px;
|
||
}
|
||
|
||
.amount-input-wrapper {
|
||
display: flex;
|
||
align-items: center;
|
||
|
||
.fill-btn {
|
||
margin-left: 10px;
|
||
height: 32px;
|
||
padding: 8px 15px;
|
||
}
|
||
}
|
||
}
|
||
|
||
.payment-section {
|
||
margin-bottom: 25px;
|
||
|
||
h4 {
|
||
margin: 15px 0 10px;
|
||
padding-left: 8px;
|
||
border-left: 3px solid #409EFF;
|
||
font-size: 16px;
|
||
color: #303133;
|
||
}
|
||
|
||
.el-descriptions {
|
||
margin-bottom: 15px;
|
||
}
|
||
|
||
.upload-wrapper {
|
||
margin-bottom: 10px;
|
||
padding: 10px;
|
||
background-color: #f9f9f9;
|
||
border-radius: 4px;
|
||
}
|
||
}
|
||
|
||
.empty-data {
|
||
margin: 20px 0;
|
||
display: flex;
|
||
justify-content: center;
|
||
}
|
||
|
||
.bill-adjustment-container {
|
||
.operation-bar {
|
||
display: flex;
|
||
justify-content: flex-end;
|
||
margin-bottom: 15px;
|
||
}
|
||
}
|
||
|
||
.form-hint {
|
||
margin-left: 10px;
|
||
color: #909399;
|
||
font-size: 13px;
|
||
}
|
||
|
||
.upload-area {
|
||
margin-bottom: 15px;
|
||
}
|
||
|
||
.preview-container {
|
||
min-height: 500px;
|
||
max-height: 700px;
|
||
overflow: auto;
|
||
|
||
.preview-iframe {
|
||
width: 100%;
|
||
height: 500px;
|
||
border: none;
|
||
}
|
||
|
||
.preview-image {
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
min-height: 300px;
|
||
}
|
||
|
||
.preview-error {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
min-height: 300px;
|
||
color: #909399;
|
||
|
||
i {
|
||
font-size: 48px;
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
p {
|
||
font-size: 16px;
|
||
}
|
||
|
||
.preview-actions {
|
||
margin-top: 20px;
|
||
}
|
||
}
|
||
}
|
||
|
||
.attachment-header {
|
||
margin-bottom: 15px;
|
||
}
|
||
}
|
||
</style> |