| | |
| | | <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no"> |
| | | <title>峰峰矿区智慧停车</title> |
| | | |
| | | <!-- 样式文件放head,正常加载 --> |
| | | <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/element-ui@2.15.14/lib/theme-chalk/index.css"> |
| | | <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/view-design@4.7.0/dist/styles/iview.css"> |
| | | <!-- |
| | | 样式文件:主源用国内阿里云 npmmirror(移动/联通/电信网络稳定可达), |
| | | 加载失败自动切回 jsdelivr。CSS 失败不会导致白屏,仅样式异常。 |
| | | --> |
| | | <link rel="stylesheet" |
| | | href="https://registry.npmmirror.com/element-ui/2.15.14/files/lib/theme-chalk/index.css" |
| | | onerror="this.onerror=null;this.href='https://cdn.jsdelivr.net/npm/element-ui@2.15.14/lib/theme-chalk/index.css'"> |
| | | <link rel="stylesheet" |
| | | href="https://registry.npmmirror.com/view-design/4.7.0/files/dist/styles/iview.css" |
| | | onerror="this.onerror=null;this.href='https://cdn.jsdelivr.net/npm/view-design@4.7.0/dist/styles/iview.css'"> |
| | | |
| | | <style> |
| | | /* 首屏加载占位(Vue 挂载后会自动替换掉 #app 内的内容) */ |
| | | #app-loading{position:fixed;top:0;left:0;right:0;bottom:0;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;background:#f5f7fa;color:#606266;font-size:15px;z-index:9998} |
| | | #app-loading .spin{width:38px;height:38px;margin-bottom:14px;border:3px solid #dcdfe6;border-top-color:#409eff;border-radius:50%;animation:appspin .8s linear infinite} |
| | | @keyframes appspin{to{transform:rotate(360deg)}} |
| | | /* 资源全部加载失败时的提示层,替代“纯白屏” */ |
| | | #fatal-mask{display:none;position:fixed;top:0;left:0;right:0;bottom:0;inset:0;z-index:9999;background:#fff;align-items:center;justify-content:center;padding:30px;box-sizing:border-box} |
| | | #fatal-mask .box{max-width:420px;text-align:center;color:#303133} |
| | | #fatal-mask .ico{font-size:44px;color:#f56c6c;margin-bottom:14px} |
| | | #fatal-mask .msg{font-size:15px;line-height:1.7;color:#606266;margin-bottom:20px;word-break:break-all} |
| | | #fatal-mask button{padding:10px 34px;font-size:15px;color:#fff;background:#409eff;border:none;border-radius:4px;outline:none} |
| | | #fatal-mask button:active{background:#3a8ee6} |
| | | </style> |
| | | |
| | | <script> |
| | | /* |
| | | * 基础库(Vue / ElementUI / ViewUI / ECharts)多源容灾加载器 |
| | | * |
| | | * 背景:这些库通过 webpack externals 外置,业务包执行强依赖对应的全局变量。 |
| | | * 之前只引用 cdn.jsdelivr.net 一个源,该域名在国内手机蜂窝网络(4G/5G)下 |
| | | * 经常被 DNS 污染 / 连接重置,脚本加载失败后 window.Vue 为 undefined, |
| | | * 业务包执行立即报错,#app 永远为空,表现为手机白屏。 |
| | | * |
| | | * 策略:国内 npmmirror 为主源,jsdelivr、bootcdn 依次兜底; |
| | | * 用“外链脚本 + 紧随其后的内联检测脚本”成对输出,保证严格的加载顺序, |
| | | * 业务包(webpack 注入在 body 末尾)一定在所有基础库就绪后才执行。 |
| | | */ |
| | | window.__cdnTry = function (globalName, urls, idx) { |
| | | if (window[globalName]) return true; |
| | | if (idx >= urls.length) { |
| | | (window.__CDN_FAILED__ = window.__CDN_FAILED__ || []).push(globalName); |
| | | return false; |
| | | } |
| | | document.write('<script src="' + urls[idx] + '"><\/script>'); |
| | | document.write('<script>window.__cdnTry("' + globalName + '",' + JSON.stringify(urls) + ',' + (idx + 1) + ');<\/script>'); |
| | | }; |
| | | |
| | | /* 白屏兜底:记录脚本错误,超时后若基础库仍未就绪则给出可见提示 */ |
| | | window.__lastError__ = ''; |
| | | window.addEventListener('error', function (e) { |
| | | if (e && e.message) { |
| | | window.__lastError__ = e.message + (e.filename ? ('(' + e.filename.split('/').pop() + ':' + e.lineno + ')') : ''); |
| | | } |
| | | }, true); |
| | | window.addEventListener('unhandledrejection', function (e) { |
| | | var r = e && e.reason; |
| | | window.__lastError__ = 'Promise异常: ' + (r && r.message ? r.message : String(r)); |
| | | }); |
| | | window.__showFatal = function (msg) { |
| | | var mask = document.getElementById('fatal-mask'); |
| | | var loading = document.getElementById('app-loading'); |
| | | if (loading) loading.style.display = 'none'; |
| | | if (mask) { |
| | | document.getElementById('fatal-msg').textContent = msg; |
| | | mask.style.display = 'flex'; |
| | | } |
| | | }; |
| | | setTimeout(function () { |
| | | /* 注意:view-design 的 UMD 包在浏览器下挂载的全局名是小写 iview,不是 ViewUI */ |
| | | var required = ['Vue', 'ELEMENT', 'iview', 'echarts']; |
| | | var labels = { Vue: 'Vue', ELEMENT: 'ElementUI', iview: 'ViewUI', echarts: 'ECharts' }; |
| | | var missing = required.filter(function (n) { return !window[n]; }); |
| | | if (missing.length) { |
| | | window.__showFatal('静态资源加载失败,页面无法启动(缺少:' + missing.map(function (n) { return labels[n]; }).join('、') + |
| | | ')。请切换 WiFi 或 4G/5G 网络后重试。' + |
| | | (window.__lastError__ ? ('\n' + window.__lastError__) : '')); |
| | | return; |
| | | } |
| | | /* 库齐全但 12 秒仍未挂载,通常是业务包/接口异常,也要让用户看到而不是白屏 */ |
| | | setTimeout(function () { |
| | | var loading = document.getElementById('app-loading'); |
| | | if (loading && loading.parentNode) { |
| | | window.__showFatal('页面加载超时,请点击重试。' + |
| | | (window.__lastError__ ? ('\n' + window.__lastError__) : '')); |
| | | } |
| | | }, 12000); |
| | | }, 8000); |
| | | </script> |
| | | </head> |
| | | <body> |
| | | <div id="app"></div> |
| | | <div id="app"><div id="app-loading"><div class="spin"></div><div>正在加载,请稍候…</div></div></div> |
| | | |
| | | <!-- 1. 先加载 Vue(所有UI库的基础) --> |
| | | <script src="https://cdn.jsdelivr.net/npm/vue@2.7.16/dist/vue.min.js"></script> |
| | | <!-- 2. 再加载 ElementUI --> |
| | | <script src="https://cdn.jsdelivr.net/npm/element-ui@2.15.14/lib/index.js"></script> |
| | | <!-- 3. 再加载 ViewUI --> |
| | | <script src="https://cdn.jsdelivr.net/npm/view-design@4.7.0/dist/iview.min.js"></script> |
| | | <!-- 4. 最后加载 ECharts --> |
| | | <script src="https://cdn.jsdelivr.net/npm/echarts@5.4.3/dist/echarts.min.js"></script> |
| | | <div id="fatal-mask"> |
| | | <div class="box"> |
| | | <div class="ico">!</div> |
| | | <div class="msg" id="fatal-msg">静态资源加载失败,请重试。</div> |
| | | <button type="button" onclick="location.reload()">重新加载</button> |
| | | </div> |
| | | </div> |
| | | |
| | | <script> |
| | | (function () { |
| | | /* 顺序不能变:Vue -> ElementUI -> ViewUI -> ECharts */ |
| | | var LIBS = [ |
| | | ['Vue', [ |
| | | 'https://registry.npmmirror.com/vue/2.7.16/files/dist/vue.min.js', |
| | | 'https://cdn.jsdelivr.net/npm/vue@2.7.16/dist/vue.min.js', |
| | | 'https://cdn.bootcdn.net/ajax/libs/vue/2.7.16/vue.min.js' |
| | | ]], |
| | | ['ELEMENT', [ |
| | | 'https://registry.npmmirror.com/element-ui/2.15.14/files/lib/index.js', |
| | | 'https://cdn.jsdelivr.net/npm/element-ui@2.15.14/lib/index.js', |
| | | 'https://cdn.bootcdn.net/ajax/libs/element-ui/2.15.14/index.js' |
| | | ]], |
| | | ['iview', [ |
| | | 'https://registry.npmmirror.com/view-design/4.7.0/files/dist/iview.min.js', |
| | | 'https://cdn.jsdelivr.net/npm/view-design@4.7.0/dist/iview.min.js', |
| | | 'https://cdn.bootcdn.net/ajax/libs/view-design/4.7.0/iview.min.js' |
| | | ]], |
| | | ['echarts', [ |
| | | 'https://registry.npmmirror.com/echarts/5.4.3/files/dist/echarts.min.js', |
| | | 'https://cdn.jsdelivr.net/npm/echarts@5.4.3/dist/echarts.min.js', |
| | | 'https://cdn.bootcdn.net/ajax/libs/echarts/5.4.3/echarts.min.js' |
| | | ]] |
| | | ]; |
| | | LIBS.forEach(function (lib) { |
| | | var name = lib[0], urls = lib[1]; |
| | | document.write('<script src="' + urls[0] + '"><\/script>'); |
| | | document.write('<script>window.__cdnTry("' + name + '",' + JSON.stringify(urls) + ',1);<\/script>'); |
| | | }); |
| | | })(); |
| | | </script> |
| | | </body> |
| | | </html> |
| | | </html> |