outpatientMedical.vue 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418
  1. <template>
  2. <view class="container">
  3. <view class="content">
  4. <screening
  5. class="screen"
  6. @dateChange="dateChange"
  7. @screenInfoChange="screenInfoChange"
  8. :queryData="queryData"
  9. showMemberList="cardAndName"
  10. :showAllMember="false"
  11. ></screening>
  12. <view class="content_inner" v-if="!showNoData">
  13. <view class="public_con" v-for="(item, index) in list" :key="index" @click="itemClick(item)">
  14. <view class="public_con_nav border_bottom">
  15. <view class="public_con_nav_tit">
  16. {{memberName}}
  17. </view>
  18. <image class="public_line" src="https://zygzh.fyey.cn/uploadFile/staticresource/st1/green/static/images/icon/line.png"></image>
  19. <view class="public_con_nav_name">
  20. <text class="colorCustom">{{item.children[0].AppointmentStatus}}</text>
  21. </view>
  22. </view>
  23. <view class="public_info_list">
  24. <view class="public_info_item">
  25. <view class="public_info_tit">就诊人</view>
  26. <view class="public_info_val">{{memberName}}</view>
  27. </view>
  28. <view class="public_info_item" v-for="(child, childIndex) in item.children" :key="childIndex">
  29. <view class="public_info_tit">项目名称</view>
  30. <view class="public_info_val">{{child.LabName}}</view>
  31. </view>
  32. <view class="public_info_item">
  33. <view class="public_info_tit">开单时间</view>
  34. <view class="public_info_val">{{item.children[0].BillDate}}</view>
  35. </view>
  36. <view class="public_info_item">
  37. <view class="public_info_tit">就诊时间</view>
  38. <view class="public_info_val">{{item.children[0].AppointmentTime}}</view>
  39. </view>
  40. <view class="public_info_item">
  41. <view class="public_info_tit">预约详情</view>
  42. <view class="public_info_val showDetail" v-if="!item.showTip" @click.stop="showTipFn(index)">点击查看详情</view>
  43. <view class="public_info_val" v-else>{{item.FriendlyReminder}}</view>
  44. </view>
  45. </view>
  46. <view class="public_info_foot border_top">
  47. <view class="info_foot_item cancel_btn border" v-if="item.cancenBtn" @click.stop="cancelYjyy(item)">退号</view>
  48. <view
  49. v-if="item.allowSignIn"
  50. class="info_foot_item cancel_btn border"
  51. :class="{'colorCustom': item.children[0].AppointmentStatus == '已预约'}"
  52. @click.stop="jumpSignPage(item)"
  53. >在线签到</view>
  54. </view>
  55. </view>
  56. </view>
  57. <view v-else class="noData">
  58. <noData :value="noDataValue"></noData>
  59. </view>
  60. </view>
  61. </view>
  62. </template>
  63. <script setup lang="ts">
  64. import { ref } from 'vue';
  65. import { useOnLoad } from '@dcloudio/uni-app';
  66. import { queryExamItemList, getMedicalReceipts, cancelAppoint } from '@/pagesPatient/service/otherService';
  67. import { orderDetailLocal } from '@/pagesPatient/service/record/index';
  68. import { request, handle } from '@kasite/uni-app-base';
  69. import { common, menuClick } from '@/utils';
  70. import { REQUEST_CONFIG } from '@/config/requestConfig';
  71. const app = getApp();
  72. const list = ref<any[]>([]);
  73. const showNoData = ref(false);
  74. const noDataValue = ref('暂无医技预约记录信息');
  75. const screenInfo = ref({});
  76. const memberName = ref('');
  77. const cardNo = ref('');
  78. const currentUser = ref<any>({});
  79. const queryData = ref({
  80. beginDate: common.dateFormat(new Date(Date.now() - (30 * 24 * 60 * 60 * 1000))).formatYear, //开始时间
  81. endDate: common.newDay(), //结束时间
  82. memberId: '',
  83. cardNo: '',
  84. cardType: ''
  85. });
  86. useOnLoad((options: any) => {
  87. if (app.globalData.logSuccess) {
  88. main(options);
  89. } else {
  90. app.loginReadyCallBack = () => main(options);
  91. }
  92. });
  93. const queryMemberCardList = async (cardType: string, isEncrypted: string) => {
  94. let resp = handle.promistHandleNew(await request.doPost(`${REQUEST_CONFIG.BASE_URL}wsgw/member/memberApi/QueryMemberCardList/callApiJSON.do`, {
  95. CardType: cardType,
  96. IsEncrypted: isEncrypted
  97. }));
  98. return handle.catchPromiseNew(resp, () => resp);
  99. };
  100. const main = async (options: any = {}) => {
  101. let queryUser = app.globalData.currentUser;
  102. if (options.orderId) {
  103. let cNo;
  104. let orderResp = await orderDetailLocal({ OrderId: options.orderId });
  105. if (!common.isEmpty(orderResp)) {
  106. cNo = orderResp[0].CardNo;
  107. }
  108. if (cNo) {
  109. let resp = await queryMemberCardList('1', '1');
  110. if (!common.isEmpty(resp)) {
  111. queryUser = resp.filter((item: any) => item.CardNo == cNo)[0] || resp[0];
  112. } else {
  113. common.showModal(`当前就诊卡不存在`, () => {
  114. uni.reLaunch({
  115. url: `/pages/st1/green/business/tabbar/homePage/homePage`,
  116. });
  117. });
  118. return;
  119. }
  120. } else {
  121. return;
  122. }
  123. } else if (options.cardNo) {
  124. let cNo = options.cardNo;
  125. let resp = await queryMemberCardList('1', '1');
  126. if (!common.isEmpty(resp)) {
  127. queryUser = resp.filter((item: any) => item.CardNo == cNo)[0] || resp[0];
  128. } else {
  129. common.showModal(`当前就诊卡不存在`, () => {
  130. uni.reLaunch({
  131. url: `/pages/st1/green/business/tabbar/homePage/homePage`,
  132. });
  133. });
  134. return;
  135. }
  136. }
  137. let user = options.userInfo ? JSON.parse(options.userInfo) : queryUser || {};
  138. // Assuming yjyyRecord_screening is in app.globalData.config.pageConfiguration
  139. let sInfo = common.deepCopy(app.globalData.config?.pageConfiguration?.yjyyRecord_screening?.value || [], []);
  140. queryData.value.memberId = user.MemberId || '';
  141. queryData.value.cardNo = user.CardNo || '';
  142. queryData.value.cardType = user.CardType || '';
  143. cardNo.value = user.CardNo || '';
  144. currentUser.value = user;
  145. screenInfo.value = sInfo;
  146. memberName.value = user.MemberName;
  147. queryExamItemListFn();
  148. };
  149. const dateChange = (e: any) => {
  150. let detail = e; // component emits detail directly? or e.detail? usually in vue3 emit payload is the value.
  151. // Assuming screening component emits {type: 'beginDate', value: '...'}
  152. // But legacy code: this.setData({ [detail.type]: detail.value })
  153. // We need to check how screening component emits.
  154. // Assuming it matches legacy `e.detail`.
  155. if (detail.type) {
  156. // @ts-ignore
  157. queryData.value[detail.type] = detail.value;
  158. }
  159. queryExamItemListFn();
  160. };
  161. const screenInfoChange = async (e: any) => {
  162. let qData = e.queryData;
  163. let obj = {
  164. memberId: qData.memberId,
  165. cardNo: qData.cardNo,
  166. cardType: qData.cardType
  167. };
  168. queryData.value = { ...queryData.value, ...obj };
  169. cardNo.value = qData.cardNo;
  170. await queryExamItemListFn();
  171. memberName.value = qData.memberName;
  172. };
  173. const queryExamItemListFn = async () => {
  174. let resp = await queryExamItemList(queryData.value) || [];
  175. resp = resp.filter((item: any) => item.IsBook == '0');
  176. const inspectDeptIds = app.globalData.config?.inspectDeptIds?.split(",") || [];
  177. resp.map((item: any) => {
  178. item.showTip = false;
  179. item.allowSignIn = inspectDeptIds.includes(item.ExamDept);
  180. });
  181. let newResp = recombination(resp);
  182. newResp.forEach((item: any, index: number) => {
  183. item.cancenBtn = [];
  184. if (common.isNotEmpty(item.children)) {
  185. let code = ['35512', '42448', '42449', '42450'];
  186. code.forEach((codeItem) => {
  187. let found = item.children.filter((childitem: any) => {
  188. return childitem.LabCode == codeItem;
  189. });
  190. if(found && found.length > 0) {
  191. item.cancenBtn = item.cancenBtn.concat(found);
  192. }
  193. });
  194. }
  195. // Legacy logic: item.cancenBtn = common.isEmpty(item.cancenBtn)
  196. // Wait, if it's empty, it returns true? So if cancelBtn is empty (no match), it shows button?
  197. // "item.cancenBtn = common.isEmpty(item.cancenBtn)" -> true if empty.
  198. // So if LabCode is NOT in the list, show cancel button?
  199. // Let's re-read legacy:
  200. // item.cancenBtn = item.children.filter(...)
  201. // item.cancenBtn = common.isEmpty(item.cancenBtn)
  202. // If empty -> true.
  203. // wx:if="{{item.cancenBtn}}" -> show if true.
  204. // So if code matches, it is NOT empty, so isEmpty is false, so button hidden.
  205. // So button is HIDDEN for these codes. Shown for others.
  206. item.cancenBtn = common.isEmpty(item.cancenBtn);
  207. const mainAllowSignIn = !item.children.map((child: any) => child.allowSignIn).includes(false);
  208. item.allowSignIn = mainAllowSignIn;
  209. });
  210. list.value = newResp;
  211. showNoData.value = common.isEmpty(resp);
  212. };
  213. const itemClick = (item: any) => {
  214. let hisKeys = "";
  215. let totalLength = item.children.length;
  216. item.children.map((child: any, index: number) => {
  217. hisKeys += child.HisKey;
  218. if (index + 1 != totalLength) {
  219. hisKeys += "|";
  220. }
  221. });
  222. let queryBean = JSON.stringify({
  223. hisKey: hisKeys,
  224. cardNo: cardNo.value
  225. });
  226. common.navigateTo(`/pages/st1/green/business/pay/payState/payState?pageType=yjyy&queryBean=${queryBean}`);
  227. };
  228. const recombination = (l: any[]) => {
  229. let newList: any[] = [];
  230. l.forEach(item => {
  231. let newItem = newList.find((i) => i.GroupId == item.GroupId);
  232. if (!newItem) {
  233. newList.push({
  234. GroupId: item.GroupId,
  235. children: [item]
  236. });
  237. } else {
  238. newItem.children.push(item);
  239. }
  240. });
  241. return newList;
  242. };
  243. // Regex helper to extract value from XML-like string
  244. const getXmlValue = (xml: string, tag: string) => {
  245. // Match <tag>...</tag> or <tag attr="...">...</tag>, support multiline content
  246. const match = xml.match(new RegExp(`<${tag}\\b[^>]*>([\\s\\S]*?)</${tag}>`));
  247. return match ? match[1] : '';
  248. };
  249. const showTipFn = async (index: number) => {
  250. let item = list.value[index];
  251. let orderType = 1;
  252. let groupStore = "";
  253. let totalLength = item.children.length;
  254. let receipt = "";
  255. if (totalLength == 1) {
  256. groupStore = item.children[0].Store;
  257. receipt = item.children[0].HisKey;
  258. } else if (totalLength > 1) {
  259. orderType = 2;
  260. item.children.map((child: any, idx: number) => {
  261. let store = child.Store;
  262. // Use regex to parse
  263. let hisKey = getXmlValue(store, "HisKey");
  264. let groupId = getXmlValue(store, "GroupID");
  265. let groupSort = getXmlValue(store, "GroupSort");
  266. let orderId = getXmlValue(store, "OrderID");
  267. let printAppointmentId = getXmlValue(store, "PrintAppointmentID");
  268. let labCode = getXmlValue(store, "LabCode");
  269. let inspectionItemId = getXmlValue(store, "InspectionItemID");
  270. let patientId = getXmlValue(store, "PatientId");
  271. let appointmentFlag = getXmlValue(store, "AppointmentFlag");
  272. let serialNo = getXmlValue(store, "SerialNo");
  273. let receiptItems = `${groupId}|${groupSort}|${orderId}|${printAppointmentId}|${labCode}|${inspectionItemId}|${patientId}|${appointmentFlag}|${serialNo}`;
  274. if (idx + 1 != totalLength) {
  275. receiptItems += ";";
  276. hisKey += "_";
  277. }
  278. groupStore += receiptItems;
  279. receipt += hisKey;
  280. });
  281. }
  282. let qData = {
  283. Store: groupStore,
  284. OrderType: orderType,
  285. HisKey: receipt
  286. };
  287. let resp = await getMedicalReceipts(qData);
  288. if (resp) {
  289. uni.previewImage({
  290. urls: [resp[0].Url]
  291. });
  292. }
  293. };
  294. const cancelYjyy = (item: any) => {
  295. common.showModal(`确认取消预约?`, async () => {
  296. let hisKeys = "";
  297. let labNames = "";
  298. let totalLength = item.children.length;
  299. item.children.map((child: any, index: number) => {
  300. hisKeys += child.HisKey;
  301. labNames += child.LabName;
  302. if (index + 1 != totalLength) {
  303. hisKeys += "|";
  304. labNames += ",";
  305. }
  306. });
  307. let qData = {
  308. CardNo: currentUser.value.CardNo,
  309. CardType: currentUser.value.CardType,
  310. HisKey: hisKeys,
  311. LabName: labNames
  312. };
  313. let resp = await cancelAppoint(qData);
  314. if (!common.isEmpty(resp)) {
  315. common.showModal('取消成功', () => {
  316. main();
  317. });
  318. }
  319. }, '取消');
  320. };
  321. const jumpSignPage = (item: any) => {
  322. if (item.children[0].AppointmentStatus != "已预约") {
  323. common.showModal("预约已经退号或已签到");
  324. return;
  325. }
  326. let menuList = app.globalData.menuList;
  327. let targetItem = item; // Default
  328. // Try to find "检查签到" menu item
  329. if (menuList) {
  330. menuList.forEach((mItem: any) => {
  331. if (common.isNotEmpty(mItem.Children)) {
  332. mItem.Children.forEach((childItem: any) => {
  333. if (common.isNotEmpty(childItem.Children)) {
  334. childItem.Children.forEach((sunItem: any) => {
  335. if (sunItem.MenuName == "检查签到") {
  336. targetItem = sunItem;
  337. }
  338. });
  339. }
  340. });
  341. }
  342. });
  343. }
  344. // The original code passed `e` which had `dataset.item`.
  345. // Here we pass `item` directly to logic, but `menuClick` expects event or item.
  346. // `menuClick` signature: (e, _this, skipWay)
  347. // Inside `menuClick`: `let item = e.currentTarget ? e.currentTarget.dataset.item : e;`
  348. // So we can pass `targetItem` as first arg.
  349. menuClick(targetItem, null);
  350. };
  351. </script>
  352. <style lang="scss">
  353. .public_con{margin: 30upx}
  354. .info_foot_item {
  355. width: 160upx;
  356. height: 56upx;
  357. font-size:30upx;
  358. font-family:Source Han Sans CN;
  359. font-weight:400;
  360. background: rgba(255, 255, 255, 1);
  361. border-radius: 28upx;
  362. text-align: center;
  363. line-height: 56upx;
  364. /* position: absolute; */
  365. }
  366. .public_info_foot {
  367. justify-content: flex-end;
  368. }
  369. .public_info_foot .cancel_btn{
  370. right: 31upx;
  371. color:rgba(166,166,166,1);
  372. margin-left: 30upx;
  373. }
  374. .cancel{
  375. color: #999;
  376. }
  377. .showDetail{
  378. color: #00a2fe;
  379. }
  380. .noData {
  381. width: 100%;
  382. position: absolute;
  383. top: 50%;
  384. transform: translateY(-50%);
  385. }
  386. </style>