锁定 Outpost 主路径主题,并优化地图编辑属性面板浅色对比。
统一 outpost-light/purple/blue 壳层 token,修复扩展字段与操作按钮在浅色下的可读性。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,23 @@
|
||||
# 迷毂 Outpost UI 实现计划(P0–P1)
|
||||
|
||||
> **面向 AI 代理的工作者:** 按任务顺序实现;规格见 `docs/superpowers/specs/2026-07-25-migu-outpost-ui-lock-design.md`。
|
||||
|
||||
## 文件
|
||||
|
||||
| 文件 | 职责 |
|
||||
|---|---|
|
||||
| `src/styles/themes.ts` | PRIMARY 三色 + LEGACY;默认 `outpost-light`;`data-shell` |
|
||||
| `src/styles/theme.css` | 浅色壳选择器改 `data-shell="outpost"`;默认 token 贴近浅色 |
|
||||
| `src/stores/ui.ts` | `availableThemes` / `legacyThemes`;未知 id 迁移 |
|
||||
| `src/components/ThemeSwitcher.vue` | 仅精选 3 |
|
||||
| `src/layouts/AppShell.vue` | 高级 → 兼容主题 |
|
||||
| `src/views/LoginView.vue` | 弱化霓虹(P1) |
|
||||
| 若干 Vue 内 `fame-lavender` 选择器 | 改为 `data-shell="outpost"` |
|
||||
|
||||
## 任务
|
||||
|
||||
1. 重写 `themes.ts`(精选 + legacy + applyThemeVars 写 `data-shell`)
|
||||
2. `theme.css` / 组件选择器:`fame-lavender` → `data-shell="outpost"`
|
||||
3. `ui` store + ThemeSwitcher + AppShell 高级入口
|
||||
4. Login 弱化星空主导(能快则做)
|
||||
5. `pnpm`/`npm` typecheck 或 build 验证
|
||||
@@ -0,0 +1,273 @@
|
||||
# 迷毂平台 UI · 锁定 Outpost 浅色设计规格
|
||||
|
||||
> 状态:待用户审查(主题策略已按反馈修订)
|
||||
> 日期:2026-07-25
|
||||
> 参考站:[Outpost · Fairyland Technology](https://fairylandtech.amerc.ai)
|
||||
> 决策:方案 1 壳层气质(Outpost)+ **主路径精选 3 套配色(浅色 / 紫色 / 蓝色)**;旧深色霓虹主题**保留并藏入高级**
|
||||
> 范围前端:`Migu2.0/frontends/apps/simple-platform-vue`
|
||||
> 不改:SimpleLite ImGui / CycleGUI 桌面壳、后端 API 契约
|
||||
|
||||
---
|
||||
|
||||
## 1. 目标与非目标
|
||||
|
||||
### 1.1 目标
|
||||
|
||||
- 迷毂 Web(登录 → AppShell → 业务页 → 地图编辑器 chrome)在视觉与壳层气质上对齐 Outpost。
|
||||
- **壳层统一为 Outpost 结构**:浅主区 + 深色纹理侧栏 + 纸白卡片 + 柔阴影;无默认霓虹 orb。
|
||||
- **主路径提供 3 套精选配色**(可一键切换):浅色(默认)、紫色、蓝色——同结构、不同主色/侧栏色相。
|
||||
- 旧深色霓虹主题(星云紫等)**代码保留**,放入「高级 / 兼容主题」,不进主切换列表。
|
||||
- 保留调度业务信息架构;**不**把 Outpost 会话列表硬套进调度导航。
|
||||
- 状态色(绿/琥珀/红/蓝/灰)与品牌色解耦,语义不变。
|
||||
|
||||
### 1.2 非目标
|
||||
|
||||
- 不重写业务逻辑、路由权限、Projection/SSE 协议。
|
||||
- 不第一阶段替换 Element Plus(以 CSS 变量 + 皮肤覆盖为主)。
|
||||
- 不强制 3D 画布(webVRender)改浅色底。
|
||||
- 不把 Outpost 的 Agent 对话 IA 迁移为迷毂主导航。
|
||||
- 主路径不堆砌过多主题(精选 3 + 高级兼容即可)。
|
||||
|
||||
---
|
||||
|
||||
## 2. 产品决策(已确认)
|
||||
|
||||
| 项 | 决策 |
|
||||
|---|---|
|
||||
| 壳层气质 | Outpost:浅主区 + 深侧栏 + 纸白卡 + 柔阴影 |
|
||||
| 主路径配色 | **3 套精选**:浅色(默认)/ 紫色 / 蓝色 |
|
||||
| 旧深色主题 | **保留**,藏入用户菜单「高级 → 兼容主题」 |
|
||||
| ThemeSwitcher | **保留在顶栏**,只列出精选 3 套;兼容主题在高级入口 |
|
||||
| 登录页 | 跟当前精选主题(默认浅色 Outpost);去掉星空霓虹主导 |
|
||||
| 地图编辑器 | Chrome 跟精选浅色壳;**3D 画布保持深色** |
|
||||
| AI 助手抽屉 | 视觉跟 Outpost 对话区,不改后端权限 |
|
||||
|
||||
---
|
||||
|
||||
## 3. 参考站提炼(Outpost)
|
||||
|
||||
### 3.1 信息架构(借壳不借业务)
|
||||
|
||||
```
|
||||
┌────────────┬──────────────────────────────────────┐
|
||||
│ Sidebar │ Top context (optional, light) │
|
||||
│ ~256px ├──────────────────────────────────────┤
|
||||
│ dark plum │ Main workspace │
|
||||
│ textured │ paper cards / lists / forms │
|
||||
│ brand+nav │ │
|
||||
└────────────┴──────────────────────────────────────┘
|
||||
```
|
||||
|
||||
迷毂映射:
|
||||
|
||||
| Outpost | 迷毂 |
|
||||
|---|---|
|
||||
| Sidebar brand + list | `AppShell` 侧栏 logo + `el-menu` |
|
||||
| Workspace | `el-main` + 各业务 View |
|
||||
| Artifact / card | KPI 卡、列表卡、属性面板、抽屉 |
|
||||
| Conversation chrome | AI 助手抽屉(仅视觉) |
|
||||
|
||||
### 3.2 设计 Token(锁定值)
|
||||
|
||||
来源:Outpost 线上 CSS(`color-scheme: light`)。
|
||||
|
||||
| Token | 值 | 用途 |
|
||||
|---|---|---|
|
||||
| `--op-page` | `#f6f3fb` | 应用主区底 |
|
||||
| `--op-paper` | `#fffefd` | 卡片/面板底 |
|
||||
| `--op-paper-soft` | `#fbf9fd` | 次级表面 |
|
||||
| `--op-ink` | `#28213a` | 主文字 |
|
||||
| `--op-muted` | `#756d85` | 次级文字 |
|
||||
| `--op-faint` | `#9a93a7` | 弱化/占位 |
|
||||
| `--op-line` | `#e8e2ef` | 分割线 |
|
||||
| `--op-line-strong` | `#ddd4e8` | 强调边框 |
|
||||
| `--op-violet` | `#7543e8` | 品牌主色 |
|
||||
| `--op-violet-strong` | `#5d2ecb` | 主色按下/深 |
|
||||
| `--op-violet-soft` | `#eee8ff` | 主色浅底 |
|
||||
| `--op-mint` / `--op-amber` / `--op-danger` / `--op-blue` | 见参考站 | 仅作辅助;**业务状态仍用既有 `--mg-status-*`** |
|
||||
| `--op-shadow` | `0 12px 35px rgba(54,35,78,.08)` | 默认浮起 |
|
||||
| `--op-shadow-strong` | `0 22px 65px rgba(46,24,72,.18)` | 弹层/强调卡 |
|
||||
| `--op-radius` | `16px` | 默认圆角 |
|
||||
| Sidebar | `linear-gradient(160deg,#3f2454,#2c193c 45%,#241530)` + 细纹理 | 侧栏底 |
|
||||
| Font | Inter + 系统无衬线;数据位保留 JetBrains Mono | 与现 `--mg-font-*` 对齐 |
|
||||
|
||||
### 3.3 明确禁止(相对当前「星云紫」)
|
||||
|
||||
- 主路径禁用:大面积霓虹 glow、多层 orb 背景、深色玻璃卡片作默认表面。
|
||||
- 主路径禁用:把品牌紫当 success/warning。
|
||||
- 避免再引入第三套互不兼容的色板命名;Outpost token **映射进**既有 `--mg-*`,业务组件继续只读 `--mg-*`。
|
||||
|
||||
---
|
||||
|
||||
## 4. 精选三套主题 + Token 映射
|
||||
|
||||
三套均遵守同一 Outpost 壳层规则(浅主区 / 纸白卡 / 柔阴影 / orb=0 / radius≈16),仅 **主色 + 侧栏色相 + 主区底轻微染色** 不同。
|
||||
|
||||
### 4.1 主路径精选(`PRIMARY_THEMES`)
|
||||
|
||||
| id | 显示名 | 定位 | 主色 | 侧栏 | 主区底 |
|
||||
|---|---|---|---|---|---|
|
||||
| `outpost-light` | **浅色**(默认) | 最贴近参考站 | `#7543e8` | `#3f2454 → #241530`(plum) | `#f6f3fb` |
|
||||
| `outpost-purple` | **紫色** | 同结构、更饱和紫品牌 | `#7c3aed` | `#2d1b69 → #1a1040` | `#f3eefc`(略偏紫) |
|
||||
| `outpost-blue` | **蓝色** | 同结构、蓝科技感 | `#3c78d2` | `#0f2744 → #0a1a30` | `#f3f6fb`(略偏蓝) |
|
||||
|
||||
共用表面:
|
||||
|
||||
| `--mg-*` | 值 |
|
||||
|---|---|
|
||||
| `--mg-bg-card-rgb` | paper `255,254,253` |
|
||||
| `--mg-text` 系 | ink `#28213a` / muted `#756d85`(浅底深字) |
|
||||
| `--mg-glass-*` | 白卡片 + 细边 + 柔阴影(无强 blur glow) |
|
||||
| `--mg-radius*` | 基准 16px |
|
||||
| `--mg-orb-*-opacity` | **0** |
|
||||
| 状态色 | 继续 `--mg-status-*`,不随主题主色改语义 |
|
||||
|
||||
### 4.2 高级兼容主题(`LEGACY_THEMES`,保留)
|
||||
|
||||
现有深色霓虹预设移入此列表(至少含):
|
||||
|
||||
- `industrial-purple` 星云紫
|
||||
- `deep-azure` 深邃蓝
|
||||
- `emerald-forge` / `crimson-iron` / `graphite-steel` / `amber-forge`(若仍在代码中)
|
||||
- 旧 `fame-lavender`:合并进 `outpost-purple` 或标 deprecated 后从主列表移除
|
||||
|
||||
入口:用户头像菜单 → **高级 → 兼容主题**(折叠)。主顶栏 `ThemeSwitcher` **只显示 3 个精选**。
|
||||
|
||||
### 4.3 默认与迁移
|
||||
|
||||
- `DEFAULT_THEME_ID = 'outpost-light'`
|
||||
- `:root` 默认 CSS 与 `outpost-light` 一致,避免闪屏
|
||||
- localStorage 若为未知 / 已 deprecated id → 迁移到 `outpost-light`
|
||||
- 若为 `LEGACY_THEMES` 中的 id → **尊重用户选择**(不强制踢回浅色)
|
||||
- 登录页品牌区跟当前主题侧栏色;`--lg-*` 随精选主题切换或登录页固定跟 `outpost-light`(实现时二选一,推荐:**登录固定 outpost-light,进壳后再跟用户主题**)
|
||||
|
||||
---
|
||||
|
||||
## 5. 壳层与页面改造要点
|
||||
|
||||
### 5.1 AppShell
|
||||
|
||||
- 侧栏:纹理渐变(随主题色相)、品牌区分隔线、菜单选中用 soft 底或左侧高光条。
|
||||
- 主区装饰 orb:`opacity=0`。
|
||||
- 顶栏:浅底、细边、弱阴影;breadcrumb + **精选 ThemeSwitcher(3 项)** + 用户区。
|
||||
- 用户菜单增加「高级 → 兼容主题」子菜单(列出 `LEGACY_THEMES`)。
|
||||
- 底栏:弱化字色或并入顶栏次级信息(可选后续)。
|
||||
- `aside-footer`:版本号;可选极简当前主题名。
|
||||
|
||||
### 5.2 登录 / 配置向导
|
||||
|
||||
- 布局可保留左右分栏,但:
|
||||
- 背景:浅底 + 可选极轻纹理,不用星空粒子主导。
|
||||
- 左:深色品牌板(默认跟 `outpost-light` 侧栏)。
|
||||
- 右:paper 表单卡 + 主色按钮。
|
||||
- 配置向导与登录同肤。
|
||||
|
||||
### 5.3 Element Plus 皮肤
|
||||
|
||||
统一覆盖(全局):
|
||||
|
||||
- Button primary / default
|
||||
- Card / Dialog / Drawer / MessageBox
|
||||
- Table / Pagination
|
||||
- Menu(侧栏已透明底,补选中态)
|
||||
- Input / Select / Tabs / Tag
|
||||
|
||||
原则:纸白表面、ink 文字、violet 主按钮、radius≈14–16、阴影用 `--op-shadow`。
|
||||
|
||||
### 5.4 业务页(渐进,同一皮肤)
|
||||
|
||||
优先级:
|
||||
|
||||
1. Dashboard / 地图监控 / 调度工作台
|
||||
2. 任务 / 告警 / 车辆中心
|
||||
3. OTA / 配置中心 / 服务状态
|
||||
4. AI 助手抽屉
|
||||
|
||||
统一模式:`page-header` + paper 卡片栅格 + 紫 CTA;清理页面内 `industrial-purple` 专用覆盖或改为仅在「兼容主题」下生效。
|
||||
|
||||
### 5.5 地图编辑器
|
||||
|
||||
| 层 | 皮肤 |
|
||||
|---|---|
|
||||
| EditTopBar / ToolRail / PropertyPanel / StatusBar / AI 侧栏 | Outpost 浅色 |
|
||||
| Workspace3D / webVRender 画布 | **保持深色视口** |
|
||||
| 与壳层交界 | 1px `--op-line`,避免深浅硬切刺眼 |
|
||||
|
||||
### 5.6 主题列表处理
|
||||
|
||||
| 分组 | 主题 | 处理 |
|
||||
|---|---|---|
|
||||
| 精选 | `outpost-light` / `outpost-purple` / `outpost-blue` | 新建或由现有浅色主题改版;顶栏可切换 |
|
||||
| 合并 | `fame-lavender` | 并入 `outpost-purple` 或启动时 remap |
|
||||
| 高级兼容 | `industrial-purple`、`deep-azure`、绿/赤/钢/琥珀等 | 保留完整 vars,仅高级菜单可选 |
|
||||
| 持久化 | 精选或 legacy id | 精选正常读写;未知 id → `outpost-light`;legacy id **不强制迁移** |
|
||||
|
||||
---
|
||||
|
||||
## 6. 文件影响面(实现时)
|
||||
|
||||
| 文件/目录 | 职责 |
|
||||
|---|---|
|
||||
| `src/styles/theme.css` | 默认 token、Element 覆盖、禁 orb |
|
||||
| `src/styles/themes.ts` | `PRIMARY_THEMES`(3)+ `LEGACY_THEMES`;默认 `outpost-light` |
|
||||
| `src/stores/ui.ts`(或等价) | 默认主题、未知 id 迁移、legacy 可选 |
|
||||
| `src/components/ThemeSwitcher.vue` | 只渲染精选 3;样式 Outpost 化 |
|
||||
| `src/layouts/AppShell.vue` | 侧栏/顶栏;用户菜单挂「兼容主题」 |
|
||||
| `src/views/LoginView.vue` | 登录换肤 |
|
||||
| `design-system/MASTER.md` | 产品风格改为 Outpost 浅色工作台 |
|
||||
| `design-system/outpost.md` | Token 与组件规范(新建) |
|
||||
| 各 View 内 `:root[data-theme="industrial-purple"]` 块 | 降级为 legacy 或删除 |
|
||||
| 地图编辑器 chrome 组件 | 浅色适配 |
|
||||
|
||||
---
|
||||
|
||||
## 7. 分阶段交付
|
||||
|
||||
| Phase | 内容 | 验收 |
|
||||
|---|---|---|
|
||||
| **P0** | 三套精选 token + 默认 `outpost-light` + legacy 分组 + 文档 | 冷启动浅色 Outpost;可切紫/蓝 |
|
||||
| **P1** | AppShell + 登录 + Element 皮肤 + ThemeSwitcher(3) + 高级兼容入口 | 壳层/登录 Outpost 化;高级仍能开星云紫等 |
|
||||
| **P2** | Dashboard / 监控 / 调度 / 列表页 | 主业务无霓虹玻璃默认态 |
|
||||
| **P3** | 地图编辑器 chrome + AI 抽屉 | 外围浅、画布深;助手区纸白气泡 |
|
||||
| **P4** | 清理 legacy 覆盖、MASTER 更新、回归清单 | 无残留 `industrial-purple` 主路径样式 |
|
||||
|
||||
每阶段可独立合并;**不在本规格内自动改 SimpleLite 原生菜单皮肤**。
|
||||
|
||||
---
|
||||
|
||||
## 8. 风险与缓解
|
||||
|
||||
| 风险 | 缓解 |
|
||||
|---|---|
|
||||
| 深色主题下写死的白字/半透明在浅底不可读 | P0 强制迁移 + P2 逐页扫对比度 |
|
||||
| 地图页信息密度高,浅色易显脏 | 卡片分区 + 表格紧凑密度,状态色保留 |
|
||||
| Element 覆盖不全 | 先覆盖高频控件,缺口记入回归清单 |
|
||||
| 用户 localStorage 旧主题 | 启动迁移 + 一次性提示(可选) |
|
||||
|
||||
---
|
||||
|
||||
## 9. 验收清单(整站)
|
||||
|
||||
- [ ] 未登录:登录页为 Outpost 浅色气质
|
||||
- [ ] 登录后:侧栏深紫纹理 + 主区 `#f6f3fb` + 纸白卡片
|
||||
- [ ] 顶栏可切换 **浅色 / 紫色 / 蓝色** 三套精选
|
||||
- [ ] 用户菜单「高级 → 兼容主题」仍能选星云紫等旧深色
|
||||
- [ ] 监控/调度/OTA/配置:纸白卡、ink 字、无默认霓虹 orb
|
||||
- [ ] 地图编辑器:工具/属性浅色,3D 画布仍深色可读
|
||||
- [ ] 告警/成功/故障色不被品牌色替换
|
||||
- [ ] 刷新后主题保持;未知 id 回落 `outpost-light`
|
||||
|
||||
---
|
||||
|
||||
## 10. 开放项(审查时确认)
|
||||
|
||||
1. ~~旧深色主题~~ → **已确认:保留并藏入高级**
|
||||
2. **底栏 footer**:保留弱化版,还是删除并入顶栏?(建议:P1 先弱化保留)
|
||||
3. 实现计划:`Migu2.0/docs/superpowers/plans/2026-07-25-migu-outpost-ui-lock.md`(本规格通过后编写)
|
||||
|
||||
---
|
||||
|
||||
## 11. 审查结论(请用户勾选)
|
||||
|
||||
- [ ] 同意本规格(含精选三色 + 高级保留旧主题),可编写实现计划并开工
|
||||
- [ ] 需要修改:________
|
||||
@@ -0,0 +1,22 @@
|
||||
# Outpost 视觉规范(迷毂壳层)
|
||||
|
||||
> 权威决策见:`Migu2.0/docs/superpowers/specs/2026-07-25-migu-outpost-ui-lock-design.md`
|
||||
> 参考:https://fairylandtech.amerc.ai
|
||||
|
||||
## 一句话
|
||||
|
||||
Outpost 壳层:浅主区 + 深色纹理侧栏 + 纸白卡片 + 柔阴影。
|
||||
主路径精选三色:**浅色(默认)/ 紫色 / 蓝色**;旧深色霓虹主题保留在「高级 → 兼容主题」。
|
||||
|
||||
## 精选主题 id
|
||||
|
||||
| id | 名称 |
|
||||
|---|---|
|
||||
| `outpost-light` | 浅色(默认) |
|
||||
| `outpost-purple` | 紫色 |
|
||||
| `outpost-blue` | 蓝色 |
|
||||
|
||||
## 例外
|
||||
|
||||
- webVRender / 地图 3D 画布:保持深色视口。
|
||||
- 状态色:继续用 `--mg-status-*`。
|
||||
@@ -1,11 +1,14 @@
|
||||
<!doctype html>
|
||||
<html lang="zh-CN">
|
||||
<html lang="zh-CN" data-shell="outpost" data-theme="outpost-light">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>迷毂 · 智能调度平台</title>
|
||||
<meta name="theme-color" content="#2e3f8a" />
|
||||
<meta name="theme-color" content="#7543e8" />
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
||||
@@ -291,14 +291,23 @@ defineExpose({ reload, enterFullscreen })
|
||||
.workspace-3d-mask {
|
||||
position: absolute; inset: 0; display: flex; flex-direction: column;
|
||||
align-items: center; justify-content: center; gap: 10px;
|
||||
/* 画布加载光晕固定深紫(不跟浅色壳白底 token,避免被冲成灰白) */
|
||||
background:
|
||||
radial-gradient(ellipse at center, rgba(var(--mg-primary-rgb), 0.35) 0%, rgba(var(--mg-bg-app-deep-rgb), 0.92) 80%),
|
||||
rgba(var(--mg-bg-app-deep-rgb), 0.85);
|
||||
radial-gradient(
|
||||
ellipse at 50% 42%,
|
||||
rgba(155, 124, 255, 0.42) 0%,
|
||||
rgba(88, 48, 168, 0.72) 38%,
|
||||
rgba(45, 27, 105, 0.92) 68%,
|
||||
rgba(15, 4, 32, 0.98) 100%
|
||||
);
|
||||
color: rgba(232, 220, 255, 0.92);
|
||||
font-size: 13px;
|
||||
backdrop-filter: blur(10px);
|
||||
pointer-events: none;
|
||||
}
|
||||
.workspace-3d-mask .muted { color: rgba(var(--mg-accent-rgb), 0.7); font-size: 12px; }
|
||||
.workspace-3d-mask .el-icon { color: var(--mg-accent); filter: drop-shadow(0 0 10px rgba(var(--mg-accent-rgb), 0.6)); }
|
||||
.workspace-3d-mask .muted { color: rgba(196, 181, 253, 0.78); font-size: 12px; }
|
||||
.workspace-3d-mask .el-icon {
|
||||
color: #c4b5fd;
|
||||
filter: drop-shadow(0 0 12px rgba(155, 124, 255, 0.65));
|
||||
}
|
||||
</style>
|
||||
|
||||
+168
@@ -0,0 +1,168 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
:model-value="open"
|
||||
title="选择字段增加"
|
||||
width="520px"
|
||||
append-to-body
|
||||
destroy-on-close
|
||||
class="better-add-field-dialog"
|
||||
@update:model-value="onOpenChange"
|
||||
>
|
||||
<p class="baf-hint">
|
||||
目标:{{ targetSummary }}。选择场景已有字段,再输入要批量写入的值(对齐 Ctrl+I)。
|
||||
</p>
|
||||
<el-input
|
||||
v-model="search"
|
||||
clearable
|
||||
placeholder="搜索字段名"
|
||||
class="baf-search"
|
||||
/>
|
||||
<div v-loading="loadingKeys" class="baf-list">
|
||||
<button
|
||||
v-for="key in filteredKeys"
|
||||
:key="key"
|
||||
type="button"
|
||||
class="baf-key"
|
||||
:class="{ 'is-active': key === pickedKey }"
|
||||
@click="pickedKey = key"
|
||||
>
|
||||
{{ key }}
|
||||
</button>
|
||||
<div v-if="!loadingKeys && filteredKeys.length === 0" class="baf-empty">
|
||||
{{ keys.length === 0 ? '当前场景还没有任何自定义字段可选' : '无匹配字段' }}
|
||||
</div>
|
||||
</div>
|
||||
<el-input
|
||||
v-model="value"
|
||||
:disabled="!pickedKey"
|
||||
:placeholder="pickedKey ? `输入字段 ${pickedKey} 的值` : '先选择字段'"
|
||||
class="baf-value"
|
||||
@keydown.enter.prevent="confirm"
|
||||
/>
|
||||
<template #footer>
|
||||
<el-button @click="close">取消</el-button>
|
||||
<el-button type="primary" :loading="applying" :disabled="!pickedKey" @click="confirm">
|
||||
写入选中对象
|
||||
</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, ref, watch } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { mapEditApi } from '@/api/mapEdit'
|
||||
|
||||
const props = defineProps<{
|
||||
open: boolean
|
||||
targets: Array<{ kind: string; id: number }>
|
||||
targetSummary: string
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
'update:open': [v: boolean]
|
||||
applied: [payload: { key: string; value: string; affected: number }]
|
||||
}>()
|
||||
|
||||
const keys = ref<string[]>([])
|
||||
const loadingKeys = ref(false)
|
||||
const search = ref('')
|
||||
const pickedKey = ref('')
|
||||
const value = ref('')
|
||||
const applying = ref(false)
|
||||
|
||||
const filteredKeys = computed(() => {
|
||||
const q = search.value.trim().toLowerCase()
|
||||
if (!q) return keys.value
|
||||
return keys.value.filter((k) => k.toLowerCase().includes(q))
|
||||
})
|
||||
|
||||
watch(
|
||||
() => props.open,
|
||||
async (v) => {
|
||||
if (!v) return
|
||||
search.value = ''
|
||||
pickedKey.value = ''
|
||||
value.value = ''
|
||||
loadingKeys.value = true
|
||||
try {
|
||||
const r = await mapEditApi.sceneFieldKeys()
|
||||
keys.value = r.keys ?? []
|
||||
} catch (err) {
|
||||
keys.value = []
|
||||
ElMessage.error(`加载字段列表失败:${(err as Error).message}`)
|
||||
} finally {
|
||||
loadingKeys.value = false
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
function onOpenChange(v: boolean) {
|
||||
emit('update:open', v)
|
||||
}
|
||||
|
||||
function close() {
|
||||
emit('update:open', false)
|
||||
}
|
||||
|
||||
async function confirm() {
|
||||
if (!pickedKey.value || props.targets.length === 0) return
|
||||
applying.value = true
|
||||
try {
|
||||
const r = await mapEditApi.batchSetField(pickedKey.value, value.value, props.targets)
|
||||
ElMessage.success(`已对 ${r.affected}/${props.targets.length} 个对象设置 ${pickedKey.value}`)
|
||||
emit('applied', { key: pickedKey.value, value: value.value, affected: r.affected })
|
||||
close()
|
||||
} catch (err) {
|
||||
ElMessage.error(`写入失败:${(err as Error).message}`)
|
||||
} finally {
|
||||
applying.value = false
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.baf-hint {
|
||||
margin: 0 0 12px;
|
||||
font-size: 13px;
|
||||
line-height: 1.45;
|
||||
color: var(--el-text-color-secondary);
|
||||
}
|
||||
.baf-search { margin-bottom: 10px; width: 100%; }
|
||||
.baf-list {
|
||||
min-height: 180px;
|
||||
max-height: 280px;
|
||||
overflow: auto;
|
||||
border: 1px solid var(--el-border-color);
|
||||
border-radius: 8px;
|
||||
padding: 6px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.baf-key {
|
||||
appearance: none;
|
||||
border: 0;
|
||||
background: transparent;
|
||||
text-align: left;
|
||||
padding: 8px 10px;
|
||||
border-radius: 6px;
|
||||
font-size: 13px;
|
||||
color: var(--el-text-color-primary);
|
||||
cursor: pointer;
|
||||
}
|
||||
.baf-key:hover { background: var(--el-fill-color-light); }
|
||||
.baf-key.is-active {
|
||||
background: var(--el-color-primary-light-9);
|
||||
color: var(--el-color-primary);
|
||||
font-weight: 600;
|
||||
}
|
||||
.baf-empty {
|
||||
padding: 28px 12px;
|
||||
text-align: center;
|
||||
font-size: 13px;
|
||||
color: var(--el-text-color-secondary);
|
||||
}
|
||||
.baf-value { width: 100%; }
|
||||
</style>
|
||||
+363
-218
@@ -16,11 +16,8 @@
|
||||
<div v-else class="object-pane">
|
||||
<!-- Header card:ID / 类型 / 名称 / 图层 / 删除按钮 -->
|
||||
<section class="prop-card prop-card--header">
|
||||
<div class="prop-card-row">
|
||||
<div class="prop-id-block">
|
||||
<span class="prop-id-tag">{{ selectionCount > 1 ? `多选 ${selectionCount}` : `#${primary?.id ?? '-'}` }}</span>
|
||||
<span class="prop-type-tag">{{ primary?.typeName ?? '-' }}</span>
|
||||
</div>
|
||||
<header class="prop-card-header">
|
||||
<span class="prop-card-title">基础属性</span>
|
||||
<div class="prop-header-actions">
|
||||
<el-tag
|
||||
v-if="primary?.status"
|
||||
@@ -42,11 +39,18 @@
|
||||
:disabled="deleting"
|
||||
@click="onDelete"
|
||||
>
|
||||
<span class="prop-delete-icon">{{ deleting ? '⟳' : '🗑' }}</span>
|
||||
<span class="prop-delete-icon">{{ deleting ? '⟳' : '×' }}</span>
|
||||
<span>删除</span>
|
||||
</button>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="prop-card-row prop-id-row">
|
||||
<div class="prop-id-block">
|
||||
<span class="prop-id-tag">{{ selectionCount > 1 ? `多选 ${selectionCount}` : `#${primary?.id ?? '-'}` }}</span>
|
||||
<span class="prop-type-tag">{{ primary?.typeName ?? '-' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="prop-card-row prop-name-row">
|
||||
@@ -863,17 +867,17 @@ function onDelete() {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* Style 2 分组卡片 + Style 5 主题色相(--me-* / --mg-primary) */
|
||||
.edit-property-panel {
|
||||
height: 100%;
|
||||
width: 360px;
|
||||
background: linear-gradient(180deg, rgba(28, 12, 56, 0.85) 0%, rgba(18, 6, 36, 0.95) 100%);
|
||||
border-left: 1px solid rgba(255,255,255,0.10);
|
||||
background: linear-gradient(180deg, var(--me-chrome-1) 0%, var(--me-chrome-2) 100%);
|
||||
border-left: 1px solid var(--me-border);
|
||||
padding: 0;
|
||||
backdrop-filter: blur(10px);
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
color: rgba(232,215,245,0.92);
|
||||
color: var(--me-text);
|
||||
}
|
||||
.prop-tabs {
|
||||
height: 100%;
|
||||
@@ -882,146 +886,205 @@ function onDelete() {
|
||||
}
|
||||
.prop-tabs :deep(.el-tabs__header) {
|
||||
margin: 0;
|
||||
padding: 0 8px;
|
||||
border-bottom: 1px solid rgba(255,255,255,0.08);
|
||||
}
|
||||
.prop-tabs :deep(.el-tabs__nav-wrap)::after {
|
||||
background: transparent;
|
||||
padding: 6px 10px 0;
|
||||
border-bottom: 1px solid var(--me-border);
|
||||
background: var(--me-surface);
|
||||
}
|
||||
.prop-tabs :deep(.el-tabs__nav-wrap)::after { background: transparent; }
|
||||
.prop-tabs :deep(.el-tabs__item) {
|
||||
color: rgba(220, 200, 240, 0.7);
|
||||
color: var(--me-text-muted);
|
||||
font-weight: 500;
|
||||
font-size: 13px;
|
||||
height: 36px;
|
||||
line-height: 36px;
|
||||
height: 34px;
|
||||
line-height: 34px;
|
||||
padding: 0 14px !important;
|
||||
}
|
||||
.prop-tabs :deep(.el-tabs__item.is-active) {
|
||||
color: #fff;
|
||||
color: var(--mg-primary);
|
||||
font-weight: 700;
|
||||
}
|
||||
.prop-tabs :deep(.el-tabs__active-bar) {
|
||||
background: linear-gradient(90deg, rgba(170, 110, 250, 1) 0%, rgba(255, 110, 220, 1) 100%);
|
||||
background: var(--mg-primary);
|
||||
height: 2px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
.prop-tabs :deep(.el-tabs__content) {
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
padding: 10px 12px 14px;
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.empty-hint {
|
||||
display: flex; align-items: center; gap: 6px;
|
||||
padding: 56px 16px; text-align: center;
|
||||
color: rgba(220, 200, 240, 0.55);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 48px 16px;
|
||||
text-align: center;
|
||||
color: var(--me-text-muted);
|
||||
flex-direction: column;
|
||||
}
|
||||
.empty-icon { font-size: 28px; color: rgba(180, 130, 220, 0.45); }
|
||||
.empty-title { font-size: 13px; color: rgba(232, 215, 245, 0.75); font-weight: 500; }
|
||||
.empty-desc { font-size: 11.5px; color: rgba(220, 200, 240, 0.5); }
|
||||
.empty-icon { font-size: 28px; color: rgba(var(--mg-primary-rgb), 0.45); }
|
||||
.empty-title { font-size: 13px; color: var(--me-text); font-weight: 600; }
|
||||
.empty-desc { font-size: 11.5px; color: var(--me-text-muted); max-width: 220px; line-height: 1.45; }
|
||||
.loading-hint {
|
||||
display: flex; align-items: center; gap: 8px;
|
||||
padding: 32px 16px; text-align: center;
|
||||
color: rgba(232,215,245,0.65); font-size: 12.5px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 32px 16px;
|
||||
justify-content: center;
|
||||
color: var(--me-text-muted);
|
||||
font-size: 12.5px;
|
||||
}
|
||||
|
||||
.object-pane {
|
||||
.object-pane,
|
||||
.defaults-pane {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
/* 分组卡片:跟 --me-card-*(Harbor 浅壳 / 紫蓝深壳共用) */
|
||||
.prop-card {
|
||||
background: rgba(255,255,255,0.04);
|
||||
border: 1px solid rgba(255,255,255,0.08);
|
||||
border-radius: 8px;
|
||||
padding: 10px 12px;
|
||||
box-shadow: 0 1px 4px rgba(0,0,0,0.20);
|
||||
position: relative;
|
||||
background: var(--me-card-bg);
|
||||
border: 1px solid var(--me-card-border);
|
||||
border-radius: 12px;
|
||||
padding: 12px 12px 10px 14px;
|
||||
box-shadow: 0 4px 14px rgba(var(--mg-shadow-rgb, 22, 58, 74), 0.08);
|
||||
}
|
||||
.prop-card::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 10px;
|
||||
bottom: 10px;
|
||||
width: 3px;
|
||||
border-radius: 0 3px 3px 0;
|
||||
background: rgba(var(--mg-primary-rgb), 0.55);
|
||||
}
|
||||
.prop-card--header {
|
||||
background: linear-gradient(140deg, rgba(170, 110, 250, 0.18) 0%, rgba(70, 30, 110, 0.10) 100%);
|
||||
border-color: rgba(170, 110, 250, 0.28);
|
||||
background: linear-gradient(
|
||||
135deg,
|
||||
rgba(var(--mg-primary-rgb), 0.12) 0%,
|
||||
var(--me-card-bg) 55%
|
||||
);
|
||||
border-color: rgba(var(--mg-primary-rgb), 0.28);
|
||||
box-shadow: 0 4px 16px rgba(var(--mg-primary-rgb), 0.08);
|
||||
}
|
||||
.prop-card--geom {
|
||||
background: linear-gradient(140deg, rgba(80, 140, 220, 0.10) 0%, rgba(40, 70, 130, 0.06) 100%);
|
||||
border-color: rgba(120, 170, 230, 0.22);
|
||||
.prop-card--header::before {
|
||||
background: var(--mg-primary);
|
||||
top: 8px;
|
||||
bottom: 8px;
|
||||
width: 3px;
|
||||
}
|
||||
.prop-card--style {
|
||||
background: linear-gradient(140deg, rgba(220, 110, 200, 0.10) 0%, rgba(150, 70, 130, 0.06) 100%);
|
||||
border-color: rgba(230, 130, 200, 0.22);
|
||||
.prop-card--geom,
|
||||
.prop-card--style,
|
||||
.prop-card--custom,
|
||||
.prop-card--status,
|
||||
.prop-card--actions {
|
||||
background: var(--me-card-bg);
|
||||
border-color: var(--me-card-border);
|
||||
}
|
||||
.prop-card--custom {
|
||||
background: rgba(255, 255, 255, 0.03);
|
||||
border-color: rgba(170, 110, 250, 0.18);
|
||||
}
|
||||
.prop-card--status { background: rgba(80, 140, 220, 0.06); border-color: rgba(120, 170, 230, 0.18); }
|
||||
.prop-card--actions { background: rgba(140, 80, 200, 0.06); border-color: rgba(170, 110, 250, 0.18); }
|
||||
|
||||
.prop-card-header {
|
||||
display: flex; align-items: center; justify-content: space-between;
|
||||
margin-bottom: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 12px;
|
||||
min-height: 22px;
|
||||
padding-bottom: 8px;
|
||||
border-bottom: 1px solid var(--me-card-border);
|
||||
}
|
||||
.prop-card-title {
|
||||
font-size: 11.5px;
|
||||
font-weight: 600;
|
||||
letter-spacing: 1px;
|
||||
color: rgba(220, 180, 250, 0.85);
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
color: var(--me-text);
|
||||
}
|
||||
.prop-card-meta {
|
||||
font-size: 10.5px;
|
||||
color: rgba(200, 180, 220, 0.5);
|
||||
margin-left: auto;
|
||||
font-size: 11px;
|
||||
font-family: var(--mg-font-mono, ui-monospace, Menlo, Consolas, monospace);
|
||||
font-variant-numeric: tabular-nums;
|
||||
color: var(--me-text-muted);
|
||||
background: var(--me-inset);
|
||||
border: 1px solid var(--me-card-border);
|
||||
border-radius: 999px;
|
||||
padding: 1px 8px;
|
||||
}
|
||||
.prop-card-flag {
|
||||
display: inline-flex; align-items: center; gap: 4px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
font-size: 10.5px;
|
||||
color: rgba(180, 200, 240, 0.6);
|
||||
padding: 2px 7px;
|
||||
border-radius: 10px;
|
||||
background: rgba(120, 160, 220, 0.10);
|
||||
transition: background .25s ease, color .25s ease, box-shadow .25s ease;
|
||||
color: var(--me-text-muted);
|
||||
padding: 2px 8px;
|
||||
border-radius: 999px;
|
||||
background: var(--me-surface);
|
||||
border: 1px solid var(--me-card-border);
|
||||
margin-left: auto;
|
||||
}
|
||||
.prop-card-flag.is-synced {
|
||||
background: linear-gradient(120deg, rgba(80, 200, 130, 0.32), rgba(120, 220, 200, 0.32));
|
||||
color: rgba(220, 255, 230, 0.95);
|
||||
box-shadow: 0 0 8px rgba(80, 200, 130, 0.45);
|
||||
background: rgba(34, 197, 94, 0.12);
|
||||
border-color: rgba(34, 197, 94, 0.35);
|
||||
color: #15803d;
|
||||
box-shadow: none;
|
||||
}
|
||||
.prop-card-flag-dot {
|
||||
width: 6px; height: 6px; border-radius: 50%;
|
||||
background: rgba(180, 200, 240, 0.6);
|
||||
transition: background .25s ease, box-shadow .25s ease;
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 50%;
|
||||
background: var(--me-text-muted);
|
||||
}
|
||||
.prop-card-flag.is-synced .prop-card-flag-dot {
|
||||
background: #6ce7a8;
|
||||
box-shadow: 0 0 6px rgba(108, 231, 168, 0.85);
|
||||
background: #4ade80;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.prop-card-row {
|
||||
display: flex; align-items: center; justify-content: space-between;
|
||||
margin-bottom: 6px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 8px;
|
||||
gap: 8px;
|
||||
}
|
||||
.prop-card-row:last-child { margin-bottom: 0; }
|
||||
.prop-id-row { margin-bottom: 10px; }
|
||||
|
||||
.prop-id-block { display: flex; align-items: center; gap: 6px; }
|
||||
.prop-id-block {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
min-width: 0;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.prop-id-tag {
|
||||
background: rgba(170, 110, 250, 0.30);
|
||||
background: var(--mg-primary);
|
||||
color: #fff;
|
||||
padding: 2px 8px;
|
||||
font-size: 11.5px;
|
||||
font-weight: 600;
|
||||
border-radius: 4px;
|
||||
letter-spacing: 0.5px;
|
||||
padding: 3px 9px;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
border-radius: 6px;
|
||||
font-family: var(--mg-font-mono, ui-monospace, Menlo, Consolas, monospace);
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
.prop-type-tag {
|
||||
color: rgba(232, 215, 245, 0.75);
|
||||
font-size: 11.5px;
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
||||
color: var(--me-text-muted);
|
||||
font-size: 12px;
|
||||
font-family: var(--mg-font-mono, ui-monospace, Menlo, Consolas, monospace);
|
||||
background: var(--me-inset);
|
||||
border: 1px solid var(--me-card-border);
|
||||
border-radius: 6px;
|
||||
padding: 2px 8px;
|
||||
}
|
||||
.prop-status-tag { font-weight: 500; }
|
||||
.prop-status-tag { font-weight: 600; }
|
||||
|
||||
.prop-header-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.prop-delete-btn {
|
||||
@@ -1030,31 +1093,31 @@ function onDelete() {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
padding: 3px 9px;
|
||||
border-radius: 5px;
|
||||
font-size: 11.5px;
|
||||
font-weight: 500;
|
||||
letter-spacing: 0.4px;
|
||||
color: rgba(255, 200, 210, 0.95);
|
||||
background: linear-gradient(135deg, rgba(255, 90, 120, 0.18) 0%, rgba(220, 60, 110, 0.22) 100%);
|
||||
border: 1px solid rgba(255, 110, 140, 0.45);
|
||||
transition: background .14s ease, border-color .14s ease, color .14s ease, box-shadow .14s ease, transform .12s ease;
|
||||
padding: 4px 10px;
|
||||
border-radius: 6px;
|
||||
font-size: 12px;
|
||||
font-weight: 550;
|
||||
color: #fecaca;
|
||||
background: rgba(239, 68, 68, 0.12);
|
||||
border: 1px solid rgba(239, 68, 68, 0.35);
|
||||
transition: background .12s ease, border-color .12s ease;
|
||||
}
|
||||
.prop-delete-btn:hover:not(:disabled) {
|
||||
background: linear-gradient(135deg, rgba(255, 90, 120, 0.55) 0%, rgba(220, 60, 110, 0.65) 100%);
|
||||
border-color: rgba(255, 140, 170, 0.85);
|
||||
background: rgba(239, 68, 68, 0.22);
|
||||
border-color: rgba(239, 68, 68, 0.55);
|
||||
color: #fff;
|
||||
box-shadow: 0 3px 10px rgba(220, 70, 110, 0.45);
|
||||
box-shadow: none;
|
||||
}
|
||||
.prop-delete-btn:active:not(:disabled) { transform: scale(0.96); }
|
||||
.prop-delete-btn:disabled { opacity: 0.55; cursor: progress; }
|
||||
.prop-delete-icon { font-size: 12px; line-height: 1; }
|
||||
|
||||
.prop-name-row, .prop-layer-row, .prop-direction-row { gap: 8px; }
|
||||
.prop-name-row,
|
||||
.prop-layer-row,
|
||||
.prop-direction-row { gap: 10px; }
|
||||
.prop-name-label {
|
||||
width: 36px;
|
||||
font-size: 11.5px;
|
||||
color: rgba(200, 180, 220, 0.7);
|
||||
font-size: 12px;
|
||||
color: var(--me-text-muted);
|
||||
flex: none;
|
||||
}
|
||||
.prop-layer-select { flex: 1; }
|
||||
@@ -1062,15 +1125,15 @@ function onDelete() {
|
||||
.prop-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
column-gap: 8px;
|
||||
row-gap: 8px;
|
||||
column-gap: 10px;
|
||||
row-gap: 10px;
|
||||
}
|
||||
.prop-grid-item { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
|
||||
.prop-grid-item { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
|
||||
.prop-grid-item--wide { grid-column: 1 / -1; }
|
||||
.prop-field-label {
|
||||
font-size: 11px;
|
||||
color: rgba(200, 180, 230, 0.78);
|
||||
letter-spacing: 0.3px;
|
||||
color: var(--me-text-muted);
|
||||
letter-spacing: 0.2px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: baseline;
|
||||
@@ -1081,155 +1144,230 @@ function onDelete() {
|
||||
}
|
||||
.prop-field-unit {
|
||||
font-size: 10px;
|
||||
color: rgba(170, 150, 200, 0.55);
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
||||
color: var(--me-text-muted);
|
||||
font-family: var(--mg-font-mono, ui-monospace, Menlo, Consolas, monospace);
|
||||
flex: none;
|
||||
}
|
||||
.prop-field-num :deep(.el-input-number) { width: 100%; }
|
||||
.prop-field-num :deep(.el-input__wrapper) {
|
||||
background: rgba(255,255,255,0.06);
|
||||
box-shadow: inset 0 0 0 1px rgba(170, 110, 250, 0.18);
|
||||
.prop-field-num :deep(.el-input__wrapper),
|
||||
.prop-card :deep(.el-input__wrapper),
|
||||
.prop-card :deep(.el-select__wrapper),
|
||||
.prop-card :deep(.el-textarea__inner) {
|
||||
background: var(--me-inset) !important;
|
||||
box-shadow: inset 0 0 0 1px var(--me-card-border) !important;
|
||||
}
|
||||
.prop-field-row { display: flex; align-items: center; gap: 4px; }
|
||||
.prop-field-num :deep(.el-input__inner),
|
||||
.prop-card :deep(.el-input__inner),
|
||||
.prop-card :deep(.el-textarea__inner),
|
||||
.prop-card :deep(.el-select__selected-item),
|
||||
.prop-card :deep(.el-select__placeholder) {
|
||||
color: var(--me-text) !important;
|
||||
}
|
||||
.prop-field-num :deep(.el-input__wrapper:hover),
|
||||
.prop-card :deep(.el-input__wrapper:hover),
|
||||
.prop-card :deep(.el-select__wrapper:hover) {
|
||||
box-shadow: inset 0 0 0 1px rgba(var(--mg-primary-rgb), 0.55) !important;
|
||||
}
|
||||
.prop-field-num :deep(.el-input__wrapper.is-focus),
|
||||
.prop-card :deep(.el-input__wrapper.is-focus),
|
||||
.prop-card :deep(.el-select__wrapper.is-focused) {
|
||||
box-shadow:
|
||||
inset 0 0 0 1px var(--mg-primary),
|
||||
0 0 0 3px rgba(var(--mg-primary-rgb), 0.16) !important;
|
||||
}
|
||||
.prop-field-row { display: flex; align-items: center; gap: 6px; }
|
||||
.prop-field-icon-btn {
|
||||
appearance: none; border: 0; cursor: pointer;
|
||||
width: 22px; height: 22px;
|
||||
border-radius: 4px;
|
||||
background: rgba(255, 100, 130, 0.12);
|
||||
color: rgba(255, 180, 200, 0.85);
|
||||
font-size: 14px; line-height: 1;
|
||||
display: inline-flex; align-items: center; justify-content: center;
|
||||
appearance: none;
|
||||
border: 1px solid rgba(239, 68, 68, 0.28);
|
||||
cursor: pointer;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border-radius: 7px;
|
||||
background: rgba(239, 68, 68, 0.08);
|
||||
color: #dc2626;
|
||||
font-size: 15px;
|
||||
line-height: 1;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex: none;
|
||||
transition: background .14s, color .14s;
|
||||
transition: background .12s ease, border-color .12s ease, color .12s ease;
|
||||
}
|
||||
.prop-field-icon-btn:hover {
|
||||
background: rgba(255, 100, 130, 0.32);
|
||||
color: #fff;
|
||||
background: rgba(239, 68, 68, 0.16);
|
||||
border-color: rgba(239, 68, 68, 0.45);
|
||||
color: #b91c1c;
|
||||
}
|
||||
|
||||
.prop-empty-row {
|
||||
font-size: 11.5px;
|
||||
color: rgba(200, 180, 220, 0.4);
|
||||
font-size: 12px;
|
||||
color: var(--me-text-muted);
|
||||
text-align: center;
|
||||
padding: 8px 0;
|
||||
padding: 14px 8px;
|
||||
border-radius: 8px;
|
||||
background: var(--me-inset);
|
||||
border: 1px dashed var(--me-card-border);
|
||||
}
|
||||
.prop-add-field-btn {
|
||||
appearance: none; border: 0; cursor: pointer;
|
||||
appearance: none;
|
||||
cursor: pointer;
|
||||
width: 100%;
|
||||
margin-top: 8px;
|
||||
padding: 6px 10px;
|
||||
border-radius: 6px;
|
||||
background: rgba(170, 110, 250, 0.14);
|
||||
border: 1px dashed rgba(170, 110, 250, 0.40);
|
||||
color: rgba(220, 180, 250, 0.92);
|
||||
margin-top: 10px;
|
||||
padding: 8px 10px;
|
||||
border-radius: 8px;
|
||||
background: var(--me-surface);
|
||||
border: 1px dashed rgba(var(--mg-primary-rgb), 0.42);
|
||||
color: var(--mg-primary);
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
transition: background .14s ease, border-color .14s ease, color .14s ease;
|
||||
font-weight: 600;
|
||||
transition: background .12s ease, border-color .12s ease, color .12s ease;
|
||||
}
|
||||
.prop-add-field-btn:hover {
|
||||
background: rgba(170, 110, 250, 0.26);
|
||||
border-color: rgba(170, 110, 250, 0.65);
|
||||
color: #fff;
|
||||
.prop-add-field-btn:hover:not(:disabled) {
|
||||
background: var(--me-hover);
|
||||
border-color: rgba(var(--mg-primary-rgb), 0.7);
|
||||
border-style: solid;
|
||||
color: var(--mg-primary);
|
||||
}
|
||||
.prop-add-field-btn:disabled { opacity: 0.45; cursor: not-allowed; }
|
||||
|
||||
.custom-field-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
margin-bottom: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.custom-field-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 3px;
|
||||
gap: 5px;
|
||||
min-width: 0;
|
||||
padding: 9px 10px;
|
||||
border-radius: 9px;
|
||||
background: var(--me-inset);
|
||||
border: 1px solid var(--me-card-border);
|
||||
}
|
||||
|
||||
.custom-field-key {
|
||||
margin: 0;
|
||||
font-family: var(--mg-font-mono, ui-monospace, monospace);
|
||||
font-size: 11px;
|
||||
font-weight: 500;
|
||||
color: rgba(200, 180, 230, 0.88);
|
||||
font-weight: 650;
|
||||
letter-spacing: 0.02em;
|
||||
color: var(--me-text);
|
||||
white-space: normal;
|
||||
word-break: break-all;
|
||||
line-height: 1.35;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.custom-field-input {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.custom-field-input { flex: 1; min-width: 0; width: 100%; }
|
||||
.custom-field-input :deep(.el-input__wrapper) {
|
||||
background: rgba(255, 255, 255, 0.06);
|
||||
box-shadow: inset 0 0 0 1px rgba(170, 110, 250, 0.18);
|
||||
background: var(--me-card-bg) !important;
|
||||
box-shadow: inset 0 0 0 1px var(--me-card-border) !important;
|
||||
min-height: 30px;
|
||||
}
|
||||
.custom-field-input :deep(.el-input__inner) {
|
||||
color: var(--me-text) !important;
|
||||
font-weight: 550;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
.custom-field-input :deep(.el-input__wrapper:hover) {
|
||||
box-shadow: inset 0 0 0 1px rgba(170, 110, 250, 0.32);
|
||||
box-shadow: inset 0 0 0 1px rgba(var(--mg-primary-rgb), 0.45) !important;
|
||||
}
|
||||
|
||||
.custom-field-input :deep(.el-input__wrapper.is-focus) {
|
||||
box-shadow: inset 0 0 0 1px rgba(190, 130, 255, 0.55);
|
||||
box-shadow:
|
||||
inset 0 0 0 1px var(--mg-primary),
|
||||
0 0 0 3px rgba(var(--mg-primary-rgb), 0.14) !important;
|
||||
}
|
||||
.add-field-select { width: 100%; }
|
||||
|
||||
.add-field-select {
|
||||
width: 100%;
|
||||
/* 运行状态:胶囊行 */
|
||||
.prop-status-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 5px;
|
||||
max-height: 280px;
|
||||
overflow: auto;
|
||||
padding-right: 2px;
|
||||
}
|
||||
|
||||
.prop-status-list { display: flex; flex-direction: column; gap: 4px; }
|
||||
.prop-status-item {
|
||||
display: flex; justify-content: space-between; align-items: center;
|
||||
padding: 5px 8px;
|
||||
border-radius: 4px;
|
||||
background: rgba(255,255,255,0.03);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 7px 10px;
|
||||
border-radius: 8px;
|
||||
background: var(--me-inset);
|
||||
border: 1px solid var(--me-card-border);
|
||||
font-size: 11.5px;
|
||||
}
|
||||
.prop-status-key { color: rgba(170, 200, 240, 0.85); font-weight: 500; }
|
||||
.prop-status-value {
|
||||
color: rgba(232, 245, 255, 0.95);
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
||||
max-width: 60%;
|
||||
.prop-status-item:nth-child(even) {
|
||||
background: rgba(var(--mg-primary-rgb), 0.08);
|
||||
}
|
||||
.prop-status-key {
|
||||
color: var(--me-text-muted);
|
||||
font-weight: 600;
|
||||
flex: none;
|
||||
max-width: 46%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.prop-status-value {
|
||||
color: var(--me-text);
|
||||
font-family: var(--mg-font-mono, ui-monospace, Menlo, Consolas, monospace);
|
||||
font-variant-numeric: tabular-nums;
|
||||
max-width: 54%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.prop-actions {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 6px;
|
||||
gap: 8px;
|
||||
}
|
||||
.prop-action-btn {
|
||||
appearance: none; border: 0; cursor: pointer;
|
||||
padding: 7px 10px;
|
||||
border-radius: 7px;
|
||||
background: linear-gradient(135deg, rgba(170, 110, 250, 0.20) 0%, rgba(120, 70, 220, 0.20) 100%);
|
||||
border: 1px solid rgba(170, 110, 250, 0.35);
|
||||
color: rgba(232, 215, 245, 0.95);
|
||||
appearance: none;
|
||||
cursor: pointer;
|
||||
padding: 9px 11px;
|
||||
border-radius: 8px;
|
||||
background: var(--mg-primary);
|
||||
border: 1px solid var(--mg-primary);
|
||||
color: #fff;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
display: flex; align-items: center; justify-content: flex-start; gap: 6px;
|
||||
transition: background .14s ease, border-color .14s ease, transform .12s ease, box-shadow .14s ease;
|
||||
font-weight: 600;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
gap: 6px;
|
||||
text-align: left;
|
||||
transition: background .12s ease, border-color .12s ease, box-shadow .12s ease, transform .12s ease;
|
||||
}
|
||||
.prop-action-btn:nth-child(2n) {
|
||||
background: var(--me-card-bg);
|
||||
border-color: rgba(var(--mg-primary-rgb), 0.38);
|
||||
color: var(--mg-primary);
|
||||
}
|
||||
.prop-action-btn:nth-child(2n) .prop-action-icon {
|
||||
color: var(--mg-primary);
|
||||
}
|
||||
.prop-action-btn:hover:not(:disabled) {
|
||||
background: linear-gradient(135deg, rgba(170, 110, 250, 0.62) 0%, rgba(255, 110, 220, 0.55) 100%);
|
||||
border-color: rgba(255, 130, 220, 0.7);
|
||||
background: var(--mg-primary-hover, var(--mg-primary));
|
||||
border-color: var(--mg-primary-hover, var(--mg-primary));
|
||||
color: #fff;
|
||||
box-shadow: 0 4px 12px rgba(180, 90, 220, 0.40);
|
||||
box-shadow: 0 4px 12px var(--me-active-glow);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
.prop-action-btn:active:not(:disabled) { transform: scale(0.985); }
|
||||
.prop-action-btn:disabled { opacity: 0.6; cursor: progress; }
|
||||
.prop-action-icon {
|
||||
font-size: 9px;
|
||||
color: rgba(255, 200, 250, 0.85);
|
||||
flex: none;
|
||||
.prop-action-btn:nth-child(2n):hover:not(:disabled) {
|
||||
background: rgba(var(--mg-primary-rgb), 0.1);
|
||||
border-color: var(--mg-primary);
|
||||
color: var(--mg-primary);
|
||||
box-shadow: 0 2px 8px var(--me-active-glow);
|
||||
}
|
||||
.prop-action-btn:nth-child(2n):hover:not(:disabled) .prop-action-icon {
|
||||
color: var(--mg-primary);
|
||||
}
|
||||
.prop-action-btn:disabled { opacity: 0.55; cursor: progress; transform: none; }
|
||||
.prop-action-icon { font-size: 9px; color: rgba(255, 255, 255, 0.92); flex: none; }
|
||||
.prop-action-spinner {
|
||||
display: inline-block;
|
||||
animation: prop-spin .8s linear infinite;
|
||||
@@ -1237,56 +1375,63 @@ function onDelete() {
|
||||
}
|
||||
.prop-action-label {
|
||||
flex: 1;
|
||||
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
@keyframes prop-spin {
|
||||
from { transform: rotate(0deg); }
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
.defaults-pane { display: flex; flex-direction: column; gap: 10px; }
|
||||
.defaults-alert { margin-bottom: 4px; }
|
||||
.defaults-empty { padding: 40px 12px; }
|
||||
.defaults-alert { margin-bottom: 0; }
|
||||
.defaults-empty { padding: 32px 12px; }
|
||||
.defaults-form .el-button { margin-top: 8px; }
|
||||
.defaults-form :deep(.el-form-item) { margin-bottom: 10px; }
|
||||
/* 图层 Tab:白底数据块(深侧栏上的可读「岛」),各主题下表头/单元格均用深字 */
|
||||
.defaults-form :deep(.el-form-item__label) { color: var(--me-text-muted); }
|
||||
|
||||
.layer-table {
|
||||
margin: 6px 0;
|
||||
border-radius: 8px;
|
||||
margin: 0;
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
background: #fff;
|
||||
box-shadow: 0 0 0 1px rgba(124, 58, 237, 0.14);
|
||||
background: rgba(255, 255, 255, 0.06);
|
||||
border: 1px solid rgba(255, 255, 255, 0.10);
|
||||
box-shadow: 0 4px 14px rgba(0, 0, 0, 0.18);
|
||||
}
|
||||
.layer-table :deep(.el-table__inner-wrapper) {
|
||||
background: #fff;
|
||||
.layer-table :deep(.el-table__inner-wrapper),
|
||||
.layer-table :deep(.el-table),
|
||||
.layer-table :deep(.el-table tr) {
|
||||
background: transparent !important;
|
||||
}
|
||||
.layer-table :deep(th.el-table__cell) {
|
||||
background: #f5f3ff !important;
|
||||
color: #2d1b69 !important;
|
||||
font-weight: 600;
|
||||
background: rgba(var(--mg-primary-rgb), 0.16) !important;
|
||||
color: var(--me-text) !important;
|
||||
font-weight: 650;
|
||||
border-color: rgba(255, 255, 255, 0.08) !important;
|
||||
}
|
||||
.layer-table :deep(td.el-table__cell) {
|
||||
background: #fff !important;
|
||||
color: #1a0f3d !important;
|
||||
font-weight: 500;
|
||||
}
|
||||
.layer-table :deep(.cell) {
|
||||
color: #1a0f3d !important;
|
||||
background: transparent !important;
|
||||
color: var(--me-text) !important;
|
||||
border-color: rgba(255, 255, 255, 0.06) !important;
|
||||
}
|
||||
.layer-table :deep(.cell) { color: inherit !important; }
|
||||
.layer-table :deep(tr:hover > td.el-table__cell) {
|
||||
background: #ede9fe !important;
|
||||
background: rgba(var(--mg-primary-rgb), 0.12) !important;
|
||||
}
|
||||
.new-layer { margin-top: 10px; }
|
||||
.new-layer :deep(.el-input__wrapper) {
|
||||
background: rgba(0, 0, 0, 0.22) !important;
|
||||
box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.10) !important;
|
||||
}
|
||||
.new-layer { margin-top: 8px; }
|
||||
.new-layer :deep(.el-input__inner) {
|
||||
color: #1a0f3d;
|
||||
color: var(--me-text);
|
||||
font-weight: 500;
|
||||
}
|
||||
.new-layer :deep(.el-input__inner::placeholder) {
|
||||
color: #7a6b9a;
|
||||
color: var(--me-text-muted);
|
||||
}
|
||||
.new-layer :deep(.el-input-group__append .el-button) {
|
||||
color: #5b21b6;
|
||||
color: var(--mg-accent);
|
||||
font-weight: 600;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -43,15 +43,15 @@ function formatXY(v: number) {
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
padding: 4px 14px;
|
||||
background: rgba(20, 8, 40, 0.85);
|
||||
border-top: 1px solid rgba(255,255,255,0.08);
|
||||
color: rgba(232,215,245,0.85);
|
||||
background: var(--me-chrome-2);
|
||||
border-top: 1px solid var(--me-border);
|
||||
color: var(--me-text-muted);
|
||||
font-size: 12px;
|
||||
font-family: ui-monospace, Menlo, Consolas, monospace;
|
||||
font-family: var(--mg-font-mono, ui-monospace, Menlo, Consolas, monospace);
|
||||
height: 28px;
|
||||
}
|
||||
.spacer { flex: 1; }
|
||||
.seg b { color: var(--mg-accent, #c4a4ff); }
|
||||
.seg b { color: var(--mg-accent); }
|
||||
.seg .on { color: #67c23a; }
|
||||
.seg .off { color: #a0a0a0; }
|
||||
.seg.ok { color: #67c23a; }
|
||||
|
||||
@@ -99,7 +99,6 @@ const groups: ToolGroup[] = [
|
||||
{ id: 'transform.move', label: '移动', glyph: '✥' },
|
||||
{ id: 'transform.rotate', label: '旋转', glyph: '⟳' },
|
||||
{ id: 'transform.scale', label: '缩放', glyph: '⤡' },
|
||||
{ id: 'transform.duplicate', label: '复制副本', glyph: '⎘', tip: 'Ctrl+D' },
|
||||
{ id: 'transform.copy', label: '复制', glyph: '📋', tip: 'Ctrl+C' },
|
||||
{ id: 'transform.paste', label: '粘贴', glyph: '📥', tip: 'Ctrl+V' },
|
||||
{ id: 'transform.delete', label: '删除', glyph: '🗑', tip: 'Delete' },
|
||||
@@ -152,12 +151,12 @@ function onClick(id: EditToolId) { emit('select-tool', id) }
|
||||
.edit-tool-rail {
|
||||
height: 100%;
|
||||
width: 170px;
|
||||
background: linear-gradient(180deg, rgba(28, 12, 56, 0.85) 0%, rgba(18, 6, 36, 0.95) 100%);
|
||||
border-right: 1px solid rgba(255,255,255,0.10);
|
||||
background: linear-gradient(180deg, var(--me-chrome-1) 0%, var(--me-chrome-2) 100%);
|
||||
border-right: 1px solid var(--me-border);
|
||||
padding: 4px 0 12px;
|
||||
backdrop-filter: blur(10px);
|
||||
display: flex; flex-direction: column;
|
||||
box-shadow: inset -1px 0 0 rgba(255,255,255,0.04);
|
||||
box-shadow: inset -1px 0 0 var(--me-card-border, transparent);
|
||||
}
|
||||
.rail-scroll { height: 100%; }
|
||||
.rail-group {
|
||||
@@ -168,12 +167,12 @@ function onClick(id: EditToolId) { emit('select-tool', id) }
|
||||
display: flex; align-items: center; gap: 6px;
|
||||
font-size: 10px;
|
||||
letter-spacing: 2px;
|
||||
color: rgba(190, 160, 230, 0.7);
|
||||
color: var(--me-text-muted);
|
||||
text-transform: uppercase;
|
||||
margin: 4px 0 6px;
|
||||
font-weight: 600;
|
||||
}
|
||||
.rail-group-bar { flex: 1; height: 1px; background: rgba(190, 160, 230, 0.2); }
|
||||
.rail-group-bar { flex: 1; height: 1px; background: rgba(var(--mg-primary-rgb), 0.18); }
|
||||
.rail-group-list {
|
||||
display: flex; flex-direction: column;
|
||||
gap: 2px;
|
||||
@@ -181,7 +180,7 @@ function onClick(id: EditToolId) { emit('select-tool', id) }
|
||||
.rail-divider {
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
background: linear-gradient(90deg, rgba(255,255,255,0.0) 0%, rgba(255,255,255,0.08) 50%, rgba(255,255,255,0.0) 100%);
|
||||
background: linear-gradient(90deg, transparent 0%, var(--me-border) 50%, transparent 100%);
|
||||
margin: 6px 0 2px;
|
||||
}
|
||||
.rail-btn {
|
||||
@@ -192,8 +191,8 @@ function onClick(id: EditToolId) { emit('select-tool', id) }
|
||||
min-height: 32px;
|
||||
padding: 0 8px;
|
||||
border-radius: 6px;
|
||||
background: rgba(255,255,255,0.04);
|
||||
color: rgba(232,215,245,0.92);
|
||||
background: var(--me-surface);
|
||||
color: var(--me-text);
|
||||
display: flex; align-items: center;
|
||||
gap: 8px;
|
||||
font-size: 12.5px;
|
||||
@@ -202,16 +201,16 @@ function onClick(id: EditToolId) { emit('select-tool', id) }
|
||||
transition: background .14s ease, color .14s ease, box-shadow .14s ease, transform .12s ease;
|
||||
}
|
||||
.rail-btn:hover {
|
||||
background: rgba(150, 90, 230, 0.20);
|
||||
color: #fff;
|
||||
background: var(--me-hover);
|
||||
color: var(--mg-primary);
|
||||
}
|
||||
.rail-btn:active {
|
||||
transform: scale(0.985);
|
||||
}
|
||||
.rail-btn.rail-btn--active {
|
||||
background: linear-gradient(120deg, rgba(170, 110, 250, 0.92) 0%, rgba(120, 70, 220, 0.92) 100%);
|
||||
background: var(--mg-primary);
|
||||
color: #fff;
|
||||
box-shadow: 0 3px 10px rgba(140, 80, 230, 0.45), inset 0 0 0 1px rgba(255,255,255,0.18);
|
||||
box-shadow: 0 3px 10px var(--me-active-glow), inset 0 0 0 1px rgba(255,255,255,0.18);
|
||||
}
|
||||
.rail-btn-glyph {
|
||||
width: 20px;
|
||||
@@ -236,21 +235,21 @@ function onClick(id: EditToolId) { emit('select-tool', id) }
|
||||
position: absolute;
|
||||
left: 8px; right: 8px; top: 0;
|
||||
height: 1px;
|
||||
background: linear-gradient(90deg, rgba(255,255,255,0.0) 0%, rgba(220, 140, 230, 0.35) 50%, rgba(255,255,255,0.0) 100%);
|
||||
background: linear-gradient(90deg, transparent 0%, rgba(var(--mg-accent-rgb), 0.4) 50%, transparent 100%);
|
||||
}
|
||||
.rail-btn--ai {
|
||||
background: linear-gradient(135deg, rgba(120, 70, 220, 0.55) 0%, rgba(255, 90, 200, 0.55) 100%);
|
||||
background: var(--mg-primary);
|
||||
color: #fff;
|
||||
font-weight: 600;
|
||||
box-shadow: 0 2px 10px rgba(220, 90, 220, 0.30), inset 0 0 0 1px rgba(255,255,255,0.20);
|
||||
box-shadow: 0 2px 10px var(--me-active-glow), inset 0 0 0 1px rgba(255,255,255,0.20);
|
||||
}
|
||||
.rail-btn--ai:hover {
|
||||
background: linear-gradient(135deg, rgba(150, 90, 240, 0.85) 0%, rgba(255, 110, 220, 0.85) 100%);
|
||||
background: var(--mg-primary-hover);
|
||||
color: #fff;
|
||||
box-shadow: 0 4px 14px rgba(230, 110, 230, 0.55), inset 0 0 0 1px rgba(255,255,255,0.30);
|
||||
box-shadow: 0 4px 14px var(--me-active-glow), inset 0 0 0 1px rgba(255,255,255,0.30);
|
||||
}
|
||||
.rail-btn--ai .rail-btn-glyph {
|
||||
font-size: 16px;
|
||||
text-shadow: 0 0 8px rgba(255, 200, 250, 0.6);
|
||||
text-shadow: 0 0 8px rgba(var(--mg-accent-rgb), 0.55);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -148,16 +148,16 @@ function mark(on: boolean): string {
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
padding: 8px 14px;
|
||||
background: linear-gradient(180deg, rgba(50, 22, 88, 0.95) 0%, rgba(34, 14, 64, 0.95) 100%);
|
||||
border-bottom: 1px solid rgba(255,255,255,0.12);
|
||||
background: linear-gradient(180deg, var(--me-chrome-1) 0%, var(--me-chrome-2) 100%);
|
||||
border-bottom: 1px solid var(--me-border);
|
||||
backdrop-filter: blur(10px);
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.25);
|
||||
box-shadow: 0 2px 8px rgba(var(--mg-shadow-rgb, 22, 58, 74), 0.08);
|
||||
}
|
||||
.spacer { flex: 1; }
|
||||
.el-divider--vertical { margin: 0 6px; background: rgba(255,255,255,0.15); }
|
||||
.el-divider--vertical { margin: 0 6px; background: var(--me-border); }
|
||||
|
||||
.topbar-btn {
|
||||
color: #fdf6ff !important;
|
||||
color: var(--me-text) !important;
|
||||
font-weight: 600;
|
||||
font-size: 13.5px;
|
||||
padding: 6px 12px;
|
||||
@@ -166,22 +166,23 @@ function mark(on: boolean): string {
|
||||
transition: background .15s ease, color .15s ease;
|
||||
}
|
||||
.topbar-btn:hover {
|
||||
color: #fff !important;
|
||||
background: rgba(170, 110, 250, 0.28) !important;
|
||||
color: var(--mg-primary) !important;
|
||||
background: var(--me-hover) !important;
|
||||
}
|
||||
.topbar-btn:focus {
|
||||
background: rgba(170, 110, 250, 0.30) !important;
|
||||
background: var(--me-hover) !important;
|
||||
}
|
||||
|
||||
.topbar-icon-btn {
|
||||
color: rgba(253, 246, 255, 0.92) !important;
|
||||
background: rgba(255,255,255,0.05) !important;
|
||||
border: 1px solid rgba(255,255,255,0.15) !important;
|
||||
color: var(--me-text) !important;
|
||||
background: var(--me-surface) !important;
|
||||
border: 1px solid var(--me-border) !important;
|
||||
font-size: 15px;
|
||||
}
|
||||
.topbar-icon-btn:hover:not(.is-disabled) {
|
||||
background: rgba(170, 110, 250, 0.30) !important;
|
||||
border-color: rgba(170, 110, 250, 0.55) !important;
|
||||
background: var(--me-hover) !important;
|
||||
border-color: rgba(var(--mg-primary-rgb), 0.55) !important;
|
||||
color: var(--mg-primary) !important;
|
||||
}
|
||||
.topbar-icon-btn.is-disabled {
|
||||
opacity: 0.4;
|
||||
@@ -194,11 +195,19 @@ function mark(on: boolean): string {
|
||||
height: auto;
|
||||
border-radius: 6px;
|
||||
margin-right: 4px;
|
||||
--el-button-bg-color: var(--mg-primary) !important;
|
||||
--el-button-border-color: var(--mg-primary) !important;
|
||||
--el-button-hover-bg-color: var(--mg-primary-hover) !important;
|
||||
--el-button-hover-border-color: var(--mg-primary-hover) !important;
|
||||
--el-button-active-bg-color: var(--mg-primary-active) !important;
|
||||
--el-button-active-border-color: var(--mg-primary-active) !important;
|
||||
--el-button-text-color: #fff !important;
|
||||
--el-button-hover-text-color: #fff !important;
|
||||
}
|
||||
|
||||
.topbar-filter-menu :deep(.topbar-filter-header) {
|
||||
font-size: 11px;
|
||||
color: rgba(232, 215, 245, 0.55);
|
||||
color: var(--me-text-muted);
|
||||
cursor: default;
|
||||
pointer-events: none;
|
||||
padding-top: 6px;
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
|
||||
<div class="aside-footer">
|
||||
<span class="aside-footer-badge">v1.6</span>
|
||||
<span v-if="!ui.sidebarCollapsed">{{ ui.activeTheme.name }} · {{ ui.activeTheme.preview }}</span>
|
||||
<span v-if="!ui.sidebarCollapsed">{{ ui.activeTheme.name }}</span>
|
||||
</div>
|
||||
</el-aside>
|
||||
|
||||
@@ -82,6 +82,15 @@
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item v-if="auth.scope === 'Platform'" command="wizard">配置向导</el-dropdown-item>
|
||||
<el-dropdown-item command="status">服务状态</el-dropdown-item>
|
||||
<el-dropdown-item divided disabled class="legacy-theme-label">高级 · 兼容主题</el-dropdown-item>
|
||||
<el-dropdown-item
|
||||
v-for="t in ui.legacyThemes"
|
||||
:key="t.id"
|
||||
:command="`theme:${t.id}`"
|
||||
:class="{ 'is-legacy-active': t.id === ui.themeId }">
|
||||
<span class="legacy-swatch" :style="{ background: t.preview }" />
|
||||
{{ t.name }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item command="logout" divided>退出登录</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
@@ -97,10 +106,13 @@
|
||||
</el-main>
|
||||
|
||||
<el-footer class="footer">
|
||||
<span>{{ scopeLabel }} · webVRender :{{ vrPort }} · API {{ apiBase }}</span>
|
||||
<span>{{ scopeLabel }} · Projection :{{ slPort }} · API {{ apiBase }}</span>
|
||||
</el-footer>
|
||||
</el-container>
|
||||
</el-container>
|
||||
|
||||
<!-- 全局 AI 助手:右侧抽屉 + 右下角悬浮唤起,所有界面可呼出。后端要求 Platform 权限,故按 scope 显示。 -->
|
||||
<AiAssistantDrawer v-if="auth.scope === 'Platform'" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -113,13 +125,14 @@ import { useAuthStore } from '@/stores/auth'
|
||||
import { useUiStore } from '@/stores/ui'
|
||||
import ScopeSwitcher from '@/components/ScopeSwitcher.vue'
|
||||
import ThemeSwitcher from '@/components/ThemeSwitcher.vue'
|
||||
import AiAssistantDrawer from '@/components/assistant/AiAssistantDrawer.vue'
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const auth = useAuthStore()
|
||||
const ui = useUiStore()
|
||||
|
||||
const vrPort = computed(() => (import.meta.env.VITE_VRENDER_HOST as string | undefined)?.split(':')[1] ?? '8223')
|
||||
const slPort = computed(() => (import.meta.env.VITE_SL_PORT as string | undefined) ?? '8222')
|
||||
const apiBase = computed(() => (import.meta.env.VITE_API_BASE as string | undefined) ?? '/api')
|
||||
|
||||
const initial = computed(() => (auth.user?.displayName ?? auth.user?.username ?? '?').slice(0, 1).toUpperCase())
|
||||
@@ -171,6 +184,10 @@ const activePath = computed(() => {
|
||||
const currentTitle = computed(() => (route.meta.title as string | undefined) ?? '')
|
||||
|
||||
function onUserCommand(cmd: string) {
|
||||
if (cmd.startsWith('theme:')) {
|
||||
ui.setThemeId(cmd.slice('theme:'.length))
|
||||
return
|
||||
}
|
||||
if (cmd === 'logout') {
|
||||
auth.logout()
|
||||
router.push('/login')
|
||||
@@ -187,6 +204,7 @@ function onUserCommand(cmd: string) {
|
||||
.app-shell {
|
||||
position: relative;
|
||||
height: 100vh;
|
||||
height: 100dvh;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
@@ -236,6 +254,7 @@ function onUserCommand(cmd: string) {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
height: 100vh;
|
||||
height: 100dvh;
|
||||
}
|
||||
|
||||
/* ─────────────── Sidebar ─────────────── */
|
||||
@@ -549,3 +568,25 @@ function onUserCommand(cmd: string) {
|
||||
flex-direction: column;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style>
|
||||
/* 用户菜单「兼容主题」项(teleport 到 body) */
|
||||
.legacy-theme-label {
|
||||
font-size: 11px !important;
|
||||
opacity: 0.65;
|
||||
cursor: default !important;
|
||||
}
|
||||
.legacy-swatch {
|
||||
display: inline-block;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 50%;
|
||||
margin-right: 8px;
|
||||
vertical-align: middle;
|
||||
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
.el-dropdown-menu__item.is-legacy-active {
|
||||
color: var(--mg-primary) !important;
|
||||
font-weight: 600;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -2,8 +2,11 @@ import { defineStore } from 'pinia'
|
||||
|
||||
import {
|
||||
DEFAULT_THEME_ID,
|
||||
LEGACY_THEMES,
|
||||
PRIMARY_THEMES,
|
||||
applyThemeVars,
|
||||
findTheme,
|
||||
resolveThemeId,
|
||||
THEMES,
|
||||
type ThemePreset
|
||||
} from '@/styles/themes'
|
||||
@@ -28,6 +31,9 @@ interface UiState {
|
||||
}
|
||||
|
||||
const STORAGE_KEY = 'simple.ui.state'
|
||||
/** Outpost 壳层上线:旧本地主题只换了 id、观感几乎不变 → 升 schema 强制一次默认浅色 */
|
||||
const THEME_SCHEMA_KEY = 'simple.ui.themeSchema'
|
||||
const THEME_SCHEMA_VERSION = 2
|
||||
|
||||
const DEFAULT_STATE: UiState = {
|
||||
sidebarCollapsed: false,
|
||||
@@ -39,14 +45,33 @@ const DEFAULT_STATE: UiState = {
|
||||
function loadInitial(): UiState {
|
||||
try {
|
||||
const raw = localStorage.getItem(STORAGE_KEY)
|
||||
if (!raw) return { ...DEFAULT_STATE }
|
||||
if (!raw) {
|
||||
localStorage.setItem(THEME_SCHEMA_KEY, String(THEME_SCHEMA_VERSION))
|
||||
return { ...DEFAULT_STATE }
|
||||
}
|
||||
const parsed = JSON.parse(raw) as Partial<UiState>
|
||||
return {
|
||||
let themeId = resolveThemeId(parsed.themeId ?? DEFAULT_STATE.themeId)
|
||||
const schema = Number(localStorage.getItem(THEME_SCHEMA_KEY) || '0')
|
||||
const migrated = schema < THEME_SCHEMA_VERSION
|
||||
if (migrated) {
|
||||
// ponytail: 一次强制 outpost-light,兼容主题仍可从高级菜单切回
|
||||
themeId = DEFAULT_THEME_ID
|
||||
localStorage.setItem(THEME_SCHEMA_KEY, String(THEME_SCHEMA_VERSION))
|
||||
}
|
||||
const state: UiState = {
|
||||
sidebarCollapsed: parsed.sidebarCollapsed ?? DEFAULT_STATE.sidebarCollapsed,
|
||||
theme: parsed.theme === 'dark' ? 'dark' : 'light',
|
||||
themeId: parsed.themeId ?? DEFAULT_STATE.themeId,
|
||||
themeId,
|
||||
themeOverrides: parsed.themeOverrides ?? {}
|
||||
}
|
||||
if (migrated) {
|
||||
try {
|
||||
localStorage.setItem(STORAGE_KEY, JSON.stringify(state))
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
return state
|
||||
} catch (err) {
|
||||
console.warn('[ui.store] localStorage 中的 UI 状态解析失败,已回退默认:', err)
|
||||
return { ...DEFAULT_STATE }
|
||||
@@ -60,8 +85,16 @@ export const useUiStore = defineStore('ui', {
|
||||
activeTheme(state): ThemePreset {
|
||||
return mergeThemePreset(findTheme(state.themeId), state.themeOverrides[state.themeId])
|
||||
},
|
||||
/** 所有可选主题列表 */
|
||||
/** 顶栏精选主题(浅色 / 紫色 / 蓝色) */
|
||||
availableThemes(): ThemePreset[] {
|
||||
return PRIMARY_THEMES
|
||||
},
|
||||
/** 高级 → 兼容主题(旧深色霓虹) */
|
||||
legacyThemes(): ThemePreset[] {
|
||||
return LEGACY_THEMES
|
||||
},
|
||||
/** 全量(自定义配色等内部用) */
|
||||
allThemes(): ThemePreset[] {
|
||||
return THEMES
|
||||
}
|
||||
},
|
||||
@@ -107,7 +140,7 @@ export const useUiStore = defineStore('ui', {
|
||||
this.persist()
|
||||
},
|
||||
|
||||
/** 切换品牌主题色板 */
|
||||
/** 切换品牌主题色板(精选或兼容均可) */
|
||||
setThemeId(id: string) {
|
||||
const preset = findTheme(id)
|
||||
this.themeId = preset.id
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,10 +1,9 @@
|
||||
/**
|
||||
* 主题色板预设
|
||||
*
|
||||
* 设计原则:
|
||||
* - 每个主题提供一组 CSS 变量,运行时通过 document.documentElement.style.setProperty 注入
|
||||
* - 命名遵循 theme.css 中既有的 --mg-* 体系
|
||||
* - 工业紫色 (industrial-purple) 为默认主题:紫蓝渐变,深蓝底 + 天蓝高光
|
||||
* 主路径:Outpost 壳层三套精选(浅色 / 紫色 / 蓝色)
|
||||
* 高级:旧深色霓虹主题(LEGACY_THEMES)
|
||||
* 规范:docs/superpowers/specs/2026-07-25-migu-outpost-ui-lock-design.md
|
||||
*/
|
||||
|
||||
export interface ThemePreset {
|
||||
@@ -15,20 +14,77 @@ export interface ThemePreset {
|
||||
preview: string
|
||||
/** 强调高光,用于 UI 预览的次代表色 */
|
||||
previewAccent: string
|
||||
/** Outpost 浅色壳(纸白主区);legacy 深色为 false */
|
||||
outpostShell?: boolean
|
||||
vars: Record<string, string>
|
||||
}
|
||||
|
||||
export const DEFAULT_THEME_ID = 'fame-lavender'
|
||||
/** Outpost 浅色壳共用表面(纸白卡、深字、关 orb);各主题可覆盖 card/tint */
|
||||
const OUTPOST_SHELL_VARS: Record<string, string> = {
|
||||
'--mg-bg-card-rgb': '255, 254, 253',
|
||||
'--mg-bg-card-hi-rgb': '251, 249, 253',
|
||||
'--mg-bg-card-darker-rgb': '245, 240, 251',
|
||||
'--mg-text-tint-rgb': '40, 33, 58',
|
||||
'--mg-text-hi-rgb': '117, 109, 133',
|
||||
'--mg-orb-a-opacity': '0',
|
||||
'--mg-orb-b-opacity': '0',
|
||||
'--mg-orb-c-opacity': '0',
|
||||
'--mg-radius-sm': '10px',
|
||||
'--mg-radius': '16px',
|
||||
'--mg-radius-lg': '20px',
|
||||
'--mg-radius-xl': '24px'
|
||||
}
|
||||
|
||||
export const THEMES: ThemePreset[] = [
|
||||
{
|
||||
id: 'fame-lavender',
|
||||
name: '迷毂浅紫',
|
||||
description: '参考 FAME 系统:深紫侧栏 + 浅紫白主区,柔和耐看,长时间使用不刺眼',
|
||||
preview: '#7c3aed',
|
||||
previewAccent: '#a855f7',
|
||||
/**
|
||||
* 地图编辑 chrome(顶栏/工具轨/属性栏/底栏)跟主题色相走。
|
||||
* 默认真色壳(跟侧栏);浅色 Harbor 等可显式覆盖 --me-* 为浅壳。
|
||||
* 画布始终保持深色。
|
||||
*/
|
||||
function withMapEditorChrome(vars: Record<string, string>): Record<string, string> {
|
||||
const primaryRgb = vars['--mg-primary-rgb'] ?? '30, 58, 95'
|
||||
const chrome1 = vars['--mg-bg-aside-1'] ?? '#1e3a5f'
|
||||
const chrome2 = vars['--mg-bg-aside-2'] ?? '#0b1220'
|
||||
const chromeRgb = vars['--mg-bg-aside-rgb'] ?? primaryRgb
|
||||
const lightChrome = vars['--me-chrome-mode'] === 'light'
|
||||
return {
|
||||
...vars,
|
||||
'--me-chrome-1': vars['--me-chrome-1'] ?? chrome1,
|
||||
'--me-chrome-2': vars['--me-chrome-2'] ?? chrome2,
|
||||
'--me-chrome-rgb': vars['--me-chrome-rgb'] ?? chromeRgb,
|
||||
'--me-border': vars['--me-border'] ?? (lightChrome ? 'rgba(30, 58, 95, 0.12)' : 'rgba(255, 255, 255, 0.12)'),
|
||||
'--me-text': vars['--me-text'] ?? (lightChrome ? 'rgba(11, 18, 32, 0.92)' : 'rgba(245, 242, 255, 0.94)'),
|
||||
'--me-text-muted': vars['--me-text-muted'] ?? (lightChrome ? 'rgba(11, 18, 32, 0.55)' : 'rgba(210, 200, 235, 0.68)'),
|
||||
'--me-surface': vars['--me-surface'] ?? (lightChrome ? 'rgba(30, 58, 95, 0.04)' : 'rgba(255, 255, 255, 0.05)'),
|
||||
'--me-hover': vars['--me-hover'] ?? (lightChrome ? `rgba(${primaryRgb}, 0.08)` : `rgba(${primaryRgb}, 0.28)`),
|
||||
'--me-active-glow': vars['--me-active-glow'] ?? `rgba(${primaryRgb}, 0.28)`,
|
||||
'--me-card-bg': vars['--me-card-bg'] ?? (lightChrome ? 'rgba(255, 255, 255, 0.96)' : 'rgba(0, 0, 0, 0.22)'),
|
||||
'--me-card-border': vars['--me-card-border'] ?? (lightChrome ? '#e2e6ec' : 'rgba(255, 255, 255, 0.08)'),
|
||||
'--me-inset': vars['--me-inset'] ?? (lightChrome ? '#f4f6f8' : 'rgba(0, 0, 0, 0.28)')
|
||||
}
|
||||
}
|
||||
|
||||
function outpostPreset(
|
||||
partial: Omit<ThemePreset, 'outpostShell' | 'vars'> & { vars: Record<string, string> }
|
||||
): ThemePreset {
|
||||
return {
|
||||
...partial,
|
||||
outpostShell: true,
|
||||
vars: withMapEditorChrome({ ...OUTPOST_SHELL_VARS, ...partial.vars })
|
||||
}
|
||||
}
|
||||
|
||||
/** 默认:Outpost 浅色(对齐 fairylandtech.amerc.ai) */
|
||||
export const DEFAULT_THEME_ID = 'outpost-light'
|
||||
|
||||
/** 主路径精选三色(顶栏 ThemeSwitcher) */
|
||||
export const PRIMARY_THEMES: ThemePreset[] = [
|
||||
outpostPreset({
|
||||
id: 'outpost-light',
|
||||
name: '浅色',
|
||||
description: '纯白壳 + 紫色品牌点缀;地图壳白,画布深色',
|
||||
preview: '#ffffff',
|
||||
previewAccent: '#7c3aed',
|
||||
vars: {
|
||||
// 主色保留紫色(与 FAME 一致),但 hover/accent 使用更明亮的 violet/purple
|
||||
'--mg-primary': '#7c3aed',
|
||||
'--mg-primary-rgb': '124, 58, 237',
|
||||
'--mg-primary-hover': '#8b5cf6',
|
||||
@@ -36,84 +92,139 @@ export const THEMES: ThemePreset[] = [
|
||||
'--mg-primary-active': '#5b21b6',
|
||||
'--mg-accent': '#a855f7',
|
||||
'--mg-accent-rgb': '168, 85, 247',
|
||||
|
||||
'--mg-primary-light-3': '#a78bfa',
|
||||
'--mg-primary-light-5': '#c4b5fd',
|
||||
'--mg-primary-light-7': '#ddd6fe',
|
||||
'--mg-primary-light-8': '#ede9fe',
|
||||
'--mg-primary-light-9': '#f5f3ff',
|
||||
'--mg-primary-dark-2': '#6d28d9',
|
||||
|
||||
// 主区背景:浅薰衣草灰(v3:从近白下沉到 ~92% 明度,让白卡片靠阴影清晰浮起)
|
||||
'--mg-bg-app-1': '#edeaf5',
|
||||
'--mg-bg-app-2': '#e7e4f1',
|
||||
'--mg-bg-app-3': '#f1eef8',
|
||||
'--mg-bg-app-deep-rgb': '231, 228, 241',
|
||||
|
||||
// 侧栏:FAME 深紫渐变 #2d1b69 -> #1a1040
|
||||
'--mg-bg-aside-1': '#2d1b69',
|
||||
'--mg-bg-aside-2': '#1a1040',
|
||||
'--mg-bg-aside-rgb': '45, 27, 105',
|
||||
|
||||
// 卡片:白底(在 fame-lavender 专属规则中走纯 #fff)
|
||||
'--mg-bg-app-1': '#ffffff',
|
||||
'--mg-bg-app-2': '#ffffff',
|
||||
'--mg-bg-app-3': '#ffffff',
|
||||
'--mg-bg-app-deep-rgb': '255, 255, 255',
|
||||
'--mg-bg-aside-1': '#ffffff',
|
||||
'--mg-bg-aside-2': '#fafafa',
|
||||
'--mg-bg-aside-rgb': '255, 255, 255',
|
||||
'--mg-shadow-rgb': '76, 29, 149',
|
||||
'--mg-bg-card-rgb': '255, 255, 255',
|
||||
'--mg-bg-card-hi-rgb': '249, 246, 255',
|
||||
'--mg-bg-card-darker-rgb':'245, 240, 251',
|
||||
'--mg-shadow-rgb': '124, 58, 237',
|
||||
|
||||
// 文字:深紫标题 #2d1b69, 中灰正文 #606266
|
||||
'--mg-bg-card-hi-rgb': '255, 255, 255',
|
||||
'--mg-bg-card-darker-rgb': '248, 250, 252',
|
||||
'--mg-text-tint-rgb': '45, 27, 105',
|
||||
'--mg-text-hi-rgb': '96, 98, 102',
|
||||
|
||||
// 关闭装饰性 orb
|
||||
'--mg-orb-a-opacity': '0',
|
||||
'--mg-orb-b-opacity': '0',
|
||||
'--mg-orb-c-opacity': '0'
|
||||
'--mg-text-hi-rgb': '109, 40, 217',
|
||||
/* 地图编辑:纯白壳 */
|
||||
'--me-chrome-mode': 'light',
|
||||
'--me-chrome-1': '#ffffff',
|
||||
'--me-chrome-2': '#ffffff',
|
||||
'--me-chrome-rgb': '255, 255, 255',
|
||||
'--me-border': 'rgba(124, 58, 237, 0.12)',
|
||||
'--me-text': 'rgba(45, 27, 105, 0.92)',
|
||||
'--me-text-muted': 'rgba(76, 29, 149, 0.55)',
|
||||
'--me-surface': 'rgba(124, 58, 237, 0.04)',
|
||||
'--me-hover': 'rgba(124, 58, 237, 0.08)',
|
||||
'--me-active-glow': 'rgba(124, 58, 237, 0.18)',
|
||||
'--me-card-bg': '#ffffff',
|
||||
'--me-card-border': '#e5e7eb',
|
||||
'--me-inset': '#f8fafc'
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'industrial-purple',
|
||||
name: '星云紫',
|
||||
description: '科幻紫主色,辅以蓝紫渐变,磨砂玻璃 · 霓虹辉光',
|
||||
}),
|
||||
outpostPreset({
|
||||
id: 'outpost-purple',
|
||||
name: '紫色',
|
||||
description: '同壳层、更饱和紫品牌;侧栏深蓝紫',
|
||||
preview: '#7c3aed',
|
||||
previewAccent: '#c4b5fd',
|
||||
previewAccent: '#a855f7',
|
||||
vars: {
|
||||
'--mg-primary': '#7c3aed',
|
||||
'--mg-primary-rgb': '124, 58, 237',
|
||||
'--mg-primary-hover': '#8b5cf6',
|
||||
'--mg-primary-hover-rgb': '139, 92, 246',
|
||||
'--mg-primary-active': '#5b21b6',
|
||||
'--mg-accent': '#a855f7',
|
||||
'--mg-accent-rgb': '168, 85, 247',
|
||||
'--mg-primary-light-3': '#a78bfa',
|
||||
'--mg-primary-light-5': '#c4b5fd',
|
||||
'--mg-primary-light-7': '#ddd6fe',
|
||||
'--mg-primary-light-8': '#ede9fe',
|
||||
'--mg-primary-light-9': '#f5f3ff',
|
||||
'--mg-primary-dark-2': '#6d28d9',
|
||||
'--mg-bg-app-1': '#f3eefc',
|
||||
'--mg-bg-app-2': '#edeaf5',
|
||||
'--mg-bg-app-3': '#f8f5ff',
|
||||
'--mg-bg-app-deep-rgb': '243, 238, 252',
|
||||
'--mg-bg-aside-1': '#2d1b69',
|
||||
'--mg-bg-aside-2': '#1a1040',
|
||||
'--mg-bg-aside-rgb': '45, 27, 105',
|
||||
'--mg-shadow-rgb': '124, 58, 237'
|
||||
}
|
||||
}),
|
||||
outpostPreset({
|
||||
id: 'outpost-blue',
|
||||
name: '蓝色',
|
||||
description: '同壳层、蓝科技感;侧栏深蓝',
|
||||
preview: '#3c78d2',
|
||||
previewAccent: '#6ea8f0',
|
||||
vars: {
|
||||
'--mg-primary': '#3c78d2',
|
||||
'--mg-primary-rgb': '60, 120, 210',
|
||||
'--mg-primary-hover': '#4d8ae0',
|
||||
'--mg-primary-hover-rgb': '77, 138, 224',
|
||||
'--mg-primary-active': '#2a5fad',
|
||||
'--mg-accent': '#6ea8f0',
|
||||
'--mg-accent-rgb': '110, 168, 240',
|
||||
'--mg-primary-light-3': '#6ea8f0',
|
||||
'--mg-primary-light-5': '#9bc4f5',
|
||||
'--mg-primary-light-7': '#c5ddf9',
|
||||
'--mg-primary-light-8': '#e3effc',
|
||||
'--mg-primary-light-9': '#f3f7fc',
|
||||
'--mg-primary-dark-2': '#2a5fad',
|
||||
'--mg-bg-app-1': '#f3f6fb',
|
||||
'--mg-bg-app-2': '#eef2f8',
|
||||
'--mg-bg-app-3': '#f8fafc',
|
||||
'--mg-bg-app-deep-rgb': '243, 246, 251',
|
||||
'--mg-bg-aside-1': '#0f2744',
|
||||
'--mg-bg-aside-2': '#0a1a30',
|
||||
'--mg-bg-aside-rgb': '15, 39, 68',
|
||||
'--mg-shadow-rgb': '30, 58, 95'
|
||||
}
|
||||
})
|
||||
]
|
||||
|
||||
/** 高级兼容:旧深色霓虹主题(用户菜单「兼容主题」) */
|
||||
export const LEGACY_THEMES: ThemePreset[] = [
|
||||
{
|
||||
id: 'industrial-purple',
|
||||
name: '星云紫',
|
||||
description: '科幻紫主色 · 磨砂玻璃 · 霓虹辉光(兼容)',
|
||||
preview: '#7c3aed',
|
||||
previewAccent: '#c4b5fd',
|
||||
outpostShell: false,
|
||||
vars: {
|
||||
// ── 主色:明亮 violet(紫),主导整个画面 ──
|
||||
'--mg-primary': '#7c3aed',
|
||||
'--mg-primary-rgb': '124, 58, 237',
|
||||
// hover/辉光:偏蓝紫 indigo,带来氛围流动感
|
||||
'--mg-primary-hover': '#8b5cf6',
|
||||
'--mg-primary-hover-rgb': '139, 92, 246',
|
||||
'--mg-primary-active': '#5b21b6',
|
||||
// accent:浅紫高光,边缘霓虹与点缀
|
||||
'--mg-accent': '#c4b5fd',
|
||||
'--mg-accent-rgb': '196, 181, 253',
|
||||
|
||||
'--mg-primary-light-3': '#9d6cf2',
|
||||
'--mg-primary-light-5': '#b594f7',
|
||||
'--mg-primary-light-7': '#6f4eab',
|
||||
'--mg-primary-light-8': '#4c357a',
|
||||
'--mg-primary-light-9': '#2d1f55',
|
||||
'--mg-primary-dark-2': '#4c1d95',
|
||||
|
||||
// ── 背景:深紫黑底,带 indigo 余晖 ──
|
||||
'--mg-bg-app-1': '#0e0a24',
|
||||
'--mg-bg-app-2': '#1f1147',
|
||||
'--mg-bg-app-3': '#06031a',
|
||||
'--mg-bg-app-deep-rgb': '6, 3, 26',
|
||||
|
||||
'--mg-bg-aside-1': '#0c0820',
|
||||
'--mg-bg-aside-2': '#03020e',
|
||||
'--mg-bg-aside-rgb': '10, 6, 30',
|
||||
|
||||
'--mg-bg-card-rgb': '38, 24, 78',
|
||||
'--mg-bg-card-hi-rgb': '58, 38, 110',
|
||||
'--mg-bg-card-darker-rgb': '20, 12, 48',
|
||||
'--mg-shadow-rgb': '8, 2, 24',
|
||||
|
||||
'--mg-text-tint-rgb': '242, 232, 255',
|
||||
'--mg-text-hi-rgb': '220, 200, 252',
|
||||
|
||||
'--mg-orb-a-opacity': '0.55',
|
||||
'--mg-orb-b-opacity': '0.50',
|
||||
'--mg-orb-c-opacity': '0.40'
|
||||
@@ -122,9 +233,10 @@ export const THEMES: ThemePreset[] = [
|
||||
{
|
||||
id: 'deep-azure',
|
||||
name: '深邃蓝',
|
||||
description: '科技感深蓝,沉静稳重',
|
||||
description: '科技感深蓝,沉静稳重(兼容)',
|
||||
preview: '#1565c0',
|
||||
previewAccent: '#82b1ff',
|
||||
outpostShell: false,
|
||||
vars: {
|
||||
'--mg-primary': '#1565c0',
|
||||
'--mg-primary-rgb': '21, 101, 192',
|
||||
@@ -133,28 +245,23 @@ export const THEMES: ThemePreset[] = [
|
||||
'--mg-primary-active': '#0d47a1',
|
||||
'--mg-accent': '#82b1ff',
|
||||
'--mg-accent-rgb': '130, 177, 255',
|
||||
|
||||
'--mg-primary-light-3': '#5a8fce',
|
||||
'--mg-primary-light-5': '#83aedb',
|
||||
'--mg-primary-light-7': '#adcde7',
|
||||
'--mg-primary-light-8': '#c4dbed',
|
||||
'--mg-primary-light-9': '#e3eef8',
|
||||
'--mg-primary-dark-2': '#0d47a1',
|
||||
|
||||
'--mg-bg-app-1': '#051a3a',
|
||||
'--mg-bg-app-2': '#0a2f6b',
|
||||
'--mg-bg-app-3': '#020a1f',
|
||||
'--mg-bg-app-deep-rgb': '2, 8, 26',
|
||||
|
||||
'--mg-bg-aside-1': '#06122a',
|
||||
'--mg-bg-aside-2': '#02060f',
|
||||
'--mg-bg-aside-rgb': '8, 18, 42',
|
||||
|
||||
'--mg-bg-card-rgb': '12, 36, 80',
|
||||
'--mg-bg-card-hi-rgb': '18, 50, 110',
|
||||
'--mg-bg-card-darker-rgb': '6, 18, 44',
|
||||
'--mg-shadow-rgb': '2, 4, 14',
|
||||
|
||||
'--mg-text-tint-rgb': '218, 234, 252',
|
||||
'--mg-text-hi-rgb': '170, 200, 240'
|
||||
}
|
||||
@@ -162,9 +269,10 @@ export const THEMES: ThemePreset[] = [
|
||||
{
|
||||
id: 'emerald-forge',
|
||||
name: '翡翠绿',
|
||||
description: '机械翡翠绿,沉稳活力',
|
||||
description: '机械翡翠绿(兼容)',
|
||||
preview: '#00796b',
|
||||
previewAccent: '#80cbc4',
|
||||
outpostShell: false,
|
||||
vars: {
|
||||
'--mg-primary': '#00796b',
|
||||
'--mg-primary-rgb': '0, 121, 107',
|
||||
@@ -173,28 +281,23 @@ export const THEMES: ThemePreset[] = [
|
||||
'--mg-primary-active': '#004d40',
|
||||
'--mg-accent': '#80cbc4',
|
||||
'--mg-accent-rgb': '128, 203, 196',
|
||||
|
||||
'--mg-primary-light-3': '#4ca094',
|
||||
'--mg-primary-light-5': '#79b9b0',
|
||||
'--mg-primary-light-7': '#a6d2cc',
|
||||
'--mg-primary-light-8': '#bfdfdb',
|
||||
'--mg-primary-light-9': '#e1f0ee',
|
||||
'--mg-primary-dark-2': '#004d40',
|
||||
|
||||
'--mg-bg-app-1': '#06241f',
|
||||
'--mg-bg-app-2': '#0a4538',
|
||||
'--mg-bg-app-3': '#020f0c',
|
||||
'--mg-bg-app-deep-rgb': '2, 12, 10',
|
||||
|
||||
'--mg-bg-aside-1': '#061a16',
|
||||
'--mg-bg-aside-2': '#020a08',
|
||||
'--mg-bg-aside-rgb': '8, 26, 22',
|
||||
|
||||
'--mg-bg-card-rgb': '10, 50, 42',
|
||||
'--mg-bg-card-hi-rgb': '14, 66, 56',
|
||||
'--mg-bg-card-darker-rgb': '6, 28, 24',
|
||||
'--mg-shadow-rgb': '2, 10, 8',
|
||||
|
||||
'--mg-text-tint-rgb': '220, 244, 240',
|
||||
'--mg-text-hi-rgb': '170, 220, 210'
|
||||
}
|
||||
@@ -202,9 +305,10 @@ export const THEMES: ThemePreset[] = [
|
||||
{
|
||||
id: 'crimson-iron',
|
||||
name: '熔铁赤',
|
||||
description: '熔铁炉火般的暗红工业风',
|
||||
description: '暗红工业风(兼容)',
|
||||
preview: '#b71c1c',
|
||||
previewAccent: '#ff8a80',
|
||||
outpostShell: false,
|
||||
vars: {
|
||||
'--mg-primary': '#b71c1c',
|
||||
'--mg-primary-rgb': '183, 28, 28',
|
||||
@@ -213,28 +317,23 @@ export const THEMES: ThemePreset[] = [
|
||||
'--mg-primary-active': '#8a0c0c',
|
||||
'--mg-accent': '#ff8a80',
|
||||
'--mg-accent-rgb': '255, 138, 128',
|
||||
|
||||
'--mg-primary-light-3': '#c95a5a',
|
||||
'--mg-primary-light-5': '#d68585',
|
||||
'--mg-primary-light-7': '#e4afaf',
|
||||
'--mg-primary-light-8': '#edc8c8',
|
||||
'--mg-primary-light-9': '#f7e6e6',
|
||||
'--mg-primary-dark-2': '#8a0c0c',
|
||||
|
||||
'--mg-bg-app-1': '#2a0606',
|
||||
'--mg-bg-app-2': '#4a0c0c',
|
||||
'--mg-bg-app-3': '#150303',
|
||||
'--mg-bg-app-deep-rgb': '20, 4, 4',
|
||||
|
||||
'--mg-bg-aside-1': '#200505',
|
||||
'--mg-bg-aside-2': '#0c0202',
|
||||
'--mg-bg-aside-rgb': '36, 6, 6',
|
||||
|
||||
'--mg-bg-card-rgb': '60, 10, 10',
|
||||
'--mg-bg-card-hi-rgb': '80, 14, 14',
|
||||
'--mg-bg-card-darker-rgb': '34, 5, 5',
|
||||
'--mg-shadow-rgb': '10, 1, 1',
|
||||
|
||||
'--mg-text-tint-rgb': '252, 220, 220',
|
||||
'--mg-text-hi-rgb': '240, 175, 175'
|
||||
}
|
||||
@@ -242,9 +341,10 @@ export const THEMES: ThemePreset[] = [
|
||||
{
|
||||
id: 'graphite-steel',
|
||||
name: '石墨钢',
|
||||
description: '中性冷灰,纯粹工业',
|
||||
description: '中性冷灰(兼容)',
|
||||
preview: '#455a64',
|
||||
previewAccent: '#b0bec5',
|
||||
outpostShell: false,
|
||||
vars: {
|
||||
'--mg-primary': '#455a64',
|
||||
'--mg-primary-rgb': '69, 90, 100',
|
||||
@@ -253,28 +353,23 @@ export const THEMES: ThemePreset[] = [
|
||||
'--mg-primary-active': '#263238',
|
||||
'--mg-accent': '#b0bec5',
|
||||
'--mg-accent-rgb': '176, 190, 197',
|
||||
|
||||
'--mg-primary-light-3': '#7a8a92',
|
||||
'--mg-primary-light-5': '#9aa6ac',
|
||||
'--mg-primary-light-7': '#bac2c7',
|
||||
'--mg-primary-light-8': '#ccd2d6',
|
||||
'--mg-primary-light-9': '#e8ebed',
|
||||
'--mg-primary-dark-2': '#263238',
|
||||
|
||||
'--mg-bg-app-1': '#161e24',
|
||||
'--mg-bg-app-2': '#243038',
|
||||
'--mg-bg-app-3': '#0a0e12',
|
||||
'--mg-bg-app-deep-rgb': '10, 14, 18',
|
||||
|
||||
'--mg-bg-aside-1': '#11181d',
|
||||
'--mg-bg-aside-2': '#060a0d',
|
||||
'--mg-bg-aside-rgb': '18, 26, 32',
|
||||
|
||||
'--mg-bg-card-rgb': '30, 42, 50',
|
||||
'--mg-bg-card-hi-rgb': '42, 56, 66',
|
||||
'--mg-bg-card-darker-rgb': '16, 22, 28',
|
||||
'--mg-shadow-rgb': '4, 6, 10',
|
||||
|
||||
'--mg-text-tint-rgb': '226, 232, 238',
|
||||
'--mg-text-hi-rgb': '186, 198, 210'
|
||||
}
|
||||
@@ -282,9 +377,10 @@ export const THEMES: ThemePreset[] = [
|
||||
{
|
||||
id: 'amber-forge',
|
||||
name: '琥珀金',
|
||||
description: '高对比的橙金调,警示与活力并存',
|
||||
description: '橙金调(兼容)',
|
||||
preview: '#e65100',
|
||||
previewAccent: '#ffb74d',
|
||||
outpostShell: false,
|
||||
vars: {
|
||||
'--mg-primary': '#e65100',
|
||||
'--mg-primary-rgb': '230, 81, 0',
|
||||
@@ -293,44 +389,69 @@ export const THEMES: ThemePreset[] = [
|
||||
'--mg-primary-active': '#bf360c',
|
||||
'--mg-accent': '#ffb74d',
|
||||
'--mg-accent-rgb': '255, 183, 77',
|
||||
|
||||
'--mg-primary-light-3': '#ec8344',
|
||||
'--mg-primary-light-5': '#f2a474',
|
||||
'--mg-primary-light-7': '#f7c4a3',
|
||||
'--mg-primary-light-8': '#fad6bc',
|
||||
'--mg-primary-light-9': '#fdeadd',
|
||||
'--mg-primary-dark-2': '#bf360c',
|
||||
|
||||
'--mg-bg-app-1': '#2a1404',
|
||||
'--mg-bg-app-2': '#4a2a0c',
|
||||
'--mg-bg-app-3': '#150a02',
|
||||
'--mg-bg-app-deep-rgb': '20, 10, 4',
|
||||
|
||||
'--mg-bg-aside-1': '#200f04',
|
||||
'--mg-bg-aside-2': '#0c0602',
|
||||
'--mg-bg-aside-rgb': '34, 16, 6',
|
||||
|
||||
'--mg-bg-card-rgb': '62, 32, 8',
|
||||
'--mg-bg-card-hi-rgb': '82, 44, 12',
|
||||
'--mg-bg-card-darker-rgb': '34, 16, 4',
|
||||
'--mg-shadow-rgb': '10, 4, 1',
|
||||
|
||||
'--mg-text-tint-rgb': '252, 232, 210',
|
||||
'--mg-text-hi-rgb': '240, 196, 140'
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
export function findTheme(id: string | undefined | null): ThemePreset {
|
||||
if (!id) return THEMES[0]
|
||||
return THEMES.find((t) => t.id === id) ?? THEMES[0]
|
||||
/** 全量(查找用):精选 + 兼容 */
|
||||
export const THEMES: ThemePreset[] = [...PRIMARY_THEMES, ...LEGACY_THEMES]
|
||||
|
||||
/** 旧 id → 新精选 id */
|
||||
const THEME_ALIASES: Record<string, string> = {
|
||||
'fame-lavender': 'outpost-purple',
|
||||
'outpost-fairyland': 'outpost-light'
|
||||
}
|
||||
|
||||
/** 把主题变量写入 :root;多次调用幂等,会覆盖同名属性 */
|
||||
export function resolveThemeId(id: string | undefined | null): string {
|
||||
if (!id) return DEFAULT_THEME_ID
|
||||
const aliased = THEME_ALIASES[id] ?? id
|
||||
if (THEMES.some((t) => t.id === aliased)) return aliased
|
||||
return DEFAULT_THEME_ID
|
||||
}
|
||||
|
||||
export function findTheme(id: string | undefined | null): ThemePreset {
|
||||
const resolved = resolveThemeId(id)
|
||||
return THEMES.find((t) => t.id === resolved) ?? PRIMARY_THEMES[0]
|
||||
}
|
||||
|
||||
export function isLegacyThemeId(id: string | undefined | null): boolean {
|
||||
if (!id) return false
|
||||
const resolved = THEME_ALIASES[id] ?? id
|
||||
return LEGACY_THEMES.some((t) => t.id === resolved)
|
||||
}
|
||||
|
||||
/** 把主题变量写入 :root;Outpost 壳写 data-shell="outpost" 供浅色 CSS 命中 */
|
||||
export function applyThemeVars(preset: ThemePreset): void {
|
||||
const root = document.documentElement
|
||||
for (const [key, value] of Object.entries(preset.vars)) {
|
||||
// preset.vars 在 outpostPreset 里已跑过 withMapEditorChrome;再跑一次以补全缺省 --me-*
|
||||
const vars = withMapEditorChrome({ ...preset.vars })
|
||||
for (const [key, value] of Object.entries(vars)) {
|
||||
if (key === '--me-chrome-mode') continue
|
||||
root.style.setProperty(key, value)
|
||||
}
|
||||
root.setAttribute('data-theme', preset.id)
|
||||
if (preset.outpostShell) {
|
||||
root.setAttribute('data-shell', 'outpost')
|
||||
} else {
|
||||
root.removeAttribute('data-shell')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,17 +4,10 @@
|
||||
<div class="bg-overlay" />
|
||||
<div class="bg-orb bg-orb-a" />
|
||||
<div class="bg-orb bg-orb-b" />
|
||||
<div class="bg-orb bg-orb-c" />
|
||||
<div class="bg-stars" aria-hidden="true">
|
||||
<span v-for="(s, i) in starStyles" :key="i" :style="s" />
|
||||
</div>
|
||||
|
||||
<div class="login-card">
|
||||
<!-- 左侧品牌 hero -->
|
||||
<div class="hero">
|
||||
<div class="hero-stars" aria-hidden="true">
|
||||
<span v-for="(s, i) in heroStarStyles" :key="i" :style="s" />
|
||||
</div>
|
||||
<div class="hero-brand">
|
||||
<span class="hero-mark">
|
||||
<img src="/FRLD-logo-white-no_title.png" alt="迷毂" class="hero-mark-img" />
|
||||
@@ -212,25 +205,6 @@ const rules: FormRules = {
|
||||
|
||||
const year = computed(() => new Date().getFullYear())
|
||||
|
||||
// 生成 n 个随机分布、随机大小与闪烁节奏的小白点,营造梦幻星点。
|
||||
function makeStars(n: number): Record<string, string>[] {
|
||||
return Array.from({ length: n }, () => {
|
||||
const size = 1.5 + Math.random() * 2.8
|
||||
return {
|
||||
top: `${Math.random() * 100}%`,
|
||||
left: `${Math.random() * 100}%`,
|
||||
width: `${size}px`,
|
||||
height: `${size}px`,
|
||||
opacity: `${0.35 + Math.random() * 0.5}`,
|
||||
animationDelay: `${-Math.random() * 6}s`,
|
||||
animationDuration: `${2.6 + Math.random() * 3.4}s`
|
||||
} as Record<string, string>
|
||||
})
|
||||
}
|
||||
// 背景层(卡片之外)+ 品牌区(卡片内深色区)两组星点
|
||||
const starStyles = makeStars(48)
|
||||
const heroStarStyles = makeStars(22)
|
||||
|
||||
async function submit() {
|
||||
if (!formRef.value) return
|
||||
await formRef.value.validate(async (ok) => {
|
||||
@@ -269,13 +243,8 @@ async function submit() {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* ════════════════════════════════════════════════════════════════════════
|
||||
* 登录页固定配色(不随全局主题切换变化)。
|
||||
* 统一用一组本地 --lg-* 变量(午夜靛蓝 + 青色霓虹),保证无论用户切到哪个
|
||||
* 主题,登录界面始终是同一套高级、克制的深色质感。
|
||||
* ════════════════════════════════════════════════════════════════════════ */
|
||||
/* Outpost 登录:纸白主区 + plum 品牌柱(对齐 fairylandtech.amerc.ai) */
|
||||
.login-page {
|
||||
/* 梦幻晨曦渐变:粉紫(左上) → 蓝紫(中) → 淡蓝(右下),高明度柔光,参考产品视觉稿。 */
|
||||
position: relative;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
@@ -283,11 +252,11 @@ async function submit() {
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
background:
|
||||
radial-gradient(ellipse 75% 55% at 16% 14%, #dcbef7 0%, transparent 55%),
|
||||
radial-gradient(ellipse 80% 65% at 86% 90%, #abc9f5 0%, transparent 58%),
|
||||
linear-gradient(135deg, #cbb2f0 0%, #b7adec 46%, #a7c6f2 100%);
|
||||
color: var(--lg-text);
|
||||
font-family: 'PingFang SC', 'Microsoft YaHei', 'Segoe UI', sans-serif;
|
||||
radial-gradient(ellipse 70% 50% at 15% 10%, rgba(117, 67, 232, 0.14), transparent 55%),
|
||||
radial-gradient(ellipse 60% 45% at 90% 85%, rgba(155, 124, 255, 0.1), transparent 50%),
|
||||
#f6f3fb;
|
||||
color: #28213a;
|
||||
font-family: 'Inter', 'PingFang SC', 'Microsoft YaHei', 'Segoe UI', sans-serif;
|
||||
}
|
||||
|
||||
/* ===== 背景层 ===== */
|
||||
@@ -329,25 +298,18 @@ async function submit() {
|
||||
animation: drift 20s ease-in-out infinite;
|
||||
}
|
||||
.bg-orb-a {
|
||||
width: 460px; height: 460px;
|
||||
top: -150px; left: -130px;
|
||||
background: radial-gradient(circle, var(--lg-primary-hover) 0%, var(--lg-primary) 52%, transparent 80%);
|
||||
opacity: 0.24;
|
||||
width: 420px; height: 420px;
|
||||
top: -140px; left: -100px;
|
||||
background: radial-gradient(circle, rgba(155, 124, 255, 0.55) 0%, rgba(117, 67, 232, 0.35) 55%, transparent 80%);
|
||||
opacity: 0.22;
|
||||
}
|
||||
.bg-orb-b {
|
||||
width: 600px; height: 600px;
|
||||
bottom: -220px; right: -190px;
|
||||
background: radial-gradient(circle, var(--lg-accent) 0%, var(--lg-primary-deep) 58%, transparent 86%);
|
||||
opacity: 0.18;
|
||||
width: 480px; height: 480px;
|
||||
bottom: -180px; right: -140px;
|
||||
background: radial-gradient(circle, rgba(117, 67, 232, 0.4) 0%, rgba(155, 124, 255, 0.2) 55%, transparent 85%);
|
||||
opacity: 0.16;
|
||||
animation-delay: -7s;
|
||||
}
|
||||
.bg-orb-c {
|
||||
width: 320px; height: 320px;
|
||||
top: 32%; right: 9%;
|
||||
background: radial-gradient(circle, var(--lg-primary-hover) 0%, var(--lg-primary) 60%, transparent 90%);
|
||||
opacity: 0.14;
|
||||
animation-delay: -13s;
|
||||
}
|
||||
@keyframes drift {
|
||||
0%, 100% { transform: translate(0, 0) scale(1); }
|
||||
50% { transform: translate(18px, -28px) scale(1.05); }
|
||||
@@ -368,7 +330,7 @@ async function submit() {
|
||||
50% { opacity: 0.95; transform: scale(1.2); }
|
||||
}
|
||||
|
||||
/* ===== 卡片:双栏 hero ===== */
|
||||
/* ===== 卡片:Outpost 纸白 + plum 品牌柱 ===== */
|
||||
.login-card {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
@@ -378,34 +340,19 @@ async function submit() {
|
||||
display: grid;
|
||||
grid-template-columns: 360px 1fr;
|
||||
overflow: hidden;
|
||||
border-radius: 22px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||
/* 登录框本体:紫 → 靛蓝的「紫蓝混合」渐变,梦幻且不发灰;高不透明度保证文字清晰 */
|
||||
background:
|
||||
linear-gradient(140deg,
|
||||
rgba(48, 26, 96, 0.95) 0%,
|
||||
rgba(33, 26, 92, 0.955) 50%,
|
||||
rgba(22, 26, 84, 0.96) 100%);
|
||||
backdrop-filter: blur(26px) saturate(150%);
|
||||
-webkit-backdrop-filter: blur(26px) saturate(150%);
|
||||
border-radius: 20px;
|
||||
border: 1px solid rgba(40, 33, 58, 0.08);
|
||||
background: #fffefd;
|
||||
box-shadow:
|
||||
0 44px 110px rgba(0, 0, 0, 0.62),
|
||||
0 0 70px rgba(var(--lg-primary-rgb), 0.24),
|
||||
0 0 0 1px rgba(var(--lg-accent-rgb), 0.16) inset,
|
||||
0 1px 0 rgba(255, 255, 255, 0.18) inset;
|
||||
0 1px 2px rgba(40, 33, 58, 0.04),
|
||||
0 24px 56px rgba(54, 35, 78, 0.12);
|
||||
}
|
||||
/* 顶部霓虹高光线 */
|
||||
.login-card::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0; left: 12%; right: 12%;
|
||||
height: 1px;
|
||||
background: linear-gradient(90deg,
|
||||
transparent 0%,
|
||||
rgba(var(--lg-accent-rgb), 0.55) 30%,
|
||||
rgba(255, 255, 255, 0.92) 50%,
|
||||
rgba(var(--lg-accent-rgb), 0.55) 70%,
|
||||
transparent 100%);
|
||||
background: linear-gradient(90deg, transparent 0%, rgba(117, 67, 232, 0.35) 50%, transparent 100%);
|
||||
pointer-events: none;
|
||||
z-index: 3;
|
||||
}
|
||||
@@ -415,12 +362,8 @@ async function submit() {
|
||||
position: relative;
|
||||
padding: 44px 34px 34px;
|
||||
color: #fff;
|
||||
/* 品牌区紫蓝混合:左上紫光球 + 右下蓝光球,叠在深紫底上 */
|
||||
background:
|
||||
radial-gradient(ellipse at 16% 18%, rgba(123, 77, 255, 0.34) 0%, transparent 56%),
|
||||
radial-gradient(ellipse at 88% 84%, rgba(94, 124, 255, 0.32) 0%, transparent 58%),
|
||||
linear-gradient(170deg, rgba(var(--lg-primary-rgb), 0.26) 0%, rgba(var(--lg-aside-rgb), 0.42) 60%, rgba(var(--lg-deep-rgb), 0.55) 100%);
|
||||
border-right: 1px solid rgba(255, 255, 255, 0.08);
|
||||
background: linear-gradient(165deg, #3f2454 0%, #241530 100%);
|
||||
border-right: 1px solid rgba(255, 255, 255, 0.06);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
@@ -550,56 +493,53 @@ async function submit() {
|
||||
padding: 44px 44px 32px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
/* 表单侧:紫 → 蓝的洗光,与品牌区一致的「紫蓝混合」基调 */
|
||||
background:
|
||||
linear-gradient(135deg, rgba(123, 77, 255, 0.14) 0%, rgba(94, 124, 255, 0.10) 100%);
|
||||
background: #fffefd;
|
||||
color: #28213a;
|
||||
}
|
||||
.panel-head { margin-bottom: 26px; }
|
||||
.panel-title {
|
||||
font-size: 30px; font-weight: 700;
|
||||
letter-spacing: 12px;
|
||||
background: linear-gradient(135deg, #ffffff 0%, var(--lg-accent) 100%);
|
||||
-webkit-background-clip: text;
|
||||
background-clip: text;
|
||||
color: transparent;
|
||||
text-shadow: 0 0 22px rgba(var(--lg-primary-hover-rgb), 0.5);
|
||||
font-size: 28px; font-weight: 700;
|
||||
letter-spacing: 8px;
|
||||
color: #28213a;
|
||||
background: none;
|
||||
-webkit-text-fill-color: unset;
|
||||
text-shadow: none;
|
||||
}
|
||||
.panel-sub {
|
||||
margin-top: 6px;
|
||||
font-size: 12.5px;
|
||||
color: var(--lg-text-dim);
|
||||
color: #756d85;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
.glass-form { flex: 1; display: flex; flex-direction: column; }
|
||||
.glass-form :deep(.el-form-item) { margin-bottom: 18px; }
|
||||
|
||||
/* 输入框 透明玻璃 */
|
||||
.glass-form :deep(.el-input__wrapper) {
|
||||
background: rgba(255, 255, 255, 0.06) !important;
|
||||
box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.16) inset !important;
|
||||
border-radius: 10px !important;
|
||||
transition: all .25s;
|
||||
background: #f6f3fb !important;
|
||||
box-shadow: 0 0 0 1px rgba(40, 33, 58, 0.1) inset !important;
|
||||
border-radius: 12px !important;
|
||||
transition: all .2s;
|
||||
}
|
||||
.glass-form :deep(.el-input__wrapper:hover) {
|
||||
background: rgba(255, 255, 255, 0.11) !important;
|
||||
box-shadow: 0 0 0 1px rgba(var(--lg-accent-rgb), 0.5) inset !important;
|
||||
background: #fbf9fd !important;
|
||||
box-shadow: 0 0 0 1px rgba(117, 67, 232, 0.35) inset !important;
|
||||
}
|
||||
.glass-form :deep(.el-input__wrapper.is-focus) {
|
||||
background: rgba(255, 255, 255, 0.13) !important;
|
||||
background: #fff !important;
|
||||
box-shadow:
|
||||
0 0 0 1px rgba(var(--lg-accent-rgb), 0.85) inset,
|
||||
0 0 20px rgba(var(--lg-primary-hover-rgb), 0.45) !important;
|
||||
0 0 0 1px rgba(117, 67, 232, 0.55) inset,
|
||||
0 0 0 3px rgba(117, 67, 232, 0.12) !important;
|
||||
}
|
||||
.glass-form :deep(.el-input__inner) {
|
||||
color: #fff !important;
|
||||
-webkit-text-fill-color: #fff !important;
|
||||
color: #28213a !important;
|
||||
-webkit-text-fill-color: #28213a !important;
|
||||
font-size: 14px;
|
||||
height: 42px;
|
||||
}
|
||||
.glass-form :deep(.el-input__inner::placeholder) { color: rgba(255, 255, 255, 0.42); }
|
||||
.glass-form :deep(.el-input__inner::placeholder) { color: #a8a0c0; }
|
||||
.glass-form :deep(.el-input__prefix-inner > :first-child),
|
||||
.glass-form :deep(.el-input__suffix-inner) { color: rgba(255, 255, 255, 0.55); }
|
||||
.glass-form :deep(.el-input__suffix-inner) { color: #756d85; }
|
||||
|
||||
/* scope 双卡 */
|
||||
.scope-item :deep(.el-form-item__content) { width: 100%; }
|
||||
@@ -611,45 +551,42 @@ async function submit() {
|
||||
display: flex; align-items: center; gap: 6px;
|
||||
margin-bottom: 8px;
|
||||
font-size: 12px;
|
||||
color: var(--lg-text-soft);
|
||||
color: #756d85;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
.launch-mode-title { font-weight: 500; }
|
||||
.launch-mode-help {
|
||||
color: var(--lg-text-dim);
|
||||
color: #a8a0c0;
|
||||
cursor: help;
|
||||
font-size: 14px;
|
||||
}
|
||||
.launch-mode-help:hover { color: var(--lg-accent); }
|
||||
.launch-mode-help:hover { color: #7543e8; }
|
||||
.launch-wrap { margin-bottom: 4px; }
|
||||
.launch-tip { max-width: 280px; line-height: 1.6; font-size: 12px; }
|
||||
.launch-tip > div + div { margin-top: 6px; }
|
||||
.scope-tab {
|
||||
appearance: none;
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
border: 1px solid rgba(255, 255, 255, 0.16);
|
||||
background: #f6f3fb;
|
||||
border: 1px solid rgba(40, 33, 58, 0.1);
|
||||
border-radius: 12px;
|
||||
padding: 14px 14px;
|
||||
display: flex; align-items: center; gap: 12px;
|
||||
color: rgba(255, 255, 255, 0.78);
|
||||
color: #28213a;
|
||||
cursor: pointer;
|
||||
transition: all .2s;
|
||||
text-align: left;
|
||||
}
|
||||
.scope-tab:hover {
|
||||
background: rgba(255, 255, 255, 0.11);
|
||||
color: #fff;
|
||||
background: #fbf9fd;
|
||||
border-color: rgba(117, 67, 232, 0.3);
|
||||
color: #28213a;
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
.scope-tab.active {
|
||||
background: linear-gradient(135deg,
|
||||
rgba(var(--lg-primary-rgb), 0.55) 0%,
|
||||
rgba(var(--lg-accent-rgb), 0.28) 100%);
|
||||
border-color: rgba(var(--lg-accent-rgb), 0.75);
|
||||
background: linear-gradient(135deg, #7543e8 0%, #9b7cff 100%);
|
||||
border-color: transparent;
|
||||
color: #fff;
|
||||
box-shadow:
|
||||
0 0 22px rgba(var(--lg-primary-hover-rgb), 0.45),
|
||||
0 0 0 1px rgba(var(--lg-accent-rgb), 0.45) inset;
|
||||
box-shadow: 0 8px 20px rgba(117, 67, 232, 0.28);
|
||||
}
|
||||
.scope-tab .el-icon { font-size: 20px; }
|
||||
.scope-meta { display: flex; flex-direction: column; line-height: 1.3; min-width: 0; }
|
||||
@@ -660,107 +597,83 @@ async function submit() {
|
||||
.row { display: flex; align-items: center; }
|
||||
.row-between { justify-content: space-between; margin: -6px 0 10px; }
|
||||
.remember :deep(.el-checkbox__label) {
|
||||
color: rgba(255, 255, 255, 0.9) !important;
|
||||
color: #28213a !important;
|
||||
font-size: 12.5px;
|
||||
text-shadow: 0 1px 2px rgba(6, 9, 18, 0.4);
|
||||
text-shadow: none;
|
||||
}
|
||||
.remember :deep(.el-checkbox__inner) {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border-color: rgba(255, 255, 255, 0.32);
|
||||
background: #fff;
|
||||
border-color: rgba(40, 33, 58, 0.25);
|
||||
}
|
||||
.remember :deep(.el-checkbox.is-checked .el-checkbox__inner) {
|
||||
background: var(--lg-primary);
|
||||
border-color: var(--lg-primary);
|
||||
background: #7543e8;
|
||||
border-color: #7543e8;
|
||||
}
|
||||
.adv-link { color: var(--lg-text-soft) !important; font-size: 12.5px; }
|
||||
.adv-link:hover { color: #fff !important; }
|
||||
.adv-link { color: #756d85 !important; font-size: 12.5px; }
|
||||
.adv-link:hover { color: #7543e8 !important; }
|
||||
|
||||
/* 高级折叠 */
|
||||
.adv-collapse { background: transparent !important; border: none !important; margin-bottom: 16px; }
|
||||
.adv-collapse :deep(.el-collapse-item__wrap),
|
||||
.adv-collapse :deep(.el-collapse-item__header) {
|
||||
background: transparent !important;
|
||||
border-color: rgba(255, 255, 255, 0.14) !important;
|
||||
color: var(--lg-text-soft) !important;
|
||||
border-color: rgba(40, 33, 58, 0.1) !important;
|
||||
color: #756d85 !important;
|
||||
}
|
||||
.adv-title { font-size: 12px; letter-spacing: 1px; }
|
||||
.adv-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px 16px; padding-top: 6px; }
|
||||
.adv-grid :deep(.el-form-item__label) {
|
||||
color: var(--lg-text-soft);
|
||||
color: #756d85;
|
||||
font-size: 12px;
|
||||
}
|
||||
.adv-grid :deep(.el-input-number) { width: 100%; }
|
||||
.adv-grid :deep(.el-input-number .el-input__inner) { text-align: left; }
|
||||
|
||||
/* 登录按钮:靛蓝→青 霓虹 · 扫光动效 */
|
||||
.btn-login {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
height: 46px !important;
|
||||
border-radius: 10px;
|
||||
border-radius: 12px;
|
||||
margin-top: 6px;
|
||||
font-size: 15px;
|
||||
letter-spacing: 6px;
|
||||
font-weight: 600;
|
||||
color: #fff !important;
|
||||
background: linear-gradient(135deg, var(--lg-primary) 0%, var(--lg-primary-hover) 100%) !important;
|
||||
background: #7543e8 !important;
|
||||
border: none !important;
|
||||
box-shadow:
|
||||
0 12px 28px rgba(var(--lg-primary-rgb), 0.5),
|
||||
0 0 26px rgba(var(--lg-primary-hover-rgb), 0.36),
|
||||
0 0 0 1px rgba(255, 255, 255, 0.20) inset !important;
|
||||
transition: all .28s cubic-bezier(.25, .8, .25, 1);
|
||||
}
|
||||
.btn-login::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0; left: -120%;
|
||||
width: 80%; height: 100%;
|
||||
background: linear-gradient(110deg,
|
||||
transparent 0%,
|
||||
rgba(255, 255, 255, 0.0) 30%,
|
||||
rgba(255, 255, 255, 0.38) 50%,
|
||||
rgba(255, 255, 255, 0.0) 70%,
|
||||
transparent 100%);
|
||||
transition: left .6s cubic-bezier(.25, .8, .25, 1);
|
||||
pointer-events: none;
|
||||
box-shadow: 0 10px 24px rgba(117, 67, 232, 0.28) !important;
|
||||
transition: all .2s ease;
|
||||
}
|
||||
.btn-login::after { display: none; }
|
||||
.btn-login:hover {
|
||||
transform: translateY(-2px);
|
||||
background: linear-gradient(135deg, var(--lg-primary-hover) 0%, var(--lg-accent) 130%) !important;
|
||||
box-shadow:
|
||||
0 18px 38px rgba(var(--lg-primary-rgb), 0.6),
|
||||
0 0 42px rgba(var(--lg-accent-rgb), 0.5),
|
||||
0 0 0 1px rgba(var(--lg-accent-rgb), 0.5) inset !important;
|
||||
transform: translateY(-1px);
|
||||
background: #8660f0 !important;
|
||||
box-shadow: 0 14px 28px rgba(117, 67, 232, 0.35) !important;
|
||||
}
|
||||
.btn-login:hover::after { left: 120%; }
|
||||
|
||||
.bottom-note {
|
||||
margin-top: 16px;
|
||||
font-size: 11.5px;
|
||||
color: var(--lg-text-dim);
|
||||
color: #756d85;
|
||||
text-align: center;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
/* 底部水印 */
|
||||
.footer-stamp {
|
||||
position: absolute;
|
||||
bottom: 18px; left: 50%;
|
||||
transform: translateX(-50%);
|
||||
font-size: 11px;
|
||||
letter-spacing: 2px;
|
||||
color: rgba(45, 32, 78, 0.6);
|
||||
color: rgba(40, 33, 58, 0.45);
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
/* 错误提示 */
|
||||
.glass-form :deep(.el-form-item__error) {
|
||||
color: #ff9bb8;
|
||||
color: #c23a5c;
|
||||
padding-top: 4px;
|
||||
}
|
||||
|
||||
/* 小屏自适应 */
|
||||
@media (max-width: 760px) {
|
||||
.login-card { grid-template-columns: 1fr; min-height: auto; }
|
||||
.hero { border-right: none; border-bottom: 1px solid rgba(255, 255, 255, 0.1); padding: 28px; }
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1740,8 +1740,14 @@ onUnmounted(() => {
|
||||
.map-editor-page {
|
||||
height: calc(100vh - 56px);
|
||||
display: flex; flex-direction: column;
|
||||
background: radial-gradient(ellipse at 50% 40%, #2d1456 0%, #1a0930 50%, #0f0420 100%);
|
||||
color: rgba(232,215,245,0.9);
|
||||
/* 画布区保持深色;色相跟当前主题侧栏 (--me-chrome-*) */
|
||||
background: radial-gradient(
|
||||
ellipse at 50% 40%,
|
||||
var(--me-chrome-1) 0%,
|
||||
var(--me-chrome-2) 52%,
|
||||
#080610 100%
|
||||
);
|
||||
color: var(--me-text);
|
||||
}
|
||||
.me-body {
|
||||
flex: 1;
|
||||
@@ -1767,13 +1773,13 @@ onUnmounted(() => {
|
||||
border-radius: 12px 0 0 12px;
|
||||
cursor: pointer;
|
||||
color: #fff;
|
||||
background: linear-gradient(135deg, rgba(150, 90, 240, 0.96) 0%, rgba(255, 90, 200, 0.92) 100%);
|
||||
box-shadow: -4px 0 16px rgba(120, 40, 200, 0.45);
|
||||
background: var(--mg-primary);
|
||||
box-shadow: -4px 0 16px var(--me-active-glow);
|
||||
transition: right 0.26s cubic-bezier(0.25, 0.8, 0.25, 1), filter 0.15s ease, box-shadow 0.15s ease;
|
||||
}
|
||||
.ai-assistant-fab:hover {
|
||||
filter: brightness(1.08);
|
||||
box-shadow: -6px 0 22px rgba(150, 60, 230, 0.6);
|
||||
box-shadow: -6px 0 22px var(--me-active-glow);
|
||||
}
|
||||
/* 展开时 right 偏移由内联样式按面板实际宽度设置(默认 360);此处仅作兜底。 */
|
||||
.ai-assistant-fab.is-open {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user