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.

1015 lines
35 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 title="打印预览" :is-show.sync="isShow" :width="90" ok-text="" @on-ok="printHtml">
<template v-slot:normalContent>
<div style="position: relative; margin-bottom: 16px; min-height: 40px;">
<div style="width: fit-content; margin: 0 auto;">
<RadioGroup v-model="currentForm" type="button">
<!-- <Radio label="pre" :disabled="!getBeforeForms">事前审批表格</Radio> -->
<!-- <Radio label="finance" :disabled="!fundLog">财务审核表</Radio> -->
<Radio label="post" :disabled="!getForms">事后支付表格</Radio>
</RadioGroup>
</div>
<div style="position: absolute; right: 0; top: 0;">
<label>打印方向:</label>
<select v-model="printOrientation" style="margin-right: 16px;">
<option value="portrait">纵向</option>
<option value="landscape">横向</option>
</select>
</div>
</div>
<div class="white-container">
<div class="form-container">
<!-- Pre-payment Form -->
<div v-if="currentForm === 'pre'" class="payment-form">
<div v-if="getBeforeForms" v-html="getBeforeForms" />
<div v-else class="no-form-message">暂无事前审批表格内容</div>
</div>
<!-- Post-payment Form -->
<div v-else-if="currentForm === 'post'" ref="printtable" class="payment-form">
<!-- 财务审核表内容放到事后支付表头部 -->
<table class="finance-review-table no-print">
<tr>
<th colspan="2" class="finance-header-row">合同信息</th>
<th colspan="5" class="finance-header-row">付款信息</th>
</tr>
<tr>
<td class="sub-header">受款单位</td>
<td>{{ fundLog && fundLog.contract && fundLog.contract.supply || '-' }}</td>
<td class="sub-header">申请付款金额</td>
<td>{{ fundLog && fundLog.apply_money || '-' }}</td>
<td class="sub-header">实际支付金额</td>
<td>{{ fundLog && fundLog.act_money || '-' }}</td>
<td class="sub-header">款项类型</td>
</tr>
<tr>
<td class="sub-header">合同名称</td>
<td>{{ fundLog && fundLog.contract && fundLog.contract.name || '-' }}</td>
<td class="sub-header">审计金额</td>
<td>{{ fundLog && fundLog.audit_money || '-' }}</td>
<td class="sub-header">本期扣款金额</td>
<td>{{ fundLog && fundLog.discount_money || '-' }}</td>
<td>{{ fundLog && fundLog.type || '-' }}</td>
</tr>
<tr>
<td class="sub-header">合同金额</td>
<td>{{ fundLog && fundLog.contract && moneyFormat(fundLog.contract.money) || '-' }} (元)</td>
<td class="sub-header">是否为最后一笔</td>
<td>{{ fundLog && fundLog.is_end === 1 ? '是' : '否' }}</td>
<td class="sub-header">备注</td>
<td colspan="2">{{ fundLog && fundLog.remark || '-' }}</td>
</tr>
</table>
<div v-if="getForms" v-html="getForms" />
<div v-else class="no-form-message">暂无事后支付表格内容</div>
</div>
</div>
</div>
</template>
</xy-dialog>
</div>
</template>
<script>
import { detailFundLog } from '@/api/paymentRegistration/fundLog'
import html2canvas from 'html2canvas'
import * as printJS from 'print-js'
// 添加金额转大写的工具函数
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 + '元'
}
export default {
name: 'PrintPaymentForm',
data() {
return {
isShow: false,
currentForm: 'post',
fundLog: null,
printOrientation: 'portrait' // 默认横向
}
},
computed: {
getBeforeForms() {
return this.fundLog && this.fundLog.contract && this.fundLog.contract.before_forms
},
getForms() {
return this.fundLog && this.fundLog.contract && this.fundLog.contract.forms
}
},
watch: {
getForms: {
handler(newVal) {
if (newVal) {
this.$nextTick(() => {
this.setupAmountListeners()
})
}
},
immediate: true
},
isShow: {
handler(newVal) {
if (newVal) {
this.$nextTick(() => {
this.setupAmountListeners()
})
}
},
immediate: true
}
},
methods: {
async getDetailFundLog(id) {
try {
const res = await detailFundLog({ id })
this.fundLog = res
} catch (error) {
console.error('获取付款详情失败:', error)
this.$Message.error('获取付款详情失败')
}
},
setupAmountListeners() {
const dom = this.$refs.printtable
if (!dom) return
const sdateAmountInputs = dom.querySelectorAll('input[data-field^="sdate"]')
// 移除旧的监听器
sdateAmountInputs.forEach(input => {
input.removeEventListener('input', this.caculateRoadDay)
input.removeEventListener('change', this.caculateRoadDay)
input.removeEventListener('blur', this.caculateRoadDay)
})
// 添加新的监听器
sdateAmountInputs.forEach(input => {
input.addEventListener('input', this.caculateRoadDay)
input.addEventListener('change', this.caculateRoadDay)
input.addEventListener('blur', this.caculateRoadDay)
})
const edateAmountInputs = dom.querySelectorAll('input[data-field^="edate"]')
// 移除旧的监听器
edateAmountInputs.forEach(input => {
input.removeEventListener('input', this.caculateRoadDay)
input.removeEventListener('change', this.caculateRoadDay)
input.removeEventListener('blur', this.caculateRoadDay)
})
// 添加新的监听器
edateAmountInputs.forEach(input => {
input.addEventListener('input', this.caculateRoadDay)
input.addEventListener('change', this.caculateRoadDay)
input.addEventListener('blur', this.caculateRoadDay)
})
// 获取所有以 wan 开头的输入框
const wanAmountInputs = dom.querySelectorAll('input[data-field^="wan"]')
// 移除旧的监听器
wanAmountInputs.forEach(input => {
input.removeEventListener('input', this.calculateTotal)
input.removeEventListener('change', this.calculateTotal)
input.removeEventListener('blur', this.calculateTotal)
})
// 添加新的监听器
wanAmountInputs.forEach(input => {
input.addEventListener('input', this.calculateTotal)
input.addEventListener('change', this.calculateTotal)
input.addEventListener('blur', this.calculateTotal)
})
// 获取所有以 qian 开头的输入框
const qianAmountInputs = dom.querySelectorAll('input[data-field^="qian"]')
// 移除旧的监听器
qianAmountInputs.forEach(input => {
input.removeEventListener('input', this.calculateTotal)
input.removeEventListener('change', this.calculateTotal)
input.removeEventListener('blur', this.calculateTotal)
})
// 添加新的监听器
qianAmountInputs.forEach(input => {
input.addEventListener('input', this.calculateTotal)
input.addEventListener('change', this.calculateTotal)
input.addEventListener('blur', this.calculateTotal)
})
// 获取所有以 bai 开头的输入框
const baiAmountInputs = dom.querySelectorAll('input[data-field^="bai"]')
// 移除旧的监听器
baiAmountInputs.forEach(input => {
input.removeEventListener('input', this.calculateTotal)
input.removeEventListener('change', this.calculateTotal)
input.removeEventListener('blur', this.calculateTotal)
})
// 添加新的监听器
baiAmountInputs.forEach(input => {
input.addEventListener('input', this.calculateTotal)
input.addEventListener('change', this.calculateTotal)
input.addEventListener('blur', this.calculateTotal)
})
// 获取所有以 shi 开头的输入框
const shiAmountInputs = dom.querySelectorAll('input[data-field^="shi"]')
// 移除旧的监听器
shiAmountInputs.forEach(input => {
input.removeEventListener('input', this.calculateTotal)
input.removeEventListener('change', this.calculateTotal)
input.removeEventListener('blur', this.calculateTotal)
})
// 添加新的监听器
shiAmountInputs.forEach(input => {
input.addEventListener('input', this.calculateTotal)
input.addEventListener('change', this.calculateTotal)
input.addEventListener('blur', this.calculateTotal)
})
// 获取所有以 yuan 开头的输入框
const yuanAmountInputs = dom.querySelectorAll('input[data-field^="yuan"]')
// 移除旧的监听器
yuanAmountInputs.forEach(input => {
input.removeEventListener('input', this.calculateTotal)
input.removeEventListener('change', this.calculateTotal)
input.removeEventListener('blur', this.calculateTotal)
})
// 添加新的监听器
yuanAmountInputs.forEach(input => {
input.addEventListener('input', this.calculateTotal)
input.addEventListener('change', this.calculateTotal)
input.addEventListener('blur', this.calculateTotal)
})
// 获取所有以 jiao 开头的输入框
const jiaoAmountInputs = dom.querySelectorAll('input[data-field^="jiao"]')
// 移除旧的监听器
jiaoAmountInputs.forEach(input => {
input.removeEventListener('input', this.calculateTotal)
input.removeEventListener('change', this.calculateTotal)
input.removeEventListener('blur', this.calculateTotal)
})
// 添加新的监听器
jiaoAmountInputs.forEach(input => {
input.addEventListener('input', this.calculateTotal)
input.addEventListener('change', this.calculateTotal)
input.addEventListener('blur', this.calculateTotal)
})
// 获取所有以 fen 开头的输入框
const fenAmountInputs = dom.querySelectorAll('input[data-field^="fen"]')
// 移除旧的监听器
fenAmountInputs.forEach(input => {
input.removeEventListener('input', this.calculateTotal)
input.removeEventListener('change', this.calculateTotal)
input.removeEventListener('blur', this.calculateTotal)
})
// 添加新的监听器
fenAmountInputs.forEach(input => {
input.addEventListener('input', this.calculateTotal)
input.addEventListener('change', this.calculateTotal)
input.addEventListener('blur', this.calculateTotal)
})
// 获取所有以 amount 开头的输入框
const amountInputs = dom.querySelectorAll('input[data-field^="amount"]')
console.log('找到的金额输入框:', amountInputs.length)
// 移除旧的监听器
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)
})
// 监听 total 输入框的变化
const totalInput = dom.querySelector('input[data-field="total"]')
if (totalInput) {
totalInput.removeEventListener('input', this.updateUpperCaseFromTotal)
totalInput.removeEventListener('change', this.updateUpperCaseFromTotal)
totalInput.removeEventListener('blur', this.updateUpperCaseFromTotal)
totalInput.addEventListener('input', this.updateUpperCaseFromTotal)
totalInput.addEventListener('change', this.updateUpperCaseFromTotal)
totalInput.addEventListener('blur', this.updateUpperCaseFromTotal)
}
// 初始计算一次
this.calculateTotal()
},
caculateRoadDay() {
const sdateInput = this.$refs.printtable.querySelector('input[data-field^="sdate"]')
const edateInput = this.$refs.printtable.querySelector('input[data-field^="edate"]')
const sdate = sdateInput.value
const edate = edateInput.value
if (sdate && edate) {
const s = new Date(sdate)
const e = new Date(edate)
// 计算天数差
const roadDay = Math.floor((e - s) / (1000 * 60 * 60 * 24)) + 1
console.log('roadDay', sdate, edate, roadDay)
const roadDayInput = this.$refs.printtable.querySelector('input[data-field="roadDay"]')
if (roadDayInput) {
roadDayInput.value = roadDay
}
}
},
calculateTotal() {
const dom = this.$refs.printtable
if (!dom) return
let wanTotal = 0
const wanAmountInputs = dom.querySelectorAll('input[data-field^="wan"]')
wanAmountInputs.forEach(input => {
const value = parseFloat(input.value) || 0
wanTotal += value
})
const wanInput = dom.querySelector('input[data-field="wTotal"]')
if (wanInput) {
wanInput.value = wanTotal===0?'0':wanTotal
}
let qianTotal = 0
const qianAmountInputs = dom.querySelectorAll('input[data-field^="qian"]')
qianAmountInputs.forEach(input => {
const value = parseFloat(input.value) || 0
qianTotal += value
})
const qianInput = dom.querySelector('input[data-field="qTotal"]')
if (qianInput) {
qianInput.value = qianTotal===0?'0':qianTotal
}
let baiTotal = 0
const baiAmountInputs = dom.querySelectorAll('input[data-field^="bai"]')
baiAmountInputs.forEach(input => {
const value = parseFloat(input.value) || 0
baiTotal += value
})
const baiInput = dom.querySelector('input[data-field="bTotal"]')
if (baiInput) {
baiInput.value = baiTotal===0?'0':baiTotal
}
let shiTotal = 0
const shiAmountInputs = dom.querySelectorAll('input[data-field^="shi"]')
shiAmountInputs.forEach(input => {
const value = parseFloat(input.value) || 0
shiTotal += value
})
const shiInput = dom.querySelector('input[data-field="sTotal"]')
if (shiInput) {
shiInput.value = shiTotal===0?'0':shiTotal
}
let yuanTotal = 0
const yuanAmountInputs = dom.querySelectorAll('input[data-field^="yuan"]')
yuanAmountInputs.forEach(input => {
const value = parseFloat(input.value) || 0
yuanTotal += value
})
const yuanInput = dom.querySelector('input[data-field="yTotal"]')
if (yuanInput) {
yuanInput.value = yuanTotal===0?'0':yuanTotal
}
let jiaoTotal = 0
const jiaoAmountInputs = dom.querySelectorAll('input[data-field^="jiao"]')
jiaoAmountInputs.forEach(input => {
const value = parseFloat(input.value) || 0
jiaoTotal += value
})
const jiaoInput = dom.querySelector('input[data-field="jTotal"]')
if (jiaoInput) {
jiaoInput.value = jiaoTotal===0?'0':jiaoTotal
}
let fenTotal = 0
const fenAmountInputs = dom.querySelectorAll('input[data-field^="fen"]')
fenAmountInputs.forEach(input => {
const value = parseFloat(input.value) || 0
fenTotal += value
})
const fenInput = dom.querySelector('input[data-field="fTotal"]')
if (fenInput) {
fenInput.value = fenTotal===0?'0':fenTotal
}
// 统计各位后,进行进位处理
if (fenTotal >= 10) {
jiaoTotal += Math.floor(fenTotal / 10);
fenTotal = fenTotal % 10;
}
if (jiaoTotal >= 10) {
yuanTotal += Math.floor(jiaoTotal / 10);
jiaoTotal = jiaoTotal % 10;
}
if (yuanTotal >= 10) {
shiTotal += Math.floor(yuanTotal / 10);
yuanTotal = yuanTotal % 10;
}
if (shiTotal >= 10) {
baiTotal += Math.floor(shiTotal / 10);
shiTotal = shiTotal % 10;
}
if (baiTotal >= 10) {
qianTotal += Math.floor(baiTotal / 10);
baiTotal = baiTotal % 10;
}
if (qianTotal >= 10) {
wanTotal += Math.floor(qianTotal / 10);
qianTotal = qianTotal % 10;
}
// 进位后同步更新input显示
// 通用解决方案:根据实际数值决定是否显示前导零
const totalAmount = wanTotal * 10000 + qianTotal * 1000 + baiTotal * 100 + shiTotal * 10 + yuanTotal + jiaoTotal * 0.1 + fenTotal * 0.01;
if (wanInput) {
// 如果总金额小于10000不显示万位
wanInput.value = totalAmount >= 10000 ? (wanTotal === 0 ? '0' : wanTotal) : '';
}
if (qianInput) {
// 如果总金额小于1000不显示千位
qianInput.value = totalAmount >= 1000 ? (qianTotal === 0 ? '0' : qianTotal) : '';
}
if (baiInput) {
// 如果总金额小于100不显示百位
baiInput.value = totalAmount >= 100 ? (baiTotal === 0 ? '0' : baiTotal) : '';
}
if (shiInput) {
// 如果总金额小于10不显示十位
shiInput.value = totalAmount >= 10 ? (shiTotal === 0 ? '0' : shiTotal) : '';
}
if (yuanInput) {
// 如果总金额小于1不显示元位
yuanInput.value = totalAmount >= 1 ? (yuanTotal === 0 ? '0' : yuanTotal) : '';
}
if (jiaoInput) {
// 如果总金额小于0.1,不显示角位
jiaoInput.value = totalAmount >= 0.1 ? (jiaoTotal === 0 ? '0' : jiaoTotal) : '';
}
if (fenInput) {
// 如果总金额小于0.01,不显示分位
fenInput.value = totalAmount >= 0.01 ? (fenTotal === 0 ? '0' : fenTotal) : '';
}
let otherTotal = wanTotal * 10000 + qianTotal * 1000 + baiTotal * 100 + shiTotal * 10 + yuanTotal + jiaoTotal * 0.1 + fenTotal * 0.01
let total = 0
// 只计算以 amount 开头的输入框
const amountInputs = dom.querySelectorAll('input[data-field^="amount"]')
console.log('计算总金额,找到输入框数量:', amountInputs.length)
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) {
totalInput.value = total.toFixed(2)
console.log('更新总金额输入框:', totalInput.value)
}
// 更新大写金额
const upperCaseInput = dom.querySelector('input[data-field="upperCaseAmount"]')
if (upperCaseInput) {
upperCaseInput.value = numberToChinese(total)
console.log('更新大写金额:', upperCaseInput.value)
}
if (otherTotal !== 0) {
upperCaseInput.value = numberToChinese(otherTotal)
}
},
updateUpperCaseFromTotal() {
const dom = this.$refs.printtable
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)
}
},
replaceControls(element) {
const inputs = element.getElementsByTagName('input')
Array.from(inputs).forEach(input => {
if (input.type === 'checkbox' || input.type === 'radio') {
if (input.type === 'radio') {
// 找到同组radio中被选中的
const name = input.name
const checkedRadio = element.querySelector(`input[type="radio"][name="${name}"]:checked`)
if (checkedRadio) {
// 找到包含radio组的td
const td = checkedRadio.closest('td')
if (td) {
// 找到隐藏的div
const hiddenDiv = td.querySelector('div[style*="display: none"]')
if (hiddenDiv) {
hiddenDiv.textContent = checkedRadio.value
}
}
}
}
return
}
const span = document.createElement('span')
let displayText = input.value || ''
if (input.type === 'date') {
displayText = input.value ? new Date(input.value).toLocaleDateString() : ''
}
span.textContent = displayText
const style = window.getComputedStyle(input)
// 先复制所有样式
span.style.cssText = style.cssText
;[
'width',
'height',
'padding',
'margin',
'font',
'fontSize',
'fontFamily',
'lineHeight',
'verticalAlign',
'border',
'background',
'color',
'boxSizing'
].forEach(key => {
span.style[key] = style[key]
})
// 确保width和height与Input控件保持一致
span.style.width = style.width//'100%'
span.style.height = style.height
span.style.whiteSpace = 'normal'
span.style.wordBreak = 'break-all'
span.style.overflowWrap = 'break-word'
span.style.display = 'block'
span.style.textAlign = 'center'
span.style.verticalAlign = 'middle'
input.parentNode.replaceChild(span, input)
})
const selects = element.getElementsByTagName('select')
Array.from(selects).forEach(select => {
const span = document.createElement('span')
span.textContent = select.options[select.selectedIndex]?.text || ''
const style = window.getComputedStyle(select)
// 先复制所有样式
span.style.cssText = style.cssText
;[
'width',
'height',
'padding',
'margin',
'font',
'fontSize',
'fontFamily',
'lineHeight',
'verticalAlign',
'border',
'background',
'color',
'boxSizing'
].forEach(key => {
span.style[key] = style[key]
})
// 确保width和height与select控件保持一致
span.style.width = style.width
span.style.height = style.height
span.style.display = 'block'
span.style.textAlign = 'center'
span.style.whiteSpace = 'pre-line' // 关键:自动换行
select.parentNode.replaceChild(span, select)
})
const textareas = element.getElementsByTagName('textarea')
Array.from(textareas).forEach(textarea => {
const span = document.createElement('span')
span.textContent = textarea.value || ''
const style = window.getComputedStyle(textarea)
// 先复制所有样式
span.style.cssText = style.cssText
;[
'width',
'height',
'padding',
'margin',
'font',
'fontSize',
'fontFamily',
'lineHeight',
'verticalAlign',
'border',
'background',
'color',
'boxSizing'
].forEach(key => {
span.style[key] = style[key]
})
// 确保width和height与textarea控件保持一致
span.style.width = style.width
span.style.height = style.height
span.style.display = 'block'
span.style.textAlign = 'center'
span.style.whiteSpace = 'pre-line' // 关键:自动换行
textarea.parentNode.replaceChild(span, textarea)
})
},
async print() {
try {
const tempContainer = document.createElement('div')
tempContainer.style.position = 'absolute'
tempContainer.style.left = '-9999px'
tempContainer.style.top = '-9999px'
document.body.appendChild(tempContainer)
const originalContent = this.$refs['printtable'].cloneNode(true)
tempContainer.appendChild(originalContent)
this.replaceControls(tempContainer)
const canvas = await html2canvas(tempContainer, {
backgroundColor: null,
useCORS: true
})
document.body.removeChild(tempContainer)
printJS({
printable: canvas.toDataURL(),
type: 'image',
documentTitle: `苏州市河道管理处${this.currentForm === 'pre' ? '事前审批表格' : this.currentForm === 'post' ? '事后支付表格' : '财务审核表'}`,
style: '@page{margin:auto;}'
})
} catch (error) {
console.error('打印失败:', error)
this.$Message.error('打印失败')
}
},
moneyFormat(val) {
if (!val && val !== 0) return '-'
return Number(val).toFixed(2).replace(/(\d)(?=(\d{3})+\.)/g, '$1,')
},
printHtml() {
// 先同步用户输入到HTML
const dom = this.$refs.printtable
if (dom) {
// 获取所有输入控件
const inputs = dom.querySelectorAll('input, select, textarea')
inputs.forEach(input => {
const fieldName = input.getAttribute('data-field')
if (fieldName) {
if (input.type === 'checkbox' || input.type === 'radio') {
// 对于复选框和单选框,需要找到选中的值
const checkedInput = dom.querySelector(`[data-field="${fieldName}"]:checked`)
if (checkedInput) {
// 找到同组的所有radio
const name = checkedInput.name
const radioGroup = dom.querySelectorAll(`input[type="radio"][name="${name}"]`)
radioGroup.forEach(radio => {
// 移除所有radio的checked属性
radio.removeAttribute('checked')
})
// 设置选中radio的checked属性
checkedInput.setAttribute('checked', 'checked')
}
} else {
// 对于其他类型的输入直接设置value
input.setAttribute('value', input.value)
}
}
})
}
// 使用同步后的HTML数据
const printNode = document.createElement('div')
printNode.innerHTML = dom.innerHTML
// 移除财务审核表
const financeTable = printNode.querySelector('.finance-review-table')
if (financeTable) {
financeTable.remove()
}
// 替换控件为纯文本(保留样式)
this.replaceControls(printNode)
console.log('printNode', printNode.innerHTML)
// 计算并更新总金额
const amountInputs = printNode.querySelectorAll('input[data-field^="amount"]')
let total = 0
amountInputs.forEach(input => {
const value = parseFloat(input.value) || 0
total += value
})
// 更新总金额显示
const totalInput = printNode.querySelector('input[data-field="total"]')
if (totalInput) {
totalInput.value = total.toFixed(2)
}
// 更新大写金额
const upperCaseInput = printNode.querySelector('input[data-field="upperCaseAmount"]')
if (upperCaseInput) {
upperCaseInput.value = numberToChinese(total)
}
const orientation = this.printOrientation || 'portrait';
const margin = orientation === 'portrait'
? '5mm 5mm 10mm 2mm' // 减少左右边距
: '5mm 5mm 10mm 5mm'; // 减少左右边距
const win = window.open('', '_blank')
// 动态计算宽高、缩放和平移
const isPortrait = orientation === 'portrait';
const pageWidth = isPortrait ? 290 : 297;
// 根据内容动态调整缩放和偏移
const hasTravelExpense = this.getForms && this.getForms.includes('差旅报销单');
const hasUtilityBill = this.getForms && this.getForms.includes('水电费结算单');
const hasReimbursementForm2 = this.getForms && this.getForms.includes('报销贴单2');
const scale = hasTravelExpense ? 0.66 : 0.9;
const offset = hasTravelExpense ? 62 : 5;
const translateX = ((pageWidth - pageWidth * scale) / 2 + offset).toFixed(2) + 'mm';
// 居中打印.
// table { width: 100%; border-collapse: collapse;table-layout: fixed; text-align: center; margin: 0 auto; }
// td { border: 1px solid #000; padding: 8px; min-width: 100px; word-break: break-all; text-align: center; vertical-align: middle; }
// th { text-align: center; vertical-align: middle; }
win.document.write(`
<html>
<head>
<title>打印</title>
<style>
@page { size: A4 ${orientation}; margin: ${margin}; }
body {
margin: 0;
padding: 0;
width: ${pageWidth}mm;
display: flex;
justify-content: center;
align-items: flex-start;
}
.white-container {
display: inline-block;
background: #fff;
box-sizing: border-box;
transform: scale(${scale}) translateX(${translateX});
transform-origin: top left;
margin: 0;
padding: 0;
}
.form-container { width: 100% !important; margin: 0 auto; }
table { width: 100%; border-collapse: collapse;table-layout: fixed; margin: 0 auto; }
td { border: 1px solid #000; padding: 8px; min-width: 100px; word-break: break-all; vertical-align: middle; }
th { vertical-align: middle; }
/* 资金划拨单最后一列宽度调整 */
${this.getForms && this.getForms.includes('资金划拨审批单') ? `
/* 改变表格布局方式 */
table {
table-layout: auto !important;
width: 100% !important;
}
/* 设置各列宽度比例 */
table td:nth-child(1) { width: 20% !important; }
table td:nth-child(2) { width: 20% !important; }
table td:nth-child(3) { width: 20% !important; }
table td:nth-child(4) {
width: 40% !important;
min-width: 500px !important;
max-width: none !important;
word-break: break-all !important;
white-space: normal !important;
overflow-wrap: break-word !important;
}
/* 处理跨列情况 */
table td[colspan] { width: auto !important; min-width: auto !important; }
/* 确保所有文本内容正常换行 */
table td * {
white-space: normal !important;
word-break: break-all !important;
overflow-wrap: break-word !important;
}
` : ''}
/* 水电费结算单特殊行高控制 */
${hasUtilityBill ? `
table td { height: 80px !important; min-height: 60px !important; line-height: 1.8 !important; }
table tr { height: 80px !important; min-height: 60px !important; }
` : ''}
/* 报销贴单2特殊行高控制 */
${hasReimbursementForm2 ? `
table td { height: 80px !important; min-height: 80px !important; line-height: 2.0 !important; }
table tr { height: 80px !important; min-height: 80px !important; }
` : ''}
@media print {
body {
width: ${pageWidth}mm;
margin: 0;
padding: 0;
display: flex;
justify-content: center;
align-items: flex-start;
}
.white-container {
transform: scale(${scale}) translateX(${translateX});
transform-origin: top left;
display: inline-block;
}
}
</style>
</head>
<body>
<div class="white-container">
<div class="form-container">
${printNode.innerHTML}
</div>
</div>
</body>
</html>
`)
win.document.close()
win.focus()
win.print()
win.close()
}
}
}
</script>
<style scoped lang="scss">
.form-switch {
margin-bottom: 20px;
text-align: center;
}
.white-container {
background: #fff;
padding: 20px;
}
.form-container {
position: relative;
width: 100%;
padding: 20px;
font-family: SimSun, serif;
}
.payment-form {
border: 1px solid #000;
padding: 20px;
}
.no-form-message {
text-align: center;
padding: 40px;
color: #999;
font-size: 16px;
}
.finance-review-table {
width: 100%;
border-collapse: collapse;
margin: 20px 0;
font-size: 15px;
background: #fff;
th, td {
border: 1px solid #e0e0e0;
padding: 10px 8px;
text-align: center;
}
th.finance-header-row {
background: #eaf3ff;
color: #2d8cf0;
font-size: 16px;
font-weight: 600;
}
.sub-header {
background: #f5f7fa;
color: #333;
font-weight: 600;
width: 120px;
}
}
.finance-review-header {
margin-bottom: 16px;
}
.payment-registration-row {
display: flex;
margin-bottom: 4px;
}
.payment-registration-row-title {
width: 100px;
font-weight: bold;
color: #333;
}
.payment-registration-row-content {
flex: 1;
color: #666;
}
@media print {
.white-container {
padding: 0;
}
.form-container {
padding: 0;
}
.payment-form {
border: 1px solid #000;
}
.no-print {
display: none !important;
}
}
</style>