| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547 |
- <template>
- <Dialog
- v-model="dialogVisible"
- title="缴费通知单预览"
- class="billPayPreview"
- width="70%"
- @closed="handleClosed"
- scroll
- >
- <div class="first" id="printTab1">
- <div class="header">
- <p class="title" style="font-size: 18px">缴费通知单</p>
- <!-- 右上角按钮:仅屏幕可见,打印时通过 .no-print + printExtraCss 隐藏 -->
- <el-button
- v-if="relatives.length"
- class="no-print relative-action"
- type="primary"
- plain
- size="small"
- @click="relativeDialogVisible = true"
- >
- 家属信息({{ relatives.length }})
- </el-button>
- <div class="info-wrap" style="margin-top: -4px">
- <span><b>机构:</b> {{ tenantName }}</span>
- <span><b>姓名:</b> {{ dataForm.elderName }}</span>
- <span><b>编号:</b> {{ dataForm.billOrderNumber }}</span>
- </div>
- <div class="info-wrap">
- <span><b>床位号:</b> {{ dataForm.bedName }}</span>
- <span><b>性别:</b> {{ getDictLabel(DICT_TYPE.SYSTEM_USER_SEX, dataForm.elderSex) }}</span>
- <span><b>经手人:</b> {{ dataForm.createdBy }}</span>
- </div>
- </div>
- <el-divider style="margin-top: 0px" />
- <div class="content">
- <!-- <p class="info">以下是账单明细</p>-->
- <div class="flex">
- <b>{{
- dataForm.description
- ? tTitle + '(' + dataForm.description + ')'
- : tTitle + '(' + bTitle + ')'
- }}</b>
- </div>
- <table style="width: 100%" border="2">
- <tr align="center">
- <td>序号</td>
- <td>费用分类</td>
- <td>费用名称</td>
- <td>金额(元)</td>
- <td>缴费状态</td>
- <td>已缴金额(元)</td>
- <td>费用发生时间</td>
- <td>备注</td>
- </tr>
- <tr v-for="(item, index) in dataForm.items" :key="index" align="center">
- <td>{{ index + 1 }}</td>
- <td>{{ item.itemCategoryName }}</td>
- <td>{{ item.itemName }}</td>
- <td>{{ calcTotalPrice(item) }}</td>
- <td>{{ getDictLabel(DICT_TYPE.PAY_TYPE, item.payStatus) }}</td>
- <td>{{ item.payAmount ?? 0 }}元</td>
- <td>{{ formatStartDate(item) }}</td>
- <td>{{ item.description }}</td>
- </tr>
- <tr align="center">
- <td>合计</td>
- <td :colspan="8">
- <div>
- 金额:{{ billTotalPrice }}元;已缴费共计: {{
- rounding ? formatTrunc(payTotal) : formatNum(payTotal)
- }} 元;剩余未缴费: {{
- rounding ? formatTrunc(unPayTotal) : formatNum(unPayTotal)
- }} 元
- </div>
- <div
- v-show="tempResValue.payInfos && tempResValue.payInfos.length > 0"
- style="
- display: flex;
- align-items: center;
- flex-direction: row;
- justify-content: center;
- "
- >
- 其中(<div
- style="margin-right: 10px"
- v-for="(item, index) in tempResValue.payInfos || []"
- :key="index"
- >
- {{ getDictLabel(DICT_TYPE.BILL_PAY_TYPE, parseInt(item.payType)) }}支付:{{
- formatNum(item.items[0].payAmount)
- }}元 {{ formatTimestamp(item.items[0].createdTime) }};</div
- >)
- </div>
- </td>
- </tr>
- </table>
- </div>
- </div>
- <template #footer>
- <el-button @click="handleClosed">取消</el-button>
- <el-button type="primary" v-print="printTab">打印</el-button>
- </template>
- </Dialog>
- <!-- 家属信息弹窗(独立于缴费通知单预览,不在 #printTab1 范围内,自然不会被打印) -->
- <Dialog
- v-model="relativeDialogVisible"
- title="家属信息"
- width="60%"
- @closed="handleRelativeClosed"
- >
- <el-table :data="relatives" border size="default">
- <el-table-column type="index" label="序号" width="60" align="center" />
- <el-table-column label="姓名" prop="peopleName" min-width="100" />
- <el-table-column label="关系" prop="relation" min-width="80" />
- <el-table-column label="联系电话" prop="phone" min-width="130" />
- <el-table-column label="身份证号" prop="idCard" min-width="170" />
- <el-table-column label="账单发送人" min-width="100">
- <template #default="{ row }">
- <el-tag :type="row.isBillSender ? 'success' : 'info'" size="small">
- {{ row.isBillSender ? '是' : '否' }}
- </el-tag>
- </template>
- </el-table-column>
- </el-table>
- </Dialog>
- </template>
- <script setup lang="ts">
- import { getExpenseOrderDetail } from '@/api/elderly/fee/bill-pay'
- import { useUserStore } from '@/store/modules/user'
- import { formatNum, formatCeil, formatRound, formatTrunc } from '@/utils/formatter'
- import { DICT_TYPE, getDictLabel } from '@/utils/dict'
- import { useSettingStore } from '@/store/modules/setting'
- import { formatDateTime } from '@/components/UploadFile'
- import { formatTimestamp, formatToDateTime } from '@/utils/dateUtil'
- defineOptions({ name: 'BillPayPreview' })
- const settingStore = useSettingStore()
- const tenantName = useUserStore().getTenantName // 当前登录的编号
- const dialogVisible = ref(false)
- const dataForm = ref({
- insuranceAmount: 0,
- payAmount: 0,
- billingMonth: '',
- items: [],
- otherName: '',
- otherNum: '',
- monthNum: '',
- elderName: '',
- bedName: '',
- actualAmount: '',
- type: '',
- createdBy: '',
- invoiceNumber: '',
- description: ''
- })
- const total = ref(0)
- const totalDecimal = ref(0)
- const unPayTotal = ref(0) // 未缴总额
- const payTotal = ref(0) // 已缴总额
- const longTermAmount = ref(0) // 长护险
- // 取整金额
- const rounding = computed(() => {
- return settingStore.getRounding
- })
- // 四舍五入保留两位
- const fixed2Num = computed(() => {
- return settingStore.getFix2Num
- })
- // 长护险规则
- const longCareRuleValue = computed(() => {
- return settingStore.getLongCareRuleValue
- })
- /** 消费券/长护险都按各自的抹零配置单独取数 */
- const hasSourceRule = (item) =>
- item.expenseSource == 'consumer_voucher' || item.expenseSource == 'long_term_care_insurance'
- /** 长护险金额:按长护险抹零配置取数(2=取整,其它=保留两位小数) */
- const getLongCareAmountNumber = (item) => {
- const rawAmount = item?.totalAmount ?? 0
- if (longCareRuleValue.value == 2 || longCareRuleValue.value == '2') {
- return Number(formatRound(rawAmount))
- }
- return Number(formatNum(rawAmount))
- }
- /** 长护险金额:展示用字符串 */
- const getLongCareAmountText = (item) => {
- if (longCareRuleValue.value == 2 || longCareRuleValue.value == '2') {
- return formatRound(item?.totalAmount ?? 0)
- }
- return formatNum(item?.totalAmount ?? 0)
- }
- /** 按长护险规则处理后的补贴金额 */
- const formatLongCareAmount = (amount) => {
- if (longCareRuleValue.value == 2 || longCareRuleValue.value == '2') {
- return Number(formatRound(amount || 0))
- }
- return Number(formatNum(amount || 0))
- }
- const tempResValue = ref({})
- // 家属信息:后端响应 relatives 数组(每项含 peopleName/relation/phone/idCard/insurance/isBillSender 等)
- const relatives = ref<any[]>([])
- const relativeDialogVisible = ref(false)
- const getItemPreviewAmount = (item) => {
- // 消费券/长护险优先按各自抹零规则取数(与表格展示保持一致)
- if (item.expenseSource == 'consumer_voucher') {
- const ruleValue = settingStore.getCouponRuleValue
- if (ruleValue == 2 || ruleValue == '2') {
- return Number(formatRound(item.totalAmount))
- }
- return Number(formatNum(item.totalAmount))
- }
- if (item.expenseSource == 'long_term_care_insurance') {
- return getLongCareAmountNumber(item)
- }
- if (dataForm.value.type == '1' && item.isMonthlyExpense == 1) {
- if (settingStore.getAdmissionBill == 2) {
- return Number(formatCeil(item.totalAmount))
- } else if (settingStore.getAdmissionBill == 3) {
- return Number(item.roundTwoDecimalAmount)
- } else if (settingStore.getAdmissionBill == 4) {
- return Number(item.roundAmount)
- } else {
- return Number(formatNum(item.totalAmount))
- }
- } else if (rounding.value) {
- return Number(item.roundAmount)
- } else if (fixed2Num.value) {
- return Number(item.roundTwoDecimalAmount)
- } else {
- return Number(formatNum(item.totalAmount))
- }
- }
- /** 打开弹窗 */
- const open = async (id) => {
- dialogVisible.value = true
- unPayTotal.value = 0
- total.value = 0
- totalDecimal.value = 0
- longTermAmount.value = 0
- payTotal.value = 0
- const res = await getExpenseOrderDetail(id)
- tempResValue.value = res
- // 后端响应新增 relatives 字段:家属信息列表
- relatives.value = Array.isArray(res.relatives) ? res.relatives : []
- res.items = res.items.filter(
- (item) =>
- (item.expenseSource == 'long_term_care_insurance' ||
- item.payStatus == 0 ||
- item.payStatus == 1 ||
- item.payStatus == 2) &&
- item.isShow == 1
- )
- let flag = false
- let hasPartialPay = false
- dataForm.value = res
- res.items.map((item) => {
- if (item.expenseSource == 'long_term_care_insurance') {
- longTermAmount.value += formatLongCareAmount(item.actualPrice)
- item.payStatus = 0
- flag = true
- // 遍历长护险
- res.expenseSubsidyDO.map((d) => {
- if (Math.abs(d.id) == Math.abs(item.sourceExpenseItemId || 0)) {
- item.createdTime = d.month
- item.description = d.remarks
- if (!item.itemCategoryName) {
- item.itemCategoryName = '长护险'
- }
- }
- })
- }
- if (hasPartialPay) {
- total.value +=
- hasSourceRule(item) ? getItemPreviewAmount(item) : item.roundAmount
- totalDecimal.value +=
- hasSourceRule(item)
- ? getItemPreviewAmount(item)
- : item.roundTwoDecimalAmount
- return
- }
- if (item.payStatus == 0) {
- unPayTotal.value += getItemPreviewAmount(item)
- } else if (item.payStatus == 1) {
- payTotal.value += getItemPreviewAmount(item)
- } else if (item.payStatus == 2) {
- hasPartialPay = true
- try {
- payTotal.value = tempResValue.value.payableAmount || 0
- unPayTotal.value = tempResValue.value.actualAmount - tempResValue.value.payableAmount || 0
- } catch (e) {}
- }
- total.value +=
- hasSourceRule(item) ? getItemPreviewAmount(item) : item.roundAmount
- totalDecimal.value +=
- hasSourceRule(item) ? getItemPreviewAmount(item) : item.roundTwoDecimalAmount
- })
- // 有长护险
- if (!flag) {
- if (res.expenseSubsidyDO && res.expenseSubsidyDO.length) {
- let num = 0
- res.expenseSubsidyDO.map((item) => {
- // 按长护险抹零配置取数
- const amount = formatLongCareAmount(item.amount)
- num += amount
- dataForm.value.items.push({
- expenseSource: 'long_term_care_insurance',
- itemCategoryName: '长护险',
- itemName: '长护险抵扣',
- description: item.remarks ? item.remarks : item.month + '长护险抵扣',
- roundAmount: -amount,
- roundTwoDecimalAmount: -amount,
- totalAmount: -amount,
- createdTime: item.month,
- payTime: '',
- payStatus: item.status
- })
- })
- // 去除长护险费用
- unPayTotal.value -= num
- longTermAmount.value = num
- payTotal.value -= res.num
- }
- }
- }
- defineExpose({ open }) // 提供 open 方法,用于打开弹窗
- const handleClosed = () => {
- unPayTotal.value = 0
- total.value = 0
- totalDecimal.value = 0
- longTermAmount.value = 0
- payTotal.value = 0
- relatives.value = []
- relativeDialogVisible.value = false
- dialogVisible.value = false
- }
- /** 家属信息 Dialog 关闭 */
- const handleRelativeClosed = () => {
- // 不动 relatives.value,只关弹窗;点开可继续看同一组家属
- }
- const tTitle = computed(() => {
- const arr = dataForm.value.billingMonth ? dataForm.value.billingMonth.split('-') : ''
- if (arr.length) {
- return `预收${arr[0]}年${arr[1]}月费用`
- }
- return ''
- })
- const bTitle = computed(() => {
- const arr = dataForm.value.billingMonth ? dataForm.value.billingMonth.split('-') : ''
- if (arr.length) {
- // 获取当月最后一天
- const date = new Date(new Date(Number(arr[0]), Number(arr[1]), 0)).getDate()
- return `${arr[0]}年${arr[1]}月01日-${arr[0]}年${arr[1]}月${date}日`
- }
- return ''
- })
- // 入院账单费用计算
- const caclAdminssionTotalPrice = computed(() => {
- let num = 0
- dataForm.value.items.map((item) => {
- num += getItemPreviewAmount(item)
- })
- return num + longTermAmount.value
- })
- // 总额
- const calcTotalPrice = (item) => {
- // 消费券/长护险优先按各自抹零规则取数
- if (item.expenseSource == 'consumer_voucher') {
- const ruleValue = settingStore.getCouponRuleValue
- if (ruleValue == 2 || ruleValue == '2') {
- return formatRound(item.totalAmount)
- }
- return formatNum(item.totalAmount)
- } else if (item.expenseSource == 'long_term_care_insurance') {
- return getLongCareAmountText(item)
- } else if (dataForm.value.type == '1' && item.isMonthlyExpense == 1) {
- if (settingStore.getAdmissionBill == 2) {
- // 向上取整
- return formatCeil(item.totalAmount)
- } else if (settingStore.getAdmissionBill == 3) {
- // 四舍五入保留两位
- return formatNum(item.roundTwoDecimalAmount)
- } else if (settingStore.getAdmissionBill == 4) {
- // 四舍五入保留整数
- return formatNum(item.roundAmount)
- } else {
- // 应收
- return formatNum(item.totalAmount)
- }
- } else if (rounding.value) {
- return formatRound(item.roundAmount)
- } else if (fixed2Num.value) {
- return formatNum(item.roundTwoDecimalAmount)
- } else {
- return formatNum(item.totalAmount)
- }
- }
- // 账单总额
- const billTotalPrice = computed(() => {
- if (dataForm.value.type == '1') {
- return formatNum(caclAdminssionTotalPrice.value)
- } else {
- if (rounding.value) {
- return formatNum(total.value)
- } else if (fixed2Num.value) {
- return formatNum(totalDecimal.value)
- } else {
- return formatNum(totalDecimal.value)
- }
- }
- })
- const formatStartDate = (item) => {
- if (item.itemName == '长护险护理补贴费用' || item.expenseSource == 'long_term_care_insurance') {
- // 长护险
- return item.createdTime
- } else if (item.expenseSource == 'expense_item') {
- if (item.description && item.description.includes('的')) {
- return item.description.split('的')[0]
- } else if (item.startDate != null && item.endDate != null) {
- return `${item.startDate}至${item.endDate}`
- } else if (
- item.startDate == null &&
- dataForm.value.description &&
- dataForm.value.description.length > 21
- ) {
- return dataForm.value.description.toString().substring(0, 21)
- } else {
- return bTitle
- }
- } else {
- return item.startDate + '至' + item.endDate
- }
- }
- const printExtraCss = `
- html, body { height: auto !important; overflow: visible !important; }
- #printTab1 { height: auto !important; overflow: visible !important; }
- table { page-break-inside: auto; width: 100%; border-collapse: collapse; }
- tr { page-break-inside: avoid; page-break-after: auto; }
- thead { display: table-header-group; }
- tfoot { display: table-footer-group; }
- /* 家属信息按钮:屏幕可见,但打印时必须隐藏 */
- .no-print { display: none !important; }
- /* 兜底:屏幕视图的家属信息 Dialog 不在 #printTab1 内,本身就不会进 iframe */
- @media print { .no-print { display: none !important; } }
- `
- // 打印
- const printTab = {
- id: 'printTab1',
- extraCss: '',
- popTitle: '',
- extraHead: `<style>${printExtraCss}</style>`,
- preview: false,
- previewTitle: tenantName + '对账单预览',
- previewPrintBtnLabel: '预览结束,开始打印',
- zIndex: 20002,
- previewBeforeOpenCallback() {
- console.log('正在加载预览窗口!')
- },
- previewOpenCallback() {
- console.log('已经加载完预览窗口,预览打开了!')
- },
- beforeOpenCallback() {
- console.log('开始打印之前!')
- },
- openCallback() {
- console.log('执行打印了!')
- },
- closeCallback() {
- console.log('关闭了打印工具!')
- },
- clickMounted() {
- console.log('点击v-print绑定的按钮了!')
- },
- standard: ''
- }
- </script>
- <style lang="scss" scoped>
- .first {
- .header {
- position: relative; // 让右上角 .relative-action 按钮可绝对定位
- .title {
- font-size: 20px;
- font-weight: bold;
- text-align: center;
- }
- // 右上角"家属信息"按钮:屏幕可见 / 打印隐藏
- .relative-action {
- position: absolute;
- right: 0;
- top: 4px;
- }
- .info-wrap {
- position: relative;
- padding: 5px;
- span {
- display: inline-block;
- margin-bottom: 10px;
- width: 33%;
- }
- .right {
- text-align: right;
- }
- }
- }
- .content {
- .info {
- text-align: center;
- }
- .flex {
- display: flex;
- justify-content: space-between;
- margin-bottom: 10px;
- }
- }
- }
- </style>
|