feat: 页面背景预设与工作台增强,优化官网与登录体验
新增石米等淡色背景预设并设为默认,修复页面配置抽屉偶发不弹出问题,移除登录欢迎卡;同步 IDE 官网导航与背景样式,并扩展管理端工作台统计展示。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
7c08e83f38
commit
b5edbb8447
@ -8,8 +8,8 @@
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body {
|
||||
font-family: -apple-system, "PingFang SC", "Microsoft YaHei", sans-serif;
|
||||
background: #f7f8fa;
|
||||
color: #2c3e50;
|
||||
background: #f6f1e7;
|
||||
color: #1a2e38;
|
||||
line-height: 1.7;
|
||||
padding: 40px 20px;
|
||||
}
|
||||
@ -25,7 +25,7 @@
|
||||
text-align: center;
|
||||
margin-bottom: 36px;
|
||||
padding-bottom: 24px;
|
||||
border-bottom: 1px solid #eef0f3;
|
||||
border-bottom: 1px solid #ebe4d8;
|
||||
}
|
||||
.header .brand {
|
||||
display: inline-flex;
|
||||
@ -33,8 +33,8 @@
|
||||
gap: 8px;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: #003860;
|
||||
background: #E8F0F5;
|
||||
color: #054f74;
|
||||
background: #e0edf2;
|
||||
padding: 4px 12px;
|
||||
border-radius: 20px;
|
||||
margin-bottom: 14px;
|
||||
@ -57,26 +57,26 @@
|
||||
color: #1a2332;
|
||||
margin: 36px 0 18px;
|
||||
padding-left: 12px;
|
||||
border-left: 3px solid #003860;
|
||||
border-left: 3px solid #054f74;
|
||||
}
|
||||
|
||||
/* ========== 决策树 ========== */
|
||||
.decision-tree {
|
||||
background: linear-gradient(180deg, #f6f8fa 0%, #f9fafb 100%);
|
||||
background: linear-gradient(180deg, #fbf8f2 0%, #f6f1e7 100%);
|
||||
border-radius: 12px;
|
||||
padding: 36px 28px 32px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.q-card {
|
||||
background: #fff;
|
||||
border: 2px solid #e2e6eb;
|
||||
border: 2px solid #e0d9cc;
|
||||
border-radius: 10px;
|
||||
padding: 16px 24px;
|
||||
text-align: center;
|
||||
transition: all 0.25s ease;
|
||||
}
|
||||
.q-card:hover {
|
||||
border-color: #003860;
|
||||
border-color: #054f74;
|
||||
box-shadow: 0 4px 16px rgba(74,124,89,0.1);
|
||||
}
|
||||
.q-card .step-tag {
|
||||
@ -84,7 +84,7 @@
|
||||
font-size: 10px;
|
||||
font-weight: 700;
|
||||
color: #fff;
|
||||
background: #003860;
|
||||
background: #054f74;
|
||||
padding: 2px 8px;
|
||||
border-radius: 3px;
|
||||
margin-bottom: 8px;
|
||||
@ -101,7 +101,7 @@
|
||||
border-color: #d4c8a8;
|
||||
background: #fdfaf3;
|
||||
}
|
||||
.q-card.sub .step-tag { background: #1d7281; }
|
||||
.q-card.sub .step-tag { background: #054f74; }
|
||||
.connector {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
@ -150,9 +150,9 @@
|
||||
white-space: nowrap;
|
||||
}
|
||||
.option-label:hover { transform: scale(1.05); }
|
||||
.option-label.yes { background: #E8F0F5; color: #003860; }
|
||||
.option-label.yes { background: #e0edf2; color: #054f74; }
|
||||
.option-label.no { background: #faf0e0; color: #8a5a20; }
|
||||
.option-label.neutral { background: #eef0f3; color: #5a6472; }
|
||||
.option-label.neutral { background: #ebe4d8; color: #4a6270; }
|
||||
.sol-card {
|
||||
width: 100%;
|
||||
border-radius: 10px;
|
||||
@ -166,7 +166,7 @@
|
||||
box-shadow: 0 8px 24px rgba(0,0,0,0.08);
|
||||
}
|
||||
.sol-card.refund {
|
||||
background: linear-gradient(135deg, #E8F0F5 0%, #DCE8F0 100%);
|
||||
background: linear-gradient(135deg, #e0edf2 0%, #d4e4ec 100%);
|
||||
border-color: #b0ccb8;
|
||||
}
|
||||
.sol-card.exempt {
|
||||
@ -187,7 +187,7 @@
|
||||
font-size: 14.5px;
|
||||
font-weight: 700;
|
||||
}
|
||||
.sol-card.refund .sol-title { color: #003860; }
|
||||
.sol-card.refund .sol-title { color: #054f74; }
|
||||
.sol-card.exempt .sol-title { color: #8a5a20; }
|
||||
.sol-card.basic .sol-title { color: #4a5568; }
|
||||
.sol-card .sol-badge {
|
||||
@ -198,8 +198,8 @@
|
||||
color: #fff;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
.sol-card.refund .sol-badge { background: #003860; }
|
||||
.sol-card.exempt .sol-badge { background: #1d7281; }
|
||||
.sol-card.refund .sol-badge { background: #054f74; }
|
||||
.sol-card.exempt .sol-badge { background: #054f74; }
|
||||
.sol-card.basic .sol-badge { background: #7a8494; }
|
||||
.sol-card .sol-list {
|
||||
list-style: none;
|
||||
@ -221,8 +221,8 @@
|
||||
height: 5px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
.sol-card.refund .sol-list li::before { background: #003860; }
|
||||
.sol-card.exempt .sol-list li::before { background: #1d7281; }
|
||||
.sol-card.refund .sol-list li::before { background: #054f74; }
|
||||
.sol-card.exempt .sol-list li::before { background: #054f74; }
|
||||
.sol-card.basic .sol-list li::before { background: #7a8494; }
|
||||
.sol-card .sol-result {
|
||||
padding-top: 10px;
|
||||
@ -231,7 +231,7 @@
|
||||
font-weight: 600;
|
||||
line-height: 1.5;
|
||||
}
|
||||
.sol-card.refund .sol-result { border-color: #c0d4c6; color: #003860; }
|
||||
.sol-card.refund .sol-result { border-color: #c0d4c6; color: #054f74; }
|
||||
.sol-card.exempt .sol-result { border-color: #e0d0a8; color: #8a5a20; }
|
||||
.sol-card.basic .sol-result { border-color: #d0d5dc; color: #4a5568; }
|
||||
.sol-card .sol-tip {
|
||||
@ -282,11 +282,11 @@
|
||||
}
|
||||
.compare-table td {
|
||||
padding: 11px 14px;
|
||||
border-bottom: 1px solid #eef0f3;
|
||||
border-bottom: 1px solid #ebe4d8;
|
||||
color: #4a5568;
|
||||
}
|
||||
.compare-table tr:last-child td { border-bottom: none; }
|
||||
.compare-table .col-refund { color: #003860; }
|
||||
.compare-table .col-refund { color: #054f74; }
|
||||
.compare-table .col-exempt { color: #9a6a2a; }
|
||||
|
||||
/* ========== 我们的服务 ========== */
|
||||
@ -313,7 +313,7 @@
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
border-radius: 10px;
|
||||
background: linear-gradient(135deg, #E8F0F5, #e0ebe4);
|
||||
background: linear-gradient(135deg, #e0edf2, #ebe4d8);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
@ -335,8 +335,8 @@
|
||||
display: inline-block;
|
||||
font-size: 10px;
|
||||
font-weight: 600;
|
||||
color: #003860;
|
||||
background: #E8F0F5;
|
||||
color: #054f74;
|
||||
background: #e0edf2;
|
||||
padding: 2px 8px;
|
||||
border-radius: 3px;
|
||||
margin-top: 10px;
|
||||
@ -373,7 +373,7 @@
|
||||
font-weight: 600;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
.mode-card.single h4 { color: #003860; }
|
||||
.mode-card.single h4 { color: #054f74; }
|
||||
.mode-card.double h4 { color: #5a4a7a; }
|
||||
.mode-card .mode-sub {
|
||||
font-size: 12px;
|
||||
@ -407,7 +407,7 @@
|
||||
display: block;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
.mode-card.single .mode-result strong { color: #003860; }
|
||||
.mode-card.single .mode-result strong { color: #054f74; }
|
||||
.mode-card.double .mode-result strong { color: #5a4a7a; }
|
||||
.mode-note {
|
||||
margin-top: 16px;
|
||||
@ -451,7 +451,7 @@
|
||||
left: 0;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
background: #1d7281;
|
||||
background: #054f74;
|
||||
color: #fff;
|
||||
border-radius: 50%;
|
||||
font-size: 10px;
|
||||
@ -464,7 +464,7 @@
|
||||
/* ========== 底部CTA ========== */
|
||||
.cta-box {
|
||||
margin-top: 36px;
|
||||
background: linear-gradient(135deg, #003860 0%, #003860 100%);
|
||||
background: linear-gradient(135deg, #054f74 0%, #054f74 100%);
|
||||
border-radius: 12px;
|
||||
padding: 32px 36px;
|
||||
text-align: center;
|
||||
@ -516,7 +516,7 @@
|
||||
text-decoration: none;
|
||||
}
|
||||
.site-topbar__brand {
|
||||
color: #003860;
|
||||
color: #054f74;
|
||||
font-size: 15px;
|
||||
font-weight: 700;
|
||||
}
|
||||
@ -528,7 +528,7 @@
|
||||
height: 32px;
|
||||
padding: 0 14px;
|
||||
border-radius: 4px;
|
||||
background: #1d7281;
|
||||
background: #054f74;
|
||||
color: #fff;
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
|
||||
@ -3,15 +3,15 @@
|
||||
:root {
|
||||
--ai-header-h: 72px;
|
||||
--ai-ease: cubic-bezier(0.22, 1, 0.36, 1);
|
||||
--ai-bg: #e9ecf1;
|
||||
--ai-bg-soft: #f2f4f8;
|
||||
--ai-bg: #f5f4f1;
|
||||
--ai-bg-soft: #faf8f6;
|
||||
--ai-surface: #ffffff;
|
||||
--ai-text: #121820;
|
||||
--ai-text-2: #3a4450;
|
||||
--ai-text-3: #66707d;
|
||||
--ai-border: #d2d8e0;
|
||||
--ai-border-soft: #e2e7ee;
|
||||
--ai-input-bg: #e4e8ef;
|
||||
--ai-border: #e0d9cc;
|
||||
--ai-border-soft: #ebe4d8;
|
||||
--ai-input-bg: #f0ebe3;
|
||||
--ai-radius: 16px;
|
||||
--ai-radius-lg: 22px;
|
||||
--ai-radius-sm: 12px;
|
||||
@ -26,17 +26,18 @@
|
||||
rgba(255, 255, 255, 0.9) 52%,
|
||||
rgba(255, 255, 255, 0.95) 100%
|
||||
);
|
||||
--ai-glass-panel: linear-gradient(180deg, #ffffff 0%, #f8fafc 100%);
|
||||
--ai-glass-panel: linear-gradient(180deg, #ffffff 0%, var(--ai-bg-soft) 100%);
|
||||
--ai-glass-shadow: none;
|
||||
--ai-action: #1d7281;
|
||||
--ai-action-hover: #186170;
|
||||
--ai-action-rgb: 29, 114, 129;
|
||||
--ai-primary-soft: #d2e9ec;
|
||||
--ai-action: #054f74;
|
||||
--ai-action-hover: #054b6e;
|
||||
--ai-action-rgb: 5, 79, 116;
|
||||
--ai-primary-soft: #e0edf2;
|
||||
--ai-primary-mid: color-mix(in srgb, var(--ai-action) 16%, #fff);
|
||||
--ai-primary-softer: color-mix(in srgb, var(--ai-action) 10%, #fff);
|
||||
--ai-navy-rgb: var(--kc-primary-rgb, 0, 56, 96);
|
||||
--ai-navy-rgb: var(--kc-primary-rgb, 5, 79, 116);
|
||||
--ai-accent-rgb: var(--ai-action-rgb);
|
||||
--ai-canvas: var(--ai-bg);
|
||||
--ai-header-bg: color-mix(in srgb, var(--ai-bg) 94%, transparent);
|
||||
--ai-ink: var(--ai-text);
|
||||
--ai-muted: var(--ai-text-2);
|
||||
--ai-line: var(--ai-border);
|
||||
@ -44,6 +45,8 @@
|
||||
--ai-gold: var(--ai-action);
|
||||
--ai-content-max: 1120px;
|
||||
--ai-guide-panel-w: 520px;
|
||||
--ai-acrylic-bg: rgba(255, 255, 255, 0.32);
|
||||
--ai-acrylic-bg-primary: rgba(255, 255, 255, 0.38);
|
||||
}
|
||||
|
||||
body.command-home {
|
||||
@ -57,10 +60,10 @@ body.command-home #header.header,
|
||||
body.command-home #header.header.sticked {
|
||||
display: flex !important;
|
||||
padding: 10px 0;
|
||||
background: color-mix(in srgb, var(--ai-surface) 98%, transparent);
|
||||
background: var(--ai-header-bg);
|
||||
backdrop-filter: blur(14px) saturate(165%);
|
||||
-webkit-backdrop-filter: blur(14px) saturate(165%);
|
||||
border-bottom: 1px solid var(--ai-border);
|
||||
border-bottom: 1px solid color-mix(in srgb, var(--ai-border) 28%, transparent);
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
@ -84,6 +87,19 @@ body.command-home .site-nav-dock {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
body.command-home #navbar .site-nav-dock a {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
body.command-home #navbar .site-nav-dock a .site-nav-dock__icon {
|
||||
font-size: 1.05rem;
|
||||
line-height: 1;
|
||||
color: var(--ai-text-3);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
@media (max-width: 1279px) {
|
||||
body.command-home .site-nav-dock {
|
||||
display: contents;
|
||||
@ -115,22 +131,21 @@ body.command-home .site-nav-dock {
|
||||
z-index: 3;
|
||||
transform: translate(-50%, -50%);
|
||||
flex-direction: row;
|
||||
align-items: flex-end;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 2px;
|
||||
padding: 5px 6px 6px;
|
||||
gap: 4px;
|
||||
padding: 6px 8px;
|
||||
border-radius: 22px;
|
||||
border: 1px solid var(--ai-border);
|
||||
background: var(--ai-surface);
|
||||
backdrop-filter: blur(14px) saturate(165%);
|
||||
-webkit-backdrop-filter: blur(14px) saturate(165%);
|
||||
box-shadow: var(--ai-shadow);
|
||||
border: 1px solid rgba(255, 255, 255, 0.72);
|
||||
background: var(--ai-acrylic-bg);
|
||||
backdrop-filter: blur(28px) saturate(160%);
|
||||
-webkit-backdrop-filter: blur(28px) saturate(160%);
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
body.command-home #navbar li.site-nav-dock:hover > a,
|
||||
body.command-home #navbar .site-nav-dock:hover > a {
|
||||
color: var(--ai-text-2);
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
body.command-home #navbar .site-nav-dock a,
|
||||
@ -139,48 +154,47 @@ body.command-home .site-nav-dock {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 36px;
|
||||
padding: 0 16px;
|
||||
gap: 6px;
|
||||
min-height: 40px;
|
||||
padding: 8px 14px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid transparent;
|
||||
color: var(--ai-text-2);
|
||||
font-size: 0.84rem;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.01em;
|
||||
white-space: nowrap;
|
||||
background: transparent;
|
||||
transform-origin: 50% 100%;
|
||||
transition: background 0.18s var(--ai-ease), color 0.18s var(--ai-ease), transform 0.18s var(--ai-ease);
|
||||
transition:
|
||||
border-color 0.2s var(--ai-ease),
|
||||
background 0.2s var(--ai-ease),
|
||||
color 0.2s var(--ai-ease);
|
||||
}
|
||||
|
||||
body.command-home #navbar .site-nav-dock a .site-nav-dock__icon {
|
||||
font-size: 1rem;
|
||||
line-height: 1;
|
||||
color: var(--ai-text-3);
|
||||
transition: color 0.2s var(--ai-ease);
|
||||
}
|
||||
|
||||
body.command-home #navbar .site-nav-dock a:hover,
|
||||
body.command-home #navbar .site-nav-dock a.active,
|
||||
body.command-home #navbar .site-nav-dock a.active:focus {
|
||||
color: var(--ai-text);
|
||||
background: var(--ai-primary-soft);
|
||||
border-color: rgba(255, 255, 255, 0.92);
|
||||
background: rgba(255, 255, 255, 0.44);
|
||||
transform: none;
|
||||
}
|
||||
|
||||
body.command-home #navbar .site-nav-dock a.active,
|
||||
body.command-home #navbar .site-nav-dock a.active:focus {
|
||||
padding-right: 22px;
|
||||
color: var(--ai-action);
|
||||
}
|
||||
|
||||
body.command-home #navbar .site-nav-dock a:hover {
|
||||
transform: translateY(-3px) scale(1.06);
|
||||
}
|
||||
|
||||
body.command-home #navbar .site-nav-dock a.active::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 8px;
|
||||
left: auto;
|
||||
bottom: auto;
|
||||
width: 5px;
|
||||
height: 5px;
|
||||
border-radius: 50%;
|
||||
background: var(--ai-action);
|
||||
transform: translateY(-50%);
|
||||
box-shadow: 0 0 0 3px rgba(var(--ai-action-rgb), 0.16);
|
||||
body.command-home #navbar .site-nav-dock a:hover .site-nav-dock__icon,
|
||||
body.command-home #navbar .site-nav-dock a.active .site-nav-dock__icon {
|
||||
color: var(--ai-action);
|
||||
}
|
||||
|
||||
body.command-home #navbar > ul > li.site-nav-tools {
|
||||
@ -204,7 +218,7 @@ body.command-home #header .logo img {
|
||||
body.command-home #header .logo h1,
|
||||
body.command-home #header .brand-lockup h1,
|
||||
body.command-home #header .brand-lockup .brand-name {
|
||||
color: var(--kc-primary, #003860);
|
||||
color: var(--kc-primary, #054f74);
|
||||
font-size: 1.45rem;
|
||||
}
|
||||
|
||||
@ -247,16 +261,18 @@ body.command-home #navbar .language-toggle-link.active:focus {
|
||||
height: 32px;
|
||||
margin: 0;
|
||||
padding: 0 10px 0 8px;
|
||||
border: 1px solid var(--ai-border);
|
||||
border: 1px solid rgba(255, 255, 255, 0.72);
|
||||
border-radius: var(--ai-radius-sm);
|
||||
background: var(--ai-surface);
|
||||
color: var(--ai-text);
|
||||
background: var(--ai-acrylic-bg);
|
||||
backdrop-filter: blur(28px) saturate(160%);
|
||||
-webkit-backdrop-filter: blur(28px) saturate(160%);
|
||||
color: var(--ai-text-2);
|
||||
box-shadow: none;
|
||||
cursor: pointer;
|
||||
transition:
|
||||
border-color 0.2s var(--ai-ease),
|
||||
background 0.2s var(--ai-ease),
|
||||
box-shadow 0.2s var(--ai-ease);
|
||||
color 0.2s var(--ai-ease);
|
||||
}
|
||||
|
||||
body.command-home .language-toggle-icon {
|
||||
@ -275,10 +291,12 @@ body.command-home .language-toggle__next {
|
||||
color: var(--ai-action);
|
||||
}
|
||||
|
||||
body.command-home #navbar .language-toggle-link:hover {
|
||||
border-color: color-mix(in srgb, var(--ai-action) 35%, var(--ai-border));
|
||||
background: var(--ai-surface);
|
||||
box-shadow: 0 0 0 3px color-mix(in srgb, var(--ai-action) 12%, transparent);
|
||||
body.command-home #navbar .language-toggle-link:hover,
|
||||
body.command-home #navbar .language-toggle-link.active,
|
||||
body.command-home #navbar .language-toggle-link.active:focus {
|
||||
border-color: rgba(255, 255, 255, 0.92);
|
||||
background: rgba(255, 255, 255, 0.44);
|
||||
box-shadow: none;
|
||||
color: var(--ai-text);
|
||||
}
|
||||
|
||||
@ -310,48 +328,6 @@ body.command-home #ai-advisor-shell {
|
||||
background: var(--ai-bg);
|
||||
}
|
||||
|
||||
.ai-shell-bg__blobs,
|
||||
.ai-shell-bg__blobs::before,
|
||||
.ai-shell-bg__blobs::after {
|
||||
position: absolute;
|
||||
border-radius: 50%;
|
||||
filter: blur(68px);
|
||||
transform: translateZ(0);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.ai-shell-bg__blobs {
|
||||
width: min(88vw, 900px);
|
||||
height: min(72vh, 720px);
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
background: radial-gradient(circle, rgba(255, 255, 255, 0.9) 0%, rgba(255, 255, 255, 0.45) 50%, transparent 78%);
|
||||
animation: ai-bg-drift-a 20s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.ai-shell-bg__blobs::before {
|
||||
content: '';
|
||||
width: min(64vw, 640px);
|
||||
height: min(64vw, 640px);
|
||||
top: 6%;
|
||||
right: -8%;
|
||||
left: auto;
|
||||
transform: none;
|
||||
background: radial-gradient(circle, rgba(255, 255, 255, 0.75) 0%, rgba(255, 255, 255, 0.28) 48%, transparent 78%);
|
||||
animation: ai-bg-drift-b 24s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.ai-shell-bg__blobs::after {
|
||||
content: '';
|
||||
width: min(58vw, 580px);
|
||||
height: min(58vw, 580px);
|
||||
bottom: -6%;
|
||||
left: -2%;
|
||||
background: radial-gradient(circle, rgba(255, 255, 255, 0.7) 0%, rgba(255, 255, 255, 0.24) 46%, transparent 78%);
|
||||
animation: ai-bg-drift-c 18s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.ai-shell-bg__veil {
|
||||
display: none;
|
||||
}
|
||||
@ -387,7 +363,7 @@ body.command-home #ai-advisor-shell {
|
||||
width: min(50vw, 720px);
|
||||
height: min(58%, 760px);
|
||||
margin: 0;
|
||||
opacity: 0.62;
|
||||
opacity: 0.24;
|
||||
overflow: hidden;
|
||||
transform: none;
|
||||
-webkit-mask-image: linear-gradient(to right, #000 90%, transparent 100%),
|
||||
@ -645,39 +621,8 @@ body.command-home .ai-stage-map--eu {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
@keyframes ai-bg-drift-a {
|
||||
0%,
|
||||
100% {
|
||||
transform: translate(-50%, -50%) scale(1);
|
||||
}
|
||||
50% {
|
||||
transform: translate(calc(-50% + 2%), calc(-50% + 3%)) scale(1.05);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes ai-bg-drift-b {
|
||||
0%,
|
||||
100% {
|
||||
transform: translate(0, 0) scale(1);
|
||||
}
|
||||
50% {
|
||||
transform: translate(-5%, 4%) scale(1.1);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes ai-bg-drift-c {
|
||||
0%,
|
||||
100% {
|
||||
transform: translate(0, 0) scale(1);
|
||||
}
|
||||
50% {
|
||||
transform: translate(3%, -5%) scale(1.06);
|
||||
}
|
||||
}
|
||||
|
||||
/* iOS liquid glass — intent doors, reply cards */
|
||||
.ai-intent-door,
|
||||
.ai-card {
|
||||
/* iOS liquid glass — intent doors */
|
||||
.ai-intent-door {
|
||||
position: relative;
|
||||
isolation: isolate;
|
||||
overflow: hidden;
|
||||
@ -692,8 +637,7 @@ body.command-home .ai-stage-map--eu {
|
||||
transition: transform 0.22s var(--ai-ease), box-shadow 0.22s var(--ai-ease), border-color 0.22s var(--ai-ease);
|
||||
}
|
||||
|
||||
.ai-intent-door::before,
|
||||
.ai-card::before {
|
||||
.ai-intent-door::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
@ -707,8 +651,7 @@ body.command-home .ai-stage-map--eu {
|
||||
opacity: 0.75;
|
||||
}
|
||||
|
||||
.ai-intent-door::after,
|
||||
.ai-card::after {
|
||||
.ai-intent-door::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 120%;
|
||||
@ -724,8 +667,7 @@ body.command-home .ai-stage-map--eu {
|
||||
animation: ai-ios-glass-blob 10s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.ai-intent-door > *,
|
||||
.ai-card > * {
|
||||
.ai-intent-door > * {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
@ -742,10 +684,7 @@ body.command-home .ai-stage-map--eu {
|
||||
}
|
||||
}
|
||||
|
||||
.ai-composer,
|
||||
.ai-msg-row--assistant .ai-msg-bubble,
|
||||
.ai-guide-panel,
|
||||
.ai-history-sidebar {
|
||||
.ai-msg-row--assistant .ai-msg-bubble {
|
||||
position: relative;
|
||||
isolation: isolate;
|
||||
border: 1px solid var(--ai-border);
|
||||
@ -758,13 +697,32 @@ body.command-home .ai-stage-map--eu {
|
||||
}
|
||||
|
||||
.ai-composer {
|
||||
position: relative;
|
||||
isolation: isolate;
|
||||
overflow: hidden;
|
||||
min-height: 88px;
|
||||
padding: 16px 16px 16px 24px;
|
||||
border-radius: 20px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.72);
|
||||
background: var(--ai-acrylic-bg);
|
||||
backdrop-filter: blur(28px) saturate(160%);
|
||||
-webkit-backdrop-filter: blur(28px) saturate(160%);
|
||||
box-shadow: none;
|
||||
transition:
|
||||
border-color 0.2s var(--ai-ease),
|
||||
background 0.2s var(--ai-ease);
|
||||
}
|
||||
|
||||
.ai-composer::before,
|
||||
.ai-msg-row--assistant .ai-msg-bubble:not(:has(.ai-card))::before,
|
||||
.ai-guide-panel::before,
|
||||
.ai-history-sidebar::before {
|
||||
.ai-composer::before {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.ai-composer:focus-within {
|
||||
border-color: rgba(255, 255, 255, 0.92);
|
||||
background: rgba(255, 255, 255, 0.44);
|
||||
}
|
||||
|
||||
.ai-msg-row--assistant .ai-msg-bubble:not(:has(.ai-card))::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
@ -779,22 +737,13 @@ body.command-home .ai-stage-map--eu {
|
||||
}
|
||||
|
||||
.ai-composer > .ai-composer-form,
|
||||
.ai-msg-row--assistant .ai-msg-bubble > *,
|
||||
.ai-guide-panel > *,
|
||||
.ai-history-sidebar > * {
|
||||
.ai-msg-row--assistant .ai-msg-bubble > * {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.ai-card-opt,
|
||||
.ai-card-link,
|
||||
.ai-followup-chip,
|
||||
.ai-sidebar-toggle,
|
||||
.ai-new-chat-ghost,
|
||||
.ai-new-chat-btn,
|
||||
.ai-guide-panel__close,
|
||||
.ai-card__item,
|
||||
.ai-history-item {
|
||||
.ai-card-link {
|
||||
position: relative;
|
||||
isolation: isolate;
|
||||
border: 1px solid var(--ai-border);
|
||||
@ -809,13 +758,33 @@ body.command-home .ai-stage-map--eu {
|
||||
}
|
||||
|
||||
.ai-card-opt::before,
|
||||
.ai-card-link::before,
|
||||
.ai-followup-chip::before,
|
||||
.ai-card-link::before {
|
||||
content: none;
|
||||
}
|
||||
|
||||
/* 工具栏 / 快捷芯片 — 极简磨砂玻璃(与意图卡片、导航坞一致) */
|
||||
.ai-sidebar-toggle,
|
||||
.ai-new-chat-ghost,
|
||||
.ai-new-chat-btn,
|
||||
.ai-followup-chip,
|
||||
.ai-history-item {
|
||||
position: relative;
|
||||
isolation: isolate;
|
||||
border: 1px solid rgba(255, 255, 255, 0.72);
|
||||
background: var(--ai-acrylic-bg);
|
||||
backdrop-filter: blur(28px) saturate(160%);
|
||||
-webkit-backdrop-filter: blur(28px) saturate(160%);
|
||||
box-shadow: none;
|
||||
transition:
|
||||
border-color 0.2s var(--ai-ease),
|
||||
background 0.2s var(--ai-ease),
|
||||
color 0.2s var(--ai-ease);
|
||||
}
|
||||
|
||||
.ai-sidebar-toggle::before,
|
||||
.ai-new-chat-ghost::before,
|
||||
.ai-new-chat-btn::before,
|
||||
.ai-guide-panel__close::before,
|
||||
.ai-card__item::before,
|
||||
.ai-followup-chip::before,
|
||||
.ai-history-item::before {
|
||||
content: none;
|
||||
}
|
||||
@ -847,6 +816,16 @@ body.command-home .ai-stage-map--eu {
|
||||
top: 8px;
|
||||
left: 0;
|
||||
z-index: 3;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
padding: 4px;
|
||||
border-radius: 18px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.72);
|
||||
background: var(--ai-acrylic-bg);
|
||||
backdrop-filter: blur(28px) saturate(160%);
|
||||
-webkit-backdrop-filter: blur(28px) saturate(160%);
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.ai-sidebar-toggle,
|
||||
@ -856,9 +835,13 @@ body.command-home .ai-stage-map--eu {
|
||||
justify-content: center;
|
||||
gap: 6px;
|
||||
min-height: 34px;
|
||||
padding: 0 10px;
|
||||
border-radius: 12px;
|
||||
color: var(--ai-muted);
|
||||
padding: 0 12px;
|
||||
border-radius: 14px;
|
||||
border: 1px solid transparent;
|
||||
background: transparent;
|
||||
backdrop-filter: none;
|
||||
-webkit-backdrop-filter: none;
|
||||
color: var(--ai-text-2);
|
||||
font-size: 0.78rem;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
@ -867,14 +850,26 @@ body.command-home .ai-stage-map--eu {
|
||||
.ai-sidebar-toggle {
|
||||
width: 34px;
|
||||
padding: 0;
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
.ai-sidebar-toggle i,
|
||||
.ai-new-chat-ghost i {
|
||||
font-size: 0.95rem;
|
||||
line-height: 1;
|
||||
color: var(--ai-text-3);
|
||||
transition: color 0.2s var(--ai-ease);
|
||||
}
|
||||
|
||||
.ai-sidebar-toggle:hover,
|
||||
.ai-new-chat-ghost:hover {
|
||||
color: var(--ai-action);
|
||||
border-color: color-mix(in srgb, var(--ai-action) 35%, var(--ai-border));
|
||||
background: var(--ai-primary-soft);
|
||||
border-color: rgba(255, 255, 255, 0.92);
|
||||
background: rgba(255, 255, 255, 0.44);
|
||||
}
|
||||
|
||||
.ai-sidebar-toggle:hover i,
|
||||
.ai-new-chat-ghost:hover i {
|
||||
color: var(--ai-action);
|
||||
}
|
||||
|
||||
.ai-history-sidebar {
|
||||
@ -887,11 +882,20 @@ body.command-home .ai-stage-map--eu {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 16px 12px 20px;
|
||||
border-right: 1px solid var(--ai-border);
|
||||
border-right: 1px solid rgba(255, 255, 255, 0.55);
|
||||
background: color-mix(in srgb, var(--ai-bg) 90%, var(--ai-surface));
|
||||
backdrop-filter: blur(28px) saturate(160%);
|
||||
-webkit-backdrop-filter: blur(28px) saturate(160%);
|
||||
box-shadow: none;
|
||||
transform: translateX(-105%);
|
||||
transition: transform 0.28s var(--ai-ease);
|
||||
}
|
||||
|
||||
.ai-history-sidebar > * {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
#ai-advisor-shell.ai-history-open .ai-history-sidebar {
|
||||
transform: translateX(0);
|
||||
}
|
||||
@ -907,7 +911,9 @@ body.command-home .ai-stage-map--eu {
|
||||
inset: 0;
|
||||
z-index: 19;
|
||||
border: none;
|
||||
background: rgba(14, 29, 52, 0.28);
|
||||
background: color-mix(in srgb, var(--ai-bg) 42%, rgba(26, 46, 56, 0.08));
|
||||
backdrop-filter: blur(8px) saturate(140%);
|
||||
-webkit-backdrop-filter: blur(8px) saturate(140%);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@ -921,12 +927,25 @@ body.command-home .ai-stage-map--eu {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 22px 20px 24px;
|
||||
border-left: 1px solid var(--ai-border);
|
||||
border-left: 1px solid rgba(255, 255, 255, 0.55);
|
||||
background: color-mix(in srgb, var(--ai-bg) 90%, var(--ai-surface));
|
||||
backdrop-filter: blur(28px) saturate(160%);
|
||||
-webkit-backdrop-filter: blur(28px) saturate(160%);
|
||||
box-shadow: none;
|
||||
transform: translateX(105%);
|
||||
transition: transform 0.28s var(--ai-ease);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.ai-guide-panel::before {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.ai-guide-panel > * {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
#ai-advisor-shell.ai-guide-open .ai-guide-panel {
|
||||
transform: translateX(0);
|
||||
}
|
||||
@ -964,15 +983,24 @@ body.command-home .ai-stage-map--eu {
|
||||
justify-content: center;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 10px;
|
||||
color: var(--ai-muted);
|
||||
border-radius: 12px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.72);
|
||||
background: var(--ai-acrylic-bg);
|
||||
backdrop-filter: blur(28px) saturate(160%);
|
||||
-webkit-backdrop-filter: blur(28px) saturate(160%);
|
||||
box-shadow: none;
|
||||
color: var(--ai-text-2);
|
||||
cursor: pointer;
|
||||
transition:
|
||||
border-color 0.2s var(--ai-ease),
|
||||
background 0.2s var(--ai-ease),
|
||||
color 0.2s var(--ai-ease);
|
||||
}
|
||||
|
||||
.ai-guide-panel__close:hover {
|
||||
color: var(--ai-action);
|
||||
border-color: color-mix(in srgb, var(--ai-action) 35%, var(--ai-border));
|
||||
background: var(--ai-primary-soft);
|
||||
border-color: rgba(255, 255, 255, 0.92);
|
||||
background: rgba(255, 255, 255, 0.44);
|
||||
}
|
||||
|
||||
.ai-guide-panel__sub {
|
||||
@ -988,7 +1016,7 @@ body.command-home .ai-stage-map--eu {
|
||||
overflow-y: auto;
|
||||
display: grid;
|
||||
align-content: start;
|
||||
gap: 8px;
|
||||
gap: 10px;
|
||||
padding-right: 0;
|
||||
scrollbar-width: none;
|
||||
-ms-overflow-style: none;
|
||||
@ -1006,19 +1034,38 @@ body.command-home .ai-stage-map--eu {
|
||||
column-gap: 12px;
|
||||
min-height: 104px;
|
||||
padding: 18px 16px 18px 14px;
|
||||
border-radius: 16px;
|
||||
border-radius: 20px;
|
||||
text-align: left;
|
||||
color: inherit;
|
||||
cursor: pointer;
|
||||
border: 1px solid var(--ai-border-soft);
|
||||
background: var(--ai-surface);
|
||||
transition: border-color 0.2s var(--ai-ease), background 0.2s var(--ai-ease);
|
||||
overflow: hidden;
|
||||
border: 1px solid rgba(255, 255, 255, 0.72);
|
||||
background: var(--ai-acrylic-bg);
|
||||
backdrop-filter: blur(28px) saturate(160%);
|
||||
-webkit-backdrop-filter: blur(28px) saturate(160%);
|
||||
box-shadow: none;
|
||||
transition:
|
||||
border-color 0.2s var(--ai-ease),
|
||||
background 0.2s var(--ai-ease),
|
||||
color 0.2s var(--ai-ease);
|
||||
}
|
||||
|
||||
.ai-guide-card::before,
|
||||
.ai-guide-card::after {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.ai-guide-card:hover,
|
||||
.ai-guide-card:focus-visible {
|
||||
border-color: color-mix(in srgb, var(--ai-action) 28%, var(--ai-border-soft));
|
||||
background: color-mix(in srgb, var(--ai-primary-soft) 18%, var(--ai-surface));
|
||||
border-color: rgba(255, 255, 255, 0.92);
|
||||
background: rgba(255, 255, 255, 0.44);
|
||||
box-shadow: none;
|
||||
transform: none;
|
||||
}
|
||||
|
||||
.ai-guide-card:hover .ai-guide-card__title,
|
||||
.ai-guide-card:focus-visible .ai-guide-card__title {
|
||||
color: var(--ai-action);
|
||||
}
|
||||
|
||||
.ai-guide-card__icon {
|
||||
@ -1061,7 +1108,8 @@ body.command-home .ai-stage-map--eu {
|
||||
align-items: center;
|
||||
padding: 1px 7px;
|
||||
border-radius: 999px;
|
||||
background: var(--ai-primary-soft);
|
||||
border: 1px solid rgba(255, 255, 255, 0.55);
|
||||
background: rgba(255, 255, 255, 0.28);
|
||||
color: var(--ai-action);
|
||||
font-size: 0.68rem;
|
||||
font-weight: 700;
|
||||
@ -1115,19 +1163,26 @@ body.command-home .ai-stage-map--eu {
|
||||
margin-bottom: 12px;
|
||||
padding: 0 12px;
|
||||
border-radius: 14px;
|
||||
color: var(--ai-ink);
|
||||
color: var(--ai-text-2);
|
||||
font-size: 0.84rem;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.ai-new-chat-btn i {
|
||||
color: var(--ai-gold);
|
||||
font-size: 0.95rem;
|
||||
line-height: 1;
|
||||
color: var(--ai-text-3);
|
||||
transition: color 0.2s var(--ai-ease);
|
||||
}
|
||||
|
||||
.ai-new-chat-btn:hover {
|
||||
border-color: color-mix(in srgb, var(--ai-action) 35%, var(--ai-border));
|
||||
background: var(--ai-primary-soft);
|
||||
border-color: rgba(255, 255, 255, 0.92);
|
||||
background: rgba(255, 255, 255, 0.44);
|
||||
color: var(--ai-action);
|
||||
}
|
||||
|
||||
.ai-new-chat-btn:hover i {
|
||||
color: var(--ai-action);
|
||||
}
|
||||
|
||||
@ -1137,7 +1192,7 @@ body.command-home .ai-stage-map--eu {
|
||||
overflow-y: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
gap: 6px;
|
||||
scrollbar-width: none;
|
||||
-ms-overflow-style: none;
|
||||
}
|
||||
@ -1157,7 +1212,7 @@ body.command-home .ai-stage-map--eu {
|
||||
align-items: flex-start;
|
||||
gap: 10px;
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
padding: 10px 12px;
|
||||
border-radius: 14px;
|
||||
text-align: left;
|
||||
cursor: pointer;
|
||||
@ -1166,8 +1221,12 @@ body.command-home .ai-stage-map--eu {
|
||||
|
||||
.ai-history-item:hover,
|
||||
.ai-history-item.is-active {
|
||||
border-color: color-mix(in srgb, var(--ai-action) 35%, var(--ai-border));
|
||||
background: var(--ai-primary-soft);
|
||||
border-color: rgba(255, 255, 255, 0.92);
|
||||
background: rgba(255, 255, 255, 0.44);
|
||||
}
|
||||
|
||||
.ai-history-item.is-active .ai-history-item__title {
|
||||
color: var(--ai-action);
|
||||
}
|
||||
|
||||
.ai-history-item__icon {
|
||||
@ -1178,9 +1237,15 @@ body.command-home .ai-stage-map--eu {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border-radius: 8px;
|
||||
background: var(--ai-primary-soft);
|
||||
color: var(--ai-action);
|
||||
background: transparent;
|
||||
color: var(--ai-text-3);
|
||||
font-size: 0.85rem;
|
||||
transition: color 0.2s var(--ai-ease);
|
||||
}
|
||||
|
||||
.ai-history-item:hover .ai-history-item__icon,
|
||||
.ai-history-item.is-active .ai-history-item__icon {
|
||||
color: var(--ai-action);
|
||||
}
|
||||
|
||||
.ai-history-item__title {
|
||||
@ -1342,7 +1407,7 @@ body.command-home .ai-stage-map--eu {
|
||||
line-height: 1.22;
|
||||
white-space: normal;
|
||||
text-align: center;
|
||||
color: var(--kc-primary, #003860);
|
||||
color: var(--kc-primary, #054f74);
|
||||
}
|
||||
|
||||
.ai-welcome-sub {
|
||||
@ -1375,24 +1440,25 @@ body.command-home .ai-stage-map--eu {
|
||||
}
|
||||
|
||||
.ai-welcome__command .ai-intent-grid {
|
||||
gap: 6px 16px;
|
||||
gap: 10px 14px;
|
||||
margin-top: 4px;
|
||||
padding-top: 18px;
|
||||
border-top: 1px solid color-mix(in srgb, var(--ai-border) 50%, transparent);
|
||||
}
|
||||
|
||||
/* 首页意图卡片 — 极简磨砂玻璃 */
|
||||
.ai-welcome__command .ai-intent-door {
|
||||
border: 1px solid color-mix(in srgb, var(--ai-border) 82%, transparent);
|
||||
overflow: hidden;
|
||||
border: 1px solid rgba(255, 255, 255, 0.72);
|
||||
border-radius: 20px;
|
||||
padding: 14px 14px;
|
||||
background: var(--ai-acrylic-bg);
|
||||
backdrop-filter: blur(28px) saturate(160%);
|
||||
-webkit-backdrop-filter: blur(28px) saturate(160%);
|
||||
box-shadow: none;
|
||||
background: color-mix(in srgb, var(--ai-surface) 72%, transparent);
|
||||
backdrop-filter: none;
|
||||
-webkit-backdrop-filter: none;
|
||||
border-radius: 12px;
|
||||
padding: 12px 10px;
|
||||
transition:
|
||||
background 0.2s var(--ai-ease),
|
||||
border-color 0.2s var(--ai-ease),
|
||||
color 0.2s var(--ai-ease);
|
||||
background 0.2s var(--ai-ease);
|
||||
}
|
||||
|
||||
.ai-welcome__command .ai-intent-door::before,
|
||||
@ -1400,26 +1466,56 @@ body.command-home .ai-stage-map--eu {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.ai-welcome__command .ai-intent-door.is-primary {
|
||||
background: color-mix(in srgb, var(--ai-primary-soft) 38%, transparent);
|
||||
.ai-welcome__command .ai-intent-door__icon {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border: none;
|
||||
background: transparent;
|
||||
box-shadow: none;
|
||||
border: 1px solid color-mix(in srgb, var(--ai-action) 32%, var(--ai-border));
|
||||
border-left: 2px solid var(--ai-action);
|
||||
padding-left: 14px;
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.ai-welcome__command .ai-intent-door.is-primary {
|
||||
padding: 18px 16px;
|
||||
background: var(--ai-acrylic-bg-primary);
|
||||
border-color: rgba(255, 255, 255, 0.8);
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.ai-welcome__command .ai-intent-door.is-primary .ai-intent-door__icon {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
background: transparent;
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.ai-welcome__command .ai-intent-door.is-primary .ai-intent-door__title {
|
||||
color: var(--ai-action);
|
||||
}
|
||||
|
||||
.ai-welcome__command .ai-intent-door:hover {
|
||||
transform: none;
|
||||
border-color: color-mix(in srgb, var(--ai-action) 38%, var(--ai-border));
|
||||
background: color-mix(in srgb, var(--ai-primary-soft) 55%, transparent);
|
||||
border-color: rgba(255, 255, 255, 0.92);
|
||||
background: rgba(255, 255, 255, 0.44);
|
||||
box-shadow: none;
|
||||
transform: none;
|
||||
}
|
||||
|
||||
.ai-welcome__command .ai-intent-door.is-primary:hover {
|
||||
border-color: color-mix(in srgb, var(--ai-action) 45%, var(--ai-border));
|
||||
border-left-color: var(--ai-action);
|
||||
background: color-mix(in srgb, var(--ai-primary-soft) 65%, transparent);
|
||||
background: rgba(255, 255, 255, 0.48);
|
||||
box-shadow: none;
|
||||
transform: none;
|
||||
}
|
||||
|
||||
.ai-welcome__command .ai-intent-door__title {
|
||||
font-weight: 700;
|
||||
letter-spacing: -0.01em;
|
||||
}
|
||||
|
||||
.ai-welcome__command .ai-intent-door__hint {
|
||||
color: var(--ai-text-2);
|
||||
}
|
||||
|
||||
.ai-welcome-plane {
|
||||
@ -1633,7 +1729,7 @@ body.command-home .ai-stage-map--eu {
|
||||
font-weight: 800;
|
||||
letter-spacing: -0.02em;
|
||||
line-height: 1.1;
|
||||
color: var(--kc-primary, #003860);
|
||||
color: var(--kc-primary, #054f74);
|
||||
}
|
||||
|
||||
.ai-welcome-stats__label {
|
||||
@ -1963,10 +2059,28 @@ body.command-home .ai-stage-map--eu {
|
||||
40% { opacity: 1; transform: scale(1); }
|
||||
}
|
||||
|
||||
/* Cards inside assistant replies */
|
||||
/* Cards inside assistant replies — 极简磨砂玻璃 */
|
||||
.ai-card {
|
||||
position: relative;
|
||||
isolation: isolate;
|
||||
overflow: hidden;
|
||||
padding: 18px 18px 16px;
|
||||
border-radius: var(--ai-radius-lg);
|
||||
border-radius: 20px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.72);
|
||||
background: var(--ai-acrylic-bg);
|
||||
backdrop-filter: blur(28px) saturate(160%);
|
||||
-webkit-backdrop-filter: blur(28px) saturate(160%);
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.ai-card::before,
|
||||
.ai-card::after {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.ai-card > * {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.ai-card__kicker {
|
||||
@ -2014,9 +2128,9 @@ body.command-home .ai-stage-map--eu {
|
||||
padding: 10px 12px;
|
||||
border-radius: var(--ai-radius-sm);
|
||||
text-align: left;
|
||||
border-color: var(--ai-border-soft);
|
||||
background: var(--ai-primary-mid);
|
||||
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.9);
|
||||
border: 1px solid rgba(255, 255, 255, 0.55);
|
||||
background: transparent;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.ai-card__item strong {
|
||||
@ -2143,13 +2257,11 @@ body.command-home .ai-stage-map--eu {
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
background: var(--ai-primary-mid);
|
||||
border-color: color-mix(in srgb, var(--ai-action) 22%, var(--ai-border));
|
||||
}
|
||||
|
||||
.ai-followup-chip:hover {
|
||||
background: var(--ai-primary-soft);
|
||||
border-color: color-mix(in srgb, var(--ai-action) 45%, var(--ai-border));
|
||||
border-color: rgba(255, 255, 255, 0.92);
|
||||
background: rgba(255, 255, 255, 0.44);
|
||||
color: var(--ai-action);
|
||||
}
|
||||
|
||||
@ -2177,9 +2289,9 @@ body.command-home .ai-stage-map--eu {
|
||||
.ai-composer-wrap.is-welcome .ai-composer {
|
||||
min-height: 72px;
|
||||
padding: 12px 14px 12px 18px;
|
||||
border-radius: 14px;
|
||||
border: none;
|
||||
background: color-mix(in srgb, var(--ai-input-bg) 72%, transparent);
|
||||
border-radius: 16px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.72);
|
||||
background: var(--ai-acrylic-bg);
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
@ -2214,22 +2326,13 @@ body.command-home .ai-stage-map--eu {
|
||||
|
||||
.ai-composer-wrap.is-docked {
|
||||
padding: 10px 0 0;
|
||||
border-top: 1px solid color-mix(in srgb, var(--ai-border) 70%, transparent);
|
||||
border-top: 1px solid color-mix(in srgb, var(--ai-border) 45%, transparent);
|
||||
}
|
||||
|
||||
.ai-composer-wrap.is-docked .ai-composer {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.ai-composer {
|
||||
min-height: 88px;
|
||||
padding: 16px 16px 16px 24px;
|
||||
border-radius: var(--ai-radius-lg);
|
||||
transition:
|
||||
border-color 0.2s var(--ai-ease),
|
||||
box-shadow 0.2s var(--ai-ease);
|
||||
}
|
||||
|
||||
.ai-composer__caustic,
|
||||
.ai-composer__sheen {
|
||||
display: none;
|
||||
@ -2341,11 +2444,7 @@ body.command-home .ai-stage-map--eu {
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.ai-composer__sheen,
|
||||
.ai-composer__caustic,
|
||||
.ai-intent-door::after,
|
||||
.ai-card::after,
|
||||
.ai-shell-bg__blobs,
|
||||
.ai-shell-bg__blobs::before,
|
||||
.ai-shell-bg__blobs::after {
|
||||
.ai-intent-door::after {
|
||||
animation: none;
|
||||
}
|
||||
}
|
||||
@ -2453,7 +2552,7 @@ body.command-home .ai-stage-map--eu {
|
||||
width: min(54vw, 480px);
|
||||
height: min(46%, 480px);
|
||||
transform: none;
|
||||
opacity: 0.48;
|
||||
opacity: 0.38;
|
||||
}
|
||||
|
||||
.ai-stage-map--am {
|
||||
@ -2502,8 +2601,12 @@ body.command-home .ai-stage-map--eu {
|
||||
}
|
||||
|
||||
.ai-welcome__command .ai-intent-door.is-primary {
|
||||
padding-left: 10px;
|
||||
border-radius: 12px;
|
||||
padding: 16px 14px;
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
||||
.ai-welcome__command .ai-intent-door {
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
||||
.ai-welcome-plane img {
|
||||
|
||||
@ -18,9 +18,9 @@
|
||||
|
||||
/* Colors */
|
||||
:root {
|
||||
--color-default: #0a0d13;
|
||||
--color-primary: #0d42ff;
|
||||
--color-secondary: #0e1d34;
|
||||
--color-default: #1a2e38;
|
||||
--color-primary: #054f74;
|
||||
--color-secondary: #3d6678;
|
||||
}
|
||||
|
||||
/* Smooth scroll behavior */
|
||||
@ -139,7 +139,7 @@ section {
|
||||
|
||||
.breadcrumbs .page-header:before {
|
||||
content: "";
|
||||
background-color: rgba(14, 29, 52, 0.8);
|
||||
background: color-mix(in srgb, var(--kc-page-bg, #f5f4f1) 88%, var(--kc-brand, #054f74));
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
}
|
||||
@ -147,12 +147,12 @@ section {
|
||||
.breadcrumbs .page-header h2 {
|
||||
font-size: 56px;
|
||||
font-weight: 500;
|
||||
color: #fff;
|
||||
color: var(--kc-primary, #054f74);
|
||||
font-family: var(--font-secondary);
|
||||
}
|
||||
|
||||
.breadcrumbs .page-header p {
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
color: var(--kc-text-muted, #4a6270);
|
||||
}
|
||||
|
||||
.breadcrumbs nav {
|
||||
@ -390,7 +390,7 @@ section {
|
||||
.navbar .get-a-quote:hover,
|
||||
.navbar .get-a-quote:focus:hover {
|
||||
color: #fff;
|
||||
background: #2756ff;
|
||||
background: var(--kc-brand-hover, var(--color-primary));
|
||||
}
|
||||
|
||||
.navbar .get-a-quote.wms-link,
|
||||
@ -1723,7 +1723,7 @@ section {
|
||||
}
|
||||
|
||||
.hero form .btn-primary:hover {
|
||||
background-color: #2756ff;
|
||||
background-color: var(--kc-brand-hover, var(--color-primary));
|
||||
}
|
||||
|
||||
.hero .stats-item {
|
||||
@ -1995,15 +1995,24 @@ section {
|
||||
@import url('https://fonts.googleapis.com/css2?family=Fredoka:wght@400;600;700&display=swap');
|
||||
|
||||
:root {
|
||||
--kc-primary: #003860;
|
||||
--kc-primary-rgb: 0, 56, 96;
|
||||
--kc-secondary: #4A7088;
|
||||
--kc-tertiary: #1d7281;
|
||||
--kc-tertiary-rgb: 29, 114, 129;
|
||||
--kc-neutral: #F2F5F8;
|
||||
--kc-page-bg: #F2F5F8;
|
||||
--kc-surface: #FFFFFF;
|
||||
--kc-on-primary: #FFFFFF;
|
||||
/* IDE 品牌色 + 凝脂背景 — 与 OMS 主题 / 首页 command-home 一致 */
|
||||
--kc-brand: #054f74;
|
||||
--kc-brand-hover: #054b6e;
|
||||
--kc-brand-rgb: 5, 79, 116;
|
||||
--kc-brand-soft: #e0edf2;
|
||||
--kc-primary: #054f74;
|
||||
--kc-primary-rgb: 5, 79, 116;
|
||||
--kc-secondary: #3d6678;
|
||||
--kc-tertiary: #054f74;
|
||||
--kc-tertiary-rgb: 5, 79, 116;
|
||||
--kc-page-bg: #f5f4f1;
|
||||
--kc-neutral: #faf8f6;
|
||||
--kc-surface: #ffffff;
|
||||
--kc-border: #e0d9cc;
|
||||
--kc-border-soft: #ebe4d8;
|
||||
--kc-text: #1a2e38;
|
||||
--kc-text-muted: #4a6270;
|
||||
--kc-on-primary: #ffffff;
|
||||
--kc-radius-sm: 10px;
|
||||
--kc-radius-md: 20px;
|
||||
--kc-radius-lg: 32px;
|
||||
@ -2024,7 +2033,7 @@ section {
|
||||
|
||||
body {
|
||||
background: var(--kc-page-bg);
|
||||
color: var(--kc-primary);
|
||||
color: var(--kc-text);
|
||||
font-size: 1.05rem;
|
||||
line-height: 1.55;
|
||||
}
|
||||
@ -2055,8 +2064,9 @@ h2,
|
||||
|
||||
.header,
|
||||
.header.sticked {
|
||||
background: var(--kc-surface);
|
||||
box-shadow: 0 8px 24px rgba(var(--kc-primary-rgb), 0.12);
|
||||
background: color-mix(in srgb, var(--kc-page-bg) 94%, var(--kc-surface));
|
||||
border-bottom: 1px solid color-mix(in srgb, var(--kc-border) 28%, transparent);
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.header .logo h1 {
|
||||
@ -2113,11 +2123,11 @@ h2,
|
||||
.horizontal-pricing .buy-btn:hover,
|
||||
.contact .php-email-form button[type=submit]:hover,
|
||||
.get-a-quote .php-email-form button[type=submit]:hover {
|
||||
background: #f07a32;
|
||||
border-color: #f07a32;
|
||||
background: var(--kc-brand-hover);
|
||||
border-color: var(--kc-brand-hover);
|
||||
color: var(--kc-on-primary);
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 8px 20px rgba(var(--kc-tertiary-rgb), 0.35);
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.language-toggle-link,
|
||||
@ -2131,7 +2141,7 @@ h2,
|
||||
margin-top: 0;
|
||||
margin-left: 14px;
|
||||
padding: 0 4px 0 0;
|
||||
color: rgba(255, 255, 255, 0.65);
|
||||
color: var(--kc-text-muted);
|
||||
font-size: 1rem;
|
||||
font-weight: 400;
|
||||
letter-spacing: 0;
|
||||
@ -2179,7 +2189,7 @@ h2,
|
||||
line-height: 1.2;
|
||||
letter-spacing: 0.05em;
|
||||
text-align: center;
|
||||
color: rgba(255, 255, 255, 0.42);
|
||||
color: var(--kc-text-muted);
|
||||
overflow: hidden;
|
||||
max-width: 0;
|
||||
opacity: 0;
|
||||
@ -2191,14 +2201,14 @@ h2,
|
||||
max-width: 40px;
|
||||
padding: 3px 6px;
|
||||
opacity: 1;
|
||||
color: var(--kc-primary);
|
||||
background: var(--kc-tertiary);
|
||||
box-shadow: 0 1px 6px rgba(var(--kc-tertiary-rgb), 0.4);
|
||||
color: var(--kc-on-primary);
|
||||
background: var(--kc-brand);
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.language-toggle-link:hover {
|
||||
color: #fff;
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
color: var(--kc-primary);
|
||||
background: color-mix(in srgb, var(--kc-brand) 8%, var(--kc-surface));
|
||||
}
|
||||
|
||||
.language-toggle-link:hover .language-toggle-icon {
|
||||
@ -2206,9 +2216,9 @@ h2,
|
||||
}
|
||||
|
||||
.hero {
|
||||
background-color: #142637;
|
||||
background-color: var(--kc-page-bg);
|
||||
background-image: none;
|
||||
color: #fff;
|
||||
color: var(--kc-text);
|
||||
min-height: 60vh;
|
||||
}
|
||||
|
||||
@ -2216,7 +2226,7 @@ h2,
|
||||
font-size: 4.5rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: -0.02em;
|
||||
color: #fff;
|
||||
color: var(--kc-primary);
|
||||
line-height: 1.08;
|
||||
}
|
||||
|
||||
@ -2231,7 +2241,7 @@ h2,
|
||||
font-weight: 700;
|
||||
letter-spacing: -0.02em;
|
||||
line-height: 1.08;
|
||||
color: #fff;
|
||||
color: var(--kc-primary);
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
@ -2256,8 +2266,8 @@ h2,
|
||||
.breadcrumbs nav {
|
||||
background: var(--kc-surface);
|
||||
border-radius: 12px;
|
||||
border: 1px solid rgba(106, 117, 184, 0.22);
|
||||
box-shadow: 0 8px 24px rgba(27, 42, 107, 0.08);
|
||||
border: 1px solid var(--kc-border);
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.services .service-card.stat-card:hover {
|
||||
@ -2311,7 +2321,7 @@ h2,
|
||||
}
|
||||
|
||||
.section-header span {
|
||||
color: rgba(27, 42, 107, 0.12);
|
||||
color: color-mix(in srgb, var(--kc-primary) 10%, transparent);
|
||||
}
|
||||
|
||||
.featured-services .service-item .icon i,
|
||||
@ -2336,7 +2346,7 @@ h2,
|
||||
}
|
||||
|
||||
.contact .info-item i {
|
||||
background: rgba(27, 42, 107, 0.08);
|
||||
background: rgba(var(--kc-brand-rgb), 0.08);
|
||||
color: var(--kc-primary);
|
||||
border-radius: var(--kc-radius-md);
|
||||
width: 50px;
|
||||
@ -2377,7 +2387,7 @@ h2,
|
||||
}
|
||||
|
||||
.footer .social-links a {
|
||||
border-color: rgba(106, 117, 184, 0.35);
|
||||
border-color: rgba(var(--kc-brand-rgb), 0.35);
|
||||
border-radius: var(--kc-radius-md);
|
||||
width: 46px;
|
||||
height: 46px;
|
||||
@ -2444,7 +2454,7 @@ h2,
|
||||
.hero-stats {
|
||||
padding: 28px 0;
|
||||
background: var(--kc-surface, #fff);
|
||||
border-bottom: 1px solid rgba(27, 42, 107, 0.08);
|
||||
border-bottom: 1px solid rgba(var(--kc-brand-rgb), 0.08);
|
||||
}
|
||||
|
||||
.hero-stats .stats-item span {
|
||||
@ -2458,7 +2468,7 @@ h2,
|
||||
.hero-stats .stats-item p {
|
||||
margin: 6px 0 0;
|
||||
font-size: 0.9rem;
|
||||
color: var(--kc-secondary, #6a75b8);
|
||||
color: var(--kc-secondary, var(--kc-text-muted));
|
||||
}
|
||||
|
||||
.pricing-more-link {
|
||||
@ -2484,15 +2494,15 @@ h2,
|
||||
pointer-events: none;
|
||||
opacity: 0.35;
|
||||
background-image:
|
||||
linear-gradient(rgba(255, 180, 0, 0.08) 1px, transparent 1px),
|
||||
linear-gradient(90deg, rgba(255, 180, 0, 0.08) 1px, transparent 1px);
|
||||
linear-gradient(rgba(var(--kc-brand-rgb), 0.06) 1px, transparent 1px),
|
||||
linear-gradient(90deg, rgba(var(--kc-brand-rgb), 0.06) 1px, transparent 1px);
|
||||
background-size: 48px 48px;
|
||||
mask-image: linear-gradient(to bottom, rgba(0, 0, 0, 0.9), transparent 85%);
|
||||
}
|
||||
|
||||
.hero .hero-main-title,
|
||||
.hero h2 {
|
||||
color: #fff;
|
||||
color: var(--kc-primary);
|
||||
}
|
||||
|
||||
.hero-desc {
|
||||
@ -2500,7 +2510,7 @@ h2,
|
||||
max-width: 560px;
|
||||
font-size: 1rem;
|
||||
line-height: 1.65;
|
||||
color: rgba(255, 255, 255, 0.82);
|
||||
color: var(--kc-text-muted);
|
||||
}
|
||||
|
||||
.hero-cta-row {
|
||||
@ -2525,26 +2535,26 @@ h2,
|
||||
}
|
||||
|
||||
.btn-hero-primary {
|
||||
background: var(--kc-tertiary);
|
||||
color: var(--kc-primary);
|
||||
box-shadow: 0 8px 20px rgba(255, 180, 0, 0.35);
|
||||
background: var(--kc-brand);
|
||||
color: var(--kc-on-primary);
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.btn-hero-primary:hover {
|
||||
filter: brightness(1.05);
|
||||
color: var(--kc-primary);
|
||||
background: var(--kc-brand-hover);
|
||||
color: var(--kc-on-primary);
|
||||
}
|
||||
|
||||
.btn-hero-secondary {
|
||||
background: transparent;
|
||||
color: #fff;
|
||||
border: 1px solid rgba(255, 255, 255, 0.35);
|
||||
color: var(--kc-brand);
|
||||
border: 1px solid color-mix(in srgb, var(--kc-brand) 35%, var(--kc-border));
|
||||
}
|
||||
|
||||
.btn-hero-secondary:hover {
|
||||
border-color: rgba(255, 255, 255, 0.75);
|
||||
color: #fff;
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
border-color: var(--kc-brand);
|
||||
color: var(--kc-brand-hover);
|
||||
background: color-mix(in srgb, var(--kc-brand) 6%, var(--kc-surface));
|
||||
}
|
||||
|
||||
.hero-track-form {
|
||||
@ -2567,7 +2577,7 @@ h2,
|
||||
padding: 6px 6px 6px 28px;
|
||||
border-radius: 999px;
|
||||
border: none !important;
|
||||
box-shadow: 0 12px 40px rgba(0, 0, 0, 0.2);
|
||||
box-shadow: 0 8px 24px rgba(var(--kc-brand-rgb), 0.08);
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
@ -2580,7 +2590,7 @@ h2,
|
||||
}
|
||||
|
||||
.hero-track-stats .hero-track-form .form-control::placeholder {
|
||||
color: rgba(106, 117, 184, 0.75);
|
||||
color: rgba(var(--kc-brand-rgb), 0.75);
|
||||
}
|
||||
|
||||
.hero-track-stats .hero-track-form .btn-primary {
|
||||
@ -2635,10 +2645,10 @@ h2,
|
||||
}
|
||||
|
||||
.stat-card {
|
||||
--liquid-a: rgba(27, 42, 107, 0.1);
|
||||
--liquid-b: rgba(106, 117, 184, 0.08);
|
||||
--liquid-a: rgba(var(--kc-brand-rgb), 0.1);
|
||||
--liquid-b: rgba(var(--kc-brand-rgb), 0.08);
|
||||
--liquid-edge: rgba(255, 255, 255, 0.78);
|
||||
--liquid-glow: rgba(27, 42, 107, 0.05);
|
||||
--liquid-glow: rgba(var(--kc-brand-rgb), 0.05);
|
||||
|
||||
position: relative;
|
||||
z-index: 0;
|
||||
@ -2730,15 +2740,15 @@ h2,
|
||||
}
|
||||
|
||||
.stat-card--primary {
|
||||
--liquid-a: rgba(27, 42, 107, 0.12);
|
||||
--liquid-b: rgba(106, 117, 184, 0.08);
|
||||
--liquid-glow: rgba(27, 42, 107, 0.06);
|
||||
--liquid-a: rgba(var(--kc-brand-rgb), 0.12);
|
||||
--liquid-b: rgba(var(--kc-brand-rgb), 0.08);
|
||||
--liquid-glow: rgba(var(--kc-brand-rgb), 0.06);
|
||||
}
|
||||
|
||||
.stat-card--accent {
|
||||
--liquid-a: rgba(255, 180, 0, 0.14);
|
||||
--liquid-b: rgba(255, 210, 80, 0.1);
|
||||
--liquid-glow: rgba(255, 180, 0, 0.07);
|
||||
--liquid-a: rgba(var(--kc-brand-rgb), 0.12);
|
||||
--liquid-b: rgba(var(--kc-brand-rgb), 0.08);
|
||||
--liquid-glow: rgba(var(--kc-brand-rgb), 0.06);
|
||||
}
|
||||
|
||||
.stat-card--success {
|
||||
@ -2748,9 +2758,9 @@ h2,
|
||||
}
|
||||
|
||||
.stat-card--neutral {
|
||||
--liquid-a: rgba(106, 117, 184, 0.12);
|
||||
--liquid-a: rgba(var(--kc-brand-rgb), 0.12);
|
||||
--liquid-b: rgba(180, 190, 230, 0.08);
|
||||
--liquid-glow: rgba(106, 117, 184, 0.05);
|
||||
--liquid-glow: rgba(var(--kc-brand-rgb), 0.05);
|
||||
}
|
||||
|
||||
.how-step.stat-card:hover,
|
||||
@ -2802,22 +2812,22 @@ h2,
|
||||
letter-spacing: 0.02em;
|
||||
line-height: 1.2;
|
||||
color: var(--kc-primary);
|
||||
background: linear-gradient(135deg, rgba(255, 180, 0, 0.22) 0%, rgba(255, 180, 0, 0.08) 100%);
|
||||
border: 1px solid rgba(255, 180, 0, 0.35);
|
||||
background: color-mix(in srgb, var(--kc-brand) 10%, var(--kc-surface));
|
||||
border: 1px solid color-mix(in srgb, var(--kc-brand) 18%, var(--kc-border));
|
||||
clip-path: polygon(10px 0, 100% 0, calc(100% - 10px) 100%, 0 100%);
|
||||
-webkit-clip-path: polygon(10px 0, 100% 0, calc(100% - 10px) 100%, 0 100%);
|
||||
}
|
||||
|
||||
.poly-highlight--primary {
|
||||
color: #fff;
|
||||
background: linear-gradient(135deg, var(--kc-primary) 0%, #2a3d8f 100%);
|
||||
border-color: rgba(27, 42, 107, 0.2);
|
||||
background: linear-gradient(135deg, var(--kc-primary) 0%, color-mix(in srgb, var(--kc-brand) 75%, #1a3d52) 100%);
|
||||
border-color: rgba(var(--kc-brand-rgb), 0.2);
|
||||
}
|
||||
|
||||
.poly-highlight--soft {
|
||||
color: var(--kc-secondary);
|
||||
background: rgba(27, 42, 107, 0.06);
|
||||
border-color: rgba(27, 42, 107, 0.1);
|
||||
background: rgba(var(--kc-brand-rgb), 0.06);
|
||||
border-color: rgba(var(--kc-brand-rgb), 0.1);
|
||||
clip-path: polygon(8px 0, 100% 0, calc(100% - 8px) 100%, 0 100%);
|
||||
-webkit-clip-path: polygon(8px 0, 100% 0, calc(100% - 8px) 100%, 0 100%);
|
||||
}
|
||||
@ -2954,7 +2964,7 @@ h2,
|
||||
justify-content: center;
|
||||
margin-bottom: 16px;
|
||||
border-radius: 0;
|
||||
background: rgba(27, 42, 107, 0.08);
|
||||
background: rgba(var(--kc-brand-rgb), 0.08);
|
||||
color: var(--kc-primary);
|
||||
font-size: 1.5rem;
|
||||
clip-path: polygon(15% 0, 100% 0, 85% 100%, 0 100%);
|
||||
@ -3094,7 +3104,7 @@ h2,
|
||||
.faq .faq-accordion .accordion-item {
|
||||
background: transparent;
|
||||
border: none;
|
||||
border-bottom: 1px solid rgba(27, 42, 107, 0.08);
|
||||
border-bottom: 1px solid rgba(var(--kc-brand-rgb), 0.08);
|
||||
}
|
||||
|
||||
.faq .faq-accordion .accordion-item:last-child {
|
||||
@ -3220,7 +3230,7 @@ h2,
|
||||
}
|
||||
|
||||
.call-to-action .cta-btn-outline {
|
||||
border-color: rgba(27, 42, 107, 0.25) !important;
|
||||
border-color: rgba(var(--kc-brand-rgb), 0.25) !important;
|
||||
color: var(--kc-primary) !important;
|
||||
}
|
||||
|
||||
@ -3242,7 +3252,7 @@ h2,
|
||||
border: 1px solid rgba(255, 255, 255, 0.78);
|
||||
box-shadow:
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.9),
|
||||
0 6px 18px rgba(27, 42, 107, 0.05),
|
||||
0 6px 18px rgba(var(--kc-brand-rgb), 0.05),
|
||||
0 1px 2px rgba(15, 23, 42, 0.03);
|
||||
}
|
||||
|
||||
@ -3339,7 +3349,7 @@ h2,
|
||||
border-radius: 12px;
|
||||
background: linear-gradient(155deg, rgba(255, 255, 255, 0.92) 0%, rgba(255, 255, 255, 0.78) 100%);
|
||||
border: 1px solid rgba(255, 255, 255, 0.78);
|
||||
box-shadow: 0 4px 14px rgba(27, 42, 107, 0.05);
|
||||
box-shadow: 0 4px 14px rgba(var(--kc-brand-rgb), 0.05);
|
||||
}
|
||||
|
||||
.glass-panel,
|
||||
@ -3361,7 +3371,7 @@ h2,
|
||||
background: linear-gradient(155deg, rgba(255, 255, 255, 0.92) 0%, rgba(255, 255, 255, 0.78) 48%, rgba(255, 255, 255, 0.88) 100%);
|
||||
box-shadow:
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.9),
|
||||
0 6px 18px rgba(27, 42, 107, 0.05),
|
||||
0 6px 18px rgba(var(--kc-brand-rgb), 0.05),
|
||||
0 1px 2px rgba(15, 23, 42, 0.03);
|
||||
backdrop-filter: blur(12px) saturate(1.15);
|
||||
-webkit-backdrop-filter: blur(12px) saturate(1.15);
|
||||
@ -3461,8 +3471,8 @@ h2,
|
||||
padding: 16px;
|
||||
margin-bottom: 12px;
|
||||
border-radius: 12px;
|
||||
background: rgba(27, 42, 107, 0.04);
|
||||
border: 1px solid rgba(106, 117, 184, 0.12);
|
||||
background: rgba(var(--kc-brand-rgb), 0.04);
|
||||
border: 1px solid rgba(var(--kc-brand-rgb), 0.12);
|
||||
}
|
||||
|
||||
.contact .php-email-form,
|
||||
|
||||
@ -842,7 +842,6 @@
|
||||
shell.id = 'ai-advisor-shell';
|
||||
shell.innerHTML = `
|
||||
<div class="ai-shell-bg" aria-hidden="true">
|
||||
<div class="ai-shell-bg__blobs" aria-hidden="true"></div>
|
||||
<div class="ai-shell-bg__veil" aria-hidden="true"></div>
|
||||
</div>
|
||||
<div class="ai-stage-maps" aria-hidden="true"></div>
|
||||
@ -1468,7 +1467,7 @@
|
||||
const heroCta = shell.querySelector('.ai-welcome-cta');
|
||||
if (heroCta) {
|
||||
heroCta.textContent = t('heroCta');
|
||||
heroCta.href = registerUrl();
|
||||
heroCta.href = loginPortalUrl();
|
||||
}
|
||||
renderWelcomeStats();
|
||||
const mapHint = shell.querySelector('.ai-cover-panel__hint');
|
||||
@ -1931,7 +1930,7 @@
|
||||
renderStaticCopy();
|
||||
},
|
||||
open: () => {
|
||||
if (!isHomePage()) window.location.href = 'index.html';
|
||||
if (!isHomePage()) window.location.href = '/';
|
||||
},
|
||||
close: () => closeDrawers(),
|
||||
};
|
||||
|
||||
@ -7,6 +7,112 @@
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
"use strict";
|
||||
|
||||
const pageBackgroundStorageKey = 'app-page-background-color';
|
||||
const themeColorStorageKey = 'app-theme-color';
|
||||
const DEFAULT_THEME_COLOR = '#054f74';
|
||||
|
||||
function hexToRgb(hex) {
|
||||
const normalized = hex.replace(/^#/, '');
|
||||
if (!/^[a-f\d]{6}$/i.test(normalized)) return null;
|
||||
return {
|
||||
r: parseInt(normalized.slice(0, 2), 16),
|
||||
g: parseInt(normalized.slice(2, 4), 16),
|
||||
b: parseInt(normalized.slice(4, 6), 16),
|
||||
};
|
||||
}
|
||||
|
||||
function darkenHex(hex, ratio) {
|
||||
const rgb = hexToRgb(hex);
|
||||
if (!rgb) return hex;
|
||||
const toHex = (value) => Math.round(value).toString(16).padStart(2, '0');
|
||||
return (
|
||||
'#' +
|
||||
toHex(rgb.r * (1 - ratio)) +
|
||||
toHex(rgb.g * (1 - ratio)) +
|
||||
toHex(rgb.b * (1 - ratio))
|
||||
);
|
||||
}
|
||||
|
||||
function lightenHex(hex, mixWhite) {
|
||||
const rgb = hexToRgb(hex);
|
||||
if (!rgb) return hex;
|
||||
const mix = typeof mixWhite === 'number' ? mixWhite : 0.92;
|
||||
const toHex = (value) => Math.round(value).toString(16).padStart(2, '0');
|
||||
return (
|
||||
'#' +
|
||||
toHex(rgb.r * (1 - mix) + 255 * mix) +
|
||||
toHex(rgb.g * (1 - mix) + 255 * mix) +
|
||||
toHex(rgb.b * (1 - mix) + 255 * mix)
|
||||
);
|
||||
}
|
||||
|
||||
function applyStoredThemeColor() {
|
||||
let color = '';
|
||||
try {
|
||||
color = localStorage.getItem(themeColorStorageKey) || '';
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
if (!color) color = DEFAULT_THEME_COLOR;
|
||||
if (!color.startsWith('#')) color = `#${color}`;
|
||||
|
||||
const root = document.documentElement;
|
||||
const hover = darkenHex(color, 0.05);
|
||||
const soft = lightenHex(color, 0.9);
|
||||
const rgb = hexToRgb(color);
|
||||
const brandTokens = ['--kc-brand', '--kc-primary', '--kc-tertiary', '--color-primary', '--ai-action'];
|
||||
brandTokens.forEach((token) => root.style.setProperty(token, color));
|
||||
root.style.setProperty('--kc-brand-hover', hover);
|
||||
root.style.setProperty('--ai-action-hover', hover);
|
||||
root.style.setProperty('--kc-brand-soft', soft);
|
||||
root.style.setProperty('--ai-primary-soft', soft);
|
||||
if (rgb) {
|
||||
const rgbStr = `${rgb.r}, ${rgb.g}, ${rgb.b}`;
|
||||
['--kc-brand-rgb', '--kc-primary-rgb', '--kc-tertiary-rgb', '--ai-action-rgb', '--ai-navy-rgb'].forEach(
|
||||
(token) => root.style.setProperty(token, rgbStr),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function softenHex(hex, mixWhite) {
|
||||
const normalized = hex.replace(/^#/, '');
|
||||
if (!/^[a-f\d]{6}$/i.test(normalized)) return hex;
|
||||
const r = parseInt(normalized.slice(0, 2), 16);
|
||||
const g = parseInt(normalized.slice(2, 4), 16);
|
||||
const b = parseInt(normalized.slice(4, 6), 16);
|
||||
const mix = typeof mixWhite === 'number' ? mixWhite : 0.55;
|
||||
const toHex = (value) => Math.round(value).toString(16).padStart(2, '0');
|
||||
return (
|
||||
'#' +
|
||||
toHex(r * (1 - mix) + 255 * mix) +
|
||||
toHex(g * (1 - mix) + 255 * mix) +
|
||||
toHex(b * (1 - mix) + 255 * mix)
|
||||
);
|
||||
}
|
||||
|
||||
function applyStoredPageBackground() {
|
||||
let color = '';
|
||||
try {
|
||||
color = localStorage.getItem(pageBackgroundStorageKey) || '';
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
if (!color) return;
|
||||
if (!color.startsWith('#')) color = `#${color}`;
|
||||
const root = document.documentElement;
|
||||
const warmPageBgs = new Set(['#f6f1e7', '#f5f4f1', '#faf9f7']);
|
||||
const soft = softenHex(color, warmPageBgs.has(color.toLowerCase()) ? 0.42 : 0.55);
|
||||
root.style.setProperty('--ai-bg', color);
|
||||
root.style.setProperty('--ai-canvas', color);
|
||||
root.style.setProperty('--ai-bg-soft', soft);
|
||||
root.style.setProperty('--ai-header-bg', `color-mix(in srgb, ${color} 94%, transparent)`);
|
||||
root.style.setProperty('--kc-page-bg', color);
|
||||
root.style.setProperty('--kc-neutral', soft);
|
||||
}
|
||||
|
||||
applyStoredThemeColor();
|
||||
applyStoredPageBackground();
|
||||
|
||||
const languageStorageKey = 'ide-language';
|
||||
const pageTitleMap = {
|
||||
'IDE|Faster & Simpler': 'IDE|更快 · 更简单',
|
||||
@ -447,6 +553,9 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
if (!element || element.hasAttribute('data-i18n-key')) {
|
||||
return;
|
||||
}
|
||||
if (element.closest('.site-nav-dock')) {
|
||||
return;
|
||||
}
|
||||
const key = resolveEnglishKey(element.textContent || '');
|
||||
if (key) {
|
||||
element.setAttribute('data-i18n-key', key);
|
||||
@ -487,7 +596,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
'.footer-info > p',
|
||||
'.footer-links h4',
|
||||
'.footer-contact h4',
|
||||
'#navbar ul li > a:not(#language-toggle-btn)',
|
||||
'#navbar ul > li:not(.site-nav-dock) > a:not(#language-toggle-btn)',
|
||||
].forEach((selector) => {
|
||||
document.querySelectorAll(selector).forEach(tagI18nElement);
|
||||
});
|
||||
|
||||
@ -44,11 +44,26 @@
|
||||
|
||||
<ul>
|
||||
<li class="site-nav-dock">
|
||||
<a href="/" class="active" data-i18n-key="Home">Home</a>
|
||||
<a href="#services" data-i18n-key="Services">Services</a>
|
||||
<a href="#pricing" data-i18n-key="Pricing">Pricing</a>
|
||||
<a href="#about" data-i18n-key="About">About</a>
|
||||
<a href="#contact" data-i18n-key="Contact">Contact</a>
|
||||
<a href="/" class="active">
|
||||
<i class="bi bi-house-door site-nav-dock__icon" aria-hidden="true"></i>
|
||||
<span data-i18n-key="Home">Home</span>
|
||||
</a>
|
||||
<a href="#services">
|
||||
<i class="bi bi-box-seam site-nav-dock__icon" aria-hidden="true"></i>
|
||||
<span data-i18n-key="Services">Services</span>
|
||||
</a>
|
||||
<a href="#pricing">
|
||||
<i class="bi bi-tag site-nav-dock__icon" aria-hidden="true"></i>
|
||||
<span data-i18n-key="Pricing">Pricing</span>
|
||||
</a>
|
||||
<a href="#about">
|
||||
<i class="bi bi-building site-nav-dock__icon" aria-hidden="true"></i>
|
||||
<span data-i18n-key="About">About</span>
|
||||
</a>
|
||||
<a href="#contact">
|
||||
<i class="bi bi-envelope site-nav-dock__icon" aria-hidden="true"></i>
|
||||
<span data-i18n-key="Contact">Contact</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="site-nav-tools">
|
||||
<button type="button" id="language-toggle-btn" class="language-toggle-link" aria-label="Switch language">
|
||||
|
||||
12
index.html
12
index.html
@ -87,7 +87,7 @@
|
||||
*/
|
||||
html {
|
||||
scrollbar-gutter: stable;
|
||||
background: #f0f3f4;
|
||||
background: #f5f4f1;
|
||||
}
|
||||
html.app-booting,
|
||||
html.app-booting body {
|
||||
@ -99,7 +99,7 @@
|
||||
#app {
|
||||
margin: 0;
|
||||
min-height: 100%;
|
||||
background: #f0f3f4;
|
||||
background: #f5f4f1;
|
||||
}
|
||||
#app-boot {
|
||||
position: fixed;
|
||||
@ -114,7 +114,7 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: #f0f3f4;
|
||||
background: #f5f4f1;
|
||||
/*
|
||||
* 必须与 global.less 首屏后状态一致:
|
||||
* Vue 加载后 html 会带上 antialiased,若 boot 首帧没有,中文会突然变细像「字号缩小」。
|
||||
@ -187,7 +187,7 @@
|
||||
height: 22px;
|
||||
margin: 0;
|
||||
border: 2px solid #e5e6eb;
|
||||
border-top-color: var(--app-primary-color, #007979);
|
||||
border-top-color: var(--app-primary-color, #054f74);
|
||||
border-radius: 50%;
|
||||
box-sizing: border-box;
|
||||
transform: translate(-50%, -50%);
|
||||
@ -216,8 +216,8 @@
|
||||
white-space: nowrap;
|
||||
}
|
||||
#app-boot .boot-retry:hover {
|
||||
border-color: var(--app-primary-color, #007979);
|
||||
color: var(--app-primary-color, #007979);
|
||||
border-color: var(--app-primary-color, #054f74);
|
||||
color: var(--app-primary-color, #054f74);
|
||||
}
|
||||
#app-boot.is-timeout .boot-spinner {
|
||||
opacity: 0;
|
||||
|
||||
@ -55,7 +55,17 @@ server {
|
||||
add_header Cache-Control "no-cache, no-store, must-revalidate";
|
||||
}
|
||||
|
||||
# 官网静态资源(/site/index.html 通过 base href 引用 /site/assets/...)
|
||||
# 官网仅静态资源在 /site/;首页保持在 /
|
||||
location = /site {
|
||||
return 301 /;
|
||||
}
|
||||
location = /site/ {
|
||||
return 301 /;
|
||||
}
|
||||
location = /site/index.html {
|
||||
return 301 /;
|
||||
}
|
||||
|
||||
location ^~ /site/ {
|
||||
try_files $uri =404;
|
||||
expires 7d;
|
||||
|
||||
@ -22,6 +22,16 @@ server {
|
||||
add_header Cache-Control "no-cache, no-store, must-revalidate";
|
||||
}
|
||||
|
||||
location = /site {
|
||||
return 301 /;
|
||||
}
|
||||
location = /site/ {
|
||||
return 301 /;
|
||||
}
|
||||
location = /site/index.html {
|
||||
return 301 /;
|
||||
}
|
||||
|
||||
location ^~ /site/ {
|
||||
try_files $uri =404;
|
||||
expires 7d;
|
||||
|
||||
@ -26,9 +26,11 @@ function copyDirSync(src, dest, options = {}) {
|
||||
}
|
||||
}
|
||||
|
||||
function injectSiteBaseHref(html) {
|
||||
if (html.includes('<base ')) return html;
|
||||
return html.replace('<head>', '<head>\n <base href="/site/">');
|
||||
/** 生产环境首页在 /,静态资源在 /site/assets/;勿用 base href(会把地址栏带到 /site/) */
|
||||
function rewriteSiteAssetPaths(content) {
|
||||
return content
|
||||
.replace(/((?:href|src)=["'])assets\//g, '$1/site/assets/')
|
||||
.replace(/(["'])assets\//g, '$1/site/assets/');
|
||||
}
|
||||
|
||||
if (fs.existsSync(legacyLoginDir)) {
|
||||
@ -49,10 +51,22 @@ function copyOfficialSite() {
|
||||
skipDirNames: new Set(['.well-known']),
|
||||
});
|
||||
|
||||
const siteIndex = path.join(siteDir, 'index.html');
|
||||
if (fs.existsSync(siteIndex)) {
|
||||
const html = fs.readFileSync(siteIndex, 'utf8');
|
||||
fs.writeFileSync(siteIndex, injectSiteBaseHref(html), 'utf8');
|
||||
for (const rel of ['index.html', '404.html', '9610.html']) {
|
||||
const filePath = path.join(siteDir, rel);
|
||||
if (!fs.existsSync(filePath)) continue;
|
||||
const html = fs.readFileSync(filePath, 'utf8');
|
||||
fs.writeFileSync(filePath, rewriteSiteAssetPaths(html), 'utf8');
|
||||
}
|
||||
|
||||
const jsDir = path.join(siteDir, 'assets', 'js');
|
||||
if (fs.existsSync(jsDir)) {
|
||||
for (const name of fs.readdirSync(jsDir)) {
|
||||
if (!name.endsWith('.js')) continue;
|
||||
const filePath = path.join(jsDir, name);
|
||||
const js = fs.readFileSync(filePath, 'utf8');
|
||||
if (!js.includes('assets/')) continue;
|
||||
fs.writeFileSync(filePath, rewriteSiteAssetPaths(js), 'utf8');
|
||||
}
|
||||
}
|
||||
|
||||
const src9610 = path.join(siteSrc, '9610.html');
|
||||
|
||||
38
src/App.vue
38
src/App.vue
@ -1,52 +1,42 @@
|
||||
<template>
|
||||
<a-config-provider :locale="locale">
|
||||
<router-view />
|
||||
<AdminLoginCareToast v-if="showAdminCareToast" />
|
||||
<GlobalSetting v-if="showGlobalSetting" />
|
||||
</a-config-provider>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, defineAsyncComponent } from 'vue';
|
||||
import { computed } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import enUS from '@arco-design/web-vue/es/locale/lang/en-us';
|
||||
import zhCN from '@arco-design/web-vue/es/locale/lang/zh-cn';
|
||||
import useLocale from '@/hooks/locale';
|
||||
import { isLoginRouteName } from '@/utils/login-portal';
|
||||
|
||||
/** Settings drawer not needed on login; skip load to shrink first paint */
|
||||
const GlobalSetting = defineAsyncComponent(
|
||||
() => import('@/components/global-setting/index.vue'),
|
||||
);
|
||||
/** 管理端关怀 Toast:H5 不挂,避免无关模块进客户首屏 */
|
||||
const AdminLoginCareToast = defineAsyncComponent(
|
||||
() => import('@/views/login/components/AdminLoginCareToast.vue'),
|
||||
);
|
||||
import GlobalSetting from '@/components/global-setting/index.vue';
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
function isH5LikePath(path: string) {
|
||||
function isStandalonePortalPath(path: string) {
|
||||
return (
|
||||
path.startsWith('/h5') ||
|
||||
path.startsWith('/wechat/') ||
|
||||
path.startsWith('/track/')
|
||||
path === '/track/query' ||
|
||||
path.startsWith('/track/query/')
|
||||
);
|
||||
}
|
||||
|
||||
// route.name can be empty on first tick — do not load settings until we know it's not login
|
||||
const showGlobalSetting = computed(() => {
|
||||
const name = route.name ? String(route.name) : '';
|
||||
if (!name) return false;
|
||||
if (isLoginRouteName(name)) return false;
|
||||
const path = String(route.path || '');
|
||||
if (isH5LikePath(path)) return false;
|
||||
return true;
|
||||
});
|
||||
function isLoginPath(path: string) {
|
||||
return path === '/login' || path === '/admin' || path === '/h5/login';
|
||||
}
|
||||
|
||||
const showAdminCareToast = computed(() => {
|
||||
const showGlobalSetting = computed(() => {
|
||||
const path = String(route.path || '');
|
||||
if (!path) return false;
|
||||
return !isH5LikePath(path);
|
||||
if (isStandalonePortalPath(path)) return false;
|
||||
if (isLoginPath(path)) return false;
|
||||
const name = route.name ? String(route.name) : '';
|
||||
if (name && isLoginRouteName(name)) return false;
|
||||
return true;
|
||||
});
|
||||
|
||||
const { currentLocale } = useLocale();
|
||||
|
||||
@ -31,6 +31,20 @@ function unwrapData<T>(body: unknown): T {
|
||||
return (payload.data ?? payload.Data ?? body) as T
|
||||
}
|
||||
|
||||
export interface FulfillWorkplaceStats {
|
||||
pendingBind: number
|
||||
domesticInTransit: number
|
||||
arrivedPendingPick: number
|
||||
outboundDone: number
|
||||
}
|
||||
|
||||
export interface PickupWorkplaceStats {
|
||||
pendingPayment: number
|
||||
pendingSchedule: number
|
||||
pendingPickup: number
|
||||
pendingInbound: number
|
||||
}
|
||||
|
||||
export interface OrderWorkplaceStats {
|
||||
total: number
|
||||
counts: Record<string, number>
|
||||
@ -44,6 +58,20 @@ export interface OrderWorkplaceStats {
|
||||
claimed: number
|
||||
storeCount: number
|
||||
storeOrderPending: number
|
||||
fulfill: FulfillWorkplaceStats
|
||||
pickup: PickupWorkplaceStats
|
||||
}
|
||||
|
||||
export interface OrderWorkplaceBusinessTrendPoint {
|
||||
label: string
|
||||
waybillCount: number
|
||||
fulfillCount: number
|
||||
pickupCount: number
|
||||
}
|
||||
|
||||
export interface OrderWorkplaceBusinessTrend {
|
||||
dimension: string
|
||||
points: OrderWorkplaceBusinessTrendPoint[]
|
||||
}
|
||||
|
||||
export interface OrderWorkplaceTrendPoint {
|
||||
@ -72,6 +100,42 @@ export interface OrderWorkplaceSpendTrend {
|
||||
points: OrderWorkplaceSpendTrendPoint[]
|
||||
}
|
||||
|
||||
function normalizeFulfillStats(raw: unknown): FulfillWorkplaceStats {
|
||||
const row =
|
||||
typeof raw === 'object' && raw != null ? (raw as Record<string, unknown>) : {}
|
||||
const num = (...keys: string[]) => {
|
||||
for (const key of keys) {
|
||||
const v = row[key]
|
||||
if (v != null && Number.isFinite(Number(v))) return Number(v) || 0
|
||||
}
|
||||
return 0
|
||||
}
|
||||
return {
|
||||
pendingBind: num('pendingBind', 'PendingBind'),
|
||||
domesticInTransit: num('domesticInTransit', 'DomesticInTransit'),
|
||||
arrivedPendingPick: num('arrivedPendingPick', 'ArrivedPendingPick'),
|
||||
outboundDone: num('outboundDone', 'OutboundDone'),
|
||||
}
|
||||
}
|
||||
|
||||
function normalizePickupStats(raw: unknown): PickupWorkplaceStats {
|
||||
const row =
|
||||
typeof raw === 'object' && raw != null ? (raw as Record<string, unknown>) : {}
|
||||
const num = (...keys: string[]) => {
|
||||
for (const key of keys) {
|
||||
const v = row[key]
|
||||
if (v != null && Number.isFinite(Number(v))) return Number(v) || 0
|
||||
}
|
||||
return 0
|
||||
}
|
||||
return {
|
||||
pendingPayment: num('pendingPayment', 'PendingPayment'),
|
||||
pendingSchedule: num('pendingSchedule', 'PendingSchedule'),
|
||||
pendingPickup: num('pendingPickup', 'PendingPickup'),
|
||||
pendingInbound: num('pendingInbound', 'PendingInbound'),
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeStats(raw: Record<string, unknown>): OrderWorkplaceStats {
|
||||
const countsRaw = raw.counts ?? raw.Counts
|
||||
const counts =
|
||||
@ -100,6 +164,28 @@ function normalizeStats(raw: Record<string, unknown>): OrderWorkplaceStats {
|
||||
Number(raw.claimed ?? raw.Claimed ?? countOf('claimed', 'Claimed') ?? 0) || 0,
|
||||
storeCount: Number(raw.storeCount ?? raw.StoreCount ?? 0) || 0,
|
||||
storeOrderPending: Number(raw.storeOrderPending ?? raw.StoreOrderPending ?? 0) || 0,
|
||||
fulfill: normalizeFulfillStats(raw.fulfill ?? raw.Fulfill),
|
||||
pickup: normalizePickupStats(raw.pickup ?? raw.Pickup),
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeBusinessTrend(raw: Record<string, unknown>): OrderWorkplaceBusinessTrend {
|
||||
const pointsRaw = raw.points ?? raw.Points
|
||||
const points = Array.isArray(pointsRaw)
|
||||
? pointsRaw.map((item) => {
|
||||
const row = item as Record<string, unknown>
|
||||
return {
|
||||
label: String(row.label ?? row.Label ?? ''),
|
||||
waybillCount: Number(row.waybillCount ?? row.WaybillCount ?? 0) || 0,
|
||||
fulfillCount: Number(row.fulfillCount ?? row.FulfillCount ?? 0) || 0,
|
||||
pickupCount: Number(row.pickupCount ?? row.PickupCount ?? 0) || 0,
|
||||
}
|
||||
})
|
||||
: []
|
||||
|
||||
return {
|
||||
dimension: String(raw.dimension ?? raw.Dimension ?? 'day'),
|
||||
points,
|
||||
}
|
||||
}
|
||||
|
||||
@ -152,6 +238,21 @@ export async function getOrderWorkplaceStats(): Promise<OrderWorkplaceStats> {
|
||||
return normalizeStats(data)
|
||||
}
|
||||
|
||||
/** 工作台业务线趋势 GET /order/workplace-business-trend */
|
||||
export async function getOrderWorkplaceBusinessTrend(params: {
|
||||
dimension: 'day' | 'week' | 'month'
|
||||
count?: number
|
||||
}): Promise<OrderWorkplaceBusinessTrend> {
|
||||
const res = await axiosInstance.get(orderUrl('/order/workplace-business-trend'), {
|
||||
params: {
|
||||
dimension: params.dimension,
|
||||
count: params.count,
|
||||
},
|
||||
})
|
||||
const data = unwrapData<Record<string, unknown>>(res.data)
|
||||
return normalizeBusinessTrend(data)
|
||||
}
|
||||
|
||||
/** 工作台订单趋势 GET /order/workplace-trend */
|
||||
export async function getOrderWorkplaceTrend(params: {
|
||||
dimension: 'day' | 'week' | 'month'
|
||||
|
||||
@ -1,20 +1,26 @@
|
||||
/* 主题色 CSS 变量(默认值,运行时由 themeColor 工具覆盖) */
|
||||
:root {
|
||||
--app-primary-color: #1d7281;
|
||||
--app-primary-color-hover: #186170;
|
||||
--app-primary-color-light: #e8f3f5;
|
||||
--app-navbar-bg: #1d7281;
|
||||
--app-sidebar-bg: #1d7281;
|
||||
--app-page-bg: #f0f3f4;
|
||||
--app-primary-color: #054f74;
|
||||
--app-primary-color-hover: #054b6e;
|
||||
--app-primary-color-light: #eef3f6;
|
||||
--app-navbar-bg: #054f74;
|
||||
--app-sidebar-bg: #054f74;
|
||||
--app-page-bg: #f5f4f1;
|
||||
--app-card-bg: #ffffff;
|
||||
--app-card-border: #e5e6eb;
|
||||
--app-elevated-bg: #ffffff;
|
||||
/* 官网同款极简磨砂玻璃 */
|
||||
--app-acrylic-bg: rgba(255, 255, 255, 0.32);
|
||||
--app-acrylic-hover-bg: rgba(255, 255, 255, 0.44);
|
||||
--app-acrylic-border: rgba(255, 255, 255, 0.72);
|
||||
--app-acrylic-border-strong: rgba(255, 255, 255, 0.92);
|
||||
--app-acrylic-blur: blur(28px) saturate(160%);
|
||||
--app-button-gap: 8px; /* 相邻按钮间距,规范统一 */
|
||||
/* 同步 Arco Design 主色(rgb 分量) */
|
||||
--primary-6: 29, 114, 129;
|
||||
--primary-5: 48, 135, 150;
|
||||
--primary-7: 24, 97, 112;
|
||||
--color-primary-light-1: #e8f3f5;
|
||||
--primary-6: 5, 79, 116;
|
||||
--primary-5: 38, 103, 134;
|
||||
--primary-7: 5, 75, 110;
|
||||
--color-primary-light-1: #eef3f6;
|
||||
}
|
||||
|
||||
* {
|
||||
@ -424,17 +430,23 @@ body .arco-button + .arco-btn {
|
||||
}
|
||||
|
||||
.general-card {
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
border: 1px solid var(--app-acrylic-border);
|
||||
border-radius: 16px;
|
||||
background: var(--app-acrylic-bg);
|
||||
backdrop-filter: var(--app-acrylic-blur);
|
||||
-webkit-backdrop-filter: var(--app-acrylic-blur);
|
||||
box-shadow: none;
|
||||
|
||||
& > .arco-card-header {
|
||||
height: auto;
|
||||
padding: 20px;
|
||||
border: none;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
& > .arco-card-body {
|
||||
padding: 0 20px 20px;
|
||||
background: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -5,10 +5,10 @@
|
||||
}
|
||||
|
||||
.stat-card {
|
||||
--liquid-a: rgba(0, 121, 121, 0.1);
|
||||
--liquid-b: rgba(125, 162, 255, 0.08);
|
||||
--liquid-edge: rgba(255, 255, 255, 0.78);
|
||||
--liquid-glow: rgba(0, 121, 121, 0.05);
|
||||
--liquid-a: rgba(var(--primary-6), 0.1);
|
||||
--liquid-b: rgba(var(--primary-6), 0.06);
|
||||
--liquid-edge: var(--app-acrylic-border, rgba(255, 255, 255, 0.72));
|
||||
--liquid-glow: transparent;
|
||||
|
||||
position: relative;
|
||||
z-index: 0;
|
||||
@ -22,23 +22,23 @@
|
||||
padding: 14px 16px 12px;
|
||||
border: 1px solid var(--liquid-edge);
|
||||
border-radius: 18px;
|
||||
background:
|
||||
linear-gradient(155deg, rgba(255, 255, 255, 0.82) 0%, rgba(255, 255, 255, 0.55) 48%, rgba(255, 255, 255, 0.72) 100%);
|
||||
box-shadow:
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.9),
|
||||
inset 0 -1px 0 rgba(255, 255, 255, 0.18),
|
||||
0 6px 18px var(--liquid-glow),
|
||||
0 1px 2px rgba(15, 23, 42, 0.03);
|
||||
backdrop-filter: blur(12px) saturate(1.15);
|
||||
-webkit-backdrop-filter: blur(12px) saturate(1.15);
|
||||
background: var(--app-acrylic-bg, rgba(255, 255, 255, 0.32));
|
||||
box-shadow: none;
|
||||
backdrop-filter: var(--app-acrylic-blur, blur(28px) saturate(160%));
|
||||
-webkit-backdrop-filter: var(--app-acrylic-blur, blur(28px) saturate(160%));
|
||||
text-align: left;
|
||||
font: inherit;
|
||||
color: inherit;
|
||||
cursor: default;
|
||||
transition:
|
||||
border-color 0.35s ease,
|
||||
box-shadow 0.35s ease,
|
||||
transform 0.35s cubic-bezier(0.22, 1, 0.36, 1);
|
||||
border-color 0.2s ease,
|
||||
background 0.2s ease,
|
||||
color 0.2s ease;
|
||||
|
||||
&__blob,
|
||||
&__shine {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&__blob,
|
||||
&__shine,
|
||||
@ -48,60 +48,19 @@
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
&__blob {
|
||||
position: absolute;
|
||||
z-index: 0;
|
||||
inset: -40% -20% auto auto;
|
||||
width: 78%;
|
||||
height: 120%;
|
||||
border-radius: 42% 58% 48% 52% / 48% 42% 58% 52%;
|
||||
background:
|
||||
radial-gradient(circle at 32% 28%, rgba(255, 255, 255, 0.55) 0%, transparent 42%),
|
||||
radial-gradient(circle at 70% 65%, var(--liquid-a) 0%, transparent 58%),
|
||||
radial-gradient(circle at 20% 80%, var(--liquid-b) 0%, transparent 55%);
|
||||
filter: blur(12px);
|
||||
opacity: 0.55;
|
||||
pointer-events: none;
|
||||
transform-origin: 60% 40%;
|
||||
animation: liquid-drift 9s ease-in-out infinite;
|
||||
}
|
||||
|
||||
&__shine {
|
||||
position: absolute;
|
||||
z-index: 0;
|
||||
inset: 0;
|
||||
border-radius: inherit;
|
||||
background: linear-gradient(
|
||||
115deg,
|
||||
transparent 28%,
|
||||
rgba(255, 255, 255, 0.05) 42%,
|
||||
rgba(255, 255, 255, 0.22) 50%,
|
||||
rgba(255, 255, 255, 0.05) 58%,
|
||||
transparent 72%
|
||||
);
|
||||
background-size: 220% 100%;
|
||||
background-position: 120% 0;
|
||||
opacity: 0.35;
|
||||
pointer-events: none;
|
||||
transition: background-position 0.7s ease, opacity 0.35s ease;
|
||||
}
|
||||
|
||||
&--primary {
|
||||
--liquid-a: rgba(0, 121, 121, 0.12);
|
||||
--liquid-b: rgba(64, 128, 255, 0.08);
|
||||
--liquid-glow: rgba(0, 121, 121, 0.06);
|
||||
--liquid-a: rgba(var(--primary-6), 0.12);
|
||||
--liquid-b: rgba(var(--primary-6), 0.08);
|
||||
}
|
||||
|
||||
&--warning {
|
||||
--liquid-a: rgba(255, 125, 0, 0.12);
|
||||
--liquid-b: rgba(255, 182, 93, 0.1);
|
||||
--liquid-glow: rgba(255, 125, 0, 0.06);
|
||||
--liquid-b: rgba(255, 182, 93, 0.08);
|
||||
}
|
||||
|
||||
&--danger {
|
||||
--liquid-a: rgba(245, 63, 63, 0.12);
|
||||
--liquid-b: rgba(249, 137, 129, 0.09);
|
||||
--liquid-glow: rgba(245, 63, 63, 0.06);
|
||||
--liquid-b: rgba(249, 137, 129, 0.08);
|
||||
|
||||
&.stat-card--alert .stat-card__value {
|
||||
color: #f98981;
|
||||
@ -111,42 +70,25 @@
|
||||
&--success {
|
||||
--liquid-a: rgba(0, 180, 42, 0.1);
|
||||
--liquid-b: rgba(76, 210, 99, 0.08);
|
||||
--liquid-glow: rgba(0, 180, 42, 0.05);
|
||||
}
|
||||
|
||||
&--neutral {
|
||||
--liquid-a: rgba(114, 46, 209, 0.09);
|
||||
--liquid-b: rgba(180, 126, 255, 0.07);
|
||||
--liquid-glow: rgba(114, 46, 209, 0.04);
|
||||
--liquid-a: rgba(var(--primary-6), 0.08);
|
||||
--liquid-b: rgba(var(--primary-6), 0.05);
|
||||
}
|
||||
|
||||
&--alert {
|
||||
border-color: color-mix(in srgb, var(--liquid-a) 35%, white);
|
||||
border-color: color-mix(in srgb, var(--app-primary-color) 22%, var(--app-acrylic-border));
|
||||
}
|
||||
|
||||
&--clickable {
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
border-color: rgba(255, 255, 255, 0.95);
|
||||
box-shadow:
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.95),
|
||||
inset 0 -1px 0 rgba(255, 255, 255, 0.22),
|
||||
0 10px 24px var(--liquid-glow),
|
||||
0 3px 8px rgba(15, 23, 42, 0.04);
|
||||
transform: translateY(-2px);
|
||||
|
||||
.stat-card__blob {
|
||||
animation-duration: 5.5s;
|
||||
filter: blur(11px) saturate(1.1);
|
||||
opacity: 0.72;
|
||||
transform: scale(1.08) translate(-4%, 3%);
|
||||
}
|
||||
|
||||
.stat-card__shine {
|
||||
opacity: 0.55;
|
||||
background-position: -40% 0;
|
||||
}
|
||||
border-color: var(--app-acrylic-border-strong, rgba(255, 255, 255, 0.92));
|
||||
background: var(--app-acrylic-hover-bg, rgba(255, 255, 255, 0.44));
|
||||
box-shadow: none;
|
||||
transform: none;
|
||||
|
||||
.stat-card__hint {
|
||||
opacity: 1;
|
||||
@ -154,7 +96,7 @@
|
||||
}
|
||||
|
||||
&:active {
|
||||
transform: translateY(0);
|
||||
transform: none;
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
@ -183,7 +125,7 @@
|
||||
&__avatar {
|
||||
flex-shrink: 0;
|
||||
background-color: transparent;
|
||||
filter: drop-shadow(0 4px 8px rgba(15, 23, 42, 0.08));
|
||||
filter: none;
|
||||
|
||||
:deep(img) {
|
||||
width: 48px;
|
||||
@ -196,7 +138,7 @@
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: rgba(255, 255, 255, 0.88) !important;
|
||||
background: transparent !important;
|
||||
color: var(--app-primary-color, rgb(var(--primary-6)));
|
||||
font-size: 22px;
|
||||
}
|
||||
@ -227,7 +169,7 @@
|
||||
font-variant-numeric: tabular-nums;
|
||||
line-height: 1;
|
||||
letter-spacing: -0.02em;
|
||||
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.55);
|
||||
text-shadow: none;
|
||||
}
|
||||
|
||||
&__unit {
|
||||
@ -275,36 +217,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes liquid-drift {
|
||||
0%,
|
||||
100% {
|
||||
border-radius: 42% 58% 48% 52% / 48% 42% 58% 52%;
|
||||
transform: translate(0, 0) rotate(0deg) scale(1);
|
||||
}
|
||||
33% {
|
||||
border-radius: 58% 42% 55% 45% / 42% 58% 42% 58%;
|
||||
transform: translate(-8%, 6%) rotate(8deg) scale(1.06);
|
||||
}
|
||||
66% {
|
||||
border-radius: 48% 52% 40% 60% / 55% 45% 55% 45%;
|
||||
transform: translate(6%, -4%) rotate(-6deg) scale(0.96);
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.stat-card__blob {
|
||||
animation: none;
|
||||
}
|
||||
|
||||
.stat-card__shine {
|
||||
transition: none;
|
||||
}
|
||||
|
||||
.stat-card--clickable:hover {
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 1280px) {
|
||||
.stat-grid {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
@ -319,9 +231,4 @@
|
||||
.stat-card__hint {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.stat-card__blob {
|
||||
animation: none;
|
||||
opacity: 0.7;
|
||||
}
|
||||
}
|
||||
|
||||
@ -804,7 +804,7 @@
|
||||
el.style.setProperty('font-weight', '500', 'important');
|
||||
el.style.setProperty('line-height', '22px', 'important');
|
||||
el.style.setProperty('font-family', 'inherit', 'important');
|
||||
el.style.setProperty('caret-color', 'var(--app-primary-color, #1d7281)', 'important');
|
||||
el.style.setProperty('caret-color', 'var(--app-primary-color, #054f74)', 'important');
|
||||
el.style.setProperty('-webkit-appearance', 'none', 'important');
|
||||
el.style.setProperty('appearance', 'none', 'important');
|
||||
if (el.tagName === 'BUTTON') {
|
||||
@ -2759,7 +2759,7 @@
|
||||
outline: none !important;
|
||||
box-sizing: border-box !important;
|
||||
text-align: left;
|
||||
caret-color: var(--app-primary-color, #1d7281);
|
||||
caret-color: var(--app-primary-color, #054f74);
|
||||
-webkit-appearance: none !important;
|
||||
appearance: none !important;
|
||||
}
|
||||
|
||||
@ -49,7 +49,7 @@
|
||||
|
||||
const colorValue = computed(() => {
|
||||
const v = props.defaultValue as string;
|
||||
if (!v || typeof v !== 'string') return '#1d7281';
|
||||
if (!v || typeof v !== 'string') return '#054f74';
|
||||
return v.startsWith('#') ? v : `#${v}`;
|
||||
});
|
||||
|
||||
|
||||
@ -57,17 +57,19 @@
|
||||
/>
|
||||
<span class="color-swatch" :style="{ background: themeColor }" />
|
||||
</div>
|
||||
<div class="color-presets">
|
||||
<div class="brand-presets">
|
||||
<button
|
||||
v-for="c in solidPresets"
|
||||
:key="c"
|
||||
v-for="item in brandPresets"
|
||||
:key="item.color"
|
||||
type="button"
|
||||
class="color-preset"
|
||||
:class="{ 'is-active': themeColor.toLowerCase() === c.toLowerCase() }"
|
||||
:style="{ background: c }"
|
||||
:title="c"
|
||||
@click="setColor(c)"
|
||||
/>
|
||||
class="brand-preset"
|
||||
:class="{ 'is-active': themeColor.toLowerCase() === item.color.toLowerCase() }"
|
||||
:title="item.color"
|
||||
@click="setColor(item.color)"
|
||||
>
|
||||
<span class="brand-preset__swatch" :style="{ background: item.color }" />
|
||||
<span class="brand-preset__name">{{ $t(item.labelKey) }}</span>
|
||||
</button>
|
||||
</div>
|
||||
<p class="settings-section__subhint">{{ $t('settings.themeColorLiquid') }}</p>
|
||||
<div class="liquid-presets">
|
||||
@ -88,15 +90,41 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="settings-section">
|
||||
<h5 class="settings-section__title">{{ $t('settings.pageBg') }}</h5>
|
||||
<p class="settings-section__hint">{{ $t('settings.pageBgHint') }}</p>
|
||||
<div class="brand-presets">
|
||||
<button
|
||||
v-for="item in pageBgPresets"
|
||||
:key="item.color"
|
||||
type="button"
|
||||
class="brand-preset"
|
||||
:class="{ 'is-active': pageBackgroundColor.toLowerCase() === item.color.toLowerCase() }"
|
||||
:title="item.color"
|
||||
@click="setPageBg(item.color)"
|
||||
>
|
||||
<span
|
||||
class="brand-preset__swatch brand-preset__swatch--page"
|
||||
:style="{ background: item.color }"
|
||||
/>
|
||||
<span class="brand-preset__name">{{ $t(item.labelKey) }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p class="settings-persist-tip">{{ $t('settings.persistTip') }}</p>
|
||||
</a-drawer>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed } from 'vue';
|
||||
import { computed, onBeforeUnmount } from 'vue';
|
||||
import { IconSun, IconMoon } from '@arco-design/web-vue/es/icon';
|
||||
import { useAppStore } from '@/store';
|
||||
import { DEFAULT_THEME_COLOR } from '@/utils/themeColor';
|
||||
import {
|
||||
DEFAULT_PAGE_BACKGROUND,
|
||||
PAGE_BACKGROUND_PRESETS,
|
||||
} from '@/utils/pageBackground';
|
||||
|
||||
const emit = defineEmits(['cancel']);
|
||||
|
||||
@ -104,16 +132,19 @@
|
||||
const visible = computed(() => appStore.globalSettings);
|
||||
const isDark = computed(() => appStore.theme === 'dark');
|
||||
const themeColor = computed(() => appStore.themeColor || DEFAULT_THEME_COLOR);
|
||||
const pageBackgroundColor = computed(
|
||||
() => appStore.pageBackgroundColor || DEFAULT_PAGE_BACKGROUND,
|
||||
);
|
||||
|
||||
// 经典纯色
|
||||
const solidPresets = [
|
||||
DEFAULT_THEME_COLOR,
|
||||
'#007979',
|
||||
'#2d8c8c',
|
||||
'#165dff',
|
||||
'#0fc6c2',
|
||||
'#722ed1',
|
||||
'#f77234',
|
||||
const pageBgPresets = PAGE_BACKGROUND_PRESETS;
|
||||
|
||||
// 品牌主题色预设
|
||||
const brandPresets = [
|
||||
{ color: '#382c77', labelKey: 'settings.themeBrand.violet' },
|
||||
{ color: '#004088', labelKey: 'settings.themeBrand.navy' },
|
||||
{ color: '#054f74', labelKey: 'settings.themeBrand.teal' },
|
||||
{ color: '#004ea2', labelKey: 'settings.themeBrand.blue' },
|
||||
{ color: '#1e7fa0', labelKey: 'settings.themeBrand.cyan' },
|
||||
];
|
||||
|
||||
// 液态暖色卡片(主色取自关怀卡片中调,样式复刻液态质感)
|
||||
@ -135,6 +166,12 @@
|
||||
emit('cancel');
|
||||
};
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
if (appStore.globalSettings) {
|
||||
appStore.updateSettings({ globalSettings: false });
|
||||
}
|
||||
});
|
||||
|
||||
const setMode = (dark: boolean) => {
|
||||
appStore.toggleTheme(dark);
|
||||
};
|
||||
@ -143,6 +180,10 @@
|
||||
appStore.updateSettings({ themeColor: hex });
|
||||
};
|
||||
|
||||
const setPageBg = (hex: string) => {
|
||||
appStore.updateSettings({ pageBackgroundColor: hex });
|
||||
};
|
||||
|
||||
const onColorInput = (e: Event) => {
|
||||
setColor((e.target as HTMLInputElement).value);
|
||||
};
|
||||
@ -268,6 +309,63 @@
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.brand-presets {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 8px;
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.brand-preset {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
min-height: 36px;
|
||||
padding: 6px 10px;
|
||||
border: 1px solid var(--color-border-2);
|
||||
border-radius: 8px;
|
||||
background: var(--color-fill-1);
|
||||
cursor: pointer;
|
||||
text-align: left;
|
||||
transition: border-color 0.2s, background 0.2s, box-shadow 0.2s;
|
||||
|
||||
&:hover {
|
||||
border-color: var(--color-border-3);
|
||||
background: var(--color-fill-2);
|
||||
}
|
||||
|
||||
&.is-active {
|
||||
border-color: var(--app-primary-color);
|
||||
background: var(--app-primary-color-light);
|
||||
box-shadow: 0 0 0 1px var(--app-primary-color);
|
||||
}
|
||||
}
|
||||
|
||||
.brand-preset__swatch {
|
||||
flex-shrink: 0;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
border-radius: 50%;
|
||||
border: 1px solid rgba(0, 0, 0, 0.08);
|
||||
box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.35);
|
||||
}
|
||||
|
||||
.brand-preset__swatch--page {
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.brand-preset__name {
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
color: var(--color-text-2);
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.brand-preset.is-active .brand-preset__name {
|
||||
color: var(--app-primary-color);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.color-preset {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
|
||||
@ -1,35 +1,39 @@
|
||||
<template>
|
||||
<a-dropdown trigger="hover" placement="bottomRight" popup-class="user-account-dropdown">
|
||||
<a-dropdown
|
||||
trigger="hover"
|
||||
position="br"
|
||||
@select="onDropdownSelect"
|
||||
>
|
||||
<div class="user-dropdown-trigger">
|
||||
<span class="user-name">{{ displayName }}</span>
|
||||
<IconSettings class="user-icon" />
|
||||
</div>
|
||||
<template #content>
|
||||
<a-doption v-if="isCustomerPortal" @click="() => onMenuClick({ key: 'userinfo' })">
|
||||
<a-doption v-if="isCustomerPortal" value="userinfo">
|
||||
<span class="user-menu-item">
|
||||
<span class="user-menu-item__icon"><i class="fa fa-user" /></span>
|
||||
<span class="user-menu-item__label">{{ t('menu.user.profile') }}</span>
|
||||
</span>
|
||||
</a-doption>
|
||||
<a-doption v-if="!isCustomerPortal" @click="() => onMenuClick({ key: 'settings' })">
|
||||
<a-doption v-if="!isCustomerPortal" value="settings">
|
||||
<span class="user-menu-item">
|
||||
<span class="user-menu-item__icon"><i class="fa fa-cog" /></span>
|
||||
<span class="user-menu-item__label">页面配置</span>
|
||||
</span>
|
||||
</a-doption>
|
||||
<a-doption @click="() => onMenuClick({ key: 'changePassword' })">
|
||||
<a-doption value="changePassword">
|
||||
<span class="user-menu-item">
|
||||
<span class="user-menu-item__icon"><i class="fa fa-lock" /></span>
|
||||
<span class="user-menu-item__label">{{ t('profile.changePassword') }}</span>
|
||||
</span>
|
||||
</a-doption>
|
||||
<a-doption v-if="isCustomerPortal" @click="() => onMenuClick({ key: 'settings' })">
|
||||
<a-doption v-if="isCustomerPortal" value="settings">
|
||||
<span class="user-menu-item">
|
||||
<span class="user-menu-item__icon"><i class="fa fa-cog" /></span>
|
||||
<span class="user-menu-item__label">页面配置</span>
|
||||
</span>
|
||||
</a-doption>
|
||||
<a-doption @click="() => onMenuClick({ key: 'logout' })">
|
||||
<a-doption value="logout">
|
||||
<span class="user-menu-item is-danger">
|
||||
<span class="user-menu-item__icon"><i class="fa fa-sign-in" /></span>
|
||||
<span class="user-menu-item__label">退出登录</span>
|
||||
@ -187,6 +191,11 @@ async function handleChangePasswordSubmit() {
|
||||
}
|
||||
}
|
||||
|
||||
const onDropdownSelect = (value: string | number | Record<string, unknown> | undefined) => {
|
||||
if (value === undefined || value === null) return;
|
||||
onMenuClick({ key: String(value) });
|
||||
};
|
||||
|
||||
const onMenuClick = async (event: { key: string }) => {
|
||||
const { key } = event;
|
||||
|
||||
@ -206,7 +215,7 @@ const onMenuClick = async (event: { key: string }) => {
|
||||
} else if (key === 'changePassword') {
|
||||
openChangePasswordModal();
|
||||
} else if (key === 'settings') {
|
||||
appStore.updateSettings({ globalSettings: true });
|
||||
void appStore.openGlobalSettings();
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
@ -7,7 +7,8 @@
|
||||
"hideMenu": false,
|
||||
"menuCollapse": false,
|
||||
"footer": false,
|
||||
"themeColor": "#1d7281",
|
||||
"themeColor": "#054f74",
|
||||
"pageBackgroundColor": "#f5f4f1",
|
||||
"menuWidth": 220,
|
||||
"globalSettings": false,
|
||||
"device": "desktop",
|
||||
|
||||
@ -2,6 +2,22 @@ export default {
|
||||
'settings.title': 'Settings',
|
||||
'settings.themeColor': 'Theme Color',
|
||||
'settings.themeColorHint': 'Navbar, buttons, and selection states follow this color',
|
||||
'settings.themeBrand.violet': 'Violet',
|
||||
'settings.themeBrand.navy': 'Navy',
|
||||
'settings.themeBrand.teal': 'Teal',
|
||||
'settings.themeBrand.blue': 'Blue',
|
||||
'settings.themeBrand.cyan': 'Cyan',
|
||||
'settings.pageBg': 'Page Background',
|
||||
'settings.pageBgHint': 'Admin content area and official site homepage',
|
||||
'settings.pageBg.ningzhi': 'Cream',
|
||||
'settings.pageBg.default': 'Default Gray',
|
||||
'settings.pageBg.mist': 'Mist Blue',
|
||||
'settings.pageBg.stone': 'Warm Stone',
|
||||
'settings.pageBg.sage': 'Sage Mist',
|
||||
'settings.pageBg.lilac': 'Soft Lilac',
|
||||
'settings.pageBg.slate': 'Cool Slate',
|
||||
'settings.pageBg.pearl': 'Pearl',
|
||||
'settings.pageBg.white': 'White',
|
||||
'settings.themeColorLiquid': 'Warm liquid textures',
|
||||
'settings.themeLiquid.peach': 'Peach',
|
||||
'settings.themeLiquid.apricot': 'Apricot',
|
||||
@ -19,7 +35,7 @@ export default {
|
||||
'settings.theme.lightDesc': 'Bright background',
|
||||
'settings.theme.dark': 'Dark',
|
||||
'settings.theme.darkDesc': 'Easier on the eyes',
|
||||
'settings.persistTip': 'Theme color and appearance are saved on this device and restored next time.',
|
||||
'settings.persistTip': 'Theme color, page background, and appearance are saved on this device and restored next time.',
|
||||
'settings.search': 'Search',
|
||||
'settings.language': 'Language',
|
||||
'settings.navbar': 'Navbar',
|
||||
|
||||
@ -2,6 +2,22 @@ export default {
|
||||
'settings.title': '页面配置',
|
||||
'settings.themeColor': '主题色',
|
||||
'settings.themeColorHint': '导航栏、按钮、选中态等跟随此颜色',
|
||||
'settings.themeBrand.violet': '深紫',
|
||||
'settings.themeBrand.navy': '藏蓝',
|
||||
'settings.themeBrand.teal': '墨青',
|
||||
'settings.themeBrand.blue': '海蓝',
|
||||
'settings.themeBrand.cyan': '青蓝',
|
||||
'settings.pageBg': '页面背景',
|
||||
'settings.pageBgHint': '管理端内容区与官网首页背景色',
|
||||
'settings.pageBg.ningzhi': '凝脂',
|
||||
'settings.pageBg.default': '默认灰',
|
||||
'settings.pageBg.mist': '雾蓝',
|
||||
'settings.pageBg.stone': '石米',
|
||||
'settings.pageBg.sage': '苔雾',
|
||||
'settings.pageBg.lilac': '薄紫',
|
||||
'settings.pageBg.slate': '冷灰',
|
||||
'settings.pageBg.pearl': '珍珠',
|
||||
'settings.pageBg.white': '纯白',
|
||||
'settings.themeColorLiquid': '暖色液态质感',
|
||||
'settings.themeLiquid.peach': '蜜桃',
|
||||
'settings.themeLiquid.apricot': '暖杏',
|
||||
@ -19,7 +35,7 @@ export default {
|
||||
'settings.theme.lightDesc': '浅色背景',
|
||||
'settings.theme.dark': '夜间模式',
|
||||
'settings.theme.darkDesc': '深色护眼',
|
||||
'settings.persistTip': '主题色与外观模式会自动保存到本机,下次打开仍生效。',
|
||||
'settings.persistTip': '主题色、页面背景与外观模式会自动保存到本机,下次打开仍生效。',
|
||||
'settings.search': '搜索',
|
||||
'settings.language': '语言',
|
||||
'settings.navbar': '导航栏',
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { defineStore } from 'pinia';
|
||||
import { nextTick } from 'vue';
|
||||
import { Notification } from '@arco-design/web-vue';
|
||||
import type { NotificationReturn } from '@arco-design/web-vue/es/notification/interface';
|
||||
import type { RouteRecordNormalized } from 'vue-router';
|
||||
@ -14,14 +15,21 @@ import {
|
||||
setStoredThemeMode,
|
||||
type ThemeMode,
|
||||
} from '@/utils/themeColor';
|
||||
import {
|
||||
applyPageBackgroundColor,
|
||||
getStoredPageBackground,
|
||||
setStoredPageBackground,
|
||||
} from '@/utils/pageBackground';
|
||||
|
||||
const useAppStore = defineStore('app', {
|
||||
state: (): AppState => {
|
||||
const storedColor = getStoredThemeColor();
|
||||
const storedMode = getStoredThemeMode();
|
||||
const themeColor = storedColor || defaultSettings.themeColor;
|
||||
const pageBackgroundColor =
|
||||
getStoredPageBackground() || defaultSettings.pageBackgroundColor;
|
||||
const theme = (storedMode || defaultSettings.theme || 'light') as ThemeMode;
|
||||
return { ...defaultSettings, themeColor, theme };
|
||||
return { ...defaultSettings, themeColor, pageBackgroundColor, theme };
|
||||
},
|
||||
|
||||
getters: {
|
||||
@ -53,6 +61,19 @@ const useAppStore = defineStore('app', {
|
||||
setStoredThemeMode(mode);
|
||||
applyThemeMode(mode);
|
||||
}
|
||||
if (partial.pageBackgroundColor !== undefined) {
|
||||
setStoredPageBackground(partial.pageBackgroundColor);
|
||||
applyPageBackgroundColor(partial.pageBackgroundColor);
|
||||
}
|
||||
},
|
||||
|
||||
/** 打开页面配置抽屉;若状态已打开则先关再开,避免 v-if 卸载后无法再次弹出 */
|
||||
async openGlobalSettings() {
|
||||
if (this.globalSettings) {
|
||||
this.globalSettings = false;
|
||||
await nextTick();
|
||||
}
|
||||
this.globalSettings = true;
|
||||
},
|
||||
|
||||
// Change theme color
|
||||
@ -72,6 +93,7 @@ const useAppStore = defineStore('app', {
|
||||
hydrateTheme() {
|
||||
applyThemeColor(this.themeColor);
|
||||
applyThemeMode(this.theme === 'dark' ? 'dark' : 'light');
|
||||
applyPageBackgroundColor(this.pageBackgroundColor);
|
||||
},
|
||||
|
||||
toggleDevice(device: string) {
|
||||
|
||||
@ -10,6 +10,7 @@ export interface AppState {
|
||||
menuCollapse: boolean;
|
||||
footer: boolean;
|
||||
themeColor: string;
|
||||
pageBackgroundColor: string;
|
||||
menuWidth: number;
|
||||
globalSettings: boolean;
|
||||
device: string;
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
*/
|
||||
|
||||
:root {
|
||||
--app-page-bg: #f0f3f4;
|
||||
--app-page-bg: #f5f4f1;
|
||||
--app-card-bg: #ffffff;
|
||||
--app-card-border: #e5e6eb;
|
||||
--app-elevated-bg: #ffffff;
|
||||
|
||||
@ -21,7 +21,7 @@ export type AdminCareSettings = {
|
||||
export const DEFAULT_ADMIN_CARE_SETTINGS: AdminCareSettings = {
|
||||
careEnabled: true,
|
||||
loginBannerEnabled: true,
|
||||
loginToastEnabled: true,
|
||||
loginToastEnabled: false,
|
||||
workplacePanelEnabled: true,
|
||||
longAbsenceDays: 7,
|
||||
lateNightThreshold: 3,
|
||||
|
||||
80
src/utils/pageBackground.ts
Normal file
80
src/utils/pageBackground.ts
Normal file
@ -0,0 +1,80 @@
|
||||
/**
|
||||
* 页面背景色:OMS 与官网首页共用 localStorage
|
||||
*/
|
||||
|
||||
export const PAGE_BACKGROUND_STORAGE_KEY = 'app-page-background-color';
|
||||
|
||||
export const DEFAULT_PAGE_BACKGROUND = '#f5f4f1';
|
||||
|
||||
export const PAGE_BACKGROUND_PRESETS = [
|
||||
{ color: '#f0f3f4', labelKey: 'settings.pageBg.default' },
|
||||
{ color: '#f6f1e7', labelKey: 'settings.pageBg.ningzhi' },
|
||||
{ color: '#f4f6f9', labelKey: 'settings.pageBg.mist' },
|
||||
{ color: '#f5f4f1', labelKey: 'settings.pageBg.stone' },
|
||||
{ color: '#f1f4f2', labelKey: 'settings.pageBg.sage' },
|
||||
{ color: '#f5f2f7', labelKey: 'settings.pageBg.lilac' },
|
||||
{ color: '#f0f2f6', labelKey: 'settings.pageBg.slate' },
|
||||
{ color: '#faf9f7', labelKey: 'settings.pageBg.pearl' },
|
||||
{ color: '#ffffff', labelKey: 'settings.pageBg.white' },
|
||||
] as const;
|
||||
|
||||
function hexToRgb(hex: string): { r: number; g: number; b: number } | null {
|
||||
const m = hex.replace(/^#/, '').match(/^([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i);
|
||||
if (!m) return null;
|
||||
return {
|
||||
r: parseInt(m[1], 16),
|
||||
g: parseInt(m[2], 16),
|
||||
b: parseInt(m[3], 16),
|
||||
};
|
||||
}
|
||||
|
||||
function rgbToHex(r: number, g: number, b: number): string {
|
||||
return (
|
||||
'#' +
|
||||
[r, g, b]
|
||||
.map((x) => Math.round(Math.max(0, Math.min(255, x))).toString(16).padStart(2, '0'))
|
||||
.join('')
|
||||
);
|
||||
}
|
||||
|
||||
/** 生成略浅一档的背景色(用于官网辅助层) */
|
||||
function softenHex(hex: string, mixWhite = 0.55): string {
|
||||
const rgb = hexToRgb(hex);
|
||||
if (!rgb) return hex;
|
||||
const r = Math.round(rgb.r * (1 - mixWhite) + 255 * mixWhite);
|
||||
const g = Math.round(rgb.g * (1 - mixWhite) + 255 * mixWhite);
|
||||
const b = Math.round(rgb.b * (1 - mixWhite) + 255 * mixWhite);
|
||||
return rgbToHex(r, g, b);
|
||||
}
|
||||
|
||||
export function getStoredPageBackground(): string | null {
|
||||
try {
|
||||
return localStorage.getItem(PAGE_BACKGROUND_STORAGE_KEY);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export function setStoredPageBackground(hex: string): void {
|
||||
try {
|
||||
localStorage.setItem(PAGE_BACKGROUND_STORAGE_KEY, hex);
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
/** 写入 OMS + 官网首页共用的背景 CSS 变量 */
|
||||
export function applyPageBackgroundColor(hex: string): void {
|
||||
const color = hex.startsWith('#') ? hex : `#${hex}`;
|
||||
const root = document.documentElement;
|
||||
const warmPageBgs = new Set(['#f6f1e7', '#f5f4f1', '#faf9f7']);
|
||||
const soft = softenHex(color, warmPageBgs.has(color.toLowerCase()) ? 0.42 : 0.55);
|
||||
|
||||
root.style.setProperty('--app-page-bg', color);
|
||||
root.style.setProperty('--kc-page-bg', color);
|
||||
root.style.setProperty('--kc-neutral', soft);
|
||||
root.style.setProperty('--ai-bg', color);
|
||||
root.style.setProperty('--ai-canvas', color);
|
||||
root.style.setProperty('--ai-bg-soft', soft);
|
||||
root.style.setProperty('--ai-header-bg', `color-mix(in srgb, ${color} 94%, transparent)`);
|
||||
}
|
||||
@ -13,9 +13,13 @@ const LEGACY_THEME_COLORS = new Set([
|
||||
'#2d8c8c',
|
||||
'#2D8C8C',
|
||||
'#007979',
|
||||
'#1d7281',
|
||||
'#1D7281',
|
||||
'#1e7fa0',
|
||||
'#1E7FA0',
|
||||
]);
|
||||
|
||||
export const DEFAULT_THEME_COLOR = '#1d7281';
|
||||
export const DEFAULT_THEME_COLOR = '#054f74';
|
||||
export type ThemeMode = 'light' | 'dark';
|
||||
|
||||
export function getStoredThemeColor(): string | null {
|
||||
|
||||
@ -55,7 +55,7 @@ function clearPriceContext() {
|
||||
|
||||
<style lang="less" scoped>
|
||||
.h5-assistant-page {
|
||||
height: calc(100dvh - 56px - env(safe-area-inset-bottom, 0px));
|
||||
height: calc(100dvh - var(--h5-dock-offset));
|
||||
min-height: 360px;
|
||||
padding: 10px 12px 8px;
|
||||
box-sizing: border-box;
|
||||
|
||||
@ -1,50 +1,51 @@
|
||||
<template>
|
||||
<nav class="h5-tabbar" aria-label="Customer center tabs">
|
||||
<router-link
|
||||
v-for="item in leftTabs"
|
||||
:key="item.path"
|
||||
:to="item.path"
|
||||
replace
|
||||
class="h5-tabbar__item"
|
||||
:class="{ 'is-active': isActive(item.path) }"
|
||||
>
|
||||
<span class="h5-tabbar__icon-wrap">
|
||||
<component :is="item.icon" class="h5-tabbar__icon" />
|
||||
</span>
|
||||
<span class="h5-tabbar__label">{{ t(item.labelKey) }}</span>
|
||||
</router-link>
|
||||
|
||||
<button
|
||||
v-if="!hideCreate"
|
||||
type="button"
|
||||
class="h5-tabbar__create"
|
||||
:aria-label="t('h5.tab.createOrder')"
|
||||
@click="emit('create-order')"
|
||||
>
|
||||
<span class="h5-tabbar__create-btn">
|
||||
<icon-plus :size="28" class="h5-tabbar__create-plus" />
|
||||
</span>
|
||||
<span class="h5-tabbar__label">{{ t('h5.tab.createOrder') }}</span>
|
||||
</button>
|
||||
<div v-else class="h5-tabbar__create-spacer" aria-hidden="true" />
|
||||
|
||||
<router-link
|
||||
v-for="item in rightTabs"
|
||||
:key="item.path"
|
||||
:to="item.path"
|
||||
replace
|
||||
class="h5-tabbar__item"
|
||||
:class="{ 'is-active': isActive(item.path) }"
|
||||
>
|
||||
<span class="h5-tabbar__icon-wrap">
|
||||
<component :is="item.icon" class="h5-tabbar__icon" />
|
||||
<span v-if="item.badge && item.badge > 0" class="h5-tabbar__badge">
|
||||
{{ item.badge > 99 ? '99+' : item.badge }}
|
||||
<div class="h5-dock-shell">
|
||||
<nav class="h5-dock" aria-label="Customer center tabs">
|
||||
<router-link
|
||||
v-for="item in leftTabs"
|
||||
:key="item.path"
|
||||
:to="item.path"
|
||||
replace
|
||||
class="h5-dock__item"
|
||||
:class="{ 'is-active': isActive(item.path) }"
|
||||
>
|
||||
<span class="h5-dock__icon-wrap">
|
||||
<component :is="item.icon" class="h5-dock__icon" />
|
||||
</span>
|
||||
</span>
|
||||
<span class="h5-tabbar__label">{{ t(item.labelKey) }}</span>
|
||||
</router-link>
|
||||
</nav>
|
||||
<span class="h5-dock__label">{{ t(item.labelKey) }}</span>
|
||||
</router-link>
|
||||
|
||||
<button
|
||||
v-if="!hideCreate"
|
||||
type="button"
|
||||
class="h5-dock__item h5-dock__item--create"
|
||||
:aria-label="t('h5.tab.createOrder')"
|
||||
@click="emit('create-order')"
|
||||
>
|
||||
<span class="h5-dock__icon-wrap h5-dock__icon-wrap--create">
|
||||
<icon-plus :size="20" class="h5-dock__icon h5-dock__icon--plus" />
|
||||
</span>
|
||||
<span class="h5-dock__label">{{ t('h5.tab.createOrder') }}</span>
|
||||
</button>
|
||||
|
||||
<router-link
|
||||
v-for="item in rightTabs"
|
||||
:key="item.path"
|
||||
:to="item.path"
|
||||
replace
|
||||
class="h5-dock__item"
|
||||
:class="{ 'is-active': isActive(item.path) }"
|
||||
>
|
||||
<span class="h5-dock__icon-wrap">
|
||||
<component :is="item.icon" class="h5-dock__icon" />
|
||||
<span v-if="item.badge && item.badge > 0" class="h5-dock__badge">
|
||||
{{ item.badge > 99 ? '99+' : item.badge }}
|
||||
</span>
|
||||
</span>
|
||||
<span class="h5-dock__label">{{ t(item.labelKey) }}</span>
|
||||
</router-link>
|
||||
</nav>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
@ -103,124 +104,126 @@ function isActive(path: string) {
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.h5-tabbar {
|
||||
.h5-dock-shell {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 100;
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
height: calc(56px + env(safe-area-inset-bottom, 0px));
|
||||
padding-bottom: env(safe-area-inset-bottom, 0px);
|
||||
background: color-mix(in srgb, #fff 94%, transparent);
|
||||
backdrop-filter: blur(12px);
|
||||
-webkit-backdrop-filter: blur(12px);
|
||||
border-top: 1px solid rgba(229, 230, 235, 0.9);
|
||||
box-sizing: border-box;
|
||||
box-shadow: 0 -4px 16px rgba(15, 23, 42, 0.04);
|
||||
justify-content: center;
|
||||
padding: 0 14px calc(6px + env(safe-area-inset-bottom, 0px));
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.h5-tabbar__item,
|
||||
.h5-tabbar__create {
|
||||
flex: 1;
|
||||
.h5-dock {
|
||||
pointer-events: auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 2px;
|
||||
width: min(100%, 400px);
|
||||
min-height: 48px;
|
||||
padding: 4px 10px;
|
||||
border-radius: 9999px;
|
||||
background: color-mix(in srgb, #fff 72%, transparent);
|
||||
backdrop-filter: blur(22px) saturate(185%);
|
||||
-webkit-backdrop-filter: blur(22px) saturate(185%);
|
||||
border: 1px solid rgba(255, 255, 255, 0.78);
|
||||
box-shadow:
|
||||
0 10px 36px rgba(15, 23, 42, 0.14),
|
||||
0 2px 8px rgba(15, 23, 42, 0.06),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.92),
|
||||
inset 0 -1px 0 rgba(15, 23, 42, 0.04);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.h5-dock__item {
|
||||
flex: 1 1 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 3px;
|
||||
gap: 2px;
|
||||
min-width: 0;
|
||||
height: 56px;
|
||||
padding: 0 2px;
|
||||
color: #86909c;
|
||||
text-decoration: none;
|
||||
background: transparent;
|
||||
border: none;
|
||||
padding: 0;
|
||||
font: inherit;
|
||||
cursor: pointer;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
transition: color 0.2s ease;
|
||||
}
|
||||
|
||||
.h5-tabbar__item {
|
||||
&.is-active {
|
||||
color: var(--app-primary-color);
|
||||
|
||||
.h5-tabbar__icon-wrap {
|
||||
.h5-dock__icon-wrap {
|
||||
background: color-mix(in srgb, var(--app-primary-color) 14%, #fff);
|
||||
color: var(--app-primary-color);
|
||||
transform: translateY(-1px);
|
||||
border-radius: 9999px;
|
||||
}
|
||||
|
||||
.h5-tabbar__label {
|
||||
.h5-dock__label {
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.h5-tabbar__create {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
.h5-dock__item--create {
|
||||
color: var(--app-primary-color);
|
||||
justify-content: flex-end;
|
||||
padding-bottom: 4px;
|
||||
gap: 2px;
|
||||
|
||||
&:active .h5-tabbar__create-btn {
|
||||
transform: translateY(-10px) scale(0.96);
|
||||
.h5-dock__icon-wrap--create {
|
||||
color: #fff;
|
||||
background: var(--app-primary-color);
|
||||
box-shadow:
|
||||
0 4px 14px color-mix(in srgb, var(--app-primary-color) 38%, transparent),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.28);
|
||||
}
|
||||
|
||||
&:active .h5-dock__icon-wrap--create {
|
||||
filter: brightness(0.96);
|
||||
}
|
||||
|
||||
.h5-dock__label {
|
||||
color: var(--app-primary-color);
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
|
||||
.h5-tabbar__create-spacer {
|
||||
flex: 0 0 8px;
|
||||
height: 56px;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.h5-tabbar__create-btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 52px;
|
||||
height: 52px;
|
||||
margin-top: -22px;
|
||||
border-radius: 50%;
|
||||
color: #fff;
|
||||
background: var(--app-primary-color);
|
||||
box-shadow:
|
||||
0 6px 16px color-mix(in srgb, var(--app-primary-color) 36%, transparent),
|
||||
0 0 0 4px #fff;
|
||||
transform: translateY(-10px);
|
||||
transition: transform 0.15s ease;
|
||||
}
|
||||
|
||||
.h5-tabbar__create-plus {
|
||||
color: #fff;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.h5-tabbar__icon-wrap {
|
||||
.h5-dock__icon-wrap {
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 36px;
|
||||
height: 28px;
|
||||
border-radius: 14px;
|
||||
width: 30px;
|
||||
height: 24px;
|
||||
border-radius: 0;
|
||||
line-height: 1;
|
||||
transition:
|
||||
background 0.2s ease,
|
||||
color 0.2s ease,
|
||||
transform 0.2s ease;
|
||||
color 0.2s ease;
|
||||
}
|
||||
|
||||
.h5-tabbar__icon {
|
||||
font-size: 20px;
|
||||
.h5-dock__icon-wrap--create {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.h5-dock__icon {
|
||||
font-size: 18px;
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
}
|
||||
|
||||
.h5-tabbar__badge {
|
||||
.h5-dock__icon--plus {
|
||||
color: #fff;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.h5-dock__badge {
|
||||
position: absolute;
|
||||
top: -4px;
|
||||
right: -2px;
|
||||
@ -234,11 +237,16 @@ function isActive(path: string) {
|
||||
line-height: 16px;
|
||||
text-align: center;
|
||||
box-sizing: border-box;
|
||||
box-shadow: 0 0 0 2px #fff;
|
||||
box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.92);
|
||||
}
|
||||
|
||||
.h5-tabbar__label {
|
||||
font-size: 11px;
|
||||
line-height: 1.2;
|
||||
.h5-dock__label {
|
||||
max-width: 100%;
|
||||
font-size: 10px;
|
||||
line-height: 1.15;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -209,7 +209,7 @@ watch(
|
||||
.h5-center-main {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
padding-bottom: calc(56px + var(--h5-safe-bottom));
|
||||
padding-bottom: var(--h5-dock-offset);
|
||||
box-sizing: border-box;
|
||||
overflow: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
|
||||
@ -317,9 +317,7 @@ onMounted(async () => {
|
||||
min-height: 100dvh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
/* Stripe / Linear 常见:浅灰画布衬白卡片,突出登录区 */
|
||||
background: #f5f6f8;
|
||||
/* 独立路由壳:避免父级 overflow/safe-area 二次撑开导致首帧错位 */
|
||||
background: var(--app-page-bg, #f0f3f4);
|
||||
overflow-x: hidden;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
|
||||
@ -340,12 +338,12 @@ onMounted(async () => {
|
||||
.h5-login-oauth-screen__card {
|
||||
width: min(320px, 100%);
|
||||
padding: 36px 24px;
|
||||
border-radius: 16px;
|
||||
background: #fff;
|
||||
border: 1px solid #e5e7eb;
|
||||
box-shadow:
|
||||
0 1px 2px rgba(15, 23, 42, 0.04),
|
||||
0 8px 24px rgba(15, 23, 42, 0.06);
|
||||
border-radius: 20px;
|
||||
background: var(--app-acrylic-bg);
|
||||
border: 1px solid var(--app-acrylic-border);
|
||||
box-shadow: none;
|
||||
backdrop-filter: var(--app-acrylic-blur);
|
||||
-webkit-backdrop-filter: var(--app-acrylic-blur);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
@ -380,8 +378,7 @@ onMounted(async () => {
|
||||
justify-content: space-between;
|
||||
height: 48px;
|
||||
padding: 0 8px 0 4px;
|
||||
background: #f5f6f8;
|
||||
/* 卡内已有品牌标题,顶栏只留返回 + 语言,去掉底部分割避免「双标题」感 */
|
||||
background: transparent;
|
||||
border-bottom: none;
|
||||
color: #4e5969;
|
||||
}
|
||||
@ -432,13 +429,13 @@ onMounted(async () => {
|
||||
}
|
||||
|
||||
.h5-login-card {
|
||||
background: #fff;
|
||||
border: 1px solid #e5e7eb;
|
||||
border-radius: 16px;
|
||||
background: var(--app-acrylic-bg);
|
||||
border: 1px solid var(--app-acrylic-border);
|
||||
border-radius: 20px;
|
||||
padding: 28px 20px 22px;
|
||||
box-shadow:
|
||||
0 1px 2px rgba(15, 23, 42, 0.04),
|
||||
0 8px 24px rgba(15, 23, 42, 0.06);
|
||||
box-shadow: none;
|
||||
backdrop-filter: var(--app-acrylic-blur);
|
||||
-webkit-backdrop-filter: var(--app-acrylic-blur);
|
||||
}
|
||||
|
||||
/* 品牌放进卡片:一个完整登录单元(Clerk / Vercel 鉴权卡常见) */
|
||||
@ -496,19 +493,22 @@ onMounted(async () => {
|
||||
|
||||
.h5-login-form :deep(.arco-input-wrapper),
|
||||
.h5-login-form :deep(.arco-input-password) {
|
||||
border-radius: 10px;
|
||||
background: #fff;
|
||||
border: 1px solid #e5e7eb;
|
||||
border-radius: 12px;
|
||||
background: var(--app-acrylic-bg);
|
||||
border: 1px solid var(--app-acrylic-border);
|
||||
min-height: 48px;
|
||||
backdrop-filter: var(--app-acrylic-blur);
|
||||
-webkit-backdrop-filter: var(--app-acrylic-blur);
|
||||
transition:
|
||||
border-color 0.2s var(--login-ease),
|
||||
box-shadow 0.2s var(--login-ease);
|
||||
box-shadow 0.2s var(--login-ease),
|
||||
background 0.2s var(--login-ease);
|
||||
|
||||
&:hover,
|
||||
&.arco-input-wrapper-focus,
|
||||
&.arco-input-focus {
|
||||
background: #fff;
|
||||
border-color: var(--app-primary-color);
|
||||
background: var(--app-acrylic-hover-bg);
|
||||
border-color: var(--app-acrylic-border-strong);
|
||||
box-shadow: 0 0 0 3px color-mix(in srgb, var(--app-primary-color) 14%, transparent);
|
||||
}
|
||||
}
|
||||
@ -516,11 +516,14 @@ onMounted(async () => {
|
||||
.h5-login-error {
|
||||
margin: 0 0 8px;
|
||||
padding: 10px 12px;
|
||||
border-radius: 10px;
|
||||
background: #ffece8;
|
||||
border-radius: 12px;
|
||||
background: color-mix(in srgb, #ffece8 72%, var(--app-acrylic-bg));
|
||||
border: 1px solid color-mix(in srgb, #ffb4a8 65%, var(--app-acrylic-border));
|
||||
color: #f53f3f;
|
||||
font-size: 13px;
|
||||
line-height: 1.4;
|
||||
backdrop-filter: var(--app-acrylic-blur);
|
||||
-webkit-backdrop-filter: var(--app-acrylic-blur);
|
||||
animation: h5-login-rise 0.28s var(--login-ease) both;
|
||||
}
|
||||
|
||||
|
||||
@ -1754,7 +1754,7 @@ void bootstrap();
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: calc(56px + env(safe-area-inset-bottom, 0px));
|
||||
bottom: var(--h5-dock-offset);
|
||||
z-index: 110;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@ -2004,7 +2004,7 @@ void bootstrap();
|
||||
position: fixed;
|
||||
left: 12px;
|
||||
right: 12px;
|
||||
bottom: calc(56px + env(safe-area-inset-bottom, 0px));
|
||||
bottom: var(--h5-dock-offset);
|
||||
z-index: 110;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@ -2164,7 +2164,7 @@ void bootstrap();
|
||||
border-radius: 4px;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
color: var(--app-primary-color, #1d7281);
|
||||
color: var(--app-primary-color, #054f74);
|
||||
background: rgba(29, 114, 129, 0.1);
|
||||
}
|
||||
|
||||
|
||||
@ -23,6 +23,7 @@
|
||||
--h5-primary-soft: color-mix(in srgb, var(--app-primary-color) 12%, #fff);
|
||||
--h5-primary-softer: color-mix(in srgb, var(--app-primary-color) 8%, #fff);
|
||||
--h5-safe-bottom: env(safe-area-inset-bottom, 0px);
|
||||
--h5-dock-offset: calc(62px + var(--h5-safe-bottom));
|
||||
|
||||
background: var(--h5-bg);
|
||||
color: var(--h5-text);
|
||||
|
||||
297
src/views/home/workplace/components/AdminBusinessTrend.vue
Normal file
297
src/views/home/workplace/components/AdminBusinessTrend.vue
Normal file
@ -0,0 +1,297 @@
|
||||
<template>
|
||||
<section class="aw-trend" aria-label="business-trend">
|
||||
<div class="aw-trend__head">
|
||||
<p class="aw-trend__title">{{ t('workplace.admin.businessTrendTitle') }}</p>
|
||||
<a-radio-group
|
||||
v-model="dimension"
|
||||
type="button"
|
||||
size="small"
|
||||
@change="handleDimensionChange"
|
||||
>
|
||||
<a-radio value="day">{{ t('workplace.orderAnalysis.day') }}</a-radio>
|
||||
<a-radio value="week">{{ t('workplace.orderAnalysis.week') }}</a-radio>
|
||||
<a-radio value="month">{{ t('workplace.orderAnalysis.month') }}</a-radio>
|
||||
</a-radio-group>
|
||||
</div>
|
||||
|
||||
<a-spin v-if="loading" class="aw-trend__loading" />
|
||||
<div v-else-if="isEmpty" class="aw-trend__empty">
|
||||
<p class="aw-trend__empty-title">{{ t('workplace.admin.businessTrendEmptyTitle') }}</p>
|
||||
<p class="aw-trend__empty-desc">{{ t('workplace.admin.businessTrendEmptyDesc') }}</p>
|
||||
</div>
|
||||
<div v-else class="aw-trend__chart">
|
||||
<Chart width="100%" height="320px" :options="chartOption" />
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import Chart from '@/components/chart/index.vue'
|
||||
import { useAppStore } from '@/store'
|
||||
import { getOrderWorkplaceBusinessTrend } from '@/api/workplace'
|
||||
|
||||
defineOptions({ name: 'AdminBusinessTrend' })
|
||||
|
||||
const { t } = useI18n()
|
||||
const dimension = ref<'day' | 'week' | 'month'>('week')
|
||||
const pointCount = ref(4)
|
||||
const loading = ref(true)
|
||||
|
||||
const xAxisLabels = ref<string[]>([])
|
||||
const waybillData = ref<number[]>([])
|
||||
const fulfillData = ref<number[]>([])
|
||||
const pickupData = ref<number[]>([])
|
||||
|
||||
const isEmpty = computed(() => {
|
||||
if (xAxisLabels.value.length === 0) return true
|
||||
const sum = (arr: number[]) => arr.reduce((acc, n) => acc + (Number(n) || 0), 0)
|
||||
return sum(waybillData.value) === 0 && sum(fulfillData.value) === 0 && sum(pickupData.value) === 0
|
||||
})
|
||||
|
||||
function resolvePointCount(dim: 'day' | 'week' | 'month') {
|
||||
if (dim === 'week') return 4
|
||||
if (dim === 'month') return 6
|
||||
return 7
|
||||
}
|
||||
|
||||
async function loadData() {
|
||||
loading.value = true
|
||||
try {
|
||||
const trend = await getOrderWorkplaceBusinessTrend({
|
||||
dimension: dimension.value,
|
||||
count: pointCount.value,
|
||||
})
|
||||
xAxisLabels.value = trend.points.map((item) => item.label)
|
||||
waybillData.value = trend.points.map((item) => item.waybillCount)
|
||||
fulfillData.value = trend.points.map((item) => item.fulfillCount)
|
||||
pickupData.value = trend.points.map((item) => item.pickupCount)
|
||||
} catch {
|
||||
xAxisLabels.value = []
|
||||
waybillData.value = []
|
||||
fulfillData.value = []
|
||||
pickupData.value = []
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function handleDimensionChange() {
|
||||
pointCount.value = resolvePointCount(dimension.value)
|
||||
void loadData()
|
||||
}
|
||||
|
||||
const chartOption = computed(() => {
|
||||
const appStore = useAppStore()
|
||||
const isDark = appStore.theme === 'dark'
|
||||
const labels = xAxisLabels.value || []
|
||||
const primary = '#054f74'
|
||||
const fulfillColor = isDark ? '#FFB65D' : '#D9924A'
|
||||
const pickupColor = isDark ? '#36CFC9' : '#14B8A6'
|
||||
|
||||
return {
|
||||
animationDuration: 280,
|
||||
grid: {
|
||||
left: 8,
|
||||
right: 8,
|
||||
top: 12,
|
||||
bottom: 28,
|
||||
containLabel: true,
|
||||
},
|
||||
legend: {
|
||||
bottom: 0,
|
||||
left: 'center',
|
||||
icon: 'circle',
|
||||
itemWidth: 8,
|
||||
itemHeight: 8,
|
||||
itemGap: 14,
|
||||
textStyle: {
|
||||
color: isDark ? 'rgba(255, 255, 255, 0.7)' : '#4E5969',
|
||||
fontSize: 12,
|
||||
},
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis' as const,
|
||||
confine: true,
|
||||
axisPointer: { type: 'cross' as const },
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category' as const,
|
||||
boundaryGap: false,
|
||||
data: labels.length ? labels : ['—'],
|
||||
axisLine: {
|
||||
lineStyle: { color: isDark ? '#3f3f3f' : '#A9AEB8' },
|
||||
},
|
||||
axisTick: { show: false },
|
||||
axisLabel: {
|
||||
color: isDark ? 'rgba(255, 255, 255, 0.7)' : '#86909C',
|
||||
hideOverlap: true,
|
||||
},
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value' as const,
|
||||
min: 0,
|
||||
minInterval: 1,
|
||||
max: (extent: { max: number }) => {
|
||||
const m = Number(extent?.max) || 0
|
||||
if (m <= 0) return 5
|
||||
return Math.ceil(m * 1.15)
|
||||
},
|
||||
axisLine: { show: false },
|
||||
axisLabel: {
|
||||
color: isDark ? 'rgba(255, 255, 255, 0.7)' : '#86909C',
|
||||
},
|
||||
splitLine: {
|
||||
lineStyle: { color: isDark ? '#3F3F3F' : '#E5E6EB' },
|
||||
},
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: t('workplace.admin.businessTrend.waybill'),
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
data: waybillData.value,
|
||||
symbol: 'circle',
|
||||
symbolSize: 5,
|
||||
lineStyle: { width: 2, color: primary },
|
||||
itemStyle: { color: primary },
|
||||
areaStyle: {
|
||||
color: {
|
||||
type: 'linear',
|
||||
x: 0,
|
||||
y: 0,
|
||||
x2: 0,
|
||||
y2: 1,
|
||||
colorStops: [
|
||||
{
|
||||
offset: 0,
|
||||
color: isDark ? 'rgba(5, 79, 116, 0.28)' : 'rgba(5, 79, 116, 0.18)',
|
||||
},
|
||||
{ offset: 1, color: isDark ? 'rgba(5, 79, 116, 0.04)' : 'rgba(5, 79, 116, 0.03)' },
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: t('workplace.admin.businessTrend.fulfill'),
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
data: fulfillData.value,
|
||||
symbol: 'circle',
|
||||
symbolSize: 5,
|
||||
lineStyle: { width: 2, color: fulfillColor },
|
||||
itemStyle: { color: fulfillColor },
|
||||
areaStyle: {
|
||||
color: {
|
||||
type: 'linear',
|
||||
x: 0,
|
||||
y: 0,
|
||||
x2: 0,
|
||||
y2: 1,
|
||||
colorStops: [
|
||||
{
|
||||
offset: 0,
|
||||
color: isDark ? 'rgba(255, 182, 93, 0.22)' : 'rgba(217, 146, 74, 0.16)',
|
||||
},
|
||||
{ offset: 1, color: isDark ? 'rgba(255, 182, 93, 0.04)' : 'rgba(217, 146, 74, 0.03)' },
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: t('workplace.admin.businessTrend.pickup'),
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
data: pickupData.value,
|
||||
symbol: 'circle',
|
||||
symbolSize: 5,
|
||||
lineStyle: { width: 2, color: pickupColor },
|
||||
itemStyle: { color: pickupColor },
|
||||
areaStyle: {
|
||||
color: {
|
||||
type: 'linear',
|
||||
x: 0,
|
||||
y: 0,
|
||||
x2: 0,
|
||||
y2: 1,
|
||||
colorStops: [
|
||||
{
|
||||
offset: 0,
|
||||
color: isDark ? 'rgba(54, 207, 201, 0.22)' : 'rgba(20, 184, 166, 0.16)',
|
||||
},
|
||||
{ offset: 1, color: isDark ? 'rgba(54, 207, 201, 0.04)' : 'rgba(20, 184, 166, 0.03)' },
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
void loadData()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.aw-trend {
|
||||
--aw-ink: #1a2e38;
|
||||
--aw-soft: rgba(26, 46, 56, 0.48);
|
||||
--aw-chart-h: 320px;
|
||||
|
||||
padding: 12px 14px 14px;
|
||||
}
|
||||
|
||||
.aw-trend__head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
margin-bottom: 8px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.aw-trend__title {
|
||||
margin: 0;
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
color: var(--aw-ink);
|
||||
}
|
||||
|
||||
.aw-trend__loading {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: var(--aw-chart-h);
|
||||
}
|
||||
|
||||
.aw-trend__empty {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: var(--aw-chart-h);
|
||||
padding: 16px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.aw-trend__empty-title {
|
||||
margin: 0 0 6px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: var(--aw-ink);
|
||||
}
|
||||
|
||||
.aw-trend__empty-desc {
|
||||
margin: 0;
|
||||
font-size: 12px;
|
||||
line-height: 1.45;
|
||||
color: var(--aw-soft);
|
||||
}
|
||||
|
||||
.aw-trend__chart {
|
||||
width: 100%;
|
||||
height: var(--aw-chart-h);
|
||||
}
|
||||
</style>
|
||||
@ -205,16 +205,16 @@ onMounted(() => {
|
||||
@import '@/assets/style/stat-card.less';
|
||||
|
||||
.aw-ops {
|
||||
--aw-ink: #5c3d2e;
|
||||
--aw-soft: rgba(92, 61, 46, 0.48);
|
||||
--aw-edge: rgba(220, 180, 140, 0.42);
|
||||
--aw-shadow: 0 8px 20px rgba(160, 110, 70, 0.08);
|
||||
--aw-ink: #1a2e38;
|
||||
--aw-soft: rgba(26, 46, 56, 0.48);
|
||||
--aw-edge: var(--app-acrylic-border);
|
||||
--aw-shadow: none;
|
||||
|
||||
padding: 14px 16px 16px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--aw-edge);
|
||||
background: #fff;
|
||||
box-shadow: var(--aw-shadow);
|
||||
padding: 0;
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
background: transparent;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.aw-ops__head {
|
||||
@ -248,15 +248,18 @@ onMounted(() => {
|
||||
height: 28px;
|
||||
padding: 0 10px;
|
||||
border-radius: 8px;
|
||||
border: 1px solid var(--aw-edge);
|
||||
background: #fff;
|
||||
border: 1px solid var(--app-acrylic-border);
|
||||
background: var(--app-acrylic-bg);
|
||||
backdrop-filter: var(--app-acrylic-blur);
|
||||
-webkit-backdrop-filter: var(--app-acrylic-blur);
|
||||
color: var(--aw-ink);
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover:not(:disabled) {
|
||||
border-color: rgba(193, 122, 82, 0.55);
|
||||
border-color: var(--app-acrylic-border-strong);
|
||||
background: var(--app-acrylic-hover-bg);
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
@ -288,31 +291,11 @@ onMounted(() => {
|
||||
}
|
||||
|
||||
.stat-card {
|
||||
border: 1px solid var(--color-border-2, #e5e6eb);
|
||||
border-radius: 12px;
|
||||
background: #fff;
|
||||
box-shadow: 0 2px 8px rgba(15, 23, 42, 0.04);
|
||||
backdrop-filter: none;
|
||||
-webkit-backdrop-filter: none;
|
||||
min-height: 84px;
|
||||
padding: 12px 14px 10px;
|
||||
|
||||
.stat-card__blob,
|
||||
.stat-card__shine {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
border-color: color-mix(in srgb, #c17a52 28%, var(--color-border-2));
|
||||
background: #fff;
|
||||
box-shadow: 0 4px 14px rgba(160, 110, 70, 0.08);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
&.is-primary {
|
||||
border-color: color-mix(in srgb, #c45a3a 45%, white);
|
||||
background: #fff;
|
||||
box-shadow: 0 6px 16px rgba(196, 90, 58, 0.1);
|
||||
border-color: color-mix(in srgb, var(--app-primary-color) 22%, var(--app-acrylic-border));
|
||||
}
|
||||
}
|
||||
|
||||
@ -323,7 +306,7 @@ onMounted(() => {
|
||||
gap: 8px;
|
||||
margin-top: 12px;
|
||||
padding-top: 10px;
|
||||
border-top: 1px dashed rgba(220, 180, 140, 0.4);
|
||||
border-top: 1px dashed color-mix(in srgb, var(--app-acrylic-border) 65%, transparent);
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
|
||||
119
src/views/home/workplace/components/AdminStatsPanel.vue
Normal file
119
src/views/home/workplace/components/AdminStatsPanel.vue
Normal file
@ -0,0 +1,119 @@
|
||||
<template>
|
||||
<section
|
||||
class="aw-stats-panel"
|
||||
:class="`aw-stats-panel--${variant}`"
|
||||
:aria-label="variant === 'fulfill' ? 'fulfill-stats' : 'pickup-stats'"
|
||||
>
|
||||
<p class="aw-stats-panel__title">{{ t(titleKey) }}</p>
|
||||
<div v-if="loading" class="aw-stats-panel__loading">
|
||||
<a-spin :size="20" />
|
||||
</div>
|
||||
<div v-else class="stat-grid stat-grid--inner">
|
||||
<button
|
||||
v-for="item in items"
|
||||
:key="item.key"
|
||||
type="button"
|
||||
class="stat-card stat-card--clickable"
|
||||
:class="[
|
||||
`stat-card--${item.tone}`,
|
||||
{ 'stat-card--alert': item.alert && item.value > 0 },
|
||||
]"
|
||||
@click="go(item)"
|
||||
>
|
||||
<div class="stat-card__main">
|
||||
<div class="stat-card__meta">
|
||||
<a-avatar :size="40" class="stat-card__avatar">
|
||||
<img alt="" :src="item.icon" />
|
||||
</a-avatar>
|
||||
<span class="stat-card__label">{{ t(item.titleKey) }}</span>
|
||||
</div>
|
||||
<div class="stat-card__body">
|
||||
<span class="stat-card__value">{{ formatValue(item.value) }}</span>
|
||||
<span class="stat-card__unit">{{ t('workplace.pecs') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useWorkplaceFulfillPickupStats } from '../composables/useWorkplaceFulfillPickupStats'
|
||||
|
||||
defineOptions({ name: 'AdminStatsPanel' })
|
||||
|
||||
const props = defineProps<{
|
||||
variant: 'fulfill' | 'pickup'
|
||||
}>()
|
||||
|
||||
const { t } = useI18n()
|
||||
const { loading, fulfillItems, pickupItems, formatValue, go } = useWorkplaceFulfillPickupStats()
|
||||
|
||||
const titleKey = computed(() =>
|
||||
props.variant === 'fulfill'
|
||||
? 'workplace.admin.fulfillTitle'
|
||||
: 'workplace.admin.pickupTitle',
|
||||
)
|
||||
|
||||
const items = computed(() =>
|
||||
props.variant === 'fulfill' ? fulfillItems.value : pickupItems.value,
|
||||
)
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
@import '@/assets/style/stat-card.less';
|
||||
|
||||
.aw-stats-panel {
|
||||
--aw-ink: #1a2e38;
|
||||
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
padding: 12px 14px 14px;
|
||||
}
|
||||
|
||||
.aw-stats-panel__title {
|
||||
margin: 0 0 10px;
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
color: var(--aw-ink);
|
||||
}
|
||||
|
||||
.aw-stats-panel__loading {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 72px;
|
||||
}
|
||||
|
||||
.stat-grid--inner {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.stat-grid--inner .stat-card {
|
||||
width: 100%;
|
||||
min-height: 72px;
|
||||
margin: 0;
|
||||
padding: 10px 12px 8px;
|
||||
border-radius: 14px;
|
||||
}
|
||||
|
||||
.stat-grid--inner .stat-card__value {
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
@media (max-width: 1280px) {
|
||||
.stat-grid--inner {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.stat-grid--inner {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -9,35 +9,49 @@
|
||||
<AdminOpsQueue />
|
||||
</section>
|
||||
|
||||
<div class="admin-left__row">
|
||||
<div class="admin-left__body">
|
||||
<div class="admin-left__stats">
|
||||
<section class="aw-panel aw-panel--fulfill" aria-label="fulfill-stats">
|
||||
<AdminStatsPanel variant="fulfill" />
|
||||
</section>
|
||||
<section class="aw-panel aw-panel--pickup" aria-label="pickup-stats">
|
||||
<AdminStatsPanel variant="pickup" />
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<section class="aw-panel aw-panel--care" aria-label="today-care">
|
||||
<CareTodayPanel variant="hero" />
|
||||
</section>
|
||||
<section class="aw-panel aw-panel--links" aria-label="focus-links">
|
||||
<p class="aw-panel__title">{{ t('workplace.admin.linksTitle') }}</p>
|
||||
<div class="aw-link-grid">
|
||||
<button
|
||||
v-for="item in links"
|
||||
:key="item.key"
|
||||
type="button"
|
||||
class="aw-link"
|
||||
:class="`aw-tone--${item.tone}`"
|
||||
@click="go(item.path, item.query)"
|
||||
>
|
||||
<span class="aw-link__icon" aria-hidden="true">
|
||||
<component :is="item.icon" />
|
||||
</span>
|
||||
<span class="aw-link__body">
|
||||
<span class="aw-link__label">{{ t(item.labelKey) }}</span>
|
||||
<span class="aw-link__hint">{{ t(item.hintKey) }}</span>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<section class="aw-panel aw-panel--trend" aria-label="business-trend">
|
||||
<AdminBusinessTrend />
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<aside class="admin-right">
|
||||
<section class="aw-panel aw-panel--links" aria-label="focus-links">
|
||||
<p class="aw-panel__title">{{ t('workplace.admin.linksTitle') }}</p>
|
||||
<div class="aw-link-grid">
|
||||
<button
|
||||
v-for="item in links"
|
||||
:key="item.key"
|
||||
type="button"
|
||||
class="aw-link"
|
||||
:class="`aw-tone--${item.tone}`"
|
||||
@click="go(item.path, item.query)"
|
||||
>
|
||||
<span class="aw-link__icon" aria-hidden="true">
|
||||
<component :is="item.icon" />
|
||||
</span>
|
||||
<span class="aw-link__body">
|
||||
<span class="aw-link__label">{{ t(item.labelKey) }}</span>
|
||||
<span class="aw-link__hint">{{ t(item.hintKey) }}</span>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="aw-panel aw-panel--announce">
|
||||
<Announcement notice-type="message" />
|
||||
</section>
|
||||
@ -61,6 +75,8 @@ import {
|
||||
import Banner from './banner.vue'
|
||||
import CareTodayPanel from './CareTodayPanel.vue'
|
||||
import AdminOpsQueue from './AdminOpsQueue.vue'
|
||||
import AdminStatsPanel from './AdminStatsPanel.vue'
|
||||
import AdminBusinessTrend from './AdminBusinessTrend.vue'
|
||||
import Announcement from './announcement.vue'
|
||||
import NoticePopup from './notice-popup.vue'
|
||||
import { useRbacUserStore } from '@/store/modules/rbac-user'
|
||||
@ -137,13 +153,13 @@ function go(path: string, query?: Record<string, string>) {
|
||||
|
||||
<style scoped lang="less">
|
||||
.admin-workplace {
|
||||
--aw-ink: #5c3d2e;
|
||||
--aw-muted: rgba(92, 61, 46, 0.68);
|
||||
--aw-soft: rgba(92, 61, 46, 0.48);
|
||||
--aw-edge: rgba(220, 180, 140, 0.42);
|
||||
--aw-shadow: 0 8px 20px rgba(160, 110, 70, 0.06);
|
||||
--aw-ink: #1a2e38;
|
||||
--aw-muted: rgba(26, 46, 56, 0.68);
|
||||
--aw-soft: rgba(26, 46, 56, 0.48);
|
||||
--aw-edge: var(--app-acrylic-border);
|
||||
--aw-shadow: none;
|
||||
--aw-radius: 16px;
|
||||
--aw-panel-bg: #fff;
|
||||
--aw-panel-bg: var(--app-acrylic-bg);
|
||||
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
@ -157,6 +173,7 @@ function go(path: string, query?: Record<string, string>) {
|
||||
.admin-workplace__container {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
align-items: stretch;
|
||||
gap: 14px;
|
||||
min-height: 0;
|
||||
width: min(1480px, 94%);
|
||||
@ -169,28 +186,30 @@ function go(path: string, query?: Record<string, string>) {
|
||||
.admin-left {
|
||||
flex: 7;
|
||||
min-width: 0;
|
||||
min-height: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
overflow: auto;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.admin-left__row {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1.15fr) minmax(260px, 0.85fr);
|
||||
gap: 12px;
|
||||
align-items: stretch;
|
||||
.admin-left__body {
|
||||
flex: 1 1 auto;
|
||||
min-height: 220px;
|
||||
min-height: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.admin-right {
|
||||
flex: 3;
|
||||
min-width: 280px;
|
||||
max-width: 380px;
|
||||
min-height: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 0;
|
||||
gap: 12px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
@ -199,6 +218,8 @@ function go(path: string, query?: Record<string, string>) {
|
||||
border-radius: var(--aw-radius);
|
||||
background: var(--aw-panel-bg);
|
||||
box-shadow: var(--aw-shadow);
|
||||
backdrop-filter: var(--app-acrylic-blur);
|
||||
-webkit-backdrop-filter: var(--app-acrylic-blur);
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
@ -208,36 +229,61 @@ function go(path: string, query?: Record<string, string>) {
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.aw-panel--care,
|
||||
.aw-panel--links {
|
||||
min-height: 0;
|
||||
overflow: auto;
|
||||
.admin-left__stats {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
|
||||
gap: 12px;
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
.aw-panel--fulfill,
|
||||
.aw-panel--pickup {
|
||||
flex: 0 0 auto;
|
||||
padding: 0;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.aw-panel--pickup {
|
||||
margin-top: 18px;
|
||||
}
|
||||
|
||||
.aw-panel--trend {
|
||||
flex: 0 0 auto;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
.aw-panel--care {
|
||||
flex: 0 0 auto;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
|
||||
:deep(.care-today) {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
min-height: 220px;
|
||||
min-height: 148px;
|
||||
box-sizing: border-box;
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
border-radius: var(--aw-radius);
|
||||
background: #fff;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
:deep(.care-today.is-hero.is-collapsed) {
|
||||
min-height: 88px;
|
||||
}
|
||||
}
|
||||
|
||||
.aw-panel--links {
|
||||
flex: 0 0 auto;
|
||||
padding: 14px 14px 16px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.aw-panel--announce {
|
||||
flex: 1 1 auto;
|
||||
min-height: 0;
|
||||
height: 100%;
|
||||
margin-top: auto;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@ -262,7 +308,7 @@ function go(path: string, query?: Record<string, string>) {
|
||||
|
||||
:deep(.arco-card-header) {
|
||||
flex-shrink: 0;
|
||||
border-bottom: 1px solid rgba(220, 180, 140, 0.35);
|
||||
border-bottom: 1px solid color-mix(in srgb, var(--app-acrylic-border) 55%, transparent);
|
||||
}
|
||||
|
||||
:deep(.arco-card-body) {
|
||||
@ -301,24 +347,29 @@ function go(path: string, query?: Record<string, string>) {
|
||||
}
|
||||
|
||||
.aw-link {
|
||||
--tone: #c17a52;
|
||||
--tone-edge: rgba(193, 122, 82, 0.35);
|
||||
--tone-strong: #b86b45;
|
||||
--tone: var(--app-primary-color);
|
||||
--tone-edge: var(--app-acrylic-border);
|
||||
--tone-strong: var(--app-primary-color);
|
||||
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 10px;
|
||||
min-height: 64px;
|
||||
padding: 10px 12px;
|
||||
min-height: 56px;
|
||||
padding: 8px 12px;
|
||||
border-radius: 12px;
|
||||
border: 1px solid var(--tone-edge);
|
||||
background: #fff;
|
||||
background: var(--app-acrylic-bg);
|
||||
backdrop-filter: var(--app-acrylic-blur);
|
||||
-webkit-backdrop-filter: var(--app-acrylic-blur);
|
||||
box-shadow: none;
|
||||
text-align: left;
|
||||
cursor: pointer;
|
||||
transition: background 0.15s ease, border-color 0.15s ease, box-shadow 0.15s ease;
|
||||
transition: border-color 0.2s ease, background 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
box-shadow: 0 6px 14px rgba(160, 110, 70, 0.08);
|
||||
border-color: var(--app-acrylic-border-strong);
|
||||
background: var(--app-acrylic-hover-bg);
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
@ -361,14 +412,14 @@ function go(path: string, query?: Record<string, string>) {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border-radius: 10px;
|
||||
flex-shrink: 0;
|
||||
background: #fff;
|
||||
background: transparent;
|
||||
color: var(--tone-strong);
|
||||
font-size: 15px;
|
||||
border: 1px solid var(--tone-edge);
|
||||
font-size: 14px;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.aw-link__body {
|
||||
@ -390,7 +441,6 @@ function go(path: string, query?: Record<string, string>) {
|
||||
color: var(--aw-soft);
|
||||
}
|
||||
|
||||
/* Ops 嵌在 hero 内时去掉外层重复边框 */
|
||||
.aw-panel--hero :deep(.aw-ops.aw-surface) {
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
@ -410,14 +460,34 @@ function go(path: string, query?: Record<string, string>) {
|
||||
flex: none;
|
||||
max-width: none;
|
||||
width: 100%;
|
||||
min-height: auto;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.admin-left__body {
|
||||
flex: none;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.aw-panel--trend,
|
||||
.aw-panel--announce {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.admin-left__stats {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.aw-panel--pickup {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.admin-right {
|
||||
min-height: 280px;
|
||||
min-height: 360px;
|
||||
}
|
||||
|
||||
.admin-left__row {
|
||||
grid-template-columns: 1fr;
|
||||
.aw-panel--links {
|
||||
max-height: none;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -383,8 +383,8 @@ onMounted(() => {
|
||||
|
||||
<style scoped lang="less">
|
||||
.care-today {
|
||||
--care-ink: #5c3d2e;
|
||||
--care-muted: rgba(92, 61, 46, 0.72);
|
||||
--care-ink: #1a2e38;
|
||||
--care-muted: rgba(26, 46, 56, 0.72);
|
||||
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
@ -392,18 +392,20 @@ onMounted(() => {
|
||||
flex: 0 0 auto;
|
||||
padding: 12px 14px 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid rgba(220, 180, 140, 0.42);
|
||||
background: #fff;
|
||||
box-shadow: 0 8px 20px rgba(160, 110, 70, 0.06);
|
||||
transition: border-radius 0.6s ease, box-shadow 0.2s ease;
|
||||
border: 1px solid var(--app-acrylic-border);
|
||||
background: var(--app-acrylic-bg);
|
||||
backdrop-filter: var(--app-acrylic-blur);
|
||||
-webkit-backdrop-filter: var(--app-acrylic-blur);
|
||||
box-shadow: none;
|
||||
transition: border-radius 0.6s ease, background 0.2s ease, border-color 0.2s ease;
|
||||
|
||||
&.is-pressure {
|
||||
border-color: rgba(230, 170, 140, 0.55);
|
||||
border-color: color-mix(in srgb, var(--app-primary-color) 18%, var(--app-acrylic-border));
|
||||
}
|
||||
|
||||
&.is-clocked-out {
|
||||
border-color: rgba(220, 180, 140, 0.5);
|
||||
background: #fff;
|
||||
border-color: var(--app-acrylic-border);
|
||||
background: var(--app-acrylic-bg);
|
||||
}
|
||||
|
||||
&.is-collapsed {
|
||||
@ -431,16 +433,16 @@ onMounted(() => {
|
||||
}
|
||||
|
||||
.care-action--primary {
|
||||
border-color: rgba(193, 122, 82, 0.35);
|
||||
background: #fff;
|
||||
border-color: var(--app-acrylic-border);
|
||||
background: transparent;
|
||||
|
||||
em {
|
||||
color: #b86b45;
|
||||
color: var(--app-primary-color);
|
||||
}
|
||||
}
|
||||
|
||||
.care-today__clocked {
|
||||
color: #5c3d2e;
|
||||
color: var(--care-ink);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -477,8 +479,8 @@ onMounted(() => {
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
color: var(--care-ink);
|
||||
background: #fff;
|
||||
border: 1px solid rgba(220, 180, 140, 0.4);
|
||||
background: transparent;
|
||||
border: 1px solid color-mix(in srgb, var(--app-acrylic-border) 80%, transparent);
|
||||
}
|
||||
|
||||
.care-today__head {
|
||||
@ -505,8 +507,8 @@ onMounted(() => {
|
||||
|
||||
.care-today__toggle {
|
||||
flex-shrink: 0;
|
||||
border: 1px solid rgba(220, 180, 140, 0.4);
|
||||
background: #fff;
|
||||
border: 1px solid var(--app-acrylic-border);
|
||||
background: transparent;
|
||||
color: var(--care-muted);
|
||||
font-size: 11px;
|
||||
line-height: 1;
|
||||
@ -516,7 +518,8 @@ onMounted(() => {
|
||||
|
||||
&:hover {
|
||||
color: var(--care-ink);
|
||||
border-color: rgba(193, 122, 82, 0.45);
|
||||
border-color: var(--app-acrylic-border-strong);
|
||||
background: var(--app-acrylic-hover-bg);
|
||||
}
|
||||
}
|
||||
|
||||
@ -565,21 +568,22 @@ onMounted(() => {
|
||||
width: 100%;
|
||||
padding: 8px 10px;
|
||||
border-radius: 12px;
|
||||
border: 1px solid rgba(220, 180, 140, 0.4);
|
||||
background: #fff;
|
||||
border: 1px solid var(--app-acrylic-border);
|
||||
background: transparent;
|
||||
text-align: left;
|
||||
cursor: pointer;
|
||||
transition: transform 0.15s ease, background 0.15s ease, box-shadow 0.15s ease;
|
||||
transition: border-color 0.2s ease, background 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
transform: translateY(-1px);
|
||||
background: #fff;
|
||||
box-shadow: 0 6px 14px rgba(180, 110, 70, 0.08);
|
||||
transform: none;
|
||||
border-color: var(--app-acrylic-border-strong);
|
||||
background: var(--app-acrylic-hover-bg);
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
&--primary {
|
||||
border-color: rgba(193, 122, 82, 0.4);
|
||||
background: #fff;
|
||||
border-color: color-mix(in srgb, var(--app-primary-color) 18%, var(--app-acrylic-border));
|
||||
background: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
@ -592,7 +596,7 @@ onMounted(() => {
|
||||
margin-left: 4px;
|
||||
font-style: normal;
|
||||
font-variant-numeric: tabular-nums;
|
||||
color: var(--app-primary-color, #1d7281);
|
||||
color: var(--app-primary-color, #054f74);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -44,16 +44,9 @@ defineOptions({ name: 'CustomerWorkplace' })
|
||||
.workplace-container {
|
||||
--wp-card-radius: 16px;
|
||||
--wp-inner-radius: 12px;
|
||||
--wp-card-border: 1px solid rgba(255, 255, 255, 0.78);
|
||||
--wp-card-bg: linear-gradient(
|
||||
155deg,
|
||||
rgba(255, 255, 255, 0.94) 0%,
|
||||
rgba(255, 255, 255, 0.78) 100%
|
||||
);
|
||||
--wp-card-shadow:
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.9),
|
||||
0 6px 18px rgba(0, 121, 121, 0.05),
|
||||
0 2px 12px rgba(0, 0, 0, 0.04);
|
||||
--wp-card-border: 1px solid var(--app-acrylic-border);
|
||||
--wp-card-bg: var(--app-acrylic-bg);
|
||||
--wp-card-shadow: none;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
min-height: 100%;
|
||||
@ -195,6 +188,8 @@ defineOptions({ name: 'CustomerWorkplace' })
|
||||
border-radius: var(--wp-card-radius);
|
||||
background: var(--wp-card-bg);
|
||||
box-shadow: var(--wp-card-shadow);
|
||||
backdrop-filter: var(--app-acrylic-blur);
|
||||
-webkit-backdrop-filter: var(--app-acrylic-blur);
|
||||
flex-shrink: 0;
|
||||
|
||||
:deep(.arco-card) {
|
||||
@ -278,6 +273,8 @@ defineOptions({ name: 'CustomerWorkplace' })
|
||||
border-radius: var(--wp-card-radius);
|
||||
background: var(--wp-card-bg);
|
||||
box-shadow: var(--wp-card-shadow);
|
||||
backdrop-filter: var(--app-acrylic-blur);
|
||||
-webkit-backdrop-filter: var(--app-acrylic-blur);
|
||||
}
|
||||
|
||||
:deep(.chart-controls) {
|
||||
|
||||
@ -231,16 +231,18 @@
|
||||
gap: 10px;
|
||||
height: 44px;
|
||||
padding: 0 12px 0 14px;
|
||||
border: 1px solid var(--color-border-2);
|
||||
border: 1px solid var(--app-acrylic-border);
|
||||
border-radius: 12px;
|
||||
background: var(--color-fill-1);
|
||||
background: var(--app-acrylic-bg);
|
||||
backdrop-filter: var(--app-acrylic-blur);
|
||||
-webkit-backdrop-filter: var(--app-acrylic-blur);
|
||||
cursor: pointer;
|
||||
transition: border-color 0.2s, background 0.2s, box-shadow 0.2s;
|
||||
transition: border-color 0.2s, background 0.2s;
|
||||
|
||||
&:hover {
|
||||
border-color: var(--app-primary-color, rgb(var(--primary-6)));
|
||||
background: var(--color-primary-light-1, #e8fffb);
|
||||
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.06);
|
||||
border-color: var(--app-acrylic-border-strong);
|
||||
background: var(--app-acrylic-hover-bg);
|
||||
box-shadow: none;
|
||||
|
||||
.balance-chip__cta {
|
||||
opacity: 1;
|
||||
|
||||
@ -322,31 +322,10 @@ watch(showWorldTime, (val) => {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
/* 与营销入口、右侧内层卡片统一 */
|
||||
/* 首页统计卡与营销入口统一磨砂质感(继承 stat-card.less) */
|
||||
.stat-card {
|
||||
border: 1px solid rgba(255, 255, 255, 0.78);
|
||||
border-radius: var(--wp-inner-radius, 12px);
|
||||
background:
|
||||
linear-gradient(155deg, rgba(255, 255, 255, 0.9) 0%, rgba(255, 255, 255, 0.68) 100%);
|
||||
box-shadow:
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.9),
|
||||
0 2px 8px rgba(0, 121, 121, 0.05);
|
||||
backdrop-filter: none;
|
||||
-webkit-backdrop-filter: none;
|
||||
min-height: 84px;
|
||||
padding: 12px 14px 10px;
|
||||
|
||||
.stat-card__blob,
|
||||
.stat-card__shine {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
border-color: color-mix(in srgb, var(--app-primary-color) 28%, var(--color-border-2));
|
||||
background: rgba(255, 255, 255, 0.96);
|
||||
box-shadow: 0 4px 14px rgba(0, 121, 121, 0.1);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
}
|
||||
|
||||
.stat-card--compact {
|
||||
|
||||
@ -150,24 +150,23 @@ onMounted(async () => {
|
||||
min-height: 64px;
|
||||
margin: 0;
|
||||
padding: 10px 12px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.78);
|
||||
border: 1px solid var(--app-acrylic-border);
|
||||
border-radius: var(--wp-inner-radius, 12px);
|
||||
background:
|
||||
linear-gradient(155deg, rgba(255, 255, 255, 0.88) 0%, rgba(255, 255, 255, 0.62) 100%);
|
||||
box-shadow:
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.9),
|
||||
0 2px 8px rgba(0, 121, 121, 0.05);
|
||||
background: var(--app-acrylic-bg);
|
||||
backdrop-filter: var(--app-acrylic-blur);
|
||||
-webkit-backdrop-filter: var(--app-acrylic-blur);
|
||||
box-shadow: none;
|
||||
text-align: left;
|
||||
font: inherit;
|
||||
color: inherit;
|
||||
cursor: pointer;
|
||||
transition: border-color 0.2s, background 0.2s, box-shadow 0.2s, transform 0.2s;
|
||||
transition: border-color 0.2s, background 0.2s;
|
||||
|
||||
&:hover {
|
||||
border-color: color-mix(in srgb, var(--app-primary-color) 28%, var(--color-border-2));
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
box-shadow: 0 4px 14px rgba(0, 121, 121, 0.1);
|
||||
transform: translateY(-1px);
|
||||
border-color: var(--app-acrylic-border-strong);
|
||||
background: var(--app-acrylic-hover-bg);
|
||||
box-shadow: none;
|
||||
transform: none;
|
||||
}
|
||||
|
||||
&__badge {
|
||||
@ -198,7 +197,7 @@ onMounted(async () => {
|
||||
border-radius: 10px;
|
||||
font-size: 15px;
|
||||
color: var(--app-primary-color, rgb(var(--primary-6)));
|
||||
background: color-mix(in srgb, var(--app-primary-color) 10%, #fff);
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
&__body {
|
||||
|
||||
@ -278,24 +278,26 @@
|
||||
gap: 6px;
|
||||
min-width: 0;
|
||||
padding: 8px 10px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.78);
|
||||
border: 1px solid var(--app-acrylic-border);
|
||||
border-radius: var(--wp-inner-radius, 12px);
|
||||
background: rgba(255, 255, 255, 0.55);
|
||||
background: var(--app-acrylic-bg);
|
||||
backdrop-filter: var(--app-acrylic-blur);
|
||||
-webkit-backdrop-filter: var(--app-acrylic-blur);
|
||||
color: var(--color-text-2);
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: border-color 0.25s, background 0.25s, box-shadow 0.25s, color 0.2s;
|
||||
transition: border-color 0.2s, background 0.2s, color 0.2s;
|
||||
|
||||
&:hover {
|
||||
border-color: rgba(255, 255, 255, 0.9);
|
||||
background: rgba(255, 255, 255, 0.62);
|
||||
border-color: var(--app-acrylic-border-strong);
|
||||
background: var(--app-acrylic-hover-bg);
|
||||
}
|
||||
|
||||
&.is-active {
|
||||
border-color: rgba(0, 121, 121, 0.35);
|
||||
background: linear-gradient(155deg, rgba(0, 121, 121, 0.12), rgba(255, 255, 255, 0.7));
|
||||
box-shadow: 0 6px 16px rgba(0, 121, 121, 0.1);
|
||||
border-color: color-mix(in srgb, var(--app-primary-color) 22%, var(--app-acrylic-border));
|
||||
background: var(--app-acrylic-hover-bg);
|
||||
box-shadow: none;
|
||||
color: var(--app-primary-color, rgb(var(--primary-6)));
|
||||
|
||||
.biz-switch__icon {
|
||||
@ -313,7 +315,7 @@
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border-radius: 8px;
|
||||
background: rgba(255, 255, 255, 0.8);
|
||||
background: transparent;
|
||||
color: var(--color-text-2);
|
||||
font-size: 13px;
|
||||
}
|
||||
@ -325,16 +327,18 @@
|
||||
}
|
||||
|
||||
.flow-step {
|
||||
border: 1px solid rgba(255, 255, 255, 0.78);
|
||||
border: 1px solid var(--app-acrylic-border);
|
||||
border-radius: var(--wp-inner-radius, 12px);
|
||||
background: rgba(255, 255, 255, 0.55);
|
||||
background: var(--app-acrylic-bg);
|
||||
backdrop-filter: var(--app-acrylic-blur);
|
||||
-webkit-backdrop-filter: var(--app-acrylic-blur);
|
||||
overflow: hidden;
|
||||
transition: border-color 0.2s, background 0.2s, box-shadow 0.2s;
|
||||
transition: border-color 0.2s, background 0.2s;
|
||||
|
||||
&:hover {
|
||||
border-color: rgba(0, 121, 121, 0.28);
|
||||
background: rgba(255, 255, 255, 0.62);
|
||||
box-shadow: 0 4px 14px rgba(0, 121, 121, 0.08);
|
||||
border-color: var(--app-acrylic-border-strong);
|
||||
background: var(--app-acrylic-hover-bg);
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
&__main {
|
||||
@ -408,18 +412,18 @@
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 4px 10px;
|
||||
border: 1px solid var(--color-border-2);
|
||||
border: 1px solid var(--app-acrylic-border);
|
||||
border-radius: 14px;
|
||||
background: rgba(255, 255, 255, 0.85);
|
||||
background: transparent;
|
||||
color: var(--color-text-2);
|
||||
font-size: 12px;
|
||||
cursor: pointer;
|
||||
transition: border-color 0.2s, color 0.2s, background 0.2s;
|
||||
|
||||
&:hover {
|
||||
border-color: var(--app-primary-color, rgb(var(--primary-6)));
|
||||
border-color: var(--app-acrylic-border-strong);
|
||||
color: var(--app-primary-color, rgb(var(--primary-6)));
|
||||
background: var(--app-card-bg);
|
||||
background: var(--app-acrylic-hover-bg);
|
||||
}
|
||||
}
|
||||
|
||||
@ -440,18 +444,18 @@
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 5px 10px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.7);
|
||||
border: 1px solid var(--app-acrylic-border);
|
||||
border-radius: 16px;
|
||||
background: rgba(255, 255, 255, 0.7);
|
||||
background: transparent;
|
||||
color: var(--color-text-2);
|
||||
font-size: 12px;
|
||||
cursor: pointer;
|
||||
transition: border-color 0.2s, color 0.2s, background 0.2s;
|
||||
|
||||
&:hover {
|
||||
border-color: var(--app-primary-color, rgb(var(--primary-6)));
|
||||
border-color: var(--app-acrylic-border-strong);
|
||||
color: var(--app-primary-color, rgb(var(--primary-6)));
|
||||
background: var(--app-card-bg);
|
||||
background: var(--app-acrylic-hover-bg);
|
||||
}
|
||||
|
||||
&__icon {
|
||||
|
||||
@ -135,20 +135,22 @@
|
||||
min-width: 0;
|
||||
padding: 10px 12px;
|
||||
border-radius: 12px;
|
||||
border: 1px solid rgba(220, 180, 140, 0.38);
|
||||
background: #fff;
|
||||
box-shadow: 0 2px 8px rgba(160, 110, 70, 0.05);
|
||||
border: 1px solid var(--app-acrylic-border);
|
||||
background: var(--app-acrylic-bg);
|
||||
backdrop-filter: var(--app-acrylic-blur);
|
||||
-webkit-backdrop-filter: var(--app-acrylic-blur);
|
||||
box-shadow: none;
|
||||
|
||||
&--local {
|
||||
border-color: rgba(193, 122, 82, 0.42);
|
||||
background: #fff;
|
||||
border-color: var(--app-acrylic-border-strong);
|
||||
background: var(--app-acrylic-hover-bg);
|
||||
}
|
||||
}
|
||||
|
||||
.wt-card__label {
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
color: rgba(92, 61, 46, 0.62);
|
||||
color: var(--color-text-3);
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
@ -159,7 +161,7 @@
|
||||
font-weight: 700;
|
||||
font-variant-numeric: tabular-nums;
|
||||
letter-spacing: 0.03em;
|
||||
color: #5c3d2e;
|
||||
color: var(--color-text-1);
|
||||
line-height: 1.15;
|
||||
}
|
||||
|
||||
@ -169,7 +171,7 @@
|
||||
|
||||
.wt-card__meta {
|
||||
font-size: 11px;
|
||||
color: rgba(92, 61, 46, 0.5);
|
||||
color: var(--color-text-3);
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
@ -0,0 +1,171 @@
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import {
|
||||
getOrderWorkplaceStats,
|
||||
type FulfillWorkplaceStats,
|
||||
type PickupWorkplaceStats,
|
||||
} from '@/api/workplace'
|
||||
import iconStorePending from '@/assets/icons/workplace/order-store-pending.svg?url'
|
||||
import iconPendingShip from '@/assets/icons/workplace/order-pending-ship.svg?url'
|
||||
import iconPendingOnline from '@/assets/icons/workplace/order-pending-online.svg?url'
|
||||
import iconShipped from '@/assets/icons/workplace/order-shipped.svg?url'
|
||||
import iconNew from '@/assets/icons/workplace/order-new.svg?url'
|
||||
|
||||
export type WorkplaceStatTone = 'neutral' | 'primary' | 'warning' | 'danger' | 'success'
|
||||
|
||||
export type WorkplaceStatItem = {
|
||||
key: string
|
||||
titleKey: string
|
||||
value: number
|
||||
icon: string
|
||||
tone: WorkplaceStatTone
|
||||
alert?: boolean
|
||||
path: string
|
||||
query: Record<string, string>
|
||||
}
|
||||
|
||||
const FULFILL_PATH = '/order/fulfillOrders'
|
||||
const PICKUP_PATH = '/order/pickupOrder'
|
||||
|
||||
const defaultFulfill = (): FulfillWorkplaceStats => ({
|
||||
pendingBind: 0,
|
||||
domesticInTransit: 0,
|
||||
arrivedPendingPick: 0,
|
||||
outboundDone: 0,
|
||||
})
|
||||
|
||||
const defaultPickup = (): PickupWorkplaceStats => ({
|
||||
pendingPayment: 0,
|
||||
pendingSchedule: 0,
|
||||
pendingPickup: 0,
|
||||
pendingInbound: 0,
|
||||
})
|
||||
|
||||
const fulfill = ref<FulfillWorkplaceStats>(defaultFulfill())
|
||||
const pickup = ref<PickupWorkplaceStats>(defaultPickup())
|
||||
const loading = ref(false)
|
||||
let loadPromise: Promise<void> | null = null
|
||||
|
||||
async function loadStats() {
|
||||
if (loadPromise) return loadPromise
|
||||
loading.value = true
|
||||
loadPromise = (async () => {
|
||||
try {
|
||||
const stats = await getOrderWorkplaceStats()
|
||||
fulfill.value = stats.fulfill
|
||||
pickup.value = stats.pickup
|
||||
} catch {
|
||||
fulfill.value = defaultFulfill()
|
||||
pickup.value = defaultPickup()
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
})()
|
||||
return loadPromise
|
||||
}
|
||||
|
||||
export function useWorkplaceFulfillPickupStats() {
|
||||
const router = useRouter()
|
||||
|
||||
const fulfillItems = computed<WorkplaceStatItem[]>(() => [
|
||||
{
|
||||
key: 'pendingBind',
|
||||
titleKey: 'workplace.admin.fulfill.pendingBind',
|
||||
value: fulfill.value.pendingBind,
|
||||
icon: iconStorePending,
|
||||
tone: 'warning',
|
||||
alert: true,
|
||||
path: FULFILL_PATH,
|
||||
query: { progress: 'pendingBind' },
|
||||
},
|
||||
{
|
||||
key: 'domesticInTransit',
|
||||
titleKey: 'workplace.admin.fulfill.domesticInTransit',
|
||||
value: fulfill.value.domesticInTransit,
|
||||
icon: iconPendingOnline,
|
||||
tone: 'neutral',
|
||||
path: FULFILL_PATH,
|
||||
query: { progress: 'domesticInTransit' },
|
||||
},
|
||||
{
|
||||
key: 'arrivedPendingPick',
|
||||
titleKey: 'workplace.admin.fulfill.arrivedPendingPick',
|
||||
value: fulfill.value.arrivedPendingPick,
|
||||
icon: iconPendingShip,
|
||||
tone: 'warning',
|
||||
alert: true,
|
||||
path: FULFILL_PATH,
|
||||
query: { progress: 'arrivedPendingPick' },
|
||||
},
|
||||
{
|
||||
key: 'outboundDone',
|
||||
titleKey: 'workplace.admin.fulfill.outboundDone',
|
||||
value: fulfill.value.outboundDone,
|
||||
icon: iconShipped,
|
||||
tone: 'success',
|
||||
path: FULFILL_PATH,
|
||||
query: { progress: 'outboundDone' },
|
||||
},
|
||||
])
|
||||
|
||||
const pickupItems = computed<WorkplaceStatItem[]>(() => [
|
||||
{
|
||||
key: 'pendingPayment',
|
||||
titleKey: 'workplace.admin.pickup.pendingPayment',
|
||||
value: pickup.value.pendingPayment,
|
||||
icon: iconPendingOnline,
|
||||
tone: 'warning',
|
||||
alert: true,
|
||||
path: PICKUP_PATH,
|
||||
query: { status: 'pendingPayment' },
|
||||
},
|
||||
{
|
||||
key: 'pendingSchedule',
|
||||
titleKey: 'workplace.admin.pickup.pendingSchedule',
|
||||
value: pickup.value.pendingSchedule,
|
||||
icon: iconNew,
|
||||
tone: 'neutral',
|
||||
path: PICKUP_PATH,
|
||||
query: { status: 'draft' },
|
||||
},
|
||||
{
|
||||
key: 'pendingPickup',
|
||||
titleKey: 'workplace.admin.pickup.pendingPickup',
|
||||
value: pickup.value.pendingPickup,
|
||||
icon: iconPendingShip,
|
||||
tone: 'warning',
|
||||
alert: true,
|
||||
path: PICKUP_PATH,
|
||||
query: { status: 'scheduled' },
|
||||
},
|
||||
{
|
||||
key: 'pendingInbound',
|
||||
titleKey: 'workplace.admin.pickup.pendingInbound',
|
||||
value: pickup.value.pendingInbound,
|
||||
icon: iconShipped,
|
||||
tone: 'primary',
|
||||
path: PICKUP_PATH,
|
||||
query: { status: 'picked' },
|
||||
},
|
||||
])
|
||||
|
||||
function formatValue(n: number) {
|
||||
return Math.round(Number(n) || 0).toLocaleString('zh-CN')
|
||||
}
|
||||
|
||||
function go(item: WorkplaceStatItem) {
|
||||
router.push({ path: item.path, query: item.query }).catch(() => undefined)
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
void loadStats()
|
||||
})
|
||||
|
||||
return {
|
||||
loading,
|
||||
fulfillItems,
|
||||
pickupItems,
|
||||
formatValue,
|
||||
go,
|
||||
}
|
||||
}
|
||||
@ -233,4 +233,20 @@ export default {
|
||||
'workplace.admin.link.pickupHint': 'Pickup and handoff',
|
||||
'workplace.admin.link.price': 'Price check',
|
||||
'workplace.admin.link.priceHint': 'Quote checks',
|
||||
'workplace.admin.fulfillTitle': 'Fulfillment orders',
|
||||
'workplace.admin.pickupTitle': 'Pickup orders',
|
||||
'workplace.admin.fulfill.pendingBind': 'Pending bind',
|
||||
'workplace.admin.fulfill.domesticInTransit': 'Domestic in transit',
|
||||
'workplace.admin.fulfill.arrivedPendingPick': 'Pending pick',
|
||||
'workplace.admin.fulfill.outboundDone': 'Outbound done',
|
||||
'workplace.admin.pickup.pendingPayment': 'Pending payment',
|
||||
'workplace.admin.pickup.pendingSchedule': 'Pending schedule',
|
||||
'workplace.admin.pickup.pendingPickup': 'Pending pickup',
|
||||
'workplace.admin.pickup.pendingInbound': 'Pending inbound',
|
||||
'workplace.admin.businessTrendTitle': 'Business trends',
|
||||
'workplace.admin.businessTrend.waybill': 'Waybills',
|
||||
'workplace.admin.businessTrend.fulfill': 'Fulfillment',
|
||||
'workplace.admin.businessTrend.pickup': 'Pickup',
|
||||
'workplace.admin.businessTrendEmptyTitle': 'No trend data yet',
|
||||
'workplace.admin.businessTrendEmptyDesc': 'Trends appear once waybills, fulfillment, or pickup orders are created.',
|
||||
};
|
||||
|
||||
@ -230,4 +230,20 @@ export default {
|
||||
'workplace.admin.link.pickupHint': '揽收与交接',
|
||||
'workplace.admin.link.price': '价格查询',
|
||||
'workplace.admin.link.priceHint': '报价核对',
|
||||
'workplace.admin.fulfillTitle': '代发订单',
|
||||
'workplace.admin.pickupTitle': '揽收订单',
|
||||
'workplace.admin.fulfill.pendingBind': '待绑定',
|
||||
'workplace.admin.fulfill.domesticInTransit': '国内在途',
|
||||
'workplace.admin.fulfill.arrivedPendingPick': '待拣货',
|
||||
'workplace.admin.fulfill.outboundDone': '已出库',
|
||||
'workplace.admin.pickup.pendingPayment': '待支付',
|
||||
'workplace.admin.pickup.pendingSchedule': '待下发',
|
||||
'workplace.admin.pickup.pendingPickup': '待取件',
|
||||
'workplace.admin.pickup.pendingInbound': '待到仓',
|
||||
'workplace.admin.businessTrendTitle': '业务趋势',
|
||||
'workplace.admin.businessTrend.waybill': '运单',
|
||||
'workplace.admin.businessTrend.fulfill': '代发',
|
||||
'workplace.admin.businessTrend.pickup': '揽收',
|
||||
'workplace.admin.businessTrendEmptyTitle': '还没有业务趋势数据',
|
||||
'workplace.admin.businessTrendEmptyDesc': '运单、代发或揽收产生新增后,趋势会显示在这里。',
|
||||
};
|
||||
|
||||
@ -121,15 +121,11 @@ import {
|
||||
IconEyeInvisible,
|
||||
} from '@arco-design/web-vue/es/icon';
|
||||
import { OmsLoginType } from '@/api/rbac';
|
||||
import { getEmployeeByUser } from '@/api/system/employee';
|
||||
import { useRbacUserStore } from '@/store/modules/rbac-user';
|
||||
import { setToken } from '@/utils/auth';
|
||||
import { getDefaultHomePathByType, resolveCustomerEntryPath } from '@/utils/login-portal';
|
||||
import { enterSystemAfterLogin, applyRbacUserToUserStore } from '@/utils/enter-system';
|
||||
import { ensureHeavyPlugins } from '@/plugins/heavy-plugins';
|
||||
import { buildAdminLoginCarePayload } from '@/utils/admin-login-care';
|
||||
import { openAdminCareToast } from '@/utils/admin-care-toast';
|
||||
import { getAdminCareSettings } from '@/utils/admin-care-settings';
|
||||
import { LOGIN_CHARACTERS_KEY, type LoginCharactersApi } from '../../composables/useLoginCharacters';
|
||||
import { LOGIN_EXIT_KEY, type LoginExitApi } from '../../composables/useLoginExit';
|
||||
import LoginLockLogo from '../LoginLockLogo.vue';
|
||||
@ -336,106 +332,6 @@ function validateForm(): boolean {
|
||||
return valid;
|
||||
}
|
||||
|
||||
async function buildAdminLoginCareToast(userInfoData: {
|
||||
id?: string;
|
||||
name?: string | null;
|
||||
account?: string | null;
|
||||
}) {
|
||||
const settings = getAdminCareSettings();
|
||||
if (!settings.careEnabled || !settings.loginToastEnabled) return null;
|
||||
|
||||
const fallbackName =
|
||||
userInfoData.name || userInfoData.account || loginForm.account.trim() || '同事';
|
||||
let birthday: string | undefined;
|
||||
let hireDate: string | undefined;
|
||||
let displayName = fallbackName;
|
||||
|
||||
try {
|
||||
if (userInfoData.id) {
|
||||
const emp = await Promise.race([
|
||||
getEmployeeByUser(userInfoData.id),
|
||||
new Promise<null>((resolve) => {
|
||||
window.setTimeout(() => resolve(null), 1200);
|
||||
}),
|
||||
]);
|
||||
if (emp) {
|
||||
birthday = emp.birthday;
|
||||
hireDate = emp.hireDate;
|
||||
if (emp.realName) displayName = emp.realName;
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// 员工档案缺失时仍给出时段关怀
|
||||
}
|
||||
|
||||
const care = buildAdminLoginCarePayload({
|
||||
displayName,
|
||||
birthday,
|
||||
hireDate,
|
||||
userId: userInfoData.id,
|
||||
longAbsenceDays: settings.longAbsenceDays,
|
||||
lateNightThreshold: settings.lateNightThreshold,
|
||||
});
|
||||
const name = care.displayName || fallbackName;
|
||||
const lines: string[] = [];
|
||||
|
||||
if (care.birthdayToday) {
|
||||
lines.push(t('login.care.birthday', { name }));
|
||||
} else if (care.tenureAnniversaryYears != null) {
|
||||
lines.push(
|
||||
t('login.care.tenureAnniversary', { name, years: care.tenureAnniversaryYears }),
|
||||
);
|
||||
} else if (care.holidayId) {
|
||||
lines.push(t(`login.care.holidayToast.${care.holidayId}`, { name }));
|
||||
} else if (care.birthdaySoon) {
|
||||
lines.push(t('login.care.birthdaySoon', { name }));
|
||||
}
|
||||
|
||||
if (care.longAbsence && care.daysSinceLastLogin != null) {
|
||||
lines.push(
|
||||
t('login.care.longAbsence', { name, days: care.daysSinceLastLogin }),
|
||||
);
|
||||
}
|
||||
|
||||
// 入职周年已单独祝福时,不再叠「并肩 N 天」
|
||||
if (
|
||||
care.tenureAnniversaryYears == null &&
|
||||
care.tenureDays != null &&
|
||||
care.tenureDays > 0
|
||||
) {
|
||||
const milestone =
|
||||
care.tenureDays === 1 ||
|
||||
care.tenureDays % 100 === 0 ||
|
||||
care.tenureDays % 365 === 0;
|
||||
lines.push(
|
||||
t(milestone ? 'login.care.tenureMilestone' : 'login.care.tenure', {
|
||||
name,
|
||||
days: care.tenureDays,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
if (care.lateNightOverwork) {
|
||||
lines.push(
|
||||
t('login.care.lateNightOverwork', { count: care.lateNightStreak }),
|
||||
);
|
||||
} else if (care.afterHours) {
|
||||
lines.push(t('login.care.afterHoursThanks'));
|
||||
}
|
||||
|
||||
if (!lines.length) {
|
||||
lines.push(t('login.care.defaultWelcome', { name }));
|
||||
}
|
||||
|
||||
let title = t('login.care.notifyTitle');
|
||||
if (care.birthdayToday) title = t('login.care.birthdayTitle');
|
||||
else if (care.tenureAnniversaryYears != null) title = t('login.care.anniversaryTitle');
|
||||
else if (care.holidayId) title = t('login.care.holidayTitle');
|
||||
else if (care.longAbsence) title = t('login.care.longAbsenceTitle');
|
||||
|
||||
return { title, lines };
|
||||
}
|
||||
|
||||
async function handleSubmit() {
|
||||
if (loading.value) return;
|
||||
|
||||
@ -487,25 +383,12 @@ async function handleSubmit() {
|
||||
}
|
||||
|
||||
Message.success(t('login.form.login.success'));
|
||||
const careToastPromise =
|
||||
props.loginType === OmsLoginType.User
|
||||
? buildAdminLoginCareToast(userInfoData)
|
||||
: null;
|
||||
await nextTick();
|
||||
await enterSystemAfterLogin({
|
||||
target: redirectParams,
|
||||
fallbackPath: defaultHomePath,
|
||||
playExit: () => loginExit?.playExitAnimation() ?? Promise.resolve(),
|
||||
});
|
||||
if (careToastPromise) {
|
||||
const toast = await careToastPromise;
|
||||
if (toast) {
|
||||
// 等进系统后再弹出,避免登录页卸载冲掉
|
||||
window.setTimeout(() => {
|
||||
openAdminCareToast(toast);
|
||||
}, 420);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
errorBox.value = t('login.form.login.failed');
|
||||
Message.error(t('login.form.login.failed'));
|
||||
@ -568,20 +451,22 @@ async function handleSubmit() {
|
||||
min-width: 0;
|
||||
height: 50px;
|
||||
overflow: hidden;
|
||||
background: rgba(248, 250, 252, 0.95);
|
||||
border: 1px solid #d8dee8;
|
||||
background: var(--app-acrylic-bg);
|
||||
border: 1px solid var(--app-acrylic-border);
|
||||
border-radius: 14px;
|
||||
backdrop-filter: var(--app-acrylic-blur);
|
||||
-webkit-backdrop-filter: var(--app-acrylic-blur);
|
||||
transition: border-color 0.2s, box-shadow 0.2s, background 0.2s;
|
||||
|
||||
&:hover {
|
||||
border-color: #14b8a6;
|
||||
background: #fff;
|
||||
border-color: var(--app-acrylic-border-strong);
|
||||
background: var(--app-acrylic-hover-bg);
|
||||
}
|
||||
|
||||
&:focus-within {
|
||||
border-color: #0f766e;
|
||||
box-shadow: 0 0 0 4px rgba(20, 184, 166, 0.15);
|
||||
background: #fff;
|
||||
border-color: var(--app-primary-color);
|
||||
box-shadow: 0 0 0 4px color-mix(in srgb, var(--app-primary-color) 15%, transparent);
|
||||
background: var(--app-acrylic-hover-bg);
|
||||
}
|
||||
}
|
||||
|
||||
@ -603,7 +488,7 @@ async function handleSubmit() {
|
||||
}
|
||||
|
||||
.nova-login__input-wrapper:focus-within .nova-login__prefix-icon {
|
||||
color: #0f766e;
|
||||
color: var(--app-primary-color);
|
||||
}
|
||||
|
||||
.nova-login__input {
|
||||
@ -629,16 +514,16 @@ async function handleSubmit() {
|
||||
&:-webkit-autofill:active {
|
||||
-webkit-text-fill-color: #111827;
|
||||
caret-color: #111827;
|
||||
box-shadow: 0 0 0 1000px #f8fafc inset;
|
||||
-webkit-box-shadow: 0 0 0 1000px #f8fafc inset;
|
||||
box-shadow: 0 0 0 1000px rgba(255, 255, 255, 0.42) inset;
|
||||
-webkit-box-shadow: 0 0 0 1000px rgba(255, 255, 255, 0.42) inset;
|
||||
transition: background-color 99999s ease-out 0s;
|
||||
}
|
||||
}
|
||||
|
||||
.nova-login__input-wrapper:focus-within .nova-login__input:-webkit-autofill,
|
||||
.nova-login__input-wrapper:hover .nova-login__input:-webkit-autofill {
|
||||
box-shadow: 0 0 0 1000px #fff inset;
|
||||
-webkit-box-shadow: 0 0 0 1000px #fff inset;
|
||||
box-shadow: 0 0 0 1000px rgba(255, 255, 255, 0.52) inset;
|
||||
-webkit-box-shadow: 0 0 0 1000px rgba(255, 255, 255, 0.52) inset;
|
||||
}
|
||||
|
||||
.nova-login__eye-toggle {
|
||||
@ -652,7 +537,7 @@ async function handleSubmit() {
|
||||
border: none;
|
||||
|
||||
&:hover {
|
||||
color: #0f766e;
|
||||
color: var(--app-primary-color);
|
||||
}
|
||||
}
|
||||
|
||||
@ -673,10 +558,12 @@ async function handleSubmit() {
|
||||
padding: 11px 14px;
|
||||
font-size: 13px;
|
||||
color: #b91c1c;
|
||||
background: #fff1f2;
|
||||
border: 1px solid #fecdd3;
|
||||
background: color-mix(in srgb, #fff1f2 72%, var(--app-acrylic-bg));
|
||||
border: 1px solid color-mix(in srgb, #fecdd3 80%, var(--app-acrylic-border));
|
||||
border-radius: 12px;
|
||||
margin-bottom: 16px;
|
||||
backdrop-filter: var(--app-acrylic-blur);
|
||||
-webkit-backdrop-filter: var(--app-acrylic-blur);
|
||||
}
|
||||
|
||||
.nova-login__submit {
|
||||
@ -686,19 +573,19 @@ async function handleSubmit() {
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
border-radius: 14px;
|
||||
background: #007979;
|
||||
border: none;
|
||||
background: var(--app-primary-color);
|
||||
border: 1px solid color-mix(in srgb, var(--app-primary-color) 80%, transparent);
|
||||
color: #fff;
|
||||
letter-spacing: 0.5px;
|
||||
cursor: pointer;
|
||||
box-shadow: 0 14px 26px rgba(0, 121, 121, 0.24);
|
||||
transition: transform 0.2s, box-shadow 0.2s, background 0.2s, opacity 0.2s;
|
||||
box-shadow: none;
|
||||
transition: transform 0.2s, background 0.2s, opacity 0.2s;
|
||||
font-family: inherit;
|
||||
|
||||
&:hover:not(:disabled) {
|
||||
transform: translateY(-1px);
|
||||
background: #008f8f;
|
||||
box-shadow: 0 16px 28px rgba(0, 121, 121, 0.32);
|
||||
background: var(--app-primary-color-hover);
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
&:active:not(:disabled) {
|
||||
@ -757,8 +644,17 @@ async function handleSubmit() {
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.arco-checkbox-checked .arco-checkbox-icon) {
|
||||
background-color: var(--app-primary-color);
|
||||
border-color: var(--app-primary-color);
|
||||
}
|
||||
|
||||
:deep(.arco-checkbox-icon-hover::before) {
|
||||
background-color: color-mix(in srgb, var(--app-primary-color) 12%, transparent);
|
||||
}
|
||||
|
||||
:deep(.agreement-line .arco-link) {
|
||||
color: #0f766e;
|
||||
color: var(--app-primary-color);
|
||||
font-size: inherit;
|
||||
font-weight: 600;
|
||||
text-decoration: none;
|
||||
@ -766,8 +662,8 @@ async function handleSubmit() {
|
||||
padding: 0 2px;
|
||||
|
||||
&:hover {
|
||||
color: #14b8a6;
|
||||
background-color: rgba(20, 184, 166, 0.1);
|
||||
color: var(--app-primary-color-hover);
|
||||
background-color: color-mix(in srgb, var(--app-primary-color) 10%, transparent);
|
||||
text-decoration: underline;
|
||||
text-underline-offset: 2px;
|
||||
}
|
||||
|
||||
@ -377,10 +377,12 @@ onBeforeUnmount(() => {
|
||||
width: 210px;
|
||||
height: 210px;
|
||||
padding: 10px;
|
||||
background: #fff;
|
||||
border: 1px solid #f0f0f0;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
||||
background: var(--app-acrylic-bg);
|
||||
border: 1px solid var(--app-acrylic-border);
|
||||
border-radius: 14px;
|
||||
box-shadow: none;
|
||||
backdrop-filter: var(--app-acrylic-blur);
|
||||
-webkit-backdrop-filter: var(--app-acrylic-blur);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
@ -503,17 +505,19 @@ onBeforeUnmount(() => {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
padding: 0;
|
||||
border: 1px solid #d8dee8;
|
||||
border: 1px solid var(--app-acrylic-border);
|
||||
border-radius: 50%;
|
||||
background: rgba(248, 250, 252, 0.95);
|
||||
background: var(--app-acrylic-bg);
|
||||
backdrop-filter: var(--app-acrylic-blur);
|
||||
-webkit-backdrop-filter: var(--app-acrylic-blur);
|
||||
color: rgba(26, 45, 74, 0.72);
|
||||
cursor: pointer;
|
||||
line-height: 1;
|
||||
transition: border-color 0.2s, background 0.2s, color 0.2s;
|
||||
|
||||
&:hover {
|
||||
border-color: var(--app-primary-color, #007979);
|
||||
background: #fff;
|
||||
border-color: var(--app-acrylic-border-strong);
|
||||
background: var(--app-acrylic-hover-bg);
|
||||
color: var(--app-primary-color, #007979);
|
||||
}
|
||||
}
|
||||
|
||||
@ -208,10 +208,13 @@ provide(LOGIN_EXIT_KEY, {
|
||||
);
|
||||
|
||||
if (containerRef.value) {
|
||||
const pageBg =
|
||||
getComputedStyle(document.documentElement).getPropertyValue('--app-page-bg').trim() ||
|
||||
'#f0f3f4';
|
||||
tl.to(
|
||||
containerRef.value,
|
||||
{
|
||||
backgroundColor: '#f0f3f4',
|
||||
backgroundColor: pageBg,
|
||||
duration: 0.28,
|
||||
ease: 'power1.inOut',
|
||||
},
|
||||
@ -292,7 +295,7 @@ onBeforeUnmount(() => {
|
||||
--login-split-top: 62%;
|
||||
--login-split-bottom: 38%;
|
||||
--login-left-bg: #142637;
|
||||
--login-right-bg: #fff;
|
||||
--login-right-bg: var(--app-page-bg, #f0f3f4);
|
||||
|
||||
box-sizing: border-box;
|
||||
height: 100dvh;
|
||||
@ -308,7 +311,7 @@ onBeforeUnmount(() => {
|
||||
--entry-btn-bg-hover: rgba(0, 0, 0, 0.09);
|
||||
--entry-account-color: rgba(26, 45, 74, 0.85);
|
||||
--login-left-bg: #e8eef5;
|
||||
--login-right-bg: #fff;
|
||||
--login-right-bg: var(--app-page-bg, #f0f3f4);
|
||||
}
|
||||
|
||||
.login-container--exit {
|
||||
@ -475,7 +478,7 @@ onBeforeUnmount(() => {
|
||||
.login-form-toolbar :deep(.contact-service-trigger--login:hover) {
|
||||
border: none !important;
|
||||
box-shadow: none !important;
|
||||
background: rgba(15, 23, 42, 0.06) !important;
|
||||
background: var(--app-acrylic-hover-bg) !important;
|
||||
color: var(--app-primary-color) !important;
|
||||
}
|
||||
|
||||
@ -492,19 +495,23 @@ onBeforeUnmount(() => {
|
||||
max-width: 430px;
|
||||
min-height: auto;
|
||||
padding: 51px 32px 45px;
|
||||
background: #fff;
|
||||
border: 1px solid rgba(148, 163, 184, 0.22);
|
||||
background: var(--app-acrylic-bg);
|
||||
border: 1px solid var(--app-acrylic-border);
|
||||
border-radius: 24px;
|
||||
box-shadow: 0 16px 40px rgba(30, 41, 59, 0.08);
|
||||
box-shadow: none;
|
||||
backdrop-filter: var(--app-acrylic-blur);
|
||||
-webkit-backdrop-filter: var(--app-acrylic-blur);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
opacity: 1;
|
||||
transform: none;
|
||||
transition: border-color 0.2s ease, background 0.2s ease;
|
||||
}
|
||||
|
||||
.login-form:hover {
|
||||
border-color: rgba(148, 163, 184, 0.32);
|
||||
box-shadow: 0 20px 44px rgba(30, 41, 59, 0.1);
|
||||
border-color: var(--app-acrylic-border-strong);
|
||||
background: var(--app-acrylic-hover-bg);
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.login-form--ready {
|
||||
@ -524,11 +531,15 @@ onBeforeUnmount(() => {
|
||||
}
|
||||
|
||||
.login-form :deep(.account-entry-btn) {
|
||||
background: rgba(248, 250, 252, 0.95);
|
||||
background: var(--app-acrylic-bg);
|
||||
border-color: var(--app-acrylic-border);
|
||||
backdrop-filter: var(--app-acrylic-blur);
|
||||
-webkit-backdrop-filter: var(--app-acrylic-blur);
|
||||
}
|
||||
|
||||
.login-form :deep(.account-entry-btn:hover) {
|
||||
background: #fff;
|
||||
background: var(--app-acrylic-hover-bg);
|
||||
border-color: var(--app-acrylic-border-strong);
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1024px) {
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, reactive, ref } from 'vue'
|
||||
import { computed, onMounted, reactive, ref, watch } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useRoute } from 'vue-router'
|
||||
import dayjs from 'dayjs'
|
||||
import { Modal } from '@arco-design/web-vue'
|
||||
import { IconInfoCircle } from '@arco-design/web-vue/es/icon'
|
||||
@ -37,6 +38,7 @@ import {
|
||||
defineOptions({ name: 'OrderPickupOrder' })
|
||||
|
||||
const { t } = useI18n()
|
||||
const route = useRoute()
|
||||
const { tableContainerRef, tableHeight } = useTableContainerHeight()
|
||||
const {
|
||||
isAdmin,
|
||||
@ -47,7 +49,6 @@ const {
|
||||
|
||||
const keyword = ref('')
|
||||
const STATUS_TAB_ALL = 'all'
|
||||
const statusFilter = ref(STATUS_TAB_ALL)
|
||||
const listLoading = ref(false)
|
||||
const tableRows = ref<any[]>([])
|
||||
const currentPage = ref(1)
|
||||
@ -102,6 +103,19 @@ const statusTabs = [
|
||||
{ value: 'cancelled', labelKey: 'order.pickupOrder.status.cancelled' },
|
||||
]
|
||||
|
||||
const validStatusSet = new Set(statusTabs.map((tab) => tab.value))
|
||||
|
||||
function resolveStatusFromQuery(): string {
|
||||
const raw = route.query.status
|
||||
const status = Array.isArray(raw) ? raw[0] : raw
|
||||
if (typeof status === 'string' && validStatusSet.has(status)) {
|
||||
return status
|
||||
}
|
||||
return STATUS_TAB_ALL
|
||||
}
|
||||
|
||||
const statusFilter = ref(resolveStatusFromQuery())
|
||||
|
||||
|
||||
function canShowPay(row: { status?: string; payStatus?: string; receivableAmount?: unknown; Status?: string; PayStatus?: string; ReceivableAmount?: unknown }) {
|
||||
const status = String(row?.status ?? row?.Status ?? '')
|
||||
@ -667,6 +681,15 @@ const detailOrderColumns = computed(() => [
|
||||
const detailStatus = computed(() => String(detail.value?.status ?? detail.value?.Status ?? ''))
|
||||
const detailPayStatus = computed(() => String(detail.value?.payStatus ?? detail.value?.PayStatus ?? ''))
|
||||
|
||||
watch(
|
||||
() => route.query.status,
|
||||
() => {
|
||||
statusFilter.value = resolveStatusFromQuery()
|
||||
currentPage.value = 1
|
||||
void loadList()
|
||||
},
|
||||
)
|
||||
|
||||
onMounted(async () => {
|
||||
await ensureCarrierServiceOptions()
|
||||
void loadList()
|
||||
|
||||
@ -225,7 +225,7 @@ const noticeItems = [
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
@guide-accent: var(--app-primary-color, #1d7281);
|
||||
@guide-accent: var(--app-primary-color, #054f74);
|
||||
@guide-accent-soft: color-mix(in srgb, @guide-accent 12%, white);
|
||||
@guide-accent-border: color-mix(in srgb, @guide-accent 35%, white);
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user