This commit is contained in:
zlwu committed 2026-07-04 08:21:49 +08:00
1 parent b4a49183db
commit 33c038b8d9
37 files changed
+749

No files matched your search

@@ -0,0 +1,5 @@
-- CRM 产品:标准价、成本价、计量单位改为可空(产品模块不再维护,由报价/合同行维护)
ALTER TABLE `crm_product`
MODIFY COLUMN `unit` varchar(20) DEFAULT NULL COMMENT '单位',
MODIFY COLUMN `price` decimal(15,2) DEFAULT NULL COMMENT '标准单价',
MODIFY COLUMN `cost_price` decimal(15,2) DEFAULT NULL COMMENT '成本价';
@@ -0,0 +1,6 @@
-- 运输计划取货/送货地址坐标(高德导航)
ALTER TABLE `pl_plan`
ADD COLUMN `pickup_lng` decimal(10,6) DEFAULT NULL COMMENT '取货经度' AFTER `pickup_address`,
ADD COLUMN `pickup_lat` decimal(10,6) DEFAULT NULL COMMENT '取货纬度' AFTER `pickup_lng`,
ADD COLUMN `destination_lng` decimal(10,6) DEFAULT NULL COMMENT '送货经度' AFTER `destination_address`,
ADD COLUMN `destination_lat` decimal(10,6) DEFAULT NULL COMMENT '送货纬度' AFTER `destination_lng`;
@@ -0,0 +1,15 @@
-- 运费结算:新增「已驳回」状态字典(审批驳回后 settlement_status = rejected)
INSERT INTO `sys_dict_data` (
`id`, `dict_label`, `dict_value`, `tag_type`, `dict_type`, `tenant_no`, `order_num`,
`remark`, `create_by`, `create_dept_id`, `create_time`, `update_by`, `update_time`, `del_flag`
)
SELECT
'PL_SETTLE_REJECTED', '已驳回', 'rejected', 'danger', 'pl_settlement_status', t.tenant_no, 2,
NULL, 'system', '', NOW(), NULL, NOW(), 0
FROM (
SELECT DISTINCT `tenant_no` FROM `sys_dict_type` WHERE `dict_type` = 'pl_settlement_status' AND `del_flag` = 0
) t
WHERE NOT EXISTS (
SELECT 1 FROM `sys_dict_data` d
WHERE d.id = 'PL_SETTLE_REJECTED' AND d.tenant_no = t.tenant_no AND d.del_flag = 0
);
@@ -0,0 +1,22 @@
-- 运输通用地址库
CREATE TABLE IF NOT EXISTS `pl_transport_address` (
`id` varchar(64) NOT NULL COMMENT '主键ID',
`address_name` varchar(200) NOT NULL COMMENT '地址名称',
`full_address` varchar(500) NOT NULL COMMENT '详细地址',
`lng` decimal(10,6) NOT NULL COMMENT '经度',
`lat` decimal(10,6) NOT NULL COMMENT '纬度',
`contact_name` varchar(100) DEFAULT NULL COMMENT '联系人',
`contact_phone` varchar(32) DEFAULT NULL COMMENT '联系电话',
`status` char(1) NOT NULL DEFAULT '1' COMMENT '状态 1-启用 0-停用',
`tenant_no` varchar(64) NOT NULL DEFAULT 'shanxia2.0' COMMENT '租户编号',
`order_num` int(11) DEFAULT NULL COMMENT '排序号',
`remark` varchar(512) DEFAULT NULL COMMENT '备注',
`create_by` varchar(64) DEFAULT NULL COMMENT '创建人id',
`create_dept_id` varchar(64) NOT NULL DEFAULT '' COMMENT '创建人部门ID',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_by` varchar(64) DEFAULT NULL COMMENT '更新人id',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`del_flag` tinyint(4) NOT NULL DEFAULT '0' COMMENT '删除标记:0-正常 1-已删除',
PRIMARY KEY (`id`),
KEY `idx_pl_transport_address_tenant` (`tenant_no`, `del_flag`, `status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='运输通用地址库';
@@ -0,0 +1,68 @@
# Portal 顶栏全量导航 — Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** 去掉 Portal 左侧栏,一级模块改为顶栏下拉,二三级改为顶栏水平菜单(ellipsis),≤800px 用抽屉导航。
**Architecture:** 复用 `filterPortalTopModules` / `filterSidebarRoutes` 与 `ArtHorizontalMenu`;新建 `ArtPortalModuleSelect`、`ArtPortalSubNav`、`ArtPortalMobileMenu`;布局仅保留 `#app-workspace` 全宽;WorkTab 空标签落点按当前一级模块 children 解析。
**Tech Stack:** Vue 3 · Element Plus · Pinia · vue-router · SCSS
**Spec:** `docs/superpowers/specs/2026-06-30-portal-top-nav-only-design.md`
---
### Task 1: Portal 菜单工具函数
**Files:**
- Modify: `web-admin/src/utils/router/portal-menu.ts`
- Modify: `web-admin/src/utils/navigation/route.ts`
- [ ] **Step 1:** 在 `portal-menu.ts` 增加 `resolvePortalSubMenus(menuList, routePath)`、`getPortalDefaultPathForRoute(menuList, routePath)`
- [ ] **Step 2:** 在 `route.ts` 增加 `getFirstSecondLevelMenuPathForModule(moduleChildren, modulePath?)` 导出
### Task 2: 水平菜单支持无图标
**Files:**
- Modify: `web-admin/src/components/core/layouts/art-menus/art-horizontal-menu/index.vue`
- Modify: `web-admin/src/components/core/layouts/art-menus/art-horizontal-menu/widget/HorizontalSubmenu.vue`
- [ ] **Step 1:** `HorizontalSubmenu` 增加 `showIcon` prop(默认 `true`)
- [ ] **Step 2:** `ArtHorizontalMenu` 增加 `showIcon` prop 并向下传递
### Task 3: 新建顶栏导航组件
**Files:**
- Create: `web-admin/src/components/core/layouts/art-portal-module-select/index.vue`
- Create: `web-admin/src/components/core/layouts/art-portal-sub-nav/index.vue`
- Create: `web-admin/src/components/core/layouts/art-portal-mobile-menu/index.vue`
- [ ] **Step 1:** 一级下拉 `el-dropdown` + 模块切换
- [ ] **Step 2:** 二三级 `ArtHorizontalMenu` + `ellipsis` + Fresh Mint 样式
- [ ] **Step 3:** ≤800px `el-drawer` + `SidebarSubmenu` 竖向树
### Task 4: 组装顶栏与布局
**Files:**
- Modify: `web-admin/src/components/core/layouts/art-portal-header/index.vue`
- Modify: `web-admin/src/views/index/index.vue`
- Modify: `web-admin/src/views/index/style.portal.scss`
- Modify: `web-admin/src/assets/styles/core/app.scss`
- [ ] **Step 1:** Header 替换 TopNav 为 ModuleSelect + SubNav + MobileMenu
- [ ] **Step 2:** Portal 布局移除 `#app-sidebar`
- [ ] **Step 3:** SCSS 全宽 workspace,删除侧栏规则
### Task 5: WorkTab 联动
**Files:**
- Modify: `web-admin/src/store/modules/worktab.ts`
- Modify: `web-admin/src/components/core/layouts/art-work-tab/index.vue`
- [ ] **Step 1:** 空标签落点改用 `getPortalDefaultPathForRoute`
### Task 6: 验证
- [ ] **Step 1:** `pnpm lint`(web-admin)
- [ ] **Step 2:** `./scripts/update-graph.sh frontend`
- [ ] **Step 3:** 浏览器手测:模块切换、ellipsis、800px 抽屉
@@ -0,0 +1,326 @@
# web-admin 门户布局 — 顶栏全量导航改造设计
> **日期**: 2026-06-30
> **状态**: 实施中
> **范围**: `web-admin` 管理端框架(Portal 壳层布局 + 菜单展示 + 导航联动)
> **前置文档**: `2026-06-20-web-admin-portal-layout-design.md`(已实现:顶栏一级卡片 + 左侧二三级)
> **UI 规范**: `docs/web-admin-ui-spec-fresh-mint.md`(主色 `#10b981`、Fresh Mint Token)
## 背景与目标
客户希望 **去掉左侧菜单栏**,将全部导航收拢到顶栏:
1. 顶栏 **第一位**(品牌区之后)为 **一级模块下拉**(替代现有横向卡片)。
2. 顶栏 **后续区域** 展示当前一级模块对应的 **二级、三级** 水平菜单。
3. 内容区 **全宽**,WorkTab + 业务页面逻辑不变。
4. 顶栏保持 **单行 56px**,菜单过多时使用 Element Plus 水平菜单 **`ellipsis`** 收进「更多」。
**已选方案**:**方案 A — 单行顶栏 + ellipsis 溢出**(2026-06-30 确认)。
---
## 已确认决策
| # | 决策 | 说明 |
|---|------|------|
| D1 | **布局形态** | 去掉 `#app-sidebar`;Portal 布局仅保留顶栏 + 全宽 workspace |
| D2 | **一级展示** | `el-dropdown` 下拉选择模块(CRM / 报销 / 地磅 / 运输 / 工作流 / 系统),替代 `ArtPortalTopNav` 卡片 |
| D3 | **二三级展示** | `ElMenu mode="horizontal"` + `ellipsis`,渲染当前模块 `children` |
| D4 | **顶栏高度** | 保持 **56px**,不采用双行顶栏 |
| D5 | **溢出策略** | 优先使用 EP 内置 `ellipsis`;必要时可补充横向 scroll(参考 `ArtMixedMenu`) |
| D6 | **权限与路由** | 不改 `MenuProcessor`、路由注册、`menuCode` 过滤链路 |
| D7 | **菜单图标** | 一级下拉项可带 icon;顶栏二三级水平菜单 **不展示图标**(延续 OpenSpec「更深层仅文字」语义) |
| D8 | **控制台** | 仍从顶栏/导航中排除;默认首页逻辑不变 |
| D9 | **旧布局** | `MenuTypeEnum.PORTAL` 壳层改造;LEFT / TOP / TOP_LEFT / DUAL 代码保留但不作为产品入口 |
---
## 非目标(本期不做)
- 业务页面 `<script>`、API、权限后端改造
- 路由结构(`sqxp.ts` / `pl.ts`)重组或新增中间目录层
- 小程序端布局
- 设置面板重新开放 Left / Top / Mixed 等旧布局切换
- Demo 菜单独立提升为一级模块
---
## 一、目标布局
```
┌──────────────────────────────────────────────────────────────────────────────┐
│ [Logo + 系统名] │ [一级模块 ▼] │ 二级A │ 二级B │ 二级C ▼ │ … │ [更多▼] │ [全屏][用户] │ ← 56px
├──────────────────────────────────────────────────────────────────────────────┤
│ ArtWorkTab(标签页) │
├──────────────────────────────────────────────────────────────────────────────┤
│ ArtPageContent(业务页面,全宽) │
└──────────────────────────────────────────────────────────────────────────────┘
```
**与现布局对比**
| 项目 | 现 Portal(2026-06-20) | 本改造 |
|------|-------------------------|--------|
| 一级 | 顶栏横向卡片 | 顶栏第一位下拉 |
| 二三级 | 左侧 `ArtSidebarMenu` | 顶栏水平菜单 |
| 内容区 | 侧栏右侧,非全宽 | 全宽 |
| 顶栏高度 | 56px | 56px(不变) |
**DOM 结构(`views/index/index.vue`)**
```
.app-layout-portal
├── #app-portal-header → ArtPortalHeader(改造)
└── #app-portal-body → 仅 workspace,无 sidebar
└── #app-workspace → flex column,width 100%
├── #app-worktab → ArtWorkTab
└── #app-content → ArtPageContent
```
---
## 二、顶栏分区(ArtPortalHeader)
### 2.1 品牌区(保留)
- `ArtLogo` + `AppConfig.systemInfo.name`
- 点击跳转 `homePath`
- 右侧竖线分隔(沿用现有 `portal-header-brand` 样式)
### 2.2 一级模块下拉(新建 ArtPortalModuleSelect)
**数据源**
- `filterPortalTopModules(menuStore.menuList)`(`utils/router/portal-menu.ts`)
- 顺序:`PORTAL_TOP_MODULE_ORDER`(CRM → 报销 → 地磅 → 运输 → 工作流 → 系统)
- 排除控制台与 `meta.isHide`
**UI**
| 属性 | 值 |
|------|-----|
| 控件 | `el-dropdown`(trigger 为按钮,展示当前模块名 + 箭头) |
| 高度 | 36px(与筛选控件高度节奏一致) |
| 圆角 | 8px |
| 下拉项 | 可选 icon + 模块名;当前模块高亮(主色或 `--primary-bg`) |
| 空态 | 0 个可见模块时 trigger 展示「暂无可用模块」且禁用 |
**交互**
- 选择模块:`router.push(getFirstAccessiblePath(module))`
- 若所选模块已是当前模块:不重复跳转
- 当前模块解析:复用 `isPortalModuleActive(item, routePath)`
### 2.3 二三级水平菜单(新建 ArtPortalSubNav)
**数据源**
```typescript
const currentTopPath = `/${route.path.split('/')[1]}`
const module = menuList.find((m) => m.path === currentTopPath)
const subMenus = filterSidebarRoutes(module?.children ?? [])
```
**实现**
- 包装现有 `ArtHorizontalMenu` + `HorizontalSubmenu`,或抽取共用递归组件
- `ElMenu` 属性:`mode="horizontal"`、`ellipsis="true"`、`default-active` = 当前路由
- 过滤:与侧栏一致,使用 `filterSidebarRoutes`
- iframe 路由:沿用 `art-sidebar-menu` 中 `findIframeMenuList` 逻辑(迁移为 util 或在 SubNav 内复用)
**菜单项行为**
| 类型 | 渲染 | 点击 |
|------|------|------|
| 叶子(无可导航子节点) | `ElMenuItem` | `handleMenuJump(item)` |
| 目录(有可见子节点) | `ElSubMenu` → 递归三级 | 展开下拉 |
**视觉**
- 菜单项字号 14px,选中态主色 `#10b981`(底边或背景 `--primary-bg`,与 Fresh Mint 顶栏菜单一致)
- **不渲染** 二三级 icon(`HorizontalSubmenu` 需增加 `showIcon={false}` 或 Portal 专用 variant)
- Popper / 下拉:`backdrop-filter: none`
### 2.4 操作区(保留)
- 全屏按钮(`shouldShowFullscreen`)
- `ArtUserMenu`
- 不恢复:折叠、面包屑、刷新
### 2.5 顶栏 flex 分配
```
portal-header-brand → flex-shrink: 0
portal-module-select → flex-shrink: 0(约 120–160px)
portal-sub-nav → flex: 1; min-width: 0(ellipsis 生效关键)
portal-header-actions → flex-shrink: 0
```
---
## 三、移除左侧栏
### 3.1 布局
- `views/index/index.vue`:Portal 分支删除 `<aside id="app-sidebar">` 与 `<ArtSidebarMenu />`
- `style.portal.scss`:删除 `#app-sidebar` 及 `<800px` fixed 抽屉规则
- `#app-portal-body`:由 `flex row` 改为单列;`#app-workspace` 占满宽度
### 3.2 组件处置
| 组件 | 处置 |
|------|------|
| `ArtPortalTopNav` | 废弃(卡片式一级),由 `ArtPortalModuleSelect` 替代 |
| `ArtSidebarMenu` | Portal 模式下不再挂载;组件文件保留供非 PORTAL 布局或后续清理 |
| `art-sidebar-menu` 中 PORTAL 分支 | 可删除或留空(仅 PORTAL 使用时) |
---
## 四、导航联动
### 4.1 WorkTab 关闭全部标签
现逻辑注释为「跳转侧栏第一个二级菜单」。改造后:
- 解析 **当前一级模块** 的 `children`
- 调用 `getFirstSecondLevelMenuPath(moduleChildren)` 或等价函数
- 若无二级结构,回退 `getFirstAccessiblePath(activeModule)`
**涉及文件**
- `store/modules/worktab.ts`
- `components/core/layouts/art-work-tab/index.vue`
- 可选:在 `utils/navigation/route.ts` 新增 `getFirstSecondLevelMenuPathForModule(modulePath, menuList)`
### 4.2 默认首页(homePath)
- `menuStore.setHomePath` 逻辑可不变(仍从全量树取第一个二级)
- 登录后若落在某模块,顶栏下拉与水平菜单选中态需与路由同步
### 4.3 跨模块 deep link
- 直接访问 `/pl/plan` 时:下拉显示「运输管理」;水平菜单高亮「运输计划」
- 依赖 `isPortalModuleActive` + 水平菜单 `default-active`(`meta.activePath || path`)
---
## 五、菜单数据与权限(不变)
沿用现有链路:
```
beforeEach → MenuProcessor.getMenuList()
→ filterMenuByRoles / filterMenuByCodes
→ filterEmptyMenus
→ menuStore.setMenuList
→ RouteRegistry.register
```
| 层级 | 过滤 |
|------|------|
| 一级(下拉) | `filterPortalTopModules` |
| 二三级(水平) | `filterSidebarRoutes` + `HorizontalSubmenu` 递归 |
三级权限、`menuCode`、非超管角色菜单可见性与现 Portal 一致。
---
## 六、响应式
| 断点 | 策略 |
|------|------|
| ≥1180px | 单行顶栏;水平菜单 ellipsis 处理溢出 |
| 800–1180px | 系统名可缩短 max-width;ellipsis 继续生效 |
| ≤800px | **必须**提供可用导航:顶栏右侧增加「菜单」按钮,点击打开 **抽屉 / 全屏面板**,内嵌当前模块二三级树(可复用 `SidebarSubmenu` 竖向渲染) |
窄屏下不可仅依赖 ellipsis(触控体验差),抽屉为 **本期必做**,非 follow-up。
---
## 七、样式要点
| 项 | 值 |
|----|-----|
| 顶栏高度 | 56px |
| 顶栏背景 | `var(--mint-surface, #fff)` + 底边 `var(--mint-border)` |
| 主色 | `#10b981` / `#059669` / `#ecfdf5` |
| 遮罩 | `backdrop-filter: none` |
| 内容区 padding | 沿用 `--mint-content-padding`(24px) |
| 侧栏宽度 | **0**(无侧栏) |
全局 SCSS:更新 `app.scss` 中 `.app-layout-portal #app-sidebar` 相关规则(删除或注释)。
---
## 八、影响文件清单
**新建**
- `components/core/layouts/art-portal-module-select/index.vue` — 一级模块下拉
- `components/core/layouts/art-portal-sub-nav/index.vue` — 二三级水平菜单
- `components/core/layouts/art-portal-mobile-menu/index.vue`(可选独立文件)— ≤800px 抽屉菜单
**修改**
- `components/core/layouts/art-portal-header/index.vue` — 组装新顶栏
- `components/core/layouts/art-menus/art-horizontal-menu/widget/HorizontalSubmenu.vue` — Portal 模式隐藏 icon(或 props 控制)
- `views/index/index.vue` — 去掉 sidebar
- `views/index/style.portal.scss` — 全宽 workspace
- `assets/styles/core/app.scss` — 删除 Portal 侧栏兜底样式
- `store/modules/worktab.ts` — 关闭全部标签后的落点
- `components/core/layouts/art-work-tab/index.vue` — 同上
- `utils/navigation/route.ts` — 可选:按模块取第一个二级路径
**废弃 / 不再引用(Portal)**
- `components/core/layouts/art-portal-top-nav/index.vue`
**文档**
- 更新 `2026-06-29-fresh-mint-master-plan.md` §二「Portal 顶栏 + 侧栏交互结构不变」→ 注明已被本 spec 取代
---
## 九、测试计划
| 场景 | 预期 |
|------|------|
| super_admin | 下拉 6 个模块;切换后水平菜单切换为对应子树 |
| admin 仅有 bx 权限 | 下拉仅「报销管理」;水平菜单仅 bx 子项 |
| 无任何业务 menuCode | 下拉空态「暂无可用模块」 |
| 切换 CRM → 报销 | 路由进入报销首个可访问页;水平菜单更新 |
| CRM 模块 10+ 项 | 单行顶栏不撑破;溢出项进入「更多」 |
| 含三级目录的菜单树 | 二级为 SubMenu,三级在下拉中可点击 |
| 直接 URL `/pl/plan` | 下拉选中运输;水平菜单高亮对应项 |
| WorkTab 关闭全部 | 跳转当前模块第一个二级菜单页 |
| ≤800px 视口 | 「菜单」抽屉可打开并导航 |
| 控制台 `/dashboard` | 不出现在下拉与水平菜单 |
---
## 十、风险
1. **CRM 等模块项数多**:ellipsis 后部分菜单藏在「更多」中,需验收可发现性。
2. **HorizontalSubmenu 默认带 icon**:需 Portal 专用样式/ props,避免违反 OpenSpec。
3. **WorkTab 落点**:若仍读全量 `menuList` 取第一个二级,关闭标签可能跳到错误模块。
4. **与 2026-06-20 spec 冲突**:属新产品决策,以本文档为准。
5. **移动端抽屉**:若遗漏,小屏将无法正常切换页面。
---
## 十一、验收标准
- [ ] Portal 布局下无左侧栏,内容区全宽
- [ ] 顶栏 56px 单行,一级为下拉,二三级为水平菜单
- [ ] 菜单权限、路由跳转、WorkTab 行为与改造前一致
- [ ] Fresh Mint 色值与无 blur 遮罩符合 `web-admin-ui-spec-fresh-mint.md`
- [ ] ≤800px 具备抽屉导航
- [ ] 不影响非 Portal 布局代码路径(若仍保留)
---
## 十二、后续(非本期)
- 清理 `ArtPortalTopNav`、Portal 专用侧栏 SCSS dead code
- OpenSpec `ui-conventions` 增加「Portal 顶栏全量导航」章节
- 设置面板移除已隐藏的 layout 切换项