preview.vue 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825
  1. <template>
  2. <Dialog
  3. v-model="dialogVisible"
  4. title="对账单预览"
  5. class="billPayPreview"
  6. width="60%"
  7. scroll
  8. @closed="handleClosed"
  9. >
  10. <el-tabs v-model="activeName" class="demo-tabs" @tab-change="handleClick">
  11. <el-tab-pane label="打印类型A(含固定费明细)" name="first">
  12. <div class="first" id="printTab1">
  13. <div class="header">
  14. <p class="title" style="font-size: 18px">{{ tenantName }}对账单预览</p>
  15. <div class="info-wrap" style="margin-top: -4px">
  16. <span
  17. ><b>姓名:</b> {{ dataForm.elderName
  18. }}{{
  19. dataForm.contractNumber == null || dataForm.contractNumber == ''
  20. ? ''
  21. : `(${dataForm.contractNumber})`
  22. }}</span
  23. >
  24. <span><b>床位号:</b> {{ dataForm.bedName }}</span>
  25. <span><b>缴费时间:</b> {{ formatTime(dataForm.payTime, 'yyyy-MM-dd') }}</span>
  26. <!-- <span><b>应收金额:</b> {{ totalPayAmount }}元</span>-->
  27. <span><b>经手人:</b> {{ dataForm.payeeName }}</span>
  28. <!-- <span><b>账单月:</b> {{ dataForm.billingMonth }}</span>-->
  29. <!-- <span><b>打印时间:</b> {{ dayjs(new Date()).format('YYYY-MM-DD HH:mm:ss') }}</span>-->
  30. </div>
  31. <!-- <div class="info-wrap" style="margin-top: -10px">-->
  32. <!-- <span><b>发票号:</b> {{ dataForm.invoiceNumber }}</span> -->
  33. <!-- <span><b>应收金额:</b> {{ totalPayAmount }}元</span>-->
  34. <!-- <span><b>医保费用:</b> {{ formatNum(dataForm.insuranceAmount) || '0' }}元</span>
  35. <span><b>个人应缴:</b> {{ formatNum(dataForm.payAmount) }}元</span> -->
  36. <!-- <span><b>长护险余额:</b> {{ formatNum(longTermAmount) || '0' }}元</span>-->
  37. <!-- <span><b>抹零金额:</b> {{ roundDown }}元</span>-->
  38. <!-- <span><b>个人应缴:</b> {{ payTotal }}元</span>-->
  39. <!-- </div>-->
  40. <!-- <div class="info-wrap" style="margin-top: -10px">-->
  41. <!-- <span><b>缴费时间:</b> {{ formatTime(dataForm.payTime, 'yyyy-MM-dd') }}</span>-->
  42. <!-- </div>-->
  43. </div>
  44. <el-divider style="margin-top: 0px" />
  45. <div class="content">
  46. <!-- <p class="info">以下是账单明细</p>-->
  47. <div class="flex">
  48. <b>{{
  49. dataForm.description
  50. ? tTitle + '(' + dataForm.description + ')'
  51. : tTitle + '(' + bTitle + ')'
  52. }}</b>
  53. <span><b>金额小计:</b>¥ {{ tableSubtotal }}</span>
  54. </div>
  55. <table style="width: 100%" border="2">
  56. <tr align="center">
  57. <td>序号</td>
  58. <td>费用分类</td>
  59. <td>费用名称</td>
  60. <!-- <td>单价(元/月)</td>
  61. <td>数量</td> -->
  62. <td>金额(元)</td>
  63. <td>实际发生时间</td>
  64. <td>备注</td>
  65. </tr>
  66. <tr v-for="(item, index) in dataForm.items" :key="index" align="center">
  67. <td>{{ index + 1 }}</td>
  68. <td>{{ item.itemCategoryName }}</td>
  69. <td>{{ item.itemName }}</td>
  70. <!-- <td>{{ tableRowPrice(item) }}</td>
  71. <td>{{ item.count }}</td> -->
  72. <td>{{ tableRowPay(item) }}</td>
  73. <td>{{ formatStartDate(item) }}</td>
  74. <td>{{ item.description }}</td>
  75. </tr>
  76. <tr align="center">
  77. <td>支付方式描述</td>
  78. <td colspan="5" class="pay-text-cell">{{ payText }}</td>
  79. </tr>
  80. <!-- <tr align="center">
  81. <td>预缴金额</td>
  82. <td colspan="5"></td>
  83. </tr> -->
  84. </table>
  85. </div>
  86. <!-- 机构签章:叠在表格右下角,旋转 -45° -->
  87. <div v-if="sealUrl" class="seal-overlay">
  88. <img :src="sealUrl" class="seal-img" alt="机构公章" />
  89. </div>
  90. </div>
  91. </el-tab-pane>
  92. <el-tab-pane label="打印类型B(不含固定费明细)" name="second">
  93. <div class="second" id="printTab2">
  94. <div class="header">
  95. <p class="title">{{ tenantName }}对账单预览</p>
  96. <div class="info-wrap">
  97. <span>{{ dataForm.billingMonth }}账单</span>
  98. <span>{{ tenantName }}</span>
  99. <div class="right">
  100. <span><b>打印时间:</b> {{ dayjs(new Date()).format('YYYY-MM-DD HH:mm:ss') }}</span>
  101. </div>
  102. </div>
  103. </div>
  104. <div class="content">
  105. <table style="width: 100%" border="2">
  106. <tr align="center">
  107. <td>姓名</td>
  108. <td colspan="3">{{ dataForm.elderName }}</td>
  109. <td>床位号</td>
  110. <td colspan="3"> {{ dataForm.bedName }}</td>
  111. </tr>
  112. <tr align="center">
  113. <td rowspan="4">月度费用</td>
  114. <td colspan="2">费用名称</td>
  115. <td>金额(元)</td>
  116. <td rowspan="4">其他费用</td>
  117. <td colspan="2">费用名称</td>
  118. <td>金额(元)</td>
  119. </tr>
  120. <tr align="center">
  121. <td colspan="2">
  122. <p>{{ tTitle }}</p>
  123. <p v-if="!dataForm.description">({{ bTitle }})</p>
  124. <p v-if="dataForm.description">({{ dataForm.description }})</p>
  125. </td>
  126. <td>{{ table2MonthPay }}</td>
  127. <td colspan="2">{{ dataForm.otherName }}</td>
  128. <td>{{ table2OtherPay }}</td>
  129. </tr>
  130. <tr></tr>
  131. <tr align="center">
  132. <td colspan="2">小计</td>
  133. <td>{{ formatNum(dataForm.monthNum) }}</td>
  134. <td colspan="2">小计</td>
  135. <td>{{ formatNum(dataForm.otherNum) }}</td>
  136. </tr>
  137. <tr>
  138. <td colspan="2" align="center">账单金额</td>
  139. <td colspan="6">{{ totalPayAmount }}元</td>
  140. </tr>
  141. <tr>
  142. <td colspan="2" align="center">长护险费用</td>
  143. <td colspan="6">{{ formatNum(longTermAmount) || '0' }}元</td>
  144. </tr>
  145. <tr>
  146. <td colspan="2" align="center">抹零金额</td>
  147. <td colspan="6">{{ formatNum(roundDown) }}元</td>
  148. </tr>
  149. <tr>
  150. <td colspan="2" align="center">个人应缴</td>
  151. <td colspan="6">{{ payTotal }}元</td>
  152. </tr>
  153. </table>
  154. </div>
  155. <!-- 机构签章:叠在表格右下角,旋转 -45° -->
  156. <div v-if="sealUrl" class="seal-overlay">
  157. <img :src="sealUrl" class="seal-img" alt="机构公章" />
  158. </div>
  159. </div>
  160. </el-tab-pane>
  161. </el-tabs>
  162. <template #footer>
  163. <el-button @click="handleClosed">取消</el-button>
  164. <el-button type="primary" v-print="printTab2" v-if="activeName == 'second'">打印</el-button>
  165. <el-button type="primary" v-print="printTab1" v-if="activeName == 'first'">打印</el-button>
  166. </template>
  167. </Dialog>
  168. </template>
  169. <script setup lang="ts">
  170. import { getExpenseOrderDetail } from '@/api/elderly/fee/bill-pay'
  171. import dayjs from 'dayjs'
  172. import { useUserStore } from '@/store/modules/user'
  173. import { getTenant } from '@/api/system/tenant'
  174. import { getTenantId } from '@/utils/auth'
  175. import {
  176. fixedNum,
  177. formatCeil,
  178. formatDecimal,
  179. formatNum,
  180. formatRid,
  181. formatRound
  182. } from '@/utils/formatter'
  183. import { useSettingStore } from '@/store/modules/setting'
  184. import { formatTime } from '@/utils'
  185. import { getDictLabel, DICT_TYPE } from '@/utils/dict'
  186. defineOptions({ name: 'BillPayPreview' })
  187. const settingStore = useSettingStore()
  188. const tenantName = useUserStore().getTenantName // 当前登录的编号
  189. const dialogVisible = ref(false)
  190. const activeName = ref('first')
  191. const dataForm = ref({
  192. insuranceAmount: 0,
  193. payAmount: 0,
  194. billingMonth: '',
  195. contractNumber: '',
  196. items: [],
  197. payInfos: [],
  198. otherName: '',
  199. otherNum: '',
  200. monthNum: '',
  201. elderName: '',
  202. bedName: '',
  203. actualAmount: '',
  204. type: '',
  205. invoiceNumber: '',
  206. payTime: '',
  207. payeeName: '',
  208. description: ''
  209. })
  210. const longTermAmount = ref(0)
  211. const payText = ref('')
  212. const roundDown = ref(0)
  213. // 当前机构公章(来自租户接口 expenseCachetUrl);为空字符串时签章区不渲染
  214. const sealUrl = ref('')
  215. const formatPayCreatedTime = (createdTime) => {
  216. if (!createdTime) return ''
  217. const timestamp = String(createdTime).length === 10 ? Number(createdTime) * 1000 : Number(createdTime)
  218. return dayjs(timestamp).format('YYYY-MM-DD')
  219. }
  220. /** 打开弹窗 */
  221. const open = async (id, row) => {
  222. dialogVisible.value = true
  223. // 并行:账单详情 + 当前机构公章(容错,租户接口失败不影响主流程)
  224. const [res, tenant] = await Promise.all([
  225. getExpenseOrderDetail(id),
  226. getTenant(getTenantId() as number).catch(() => null)
  227. ])
  228. sealUrl.value = tenant?.expenseCachetUrl || ''
  229. let flag = false
  230. dataForm.value = res
  231. res.items = res.items.filter((item) => item.isShow == 1)
  232. res.items.map((item) => {
  233. if (item.expenseSource == 'long_term_care_insurance') {
  234. longTermAmount.value += item.actualPrice
  235. item.payStatus = 0
  236. flag = true
  237. // 遍历长护险
  238. res.expenseSubsidyDO.map((d) => {
  239. if (Math.abs(d.id || 1) == Math.abs(item.sourceExpenseItemId || 0)) {
  240. item.createdTime = d.month
  241. item.description = d.remarks
  242. if (!item.itemCategoryName) {
  243. item.itemCategoryName = '长护险'
  244. }
  245. }
  246. })
  247. }
  248. })
  249. dataForm.value.payeeName = row.payeeName
  250. // 已支付状态下的才是所有items
  251. if (res.payStatus == 0) {
  252. const arr = []
  253. res.items.map((item) => {
  254. if (item.payStatus == 1) {
  255. arr.push(item)
  256. }
  257. })
  258. dataForm.value.items = arr
  259. }
  260. // 已支付订单金额
  261. let num = 0
  262. let insurance = 0
  263. res.payInfos.map((item) => {
  264. item.items.map((info) => {
  265. const payLine =
  266. getDictLabel(DICT_TYPE.BILL_PAY_TYPE, info.payType) +
  267. ':' +
  268. info.payAmount +
  269. '元(支付时间:' +
  270. formatPayCreatedTime(info.createdTime) +
  271. ')'
  272. payText.value += payText.value ? '\n' + payLine : payLine
  273. })
  274. num += formatDecimal(item.payAmount) as number
  275. insurance += item.insuranceAmount ? (formatDecimal(item.insuranceAmount) as number) : 0
  276. // 看看是否有抹零金额
  277. if (item.discountAmount) {
  278. roundDown.value += item.discountAmount
  279. }
  280. })
  281. dataForm.value.payAmount = formatRid(num) as number
  282. dataForm.value.insuranceAmount = formatRid(insurance) as number
  283. // 有长护险
  284. if (!flag) {
  285. if (res.expenseSubsidyDO && res.expenseSubsidyDO.length) {
  286. let num = 0
  287. res.expenseSubsidyDO.map((item) => {
  288. num += item.amount
  289. dataForm.value.items.push({
  290. expenseSource: 'long_term_care_insurance',
  291. itemCategoryName: '长护险',
  292. itemName: '长护险抵扣',
  293. description: item.remarks ? item.remarks : item.month + '长护险抵扣',
  294. roundAmount: -item.amount,
  295. roundTwoDecimalAmount: -item.amount,
  296. totalAmount: -item.amount,
  297. createdTime: item.month,
  298. payTime: '',
  299. payStatus: item.status
  300. })
  301. })
  302. // 去除长护险费用
  303. // unPayTotal.value -= num
  304. longTermAmount.value = num
  305. // payTotal.value -= res.num
  306. }
  307. }
  308. }
  309. defineExpose({ open }) // 提供 open 方法,用于打开弹窗
  310. const handleClick = (val) => {
  311. activeName.value = val
  312. // 计算固定费用和其他费用
  313. if (val == 'second') {
  314. let monthNum = 0,
  315. otherNum = 0,
  316. otherName = ''
  317. dataForm.value.items.map((item) => {
  318. if (item.isMonthlyExpense == 1) {
  319. if (rounding.value) {
  320. // 取整
  321. monthNum +=
  322. item.expenseSource == 'consumer_voucher'
  323. ? getConsumerVoucherAmountNumber(item)
  324. : Number(item.roundAmount)
  325. } else if (fixed2Num.value) {
  326. // 四舍五入
  327. monthNum +=
  328. item.expenseSource == 'consumer_voucher'
  329. ? getConsumerVoucherAmountNumber(item)
  330. : Number(item.roundTwoDecimalAmount)
  331. } else {
  332. // 应收
  333. monthNum +=
  334. item.expenseSource == 'consumer_voucher'
  335. ? getConsumerVoucherAmountNumber(item)
  336. : Number(formatNum(item.totalAmount))
  337. }
  338. } else {
  339. if (item.itemName != '长护险护理补贴费用') {
  340. otherName += item.itemName + ';'
  341. if (rounding.value) {
  342. // 取整
  343. otherNum +=
  344. item.expenseSource == 'consumer_voucher'
  345. ? getConsumerVoucherAmountNumber(item)
  346. : Number(item.roundAmount)
  347. } else if (fixed2Num.value) {
  348. // 四舍五入
  349. otherNum +=
  350. item.expenseSource == 'consumer_voucher'
  351. ? getConsumerVoucherAmountNumber(item)
  352. : Number(item.roundTwoDecimalAmount)
  353. } else {
  354. // 应收
  355. otherNum +=
  356. item.expenseSource == 'consumer_voucher'
  357. ? getConsumerVoucherAmountNumber(item)
  358. : Number(formatNum(item.totalAmount))
  359. }
  360. }
  361. }
  362. })
  363. dataForm.value.monthNum = monthNum
  364. dataForm.value.otherNum = otherNum
  365. dataForm.value.otherName = otherName
  366. }
  367. }
  368. const handleClosed = () => {
  369. payText.value = ''
  370. activeName.value = 'first'
  371. longTermAmount.value = 0
  372. sealUrl.value = ''
  373. dialogVisible.value = false
  374. }
  375. // 取整金额
  376. const rounding = computed(() => {
  377. return settingStore.getRounding
  378. })
  379. // 四舍五入保留两位
  380. const fixed2Num = computed(() => {
  381. return settingStore.getFix2Num
  382. })
  383. const getConsumerVoucherAmountNumber = (item) => {
  384. const ruleValue = settingStore.getCouponRuleValue
  385. const rawAmount = item?.totalAmount ?? 0
  386. if (ruleValue == 2 || ruleValue == '2') {
  387. return Number(formatRound(rawAmount))
  388. }
  389. return Number(formatNum(rawAmount))
  390. }
  391. const getConsumerVoucherAmountText = (item) => {
  392. const ruleValue = settingStore.getCouponRuleValue
  393. const rawAmount = item?.totalAmount ?? 0
  394. if (ruleValue == 2 || ruleValue == '2') {
  395. return formatRound(rawAmount)
  396. }
  397. return formatNum(rawAmount)
  398. }
  399. // 账单总额
  400. const totalPayAmount = computed(() => {
  401. let num1 = 0,
  402. num2 = 0,
  403. num3 = 0 // 入院向上取整 、 入院保留两位、 入院应收
  404. // 遍历里面的所有数据得到数据
  405. dataForm.value.items.map((item) => {
  406. if (item.itemName != '长护险护理补贴费用') {
  407. num1 +=
  408. item.expenseSource == 'consumer_voucher'
  409. ? getConsumerVoucherAmountNumber(item)
  410. : Number(item.roundAmount)
  411. num2 +=
  412. item.expenseSource == 'consumer_voucher'
  413. ? getConsumerVoucherAmountNumber(item)
  414. : Number(item.roundTwoDecimalAmount)
  415. if (item.isMonthlyExpense == 1 && dataForm.value.type == 1) {
  416. if (settingStore.getAdmissionBill == 2) {
  417. // 向上取整
  418. num3 +=
  419. item.expenseSource == 'consumer_voucher'
  420. ? getConsumerVoucherAmountNumber(item)
  421. : Number(formatCeil(item.totalAmount))
  422. } else if (settingStore.getAdmissionBill == 3) {
  423. // 四舍五入保留两位
  424. num3 +=
  425. item.expenseSource == 'consumer_voucher'
  426. ? getConsumerVoucherAmountNumber(item)
  427. : Number(item.roundTwoDecimalAmount)
  428. } else if (settingStore.getAdmissionBill == 4) {
  429. // 四舍五入取整
  430. num3 +=
  431. item.expenseSource == 'consumer_voucher'
  432. ? getConsumerVoucherAmountNumber(item)
  433. : Number(item.roundAmount)
  434. } else {
  435. num3 +=
  436. item.expenseSource == 'consumer_voucher'
  437. ? getConsumerVoucherAmountNumber(item)
  438. : Number(item.totalAmount)
  439. }
  440. } else {
  441. if (rounding.value) {
  442. num3 +=
  443. item.expenseSource == 'consumer_voucher'
  444. ? getConsumerVoucherAmountNumber(item)
  445. : Number(item.roundAmount)
  446. } else if (fixed2Num.value) {
  447. num3 +=
  448. item.expenseSource == 'consumer_voucher'
  449. ? getConsumerVoucherAmountNumber(item)
  450. : Number(item.roundTwoDecimalAmount)
  451. } else {
  452. num3 +=
  453. item.expenseSource == 'consumer_voucher'
  454. ? getConsumerVoucherAmountNumber(item)
  455. : Number(item.totalAmount)
  456. }
  457. }
  458. }
  459. })
  460. if (dataForm.value.type == 1) {
  461. return formatNum(num3)
  462. } else {
  463. if (rounding.value) {
  464. // 取整(有问题)
  465. return formatNum(num1)
  466. } else if (fixed2Num.value) {
  467. // 四舍五入
  468. return formatNum(num2)
  469. } else {
  470. return formatNum(dataForm.value.actualAmount)
  471. }
  472. }
  473. })
  474. // 个人应缴
  475. const payTotal = computed(() => {
  476. let num = 0
  477. dataForm.value.payInfos.map((item) => {
  478. num += item.payAmount
  479. })
  480. return formatNum(num)
  481. // return formatNum(totalPayAmount.value - longTermAmount.value - roundDown.value)
  482. })
  483. // 表格1单价
  484. const tableRowPrice = (row) => {
  485. return formatNum(row.actualPrice)
  486. }
  487. // 表格1总额单项小计
  488. const tableRowPay = (row) => {
  489. if (row.expenseSource == 'consumer_voucher') {
  490. return getConsumerVoucherAmountText(row)
  491. }
  492. if (dataForm.value.type == '1' && row.isMonthlyExpense == 1) {
  493. if (settingStore.getAdmissionBill == 2) {
  494. // 向上取整
  495. return formatNum(formatCeil(row.totalAmount))
  496. } else if (settingStore.getAdmissionBill == 3) {
  497. // 四舍五入保留两位
  498. return formatNum(row.roundTwoDecimalAmount)
  499. } else if (settingStore.getAdmissionBill == 4) {
  500. // 四舍五入取整
  501. return formatNum(row.roundAmount)
  502. } else {
  503. return formatNum(row.totalAmount)
  504. }
  505. } else {
  506. if (rounding.value) {
  507. return formatNum(row.roundAmount)
  508. } else if (fixed2Num.value) {
  509. return formatNum(row.roundTwoDecimalAmount)
  510. } else {
  511. return formatNum(row.totalAmount)
  512. }
  513. }
  514. }
  515. // 表格1总额小计
  516. const tableSubtotal = computed(() => {
  517. let num = 0
  518. dataForm.value.items.map((item) => {
  519. if (dataForm.value.type == '1' && item.isMonthlyExpense == 1) {
  520. if (settingStore.getAdmissionBill == 2) {
  521. // 向上取整
  522. num +=
  523. item.expenseSource == 'consumer_voucher'
  524. ? getConsumerVoucherAmountNumber(item)
  525. : Number(formatCeil(item.totalAmount))
  526. } else if (settingStore.getAdmissionBill == 3) {
  527. // 四舍五入保留两位
  528. num +=
  529. item.expenseSource == 'consumer_voucher'
  530. ? getConsumerVoucherAmountNumber(item)
  531. : Number(item.roundTwoDecimalAmount)
  532. } else if (settingStore.getAdmissionBill == 4) {
  533. // 四舍五入取整
  534. num +=
  535. item.expenseSource == 'consumer_voucher'
  536. ? getConsumerVoucherAmountNumber(item)
  537. : Number(item.roundAmount)
  538. } else {
  539. // 应收
  540. num +=
  541. item.expenseSource == 'consumer_voucher'
  542. ? getConsumerVoucherAmountNumber(item)
  543. : Number(item.totalAmount)
  544. }
  545. } else {
  546. if (rounding.value) {
  547. num +=
  548. item.expenseSource == 'consumer_voucher'
  549. ? getConsumerVoucherAmountNumber(item)
  550. : Number(item.roundAmount)
  551. } else if (fixed2Num.value) {
  552. num +=
  553. item.expenseSource == 'consumer_voucher'
  554. ? getConsumerVoucherAmountNumber(item)
  555. : Number(item.roundTwoDecimalAmount)
  556. } else {
  557. num +=
  558. item.expenseSource == 'consumer_voucher'
  559. ? getConsumerVoucherAmountNumber(item)
  560. : Number(item.totalAmount)
  561. }
  562. }
  563. })
  564. return formatNum(num)
  565. })
  566. // 表格2月度总额
  567. const table2MonthPay = computed(() => {
  568. return formatNum(dataForm.value.monthNum)
  569. })
  570. // 表格2其他费用
  571. const table2OtherPay = computed(() => {
  572. return formatNum(dataForm.value.otherNum)
  573. })
  574. const tTitle = computed(() => {
  575. const arr = dataForm.value.billingMonth ? dataForm.value.billingMonth.split('-') : ''
  576. if (arr.length) {
  577. return `预收${arr[0]}年${arr[1]}月固定费用`
  578. }
  579. return ''
  580. })
  581. const bTitle = computed(() => {
  582. const arr = dataForm.value.billingMonth ? dataForm.value.billingMonth.split('-') : ''
  583. if (arr.length) {
  584. // 获取当月最后一天
  585. const date = new Date(new Date(Number(arr[0]), Number(arr[1]), 0)).getDate()
  586. return `${arr[0]}年${arr[1]}月01日-${arr[0]}年${arr[1]}月${date}日`
  587. }
  588. return ''
  589. })
  590. // const formatStartDate = (item) => {
  591. // return item.expenseSource == 'long_term_care_insurance' || item.itemName == '长护险护理补贴费用' ? item.createdTime : (item.expenseSource == 'expense_item' ? (dataForm.value.description ? dataForm.value.description : bTitle) : (item.startDate + '至' + item.endDate))
  592. // }
  593. const formatStartDate = (item) => {
  594. if (item.itemName == '长护险护理补贴费用' || item.expenseSource == 'long_term_care_insurance') {
  595. // 长护险
  596. return item.createdTime
  597. // let dataStr = item.startDate??"--"
  598. // if(dataStr.length>=10){
  599. // dataStr = dataStr.substring(0,7)
  600. // }
  601. // return dataStr
  602. } else if (item.expenseSource == 'expense_item') {
  603. // if(dataForm.value.description){
  604. // return dataForm.value.description
  605. // }else{
  606. // return bTitle
  607. // }
  608. if (item.description && item.description.includes('的')) {
  609. return item.description.split('的')[0]
  610. } else if (item.startDate != null && item.endDate != null) {
  611. return `${item.startDate}至${item.endDate}`
  612. } else if (
  613. item.startDate == null &&
  614. dataForm.value.description &&
  615. dataForm.value.description.length > 21
  616. ) {
  617. return dataForm.value.description.toString().substring(0, 21)
  618. } else {
  619. return bTitle
  620. }
  621. } else {
  622. return item.startDate + '至' + item.endDate
  623. }
  624. }
  625. const formatStr = (dateStr) => {
  626. if (dateStr) {
  627. // 2. 按 "-" 拆分字符串,得到 [年, 月, 日] 数组
  628. const [year, month, day] = dateStr.split('-')
  629. // 3. 按目标格式拼接
  630. return `${year}年${month}月${day}日`
  631. }
  632. return ''
  633. }
  634. // 打印样式:与预览样式保持一致
  635. // - .first/.second 加 padding-bottom 给签章留出空间,否则签章会贴到纸张边缘或被外框裁掉
  636. // - 取消容器滚动限制(v-print 复制到 iframe 时容易继承 height/overflow 限制导致内容被裁)
  637. // - 同步印章位置:right: 5mm / bottom: 8mm,让印章向下偏移并完整可见
  638. const printExtraCss = `
  639. html, body { height: auto !important; overflow: visible !important; }
  640. #printTab1, #printTab2 { height: auto !important; overflow: visible !important; padding-bottom: 12mm !important; }
  641. table { page-break-inside: auto; width: 100%; border-collapse: collapse; }
  642. tr { page-break-inside: avoid; page-break-after: auto; }
  643. thead { display: table-header-group; }
  644. tfoot { display: table-footer-group; }
  645. .seal-overlay { right: 5mm !important; bottom: 0mm !important; }
  646. `
  647. // 打印
  648. const printTab1 = {
  649. id: 'printTab1',
  650. extraCss: '',
  651. popTitle: '',
  652. extraHead: `<style>${printExtraCss}</style>`,
  653. preview: false,
  654. previewTitle: tenantName + '对账单预览',
  655. previewPrintBtnLabel: '预览结束,开始打印',
  656. zIndex: 20002,
  657. previewBeforeOpenCallback() {
  658. console.log('正在加载预览窗口!')
  659. },
  660. previewOpenCallback() {
  661. console.log('已经加载完预览窗口,预览打开了!')
  662. },
  663. beforeOpenCallback() {
  664. console.log('开始打印之前!')
  665. },
  666. openCallback() {
  667. console.log('执行打印了!')
  668. },
  669. closeCallback() {
  670. console.log('关闭了打印工具!')
  671. },
  672. clickMounted() {
  673. console.log('点击v-print绑定的按钮了!')
  674. },
  675. standard: ''
  676. }
  677. // 打印
  678. const printTab2 = {
  679. id: 'printTab2',
  680. extraCss: '',
  681. popTitle: '',
  682. extraHead: `<style>${printExtraCss}</style>`,
  683. preview: false,
  684. previewTitle: tenantName + '对账单预览',
  685. previewPrintBtnLabel: '预览结束,开始打印',
  686. zIndex: 20002,
  687. previewBeforeOpenCallback() {
  688. console.log('正在加载预览窗口!')
  689. },
  690. previewOpenCallback() {
  691. console.log('已经加载完预览窗口,预览打开了!')
  692. },
  693. beforeOpenCallback() {
  694. console.log('开始打印之前!')
  695. },
  696. openCallback() {
  697. console.log('执行打印了!')
  698. },
  699. closeCallback() {
  700. console.log('关闭了打印工具!')
  701. },
  702. clickMounted() {
  703. console.log('点击v-print绑定的按钮了!')
  704. },
  705. standard: ''
  706. }
  707. </script>
  708. <style lang="scss" scoped>
  709. .first,
  710. .second {
  711. position: relative; // 让 .seal-overlay 相对本容器定位
  712. // 给"向下偏移"的印章留出空间,避免被外层(dialog 滚动容器 / 打印纸张)裁掉
  713. // 注意:打印样式 printExtraCss 同步设置 padding-bottom: 40mm,保证预览/打印一致
  714. padding-bottom: 12mm;
  715. }
  716. .first {
  717. .header {
  718. .title {
  719. font-size: 20px;
  720. font-weight: bold;
  721. text-align: center;
  722. }
  723. .info-wrap {
  724. position: relative;
  725. span {
  726. display: inline-block;
  727. margin-bottom: 10px;
  728. width: 25%;
  729. }
  730. .right {
  731. display: inline-block;
  732. width: 49%;
  733. text-align: right;
  734. span {
  735. width: 100%;
  736. }
  737. }
  738. }
  739. }
  740. .content {
  741. .info {
  742. text-align: center;
  743. }
  744. .flex {
  745. display: flex;
  746. justify-content: space-between;
  747. margin-bottom: 10px;
  748. }
  749. }
  750. }
  751. .second {
  752. .header {
  753. .title {
  754. font-size: 20px;
  755. font-weight: bold;
  756. text-align: center;
  757. }
  758. .info-wrap {
  759. display: flex;
  760. justify-content: space-between;
  761. }
  762. }
  763. }
  764. .pay-text-cell {
  765. white-space: pre-line;
  766. }
  767. // 机构签章:绝对定位叠在表格右下角,旋转 -45°
  768. // - 位置:right: 5mm / bottom: 8mm,让印章向下偏移并在 .first 容器的 padding-bottom 区域内完整可见
  769. // (之前 bottom: -10mm 会让印章底边超出容器底边,被 dialog 滚动容器或打印纸张裁切)
  770. // - z-index: 20 + 父级 position: relative 保证印章叠在表格之上
  771. // - pointer-events: none 不挡表格交互(hover/点击备注等不受影响)
  772. // - opacity: 0.85 略透,不完全遮住备注列文字
  773. .seal-overlay {
  774. position: absolute;
  775. right: 5mm;
  776. bottom: 0mm;
  777. width: 30mm;
  778. height: 30mm;
  779. z-index: 20;
  780. pointer-events: none;
  781. .seal-img {
  782. width: 100%;
  783. height: 100%;
  784. object-fit: contain;
  785. transform: rotate(0deg);
  786. transform-origin: 100% 100%;
  787. opacity: 0.85;
  788. }
  789. }
  790. </style>