InHospFeeUpload.vue 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490
  1. <template>
  2. <el-container>
  3. <el-header height="35px">
  4. <el-button icon="el-icon-thumb" type="success" @click="preCalculateCost">获取费用</el-button>
  5. <el-button icon="el-icon-upload2" type="success" @click="uploadFees">费用上传</el-button>
  6. <el-button icon="el-icon-remove-outline" type="warning" :disabled="!isAdmin" @click="cancelFees">取消上传</el-button>
  7. <el-button type="primary" @click="weiGuiTuiFeiFenXiDialogOpen">违规费用分析</el-button>
  8. <el-popover placement="left" width="730" trigger="click">
  9. <template #reference>
  10. <el-button type="warning" icon="el-icon-circle-close" 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="inpatientNo" label="住院号"></el-table-column>
  17. <el-table-column width="80" property="patientName" label="姓名"></el-table-column>
  18. <el-table-column width="80" property="detailSn" label="流水号"></el-table-column>
  19. <el-table-column width="450" property="msg" label="错误详情"></el-table-column>
  20. </el-table>
  21. </el-popover>
  22. &nbsp;&nbsp;
  23. <el-tag type="success">
  24. 总费用:<span style="color: black">¥ {{ patient.totalCharge }}</span>
  25. ,医保报销:
  26. <span style="color: orangered">¥ {{ patient.chargeYb }}</span>
  27. </el-tag>
  28. </el-header>
  29. <el-main>
  30. <el-tag type="info">治疗明细</el-tag><el-tag>合计:¥ {{ fees.xmSum }}</el-tag>
  31. <el-table :data="fees.xm" 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="医保码"></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>
  41. <el-pagination
  42. @size-change="handleXmSizeChange"
  43. @current-change="handleCurrentXmChange"
  44. :current-page="page.xmPage"
  45. :page-sizes="[10, 30, 50, 70, 100, 300]"
  46. :page-size="page.xmPageSize"
  47. layout="total, sizes, prev, pager, next"
  48. :total="fees.xmTotal"
  49. >
  50. </el-pagination>
  51. <div style="height: 5px"></div>
  52. <el-tag type="info">药品明细</el-tag><el-tag>合计:¥ {{ fees.ypSum }}</el-tag>
  53. <el-table :data="fees.yp" stripe :height="feeTableHeight">
  54. <el-table-column prop="detailSn" label="流水号" width="80"></el-table-column>
  55. <el-table-column prop="chargeCodeMx" label="院内码" width="100"></el-table-column>
  56. <el-table-column prop="chargeAmount" label="数量" sortable width="80"></el-table-column>
  57. <el-table-column prop="chargeFee" label="金额" width="80"></el-table-column>
  58. <el-table-column prop="chargeDate" label="收费日期"></el-table-column>
  59. <el-table-column prop="ybCode" label="医保码"></el-table-column>
  60. <el-table-column prop="chargeName" label="项目名称"></el-table-column>
  61. <el-table-column prop="ybSelfFlag" label="是否报销" width="80"></el-table-column>
  62. </el-table>
  63. <el-pagination
  64. @size-change="handleYpSizeChange"
  65. @current-change="handleCurrentYpChange"
  66. :current-page="page.ypPage"
  67. :page-sizes="[10, 30, 50, 70, 100, 300]"
  68. :page-size="page.ypPageSize"
  69. layout="total, sizes, prev, pager, next"
  70. :total="fees.ypTotal"
  71. >
  72. </el-pagination>
  73. <div class="dj-center-box-wrapper" style="z-index: 10000" v-show="showProofread">
  74. <div class="el-message-box" style="padding: 10px; text-align: center; font-size: 15px">
  75. <h3>校对消息</h3>
  76. <div :key="msg" v-for="msg in proofreadMsgs" style="margin-top: 5px">
  77. {{ msg }}
  78. </div>
  79. </div>
  80. </div>
  81. <div class="m-wrapper" v-show="showProgress">
  82. <div class="dj-center-box-wrapper">
  83. <div class="el-message-box" style="font-size: 13px">
  84. <div style="background: #409eff; color: white; padding: 7px 10px; font-weight: bold">
  85. <i class="el-icon-loading"></i>
  86. 上传进度
  87. </div>
  88. <div style="padding: 10px">
  89. <div style="margin-bottom: 10px">{{ uploadIndexText }} ...</div>
  90. <div style="margin-bottom: 10px" v-show="percentage === 100">上传结束,正在进行费用计算 ...</div>
  91. <el-progress :text-inside="true" :stroke-width="22" :percentage="percentage" status="success"></el-progress>
  92. <div style="height: 5px"></div>
  93. </div>
  94. </div>
  95. </div>
  96. </div>
  97. </el-main>
  98. <el-dialog v-model="weiGuiTuiFeiFenXiDialog" title="违规费用分析" :fullscreen="true">
  99. <wei-gui-fei-yong-fen-xi :init="weiGuiTuiFeiInit" @shuaXin="weiGuiTuiFeiFenXiDialogOpen" :patient="weiGuiJiBenXinXi"></wei-gui-fei-yong-fen-xi>
  100. </el-dialog>
  101. </el-container>
  102. </template>
  103. <script>
  104. import store from '@/store'
  105. import { computed, onActivated, onDeactivated, onMounted, reactive, ref, watchEffect } from 'vue'
  106. import { fetchNotUploadedFees } from '@/api/yibao/patient'
  107. import { ElMessage, ElMessageBox, ElNotification } from 'element-plus'
  108. import { nullPatient } from '@/utils/validate'
  109. import { proofread, multipleUpload } from '@/api/yibao/yibao'
  110. import { hospitalizationPreSettlement, uploadFeeDetail, revokeUploadFees } from '@/api/medical-insurance/si-inpatient'
  111. import { setCallback } from '@/utils/websocket'
  112. import { getGreatestRole } from '@/utils/permission'
  113. import { baseinfo } from '@/data/inpatient'
  114. import { weiGuiFeiYongFenXi } from '@/api/yibao/xiang-mu-lu-ru'
  115. import WeiGuiFeiYongFenXi from '../../../components/inpatient/WeiGuiFeiYongFenXi.vue'
  116. export default {
  117. components: { WeiGuiFeiYongFenXi },
  118. setup() {
  119. const feeTableHeight = (store.state.app.windowSize.h - 200) / 2
  120. const isAdmin = getGreatestRole() < 10
  121. const patient = computed(() => {
  122. return baseinfo()
  123. })
  124. const fees = initFees()
  125. const page = initPage()
  126. const fetchProjectFees = () => {
  127. const param = {
  128. patNo: patient.value.inpatientNo,
  129. times: patient.value.admissTimes,
  130. currentPage: page.xmPage,
  131. pageSize: page.xmPageSize,
  132. zdTable: 'zd_charge_item',
  133. }
  134. fetchNotUploadedFees(param).then((res) => {
  135. fees.xm = res.list
  136. fees.xmSum = res.sum
  137. fees.xmTotal = res.totalSize
  138. })
  139. }
  140. const fetchMedicineFees = () => {
  141. const param = {
  142. patNo: patient.value.inpatientNo,
  143. times: patient.value.admissTimes,
  144. currentPage: page.ypPage,
  145. pageSize: page.ypPageSize,
  146. zdTable: 'yp_zd_dict',
  147. }
  148. fetchNotUploadedFees(param).then((res) => {
  149. fees.yp = res.list
  150. fees.ypSum = res.sum
  151. fees.ypTotal = res.totalSize
  152. })
  153. }
  154. const clearFees = () => {
  155. fees.xm = []
  156. fees.yp = []
  157. fees.xmSum = '0.00'
  158. fees.ypSum = '0.00'
  159. fees.xmTotal = 0
  160. fees.ypTotal = 0
  161. }
  162. const actived = ref(false)
  163. onActivated(() => {
  164. actived.value = true
  165. store.commit('app/setCurrentPageName', 'inHospFeeUpload')
  166. })
  167. onDeactivated(() => {
  168. actived.value = false
  169. store.commit('app/setCurrentPageName', '')
  170. })
  171. watchEffect(() => {
  172. if (actived.value) {
  173. if (patient.value.inpatientNo) {
  174. fetchProjectFees()
  175. fetchMedicineFees()
  176. weiGuiTuiFeiFenXiDialogOpen()
  177. } else {
  178. clearFees()
  179. }
  180. }
  181. })
  182. const errorMessages = ref([])
  183. const clearErrorMessages = () => {
  184. errorMessages.value = []
  185. }
  186. const handleXmSizeChange = (val) => {
  187. page.xmPageSize = val
  188. fetchProjectFees()
  189. }
  190. const handleCurrentXmChange = (val) => {
  191. page.xmPage = val
  192. fetchProjectFees()
  193. }
  194. const handleYpSizeChange = (val) => {
  195. page.ypPageSize = val
  196. fetchMedicineFees()
  197. }
  198. const handleCurrentYpChange = (val) => {
  199. page.ypPage = val
  200. fetchMedicineFees()
  201. }
  202. const preCalculateCost = () => {
  203. if (nullPatient()) return
  204. hospitalizationPreSettlement(patient.value).then((res) => {
  205. patient.value.chargeYb = res.fundPay
  206. ElMessageBox.alert(res, {
  207. type: 'success',
  208. confirmButtonText: '确定',
  209. })
  210. })
  211. }
  212. const patientIndex = ref(1)
  213. const showProgress = ref(false)
  214. const showProofread = ref(false)
  215. const percentage = ref(0)
  216. const proofreadMsgs = ref([])
  217. const startProofread = () => {
  218. proofreadMsgs.value = []
  219. showProofread.value = true
  220. const data = {
  221. sid: store.getters['user/sid'],
  222. inpatientNo: patient.value.inpatientNo,
  223. admissTimes: patient.value.admissTimes,
  224. ybJlh: patient.value.ybJlh,
  225. }
  226. proofread(data)
  227. .then((res) => {
  228. percentage.value = 0
  229. showProofread.value = false
  230. ElMessageBox.alert('患者【' + patient.value.name + '】院内总费用与医保中心总费用一致,医保报销金额为:¥ ' + res.fundPay + '。', '成功', {
  231. type: 'success',
  232. confirmButtonText: '确定',
  233. }).then(() => {
  234. fetchProjectFees()
  235. fetchMedicineFees()
  236. })
  237. proofreadMsgs.value = []
  238. })
  239. .catch(() => {
  240. showProofread.value = false
  241. })
  242. }
  243. const selections = computed(() => {
  244. return store.state.ptnt.selections
  245. })
  246. const uploadFees = () => {
  247. if (selections.value.length === 0) {
  248. if (nullPatient()) return
  249. }
  250. showProgress.value = true
  251. if (selections.value.length > 1) {
  252. doMultipleUpload()
  253. } else {
  254. doSingleUpload()
  255. }
  256. }
  257. const doMultipleUpload = () => {
  258. selections.value.forEach((item) => {
  259. item.sid = store.getters['user/sid']
  260. })
  261. multipleUpload(selections.value)
  262. .then((res) => {
  263. ElMessage({
  264. message: '处理完成。',
  265. type: 'success',
  266. duration: 2500,
  267. showClose: true,
  268. })
  269. showProgress.value = false
  270. percentage.value = 0
  271. })
  272. .catch(() => {
  273. showProgress.value = false
  274. percentage.value = 0
  275. })
  276. }
  277. const doSingleUpload = () => {
  278. const param = {
  279. inpatientNo: patient.value.inpatientNo,
  280. admissTimes: patient.value.admissTimes,
  281. ledgerSn: patient.value.ledgerSn,
  282. sid: store.getters['user/sid'],
  283. }
  284. uploadFeeDetail(param)
  285. .then((res) => {
  286. fetchProjectFees()
  287. fetchMedicineFees()
  288. showProgress.value = false
  289. percentage.value = 0
  290. if (res && res.code === 2002) {
  291. ElMessageBox.confirm(res.message + '是否进行校对?', '提示', {
  292. type: 'warning',
  293. confirmButtonText: '进行校对',
  294. cancelButtonText: '暂不校对',
  295. })
  296. .then(() => {
  297. startProofread()
  298. })
  299. .catch(() => {})
  300. } else {
  301. patient.value.chargeYb = res.fundPay
  302. ElMessageBox.alert(res, '成功', {
  303. type: 'success',
  304. confirmButtonText: '确定',
  305. })
  306. }
  307. })
  308. .catch(() => {
  309. fetchProjectFees()
  310. fetchMedicineFees()
  311. showProgress.value = false
  312. percentage.value = 0
  313. })
  314. }
  315. const socketCallback = (data) => {
  316. switch (data.name) {
  317. case 'uploadFeeResponse':
  318. errorMessages.value.push(data)
  319. break
  320. case 'updatePatientIndex':
  321. patientIndex.value = data.val
  322. percentage.value = 0
  323. break
  324. case 'updateProgress':
  325. percentage.value = data.percentage
  326. break
  327. case 'proofread':
  328. proofreadMsgs.value.push(data.msg)
  329. percentage.value = 0
  330. break
  331. default:
  332. break
  333. }
  334. }
  335. const uploadIndexText = computed(() => {
  336. let total = selections.value.length
  337. if (total === 0) {
  338. total = 1
  339. }
  340. return '共 ' + total + ' 人,正在处理第 ' + patientIndex.value + ' 人'
  341. })
  342. const cancelFees = () => {
  343. ElMessageBox.confirm('是否确定取消此患者已上传的费用?', '提示', {
  344. type: 'warning',
  345. confirmButtonText: '确定',
  346. cancelButtonText: '放弃',
  347. })
  348. .then(() => {
  349. revokeUploadFees(patient.value).then(() => {
  350. ElMessage({
  351. message: '操作成功。',
  352. type: 'success',
  353. duration: 2500,
  354. showClose: true,
  355. })
  356. fetchProjectFees()
  357. fetchMedicineFees()
  358. })
  359. })
  360. .catch(() => {})
  361. }
  362. ///////////////////////////////////////////// 违规退费分析 /////////////////////////////////////////////////////////////////////////
  363. const weiGuiTuiFeiInit = ref(0)
  364. const weiGuiTuiFeiFenXiDialog = ref(false)
  365. const weiGuiJiBenXinXi = ref({})
  366. const weiGuiTuiFeiFenXiDialogOpen = () => {
  367. if (nullPatient()) return
  368. weiGuiFeiYongFenXi(patient.value.inpatientNo, patient.value.admissTimes, '').then((res) => {
  369. if (res.weiXieDaiYuanLiuShui.length > 0 || res.weiPiPei.length > 0) {
  370. weiGuiTuiFeiFenXiDialog.value = true
  371. weiGuiJiBenXinXi.value.inpatientNo = patient.value.inpatientNo
  372. weiGuiJiBenXinXi.value.admissTimes = patient.value.admissTimes
  373. weiGuiJiBenXinXi.value.name = patient.value.name
  374. weiGuiJiBenXinXi.value.pageSize = 20
  375. weiGuiJiBenXinXi.value.currentPage = 1
  376. }
  377. weiGuiTuiFeiInit.value += 1
  378. weiGuiJiBenXinXi.value.weiPiPei = res.weiPiPei
  379. weiGuiJiBenXinXi.value.weiXieDaiYuanLiuShui = res.weiXieDaiYuanLiuShui
  380. })
  381. }
  382. onMounted(() => {
  383. setCallback(socketCallback)
  384. if (patient.value.inpatientNo) {
  385. fetchProjectFees()
  386. fetchMedicineFees()
  387. }
  388. ElNotification({
  389. title: '提示',
  390. dangerouslyUseHTMLString: true,
  391. type: 'success',
  392. message: `
  393. 1、如果上传不成功或者费用不一致,那么请医保科取消上传,重新上传费用。<br>
  394. 2、退药品的话(长期医嘱撤销就可以了,如果不能撤销提单子)(临嘱的话使用医保入院登记里面的【医嘱退费】,然后护士执行这个条医嘱就可以了)。<br>
  395. 3、全部的项目都要用本系统来退费。<br>你们终于可以不用向信息科打电话了 ヾ(✿゚▽゚)ノ`,
  396. })
  397. })
  398. return {
  399. isAdmin,
  400. patient,
  401. fees,
  402. errorMessages,
  403. clearErrorMessages,
  404. page,
  405. handleXmSizeChange,
  406. handleCurrentXmChange,
  407. handleYpSizeChange,
  408. handleCurrentYpChange,
  409. preCalculateCost,
  410. showProofread,
  411. proofreadMsgs,
  412. percentage,
  413. uploadFees,
  414. showProgress,
  415. patientIndex,
  416. uploadIndexText,
  417. cancelFees,
  418. feeTableHeight,
  419. weiGuiTuiFeiFenXiDialog,
  420. weiGuiTuiFeiFenXiDialogOpen,
  421. weiGuiJiBenXinXi,
  422. weiGuiTuiFeiInit,
  423. }
  424. },
  425. }
  426. function initFees() {
  427. const fees = reactive({
  428. xmSum: '0.00',
  429. ypSum: '0.00',
  430. xm: [],
  431. yp: [],
  432. xmTotal: 0,
  433. ypTotal: 0,
  434. })
  435. return fees
  436. }
  437. function initPage() {
  438. const page = reactive({
  439. xmPage: 1,
  440. xmPageSize: 10,
  441. ypPage: 1,
  442. ypPageSize: 10,
  443. })
  444. return page
  445. }
  446. </script>
  447. <style scoped>
  448. .m-wrapper {
  449. position: fixed;
  450. top: 0;
  451. left: 0;
  452. right: 0;
  453. bottom: 0;
  454. z-index: 10000;
  455. background-color: rgba(0, 0, 0, 0.6);
  456. }
  457. .dj-center-box-wrapper {
  458. position: fixed;
  459. inset: 0px;
  460. text-align: center;
  461. }
  462. .dj-center-box-wrapper::after {
  463. content: '';
  464. display: inline-block;
  465. height: 100%;
  466. width: 0px;
  467. vertical-align: middle;
  468. }
  469. </style>