diff --git a/.cursor/README.md b/.cursor/README.md
new file mode 100644
index 0000000..54683de
--- /dev/null
+++ b/.cursor/README.md
@@ -0,0 +1,15 @@
+# Agent Rules
+
+The files in this directory are shared project instructions, not Cursor-only notes.
+
+Codex and other coding agents must read and follow the matching rules through the repository-level `AGENTS.md` entry point.
+
+## Always Applied
+
+- `skills/react-best-practices/SKILL.md`
+- `rules/changelog-writing.mdc`
+
+## Conditional Skills
+
+- `skills/frontend-design/SKILL.md`: frontend UI creation, styling, visual refinement, component/page/app design work.
+- `skills/figma-ui-implementation/SKILL.md`: Figma-linked implementation or pixel-level design restoration.
diff --git a/AGENTS.md b/AGENTS.md
new file mode 100644
index 0000000..0787610
--- /dev/null
+++ b/AGENTS.md
@@ -0,0 +1,26 @@
+# Codex Project Instructions
+
+This repository keeps project-specific agent rules in `.cursor/`. These rules are mandatory for Codex work in this repo.
+
+## Mandatory Rule Loading
+
+Before making code, UI, release-note, or documentation changes, inspect the relevant `.cursor` instructions and follow them as binding project policy.
+
+Always apply:
+- `.cursor/skills/react-best-practices/SKILL.md`
+- `.cursor/rules/changelog-writing.mdc`
+
+Apply for frontend UI work:
+- `.cursor/skills/frontend-design/SKILL.md`
+
+Apply for Figma design implementation:
+- `.cursor/skills/figma-ui-implementation/SKILL.md`
+
+## Enforcement
+
+- Treat the matching `.cursor` instructions as higher-priority project conventions.
+- If a task matches multiple files, read all matching files before editing.
+- Do not silently ignore a matching rule. If a rule conflicts with user instructions or existing code constraints, explain the conflict and choose the safest path.
+- For Figma-linked UI implementation, read the Figma design context first, then implement with Flex/Grid where possible and verify visual fidelity before finishing.
+- For React changes, follow the React, TypeScript, state, effect, styling, i18n, and maintainability rules in `react-best-practices`.
+- For changelog or version-update requests, inspect real repository changes first and write only from observed changes.
diff --git a/src/assets/images/layout/nav/characters.png b/src/assets/images/layout/nav/characters.png
new file mode 100644
index 0000000..4d9f050
Binary files /dev/null and b/src/assets/images/layout/nav/characters.png differ
diff --git a/src/assets/images/layout/nav/characters_active.png b/src/assets/images/layout/nav/characters_active.png
new file mode 100644
index 0000000..3fb9c51
Binary files /dev/null and b/src/assets/images/layout/nav/characters_active.png differ
diff --git a/src/assets/images/layout/nav/collapse.png b/src/assets/images/layout/nav/collapse.png
new file mode 100644
index 0000000..9c62c7a
Binary files /dev/null and b/src/assets/images/layout/nav/collapse.png differ
diff --git a/src/assets/images/layout/nav/create.png b/src/assets/images/layout/nav/create.png
new file mode 100644
index 0000000..95e91cf
Binary files /dev/null and b/src/assets/images/layout/nav/create.png differ
diff --git a/src/assets/images/layout/nav/create_active.png b/src/assets/images/layout/nav/create_active.png
new file mode 100644
index 0000000..794c324
Binary files /dev/null and b/src/assets/images/layout/nav/create_active.png differ
diff --git a/src/assets/images/layout/nav/explore.png b/src/assets/images/layout/nav/explore.png
new file mode 100644
index 0000000..efc4364
Binary files /dev/null and b/src/assets/images/layout/nav/explore.png differ
diff --git a/src/assets/images/layout/nav/explore_active.png b/src/assets/images/layout/nav/explore_active.png
new file mode 100644
index 0000000..5a7a3a5
Binary files /dev/null and b/src/assets/images/layout/nav/explore_active.png differ
diff --git a/src/assets/images/layout/nav/more.png b/src/assets/images/layout/nav/more.png
new file mode 100644
index 0000000..9e0a90c
Binary files /dev/null and b/src/assets/images/layout/nav/more.png differ
diff --git a/src/assets/images/layout/nav/popi-logo.png b/src/assets/images/layout/nav/popi-logo.png
new file mode 100644
index 0000000..4336b0e
Binary files /dev/null and b/src/assets/images/layout/nav/popi-logo.png differ
diff --git a/src/assets/images/layout/nav/popi-tv.png b/src/assets/images/layout/nav/popi-tv.png
new file mode 100644
index 0000000..13548d2
Binary files /dev/null and b/src/assets/images/layout/nav/popi-tv.png differ
diff --git a/src/assets/images/layout/nav/popi-tv_active.png b/src/assets/images/layout/nav/popi-tv_active.png
new file mode 100644
index 0000000..cb8bda2
Binary files /dev/null and b/src/assets/images/layout/nav/popi-tv_active.png differ
diff --git a/src/assets/images/layout/nav/profile.png b/src/assets/images/layout/nav/profile.png
new file mode 100644
index 0000000..178e87e
Binary files /dev/null and b/src/assets/images/layout/nav/profile.png differ
diff --git a/src/assets/images/layout/nav/profile_active.png b/src/assets/images/layout/nav/profile_active.png
new file mode 100644
index 0000000..c26cca8
Binary files /dev/null and b/src/assets/images/layout/nav/profile_active.png differ
diff --git a/src/assets/images/layout/nav/subscribe.png b/src/assets/images/layout/nav/subscribe.png
new file mode 100644
index 0000000..647c421
Binary files /dev/null and b/src/assets/images/layout/nav/subscribe.png differ
diff --git a/src/assets/images/layout/nav/subscribe_active.png b/src/assets/images/layout/nav/subscribe_active.png
new file mode 100644
index 0000000..41d7932
Binary files /dev/null and b/src/assets/images/layout/nav/subscribe_active.png differ
diff --git a/src/assets/images/layout/nav/top-help.png b/src/assets/images/layout/nav/top-help.png
new file mode 100644
index 0000000..d6ece4e
Binary files /dev/null and b/src/assets/images/layout/nav/top-help.png differ
diff --git a/src/assets/images/layout/nav/top-language.png b/src/assets/images/layout/nav/top-language.png
new file mode 100644
index 0000000..2fd7f85
Binary files /dev/null and b/src/assets/images/layout/nav/top-language.png differ
diff --git a/src/assets/images/layout/nav/top-skill.png b/src/assets/images/layout/nav/top-skill.png
new file mode 100644
index 0000000..2e39a74
Binary files /dev/null and b/src/assets/images/layout/nav/top-skill.png differ
diff --git a/src/assets/images/layout/nav/top-weixin.png b/src/assets/images/layout/nav/top-weixin.png
new file mode 100644
index 0000000..e51c865
Binary files /dev/null and b/src/assets/images/layout/nav/top-weixin.png differ
diff --git a/src/assets/images/layout/nav/voice.png b/src/assets/images/layout/nav/voice.png
new file mode 100644
index 0000000..859242e
Binary files /dev/null and b/src/assets/images/layout/nav/voice.png differ
diff --git a/src/assets/images/layout/nav/voice_active.png b/src/assets/images/layout/nav/voice_active.png
new file mode 100644
index 0000000..6e3b817
Binary files /dev/null and b/src/assets/images/layout/nav/voice_active.png differ
diff --git a/src/assets/images/menu/canvas.svg b/src/assets/images/menu/canvas.svg
new file mode 100644
index 0000000..3e73e65
--- /dev/null
+++ b/src/assets/images/menu/canvas.svg
@@ -0,0 +1,6 @@
+
diff --git a/src/assets/images/menu/characters.svg b/src/assets/images/menu/characters.svg
new file mode 100644
index 0000000..468f69c
--- /dev/null
+++ b/src/assets/images/menu/characters.svg
@@ -0,0 +1,8 @@
+
diff --git a/src/assets/images/menu/create.svg b/src/assets/images/menu/create.svg
new file mode 100644
index 0000000..199c07a
--- /dev/null
+++ b/src/assets/images/menu/create.svg
@@ -0,0 +1,8 @@
+
diff --git a/src/assets/images/menu/explore-group.svg b/src/assets/images/menu/explore-group.svg
new file mode 100644
index 0000000..fc17e66
--- /dev/null
+++ b/src/assets/images/menu/explore-group.svg
@@ -0,0 +1,6 @@
+
diff --git a/src/assets/images/menu/explore-vector.svg b/src/assets/images/menu/explore-vector.svg
new file mode 100644
index 0000000..3381de9
--- /dev/null
+++ b/src/assets/images/menu/explore-vector.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/assets/images/menu/more.svg b/src/assets/images/menu/more.svg
new file mode 100644
index 0000000..34f3b35
--- /dev/null
+++ b/src/assets/images/menu/more.svg
@@ -0,0 +1,12 @@
+
diff --git a/src/layouts/Layout/Layout.scss b/src/layouts/Layout/Layout.scss
index 74b2da7..c977b8b 100644
--- a/src/layouts/Layout/Layout.scss
+++ b/src/layouts/Layout/Layout.scss
@@ -1,18 +1,16 @@
// =============================================================================
// Layout 视口缩放(逻辑见 useViewportScale.ts)
// DOM:layoutShellViewport → layoutShellScaleSpacer → layoutShellScaleInner → layoutShell
-// 变量:--layout-scale(JS 写入) / --layout-chrome-top(顶栏 + promo 总高)
+// 变量:--layout-scale(JS 写入) / --layout-chrome-top(供页面高度计算)
// =============================================================================
-// 1. 视口容器 & 缩放包装层(未缩放时的基础样式)
.layoutShellViewport {
width: 100%;
height: 100vh;
- overflow-x: hidden;
+ overflow: hidden;
box-sizing: border-box;
scrollbar-width: none;
-ms-overflow-style: none;
- overflow-y: hidden;
}
.layoutShellScaleSpacer {
@@ -24,18 +22,15 @@
box-sizing: border-box;
}
-// 3. 缩放激活态(host 挂 layoutShellViewport_scaled;--layout-chrome-top 由下方 .layoutShell 提供)
.layoutShellViewport_scaled {
.layoutShellScaleInner {
width: 1920px;
}
- /* Chromium / Safari:zoom 重排文字,通常比 transform 栅格化更清晰 */
&.layoutShellViewport_scaled--zoom .layoutShellScaleInner {
zoom: var(--layout-scale, 1);
}
- /* Firefox 等:回退 transform */
&.layoutShellViewport_scaled--transform .layoutShellScaleInner {
transform-origin: top left;
transform: scale(var(--layout-scale, 1));
@@ -43,77 +38,61 @@
-moz-osx-font-smoothing: grayscale;
}
- /* 缩放后 100vh 视觉变矮,用未缩放坐标系下的 vh / scale 补足 */
.layoutShell {
min-height: calc(100vh / var(--layout-scale, 1));
-
- .layoutShell__body,
- .layoutShell__body .layoutShell__sidebar {
- height: calc((100vh - var(--layout-chrome-top)) / var(--layout-scale, 1));
- }
}
}
-// 4. 主壳层 & 顶栏高度 token
.layoutShell {
- --layout-chrome-top: 89px; /* 33px promo + 56px topbar */
+ --layout-promo-height: 33px;
+ --layout-sidebar-width: 110px;
+ --layout-topbar-height: 87px;
+ --layout-chrome-top: calc(var(--layout-promo-height) + var(--layout-topbar-height));
+ --layout-sidebar-transition: 0.26s cubic-bezier(0.4, 0, 0.2, 1);
+
+ position: relative;
+ width: 100%;
+ min-height: 100vh;
+ overflow: hidden;
+ font-family: "Source Han Sans CN", "Noto Sans SC", system-ui, sans-serif;
+ background: #ffffff;
&.layoutShell_promoDismissed {
- --layout-chrome-top: 56px;
+ --layout-promo-height: 0px;
}
- min-height: 100vh;
- overflow-x: hidden;
- font-family: "Source Han Sans CN", "Noto Sans SC", system-ui, sans-serif;
- background-color: #f9f9f9;
+ &.layoutShell_sidebarCollapsed {
+ --layout-sidebar-width: 0px;
+ }
.layoutShell__promoBar {
+ position: absolute;
+ inset: 0 0 auto;
+ z-index: 40;
height: 33px;
- flex-shrink: 0;
display: flex;
align-items: center;
justify-content: center;
- background: linear-gradient(90deg, #8a7fae 0%, #5c3bc7 50%, #8a7fae 100%);
- cursor: pointer;
overflow: hidden;
box-sizing: border-box;
- position: relative;
-
- .layoutShell__promoClose {
- position: absolute;
- right: 16px;
- top: 7px;
- width: 16px;
- height: 16px;
- padding: 0;
- border: 0;
- background: transparent;
- display: block;
- cursor: pointer;
-
- img {
- width: 16px;
- height: 16px;
- object-fit: cover;
- display: block;
- }
- }
+ background: linear-gradient(90deg, #f2def9 0%, #7f61d5 50%, #f2def9 100%);
+ cursor: pointer;
}
.layoutShell__promoBarInner {
display: flex;
align-items: center;
justify-content: center;
- gap: 36px;
+ gap: 43px;
max-width: 100%;
- padding: 0 16px;
+ padding: 0 48px;
box-sizing: border-box;
}
.layoutShell__promoSegment {
display: flex;
align-items: center;
- gap: 6px;
+ gap: 5px;
flex-shrink: 0;
}
@@ -123,1119 +102,1077 @@
justify-content: center;
min-width: 39px;
height: 16px;
- padding: 0 4px;
+ padding: 0 5px;
box-sizing: border-box;
border-radius: 5px 0 5px 0;
- background-color: rgba(255, 255, 255, 0.3);
+ background: rgba(255, 255, 255, 0.3);
+ color: #ffffff;
font-size: 12px;
font-weight: 500;
- line-height: 1;
- color: #ffffff;
- flex-shrink: 0;
+ line-height: 16px;
+ white-space: nowrap;
}
.layoutShell__promoText {
margin: 0;
- font-size: 14px;
- line-height: 21px;
- font-weight: 500;
color: #ffffff;
+ font-size: 14px;
+ font-weight: 400;
+ line-height: 20px;
white-space: nowrap;
- flex-shrink: 0;
&_heavy {
- font-weight: 800;
+ font-weight: 500;
}
&_seedance {
display: flex;
align-items: center;
- gap: 0;
}
}
- @media (max-width: 1200px) {
- .layoutShell__promoBarInner {
- gap: 20px;
+ .layoutShell__promoClose {
+ position: absolute;
+ top: 8px;
+ right: 16px;
+ width: 16px;
+ height: 16px;
+ padding: 0;
+ border: 0;
+ background: transparent;
+ cursor: pointer;
+
+ img {
+ width: 16px;
+ height: 16px;
+ display: block;
+ object-fit: contain;
}
}
- @media (max-width: 960px) {
- .layoutShell__promoBar {
- display: none;
+ .layoutShell__sidebarCollapse {
+ position: absolute;
+ top: calc(var(--layout-promo-height) + 27px);
+ left: max(16px, calc(var(--layout-sidebar-width) - 18px));
+ z-index: 45;
+ width: 36px;
+ height: 36px;
+ padding: 10px;
+ border: 0;
+ border-radius: 24px;
+ background: #ffffff;
+ color: #333333;
+ cursor: pointer;
+ box-sizing: border-box;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ gap: 3px;
+ transition:
+ top var(--layout-sidebar-transition),
+ left var(--layout-sidebar-transition),
+ width var(--layout-sidebar-transition),
+ height var(--layout-sidebar-transition),
+ padding var(--layout-sidebar-transition),
+ transform var(--layout-sidebar-transition),
+ box-shadow var(--layout-sidebar-transition);
+
+ .layoutShell__sidebarCollapseText {
+ max-height: 0;
+ opacity: 0;
+ overflow: hidden;
+ color: #333333;
+ font-size: 14px;
+ font-weight: 400;
+ line-height: 20px;
+ text-align: center;
+ white-space: pre-line;
+ transition:
+ max-height var(--layout-sidebar-transition),
+ opacity 0.14s ease;
+ }
+
+ img {
+ width: 16px;
+ height: 16px;
+ display: block;
+ object-fit: contain;
+ transition: transform var(--layout-sidebar-transition);
}
+ }
- --layout-chrome-top: 56px;
+ &.layoutShell_sidebarCollapsed .layoutShell__sidebarCollapse {
+ top: calc(var(--layout-promo-height) + ((100vh - var(--layout-promo-height)) / 2));
+ left: 0;
+ width: 36px;
+ height: 91px;
+ padding: 16px 10px;
+ transform: translateY(-50%);
+
+ .layoutShell__sidebarCollapseText {
+ max-height: 40px;
+ opacity: 1;
+ transition:
+ max-height var(--layout-sidebar-transition),
+ opacity 0.16s ease 0.1s;
+ }
+ }
+
+ &.layoutShell_sidebarCollapsed .layoutShell__sidebarCollapse img {
+ transform: rotate(270deg);
}
.layoutShell__topbar {
- height: 56px;
+ position: absolute;
+ top: var(--layout-promo-height);
+ left: var(--layout-sidebar-width);
+ right: 0;
+ z-index: 30;
+ height: var(--layout-topbar-height);
+ background: #f1f1f1;
+ box-sizing: border-box;
+ transition: left var(--layout-sidebar-transition);
+ }
+
+ .layoutShell__topbarRight {
+ position: absolute;
+ top: 20px;
+ right: 20px;
+ height: 47px;
display: flex;
align-items: center;
- justify-content: space-between;
- padding: 0 16px 0 0;
+ gap: 10px;
+ }
+
+ .layoutShell__topbarTools {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ }
+
+ .goToSkillHub {
+ width: 88px;
+ height: 31px;
+ padding: 7px;
+ border: 1px solid #e7e7e7;
+ border-radius: 15.5px;
+ background: #ffffff;
box-sizing: border-box;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 12px;
+ color: #7f61d5;
+ font-family: inherit;
+ cursor: pointer;
+ }
- .layoutShell__topbarLeft {
- width: 160px;
+ .layoutShell__topbarSkillIcon {
+ width: 17px;
+ height: 17px;
+ flex-shrink: 0;
+ display: block;
+ object-fit: contain;
+ }
- .layoutShell__logo {
- width: 83px;
- height: 21px;
- display: block;
- cursor: pointer;
- margin-left: 18px;
- }
+ .layoutShell__topbarSkillText {
+ font-size: 12px;
+ font-weight: 400;
+ line-height: 17px;
+ color: #7f61d5;
+ }
- .layoutShell__collapse {
- border: none;
- background: transparent;
- padding: 0;
- cursor: pointer;
- display: flex;
- align-items: center;
- justify-content: center;
- flex-shrink: 0;
-
- img {
- width: 18px;
- height: 18px;
- display: block;
- transition: transform 0.2s ease;
- }
- }
+ .layoutShell__topbarTool {
+ position: relative;
+ width: 31px;
+ height: 31px;
+ flex-shrink: 0;
+ }
+
+ .layoutShell__topbarIconBtn {
+ width: 31px;
+ height: 31px;
+ padding: 0;
+ border: 0;
+ border-radius: 15.5px;
+ background: transparent;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ cursor: pointer;
+
+ img {
+ width: 31px;
+ height: 31px;
+ display: block;
+ object-fit: contain;
}
+ }
- .layoutShell__topbarRight {
- display: flex;
- align-items: center;
- gap: 8px;
+ .layoutShell__topbarIconBtn_active {
+ background: #ffffff;
+ }
- .layoutShell__aliceMeetupTool {
- position: relative;
+ .layoutShell__topbarServiceQrFloat {
+ position: absolute;
+ top: calc(100% + 8px);
+ right: 0;
+ z-index: 1300;
+ width: 140px;
+ padding: 10px;
+ border: 1px solid #e8e8e8;
+ border-radius: 8px;
+ background: #ffffff;
+ box-shadow: 2px 4px 15px rgba(0, 0, 0, 0.12);
+ box-sizing: border-box;
+ }
- &:hover,
- &:focus-within {
- .layoutShell__aliceMeetupFloat {
- opacity: 1;
- visibility: visible;
- pointer-events: auto;
- }
- }
+ .layoutShell__topbarServiceQrImage {
+ width: 120px;
+ height: 120px;
+ display: block;
+ margin: 0 auto;
+ object-fit: contain;
+ }
- .layoutShell__aliceMeetupFloat {
- position: absolute;
- top: calc(100% + 8px);
- left: 50%;
- transform: translateX(-50%);
- width: 140px;
- padding: 10px;
- border-radius: 8px;
- background: #fff;
- border: 1px solid #e8e8e8;
- box-shadow: 2px 4px 15px rgb(0 0 0 / 12%);
- box-sizing: border-box;
- z-index: 1300;
- opacity: 0;
- visibility: hidden;
- pointer-events: none;
- transition:
- opacity 0.15s ease,
- visibility 0.15s ease;
- }
+ .layoutShell__topbarServiceQrTip {
+ margin: 8px 0 0;
+ color: #333333;
+ font-size: 12px;
+ line-height: 1.4;
+ text-align: center;
+ }
- .layoutShell__aliceMeetupQr {
- width: 120px;
- height: 120px;
- display: block;
- margin: 0 auto;
- object-fit: contain;
- }
+ .layoutShell__languageDropdown {
+ min-width: 128px;
+ padding: 4px;
+ border: 1px solid #e5e6eb;
+ border-radius: 9px;
+ background: #ffffff;
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.08);
+ box-sizing: border-box;
+ }
- .layoutShell__aliceMeetupFloatText {
- margin: 8px 0 0;
- font-size: 12px;
- line-height: 1.4;
- color: #333;
- text-align: center;
- }
- }
+ .layoutShell__languageDropdownItem {
+ width: 100%;
+ height: 32px;
+ padding: 0 8px;
+ border: 0;
+ border-radius: 6px;
+ background: transparent;
+ color: #333333;
+ font-size: 13px;
+ text-align: left;
+ cursor: pointer;
+ }
- .layoutShell__aliceMeetup {
- display: flex;
- align-items: center;
- justify-content: center;
- gap: 4px;
- padding: 6px;
- border: none;
- border-radius: 100px;
- background: #f9f9f9;
- cursor: pointer;
- font-family: inherit;
-
- .layoutShell__aliceMeetupIcon {
- width: 20px;
- height: 20px;
- flex-shrink: 0;
- display: block;
- border-radius: 50%;
- object-fit: cover;
- }
+ .layoutShell__languageDropdownItem:hover {
+ background: #f5f5f5;
+ }
- .layoutShell__aliceMeetupText {
- font-size: 14px;
- font-weight: 500;
- line-height: normal;
- color: #5c3bc7;
- white-space: nowrap;
- }
- }
+ .layoutShell__languageDropdownItem.layoutShell__languageDropdownItem_active {
+ color: #7f61d5;
+ font-weight: 500;
+ }
- .layoutShell__topbarTools {
- display: flex;
- align-items: center;
- gap: 8px;
- }
+ .layoutShell__userPoints {
+ height: 31px;
+ min-width: 110px;
+ padding: 0 16px;
+ border: 1px solid #e7e7e7;
+ border-radius: 15.5px;
+ background: #ffffff;
+ box-sizing: border-box;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 4px;
+ cursor: pointer;
+ }
- .goToSkillHub {
- width: 68px;
- height: 33px;
- display: flex;
- align-items: center;
- justify-content: center;
- border-radius: 9px;
- background-color: #ffffff;
- cursor: pointer;
-
- img {
- width: 9px;
- height: 9px;
- display: block;
- margin-right: 3px;
- }
+ .layoutShell__userPointsValue,
+ .layoutShell__userPointsLabel {
+ color: #7b7b7b;
+ font-size: 12px;
+ font-weight: 400;
+ line-height: 17px;
+ }
- font-size: 12px;
- font-weight: 700;
- color: #666666;
- }
+ .layoutShell__avatarMenu {
+ position: relative;
+ width: 47px;
+ height: 47px;
+ flex-shrink: 0;
+ }
- .layoutShell__topbarTool {
- position: relative;
-
- .layoutShell__topbarIconBtn {
- width: 33px;
- height: 33px;
- border: none;
- background: transparent;
- padding: 0;
- cursor: pointer;
- border-radius: 9px;
-
- img {
- width: 20px;
- height: 20px;
- display: block;
- }
- }
+ .layoutShell__avatar,
+ .layoutShell__avatar_guest {
+ width: 47px;
+ height: 47px;
+ padding: 0;
+ border: 0;
+ border-radius: 50%;
+ background: transparent;
+ display: block;
+ cursor: pointer;
+ }
- .layoutShell__topbarIconBtn_active {
- background: #f0f0f0;
- }
+ .layoutShell__avatarBg {
+ width: 47px;
+ height: 47px;
+ border-radius: 50%;
+ display: block;
+ object-fit: cover;
+ }
- .layoutShell__topbarServiceQrFloat {
- position: absolute;
- top: calc(100% + 8px);
- left: 0;
- width: 140px;
- padding: 10px;
- border-radius: 8px;
- background: #fff;
- border: 1px solid #e8e8e8;
- box-shadow: 2px 4px 15px rgb(0 0 0 / 12%);
- box-sizing: border-box;
- z-index: 1300;
- }
+ .layoutShell__avatarDropdown {
+ position: absolute;
+ top: 55px;
+ right: 0;
+ z-index: 1300;
+ width: 317px;
+ padding: 10px;
+ border-radius: 9px;
+ background: #ffffff;
+ box-shadow: 0 2px 15px rgba(51, 51, 51, 0.2);
+ box-sizing: border-box;
+ display: flex;
+ flex-direction: column;
+ gap: 5px;
+ }
- .layoutShell__topbarServiceQrImage {
- width: 120px;
- height: 120px;
- display: block;
- margin: 0 auto;
- object-fit: contain;
- }
+ .layoutShell__avatarUserCard {
+ width: 297px;
+ min-height: 67px;
+ padding: 13px 17px;
+ border-radius: 5px;
+ background: #ffffff;
+ display: flex;
+ align-items: flex-start;
+ gap: 10px;
+ box-sizing: border-box;
+ }
- .layoutShell__topbarServiceQrTip {
- margin: 8px 0 0;
- font-size: 12px;
- line-height: 1.4;
- color: #333;
- text-align: center;
- }
+ .layoutShell__avatarUserAvatar {
+ width: 38px;
+ height: 38px;
+ border-radius: 19px;
+ flex-shrink: 0;
+ display: block;
+ object-fit: cover;
+ }
- /* 语言菜单:由 Arco Dropdown 定位,此处只保留卡片样式 */
- .layoutShell__languageDropdown {
- min-width: 128px;
- background: #fff;
- border: 1px solid #e5e6eb;
- border-radius: 9px;
- box-shadow: 0 8px 24px rgba(0, 0, 0, 0.08);
- padding: 4px;
- box-sizing: border-box;
- }
+ .layoutShell__avatarUserMain {
+ min-width: 0;
+ padding-top: 2px;
+ display: flex;
+ flex-direction: column;
+ }
- .layoutShell__languageDropdownItem {
- width: 100%;
- height: 32px;
- border: none;
- background: transparent;
- border-radius: 6px;
- font-size: 13px;
- color: #333;
- cursor: pointer;
- text-align: left;
- padding: 0 8px;
- }
+ .layoutShell__avatarUserName {
+ overflow: hidden;
+ color: #333333;
+ font-size: 16px;
+ font-weight: 500;
+ line-height: 16px;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
- .layoutShell__languageDropdownItem:hover {
- background: #f5f5f5;
- }
+ .layoutShell__avatarUserMeta {
+ display: flex;
+ align-items: center;
+ }
- .layoutShell__languageDropdownItem.layoutShell__languageDropdownItem_active {
- color: #165dff;
- font-weight: 500;
- }
- }
+ .layoutShell__avatarUserId {
+ color: #999999;
+ font-size: 12px;
+ line-height: 26px;
+ }
- .layoutShell__userPoints {
- border-radius: 9px;
- padding: 0 10px;
- height: 33px;
- display: flex;
- align-items: center;
- gap: 4px;
- background-color: #ffffff;
- cursor: pointer;
-
- .layoutShell__userPointsIcon {
- width: 18px;
- height: 18px;
- object-fit: cover;
- display: block;
- }
+ .layoutShell__avatarCopyId {
+ width: 15px;
+ height: 15px;
+ margin-left: 2px;
+ padding: 0;
+ border: 0;
+ background: transparent;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ cursor: pointer;
- .layoutShell__userPointsValue {
- font-size: 14px;
- font-weight: 500;
- line-height: 14px;
- color: #333333;
- }
+ img {
+ width: 15px;
+ height: 15px;
+ display: block;
+ }
+ }
- .layoutShell__userPointsLabel {
- font-size: 12px;
- font-weight: 400;
- line-height: 12px;
- color: #666666;
- }
- }
+ .layoutShell__avatarMemberRow {
+ width: 297px;
+ height: 39px;
+ padding: 0 17px;
+ border: 0;
+ border-radius: 5px;
+ background: linear-gradient(90deg, #f0f0f0 0%, #d6ceef 100%);
+ display: flex;
+ align-items: center;
+ text-align: left;
+ cursor: pointer;
+ }
- .layoutShell__avatarMenu {
- position: relative;
-
- .layoutShell__avatar {
- width: 38px;
- height: 38px;
- position: relative;
- border: none;
- background: transparent;
- padding: 0;
- cursor: pointer;
-
- .layoutShell__avatarBg {
- width: 38px;
- height: 38px;
- display: block;
- border-radius: 19px;
- object-fit: cover;
- }
- }
+ .layoutShell__avatarMemberLabel {
+ margin-right: auto;
+ color: #666666;
+ font-size: 12px;
+ line-height: 12px;
+ }
- .layoutShell__avatarDropdown {
- position: absolute;
- top: 44px;
- right: 0;
- width: 317px;
- background: #fff;
- border-radius: 9px;
- box-shadow: 0 2px 15px rgba(51, 51, 51, 0.2);
- padding: 10px;
- box-sizing: border-box;
- z-index: 20;
- display: flex;
- flex-direction: column;
- gap: 5px;
-
- .layoutShell__avatarUserCard {
- width: 297px;
- height: 67px;
- border-radius: 5px;
- background: #fff;
- padding: 13px 17px;
- display: flex;
- align-items: flex-start;
- box-sizing: border-box;
- gap: 10px;
- }
-
- .layoutShell__avatarUserAvatar {
- width: 38px;
- height: 38px;
- border-radius: 19px;
- display: block;
- object-fit: cover;
- }
-
- .layoutShell__avatarUserMain {
- display: flex;
- flex-direction: column;
- min-width: 0;
- padding-top: 2px;
- }
-
- .layoutShell__avatarUserName {
- font-size: 16px;
- line-height: 16px;
- font-weight: 500;
- color: #333;
- }
-
- .layoutShell__avatarUserMeta {
- display: flex;
- align-items: center;
- }
-
- .layoutShell__avatarUserId {
- font-size: 12px;
- line-height: 26px;
- color: #999;
- }
-
- .layoutShell__avatarCopyId {
- width: 15px;
- height: 15px;
- margin-left: 2px;
- border: none;
- background: transparent;
- padding: 0;
- cursor: pointer;
- display: inline-flex;
- align-items: center;
- justify-content: center;
-
- img {
- width: 15px;
- height: 15px;
- display: block;
- }
- }
-
- .layoutShell__avatarMemberRow {
- width: 297px;
- height: 39px;
- border: none;
- border-radius: 5px;
- background: linear-gradient(90deg, #f0f0f0 0%, #d6ceef 100%);
- padding: 0 17px;
- display: flex;
- align-items: center;
- text-align: left;
- cursor: pointer;
- }
-
- .layoutShell__avatarMemberLabel {
- font-size: 12px;
- line-height: 12px;
- color: #666;
- margin-right: auto;
- }
-
- .layoutShell__avatarMemberAction {
- font-size: 12px;
- line-height: 12px;
- font-weight: 500;
- color: #333;
- }
-
- .layoutShell__avatarChevron {
- width: 15px;
- height: 15px;
- margin-left: 1px;
- display: block;
- }
-
- .layoutShell__avatarPointsCard {
- width: 297px;
- height: 63px;
- border-radius: 5px;
- border: 1px solid #f8f8f8;
- background: #f0f0f0;
- padding: 8px 17px;
- display: flex;
- align-items: flex-start;
- justify-content: space-between;
- box-sizing: border-box;
- }
-
- .layoutShell__avatarPointsLeft {
- display: flex;
- flex-direction: column;
- }
-
- .layoutShell__avatarPointsTop {
- display: flex;
- align-items: center;
- }
-
- .layoutShell__avatarPointsValue {
- font-size: 16px;
- line-height: 16px;
- font-weight: 500;
- color: #333;
- }
-
- .layoutShell__avatarPointsIcon {
- width: 15px;
- height: 15px;
- margin-left: 0;
- display: block;
- }
-
- .layoutShell__avatarPointsDetail {
- margin-top: 7px;
- border: none;
- background: transparent;
- padding: 0;
- display: flex;
- align-items: center;
- text-align: left;
- font-size: 12px;
- line-height: 12px;
- color: #666;
- cursor: pointer;
- }
-
- .layoutShell__avatarArrow {
- width: 15px;
- height: 15px;
- margin-left: 0;
- display: block;
- }
-
- .layoutShell__avatarRechargeBtn {
- margin-top: 1px;
- width: 61px;
- height: 19px;
- border: none;
- border-radius: 5px;
- background: linear-gradient(90deg, #8a7fae 0%, #5c3bc7 100%);
- color: #fff;
- font-size: 12px;
- line-height: 12px;
- font-weight: 500;
- padding: 0;
- cursor: pointer;
- }
-
- .layoutShell__avatarDropdownItem {
- width: 297px;
- height: 37px;
- border: none;
- border-radius: 5px;
- background: #fff;
- padding: 0 12px;
- display: flex;
- align-items: center;
- gap: 8px;
- text-align: left;
- cursor: pointer;
- color: #666;
- font-size: 14px;
- line-height: 14px;
- }
-
- .layoutShell__avatarDropdownIcon {
- width: 18px;
- height: 18px;
- display: block;
- }
-
- .layoutShell__avatarDropdownItem:hover {
- background: #f7f7f7;
- }
- }
- }
+ .layoutShell__avatarMemberAction {
+ color: #333333;
+ font-size: 12px;
+ font-weight: 500;
+ line-height: 12px;
+ }
- .layoutShell__loginBtn {
- height: 33px;
- border-radius: 9px;
- background-color: #ffffff;
- display: flex;
- align-items: center;
- justify-content: center;
- padding: 0 10px;
- color: #333333;
- cursor: pointer;
- }
- }
+ .layoutShell__avatarChevron {
+ width: 15px;
+ height: 15px;
+ margin-left: 1px;
+ display: block;
}
- .layoutShell__body {
- position: relative;
+ .layoutShell__avatarPointsCard {
+ width: 297px;
+ height: 63px;
+ padding: 8px 17px;
+ border: 1px solid #f8f8f8;
+ border-radius: 5px;
+ background: #f0f0f0;
display: flex;
- height: calc(100vh - var(--layout-chrome-top));
+ align-items: flex-start;
+ justify-content: space-between;
box-sizing: border-box;
+ }
- .layoutShell__sidebar {
- position: relative;
- width: 160px;
- height: calc(100vh - var(--layout-chrome-top));
- padding: 0 8px 16px;
- display: flex;
- flex-direction: column;
- box-sizing: border-box;
- position: relative;
- z-index: 21; // 高于 modalMount 的 20
+ .layoutShell__avatarPointsLeft {
+ display: flex;
+ flex-direction: column;
+ }
- &.layoutShell__sidebar_footerPopoverOpen {
- z-index: 100;
- overflow: visible;
- }
+ .layoutShell__avatarPointsTop {
+ display: flex;
+ align-items: center;
+ }
- .layoutShell__appDownload {
- width: 100%;
- padding: 8px 0 ;
- box-sizing: border-box;
-
- .layoutShell__appDownloadTitle {
- display: flex;
- align-items: center;
- gap: 8px;
- width: 100%;
- height: 37px;
- margin: 0;
- padding: 0 10px;
- box-sizing: border-box;
- border: none;
- border-radius: 9px;
- background-color: #f9f9f9;
- cursor: pointer;
- font-family: inherit;
- text-align: left;
-
- }
+ .layoutShell__avatarPointsValue {
+ color: #333333;
+ font-size: 16px;
+ font-weight: 500;
+ line-height: 16px;
+ }
- .layoutShell__appDownloadTitleIcon {
- width: 18px;
- height: 18px;
- flex-shrink: 0;
- display: block;
- object-fit: cover;
- }
+ .layoutShell__avatarPointsIcon {
+ width: 15px;
+ height: 15px;
+ display: block;
+ }
- .layoutShell__appDownloadTitleText {
- font-size: 12px;
- font-weight: 400;
- line-height: normal;
- color: #333333;
- white-space: nowrap;
- }
+ .layoutShell__avatarPointsDetail {
+ margin-top: 7px;
+ padding: 0;
+ border: 0;
+ background: transparent;
+ display: flex;
+ align-items: center;
+ color: #666666;
+ font-size: 12px;
+ line-height: 12px;
+ text-align: left;
+ cursor: pointer;
+ }
- }
+ .layoutShell__avatarArrow {
+ width: 15px;
+ height: 15px;
+ display: block;
+ }
- .layoutShell__sideNav {
- flex: 1;
- width: 100%;
- padding-top: 7px;
- display: flex;
- flex-direction: column;
- gap: 10px;
-
- .layoutShell__sideItem {
- height: 37px;
- width: 100%;
- border-radius: 9px;
- background-color: #f9f9f9;
- display: flex;
- align-items: center;
- gap: 8px;
- text-decoration: none;
- box-sizing: border-box;
- padding: 0 10px;
-
- .layoutShell__sideIcon {
- width: 18px;
- height: 18px;
- object-fit: cover;
- display: block;
- }
-
- .layoutShell__sideText {
- font-size: 14px;
- font-weight: 500;
- line-height: 14px;
- color: #333333;
- white-space: nowrap;
- }
-
- .layoutShell__sideBadge {
- flex-shrink: 0;
- display: inline-flex;
- align-items: center;
- justify-content: center;
- min-width: 34px;
- height: 16px;
- margin-left: auto;
- padding: 2px 3px;
- box-sizing: border-box;
- border-radius: 24px;
- background-color: #d6ceef;
- font-size: 12px;
- font-weight: 500;
- line-height: 1;
- color: #ffffff;
- white-space: nowrap;
- }
- }
+ .layoutShell__avatarRechargeBtn {
+ width: 61px;
+ height: 19px;
+ margin-top: 1px;
+ padding: 0;
+ border: 0;
+ border-radius: 5px;
+ background: linear-gradient(90deg, #8a7fae 0%, #5c3bc7 100%);
+ color: #ffffff;
+ font-size: 12px;
+ font-weight: 500;
+ line-height: 12px;
+ cursor: pointer;
+ }
- .layoutShell__sideItem.layoutShell__sideItem_active {
- background-color: #f0f0f0;
- }
- }
+ .layoutShell__avatarDropdownItem {
+ width: 297px;
+ height: 37px;
+ padding: 0 12px;
+ border: 0;
+ border-radius: 5px;
+ background: #ffffff;
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ color: #666666;
+ font-family: inherit;
+ font-size: 14px;
+ line-height: 14px;
+ text-align: left;
+ cursor: pointer;
+ }
- .layoutShell__aliceLiveClassTool {
- flex-shrink: 0;
- align-self: flex-start;
- margin-left: -8px;
- margin-bottom: 10px;
- z-index: 16;
-
- .layoutShell__aliceLiveClass {
- width: 212px;
- height: auto;
- height: 47px;
- padding: 5px;
- box-sizing: border-box;
- border: none;
- border-radius: 0 24px 24px 0;
- background: linear-gradient(270deg, #ebe5ff 0%, #f0f0f0 100%);
- box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
- display: flex;
- align-items: center;
- gap: 5px;
- cursor: pointer;
- font-family: inherit;
- }
+ .layoutShell__avatarDropdownItem:hover {
+ background: #f7f7f7;
+ }
- .layoutShell__aliceLiveClass:focus-visible {
- outline: 2px solid #5c3bc7;
- outline-offset: 2px;
- }
+ .layoutShell__avatarDropdownIcon {
+ width: 18px;
+ height: 18px;
+ display: block;
+ }
- .layoutShell__aliceLiveClassIcon {
- width: 37px;
- height: 37px;
- flex-shrink: 0;
- display: block;
- object-fit: cover;
- }
+ .layoutShell__body {
+ position: absolute;
+ top: var(--layout-promo-height);
+ right: 0;
+ bottom: 0;
+ left: 0;
+ z-index: 20;
+ background: #ffffff;
+ }
- .layoutShell__aliceLiveClassText {
- flex-shrink: 0;
- font-size: 18px;
- font-weight: 500;
- line-height: normal;
- color: #333333;
- white-space: nowrap;
- }
+ .layoutShell__sidebar {
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ left: 0;
+ z-index: 35;
+ width: 110px;
+ padding: 27px 20px;
+ background: #ffffff;
+ box-sizing: border-box;
+ display: flex;
+ flex-direction: column;
+ transition:
+ opacity var(--layout-sidebar-transition),
+ transform var(--layout-sidebar-transition);
+ }
- .layoutShell__aliceLiveClassArrow {
- width: 25px;
- height: 25px;
- flex-shrink: 0;
- display: block;
- object-fit: contain;
- transform: rotate(-90deg);
- }
- }
+ &.layoutShell_sidebarCollapsed .layoutShell__sidebar {
+ transform: translateX(-110px);
+ opacity: 0;
+ pointer-events: none;
+ }
- .layoutShell__footer {
- position: relative;
- margin-top: auto;
- padding-top: 8px;
- padding-bottom: 44px;
-
- .layoutShell__footerNav {
- display: flex;
- flex-direction: column;
- gap: 10px;
- width: 100%;
-
- .layoutShell__sideItem {
- height: 37px;
- width: 100%;
- border-radius: 9px;
- background-color: #f9f9f9;
- display: flex;
- align-items: center;
- gap: 8px;
- text-decoration: none;
- box-sizing: border-box;
- padding: 0 10px;
- border: none;
- cursor: pointer;
- font-family: inherit;
- text-align: left;
-
- .layoutShell__sideIcon {
- width: 18px;
- height: 18px;
- object-fit: cover;
- display: block;
- }
-
- .layoutShell__sideText {
- font-size: 14px;
- font-weight: 500;
- line-height: 14px;
- color: #333333;
- white-space: nowrap;
- }
- }
-
- .layoutShell__sideItem.layoutShell__sideItem_active {
- background-color: #f0f0f0;
- }
- }
+ .layoutShell__sidebar_footerPopoverOpen {
+ z-index: 100;
+ overflow: visible;
+ }
- .layoutShell__footerPopoverTrigger {
- position: absolute;
- left: 5px;
- bottom: 0;
- }
+ .layoutShell__brand {
+ width: 70px;
+ height: 36px;
+ padding: 5px 15px 4px;
+ border: 0;
+ border-radius: 24px;
+ background: #ffffff;
+ box-sizing: border-box;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ cursor: pointer;
+ }
- .layoutShell__footerMenuBtn {
- display: block;
- width: 32px;
- min-width: auto;
- height: 32px;
- padding: 0;
- line-height: 32px;
- border: none;
- outline: none;
- background: transparent;
- color: inherit;
- cursor: pointer;
-
- img {
- display: block;
- width: 14px;
- height: 10px;
- margin: 0 auto;
- }
- }
+ .layoutShell__brandLogo {
+ width: 40px;
+ height: 27px;
+ display: block;
+ object-fit: contain;
+ }
- .layoutShell__footerPopoverPanel {
- position: absolute;
- left: 0;
- bottom: 30px;
- margin-left: 8px;
- width: 235px;
- padding: 5px;
- box-sizing: border-box;
- background: #ffffff;
- border-radius: 8px;
- box-shadow: 2px 4px 15px rgba(0, 0, 0, 0.12);
- border-left: 1px solid #e8e8e8;
- z-index: 1200;
- display: flex;
- flex-direction: column;
- overflow: visible;
- }
+ .layoutShell__sideNav {
+ width: 70px;
+ height: 390px;
+ margin-top: 50px;
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+ }
- .layoutShell__footerPopoverLink {
- width: 100%;
- height: 40px;
- display: flex;
- align-items: center;
- font-size: 12px;
- color: #666666;
- padding-left: 10px;
- box-sizing: border-box;
- text-decoration: none;
- border: none;
- background: transparent;
- cursor: pointer;
- font-family: inherit;
- text-align: left;
- }
+ .layoutShell__sideItem {
+ position: relative;
+ width: 70px;
+ height: 70px;
+ padding: 10px 16px;
+ border: 0;
+ border-radius: 24px;
+ background: #ffffff;
+ box-sizing: border-box;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ gap: 3px;
+ color: #333333;
+ font-family: inherit;
+ text-align: center;
+ text-decoration: none;
+ cursor: pointer;
+ }
- .layoutShell__footerPopoverLink:hover {
- background: #f0f0f0;
- }
+ .layoutShell__sideItem_active {
+ background: #f0f4f9;
+ }
- .layoutShell__footerPopoverMeta {
- padding: 16px 0 0 16px;
- font-size: 10px;
- color: #999999;
- line-height: 1.5;
- text-align: left;
- margin-bottom: 20px;
- }
- }
+ .layoutShell__sideIcon {
+ position: relative;
+ width: 25px;
+ height: 25px;
+ flex-shrink: 0;
+ display: block;
+ }
+
+ img.layoutShell__sideIcon {
+ object-fit: contain;
+ }
+
+ .layoutShell__sideIconImage {
+ position: absolute;
+ inset: 0;
+ width: 25px;
+ height: 25px;
+ display: block;
+ object-fit: contain;
+ }
+
+ .layoutShell__sideIconImage_active {
+ opacity: 0;
+ }
+
+ .layoutShell__sideItem_active .layoutShell__sideIconImage_default {
+ opacity: 0;
+ }
+
+ .layoutShell__sideItem_active .layoutShell__sideIconImage_active {
+ opacity: 1;
+ }
+
+ .layoutShell__sideText {
+ min-width: 38px;
+ color: #333333;
+ font-size: 14px;
+ font-weight: 500;
+ line-height: 20px;
+ white-space: nowrap;
+ }
+
+ .layoutShell__sideItem_active .layoutShell__sideText {
+ color: #6f47f5;
+ }
+
+ .layoutShell__sideBadge {
+ position: absolute;
+ top: 10px;
+ right: -17px;
+ width: 27px;
+ height: 12px;
+ border: 1px solid #b59ce6;
+ border-radius: 7.5px;
+ background: linear-gradient(90deg, #f2def9 0%, #7f61d5 100%);
+ color: #ffffff;
+ box-sizing: border-box;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 6px;
+ font-weight: 700;
+ line-height: 7px;
+ }
+
+ .layoutShell__footer {
+ width: 70px;
+ height: 205px;
+ margin-top: auto;
+ position: relative;
+ display: flex;
+ flex-direction: column;
+ }
+
+ .layoutShell__footerNav {
+ width: 70px;
+ display: flex;
+ flex-direction: column;
+ gap: 20px;
+ }
+
+ .layoutShell__footerPopoverTrigger {
+ position: relative;
+ width: 25px;
+ height: 25px;
+ margin: 20px auto 0;
+ }
+
+ .layoutShell__footerMenuBtn {
+ width: 25px;
+ height: 25px;
+ padding: 0;
+ border: 0;
+ background: #f9f9f9;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ cursor: pointer;
+
+ img {
+ width: 19.5px;
+ height: 14px;
+ display: block;
+ object-fit: contain;
}
+ }
- /* 内容区视口壳:不滚动;滚动与全屏弹窗分层(方案 A) */
- .layoutShell__contentFrame {
- position: relative;
- flex: 1;
- min-width: 0;
+ .layoutShell__footerPopoverPanel {
+ position: absolute;
+ left: 20px;
+ bottom: 30px;
+ z-index: 1200;
+ width: 235px;
+ padding: 5px;
+ border-left: 1px solid #e8e8e8;
+ border-radius: 8px;
+ background: #ffffff;
+ box-shadow: 2px 4px 15px rgba(0, 0, 0, 0.12);
+ box-sizing: border-box;
+ display: flex;
+ flex-direction: column;
+ }
+
+ .layoutShell__footerPopoverLink {
+ width: 100%;
+ height: 40px;
+ padding-left: 10px;
+ border: 0;
+ background: transparent;
+ color: #666666;
+ font-family: inherit;
+ font-size: 12px;
+ text-align: left;
+ text-decoration: none;
+ cursor: pointer;
+ box-sizing: border-box;
+ }
+
+ .layoutShell__footerPopoverLink:hover {
+ background: #f0f0f0;
+ }
+
+ .layoutShell__footerPopoverMeta {
+ margin-bottom: 20px;
+ padding: 16px 0 0 16px;
+ color: #999999;
+ font-size: 10px;
+ line-height: 1.5;
+ text-align: left;
+ }
+
+ .layoutShell__contentFrame {
+ position: absolute;
+ top: var(--layout-topbar-height);
+ right: 0;
+ bottom: 0;
+ left: var(--layout-sidebar-width);
+ z-index: 20;
+ min-width: 0;
+ min-height: 0;
+ overflow: hidden;
+ background: #f2f2f2;
+ display: flex;
+ flex-direction: column;
+ transition: left var(--layout-sidebar-transition);
+ }
+
+ .layoutShell__contentScroll {
+ position: relative;
+ flex: 1 1 0;
+ min-width: 0;
+ min-height: 0;
+ overflow: auto;
+ scrollbar-width: none;
+ -ms-overflow-style: none;
+
+ > .createPage {
+ height: 100%;
+ max-height: 100%;
min-height: 0;
- margin-right: 14px;
- margin-bottom: 14px;
- border-radius: 20px;
+ }
+
+ &.layoutShell__contentScroll_scrollLocked {
overflow: hidden;
+ }
+
+ &::-webkit-scrollbar {
+ display: none;
+ }
+ }
+
+ .layoutShell__modalMount {
+ position: absolute;
+ inset: 0;
+ z-index: 20;
+ overflow: hidden;
+ pointer-events: none;
+
+ .arco-modal-mask {
+ position: absolute;
+ inset: 0;
+ width: 100%;
+ height: 100%;
+ max-width: 100%;
+ max-height: 100%;
+ box-sizing: border-box;
+ pointer-events: auto;
+ }
+
+ .arco-modal-wrapper {
+ position: absolute;
+ inset: 0;
+ width: 100%;
+ height: 100%;
+ max-width: 100%;
+ max-height: 100%;
+ box-sizing: border-box;
display: flex;
flex-direction: column;
+ align-items: stretch;
+ pointer-events: auto;
+ background: #f9f9f9;
+ overflow: hidden;
+ overscroll-behavior: contain;
+ }
- > .layoutShell__contentScroll {
- position: relative;
- flex: 1 1 0;
- min-height: 0;
- min-width: 0;
- overflow-y: auto;
- overflow-x: hidden;
- scrollbar-width: none;
- -ms-overflow-style: none;
-
- /* Create 页自管内部滚动,占满滚动层高度 */
- > .createPage {
- height: 100%;
- max-height: 100%;
- min-height: 0;
- }
-
- &.layoutShell__contentScroll_scrollLocked {
- overflow: hidden;
- }
+ .arco-modal-wrapper.layoutFullscreenDetailModalWrap {
+ overflow: hidden !important;
+ scrollbar-width: none;
+ -ms-overflow-style: none;
- &::-webkit-scrollbar {
- display: none;
- }
+ &::-webkit-scrollbar {
+ display: none;
}
- /* 全屏详情弹窗遮罩:与滚动层兄弟,absolute 铺满视口壳,不受 scrollTop 影响 */
- > .layoutShell__modalMount {
- position: absolute;
- inset: 0;
- z-index: 20;
- pointer-events: none;
- border-radius: inherit;
- overflow: hidden;
-
- .arco-modal-mask {
- position: absolute;
- inset: 0;
- width: 100%;
- height: 100%;
- max-width: 100%;
- max-height: 100%;
- box-sizing: border-box;
- pointer-events: auto;
- }
-
- .arco-modal-wrapper {
- position: absolute;
- inset: 0;
- width: 100%;
- height: 100%;
- max-width: 100%;
- max-height: 100%;
- box-sizing: border-box;
- display: flex;
- flex-direction: column;
- align-items: stretch;
- pointer-events: auto;
- background: #f9f9f9;
- overflow: hidden;
- overflow-x: hidden;
- overscroll-behavior: contain;
+ .character-detail-arco-modal,
+ .asset-details-arco-modal {
+ &.zoomModal-appear,
+ &.zoomModal-enter,
+ &.zoomModal-appear-active,
+ &.zoomModal-enter-active {
+ opacity: 1 !important;
+ transform: none !important;
+ transition: none !important;
}
+ }
+ }
- /* 全屏详情弹窗:覆盖 Arco 默认 overflow:auto,并关闭 zoom 进入动画避免首帧滚动条闪烁 */
- .arco-modal-wrapper.layoutFullscreenDetailModalWrap {
- overflow: hidden !important;
- scrollbar-width: none;
- -ms-overflow-style: none;
-
- &::-webkit-scrollbar {
- display: none;
- }
-
- .character-detail-arco-modal,
- .asset-details-arco-modal {
- &.zoomModal-appear,
- &.zoomModal-enter,
- &.zoomModal-appear-active,
- &.zoomModal-enter-active {
- opacity: 1 !important;
- transform: none !important;
- transition: none !important;
- }
- }
- }
+ .arco-modal-wrapper > * {
+ flex: 1 1 0;
+ min-width: 0;
+ min-height: 0;
+ display: flex;
+ flex-direction: column;
+ }
- .arco-modal-wrapper > * {
- flex: 1 1 0;
- min-height: 0;
- min-width: 0;
- display: flex;
- flex-direction: column;
- }
+ .arco-modal-wrapper .asset-details-arco-modal,
+ .arco-modal-wrapper .character-detail-arco-modal {
+ flex: 1 1 0;
+ min-width: 0;
+ min-height: 0;
+ max-width: 100%;
+ align-self: stretch;
+ overflow: hidden;
+ box-sizing: border-box;
+ display: flex;
+ flex-direction: column;
+ }
- .arco-modal-wrapper .asset-details-arco-modal,
- .arco-modal-wrapper .character-detail-arco-modal {
- flex: 1 1 0;
- min-height: 0;
- min-width: 0;
- max-width: 100%;
- align-self: stretch;
- display: flex;
- flex-direction: column;
- box-sizing: border-box;
- overflow: hidden;
- overflow-x: hidden;
- }
+ .arco-modal-wrapper .asset-details-arco-modal.arco-modal > *,
+ .arco-modal-wrapper .character-detail-arco-modal.arco-modal > * {
+ flex: 1 1 0;
+ min-width: 0;
+ min-height: 0;
+ display: flex;
+ flex-direction: column;
+ align-items: stretch;
+ }
- .arco-modal-wrapper .asset-details-arco-modal.arco-modal > *,
- .arco-modal-wrapper .character-detail-arco-modal.arco-modal > * {
- flex: 1 1 0;
- min-height: 0;
- min-width: 0;
- display: flex;
- flex-direction: column;
- align-items: stretch;
- }
+ .arco-modal-wrapper .asset-details-arco-modal .arco-modal-content,
+ .arco-modal-wrapper .character-detail-arco-modal .arco-modal-content {
+ flex: 1 1 0;
+ min-height: 0;
+ overflow: hidden;
+ display: flex;
+ flex-direction: column;
+ }
- .arco-modal-wrapper .asset-details-arco-modal .arco-modal-content,
- .arco-modal-wrapper .character-detail-arco-modal .arco-modal-content {
- flex: 1 1 0;
- min-height: 0;
- overflow: hidden;
- display: flex;
- flex-direction: column;
- }
+ .arco-modal-wrapper .asset-details-arco-modal .arco-modal-body,
+ .arco-modal-wrapper .character-detail-arco-modal .arco-modal-body {
+ flex: 1 1 0;
+ min-height: 0;
+ overflow: hidden;
+ }
+ }
- .arco-modal-wrapper .asset-details-arco-modal .arco-modal-body,
- .arco-modal-wrapper .character-detail-arco-modal .arco-modal-body {
- flex: 1 1 0;
- min-height: 0;
- overflow: hidden;
- }
- }
+ .layoutShell__floatingComposerMount {
+ position: fixed;
+ left: 50%;
+ bottom: 30px;
+ z-index: 25;
+ width: 1040px;
+ max-width: calc(100vw - 40px);
+ transform: translateX(-50%);
+
+ .layoutShell__composer {
+ width: 100%;
+ max-width: 100%;
}
- .layoutShell__floatingComposerMount {
- width: 1040px;
- max-width: calc(100vw - 40px);
- position: fixed;
- left: 50%;
- transform: translateX(-50%);
- bottom: 30px;
- z-index: 15;
-
- .layoutShell__composer {
- width: 100%;
- max-width: 100%;
- }
+ &:has(.layoutShell__composer_collapsed) {
+ width: 360px;
+ max-width: min(360px, calc(100vw - 40px));
+ }
+ }
+}
- &:has(.layoutShell__composer_collapsed) {
- width: 360px;
- max-width: min(360px, calc(100vw - 40px));
- }
+@media (max-width: 1200px) {
+ .layoutShell {
+ .layoutShell__promoBarInner {
+ gap: 24px;
}
}
}
-.layoutShell.layoutShell_sidebarCollapsed {
- .layoutShell__topbarLeft {
- width: 64px;
- justify-content: center;
+@media (max-width: 960px) {
+ .layoutShell {
+ --layout-promo-height: 0px;
- .layoutShell__collapse img {
- transform: rotate(180deg);
+ .layoutShell__promoBar {
+ display: none;
}
}
+}
+
+@media (max-width: 768px) {
+ .layoutShell {
+ --layout-sidebar-width: 76px;
+ --layout-topbar-height: 70px;
- .layoutShell__body {
.layoutShell__sidebar {
- width: 64px;
- padding-left: 8px;
- padding-right: 8px;
+ width: 76px;
+ padding: 14px 8px;
}
- .layoutShell__appDownload {
- display: none;
+ &.layoutShell_sidebarCollapsed {
+ --layout-sidebar-width: 0px;
+ }
+
+ &.layoutShell_sidebarCollapsed .layoutShell__sidebar {
+ transform: translateX(-76px);
+ }
+
+ .layoutShell__sidebarCollapse {
+ top: 17px;
+ left: max(10px, calc(var(--layout-sidebar-width) - 18px));
+ }
+
+ &.layoutShell_sidebarCollapsed .layoutShell__sidebarCollapse {
+ top: calc(var(--layout-promo-height) + ((100vh - var(--layout-promo-height)) / 2));
+ left: 0;
+ }
+
+ .layoutShell__brand,
+ .layoutShell__sideNav,
+ .layoutShell__footer,
+ .layoutShell__footerNav {
+ width: 60px;
+ }
+
+ .layoutShell__brand {
+ padding: 4px 10px;
}
.layoutShell__sideNav {
- .layoutShell__sideItem {
- justify-content: center;
- padding: 0 8px;
- gap: 0;
+ height: auto;
+ margin-top: 34px;
+ gap: 8px;
+ }
- .layoutShell__sideText {
- display: none;
- }
+ .layoutShell__sideItem {
+ width: 60px;
+ height: 60px;
+ padding: 8px 10px;
+ border-radius: 20px;
+ }
- .layoutShell__sideBadge {
- display: none;
- }
- }
+ .layoutShell__sideText {
+ font-size: 12px;
+ line-height: 18px;
}
.layoutShell__footer {
- .layoutShell__footerNav {
- .layoutShell__sideItem {
- justify-content: center;
- padding: 0 8px;
- gap: 0;
-
- .layoutShell__sideText {
- display: none;
- }
- }
- }
+ height: auto;
}
- }
-}
-@media (max-width: 768px) {
- .layoutShell {
- .layoutShell__topbar {
- .layoutShell__topbarRight {
- .layoutShell__avatarMenu {
- .layoutShell__avatarDropdown {
- right: -8px;
- width: min(317px, calc(100vw - 16px));
-
- .layoutShell__avatarUserCard,
- .layoutShell__avatarMemberRow,
- .layoutShell__avatarPointsCard,
- .layoutShell__avatarDropdownItem {
- width: 100%;
- }
- }
- }
- }
+ .layoutShell__footerNav {
+ gap: 10px;
+ }
+
+ .layoutShell__footerPopoverTrigger {
+ margin-top: 10px;
+ }
+
+ .layoutShell__topbarRight {
+ top: 14px;
+ right: 10px;
+ gap: 6px;
+ }
+
+ .layoutShell__topbarTools {
+ gap: 6px;
+ }
+
+ .goToSkillHub,
+ .layoutShell__userPoints {
+ display: none;
+ }
+
+ .layoutShell__avatarMenu,
+ .layoutShell__avatar,
+ .layoutShell__avatar_guest,
+ .layoutShell__avatarBg {
+ width: 38px;
+ height: 38px;
+ }
+
+ .layoutShell__avatarDropdown {
+ right: -8px;
+ width: min(317px, calc(100vw - 16px));
+ }
+
+ .layoutShell__avatarUserCard,
+ .layoutShell__avatarMemberRow,
+ .layoutShell__avatarPointsCard,
+ .layoutShell__avatarDropdownItem {
+ width: 100%;
}
}
}
diff --git a/src/layouts/Layout/Layout.tsx b/src/layouts/Layout/Layout.tsx
index c51be78..fe16f5a 100644
--- a/src/layouts/Layout/Layout.tsx
+++ b/src/layouts/Layout/Layout.tsx
@@ -1,7 +1,6 @@
import type { MouseEvent as ReactMouseEvent, ReactNode } from "react";
import { useCallback, useEffect, useRef, useState } from "react";
-import { createPortal } from "react-dom";
-import { Dropdown, Message, Tooltip } from "@arco-design/web-react";
+import { Dropdown, Tooltip } from "@arco-design/web-react";
import { Link, useLocation, useNavigate } from "react-router-dom";
import { useTranslation } from "react-i18next";
import { useShallow } from "zustand/react/shallow";
@@ -12,14 +11,12 @@ import { hasPendingExploreTemplateTabIntent } from "@/store/useExploreFeedTabSto
import { isLayoutContentScrollLocked, isScrollEventFromLayoutModalMount, isScrollEventInLayoutContentScroll, setLayoutContentModalMount, setLayoutContentScrollRoot } from "@/layouts/layoutContentModalMount";
import { changeLanguage, type AppLanguage, SUPPORTED_LANGUAGES } from "@/i18n";
import { closeAssetDetailsModal, closeCharacterDetailModal, openGatewayApikeyDialog, openLoginDialog, openPointsDetailDialog, openPointsRechargeDialog } from "@/dialog";
-import { AliceLiveClassDialogOverlay } from "@/dialog/alice-live-class";
-import { AppMacDownloadDialogOverlay } from "@/dialog/app-download-mac";
import { canAccessRoute, guardRouteAccess } from "@/router/routeGuard";
import { useComposerStore } from "@/store/useComposerStore";
import { useUserStore } from "@/store/useUserStore";
import { getToken, removeToken } from "@/utils/auth";
import { openLatestPrivacyPolicyLink, PRIVACY_POLICY_KEY_AI_USE_NOTICE, PRIVACY_POLICY_KEY_PRIVACY_POLICY, PRIVACY_POLICY_KEY_USAGE_TUTORIAL, PRIVACY_POLICY_KEY_USER_TERMS } from "@/utils/openLatestPrivacyPolicyLink";
-import { getAppLatest, getParamConfig, logout } from "@/api/api";
+import { getParamConfig, logout } from "@/api/api";
import { useCopyText } from "@/hooks/useCopyText";
import { useLayoutViewportScale } from "@/hooks/useViewportScale";
import { memberLevelMap } from "@/constants";
@@ -34,44 +31,78 @@ interface LayoutSideNavItem {
id: string;
labelKey: string;
iconSrc: string;
+ activeIconSrc?: string;
to: string;
- badge?: string;
+ badgeKey?: string;
+}
+
+interface LayoutSideNavIconProps {
+ item: LayoutSideNavItem;
}
const CANVAS_BASE_URL = import.meta.env.VITE_CANVAS_URL as string | undefined;
-// 侧栏图标(顺序:探索 → 角色 → 声音 → 创造 → Popi TV)
+const LAYOUT_NAV_ASSETS = {
+ logo: new URL("@/assets/images/layout/nav/popi-logo.png", import.meta.url).href,
+ collapse: new URL("@/assets/images/layout/nav/collapse.png", import.meta.url).href,
+ explore: new URL("@/assets/images/layout/nav/explore.png", import.meta.url).href,
+ exploreActive: new URL("@/assets/images/layout/nav/explore_active.png", import.meta.url).href,
+ characters: new URL("@/assets/images/layout/nav/characters.png", import.meta.url).href,
+ charactersActive: new URL("@/assets/images/layout/nav/characters_active.png", import.meta.url).href,
+ create: new URL("@/assets/images/layout/nav/create.png", import.meta.url).href,
+ createActive: new URL("@/assets/images/layout/nav/create_active.png", import.meta.url).href,
+ canvas: new URL("@/assets/images/layout/nav/popi-tv.png", import.meta.url).href,
+ canvasActive: new URL("@/assets/images/layout/nav/popi-tv_active.png", import.meta.url).href,
+ voice: new URL("@/assets/images/layout/nav/voice.png", import.meta.url).href,
+ voiceActive: new URL("@/assets/images/layout/nav/voice_active.png", import.meta.url).href,
+ subscribe: new URL("@/assets/images/layout/nav/subscribe.png", import.meta.url).href,
+ subscribeActive: new URL("@/assets/images/layout/nav/subscribe_active.png", import.meta.url).href,
+ profile: new URL("@/assets/images/layout/nav/profile.png", import.meta.url).href,
+ profileActive: new URL("@/assets/images/layout/nav/profile_active.png", import.meta.url).href,
+ more: new URL("@/assets/images/layout/nav/more.png", import.meta.url).href,
+ topHelp: new URL("@/assets/images/layout/nav/top-language.png", import.meta.url).href,
+ topLanguage: new URL("@/assets/images/layout/nav/top-help.png", import.meta.url).href,
+ topService: new URL("@/assets/images/layout/nav/top-weixin.png", import.meta.url).href,
+ topSkill: new URL("@/assets/images/layout/nav/top-skill.png", import.meta.url).href,
+} as const;
+
+// 侧栏图标顺序与 2026 7月官网 Figma 设计保持一致。
const LAYOUT_SIDE_NAV_ITEMS: readonly LayoutSideNavItem[] = [
{
id: "explore",
labelKey: "layout.nav.explore",
- iconSrc: new URL("@/assets/images/menu/explore.png", import.meta.url).href,
+ iconSrc: LAYOUT_NAV_ASSETS.explore,
+ activeIconSrc: LAYOUT_NAV_ASSETS.exploreActive,
to: ROUTES.EXPLORE,
},
{
id: "characters",
labelKey: "layout.nav.characters",
- iconSrc: new URL("@/assets/images/menu/characters.png", import.meta.url).href,
+ iconSrc: LAYOUT_NAV_ASSETS.characters,
+ activeIconSrc: LAYOUT_NAV_ASSETS.charactersActive,
to: ROUTES.CHARACTERS,
},
- {
- id: "voice",
- labelKey: "layout.nav.voice",
- iconSrc: new URL("@/assets/images/menu/voice.png", import.meta.url).href,
- to: ROUTES.VOICE,
- },
{
id: "create",
labelKey: "layout.nav.create",
- iconSrc: new URL("@/assets/images/menu/create.png", import.meta.url).href,
+ iconSrc: LAYOUT_NAV_ASSETS.create,
+ activeIconSrc: LAYOUT_NAV_ASSETS.createActive,
to: ROUTES.CREATE,
},
{
id: "canvas",
labelKey: "layout.nav.canvas",
- iconSrc: new URL("@/assets/images/menu/canvas.png", import.meta.url).href,
+ iconSrc: LAYOUT_NAV_ASSETS.canvas,
+ activeIconSrc: LAYOUT_NAV_ASSETS.canvasActive,
to: CANVAS_BASE_URL ?? "",
- badge: "Beta",
+ badgeKey: "layout.nav.canvasBadge",
+ },
+ {
+ id: "voice",
+ labelKey: "layout.nav.voice",
+ iconSrc: LAYOUT_NAV_ASSETS.voice,
+ activeIconSrc: LAYOUT_NAV_ASSETS.voiceActive,
+ to: ROUTES.VOICE,
},
];
@@ -82,7 +113,6 @@ const LANGUAGE_LABEL_KEYS: Record;
+ }
+
+ return (
+
+ );
+}
+
/**
* 主布局(与路由结合):顶部栏 + 左侧栏 + 右侧内容区
*
@@ -147,10 +199,8 @@ export function Layout({ children }: LayoutProps) {
const [topbarServiceQrOpen, setTopbarServiceQrOpen] = useState(false);
const [sidebarCollapsed, setSidebarCollapsed] = useState(false);
const [promoBarVisible, setPromoBarVisible] = useState(() => !isLayoutPromoBarDismissed());
- const [aliceLiveClassOpen, setAliceLiveClassOpen] = useState(false);
const activePromptPanelHost = useComposerStore((s) => s.activePromptPanelHost);
- const footerRef = useRef
{t("layout.promo.discountPrefix")} {t("layout.promo.discountValue")} {t("layout.promo.discountSuffix")}
- {t("layout.promo.badgeHot")}
{t("layout.promo.pointsPrefix")}
@@ -430,82 +462,45 @@ export function Layout({ children }: LayoutProps) {
) : null}
+
{t("layout.topbar.aliceMeetupBook")} {t("layout.topbar.scan_contact_service")} {t("layout.topbar.scan_contact_service")} navigate(ROUTES.INDEX)} src={new URL("@/assets/images/popiart.png", import.meta.url).href} /> : null}
-
-
-
- SKILL
-
-
+ {t("layout.topbar.skill")}
+
{/* 使用说明 */}
+
+