App.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432
  1. <script>
  2. import {
  3. loginAfterHandle
  4. } from './utils/loginHandle.js'
  5. import AppConfirm from '@/components/app-confirm/app-confirm.vue'
  6. // #ifdef H5
  7. import Vue from 'vue'
  8. // #endif
  9. export default {
  10. components:{ AppConfirm },
  11. globalData: {
  12. isPad: false,
  13. lastAuthCheckTime: 0
  14. },
  15. onLaunch: function(e) {
  16. //当url存在loginData时直接种上数据
  17. /* 条件编译,仅在H5平台生效 */
  18. // #ifdef H5
  19. if (e.query.loginData && e.query.hospSign) {
  20. try {
  21. loginAfterHandle(JSON.parse(decodeURI(e.query.loginData)), e.query.hospSign);
  22. } catch (e) {
  23. console.log({
  24. e
  25. })
  26. }
  27. }
  28. uni.getSystemInfo({
  29. success:(e)=>{
  30. /* 窗口宽度大于420px且不在PC页面且不在移动设备时跳转至 PC.html 页面 */
  31. console.log(e, window.top.isPC, !/iOS|Android/i.test(e.system));
  32. this.globalData.isPad =
  33. e.deviceType === 'pad' ||
  34. /iPad|Pad/i.test(e.model) ||
  35. e.screenWidth >= 768
  36. if (e.windowWidth > 420 && !window.top.isPC && !/iOS|Android/i.test(e.system)) {
  37. const url = decodeURIComponent(`/TracerMethodology/static/html/template.html`);
  38. // console.log({hospSign,url});
  39. window.location.pathname = url;
  40. /* 若项目未设置根目录(默认为 / 时),则使用下方代码 */
  41. // window.location.pathname = '/static/html/pc.html';
  42. }
  43. }
  44. })
  45. // #endif
  46. // 统一:H5 端在关闭页签/刷新时清空 AI 问答本地缓存
  47. // #ifdef H5
  48. try {
  49. if (typeof window !== 'undefined') {
  50. window.addEventListener('beforeunload', () => {
  51. try {
  52. uni.removeStorageSync('aiQaMessagesV1');
  53. } catch (err) {}
  54. });
  55. }
  56. } catch (err) {}
  57. // #endif
  58. // 创建全局自定义弹窗服务(无侵入替换 uni.showModal)
  59. try {
  60. if (!uni.__ORIG_SHOW_MODAL__) {
  61. uni.__ORIG_SHOW_MODAL__ = uni.showModal;
  62. uni.showModal = (options = {}) => {
  63. try {
  64. const app = getApp && getApp();
  65. const root = app && app.$vm;
  66. const confirmRef = root && root.$refs && (root.$refs.AppConfirm || root.$refs.appConfirm);
  67. if (confirmRef && typeof confirmRef.open === 'function') {
  68. return confirmRef.open({
  69. title: options.title || '提示',
  70. content: options.content || '',
  71. confirmText: options.confirmText || '确定',
  72. cancelText: options.cancelText || '取消',
  73. showCancel: options.showCancel !== false, // 透传 showCancel,默认 true
  74. type: options.type || 'default'
  75. }).then((ok) => {
  76. options.success && options.success({ confirm: ok, cancel: !ok });
  77. options.complete && options.complete({ confirm: ok, cancel: !ok });
  78. return { confirm: ok, cancel: !ok };
  79. });
  80. }
  81. } catch (err) {}
  82. // 兜底:若全局组件未就绪,回退原生
  83. return uni.__ORIG_SHOW_MODAL__.call(uni, options);
  84. }
  85. }
  86. } catch (err) {}
  87. // H5:程序化挂载(仅当静态挂载未就绪时)
  88. // #ifdef H5
  89. try {
  90. const app = getApp && getApp();
  91. const root = app && app.$vm;
  92. if (root && !(root.$refs && root.$refs.AppConfirm)) {
  93. const Ctor = Vue.extend(AppConfirm);
  94. const vm = new Ctor();
  95. vm.$mount();
  96. document.body.appendChild(vm.$el);
  97. root.$refs = root.$refs || {};
  98. root.$refs.AppConfirm = vm;
  99. }
  100. } catch (e) {}
  101. // #endif
  102. // APP-PLUS:不再进行运行时 DOM 插入,统一依赖静态挂载的 <AppConfirm />
  103. // 应用启动时执行设备授权校验
  104. this.checkDeviceAuthorization();
  105. },
  106. onShow: function() {
  107. // 所有平台在应用启动或回到前台时都执行设备授权校验
  108. this.checkDeviceAuthorization();
  109. },
  110. onHide: function() {
  111. // 应用进入后台或被关闭时,清空 AI 问答的本地缓存
  112. try {
  113. uni.removeStorageSync('aiQaMessagesV1');
  114. } catch (e) {}
  115. },
  116. methods: {
  117. // 启动设备授权校验:基于 IMEI 获取授权状态,根据不同状态进行相应跳转
  118. async checkDeviceAuthorization() {
  119. console.log('checkDeviceAuthorization 被调用');
  120. // H5 平台:不走授权流程
  121. // #ifdef H5
  122. return;
  123. // #endif
  124. // 移除H5的debug授权页面兜底逻辑
  125. // APP 平台:等待 plusready 再执行,确保能获取到设备标识
  126. // #ifdef APP-PLUS
  127. try {
  128. if (typeof plus === 'undefined' || !plus.device) {
  129. console.log('APP 未就绪,等待 plusready 后再执行授权校验');
  130. const once = () => {
  131. try { document.removeEventListener('plusready', once); } catch (e) {}
  132. this.checkDeviceAuthorization();
  133. };
  134. document.addEventListener('plusready', once);
  135. return;
  136. }
  137. } catch (e) {}
  138. // #endif
  139. // 检查当前页面路径
  140. const pages = getCurrentPages();
  141. const currentPage = pages[pages.length - 1];
  142. const currentRoute = currentPage ? currentPage.route : '';
  143. console.log('当前页面路径:', currentRoute);
  144. // 不再存在需要排除的 debug 调试页面
  145. // 节流机制:避免短时间内重复检查(5秒内只检查一次)
  146. const now = Date.now();
  147. if (now - this.globalData.lastAuthCheckTime < 5000) {
  148. console.log('授权检查节流:跳过重复检查', {
  149. now,
  150. last: this.globalData.lastAuthCheckTime,
  151. diff: now - this.globalData.lastAuthCheckTime
  152. });
  153. return;
  154. }
  155. this.globalData.lastAuthCheckTime = now;
  156. try {
  157. const { getDeviceImeiOrUuid } = await import('@/utils/device.js');
  158. const { fetchDeviceAuthorization } = await import('@/utils/authorize.js');
  159. const imei = await getDeviceImeiOrUuid();
  160. if (!imei) {
  161. // H5+Debug才会执行到这里,为避免误扰,仅提示一次
  162. uni.showToast({ title: '无法获取设备标识', icon: 'none' });
  163. return;
  164. }
  165. console.log('开始调用授权查询接口, IMEI:', imei);
  166. const auth = await fetchDeviceAuthorization(imei);
  167. console.log('设备授权状态返回结果:', auth);
  168. if (!auth) {
  169. uni.showToast({ title: '授权查询失败', icon: 'none' });
  170. return;
  171. }
  172. switch (auth.status) {
  173. case 'authorized':
  174. // 已授权:保存hospSign并跳转到登录页
  175. if (auth.hospSign) {
  176. uni.setStorageSync('hospSign', auth.hospSign);
  177. }
  178. // 保存后端region作为业务接口域名
  179. if (auth.region) {
  180. try {
  181. const { setRuntimeRegionDomain } = await import('@/utils/requestUrl.js');
  182. setRuntimeRegionDomain(auth.region);
  183. } catch (e) {}
  184. }
  185. // 已授权:根据是否已登录决定是否跳转登录页
  186. try {
  187. const hasToken = !!uni.getStorageSync('token');
  188. if (hasToken) {
  189. console.log('设备已授权,已登录,跳过登录跳转');
  190. return;
  191. }
  192. } catch (e) {}
  193. console.log('设备已授权,未登录,跳转登录页');
  194. uni.reLaunch({ url: `/pages/login/login?hospSign=${encodeURIComponent(auth.hospSign || '')}` });
  195. return;
  196. case 'reviewing':
  197. // 审核中:跳转到申请页面并显示待审核状态
  198. if (auth.applyInfo) {
  199. uni.setStorageSync('authApplyInfo', auth.applyInfo);
  200. }
  201. uni.reLaunch({ url: `/pages/applyAuth/applyAuth?imei=${encodeURIComponent(imei)}` });
  202. break;
  203. case 'expired':
  204. // 授权已到期:跳转到申请页面并显示到期状态
  205. if (auth.applyInfo) {
  206. uni.setStorageSync('authExpiredInfo', auth.applyInfo);
  207. }
  208. uni.reLaunch({ url: `/pages/applyAuth/applyAuth?imei=${encodeURIComponent(imei)}&expired=true` });
  209. break;
  210. case 'unauthorized':
  211. default:
  212. // 未授权:清理可能残留的本地状态并跳转到申请页面
  213. try {
  214. uni.removeStorageSync('authApplyInfo');
  215. uni.removeStorageSync('authExpiredInfo');
  216. } catch (e) {}
  217. uni.reLaunch({ url: `/pages/applyAuth/applyAuth?imei=${encodeURIComponent(imei)}&status=unauthorized` });
  218. break;
  219. }
  220. } catch (e) {
  221. console.warn('设备授权校验失败', e);
  222. uni.showToast({ title: '授权校验失败', icon: 'none' });
  223. }
  224. },
  225. // 监测是否传参数
  226. checkArguments() {
  227. try {
  228. if (uni.getSystemInfoSync().platform === 'android') {
  229. // 接收第三方app传递的参数 extra;
  230. // #ifdef APP-PLUS
  231. if (plus.runtime.arguments) {
  232. // patParams: 院区,病区, 床号
  233. const {
  234. patParams
  235. } = JSON.parse(plus.runtime.arguments);
  236. console.log({
  237. patParams
  238. });
  239. uni.setStorageSync('patParams', patParams);
  240. }
  241. // #endif
  242. }
  243. } catch (e) {}
  244. },
  245. //角色和对应的跳转页面
  246. rolToTarget(nowPermission) {
  247. if (nowPermission != 0) {
  248. let current = this.rolList.find(item => item.permission == nowPermission);
  249. if (current) {
  250. // 页面跳转
  251. uni.redirectTo({
  252. url: `/${current.pagePath}`
  253. });
  254. }
  255. }
  256. }
  257. }
  258. };
  259. </script>
  260. <style lang="scss">
  261. @import "uview-ui/index.scss";
  262. /*每个页面公共css */
  263. /* 条件编译,仅在H5平台生效 */
  264. // #ifdef H5
  265. body::-webkit-scrollbar,
  266. html::-webkit-scrollbar {
  267. display: none;
  268. }
  269. // #endif
  270. body,
  271. uni-app,
  272. uni-page,
  273. uni-page-wrapper,
  274. uni-page-body {
  275. height: 100%;
  276. font-size: 20rpx;
  277. line-height: 30rpx;
  278. color: #292C33;
  279. background-color: #F5F6FA;
  280. }
  281. view,
  282. label,
  283. scroll-view {
  284. box-sizing: border-box;
  285. }
  286. // 底部固定的按钮
  287. .fixed-buttom-btn {
  288. position: fixed;
  289. left: 0;
  290. bottom: 0;
  291. display: flex;
  292. justify-content: center;
  293. align-items: center;
  294. margin-top: 12.5rpx;
  295. width: 100%;
  296. height: 75rpx;
  297. background-color: #3377FF;
  298. .btn-text {
  299. flex: 1;
  300. font-size: 22.5rpx;
  301. color: #fff;
  302. text-align: center;
  303. }
  304. .btn-text.cancle {
  305. line-height: 76.25rpx;
  306. background-color: #FFFFFF;
  307. color: #3377FF;
  308. }
  309. }
  310. // 新建情境样式start
  311. .creatingSituations {
  312. .title {
  313. padding-bottom: 35rpx;
  314. padding-left: 25rpx;
  315. font-size: 30rpx;
  316. line-height: 45rpx;
  317. color: #292C33;
  318. }
  319. }
  320. // 新建情境样式end
  321. // 查核地图列表样式start
  322. .check-map-list {
  323. overflow: hidden;
  324. padding: 0 25rpx;
  325. .item {
  326. position: relative;
  327. overflow: hidden;
  328. margin-bottom: 25rpx;
  329. border-radius: 5rpx;
  330. padding-top: 16.25rpx;
  331. padding-bottom: 0;
  332. width: 100%;
  333. background-color: #fff;
  334. box-shadow: 0 3.75rpx 12.5rpx 0 rgba(0, 13, 51, 0.1);
  335. .title-wrap {
  336. display: flex;
  337. flex-direction: row;
  338. align-items: center;
  339. padding: 0 25rpx;
  340. >text {
  341. font-size: 35rpx;
  342. line-height: 52.5rpx;
  343. font-weight: normal;
  344. color: #292C33;
  345. }
  346. >view {
  347. display: flex;
  348. flex-direction: row;
  349. align-items: center;
  350. margin-left: 20rpx;
  351. // border-radius: 17.5rpx;
  352. height: 35rpx;
  353. font-size: 17.5rpx;
  354. line-height: 35rpx;
  355. color: #8F9BB3;
  356. background-color: #EDF2FA;
  357. image {
  358. width: 35rpx;
  359. height: 35rpx;
  360. }
  361. text {
  362. padding-left: 10rpx;
  363. padding-right: 20rpx;
  364. }
  365. }
  366. }
  367. .content {
  368. display: flex;
  369. flex-direction: column;
  370. padding: 11.25rpx 25rpx 20rpx;
  371. >text {
  372. overflow: hidden;
  373. white-space: nowrap;
  374. text-overflow: ellipsis;
  375. font-size: 20rpx;
  376. line-height: 30rpx;
  377. color: #666F80;
  378. &:first-child {
  379. margin-bottom: 5rpx;
  380. font-weight: bold;
  381. color: #292C33;
  382. }
  383. }
  384. }
  385. }
  386. }
  387. // 查核地图列表样式end
  388. </style>