Add product/channel billing condition editors, place-order additional services, billing simulator, forecast-to-inbound fee delta and margin warnings, insurance-aware claim tips, OMS preset dict seeds, and related AI skills/system pages. Co-authored-by: Cursor <cursoragent@cursor.com>
365 lines
12 KiB
HTML
365 lines
12 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="zh-CN" class="app-booting">
|
||
<head>
|
||
<meta charset="UTF-8" />
|
||
<script>
|
||
/* 首屏前判定 H5/语言,避免 body 绘出后再改「IDE」→「IDE客户中心」造成字号/布局抖 */
|
||
(function () {
|
||
var path = location.pathname || '';
|
||
var isH5 =
|
||
path === '/h5' ||
|
||
path.indexOf('/h5/') === 0 ||
|
||
path.indexOf('/wechat/') === 0 ||
|
||
path === '/track/query' ||
|
||
path.indexOf('/track/query/') === 0;
|
||
var isEn = false;
|
||
try {
|
||
var locale =
|
||
localStorage.getItem('arco-locale') || localStorage.getItem('locale') || '';
|
||
isEn = /^en/i.test(locale);
|
||
} catch (e) {}
|
||
var root = document.documentElement;
|
||
if (isH5) root.classList.add('app-boot-h5');
|
||
if (isEn) root.classList.add('app-boot-en');
|
||
try {
|
||
document.title = isH5
|
||
? isEn
|
||
? 'IDE Customer Center'
|
||
: 'IDE客户中心'
|
||
: 'IDE I Faster & Simpler';
|
||
} catch (e2) {}
|
||
})();
|
||
</script>
|
||
<link rel="icon" type="image/png" href="/favicon.png" />
|
||
<link rel="preload" href="/boot-logo.png" as="image" />
|
||
<meta
|
||
name="viewport"
|
||
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover"
|
||
/>
|
||
<title>IDE I Faster & Simpler</title>
|
||
<link rel="stylesheet" type="text/css" media="print" href="/print-lock.css" />
|
||
<!-- DNS 预解析,加速资源加载 -->
|
||
<link rel="dns-prefetch" href="//43.139.210.35" />
|
||
<!-- 预连接,提前建立连接 -->
|
||
<link rel="preconnect" href="//43.139.210.35" crossorigin />
|
||
<style>
|
||
/*
|
||
* 右→左偏移根因:Vue 挂载后页面出现纵向滚动条,视口变窄,
|
||
* fixed 居中点左移约半个滚动条宽度。boot 期间锁滚动 + 常驻 gutter。
|
||
*/
|
||
html {
|
||
scrollbar-gutter: stable;
|
||
background: #f0f3f4;
|
||
}
|
||
html.app-booting,
|
||
html.app-booting body {
|
||
overflow: hidden !important;
|
||
overscroll-behavior: none;
|
||
}
|
||
html,
|
||
body,
|
||
#app {
|
||
margin: 0;
|
||
min-height: 100%;
|
||
background: #f0f3f4;
|
||
}
|
||
#app-boot {
|
||
position: fixed;
|
||
inset: 0;
|
||
z-index: 99999;
|
||
margin: 0;
|
||
padding: 0;
|
||
box-sizing: border-box;
|
||
/* 用 100% 相对自身,避免 100vw 把滚动条宽度算进去 */
|
||
width: 100%;
|
||
height: 100%;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
background: #f0f3f4;
|
||
/*
|
||
* 必须与 global.less 首屏后状态一致:
|
||
* Vue 加载后 html 会带上 antialiased,若 boot 首帧没有,中文会突然变细像「字号缩小」。
|
||
*/
|
||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC',
|
||
'Microsoft YaHei', Inter, Roboto, 'Helvetica Neue', Arial, sans-serif;
|
||
-webkit-font-smoothing: antialiased;
|
||
-moz-osx-font-smoothing: grayscale;
|
||
font-size: 16px;
|
||
color: #4e5969;
|
||
text-align: center;
|
||
opacity: 1;
|
||
transition: opacity 0.28s ease;
|
||
pointer-events: auto;
|
||
}
|
||
#app-boot.is-leaving {
|
||
opacity: 0;
|
||
pointer-events: none;
|
||
}
|
||
#app-boot .boot-panel {
|
||
width: 300px;
|
||
margin: 0;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
/* 不用 left:50% + translate,避免视口宽度变化时二次偏移 */
|
||
flex-shrink: 0;
|
||
}
|
||
#app-boot .boot-brand {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
flex-shrink: 0;
|
||
}
|
||
#app-boot .boot-logo-wrap {
|
||
width: 72px;
|
||
height: 72px;
|
||
border-radius: 16px;
|
||
background: #fff;
|
||
box-shadow: 0 1px 0 rgba(0, 0, 0, 0.04), 0 8px 24px rgba(15, 23, 42, 0.06);
|
||
box-sizing: border-box;
|
||
padding: 10px;
|
||
overflow: hidden;
|
||
}
|
||
#app-boot .boot-logo {
|
||
display: block;
|
||
width: 52px;
|
||
height: 52px;
|
||
object-fit: contain;
|
||
}
|
||
#app-boot .boot-status {
|
||
margin-top: 20px;
|
||
width: 100%;
|
||
height: 96px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
}
|
||
#app-boot .boot-spinner-slot {
|
||
position: relative;
|
||
width: 120px;
|
||
height: 32px;
|
||
flex-shrink: 0;
|
||
}
|
||
#app-boot .boot-spinner {
|
||
position: absolute;
|
||
left: 50%;
|
||
top: 50%;
|
||
width: 22px;
|
||
height: 22px;
|
||
margin: 0;
|
||
border: 2px solid #e5e6eb;
|
||
border-top-color: var(--app-primary-color, #007979);
|
||
border-radius: 50%;
|
||
box-sizing: border-box;
|
||
transform: translate(-50%, -50%);
|
||
transform-origin: center center;
|
||
animation: app-boot-spin 0.8s linear infinite;
|
||
will-change: transform;
|
||
backface-visibility: hidden;
|
||
}
|
||
#app-boot .boot-retry {
|
||
position: absolute;
|
||
left: 50%;
|
||
top: 50%;
|
||
transform: translate(-50%, -50%);
|
||
height: 32px;
|
||
padding: 0 16px;
|
||
border: 1px solid #e5e6eb;
|
||
border-radius: 4px;
|
||
background: #fff;
|
||
color: #1d2129;
|
||
font-size: 14px;
|
||
font-weight: 500;
|
||
cursor: pointer;
|
||
opacity: 0;
|
||
visibility: hidden;
|
||
pointer-events: none;
|
||
white-space: nowrap;
|
||
}
|
||
#app-boot .boot-retry:hover {
|
||
border-color: var(--app-primary-color, #007979);
|
||
color: var(--app-primary-color, #007979);
|
||
}
|
||
#app-boot.is-timeout .boot-spinner {
|
||
opacity: 0;
|
||
visibility: hidden;
|
||
animation: none;
|
||
}
|
||
#app-boot.is-timeout .boot-retry {
|
||
opacity: 1;
|
||
visibility: visible;
|
||
pointer-events: auto;
|
||
}
|
||
#app-boot .boot-title {
|
||
margin: 10px 0 0;
|
||
height: 20px;
|
||
font-size: 14px;
|
||
font-weight: 500;
|
||
color: #86909c;
|
||
line-height: 20px;
|
||
}
|
||
#app-boot .boot-sub {
|
||
margin: 6px 0 0;
|
||
height: 36px;
|
||
font-size: 12px;
|
||
line-height: 18px;
|
||
color: #c9cdd4;
|
||
width: 100%;
|
||
overflow: hidden;
|
||
}
|
||
#app-boot.is-timeout .boot-title {
|
||
color: #1d2129;
|
||
font-weight: 600;
|
||
}
|
||
#app-boot.is-timeout .boot-sub {
|
||
color: #86909c;
|
||
}
|
||
/* H5:样式挂在 html.app-boot-h5 上,首屏即正确,不再二次改字号/Logo */
|
||
html.app-boot-h5 {
|
||
scrollbar-gutter: stable;
|
||
background: #f5f6f8;
|
||
}
|
||
html.app-boot-h5,
|
||
html.app-boot-h5 body,
|
||
html.app-boot-h5 #app {
|
||
background: #f5f6f8;
|
||
}
|
||
html.app-boot-h5 #app-boot {
|
||
background: #f5f6f8;
|
||
padding: env(safe-area-inset-top, 0) 24px env(safe-area-inset-bottom, 0);
|
||
}
|
||
html.app-boot-h5 #app-boot .boot-panel {
|
||
width: min(300px, 100%);
|
||
}
|
||
html.app-boot-h5 #app-boot .boot-status {
|
||
height: 104px;
|
||
}
|
||
/* boot 盖住期间禁止登录页入场动画,避免淡出瞬间叠 rise 抖动 */
|
||
html.app-booting .h5-login-main,
|
||
html.app-booting .h5-login-error {
|
||
animation: none !important;
|
||
}
|
||
@keyframes app-boot-spin {
|
||
from {
|
||
transform: translate(-50%, -50%) rotate(0deg);
|
||
}
|
||
to {
|
||
transform: translate(-50%, -50%) rotate(360deg);
|
||
}
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div id="app-boot" role="status" aria-live="polite">
|
||
<div class="boot-panel">
|
||
<div class="boot-brand">
|
||
<div class="boot-logo-wrap">
|
||
<img
|
||
class="boot-logo"
|
||
src="/boot-logo.png"
|
||
width="52"
|
||
height="52"
|
||
alt=""
|
||
decoding="sync"
|
||
fetchpriority="high"
|
||
/>
|
||
</div>
|
||
</div>
|
||
<div class="boot-status">
|
||
<div class="boot-spinner-slot">
|
||
<div class="boot-spinner" aria-hidden="true"></div>
|
||
<button type="button" class="boot-retry" id="app-boot-retry" onclick="location.reload()">
|
||
重新加载
|
||
</button>
|
||
</div>
|
||
<p class="boot-title" id="app-boot-title">正在加载系统…</p>
|
||
<p class="boot-sub" id="app-boot-sub">首次打开或网络较慢时可能稍等片刻</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div id="app"></div>
|
||
<script>
|
||
(function () {
|
||
var boot = document.getElementById('app-boot');
|
||
var title = document.getElementById('app-boot-title');
|
||
var sub = document.getElementById('app-boot-sub');
|
||
var retry = document.getElementById('app-boot-retry');
|
||
if (!boot) return;
|
||
|
||
var root = document.documentElement;
|
||
var isH5 = root.classList.contains('app-boot-h5');
|
||
var isEn = root.classList.contains('app-boot-en');
|
||
|
||
var copy = isH5
|
||
? isEn
|
||
? {
|
||
title: 'Loading…',
|
||
sub: 'Please wait a moment',
|
||
slow: 'Still loading, please wait…',
|
||
timeoutTitle: 'Taking too long',
|
||
timeoutSub: 'Check your network and try again',
|
||
retry: 'Reload',
|
||
}
|
||
: {
|
||
title: '正在加载…',
|
||
sub: '请稍候',
|
||
slow: '仍在加载,请稍候…',
|
||
timeoutTitle: '加载时间过长',
|
||
timeoutSub: '请检查网络后重试',
|
||
retry: '重新加载',
|
||
}
|
||
: isEn
|
||
? {
|
||
title: 'Loading…',
|
||
sub: 'First open or slow network may take a moment',
|
||
slow: 'Still loading resources…',
|
||
timeoutTitle: 'Taking too long',
|
||
timeoutSub: 'Check your network and try again',
|
||
retry: 'Reload',
|
||
}
|
||
: {
|
||
title: '正在加载系统…',
|
||
sub: '首次打开或网络较慢时可能稍等片刻',
|
||
slow: '仍在加载资源,请稍候…',
|
||
timeoutTitle: '加载时间过长',
|
||
timeoutSub: '请检查网络后重试;若持续失败,可刷新页面或稍后再试',
|
||
retry: '重新加载',
|
||
};
|
||
|
||
if (title) title.textContent = copy.title;
|
||
if (sub) sub.textContent = copy.sub;
|
||
if (retry) retry.textContent = copy.retry;
|
||
|
||
window.__dismissAppBoot = function () {
|
||
if (!boot || boot.classList.contains('is-leaving')) return;
|
||
boot.classList.add('is-leaving');
|
||
var done = false;
|
||
function removeBoot() {
|
||
if (done) return;
|
||
done = true;
|
||
document.documentElement.classList.remove('app-booting');
|
||
document.documentElement.classList.remove('app-boot-h5');
|
||
document.documentElement.classList.remove('app-boot-en');
|
||
if (boot && boot.parentNode) boot.parentNode.removeChild(boot);
|
||
}
|
||
boot.addEventListener('transitionend', removeBoot, { once: true });
|
||
window.setTimeout(removeBoot, 360);
|
||
};
|
||
|
||
window.setTimeout(function () {
|
||
if (window.__APP_MOUNTED__ || !document.getElementById('app-boot')) return;
|
||
if (sub) sub.textContent = copy.slow;
|
||
}, 4000);
|
||
window.setTimeout(function () {
|
||
if (window.__APP_MOUNTED__ || !document.getElementById('app-boot')) return;
|
||
boot.classList.add('is-timeout');
|
||
if (title) title.textContent = copy.timeoutTitle;
|
||
if (sub) sub.textContent = copy.timeoutSub;
|
||
}, 20000);
|
||
})();
|
||
</script>
|
||
<script type="module" src="/src/main.ts"></script>
|
||
</body>
|
||
</html>
|