# 张江高新区青浦园产业地图 — 架构与业务流程说明 > 本文档面向二次开发与维护,描述当前仓库的技术栈、目录结构、运行时链路、数据与地图逻辑、认证与配置,以及可扩展点。 > 仓库 `package.json` 中项目名为 `shqpgov_school_district_inquiry`,与业务展示名称不一致,以页面标题「张江高新区青浦园产业地图」为准。 --- ## 1. 项目概览 ### 1.1 产品定位 单页大屏类应用:在 **Leaflet 二维地图** 上展示青浦区 **园区边界**、**行政区划** 与 **重点企业点位**,两侧面板提供产业统计(ECharts)、园区列表、图层与图例控制、企业搜索与底图切换。业务数据当前以 **前端静态 GeoJSON/JS 模块** 为主,非实时接口驱动(「立即同步」为前端模拟)。 ### 1.2 技术栈 | 类别 | 技术 | | -------- | -------------------------------------------------------------------------------------- | | 框架 | Vue 2.6、Vue Router 3(hash)、Vuex 3 | | UI | Element UI 2 | | 地图 | Leaflet 1.3.1(`public/index.html` 全局引入)、Esri Leaflet 3、jQuery(部分 DOM 操作) | | 图表 | ECharts 5 | | 空间分析 | @turf/turf 7、proj4 | | HTTP | axios(`src/utils/request.js` 封装) | | 构建 | Vue CLI 5、`vue.config.js` 中 `publicPath: './'`、打包文件名带时间戳 | ### 1.3 外部脚本与全局变量 `public/index.html` 在打包前加载: - `./static/config/config.js`:定义 **`systemConfig`**、**`map2DViewer`** 等全局对象(非 ES 模块)。 - Leaflet、Esri Leaflet、proj4、leaflet.draw、shpjs、turf、**`createAuth.js`**(混淆脚本,提供登录密码加密相关 **`AesEncryptUtil`**)。 - 百度地图 WebGL API(`ak=...`,`callback=initialize`)—— 当前业务主路径以 Leaflet 为主,需确认是否仍依赖百度回调。 Vue 入口 `src/main.js` 将 **`$Coordinate`**(`coordinate`)、**`$Decrypt`**(`aes.js`)、**`$CryptoJS`**(实为 `publicFunction.js` 默认导出)、**`$dayjs`**、**`$proj4`**、**`$bus`** 挂到实例上。 --- ## 2. 目录结构(源码层面) ``` Industry_map/ ├── public/ │ ├── index.html # 全局脚本、Leaflet CSS/JS │ └── static/ │ ├── config/config.js # systemConfig、地图全局 map2DViewer 等 │ ├── images/ # 大屏装饰、图例、marker 图标 │ └── plugins/ # Leaflet 插件、createAuth.js 等 ├── src/ │ ├── main.js # 应用入口 │ ├── App.vue # 根组件:启动时登录 │ ├── router/index.js # 路由(实质单页) │ ├── store/index.js # Vuex:token、用户信息、底图类型等 │ ├── views/ │ │ ├── qpjyj.vue # 【主页面】产业地图大屏布局与业务编排 │ │ ├── industryDatas.js # 企业 FeatureCollection 式数组(体积大) │ │ ├── parkDatas.js # 园区多边形 │ │ ├── qpDivisionDatas.js # 区县边界 │ │ ├── qpStreetDatas.js # 乡镇/街道边界 │ │ └── kewei_cydt_project.js # 按统一社会信用代码扩展企业工商等字段 │ ├── components/ │ │ ├── map/appMap.vue # Leaflet 地图:底图、marker、面、Popup │ │ └── card/ # 卡片壳、饼图、柱状图 │ ├── api/common.js # 当前仅 OAuth 登录 │ ├── utils/ │ │ ├── request.js # axios 实例、拦截器里带 token 头 │ │ ├── encrypt‌.js # 登录封装(调用 common.login) │ │ ├── publicFunction.js # Turf 几何包含、面积、坐标处理等 │ │ ├── aes.js # 另一套 AES 加解密(与 encrypt 流程区分) │ │ └── coordinate.js # 坐标相关工具 │ ├── directives/drag.js │ └── assets/ # 全局样式、字体 ├── vue.config.js # 代理 /proxy_oauth、别名 @$、@static └── package.json ``` --- ## 3. 应用启动与路由 ### 3.1 启动顺序 1. 浏览器加载 `index.html` 中的 `config.js`、`createAuth.js`、Leaflet 等。 2. Webpack 入口 `main.js`:注册 Element UI、`v-drag`、无缝滚动;`router.beforeEach` 根据 `meta.title` 设置 `document.title`。 3. 挂载根实例,`App.vue` 在 `mounted` 中调用 **`encrypt‌()`**(无参则走默认账号,见下节)。 4. 路由 **`/`** 与 **`/*`** 均渲染 **`src/views/qpjyj.vue`**,`mode: 'hash'`。 ### 3.2 主页面与地图的显示条件 `qpjyj.vue` 中: ```vue ``` 即 **OAuth 返回 token 写入 Vuex/localStorage 后** 才挂载地图组件,避免无 token 时政务底图 URL 缺少 `proxyToken`。 --- ## 4. 认证与网络 ### 4.1 默认登录流程(`src/utils/encrypt‌.js`) - 无参数:使用 `systemConfig.defaultAccount.username`(`config.js` 中为 `user_kwyzt`)与 **`AesEncryptUtil.getPassword()`**(来自 `createAuth.js`)调用 `api.login`。 - 有参数:校验用户名密码后使用 **`AesEncryptUtil.getPassword(loginObj.password)`** 加密密码再登录;成功则 `sessionStorage.sessionUserInfo` 保存明文账号信息(用于刷新页时复登)。 登录接口:`POST ${systemConfig.oauthServiceUrl}/user/pwd/login`,表单 `application/x-www-form-urlencoded`,字段含 `userName`、`password`、`clientId`。 成功:`result.code == 200` 时 `result.message` 作为 **token** 存入 Vuex 与 `localStorage.TOKEN`,`result.content` 为 **userInfo**。 ### 4.2 开发环境代理 `vue.config.js`: - `devServer.port`: **2024** - `/proxy_oauth/` → `http://121.43.55.7:10086/oauth`(`pathRewrite` 去掉前缀) 生产/配置里 `oauthServiceUrl` 与底图域名需与部署环境一致;当前 `config.js` 中 oauth 为直连 IP 端口。 ### 4.3 axios 封装(`src/utils/request.js`) - 请求拦截器:`config.headers.token = localStorage.getItem("TOKEN")`。 - 导出 `get`、`postform`、`postBody` 等;**`post` 函数内部引用未定义的 `ls`**,若调用会报错——当前业务主要使用 `postform`(登录)。 --- ## 5. 状态管理(Vuex) `src/store/index.js` 核心字段: | state | 含义 | | ----------------------------------------------- | ---------------------------------------------- | | `token` | 与 localStorage `TOKEN` 同步 | | `userInfo` | 登录返回内容 | | `baseMapType` | 底图类型枚举(与页面实际切换逻辑部分重叠) | | `windowsSize` | 视口宽高 | | `mapMethodsCollection`、`treeDataCollection` 等 | 预留/历史地图能力 | | `year` | 首页年份字符串(当前大屏主要统计未绑定该字段) | 变更:`setToken`、`setUserState`、`setUserInfo`。 --- ## 6. 主页面业务:`qpjyj.vue` ### 6.1 布局结构 - **顶栏**:标题、数据同步区(日期时间 +「立即同步」)、企业搜索(类型:企业名称 / 企业地址 / 统一社会信用代码)。 - **主体**:全屏 **`AppMap`**;左侧三张 **Card**(产业分布汇总、产业筛选说明、图层控制);右侧 **Card**(饼图热度、柱状集聚程度、园区列表)。 - **浮动**:左右收起侧栏、左下底图切换、右下图例多选。 ### 6.2 数据流与初始化(`initMap`) 地图组件 `mapInit` 完成后触发 `@mapInit` → `initMap()`: 1. **区县边界**:`qpDivisionDatas` → `pieaddParkPolygon('区县行政边界', item, false)`。 2. **乡镇边界**:`qpStreetDatas` → `pieaddParkPolygon('乡镇行政边界', item, false)`。 3. **企业点位**:遍历 `industryDatas`,按 `统一社会信用代码` 与 `kewei_cydt_project` 合并属性(如 `trd_scope` 经营范围等),再 `addCompanyMarker(company)`。 4. **园区**:`initParkDatas()` 内遍历 `parkDatas`,用 Turf 计算包含关系与统计,绘制 `所有园区范围边界`,并填充 `parkList`;延时后默认 `changePark(parkList[0])`。 ### 6.3 园区与统计逻辑(`initParkDatas`) 对每个园区多边形: - `properties.area`:通过 **`$CryptoJS.calculateMultiPolygonAreaInHectare(geometry)`** 得到公顷数文案。 - 对每个企业: **`isGeometryAContainsGeometryB(园区, 企业点)`** 判断是否入园。 - 饼图数据 `echartsDatas`:按 `properties.所属产业` 三类(医药器械制造、高端智能装备、新一代信息技术)计数。 - 柱状 `barEchartsDatas`:按 `properties.专业` 映射到「规上工业」「规上服务业」「其他」(无 `专业` 则归为其他)。 - **主导产业**:饼图三项中取 value 最大者写入园区对象的 `主导产业`;`企业数量` 为饼图三项之和。 切换园区 **`changePark`**:`fitBounds` 该园区面,并把该园区上的 `echartsDatas` / `barEchartsDatas` 赋给右侧面板图表。 ### 6.4 搜索 `search()`:在 **`industryDatas`** 内存数组上按所选字段 `includes` 关键字过滤,去重信用代码,结果列表点击 **`panToLocation`** 飞行到企业点。 ### 6.5 图例与图层 - **图例**:`checkedLegends` 变化时调用 `appMap.changeMarkerState(产业名, state)` 控制对应分组 marker 显隐。 - **图层开关**:`LayerControls` 中「区县/乡镇/所有园区范围边界」开关 → `appMap.changeLayerControl(item)`,在地图侧批量 `addLayer` / `removeLayer` 面与面心文字 marker。 ### 6.6 底图切换 `baseMapServices` 与 `AppMap` 内 `baseMapServices` 键一致:`shmap_blue_web`、`shmap_normal_web`、`arcgisImagery`。政务瓦片 URL 含 **`proxyToken=` + localStorage TOKEN**。 ### 6.7 「数据同步」 `handleSync` → `syncData()`:**`setTimeout` 2 秒模拟**,成功后更新 `lastSyncTime`。`startAutoSync`(定时 30 分钟)在 `mounted` 中注释掉,**未对接真实刷新数据接口**;若二次开发对接后端,应替换 `syncData` 并考虑重新拉取或热更新 `industryDatas` 等数据源。 ### 6.8 左侧「产业分布」静态数 `industrialDistributionSum` 中三类企业数为 **写死的展示文案**(如 30 / 114 / 124),与地图统计无联动;改版时可改为由 `industryDatas` 聚合或接口返回。 --- ## 7. 地图组件:`appMap.vue` ### 7.1 初始化 - `L.map` 中心约 `[31.146..., 121.111...]`,`minZoom` 9,`maxZoom` 18,去掉默认 zoom 控件。 - `loadBaseMap(activeBaseMap)`:`L.tileLayer` 或预留 `wms` / `esriVector`。 - `zoomend`:缩放 ≥15 显示企业 marker 上的 `.title` 文字,否则隐藏(jQuery 操作 class)。 ### 7.2 企业 Marker - `addCompanyMarker`:`L.divIcon` 拼 HTML,图标取自 `legendsTypes[所属产业].imageUrl`;**`所属产业` 必须在 `legendsTypes` 中有配置**,否则运行时会报错。 - Popup:企业名称、信用代码、地址、专业、产业、经营范围(来自合并后的 `trd_scope`)。 - 按产业分桶:`this.markers[产业名][企业title] = marker`。 ### 7.3 面图层 - `pieaddParkPolygon(title, data, toCenter)`:用 `publicFun.latLngsToReverse2` 处理坐标后 `L.geoJSON`,样式颜色来自 `layerColor[title]`。 - 区县/乡镇/园区:园区面与中心 marker 可点击,`$emit('changePark', data)`;园区 Popup 含主导产业、企业数、`parkInfo[园区名]` 的简介文案(硬编码在 `appMap.vue` 的 `parkInfo` 对象)。 ### 7.4 图层显隐 `changeLayerControl(item)`:根据 `item.name` 找到 `layerControlPolygon[item.name]` 下所有 key,同步切换 polygon 与 `markers['polygon'][key]` 标签点。 --- ## 8. 数据模块约定 ### 8.1 `industryDatas.js` - GeoJSON **Feature** 数组:`geometry` 为 **Point**,坐标 **[lng, lat]**。 - `properties` 至少包含:`企业名称`、`企业地址`、`统一社会信用代码`、`所属产业`、`专业`(用于柱状分类)等。 ### 8.2 `kewei_cydt_project.js` - 以 **`uni_sc_id`** 与企业的 **`统一社会信用代码`** 匹配,`Object.assign` 合并到 `properties`,供 Popup 中经营范围等展示。 ### 8.3 `parkDatas.js` - 园区 **MultiPolygon**,`properties.name2` 为园区展示名(与 `parkInfo`、列表、统计 key 一致)。 ### 8.4 行政区划数据 - `qpDivisionDatas`:`properties.quxian` 作为 `title`。 - `qpStreetDatas`:`properties.zhenjie` 作为 `title`。 --- ## 9. 公共几何与工具(`publicFunction.js`) 与地图强相关导出包括: - **`latLngsToReverse2`**:多级坐标数组处理(与 Leaflet GeoJSON .latLng 顺序适配有关,需与现有数据坐标系一致)。 - **`calculateMultiPolygonAreaInHectare`**:基于 Turf 的面积换算公顷。 - **`isGeometryAContainsGeometryB`**:判断企业点是否落在园区/行政面内。 二次开发若更换坐标系(WGS84 / GCJ-02 / 地方坐标),需统一修正 **数据** 与 **底图** 或在此处增加纠偏。 `config.js` 中 **`lonCorrectParams` / `latCorrectParams`** 注释为 WGS84→ 上海 2000 类偏移,需在代码中确认是否被实际调用(当前 `qpjyj` 主路径未直接引用)。 --- ## 10. 构建与部署注意 - **`publicPath: './'`**:适合静态资源相对路径部署(子目录或本地文件)。 - **文件名带时间戳**:利于缓存刷新;若 CDN 需同步规则。 - **`configureWebpack.resolve.fallback.zlib`**:指向 `browserify-zlib`,与部分打包依赖有关。 --- ## 11. 二次开发建议清单 1. **数据改为接口驱动**:将 `industryDatas` / `parkDatas` 等改为 API + 前端缓存,同步按钮调用真实任务;注意 TOKEN 与跨域。 2. **统一产业/图例配置**:避免 `legendsTypes`、左侧静态数、图例 checkbox、`industrialDistribution` 多处硬编码不一致。 3. **OAuth 与底图环境**:`config.js` 中 URL 抽为环境变量或部署时注入;开发可用 `proxy_oauth` 同源代理。 4. **依赖清理**:确认百度地图脚本是否必需;`request.js` 中未使用的 `post` / `ls` 引用可修复或删除以免误用。 5. **TypeScript/模块**:长期可逐步把全局 `systemConfig` 迁入模块并在 `vue.config` 中定义 `ProvidePlugin` 或显式 import,便于类型检查。 6. **大屏适配**:已有 `windowsSize` state,可在布局中进一步使用;地图 `resize` 已监听 `invalidateSize`。 --- ## 12. 昼夜主题与本地配色 昼夜由 **左下角底图** 决定(暗蓝色 → 黑夜,标准版 / 卫星影像 → 白天),**主题配置** 仅编辑 Token;持久化见 **[THEME.md](./THEME.md)**。 ## 13. 关键文件索引 | 用途 | 路径 | | ------------------ | ----------------------------------------------------------- | | 入口 | `src/main.js` | | 登录与 token | `src/App.vue`、`src/utils/encrypt‌.js`、`src/api/common.js` | | 全局配置(非打包) | `public/static/config/config.js` | | 主界面 | `src/views/qpjyj.vue` | | 地图 | `src/components/map/appMap.vue` | | 企业数据 | `src/views/industryDatas.js` | | 园区数据 | `src/views/parkDatas.js` | | 工商扩展 | `src/views/kewei_cydt_project.js` | | 几何工具 | `src/utils/publicFunction.js` | | HTTP | `src/utils/request.js` | | 主题 Token | `src/utils/uiTheme.js`、`docs/THEME.md` | --- _文档版本:与仓库当前代码同步整理;若后续有路由拆分、接口落地或数据迁移,请在本文件追加「变更记录」小节。_