You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1190 lines
38 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<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>