|
|
<template>
|
|
|
<div>
|
|
|
<!-- 付款登记-->
|
|
|
<xy-dialog
|
|
|
ref="paymentRegistration"
|
|
|
title="付款登记"
|
|
|
:is-show.sync="isShowPaymentRegistration"
|
|
|
type="form"
|
|
|
class="payment-registration"
|
|
|
:form="paymentRegistrationForm"
|
|
|
:rules="paymentRegistrationRules"
|
|
|
@submit="submit"
|
|
|
>
|
|
|
<template v-slot:extraFormTop>
|
|
|
<div class="payment-registration-row">
|
|
|
<div class="payment-registration-row-title">受款单位</div>
|
|
|
<div class="payment-registration-row-content">{{ contract.supply }}</div>
|
|
|
</div>
|
|
|
<div class="payment-registration-row">
|
|
|
<div class="payment-registration-row-title">合同名称</div>
|
|
|
<div class="payment-registration-row-content">{{ contract.name }}</div>
|
|
|
</div>
|
|
|
<div class="payment-registration-row">
|
|
|
<div class="payment-registration-row-title">合同金额</div>
|
|
|
<div class="payment-registration-row-content">{{ priceFormat(contract.money) }} (元)</div>
|
|
|
</div>
|
|
|
<div style="display: flex">
|
|
|
<div class="payment-registration-row">
|
|
|
<div class="payment-registration-row-title">已申请金额</div>
|
|
|
<div class="payment-registration-row-content">{{ totalApplyMoney() }} (元)</div>
|
|
|
</div>
|
|
|
<div class="payment-registration-row">
|
|
|
<div class="payment-registration-row-title">已申请笔数</div>
|
|
|
<div class="payment-registration-row-content">{{ payment.length }}</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
<div style="display: flex">
|
|
|
<div class="payment-registration-row">
|
|
|
<div class="payment-registration-row-title">已付金额</div>
|
|
|
<div class="payment-registration-row-content">{{ totalMoney() }} (元)</div>
|
|
|
</div>
|
|
|
<div class="payment-registration-row">
|
|
|
<div class="payment-registration-row-title">支付占比</div>
|
|
|
<div class="payment-registration-row-content">{{ percentPay() }}%</div>
|
|
|
</div>
|
|
|
<div class="payment-registration-row">
|
|
|
<div class="payment-registration-row-title">已付笔数</div>
|
|
|
<div class="payment-registration-row-content">{{ actNumsTotal() }}</div>
|
|
|
<div class="payment-registration-row-content" style="color: #ff0000;padding-left: 16px;cursor: pointer;">
|
|
|
<Poptip :transfer="true">
|
|
|
<div>点击查看列表</div>
|
|
|
<template v-slot:content>
|
|
|
<template v-if="payment&&payment.length>0">
|
|
|
<xy-table :height="200" :list="payment" :table-item="payTable">
|
|
|
<template v-slot:btns>
|
|
|
<p />
|
|
|
</template>
|
|
|
</xy-table>
|
|
|
</template>
|
|
|
<template v-else>
|
|
|
<div style="text-align: center">暂无已付笔数</div>
|
|
|
</template>
|
|
|
</template>
|
|
|
</Poptip>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
<template v-slot:act_date>
|
|
|
<div v-if="contract_category && contract_category.show_apply_money === 1 && (!hasPostPaymentForm || (hasPostPaymentForm && currentStep === 2))" class="xy-table-item">
|
|
|
<div class="xy-table-item-label">
|
|
|
<span v-if="contract_category && contract_category.required_act_date === 1" style="color: red;font-weight: 600;padding-right: 4px;">*</span>
|
|
|
实付日期
|
|
|
</div>
|
|
|
<div class="xy-table-item-content">
|
|
|
<el-date-picker v-model="paymentRegistrationForm.act_date" type="date" value-format="yyyy-MM-dd" style="width: 150px;" />
|
|
|
</div>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
<template v-slot:applyMoney>
|
|
|
<div v-if="contract_category && contract_category.show_apply_money === 1 && (!hasPostPaymentForm || (hasPostPaymentForm && currentStep === 2))" class="xy-table-item">
|
|
|
<div class="xy-table-item-label">
|
|
|
<span v-if="contract_category && contract_category.required_apply_money === 1" style="color: red;font-weight: 600;padding-right: 4px;">*</span>申请付款金额
|
|
|
</div>
|
|
|
<div class="xy-table-item-content xy-table-item-price">
|
|
|
<el-input
|
|
|
v-model="paymentRegistrationForm.applyMoney"
|
|
|
clearable
|
|
|
placeholder="请填写付款金额"
|
|
|
style="width: 150px;"
|
|
|
@input="checkIsEnd"
|
|
|
/>
|
|
|
</div>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
<template v-slot:audit_money>
|
|
|
<div v-if="contract_category && contract_category.show_audit_money === 1 && (!hasPostPaymentForm || (hasPostPaymentForm && currentStep === 2))" class="xy-table-item">
|
|
|
<div class="xy-table-item-label">
|
|
|
<span v-if="contract_category && contract_category.required_audit_money === 1" style="color: red;font-weight: 600;padding-right: 4px;">*</span>审计金额
|
|
|
</div>
|
|
|
<div class="xy-table-item-content xy-table-item-price">
|
|
|
<el-input v-model="paymentRegistrationForm.audit_money" clearable placeholder="请填写审计金额" style="width: 150px;" />
|
|
|
</div>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
<template v-slot:deductionMoney>
|
|
|
<div v-if="contract_category && contract_category.show_discount_money === 1 && (!hasPostPaymentForm || (hasPostPaymentForm && currentStep === 2))" class="xy-table-item">
|
|
|
<div class="xy-table-item-label">
|
|
|
<span v-if="contract_category && contract_category.required_discount_money === 1" style="color: red;font-weight: 600;padding-right: 4px;">*</span>本期扣款金额
|
|
|
</div>
|
|
|
<div class="xy-table-item-content xy-table-item-price">
|
|
|
<el-input
|
|
|
v-model="paymentRegistrationForm.deductionMoney"
|
|
|
clearable
|
|
|
placeholder="请填写扣款金额"
|
|
|
style="width: 150px;"
|
|
|
/>
|
|
|
</div>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
<template v-slot:remark>
|
|
|
<div v-if="contract_category && contract_category.show_remark === 1 && (!hasPostPaymentForm || (hasPostPaymentForm && currentStep === 2))" class="xy-table-item">
|
|
|
<div class="xy-table-item-label">
|
|
|
<span v-if="contract_category && contract_category.required_remark === 1" style="color: red;font-weight: 600;padding-right: 4px;">*</span>备注
|
|
|
</div>
|
|
|
<div class="xy-table-item-content">
|
|
|
<el-input
|
|
|
v-model="paymentRegistrationForm.remark"
|
|
|
type="textarea"
|
|
|
clearable
|
|
|
placeholder="进度款日期:2022.6.8-2022.7.7"
|
|
|
style="width: 300px;"
|
|
|
/>
|
|
|
</div>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
<template v-slot:type>
|
|
|
<div v-if="contract_category && contract_category.show_type === 1 && (!hasPostPaymentForm || (hasPostPaymentForm && currentStep === 2))" class="xy-table-item">
|
|
|
<div class="xy-table-item-label">
|
|
|
<span v-if="contract_category && contract_category.required_type === 1" style="color: red;font-weight: 600;padding-right: 4px;">*</span>款项类型
|
|
|
</div>
|
|
|
<div class="xy-table-item-content">
|
|
|
<el-select
|
|
|
v-model="paymentRegistrationForm.type"
|
|
|
placeholder="选择款项类型或直接录入其他类型"
|
|
|
style="width: 150px;"
|
|
|
filterable
|
|
|
allow-create
|
|
|
clearable
|
|
|
>
|
|
|
<el-option v-for="item in paymentType" :key="item" :label="item" :value="item" />
|
|
|
</el-select>
|
|
|
</div>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
<template v-slot:isLast>
|
|
|
<div v-if="contract_category && contract_category.show_is_end === 1 && (!hasPostPaymentForm || (hasPostPaymentForm && currentStep === 2))" class="xy-table-item">
|
|
|
<div class="xy-table-item-label" style="width: 200px">
|
|
|
<span v-if="contract_category && contract_category.required_is_end === 1" style="color: red;font-weight: 600;padding-right: 4px;">*</span>是否为最后一笔
|
|
|
</div>
|
|
|
<div class="xy-table-item-content">
|
|
|
<el-switch v-model="paymentRegistrationForm.isLast" />
|
|
|
</div>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
<template v-slot:end_time>
|
|
|
<div v-if="contract_category && contract_category.show_end_time === 1 && (!hasPostPaymentForm || (hasPostPaymentForm && currentStep === 2))" class="xy-table-item">
|
|
|
<div class="xy-table-item-label">项目完成时间</div>
|
|
|
<div class="xy-table-item-content">
|
|
|
<el-date-picker v-model="paymentRegistrationForm.end_time" type="date" value-format="yyyy-MM-dd HH:mm:ss" style="width: 150px;" />
|
|
|
</div>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
<template v-slot:isCheck>
|
|
|
<div v-if="contract_category && contract_category.show_check === 1 && (!hasPostPaymentForm || (hasPostPaymentForm && currentStep === 2))" class="xy-table-item">
|
|
|
<div class="xy-table-item-label" style="width: 200px">
|
|
|
<span style="color: red;font-weight: 600;padding-right: 4px;" />是否验收
|
|
|
</div>
|
|
|
<div class="xy-table-item-content">
|
|
|
<el-switch v-model="paymentRegistrationForm.isCheck" />
|
|
|
</div>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
<template v-slot:extraFormBottom>
|
|
|
<div v-if="hasPostPaymentForm && currentStep === 1" class="payment-table-section">
|
|
|
<div class="section-title">
|
|
|
事后支付表格
|
|
|
<el-button type="text" style="margin-left: 10px;" @click="openZoomedTable">
|
|
|
<i class="el-icon-zoom-in" /> 放大查看
|
|
|
</el-button>
|
|
|
</div>
|
|
|
<div ref="mainTable" class="payment-table" v-html="forms" />
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
<template v-slot:footerContent>
|
|
|
<div class="dialog-footer">
|
|
|
<el-button v-if="hasPostPaymentForm && currentStep === 2" @click="currentStep = 1">上一步</el-button>
|
|
|
<el-button v-if="hasPostPaymentForm && currentStep === 1" @click="nextStep">下一步</el-button>
|
|
|
<el-button v-if="(hasPostPaymentForm && currentStep === 2) || (!hasPostPaymentForm)" type="primary" @click="submit">确定</el-button>
|
|
|
<el-button @click="resetForm">重置</el-button>
|
|
|
</div>
|
|
|
</template>
|
|
|
</xy-dialog>
|
|
|
|
|
|
<!-- 放大窗口 -->
|
|
|
<el-dialog
|
|
|
title="事后支付表格"
|
|
|
:visible.sync="zoomedDialogVisible"
|
|
|
width="80%"
|
|
|
:append-to-body="false"
|
|
|
:destroy-on-close="true"
|
|
|
:modal-append-to-body="false"
|
|
|
:top="'5vh'"
|
|
|
custom-class="zoomed-table-dialog"
|
|
|
@close="handleZoomedDialogClose"
|
|
|
>
|
|
|
<div ref="zoomedTable" class="zoomed-table" v-html="forms" />
|
|
|
</el-dialog>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
<script>
|
|
|
import {
|
|
|
getparameter
|
|
|
} from '@/api/system/dictionary'
|
|
|
import {
|
|
|
getFundLog,
|
|
|
addFundLog
|
|
|
} from '@/api/paymentRegistration/fundLog'
|
|
|
import {
|
|
|
getBudget
|
|
|
} from '@/api/budget/budget'
|
|
|
import {
|
|
|
detailContract,
|
|
|
editorContract
|
|
|
} from '@/api/contract/contract'
|
|
|
import {
|
|
|
Message
|
|
|
} from 'element-ui'
|
|
|
import {
|
|
|
parseTime
|
|
|
} from '@/utils'
|
|
|
import { getContractTemplateContext } from '@/api/businessConfig/businessConfig'
|
|
|
|
|
|
// 添加金额转大写的工具函数
|
|
|
function numberToChinese(num) {
|
|
|
const units = ['', '拾', '佰', '仟', '万', '拾', '佰', '仟', '亿', '拾', '佰', '仟', '万']
|
|
|
const digits = ['零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖']
|
|
|
const [integer, decimal] = num.toString().split('.')
|
|
|
let result = ''
|
|
|
let intNum = parseInt(integer)
|
|
|
if (intNum === 0) {
|
|
|
result = '零'
|
|
|
} else {
|
|
|
let i = 0
|
|
|
let lastDigit = null
|
|
|
let hasZero = false
|
|
|
while (intNum > 0) {
|
|
|
const digit = intNum % 10
|
|
|
if (digit === 0) {
|
|
|
if (!hasZero && lastDigit !== 0) {
|
|
|
result = '零' + result
|
|
|
hasZero = true
|
|
|
}
|
|
|
} else {
|
|
|
let unit = units[i]
|
|
|
if (i === 4 && result.startsWith('零')) {
|
|
|
unit = '万'
|
|
|
} else if (i === 8 && result.startsWith('零')) {
|
|
|
unit = '亿'
|
|
|
}
|
|
|
result = digits[digit] + unit + result
|
|
|
hasZero = false
|
|
|
}
|
|
|
lastDigit = digit
|
|
|
intNum = Math.floor(intNum / 10)
|
|
|
i++
|
|
|
}
|
|
|
result = result.replace(/零+$/, '')
|
|
|
result = result.replace(/零+/, '零')
|
|
|
}
|
|
|
if (decimal) {
|
|
|
const decimalNum = parseInt(decimal)
|
|
|
if (decimalNum > 0) {
|
|
|
result += '点'
|
|
|
for (let i = 0; i < decimal.length; i++) {
|
|
|
result += digits[parseInt(decimal[i])]
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
if (!decimal || parseInt(decimal) === 0) {
|
|
|
result += '整'
|
|
|
}
|
|
|
return result + '元'
|
|
|
}
|
|
|
|
|
|
// 添加同步表单DOM到HTML的函数
|
|
|
function syncFormDomToHtml(dom, contractTemplateFields) {
|
|
|
if (!dom) return ''
|
|
|
const inputs = dom.querySelectorAll('input, select, textarea')
|
|
|
inputs.forEach(input => {
|
|
|
const fieldName = input.getAttribute('data-field')
|
|
|
if (fieldName && contractTemplateFields) {
|
|
|
const field = contractTemplateFields.find(f => f.field === fieldName)
|
|
|
if (field) {
|
|
|
if (input.type === 'checkbox' || input.type === 'radio') {
|
|
|
const checkedInput = dom.querySelector(`[data-field="${fieldName}"]:checked`)
|
|
|
field.value = checkedInput ? checkedInput.value : ''
|
|
|
if (checkedInput) {
|
|
|
checkedInput.setAttribute('checked', 'checked')
|
|
|
}
|
|
|
} else {
|
|
|
field.value = input.value
|
|
|
input.setAttribute('value', input.value)
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
})
|
|
|
return dom.innerHTML
|
|
|
}
|
|
|
|
|
|
export default {
|
|
|
data() {
|
|
|
return {
|
|
|
currentStep: 1, // 当前步骤
|
|
|
searchContent: '',
|
|
|
planTotal: 0,
|
|
|
pageIndex: 1,
|
|
|
// 付款登记
|
|
|
plans: [],
|
|
|
planTypes: [],
|
|
|
contract: {},
|
|
|
payment: [], // 合同关联的付款登记
|
|
|
contractTemplate: null, // 合同模板HTML
|
|
|
forms: null, // 实际表单HTML
|
|
|
payTable: [{
|
|
|
label: '申请金额',
|
|
|
prop: 'apply_money',
|
|
|
sortable: false,
|
|
|
width: 160,
|
|
|
align: 'right'
|
|
|
},
|
|
|
{
|
|
|
label: '已付金额',
|
|
|
prop: 'act_money',
|
|
|
sortable: false,
|
|
|
width: 160,
|
|
|
align: 'right'
|
|
|
},
|
|
|
{
|
|
|
label: '时间',
|
|
|
prop: 'created_at',
|
|
|
sortable: false,
|
|
|
width: 120,
|
|
|
formatter: (t1, t2, value) => {
|
|
|
return parseTime(new Date(value), '{y}-{m}-{d}')
|
|
|
}
|
|
|
}
|
|
|
],
|
|
|
paymentType: ['预付款', '进度款', '结算款', '质保金'],
|
|
|
isShowPaymentRegistration: false,
|
|
|
paymentRegistrationForm: {
|
|
|
applyMoney: '',
|
|
|
deductionMoney: '',
|
|
|
audit_money: '',
|
|
|
act_date: '',
|
|
|
type: '',
|
|
|
isLast: false,
|
|
|
end_time: '',
|
|
|
isCheck: false,
|
|
|
plan: [],
|
|
|
remark: ''
|
|
|
},
|
|
|
form: {
|
|
|
audit_money: 0
|
|
|
},
|
|
|
paymentRegistrationRules: {
|
|
|
applyMoney: [
|
|
|
{
|
|
|
required: false,
|
|
|
message: '必填'
|
|
|
},
|
|
|
{
|
|
|
pattern: /(^-?(?:\d+|\d{1,3}(?:,\d{3})+)(?:\.\d{1,2})?$)/,
|
|
|
message: '必须为数字'
|
|
|
}
|
|
|
],
|
|
|
deductionMoney: [
|
|
|
{
|
|
|
required: false,
|
|
|
message: '必填'
|
|
|
},
|
|
|
{
|
|
|
pattern: /(^-?(?:\d+|\d{1,3}(?:,\d{3})+)(?:\.\d{1,2})?$)/,
|
|
|
message: '必须为数字'
|
|
|
}
|
|
|
],
|
|
|
audit_money: [
|
|
|
{
|
|
|
required: false,
|
|
|
message: '必填'
|
|
|
}
|
|
|
],
|
|
|
type: [
|
|
|
{
|
|
|
required: false,
|
|
|
message: '必选'
|
|
|
}
|
|
|
],
|
|
|
isLast: [
|
|
|
{
|
|
|
required: false,
|
|
|
message: '必选'
|
|
|
}
|
|
|
],
|
|
|
remark: [
|
|
|
{
|
|
|
required: false,
|
|
|
message: '必填'
|
|
|
}
|
|
|
],
|
|
|
act_date: [
|
|
|
{
|
|
|
required: false,
|
|
|
message: '必填'
|
|
|
}
|
|
|
]
|
|
|
},
|
|
|
planTable: [{
|
|
|
sortable: false,
|
|
|
width: 36,
|
|
|
type: 'selection'
|
|
|
},
|
|
|
{
|
|
|
label: '分类',
|
|
|
prop: 'type',
|
|
|
formatter: (cell, data, value) => {
|
|
|
const res = this.planTypes.filter(item => {
|
|
|
return item.id === value
|
|
|
})
|
|
|
return res[0]?.value || '未知'
|
|
|
}
|
|
|
},
|
|
|
{
|
|
|
label: '名称',
|
|
|
prop: 'name',
|
|
|
align: 'left'
|
|
|
},
|
|
|
{
|
|
|
label: '计划金额',
|
|
|
prop: 'money',
|
|
|
align: 'right'
|
|
|
}
|
|
|
],
|
|
|
zoomedDialogVisible: false, // 控制放大表格弹窗的显示
|
|
|
hasPostPaymentForm: false, // 是否有事后支付表格
|
|
|
contract_category: {},
|
|
|
templateContextData: null // 合同模板关联数据
|
|
|
}
|
|
|
},
|
|
|
computed: {
|
|
|
priceFormat() {
|
|
|
return function(price) {
|
|
|
return Number(price).toFixed(2).replace(/(\d)(?=(\d{3})+\.)/g, '$1,')
|
|
|
}
|
|
|
}
|
|
|
},
|
|
|
watch: {
|
|
|
isShowPaymentRegistration(newVal) {
|
|
|
if (newVal) {
|
|
|
this.getBudgets()
|
|
|
this.currentStep = 1 // 重置到第一步
|
|
|
this.paymentRegistrationForm = this.getDefaultPaymentRegistrationForm() // 每次弹窗打开时重置表单
|
|
|
// 重置校验状态,避免立刻弹出必填项提示
|
|
|
this.$nextTick(() => {
|
|
|
if (this.$refs.paymentRegistration && this.$refs.paymentRegistration.$refs.elForm) {
|
|
|
this.$refs.paymentRegistration.$refs.elForm.resetFields()
|
|
|
}
|
|
|
})
|
|
|
}
|
|
|
},
|
|
|
forms: {
|
|
|
handler(newVal) {
|
|
|
if (newVal) {
|
|
|
// 当forms内容更新后,重新设置监听器
|
|
|
this.$nextTick(() => {
|
|
|
this.setupAmountListeners(this.$refs.mainTable)
|
|
|
})
|
|
|
}
|
|
|
},
|
|
|
immediate: true
|
|
|
}
|
|
|
},
|
|
|
mounted() {
|
|
|
this.getPlanTypes()
|
|
|
},
|
|
|
methods: {
|
|
|
getDefaultPaymentRegistrationForm() {
|
|
|
return {
|
|
|
applyMoney: '',
|
|
|
deductionMoney: '',
|
|
|
audit_money: '',
|
|
|
act_date: '',
|
|
|
type: '',
|
|
|
isLast: false,
|
|
|
end_time: '',
|
|
|
isCheck: false,
|
|
|
plan: [],
|
|
|
remark: ''
|
|
|
}
|
|
|
},
|
|
|
checkIsEnd(e) {
|
|
|
// this.paymentRegistrationForm.isLast = (Number(this.totalMoney()) + Number(e)) >= (this.contract.money * 0.95);
|
|
|
},
|
|
|
|
|
|
async getPlanTypes() {
|
|
|
const res = await getparameter({
|
|
|
number: 'money_way'
|
|
|
})
|
|
|
this.planTypes = res.detail
|
|
|
},
|
|
|
|
|
|
// 翻页
|
|
|
pageChange(e) {
|
|
|
this.pageIndex = e
|
|
|
this.getBudgets()
|
|
|
},
|
|
|
|
|
|
// 合计申请金额
|
|
|
totalApplyMoney() {
|
|
|
let total = 0.00
|
|
|
this.payment.map(item => {
|
|
|
total += Number(item.apply_money)
|
|
|
})
|
|
|
return total.toFixed(2)
|
|
|
},
|
|
|
// 合计金额
|
|
|
totalMoney() {
|
|
|
let total = 0.00
|
|
|
this.payment.map(item => {
|
|
|
total += Number(item.act_money)
|
|
|
})
|
|
|
return total.toFixed(2)
|
|
|
},
|
|
|
// 已付笔数
|
|
|
actNumsTotal() {
|
|
|
let total = 0
|
|
|
this.payment.map(item => {
|
|
|
if (Number(item.act_money)) {
|
|
|
total++
|
|
|
}
|
|
|
})
|
|
|
return total
|
|
|
},
|
|
|
// 支付占比
|
|
|
percentPay() {
|
|
|
const total = this.totalMoney()
|
|
|
return ((total / this.contract.money) * 100).toFixed(2) || 0
|
|
|
},
|
|
|
|
|
|
// 获取合同信息
|
|
|
async getContract(info) {
|
|
|
this.contract = await detailContract({
|
|
|
id: info.id
|
|
|
})
|
|
|
this.paymentRegistrationForm.plan = this.contract.plans.map(item => {
|
|
|
return {
|
|
|
plan_id: item.id,
|
|
|
use_money: item.useMoney,
|
|
|
new_money: item.money
|
|
|
}
|
|
|
})
|
|
|
this.form.audit_money = this.contract.audit_money
|
|
|
this.contract_category = this.contract.contract_category || {}
|
|
|
// 设置默认值
|
|
|
if (this.contract_category && this.contract_category.default_apply_money !== undefined) {
|
|
|
this.paymentRegistrationForm.applyMoney = this.contract_category.default_apply_money
|
|
|
}
|
|
|
if (this.contract_category && this.contract_category.default_discount_money !== undefined) {
|
|
|
this.paymentRegistrationForm.deductionMoney = this.contract_category.default_discount_money
|
|
|
}
|
|
|
if (this.contract_category && this.contract_category.default_audit_money !== undefined) {
|
|
|
this.paymentRegistrationForm.audit_money = this.contract_category.default_audit_money
|
|
|
}
|
|
|
if (this.contract_category && this.contract_category.default_act_date !== undefined) {
|
|
|
this.paymentRegistrationForm.act_date = this.contract_category.default_act_date
|
|
|
}
|
|
|
if (this.contract_category && this.contract_category.default_type !== undefined) {
|
|
|
this.paymentRegistrationForm.type = this.contract_category.default_type
|
|
|
}
|
|
|
if (this.contract_category && this.contract_category.default_is_end !== undefined) {
|
|
|
this.paymentRegistrationForm.isLast = this.contract_category.default_is_end === 1 || this.contract_category.default_is_end === '1'
|
|
|
}
|
|
|
if (this.contract_category && this.contract_category.default_remark !== undefined) {
|
|
|
this.paymentRegistrationForm.remark = this.contract_category.default_remark
|
|
|
}
|
|
|
// 动态生成rules
|
|
|
this.paymentRegistrationRules = {
|
|
|
applyMoney: [
|
|
|
{
|
|
|
required: this.contract_category.required_apply_money === 1,
|
|
|
message: '必填'
|
|
|
},
|
|
|
{
|
|
|
pattern: /(^-?(?:\d+|\d{1,3}(?:,\d{3})+)(?:\.\d{1,2})?$)/,
|
|
|
message: '必须为数字'
|
|
|
}
|
|
|
],
|
|
|
deductionMoney: [
|
|
|
{
|
|
|
required: this.contract_category.required_discount_money === 1,
|
|
|
message: '必填'
|
|
|
},
|
|
|
{
|
|
|
pattern: /(^-?(?:\d+|\d{1,3}(?:,\d{3})+)(?:\.\d{1,2})?$)/,
|
|
|
message: '必须为数字'
|
|
|
}
|
|
|
],
|
|
|
audit_money: [
|
|
|
{
|
|
|
required: this.contract_category.required_audit_money === 1,
|
|
|
message: '必填'
|
|
|
}
|
|
|
],
|
|
|
type: [
|
|
|
{
|
|
|
required: this.contract_category.required_type === 1,
|
|
|
message: '必选'
|
|
|
}
|
|
|
],
|
|
|
isLast: [
|
|
|
{
|
|
|
required: this.contract_category.required_is_end === 1,
|
|
|
message: '必选'
|
|
|
}
|
|
|
],
|
|
|
remark: [
|
|
|
{
|
|
|
required: this.contract_category.required_remark === 1,
|
|
|
message: '必填'
|
|
|
}
|
|
|
],
|
|
|
act_date: [
|
|
|
{
|
|
|
required: this.contract_category.required_act_date === 1,
|
|
|
message: '必填'
|
|
|
}
|
|
|
]
|
|
|
}
|
|
|
const res = await getFundLog({
|
|
|
contract_id: this.contract.id,
|
|
|
page: 1,
|
|
|
page_size: 999
|
|
|
})
|
|
|
this.payment = res.data
|
|
|
// 判断是否有事后支付表格模板
|
|
|
if (this.contract.contract_template && this.contract.contract_template.template) {
|
|
|
this.hasPostPaymentForm = true
|
|
|
this.currentStep = 1
|
|
|
this.contractTemplate = this.contract.contract_template.template
|
|
|
this.forms = this.contract.forms
|
|
|
if (!this.contract.contract_template.contract_template_fields) {
|
|
|
this.contract.contract_template.contract_template_fields = this.contract.other_data || []
|
|
|
}
|
|
|
} else {
|
|
|
this.hasPostPaymentForm = false
|
|
|
this.currentStep = 1
|
|
|
this.contractTemplate = null
|
|
|
this.forms = null
|
|
|
}
|
|
|
|
|
|
// 拉取合同模板关联数据
|
|
|
if (this.contract && this.contract.id) {
|
|
|
getContractTemplateContext({
|
|
|
id: this.contract.id,
|
|
|
model: 'Contract'
|
|
|
}).then(res => {
|
|
|
this.templateContextData = res
|
|
|
// 初始化模板字段值
|
|
|
this.initTemplateFields(res)
|
|
|
})
|
|
|
}
|
|
|
|
|
|
// 设置 amount 输入框的监听
|
|
|
this.$nextTick(() => {
|
|
|
// 为原始表格设置监听器
|
|
|
this.setupAmountListeners(this.$refs.mainTable)
|
|
|
// 为放大表格设置监听器
|
|
|
this.setupAmountListeners(this.$refs.zoomedTable)
|
|
|
})
|
|
|
},
|
|
|
|
|
|
// 初始化模板字段值
|
|
|
initTemplateFields(contextData) {
|
|
|
if (!contextData || !contextData.other_data_fill) return
|
|
|
|
|
|
// 获取事后支付模板
|
|
|
if (this.contract.contract_template && this.contract.contract_template.contract_template_fields) {
|
|
|
const fields = this.contract.contract_template.contract_template_fields
|
|
|
|
|
|
// 遍历模板字段
|
|
|
fields.forEach(field => {
|
|
|
// 检查字段是否有link_type且该字段在other_data_fill中存在
|
|
|
if (field.link_field && contextData.other_data_fill[field.link_field]) {
|
|
|
let value = contextData.other_data_fill[field.link_field]
|
|
|
|
|
|
// 如果是upper_money字段,转换为金额大写
|
|
|
if (field.link_field === 'upper_money') {
|
|
|
value = numberToChinese(Number(value))
|
|
|
}
|
|
|
|
|
|
// 设置字段值
|
|
|
field.value = value
|
|
|
|
|
|
// 更新DOM中的值
|
|
|
const dom = this.$refs.mainTable
|
|
|
if (dom) {
|
|
|
const input = dom.querySelector(`[data-field="${field.field}"]`)
|
|
|
if (input) {
|
|
|
if (input.type === 'checkbox' || input.type === 'radio') {
|
|
|
// 对于复选框和单选框,需要设置checked属性
|
|
|
if (field.value === input.value) {
|
|
|
input.checked = true
|
|
|
}
|
|
|
} else {
|
|
|
// 对于其他类型的输入,直接设置value
|
|
|
input.value = field.value
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
})
|
|
|
|
|
|
// 更新forms内容
|
|
|
if (this.$refs.mainTable) {
|
|
|
this.forms = syncFormDomToHtml(this.$refs.mainTable, fields)
|
|
|
}
|
|
|
}
|
|
|
},
|
|
|
|
|
|
submit() {
|
|
|
// 先进行表单校验
|
|
|
this.$refs['paymentRegistration'].$refs['elForm'].validate().then(res => {
|
|
|
if (res) {
|
|
|
// 保存事后支付表格的数据
|
|
|
if (this.contract.contract_template) {
|
|
|
const dom = this.$refs.zoomedTable
|
|
|
if (dom) {
|
|
|
// 获取所有输入控件
|
|
|
const inputs = dom.querySelectorAll('input, select, textarea')
|
|
|
|
|
|
// 遍历所有输入控件,更新值到 HTML
|
|
|
inputs.forEach(input => {
|
|
|
const fieldName = input.getAttribute('data-field')
|
|
|
if (fieldName) {
|
|
|
const field = this.contract.contract_template.contract_template_fields.find(f => f.field === fieldName)
|
|
|
if (field) {
|
|
|
if (input.type === 'checkbox' || input.type === 'radio') {
|
|
|
// 对于复选框和单选框,需要找到选中的值
|
|
|
const checkedInput = dom.querySelector(`[data-field="${fieldName}"]:checked`)
|
|
|
field.value = checkedInput ? checkedInput.value : ''
|
|
|
// 更新 HTML 中的 checked 状态
|
|
|
if (checkedInput) {
|
|
|
checkedInput.setAttribute('checked', 'checked')
|
|
|
}
|
|
|
} else {
|
|
|
field.value = input.value
|
|
|
// 更新 HTML 中的 value
|
|
|
input.setAttribute('value', input.value)
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
})
|
|
|
|
|
|
// 获取更新后的 HTML
|
|
|
this.forms = dom.innerHTML
|
|
|
}
|
|
|
}
|
|
|
|
|
|
const data = {
|
|
|
contract_id: this.contract.id,
|
|
|
apply_money: this.paymentRegistrationForm.applyMoney ? this.paymentRegistrationForm.applyMoney : 0,
|
|
|
discount_money: this.paymentRegistrationForm.deductionMoney ? this.paymentRegistrationForm.deductionMoney : 0,
|
|
|
type: this.paymentRegistrationForm.type ? this.paymentRegistrationForm.type : 0,
|
|
|
is_end: this.paymentRegistrationForm.isLast ? 1 : 0,
|
|
|
remark: this.paymentRegistrationForm.remark,
|
|
|
audit_money: this.paymentRegistrationForm.audit_money ? this.paymentRegistrationForm.audit_money : 0,
|
|
|
end_time: this.paymentRegistrationForm.end_time,
|
|
|
is_check: this.paymentRegistrationForm.isCheck ? 1 : 0,
|
|
|
// 提交更新后的HTML和字段数据
|
|
|
forms: this.forms,
|
|
|
other_data: this.contract.contract_template?.contract_template_fields || []
|
|
|
}
|
|
|
addFundLog(data).then(() => {
|
|
|
this.isShowPaymentRegistration = false
|
|
|
// 付款申请后同时更新一下合同中审计金额
|
|
|
editorContract({
|
|
|
id: this.contract.id,
|
|
|
audit_money: this.form.audit_money,
|
|
|
forms: this.forms,
|
|
|
other_data: this.contract.contract_template?.contract_template_fields || []
|
|
|
}).then(() => {
|
|
|
Message({
|
|
|
type: 'success',
|
|
|
message: '操作成功'
|
|
|
})
|
|
|
}).catch(error => {
|
|
|
console.error('更新合同失败:', error)
|
|
|
Message({
|
|
|
type: 'error',
|
|
|
message: '更新合同失败'
|
|
|
})
|
|
|
})
|
|
|
this.$refs['paymentRegistration'].reset()
|
|
|
}).catch(error => {
|
|
|
console.error('添加付款记录失败:', error)
|
|
|
Message({
|
|
|
type: 'error',
|
|
|
message: '添加付款记录失败'
|
|
|
})
|
|
|
})
|
|
|
} else {
|
|
|
this.$Message.warning({
|
|
|
content: '请填写完整信息',
|
|
|
duration: 1
|
|
|
})
|
|
|
}
|
|
|
}).catch(error => {
|
|
|
console.error('表单验证失败:', error)
|
|
|
this.$Message.warning({
|
|
|
content: '请填写完整信息',
|
|
|
duration: 1
|
|
|
})
|
|
|
})
|
|
|
},
|
|
|
|
|
|
// 计划
|
|
|
// 获取预算计划
|
|
|
async getBudgets() {
|
|
|
try {
|
|
|
const res = await getBudget({
|
|
|
name: this.searchContent,
|
|
|
page_size: 10,
|
|
|
page: this.pageIndex
|
|
|
})
|
|
|
this.plans = res.list.data
|
|
|
this.planTotal = res.list.total
|
|
|
} catch (error) {
|
|
|
console.error('获取预算计划失败:', error)
|
|
|
this.$Message.error({
|
|
|
content: '获取预算计划失败',
|
|
|
duration: 1
|
|
|
})
|
|
|
}
|
|
|
},
|
|
|
planPageChange(e) {
|
|
|
this.plansPageIndex = e
|
|
|
this.getBudgets()
|
|
|
},
|
|
|
selectPlan(sel, row) {
|
|
|
if (sel) {
|
|
|
this.paymentRegistrationForm.plan = sel.map(item => {
|
|
|
return {
|
|
|
plan_id: item.id,
|
|
|
use_money: item.useMoney,
|
|
|
new_money: item.money
|
|
|
}
|
|
|
})
|
|
|
} else {
|
|
|
this.paymentRegistrationForm.plan = []
|
|
|
}
|
|
|
},
|
|
|
toggleSelection(e) {
|
|
|
if (!e) {
|
|
|
return
|
|
|
}
|
|
|
try {
|
|
|
const plans = this.paymentRegistrationForm.plan.map(item => {
|
|
|
return item.plan_id
|
|
|
})
|
|
|
if (plans) {
|
|
|
this.plans.filter(plan => {
|
|
|
return plans.includes(plan.id)
|
|
|
}).map(row => {
|
|
|
this.$nextTick(() => {
|
|
|
this.$refs['planTable'].toggleRowSelection(row)
|
|
|
})
|
|
|
})
|
|
|
} else {
|
|
|
this.$refs['planTable'].clearSelection()
|
|
|
}
|
|
|
} catch (error) {
|
|
|
console.error('切换选择状态失败:', error)
|
|
|
this.$Message.error({
|
|
|
content: '切换选择状态失败',
|
|
|
duration: 1
|
|
|
})
|
|
|
}
|
|
|
},
|
|
|
nextStep() {
|
|
|
try {
|
|
|
// 如果有事后支付表格,下一步不做表单校验,直接切换 currentStep=2
|
|
|
if (this.hasPostPaymentForm) {
|
|
|
// 保存事后支付表格HTML数据,参考 openZoomedTable 逻辑
|
|
|
const dom = this.$refs.mainTable
|
|
|
if (dom && this.contract.contract_template) {
|
|
|
this.forms = syncFormDomToHtml(dom, this.contract.contract_template.contract_template_fields)
|
|
|
this.contract.forms = this.forms
|
|
|
}
|
|
|
this.currentStep = 2
|
|
|
} else {
|
|
|
// 没有事后支付表格时,下一步其实就是提交,校验在 submit 里
|
|
|
if (this.$refs['paymentRegistration'] && this.$refs['paymentRegistration'].$refs['elForm']) {
|
|
|
this.$refs['paymentRegistration'].$refs['elForm'].validate().then(res => {
|
|
|
if (res) {
|
|
|
this.submit()
|
|
|
}
|
|
|
}).catch(err => {
|
|
|
console.error('表单验证失败:', err)
|
|
|
this.$Message.warning({
|
|
|
content: '请填写完整信息',
|
|
|
duration: 1
|
|
|
})
|
|
|
})
|
|
|
} else {
|
|
|
this.submit()
|
|
|
}
|
|
|
}
|
|
|
} catch (error) {
|
|
|
console.error('下一步操作失败:', error)
|
|
|
this.$Message.error({
|
|
|
content: '下一步操作失败',
|
|
|
duration: 1
|
|
|
})
|
|
|
}
|
|
|
},
|
|
|
|
|
|
resetForm() {
|
|
|
this.currentStep = 1
|
|
|
this.$refs['paymentRegistration'].reset()
|
|
|
},
|
|
|
|
|
|
// 打开放大预览前,先同步当前表单数据
|
|
|
openZoomedTable() {
|
|
|
const dom = this.$refs.mainTable
|
|
|
if (dom && this.contract.contract_template) {
|
|
|
console.log(this.contract.contract_template.contract_template_fields)
|
|
|
this.forms = syncFormDomToHtml(dom, this.contract.contract_template.contract_template_fields)
|
|
|
this.contract.forms = this.forms
|
|
|
}
|
|
|
this.zoomedDialogVisible = true
|
|
|
// 设置 amount 输入框的监听
|
|
|
this.$nextTick(() => {
|
|
|
// 使用setTimeout确保DOM完全加载
|
|
|
setTimeout(() => {
|
|
|
this.setupAmountListeners(this.$refs.zoomedTable)
|
|
|
}, 200)
|
|
|
})
|
|
|
},
|
|
|
|
|
|
handleZoomedDialogClose() {
|
|
|
const dom = this.$refs.zoomedTable
|
|
|
if (dom && this.contract.contract_template) {
|
|
|
this.forms = syncFormDomToHtml(dom, this.contract.contract_template.contract_template_fields)
|
|
|
this.contract.forms = this.forms
|
|
|
}
|
|
|
this.zoomedDialogVisible = false
|
|
|
},
|
|
|
|
|
|
setupAmountListeners(dom) {
|
|
|
if (!dom) return
|
|
|
|
|
|
// 使用setTimeout确保DOM完全加载
|
|
|
setTimeout(() => {
|
|
|
// 获取所有以 amount 开头的输入框
|
|
|
const amountInputs = dom.querySelectorAll('input[data-field^="amount"]')
|
|
|
console.log('找到的金额输入框:', amountInputs.length, dom)
|
|
|
|
|
|
// 移除旧的监听器
|
|
|
amountInputs.forEach(input => {
|
|
|
input.removeEventListener('input', this.calculateTotal)
|
|
|
input.removeEventListener('change', this.calculateTotal)
|
|
|
input.removeEventListener('blur', this.calculateTotal)
|
|
|
})
|
|
|
|
|
|
// 添加新的监听器
|
|
|
amountInputs.forEach(input => {
|
|
|
input.addEventListener('input', this.calculateTotal)
|
|
|
input.addEventListener('change', this.calculateTotal)
|
|
|
input.addEventListener('blur', this.calculateTotal)
|
|
|
})
|
|
|
|
|
|
// 初始计算一次
|
|
|
this.calculateTotal()
|
|
|
}, 100)
|
|
|
},
|
|
|
|
|
|
calculateTotal() {
|
|
|
// 计算原始表格的总金额
|
|
|
this.calculateTableTotal(this.$refs.mainTable)
|
|
|
// 计算放大表格的总金额
|
|
|
this.calculateTableTotal(this.$refs.zoomedTable)
|
|
|
},
|
|
|
|
|
|
calculateTableTotal(dom) {
|
|
|
if (!dom) return
|
|
|
|
|
|
let total = 0
|
|
|
// 只计算以 amount 开头的输入框
|
|
|
const amountInputs = dom.querySelectorAll('input[data-field^="amount"]')
|
|
|
console.log('计算总金额,找到输入框数量:', amountInputs.length, dom)
|
|
|
|
|
|
amountInputs.forEach(input => {
|
|
|
const value = parseFloat(input.value) || 0
|
|
|
console.log('输入框值:', input.getAttribute('data-field'), value)
|
|
|
total += value
|
|
|
})
|
|
|
|
|
|
console.log('计算得到的总金额:', total)
|
|
|
|
|
|
// 更新总金额输入框
|
|
|
const totalInput = dom.querySelector('input[data-field="total"]')
|
|
|
if (totalInput) {
|
|
|
if (total !== 0) {
|
|
|
totalInput.value = total.toFixed(2)
|
|
|
}
|
|
|
console.log('更新总金额输入框:', totalInput.value)
|
|
|
|
|
|
// 监听 total 输入框的变化
|
|
|
totalInput.removeEventListener('input', (e) => this.updateUpperCaseFromTotal(e, dom))
|
|
|
totalInput.removeEventListener('change', (e) => this.updateUpperCaseFromTotal(e, dom))
|
|
|
totalInput.removeEventListener('blur', (e) => this.updateUpperCaseFromTotal(e, dom))
|
|
|
|
|
|
totalInput.addEventListener('input', (e) => this.updateUpperCaseFromTotal(e, dom))
|
|
|
totalInput.addEventListener('change', (e) => this.updateUpperCaseFromTotal(e, dom))
|
|
|
totalInput.addEventListener('blur', (e) => this.updateUpperCaseFromTotal(e, dom))
|
|
|
}
|
|
|
|
|
|
// 更新大写金额
|
|
|
const upperCaseInput = dom.querySelector('input[data-field="upperCaseAmount"]')
|
|
|
if (upperCaseInput) {
|
|
|
if (total !== 0) {
|
|
|
upperCaseInput.value = numberToChinese(total)
|
|
|
}
|
|
|
console.log('更新大写金额:', upperCaseInput.value)
|
|
|
}
|
|
|
},
|
|
|
|
|
|
updateUpperCaseFromTotal(event, dom) {
|
|
|
if (!dom) return
|
|
|
|
|
|
const totalInput = dom.querySelector('input[data-field="total"]')
|
|
|
const upperCaseInput = dom.querySelector('input[data-field="upperCaseAmount"]')
|
|
|
|
|
|
if (totalInput && upperCaseInput) {
|
|
|
const total = parseFloat(totalInput.value) || 0
|
|
|
upperCaseInput.value = numberToChinese(total)
|
|
|
console.log('从总金额更新大写金额:', total, upperCaseInput.value)
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
</script>
|
|
|
|
|
|
<style scoped lang="scss">
|
|
|
.payment-registration {
|
|
|
&-row {
|
|
|
display: flex;
|
|
|
padding: 6px 0;
|
|
|
|
|
|
&-title {
|
|
|
padding: 0 10px;
|
|
|
}
|
|
|
|
|
|
&-content {}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
.payment-table-section {
|
|
|
margin-top: 20px;
|
|
|
padding: 0 20px;
|
|
|
|
|
|
.section-title {
|
|
|
font-size: 14px;
|
|
|
font-weight: bold;
|
|
|
margin-bottom: 10px;
|
|
|
color: #333;
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
}
|
|
|
|
|
|
.no-data {
|
|
|
text-align: center;
|
|
|
color: #909399;
|
|
|
padding: 20px 0;
|
|
|
}
|
|
|
|
|
|
::v-deep table {
|
|
|
width: 100%;
|
|
|
border-collapse: collapse;
|
|
|
margin-bottom: 20px;
|
|
|
|
|
|
th, td {
|
|
|
border: 1px solid #EBEEF5;
|
|
|
padding: 8px;
|
|
|
text-align: center;
|
|
|
}
|
|
|
|
|
|
th {
|
|
|
background-color: #F5F7FA;
|
|
|
color: #606266;
|
|
|
font-weight: 500;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
.xy-table-item-label {
|
|
|
width: 140px;
|
|
|
}
|
|
|
|
|
|
.xy-table-item-price {
|
|
|
position: relative;
|
|
|
|
|
|
&::after {
|
|
|
z-index: 1;
|
|
|
position: absolute;
|
|
|
right: 0;
|
|
|
top: 0;
|
|
|
content: '(元)'
|
|
|
}
|
|
|
|
|
|
::v-deep .el-input__clear {
|
|
|
position: relative;
|
|
|
right: 30px;
|
|
|
z-index: 2;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
::v-deep .zoomed-table-dialog {
|
|
|
position: fixed;
|
|
|
top: 5vh;
|
|
|
left: 50%;
|
|
|
transform: translateX(-50%);
|
|
|
margin: 0 !important;
|
|
|
max-height: 90vh;
|
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
|
z-index: 2000;
|
|
|
|
|
|
.el-dialog__header {
|
|
|
padding: 15px 20px;
|
|
|
margin: 0;
|
|
|
border-bottom: 1px solid #EBEEF5;
|
|
|
}
|
|
|
|
|
|
.el-dialog__body {
|
|
|
flex: 1;
|
|
|
padding: 20px;
|
|
|
overflow-y: auto;
|
|
|
margin: 0;
|
|
|
max-height: calc(90vh - 120px);
|
|
|
}
|
|
|
|
|
|
.el-dialog__footer {
|
|
|
padding: 15px 20px;
|
|
|
margin: 0;
|
|
|
border-top: 1px solid #EBEEF5;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
::v-deep .el-dialog__wrapper {
|
|
|
padding-top: 0 !important;
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
justify-content: center;
|
|
|
}
|
|
|
</style>
|
|
|
|