InHospFeeUpload.vue 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557
  1. <template>
  2. <div>
  3. <el-button type="success" @click="feeDtle">费用明细</el-button>
  4. <el-button type="success" @click="preCalculateCost">医保试算</el-button>
  5. <el-button type="success" @click="uploadFees">费用上传</el-button>
  6. <el-button type="warning" :disabled="!isAdmin" @click="cancelFees">取消上传</el-button>
  7. <el-button type="primary" @click="weiGuiTuiFeiFenXiDialogOpen(true)">违规费用分析</el-button>
  8. <el-popover placement="left" width="730" trigger="click">
  9. <template #reference>
  10. <el-button type="warning" icon="CircleClose" plain>错误信息 ({{ errorMessages.length }})</el-button>
  11. </template>
  12. <el-tag type="info">错误信息</el-tag>
  13. <el-divider direction="vertical"></el-divider>
  14. <el-button type="warning" @click="clearErrorMessages">清除内容</el-button>
  15. <el-table width="700" max-height="300" class="errTable" :data="errorMessages">
  16. <el-table-column width="80" property="patNo" label="住院号"></el-table-column>
  17. <el-table-column width="80" property="patName" label="姓名"></el-table-column>
  18. <el-table-column width="450" property="message" label="错误详情"></el-table-column>
  19. </el-table>
  20. </el-popover>
  21. &nbsp;&nbsp;
  22. <el-tag type="info" size="small">
  23. <span style="color: black">¥ {{ patient.totalCharge }}</span>
  24. <span style="color: orangered">¥ {{ patient.chargeYb }}</span>
  25. </el-tag>
  26. <el-tag>可用余额:{{ patient.balance }}</el-tag>
  27. </div>
  28. <div style="margin-top: 4px">
  29. <el-tag type="info">治疗明细</el-tag>
  30. <el-tag>合计:¥ {{ xmFeeSum }}</el-tag>
  31. <el-table :data="xmFeeList" stripe :height="feeTableHeight">
  32. <el-table-column prop="detailSn" label="流水号" width="80"></el-table-column>
  33. <el-table-column prop="chargeCodeMx" label="院内码" width="100"></el-table-column>
  34. <el-table-column prop="chargeAmount" label="数量" sortable width="80"></el-table-column>
  35. <el-table-column prop="chargeFee" label="金额" width="80"></el-table-column>
  36. <el-table-column prop="chargeDate" label="收费日期"></el-table-column>
  37. <el-table-column prop="ybCode" :label="ybCodeLabel"></el-table-column>
  38. <el-table-column prop="chargeName" label="项目名称"></el-table-column>
  39. <el-table-column prop="ybSelfFlag" label="报销" width="80"></el-table-column>
  40. <el-table-column>
  41. <template #default="scope">
  42. <el-button circle v-if="!injuryMode && patient.mdtrtId && scope.row.chargeAmount < 0"
  43. @click="fixNegativeFeeUploadProblem(scope.row, 1)">
  44. <i class="iconfont icon-tools-hardware" style="font-size: 10px;"></i>
  45. </el-button>
  46. </template>
  47. </el-table-column>
  48. </el-table>
  49. <el-pagination
  50. @size-change="handleXmSizeChange"
  51. @current-change="handleCurrentXmChange"
  52. :current-page="page.xmPage"
  53. :page-sizes="[10, 30, 50, 70, 100, 300]"
  54. :page-size="page.xmPageSize"
  55. layout="total, sizes, prev, pager, next"
  56. :total="xmTotalSize"
  57. >
  58. </el-pagination>
  59. <div style="height: 5px"></div>
  60. <el-tag type="info">药品明细</el-tag>
  61. <el-tag>合计:¥ {{ ypFeeSum }}</el-tag>
  62. <el-table :data="ypFeeList" stripe :height="feeTableHeight">
  63. <el-table-column prop="detailSn" label="流水号" width="80"></el-table-column>
  64. <el-table-column prop="chargeCodeMx" label="院内码" width="100"></el-table-column>
  65. <el-table-column prop="chargeAmount" label="数量" sortable width="80"></el-table-column>
  66. <el-table-column prop="chargeFee" label="金额" width="80"></el-table-column>
  67. <el-table-column prop="chargeDate" label="收费日期"></el-table-column>
  68. <el-table-column prop="ybCode" :label="ybCodeLabel"></el-table-column>
  69. <el-table-column prop="chargeName" label="项目名称"></el-table-column>
  70. <el-table-column prop="ybSelfFlag" label="报销" width="80"></el-table-column>
  71. <el-table-column>
  72. <template #default="scope">
  73. <el-button circle v-if="!injuryMode && patient.mdtrtId && scope.row.chargeAmount < 0"
  74. @click="fixNegativeFeeUploadProblem(scope.row, 2)">
  75. <i class="iconfont icon-tools-hardware" style="font-size: 10px;"></i>
  76. </el-button>
  77. </template>
  78. </el-table-column>
  79. </el-table>
  80. <el-pagination
  81. @size-change="handleYpSizeChange"
  82. @current-change="handleCurrentYpChange"
  83. :current-page="page.ypPage"
  84. :page-sizes="[10, 30, 50, 70, 100, 300]"
  85. :page-size="page.ypPageSize"
  86. layout="total, sizes, prev, pager, next"
  87. :total="ypTotalSize"
  88. >
  89. </el-pagination>
  90. <div class="m-wrapper" v-show="showProgress">
  91. <div class="dj-center-box-wrapper">
  92. <div class="el-message-box" style="font-size: 13px">
  93. <div style="background: #409eff; color: white; padding: 7px 10px; font-weight: bold">
  94. <i class="el-icon-loading"></i>
  95. 上传进度
  96. </div>
  97. <div style="padding: 10px">
  98. <div style="margin-bottom: 10px">{{ uploadIndexText }} ...</div>
  99. <div style="margin-bottom: 10px" v-show="percentage === 100">上传结束,正在进行费用计算 ...</div>
  100. <el-progress :text-inside="true" :stroke-width="22" :percentage="percentage"
  101. status="success"></el-progress>
  102. <div style="height: 5px"></div>
  103. </div>
  104. </div>
  105. </div>
  106. </div>
  107. </div>
  108. <el-dialog v-model="weiGuiTuiFeiFenXiDialog" title="违规费用分析" :fullscreen="true">
  109. <wei-gui-fei-yong-fen-xi :init="weiGuiTuiFeiInit" @open="weiGuiTuiFeiOpenDialog" ref="weiGui"
  110. :patient="weiGuiJiBenXinXi"></wei-gui-fei-yong-fen-xi>
  111. </el-dialog>
  112. <MedfeeAnalyse v-if="showFeeDetl" type="unsettled" :mdtrt-id="patient.mdtrtId" @close="showFeeDetl = false"/>
  113. </template>
  114. <script>
  115. import store from '@/store'
  116. import {computed, onActivated, onDeactivated, onMounted, reactive, ref, watch} from 'vue'
  117. import {fetchNotUploadedFees, getPatientInfo} from '@/api/inpatient/patient'
  118. import {ElMessage, ElMessageBox} from 'element-plus'
  119. import {nullPatient} from '@/utils/validate'
  120. import {
  121. hospitalizationPreSettlement,
  122. multipleUpload,
  123. pairNegativeFee,
  124. revokeUploadFees,
  125. uploadFeeDetail
  126. } from '@/api/medical-insurance/si-inpatient'
  127. import {revokeInpatientCost, inpatientSettlement, inpatientCostUpload} from '@/api/medical-insurance/si-injury'
  128. import {setCallback} from '@/utils/websocket'
  129. import {getGreatestRole} from '@/utils/permission'
  130. import {baseinfo, setBaseinfo} from '@/data/inpatient'
  131. import WeiGuiFeiYongFenXi from '@/components/inpatient/WeiGuiFeiYongFenXi.vue'
  132. import MedfeeAnalyse from '../../../components/medical-insurance/medfee-analyse/Index.vue'
  133. import {clone} from "@/utils/clone";
  134. export default {
  135. components: {WeiGuiFeiYongFenXi, MedfeeAnalyse},
  136. setup() {
  137. const feeTableHeight = (store.state.app.windowSize.h - 200) / 2
  138. const injuryMode = computed(() => {
  139. return store.state.ptnt.injuryMode
  140. })
  141. const isAdmin = getGreatestRole() < 10
  142. const patient = computed(() => {
  143. return baseinfo()
  144. })
  145. const xmFeeSum = ref('0.00')
  146. const ypFeeSum = ref('0.00')
  147. const xmFeeList = ref([])
  148. const ypFeeList = ref([])
  149. const xmTotalSize = ref(0)
  150. const ypTotalSize = ref(0)
  151. const ybCodeLabel = computed(() => {
  152. return injuryMode.value ? '社保三大目录ID' : '国家医保编码'
  153. })
  154. const page = initPage()
  155. const fetchProjectFees = () => {
  156. const param = {
  157. patNo: patient.value.inpatientNo,
  158. times: patient.value.admissTimes,
  159. currentPage: page.xmPage,
  160. pageSize: page.xmPageSize,
  161. zdTable: 'zd_charge_item',
  162. injuryMode: injuryMode.value,
  163. }
  164. fetchNotUploadedFees(param).then((res) => {
  165. xmFeeList.value = res.list
  166. xmFeeSum.value = res.sum
  167. xmTotalSize.value = res.totalSize
  168. })
  169. }
  170. const fetchMedicineFees = () => {
  171. const param = {
  172. patNo: patient.value.inpatientNo,
  173. times: patient.value.admissTimes,
  174. currentPage: page.ypPage,
  175. pageSize: page.ypPageSize,
  176. zdTable: 'yp_zd_dict',
  177. injuryMode: injuryMode.value,
  178. }
  179. fetchNotUploadedFees(param).then((res) => {
  180. ypFeeList.value = res.list
  181. ypFeeSum.value = res.sum
  182. ypTotalSize.value = res.totalSize
  183. })
  184. }
  185. const clearFees = () => {
  186. xmFeeList.value = []
  187. ypFeeList.value = []
  188. xmFeeSum.value = '0.00'
  189. ypFeeSum.value = '0.00'
  190. xmTotalSize.value = 0
  191. ypTotalSize.value = 0
  192. }
  193. const activated = ref(false)
  194. onActivated(() => {
  195. activated.value = true
  196. store.commit('app/setCurrentPageName', 'inHospFeeUpload')
  197. setCallback('medInsFeeUploadProgress', socketCallback)
  198. })
  199. onDeactivated(() => {
  200. activated.value = false
  201. store.commit('app/setCurrentPageName', '')
  202. })
  203. watch(
  204. () => patient.value.inpatientNo,
  205. () => {
  206. if (activated.value) {
  207. if (patient.value.inpatientNo) {
  208. fetchProjectFees()
  209. fetchMedicineFees()
  210. weiGuiTuiFeiFenXiDialogOpen(false)
  211. } else {
  212. clearFees()
  213. }
  214. }
  215. }
  216. )
  217. const errorMessages = ref([])
  218. const clearErrorMessages = () => {
  219. errorMessages.value = []
  220. }
  221. const handleXmSizeChange = (val) => {
  222. page.xmPageSize = val
  223. fetchProjectFees()
  224. }
  225. const handleCurrentXmChange = (val) => {
  226. page.xmPage = val
  227. fetchProjectFees()
  228. }
  229. const handleYpSizeChange = (val) => {
  230. page.ypPageSize = val
  231. fetchMedicineFees()
  232. }
  233. const handleCurrentYpChange = (val) => {
  234. page.ypPage = val
  235. fetchMedicineFees()
  236. }
  237. const preCalculateCost = () => {
  238. if (nullPatient()) return
  239. excutePreCal().then(() => {
  240. getPatientInfo(patient.value.inpatientNo).then(res => {
  241. setBaseinfo(res)
  242. })
  243. })
  244. }
  245. const excutePreCal = () => {
  246. return new Promise((resolve, reject) => {
  247. if (injuryMode.value) {
  248. inpatientSettlement(patient.value).then((res) => {
  249. patient.value.chargeYb = res.fundPay
  250. ElMessageBox.alert(res, {
  251. type: 'success',
  252. confirmButtonText: '确定',
  253. }).then(() => {
  254. resolve()
  255. })
  256. })
  257. } else {
  258. hospitalizationPreSettlement(patient.value).then((res) => {
  259. ElMessageBox.alert(res, {
  260. type: 'success',
  261. confirmButtonText: '确定',
  262. }).then(() => {
  263. resolve()
  264. })
  265. })
  266. }
  267. })
  268. }
  269. const patientIndex = ref(1)
  270. const showProgress = ref(false)
  271. const percentage = ref(0)
  272. const selections = computed(() => {
  273. return store.state.ptnt.selections
  274. })
  275. const uploadFees = () => {
  276. if (selections.value.length > 0) {
  277. showProgress.value = true
  278. doMultipleUpload()
  279. } else {
  280. if (nullPatient()) return
  281. showProgress.value = true
  282. if (injuryMode.value) {
  283. doSingleInjuryUpload()
  284. } else {
  285. doSingleNormalUpload()
  286. }
  287. }
  288. }
  289. const doSingleInjuryUpload = () => {
  290. patient.value.sid = store.getters['user/sid']
  291. inpatientCostUpload(patient.value).then((res) => {
  292. fetchProjectFees()
  293. fetchMedicineFees()
  294. showProgress.value = false
  295. percentage.value = 0
  296. patient.value.chargeYb = res.fundPay
  297. ElMessageBox.alert(res, '成功', {
  298. type: 'success',
  299. confirmButtonText: '确定',
  300. })
  301. getPatientInfo(patient.value.inpatientNo).then(res => {
  302. setBaseinfo(res)
  303. })
  304. }).catch(() => {
  305. showProgress.value = false
  306. percentage.value = 0
  307. fetchProjectFees()
  308. fetchMedicineFees()
  309. })
  310. }
  311. const doSingleNormalUpload = () => {
  312. const param = {
  313. inpatientNo: patient.value.inpatientNo,
  314. admissTimes: patient.value.admissTimes,
  315. ledgerSn: patient.value.ledgerSn,
  316. sid: store.getters['user/sid'],
  317. }
  318. uploadFeeDetail(param).then((res) => {
  319. fetchProjectFees()
  320. fetchMedicineFees()
  321. showProgress.value = false
  322. percentage.value = 0
  323. ElMessageBox.alert(res, '成功', {
  324. type: 'success',
  325. confirmButtonText: '确定',
  326. })
  327. store.commit('app/closeJdt')
  328. getPatientInfo(patient.value.inpatientNo).then(res => {
  329. setBaseinfo(res)
  330. })
  331. }).catch(() => {
  332. fetchProjectFees()
  333. fetchMedicineFees()
  334. showProgress.value = false
  335. percentage.value = 0
  336. })
  337. }
  338. const doMultipleUpload = () => {
  339. let list = clone(selections.value)
  340. list.forEach((item) => {
  341. item.sid = store.getters['user/sid']
  342. })
  343. multipleUpload(list).then((res) => {
  344. ElMessage({
  345. message: '处理完成。',
  346. type: 'success',
  347. duration: 2500,
  348. showClose: true,
  349. })
  350. showProgress.value = false
  351. percentage.value = 0
  352. }).catch(() => {
  353. showProgress.value = false
  354. percentage.value = 0
  355. })
  356. }
  357. const socketCallback = (data) => {
  358. if (typeof data === 'string') {
  359. data = JSON.parse(data)
  360. }
  361. switch (data.name) {
  362. case 'uploadFeeResponse':
  363. errorMessages.value.push(data)
  364. break
  365. case 'updatePatientIndex':
  366. patientIndex.value = data.patientIndex
  367. percentage.value = 0
  368. break
  369. case 'updateProgress':
  370. percentage.value = data.percentage
  371. break
  372. default:
  373. break
  374. }
  375. }
  376. const uploadIndexText = computed(() => {
  377. let total = selections.value.length
  378. if (total === 0) {
  379. total = 1
  380. }
  381. return '共 ' + total + ' 人,正在处理第 ' + patientIndex.value + ' 人'
  382. })
  383. const cancelFees = () => {
  384. ElMessageBox.confirm('是否确定取消此患者已上传的费用?', '提示', {
  385. type: 'warning',
  386. confirmButtonText: '确定',
  387. cancelButtonText: '放弃',
  388. })
  389. .then(() => {
  390. if (injuryMode.value) {
  391. revokeInpatientCost(patient.value).then(() => {
  392. ElMessage({
  393. message: '操作成功。',
  394. type: 'success',
  395. duration: 2500,
  396. showClose: true,
  397. })
  398. fetchProjectFees()
  399. fetchMedicineFees()
  400. })
  401. } else {
  402. revokeUploadFees(patient.value).then(() => {
  403. ElMessage({
  404. message: '操作成功。',
  405. type: 'success',
  406. duration: 2500,
  407. showClose: true,
  408. })
  409. fetchProjectFees()
  410. fetchMedicineFees()
  411. })
  412. }
  413. })
  414. .catch(() => {
  415. })
  416. }
  417. const showFeeDetl = ref(false)
  418. const feeDtle = () => {
  419. if (nullPatient()) {
  420. return
  421. }
  422. showFeeDetl.value = true
  423. }
  424. const fixNegativeFeeUploadProblem = (row, flag) => {
  425. pairNegativeFee(row).then((res) => {
  426. ElMessage({
  427. message: res,
  428. type: 'success',
  429. duration: 2500,
  430. showClose: true
  431. })
  432. flag === 1 ? fetchProjectFees() : fetchMedicineFees()
  433. })
  434. }
  435. ///////////////////////////////////////////// 违规退费分析 /////////////////////////////////////////////////////////////////////////
  436. const weiGuiTuiFeiInit = ref(0)
  437. const weiGuiTuiFeiFenXiDialog = ref(true)
  438. const weiGuiJiBenXinXi = ref({})
  439. const weiGui = ref()
  440. const weiGuiTuiFeiFenXiDialogOpen = (val) => {
  441. weiGuiTuiFeiInit.value += 1
  442. weiGuiJiBenXinXi.value.deptCode = ''
  443. weiGuiJiBenXinXi.value.inpatientNo = typeof patient.value.inpatientNo === 'undefined' ? '' : patient.value.inpatientNo
  444. weiGuiJiBenXinXi.value.openDialog = val
  445. }
  446. const weiGuiTuiFeiOpenDialog = (val) => {
  447. weiGuiTuiFeiFenXiDialog.value = val
  448. }
  449. onMounted(() => {
  450. if (patient.value.inpatientNo) {
  451. fetchProjectFees()
  452. fetchMedicineFees()
  453. }
  454. })
  455. return {
  456. isAdmin,
  457. patient,
  458. injuryMode,
  459. errorMessages,
  460. clearErrorMessages,
  461. page,
  462. handleXmSizeChange,
  463. handleCurrentXmChange,
  464. handleYpSizeChange,
  465. handleCurrentYpChange,
  466. preCalculateCost,
  467. percentage,
  468. uploadFees,
  469. showProgress,
  470. patientIndex,
  471. uploadIndexText,
  472. cancelFees,
  473. feeDtle,
  474. showFeeDetl,
  475. feeTableHeight,
  476. weiGuiTuiFeiFenXiDialog,
  477. weiGuiTuiFeiFenXiDialogOpen,
  478. fixNegativeFeeUploadProblem,
  479. weiGuiJiBenXinXi,
  480. weiGuiTuiFeiInit,
  481. weiGuiTuiFeiOpenDialog,
  482. weiGui,
  483. xmFeeSum,
  484. ypFeeSum,
  485. xmFeeList,
  486. ypFeeList,
  487. xmTotalSize,
  488. ypTotalSize,
  489. ybCodeLabel
  490. }
  491. },
  492. }
  493. function initPage() {
  494. return reactive({
  495. xmPage: 1,
  496. xmPageSize: 10,
  497. ypPage: 1,
  498. ypPageSize: 10,
  499. })
  500. }
  501. </script>
  502. <style scoped>
  503. .m-wrapper {
  504. position: fixed;
  505. top: 0;
  506. left: 0;
  507. right: 0;
  508. bottom: 0;
  509. z-index: 8888;
  510. background-color: rgba(0, 0, 0, 0.6);
  511. }
  512. .dj-center-box-wrapper {
  513. position: fixed;
  514. inset: 0px;
  515. text-align: center;
  516. }
  517. .dj-center-box-wrapper::after {
  518. content: '';
  519. display: inline-block;
  520. height: 100%;
  521. width: 0px;
  522. vertical-align: middle;
  523. }
  524. </style>