Commit 092fad97 authored by yanglilong's avatar yanglilong

fix(custom): 修改回款管理账单列表虚拟滚动

parent 4b18951c
<template>
<div ref="burt" class="white_bg burt-container">
<Goback ref="goback" title="回款详情" />
<a-tabs v-model="activeKey" @change="paneChange">
<a-tab-pane v-for="pane in panes" :key="pane.key" :tab="pane.title">
<a-form-model ref="ruleForm" :model="form" :rules="rules">
<a-row :gutter="30">
<template v-if="activeKey === '0'">
<a-col :lg="7" :sm="12">
<a-form-model-item label="保险公司" prop="payorCode">
<a-select
v-model="form.payorCode"
placeholder="请选择保险公司"
allow-clear
show-search
:disabled="!isEdit"
style="min-width: 200px; max-width: 250px"
@change="changePayor"
:filterOption="filterCode"
>
<a-select-option v-for="item in companyOptions" :key="item.payorCode" :value="item.payorCode">
{{ item.longName }}
</a-select-option>
</a-select>
</a-form-model-item>
</a-col>
<a-col :lg="5" :sm="12">
<a-form-model-item label="回款日期" prop="backDate">
<a-date-picker
format="YYYY-MM-DD"
format-value="YYYY-MM-DD 00:00:00"
v-model="form.backDate"
placeholder="选择日期"
allow-clear
:disabled="!isEdit"
/>
</a-form-model-item>
</a-col>
<a-col :lg="6" :sm="12">
<a-form-model-item label="回款金额(人民币)" prop="backAmountCny">
<a-input
class="fixed_width"
type="number"
v-model="form.backAmountCny"
placeholder="请输入金额"
allow-clear
:disabled="!isEdit"
/>
</a-form-model-item>
</a-col>
<a-col :lg="5" :sm="12">
<a-form-model-item label="可核销余额">
<a-input class="fixed_width" v-model="residueBackAmount" disabled />
</a-form-model-item>
</a-col>
<a-col :lg="7" :sm="12">
<a-form-model-item label="回款金额(美元)">
<a-input
class="fixed_width"
type="number"
v-model="form.backAmountUsd"
placeholder="请输入金额"
allow-clear
:disabled="!isEdit"
/>
</a-form-model-item>
</a-col>
<a-col :lg="5" :sm="12">
<a-form-model-item label="汇率差">
<a-input v-model="form.backExchangeRate" placeholder="请输入金额" allow-clear :disabled="!isEdit" />
</a-form-model-item>
</a-col>
<a-col :lg="6" :sm="12">
<a-form-model-item label="EOB编号">
<a-input v-model="form.eobNos" placeholder="请输入EOB编号" allow-clear :disabled="!isEdit" />
</a-form-model-item>
</a-col>
<a-col :lg="5" :sm="12">
<a-form-model-item label="EOB备注">
<a-input v-model="form.eobRemark" placeholder="请输入EOB备注" allow-clear :disabled="!isEdit" />
</a-form-model-item>
</a-col>
<a-col :lg="5" :sm="12">
<a-form-model-item label="上传附件">
<a-upload
name="file"
:multiple="false"
:showUploadList="true"
:disabled="!isEdit"
:fileList="fileList"
:customRequest="(file) => uploadFile(file)"
:beforeUpload="() => beforeUpload()"
:remove="(file) => removeFile(file)"
>
<a-button type="primary"> <Icon name="ssiupload" :size="18" />上传文件 </a-button>
</a-upload>
</a-form-model-item>
</a-col>
</template>
<template v-else>
<a-col :lg="6" :sm="12">
<a-form-model-item label="保险公司" prop="payorCode">
<a-select
v-model="form.payorCode"
placeholder="请选择保险公司"
allow-clear
show-search
:disabled="!isEdit"
style="min-width: 200px"
@change="changePayor"
:filterOption="filterCode"
>
<a-select-option v-for="item in companyOptions" :key="item.payorCode" :value="item.payorCode">
{{ item.longName }}
</a-select-option>
</a-select>
</a-form-model-item>
</a-col>
<a-col :lg="6" :sm="12">
<a-form-model-item label="回款金额(人民币)" prop="backAmountCny">
<a-input
class="fixed_width"
type="number"
v-model="form.backAmountCny"
placeholder="请输入金额"
allow-clear
:disabled="!isEdit"
/>
</a-form-model-item>
</a-col>
<a-col :lg="6" :sm="12">
<a-form-model-item label="本次账单回款金额合计">
<!-- <div class="blue-text">{{ ciReceiptTotalVo.backAmountTotal || 0 }}</div> -->
<div class="blue-text">{{ accuracy.subtract(form.backAmountCny, residueBackAmount) || 0 }}</div>
</a-form-model-item>
</a-col>
<a-col :lg="6" :sm="12">
<a-form-model-item label="可核销余额">
<a-input class="fixed_width" v-model="residueBackAmount" disabled />
</a-form-model-item>
</a-col>
</template>
</a-row>
</a-form-model>
<template v-if="activeKey === '1'">
<div class="bill-content">
<div class="checked-count">
<a-button type="primary" size="small" @click="selectAllList">全选</a-button>
已勾选账单:<span class="blue-text">{{ selectedRowKeys.length || 0 }}</span>
<span v-if="selectedRows.filter(v=> v.status == 2).length != 0">,其中:无效 <span style="color: red;">{{ selectedRows.filter(v=> v.status == 2).length || 0 }}</span></span>
</div>
<a-tabs type="card" v-model="activeKey1">
<a-tab-pane v-for="pane in panes1" :key="pane.key" :tab="pane.title">
<div>
<a-row type="flex" align="middle" class="search-form">
<a-form-model
ref="searchForm"
layout="inline"
:labelCol="{ span: 8 }"
:wrapperCol="{ span: 16 }"
:model="searchForm"
>
<a-row>
<a-col :lg="4" :sm="12">
<a-form-model-item label="账单日期">
<a-range-picker
format="YYYY-MM-DD"
value-format="YYYY-MM-DD"
v-model="searchForm.billDate"
:placeholder="['开始时间', '结束时间']"
/>
</a-form-model-item>
</a-col>
<a-col :lg="4" :sm="12">
<a-form-model-item label="病历号">
<a-input
v-model="searchForm.mrnNo"
placeholder="请输入病历号"
allow-clear
:disabled="!isEdit"
/>
</a-form-model-item>
</a-col>
<a-col :lg="4" :sm="12">
<a-form-model-item label="客户名称">
<a-input
v-model="searchForm.patientName"
placeholder="请输入客户名称"
allow-clear
:disabled="!isEdit"
/>
</a-form-model-item>
</a-col>
<a-col :lg="4" :sm="12">
<a-form-model-item label="状态" :labelCol="{ span: 7 }" :wrapperCol="{ span: 14 }">
<a-select v-model="searchForm.rStatus" placeholder="请选择状态" allowClear>
<a-select-option v-for="item in statusOptions" :key="item.code" :value="item.code">
{{ item.name }}
</a-select-option>
</a-select>
</a-form-model-item>
</a-col>
<a-col :lg="4" :sm="12">
<a-form-model-item label="账单编号">
<a-input
v-model="searchForm.receiptNo"
placeholder="请输入账单编号"
allow-clear
:disabled="!isEdit"
/>
</a-form-model-item>
</a-col>
<a-col class="flex-col" :lg="4" :sm="12">
<div>
<a-button type="primary" @click="searchData">
<Icon name="ssisearch_active" :size="14" />查询
</a-button>
</div>
<div v-if="activeKey1 === '0' && selectedRows.length > 0">
<a-button type="primary" @click="exportExcel">
<Icon name="ssidaochu" :size="14" />导出
</a-button>
</div>
</a-col>
</a-row>
</a-form-model>
</a-row>
</div>
<!-- 已关联账单 -->
<template v-if="activeKey1 === '0'">
<template v-if="selectedRows.length > 0">
<div class="all-list_box">
<a-table
class="table-content"
:rowClassName="rowClassName"
:columns="selectedColumns"
:data-source="selectedRows"
:rowKey="'id'"
:scroll="{ x: '100%', y: tableHeight }"
:pagination="false"
>
<template slot="status" slot-scope="text">
<span :style="{ color: text == 2 ? 'red' : '' }">{{
text == 1 ? '有效' : text == 2 ? '无效' : ''
}}</span>
</template>
<template slot="patientName" slot-scope="text">
<a-tooltip placement="top">
<template slot="title">
<span>{{ text }}</span>
</template>
<span class="ellipsis_">{{ text }}</span>
</a-tooltip>
</template>
<template slot="operation" slot-scope="text, record, index">
<a-button type="link" class="danger" @click.stop="delRecord(record, index)">删除</a-button>
</template>
</a-table>
</div>
</template>
<div class="all-list_box no-data" v-else><a-empty :image="simpleImage" /></div>
</template>
<template v-else>
<!-- table -->
<template v-if="isEdit">
<div class="all-list_box">
<a-table
class="table-content all-list"
:columns="columns"
:data-source="dataList"
:scroll="{ x: '100%', y: tableHeight1 }"
:pagination="false"
:rowKey="'id'"
:row-selection="{
selectedRowKeys: selectedRowKeys,
onSelect: onSelectChange,
onSelectAll: onSelectAll
}"
>
<template slot="status" slot-scope="text">
<span :style="{ color: text == 2 ? 'red' : '' }">{{
text == 1 ? '有效' : text == 2 ? '无效' : ''
}}</span>
</template>
<template slot="patientName" slot-scope="text">
<a-tooltip placement="top">
<template slot="title">
<span>{{ text }}</span>
</template>
<span class="ellipsis_">{{ text }}</span>
</a-tooltip>
</template>
</a-table>
<BurtPagination class="pagination" :pagination="pagination" @pageChange="pageChange" />
</div>
</template>
</template>
</a-tab-pane>
</a-tabs>
</div>
</template>
</a-tab-pane>
<div v-if="isEdit" slot="tabBarExtraContent">
<a-button class="mar-left10" type="primary" @click="addNewEvt(0)">
<Icon :name="backMoneyNo ? 'ssibaocun' : 'ssiadd'" :size="14" />暂存
</a-button>
<a-button class="mar-left10" type="primary" @click="addNewEvt(1)">
<Icon :name="backMoneyNo ? 'ssibaocun' : 'ssiadd'" :size="14" />结案
</a-button>
</div>
</a-tabs>
</div>
<div ref="burt" class="white_bg burt-container">
<Goback ref="goback" title="回款详情" />
<a-tabs v-model="activeKey" @change="paneChange">
<a-tab-pane v-for="pane in panes" :key="pane.key" :tab="pane.title">
<a-form-model ref="ruleForm" :model="form" :rules="rules">
<a-row :gutter="30">
<template v-if="activeKey === '0'">
<a-col :lg="7" :sm="12">
<a-form-model-item label="保险公司" prop="payorCode">
<a-select
v-model="form.payorCode"
placeholder="请选择保险公司"
allow-clear
show-search
:disabled="!isEdit"
style="min-width: 200px; max-width: 250px"
@change="changePayor"
:filterOption="filterCode"
>
<a-select-option
v-for="item in companyOptions"
:key="item.payorCode"
:value="item.payorCode"
>
{{ item.longName }}
</a-select-option>
</a-select>
</a-form-model-item>
</a-col>
<a-col :lg="5" :sm="12">
<a-form-model-item label="回款日期" prop="backDate">
<a-date-picker
format="YYYY-MM-DD"
format-value="YYYY-MM-DD 00:00:00"
v-model="form.backDate"
placeholder="选择日期"
allow-clear
:disabled="!isEdit"
/>
</a-form-model-item>
</a-col>
<a-col :lg="6" :sm="12">
<a-form-model-item
label="回款金额(人民币)"
prop="backAmountCny"
>
<a-input
class="fixed_width"
type="number"
v-model="form.backAmountCny"
placeholder="请输入金额"
allow-clear
:disabled="!isEdit"
/>
</a-form-model-item>
</a-col>
<a-col :lg="5" :sm="12">
<a-form-model-item label="可核销余额">
<a-input
class="fixed_width"
v-model="residueBackAmount"
disabled
/>
</a-form-model-item>
</a-col>
<a-col :lg="7" :sm="12">
<a-form-model-item label="回款金额(美元)">
<a-input
class="fixed_width"
type="number"
v-model="form.backAmountUsd"
placeholder="请输入金额"
allow-clear
:disabled="!isEdit"
/>
</a-form-model-item>
</a-col>
<a-col :lg="5" :sm="12">
<a-form-model-item label="汇率差">
<a-input
v-model="form.backExchangeRate"
placeholder="请输入金额"
allow-clear
:disabled="!isEdit"
/>
</a-form-model-item>
</a-col>
<a-col :lg="6" :sm="12">
<a-form-model-item label="EOB编号">
<a-input
v-model="form.eobNos"
placeholder="请输入EOB编号"
allow-clear
:disabled="!isEdit"
/>
</a-form-model-item>
</a-col>
<a-col :lg="5" :sm="12">
<a-form-model-item label="EOB备注">
<a-input
v-model="form.eobRemark"
placeholder="请输入EOB备注"
allow-clear
:disabled="!isEdit"
/>
</a-form-model-item>
</a-col>
<a-col :lg="5" :sm="12">
<a-form-model-item label="上传附件">
<a-upload
name="file"
:multiple="false"
:showUploadList="true"
:disabled="!isEdit"
:fileList="fileList"
:customRequest="(file) => uploadFile(file)"
:beforeUpload="() => beforeUpload()"
:remove="(file) => removeFile(file)"
>
<a-button type="primary">
<Icon name="ssiupload" :size="18" />上传文件
</a-button>
</a-upload>
</a-form-model-item>
</a-col>
</template>
<template v-else>
<a-col :lg="6" :sm="12">
<a-form-model-item label="保险公司" prop="payorCode">
<a-select
v-model="form.payorCode"
placeholder="请选择保险公司"
allow-clear
show-search
:disabled="!isEdit"
style="min-width: 200px"
@change="changePayor"
:filterOption="filterCode"
>
<a-select-option
v-for="item in companyOptions"
:key="item.payorCode"
:value="item.payorCode"
>
{{ item.longName }}
</a-select-option>
</a-select>
</a-form-model-item>
</a-col>
<a-col :lg="6" :sm="12">
<a-form-model-item
label="回款金额(人民币)"
prop="backAmountCny"
>
<a-input
class="fixed_width"
type="number"
v-model="form.backAmountCny"
placeholder="请输入金额"
allow-clear
:disabled="!isEdit"
/>
</a-form-model-item>
</a-col>
<a-col :lg="6" :sm="12">
<a-form-model-item label="本次账单回款金额合计">
<!-- <div class="blue-text">{{ ciReceiptTotalVo.backAmountTotal || 0 }}</div> -->
<div class="blue-text">
{{
accuracy.subtract(
form.backAmountCny,
residueBackAmount
) || 0
}}
</div>
</a-form-model-item>
</a-col>
<a-col :lg="6" :sm="12">
<a-form-model-item label="可核销余额">
<a-input
class="fixed_width"
v-model="residueBackAmount"
disabled
/>
</a-form-model-item>
</a-col>
</template>
</a-row>
</a-form-model>
<template v-if="activeKey === '1'">
<div class="bill-content">
<div class="checked-count">
<a-button type="primary" size="small" @click="selectAllList"
>全选</a-button
>
已勾选账单:<span class="blue-text">{{
selectedRowKeys.length || 0
}}</span>
<span v-if="selectedRows.filter((v) => v.status == 2).length != 0"
>,其中:无效
<span style="color: red">{{
selectedRows.filter((v) => v.status == 2).length || 0
}}</span>
</span
>
</div>
<a-tabs type="card" v-model="activeKey1">
<a-tab-pane
v-for="pane in panes1"
:key="pane.key"
:tab="pane.title"
>
<div>
<a-row type="flex" align="middle" class="search-form">
<a-form-model
ref="searchForm"
layout="inline"
:labelCol="{ span: 8 }"
:wrapperCol="{ span: 16 }"
:model="searchForm"
>
<a-row>
<a-col :lg="4" :sm="12">
<a-form-model-item label="账单日期">
<a-range-picker
format="YYYY-MM-DD"
value-format="YYYY-MM-DD"
v-model="searchForm.billDate"
:placeholder="['开始时间', '结束时间']"
/>
</a-form-model-item>
</a-col>
<a-col :lg="4" :sm="12">
<a-form-model-item label="病历号">
<a-input
v-model="searchForm.mrnNo"
placeholder="请输入病历号"
allow-clear
:disabled="!isEdit"
/>
</a-form-model-item>
</a-col>
<a-col :lg="4" :sm="12">
<a-form-model-item label="客户名称">
<a-input
v-model="searchForm.patientName"
placeholder="请输入客户名称"
allow-clear
:disabled="!isEdit"
/>
</a-form-model-item>
</a-col>
<a-col :lg="4" :sm="12">
<a-form-model-item
label="状态"
:labelCol="{ span: 7 }"
:wrapperCol="{ span: 14 }"
>
<a-select
v-model="searchForm.rStatus"
placeholder="请选择状态"
allowClear
>
<a-select-option
v-for="item in statusOptions"
:key="item.code"
:value="item.code"
>
{{ item.name }}
</a-select-option>
</a-select>
</a-form-model-item>
</a-col>
<a-col :lg="4" :sm="12">
<a-form-model-item label="账单编号">
<a-input
v-model="searchForm.receiptNo"
placeholder="请输入账单编号"
allow-clear
:disabled="!isEdit"
/>
</a-form-model-item>
</a-col>
<a-col class="flex-col" :lg="4" :sm="12">
<div>
<a-button type="primary" @click="searchData">
<Icon name="ssisearch_active" :size="14" />查询
</a-button>
</div>
<div
v-if="activeKey1 === '0' && selectedRows.length > 0"
>
<a-button type="primary" @click="exportExcel">
<Icon name="ssidaochu" :size="14" />导出
</a-button>
</div>
</a-col>
</a-row>
</a-form-model>
</a-row>
</div>
<!-- 已关联账单 -->
<template v-if="activeKey1 === '0'">
<template v-if="selectedRows.length > 0">
<div class="all-list_box">
<a-virtual-table
class="table-content"
:rowClassName="rowClassName"
:columns="selectedColumns"
:data-source="selectedRows"
:itemSize="46"
keyProp="id"
row-key="id"
:pagination="false"
bordered
:scroll="{ x: 1300, y: tableHeight }"
>
<!-- 账单状态 -->
<template slot="status" slot-scope="{ text }">
<span
:style="{
color: text == 2 ? 'red' : ''
}"
>{{
text == 1 ? '有效' : text == 2 ? '无效' : ''
}}</span
>
</template>
<!-- 客户姓名 -->
<template slot="patientName" slot-scope="{ text }">
<a-tooltip placement="top">
<template slot="title">
<span>{{ text }}</span>
</template>
<span class="ellipsis_">{{ text }}</span>
</a-tooltip>
</template>
</a-virtual-table>
<!-- s -->
</div>
</template>
<div class="all-list_box no-data" v-else>
<a-empty :image="simpleImage" />
</div>
</template>
<template v-else>
<!-- table -->
<template v-if="isEdit">
<div class="all-list_box">
<a-table
class="table-content all-list"
:columns="columns"
:data-source="dataList"
:scroll="{ x: '100%', y: tableHeight1 }"
:pagination="false"
:rowKey="'id'"
:row-selection="{
selectedRowKeys: selectedRowKeys,
onSelect: onSelectChange,
onSelectAll: onSelectAll
}"
>
<template slot="status" slot-scope="text">
<span :style="{ color: text == 2 ? 'red' : '' }">{{
text == 1 ? '有效' : text == 2 ? '无效' : ''
}}</span>
</template>
<template slot="patientName" slot-scope="text">
<a-tooltip placement="top">
<template slot="title">
<span>{{ text }}</span>
</template>
<span class="ellipsis_">{{ text }}</span>
</a-tooltip>
</template>
</a-table>
<BurtPagination
class="pagination"
:pagination="pagination"
@pageChange="pageChange"
/>
</div>
</template>
</template>
</a-tab-pane>
</a-tabs>
</div>
</template>
</a-tab-pane>
<div v-if="isEdit" slot="tabBarExtraContent">
<a-button class="mar-left10" type="primary" @click="addNewEvt(0)">
<Icon :name="backMoneyNo ? 'ssibaocun' : 'ssiadd'" :size="14" />暂存
</a-button>
<a-button class="mar-left10" type="primary" @click="addNewEvt(1)">
<Icon :name="backMoneyNo ? 'ssibaocun' : 'ssiadd'" :size="14" />结案
</a-button>
</div>
</a-tabs>
</div>
</template>
<script>
import { Empty } from 'ant-design-vue';
import Goback from '@/components/Customers/goback';
import BurtPagination from '@/components/Customers/pagation';
import { eobStatusOptions } from '@/assets/js/utilsdictOptions.js';
import { exportFile, accuracy } from '@/utils/index';
import moment from 'moment';
import mixins from '@/mixins';
import { Empty } from 'ant-design-vue'
import Goback from '@/components/Customers/goback'
import BurtPagination from '@/components/Customers/pagation'
import { eobStatusOptions } from '@/assets/js/utilsdictOptions.js'
import { exportFile, accuracy } from '@/utils/index'
import moment from 'moment'
import mixins from '@/mixins'
const panes = [
{ title: '基础信息', key: '0', show: true, content: 'PaymentClaims' },
{ title: '账单列表', key: '1', show: false, content: 'Insurance' }
];
{ title: '基础信息', key: '0', show: true, content: 'PaymentClaims' },
{ title: '账单列表', key: '1', show: false, content: 'Insurance' }
]
import AVirtualTable from './components/a-virtual-table/Index.vue'
export default {
data() {
return {
accuracy,
isEdit: false,
eobStatusOptions,
dialogShow: false,
ciReceiptTotalVo:{},
form: {
payorCode: undefined,
backDate: null,
backAmountCny: '',
backAmountUsd: '',
backExchangeRate: '',
eobNos: '', // EOB编号
eobRemark: '' // EOB备注
},
fileList: [], // 上传文件列表
dataList: [],
isEditNewEOB: false, //是否在新建回款
companyOptions: [], //保险公司
pagination: {
pageNum: 1,
pageSize: 5,
total: 0
},
selectedRowKeys: [], // Check here to configure the default column
selectedRows: [], // Check here to configure the default column
backMoneyNo: '',
rules: {
payorCode: [{ required: true, message: '请选择保险公司', trigger: 'change' }],
backDate: [{ required: true, message: '请选择回款日期', trigger: 'change' }],
backAmountCny: [{ required: true, message: '请输入回款金额(人民币)', trigger: ['change', 'blur'] }]
},
data() {
return {
accuracy,
crollHeight: 0,
isEdit: false,
eobStatusOptions,
dialogShow: false,
ciReceiptTotalVo: {},
form: {
payorCode: undefined,
backDate: null,
backAmountCny: '',
backAmountUsd: '',
backExchangeRate: '',
eobNos: '', // EOB编号
eobRemark: '' // EOB备注
},
fileList: [], // 上传文件列表
dataList: [],
isEditNewEOB: false, //是否在新建回款
companyOptions: [], //保险公司
pagination: {
pageNum: 1,
pageSize: 5,
total: 0
},
selectedRowKeys: [], // Check here to configure the default column
selectedRows: [], // Check here to configure the default column
backMoneyNo: '',
rules: {
payorCode: [
{ required: true, message: '请选择保险公司', trigger: 'change' }
],
backDate: [
{ required: true, message: '请选择回款日期', trigger: 'change' }
],
backAmountCny: [
{
required: true,
message: '请输入回款金额(人民币)',
trigger: ['change', 'blur']
}
]
},
searchForm: {
billDate: [],
mrnNo: '', // 病历号
patientName: '' // 客户名字
},
savedStatus: false, //是否已保存
relatedList: [],
searchForm: {
billDate: [],
mrnNo: '', // 病历号
patientName: '' // 客户名字
},
savedStatus: false, //是否已保存
relatedList: [],
panes: Object.seal(panes),
activeKey: '0',
activeKey1: '0',
statusOptions: [
{
name: '无效',
code: 2
},
{
name: '有效',
code: 1
}
],
tableHeight: 200, // 已关联账单表格高度
tableHeight1: 200 // 全部账单表格高度
};
},
mixins: [mixins],
components: {
Goback,
BurtPagination
},
computed: {
panes1() {
const panes = [{ title: '已关联账单', key: '0', show: true, content: 'Associated' }];
if (this.isEdit) {
panes.push({ title: '全部账单', key: '1', show: false, content: 'Insurance' });
}
return panes;
},
columns() {
const base = [
{
title: '账单日期',
dataIndex: 'receiptDate',
ellipsis: true,
width: 160,
fixed: 'left'
},
{
title: '账单状态',
dataIndex: 'status',
ellipsis: true,
width: 100,
fixed: 'left',
scopedSlots: { customRender: 'status' }
},
{
title: '客户姓名',
dataIndex: 'patientName',
ellipsis: true,
fixed: 'left',
width: 180,
scopedSlots: { customRender: 'patientName' }
},
{ title: '病历号', dataIndex: 'mrnNo', ellipsis: true, width: 195 },
{ title: '保险公司', dataIndex: 'payorName', ellipsis: true, width: 195 },
{
title: '账单编号',
dataIndex: 'receiptNo',
ellipsis: true,
width: 150
},
{
title: '理赔账单金额',
dataIndex: 'actualAmount',
ellipsis: true,
width: 150
},
{
title: '回款金额',
dataIndex: 'writeOffAmount',
ellipsis: true,
width: 150
},
{
title: '未清余额',
dataIndex: 'residueBackAmount',
ellipsis: true,
width: 150
},
{
title: '个人欠费',
dataIndex: 'arrearsAmountShow',
ellipsis: true,
width: 150,
customRender: (val) => {
return <span style="color: red;">{val}</span>;
}
},
{
title: '备注',
dataIndex: 'remark',
ellipsis: true,
width: 200,
customRender: (val) => {
return <span style="color: red;">{val}</span>;
}
},
{
title: '收银',
dataIndex: 'receiptTellerName',
ellipsis: true,
width: 120
}
];
return base;
},
selectedColumns() {
const base = JSON.parse(JSON.stringify(this.columns));
const changeAmount = this.changeAmount;
base[7] = {
title: '回款金额',
dataIndex: 'backAmount',
ellipsis: true,
width: 150,
customRender: (val, row) => {
return (
<a-input-number
v-model={row.backAmount}
allow-clear
disabled={!this.isEdit}
style={{ color: row.backAmount == row.currentReceiptAmount ? '' : 'red' }}
onBlur={() => {
changeAmount(row, 'backAmount');
}}
/>
);
}
};
base[10].customRender = (val, row) => {
return (
<div>
{this.isEdit ? (
<a-popover title="备注" trigger="click">
<template slot="content">
<a-textarea
class="remark_inp red_inp"
v-model={row.remark}
auto-size={{ minRows: 3, maxRows: 5 }}
allow-clear
disabled={!this.isEdit}
></a-textarea>
</template>
<a-tooltip>
<template slot="title">{row.remark}</template>
<a-input class="red_inp" v-model={row.remark} allow-clear disabled={!this.isEdit} />
</a-tooltip>
</a-popover>
) : (
<a-tooltip>
<template slot="title">{row.remark}</template>
<a-input class="red_inp" v-model={row.remark} disabled />
</a-tooltip>
)}
</div>
);
};
base[8] = {
title: '未清余额',
dataIndex: 'residueBackAmount',
ellipsis: true,
width: 150,
customRender: (val, row) => {
const residueBackAmount =
Number(row.currentReceiptAmount || 0) - Number(row.backAmount || 0) - Number(row.arrearsAmount || 0);
return Number(residueBackAmount.toFixed(2));
}
};
base[9] = {
title: '个人欠费',
dataIndex: 'arrearsAmount',
ellipsis: true,
width: 150,
customRender: (val, row) => {
return (
<a-input-number
class="red_inp"
v-model={row.arrearsAmount}
allow-clear
disabled={!this.isEdit}
onBlur={() => {
changeAmount(row, 'arrearsAmount');
}}
/>
);
}
};
base.splice(7, 0, {
title: '余末金额',
dataIndex: 'currentReceiptAmount',
ellipsis: true,
width: 150
});
base.push({
title: '回款日期',
dataIndex: 'backDate',
ellipsis: true,
width: 150
});
if (this.isEdit) {
base.push({
title: '操作',
dataIndex: 'operation',
fixed: 'right',
width: 100,
scopedSlots: { customRender: 'operation' }
});
}
return base;
},
// 可核销余额
residueBackAmount() {
let totalMoney = Number(this.form.backAmountCny || 0);
this.selectedRows.forEach((item) => {
totalMoney -= Number(item.backAmount);
});
return Number(totalMoney.toFixed(2));
}
},
watch: {
activeKey1() {
this.searchForm = {
billDate: [],
mrnNo: '', // 病历号
patientName: '' // 客户名字
};
}
},
created() {
this.simpleImage = Empty.PRESENTED_IMAGE_SIMPLE;
const { backMoneyNo, isEdit } = this.$route.query;
this.backMoneyNo = backMoneyNo;
this.isEdit = isEdit;
this._getCompanyOptions();
if (backMoneyNo) {
const backMoneyDataDetail = JSON.parse(localStorage.getItem('backMoneyDataDetail') || '{}');
this.form = backMoneyDataDetail;
this.form.backDate = this.form.backDate ? moment(this.form.backDate).format('YYYY-MM-DD 00:00:00') : null;
panes: Object.seal(panes),
activeKey: '0',
activeKey1: '0',
statusOptions: [
{
name: '无效',
code: 2
},
{
name: '有效',
code: 1
}
],
tableHeight1: 200 // 全部账单表格高度
}
},
mixins: [mixins],
components: {
Goback,
BurtPagination,
AVirtualTable
},
computed: {
panes1() {
const panes = [
{ title: '已关联账单', key: '0', show: true, content: 'Associated' }
]
if (this.isEdit) {
panes.push({
title: '全部账单',
key: '1',
show: false,
content: 'Insurance'
})
}
return panes
},
columns() {
const base = [
{
title: '账单日期',
dataIndex: 'receiptDate',
ellipsis: true,
width: 160,
fixed: 'left'
},
{
title: '账单状态',
dataIndex: 'status',
ellipsis: true,
width: 100,
fixed: 'left',
scopedSlots: { customRender: 'status' }
},
{
title: '客户姓名',
dataIndex: 'patientName',
ellipsis: true,
fixed: 'left',
width: 180,
scopedSlots: { customRender: 'patientName' }
},
{ title: '病历号', dataIndex: 'mrnNo', ellipsis: true, width: 195 },
{
title: '保险公司',
dataIndex: 'payorName',
ellipsis: true,
width: 195
},
{
title: '账单编号',
dataIndex: 'receiptNo',
ellipsis: true,
width: 150
},
{
title: '理赔账单金额',
dataIndex: 'actualAmount',
ellipsis: true,
width: 150
},
{
title: '回款金额',
dataIndex: 'writeOffAmount',
ellipsis: true,
width: 150
},
{
title: '未清余额',
dataIndex: 'residueBackAmount',
ellipsis: true,
width: 150
},
{
title: '个人欠费',
dataIndex: 'arrearsAmountShow',
ellipsis: true,
width: 150,
customRender: (val) => {
return <span style="color: red;">{val}</span>
}
},
{
title: '备注',
dataIndex: 'remark',
ellipsis: true,
width: 200,
customRender: (val) => {
return <span style="color: red;">{val}</span>
}
},
{
title: '收银',
dataIndex: 'receiptTellerName',
ellipsis: true,
width: 120
}
]
return base
},
selectedColumns() {
const base = JSON.parse(JSON.stringify(this.columns))
const changeAmount = this.changeAmount
const delRecord = this.delRecord
// base[1] = {
// title: '账单状态',
// dataIndex: 'status',
// ellipsis: true,
// width: 100,
// fixed: 'left',
// customRender: (val, row) => {
// return (
// <span
// style={{
// color: row.staus == 2 ? 'red' : ''
// }}
// >
// {row.staus == 1 ? '有效' : row.staus == 2 ? '无效' : ''}
// </span>
// )
// }
// }
base[7] = {
title: '回款金额',
dataIndex: 'backAmount',
ellipsis: true,
width: 150,
customRender: (val, row) => {
return (
<a-input-number
v-model={row.backAmount}
allow-clear
disabled={!this.isEdit}
style={{
color: row.backAmount == row.currentReceiptAmount ? '' : 'red'
}}
onBlur={() => {
changeAmount(row, 'backAmount')
}}
/>
)
}
}
base[10].customRender = (val, row) => {
return (
<div>
{this.isEdit ? (
<a-popover title="备注" trigger="click">
<template slot="content">
<a-textarea
class="remark_inp red_inp"
v-model={row.remark}
auto-size={{ minRows: 3, maxRows: 5 }}
allow-clear
disabled={!this.isEdit}
></a-textarea>
</template>
<a-tooltip>
<template slot="title">{row.remark}</template>
<a-input
class="red_inp"
v-model={row.remark}
allow-clear
disabled={!this.isEdit}
/>
</a-tooltip>
</a-popover>
) : (
<a-tooltip>
<template slot="title">{row.remark}</template>
<a-input class="red_inp" v-model={row.remark} disabled />
</a-tooltip>
)}
</div>
)
}
base[8] = {
title: '未清余额',
dataIndex: 'residueBackAmount',
ellipsis: true,
width: 150,
customRender: (val, row) => {
const residueBackAmount =
Number(row.currentReceiptAmount || 0) -
Number(row.backAmount || 0) -
Number(row.arrearsAmount || 0)
return Number(residueBackAmount.toFixed(2))
}
}
base[9] = {
title: '个人欠费',
dataIndex: 'arrearsAmount',
ellipsis: true,
width: 150,
customRender: (val, row) => {
return (
<a-input-number
class="red_inp"
v-model={row.arrearsAmount}
allow-clear
disabled={!this.isEdit}
onBlur={() => {
changeAmount(row, 'arrearsAmount')
}}
/>
)
}
}
base.splice(7, 0, {
title: '余末金额',
dataIndex: 'currentReceiptAmount',
ellipsis: true,
width: 150
})
base.push({
title: '回款日期',
dataIndex: 'backDate',
ellipsis: true,
width: 150
})
if (this.isEdit) {
base.push({
title: '操作',
dataIndex: 'operation',
fixed: 'right',
width: 100,
scopedSlots: { customRender: 'operation' },
customRender: (val, row, index) => {
return (
<a-button
type="link"
class="danger"
onClick={() => {
delRecord(row, index)
}}
>
删除
</a-button>
)
}
})
}
return base
},
// 可核销余额
residueBackAmount() {
let totalMoney = Number(this.form.backAmountCny || 0)
this.selectedRows.forEach((item) => {
totalMoney -= Number(item.backAmount)
})
return Number(totalMoney.toFixed(2))
}
},
watch: {
activeKey1() {
this.searchForm = {
billDate: [],
mrnNo: '', // 病历号
patientName: '' // 客户名字
}
}
},
created() {
this.simpleImage = Empty.PRESENTED_IMAGE_SIMPLE
const { backMoneyNo, isEdit } = this.$route.query
this.backMoneyNo = backMoneyNo
this.isEdit = isEdit
this._getCompanyOptions()
if (backMoneyNo) {
const backMoneyDataDetail = JSON.parse(
localStorage.getItem('backMoneyDataDetail') || '{}'
)
this.form = backMoneyDataDetail
this.form.backDate = this.form.backDate
? moment(this.form.backDate).format('YYYY-MM-DD 00:00:00')
: null
// 如果有上传附件则显示列表
if (backMoneyDataDetail.fileList) {
this.fileList = backMoneyDataDetail.fileList.map((d) => {
const file = {
uid: Math.random() * 10000,
name: d.fileName,
status: 'done',
url: d.fileUrl
};
return file;
});
}
this.getData();
}
},
mounted() {
this.calcTableHeight();
this._getNewEOBList();
},
methods: {
moment,
// 获取未清余额合计
getBackMoneyReportCount(params) {
this.$apis.queryBackReceiptCount({
...params,
...this.pagination
}).then((res) => {
if (res.returnCode == '0000') {
console.log(res.content)
this.ciReceiptTotalVo = res.content
}
});
},
// 计算表格最大高度
calcTableHeight() {
const dom = this.$refs.burt;
const containterH = dom.clientHeight;
const gobackH = document.querySelector('.back-container').clientHeight;
const style = window.getComputedStyle(dom, null);
const paddingT = parseFloat(style.getPropertyValue('padding-top'));
const paddingB = parseFloat(style.getPropertyValue('padding-bottom'));
const paddingSum = paddingT + paddingB;
this.tableHeight = containterH - 300 - gobackH - paddingSum;
this.tableHeight1 = containterH - 340 - gobackH - paddingSum;
// 设置每页展示条数
const pageSize = Math.floor((this.tableHeight1 - 10) / 32);
this.$set(this.pagination, 'pageSize', pageSize);
},
// 已关联账单表格行类名
rowClassName(record) {
return record.hidden ? 'hide_' : '';
},
// 账单查询
searchData() {
if (this.activeKey1 === '1') {
this._getNewEOBList();
} else {
this.getData();
}
},
// 选择框筛选
filterCode(input, option) {
return option.componentOptions.children[0].text.toLowerCase().indexOf(input.toLowerCase()) >= 0;
},
paneChange() {
this.$refs.ruleForm[0].clearValidate();
this.panes.forEach((item) => {
item.show = false;
});
this.panes[Number(this.activeKey)].show = true;
},
changeAmount(row) {
let totalMoney = Number(this.form.backAmountCny || 0);
this.selectedRows.forEach((item) => {
totalMoney -= Number(item.backAmount);
});
if (totalMoney < 0) {
this.$message.error('可核销余额不足');
}
if (Number(row.actualAmount || 0) - Number(row.backAmount || 0) < 0) {
this.$message.warning('录入账单回款金额大于账单金额');
}
},
onSelectChange(selectedRow, selected) {
selectedRow['backAmount'] =
this.residueBackAmount > selectedRow.currentReceiptAmount
? selectedRow.currentReceiptAmount
: this.residueBackAmount;
if (selected) {
this.selectedRowKeys.push(selectedRow.id);
this.selectedRows.push(selectedRow);
} else {
const index = this.selectedRowKeys.findIndex((item) => item === selectedRow.id);
this.selectedRowKeys.splice(index, 1);
this.selectedRows.splice(index, 1);
this._confirmDelReceipt([selectedRow]);
}
this.addNewEvt(0);
},
onSelectAll(selected, selectedRows, changeRows) {
if (selected) {
this.selectedRowKeys = this.selectedRowKeys.concat(changeRows.map((item) => item.id));
changeRows.forEach((item) => {
const obj = {
...item,
backAmount:
this.residueBackAmount > item.currentReceiptAmount ? item.currentReceiptAmount : this.residueBackAmount
};
this.selectedRows.push(obj);
});
// this.selectedRows = this.selectedRows.concat(chgRows);
} else {
changeRows.forEach((item) => {
const findIndex = this.selectedRowKeys.findIndex((rowId) => rowId === item.id);
this.selectedRowKeys.splice(findIndex, 1);
this.selectedRows.splice(findIndex, 1);
});
this._confirmDelReceipt(changeRows);
}
this.addNewEvt(0);
},
// 全选
selectAllList() {
let billDate = this.searchForm.billDate || [];
this.$apis
.queryReceiptInfoList({
pageNum: 1,
pageSize: this.pagination.total,
backMoneyNo: this.backMoneyNo,
payorCode: this.form.payorCode,
...this.searchForm,
receiptEndDate: billDate[1] ? billDate[1] + ' 23:59:59' : undefined,
receiptStartDate: billDate[0] ? billDate[0] + ' 00:00:00' : undefined
})
.then((res) => {
if (res.returnCode == '0000') {
let content = res.content || {};
const dataList =
content.list.map((item) => {
item.arrearsAmountShow = item.arrearsAmount;
item.arrearsAmount = '';
return item;
}) || [];
//
this.selectedRowKeys = dataList.map(item => item.id)
this.selectedRows = dataList
this.addNewEvt(0);
} else {
this.$message.error(res.returnMsg);
}
});
},
delRecord(record, index) {
this.selectedRowKeys.splice(index, 1);
this.selectedRows.splice(index, 1);
if (record.relationed) {
// 已经关联的调用接口删除
this._confirmDelReceipt([record]);
}
},
_confirmDelReceipt(records) {
if (!this.backMoneyNo) return;
const receiptVoList = records
.filter((item) => {
const findIndex = this.relatedList.findIndex((rowId) => rowId === item.id);
return findIndex > -1;
})
.map((item) => {
return {
id: item.id
};
});
if (receiptVoList.length === 0) return;
this.$apis
.deleteReceiptRecord({
backMoneyNo: this.backMoneyNo,
receiptVoList
})
.then((res) => {
if (res.returnCode == '0000') {
this._getNewEOBList();
} else {
this.$message.error(res.returnMsg);
}
});
},
// 修改保险公司
changePayor() {
if (this.selectedRowKeys.length > 0 && (this.form.id || this.savedStatus)) {
this.$modal.confirm({
title: '提示',
content: '是否解除已关联账单',
okText: '确认',
cancelText: '取消',
onOk: () => {
this.selectedRowKeys = [];
this.selectedRows = [];
},
onCancel: () => {}
});
}
this._getNewEOBList();
},
pageChange(pager) {
this.pagination = {
...this.pagination,
...pager
};
this._getNewEOBList();
},
// 获取保险公司下拉选项
_getCompanyOptions() {
this.$apis.getCompanyOptions().then((res) => {
this.companyOptions = res.content || [];
});
},
// 获取已关联的账单
getData() {
if (!this.backMoneyNo) {
if (this.activeKey1 === '0') {
this.$message.error('暂未关联账单,请在全部账单中添加账单');
}
return;
}
let billDate = this.searchForm.billDate || [];
this.$apis
.queryBackReceiptList({
pageNum: 1,
pageSize: 999,
backMoneyNo: this.backMoneyNo,
...this.searchForm,
receiptEndDate: billDate[1] ? billDate[1] + ' 23:59:59' : undefined,
receiptStartDate: billDate[0] ? billDate[0] + ' 00:00:00' : undefined
})
.then((res) => {
if (res.returnCode == '0000') {
this.getBackMoneyReportCount({
pageNum: 1,
pageSize: 999,
backMoneyNo: this.backMoneyNo,
...this.searchForm,
receiptEndDate: billDate[1] ? billDate[1] + ' 23:59:59' : undefined,
receiptStartDate: billDate[0] ? billDate[0] + ' 00:00:00' : undefined
})
const list = res.content.list || [];
let ids = [];
this.selectedRows = list.map((item) => {
item.relationed = true;
ids.push(item.id);
return item;
});
this.selectedRowKeys = this.$lodash.cloneDeep(ids);
this.relatedList = this.$lodash.cloneDeep(ids);
} else {
this.$message.error(res.returnMsg);
}
});
},
// 获取所有账单
_getNewEOBList() {
let billDate = this.searchForm.billDate || [];
this.$apis
.queryReceiptInfoList({
pageNum: this.pagination.pageNum,
pageSize: this.pagination.pageSize,
backMoneyNo: this.backMoneyNo,
payorCode: this.form.payorCode,
...this.searchForm,
receiptEndDate: billDate[1] ? billDate[1] + ' 23:59:59' : undefined,
receiptStartDate: billDate[0] ? billDate[0] + ' 00:00:00' : undefined
})
.then((res) => {
if (res.returnCode == '0000') {
this.getBackMoneyReportCount({
pageNum: this.pagination.pageNum,
pageSize: this.pagination.pageSize,
backMoneyNo: this.backMoneyNo,
payorCode: this.form.payorCode,
...this.searchForm,
receiptEndDate: billDate[1] ? billDate[1] + ' 23:59:59' : undefined,
receiptStartDate: billDate[0] ? billDate[0] + ' 00:00:00' : undefined
})
let content = res.content || {};
this.pagination.total = content.total || 0;
this.dataList =
content.list.map((item) => {
item.arrearsAmountShow = item.arrearsAmount;
item.arrearsAmount = '';
return item;
}) || [];
} else {
this.$message.error(res.returnMsg);
}
});
},
//新建/保存回款
addNewEvt(backStatus) {
if (!this.form.payorCode) {
this.$message.warning('请选择保险公司');
return;
}
if (!this.form.backDate) {
this.$message.warning('请选择回款日期');
return;
}
if (!this.form.backAmountCny && this.form.backAmountCny !== 0) {
this.$message.warning('请输入回款金额(人民币)');
return;
}
let receiptVoList = this.selectedRows.map((item) => {
return {
id: item.id,
backAmount: item.backAmount,
arrearsAmount: item.arrearsAmount,
remark: item.remark
};
});
const flag = receiptVoList.some((item) => {
const exist = !item.backAmount && item.backAmount !== 0;
return exist;
});
if (flag) {
this.$message.warning('存在关联账单未输入回款金额');
return;
}
// 如果有上传附件则显示列表
if (backMoneyDataDetail.fileList) {
this.fileList = backMoneyDataDetail.fileList.map((d) => {
const file = {
uid: Math.random() * 10000,
name: d.fileName,
status: 'done',
url: d.fileUrl
}
return file
})
}
this.getData()
}
},
mounted() {
this.calcTableHeight()
this._getNewEOBList()
},
methods: {
moment,
// 获取未清余额合计
getBackMoneyReportCount(params) {
this.$apis
.queryBackReceiptCount({
...params,
...this.pagination
})
.then((res) => {
if (res.returnCode == '0000') {
console.log(res.content)
this.ciReceiptTotalVo = res.content
}
})
},
// 计算表格最大高度
calcTableHeight() {
const dom = this.$refs.burt
const containterH = dom.clientHeight
const gobackH = document.querySelector('.back-container').clientHeight
const style = window.getComputedStyle(dom, null)
const paddingT = parseFloat(style.getPropertyValue('padding-top'))
const paddingB = parseFloat(style.getPropertyValue('padding-bottom'))
const paddingSum = paddingT + paddingB
this.tableHeight = containterH - 300 - gobackH - paddingSum
this.tableHeight1 = containterH - 340 - gobackH - paddingSum
// 设置每页展示条数
const pageSize = Math.floor((this.tableHeight1 - 10) / 32)
this.$set(this.pagination, 'pageSize', pageSize)
},
// 已关联账单表格行类名
rowClassName(record) {
return record.hidden ? 'hide_' : ''
},
// 账单查询
searchData() {
if (this.activeKey1 === '1') {
this._getNewEOBList()
} else {
this.getData()
}
},
// 选择框筛选
filterCode(input, option) {
return (
option.componentOptions.children[0].text
.toLowerCase()
.indexOf(input.toLowerCase()) >= 0
)
},
paneChange() {
this.$refs.ruleForm[0].clearValidate()
this.panes.forEach((item) => {
item.show = false
})
this.panes[Number(this.activeKey)].show = true
},
changeAmount(row) {
let totalMoney = Number(this.form.backAmountCny || 0)
this.selectedRows.forEach((item) => {
totalMoney -= Number(item.backAmount)
})
if (totalMoney < 0) {
this.$message.error('可核销余额不足')
}
if (Number(row.actualAmount || 0) - Number(row.backAmount || 0) < 0) {
this.$message.warning('录入账单回款金额大于账单金额')
}
},
onSelectChange(selectedRow, selected) {
selectedRow['backAmount'] =
this.residueBackAmount > selectedRow.currentReceiptAmount
? selectedRow.currentReceiptAmount
: this.residueBackAmount
if (selected) {
this.selectedRowKeys.push(selectedRow.id)
this.selectedRows.push(selectedRow)
} else {
const index = this.selectedRowKeys.findIndex(
(item) => item === selectedRow.id
)
this.selectedRowKeys.splice(index, 1)
this.selectedRows.splice(index, 1)
this._confirmDelReceipt([selectedRow])
}
this.addNewEvt(0)
},
onSelectAll(selected, selectedRows, changeRows) {
if (selected) {
this.selectedRowKeys = this.selectedRowKeys.concat(
changeRows.map((item) => item.id)
)
changeRows.forEach((item) => {
const obj = {
...item,
backAmount:
this.residueBackAmount > item.currentReceiptAmount
? item.currentReceiptAmount
: this.residueBackAmount
}
this.selectedRows.push(obj)
})
// this.selectedRows = this.selectedRows.concat(chgRows);
} else {
changeRows.forEach((item) => {
const findIndex = this.selectedRowKeys.findIndex(
(rowId) => rowId === item.id
)
this.selectedRowKeys.splice(findIndex, 1)
this.selectedRows.splice(findIndex, 1)
})
this._confirmDelReceipt(changeRows)
}
this.addNewEvt(0)
},
// 全选
selectAllList() {
let billDate = this.searchForm.billDate || []
this.$apis
.queryReceiptInfoList({
pageNum: 1,
pageSize: this.pagination.total,
backMoneyNo: this.backMoneyNo,
payorCode: this.form.payorCode,
...this.searchForm,
receiptEndDate: billDate[1] ? billDate[1] + ' 23:59:59' : undefined,
receiptStartDate: billDate[0] ? billDate[0] + ' 00:00:00' : undefined
})
.then((res) => {
if (res.returnCode == '0000') {
let content = res.content || {}
const dataList =
content.list.map((item) => {
item.arrearsAmountShow = item.arrearsAmount
item.arrearsAmount = ''
return item
}) || []
//
this.selectedRowKeys = dataList.map((item) => item.id)
this.selectedRows = dataList
this.addNewEvt(0)
} else {
this.$message.error(res.returnMsg)
}
})
},
delRecord(record, index) {
this.selectedRowKeys.splice(index, 1)
this.selectedRows.splice(index, 1)
if (record.relationed) {
// 已经关联的调用接口删除
this._confirmDelReceipt([record])
}
},
_confirmDelReceipt(records) {
if (!this.backMoneyNo) return
const receiptVoList = records
.filter((item) => {
const findIndex = this.relatedList.findIndex(
(rowId) => rowId === item.id
)
return findIndex > -1
})
.map((item) => {
return {
id: item.id
}
})
if (receiptVoList.length === 0) return
this.$apis
.deleteReceiptRecord({
backMoneyNo: this.backMoneyNo,
receiptVoList
})
.then((res) => {
if (res.returnCode == '0000') {
this._getNewEOBList()
} else {
this.$message.error(res.returnMsg)
}
})
},
// 修改保险公司
changePayor() {
if (
this.selectedRowKeys.length > 0 &&
(this.form.id || this.savedStatus)
) {
this.$modal.confirm({
title: '提示',
content: '是否解除已关联账单',
okText: '确认',
cancelText: '取消',
onOk: () => {
this.selectedRowKeys = []
this.selectedRows = []
},
onCancel: () => {}
})
}
this._getNewEOBList()
},
pageChange(pager) {
this.pagination = {
...this.pagination,
...pager
}
this._getNewEOBList()
},
// 获取保险公司下拉选项
_getCompanyOptions() {
this.$apis.getCompanyOptions().then((res) => {
this.companyOptions = res.content || []
})
},
// 获取已关联的账单
getData() {
if (!this.backMoneyNo) {
if (this.activeKey1 === '0') {
this.$message.error('暂未关联账单,请在全部账单中添加账单')
}
return
}
let billDate = this.searchForm.billDate || []
this.$apis
.queryBackReceiptList({
pageNum: 1,
pageSize: 999,
backMoneyNo: this.backMoneyNo,
...this.searchForm,
receiptEndDate: billDate[1] ? billDate[1] + ' 23:59:59' : undefined,
receiptStartDate: billDate[0] ? billDate[0] + ' 00:00:00' : undefined
})
.then((res) => {
if (res.returnCode == '0000') {
this.getBackMoneyReportCount({
pageNum: 1,
pageSize: 999,
backMoneyNo: this.backMoneyNo,
...this.searchForm,
receiptEndDate: billDate[1]
? billDate[1] + ' 23:59:59'
: undefined,
receiptStartDate: billDate[0]
? billDate[0] + ' 00:00:00'
: undefined
})
const list = res.content.list || []
let ids = []
this.selectedRows = list.map((item) => {
item.relationed = true
ids.push(item.id)
return item
})
this.selectedRowKeys = this.$lodash.cloneDeep(ids)
this.relatedList = this.$lodash.cloneDeep(ids)
} else {
this.$message.error(res.returnMsg)
}
})
},
// 获取所有账单
_getNewEOBList() {
let billDate = this.searchForm.billDate || []
this.$apis
.queryReceiptInfoList({
pageNum: this.pagination.pageNum,
pageSize: this.pagination.pageSize,
backMoneyNo: this.backMoneyNo,
payorCode: this.form.payorCode,
...this.searchForm,
receiptEndDate: billDate[1] ? billDate[1] + ' 23:59:59' : undefined,
receiptStartDate: billDate[0] ? billDate[0] + ' 00:00:00' : undefined
})
.then((res) => {
if (res.returnCode == '0000') {
this.getBackMoneyReportCount({
pageNum: this.pagination.pageNum,
pageSize: this.pagination.pageSize,
backMoneyNo: this.backMoneyNo,
payorCode: this.form.payorCode,
...this.searchForm,
receiptEndDate: billDate[1]
? billDate[1] + ' 23:59:59'
: undefined,
receiptStartDate: billDate[0]
? billDate[0] + ' 00:00:00'
: undefined
})
let content = res.content || {}
this.pagination.total = content.total || 0
this.dataList =
content.list.map((item) => {
item.arrearsAmountShow = item.arrearsAmount
item.arrearsAmount = ''
return item
}) || []
} else {
this.$message.error(res.returnMsg)
}
})
},
//新建/保存回款
addNewEvt(backStatus) {
if (!this.form.payorCode) {
this.$message.warning('请选择保险公司')
return
}
if (!this.form.backDate) {
this.$message.warning('请选择回款日期')
return
}
if (!this.form.backAmountCny && this.form.backAmountCny !== 0) {
this.$message.warning('请输入回款金额(人民币)')
return
}
let receiptVoList = this.selectedRows.map((item) => {
return {
id: item.id,
backAmount: item.backAmount,
arrearsAmount: item.arrearsAmount,
remark: item.remark
}
})
const flag = receiptVoList.some((item) => {
const exist = !item.backAmount && item.backAmount !== 0
return exist
})
if (flag) {
this.$message.warning('存在关联账单未输入回款金额')
return
}
const formData = {
...this.form,
receiptVoList,
backDate: this.form.backDate ? moment(this.form.backDate).format('YYYY-MM-DD HH:mm:ss') : '',
backMoneyNo: this.backMoneyNo, //回款编号
backStatus // 0暂存 1结案
};
const formData = {
...this.form,
receiptVoList,
backDate: this.form.backDate
? moment(this.form.backDate).format('YYYY-MM-DD HH:mm:ss')
: '',
backMoneyNo: this.backMoneyNo, //回款编号
backStatus // 0暂存 1结案
}
// 上传附件格式转换
formData.fileList = this.fileList.map((d) => {
const file = {
fileName: d.name,
fileUrl: d.url
};
return file;
});
// 上传附件格式转换
formData.fileList = this.fileList.map((d) => {
const file = {
fileName: d.name,
fileUrl: d.url
}
return file
})
this.$apis.saveBackMoney(formData).then((res) => {
const msg = backStatus === 1 ? '结案' : '暂存';
if (res.returnCode == '0000') {
this.backMoneyNo = res.content;
this.savedStatus = true;
this.$message.success(`${msg}成功`);
this.selectedRowKeys = [];
this.getData();
this._getNewEOBList();
this.$apis.saveBackMoney(formData).then((res) => {
const msg = backStatus === 1 ? '结案' : '暂存'
if (res.returnCode == '0000') {
this.backMoneyNo = res.content
this.savedStatus = true
this.$message.success(`${msg}成功`)
this.selectedRowKeys = []
this.getData()
this._getNewEOBList()
// this.$router.go(-1);
} else {
this.$message.error(res.returnMsg);
}
});
},
// this.$router.go(-1);
} else {
this.$message.error(res.returnMsg)
}
})
},
/* ======== 上传区域 ======== */
// 上传之前
beforeUpload() {
const len = this.fileList.length;
if (len >= 5) {
this.$message.warning('不能超过5个文件');
return false;
}
return true;
},
// 删除文件
removeFile(file) {
let index;
this.fileList.forEach((item, i) => {
if (item.uid == file.uid) {
index = i;
}
});
this.fileList.splice(index, 1);
return true;
},
// 上传文件
uploadFile(fileData) {
let formData = new FormData();
formData.append('file', fileData.file);
this.$apis.uploadImg(formData).then((res) => {
fileData.onSuccess();
let tmp = {
uid: Math.random() * 10000,
name: res.original,
status: 'done',
url: res.url
};
this.fileList.push(tmp);
/* ======== 上传区域 ======== */
// 上传之前
beforeUpload() {
const len = this.fileList.length
if (len >= 5) {
this.$message.warning('不能超过5个文件')
return false
}
return true
},
// 删除文件
removeFile(file) {
let index
this.fileList.forEach((item, i) => {
if (item.uid == file.uid) {
index = i
}
})
this.fileList.splice(index, 1)
return true
},
// 上传文件
uploadFile(fileData) {
let formData = new FormData()
formData.append('file', fileData.file)
this.$apis.uploadImg(formData).then((res) => {
fileData.onSuccess()
let tmp = {
uid: Math.random() * 10000,
name: res.original,
status: 'done',
url: res.url
}
this.fileList.push(tmp)
this.$forceUpdate();
});
},
//导出报表
exportExcel() {
let filter = {
backMoneyNo: this.backMoneyNo,
payorCode: this.form.payorCode
};
this.$apis.exportBackReceiptList(filter).then((res) => {
exportFile(res, '已关联账单.xls');
});
},
// 保险公司支持输入搜索
filterOption(input, option) {
return option.componentOptions.children[0].text.indexOf(input) >= 0;
}
}
};
this.$forceUpdate()
})
},
//导出报表
exportExcel() {
let filter = {
backMoneyNo: this.backMoneyNo,
payorCode: this.form.payorCode
}
this.$apis.exportBackReceiptList(filter).then((res) => {
exportFile(res, '已关联账单.xls')
})
},
// 保险公司支持输入搜索
filterOption(input, option) {
return option.componentOptions.children[0].text.indexOf(input) >= 0
}
}
}
</script>
<style lang="less" scoped>
.none-label {
text-align: right;
.ant-form-item-label {
opacity: 0;
}
text-align: right;
.ant-form-item-label {
opacity: 0;
}
}
.all-list_box {
height: calc(100vh - 400px);
overflow-y: auto;
&.no-data {
display: flex;
justify-content: center;
align-items: center;
color: red;
}
height: calc(100vh - 400px);
overflow-y: auto;
&.no-data {
display: flex;
justify-content: center;
align-items: center;
color: red;
}
}
.table-content {
::v-deep {
tr.hide_ {
display: none;
}
td {
padding: 5px 16px !important;
}
}
::v-deep {
tr.hide_ {
display: none;
}
td {
padding: 5px 16px !important;
}
}
}
.search-form {
.ant-form .ant-form-item {
margin-bottom: 4px;
}
.flex-col {
display: flex;
justify-content: space-around;
align-items: center;
height: 44px;
}
.ant-form .ant-form-item {
margin-bottom: 4px;
}
.flex-col {
display: flex;
justify-content: space-around;
align-items: center;
height: 44px;
}
}
.ant-btn .icon-class {
.mg-r(10);
.mg-r(10);
}
.success.ant-btn-link {
color: #4cd964;
color: #4cd964;
}
.danger.ant-btn-link {
color: #ff3b30;
color: #ff3b30;
}
.burt-container {
height: calc(100vh - 86px);
display: flex;
flex-direction: column;
padding-bottom: 10px;
::v-deep {
.ant-form-item {
display: flex;
&.ant-form-item-with-help {
margin-bottom: 0;
}
}
.ant-tabs {
flex: 1;
min-height: 0;
}
}
.fixed_width {
max-width: 150px;
}
height: calc(100vh - 86px);
display: flex;
flex-direction: column;
padding-bottom: 10px;
::v-deep {
.ant-form-item {
display: flex;
&.ant-form-item-with-help {
margin-bottom: 0;
}
}
.ant-tabs {
flex: 1;
min-height: 0;
}
}
.fixed_width {
max-width: 150px;
}
}
.bill-content {
overflow-y: auto;
overflow-x: hidden;
padding-right: 12px;
position: relative;
::v-deep {
.ant-form-item {
margin-right: 0;
}
}
.pagination {
margin-top: 10px;
::v-deep .jump-page {
height: 30px;
}
}
overflow-y: auto;
overflow-x: hidden;
padding-right: 12px;
position: relative;
::v-deep {
.ant-form-item {
margin-right: 0;
}
}
.pagination {
margin-top: 10px;
::v-deep .jump-page {
height: 30px;
}
}
}
::v-deep .bill-form {
.ant-form .ant-form-item {
margin-bottom: 4px !important;
}
.ant-form .ant-form-item {
margin-bottom: 4px !important;
}
}
.remark_inp {
width: 300px;
width: 300px;
}
.red_inp {
color: red !important;
::v-deep .ant-input {
color: red;
}
color: red !important;
::v-deep .ant-input {
color: red;
}
}
.ellipsis_ {
display: block;
max-width: 148px;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
display: block;
max-width: 148px;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
@media screen and (min-width: 1920px) {
.all-list_box {
height: calc(100vh - 420px);
}
.all-list_box {
height: calc(100vh - 420px);
}
}
@media screen and (min-width: 1440px) {
.all-list_box {
height: calc(100vh - 400px);
}
.all-list_box {
height: calc(100vh - 400px);
}
}
.checked-count{
position: absolute;
top: 14px;
left: 210px;
z-index: 10;
button {
margin-right: 6px;
}
.checked-count {
position: absolute;
top: 14px;
left: 210px;
z-index: 10;
button {
margin-right: 6px;
}
}
</style>
<template>
<!-- 表格虚拟滚动 -->
<div>
<a-table
v-bind="$attrs"
v-on="$listeners"
:pagination="false"
:columns="tableColumns"
:data-source="renderData"
>
<template
v-for="slot in Object.keys($scopedSlots)"
:slot="slot"
slot-scope="text, record, index"
>
<slot :name="slot" v-bind="{ text, record, index }"></slot>
</template>
</a-table>
<div class="ant-table-append" ref="append" v-show="!isHideAppend">
<slot name="append"></slot>
</div>
</div>
</template>
<script>
import { throttle } from 'lodash'
// 判断是否是滚动容器
function isScroller(el) {
const style = window.getComputedStyle(el, null)
const scrollValues = ['auto', 'scroll']
return (
scrollValues.includes(style.overflow) ||
scrollValues.includes(style['overflow-y'])
)
}
// 获取父层滚动容器
function getParentScroller(el) {
let parent = el
while (parent) {
if ([window, document, document.documentElement].includes(parent)) {
return window
}
if (isScroller(parent)) {
return parent
}
parent = parent.parentNode
}
return parent || window
}
// 获取容器滚动位置
function getScrollTop(el) {
return el === window ? window.pageYOffset : el.scrollTop
}
// 获取容器高度
function getOffsetHeight(el) {
return el === window ? window.innerHeight : el.offsetHeight
}
// 滚动到某个位置
function scrollToY(el, y) {
if (el === window) {
window.scroll(0, y)
} else {
el.scrollTop = y
}
}
// 表格body class名称
const TableBodyClassNames = [
'.ant-table-scroll .ant-table-body',
'.ant-table-fixed-left .ant-table-body-inner',
'.ant-table-fixed-right .ant-table-body-inner'
]
let checkOrder = 0 // 多选:记录多选选项改变的顺序
export default {
inheritAttrs: false,
name: 'a-virtual-table',
props: {
dataSource: {
type: Array,
default: () => []
},
columns: {
type: Array,
default: () => []
},
// key值,data数据中的唯一id
keyProp: {
type: String,
default: 'id'
},
// 每一行的预估高度
itemSize: {
type: Number,
default: 60
},
// 指定滚动容器
scrollBox: {
type: String
},
// 顶部和底部缓冲区域,值越大显示表格的行数越多
buffer: {
type: Number,
default: 100
},
// 滚动事件的节流时间
throttleTime: {
type: Number,
default: 10
},
// 是否获取表格行动态高度
dynamic: {
type: Boolean,
default: true
},
// 是否开启虚拟滚动
virtualized: {
type: Boolean,
default: true
},
// 是否是树形结构
isTree: {
type: Boolean,
default: false
}
},
data() {
return {
start: 0,
end: undefined,
sizes: {}, // 尺寸映射(依赖响应式)
renderData: [],
// 兼容多选
isCheckedAll: false, // 全选
isCheckedImn: false, // 控制半选样式
isHideAppend: false
}
},
computed: {
tableColumns() {
return this.columns.map((column) => {
// 兼容多选
if (column.type === 'selection') {
return {
title: () => {
return (
<a-checkbox
checked={this.isCheckedAll}
indeterminate={this.isCheckedImn}
onchange={() => this.onCheckAllRows(!this.isCheckedAll)}
></a-checkbox>
)
},
customRender: (text, row) => {
return (
<a-checkbox
checked={row.$v_checked}
onchange={() => this.onCheckRow(row, !row.$v_checked)}
></a-checkbox>
)
},
width: 60,
...column
}
} else if (column.index) {
// 兼容索引
return {
customRender: (text, row, index) => {
const curIndex = this.start + index
return typeof column.index === 'function'
? column.index(curIndex)
: curIndex + 1
},
...column
}
} else if (
column.customRender &&
typeof column.customRender === 'function'
) {
// 如果定义了 customRender 函数,使用一个新的 customRender 方法包装它
const originalCustomRender = column.customRender
column.customRender = (text, record, index) => {
// 调用原始的 customRender 函数,并传入必要的参数
return originalCustomRender(text, record, index)
}
}
return column
})
},
// 计算出每个item(的key值)到滚动容器顶部的距离
offsetMap({ keyProp, itemSize, sizes, dataSource }) {
if (!this.dynamic) return {}
const res = {}
let total = 0
for (let i = 0; i < dataSource.length; i++) {
const key = dataSource[i][keyProp]
res[key] = total
const curSize = sizes[key]
const size = typeof curSize === 'number' ? curSize : itemSize
total += size
}
return res
}
},
methods: {
// 初始化数据
initData() {
// 是否是表格内部滚动
this.isInnerScroll = false
this.scroller = this.getScroller()
this.setToTop()
// 首次需要执行2次handleScroll:因为第一次计算renderData时表格高度未确认导致计算不准确;第二次执行时,表格高度确认后,计算renderData是准确的
this.handleScroll()
this.$nextTick(() => {
this.handleScroll()
})
// 监听事件
this.onScroll = throttle(this.handleScroll, this.throttleTime)
this.scroller.addEventListener('scroll', this.onScroll)
window.addEventListener('resize', this.onScroll)
},
// 设置表格到滚动容器的距离
setToTop() {
if (this.isInnerScroll) {
this.toTop = 0
} else {
this.toTop =
this.$el.getBoundingClientRect().top -
(this.scroller === window
? 0
: this.scroller.getBoundingClientRect().top) +
getScrollTop(this.scroller)
}
},
// 获取滚动元素
getScroller() {
let el
if (this.scrollBox) {
if (this.scrollBox === 'window' || this.scrollBox === window)
return window
el = document.querySelector(this.scrollBox)
if (!el)
throw new Error(
` scrollBox prop: '${this.scrollBox}' is not a valid selector`
)
if (!isScroller(el))
console.warn(
`Warning! scrollBox prop: '${this.scrollBox}' is not a scroll element`
)
return el
}
// 如果表格是固定高度,则获取表格内的滚动节点,否则获取父层滚动节点
if (this.$attrs.scroll && this.$attrs.scroll.y) {
this.isInnerScroll = true
return this.$el.querySelector('.ant-table-body')
} else {
return getParentScroller(this.$el)
}
},
// 处理滚动事件
handleScroll() {
if (!this.virtualized) return
// 更新当前尺寸(高度)
this.updateSizes()
// 计算renderData
this.calcRenderData()
// 计算位置
this.calcPosition()
},
// 更新尺寸(高度)
updateSizes() {
if (!this.dynamic) return
let rows = []
if (this.isTree) {
// 处理树形表格,筛选出一级树形结构
rows = this.$el.querySelectorAll(
'.ant-table-body .ant-table-row-level-0'
)
} else {
rows = this.$el.querySelectorAll(
'.ant-table-body .ant-table-tbody .ant-table-row'
)
}
Array.from(rows).forEach((row, index) => {
const item = this.renderData[index]
if (!item) return
// 计算表格行的高度
let offsetHeight = row.offsetHeight
// 表格行如果有扩展行,需要加上扩展内容的高度
const nextEl = row.nextSibling
if (
nextEl &&
nextEl.classList &&
nextEl.classList.contains('ant-table-expanded-row')
) {
offsetHeight += row.nextSibling.offsetHeight
}
// 表格行如果有子孙节点,需要加上子孙节点的高度
if (this.isTree) {
let next = row.nextSibling
while (
next &&
next.tagName === 'TR' &&
!next.classList.contains('ant-table-row-level-0')
) {
offsetHeight += next.offsetHeight
next = next.nextSibling
}
}
const key = item[this.keyProp]
if (this.sizes[key] !== offsetHeight) {
this.$set(this.sizes, key, offsetHeight)
row._offsetHeight = offsetHeight
}
})
},
// 计算只在视图上渲染的数据
calcRenderData() {
const { scroller, buffer, dataSource: data } = this
// 计算可视范围顶部、底部
const top = getScrollTop(scroller) - buffer - this.toTop
const scrollerHeight = this.isInnerScroll
? this.$attrs.scroll.y
: getOffsetHeight(scroller)
const bottom =
getScrollTop(scroller) + scrollerHeight + buffer - this.toTop
let start
let end
if (!this.dynamic) {
start = top <= 0 ? 0 : Math.floor(top / this.itemSize)
end = bottom <= 0 ? 0 : Math.ceil(bottom / this.itemSize)
} else {
// 二分法计算可视范围内的开始的第一个内容
let l = 0
let r = data.length - 1
let mid = 0
while (l <= r) {
mid = Math.floor((l + r) / 2)
const midVal = this.getItemOffsetTop(mid)
if (midVal < top) {
const midNextVal = this.getItemOffsetTop(mid + 1)
if (midNextVal > top) break
l = mid + 1
} else {
r = mid - 1
}
}
// 计算渲染内容的开始、结束索引
start = mid
end = data.length - 1
for (let i = start + 1; i < data.length; i++) {
const offsetTop = this.getItemOffsetTop(i)
if (offsetTop >= bottom) {
end = i
break
}
}
}
// 开始索引始终保持偶数,如果为奇数,则加1使其保持偶数【确保表格行的偶数数一致,不会导致斑马纹乱序显示】
if (start % 2) {
start = start - 1
}
this.top = top
this.bottom = bottom
this.start = start
this.end = end
this.renderData = data.slice(start, end + 1)
this.$emit('change', this.renderData, this.start, this.end)
},
// 计算位置
calcPosition() {
const last = this.dataSource.length - 1
// 计算内容总高度
const wrapHeight = this.getItemOffsetTop(last) + this.getItemSize(last)
// 计算当前滚动位置需要撑起的高度
const offsetTop = this.getItemOffsetTop(this.start)
// 设置dom位置
TableBodyClassNames.forEach((className) => {
const el = this.$el.querySelector(className)
if (!el) return
// 创建wrapEl、innerEl
if (!el.wrapEl) {
const wrapEl = document.createElement('div')
const innerEl = document.createElement('div')
// 此处设置display为'inline-block',是让div宽度等于表格的宽度,修复x轴滚动时右边固定列没有阴影的bug
wrapEl.style.display = 'inline-block'
innerEl.style.display = 'inline-block'
wrapEl.appendChild(innerEl)
innerEl.appendChild(el.children[0])
el.insertBefore(wrapEl, el.firstChild)
el.wrapEl = wrapEl
el.innerEl = innerEl
}
if (el.wrapEl) {
// 设置高度
el.wrapEl.style.height = wrapHeight + 'px'
// 设置transform撑起高度
el.innerEl.style.transform = `translateY(${offsetTop}px)`
// 设置paddingTop撑起高度
// el.innerEl.style.paddingTop = `${offsetTop}px`
}
})
},
// 获取某条数据offsetTop
getItemOffsetTop(index) {
if (!this.dynamic) {
return this.itemSize * index
}
const item = this.dataSource[index]
if (item) {
return this.offsetMap[item[this.keyProp]] || 0
}
return 0
},
// 获取某条数据的尺寸
getItemSize(index) {
if (index <= -1) return 0
const item = this.dataSource[index]
if (item) {
const key = item[this.keyProp]
return this.sizes[key] || this.itemSize
}
return this.itemSize
},
// 【外部调用】更新
update() {
this.setToTop()
this.handleScroll()
},
// 【外部调用】滚动到第几行
// (不太精确:滚动到第n行时,如果周围的表格行计算出真实高度后会更新高度,导致内容坍塌或撑起)
scrollTo(index, stop = false) {
const item = this.dataSource[index]
if (item && this.scroller) {
this.updateSizes()
this.calcRenderData()
this.$nextTick(() => {
const offsetTop = this.getItemOffsetTop(index)
scrollToY(this.scroller, offsetTop)
// 调用两次scrollTo,第一次滚动时,如果表格行初次渲染高度发生变化时,会导致滚动位置有偏差,此时需要第二次执行滚动,确保滚动位置无误
if (!stop) {
setTimeout(() => {
this.scrollTo(index, true)
}, 50)
}
})
}
},
// 渲染全部数据
renderAllData() {
this.renderData = this.dataSource
this.$emit('change', this.dataSource, 0, this.dataSource.length - 1)
this.$nextTick(() => {
// 清除撑起的高度和位置
TableBodyClassNames.forEach((className) => {
const el = this.$el.querySelector(className)
if (!el) return
if (el.wrapEl) {
// 设置高度
el.wrapEl.style.height = 'auto'
// 设置transform撑起高度
el.innerEl.style.transform = `translateY(${0}px)`
}
})
})
},
// 执行update方法更新虚拟滚动,且每次nextTick只能执行一次【在数据大于100条开启虚拟滚动时,由于监听了data、virtualized会连续触发两次update方法:第一次update时,(updateSize)计算尺寸里的渲染数据(renderData)与表格行的dom是一一对应,之后会改变渲染数据(renderData)的值;而第二次执行update时,renderData改变了,而表格行dom未改变,导致renderData与dom不一一对应,从而位置计算错误,最终渲染的数据对应不上。因此使用每次nextTick只能执行一次来避免bug发生】
doUpdate() {
if (this.hasDoUpdate) return // nextTick内已经执行过一次就不执行
if (!this.scroller) return // scroller不存在说明未初始化完成,不执行
// 启动虚拟滚动的瞬间,需要暂时隐藏el-table__append-wrapper里的内容,不然会导致滚动位置一直到append的内容处
this.isHideAppend = true
this.update()
this.hasDoUpdate = true
this.$nextTick(() => {
this.hasDoUpdate = false
this.isHideAppend = false
})
},
// 兼容多选:选择表格所有行
onCheckAllRows(val) {
val = this.isCheckedImn ? true : val
this.dataSource.forEach((row) => {
if (row.$v_checked === val) return
this.$set(row, '$v_checked', val)
this.$set(row, '$v_checkedOrder', val ? checkOrder++ : undefined)
})
this.isCheckedAll = val
this.isCheckedImn = false
this.emitSelectionChange()
// 取消全选,则重置checkOrder
if (val === false) checkOrder = 0
},
// 兼容多选:选择表格某行
onCheckRow(row, val) {
if (row.$v_checked === val) return
this.$set(row, '$v_checked', val)
this.$set(row, '$v_checkedOrder', val ? checkOrder++ : undefined)
const checkedLen = this.dataSource.filter(
(row) => row.$v_checked === true
).length
if (checkedLen === 0) {
this.isCheckedAll = false
this.isCheckedImn = false
} else if (checkedLen === this.dataSource.length) {
this.isCheckedAll = true
this.isCheckedImn = false
} else {
this.isCheckedAll = false
this.isCheckedImn = true
}
this.emitSelectionChange()
},
// 多选:兼容表格selection-change事件
emitSelectionChange() {
const selection = this.dataSource
.filter((row) => row.$v_checked)
.sort((a, b) => a.$v_checkedOrder - b.$v_checkedOrder)
this.$emit('selection-change', selection)
},
// 多选:兼容表格toggleRowSelection方法
toggleRowSelection(row, selected) {
const val = typeof selected === 'boolean' ? selected : !row.$v_checked
this.onCheckRow(row, val)
},
// 多选:兼容表格clearSelection方法
clearSelection() {
this.isCheckedImn = false
this.onCheckAllRows(false)
}
},
watch: {
dataSource() {
if (!this.virtualized) {
this.renderAllData()
} else {
this.doUpdate()
}
},
virtualized: {
immediate: true,
handler(val) {
if (!val) {
this.renderAllData()
} else {
this.doUpdate()
}
}
}
},
created() {
this.$nextTick(() => {
this.initData()
})
},
mounted() {
const appendEl = this.$refs.append
this.$el.querySelector('.ant-table-body').appendChild(appendEl)
},
beforeDestroy() {
if (this.scroller) {
this.scroller.removeEventListener('scroll', this.onScroll)
window.removeEventListener('resize', this.onScroll)
}
}
}
</script>
<style lang="scss" scoped></style>
### 表格虚拟滚动
#### api文档
1. 属性说明
参数 说明 类型 可选值 默认值
dataSource 总数据 Array 必填
keyProp key值,data数据中的唯一id【⚠️若keyProp未设置或keyProp值不唯一,可能导致表格空数据或者滚动时渲染的数据断层、不连贯】 string — id
itemSize 每一行的预估高度 number — 60
scrollBox 指定滚动容器;在指定滚动容器时,如果表格设置了height高度,则滚动容器为表格内的滚动容器;如果表格为设置height高度,则自动获取父层以外的滚动容器,直至window容器为止 string — -
buffer 顶部和底部缓冲区域,值越大显示表格的行数越多 Number — 200
throttleTime 滚动事件的节流时间 number — 10
dynamic 动态获取表格行高度,默认开启。设置为false时,则以itemSize为表格行的真实高度,能大大减少虚拟滚动计算量,减少滚动白屏;如果itemSize与表格行的真实高度不一致,可能导致滚动时表格数据错乱 boolean — true
virtualized 是否开启虚拟滚动 boolean — true
* 支持 <a-table> 组件的props属性,更多请看 <a-table> api - — -
2. 方法说明
方法名 说明 参数
scrollTo 滚动到第几行【不太精确:因为滚动到第n行时,如果周围的表格行计算出真实高度后会更新高度,导致当前行坍塌或撑起】 index
update 更新 -
clearSelection 用于多选 <virtual-column type="selection">,清空用户的选择 -
toggleRowSelection 用于多选 <virtual-column type="selection">, 切换某一行的选中状态,如果使用了第二个参数,则是设置这一行选中与否(selected 为 true 则选中) row, selected
3. 事件说明
事件名称 说明 参数
change 计算完成真实显示的表格行数 (renderData, start, end):renderData 真实渲染的数据,start和end指的是渲染的数据在总数据的开始到结束的区间范围
selection-change 虚拟表格多选选项发生更改时触发事件 selectedRows
const path = require("path");
const LodashModuleReplacementPlugin = require("lodash-webpack-plugin");
const CompressionPlugin = require("compression-webpack-plugin"); // 代码压缩
const defaultSettings = require("./src/settings.js");
const path = require('path')
const LodashModuleReplacementPlugin = require('lodash-webpack-plugin')
const CompressionPlugin = require('compression-webpack-plugin') // 代码压缩
const defaultSettings = require('./src/settings.js')
module.exports = {
publicPath: "/bims",
publicPath: '/bims',
productionSourceMap: false,
pluginOptions: {
"style-resources-loader": {
preProcessor: "less",
patterns: [path.resolve(__dirname, "src/assets/style/common.less")],
},
'style-resources-loader': {
preProcessor: 'less',
patterns: [path.resolve(__dirname, 'src/assets/style/common.less')]
}
},
css: {
loaderOptions: {
less: {
javascriptEnabled: true,
},
javascriptEnabled: true
}
}
},
chainWebpack: (config) => {
if (process.env.NODE_ENV === "production") {
if (process.env.NODE_ENV === 'production') {
// lodash 按需加载
config.plugin("loadshReplace").use(new LodashModuleReplacementPlugin());
config.plugin('loadshReplace').use(new LodashModuleReplacementPlugin())
}
},
configureWebpack: (config) => {
config.name = defaultSettings.title;
const plugins = [];
if (process.env.NODE_ENV === "production") {
config.name = defaultSettings.title
const plugins = []
if (process.env.NODE_ENV === 'production') {
plugins.push(
new CompressionPlugin({
filename: "[path][base].gz",
algorithm: "gzip",
test: new RegExp(".(" + ["js", "css", "json"].join("|") + ")$"),
filename: '[path][base].gz',
algorithm: 'gzip',
test: new RegExp('.(' + ['js', 'css', 'json'].join('|') + ')$'),
threshold: 8192,
include: /\/src/,
exclude: /node_modules/,
minRatio: 0.8,
deleteOriginalAssets: false, // 此处源文件保留吧,以防不测
deleteOriginalAssets: false // 此处源文件保留吧,以防不测
})
);
)
}
config.plugins = [...config.plugins, ...plugins];
config.plugins = [...config.plugins, ...plugins]
},
devServer: {
......@@ -51,14 +51,15 @@ module.exports = {
port: 8888,
hot: true,
proxy: {
"^/api_bims": {
target: "http://47.99.75.3:8070",
'^/api_bims': {
target: 'http://47.99.75.3:8070', // 测试
// target: 'http://bims.medilink-global.com.cn/api_bims', // 生产
pathRewrite: {
"^/api_bims": "/",
'^/api_bims': '/'
},
changeOrigin: true,
secure: false,
},
},
},
};
secure: false
}
}
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment