# laic-frontend 目录对齐与地图壳设计 日期:2026-08-19 状态:已确认 参照仓:`/Users/qingyuan/CodeProject/uav-edu-web` ## 背景 `laic-frontend`(嘉谷低空智控平台)已是 Vue 3 + Vite + Pinia + Vue Router 的 SPA,但目录扁平、UI 以自研 `prototype.css` 为主,地图能力缺失。后续开发要以 `uav-edu-web` 的目录构成、代码风格与地图架构为准;**开发环境底图使用天地图**。 当前关键事实: - 入口:`index.html` → `src/main.js` → `App.vue` + `layout/AppShell.vue` - 视图:`src/views/*.vue`(无 `XxxView/` 目录约定) - 状态:`src/store/{user,devices,ui}.js` - HTTP:`src/api/request.js`(Axios,`/api` 代理到 `127.0.0.1:8080`) - `jcWeb/` 为静态原型,非运行入口 - `element-plus` 已声明但源码未使用 ## 目标 本轮一次性完成(方案 A): 1. 目录与命名对齐 `uav-edu-web` 2. 引入 TDesign(**按需引入**),重写主壳与登录页 3. 挂上全局地图壳:开发环境天地图底图 4. 修全量 import / 路由懒加载路径,保证 `npm run build` 通过 ## 非目标 - 业务页批量换成 TDesign 组件 - Monitor / 设备 / 航迹等业务图层实现 - 删除或改造 `jcWeb/` - 引入 Mapbox 底图;生产环境底图 provider 切换定稿 - TypeScript、测试框架、细粒度权限 - 坐标系转换(GCJ-02 等);除非启动即发现阻塞再单开 ## 方案选择 | 方案 | 内容 | 结论 | |---|---|---| | A 大爆炸一次性对齐 | 目录 + TDesign 壳 + Login + 天地图骨架同一轮 | **采用** | | B 两阶段 | 先搬家,再壳与地图 | 否:中间态与目标不一致 | | C 适配层 | 旧 AppShell 包一层 | 否:双壳并存,对照成本高 | ## 目录映射 目标树(相对 `src/`): ```text src/ ├── main.js ├── App.vue ├── router/index.js ├── layout/ │ ├── MainContainer.vue # 取代 AppShell │ ├── MapLayer.vue # 单例地图(开发天地图) │ └── components/ │ ├── SideMenu.vue │ └── TopBar.vue ├── views/ │ ├── LoginView/LoginView.vue │ ├── MonitorView/MonitorView.vue │ ├── DevicesView/DevicesView.vue │ ├── DeviceDetailView/DeviceDetailView.vue │ ├── ControlView/ControlView.vue │ ├── TasksView/TasksView.vue │ ├── ReplayView/ReplayView.vue │ ├── MediaView/MediaView.vue │ ├── AlarmsView/AlarmsView.vue │ ├── AccountView/AccountView.vue │ ├── UsersView/UsersView.vue │ ├── FirmwaresView/FirmwaresView.vue │ ├── SystemView/SystemView.vue │ └── OperationLogsView/OperationLogsView.vue ├── stores/ │ ├── index.js # createPinia + re-export │ └── modules/ │ ├── userStore.js │ ├── devicesStore.js │ └── uiStore.js ├── core/ │ └── mapHelper.js ├── config/ │ ├── map.js │ └── urls.js # 可先骨架,后续从调用点抽全 ├── utils/ │ ├── commonRefs.js │ ├── http.js # 由 api/request.js 迁入 │ └── setupTDesign.js # 按需注册本轮用到的 TDesign 组件 ├── components/ # 跨页组件(如 DockEditModal)保留 └── styles/ # prototype.css 暂留;壳层用 less module ``` 文件搬迁对照: | 现路径 | 新路径 | |---|---| | `src/views/Login.vue` | `src/views/LoginView/LoginView.vue` | | `src/views/Monitor.vue` | `src/views/MonitorView/MonitorView.vue` | | `src/views/Devices.vue` | `src/views/DevicesView/DevicesView.vue` | | `src/views/DeviceDetail.vue` | `src/views/DeviceDetailView/DeviceDetailView.vue` | | `src/views/Control.vue` | `src/views/ControlView/ControlView.vue` | | `src/views/Tasks.vue` | `src/views/TasksView/TasksView.vue` | | `src/views/Replay.vue` | `src/views/ReplayView/ReplayView.vue` | | `src/views/Media.vue` | `src/views/MediaView/MediaView.vue` | | `src/views/Alarms.vue` | `src/views/AlarmsView/AlarmsView.vue` | | `src/views/Account.vue` | `src/views/AccountView/AccountView.vue` | | `src/views/Users.vue` | `src/views/UsersView/UsersView.vue` | | `src/views/Firmwares.vue` | `src/views/FirmwaresView/FirmwaresView.vue` | | `src/views/System.vue` | `src/views/SystemView/SystemView.vue` | | `src/views/OperationLogs.vue` | `src/views/OperationLogsView/OperationLogsView.vue` | | `src/store/user.js` | `src/stores/modules/userStore.js` | | `src/store/devices.js` | `src/stores/modules/devicesStore.js` | | `src/store/ui.js` | `src/stores/modules/uiStore.js` | | `src/api/request.js` | `src/utils/http.js` | | `src/layout/AppShell.vue` | 拆为 `MainContainer.vue` + `SideMenu.vue` + `TopBar.vue` | 不变:`jcWeb/`、`public/`、业务页功能逻辑(仅路径与必要 import)。 Vite:`resolve.alias['@'] → src`;`envPrefix` 含 `APP_` / `VITE_`。 ## 壳层与路由 - `App.vue`:保留全局 SVG sprite / toast / confirm 能力(可逐步收敛);路由驱动:`/login` 独立全屏,登录后子路由挂在 `MainContainer`(保持现 laic 路由结构,不改为 edu 的 App 二选一挂载) - `MainContainer.vue`:`t-layout` + `SideMenu` + `TopBar`;`t-content` 内 **常驻** `MapLayer` + `RouterView`(对齐 edu:非登录页同一张地图) - `SideMenu` / `TopBar`:TDesign 按需组件重写;菜单仍按 `admin` / 普通用户分流(行为对齐现 `AppShell`) - `LoginView`:TDesign 表单风格重写;对接现有 `/v1/auth/*` 行为不变 - 路由懒加载全部改为 `views/XxxView/XxxView.vue` - `beforeEach`:token 校验、`meta.admin`、`meta.userOnly` **行为不变** TDesign: - 依赖:`tdesign-vue-next`、`less`(壳层 CSS Modules);样式按需方案在实施计划中二选一:`unplugin-vue-components` + `unplugin-auto-import`,或手写 `setupTDesign.js` 注册本轮组件 - **按需引入**,禁止全量 `app.use(TDesign)` - 同步移除未使用的 `element-plus`、`@element-plus/icons-vue` 依赖 - 本轮用到的最小集合预期:`Layout`/`Aside`/`Content`/`Header`、`Menu`/`MenuItem`、`Button`、`Input`、`Form`/`FormItem`、`Message`(以实装为准,spec 约束「按需」而非插件选型) ## 地图骨架(开发环境天地图) 依赖本轮:`maptalks`(必要)。**不**为底图引入 `mapbox-gl` / `maptalks.mapboxgl`。 | 模块 | 职责 | |---|---| | `config/map.js` | `center`/`zoom`;`APP_TIANDITU_TOKEN`(或 `VITE_`);影像+注记图层 URL 模板 | | `layout/MapLayer.vue` | `maptalks.Map`;`baseLayer` = 天地图 `TileLayer` / `GroupTileLayer`;`commonRefs.setRef('map')` + `mapHelper.setMap`;卸载销毁 | | `core/mapHelper.js` | `setMap`/`getMap`;`toggleMapMode({is2D,is3D})` 最小实现;`replaceMapContainer`/`restoreMapContainer` API 先留齐(弹窗复用后续用) | | `utils/commonRefs.js` | `setRef`/`getRef`/`removeRef` | | `.env.development` | `APP_TIANDITU_TOKEN=` 占位;无 token 时地图初始化明确报错/占位,**不拖垮整站 SPA** | 约定: - 开发默认天地图;生产 provider 本轮不定稿 - 地图仅在 `MainContainer` 挂载时初始化一次;路由切换不 `new Map` - 业务页本轮不画设备/航迹;非透图页面可用不透明面板盖住地图 - 坐标:本轮按经纬度直接交给 maptalks;转换需求单开 ## 代码风格对齐(本轮强制) - SFC:`