selectRefundCardList.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410
  1. <template>
  2. <view class="container">
  3. <view class="content">
  4. <!-- <cardPanel>
  5. <view slot="header">
  6. <view class="text-primary display-flex__ac btn-record">申请退款记录</view>
  7. <view class="btn_arrow"></view>
  8. </view>
  9. </cardPanel> -->
  10. <view class="white-panel" @click="toRecord">
  11. <view class="text-primary display-flex__ac btn-record">申请退款记录</view>
  12. <view class="btn_arrow"></view>
  13. </view>
  14. <!-- 就诊人卡片 -->
  15. <view class="white-panel no-padding" v-for="(item, index) in memberList" :key="index">
  16. <view class="panel-header">
  17. <view class="title-main display-flex__ac">
  18. <view class="text-color__dominant font-bold">{{ item.memberName }}</view>
  19. <view class="text-color__secondary" style="margin-left: 20upx; font-size: 30upx;">{{ item.mobile }}</view>
  20. </view>
  21. <view class="title-sub display-flex__ac">
  22. <view class="text-color__secondary">{{ item.certTypeName }}:{{ item.certNum }}</view>
  23. </view>
  24. </view>
  25. <!-- 分割线 -->
  26. <cardDiv colorLine="#eee" colorGap="#f0f1f6"></cardDiv>
  27. <view class="panel-main">
  28. <!-- 卡列表 -->
  29. <view class="card-list">
  30. <view class="card-list__item display-flex__ac displayFlexBetween" v-for="(cardItem, cardIndex) in item.cardList" :key="cardIndex">
  31. <view>就诊卡:{{ cardFormatter(cardItem.CardNo) }}</view>
  32. <view>余额:{{ amountFormatter(cardItem.Balance) / 100 || 0 }}元</view>
  33. </view>
  34. </view>
  35. </view>
  36. <view class="panel-footer">
  37. <view class="public_btn backgroundCustom" @click="selectRefundChannel(item)">退款</view>
  38. </view>
  39. </view>
  40. </view>
  41. </view>
  42. <!-- 遮罩层 -->
  43. <view class="mask" v-if="showModalStatus"></view>
  44. <!-- 弹窗 -->
  45. <view class="modal" :style="{ animation: showModalStatus ? 'slideIn 0.3s' : 'slideOut 0.3s' }" v-if="showModalStatus">
  46. <!-- 弹窗头部 -->
  47. <view class="modal-header">
  48. <text>门诊历史预缴金退款申请需知</text>
  49. </view>
  50. <!-- 弹窗内容 -->
  51. <view class="modal-content">
  52. <view class="p">尊敬的患者:</view>
  53. <view class="p text-ident">
  54. 此功能针对退款对象为医院就诊卡有账户余额的患者。
  55. </view>
  56. <view class="p">
  57. 1.原路退的方式仅支持近期在线上进行充值的患者。如果原路退的可退金额为0,请选择转账退费。
  58. </view>
  59. <view class="p">
  60. 2.转账退支持银行卡转账、微信/支付宝转账方式。如果无法提供转账退费所需要的信息,请您携带就诊卡及就诊人的身份证件,到医院收费处窗口办理。
  61. </view>
  62. <view class="p">
  63. 3、改善就医感受、提升患者体验,我们一直在努力!感谢您的支持与配合!
  64. </view>
  65. <view class="p">
  66. 友情提醒:为了保障您预缴金的资金安全,线上转账退申请登记完成后,医院将进行基本信息审核,核验无误后为您办理退款。办理时间约30个工作日,请您耐心等待,有疑问欢迎在工作时间拨打电话 0594-00000;
  67. </view>
  68. </view>
  69. <!-- 弹窗底部按钮区域 -->
  70. <view class="modal-footer">
  71. <!-- Note: Original bindtap="selectRefundChannel" but passed no arg? -->
  72. <!-- Original: bindtap="selectRefundChannel". In selectRefundChannel(e), it uses e.currentTarget.dataset.item. -->
  73. <!-- But here in modal footer, there is no dataset.item. -->
  74. <!-- However, the modal seems to confirm the selection? -->
  75. <!-- Wait, looking at original WXML: -->
  76. <!-- <view class="btn btn-confirm" bindtap="selectRefundChannel">确认</view> -->
  77. <!-- But selectRefundChannel(e) expects e.currentTarget.dataset.item. -->
  78. <!-- If called from modal, item is undefined. -->
  79. <!-- app.globalData.queryBean = item; -->
  80. <!-- If item is undefined, queryBean is undefined. -->
  81. <!-- Then common.goToUrl(...) -->
  82. <!-- This seems like a bug or incomplete logic in the original code, or I missed something. -->
  83. <!-- Ah, the modal is shown when? -->
  84. <!-- showModalStatus is false initially. -->
  85. <!-- There is NO code that sets showModalStatus to true in the original JS file! -->
  86. <!-- So the modal is never shown? -->
  87. <!-- Line 16: showModalStatus: false -->
  88. <!-- No usage of setData({showModalStatus: true}). -->
  89. <!-- So the modal code is dead code or unfinished. -->
  90. <!-- I will implement it as is (hidden), but fix the click handler to avoid error if it were to be shown. -->
  91. <!-- Actually, I should just replicate the original logic. If original calls selectRefundChannel, I call it. -->
  92. <!-- But I can't pass 'item' if it's not available. -->
  93. <!-- I'll just bind @click="selectRefundChannel(null)" and handle null in the function or just let it fail/pass undefined as original. -->
  94. <!-- Wait, if I pass null, item is null. -->
  95. <view class="btn btn-confirm" @click="selectRefundChannel(null)">确认</view>
  96. </view>
  97. </view>
  98. </template>
  99. <script setup lang="ts">
  100. import { ref } from 'vue';
  101. import { onLoad, onShow } from '@dcloudio/uni-app';
  102. import { REQUEST_CONFIG } from '@/config';
  103. import { request, handle } from '@kasite/uni-app-base';
  104. import { common } from '@/utils';
  105. import cardDiv from '../components/cardDiv/cardDiv.vue';
  106. const app = getApp();
  107. const currentUser = ref<any>({});
  108. const memberList = ref<any[]>([]);
  109. const showModalStatus = ref(false);
  110. const amountFormatter = (val: any) => {
  111. if (!val) return 0;
  112. return Number(val);
  113. };
  114. const cardFormatter = (val: string) => {
  115. if (!val) return '';
  116. if (val.length !== 36) { // 非现金卡
  117. return val;
  118. }
  119. // 现金卡
  120. return val.substring(5, 13);
  121. };
  122. onLoad((options) => {
  123. initPageParam(options);
  124. });
  125. onShow(async () => {
  126. await getMemberList(); // 获取就诊人列表
  127. await buildQueue_getMemberCardList(); // 获取卡列表及卡余额
  128. });
  129. /**
  130. * 构建请求队列-获取患者就诊卡列表
  131. */
  132. const buildQueue_getMemberCardList = async () => {
  133. if (common.isEmpty(memberList.value)) {
  134. return;
  135. }
  136. const reqArr = memberList.value.map(async (item) => {
  137. item.cardList = await getMemberCardList(item);
  138. return item;
  139. });
  140. await Promise.all(reqArr);
  141. // No need to setData, memberList is reactive
  142. };
  143. /**
  144. * 获取就诊人列表
  145. */
  146. const getMemberList = async () => {
  147. // const resp = await publicFn.getMember('memberList');
  148. const queryData = {
  149. isCache: false,
  150. isEncrypt: true,
  151. hosId: app.globalData.districtId || app.globalData.hosId,
  152. openid: uni.getStorageSync('openid'),
  153. mobileFormatDesensitization: "false",
  154. memberNameFormatDesensitization: "false",
  155. certNumFormatDesensitization: "false",
  156. cardNoFormatDesensitization: "false"
  157. };
  158. let resp = handle.promistHandleNew(
  159. await request.doPost(
  160. `${REQUEST_CONFIG.BASE_URL}wsgw/accountMember/api/QueryBaseMemberList_V3/callApiJSON.do`,
  161. queryData
  162. )
  163. );
  164. const { resp: listResp } = handle.catchPromiseNew(resp, () => resp);
  165. // 只构建需要的结构,下列明文展示的就是当前业务逻辑所需的所有参数
  166. const list = (listResp || []).map((item: any) => ({
  167. memberId: item.memberId, // memberId
  168. memberName: item.memberName, // 姓名
  169. mobile: item.mobile, // 联系电话
  170. certType: item.certType, // 证件类型编码
  171. certTypeName: item.certTypeName, // 证件类型名称
  172. certNum: item.certNum, // 证件号
  173. baseMemberEncryptionStore: item.baseMemberEncryptionStore, // 加密串
  174. cardList: [], // 卡列表
  175. }));
  176. memberList.value = list || [];
  177. };
  178. /**
  179. * 获取就诊人的就诊卡
  180. */
  181. const getMemberCardList = async (data: any) => {
  182. const reqData = {
  183. BaseMemberEncryptionStore: data.baseMemberEncryptionStore,
  184. MemberId: data.memberId,
  185. IsQueryHis: 1, // 就诊卡列表是否实时查HIS(0——本地(旧版),1——查HIS(新版))
  186. };
  187. let resp = handle.promistHandleNew(
  188. await request.doPost(
  189. `${REQUEST_CONFIG.BASE_URL}wsgw/accountMember/api/QueryMemberCardListAdnCardBalance/callApiJSON.do`,
  190. reqData,
  191. {
  192. showModal: false,
  193. }
  194. )
  195. );
  196. const { resData, resp: listResp } = handle.catchPromiseNew(resp, () => resp);
  197. if (resData && resData.RespCode != "10000") {
  198. return [];
  199. }
  200. return listResp || [];
  201. };
  202. /**
  203. * 查看申请记录
  204. */
  205. const toRecord = () => {
  206. // 旧
  207. // common.goToUrl(`/pagesPatient/st1/business/refund/refundApplicationRecord/refundApplicationRecord`, { skipWay: "navigateTo", data: "" });
  208. // 新
  209. common.goToUrl(`/pagesPatient/st1/business/outpatient/outpatientRefundNew/refundRecord/refundRecord`, { skipWay: "navigateTo", data: "" });
  210. };
  211. /**
  212. * 前往选择退款方式页面
  213. */
  214. const selectRefundChannel = (item: any) => {
  215. // Original logic:
  216. // const { item } = e.currentTarget.dataset;
  217. // If called from modal, item might be null/undefined.
  218. // In Vue, we pass item directly from v-for.
  219. if (item) {
  220. app.globalData.queryBean = item;
  221. }
  222. common.goToUrl(`/pagesPatient/st1/business/outpatient/outpatientRefundNew/selectRefundChannel/selectRefundChannel`);
  223. };
  224. /**处理页面入参 */
  225. const initPageParam = (options: any) => {
  226. currentUser.value = getApp().globalData.currentUser;
  227. };
  228. </script>
  229. <style>
  230. @import "../static/css/refund.wxss";
  231. .white-panel {
  232. padding: 30upx 40upx;
  233. }
  234. .white-panel .panel-header {
  235. width: 100%;
  236. padding: 30upx 40upx 0;
  237. min-height: 60upx;
  238. height: fit-content;
  239. }
  240. .white-panel .panel-main {
  241. width: 100%;
  242. padding: 0upx 40upx;
  243. /* min-height: 100upx; */
  244. height: fit-content;
  245. overflow: hidden;
  246. }
  247. .white-panel .panel-footer {
  248. width: 100%;
  249. padding: 40upx 40upx;
  250. }
  251. .btn-record {
  252. font-size: 30upx;
  253. }
  254. .title-main {
  255. font-size: 34upx;
  256. height: 60upx;
  257. }
  258. .title-sub {
  259. color: #4D4D53;
  260. margin-top: 20upx;
  261. }
  262. /* 卡列表 */
  263. .card-list{}
  264. .card-list .card-list__item {
  265. width: 100%;
  266. height: 90upx;
  267. border-radius: 14upx;
  268. background-color: #F7F7F9;
  269. padding: 20upx;
  270. }
  271. .card-list .card-list__item + .card-list__item {
  272. margin-top: 30upx;
  273. }
  274. /* 弹窗 */
  275. /* 遮罩层样式 */
  276. .mask {
  277. position: fixed;
  278. top: 0;
  279. left: 0;
  280. width: 100%;
  281. height: 100%;
  282. background-color: rgba(0, 0, 0, 0.5);
  283. z-index: 1000;
  284. }
  285. /* 弹窗样式 */
  286. .modal {
  287. position: fixed;
  288. top: 50%;
  289. left: 50%;
  290. transform: translate(-50%, -50%);
  291. width: 80%;
  292. max-height: 80vh;
  293. background-color: #fff;
  294. border-radius: 8px;
  295. overflow: hidden;
  296. z-index: 1001;
  297. display: flex;
  298. flex-direction: column;
  299. }
  300. /* 弹窗头部样式 */
  301. .modal-header {
  302. padding: 30upx 40upx ;
  303. border-bottom: 1px solid #e5e5e5;
  304. font-size: 34upx;
  305. font-weight: bold;
  306. text-align: center;
  307. }
  308. /* 弹窗内容样式 */
  309. .modal-content {
  310. padding: 15px;
  311. flex: 1;
  312. overflow-y: auto;
  313. }
  314. .p {
  315. font-size: 30upx;
  316. line-height: 50upx;
  317. text-align: justify;
  318. }
  319. /* 弹窗底部样式 */
  320. .modal-footer {
  321. border-top: 1px solid #e5e5e5;
  322. text-align: right;
  323. height: 100upx;
  324. display: flex;
  325. align-items: stretch;
  326. justify-content: stretch;
  327. }
  328. .btn {
  329. flex: 1 0 0;
  330. display: flex;
  331. align-items: center;
  332. justify-content: center;
  333. }
  334. .btn:active {
  335. backdrop-filter: brightness(0.86);
  336. }
  337. .btn + .btn {
  338. border-left: 2upx solid #e5e5e5;
  339. }
  340. /* 弹窗动画 */
  341. @keyframes slideIn {
  342. from {
  343. opacity: 0;
  344. transform: translate(-50%, -40%);
  345. }
  346. to {
  347. opacity: 1;
  348. transform: translate(-50%, -50%);
  349. }
  350. }
  351. @keyframes slideOut {
  352. from {
  353. opacity: 1;
  354. transform: translate(-50%, -50%);
  355. }
  356. to {
  357. opacity: 0;
  358. transform: translate(-50%, -40%);
  359. }
  360. }
  361. </style>